From b8667e61af1f58ed2d7f6e7487b30dda2a5d2b05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Bosc=C3=A1?= Date: Thu, 26 Feb 2026 18:13:06 -0300 Subject: [PATCH 01/18] Add ROla field maps; update Field.pm and servers Add a large collection of .fld2.gz field map files under fields/ROla. Modify src/Field.pm to accommodate changes in field handling/registration and update tables/servers.txt to reflect server/config updates required for the new maps. --- fields/ROla/1@def03.fld2.gz | Bin 0 -> 763 bytes fields/ROla/1@gl_k.fld2.gz | Bin 0 -> 4040 bytes fields/ROla/1@glast.fld2.gz | Bin 0 -> 7242 bytes fields/ROla/1@jtb.fld2.gz | Bin 0 -> 5440 bytes fields/ROla/1@ma_b.fld2.gz | Bin 0 -> 1243 bytes fields/ROla/1@ma_c.fld2.gz | Bin 0 -> 792 bytes fields/ROla/1@pump.fld2.gz | Bin 0 -> 900 bytes fields/ROla/1@vrsn.fld2.gz | Bin 0 -> 1074 bytes fields/ROla/2@gl_k.fld2.gz | Bin 0 -> 3742 bytes fields/ROla/2@pump.fld2.gz | Bin 0 -> 1774 bytes fields/ROla/abbey03.fld2.gz | Bin 0 -> 1429 bytes fields/ROla/abbey03a.fld2.gz | Bin 0 -> 1430 bytes fields/ROla/abbey03b.fld2.gz | Bin 0 -> 1430 bytes fields/ROla/abyss_01.fld2.gz | Bin 0 -> 2856 bytes fields/ROla/abyss_02.fld2.gz | Bin 0 -> 3200 bytes fields/ROla/abyss_03.fld2.gz | Bin 0 -> 2129 bytes fields/ROla/abyss_03a.fld2.gz | Bin 0 -> 2130 bytes fields/ROla/abyss_03b.fld2.gz | Bin 0 -> 2130 bytes fields/ROla/alberta.fld2.gz | Bin 0 -> 2452 bytes fields/ROla/alde_dun03.fld2.gz | Bin 0 -> 3895 bytes fields/ROla/alde_dun04.fld2.gz | Bin 0 -> 2919 bytes fields/ROla/alde_gld.fld2.gz | Bin 0 -> 2767 bytes fields/ROla/alde_tt03.fld2.gz | Bin 0 -> 3102 bytes fields/ROla/aldeg_cas01.fld2.gz | Bin 0 -> 1573 bytes fields/ROla/aldeg_cas02.fld2.gz | Bin 0 -> 1448 bytes fields/ROla/aldeg_cas03.fld2.gz | Bin 0 -> 1411 bytes fields/ROla/aldeg_cas04.fld2.gz | Bin 0 -> 1288 bytes fields/ROla/aldeg_cas05.fld2.gz | Bin 0 -> 1351 bytes fields/ROla/aru_gld.fld2.gz | Bin 0 -> 7280 bytes fields/ROla/arug_cas02.fld2.gz | Bin 0 -> 3281 bytes fields/ROla/ba_bath.fld2.gz | Bin 0 -> 3364 bytes fields/ROla/ba_lib.fld2.gz | Bin 0 -> 4084 bytes fields/ROla/ba_pw01.fld2.gz | Bin 0 -> 3454 bytes fields/ROla/ba_pw02.fld2.gz | Bin 0 -> 2597 bytes fields/ROla/bat_a01.fld2.gz | Bin 0 -> 8611 bytes fields/ROla/beach_dun2.fld2.gz | Bin 0 -> 4418 bytes fields/ROla/bif_fild01.fld2.gz | Bin 0 -> 4343 bytes fields/ROla/bif_fild02.fld2.gz | Bin 0 -> 4651 bytes fields/ROla/bra_dun01.fld2.gz | Bin 0 -> 2713 bytes fields/ROla/bra_dun02.fld2.gz | Bin 0 -> 5024 bytes fields/ROla/bra_fild01.fld2.gz | Bin 0 -> 6350 bytes fields/ROla/bra_in01.fld2.gz | Bin 0 -> 1018 bytes fields/ROla/brasilis.fld2.gz | Bin 0 -> 5604 bytes fields/ROla/cmd_fild02.fld2.gz | Bin 0 -> 5473 bytes fields/ROla/cmd_fild03.fld2.gz | Bin 0 -> 7101 bytes fields/ROla/cmd_fild04.fld2.gz | Bin 0 -> 5509 bytes fields/ROla/cmd_fild05.fld2.gz | Bin 0 -> 6493 bytes fields/ROla/cmd_fild06.fld2.gz | Bin 0 -> 6239 bytes fields/ROla/cmd_fild07.fld2.gz | Bin 0 -> 5348 bytes fields/ROla/cmd_fild08.fld2.gz | Bin 0 -> 7790 bytes fields/ROla/com_d02_i.fld2.gz | Bin 0 -> 4525 bytes fields/ROla/comodo.fld2.gz | Bin 0 -> 5924 bytes fields/ROla/dic_dun01.fld2.gz | Bin 0 -> 1446 bytes fields/ROla/dic_dun02.fld2.gz | Bin 0 -> 3072 bytes fields/ROla/dic_dun02a.fld2.gz | Bin 0 -> 3073 bytes fields/ROla/dic_dun02b.fld2.gz | Bin 0 -> 3073 bytes fields/ROla/dic_fild01.fld2.gz | Bin 0 -> 3259 bytes fields/ROla/dic_fild02.fld2.gz | Bin 0 -> 4683 bytes fields/ROla/dic_in01.fld2.gz | Bin 0 -> 2709 bytes fields/ROla/dicastes01.fld2.gz | Bin 0 -> 4087 bytes fields/ROla/dicastes02.fld2.gz | Bin 0 -> 1277 bytes fields/ROla/ein_fild01.fld2.gz | Bin 0 -> 3855 bytes fields/ROla/ein_fild04.fld2.gz | Bin 0 -> 5852 bytes fields/ROla/force_map1.fld2.gz | Bin 0 -> 701 bytes fields/ROla/force_map2.fld2.gz | Bin 0 -> 940 bytes fields/ROla/gef_f10_a.fld2.gz | Bin 0 -> 5878 bytes fields/ROla/gef_f10_b.fld2.gz | Bin 0 -> 5878 bytes fields/ROla/gef_f10_c.fld2.gz | Bin 0 -> 5878 bytes fields/ROla/gef_fild00.fld2.gz | Bin 0 -> 5085 bytes fields/ROla/gef_fild01.fld2.gz | Bin 0 -> 3736 bytes fields/ROla/gef_fild03.fld2.gz | Bin 0 -> 6286 bytes fields/ROla/gef_fild05.fld2.gz | Bin 0 -> 5085 bytes fields/ROla/gef_fild06.fld2.gz | Bin 0 -> 5047 bytes fields/ROla/gef_fild07.fld2.gz | Bin 0 -> 4362 bytes fields/ROla/gef_fild09.fld2.gz | Bin 0 -> 4257 bytes fields/ROla/gef_fild10.fld2.gz | Bin 0 -> 5483 bytes fields/ROla/gef_fild10_a.fld2.gz | Bin 0 -> 5881 bytes fields/ROla/gef_fild10_b.fld2.gz | Bin 0 -> 5881 bytes fields/ROla/gef_fild10_c.fld2.gz | Bin 0 -> 5881 bytes fields/ROla/geffen.fld2.gz | Bin 0 -> 2689 bytes fields/ROla/gefg_cas02.fld2.gz | Bin 0 -> 1167 bytes fields/ROla/gefg_cas03.fld2.gz | Bin 0 -> 2551 bytes fields/ROla/gefg_cas05.fld2.gz | Bin 0 -> 1469 bytes fields/ROla/gl_cas01_.fld2.gz | Bin 0 -> 6138 bytes fields/ROla/gl_dun01.fld2.gz | Bin 0 -> 3369 bytes fields/ROla/gl_sew02.fld2.gz | Bin 0 -> 3559 bytes fields/ROla/glast_01.fld2.gz | Bin 0 -> 7225 bytes fields/ROla/gld_dun02.fld2.gz | Bin 0 -> 1599 bytes fields/ROla/gld_dun04.fld2.gz | Bin 0 -> 3028 bytes fields/ROla/gon_dun01.fld2.gz | Bin 0 -> 2348 bytes fields/ROla/hu_fild03.fld2.gz | Bin 0 -> 6366 bytes fields/ROla/ice_dun01.fld2.gz | Bin 0 -> 3560 bytes fields/ROla/itemmall.fld2.gz | Bin 0 -> 362 bytes fields/ROla/iz_ac01.fld2.gz | Bin 0 -> 1014 bytes fields/ROla/iz_ac01_a.fld2.gz | Bin 0 -> 1016 bytes fields/ROla/iz_ac01_b.fld2.gz | Bin 0 -> 1016 bytes fields/ROla/iz_ac01_c.fld2.gz | Bin 0 -> 1016 bytes fields/ROla/iz_ac01_d.fld2.gz | Bin 0 -> 1016 bytes fields/ROla/iz_ac02.fld2.gz | Bin 0 -> 1611 bytes fields/ROla/iz_ac02_a.fld2.gz | Bin 0 -> 1613 bytes fields/ROla/iz_ac02_b.fld2.gz | Bin 0 -> 1613 bytes fields/ROla/iz_ac02_c.fld2.gz | Bin 0 -> 1613 bytes fields/ROla/iz_ac02_d.fld2.gz | Bin 0 -> 1613 bytes fields/ROla/iz_int.fld2.gz | Bin 0 -> 217 bytes fields/ROla/iz_int01.fld2.gz | Bin 0 -> 219 bytes fields/ROla/iz_int02.fld2.gz | Bin 0 -> 219 bytes fields/ROla/iz_int03.fld2.gz | Bin 0 -> 219 bytes fields/ROla/iz_int04.fld2.gz | Bin 0 -> 219 bytes fields/ROla/izlude.fld2.gz | Bin 0 -> 2639 bytes fields/ROla/izlude_a.fld2.gz | Bin 0 -> 2661 bytes fields/ROla/izlude_b.fld2.gz | Bin 0 -> 2661 bytes fields/ROla/izlude_c.fld2.gz | Bin 0 -> 2661 bytes fields/ROla/izlude_d.fld2.gz | Bin 0 -> 2661 bytes fields/ROla/job3_arch01.fld2.gz | Bin 0 -> 81 bytes fields/ROla/job3_arch02.fld2.gz | Bin 0 -> 5503 bytes fields/ROla/job3_gen01.fld2.gz | Bin 0 -> 369 bytes fields/ROla/job3_guil01.fld2.gz | Bin 0 -> 417 bytes fields/ROla/job3_guil02.fld2.gz | Bin 0 -> 143 bytes fields/ROla/job3_guil03.fld2.gz | Bin 0 -> 577 bytes fields/ROla/job3_rang01.fld2.gz | Bin 0 -> 138 bytes fields/ROla/job3_rang02.fld2.gz | Bin 0 -> 1369 bytes fields/ROla/job3_rune01.fld2.gz | Bin 0 -> 392 bytes fields/ROla/job3_rune02.fld2.gz | Bin 0 -> 104 bytes fields/ROla/job3_sha01.fld2.gz | Bin 0 -> 390 bytes fields/ROla/job3_war01.fld2.gz | Bin 0 -> 144 bytes fields/ROla/job_hunte.fld2.gz | Bin 0 -> 799 bytes fields/ROla/job_hunter.fld2.gz | Bin 0 -> 852 bytes fields/ROla/jupe_core.fld2.gz | Bin 0 -> 2536 bytes fields/ROla/juperos_01.fld2.gz | Bin 0 -> 2888 bytes fields/ROla/juperos_01a.fld2.gz | Bin 0 -> 2889 bytes fields/ROla/juperos_01b.fld2.gz | Bin 0 -> 2889 bytes fields/ROla/lasa_dun01.fld2.gz | Bin 0 -> 1881 bytes fields/ROla/lasa_dun02.fld2.gz | Bin 0 -> 2450 bytes fields/ROla/lasagna.fld2.gz | Bin 0 -> 7061 bytes fields/ROla/lhz_dun03.fld2.gz | Bin 0 -> 2275 bytes fields/ROla/lhz_dun_n.fld2.gz | Bin 0 -> 2275 bytes fields/ROla/lou_dun01.fld2.gz | Bin 0 -> 4399 bytes fields/ROla/lou_fild01.fld2.gz | Bin 0 -> 4329 bytes fields/ROla/lou_fild01a.fld2.gz | Bin 0 -> 4330 bytes fields/ROla/lou_fild01b.fld2.gz | Bin 0 -> 4330 bytes fields/ROla/mag_dun01.fld2.gz | Bin 0 -> 3053 bytes fields/ROla/mag_dun01a.fld2.gz | Bin 0 -> 3054 bytes fields/ROla/mag_dun01b.fld2.gz | Bin 0 -> 3054 bytes fields/ROla/mag_dun02.fld2.gz | Bin 0 -> 3040 bytes fields/ROla/mag_dun02a.fld2.gz | Bin 0 -> 3041 bytes fields/ROla/mag_dun02b.fld2.gz | Bin 0 -> 3041 bytes fields/ROla/malangdo.fld2.gz | Bin 0 -> 3482 bytes fields/ROla/man_fild01.fld2.gz | Bin 0 -> 6126 bytes fields/ROla/manuk.fld2.gz | Bin 0 -> 3312 bytes fields/ROla/mid_camp.fld2.gz | Bin 0 -> 5647 bytes fields/ROla/mjolnir_01.fld2.gz | Bin 0 -> 6589 bytes fields/ROla/mjolnir_02.fld2.gz | Bin 0 -> 6598 bytes fields/ROla/mjolnir_07.fld2.gz | Bin 0 -> 4702 bytes fields/ROla/mjolnir_07a.fld2.gz | Bin 0 -> 4703 bytes fields/ROla/mjolnir_07b.fld2.gz | Bin 0 -> 4703 bytes fields/ROla/moc_fild01.fld2.gz | Bin 0 -> 4526 bytes fields/ROla/moc_fild10.fld2.gz | Bin 0 -> 6943 bytes fields/ROla/moc_fild12.fld2.gz | Bin 0 -> 4528 bytes fields/ROla/moc_fild20.fld2.gz | Bin 0 -> 5483 bytes fields/ROla/moc_fild22.fld2.gz | Bin 0 -> 5750 bytes fields/ROla/moc_para0a.fld2.gz | Bin 0 -> 1056 bytes fields/ROla/moc_para0b.fld2.gz | Bin 0 -> 1056 bytes fields/ROla/moc_para0c.fld2.gz | Bin 0 -> 1056 bytes fields/ROla/moc_pryd02.fld2.gz | Bin 0 -> 758 bytes fields/ROla/moc_pryd04.fld2.gz | Bin 0 -> 782 bytes fields/ROla/morocc.fld2.gz | Bin 0 -> 3887 bytes fields/ROla/mosk_dun03.fld2.gz | Bin 0 -> 2802 bytes fields/ROla/mosk_fild02.fld2.gz | Bin 0 -> 3782 bytes fields/ROla/new_zone03.fld2.gz | Bin 0 -> 919 bytes fields/ROla/nyd_dun01.fld2.gz | Bin 0 -> 3205 bytes fields/ROla/nyd_dun02.fld2.gz | Bin 0 -> 3121 bytes fields/ROla/odin_tem02a.fld2.gz | Bin 0 -> 5947 bytes fields/ROla/odin_tem02b.fld2.gz | Bin 0 -> 5947 bytes fields/ROla/odin_tem03.fld2.gz | Bin 0 -> 5505 bytes fields/ROla/orcsdun01_a.fld2.gz | Bin 0 -> 1215 bytes fields/ROla/orcsdun01_b.fld2.gz | Bin 0 -> 1215 bytes fields/ROla/orcsdun01_c.fld2.gz | Bin 0 -> 1215 bytes fields/ROla/pay_dun00_a.fld2.gz | Bin 0 -> 1284 bytes fields/ROla/pay_dun00_b.fld2.gz | Bin 0 -> 1284 bytes fields/ROla/pay_dun00_c.fld2.gz | Bin 0 -> 1284 bytes fields/ROla/pay_fild04.fld2.gz | Bin 0 -> 6356 bytes fields/ROla/pay_mk.fld2.gz | Bin 0 -> 950 bytes fields/ROla/payg_cas01.fld2.gz | Bin 0 -> 1267 bytes fields/ROla/payg_cas02.fld2.gz | Bin 0 -> 1950 bytes fields/ROla/payg_cas03.fld2.gz | Bin 0 -> 1670 bytes fields/ROla/poring_c02.fld2.gz | Bin 0 -> 835 bytes fields/ROla/prt_castle.fld2.gz | Bin 0 -> 2336 bytes fields/ROla/prt_fild00.fld2.gz | Bin 0 -> 7163 bytes fields/ROla/prt_fild01.fld2.gz | Bin 0 -> 7590 bytes fields/ROla/prt_fild02.fld2.gz | Bin 0 -> 8434 bytes fields/ROla/prt_fild04.fld2.gz | Bin 0 -> 5155 bytes fields/ROla/prt_fild05.fld2.gz | Bin 0 -> 7564 bytes fields/ROla/prt_fild06.fld2.gz | Bin 0 -> 4587 bytes fields/ROla/prt_fild08.fld2.gz | Bin 0 -> 5729 bytes fields/ROla/prt_fild08a.fld2.gz | Bin 0 -> 5730 bytes fields/ROla/prt_fild08b.fld2.gz | Bin 0 -> 5730 bytes fields/ROla/prt_fild08c.fld2.gz | Bin 0 -> 5730 bytes fields/ROla/prt_fild08d.fld2.gz | Bin 0 -> 5730 bytes fields/ROla/prt_fild09.fld2.gz | Bin 0 -> 5692 bytes fields/ROla/prt_maze02.fld2.gz | Bin 0 -> 1026 bytes fields/ROla/prt_mk.fld2.gz | Bin 0 -> 1490 bytes fields/ROla/prt_mk_a.fld2.gz | Bin 0 -> 1492 bytes fields/ROla/prt_mz03_i.fld2.gz | Bin 0 -> 2013 bytes fields/ROla/prt_sewb1.fld2.gz | Bin 0 -> 1927 bytes fields/ROla/prt_sewb2.fld2.gz | Bin 0 -> 548 bytes fields/ROla/prt_sewb4.fld2.gz | Bin 0 -> 781 bytes fields/ROla/prtg_cas01.fld2.gz | Bin 0 -> 1369 bytes fields/ROla/prtg_cas02.fld2.gz | Bin 0 -> 1344 bytes fields/ROla/prtg_cas05.fld2.gz | Bin 0 -> 2084 bytes fields/ROla/pvp_y_1-2.fld2.gz | Bin 0 -> 2611 bytes fields/ROla/pvp_y_2-2.fld2.gz | Bin 0 -> 2611 bytes fields/ROla/pvp_y_3-2.fld2.gz | Bin 0 -> 2611 bytes fields/ROla/pvp_y_4-2.fld2.gz | Bin 0 -> 2611 bytes fields/ROla/pvp_y_5-2.fld2.gz | Bin 0 -> 2611 bytes fields/ROla/pvp_y_6-2.fld2.gz | Bin 0 -> 2611 bytes fields/ROla/pvp_y_7-2.fld2.gz | Bin 0 -> 2611 bytes fields/ROla/ra_fild01.fld2.gz | Bin 0 -> 4363 bytes fields/ROla/ra_fild03.fld2.gz | Bin 0 -> 5284 bytes fields/ROla/ra_fild05.fld2.gz | Bin 0 -> 4870 bytes fields/ROla/ra_fild12.fld2.gz | Bin 0 -> 3866 bytes fields/ROla/ra_fild12a.fld2.gz | Bin 0 -> 3867 bytes fields/ROla/ra_fild12b.fld2.gz | Bin 0 -> 3867 bytes fields/ROla/ra_san01.fld2.gz | Bin 0 -> 3095 bytes fields/ROla/ra_san01a.fld2.gz | Bin 0 -> 3096 bytes fields/ROla/ra_san01b.fld2.gz | Bin 0 -> 3096 bytes fields/ROla/ra_san05.fld2.gz | Bin 0 -> 2074 bytes fields/ROla/rachel.fld2.gz | Bin 0 -> 3684 bytes fields/ROla/rgsr_in.fld2.gz | Bin 0 -> 1832 bytes fields/ROla/rwc01.fld2.gz | Bin 0 -> 275 bytes fields/ROla/schg_cas01.fld2.gz | Bin 0 -> 3601 bytes fields/ROla/sec_in02.fld2.gz | Bin 0 -> 1087 bytes fields/ROla/sec_pri.fld2.gz | Bin 0 -> 317 bytes fields/ROla/spl_fild01.fld2.gz | Bin 0 -> 9554 bytes fields/ROla/spl_fild02.fld2.gz | Bin 0 -> 9638 bytes fields/ROla/spl_fild03.fld2.gz | Bin 0 -> 8980 bytes fields/ROla/te_aldecas2.fld2.gz | Bin 0 -> 1448 bytes fields/ROla/te_aldecas3.fld2.gz | Bin 0 -> 1411 bytes fields/ROla/te_aldecas4.fld2.gz | Bin 0 -> 1288 bytes fields/ROla/te_aldecas5.fld2.gz | Bin 0 -> 1351 bytes fields/ROla/te_prtcas02.fld2.gz | Bin 0 -> 1345 bytes fields/ROla/te_prtcas05.fld2.gz | Bin 0 -> 2085 bytes fields/ROla/teg_dun02.fld2.gz | Bin 0 -> 1599 bytes fields/ROla/thor_v01.fld2.gz | Bin 0 -> 2798 bytes fields/ROla/thor_v03.fld2.gz | Bin 0 -> 3310 bytes fields/ROla/thor_v03a.fld2.gz | Bin 0 -> 3311 bytes fields/ROla/thor_v03b.fld2.gz | Bin 0 -> 3311 bytes fields/ROla/tur_dun01.fld2.gz | Bin 0 -> 3746 bytes fields/ROla/um_fild01.fld2.gz | Bin 0 -> 6376 bytes fields/ROla/um_fild03.fld2.gz | Bin 0 -> 5912 bytes fields/ROla/ve_fild01.fld2.gz | Bin 0 -> 5640 bytes fields/ROla/ve_fild02.fld2.gz | Bin 0 -> 5959 bytes fields/ROla/ve_fild03.fld2.gz | Bin 0 -> 3350 bytes fields/ROla/ve_fild03a.fld2.gz | Bin 0 -> 3351 bytes fields/ROla/ve_fild03b.fld2.gz | Bin 0 -> 3351 bytes fields/ROla/ve_fild05.fld2.gz | Bin 0 -> 4303 bytes fields/ROla/ve_fild07.fld2.gz | Bin 0 -> 4268 bytes fields/ROla/ve_fild07a.fld2.gz | Bin 0 -> 4269 bytes fields/ROla/ve_fild07b.fld2.gz | Bin 0 -> 4269 bytes fields/ROla/yuno_fild12.fld2.gz | Bin 0 -> 3847 bytes src/Field.pm | 31 +++++++++++++++++++++++++------ tables/servers.txt | 1 + 261 files changed, 26 insertions(+), 6 deletions(-) create mode 100644 fields/ROla/1@def03.fld2.gz create mode 100644 fields/ROla/1@gl_k.fld2.gz create mode 100644 fields/ROla/1@glast.fld2.gz create mode 100644 fields/ROla/1@jtb.fld2.gz create mode 100644 fields/ROla/1@ma_b.fld2.gz create mode 100644 fields/ROla/1@ma_c.fld2.gz create mode 100644 fields/ROla/1@pump.fld2.gz create mode 100644 fields/ROla/1@vrsn.fld2.gz create mode 100644 fields/ROla/2@gl_k.fld2.gz create mode 100644 fields/ROla/2@pump.fld2.gz create mode 100644 fields/ROla/abbey03.fld2.gz create mode 100644 fields/ROla/abbey03a.fld2.gz create mode 100644 fields/ROla/abbey03b.fld2.gz create mode 100644 fields/ROla/abyss_01.fld2.gz create mode 100644 fields/ROla/abyss_02.fld2.gz create mode 100644 fields/ROla/abyss_03.fld2.gz create mode 100644 fields/ROla/abyss_03a.fld2.gz create mode 100644 fields/ROla/abyss_03b.fld2.gz create mode 100644 fields/ROla/alberta.fld2.gz create mode 100644 fields/ROla/alde_dun03.fld2.gz create mode 100644 fields/ROla/alde_dun04.fld2.gz create mode 100644 fields/ROla/alde_gld.fld2.gz create mode 100644 fields/ROla/alde_tt03.fld2.gz create mode 100644 fields/ROla/aldeg_cas01.fld2.gz create mode 100644 fields/ROla/aldeg_cas02.fld2.gz create mode 100644 fields/ROla/aldeg_cas03.fld2.gz create mode 100644 fields/ROla/aldeg_cas04.fld2.gz create mode 100644 fields/ROla/aldeg_cas05.fld2.gz create mode 100644 fields/ROla/aru_gld.fld2.gz create mode 100644 fields/ROla/arug_cas02.fld2.gz create mode 100644 fields/ROla/ba_bath.fld2.gz create mode 100644 fields/ROla/ba_lib.fld2.gz create mode 100644 fields/ROla/ba_pw01.fld2.gz create mode 100644 fields/ROla/ba_pw02.fld2.gz create mode 100644 fields/ROla/bat_a01.fld2.gz create mode 100644 fields/ROla/beach_dun2.fld2.gz create mode 100644 fields/ROla/bif_fild01.fld2.gz create mode 100644 fields/ROla/bif_fild02.fld2.gz create mode 100644 fields/ROla/bra_dun01.fld2.gz create mode 100644 fields/ROla/bra_dun02.fld2.gz create mode 100644 fields/ROla/bra_fild01.fld2.gz create mode 100644 fields/ROla/bra_in01.fld2.gz create mode 100644 fields/ROla/brasilis.fld2.gz create mode 100644 fields/ROla/cmd_fild02.fld2.gz create mode 100644 fields/ROla/cmd_fild03.fld2.gz create mode 100644 fields/ROla/cmd_fild04.fld2.gz create mode 100644 fields/ROla/cmd_fild05.fld2.gz create mode 100644 fields/ROla/cmd_fild06.fld2.gz create mode 100644 fields/ROla/cmd_fild07.fld2.gz create mode 100644 fields/ROla/cmd_fild08.fld2.gz create mode 100644 fields/ROla/com_d02_i.fld2.gz create mode 100644 fields/ROla/comodo.fld2.gz create mode 100644 fields/ROla/dic_dun01.fld2.gz create mode 100644 fields/ROla/dic_dun02.fld2.gz create mode 100644 fields/ROla/dic_dun02a.fld2.gz create mode 100644 fields/ROla/dic_dun02b.fld2.gz create mode 100644 fields/ROla/dic_fild01.fld2.gz create mode 100644 fields/ROla/dic_fild02.fld2.gz create mode 100644 fields/ROla/dic_in01.fld2.gz create mode 100644 fields/ROla/dicastes01.fld2.gz create mode 100644 fields/ROla/dicastes02.fld2.gz create mode 100644 fields/ROla/ein_fild01.fld2.gz create mode 100644 fields/ROla/ein_fild04.fld2.gz create mode 100644 fields/ROla/force_map1.fld2.gz create mode 100644 fields/ROla/force_map2.fld2.gz create mode 100644 fields/ROla/gef_f10_a.fld2.gz create mode 100644 fields/ROla/gef_f10_b.fld2.gz create mode 100644 fields/ROla/gef_f10_c.fld2.gz create mode 100644 fields/ROla/gef_fild00.fld2.gz create mode 100644 fields/ROla/gef_fild01.fld2.gz create mode 100644 fields/ROla/gef_fild03.fld2.gz create mode 100644 fields/ROla/gef_fild05.fld2.gz create mode 100644 fields/ROla/gef_fild06.fld2.gz create mode 100644 fields/ROla/gef_fild07.fld2.gz create mode 100644 fields/ROla/gef_fild09.fld2.gz create mode 100644 fields/ROla/gef_fild10.fld2.gz create mode 100644 fields/ROla/gef_fild10_a.fld2.gz create mode 100644 fields/ROla/gef_fild10_b.fld2.gz create mode 100644 fields/ROla/gef_fild10_c.fld2.gz create mode 100644 fields/ROla/geffen.fld2.gz create mode 100644 fields/ROla/gefg_cas02.fld2.gz create mode 100644 fields/ROla/gefg_cas03.fld2.gz create mode 100644 fields/ROla/gefg_cas05.fld2.gz create mode 100644 fields/ROla/gl_cas01_.fld2.gz create mode 100644 fields/ROla/gl_dun01.fld2.gz create mode 100644 fields/ROla/gl_sew02.fld2.gz create mode 100644 fields/ROla/glast_01.fld2.gz create mode 100644 fields/ROla/gld_dun02.fld2.gz create mode 100644 fields/ROla/gld_dun04.fld2.gz create mode 100644 fields/ROla/gon_dun01.fld2.gz create mode 100644 fields/ROla/hu_fild03.fld2.gz create mode 100644 fields/ROla/ice_dun01.fld2.gz create mode 100644 fields/ROla/itemmall.fld2.gz create mode 100644 fields/ROla/iz_ac01.fld2.gz create mode 100644 fields/ROla/iz_ac01_a.fld2.gz create mode 100644 fields/ROla/iz_ac01_b.fld2.gz create mode 100644 fields/ROla/iz_ac01_c.fld2.gz create mode 100644 fields/ROla/iz_ac01_d.fld2.gz create mode 100644 fields/ROla/iz_ac02.fld2.gz create mode 100644 fields/ROla/iz_ac02_a.fld2.gz create mode 100644 fields/ROla/iz_ac02_b.fld2.gz create mode 100644 fields/ROla/iz_ac02_c.fld2.gz create mode 100644 fields/ROla/iz_ac02_d.fld2.gz create mode 100644 fields/ROla/iz_int.fld2.gz create mode 100644 fields/ROla/iz_int01.fld2.gz create mode 100644 fields/ROla/iz_int02.fld2.gz create mode 100644 fields/ROla/iz_int03.fld2.gz create mode 100644 fields/ROla/iz_int04.fld2.gz create mode 100644 fields/ROla/izlude.fld2.gz create mode 100644 fields/ROla/izlude_a.fld2.gz create mode 100644 fields/ROla/izlude_b.fld2.gz create mode 100644 fields/ROla/izlude_c.fld2.gz create mode 100644 fields/ROla/izlude_d.fld2.gz create mode 100644 fields/ROla/job3_arch01.fld2.gz create mode 100644 fields/ROla/job3_arch02.fld2.gz create mode 100644 fields/ROla/job3_gen01.fld2.gz create mode 100644 fields/ROla/job3_guil01.fld2.gz create mode 100644 fields/ROla/job3_guil02.fld2.gz create mode 100644 fields/ROla/job3_guil03.fld2.gz create mode 100644 fields/ROla/job3_rang01.fld2.gz create mode 100644 fields/ROla/job3_rang02.fld2.gz create mode 100644 fields/ROla/job3_rune01.fld2.gz create mode 100644 fields/ROla/job3_rune02.fld2.gz create mode 100644 fields/ROla/job3_sha01.fld2.gz create mode 100644 fields/ROla/job3_war01.fld2.gz create mode 100644 fields/ROla/job_hunte.fld2.gz create mode 100644 fields/ROla/job_hunter.fld2.gz create mode 100644 fields/ROla/jupe_core.fld2.gz create mode 100644 fields/ROla/juperos_01.fld2.gz create mode 100644 fields/ROla/juperos_01a.fld2.gz create mode 100644 fields/ROla/juperos_01b.fld2.gz create mode 100644 fields/ROla/lasa_dun01.fld2.gz create mode 100644 fields/ROla/lasa_dun02.fld2.gz create mode 100644 fields/ROla/lasagna.fld2.gz create mode 100644 fields/ROla/lhz_dun03.fld2.gz create mode 100644 fields/ROla/lhz_dun_n.fld2.gz create mode 100644 fields/ROla/lou_dun01.fld2.gz create mode 100644 fields/ROla/lou_fild01.fld2.gz create mode 100644 fields/ROla/lou_fild01a.fld2.gz create mode 100644 fields/ROla/lou_fild01b.fld2.gz create mode 100644 fields/ROla/mag_dun01.fld2.gz create mode 100644 fields/ROla/mag_dun01a.fld2.gz create mode 100644 fields/ROla/mag_dun01b.fld2.gz create mode 100644 fields/ROla/mag_dun02.fld2.gz create mode 100644 fields/ROla/mag_dun02a.fld2.gz create mode 100644 fields/ROla/mag_dun02b.fld2.gz create mode 100644 fields/ROla/malangdo.fld2.gz create mode 100644 fields/ROla/man_fild01.fld2.gz create mode 100644 fields/ROla/manuk.fld2.gz create mode 100644 fields/ROla/mid_camp.fld2.gz create mode 100644 fields/ROla/mjolnir_01.fld2.gz create mode 100644 fields/ROla/mjolnir_02.fld2.gz create mode 100644 fields/ROla/mjolnir_07.fld2.gz create mode 100644 fields/ROla/mjolnir_07a.fld2.gz create mode 100644 fields/ROla/mjolnir_07b.fld2.gz create mode 100644 fields/ROla/moc_fild01.fld2.gz create mode 100644 fields/ROla/moc_fild10.fld2.gz create mode 100644 fields/ROla/moc_fild12.fld2.gz create mode 100644 fields/ROla/moc_fild20.fld2.gz create mode 100644 fields/ROla/moc_fild22.fld2.gz create mode 100644 fields/ROla/moc_para0a.fld2.gz create mode 100644 fields/ROla/moc_para0b.fld2.gz create mode 100644 fields/ROla/moc_para0c.fld2.gz create mode 100644 fields/ROla/moc_pryd02.fld2.gz create mode 100644 fields/ROla/moc_pryd04.fld2.gz create mode 100644 fields/ROla/morocc.fld2.gz create mode 100644 fields/ROla/mosk_dun03.fld2.gz create mode 100644 fields/ROla/mosk_fild02.fld2.gz create mode 100644 fields/ROla/new_zone03.fld2.gz create mode 100644 fields/ROla/nyd_dun01.fld2.gz create mode 100644 fields/ROla/nyd_dun02.fld2.gz create mode 100644 fields/ROla/odin_tem02a.fld2.gz create mode 100644 fields/ROla/odin_tem02b.fld2.gz create mode 100644 fields/ROla/odin_tem03.fld2.gz create mode 100644 fields/ROla/orcsdun01_a.fld2.gz create mode 100644 fields/ROla/orcsdun01_b.fld2.gz create mode 100644 fields/ROla/orcsdun01_c.fld2.gz create mode 100644 fields/ROla/pay_dun00_a.fld2.gz create mode 100644 fields/ROla/pay_dun00_b.fld2.gz create mode 100644 fields/ROla/pay_dun00_c.fld2.gz create mode 100644 fields/ROla/pay_fild04.fld2.gz create mode 100644 fields/ROla/pay_mk.fld2.gz create mode 100644 fields/ROla/payg_cas01.fld2.gz create mode 100644 fields/ROla/payg_cas02.fld2.gz create mode 100644 fields/ROla/payg_cas03.fld2.gz create mode 100644 fields/ROla/poring_c02.fld2.gz create mode 100644 fields/ROla/prt_castle.fld2.gz create mode 100644 fields/ROla/prt_fild00.fld2.gz create mode 100644 fields/ROla/prt_fild01.fld2.gz create mode 100644 fields/ROla/prt_fild02.fld2.gz create mode 100644 fields/ROla/prt_fild04.fld2.gz create mode 100644 fields/ROla/prt_fild05.fld2.gz create mode 100644 fields/ROla/prt_fild06.fld2.gz create mode 100644 fields/ROla/prt_fild08.fld2.gz create mode 100644 fields/ROla/prt_fild08a.fld2.gz create mode 100644 fields/ROla/prt_fild08b.fld2.gz create mode 100644 fields/ROla/prt_fild08c.fld2.gz create mode 100644 fields/ROla/prt_fild08d.fld2.gz create mode 100644 fields/ROla/prt_fild09.fld2.gz create mode 100644 fields/ROla/prt_maze02.fld2.gz create mode 100644 fields/ROla/prt_mk.fld2.gz create mode 100644 fields/ROla/prt_mk_a.fld2.gz create mode 100644 fields/ROla/prt_mz03_i.fld2.gz create mode 100644 fields/ROla/prt_sewb1.fld2.gz create mode 100644 fields/ROla/prt_sewb2.fld2.gz create mode 100644 fields/ROla/prt_sewb4.fld2.gz create mode 100644 fields/ROla/prtg_cas01.fld2.gz create mode 100644 fields/ROla/prtg_cas02.fld2.gz create mode 100644 fields/ROla/prtg_cas05.fld2.gz create mode 100644 fields/ROla/pvp_y_1-2.fld2.gz create mode 100644 fields/ROla/pvp_y_2-2.fld2.gz create mode 100644 fields/ROla/pvp_y_3-2.fld2.gz create mode 100644 fields/ROla/pvp_y_4-2.fld2.gz create mode 100644 fields/ROla/pvp_y_5-2.fld2.gz create mode 100644 fields/ROla/pvp_y_6-2.fld2.gz create mode 100644 fields/ROla/pvp_y_7-2.fld2.gz create mode 100644 fields/ROla/ra_fild01.fld2.gz create mode 100644 fields/ROla/ra_fild03.fld2.gz create mode 100644 fields/ROla/ra_fild05.fld2.gz create mode 100644 fields/ROla/ra_fild12.fld2.gz create mode 100644 fields/ROla/ra_fild12a.fld2.gz create mode 100644 fields/ROla/ra_fild12b.fld2.gz create mode 100644 fields/ROla/ra_san01.fld2.gz create mode 100644 fields/ROla/ra_san01a.fld2.gz create mode 100644 fields/ROla/ra_san01b.fld2.gz create mode 100644 fields/ROla/ra_san05.fld2.gz create mode 100644 fields/ROla/rachel.fld2.gz create mode 100644 fields/ROla/rgsr_in.fld2.gz create mode 100644 fields/ROla/rwc01.fld2.gz create mode 100644 fields/ROla/schg_cas01.fld2.gz create mode 100644 fields/ROla/sec_in02.fld2.gz create mode 100644 fields/ROla/sec_pri.fld2.gz create mode 100644 fields/ROla/spl_fild01.fld2.gz create mode 100644 fields/ROla/spl_fild02.fld2.gz create mode 100644 fields/ROla/spl_fild03.fld2.gz create mode 100644 fields/ROla/te_aldecas2.fld2.gz create mode 100644 fields/ROla/te_aldecas3.fld2.gz create mode 100644 fields/ROla/te_aldecas4.fld2.gz create mode 100644 fields/ROla/te_aldecas5.fld2.gz create mode 100644 fields/ROla/te_prtcas02.fld2.gz create mode 100644 fields/ROla/te_prtcas05.fld2.gz create mode 100644 fields/ROla/teg_dun02.fld2.gz create mode 100644 fields/ROla/thor_v01.fld2.gz create mode 100644 fields/ROla/thor_v03.fld2.gz create mode 100644 fields/ROla/thor_v03a.fld2.gz create mode 100644 fields/ROla/thor_v03b.fld2.gz create mode 100644 fields/ROla/tur_dun01.fld2.gz create mode 100644 fields/ROla/um_fild01.fld2.gz create mode 100644 fields/ROla/um_fild03.fld2.gz create mode 100644 fields/ROla/ve_fild01.fld2.gz create mode 100644 fields/ROla/ve_fild02.fld2.gz create mode 100644 fields/ROla/ve_fild03.fld2.gz create mode 100644 fields/ROla/ve_fild03a.fld2.gz create mode 100644 fields/ROla/ve_fild03b.fld2.gz create mode 100644 fields/ROla/ve_fild05.fld2.gz create mode 100644 fields/ROla/ve_fild07.fld2.gz create mode 100644 fields/ROla/ve_fild07a.fld2.gz create mode 100644 fields/ROla/ve_fild07b.fld2.gz create mode 100644 fields/ROla/yuno_fild12.fld2.gz diff --git a/fields/ROla/1@def03.fld2.gz b/fields/ROla/1@def03.fld2.gz new file mode 100644 index 0000000000000000000000000000000000000000..1e7b67d50ecf23c547f7186a994b10ba721e888e GIT binary patch literal 763 zcmb2|=HQ6g?UKR3XXucUnr2|EmzI-a#PIe`WZq)~0hR#43upfJPgu++)bnsgbol13 z?#b)?XTGgV(CPX#ssHt3E1UW<=AM>*?;ksmi1m|xYhSqVbl-2ulpjI&=dx-W0(Jq(-!+}@St?p`^dCD;ok)hwjO`Z z5V!pI8P~s$YAw${Wq$N!>G>M_2OpOHpZUUDGdt+Rt$mBao>!arYUVfnJ{2|d{Y6t% z296Il4(<9&y@W4>uND3IZXSP3yxOmK-@he_?}*&@J~rCc>{s5Ac82Z0UL6pB#TTE= zq#b-yPxRNG1J5t_e2%eG4>)Liefsq{xdkoHBmd8F;5`3YyLwy0-i1N;B?68(f7g#& z(AECCC{bMFab=CIY=j_|P@l~30sm+IfhhuJJg>v2N;Afe2dG+UBhU(|%eeTEe z+_2p%dtl+u&5gD5m@QryH;D1G_x;-CFx&k8bw;krmi3zt8drMPm~DvNe>MESJ%eR>Q(bok|*?^-27@*n{(4+ox*Ir9o%?do*LC0P^q&Ru zj(wMC`YE&PsonJ4jpuWVe_HMN#I{32YEx&erl>=h`HR#1B73fXH|Mx_;@>KZh=+S? zb{DN&Uv%Yzq0Oao*~i&?ujI-fo&Eap^7F4(%f0_)G4J!Qi1_5K?z;*IXd_CDxL|0UwT=6CIP mk$E4!sxPlq{r8`3`iDYJdEyAmd*xOC82=Ycdcu;zzyJW3Gm=yQ literal 0 HcmV?d00001 diff --git a/fields/ROla/1@gl_k.fld2.gz b/fields/ROla/1@gl_k.fld2.gz new file mode 100644 index 0000000000000000000000000000000000000000..1326696626d7af00d69928c766532feee32facdc GIT binary patch literal 4040 zcmXAsdpy&N|Hq?JCW%^(dy(tMCD+_bl3GzM``Fy?mq{J>%aBBJkD8Rr4y$DQFxy;0 zi{{$gFQY6JM{d!HHg|r$kKezq|K9J%`}ummUUF%N4?8zp^4oV*`-1Ns*8okQJ6<~b z77GJ%r&Nw4EjCu?j_+>xki?|=R$z&XhQo7%RKBbBuJPZ7u5%l?6ZNK#Mw?-^$?&@( zz*kxDopTO*qp`5uqVn<}$#G6;C+v8Hwx9-3fW}VqLQbE8LTIU~pxcHlIbjv6~F9m@a}gh@T~Xv}DZWw~@vb zlf!3wJ*H(|sE`}U)7*M!F)Sv-Ifb3~@)M*P?$hDhO5@oVtP}^K%{gJb@i=u-am>Mo z+;qF1lKpDlg>o&dupUqD3XyJ{3NppM67J!bo~0Uqf+D?RKE5HVyzwYDwRQoLZoia9 zcOVHll6w&5x@*RiF2vmNb}dLD3;#|a%Q-=C!nJsYN7YwZT0Bd(dJ@koS^rlPDE)T+ zH%>tBt|nkNQ6e-nzV>Pb{-#$lLJzR~HvA!iG19$C8|ay560di%$ffNS?ejB-#8e)z z(bJ|Tt_0M~xXUQW?K%~kxi(oY%6->7@=spt&s{9&O__J+F3rZYS^Tk~4@D`)c`2dJ zh@C%~`$8*3I1_)St#W_XnJRH|aUU$|1R#_iS$MK&~Chr$=Y0YvNZnY35z15l`r6gix%OC}`x{uW=*H8TtHj7MnRK7x96A z*sU31#INpgst2Zxj2{y6Yxnn}@yno1Hj7R(v8cbVT16`=5jgt^*}pD-4@#_(Yl0d7 zR;KAJ3yM~GzFSt&#j{goOLEXJ)=#L1nIUk~yKHFol(lCbJ6G%imk;V|cI`%h_Lp*> zW9=A(qm?Y(G<<1W+&Os&@%(R0!pG5>&-(ym88^LoEdBMt{O`uRD|ngE_J6E3q1n<4 z{HmK>PkLeIL>29BhmTS0=bN+EGyO3$v;;yB?ao$tm`AVULvfs}v@@^cy=1z`h#`w5 zd$2&_8~rV$*@XBjN&^)9g&U50P2&$cLZ4r=RdUlICrL`q1)wFeTKvxZew((YOw~zt z_I6R--~G+GcdW#px*5HrVyDAI}T=uDm=ykoKm*5U6ZX=q(9U z$xddM7E<`(YE_!;q&R%Q-}4Ja_L}4cGZV^8ZCDE$fy*oq2H$D3gnjEeBr5uH9G zkn?;hzToxGrXY1}exXl(^M(SIomZJR3Q9MdVNhUF>8E&*%!@U-9*5 z8){mm7)}|Q#?ap zrr^2)1X*hquoD^E1`ta_Sci#_bzJH$G0FJQ{pPL$s|-RfIHMKIErL`iYJ*6$z5@zf z9OWGs(fYN=-Mhf5{FDe#(s~#fc8&e@Dc*qPS*=9hSnIVQYHD3`$J1*$8%xa8%k1E$ ze=fN?-XJJagXyLaFYa9DWW`5Mv{9HLqvIXW4O}AJczFLfb-N(g0O&+y8%VK#Sz@?N z#_{ll2BzfC2UZxop$Nc^kR?Cmg)c~yCZn9sP}U>C^1xlQqYgr!W@dRMesT6*)!VDr z8+(gBuNe@CoQ#q+LC8VQWkwX*ae|l~d!i`&@#;q?{cTLlg1=RDNHkUqErE(L09@wQ zp*S_-#|3kZG-vI>@^DFW<;q}bw7?xN+^@??#!1G>nA27P)paNvk5&OQXy|QdUhc08 zpmMpzFLmP{ENa!3(@4w>UGP9-swIX!mQDK#V7>2Ezo^9RJ3R!S!^HFSkG7UhcBZv$kSv@cDr{z`=m!Yim+q^{p=4{q%4rENIDzc7RF=;#pQ^2 zRJCBg%jjjy_7S?n2p@<%tts(>of1IXr4UsHV-Aa@$RSJlmk?+9)Z2 zIk?J;?Gvw*bb9FBX91}rT{JUL$IWQ9LbpT%N3TwRS!x%mylAwQe)H=D`~33p`Ju@S zs=TMI2fXav-Bs%S%7#m6CHhI3 zg3zG~B5I_@408AdIv)iJ`wRiq%js}jQP7F1an9lcNZs9L|M$LO^{f|NsUMaUz+gfX z3qGuOQ%e>3ggp_+CV~&_>@o~f@uX0Gpu|HHpnMHN>aK>{wI^3@Y}QaS6c6~?;4+x! zVkeqXA60yM>o{?a>QIgUgNmTDavsW{z}~qqQpJEYv_Q?|D&*fV6MYe7W$> z>BG6nu@vz?)g>YvOJANF)jhrj8ro+}^CCJaGnqi%F)YSP* zYR-txe{Ryz;g_LZ21B0AiydkEhH#XzcT4Jt))N?ztIa@t4Y{^y1`Ss@02p1PG6edD5}s-RQdVqGb~Ku9T8ImUdK1 zvd!CSzXOT$=)+4&6Un}siLi0x=dz`LzpNI(p`M$FwRiG+shbBCk{%z3|NV0@)*Pra zEqE$FnE-uN57>N;Vmc3>+Lf;QkQa?+bGP=GkRUVI!%9{c=Kot*qPl=pvJ@&E9Ws*T1U6&C2og#sp4?jbd2z&;Sk<#v=;J8}QIIb*jsZbPo^!ZmS# zXpn~w_Q`8wJ@)D*=YswrLz+^fqR2H&Rr@kV@Voz;nwmhDiU3*Qb5OF?nbz=<#E%CvLQ)Ps7d@_ z;|cy3_P#W+ahx_;?@?`8-UVIRqGzIUNBk3?SP$H#Bg5SFmFZ&qCtgENuPtxSEnNPA z|HHYSL`q_u!K1j1&FV2NX8}>mJiMq}&XX2ak-guMYj24rv|1CI{=ui1sPCZ%YFHd9 zKhrrGe?QZ6(KZlSNRe`HtTzHm^+NR0BH>^rf04cY8K5?RI8uM~UgB%@4EDhLvl+3F zi;a-*CRSON|Iq0o{*;yuUn{jceyzhvvRt2F%CzJR4F-cwS`u}wH_=lZTG)< zEc1oaSxwkF3f=TgWYe|>BK!ZRhc`(&xnP!>)qZzX9)c>CnoGczPv*`9kJs0(K?%5q*Y75jsPg3n zY^2Da)$lwxi6dW9eaC?lZcEJl$Q}qM*|=FPoA?Su3daFJ%OqrISRk*i!Ga;M)Qj9s z?1h^FiTHC&ce{wc_(GT~lvQFMBkR`+SuPH9j*MdIrFT@~8jE!npsbE7KtFXK%2~;I z(n+}4uxUas>`rdVtQl3m!*Ll?9R2F|6!f1Hl`08zTc+$qW9czoaotaq(L4*V!Sg(@ z^GTTIacxNrdUhB_p6$xf8O1}E&{@=gDIa=qOx$C-B_;Zrr#84{J9i!rXAH^@{GOG3 zrdsd`F%0{YV>g=l1-lY#3O)I3I`{y}t)8;ZoPr?zE5x~o_t*@0e@z7amJ+#0P=K}( z`vr-z0qSU@LEs`O6g>wxyQB62vg2_U$$dXID)RAxY5t^O?vBi@ugCv=o3_!7oBBl+ zGh2rTCi~V4s}kXTki7`g{Dj>UpKl#_6ncfv0!$6i(Wo1`jaGdWiEzmDc+DUfll5fWL1bc0 zn4Q$sf$!CIMcs(it}j=u(+QLwnoX9zyuPFQRZO94JE^y#4S__K$KMW%Qzqje3j*rV sKxX-N0&cSzhWeFdIQGAKZPl*)`TzU2bs(a)a(7?eyy);VSH6A!2krC>lK=n! literal 0 HcmV?d00001 diff --git a/fields/ROla/1@glast.fld2.gz b/fields/ROla/1@glast.fld2.gz new file mode 100644 index 0000000000000000000000000000000000000000..edb70fa8d1f265d168305238f35f7b1594d3957d GIT binary patch literal 7242 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%0D0h3?Q|0sX(-QhS+43EInhW>3!DPb^T;I9| zHQx!#nox?3_6F){G|U7IZh3~b^?G{OAo!J|vQeiBwbrsr0t-y9h))Zcj?@{iHH(4o zd9`w^9!m@}KIK{xhzWzcjq%uu4A=I-9Tlzv6ii;;W@$&CEU&-aW%9O(az~qT226E}n0kKC zKvb{4e6AdRy4Erky)I)hPxpJc4}8s!QGzG4X9~q>&fQBa(|hH9JL)zENx{|`%=q?R zkw<<J<+V_G!OJlA|xET6-#Hi0sSXt-Y?ulfG z?FZb^6nKbOzu)kwu~_nWCo!cz%&JTCkF*@LRd0VT+xQh9K$hCec;{{|PgCU|+KOH_ z`u@Ani1HFnfX~=G6O|>QADh~;x2}yePINPIOROkmP{BCUGE0hEul?e(T_EnY8`YKw zK|d}C(_Y@QGPR>yR@1nIc2DUPx{*Z|dyyN1`mb01Dx*aFxsi^1arS@cx7>AY{V#ZX zkvvh*bORv1@Rq5d*rX}hv%CS$uPErCt~j|P=4iSjCiAcxIc=7do>}VLph72lz7s3L z*A-P8?-Unjfhd7hL@_jmxjv&s=XXlFdOhdx*~gO-Xi%FdSdSRu^3SypVVRe&ZzSSq zDG=~F`bdm*0*5)~4gkF>cApH7nw3h!9Oa^c$q>!BRni~v0IPliWp}?X2=H534g52E z-=FM2Xs_jZ!1;i#wR#HDc9eD_bpZ+{c6@VO1r(g{>DtBP(bcw{?H06X&b%h^bG|u` L!|cmmmni-RBi&QA literal 0 HcmV?d00001 diff --git a/fields/ROla/1@jtb.fld2.gz b/fields/ROla/1@jtb.fld2.gz new file mode 100644 index 0000000000000000000000000000000000000000..6f359e1fd60f21ba2acacbf9bb018c38ad931615 GIT binary patch literal 5440 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 F005YYl_~%L literal 0 HcmV?d00001 diff --git a/fields/ROla/1@ma_c.fld2.gz b/fields/ROla/1@ma_c.fld2.gz new file mode 100644 index 0000000000000000000000000000000000000000..4c7fd8ec46247a625874164f8a9a16510594c0aa GIT binary patch literal 792 zcmV+z1Lyo7iwFo^yF_RJ4lzJ&VP9h|W^80K0PWh_j-((E24G3#rZ0T@x!A{@@c>j& zCD4%qH~If$Gb26f*9FRY__zEl2!bF8f*=TjAP9mW2!bHUf0K1KK+XD>8T)n4l|Ej* z_wR5y7C*`5a@S6o?Yj0&oBWY?xVVDLqN_3vEm_1AWPhmZfSl*@v|8M?zQh%~GPqW8 ze2jACs*sa)C?}{~8tfYE{BQ(DtJY27uC{5eyAE~X_#EL<#{5jzjjhy;>3nAFI_E%A zq0*?oDmm7bbscZ@Q4|^r#{YvT>Kh`%57WxMh30~Yi1pP`bjKf*B7t| zykg;slVTV(jRm9UFe?{H%@s<^YHaWNR9v3B+&EWDxqLQN*|;c-xs-z;bswF{t$b;} zY)Lyv#i482pSnvwd5G4y+*?H_@VYCzoyotFflE%4aj|~dHM$Ar=xI{8>eGP%NxC>p zxeyuYqO;w`_NjO$)k_Yt7r3+=C&!O%D@+A;u0YdoocP04nxlro*r+2@y4J1MF!cu9 zwpS~r&czXrN%^M(1+*p=0b{(fGW#XoR) z%v`cuTJeNuh-<&-$_;c`jH|VN*S3qxA5Z#QUHj9kb7gav=(AEcM%OahRUBP=i}}NM zZ34P5`gjlHaUM)un9)z{VJ$9MOMBNntl%2)bemkQ-MGX@gC4q{&(ib4&}wBjPlmmu zaJ>_8>!NL=`Y|S)GgTM;G5FOX?qX*NUbnHVh4+~}rG^hjziHLx>iLB4o3(u0y(0T^ zYdu{q-V?ifHaNpo+{*FqnW~?QZfkk)9zb7L+Suc5q)qj6bqrkJ1+MP`7lI%Nf*=Tj WAP9mW2+|t&()^X2D5S`Zs=pfSUeu{k=k=P$(1%g+ifFC=?2XLZQ$i3T|+N8{FUqH@LwKZg7Je z+~5ZH|KSo5KT40TtNt1fsN88+`aChWzj%%wE*CISZ8s$Wlj!O#MxnL=& z6iS^XUa?drFSj;9)z{6^c8ldsp_MD?N@tcaIK;JW&%#G1xRz5#8!rqOyKp5hw>6ia zpVB78>%`^uhH#UU+c`Ps)w71G@0{f33}B3{(FJ{@W}es2U1;x!-;q=DPS#g`ZufgG zBbOTYzL9K)f?W32>=eJ6*cFsD*@S?L`+!Z$1Xy#vMw!#z#)T-N7w(i)_5LY)4VNdb zEh77{`eOcFJo#?D}8bi%tp>7S{=C>;6+f@GUfDc$7mn?S@ zTQ1%^$Gz{5T$;JN)zzN6#;%TbpA!jl?AuKGEf!&dPPJV`V;q*wV?JmfBci9u^3vxKGn}0o!CX0Z^d#8SWIrC7D{YvX**! zf-^GbTgr=X9=x={d37!k2Ha+M>zQ+1cY!6QJdObCT$65_Sz&K4av9GXc{oFR_RTE# zMIJ4M5Z&C6LH`$mmvoLmqY?ok5{yQ%JzMal=Nmo*m#~ zZMH|W5FFy=b_3UtUclzT0q(qLGa>$g%cr>0KFE%+rVit3*H^FJMiOI-wN?x+`?*O< z<>Fuf53a^sdCj*^=_OjNqlIghv@jYjZrLDpGv#~D)PvVV+U`>}YX2u3z1J@}{0dg? z<>X#TgJgWlk?$%drdg(p_I<_w6|QeY#Q^+bgxnrY^?Ad3{oIVx1)?<9{YqduYY6Pu av7t~X6bgkxp-?Ck68!_Nsu=48O8@}1%*JZ~ literal 0 HcmV?d00001 diff --git a/fields/ROla/1@vrsn.fld2.gz b/fields/ROla/1@vrsn.fld2.gz new file mode 100644 index 0000000000000000000000000000000000000000..93965dfe6843b00805e494f75195e37ec102e9ad GIT binary patch literal 1074 zcmb2|=HQ6m?UKR3XXsE?RGg=mmXl({@b*scy2T0tZ4b>3{@(xe?#k0%QyUw#l=j3I zO$uY2d3?UvylwB^-g`H(a;_wg!2t$ltOQq4xoPP^24+q+2@it<4o!?~LOcc&5*iv= zn0Zuw?z%TKp@ETYEzhUo9`@!U=DmCF@%*`z8d=7_W!kZi!cWg8P0(6z-|T<(boiZL z-fwT2yN-;2u!Ub5Kl(F2(w);y^vOW;&_w4=Fo<5(J{adH``)zray47{p`*R*lU;o;vd27>Ig`9N% zdk@yKH2>y#be-|POmihq(O<(A<`)E?8Laqy?{@-Q=>GZ(*A(s-XNRT-e72T2EU5wv zMu`leD(%}cudm(D|FB6Qq2b_Bw(I-dwH!URp3HxLlWo1tK~A=vyI)NEsBdC$r}%U} zKlA0Oyv)}9oXZ4_NNKYLN_`nOyGylfB z7!)W=91$@15E^OyJ#9j^`0MtA`D~Noc(N8tM!c54c_5OX`TF^t1&s$EuVa@0x;m9j ztZCu;PO}D6i6#4f|IT6Ff383)!C*_{&yWKN8O(9gmV5>mc$2)_%{jXHs?H<{m_+Q0 zdON>AzhFR82o(!4`j1ELOD5WW|XNwxw#xQd2a0 zG6K2`W;Fh;pPC((u2n9z<<9k$cTz7L7u@hntohORQ`euVHT#`wGL=YB;9PaBUiXsD zqTO#9Ya)YBiTcgXF19QG? zlS^12D59`r`_=7@adna|Rnb5L4=Q|YT+_*OZAo9-j^kJTB(|8J;Lg~1W~0&C{^o>y z|LhI5IXeXsHJTsHI~jDqVA{d5_d0n3wgpP3&!2j&)_mopK=!>W4yu+H7i^Bu+Wh=o t)~iJ#!CSr?f^sOE`HC}luU-KK1jr&B*%F7;lbB}vsVN_iif}M8007^0*NOlD literal 0 HcmV?d00001 diff --git a/fields/ROla/2@gl_k.fld2.gz b/fields/ROla/2@gl_k.fld2.gz new file mode 100644 index 0000000000000000000000000000000000000000..863fd38ccbfb027032c2b195d1769b9ff71abe11 GIT binary patch literal 3742 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 literal 0 HcmV?d00001 diff --git a/fields/ROla/2@pump.fld2.gz b/fields/ROla/2@pump.fld2.gz new file mode 100644 index 0000000000000000000000000000000000000000..7c90c5a400c590aadc13a789bc6cad5726219901 GIT binary patch literal 1774 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 literal 0 HcmV?d00001 diff --git a/fields/ROla/abyss_01.fld2.gz b/fields/ROla/abyss_01.fld2.gz new file mode 100644 index 0000000000000000000000000000000000000000..bb83e51fbe013a6b8690959a39fa1cf8a1f69fb2 GIT binary patch literal 2856 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 literal 0 HcmV?d00001 diff --git a/fields/ROla/abyss_03.fld2.gz b/fields/ROla/abyss_03.fld2.gz new file mode 100644 index 0000000000000000000000000000000000000000..36babeaf0caaa61a2dc5a73ed2b026a94d3b6047 GIT binary patch literal 2129 zcmV-X2(I@ZiwFo|yF_RJ4q;+>b8}xXGcIOqWHJEl-O;kEDhvQnoskzF{Qpn7pLJVB zNFX5zpk<#i4`+_uDp?}bBDMc5|1JMWtiMiRP`r-0;&sdwuVb!w9dpI&m@8h#T=6>Q ziq|n$ypFlzb<7p7W3CwL*l=(~euCW|EgFix!Ljdq6;gA+)!eTfNAB*!a3SbKSjXlZ zSc~1B-l-)~A+`u;bzBv)*0!g2YE8BhJE)2l$9I!+jcW(`vPV0Rq`~37e~mlPXE)BV zy1Y6V2CdNd zRBAKs<8Xxk4R&m-12om6>~4nxW!&Z{6{yn@9c~D-5giuD4GyA|brl@v zAI1GV=b(<_3Io256$oNNIPOiU*+85P>&iLuzq(LI#-@RfNwWidwm3ULGEwh6qHM() zm(XrF_c6xd&a0@*k&d;;khC6GQBOOrQ>nPFb@?gf!T+O!J>hJzjc^=$BHq4ZRjg$! zpmh&`Y-9szaS%~qkbx4T_RC-D^k0|oj`cj`UJpqgvgdP^Yqnko-@6<;vQX94gTEw?(w4Xw2~ZUkBb;vZa^N}$rTb05SKZ&9tU;Bpmf-q{FV70 zhDJaCfkXGTlS<8#-M~SgIN%2Ym$>5leYJ-i4_+M274r>cd5a#OQFu zTUy%~M>=USM8)W-5BOfI8LCz}zU?g*^F35LhB7?pYS;4$`1~|H$gx4kns+F@xmT;o zd*qNAoC7zF(5L$WfaIt_7Qc*e>T;-3}>W8to4El!LF9dmQG=@NUPNhO?U;j&{f;Uy=5YmotKJXq>t_RE{|8xgS$` z6b@JbUPcb7iibN6NnqrkITVg1TmGTLSsZ!Zv_lNn?Vu|pTqFKzhoShH9VW(TM|;03 z-HFvR%Deu*bhHs@haMA)?{KtTVNnI}FN==e>yqXxMZH4-Np}w09fxHEquFsZj3JK2 z${6Wz(9t_yV(qI2I#7M|j$c`O+#4L)hF%}P>Syq?b!A8DHRZb-!Z>voq3#ec$k8`W zt#=5C*2=Fy;JaAoeS=ugetg`R@_0i@CfAA|FyR$mNvG99m zeJCrWlHKJvhpLQ@%kNk5U0u3=-sXr34;=MX3CA8M-T2=6XmF&L({LdkHq<)Oj@D7< z6<=VU7$RHif?ISs~6;fFT)sDFS1_oe#Z#$2U z5%;8Qw}D`FjB>^a+2aHyS<*sem8_uC}FE*B6 ziBz(C$U&!d+{lazB%|hVMKn5`)}hGDGmiR*$?qIQUd2@2rM~`PN9}8K6-SvGe{FTY zBaOIFGtUJ_%nnS0Wv+Hj4)gUD7&ihtv^bWcBAE3I4c-2EaDZbuFUioQ;SU_BQHU*s z-+P*`KY>Gpa68XBDf9h<5sq_=qQ{|Iuja_VW@vY8$RR39)U*x+`ST93rQhXHGBn@w(nAJ1)^iPI>rgoIrY;`E zL5{ObEkp4=$WY{9hXx_OiR&F#p-$C4hRy3sZ&%t-InW4@d?f1*dHZ#}Zf&X=4bKBm zq)~Hd815#AbI&>~UzqrjL&ccIktc3;$m7yaJLqL4WV8ddbX6Kz+GfUGj&E2T`F(^I z$9~uIcMXj0xYMDWZPF1(M!~WFSx3z2A8{-4Bec1J9r%^}V= zdBg!ALORX!W6FyEsUv!t9_e*G8|PMs<P-zi#2CkwN%jMTCdd9W#}c~B@L+FrXpbbh?Z_W0+Ivo`0gmk8!ewqdLebeg z`WzF*>zFHE$6WC`=8D%bSGQ3eByrzYy+!x^|oW HM4SKsSBxSY literal 0 HcmV?d00001 diff --git a/fields/ROla/abyss_03a.fld2.gz b/fields/ROla/abyss_03a.fld2.gz new file mode 100644 index 0000000000000000000000000000000000000000..a9218ff75a1adae379c0100b4acd5ca99cd23642 GIT binary patch literal 2130 zcmV-Y2(9-YiwFo|yF_RJ4q;+>b8}xXGhr@fY-BP3?cLF`t11itP@Rz%9{m4Lx}SAh zMMxka37}=4F%M^s-6~lk)FQS2E&nb5NUXn3U{Jh{x#D%q6|ZBicpY=a>zFHE$6WC` z=8D%bSGez5_MSg z99WCpp5Cb?Q6aVnXmwl_vevezcWO&Mrc?JsDk&*w3* za8znD?&ENT{|$C*tOGRFqwH>n17+OiC>5yF5glyPpyM9L=TMOh1Q8t;$PEsnlywyx z=O4xWJm;W};tB)4jui-ELOAYCso6lB4eQD|^1r%JN5-ask4duwe6~0{Kr&J9J)&&I z8kf*+IQKEe;m)h5%#n_@$B?uhS5Z$pu2ZSFu66k-<-z}>gFWGFv5jyXdm`SxV^yqW zETDA{fNW#~X>kxyVUU3mqxQ>R>hxci@Q(F7a3zS~0vBGQU>N9_WrOwD|Dfy0D^dWG6it&W0WcEphd_%c-*o1(^1 zDAJocD*4FY-qz1fLgnCdY;k~#6I5_;SQ#kms-yBzUE|;wUr3%#sE!6r?jRTsI?&l= z(&QB~+_B|*gAm@~jSc?X`|k0g^0bm1Wsi#(TW&xe+sPFY4-l6*wjKv{#h`T9oBWme z9)?Cg|A9mIwUbKYAkNYl#dRzYnHxG?OjxQKBnN#37ZFQ&J>G?>I7Ei*DC)ySg~aG^ z#9Lb17)Ls3F+|1asSo&Gs~M_RIlk>J7V|w+I)*Yl=xW#V3i$jqJjk&@$C`I2y}4Jb z%6sIH8Jq(*jnJq20f6MFK^DJ^aO4eqSo)NM1bH;nH^?!b0Z%NtViwr#_c(5?4mN%0 zNNIF9+}#c-U>fZX_mqRLmU|rL%kXZ;nufER9gcR$C0~*DkC!uoaA=&mI#iB0?71IP zc@z#<0A5B8sfvd?4oP6-pE(qcC0qWX!&w}8-n2su*X^JyBwQo@X@{ZsnH?s^Xh(a$ zE8U6JGs?UEzjU+_XonsXi|=r>U13oL@Gpyw-s_U)D@DCS0ZDfb+Z~5x1f$t;G>jpR z#mX4zaL~~^USjR520BoE^p0Oyd)ylw+J;^qzv^f3vvp-h>NVxN8^Sns7oqMDFv!t2 zPOWzciPp-mK;XMr=Y4}-hv?)Xg`+AN;*hg!a-isQ=-Rm|N2BA+eGbQ>SgGd7Yox=W zf^gf1d+8mHZ9WIHUcEZbX@u88vqSlY&gPeohL|1eF|4xh{q@o22!HS^9=o$T!m;ps zXMHFuq>|m`IESi?j?3>?@m*cIf8OSZ3J)ChRSCx)C*Ano`e<;Zm(y?|9yZiE(vH?q z=M`UIo)~zU9rjc>Hau|I=SZDKr9_+;d^uMnmdGF-&Fv#Ov=vfW2i1deI=`&5%Fr&>c^pk!$A5n3NZEHdya$X{hvlwq*c;BFaxXTP zUx`$*d&ohjb==5|3nZiFa78paoYtYp%QKGph{^99L|(;I-le|&U`Oq1a}`IK8h>qd zzax#fP&3a3N6ZdPgJrIEO%C(*6&N=HJG3~Kq9T~}3=Q4>d2oPZIWNi3rQr`8s8NV5 zgx`CbuRno9gm630Iw|x0gAtB%jH1V(Td(HGzh-E6Y{(%&xP8yz*n1s$k~-{I$3{9B zNM-lR6=X--IQ7>-oLA-wk?!Nv84%KZAIDSm;n%^U^~GI@WUyW$REl@}@2x z#X*jDkbETT4te`^y>4x)84b?^ zP^3|FXc+D$hjY(5EMJ)Tkwe9p#gQj&cF5z>Pdn&kC1kV%wRBY)S=wgCU5;;99Ql2O z7RP?q^LGu5?zq#ToNdw(M@GT1|5-=O=^t?{H#?Sl9WbkB9A{B?I^1K9@s!MQUw^fP zeiv+gk7Iqr0k^J)I01m3m$k;(qBlCw)k2>mf0oh14)~e5hY{wU6gQ+3<90_Zpv@u9 zH+jSXAVNCL^JB`2|EVK-njYzOJsam%hvm^Y)@LH1k8R3Yx!Z9*yr7tHIfcXWIfR}M zr+K=>Tn+|Tfw*TK9grF$SNzf;8F@yjxW%FT60}rVoP$x^;m|yWNj~c`NLZv8_c>_D zf*+4oI%<8iPcw~SSTbvQo<0FtV475Ion=)6H$H1p4G!4cL7GKExr3{Xv{ig6pgpHf z2RM4=fyEH69(;PB==V{a4?5_lJQL?f{Y1JLK5lY3EFWlxJM#O}O33x)CwD%k@wgN9 zki+o9Bjp+7_Uz^Gn-30r;jtm5Mu!pN_KUGyGonyzX(nM?b+kEzAABkuVD>A04ASr9 zSw776JHDY}=#Rm{XilcXv7mFB41PPy`kLr>5E7xm!Zpv;I~sV1E=T%xmMfF}ierg6 z)xiT;(dtbNJj58sl}YvkgeJ%SGshCT%kW@tbZCzxx$VdwD%yKas{xMe;KF5YJ3`Uf zJo+3H#p{?WUdLSVI_8SkF;~2fx#D%q6|ZBicpY=a>zFHE$6WC`<_gWNufGuPf4X*? I{Y0Dq0DW#EeEb8}xXGh!}gY-BP3?cLF`t11itP@Rz%9{m4Lx}SAh zMMxka37}=4F%M^s-6~lk)FQS2E&nb5NUXn3U{Jh{x#D%q6|ZBicpY=a>zFHE$6WC` z=8D%bSGez5_MSg z99WCpp5Cb?Q6aVnXmwl_vevezcWO&Mrc?JsDk&*w3* za8znD?&ENT{|$C*tOGRFqwH>n17+OiC>5yF5glyPpyM9L=TMOh1Q8t;$PEsnlywyx z=O4xWJm;W};tB)4jui-ELOAYCso6lB4eQD|^1r%JN5-ask4duwe6~0{Kr&J9J)&&I z8kf*+IQKEe;m)h5%#n_@$B?uhS5Z$pu2ZSFu66k-<-z}>gFWGFv5jyXdm`SxV^yqW zETDA{fNW#~X>kxyVUU3mqxQ>R>hxci@Q(F7a3zS~0vBGQU>N9_WrOwD|Dfy0D^dWG6it&W0WcEphd_%c-*o1(^1 zDAJocD*4FY-qz1fLgnCdY;k~#6I5_;SQ#kms-yBzUE|;wUr3%#sE!6r?jRTsI?&l= z(&QB~+_B|*gAm@~jSc?X`|k0g^0bm1Wsi#(TW&xe+sPFY4-l6*wjKv{#h`T9oBWme z9)?Cg|A9mIwUbKYAkNYl#dRzYnHxG?OjxQKBnN#37ZFQ&J>G?>I7Ei*DC)ySg~aG^ z#9Lb17)Ls3F+|1asSo&Gs~M_RIlk>J7V|w+I)*Yl=xW#V3i$jqJjk&@$C`I2y}4Jb z%6sIH8Jq(*jnJq20f6MFK^DJ^aO4eqSo)NM1bH;nH^?!b0Z%NtViwr#_c(5?4mN%0 zNNIF9+}#c-U>fZX_mqRLmU|rL%kXZ;nufER9gcR$C0~*DkC!uoaA=&mI#iB0?71IP zc@z#<0A5B8sfvd?4oP6-pE(qcC0qWX!&w}8-n2su*X^JyBwQo@X@{ZsnH?s^Xh(a$ zE8U6JGs?UEzjU+_XonsXi|=r>U13oL@Gpyw-s_U)D@DCS0ZDfb+Z~5x1f$t;G>jpR z#mX4zaL~~^USjR520BoE^p0Oyd)ylw+J;^qzv^f3vvp-h>NVxN8^Sns7oqMDFv!t2 zPOWzciPp-mK;XMr=Y4}-hv?)Xg`+AN;*hg!a-isQ=-Rm|N2BA+eGbQ>SgGd7Yox=W zf^gf1d+8mHZ9WIHUcEZbX@u88vqSlY&gPeohL|1eF|4xh{q@o22!HS^9=o$T!m;ps zXMHFuq>|m`IESi?j?3>?@m*cIf8OSZ3J)ChRSCx)C*Ano`e<;Zm(y?|9yZiE(vH?q z=M`UIo)~zU9rjc>Hau|I=SZDKr9_+;d^uMnmdGF-&Fv#Ov=vfW2i1deI=`&5%Fr&>c^pk!$A5n3NZEHdya$X{hvlwq*c;BFaxXTP zUx`$*d&ohjb==5|3nZiFa78paoYtYp%QKGph{^99L|(;I-le|&U`Oq1a}`IK8h>qd zzax#fP&3a3N6ZdPgJrIEO%C(*6&N=HJG3~Kq9T~}3=Q4>d2oPZIWNi3rQr`8s8NV5 zgx`CbuRno9gm630Iw|x0gAtB%jH1V(Td(HGzh-E6Y{(%&xP8yz*n1s$k~-{I$3{9B zNM-lR6=X--IQ7>-oLA-wk?!Nv84%KZAIDSm;n%^U^~GI@WUyW$REl@}@2x z#X*jDkbETT4te`^y>4x)84b?^ zP^3|FXc+D$hjY(5EMJ)Tkwe9p#gQj&cF5z>Pdn&kC1kV%wRBY)S=wgCU5;;99Ql2O z7RP?q^LGu5?zq#ToNdw(M@GT1|5-=O=^t?{H#?Sl9WbkB9A{B?I^1K9@s!MQUw^fP zeiv+gk7Iqr0k^J)I01m3m$k;(qBlCw)k2>mf0oh14)~e5hY{wU6gQ+3<90_Zpv@u9 zH+jSXAVNCL^JB`2|EVK-njYzOJsam%hvm^Y)@LH1k8R3Yx!Z9*yr7tHIfcXWIfR}M zr+K=>Tn+|Tfw*TK9grF$SNzf;8F@yjxW%FT60}rVoP$x^;m|yWNj~c`NLZv8_c>_D zf*+4oI%<8iPcw~SSTbvQo<0FtV475Ion=)6H$H1p4G!4cL7GKExr3{Xv{ig6pgpHf z2RM4=fyEH69(;PB==V{a4?5_lJQL?f{Y1JLK5lY3EFWlxJM#O}O33x)CwD%k@wgN9 zki+o9Bjp+7_Uz^Gn-30r;jtm5Mu!pN_KUGyGonyzX(nM?b+kEzAABkuVD>A04ASr9 zSw776JHDY}=#Rm{XilcXv7mFB41PPy`kLr>5E7xm!Zpv;I~sV1E=T%xmMfF}ierg6 z)xiT;(dtbNJj58sl}YvkgeJ%SGshCT%kW@tbZCzxx$VdwD%yKas{xMe;KF5YJ3`Uf zJo+3H#p{?WUdLSVI_8SkF;~2fx#D%q6|ZBicpY=a>zFHE$6WC`<_gWNufGuPf4X*? I{Y0Dq0EMn1egFUf literal 0 HcmV?d00001 diff --git a/fields/ROla/alberta.fld2.gz b/fields/ROla/alberta.fld2.gz new file mode 100644 index 0000000000000000000000000000000000000000..accca633c7645abe24dd258809bddf6690978d82 GIT binary patch literal 2452 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 N$Ral*O?8F#{2PwT>}UW0 literal 0 HcmV?d00001 diff --git a/fields/ROla/alde_dun03.fld2.gz b/fields/ROla/alde_dun03.fld2.gz new file mode 100644 index 0000000000000000000000000000000000000000..dabe18e32efa5e9bb108b227cd93969dd4cd4bde GIT binary patch literal 3895 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 literal 0 HcmV?d00001 diff --git a/fields/ROla/alde_dun04.fld2.gz b/fields/ROla/alde_dun04.fld2.gz new file mode 100644 index 0000000000000000000000000000000000000000..4b6d66e0bcb2db5262fd27b6c66c0f22a99d27de GIT binary patch literal 2919 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 X=>LEtzk^Pf&m~M1uNmar6c+jq?N7;Q literal 0 HcmV?d00001 diff --git a/fields/ROla/alde_gld.fld2.gz b/fields/ROla/alde_gld.fld2.gz new file mode 100644 index 0000000000000000000000000000000000000000..ab93960cac9ef93b6cd418f279194b5117499599 GIT binary patch literal 2767 zcmb`JYdn+*8^)z=>rHAS=1pp19kwP-Q7CD)wnA8?)s#cCg@({9g`A#gE1iU>%!_5- z7?d$hIWE>b&a)adITJIFh;bNZ#xS0ld7hbV-|z3Y{d(Wm|HF0v?%(yhuqkWST&zD9 zqO<;Dcu+8j93Hg$a(Ixf&YVCH)AC~ds-F{Bb`C&koVMiP6RYavZ;oycY!s>sA`DiJ z;nMOz1+)Z3A35|Z9WRxy0yvF)46M{8kVZv%Na8;^w7m~$H7VD(zKaigl-#lbklv=- z8G{r58Lp1_%$4gRaI~}{Sshu=AUTrY44w~=Yf>IJ2pa+1UCu!KR*KBMj5eT>O#$AT zoF)k^3cNOOmoyJYc5Vb73?i>J-c2fb(FX}TH8_qlB%wsJ#@wW&EpXnikTrr7RtN7K z;rE`}%2+?S*GJ+#{8%`)o7AAD_G-(ILP)1S!C*%)SKG z_lj{Aj^ogRLDabaTVw`Rq){nDhUVEbB7Tu!rTq)BW<9~OLfxg;J%yr~GueZh0mnqS;N zQU%JF;Wj(S^|>OWCErh#9B_uvo!;X``BvGH(0m_wi8_?uq)%d2H1j7g&b)z^^rX(H zAVTrbu!Pup8@XZQC#@yKH;$x%MY6+Sh1lQ5a)-i+>heUHbctD1L@LF?wa8kMdNxus zBJi4PFBur%%U5^Jpw+2A9b}N!IXgF8;Y9!cMHdd9`>zfiF-=kbBBLuaf5m*j?FhqZOZ0*NW3iWK8EGF`$3ivb4X zp3=vedg?QG+Z`=;flu8m@G>FWf-E{|FqQ&tm&%o_B_K9m+m%mV(8Jsm6UV+>zfYa< zm`jo7aw(SSCeff{%xM9>>}7X)kOv6GF3`+#VVgoV3!w;n2e8bpSr-U2Hr`iZZHO0$z{PXOL*EYISxBr(_V5#Nx~7r+mU2LR7>ZYRfZaG`cYa5RnthgM z{W2y{6%eiQ7n)b8yyxnavK6jP%WUvtpTlnuVCDFT&QZ0?3cc!u4VoDu^ca{GEK`@5 zY#M!!4(^XA$LODuhI9HH7|^?AYX09$6q{bI@L*CL!9vB`PravmhWlP|bP2aLR4?F5#kKqX<+(mqUYFW&r6Lo8q^tq;m>@>J3K%Rf=PxnM@cd)Iemh+FnQgGr zHUm4ab-gOBx70_fVbz&HYz?5*x4?N8L2KCiG-z@}nZi7de06xsu` zR>J5}(k*vYa+QR@SXZd;ckCPS7^ZiV$+4P>>r007D_s`+7{ligz+KAP)9T9e1j=%r zVjB{0vXlqLQ4W0VZVsfyR&ya90k-cmgwSxFbu8nk+h zAaEpU^a;9XGxQ-7k_E;;GDFhd$*pRAjPydgacnIj1*flY%#d-e zQdLG6&Q42VXV4SRQtx5$cldj|it8S3LDRGgms!=5h~)s;b4ykGHUMb{Q`G+N{i2`G zB8Toqpt_dxTm@TNy;#vz*Q%V6?`^!$4bi5N`xN}Hk0ak$(zubF4+HWyu9qVSEzShKc$Eocr-xt)Ur z<20&J*wTJ^g2(Pv`E@>;?H77}$(x#=+eiawH< z-OD~?rwWKgZj?atN63-u))CAK=r9uclk1-P8db*wa->${o&fHtQJ>fOgemS%H^xMm zTg~s_Omx3Yog1*A{iqLc7M*}_KWWU@@f(4Q76jG%CUik2TLZ7!sDL~dheww^XLemu zcAc`(Rn@NrHiNGC#Feklz4(_|#F7P}j+x2MAeGW`{55~h?W16_fBIVN;dSZ+#>1i>#j0hNmd zLd{w*8WcHknkBCBXS+@lT{-mb%It~mHi?s!^7V*`|C>h1@qe-a{=ZVW3rjxo6u}PX L1s8txc!kbijq)|y literal 0 HcmV?d00001 diff --git a/fields/ROla/alde_tt03.fld2.gz b/fields/ROla/alde_tt03.fld2.gz new file mode 100644 index 0000000000000000000000000000000000000000..acea530c8d44dde3aaf0406cf5729501ce6023c2 GIT binary patch literal 3102 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 literal 0 HcmV?d00001 diff --git a/fields/ROla/aldeg_cas01.fld2.gz b/fields/ROla/aldeg_cas01.fld2.gz new file mode 100644 index 0000000000000000000000000000000000000000..ba6b1eda0fe7176de75034c54b03464f70190277 GIT binary patch literal 1573 zcmV+=2HN=_iwFo}yF_RJ4qX7W`HMwy0?$*AZ zgaaD-DEW5Prlg^L8G3D7`ssE8Pj9x3jd*EiUoV*Cp0M`*bL6j8&u8LgkjVS<8}?q6 z^D^E4I9AFt1PfAjNW*ZucQyi?Y{%Bw=Je!;7md25oD-WlVScvJJ& zc}E{_eAGMbc=G#R+ndGPw_F7<*Eu!%XRhfQm4fyupF(uLk52p_cngvSubnj9JnMQ6 z66Cz;#BT9^)0-01y@?NCiZ`D5;Cg$d#oE5Mn(NKwxi_=ab>8InDU7|fnvM4U7U=K} z9aIF+qbSrC$-BMLMzrXHS5-b^tl6QqO$Y0vYB|B+nJ^}Smd8KdFH^@tDzU(@f5s_ zjzu1`O4S_Wt*)UEy?-cNc;0LP7ogoMdfv|dOy0X&`*wQqo5Z`5*DyTZz;6=of{3?RTD(>6KHlBhSG?A2 z#QRdjTgt=pK}F5m*}gk@Q`d9Ic{}@a@CnfKcC_zqUKagX+IMqrX~3fFHGj8lnCK2g zecaBGu~YU!7jEACK8dW?Kto3DdVq@}>n%{-jJTzVdJS^eHZ;zsG?p6hjH1_TjLvPX zxvB1c@Z%U{lBgo+E1m1X*HKjD%#B{g5NcmbL@%Hh^xDD>d6K9i=qsITx}&e63Bc`q zq`g7^47}IpVE~$_*VZ46!PoeVo7_Gmwrcxxpx1L_e{{4Bu77U4Qt2)D9H=LWDuTY! zxfUyggEwNB%2*tD3H4`W%pX%W^nppDilDDFY}wM85GQX#Dpr#Hxo6A@y`;vg=h@Cn zu|G$-dAq~Ax#6`>+=PVpoTrK(!DvdGV89}(^6^b?L|$yr}q=r47(!Un)lekck(L5#Ouc9>y|gyo?N}>4b?R-oBmw)y5kcb-fDm5 z<;CNbc*QH;uLpXs*W%s63p+S7uW~;f@jt1E!pZA?Q&9IB23{@RqE~I_HR7d_&ArEi zo9v^`Q)BV2u77W^_k76zu_l(@Ld3he{=E@z?km#cu!^B~yY@|#0I$SLDk(2!TiH2{ zrPtcrE8bprEVa9r>GEvQ~SL{-NZX z%Femvy*^yyFLsZt^n*`Mg2wT9~a zVljDGjc2Fm&Ax|;7hel&-5-6ZKi4zB`=Rlwo;%*}j?eYvx8L)ZA4v%p@A2AR`#2sG ze}Z1I@~YTtLLHy&SEY_6QnxjQMV8e4PU8MtN&3#NHI=`S%=Mf&g_)GS_Kw7AJG1fH zop7l|`FO88090zf!_up-i<`wQmWda9@z83e?=|s%0e-#eJoux|fcyvTG^&tIP&;T?== zTDN}=CGidk?3x&Q!{*gc8GY-}<6YUC+r0qmI=8JeZEx!PP8}Un-uHg3KPSDY3){{4 zY-sAigyREfAwaaW=6ch zY!ydz%T(c9&J#m&TWg!cddIjL=z literal 0 HcmV?d00001 diff --git a/fields/ROla/aldeg_cas02.fld2.gz b/fields/ROla/aldeg_cas02.fld2.gz new file mode 100644 index 0000000000000000000000000000000000000000..ed7813d9d61aa8f9bb47d6e367aa54ce5f08457b GIT binary patch literal 1448 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 literal 0 HcmV?d00001 diff --git a/fields/ROla/aldeg_cas05.fld2.gz b/fields/ROla/aldeg_cas05.fld2.gz new file mode 100644 index 0000000000000000000000000000000000000000..32bb352320e06e2c421a0dc5009d6bc53449f572 GIT binary patch literal 1351 zcmV-N1-SYjiwFo}yF_RJ4q^|1;+k zW}*V!&?p8FfqSa9c8spgNw)}QGyJMw1pokGRFBU6!goa9pB4Cy>bpG!gzu=n`lGAiJH7Av zq~AR7@9(kyRo{jCcK1OCe9wG6S_@pnx;ogVUK!d~fb>_sa^v=2Me^0b8{l8R22%j7 zT7R90ucsW=v!<2bxv!k%y`I;6Qs2+J)7m1PLtnOI_hpB4?Ca7QUcMG3`F2^)ny?q` zUb);?GmLj@i*%eXO`2AI-zVGMhobmPkm&cI4v*ovr?<;{;zP8JHhrZvAH@fqU&xsR# z;}7jOp5=Q~{RMpgJIPkQZ+8nmlO4tP0{z!>E#I@V)b=d|d_`WbcqMn@8|!&Q{(Yk# zBaiDV|6iX$eXZGCzTQc6H@+%Vjvq;U**_THS5ss7J{tJOfNyuczU-kgeBpa*>#tM7 zcO8BV@9UjrY~PShd<}=5hJk#OfbSf>-9N8OWKQ2BhVwo6n#&!Zr`*egzLV8+>(9II zWj0>`000000000000000000000000000000000000043@Un84^X?^YAYF}b^TS?3v zWREuobP4w*-;V3_%`-h%C~;=7Q=;6q~L&LV+NDWMpI^;S#f*;bJYu0&vIaVq}q z7AN#&*(S$u-y(q<`TF%tT49eN+ui#vUVo*v7mLTQ<15X_vxCTC!59G;!pLE12(xuK${f&lzbpF(-|CwkMF`7N7-ZFNWADD}Y>*3|E-B2O{(Ku) z@C}}(l`!&q>LNoEeSBJjk6Wl7h(v88@ zvm4sx#|fi8!q;BO+dgwdew;As!+Zy-qzp$Jf2?_UFgp zT=%Wh4ufCgrO!XNZ?ubAd$ZYmKg{M^Z)}uq$;NEHj=KE>004kz@CVZe JVTuId003sL5Ln9q7Q};B}DI?5TZpdqZ^4XdhcOG@4d!o(R+z9dN
  • 3Hq~-^U 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 Sce?Q2ff%;`c^_P`AN@b*k)x0R literal 0 HcmV?d00001 diff --git a/fields/ROla/arug_cas02.fld2.gz b/fields/ROla/arug_cas02.fld2.gz new file mode 100644 index 0000000000000000000000000000000000000000..1fb6244044500ed545d19f9a501222149ea5de43 GIT binary patch literal 3281 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 literal 0 HcmV?d00001 diff --git a/fields/ROla/ba_bath.fld2.gz b/fields/ROla/ba_bath.fld2.gz new file mode 100644 index 0000000000000000000000000000000000000000..5b4d9b0f275b0155a2ac70c37152e2a036e9bfa8 GIT binary patch literal 3364 zcmaKvX*kpi1I2BjTn61pVs1p3G`1+)BxEg>kkCYB3AtpcCjXK3W?!?#Bv(V$8F{m6a?4y)`^}OTRQrHQc=uxaoxR{wW!n2@DP33L6Mtvj(b^v1)$nRU7r&BMzWpD@ zpc?dWYRuQce-wZ#3V`YV#3X|>ZW&H6xudsAW%x+}E=Giw^1)T5u7|4eEe3iwEvzs2 zUQ#1?yGtfCo84prFsEM{NtV^dZX7ebeX8MSd$ZYVogi_@T>+Jh9@^VjXY)hXP1hw* z7-ABE9J(n%K+D!H z$u@GiSBP!0{6j&3`M>9^MRKkJWvodm!Zwx9QlePWx{kP7{P|14cU7cGLZi zpwp=l0C>$uNzikq4-NUyTb1v~-4U-oVu^MCkctLO(%=QL12AU(B!9#-zGG*Qg$xVY zrv`~7KNLYBE8QPV4qNIT1bGAyB+d{XizX)J?rsV0TP2EF@Z!&Y?Q| zSJjqj`+9XWgqAgQojd&nwsubi@H$w)@5m2cqPM2VuK1wE`3Udy6Vb(BDOh&nh(Ad4 z*y*h0ka&?X3!u}GfL+tN&o!Xp9F}Qnd#4Osy|U|P&?gw=f$>OylcSFv`?u%}tHu>o zV+mIHn;kTnjHP=*qQ}3n%aRs+q$Sn^p`TXj;#>MFF4-14ywEKlx;aIkhL}h*px=G4 zw3h+Z@j1`?T#@U!_wUtnx?#z5%9h`$N-AR$mtk~JPD&9aueOfJ#5r_irQsxJ8Q|Gk zf{Q%LG;0^^9GG!CISj>N^FZ>siTRa$!sS_$6r5`U{P3qY?oV5C4u0+1S+F9lHJ>>L z2uCC6j-UH$e_~P>SrmhP_G51NpYRu}rq{ojpN6iN~KEsbvy}T|j zt@3eUC>*bwxWa3Oo^DVXYyYL92;HdCW$M-#B3LFTS3staPd$*9Pto)A4{5D+qmAMa zj3ntkM%X_)SAP{#0>CJc&@A%4)wOl_DrPmUpbBV>7FNxnuHpzf;q9hX^tPp97<#u15zSTjxH4vjAmO21WO z)*+5%gGuB6#KE11KTM^=?JYkQ`GLW&$`9S2s^7g_xM1~Mn|iq84aNNwE?)ne!3>Byk5`XTr7BRI(H%V8w7} zJN|6;KU!L18K8hPUoUmPdWmuYo)Y<4TvE(RUp?`BSNu)7z;{~;;`)rZR{q<3^utB6 z&+yx{@6fR?3*gd$0_R6%-BvV2QnT5+lrjyidpcHz&$3HW;iEXz(y7@pjfDKM1#?YD zn<_!nXN8}1>f~H_XnOe24I82Zydc1NdWOl@sY!;R?)K$`u|Pz;?kqw3 zM>lLnk6Jl1vd|Q}39Y}~S!Ou|LYFpqZBm%=X52QU76BvU0bYqa>G@9jQCS&n?(2*R z*Cz_HtG;sAW!P)EhaY3k3bp0gpGUC$UX8~mpuJ0&Iq1TRw{0^REWx6-UyTM|*T`0`;Ar`H9pDg6Z2Q2I?jGPI$B6`EKW;zTh#}?_j#HF_IZEj=b%IH)RztB>FcBs z8as?r_iQtCeRSe+{SH3QUkyEuTQ_4&Qt0*h)8*gicEqg0?G#t@MGq?5=t}g4REfDCVlt6K6(Y z2zTMg=d_098f_v`0l>!5C@IqdJq)sl{f1ICC|I__s_2=6jWQK}1^fUoKGk5XGvZXX z)Ssf#mfD`CZ!z%Nx*XGT8GBVtDWC=J~#_*3&_Tq9{|wPO=UJeA8ExU0_K}g9=NA z&~c3@SN8dX4(AC<#JGIs8(w!B)e9_QD~o>{ehdD#ATN+E^C}Z?s7lK7|qW0INPszi8*IjVrFXZ+zJwcbHA#tzTL zxLf+hf&Sr&iy9ZjJFPWG*msG`J!}j^EaZ3PO!zm8&?oHXb|yu3(Z?n-v#={@5Ok{B z2$D^};Scm7MaiiQmqKu|3XZDJ9a(WH$z=EX23&EBr5#RI@nKm8l-pJKakOZdMhSwj z6ta07nya}Cio0dUD%Q1h!@_%<9Dfb9NwQD4SUV*$SU;TVY3o8zzKMCaC zxY~d*Andgip}~Dhq#&9J*=BIava`KH0Bj5c9#D{uSnS_hdBq!wCKr?tokom^9 z=%sD9sfo$9AR5`)7ZRXW*LD+6CBpOD z3P=d*HHa#I>$WltBl3x`-dF3^lPO#bmjv3O@*iDC#f=F{(8yvFv(3fyq#muZ>Wz}S z?OeiX1r!nHw%JQGyqXO`QmSkT8C~VtBOVRzMxaV6`nQh#bLI0sEi#ZYDTR5AGPwaA z&}&zIC+&4c_SXwW9}U)8G6SrVsxj{ zfP(jXvzSX4R#rY<{&H1<;HJ!0-#*wWm74W?(X)G&4Ee)k_g`P(1q{47O=cZ|D;)Af z0~wx?9tC@pDNQ0$zb4JWG6A%)fdMa?XYz2#hzzDqxjo{TfBjiM05CgRsD-Z+sF)%N6z+fWeV?i9&J(nKc5O^3ouBVNZs!Lc literal 0 HcmV?d00001 diff --git a/fields/ROla/ba_lib.fld2.gz b/fields/ROla/ba_lib.fld2.gz new file mode 100644 index 0000000000000000000000000000000000000000..4a4902da510b75f6b121364fed572490c2330074 GIT binary patch literal 4084 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 literal 0 HcmV?d00001 diff --git a/fields/ROla/ba_pw01.fld2.gz b/fields/ROla/ba_pw01.fld2.gz new file mode 100644 index 0000000000000000000000000000000000000000..bc24c7cd7557fc7d9ae659c64e9008fdd559044b GIT binary patch literal 3454 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 bH3R>R{{MHRwy6AyCDcy8wHKS|E-v;zgSFv` literal 0 HcmV?d00001 diff --git a/fields/ROla/ba_pw02.fld2.gz b/fields/ROla/ba_pw02.fld2.gz new file mode 100644 index 0000000000000000000000000000000000000000..bfefd2ac48b510a5eb60d295eec953e38a1e0e6c GIT binary patch literal 2597 zcmah~dr(t%8pXAAEu|o}h%cnpqDWK(i-Ld@EsCK^)xAGLf*|h@UJ3>zyjn4$Af`n@ zd58+eM{aH*^>0HG)dCWI&&~OETsZkrAlBTP*WH-pOOBxtxOsk z8@wEyb(;t+!w;vkt=-POT5%Y@JRfkfbDNAoUJ8?A`-Cd*p00dA$r-+;k-ELlbw#oL zDqq&CG-M*x-|%Ku3}g;Oa$A0DYE*mOAihkUwR9q9_KfxfJit0;M7r|b8)lg**c%BD7~#V^4T2(G(uD>yLH4Dyc_hjzMCEVKL~Ki00z$cnRLoRQuf z>K}-2XHDw+WPGjo8}tZ)N!tOdEL@T4<;v<_p_a!$9X^K24CsI53PrV3u=79MP{w(6 zlR)BpazNiZ8MxQPdK2Dsv0IG9Sn;@K-xpmbBKUSHUBZq36r(&(+u|Kj+@Rckx%k7r zR7xnZ|2%hKlhzfZADk(JW)p62Eh#qsJYWuDLURY1+=hbi4L|VeN&*Szikxe3~pqs_z^qZbP016G*zt$g57 z5jet605!}fKuZM=tbe4eOJXUk@WZ#PD|tBW*Vi98u4vxxUTn1_<@)yTkB_E^(mQ<# z^hmnpHIRQmmL-mhxt}HCuqX8Ke3>GgNPdb{4(kPTs|LKSod4YJYs+k&pI_d9!Slla z{g(7jxn#9BLH`ZhF!@6L`5y%pU_X&MmJOd&=UzvG-ES00>Mj{C0Ox(-+Q28q^M}^y z9Hjmk1?%%9U{W_oPtcBvzRw@6L6grx)97<2Bc@=q(TLVGNdr+s7dG@l_{8LguUf*2 zEvE1!CYn`qY#|zcFnJ`3(-~!in;2(QU7{F2pqK*7En3#sj1heZNZBMVW!O^ zqI0=~(`7~@MSU`R=>V?Z82s^91*=qJI}DdVzvAiOh0@oj$icfJM$>MNX&k?eTmF|$ zYw^SPS#xW_vqVczt|fcdE)IhJQs^1uKqyvxAp6)Vci(%EYwR=PuXQ{8UUb2=lO5HL zwRsyp+Dx~CtY99b#07TFO|!_OYg%D+)%vA>la@ZhW)q?iSYUtFWok3Jmq?8xS&pUjf?MWDC6RN93#H#KHV7oYTfz#}P$b#l$==Om@a`4^C@BDg zFyywny!#=>JqGJ@v%kqsu5)5^^ESD37FA_6iIJp{)kcbRwj}wAj5&- zVbI4-;&orrPi1x0I2GQaLDO;{ObQy;#!ZffmkX3?4cg7sgunwq_sBgf)iJW&Z=1TY z+>Ka5wGjFMzq@CC)*Y|Z>)&?oX2lded5`HiQ%5JzN&)gB)u_doWE zn_Yk)ht~AR)E=p&;GV4KGOK*Z?4ng~F=2Pv3En8XZJd+6BB-UNTRJ#HJz?5bVXc!c}H0`$f zxxCx)Ve5h!=4za4FJsuqPi>NLSdEX(X3a;w06u!_WOyg1_(#{B*)83r3kE$RnX z`60QjUved0xFoc0VPAgEPZ`;Hstds$m!bKg%d-U5aumFW{hW_ z2lg6gUdlJ1bC?LwIzu6}y_7vm76sd4w#^U_itNRI%kh6T`TtOWB^*4G)eMh%h$EQB Of84FrW3GfQGWj=+Jgv|G literal 0 HcmV?d00001 diff --git a/fields/ROla/bat_a01.fld2.gz b/fields/ROla/bat_a01.fld2.gz new file mode 100644 index 0000000000000000000000000000000000000000..3578412e79c5c4984dd4a581b9179c8eb3192d1c GIT binary patch literal 8611 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 literal 0 HcmV?d00001 diff --git a/fields/ROla/beach_dun2.fld2.gz b/fields/ROla/beach_dun2.fld2.gz new file mode 100644 index 0000000000000000000000000000000000000000..4938512b4c7d49dc5e0987938b172df243fa6c7c GIT binary patch literal 4418 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 PTgG@?l0@D$PLBTp&m&wW literal 0 HcmV?d00001 diff --git a/fields/ROla/bif_fild01.fld2.gz b/fields/ROla/bif_fild01.fld2.gz new file mode 100644 index 0000000000000000000000000000000000000000..4dc40052d8adf223b741cea2e5be82eca8d56cd7 GIT binary patch literal 4343 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 bRj>a4Rct8U7sU`O4ur3BJ}77QKf&-Hc9?S} literal 0 HcmV?d00001 diff --git a/fields/ROla/bif_fild02.fld2.gz b/fields/ROla/bif_fild02.fld2.gz new file mode 100644 index 0000000000000000000000000000000000000000..9f8741a79fb86e155d37e1093c8e3525c69ea029 GIT binary patch literal 4651 zcmc&&XFSw_!^h!B9J0sn4u$MZTqqY6g-Rm(M3;G1cV(};tTRfNq%uR9*(3XiGcvO| zvgg^Wr_b~9dEWp3FTd;a`M&&qzR+kUCZ`(2QyMl0l#{I!%GFUpNzTdDQHf^l!$e9( zH5;P&c&bZm_W@ftCm{NJ+wO+gL6IcjqB>(rA~~SYRywgO z7s79MQnt}n0L#i#c2nMLnSJ%EZvk|3@+usBmkdGS7xoyNa2(gyW&8DN5 z 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�v}JFdvKC*5T?dWPsr`_3i|F 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 literal 0 HcmV?d00001 diff --git a/fields/ROla/bra_fild01.fld2.gz b/fields/ROla/bra_fild01.fld2.gz new file mode 100644 index 0000000000000000000000000000000000000000..139b8aa49c9dfd68dff04e5927ada4c063928fea GIT binary patch literal 6350 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 ad7tJi21|i2*WPdb!aptH!=5Lr85jVhHx4}j literal 0 HcmV?d00001 diff --git a/fields/ROla/brasilis.fld2.gz b/fields/ROla/brasilis.fld2.gz new file mode 100644 index 0000000000000000000000000000000000000000..222329a0f8ca9bfd82df0b58a9f7ac4ee561072a GIT binary patch literal 5604 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 Q=M-uk>z#SY`y$1E0VxIz%K!iX literal 0 HcmV?d00001 diff --git a/fields/ROla/cmd_fild02.fld2.gz b/fields/ROla/cmd_fild02.fld2.gz new file mode 100644 index 0000000000000000000000000000000000000000..418ebb32ba4f7a937f456c6c6aca3766851ce0e1 GIT binary patch literal 5473 zcmb_g`8U)L!?h13V0)&^rAON8ty`z}i%`&b4^ zvM*y~--b}xp6_$cd(Qj&d)^=JIrrW_;NE-gm1tI0=g-<0Iu3g`hsRDXt`4&D*PL7( z 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 QcB?@uYTK+#o(y#V0maFI{Qv*} literal 0 HcmV?d00001 diff --git a/fields/ROla/cmd_fild03.fld2.gz b/fields/ROla/cmd_fild03.fld2.gz new file mode 100644 index 0000000000000000000000000000000000000000..ac6f7d53d7167cbf98ec7c001855d4eea72c3942 GIT binary patch literal 7101 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 QTCAkz}eFwzQU#(j8 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 literal 0 HcmV?d00001 diff --git a/fields/ROla/cmd_fild05.fld2.gz b/fields/ROla/cmd_fild05.fld2.gz new file mode 100644 index 0000000000000000000000000000000000000000..4fd32e0512171959ae65724e1ba8435b8c253961 GIT binary patch literal 6493 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 M!NyO>OMvfx04JBCA^-pY literal 0 HcmV?d00001 diff --git a/fields/ROla/cmd_fild08.fld2.gz b/fields/ROla/cmd_fild08.fld2.gz new file mode 100644 index 0000000000000000000000000000000000000000..f231c1fc55a6896db0b522b8fd4678eb8dc6bf29 GIT binary patch literal 7790 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

    yIl5HZ-I5XRoBTVMw*#b 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 literal 0 HcmV?d00001 diff --git a/fields/ROla/dic_dun01.fld2.gz b/fields/ROla/dic_dun01.fld2.gz new file mode 100644 index 0000000000000000000000000000000000000000..73f66a0346c0338d5c7bd68ac73b22b13aca69b2 GIT binary patch literal 1446 zcmZvcX;4!K5XVudoN^QzwUmbzENTY=I22JIsKG`IMa5dCoN6KjLa{;47r=U8tU;n! zp`Zp+KtL+DMo7RB2!Vh(z<^v)h=c?TL3lYdAO*BD{m}ieyB~IDcmMO-wWk;v;hDa8 zJ#$<%HUgIz=j!GX8H;n*Q;dwn54zgyKjE(L8Txua*RhuVS!`EMterg8Z;C0aFP9vM z%A5&#KQT3%xJqx?_9!clh%0XKRo|Wmo(WN=V7&l^sW$ANh-(2^K^{m*J2=d~ORkWq zJi!7|b^);MaT=aZMnHJ0DYu1@fCqrY$K3L)H+JGf`#m&4e6+%eAfB)*hhC*E%yx-0)*DyvusmSt&CuVjcr6m@YS*#prlRmPjI- zh2uOD#Mp;RecpwJ@@~tI^u`n6Umpbz6L`mzO+O9$DnYf2eclynguvls!wyKR3mw*# z8iVumw!JyR8GYGxOIDAES@k}u0ff)5<1`FSmIVBfQv1u{R7(|t;9tY(UYU&dBx#T| zj{R$SqnZTn3~&Z?dVoRTJy#Ar%}Es z2gaj1ypbS+OR}Nm0j3(#ov&eS!r4x*TAqo!MKMSEV&n;TK+AKB0cR(O(^^dwn%IUryROin52}uo5;WMGqa=oOutVX`EpY}s@hPNk7>zk`W^%`X` z0NP-ulJP_aALB21$xP;+Mqqj4w zC(xuS{bq~ljm$QzCC&k?=CTa$I;p62{z8Y6XmiMMh1hyUP4rk(x)&_S@oZJ;0&`i) zZQ#jormmBa9g1LH1A1y>p)&I=bDo1_D2NR96H+8|!n1)YT#H4)WW!4285q zl}cj+36(pu8XL<48uzZil2L_WmG;s!FOr2QOeqLpkPB&ku%Pzwq4R$fww z{2d6ayrbSS4r;>S!*z4oTus~LEC88-&zF=P1;FrAX|q5|3-8$F-}sxR)6<-l-Qw-k z>tWJb4v*sruAZ3c`^3B4ct&qTh z5C-2h1l9c>1ZvxPB#e*Ic9Gj93OVzO#m%ISGFS3zgyPDNDHUDa44aN!Z(iBjtT&e` zg5M4k$Vo%z3ao2=lHXKZrAsfq;oIqAFip)=zh}k+g}c_RAzN!v&*0D-~VuH T;Um-f?@b+(_JkW;Tc!68mbnM; literal 0 HcmV?d00001 diff --git a/fields/ROla/dic_dun02.fld2.gz b/fields/ROla/dic_dun02.fld2.gz new file mode 100644 index 0000000000000000000000000000000000000000..05a8013341fc350b68f691b5afceffb98057e4c6 GIT binary patch literal 3072 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 Og#8CwnmODA;Q#;l1JyxC3* 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 P|AhSqTbeoC1mOSxscb!M literal 0 HcmV?d00001 diff --git a/fields/ROla/dic_dun02b.fld2.gz b/fields/ROla/dic_dun02b.fld2.gz new file mode 100644 index 0000000000000000000000000000000000000000..603aa8ac3966fe761ead80d8b9fbf6985de382f4 GIT binary patch literal 3073 zcmV+c4F2;UiwFp5yF_RJ4rFO#Uu1P|Ffw8;W^80K0PUUIlB6IEhNVhxyy*9T%zcEN zM!^u0kOZ{t8B?`8-3rP70m{Kc|C|0zf6V4j3wZ3Y#~yp^vBw^J?6Jold+f2Vwz;rR zt2xl1JyxC3* zL`Tnzuh;8*-jH#B0(}DbLf#Ny-Tr#svK21}@1@Wo124(WIYHOB%Lj0$J$g8B&jAiH zZv;M*dah(tyd#P6Ng3n5fzPbvou@8yK-jO7ouEtSR0axFbNssd%US?GC&Z za7TdUXru2>>zW(wnqm>PJd6)p%{xKh?R4G28xSw3-&JbW!vK+``ok@4f#&5!2qnxx zxKtzZViy^_;;U@XR6kX^`|wQalzcmQujGZVb)3R9e983zU$#0y*N2AqiI zQ+Uhl;n=iH`zeZSNn2biFVm48j;L*Hd-mY+yzd6Jq{x-P$ZI`*XQJc~n+%K!eE@F& zr%2G>O2tR>ON7jiM_lNS*Nv-eNSjt}p7iYV*#5aFL1IXC`HO zz7>vX>b)_RLcxP0yQWe%^z8`6WcxChQ&gGPuwlsFp&b@#@Yro>gNwuEWyvGz$LcU4KZC3OuVhNoS6e|;tpeFD!1H$ zCqS?bM|=G=<2LWi@-jA0>g_R|D6C&FW=fGjQyU#Zy4Psd@7yLBy*I)8btMa5|onWRmvPye|`P z-5G%v1NL3VOYEx$nn_hgP-`oAfxD}3J>gi3{RObE{0Z84%sOSet;JjSOMSd}*yvO3 zH)H*}6&}_4Szg&{S~u@7Pp8^2;GBECGcEx<%Fo?-)5lIXcjpBsv^Dm}UKd}*8?~)( z^Sih|FVgmxc;l+?Sls&ozct~7L5~m2cUSWF`Dq|8*!ba_nHgJ$)-SX9M!W+Au^#To zyLPHsUa#ewcssSsKfD|gZqIQ4$KX4Qem>^*jBAqjbCtd6@U8yNvk@0u%>7*J_P1!b zbi?{p@m|Bf$5_38aUspLchVSygo zWC5`Az!A*mBQ`uqfL_C$jYfo}m$cH}n!A!imRFv(wLb{h1n8G4?!+uxG1-yIp@(DqLUf!1J z{duG2>&0xsKKZ<_4YeaeY_fV zEOXjNrfJO5%d#s@S2Az9@z%-axFcB4d*mQ^X*-#hD*ku_&-Tf(^2PH~ zhA{IpxgC>2SFX3M>oH%dCR&R8AK`Uoi@a3l(FDd>8WS>%C|BX(LLcRcI&og-12IbyLbaR{uspT z17=1_BYCSGgLs|TByS724Z8iIlfonTa^CmcbzSk&LBpwr7kwPWTLpJG*m=QNtyPL5 zXS=y~z80|xY=qu;i?NH0%g>MY_)1<6EFazP$A0|o+m)k`vApr3$pAkk4!5#bbpOg> zSLZX*wL{-i8a!*hQ=(4D`4Y&N__73tKvw5)-U1EpZh1p_;*8KCPuA)2LptLEw8Es* zKqb<>wt@ZkBGYiBYb9?w?8`hucx__TE+pQTx56_#o(J$ISLGSf=VNmOJ$QJL>yp>k zVG>$<-dHLk_44}jYPI(9#trq}RGh_IY#j28(wZKr>)9$7upN{2yY9SQ^7?t{v7B2I zzt8u0#7N%MZxY019T`-m@AV=@FK^K+yR0=M@u@1m_joNl*|IfMYOzCkWFjxcJ<0=E zp&KV6>1z~k0L__n-=)zB$@(KvYu@7oOAK)nDDApWpA(Sv$1<0xe|Ymyk#O?fNgl-A z`n-gUyEg2eys<==b2VJq^>u+VN=cFX{VnCdA}3(-(XP5Ap1@1B!ng>)7YO~WwQ^@y z*#B&fg7w7QUG?-V3yRX}6tBD5g zkm$^+ktH#!qk|k}cArXbR$5iV%Fgo>yxyFQDhsbt`8|1|E(LEsg4K>e2*2o_ytFKt zw?MV=22&o~G`MP4?B^dsX`L^O}&~&Sl0?yd!4}ZQfD9-72?FQ`c|H$Rt6yDr9!= z)(8hh&7X%e2(s`xj$pUO>}Saj0CzePcsaAXcWX?v>1P1F4@zP|(aM{^DALvNyMQT+ z_nmY5MGErKL6^SFz#YR&Z6koS zmq$9^d(ZyXoBJFM8IrIdbh}PoA2a{ccH`Rfq~TRFSkhvXe#!6sY6=G~*`Pr+ck@Cw zHeoz#G6$`Axr8YSXsQKe7q9#?jf=HE($pPQm5%!L^2)@<1NbUl*eI;HBCwCw2JTpM zw4}wf%?dX2PK{0n!%;;rmUk|!)u8prhk$mqN;a66#jWAD1SVc}uX7nM*OG~C9$w?| zYa*?YRjDn!iV-BdiAOOvL%#7d08)x;HkEq`G$H_}9 zc?NKJQ`2PS!(kzq>laj?Z0;e)_N_z!yc1E)@Req*AotmKA%pRfp>BAzmuO=Lt$tNyQx}o2d}Q|S=um(neW0|AXw8A zEnAgI6~=^XTH~?Wmwbuc{S1Q#A|=csvQ2ja*^U6@!z0h{6jpN}+(-Ls6NELm5Gq1zA zrc1TvYk2kbHH?=jf-86_jci);3vt!B_vGag58#c=AK|tA?oY#at5S=vOKdR_}bN)dE-xHJYEXsS3Aj032C-%G`Ne3hIFQ$gw)%P=Uxh`JRJPzz|* zFI~YiVb}2j=EiMjRfBKhPI09jwcDAP8C=HuHpBVW%v)a_q;NN2uYRkY$U9ZMGtGOY zg~mMacbU2``X%OJ@(jRk3II-V~;)d*kg~KX`ft~ P|AhSqTbeoC1mOSxwOT!H literal 0 HcmV?d00001 diff --git a/fields/ROla/dic_fild01.fld2.gz b/fields/ROla/dic_fild01.fld2.gz new file mode 100644 index 0000000000000000000000000000000000000000..39cd3a35277569a86fbffc9b081a7dbb16d5b9d1 GIT binary patch literal 3259 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 literal 0 HcmV?d00001 diff --git a/fields/ROla/dic_in01.fld2.gz b/fields/ROla/dic_in01.fld2.gz new file mode 100644 index 0000000000000000000000000000000000000000..419e79f2fa9461513432b81222b65a4e595e227a GIT binary patch literal 2709 zcmb`Ic~H{_7RLovMTJI04gvkGO++LZ4PZfzA4)h3H^l%V$bCZu6e2VUr5pi*WL0X3 zL{I|A5z1Y{p|YX`iyVOW@>e{j97el{Caz67}Q)&SA-*KCrMFlisb$9H`*{vf%UW6Qhk8q+ z-HqT`pSrb8sr(=h*2ZMb78G;zZ=!ow_jP9wD%vL{r}_! z3eQUQ-9RM4$A@E@9C! z#~L`${y8uzg|V(cuzDDCmON2LXP0O9)tTohV)xqO114j^>_d3=$V|dVTX3ew2$@@Utjzi8YTr9)0=kax|VMwIJ!n761C0INtD3 z3W#?!NFW9r@ABx-R00(DfrX*DFxRx|kJIEjBjR-vJ{^xek$3ezGSQ4uqM*|BD{JB9 zQeN3TNqoF8y$iuQ`zN%nhhDCbeb6&X!t2k}8rbAkS61;N6nn}yZzI38f;TcDBjUdt z3O#*w^Hsx{(T6_=-uBW`b^*(JE9$bE1_yL{-^r@@dt1~?8_Vd!ZRqA{zn#%MQB`}J z2_nqv^4ct|pvVzq91&)k8hDj_1>hGFYH2}yZ9t`Qsyh9#RQR@_)Vj939b>tM>bVaa zpUo)#mJ%nAlN>%ywFfqQ7p?FCh+3@-%F3MY9Ww&4-nL=dJZWW5h-7`S@JfY)HKS}* zlj+CRb@*!>;M<1=p{{VuRX zJ*xzFv8?HMbXt=R?Y5yC*z993iej&}+_APwuz&+6VjG4k@HZ3mPDb`i_#!(cFTt$s zHRB++=Z??#&=(}`O{Ha|aoFd*F~H*WXh$G#V{gIp!H&3hB{6{GBJ=~CYs72M#6O20 zPTD0Y_%@a$PGn#1w*8>;`Gso{Hmq3{eqkTW6(A&O*@TH zLgDTyp`@F)=u z7CKuX7!uWxjl*OAZ2KiqB274?k%H6wG;qR;Uw(n~@5;gkxEU!PcBtPGeM7+SKOcSx zIOp^Jd+Wpl6{-J`@nLg7 zd3ewcP&Q$cZdNmO6?rn`XQGg%Ps*o-NpI@#!iXvL-7WIxP=B`2<61p?2#vTX#u}Ot zStK*!q$hJV4bQ<*FzzKkgG=+_j7YKztl+1o0`|D6Q{XpU8(B5%+t0I)^sbH(n62sD zs{@EYUhqo$vv(SX=^z5$_EACJcNXfXW;4P?8FZE2yk96Ax^IA`B}B%5i3*etE~z&1 z);;~P&?A^n*YTp&&4|M8LRP>1xbI?OzhwPde+uLPdOzB0a@v1VI}^TGt~JA7-%t~3 z4QJ@3_#bT_pGVpBW0ZBUJhxmvdNjrNGxBOh$WI)%zAQykZ>revL}li-$15$i>S7dK zREPFA;%_OS`z+peI5GUp&|*govVBgpIq+f-mOmYD!`-XD|5#w0QVV+L1`rw*ZZasR zE?=uNt@>6vyO^T$q~wbwjVt=qNB;V$tkYiE_aHeJ_%@?6^yaijn4^QmPiiyK-I51K z&G+Hm&(1iCN_Rw8ju=nTW2i{?b`0q0F&S(fGFrgdbdIq0LJTCx+1KV(*x3F+|J0j( zYaRz9G(s@$YWNOoXcPNch2Gn4&1;e<(=(_CS>>MoFovH=e-5Q+K94|4O*ywpld?Im z<5GmEx}W&l(N&HmrdeHdWi&SSk;dympE3P9wcz9zgDjV~LoJP~`k+?8soiwAsr;8j UjV4~R#!e@mlxuT-&5_#kH^|^w_y7O^ literal 0 HcmV?d00001 diff --git a/fields/ROla/dicastes01.fld2.gz b/fields/ROla/dicastes01.fld2.gz new file mode 100644 index 0000000000000000000000000000000000000000..74d3bc4944ac1a08d13d962672928d875b6da3c0 GIT binary patch literal 4087 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 qwcOVd=n literal 0 HcmV?d00001 diff --git a/fields/ROla/ein_fild01.fld2.gz b/fields/ROla/ein_fild01.fld2.gz new file mode 100644 index 0000000000000000000000000000000000000000..dce90cb71bf27b507e4ad53b379064bce43c9dbc GIT binary patch literal 3855 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 C<8ns; literal 0 HcmV?d00001 diff --git a/fields/ROla/force_map1.fld2.gz b/fields/ROla/force_map1.fld2.gz new file mode 100644 index 0000000000000000000000000000000000000000..7abfb9a33632f8ddc4597dfc2434813609d6eb69 GIT binary patch literal 701 zcmV;u0z&;CiwFp8yF_RJ4rXt1V`X1$VQ?`nW^80K0PWpDPQx$|h2ciZlKY>DV<91E zNa}h#=Trje85(4T4K!7Pp}*AbU5}uIUGj}Q(7F| z>)-&#lJ)#(U&=Ix!*%8Euv}L-euCp|aNxMT<8}2bI}Gcg#bI6t2Rdf1=e6?XjCI(~ zrB~Qz$AJR}j@MDG=dnp_vloBs=!musn*V0O_9gb`{^_YZ3{&F3f#ZE}+~1MqbI~lH zQ#)K&A-*^0b{x6)_c%vzC_CY33Jztb%#QAL@Q9mZ$$DOx%WEEu!|}R$csUloKHP!> z$B5v-F(MX+?>gv}dZ*lu`vpg=;D{9*v4SI3aKs9ZSiuo1IAR4ytYma-)hz1Q*eRc* zK4Oiey;C-aJYv2?(+Njthx>gR92_C;H-Q7k=6;_aH;34%&(i{p~J?%PXQ?GQWRz%jugWQn7x6ONdUmQiyR jx=#5V(x~|gO{a{GI|oOs;E0tpjx6~Cg^^|KM4SKsZEb4u literal 0 HcmV?d00001 diff --git a/fields/ROla/force_map2.fld2.gz b/fields/ROla/force_map2.fld2.gz new file mode 100644 index 0000000000000000000000000000000000000000..647d9a206f5a1c674fb914b51d0d6b5dbcfdd455 GIT binary patch literal 940 zcmV;d15^ATiwFp8yF_RJ4rXt1V`X1$VQ?}oW^80K0PULVQiL!JMqx((`To!3W4VK{ z+;Nc5+_~PI(S1>7$TGP>EYj|wF zy<&9d$TfBH_*$vG*2$xE1LYp;?H78`YPxv*yttmeyvFv(CFgsPYPyW`Y@3-k&R-sT z(Cz9izcW3kHNm6qaSgQe=rJB8tUO3HQQoiC*O?v}1n*acx_W!$uOu^y#G`_V2eqa? z9)kkG10L{z2Rwp#6r$-wDSDLVCG7RNl85&E%5XhsIlnTUx6g6bQOfJu`^AF?-6P7r zPia`OJ+?i!J;1>1Mu(#JbVEUU%Tlfaz!eIeWV9JyPgNFCc^VWn*BRLn@8Jo z5FYSo^(Z09ZZ*!cr`vhp`H?YUdg#ncsB_n@9t!hBZ4X}dU4HI3Z!dVHKC{)D{X8~w zJS3m%Ysjv$uJz(^Kkn?Yug|vtr>@S(Uh3G=NT-^J>vQ)Ww@7D?d;Tf$89GY#oQX72 zs-8&mXm})eWWTcY=<~k29t%CF zH4XDfUd28h6~rFr&`%oYwjO%@D$&}HM>a;inz0^~Mu#Jo$8qKk51L26ex=s(qdb@l z@#ydzgoj?sY$SNV9!9-pSqov?E=n;7M zB8*3c{Y1ke354;8(yyX;MCn&iJfid~c(@{rN0f064_Cm$7oY4=J=Xw{$Gzd@jPXdG zgMmEC=NcgFS1~+l=NbTy5A>)dVPfK;tB}n@ZUYZ>*lBXlK}LLdl*;0#fk!_VGmk2I z5#aG)5B+(2t43;VcT|Y(p}nq+;?a!ABg5d*PvmO4)#iL3jto3}0S{lm!x!-IMeTw4 O82$jTi%~{IoB#m8GUufL literal 0 HcmV?d00001 diff --git a/fields/ROla/gef_f10_a.fld2.gz b/fields/ROla/gef_f10_a.fld2.gz new file mode 100644 index 0000000000000000000000000000000000000000..b24e8317c9df14b95d02d9d02537e54506d5c54a GIT binary patch literal 5878 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 literal 0 HcmV?d00001 diff --git a/fields/ROla/gef_fild01.fld2.gz b/fields/ROla/gef_fild01.fld2.gz new file mode 100644 index 0000000000000000000000000000000000000000..0ad15c9617c4a796941feb0cbf6f16e9f06e5c8f GIT binary patch literal 3736 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 literal 0 HcmV?d00001 diff --git a/fields/ROla/gef_fild06.fld2.gz b/fields/ROla/gef_fild06.fld2.gz new file mode 100644 index 0000000000000000000000000000000000000000..db99f13a37e4087e1659720bb97b8156abf3c3f6 GIT binary patch literal 5047 zcma)=X*kr6*Tyk4<7XKnTYQZrgk+8QhLAz_B2jjcmY>NslRe82X2>o}qEcj#t%a;J zga&0xVP+IrX9+{%-~W29=Xw3SI@f)l>%2JU#eJRYBbyF`-Fj;g#32|MaLw!5^_%{h zr!=nJ^w;KCE1iy5X%n>g-h9VvZ}ZPUs}Tcn9^sUnaL<3@yuG@x`I_Ua=>6A!LOq_U zUT0Q68K}%x!n+rP!T(S3^D%fEE^`(7lI6nxeIZm$~I=@iS z_9e*)+JAj}$pEjB9PDi$)vv~4lZVFPHL*IiGTi_rVz^yX-1ltms?16-OO3fisTN%D2AoWHVf)GRNq))Jc%x#ZkWeNzve6c3 z=}^W?nM!b1V$^d;C9))DJvBb_F{eBB>OHIsbXinj#Bli^5Hv8Y35qA29h^oN#RDYk;ku_ zk4Ei*#ZX?w?zn8;LZO%GE5VoB2@z(1t7hAxaG4`bINf|dwrxPc;=Cxn*c8_?`FMZ^3)j$g0Gs{M zCS3}>osl^W6bVGwa|A9Wy_|2ooA6#%T@mkn(EZ+6UVx;G2Lwkgh2uMEI?)9dT)3nI zS!F&F6mN_xzz<&Q)#wbB;luMwOCESR$pJH*hw*<1gUx7fQ+7Q7yv0X>x99qMS`SpZ z6=vVk&}}$EFx`r-@qOj>op>uSLaeKBchCsb=J(Jm68>ch&@sG8=BeeB$u|QTkIrF& z)Mi@(6V3Mc-x?PW!R`v-pEy`3D4&qXRQP#t{!B=$ZbEg9b@?RCN7YXePU)5-(v&nd z;J%rUp7_^KpqBQ6R*&x1Y#JMxz*Au!svmnalI1oaGm6(ZW!Yar_w-RI%=Hn!(wAb0&A2&Vs|;;@i79PdFEs)t6eKKjY1Gi}lkvs(OY zuGbYXp_3xzE-GqNjJ;ic&^Z}x97IR*_e60l9wqk>qGVz2$EJ#fQdr({%P4=p8y>Q?^-F4gzA5hTWvo3N0oUu^)NmX zg02ql@8G!-2j{XItNsXSK5;AkyMXyv{{rP(ObAF#jRx<17bGqlf+?Pn$UdqSt>Tko zf>29%yUv=OCZIK$7Xqx^IN~XK7EC(fIyC&N`n339&x1oG;bFh|7@;f5)QUVy73owt z@5=dsM9uSj5_Hqx$hS`*?nWirWtKe=Oyr+aglmF zRax_3k6Z!nnidju&~96DFCV|cglTiLf-W_BKIpEDluf$G1^>nQI+Tnj`aNH-XMiKp z{P_BfNWCxD-j$ID2O)N^94skxdK=#z%+mllq&A*v!4T3;VJ>}^qgc(Kl@1u_yJMgN zymVMISY;)4Q(pHjE!a_E<+4a>e<5IxP{#!UEk8NWD2|*uo<{WJx)bMCuJ=U|`yZWn~hZ=!)}J^P}N z1-iF0e38?GmdlI$2Ok-N*hBzr3TGQB{qtQA=-K?bmBWwuJ{i+yCr*kA+=(rT@DeY@R)LS;t&%#1rB~I>xE;=uCn3^WO zjpXWf^4P6XwYx{2zp534i-_Z-UnQ zxgQeQ``_e1dBQszkSli(5?AH}gTK0iswFA2;^#u^hW%38Ac(-jx7uGZQ=_PlFl+_c zB?fPPxLEji79&+4O*->X*u`Y*b9Mq1Wx)sn-&{_F?y8%$#m6A~=cYTwW2Xy-cr5ii~NR^0EKTH$BUjNp5{yC;f^*vT)P6QKn z{l@d2O2h0thauhsWq1_`G$&mA=6SuqNE)<(SX1QGEy-lcJ3#;m`T0GPfsm?P#NM5y z&7*X)a35|Heywl`0^tcN#(kTiUIQ*D^J_r!)T-^5lCCdkt662p>#4mgJMFE|f_LI3 z85eE=A>C!x6S3=&)4xyt;5;98`w#le963hW#~Q7G0c-yiD*Vj8&~Y4lGNe~5i*8v0 zE)NxEgR2Jzk5rZMW)Bj=W`6!spa{k5TFzwJP*I|WWt8L zR42_3bAt^7I3osJOk#O?(^&J=&orNwpriiNG4@H0m(F2Ad|ogdZwc8PzA&JtpJFq< zmGiB|Ue#Fl34IFEt)bGo97GtA3B=gSK2J#NDyS!z$s$-wwuQHi-##wi&h|SobIg`M zPO?9hKpjO{wONWerpuXOfTyKa3a)b%+t{~hO&*hu(8hfMNcXQ>&h$MrZZ0O&R=;a8 zkmz1~NnxH!81y?BwrS^oNR|)Q%50$-@Zrk?Yri*}_H&G1e~~D&tsQ$=LMrh4=+5r3 zX8Q{=tM%{|Ug=|)+O58BeO9}Po|vIo`K-w4TW73q5hD&H$~yhjg(6BClq&{fS;V!Z ze)ANITtidBAL!|iQx8V`Z*Ig@KKb|Xx0nQZui%#aT~?3lqbDxiLtMV$8-@V{ zViY0DnaxU;u7Hne2ymWNFb-8qyeuB}8rMvL&Bkd?tfM&Q7C(8p#*=Q;W@GK*T7^082P^oI=j1gto+AhoX~<(uRMs+=s&4d65|Gz|*8EdWrJ?teS8 zSy~ACTZ8u^B&nM&CvlE|oPje&J(D(5zc#MVsOOlTDZNj-5AGt{{jW;E8)ssQGlj2% z)q`)$(NiDCw#%!AaK0>sn8dzJ{Y}Ok2cR(n95Rf@vTMx|@OFPQ z1&q~39sj)CDBu2t%6&5fV`?aZrBGLjlHCn(C27S0xE zmyrrvg$;w>-4O5Vxu*zAO+!9bk9?_vmgcFFg4+~qFJyNm&V<#e8FJ!l zU;VNLwj>|G{2!8k%#ac`_En~$bFZY6w9Bw|vSJ$oktLrCs3;>|{M$|%V`aHPR3wWN z90|PYy7~DBN*vRFK;q`@#VUBm-nC z8M)H@NzHL{WrUqAM>U)%usnfvFEgi(mJznMrWBOL4zaf^y*{yP#CN`9^u}yl^@#k+k?b(aGkatfjOBya~h{optQb28n3<{DaFBU1Xf6<~i_eQe!MAe1S3vC- zIYZKjT9~F4rPcH@AP4F(|9B@-XVvB|=}^dYut$Cb3oVo^BG~wG+c$5%VZ{?(lHqsB zmu#*Ek-I1Wr0o4Ph!5PPqQ5$jdpMF#$V7}Lnf?f?KHsq0i#u_;YItVfFn%>7z zV5gUxfequh7ax99i0*n+g~!$!-%AN(rB2l-vrKKdG$NZWRfSv$j0!j|GZA5ecsMxB zT_fv$A^O!yz}8)8cmrL+MK!|_O-(XDQ96+HOvSXiV!FRZ47^GU4n*;ZqUK##Ez!KMwRO(a`}YsW#`nXZ^6uZ?AO#4|8Kdi zf$DZd%fWTmbnF@G42}e^DFO4BkPU@n!*`glcc+{FRec($N0vlSLDnsr8m|9WIe>8d zRe88W=S%vjmaCZJl#WY392iH{3%t1Cm)@1m|26w`U;ePBP`>&Pyw{adr1jjo`PTWN zppESNuE*)1Y4l00m&<8~F`|dMTMvJFUZ#?RJcqy=&4wvkRqhtZEu}dE*APc4ELbqx zqK}nTY)jj#W8(EhI>*K|nqNtTo>^@dJaBe+Cqa+8Dr~Qd@y=ws_lo3EDl)guwYssg zKTOJIKZn6@HJyAjP=vqOv7c(IhgPArQ%(@0uLX;ZsF;<0J&`wGk-1HDSP(9afWcRy zs>v1ZdwdKP6wmpi|7N`Rx_zCh?_?j>s4+;BIk*UF%0E4IpH8Pr3`696*UxFp%G?&w z(VshlQ6Ksp7S!uWN?@NHFrVA!Ch_(7P#f;P&T^<`rE+K&BCi;ikRqPz(J+`WVE-uD z@9pOjlr;Po#ag#a+ob+?mHXq^SYgt}olqj{Bi?LAEwZd2Oj--+mIHmN1=`pArA{_X zwlAUwV~em$W^|bW_ubFb}=Gq^NFc0zFv#46sAEUlTYa zo%1)e!44s-pKIgdT*p7sR{kQO=8I7WjF`Xa?XF6u4aR?7WL*8Pkm~Ef`7!&m5=oah zC3|}kX*KZ~6iN@n&LV{#LSqAn9OQ}2dEt0JChP)t=755&)?cE6;GqX_s?_A=lML(`JD5d&pFRW{o&@#sJinZ>vjYO z2KfYqh6kK5(GLm_IJFKO;%1B;*(NtqtNT%4SrDU6XbC3oykK$Yalem|ry+9JJ0_a* zCGn4m*o0>8aT7-ypc39#H-X=&q-gr|G^z&d;Y! zuiC(bt+_L)zTDCb|i<5LwY}<8~2iYuexX!8MJt-n(aL5 z|7dUTt9V*Xgzl~Oe~%SCxV{ThuD8wlfM|y&y1U<7^~@8w!HV#x!WcEL5Yc4MHu1=J z{csFsA0u<6EzfJQdsQr4<)%PeH@4g7zbutRaWKn_>#qaSCE?^A943)#r9 z=Pty%pF}M$rE?VK1y|cu#`TW(H?X37G|q1uQ&OtK*FE};`khn`($6QU)L5QgDO=t} zcXzW>O@6(HZv2OPA0|VpEgT7{GkCnSlU%g_$JyNi3Y!N`WP*1(DL;6y5uc&*ElnfH zI>y=VI+_~o9~^2KEtCTn1%U~M?_OJ>c^YD%JAG{Bm=G=10q$&{Pcjx8q?w|n*DZ~H zUEo7hs~=4WZl86-ohDQoqUH_l^Pki=%WW2MwOdgeYc^bD{x(&g$1hv-eo zVNXkmM}p@@FI6|k|4m^zBG$||FMNbwkLZ119Ft9+kK%cH`75O9&yi)93cnxJ@uF^s zXoa2 zf|LsERxM7z=TOMl1OqILyg#771b>7okB(|w;&gmIrNW@fY@cDsdmF< zviL!Hs%LA~sp+}7vJcMJQ_U6>g)X=qgb5>!iFFXCy1DD?0VP5N?t1(m?)&P!tdESSDBL^R z8Hst*No~n8odF9v^&QL`V8)3K@~WkZ(6-feyyJ{Ok?!ikM3jK5K3oMJn*qnlTTG|J zupCyq4}X3)RPS74F5=MO=2d$7n@bO&mViw^BtN>~e$OgQVpC$N)>oD}M%V$?4cgs?*RAFct^b26I-=u?>U}z}BRDiA1wm zL*GVB8;1(wF)WDX28ZBe*uKBK0ViGE?2uz=SejLLtNanQ{ z)b8K7%4dNU-vcK!vD9)IoiriOv$_JaGB4N*gbMu!c!Hb3m$z{ZnnQYv8V43^gnS1K z4&@kURncN}Fj?|hr%wky*{U`cf^+JLVPH^1`nSF?vUKU|Jmym{Md9K!$|sYxIJO$< z$DmV|>wdEVdCg046L!J4TK1~aH&seQXgFiia=9Dh&zrlW-#BUW6T*i2^KB*tuS2M; z)CRS=47TdGq-E|E`%cqCq zUnf@ZO#ZcxOU6@AT0bM4S|vLTS3Tl#FD`f(p0VUl~X5iHCkpj@lw|n^p#@ zl$X-3dZ_+sU0ip=5Ng3KxEA+$sN&PTM7fk!Bkx>f8D^Bow7ZDQKtzgY$480qLCop! zKp)z!X62BoayCPF#}d;}@JAabzxGa6>Ch4!9*InzC%uZBy=%&t_JPS}bG zB)MH6W)&yY{0VZ9#h^$xarzpIL|lR~zJdPpoPA-`+g{{K(QJqDi%2Q9!0#$`iVuP1 zskySEk@788<8{N*^hzu2+MM2rN?< zvpZM&ePlaJ?msVyrU1sprDq+d3`8(z|(4+dWtx z-9GcE|85uUBO@$kP1TPt~Ii9Kn$3fCDpDuPkW8cfgK&@P=# zQ@0y(ufmv2zR(u5eOd`0=r)c?cpgbEJ>b8q>kJUOwZqMf%I%CtOB90gll7hHb?dw$H@ABkL;R6-0h{z&DX%FnALO-*M`# zDmw+^6aK_AB5-BF&X%n7@JKyLFR9WZbpQcz-ZsYWE z13z!}DB0zU7%2Z&8qG&;2ZQYgXp{#Yv@l=Wd z>fgD!dAeRDaBs2yK5+qDpu8Ed=9{e(*Tu-%8j60l##cC8JMyqdy-_W<(a2YtjrnY? zDoHhsUf(e~>Fzk;%4H%jBy+99{-i|%4u6wHaZfS8&zRY+5k|D1`~$-^oyO4cQC5}V zL;`Sdpx~DD!8<&m)c{}fNdh|p7wZQ*it;7+OER?!q!odKDj~HJmV)33c0&_ax+w~L ztxbZNRVV0>B>yXsQ?*>zD+?{b)Ny_`U)3t6m|vuuvx=fN;6wj^iqEMH{yu z+(PmRSw8A2nN`;5s{*~)RFLE^dFo+fh9{{W&|eLWMejKAJG>bFRqYBj8@61Z;0Q2F zOD2FfOFoO0It8T^fA!ue!4^_Q^3;(*2~C=j>J%&uxP=>#R1_)F#|Q7m;tpczu%GQn zDznGHAKrH$Iy<(`Cg!}TU)YtFUoq7Iq=fMDCELvKK{Op*N z4ax``A6xeU?Cv}o+ z=+?3m^!i~pdKqmH;LxMqFTr~sbGoR3B*lIW!FILKW?{b|R+<8d>{YHcQ_Bkmb&iVC zI&JzC5&r4>auXmaUD7n8(awi`YuQ8XGUgG*BSQ$G3%w|O@i-i{+lX(+qREr z1_qIH@%A`C4jV~YE%T7sVcJn*(?P|y36?%5E*M4lHzgox9i~h@B z7Dq@5FQnvBbD!EbVzBbD04R@Na=`X6fXCF|OI7K7;{(|Qj@rv7AsY}d+o1y5sjZyo z{!;Nd_5J+N-~GT30Uv5h{J0Lfjisi$k8P1{B@n!iSY$$J&>7CWObS}Y%1;57wKY!G=(B)$g$g9 z;41F03wnNLNJ3HnDv=RoB23~LGc1fOhlUo4iFP8*g#LqItSs{~#DByXs4k_c&{6m} zEaxoYsoAPNRJ?t4I^{ZIaCFBhJlN=ZzdDDyG72N1sNY=btI3{;V1|74%&*24PhVYc z+s_ULU9ZY^sAD4EU@N$;f-5u?OX*#M49?cea_=r|FQ~yQqO7il&6%-_i@;VJujVMg zy05&91(rEoDc2G)?qk}#xsNmV)At{z`ZP|KeaWD(w8s;_xfV}Axa>G14>TjOZ4Hcx z)wD^E^6aX&=lEVlej{AQ2fard?DYPBOFz)kG?n=a+BKRP_TAg_vGV_p{a;_hca1zX Z`ZTOlqRA3%b=jHw4eszA1&vAU;d|#Z$c}hp|@!=W_pKw4PJ3n%K zXP+D-+!V^mD>IKc|GYjE?H=AsDb)<(Fn+qL#)kT2EC~DM`oICHhJl z4*m9~R={fl$TEHC$3AW)=I8%E^7>N`X5R_uC^CPmbgzWdh&RM=wI<5Giat9S=oEcN z^b;vXAa!xr+S~O7?lmHy%UwB-_2j1gc2!P2OCvK@>4Z|UtEQ9d>|M$JI-k~>J?e1F>ip zxaFs8JBBOty?a?Ou|~sXs=XqnsV8Zof<6AKBvHvt<4!bY>ADtoS&ag?gl3$E{1rGA;fu%h%I zOI}g+(LZ~S(^dwn?j#sR`A4&P$N5Z61vWFY;MpAc2 zj?~}Pk_Nwo{37JCu6%diGR-`x0!6+?G@j5|%7;W$w&gj@68zy}+aKNzSfj1!56`=xAVcqzJQ<`lOP~WTSt`d2<73Z>AwS*z-K0%; zLP_h3`=@HeW%!&;ioOw2AR=urDV$GjL6H?VaP}6d#;rbCGpmbRUjJoB_1h&?OU$A0 z$!d|F$Xm&rVChjvc!mcffXxyA=P3T zx5?ZWbl7UTTK%~8VbPI0T7Pf*kxr3}55BHNsS75!pZsvRfWNw6lz0giEtl7`;Q$$5Gh#Id>#Lp|~f>Lf{^ft5Ydw*m79Y(215wEYgtOy-US@rUQh= z1e-j|7R#SIoykz4qsENtDoP=WBO7K`gdvwlX|j6b{bTA|w2ik)$|z+Nh_L9rj2BKZ zVr|t0!!|t%>RN{14R*B;-Kbb@6QMgo64-o-s$y7Vx@s%-NT<-7`@;wUUGiF8yggTc zP`c}5%AwW_{v%Av>bRg``UNUt)n!KIHt4i2gRh&sZl%-HoG~YRHoaUp zR$gm5o$BE7a@F}2R%Q@gaYSyX*B2^B(G{AI%X*7I#G+sDKwo0_4hZ0(Fickx;3Wp6 zBC44Id~KNzh|4^s7RP`oe5j8IP~oh%5u8cexKUh74AA-XWr%WONN)TmOS;`$rwc0y zWP;|*l#A;pwE9o@hART3$GouKK%UH>m@rTOGC+NKZIe9;tgSY?08y-6?4l!MCV@2? zrlZ}njgIKJJ4D6S_i-^yP3U4App&MX-c=!_`Nr?_;+_1Ib*b0oO-;9dJzFOv0sls4 zJQN!XGVOw`0wu6Y!qOPJY z>TL~?N*reT`?C_;D~B~!cww=nC|=5ejFY|c)&`Bf5glJnU+8beL$w2%bS!WwObayW zgQFT&i|CD~X5LCrYQuwT_4!Q3{9qrV=3R3uFwM6lW_1|yjK9qsXo6K(5Rc|<&Pkbv z9;n&)R{ZpNj)!W*_%!k;do$K~O^`5)Vk=}e=fDWI5^+MFE`KOU()5A@(>JHK@`Hvq zEy|l^Qc?R{CLRk0Kn6@&L>&ycUP_9NP8Ndt#ga6UfxptoHSIw2TqEaawG0vZw%D$z z^!oZ-9V&1ehqo9X?h%M%>s^T|9NQ}ikMf7hL0DhrKI(ii-JuD2DVuffSTs2g+y*~s~1!{E&=txtBfKV`(7DCZ`f5zn_A0|>EGRm z2A`%7wv3NL>BYoMih8UScztHQN#TZ#dplOAqj>=lYXr8e z1ZOvQ5j(a;T{{!rd6(Kr0U}zW&?S$OzN=&lfv534yQj9E{I-7x`FR`}Y9mQBN$qz) zB;Zs2Jui{SJ?HHX+`eoG=5@-=b(oLszXrRx$=7h3m*F7cZsC?`c6lb}cP1a$Bt>YZ zc*iW0_k1%)+UdvR%)tk@J;A1{$|5$Kzt@T5f2UV&an}+WudqitYgINbvZ-A_6VlzR ztf&~E@!B_c=kPIVFI#LXJk=CTAPB`Hvn|xX@CStD??Rq5#d~N3F&XWeeH|_(V96Z(@}+7HLfet=rJGCBjhHFnJDSntJp;y0pB6?&V>9X?RPH#wfp?b$WNcjNSaO2 zxE;T!Q2Vu@6W4&bG{++{0}t!m9awx8(SC0gm-Zg-WQ<$$5({N$z!KMKQtmN)=69j` zw1~3jh1@~x+Y#B6g7@3|SvtPn5&p@SYl|VQIP>UFh~d|?kCyH9lw@(FS0qYP{}R+a zqBrSFQGKY}3mdKZctb$ej3_9)YPrQ%S$bwcWADM@<>Qmw@5ZdqfR)PEz>jWX=g$_Q z>Tog9iGi_cBUWX=g2(;Ib1<${Bck88POLJuHV48W$aC6BCVL2bJYo=5-g^;3c;=30 z>a(wzum8yW_NW_`U+!NxYc~iveUHp5)16NrGl?LzwG6EzeD@RU^}2LaX$Jqbg#c zt=5fuf5fwhb&Y0^6(5~}U$VYO0MMX-iqmp6I-aWg(8pTK<_8l||V`i`m0B zn0EyN!1!(d9Ln>{JYVF3IlW&|CSNl4?EM#ICb@T*n-egnFSHGv9^dGQ$%ehs$weU~ zL4tfH62&l0JoNFHDG%w$`QmB%@#QgFkq+$W)J3K)cmn~{f4%!MuwNz2IQ-FAUqe?nP_?ZV`ZT2%)=KN;|mxInk@AP_}8@` z6Byg#*AmDv;q)sdQYkpuZziK9iEdZAHN*9SI^_c3;@kF2_p$N6K!OPIUy%}sT|uW( z1a-7?Q6D5DDs!j8VZkpZ^CM6Kz(0m?N8Wty+{VO#`M2ODd(H3d;UL&2*LdtNr8Q5${=>@yd)qFA%XfP7xb z>6_lpQo7{DaMEN6VtQqjpjgT?3y{k zg%zvdx)62sgV`~ly&#yD{-&}x$OLR~i6k6U58!qduq_1h3na*|6Y-GPj%8aLE=tIS#GtbqKzkbk_ z0B)+V9uN3AB^fIxTg%@UzI_}lg8Ako?owQ75B4SEH^OG497l`2DACCa6ZOkIJVk#c z-Yrrdsf2q-g(PbkLjp{I2-dm_Z65MbHy1{h50S0+lrUd(X@N?)_IP~7+f20ZjLTFI zMOcmdvLNR85};yfhZP+p>Dqw+p=_(`!bvQbn8^)z zrk4tLJlvW1RQm0BC)P-8Cm0>1&Y27McD63RH>FlfBhu30n`!SY@VGT%qD9*}-lXQ* zGs7g1W5h=V+YrL60OxtHZJlUR&GO8Wza66qxV8thED$RsNEyzlHy*uQfbYK7r|b&G z%;x%W4zh)9r~)6KtzJsEWtO&BtNd)~JB@0cFZuHPP9&R--}NWCv;hoO29<~lLehvJ z4QTL&3_wa!hF*bQ7qgb7G~P1k&|6n?(gL%65@kiq3_1G^*g1jd8_F1dwdb4|52;$Z zPwZniU(m5y7xCav6hBP<3A-!Pc2Y--Tw_}3R2ufRD%G;;U zC5YgIl=pQ{rGTg$KH&RvS9^<6nU`T2ag2Vu@<58AQ;Txd4CKwr$s*-%+HM2H&gpXx zd)>8+Q#roNMkoH+!w$maRpJj{tKw{xpf=xDUHA^f>az2p= z$rix7I2X|(lA;56p^g2G$5}fab$?m#NHeSy+v#k+piUJ2C9*o%cXW5S&j}_Aolm)xK-w>K z-!{OCI`?hp$RTlPYrCgZbB0dQk7Rv{=00xb2^34o0eRMbfgL-ZmP>xXW0{{Z6mxrx zLwmr8&lkY)?inM_nVp=XbWOAd8%qC?wM?}LOVTXmVGyWwlN_`#f>1J1_9iNu*>0{4 znF%?``dQl{E-EF|Ll-*9CB;(y4Ml$^IS2*(*`GV~!rC%^J`w}^RN}sp=qV!*IZiE; zZv&Jc4`#!$Lv(nGAQ;+jqGoUk&XTz<^9YZj-nhN0JfhEN7k^P`%O`MD=wZGaXMME3 z3E7%o|pl~-#CiUv7%*4UQd;KEWzn-Uk)US5^MlJ>hF-FIKQ&CLt)peCDQ4 zqs=NksEzGW89xMVPN-hy zdSW>3X`egRAQDzi(0S9jnxn7c}Q75K8K5} zVX{43{T-nujr)%yeM*~YS#{jrOXSGVTzk$wz@w{%86ENOe$ejcwitQ2+E|}X(#EKa zz6hH*wzb_AiatacxF`nTteo?G2^G|~vLu& z_9E%@SbNbc;^u)PpT3EB$l%i0*egG)C;K2+hY=UvRoR-etoI zVqvP9{B*y-DQ#_N1AEBRfqxf?OLA)E&$(;AR~f!Ihw06yMK#Q?k za)&7=272!{9dujP4=B+V>cmc(_LrF15T3W*kcc{1!=h%$^9k$dZ0ny6Z5?v83VHb4 zMg~k_8cMn0iGI?mVKv*EwHM;4*Qt>pEkB+>Pdvm8RrPdJz{)zSGnJl=-3lm@6gghU zc4hXD=dnY9{wZkp5O}ui-@ZW|o<2wZR@p33jQ9Zc%J0EGHPE@4^w&fZ&$J{bzuE`J zs=I3>mSHmS^G^5m0KR|)LzlMq7F49bK_>Gx#}$fsis&V7BSM6SUKhz<#M1ZYQ9NH= znbV4#w+q8>=vH+L?WmB?dj!o1qcu-n=c=O~fNiWoaUlyUFI|3$v|7GW76xpJ@5F#% zq{Ft>lM~_!n&x8!W98`T{Ac8aTuDGxKpJVwCYD8BrXY`CG&uxV*%+bZ+G3{S)w$p; z&QP@;5V(!Mxd+~5jcP5*c!gf!e^D)UJp)66R6Ghicyw_{OCyP~SQUc#&n0Z6BY2=8 zuL2g^LuSJFK+~(1T_M9}OB4)8GNb?Jeu-HgKb5Db$>m>_2;~CJ0+fSv$>b!$LH+@b zt`Uff4r>Lo~YoqDN)t#O6*Zxc(H_cjY&YM20#YRpy z_3+?W)vS-CtAkw>cbuwa5BcozI)ZL#WKP~1N_^!7?pglc-(u!YbOgNff<8BY3FEdY zUGyiV#8uGoXt`n-^ZU`ku%{`JYWMJnOxh11OGg8PCXgq`l?2a^Lc@0u2!cd$~XG(hOTaG zk&Um_dXx60CI3&mhx)G^2r$V&T_6Kbi&4F}=%fzuKSsC#yeYm1G^jx4DDHMHIL|Mn zrqBakjBoWH-{x3-oG5+P5@Z$#U3!m_)b$3oxjs^O1^U-FT#1qMb#9S5)YYf)L625$ ze~C<^E*MU{oA3n=6&(zx=e_k9fC1HCnIdMmk%tnwBfeYr5;V@9ROZbbasq!ncuso; ziIdd}`&4|-m+x)Gh+k0(sPSFLeWxC^;p9rF?{&+`Qre4tD_VyW7wPTDi;$Ya-sr=r zFS|wST7uKKCgmG_(O3%#@>}u7tIdj50+!%nWxB4n!p<8iilJMu_^jOLr`5YE3^YR1 zZ`Jb-T|;f{5#@xpO&q+QGK1lTdS3sQZGaKqu(wKgxtfDM2Mt zSDAA|7%)l#%DdD?i|uoi$B~*~urR=@)0U%iqZhVTcph(!lx+P>5q=YiROATumr?IH zHU`RGg$ZCtPH^6=gHi&**<;EZfC+H%*ungFU0Fn-HD6G(s7C!Qty7MmH_yY<5$8m@!$}?)H7MI#ofL&n6!V5?i#F zrY2QDhV8nQ`q&=WmGqWeM!XjXluUxNr@|sh^~5|>k>&tRhU{_d6C^!dJ=J{fo#lBs zqB-P95;$HFFxM|6KBf=YK!MJYY%kz-kGwX^+-XlkUtzi8Y8WM^5s6=MJDOX1Qkyr^yqFX$CjK-8)FFc(1(}Qb)<6PFzOD8SM>F0B4Be_%2EgNQ|Q+ zX{qrHM(w06bxL-i(pizL3Dzn|HoZrk|Cwb1mFA&h_>w?%pe~))c^UxL|7K3Xw;oi6 z2xG!}MF8SOMRiP*g<=hTmY-ZDP4BGx;A*8U6BnRw2`q0|qAPm8M>mNKkH;@ikPpMg z6+|)JlIl>Sd&Iw=s;rjJtYc{B`}p+*p*S=%Fy#dgD%Sdd;rX%zr4f01Z1EV zmG{-hU0#~Sr2)w60=&8eMH1dwl7PL*QwBjq(eb(Hsv z$hGXyf)9U8G3UfglNT2uH||Ug`3*DM+$+7%u%``noR*Lx?b!#qMcMF2^DFv6+x!MA zN*gGH7O}62y!az9jxv+cz?$eIv0yturxvrPXiMealLCt);0RG89WnOJ`Z65#&SSRaK1Iq-`%}#Nr)`P54jwE3$s#s z#_}oL-$2L(Ay65<{p|GjdUl=Hf(a*w2F6}f@AY@9wpV8R7yjAPqxK@arD1E`=06G{5O>~l=9Ub^3QtgeM1;GfQbR&(%pIE`_+mJz zKCzXbM}Oz^I9HgwpPGiJOzDzu^BXCP3V`l+GLJk1X))$O`-{Ic%5!vY*P2sK{*f|a@kP===}%142d%gOmMvcOE> z#IkLoW}klAT~Y*xugD&PHBFq?&g-1u6e@9M!Hj&E>iXh7L#9-U$>pG1C+}x%bo*pp zC;&bkKq=Hdd!%juwVS%gavnWlO3wwq5m-J+BH@!!_F;nr6mQc&+STlqdpz6jtcEo-e-pTL|*LQDT1lJnXHv zGMXWB&Q&C9$RcR)f+4UZS#b)pY2esZ39lP|*eeo+rZSu8Rt|5sWe~=L8{`LPG_3kl z!?p?Dm2+j!xbRq{gHf6t>t*qbHRl@dw`94QBIu@J{j^$S2KmF2;5WT6{?NLgGIcW+ z%|rD~{_7O$#|;6>UO>A$xGYGmWF9O1w}$E&|xofhPp&l$CS zm5|JD;LWm;7D;A2BIgzba>N|Yl{|=3uo~8?~bpNAiZI^sb?@vqf EUyqoUO8@`> literal 0 HcmV?d00001 diff --git a/fields/ROla/gef_fild10_a.fld2.gz b/fields/ROla/gef_fild10_a.fld2.gz new file mode 100644 index 0000000000000000000000000000000000000000..cb941d7282d0f57c409f68ddda4fc67a28477846 GIT binary patch literal 5881 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 literal 0 HcmV?d00001 diff --git a/fields/ROla/gef_fild10_b.fld2.gz b/fields/ROla/gef_fild10_b.fld2.gz new file mode 100644 index 0000000000000000000000000000000000000000..7c9342a14ff94531ed8889524515828d211ad912 GIT binary patch literal 5881 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 literal 0 HcmV?d00001 diff --git a/fields/ROla/gef_fild10_c.fld2.gz b/fields/ROla/gef_fild10_c.fld2.gz new file mode 100644 index 0000000000000000000000000000000000000000..1b37479e62becf5549a5cb6edabf8a9c6db173ba GIT binary patch literal 5881 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 literal 0 HcmV?d00001 diff --git a/fields/ROla/geffen.fld2.gz b/fields/ROla/geffen.fld2.gz new file mode 100644 index 0000000000000000000000000000000000000000..87671873ef018b850c6e386dbf7cff30e5d60b7c GIT binary patch literal 2689 zcmaLK`6JVf0{~!GuHmIH%u!fgM;qqITyH`p5;-dRkXyo1*xXlo<%k^1@seJYb8nR6 z-6vVA2?>rugxn4?7GV8#EvDtP zti#R2N*4THmY<--nu~6_v7<)Z2?plT$-0=n^~18mGFz`%;t^k8fEtR&^FA{e$#FPb z&5i%=bsVLUKD=HsryMa+c=$Tni@k9yDAh{1=Mnx^C^`Z>_aZp9;<<;}yFJm_2%{!k z%Zj%?#;0$B)8=UMbMEgB_`|;2P@_}HmbrK&Ed(IT%w8gcto#pYpcG5D>ht4Q(LUVTRBKli*$DH9gOg=i0r%O@^ zAsyTsrq>yf+hHDsuB)ao@;K{dMwrHOYp?(0u<6K*lPXUYC<_$F-sz<_Z+lkFT_z8V z^F26rVrxbQibIk(AtcKUV!p=eColZ$Z@Z6W_9QK5cq9UO+;hrM8ayEz7kMvEv_}NG z%tgH~yhZaH+?ix_G&PnAO9ba_`{EikOcjKjv2v43ucFX!WXYiI{)^)s7AZ=PqXAT=tC7T2wGarH+;Z`V*xVr>UV z81lVrtbTtoXmIO^{`)PFEjdYFfcW00}77fjJ=I$1RC)644X7yFlu60#}n zb2eq^t*p;tvxIM6fc^0y)2S<_0qWapIdh8x&1)RRncFP_snwln^YkbnBLZ}F18fo& zO5!ZP!mybkPw?Kqe$nMzY(?VP$#3Vm7h3WX#m%qqr^C3O;MTu0spu7P=ee%lm7a@~ z-CXPjxDs)-TOP-V)f}?EbI`Q+S~b8oa80g)2bZD)U4DXUC7jI>h}xbiPmv1u7Zf3a zax3*fJKRL!R&kduw25$$u00JWpkckU%}M;c6xw1MeOjs^seVaF?u zBMstm(=32PhsK%K=@WtsQ{!%#X*a?o@qXcn`mB<{!lOz@BlcacVLF$He0!>cPWUpM zjbKP??}M|Y6GC1I6H!HAgDizu91r(;S-h;^DR-)$XkKS{oY3KFpdP*Gva#eY&S!z) zZb#czT}qWf)O&NbBhtMl-a4039+u1}8xmOZwyB#LT;f%mNU(rueG=b#U~Gf;4qIkQ zM88Fi66388yo*+E%Ib-J(TRf3TY$|sHOnGZ24_c2SN^BzTnd!}ZPh}ZJJcvRtCWm0 z!?#7uVSdWv?l0O4*z7k`@6*=Tm-BgHWxY*U-k0rTdcx7B83JO4 zkn;(%_qM;D8h!0icf$T()^LFIH~d}`1CnLjD-g@E>5&cOuobOWV~5f5dfFmUmaYl?AswR^ zVPA{d|4ckWfc8!jSDJKL^`bYgP-FQF-Lt@F!x4y~&^0?iUwNRk z|4e{v7iVYbf>I%#7PD-)#RVk{N&1-m3Bf@%zdmE*!~A38sGNz5(AHYvVd^+OcRFOi5dP`Tg#&dW{mqx9>S)eR*IB7P(jyUK0?FHNMV6Os)}`TE`a z`aa zSwfL&zQ?R{e0YKCAd8-#`f6ILa>o7WhrDR%&N%3TSEEWt?_FV9_x(PkQnxs39jCP> z?f#yF;7^mTRPVA~$Ao@+TbxO#Menq;NSg4crrpS_*Fxl;vp&hw0}-kke^B+TNyL%w zQ9zdtfzJJ?s3Zw)_D(fLe0oP8)1_ac@^%Yn=?E4*j9$q9q>1k zRMJnEb|cRZRQ|=a#3g?p$Hpt4RqYlVMLpzKV!dr1Wn&hwgwL;j|DSA>#tx(ZZ1)$- z-l&nnoQC= zIooR*re?!tL)V`PQdtf^B&M%`Ufbl#+`a7;4m!|Q4>w2=0=VuzJ-G6Cr&@QbjW*nR z=n2Xh4rZ&%`j6cfTL(IJHb|Rm8~Ocov3N226?H@4!5C;HAZ#uchOL6Z;Fe(F?rjT|^)_TJj?R z2s_wj4I_ViAj3O!WnS63?shgZCBGeOnIwvSS$s8w&{<5)-AD{i%fg2c`#^9Cf!ygB bALldKY#ZW#bPaoWeP5%q3V&6ECLr*C4w7fg literal 0 HcmV?d00001 diff --git a/fields/ROla/gefg_cas02.fld2.gz b/fields/ROla/gefg_cas02.fld2.gz new file mode 100644 index 0000000000000000000000000000000000000000..1e8a57f423017fa0a05f62532238ea8c5617326a GIT binary patch literal 1167 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 N+u_z|tJ&?}_ixTW?{okF literal 0 HcmV?d00001 diff --git a/fields/ROla/gefg_cas05.fld2.gz b/fields/ROla/gefg_cas05.fld2.gz new file mode 100644 index 0000000000000000000000000000000000000000..7e986dfe496fa5c1732e4d099abfd3b8ac09a221 GIT binary patch literal 1469 zcmZY7eLRy10Kjq8S+5Is;>ycO((6L%Sq&A*TNhHT%OgVOWqaDO6(;oNnzm7vrItEk z9ksmfnaUDfSY}e*o}SS1GHy3=(`4T6^SS%yUjF_4@%!uh9c$4d>Qnb9z2%V+q{xu) zP@4VrEhNgxt$ONiG_KET8Rmo|_x(^<@0>zskG{iPG+c;&oxz+%qTgJydUm4Wa&y)0 zrk1U-@82#kWFz!?3LzZGb>Pc$4n)cXj=xcs7j{&NYaaAf{qcl&cN%z4-b8ZZBT!p% zadc`b@?qY&st2!O?Ch7xsi?a1?H9nJvRW-|N(q(vA$PI)#FUNHaAukRM3F!;)2hMn zvCKrX6Xfk`wLyu0TeYJ7APbx6{E+HI5KB{emAAALH{@RG3ilGMHQH)@miG(%IRC(0 zb>3&(FTnkb=taP%k-hLVEv|QD47p8#!F2rT(q}C^ zm0)*GU9VLiL**N0tRrZl$z0LA;HUokR)UVsK--3UYPLvQUIFiEG_5n1N=n#xH#T~1 zTzpG7{zN5>VOsKcg>q44omAXbc*Yr!CJ$VO@*8&eweUS(;>|T4*e7p^PIqJLY~y|N z%xetjB{xVbYH6<7#d|4P%1=X#ttuiF2Y(h;^t3-C#H8ms+O?UHUYt(i_Y^r~a)LH~ z__EW3(1>YO+DuBU(%I!{v>*+Vp^5Jgoh{6Z=j4lrb616LOYH6u#K*{DK>JY-&q$UQ zkQ^fsQ6zA)1KL49f#c;sWJ0OQei^*o;s-KaMpQkg`1K!ZcxhBI{C{kuw=4gXA^)73 z*P*cMGqJxMj1QD`c#Pc0mMOHuhf8p+XxN@c*{-&+4M(C7o6gGgSt-1OZp z-P1A2q#G)0DnNEQv*FC7y|RvMZJHc&xfaHcce1Xo8lKz+DNJ=?z6Z&9yI0X%aG zr)*A%jrJ8#=2E`t?J28`J=pnWUm1rnX9=ICSEdGkAK9ebo??jRtQaur7VTtMuwol+ z-J}irT|Biw?CDl@K_QI@XdAo&8@Zc+W=G4@KPFHP1hv|xhnEYJR{eM~-GE*UxSHR9 zpd+O?MdtL$351)Dyc#%)&R3;1n93vKlB3tK zhO8eW)4k_Ez@pO^D*H-W(#(Rv`y;{W0THpF!3ZfWK#?5Tm}YhcR0TckBAsGFYW>yy z(d*<2hr_RyYUsxwRkeG~Q8z@_9gVJ~+WK{7UrC9+F2c>a+-~*AHR${8$zf^^&Rn%E zAxM9GC$!+zIO-P0Ao%mv_v4@9?(kT|}mZd>k zQnSj4XV-5&g_5)2H&2@mGj;F7Hnp!mW;x%}LFe5O;4LYj45(MI8jF_A$Fn%jNxN7= zhQPJJ%4NyTV5O)%>J@b4)+sPofWl|U0Ft9VD<*L{h&&$Dg?#d+Xykusb9XFl`}A*` Sq!?5-X+SV^TWc)Q)B6)T2o3H4 literal 0 HcmV?d00001 diff --git a/fields/ROla/gl_cas01_.fld2.gz b/fields/ROla/gl_cas01_.fld2.gz new file mode 100644 index 0000000000000000000000000000000000000000..a71b24ac303fe38fc5b64515812a3ac501044cad GIT binary patch literal 6138 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 ROZ5@3a5=*EjOXc7{{}9=O&S0I literal 0 HcmV?d00001 diff --git a/fields/ROla/gl_dun01.fld2.gz b/fields/ROla/gl_dun01.fld2.gz new file mode 100644 index 0000000000000000000000000000000000000000..fe8e29570ae35b9b48553c84984b35906266c31c GIT binary patch literal 3369 zcmV+^4c77>iwFpCyF_RJ4rgp%WOZ&ZF)n6oWHJElo!geHx(-B>F>+q=aK2>!|M%^k z8v$FFDp>}TwqANIESE|%Kn$eQ|6KpMUS^;%GOOD*j9 zCMzN)D?BDEJO*Yp9IVL-kI4#;$qJ8=SzX3LH8!hfOjblpR(MQScr+Y@*LUO?nXCx4 z9GR{$S>Z8R!7-0PJSHnVCMzQ1>#NBMk6&PQzcbm5>-NbCkCU_F+)q|SOjdZr&+X5% z`~4KGG$#A8dcfk&j;30_(B7`uqhfu+qE@E=VLHj0L0jAK?r%b14#sD7t)tk+>BU)% zzZU)M`+9JClNBBxIzP$E4N`E8hJf`qS#AC^S;6twShduJS-nf$lNRoWv1*2eyLi0O zG*tWQWr)k-Qx;5FVF`>@z*Fhj$4^`zB5zS#$miPB|F#}jLVtb;G zs&wmXJ~Oejpfm06E%bWZ^Jp&^IkP&JhWX!*Rlv&#tTc!GjJ~X_QLs-e4Qv4|-^?m- z3=YCdaKYxYtVmraz%H>gxQEFKP+_WvP4{AjQ8LR7HYZKmC{I4gwM zaI8QSggoKF?J^-+j=_4ndacu_UyqmBZkGwsaBdg2Ypj~}t8q&87*^^z8tX8wUyhfu zj;cKj#vZIP^vm&5*HN{HvA7BWO(?Iewb{Z23TVh4ugu%3<|q~tD(vj5veNc9QlDmJ zt6GDA7caP8l!>akf6vDruf?s~jZLiFPYyM(f+C-lwP*EVwRJ=565;9CWCx#OCld+Mc&u>dZXdb4V8CPCQHhgtS#b+vC^H^5?ab^T=s zxd`vZY9-y=k>ZACwMf?^Fe~i0P`V!@MMIs6srjb#vgSF8m7GnLA*!t^K$JGhPgaDV zZ1+-n^z)h=EF2Yj)3BVCY+lQ!$^iBLJ8I&ng1*LPW!@WKYgZRrs4QhB$+^cWeaG6e za(w8jl$jjkKC7HF*7}_*%qr>%OMd%vN$L0Qg2Yr-g(g_1%@4!M4~G^1%O1e%BdD(ORj=28Is>t4xc$wuDs_-LYFm4-YONV&Rp~%=Ojf9rQC0V>o(O1-aB4q6S-DRJYO6QxnS$?C)#3mvnQ677$FzDFV~0t*3re2pP$bO z@pS~Nx-y&J1yy-p)oE`}R`j~5H!6}9d#Z2q%C_7Y70ZhBb1>Vp0$SOpKlrs{(H zIar}*_w8Mkrs{(FNmwDx_hPlC)56Mr_O&~!E_+q&V?0)MML08|uIwQC+5uIPt)ht; zWkmx~R!Eg(uQ_?~`cYO;l?1O656N2eZPiR#U1Ls-J%p>@eR@ zolLjx%bLw)tS{YhR%bkC3pmW<+4K6pA|ZfsFvUnWEJ*^ z`oL+$Y@{E9mV3OJW*_K0s#>x~id}R38CLDIqS8SN2eWFzWlLl8sCwxxIaay#z2zIn zw3?mjHNc9uY}AKXl{X0Ss-(-USNklv536#kJMVZ_dRlxpaO)Q{T_wo^QI&Mb)$Grz z+@9lGnN7OnYAR&@5-V{RaTDcc;u#6GSNRQsI#&J&$wSD!lq!Igq8aIgR3w5sHNr4ZckQHjKHc= zi5E_Iw882f!7A`J7A)-D&RX4KL)4&}-`Y8^E;~J1*UzKWu(XZxJIHTv+vt2wKaslU zV70aT>X(`S721atkl2e=?KldH-aSH9k<3o4L{j7qQc5wU2v&IhWL3G`o0aM$pOsOF zIQm9l)xI)Br$q}5#47Jvhb|QJr?)v-;r@P$RpcH_?N6N8^%_U_5}ofSCb1G`vK<`5 zO8LDCuMlh({bV%P#Nn*$>Dx~L^HF^V(T;i}zqWpb1y^ z($FR-Ghw%r71w}$j~H7*fu#T)kHHbFzD2#yGPa^3-yA#!N3bI9zlY?7LaZ-ue6pIX z@b6)G(in9otAhU$f{Z66~E02DTu#8Xy}N(lsFFdg^)Ye6_<&H&vfM?~nNZGoJr+ zs_FIQGHu^%zAG#HNDYOiB-h!Q)n)yu0m%Dm_plT*Sb5#YIX!3NsZeWjg@IWyZ4bFEu*jD?V7;>rnv%bQ(ZI3 zR#Bn)lfY_3WAz;ku(}p91_j$K&^426R>i{6d2ZdZ^5XM3KI4d8s?`}k#44w(F`eo< zKuWuyB5Z3jo?DS?XEM|-sIHl0$urrCsspiV-Lq;ns%s`$uv~Aq&&8U~YS_8i7XzmC zL3Pa}uPygy^@g>9X0WmmYJE^$Gs!}a+IGmaGJ2E3E8rAY`(U~za-}A876A4J!9s>np!9*n07#cKKMPr4}dWt;%Eld-DHEZ?O%GAl0IhvczRV1ad`8Jldl zR_mBuS{j=JTvfSm$8{YD=CzvAzet z{<)fD9f>q66weIHVSAmz!43rlVpNEvSt{YE-l6zOPRY0@K!6 znBB<=w|gk7C@q4{<1~C-J`9nCO22=G$VHpw<_YG2cuf))qw#9mIH1?qY~%E6eN@(~ zZJrhLg5dp3R_m~@1CD29IKOAG(zZ@kRULKnI^hUb#C1Owk4eS39f7sJBKPXAx7#=- ztL=7E6{u|1Du>DG04x3&RO}tjsycUav#-kseZ*`4LT2dnmnT4g+Ly}bWb zJl+4^ex+CmByd*t4YBt9sx5w-m1|E4HNQ9pH+j#WdB>oXr}tIm`zc#>&zz5{TBvV- zZ1SEz^3LssZ$uWzS&REceN;vMX1@}|Lu&>M0flM=R^RvV&Yx$w* z)2N1E<=2E*tv7F2$#y2I$qHiiom8?C6CR%V&W>iz%smKIi_V-U$=V*pm}shDTgC;GCIHwihDm9k05 znXK?>lT}hoR&WH?41Hp~!a)5#s3Nkru^iIKCMW9m0VN+`z>rAgc=ibRt! zW`=IfY#M2#aW-=sW7;H#?wKLQ);K&p>wW)v*Spr|k853jey??X*4H3y$&v`<*u@2! z0U;O2ew3YN1pg3UvjuV~nmw>>>4MYle!PF4@P_B+DN>u}4T%dF=%*9T9d{>x>tECu z6*$C`^Fj;OIq3r7<8Lv1JMcN5I>>9c=TP(eUS4LDnOsYjKF)@c#9g=l7gl59EpMX1 z|AU5)W8N}#kMQs9-joI~n~a{hk$`FbSvzH+%?qZrSmfrlSSt&Y90IILxO6nj3o zQ7|{>((CU$#*E672T^8jlDv3o<~HUGoWv*k)Atjc6%fy2wTAuX*N?^O5qCgZrjn3I zUW@#&RN8g$=3zB9`ars>H2PUYrZh!9qit~hSw#9bGvuX!UM|~j5j1xmblYH8Q6rZ? zw(aL1i;=`bUk)_cC_JA}o}Qqw2994Owkjh{3rMwotzm}Ba@?PrSU9tbBW|9>;%CFj zzIs@;W_YEc*?chp2>ObfvsAqKX6;X$(Uzy15T|TYWMX@`DOH92$p?p!mr_Ds%IXnH zT&vq?QsTW1^%VGH)@b$w`fTs|H}MM~VY@omsd1-JoP(m@Hw>o2#2Zk5C#2?oWMFF5 zI^ow#z!EWz^6)V7ktc@{1BdL>D^rh^bfT z>iiqM1nG@gq`6hd>kWH!B;$pJvfLcLJ=XLVi*KNt^A(4YmNLsz;}3J5&wepI$$||; zQ6I2~270lKRF=U>^IH!D{wbtuOg^3KI=)@#j9t32LL3?_PTIV(fzFW*N3|1xeuyhF zK6U3x#oO?4C2I7W(N=Id;|dt>;tL(lV#8}?zM_Sh1e!BXoFdhQt{-L?GgMV}*f~(w zkf#98kDFBL$&~NQpn*X?-H1=Oe8k>m`*Zr!Mj9jd-LApw44Ttl7?(l_cT_ zHtJ&zv2Ma^{^oU3t5v3<)_cq!)xBdQn#>`NWK>0phrQ97`RnaekIwCas*Y*ER0|Xa zmQ2jwM<`vG$IY$Ob6<=M|#$ zW~lIi4jLWvDqKQUuKJqjUT5k`gwEe&bzZux{-w^$p+fJ%SIq>Q2n)eUh zg>7xITavn4JHm!$dc(Uw{f=1M?vGC2=whrmngV5eMHGRCbaO9vr|E7FbgUT zZ`#Hz%WROpUyjhkBwq=DY9I42h7v@i{u zioK&Du@;}+tVs~H5az@RmV8nVJEzTb^loI~N;JUFpG{h5>9GZn6>kO9PEA(FnmZk@ zhfXelPQLr-ic&+Cw-6eZ$cMMr06MjI)daDpW{}26xMkZDs$|(FiM^1o#6#Z$x>do9 z1m`Cq&YrhvT@jWO(Uhx1P6M4!ewP6eCHM- zQudD#$`;k%z^jIX%Eye)d0f=7g5ff1rD2l}au)g)%NC#VBWBQyDT(-UBfs0gLn?os zGp%j1u}Y#<%c-uTp7XOoJ|(kD;IBmXZ3+ViDOP}}%e9a2$I?%@h@U+w)a5X(&st;# zaE>7Be$>J;?&R$D?RNt-B)i0#KQ9kcI?%e7`upV5_h8Yt$^o>1GvS{mjY9T4l5ww^ zpF5r_WxYDxt+`UbWm|L-QuiiEy$bl7Kp-?D+B#P(u-vri9*Dz*SK#`W0U1jo=eqLcH-~)#c$u#gq$N+& z>Z;a-4sl9)gij;VNT;3jJh<)*w=P16Dzhird1QYe^n$>ckbA3A^RO3V`v%$KHEVos zf31Ev(n1DSrRB#bB)O(d0*)jQG4!WAVE!r(%Hg-e zv*mdnK9q)DKuCwD&jjl5JlZ^`I=SBYa2V=;__FQDh&q_GJR3|==^BO8QZ?iuwF2t- zOs+>3r>ODqPFW_5&fi0GI&utJCOKpaW|v}=0_K^?Bymcxu*Ol9I;$$Vf2nBl;DY*D z?wjDt1Z3FDh_Gus-_$2lY*R9Nt-o`j ze8Zpbs6X;k&qE!;*cvX}ejzG9$}Pe4WEtLmN8}&~LX^6@??-y-(C}e8(V{>0( z7Eel!dTIU!Kg|;Fhq(G)l-R+!B@nLq7T#;s#{Kn#-$VjxT8aF@V7+LZEIR;k0~AXS z_Jv90&7BcIBYJMN)TCZc@g{}@lr;MW3`JtqgYP(~J}8)2e5{mknyFq8niT-hVMkfG6j zNu9SF8(j=l_CA*u8>0y&IccN-$lbP=`aVYX#;vU#XupmFS#Rv(Vbotk^7ongGK~J{ z4y)s9yhD&os2S(<)G1FbRD+z~N1xD5sxnOM{XVgATND%i(8k>L)@`(};Q@Y)cgZ}R zY>M3Vp#YAfXWo^Vqz3F}q~k2%zOMlSS8IM?=vi{}gu<>FW8kYlfC>MZb^fK)<9M58 z>@@XSC_D~v(svM}T@CbB&5av?_hwM_%UJtL5Bx-p$nL0u{0vl(POQKeRTpJ#m5=T6 zEUqMHU_O*L+9JgCy?ti6f>i`r5s_<&FfNK0{gR}63UoI5K9T+|$A)gOcV%i8ec1iX zE>x$kCOf1`vgc=9rD4scM&WhZLEC=wX4$bHOk7AL7@P{?bqfpdxnax})QVacBUUG0 zZ?fmyO%I9Q4!OB!z|##J9|L0S&`n-YNQK@iQ=|g0{-#u5=D9Uf=qZODzYeOz`LGlJ zCD8cMx#&K8#N-jnvBvaY%~2hClAX}<&sL-M5)@+2^8 z*)w)$cW;Dqj?F0o11I_{ezMV}$`MvcUHX*2$}yi_emW@qk7H@@A5dH(I5e684yV;# zWZ2$Q<$-%?xQn#&I@tIHQfniTSk@K&}fMGOv~Lrr(v=;bj`|J3m5zc!cl~D literal 0 HcmV?d00001 diff --git a/fields/ROla/glast_01.fld2.gz b/fields/ROla/glast_01.fld2.gz new file mode 100644 index 0000000000000000000000000000000000000000..df482a7b918e7da9311bbc94cbb7e0b306419e43 GIT binary patch literal 7225 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 literal 0 HcmV?d00001 diff --git a/fields/ROla/gld_dun02.fld2.gz b/fields/ROla/gld_dun02.fld2.gz new file mode 100644 index 0000000000000000000000000000000000000000..604871169fefec75f9cbb7f3c9979d05a7b97b36 GIT binary patch literal 1599 zcmaLW|3A|S0|0Q}x=D;`7Wy>F*W=1a_4zbi(`Z)NIM-8j*PbC?GMbI@t){|!S)a%e zuQ3d-^QFVc!;^S49@B(dzEx7O&1_C4aSNU2`3vsH_aE@K&NVcQ>-3J)K}OKSs9_1Q zSocHW^e}gw#nPY$)mUTiIPc|N@!Kt5)}s77fSj>5td7#q z;7^0GJ#%NQ{3fJ;mn_fdX`DavCW|Z6qTskN=uYz&W28Zd!YR}I9)SVg*6-6YxZ?s( z`4-u0Ay3yRvuanHiJt1qdq2t>@}0589)G+sbG9CpnL()FEp}czi*y*;XwP1$g3^ab zG({RVYOX8JO1#GAxBqwvV+Ibr21XtI4ldDKH8HGPmn)+hXPgfGN}2IJM8TiLcE@|Z zYZZ;Wz{&r?EFS=b6 zTIFN{=j6ZP=@zQ-VCZ#G4BIZPx0uh2HqdKWn4zn1M6}_fMow$Z5^%Ss)4$#i4b!ZN zFQ+gblvHZ4MWH5^x3mnL_QHYx+Bo(LuU?r9t4&Zte&eBC{KNmQn?wkBm~khud;&_V zJqn>+leJTuJnDWB0TjJWBQI3Ypr~|0`6##jJ^ErXY?~&;<4)$Nh5>MDCweCGwM>ycm^5o zEF^DG6luIAGO~MecCT$ss4Ltg9i96C$6>*7BBur}MBQ5zKyRIs>~Q+>uH9*ww_zBAyYM3_Vz zF}RK4Fh4B6`6xXcoOs%)72TBhGpsk@eNR??l8rVSb8MM-yA=-)B!q#PyJ}Lm?Nq)> zVcUPQ-h(c#a?`#Y5YGP!di;_C_RouVgZ6>?tbg~~X4z2wn9{a?$m*&A3H^W#W)Y|t!lAYAiZ|Uk z>p_`fM#bTSHKnV{PYq@oq;yx^Dby7~dAKT)2@xBml6;fg2v)80s#V=*;pTMX_mYU~ z)lmO&&Ut*%$REDUu+cuM!lH{crf=7~Ry_5@t*^;c|&|Ad@T`fZ(?;sDwhL$6L!1(6qouQ z4EXkYi^7_3tbrVRRMUmbH5^bWH*5=Kx**boSNW9s$>==rW2xgQ3!B*4-hLrHY0Lab zb1oE|9Su;W<+Ih(oL4FbIDF=ztnGS-s_rQJ#PI*q(CD_>^~oIC%{Y6?Ib1V9Y9RAK zGqWNe+~d(XAnMN_Rm{6=Six+nIqJyOG0?{BsOf+b7Wt99iG-{OT#VO1SrYR$&$Be}K5rp$iDi z?C4!NP^LaIi1*h(oh@9{Li*Y`c#Wa+%3ydLZ#T zpRU*;dwfMN|644Ngwa`Z;beUXE6p`#94*r4OK&a&N)aW5gw{FThJqO!$IUdj%$RK{ u)}WAy&y|5C$feQa9M=V)`w>gqGCVW*w@x)yr? literal 0 HcmV?d00001 diff --git a/fields/ROla/gld_dun04.fld2.gz b/fields/ROla/gld_dun04.fld2.gz new file mode 100644 index 0000000000000000000000000000000000000000..4a0ee0dd1ba11b6a3d8e19922ad11fb7258d1228 GIT binary patch literal 3028 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 literal 0 HcmV?d00001 diff --git a/fields/ROla/gon_dun01.fld2.gz b/fields/ROla/gon_dun01.fld2.gz new file mode 100644 index 0000000000000000000000000000000000000000..5dab7d1a385a778fd0b4a679bf4e8821f6ce9786 GIT binary patch literal 2348 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 literal 0 HcmV?d00001 diff --git a/fields/ROla/ice_dun01.fld2.gz b/fields/ROla/ice_dun01.fld2.gz new file mode 100644 index 0000000000000000000000000000000000000000..523bb7deaeba948aa16c509de91438c0eddf64c7 GIT binary patch literal 3560 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 literal 0 HcmV?d00001 diff --git a/fields/ROla/itemmall.fld2.gz b/fields/ROla/itemmall.fld2.gz new file mode 100644 index 0000000000000000000000000000000000000000..3127a8c70c4ee8e16c034d63bcf2bee4cdc87b98 GIT binary patch literal 362 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 literal 0 HcmV?d00001 diff --git a/fields/ROla/iz_ac01_d.fld2.gz b/fields/ROla/iz_ac01_d.fld2.gz new file mode 100644 index 0000000000000000000000000000000000000000..808506d1b4d09c24603b83f5c843c3622d7a962f GIT binary patch literal 1016 zcmVPx@_!TNLdJH=i2CzH)9+G_-WYu>BR>k{4sAxV_xuoV;*zoLdF%tm0P+b4fg_ zh`Vu-ou=>HZc1`)SCRSiqwm~YW_0KX&m}i*G|u}MOHUmhW9O3EKBypd^IQ)qbt{ju zQR`bxr1fepY~jc#i)JUuwOiIL6xovSzN9YT7S2^IU+MfBIr+-Ym3uJxd9{pMkDAtX zJ6Gf8y5!uR2E;#m=~iVsPpz&UR&lH8w>q`DcO>1;Ao4;Kr*r$CVvOOwI=6k06=Nj# z6SvDCMBJi(DhQ{}J#nLRi2=7LCmFYLP6~xD=ay8Hxw-J-49*@Pt5^v3GADD=oIGlr zAr|4&BS|}_-0cS1zeP7LClyfgDOqX)Zg5+TE#kWUccEnsm<69oY+|W% zyBc-iwdiuccXT6~JxJbY*U2 zyp`Oth7eNYzL$KxMeT{3keIX^?=;GdH{q}JhT2n@gf*cpP}C#!2%e zxt9Hm)91#0hYQqsGWc236|>#a{cv6)pKqN@9yH4eZmwm#6~7W1MNTa@Wgoa}{DNxILIZi? zmfU=ACGmF7g8&KBSXx7GNF+jj2A!HygHO%Xn<-P^e% z2mNa|_9Y52KHku83i*he<(#zaww*h2(C=n>?Y6w08nV0DvX)DD!NhWTdXroC>BEOPAy^?bK>pYCM4{Y9)$1T7X)%Y|}F7TB4Df zZEY??O3RC%Vo2nLmKUBNkx>3rE)lPA?JwA#=l$}2dq2F6`KG4n?2t1C7H2bXr{lc5 z5eWpmx53PPuyE9A!&Q~Ic{OKt>x)sI%mra+!>^Xj;Wt7pH87(%V)oe0%wihb(4$Z_ z^&i-Xq-6mtJ$#%*@^Xe>{YcBtS=UY}(DnW6FxDjz>eS7EC*9L^oz-4ucz{ZeKMl<* zp`SGNhrT9t)|kkC0=f3#Zp2ULP+SLDK20ddY+7m7*Uq+JSit;Iqn>oet>Lp$ZIs<6 zp4sc2B4;s7G@McRJ~_(LI@_`Vi{)Z`(W7C(lO-napF5LBV8m9|;A517&8^`SA~sj4 zE`8-dac1|`#BA}6Q=L2YmM~T?^whSJ4-t8SDDFwS9Sl=N;H;}O z!Q(X3>^A`gMj7Wz{$<59W zi~O1u{N)&urWZ$(*;f!j|Dn0R&lk51px4XTo(e)hFKXP+kY*`iMkoZp&o!p3Ws&|O z&Z#v~nGmMv$Xsx>l34QIP2y}O0VZ+PY!o9kE`rNdi{=^v*Ija}Tru@So#gQ5KnzD}NI~MW;n&IrRjT);-hfFvL);NtDh1w>tcdB^m(=_ME0SBe zO4RDWE#z=UC=Q_Q#DTr<$7pp~dxKDsGxhKnTtJR5oOf^{xDzdjvp<4sCS%1BwP$*^ z6h$)4Zh7E}s?#Y4h+Xa^v^_IDY+f?`Gj*VG!*+M&?y#i#6cp#w6#1}Ex1U-KO(HI& zI1B1`5|8EKN=e2FB>$5 z0@-@7OH&ei1Q1s<)!zL+7cGj`YL>Je8e{F#0=kVN<9k|*3skfqe5;K3P-3dvCYoE$ ze*^B#`G%U5Bs7gaz5ijQV{txtH6whL5)%JID?`8CIm=Oio@m&mxIO!rwsrXU@2%)O z-HM)HXEmYB)P!oEmg({a-S0>K@hGroBsIqSYy30pp1DQii$r8@Cs4MTqSvKkuPrvE zp+BLm6(YXB)yxyK>pGGA`6fKj_+^cFK(O^B#scBN zxlzQ=%F=+|xcTIJj#lRMBY`X2Jsg{s99@_^(N4p)=G;A?-w4_AKn%uVKMX0&u@@%V zGfJ7ST#zU)UZ%^9i4d?DZqE=CXs7u3MtB6B26@BX9x;9APe|)HweqaVhC|^DmEWV% zU20>z8}iv?#dQBfn$Uk+FMVVQe!#4B!Wzi>RNJ)0cHt_EQ-sUh=Yc7TU588%?sT$8 zNX!vU!D2BKDI4^0vafPWrG(d1LFC*$zh;Q$FQN0=I@l=}5-DcUZ@NxM!e@jPg!|O8 z9nyQS&?f=MV939yjVt@BLP~Cj2>Og7eTXy1(;TaZTF#G+UVh9^mWYfzd6 literal 0 HcmV?d00001 diff --git a/fields/ROla/iz_ac02_a.fld2.gz b/fields/ROla/iz_ac02_a.fld2.gz new file mode 100644 index 0000000000000000000000000000000000000000..540fcd62b320dc160baa7ea59b3f0d71c5f95391 GIT binary patch literal 1613 zcma))>08nV0DvX)Xy(~O$w*zLITcJTmM+O-+NssH)OZ39)Jhb2v;ej2+O$l%MI$xa z+FXW|HZOjPA(0naUU-5;LitmGo0?{@Lrxi3oX*6>#(R0= zViAc1ytl#31F&$^Y5i4|xOw%;?3S0KJedo^(1u?vo5OE_T54cM@x+|5n_0y)wxLI% zYU)3*5lPDeSbF$4iR9%Bzxt7ue`Q@er9jvBuftduM5vQD1D<@iS>Z~!5{S0#L!`+CV&Y-vsvV59Qkkz!(tgoGI!LWe&BSt+Lj9bH}rP?UF zjXbm0J4DW6m}oe&@O?^@rFD*F0~X80_@YO{f+tH%-oJ1rkHCnntidNJ2b){NsYGn9 zQeFDWf#S^WtBKj{8?QQZ@-1PkUg)s_XolrSf|ox`9pQ|BiXI~J1X0`*cH0@IiojV{ zX`)9g)9jvr0;7X!*rm#;(II0jWMF?gfjpEz;sXwgyd%D zM@4?k3jT5oNz;p?$?PkLp#RX^UuTQk2GC#0*q#bPKrd?C&yZ#*VMZtfz@-{f*0M-{ z9_Q4Wq)ZG`bYv~KT1hN8eirEsz{{86>h7-L5!FVTMU|XQ zRe`yx*_P46{;%0RncH4ts}{{Q1g^W}R>|*wY3i<$4{QQ5 z?%UFuis`}pWT07N4k=Cm8mUd!?*-b8dHUxv(?`5K*}>to)g?^j!&f)Vv0XpYP`k?H zle_Z)K(da!mBlH0eMl$wd=jKbV)k73Juc_ujIYHkWwk!m@LL|q*HA(4+cG+c_ zo9MGlw_GvxW1ZyCra%lwYDhuibKuv?234x}rQU!^J44(NS1JYGlB|dsITzIY0xObR zxk}XPz%Ar(Mko%T?Zo~)@5g9$*?WReku&x1ms~)OFPwL9BDfPRiL*a}YbN8w5w)j! zHWx)Q&2D+%imEdx`-xrdB(yy?Gu7VxKIbip)@qit9U5cpvjVz}BJ&4Yiwjh=AbhKg_)uc1+bWt{ z&VK{$x$-SFIaz2L9lP&QrDJhEc{MY9l@gNhV=F_y%{kjqfSzdBsklA+gtle)*l(@q zJl%?(UuQL;%+iEvpOxwI2HhV-{{A?yXCy7g`y2do?C!Zm*IB} zuenjg&(79>-njXc`;J!T^uvKG+}#|TmMgk2d6J!mYt6a4U%vse`Jot$$9@=6nq$vR zv}cwwU%4PrUc4-q8xtX5G2EUZCeTju^NsKbIt}uMyDeh+&L5E0acbphll2F~87jZW zr90KecsJzp$%^UziFBd=)?WI^5`4c|>4Y_q{i(KTv+crF7N-c8wbuhv6t@nUDBR&> zkC2!nnu5h*C{i})<78jumPQG$se;J4yMN6T&0j$0wRNyl&m~dJqTh5KmxRv3?CQX5zHRfUw?4iWSjMfwnDj-@+R54D^f8@>31pCS<%dH52l_a;25 zgB(;t=ZBKmU2V*mpj!AHScB~$p6}DqV|&Cw1Ypx2C3o!EwugCh!F^GzewANkl0dt% z3%fcC*RIrRr literal 0 HcmV?d00001 diff --git a/fields/ROla/iz_ac02_b.fld2.gz b/fields/ROla/iz_ac02_b.fld2.gz new file mode 100644 index 0000000000000000000000000000000000000000..165e19ff3b8db9269ba6968499d511ecdae5cdaa GIT binary patch literal 1613 zcma))>08nV0DvX)DD!NhWTdXroC>BEOPAy^?bK>pYCM4{Y9)$1T7X)%Y|}F7TB4Df zZEY??O3RC%Vo2nLmKUBNkx>3rE)lPA?JwA#=l$}2dq2F6`KG4n?2t1C7H2bXr{lc5 zaj}R50^ZwT<~~?B>a^jiO5D7fGrRT0C{N~sFtp)U%jWPKp_Uq$Q5-RQ>}F;$jcw>r zsG9l@Y(&zs0G1v;P9k|Z!>@j%<>#zxrxfV={&g7Zk_dI`X26r~>AKEpFEczqrN^I! z=9SP-8v8?ElR9fmWIusi`*1hnr*kN-gDjsW6l6B7H0x_;TQDqO{-{w;I^)*xS*bS4 zZWGV!^-htq7$zFdD14tBWoeyl*?`4zF}~>0u;9rOllRY^$s;ggD{JsE%E9K=a0(Hd zt5lc1a-cY~`)Xpg_{OQuoq9_cs~37~1e#&_k>HgNQ%5=DpQ49|JV6xqq}>jNsUmRJ zRhr;&nrZf%fC8hCnex8W;m>*jc<-*%T=3cCQNwhgyW%@5%S|z&>iBa20l;)n3WVfl z=Z8gp%?kc<3`x_Aqsil5xLQdpdG981Hj@CA%0nh&8#~tLc0Y~u2H@q(aCLXr@Q7*?&7w-q zrmDbP)ojb?5&zfho{a6UI+Y98_eX)f$%SbDl7g_44arp;8{osc4r&})XCT7aA+T9Z z2REwKwF8QeJNT@!9ZeWrHf!`%-Vfq@5w|h%J=@Z%J0f^z2J&et{Lq ztz0E)b>J3qI3pAX(01a$-uGj)x~#oHsK}Xm_zNx|#~02!I1${5mc-c~!8Mby;)vQa zJzI(*nP#^ZgrSy=p*;a$rT~)ZT*)G z8bg6>J=mowi9G^{tC?!=exHjLMQb%n+76Ae_Gtm#Mv?J7t;Gc@S`fZfMtmqS)ol~a zE$6=h_vU;{8sGeN5XreEj!T zbe?WS&#$wZP-bdEwNJ}*d4ulvBma05*fWwEaE8k7 zQRyzVG2RXNY_ejyebk)`Z(EFxusIVYpNh}?w(&WMDv%>d2Jo+lnaRzv*V5GK z>mY~J(D|W6c2^rSCa4yE2i9PFnCJUs^!Q$J5CPcyXUQFVw(Sw#TyS3$t6$|;nJCb% z?8dIn!nG^4+9{^?f!<~Ic@)1h4pTA0sHC=kj)_?I_N*5pUvzF5>%Ah6oEiM3T7H+% sjg+20rVYh-w(rDzi3;Z0DN}Yx2R#1=%q5)at10B-!#n+JB?bon0Es$lUH||9 literal 0 HcmV?d00001 diff --git a/fields/ROla/iz_ac02_c.fld2.gz b/fields/ROla/iz_ac02_c.fld2.gz new file mode 100644 index 0000000000000000000000000000000000000000..c6c826fcabb3c63a56768403a53bfbc9f0c2bdf8 GIT binary patch literal 1613 zcma))>08nV0DvX)DD!NhWTdXroC>BEOPAy^?bK>pYCM4{Y9)$1T7X)%Y|}F7TB4Df zZEY??O3RC%Vo2nLmKUBNkx>3rE)lPA?JwA#=l$}2dq2F6`KG4n?2t1C7H2bXr{lc5 zadC(Q0^ZwT<~~?B>a^jiO5D7fGrRT0C{N~sFtp)U%jWPKp_Uq$Q5-RQ>}F;$jcw>r zsG9l@Y(&zs0G1v;P9k|Z!>@j%<>#zxrxfV={&g7Zk_dI`X26r~>AKEpFEczqrN^I! z=9SP-8v8?ElR9fmWIusi`*1hnr*kN-gDjsW6l6B7H0x_;TQDqO{-{w;I^)*xS*bS4 zZWGV!^-htq7$zFdD14tBWoeyl*?`4zF}~>0u;9rOllRY^$s;ggD{JsE%E9K=a0(Hd zt5lc1a-cY~`)Xpg_{OQuoq9_cs~37~1e#&_k>HgNQ%5=DpQ49|JV6xqq}>jNsUmRJ zRhr;&nrZf%fC8hCnex8W;m>*jc<-*%T=3cCQNwhgyW%@5%S|z&>iBa20l;)n3WVfl z=Z8gp%?kc<3`x_Aqsil5xLQdpdG981Hj@CA%0nh&8#~tLc0Y~u2H@q(aCLXr@Q7*?&7w-q zrmDbP)ojb?5&zfho{a6UI+Y98_eX)f$%SbDl7g_44arp;8{osc4r&})XCT7aA+T9Z z2REwKwF8QeJNT@!9ZeWrHf!`%-Vfq@5w|h%J=@Z%J0f^z2J&et{Lq ztz0E)b>J3qI3pAX(01a$-uGj)x~#oHsK}Xm_zNx|#~02!I1${5mc-c~!8Mby;)vQa zJzI(*nP#^ZgrSy=p*;a$rT~)ZT*)G z8bg6>J=mowi9G^{tC?!=exHjLMQb%n+76Ae_Gtm#Mv?J7t;Gc@S`fZfMtmqS)ol~a zE$6=h_vU;{8sGeN5XreEj!T zbe?WS&#$wZP-bdEwNJ}*d4ulvBma05*fWwEaE8k7 zQRyzVG2RXNY_ejyebk)`Z(EFxusIVYpNh}?w(&WMDv%>d2Jo+lnaRzv*V5GK z>mY~J(D|W6c2^rSCa4yE2i9PFnCJUs^!Q$J5CPcyXUQFVw(Sw#TyS3$t6$|;nJCb% z?8dIn!nG^4+9{^?f!<~Ic@)1h4pTA0sHC=kj)_?I_N*5pUvzF5>%Ah6oEiM3T7H+% sjg+20rVYh-w(rDzi3;Z0DN}Yx2R#1=%q5)at10B-!#n+JB?bon0FQNRUjP6A literal 0 HcmV?d00001 diff --git a/fields/ROla/iz_ac02_d.fld2.gz b/fields/ROla/iz_ac02_d.fld2.gz new file mode 100644 index 0000000000000000000000000000000000000000..24d5ac92106aaab5a5cc00cf913152a93f609ebd GIT binary patch literal 1613 zcma))>08nV0DvX)DD!NhWTdXroC>BEOPAy^?bK>pYCM4{Y9)$1T7X)%Y|}F7TB4Df zZEY??O3RC%Vo2nLmKUBNkx>3rE)lPA?JwA#=l$}2dq2F6`KG4n?2t1C7H2bXr{lc5 zad<=m0q<=va~~`mb=q)MC2n5LncezglqYjR7~1fwWpntAP)iNWD2|vtb~Ce>#y0dQ zR89Q{HX>@^jX;Qwnr_|2m9yNrXCeGvG=0bX{k)ml+WB^L6%Pw3No8kn)S7_Ef^Lsf7GZaopEdUtW+Ci zw~1%=dZ)-)3=<7!6uwW6vb4^&Y`|i<7+>^gSny%{2Q>K!H)nOnG1G@MpaMymwb>F8FNns9`$LUGbfj<)#=>b^JMi0AM;O1wwMO z^TQ&)W(9vahNS7m(PZ`&M9_a|?(g%(Z3F1_GPb9J5YUSn_cNqfN|+G}0q}E;DQj7z zzld{cO;jd?DLOJ2T&*OQymym0n@NC5F<) zQ&nKDYPMzci2rMLPsa9Foyvvl`=h|#2+q!Js&NR5l&a@C@_hQM`~+$#CwZ%y4*^1;nO z`aN4(Q!zc5p9C~(%pt|`KqIy3`fq`DW1jxG%(M|NPj+xPZFLD#`QX(Jb8Od7G}P`g z`Q)B_0FbOBZ)0)FULV%UJs$_@QQ1Y$c=Zs)u4}5fP)^WyhAj)ht`Nxya7|(wqFr`H z<|g_q(=As_{ZJ=4yg3lVks4Bv_-y#KvO$&VeW^EK(#{Zf#Fk2dwnHU^pShz=qsaK4*5U#cEePK#BR-Uv>b8mI zmh<0$dvm^_CM5|?qfhUDSm{`tPhQOkU!{b^|Io_NZ+Fge6rd*>b}4SpKBjFQKK^?v zI#0Kv=hs={Xo@%|eB47+D;(fA?}ncE4JZKmjT>DX(F zO=;*)XlsRt?{78p#O%6GB!9jM4>W#RBOVZJJ&CbE_ptcJlQu}xv z?rUxo@w2itpf_$l`JSVdIsHiB3U?33rX@!gCQr1}aIHCa59l{SwmcAnao7(-N^|Um ziS~?A<|`K@%8Qrja$_O{EQZ@N#01(Ye!dYNL8n39aJNTH-}w{LI!>)TYqH@`I78+4 zsC1Xw81IIBHd!&=6=kL{qR>3`NQYeVpv8+)^pwHB}Hfch9dGqWMecytWQ@%7sLVS@fH(6O!;5VFlqn zwQPs<9xU`pz%dx|FKXk;{;H6Y+aZEJqevg(%<(kG>Y)vxlaOcZEW zc4Jp(;o6m2?G#h{K<_g9Jc{2Lhp8B0R8reN$3!f9d)AAQFFH4j^N2bW^80K0PWRX4umiW1Ymsibl(3o8-LieGLXu$ zNlk_~)5%v-D{it$aV_-d(OZt9m+^K-J!8@v0M!HUB-LMA@AcYSpym_*LLzIQ$8=QiZo#Xc$D z&)C!Ydm4M9o}J^#{+gpM@W!gz3undz{r%IM@_#uvpELb|&2r`4_*p!6AA0oY(W6I? T9zA-sUUhu{`M)y#1Q`GTLi%sX literal 0 HcmV?d00001 diff --git a/fields/ROla/iz_int01.fld2.gz b/fields/ROla/iz_int01.fld2.gz new file mode 100644 index 0000000000000000000000000000000000000000..ceeb7f89f54ea7fe901e928264d2f0ffa010b97b GIT binary patch literal 219 zcmV<103`n(iwFpHyF_RJ4rzK{X>N2dF)n6oWHJEl)m;vRFbD)-eD!qR|1=wa*t9Z` z%CbpKhBwp6S5hl(vPp3*^ytxBj-r?Gc1Jy9(i;HP1Mei&Ut90>+K6*oA9`1|%-g|^ z{ms+x6VEIH&xt}Y_QI`UgOLX+qF~CL=gi)@x)yms3YdaZH!qk(&c42PJ?ZB*@^ytx} VM~@yodbM74eE|8tGW`S@005d8a4G-* literal 0 HcmV?d00001 diff --git a/fields/ROla/iz_int02.fld2.gz b/fields/ROla/iz_int02.fld2.gz new file mode 100644 index 0000000000000000000000000000000000000000..cc10d8866f1f468b279f2e3f4d1e9e19b35401c0 GIT binary patch literal 219 zcmV<103`n(iwFpHyF_RJ4rzK{X>N2dGA?FpWHJEl)m;vRFbD)-eD!qR|1=wa*t9Z` z%CbpKhBwp6S5hl(vPp3*^ytxBj-r?Gc1Jy9(i;HP1Mei&Ut90>+K6*oA9`1|%-g|^ z{ms+x6VEIH&xt}Y_QI`UgOLX+qF~CL=gi)@x)yms3YdaZH!qk(&c42PJ?ZB*@^ytx} VM~@yodbM74eE|8tGW`S@005fea4P@+ literal 0 HcmV?d00001 diff --git a/fields/ROla/iz_int03.fld2.gz b/fields/ROla/iz_int03.fld2.gz new file mode 100644 index 0000000000000000000000000000000000000000..f31c3325d960f70b280cbe1590af1f6e06d2ee91 GIT binary patch literal 219 zcmV<103`n(iwFpHyF_RJ4rzK{X>N2dGcIOqWHJEl)m;vRFbD)-eD!qR|1=wa*t9Z` z%CbpKhBwp6S5hl(vPp3*^ytxBj-r?Gc1Jy9(i;HP1Mei&Ut90>+K6*oA9`1|%-g|^ z{ms+x6VEIH&xt}Y_QI`UgOLX+qF~CL=gi)@x)yms3YdaZH!qk(&c42PJ?ZB*@^ytx} VM~@yodbM74eE|8tGW`S@005h;a4Y}- literal 0 HcmV?d00001 diff --git a/fields/ROla/iz_int04.fld2.gz b/fields/ROla/iz_int04.fld2.gz new file mode 100644 index 0000000000000000000000000000000000000000..2039010ebfb0517543792839e0ce298cb751d27b GIT binary patch literal 219 zcmV<103`n(iwFpHyF_RJ4rzK{X>N2dG%jXrWHJEl)m;vRFbD)-eD!qR|1=wa*t9Z` z%CbpKhBwp6S5hl(vPp3*^ytxBj-r?Gc1Jy9(i;HP1Mei&Ut90>+K6*oA9`1|%-g|^ z{ms+x6VEIH&xt}Y_QI`UgOLX+qF~CL=gi)@x)yms3YdaZH!qk(&c42PJ?ZB*@^ytx} VM~@yodbM74eE|8tGW`S@005kJa4i4; literal 0 HcmV?d00001 diff --git a/fields/ROla/izlude.fld2.gz b/fields/ROla/izlude.fld2.gz new file mode 100644 index 0000000000000000000000000000000000000000..bdb1bcee71fddf076c4a8200728b61bd1925ec8f GIT binary patch literal 2639 zcmb`J`9IVN1IIVpSbWuHjjvf*g=t+gH0i0xmCPj9GF!}ne8)9zk}D!Kp|5gQ$TcKK zjv2!s?tIxdb*-u7P~(RsLh2epnm|6sY#aI0lY zW6g?W)GqQ1n9RC;2Z6>MhYpAXrvG0wLa{g8Z>cPDzKvnm*O#LtU7`^h(YUq8@2kR@ z{dFZaE_kzvOxJ7h&GSFs55#`P9cI;Y^u|R5-KRsF@RV80xo}L$60XyZB&NEMxuyqe z-aFV4;qo^|R5h0dU62A_aYD)D(IoGO{3CqcM@BS5r?UqfL0zJvVzWJ_W7$jEg28r4 z=d`c@_F^U~huBC)_Gd}QmvPUjC@G9_!S?&5o=iQ}iH;cscB|bl1Jxg_hK?M1Ee6XAAm9n$qLB9d;Q_?vZH1|-3!D=S)V4nmTABT9_avNBzi)$D`)xOGGzUS zo$C11*wI^0{xroN{82IQ1mSsI3A9jFamh_l4$wE6=_?-!}F3D@SB@IZ^% zytub{+7V;Uu`^pVsCr?l>y@w+B15R$S+D=}E3@c9Bt4x(nvY`90yjTuH*cr*J|n|8 z${ev$9qNm6&P13G8GQF5-EjU)vvJh2r93^_69#sKt>S=Si%u}rE8a|EGf#^i$IYm1 zNw%jtdZz3x`lf8Yzu81>bFN*dYG?CtPNMbu#&3j-zLJLV+>tIis z8PLZXYNDHXM*9Vw$PJ6fzf4WY=}zVwOqgP60HomB=A(QOe0I^QnjVEi)BerZ#;o7~ zev=bdjhV+`;GJTh%XDhT>sZw+L?_ebol*)0Pl&5gP%4!5YkUX4fcrTu=ibb``9v24 z0o;iTi4JusTg1v&2+i4np zcU35@1?mo;X+r^IieEs$#3u_FM!mfPzv@UPp~BGS5Ks`F4Smg`2Lyn_y6WfGYeTP@ zTYi5?_Sf_|#XvOs8v?_%!!Z>#P+PhRdqGEKBp}Curl@5Z85yS;zdZO>q+(qWZnPgq z!g6=pl^m2-G`*BC%T{i2gdda4EFk9;tklNix*^i!>$Ag$Rpzl3*wL#+>~2}6*SqA5 zZ?_Hx`E2A|Y04OpIh)%yP~gny?F}T|Af*FWA=M~zXT1+M<~GgOS*DL%bVxi`8#shr zuZMXd%MG3)b5y@4rv>#A5%=WCBvZ65b{G|L|Mgv4P{4Gx+{dK(S^h2G9Kg+@6n@S;HxLfa?`5Zr6@V5@ z!-L5&0yy%4sf$9G(41(@J^4maC39DYNM1~;tG_HEGJgZ!(lT9jn2{u`T`5}QU{-ze ztJOZZ3qV9EyY0LKXn7CDD2LNJ+QkdI3M?-~w)O2RRIg62vhyXT_a_lNp7E6t7DK;N z2i!wF0>7_iJ+#9;BBLE-`B4Tt&DbW~>8%%!kn=WoO8Na3weui(O7qD>~%0Q-8i9`oXp#?h1clj7ZthI3FS52ugp}@HK1`~E!e}ntr;e@bT&hMotmG3 z8=GnOBfHzs`nX28+Y8*#`6*j*;Jf4fId;vq{J;spu4CZwuS8HffgN=XhCjb6V&oUs z?Cu&odN*#o?aM&mw5x9sq7IP>n>xx!2Qc9h$$oz5T?p0svFA^orrN-SC8v8c=7>{p;+ilI}by6K+hWMHztlxxrI+YPD3~Hw8PMp zf@EZ5h#&Wj*9D(?L4yXD6gT8C^sL*-5nBJbk7ZQwmTz6YZ)Vd@sGrXF?&Q8yN7nFa1B?u7Q+CvsE1 z)YALlw!sroY{bMTr{>yh=jlkXmFgD%YX3h-M)F)KOwp|FJEe-PZ+7o(-Pt0%X_Q{w z2Xwr!_?V$qyNHkS+}-5epoKFrk<6^BofmkK&4VyaDStfP=c!&AuTM?*L>Z=gD=CH=@aL~6wk6se`VJ10#@W~)YwOtA02BD~Zg{!Q1qrD%u)3Vi&x(9yv EFW6xE)&Kwi literal 0 HcmV?d00001 diff --git a/fields/ROla/izlude_a.fld2.gz b/fields/ROla/izlude_a.fld2.gz new file mode 100644 index 0000000000000000000000000000000000000000..c2c4b94ee8a73b48df071c841333962af1cbfa1a GIT binary patch literal 2661 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 literal 0 HcmV?d00001 diff --git a/fields/ROla/izlude_b.fld2.gz b/fields/ROla/izlude_b.fld2.gz new file mode 100644 index 0000000000000000000000000000000000000000..faf745cc7782f73186182fbe7c0c748fe9de5ed1 GIT binary patch literal 2661 zcmb`J`9IW&1IEiXl5q@8ACe{4Mm{r4NhlpMIks($RCDB*%Po{~%oG_7!#HN#6_r_c ziowiSM{>;U$XUCt$Ru|tom7kM_5B;Zzdp||&-?XyUa#lT=O`)BnjHPNs0Gl2X%wnA z3F#Y5Ik07UJS6kAjf&!{;*@)g?3T^1k&M!lB9qZf#8;#Fhc8K6b`sRjD!I(Zy^VW! zak{YP#!ZMZxRc{aprjYEN~ZI-J{;vB zQ?_+pqb!1orgurilNvyOT{I3ik`bIEfk`@MS@A4eX%{f0?L^Q{CzfkoqHW1X+{1W)`l`)XgQJ%gz}(3SgQ zI!rme-eFSFIVz^Dq5Mf*CGX*pBw69x7#JUgtC1yO9Y}#`` zX?g-nLPmJecdVVjr2ALhj~~Hu?^P6fdln-08y&2-)TydQFJFI-H?d#| zpW;w;8ThKUk>#rt6RGugtvyvHdvG?Mz2^Paawk{KxyFjrSig(vjV z?y^^0`U+L7nj+6)b@Aguk5`u^Qdz|4)apU2j3@j-;`>*YDMD5@e~h{DIqpj69w>^n zk*PdReLrF;Jpj$+x2z7=%u7ck{G;Ag_0AVFKZ5M3S7cx*KNVp-2g`vk-Lvpd66Fh9 zxCI2SK;8sR?92$Y_Z~`7_S@)8f-BOTUM{j1Xtq7yV_Ty390Rd)NH5GT7rNuRx{CaZ zj)@~rA&ljx?&;G;%2%^Z&OqA@cJs}F;q{Go*EqrM_s^I7T!fyo6ThAO-V2376^lpX z^po$Px1*WvFI7V#<;jy&ULS1cpA$%V;K^^nftYtc;jnH?fd;x~mOn;}GneC7=&P1r z^|sM{xDGa`h@6&hk29P>6{9j(8ZTqGIjneu+WNPQ>EHhN4GQQfI+`Xqs;E^JZ8Wi4>Cz!zRB$v@(Esc;xQ z4k(L!n(O>rFi`_L8OVCCLQ3C>pzk%-*^VtXjc{>D0i?;mx?lJ(;S%{Ti@y4`Pquj4 zOXhMUj66)6ZpmJ}%8VDfx#@=RUJgJ4OvgcX5ONu`(Z7UoH@jTX?zYjJRJ+PgvIXil z9ido{5{@TmclOss1oHBFK|r|1)a}6cx3o2(Mi~^d;FA#<2SKl$2xK3Vp7B)eI$~nZ zJu8+96#RPU4DqDJc)tP-A@ng1!CKC^fb{faT7h34R`zR_zPt!ZI`T<)BJ>+q;)x7N zL93o3@{k%)d51J|Blp#v`Xk=Sq_jpL%VmG-9`gq;FF*sEvOeSv0ZW7uflR9?=p{G* zvP<;~U1*O+Qm{=mqUMn74QE$7V>d0^L-n#68C@QxH7 zgN?8VUKZ8)Vm=w{Tg)v{h99b};W|g)%Ta4OC8`g>%2VC9Q@Y>O-j|9CSr&@t`eOk! zAtvN^fseSb_2J@y-$bi*lIOCIMoUlrMGOeUYMj|57b3=f-`-+N(Hg^Ao%WQlBC*z4 z3XW$=^B%Z>f!-0e&@Q{zA756H;`>$s;Pj}Je>&cdh)BHV3iZ9Q0aPxwg9sm62k5ia z%-ulDSWP*Bn-PVp8TOvGzy)Dc*wJ}sz{;ry1P}83UH&xnIYB;*(u)x{O zYWwEs3|I&I%(|m#Qr^cq4>nGKd!Q1NwQR`chr;Mhj=7BOK$}?Vr>4&5>iuKMYA1Ee zcfe*M&#_l6I(2&|8JmyT?3I6%LZ6{bF{iwtAJ&OMp%oE$_Eb}P7|?UKVxS$`5yNOZ z11*H$^Jnt2&d9y2-caGStXn@?xzx{ldS@e=bEkN(+gWk+CUcv&NuQ6g`uY9GyA?te z_digG^$5Xmb@nv>O?_6}AUjaI{WMr$Ne5rRr?q$s$O~R!6bKd{g9#9T*NY7MDo;qL3ADR||eh&a2ILPF=r_K-ON&1DJQ8 ze_3UCM+QMBAM64;dt{>B>xU@2M`u&rB5=lf#PA$Olsx2QVdc-|2E8MH&crGgLZmAV z{sF=ICHEWx;)gPq4qhu)WJ@-5!b{3tq3t*q-*+`WKwta>%)}-hLSv0_KH<;4zvAa* z3Uo%!aGIIz4jzMKFh$uEDkQ`XoU3h=`!>k!)mjNd-Wp<9Se=}s^V)%pCnH!T?AV{@NsJZ zSxFJ`lG`?$A2mIX3`Ts&3>(uy|IN21pCN@}-X3-_yKU}5m5xwK(T(B$ z#1+qU4?@Or=r14mDN?suAK#VhYB_{nGhmlVU38VvEl6qkv<5I9*ncPhztXfab?7Pz z@AP<>kFroamG`d%2)>u_dFVpQZc`%8OQyFGDhIM%5qJAE;NQ$Nw)()_TG1WD{T z7Ja$R1vdQq@mQ~$5WJIYmqAn1yd5*P=yvN@8fV+m4nX2~La4Fm9Alhx>)aQu^QI&! zuSM9|?CjfH2g(vO*Is|-hmiN(ywq0e+xdF79eXLCBgWo9)uu;aVOo=I2PI~#1-+~} zmUUu|XvzlzKR`k4G6MHz^_L~0vHmYE#|v5{jw+i$*{2b+F>BsUzC_e^@+Yy?R|IVr zFIfWrVR?4^?~Sc>CX$q;C}3F5ic$Nl%Xuz2)(0Rpy|nKFUNA51bl@&L3D@9$6OFwo z$pWvLrPqd)+DO~SCiil7!;#wU$3^#L0(kWp{m-kW1+%Q>D}I;;+z29Chjs7AEYI26 g5GaK`4=XLsfoEN?Cv-tW?&g+N%8g36-PSGt0e`&*7ytkO literal 0 HcmV?d00001 diff --git a/fields/ROla/izlude_c.fld2.gz b/fields/ROla/izlude_c.fld2.gz new file mode 100644 index 0000000000000000000000000000000000000000..1c718569a4cb9256135126da7c96d732b3e3ea70 GIT binary patch literal 2661 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 literal 0 HcmV?d00001 diff --git a/fields/ROla/izlude_d.fld2.gz b/fields/ROla/izlude_d.fld2.gz new file mode 100644 index 0000000000000000000000000000000000000000..2d3f4623549c5f793ef758dfb878a673dcbbffd1 GIT binary patch literal 2661 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 literal 0 HcmV?d00001 diff --git a/fields/ROla/job3_arch01.fld2.gz b/fields/ROla/job3_arch01.fld2.gz new file mode 100644 index 0000000000000000000000000000000000000000..5f0368721fb05f5769b5d86075385e1c7ce0aec3 GIT binary patch literal 81 zcmV-X0IvTZiwFpHyF_RJ4r*^=GhbnHV`wljE@o_GG63z%u?+w)2n4auUhF?tD(HcT n2B2h&yQ|8b8CC{`mW601pr7FsvH{c6b=9Y@_Hbs literal 0 HcmV?d00001 diff --git a/fields/ROla/job3_arch02.fld2.gz b/fields/ROla/job3_arch02.fld2.gz new file mode 100644 index 0000000000000000000000000000000000000000..8308e8639735a35b6c4cbb79849597658d766cda GIT binary patch literal 5503 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 literal 0 HcmV?d00001 diff --git a/fields/ROla/job3_gen01.fld2.gz b/fields/ROla/job3_gen01.fld2.gz new file mode 100644 index 0000000000000000000000000000000000000000..9f196f0ed515ad565838aee6fcda038f15506bf5 GIT binary patch literal 369 zcmV-%0gnD3iwFpIyF_RJ4r*^=Ghb(AZZI(}W^80K0PWXX5`r)c1yH(I_rC^ZC}7hE zHz_yjn0zrBdYTkkeJqb9A|fIpf*0u(?D4k5O49 z#~e~|g|A87gH}ev$g6@HC)TM#JWJQ}s>|pbMf;B-a~YtKK;t_4qt`~6jf|%?WcGVc z=IZEYgFwfS&kN0{y7?iay;)WBxVa9S(M;IBm1l+g2x+&?cp3^jr_`Ro|YBTuCHh41arb@%34z}U29{g8i>X`mB!)f1T0}&As P5!84B>kP(c6ej=x-si2y literal 0 HcmV?d00001 diff --git a/fields/ROla/job3_guil01.fld2.gz b/fields/ROla/job3_guil01.fld2.gz new file mode 100644 index 0000000000000000000000000000000000000000..983376aee9580c1999a7319e904177555d24a21f GIT binary patch literal 417 zcmV;S0bc$eiwFpIyF_RJ4r*^=Ghb(QX>2etE@o_GG63z{QIdi%5CqW4T|L+yRV@|_ zA*mR;M<&``eps${-ZBVk>7U&!c+%B?PUORq`O+iTK^mFFmv%W>mDVN}&L;A$-H7k` zNTi`wx^^#@xIX55UACX$^Tj)krMzy&4B9@%d_8@n=ZBxqIP*0p-)0LsU&{WMb$R%( z?oarjs>}zfveK=a;bZT8e4p3(ExQiYadp%9Xmwnjd~hMPExeB&`LM``MLsO@VUZ7u ze1j=oVaPX_;vK>l`slAg5AEOVOGVhSqIWu9Z_U!j6VBkX^7GeVzTn$0TY=?c>E`mi zZXEUb9iOZJbu?f1{Nc5FKh=FEkZ)Z*x^I6&_Z}Zj%jb2>7>#`7Oys*3(ujOmVzr2|4%*5g8DYk;$wbGjNscjmutN9 z5!Yw0?MKmhZNE_SZ6%J+Qk@yaSGE0=@BDO9_RELEU*vn6NP0d@>LV1%7cs5FSD4%Z LAC!W4ggyWO&-UF1 literal 0 HcmV?d00001 diff --git a/fields/ROla/job3_guil02.fld2.gz b/fields/ROla/job3_guil02.fld2.gz new file mode 100644 index 0000000000000000000000000000000000000000..724b1fb63bf2d69b9b63293d8c549afaa66990ca GIT binary patch literal 143 zcmb2|=HMvX?UKR3mzAGn9G_mAnPXt2mzI-a#PIffG}i$I9+!(doWJjvS{UD&C6}th zn*Wc<*!}X_*%eZ9FmNw!z3p>@^W4{Mw|{5)xKT% rfBRYN>MPubHkOw^4!Fmyx3+qv`Nt1nu8YjSFNa>YySJ!lW$*ZZf?02zBKD+ zhr9e8EBPyHXUuwR_NyqWnSblO*T2R0N!R$Nh0E_d)3;;2%>Mj)kDFhu5ZHI9{qqO5 z_1_t@nXH^XS0*oi|04OX_HSFGu46%upB-M?62p`Eye27f_j*0I?E6nAuX6sUn)2~a z&BA}PN^9#mE^fT-F8A!s;hggMQnmAowqO3UC~V*T_G|GOWwl$^tDjcwUT?iD)-J?o z>6N0&+I=dor|_>mf4la@)=mGFXU0Ag-*RSBU+9ECtM6<-9DdGlzRCZEHZ|fuR_bR4 z#x60x_eeNu<*ygBJI`Fb&hbR}dbaxfYX*1O&-)h^{QA82Z2Rx$Z@rcG&2gIX|J;uo zZULVsUcYR2b!kt;m#Lqe{wmwK-udKt)28N>u<|?q>fMU$@+a&R><&!7TXU%Dj)aFF zM^5VPrpZ?HRamL zhyPx->HT-!_Mozj{`XJkeDAcbxlp#CB>nMw<0EhFZsvTF?ZO(Nz-UhTIbU$~WASY7 Ki484w3=9BXx;4H4 literal 0 HcmV?d00001 diff --git a/fields/ROla/job3_rang01.fld2.gz b/fields/ROla/job3_rang01.fld2.gz new file mode 100644 index 0000000000000000000000000000000000000000..288d08447ff5bb19ce11cfa2ec463f625983c01c GIT binary patch literal 138 zcmV;50CoQ#iwFpIyF_RJ4r*^=GhcFHZf7ttE@o_GG63z>K@vbP5Jf?AL&tG^rIZEP zNP?ks?_d6IG7aMZ000255sRL-DURrG8{$Z5BYxG!URGNbf37`-+I!kEKA&FLR)pnl sb=-W}d)n=RlyaSOQ{ko}@LkX_xwd(9vFV-Osd7r#h zIF!6qanxce%|{Q`6;`wuJ#4uYx~6E=hSg$KqAKMeY1P#G;N2st|4sb=qumdoRa0}< zS%qAkP$88vR5r}>$f~8m)2!^uAsCMLImb%FEK0(>jx+HAR_x5`;&L;J$HHdwtcp~4 zQzjS>Wi^Crqpe;qoP!9ft+8{ge2$f^h$X}bt9Qn^o*N?n&}U_u;GWf6ZAGhKbwA!p zAIR$E_}pJ@%`{j^>J??Rr&ejvY8r&Q&QY1(E7W(Fm1C^l@4-E*x1w@z-x4KKs@uxx zpuQirVokTISht7$A+401i9@YyJr5?9t?qhi4t81*1s+yj=CO7VNxjmR#`pfSxKen{ za6hZk0t-#F4Jv)V)u*^?lor7Bd9{hE?Xz>mo{Xfp<7qX@>aBz(mTk$+xSLuLTP<MYMTs9q_!I~gnU3Z7nrtl}!wZN=^3;dBgA zv(L&-pTk-yC|}-cRBcDgIli`+uz0III*0$>QM(m%TE|wfIWx)%{#^!Hk)z_Rns#dJ z*+;kp)v$AA0HI_JT8jo&+*-7Rm6=y`#ZuvIv0BTX8d|x8ge?XISd*=CSjniltIAfg z5!tg2YMp>KWgsb=sG4&y9QGd17r$g;t9L}O(E3W-&pl7D+R3K^t1WM3U-kKC`kzkJ!|Y^r zTGi|!m#nz2JKB3eG*RgEi>;EkqO9Cb@#~06m#gbHu?ojhwn|`mD6thJ+^55q$yjNe zik(*7Iasu^XC+qgWgJ)8O2P8nS=mhnbQ}Eqd~+)}YlqoIcxAFsZiVI*SSfFH)moIb zihrT(&+@09VP%udl`Auy@Ry42Xti-4anPx>)z)pL&2GEKR{BO(+@NAAu2O*(Ov$z! zv{-0Sn#`)_SQWz%wahF0DOmxU-#iJeYN_T}$v{J`!m6LP(((H9`>g~Yl2|JpQsF(h z(5jm1#M<6tRsLTCWaD6AoRof`6|r?%xS;YrXx|KJxN1&P=_aak_GGxR)moL_PtZDh zsKIJvWU)$Xg(Rw`71ug@WUdSmbr|`mOF^!6?#N6PF2+jLSy7F1ho*rD;$$mntIUx( z2I3fE#U7S6N*|a+AdeANbt#xOO20m3_;oSDsxk*38_P->T~c1_2dKKG@^SU>M#jlh zUu1Q*)c07OFSV$C&uRwMmsl+-Sxj;I3ae>S&$n7s@>o{lSXAPeNVTkf$7)fDV*UTXwpwhjvc#U2{28DwjVh=Ns#bt2N2ywWnKe z?z?cW((d-F&8MSpzMA!Bx7gJ?wO*TUU5VOp==#>h_rv4LJ$sk_PRUdih^3PeF-_&_zt=TC{C+xT{yL4nPyYUSE?RZ_zx3;x&EH?2Z`*o&Z>7VuXW4E3HV<{a-`uP` z`^UQIDeb>^y_Np*_LcGJ=Q(0?Wc*JbiMzIzul!8Tv$`xU6Q8$Xcf{;@%I}%*0$I&FaQ7>Cex+> literal 0 HcmV?d00001 diff --git a/fields/ROla/job3_rune02.fld2.gz b/fields/ROla/job3_rune02.fld2.gz new file mode 100644 index 0000000000000000000000000000000000000000..4c6e4ca92349153bdf84b573b5538b2b73a4e205 GIT binary patch literal 104 zcmV-u0GIzCiwFpIyF_RJ4r*^=GhcFbZe=hsE@o_GG63z=!3n@15CB21L;J5LKS9Jt z5{2WKDPW)5TW$d% zkT`zDM5whj5+8Y-aRLGPwY-)Ifi%)cBaJlD_~QnciGmD(LAosfJ1)S$5qgAaV+FNg z&*MGdX$IeyYJhdEgwBk??qlhD{Y%uCp7DE?iBAgs)CEIAh79P8p|zT{=lZ9^aqkvJ zYmA&%w;JYn?EB++#*DS%{WQ`@BaJlDNMp9K?s2oRmbk%K+XyxYAqbI-2t|swx7Uz4I3!TC}oD)@DYS@u9j#G zzWC7?@Eg5?S3S*zXl`sd5wgZZrg4nY8AAC=FzQ_oF&4LRx(Y_MA39?x2Ho7O_=QkD zA&1dvUF%sq_by*kYmD;~()hg5OQbcv8T+xzB|;j#5DZfuP9r1__TY^&e5bq7a3QvD k1|RZo4F~eXJRF48@DOvpcV`&b3;|z!0UE9P)D$NG03b=V7ytkO literal 0 HcmV?d00001 diff --git a/fields/ROla/job3_war01.fld2.gz b/fields/ROla/job3_war01.fld2.gz new file mode 100644 index 0000000000000000000000000000000000000000..39c4a9ff14c19918a5345873a392bd1928efac74 GIT binary patch literal 144 zcmV;B0B`>viwFpIyF_RJ4r*^=GhcUMaxgJ2W^80K0PWO44!|G?1VHvr{{L;g*w)B` zV$>5Hkue6;B1IMgY0{(_8NgWNX;i}%Nxrp!p>zsygRc}8>NC)B!Vq%HC_-o2_KhrW yGUMZ!GYuF@YSBTbsCW+fgpiDV*_3IG7;YCYrt literal 0 HcmV?d00001 diff --git a/fields/ROla/job_hunte.fld2.gz b/fields/ROla/job_hunte.fld2.gz new file mode 100644 index 0000000000000000000000000000000000000000..0e9e53733a192f0af73d9bc9ecb11efc04ddf23d GIT binary patch literal 799 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 literal 0 HcmV?d00001 diff --git a/fields/ROla/juperos_01.fld2.gz b/fields/ROla/juperos_01.fld2.gz new file mode 100644 index 0000000000000000000000000000000000000000..f5034e44c5c5c1a7c13e4f013c7316fb9e0de98a GIT binary patch literal 2888 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 literal 0 HcmV?d00001 diff --git a/fields/ROla/juperos_01a.fld2.gz b/fields/ROla/juperos_01a.fld2.gz new file mode 100644 index 0000000000000000000000000000000000000000..89cf0eb698f045366456ebe5c3cc8185ef9498da GIT binary patch literal 2889 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 Tu35KcyL0^q?lN(-6ukfd2y35x literal 0 HcmV?d00001 diff --git a/fields/ROla/lasa_dun02.fld2.gz b/fields/ROla/lasa_dun02.fld2.gz new file mode 100644 index 0000000000000000000000000000000000000000..315437fcb942ead1fd632a0e3ef3dcc0bc87e513 GIT binary patch literal 2450 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 literal 0 HcmV?d00001 diff --git a/fields/ROla/lhz_dun03.fld2.gz b/fields/ROla/lhz_dun03.fld2.gz new file mode 100644 index 0000000000000000000000000000000000000000..3270b97c85631d8aeb46797953439925faf67b20 GIT binary patch literal 2275 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 literal 0 HcmV?d00001 diff --git a/fields/ROla/lou_dun01.fld2.gz b/fields/ROla/lou_dun01.fld2.gz new file mode 100644 index 0000000000000000000000000000000000000000..10eae81ce2b5cc30b2686285fadd799797fda76b GIT binary patch literal 4399 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 literal 0 HcmV?d00001 diff --git a/fields/ROla/mag_dun01a.fld2.gz b/fields/ROla/mag_dun01a.fld2.gz new file mode 100644 index 0000000000000000000000000000000000000000..094e2d7c40d8543240123fea951534b18821331d GIT binary patch literal 3054 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 literal 0 HcmV?d00001 diff --git a/fields/ROla/mag_dun01b.fld2.gz b/fields/ROla/mag_dun01b.fld2.gz new file mode 100644 index 0000000000000000000000000000000000000000..83bba6fc11830ec019ff5641396433366e373f7c GIT binary patch literal 3054 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 literal 0 HcmV?d00001 diff --git a/fields/ROla/mag_dun02.fld2.gz b/fields/ROla/mag_dun02.fld2.gz new file mode 100644 index 0000000000000000000000000000000000000000..3a136874af60060554f053840a8ff324cfbc2577 GIT binary patch literal 3040 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 literal 0 HcmV?d00001 diff --git a/fields/ROla/mag_dun02b.fld2.gz b/fields/ROla/mag_dun02b.fld2.gz new file mode 100644 index 0000000000000000000000000000000000000000..2bb98ff9a540349424b1c2b9a44aa1e2e0d2a061 GIT binary patch literal 3041 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 literal 0 HcmV?d00001 diff --git a/fields/ROla/malangdo.fld2.gz b/fields/ROla/malangdo.fld2.gz new file mode 100644 index 0000000000000000000000000000000000000000..3cda9c16c3af9bfeea1ee1055112d6831de00340 GIT binary patch literal 3482 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 literal 0 HcmV?d00001 diff --git a/fields/ROla/man_fild01.fld2.gz b/fields/ROla/man_fild01.fld2.gz new file mode 100644 index 0000000000000000000000000000000000000000..999a3fcc565efe30fd5dd1c77e0a4b641f92ca9b GIT binary patch literal 6126 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 literal 0 HcmV?d00001 diff --git a/fields/ROla/mid_camp.fld2.gz b/fields/ROla/mid_camp.fld2.gz new file mode 100644 index 0000000000000000000000000000000000000000..2b1739a883d83a6abfdd661385d80efd8fa49842 GIT binary patch literal 5647 zcmXY#X*AS<8pbi0k!4VJ{>GA}P}x%cF_w(2NRjMYLX^qUjCHItvdkzI*|)5bT}bw^ zlObRe0!efoL4j!1`BG`b))0(a(A(JcJ%UAaP@Rir2CmZ zQ=8Mo9p%V&lDhlrFmca~sXu$LNRr9b&bI_v>=7arMkqV^c`{R%uWto98FWy0O+G4d zt>#axC)EU8CBobvN-lp^=ZfJa5_86OgM`Ha$5Yne+^O5I!%%S@OWa$8+g~U&6ZH4W zEm&f`4M;@Zmj|r#HXNy35`IONK;knikeAp>!RVgrjh4{cFk`{8^+b(QxQYXlu^ruZ zQg2X&0sU&>ix;?iB+Rb(3u>Jr#Zb74-tY=*8FISQ2xr4lc~;ZjjI!F|X{bUoUZUx& zGmX%h%Yz~*GHPRWi*rR#aaS3gFmc9AFCg)Vm#|10!88(1Ky4-4N1l&6q@%|gE3}Gq z4*VP0@Pt&lwILlpXz&HzI(mlsr5Arg4A7=`g)c8HBz=rR*$davnSEG#d6+?;=&*K! z`U?DNHnF{GLghtr0vzm{0xwRp)*f;S?@NTLi1Xq)|5KRjPZ=!z(a0PMdt=b(U+Jn~!On&um*vFUwHS+5$vs2Dm<@)5Qmt z1;5=Qu)DzEca*$4nA>c*R)y}Ye(KB}(*;hBFUO!<5dGU3*;-6Q1GSB=T-hBVUt%iF z&B*4nrt1QMEe{G8OLA9!)Ym?il|=5?;NmA}!q*5j7-SA+Qt<1LP}xNhroS1ToB}*qp1r#xv|WZ1nX^|L$oif zOTM9{2%>bb2i5dgV?Q(gsbg&xR9DIGlr;#P0 zaf>AKv^!jqIlgg=6L@V1ISW@cPnYvD>(s$a8i@b7+wUwMZu}AS`rN9MLalosqp#b} z#Z8DoF;h0-8dGf{aEF|e{Hq5ZFW*3qGRo~WG?I(`Z9shjXb8sG|WZ&6HT*T>w zr%23RlO*Dhl{+R2^k7V9d-1PGR8U;J-W78uQ$C~AyyfgwVY)1NULpqR@3m(MHhk~X z3Bo18;v{&9-pDa9?)=aB_JV2ql86HN223~661-|S(4Ig&?Ob+<3%K%dloQy5tTZH) zq}E8KgNsmpv>YJ*b9%nMl41$!PS<>5dC*ybAyjAXlB)kWm=>L#)p5k7jED?hV zwK0b&hX-^@#6qO-tmo^~#677k)fbDyQ(ey6+ao&7ARVG7yDFHxxRj5t1__QaLglVA z3B9937P43eHShF-(W)B-W9^o?zMwyi*lBYxraptVN^h#s@1!1w6!e!%lcp?U$NFp{ zI>5oQuwCMwi`~#l)jQszf>uL!OjG_pJ$3mzCV0Q++xu3fbleL@rs94zmS=g0dnZqT zr`hPoK!3UQEUAkfo2J8aymPI{)w=XH>fb)C-iG(+Xy!P8{NoXLs{HIfo4+?`i+FnS z8*HO<#9BJY0j}}@UN2dGn1XhToMI@#!ah9=Q3teeZdiv<*5VGdCU6PoD_H)BAi+%P zl=uv57mx>v^wb6?&b8&P@$b{BQs)<*VvM2U{N<`5B>96`*1QAt!N~!y2jr3= zyZHxIB3@&3Lk6Ex29C<9kBMzusbt5>Wb!dOa=GZR^k9KbK0A6kw&x7 zl}POu4RcoIOv1P0Eng3gZBgvF?=pNTjG;#lpAEVoGkPJA z@DJbmAPnMdG#da$(gORHx_z)M$nYJ^#RNX%MeBwOoP5Ac71k743j=G9yeh7xLr+OE zjYi?CBk~$GM4?xU9$cQN|HUn zIr9VM)`Ub{BuWX_?*0xcP5U{= zRV{m$?6{2fou86oztag3xddYM9fY&@>~mn(=^5`8-x<7Uy{k3j|W`IC=Gl-F#dCWkM^_;47k_J3Q|7A3a7Sm+ zp&a^70ou72A94`)dN#`G#a^L~2o889`?aH^@ir>WGnYBW3PArrtkMvqp;^ll)dGRP zFp?=aa!64C$}`{SChVM_Jm`Cu0dA>;g|2;kaU_NyAMi}`*fhXeAt_o&Nrr&nbIsmgTN>Su! z_Pdq84(WPwA9vPnehG!o2DsS{Vu0P1nkPUy!SbOehs}i3jVPWu&C*enS@??Z$esJv zZmaLclQ_g=;eP0ZYaO|R(Odc=z`gP38TJB(JFDUu`GU7w=I>_oq$_TjGF+M-&ULuW zeTQp~<@eHOSjQ(zR?fGKn{18SZYjD=tmAJpEReu*s)Ie?k7zc3^W!r`mVx3Io;A{$fT1QOH~@QvNH>g~-Zmf08omguONO77`F87*%{q%Hxu8UW zTUJ|PU0vfZ$d7Of$DX^daadszZ2I?%XR6T`v$Jce|(o!-^m|($k>)Rf<0sBnY^IS>L z$iD#;QvtKWcxc1(rT&N=f&M;Zei!}mz_zkWpqE?GEJj&+faw?YmW`^#@!O8iVfsSw z&oD|#b{zT9BK=MezEm-Xm8tYA4&2A}Q_PiK90UzifXz-K@Gzah9`u4R=balX*3Tl- zE|RXF$7}_6Tu}ODtlxk!mG1R|3d-N`rs7`y>~GKT=DF$c?$oJ9B~HHgtz1lY;E*%^ z?G1@-x)knB_<3gn&UVnl(&P>uSv=w9MeoU*A z%5F>5N3qG9g!u%RAP7PuCLvj#w%qk|$++tftc?~5BFpBe;o&{RlMor5beQa7SrETI zSJKUF!n}i%SP<(pRQFD=EzYMQM{2-GRKY{v1nA=1Nv-QAFjf~ixFqM&+b*Q1dI;iR zTMnUv%|fgCPdX_GzLVL}D?^!AT;s&eDbt4E#*mAmLstWmytj*)r+c4ImMc7?@iV)q z!n1<2W9=`0$m;}~vr~;!+|=NzaV_e@L)9u-H+p2Xr|IgGRaQqXo`HWe3O$EVZ_x?3 zl|tpOYfhfp5$w?slwy@_O-y&_!HylCRr{tf64Pah5Sm&sCmp^r?%sc(Cm~c!TWh^S&&|cAVeN_cHI}*D%1G=P&5u z9oIEpU@$djDX}sR<;atGt_OHgU$Qcbe>hOo9*2!@3j;wt??By~GuZBk^s_pFa7M*A zs9?ST<8rc$#TS<0L1hlul}uKg@lvaeQHhh*Lkmgb&8K~zUn1T2mc>53YPR}@4Ejx7 z$n9Z=C_cl3bT_i9O1Ppq5Hw( zRnF@_7R_Y>)V}UxfUj{IEjso5cBYqhvLZ#r{NWhw{c<~#ELBp$ zRJnd9GvIY4%QNV|jyYk+=0()J!8^1t6w3;gP8f|yd++#Q`NO;~ZL8&Ev-+k!sws0F z;(Nc<=^(zjvcp*fvbk5MIpAF1AFMX`F~?CavFfAGOi1$P1+7V1mI-lUPtFko6mKR_ z-snca=9zWpE-$ur;?lw`m~qBsAJ?QRWVpH6;CB&dES;L_cM%hN4dku;tixSZ#O)I; zU^j%&?j5)q8;ifl*&CtZtSwk#ufh6SFaK8r_#FrrWjr}DY*rWL`N+ViOvHOJ8JV4q zfCyHaN;9o^cse&JU4;kG3JI7kQrgpP-e+gs44-Lxw7$cv8dsaqog%GEnEoqonV-td zXnDr>47%#^jEbrFIaK;?^>|MDqpn=9CZ~9bOzWk1X8`^!g643F?to7 zbW9!Za@HVGbJ*WBf&d5z}U{Ozg+i_QK@|`AvmM zeErjvTO5`zFhTgzl1tT$a>;5jT54~1-b1du4qXZl=GBu>Nx`!dny|9)@$0D_J|03v zJzyo{QiT;J=;;mOt*uFtj{+KF=!hwFa(`n|{(IZD%17p%{~yl$7PJZQ?4hs2Yo`Zc zd-^Iz(zlRjq?};D1wX0hzw3T*mnKZcMU=CyTvzeG4R}JcA;p7lVPW zY-22>iUmF~h%L!J)iFYMCJL{SF<0(m^dX>I63st8{^6?>*$a<-;J_+=_wI9-4JW@ zYP{ESVw!h$aD8?1RR7Ab2tE!P(CGn>Wsu!+0Ke6#D+-97P%r8O66k8$q!7gknFnIj zuk-PSTSQl24o5n2A*67WBm2Xv_shbdk77l`zw-XrC8OjM7xc+GGOC>sRknU1o$2+v z?Vo$8pI!|+$fQA2jn`a5-Y*U}MSYf=ds|I&TSF`w@a~7VjaYC+WA4EN#lLC&86MH{ z5YHD)(bBVL$CI8Y0zQ!|S{W%PE3Ay_KSeqsc5*JWi@_;lO%)MYNJBuE`zGbyuL7}! z#6vTRn@B~Tk@PIKqeYCW#XF+rWN?>n9IR#~T&8GoSl%eG%oJv%-<{R*3m0O-Cqd2A zbhv|bS%{m9xkxTnd@{5~@jHo$&n#N!_urVf_9(ngnXz$9DVmiTmsM?b;-JKPto^x5%QPGGJkj6o`Pp1{7Ns+r7;2%%;YD=Q{IOJLBDfZ*9VTN{FOF8E-mV8 zB1(qOAM>s?e+l2^dH_T_#!kz;8@v$Qm}IY{jk8fXTz=aFos}#bP{)1ZkPK|Lvf^26v&E#A_O0oc4tV+nMv6H8ebqW4fCn7&k?aE77-i zO#E+NZC9jQOODN)4%PPKh07lZIH0iQx}7=1N96sp zJ^6jvl@FuzVH>A9^k2Lo8jDKhC0c$Tbf&-c?{}HsDe+49@8S|=L1scz>y(Fo)ZPe% z%P1BlCl6FqV>--20ak(@1S5wy^*=De39!akN?<%=@s<()u=fA)|Bm7*6}WEYI5LCy g|5f6t6*zrm68X9AcwOqNa5d}mG!trg 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 m;QxN+OSTz1Qw>=L(Qa$9MYfWC9hoL1 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 literal 0 HcmV?d00001 diff --git a/fields/ROla/mjolnir_07b.fld2.gz b/fields/ROla/mjolnir_07b.fld2.gz new file mode 100644 index 0000000000000000000000000000000000000000..9511be4eb4bd2a9c3cd90a882400ef568a3b2705 GIT binary patch literal 4703 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 w`!UV+gPk1FI1c&NVdeswC~;3g zYUgj~O{o&U3>s377u8+quj>4g$2{4EPiG>t_mcxcmK&x#y-;6u>ePg|bA><6Wp)MZ zTD5R%t&rQkH|{deJ~%it%WTWrQQqZYdg;~Y!sD{5W!_DedA7CXf3D2@x9Z|$@23f7 zJ+W}xy=DET+ZDX+QKv432A3A6JU<>_w`)~dzqUnR_1!Puep?)SHtm7h(TOV--*I=2 zoA>ZU%E7gF97u}JmeTPHc3uN~YhvsPlE^3!z^zb$4N*0^84>3g@I z@2SH{ck5%(&)qi^^%eK*kWBq));CK-Id0dhE<3&_A68v2{4v$Qg6VVN1Z9hN!U`Pj z*{0FgP1Waxac@?9nEtx7n)kSt{b89`Y&T9Oy1TcBJxOlwseZad?bXy|i!H?$^>{js zE%uncPTB7{Xltgya52_ghal=@e}-+p^6%?)1ce z-9L5u9FKQ?NQ};Z7-7GTO;-KT>D-CRH!lSB=s%S(aO&IjAu0Nahxrz<`Te6hgl%Ptjr+rDQOMKO|=r{8^)$v@%l1Wrd7 z9tMkkX_fiE=Gx7dVwOyt;*Ay7q?e#jy@`QSj1w{?CnA_iVhdf z-RGHKZtQ(#qJG`~&o_(gHr+ZWc-LRO_=#p%@f0QRJ_8G*b!vq(rj{4lZTTMjhPRP5 zr}J^uqC@{}x8}smJQyFp^~Az=6=sIHKl6=?S^wl{bzkq%UV1jtChCb=@fNnc2L;6i zwy`t#?Ncm^xZSPTKU>~f?;Z8&;S+^f&y5wk_PJRsayo4(<@aJ?zfSv{=BYBdPqRNy z)aXB6C|elsH?^kau=jtRK9)|C!exb5j_a0a3Ao?2DG%qE>AWN(Cs(!o=Cn^6r!W5Y zt4qecNMpC4-0KhP_IApMzP`HmWYL|E-u>w(-`+2Z+-y7N@Vi$Qe4;3UDH2~Mw<=!N Lf%lNh90mpeNe2Ka literal 0 HcmV?d00001 diff --git a/fields/ROla/moc_para0b.fld2.gz b/fields/ROla/moc_para0b.fld2.gz new file mode 100644 index 0000000000000000000000000000000000000000..42d93e19ebfa7ab29c5eeffd370fd18103ad3242 GIT binary patch literal 1056 zcmb2|=HO`D?UKR3mz$p)UyxXoXpp3rmXl({@OEx=(Q5?(mxZt1)yGDLy>eswC~;3g zYUgj~O{o&U3>s377u8+quj>4g$2{4EPiG>t_mcxcmK&x#y-;6u>ePg|bA><6Wp)MZ zTD5R%t&rQkH|{deJ~%it%WTWrQQqZYdg;~Y!sD{5W!_DedA7CXf3D2@x9Z|$@23f7 zJ+W}xy=DET+ZDX+QKv432A3A6JU<>_w`)~dzqUnR_1!Puep?)SHtm7h(TOV--*I=2 zoA>ZU%E7gF97u}JmeTPHc3uN~YhvsPlE^3!z^zb$4N*0^84>3g@I z@2SH{ck5%(&)qi^^%eK*kWBq));CK-Id0dhE<3&_A68v2{4v$Qg6VVN1Z9hN!U`Pj z*{0FgP1Waxac@?9nEtx7n)kSt{b89`Y&T9Oy1TcBJxOlwseZad?bXy|i!H?$^>{js zE%uncPTB7{Xltgya52_ghal=@e}-+p^6%?)1ce z-9L5u9FKQ?NQ};Z7-7GTO;-KT>D-CRH!lSB=s%S(aO&IjAu0Nahxrz<`Te6hgl%Ptjr+rDQOMKO|=r{8^)$v@%l1Wrd7 z9tMkkX_fiE=Gx7dVwOyt;*Ay7q?e#jy@`QSj1w{?CnA_iVhdf z-RGHKZtQ(#qJG`~&o_(gHr+ZWc-LRO_=#p%@f0QRJ_8G*b!vq(rj{4lZTTMjhPRP5 zr}J^uqC@{}x8}smJQyFp^~Az=6=sIHKl6=?S^wl{bzkq%UV1jtChCb=@fNnc2L;6i zwy`t#?Ncm^xZSPTKU>~f?;Z8&;S+^f&y5wk_PJRsayo4(<@aJ?zfSv{=BYBdPqRNy z)aXB6C|elsH?^kau=jtRK9)|C!exb5j_a0a3Ao?2DG%qE>AWN(Cs(!o=Cn^6r!W5Y zt4qecNMpC4-0KhP_IApMzP`HmWYL|E-u>w(-`+2Z+-y7N@Vi$Qe4;3UDH2~Mw<=!N Lf%lNh90mpeO%wns literal 0 HcmV?d00001 diff --git a/fields/ROla/moc_para0c.fld2.gz b/fields/ROla/moc_para0c.fld2.gz new file mode 100644 index 0000000000000000000000000000000000000000..6f938f7267e8a6aa766089887d4ac1f4c0065002 GIT binary patch literal 1056 zcmb2|=HO`D?UKR3mz$p)UyxXoXppRzmXl({@OEx=(Q5?(mxZt1)yGDLy>eswC~;3g zYUgj~O{o&U3>s377u8+quj>4g$2{4EPiG>t_mcxcmK&x#y-;6u>ePg|bA><6Wp)MZ zTD5R%t&rQkH|{deJ~%it%WTWrQQqZYdg;~Y!sD{5W!_DedA7CXf3D2@x9Z|$@23f7 zJ+W}xy=DET+ZDX+QKv432A3A6JU<>_w`)~dzqUnR_1!Puep?)SHtm7h(TOV--*I=2 zoA>ZU%E7gF97u}JmeTPHc3uN~YhvsPlE^3!z^zb$4N*0^84>3g@I z@2SH{ck5%(&)qi^^%eK*kWBq));CK-Id0dhE<3&_A68v2{4v$Qg6VVN1Z9hN!U`Pj z*{0FgP1Waxac@?9nEtx7n)kSt{b89`Y&T9Oy1TcBJxOlwseZad?bXy|i!H?$^>{js zE%uncPTB7{Xltgya52_ghal=@e}-+p^6%?)1ce z-9L5u9FKQ?NQ};Z7-7GTO;-KT>D-CRH!lSB=s%S(aO&IjAu0Nahxrz<`Te6hgl%Ptjr+rDQOMKO|=r{8^)$v@%l1Wrd7 z9tMkkX_fiE=Gx7dVwOyt;*Ay7q?e#jy@`QSj1w{?CnA_iVhdf z-RGHKZtQ(#qJG`~&o_(gHr+ZWc-LRO_=#p%@f0QRJ_8G*b!vq(rj{4lZTTMjhPRP5 zr}J^uqC@{}x8}smJQyFp^~Az=6=sIHKl6=?S^wl{bzkq%UV1jtChCb=@fNnc2L;6i zwy`t#?Ncm^xZSPTKU>~f?;Z8&;S+^f&y5wk_PJRsayo4(<@aJ?zfSv{=BYBdPqRNy z)aXB6C|elsH?^kau=jtRK9)|C!exb5j_a0a3Ao?2DG%qE>AWN(Cs(!o=Cn^6r!W5Y zt4qecNMpC4-0KhP_IApMzP`HmWYL|E-u>w(-`+2Z+-y7N@Vi$Qe4;3UDH2~Mw<=!N Lf%lNh90mpeQ6T^; literal 0 HcmV?d00001 diff --git a/fields/ROla/moc_pryd02.fld2.gz b/fields/ROla/moc_pryd02.fld2.gz new file mode 100644 index 0000000000000000000000000000000000000000..48e976879689c476c224b49ee6d760fb2ccc46d6 GIT binary patch literal 758 zcmV*3v}?)tUh5rM~1rT#7+K6o5e z>hI3w;a@E#eg!o%ubaom$J=>qYbp2exy&QoS9suo2Oh(Cd_RfD^zf!t-XofR`PtaX zBj~H^JodG$Sk?RZed>?fk#}Gzh>>8;IWy0KG%C#Gs>RgKHhmzM0bxR z`Yro79xG}e_`CjnYAhaj#3%dUfk&nmm6<)XS+>n#PqppWvK;o>3?AF6_G>D-M>a1W zCE$Sv9$Gg4iJ8qin}?rwMg8rx>JiPxc|E+VyoZIFLyf5P`1|+&pYQOv(Bm$Bg$EvZ z{NDF(9z`D31WQb{{N$X-V@36_$ajuQnE-Bl?`^SD@CX-iOC~^_4e|e2<5T z6{|(m&B42PJa_o7xytrErJan&DIQCkPx^S|>y4h#LwlJ2;ey9y9)t9iI**PfSnLty zNK`O^hjw+0;<1lXJNrbBc(m>&i;`|yukdIM z9(eQskGVbavrI*;;2_(S9xJUoOwD1h;em$@9(WwB)qqFF!=F~xZq3Zg$IjrPQWKHS zi-&%4J^~Ls;*;|cc+BS!&7*U{qYbUkvrfjNBwK6nsLR$GJnFKwW;Txo!|NYyPM*u- oeZXTZ@E8j`#$pbSx*2sLH}iOV@ED7;A2Z$f0t5ij=R}+U0K6KGp#T5? literal 0 HcmV?d00001 diff --git a/fields/ROla/moc_pryd04.fld2.gz b/fields/ROla/moc_pryd04.fld2.gz new file mode 100644 index 0000000000000000000000000000000000000000..cfd0cb3c2306da2054b8bad7d881861945299fe8 GIT binary patch literal 782 zcmV+p1M&PHiwFpTyF_RJ4sCB^UvP4HWH2-?W^80K0PUJvcET_W1(OYQ|4U+7J(MH_ z*Cu{QmUMIH0~Z34rgdby?PGZ?AgtB}2tf~D{CTXwIXuoPudnx4e2z)Ia2~-qdmJ}R z5YF||!vpm21-i%kF#_fL{QVe#l1F5+`;uss_E*sW-CrfIdzH(U_QhjPRXyCnBe~?* z;z?3$xnG6#2o61bF~wuTabVKVV?qKjkI1(O_q*<(hc9Y949_D~sT(|Gs_Nm69v5#l zzJV@^t6x9Dcm#(YzTo+zF^zfxFptQ$2+tqxpocGdJrq}juFwM>tshYh4(HMMQRN$| zv6%k#!>vbf=-~^VKN{1hCjj$^e2eh>;SPHELds)cg%DX>1U-yC{0;%-F&JvFkiq0~ z>xUbU;LyVt(jMDluq5MgDZM6`SK4F$;&aTsMvrV>$$fic6rrT@=MQs_;LxKGPxb9z z0x5a?T#&qdJg-vUt?~%4ewcX(_uZ-UO7ZtsW*)*+dWb=fLcDF9ISHXev7*rYjMhqtB|L^_}H4s85KRrI;^ac@GN8tKZaNs;5 zt0CeoaWo%P(@L(8NqG+e(5 z4xC416`nsjGHEBEM@tf(KkA@|FKYX)vFJr{_3K9%kE=kIQ?IhVn|WB7RHcUNSHXev zh^)f%M@J^@1oUW0!t+NR^zcQg?>ZMtiY>1nVLj3}dY)>9dci%snq*5u?^mJVe%Bpm zk7b##p%Y}U`t^wFVOd!l=kGf`?74?W920bp$Yl2==f>=HOzMU62+r;S06lzRJ+;O7 M1(%3`?nImb04~sm(f|Me literal 0 HcmV?d00001 diff --git a/fields/ROla/morocc.fld2.gz b/fields/ROla/morocc.fld2.gz new file mode 100644 index 0000000000000000000000000000000000000000..2267fe68f424dff2f0c1e8599bd4f91418b6804a GIT binary patch literal 3887 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 D2L@ll literal 0 HcmV?d00001 diff --git a/fields/ROla/mosk_fild02.fld2.gz b/fields/ROla/mosk_fild02.fld2.gz new file mode 100644 index 0000000000000000000000000000000000000000..ae4007a17e96faccb3c0dcf62f036fdb8af72107 GIT binary patch literal 3782 zcmb`H={J;(#0Nuatt zAD%K$8UC@d;s2d$5oj8U*CeIlK*#YFx+pkrtE_kyv~-ZU;ZAm*UZi)nQt)zA?u=72 z$af=$d(x}7UOe}woA<*o%58`z>*@OKB2KCT&muvju%epti4hn)2X4 zSSt0O>AIsn6RFLz&QXt@8$%vb$wGn9FE9E{l>}-)2?tDk?O1EV5Tf&I@o^}oa4e|5 z``xYU$Plk~AnvxWI|JY}KME`_YaFxGZ07h%kGYG~bkR!u?P@HSaUh!pM{$WJM`8u8 znDe;zR(-U=SG=+!xOnrh7(;zw>Ru(vabx9O(h`zS;o`JVEcFEh%vq31y?5(T|2yJ(fSrxpFEz(xVIKR)IZea2wd_u zw|Jvo9;{FQ51Vi7^G>q1( zxH;KXNc2GBEX^mhZeTkQQ+iLuGyiF5Bz!L;6@H`Llls|9^3-|yp1Q;kDY2f&Y zm2HUKt_|6m4yc5;D#pJKES=>AVW*Evd=Gr{n0ih)o-#M7s~n@ZDWUwHUfv&=7b0XD~ka`Eh4u-e>uT)Fq}t2S=@J2w`gw z(aW*%Ra1mH$vTn!+@@5fZbJKy4LuTfn9^>g$yTH$Ce|=VttU~0$!)oI~NBxaLNad&dQJ8 z;d+a+);ES8iiDUhejCjx^gI$h>{B}1qz)z;%R*mHfk5e06HE{kGX3==^2b=JRgTjd ze%X4W1o$E5yo38b&7cmDvHFUI2x*Cc+QvLWisx+=e(*l|Lw-Ew!0B+P!)#bie% z*`g`7sp=yPBlFD@-O+nR`jE#HG+Ipbvn!ez77zF3NwR##*URMXMWAzBr#<+8W>p=7 z@*MiPuLDE~QO&t>Hp7hL*8q;(W+UjTdbh~J;J%&|F&+M#s=Kq1ChhSpPYHXKRrTX1 z&S8SaC^z5jYJLT{UaQ#!`+&2Ic?!R`C`8B8t&A3A!o?aJoonJW`-@F6ZDQ*!b+=HR zO9$u`?S;4uxtd`a%a{Ed%oM@=(q+E>W@d(^?ZEJ0q+AToUBrTgUmmzqmBDo8H6r{@ z8yDLEhUTd1>qY^hav)`A)9CdGQzY>rI#k_rQ-ID|-=(XW0;@pS;jCxX0JM~)>L%t) zYw`xx)_f_L-F!?75!&RO;1cy8X3(2YlZKO;$+jg?<3LY_gtU`2GEH(zaCvpqv^euC zJS+M>*i3L~u6EY^Q*x5lt^ht<8$%n}RFXRKD|T6?m6#Ms|MpR-p?o;mhyy4Ik< zEx+y)D^6p*b+J@mFai`_?Hj1()voX~ylOt2CG7JkBEpH#{-_0l$YF?Di?bnL!C~++zh}g0FqRyzCIXnJ5+*%}i(rKaA@i}AOwov{NC1LYAQnluc)#o-^ z0j(6l9m9j=5LPVP_HFji@F42rwm8xx+yn3oShQCRg1Y85aacLC6}^=6HL+BUBtPW* z#r8b@2lJW}E`ZHhrbhO5#?KsVHNyA_#k8tH?o%l&b?wd#Hb+nU)qVZnRk6u;w%+Rc zE`*TMDh7cOMEHNA_u@DFe!N!vo}Vi?+BJx}k>AV-3{>f`9i8EF>t<^YwhX1xM^U(+ z0eNUcUwFyBy9WUc?RpmEYS(RJhnBKV+Q<S~L zH>E!syck1`C76U+c8tNN)Bp~DxHP4Ej-Erny@fsqBt4>z-3nAdn;{Eq?@TP3+c{>S zM~^#+*u8DE%yzOUg79*o$SR`_j<1WvRk1SK;oIfbmyrearl2&s0$ysV^%?F6SZgMrJmlgM(c;+INcU{T{`Qra^litA{?Ug1{Q1+UVW<%bN*LTO@b z6gOsl1UEO0%d!rzbJP-XzWZ_)uE>jS+(8Ga=~DySKUiPP*R;_VunL;rqt%y#3PJK~ zSU27-k6hsyZorOjj4zZOAH7ABuPKfSN~F^GUUeITj;sJDF-A{(1OH*VGy0Siqk+)> zz@v}0lnmmEospA8yORwdO6DUIXQrNRa`^-w&^-7AUY1Rer$w2@tVbsFRk@XiOzcK)V2+o`tIL(NPpWgaVpauRK z8Em<#6LQAJGED2tgM&r~zVk}Ahe(ijdi+S>B`rMw{Mw*SnH)mxF8E^XS zRABNXofMtzs;m$;rx*2=l}X*bLa|n5Bdzk`26lNVspY7am$^iA!E3C{jDBap{6fI= z=|%r_1D)5E2O2TJiESCc!Qku0%k)U-d6$KFp8W8-B$DS$AJPTGPoG;SF&*RM{E*(o z@7$UPJX&CQk(>OB+`A^>riUy*-R*X5618*j4$yP8!$H~V*+y6abqIX^_kYvJ0c)w~ zUmT|8Vu>Pv%Jjr|BQIDxb)kiwpkE=q(D)i11*6_JS-d&?_968R=JYK_*!$g<#nO}| zbzlUR;<(Q7&?_FRx!BoHFi2o+xDiaioUaoO^f`U(I}}#h-sh9q>|TGHRsQCw2UU~c zitII9n|Vj`r#<++Z>dMB5J((c^eJ4)eWwc86ojGlMQXL8I1=TS3&uk9c+3tcQqB3L zk>=6VAwzK!k5S@!NBJ|j?|d)bjln;54bzrpbe1Z8#XZ^-`K+oJbzfx4dzkC%rVPAHrYW5VNYPeHFibns5M#JC9NxOQFpC@r=U1$Hn5hlOAuF ztQ7lf5THJ-XyU*;K+e=;c+4b<%YEI4g-f`>(Ty2b7Z(|T%^jnl3ok3-7L`=`xYetxmZM%RPG1zQr3Sc6T9 I-9JMA1NZr+ApigX literal 0 HcmV?d00001 diff --git a/fields/ROla/new_zone03.fld2.gz b/fields/ROla/new_zone03.fld2.gz new file mode 100644 index 0000000000000000000000000000000000000000..3cae86b8f9bb7bc6151851659f9ff855158c0dc9 GIT binary patch literal 919 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 literal 0 HcmV?d00001 diff --git a/fields/ROla/odin_tem02a.fld2.gz b/fields/ROla/odin_tem02a.fld2.gz new file mode 100644 index 0000000000000000000000000000000000000000..958dc2881f4fb64ec33d7c3ea147ad8f66c580c0 GIT binary patch literal 5947 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 dJ95Q#)~X3Btq+kt?PnS4>B)n2uaA z9l2sUa>aDyijzCmN;Fdug6yysVi0@~ii6Pkr#s$rP#vnm6i1n4ltY?8arhuWTh$4j zO5Ic*NIC0&rmF#>F0XVz5-HWsuO>u+Q$ZS!wg>{SU6;5opsIyyt@{#b{Kv1rC0>d-M%D^k2e*{s6R9~9{DGn7|@u?_-byrX~4LPD9V z>aef7nsb&pw3%v-@AF!HjxauGuTm$fINEcBQbY%orySUy8SB8(6hjp=^E%euhVGnp z-6I{QiR$wQ#evOA=fGw;9dBE#gi5}ss5%;(4B1C9st&msYdgaj_3IL`8`$A(vtLQ9 zTj}OR4qHdV7{AJaLy|cbbOp`lz}C==4rJNFjw&QT2l2ieCkzhoJTeA?IfAPyGf{&~jtPy5^=m)N5vfddlh_}K=%=kwg(vd=6s$?1`1fPTUq@I{BQdaT0% zF;H{45{AY9=b)SymSkN^KUgv~V%>t%OwyqlAO+cb|i^r166nPy+$Ts2~ zaWBYV2jN|K4u`(ZuXGGK&fk0T*D2Bua@?y$Iq1*RQH}*&6l=*;mHc&D*~1;dpNmb0 zkV{p`U#FFQIySjfmHc&D*{5TZOI68VrVOLRqXT->>%yB{T?D6#hMsL33LdGI7ytN!kCVB z52thB1#FJ+8(DAxqay^uDA0Ah?kC^>h4)+SVRO&=`u7i(L*BY`l-1CVD?RKSm&?!Z z+fPFdd8_nFhwWXvYL{n_9nw~3sGbht1`mDgh(&P z{XjP)>7=ffbTnSgIn2R_>=N$wD_aetW2voW^)c!Qa5N9-z5`7p9)W&!9P; z?bwwR$9>PDIO^ZFd{z4hNA>Hkn{rW%+nz;oEQ%-mERJG?x>Lo_V285T&ZJLqfcmX5 zg9GI!bD;cglCchyk3xnzI`gCv6bIN3=#YmH9O&~(btoadO>o82)=TLT|&-gx5F}q+V zaoBqq=CEBv1M_aXR)ho2iG3Qn3g-~RY*jagq%eL7a)>*uF)R%DU1rxqCAdYvi z7~ZP@()&SU7mSE-(3&#^-9hWj7-R>nFLO{Gw61`djxrCeDIm_`KoB) un2uaA9l2sUa>bb)pBU4TE2bk?Oh>Mmj$APvx#BdAv&26Yu@XQ;oB#k%GHvAm literal 0 HcmV?d00001 diff --git a/fields/ROla/pay_dun00_b.fld2.gz b/fields/ROla/pay_dun00_b.fld2.gz new file mode 100644 index 0000000000000000000000000000000000000000..ce424c77ae44156ab3ec559d7a26e77b1ac91d00 GIT binary patch literal 1284 zcmV+f1^fCRiwFpXyF_RJ4sc<4Uu1P|Ffd)~X3Btq+kt?PnS4>B)n2uaA z9l2sUa>aDyijzCmN;Fdug6yysVi0@~ii6Pkr#s$rP#vnm6i1n4ltY?8arhuWTh$4j zO5Ic*NIC0&rmF#>F0XVz5-HWsuO>u+Q$ZS!wg>{SU6;5opsIyyt@{#b{Kv1rC0>d-M%D^k2e*{s6R9~9{DGn7|@u?_-byrX~4LPD9V z>aef7nsb&pw3%v-@AF!HjxauGuTm$fINEcBQbY%orySUy8SB8(6hjp=^E%euhVGnp z-6I{QiR$wQ#evOA=fGw;9dBE#gi5}ss5%;(4B1C9st&msYdgaj_3IL`8`$A(vtLQ9 zTj}OR4qHdV7{AJaLy|cbbOp`lz}C==4rJNFjw&QT2l2ieCkzhoJTeA?IfAPyGf{&~jtPy5^=m)N5vfddlh_}K=%=kwg(vd=6s$?1`1fPTUq@I{BQdaT0% zF;H{45{AY9=b)SymSkN^KUgv~V%>t%OwyqlAO+cb|i^r166nPy+$Ts2~ zaWBYV2jN|K4u`(ZuXGGK&fk0T*D2Bua@?y$Iq1*RQH}*&6l=*;mHc&D*~1;dpNmb0 zkV{p`U#FFQIySjfmHc&D*{5TZOI68VrVOLRqXT->>%yB{T?D6#hMsL33LdGI7ytN!kCVB z52thB1#FJ+8(DAxqay^uDA0Ah?kC^>h4)+SVRO&=`u7i(L*BY`l-1CVD?RKSm&?!Z z+fPFdd8_nFhwWXvYL{n_9nw~3sGbht1`mDgh(&P z{XjP)>7=ffbTnSgIn2R_>=N$wD_aetW2voW^)c!Qa5N9-z5`7p9)W&!9P; z?bwwR$9>PDIO^ZFd{z4hNA>Hkn{rW%+nz;oEQ%-mERJG?x>Lo_V285T&ZJLqfcmX5 zg9GI!bD;cglCchyk3xnzI`gCv6bIN3=#YmH9O&~(btoadO>o82)=TLT|&-gx5F}q+V zaoBqq=CEBv1M_aXR)ho2iG3Qn3g-~RY*jagq%eL7a)>*uF)R%DU1rxqCAdYvi z7~ZP@()&SU7mSE-(3&#^-9hWj7-R>nFLO{Gw61`djxrCeDIm_`KoB) un2uaA9l2sUa>bb)pBU4TE2bk?Oh>Mmj$APvx#BdAv&26Yu@XQ;oB#k+B5mdX literal 0 HcmV?d00001 diff --git a/fields/ROla/pay_dun00_c.fld2.gz b/fields/ROla/pay_dun00_c.fld2.gz new file mode 100644 index 0000000000000000000000000000000000000000..681f9d7671d5d6916540edfbd0d28fd293ab07d9 GIT binary patch literal 1284 zcmV+f1^fCRiwFpXyF_RJ4sc<4Uu1P|Ffd)~X3Btq+kt?PnS4>B)n2uaA z9l2sUa>aDyijzCmN;Fdug6yysVi0@~ii6Pkr#s$rP#vnm6i1n4ltY?8arhuWTh$4j zO5Ic*NIC0&rmF#>F0XVz5-HWsuO>u+Q$ZS!wg>{SU6;5opsIyyt@{#b{Kv1rC0>d-M%D^k2e*{s6R9~9{DGn7|@u?_-byrX~4LPD9V z>aef7nsb&pw3%v-@AF!HjxauGuTm$fINEcBQbY%orySUy8SB8(6hjp=^E%euhVGnp z-6I{QiR$wQ#evOA=fGw;9dBE#gi5}ss5%;(4B1C9st&msYdgaj_3IL`8`$A(vtLQ9 zTj}OR4qHdV7{AJaLy|cbbOp`lz}C==4rJNFjw&QT2l2ieCkzhoJTeA?IfAPyGf{&~jtPy5^=m)N5vfddlh_}K=%=kwg(vd=6s$?1`1fPTUq@I{BQdaT0% zF;H{45{AY9=b)SymSkN^KUgv~V%>t%OwyqlAO+cb|i^r166nPy+$Ts2~ zaWBYV2jN|K4u`(ZuXGGK&fk0T*D2Bua@?y$Iq1*RQH}*&6l=*;mHc&D*~1;dpNmb0 zkV{p`U#FFQIySjfmHc&D*{5TZOI68VrVOLRqXT->>%yB{T?D6#hMsL33LdGI7ytN!kCVB z52thB1#FJ+8(DAxqay^uDA0Ah?kC^>h4)+SVRO&=`u7i(L*BY`l-1CVD?RKSm&?!Z z+fPFdd8_nFhwWXvYL{n_9nw~3sGbht1`mDgh(&P z{XjP)>7=ffbTnSgIn2R_>=N$wD_aetW2voW^)c!Qa5N9-z5`7p9)W&!9P; z?bwwR$9>PDIO^ZFd{z4hNA>Hkn{rW%+nz;oEQ%-mERJG?x>Lo_V285T&ZJLqfcmX5 zg9GI!bD;cglCchyk3xnzI`gCv6bIN3=#YmH9O&~(btoadO>o82)=TLT|&-gx5F}q+V zaoBqq=CEBv1M_aXR)ho2iG3Qn3g-~RY*jagq%eL7a)>*uF)R%DU1rxqCAdYvi z7~ZP@()&SU7mSE-(3&#^-9hWj7-R>nFLO{Gw61`djxrCeDIm_`KoB) un2uaA9l2sUa>bb)pBU4TE2bk?Oh>Mmj$APvx#BdAv&26Yu@XQ;oB#k>5^d)I literal 0 HcmV?d00001 diff --git a/fields/ROla/pay_fild04.fld2.gz b/fields/ROla/pay_fild04.fld2.gz new file mode 100644 index 0000000000000000000000000000000000000000..683db837f0ae78b54bb57f2965a2edd10bea1fcd GIT binary patch literal 6356 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 YhG7_nVHk#C80MY%25^Z7=tP_V0MuOGxc~qF literal 0 HcmV?d00001 diff --git a/fields/ROla/payg_cas01.fld2.gz b/fields/ROla/payg_cas01.fld2.gz new file mode 100644 index 0000000000000000000000000000000000000000..7f828d6b0453cd63925a186a7362dc28923d400c GIT binary patch literal 1267 zcmV?dOj3jO>Y6vIu$NgP^8rQpkiGV9TaHR*XinyHxu1vIl~^L z{kU#R*M;>{{!d`)O0tk&f~yj6m2>6cDh;3*S78fWRk~Wf*_CI_7q-Av+(+Bv6;}?f z!dmJCu3}el560W!N^-?}ui$E#=sfXZSL{V!=_)nDx5rMV>b(G5>8Y;Q`*1A(gv+{0 z7G;HF^RO#P)iko29NW`vl1sRPt6~7b#j)jQ2d?1iQh;cWQwKkU=oNnZGncPEuI3zD zjXq12U6rudnZ`79m7})Yp(L#xQgzR~T1fnRE?Yox2JL zb-1dfzJ_cuhM}Kv72V|iv@6Se4~?s*T&YwI?y5=cxAWgkR9C06Rz6|3P!7LN(}1_QTHxwhg1y2=TnVp;uk{b_D*SDe;61LIbKTYLOchm*0dplC zp<-9}GZ|Lh2bHVsg_W5s%IykV>47UfaHS{Xirz)By|6NqMY&x`9PO@xJWm#@GFg?} zjWbscu6A-gNVmb&FTzp$7q}A0xk3+eWK|12TbAxAyq>AL*4|`Yo!x(VJyUh9y2cH!TorHx>dMI2OI>j+QW(S-0=UuxS9;(|4_xViD?MYnGRtjB zC~E&Vx%a~&#m%aQa}_vBYdNm8W4A8%S+CUg4qRP|zdhS;yK2ybbGvfK?X*ic1FUf_ zr8wIQv8q8`r9TN**|_|dtsODoipG7=mHc&}xxKxe@B{zu(agQDqq zJk7bHOYv{wxRr2sRob)b`#6TH*w&n`mdARKF2mI?aCMe~??+lGb!O`-b6~$0H~wA$ z_AcM8H%`I#;|;Fjircu#%?FyfV(-q~sO4<$bY*R;ohx4XVTtx)MZyzTwzgj8ifz6h zJD07!D{ANHz1HK((iU7@&DP#k?Ch93KH|#ERu5OMwJRS#%;1V1Fl_o9S8=O*x*X1>r1PLGBRzB!&gsWaBJD}`F`LYyQs!SZZx#k8pF}v|WFqYha3vqBRyD-x%*8^fI!C{tTc9??+Dcn+a?d7A zAIYXArOly{UGvjY1XQnc+23|4fT}rG439Um+`rd=mr*nmUC6$?e$A~c1v`}%hjsJg z>--ICTPRKxpGF)&*!SEQFJN0`rQbm4ac|pnl4_U=(Wg2|gxFR=s4hcC#HRmvG=g#} z+>@{P%4^?`WmZ{C!Nz_PL%r)_my4G&PkcA8%K1;_Ie5-|Z-ZYHs>RqEkzVCHdkMqkb zFkE(w$U zGez=ai{$DM;*Anofevdtq?|T8-1X$@+TlnKD6=~`*xd|CWcaG{^EIo^`*B3p)d(XDv}=amy6uCDwgT z2Yn_!j;HQejoc#ZJ2D%FvwvwF{p89Uxnx zsirdPk7Ux*f0BEd;!M_?sM`J8y&gLZ9^Cq0`M-~l-`fBG`b=f@t+Cz1*Z;`BnK zz0pZF`qB0B42#_~edTeuW{Xkh(yfbn+cG%JNZRvp1`efoZ>C>F!YV1@>+d(jV(xHV z8~@28RuuzXVD-+#|9v_5xcBP*#^@QR6X|*Uq(6Aou^pfo^=`tua~;wUq8%bG=l%8C zyLzmbhsci0qf|l+M-fHTOdo&g9BQoFVx<&NMid|6Rh9X*Q0?2EFQhxurFOY!WaOo3 zS-5Ff(v{VfX9JOeRBtP8r&_C+@tok`P=cvC&+n}I5S?}secKZixKmg0)%Gs= zAk@wa=mhgKX{iO_`V`C0un4B_AKcS#ePHD={X1c4V8^NIlJ&BN1>Ib;>oTkvGy-9I}eBZ+1Fc@^>@c#c*tI>4QCcX0}8r zZyE)L-=WRA0p&JmI4rIR8>)Yu2%djPRL+B^Cd1xrOB_WckI;v&r~dU{X`elawo_~Q M%1j>ICv4pC53N`Qd;kCd literal 0 HcmV?d00001 diff --git a/fields/ROla/payg_cas03.fld2.gz b/fields/ROla/payg_cas03.fld2.gz new file mode 100644 index 0000000000000000000000000000000000000000..ee6c959164127544cd1c1b4c36efea5ea1a4a904 GIT binary patch literal 1670 zcmd6o{Wp{e6vy?0mqj_Xm9o`TvYb(=7_|xQwyh-Tte!`nVPt2*JTsbVVu+~DYSySo zM)G<@hIxz;Gi<`hOS6kOb{aGcyN#C`#u(GkFfYq-&YrV>!R~MOe1Ew2d+z;y&PAnJ zSkPa7A7!%cN_c!^7(SeOz;%BFnQ+KNA@$ELaW^puAFTP!_nJ}d;J*3P2YTzRp}Xvp zk}c;-C@m*MN`HgGsPHHuc8D0PO*X-r-X5%D)l!M% z%Saj-Qt{psJ7j34US`UXED5b3{zAHK#N)SRjoU^%2s?_L^idK+YD@xRG;K|r{ElRd z+ZD4@Und@OBk`+e$iR!#awW=TxSOq{cYj=SLF$eBH3yh#!IBVJhn4LTIqW$*^v*D> z`#QXW%)Cq;A<$wM<1vy_TAm+rJx}eZkAPIrxjsqwS?8a`#PN7j1kyBtY3Ng-uh>J0 zYhUB*W=@M@SiM>)45o>>RJI6~!XXT9Yw;mm^lTb-tsi0;wNfJ3eWM=-_&huB33`(1 zZL!<3HU)VKvMT$_vVv0t*0>#QPut-TT{~|SE&>cK;cw8|U9OGInLrS~GXmuAUY>Xj z=TA_@b-kh@yAq1Uo}33wX547NNOlCfD6SiY31m$kn8uVQAs-!o*EQa#x0DvBSn6(@*adv{1Jzu%v6nKpNClnZ0_t}|>w2Gsc< z#y3@FE3-H)n4IYhti$NkLM0>d31I!z{rYW*Ufw9>c}_M=>!GK7)5xI7;LuWyz&vQ)+mO-hzS?M+3ORKZjen3>N&gBf|FzIO z2nz+dH_nDpcg80eQr`tGX3LyP|7D9`Y|sdN7pG(vH{aaW9bYB)|KR;HET*8RKe6b# z$dzT%ctDM`N#ao7d`hR~i>Z$WVr5e{XJz)x($ioG zI9TD93AW2IKSVI6skc{-;w*J}J7a$f$On5182I5HbY2x081pb1)OZ~&2=qHS_XNh& z3(*(GhF;@C$0rM~KNwf<4)vJXtZYicg}!FXK#}pO<8#=zlA`iR@t!}A_d>moxw+6R zrcR@{%E0tGKx^077taiKP>W!>1yY6hbx(1L{J-q(`BbEj>YGI+$JHZ|32vRW#Wn$3 zTGO2K1B#OdJWsfFdR8tjC{GF}^qpYar9uaxol<|SoC(HP2t9fUGtt0NMBcjCY`9At)Lch)s5HlQDW2Kt-llMoP4N2Bz~Y6X*w_NpVVHCjsKqc#=r5&tBEs=L>sr z2K}OcO2J$cg(ME?BQnoSX;7R@1}lJH9T>0dMvtmlB1V0$eC9UwTOT&Tp(^lYSv3@>#5gscD8fQz75jw@JijGJi>}% zUP*pYZdMK@ekX@}HlE=AJS4B%NH~&x+|ef$&zozKKQT-MNyc&gO6>v#Cdbs~yaDNv9M|}6UJ?-7go)pUXkmKvMB&Y(n z8u}=z=WmgT;X!E6&Y4Rbn|q9g_H6=(Q8drooDHg6vOI(?m9 z@pxN9BJ-vsi&Wd0sY;;3xW{gil29?P#UEwv_;nW?nwcBlqU4c&*LC>kzMw%O*m$zA zk{!A*EgG?|(Obu(5DQdkOq^OkD2}LKZybT(z zmx~`ICQ=v!G?srASO*zCAwzPe{9U-{I-ZjOB&cEIWxqfK@k4V*mF2AhR*YmjvIe>W zgP6x&*exO>wGeY&aaUVq38Z#Tr)?o_rHdGnL2ex_lk@Cq7%vs| z!g!JIg5pLgt2n>4MHDye)W-6HzEsF1dly)WTfV7J({+kT7;HbS6gS2#F~CEAn7-wc OTOiN+_*9K`Y}sl_t&=3sJ+)*1sj~K z;_H7OS>P@3&`v`aVnFOal?DXmj@A>);bre0q6bZXA*8j}zi}1hY3X_+AD9H>WQ>86 z*TP-aPy)~v)e?JPLkMJ-J+(Ql4b*wb&Bf~zZuVhJ%Gxk{#AN$VDKA$!0SO&Sr)L)I z-Q;hTvCEEVJ@Il%UVi)X(?Oab?|cu& z$_dE6*~NZypBzAk%Naf?W1@xJnX-%ikqSAhc;@=?8rxk|yG!drDR%bup>PPnAKl zbjyD0A15SPX!5Md;e?)6sG$d%wFW9d@+*J-NlxZGXyB%uMW=($;wQC3Z^D~IraDqX zLmA7jgB)e>P@fyxilAwOy3qbDkJus5M^@tVJcbIs@&`$R^lPPtXdX#hg7j`5beUf# zBeCvN;=N_F5xV!;>y$A#-eB2W*y=CqZoFpC3j{1C>A+P#*T#dsvtF%0k50G5Cd>_`LW+wgX7y2dsm$S+;@1DB^CC&hR$a5@`C zQOYPAK!P5BahG?(u|VSS6)`9sv`D)J%d7NO6&jgJS0-i%${lV$bTE5Rvd#H<_Kbgr zPV95cI-}!-HZB;Rpfp@hKQ1FHUc?GJndLS7x~S<H3o+83U{1Ct?lT-~Nt_{?*-QtdYjn%n)Rx70_%UPlbzw#uvbS|~3* zuDc(f-;D{zPg25guWLG#PwCKLFb+t#yO7D5$z{)G5OxPmbn8=ktJ8L<$p0_pP^sK0 z#yTnCogJ~pDrMy^uHk{q*5!RL6n*{4~9$+PivCX?io}#Ip9p zG0~1A%oWf}&jFFb*?c6qW$v@2oc-sO*phL}nq!HkvfpntVuD{USBb)zisTH`BxWG# z(H0^F7Emy4awt?A%iHPa*Jd32Vy_-B=GYig&yZhZW2i@zc$Ym6w zSpIz!;dU>^v;laYBotW&L2idJG2Y#8PEC?Bz~rzo22bk3;)LEXr&Ek{@f@kbA+;k32C-c@W~sBvm2a*2FN=2GY$Nr! zCRA@yr=52%2K2kyuh-ta8`#_nVz)-AjD2O&)-3bT5K6_@dnqm|Z2KqQ|9vWa*rnMj zuna}SA#`)?x*jzoO)2{w6ZA@DGklH08>T+x*x#jtjoGy9LvNs5v!?QQ&GjXX+9yL^Y-<(b$0b|l#-Hk_HdLT zImpEY;_uW)_cc}t!_McgkR3RFEq>EE7F9L3nSw=DW7jbLF2|j)FIVaEgFuthTp0M= zWdG{#kEP0xXbD}!UwrL+8`p*R-HoH~&K+Gc==Qb@ubLd(!}J0C{~}A^W*rFp_)buS zC*s<;elfP8hn;R?TlP1lGyXUGM+Oozzjy+ER*YDCm#r_Zkuv`P=!gHx+yeov6_X`K z;6(Y?4Z!c3E?qG3aDK$$J;pMo((N|%-+UhUo>WE=yRu^ZU<=k{r>IIqZ=s}fbQd9tNhwO$NY4W%MsT5Bv9?Tx7{)* zgv(HAB(fL}pd;?D4G`98ji%SinzJ9Be=1zDH7NKr-~`lD z^m0YuqE`tzHzf!?Ha zicq~+Zx#utt}Rni(JhbEl4%On3Q&il+iVJST=PKO%6EC$=e11Ne2#YBkbckm2GZ^? z2ab}}Q$ed5YDiqJaY6rPdk%;Jbt(QsuBMT&w1#EK{a~L0It0!<8-!!&^S0DXyO`NUSB;4}6h#j;-ad+S ztE3DrqB>Fl#6L$?N*Q%!k&yEf5h_`#8$N+%e`(SdN_?(k{bJkQkm88PgnhoWfvKB} z(2~(d{`_yPug4%;f(N{EX#wp0Cuta}D2s>kHF|Cyob>6)96iwq8&wuc3Vwc{T9TmT zmO62=h6@Xa>bKLtjbyv_>Atk0p#NWyhHAiWej?uO78#Egq8C zr0oV&mX-hv+o2r>WM;c!1xW<_(E@{^R}Vazd4-!tMDKjVau+iE0JiPX2&H)>PIcrR zytgiqaXhA%qOQp0Sll;*?O&?HlJIXw;;Em2tZ3hpTLUqQ^$DwjY>P}hs z0KS5!O3pkn>VLp{)Il$yizN~>>n?6@~Qt5F)Qsen}yWp{ujn_?_HPK_# zQFYRB+Qr4~JXGX6it}zABLh;tm*|EIz9;q}s zyCLtUBSg%yQK|kn>Vt;lkJI)>Yhl(@N)_|HRpvd}k_q5YD*AAQ-RVV$gThC8$2oSU zmU0&17yCSQ!q11^4v?c`?(g#r)Ci#2Pw741bo8omz-82u3YmkcL@Guas_b|t54%7I zg;RF_Z}iNK5+`lf1GxhJ7P6L6Cnm}W&PR?#;PfrGPWMo;xdr@cd7p0t(@S-ZOmzsY zMYvL+V`P}^SFgd}-`ZTY7o!n+sY8yYWHhy>WntIf8J4w8T>Csj{iHB)!8Ykws^RJW z@i+gqpsL%1Vq=7iLLI8N+D_W(jkVspUz#8ke-db8?NW~+sOuU7SQrpWxIDp=FLf2G z2)y>YEo+mOUus4-aN#PL^@P@1W#E3^%_<4H&tt_mX8cqFA)Lj3%NlQ4le(-LgRsmO zi1j_>Ge3ReA&Htr776qsm6;{7^zC=fm#v9E_p3MZqDg-t0AMe5JG(6#!lsYo`%$8v zVh^02Mm!j*Qgc~+(%o3aJFL;sDd@r3-GeKoL$lDyLa8u*y4Yj8o{RGNunt;WOJ3xG zYfJCJLaBi=c6H-fNqN6I;s)S|xnjgldcPEq)s*gh*rQQ<11R0QaQ3z!g-wTiZF*V( zuWNd)j7`mr{8MjC7n^-p#_c$FVly=LzO3hQ7$rAyQE;Hg$~+UtE7pUqX!Ljq%JFhE zMOy)(pA?*Ps(bkyBPd~sD#<=9FK>Kfbw<+`r3`5tDt_14IHoeNVgRgb1u|(jjaznd ztz~1<=~D+F#N-*0w|>F>G~ufs^aDKZX`%OiMFwdP_xD&@$$PG}(^7n3>6;mGYB&^gv4uIHvtyb49eX8OxG^cU{&^FxutsE4hMYdMw zyN5f~l79)BvdchK_g&EiO#zQ?dR0vq%(6i^g2v!AVeo^bLbZGO;u9%&Y?(1XgC$jk-}5==N2a=|I}s68QclD}CQ{~jd^39P z0`}Qqq}QjymIT_yDS$eUT}6CL@k7Bypjghy!|XstNAJIRgy8S*D~)GUt}_?ASe@eQrFs`*(jLTWKLx zCsLQl^8SbfrU?B__c&u_-#)nL(v*rL_e;-NuaAO-(pSZ+;jZE#*V&o(bKC{`ow{)2*tb6{*v8x%8sw1K4KbXOUgOni=NMT(zj$34KKG6ZKr04tR9VPU<9Az2W zdO!%@Uao~PO>5-CTvqK5u-O6CtM+hW)5mTbs%OM8h~<-t&IA^hl>6=1QFe)UPCkb= zaFY*pkDv1bI{A-6;mUn7wfC));b7A7y%9eYZR=G;gYMuYAf#@n^UagDBk0e=A%}rW zwSv3%Ye+!^F9J%6doY#R=g@WOSD>p`$v`yA?cV?GVnNgi>mc7w8FOQYZVp!msfj%3 zI{MP@{_m?Q12qHmM4dJHWTnnuo=P^#O7W1Oz$;&dSV3D-^p_MnHBhBwX!0)B6t z$h?!P3ARMf3H@FJ%lJgUAi1FTY=b6b$F_)LvBO;sKmJVrT>#W`dlJF17)gS?G%7}# zVhS@2-ho|_W1rJVui?z;Q;}vn`N&vgXb3HAS3>r1`zB+6*gnFgxeP$9|IHM)tE z56EK*2yslnafGb_eo5VyM!+-+<#cVPuw^EotMx=M99L*K8aQTwM`mPP(^*X_;HJhcOe)1W7Q|_wCv5izH)ousW@&1 z?ayQLZ6{&v{4!;zpy^WvgIJzJ&jAM4Og{;I) zLg*pu`5lYFNT&BXsmivxOf%N#f(#7=a5D@aV&;e<$#0j6gIFC!fy}&7rZfiSocSQ~n ziTSlV<9|L^RP%E`Yas2khp5db$$L!0M4P4v5G8ITN3@_9zFi_gSzY5hR@oY zcgJ>L*Mp~Ri#2(lX7X_K^DnhD4xNgE$>PgX{pq+JLXW-VL|Ik_M3qw)JNJO&lNsF@ zaW%q!nTqZ$^;kPdz<*2_qGO6Mg0x0m?i0<{Fl)`Jkm}$1peFAlV7g3l1U;nJQF8D2 zfkZyCl@yfQLlr!G&=HMmR*0V~fIMNNpRE(&= z%IT!f5g>AsU=X_wZ`4_4_N3=;PS&nw33s zAHt|boC9P1g$A4(Oj0fR{^s4J9r`@A&2vI*@#JWrdENydvD#JjqvI0%B%KTSVfDvf z6dFs}O$JI#ZI-Dnn#wR>1~ja0z<(%_LFtjpXV9VIF5Vgz3x5g3TztQE;#TKfqJRlS z1!r8(VQA^+$-U%fUgS_hm(HD-x=yEM9Nyldp&9OZFzI*`Heg9)sPvG6vg4Y6EOul= zWSHNRrYDHS;Muz!rhd^p4TQNyhrpn_!UN-W2&{jwFmyI}d7vlFKKN_ro^NyrwKtiEfpt*=bg@bw9Is90}$ z;{7!}j(e;ftqYL3$2Mh*(sT~aZ}2jpJ8M+(1jkM>W=Ml`;bE}eE+`CH%z z-LcrdSUe|yer+zQEip2Gch98bSl~OaC}N0;1lzGt3$CAi7@ctw)bSbh8iL<}cgF@( z4Qup9Jg4GN!&3wLkIMs^>dnyGhzU;L?@xRssFP0QLp`suOb02S3!bz3d%o6{L%oa>veo#4 zVYcCE@Tpv=YRRm6bt{QHm_#jK^IaY6zmO+dGdZ>`sQFWzpo6obgSGV2*S*kwC-0EJDTIN zgFZ1Q}2!8WtCrojs66L zo&!T$k7PipNwn&V24~~R_(E!Xk6;N&T@IUjJ_Sm>teGuRR)iNnw}*wCf*z2? z2FHr-LkElX$aTlF3~+G2mmICSy_)ofGg`$`Rn9P;egSMhL*l}r!-^I&j0Z<{?Nz)+ z`!L_~`vv8O<3Ia)L9fc=wEA{mTgm;@#hR<{W@tOOI-m7F=k8Dc4JP=P*`!7!PzCz)Fo;&v2 zy~QdsaUquPMb)lFclg;hf;CaH(Caz`TB8PP9h)foGdp#l4=GoJ$RU_NrGV!kOYO_4 zlw@5&b;SGi2ojE7heDKyPzds=Rk^_n<)Xg%^t+8`Ya*zqSDHQ{?AY-8fr7Pr0A^#%0-V4&Npliwi&{!x&6)v za#v_i0*EJkonZo}*OS0wJ9a70C**8(+{~Cot^$I|Io{Z9H*3!C z?KeKZ^Ijjwt8&IikfAU6vOh5)C`@BoK6`B#=YL4heUX~7SRnXHp{YkQr7w#R*PCwl z@tbm+DzjI*>TbW_hcw+;;vac=1$3}z1o%4Or;ISv)<*fjhI%3+P&pDuqAz#qIN8^UqSgZLeX{=Q|GHVYWSE3|}qt4{kXqhAfRmEGTZHDl$ga4SS zX1>K0eokdvXE-dvVjedmW`Dv1x@SWSTr-YQ@+GrB>G*v|W>-;PFBoVzs}Pk~Qm2tY zyf4SP3y+mkufSYyj~fR6Zl^2mCdfM8kbWPjozUs)4Ls4`mvm6Sgs$jL4THcACc5O- z^^*?|b%#svDYtF=3`gf{qT{90ZN_7x&A_1~Wb&Rrn*{l_RI1YW%ueZ+y(szw!>!J$ zI(vKdWl4AE$>u1nB@XH4>>Rct`xY8bZv(4aj)E(DhS&L34E8HpnIcdr@i69Zj~Uit zyZwsZfU&}PZ=5EdYP@iCH_UFj*|PIiDjH$aH(FP|yp!>}@|CdS=5G|3Ep~Q4z$!Ue z414QN`YqMR!w>$##UzYuoYY(1EGKA|<)?Oi5t!hyF>!v|y6UZJXwE=IraQPQ{Q6&O zRCjk;Bf9SbgL0(Q^ji==_XV~}b?&P+?iHBY4i~pN=W={~mg;DD+z8g~k{8>&W6qLL zdH%SBAT#cracoz`Nr=9O&2;vj{rAj-&ZDxq9fnDGFTQBunCt^mU-sd{_1$Bw#tn$! zKj?q$my8eht%{24!J+fRV9b2)r{(`kh5eIUl5Oa3r-A=pS@wU3|CdQy*h=>duD?|2 M1^IZ%eXf!G4`FY*`Tzg` literal 0 HcmV?d00001 diff --git a/fields/ROla/prt_fild01.fld2.gz b/fields/ROla/prt_fild01.fld2.gz new file mode 100644 index 0000000000000000000000000000000000000000..53c8dfc6ae3f744ece53f971d44b04de112883fd GIT binary patch literal 7590 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 literal 0 HcmV?d00001 diff --git a/fields/ROla/prt_fild04.fld2.gz b/fields/ROla/prt_fild04.fld2.gz new file mode 100644 index 0000000000000000000000000000000000000000..b171e2684439fae70b1459e0b5760fd4aae668bf GIT binary patch literal 5155 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 literal 0 HcmV?d00001 diff --git a/fields/ROla/prt_fild06.fld2.gz b/fields/ROla/prt_fild06.fld2.gz new file mode 100644 index 0000000000000000000000000000000000000000..b89b038c6bf2f8b0437ff55ab1ba8bba688ff4e2 GIT binary patch literal 4587 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 tgCgfa(7eRKMys8>p3q! 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 literal 0 HcmV?d00001 diff --git a/fields/ROla/prt_fild08b.fld2.gz b/fields/ROla/prt_fild08b.fld2.gz new file mode 100644 index 0000000000000000000000000000000000000000..bd61e393457bdb7b5dfbf3eabdf22b59651e9330 GIT binary patch literal 5730 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 literal 0 HcmV?d00001 diff --git a/fields/ROla/prt_fild08c.fld2.gz b/fields/ROla/prt_fild08c.fld2.gz new file mode 100644 index 0000000000000000000000000000000000000000..d6498226c1b761678dbe93a8cc0197b5138a5b93 GIT binary patch literal 5730 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 literal 0 HcmV?d00001 diff --git a/fields/ROla/prt_maze02.fld2.gz b/fields/ROla/prt_maze02.fld2.gz new file mode 100644 index 0000000000000000000000000000000000000000..44d9ed94d93afaef02c812729e30221484f04523 GIT binary patch literal 1026 zcmV+d1pWITiwFpcyF_RJ4sddGUu|J}WiT=>W^80K0PWk!cB?Q92H^ywiNOX;RpvzA@2~5aKIGu4$pIJ?b5?QmyXi?E*!;7z8s}UIE5ZW z(kB<`xb@@6W?JBx&xS%tqUtzSFwLPN5jpaKIqxWQ9feE`hgj0V!Kl~}{ zT%WaHffk+}<%1eG4)C@gj=7HeYlK?|V1*+b;Rr`KMmmHLI}RBn9O@1c#g>ClG35|k zz_sLf3C_w{ay(^w?3i$va_|xja?nn9`EY#Kipv`0_&$GB6+7f;zW?m%*k@`2F6+64 z*q0pWI5s(wL37-ylcTi>oH$qvf&)stI_@>w0oSoTM`_N|qvjl$bqxYHed|`o?-Lx& z^;&rJGj7e4K}X>&dmooWG$DG9+A~LdDVq!J6Q>8g*KaD1Vs z%mN)cibZ#PwyZ}-ZV$#pwB^XZp15=58SWefMmXj;N(^U?BEy5@9TPHcpFV9n%Ip9B zDcZ23jBw?sGTb=oig2hp5XbFy9rq+vM~gAVQ4RE2B5{xzBOJ|8*KqhH$2!oA#&yGb zIdEVY{T%NEG)BRJVsvs4!qXkutMmE}o`LS5{|gw&!DkTntG*6w+Fa(Wqr~XDUy&5| z)Mgy*jR9jp2QoS1TQBe)c-I%|nNiR6x$VKgIm*`uyEy0yO$TyS_hB#{m&N~=j^oAt zrhYh5#X@)~LSRNa(%IDa{=$7M0KQeYq*xq;y@=}-`LabWw3 z(}x4&K=-wTfDU|XQ;Lg&*4vaKbr74|b`SQ2J2;pcsKH-Ki$ghty2ma`Ni^IMS)!Tt?e=r2s z3pzWH7p#}#y0QsK?z|{!g2y@d3{6Phftg|1Bof8|2NuFgpEx?57fs>~a$sgM>Hw}D z4t)D?ToK)_e>}i(onZ(>f4?Gk4@XA_qkTBSL3JR9TX&$xFY>QuSa7_a;xJOrc1#S! wtgK^V9&RO>Ju~i(c|nBbH~F=~VXMMntHNQc!eOf(<9JH_1Ji|XbVQs00H)yKy8r+H literal 0 HcmV?d00001 diff --git a/fields/ROla/prt_mk.fld2.gz b/fields/ROla/prt_mk.fld2.gz new file mode 100644 index 0000000000000000000000000000000000000000..d5b6559941d3014e40ee6250afa582147d8b4316 GIT binary patch literal 1490 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 literal 0 HcmV?d00001 diff --git a/fields/ROla/prt_mz03_i.fld2.gz b/fields/ROla/prt_mz03_i.fld2.gz new file mode 100644 index 0000000000000000000000000000000000000000..0a376993f42607dd6f7886f804d820394683502f GIT binary patch literal 2013 zcmV<32O{_%iwFpcyF_RJ4sddGUu}9YGhb;gW^80K0PUSyvg05OM9r`Fzo=Q(%p`sQ zQnw_)F-ch;w<>ni(!uz}wUa-~pXEm)-Ce+-csS+?l_R}FZ;%Bvy{30qV#cxl3t~2c zj6w%Q>V!;aF8yABPv@G&)myIE25&LCsLPBRCZID95gKaOhNI zQ*pPA0~U)xv|WaGxRV36ug%u=)N&4(PvgMSBhJh=hXL~51SJP-tK>UWi7T}-%3Q-Z z);;_jN#!^RZ{@&kv*jXhh-3RWY+U`X`lvj_A#x-|MmQ4FsvHcYs9Itm4w0mRLjyS~ z(7h-}R=dCe`>sqegyYyh*R<$nCWkdn3qL&p=cw{(xz;QUsW4p)5McL74gwPA&{l*v z&=5UV?i})BOdk52rXG&dJWIz6`S$p~=;%**G3MZSeS{k%Vnos|bpQ^a4soa;mQkLI zR%sP>a!9c=e^SpJE{-A<%<8G@;7~DO36&r_QryIG42dod2x4#)5|q*|Do+hL&f^Nw zlpq0)(j~%CX~uD6hI$PaM#15`k*h(tT3i-EL3LB5Ho@!S9B*N1>fm60B?oDP69&hX zZfX~4`yCtD#Uar6IhD!*gMOD@S?5Fo5H{tGa>1-X5MC-v|f0A4I)sav*b> z7+OLxmGpDK2A7EYn0-WJ&$%J_G4@a_;vU!Rx?ccIBa z4;h?;x&YQqD4Nlv7joD(wcY+{sW;JN9OlmZ{Ov&?@SN)oUTZ7KJs52qLR636;vXrHqy3=YY^8}+jwQz7xd`xJgg6QX<?x-9gL=tiJg+9Fpb?I0IW5!8VP@4k zF%R<9B|#4JrXt>bbu(?nx_!Na({S%WF9$DZ9LIa-N3Wh^H_TxgHE|FfOONpuQBJr`#IVyT0O7g zXtijq?;?(7i`M#b(KwEF3+!pwj^n5kiuC774tMsU9EvO9I3(EApS_Qx@=-Z( zbaRY2%7`L|aRh2RgylC=>ngktb#f$?Bhtqw27jZ<@N4BL-$zT(GO(LlZ-zxUv2kr4DeAqFirs9tGs? zki)9r3J$&VV2QS>K1eCjYdECX%c08lGKlUj4oBXKqip2Jc?CyH{|OG3-OC~60ggou zs~|_JTXu4|t`V>0crWPXunv*PvmtLK#`$&$^A#M{HJ$3^W5pcjH8IP2HHVLNlX4E0 z_3#BXbaHS_K2Y-#pctp!wRxkwU-~%AtP!o;;|-2s+r!8jxw+)`D0W=R;W_@5T89p_ z))PBQb7c*1EWgCj3^EZ%*A{8tl5@qwF;_esbH&3kSNtT$G1WLW1w0pq5+!;Dt~t!# zwTj109K%8G;usBb1&5s4$UhX1J%;PG(yQwNMtsUK|FCm(-U}4|_}lh_SxOEuWtM-~ zpF@paZL zE}xyBh{L%(jw4F}@*$4E9FoaV#3wjfC-pdo0&+D+1Vpz~I7-F-k9Ou9;_+8^6zJgC zbqVVnPa#A=Jgi#}s!cS|ta{uj{CGu+`vga2LpW%qygDVjhqXq6I_Y9qS>5R6@Q~iX zVRF_=539kE3FY%%k^}q(Im~=Xa4!h#qS8<59;E0)j zqeR0u4BQ;rsZPZh4dOaVufsWF69zp2J1^&07CEk59K*LqJgV(s-qiw(+d)bW^X?pC vOu1f zn>M91|NWl^TQ71VnGu1}=l9zLg?>vjn9J+rNa))(9Ab^C$o z)enb6$1;=J4@;5^-pkOrKo{PX_3%K!61JF@A94D=a3eghPH~TeSa3-vriw3B(us2xnuwc?T|Dli^1Y z0oq^ma=Crd2*HT@vXJ_*&^;)%J3Yz`B$wqPteb2oBjc@!L#ldfzag#Z?Vv!#7T~{1 zG3NOWSXTT@JJ-zfr&M(x>Ttg6Fu_rpbUxQ6Mo{Wfkmn+%MEGI^6(5P()pMSD8-cvD z0U<;>ST`8?e#Fj6|&%@l`M*5*PQ4 zdwL`?tj%DJzDQe4Ii7};ug9zJwn+_1)+$Rw9`c*yi*8L)GafPmB>uM9^#TUJZKnKX zSVD1WJjrK(*apOx1`mx#Jv8}_0?IC3zpB`S`#rXQ{}IztaOXp~Pi3@0<&zw|CkZ+3 z;{L#%u)0^Mirx#*GNA>@n%RD`7Hm;PXQG9Tik%0*4`_%39SqBr{k{N+^7;x#?H)39 zPM<%c$~_MPt# z!!C3NXBMzPRtOcy>0rl%ZB{Ds>}5s8jp-t9z*+82OVB8Y)lT_)X!tvJLbd5Qn7WB9KedUm}Q?QI?ecwf@fy&q*LcO1(>W&(tGngb-d9(`bDREr3Jbd2h3qy zk+*xdFtGlOD%;AeChZT4K7Z<$2uq1NWH;=%md6l{k_T%A9S^B6gQqH7bncMKXpjzc z_h21yLbbKXxt5{Y_339LPc9e%epB!g_6_`tHK{QKxL;NqlX;e8aiuUcpE_2qBQLQ+ zOF11VwN===2`CO7pBi=@YR?L>Dh-+G?b{C3=ir6qaSD1A_t(hf8_@36Cu~6y>if*% z-0(NJRYne1SMWVT4BF!2R}-EkDU4DZSkshGhS^M(qnNUP8(gUWBoS$CTHe2|K?Ty% z{cVJCfKviPfg*UPg{!XFI;xS76dvaB3NqRcdwyD34O@D9sLQuxzL1D?(^G!L+k~}= z6)TIJc464K@2f#|EX87&v(zin`JNie7BMM!bKW^P@w!g?)$O6jQv0W>84Hmtguajc zLMluaBCH#pe(|Q0XhGGoWO$6KaO$xz>jQ9R)Dev4!q5PcWvYO)yCK8U!)6}%8M z*cf{xtJ4&Uxd1)GpB!T=WOQg_57ITI0Wvnl zyA>o!-Myd2Qh-<4g)d2NI0IC!PV@7pQcj+o7}bn-P1{;7x!V8BRIZE}-bZL|d~2rh zTK#IxmY;>(JXp}(60-yTM>JT)D=F>ccKX}Be3bWdzpDY+sKRru#h_#5Zy#O99ArrU zdkdT`Y?-YYzIWE=)lyZcSfNywRIZiV0q=cqw1>$S49S$eT{(up_4nRgaHBMPuV}Q( zUZJ%h`%yd4T%qx3-OY8WMJE&GF-TJ^ZA|N9wtGAio8Dzhiz!%&zUqQUe82X9T7xm! Sw54{2pd{l~?00`!XYgM(GGOWe literal 0 HcmV?d00001 diff --git a/fields/ROla/prt_sewb2.fld2.gz b/fields/ROla/prt_sewb2.fld2.gz new file mode 100644 index 0000000000000000000000000000000000000000..0f71b0847fc2f6133e05dee0238d742439eaf96d GIT binary patch literal 548 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 be>=Rs&bPo}WIxM#gIgh0XI>BaZp%Lb(qt>R literal 0 HcmV?d00001 diff --git a/fields/ROla/pvp_y_1-2.fld2.gz b/fields/ROla/pvp_y_1-2.fld2.gz new file mode 100644 index 0000000000000000000000000000000000000000..2be898d50053d7f021e8de59167eaeaaa35f5145 GIT binary patch literal 2611 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 Fe*xje-VFc% literal 0 HcmV?d00001 diff --git a/fields/ROla/pvp_y_3-2.fld2.gz b/fields/ROla/pvp_y_3-2.fld2.gz new file mode 100644 index 0000000000000000000000000000000000000000..622277e0b2582eb4b701e06fb32f24fbc95827b4 GIT binary patch literal 2611 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 G5dQ`1=-v(h literal 0 HcmV?d00001 diff --git a/fields/ROla/pvp_y_4-2.fld2.gz b/fields/ROla/pvp_y_4-2.fld2.gz new file mode 100644 index 0000000000000000000000000000000000000000..13771c6d5b43bf227fdccea770abe12b92bcf161 GIT binary patch literal 2611 zcmb7`=|7Z<0)U4a+v{|axi3v+Em89t*>{Bz+H9T1jAh0$#=eKhmN}M}qGZXbhAg3* z8D_~rmSd1>Z|ow=iIg*B?Fje$?tgH&xc1I788RTEUAbiA-6-^W8AeNO?66u zmnLFmYBI0LP!eHRJ7TrKV z;EnaNuhfheNNB-1pk~O5k~0A#gvRbSZenov8H_d1=_2r%6%BI)@-w6ANDrR&b4lk8 zbm6&SENjOG{ImLJHa*-7%BJ+!l4>tQvXT`}uF#te`JhmR&P&nrqC|dW*tpqPiOps# zG~rUeFe6_zFv+5&=H~P4o0B*&lUy0qTSExAtkllHN|Rd3MD-M=_s6#>s5 zl;5?;+gpMT4;I2BZGIo?D#CB+nydtq4Z{M9cjl(`RElys+SHlVp;q=AD6mE3W0#4A zgu?{CWd8}b0=KrN-G>os5S511lxin4l&DTGRiy9i7yi%U=K0kLz zWos^mc4+6jV^nTrL4r4O63hBne%pUANdj-jtfOru3Dt(%G-N>-HPGV~i@rE<4O+;5 z;pLi2v+~SB+PAOO8oRyJ%;pdK7LU6`==hcK1Px(YHvpE~kkH|GH4jxW?GzP|@21IC zP|4EfJqCM0{xLd42NIKy38)vev9}o{fugOgE@OHzDD6(Q`YapApDAH3wRbZLBm)Wk}_>w>_tOlcix zHNCC~NqWIWoNuD>V#-8Wq-F2Z-)8d(1M-Ah>I;n@x0-(QU?U?Bs#u8-&D>X^5y<8TQCLaqck<9>V`}%0k6!*DI zZz%#RUs!7Thf?*ssW#2c&tS5F!-f$44GB*~i7&Fn7ij}+>a&_rX0X%Wr(vc~w9BZq z_u76@I_~KOR{9+XME*`vh}m@+A61fef#q<5&uA|L(xJ()oO5wp&-d zZr%$1rAnC6-vpM?GiG8q=7`8=T(Nc0U!Qnm27Zk3hGOW8%c|Ko1MZ;P37gr1dN4G){mn@yQ2+hNyw)cjH~jI#^`~2au)tV zl+mcyNI7h2*2E9XMM56vmCP&8_}vhb_`}jO#QR7e9HCb+l?PRo9d$W{^cU{1ki&;? z-iW;7c&pXcd`U1v%7y{-UJrQNT`YfVT8m?&lb-#9Dt-uVtP4O5luTu_j~=GB{#C8a zzRh`A5sntC)etX6t{0jG2)S&(wd(ryJojG&>%YWVD754STn$b~o6`F0R{Z&29`__; z9obf-Ol?$v_pzQMpD?mw#mJ}|&H6I!&>cMEi;v|K1`K_+(^KE1orTnZwmL_4!@!Ja9iO zJuGYCTJf$h1rY-^f&3%=SB8oFM+&_K*A|hNLmRUTiyM3$f)@?73?aNk042^X+hJ;Y zE*ug$msn}+4zx6eJPu(-4}(MJ4HA*8bQxYY()GAqa=>}Nc~N}a{roCH#l|m3P!o$r zQh+%8tpv&ce1iXzMNp1U$V&b4n2JIUaX?^{m&woj9SGY>I#jEmNyiV*RX`So>Hm%CZ5kDtR0ffOVBr$ZU7yUNcc zi%#WD`^4%)8E$8kn;+|C?{Y+ok?~e_FmEh8JmC}q?6D^2M9PD*9lD^xuT@Qa zVcuA$aLa&>43Sjrr3}{v$QmmT0bRLk0)$;L(+^DUo*99PVJs%vq9XKvf-ksVIarw|8F zJ?DzcGcRorx>V7?{p5X0STL*{*rVm7utUG6Bdv3dpzI!O-(dmubjK$mQ^}-3%4QR| zRPYCG{tRT|cC=rLe((Os)!c>n_)ER0r?lW6hJu;ngD(#z2?bBxQ)ulT`>sX2PB^$o zrhq5(V=V&k%lhpGZ8|0^1vhv-AoL^HxycIgC%aU>EdRbJGg|Y#{JYV1t*o>1VomSJ zC^2R}=E6+!fnLnR-P3Y|{)#UUi^v(ACCVDbJRfK!Q_H5y_Hf*2K!80|Bg6Es`uBjP^-R5Y#p83bu_Gpd*zDALyK=RsYz&(TL ztnWD0(Kb121!}3f)N;6Q&f@U7zB9v0Bi8sH`VQVQ?tecgv7^YxW*s1;jy6Q+$_gR= E3-$}%4*&oF literal 0 HcmV?d00001 diff --git a/fields/ROla/pvp_y_5-2.fld2.gz b/fields/ROla/pvp_y_5-2.fld2.gz new file mode 100644 index 0000000000000000000000000000000000000000..7722e60f10d0becaa8c143213619a6cd29680ea1 GIT binary patch literal 2611 zcmb7`=|7Z<0)U5_EU)7vb6=XuTB7DP!q}I>2rbr5W5zOL8Dp0vlx&$}c_~VkoHAqy z-OMmc4ze7BTttmsWH~A23|Tw2dw%ynxL=(O!;Mxngml==Fwa;|9HgyD z4f4@OEKN@26&Xn(9O{NmHuhUKdkYW7y&uX8vMfuzdb*9vW=V*QC)6g?s?KIIZB_zT zWLJ(+a#S!*DfkSgjSQ&Ku7W3&iNmP>A1*=f?o1!G3=VeuAhxOS^(y$XFeGHqL}|uk z)DC!Mv*<4~?E?~9@lL2|in7#n&@i#7=aq*z+;a+J19Z6w{bt0%oq_z!=z8+~CvSP= zGy8h*>=2f{WefgU^D~DLVGdVab3@JDU@Oq9$d0DJEgByl+)R+$*Q?w?YM>lTMvEg zHno&=8W)uAJ?2&7S5|O7E7gqw+{D*^2K%2g!J>i1xS^u<&QfzU{OB$JGv=k})E|NFJNw`dJ5lS>uV6W$(f%~Jx4)h`laMux{PRb;%+ z&wZ!1F&j%iu=T?^Iyb5y!52A!Wq+)=8910ENpN7*(>Ibt>LP3#vmlHH==FllSQx(o z&F4S!aZjaNduJgXJ67sUJf3T33x@nl#@r%t0p)yQW4Mkz!1j12a_~*<19eOXRSgt) zXmga*vUK^6z&=oLgaI*t#N;DF+Bse9O(t2WY-gv(9HnG2H}hJ0W`oo&l^e+UOMc~% z?G{U!@gUON!|haIFQ9j~U$72#kgJXaMVa&@UlwC+xm0jl_a4a<6<3!E9Q@nLoWBiy z!)gPV0Qh@7U~|4Qj59eVx;x@WFnf^h9n$9D0=n9I0gK*;V=n(iaDv6AqrFt|KZ z76&b-*B2ql&v=Nl&2)Zjxfq+g=zH?}Og?cyk$7EmzUkve^KV`pWYm5&J5f>vvy5}{ zO!I+^)OY&Z41pLTxH4^+9+l7yR!=mWspPXZ--vVPvX=fC<42`mNSCYWh!1c3w3X({Y?*noVz0kzy)g$hWF!>A# z=i^nHT@J2g9)t6X`CU>DfKqJ(Sam%P9RT?d=fDT(bx>?~KB?a#W<2Sqyq`5_fwUV} zq>SPBh-UVIOOZsas6}Y})@o9Rcm`ShGtPo@jI}j!9DS@nj^EgMBn7{BXJAa_Y(6X7 zqq{*bZ;9|+EnMYqBHQ>WD=`8iedr6m#HMI45`IA%6^&PH>vr;04*-9TuF+4R1^(Vy*r^UX`PGO~{@^fQ@Mk6K~#?UuR>AF%R@f=2v9=ZiGqPvGNY{mF|Zl^(!aypz89&ZYPj|qMeos zgfQ-_Loc|#8g;c_6U>qF;Q*u03*Ph;&)=BR;o9QTvv+6`2jHgqAk;wVWH#sUL0a2i zHL9GO+~<`MXz@BNi4x>$p?Q#q+vaQQ?qARH{zbI;OM;C;OP#~l;&HmvwqG~m&;IhL zHyP{9u_kBgqJn&n^h$ri$cvXCqwQM^#9dJW|OtM1Q6Jd7PAFnqtLD=@9I-7hO* z4}v^`ss*ssk{w%s=U9gUw>-$at0o$GeZc38unp6)J*D_$WKtV}J$;eOGcfb}U058v z7oHxTHGidKTa=23g_=Qu^xI2AB*8zn%ES|BZ3=r7#)=sN-<>r~M6%Q6_}NJJqYlYIX9MQM2yyrFtA&+ozeuCT z7mQ^93HW;{Qt;)N;3vzF9KW!ohQ(1er5w_L&^R#-${57|r?`sX6v%Nk!yeblT`et; ze-9=0Jq-{E&?5bt*IMH*Tbpo&<^xz`kL6AnA3$-tQhm|G@AE73)WcS!Gj(kXl-CpGB+bHkZd=YjC{GMf5y}ZfSk1EAkUrde>>u^o!A3 zTKC!^=-oIrq5W>5t3gL1(&eaWv|?bJv+gdH?|Is8q=CH@>ueuMvQe+Tx0>!in%74K zoq6A71zQGOD4&zSy-~TX;VBzM)~Pfp^lxFP;XCsA~Kai9;B`} zgP#ljz|Wn6Og#?2m0{eyH+(sFK0f|JAL& zAh{YiPjTY#h-mBAiLazVxkjk(Pp_j2mu!{TN&q;163$R)H2${o;F}d<0 Gi2nirJ>C!i literal 0 HcmV?d00001 diff --git a/fields/ROla/pvp_y_6-2.fld2.gz b/fields/ROla/pvp_y_6-2.fld2.gz new file mode 100644 index 0000000000000000000000000000000000000000..68b0774908f0be93cba8367b05c28aba12ffdacb GIT binary patch literal 2611 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 G5dQ@VVBQe` literal 0 HcmV?d00001 diff --git a/fields/ROla/pvp_y_7-2.fld2.gz b/fields/ROla/pvp_y_7-2.fld2.gz new file mode 100644 index 0000000000000000000000000000000000000000..b01b9b4315f3fd8e091d9dc96e18e928978bc648 GIT binary patch literal 2611 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 literal 0 HcmV?d00001 diff --git a/fields/ROla/ra_fild05.fld2.gz b/fields/ROla/ra_fild05.fld2.gz new file mode 100644 index 0000000000000000000000000000000000000000..0c7af8a60bfef4c59c70fd3891a443348a8441a3 GIT binary patch literal 4870 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 CGQIr( literal 0 HcmV?d00001 diff --git a/fields/ROla/ra_fild12.fld2.gz b/fields/ROla/ra_fild12.fld2.gz new file mode 100644 index 0000000000000000000000000000000000000000..d0abc5603241efe41cc98b0490e454cc7aa142fb GIT binary patch literal 3866 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 literal 0 HcmV?d00001 diff --git a/fields/ROla/ra_fild12a.fld2.gz b/fields/ROla/ra_fild12a.fld2.gz new file mode 100644 index 0000000000000000000000000000000000000000..48f6627d4c72df3e995be0b0a0151c05bfe52c92 GIT binary patch literal 3867 zcmbuCXHe6PlgDXN1PsWd6osFNB1)0arGyTOiU>Y{(u1k(PjzABZ&R1_cYD zH}N4rl+Zy$kWL7_CGGrM5GTQ`Egtl=$tU)q&Hup_u>~=W2VJVe~8@DHLw9T zt%nEn`u~{=Q!NDTbSJQIG1yBrx_o|i%LK5I^*w7 z#Ef+xhr!wn%S{AFCdZAlTTa->VOqfjRJOQe1;0q(@A%7>H|jv8zR(y2i+!Pt%P1Zt zhcp_LkBrm64#zF)S0)R4up(Iou}dTRD^D(JOc?q$08{&BGCa!^r3x%a1Rc?JZX>-` z&fSr8slM6sB}380qZb&Pr{Tb>+tEHzT??#1yZMqRM`$5LSl^lluol{H!V8A@Yh$*= z)zw)DN#ukAh&MaG^AhWq3Gt^g^4hkt85+5hq(9Q00PjxW{E4?t!<5QYtO{GnIAJ?< zJVJ>zCLJ>5htzEf{hn;Ypl2-D^WLagMGCONy)5V9K)O>mObb=M%kQDHUA`n^!C0_+ z0rhd<7^-gGUqFtH1ysj+b+=%fL*D6&wqwta+n6`EfYy(fzsf}~>MN^`-0 zL1OHHK9NhU2HfKW*hCtMJOCSyO56a3jN>|$_ZAD`eIQQ8@seH4S~!I4&`mko0lH?c z+Q8(0|Iww3vCF&YQdp|^ zXpbpSVXanp7M66SJpu-+WD^KJSHHSg2%AD|@axT7O7%dE5wYx{z$CSmWLvJcBJnHs zxf+e(q`f2w-^4B4RF^^nE_#sq0uV8&dNWCDK7a8*Xi*cJM)c|(#nL|wzd1>288;^M zV+`nHT>FXIwfOv=D|%Oel<8dsZoqnLE4&8kG9mQiF;5Ok(*&zg_zrMo@M@t}tcOUV zbFz7dQn^|tp{w((Y~;?14SnxJr`k4PUAvtCpdd{q=F*@Fi=0H`D+>G@=!3( z+cVe^jEV6=?G64FcM^p{Whw-ru45_n^UrfP*Mr#mIL^n~0NU*fj`X?(HEvJq$K3G~ zP40NkHbbMGL5co6CIpdXH>Pq@USv(Cv^`E4SP@`dqMk8kojkc3>Zh!@9jNjaz zmV!AsV1&?z%1P%fvB8~nqEoK-q_K>=TR|^VZCjpZH-DKnp+Eye0dJp1& ztp;Bu^~=A~O`z)6X!jm|%>=v35-5@C=~>gzMg!B{`>Nabh4RD~)a_X{zZaq>0?$*A z9QW__O!hvM69A0G+0U5X4LI_}ZZ@KTXs-Smb?qEJy4XG84v2mMH ze@WH8srSgz7^im0U-ScmxlF}c%tJx3bF^^cJ9^AFFiRpOVjH$mG?pjLG^V*KkN#o( zbZ(9H+-Oc@q3hyfo@c-f@l55O_#@@tO(c`DPigkZUws@$DE0IjhW5HwX3@2ctQ1h-lZ)en#>6 zwX~my6K7b)nsZ$YCzo}1v@h=0HLUcWflc@&Vngo?JYS28nh9N7q&&g1pt-B(Mcx_Q zYz%b=zHcr+&x=+(B(lQ(+F6_=<@=Fxo}3+jmWZ;G<#_Jf7%Rg`pSCSk=MNIu=O{lAUB6o@o`mr4NViuK&q#UI`z z*s}|YFIuccd(TLY?E4eIm~NxhOEU+!Nk4^Q52TJ3XqZ|&%sYJjxN$JI+o*(QBKJX1&rPXhv;OVKl}~4F z;mBM6@G0OHPyRP~ChQJr8|8{o==py=Ik@9#Ae(#tr?aGLA@psO=)drNjScJ2=UX={ zMm|S|PW<|O)n~9-iwYlnts?otR>!>czgUV|9c&-?Z^IkqcL`iep$_(E(>1Ls zBmOhXyRQfAoDS3<4H{0zkQ$r;n>AU&py zQhppjJYBi6pp_!o`e*Vixwi!Vh-b^(TNwMNle6+8klnB_N~RLjEr57FkmExd%+>Ch z~I@KmNeL;?^Mb8(4vZ_#Yc1hV#7_B3<3 zHqG`ug1^0anA*;%DKJN##bp-J$ZVH~e%l|4gh+IbEI|dNrszpd+poF)@<(lHX3i!p zH`w@2k}1rJ-j=pqvvlbx7oZvej#WK-Sbp{lJ~EudPzwh7Z%53+LVO+im(vIyb>UCm zI?GAh+)_y~az(wyzCM;!T@NtDt}+K9T0_1mnOhy0G#^)(6SULXc&?{d06=^aGc0|) zKV|-bxZ+n^Q*20P+>^m4pUb@3RC?{TtlBygU-cuEQ%^nhT4HzLa0-b6d$N~Z*hsfx ze}?_>1FOow*BzyJ7;1x7Y6Lwd)xD% z)AKOE5mHoun^D8zZpj~cTlN*g-MJ?$LwE9Na&i3oGdZu79Y^4Q1A zpbuv!FO@aLXVf+N4u#bNJklHy7=(U>q+x%${66q?env3Twf+478wdHJ(|`ViPGj=* zZ_Xo`9oQkQJE$)wS~{^{(k;|qYgaXrAYCPAvfPC&=w;N6KD5O6>^9E~QJ6%%smKXv zw;21kb{kvslUF37XpV-`>+1q6We5y5{c{g?sHmcp`1Xc3gI;IP^L+O!oOy#1ByI`| z)C#kjJu_mV)Kah{{3Bxw)mO@Cwjp=UEXLK0!pPtb0@@v9ndf%k_r(|ItA3rS-YI{{ zrvpfsCf#%VHD-W?M9F3pLFUBR$efGH7@OJK%l)|J3D`%$RiO3d6y ze3{I4R$iZe?q<)GfWNKfj7hwTEQlCdfks@GF|sJ(4r_+pJ3SqDS`B? zQ^A9T$tz0zgB-_ikEZ{=Zvw=qpJUYF>-$equ2lz4G0f64sE;OYv{39(sKT?J3YQ;1 zeq@f55Sc1jQf<6W+Q4D-g=w<>r5V~e6d>VKYM^=#@v&6E+0G7Kh|kJmMQDk^3|#mU zCs_{%1GoWKv5+Db?=22i7FOcSnQ6_*|YVZK}_Q8#C zla$pnZJ24zlkxXO_Y7wjBra6Gh7cMaNvjW1VM(a6++;r3pw0}g;tt)TtJB!NqYb8x zD*KWpFpPCfEz)`1q|eCjC`+l?O>Veuw}+V$U<5ZBoC^!yvYSjJ%=t8Q2JPTsIujv2 z(Ft)Mhq|uUOy+rOqO0CWMRkm#SMSz~K!94mq5X}|LbJJL7hoeO-I8XLsyaHIvmRW3 zW6<)Gl(8k+2!i3!PsJKjKtzJfDl0-z;{(|E_-e*)4t!Go?*Tti>F&rM5!oZgVWf^4(<%xy9C>iP6N9>=8jzJL$@udr`I0`sRGqv2WT~&1m6JvmtAl^ zyLr`XYRau_&zSyp2?_ajuwX=Q`UtJDk{(by{r~(y`SATbk2U~c{OJT6f`#cn0NOpK ANdN!< literal 0 HcmV?d00001 diff --git a/fields/ROla/ra_fild12b.fld2.gz b/fields/ROla/ra_fild12b.fld2.gz new file mode 100644 index 0000000000000000000000000000000000000000..44d33f48b3ce4329bfa07de4a606200cc9067158 GIT binary patch literal 3867 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 literal 0 HcmV?d00001 diff --git a/fields/ROla/ra_san01.fld2.gz b/fields/ROla/ra_san01.fld2.gz new file mode 100644 index 0000000000000000000000000000000000000000..43a71c06069707f200e3a970f0b1dc92154e2c1b GIT binary patch literal 3095 zcmYk;c{tPy7YFd_Qly(?9YQK)$j_2(Bo(HzRfZCNC_6)$nXF^EbVnE(``DL65oJVX z#*#3D#>}Y5Zn6v-TPU)>-S>U}xX<(b=bYy`=Q)3!=Od375Fpf>cDSn~SJLh-L6J%|C zfrtOg**5N$3}@A!rhi>W^0bbmz77$_I^8tSg&@iOU}ir!(+>&=nS>2}o@&Y3cpJsU z)mBl|NypNeIJ~%lfvdu4!W~+>Ojn&_gDd1@j*u)??jwoP{jaE##4?z;xPXMY5EB|g zH?dt4l&H@c1<7|>Y57-uD{$_zz@`DSZOVwAfA#-Y`2rSaaWp#jBe^RSN_<`gAEuXd zht3;lB4q}@IF)mX{BG)MY)w<7sKJve!+k2dIUrBswGZGa85o%qmh8*jK${Z9Zqn+1 zf;9`Fn6HDk%2!>#kt3j$h1Ys3{C-$nEk%4Tnwvn7=wgS6%?-LTPzTp|zP$GC_L94& zd!f{avW6+0xo)QzgZ1_2PFHGZWt?){4GZE5Pc@t>jP2MY0Mt>A*pi(Sr}V2?1IwKKXPB@6_}+7`RWMcwTX2Go)^t`ER^VO zJLAI_#+DA=9=u3>|3mqoN<;W&dLUp%{iW@hi}w;!{UmC5)yq4oDVt!XbgAXey`s@^ za%H=bM^}jOv+p}|_kq4rz4Ws{%8i=losVo>{{&Q}-}yU6jcB8s1x_s?%e%#0s~gsA ztw(hy4WbkS{3!F)^jd|-W~U`b=fgp%KW~@{NHAyOuWAhHwG&k+h9}Ib@ zLga*`*_l24V*G0CQ)&^<*X|~}Y_-xOMo7!jPuxD#RPx1+}>R z+M*X@n#^a`38jLA*~~Ptvc7=Vo@vg7*T-=^`HYlX*Hc(sB{CeBJWp)aT38GkGBsNc zYOyWSztW+9bQ-?QJ(ZUwq?ej0Dt&1b3%OU+&99o@*9|3*`e1i3klXDZ4X!hzye)o*0qAN7iB>IGh22V%{q@^9dEU_`D+oxeW ztU!sl33{;-aCfcl@$aWi?dy0)+0qrN{)vgAUbA&$E(u zYbu&?m@CQ2LlF@?uzPr?I(i!g^eqOHOPPPP^M@_Em;&1mVEOMckaQDns-wd)`aM894baH?JPV#s(-ht%)B5s!R#(Wz-Sf?M5+5jJC02wCg$e}6Nm!XBZk88cD=9Tf(=j> zJQ~nO0Y?ozKc(6;O?2nxA~8}FFzY1f@!V1t3La7%lHLneTkB*kPPn`q4^Ez;>i-aABMlFQ{*CfiAb(DT|!U0Dfr|FFvRcN z zOSXF2^9E)@;?5hZEOT_t8h=Uq8rt1zozCA>rbd~6@FJB>Vcw@r8fx44bJOBq3_BU(<4AaU{h-Qu1emT{D+mAVq|wcJQ+`R}1PG;a&hj{=%EyH_h&uSEr8II+q>( zV7rePA@?=^F_lvvhv!a^cSCd&T#4EZWmb zMAQr9kb9!R+BdMj7FUrN@O&_XtoKDK&U~NvJwI=mn%$}-?4JW5@=#fJJ1)=+qkvup zpKzR*#Eg>sN)12_QVMZVT-%feJ#>D8fN*_9=+N(d@qw z3!k@)xp12_M*XImpbh#0;Mo{=2!I{oxJp zSqe?_)BV5s+uVB1T~Lz~R7LTZ(2*Q$nQEaUEaXTr_}^OoJ-dRDuZk(w1`S-ZNXxMS fxxW|um+ZTj{wMdFx@XSs(09v?4dYET_wM-*9I7s> literal 0 HcmV?d00001 diff --git a/fields/ROla/ra_san01a.fld2.gz b/fields/ROla/ra_san01a.fld2.gz new file mode 100644 index 0000000000000000000000000000000000000000..27605706b9c9e2ee70b851c9e4b851e183d9db1e GIT binary patch literal 3096 zcmYk;c{J1w7YFd_p-3fJhmcAc^0Q> zwu-DuJf6k-9>lgcnutVOmLd z$h@99Qflz4Lpi(1_m;NW)-+jy5;Una+^4jg19HXQcmp2d0g*{z$v&J7v@t>C7PbBt zShEm<`8IgFeAVSUDFRwqc)hp6_ov0RQpA^{xd{Z3CUS_-+@LK5wR4H*$!qU!FS&Q7 z7fN|3t)J4F>w20#SYLnsOr@Ge#%cTAut1LBRKw}Q*p5wnUV&+cjz#!xX8EGf57%F8 zf4<^q&N1_eGL;Uh&W-gHqAG`ES$k;3dMkzzaNd4y)Ig~gyDQ?uou z2Fon{8x8tbr~a$l(|K6}I;ok`65=Nf@4$@Js~-5>`!Rp}S8g=IN(bOdV&-QS&vI5j z>M@`Sa5swNmA*{m`L+@ZvCL_av}Y|S3y&0+x8=E(x8h^z@6&M>ns*V=F<=ylmL%m5 zeBA0%fL?L=>F;~MJ4ojQDK|lZYO8wmPtR~FxY0dkJ12ND?4A(^sT5?5Z9yxwB3y+Yn;sJ8f~eD zb?817&4ZA+! zOaWl<{!E^jA5pT!#1nE0%GBg!B9HpyV0yp5XAS zF%3?@WctBxM?4kLMEdu{lqri&%8=-1ju-ywa`vW(nO$I(=J`*=W_iP{mDCRM83GKS zIY1a+No5?LG=Ln=Qki@f#Q27WJ;aPju9HZX&PuQ21OjF%(kIbT=0Gd&`O^fowJDC|HqYAGDJE>^~dP^!F863r=$z>ais3o zB2gN^qrat-^8`-Q?|#y#CM5*x8J^7(SlR!0d<~)o1j#&RPPL>b)3Bt$r0u7u6+J{e z(NE~{9spNapC7s}nXcvlm5lkM493jv?H?WZ>OQp`bXldWEzcC}W1RdVeL&a2XK8Wd zK$n9nkeMxie$mZaRhl~7^-xKe5FmqkahWZ5tej@2!0#FAd(ph;-O}fZ`rJk;`Nz-@ zCO+NNJ`IDSB$InWi{1j0@M&k&DF+UsKPb_b^dqno5iomlvUtpf`}-uR=j$ogW*)1Q zPO8V3k8o!fO|PG>owCdFC9l2SfkjQCrJ|(`s8Z#WLqm3q(J<3-DC`YOuyh{uR zm{%GXTsHJBH4)eE9vKh<-hCrIk9?ZaA@6;$fX=gjW4LmK0rzf=Tr;L%vT#art?>0+ zt~-d>aitC$~*Jtqn_ zm<<+galwsr(XsJu2h%E=B+#-}Sc*}L5G>C#-7mTwih*sfXDvKuVCx_EAW27E>i_)8 zVHU63G9t-6ER9|Ae~U-}(-u)GW6nD=YsTxx^<}Fa2V?{d&mw_v>7RaM zy|%!8b@P$MkrX>|@m~!#%^X&N1Ob-X!OK!#EvTc1djbgAi*I}1HoIkBn>udoSa#&2 z^*%y`%(wg}6n1@#&x_yI&un8lr7#*i_{*XiknzmsrfEvt=D;5Dg}b94g_G_>`QvcN#3J$u4`YkOpNJ>edYX6R@6HB? z=YC`Mqbw|HVq&?zS{{*Q=URXdds-7edb3pdOu8kc&H&pETbS#~9-ISnk%y?%;;7(m zd_xzOTjGj&qb3UpycyJ$&npyIB09a``7u&Z7=Qjo+W(9K;C*c zqE^v9bS-D4jItgNjMS{zwOE{<$z7za7J;!3o1QO9Y>+1L8PUNaXr#COwUQXqW@4Tx zbr3CrA+kaZhgxbmFUQCAGXenkj?&i=e8oVmmyaj{Ix$|;c!&_f2?5-Z`>jhlh2sg4pnF{Ocn}FL#wsxUNxCchTT~r{;}>qn*BFo z;q&G(7w-_qDBqP6G(jH#JQw2z0YXPO&d_r?0>ZNr11qr!4D8c?C9b6E4{0l0Tv^3X zrd-qfbpIdzHn(1P<5y<~R+0V0w8RHnrdntSGZ|tG?vIxL%&uT$t73{ZK|Pl&;&QBi f?w|H=K9_Sp+NwB2$;{dgnwy?g!xu)r@2 literal 0 HcmV?d00001 diff --git a/fields/ROla/ra_san01b.fld2.gz b/fields/ROla/ra_san01b.fld2.gz new file mode 100644 index 0000000000000000000000000000000000000000..3130c5c1442df4834d2a5e51cb16142d062df102 GIT binary patch literal 3096 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 URvvSe(V~{C-A`RNeOD0v1ak#G%>V!Z literal 0 HcmV?d00001 diff --git a/fields/ROla/rachel.fld2.gz b/fields/ROla/rachel.fld2.gz new file mode 100644 index 0000000000000000000000000000000000000000..875ae5936e30073d9eeebb5216150ee16c7532e6 GIT binary patch literal 3684 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 literal 0 HcmV?d00001 diff --git a/fields/ROla/rgsr_in.fld2.gz b/fields/ROla/rgsr_in.fld2.gz new file mode 100644 index 0000000000000000000000000000000000000000..b5d2ba5c4e8ad763308eeb471dedde5d03182feb GIT binary patch literal 1832 zcmbW2X*8RO0*0mO{kZKHF}lbynpCH#(;-cY9$K}OF|}2dX;rO>P+Mqq5V15EqiB^{ zYOS>)T1v;3jxSTRwT1~ogV@y;duqQu^W*-!Kko1MJ@23QJm7qr{P2XtT@OlLs4x!T0jg;yQzHq^3fx zWsX4wc&q}pk0$cb7lY1RtU(eHD+Pu7749AL zYrzuxyx(GW*{)BL)#j($&#KdraK??493d2iG@Jhv*P}sr;<$BuU5wnMRBdB;NVDZn zvpqKGl%elR&ANQ@Lfl@e>(y}K+t3YbJwGTEEMs5FjC5dy;Y`yFMUmEX}7J0BfXc4`RJiO1oTm%7yc; z>#HATObOKwibAXH!!isk3lIS`)@Kx9F0{m;n_AqJwsn2uc!JhPZjmp!qDM~^d4~ST z1Un(RF8Lv-DbHfM_f}YjqbE;Yi|AZt>Bd_cNK;84fkF`};9n)~M>xBc#?zz=Z#E*m zdGop{5_E0C9I37O zqhbE3*pYA?sG0W$sVN$a8_a6O_s+`#8Ukt7_l^ylK!3v0*777!+~i!zpp{Dj0|3i# z6G&3)FC(ot11`!#9t-h?aH6dXqPg@u`aX<#3!HgQLd}#p+d_2fReD#a`gD7Ky z4N)LtgRK6oL2~|S8k=r&KLj4gpJ%efzoTD-*>!5qV7pML4|x;sDmk}1;-@9t%`IIC z^9~}*E0X@&r)R+&h|w0%aKd8}O~Kb36laeh;7j!z?))T{kVKAQDDolBa2{9v=4X5` zD;}IZqrS7Q40OAELnDG4>`I5v!+lu<9;$XElUBgWC~tYDhh!Zif&yy%Ux{>VkcNFW zEQ4qpq67c(xv>|)c_*A=oGiIt=1&qLhdr0jPJW`)5kB5L-G(6#FIkpnuB5-mpK_LB zT5ZXmagl<n2siUGoOJE z{Ti}s4vD{>0I0pYAm{RehcHmOF?N5fodOEIxL+mG<6q8lkc+l42c4os?n#~hj2r>D z9Z5R*t%6rBqkR9}IZVl1lqOBS325A&ywQJP=!Md0H(HL8dNc&l=&Do~Ga!>AGR{?40R@XOsI0Wm_U>lQ48diLLJ~-n^%}XJ5EB9gQMk@;<5RIaV>ORVD&y8us;esRsEis&HRNBlz2UgZSQL zA)!XL=1`!CX~yE*iF|wGR((mi>TP2VZfEcH=&AA7KbLWs6iWM`^l6};@x0sLKb}Z? zbd5S;Z-w=UOVH+>Z%aT^&lR;a z7{4S1lv5EQdbl9u$xb)J`aaBd1k$2h6BH`((RF=)1GOp}M3IrrTrl(gZ*3SMZeB}^ z2JD1QRsF3p;cG_}wY3hh-SB*;LH)XiYi~)F+~B1dUpdNCJMOQ-4rt*ls^pZZ#> z#9@o4CV@p;&5HuMt7~-t&Vh;a<7@AS0*)grA9myMRGHHUz~kB%aGT@d(gh~f8#n)J bAm0D$rf=$6My@Q#@Bi8Nqr*`dnACp&eYtF= literal 0 HcmV?d00001 diff --git a/fields/ROla/rwc01.fld2.gz b/fields/ROla/rwc01.fld2.gz new file mode 100644 index 0000000000000000000000000000000000000000..64deeeb1df193f5c0020d721f54251fc4f35738e GIT binary patch literal 275 zcmV+u0qp)CiwFphyF_RJ4sv&6FflG>Y-BP3?bk^T!ypU=(6Hs4x&Im3N^KQWY-2`~ zes2-~g;C@@&SOFa7~_oato@DfE*LOiz<>e6E*KqWMhJTv*DRVIW3H{M5&Ijj-c*R$ zmcB;5rOjX#BdLxCTmNiacO{>z3J#3#pMW?)ico0CtI%Rd$;^;o3^URMa^2dLg+?wx zHyCB2q4%zwTnliqHbf%s0ZL=$9ohf005f;feZiu literal 0 HcmV?d00001 diff --git a/fields/ROla/schg_cas01.fld2.gz b/fields/ROla/schg_cas01.fld2.gz new file mode 100644 index 0000000000000000000000000000000000000000..c2cd8f93376249abfdcacbddb2dd8d8e39408c0c GIT binary patch literal 3601 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 G=zjoIj9fhc literal 0 HcmV?d00001 diff --git a/fields/ROla/sec_in02.fld2.gz b/fields/ROla/sec_in02.fld2.gz new file mode 100644 index 0000000000000000000000000000000000000000..9ca19ebe48ce228e3ec5503f536c468955320ec1 GIT binary patch literal 1087 zcmbu+`8V4K003~s=4gGzh%;ZY(LLA9F_nDN7DE$SZwrm(d304*ip3F$6sQ^$y ztIgI0&$T`hEK)~DV!npin5}gLNgR!{R9#QmPx}k@+voQWwnST-+v7`90~pbfU*4rV zyE?@rM7gSM&dt(uoiDt*_rauWhoRv&s6DfRjq)=knF{)peit zQm4gQv4+D|e=$Ljvq6>AuWSa9!|lNmZUeJH0oW4B7sW8~V3vpzWi+H+Hk(G>L~_=+ zp&lK5<&^WSbr)Q7t+muQP*&+zh5?;g%G4-XO3l0P>(E^8^S0K%C>5$^h^43BqK9L= zk}wu!t{UIZw(3ZPcgs}*%9I*$%!*6Pylj%^b0?3>?sb07=8QRmcl zS%V8tR&FD9wwz9|88FLP6tJ|U{5GBo5YgcQW?Gk;SjxHRhFovOG6K=`S9Als3LvU} zkzU|@yJ!dQQ!&HcFuU%`c+&x4tC2(+K1n{`FKkUDjSPrLjz~Ig_hZa&R@*VXJ_VY7 z0$w3tCi&^dU@j&#-)5WO=%P6&L~$}z7rpn-IV1LO;=J~=Cjh7BlsPRTzCN%~Q_|HP z0en>%wna1KR|#tDz%{?^1Yr5WKpq@dNSML392k7UIewgOxzzW9P5el6JlVWG8y9!L zL#fM~t|YvJLUGs&PrcNYIGq88(Y;q9hv|r1wF8EXAN&gLp#YLYO~kcC+MEBz+?SCM z4c|}F<@}(Zj43%_)jQo_=L>+qAi1^)cG0$XNWwI_wWomIi}!tPR_YDUEcC4_^oODh(0bnQi?8&oOoV`^5p-8^+ zblAXsR)(V-+MyK8W6{P+TCPxH$HfIK2oxf!IhYDWbWuLAbHIAd8plzmQ)gmwW z9+XDR4`BCPFD?j8SmD?|>PBOarjaOKi>HDd-~aXv)H z%Eht|lWc{c#N{Syu+7l)Sr*A3Qv5958PCAyDJfOfj+PSsc8)`b=P*XFl8HOgiYWGO}`^0!F`##5fJf+IfRB? z$s%jr;@oG2D1`U&5{pKW<6u2V{F$NNbKEeJdNBG;muIz}ux;LyF1HQM3c3CTG?I~q qkhBu(s+Cti#jcceYuIn~k9e}YhW>j~DQF`n>Nn8WKk%(lQ~MX?ln(#^ literal 0 HcmV?d00001 diff --git a/fields/ROla/sec_pri.fld2.gz b/fields/ROla/sec_pri.fld2.gz new file mode 100644 index 0000000000000000000000000000000000000000..f7f11be5d0bd120e5432e97b38083596fdcc4f45 GIT binary patch literal 317 zcmV-D0mA+tiwFpiyF_RJ4s&H=UvP41E@o_GG63z?Z4QGV3 zF`c1atlenB!b|L6@WVvO`SZXiZkxtP*GN_@?VBc7d5KkAtJ%mW^WBxG8hKT680-jt z3H3U0dp}3Yw` PF-F7>d*b`n6ej=xgI1g~ literal 0 HcmV?d00001 diff --git a/fields/ROla/spl_fild01.fld2.gz b/fields/ROla/spl_fild01.fld2.gz new file mode 100644 index 0000000000000000000000000000000000000000..b0eeabdf97101c792125db97598eeced49e14f13 GIT binary patch literal 9554 zcmV-YC9T>YiwFpjyF_RJ4s&p9UuJ1+WH2!Q$h3%^3OMd}Ya_MCdA&$R_2!c_*mj)w*$ z<02R*5+ZD$<8jRZiKt^b-HhYQcuWHcsgvE--SjA-2yy!$k6`SsOiFLieW)*Xu!~&< zkT1aTmwAl+`}%esM6cgw1CN_=a7pFOnctlP660Z=h@Q{qx{hsOkWlg&`)fRu;3<(aBNNF z9}XK1hR0MPXfx6RgFI?E9wPOwP!8`mEmaL{2%^em9qgQdV<(*gSr_ z`#3rvx%6`;XsAa^*k*aGYGtkQv0w5H({YT?koWUAYT_R)Q6oAjJluL3qeJ2l{`9fY zHQn{;31%zfm&6;rnYc_B$iu-A=dq0an`B{^BV&U{TbC_Lyhf-Z%-Qr#9;$@1oFN{_ zLC3>L+2=8MuoMjq4f1r#!wtEgHOdXGd$Wt%Xpmv6b4rGzT1M_f9;}MY7 zY1CX?#$!1q!7U)|IHt~4N-c~5BHIBTv8#vG*3jl0nlMx)kAH|bTIBANhkgrUB~8?( z&w6)j^{VDk1tfk$2DCJ4;9i@D`n1OZDdAy<(4{qI__&mZ2}H$VJTmD|-yV;jev8J# zwmBY0Qt+7cf|c^nfOuxt=KX(4-hOS?DIQFFRTq=u#XQP^?5a`T&XeTfJI{k@u0>rb zAlLEe^o+Bdb39(JmpF4NJep>wYqP-P)Co!|USWn+JW3p5_tHbP>nPB|Ksm^dOIy05WtR-Ntc5 z3dPl%(l!sY#!zdnSs^Ts1WjwHg8Xir+Na}-BbBzH} zdTigq!vR95mAPq!X|~_b!`Qb_ibSppcn+=QA!mAjuUfWRv+iQY{!cP-?l9tb*n^}c%>*)nBJe-2lJbqnSsyx%vw`~zT zR_OO!Z9@Rrs;%DJnTqtx_ASl=jw&9uEEY%bMziICBD3j=Mys(T#8hPDw&SoDIDZ=t zs}yzxKx8)INIXuA2E<()`xQLe8E-%7eyv}W2TIzHNt(msSUj$KxP(U#$4DVmpgwc0 z7IyA|aHJNf;atk^D*7ysni`16Ue^nGz@@V;j*EE2a720ZA(1=R@z(!391V~5>geHU z1pYA|`t$j@&2rIY9Tb4ir-Kam2c3MlZ{b19RYR>|9!x+Sy)h{S{k=02ZV@lgCV2#L zFg)Z#GzfJp|8aRV)tNq9Z7#_CJZIbgzJ`aLv>oVp(2Dpa8oA19c^qDgc-YF4dO7O- zF3K$v+-QClppu7D<`fV2KuHPaq73_*42|Ri21jY53CoHEIO5#ELv7*I03@!*$X0?h zn+VjF3R1?y#}SmgzghGHSTR-Cbd8QuISuEJ2DQuuJRV4$c#`I|hGbn`V|71z$NpEjW?q#;w3|1grq=B)J2T?$hmC_$6Y)u<*xcE z9JvdszRCDnuU>9E>^sgJk9C3vJ3_vJN5DSG153&9(DhHjh<`iEI{(-{0pz1RB#`Ns zDJ48|)|O+)ndf^G549#kn{pmz@LC&rI3SI$8aO}Wocw-C|HiD}d^ z$s;D}c z#Jdy-m(RG%D`uPrBPclIFoi)PupGyP9r;8avCWye&+rJ^42}VTq|;h<`h;H>CYPHG zO|IZk%p{BBx$f_P250MD!6&%?t<~O|vQDpWQqChbLYUSQJg~DfE`ANT(8L0B3`j7PhrFF|20aUQIdUC&=mk5Ki4IK~AA z$IVYyooGgeEzRwf@`c^SJi-c{{Sg}Ccn{1Jtb6;`T`nam-Y^+`?)@+iPCLV*KNXyt zxFAP+um60GdJ3EWi+M~WK;dY43{x;PkH$sgK;tn%#7trHe;p4wV_c#h=CXy~wXG>ZQ5nrhYyr7P{V}GM(ABEaOqA zUI^Yqw>*jqhC^5T8z(n$oG;_C-%^4ho#hc#R|^CS2I=i~E06daT{RIpWQNE2gGo#T z#~IRV00|PgdVC1URDPPfj5rj-ia0Ek!mcbH&zY-8ipOu0Xkk#<+XSdmXATxoCi@WR z_Vf?&TQAVn0HRj8l*jh|aB?i2`l+~UDqvS8QMA#Yfy#!c4v<56mWQ9>(Wr<=@W5x! zZ+#GHdSkH1{24{x@o5+Ec>5*W8P?(;=0)0@HC_S$(fuSce2`GQkTL8mYI!W;9}~8# z4;%(V&VDzY$8iGlrr10jAs(CVZyX=(D+_x1yGz zl#k}ogfiV-@ocY15vj{9uX7jdEu4ToPy69f^02hO-y)UraByVKSrPMV*?I>7`tHrt z@-pt_^e@fJdV+^rKB}TkG#Z6h>G%C@bVf*HNX;G)w2YD>7 z=HJ3+`a&bBIlgEdpDblUX7Qptl((E(CLiR{e4cxXUu+&@Jw_$n0O^iq7%j#F9UV9Y z9|gzbcT1Ru26omw9L&UI9gk%J0#I-g#+5vXbbM1!?gkDowo~wa_hO#>dLG6ER~3&X z#A9g%m^C5oIO5Mw7(qX&#qp4P#xTFW+Up>g)=_y3<(S|h(`mD}y`2KgnsC-u&KeMV zbKWi7zNOI3Zi#i`%kWQC(Iq^#uUcq>@t&tmV}M}PS+_zuArUpJ;<0{`M+!#yVUUps zWR>y=OqbMA(egb0{c;@47zZS9tW@#n0|y*W1A$o{qqR#l9fML3P>6U0%SD#(jB^K% zaZac`8bIUa=u~V54;{vMv|PfYe|2;N51gds?}~Av0@*a-cU`Tf0vM|g;s2`}<2v-be`-EKtj4IWxG zig^U@dk=Bgt|85(P>$0)WEtS}HRA)?LiR?h zTG~%TbF|S&_A(xK0AZT*_Ikx>wqklsM>as{#XQFEy|q0T0_jD}+Uu2=l^ic67LTRb zcfrp%X#6#r!;%ao zlZg?cJe*@n=Y{1={%`V-W$QjH{p!ILAM57C6f0!x*Y|8UgMS5P#6QG?rZQyjm2mJ% zyj*D-6LN6Zr)P(@KhMK@y8w*faX4!K&NE_C&6n}uqa}E_7g9Ff+(urJZ(yhxT>r=<$G9d6^L-E@OH@*Knk7bze z^ms(dL$3E}GPCvG!Q*&`Fod4q5wf>#-)--_n8&aJ%Mg1NkM2ug#-n)|PVJ(1B@n5N za9zmb{pL}9)*K5{%wtcwKOHNlm$44*=Mo-6RGJ4{kwBFw@gW|`TP!epBpME%?HcZT z601=e9+7$wHCo0Krll{)BMHPg-ro&WfGQC0M@+gzxBq;C$B=_5Gi*fa;O~kr8oaBMF)Y3rrt4{S%qT4@i=MfSg=P?FEZ|}-b7#@ecMvf6}!J345 zJe(g}oaKQOl*7=R!hNfQ$JbEJqYZEYkEJ~yjGv8+?foW zdmu7)Ti7txBoE=(Ya1@bH}DwGSnDuAl%jO~f;94%q^CD9yJmOrFw;!&P~w_6#xAuu z`i4Z0emXH-j>7SgC*crza$hdV>G@nFu*(=cFcs#v=Kg2r*4i{a44e1^xbl=cNDOmp6t3h0G4D8yHAAx$L?GK{FuhIoX@O~A@UIFdLGU&BxPU0!_&Jz zQOWlf^VWxh)k+s!Oyev0g@?zot-q0h;LxV}IYtgk|&ua=hG2&E-bJ8bj`tnQQ zTLlkN3)8%9(6v1Tbi17LF-44+?TRRYZwVgRr*opcT7^SEuzWv%`_F0``$n>?x}ORC z-rdPVwqip0?UmY$pV;w1@lX!}xbbSs@DRTVM5!ejd-Zkp4=uR-BWpw{k8>8`q2FRL z_ZLKJWSNbEg^Q>ALhi3On#Xp>+5AA9Q=2gl>CW4tn7+uCyN4r^RsCOX0Upb_vH%vq z$)np4D#PfFY^LT!WXt%x#Q5`7`_?&>#L-#a1BCr>v7D5sbXnKM7P@{AkFc@qO56yq zZc2fK3FoE8^4ND&^0>xemTNaU!SmbRmAZ?&&;mSu|G#vATwyRt(MGg9J}X|`7)2I5 zUw!=mfjI`+?x;XxuVaaYd3-OB<-PVWkPoiN`X_mOArQ#J?tm(yH8Xov8RGG0&sfXB zBhW{ruQk%U6k2?IY->k9z=M#+rXYGaWw@sJD5@g_*&PD}sknrPRcfkHtzE}M-pWTH zqFGl$8<~OZqEv@zRB9O>91!`nr}M*#;}LagmB(*VYuE9BT`oS;u?e7FxIA8iY74zN z-Ou1|S@-AN{Ufs;Q;9`d^T-|PwjRB#GE^7c3g}3IHF8S2^=^F#96l07g4#OJ7{#)R zP+fGxVmx*`3u@d4ojxE9lgN98K&YY>O`5iC5$*u72Q+r+Mjhe12bG3!aL&Wp;d2)9 z*%sk?3Qf`VJ%;fYW(x+xl&_#~Q;+RSGY5R3C3tjKXFVgIgfd9Z_bhz*D4Tt5H+GfT z-1*8V8joXeu`I*G%ETCnW#x3W{T7+y8p!j{PXQ?)g4$RJhZF~5BWGjtKnhP zInXZipfXXWfRDLA5%b+44Ot#}`v{M!NCC_7nE5iVe;tofB1zp&^)47LS5&UX=YKr* zGQ?*QgK1Xyy@Q8S@|F8Kqe&dqFQ0;*Xuly#kB`8etGL>?e)2f3dKs9q5*W)B=PTK0 z#h{Edz6cf?QOn6^&g37qV>qg#vr8X7;&7bToPF z-@EW?`WtUwYAZRskz+{O2e&zuuj$gACar-@CaUKig;a7u4=b5863C2x)ZxoOe zH}7~8?~Q-{n;9Q>@PLvtZmT3!^AN%?53UL~@sQPwKfjxe0+LF3a1ZC+?|XIK9wmR~ zW~-ILK~;X#RSnmg^g zyVv<7c}w*z5xIrK>Ypb6+KL8<169kq+<2jEX^Db?uvd2C`+=d zT&%PNk1CnJ;w<&TA~?3mBrrhS1gq2Rea3=*GY|FZVMzrZ+zji}WNqxo{N+xm(1`Xp z57mRy?p2y3{dQg0>)*!%4UnOma`RA{Cty(3C}hUS`-2^$i=5lMyz0OECQ`YY!;ZN}^)=4bL1Jhpqkwv>S@$X;}O_4y_q zbT$`+4yk!xl-n}Kcr;zW7+K_MAh|T*1}j7nYrVwJSIg3WRaga&=W(ZH{2lFmMx+pR z6$l=awBzV`uq4_(3X_@aYm7)B>?;sGENUoTf=6uLC6G(7z2+nQ4$)Cmz+=&oB+gK_ zD+fq8d%lCT+PAZrF19mC#XY+Oj2s|jK4kLko!qmTwE4Af=Yb2@|465z*-wlGLMFsF z?!&iT7}1QD!5Q7f`}>bUV9O))o>nmy2bEH|X%p7Sxpm2TAN3URSY%E4@;NgN&|eh~ zt?jwD555jO7WtD+=YFQBqIPtr7>_{tOxyj2{6z}jHW=f!O=RB4VYgl6hN?%U65&Cg z?Xie)!|2X^5$~DRb{Mc4kM~S7Gj+3X#8auLd{ib*mSzE+`t=ILrqc=QFTev-kK@4} z)Ms9}lj$taqjy<#zG{56AKX5y&b_r8{RnG10>NFWtMypiQ3GV;G4yAh7102TT!b^>6GltGknv(X%$qDae1Dl7y6+#rjV7B$ z&_n{lo53R*3|keCCYi@0B~BD$|46-Itl)tTsNS7$TMJ_hSi~ct4~?$CV5{P>f5#^F zehUwn%M=gtDI9f`r>m(eh!<0A!aVRpMz@F1NLxLRHF!yT4u;1|C+8br99Y} zvS%8|hNGWuauV*HyYc=ik4$(12Q!CtrqXU#TL%fTplIVHH^-x9<;t(Ue&c6&tY91z^U#WY6%UaW ziKCE*7noIkekent|L*)859@V7w;$BvHX_q;nj&O~Q12U4Joej1I}(rrEAiPO66ij8 zs*1cPjPUsR&}U*jh_$Aa;*&t?d~^3*X!jhC*DzRCd!}MHqZBHS>w#4HU7R0zJ3?K+ z_xAvSv}`+ke?xrPHO1+2mW#Bc6eK@S8y1H_W^6aPUz|`^B`i z;|~uyVkl;KROrm`=+ca=N2OOSdh z9ge>5;E_`I)6JsME9|$pBP4x{pB`)HXB+2Fd?vR0M|fDCtRN8{y*Vwt{DcnSneEJm z7=Xxtck%F-5M+3iydr@LgS~?Wyfb=(Jk-a0xf({(7cOWS5IFC45fCVkh*-qqa0ItY zfz>>M>r{nbx;JT191}0Z!<2yA9NQ+U$pJAubvsUpIi8eZRiEH7v^ux+S|UI~JoGsV zal5Q^4BbR3jghFOnuo7VG#fTGO4KSEi|qz`NJF`5tTd8A9-uWoN}8KY)8Bb7DM*Tk+ls~EEys9vwRCtqo@P59h+|OtdBff( zAbAX4GCaESGSqk;2uPDmh{wZhR?fpqN@-urgTcY^Sk9r16L}7$ba^~J!NZ-$(s;n~ zM0wb-fLCJ%VmYsUAdTm9ETzxGWmS)ZnFj@X*EbK*xr~SAby?K!h5T9NIH)(PImg2T z!XneM=?mU@JNLAwtNHQau)31R<6C&RINljO9_B>jUZ;zASZ=Ko)*=53cswZ{1diX7 zP;$K+T$d6awqJcQ#CP$KMMK-nBWy@cf2gnL;p-tAcRIg`2avbVMw2{b|H1i}fMU10%kehiN3BdJQ#M!!Q_H6HYKg5ISaVDzM*oZVmxq-)-!aAIB=;XbW-Ksh38Dcbi z9$o%6jvpYm@=%qs$*T{0-di7@N5AfXQ54|e;P?kfNAF@DhEh6xr6l%zVqbEHM<_CP zzB*F%c}xRISS+ zPx5SUtsIrk8^9osRa?7?bky?DjO0<^dz@}O|M~E6bgt@n{CY+>@TK?`9@P1&#C30v z^Vc5R#|~wwP3Oye9IJ=IA>7zM58y?5~e(Od!%MK8T10DCD<1rRK z#{)ESd^B}=oIF;j_f+!egTXW>c?2h5bsnS_(=)?k@93#rb3l3=&tA+B2nqmQ zBX$#ywzuk7$FcL!WP>#;B|Pkm;Py!#zHvNaKA_*hNmrO{cmxD@3+3+Ov5Y*jcR3(j zm)#r>)FFuDAsVQv4`-M1P;92JmDip;D%&+Y)Wm|rW8e`aGKoW7ADXvzjusa#z(YMX z`Q0aZoIuh%62%kU^(S1`{MvO zJF95smg3P&Eyj8DE=hX|0}{;yl%($Dp{;{U@DL%{ONd^_&BB+Jzg80a#qU&?Q?ky@@IKy zKqwqRD|Cf2iSV!-jvEL4A{j$fA_vIBqfvY#3tWLj~3s`K@1sYGEx&WJp zn$EGPG?_p-j|E?<=XhwvF?hIfTpl_RgT~t*N#j++!+tN0D*%eK{nN<1388Hta3CJr zLUQ54J0eUT)}y8B&`%)8L%XSMv+Sio%{&i$z)4vO5B&2HL9!QfD1EZ-HkOu=q?ztHr876-}WEFM#Ao(CY4;$hulQL7GKKejMD z_P>djskQkUe^@`yqb(VA@RK~c`fFd9N%Alk#ZWx@2ox5Tho*6w2T|%N9U| zSjYelbi&%~Xwis{@<1K>D5%Jf{s#t(mO&nhSdWMD=ARoTK^$T+>K;pT6ysssvM9r1 z{~BouM=1|8A3DMqC`RKUZ+K7FN{0#_qO1dsk;m|@Jf%2vsc|49k1&nLFo8nwV1O9s ztM?vIR)fd+wN>BE19;663Gg70SUf~+_lOOS$3x;k-%@cKkKjVPJd3l!0URt3J$Y2k z7DuC}>yrZE;3(tq zG7w1tJ!jN89)?;nC)a-=j)4cpLENf0JoMaGb)k}s*d7GGP3ngS`E2ESug1}YRB%xD z>c5UO9*>QOl9hS@oIL7ru+O@t`$`ys2i{x2!!E3iCF&lmdKHIHq}AKtt$J_QAGD zczeGK!?g)L$NDNd-gcA$D9qsCvx5GwfN|)-C+@L2LnbN@%NiQnEiAm7HhN0+TFtyF zi>Lu0m*MF1!ntZ2rg*H|Cm9a&*v6KoTo%nOvX%wG<7yncE|{p;B1@6rAwqgQet=+5 zxN4O=yjhG(4AmhvpXKuyo^?VzTp2MO6Fg$_?z1=#aGM02Wp#vbhAa%VXRe*Kq-m`*;X_R#UYv&EpNk5CS$5PA7PT@34G1SyKcW zP0jw?Gpl-p=>c4yDIT#i7MTC3Hcg<_)^ad8hhVs8x`xf_Ew-NSo1K{PcS6wm2U z!k~~Ck8uM!D9#Uc`4L$#O?3|!@^Cy?@UXm>Px# literal 0 HcmV?d00001 diff --git a/fields/ROla/spl_fild02.fld2.gz b/fields/ROla/spl_fild02.fld2.gz new file mode 100644 index 0000000000000000000000000000000000000000..202e3e462fbedf1de1e90d80e5a7ceebbbe93685 GIT binary patch literal 9638 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 literal 0 HcmV?d00001 diff --git a/fields/ROla/te_aldecas2.fld2.gz b/fields/ROla/te_aldecas2.fld2.gz new file mode 100644 index 0000000000000000000000000000000000000000..e80bad172fb07a4198b201930d3f4cee57600253 GIT binary patch literal 1448 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 JVTuId006;+)ph^? literal 0 HcmV?d00001 diff --git a/fields/ROla/te_prtcas02.fld2.gz b/fields/ROla/te_prtcas02.fld2.gz new file mode 100644 index 0000000000000000000000000000000000000000..66fce157aa6d41db574cc3e53a45ab67b3631872 GIT binary patch literal 1345 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 literal 0 HcmV?d00001 diff --git a/fields/ROla/tur_dun01.fld2.gz b/fields/ROla/tur_dun01.fld2.gz new file mode 100644 index 0000000000000000000000000000000000000000..7353e3e9f26543af4c03647ff1a4132b921b8f05 GIT binary patch literal 3746 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 PTad()tz-fy9?t&&{iKU> literal 0 HcmV?d00001 diff --git a/fields/ROla/um_fild03.fld2.gz b/fields/ROla/um_fild03.fld2.gz new file mode 100644 index 0000000000000000000000000000000000000000..b313e34f44795d4cbac753d39d3f4d18143fdd7e GIT binary patch literal 5912 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 cpB@~ literal 0 HcmV?d00001 diff --git a/fields/ROla/ve_fild01.fld2.gz b/fields/ROla/ve_fild01.fld2.gz new file mode 100644 index 0000000000000000000000000000000000000000..ab9472da190f75d00ba2a23c5ad8b66f0178afc5 GIT binary patch literal 5640 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 literal 0 HcmV?d00001 diff --git a/fields/ROla/ve_fild03.fld2.gz b/fields/ROla/ve_fild03.fld2.gz new file mode 100644 index 0000000000000000000000000000000000000000..f30612cddeee0308ff88c4132569a6f3e8e13ba4 GIT binary patch literal 3350 zcmeH}>p#;CAIH^djx{Qg(=UfE${`_ye#)Uq4ha!+mh)N@<*=m-rBX&NOG3>;PM33z zTNFkPn^g`|#GE3{Y?z_l-Pe=*AGlxlqx;eK{dw?teLk=6qYscOB7$$R3K80S^Quo! zXhfi{zD`g?pq|iD-ssIK8%e7|H!L<`knzS1MZrIH=k`2cy>t3e1uaXW#hphEm? zB3ShjrZo={=ePZDkarPS%oyztX>_giAG#G}q&5rYg*EaTA7{o#mD~j{YUI4cg&JVK zyc4~hV)9R~0w%;;G$uaGVGzYV@tpX$VjeOk>7u{G9dr0-`}dAcedp>O{B-}GR4}q? zwvdB9trM?v!Z>ehip`GC{zQq~7Txt*`E!+P&H!OKq6=oj6TY^iv-YE1VGRl@HlOtjTw< zhPi&l)rg5X=Ge?Q`0)I5l^@FH!8J1b>aiYGSo|{NYfT7RiE_5c@5=nDE;#f@0Q4>6V) ztri$7G!w=~_tj-wJGCJRP!onW!14G+B`Jq;7>bgwp%|_iit=QMT>qjNUl3KUUA%q6 z73ux+S(LbJts4du-8z-LF@nE37C$Hvqu&AM zAG@3=jT+(S04|aCnt%g3V&RH!KoK zh6`pnp?+f2k8TSQgs3|-^5Y9CG=WOU8ahI2d*MsV`g@GYwn@`-7SlD_v()Xcr43wW z#j@*+klMKoV`(JM#&=$OQm-tS=meho;zMdAzm^=~2n24@lK z551(5&8SZgZ-~4y<|ZpG05witPo@GoTK5n9)r)xZ=%z1bNln#ZaEFVu`qoxW)qSycry{#E8`tRes2rc zCkRryS5ENQTZW)V#2GL*_V#^P?pzC(O-bSLWG`aU->M%u3L2cA?iaqG{aP6$>#+?h z!mlOb)^}c2j&z)*gwet+v^7^fORj=CpHr(fEb_TuLdE&HW#R!?M49Or^`W)PItODs z`lb$Hl@+iaL4vNtB$(FWkomYrwf@`_N<9?gp^pW)kY z{zS3rIOW(Tv-5`drrU6etOAh45L!mVpkDfV7-NqI@>)5lWpu1+VL&>!<;@OIOHT!h zU{qHsvR$-BQwGoF!-PZLmbiDXWn|kmbeekftg#C?gG!=#6YQWTOU&ga>AP>kFwq%< zMpy*4@5kmn6Y?kFze`5Ja(*BfR)&7@Xa6#~5mClUnb5V?HXQm$Ax!fzx#o&G+f_n* zkyW&I0^iB#btY`7HDy9J73sNFeo#*fvcg`sMm=iKt>=8W=E3mN#LD6A*U+sXh>4=~ ztxFNDXuUJR&RDJD$|{V6Wa1)m!vi@9=VnY<(%JQ)PjOl6ZsfE|TOBaU(RXemJt{^M zNZ~Dk$*MjwyY2RWR(OI7FTC%w92YQhkG^g&0WY4pK#vBLj*w2tS)ME%AA}3%!D=Nb zY66+Zn=S;$<%}4Jl*7JTdOvQ0$FgR5yv^x`ZvD(<&}5H!!nL7ll)v27^+Am?dsPZN zKRryk61I67-%HtKr~y~PJUr!DM))+`xP+Ht>1XKv_|z!~=~YCjCc}h2NA!9MhFLGd zyZGI)f@^_MN1vDW&dQX#LOB^VCFE|%In-RK8 z?3wm+tL^~Voce5riAZp7Z)iRWc4Qob0d3`czVRNh9OYhI?$h4#<|i7THBFB8;uLzy z4Y^LGZgtLh;M8ANkn=>B%8+6$^GJKr~gs%*t=0%Lp2UbLJ z_p4pMD1%luy)Ed%?LsoCn#5@X+?x$!?6 z1cl(G1#UxXn2+lQEos?U8nWqzUt}HlM2=Q+0nX1rqkeTQ;@$<;hCpQkPzkYLk%2C~ z;jPeQ*rso9@kaXP+XgQWA(BW)`%OmNd9Rj5vfM?O&es^4P;*?5*Cu5{4K36;B{PW<}q|${N zob|R~u@&ZagD}MNvt$E2-m-O4&S{ z{R7bAnYl44JN9!Am}}K3+w3J+#;y46wbxDPyN%vHU=pP!bN`ga%+`s`&ea=8nu4Fk zP;MQE@DsOQP?j{15Q2_bFQ;WofoY#Dnp22tVHaFan7>6dP^-#3^EW$%FG-*HVLdA&3Lk zm3LobkFf+!fd_J8iZ5erP!6Z#wAEO|ZVdyrLq%M}Kz7HUb_{riQ^-`_zm4sUI2GbZ z_w&L(_I*x51J*Xx&x|hEtCb2fbswf%sG^?c+^;3hRd=BeD97#_B8cf`$H5B*@QJW~ zn}_KUqaM2F4kgys7EW0O-oyfDxLm1C%}?H*ZCy;%6rnn ztWit8zgig?DzgB&RC!%l34wn+I|5x3Opm#~kxjq-Kp1^z(4ghvFUR2*ALVw^kF(Qr zni6lg9S6(-htUN?^Osz)xyZx0Eqrgl{tteP_w^O@l7@K_S1fWK(n~p`yxPn~`=;SI z*1u1BTD_}t+!Y6*@6w;#-<=E#{dr~KNCJ;57N#|N*H}Z?hvziAY+3rcDp00mYTqy2 zL*lPskV%uG&Xq%8Bnq$;e&%X?@INy))gS-BgICoq_0K@HkTJ#zRf&1~+Lh-wtRla^Z8I(~Y|^85c%Nu#BN1R) p=N4b$47u!?-;vxucVm6e0`C9E|LPw)ZL4@NiL;50yU;s@{srQ=dvyQ+ literal 0 HcmV?d00001 diff --git a/fields/ROla/ve_fild03a.fld2.gz b/fields/ROla/ve_fild03a.fld2.gz new file mode 100644 index 0000000000000000000000000000000000000000..d03986ed333030e3684a7b30a8c16ec1625b9a5a GIT binary patch literal 3351 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 literal 0 HcmV?d00001 diff --git a/fields/ROla/ve_fild03b.fld2.gz b/fields/ROla/ve_fild03b.fld2.gz new file mode 100644 index 0000000000000000000000000000000000000000..7e401526c4205d1ef7c83b990bb264af1241d88e GIT binary patch literal 3351 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 literal 0 HcmV?d00001 diff --git a/fields/ROla/ve_fild05.fld2.gz b/fields/ROla/ve_fild05.fld2.gz new file mode 100644 index 0000000000000000000000000000000000000000..dc88df37e3e16a8f28037a0125cfdeb55f861bcd GIT binary patch literal 4303 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 Ql-p;aIJyy!x*uWs4=*KiZU6uP literal 0 HcmV?d00001 diff --git a/fields/ROla/ve_fild07a.fld2.gz b/fields/ROla/ve_fild07a.fld2.gz new file mode 100644 index 0000000000000000000000000000000000000000..962845f457c7411c2b8f63f0c91d6365104cfb00 GIT binary patch literal 4269 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 b5c=od>3{baseName} = $baseName; my $file = $self->sourceName . ".fld2"; - if ($Settings::fields_folder) { - $file = File::Spec->catfile($Settings::fields_folder, $file); + my $fieldsFolder = $Settings::fields_folder; + if (defined $masterServer) { + $fieldsFolder = $masterServer->{fields_folder} if $masterServer->{fields_folder}; + $fieldsFolder = $masterServer->{fieldsFolder} if $masterServer->{fieldsFolder}; } - if (! -f $file) { - $file .= ".gz"; + my @fieldFolders = grep { defined $_ && $_ ne '' } ($fieldsFolder); + push @fieldFolders, $Settings::fields_folder if !defined($fieldsFolder) || $fieldsFolder ne $Settings::fields_folder; + + my $resolvedFile; + for my $folder (@fieldFolders) { + my $candidate = File::Spec->catfile($folder, $file); + if (-f $candidate) { + $resolvedFile = $candidate; + last; + } + if (-f "$candidate.gz") { + $resolvedFile = "$candidate.gz"; + last; + } } - if (-f $file) { - $self->loadFile($file, $loadWeightMap); + if ($resolvedFile) { + $self->loadFile($resolvedFile, $loadWeightMap); $self->{baseName} = $baseName; $self->{name} = $name; } else { diff --git a/tables/servers.txt b/tables/servers.txt index 65c7c6d389..7a8eaaf60b 100644 --- a/tables/servers.txt +++ b/tables/servers.txt @@ -219,6 +219,7 @@ serverType ROla serverEncoding Western storageEncryptKey 0x050B6F79, 0x0202C179, 0x0E20120, 0x04FA43E3, 0x0179B6C8, 0x05973DF2, 0x07D8D6B, 0x08CB9ED9 addTableFolders ROla +fields_folder fields/ROla charBlockSize 155 gameGuard 1 pinCode 1 From 1103dcde40e0959390104ddf4e5d14b07722b6db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Bosc=C3=A1?= Date: Tue, 10 Mar 2026 17:55:56 -0300 Subject: [PATCH 02/18] Add OpenKore LLM curation planning documents --- openkore_llm_plan/architecture_overview.md | 38 ++++++++++++ openkore_llm_plan/code_index.md | 43 +++++++++++++ openkore_llm_plan/curation_plan.md | 51 ++++++++++++++++ openkore_llm_plan/recommended_file_groups.md | 63 ++++++++++++++++++++ 4 files changed, 195 insertions(+) create mode 100644 openkore_llm_plan/architecture_overview.md create mode 100644 openkore_llm_plan/code_index.md create mode 100644 openkore_llm_plan/curation_plan.md create mode 100644 openkore_llm_plan/recommended_file_groups.md diff --git a/openkore_llm_plan/architecture_overview.md b/openkore_llm_plan/architecture_overview.md new file mode 100644 index 0000000000..c078f79398 --- /dev/null +++ b/openkore_llm_plan/architecture_overview.md @@ -0,0 +1,38 @@ +# OpenKore Architecture Overview (for LLM Curation) + +## 1) Runtime entry and initialization flow +- Entry script: `openkore.pl`. +- Bootstrap loads `src/` and `src/deps/`, initializes interface + settings, then requires `src/functions.pl`. +- Main loop state machine (in `functions.pl`) progresses through: + 1. plugin loading + 2. control/table loading + 3. networking init + 4. portal DB init + 5. first-time prompt + 6. final init + 7. steady-state loop. + +## 2) Core functional layers +- **Execution & AI**: `src/AI*.pm`, `src/Task*.pm`, `src/TaskManager.pm`. +- **Domain model**: `src/Actor*.pm`, `src/ActorList.pm`, `src/InventoryList*.pm`, `src/Skill.pm`, `src/Field.pm`. +- **Network stack**: `src/Network*.pm`, `src/Network/Receive/*`, `src/Network/Send/*`, plus XKore modes. +- **Configuration/data loading**: `src/Settings.pm`, `src/FileParsers.pm`, `src/Globals.pm`, `control/*.txt`, `tables/**`. +- **Extensibility**: `src/Plugins.pm` + `plugins/*`. +- **Interfaces/logging/debug**: `src/Interface*.pm`, `src/Log.pm`, `src/ErrorHandler.pm`, `src/test/*`. + +## 3) Plugin and automation architecture +- Plugin framework discovers `.pl` plugin files and supports selective load via `control/sys.txt`. +- Two automation ecosystems to prioritize for GPT knowledge: + - **macro** plugin (`plugins/macro/*`) using `macro_file` (default `macros.txt`). + - **eventMacro** plugin (`plugins/eventMacro/*`) using `eventMacro_file` (default `eventMacros.txt`). + +## 4) Networking specialization points +- Packet parsing/sending core in `src/Network/PacketParser.pm`, `src/Network/Receive.pm`, `src/Network/Send.pm`. +- Server/client packet mappings are server-type specific (`src/Network/Receive/*`, `src/Network/Send/*`) and table-backed by `tables/**/recvpackets.txt`. +- XKore paths: + - `src/Network/XKore.pm` (mode 1) + - `src/Network/XKore2.pm` + `src/Network/XKore2/*` (proxy mode) + - `src/Network/XKoreProxy.pm`. + +## 5) Curation implication +For a public knowledge base, keep conceptual coverage broad but file payload bounded: include framework files + representative server-type packet files first, then expand by region/date-specific packet modules in later batches. diff --git a/openkore_llm_plan/code_index.md b/openkore_llm_plan/code_index.md new file mode 100644 index 0000000000..f3da3f0b02 --- /dev/null +++ b/openkore_llm_plan/code_index.md @@ -0,0 +1,43 @@ +# Code Index for Knowledge Curation + +## Runtime entry & orchestration +- `openkore.pl` — process startup, module bootstrap, interface init. +- `src/functions.pl` — main loop state machine, plugin/data/network init sequencing. +- `src/Modules.pm` — module registration/reload utilities. + +## Configuration and data +- `src/Settings.pm` — control/table folder resolution, CLI args, file registration/loading. +- `src/FileParsers.pm` — parsers for config/control/table files. +- `src/Globals.pm` — shared runtime state and loaded data structures. +- `control/*.txt` — live bot behavior config. +- `tables/**` — static/server-specific knowledge (items, maps, skills, recvpackets, etc.). + +## AI, behavior, and tasks +- `src/AI.pm`, `src/AI/CoreLogic.pm`, `src/AI/Attack.pm`. +- `src/TaskManager.pm`, `src/Task.pm`, `src/Task/*.pm`. + +## Actors and game entities +- `src/Actor.pm`, `src/Actor/*.pm`, `src/ActorList.pm`. +- `src/InventoryList.pm`, `src/InventoryList/*.pm`. +- `src/Skill.pm`, `src/Field.pm`. + +## Networking, packets, and proxies +- `src/Network.pm`, `src/Network/DirectConnection.pm`. +- `src/Network/PacketParser.pm`, `src/Network/Receive.pm`, `src/Network/Send.pm`. +- `src/Network/Receive/*`, `src/Network/Send/*` — serverType/region/version specialization. +- `src/Network/XKore.pm`, `src/Network/XKore2.pm`, `src/Network/XKoreProxy.pm`, `src/Network/XKore2/*`. + +## Plugin framework and notable plugins +- `src/Plugins.pm` — plugin lifecycle + hooks. +- `plugins/*/*.pl` — plugin entrypoints. +- High-value deep plugin trees: + - `plugins/macro/**` + - `plugins/eventMacro/**` + - `plugins/profiles/**` + - `plugins/reconnect/**` + - `plugins/map/**` + +## Debugging and diagnostics +- `src/Log.pm`, `src/ErrorHandler.pm`, `src/Interface/Console.pm`. +- `src/test/*` — unit/regression tests useful for behavior confirmation. +- Optional advanced debug plugins under `plugins/needs-review/*`. diff --git a/openkore_llm_plan/curation_plan.md b/openkore_llm_plan/curation_plan.md new file mode 100644 index 0000000000..b97f6db376 --- /dev/null +++ b/openkore_llm_plan/curation_plan.md @@ -0,0 +1,51 @@ +# Curation Plan (Staged, Small-Batch Extraction) + +## Stage 0 — Seed context (small, high-signal) +**Goal:** Let GPT answer “what is OpenKore?” and explain startup/config basics. +- `README.md`, `openkore.pl` +- `src/functions.pl`, `src/Settings.pm`, `src/Globals.pm`, `src/FileParsers.pm` +- `control/config.txt`, `control/sys.txt` +- `tables/servers.txt`, top-level `tables/recvpackets.txt` (if present for base profile) + +## Stage 1 — Core runtime and AI +**Goal:** Explain bot behavior orchestration and object model. +- `src/AI.pm`, `src/AI/CoreLogic.pm`, `src/AI/Attack.pm` +- `src/Task.pm`, `src/TaskManager.pm`, key `src/Task/*.pm` +- `src/Actor.pm`, `src/Actor/*.pm`, `src/ActorList.pm`, `src/InventoryList*.pm`, `src/Skill.pm`, `src/Field.pm` + +## Stage 2 — Plugin framework + mainstream plugins +**Goal:** Cover extension model and commonly used plugin workflows. +- `src/Plugins.pm` +- Mainline plugin entrypoints (top-level plugin `.pl` files) +- Deep include: `plugins/macro/**`, `plugins/eventMacro/**`, `plugins/profiles/**`, `plugins/reconnect/**`, `plugins/map/**` + +## Stage 3 — Config/control corpus +**Goal:** Make GPT strong at control-file troubleshooting. +- Entire `control/*.txt` defaults +- Any config-relevant docs in repo root and `src/doc/` (only user-facing references) +- Map each file to parser + runtime module (from `Settings.pm` / `functions.pl`) + +## Stage 4 — Tables corpus (split by profile) +**Goal:** Avoid oversize ingestion while preserving lookup value. +- Batch A: core generic top-level tables (`tables/*.txt`) +- Batch B: one reference server profile (e.g., `tables/kRO`) +- Batch C+: regional packs (`iRO`, `bRO`, `idRO`, etc.) as separate increments +- Keep `Old/` as optional legacy annex + +## Stage 5 — Networking and packets +**Goal:** Enable packet/version/serverType reasoning. +- Core: `src/Network.pm`, `src/Network/PacketParser.pm`, `src/Network/Receive.pm`, `src/Network/Send.pm` +- XKore: `src/Network/XKore*.pm`, `src/Network/XKore2/*` +- Packet families in chunks: generic server types first, then regional/date variants +- Align each code chunk with corresponding `tables/**/recvpackets.txt` + +## Stage 6 — Debugging and validation corpus +**Goal:** Improve diagnostic answers and developer guidance. +- `src/Log.pm`, `src/ErrorHandler.pm`, `src/Interface/Console.pm` +- `src/test/*` (selected high-signal unit tests) +- Debug-oriented plugins (e.g., `plugins/needs-review/packet-recorder`, `packetMiner`) as optional annex + +## Stage 7 — Public GPT polish +- Add concise metadata per file group: purpose, audience, volatility, risk of stale packet info. +- Tag known “advanced/internal” areas (Poseidon, bus, XKore2 internals) to avoid novice confusion. +- Maintain a changelog-driven refresh cycle (network files and tables refresh most frequently). diff --git a/openkore_llm_plan/recommended_file_groups.md b/openkore_llm_plan/recommended_file_groups.md new file mode 100644 index 0000000000..822fcbe823 --- /dev/null +++ b/openkore_llm_plan/recommended_file_groups.md @@ -0,0 +1,63 @@ +# Recommended File Groups for Public OpenKore GPT + +## 01_openkore_core +- `README.md`, `openkore.pl`, `start.pl` +- `src/functions.pl`, `src/Modules.pm`, `src/Globals.pm`, `src/Settings.pm`, `src/FileParsers.pm` +- `src/AI.pm`, `src/AI/CoreLogic.pm`, `src/AI/Attack.pm`, `src/AI/Slave*.pm` +- `src/TaskManager.pm`, `src/Task.pm`, `src/Task/*.pm` +- `src/Actor.pm`, `src/Actor/*.pm`, `src/ActorList.pm` +- `src/InventoryList*.pm`, `src/Skill.pm`, `src/Field.pm`, `src/Misc.pm`, `src/Commands.pm` + +## 02_openkore_plugins +- `src/Plugins.pm` +- Plugin entrypoints: `plugins/*/*.pl` +- Full include priority: + - `plugins/macro/**` + - `plugins/eventMacro/**` + - `plugins/profiles/**` + - `plugins/reconnect/**` + - `plugins/map/**` +- Optional annex (advanced/legacy): `plugins/needs-review/**` + +## 03_openkore_config +- Entire `control/*.txt` +- Include especially: + - `control/config.txt` + - `control/sys.txt` + - `control/mon_control.txt` + - `control/items_control.txt` + - `control/timeouts.txt` + - `control/poseidon.txt` +- If macro/eventMacro examples exist in deployments, include `control/macros.txt` and `control/eventMacros.txt` patterns as documentation snippets. + +## 04_openkore_tables +- Top-level canonical tables in `tables/*.txt` +- One primary server profile first (recommended: `tables/kRO/**` due breadth) +- Then regional packs in separate batches (`tables/iRO/**`, `tables/bRO/**`, `tables/idRO/**`, etc.) +- Optional legacy: `tables/Old/**` + +## 05_openkore_networking +- Core networking: + - `src/Network.pm` + - `src/Network/DirectConnection.pm` + - `src/Network/PacketParser.pm` + - `src/Network/Receive.pm` + - `src/Network/Send.pm` +- Server specializations: + - `src/Network/Receive/**` + - `src/Network/Send/**` +- XKore: + - `src/Network/XKore.pm` + - `src/Network/XKore2.pm` + - `src/Network/XKoreProxy.pm` + - `src/Network/XKore2/**` +- Packet mapping tables: + - `tables/**/recvpackets.txt` + - `tables/**/sync.txt` + +## 06_openkore_docs +- `README.md`, `LegacyChangelog.md` +- `src/doc/**` +- `plugins/eventMacro/README.md` +- Any plugin-local README files and test docs that explain behavior boundaries +- Keep historical/obsolete docs lower priority than runtime + config + packet sources From 003431d6da89c689ecf91799676aefcf31638a82 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Bosc=C3=A1?= Date: Tue, 10 Mar 2026 18:13:13 -0300 Subject: [PATCH 03/18] Create focused OpenKore core knowledge bundle --- openkore_llm_knowledge/core/src/AI.pm | 804 + openkore_llm_knowledge/core/src/AI/Attack.pm | 860 ++ .../core/src/AI/CoreLogic.pm | 4122 +++++ openkore_llm_knowledge/core/src/Actor.pm | 973 ++ openkore_llm_knowledge/core/src/ActorList.pm | 202 + openkore_llm_knowledge/core/src/Commands.pm | 8750 +++++++++++ .../core/src/ErrorHandler.pm | 127 + .../core/src/FileParsers.pm | 1607 ++ openkore_llm_knowledge/core/src/Globals.pm | 634 + openkore_llm_knowledge/core/src/Interface.pm | 250 + openkore_llm_knowledge/core/src/Log.pm | 458 + openkore_llm_knowledge/core/src/Misc.pm | 5764 +++++++ openkore_llm_knowledge/core/src/Modules.pm | 223 + openkore_llm_knowledge/core/src/Network.pm | 53 + .../core/src/Network/PacketParser.pm | 538 + .../core/src/Network/Receive.pm | 12561 ++++++++++++++++ .../core/src/Network/Send.pm | 3589 +++++ openkore_llm_knowledge/core/src/Plugins.pm | 554 + openkore_llm_knowledge/core/src/Settings.pm | 917 ++ openkore_llm_knowledge/core/src/Task.pm | 413 + openkore_llm_knowledge/core/src/Task/Move.pm | 197 + openkore_llm_knowledge/core/src/Task/Route.pm | 847 ++ .../core/src/Task/TalkNPC.pm | 977 ++ .../core/src/TaskManager.pm | 500 + .../core/src/doc/modules.txt | 66 + openkore_llm_knowledge/core/src/functions.pl | 1120 ++ .../knowledge/architecture_overview.md | 27 + .../knowledge/code_index_core.md | 41 + 28 files changed, 47174 insertions(+) create mode 100644 openkore_llm_knowledge/core/src/AI.pm create mode 100644 openkore_llm_knowledge/core/src/AI/Attack.pm create mode 100644 openkore_llm_knowledge/core/src/AI/CoreLogic.pm create mode 100644 openkore_llm_knowledge/core/src/Actor.pm create mode 100644 openkore_llm_knowledge/core/src/ActorList.pm create mode 100644 openkore_llm_knowledge/core/src/Commands.pm create mode 100644 openkore_llm_knowledge/core/src/ErrorHandler.pm create mode 100644 openkore_llm_knowledge/core/src/FileParsers.pm create mode 100644 openkore_llm_knowledge/core/src/Globals.pm create mode 100644 openkore_llm_knowledge/core/src/Interface.pm create mode 100644 openkore_llm_knowledge/core/src/Log.pm create mode 100644 openkore_llm_knowledge/core/src/Misc.pm create mode 100644 openkore_llm_knowledge/core/src/Modules.pm create mode 100644 openkore_llm_knowledge/core/src/Network.pm create mode 100644 openkore_llm_knowledge/core/src/Network/PacketParser.pm create mode 100644 openkore_llm_knowledge/core/src/Network/Receive.pm create mode 100644 openkore_llm_knowledge/core/src/Network/Send.pm create mode 100644 openkore_llm_knowledge/core/src/Plugins.pm create mode 100644 openkore_llm_knowledge/core/src/Settings.pm create mode 100644 openkore_llm_knowledge/core/src/Task.pm create mode 100644 openkore_llm_knowledge/core/src/Task/Move.pm create mode 100644 openkore_llm_knowledge/core/src/Task/Route.pm create mode 100644 openkore_llm_knowledge/core/src/Task/TalkNPC.pm create mode 100644 openkore_llm_knowledge/core/src/TaskManager.pm create mode 100644 openkore_llm_knowledge/core/src/doc/modules.txt create mode 100644 openkore_llm_knowledge/core/src/functions.pl create mode 100644 openkore_llm_knowledge/knowledge/architecture_overview.md create mode 100644 openkore_llm_knowledge/knowledge/code_index_core.md diff --git a/openkore_llm_knowledge/core/src/AI.pm b/openkore_llm_knowledge/core/src/AI.pm new file mode 100644 index 0000000000..ce222483ff --- /dev/null +++ b/openkore_llm_knowledge/core/src/AI.pm @@ -0,0 +1,804 @@ +######################################################################### +# OpenKore - AI +# Copyright (c) OpenKore Team +# +# This software is open source, licensed under the GNU General Public +# License, version 2. +# Basically, this means that you're allowed to modify and distribute +# this software. However, if you distribute modified versions, you MUST +# also distribute the source code. +# See http://www.gnu.org/licenses/gpl.html for the full license. +######################################################################### +## +# MODULE DESCRIPTION: Helper functions for managing @ai_seq +# +# +# Eventually, @ai_seq should never be referenced directly, and then it can be +# moved into this package. +# +# +# Eventually, all AI managing functions can be moved to various packages such as Actor. + +package AI; + +use strict; +use Time::HiRes qw(time); +use Globals; +use Utils qw(binFind); +use Log qw(message warning error debug); +use Utils; +use Field; +use Exporter; +use base qw(Exporter); +use Translation; + +our @EXPORT = ( + qw/ + ai_clientSuspend + ai_drop + ai_follow + ai_partyfollow + ai_getAggressives + ai_slave_getAggressives + ai_getPlayerAggressives + ai_getMonstersAttacking + ai_mapRoute_searchStep + ai_items_take + ai_route + ai_route_getRoute + ai_sellAutoCheck + ai_setMapChanged + ai_setSuspend + ai_skillUse + ai_skillUse2 + ai_storageAutoCheck + ai_useTeleport + ai_canOpenStorage + cartGet + cartAdd + ai_talkNPC + attack + gather + move + sit + stand + take/ +); + +### CATEGORY: AI state constants + +## +# AI::OFF +# +# AI is turned off. +use constant OFF => 0; + +## +# AI::MANUAL +# +# AI is set to manual mode. +use constant MANUAL => 1; + +## +# AI::AUTO +# +# AI is turned on. +use constant AUTO => 2; + +# Do not change $AI::AI directly, use AI::state instead +our $AI = AUTO; + +### CATEGORY: Functions + +sub state { + if (defined $_[0]) { + if ($_[0] != OFF && $_[0] != MANUAL && $_[0] != AUTO) { + error "Invalid AI state value given to AI::state (".($_[0])."). Ignoring state change.\n"; + return; + } + Plugins::callHook('AI_state_change', { + old => $AI, + new => $_[0] + }); + $AI = $_[0]; + } + return $AI; +} + +sub action { + my $i = (defined $_[0] ? $_[0] : 0); + return $ai_seq[$i]; +} + +sub args { + my $i = (defined $_[0] ? $_[0] : 0); + return \%{$ai_seq_args[$i]}; +} + +sub dequeue { + shift @ai_seq; + shift @ai_seq_args; +} + +sub queue { + my $action = shift; + my $args = shift; + unshift(@ai_seq, $action); + unshift @ai_seq_args, ((defined $args) ? $args : {}); +} + +sub clear { + my $total = scalar @_; + + # If no arg was given clear all AI queue + if ($total == 0) { + undef @ai_seq; + undef @ai_seq_args; + + # If 1 arg was given find it in the queue + } elsif ($total == 1) { + my $wanted_action = shift; + my $seq_index; + foreach my $i (0..$#ai_seq) { + next unless ($ai_seq[$i] eq $wanted_action); + $seq_index = $i; + last; + } + return unless (defined $seq_index); # return unless we found the action in the queue + + splice(@ai_seq, $seq_index , 1); # Splice it out of @ai_seq + splice(@ai_seq_args, $seq_index , 1); # Splice it out of @ai_seq_args + # When there are multiple of the same action (route, attack, route) the splices of remove the first one + # So recursively call AI::clear again with the same action until none is found + AI::clear($wanted_action); + + # If more than 1 arg was given recursively call AI::clear for each one + } else { + foreach (@_) { + AI::clear($_); + } + } +} + +sub suspend { + my $i = (defined $_[0] ? $_[0] : 0); + $ai_seq_args[$i]{suspended} = time if $i < @ai_seq_args; +} + +sub mapChanged { + my $i = (defined $_[0] ? $_[0] : 0); + $ai_seq_args[$i]{mapChanged} = time if $i < @ai_seq_args; +} + +sub findAction { + my $wanted_action = shift; + my $skip = shift; + if (!defined $skip) { + $skip = 0; + } + + foreach my $i (0..$#ai_seq) { + next unless ($ai_seq[$i] eq $wanted_action); + if ($skip) { + $skip--; + } else { + return $i; + } + } + + return undef; +} + +sub inQueue { + foreach (@_) { + # Apparently using a loop is faster than calling + # binFind() (which is optimized in C), because + # of function call overhead. + #return 1 if defined binFind(\@ai_seq, $_); + foreach my $seq (@ai_seq) { + return 1 if ($_ eq $seq); + } + } + return 0; +} + +sub isIdle { + return $ai_seq[0] eq ""; +} + +sub is { + foreach (@_) { + return 1 if ($ai_seq[0] eq $_); + } + return 0; +} + + +########################################## + + +sub ai_clientSuspend { $char->clientSuspend(@_) if $char } # check for $char: its possible that we start xkore 1 with the client open telling us to relog + +## +# ai_drop(items, max) +# items: reference to an array of inventory item numbers. +# max: the maximum amount to drop, for each item, or 0 for unlimited. +# +# Drop one or more items. +# +# Example: +# # Drop inventory items 2 and 5. +# ai_drop([2, 5]); +# # Drop inventory items 2 and 5, but at most 30 of each item. +# ai_drop([2, 5], 30); +sub ai_drop { + my $r_items = shift; + my $max = shift; + my %seq = (); + + if (@{$r_items} == 1) { + # Dropping one item; do it immediately + Misc::drop($r_items->[0], $max); + } else { + # Dropping multiple items; queue an AI sequence + $seq{items} = \@{$r_items}; + $seq{max} = $max; + $seq{timeout} = 1; + AI::queue("drop", \%seq); + } +} + +sub ai_follow { + my $name = shift; + + return 0 if (!$name); + + if (binFind(\@ai_seq, "follow") eq "") { + my %args; + $args{name} = $name; + push @ai_seq, "follow"; + push @ai_seq_args, \%args; + } + + return 1; +} + +sub ai_partyfollow { + # we have to enable re-calc of route based on master's possition regulary, even when it is + # on route and move, otherwise we have finaly moved to the possition and found that the master + # already teleported to another side of the map. + + # This however will give problem on few seq such as storageAuto as 'move' and 'route' might + # be triggered to move to the NPC + + my %master; + $master{id} = main::findPartyUserID($config{followTarget}); + if ($master{id} ne "" && !AI::inQueue("storageAuto","transferItems","sellAuto","buyAuto")) { + + $master{x} = $char->{party}{users}{$master{id}}{pos}{x}; + $master{y} = $char->{party}{users}{$master{id}}{pos}{y}; + ($master{map}) = $char->{party}{users}{$master{id}}{map} =~ /([\s\S]*)\.gat/; + + if ($master{map} ne $field->name || $master{x} == 0 || $master{y} == 0) { # Compare including InstanceID + delete $master{x}; + delete $master{y}; + } + + return unless ($master{map} ne $field->name || exists $master{x}); # Compare including InstanceID + + # Compare map names including InstanceID + if ((exists $ai_v{master} && blockDistance(\%master, $ai_v{master}) > 15) + || $master{map} != $ai_v{master}{map} + || (timeOut($ai_v{master}{time}, 15) && blockDistance(\%master, $char->{pos_to}) > $config{followDistanceMax})) { + + $ai_v{master}{x} = $master{x}; + $ai_v{master}{y} = $master{y}; + $ai_v{master}{map} = $master{map}; + ($ai_v{master}{map_name}, undef) = Field::nameToBaseName(undef, $master{map}); # Hack to clean up InstanceID + $ai_v{master}{time} = time; + + if ($ai_v{master}{map} ne $field->name) { + message TF("Calculating route to find master: %s\n", $ai_v{master}{map_name}), "follow"; + } elsif (blockDistance(\%master, $char->{pos_to}) > $config{followDistanceMax} ) { + message TF("Calculating route to find master: %s (%s,%s)\n", $ai_v{master}{map_name}, $ai_v{master}{x}, $ai_v{master}{y}), "follow"; + } else { + return; + } + + AI::clear("move", "route", "mapRoute"); + ai_route( + $ai_v{master}{map_name}, + $ai_v{master}{x}, + $ai_v{master}{y}, + distFromGoal => $config{followDistanceMin}, + isFollow => 1 + ); + + my $followIndex = AI::findAction("follow"); + if (defined $followIndex) { + $ai_seq_args[$followIndex]{ai_follow_lost_end}{timeout} = $timeout{ai_follow_lost_end}{timeout}; + } + } + } +} + +## +# ai_getAggressives([check_mon_control], [party]) +# Returns: an array of monster IDs, or a number. +# +# Get a list of all aggressive monsters on screen. +# The definition of "aggressive" is: a monster who has hit or missed me. +# +# If $check_mon_control is set, then all monsters in mon_control.txt +# with the 'attack_auto' flag set to 2, will be considered as aggressive. +# See also the manual for more information about this. +# +# If $party is set, then monsters that have fought with party members +# (not just you) will be considered as aggressive. +sub ai_getAggressives { + my ($type, $party) = @_; + my $wantArray = wantarray; + my $num = 0; + my @agMonsters; + + for my $monster (@$monstersList) { + my $control = Misc::mon_control($monster->name,$monster->{nameID}) if $type || !$wantArray; + my $ID = $monster->{ID}; + # Never attack monsters that we failed to get LOS with + next if (!timeOut($monster->{attack_failedLOS}, $timeout{ai_attack_failedLOS}{timeout})); + next if (!timeOut($monster->{attack_failed}, $timeout{ai_attack_unfail}{timeout})); + next if (!Misc::checkMonsterCleanness($ID)); + + if (Misc::is_aggressive($monster, $control, $type, $party)) { + if ($wantArray) { + # Function is called in array context + push @agMonsters, $ID; + + } else { + # Function is called in scalar context + if ($control->{weight} > 0) { + $num += $control->{weight}; + } elsif ($control->{weight} != -1) { + $num++; + } + } + } + } + + if ($wantArray) { + return @agMonsters; + } else { + return $num; + } +} + +## +# ai_slave_getAggressives(slave, [check_mon_control]) +# Returns: an array of monster IDs, or a number. +# +# Get a list of all aggressive monsters on screen for a given slave. +# The definition of "aggressive" is: a monster who has hit or missed me. +# +# If $check_mon_control is set, then all monsters in mon_control.txt +# with the 'attack_auto' flag set to 2, will be considered as aggressive. +# See also the manual for more information about this. +sub ai_slave_getAggressives { + my ($slave, $type, $party) = @_; + my $wantArray = wantarray; + my $num = 0; + my @agMonsters; + + for my $monster (@$monstersList) { + my $control = Misc::mon_control($monster->name,$monster->{nameID}) if $type || !$wantArray; + my $ID = $monster->{ID}; + # Never attack monsters that we failed to get LOS with + next if (!timeOut($monster->{attack_failedLOS}, $timeout{ai_attack_failedLOS}{timeout})); + next if (!timeOut($monster->{$slave->{ai_attack_failed_timeout}}, $timeout{ai_attack_unfail}{timeout})); + next if (!Misc::slave_checkMonsterCleanness($slave, $ID)); + # TODO: Is there any situation where we should use calcPosFromPathfinding or calcPosFromTime here? + my $pos = calcPosition($monster); + next if (blockDistance($char->position, $pos) > ($config{$slave->{configPrefix}.'followDistanceMax'} + $config{$slave->{configPrefix}.'attackMaxDistance'})); + + if (Misc::is_aggressive_slave($slave, $monster, $control, $type, $party)) { + if ($wantArray) { + # Function is called in array context + push @agMonsters, $ID; + + } else { + # Function is called in scalar context + if ($control->{weight} > 0) { + $num += $control->{weight}; + } elsif ($control->{weight} != -1) { + $num++; + } + } + } + } + + if ($wantArray) { + return @agMonsters; + } else { + return $num; + } +} + +sub ai_getPlayerAggressives { + my $ID = shift; + my @agMonsters; + + foreach (@monstersID) { + next if ($_ eq ""); + if ($monsters{$_}{dmgToPlayer}{$ID} > 0 || $monsters{$_}{missedToPlayer}{$ID} > 0 || $monsters{$_}{dmgFromPlayer}{$ID} > 0 || $monsters{$_}{missedFromPlayer}{$ID} > 0) { + push @agMonsters, $_; + } + } + return @agMonsters; +} + +## +# ai_getMonstersAttacking($ID) +# +# Get the monsters who are attacking player $ID. +sub ai_getMonstersAttacking { + my $ID = shift; + my @agMonsters; + foreach (@monstersID) { + next unless $_; + my $monster = $monsters{$_}; + push @agMonsters, $_ if $monster->{target} eq $ID; + } + return @agMonsters; +} + +sub ai_mapRoute_searchStep { + my $r_args = shift; + + unless ($r_args->{openlist} && %{$r_args->{openlist}}) { + $r_args->{done} = 1; + $r_args->{found} = ''; + return 0; + } + + my $parent = (sort {$$r_args{'openlist'}{$a}{'walk'} <=> $$r_args{'openlist'}{$b}{'walk'}} keys %{$$r_args{'openlist'}})[0]; + debug "$parent, $$r_args{'openlist'}{$parent}{'walk'}\n", "route/path"; + # Uncomment this if you want minimum MAP count. Otherwise use the above for minimum step count + #foreach my $parent (keys %{$$r_args{'openlist'}}) + { + my ($portal,$dest) = split /=/, $parent; + if ($$r_args{'budget'} ne '' && $$r_args{'openlist'}{$parent}{'zeny'} > $$r_args{'budget'}) { + #This link is too expensive + delete $$r_args{'openlist'}{$parent}; + next; + } else { + #MOVE this entry into the CLOSELIST + $$r_args{'closelist'}{$parent}{'walk'} = $$r_args{'openlist'}{$parent}{'walk'}; + $$r_args{'closelist'}{$parent}{'zeny'} = $$r_args{'openlist'}{$parent}{'zeny'}; + $$r_args{'closelist'}{$parent}{'parent'} = $$r_args{'openlist'}{$parent}{'parent'}; + #Then delete in from OPENLIST + delete $$r_args{'openlist'}{$parent}; + } + + if ($portals_lut{$portal}{'dest'}{$dest}{'map'} eq $$r_args{'dest'}{'map'}) { + if ($$r_args{'dest'}{'pos'}{'x'} eq '' && $$r_args{'dest'}{'pos'}{'y'} eq '') { + $$r_args{'found'} = $parent; + $$r_args{'done'} = 1; + undef @{$$r_args{'mapSolution'}}; + my $this = $$r_args{'found'}; + while ($this) { + my %arg; + $arg{'portal'} = $this; + my ($from,$to) = split /=/, $this; + ($arg{'map'},$arg{'pos'}{'x'},$arg{'pos'}{'y'}) = split / /,$from; + ($arg{dest_map}, $arg{dest_pos}{x}, $arg{dest_pos}{y}) = split(' ', $to); + $arg{'walk'} = $$r_args{'closelist'}{$this}{'walk'}; + $arg{'zeny'} = $$r_args{'closelist'}{$this}{'zeny'}; + $arg{'steps'} = $portals_lut{$from}{'dest'}{$to}{'steps'}; + unshift @{$$r_args{'mapSolution'}},\%arg; + $this = $$r_args{'closelist'}{$this}{'parent'}; + } + return; + } elsif ( ai_route_getRoute(\@{$$r_args{'solution'}}, $$r_args{'dest'}{'field'}, $portals_lut{$portal}{'dest'}{$dest}, $$r_args{'dest'}{'pos'}) ) { + my $walk = "$$r_args{'dest'}{'map'} $$r_args{'dest'}{'pos'}{'x'} $$r_args{'dest'}{'pos'}{'y'}=$$r_args{'dest'}{'map'} $$r_args{'dest'}{'pos'}{'x'} $$r_args{'dest'}{'pos'}{'y'}"; + $$r_args{'closelist'}{$walk}{'walk'} = scalar @{$$r_args{'solution'}} + $$r_args{'closelist'}{$parent}{$dest}{'walk'}; + $$r_args{'closelist'}{$walk}{'parent'} = $parent; + $$r_args{'closelist'}{$walk}{'zeny'} = $$r_args{'closelist'}{$parent}{'zeny'}; + $$r_args{'found'} = $walk; + $$r_args{'done'} = 1; + undef @{$$r_args{'mapSolution'}}; + my $this = $$r_args{'found'}; + while ($this) { + my %arg; + $arg{'portal'} = $this; + my ($from,$to) = split /=/, $this; + ($arg{'map'},$arg{'pos'}{'x'},$arg{'pos'}{'y'}) = split / /,$from; + $arg{'walk'} = $$r_args{'closelist'}{$this}{'walk'}; + $arg{'zeny'} = $$r_args{'closelist'}{$this}{'zeny'}; + $arg{'steps'} = $portals_lut{$from}{'dest'}{$to}{'steps'}; + unshift @{$$r_args{'mapSolution'}},\%arg; + $this = $$r_args{'closelist'}{$this}{'parent'}; + } + return; + } + } + #get all children of each openlist + foreach my $child (keys %{$portals_los{$dest}}) { + next unless $portals_los{$dest}{$child}; + foreach my $subchild (keys %{$portals_lut{$child}{'dest'}}) { + my $destID = $subchild; + my $mapName = $portals_lut{$child}{'source'}{'map'}; + ############################################################# + my $penalty = int($routeWeights{lc($mapName)}) + int(($portals_lut{$child}{'dest'}{$subchild}{'steps'} ne '') ? $routeWeights{'NPC'} : $routeWeights{'PORTAL'}); + my $thisWalk = $penalty + $$r_args{'closelist'}{$parent}{'walk'} + $portals_los{$dest}{$child}; + if (!exists $$r_args{'closelist'}{"$child=$subchild"}) { + if ( !exists $$r_args{'openlist'}{"$child=$subchild"} || $$r_args{'openlist'}{"$child=$subchild"}{'walk'} > $thisWalk ) { + $$r_args{'openlist'}{"$child=$subchild"}{'parent'} = $parent; + $$r_args{'openlist'}{"$child=$subchild"}{'walk'} = $thisWalk; + $$r_args{'openlist'}{"$child=$subchild"}{'zeny'} = $$r_args{'closelist'}{$parent}{'zeny'} + $portals_lut{$child}{'dest'}{$subchild}{'cost'}; + } + } + } + } + } +} + +sub ai_items_take { + my ($x1, $y1, $x2, $y2) = @_; + my %args; + $args{pos}{x} = $x1; + $args{pos}{y} = $y1; + $args{pos_to}{x} = $x2; + $args{pos_to}{y} = $y2; + $args{ai_items_take_end}{time} = time; + $args{ai_items_take_end}{timeout} = $timeout{ai_items_take_end}{timeout}; + $args{ai_items_take_start}{time} = time; + $args{ai_items_take_start}{timeout} = $timeout{ai_items_take_start}{timeout}; + $args{ai_items_take_delay}{timeout} = $timeout{ai_items_take_delay}{timeout}; + AI::queue("items_take", \%args); +} + +sub ai_route { $char->route(@_) } + +#sellAuto for items_control - chobit andy 20030210 +sub ai_sellAutoCheck { + for my $item (@{$char->inventory}) { + next if ($item->{equipped} || $item->{unsellable}); + my $control = Misc::items_control($item->{name}, $item->{nameID}); + if ($control->{sell} && $item->{amount} > $control->{keep}) { + return 1; + } + } +} + +sub ai_setMapChanged { + my $index = shift; + $index = 0 if ($index eq ""); + if ($index < @ai_seq_args) { + $ai_seq_args[$index]{'mapChanged'} = time; + } +} + +sub ai_setSuspend { $char->setSuspend(@_) } + +sub ai_skillUse { + return if ($char->{muted}); + my %args = ( + skillHandle => shift, + lv => shift, + maxCastTime => { time => time, timeout => shift }, + minCastTime => { time => time, timeout => shift }, + target => shift, + y => shift, + tag => shift, + ret => shift, + waitBeforeUse => { time => time, timeout => shift }, + prefix => shift, + isStartSkill => shift + ); + $args{giveup}{time} = time; + $args{giveup}{timeout} = $timeout{ai_skill_use_giveup}{timeout}; + + if ($args{y} ne "") { + $args{x} = $args{target}; + delete $args{target}; + } + + my $skill = Skill->new(auto => $args{skillHandle}); + my $owner = $skill->getOwner(); + my $lvl = $owner->getSkillLevel($skill); + if ($lvl < $args{lv}) { + debug "[$owner] Attempted to use skill (".$args{skillHandle}.") level ".$args{lv}." which you do not have, adjusting to level ".$lvl.".\n", "ai"; + $args{lv} = $lvl; + } + + if ($skill->getOwnerType == Skill::OWNER_CHAR) { + AI::queue("skill_use", \%args); + } else { + $owner->queue("skill_use", \%args); + } +} + +## +# ai_skillUse2($skill, $lvl, $maxCastTime, $minCastTime, $target) +# +# Calls ai_skillUse(), +# resolving $target to ($x, $y) if $skill is an area skill, +# or to $skill->getOwner if $skill is a self skill. +# +# FIXME: Finish and use Task::UseSkill instead. +sub ai_skillUse2 { + my ($skill, $lvl, $maxCastTime, $minCastTime, $target, $prefix, $waitBeforeUse, $tag, $isStartSkill) = @_; + + ai_skillUse( + $skill->getHandle(), + $lvl, + $maxCastTime, + $minCastTime, + $skill->getTargetType == Skill::TARGET_LOCATION ? (@{$target->{pos_to}}{qw(x y)}) + : $skill->getTargetType == Skill::TARGET_SELF ? ($skill->getOwner->{ID}, undef) + : ($target->{ID}, undef), + $tag, undef, $waitBeforeUse, $prefix, $isStartSkill + ) +} + +## +# ai_storageAutoCheck() +# +# Returns 1 if it is time to perform storageAuto sequence. +# Returns 0 otherwise. +sub ai_storageAutoCheck { + return 0 unless ai_canOpenStorage(); + + for my $item (@{$char->inventory}) { + next if ($item->{equipped}); + my $control = Misc::items_control($item->{name}, $item->{nameID}); + if ($control->{storage} && $item->{amount} > $control->{keep}) { + return 1; + } + } + # TODO: check getAuto + return 0; +} + +sub ai_canOpenStorage { + # Check NV_BASIC and SU_BASIC_SKILL (Doram) + return 0 if ($char->getSkillLevel(new Skill(handle => 'NV_BASIC')) < 6 && $char->getSkillLevel(new Skill(handle => 'SU_BASIC_SKILL')) < 1); + + # Check if we have enough zeny to open storage (and if it matters) + # Also check for a Free Ticket for Kafra Storage (7059) + return 0 if (!$config{storageAuto_useChatCommand} && !$config{storageAuto_useItem} && $config{minStorageZeny} > 0 && + $char->{zeny} < $config{minStorageZeny} && !$char->inventory->getByNameID(7059)); + + return 1; +} + + +## +# cartGet(items) +# items: a reference to an array of indices. +# +# Get one or more items from cart. +# \@items is a list of hashes; each has must have an "index" key, and may optionally have an "amount" key. +# "index" is the index of the cart inventory item number. If "amount" is given, only the given amount of +# items will retrieved from cart. +# +# Example: +# # You want to get 5 Apples (inventory item 2) and all +# # Fly Wings (inventory item 5) from cart. +# my @items; +# push @items, {index => 2, amount => 5}; +# push @items, {index => 5}; +# cartGet(\@items); +sub cartGet { + my $items = shift; + return unless ($items && @{$items}); + + my %args; + $args{items} = $items; + $args{timeout} = $timeout{ai_cartAuto} ? $timeout{ai_cartAuto}{timeout} : 0.15; + AI::queue("cartGet", \%args); +} + +## +# cartAdd(items) +# items: a reference to an array of hashes. +# +# Put one or more items in cart. +# \@items is a list of hashes; each has must have an "index" key, and may optionally have an "amount" key. +# "index" is the index of the inventory item number. If "amount" is given, only the given amount of items will be put in cart. +# +# Example: +# # You want to add 5 Apples (inventory item 2) and all +# # Fly Wings (inventory item 5) to cart. +# my @items; +# push @items, {index => 2, amount => 5}; +# push @items, {index => 5}; +# cartAdd(\@items); +sub cartAdd { + my $items = shift; + return unless ($items && @{$items}); + + my %args; + $args{items} = $items; + $args{timeout} = $timeout{ai_cartAuto} ? $timeout{ai_cartAuto}{timeout} : 0.15; + AI::queue("cartAdd", \%args); +} + +## +# ai_talkNPC(x, y, sequence) +# x, y: the position of the NPC to talk to. +# sequence: A string containing the NPC talk sequences. +# +# Talks to an NPC. +sub ai_talkNPC { + require Task::TalkNPC; + AI::queue("NPC", new Task::TalkNPC(type => 'talknpc', x => $_[0], y => $_[1], sequence => $_[2])); +} + +## +# void ai_useTeleport(int level) +# level: 1 - Random, 2 - Respawn +sub ai_useTeleport { + $char->useTeleport(@_); +} + +sub attack { $char->attack(@_) } + +sub gather { + my $ID = shift; + my %args; + $args{ai_items_gather_giveup}{time} = time; + $args{ai_items_gather_giveup}{timeout} = $timeout{ai_items_gather_giveup}{timeout}; + $args{ID} = $ID; + $args{pos} = { %{$items{$ID}{pos}} }; + AI::queue("items_gather", \%args); + debug "Targeting for Gather: $items{$ID}{name} ($items{$ID}{binID})\n"; +} + +sub sit { + require Task::SitStand; + my $task = new Task::SitStand(actor => $char, mode => 'sit', wait => $timeout{ai_sit_wait}{timeout}); + AI::queue("sitting", $task); + my $lookDelay = $config{sitAuto_look_delay}; + $lookDelay = 0 if (!defined $lookDelay || $lookDelay < 0); + delete $ai_v{sitAuto_pendingLook}; + if (defined $config{sitAuto_look}) { + my $defaultLook = $config{sitAuto_look}; + my $lookPlan = { body => $defaultLook, delay => $lookDelay }; + + if ($config{sitAuto_look_from_wall}) { + my $closestWalls = Misc::getClosestWalls($char->{pos}, $config{sitAuto_look_from_wall}); + if (@{$closestWalls}) { + my $referenceWall = $closestWalls->[int(rand(@{$closestWalls}))]; + my $oppositeDirectionPos = { + x => (2 * $char->{pos}{x}) - $referenceWall->{x}, + y => (2 * $char->{pos}{y}) - $referenceWall->{y}, + }; + my ($bodyDirection, $headDirection) = Misc::getNaturalLookDirections($char->{pos}, $oppositeDirectionPos, $defaultLook); + $lookPlan = { body => $bodyDirection, head => $headDirection, delay => $lookDelay }; + } + } + + $ai_v{sitAuto_pendingLook} = $lookPlan; + } +} + +sub stand { + require Task::SitStand; + my $task = new Task::SitStand(actor => $char, mode => 'stand', wait => $timeout{ai_stand_wait}{timeout}); + AI::queue("standing", $task); +} + +sub take { + my $ID = shift; + my %args; + return if (!$items{$ID}); + $args{ai_take_giveup}{time} = time; + $args{ai_take_giveup}{timeout} = $timeout{ai_take_giveup}{timeout}; + $args{ID} = $ID; + $args{pos} = {%{$items{$ID}{pos}}}; + AI::queue("take", \%args); + debug "Picking up: $items{$ID}{name} ($items{$ID}{binID})\n"; +} + +return 1; + diff --git a/openkore_llm_knowledge/core/src/AI/Attack.pm b/openkore_llm_knowledge/core/src/AI/Attack.pm new file mode 100644 index 0000000000..c580c8ac6e --- /dev/null +++ b/openkore_llm_knowledge/core/src/AI/Attack.pm @@ -0,0 +1,860 @@ +######################################################################### +# OpenKore - Attack AI +# Copyright (c) 2006 OpenKore Team +# +# This software is open source, licensed under the GNU General Public +# License, version 2. +# Basically, this means that you're allowed to modify and distribute +# this software. However, if you distribute modified versions, you MUST +# also distribute the source code. +# See http://www.gnu.org/licenses/gpl.html for the full license. +# +# $Revision: 4286 $ +# $Id: Commands.pm 4286 2006-04-17 14:02:27Z illusion_kore $ +# +######################################################################### +# +# This module contains the attack AI's code. +package AI::Attack; + +use strict; +use Carp::Assert; +use Time::HiRes qw(time); + +use Globals; +use AI; +use Actor; +use Field; +use Log qw(message debug warning); +use Translation qw(T TF); +use Misc; +use Network::Send (); +use Skill; +use Utils; +use Utils::Benchmark; +use Utils::PathFinding; +use Data::Dumper; +$Data::Dumper::Sortkeys = 1; + +use constant { + MOVING_TO_ATTACK => 1, + ATTACKING => 2, +}; + +sub process { + Benchmark::begin("ai_attack") if DEBUG; + my $args = AI::args; + my $action = AI::action; + + if (shouldAttack($action, $args)) { + my $ID; + my $ataqArgs; + my $stage; # 1 - moving to attack | 2 - attacking + if (AI::action eq "attack") { + $ID = $args->{ID}; + $ataqArgs = AI::args(0); + $stage = ATTACKING; + } else { + if (AI::action(1) eq "attack") { + $ataqArgs = AI::args(1); + + } elsif (AI::action(2) eq "attack") { + $ataqArgs = AI::args(2); + } + $ID = $args->{attackID}; + $stage = MOVING_TO_ATTACK; + } + + if (targetGone($ataqArgs, $ID)) { + finishAttacking($ataqArgs, $ID); + return; + } elsif (shouldGiveUp($ataqArgs, $ID)) { + giveUp($ataqArgs, $ID, 0); + return; + } + + my $target = Actor::get($ID); + unless ($target && $target->{type} ne 'Unknown') { + finishAttacking($ataqArgs, $ID); + return; + } + + my $party = $config{'attackAuto_party'} ? 1 : 0; + my $target_is_aggressive = is_aggressive($target, undef, 0, $party); + + if ($config{attackChangeTarget}) { + my $routeIndex = AI::findAction("route"); + $routeIndex = AI::findAction("mapRoute") if (!defined $routeIndex); + my $attackOnRoute; + if (defined $routeIndex) { + $attackOnRoute = AI::args($routeIndex)->{attackOnRoute}; + } else { + $attackOnRoute = 2; + } + + my @aggressives = ai_getAggressives($attackOnRoute, $party); + + if (!$target_is_aggressive && @aggressives) { + my $attackTarget = getBestTarget(\@aggressives, $config{attackCheckLOS}, $config{attackCanSnipe}); + if ($attackTarget && $attackTarget ne $target->{ID}) { + $char->sendAttackStop; + AI::dequeue while ( AI::inQueue("attack") ); + ai_setSuspend(0); + my $new_target = Actor::get($attackTarget); + warning TF("Your target is not aggressive: %s, changing target to aggressive: %s.\n", $target, $new_target), 'ai_attack'; + $target->{droppedForAggressive} = 1; + $char->attack($attackTarget); + AI::Attack::process(); + return; + } + } + } + + my $cleanMonster = checkMonsterCleanness($ID); + if (!$cleanMonster) { + message TF("Dropping target %s - will not kill steal others\n", $target), 'ai_attack'; + $char->sendAttackStop; + $target->{ignore} = 1; + AI::dequeue while (AI::inQueue("attack")); + if ($config{teleportAuto_dropTargetKS}) { + message T("Teleport due to dropping attack target\n"), "teleport"; + ai_useTeleport(1); + } + return; + } + + my $control = mon_control($target->{name},$target->{nameID}); + if ($control->{attack_auto} == 3 && ($target->{dmgToYou} || $target->{missedYou} || $target->{dmgFromYou})) { + message TF("Dropping target - %s (%s) has been provoked\n", $target->{name}, $target->{binID}); + $char->sendAttackStop; + $target->{ignore} = 1; + AI::dequeue while (AI::inQueue("attack")); + return; + } + + my %plugin_args; + $plugin_args{target} = $target; + $plugin_args{control} = $control; + $plugin_args{stage} = $stage; + $plugin_args{party} = $party; + $plugin_args{target_is_aggressive} = $target_is_aggressive; + $plugin_args{return} = 0; + Plugins::callHook('AI::Attack::process' => \%plugin_args); + return if ($plugin_args{return}); + + if ($stage == MOVING_TO_ATTACK) { + # Check for hidden monsters + if (($target->{statuses}->{EFFECTSTATE_BURROW} || $target->{statuses}->{EFFECTSTATE_HIDING}) && $config{avoidHiddenMonsters}) { + message TF("Dropping target %s - will not attack hidden monsters\n", $target), 'ai_attack'; + $char->sendAttackStop; + $target->{ignore} = 1; + + AI::dequeue while (AI::inQueue("attack")); + if ($config{teleportAuto_dropTargetHidden}) { + message T("Teleport due to dropping hidden target\n"); + ai_useTeleport(1); + } + return; + } + + # We're on route to the monster; check whether the monster has moved + if ($args->{attackID} && timeOut($timeout{ai_attack_route_adjust})) { + if ( + $target->{type} ne 'Unknown' && + $ataqArgs->{monsterLastMoveTime} && + $ataqArgs->{monsterLastMoveTime} != $target->{time_move} + ) { + if ( + ($args->{monsterLastMovePosTo}{x} == $target->{pos_to}{x} && $args->{monsterLastMovePosTo}{y} == $target->{pos_to}{y}) + ) { + $args->{monsterLastMoveTime} = $target->{time_move}; + $args->{monsterLastMovePosTo}{x} = $target->{pos_to}{x}; + $args->{monsterLastMovePosTo}{y} = $target->{pos_to}{y}; + } else { + # Monster has moved; stop moving and let the attack AI readjust route + debug "Target $target has moved since we started routing to it - Adjusting route\n", "ai_attack"; + AI::dequeue while (AI::is("move", "route")); + + $ataqArgs->{ai_attack_giveup}{time} = time; + $ataqArgs->{sentApproach} = 0; + undef $args->{unstuck}{time}; + undef $args->{avoiding}; + undef $args->{move_start}; + } + } else { + $timeout{ai_attack_route_adjust}{time} = time; + } + } + } + + if ($stage == ATTACKING) { + if (AI::args->{suspended}) { + $args->{ai_attack_giveup}{time} += time - $args->{suspended}; + delete $args->{suspended}; + + # We've just finished moving to the monster. + # Don't count the time we spent on moving + } elsif ($args->{move_start}) { + $args->{ai_attack_giveup}{time} += time - $args->{move_start}; + undef $args->{unstuck}{time}; + undef $args->{move_start}; + + } elsif ($args->{avoiding}) { + $args->{ai_attack_giveup}{time} = time; + undef $args->{avoiding}; + debug "Finished avoiding movement from target $target, updating ai_attack_giveup\n", "ai_attack"; + } + + if (timeOut($timeout{ai_attack_main})) { + if ($char->{sitting}) { + ai_setSuspend(0); + stand(); + } else { + main(); + } + $timeout{ai_attack_main}{time} = time; + } + + } + } + + Benchmark::end("ai_attack") if DEBUG; +} + +sub shouldAttack { + my ($action, $args) = @_; + return ( + ($action eq "attack" && $args->{ID}) || + ($action eq "route" && AI::action(1) eq "attack" && $args->{attackID}) || + ($action eq "move" && AI::action(2) eq "attack" && $args->{attackID}) + ); +} + +sub shouldGiveUp { + my ($args, $ID) = @_; + return !$config{attackNoGiveup} && (timeOut($args->{ai_attack_giveup}) || $args->{unstuck}{count} > 5); +} + +sub giveUp { + my ($args, $ID, $LOS) = @_; + my $target = Actor::get($ID); + if ($monsters{$ID}) { + if ($LOS) { + $target->{attack_failedLOS} = time; + } else { + $target->{attack_failed} = time; + } + } + $target->{dmgFromYou} = 0; # Hack | TODO: Fix me + AI::dequeue while (AI::inQueue("attack")); + message T("Can't reach or damage target, dropping target\n"), "ai_attack"; + if ($config{'teleportAuto_dropTarget'}) { + message T("Teleport due to dropping attack target\n"); + ai_useTeleport(1); + } +} + +sub targetGone { + my ($args, $ID) = @_; + my $target = Actor::get($ID, 1); + unless ($target) { + return 1; + } + if (exists $target->{dead} && $target->{dead} == 1) { + return 1; + } + return 0; +} + +sub finishAttacking { + my ($args, $ID) = @_; + $timeout{'ai_attack'}{'time'} -= $timeout{'ai_attack'}{'timeout'}; + AI::dequeue while (AI::inQueue("attack")); + message TF( "Finished attacking\n"), "ai_attack"; + if ($monsters_old{$ID} && $monsters_old{$ID}{dead}) { + message TF("Target %s died\n", $monsters_old{$ID}), "ai_attack"; + Plugins::callHook('target_died', {monster => $monsters_old{$ID}}); + monKilled(); + + # Pickup loot when monster's dead + if (AI::state == AI::AUTO && $config{'itemsTakeAuto'} && $monsters_old{$ID}{dmgFromYou} > 0 && !$monsters_old{$ID}{ignore}) { + AI::clear("items_take"); + ai_items_take($monsters_old{$ID}{pos}{x}, $monsters_old{$ID}{pos}{y}, + $monsters_old{$ID}{pos_to}{x}, $monsters_old{$ID}{pos_to}{y}); + } else { + # Cheap way to suspend all movement to make it look real + ai_clientSuspend(0, $timeout{'ai_attack_waitAfterKill'}{'timeout'}); + } + + ## kokal start + ## mosters counting + my $i = 0; + my $found = 0; + while ($monsters_Killed[$i]) { + if ($monsters_Killed[$i]{'nameID'} eq $monsters_old{$ID}{'nameID'}) { + $monsters_Killed[$i]{'count'}++; + monsterLog($monsters_Killed[$i]{'name'}); + $found = 1; + last; + } + $i++; + } + if (!$found) { + $monsters_Killed[$i]{'nameID'} = $monsters_old{$ID}{'nameID'}; + $monsters_Killed[$i]{'name'} = $monsters_old{$ID}{'name'}; + $monsters_Killed[$i]{'count'} = 1; + monsterLog($monsters_Killed[$i]{'name'}) + } + ## kokal end + + } elsif ($config{teleportAuto_lostTarget}) { + message T("Target lost, teleporting.\n"), "ai_attack"; + ai_useTeleport(1); + } else { + message T("Target lost\n"), "ai_attack"; + } + + $messageSender->sendStopSkillUse($char->{last_continuous_skill_used}) if $char->{last_skill_used_is_continuous}; + Plugins::callHook('attack_end', {ID => $ID}) +} + +sub find_kite_position { + my ($args, $inAdvance, $target, $realMyPos, $realMonsterPos, $noAttackMethodFallback_runFromTarget) = @_; + + my $maxDistance; + if (!$noAttackMethodFallback_runFromTarget && defined $args->{attackMethod}{type} && defined $args->{attackMethod}{maxDistance}) { + $maxDistance = $args->{attackMethod}{maxDistance}; + } elsif ($noAttackMethodFallback_runFromTarget) { + $maxDistance = $config{'runFromTarget_noAttackMethodFallback_attackMaxDist'}; + } else { + # Should never happen. + return 0; + } + + # We try to find a position to kite from at least runFromTarget_minStep away from the target but at maximun {attackMethod}{maxDistance} away from it + my $pos = meetingPosition($char, 1, $target, $maxDistance, ($noAttackMethodFallback_runFromTarget ? 2 : 1)); + if ($pos) { + if ($inAdvance) { + debug TF("[runFromTarget_inAdvance] %s kiting in advance (%d %d) to (%d %d), mob at (%d %d).\n", $char, $realMyPos->{x}, $realMyPos->{y}, $pos->{x}, $pos->{y}, $realMonsterPos->{x}, $realMonsterPos->{y}), 'ai_attack'; + } elsif ($noAttackMethodFallback_runFromTarget) { + debug TF("[runFromTarget_noAttackMethodFallback] %s kiting in advance (%d %d) to (%d %d), mob at (%d %d).\n", $char, $realMyPos->{x}, $realMyPos->{y}, $pos->{x}, $pos->{y}, $realMonsterPos->{x}, $realMonsterPos->{y}), 'ai_attack'; + } else { + debug TF("[runFromTarget] (attackmaxDistance %s) %s kiteing from (%d %d) to (%d %d), mob at (%d %d).\n", $maxDistance, $char, $realMyPos->{x}, $realMyPos->{y}, $pos->{x}, $pos->{y}, $realMonsterPos->{x}, $realMonsterPos->{y}), 'ai_attack'; + } + $args->{avoiding} = 1; + $char->route( + undef, + @{$pos}{qw(x y)}, + noMapRoute => 1, + avoidWalls => 0, + randomFactor => 0, + useManhattan => 1, + runFromTarget => 1 + ); + return 1; + + } else { + if ($inAdvance) { + debug TF("[runFromTarget_inAdvance] %s no acceptable place to kite in advance from (%d %d), mob at (%d %d).\n", $char, $realMyPos->{x}, $realMyPos->{y}, $realMonsterPos->{x}, $realMonsterPos->{y}), 'ai_attack'; + } elsif ($noAttackMethodFallback_runFromTarget) { + debug TF("[runFromTarget_noAttackMethodFallback] %s no acceptable place to kite from (%d %d), mob at (%d %d).\n", $char, $realMyPos->{x}, $realMyPos->{y}, $realMonsterPos->{x}, $realMonsterPos->{y}), 'ai_attack'; + } else { + debug TF("[runFromTarget] %s no acceptable place to kite from (%d %d), mob at (%d %d).\n", $char, $realMyPos->{x}, $realMyPos->{y}, $realMonsterPos->{x}, $realMonsterPos->{y}), 'ai_attack'; + } + return 0; + } +} + +sub main { + my $args = AI::args; + + Benchmark::begin("ai_attack (part 1)") if DEBUG; + Benchmark::begin("ai_attack (part 1.1)") if DEBUG; + # The attack sequence hasn't timed out and the monster is on screen + + # Update information about the monster and the current situation + my $args = AI::args; + my $ID = $args->{ID}; + + if (!defined $ID) { + warning "[attack main] Bug where ID is undefined found.\n"; + warning "Args Dump: " . Dumper($args); + warning "ML Dump: " . Dumper(\@$monstersList); + warning "ai_seq Dump: " . Dumper(\@ai_seq); + warning "ai_seq_args Dump: " . Dumper(\@ai_seq_args); + Plugins::callHook('undefined_object_id'); + } + + my $target = Actor::get($ID); + + if (!exists $args->{temporary_extra_range} || !defined $args->{temporary_extra_range}) { + $args->{temporary_extra_range} = 0; + } + + if (exists $char->{movetoattack_pos}) { + if (!exists $char->{movetoattack_targetID} || $char->{movetoattack_targetID} ne $ID || $char->{time_move} > $char->{movetoattack_time}) { + delete $char->{movetoattack_pos}; + } else { + my $time_since_char_moved = time - $char->{time_move}; + if ($time_since_char_moved > $char->{time_move_calc}) { + debug "[Attack] [Char] Fixing failed to attack target, setting char position to: $char->{movetoattack_pos}{x} $char->{movetoattack_pos}{y}\n", "ai_attack"; + $char->{pos}{x} = $char->{movetoattack_pos}{x}; + $char->{pos}{y} = $char->{movetoattack_pos}{y}; + $char->{pos_to}{x} = $char->{movetoattack_pos}{x}; + $char->{pos_to}{y} = $char->{movetoattack_pos}{y}; + $char->{time_move} = time; + $char->{time_move_calc} = 0; + delete $char->{movetoattack_pos}; + } + } + } + + if (exists $target->{movetoattack_pos}) { + if ($target->{time_move} > $target->{movetoattack_time}) { + delete $target->{movetoattack_pos}; + } else { + my $target_solution = get_solution($field, $target->{pos}, $target->{pos_to}); + my $target_time_to_move = calcTimeFromSolution($target_solution, $target->{walk_speed}); + my $time_since_target_moved = time - $target->{time_move}; + if ($time_since_target_moved > $target_time_to_move) { + debug "[Attack] [Target] Fixing failed to attack target, setting target $target position to: $target->{movetoattack_pos}{x} $target->{movetoattack_pos}{y}\n", "ai_attack"; + $target->{pos}{x} = $target->{movetoattack_pos}{x}; + $target->{pos}{y} = $target->{movetoattack_pos}{y}; + $target->{pos_to}{x} = $target->{movetoattack_pos}{x}; + $target->{pos_to}{y} = $target->{movetoattack_pos}{y}; + $target->{time_move} = time; + $target->{time_move_calc} = 0; + delete $target->{movetoattack_pos}; + } + } + } + + my $myPos = $char->{pos_to}; + my $monsterPos = $target->{pos_to}; + my $monsterDist = blockDistance($myPos, $monsterPos); + + my $realMyPos = calcPosFromPathfinding($field, $char); + my $realMonsterPos = calcPosFromPathfinding($field, $target); + + my $realMonsterDist = blockDistance($realMyPos, $realMonsterPos); + my $clientDist = getClientDist($realMyPos, $realMonsterPos); + + + # If the damage numbers have changed, update the giveup time so we don't timeout + if ($args->{dmgToYou_last} != $target->{dmgToYou} + || $args->{missedYou_last} != $target->{missedYou} + || $args->{dmgFromYou_last} != $target->{dmgFromYou} + || $args->{lastSkillTime} != $char->{last_skill_time}) { + $args->{ai_attack_giveup}{time} = time; + debug "Update attack giveup time\n", "ai_attack", 2; + } + + if (!exists $args->{firstLoop}) { + $args->{firstLoop} = 1; + } else { + $args->{firstLoop} = 0; + } + + my $hitYou = ($args->{dmgToYou_last} != $target->{dmgToYou} || $args->{missedYou_last} != $target->{missedYou}); + my $youHitTarget = ($args->{dmgFromYou_last} != $target->{dmgFromYou} || $args->{missedFromYou_last} != $target->{missedFromYou}); + + $args->{dmgToYou_last} = $target->{dmgToYou}; + $args->{missedYou_last} = $target->{missedYou}; + $args->{dmgFromYou_last} = $target->{dmgFromYou}; + $args->{missedFromYou_last} = $target->{missedFromYou}; + + $args->{lastSkillTime} = $char->{last_skill_time}; + + Benchmark::end("ai_attack (part 1.1)") if DEBUG; + Benchmark::begin("ai_attack (part 1.2)") if DEBUG; + + # Determine what combo skill to use + delete $args->{attackMethod}; + + my $i = 0; + while (exists $config{"attackComboSlot_$i"}) { + next unless (defined $config{"attackComboSlot_$i"}); + + next unless (checkSelfCondition("attackComboSlot_$i")); + next unless ($config{"attackComboSlot_${i}_afterSkill"}); + next unless (Skill->new(auto => $config{"attackComboSlot_${i}_afterSkill"})->getIDN == $char->{last_skill_used}); + next unless (( !$config{"attackComboSlot_${i}_maxUses"} || $args->{attackComboSlot_uses}{$i} < $config{"attackComboSlot_${i}_maxUses"} )); + next unless (( !$config{"attackComboSlot_${i}_autoCombo"} || ($char->{combo_packet} && $config{"attackComboSlot_${i}_autoCombo"}) )); + next unless (( !defined($args->{ID}) || $args->{ID} eq $char->{last_skill_target} || !$config{"attackComboSlot_${i}_isSelfSkill"})); + next unless ((!$config{"attackComboSlot_${i}_monsters"} || existsInList($config{"attackComboSlot_${i}_monsters"}, $target->{name}) || existsInList($config{"attackComboSlot_${i}_monsters"}, $target->{nameID}))); + next unless ((!$config{"attackComboSlot_${i}_notMonsters"} || !(existsInList($config{"attackComboSlot_${i}_notMonsters"}, $target->{name}) || existsInList($config{"attackComboSlot_${i}_notMonsters"}, $target->{nameID})))); + next unless (checkMonsterCondition("attackComboSlot_${i}_target", $target)); + + $args->{attackComboSlot_uses}{$i}++; + delete $char->{last_skill_used}; + if ($config{"attackComboSlot_${i}_autoCombo"}) { + $char->{combo_packet} = 1500 if ($char->{combo_packet} > 1500); + # eAthena seems to have a bug where the combo_packet overflows and gives an + # abnormally high number. This causes kore to get stuck in a waitBeforeUse timeout. + $config{"attackComboSlot_${i}_waitBeforeUse"} = ($char->{combo_packet} / 1000); + } + delete $char->{combo_packet}; + $args->{attackMethod}{type} = "combo"; + $args->{attackMethod}{comboSlot} = $i; + $args->{attackMethod}{distance} = $config{"attackComboSlot_${i}_dist"}; + $args->{attackMethod}{maxDistance} = $config{"attackComboSlot_${i}_maxDist"} || $config{"attackComboSlot_${i}_dist"}; + last; + } continue { + $i++; + } + + # Determine what skill to use to attack + if (!$args->{attackMethod}{type}) { + if ($config{'attackUseWeapon'}) { + $args->{attackMethod}{type} = "weapon"; + $args->{attackMethod}{distance} = $config{'attackDistance'}; + $args->{attackMethod}{maxDistance} = $config{'attackMaxDistance'}; + } else { + undef $args->{attackMethod}{type}; + $args->{attackMethod}{distance} = 1; + $args->{attackMethod}{maxDistance} = 1; + } + + $i = 0; + while (exists $config{"attackSkillSlot_$i"}) { + next unless (defined $config{"attackSkillSlot_$i"}); + + my $skill = new Skill(auto => $config{"attackSkillSlot_$i"}); + next unless ($skill); + next unless ($skill->getOwnerType == Skill::OWNER_CHAR); + + my $handle = $skill->getHandle(); + + next unless (checkSelfCondition("attackSkillSlot_$i")); + next unless ((!$config{"attackSkillSlot_$i"."_maxUses"} || $target->{skillUses}{$handle} < $config{"attackSkillSlot_$i"."_maxUses"})); + next unless ((!$config{"attackSkillSlot_$i"."_maxAttempts"} || $args->{attackSkillSlot_attempts}{$i} < $config{"attackSkillSlot_$i"."_maxAttempts"})); + next unless ((!$config{"attackSkillSlot_$i"."_monsters"} || existsInList($config{"attackSkillSlot_$i"."_monsters"}, $target->{'name'}) || existsInList($config{"attackSkillSlot_$i"."_monsters"}, $target->{nameID}))); + next unless ((!$config{"attackSkillSlot_$i"."_notMonsters"} || !(existsInList($config{"attackSkillSlot_$i"."_notMonsters"}, $target->{'name'}) ||existsInList($config{"attackSkillSlot_$i"."_notMonsters"}, $target->{nameID})))); + next unless ((!$config{"attackSkillSlot_$i"."_previousDamage"} || inRange($target->{dmgTo}, $config{"attackSkillSlot_$i"."_previousDamage"}))); + next unless (checkMonsterCondition("attackSkillSlot_${i}_target", $target)); + + $args->{attackMethod}{type} = "skill"; + $args->{attackMethod}{skillSlot} = $i; + $args->{attackMethod}{distance} = $config{"attackSkillSlot_$i"."_dist"}; + $args->{attackMethod}{maxDistance} = $config{"attackSkillSlot_$i"."_maxDist"} || $config{"attackSkillSlot_$i"."_dist"}; + last; + } continue { + $i++; + } + } + + $args->{attackMethod}{maxDistance} ||= $config{attackMaxDistance}; + $args->{attackMethod}{distance} ||= $config{attackDistance}; + + if ($args->{attackMethod}{maxDistance} < $args->{attackMethod}{distance}) { + $args->{attackMethod}{maxDistance} = $args->{attackMethod}{distance}; + } + + Benchmark::end("ai_attack (part 1.2)") if DEBUG; + Benchmark::end("ai_attack (part 1)") if DEBUG; + + if (defined $args->{attackMethod}{type} && exists $args->{ai_attack_failed_give_up} && defined $args->{ai_attack_failed_give_up}{time}) { + debug "Deleting ai_attack_failed_give_up time.\n"; + delete $args->{ai_attack_failed_give_up}{time}; + + } + + $args->{attackMethod}{maxDistance} += $args->{temporary_extra_range}; + + # -2: undefined attackMethod + # -1: No LOS + # 0: out of range + # 1: sucess + my $canAttack; + if (defined $args->{attackMethod}{type} && defined $args->{attackMethod}{maxDistance}) { + $canAttack = canAttack($field, $realMyPos, $realMonsterPos, $config{attackCanSnipe}, $args->{attackMethod}{maxDistance}, $config{clientSight}); + } else { + $canAttack = -2; + } + + my $canAttack_fail_string = (($canAttack == -2) ? "No Method" : (($canAttack == -1) ? "No LOS" : (($canAttack == 0) ? "No Range" : "OK"))); + + # Here we check if the monster which we are waiting to get closer to us is in fact close enough + # If it is close enough delete the ai_attack_failed_waitForAgressive_give_up keys and loop attack logic + if ( + $config{"attackBeyondMaxDistance_waitForAgressive"} && + $target->{dmgFromYou} > 0 && + $canAttack == 1 && + exists $args->{ai_attack_failed_waitForAgressive_give_up} && + defined $args->{ai_attack_failed_waitForAgressive_give_up}{time} + ) { + debug "Deleting ai_attack_failed_waitForAgressive_give_up time.\n"; + delete $args->{ai_attack_failed_waitForAgressive_give_up}{time}; + } + + # Here we check if we have finished moving to the meeting position to attack our target, only checks this if attackWaitApproachFinish is set to 1 in config + # If so sets sentApproach to 0 + if ($args->{sentApproach}) { + if ($config{"attackWaitApproachFinish"}) { + if (!timeOut($char->{time_move}, $char->{time_move_calc})) { + debug TF("[attackWaitApproachFinish - Waiting] %s (%d %d), target %s (%d %d), distance %d, maxDistance %d, dmgFromYou %d.\n", $char, $realMyPos->{x}, $realMyPos->{y}, $target, $realMonsterPos->{x}, $realMonsterPos->{y}, $realMonsterDist, $args->{attackMethod}{maxDistance}, $target->{dmgFromYou}), 'ai_attack'; + return; + } else { + debug TF("[attackWaitApproachFinish - Ended Approaching] %s (%d %d), target %s (%d %d), distance %d, maxDistance %d, dmgFromYou %d.\n", $char, $realMyPos->{x}, $realMyPos->{y}, $target, $realMonsterPos->{x}, $realMonsterPos->{y}, $realMonsterDist, $args->{attackMethod}{maxDistance}, $target->{dmgFromYou}), 'ai_attack'; + $args->{sentApproach} = 0; + } + } else { + if ($canAttack == 2) { + debug TF("[Approaching - Can now attack] %s (%d %d), target %s (%d %d), distance %d, maxDistance %d, dmgFromYou %d.\n", $char, $realMyPos->{x}, $realMyPos->{y}, $target, $realMonsterPos->{x}, $realMonsterPos->{y}, $realMonsterDist, $args->{attackMethod}{maxDistance}, $target->{dmgFromYou}), 'ai_attack'; + $args->{sentApproach} = 0; + } elsif (timeOut($char->{time_move}, $char->{time_move_calc})) { + debug TF("[Approaching - Ended] Still no LOS/Range - %s (%d %d), target %s (%d %d), distance %d, maxDistance %d, dmgFromYou %d.\n", $char, $realMyPos->{x}, $realMyPos->{y}, $target, $realMonsterPos->{x}, $realMonsterPos->{y}, $realMonsterDist, $args->{attackMethod}{maxDistance}, $target->{dmgFromYou}), 'ai_attack'; + $args->{sentApproach} = 0; + } + } + } + + my $found_action = 0; + my $failed_runFromTarget = 0; + my $hitTarget_when_not_possible = 0; + + # Here, if runFromTarget is active, we check if the target mob is closer to us than the minimun distance specified in runFromTarget_dist + # If so try to kite it + if ( + !$found_action && + $config{"runFromTarget"} && + $realMonsterDist < $config{"runFromTarget_dist"} + ) { + my $try_runFromTarget = find_kite_position($args, 0, $target, $realMyPos, $realMonsterPos, 0); + if ($try_runFromTarget) { + $found_action = 1; + } else { + $failed_runFromTarget = 1; + } + } + + # Here, if runFromTarget is active, and we can't attack right now (eg. all skills in cooldown) we check if the target mob is closer to us than the minimun distance specified in runFromTarget_noAttackMethodFallback_minStep + # If so try to kite it using maxdistance of runFromTarget_noAttackMethodFallback_attackMaxDist + if ( + !$found_action && + $canAttack == -2 && + #$config{"runFromTarget"} && + $config{'runFromTarget_noAttackMethodFallback'} && + $realMonsterDist < $config{'runFromTarget_noAttackMethodFallback_minStep'} + ) { + my $try_runFromTarget = find_kite_position($args, 0, $target, $realMyPos, $realMonsterPos, 1); + if ($try_runFromTarget) { + $found_action = 1; + } + } + + if ( + !$found_action && + $canAttack == -2 + ) { + debug T("Can't determine a attackMethod (check attackUseWeapon and Skills blocks)\n"), "ai_attack"; + $args->{ai_attack_failed_give_up}{timeout} = 6 if !$args->{ai_attack_failed_give_up}{timeout}; + $args->{ai_attack_failed_give_up}{time} = time if !$args->{ai_attack_failed_give_up}{time}; + if (timeOut($args->{ai_attack_failed_give_up})) { + delete $args->{ai_attack_failed_give_up}{time}; + warning T("Unable to determine a attackMethod (check attackUseWeapon and Skills blocks), dropping target.\n"), "ai_attack"; + $found_action = 1; + giveUp($args, $ID, 0); + } + } + + if (!$args->{firstLoop} && $canAttack == 0 && $youHitTarget) { + debug TF("[%s] We were able to hit target even though it is out of range or LOS, accepting and continuing. (you %s (%d %d), target %s (%d %d) [(%d %d) -> (%d %d)], distance %d, maxDistance %d, dmgFromYou %d)\n", $canAttack_fail_string, $char, $realMyPos->{x}, $realMyPos->{y}, $target, $realMonsterPos->{x}, $realMonsterPos->{y}, $target->{pos}{x}, $target->{pos}{y}, $target->{pos_to}{x}, $target->{pos_to}{y}, $realMonsterDist, $args->{attackMethod}{maxDistance}, $target->{dmgFromYou}), 'ai_attack'; + if ($clientDist > $args->{attackMethod}{maxDistance} && $clientDist <= ($args->{attackMethod}{maxDistance} + 1) && $args->{temporary_extra_range} == 0) { + debug TF("[%s] Probably extra range provided by the server due to chasing, increasing range by 1.\n", $canAttack_fail_string), 'ai_attack'; + $args->{temporary_extra_range} = 1; + $args->{attackMethod}{maxDistance} += $args->{temporary_extra_range}; + $canAttack = canAttack($field, $realMyPos, $realMonsterPos, $config{attackCanSnipe}, $args->{attackMethod}{maxDistance}, $config{clientSight}); + } else { + debug TF("[%s] Reason unknown, allowing once.\n", $canAttack_fail_string), 'ai_attack'; + $hitTarget_when_not_possible = 1; + } + if ( + $config{"attackBeyondMaxDistance_waitForAgressive"} && + exists $args->{ai_attack_failed_waitForAgressive_give_up} && + defined $args->{ai_attack_failed_waitForAgressive_give_up}{time} + ) { + debug "[Accepting] Deleting ai_attack_failed_waitForAgressive_give_up time.\n"; + delete $args->{ai_attack_failed_waitForAgressive_give_up}{time};; + } + } + + # Here we decide what to do when a mob we have already hit is no longer in range or we have no LOS to it + # We also check if we have waited too long for the monster which we are waiting to get closer to us to approach + # TODO: Maybe we should separate this into 2 sections, one for out of range and another for no LOS - low priority + if ( + !$found_action && + $config{"attackBeyondMaxDistance_waitForAgressive"} && + $target->{dmgFromYou} > 0 && + ($canAttack == 0 || $canAttack == -1) && + !$hitTarget_when_not_possible + ) { + $args->{ai_attack_failed_waitForAgressive_give_up}{timeout} = 6 if !$args->{ai_attack_failed_waitForAgressive_give_up}{timeout}; + $args->{ai_attack_failed_waitForAgressive_give_up}{time} = time if !$args->{ai_attack_failed_waitForAgressive_give_up}{time}; + if (timeOut($args->{ai_attack_failed_waitForAgressive_give_up})) { + delete $args->{ai_attack_failed_waitForAgressive_give_up}{time}; + warning TF("[%s] Waited too long for target to get closer, dropping target. (you %s (%d %d), target %s (%d %d) [(%d %d) -> (%d %d)], distance %d, maxDistance %d, dmgFromYou %d)\n", $canAttack_fail_string, $char, $realMyPos->{x}, $realMyPos->{y}, $target, $realMonsterPos->{x}, $realMonsterPos->{y}, $target->{pos}{x}, $target->{pos}{y}, $target->{pos_to}{x}, $target->{pos_to}{y}, $realMonsterDist, $args->{attackMethod}{maxDistance}, $target->{dmgFromYou}), 'ai_attack'; + giveUp($args, $ID, 0); + } else { + $messageSender->sendAction($ID, ($config{'tankMode'}) ? 0 : 7) if ($config{"attackBeyondMaxDistance_sendAttackWhileWaiting"}); + debug TF("[%s - Waiting] %s (%d %d), target %s (%d %d) [(%d %d) -> (%d %d)], distance %d, maxDistance %d, dmgFromYou %d.\n", $canAttack_fail_string, $char, $realMyPos->{x}, $realMyPos->{y}, $target, $realMonsterPos->{x}, $realMonsterPos->{y}, $target->{pos}{x}, $target->{pos}{y}, $target->{pos_to}{x}, $target->{pos_to}{y}, $realMonsterDist, $args->{attackMethod}{maxDistance}, $target->{dmgFromYou}), 'ai_attack'; + } + $found_action = 1; + } + + # Here we decide what to do with a mob which is out of range or we have no LOS to + if ( + !$found_action && + ($canAttack == 0 || $canAttack == -1) && + !$hitTarget_when_not_possible + ) { + debug "Attack $char ($realMyPos->{x} $realMyPos->{y}) - target $target ($realMonsterPos->{x} $realMonsterPos->{y})\n"; + if ($canAttack == 0) { + debug "[Attack] [No range] Too far from us to attack, distance is $realMonsterDist, attack maxDistance is $args->{attackMethod}{maxDistance}\n", 'ai_attack'; + + } elsif ($canAttack == -1) { + debug "[Attack] [No LOS] No LOS from player to mob\n", 'ai_attack'; + } + + my $pos = meetingPosition($char, 1, $target, $args->{attackMethod}{maxDistance}); + if ($pos) { + debug "Attack $char ($realMyPos->{x} $realMyPos->{y}) - moving to meeting position ($pos->{x} $pos->{y})\n", 'ai_attack'; + + $args->{move_start} = time; + $args->{monsterLastMoveTime} = $target->{time_move}; + $args->{sentApproach} = 1; + + my $sendAttackWithMove = 0; + if ($config{"attackSendAttackWithMove"} && $args->{attackMethod}{type} eq "weapon") { + $sendAttackWithMove = 1; + } + + $char->route( + undef, + @{$pos}{qw(x y)}, + maxRouteTime => $config{'attackMaxRouteTime'}, + attackID => $ID, + sendAttackWithMove => $sendAttackWithMove, + avoidWalls => 0, + randomFactor => 0, + useManhattan => 1, + meetingSubRoute => 1, + noMapRoute => 1 + ); + } else { + message T("Unable to calculate a meetingPosition to target, dropping target\n"), "ai_attack"; + giveUp($args, $ID, 1); + } + $found_action = 1; + } + + if ( + !$found_action && + (!$config{"runFromTarget"} || $realMonsterDist >= $config{"runFromTarget_dist"} || $failed_runFromTarget) && + (!$config{"tankMode"} || !$target->{dmgFromYou}) + ) { + # Attack the target. In case of tanking, only attack if it hasn't been hit once. + if (!$args->{firstAttack}) { + $args->{firstAttack} = 1; + debug "Ready to attack target $target ($realMonsterPos->{x} $realMonsterPos->{y}) ($realMonsterDist blocks away); we're at ($realMyPos->{x} $realMyPos->{y})\n", "ai_attack"; + } + + $args->{unstuck}{time} = time if (!$args->{unstuck}{time}); + if (!$target->{dmgFromYou} && timeOut($args->{unstuck})) { + # We are close enough to the target, and we're trying to attack it, + # but some time has passed and we still haven't dealed any damage. + # Our recorded position might be out of sync, so try to unstuck + $args->{unstuck}{time} = time; + debug("Attack - trying to unstuck\n", "ai_attack"); + $char->move(@{$myPos}{qw(x y)}); + $args->{unstuck}{count}++; + } + + # Attack with weapon logic + if ($args->{attackMethod}{type} eq "weapon" && timeOut($timeout{ai_attack}) && timeOut($timeout{ai_attack_after_skill})) { + if (Actor::Item::scanConfigAndCheck("attackEquip")) { + #check if item needs to be equipped + Actor::Item::scanConfigAndEquip("attackEquip"); + } else { + debug "[Attack] Sending attack target $target ($realMonsterPos->{x} $realMonsterPos->{y}) ($realMonsterDist blocks away); we're at ($realMyPos->{x} $realMyPos->{y})\n", "ai_attack"; + $messageSender->sendAction($ID, ($config{'tankMode'}) ? 0 : 7); + $timeout{ai_attack}{time} = time; + delete $args->{attackMethod}; + + if ($config{"runFromTarget"} && $config{"runFromTarget_inAdvance"} && $realMonsterDist < $config{"runFromTarget_minStep"}) { + find_kite_position($args, 1, $target, $realMyPos, $realMonsterPos, 0); + } + } + $found_action = 1; + + # Attack with skill logic + } elsif ($args->{attackMethod}{type} eq "skill") { + my $slot = $args->{attackMethod}{skillSlot}; + delete $args->{attackMethod}; + + $ai_v{"attackSkillSlot_${slot}_time"} = time; + $ai_v{"attackSkillSlot_${slot}_target_time"}{$ID} = time; + + $args->{attackSkillSlot_attempts}{$slot}++; + + ai_setSuspend(0); + my $skill = new Skill(auto => $config{"attackSkillSlot_$slot"}); + my $skill_lvl = $config{"attackSkillSlot_${slot}_lvl"} || $char->getSkillLevel($skill); + ai_skillUse2( + $skill, + $skill_lvl, + $config{"attackSkillSlot_${slot}_maxCastTime"}, + $config{"attackSkillSlot_${slot}_minCastTime"}, + $config{"attackSkillSlot_${slot}_isSelfSkill"} ? $char : $target, + "attackSkillSlot_${slot}", + undef, + "attackSkill", + $config{"attackSkillSlot_${slot}_isStartSkill"} ? 1 : 0, + ); + debug "[attackSkillSlot] Auto-skill on monster ".getActorName($ID).": ".qq~$config{"attackSkillSlot_$slot"} (lvl $skill_lvl)\n~, "ai_attack"; + # TODO: We sould probably add a runFromTarget_inAdvance logic here also, we could want to kite using skills, but only instant cast ones like double strafe I believe + + $timeout{ai_attack_after_skill}{time} = time; + + $args->{monsterID} = $ID; + $found_action = 1; + + # Attack with combo logic + } elsif ($args->{attackMethod}{type} eq "combo") { + my $slot = $args->{attackMethod}{comboSlot}; + delete $args->{attackMethod}; + + $ai_v{"attackComboSlot_${slot}_time"} = time; + $ai_v{"attackComboSlot_${slot}_target_time"}{$ID} = time; + + my $skill = Skill->new(auto => $config{"attackComboSlot_$slot"}); + my $skill_lvl = $config{"attackComboSlot_${slot}_lvl"} || $char->getSkillLevel($skill); + ai_skillUse2( + $skill, + $skill_lvl, + $config{"attackComboSlot_${slot}_maxCastTime"}, + $config{"attackComboSlot_${slot}_minCastTime"}, + $config{"attackComboSlot_${slot}_isSelfSkill"} ? $char : $target, + undef, + $config{"attackComboSlot_${slot}_waitBeforeUse"}, + ); + + $args->{monsterID} = $ID; + $found_action = 1; + } + + } + + if (!$found_action && $config{tankMode}) { + if ($args->{dmgTo_last} != $target->{dmgTo}) { + $args->{ai_attack_giveup}{time} = time; + $char->sendAttackStop; + } + $args->{dmgTo_last} = $target->{dmgTo}; + $found_action = 1; + } + + Plugins::callHook('AI::Attack::main', {target => $target}) +} + +1; diff --git a/openkore_llm_knowledge/core/src/AI/CoreLogic.pm b/openkore_llm_knowledge/core/src/AI/CoreLogic.pm new file mode 100644 index 0000000000..5adaa5cefb --- /dev/null +++ b/openkore_llm_knowledge/core/src/AI/CoreLogic.pm @@ -0,0 +1,4122 @@ +######################################################################### +# OpenKore - AI +# Copyright (c) OpenKore Team +# +# This software is open source, licensed under the GNU General Public +# License, version 2. +# Basically, this means that you're allowed to modify and distribute +# this software. However, if you distribute modified versions, you MUST +# also distribute the source code. +# See http://www.gnu.org/licenses/gpl.html for the full license. +# +# $Revision: 4286 $ +# $Id: Commands.pm 4286 2006-04-17 14:02:27Z illusion_kore $ +# +######################################################################### +# +# This module contains the core AI logic. + +package AI::CoreLogic; +use strict; +use Time::HiRes qw(time); +use Carp::Assert; +use IO::Socket; +use Text::ParseWords; +use utf8; + +use Globals; +use Log qw(message warning error debug); +use Misc; +use Network::Send (); +use Settings; +use AI; +use AI::SlaveManager; +use ChatQueue; +use Utils; +use Commands; +use Network; +use FileParsers; +use Translation; +use Field; +use Task::TalkNPC; +use Task::UseSkill; +use Task::ErrorReport; +use Utils::Exceptions; + +# This is the main function from which the rest of the AI +# will be invoked. +sub iterate { + Benchmark::begin("ai_prepare") if DEBUG; + processWipeOldActors(); + processActorAvoid(); + processGetPlayerInfo(); + processMisc(); + processReAddMissingPortals(); + processPortalRecording(); + Benchmark::end("ai_prepare") if DEBUG; + + Plugins::callHook('AI_start', {state => AI::state}); + + return if AI::state == AI::OFF; + if ($net->clientAlive() && !$sentWelcomeMessage && timeOut($timeout{welcomeText})) { + $messageSender->injectAdminMessage($Settings::welcomeText) if ($config{'verbose'} && !$config{'XKore_silent'}); + $sentWelcomeMessage = 1; + } + + + ##### MANUAL AI STARTS HERE ##### + + Plugins::callHook('AI_pre/manual'); + Benchmark::begin("AI (part 1)") if DEBUG; + return if processClientSuspend(); + Benchmark::begin("AI (part 1.1)") if DEBUG; + processLook(); + $char->processTask('NPC'); + processEquip(); + processDrop(); + processEscapeUnknownMaps(); + Benchmark::end("AI (part 1.1)") if DEBUG; + Benchmark::begin("AI (part 1.2)") if DEBUG; + $char->processTask("sitting"); + $char->processTask("standing"); + $char->processTask("teleport"); + AI::Attack::process(); + Benchmark::end("AI (part 1.2)") if DEBUG; + Benchmark::begin("AI (part 1.3)") if DEBUG; + processSkillUse(); + processAutoCommandUse(); + processAutoAttack() if AI::state == AI::AUTO; + $char->processTask("route", onError => sub { + my ($task, $error) = @_; + if (!($task->isa('Task::MapRoute') && $error->{code} == Task::MapRoute::TOO_MUCH_TIME()) + && !($task->isa('Task::Route') && $error->{code} == Task::Route::TOO_MUCH_TIME())) { + error("$error->{message}\n"); + } + }); + processTake(); + $char->processTask('move'); + Benchmark::end("AI (part 1.3)") if DEBUG; + + Benchmark::begin("AI (part 1.4)") if DEBUG; + Benchmark::begin("ai_autoItemUse") if DEBUG; + processAutoItemUse(); + Benchmark::end("ai_autoItemUse") if DEBUG; + Benchmark::begin("ai_autoSkillUse") if DEBUG; + processAutoSkillUse(); + Benchmark::end("ai_autoSkillUse") if DEBUG; + processFeed(); + Benchmark::end("AI (part 1.4)") if DEBUG; + + Benchmark::end("AI (part 1)") if DEBUG; + + + + Misc::checkValidity("AI part 1"); + return unless AI::state == AI::AUTO; + + + ##### AUTOMATIC AI STARTS HERE ##### + + Plugins::callHook('AI_pre'); + Benchmark::begin("AI (part 2)") if DEBUG; + + ChatQueue::processFirst; + + processDcOnPlayer(); + processDeal(); + processDealAuto(); + processPartyAuto(); + processPartyShareAuto(); + processGuildAutoDeny(); + + Misc::checkValidity("AI part 1.1"); + #processAutoBreakTime(); moved to a plugin + processDead(); + processTransferItems(); + processCartAdd(); + processCartGet(); + processAutoMakeArrow(); + Benchmark::end("AI (part 2)") if DEBUG; + Misc::checkValidity("AI part 2"); + + + Benchmark::begin("AI (part 3)") if DEBUG; + Benchmark::begin("AI (part 3.1)") if DEBUG; + processAutoStorage(); + Misc::checkValidity("AI (autostorage)"); + processAutoSell(); + Misc::checkValidity("AI (autosell)"); + processAutoBuy(); + Misc::checkValidity("AI (autobuy)"); + processAutoCart(); + Misc::checkValidity("AI (autocart)"); + Benchmark::end("AI (part 3.1)") if DEBUG; + + Benchmark::begin("AI (part 3.2)") if DEBUG; + processLockMap(); + processRescueSlave(); + processRandomWalk_stopDuringSlaveAttack(); + processMoveNearSlave(); + processRandomWalk(); + processFollow(); + Benchmark::end("AI (part 3.2)") if DEBUG; + + Benchmark::begin("AI (part 3.3)") if DEBUG; + processSitAutoIdle(); + processSitAuto(); + + + Benchmark::end("AI (part 3.3)") if DEBUG; + Benchmark::end("AI (part 3)") if DEBUG; + + Benchmark::begin("AI (part 4)") if DEBUG; + processPartySkillUse(); + processMonsterSkillUse(); + + Misc::checkValidity("AI part 3"); + processAutoEquip(); + processAutoAttack(); + processItemsTake(); + processItemsAutoGather(); + processItemsGather(); + processAutoTeleport(); + processAllowedMaps(); + processAutoResponse(); + processAvoid(); + processSendEmotion(); + processAutoShopOpen(); + processAutoBuyerShopOpen(); + processRepairAuto(); + processSendIgnoreAll(); + Benchmark::end("AI (part 4)") if DEBUG; + + + ########## + + # DEBUG CODE + if (timeOut($ai_v{time}, 2) && $config{'debug'} >= 2) { + my $len = @ai_seq_args; + debug "AI: @ai_seq | $len\n", "ai", 2; + $ai_v{time} = time; + } + $ai_v{'AI_last_finished'} = time; + + if ($cmdQueue && timeOut($cmdQueueStartTime,$cmdQueueTime)) { + my $execCommand = ''; + if (@cmdQueueList) { + $execCommand = join (";;", @cmdQueueList); + } else { + $execCommand = $cmdQueueList[0]; + } + @cmdQueueList = (); + $cmdQueue = 0; + $cmdQueueTime = 0; + debug "Executing queued command: $execCommand\n", "ai"; + Commands::run($execCommand); + } + + + Plugins::callHook('AI_post'); +} + + +############################################################# + + +# Wipe old entries in the %actor_old hashes. +sub processWipeOldActors { + if (timeOut($timeout{ai_wipe_check})) { + my $timeout = $timeout{ai_wipe_old}{timeout}; + + foreach (keys %players_old) { + if (timeOut($players_old{$_}{'gone_time'}, $timeout)) { + delete $players_old{$_}; + binRemove(\@playersID_old, $_); + } + } + foreach (keys %monsters_old) { + if (timeOut($monsters_old{$_}{'gone_time'}, $timeout)) { + delete $monsters_old{$_}; + binRemove(\@monstersID_old, $_); + } + } + foreach (keys %npcs_old) { + delete $npcs_old{$_} if (time - $npcs_old{$_}{'gone_time'} >= $timeout{'ai_wipe_old'}{'timeout'}); + } + foreach (keys %items_old) { + delete $items_old{$_} if (time - $items_old{$_}{'gone_time'} >= $timeout{'ai_wipe_old'}{'timeout'}); + } + foreach (keys %portals_old) { + if (timeOut($portals_old{$_}{gone_time}, $timeout)) { + delete $portals_old{$_}; + binRemove(\@portalsID_old, $_); + } + } + + # Remove players that are too far away; sometimes they don't get + # removed from the list for some reason + #foreach (keys %players) { + # if (distance($char->{pos_to}, $players{$_}{pos_to}) > 35) { + # $playersList->remove($players{$_}); + # last; + # } + #} + + $timeout{'ai_wipe_check'}{'time'} = time; + debug "Wiped old\n", "ai", 2; + } +} + +sub processActorAvoid { + my $realMyPos = calcPosFromPathfinding($field, $char); + my $max_dist = $config{clientSight} + 1; + my $max_to_delete = $max_dist*2; + + if (timeOut($timeout{'avoidDistantActors'}{'time'}, 1)) { + $timeout{'avoidDistantActors'}{'time'} = time; + foreach my $list ($playersList, $monstersList, $npcsList, $petsList, $portalsList, $slavesList, $elementalsList) { + for my $actor (@$list) { + my $realActorPos = calcPosition($actor); + my $realActorDist = blockDistance($realMyPos, $realActorPos); + + if ($realActorDist > $max_to_delete) { + warning TF("Removing way out of sight actor %s at (%d, %d) (distance: %d > max %d)\n", $actor, $actor->{pos_to}{x}, $actor->{pos_to}{y}, $realActorDist, $max_to_delete); + Plugins::callHook('actor_avoid_removal', {actor => $actor}); + $list->remove($actor); + + } elsif ($realActorDist > $max_dist) { + if ($actor->{avoid} == 0) { + warning TF("Avoiding out of sight actor %s at (%d, %d) (distance: %d > max %d)\n", $actor, $actor->{pos_to}{x}, $actor->{pos_to}{y}, $realActorDist, $max_dist); + } + $actor->{avoid} = 1; + + } else { + if ($actor->{avoid} == 1) { + warning TF("Stopped avoiding now in bounds actor %s at (%d, %d) (distance: %d <= max %d)\n", $actor, $actor->{pos_to}{x}, $actor->{pos_to}{y}, $realActorDist, $max_dist); + } + $actor->{avoid} = 0; + } + } + } + } +} + +sub processGetPlayerInfo { + if (timeOut($timeout{ai_getInfo})) { + processNameRequestQueue(\@unknownPlayers, [$playersList, $slavesList]); + processNameRequestQueue(\@unknownNPCs, [$npcsList]); + + foreach (keys %monsters) { + last if (isSafeActorQuery($_) != 1); # Do not Query GM hidden Monster names + if ($monsters{$_}{'name'} =~ /Unknown/) { + $messageSender->sendGetPlayerInfo($_); + last; + } + if ($monsters{$_}{'name_given'} =~ /Unknown/) { + $messageSender->sendGetPlayerInfo($_); + last; + } + } + foreach (keys %pets) { + if ($pets{$_}{'name_given'} =~ /Unknown/) { + last if (isSafeActorQuery($_) != 1); # Do not Query GM hidden Pet names + $messageSender->sendGetPlayerInfo($_); + last; + } + } + $timeout{ai_getInfo}{time} = time; + } +} + +sub processMisc { + if (timeOut($timeout{ai_sync})) { + $timeout{ai_sync}{time} = time; + $messageSender->sendSync(); + } + + if (timeOut($char->{muted}, $char->{mute_period})) { + delete $char->{muted}; + delete $char->{mute_period}; + + if ($char->statusActive('EFST_MUTED')) { + $char->setStatus('EFST_MUTED', 0); + } + } +} + +##### CLIENT SUSPEND ##### +# The clientSuspend AI sequence is used to freeze all other AI activity +# for a certain period of time. +sub processClientSuspend { + my $result = 0; + if (AI::action eq 'clientSuspend' && timeOut(AI::args)) { + debug "AI suspend by clientSuspend dequeued\n"; + AI::dequeue; + } elsif (AI::action eq "clientSuspend" && $net->clientAlive()) { + # When XKore mode is turned on, clientSuspend will increase it's timeout + # every time the user tries to do something manually. + my $args = AI::args; + + if ($args->{'type'} eq "0089") { + # Player's manually attacking + if ($args->{'args'}[0] == 2) { + if ($chars[$config{'char'}]{'sitting'}) { + $args->{'time'} = time; + } + } elsif ($args->{'args'}[0] == 3) { + $args->{'timeout'} = 6; + } else { + my $ID = $args->{args}[1]; + my $monster = $monstersList->getByID($ID); + + if (!$args->{'forceGiveup'}{'timeout'}) { + $args->{'forceGiveup'}{'timeout'} = 6; + $args->{'forceGiveup'}{'time'} = time; + } + if ($monster) { + $args->{time} = time; + $args->{dmgFromYou_last} = $monster->{dmgFromYou}; + $args->{missedFromYou_last} = $monster->{missedFromYou}; + if ($args->{dmgFromYou_last} != $monster->{dmgFromYou}) { + $args->{forceGiveup}{time} = time; + } + } else { + $args->{time} -= $args->{'timeout'}; + } + if (timeOut($args->{forceGiveup})) { + $args->{time} -= $args->{timeout}; + } + } + + } elsif ($args->{'type'} eq "009F") { + # Player's manually picking up an item + if (!$args->{'forceGiveup'}{'timeout'}) { + $args->{'forceGiveup'}{'timeout'} = 4; + $args->{'forceGiveup'}{'time'} = time; + } + if ($items{$args->{'args'}[0]}) { + $args->{'time'} = time; + } else { + $args->{'time'} -= $args->{'timeout'}; + } + if (timeOut($args->{'forceGiveup'})) { + $args->{'time'} -= $args->{'timeout'}; + } + } + + # Client suspended, do not continue with AI + $result = 1; + } elsif (AI::action eq "clientSuspend") { # Should be called only if AI Suspend itself. (this mode used in AI::CoreLogic::processItemsTake and AI::CoreLogic::processAllowedMaps) + # Client suspended, do not continue with AI + $result = 1; + } + return $result; +} + +sub processLook { + if ($ai_v{sitAuto_scheduledLook} && time >= $ai_v{sitAuto_scheduledLook}{due}) { + my $scheduledLook = delete $ai_v{sitAuto_scheduledLook}; + Misc::look($scheduledLook->{body}, $scheduledLook->{head}); + } + + if (AI::action eq "look" && timeOut($timeout{'ai_look'})) { + $timeout{'ai_look'}{'time'} = time; + $messageSender->sendLook(AI::args->{'look_body'}, AI::args->{'look_head'}); + AI::dequeue; + } +} + +=pod +##### TALK WITH NPC ###### +sub processNPCTalk { + return if (AI::action ne "NPC"); + my $args = AI::args; + my $task = $args->{task}; + if (!$task) { + $task = new Task::TalkNPC(x => $args->{pos}{x}, + y => $args->{pos}{y}, + sequence => $args->{sequence}); + $task->activate(); + $args->{task} = $task; + } else { + $task->iterate(); + if ($task->getStatus() == Task::DONE) { + AI::dequeue; + my $error = $task->getError(); + if ($error) { + error("$error->{message}\n", "ai_npcTalk"); + } else { + message TF("Done talking with %s.\n", $task->target()->name), "ai_npcTalk"; + } + } + } +} +=cut + +##### DROPPING ##### +# Drop one or more items from inventory. +sub processDrop { + if (AI::action eq "drop" && timeOut(AI::args)) { + my $item = AI::args->{'items'}[0]; + my $amount = AI::args->{max}; + + drop($item, $amount); + shift @{AI::args->{items}}; + AI::args->{time} = time; + AI::dequeue if (@{AI::args->{'items'}} <= 0); + } +} + +##### PORTALREADD ##### +# Automatically adds the last missing portals to portals_lut +sub processReAddMissingPortals { + return unless ($config{route_reAddMissingPortals}); + return unless (@portals_lut_missed); + return unless (timeOut($portals_lut_missed[0]{time}, $timeout{ai_portal_re_add_missed}{timeout})); + my $portal = shift(@portals_lut_missed); + $portals_lut{$portal->{name}} = $portal->{portal}; + debug "Re adding portal '".$portal->{name}."' to portals list.\n", "portalReAdd"; +} + +##### PORTALRECORD ##### +# Automatically record new unknown portals +sub processPortalRecording { + return unless $config{portalRecord}; + return unless $ai_v{portalTrace_mapChanged} && timeOut($ai_v{portalTrace_mapChanged}, 0.5); + delete $ai_v{portalTrace_mapChanged}; + + debug "Checking for new portals...\n", "portalRecord"; + my $first = 1; + my ($foundID, $smallDist, $dist); + + if (!$field->baseName) { + debug "Field name not known - abort\n", "portalRecord"; + return; + } + + + # Find the nearest portal or the only portal on the map + # you came from (source portal) + foreach (@portalsID_old) { + next if (!$_); + $dist = distance($char->{old_pos_to}, $portals_old{$_}{pos}); + if ($dist <= 7 && ($first || $dist < $smallDist)) { + $smallDist = $dist; + $foundID = $_; + undef $first; + } + } + + my ($sourceMap, $sourceID, %sourcePos, $sourceIndex); + if (defined $foundID) { + $sourceMap = $portals_old{$foundID}{source}{map}; + $sourceID = $portals_old{$foundID}{nameID}; + %sourcePos = %{$portals_old{$foundID}{pos}}; + $sourceIndex = $foundID; + debug "Source portal: $sourceMap ($sourcePos{x}, $sourcePos{y})\n", "portalRecord"; + } else { + debug "No source portal found.\n", "portalRecord"; + return; + } + + #if (defined portalExists($sourceMap, \%sourcePos)) { + # debug "Source portal is already in portals.txt - abort\n", "portalRecord"; + # return; + #} + + + # Find the nearest portal or only portal on the + # current map (destination portal) + $first = 1; + undef $foundID; + undef $smallDist; + + foreach (@portalsID) { + next if (!$_); + $dist = distance($chars[$config{'char'}]{pos_to}, $portals{$_}{pos}); + if ($first || $dist < $smallDist) { + $smallDist = $dist; + $foundID = $_; + undef $first; + } + } + + # Sanity checks + if (!defined $foundID) { + debug "No destination portal found.\n", "portalRecord"; + return; + } + #if (defined portalExists($field->baseName, $portals{$foundID}{pos})) { + # debug "Destination portal is already in portals.txt\n", "portalRecord"; + # last PORTALRECORD; + #} + if (defined portalExists2($sourceMap, \%sourcePos, $field->baseName, $portals{$foundID}{pos})) { + debug "This portal is already in portals.txt\n", "portalRecord"; + return; + } + + if (defined portalExistsAirship($sourceMap, \%sourcePos)) { + debug "This portal is already in portals_airships.txt\n", "portalRecord"; + return; + } + + + # And finally, record the portal information + my ($destMap, $destID, %destPos); + $destMap = $field->baseName; + $destID = $portals{$foundID}{nameID}; + %destPos = %{$portals{$foundID}{pos}}; + debug "Destination portal: $destMap ($destPos{x}, $destPos{y})\n", "portalRecord"; + + $portals{$foundID}{name} = "$destMap -> $sourceMap"; + $portals_old{$sourceIndex}{name} = "$sourceMap -> $destMap"; + + + my ($ID, $destName); + my $recorded = 0; + + # Record information about destination portal + if ($config{portalRecord} > 1 && + !defined portalExists($destMap, $portals{$foundID}{pos})) { + $ID = "$destMap $destPos{x} $destPos{y}"; + $portals_lut{$ID}{source}{map} = $destMap; + $portals_lut{$ID}{source}{x} = $destPos{x}; + $portals_lut{$ID}{source}{y} = $destPos{y}; + $destName = "$sourceMap $sourcePos{x} $sourcePos{y}"; + $portals_lut{$ID}{dest}{$destName}{map} = $sourceMap; + $portals_lut{$ID}{dest}{$destName}{x} = $sourcePos{x}; + $portals_lut{$ID}{dest}{$destName}{y} = $sourcePos{y}; + + message TF("Recorded new portal (destination): %s (%s, %s) -> %s (%s, %s)\n", $destMap, $destPos{x}, $destPos{y}, $sourceMap, $sourcePos{x}, $sourcePos{y}), "portalRecord"; + updatePortalLUT(Settings::getTableFilename("portals.txt"), + $destMap, $destPos{x}, $destPos{y}, + $sourceMap, $sourcePos{x}, $sourcePos{y}); + Plugins::callHook('portal_exist2', { + srcMap => $destMap, + srcx => $destPos{x}, + srcy => $destPos{y}, + dstMap => $sourceMap, + dstx => $sourcePos{x}, + dsty => $sourcePos{y} + }); + $recorded = 1; + } + + # Record information about the source portal + if (!defined portalExists($sourceMap, \%sourcePos)) { + $ID = "$sourceMap $sourcePos{x} $sourcePos{y}"; + $portals_lut{$ID}{source}{map} = $sourceMap; + $portals_lut{$ID}{source}{x} = $sourcePos{x}; + $portals_lut{$ID}{source}{y} = $sourcePos{y}; + $destName = "$destMap $destPos{x} $destPos{y}"; + $portals_lut{$ID}{dest}{$destName}{map} = $destMap; + $portals_lut{$ID}{dest}{$destName}{x} = $destPos{x}; + $portals_lut{$ID}{dest}{$destName}{y} = $destPos{y}; + + message TF("Recorded new portal (source): %s (%s, %s) -> %s (%s, %s)\n", $sourceMap, $sourcePos{x}, $sourcePos{y}, $destMap, $char->{pos}{x}, $char->{pos}{y}), "portalRecord"; + updatePortalLUT(Settings::getTableFilename("portals.txt"), + $sourceMap, $sourcePos{x}, $sourcePos{y}, + $destMap, $char->{pos}{x}, $char->{pos}{y}); + Plugins::callHook('portal_exist2', { + srcMap => $sourceMap, + srcx => $sourcePos{x}, + srcy => $sourcePos{y}, + dstMap => $destMap, + dstx => $char->{pos}{x}, + dsty => $char->{pos}{y} + }); + $recorded = 1; + } + + if ($recorded && $config{portalRecord_recompileAfter}) { + Settings::loadByRegexp(qr/portals/); + Misc::compilePortals() if Misc::compilePortals_check(); + } +} + +##### ESCAPE UNKNOWN MAPS ##### +sub processEscapeUnknownMaps { + # escape from unknown maps. Happens when kore accidentally teleports onto an + # portal. With this, kore should automaticly go into the portal on the other side + # Todo: Make kore do a random walk searching for portal if there's no portal arround. + + if (AI::action eq "escape" && AI::state == AI::AUTO) { + my $skip = 0; + if (timeOut($timeout{ai_route_escape}) && $timeout{ai_route_escape}{time}){ + AI::dequeue; + if ($portalsID[0]) { + message T("Escaping to into nearest portal.\n"); + main::ai_route( + $field->baseName, $portals{$portalsID[0]}{'pos'}{'x'}, + $portals{$portalsID[0]}{'pos'}{'y'}, + attackOnRoute => 1, + noSitAuto => 1, + isEscape => 1 + ); + $skip = 1; + + } elsif ($spellsID[0]){ #get into the first portal you see + my $spell = $spells{$spellsID[0]}; + if (getSpellName($spell->{type}) eq "Warp Portal" ){ + message T("Found warp portal escaping into warp portal.\n"); + main::ai_route( + $field->baseName, + $spell->{pos}{x}, + $spell->{pos}{y}, + attackOnRoute => 1, + noSitAuto => 1, + isEscape => 1 + ); + $skip = 1; + }else{ + error T("Escape failed no portal found.\n");; + } + + } else { + error T("Escape failed no portal found.\n"); + } + } + if ($config{route_escape_randomWalk} && !$skip) { #randomly search for portals... + my ($randX, $randY); + my $i = 500; + # TODO: Is there any situation where we should use calcPosFromPathfinding or calcPosFromTime here? + my $pos = calcPosition($char); + do { + if ((rand(2)+1)%2) { + $randX = $pos->{x} + int(rand(9) + 1); + } else { + $randX = $pos->{x} - int(rand(9) + 1); + } + if ((rand(2)+1)%2) { + $randY = $pos->{y} + int(rand(9) + 1); + } else { + $randY = $pos->{y} - int(rand(9) + 1); + } + } while (--$i && !$field->isWalkable($randX, $randY)); + if (!$i) { + error T("Invalid coordinates specified for randomWalk\n Retrying..."); + } else { + message TF("Calculating random route to: %s: %s, %s\n", $field->descString(), $randX, $randY), "route"; + ai_route( + $field->baseName, $randX, $randY, + maxRouteTime => $config{route_randomWalk_maxRouteTime}, + attackOnRoute => 2, + noMapRoute => ($config{route_randomWalk} == 2 ? 1 : 0), + isRandomWalk => 1, + isEscape => 1 + ); + } + } + } +} + +##### DELAYED-TELEPORT ##### +sub processDelayedTeleport { + if (AI::action eq 'teleport') { + if ($char->{last_skill_used_is_continuous}) { + $messageSender->sendStopSkillUse($char->{last_continuous_skill_used}); + } + if ($timeout{ai_teleport_delay}{time} && timeOut($timeout{ai_teleport_delay})) { + # We have already successfully used the Teleport skill, + # and the ai_teleport_delay timeout has elapsed + $messageSender->sendWarpTele(26, AI::args->{lv} == 2 ? "$config{saveMap}.gat" : "Random"); + $ai_v{temp}{clear_aiQueue} = 1; + } elsif (!$timeout{ai_teleport_delay}{time} && timeOut($timeout{ai_teleport_retry})) { + # We are still trying to use the Teleport skill + $messageSender->sendSkillUse(26, $char->{skills}{AL_TELEPORT}{lv}, $accountID); + $timeout{ai_teleport_retry}{time} = time; + } + } +} + +##### SKILL USE ##### +sub processSkillUse { + #FIXME: need to move closer before using skill on player, + #there might be line of sight problem too + #or the player disappers from the area + + if (AI::action eq "skill_use" && AI::args->{suspended}) { + AI::args->{giveup}{time} += time - AI::args->{suspended}; + AI::args->{minCastTime}{time} += time - AI::args->{suspended}; + AI::args->{maxCastTime}{time} += time - AI::args->{suspended}; + delete AI::args->{suspended}; + } + + SKILL_USE: { + last SKILL_USE if (AI::action ne "skill_use"); + my $args = AI::args; + + if ($args->{monsterID} && $skillsArea{$args->{skillHandle}} == 2) { + delete $args->{monsterID}; + } + + if (exists $args->{ai_equipAuto_skilluse_giveup} && binFind(\@skillsID, $args->{skillHandle}) eq "" && timeOut($args->{ai_equipAuto_skilluse_giveup})) { + warning T("Timeout equiping for skill\n"); + AI::dequeue; + ${$args->{ret}} = 'equip timeout' if ($args->{ret}); + } elsif (Actor::Item::scanConfigAndCheck("$args->{prefix}_equip")) { + #check if item needs to be equipped + Actor::Item::scanConfigAndEquip("$args->{prefix}_equip"); + } elsif (timeOut($args->{waitBeforeUse})) { + if (defined $args->{monsterID} && !defined $monsters{$args->{monsterID}}) { + # This skill is supposed to be used for attacking a monster, but that monster has died + AI::dequeue; + ${$args->{ret}} = 'target gone' if ($args->{ret}); + + } elsif ($char->{sitting} && !$char->{statuses}->{EFST_TENSIONRELAX}) { + AI::suspend; + stand(); + + # Use skill if we haven't done so yet + } elsif (!$args->{skill_used}) { + if ($char->{last_skill_used_is_continuous}) { + message T("Stoping rolling\n"); + $messageSender->sendStopSkillUse($char->{last_continuous_skill_used}); + } elsif(($char->{last_skill_used} == 2027 || $char->{last_skill_used} == 147) && !$char->{selected_craft}) { + message T("No use skill due to not select the craft / poison\n"); + last SKILL_USE; + } + my $handle = $args->{skillHandle}; + if (!defined $args->{skillID}) { + my $skill = new Skill(handle => $handle); + $args->{skillID} = $skill->getIDN(); + } + my $skillID = $args->{skillID}; + + $args->{skill_used} = 1; + $args->{giveup}{time} = time; + + # Stop attacking, otherwise skill use might fail + my $attackIndex = AI::findAction("attack"); + if (defined($attackIndex) && AI::args($attackIndex)->{attackMethod}{type} eq "weapon") { + # 2005-01-24 pmak: Commenting this out since it may + # be causing bot to attack slowly when a buff runs + # out. + #$char->stopAttack(); + } + + # Give an error if we don't actually possess this skill + my $skill = new Skill(handle => $handle); + my $owner = $skill->getOwner(); + my $lvl = $owner->getSkillLevel($skill); + + if ($lvl <= 0 && (!$char->{permitSkill} || $char->{permitSkill}->getHandle() ne $handle)) { + debug "Attempted to use skill (".$skill->getName().") which you do not have.\n"; + } + + $args->{maxCastTime}{time} = time; + if ($skillsArea{$handle} == 2) { + $messageSender->sendSkillUse($skillID, $args->{lv}, $accountID); + } elsif ($args->{x} ne "") { + $messageSender->sendSkillUseLoc($skillID, $args->{lv}, $args->{x}, $args->{y}); + } elsif ($args->{isStartSkill}) { + $messageSender->sendStartSkillUse($skillID, $args->{lv}, $args->{target}); + } else { + $messageSender->sendSkillUse($skillID, $args->{lv}, $args->{target}); + } + undef $char->{permitSkill}; + $args->{skill_use_last} = $char->{skills}{$handle}{time_used}; + + delete $char->{cast_cancelled}; + + } elsif (timeOut($args->{minCastTime})) { + if ($args->{skill_use_last} != $char->{skills}{$args->{skillHandle}}{time_used}) { + AI::dequeue; + ${$args->{ret}} = 'ok' if ($args->{ret}); + + } elsif ($char->{cast_cancelled} > $char->{time_cast}) { + AI::dequeue; + ${$args->{ret}} = 'cancelled' if ($args->{ret}); + + } elsif (timeOut($char->{time_cast}, $char->{time_cast_wait} + 0.5) + && ( (timeOut($args->{giveup}) && (!$char->{time_cast} || !$args->{maxCastTime}{timeout}) ) + || ( $args->{maxCastTime}{timeout} && timeOut($args->{maxCastTime})) ) + ) { + AI::dequeue; + ${$args->{ret}} = 'timeout' if ($args->{ret}); + } + } + } + } +} + +sub processTake { + ##### TAKE ##### + + if (AI::action eq "take" && AI::args->{suspended}) { + AI::args->{ai_take_giveup}{time} += time - AI::args->{suspended}; + delete AI::args->{suspended}; + } + + if (AI::action eq "take" && !(my $item = $items{AI::args->{ID}})) { + AI::dequeue; + + } elsif (AI::action eq "take" && timeOut(AI::args->{ai_take_giveup})) { + message TF("Failed to take %s (%s) from (%s, %s) to (%s, %s)\n", $item->{name}, $item->{binID}, $char->{pos}{x}, $char->{pos}{y}, $item->{pos}{x}, $item->{pos}{y}); + $item->{take_failed}++; + AI::dequeue; + + } elsif (AI::action eq "take") { + my $myPos = $char->{pos}; + my $dist = blockDistance($item->{pos}, $myPos); + debug "Planning to take $item->{name} ($item->{binID}), distance $dist\n", "drop"; + + if ($char->{sitting}) { + stand(); + + } elsif ($dist <= 2 && $config{'itemsTakeGreed'} && $char->{skills}{BS_GREED}{lv} >= 1) { + my $skill = new Skill(handle => 'BS_GREED'); + ai_skillUse2($skill, $char->{skills}{BS_GREED}{lv}, 1, 0, $char, "BS_GREED"); + } elsif ($dist > 1 && timeOut(AI::args->{time_route}, $timeout{ai_take_giveup}{timeout})) { + my $pos = $item->{pos}; + AI::args->{time_route} = time; + ai_route( + $field->baseName, + $pos->{x}, + $pos->{y}, + maxRouteDistance => $config{'attackRouteMaxPathDistance'}, + noSitAuto => 1, + distFromGoal => 1, + isItemTake => 1 + ); + } elsif (timeOut($timeout{ai_take})) { + my %vec; + my $direction; + getVector(\%vec, $item->{pos}, $myPos); + $direction = int(sprintf("%.0f", (360 - vectorToDegree(\%vec)) / 45)) % 8; + $messageSender->sendLook($direction, 0) if ($direction != $char->{look}{body}); + $messageSender->sendTake($item->{ID}); + $timeout{ai_take}{time} = time; + } + } +} + +sub processEquip { + if (AI::action eq "equip") { + # Just wait until everything is equipped or timedOut + if (!$ai_v{temp}{waitForEquip} || timeOut($timeout{ai_equip_giveup})) { + AI::dequeue; + delete $ai_v{temp}{waitForEquip}; + } + } +} + +sub processDeal { + if (AI::action ne "deal" && %currentDeal) { + my %plugin_args = ( return => 0 ); + Plugins::callHook('deal_queue' => \%plugin_args); + return if ($plugin_args{return}); + + AI::queue('deal'); + } elsif (AI::action eq "deal") { + if (%currentDeal) { + if (!$currentDeal{you_finalize} && timeOut($timeout{ai_dealAuto}) && + (!$config{dealAuto_names} || existsInList($config{dealAuto_names}, $currentDeal{name})) && + ($config{dealAuto} == 2 || + $config{dealAuto} == 3 && $currentDeal{other_finalize})) { + $messageSender->sendDealAddItem(pack('v', 0), $currentDeal{'you_zeny'}); + $messageSender->sendDealFinalize(); + $timeout{ai_dealAuto}{time} = time; + } elsif ($currentDeal{other_finalize} && $currentDeal{you_finalize} &&timeOut($timeout{ai_dealAuto}) && $config{dealAuto} >= 2 && + (!$config{dealAuto_names} || existsInList($config{dealAuto_names}, $currentDeal{name}))) { + $messageSender->sendDealTrade(); + $timeout{ai_dealAuto}{time} = time; + } + } else { + AI::dequeue(); + } + } +} + +sub processDealAuto { + # dealAuto 1=refuse 2,3=accept + if ($config{'dealAuto'} && %incomingDeal) { + my %plugin_args = ( return => 0 ); + Plugins::callHook('deal_incoming' => \%plugin_args); + return if ($plugin_args{return}); + + if ($config{'dealAuto'} == 1 && timeOut($timeout{ai_dealAutoCancel})) { + $messageSender->sendDealReply(4); + $timeout{'ai_dealAutoCancel'}{'time'} = time; + } elsif ($config{'dealAuto'} >= 2 && + (!$config{dealAuto_names} || existsInList($config{dealAuto_names}, $incomingDeal{name})) && + timeOut($timeout{ai_dealAuto})) { + $messageSender->sendDealReply(3); + $timeout{'ai_dealAuto'}{'time'} = time; + } + } +} + +sub processPartyAuto { + # partyAuto 1=refuse 2=accept + if ($config{'partyAuto'} && %incomingParty && timeOut($timeout{'ai_partyAuto'})) { + # Do not React on other settings, then 1 or 2 + if ($config{partyAuto} == 1) { + message T("Auto-denying party request\n"); + # JOIN_REFUSE + Commands::run('party join 0'); + } elsif ($config{partyAuto} == 2) { + message T("Auto-accepting party request\n"); + # JOIN_ACCEPT + Commands::run('party join 1'); } + $timeout{'ai_partyAuto'}{'time'} = time; + } +} + +sub processGuildAutoDeny { + if ($config{'guildAutoDeny'} && %incomingGuild && timeOut($timeout{'ai_guildAutoDeny'})) { + $messageSender->sendGuildJoin($incomingGuild{'ID'}, 0) if ($incomingGuild{'Type'} == 1); + $messageSender->sendGuildAlly($incomingGuild{'ID'}, 0) if ($incomingGuild{'Type'} == 2); + $timeout{'ai_guildAutoDeny'}{'time'} = time; + undef %incomingGuild; + } +} + +=pod moved to a plugin +##### AUTOBREAKTIME ##### +# Break time: automatically disconnect at certain times of the day +sub processAutoBreakTime { + if (timeOut($AI::Timeouts::autoBreakTime, 30)) { + my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime; + my $hormin = sprintf("%02d:%02d", $hour, $min); + my @wdays = ('sun','mon','tue','wed','thu','fri','sat'); + debug "autoBreakTime: hormin = $hormin, weekday = $wdays[$wday]\n", "autoBreakTime", 2; + for (my $i = 0; exists $config{"autoBreakTime_$i"}; $i++) { + next if (!$config{"autoBreakTime_$i"}); + + if ( ($wdays[$wday] eq lc($config{"autoBreakTime_$i"})) || (lc($config{"autoBreakTime_$i"}) eq "all") ) { + if ($config{"autoBreakTime_${i}_startTime"} eq $hormin) { + my ($hr1, $min1) = split /:/, $config{"autoBreakTime_${i}_startTime"}; + my ($hr2, $min2) = split /:/, $config{"autoBreakTime_${i}_stopTime"}; + my $time1 = $hr1 * 60 * 60 + $min1 * 60; + my $time2 = $hr2 * 60 * 60 + $min2 * 60; + my $diff = ($time2 - $time1) % (60 * 60 * 24); + + message TF("\nDisconnecting due to break time: %s to %s\n\n", $config{"autoBreakTime_$i"."_startTime"}, $config{"autoBreakTime_$i"."_stopTime"}), "system"; + chatLog("k", TF("*** Disconnected due to Break Time: %s to %s ***\n", $config{"autoBreakTime_$i"."_startTime"}, $config{"autoBreakTime_$i"."_stopTime"})); + + $timeout_ex{'master'}{'timeout'} = $diff; + $timeout_ex{'master'}{'time'} = time; + $KoreStartTime = time; + $net->serverDisconnect(); + AI::clear(); + undef %ai_v; + $net->setState(Network::NOT_CONNECTED); + undef $conState_tries; + last; + } + } + } + $AI::Timeouts::autoBreakTime = time; + } +} +=cut + +##### DEAD ##### +sub processDead { + if (AI::action eq "dead" && !$char->{dead}) { + AI::dequeue; + $char->setStatus('Dead', 0); + + if ($char->{resurrected}) { + # We've been resurrected + $char->{resurrected} = 0; + + } else { + # Force storage after death + if ($config{storageAuto} && !$config{storageAuto_notAfterDeath} && ai_storageAutoCheck()) { + message T("Auto-storaging due to death\n"); + AI::queue("storageAuto"); + Plugins::callHook('AI_storage_auto_queued'); + } + + if ($config{autoMoveOnDeath} && $config{autoMoveOnDeath_map}) { + if ($config{autoMoveOnDeath_x} && $config{autoMoveOnDeath_y}) { + message TF("Moving to %s - %d,%d\n", $config{autoMoveOnDeath_map}, $config{autoMoveOnDeath_x}, $config{autoMoveOnDeath_y}); + } else { + message TF("Moving to %s\n", $config{autoMoveOnDeath_map}); + } + AI::queue("sitAuto"); + ai_route($config{autoMoveOnDeath_map}, $config{autoMoveOnDeath_x}, $config{autoMoveOnDeath_y}, isDeath => 1); + } + } + + } elsif (AI::action ne "dead" && AI::action ne "deal" && $char->{'dead'}) { + AI::clear(); + AI::queue("dead"); + $char->setStatus('Dead', 1); + + my $dt = getFormattedDate(int(time)); + push @deadTime, $dt; + if ($config{logDead}) { + deadLog($dt); + } + } + + if (AI::action eq "dead" && $config{dcOnDeath} != -1 && time - $char->{dead_time} >= $timeout{ai_dead_respawn}{timeout}) { + $messageSender->sendRestart(0); + $char->{'dead_time'} = time; + } + + if (AI::action eq "dead" && $config{dcOnDeath} && $config{dcOnDeath} != -1) { + error T("Auto disconnecting on death!\n"); + chatLog("k", T("*** You died, auto disconnect! ***\n")); + $messageSender->sendQuit(); + quit(); + } +} + +##### TRANSFER ITEMS ##### +# Transfer one or more items from one location (inventory/cart/storage) to another. +# AI::args should look like: +# { +# items => [ # list of items to transfer +# { source => 'inventory', target => 'cart', item => Actor::Item, amount => 10 }, +# { source => 'inventory', target => 'storage', item => Actor::Item }, +# ], +# timeout => 0.5, # 0.5 seconds between transfers +# } +sub processTransferItems { + return if AI::action ne 'transferItems'; + return if !timeOut( AI::args ); + + { + my $row = shift @{ AI::args->{items} }; + last if !$row; + redo if !$row->{source} || !$row->{target} || !$row->{item}; + + # Skip if we're transferring from and to the same location. + redo if $row->{source} eq $row->{target}; + + # Skip if we don't know how to handle the source and/or target. + redo if $row->{source} !~ /^(inventory|storage|cart)$/o; + redo if $row->{target} !~ /^(inventory|storage|cart)$/o; + + # Figure out where the item came from and what packet we need to use to transfer it. + my ( $source, $target, $method ); + if ( $row->{source} eq 'inventory' ) { + ( $source, $target, $method ) = ( $char->inventory => $char->storage, 'sendStorageAdd' ) if $row->{target} eq 'storage'; + ( $source, $target, $method ) = ( $char->inventory => $char->cart, 'sendCartAdd' ) if $row->{target} eq 'cart'; + } elsif ( $row->{source} eq 'storage' ) { + ( $source, $target, $method ) = ( $char->storage => $char->inventory, 'sendStorageGet' ) if $row->{target} eq 'inventory'; + ( $source, $target, $method ) = ( $char->storage => $char->cart, 'sendStorageGetToCart' ) if $row->{target} eq 'cart'; + } elsif ( $row->{source} eq 'cart' ) { + ( $source, $target, $method ) = ( $char->cart => $char->inventory, 'sendCartGet' ) if $row->{target} eq 'inventory'; + ( $source, $target, $method ) = ( $char->cart => $char->storage, 'sendStorageAddFromCart' ) if $row->{target} eq 'storage'; + } + + # Make sure source and target are available. + foreach ( [ $row->{source}, $source ], [ $row->{target}, $target ] ) { + my ( $name, $list ) = @$_; + next if $list->isReady; + #name can be storage, inventory or cart + error TF( "Your %s is not available. Unable to transfer item '%s'.\n", $name, $row->{item} ); + redo; + } + + # Verify that the item is still in the source list and has not changed. + my $item = $source->get( $row->{item}->{binID} ); + + if ( !$item || $item->nameString ne $row->{item}->nameString ) { + error TF( "%s item '%s' disappeared!\n", $row->{source}, $row->{item} ); + redo; + } + + #verify if we can carry the amount of item + #if you're sending to storage, doesn't need to check weight + #if there is no information about weight, there's no point in check + if ($row->{target} ne 'storage' && $item->weight() && $config{itemsCheckWeight}) { + my $freeWeight; + if ($row->{target} eq 'inventory') { + $freeWeight = int ($char->{weight_max} - $char->{weight}); + } else { #if target not inventory, then is cart! + $freeWeight = int ($char->cart->{weight_max} - $char->cart->{weight}); + } + my $weightNeeded = min( $item->{amount}, $row->{amount} || $item->{amount} ) * ($item->weight()/10); + + if ($weightNeeded > $freeWeight) { + #need to low down the amount + $row->{amount} = $freeWeight / ( $item->weight()/10 ); + warning TF("Amount of %s is more than you can carry, getting the maximum possible (%d)\n", $row->{item}, $row->{amount}); + } + } + + if ( $row->{source} eq 'inventory' && $item->{equipped} ) { + error TF( "Inventory item '%s' is equipped.\n", $item->{name} ); + redo; + } + + my $target_item = $target->getByName( $row->{item}->{name} ); + my $stack_limit = $target->item_max_stack( $row->{item}->{nameID} ); + + my $amount = min($stack_limit, min( $item->{amount}, $row->{amount} || $item->{amount} )); + if ( $stack_limit - $amount < 0 || $target_item->{amount} - $stack_limit == 0) { + error TF("Unable to add %s to %s. You can't stack over %s of this item\n", $item->name, $row->{target}, $stack_limit); + redo; + + } elsif ( $target_item->{amount} + $amount > $stack_limit ) { + warning TF("Amount of %s will surpass the maximum %s capacity (%d), transfering maximum possible (%d)\n", + $row->{item}->name, $row->{target}, $stack_limit, $stack_limit - $target_item->{amount} ); + + $amount = $stack_limit - $target_item->{amount}; + + } + # Transfer the item! + $messageSender->$method( $item->{ID}, $amount ); + } + + AI::args->{time} = time; + AI::dequeue if !@{ AI::args->{items} }; +} + +#### CART ADD #### +# Put one or more items in cart. +# TODO: check for cart weight & number of items +sub processCartAdd { + if (AI::action eq "cartAdd" && timeOut(AI::args)) { + my $item = AI::args->{items}[0]; + my $invItem = $char->inventory->get($item->{index}); + if ($invItem) { + $messageSender->sendCartAdd($invItem->{ID}, min($invItem->{amount}, $item->{amount} || $invItem->{amount})); + } + shift @{AI::args->{items}}; + AI::args->{time} = time; + AI::dequeue if (@{AI::args->{items}} <= 0); + } +} + +#### CART Get #### +# Get one or more items from cart. +sub processCartGet { + if (AI::action eq "cartGet" && timeOut(AI::args)) { + my $item = AI::args->{items}[0]; + my $amount = $item->{amount}; + my $cartItem = $char->cart->get($item->{index}); + if ($cartItem) { + $messageSender->sendCartGet($cartItem->{ID}, min($cartItem->{amount}, $item->{amount} || $cartItem->{amount})); + } + shift @{AI::args->{items}}; + AI::args->{time} = time; + AI::dequeue if (@{AI::args->{items}} <= 0); + } +} + +sub processAutoMakeArrow { + ####### AUTO MAKE ARROW ####### + if ((AI::isIdle || AI::is(qw/route move autoBuy storageAuto follow sitAuto items_take items_gather/)) + && timeOut($AI::Timeouts::autoArrow, 0.2) && $config{autoMakeArrows} && defined binFind(\@skillsID, 'AC_MAKINGARROW') ) { + my $max = @arrowCraftID; + my $nMake = 0; + for (my $i = 0; $i < $max; $i++) { + my $item = $char->inventory->get($arrowCraftID[$i]); + next if (!$item); + if ($arrowcraft_items{lc($item->{name})}) { + $messageSender->sendArrowCraft($item->{nameID}); + debug "Making item\n", "ai_makeItem"; + $nMake++; + last; + } + } + $messageSender->sendArrowCraft(-1) if ($nMake == 0 && $max > 0); + if ($nMake == 0 && !$useArrowCraft){ + for my $item (@{$char->inventory}) { + if ($arrowcraft_items{lc($item->{name})}) { + $useArrowCraft = 1; + last; + } + } + } + $AI::Timeouts::autoArrow = time; + } + + if ($config{autoMakeArrows} && $useArrowCraft) { + if (defined binFind(\@skillsID, 'AC_MAKINGARROW')) { + ai_skillUse('AC_MAKINGARROW', 1, 0, 0, $accountID); + } + undef $useArrowCraft; + } +} + +##### AUTO STORAGE ##### +sub processAutoStorage { + return if ($shopstarted || $buyershopstarted); + # storageAuto - chobit aska 20030128 + if ((AI::isIdle || AI::is("route", "sitAuto", "follow")) + && $config{storageAuto} && ($config{storageAuto_npc} ne "" || $config{storageAuto_useChatCommand} || $config{storageAuto_useItem}) + && !$ai_v{sitAuto_forcedBySitCommand} + && !AI::inQueue("buyAuto") + && !AI::inQueue("sellAuto") + && ai_canOpenStorage() + && ( + ($config{'itemsMaxWeight_sellOrStore'} && percent_weight($char) >= $config{'itemsMaxWeight_sellOrStore'}) + || (!$config{'itemsMaxWeight_sellOrStore'} && percent_weight($char) >= $config{'itemsMaxWeight'}) + || ($config{itemsMaxNum_sellOrStore} && $char->inventory->size() >= $config{itemsMaxNum_sellOrStore}) + || ($config{storageAuto_onStart} && !$char->storage->wasOpenedThisSession()) + ) + && !AI::inQueue("storageAuto") && $char->inventory->isReady() + + ) { + my %plugin_args = ( return => 0 ); + Plugins::callHook('AI_storage_auto_weight_start' => \%plugin_args); + return if ($plugin_args{return}); + + # Initiate autostorage when the weight limit has been reached + my $routeIndex = AI::findAction("route"); + my $attackOnRoute = 2; + $attackOnRoute = AI::args($routeIndex)->{attackOnRoute} if (defined $routeIndex); + # Only autostorage when we're on an attack route, or not moving + if ($attackOnRoute > 1 && (ai_storageAutoCheck() || ($config{storageAuto_onStart} && !$char->storage->wasOpenedThisSession()))) { + message T("Auto-storaging due to excess weight, excess items or storageAuto_onStart\n"); + AI::queue("storageAuto"); + Plugins::callHook('AI_storage_auto_queued'); + } + + } elsif ((AI::isIdle || AI::is("route", "attack")) + && $config{storageAuto} + && ($config{storageAuto_npc} ne "" || $config{storageAuto_useChatCommand} || $config{storageAuto_useItem}) + && !$ai_v{sitAuto_forcedBySitCommand} + && !AI::inQueue("storageAuto") + && !AI::inQueue("buyAuto") + && !AI::inQueue("sellAuto") + && $char->inventory->isReady()) { + + my %plugin_args = ( return => 0 ); + Plugins::callHook('AI_storage_auto_get_auto_start' => \%plugin_args); + return if ($plugin_args{return}); + + # Initiate autostorage when we're low on some item, and getAuto is set + my $needitem = ""; + my $i; + Misc::checkValidity("AutoStorage part 1"); + for ($i = 0; exists $config{"getAuto_$i"}; $i++) { + next unless ($config{"getAuto_$i"}); + next if ($config{"getAuto_$i"."_disabled"}); + next if ($config{"getAuto_$i"."_minBase"} =~ /^\d+$/ && $char->{lv} <= $config{"getAuto_$i"."_minBase"}); + next if ($config{"getAuto_$i"."_maxBase"} =~ /^\d+$/ && $char->{lv} >= $config{"getAuto_$i"."_maxBase"}); + if ($char->storage->isReady() && !$char->storage->getByName($config{"getAuto_$i"})) { + foreach my $nameID (keys %items_lut) { + if (lc($items_lut{$nameID}) eq lc($config{"getAuto_$i"}) && $items_lut{$nameID} ne $config{"getAuto_$i"}) { + configModify("getAuto_$i", $items_lut{$nameID}); + } + } + } + + my $item = $char->inventory->getByName($config{"getAuto_$i"}) || $char->inventory->getByNameID($config{"getAuto_$i"}); + # total amount of the same name items + my $amount = $char->inventory->sumByName($config{"getAuto_$i"}) || $char->inventory->sumByNameID($config{"getAuto_$i"}); + if ($config{"getAuto_${i}_minAmount"} ne "" && + $config{"getAuto_${i}_maxAmount"} ne "" && + !$config{"getAuto_${i}_passive"} && + (!$item || + ($amount <= $config{"getAuto_${i}_minAmount"} && + $amount < $config{"getAuto_${i}_maxAmount"}) + ) && + checkSelfCondition("getAuto_$i") + ) { + if ($char->storage->isReady() && + !($char->storage->getByName($config{"getAuto_$i"}) || $char->storage->getByNameID($config{"getAuto_$i"}))) { +=pod + #This works only for last getAuto item + if ($config{"getAuto_${i}_dcOnEmpty"}) { + message TF("Disconnecting on empty %s!\n", $config{"getAuto_$i"}); + chatLog("k", TF("Disconnecting on empty %s!\n", $config{"getAuto_$i"})); + quit(); + } +=cut + } else { + if ($char->storage->wasOpenedThisSession() && + !($char->storage->getByName($config{"getAuto_$i"}) || $char->storage->getByNameID($config{"getAuto_$i"}))) { + debug TF("storage: %s out of stock\n\n", $config{"getAuto_$i"}), "storage", 2; + Plugins::callHook('AI_storage_item_out_of_stock', { + name => $config{"getAuto_$i"}, + getAutoIndex => $i, + } + ); + } else { + my $sti = $config{"getAuto_$i"}; + if ($needitem eq "") { + $needitem = "$sti"; + } else {$needitem = "$needitem, $sti";} + } + } + } + } + Misc::checkValidity("AutoStorage part 2"); + + my $routeIndex = AI::findAction("route"); + my $attackOnRoute; + $attackOnRoute = AI::args($routeIndex)->{attackOnRoute} if (defined $routeIndex); + + # Only autostorage when we're on an attack route, or not moving + if ((!defined($routeIndex) || $attackOnRoute > 1 || AI::isIdle) && $needitem ne "" && + $char->inventory->isReady() && ai_canOpenStorage()){ + message TF("Auto-storaging due to insufficient %s\n", $needitem); + AI::queue("storageAuto"); + Plugins::callHook('AI_storage_auto_queued'); + } + $timeout{'ai_storageAuto'}{'time'} = time; + } + + + if (AI::action eq "storageAuto" && AI::args->{done}) { + # Autostorage finished; trigger sellAuto unless autostorage was already triggered by it + my $forcedBySell = AI::args->{forcedBySell}; + my $forcedByBuy = AI::args->{forcedByBuy}; + + undef $timeout{ai_storageAuto_wait_before_action}{time}; + AI::dequeue; + + if ($config{sellAuto} && ai_sellAutoCheck()) { + if ($forcedByBuy) { + AI::queue("sellAuto", {forcedByBuy => 1}); + Plugins::callHook('AI_sell_auto_queued'); + + } elsif (!$forcedBySell) { + AI::queue("sellAuto", {forcedByStorage => 1}); + Plugins::callHook('AI_sell_auto_queued'); + } + } + + } elsif (AI::action eq "storageAuto" && timeOut($timeout{'ai_storageAuto'})) { + # Main autostorage block + my $args = AI::args; + + my $do_route; + + if (!$config{storageAuto_useChatCommand} && !$config{storageAuto_useItem}) { + + if (!exists $args->{npc} || !exists $args->{npc}{ok}) { + # Check if NPC info is provided in args + if (exists $args->{npc}) { + debug "[storageAuto] Using npc information from arguments.\n"; + if (exists $args->{npc}{map} && $args->{npc}{map} ne "" && exists $args->{npc}{pos} && exists $args->{npc}{pos}{x} && $args->{npc}{pos}{x} ne "" && exists $args->{npc}{pos}{y} && $args->{npc}{pos}{y} ne "") { + $args->{npc}{ok} = 1; + } + # If not, get from config + } else { + debug "[storageAuto] Using npc information from config.\n"; + $args->{npc} = {}; + getNPCInfo($config{storageAuto_standpoint} || $config{'storageAuto_npc'}, $args->{npc}); + } + if (!defined($args->{npc}{ok})) { + error "[storageAuto] Invalid npc information given.\n"; + $args->{done} = 1; + return; + } + } + + if (!AI::args->{distance}) { + if ($config{storageAuto_standpoint}) { + AI::args->{distance} = 1; + } elsif ($config{'storageAuto_maxDistance'} && $config{'storageAuto_distance'}) { # Calculate variable or fixed (old) distance + AI::args->{distance} = $config{'storageAuto_distance'} + round(rand($config{'storageAuto_maxDistance'} - $config{'storageAuto_distance'})); + } else { + AI::args->{distance} = $config{'storageAuto_distance'} || 3; + } + } + + # Determine whether we have to move to the NPC + if ($field->baseName ne $args->{npc}{map}) { + $do_route = 1; + } else { + my $distance_from_char = blockDistance($args->{npc}{pos}, $char->{pos_to}); + if (($distance_from_char > AI::args->{distance}) && !defined($args->{sentStore}) && !$char->storage->isReady()) { + $do_route = 1; + } + } + + if ($do_route) { + if ($args->{warpedToSave} && !$args->{mapChanged} && !timeOut($args->{warpStart}, 8)) { + undef $args->{warpedToSave}; + } + + # If warpToBuyOrSell is set, warp to saveMap if we haven't done so + if (shouldUseWarpToSaveMapForBuyOrSell($args)) { + if ($char->{sitting}) { + message T("Standing up to auto-storage\n"), "teleport"; + ai_setSuspend(0); + stand(); + } else { + $args->{warpedToSave} = 1; + # If we still haven't warped after a certain amount of time, fallback to walking + $args->{warpStart} = time unless $args->{warpStart}; + message T("Teleporting to auto-storage\n"), "teleport"; + ai_useTeleport(2); + } + $timeout{'ai_storageAuto'}{'time'} = time; + } else { + # warpToBuyOrSell is not set, or we've already warped, or timed out. Walk to the NPC + message TF("Calculating auto-storage route to: %s(%s): %s, %s\n", $maps_lut{$args->{npc}{map}.'.rsw'}, $args->{npc}{map}, $args->{npc}{pos}{x}, $args->{npc}{pos}{y}), "route"; + ai_route($args->{npc}{map}, $args->{npc}{pos}{x}, $args->{npc}{pos}{y}, + attackOnRoute => 1, + distFromGoal => AI::args->{distance}); + } + } + } + if (!$do_route) { + # Talk to NPC if we haven't done so + if (!defined($args->{sentStore})) { + if ($config{storageAuto_useChatCommand}) { + $messageSender->sendChat($config{storageAuto_useChatCommand}); + } elsif ($config{storageAuto_useItem}) { + my $itemToOpenStorageWith = Actor::Item::get($config{storageAuto_useItem_item}); + + if (!$itemToOpenStorageWith) { + error TF("Cannot find item %s to open storage\n", $config{storageAuto_useItem_item}); + + if ($config{storageAuto_npc}) { + warning TF("Falling back to regular npc at %s, disabling storageAuto_useItem\n", $config{storageAuto_npc}); + configModify("storageAuto_useItem", 0); + } else { + warning T("No fallback npc specified, disabling storageAuto\n"); + configModify("storageAuto", 0); + AI::dequeue if (AI::action eq "storageAuto"); + } + return; + } + + if (timeOut($timeout{ai_storageAuto_useItem})) { + debug TF("Consuming item %s to open storage\n", $config{storageAuto_useItem}); + + $itemToOpenStorageWith->use; + $timeout{ai_storageAuto_useItem}{time} = time; + } + } else { + if (!ai_canOpenStorage()) { + warning TF("Cannot open storage, giving up\n"); + AI::args->{done} = 1; + return; + } + + my $steps; + # Determine steps based on args or config + if ($args->{npc} && $args->{npc}{sequence}) { + $steps = $args->{npc}{sequence}; + } else { + # Use config steps based on storageAuto_npc_type + if ($config{'storageAuto_npc_type'} eq "" || $config{'storageAuto_npc_type'} eq "1") { + warning T("Warning storageAuto has changed. Please read News.txt\n") if ($config{'storageAuto_npc_type'} eq ""); + if (grep { $masterServer->{serverType} eq $_ } qw(ROla) && $config{'storageAuto_npc'} =~ /prontera|payon/i) { + $steps = "c r2"; + debug "Using standard ROla npc storage steps.\n", "npc"; + } else { + $steps = "c r1"; + debug "Using standard iRO npc storage steps.\n", "npc"; + } + } elsif ($config{'storageAuto_npc_type'} eq "2") { + $steps = "c c r1"; + debug "Using iRO comodo (location) npc storage steps.\n", "npc"; + } elsif ($config{'storageAuto_npc_type'} eq "3") { + message T("Using storage steps defined in config.\n"), "info"; + $steps = $config{'storageAuto_npc_steps'}; + } elsif ($config{'storageAuto_npc_type'} ne "" && $config{'storageAuto_npc_type'} ne "1" && $config{'storageAuto_npc_type'} ne "2" && $config{'storageAuto_npc_type'} ne "3") { + error T("Something is wrong with storageAuto_npc_type in your config.\n"); + $steps = $config{'storageAuto_npc_steps'} || "c r1"; # default + } + } + + my $x = $args->{npc}{pos}{x}; + my $y = $args->{npc}{pos}{y}; + ai_talkNPC($x, $y, $steps); + AI::args->{'is_storageAuto'} = 1; + } + + #delete $ai_v{temp}{storage_opened}; + $args->{sentStore} = 1; + + # NPC talk retry + $AI::Timeouts::storageOpening = time; + $timeout{'ai_storageAuto'}{'time'} = time; + return; + } + + if (!$char->storage->isReady()) { + # NPC talk retry + if (timeOut($AI::Timeouts::storageOpening, 40)) { + undef $args->{sentStore}; + debug "Retry talking to autostorage NPC.\n", "npc"; + } + + # Storage not yet opened; stop and wait until it's open + return; + } + + my %pluginArgs; + Plugins::callHook('AI_storage_open', \%pluginArgs); # we can hook here to perform actions BEFORE any storage function + return if ($pluginArgs{return}); + + if (!$timeout{ai_storageAuto_wait_before_action}{time}) { + $timeout{ai_storageAuto_wait_before_action}{time} = time; + return; + } elsif (!timeOut($timeout{ai_storageAuto_wait_before_action})) { + return; + } + + if (!$args->{getStart}) { + $args->{done} = 1; + + # if storage is full disconnect if it says so in conf + if ($char->storage->wasOpenedThisSession() && $char->storage->isFull()) { + Plugins::callHook('AI_storage_full', \%pluginArgs); + if ($config{'dcOnStorageFull'}) { + $messageSender->sendQuit(); + error T("Auto disconnecting on StorageFull!\n"); + chatLog("k", T("*** Your storage is full , disconnect! ***\n")); + quit(); + } + } + + # inventory to storage + $args->{nextItem} = 0 unless $args->{nextItem}; + for (my $i = $args->{nextItem}; $i < $char->inventory->size; $i++) { + my $item = $char->inventory->[$i]; + next if $item->{equipped}; + next if ($item->{broken} && $item->{type} == 7); # dont store pet egg in use + + if (defined($args->{lastInventoryCount}) && defined($args->{lastNameID}) && defined($args->{lastAmount}) && + $args->{lastNameID} == $item->{nameID} && + $args->{lastInventoryCount} == $char->inventory->size && + $args->{lastAmount} == $item->{amount} + ) { + error TF("Unable to store %s.\n", $item->{name}); + + if ($char->storage->getByName($item->{name})) { + Plugins::callHook('AI_storage_item_full', { + item => $item, + } + ); + } + next; + } + + my $control = items_control($item->{name}, $item->{nameID}); + + debug "AUTOSTORAGE: $item->{name} x $item->{amount} - store = $control->{storage}, keep = $control->{keep}\n", "storage"; + if ($control->{storage} && $item->{amount} > $control->{keep}) { + if ($args->{lastIndex} eq $item->{ID} && + timeOut($timeout{'ai_storageAuto_giveup'})) { + return; + } elsif ($args->{lastIndex} ne $item->{ID}) { + $timeout{ai_storageAuto_giveup}{time} = time; + } + undef $args->{done}; + $args->{lastIndex} = $item->{ID}; + $args->{lastNameID} = $item->{nameID}; + $args->{lastAmount} = $item->{amount}; + $args->{lastInventoryCount} = $char->inventory->size; + $messageSender->sendStorageAdd($item->{ID}, $item->{amount} - $control->{keep}); + $timeout{ai_storageAuto}{time} = time; + $args->{nextItem} = $i; + return; + } + } + + # cart to storage + # we don't really need to check if we have a cart + # if we don't have one it will not find any items to loop through + $args->{cartNextItem} = 0 unless $args->{cartNextItem}; + for (my $i = $args->{cartNextItem}; $i < $char->cart->size; $i++) { + my $item = $char->cart->[$i]; + next unless ($item && %{$item}); + + my $control = items_control($item->{name}, $item->{nameID}); + + debug "AUTOSTORAGE (cart): $item->{name} x $item->{amount} - store = $control->{storage}, keep = $control->{keep}\n", "storage"; + # store from cart as well as inventory if the flag is equal to 2 + if ($control->{storage} == 2 && $item->{amount} > $control->{keep}) { + if ($args->{cartLastIndex} eq $item->{ID} && + timeOut($timeout{'ai_storageAuto_giveup'})) { + return; + } elsif ($args->{cartLastIndex} ne $item->{ID}) { + $timeout{ai_storageAuto_giveup}{time} = time; + } + undef $args->{done}; + $args->{cartLastIndex} = $item->{ID}; + $messageSender->sendStorageAddFromCart($item->{ID}, $item->{amount} - $control->{keep}); + $timeout{ai_storageAuto}{time} = time; + $args->{cartNextItem} = $i; + return; + } + } + + if ($args->{done}) { + # plugins can hook here and decide to keep storage open longer + my %hookArgs; + Plugins::callHook('AI_storage_done', \%hookArgs); + undef $args->{done} if ($hookArgs{return}); + } + } + + + # getAuto begin + + if (!$args->{getStart} && $args->{done} == 1) { + $args->{getStart} = 1; + undef $args->{done}; + $args->{index} = 0; + $args->{retry} = 0; + return; + } + + # Repeat until there is no more items to get + if (defined($args->{getStart}) && $args->{done} != 1) { + Misc::checkValidity("AutoStorage part 3"); + while (exists $config{"getAuto_$args->{index}"}) { + if (!$config{"getAuto_$args->{index}"} + || $config{"getAuto_$args->{index}_disabled"} + || !checkSelfCondition("getAuto_$args->{index}")) { + $args->{index}++; + next; + } + + my %item; + my $itemName = $config{"getAuto_$args->{index}"}; + if (!$itemName) { + $args->{index}++; + next; + } + my $invItem = $char->inventory->getByName($itemName) || $char->inventory->getByNameID($itemName); + my $invAmount = $char->inventory->sumByName($itemName) || $char->inventory->sumByNameID($itemName); + my $storeItem = $char->storage->getByName($itemName) || $char->storage->getByNameID($itemName); + my $storeAmount = $char->storage->sumByName($itemName) || $char->storage->sumByNameID($itemName); + $item{name} = $itemName; + $item{inventory}{index} = $invItem ? $invItem->{binID} : undef; + $item{inventory}{amount} = $invItem ? $invAmount : 0; + $item{storage}{index} = $storeItem ? $storeItem->{binID} : undef; + $item{storage}{amount} = $storeItem ? $storeAmount : 0; + $item{max_amount} = $config{"getAuto_$args->{index}"."_maxAmount"}; + $item{amount_needed} = $item{max_amount} - $item{inventory}{amount}; + $item{dcOnEmpty} = $config{"getAuto_$args->{index}"."_dcOnEmpty"}; + + # Calculate the amount to get + if ($item{amount_needed} > 0) { + $item{amount_get} = ($item{storage}{amount} >= $item{amount_needed})? $item{amount_needed} : $item{storage}{amount}; + } + + # Try at most 3 times to get the item + if (($item{amount_get} > 0) && ($args->{retry} < 3)) { + + my $batchSize = $config{"getAuto_$args->{index}"."_batchSize"}; + + if ($batchSize && $batchSize < $item{amount_get}) { + + my $remaining = $item{amount_get} - $batchSize; + $item{amount_get} = $batchSize; + + # Last loop attempted to get batchSize of item and succeeded + if ($args->{getAuto_batchSize_remaining} && $args->{getAuto_batchSize_remaining} != $remaining) { + $args->{retry} = 0; + } + + $args->{getAuto_batchSize_remaining} = $remaining; + + message TF("Attempt to get %s (batchSize) x %s from storage, retry: %s, remaining %s\n", $item{amount_get}, $item{name}, $ai_seq_args[0]{retry}, $args->{getAuto_batchSize_remaining}), "storage", 1; + } else { + message TF("Attempt to get %s x %s from storage, retry: %s\n", $item{amount_get}, $item{name}, $ai_seq_args[0]{retry}), "storage", 1; + } + + $messageSender->sendStorageGet($storeItem->{ID}, $item{amount_get}); + $timeout{ai_storageAuto}{time} = time; + $args->{retry}++; + return; + + # we don't inc the index when amount_get is more then 0, this will enable a way of retrying + # on next loop if it fails this time + } + + if ($item{storage}{amount} < $item{amount_needed}) { + warning TF("storage: %s out of stock\n", $item{name}); + Plugins::callHook('AI_storage_item_out_of_stock', { + name => $config{"getAuto_$args->{index}"}, + getAutoIndex => $args->{index}, + } + ); + if ($item{dcOnEmpty}) { + debug TF("Disconnecting on empty %s!\n", $item{name}); + $char->{dcOnEmptyItems} .= "," if ($char->{dcOnEmptyItems} ne ""); + $char->{dcOnEmptyItems} .= $item{name}; + } + } + + if (!$config{relogAfterStorage} && $args->{retry} >= 3 && !$args->{warned}) { + # We tried 3 times to get the item and failed. + # There is a weird server bug which causes this to happen, + # but I can't reproduce it. This can be worked around by + # relogging in after autostorage. + warning T("Kore tried to get an item from storage 3 times, but failed.\n" . + "This problem could be caused by a server bug.\n" . + "To work around this problem, set 'relogAfterStorage' to 1, and relogin.\n"); + $args->{warned} = 1; + } + + # We got the item, or we tried 3 times to get it, but failed. + # Increment index and process the next item. + $args->{index}++; + $args->{getAuto_batchSize_remaining} = 0; + $args->{retry} = 0; + } + Misc::checkValidity("AutoStorage part 4"); + } + + # plugins can hook here and decide to keep storage open longer after getAuto + my %hookArgs; + Plugins::callHook('AI_storage_done_after_getAuto', \%hookArgs); + return if ($hookArgs{return}); + + $messageSender->sendStorageClose() unless $config{storageAuto_keepOpen}; + if ($args->{'forcedBySell'} == 1 && percent_weight($char) >= $config{'itemsMaxWeight_sellOrStore'} && ai_storageAutoCheck()) { + error T("Character is still overweight after storageAuto (storage is full?)\n"); + if ($config{dcOnStorageFull}) { + $messageSender->sendQuit(); + error T("Auto disconnecting on StorageFull!\n"); + chatLog("k", T("*** Your storage is full , disconnect! ***\n")); + quit(); + } + } + + if ($config{'relogAfterStorage'} && $config{'XKore'} ne "1") { + writeStorageLog(0); + relog(); + } + $args->{done} = 1; + } + } +} + +#####AUTO SELL##### +sub processAutoSell { + return if ($shopstarted || $buyershopstarted); + if ((AI::isIdle || AI::action eq "route" || AI::action eq "sitAuto" || AI::action eq "follow") + && (($config{'itemsMaxWeight_sellOrStore'} && percent_weight($char) >= $config{'itemsMaxWeight_sellOrStore'}) + || ($config{'itemsMaxNum_sellOrStore'} && $char->inventory->size() >= $config{'itemsMaxNum_sellOrStore'}) + || (!$config{'itemsMaxWeight_sellOrStore'} && percent_weight($char) >= $config{'itemsMaxWeight'}) + ) + && !AI::inQueue("storageAuto") + && !AI::inQueue("buyAuto") + && $config{'sellAuto'} + && $config{'sellAuto_npc'} ne "" + && !$ai_v{sitAuto_forcedBySitCommand} + ) { + my %plugin_args = ( return => 0 ); + Plugins::callHook('AI_sell_auto_start' => \%plugin_args); + return if ($plugin_args{return}); + $ai_v{'temp'}{'ai_route_index'} = AI::findAction("route"); + if ($ai_v{'temp'}{'ai_route_index'} ne "") { + $ai_v{'temp'}{'ai_route_attackOnRoute'} = AI::args($ai_v{'temp'}{'ai_route_index'})->{'attackOnRoute'}; + } + if (!($ai_v{'temp'}{'ai_route_index'} ne "" && $ai_v{'temp'}{'ai_route_attackOnRoute'} <= 1) && ai_sellAutoCheck()) { + AI::queue("sellAuto"); + Plugins::callHook('AI_sell_auto_queued'); + } + } + + if (AI::action eq "sellAuto" && AI::args->{'done'}) { + + if (exists AI::args->{'error'}) { + error AI::args->{'error'}.".\n"; + } + message T("Auto-sell sequence completed.\n"), "success"; + + my $forcedByBuy = AI::args->{'forcedByBuy'}; + my $forcedByStorage = AI::args->{'forcedByStorage'}; + AI::dequeue; + + if ($forcedByStorage) { + AI::queue("buyAuto", {forcedByStorage => 1}); + Plugins::callHook('AI_buy_auto_queued'); + + } elsif (!$forcedByBuy) { + AI::queue("buyAuto", {forcedBySell => 1}); + Plugins::callHook('AI_buy_auto_queued'); + } + } elsif (AI::action eq "sellAuto" && timeOut($timeout{'ai_sellAuto'})) { + my $args = AI::args; + + if (exists $args->{sentSellPacket_time} && exists $args->{'sentEmptyList'}) { + $args->{'done'} = 1; + return; + + } elsif (exists $args->{sentSellPacket_time} && !exists $args->{'sentEmptyList'}) { + if (exists $args->{recv_sell_packet}) { + $args->{'done'} = 1; + + } elsif (timeOut($args->{sentSellPacket_time}, $timeout{ai_sellAuto_wait_after_packet_giveup}{timeout})) { + $args->{'error'} = 'Did not received the sell result from server after sell packet was sent'; + $args->{'done'} = 1; + } + return; + + } + + $args->{'npc'} = {}; + my $destination = $config{sellAuto_standpoint} || $config{sellAuto_npc}; + getNPCInfo($destination, $args->{'npc'}); + if (!defined($args->{'npc'}{'ok'})) { + $args->{'done'} = 1; + return; + } + + if (!$args->{distance}) { + if ($config{'sellAuto_standpoint'}) { + $args->{distance} = 1; + } elsif ($config{'sellAuto_maxDistance'} && $config{'sellAuto_distance'}) { + $args->{distance} = $config{'sellAuto_distance'} + round(rand($config{'sellAuto_maxDistance'} - $config{'sellAuto_distance'})); + } else { + $args->{distance} = $config{'sellAuto_distance'} || 3; + } + } + + undef $ai_v{'temp'}{'do_route'}; + if ($field->baseName ne $args->{'npc'}{'map'}) { + $ai_v{'temp'}{'do_route'} = 1; + } else { + my $found = 0; + foreach my $actor (@{$npcsList->getItems()}) { + my $pos = $actor->{pos}; + next if ($actor->{statuses}->{EFFECTSTATE_BURROW}); + if ($pos->{x} == $args->{npc}{pos}{x} && $pos->{y} == $args->{npc}{pos}{y}) { + if (defined $actor->{name}) { + $found = 1; + last; + } + } + } + unless ($found) { + $ai_v{'temp'}{'distance'} = blockDistance($args->{'npc'}{'pos'}, $chars[$config{'char'}]{'pos_to'}); + if (($ai_v{'temp'}{'distance'} > $args->{distance}) && !defined($args->{sentSell})) { + $ai_v{'temp'}{'do_route'} = 1; + } + } + } + if ($ai_v{'temp'}{'do_route'}) { + if ($args->{'warpedToSave'} && !$args->{'mapChanged'} && !timeOut($args->{warpStart}, 8)) { + undef $args->{'warpedToSave'}; + } + + if (shouldUseWarpToSaveMapForBuyOrSell($args)) { + if ($char->{sitting}) { + message T("Standing up to auto-sell\n"), "teleport"; + ai_setSuspend(0); + stand(); + } else { + $args->{'warpedToSave'} = 1; + # If we still haven't warped after a certain amount of time, fallback to walking + $args->{warpStart} = time unless $args->{warpStart}; + message T("Teleporting to auto-sell\n"), "teleport"; + ai_useTeleport(2); + } + $timeout{'ai_sellAuto'}{'time'} = time; + } else { + message TF("Calculating auto-sell route to: %s(%s): %s, %s\n", $maps_lut{$ai_seq_args[0]{'npc'}{'map'}.'.rsw'}, $ai_seq_args[0]{'npc'}{'map'}, $ai_seq_args[0]{'npc'}{'pos'}{'x'}, $ai_seq_args[0]{'npc'}{'pos'}{'y'}), "route"; + ai_route($args->{'npc'}{'map'}, $args->{'npc'}{'pos'}{'x'}, $args->{'npc'}{'pos'}{'y'}, + attackOnRoute => 1, + distFromGoal => $args->{distance}, + noSitAuto => 1); + } + } else { + + if (!exists $args->{'sentNpcTalk'}) { + + # load the real npc location just in case we used standpoint + my $realpos = {}; + getNPCInfo($config{"sellAuto_npc"}, $realpos); + + ai_talkNPC($realpos->{pos}{x}, $realpos->{pos}{y}, $config{sellAuto_npc_steps} || 's'); + + $args->{'sentNpcTalk'} = 1; + $args->{'sentNpcTalk_time'} = time; + + return; + + } elsif (!defined $ai_v{'npc_talk'} || $ai_v{'npc_talk'}{'talk'} ne 'sell') { + if (timeOut($args->{'sentNpcTalk_time'}, $timeout{ai_sellAuto_wait_giveup_npc}{timeout})) { + $args->{'error'} = 'Npc did not respond'; + $args->{'done'} = 1; + } + return; + + } elsif (!exists $args->{'recv_sellList_time'}) { + $args->{'recv_sellList_time'} = time; + return; + + } else { + return unless (timeOut($args->{'recv_sellList_time'}, $timeout{ai_sellAuto_wait_before_sell}{timeout})); + } + + Plugins::callHook('AI_sell_auto'); + + # Form list of items to sell + my @sellItems; + for my $item (@{$char->inventory}) { + next if ($item->{equipped} || !$item->{sellable}); + + my $control = items_control($item->{name}, $item->{nameID}); + + if ($control->{'sell'} && $item->{'amount'} > $control->{keep}) { + my %obj; + $obj{ID} = $item->{ID}; + $obj{amount} = $item->{amount} - $control->{keep}; + push @sellItems, \%obj; + } + } + + if (@sellItems == 0) { + $args->{'sentEmptyList'} = 1; + } + + completeNpcSell(\@sellItems); + + delete $args->{'sentNpcTalk'}; + delete $args->{'sentNpcTalk_time'}; + delete $args->{'recv_sellList_time'}; + + $args->{sentSellPacket_time} = time; + + Plugins::callHook('AI_sell_auto_done'); + } + } +} + +#####AUTO BUY##### +sub processAutoBuy { + return if ($shopstarted || $buyershopstarted); + my $needitem; + if ( + (AI::isIdle || AI::action eq "route" || AI::action eq "follow") + && timeOut($timeout{'ai_buyAuto'}) + && $char->inventory->isReady() + && !AI::inQueue("sellAuto") + && !AI::inQueue("storageAuto") + ) { + my %plugin_args = ( return => 0 ); + Plugins::callHook('AI_buy_auto_start' => \%plugin_args); + return if ($plugin_args{return}); + + undef $ai_v{'temp'}{'found'}; + + for(my $i = 0; exists $config{"buyAuto_$i"}; $i++) { + next if (!$config{"buyAuto_$i"} || !$config{"buyAuto_$i"."_npc"} || $config{"buyAuto_${i}_disabled"}); + my $amount; + if ($config{"buyAuto_$i"} =~ /^\d{3,}$/) { + $amount = $char->inventory->sumByNameID($config{"buyAuto_$i"}, $config{"buyAuto_${i}_onlyIdentified"}); + } + else { + $amount = $char->inventory->sumByName($config{"buyAuto_$i"}, $config{"buyAuto_${i}_onlyIdentified"}); + } + if ( + $config{"buyAuto_$i"."_minAmount"} ne "" && + $config{"buyAuto_$i"."_maxAmount"} ne "" && + (checkSelfCondition("buyAuto_$i")) && + $amount <= $config{"buyAuto_$i"."_minAmount"} && + $amount < $config{"buyAuto_$i"."_maxAmount"} + ) { + $ai_v{'temp'}{'found'} = 1; + my $bai = $config{"buyAuto_$i"}; + if ($needitem eq "") { + $needitem = "$bai"; + } else { + $needitem = "$needitem, $bai"; + } + } + } + $ai_v{'temp'}{'ai_route_index'} = AI::findAction("route"); + if ($ai_v{'temp'}{'ai_route_index'} ne "") { + $ai_v{'temp'}{'ai_route_attackOnRoute'} = AI::args($ai_v{'temp'}{'ai_route_index'})->{'attackOnRoute'}; + } + if (!($ai_v{'temp'}{'ai_route_index'} ne "" && AI::findAction("buyAuto")) && $ai_v{'temp'}{'found'}) { + AI::queue("buyAuto"); + Plugins::callHook('AI_buy_auto_queued'); + } + $timeout{'ai_buyAuto'}{'time'} = time; + } + + if (AI::action eq "buyAuto" && AI::args->{'done'}) { + + if (exists AI::args->{'error'}) { + error AI::args->{'error'}.".\n"; + } + + # buyAuto finished + my $forcedBySell = AI::args->{'forcedBySell'}; + my $forcedByStorage = AI::args->{'forcedByStorage'}; + + AI::dequeue; + Plugins::callHook('AI_buy_auto_done'); + + if ($forcedBySell && $config{storageAuto}) { + AI::queue("storageAuto", {forcedBySell => 1}); + Plugins::callHook('AI_storage_auto_queued'); + + } elsif (!$forcedByStorage && $config{storageAuto}) { + AI::queue("storageAuto", {forcedByBuy => 1}); + Plugins::callHook('AI_storage_auto_queued'); + } + + } elsif (AI::action eq "buyAuto" && timeOut($timeout{ai_buyAuto_wait})) { + Plugins::callHook('AI_buy_auto'); + my $args = AI::args; + + if (exists $args->{sentBuyPacket_time} && exists $args->{index_failed}{$args->{lastIndex}}) { + if (timeOut($args->{sentBuyPacket_time}, $timeout{ai_buyAuto_wait_after_restart}{timeout})) { + delete $args->{sentBuyPacket_time}; + delete $args->{lastIndex}; + delete $args->{distance}; + } + return; + + } elsif (exists $args->{sentBuyPacket_time} && !exists $args->{index_failed}{$args->{lastIndex}}) { + if (exists $args->{recv_buy_packet}) { + delete $args->{sentBuyPacket_time}; + delete $args->{recv_buy_packet}; + $args->{recv_buy_packet_time} = time; + + } elsif (timeOut($args->{sentBuyPacket_time}, $timeout{ai_buyAuto_wait_after_packet_giveup}{timeout})) { + $args->{'error'} = 'Did not received the buy result from server after buy packet was sent'; + $args->{'done'} = 1; + } + return; + + } elsif (exists $args->{recv_buy_packet_time}) { + if (timeOut($args->{recv_buy_packet_time}, $timeout{ai_buyAuto_wait_after_restart}{timeout})) { + delete $args->{recv_buy_packet_time}; + delete $args->{lastIndex}; + delete $args->{distance}; + } + return; + + } + + if (!exists $args->{lastIndex}) { + + delete $args->{index}; + for (my $i = 0; exists $config{"buyAuto_$i"}; $i++) { + next if (!$config{"buyAuto_$i"} || $config{"buyAuto_${i}_disabled"}); + next if ($config{"buyAuto_${i}_maxBase"} =~ /^\d{1,}$/ && $char->{lv} > $config{"buyAuto_${i}_maxBase"}); + next if ($config{"buyAuto_${i}_minBase"} =~ /^\d{1,}$/ && $char->{lv} < $config{"buyAuto_${i}_minBase"}); + # did we already fail to do this buyAuto slot? (only fails in this way if the item is nonexistant) + next if (exists $args->{index_failed}{$i}); + + my $amount; + if ($config{"buyAuto_$i"} =~ /^\d{3,}$/) { + $amount = $char->inventory->sumByNameID($config{"buyAuto_$i"}, $config{"buyAuto_${i}_onlyIdentified"}); + } + else { + $amount = $char->inventory->sumByName($config{"buyAuto_$i"}, $config{"buyAuto_${i}_onlyIdentified"}); + } + + if ($config{"buyAuto_$i"."_maxAmount"} ne "" && $amount < $config{"buyAuto_$i"."_maxAmount"}) { + next if (($config{"buyAuto_$i"."_price"} && ($char->{zeny} < $config{"buyAuto_$i"."_price"})) || ($config{"buyAuto_$i"."_zeny"} && !inRange($char->{zeny}, $config{"buyAuto_$i"."_zeny"}))); + + # get NPC info, use standpoint if provided + $args->{npc} = {}; + my $destination = $config{"buyAuto_$i"."_standpoint"} || $config{"buyAuto_$i"."_npc"}; + getNPCInfo($destination, $args->{npc}); + + # did we succeed to load NPC info from this slot? + # (doesnt check validity of _npc if we used _standpoint...) + if ($args->{npc}{ok}) { + $args->{index} = $i; + } + last; + } + } + + # Failed to load any slots for buyAuto (we're done or they're all invalid) + if (!exists $args->{index}) { + $args->{'done'} = 1; + return; + } + + undef $ai_v{'temp'}{'do_route'}; + if (!$args->{distance}) { + # Calculate variable or fixed (old) distance + if ($config{"buyAuto_$args->{index}"."_standpoint"}) { + $args->{distance} = 1; + } elsif ($config{"buyAuto_".$args->{index}."_minDistance"} && $config{"buyAuto_".$args->{index}."_maxDistance"}) { + $args->{distance} = $config{"buyAuto_$args->{index}"."_minDistance"} + round(rand($config{"buyAuto_$args->{index}"."_maxDistance"} - $config{"buyAuto_$args->{index}"."_minDistance"})); + } else { + $args->{distance} = $config{"buyAuto_$args->{index}"."_distance"}; + } + } + + if ($field->baseName ne $args->{'npc'}{'map'}) { + $ai_v{'temp'}{'do_route'} = 1; + } else { + my $found = 0; + foreach my $actor (@{$npcsList->getItems()}) { + my $pos = $actor->{pos}; + next if ($actor->{statuses}->{EFFECTSTATE_BURROW}); + if ($pos->{x} == $args->{npc}{pos}{x} && $pos->{y} == $args->{npc}{pos}{y}) { + if (defined $actor->{name}) { + $found = 1; + last; + } + } + } + unless ($found) { + $ai_v{'temp'}{'distance'} = blockDistance($args->{'npc'}{'pos'}, $chars[$config{'char'}]{'pos_to'}); + if (($ai_v{'temp'}{'distance'} > $args->{distance}) && !exists $args->{'sentNpcTalk'}) { + $ai_v{'temp'}{'do_route'} = 1; + } + } + } + + if ($ai_v{'temp'}{'do_route'}) { + if ($args->{warpedToSave} && !$args->{mapChanged} && !timeOut($args->{warpStart}, 8)) { + undef $args->{warpedToSave}; + } + + my $msgneeditem; + if (shouldUseWarpToSaveMapForBuyOrSell($args)) { + if ($char->{sitting}) { + message T($msgneeditem."Standing up to auto-buy\n"), "teleport"; + ai_setSuspend(0); + stand(); + } else { + $args->{warpedToSave} = 1; + if ($needitem ne "") { + $msgneeditem = "Auto-buy: $needitem\n"; + } + # If we still haven't warped after a certain amount of time, fallback to walking + $args->{warpStart} = time unless $args->{warpStart}; + message T($msgneeditem."Teleporting to auto-buy\n"), "teleport"; + ai_useTeleport(2); + } + $timeout{ai_buyAuto_wait}{time} = time; + + } else { + if ($needitem ne "") { + $msgneeditem = "Auto-buy: $needitem\n"; + } + message TF($msgneeditem."Calculating auto-buy route to: %s (%s): %s, %s\n", $maps_lut{$args->{npc}{map}.'.rsw'}, $args->{npc}{map}, $args->{npc}{pos}{x}, $args->{npc}{pos}{y}), "route"; + ai_route($args->{npc}{map}, $args->{npc}{pos}{x}, $args->{npc}{pos}{y}, + attackOnRoute => 1, + distFromGoal => $args->{distance}); + } + return; + } + } + + if (!exists $args->{lastIndex}) { + $args->{lastIndex} = $args->{index}; + return; + + } elsif (!exists $args->{'sentNpcTalk'}) { + + # load the real npc location just in case we used standpoint + my $realpos = {}; + getNPCInfo($config{"buyAuto_".$args->{lastIndex}."_npc"}, $realpos); + + if ( $config{"buyAuto_".$args->{lastIndex}."_isMarket"} ) { + ai_talkNPC($realpos->{pos}{x}, $realpos->{pos}{y}, $config{"buyAuto_".$args->{lastIndex}."_npc_steps"} || undef); + } else { + ai_talkNPC($realpos->{pos}{x}, $realpos->{pos}{y}, $config{"buyAuto_".$args->{lastIndex}."_npc_steps"} || 'b'); + } + + $args->{'sentNpcTalk'} = 1; + $args->{'sentNpcTalk_time'} = time; + + return; + + } elsif (!defined $ai_v{'npc_talk'} || $ai_v{'npc_talk'}{'talk'} ne 'store') { + if (timeOut($args->{'sentNpcTalk_time'}, $timeout{ai_buyAuto_wait_giveup_npc}{timeout})) { + $args->{'error'} = 'Npc did not respond'; + $args->{'done'} = 1; + } + return; + + } elsif (!exists $args->{'recv_buyList_time'}) { + $args->{'recv_buyList_time'} = time; + return; + + } else { + return unless (timeOut($args->{'recv_buyList_time'}, $timeout{ai_buyAuto_wait_before_buy}{timeout})); + } + + my @buyList; + + my $item; + if ($config{"buyAuto_".$args->{lastIndex}} =~ /^\d{3,}$/) { + $item = $storeList->getByNameID( $config{"buyAuto_".$args->{lastIndex}} ); + $args->{'nameID'} = $config{"buyAuto_".$args->{lastIndex}} if (defined $item); + } + else { + $item = $storeList->getByName( $config{"buyAuto_".$args->{lastIndex}} ); + $args->{'nameID'} = $item->{nameID} if (defined $item); + } + + if (!exists $args->{'nameID'}) { + $args->{index_failed}{$args->{lastIndex}} = 1; + error "buyAuto index ".$args->{lastIndex}." (".$config{"buyAuto_".$args->{lastIndex}}.") failed, item doesn't exist in npc sell list.\n", "npc"; + + } else { + my $maxbuy = ($config{"buyAuto_".$args->{lastIndex}."_price"}) ? int($char->{zeny}/$config{"buyAuto_$args->{index}"."_price"}) : 30000; # we assume we can buy 30000, when price of the item is set to 0 or undef + my $needbuy = $config{"buyAuto_".$args->{lastIndex}."_maxAmount"}; + + my $inv_amount = $char->inventory->sumByNameID($args->{'nameID'}, $config{"buyAuto_".$args->{lastIndex}."_onlyIdentified"}); + + $needbuy -= $inv_amount; + + my $buy_amount = ($maxbuy > $needbuy) ? $needbuy : $maxbuy; + + # support to market + if ($item->{amount} && $item->{amount} < $buy_amount) { + $buy_amount = $item->{amount}; + } + + my $batchSize = $config{"buyAuto_".$args->{lastIndex}."_batchSize"}; + + if ($batchSize && $batchSize < $buy_amount) { + + while ($buy_amount > 0) { + my $amount = ($buy_amount > $batchSize) ? $batchSize : $buy_amount; + my %buy = ( + itemID => $args->{'nameID'}, + amount => $amount + ); + push(@buyList, \%buy); + $buy_amount -= $amount; + } + + } else { + my %buy = ( + itemID => $args->{'nameID'}, + amount => $buy_amount + ); + push(@buyList, \%buy); + } + } + + completeNpcBuy(\@buyList); + + delete $args->{'nameID'}; + delete $args->{'sentNpcTalk'}; + delete $args->{'sentNpcTalk_time'}; + delete $args->{'recv_buyList_time'}; + + $args->{sentBuyPacket_time} = time; + } +} + +##### AUTO-CART ADD/GET #### +sub processAutoCart { + if ((AI::isIdle || AI::is(qw/route move buyAuto follow sitAuto items_take items_gather/))) { + my $timeout = $timeout{ai_cartAutoCheck}{timeout} || 2; + if (timeOut($AI::Timeouts::autoCart, $timeout) && $char->cartActive) { + my @addItems; + my @getItems; + my $max; + + if ($config{cartMaxWeight} && $char->cart->{weight} < $config{cartMaxWeight}) { + for my $invItem (@{$char->inventory}) { + next if ($invItem->{broken} && $invItem->{type} == 7); # dont auto-cart add pet eggs in use + next if ($invItem->{equipped}); + my $control = items_control($invItem->{name}, $invItem->{nameID}); + if ($control->{cart_add} && $invItem->{amount} > $control->{keep}) { + my %obj; + $obj{index} = $invItem->{binID}; + $obj{amount} = $invItem->{amount} - $control->{keep}; + push @addItems, \%obj; + debug "Scheduling $invItem->{name} ($invItem->{binID}) x $obj{amount} for adding to cart\n", "ai_autoCart"; + } + } + cartAdd(\@addItems); + } + + for my $cartItem (@{$char->cart}) { + my $control = items_control($cartItem->{name}, $cartItem->{nameID}); + next unless ($control->{cart_get}); + + my $invItem = $char->inventory->getByName($cartItem->{name}); + my $amount; + if (!$invItem) { + $amount = $control->{keep}; + } elsif ($invItem->{amount} < $control->{keep}) { + $amount = $control->{keep} - $invItem->{amount}; + } + if ($amount > $cartItem->{amount}) { + $amount = $cartItem->{amount}; + } + if ($amount > 0) { + my %obj; + $obj{index} = $cartItem->{binID}; + $obj{amount} = $amount; + push @getItems, \%obj; + debug "Scheduling $cartItem->{name} ($cartItem->{ID}) x $obj{amount} for getting from cart\n", "ai_autoCart"; + } + } + cartGet(\@getItems); + $AI::Timeouts::autoCart = time; + } + } +} + +##### LOCKMAP ##### +sub processLockMap { + if (AI::isIdle && $config{'lockMap'} + && !$ai_v{'sitAuto_forcedBySitCommand'} + && ($field->baseName ne $config{'lockMap'} + || ($config{'lockMap_x'} && ($char->{pos_to}{x} < $config{'lockMap_x'} - $config{'lockMap_randX'} || $char->{pos_to}{x} > $config{'lockMap_x'} + $config{'lockMap_randX'})) + || ($config{'lockMap_y'} && ($char->{pos_to}{y} < $config{'lockMap_y'} - $config{'lockMap_randY'} || $char->{pos_to}{y} > $config{'lockMap_y'} + $config{'lockMap_randY'})) + )) { + + unless ($maps_lut{$config{'lockMap'}.'.rsw'}) { + error TF("Invalid map specified for lockMap - map %s doesn't exist\n", $config{'lockMap'}); + $config{'lockMap'} = ''; + } else { + my %args; + Plugins::callHook('AI/lockMap', \%args); + unless ($args{'return'}) { + my ($lockX, $lockY, $i); + eval { + my $lockField = new Field(name => $config{'lockMap'}, loadWeightMap => 0); + $i = 500; + if ($config{'lockMap_x'} || $config{'lockMap_y'}) { + do { + $lockX = int(rand($field->width + 1)) if (!$config{'lockMap_x'} && $config{'lockMap_y'}); + $lockX = int($config{'lockMap_x'}) if ($config{'lockMap_x'}); + $lockX += (int(rand(2*$config{'lockMap_randX'} + 1) - $config{'lockMap_randX'})) if ($config{'lockMap_x'} && $config{'lockMap_randX'}); + + $lockY = int(rand($field->width + 1)) if (!$config{'lockMap_y'} && $config{'lockMap_x'}); + $lockY = int($config{'lockMap_y'}) if ($config{'lockMap_y'}); + $lockY += (int(rand(2*$config{'lockMap_randY'} + 1) - $config{'lockMap_randY'})) if ($config{'lockMap_y'} && $config{'lockMap_randY'}); + } while (--$i && !$lockField->isWalkable($lockX, $lockY)); + } + }; + if (caught('FileNotFoundException') || !$i) { + error T("Invalid coordinates specified for lockMap, coordinates are unwalkable\n"); + $config{'lockMap'} = ''; + } else { + my $attackOnRoute = 2; + $attackOnRoute = 1 if ($config{'attackAuto_inLockOnly'} == 1); + $attackOnRoute = 0 if ($config{'attackAuto_inLockOnly'} > 1); + if (defined $lockX || defined $lockY) { + message TF("Calculating lockMap route to: %s(%s): %s, %s\n", $maps_lut{$config{'lockMap'}.'.rsw'}, $config{'lockMap'}, $lockX, $lockY), "route"; + } else { + message TF("Calculating lockMap route to: %s(%s)\n", $maps_lut{$config{'lockMap'}.'.rsw'}, $config{'lockMap'}), "route"; + } + ai_route( + $config{'lockMap'}, + $lockX, + $lockY, + attackOnRoute => $attackOnRoute, + isToLockMap => 1 + ); + } + } + } + } +} + +sub processRescueSlave { + if ( + (AI::isIdle || (AI::is('route') && AI::args()->{isRandomWalk})) + && $char->{slaves} + ) { + my $slave = AI::SlaveManager::mustRescue(); + if (defined $slave) { + AI::dequeue() while (AI::is(qw/move route mapRoute/) && AI::args()->{isRandomWalk}); + ai_route( + $field->baseName, + $slave->{pos_to}{x}, + $slave->{pos_to}{y}, + distFromGoal => ($config{$slave->{configPrefix}.'followDistanceMin'} || 3), + attackOnRoute => 1, + noSitAuto => 1, + isSlaveRescue => 1 + ); + warning TF("%s got lost during randomWalk (distance: %d) - Rescuing it.\n", $slave, $slave->blockDistance_master), 'slave'; + return; + } + } +} + +# route_randomWalk_stopDuringSlaveAttack +sub processRandomWalk_stopDuringSlaveAttack { + if (AI::is('route') && AI::args()->{isRandomWalk} + && $char->{slaves} + && !AI::SlaveManager::isIdle() + ){ + my $slave = AI::SlaveManager::mustStopForAttack(); + if (defined $slave) { + message TF("%s started attacking during randomWalk - Stoping movement for it.\n", $slave), 'slave'; + # TODO: Since meetingposition takes into account the movement of the character + # we shoudl probably not stop it, just not send new move commands after the current one + $char->sendAttackStop; + # TODO: This should probably just pause route instead of dequeuing it + AI::dequeue() while (AI::is(qw/move route mapRoute/) && AI::args()->{isRandomWalk}); + } + } +} + +sub processMoveNearSlave { + if ( + AI::isIdle + && $char->{slaves} + && !AI::SlaveManager::isIdle() + ) { + + my $slave = AI::SlaveManager::mustMoveNear(); + if (defined $slave) { + ai_route( + $field->baseName, + $slave->{pos_to}{x}, + $slave->{pos_to}{y}, + distFromGoal => ($config{$slave->{configPrefix}.'moveNearWhenIdle_minDistance'} || 4), + attackOnRoute => 1, + noSitAuto => 1, + isMoveNearSlave => 1 + ); + message TF("%s moved too far - Moving near it.\n", $slave), 'slave'; + } + } +} + +##### RANDOM WALK ##### +sub processRandomWalk { + if (AI::isIdle && (AI::SlaveManager::isIdle()) && $config{route_randomWalk} && !$ai_v{sitAuto_forcedBySitCommand} + && (!$field->isCity || $config{route_randomWalk_inTown}) + && length($field->{rawMap}) + ){ + if ($char->{pos}{x} == $config{'lockMap_x'} && !($config{'lockMap_randX'} > 0) && ($char->{pos}{y} == $config{'lockMap_y'} && !($config{'lockMap_randY'} >0))) { + error T("Coordinate lockmap is used; randomWalk disabled\n"); + $config{'route_randomWalk'} = 0; + return; + } + + my %plugin_args; + $plugin_args{return} = 0; + Plugins::callHook('ai_processRandomWalk' => \%plugin_args); + return if ($plugin_args{return}); + + my ($randX, $randY); + my $i = 500; + do { + $randX = int(rand($field->width-1)+1); + $randX = $config{'lockMap_x'} if ($char->{pos}{x} == $config{'lockMap_x'} && !($config{'lockMap_randX'} > 0)); + $randX = $config{'lockMap_x'} - $config{'lockMap_randX'} + int(rand(2*$config{'lockMap_randX'}+1)) if ($config{'lockMap_x'} ne '' && $config{'lockMap_randX'} >= 0); + $randY = int(rand($field->height-1)+1); + $randY = $config{'lockMap_y'} if ($char->{pos}{y} == $config{'lockMap_y'} && !($config{'lockMap_randY'} > 0)); + $randY = $config{'lockMap_y'} - $config{'lockMap_randY'} + int(rand(2*$config{'lockMap_randY'}+1)) if ($config{'lockMap_y'} ne '' && $config{'lockMap_randY'} >= 0); + } while (--$i && (!$field->isWalkable($randX, $randY) || $randX == 0 || $randY == 0)); + if (!$i) { + error T("Invalid coordinates specified for randomWalk (coordinates are unwalkable); randomWalk disabled\n"); + $config{'route_randomWalk'} = 0; + } else { + message TF("Calculating random route to: %s: %s, %s\n", $field->descString(), $randX, $randY), "route"; + ai_route( + $field->baseName, + $randX, + $randY, + maxRouteTime => $config{route_randomWalk_maxRouteTime}, + attackOnRoute => (defined $config{attackAuto}) ? $config{attackAuto} : 2, + noMapRoute => ($config{route_randomWalk} == 2 ? 1 : 0), + isRandomWalk => 1 + ); + } + } +} + +##### FOLLOW ##### +sub processFollow { + # FIXME: Should use actors list to determine who and where is the master + # TODO: follow should be a 'mode' rather then a sequence, hence all + # var/flag about follow should be moved to %ai_v + + if (!$config{follow}) { + AI::clear("follow") if (AI::findAction("follow") ne undef); # if follow is disabled and there's still "follow" in AI queue, remove it + return; + } + + return unless ( + (AI::isIdle || (AI::is('route') && AI::args()->{isRandomWalk})) || + (AI::action eq "follow") || + ((AI::action eq "route" && AI::action(1) eq "follow") || (AI::action eq "move" && AI::action(2) eq "follow")) + ); + + # stop follow when talking with NPC + if (AI::action eq 'route' && defined(AI::args(0)->getSubtask())) { + my $rrr = AI::args(0)->getSubtask(); + return if ($rrr->getName() eq 'TalkNPC'); + } + if ($config{'sitAuto_follow'} && (percent_hp($char) < $config{'sitAuto_hp_lower'} || percent_sp($char) < $config{'sitAuto_sp_lower'}) && $field->isCity) { + my $action = AI::action; + if ($action eq "sitting" && !$char->{sitting} && ($char->{skills}{NV_BASIC}{lv} >= 3 || $char->{skills}{SU_BASIC_SKILL}{lv} == 1)){ + sit(); + } + return; + } + + my $followIndex; + if (($followIndex = AI::findAction("follow")) eq "") { + # ai_follow will determine if the Target is 'follow-able' + return if (!ai_follow($config{followTarget})); + $followIndex = AI::findAction("follow"); + } + my $args = AI::args($followIndex); + + # if we are not following now but master is in the screen... + if (!defined $args->{'ID'}) { + for my Actor::Player $player (@$playersList) { + if (($player->name eq $config{followTarget}) && !$player->{'dead'}) { + $args->{'ID'} = $player->{ID}; + $args->{'following'} = 1; + $args->{'name'} = $player->name; + AI::clear(qw/move route/); + message TF("Found my master - %s\n", $player->name), "follow"; + last; + } + } + } elsif (!$args->{'following'} && $players{$args->{'ID'}} && %{$players{$args->{'ID'}}} && !${$players{$args->{'ID'}}}{'dead'} && ($players{$args->{'ID'}}->name eq $config{followTarget})) { + $args->{'following'} = 1; + delete $args->{'ai_follow_lost'}; + AI::clear(qw/move route/); + message TF("Found my master!\n"), "follow" + } + + # if we are not doing anything else now... + if (AI::action eq "follow") { + if (AI::args->{'suspended'}) { + if (AI::args->{'ai_follow_lost'}) { + AI::args->{'ai_follow_lost_end'}{'time'} += time - AI::args->{'suspended'}; + } + delete AI::args->{'suspended'}; + } + + # if we are not doing anything else now... + if (!$args->{ai_follow_lost}) { + my $ID = $args->{ID}; + my $player = $players{$ID}; + + if ($args->{following} && $player->{pos_to}) { + my $dist = blockDistance($char->{pos_to}, $player->{pos_to}); + if ($dist > $config{followDistanceMax} && timeOut($args->{move_timeout}, 0.25)) { + $args->{move_timeout} = time; + $args->{masterLastMoveTime} = $player->{time_move}; + + ai_route( + $field->baseName, + $player->{pos_to}{x}, + $player->{pos_to}{y}, + attackOnRoute => 1, + isFollow => 1, + distFromGoal => $config{followDistanceMin} + ); + } + } + + if ($args->{following} && $player && %{$player}) { + if ($config{'followSitAuto'} && $players{$args->{'ID'}}{'sitting'} == 1 && $chars[$config{'char'}]{'sitting'} == 0) { + sit(); + } + + my $dx = $args->{'last_pos_to'}{'x'} - $players{$args->{'ID'}}{'pos_to'}{'x'}; + my $dy = $args->{'last_pos_to'}{'y'} - $players{$args->{'ID'}}{'pos_to'}{'y'}; + $args->{'last_pos_to'}{'x'} = $players{$args->{'ID'}}{'pos_to'}{'x'}; + $args->{'last_pos_to'}{'y'} = $players{$args->{'ID'}}{'pos_to'}{'y'}; + if ($dx != 0 || $dy != 0) { + lookAtPosition($players{$args->{'ID'}}{'pos_to'}) if ($config{'followFaceDirection'}); + } + } + } + } elsif (((AI::action eq "route" && AI::action(1) eq "follow") || (AI::action eq "move" && AI::action(2) eq "follow")) && !$args->{ai_follow_lost}) { + my $ID = $args->{ID}; + my $player = $players{$ID}; + if ( + $args->{following} && + $player && + %{$player} && + $player->{pos_to} && + $args->{masterLastMoveTime} && + $args->{masterLastMoveTime} != $player->{time_move} + ) { + debug "Master $player has moved since we started routing to it - Adjusting route\n", "ai_attack"; + AI::dequeue; + AI::dequeue if (AI::action eq "route"); + + $args->{move_timeout} = time; + $args->{masterLastMoveTime} = $player->{time_move}; + + ai_route( + $field->baseName, + $player->{pos_to}{x}, + $player->{pos_to}{y}, + attackOnRoute => 1, + isFollow => 1, + distFromGoal => $config{followDistanceMin} + ); + } + } + + if (AI::action eq "follow" && $args->{'following'} && ( + ( $players{$args->{'ID'}} && $players{$args->{'ID'}}{'dead'} ) || ( + ( !$players{$args->{'ID'}} || !%{$players{$args->{'ID'}}} ) + && $players_old{$args->{'ID'}} && $players_old{$args->{'ID'}}{'dead'} + ) + )) { + message T("Master died. I'll wait here.\n"), "party"; + delete $args->{'following'}; + } elsif ($args->{'following'} && ( !$players{$args->{'ID'}} || !%{$players{$args->{'ID'}}} )) { + message T("I lost my master\n"), "follow"; + if ($config{'followBot'}) { + message T("Trying to get him back\n"), "follow"; + sendMessage($messageSender, "pm", "move $chars[$config{'char'}]{'pos_to'}{'x'} $chars[$config{'char'}]{'pos_to'}{'y'}", $config{followTarget}); + } + + delete $args->{'following'}; + + if ($players_old{$args->{'ID'}} && $players_old{$args->{'ID'}}{'disconnected'}) { + message T("My master disconnected\n"), "follow"; + + } elsif ($players_old{$args->{'ID'}} && $players_old{$args->{'ID'}}{'teleported'}) { + delete $args->{'ai_follow_lost_warped'}; + delete $ai_v{'temp'}{'warp_pos'}; + + # Check to see if the player went through a warp portal and follow him through it. + my $pos = calcPosition($players_old{$args->{'ID'}}); + my $oldPos = $players_old{$args->{'ID'}}->{pos}; + my (@blocks, $found); + my %vec; + + debug "Last time i saw, master was moving from ($oldPos->{x}, $oldPos->{y}) to ($pos->{x}, $pos->{y})\n", "follow"; + + # We must check the ground about 9x9 area of where we last saw our master. That's the only way + # to ensure he walked through a warp portal. The range is because of lag in some situations. + @blocks = calcRectArea2($pos->{x}, $pos->{y}, 4, 0); + foreach (@blocks) { + next unless (whenGroundStatus($_, "Warp Portal")); + # We must certify that our master was walking towards that portal. + getVector(\%vec, $_, $oldPos); + next unless (checkMovementDirection($oldPos, \%vec, $_, 15)); + $found = $_; + last; + } + + if ($found) { + %{$ai_v{'temp'}{'warp_pos'}} = %{$found}; + $args->{'ai_follow_lost_warped'} = 1; + $args->{'ai_follow_lost'} = 1; + $args->{'ai_follow_lost_end'}{'timeout'} = $timeout{'ai_follow_lost_end'}{'timeout'}; + $args->{'ai_follow_lost_end'}{'time'} = time; + $args->{'ai_follow_lost_vec'} = {}; + getVector($args->{'ai_follow_lost_vec'}, $players_old{$args->{'ID'}}{'pos_to'}, $chars[$config{'char'}]{'pos_to'}); + + } else { + message T("My master teleported\n"), "follow", 1; + } + + } elsif ($players_old{$args->{'ID'}} && $players_old{$args->{'ID'}}{'disappeared'}) { + message T("Trying to find lost master\n"), "follow", 1; + + delete $args->{'ai_follow_lost_char_last_pos'}; + delete $args->{'follow_lost_portal_tried'}; + $args->{'ai_follow_lost'} = 1; + $args->{'ai_follow_lost_end'}{'timeout'} = $timeout{'ai_follow_lost_end'}{'timeout'}; + $args->{'ai_follow_lost_end'}{'time'} = time; + $args->{'ai_follow_lost_vec'} = {}; + getVector($args->{'ai_follow_lost_vec'}, $players_old{$args->{'ID'}}{'pos_to'}, $chars[$config{'char'}]{'pos_to'}); + + #check if player went through portal + my $first = 1; + my $foundID; + my $smallDist; + foreach (@portalsID) { + next if (!defined $_); + $ai_v{'temp'}{'dist'} = blockDistance($players_old{$args->{'ID'}}{'pos_to'}, $portals{$_}{'pos'}); + if ($ai_v{'temp'}{'dist'} <= 7 && ($first || $ai_v{'temp'}{'dist'} < $smallDist)) { + $smallDist = $ai_v{'temp'}{'dist'}; + $foundID = $_; + undef $first; + } + } + $args->{'follow_lost_portalID'} = $foundID; + } else { + message T("Don't know what happened to Master\n"), "follow", 1; + } + } + + ##### FOLLOW-LOST ##### + + if (AI::action eq "follow" && $args->{'ai_follow_lost'}) { + if ($args->{'ai_follow_lost_char_last_pos'}{'x'} == $chars[$config{'char'}]{'pos_to'}{'x'} && $args->{'ai_follow_lost_char_last_pos'}{'y'} == $chars[$config{'char'}]{'pos_to'}{'y'}) { + $args->{'lost_stuck'}++; + } else { + delete $args->{'lost_stuck'}; + } + %{AI::args->{'ai_follow_lost_char_last_pos'}} = %{$chars[$config{'char'}]{'pos_to'}}; + + if (timeOut($args->{'ai_follow_lost_end'})) { + delete $args->{'ai_follow_lost'}; + message T("Couldn't find master, giving up\n"), "follow"; + + } elsif ($players_old{$args->{'ID'}} && $players_old{$args->{'ID'}}{'disconnected'}) { + delete AI::args->{'ai_follow_lost'}; + message T("My master disconnected\n"), "follow"; + + } elsif ($args->{'ai_follow_lost_warped'} && $ai_v{'temp'}{'warp_pos'} && %{$ai_v{'temp'}{'warp_pos'}}) { + my $pos = $ai_v{'temp'}{'warp_pos'}; + + if (!$field->canMove($char->{pos_to}, $pos)) { + ai_route( + $field->baseName, $pos->{x}, $pos->{y}, + attackOnRoute => 0, + isFollow => 1 + ); #distFromGoal => 0); + } else { + my (%vec, %pos_to); + my $dist = distance($char->{pos_to}, $pos); + + stand() if ($char->{sitting}); + getVector(\%vec, $pos, $char->{pos_to}); + moveAlongVector(\%pos_to, $char->{pos_to}, \%vec, $dist); + $char->move(@pos_to{qw(x y)}); + $pos->{x} = int $pos_to{x}; + $pos->{y} = int $pos_to{y}; + + } + delete $args->{'ai_follow_lost_warped'}; + delete $ai_v{'temp'}{'warp_pos'}; + + message TF("My master warped at (%s, %s) - moving to warp point\n", $pos->{x}, $pos->{y}), "follow"; + + } elsif ($players_old{$args->{'ID'}} && $players_old{$args->{'ID'}}{'teleported'}) { + delete AI::args->{'ai_follow_lost'}; + message T("My master teleported\n"), "follow"; + + } elsif ($args->{'lost_stuck'}) { + if ($args->{'follow_lost_portalID'} eq "") { + moveAlongVector($ai_v{'temp'}{'pos'}, $chars[$config{'char'}]{'pos_to'}, $args->{'ai_follow_lost_vec'}, $config{'followLostStep'} / ($args->{'lost_stuck'} + 1)); + $char->move(@{$ai_v{temp}{pos}}{qw(x y)}); + } + } else { + my $portalID = $args->{follow_lost_portalID}; + if ($args->{'follow_lost_portalID'} ne "" && $portalID) { + if ($portals{$portalID} && !$args->{'follow_lost_portal_tried'}) { + $args->{'follow_lost_portal_tried'} = 1; + %{$ai_v{'temp'}{'pos'}} = %{$portals{$args->{'follow_lost_portalID'}}{'pos'}}; + ai_route( + $field->baseName, $ai_v{'temp'}{'pos'}{'x'}, $ai_v{'temp'}{'pos'}{'y'}, + attackOnRoute => 1, + isFollow => 1 + ); + } + } else { + moveAlongVector($ai_v{'temp'}{'pos'}, $chars[$config{'char'}]{'pos_to'}, $args->{'ai_follow_lost_vec'}, $config{'followLostStep'}); + $char->move(@{$ai_v{temp}{pos}}{qw(x y)}); + } + } + } + + # Use party information to find master + if (!exists $args->{following} && !exists $args->{ai_follow_lost}) { + ai_partyfollow(); + } + + Plugins::callHook('ai_follow', $args); +} + +##### SITAUTO-IDLE ##### +sub processSitAutoIdle { + if ($config{sitAuto_idle}) { + if (!AI::isIdle) { + $timeout{ai_sit_idle}{time} = time; + } + + if (($char->{skills}{NV_BASIC}{lv} >= 3 || $char->{skills}{SU_BASIC_SKILL}{lv} == 1) && !$char->{sitting} && timeOut($timeout{ai_sit_idle}) + && (!$config{shopAuto_open} || timeOut($timeout{ai_shop})) ) { + sit(); + } + } +} + +##### SIT AUTO ##### +sub processSitAuto { + my $weight = percent_weight($char); + my $action = AI::action; + my $lower_ok = (percent_hp($char) >= $config{'sitAuto_hp_lower'} && percent_sp($char) >= $config{'sitAuto_sp_lower'}); + my $upper_ok = (percent_hp($char) >= $config{'sitAuto_hp_upper'} && percent_sp($char) >= $config{'sitAuto_sp_upper'}); + + if ($ai_v{'sitAuto_forceStop'} && $lower_ok) { + $ai_v{'sitAuto_forceStop'} = 0; + } + + # Sit if we're not already sitting + if ($action eq "sitAuto" && !$char->{sitting} && ($char->{skills}{NV_BASIC}{lv} >= 3 || $char->{skills}{SU_BASIC_SKILL}{lv} == 1) && + !ai_getAggressives() && ($weight < 50 || $config{'sitAuto_over_50'})) { + debug "sitAuto - sit\n", "sitAuto"; + sit(); + + } elsif ($action eq "sitAuto" && $ai_v{'sitAuto_forceStop'}) { + AI::dequeue; + stand() if (!AI::isIdle && !AI::is(qw(follow sitting clientSuspend)) && !$config{'sitAuto_idle'} && $char->{sitting}); + + # Stand if our HP is high enough + } elsif ($action eq "sitAuto" && $upper_ok) { + if ($timeout{ai_safe_stand_up}{timeout} && !isSafe()) { + if (!$timeout{ai_safe_stand_up}{passed} + || ($timeout{ai_safe_stand_up}{passed} && timeOut($timeout{ai_safe_stand_up}{time}, $timeout{ai_safe_stand_up}{timeout} + 1)) + ) { + $timeout{ai_safe_stand_up}{time} = time; + $timeout{ai_safe_stand_up}{passed} = 1; + return; + } elsif ($timeout{ai_safe_stand_up}{passed} && !timeOut($timeout{ai_safe_stand_up})) { + return; + } elsif ($timeout{ai_safe_stand_up}{passed} && timeOut($timeout{ai_safe_stand_up})) { + $timeout{ai_safe_stand_up}{time} = 0; + $timeout{ai_safe_stand_up}{passed} = 0; + } + } + AI::dequeue; + debug "HP is now > $config{sitAuto_hp_upper}\n", "sitAuto"; + stand() if (!AI::isIdle && !AI::is(qw(follow sitting clientSuspend)) && !$config{'sitAuto_idle'} && $char->{sitting}); + + } elsif (!$ai_v{'sitAuto_forceStop'} && ($weight < 50 || $config{'sitAuto_over_50'}) && AI::action ne "sitAuto" && ($char->{skills}{NV_BASIC}{lv} >= 3 || $char->{skills}{SU_BASIC_SKILL}{lv} == 1)) { + if ($action eq "" || $action eq "follow" + || ($action eq "route" && !AI::args->{noSitAuto}) + || ($action eq "mapRoute" && !AI::args->{noSitAuto}) + ) { + if (!AI::inQueue("attack") && !ai_getAggressives() + && !AI::inQueue("sitAuto") # do not queue sitAuto if there is an existing sitAuto sequence + && (percent_hp($char) < $config{'sitAuto_hp_lower'} || percent_sp($char) < $config{'sitAuto_sp_lower'})) { + AI::queue("sitAuto"); + debug "Auto-sitting\n", "sitAuto"; + } + } + } +} + +##### AUTO-COMMAND USE ##### +sub processAutoCommandUse { + if (AI::isIdle || AI::is(qw(route mapRoute follow sitAuto take items_gather items_take attack skill_use))) { + my $i = 0; + while (exists $config{"doCommand_$i"}) { + if ($config{"doCommand_$i"} && checkSelfCondition("doCommand_$i")) { + Commands::run($config{"doCommand_$i"}); + $ai_v{"doCommand_$i"."_time"} = time; + my $cmd_prefix = $config{"doCommand_$i"}; + debug qq~Auto-Command use: $cmd_prefix\n~, "ai"; + last; + } + $i++; + } + } +} + +##### AUTO-ITEM USE ##### +sub processAutoItemUse { + if ((AI::isIdle || AI::is(qw(route mapRoute follow sitAuto take items_gather items_take attack skill_use))) + && timeOut($timeout{ai_item_use_auto})) { + my $i = 0; + while (exists $config{"useSelf_item_$i"}) { + if ($config{"useSelf_item_${i}_timeout"} eq "") {$config{"useSelf_item_${i}_timeout"} = 0;} + if ($config{"useSelf_item_$i"} && checkSelfCondition("useSelf_item_$i")) { + my $item = $char->inventory->getByNameList($config{"useSelf_item_$i"}); + if ($item) { + $messageSender->sendItemUse($item->{ID}, $accountID); + $ai_v{"useSelf_item_$i"."_time"} = time; + $timeout{ai_item_use_auto}{time} = time; + debug qq~Auto-item use: $item->{name}\n~, "ai"; + last; + } elsif ($config{"useSelf_item_${i}_dcOnEmpty"} && $char->inventory->isReady()) { + error TF("Disconnecting on empty %s!\n", $config{"useSelf_item_$i"}); + chatLog("k", TF("Disconnecting on empty %s!\n", $config{"useSelf_item_$i"})); + quit(); + } + } + $i++; + } + } +} + +##### AUTO-SKILL USE ##### +sub processAutoSkillUse { + if (AI::isIdle || AI::is(qw(route mapRoute follow sitAuto take items_gather items_take attack teleport) ) + || (AI::action eq "skill_use" && AI::args->{tag} eq "attackSkill")) { + my %self_skill; + for (my $i = 0; exists $config{"useSelf_skill_$i"}; $i++) { + if ($config{"useSelf_skill_$i"} && checkSelfCondition("useSelf_skill_$i")) { + $ai_v{"useSelf_skill_$i"."_time"} = time; + $self_skill{skillObject} = Skill->new(auto => $config{"useSelf_skill_$i"}); + $self_skill{ID} = $self_skill{skillObject}->getHandle(); + $self_skill{owner} = $self_skill{skillObject}->getOwner(); + unless ($self_skill{ID}) { + # we will never get here, if the skill doesn't exist then checkSelfCondition will return false already for $char->getSkillLevel($skill) + error "Unknown skill name '".$config{"useSelf_skill_$i"}."' in useSelf_skill_$i\n"; + configModify("useSelf_skill_${i}_disabled", 1); + next; + } + $self_skill{lvl} = $config{"useSelf_skill_$i"."_lvl"} || $char->getSkillLevel($self_skill{skillObject}); + $self_skill{maxCastTime} = $config{"useSelf_skill_$i"."_maxCastTime"}; + $self_skill{minCastTime} = $config{"useSelf_skill_$i"."_minCastTime"}; + $self_skill{prefix} = "useSelf_skill_$i"; + last; + } + } + if ($config{useSelf_skill_smartHeal} && $self_skill{ID} eq "AL_HEAL" && !$config{$self_skill{prefix}."_noSmartHeal"}) { + my $smartHeal_lv = 1; + my $hp_diff = $char->{hp_max} - $char->{hp}; + my $meditatioBonus = 1; + $meditatioBonus = 1 + int(($char->{skills}{HP_MEDITATIO}{lv} * 2) / 100) if ($char->{skills}{HP_MEDITATIO}); + for (my $i = 1; $i <= $char->{skills}{$self_skill{ID}}{lv}; $i++) { + my ($sp_req, $amount); + + $smartHeal_lv = $i; + $sp_req = 10 + ($i * 3); + if ($config{useSelf_skill_smartHeal} == 2) { + $amount = (int(($char->{lv} + $char->{int} + $char->{'int_bonus'}) / 5) * 30) * ($i / 10) * (1 + $meditatioBonus) + ($char->{'attack_magic_min'}); + } else { + $amount = (int(($char->{lv} + $char->{int}) / 8) * (4 + $i * 8)) * $meditatioBonus; + } + if ($char->{sp} < $sp_req) { + $smartHeal_lv--; + last; + } + last if ($amount >= $hp_diff); + } + $self_skill{lvl} = $smartHeal_lv; + } + if ($config{$self_skill{prefix}."_smartEncore"} && + $char->{encoreSkill} && + $char->{encoreSkill}->getHandle() eq $self_skill{ID}) { + # Use Encore skill instead if applicable + $self_skill{ID} = 'BD_ENCORE'; + $self_skill{skillObject} = Skill->new(auto => 'BD_ENCORE'); + $self_skill{owner} = $self_skill{skillObject}->getOwner(); + $self_skill{lvl} = $char->getSkillLevel($self_skill{skillObject}); + } + if ($self_skill{ID}) { + debug qq~Auto-skill on self: $config{$self_skill{prefix}} (lvl $self_skill{lvl})\n~, "ai"; + ai_skillUse2($self_skill{skillObject}, $self_skill{lvl}, $self_skill{maxCastTime}, $self_skill{minCastTime}, $self_skill{owner}, $self_skill{prefix}); + } + } +} + +##### PARTY-SKILL USE ##### +sub processPartySkillUse { + if (AI::isIdle || AI::is(qw(route mapRoute follow sitAuto take items_gather items_take attack move))){ + my $realMyPos = calcPosFromPathfinding($field, $char); + my %party_skill; + PARTYSKILL: + for (my $i = 0; exists $config{"partySkill_$i"}; $i++) { + next if (!$config{"partySkill_$i"}); + next unless checkSelfCondition("partySkill_$i"); + $party_skill{skillObject} = Skill->new(auto => $config{"partySkill_$i"}); + $party_skill{owner} = $party_skill{skillObject}->getOwner; + + foreach my $ID ($accountID, @slavesID, @playersID) { + next if $ID eq '' || $ID eq $party_skill{owner}{ID}; + + if ($ID eq $accountID) { + } elsif ($slavesList->getByID($ID)) { + next if ((!$char->{slaves} || !$char->{slaves}{$ID}) && !$config{"partySkill_$i"."_notPartyOnly"}); + next if (($char->{slaves}{$ID} ne $slavesList->getByID($ID)) && !$config{"partySkill_$i"."_notPartyOnly"}); + } elsif ($playersList->getByID($ID)) { + unless ($config{"partySkill_$i"."_notPartyOnly"}) { + next unless $char->{party}{joined} && $char->{party}{users}{$ID}; + # party member should be online, otherwise it's another character on the same account (not in party) + next unless $char->{party}{users}{$ID} && $char->{party}{users}{$ID}{online}; + } + } + my $player = Actor::get($ID); + next unless (UNIVERSAL::isa($player, 'Actor::You') || UNIVERSAL::isa($player, 'Actor::Player') || UNIVERSAL::isa($player, 'Actor::Slave')); + next unless ( # target check + !$config{"partySkill_$i"."_target"} + || existsInList($config{"partySkill_$i"."_target"}, $player->{name}) + || $player->{ID} eq $char->{ID} && existsInList($config{"partySkill_$i"."_target"}, '@main') + || $char->has_homunculus && $player->{ID} eq $char->{homunculus}{ID} && existsInList($config{"partySkill_$i"."_target"}, '@homunculus') + || $char->has_mercenary && $player->{ID} eq $char->{mercenary}{ID} && existsInList($config{"partySkill_$i"."_target"}, '@mercenary') + ); + my $party_skill_dist = $config{"partySkill_$i"."_dist"} || $config{partySkillDistance} || "0..8"; + if (defined($config{"partySkill_$i"."_dist"}) && defined($config{"partySkill_$i"."_maxDist"})) { $party_skill_dist = $config{"partySkill_$i"."_dist"} . ".." . $config{"partySkill_$i"."_maxDist"};} + my $realActorPos = calcPosFromPathfinding($field, $player); + my $distance = blockDistance($realMyPos, $realActorPos); + next unless ($party_skill{owner}{ID} eq $player->{ID} || inRange($distance, $party_skill_dist)); + next unless (checkPlayerCondition("partySkill_$i"."_target", $ID)); + + $party_skill{ID} = $party_skill{skillObject}->getHandle; + $party_skill{lvl} = $config{"partySkill_$i"."_lvl"} || $char->getSkillLevel($party_skill{skillObject}); + $party_skill{target} = $player->{name}; + $party_skill{targetActor} = $player; + $party_skill{x} = $realActorPos->{x}; + $party_skill{y} = $realActorPos->{y}; + $party_skill{targetID} = $ID; + $party_skill{maxCastTime} = $config{"partySkill_$i"."_maxCastTime"}; + $party_skill{minCastTime} = $config{"partySkill_$i"."_minCastTime"}; + $party_skill{isSelfSkill} = $config{"partySkill_$i"."_isSelfSkill"}; + $party_skill{prefix} = "partySkill_$i"; + # This is used by setSkillUseTimer() to set + # $ai_v{"partySkill_${i}_target_time"}{$targetID} + # when the skill is actually cast + $targetTimeout{$ID}{$party_skill{ID}} = $i; + last PARTYSKILL; + } + } + + if ($config{useSelf_skill_smartHeal} && $party_skill{ID} eq "AL_HEAL" && !$config{$party_skill{prefix}."_noSmartHeal"}) { + my $smartHeal_lv = 1; + my $hp_diff; + my $modifier = 1 + int(($char->{skills}{HP_MEDITATIO}{lv} * 2) / 100); + + if ($char->{party}{joined} && $char->{party}{users}{$party_skill{targetID}} && $char->{party}{users}{$party_skill{targetID}}{hp}) { + $hp_diff = $char->{party}{users}{$party_skill{targetID}}{hp_max} - $char->{party}{users}{$party_skill{targetID}}{hp}; + + } elsif ($char->has_mercenary && $party_skill{targetID} eq $char->{mercenary}{ID} && $char->{mercenary}{hp} && $char->{mercenary}{hp_max}) { + $hp_diff = $char->{mercenary}{hp_max} - $char->{mercenary}{hp}; + $modifier /= 2; + + } else { + if ($players{$party_skill{targetID}}) { + $hp_diff = -$players{$party_skill{targetID}}{deltaHp}; + } + } + for (my $i = 1; $i <= $char->{skills}{$party_skill{ID}}{lv}; $i++) { + my ($sp_req, $amount); + + $smartHeal_lv = $i; + $sp_req = 10 + ($i * 3); + if ($config{useSelf_skill_smartHeal} == 2) { + $amount = (int(($char->{lv} + $char->{int} + $char->{'int_bonus'}) / 5) * 30) * ($i / 10) * (1 + $modifier) + ($char->{'attack_magic_min'}); + } else { + $amount = (int(($char->{lv} + $char->{int}) / 8) * (4 + $i * 8)) * $modifier; + } + if ($char->{sp} < $sp_req) { + $smartHeal_lv--; + last; + } + last if ($amount >= $hp_diff); + } + $party_skill{lvl} = $smartHeal_lv; + } + if (defined $party_skill{targetID}) { + debug qq~Party Skill used ($party_skill{target}) Skills Used: $config{$party_skill{prefix}} (lvl $party_skill{lvl})\n~, "skill"; + ai_skillUse2( + $party_skill{skillObject}, + $party_skill{lvl}, + $party_skill{maxCastTime}, + $party_skill{minCastTime}, + $party_skill{isSelfSkill} ? $party_skill{owner} : $party_skill{targetActor}, + $party_skill{prefix}, + ); + } + } +} + +##### MONSTER SKILL USE ##### +sub processMonsterSkillUse { + if (AI::isIdle || AI::is(qw(route mapRoute follow sitAuto take items_gather items_take attack move))) { + my $i = 0; + my $prefix = "monsterSkill_$i"; + while ($config{$prefix}) { + # monsterSkill can be used on any monster that we could + # attackAuto + my @monsterIDs = ai_getAggressives(1, 1); + for my $monsterID (@monsterIDs) { + my $monster = $monsters{$monsterID}; + if (checkSelfCondition($prefix) + && checkMonsterCondition("${prefix}_target", $monster)) { + my $skill = new Skill(auto => $config{$prefix}); + + next if $config{"${prefix}_maxUses"} && $monster->{skillUses}{$skill->getHandle()} >= $config{"${prefix}_maxUses"}; + next if $config{"${prefix}_target"} && !existsInList($config{"${prefix}_target"}, $monster->{name}); + + my $lvl = $config{"${prefix}_lvl"} || $char->getSkillLevel($skill); + my $maxCastTime = $config{"${prefix}_maxCastTime"}; + my $minCastTime = $config{"${prefix}_minCastTime"}; + debug "Auto-monsterSkill on $monster->{name} ($monster->{binID}): ".$skill->getName()." (lvl $lvl)\n", "monsterSkill"; + # FIXME: $skill->getOwner (homun, merc) instead of $char? + my $target = $config{"${prefix}_isSelfSkill"} ? $char : $monster; + ai_skillUse2($skill, $lvl, $maxCastTime, $minCastTime, $target, $prefix); + $ai_v{$prefix . "_time"} = time; + $ai_v{$prefix . "_target_time"}{$monsterID} = time; + last; + } + } + $i++; + $prefix = "monsterSkill_$i"; + } + } +} + +##### AUTO-EQUIP ##### +sub processAutoEquip { + Benchmark::begin("ai_autoEquip") if DEBUG; + if ((AI::isIdle || AI::is(qw(route mapRoute follow sitAuto skill_use take items_gather items_take attack))) + && timeOut($timeout{ai_item_equip_auto}) && $char->inventory->isReady()) { + + my $ai_index_attack = AI::findAction("attack"); + + my $monster; + if (defined $ai_index_attack) { + my $ID = AI::args($ai_index_attack)->{ID}; + $monster = $monsters{$ID}; + } + + my @skip_slots; + if (AI::is('skill_use')) { + my $args = AI::args; + foreach my $slot (values %equipSlot_lut) { + if (exists $config{"$args->{prefix}_equip_$slot"}) { + push(@skip_slots, $slot); + } + } + } + + # we will create a list of items to equip + my %eq_list; + + for (my $i = 0; exists $config{"equipAuto_$i"}; $i++) { + if ((!$config{"equipAuto_${i}_weight"} || $char->{percent_weight} >= $config{"equipAuto_$i" . "_weight"}) + && (!$config{"equipAuto_${i}_whileSitting"} || ($config{"equipAuto_${i}_whileSitting"} && $char->{sitting})) + && (!$config{"equipAuto_${i}_target"} || (defined $monster && existsInList($config{"equipAuto_$i" . "_target"}, $monster->{name}))) + && (!$monster || (defined $monster && checkMonsterCondition("equipAuto_${i}_target", $monster))) + && checkSelfCondition("equipAuto_$i") + && Actor::Item::scanConfigAndCheck("equipAuto_$i") + ) { + foreach my $slot (values %equipSlot_lut) { + next if (defined binFind(\@skip_slots, $slot)); + if (exists $config{"equipAuto_$i"."_$slot"} && defined $config{"equipAuto_$i"."_$slot"}) { + debug "Equip $slot with ".$config{"equipAuto_$i"."_$slot"}."\n"; + $eq_list{$slot} = $config{"equipAuto_$i"."_$slot"} if (!$eq_list{$slot}); + } + } + $ai_v{"equipAuto_$i" . "_time"} = time; + } + } + + if (%eq_list) { + debug "Auto-equipping items\n", "equipAuto"; + Actor::Item::bulkEquip(\%eq_list); + } + $timeout{ai_item_equip_auto}{time} = time; + + } + Benchmark::end("ai_autoEquip") if DEBUG; +} + +##### AUTO-ATTACK ##### +sub processAutoAttack { + # Don't even think about attacking if attackAuto is -1. + return if $config{attackAuto} && $config{attackAuto} eq -1; + + # The auto-attack logic is as follows: + # 1. Generate a list of monsters that we are allowed to attack. + # 2. Pick the "best" monster out of that list, and attack it. + + Benchmark::begin("ai_autoAttack") if DEBUG; + + return if (AI::inQueue("attack")); + + return if (!$field); + if ((AI::isIdle || AI::is(qw/route follow sitAuto take items_gather items_take/) || (AI::action eq "mapRoute" && AI::args->{stage} eq 'Getting Map Solution')) + # Don't auto-attack monsters while taking loot, and itemsTake/GatherAuto >= 2 + && !($config{'itemsTakeAuto'} >= 2 && AI::is("take", "items_take")) + && !($config{'itemsGatherAuto'} >= 2 && AI::is("take", "items_gather")) + && timeOut($timeout{ai_attack_auto}) + && $ai_v{temp}{searchMonsters} >= $config{teleportAuto_search} + && (!$config{attackAuto_notInTown} || !$field->isCity) + && ($config{attackAuto_inLockOnly} <= 1 || $field->baseName eq $config{'lockMap'}) + && (!$config{attackAuto_notWhile_storageAuto} || !AI::inQueue("storageAuto")) + && (!$config{attackAuto_notWhile_buyAuto} || !AI::inQueue("buyAuto")) + && (!$config{attackAuto_notWhile_sellAuto} || !AI::inQueue("sellAuto")) + ) { + + # If we're in tanking mode, only attack something if the person we're tanking for is on screen. + my $foundTankee; + if ($config{'tankMode'}) { + for (@$playersList, @$slavesList) { + if ( + ($config{tankModeTarget} eq $_->{name}) + || ($char->has_homunculus && $config{tankModeTarget} eq '@homunculus' && $_->{ID} eq $char->{homunculus}{ID}) + || ($char->has_mercenary && $config{tankModeTarget} eq '@mercenary' && $_->{ID} eq $char->{mercenary}{ID}) + ) { + $foundTankee = 1; + last; + } + } + } + + my $attackTarget; + + if (!$config{'tankMode'} || $foundTankee) { + # Detect whether we are currently in follow mode + my $following; + my $followID; + if (defined(my $followIndex = AI::findAction("follow"))) { + $following = AI::args($followIndex)->{following}; + $followID = AI::args($followIndex)->{ID}; + } + + my $routeIndex = AI::findAction("route"); + $routeIndex = AI::findAction("mapRoute") if (!defined $routeIndex); + my $attackOnRoute; + if (defined $routeIndex) { + $attackOnRoute = AI::args($routeIndex)->{attackOnRoute}; + } else { + $attackOnRoute = 2; + } + + ### Step 1: Generate a list of all monsters that we are allowed to attack. ### + + my @skillCancelMonsters; + my @looterMonsters; + + my @aggressives; + + my @partyMonsters; + my @cleanMonsters; + my @droppedMonsters; + + # List aggressive monsters + my $party = $config{'attackAuto_party'} ? 1 : 0; + @aggressives = ai_getAggressives($attackOnRoute, $party) if $attackOnRoute; + + # List party monsters + foreach (@monstersID) { + next if (!$_ || !checkMonsterCleanness($_)); + my $monster = $monsters{$_}; + + # Never attack monsters that we failed to get LOS with + next if (!timeOut($monster->{attack_failedLOS}, $timeout{ai_attack_failedLOS}{timeout})); + # Avoid Hidden monsters + next if ($config{avoidHiddenMonsters} && ($monster->{statuses}->{EFFECTSTATE_BURROW} || $monster->{statuses}->{EFFECTSTATE_HIDING})); + # Avoid monster marked to ignore + next if ($monster->{ignore}); + # Avoid monster marked to avoid + next if ($monster->{avoid}); + + OpenKoreMod::autoAttack($monster) if (defined &OpenKoreMod::autoAttack); + + if ($monster->{monsterSkillCancel}) { + push @skillCancelMonsters, $_; + next; + } + + if ($monster->{attackLooters}) { + push @looterMonsters, $_; + next; + } + + # List monsters that our slaves are attacking + if ( + $config{attackAuto_party} + && $attackOnRoute && !AI::is("take", "items_take") + && !$ai_v{sitAuto_forcedBySitCommand} + && timeOut($monster->{attack_failed}, $timeout{ai_attack_unfail}{timeout}) + && ( + (scalar(grep { isMySlaveID($_) } keys %{$monster->{missedFromPlayer}}) && $config{attackAuto_party} != 2) + || (scalar(grep { isMySlaveID($_) } keys %{$monster->{dmgFromPlayer}}) && $config{attackAuto_party} != 2) + || (scalar(grep { isMySlaveID($_) } keys %{$monster->{castOnByPlayer}}) && $config{attackAuto_party} != 2) + || scalar(grep { isMySlaveID($_) } keys %{$monster->{missedToPlayer}}) + || scalar(grep { isMySlaveID($_) } keys %{$monster->{dmgToPlayer}}) + || scalar(grep { isMySlaveID($_) } keys %{$monster->{castOnToPlayer}}) + ) + ) { + push @partyMonsters, $_; + next; + } + + # List monsters that party members are attacking + if ($config{attackAuto_party} && $attackOnRoute && !AI::is("take", "items_take") + && !$ai_v{sitAuto_forcedBySitCommand} + && (($monster->{dmgFromParty} && $config{attackAuto_party} != 2) || + $monster->{dmgToParty} || $monster->{missedToParty}) + && timeOut($monster->{attack_failed}, $timeout{ai_attack_unfail}{timeout})) { + push @partyMonsters, $_; + next; + } + + # List monsters that the master is attacking + if ($following && $config{'attackAuto_followTarget'} && $attackOnRoute && !AI::is("take", "items_take") + && ($monster->{dmgToPlayer}{$followID} || $monster->{dmgFromPlayer}{$followID} || $monster->{missedToPlayer}{$followID}) + && timeOut($monster->{attack_failed}, $timeout{ai_attack_unfail}{timeout})) { + push @partyMonsters, $_; + next; + } + + my $control = mon_control($monster->{name}, $monster->{nameID}); + # List dropped targets and already engaged targets + if( + $monster->{droppedForAggressive} || # check for dropped target + ($monster->{dmgFromYou} && $config{'attackAuto'} >= 1 && ($control->{attack_auto} == 1 || $control->{attack_auto} == 3)) # if received damage then check if we can continue the attack + ) { + push @droppedMonsters, $_; + next; + } + + next unless (!AI::is(qw/sitAuto take items_gather items_take/) + && $config{'attackAuto'} >= 2 + && ($control->{attack_auto} == 1 || $control->{attack_auto} == 3) + && (!$config{'attackAuto_onlyWhenSafe'} || isSafe()) + && !$ai_v{sitAuto_forcedBySitCommand} + && $attackOnRoute >= 2 + && !$monster->{dmgFromYou} + ); + + my $myPos = calcPosition($char); + my $target_pos = calcPosition($monster); + # TODO: Is there any situation where we should use calcPosFromPathfinding or calcPosFromTime here? + next unless ($control->{dist} eq '' || blockDistance($target_pos, $myPos) <= $control->{dist}); + + # TODO: Sometimes we had no LOS to attack mob and dropped it, but now it is following us and attacking us + # which means we now have LOS to is, it we should have a way to delete ai_attack_unfail and ai_attack_failedLOS + # timeouts in these cases. + next unless (timeOut($monster->{attack_failed}, $timeout{ai_attack_unfail}{timeout})); + next unless (timeOut($monster->{attack_failedLOS}, $timeout{ai_attack_failedLOS}{timeout})); + + my %hookArgs; + $hookArgs{monster} = $monster; + $hookArgs{return} = 1; + Plugins::callHook('checkMonsterAutoAttack', \%hookArgs); + next if (!$hookArgs{return}); + push @cleanMonsters, $_; + } + + ### Step 2: Pick out the "best" monster ### + + # We define whether we should attack only monsters in LOS or not + my $checkLOS = $config{attackCheckLOS}; + my $canSnipe = $config{attackCanSnipe}; + $attackTarget = getBestTarget(\@skillCancelMonsters, $checkLOS, $canSnipe) || + getBestTarget(\@looterMonsters, $checkLOS, $canSnipe) || + getBestTarget(\@aggressives, $checkLOS, $canSnipe) || + getBestTarget(\@partyMonsters, $checkLOS, $canSnipe) || + getBestTarget(\@droppedMonsters, $checkLOS, $canSnipe) || + getBestTarget(\@cleanMonsters, $checkLOS, $canSnipe); + } + + # If an appropriate monster's found, attack it. If not, wait ai_attack_auto secs before searching again. + if ($attackTarget) { + ai_setSuspend(0); + + $char->attack($attackTarget); + } else { + $timeout{'ai_attack_auto'}{'time'} = time; + } + } + + Benchmark::end("ai_autoAttack") if DEBUG; +} + +##### ITEMS TAKE ##### +# Look for loot to pickup when your monster died. +sub processItemsTake { + if (AI::action eq "items_take" && AI::args->{suspended}) { + AI::args->{ai_items_take_start}{time} += time - AI::args->{suspended}; + AI::args->{ai_items_take_end}{time} += time - AI::args->{suspended}; + delete AI::args->{suspended}; + } + if (AI::action eq "items_take" && (percent_weight($char) >= $config{itemsMaxWeight})) { + AI::dequeue; + ai_clientSuspend(0, $timeout{ai_attack_waitAfterKill}{timeout}) unless (ai_getAggressives()); + } + if (AI::action eq "items_take" && timeOut(AI::args->{ai_items_take_start}) + && timeOut(AI::args->{ai_items_take_delay})) { + my $foundID; + my ($dist, $dist_to); + + foreach (@itemsID) { + next unless $_; + my $item = $items{$_}; + next if (pickupitems($item->{name}, $item->{nameID}) eq "0" || pickupitems($item->{name}, $item->{nameID}) == -1); + + $dist = distance($item->{pos}, AI::args->{pos}); + $dist_to = distance($item->{pos}, AI::args->{pos_to}); + if (($dist <= 4 || $dist_to <= 4) && $item->{take_failed} == 0) { + $foundID = $_; + last; + } + } + if (defined $foundID) { + AI::args->{ai_items_take_end}{time} = time; + AI::args->{started} = 1; + AI::args->{ai_items_take_delay}{time} = time; + take($foundID); + Plugins::callHook('ai_items_take'); + } elsif (AI::args->{started} || timeOut(AI::args->{ai_items_take_end})) { + $timeout{'ai_attack_auto'}{'time'} = 0; + AI::dequeue; + } + } +} + +##### ITEMS AUTO-GATHER ##### +sub processItemsAutoGather { + return if (AI::inQueue("take", "items_gather")); + if ( (AI::isIdle || AI::action eq "follow" + || ( AI::is("route", "mapRoute") && (!AI::args->{ID} || $config{'itemsGatherAuto'} >= 2) )) + && $config{'itemsGatherAuto'} + && (!$config{itemsGatherAuto_notInTown} || !$field->isCity) + && !$ai_v{sitAuto_forcedBySitCommand} + && ($config{'itemsGatherAuto'} >= 2 || !ai_getAggressives()) + && percent_weight($char) < $config{'itemsMaxWeight'} + && timeOut($timeout{ai_items_gather_auto}) ) { + + my $bestItem; + my $smallestDist; + my $myPos = calcPosition($char); + my $minPlayerDist = $config{itemsGatherAutoMinPlayerDistance} || 6; + my $minPortalDist = $config{itemsGatherAutoMinPortalDistance} || 5; + + foreach (@itemsID) { + next unless $_; + my $item = $items{$_}; + next if (!timeOut($item->{appear_time}, $timeout{ai_items_gather_start}{timeout}) + || $item->{take_failed} >= 1 + || pickupitems($item->{name}, $item->{nameID}) eq "0" + || pickupitems($item->{name}, $item->{nameID}) == -1 ); + if (!positionNearPlayer($item->{pos}, $minPlayerDist) && + !positionNearPortal($item->{pos}, $minPortalDist)) { + my $pos = calcPosition($item); + my $dist = adjustedBlockDistance($myPos, $pos); + if (!defined($bestItem)) { + $smallestDist = $dist; + $bestItem = $item; + } elsif ( $dist < $smallestDist ) { + $smallestDist = $dist; + $bestItem = $item; + } + } + } + + if (defined($bestItem)) { + message TF("Gathering: %s (%s)\n", $bestItem->{name}, $bestItem->{binID}); + gather($bestItem->{ID}); + $timeout{ai_items_gather_auto}{time} = time; + } + } +} + +##### ITEMS GATHER ##### +sub processItemsGather { + if (AI::action eq "items_gather" && AI::args->{suspended}) { + AI::args->{ai_items_gather_giveup}{time} += time - AI::args->{suspended}; + delete AI::args->{suspended}; + } + if (AI::action eq "items_gather" && !($items{AI::args->{ID}} && %{$items{AI::args->{ID}}})) { + my $ID = AI::args->{ID}; + message TF("Failed to gather %s (%s) : Lost target\n", $items_old{$ID}{name}, $items_old{$ID}{binID}), "drop"; + AI::dequeue; + + } elsif (AI::action eq "items_gather") { + my $ID = AI::args->{ID}; + my $minPlayerDist = $config{itemsGatherAutoMinPlayerDistance} || 6; + + if (positionNearPlayer($items{$ID}{pos}, $minPlayerDist)) { + message TF("Failed to gather %s (%s) : No looting! (player near)\n", $items{$ID}{name}, $items{$ID}{binID}), undef, 1; + AI::dequeue; + + } elsif (timeOut(AI::args->{ai_items_gather_giveup})) { + message TF("Failed to gather %s (%s) : Timeout\n", $items{$ID}{name}, $items{$ID}{binID}), undef, 1; + $items{$ID}{take_failed}++; + AI::dequeue; + + } elsif ($char->{sitting}) { + AI::suspend(); + stand(); + + } elsif (blockDistance($items{$ID}{pos}, $char->{pos}) > 2 && timeOut(AI::args->{time_route} = time, $timeout{ai_take_giveup}{timeout})) { + my $item = $items{$ID}; + my $pos = $item->{pos}; + AI::args->{time_route} = time; + ai_route( + $field->baseName, + $pos->{x}, + $pos->{y}, + maxRouteDistance => $config{'attackRouteMaxPathDistance'}, + noSitAuto => 1, + distFromGoal => 1, + isItemGather => 1 + ); + } else { + AI::dequeue; + take($ID); + } + } +} + +##### AUTO-TELEPORT ##### +sub processAutoTeleport { + return if (AI::inQueue("teleport", "NPC")); + + my $safe = 0; + + if (!$field->isCity && !AI::inQueue("storageAuto", "buyAuto", "skill_use") && $config{teleportAuto_allPlayers} + && ($config{'lockMap'} eq "" || $field->baseName eq $config{'lockMap'}) + && binSize(\@playersID) && timeOut($AI::Temp::Teleport_allPlayers, 0.75)) { + + my $ok; + if ($config{teleportAuto_allPlayers} >= 2) { + if (!isSafe()) { + $ok = 1; + } + } else { + for my Actor::Player $player (@$playersList) { + if (!existsInList($config{teleportAuto_notPlayers}, $player->{name}) && !existsInList($config{teleportAuto_notPlayers}, $player->{nameID})) { + $ok = 1; + last; + } + } + } + + if ($ok) { + message T("Teleporting to avoid all players\n"), "teleport"; + ai_useTeleport(1); + $ai_v{temp}{clear_aiQueue} = 1; + $AI::Temp::Teleport_allPlayers = time; + } + + } + + # Check whether it's safe to teleport + if (!$field->isCity) { + if ($config{teleportAuto_onlyWhenSafe}) { + if (isSafe() || timeOut($timeout{ai_teleport_safe_force})) { + $safe = 1; + $timeout{ai_teleport_safe_force}{time} = time; + } + } else { + $safe = 1; + } + } + + ##### TELEPORT HP ##### + if ($safe && timeOut($timeout{ai_teleport_hp}) + && ( + ( + ($config{teleportAuto_hp} && percent_hp($char) <= $config{teleportAuto_hp}) + || ($config{teleportAuto_sp} && percent_sp($char) <= $config{teleportAuto_sp}) + ) + && scalar(ai_getAggressives()) + || ( + $config{teleportAuto_minAggressives} + && scalar(ai_getAggressives()) >= $config{teleportAuto_minAggressives} + && !($config{teleportAuto_minAggressivesInLock} && $field->baseName eq $config{'lockMap'}) + ) || ( + $config{teleportAuto_minAggressivesInLock} + && scalar(ai_getAggressives()) >= $config{teleportAuto_minAggressivesInLock} + && $field->baseName eq $config{'lockMap'} + ) + ) + && !$char->{dead} + ) { + message T("Teleporting due to insufficient HP/SP or too many aggressives\n"), "teleport"; + $ai_v{temp}{clear_aiQueue} = 1; + ai_useTeleport(1); + $timeout{ai_teleport_hp}{time} = time; + return; + } + + ##### TELEPORT MONSTER ##### + if ($safe && timeOut($timeout{ai_teleport_away})) { + foreach (@monstersID) { + next unless $_; + my $teleAuto = mon_control($monsters{$_}{name},$monsters{$_}{nameID})->{teleport_auto}; + # TODO: check for dead should actually be for the whole autoteleport logic + if ($teleAuto == 1 && !$char->{dead}) { + message TF("Teleporting to avoid %s\n", $monsters{$_}{name}), "teleport"; + $ai_v{temp}{clear_aiQueue} = 1; + ai_useTeleport(1); + $timeout{ai_teleport_away}{time} = time; + return; + } elsif ($teleAuto < 0 && !$char->{dead}) { + # TODO: Is there any situation where we should use calcPosFromPathfinding or calcPosFromTime here? + my $pos = calcPosition($monsters{$_}); + my $myPos = calcPosition($char); + my $dist = blockDistance($pos, $myPos); + if ($dist <= abs($teleAuto)) { + if ($field->canMove($myPos, $pos)) { + message TF("Teleporting due to monster being too close %s\n", $monsters{$_}{name}), "teleport"; + $ai_v{temp}{clear_aiQueue} = 1; + ai_useTeleport(1); + $timeout{ai_teleport_away}{time} = time; + return; + } + } + } + } + $timeout{ai_teleport_away}{time} = time; + } + + ##### TELEPORT IDLE / PORTAL ##### + if ($config{teleportAuto_idle} && (AI::action ne "" || !AI::SlaveManager::isIdle)) { + $timeout{ai_teleport_idle}{time} = time; + } + + if ($safe && $config{teleportAuto_idle} && !$ai_v{sitAuto_forcedBySitCommand} && timeOut($timeout{ai_teleport_idle})){ + message T("Teleporting due to idle\n"), "teleport"; + ai_useTeleport(1); + $ai_v{temp}{clear_aiQueue} = 1; + $timeout{ai_teleport_idle}{time} = time; + return; + } + + if ($safe && $config{teleportAuto_portal} + && ($config{'lockMap'} eq "" || $config{lockMap} eq $field->baseName) + && timeOut($timeout{ai_teleport_portal}) + && !AI::inQueue("storageAuto", "buyAuto", "sellAuto")) { + if (scalar(@portalsID)) { + message T("Teleporting to avoid portal\n"), "teleport"; + $ai_v{temp}{clear_aiQueue} = 1; + ai_useTeleport(1); + $timeout{ai_teleport_portal}{time} = time; + return; + } + $timeout{ai_teleport_portal}{time} = time; + } +} + +##### ALLOWED MAPS ##### +sub processAllowedMaps { + # Respawn/disconnect if you're on a map other than the specified + # list of maps. + # This is to mostly useful on pRO, where GMs warp you to a secret room. + # + # Here, we only check for respawn. (Disconnect is handled in + # packets 0091 and 0092.) + if ($field->baseName && + $config{allowedMaps} && $config{allowedMaps_reaction} == 0 && + timeOut($timeout{ai_teleport}) && + !existsInList($config{allowedMaps}, $field->baseName) && + $ai_v{temp}{allowedMapRespawnAttempts} < 3) { + warning TF("The current map (%s) is not on the list of allowed maps.\n", $field->baseName); + chatLog("k", TF("** The current map (%s) is not on the list of allowed maps.\n", $field->baseName)); + ai_clientSuspend(0, 5); + message T("Respawning to save point.\n"); + chatLog("k", T("** Respawning to save point.\n")); + $ai_v{temp}{allowedMapRespawnAttempts}++; + ai_useTeleport(2); + $timeout{ai_teleport}{time} = time; + } +} + +##### AUTO RESPONSE ##### +sub processAutoResponse { + if (AI::action eq "autoResponse") { + my $args = AI::args; + + if ($args->{mapChanged} || !$config{autoResponse}) { + AI::dequeue; + } elsif (timeOut($args)) { + if ($args->{type} eq "c") { + sendMessage($messageSender, "c", $args->{reply}); + } elsif ($args->{type} eq "pm") { + sendMessage($messageSender, "pm", $args->{reply}, $args->{from}); + } + AI::dequeue; + } + } +} + +##### AVOID GM, PLAYERS OR MONSTERS ##### +sub processAvoid { + if (timeOut($timeout{ai_avoidcheck})) { + avoidGM_near() if ($config{avoidGM_near} >= 1 && (!$field->isCity || $config{avoidGM_near_inTown})); + avoidList_near() if $config{avoidList} >= 1; + $timeout{ai_avoidcheck}{time} = time; + } + foreach (@monstersID) { + next unless $_; + my $action = mon_control($monsters{$_}{name},$monsters{$_}{nameID})->{teleport_auto}; + + if ($action == 3) { + warning TF("Disconnecting for 30 secs to avoid %s\n", $monsters{$_}{name}); + relog(30); + } elsif ($action > 3) { + warning TF("Disconnecting for %s secs to avoid %s\n", $action, $monsters{$_}{name}); + relog($action); + } + } +} + +##### SEND EMOTICON ##### +sub processSendEmotion { + my $ai_sendemotion_index = AI::findAction("sendEmotion"); + return if (!defined $ai_sendemotion_index || time < AI::args->{timeout}); + $messageSender->sendEmotion(AI::args->{emotion}); + AI::clear("sendEmotion"); +} + +##### AUTO SHOP OPEN ##### +sub processAutoShopOpen { + if ($config{'shopAuto_open'} && !AI::isIdle) { + $timeout{ai_shop}{time} = time; + } + + if ($config{'shopAuto_open'} && AI::isIdle && $conState == 5 && !$char->{sitting} && timeOut($timeout{ai_shop}) && !$shopstarted + && $field->baseName eq $config{'lockMap'} && !$taskManager->countTasksByName('openShop')) { + if ($config{'shop_useSkill'}) { + my $skill = new Skill(auto => "MC_VENDING"); + + require Task::UseSkill; + my $skillTask = new Task::UseSkill( + actor => $skill->getOwner, + skill => $skill, + priority => Task::USER_PRIORITY + ); + my $task = new Task::Chained( + name => 'openShop', + tasks => [ + new Task::ErrorReport(task => $skillTask), + Task::Timeout->new( + function => sub {main::openShop()}, + seconds => $timeout{ai_shop_useskill_delay}{timeout} ? $timeout{ai_shop_useskill_delay}{timeout} : 5, + ) + ] + ); + $taskManager->add($task); + } else { + main::openShop(); + } + } +} + +##### AUTO BUYER SHOP OPEN ##### +sub processAutoBuyerShopOpen { + if ($config{'buyerShopAuto_open'} && !AI::isIdle) { + $timeout{ai_shop}{time} = time; + } + + if ($config{'buyerShopAuto_open'} && AI::isIdle && $net->getState() == Network::IN_GAME && !$char->{sitting} && timeOut($timeout{ai_shop}) && timeOut($timeout{ai_buyer_shopCheck}) && !$buyershopstarted + && $field->baseName eq $config{'lockMap'} && !$taskManager->countTasksByName('openShop')) { + if (!$char->{skills}{ALL_BUYING_STORE}{lv}) { + my $item = $char->inventory->getByNameID(12548); + if (!$item) { + error T("You don't have the Buying Store skill or Black Market Bulk Buyer Shop License.\n"); + return; + } + } elsif (!$char->inventory->getByNameID(6377)) { + error T("You don't have Bulk Buyer Shop License.\n"); + return; + } + main::openBuyerShop(); + $timeout{ai_buyer_shopCheck}{time} = time; + } +} + +sub processDcOnPlayer { + # Disconnect when a player is detected + if (!$field->isCity && !AI::inQueue("storageAuto", "buyAuto") && $config{dcOnPlayer} + && ($config{'lockMap'} eq "" || $field->baseName eq $config{'lockMap'}) + && !isSafe() && timeOut($AI::Temp::Teleport_allPlayers, 0.75)) { + $messageSender->sendQuit(); + error T("Auto disconnecting on Player!\n"); + chatLog("k", T("*** Nearby is another player, auto disconnect! ***\n")); + quit(); + } +} + +##### REPAIR AUTO ##### +sub repairAutoBrokenItems { + return [] unless $config{'repairAuto'}; + + my @broken; + for my $item (@{$char->inventory}) { + next unless $item->{broken}; + my $name = itemNameSimple($item->{nameID}); + next if ($config{'repairAuto_list'} + && !existsInList($config{'repairAuto_list'}, $name) + && !existsInList($config{'repairAuto_list'}, $item->{nameID})); + + push @broken, $item; + } + + return \@broken; +} + +sub repairAutoEquipAfter { + my ($args) = @_; + + return 1 unless ($config{repairAuto_equipAfter} && $args->{repairTargets}); + + $args->{equipQueue} ||= [@{$args->{repairTargets}}]; + + # Let pending equip requests finish before sending another. + return 0 if ($ai_v{temp}{waitForEquip}); + + while (my $itemID = shift @{$args->{equipQueue}}) { + my $item = $char->inventory->getByID($itemID); + next unless $item && $item->equippable; + next if $item->{broken} || $item->{equipped}; + + $item->equip; + return 0; + } + + delete $args->{equipQueue}; + $args->{equippedAfterRepair} = 1; + return 1; +} + +sub repairAutoSkillHandle { + foreach my $handle (qw(NC_REPAIR BS_REPAIRWEAPON ABR_NET_REPAIR)) { + return $handle if ($char->{skills}{$handle}{lv}); + } + return; +} + +sub processRepairAuto { + return if ($net->getState() != Network::IN_GAME); + + if ($config{repairAuto_inTownOnly} && !$field->isCity) { + AI::dequeue if (AI::action eq "repairAuto"); + return; + } + + my $brokenItems = repairAutoBrokenItems(); + + if ($config{'repairAuto'} + && @$brokenItems + && !AI::inQueue("storageAuto", "buyAuto", "sellAuto", "repairAuto") + && !$ai_v{sitAuto_forcedBySitCommand} + && !AI::is("repairAuto")) { + $timeout{ai_repair}{time} = time; + AI::queue("repairAuto", {useSkill => $config{'repairAuto_useSkill'}, repairTargets => [map { $_->{ID} } @$brokenItems]}); + } + + if (AI::action eq "repairAuto" && AI::args->{done}) { + AI::dequeue; + return; + } + + if (AI::action eq "repairAuto" && timeOut($timeout{ai_repair})) { + my $args = AI::args; + $args->{useSkill} = $config{'repairAuto_useSkill'}; + + if (!@$brokenItems) { + return unless ($args->{equippedAfterRepair} || repairAutoEquipAfter($args)); + $args->{done} = 1; + return; + } + + if ($args->{useSkill}) { + my $handle = repairAutoSkillHandle(); + unless ($handle) { + error T("Unable to auto repair: no repair skill available.\n"); + $args->{done} = 1; + return; + } + + if (!$repairList && !$args->{waitingForList}) { + my $level = $char->{skills}{$handle}{lv}; + ai_skillUse($handle, $level, 0, 0, $accountID); + $args->{waitingForList} = 1; + $timeout{ai_repair}{time} = time; + return; + } + } else { + unless ($config{'repairAuto_npc'}) { + error T("Unable to auto repair: repairAuto_npc not configured.\n"); + $args->{done} = 1; + return; + } + + $args->{npc} = {} unless $args->{npc}; + my $destination = $config{repairAuto_standpoint} || $config{repairAuto_npc}; + getNPCInfo($destination, $args->{npc}); + if (!defined($args->{npc}{ok})) { + $args->{done} = 1; + return; + } + + if (!$args->{distance}) { + if ($config{'repairAuto_standpoint'}) { + $args->{distance} = 1; + } elsif ($config{'repairAuto_maxDistance'} && $config{'repairAuto_distance'}) { + $args->{distance} = $config{'repairAuto_distance'} + round(rand($config{'repairAuto_maxDistance'} - $config{'repairAuto_distance'})); + } else { + $args->{distance} = $config{'repairAuto_distance'} || 3; + } + } + + undef $ai_v{temp}{do_route}; + if ($field->baseName ne $args->{npc}{map}) { + $ai_v{temp}{do_route} = 1; + } else { + my $found; + foreach my $actor (@{$npcsList->getItems()}) { + my $pos = $actor->{pos}; + next if ($actor->{statuses}->{EFFECTSTATE_BURROW}); + if ($pos->{x} == $args->{npc}{pos}{x} && $pos->{y} == $args->{npc}{pos}{y}) { + if (defined $actor->{name}) { + $found = 1; + last; + } + } + } + unless ($found) { + $ai_v{temp}{distance} = blockDistance($args->{npc}{pos}, $chars[$config{'char'}]{pos_to}); + if ($ai_v{temp}{distance} > $args->{distance} && !defined($args->{sentRepair})) { + $ai_v{temp}{do_route} = 1; + } + } + } + + if ($ai_v{temp}{do_route}) { + if ($args->{warpedToSave} && !$args->{mapChanged} && !timeOut($args->{warpStart}, 8)) { + undef $args->{warpedToSave}; + } + + if ($config{'saveMap'} ne "" && $config{'repairAuto_warp'} && !$args->{warpedToSave} + && !$field->isCity && $config{'saveMap'} ne $field->baseName) { + if ($char->{sitting}) { + message T("Standing up to auto-repair\n"), "teleport"; + ai_setSuspend(0); + stand(); + } else { + $args->{warpedToSave} = 1; + $args->{warpStart} = time unless $args->{warpStart}; + message T("Teleporting to auto-repair\n"), "teleport"; + ai_useTeleport(2); + } + $timeout{ai_repair}{time} = time; + } else { + ai_route($args->{npc}{map}, $args->{npc}{pos}{x}, $args->{npc}{pos}{y}, + attackOnRoute => 1, + distFromGoal => $args->{distance}, + noSitAuto => 1); + $timeout{ai_repair}{time} = time; + } + } else { + if (!exists $args->{sentNpcTalk}) { + my $realpos = {}; + getNPCInfo($config{"repairAuto_npc"}, $realpos); + + ai_talkNPC($realpos->{pos}{x}, $realpos->{pos}{y}, $config{repairAuto_npc_steps}); + + $args->{sentNpcTalk} = 1; + $args->{sentNpcTalk_time} = time; + $timeout{ai_repair}{time} = time; + return; + + } elsif (!$repairList) { + if (timeOut($args->{sentNpcTalk_time}, 20)) { + $args->{error} = 'Npc did not respond'; + $args->{done} = 1; + } + return; + } + } + } + + if ($repairList) { + $args->{waitingForList} = 0; + $args->{repairing} = 1; + } elsif ($args->{repairing} && !$repairList) { + if (@$brokenItems) { + delete @{$args}{qw(sentNpcTalk sentNpcTalk_time repairing sentRepair waitingForList distance)}; + $timeout{ai_repair}{time} = time; + } else { + return unless ($args->{equippedAfterRepair} || repairAutoEquipAfter($args)); + $args->{done} = 1; + } + } + } + + if ($config{'repairAuto'} && $conState == 5 && timeOut($timeout{ai_repair}) && $repairList) { + my $name; + foreach my $repairListItem (@{$repairList}) { + next if (!$repairListItem); + $name = itemNameSimple($repairListItem->{nameID}); + if (existsInList($config{'repairAuto_list'}, $name) || !$config{'repairAuto_list'}) { + $messageSender->sendRepairItem($repairListItem); + $timeout{ai_repair}{time} = time; + return; + } + } + } +} + +sub processSendIgnoreAll { + return if ($net->getState() != Network::IN_GAME || !$config{ignoreAll} || $ignored_all); + + if (!$timeout{'ai_ignoreAll'}{'time'}) { + $timeout{'ai_ignoreAll'}{'time'} = time; + return; + } elsif (timeOut($timeout{ai_ignoreAll})) { + warning "Sending ignoreAll... \n"; + $messageSender->sendIgnoreAll(0); + $timeout{'ai_ignoreAll'}{'time'} = time; + } +} + +sub processFeed { + if ($config{pet_autoFeed} && timeOut($timeout{ai_petFeed}) && %pet && $pet{hungry} && ($pet{hungry} <= $config{pet_hunger} || $pet{hungry} <= $config{pet_return})) { + if ($pet{hungry} <= $config{pet_return} && %pet && $pet{hungry}) { + message TF("Pet hunger reaches the return value.\n"); + $messageSender->sendPetMenu(3); + undef %pet; # todo: instead undef %pet when the actor (our pet) dissapears, this is safer (xkore) + return; + } + message TF("Pet hunger reaches the feed value.\n"); + $messageSender->sendPetMenu(1); + $timeout{ai_petFeed}{time} = time; + } +} + +sub shouldUseWarpToSaveMapForBuyOrSell { + my ($args) = @_; + + return 0 if (!$config{'saveMap'} || !$config{'saveMap_warpToBuyOrSell'} || $args->{warpedToSave}); + return 0 if ($config{'saveMap'} eq $field->baseName); + + my $distance = getDistanceToSaveMapFromCurrentPosition($args); + return 0 unless defined $distance; + + my $minDistance = int($config{'saveMap_warp_minDistance'} || 0); + return 1 if ($minDistance <= 0); + + return $distance >= $minDistance; +} + +sub getDistanceToSaveMapFromCurrentPosition { + my ($args) = @_; + + require Task::CalcMapRoute; + my $calcTask = Task::CalcMapRoute->new( + targets => [{ map => $field->baseName, x => $char->{pos_to}{x}, y => $char->{pos_to}{y} }], + sourceMap => $field->baseName, + sourceX => $char->{pos_to}{x}, + sourceY => $char->{pos_to}{y}, + noGoCommand => 1, + noTeleSpawn => 1, + maxTime => 3, + ); + + my $saveMapDestination = $calcTask->resolveSaveMapDestination(); + return unless ($saveMapDestination); + + my $cacheKey = join('|', + $field->baseName, + $char->{pos_to}{x}, + $char->{pos_to}{y}, + $saveMapDestination->{map}, + $saveMapDestination->{x}, + $saveMapDestination->{y}, + ); + if ($args && $args->{saveMapDistanceCache} && $args->{saveMapDistanceCache}{key} eq $cacheKey) { + return $args->{saveMapDistanceCache}{value}; + } + + my $distance = $calcTask->getDistanceToSaveMap($saveMapDestination->{map}, $saveMapDestination->{x}, $saveMapDestination->{y}); + $args->{saveMapDistanceCache} = { key => $cacheKey, value => $distance } if ($args && defined $distance); + return $distance; +} + +sub processPartyShareAuto { + return if (!$char->{party}{users}{$accountID}{admin} || $char->{party}{shareForcedByCommand}); + + if (timeOut($timeout{ai_partyShareCheck})) { + if (!exists($char->{party}{shareTimes})) { $char->{party}{shareTimes} = 1; } + + if (($config{partyAutoShare} || $config{partyAutoShareItem} || $config{partyAutoShareItemDiv}) && $char->{party}{joined} && ($char->{party}{share} ne $config{partyAutoShare} || $char->{party}{itemPickup} ne $config{partyAutoShareItem} || $char->{party}{itemDivision} ne $config{partyAutoShareItemDiv})) { + $messageSender->sendPartyOption($config{partyAutoShare}, $config{partyAutoShareItem}, $config{partyAutoShareItemDiv}); + $char->{party}{shareTimes}++; + if ($char->{party}{shareTimes} > 5) { + warning T("Party Share Settings have been sent many times, please check your party\n"); + } + } + $timeout{ai_partyShareCheck}{time} = time; + } +} + +1; diff --git a/openkore_llm_knowledge/core/src/Actor.pm b/openkore_llm_knowledge/core/src/Actor.pm new file mode 100644 index 0000000000..862a5d7492 --- /dev/null +++ b/openkore_llm_knowledge/core/src/Actor.pm @@ -0,0 +1,973 @@ +######################################################################### +# OpenKore - Base class for all actor objects +# Copyright (c) 2005 OpenKore Team +# +# This software is open source, licensed under the GNU General Public +# License, version 2. +# Basically, this means that you're allowed to modify and distribute +# this software. However, if you distribute modified versions, you MUST +# also distribute the source code. +# See http://www.gnu.org/licenses/gpl.html for the full license. +# +# $Revision$ +# $Id$ +# +######################################################################### +## +# MODULE DESCRIPTION: Base class for all actor objects +# +# The Actor class is a base class for all actor objects. +# An actor object is a monster or player (all members of %monsters and +# %players). Do not create an object of this class; use one of the +# subclasses instead. +# +# An actor object is also a hash. +# +# Child classes: @MODULE(Actor::Monster), @MODULE(Actor::Player), @MODULE(Actor::You), +# @MODULE(Actor::Item), @MODULE(Actor::Pet), @MODULE(Actor::Party), @MODULE(Actor::NPC), +# @MODULE(Actor::Portal) + +package Actor; + +use strict; +use Carp::Assert; +use Scalar::Util; +use List::MoreUtils; +use Data::Dumper; +use Storable; +use Globals; +use Utils; +use Utils::CallbackList; +use Log qw(message error debug); +use Misc; +use Task; +use Translation qw(T TF); +use Actor::Unknown; +use Task::Timeout; +use Utils::Assert; + +# Make it so that +# print $actor; +# acts the same as +# print $actor->nameString; +use overload '""' => \&_nameString; +# The eq operator checks whether two variables refer to compatible objects. +use overload 'eq' => \&_eq; +use overload 'ne' => \&_ne; +# The == operator is to check whether two variables refer to the +# exact same object. +use overload '==' => \&_isis; +use overload '!=' => \&_not_is; + +sub _eq { + return UNIVERSAL::isa($_[0], "Actor") + && UNIVERSAL::isa($_[1], "Actor") + && $_[0]->{ID} eq $_[1]->{ID}; +} + +sub _ne { + return !&_eq; +} + +# This function is needed to make the operator overload respect inheritance. +sub _nameString { + my $self = shift; + return $self->nameString(@_); +} + +sub _isis { + return Scalar::Util::refaddr($_[0]) == Scalar::Util::refaddr($_[1]); +} + +sub _not_is { + return !&_isis; +} + +### CATEGORY: Class methods + +# protected Actor->new(String actorType) +# actorType: A type name for this actor, like 'Player', 'Monster', etc. +# Requires: defined($actorType) +# +# A default abstract constructor that subclasses should call. Must not +# be directly used. +sub new { + my ($class, $actorType) = @_; + my %self = ( + actorType => $actorType, + onNameChange => new CallbackList('onNameChange'), + onUpdate => new CallbackList('onUpdate'), + + # define it so deltaHp check may work immediately + # TODO: set it only for actors with actual hp (players, monsters)? + deltaHp => 0, + ); + return bless \%self, $class; +} + +## +# Actor Actor::get(Bytes ID) +# ID: an actor ID, in binary format. +# Returns: the associated Actor object, or a new Actor::Unknown object if not found. +# Requires: defined($ID) +# Ensures: defined(result) +# +# Returns the Actor object for $ID. This function will look at the various +# actor lists. If $ID is not in any of the actor lists, it will return +# a new Actor::Unknown object. +sub get { + my ($ID, $retUndefwhenNotFound) = @_; + assert(defined $ID, "ID must be provided to retrieve and Actor class") if DEBUG; + + if ($ID eq $accountID) { + # I put assertions here because $char seems to be unblessed sometimes. + assert(defined $char, '$char must be defined') if DEBUG; + assertClass($char, 'Actor::You') if DEBUG; + return $char; + } elsif ($items{$ID}) { + return $items{$ID}; + } else { + foreach my $list ($playersList, $monstersList, $npcsList, $petsList, $portalsList, $slavesList, $elementalsList) { + my $actor = $list->getByID($ID); + if ($actor) { + return $actor; + } + } + if ($retUndefwhenNotFound) { + return undef; + } + return new Actor::Unknown($ID); + } +} + +## +# Actor Actor::getByName(String name) +# Returns: the associated Actor object or undef. +# Requires: defined($name) +# Ensures: defined(result) +# +# Returns the Actor object for $name. This function will look at the various +# actor lists. If $name is not in any of the actor lists, it will return +# undef. +sub getByName { + my ($name) = @_; + assert(defined $name, "Name must be provided to retrieve and Actor class") if DEBUG; + + foreach my $list ($playersList, $monstersList, $npcsList, $petsList, $portalsList, $slavesList, $elementalsList) { + for my $actor (@$list) { + if (lc($actor->{name}) eq lc($name)) { + return $actor; + } + } + } + return undef; +} + +### CATEGORY: Hash members + +## +# int $Actor->{binID} +# Invariant: value >= 0 +# +# The index of this actor inside its associated actor list. + +## +# Bytes $Actor->{ID} +# Invariant: length(value) == 4 +# +# The server's internal unique ID for this actor (the actor's account ID). + +## +# int $Actor->{nameID} +# Invariant: value >= 0 +# +# $Actor->{ID} decoded into an 32-bit little endian integer. + +## +# int $Actor->{appear_time} +# Invariant: value >= 0 +# +# The time when this actor first appeared on screen. + +## +# String $Actor->{actorType} +# Invariant: defined(value) +# +# A human-friendly name which describes this actor type. +# For instance, "Player", "Monster", "NPC", "You", etc. +# Do not confuse this with $Actor->{type} + +## +# String $Actor->{name} +# +# The name of the actor, e.g. "Joe", "Jane", "Poring", etc. +# This field is undefined if the name for this actor isn't known yet, +# so generally you use use $Actor->name() instead, which automatically +# takes care of actor objects that don't have a name yet. + +## +# Hash* $Actor->{pos} +# +# The position where this actor was, before its last movement. +# This is a reference to a hash, containing the items 'x' and 'y'. + +## +# Hash* $Actor->{pos_to} +# +# The position where this actor is moving to, or (if the actor has finished moving), +# where it currently is. This is a reference to a hash, containing the items 'x' and 'y'. + +## +# float $Actor->{walk_speed} +# +# The actor's walking speed, in blocks per second. + +## +# float $Actor->{time_move} +# +# The time (as timestamp) at which the actor last moved. + +## +# float $Actor->{time_move_calc} +# +# The time (in seconds) that the actor needs to move from $Actor->{pos} to $Actor->{pos_to}. + +## +# Bytes $Actor->{lastAttackFrom} +# +# The ID of the actor who had done the last attack to this actor, including misses. + +## +# int $Actor->{deltaHp} +# Invariant: deltaHp <= 0 +# +# Total amount of healed HP, minus total amount of damage done to this actor. +# +# deltaHp initially starts at 0. +# When actor takes damage, the damage is subtracted from his deltaHp. +# When actor is healed, the healed amount is added to the deltaHp. +# If the deltaHp becomes positive, it is reset to 0. +# +# Someone with a lot of negative deltaHp is probably in need of healing. +# This allows to intelligently heal non-party members. + +## +# int $Actor->{dmgTo} +# +# Total damage done to this actor. + +## +# int $Actor->{dmgFrom} +# +# Total damage done by this actor. + +## +# int $Actor->{attackedYou} +# +# Number of attacks done by this actor to $char, including misses. + +## +# int $Actor->{dmgToYou} +# +# Total damage done by this actor to $char. + +## +# int $Actor->{missedYou} +# +# Number of misses done by this actor to $char. + +## +# int $Actor->{castOnToYou} + +## +# int $Actor->{numAtkFromYou} +# +# Number of attacks done by $char to this actor, including misses. + +## +# int $Actor->{dmgFromYou} +# +# Total damage done by $char to this actor. + +## +# int $Actor->{missedFromYou} +# +# Number of misses done by $char to this actor. + +## +# int $Actor->{castOnByYou} + +## +# int $Actor->{dmgToParty} +# +# Total damage done by this actor to the party. + +## +# int $Actor->{missedToParty} +# +# Number of misses done by this actor to the party. + +## +# int $Actor->{dmgFromParty} +# +# Total damage done by the party to this actor. + +## +# int $Actor->{missedFromParty} +# +# Number of misses done by the party to this actor. + +## +# Hash $Actor->{dmgToPlayer} +# +# Total damage done by this actor to the actor whose ID is used as a hash key. + +## +# Hash $Actor->{missedToPlayer} +# +# Number of misses done by this actor to the actor whose ID is used as a hash key. + +## +# Hash $Actor->{castOnToPlayer} + +## +# Hash $Actor->{dmgFromPlayer} +# +# Total damage done by the actor whose ID is used as a hash key to this actor. + +## +# Hash $Actor->{missedFromPlayer} +# +# Number of misses done by the actor whose ID is used as a hash key to this actor. + +## +# Hash $Actor->{castOnByPlayer} + +## +# Hash $Actor->{dmgToMonster} +# +# Total damage done by this actor to the actor whose ID is used as a hash key. + +## +# Hash $Actor->{missedToMonster} +# +# Number of misses done by this actor to the actor whose ID is used as a hash key. + +## +# Hash $Actor->{castOnToMonster} + +## +# Hash $Actor->{dmgFromMonster} +# +# Total damage done by the actor whose ID is used as a hash key to this actor. + +## +# Hash $Actor->{missedFromMonster} +# +# Number of misses done by the actor whose ID is used as a hash key to this actor. + +## +# Hash $Actor->{castOnByMonster} + + +### CATEGORY: Methods + +## +# String $Actor->nameString([Actor otherActor]) +# +# Returns the name string of an actor, e.g. "Player pmak (3)", +# "Monster Poring (0)" or "You". +# +# If $otherActor is specified and is equal to $actor, then it will +# return 'self' or 'yourself' instead. +sub nameString { + my ($self, $otherActor) = @_; + + return $self->selfString if $self->{ID} eq $otherActor->{ID}; + + my $nameString = ""; + $nameString .= T('Your ') if $char && exists $char->{slaves}{$self->{ID}}; + $nameString .= "$self->{actorType} " . $self->name; + $nameString .= " ($self->{binID})" if defined $self->{binID}; + return $nameString; +} + +## +# String $Actor->selfString() +# +# Returns 'itself' for monsters, or 'himself/herself' for players. +# ('yourself' is handled by Actor::You.nameString.) +sub selfString { + return T('itself'); +} + +## +# String $Actor->name() +# +# Returns the name of an actor, e.g. "pmak" or "Unknown #300001". +sub name { + my ($self) = @_; + + return $self->{name} || T("Unknown #").unpack("V1", $self->{ID}); +} + +## +# void $Actor->setName(String name) +# name: A few name for this actor. Can be undef to indicate that this actor has lost its previous name. +# +# Assign a name to this actor. An 'onNameChange' and 'onUpdate' event will +# be triggered after the name is set. +sub setName { + my ($self, $name) = @_; + + my $oldName = $self->{name}; + + my $newName = $name; + + my %hookArgs = ( + actor => $self, + new_name => $newName, + return => 0 + ); + Plugins::callHook('actor_setName', \%hookArgs); + + if ($hookArgs{return}) { + $newName = $hookArgs{new_name}; + } + + $self->{name} = $newName; + $self->{onNameChange}->call($self, { oldName => $oldName }); + $self->{onUpdate}->call($self); +} + +## +# String $Actor->nameIdx() +# +# Returns the name and index of an actor, e.g. "pmak (0)" or "Unknown #300001 (1)". +sub nameIdx { + my ($self) = @_; + + my $nameIdx = $self->name; + $nameIdx .= " ($self->{binID})" if defined $self->{binID}; + return $nameIdx; + +# return $self->{name} || "Unknown #".unpack("V1", $self->{ID}); +} + +## +# String $Actor->verb(String you, String other) +# +# Returns $you if $actor is you; $other otherwise. +sub verb { + my ($self, $you, $other) = @_; + + return $you if $self->isa('Actor::You'); + return $other; +} + +## +# Hash $Actor->position() +# +# Returns the position of the actor. +sub position { + my ($self) = @_; + + return calcPosFromPathfinding($field, $self); +} + +## +# float $Actor->distance([Actor otherActor]) +# +# Returns the distance to another actor (defaults to yourself). +sub distance { + my ($self, $otherActor) = @_; + + $otherActor ||= $char; + return Utils::distance($self->position, $otherActor->position); +} + +## +# float $Actor->blockDistance([Actor otherActor]) +# +# Returns the block distance to another actor (defaults to yourself). +sub blockDistance { + my ($self, $otherActor) = @_; + + $otherActor ||= $char; + return Utils::blockDistance($self->position, $otherActor->position); +} + +## +# Actor $Actor->deepCopy() +# Ensures: defined(result) +# +# Create a deep copy of this actor object. +sub deepCopy { + my ($self) = @_; + + # Some fields cannot be deep copied by dclone() because they contain + # function references, so we'll do that manually. + + # Delete fields that cannot be copied by dclone() and store + # them in a temporary place. + my %deepCopyFields; + my %hashCopies; + my %excludehashCopies; + foreach my $param ('onNameChange', 'onUpdate') { + $deepCopyFields{$param} = $self->{$param}; + delete $self->{$param}; + } + # $actor->{casting} may be a hash which contains a reference to another + # Actor object. + foreach my $param ('casting') { + if ($self->{$param}) { + $hashCopies{$param} = $self->{$param}; + delete $self->{$param}; + } + } + foreach my $param ('slave_ai_seq', 'slave_ai_seq_args') { + if (exists $self->{$param} && defined $self->{$param}) { + $excludehashCopies{$param} = $self->{$param}; + delete $self->{$param}; + } + } + + my $copy; + eval { + $copy = Storable::dclone($_[0]); + }; + if ($@ =~ /Can't store CODE items/) { + die "Actor hash $self contains CODE items:\n" . + Dumper($self); + } elsif ($@) { + die $@; + } + + # Restore the deleted fields in the original object, + # and assign manually-created deep copies to the clone. + foreach my $param (keys %deepCopyFields) { + $self->{$param} = $deepCopyFields{$param}; + $copy->{$param} = $deepCopyFields{$param}->deepCopy; + } + foreach my $param (keys %hashCopies) { + $self->{$param} = $hashCopies{$param}; + $copy->{$param} = {%{$hashCopies{$param}}}; + } + foreach my $param (keys %excludehashCopies) { + $self->{$param} = $excludehashCopies{$param}; + } + + return $copy; +} + +## +# CallbackList $Actor->onNameChange() +# Ensures: defined(result) +# +# Returns the onNameChange event callback list. +# This event is triggered when the name of this actor has changed. +sub onNameChange { + return $_[0]->{onNameChange}; +} + +## +# CallbackList $Actor->onUpdate() +# Ensures: defined(result) +# +# Returns the onUpdate event callback list. +sub onUpdate { + return $_[0]->{onUpdate}; +} + +## +# void $Actor->setStatus(String status_handle, boolean state, [float duration]) +# status_handle: handle of the status +# state: whether to set (true) or unset (false) that status +# duration: delay before automatically unsetting that status +# +# Set or unset specified status. Display the corresponding message. +sub setStatus { + my ($self, $handle, $flag, $tick) = @_; + + my $again; + if ($flag) { + # Skill activated + $again = $self->{statuses}{$handle} ? 'again' : 'now'; + # All these hacks are for task to get lost when re-gaining a status, + # so it won't expire from an old task + $self->{statuses}{$handle} = bless {}, 'OpenkoreFixup::EmptyObject'; + + if ($tick) { + Scalar::Util::weaken($self->{statuses}{$handle}{_actor} = $self); + + $taskManager->add(Task::Timeout->new( + object => $self->{statuses}{$handle}, + weak => 1, + function => sub { + $_[0]->{_actor}->setStatus($handle, 0); + error "BUG: setStatus($handle, 0) failed?\n" if defined $_[0]; + },#now + seconds => $tick / 1000, + )); + } + + $self->{statuses}{$handle}{time} = time; + $self->{statuses}{$handle}{tick} = $tick ? $tick : 0; + + if ($char->{party}{joined} && $char->{party}{users}{$self->{ID}} && $char->{party}{users}{$self->{ID}}{name}) { + $again = 'again' if $char->{party}{users}{$self->{ID}}{statuses}{$handle}; + $char->{party}{users}{$self->{ID}}{statuses}{$handle} = {}; + } + } else { + # Skill de-activated (expired) + return unless ($self->{statuses} && $self->{statuses}{$handle}); # silent when "again no status" + $again = 'no longer'; + delete $self->{statuses}{$handle}; + delete $char->{party}{users}{$self->{ID}}{statuses}{$handle} if ($char->{party}{joined} && $char->{party}{users}{$self->{ID}} && $char->{party}{users}{$self->{ID}}{name}); + } + debug + Misc::status_string($self, defined $statusName{$handle} ? $statusName{$handle} : $handle, $again, $flag ? $tick/1000 : 0), + "parseMsg_statuslook", ($self->{ID} eq $accountID or $char->{slaves} && $char->{slaves}{$self->{ID}}) ? 1 : 2; + + Plugins::callHook('Actor::setStatus::change', { + handle => $handle, + flag => $flag, + tick => $tick, + actor_type => ref($self) + }); +} + +## +# boolean $Actor->statusActive(String statuses) +# statuses: comma-separated list of status handles and/or names +# +# Returns false if all statuses from the list are inactive, true otherwise. +sub statusActive { + my ($self, $commaSeparatedStatuses) = @_; + + # Incase this method was called with empty values, send TRUE back... since the user doesnt have any statusses they want to check + return 1 unless $commaSeparatedStatuses; + + return unless $self->{statuses}; + + for my $status (split /\s*,\s*/, $commaSeparatedStatuses) { + return 1 if exists $self->{statuses}{$status} || List::MoreUtils::any { $statusName{$_} eq $status } keys %{$self->{statuses}}; + } + + return; +} + +## +# boolean $Actor->cartActive() +# +# Returns whether the cart is present. +sub cartActive { + my ($self) = @_; + + if ($self->cart->isReady || + $self->statusActive('EFFECTSTATE_PUSHCART, EFFECTSTATE_PUSHCART2, EFFECTSTATE_PUSHCART3, EFFECTSTATE_PUSHCART4, EFFECTSTATE_PUSHCART5')) { + return 1; + } +} + +## +# String $Actor->statusesString() +# +# Returns human-readable list of currently active statuses. +sub statusesString { + my ($self) = @_; + + $self->{statuses} && %{$self->{statuses}} + ? join ', ', map { $statusName{$_} || $_ } keys %{$self->{statuses}} + # Translation Comment: No status effect on actor + : ''; +} + +## +# String $Actor->statusesStringAndTime(integer type) +# types: +# 0 - long +# 1 - short +# Returns human-readable list and times of currently active statuses. +sub statusesStringAndTime { + my ($self, $type) = @_; + my $msg; + + if ($self->{statuses} && %{$self->{statuses}}) { + my @keys = keys %{$self->{statuses}}; + + foreach my $key (@keys) { + my $status_name = $statusName{$key} ? $statusName{$key} : $key; + + my $time_end = $self->{'statuses'}{$key}{'time'} + ($self->{'statuses'}{$key}{'tick'}/1000); + my $status_remaining_time = int($time_end - time); + my $remaining_time = $status_remaining_time > 0 ? $status_remaining_time : -1; + + $msg .= $type ? TF("%s (%d s), ", $status_name, $remaining_time) : TF("%s (%d seconds left)\n", $status_name, $remaining_time); + } + } + + $msg =~ s/\,\s+$//g; + return $msg; +} + +## +# String $Actor->action([int index]) +# +# Returns the name of the specified action from AI sequence. +# With no index specified, returns the name of the current action. + +## +# Hash* $Actor->args([int index]) +# +# Returns arguments of the specified action from AI sequence. +# With no index specified, returns arguments of the current action. + +## +# void $Actor->queue(String name, [Hash* args]) +# +# Adds action with specified name and arguments to AI sequence. +# New action would become the current. + +## +# void $Actor->dequeue() +# +# Removes the current action from AI sequence. + +## +# void ai_clientSuspend(packet_switch, duration, args...) +# initTimeout: a number of seconds. +# +# Freeze the AI for $duration seconds. $packet_switch and @args are only +# used internally and are ignored unless XKore mode is turned on. +sub clientSuspend { + my ($self, $type, $duration, @args) = @_; + + my %args = ( + type => $type, + time => time, + timeout => $duration, + args => [@args], + ); + + debug "$self AI suspended by clientSuspend for $args{timeout} seconds\n"; + $self->queue("clientSuspend", \%args); +} + +sub setSuspend { + my ($self, $index) = @_; + $index = 0 if $index eq ''; + if ($index < $self->isa('Actor::You') ? @AI::ai_seq_args : @{$self->{slave_ai_seq_args}}) { + ( + $self->isa('Actor::You') ? $AI::ai_seq_args[$index] : $self->{slave_ai_seq_args}->[$index] + )->{suspended} = time; + } +} + +## +# boolean $Actor->attack(Bytes target_ID) +# +# Instruct AI to attack the specified enemy. +# +# TODO: replace "Bytes target_ID" with "Actor otherActor". +sub attack { + my ($self, $targetID) = @_; + + my $target = Actor::get($targetID); + return unless $target->{pos} && $target->{pos_to}; + + my %args = ( + ai_attack_giveup => { time => time, timeout => $timeout{ai_attack_giveup}{timeout} }, + ID => $targetID, + unstuck => { timeout => $timeout{ai_attack_unstuck}{timeout} || 1.5 }, + pos => { %{$target->{pos}} }, + pos_to => { %{$target->{pos_to}} }, + ); + + $self->queue('attack', \%args); + + message sprintf($self->verb(T("%s are now attacking %s\n"), T("%s is now attacking %s\n")), $self, $target); + 1; +} + +## +# void $Actor->move(int x, int y) +# +# Instruct AI to move to the specified point using a single motion. +# That point should be in LOS and be relatively nearby. +# +# See also: $Actor->route() +sub move { + my ($self, $x, $y, $attackID) = @_; + + unless ($x and $y) { + # that happens when called from AI::CoreLogic::processFollow + error "BUG: Actor::move(undef, undef) called!\n"; + return; + } + + require Task::Move; + + $self->queue('move', my $task = new Task::Move( + actor => $self, + x => $x, + y => $y, + )); + $task->{attackID} = $attackID; +} + +## +# void $Actor->route(String map, int x, int y) +# +# Instruct AI to move to the specified point using pathfinding. +# +# TODO: wouldn't it be better to place map in the end of arguments and make it optional? +sub route { + my ($self, $map, $x, $y, %args) = @_; + debug "$self on route to: $maps_lut{$map.'.rsw'}($map): $x, $y\n", "route"; + + # I can't use 'use' because of circular dependencies. + require Task::Route; + require Task::MapRoute; + + # from Homunculus AI + ($x, $y) = map { $_ ne '' ? int $_ : $_ } ($x, $y); + + my $task; + my @params = ( + actor => $self, + x => $x, + y => $y, + maxDistance => $args{maxRouteDistance}, + maxTime => $args{maxRouteTime}, + map { $_ => $args{$_} } qw(targetNpcPos distFromGoal pyDistFromGoal notifyUponArrival avoidWalls randomFactor useManhattan) + ); + + if ($map && !$args{noMapRoute}) { + $task = new Task::MapRoute(map => $map, @params); + } else { + $task = new Task::Route(field => $field, @params); + } + $task->{$_} = $args{$_} for qw(targetNpcPos attackID sendAttackWithMove attackOnRoute noSitAuto LOSSubRoute meetingSubRoute isRandomWalk isFollow isIdleWalk isSlaveRescue isMoveNearSlave isEscape isItemTake isItemGather isDeath isToLockMap runFromTarget); + + $self->queue('route', $task); +} + +## +# void $Actor->useTeleport(int level) +# +# level: 1 - Random, 2 - Respawn +# +# Instruct AI to use Teleport. +sub useTeleport { + my ($self, $level) = @_; + + if(!AI::inQueue("teleport","NPC")) { + require Task::Teleport::Random; + require Task::Teleport::Respawn; + + my %tasks = qw(1 Task::Teleport::Random 2 Task::Teleport::Respawn); + my $task = $tasks{$level}->new(actor => $self); + + $self->queue('teleport', $task); + } else { + error T("NPC or Teleport in queue, finish and try again\n"); + } +} + +sub processTask { + my $self = shift; + my $ai_name = shift; + if ($self->action eq $ai_name) { + my $task = $self->args; + if ($task->getStatus() == Task::INACTIVE) { + $task->activate(); + should($task->getStatus(), Task::RUNNING) if DEBUG; + } + if (DEBUG && $task->getStatus() != Task::RUNNING) { + require Scalar::Util; + require Data::Dumper; + # Make sure redundant information is not included in the error report. + if ($task->isa('Task::MapRoute')) { + delete $task->{ST_subtask}{solution}; + } elsif ($task->isa('Task::Route') && $task->{ST_subtask}) { + delete $task->{solution}; + } + die "Task '" . $task->getName() . "' (class " . Scalar::Util::blessed($task) . ") has status " . + Task::_getStatusName($task->getStatus()) . + ", but should be RUNNING. Object details:\n" . + Data::Dumper::Dumper($task); + } + $task->iterate(); + if ($task->getStatus() == Task::DONE) { + # We can't just dequeue the last AI sequence. Perhaps the task + # pushed a new AI sequence on the AI stack just before finishing. + # For example, the Route task does that when it's stuck. + # So, we must dequeue the correct sequence without affecting the + # others. + my ($ai_seq, $ai_seq_args) = $self->isa('Actor::You') ? (\@AI::ai_seq, \@AI::ai_seq_args) : (@{$self}{qw(slave_ai_seq slave_ai_seq_args)}); + for (my $i = 0; $i < @$ai_seq; $i++) { + if ($ai_seq->[$i] eq $ai_name) { + splice(@$ai_seq, $i, 1); + splice(@$ai_seq_args, $i, 1); + last; + } + } + my %args = @_; + my $error = $task->getError(); + if ($error) { + if ($args{onError}) { + $args{onError}->($task, $error); + } else { + error("$error->{message}\n"); + } + } elsif ($args{onSuccess}) { + $args{onSuccess}->($task); + } + } + } +} + +## +# void $Actor->sendAttackStop() +# +# Send "stop attacking" to the server. +sub sendAttackStop { + my ($self) = @_; + + $self->sendMove(@{calcPosFromPathfinding($field, $self)}{qw(x y)}); +} + +## +# void $Actor->sendMove(int x, int y) +# +# Send "move to the specified point" to the server. + +## +# void $Actor->sendSit() +# +# Send "sit" to the server. + +## +# void $Actor->sendStand() +# +# Send "stand" to the server. + +## +# void $Actor->sendStandBy() +# +# Send "standby" to the server. + +## +# void $Actor->hairColor() +# +# Returns proper hair color +sub hairColor { + my ($self) = @_; + + return $self->{hair_pallete} if exists $self->{hair_pallete} && $self->{hair_pallete}; + return $self->{hair_color} if exists $self->{hair_color}; + return undef; +} + +1; diff --git a/openkore_llm_knowledge/core/src/ActorList.pm b/openkore_llm_knowledge/core/src/ActorList.pm new file mode 100644 index 0000000000..d25f93b716 --- /dev/null +++ b/openkore_llm_knowledge/core/src/ActorList.pm @@ -0,0 +1,202 @@ +######################################################################### +# OpenKore - Actor list +# +# Copyright (c) 2006 OpenKore Development Team +# +# This software is open source, licensed under the GNU General Public +# License, version 2. +# Basically, this means that you're allowed to modify and distribute +# this software. However, if you distribute modified versions, you MUST +# also distribute the source code. +# See http://www.gnu.org/licenses/gpl.html for the full license. +######################################################################### +## +# MODULE DESCRIPTION: List of actors +# +# Derived from: @CLASS(ObjectList) +# +# The ActorList class holds a list of Actor-objects. +# OpenKore stores lists of players/monsters/portals/NPCs/etc. in lists +# of this type. Those lists are $playersList, $monstersList, $npcsList, +# $petsList and $portalsList, defined in Globals.pm. +# +#

    Differences compared to ObjectList

    +# All items in ActorList are of the same class, and are all a +# subclass of @CLASS(Actor). +package ActorList; + +use strict; +use Carp::Assert; +use Utils::Assert; +use Utils::ObjectList; +use base qw(ObjectList); + +use Actor; +use Actor::Item; +use Actor::You; +use Actor::Player; +use Actor::Monster; +use Actor::Party; +use Actor::NPC; +use Actor::Portal; +use Actor::Pet; +use Actor::Slave; +use Actor::Unknown; +use Actor::Elemental; + +### CATEGORY: Class ActorList + +## +# ActorList ActorList->new(String type) +# type: The name of the class that all items in this ActorList must be. +# Requires: defined($type) && UNIVERSAL::isa($type, "Actor") +# Ensures: $self->size() == 0 +# +# Creates a new ActorList object. +sub new { + my ($class, $type) = @_; + assert(defined $type, "You must specify a type for the new Actor list") if DEBUG; + # Note: we pass a string to ActorList's new method, not an object: http://stackoverflow.com/questions/204316/why-shouldnt-i-use-universalisa + # this might cause problems + assertClass($type, "Actor") if DEBUG; + + my $self = $class->SUPER::new(); + + # Invariant: defined(type) + $self->{AL_type} = $type; + + # Hash IDmap + # Maps an actor ID ($Actor->{ID}) to an actor. Used + # for fast lookups of actors based on IDs. + # + # Invariant: + # defined(IDmap) + # scalar(keys IDmap) == size() + # for all values $v in IDmap: + # find($v) != -1 + $self->{IDmap} = {}; + + return $self; +} + +## +# int $ActorList->add(Actor actor) +# Requires: +# defined($actor) +# defined($actor->{ID}) +# $self->find($actor) == -1 +# +# Adds an actor to this ActorList. +# +# This method overloads $ObjectList->add(), and has a stronger precondition. +# See the documentation for that method for more information about this +# method. +sub add { + my ($self, $actor) = @_; + assert(defined $actor, "Actor must be defined when adding to ActorList") if DEBUG; + assert($actor->isa($self->{AL_type}), "Actor type must match with ActorList type") if DEBUG; + assert(defined $actor->{ID}, "Actor must have an ID when adding to ActorList") if DEBUG; + assert(!exists $self->{IDmap}{$actor->{ID}}, "Actor ID in ActorList must be unique") if DEBUG; + + $self->{IDmap}{$actor->{ID}} = $actor; + return $self->SUPER::add($actor); +} + +## +# Actor $ActorList->getByID(Bytes ID) +# Returns: An Actor, or undef if there is no actor with that ID in this list. +# Requires: defined($ID) +# Ensures: if defined(result): result->{ID} eq $ID +# +# Looks up an Actor object based on the actor ID. +# +# See also: $Actor->{ID} +sub getByID { + my ($self, $ID) = @_; + assert(defined $ID, "This method requires a defined ID") if DEBUG; + return $self->{IDmap}{$ID}; +} + +## +# Actor $ActorList->getByName(String name) +# Returns: the associated Actor object or undef. +# Requires: defined($name) +# Ensures: defined(result) +# +# Returns the Actor object for $name. This function will look at the various +# actor lists. If $name is not in any of the actor lists, it will return +# undef. +sub getByName { + my ($self, $name) = @_; + assert(defined $name, "This method requires a defined actor name") if DEBUG; + for my $actor (@$self) { + if (lc($actor->{name}) eq lc($name)) { + return $actor; + } + } + return undef; +} + +## +# boolean $ActorList->remove(Actor actor) +# Requires: defined($actor) && defined($actor->{ID}) +# +# Removes an actor from this ActorList. +# +# This method overloads $ObjectList->remove(), and has a stronger precondition. +# See the documentation for that method for more information about this +# method. +sub remove { + my ($self, $actor) = @_; + assert(defined $actor, "This method requires a defined actor as argument") if DEBUG; + assertClass($actor, $self->{AL_type}) if DEBUG; + assert(defined $actor->{ID}, "Actor must have an ID when removing from ActorList") if DEBUG; + + my $result = $self->SUPER::remove($actor); + if ($result) { + delete $self->{IDmap}{$actor->{ID}}; + } + return $result; +} + +## +# boolean $ActorList->removeByID(Bytes ID) +# ID: The ID of the actor to remove. +# Returns: Whether the actor with the specified ID was in the list. +# Requires: defined($ID) +# +# Removes an actor based on the actor ID. This will trigger an onRemove event +# before the actor is removed. +# +# See also: $Actor->{ID} +sub removeByID { + my ($self, $ID) = @_; + my $actor = $self->getByID($ID); + if (defined $actor) { + return $self->remove($actor); + } else { + return 0; + } +} + +# overloaded +sub doClear { + my ($self) = @_; + $self->SUPER::doClear(); + $self->{IDmap} = {}; +} + +# overloaded +sub checkValidity { + my ($self) = @_; + $self->SUPER::checkValidity(); + + assert(defined $self->{AL_type}, "ActorList has no type"); + assert(defined $self->{IDmap}, "ActorList has invalid content (IDmap is undefined)"); + should(scalar(keys %{$self->{IDmap}}), $self->size()); + foreach my $v (values %{$self->{IDmap}}) { + assert($self->find($v) != -1, "Failed to find a value that should be in ActorList"); + } +} + +1; diff --git a/openkore_llm_knowledge/core/src/Commands.pm b/openkore_llm_knowledge/core/src/Commands.pm new file mode 100644 index 0000000000..ea84ae5967 --- /dev/null +++ b/openkore_llm_knowledge/core/src/Commands.pm @@ -0,0 +1,8750 @@ +######################################################################### +# OpenKore - Commandline +# +# This software is open source, licensed under the GNU General Public +# License, version 2. +# Basically, this means that you're allowed to modify and distribute +# this software. However, if you distribute modified versions, you MUST +# also distribute the source code. +# See http://www.gnu.org/licenses/gpl.html for the full license. +# +# $Revision$ +# $Id$ +# +######################################################################### +## +# MODULE DESCRIPTION: Commandline input processing +# +# This module processes commandline input. + +package Commands; + +use strict; +use warnings; +no warnings qw(redefine uninitialized); +use Time::HiRes qw(time); +use utf8; + +use Modules 'register'; +use Globals; +use Log qw(message debug error warning); +use Field; +use Misc; +use Network; +use Network::Send (); +use Settings; +use Plugins; +use Skill; +use Utils; +use Utils::Exceptions; +use AI; +use Task; +use Task::ErrorReport; +use Match; +use Translation; +use Network::PacketParser qw(STATUS_STR STATUS_AGI STATUS_VIT STATUS_INT STATUS_DEX STATUS_LUK); + +our (%commands, %completions); +my $needsInit = 1; + +undef %commands; +undef %completions; + +sub initHandlers { + register( + ['a', [ + T("Attack a monster."), + [ T(""), T("attack the specified monster") ], + ], \&cmdAttack], + ['achieve', [ + T("Achievement management"), + [ "list", T("shows all current achievements") ], + [ "info ", T("shows information about the achievement") ], + [ "reward ", T("request reward for the achievement of achievementID") ], + ], \&cmdAchieve], + ['ai', [ + T("Enable/disable AI."), + ["", T("toggles AI on/manual/off")], + ["on", T("enables AI")], + ["off", T("disables AI")], + ["manual", T("makes AI manual")], + ["ai_v", T("displays the contents of the %ai_v hash, for debugging purposes")], + ["clear", T("clears AI sequences")], + ["print", T("displays detailed info about current AI sequence")] + ], \&cmdAI], + ['aiv', T("Display current AI sequences."), \&cmdAIv], + ['al', T("Display the status of your vending shop."), \&cmdShopInfoSelf], + ['arrowcraft', [ + T("Create Arrows."), + ["", T("lists available arrow-crafting items")], + ["use", T("use the Archer's Arrow Craft skill")], + [T(""), T("create arrows using an item from the 'arrowcraft' list")], + [T("forceuse "), T("craft arrows immediately from an item without using the skill")] + ], \&cmdArrowCraft], + ['as', T("Stop attacking a monster."), \&cmdAttackStop], + ['attendance', [ + T("Attendance System."), + ["open", T("Attendance System")], + ["request", T("Request the Current Day Reward")], + ], \&cmdAttendance], + ['autobuy', T("Initiate auto-buy AI sequence."), \&cmdAutoBuy], + ['autosell', [ + T("Auto-sell AI sequence."), + ["", T("Initiate auto-sell AI sequence")], + ["test", T("Simulate list of items to sell (synonym: 'simulate' or 'debug')")] + ], \&cmdAutoSell], + ['autostorage', T("Initiate auto-storage AI sequence."), \&cmdAutoStorage], + ['auth', [ + T("(Un)authorize a user for using Kore chat commands."), + [T(" 0"), T("unauthorize ")], + [T(" 1"), T("authorize ")] + ], \&cmdAuthorize], + ['bangbang', T("Does a bangbang body turn."), \&cmdBangBang], + ['bingbing', T("Does a bingbing body turn."), \&cmdBingBing], + ['bank', [ + T("Banking management."), + ["open", T("Open Banking Interface")], + ["deposit", T("Deposit Zeny in Banking")], + ["withdraw", T("Withdraw Zeny from Banking")], + ], \&cmdBank], + ['bg', [ + T("Send a message in the battlegrounds chat."), + [T(""), T("send in the battlegrounds chat")] + ], \&cmdChat], + ['bl', undef, \&cmdBuyerList], + ['booking', T("Interact with a group booking"), \&cmdBooking], + ['buy', [ + T("Buy an item from the current NPC shop"), + [T(" []"), T("buy items from the 'store' list")] + ], \&cmdBuy], + ['buyer', undef, \&cmdBuyer], + ['bs', undef, \&cmdBuyShopInfoSelf], + ['c', [ + T("Chat in the public chat."), + [T(""), T("send to public chat")] + ], \&cmdChat], + ['canceltransaction', undef, \&cmdCancelTransaction], + ['card', [ + T("Card compounding."), + ["list", T("lists cards in the inventory")], + ["use ", T("initiate card compounding using the specified card")], + ["mergelist", T("lists items to merge card with")], + ["mergecancel", T("cancel a card merge request")], + ["merge ", T("merge card with item and finalize card compounding")], + ["forceuse ", T("instantly merge the card with an item")] + ], \&cmdCard], + ['cart', [ + T("Cart management"), + ["", T("lists items in cart.")], + ["add []", T("add items from inventory to cart")], + ["get []", T("get items from cart to inventory")], + ["desc []", T("displays cart item description")] + ], \&cmdCart], + ['cash', [ + T("Cash shop management"), + ["open", T("open Cash shop")], + ["close", T("close Cash shop")], + [T("buy [] []"), T("buy items from Cash shop")], + ["points", T("show the number of available Cash shop points")], + ["list", T("lists the Cash shop items")], + ], \&cmdCash], + ['cashbuy', [ + T("Buy Cash item"), + [" [][, []]...", T("buy items from cash dealer")], + ], \&cmdCashShopBuy], + ['charselect', T("Ask server to exit to the character selection screen."), \&cmdCharSelect], + ['chat', [ + T("Chat room management."), + ["list", T("lists chat rooms on screen")], + [T("join "), T("join a chat room")], + ["info", T("displays info about the current chat room")], + ["leave", T("leave the current chat room")], + [T("create \"\" [<limit #> <public flag> <password>]"), T("create a chat room")], + [T("modify \"<title>\" [<limit #> <public flag> <password>]"), T("modify the current chat room")], + [T("bestow <user #>"), T("bestow admin to chat room user")], + [T("kick <user #>"), T("kick a chat room user")] + ], \&cmdChatRoom], + ['chist', [ + T("Display last few entries from the chat log."), + ["", T("display last 5 entries")], + [T("<number>"), T("display last <number> entries")] + ], \&cmdChist], + ['cil', T("Clear the item log."), \&cmdItemLogClear], + ['cln', undef, \&cmdChat], + ['clearlog', T("Clear the chat log."), \&cmdChatLogClear], + ['closeshop', T("Close your vending shop."), \&cmdCloseShop], + ['closebuyshop', undef, \&cmdCloseBuyShop], + ['closebuyershop', undef, \&cmdCloseBuyerShop], + ['conf', [ + T("Change a configuration key"), + [T("<key>"), T("displays value of <key>")], + [T("<key> <value>"), T("sets value of <key> to <value>")], + [T("<key> none"), T("unsets <key>")], + [T("<label>.<attribute>"), T("displays value of the specified configuration key through label")], + [T("<label>.<attribute> <value>"), T("set a new value for the specified configuration key through label")], + [T("<label>.<attribute> none"), T("unset the specified configuration key through label")], + [T("<label>.block"), T("display the current value of the specified block")], + [T("<label>.block <value>"), T("set a new value for the specified block through <label>")], + [T("<label>block none"), T("unset the specified block through <label>")] + ], \&cmdConf], + ['connect', undef, \&cmdConnect], + ['create', undef, \&cmdCreate], + ['damage', [ + T("Damage taken report"), + ["", T("displays the damage taken report")], + ["reset", T("resets the damage taken report")] + ], \&cmdDamage], + ['dead', undef, \&cmdDeadTime], + ['deal', [ + T("Trade items with another player."), + ["", T("accept an incoming deal/finalize the current deal/trade")], + [T("<player #> | <player_name>"), T("request a deal with player")], + [T("add <inventory item #> [<amount>]"), T("add items to current deal")], + [T("add z [<amount>]"), T("add zenny to current deal")], + ["no", T("deny an incoming deal/cancel the current deal")] + ], \&cmdDeal], + ['debug', [ + T("Toggle debug on/off."), + [T("<level>"), T("sets debug level to <level>")], + ["info", T("displays debug information")] + ], \&cmdDebug], + ['dl', T("List items in the current deal."), \&cmdDealList], + ['doridori', T("Does a doridori head turn."), \&cmdDoriDori], + ['drop', [ + T("Drop an item from the inventory."), + [T("<inventory_item_list> [<amount>]"), T("drop an item from inventory")] + ], \&cmdDrop], + ['dump', T("Dump the current packet receive buffer and quit."), \&cmdDump], + ['dumpnow', T("Dump the current packet receive buffer without quitting."), \&cmdDumpNow], + ['e', [ + T("Show emotion."), + [T("<emotion>"), T("show specified emotion (see tables/emotions.txt)")] + ], \&cmdEmotion], + ['eq', [ + T("Equip an item."), + [T("<inventory item #>"), T("equips the specified item")], + [T("<slotname> <inventory item #>"), T("equips the specified item on the specified slot")], + ["slots", T("lists slot names")] + ], \&cmdEquip], + ['eqsw', [ + T("Equip an switch item."), + [T("<inventory item #>"), T("equips the specified item")], + [T("<slotname> <inventory item #>"), T("equips the specified item on the specified slot")], + ["slots", T("lists slot names")] + ], \&cmdEquipSwitch], + ['elemental', undef, \&cmdElemental], + ['eval', [ + T("Evaluate a Perl expression."), + [T("<expression>"), T("evaluate a Perl expression")] + ], \&cmdEval], + ['exp', [ + T("Experience report."), + ["", T("displays the experience report")], + ["monster", T("display report on monsters killed")], + ["item", T("display report on inventory changes")], + ["report", T("display detailed report on experience gained, monsters killed and items gained")], + ["reset", T("resets the experience report")], + ["output", T("output the experience report in file 'exp.txt'")] + ], \&cmdExp], + ['falcon', [ + T("Falcon status."), + ["", T("displays falcon status")], + ["release", T("releases your falcon")] + ], \&cmdFalcon], + ['follow', [ + T("Follow another player."), + [T("<player name|player #>"), T("follow the specified player")], + ["stop", T("stop following")] + ], \&cmdFollow], + ['friend', [ + T("Friend management."), + ["", T("lists friends")], + [T("request <player name|player #>"), T("requests player to be your friend")], + ["accept", T("accepts a friend request")], + ["reject", T("rejects a friend request")], + [T("pm <friend #>"), T("pm a friend")], + [T("remove <friend #>"), T("remove a friend from friends list")], + ], \&cmdFriend], + ['homun', [ + T("Interact with homunculus."), + ["s", T("display homunculus status")], + ["status", T("display homunculus status")], + ["feed", T("feed your homunculus. (Food needed)")], + ["rename", T("rename your homunculus")], + ["fire", T("delete your homunculus")], + ["delete", T("delete your homunculus")], + ["move <x> <y>", T("moves your homunculus")], + ["standby ", T("makes your homunculus standby")], + ["aiv ", T("display current homunculus AI ")], + ["ai", T("toggles AI on, off or manual ")], + ["on ", T("turns homunculus AI on")], + ["auto", T("turns homunculus AI on")], + ["manual", T("turns homunculus AI to manual")], + ["off", T("turns homunculus AI off")], + ["clear", T("clears homunculus AI")], + ["print", T("prints homunculus AI")], + ["skills", T("displays homunculus skills")], + [T("skills add <skill #>"), T("add a skill point to the current homunculus skill")], + [T("desc <skill #>"), T("display a description of the specified homunculus skill")] + ], \&cmdSlave], + ['misc_conf', [ + T("Send to Server Misc Configuration."), + ["show_eq (on|off)", T("Allow / Disable Show Equipment Window")], + ["call (on|off)", T("Allow / Disable being Summoned by Urgent Call or Marriage skills")], + ["pet_feed (on|off)", T("Enable / Disable Pet Auto-Feed")], + ["homun_feed (on|off)", T("Enable / Disable Homunculus Auto-Feed")], + ], \&cmdMiscConf], + ['merc', [ + T("Interact with Mercenary."), + ["s", T("display mercenary status")], + ["status", T("display mercenary status")], + ["fire", T("fires your mercenary")], + ["move <x> <y>", T("moves your mercenary")], + ["standby", T("makes your mercenary standby")], + ["aiv", T("display current mercenary AI")], + ["ai", T("toggles AI on, off or manual")], + ["on", T("turns mercenary AI on")], + ["auto", T("turns mercenary AI on")], + ["manual", T("turns mercenary AI to manual")], + ["off", T("turns mercenary AI off")], + ["clear", T("clears mercenary AI")], + ["print", T("prints mercenary AI")], + ["skills", T("displays mercenary skills")], + [T("skills add <skill #>"), T("add a skill point to the current mercenary skill")], + [T("desc <skill #>"), T("display a description of the specified mercenary skill")] + ], \&cmdSlave], + ['g', [ + T("Chat in the guild chat."), + ["<message>", T("send <message> to guild chat")] + ], \&cmdChat], + ['getplayerinfo', [ + T("Get the name of the player with specified ID"), + ["<player ID>", T("show the name of the specified ID (needs debug 2)")] + ], \&cmdGetPlayerInfo], + ['getcharname', undef, \&cmdGetCharacterName], + # GM Commands - Start + ['gmb', undef, \&cmdGmb], + ['gmbb', undef, \&cmdGmb], + ['gmnb', undef, \&cmdGmb], + ['gmlb', undef, \&cmdGmb], + ['gmlbb', undef, \&cmdGmb], + ['gmlnb', undef, \&cmdGmb], + ['gmmapmove', undef, \&cmdGmmapmove], + ['gmcreate', undef, \&cmdGmcreate], + ['gmhide', undef, \&cmdGmhide], + ['gmwarpto', undef, \&cmdGmwarpto], + ['gmsummon', undef, \&cmdGmsummon], + ['gmrecall', undef, \&cmdGmrecall], + ['gmremove', undef, \&cmdGmremove], + ['gmdc', undef, \&cmdGmdc], + ['gmresetskill', undef, \&cmdGmresetskill], + ['gmresetstate', undef, \&cmdGmresetstate], + ['gmmute', undef, \&cmdGmmute], + ['gmunmute', undef, \&cmdGmunmute], + ['gmkickall', undef, \&cmdGmkickall], + # GM Commands - End + ['guild', [ + T("Guild management."), + ["", T("request guild info")], + ["info", T("displays guild info")], + ["members", T("displays guild member info")], + [T("create <guild name>"), T("create a guild")], + [T("request <player name|player #>"), T("request player to join your guild")], + [T("join <flag>"), T("accepts a guild join request if <flag> is 1, deny if 0")], + [T("ally <player name|player #>"), T("request alliance to another guild")], + ["leave", T("leave the guild")], + [T("kick <guild member #> <reason>"), T("kick a guild member out of the guild")], + [T("break <guild name>"), T("disband your guild")] + ], \&cmdGuild], + ['help', [ + T("Help displays commands"), + ["", T("lists available commands")], + [T("<command>"), T("displays detailed information about a command")] + ], \&cmdHelp], + ['i', [ + T("Display inventory items."), + ["", T("display all inventory items.")], + ["eq", T("lists equipped items")], + ["neq", T("lists unequipped items")], + ["nu", T("lists non-usable items")], + ["u", T("lists usable items")], + [T("desc <inventory item #>"), T("displays inventory item description")] + ], \&cmdInventory], + ['identify', [ + T("Identify an unindentified item."), + ["", T("lists items to be identified")], + [T("<identify #>"), T("identify an item")] + ], \&cmdIdentify], + ['ignore', [ + T("Ignore a user (block their messages)."), + [T("<flag> <player name>"), T("ignores a player if <flag> is 1, unignore if 0")], + [T("<flag> all"), T("ignores all players if <flag> is 1, unignore if 0")] + ], \&cmdIgnore], + ['ihist', [ + T("Displays last few entries of the item log."), + ["", T("display last 5 entries")], + [T("<number>"), T("display last <number> entries")] + ], \&cmdIhist], + ['il', T("Display items on the ground."), \&cmdItemList], + ['im', [ + T("Use item on monster."), + [T("<inventory item #> <monster #>"), T("use item on monster")] + ], \&cmdUseItemOnMonster], + ['ip', [ + T("Use item on player."), + [T("<inventory item #> <player #>"), T("use item on player")] + ], \&cmdUseItemOnPlayer], + ['is', [ + T("Use item on yourself."), + [T("<inventory item #>"), T("use item on yourself")] + ], \&cmdUseItemOnSelf], + ['kill', [ + T("Attack another player (PVP/GVG only)."), + [T("<player #>"), T("attack the specified player")] + ], \&cmdKill], + ['look', [ + T("Look in a certain direction."), + [T("<body dir> [<head dir>]"), T("look at <body dir> (0-7) with head at <head dir> (0-2)")] + ], \&cmdLook], + ['lookp', [ + T("Look at a certain player."), + [T("<player #>"), T("look at player")] + ], \&cmdLookPlayer], + ['memo', T("Save current position for warp portal."), \&cmdMemo], + ['memorial', [ + ["destroy",T("Destroy an instance.")] + ], \&cmdMemorialDungeonDestroy], + ['ml', T("List monsters that are on screen."), \&cmdMonsterList], + ['move', [ + T("Move your character."), + [T("<x> <y> [<map name>]"), T("move to the coordinates on a map")], + [T("<map name>"), T("move to map")], + [T("<portal #>"), T("move to nearby portal")], + ["stop", T("stop all movement")] + ], \&cmdMove], + ['nc', [ + T("NPC Create."), + ["", T("Create NPC by default name 'GOLDPCCAFE'")], + [T("<name>"), T("Create NPC by <name>")], + ], \&cmdNPCCreateRequest], + ['nl', T("List NPCs that are on screen."), \&cmdNPCList], + ['openbuyershop', undef, \&cmdOpenBuyerShop], + ['openshop', T("Open your vending shop."), \&cmdOpenShop], + ['p', [ + T("Chat in the party chat."), + [T("<message>"), T("send <message> to party chat")] + ], \&cmdChat], + ['party', [ + T("Party management."), + ["", T("displays party member info")], + [T("create \"<party name>\""), T("organize a party")], + [T("share <flag>"), T("sets party EXP sharing to even if flag is 1, individual take if 0")], + [T("shareitem <flag>"), T("sets party ITEM sharing to even if flag is 1, individual take if 0")], + [T("sharediv <flag>"), T("sets party ITEM PICKUP sharing to even if flag is 1, individual take if 0")], + [T("shareauto"), T("set party EXP sharing auto by AI")], + [T("request <player #>"), T("request player to join your party")], + [T("join <flag>"), T("accept a party join request if <flag> is 1, deny if 0")], + [T("kick <party member #>"), T("kick party member from party")], + ["leave", T("leave the party")] + ], \&cmdParty], + ['pecopeco', [ + T("Pecopeco status."), + ["", T("display pecopeco status")], + ["release", T("release your pecopeco")] + ], \&cmdPecopeco], + ['pet', [ + T("Pet management."), + ["s", T("displays pet status")], + ["status", T("displays pet status")], + [T("c <monster #>"), T("captures a monster")], + [T("capture <monster #>"), T("captures a monster")], + [T("hatch <egg #>"), T("hatches a pet egg, but first you should use the item Pet Incubator")], + ["info", T("sends pet menu")], + ["feed", T("feeds your pet")], + ["performance", T("plays with your pet")], + ["return", T("sends your pet back to the egg")], + ["unequip", T("unequips your pet")], + [T("name <name>"), T("changes the name of the pet")] + ], \&cmdPet], + ['petl', T("List pets that are on screen."), \&cmdPetList], + ['pl', [ + T("List players that are on screen."), + ["", T("lists players on screen")], + [T("<player #>"), T("displays detailed info about a player")], + ["p", T("lists party players on screen")], + ["g", T("lists guild players on screen")] + ], \&cmdPlayerList], + ['plugin', [ + T("Control plugins."), + ["", T("lists loaded plugins")], + [T("load <filename>"), T("loads a plugin file")], + [T("reload <plugin name|plugin #>"), T("reloads a loaded plugin")], + [T("unload <plugin name|plugin #>"), T("unloads a loaded plugin")], + ["help", T("displays plugin help")] + ], \&cmdPlugin], + ['pm', [ + T("Send a private message."), + [T("<player name|PM list #> <message>"), T("send <message> to player through PM")] + ], \&cmdPrivateMessage], + ['pml', T("Quick PM list."), \&cmdPMList], + ['poison', [ + T("Apply Poison in Weapon."), + ["", T("lists available Poisons")], + ["use", T("use the Guillotine Cross Poisonous Weapon Skill")], + [T("<poison #>"), T("Apply poison using an item from the 'poison' list")], + ], \&cmdPoison], + ['privateairship', [ + T("Use the Private Airship service."), + [T("<map name> [<item ID>]"), T("request teleport to <map name> using the specified item (default: Passport ID 25464)")], + ], \&cmdPrivateAirship], + ['portals', [ + T("List portals that are on screen."), + ["", T("list portals that are on screen")], + ["recompile", T("recompile portals")], + ["add", T("add new portals: <map1> <x> <y> <map2> <x> <y>")], + ], \&cmdPortalList], + ['quit', [ + T("Exit this program."), + ["", T("exit this program")], + ["2", T("send a special package 'quit_request' to the server, then exit this program")], + ], \&cmdQuit], + ['rc', [ + T("Reload source code files."), + ["", T("reload functions.pl")], + [T("<module names>"), T("reload module files in the space-separated <module names>")] + ], \&cmdReloadCode], + ['rc2', undef, \&cmdReloadCode2], + ['reload', [ + T("Reload configuration files."), + ["all", T("reload all control and table files")], + [T("<names>"), T("reload control files in the list of <names>")], + [T("all except <names>"), T("reload all files except those in the list of <names>")] + ], \&cmdReload], + ['relog', [ + T("Log out then log in again."), + ["", T("logout and login after 5 seconds")], + [T("<seconds>"), T("logout and login after <seconds>")], + [T("<min>..<max>"), T("logout and login after random seconds")] + ], \&cmdRelog], + ['repair', [ + T("Repair player's items."), + ["", T("list of items available for repair")], + [T("<item #>"), T("repair the specified player's item")], + [T("cancel"), T("cancel repair item")], + ], \&cmdRepair], + ['reputation', T("Show the Reputation Status"), \&cmdReputation], + ['respawn', T("Respawn back to the save point."), \&cmdRespawn], + ['revive', [ + T("Use of the 'Token Of Siegfried' to self-revive."), + ["", T("use of the 'Token Of Siegfried' to self-revive")], + ["force", T("trying to self-revive using")], + ["\"<item_name>\"", T("check <item_name> availability, then trying to self-revive")], + ["<item_ID>", T("check <item_ID> availability, then trying to self-revive")], + ], \&cmdRevive], + ['rodex', [ + T("rodex use (Ragnarok Online Delivery Express)"), + ["open", T("open rodex mailbox")], + ["open <0 | 1 | 2>", T("open rodex mailbox with a specific type")], + ["close", T("close rodex mailbox")], + ["list", T("list your first page of rodex mail")], + ["nextpage", T("request and get the next page of rodex mail")], + ["maillist", T("show ALL messages from ALL pages of rodex mail")], + ["refresh", T("send request to refresh and update rodex mailbox")], + ["read <mail_# | mail_id>", T("open the selected Rodex mail")], + ["getitems", T("get items of current rodex mail")], + ["getitems <mail_# | mail_id>", T("get items of rodex mail")], + ["getzeny", T("get zeny of current rodex mail")], + ["getzeny <mail_# | mail_id>", T("get zeny of rodex mail")], + ["write", T("open a box to start write a rodex mail")], + ["write <player_name | self>", T("open a box to start write a rodex mail to the specified player")], + ["settarget <player_name|self>", T("set target of rodex mail")], + ["itemslist", T("show current list of items in mail box that you are writting")], + ["settitle <title>", T("set rodex mail title")], + ["setbody <body>", T("set rodex mail body")], + ["setzeny <zeny_amount>", T("set zeny amount in rodex mail")], + ["add <item #> <amount>", T("add a item from inventory in rodex mail box")], + ["remove <item #> <amount>", T("remove a item or amount of item from rodex mail")], + ["draft", T("show draft rodex mail before sending")], + ["send", T("send finished rodex mail")], + ["cancel", T("close rodex mail write box")], + ["delete <mail_# | mail_id>", T("delete selected rodex mail")] + ], \&cmdRodex], + ['roulette', [ + T("Roulette System."), + ["open", T("Open Roulette System")], + ["info", T("Send Roulette System Info Request")], + ["close", T("Close Roulette System")], + ["start", T("Start Roulette System")], + ["claim", T("Claim Reward in Roulette System")], + ], \&cmdRoulette], + ['s', T("Display character status."), \&cmdStatus], + ['sell', [ + T("Sell items to an NPC."), + [T("<inventory item #> [<amount>]"), T("put inventory items in sell list")], + ["list", T("show items in the sell list")], + ["done", T("sell everything in the sell list")], + ["cancel", T("clear the sell list")] + ], \&cmdSell], + ['send', [ + T("Send a raw packet to the server."), + [T("<hex string>"), T("sends a raw packet to connected server")] + ], \&cmdSendRaw], + ['sit', T("Sit down."), \&cmdSit], + ['skills', [ + T("Skills management."), + ["", T("Lists available skills.")], + [T("add <skill #>"), T("add a skill point")], + [T("desc <skill #>"), T("displays skill description")] + ], \&cmdSkills], + ['sll', T("Display a list of slaves in your immediate area."), \&cmdSlaveList], + ['spells', T("List area effect spells on screen."), \&cmdSpells], + ['starplace', [ + T("Starplace Agree"), + ["sun", T("select sun as starplace")], + ["moon", T("select mon as starplace")], + ["star", T("select star as starplace")], + ], \&cmdStarplace], + ['storage', [ + T("Handle items in Kafra storage."), + ["", T("lists items in storage")], + ["eq", T("lists equipments in storage")], + ["nu", T("lists non-usable items in storage")], + ["u", T("lists usable items in storage")], + [T("add <inventory item #> [<amount>]"), T("adds inventory item to storage")], + [T("addfromcart <cart item #> [<amount>]"), T("adds cart item to storage")], + [T("get <storage item #> [<amount>]"), T("gets item from storage to inventory")], + [T("gettocart <storage item #> [<amount>]"), T("gets item from storage to cart")], + ["close", T("close storage")], + ["log", T("logs storage items to logs/storage.txt")] + ], \&cmdStorage], + ['store', [ + T("Display shop items from NPC."), + ["", T("lists available shop items from NPC")], + [T("desc <store item #>"), T("displays store item description")] + ], \&cmdStore], + ['sl', [ + T("Use skill on location."), + [T("<skill #> <x> <y> [<level>]"), T("use skill on location")] + ], \&cmdUseSkill], + ['sm', [ + T("Use skill on monster."), + [T("<skill #> <monster #> [<level>]"), T("use skill on monster")] + ], \&cmdUseSkill], + ['sp', [ + T("Use skill on player."), + [T("<skill #> <player #> [<level>]"), T("use skill on player")] + ], \&cmdUseSkill], + ['ss', [ + T("Use skill on self."), + [T("<skill #> [<level>]"), T("use skill on self")], + [T("start <skill #> [<level>]"), T("start use skill on self")], + [T("stop"), T("stop use skill on self")] + ], \&cmdUseSkill], + ['ssl', [ + T("Use skill on slave."), + [T("<skill #> <target #> <skill level>"), T("use skill on slave")] + ], \&cmdUseSkill], + ['ssp', [ + T("Use skill on ground spell."), + [T("<skill #> <target #> [<skill level>]"), T("use skill on ground spell")] + ], \&cmdUseSkill], + ['st', T("Display stats."), \&cmdStats], + ['stand', T("Stand up."), \&cmdStand], + ['stat_add', [ + T("Add status point."), + ["str|agi|int|vit|dex|luk", T("add status points to a stat")] + ], \&cmdStatAdd], + ['switchconf', [ + T("Switch configuration file."), + [T("<filename>"), T("switches configuration file to <filename>")] + ], \&cmdSwitchConf], + ['switch_equips', T("Switch Equips"), \&cmdSwitchEquips], + ['take', [ + T("Take an item from the ground."), + [T("<item #>"), T("take an item from the ground")], + ["first", T("take the first item on the ground")] + ], \&cmdTake], + ['talk', [ + T("Manually talk to an NPC."), + [T("<NPC #>"), T("talk to an NPC")], + ["cont", T("continue talking to NPC")], + ["resp", T("lists response options to NPC")], + [T("resp <response #>"), T("select a response to NPC")], + [T("num <number>"), T("send a number to NPC")], + [T("text <string>"), T("send text to NPC")], + ["no", T("ends/cancels conversation with NPC")] + ], \&cmdTalk], + ['talknpc', [ + T("Send a sequence of responses to an NPC."), + [T("<x> <y> <NPC talk codes>"), T("talk to the NPC standing at <x> <y> and use <NPC talk codes>")] + ], \&cmdTalkNPC], + ['tank', [ + T("Tank for a player."), + [T("<player name|player #>"), T("starts tank mode with player as tankModeTarget")], + ["stop", T("stops tank mode")] + ], \&cmdTank], + ['tele', T("Teleport to a random location."), \&cmdTeleport], + ['testshop', T("Show what your vending shop would sell."), \&cmdTestShop], + ['timeout', [ + T("Set a timeout."), + [T("<type>"), T("displays value of <type>")], + [T("<type> <second>"), T("sets value of <type> to <seconds>")] + ], \&cmdTimeout], + ['top10', [ + T("Displays top10 ranking."), + ["top10 (a | alche | alchemist)", T("displays Alchemist's top10 ranking")], + ["top10 (b | black | blacksmith)", T("displays Blackmith's top10 ranking")], + ["top10 (p | pk | pvp)", T("displays PVP top10 ranking")], + ["top10 (t | tk | taekwon)", T("displays Taekwon's top10 ranking")] + ], \&cmdTop10], + ['uneq', [ + T("Unequp an item."), + [T("<inventory item #>"), T("unequips the specified item")] + ], \&cmdUnequip], + ['uneqsw', [ + T("Unequp an switch item."), + [T("<inventory item #>"), T("unequips the specified item")] + ], \&cmdUnequipSwitch], + ['vender', [ + T("Buy items from vending shops."), + [T("<vender #>"), T("enter vender shop")], + [T("<vender #> <vender_item #> [<amount>]"), T("buy items from vender shop")], + ["end", T("leave current vender shop")] + ], \&cmdVender], + ['version', T("Display the version of openkore."), \&cmdVersion], + ['vl', T("List nearby vending shops."), \&cmdVenderList], + ['vs', T("Display the status of your vending shop."), \&cmdShopInfoSelf], + ['warp', [ + T("Open warp portal."), + ["list", T("lists available warp portals to open")], + [T("<warp portal #|map name>"), T("opens a warp portal to a map")] + ], \&cmdWarp], + ['weight', [ + T("Gives a report about your inventory weight."), + ["", T("displays info about current weight")], + [T("<item weight>"), T("calculates how much more items of specified weight can be carried")] + ], \&cmdWeight], + ['where', T("Shows your current location."), \&cmdWhere], + ['who', T("Display the number of people on the current server."), \&cmdWho], + ['whoami', T("Display your character and account ID."), \&cmdWhoAmI], + ['mail', [ + T("Mailbox use (not Rodex)"), + ["open", T("open Mailbox")], # mi + ["list", T("list your Mailbox")], + ["refresh", T("refresh Mailbox")], # new + [T("read <mail #>"), T("read the selected mail")], # mo + [T("get <mail #>"), T("take attachments from mail")], # ma get + [T("setzeny <amount|none>"), T("attach zeny to mail or return it back")], # ma add zeny, mw 2 + [T("add <item #|none> <amount>"), T("attach item to mail or return it back")], # ma add item, mw 1 + [T("send <receiver> <title> <body>"), T("send mail to <receiver>")], # ms + [T("delete <mail #>"), T("delete selected mail")], #md + ["write", T("start writing a mail")], #mw 0 + ["return <mail #>", T("returns the mail to the sender")] #mr + ], \&cmdMail], + ['au', T("Display possible commands for auction."), \&cmdAuction], # see commands + ['aua', [ + T("Adds an item to the auction."), + [T("<inventory item> <amount>"), T("adds an item to the auction")] + ], \&cmdAuction], # add item + ['aur', T("Removes item from auction."), \&cmdAuction], # remove item + ['auc', [ + T("Creates an auction."), + [T("<current price> <instant buy price> <hours>"), T("creates an auction")] + ], \&cmdAuction], # create auction + ['aue', [ + T("Ends an auction."), + [T("<index>"), T("ends an auction")] + ], \&cmdAuction], # auction end + ['aus', [ + T("Search for an auction according to the criteria."), + [T("<type> <price> <text>"), T("Item's search criteria. Type: 1 Armor, 2 Weapon, 3 Card, 4 Misc, 5 By Text, 6 By Price, 7 Sell, 8 Buy")] + ], \&cmdAuction], # search auction + ['aub', [ + T("Bids an auction."), + [T("<id> <price>"), T("bids an auction")] + ], \&cmdAuction], # make bid + ['aui', [ + T("Displays your auction info."), + ["selling", T("display selling info")], + ["buying", T("display buying info")] + ], \&cmdAuction], # info on buy/sell + ['aud', [ + T("Deletes an auction."), + [T("<index>"), T("deletes an auction")] + ], \&cmdAuction], # delete auction + + ['quest', [ + T("Quest management."), + ["", T("displays possible commands for quest")], + ["set <questID> on", T("enable quest")], + ["set <questID> off", T("disable quest")], + ["list", T("displays a list of your quests")], + ["info <questID>", T("displays quest description")] + ], \&cmdQuest], + ['showeq', [ + T("Equipment showing."), + [T("p <index|name|partialname>"), T("request equipment information for player")], + ["me on", T("enables equipment showing")], + ["me off", T("disables equipment showing")] + ], \&cmdShowEquip], + ['cook', [ + T("Attempt to create a food item."), + [T("<cook list #>"), T("attempt to create a food item")] + ], \&cmdCooking], + ['refine', [ + T("Refine an item (using the whitesmith skill)"), + [T("(<item name>|<item index>)"), T("Refine an item (using the whitesmith skill)")] + ], \&cmdWeaponRefine], + + ['north', T("Move 5 steps north."), \&cmdManualMove], + ['south', T("Move 5 steps south."), \&cmdManualMove], + ['east', T("Move 5 steps east."), \&cmdManualMove], + ['west', T("Move 5 steps west."), \&cmdManualMove], + ['northeast', T("Move 5 steps northeast."), \&cmdManualMove], + ['northwest', T("Move 5 steps northwest."), \&cmdManualMove], + ['southeast', T("Move 5 steps southeast."), \&cmdManualMove], + ['southwest', T("Move 5 steps southwest."), \&cmdManualMove], + ['captcha', T("Answer captcha"), \&cmdAnswerCaptcha], + ['refineui', undef, \&cmdRefineUI], + ['clan', undef, \&cmdClan], + ['merge', undef, \&cmdMergeItem], + + # Skill Exchange Item + ['cm', undef, \&cmdExchangeItem], + ['analysis', undef, \&cmdExchangeItem], + + ['searchstore', [ + T("Universal catalog command"), + ["close", T("Closes search store catalog")], + ["next", T("Requests catalog next page")], + ["view <page #>", T("Shows catalog page # (0-indexed)")], + ["search [match|exact] ...", T("Searches for an item")], + ["select <page #> <store #>", T("Selects a store")], + ["buy [view|end|<item #> [<amount>]]", T("Buys from a store using Universal Catalog Gold")], + ], \&cmdSearchStore], + ['pause', [ + T("Delay the next console commands."), + ["", T("delay the next console commands for 1 second")], + [T("<seconds>"), T("delay the next console commands by a specified number of seconds")] + ], undef], + ['eden', "Use Eden Group Mark", \&cmdEden], + ); + + # Built-in aliases + register( + ['cl', $commands{'chat'}{desc}, $commands{'chat'}{callback}], + ); + + $needsInit = 0; +} + +sub initCompletions { + %completions = (); +} + +### CATEGORY: Functions + +## +# Commands::run(input) +# input: a command. +# +# Processes $input. See also <a href="https://openkore.com/wiki/Category:Console_Command">the user documentation</a> +# for a list of commands. +# +# Example: +# # Same effect as typing 's' in the console. Displays character status +# Commands::run("s"); +sub run { + my $input = shift; + initHandlers() if $needsInit; + + # Resolve command aliases + my ($switch, $args) = split(/ +/, $input, 2); + if (my $alias = $config{"alias_$switch"}) { + $input = $alias; + $input .= " $args" if defined $args; + } + + # Remove trailing spaces from input + $input =~ s/^\s+|\s+$//g; + + my @commands = split(';;', $input); + # Loop through all of the commands... + foreach my $command (@commands) { + my ($switch, $args) = split(/ +/, $command, 2); + my $handler; + $handler = $commands{$switch}{callback} if (exists $commands{$switch} && $commands{$switch}); + + if (($switch eq 'pause') && (!$cmdQueue) && AI::state == AI::AUTO && ($net->getState() == Network::IN_GAME)) { + $cmdQueue = 1; + $cmdQueueStartTime = time; + if ($args > 0) { + $cmdQueueTime = $args; + } else { + $cmdQueueTime = 1; + } + debug "Command queueing started\n", "ai"; + } elsif (($switch eq 'pause') && ($cmdQueue > 0)) { + push(@cmdQueueList, $command); + } elsif (($switch eq 'pause') && (AI::state != AI::AUTO || ($net->getState() != Network::IN_GAME))) { + error T("Cannot use pause command now.\n"); + } elsif (($handler) && ($cmdQueue > 0) && (!defined binFind(\@cmdQueuePriority,$switch) && ($command ne 'cart') && ($command ne 'storage'))) { + push(@cmdQueueList, $command); + } elsif ($handler) { + my %params; + $params{switch} = $switch; + $params{args} = $args; + Plugins::callHook('Commands::run/pre', \%params); + $handler->($switch, $args); + Plugins::callHook('Commands::run/post', \%params); + + } else { + my %params = ( switch => $switch, input => $command ); + Plugins::callHook('Command_post', \%params); + if (!$params{return}) { + error TF("Unknown command '%s'. Please read the documentation for a list of commands.\n" + ."http://openkore.com/wiki/Category:Console_Command\n", $switch); + } else { + return $params{return} + } + } + } + return 1; +} + + +## +# Commands::register([name, description, callback]...) +# Returns: an ID for use with Commands::unregister() +# +# Register new commands. +# +# Example: +# my $ID = Commands::register( +# ["my_command", "My custom command's description", \&my_callback], +# ["another_command", "Yet another command description", \&another_callback] +# ); +# Commands::unregister($ID); +sub register { + my @result; + + foreach my $cmd (@_) { + my $name = $cmd->[0]; + my $desc = (ref($cmd->[1]) eq 'ARRAY') ? $cmd->[1] : [$cmd->[1]]; + + my %item = ( + desc => $desc, + callback => $cmd->[2] + ); + + warning TF("Command '%s' will be overriden\n", $name) if exists $commands{$name} && $commands{$name}; + + $commands{$name} = \%item; + push @result, $name; + } + return \@result; +} + + +## +# Commands::unregister(ID) +# ID: an ID returned by Commands::register() +# +# Unregisters a registered command. +sub unregister { + my $ID = shift; + + foreach my $name (@{$ID}) { + delete $commands{$name}; + } +} + + +sub complete { + my $input = shift; + my ($switch, $args) = split(/ +/, $input, 2); + + return if ($input eq ''); + initCompletions() if (!%completions); + + # Resolve command aliases + if (my $alias = $config{"alias_$switch"}) { + $input = $alias; + $input .= " $args" if defined $args; + ($switch, $args) = split(/ +/, $input, 2); + } + + my $completor; + if ($completions{$switch}) { + $completor = $completions{$switch}; + } else { + $completor = \&defaultCompletor; + } + + my ($last_arg_pos, $matches) = $completor->($switch, $input, 'c'); + if (@{$matches} == 1) { + my $arg = $matches->[0]; + $arg = "\"$arg\"" if ($arg =~ / /); + my $new = substr($input, 0, $last_arg_pos) . $arg; + if (length($new) > length($input)) { + return "$new "; + } elsif (length($new) == length($input)) { + return "$input "; + } + + } elsif (@{$matches} > 1) { + $interface->writeOutput("message", "\n" . join("\t", @{$matches}) . "\n", "info"); + + ## Find largest common prefix + + # Find item with smallest length + my $smallest; + foreach (@{$matches}) { + if (!defined $smallest || length($_) < $smallest) { + $smallest = length($_); + } + } + + my $commonStr; + for (my $len = $smallest; $len >= 0; $len--) { + my $first = lc(substr($matches->[0], 0, $len)); + my $common = 1; + foreach (@{$matches}) { + if ($first ne lc(substr($_, 0, $len))) { + $common = 0; + last; + } + } + if ($common) { + $commonStr = $first; + last; + } + } + + my $new = substr($input, 0, $last_arg_pos) . $commonStr; + return $new if (length($new) > length($input)); + } + return $input; +} + + +################################## + + +sub completePlayerName { + my $arg = quotemeta shift; + my @matches; + foreach (@playersID) { + next if (!$_); + if ($players{$_}{name} =~ /^$arg/i) { + push @matches, $players{$_}{name}; + } + } + return @matches; +} + +sub defaultCompletor { + my $switch = shift; + my $last_arg_pos; + my @args = parseArgs(shift, undef, undef, \$last_arg_pos); + my @matches; + + my $arg = $args[$#args]; + @matches = completePlayerName($arg); + return ($last_arg_pos, \@matches); +} + + +################################## +### CATEGORY: Commands + + +sub cmdAI { + my (undef, $args) = @_; + $args =~ s/ .*//; + + # Clear AI + @cmdQueueList = (); + $cmdQueue = 0; + if ($args eq 'clear') { + AI::clear; + $taskManager->stopAll() if defined $taskManager; + delete $ai_v{temp}; + if ($char) { + undef $char->{dead}; + } + message T("AI sequences cleared\n"), "success"; + + } elsif ($args eq 'print') { + # Display detailed info about current AI sequence + my $msg = center(T(" AI Sequence "), 50, '-') ."\n"; + my $index = 0; + foreach (@ai_seq) { + $msg .= ("$index: $_ " . dumpHash(\%{$ai_seq_args[$index]}) . "\n\n"); + $index++; + } + $msg .= ('-'x50) . "\n"; + message $msg, "list"; + + } elsif ($args eq 'ai_v') { + message dumpHash(\%ai_v) . "\n", "list"; + + } elsif ($args eq 'on' || $args eq 'auto') { + # Set AI to auto mode + if (AI::state == AI::AUTO) { + message T("AI is already set to auto mode\n"), "success"; + } else { + AI::state(AI::AUTO); + message T("AI set to auto mode\n"), "success"; + } + } elsif ($args eq 'manual') { + # Set AI to manual mode + if (AI::state == AI::MANUAL) { + message T("AI is already set to manual mode\n"), "success"; + } else { + AI::state(AI::MANUAL); + message T("AI set to manual mode\n"), "success"; + } + } elsif ($args eq 'off') { + # Turn AI off + if (AI::state == AI::OFF) { + message T("AI is already off\n"), "success"; + } else { + AI::state(AI::OFF); + message T("AI turned off\n"), "success"; + } + + } elsif ($args eq '') { + # Toggle AI + if (AI::state == AI::AUTO) { + AI::state(AI::OFF); + message T("AI turned off\n"), "success"; + } elsif (AI::state == AI::OFF) { + AI::state(AI::MANUAL); + message T("AI set to manual mode\n"), "success"; + } elsif (AI::state == AI::MANUAL) { + AI::state(AI::AUTO); + message T("AI set to auto mode\n"), "success"; + } + + } else { + error T("Syntax Error in function 'ai' (AI Commands)\n" . + "Usage: ai [ clear | print | ai_v | auto | manual | off ]\n"); + } +} + +sub cmdAIv { + # Display current AI sequences + my $on; + if (AI::state == AI::OFF) { + message TF("ai_seq (off) = %s\n", "@ai_seq"), "list"; + } elsif (AI::state == AI::MANUAL) { + message TF("ai_seq (manual) = %s\n", "@ai_seq"), "list"; + } elsif (AI::state == AI::AUTO) { + message TF("ai_seq (auto) = %s\n", "@ai_seq"), "list"; + } + message T("solution\n"), "list" if (AI::args->{'solution'}); + message TF("Active tasks: %s\n", (defined $taskManager) ? $taskManager->activeTasksString() : ''), "info"; + message TF("Inactive tasks: %s\n", (defined $taskManager) ? $taskManager->inactiveTasksString() : ''), "info"; +} + +sub cmdArrowCraft { + if (!$net || $net->getState() != Network::IN_GAME) { + error TF("You must be logged in the game to use this command '%s'\n", shift); + return; + } + my (undef, $args) = @_; + my ($command, $arg1) = parseArgs( $args ); + + if ($command eq "") { + if (@arrowCraftID) { + my $msg = center(" ". T("Item To Craft") ." ", 50, '-') ."\n"; + for (my $i = 0; $i < @arrowCraftID; $i++) { + next if ($arrowCraftID[$i] eq ""); + $msg .= swrite( + "@<<< @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<", + [$i, $char->inventory->get($arrowCraftID[$i])->{name}]); + } + $msg .= ('-'x50) . "\n"; + message $msg, "list"; + } else { + error T("Error in function 'arrowcraft' (Create Arrows)\n" . + "Type 'arrowcraft' to get list.\n"); + } + } elsif ($command eq "use") { + if (defined binFind(\@skillsID, 'AC_MAKINGARROW')) { + main::ai_skillUse('AC_MAKINGARROW', 1, 0, 0, $accountID); + } else { + error T("Error in function 'arrowcraft use' (Create Arrows)\n" . + "You don't have Arrow Making Skill.\n"); + } + } elsif ($command eq "forceuse") { + my $item = $char->inventory->get($arg1); + if ($item) { + $messageSender->sendArrowCraft($item->{nameID}); + $char->{selected_craft} = 1; + } else { + error TF("Error in function 'arrowcraft forceuse #' (Create Arrows)\n" . + "You don't have item %s in your inventory.\n", $arg1); + } + } else { + if ($arrowCraftID[$command] ne "") { + $messageSender->sendArrowCraft($char->inventory->get($arrowCraftID[$command])->{nameID}); + $char->{selected_craft} = 1; + } else { + error T("Error in function 'arrowcraft' (Create Arrows)\n" . + "Usage: arrowcraft [<identify #>]\n" . + "Type 'arrowcraft use' to get list.\n"); + } + } +} + +sub cmdPoison { + if (!$net || $net->getState() != Network::IN_GAME) { + error TF("You must be logged in the game to use this command '%s'\n", shift); + return; + } + my (undef, $args) = @_; + my ($command) = parseArgs( $args ); + + if ($command eq "") { + if (@arrowCraftID) { + my $msg = center(" ". T("Poison List") ." ", 50, '-') ."\n"; + for (my $i = 0; $i < @arrowCraftID; $i++) { + next if ($arrowCraftID[$i] eq ""); + $msg .= swrite( + "@<<< @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<", + [$i, $char->inventory->get($arrowCraftID[$i])->{name}]); + } + $msg .= ('-'x50) . "\n"; + message $msg, "list"; + } else { + error T("Error in function 'poison' (Apply Poison)\n" . + "Type 'poison' to get list.\n"); + } + } elsif ($command eq "use") { + if (defined binFind(\@skillsID, 'GC_POISONINGWEAPON')) { + main::ai_skillUse('GC_POISONINGWEAPON', 5, 0, 0, $accountID); + } else { + error T("Error in function 'poison use' (Use Poison)\n" . + "You don't have Poisonous Weapon Skill.\n"); + } + } else { + if ($arrowCraftID[$command] ne "") { + $messageSender->sendArrowCraft($char->inventory->get($arrowCraftID[$command])->{nameID}); + $char->{selected_craft} = 1; + } else { + error T("Error in function 'poison' (Apply Poison)\n" . + "Usage: poison [<poison #>]\n" . + "Type 'poison' to get list.\n"); + } + } +} + +sub cmdAttack { + my (undef, $arg1) = @_; + if ($arg1 =~ /^\d+$/) { + if ($monstersID[$arg1] eq "") { + error TF("Error in function 'a' (Attack Monster)\n" . + "Monster %s does not exist.\n", $arg1); + } else { + main::attack($monstersID[$arg1]); + } + } elsif ($arg1 eq "no") { + configModify("attackAuto", 1); + + } elsif ($arg1 eq "yes") { + configModify("attackAuto", 2); + + } else { + error T("Syntax Error in function 'a' (Attack Monster)\n" . + "Usage: attack <monster # | no | yes >\n"); + } +} + +sub cmdAttackStop { + my $index = AI::findAction("attack"); + if ($index ne "") { + my $args = AI::args($index); + my $monster = Actor::get($args->{ID}); + if ($monster) { + $monster->{ignore} = 1; + $char->sendAttackStop; + message TF("Stopped attacking %s (%s)\n", + $monster->{name}, $monster->{binID}), "success"; + AI::clear("attack"); + } + } +} + +sub cmdAuthorize { + my (undef, $args) = @_; + my ($arg1, $arg2) = $args =~ /^([\s\S]*) ([\s\S]*?)$/; + if ($arg1 eq "" || ($arg2 ne "1" && $arg2 ne "0")) { + error T("Syntax Error in function 'auth' (Overall Authorize)\n" . + "Usage: auth <username> <flag>\n"); + } else { + auth($arg1, $arg2); + } +} + +sub cmdAttendance { + my (undef, $args) = @_; + my ($command) = parseArgs( $args ); + + if (!$net || $net->getState() != Network::IN_GAME) { + error TF("You must be logged in the game to use this command '%s'\n", shift); + return; + } + + if ( $command eq "open" ) { + $messageSender->sendOpenUIRequest(5); + } elsif ( $command eq "request" ) { + $messageSender->sendAttendanceRewardRequest(); + } else { + error T("Syntax Error in function 'attendance'\n" . + "attendance <open|request>\n"); + } +} + +sub cmdAutoBuy { + message T("Initiating auto-buy.\n"); + AI::queue("buyAuto"); + Plugins::callHook('AI_buy_auto_queued'); +} + +sub cmdAutoSell { + my (undef, $arg) = @_; + if ($arg eq 'simulate' || $arg eq 'test' || $arg eq 'debug') { + # Simulate list of items to sell + my @sellItems; + my $msg = center(T(" Items to sell (simulation) "), 50, '-') ."\n". + T("Amount Item Name\n"); + for my $item (@{$char->inventory}) { + next if ($item->{unsellable}); + my $control = items_control($item->{name},$item->{nameID}); + if ($control->{'sell'} && $item->{'amount'} > $control->{keep}) { + my %obj; + $obj{index} = $item->{ID}; + $obj{amount} = $item->{amount} - $control->{keep}; + my $item_name = $item->{name}; + $item_name .= T(" (if unequipped)") if ($item->{equipped}); + $msg .= swrite( + "@>>> x @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<", + [$item->{amount}, $item_name]); + } + } + $msg .= ('-'x50) . "\n"; + message ($msg, "list"); + } elsif (!$arg) { + message T("Initiating auto-sell.\n"); + AI::queue("sellAuto"); + Plugins::callHook('AI_sell_auto_queued'); + } +} + +sub cmdAutoStorage { + message T("Initiating auto-storage.\n"); + if (ai_canOpenStorage()) { + AI::queue("storageAuto"); + Plugins::callHook('AI_storage_auto_queued'); + } else { + error T("Error in function 'autostorage' (Automatic storage of items)\n" . + "You cannot use the Storage Service. Very low level of basic skills or not enough zeny.\n"); + } +} + +sub cmdBangBang { + if (!$net || $net->getState() != Network::IN_GAME) { + error TF("You must be logged in the game to use this command '%s'\n", shift); + return; + } + my $bodydir = $char->{look}{body} - 1; + $bodydir = 7 if ($bodydir == -1); + $messageSender->sendLook($bodydir, $char->{look}{head}); +} + +sub cmdBingBing { + if (!$net || $net->getState() != Network::IN_GAME) { + error TF("You must be logged in the game to use this command '%s'\n", shift); + return; + } + my $bodydir = ($char->{look}{body} + 1) % 8; + $messageSender->sendLook($bodydir, $char->{look}{head}); +} + +sub cmdBank { + my (undef, $args) = @_; + my ($command, $zeny) = parseArgs( $args ); + + if (!$net || $net->getState() != Network::IN_GAME) { + error TF("You must be logged in the game to use this command '%s'\n", shift); + return; + } + + if ( $command eq "open" ) { + $messageSender->sendBankingCheck($accountID); + } elsif ( ( $command eq "deposit" || $command eq "withdraw" ) && !$bankingopened ) { + error T("Bank: You have to open bank before try to use the commands.\n"); + } elsif ( $command eq "deposit" ) { + if( $zeny =~ /\d+/ ) { + if( $zeny <= $char->{zeny} ) { + $messageSender->sendBankingDeposit($accountID, $zeny); + } else { + error T("Bank: You don't have that amount of zeny to DEPOSIT in Bank.\n"); + } + } else { + error T("Syntax Error in function 'bank' (Banking)\n" . + "bank deposit <amount>\n"); + } + } elsif ( $command eq "withdraw" ) { + if( $zeny =~ /\d+/ ) { + $messageSender->sendBankingWithdraw($accountID, $zeny); + } else { + error T("Syntax Error in function 'bank' (Banking)\n" . + "bank withdraw <amount>\n"); + } + } else { + error T("Syntax Error in function 'bank' (Banking)\n" . + "bank <open|deposit|withdraw>\n"); + } +} + +sub cmdBuy { + if (!$net || $net->getState() != Network::IN_GAME) { + error TF("You must be logged in the game to use this command '%s'\n", shift); + return; + } + + my (undef, $args) = @_; + my @bulkitemlist; + + foreach (split /\,/, $args) { + my($index,$amount) = $_ =~ /^\s*(\d+)\s*(\d*)\s*$/; + + if ($index eq "") { + error T("Syntax Error in function 'buy' (Buy Store Item)\n" . + "Usage: buy <item #> [<amount>][, <item #> [<amount>]]...\n"); + return; + + } elsif (!$storeList->get($index)) { + error TF("Error in function 'buy' (Buy Store Item)\n" . + "Store Item %s does not exist.\n", $index); + return; + + } elsif ($amount eq "" || $amount <= 0) { + $amount = 1; + } + + my $itemID = $storeList->get($index)->{nameID}; + push (@bulkitemlist,{itemID => $itemID, amount => $amount}); + } + + completeNpcBuy(\@bulkitemlist); +} + +sub cmdCard { + if (!$net || $net->getState() != Network::IN_GAME) { + error TF("You must be logged in the game to use this command '%s'\n", shift); + return; + } + my (undef, $input) = @_; + my ($arg1) = $input =~ /^(\w+)/; + my ($arg2) = $input =~ /^\w+ (\d+)/; + my ($arg3) = $input =~ /^\w+ \d+ (\d+)/; + + if ($arg1 eq "mergecancel") { + if (!defined $messageSender) { + error T("Error in function 'bingbing' (Change look direction)\n" . + "Can't use command while not connected to server.\n"); + } elsif ($cardMergeIndex ne "") { + undef $cardMergeIndex; + $messageSender->sendCardMerge(-1, -1); + message T("Cancelling card merge.\n"); + } else { + error T("Error in function 'card mergecancel' (Cancel a card merge request)\n" . + "You are not currently in a card merge session.\n"); + } + } elsif ($arg1 eq "mergelist") { + # FIXME: if your items change order or are used, this list will be wrong + if (@cardMergeItemsID) { + my $msg = center(T(" Card Merge Candidates "), 50, '-') ."\n"; + foreach my $card (@cardMergeItemsID) { + next if $card eq "" || !$char->inventory->get($card); + $msg .= swrite( + "@<<< @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<", + [$card, $char->inventory->get($card)]); + } + $msg .= ('-'x50) . "\n"; + message $msg, "list"; + } else { + error T("Error in function 'card mergelist' (List availible card merge items)\n" . + "You are not currently in a card merge session.\n"); + } + } elsif ($arg1 eq "merge") { + if ($arg2 =~ /^\d+$/) { + my $found = binFind(\@cardMergeItemsID, $arg2); + if (defined $found) { + $messageSender->sendCardMerge($char->inventory->get($cardMergeIndex)->{ID}, + $char->inventory->get($arg2)->{ID}); + } else { + if ($cardMergeIndex ne "") { + error TF("Error in function 'card merge' (Finalize card merging onto item)\n" . + "There is no item %s in the card mergelist.\n", $arg2); + } else { + error T("Error in function 'card merge' (Finalize card merging onto item)\n" . + "You are not currently in a card merge session.\n"); + } + } + } else { + error T("Syntax Error in function 'card merge' (Finalize card merging onto item)\n" . + "Usage: card merge <item number>\n" . + "<item number> - Merge item number. Type 'card mergelist' to get number.\n"); + } + } elsif ($arg1 eq "use") { + if ($arg2 =~ /^\d+$/) { + if ($char->inventory->get($arg2)) { + $cardMergeIndex = $arg2; + $messageSender->sendCardMergeRequest($char->inventory->get($cardMergeIndex)->{ID}); + message TF("Sending merge list request for %s...\n", + $char->inventory->get($cardMergeIndex)->{name}); + } else { + error TF("Error in function 'card use' (Request list of items for merging with card)\n" . + "Card %s does not exist.\n", $arg2); + } + } else { + error T("Syntax Error in function 'card use' (Request list of items for merging with card)\n" . + "Usage: card use <item number>\n" . + "<item number> - Card inventory number. Type 'i' to get number.\n"); + } + } elsif ($arg1 eq "list") { + my $msg = center(T(" Card List "), 50, '-') ."\n"; + for my $item (@{$char->inventory}) { + if ($item->mergeable) { + my $display = "$item->{name} x $item->{amount}"; + $msg .= swrite( + "@<<< @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<", + [$item->{binID}, $display]); + } + } + $msg .= ('-'x50) . "\n"; + message $msg, "list"; + } elsif ($arg1 eq "forceuse") { + if (!$char->inventory->get($arg2)) { + error TF("Error in function 'arrowcraft forceuse #' (Create Arrows)\n" . + "You don't have item %s in your inventory.\n", $arg2); + } elsif (!$char->inventory->get($arg3)) { + error TF("Error in function 'arrowcraft forceuse #' (Create Arrows)\n" . + "You don't have item %s in your inventory.\n"), $arg3; + } else { + $messageSender->sendCardMerge($char->inventory->get($arg2)->{ID}, + $char->inventory->get($arg3)->{ID}); + } + } else { + error T("Syntax Error in function 'card' (Card Compounding)\n" . + "Usage: card <use|mergelist|mergecancel|merge>\n"); + } +} + +sub cmdCart { + my (undef, $input) = @_; + my ($arg1, $arg2) = split(' ', $input, 2); + + if (!$char->cartActive) { + error T("Error in function 'cart' (Cart Management)\n" . + "You do not have a cart.\n"); + return; + + } elsif (!$char->cart->isReady()) { + error T("Cart inventory is not available.\n"); + return; + + } elsif ($arg1 eq "" || $arg1 eq "eq" || $arg1 eq "nu" || $arg1 eq "u") { + cmdCart_list($arg1); + + } elsif ($arg1 eq "desc") { + if($arg2 ne "") { + cmdCart_desc($arg2); + } else { + error T("Usage: cart desc <cart item #>\n"); + } + } elsif (($arg1 eq "add" || $arg1 eq "get" || $arg1 eq "release" || $arg1 eq "change") && (!$net || $net->getState() != Network::IN_GAME)) { + error TF("You must be logged in the game to use this command '%s'\n", 'cart ' .$arg1); + return; + + } elsif ($arg1 eq "add") { + if($arg2 ne "") { + cmdCart_add($arg2); + } else { + error T("Usage: cart add <inventory item> <amount>\n"); + } + } elsif ($arg1 eq "get") { + if($arg2 ne "") { + cmdCart_get($arg2); + } else { + error T("Usage: cart get <cart item> <amount>\n"); + } + } elsif ($arg1 eq "release") { + $messageSender->sendCompanionRelease(); + message T("Trying to released the cart...\n"); + + } elsif ($arg1 eq "change") { + if ($arg2 =~ m/^[1-5]$/) { + $messageSender->sendChangeCart($arg2); + } else { + error T("Usage: cart change <1-5>\n"); + } + + } else { + error TF("Error in function 'cart'\n" . + "Command '%s' is not a known command.\n", $arg1); + } +} + +sub cmdCart_desc { + my $arg = shift; + if (!($arg =~ /\d+/)) { + error TF("Syntax Error in function 'cart desc' (Show Cart Item Description)\n" . + "'%s' is not a valid cart item number.\n", $arg); + } else { + my $item = $char->cart->get($arg); + if (!$item) { + error TF("Error in function 'cart desc' (Show Cart Item Description)\n" . + "Cart Item %s does not exist.\n", $arg); + } else { + printItemDesc($item); + } + } +} + +sub cmdCart_list { + my $type = shift; + message "$type\n"; + + my @useable; + my @equipment; + my @non_useable; + my ($i, $display, $index); + + for my $item (@{$char->cart}) { + if ($item->usable) { + push @useable, $item->{binID}; + } elsif ($item->equippable) { + my %eqp; + $eqp{index} = $item->{ID}; + $eqp{binID} = $item->{binID}; + $eqp{nameID} = $item->{nameID}; + $eqp{name} = $item->{name}; + $eqp{amount} = $item->{amount}; + $eqp{identified} = " -- " . T("Not Identified") if !$item->{identified}; + $eqp{type} = $itemTypes_lut{$item->{type}}; + push @equipment, \%eqp; + } else { + push @non_useable, $item->{binID}; + } + } + + my $msg = center(T(" Cart "), 50, '-') ."\n". + T("# Name\n"); + + if (!$type || $type eq 'u') { + $msg .= T("-- Usable --\n"); + for (my $i = 0; $i < @useable; $i++) { + $index = $useable[$i]; + my $item = $char->cart->get($index); + my $nameID = "[".$item->{nameID}."]"; + $display = $item->{name}; + $display .= " x $item->{amount}"; + $msg .= swrite( + "@<<< @<<<<<<<< @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<", + [$index, $nameID, $display]); + } + } + + if (!$type || $type eq 'eq') { + $msg .= T("\n-- Equipment --\n"); + foreach my $item (@equipment) { + ## altered to allow for Arrows/Ammo which will are stackable equip. + $display = sprintf("%-3d [%-6d] %s (%s)", $item->{binID}, $item->{nameID}, $item->{name}, $item->{type}); + $display .= " x $item->{amount}" if $item->{amount} > 1; + $display .= $item->{identified}; + $msg .= sprintf("%-57s\n", $display); + } + } + + if (!$type || $type eq 'nu') { + $msg .= T("\n-- Non-Usable --\n"); + for (my $i = 0; $i < @non_useable; $i++) { + $index = $non_useable[$i]; + my $item = $char->cart->get($index); + my $nameID = "[".$item->{nameID}."]"; + $display = $item->{name}; + $display .= " x $item->{amount}"; + $msg .= swrite( + "@<<< @<<<<<<<< @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<", + [$index, $nameID, $display]); + } + } + + $msg .= TF("\nCapacity: %d/%d Weight: %d/%d\n", + $char->cart->items, $char->cart->items_max, $char->cart->{weight}, $char->cart->{weight_max}). + ('-'x50) . "\n"; + message $msg, "list"; +} + +sub cmdCart_add { + my $items = shift; + + my ( $name, $amount ); + if ( $items =~ /^[^"'].* .+$/ ) { + # Backwards compatibility: "cart add Empty Bottle 1" still works. + ( $name, $amount ) = $items =~ /^(.*?)(?: (\d+))?$/; + } else { + ( $name, $amount ) = parseArgs( $items ); + } + my @items = $char->inventory->getMultiple( $name ); + if ( !@items ) { + error TF( "Inventory item '%s' does not exist.\n", $name ); + return; + } + + transferItems( \@items, $amount, 'inventory' => 'cart' ); +} + +sub cmdCart_get { + my $items = shift; + + my ( $name, $amount ); + if ( $items =~ /^[^"'].* .+$/ ) { + # Backwards compatibility: "cart get Empty Bottle 1" still works. + ( $name, $amount ) = $items =~ /^(.*?)(?: (\d+))?$/; + } else { + ( $name, $amount ) = parseArgs( $items ); + } + my @items = $char->cart->getMultiple( $name ); + if ( !@items ) { + error TF( "Cart item '%s' does not exist.\n", $name ); + return; + } + + transferItems( \@items, $amount, 'cart' => 'inventory' ); +} + +sub cmdCash { + my (undef, $args) = @_; + my (@args) = parseArgs($args); + + if ($args[0] eq 'open') { + if (defined $cashShop{points}) { + error T("Cash shop already opened this session\n"); + return; + } + + $messageSender->sendCashShopOpen(); + return; + } + + if ($args[0] eq 'close') { + if (not defined $cashShop{points}) { + error T("Cash shop is not open\n"); + return; + } + + $messageSender->sendCashShopClose(); + return; + } + + if ($args[0] eq 'list') { + if (not defined $cashShop{list}) { + error T("The list of items of Cash shop is not available\n"); + return; + } + my %cashitem_tab = ( + 0 => T('New'), + 1 => T('Popular'), + 2 => T('Limited'), + 3 => T('Rental'), + 4 => T('Perpetuity'), + 5 => T('Buff'), + 6 => T('Recovery'), + 7 => T('Etc'), + ); + + my $msg; + for (my $tabcode = 0; $tabcode < @{$cashShop{list}}; $tabcode++) { + $msg .= center(T(' Tab: ') . $cashitem_tab{$tabcode} . ' ', 50, '-') ."\n". + T ("ID Item Name Price\n"); + foreach my $itemloop (@{$cashShop{list}[$tabcode]}) { + $msg .= swrite( + "@<<<<< @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< @>>>>>>C", + [$itemloop->{item_id}, itemNameSimple($itemloop->{item_id}), formatNumber($itemloop->{price})]); + } + } + $msg .= ('-'x50) . "\n"; + message $msg, "list"; + + return; + } + + if (not defined $cashShop{points}) { + error T("Cash shop is not open\n"); + error T("Please use 'cash open' first\n"); + return; + } + + if ($args[0] eq 'buy') { + if (scalar @args < 2 || !$args[1]) { + error T("Syntax Error in function 'cash buy' (Cash shop)\n" . + "Usage: cash buy <item> [<amount>] [<kafra shop points>]\n"); + return; + } + + my ($amount, $item, $kafra_points); + + if ($args[1] !~ /^\d+$/) { + $item = itemNameToID($args[1]); + if (!$item) { + error TF("Error in function 'cash buy': invalid item name '%s' or tables needs to be updated\n", $args[1]); + return; + } + } else { + $item = $args[1]; + } + + if (scalar @args < 3 || $args[2] !~ /^\d+$/) { + $amount = 1; + } else { + $amount = $args[2]; + } + + if (scalar @args >= 4) { + $kafra_points = $args[3]; + } else { + $kafra_points = 0; + } + + if ($kafra_points > $cashShop{points}->{kafra}) { + error TF("You don't have that many kafra shop points (Requested: %d, Available: %d)", $kafra_points, $cashShop{points}->{kafra}); + return; + } + + for (my $i = 0; $i < scalar @{$cashShop{list}}; ++$i) { + foreach my $item_in_tab (@{$cashShop{list}[$i]}) { + if ($item_in_tab->{item_id} == $item) { + if ($item_in_tab->{price} * $amount > $cashShop{points}->{cash} + $kafra_points) { + error TF("Not enough cash to buy item %s x %d (%sC), we have %sC\n", + itemNameSimple($item_in_tab->{item_id}), $amount, formatNumber($item_in_tab->{price} * $amount), + formatNumber($cashShop{points}->{cash} + $kafra_points) + ); + return; + } + + message TF("Buying %s from cash shop \n", itemNameSimple($item_in_tab->{item_id})); + $messageSender->sendCashBuy($kafra_points, [{nameID => $item_in_tab->{item_id}, amount => $amount, tab => $i}]); + return; + } + } + } + + error TF("Error in function 'cash buy': item %s not found or shop list is not ready yet.", itemNameSimple($item)); + return; + + } + + if ($args[0] eq 'points') { + message TF("Cash Points: %sC - Kafra Points: %sC\n", formatNumber($cashShop{points}->{cash}), formatNumber($cashShop{points}->{kafra})); + return; + } + + error T("Syntax Error in function 'cash' (Cash shop)\n" . + "Usage: cash <open | close | buy | points | list>\n"); +} + +sub cmdCharSelect { + my (undef,$arg1) = @_; + if (!$net || $net->getState() != Network::IN_GAME) { + error TF("You must be logged in the game to use this command '%s'\n", shift); + return; + } + if($arg1 =~ "1"){ + configModify("char",''); + } + Log::initLogFiles(); + $messageSender->sendRestart(1); +} + +# chat, party chat, guild chat, battlegrounds chat +sub cmdChat { + my ($command, $arg1) = @_; + + if (!$net || $net->getState() != Network::IN_GAME) { + error TF("You must be logged in the game to use this command '%s'\n", $command); + return; + } + + if ($arg1 eq "") { + error TF("Syntax Error in function '%1\$s' (Chat)\n" . + "Usage: %1\$s <message>\n", $command); + } else { + sendMessage($messageSender, $command, $arg1); + } +} + +sub cmdChatLogClear { + chatLog_clear(); + message T("Chat log cleared.\n"), "success"; +} + +sub cmdChatRoom { + if (!$net || $net->getState() != Network::IN_GAME) { + error TF("You must be logged in the game to use this command '%s'\n", shift); + return; + } + + my ($command, $args) = @_; + my ($arg1) = $args =~ /^(\w+)/; + + if($command eq 'cl') { + $arg1 = 'list'; + } + + if ($arg1 eq "bestow") { + my ($arg2) = $args =~ /^\w+ (\d+)/; + + if ($currentChatRoom eq "") { + error T("Error in function 'chat bestow' (Bestow Admin in Chat)\n" . + "You are not in a Chat Room.\n"); + } elsif ($arg2 eq "") { + error T("Syntax Error in function 'chat bestow' (Bestow Admin in Chat)\n" . + "Usage: chat bestow <user #>\n"); + } elsif ($currentChatRoomUsers[$arg2] eq "") { + error TF("Error in function 'chat bestow' (Bestow Admin in Chat)\n" . + "Chat Room User %s doesn't exist; type 'chat info' to see the list of users\n", $arg2); + } else { + $messageSender->sendChatRoomBestow($currentChatRoomUsers[$arg2]); + } + + } elsif ($arg1 eq "modify") { + my ($title) = $args =~ /^\w+ \"([\s\S]*?)\"/; + my ($users) = $args =~ /^\w+ \"[\s\S]*?\" (\d+)/; + my ($public) = $args =~ /^\w+ \"[\s\S]*?\" \d+ (\d+)/; + my ($password) = $args =~ /^\w+ \"[\s\S]*?\" \d+ \d+ ([\s\S]+)/; + + if ($title eq "") { + error T("Syntax Error in function 'chatmod' (Modify Chat Room)\n" . + "Usage: chat modify \"<title>\" [<limit #> <public flag> <password>]\n"); + } else { + if ($users eq "") { + $users = 20; + } + if ($public eq "") { + $public = 1; + } + $messageSender->sendChatRoomChange($title, $users, $public, $password); + } + + } elsif ($arg1 eq "kick") { + my ($arg2) = $args =~ /^\w+ (\d+)/; + + if ($currentChatRoom eq "") { + error T("Error in function 'chat kick' (Kick from Chat)\n" . + "You are not in a Chat Room.\n"); + } elsif ($arg2 eq "") { + error T("Syntax Error in function 'chat kick' (Kick from Chat)\n" . + "Usage: chat kick <user #>\n"); + } elsif ($currentChatRoomUsers[$arg2] eq "") { + error TF("Error in function 'chat kick' (Kick from Chat)\n" . + "Chat Room User %s doesn't exist\n", $arg2); + } else { + $messageSender->sendChatRoomKick($currentChatRoomUsers[$arg2]); + } + + } elsif ($arg1 eq "join") { + my ($arg2) = $args =~ /^\w+ (\d+)/; + my ($arg3) = $args =~ /^\w+ \d+ (\w+)/; + + if ($arg2 eq "") { + error T("Syntax Error in function 'chat join' (Join Chat Room)\n" . + "Usage: chat join <chat room #> [<password>]\n"); + } elsif ($currentChatRoom ne "") { + error T("Error in function 'chat join' (Join Chat Room)\n" . + "You are already in a chat room.\n"); + } elsif ($chatRoomsID[$arg2] eq "") { + error TF("Error in function 'chat join' (Join Chat Room)\n" . + "Chat Room %s does not exist.\n", $arg2); + } else { + $messageSender->sendChatRoomJoin($chatRoomsID[$arg2], $arg3); + } + + } elsif ($arg1 eq "leave") { + if ($currentChatRoom eq "") { + error T("Error in function 'chat leave' (Leave Chat Room)\n" . + "You are not in a Chat Room.\n"); + } else { + $messageSender->sendChatRoomLeave(); + } + + } elsif ($arg1 eq "create") { + my ($title) = $args =~ /^\w+ \"([\s\S]*?)\"/; + my ($users) = $args =~ /^\w+ \"[\s\S]*?\" (\d+)/; + my ($public) = $args =~ /^\w+ \"[\s\S]*?\" \d+ (\d+)/; + my ($password) = $args =~ /^\w+ \"[\s\S]*?\" \d+ \d+ ([\s\S]+)/; + + if ($title eq "") { + error T("Syntax Error in function 'chat create' (Create Chat Room)\n" . + "Usage: chat create \"<title>\" [<limit #> <public flag> <password>]\n"); + } elsif ($currentChatRoom ne "") { + error T("Error in function 'chat create' (Create Chat Room)\n" . + "You are already in a chat room.\n"); + } else { + if ($users eq "") { + $users = 20; + } + if ($public eq "") { + $public = 1; + } + $title = ($config{chatTitleOversize}) ? $title : substr($title,0,36); + $messageSender->sendChatRoomCreate($title, $users, $public, $password); + %createdChatRoom = (); + $createdChatRoom{title} = $title; + $createdChatRoom{ownerID} = $accountID; + $createdChatRoom{limit} = $users; + $createdChatRoom{public} = $public; + $createdChatRoom{num_users} = 1; + $createdChatRoom{users}{$char->{name}} = 2; + } + + } elsif ($arg1 eq "list") { + my $msg = center(T(" Chat Room List "), 79, '-') ."\n". + T("# Title Owner Users Type\n"); + for (my $i = 0; $i < @chatRoomsID; $i++) { + next if (!defined $chatRoomsID[$i]); + my $room = $chatRooms{$chatRoomsID[$i]}; + my $owner_string = Actor::get($room->{ownerID})->name; + my $public_string = ($room->{public}) ? "Public" : "Private"; + my $limit_string = $room->{num_users} . "/" . $room->{limit}; + $msg .= swrite( + "@<< @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< @<<<<<<<<<<<<<<<<<<< @<<<<<< @<<<<<<", + [$i, $room->{title}, $owner_string, $limit_string, $public_string]); + } + $msg .= ('-'x79) . "\n"; + message $msg, "list"; + } elsif ($arg1 eq "info") { + if ($currentChatRoom eq "") { + error T("There is no chat room info - you are not in a chat room\n"); + } else { + my $msg = center(T(" Chat Room Info "), 56, '-') ."\n". + T("Title Users Pub/Priv\n"); + my $public_string = ($chatRooms{$currentChatRoom}{'public'}) ? "Public" : "Private"; + my $limit_string = $chatRooms{$currentChatRoom}{'num_users'}."/".$chatRooms{$currentChatRoom}{'limit'}; + $msg .= swrite( + "@<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< @<<<<<< @<<<<<<<<<", + [$chatRooms{$currentChatRoom}{'title'}, $limit_string, $public_string]); + # Translation Comment: Users in chat room + $msg .= T("-- Users --\n"); + for (my $i = 0; $i < @currentChatRoomUsers; $i++) { + next if ($currentChatRoomUsers[$i] eq ""); + my $user_string = $currentChatRoomUsers[$i]; + my $admin_string = ($chatRooms{$currentChatRoom}{'users'}{$currentChatRoomUsers[$i]} > 1) ? "(Admin)" : ""; + $msg .= swrite( + "@<< @<<<<<<<<<<<<<<<<<<<<<<<<< @<<<<<<<<<", + [$i, $user_string, $admin_string]); + } + $msg .= ('-'x56) . "\n"; + message $msg, "list"; + } + } else { + error T("Syntax Error in function 'chat' (Chat room management)\n" . + "Usage: chat <create|modify|join|kick|leave|info|list|bestow>\n"); + } + +} + +sub cmdChist { + # Display chat history + my (undef, $args) = @_; + $args = 5 if ($args eq ""); + if (!($args =~ /^\d+$/)) { + error T("Syntax Error in function 'chist' (Show Chat History)\n" . + "Usage: chist [<number of entries #>]\n"); + } elsif (open(CHAT, "<:utf8", $Settings::chat_log_file)) { + my @chat = <CHAT>; + close(CHAT); + my $msg = center(T(" Chat History "), 79, '-') ."\n"; + my $i = @chat - $args; + $i = 0 if ($i < 0); + for (; $i < @chat; $i++) { + $msg .= $chat[$i]; + } + $msg .= ('-'x79) . "\n"; + message $msg, "list"; + } else { + error TF("Unable to open %s\n", $Settings::chat_log_file); + } +} + +sub cmdCloseShop { + if (!$net || $net->getState() != Network::IN_GAME) { + error TF("You must be logged in the game to use this command '%s'\n", shift); + return; + } + main::closeShop(); +} + +sub cmdCloseBuyShop { + if (!$net || $net->getState() != Network::IN_GAME) { + error TF("You must be logged in the game to use this command '%s'\n", shift); + return; + } + $messageSender->sendCloseBuyShop(); + message T("Buying shop closed.\n", "BuyShop"); +} + +sub cmdCloseBuyerShop { + if (!$net || $net->getState() != Network::IN_GAME) { + error TF("You must be logged in the game to use this command '%s'\n", shift); + return; + } + main::closeBuyerShop(); +} + +sub cmdConf { + my (undef, $args) = @_; + my ( $force, $arg1, $arg2 ) = $args =~ /^(-f\s+)?(\S+)\s*(.*)$/; + + # Basic Support for "label" in blocks. Thanks to "piroJOKE" + if ($arg1 =~ /\./) { + $arg1 =~ s/\.+/\./; # Filter Out Unnececary dot's + my ($label, $param) = split /\./, $arg1, 2; # Split the label form parameter + # This line is used for debug + # message TF("Params label '%s' param '%s' arg1 '%s' arg2 '%s'\n", $label, $param, $arg1, $arg2), "info"; + foreach (%config) { + if ($_ =~ /_\d+_label/){ # we only need those blocks witch have labels + if ($config{$_} eq $label) { + my ($real_key, undef) = split /_label/, $_, 2; + # "<label>.block" param support. Thanks to "vit" + if ($param ne "block") { + $real_key .= "_"; + $real_key .= $param; + } + $arg1 = $real_key; + last; + }; + }; + }; + }; + + if ($arg1 eq "") { + error T("Syntax Error in function 'conf' (Change a Configuration Key)\n"); + error T("Usage: conf [-f] <variable> [<value>|none]\n"); + error T(" -f force variable to be set, even if it does not already exist in config.txt\n"); + + } elsif ($arg1 =~ /\*/) { + my $pat = $arg1; + $pat =~ s/\*/.*/gso; + my @keys = grep {/$pat/i} sort keys %config; + error TF( "Config variables matching %s do not exist\n", $arg1 ) if !@keys; + message TF( "Config '%s' is %s\n", $_, defined $config{$_} ? $config{$_} : 'not set' ), "info" foreach @keys; + + } elsif (!exists $config{$arg1} && !$force) { + error TF("Config variable %s doesn't exist\n", $arg1); + + } elsif ($arg2 eq "") { + my $value = $config{$arg1}; + if ($arg1 =~ /password/i) { + message TF("Config '%s' is not displayed\n", $arg1), "info"; + } else { + if (defined $value) { + message TF("Config '%s' is %s\n", $arg1, $value), "info"; + } else { + message TF("Config '%s' is not set\n", $arg1), "info"; + } + } + + } else { + undef $arg2 if ($arg2 eq "none"); + Plugins::callHook('Commands::cmdConf', { + key => $arg1, + val => \$arg2 + }); + configModify($arg1, $arg2); + Log::initLogFiles(); + } +} + +sub cmdConnect { + $Settings::no_connect = 0; +} + +sub cmdDamage { + my (undef, $args) = @_; + + if ($args eq "") { + my $total = 0; + message T("Damage Taken Report:\n"), "list"; + message(sprintf("%-40s %-20s %-10s\n", 'Name', 'Skill', 'Damage'), "list"); + for my $monsterName (sort keys %damageTaken) { + my $monsterHref = $damageTaken{$monsterName}; + for my $skillName (sort keys %{$monsterHref}) { + message sprintf("%-40s %-20s %10d\n", $monsterName, $skillName, $monsterHref->{$skillName}), "list"; + $total += $monsterHref->{$skillName}; + } + } + message TF("Total Damage Taken: %s\n", $total), "list"; + message T("End of report.\n"), "list"; + + } elsif ($args eq "reset") { + undef %damageTaken; + message T("Damage Taken Report reset.\n"), "success"; + } else { + error T("Syntax error in function 'damage' (Damage Report)\n" . + "Usage: damage [reset]\n"); + } +} + +sub cmdDeal { + if (!$net || $net->getState() != Network::IN_GAME) { + error TF("You must be logged in the game to use this command '%s'\n", shift); + return; + } + + my (undef, $args) = @_; + my @arg = parseArgs( $args ); + + if ( $arg[0] && $arg[0] !~ /^(\d+|no|add)$/ ) { + my ( $partner ) = grep { $_->name eq $arg[0] } @$playersList; + if ( !$partner ) { + error TF( "Unknown player [%s]. Player not nearby?\n", $arg[0] ); + return; + } + $arg[0] = $partner->{binID}; + } + + if (%currentDeal && $arg[0] =~ /\d+/) { + error T("Error in function 'deal' (Deal a Player)\n" . + "You are already in a deal\n"); + } elsif (%incomingDeal && $arg[0] =~ /\d+/) { + error T("Error in function 'deal' (Deal a Player)\n" . + "You must first cancel the incoming deal\n"); + } elsif ($arg[0] =~ /\d+/ && !$playersID[$arg[0]]) { + error TF("Error in function 'deal' (Deal a Player)\n" . + "Player %s does not exist\n", $arg[0]); + } elsif ($arg[0] =~ /\d+/) { + my $ID = $playersID[$arg[0]]; + my $player = Actor::get($ID); + message TF("Attempting to deal %s\n", $player); + deal($player); + + } elsif ($arg[0] eq "no" && !%incomingDeal && !%outgoingDeal && !%currentDeal) { + error T("Error in function 'deal' (Deal a Player)\n" . + "There is no incoming/current deal to cancel\n"); + } elsif ($arg[0] eq "no" && (%incomingDeal || %outgoingDeal)) { + $messageSender->sendDealReply(4); + } elsif ($arg[0] eq "no" && %currentDeal) { + $messageSender->sendCurrentDealCancel(); + + } elsif ($arg[0] eq "" && !%incomingDeal && !%currentDeal) { + error T("Error in function 'deal' (Deal a Player)\n" . + "There is no deal to accept\n"); + } elsif ($arg[0] eq "" && $currentDeal{'you_finalize'} && !$currentDeal{'other_finalize'}) { + error TF("Error in function 'deal' (Deal a Player)\n" . + "Cannot make the trade - %s has not finalized\n", $currentDeal{'name'}); + } elsif ($arg[0] eq "" && $currentDeal{'final'}) { + error T("Error in function 'deal' (Deal a Player)\n" . + "You already accepted the final deal\n"); + } elsif ($arg[0] eq "" && %incomingDeal) { + $messageSender->sendDealReply(3); + } elsif ($arg[0] eq "" && $currentDeal{'you_finalize'} && $currentDeal{'other_finalize'}) { + $messageSender->sendDealTrade(); + $currentDeal{'final'} = 1; + message T("You accepted the final Deal\n"), "deal"; + } elsif ($arg[0] eq "" && %currentDeal) { + $messageSender->sendDealAddItem(pack('v', 0), $currentDeal{'you_zeny'}); + $messageSender->sendDealFinalize(); + + } elsif ($arg[0] eq "add" && !%currentDeal) { + error T("Error in function 'deal_add' (Add Item to Deal)\n" . + "No deal in progress\n"); + } elsif ($arg[0] eq "add" && $currentDeal{'you_finalize'}) { + error T("Error in function 'deal_add' (Add Item to Deal)\n" . + "Can't add any Items - You already finalized the deal\n"); + } elsif ($arg[0] eq "add" && $arg[1] =~ /\d+/ && !$char->inventory->get($arg[1])) { + error TF("Error in function 'deal_add' (Add Item to Deal)\n" . + "Inventory Item %s does not exist.\n", $arg[1]); + } elsif ($arg[0] eq "add" && $arg[2] && $arg[2] !~ /\d+/) { + error T("Error in function 'deal_add' (Add Item to Deal)\n" . + "Amount must either be a number, or not specified.\n"); + } elsif ($arg[0] eq "add" && $arg[1] =~ /^(\d+(?:-\d+)?,?)+$/) { + my $max_items = $config{dealMaxItems} || 10; + my @items = Actor::Item::getMultiple($arg[1]); + my $n = $currentDeal{you_items}; + if ($n >= $max_items) { + error T("You can't add any more items to the deal\n"), "deal"; + } + while (@items && $n < $max_items) { + my $item = shift @items; + next if $item->{equipped}; + dealAddItem( $item, min( $item->{amount}, $arg[2] || $item->{amount} ) ); + $n++; + } + } elsif ($arg[0] eq "add" && $arg[1] eq "z") { + if (!$arg[2] && !($arg[2] eq "0") || $arg[2] > $char->{'zeny'}) { + $arg[2] = $char->{'zeny'}; + } + $currentDeal{'you_zeny'} = $arg[2]; + message TF("You put forward %sz to Deal\n", formatNumber($arg[2])), "deal"; + + } elsif ($arg[0] eq "add" && $arg[1] !~ /^\d+$/) { + my $max_items = $config{dealMaxItems} || 10; + if ($currentDeal{you_items} > $max_items) { + error T("You can't add any more items to the deal\n"), "deal"; + } + my $items = [ grep { $_ && lc( $_->{name} ) eq lc( $arg[1] ) && !$_->{equipped} } @$char->inventory ]; + my $n = $currentDeal{you_items}; + my $a = $arg[2] || 1; + my $c = 0; + while ($n < $max_items && $c < $a && @$items) { + my $item = shift @$items; + my $amount = $arg[2] && $a - $c < $item->{amount} ? $a - $c : $item->{amount}; + dealAddItem($item, $amount); + $n++; + $c += $amount; + } + } else { + error T("Syntax Error in function 'deal' (Deal a player)\n" . + "Usage: deal [<Player # | no | add>] [<item #>] [<amount>]\n"); + } +} + +sub cmdDealList { + if (!%currentDeal) { + error T("There is no deal list - You are not in a deal\n"); + + } else { + my $msg = center(T(" Current Deal "), 66, '-') ."\n"; + my $other_string = $currentDeal{'name'}; + my $you_string = T("You"); + if ($currentDeal{'other_finalize'}) { + $other_string .= T(" - Finalized"); + } + if ($currentDeal{'you_finalize'}) { + $you_string .= T(" - Finalized"); + } + + $msg .= swrite( + "@<<<<<<<<<<<<<<<<<<<<<<<<<<<<< @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<", + [$you_string, $other_string]); + + my @currentDealYou; + my @currentDealOther; + foreach (keys %{$currentDeal{'you'}}) { + push @currentDealYou, $_; + } + foreach (keys %{$currentDeal{'other'}}) { + push @currentDealOther, $_; + } + + my ($lastindex, $display, $display2); + $lastindex = @currentDealOther; + $lastindex = @currentDealYou if (@currentDealYou > $lastindex); + for (my $i = 0; $i < $lastindex; $i++) { + if ($i < @currentDealYou) { + $display = ($items_lut{$currentDealYou[$i]} ne "") + ? $items_lut{$currentDealYou[$i]} + : T("Unknown ").$currentDealYou[$i]; + $display .= " x $currentDeal{'you'}{$currentDealYou[$i]}{'amount'}"; + } else { + $display = ""; + } + if ($i < @currentDealOther) { + $display2 = ($items_lut{$currentDealOther[$i]} ne "") + ? $items_lut{$currentDealOther[$i]} + : T("Unknown ").$currentDealOther[$i]; + $display2 .= " x $currentDeal{'other'}{$currentDealOther[$i]}{'amount'}"; + } else { + $display2 = ""; + } + + $msg .= swrite( + "@<<<<<<<<<<<<<<<<<<<<<<<<<<<<< @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<", + [$display, $display2]); + } + $you_string = ($currentDeal{'you_zeny'} ne "") ? $currentDeal{'you_zeny'} : 0; + $other_string = ($currentDeal{'other_zeny'} ne "") ? $currentDeal{'other_zeny'} : 0; + + $msg .= swrite( + T("zeny: \@<<<<<<<<<<<<<<<<<<<<<<< zeny: \@<<<<<<<<<<<<<<<<<<<<<<<"), + [formatNumber($you_string), formatNumber($other_string)]); + + $msg .= ('-'x66) . "\n"; + message $msg, "list"; + } +} + +sub cmdDebug { + my (undef, $args) = @_; + my ($arg1) = $args =~ /^([\w\d]+)/; + + if ($arg1 =~ /\d/) { + configModify("debug", $arg1); + } elsif ($arg1 eq "info") { + my $connected = $net && "server=".($net->serverAlive ? "yes" : "no"). + ",client=".($net->clientAlive ? "yes" : "no"); + my $time = $packetParser && sprintf("%.2f", time - $packetParser->{lastPacketTime}); + my $ai_timeout = sprintf("%.2f", time - $timeout{'ai'}{'time'}); + my $ai_time = sprintf("%.4f", time - $ai_v{'AI_last_finished'}); + + message center(T(" Debug information "), 56, '-') ."\n". + TF("ConState: %s\t\tConnected: %s\n" . + "AI enabled: %s\n" . + "\@ai_seq = %s\n" . + "Last packet: %.2f secs ago\n" . + "\$timeout{ai}: %.2f secs ago (value should be >%s)\n" . + "Last AI() call: %.2f secs ago\n" . + ('-'x56) . "\n", + $conState, $connected, AI::state, "@ai_seq", $time, $ai_timeout, + $timeout{'ai'}{'timeout'}, $ai_time), "list"; + } else { + error "Syntax Error in function 'debug' (Toggle debug on/off)\n"; + } +} + +sub cmdDoriDori { + if (!$net || $net->getState() != Network::IN_GAME) { + error TF("You must be logged in the game to use this command '%s'\n", shift); + return; + } + + my $headdir = ($char->{look}{head} == 2) ? 1 : 2; + + $messageSender->sendLook($char->{look}{body}, $headdir); + $messageSender->sendNoviceDoriDori(); +} + +sub cmdDrop { + if (!$net || $net->getState() != Network::IN_GAME) { + error TF("You must be logged in the game to use this command '%s'\n", shift); + return; + } + my (undef, $args) = @_; + my ($arg1) = $args =~ /^(\d+[\d,-]*)/; + my ($arg2) = $args =~ /^[\d,-]+ (\d+)$/; + + if ($arg1 eq "") { + error T("Syntax Error in function 'drop' (Drop Inventory Item)\n" . + "Usage: drop <inventory_item_list> [<amount>]\n"); + } else { + my @temp = split(/,/, $arg1); + @temp = grep(!/^$/, @temp); # Remove empty entries + + my @items = (); + foreach (@temp) { + if (/(\d+)-(\d+)/) { + for ($1..$2) { + push(@items, $_) if ($char->inventory->get($_)); + } + } else { + push @items, $_ if ($char->inventory->get($_)); + } + } + if (@items > 0) { + main::ai_drop(\@items, $arg2); + } else { + error T("No items were dropped.\n"); + } + } +} + +sub cmdDump { + dumpData((defined $incomingMessages) ? $incomingMessages->getBuffer() : ''); + quit(); +} + +sub cmdDumpNow { + dumpData((defined $incomingMessages) ? $incomingMessages->getBuffer() : ''); +} + +sub cmdEmotion { + # Show emotion + if (!$net || $net->getState() != Network::IN_GAME) { + error TF("You must be logged in the game to use this command '%s'\n", shift); + return; + } + my (undef, $args) = @_; + + my $num = getEmotionByCommand($args); + + if (!defined $num) { + error T("Syntax Error in function 'e' (Emotion)\n" . + "Usage: e <command>\n"); + } else { + $messageSender->sendEmotion($num); + } +} + +sub cmdEquip { + + # Equip an item + my (undef, $args) = @_; + my ($arg1,$arg2) = $args =~ /^(\S+)\s*(.*)/; + my $slot; + my $item; + + if ($arg1 eq "") { + cmdEquip_list(); + return; + } + + if ($arg1 eq "slots") { + # Translation Comment: List of equiped items on each slot + message T("Slots:\n") . join("\n", @Actor::Item::slots). "\n", "list"; + return; + } + + if (!$net || $net->getState() != Network::IN_GAME) { + error TF("You must be logged in the game to use this command '%s'\n", 'eq ' .$args); + return; + } + + if ($equipSlot_rlut{$arg1}) { + $slot = $arg1; + } else { + $arg1 .= " $arg2" if $arg2; + } + + $item = Actor::Item::get(defined $slot ? $arg2 : $arg1, undef, 1); + if (!$item) { + $args =~ s/^($slot)\s//g if ($slot); + error TF("No such non-equipped Inventory Item: %s\n", $args); + return; + } + + if (!$item->{type_equip} && $item->{type} != 10 && $item->{type} != 16 && $item->{type} != 17 && $item->{type} != 8) { + error TF("Inventory Item %s (%s) can't be equipped.\n", + $item->{name}, $item->{binID}); + return; + } + if ($slot) { + $item->equipInSlot($slot); + } else { + $item->equip(); + } +} + +sub cmdEquip_list { + if (!$char) { + error T("Character equipment not yet ready\n"); + return; + } + message TF("=====[Character Equip List]=====\n"), "info"; + for my $slot (@Actor::Item::slots) { + my $item = $char->{equipment}{$slot}; + my $name = $item ? $item->{name} : '-'; + ($item->{type} == 10 || $item->{type} == 16 || $item->{type} == 17 || $item->{type} == 19) ? + message sprintf("%-15s: %s x %s\n", $slot, $name, $item->{amount}), "list" : + message sprintf("%-15s: %s\n", $slot, $name), "list"; + } + message "================================\n", "info"; +} + +sub cmdEquipSwitch { + # Equip an item + my (undef, $args) = @_; + my ($arg1,$arg2) = $args =~ /^(\S+)\s*(.*)/; + my $slot; + my $item; + + if ($arg1 eq "") { + cmdEquipsw_list(); + return; + } + + if ($arg1 eq "slots") { + # Translation Comment: List of equiped items on each slot + message T("Slots:\n") . join("\n", @Actor::Item::slots). "\n", "list"; + return; + } + + if (!$net || $net->getState() != Network::IN_GAME) { + error TF("You must be logged in the game to use this command '%s'\n", 'eqsw ' .$args); + return; + } + + if ($equipSlot_rlut{$arg1}) { + $slot = $arg1; + } else { + $arg1 .= " $arg2" if $arg2; + } + + $item = Actor::Item::get(defined $slot ? $arg2 : $arg1, undef, 1); + if (!$item) { + $args =~ s/^($slot)\s//g if ($slot); + error TF("No such non-equipped Inventory Item: %s\n", $args); + return; + } + + if (!$item->{type_equip} && $item->{type} != 10 && $item->{type} != 16 && $item->{type} != 17 && $item->{type} != 8) { + error TF("Inventory Item %s (%s) can't be equipped.\n", + $item->{name}, $item->{binID}); + return; + } + + if ($slot) { + $item->equip_switch_slot($slot); + } else { + $item->equip_switch(); + } +} + +sub cmdEquipsw_list { + if (!$char) { + error T("Character equipment not yet ready\n"); + return; + } + message TF("=====[Equip Switch List]=====\n"), "info"; + for my $slot (@Actor::Item::slots) { + my $item = $char->{eqswitch}{$slot}; + my $name = $item ? $item->{name} : '-'; + ($item->{type} == 10 || $item->{type} == 16 || $item->{type} == 17 || $item->{type} == 19) + ? message sprintf("%-15s: %s x %s\n", $slot, $name, $item->{amount}), "list" + : message sprintf("%-15s: %s\n", $slot, $name), "list"; + } + message "=============================\n", "info"; +} + + +sub cmdEval { + if (!$Settings::lockdown) { + if ($_[1] eq "") { + error T("Syntax Error in function 'eval' (Evaluate a Perl expression)\n" . + "Usage: eval <expression>\n"); + } else { + package main; + no strict; + undef $@; + eval $_[1]; + if (defined $@ && $@ ne '') { + $@ .= "\n" if ($@ !~ /\n$/s); + Log::error($@); + } + } + } +} + +sub cmdExp { + my (undef, $args) = @_; + my $knownArg; + my $msg; + + # exp report + my ($arg1) = $args =~ /^(\w+)/; + + if ($arg1 eq "reset") { + $knownArg = 1; + ($bExpSwitch,$jExpSwitch,$totalBaseExp,$totalJobExp) = (2,2,0,0); + $startTime_EXP = time; + $startingzeny = $char->{zeny} if $char; + undef @monsters_Killed; + $dmgpsec = 0; + $totaldmg = 0; + $elasped = 0; + $totalelasped = 0; + undef %itemChange; + $char->{'deathCount'} = 0; + $bytesSent = 0; + $packetParser->{bytesProcessed} = 0 if $packetParser; + message T("Exp counter reset.\n"), "success"; + return; + } + + if (!$char) { + error T("Exp report not yet ready\n"); + return; + } + + if ($arg1 eq "output") { + open(F, ">>:utf8", "$Settings::logs_folder/exp.txt"); + } + + if (($arg1 eq "") || ($arg1 eq "report") || ($arg1 eq "output")) { + $knownArg = 1; + my ($endTime_EXP, $w_sec, $bExpPerHour, $jExpPerHour, $EstB_sec, $percentB, $percentJ, $zenyMade, $zenyPerHour, $EstJ_sec, $percentJhr, $percentBhr); + $endTime_EXP = time; + $w_sec = int($endTime_EXP - $startTime_EXP); + if ($w_sec > 0) { + $zenyMade = $char->{zeny} - $startingzeny; + $bExpPerHour = int($totalBaseExp / $w_sec * 3600); + $jExpPerHour = int($totalJobExp / $w_sec * 3600); + $zenyPerHour = int($zenyMade / $w_sec * 3600); + if ($char->{exp_max} && $bExpPerHour){ + $percentB = "(".sprintf("%.2f",$totalBaseExp * 100 / $char->{exp_max})."%)"; + $percentBhr = "(".sprintf("%.2f",$bExpPerHour * 100 / $char->{exp_max})."%)"; + $EstB_sec = int(($char->{exp_max} - $char->{exp})/($bExpPerHour/3600)); + } + if ($char->{exp_job_max} && $jExpPerHour){ + $percentJ = "(".sprintf("%.2f",$totalJobExp * 100 / $char->{exp_job_max})."%)"; + $percentJhr = "(".sprintf("%.2f",$jExpPerHour * 100 / $char->{exp_job_max})."%)"; + $EstJ_sec = int(($char->{'exp_job_max'} - $char->{exp_job})/($jExpPerHour/3600)); + } + } + $char->{deathCount} = 0 if (!defined $char->{deathCount}); + + $msg .= center(T(" Exp Report "), 50, '-') ."\n". + TF( "Botting time : %s\n" . + "BaseExp : %s %s\n" . + "JobExp : %s %s\n" . + "BaseExp/Hour : %s %s\n" . + "JobExp/Hour : %s %s\n" . + "zeny : %s\n" . + "zeny/Hour : %s\n" . + "Base Levelup Time Estimation : %s\n" . + "Job Levelup Time Estimation : %s\n" . + "Died : %s\n" . + "Bytes Sent : %s\n" . + "Bytes Rcvd : %s\n", + timeConvert($w_sec), formatNumber($totalBaseExp), $percentB, formatNumber($totalJobExp), $percentJ, + formatNumber($bExpPerHour), $percentBhr, formatNumber($jExpPerHour), $percentJhr, + formatNumber($zenyMade), formatNumber($zenyPerHour), timeConvert($EstB_sec), timeConvert($EstJ_sec), + $char->{'deathCount'}, formatNumber($bytesSent), $packetParser && formatNumber($packetParser->{bytesProcessed})); + + if ($arg1 eq "") { + $msg .= ('-'x50) . "\n"; + message $msg, "list"; + } + } + + if (($arg1 eq "monster") || ($arg1 eq "report") || ($arg1 eq "output")) { + my $total; + + $knownArg = 1; + + $msg .= center(T(" Monster Killed Count "), 40, '-') ."\n". + T("# ID Name Count\n"); + for (my $i = 0; $i < @monsters_Killed; $i++) { + next if ($monsters_Killed[$i] eq ""); + $msg .= swrite( + "@<< @<<<<< @<<<<<<<<<<<<<<<<<<<<<< @<<<<< ", + [$i, $monsters_Killed[$i]{nameID}, $monsters_Killed[$i]{name}, $monsters_Killed[$i]{count}]); + $total += $monsters_Killed[$i]{count}; + } + $msg .= "\n" . + TF("Total number of killed monsters: %s\n", $total) . + ('-'x40) . "\n"; + if ($arg1 eq "monster" || $arg1 eq "") { + message $msg, "list"; + } + } + + if (($arg1 eq "item") || ($arg1 eq "report") || ($arg1 eq "output")) { + $knownArg = 1; + + $msg .= center(T(" Item Change Count "), 36, '-') ."\n". + T("Name Count\n"); + for my $item (sort keys %itemChange) { + next unless $itemChange{$item}; + $msg .= swrite( + "@<<<<<<<<<<<<<<<<<<<<<<<<<<<<< @<<<<<<", + [$item, $itemChange{$item}]); + } + $msg .= ('-'x36) . "\n"; + message $msg, "list"; + + if ($arg1 eq "output") { + print F $msg; + close(F); + } + } + + if (!$knownArg) { + error T("Syntax error in function 'exp' (Exp Report)\n" . + "Usage: exp [<report | monster | item | reset | output>]\n"); + } +} + +sub cmdFalcon { + my (undef, $arg1) = @_; + + my $hasFalcon = $char && $char->statusActive('EFFECTSTATE_BIRD'); + if ($arg1 eq "") { + if ($hasFalcon) { + message T("Your falcon is active\n"); + } else { + message T("Your falcon is inactive\n"); + } + } elsif ($arg1 eq "release") { + if (!$hasFalcon) { + error T("Error in function 'falcon release' (Remove Falcon Status)\n" . + "You don't possess a falcon.\n"); + } elsif (!$net || $net->getState() != Network::IN_GAME) { + error TF("You must be logged in the game to use this command '%s'\n", 'falcon release'); + return; + } else { + $messageSender->sendCompanionRelease(); + } + } +} + +sub cmdFollow { + my (undef, $arg1) = @_; + if ($arg1 eq "") { + error T("Syntax Error in function 'follow' (Follow Player)\n" . + "Usage: follow <player #>\n"); + } elsif ($arg1 eq "stop") { + AI::clear("follow"); + configModify("follow", 0); + } elsif ($arg1 =~ /^\d+$/) { + if (!$playersID[$arg1]) { + error TF("Error in function 'follow' (Follow Player)\n" . + "Player %s either not visible or not online in party.\n", $arg1); + } else { + AI::clear("follow"); + main::ai_follow($players{$playersID[$arg1]}->name); + configModify("follow", 1); + configModify("followTarget", $players{$playersID[$arg1]}{name}); + } + + } else { + AI::clear("follow"); + main::ai_follow($arg1); + configModify("follow", 1); + configModify("followTarget", $arg1); + } +} + +sub cmdFriend { + my (undef, $args) = @_; + my ($arg1, $arg2) = split(' ', $args, 2); + + if ($arg1 eq "") { + my $msg = center(T(" Friends "), 36, '-') ."\n". + T("# Name Online\n"); + for (my $i = 0; $i < @friendsID; $i++) { + $msg .= swrite( + "@< @<<<<<<<<<<<<<<<<<<<<<<< @", + [$i + 1, $friends{$i}{'name'}, $friends{$i}{'online'}? 'X':'']); + } + $msg .= ('-'x36) . "\n"; + message $msg, "list"; + + } elsif (!$net || $net->getState() != Network::IN_GAME) { + error TF("You must be logged in the game to use this command '%s'\n", 'friend ' .$arg1); + return; + + } elsif ($arg1 eq "request") { + my $player = Match::player($arg2); + + if (!$player) { + error TF("Player %s does not exist\n", $arg2); + } elsif (!defined $player->{name}) { + error T("Player name has not been received, please try again\n"); + } else { + my $alreadyFriend = 0; + for (my $i = 0; $i < @friendsID; $i++) { + if ($friends{$i}{'name'} eq $player->{name}) { + $alreadyFriend = 1; + last; + } + } + if ($alreadyFriend) { + error TF("%s is already your friend\n", $player->{name}); + } else { + message TF("Requesting %s to be your friend\n", $player->{name}); + $messageSender->sendFriendRequest($player->{name}); + } + } + + } elsif ($arg1 eq "remove") { + if ($arg2 !~ /^\d+$/ || $arg2 < 1 || $arg2 > @friendsID) { + error TF("Friend #%s does not exist\n", $arg2); + } else { + $arg2--; + message TF("Attempting to remove %s from your friend list\n", $friends{$arg2}{'name'}); + $messageSender->sendFriendRemove($friends{$arg2}{'accountID'}, $friends{$arg2}{'charID'}); + } + + } elsif ($arg1 eq "accept") { + if ($incomingFriend{'accountID'} eq "") { + error T("Can't accept the friend request, no incoming request\n"); + } else { + message TF("Accepting the friend request from %s\n", $incomingFriend{'name'}); + $messageSender->sendFriendListReply($incomingFriend{'accountID'}, $incomingFriend{'charID'}, 1); + undef %incomingFriend; + } + + } elsif ($arg1 eq "reject") { + if ($incomingFriend{'accountID'} eq "") { + error T("Can't reject the friend request - no incoming request\n"); + } else { + message TF("Rejecting the friend request from %s\n", $incomingFriend{'name'}); + $messageSender->sendFriendListReply($incomingFriend{'accountID'}, $incomingFriend{'charID'}, 0); + undef %incomingFriend; + } + + } elsif ($arg1 eq "pm") { + if ($arg2 !~ /^\d+$/ || $arg2 < 1 || $arg2 > @friendsID) { + error TF("Friend #%s does not exist\n", $arg2); + } else { + $arg2--; + if (binFind(\@privMsgUsers, $friends{$arg2}{'name'}) eq "") { + message TF("Friend %s has been added to the PM list as %s\n", $friends{$arg2}{'name'}, @privMsgUsers); + $privMsgUsers[@privMsgUsers] = $friends{$arg2}{'name'}; + } else { + message TF("Friend %s is already in the PM list\n", $friends{$arg2}{'name'}); + } + } + + } else { + error T("Syntax Error in function 'friend' (Manage Friends List)\n" . + "Usage: friend [request|remove|accept|reject|pm]\n"); + } +} + +sub cmdSlave { + my ($cmd, $subcmd) = @_; + my @args = parseArgs($subcmd); + + if (!$char) { + error T("Error: Can't detect slaves - character is not yet ready\n"); + return; + } + + my $slave; + if ($cmd eq 'homun') { + if ($char->has_homunculus && $char->{homunculus}{appear_time}) { + $slave = $char->{homunculus}; + } else { + error T("Error: No slave detected.\n"); + } + } elsif ($cmd eq 'merc') { + $slave = $char->{mercenary}; + if ($char->has_mercenary && $char->{mercenary}{appear_time}) { + $slave = $char->{mercenary}; + } else { + error T("Error: No slave detected.\n"); + } + } else { + error T("Error: Unknown command in cmdSlave\n"); + } + my $string = $cmd; + + if ($slave->isa("AI::Slave::Homunculus") && $slave->{homunculus_info}{vaporized}) { + my $skill = new Skill(handle => 'AM_CALLHOMUN'); + error TF("Homunculus is in rest, use skills '%s' (ss %d).\n", $skill->getName, $skill->getIDN); + + } elsif ($slave->isa("AI::Slave::Homunculus") && $slave->{homunculus_info}{dead}) { + my $skill = new Skill(handle => 'AM_RESURRECTHOMUN'); + error TF("Homunculus is dead, use skills '%s' (ss %d).\n", $skill->getName, $skill->getIDN); + + } elsif ($subcmd eq "s" || $subcmd eq "status") { + my $hp_string = $slave->{hp}. '/' .$slave->{hp_max} . ' (' . sprintf("%.2f",$slave->hp_percent) . '%)'; + my $sp_string = $slave->{sp}."/".$slave->{sp_max}." (".sprintf("%.2f",$slave->sp_percent)."%)"; + my $exp_string = ( + defined $slave->{exp} + ? T("Exp: ") . formatNumber($slave->{exp})."/".formatNumber($slave->{exp_max})." (".sprintf("%.2f",$slave->exp_percent)."%)" + : ( + defined $slave->{kills} + ? T("Kills: ") . formatNumber($slave->{kills}) + : '' + ) + ); + + my ($intimacy_label, $intimacy_string) = ( + defined $slave->{intimacy} + ? (T('Intimacy:'), $slave->{intimacy}) + : ( + defined $slave->{faith} + ? (T('Faith:'), $slave->{faith}) + : ('', '') + ) + ); + + my $hunger_string = defined $slave->{hunger} ? $slave->{hunger} : T('N/A'); + my $accessory_string = defined $slave->{accessory} ? $slave->{accessory} : T('N/A'); + my $summons_string = defined $slave->{summons} ? $slave->{summons} : T('N/A'); + my $skillpt_string = defined $slave->{points_skill} ? $slave->{points_skill} : T('N/A'); + my $range_string = defined $slave->{attack_range} ? $slave->{attack_range} : T('N/A'); + my $contractend_string = defined $slave->{contract_end} ? getFormattedDate(int($slave->{contract_end})) : T('N/A'); + + my $msg = swrite( + center(T(" Slave Status "), 78, '-') . "\n" . + T("Name: \@<<<<<<<<<<<<<<<<<<<<<<<<< HP: \@>>>>>>>>>>>>>>>>>>\n" . + "Type: \@<<<<<<<<<<<<<<<<<<<<<<<<< SP: \@>>>>>>>>>>>>>>>>>>\n" . + "Job: \@<<<<<<<<<<<<<<<\n" . + "Level: \@<< \@>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n") . + "\n" . + T("Atk: \@>>> Matk: \@>>> Hunger: \@>>>\n" . + "Hit: \@>>> Critical: \@>>> \@<<<<<<<<< \@>>>\n" . + "Def: \@>>> Mdef: \@>>> Accessory: \@>>>\n" . + "Flee: \@>>> Aspd: \@>>> Summons: \@>>>\n" . + "Range: \@>> Skill pt: \@>>> Contract End: \@<<<<<<<<<<\n"), + [$slave->{'name'}, $hp_string, + $slave->{'actorType'}, $sp_string, + $jobs_lut{$slave->{'jobID'}}, + $slave->{'level'}, $exp_string, + $slave->{'atk'}, $slave->{'matk'}, $hunger_string, + $slave->{'hit'}, $slave->{'critical'}, $intimacy_label, $intimacy_string, + $slave->{'def'}, $slave->{'mdef'}, $accessory_string, + $slave->{'flee'}, $slave->{'attack_speed'}, $summons_string, + $range_string, $skillpt_string, $contractend_string]); + + $msg .= TF("Statuses: %s \n", $slave->statusesString); + $msg .= ('-'x78) . "\n"; + + message $msg, "info"; + + } elsif ($subcmd eq "feed") { + unless (defined $slave->{hunger}) { + error T("This slave can not be feeded\n"); + return; + } + if (!$net || $net->getState() != Network::IN_GAME) { + error TF("You must be logged in the game to use this command '%s'\n", $cmd .' ' .$subcmd); + return; + } + if ($slave->{hunger} >= 76) { + message T("Your homunculus is not yet hungry. Feeding it now will lower intimacy.\n"), "homunculus"; + } else { + $messageSender->sendHomunculusCommand(1); + message T("Feeding your homunculus.\n"), "homunculus"; + } + + } elsif ($subcmd eq "delete" || $subcmd eq "fire") { + if (!$net || $net->getState() != Network::IN_GAME) { + error TF("You must be logged in the game to use this command '%s'\n", $cmd .' ' .$subcmd); + return; + } + if ($slave->isa("AI::Slave::Mercenary")) { + $messageSender->sendMercenaryCommand (2); + } elsif ($slave->isa("AI::Slave::Homunculus")) { + $messageSender->sendHomunculusCommand (2); + } + } elsif ($args[0] eq "move") { + if (!$net || $net->getState() != Network::IN_GAME) { + error TF("You must be logged in the game to use this command '%s'\n", $cmd . ' ' .$subcmd); + return; + } + if (!($args[1] =~ /^\d+$/) || !($args[2] =~ /^\d+$/)) { + error TF("Error in function '%s move' (Slave Move)\n" . + "Invalid coordinates (%s, %s) specified.\n", $cmd, $args[1], $args[2]); + return; + } else { + # max distance that homunculus can follow: 17 + $messageSender->sendSlaveMove($slave->{ID}, $args[1], $args[2]); + } + + } elsif ($subcmd eq "standby") { + if (!$net || $net->getState() != Network::IN_GAME) { + error TF("You must be logged in the game to use this command '%s'\n", $cmd .' ' .$subcmd); + return; + } + $messageSender->sendSlaveStandBy($slave->{ID}); + + } elsif ($args[0] eq 'ai') { + if ($args[1] eq 'clear') { + $slave->clear(); + message T("Slave AI sequences cleared\n"), "success"; + + } elsif ($args[1] eq 'print') { + # Display detailed info about current AI sequence + my $msg = center(T(" Slave AI Sequence "), 50, '-') ."\n"; + my $index = 0; + foreach (@{$slave->{slave_ai_seq}}) { + $msg .= "$index: $_ " . dumpHash(\%{$slave->{slave_ai_seq_args}[$index]}) . "\n\n"; + $index++; + } + $msg .= ('-'x50) . "\n"; + message $msg, "list"; + + } elsif ($args[1] eq 'on' || $args[1] eq 'auto') { + # Set AI to auto mode + if ($slave->{slave_AI} == AI::AUTO) { + message T("Slave AI is already set to auto mode\n"), "success"; + } else { + $slave->{slave_AI} = AI::AUTO; + undef $slave->{slave_AI_forcedOff}; + message T("Slave AI set to auto mode\n"), "success"; + } + } elsif ($args[1] eq 'manual') { + # Set AI to manual mode + if ($slave->{slave_AI} == AI::MANUAL) { + message T("Slave AI is already set to manual mode\n"), "success"; + } else { + $slave->{slave_AI} = AI::MANUAL; + $slave->{slave_AI_forcedOff} = 1; + message T("Slave AI set to manual mode\n"), "success"; + } + } elsif ($args[1] eq 'off') { + # Turn AI off + if ($slave->{slave_AI}) { + undef $slave->{slave_AI}; + $slave->{slave_AI_forcedOff} = 1; + message T("Slave AI turned off\n"), "success"; + } else { + message T("Slave AI is already off\n"), "success"; + } + + } elsif ($args[1] eq '') { + # Toggle AI + if ($slave->{slave_AI} == AI::AUTO) { + undef $slave->{slave_AI}; + $slave->{slave_AI_forcedOff} = 1; + message T("Slave AI turned off\n"), "success"; + } elsif (!$slave->{slave_AI}) { + $slave->{slave_AI} = AI::MANUAL; + $slave->{slave_AI_forcedOff} = 1; + message T("Slave AI set to manual mode\n"), "success"; + } elsif ($slave->{slave_AI} == AI::MANUAL) { + $slave->{slave_AI} = AI::AUTO; + undef $slave->{slave_AI_forcedOff}; + message T("Slave AI set to auto mode\n"), "success"; + } + + } else { + error TF("Syntax Error in function 'slave ai' (Slave AI Commands)\n" . + "Usage: %s ai [ clear | print | auto | manual | off ]\n", $string); + } + + } elsif ($subcmd eq "aiv") { + if (!$slave->{slave_AI}) { + message TF("ai_seq (off) = %s\n", "@{$slave->{slave_ai_seq}}"), "list"; + } elsif ($slave->{slave_AI} == 1) { + message TF("ai_seq (manual) = %s\n", "@{$slave->{slave_ai_seq}}"), "list"; + } elsif ($slave->{slave_AI} == 2) { + message TF("ai_seq (auto) = %s\n", "@{$slave->{slave_ai_seq}}"), "list"; + } + message T("solution\n"), "list" if ($slave->args()->{'solution'}); + + } elsif ($args[0] eq "skills") { + if ($args[1] eq '') { + my $msg = center(T(" Slave Skill List "), 46, '-') ."\n". + T(" # Skill Name Lv SP\n"); + foreach my $handle (@{$slave->{slave_skillsID}}) { + my $skill = new Skill(handle => $handle); + if ($slave->checkSkillOwnership($skill)) { + my $sp = $slave->{skills}{$handle}{sp} || ''; + $msg .= swrite( + "@>>> @<<<<<<<<<<<<<<<<<<<<<<<<<<<< @>> @>>>", + [$skill->getIDN(), $skill->getName(), $slave->getSkillLevel($skill), $sp]); + } + } + $msg .= TF("\nSkill Points: %d\n", $slave->{points_skill}) if defined $slave->{points_skill}; + $msg .= ('-'x46) . "\n"; + message $msg, "list"; + + } elsif ($args[1] eq "add" && $args[2] =~ /\d+/) { + if (!$net || $net->getState() != Network::IN_GAME) { + error TF("You must be logged in the game to use this command '%s'\n", $cmd .' ' .$subcmd); + return; + } + my $skill = new Skill(idn => $args[2]); + if (!$skill->getIDN() || !$char->{skills}{$skill->getHandle()}) { + error TF("Error in function '%s skills add' (Add Skill Point)\n" . + "Skill %s does not exist.\n", $cmd, $args[2]); + } elsif ($slave->{points_skill} < 1) { + error TF("Error in function '%s skills add' (Add Skill Point)\n" . + "Not enough skill points to increase %s\n", $cmd, $skill->getName()); + } else { + $messageSender->sendAddSkillPoint($skill->getIDN()); + } + + } elsif ($args[1] eq "desc" && $args[2] =~ /\d+/) { + my $skill = new Skill(idn => $args[2]); + if (!$skill->getIDN()) { + error TF("Error in function '%s skills desc' (Skill Description)\n" . + "Skill %s does not exist.\n", $cmd, $args[2]); + } else { + my $description = $skillsDesc_lut{$skill->getHandle()} || T("Error: No description available.\n"); + my $msg = center(T(" Skill Description "), 79, '=') ."\n". + TF("Skill: %s", $description) . + ('='x79) . "\n"; + message $msg, "list"; + } + + } else { + error TF("Syntax Error in function 'slave skills' (Slave Skills Functions)\n" . + "Usage: %s skills [(<add | desc>) [<skill #>]]\n", $string); + } + + } elsif ($args[0] eq "rename") { + if ($char->{homunculus}{renameflag} == 0) { + if ($args[1] ne '') { + if (length($args[1]) < 25) { + $messageSender->sendHomunculusName($args[1]); + } else { + error T("The name can not exceed 24 characters\n"); + } + } else { + error TF("Syntax Error in function 'slave rename' (Slave Rename)\n" . + "Usage: %s rename <new name>\n", $string); + } + } else { + error T("His homunculus has been named or not under conditions to be renamed!\n"); + } + + } else { + error TF("Usage: %s <feed | s | status | move | standby | ai | aiv | skills | delete | rename>\n", $string); + } +} + +sub cmdMiscConf { + my (undef, $args) = @_; + my ($command, $flag) = parseArgs( $args ); + + if (!$net || $net->getState() != Network::IN_GAME) { + error TF("You must be logged in the game to use this command '%s'\n", shift); + return; + } + + my $check = ($flag eq 'on') ? 1 : 0; + + if ( $command eq "show_eq" ) { + $messageSender->sendMiscConfigSet(0, $check); + } elsif ( $command eq "call" ) { + $messageSender->sendMiscConfigSet(1, $check); + } elsif ( $command eq "pet_feed" ) { + $messageSender->sendMiscConfigSet(2, $check); + } elsif ( $command eq "homun_feed" ) { + $messageSender->sendMiscConfigSet(3, $check); + } else { + error T("Syntax Error in function 'misc_conf' (Misc Configuration)\n" . + "misc_conf <show_eq|call|pet_feed|homun_feed> <on|off>\n"); + } +} + +sub cmdGetPlayerInfo { + if (!$net || $net->getState() != Network::IN_GAME) { + error TF("You must be logged in the game to use this command '%s'\n", shift); + return; + } + my (undef, $args) = @_; + return 0 if (isSafeActorQuery(pack("V", $args)) != 1); # Do not Query GM's + $messageSender->sendGetPlayerInfo(pack("V", $args)); +} + +sub cmdGetCharacterName { + if (!$net || $net->getState() != Network::IN_GAME) { + error TF("You must be logged in the game to use this command '%s'\n", shift); + return; + } + my (undef, $args) = @_; + $messageSender->sendGetCharacterName(pack("V", $args)); +} + +sub cmdGmb { + if (!$net || $net->getState() != Network::IN_GAME) { + error TF("You must be logged in the game to use this command '%s'\n", shift); + return; + } + + return unless ($char); + my ($cmd, $message) = @_; + + if ($cmd eq 'gmb' || $cmd eq 'gmlb') { + $message = "$char->{name}: $message"; + } elsif ($cmd eq 'gmbb' || $cmd eq 'gmlbb') { + $message = "blue$message"; + } elsif ($cmd ne 'gmnb' && $cmd ne 'gmlnb') { + error TF("Usage: %s <MESSAGE>\n", $cmd); + return; + } + + if ($cmd =~ /^gml/) { + $messageSender->sendGMBroadcastLocal($message); + } else { + $messageSender->sendGMBroadcast($message); + } +} + +sub cmdGmmapmove { + if (!$net || $net->getState() != Network::IN_GAME) { + error TF("You must be logged in the game to use this command '%s'\n", shift); + return; + } + + my (undef, $args) = @_; + + my ($map_name) = $args =~ /(\S+)/; + # this will pack as 0 if it fails to match + my ($x, $y) = $args =~ /\w+ (\d+) (\d+)/; + + if ($map_name eq '') { + error T("Usage: gmmapmove <FIELD>\n" . + "FIELD is a field name including .gat extension, like: gef_fild01.gat\n"); + return; + } + + $messageSender->sendGMMapMove($map_name, $x, $y); +} + +sub cmdGmsummon { + if (!$net || $net->getState() != Network::IN_GAME) { + error TF("You must be logged in the game to use this command '%s'\n", shift); + return; + } + + my (undef, $args) = @_; + + if ($args eq '') { + error T("Usage: gmsummon <player name>\n" . + "Summon a player.\n"); + } else { + $messageSender->sendGMSummon($args); + } +} + +sub cmdGmdc { + if (!$net || $net->getState() != Network::IN_GAME) { + error TF("You must be logged in the game to use this command '%s'\n", shift); + return; + } + + my (undef, $args) = @_; + + if ($args !~ /^\d+$/) { + error T("Usage: gmdc <player_AID>\n"); + return; + } + + $messageSender->sendGMKick($args); +} + +sub cmdGmkickall { + if (!$net || $net->getState() != Network::IN_GAME) { + error TF("You must be logged in the game to use this command '%s'\n", shift); + return; + } + + $messageSender->sendGMKickAll(); +} + +sub cmdGmcreate { + if (!$net || $net->getState() != Network::IN_GAME) { + error TF("You must be logged in the game to use this command '%s'\n", shift); + return; + } + my (undef, $args) = @_; + + if ($args eq '') { + error T("Usage: gmcreate (<MONSTER_NAME> || <Item_Name>) \n"); + return; + } + + $messageSender->sendGMMonsterItem($args); +} + +sub cmdGmhide { + if (!$net || $net->getState() != Network::IN_GAME) { + error TF("You must be logged in the game to use this command '%s'\n", shift); + return; + } + + $messageSender->sendGMChangeEffectState(0); +} + +sub cmdGmresetstate { + if (!$net || $net->getState() != Network::IN_GAME) { + error TF("You must be logged in the game to use this command '%s'\n", shift); + return; + } + + $messageSender->sendGMResetStateSkill(0); +} + +sub cmdGmresetskill { + if (!$net || $net->getState() != Network::IN_GAME) { + error TF("You must be logged in the game to use this command '%s'\n", shift); + return; + } + + $messageSender->sendGMResetStateSkill(1); +} + +sub cmdGmmute { + if (!$net || $net->getState() != Network::IN_GAME) { + error TF("You must be logged in the game to use this command '%s'\n", shift); + return; + } + my (undef, $args) = @_; + my ($ID, $time) = $args =~ /^(\d+) (\d+)/; + if (!$ID) { + error T("Usage: gmmute <ID> <minutes>\n"); + return; + } + + $messageSender->sendAlignment($ID, 1, $time); +} + +sub cmdGmunmute { + if (!$net || $net->getState() != Network::IN_GAME) { + error TF("You must be logged in the game to use this command '%s'\n", shift); + return; + } + my (undef, $args) = @_; + my ($ID, $time) = $args =~ /^(\d+) (\d+)/; + if (!$ID) { + error T("Usage: gmunmute <ID> <minutes>\n"); + return; + } + + $messageSender->sendAlignment($ID, 0, $time); +} + +sub cmdGmwarpto { + if (!$net || $net->getState() != Network::IN_GAME) { + error TF("You must be logged in the game to use this command '%s'\n", shift); + return; + } + my (undef, $args) = @_; + + if ($args eq '') { + error T("Usage: gmwarpto <Player Name>\n"); + return; + } + + $messageSender->sendGMShift($args); +} + +sub cmdGmrecall { + if (!$net || $net->getState() != Network::IN_GAME) { + error TF("You must be logged in the game to use this command '%s'\n", shift); + return; + } + my (undef, $args) = @_; + + if ($args eq '') { + error T("Usage: gmrecall [<Character Name> | <User Name>]\n"); + return; + } + + $messageSender->sendGMRecall($args); +} + +sub cmdGmremove { + if (!$net || $net->getState() != Network::IN_GAME) { + error TF("You must be logged in the game to use this command '%s'\n", shift); + return; + } + my (undef, $args) = @_; + + if ($args eq '') { + error T("Usage: gmremove [<Character Name> | <User Name>]\n"); + return; + } + + $messageSender->sendGMRemove($args); +} + +sub cmdGuild { + my (undef, $args) = @_; + my ($arg1, $arg2) = split(' ', $args, 2); + + if ($arg1 eq "" || (!%guild && ($arg1 eq "info" || $arg1 eq "member" || $arg1 eq "kick"))) { + if (!$net || $net->getState() != Network::IN_GAME) { + if ($arg1 eq "") { + error T("You must be logged in the game to request guild information\n"); + } else { + error T("Guild information is not yet available. You must login to the game and use the 'guild' command first\n"); + } + return; + } + message T("Requesting guild information...\n"), "info"; + $messageSender->sendGuildMasterMemberCheck(); + + # Replies 01B6 (Guild Info) and 014C (Guild Ally/Enemy List) + $messageSender->sendGuildRequestInfo(0); + + # Replies 0166 (Guild Member Titles List) and 0154 (Guild Members List) + $messageSender->sendGuildRequestInfo(1); + + # Replies 0166 (Guild Member Titles List) and 0160 (Guild Member Titles Info List) + $messageSender->sendGuildRequestInfo(2); + + # Replies 0162 (Guild Skill Info List) + $messageSender->sendGuildRequestInfo(3); + + # Replies 015C (Guild Expulsion List) + $messageSender->sendGuildRequestInfo(4); + + if ($arg1 eq "") { + message T("Enter command to view guild information: guild <info | member | request | join | leave | kick | ally | create | break>\n"), "info"; + } else { + message TF("Type 'guild %s' again to view the information.\n", $args), "info"; + } + + } elsif ($arg1 eq "info") { + my $msg = center(T(" Guild Information "), 40, '-') ."\n" . + TF("Name : %s\n" . + "Lv : %d\n" . + "Exp : %d/%d\n" . + "Master : %s\n" . + "Connect : %d/%d\n", + $guild{name}, $guild{lv}, $guild{exp}, $guild{exp_next}, $guild{master}, + $guild{conMember}, $guild{maxMember}); + for my $ally (keys %{$guild{ally}}) { + # Translation Comment: List of allies. Keep the same spaces of the - Guild Information - tag. + $msg .= TF("Ally : %s (%s)\n", $guild{ally}{$ally}, $ally); + } + for my $ally (keys %{$guild{enemy}}) { + # Translation Comment: List of enemies. Keep the same spaces of the - Guild Information - tag. + $msg .= TF("Enemy : %s (%s)\n", $guild{enemy}{$ally}, $ally); + } + $msg .= ('-'x40) . "\n"; + message $msg, "info"; + + } elsif ($arg1 eq "member") { + if (!$guild{member}) { + error T("No guild member information available.\n"); + return; + } + + my $msg = center(T(" Guild Member "), 79, '-') ."\n". + T("# Name Job Lv Title Online\n"); + + my ($i, $name, $job, $lvl, $title, $online, $ID, $charID); + my $count = @{$guild{member}}; + for ($i = 0; $i < $count; $i++) { + $name = $guild{member}[$i]{name}; + next if (!defined $name); + + $job = $jobs_lut{$guild{member}[$i]{jobID}}; + $lvl = $guild{member}[$i]{lv}; + $title = $guild{positions}[ $guild{member}[$i]{position} ]{title}; + + # Translation Comment: Guild member online + $online = $guild{member}[$i]{online} ? T("Yes") : T("No"); + $ID = unpack("V",$guild{member}[$i]{ID}); + $charID = unpack("V",$guild{member}[$i]{charID}); + + $msg .= swrite("@< @<<<<<<<<<<<<<<<<<<<<<<<<< @<<<<<<<<<<<< @<< @<<<<<<<<<<<<<<<<<<<<<<< @<<", + [$i, $name, $job, $lvl, $title, $online, $ID, $charID]); + } + $msg .= ('-'x79) . "\n"; + message $msg, "list"; + + } elsif (!$net || $net->getState() != Network::IN_GAME) { + error TF("You must be logged in the game to use this command '%s'\n", 'guild ' .$arg1); + return; + + } elsif ($arg1 eq "join") { + if ($arg2 ne "1" && $arg2 ne "0") { + error T("Syntax Error in function 'guild join' (Accept/Deny Guild Join Request)\n" . + "Usage: guild join <flag>\n"); + return; + } elsif ($incomingGuild{'ID'} eq "") { + error T("Error in function 'guild join' (Join/Request to Join Guild)\n" . + "Can't accept/deny guild request - no incoming request.\n"); + return; + } + + $messageSender->sendGuildJoin($incomingGuild{ID}, $arg2); + undef %incomingGuild; + if ($arg2) { + message T("You accepted the guild join request.\n"), "success"; + } else { + message T("You denied the guild join request.\n"), "info"; + } + + } elsif ($arg1 eq "create") { + if (!$arg2) { + error T("Syntax Error in function 'guild create' (Create Guild)\n" . + "Usage: guild create <name>\n"); + } else { + $messageSender->sendGuildCreate($arg2, $charID); + } + + } elsif (!defined $char->{guild}) { + error T("You are not in a guild.\n"); + + } elsif ($arg1 eq "request") { + my $player = Match::player($arg2); + if (!$player) { + error TF("Player %s does not exist.\n", $arg2); + } else { + $messageSender->sendGuildJoinRequest($player->{ID}, $charID); + message TF("Sent guild join request to %s\n", $player->{name}); + } + + } elsif ($arg1 eq "ally") { + if (!$guild{master}) { + error T("No guild information available. Type guild to refresh and then try again.\n"); + return; + } + my $player = Match::player($arg2); + if (!$player) { + error TF("Player %s does not exist.\n", $arg2); + } elsif (!$char->{name} eq $guild{master}) { + error T("You must be guildmaster to set an alliance\n"); + return; + } else { + $messageSender->sendGuildSetAlly($player->{ID}, $accountID, $charID); + message TF("Sent guild alliance request to %s\n", $player->{name}); + } + + } elsif ($arg1 eq "leave") { + $messageSender->sendGuildLeave($arg2, $guild{ID}, $charID); + message TF("Sending guild leave: %s\n", $arg2); + + } elsif ($arg1 eq "break") { + if (!$arg2) { + error T("Syntax Error in function 'guild break' (Break Guild)\n" . + "Usage: guild break <guild name>\n"); + } else { + $messageSender->sendGuildBreak($arg2); + message TF("Sending guild break: %s\n", $arg2); + } + + } elsif ($arg1 eq "kick") { + if (!$guild{member}) { + error T("No guild member information available.\n"); + return; + } + my @params = split(' ', $arg2, 2); + if ($params[0] =~ /^\d+$/) { + if ($guild{'member'}[$params[0]]) { + $messageSender->sendGuildMemberKick($char->{guildID}, + $guild{member}[$params[0]]{ID}, + $guild{member}[$params[0]]{charID}, + $params[1]); + } else { + error TF("Error in function 'guild kick' (Kick Guild Member)\n" . + "Invalid guild member '%s' specified.\n", $params[0]); + } + } else { + error T("Syntax Error in function 'guild kick' (Kick Guild Member)\n" . + "Usage: guild kick <number> <reason>\n"); + } + } +} + +sub cmdHelp { + # Display help message + my (undef, $args) = @_; + my @commands_req = split(/ +/, $args); + my @unknown; + my @found; + + my $msg = center(T(" Available commands "), 79, '=') ."\n" unless @commands_req; + + my @commands = (@commands_req)? @commands_req : (sort keys %commands); + + foreach my $switch (@commands) { + if ($commands{$switch}) { + if (ref($commands{$switch}{desc}) eq 'ARRAY') { + if (@commands_req) { + helpIndent($switch,$commands{$switch}{desc}); + } else { + $msg .= sprintf("%-11s %s\n",$switch, $commands{$switch}{desc}->[0]); + } + } + push @found, $switch; + } else { + push @unknown, $switch unless defined binFind(\@unknown,$switch); + } + } + + foreach (@found) { + binRemoveAndShift(\@unknown,$_); + } + + if (@unknown) { + if (@unknown == 1) { + error TF("The command \"%s\" doesn't exist.\n", $unknown[0]); + } else { + error TF("These commands don't exist: %s\n", join(', ', @unknown)); + } + error T("Type 'help' to see a list of all available commands.\n"); + } + $msg .= ('='x79) . "\n" unless @commands_req; + message $msg, "list" if $msg; +} + +sub helpIndent { + my $cmd = shift; + my $desc = shift; + my @tmp = @{$desc}; + my $message; + my $messageTmp; + my @words; + my $length = 0; + + $message = center(TF(" Help for '%s' ", $cmd), 119, "=")."\n"; + $message .= shift(@tmp) . "\n"; + + foreach (@tmp) { + $length = length($_->[0]) if length($_->[0]) > $length; + } + my $pattern = "$cmd %-${length}s %s\n"; + my $padsize = length($cmd) + $length + 5; + my $pad = sprintf("%-${padsize}s", ''); + + foreach (@tmp) { + if ($padsize + length($_->[1]) > 120) { + @words = split(/ /, $_->[1]); + $message .= sprintf("$cmd %-${length}s ", $_->[0]); + $messageTmp = ''; + foreach my $word (@words) { + if ($padsize + length($messageTmp) + length($word) + 1 > 119) { + $message .= $messageTmp . "\n$pad"; + $messageTmp = "$word "; + } else { + $messageTmp .= "$word "; + } + } + $message .= $messageTmp."\n"; + } + else { + $message .= sprintf($pattern, $_->[0], $_->[1]); + } + } + $message .= "=" x 119 . "\n"; + message $message, "list"; +} + +sub cmdIdentify { + if (!$net || $net->getState() != Network::IN_GAME) { + error TF("You must be logged in the game to use this command '%s'\n", shift); + return; + } + my (undef, $arg1) = @_; + if ($arg1 eq "" && @identifyID) { + my $msg = center(T(" Identify List "), 50, '-') ."\n"; + for (my $i = 0; $i < @identifyID; $i++) { + next if ($identifyID[$i] eq ""); + $msg .= swrite( + "@<<< @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<", + [$i, $char->inventory->get($identifyID[$i])->{name}]); + } + $msg .= ('-'x50) . "\n"; + message $msg, "list"; + } elsif (!@identifyID) { + error T("The identify list is empty, please use the identify skill or a magnifier first.\n"); + } elsif ($arg1 =~ /^\d+$/) { + if ($identifyID[$arg1] eq "") { + error TF("Error in function 'identify' (Identify Item)\n" . + "Identify Item %s does not exist\n", $arg1); + } else { + $messageSender->sendIdentify($char->inventory->get($identifyID[$arg1])->{ID}); + } + + } else { + error T("Syntax Error in function 'identify' (Identify Item)\n" . + "Usage: identify [<identify #>]\n"); + } +} + +sub cmdIgnore { + if (!$net || $net->getState() != Network::IN_GAME) { + error TF("You must be logged in the game to use this command '%s'\n", shift); + return; + } + my (undef, $args) = @_; + my ($arg1, $arg2) = $args =~ /^(\d+) ([\s\S]*)/; + if ($arg1 eq "" || $arg2 eq "" || ($arg1 ne "0" && $arg1 ne "1")) { + error T("Syntax Error in function 'ignore' (Ignore Player/Everyone)\n" . + "Usage: ignore <flag> <name | all>\n"); + } else { + if ($arg2 eq "all") { + $messageSender->sendIgnoreAll(!$arg1); + } else { + $messageSender->sendIgnore($arg2, !$arg1); + } + } +} + +sub cmdIhist { + # Display item history + my (undef, $args) = @_; + $args = 5 if ($args eq ""); + + if (!($args =~ /^\d+$/)) { + error T("Syntax Error in function 'ihist' (Show Item History)\n" . + "Usage: ihist [<number of entries #>]\n"); + + } elsif (open(ITEM, "<", $Settings::item_log_file)) { + my @item = <ITEM>; + close(ITEM); + my $msg = center(T(" Item History "), 79, '-') ."\n"; + my $i = @item - $args; + $i = 0 if ($i < 0); + for (; $i < @item; $i++) { + $msg .= $item[$i]; + } + $msg .= ('-'x50) . "\n"; + message $msg, "list"; + + } else { + error TF("Unable to open %s\n", $Settings::item_log_file); + } +} + + +=pod +=head2 cmdInventory + +Console command that displays a character's inventory contents +- With pretty text headers +- Items are displayed from lowest index to highest index, but, grouped + in the following sub-categories: + eq - Equipped Items (such as armour, shield, weapon in L/R/both hands) + neq- Non-equipped equipment items + nu - Non-usable items + u - Usable (consumable) items + +All items that are not identified will be suffixed with +"-- Not Identified" on the end. + +Syntax: i [eq|neq|nu|u|desc <IndexNumber>] + +Invalid arguments to this command will display an error message to +inform and correct the user. + +All text strings for headers, and to indicate Non-identified or pending +sale items should be translatable. + +=cut +sub cmdInventory { + # Display inventory items + my (undef, $args) = @_; + my ($arg1) = $args =~ /^(\w+)/; + my ($arg2) = $args =~ /^\w+ (.+)/; + + if (!$char || !$char->inventory->isReady()) { + error "Inventory is not available\n"; + return; + } + + if ($char->inventory->size() == 0) { + error T("Inventory is empty\n"); + return; + } + + if ($arg1 eq "" || $arg1 eq "eq" || $arg1 eq "neq" || $arg1 eq "u" || $arg1 eq "nu") { + my @useable; + my @equipment; + my @uequipment; + my @non_useable; + my ($i, $display, $index, $sell); + + for my $item (@{$char->inventory}) { + if ($item->usable) { + push @useable, $item->{binID}; + } elsif ($item->equippable && $item->{type_equip} != 0) { + my %eqp; + $eqp{index} = $item->{ID}; + $eqp{binID} = $item->{binID}; + $eqp{nameID} = $item->{nameID}; + $eqp{name} = $item->{name}; + $eqp{amount} = $item->{amount}; + $eqp{equipped} = ($item->{type} == 10 || $item->{type} == 16 || $item->{type} == 17 || $item->{type} == 19) ? $item->{amount} . " left" : $equipTypes_lut{$item->{equipped}}; + $eqp{type} = $itemTypes_lut{$item->{type}}; + $eqp{equipped} .= " ($item->{equipped})"; + # Translation Comment: Mark to tell item not identified + $eqp{identified} = " -- " . T("Not Identified") if !$item->{identified}; + if ($item->{equipped}) { + push @equipment, \%eqp; + } else { + push @uequipment, \%eqp; + } + } else { + push @non_useable, $item->{binID}; + } + } + # Start header -- Note: Title is translatable. + my $msg = center(T(" Inventory "), 50, '-') ."\n"; + + if ($arg1 eq "" || $arg1 eq "eq") { + # Translation Comment: List of equipment items worn by character + $msg .= T("-- Equipment (Equipped) --\n"); + foreach my $item (@equipment) { + $sell = defined(findIndex(\@sellList, "binID", $item->{binID})) ? T("Will be sold") : ""; + $display = sprintf("%-3d [%-6d] %s -- %s", $item->{binID}, $item->{nameID}, $item->{name}, $item->{equipped}); + $msg .= sprintf("%-57s %s\n", $display, $sell); + } + } + + if ($arg1 eq "" || $arg1 eq "neq") { + # Translation Comment: List of equipment items NOT worn + $msg .= T("-- Equipment (Not Equipped) --\n"); + foreach my $item (@uequipment) { + $sell = defined(findIndex(\@sellList, "binID", $item->{binID})) ? T("Will be sold") : ""; + $display = sprintf("%-3d [%-6d] %s (%s)", $item->{binID}, $item->{nameID}, $item->{name}, $item->{type}); + $display .= " x $item->{amount}" if $item->{amount} > 1; + $display .= $item->{identified}; + $msg .= sprintf("%-57s %s\n", $display, $sell); + } + } + + if ($arg1 eq "" || $arg1 eq "nu") { + # Translation Comment: List of non-usable items + $msg .= T("-- Non-Usable --\n"); + for ($i = 0; $i < @non_useable; $i++) { + $index = $non_useable[$i]; + my $item = $char->inventory->get($index); + my $nameID = "[".$item->{nameID}."]"; + $display = $item->{name}; + $display .= " x $item->{amount}"; + # Translation Comment: Tell if the item is marked to be sold + $sell = defined(findIndex(\@sellList, "binID", $index)) ? T("Will be sold") : ""; + $msg .= swrite( + "@<<< @<<<<<<<< @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< @<<<<<<<<<<<<", + [$index, $nameID, $display, $sell]); + } + } + + if ($arg1 eq "" || $arg1 eq "u") { + # Translation Comment: List of usable items + $msg .= T("-- Usable --\n"); + for ($i = 0; $i < @useable; $i++) { + $index = $useable[$i]; + my $item = $char->inventory->get($index); + my $nameID = "[".$item->{nameID}."]"; + $display = $item->{name}; + $display .= " x $item->{amount}"; + $sell = defined(findIndex(\@sellList, "binID", $index)) ? T("Will be sold") : ""; + $msg .= swrite( + "@<<< @<<<<<<<< @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< @<<<<<<<<<<<<", + [$index, $nameID, $display, $sell]); + } + } + + $msg .= ('-'x50) . "\n"; #Add footer onto end of list. + message $msg, "list"; + + } elsif ($arg1 eq "desc" && $arg2 ne "") { + cmdInventory_desc($arg2); + + } else { + error T("Syntax Error in function 'i' (Inventory List)\n" . + "Usage: i [<u|eq|neq|nu|desc>] [<inventory item>]\n"); + } +} + +sub cmdInventory_desc { + my ($name) = @_; + + my $item = Match::inventoryItem($name); + if (!$item) { + error TF("Error in function 'i' (Inventory Item Description)\n" . + "Inventory Item %s does not exist\n", $name); + return; + } + + printItemDesc($item); +} + +sub cmdItemList { + my $msg = center(T(" Item List "), 46, '-') ."\n". + T(" # Name Coord\n"); + for (my $i = 0; $i < @itemsID; $i++) { + next if ($itemsID[$i] eq ""); + my $item = $items{$itemsID[$i]}; + my $display = "$item->{name} x $item->{amount}"; + $msg .= sprintf("%4d %-30s (%3d, %3d)\n", + $i, $display, $item->{pos}{x}, $item->{pos}{y}); + } + $msg .= ('-'x46) . "\n"; + message $msg, "list"; +} + +sub cmdItemLogClear { + itemLog_clear(); + message T("Item log cleared.\n"), "success"; +} + +#sub cmdJudge { +# my (undef, $args) = @_; +# my ($arg1) = $args =~ /^(\d+)/; +# my ($arg2) = $args =~ /^\d+ (\d+)/; +# if ($arg1 eq "" || $arg2 eq "") { +# error "Syntax Error in function 'judge' (Give an alignment point to Player)\n" . +# "Usage: judge <player #> <0 (good) | 1 (bad)>\n"; +# } elsif ($playersID[$arg1] eq "") { +# error "Error in function 'judge' (Give an alignment point to Player)\n" . +# "Player $arg1 does not exist.\n"; +# } else { +# $arg2 = ($arg2 >= 1); +# $messageSender->sendAlignment($playersID[$arg1], $arg2); +# } +#} + +sub cmdKill { + my (undef, $ID) = @_; + + my $target = $playersID[$ID]; + unless ($target) { + error TF("Player %s does not exist.\n", $ID); + return; + } + + attack($target); +} + +sub cmdLook { + my (undef, $args) = @_; + my ($arg1) = $args =~ /^(\d+)/; + my ($arg2) = $args =~ /^\d+ (\d+)$/; + if ($arg1 eq "") { + error T("Syntax Error in function 'look' (Look a Direction)\n" . + "Usage: look <body dir> [<head dir>]\n"); + } else { + look($arg1, $arg2); + } +} + +sub cmdLookPlayer { + my (undef, $arg1) = @_; + if ($arg1 eq "") { + error T("Syntax Error in function 'lookp' (Look at Player)\n" . + "Usage: lookp <player #>\n"); + } elsif (!$playersID[$arg1]) { + error TF("Error in function 'lookp' (Look at Player)\n" . + "'%s' is not a valid player number.\n", $arg1); + } else { + lookAtPosition($players{$playersID[$arg1]}{pos_to}); + } +} + +sub cmdManualMove { + if (!$net || $net->getState() != Network::IN_GAME) { + error TF("You must be logged in the game to use this command '%s'\n", shift); + return; + } + my ($switch, $steps) = @_; + if (!$steps) { + $steps = 5; + } elsif ($steps !~ /^\d+$/) { + error TF("Error in function '%s' (Manual Move)\n" . + "Usage: %s [distance]\n", $switch, $switch); + return; + } + if ($switch eq "east") { + manualMove($steps, 0); + } elsif ($switch eq "west") { + manualMove(-$steps, 0); + } elsif ($switch eq "north") { + manualMove(0, $steps); + } elsif ($switch eq "south") { + manualMove(0, -$steps); + } elsif ($switch eq "northeast") { + manualMove($steps, $steps); + } elsif ($switch eq "southwest") { + manualMove(-$steps, -$steps); + } elsif ($switch eq "northwest") { + manualMove(-$steps, $steps); + } elsif ($switch eq "southeast") { + manualMove($steps, -$steps); + } +} + +sub cmdMemo { + if (!$net || $net->getState() != Network::IN_GAME) { + error TF("You must be logged in the game to use this command '%s'\n", shift); + return; + } + $messageSender->sendMemo(); +} + +sub cmdMonsterList { + my (undef, $args) = @_; + if ($args =~ /^\d+$/) { + if (my $monster = $monstersList->get($args)) { + my $msg = center(T(" Monster Info "), 50, '-') ."\n". + TF("%s (%d)\n" . + "Walk speed: %s secs per block\n", + $monster->name, $monster->{binID}, + $monster->{walk_speed}); + $msg .= TF("Statuses: %s \n", $monster->statusesString); + $msg .= '-' x 50 . "\n"; + message $msg, "info"; + } else { + error TF("Monster \"%s\" does not exist.\n", $args); + } + } else { + my ($dmgTo, $dmgFrom, $dist, $pos, $name, $monsters); + my $msg = center(T(" Monster List "), 79, '-') ."\n". + T("# Name ID DmgTo DmgFrom Distance Coordinates\n"); + for my $monster (@$monstersList) { + $dmgTo = ($monster->{dmgTo} ne "") + ? $monster->{dmgTo} + : 0; + $dmgFrom = ($monster->{dmgFrom} ne "") + ? $monster->{dmgFrom} + : 0; + $dist = distance($char->{pos_to}, $monster->{pos_to}); + $dist = sprintf("%.1f", $dist) if (index($dist, '.') > -1); + $pos = '(' . $monster->{pos_to}{x} . ', ' . $monster->{pos_to}{y} . ')'; + $name = $monster->name; + if ($name ne $monster->{name_given}) { + $name .= '[' . $monster->{name_given} . ']'; + } + $msg .= swrite( + "@<< @<<<<<<<<<<<<<<<<<<<<<<<<<< @<<<<<< @<<<< @<<<< @<<<<< @<<<<<<<<<<", + [$monster->{binID}, $name, $monster->{binType}, $dmgTo, $dmgFrom, $dist, $pos]); + } + $msg .= ('-'x79) . "\n"; + message $msg, "list"; + } +} + +sub cmdMove { + if (!$net || $net->getState() != Network::IN_GAME) { + error TF("You must be logged in the game to use this command '%s'\n", shift); + return; + } + my (undef, $args) = @_; + my @args_split = split(/\s+/, $args); + + my ($map_or_portal, $x, $y, $dist); + if (($args_split[0] =~ /^\d+$/) && ($args_split[1] =~ /^\d+$/) && ($args_split[2] =~ /^\S+$/)) { + # coordinates and map + $map_or_portal = $args_split[2]; + $x = $args_split[0]; + $y = $args_split[1]; + } elsif (($args_split[0] =~ /^\S+$/) && ($args_split[1] =~ /^\d+$/) && ($args_split[2] =~ /^\d+$/)) { + # map and coordinates + $map_or_portal = $args_split[0]; + $x = $args_split[1]; + $y = $args_split[2]; + } elsif (($args_split[0] =~ /^\S+$/) && !$args_split[1]) { + # map only + $map_or_portal = $args_split[0]; + } elsif (($args_split[0] =~ /^\d+$/) && ($args_split[1] =~ /^\d+$/) && !$args_split[2]) { + # coordinates only + $map_or_portal = $field->baseName; + $x = $args_split[0]; + $y = $args_split[1]; + } else { + error T("Syntax Error in function 'move' (Move Player)\n" . + "Usage: move <x> <y> [<map> [<distance from coordinates>]]\n" . + " move <map> [<x> <y> [<distance from coordinates>]]\n" . + " move <portal#>\n"); + } + + # if (($args_split[0] =~ /^\d+$/) && ($args_split[1] =~ /^\d+$/) && ($args_split[2] =~ /^\d+$/)) { + # # distance from x, y + # $dist = $args_split[2]; + # } elsif { + if ($args_split[3] =~ /^\d+$/) { + # distance from map x, y + $dist = $args_split[3]; + } + + + if ($map_or_portal eq "stop") { + AI::clear(qw/move route mapRoute/); + message T("Stopped all movement\n"), "success"; + } else { + AI::clear(qw/move route mapRoute/); + if ($currentChatRoom ne "") { + error T("Error in function 'move' (Move Player)\n" . + "Unable to walk while inside a chat room!\n" . + "Use the command: chat leave\n"); + } elsif ($shopstarted) { + error T("Error in function 'move' (Move Player)\n" . + "Unable to walk while the shop is open!\n" . + "Use the command: closeshop\n"); + } else { + if ($map_or_portal =~ /^\d+$/) { + if ($portalsID[$map_or_portal]) { + message TF("Move into portal number %s (%s,%s)\n", + $map_or_portal, $portals{$portalsID[$map_or_portal]}{'pos'}{'x'}, $portals{$portalsID[$map_or_portal]}{'pos'}{'y'}); + main::ai_route($field->baseName, $portals{$portalsID[$map_or_portal]}{'pos'}{'x'}, $portals{$portalsID[$map_or_portal]}{'pos'}{'y'}, attackOnRoute => 1, noSitAuto => 1); + } else { + error T("No portals exist.\n"); + } + } else { + # map + $map_or_portal =~ s/^(\w{3})?(\d@.*)/$2/; # remove instance. is it possible to move to an instance? if not, we could throw an error here + # TODO: implement Field::sourceName function here once they are implemented there - 2013.11.26 + my $file = $map_or_portal.'.fld2'; + $file = File::Spec->catfile($Settings::fields_folder, $file) if ($Settings::fields_folder); + $file .= ".gz" if (! -f $file); # compressed file + if ($maps_lut{"${map_or_portal}.rsw"} || -f $file) { + my $move_field = new Field(name => $map_or_portal); + if (defined $x && defined $y) { + if ($move_field->isOffMap($x, $y)) { + error TF("Coordinates %s %s are off the map %s\n",$x, $y, $map_or_portal); + return; + } + if (!$move_field->isWalkable($x, $y)) { + error TF("Coordinates %s %s are not walkable on the map %s\n",$x, $y, $map_or_portal); + return; + } + } + my $map_name = $maps_lut{"${map_or_portal}.rsw"} ? $maps_lut{"${map_or_portal}.rsw"} : T('Unknown Map'); + if ($dist) { + message TF("Calculating route to: %s(%s): %s, %s (Distance: %s)\n", + $map_name, $map_or_portal, $x, $y, $dist), "route"; + } elsif ($x ne "") { + message TF("Calculating route to: %s(%s): %s, %s\n", + $map_name, $map_or_portal, $x, $y), "route"; + } else { + message TF("Calculating route to: %s(%s)\n", + $map_name, $map_or_portal), "route"; + } + main::ai_route($map_or_portal, $x, $y, + attackOnRoute => 1, + noSitAuto => 1, + notifyUponArrival => 1, + distFromGoal => $dist); + } else { + error TF("Map %s does not exist\n", $map_or_portal); + } + } + } + } +} + +sub cmdNPCList { + my (undef, $args) = @_; + my @arg = parseArgs($args); + my $msg = center(T(" NPC List "), 57, '-') ."\n". + T("# Name Coordinates ID\n"); + if ($npcsList) { + if ($arg[0] =~ /^\d+$/) { + my $i = $arg[0]; + if (my $npc = $npcsList->get($i)) { + my $pos = "($npc->{pos_to}{x}, $npc->{pos_to}{y})"; + $msg .= swrite( + "@<<< @<<<<<<<<<<<<<<<<<<<<<<<<<<< @<<<<<<<<<< @<<<<<<<<", + [$i, $npc->name, $pos, $npc->{nameID}]); + $msg .= ('-'x57) . "\n"; + message $msg, "list"; + + } else { + error T("Syntax Error in function 'nl' (List NPCs)\n" . + "Usage: nl [<npc #>]\n"); + } + return; + } + + for my $npc (@$npcsList) { + my $pos = "($npc->{pos}{x}, $npc->{pos}{y})"; + $msg .= swrite( + "@<<< @<<<<<<<<<<<<<<<<<<<<<<<<<<< @<<<<<<<<<< @<<<<<<<<", + [$npc->{binID}, $npc->name, $pos, $npc->{nameID}]); + } + } + $msg .= ('-'x57) . "\n"; + message $msg, "list"; +} + +sub cmdOpenShop { + if (!$net || $net->getState() != Network::IN_GAME) { + error TF("You must be logged in the game to use this command '%s'\n", shift); + return; + } + + if ($config{'shop_useSkill'}) { + # This method is responsible to NOT uses a bug in which openkore opens the shop, + # using a vending skill and then open the shop + my $skill = new Skill(auto => "MC_VENDING"); + + require Task::UseSkill; + my $skillTask = new Task::UseSkill( + actor => $skill->getOwner, + skill => $skill, + priority => Task::USER_PRIORITY + ); + my $task = new Task::Chained( + name => 'openShop', + tasks => [ + new Task::ErrorReport(task => $skillTask), + Task::Timeout->new( + function => sub {main::openShop()}, + seconds => $timeout{ai_shop_useskill_delay}{timeout} ? $timeout{ai_shop_useskill_delay}{timeout} : 5, + ) + ] + ); + $taskManager->add($task); + } else { + # This method is responsible to uses a bug in which openkore opens the shop + # without using a vending skill + + main::openShop(); + } +} + +sub cmdOpenBuyerShop { + my (undef, $args) = @_; + + if (!$net || $net->getState() != Network::IN_GAME) { + error TF("You must be logged in the game to use this command '%s'\n", shift); + return; + } + + main::openBuyerShop(); + +} + +sub cmdParty { + my (undef, $args) = @_; + my ($arg1, $arg2) = parseArgs($args, 2); + + if (!$net || $net->getState() != Network::IN_GAME) { + error TF("You must be logged in the game to use this command\n"); + } elsif (!$char) { + error T("Error in function 'party' (Party Functions)\n" . + "Party info not available yet\n"); + } elsif (!$char->{party}{joined}) { + if ($arg1 eq "create") { + if ($arg2 eq "") { + error T("Syntax Error in function 'party create' (Organize Party)\n" . + "Usage: party create <party name>\n"); + } else { + $messageSender->sendPartyOrganize($arg2); + } + } elsif ($arg1 eq "join") { + if ($arg2 ne "1" && $arg2 ne "0") { + error T("Syntax Error in function 'party join' (Accept/Deny Party Join Request)\n" . + "Usage: party join <flag>\n"); + } elsif ($incomingParty{ID} eq "") { + error T("Error in function 'party join' (Join/Request to Join Party)\n" . + "Can't accept/deny party request - no incoming request.\n"); + } else { + if ($incomingParty{ACK} eq '02C7') { + $messageSender->sendPartyJoinRequestByNameReply($incomingParty{ID}, $arg2); + } else { + $messageSender->sendPartyJoin($incomingParty{ID}, $arg2); + } + undef %incomingParty; + } + } else { + error T("Error in function 'party' (Party Functions)\n" . + "You're not in a party.\n"); + } + } elsif ($char->{party}{joined} && ($arg1 eq "create" || $arg1 eq "join")) { + error T("Error in function 'party' (Party Functions)\n" . + "You're already in a party.\n"); + } elsif ($arg1 eq "" || $arg1 eq "info") { + my $msg = center(T(" Party Information "), 84, '-') ."\n". + TF("Party name: %s\n" . + "EXP Take: %s Item Take: %s Item Division: %s\n\n". + "# Name Map Coord Online HP\n", + $char->{'party'}{'name'}, + ($char->{party}{share}) ? T("Even") : T("Individual"), + ($char->{party}{itemPickup}) ? T("Even") : T("Individual"), + ($char->{party}{itemDivision}) ? T("Even") : T("Individual")); + for (my $i = 0; $i < @partyUsersID; $i++) { + next if ($partyUsersID[$i] eq ""); + my $coord_string = ""; + my $hp_string = ""; + my $name_string = $char->{'party'}{'users'}{$partyUsersID[$i]}{'name'}; + my $admin_string = ($char->{'party'}{'users'}{$partyUsersID[$i]}{'admin'}) ? T("A") : ""; + my $online_string; + my $map_string; + + if ($partyUsersID[$i] eq $accountID) { + # Translation Comment: Is the party user on list online? + $online_string = T("Yes"); + ($map_string) = $field->name; + $coord_string = $char->{'pos'}{'x'}. ", ".$char->{'pos'}{'y'}; + $hp_string = $char->{'hp'}."/".$char->{'hp_max'} + ." (".int($char->{'hp'}/$char->{'hp_max'} * 100) + ."%)"; + } else { + $online_string = ($char->{'party'}{'users'}{$partyUsersID[$i]}{'online'}) ? T("Yes") : T("No"); + ($map_string) = $char->{'party'}{'users'}{$partyUsersID[$i]}{'map'} =~ /([\s\S]*)\.gat/; + $coord_string = $char->{'party'}{'users'}{$partyUsersID[$i]}{'pos'}{'x'} + . ", ".$char->{'party'}{'users'}{$partyUsersID[$i]}{'pos'}{'y'} + if ($char->{'party'}{'users'}{$partyUsersID[$i]}{'pos'}{'x'} ne "" + && $char->{'party'}{'users'}{$partyUsersID[$i]}{'online'}); + $hp_string = $char->{'party'}{'users'}{$partyUsersID[$i]}{'hp'}."/".$char->{'party'}{'users'}{$partyUsersID[$i]}{'hp_max'} + ." (".int($char->{'party'}{'users'}{$partyUsersID[$i]}{'hp'}/$char->{'party'}{'users'}{$partyUsersID[$i]}{'hp_max'} * 100) + ."%)" if ($char->{'party'}{'users'}{$partyUsersID[$i]}{'hp_max'} && $char->{'party'}{'users'}{$partyUsersID[$i]}{'online'}); + } + $msg .= swrite( + "@< @ @<<<<<<<<<<<<<<<<<<<<< @<<<<<<<<<<<< @<<<<<<< @<< @<<<<<<<<<<<<<<<<<<<<<<<", + [$i, $admin_string, $name_string, $map_string, $coord_string, $online_string, $hp_string]); + } + $msg .= ('-'x84) . "\n"; + message $msg, "list"; + + } elsif ($arg1 eq "leave") { + $messageSender->sendPartyLeave(); + # party leader specific commands + } elsif ($arg1 eq "share" || $arg1 eq "shareitem" || $arg1 eq "shareauto" || $arg1 eq "sharediv" || $arg1 eq "kick" || $arg1 eq "leader" || $arg1 eq "request") { + if ($arg2 ne "") { + my $party_admin; + # check if we are the party leader before using leader specific commands. + for (my $i = 0; $i < @partyUsersID; $i++) { + if (($char->{'party'}{'users'}{$partyUsersID[$i]}{'admin'}) && ($char->{'party'}{'users'}{$partyUsersID[$i]}{'name'} eq $char->name)){ + debug T("You are the party leader.\n"), "info"; + $party_admin = 1; + last; + } + } + + if (!$party_admin) { + error TF("Error in function 'party %s'\n" . + "You must be the party leader in order to use this !\n", $arg1); + return; + } + } + + if ($arg1 eq "request") { + if ($arg2 =~ /\D/ || $args =~ /".*"/) { + message TF("Requesting player %s to join your party.\n", $arg2); + $messageSender->sendPartyJoinRequestByName($arg2); + } else { + if ($playersID[$arg2] eq "") { + error TF("Error in function 'party request' (Request to Join Party)\n" . + "Can't request to join party - player %s does not exist.\n", $arg2); + } else { + $messageSender->sendPartyJoinRequest($playersID[$arg2]); + } + } + } elsif ($arg1 eq "share"){ + if ($arg2 ne "1" && $arg2 ne "0") { + if ($arg2 eq "") { + message TF("Party EXP is set to '%s Take'\n", ($char->{party}{share}) ? T("Even") : T("Individual")); + } else { + error T("Syntax Error in function 'party share' (Set Party Share EXP)\n" . + "Usage: party share <flag>\n"); + } + } else { + $messageSender->sendPartyOption($arg2, $char->{party}{itemPickup}, $char->{party}{itemDivision}); + $char->{party}{shareForcedByCommand} = 1; + } + } elsif ($arg1 eq "shareitem") { + if ($arg2 ne "1" && $arg2 ne "0") { + if ($arg2 eq "") { + message TF("Party item is set to '%s Take'\n", ($char->{party}{itemPickup}) ? T("Even") : T("Individual")); + } else { + error T("Syntax Error in function 'party shareitem' (Set Party Share Item)\n" . + "Usage: party shareitem <flag>\n"); + } + } else { + $messageSender->sendPartyOption($char->{party}{share}, $arg2, $char->{party}{itemDivision}); + $char->{party}{shareForcedByCommand} = 1; + } + } elsif ($arg1 eq "sharediv") { + if ($arg2 ne "1" && $arg2 ne "0") { + if ($arg2 eq "") { + message TF("Party item division is set to '%s Take'\n", ($char->{party}{itemDivision}) ? T("Even") : T("Individual")); + } else { + error T("Syntax Error in function 'party sharediv' (Set Party Item Division)\n" . + "Usage: party sharediv <flag>\n"); + } + } else { + $messageSender->sendPartyOption($char->{party}{share}, $char->{party}{itemPickup}, $arg2); + $char->{party}{shareForcedByCommand} = 1; + } + } elsif ($arg1 eq "shareauto") { + $messageSender->sendPartyOption($config{partyAutoShare}, $config{partyAutoShareItem}, $config{partyAutoShareItemDiv}); + $char->{party}{shareForcedByCommand} = undef; + } elsif ($arg1 eq "kick") { + if ($arg2 eq "") { + error T("Syntax Error in function 'party kick' (Kick Party Member)\n" . + "Usage: party kick <party member>\n"); + } elsif ($arg2 =~ /\D/ || $args =~ /".*"/) { + my $found; + foreach (@partyUsersID) { + if ($char->{'party'}{'users'}{$_}{'name'} eq $arg2) { + $messageSender->sendPartyKick($_, $arg2); + $found = 1; + last; + } + } + + if (!$found) { + error TF("Error in function 'party kick' (Kick Party Member)\n" . + "Can't kick member - member %s doesn't exist.\n", $arg2); + } + } else { + if ($partyUsersID[$arg2] eq "") { + error TF("Error in function 'party kick' (Kick Party Member)\n" . + "Can't kick member - member %s doesn't exist.\n", $arg2); + } else { + $messageSender->sendPartyKick($partyUsersID[$arg2], $char->{'party'}{'users'}{$partyUsersID[$arg2]}{'name'}); + } + } + } elsif ($arg1 eq "leader") { + my $found; + if ($arg2 eq "") { + error T("Syntax Error in function 'party leader' (Change Party Leader)\n" . + "Usage: party leader <party member>\n"); + } elsif ($arg2 =~ /\D/ || $args =~ /".*"/) { + foreach (@partyUsersID) { + if ($char->{'party'}{'users'}{$_}{'name'} eq $arg2) { + $found = $_; + last; + } + } + if (!$found) { + error TF("Error in function 'party leader' (Change Party Leader)\n" . + "Can't change party leader - member %s doesn't exist.\n", $arg2); + } + } else { + if ($partyUsersID[$arg2] eq "") { + error TF("Error in function 'party leader' (Change Party Leader)\n" . + "Can't change party leader - member %s doesn't exist.\n", $arg2); + } else { + $found = $partyUsersID[$arg2]; + } + } + if ($found && $found eq $accountID) { + warning T("Can't change party leader - you are already a party leader.\n"); + } else { + $messageSender->sendPartyLeader($found); + } + } + } else { + error T("Syntax Error in function 'party' (Party Management)\n" . + "Usage: party [<info|create|join|request|leave|share|shareitem|sharediv|shareauto|kick|leader>]\n"); + } +} + +sub cmdPecopeco { + my (undef, $arg1) = @_; + + my $hasPecopeco = $char && $char->statusActive('EFFECTSTATE_CHICKEN'); + if ($arg1 eq "") { + if ($hasPecopeco) { + message T("Your Pecopeco is active\n"); + } else { + message T("Your Pecopeco is inactive\n"); + } + } elsif ($arg1 eq "release") { + if (!$net || $net->getState() != Network::IN_GAME) { + error TF("You must be logged in the game to use this command '%s'\n", 'pecopeco release'); + return; + } + if (!$hasPecopeco) { + error T("Error in function 'pecopeco release' (Remove Pecopeco Status)\n" . + "You don't possess a Pecopeco.\n"); + } else { + $messageSender->sendCompanionRelease(); + } + } +} + +sub cmdPet { + my (undef, $args_string) = @_; + my @args = parseArgs($args_string, 2); + + if (!$net || $net->getState() != Network::IN_GAME) { + error TF("You must be logged in the game to use this command '%s'\n", 'pet ' .$args[0]); + + } elsif ($args[0] eq "c" || $args[0] eq "capture") { + # todo: maybe make a match function for monsters? + if ($args[1] =~ /^\d+$/) { + if ($monstersID[$args[1]] eq "") { + error TF("Error in function 'pet [capture|c]' (Capture Pet)\n" . + "Monster %s does not exist.\n", $args[1]); + } else { + $messageSender->sendPetCapture($monstersID[$args[1]]); + } + } else { + error TF("Error in function 'pet [capture|c]' (Capture Pet)\n" . + "'%s' must be a monster index.\n", $args[1]); + } + + } elsif ($args[0] eq "h" || $args[0] eq "hatch") { + if(my $item = Match::inventoryItem($args[1])) { + # beware, you must first use the item "Pet Incubator", else you will get disconnected + $messageSender->sendPetHatch($item->{ID}); + } else { + error TF("Error in function 'pet [hatch|h] #' (Hatch Pet)\n" . + "Egg: %s could not be found.\n", $args[1]); + } + + } elsif ((!%pet||!$pet{hungry}) && defined $args[0]) { + error T("Error in function 'pet' (Pet Management)\n" . + "You don't have a pet.\n"); + + } elsif ($args[0] eq "s" || $args[0] eq "status") { + message center(T(" Pet Status "), 46, '-') ."\n". + TF("Name: %-24s Renameable: %s\n",$pet{name}, ($pet{renameflag}?T("Yes"):T("No"))). + TF("Type: %-24s Level: %s\n", monsterName($pet{type}), $pet{level}). + TF("Accessory: %-19s Hungry: %s\n", itemNameSimple($pet{accessory}), $pet{hungry}). + TF(" Friendly: %s\n", $pet{friendly}). + ('-'x46) . "\n", "list"; + } elsif ($args[0] eq "i" || $args[0] eq "info") { + $messageSender->sendPetMenu(0); + + } elsif ($args[0] eq "f" || $args[0] eq "feed") { + $messageSender->sendPetMenu(1); + + } elsif ($args[0] eq "p" || $args[0] eq "performance") { + $messageSender->sendPetMenu(2); + + } elsif ($args[0] eq "r" || $args[0] eq "return") { + $messageSender->sendPetMenu(3); + undef %pet; # todo: instead undef %pet when the actor (our pet) dissapears, this is safer (xkore) + + } elsif ($args[0] eq "u" || $args[0] eq "unequip") { + $messageSender->sendPetMenu(4); + + } elsif (($args[0] eq "n" || $args[0] eq "name") && $args[1]) { + $messageSender->sendPetName($args[1]); + + } elsif (($args[0] eq "e" || $args[0] eq "emotion") && $args[1]) { + if ($args[1] =~ /^\d+$/) { + $messageSender->sendPetEmotion($args[1]); + } else { + error TF("Error in function 'pet [emotion|e] <number>' (Emotion Pet)\n" . + "'%s' must be an integer.\n", $args[1]); + } + + } else { + message T("Usage: pet [capture <monster #> | hatch <item #> | status | info | feed | performance | return | unequip | name <name>] | emotion <number>\n"), "info"; + } +} + +sub cmdPetList { + my ($dist, $pos, $name, $pets); + my $msg = center(T(" Pet List "), 68, '-') ."\n". + T("# Name Type Distance Coordinates\n"); + + for my $pet (@$petsList) { + $dist = distance($char->{pos_to}, $pet->{pos_to}); + $dist = sprintf("%.1f", $dist) if (index($dist, '.') > -1); + $pos = '(' . $pet->{pos_to}{x} . ', ' . $pet->{pos_to}{y} . ')'; + $name = $pet->name; + + $msg .= swrite( + "@<< @<<<<<<<<<<<<<<<<<<<<<<< @<<<<<<<<<<<<<< @<<<<< @<<<<<<<<<<", + [$pet->{binID}, $name, monsterName($pet->{type}), $dist, $pos]); + } + $msg .= ('-'x68) . "\n"; + message $msg, "list"; +} + +sub cmdPlayerList { + my (undef, $args) = @_; + my $msg; + + if ($args eq "g") { + my $maxplg; + $msg = center(T(" Guild Player List "), 79, '-') ."\n". + T("# Name Sex Lv Job Dist Coord\n"); + for my $player (@$playersList) { + my ($name, $dist, $pos); + $name = $player->name; + + if ($char->{guild}{name} eq ($player->{guild}{name})) { + + if ($player->{guild} && %{$player->{guild}}) { + $name .= " [$player->{guild}{name}]"; + } + $dist = distance($char->{pos_to}, $player->{pos_to}); + $dist = sprintf("%.1f", $dist) if (index ($dist, '.') > -1); + $pos = '(' . $player->{pos_to}{x} . ', ' . $player->{pos_to}{y} . ')'; + + $maxplg++; + + $msg .= swrite( + "@<<< @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< @<<<< @<<< @<<<<<<<<<< @<<< @<<<<<<<<<", + [$player->{binID}, $name, $sex_lut{$player->{sex}}, $player->{lv}, $player->job, $dist, $pos]); + } + } + $msg .= TF("Total guild players: %s\n",$maxplg) if $maxplg; + if (my $totalPlayers = $playersList && $playersList->size) { + $msg .= TF("Total players: %s \n", $totalPlayers); + } else { + $msg .= T("There are no players near you.\n"); + } + $msg .= ('-'x79) . "\n"; + message $msg, "list"; + return; + } + + if ($args eq "p") { + my $maxplp; + $msg = center(T(" Party Player List "), 79, '-') ."\n". + T("# Name Sex Lv Job Dist Coord\n"); + for my $player (@$playersList) { + my ($name, $dist, $pos); + $name = $player->name; + + if ($char->{party}{name} eq ($player->{party}{name})) { + + if ($player->{guild} && %{$player->{guild}}) { + $name .= " [$player->{guild}{name}]"; + } + $dist = distance($char->{pos_to}, $player->{pos_to}); + $dist = sprintf("%.1f", $dist) if (index ($dist, '.') > -1); + $pos = '(' . $player->{pos_to}{x} . ', ' . $player->{pos_to}{y} . ')'; + + $maxplp++; + + $msg .= swrite( + "@<<< @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< @<<<< @<<< @<<<<<<<<<< @<<< @<<<<<<<<<", + [$player->{binID}, $name, $sex_lut{$player->{sex}}, $player->{lv}, $player->job, $dist, $pos]); + } + } + $msg .= TF("Total party players: %s \n",$maxplp) if $maxplp; + if (my $totalPlayers = $playersList && $playersList->size) { + $msg .= TF("Total players: %s \n", $totalPlayers); + } else { + $msg .= T("There are no players near you.\n"); + } + $msg .= ('-'x79) . "\n"; + message $msg, "list"; + return; + } + + if ($args ne "") { + my Actor::Player $player = Match::player($args) if ($playersList); + if (!$player) { + error TF("Player \"%s\" does not exist.\n", $args); + return; + } + + my $ID = $player->{ID}; + my $body = $player->{look}{body} % 8; + my $head = $player->{look}{head}; + if ($head == 0) { + $head = $body; + } elsif ($head == 1) { + $head = $body - 1; + } else { + $head = $body + 1; + } + + my $pos = calcPosition($player); + my $mypos = calcPosition($char); + my $dist = sprintf("%.1f", distance($pos, $mypos)); + $dist =~ s/\.0$//; + + my %vecPlayerToYou; + my %vecYouToPlayer; + getVector(\%vecPlayerToYou, $mypos, $pos); + getVector(\%vecYouToPlayer, $pos, $mypos); + my $degPlayerToYou = vectorToDegree(\%vecPlayerToYou); + my $degYouToPlayer = vectorToDegree(\%vecYouToPlayer); + my $hex = getHex($ID); + my $playerToYou = int(sprintf("%.0f", (360 - $degPlayerToYou) / 45)) % 8; + my $youToPlayer = int(sprintf("%.0f", (360 - $degYouToPlayer) / 45)) % 8; + my $headTop = headgearName($player->{headgear}{top}); + my $headMid = headgearName($player->{headgear}{mid}); + my $headLow = headgearName($player->{headgear}{low}); + + $msg = center(T(" Player Info "), 67, '-') ."\n" . + $player->name . " (" . $player->{binID} . ")\n" . + TF("Account ID: %s (Hex: %s)\n" . + "Title ID : %s\n" . + "Party: %s\n" . + "Guild: %s\n" . + "Guild title: %s\n" . + "Position: %s, %s (%s of you: %s degrees)\n" . + "Level: %-7d Distance: %-17s\n" . + "Sex: %-6s Class: %s\n\n" . + "Body direction: %-19s Head direction: %-19s\n" . + "Weapon: %s\n" . + "Shield: %s\n" . + "Upper headgear: %-19s Middle headgear: %-19s\n" . + "Lower headgear: %-19s Hair color: %-19s\n" . + "Walk speed: %s secs per block\n", + $player->{nameID}, $hex, $player->{title}{ID} ? $player->{title}{ID}: 'N/A', + ($player->{party} && $player->{party}{name} ne '') ? $player->{party}{name} : '', + ($player->{guild}) ? $player->{guild}{name} : '', + ($player->{guild}) ? $player->{guild}{title} : '', + $pos->{x}, $pos->{y}, $directions_lut{$youToPlayer}, int($degYouToPlayer), + $player->{lv}, $dist, $sex_lut{$player->{sex}}, $jobs_lut{$player->{jobID}}, + "$directions_lut{$body} ($body)", "$directions_lut{$head} ($head)", + itemName({nameID => $player->{weapon}}), + itemName({nameID => $player->{shield}}), + $headTop, $headMid, + $headLow, $haircolors{$player->hairColor()} . " (" . $player->hairColor() . ")", + $player->{walk_speed}); + if ($player->{dead}) { + $msg .= T("Player is dead.\n"); + } elsif ($player->{sitting}) { + $msg .= T("Player is sitting.\n"); + } + + if ($degPlayerToYou >= $head * 45 - 29 && $degPlayerToYou <= $head * 45 + 29) { + $msg .= T("Player is facing towards you.\n"); + } + $msg .= TF("\nStatuses: %s \n", $player->statusesString); + $msg .= '-' x 67 . "\n"; + message $msg, "info"; + return; + } + + { + $msg = center(T(" Player List "), 79, '-') ."\n". + T("# Name Sex Lv Job Dist Coord\n"); + for my $player (@$playersList) { + my ($name, $dist, $pos); + $name = $player->name; + if ($player->{guild} && %{$player->{guild}}) { + $name .= " [$player->{guild}{name}]"; + } + $dist = distance($char->{pos_to}, $player->{pos_to}); + $dist = sprintf("%.1f", $dist) if (index ($dist, '.') > -1); + $pos = '(' . $player->{pos_to}{x} . ', ' . $player->{pos_to}{y} . ')'; + $msg .= swrite( + "@<<< @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< @<<<< @<<< @<<<<<<<<<< @<<< @<<<<<<<<<", + [$player->{binID}, $name, $sex_lut{$player->{sex}}, $player->{lv}, $player->job, $dist, $pos]); + } + if (my $playersTotal = $playersList && $playersList->size) { + $msg .= TF("Total players: %s \n", $playersTotal); + } else {$msg .= T("There are no players near you.\n");} + $msg .= '-' x 79 . "\n"; + message $msg, "list"; + } +} + +sub cmdPlugin { + return if ($Settings::lockdown); + my (undef, $input) = @_; + my @args = split(/ +/, $input, 2); + + if (@args == 0) { + my $msg = center(T(" Currently loaded plugins "), 79, '-') ."\n". + T("# Name Description\n"); + my $i = -1; + foreach my $plugin (@Plugins::plugins) { + $i++; + next unless $plugin; + $msg .= swrite( + "@<< @<<<<<<<<<<<<<<<<<<< @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<", + [$i, $plugin->{name}, $plugin->{description}]); + } + $msg .= ('-'x79) . "\n"; + message $msg, "list"; + + } elsif ($args[0] eq 'reload') { + my @names; + + if ($args[1] =~ /^\d+$/) { + Plugins::reloadPlugins([$Plugins::plugins[$args[1]]]); + + } elsif ($args[1] eq '') { + error T("Syntax Error in function 'plugin reload' (Reload Plugin)\n" . + "Usage: plugin reload <plugin name|plugin number#|\"all\">\n"); + return; + + } elsif ($args[1] eq 'all') { + Plugins::reloadAll(); + + } else { + Plugins::reloadByRegexp($args[1]); + } + + } elsif ($args[0] eq 'load') { + if ($args[1] eq '') { + error T("Syntax Error in function 'plugin load' (Load Plugin)\n" . + "Usage: plugin load <filename|\"all\">\n"); + return; + } elsif ($args[1] eq 'all') { + Plugins::loadAll(); + } else { + Plugins::loadByRegexp($args[1]); + } + + } elsif ($args[0] eq 'unload') { + if ($args[1] =~ /^\d+$/) { + Plugins::unloadPlugins([$Plugins::plugins[$args[1]]]); + + } elsif ($args[1] eq '') { + error T("Syntax Error in function 'plugin unload' (Unload Plugin)\n" . + "Usage: plugin unload <plugin name|plugin number#|\"all\">\n"); + return; + + } elsif ($args[1] eq 'all') { + Plugins::unloadAll(); + message T("All plugins have been unloaded.\n"), "system"; + return; + + } else { + Plugins::unloadByRegexp($args[1]); + } + + } else { + my $msg = center(T(" Plugin command syntax "), 79, '-') ."\n" . + T("Command: Description:\n" . + " plugin List loaded plugins\n" . + " plugin load <filename> Load a plugin\n" . + " plugin unload <plugin name|plugin number#|\"all\"> Unload a loaded plugin\n" . + " plugin reload <plugin name|plugin number#|\"all\"> Reload a loaded plugin\n") . + ('-'x79) . "\n"; + if ($args[0] eq 'help') { + message $msg, "info"; + } else { + error T("Syntax Error in function 'plugin' (Control Plugins)\n"); + error $msg; + } + } +} + +sub cmdPMList { + my $msg = center(T(" PM List "), 30, '-') ."\n"; + for (my $i = 1; $i <= @privMsgUsers; $i++) { + $msg .= swrite( + "@<<< @<<<<<<<<<<<<<<<<<<<<<<<", + [$i, $privMsgUsers[$i - 1]]); + } + $msg .= ('-'x30) . "\n"; + message $msg, "list"; +} + +sub cmdPortalList { + my (undef, $args) = @_; + my ($arg) = parseArgs($args,1); + if ($arg eq '') { + my $msg = center(T(" Portal List "), 52, '-') ."\n". + T("# Name Coordinates\n"); + for (my $i = 0; $i < @portalsID; $i++) { + next if $portalsID[$i] eq ""; + my $portal = $portals{$portalsID[$i]}; + my $coords = "($portal->{pos}{x}, $portal->{pos}{y})"; + $msg .= swrite( + "@<<< @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< @<<<<<<<<<", + [$i, $portal->{name}, $coords]); + } + $msg .= ('-'x52) . "\n"; + message $msg, "list"; + } elsif ($arg eq 'recompile') { + Settings::loadByRegexp(qr/portals/); + Misc::compilePortals() if Misc::compilePortals_check(); + } elsif ($arg =~ /^add (.*)$/) { #Manual adding portals + #Command: portals add mora 56 25 bif_fild02 176 162 + #Command: portals add y_airport 143 43 y_airport 148 51 0 c r0 c r0 + debug "Input: $args\n"; + my ($srcMap, $srcX, $srcY, $dstMap, $dstX, $dstY, $seq) = $args =~ /^add ([a-zA-Z\_\-0-9]*) (\d{1,3}) (\d{1,3}) ([a-zA-Z\_\-0-9]*) (\d{1,3}) (\d{1,3})(.*)$/; #CHECKING + my $srcfile = $srcMap.'.fld2'; + $srcfile = File::Spec->catfile($Settings::fields_folder, $srcfile) if ($Settings::fields_folder); + $srcfile .= ".gz" if (! -f $srcfile); # compressed file + my $dstfile = $dstMap.'.fld2'; + $dstfile = File::Spec->catfile($Settings::fields_folder, $dstfile) if ($Settings::fields_folder); + $dstfile .= ".gz" if (! -f $dstfile); # compressed file + error TF("Files '%s' or '%s' does not exist.\n", $srcfile, $dstfile) if (! -f $srcfile || ! -f $dstfile); + if ($srcX > 0 && $srcY > 0 && $dstX > 0 && $dstY > 0 + && -f $srcfile && -f $dstfile) { #found map and valid corrdinates + if ($seq) { + message TF("Recorded new portal (destination): %s (%s, %s) -> %s (%s, %s) [%s]\n", $srcMap, $srcX, $srcY, $dstMap, $dstX, $dstY, $seq), "portalRecord"; + + FileParsers::updatePortalLUT2(Settings::getTableFilename("portals.txt"), + $srcMap, $srcX, $srcY, + $dstMap, $dstX, $dstY, + $seq); + } else { + message TF("Recorded new portal (destination): %s (%s, %s) -> %s (%s, %s)\n", $srcMap, $srcX, $srcY, $dstMap, $dstX, $dstY), "portalRecord"; + + FileParsers::updatePortalLUT(Settings::getTableFilename("portals.txt"), + $srcMap, $srcX, $srcY, + $dstMap, $dstX, $dstY); + } + } + } else { + error T("Syntax Error in function 'portals' (List portals)\n" . + "Usage: portals or portals <recompile|add>\n"); + } +} + +sub cmdPrivateAirship { + if (!$net || $net->getState() != Network::IN_GAME) { + error TF("You must be logged in the game to use this command '%s'\n", shift); + return; + } + my (undef, $args) = @_; + my ($map, $item_id) = parseArgs($args, 2); + + if (!defined $map || $map eq '') { + error T("Syntax Error in function 'privateairship' (Use Private Airship)\n" . + "Usage: privateairship <map name> [<item ID>]\n"); + return; + } + $item_id = '25464' if (!defined $item_id || $item_id eq ''); + if ($item_id !~ /^\d+$/) { + error T("Item ID must be numeric for 'privateairship'.\n"); + return; + } + if ($item_id != '25464') { + my $requested_item = itemNameSimple($item_id); + my $required_item = itemNameSimple('25464'); + error TF("%s cannot be used for Private Airship. Please use %s.\n", $requested_item, $required_item); + return; + } + my $map_name = $map; + $map_name .= '.gat' unless $map_name =~ /\.gat$/i; + my $field_name = $map_name; + $field_name =~ s/\.gat$//i; + unless (defined $maps_lut{"$field_name.rsw"}) { + error TF("Map '%s' does not exist for Private Airship.\n", $map_name); + return; + } + if (length($map_name) > 16) { + error TF("Map name '%s' is too long for Private Airship (maximum 16 characters including extension).\n", $map_name); + return; + } + my $item = $char->inventory->getByNameID($item_id); + unless ($item && $item->{amount}) { + error TF("You do not have %s required for Private Airship.\n", itemNameSimple($item_id)); + return; + } + $char->{last_private_airship_item} = $item_id + 0; + $char->{last_private_airship_map} = $map_name; + $messageSender->sendPrivateAirshipRequest($map_name, $item_id + 0); + message TF("Requested Private Airship to %s using %s.\n", $map_name, itemNameSimple($item_id)), "info"; +} + +sub cmdPrivateMessage { + if (!$net || $net->getState() != Network::IN_GAME) { + error TF("You must be logged in the game to use this command '%s'\n", shift); + return; + } + my ($switch, $args) = @_; + my ($user, $msg) = parseArgs($args, 2); + + if ($user eq "" || $msg eq "") { + error T("Syntax Error in function 'pm' (Private Message)\n" . + "Usage: pm (username) (message)\n pm (<#>) (message)\n"); + return; + + } elsif ($user =~ /^\d+$/) { + if ($user - 1 >= @privMsgUsers) { + error TF("Error in function 'pm' (Private Message)\n" . + "Quick look-up %s does not exist\n", $user); + } elsif (!@privMsgUsers) { + error T("Error in function 'pm' (Private Message)\n" . + "You have not pm-ed anyone before\n"); + } else { + $lastpm{msg} = $msg; + $lastpm{user} = $privMsgUsers[$user - 1]; + sendMessage($messageSender, "pm", $msg, $privMsgUsers[$user - 1]); + } + + } else { + if (!defined binFind(\@privMsgUsers, $user)) { + push @privMsgUsers, $user; + } + $lastpm{msg} = $msg; + $lastpm{user} = $user; + sendMessage($messageSender, "pm", $msg, $user); + } +} + +sub cmdQuit { + my (undef, $args) = @_; + if ($args eq "2") { + $messageSender->sendQuit(); + } + quit(); +} + +sub cmdReload { + my (undef, $args) = @_; + if ($args eq '') { + error T("Syntax Error in function 'reload' (Reload Configuration Files)\n" . + "Usage: reload <name|\"all\">\n"); + } else { + parseReload($args); + } +} + +sub cmdReloadCode { + my (undef, $args) = @_; + if ($args ne "") { + Modules::addToReloadQueue(parseArgs($args)); + } else { + Modules::reloadFile("$FindBin::RealBin/src/functions.pl"); + } +} + +sub cmdReloadCode2 { + my (undef, $args) = @_; + if ($args ne "") { + ($args =~ /\.pm$/)?Modules::addToReloadQueue2($args):Modules::addToReloadQueue2($args.".pm"); + } else { + Modules::reloadFile("$FindBin::RealBin/src/functions.pl"); + } +} + +sub cmdRelog { + my (undef, $arg) = @_; + #stay offline if arg is 0 + if (defined $arg && $arg == 0) { + offlineMode(); + } elsif (!$arg || $arg =~ /^\d+$/) { + @cmdQueueList = (); + $cmdQueue = 0; + relog($arg); + } elsif ($arg =~ /^\d+\.\.\d+$/) { + # range support + my @numbers = split(/\.\./, $arg); + if ($numbers[0] > $numbers[1]) { + error T("Invalid range in function 'relog'\n"); + } else { + @cmdQueueList = (); + $cmdQueue = 0; + relog(rand($numbers[1] - $numbers[0])+$numbers[0]); + } + } else { + error T("Syntax Error in function 'relog' (Log out then log in.)\n" . + "Usage: relog [delay]\n"); + } +} + +sub cmdRepair { + if (!$net || $net->getState() != Network::IN_GAME) { + error TF("You must be logged in the game to use this command '%s'\n", shift); + return; + } + my (undef, $binID) = @_; + if (!$repairList) { + error T("'Repair List' is empty.\n"); + + } elsif ($binID eq "") { + my $msg = center(T(" Repair List "), 80, '-') ."\n". + T(" # Short name Full name\n"); + for (my $i = 0; $i < @{$repairList}; $i++) { + next if ($repairList->[$i] eq ""); + my $shortName = itemNameSimple($repairList->[$i]{nameID}); + $msg .= sprintf("%4d %-30s %s\n", $i, $shortName, $repairList->[$i]->{name}); + } + $msg .= ('-'x80) . "\n"; + message $msg, "list"; + + } elsif ($binID =~ /^\d+$/) { + if ($repairList->[$binID]) { + my $shortName = itemNameSimple($repairList->[$binID]{nameID}); + message TF("Attempting to repair item: %s (%d)\n", $shortName, $binID); + $messageSender->sendRepairItem($repairList->[$binID]); + } else { + error TF("Item with index: %s does either not exist in the 'Repair List'.\n", $binID); + } + + } elsif ($binID eq "cancel") { + message T("Cancel repair item.\n"); + my %cancel = ( + index => 65535, # 0xFFFF + ); + $messageSender->sendRepairItem(\%cancel); + + } else { + error T("Syntax Error in function 'repair' (Repair player's items)\n" . + "Usage: repair\n" . + " repair <item #>\n" . + " repair cancel\n"); + } +} + +sub cmdReputation { + if (!$net || $net->getState() != Network::IN_GAME) { + error TF("You must be logged in the game to use this command '%s'\n", shift); + } else { + my $msg = center(" ". T("Reputation Status") ." ", 80, '-') ."\n"; + $msg .= swrite( + "@<<<< @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< @<<< @<<<<<<<<<", + [T("Type"), T("Name"), T("Lvl"), T("Points")] + ); + foreach my $reputation (@reputation_list) { + $msg .= swrite( + "@<<<< @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< @<<< @<<<<<<<<<", + [$reputation->{type}, $reputation_list_name[$reputation->{type} - 1], int($reputation->{points}/1000), $reputation->{points}%1000] + ); + } + $msg .= center("", 80, '-') ."\n"; + message $msg; + } +} + +sub cmdRespawn { + if (!$net || $net->getState() != Network::IN_GAME) { + error TF("You must be logged in the game to use this command '%s'\n", shift); + return; + } + if ($char->{dead}) { + $messageSender->sendRestart(0); + } else { + ai_useTeleport(2); + } +} + +sub cmdSell { + if (!$net || $net->getState() != Network::IN_GAME) { + error TF("You must be logged in the game to use this command '%s'\n", shift); + return; + } + my @args = parseArgs($_[1]); + + if ($args[0] eq "" && defined $ai_v{'npc_talk'} && exists $ai_v{'npc_talk'}{'talk'} && $ai_v{'npc_talk'}{'talk'} eq 'buy_or_sell') { + $messageSender->sendNPCBuySellList($talk{ID}, 1); + + } elsif ($args[0] eq "list") { + if (@sellList == 0) { + message T("Your sell list is empty.\n"), "info"; + } else { + my $msg = center(T(" Sell List "), 41, '-') ."\n". + T("# Item Amount\n"); + foreach my $item (@sellList) { + $msg .= sprintf("%-3d %-30s %d\n", $item->{binID}, $item->{name}, $item->{amount}); + } + $msg .= ('-'x41) . "\n"; + message $msg, "list"; + } + + } elsif ($args[0] eq "done") { + completeNpcSell(\@sellList); + } elsif ($args[0] eq "cancel") { + @sellList = (); + completeNpcSell(\@sellList); + message T("Sell list has been cleared.\n"), "info"; + + } elsif ($args[0] eq "" || ($args[0] !~ /^\d+$/ && $args[0] !~ /[,\-]/)) { + error T("Syntax Error in function 'sell' (Sell Inventory Item)\n" . + "Usage: sell <inventory item index #> [<amount>]\n" . + " sell list\n" . + " sell done\n" . + " sell cancel\n"); + + } else { + my @items = Actor::Item::getMultiple($args[0]); + if (@items > 0) { + foreach my $item (@items) { + my %obj; + + if (defined(findIndex(\@sellList, "binID", $item->{binID}))) { + error TF("%s (%s) is already in the sell list.\n", $item->nameString, $item->{binID}); + next; + } + next if ($item->{equipped}); + + $obj{name} = $item->nameString(); + $obj{ID} = $item->{ID}; + $obj{binID} = $item->{binID}; + if (!$args[1] || $args[1] > $item->{amount}) { + $obj{amount} = $item->{amount}; + } else { + $obj{amount} = $args[1]; + } + push @sellList, \%obj; + message TF("Added to sell list: %s (%s) x %s\n", $obj{name}, $obj{binID}, $obj{amount}), "info"; + } + message T("Type 'sell done' to sell everything in your sell list.\n"), "info"; + + } else { + error TF("Error in function 'sell' (Sell Inventory Item)\n" . + "'%s' is not a valid item index #; no item has been added to the sell list.\n", + $args[0]); + } + } +} + +sub cmdSendRaw { + if (!$net || $net->getState() == Network::NOT_CONNECTED) { + error TF("You must be connected to the server to use this command (%s)\n", shift); + return; + } + my (undef, $args) = @_; + $messageSender->sendRaw($args); +} + +sub cmdShopInfoSelf { + if (!$shopstarted) { + error T("You do not have a shop open.\n"); + return; + } + # FIXME: Read the packet the server sends us to determine + # the shop title instead of using $shop{title}. + my $msg = center(" $shop{title} ", 90, '-') ."\n". + T("# Name Type Price Amount Sold\n"); + my $priceAfterSale=0; + my $i = 1; + for my $item (@articles) { + next unless $item; + $msg .= swrite( + "@< @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< @<<<<<<<<<<<<<< @>>>>>>>>>>>>z @<<<<< @>>>>>", + [$i++, $item->{name}, $itemTypes_lut{$item->{type}}, formatNumber($item->{price}), $item->{quantity}, formatNumber($item->{sold})]); + $priceAfterSale += ($item->{quantity} * $item->{price}); + } + $msg .= "\n" . + TF("You have earned: %sz.\n" . + "Current zeny: %sz.\n" . + "Maximum earned: %sz.\n" . + "Maximum zeny: %sz.\n", + formatNumber($shopEarned), formatNumber($char->{zeny}), + formatNumber($priceAfterSale), formatNumber($priceAfterSale + $char->{zeny})) . + ('-'x90) . "\n"; + message $msg, "list"; +} + +sub cmdBuyShopInfoSelf { + if (!@selfBuyerItemList) { + error T("You do not have a buying shop open.\n"); + return; + } + # FIXME: Read the packet the server sends us to determine + # the shop title instead of using $shop{title}. + my $msg = center(" Buyer Shop ", 83, '-') ."\n". + T("# Name Type Price Amount\n"); + my $index = 0; + for my $item (@selfBuyerItemList) { + next unless $item; + $msg .= swrite( + "@< @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< @<<<<<<<<<<<<<< @>>>>>>>>>>>>z @<<<<<", + [$index, $item->{name}, $itemTypes_lut{$item->{type}}, formatNumber($item->{price}), $item->{amount}]); + } + $msg .= ('-'x83) . "\n"; + message $msg, "list"; +} + +sub cmdSit { + if (!$net || $net->getState() != Network::IN_GAME) { + error TF("You must be logged in the game to use this command '%s'\n", shift); + return; + } + if ($char->{skills}{NV_BASIC}{lv} < 3 && $char->{skills}{SU_BASIC_SKILL}{lv} < 1) { + error T("Basic Skill level 3 or New Basic Skill (Doram) is required in order to sit or stand.")."\n"; + return; + } + $ai_v{sitAuto_forcedBySitCommand} = 1; + AI::clear("move", "route", "mapRoute"); + AI::clear("attack") unless ai_getAggressives(); + require Task::SitStand; + my $task = new Task::ErrorReport( + task => new Task::SitStand( + actor => $char, + mode => 'sit', + priority => Task::USER_PRIORITY + ) + ); + $taskManager->add($task); + $ai_v{sitAuto_forceStop} = 0; +} + +sub cmdSkills { + my (undef, $args) = @_; + my ($arg1) = $args =~ /^(\w+)/; + my ($arg2) = $args =~ /^\w+ (\d+)/; + if ($arg1 eq "") { + if (!$char || !$char->{skills}) { + error T("Syntax Error in function 'skills' (Skills Functions)\n" . + "Skills list is not ready yet.\n"); + return; + } + my $msg = center(T(" Skill List "), 51, '-') ."\n". + T(" # Skill Name Lv SP\n"); + for my $handle (@skillsID) { + my $skill = new Skill(handle => $handle); + my $sp = $char->{skills}{$handle}{sp} || ''; + $msg .= swrite( + "@>>> @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< @>> @>>>", + [$skill->getIDN(), $skill->getName(), $char->getSkillLevel($skill), $sp]); + } + $msg .= TF("\nSkill Points: %d\n", $char->{points_skill}); + $msg .= ('-'x51) . "\n"; + message $msg, "list"; + + } elsif ($arg1 eq "add" && $arg2 =~ /\d+/) { + if (!$net || $net->getState() != Network::IN_GAME) { + error TF("You must be logged in the game to use this command '%s'\n", 'skills add'); + return; + } + my $skill = new Skill(idn => $arg2); + if (!$skill->getIDN() || !$char->{skills}{$skill->getHandle()}) { + error TF("Error in function 'skills add' (Add Skill Point)\n" . + "Skill %s does not exist.\n", $arg2); + } elsif ($char->{points_skill} < 1) { + error TF("Error in function 'skills add' (Add Skill Point)\n" . + "Not enough skill points to increase %s\n", $skill->getName()); + } elsif ($char->{skills}{$skill->getHandle()}{up} == 0) { + error TF("Error in function 'skills add' (Add Skill Point)\n" . + "Skill %s reached its maximum level or prerequisite not reached\n", $skill->getName()); + } else { + $messageSender->sendAddSkillPoint($skill->getIDN()); + } + + } elsif ($arg1 eq "desc" && $arg2 =~ /\d+/) { + my $skill = new Skill(idn => $arg2); + if (!$skill->getIDN()) { + error TF("Error in function 'skills desc' (Skill Description)\n" . + "Skill %s does not exist.\n", $arg2); + } else { + my $description = $skillsDesc_lut{$skill->getHandle()} || T("Error: No description available.\n"); + my $msg = center(T(" Skill Description "), 79, '=') ."\n". + TF("Skill: %s\n\n", $skill->getName()); + $msg .= $description; + $msg .= ('='x79) . "\n"; + message $msg, "info"; + } + } else { + error T("Syntax Error in function 'skills' (Skills Functions)\n" . + "Usage: skills [<add | desc>] [<skill #>]\n"); + } +} + +sub cmdSlaveList { + my ($dist, $pos, $name, $slaves); + my $msg = center(T(" Slave List "), 79, '-') ."\n". + T("# Name Type Distance Coordinates\n"); + for my $slave (@$slavesList) { + $dist = distance($char->{pos_to}, $slave->{pos_to}); + $dist = sprintf("%.1f", $dist) if (index($dist, '.') > -1); + $pos = '(' . $slave->{pos_to}{x} . ', ' . $slave->{pos_to}{y} . ')'; + $name = $slave->name; + if ($name ne $jobs_lut{$slave->{type}}) { + $name .= ' [' . $jobs_lut{$slave->{type}} . ']'; + } + + $msg .= swrite( + "@<< @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< @<<<<<<<<<<< @<<<<< @<<<<<<<<<<", + [$slave->{binID}, $name, $slave->{actorType}, $dist, $pos]); + } + $msg .= ('-'x79) . "\n"; + message $msg, "list"; +} + +sub cmdSpells { + my $msg = center(T(" Area Effects List "), 66, '-') ."\n". + T(" # Type Source X Y Range lvl\n"); + for my $ID (@spellsID) { + my $spell = $spells{$ID}; + next unless $spell; + $msg .= sprintf("%3d %-20s %-20s %3d %3d %3d %2d\n", + $spell->{binID}, getSpellName($spell->{type}), main::getActorName($spell->{sourceID}), $spell->{pos}{x}, $spell->{pos}{y}, $spell->{range}, $spell->{lvl}); + } + $msg .= ('-'x66) . "\n"; + message $msg, "list"; +} + +sub cmdStarplace { + my (undef, $args) = @_; + my ($type) = parseArgs( $args ); + + if (!$net || $net->getState() != Network::IN_GAME) { + error TF("You must be logged in the game to use this command '%s'\n", shift); + return; + } + + my $flag; + if ($type eq "sun") { + $flag = 0; + } elsif ($type eq "moon") { + $flag = 1; + } elsif ($type eq "star") { + $flag = 2; + } else { + error T("Syntax Error in function 'starplace' (starplace agree)\n" . + "Usage: starplace [<sun | moon | star>]\n"); + return; + } + + $messageSender->sendFeelSaveOk($flag); +} + +sub cmdStand { + if (!$net || $net->getState() != Network::IN_GAME) { + error TF("You must be logged in the game to use this command '%s'\n", shift); + return; + } + delete $ai_v{sitAuto_forcedBySitCommand}; + $ai_v{sitAuto_forceStop} = 1; + require Task::SitStand; + my $task = new Task::ErrorReport( + task => new Task::SitStand( + actor => $char, + mode => 'stand', + priority => Task::USER_PRIORITY + ) + ); + $taskManager->add($task); +} + +sub cmdStatAdd { + cmdStats("st", "add ".$_[1]); +} + +sub cmdStats { + if (!$char) { + error T("Character stats information not yet available.\n"); + return; + } + + my ($subcmd, $arg) = parseArgs($_[1], 2); + + if ($subcmd eq "add") { + if (!$net || $net->getState() != Network::IN_GAME) { + error TF("You must be logged in the game to use this command 'st add'\n"); + return; + } + + if ($arg ne "str" && $arg ne "agi" && $arg ne "vit" && $arg ne "int" && $arg ne "dex" && $arg ne "luk") { + error T("Syntax Error in function 'st add' (Add Status Point)\n" . + "Usage: st add <str | agi | vit | int | dex | luk>\n"); + + } elsif ($char->{$arg} >= 99 && !$config{statsAdd_over_99}) { + error T("Error in function 'st add' (Add Status Point)\n" . + "You cannot add more stat points than 99\n"); + + } elsif ($char->{"points_$arg"} > $char->{'points_free'}) { + error TF("Error in function 'st add' (Add Status Point)\n" . + "Not enough status points to increase %s\n", $arg); + + } else { + my $ID; + if ($arg eq "str") { + $ID = STATUS_STR; + } elsif ($arg eq "agi") { + $ID = STATUS_AGI; + } elsif ($arg eq "vit") { + $ID = STATUS_VIT; + } elsif ($arg eq "int") { + $ID = STATUS_INT; + } elsif ($arg eq "dex") { + $ID = STATUS_DEX; + } elsif ($arg eq "luk") { + $ID = STATUS_LUK; + } + + $char->{$arg} += 1; + $messageSender->sendAddStatusPoint($ID); + } + } else { + my $guildName = $char->{guild} ? $char->{guild}{name} : T("None"); + my $msg = center(T(" Char Stats "), 44, '-') ."\n". + swrite(TF( + "Str: \@<<+\@<< #\@< Atk: \@<<+\@<< Def: \@<<+\@<<\n" . + "Agi: \@<<+\@<< #\@< Matk: \@<<\@\@<< Mdef: \@<<+\@<<\n" . + "Vit: \@<<+\@<< #\@< Hit: \@<< Flee: \@<<+\@<<\n" . + "Int: \@<<+\@<< #\@< Critical: \@<< Aspd: \@<<\n" . + "Dex: \@<<+\@<< #\@< Status Points: \@<<<\n" . + "Luk: \@<<+\@<< #\@< Guild: \@<<<<<<<<<<<<<<<<<<<<<<<\n\n" . + "Hair color: \@<<<<<<<<<<<<<<<<<\n" . + "Walk speed: %.2f secs per block", $char->{walk_speed}), + [$char->{'str'}, $char->{'str_bonus'}, $char->{'points_str'}, $char->{'attack'}, $char->{'attack_bonus'}, $char->{'def'}, $char->{'def_bonus'}, + $char->{'agi'}, $char->{'agi_bonus'}, $char->{'points_agi'}, $char->{'attack_magic_min'}, '~', $char->{'attack_magic_max'}, $char->{'def_magic'}, $char->{'def_magic_bonus'}, + $char->{'vit'}, $char->{'vit_bonus'}, $char->{'points_vit'}, $char->{'hit'}, $char->{'flee'}, $char->{'flee_bonus'}, + $char->{'int'}, $char->{'int_bonus'}, $char->{'points_int'}, $char->{'critical'}, $char->{'attack_speed'}, + $char->{'dex'}, $char->{'dex_bonus'}, $char->{'points_dex'}, $char->{'points_free'}, + $char->{'luk'}, $char->{'luk_bonus'}, $char->{'points_luk'}, $guildName, + $haircolors{$char->hairColor()} . " (" . $char->hairColor() . ")"]); + if (exists $char->{need_pow}) { + $msg .= center("", 44, '-') ."\n"; + $msg .= center(" ". T("Trait Stats") ." ", 44, '-') ."\n". + swrite(TF( + "Pow: \@<<< #\@<< P.Atk: \@<<< Res: \@<<<\n" . + "Sta: \@<<< #\@<< S.Matk: \@<<< Mres: \@<<<\n" . + "Wis: \@<<< #\@<< H.Plus: \@<<<\n" . + "Spl: \@<<< #\@<< C.Rate: \@<<<\n" . + "Con: \@<<< #\@<< T.Status Points: \@<<<\n" . + "Crt: \@<<< #\@<<" ), + [$char->{'pow'} ? $char->{'pow'} : 0, $char->{'need_pow'}, $char->{'patk'}, $char->{'res'}, + $char->{'sta'} ? $char->{'sta'} : 0, $char->{'need_sta'}, $char->{'smatk'}, $char->{'mres'}, + $char->{'wis'} ? $char->{'wis'} : 0, $char->{'need_wis'}, $char->{'hplus'}, + $char->{'spl'} ? $char->{'spl'} : 0, $char->{'need_spl'}, $char->{'crate'}, + $char->{'con'} ? $char->{'con'} : 0, $char->{'need_con'}, $char->{'traitpoint'}, + $char->{'crt'} ? $char->{'crt'} : 0, $char->{'need_crt'}]); + } + + $msg .= T("You are sitting.\n") if $char->{sitting}; + $msg .= ('-'x44) . "\n"; + message $msg, "info"; + } +} + +sub cmdStatus { + # Display character status + my ($baseEXPKill, $jobEXPKill); + + if (!$char) { + error T("Character status information not yet available.\n"); + return; + } + + if ($char->{'exp_last'} > $char->{'exp'}) { + $baseEXPKill = $char->{'exp_max_last'} - $char->{'exp_last'} + $char->{'exp'}; + } elsif ($char->{'exp_last'} == 0 && $char->{'exp_max_last'} == 0) { + $baseEXPKill = 0; + } else { + $baseEXPKill = $char->{'exp'} - $char->{'exp_last'}; + } + if ($char->{'exp_job_last'} > $char->{'exp_job'}) { + $jobEXPKill = $char->{'exp_job_max_last'} - $char->{'exp_job_last'} + $char->{'exp_job'}; + } elsif ($char->{'exp_job_last'} == 0 && $char->{'exp_job_max_last'} == 0) { + $jobEXPKill = 0; + } else { + $jobEXPKill = $char->{'exp_job'} - $char->{'exp_job_last'}; + } + + + my ($hp_string, $sp_string, $ap_string, $base_string, $job_string, $weight_string, $job_name_string, $zeny_string); + + $hp_string = $char->{'hp'}."/".$char->{'hp_max'}." (" + .int($char->{'hp'}/$char->{'hp_max'} * 100) + ."%)" if $char->{'hp_max'}; + $sp_string = $char->{'sp'}."/".$char->{'sp_max'}." (" + .int($char->{'sp'}/$char->{'sp_max'} * 100) + ."%)" if $char->{'sp_max'}; + $ap_string = $char->{'ap'}."/".$char->{'ap_max'}." (" + .int($char->{'ap'}/$char->{'ap_max'} * 100) + ."%)" if $char->{'ap_max'}; + $base_string = formatNumber($char->{'exp'})."/".formatNumber($char->{'exp_max'})." /$baseEXPKill (" + .sprintf("%.2f",$char->{'exp'}/$char->{'exp_max'} * 100) + ."%)" + if $char->{'exp_max'}; + $job_string = formatNumber($char->{'exp_job'})."/".formatNumber($char->{'exp_job_max'})." /$jobEXPKill (" + .sprintf("%.2f",$char->{'exp_job'}/$char->{'exp_job_max'} * 100) + ."%)" + if $char->{'exp_job_max'}; + $weight_string = $char->{'weight'}."/".$char->{'weight_max'} . + " (" . sprintf("%.1f", $char->{'weight'}/$char->{'weight_max'} * 100) + . "%)" + if $char->{'weight_max'}; + $job_name_string = "$jobs_lut{$char->{'jobID'}} ($sex_lut{$char->{'sex'}})"; + $zeny_string = formatNumber($char->{'zeny'}) if (defined($char->{'zeny'})); + + my $dmgpsec_string = sprintf("%.2f", $dmgpsec); + my $totalelasped_string = sprintf("%.2f", $totalelasped); + my $elasped_string = sprintf("%.2f", $elasped); + + my $msg = center(T(" Status "), 56, '-') ."\n" . + swrite( + TF("\@<<<<<<<<<<<<<<<<<<<<<<< HP: \@>>>>>>>>>>>>>>>>>>\n" . + "\@<<<<<<<<<<<<<<<<<<<<<<< SP: \@>>>>>>>>>>>>>>>>>>\n" . + " AP: \@>>>>>>>>>>>>>>>>>>\n" . + "Base: \@<< \@>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n" . + "Job : \@<< \@>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n" . + "Zeny: \@<<<<<<<<<<<<<<<<< Weight: \@>>>>>>>>>>>>>>>>>>\n" . + "Spirits/Coins/Amulets: %s\n\n" . + "Total Damage: \@<<<<<<<<<<<<< Dmg/sec: \@<<<<<<<<<<<<<<\n" . + "Total Time spent (sec): \@>>>>>>>>\n" . + "Last Monster took (sec): \@>>>>>>>", + (exists $char->{spirits} && $char->{spirits} != 0 ? ($char->{amuletType} ? $char->{spirits} . "\tType: " . $char->{amuletType} : $char->{spirits}) : 0)), + [$char->{'name'}, $hp_string, $job_name_string, $sp_string, $ap_string, + $char->{'lv'}, $base_string, $char->{'lv_job'}, $job_string, $zeny_string, $weight_string, + $totaldmg, $dmgpsec_string, $totalelasped_string, $elasped_string]). + ('-'x56) . "\n"; + $msg .= TF("Statuses: %s\n", $char->statusesStringAndTime(1)); + + message $msg, "info"; +} + +sub cmdStorage { + if ($char->storage->wasOpenedThisSession()) { + my (undef, $args) = @_; + + my ($switch, $items) = split(' ', $args, 2); + if (!$switch || $switch eq 'eq' || $switch eq 'u' || $switch eq 'nu') { + cmdStorage_list($switch); + } elsif ($switch eq 'log') { + cmdStorage_log(); + } elsif ($switch eq 'desc') { + cmdStorage_desc($items); + } elsif (($switch =~ /^(add|addfromcart|get|gettocart)$/ && ($items || $args =~ /$switch 0/)) || $switch eq 'close') { + if ($char->storage->isReady()) { + my ( $name, $amount ); + if ( $items =~ /^[^"'].* .+$/ ) { + # Backwards compatibility: "storage add Empty Bottle 1" still works. + ( $name, $amount ) = $items =~ /^(.*?)(?: (\d+))?$/; + } else { + ( $name, $amount ) = parseArgs( $items ); + } + if ($switch eq 'add') { + cmdStorage_add($name, $amount); + } elsif ($switch eq 'addfromcart') { + cmdStorage_addfromcart($name, $amount); + } elsif ($switch eq 'get') { + cmdStorage_get($name, $amount); + } elsif ($switch eq 'gettocart') { + cmdStorage_gettocart($name, $amount); + } elsif ($switch eq 'close') { + cmdStorage_close(); + } + } else { + error T("Cannot get/add/close storage because storage is not opened\n"); + } + } else { + error T("Syntax Error in function 'storage' (Storage Functions)\n" . + "Usage: storage [<eq|u|nu>]\n" . + " storage close\n" . + " storage add <inventory_item> [<amount>]\n" . + " storage addfromcart <cart_item> [<amount>]\n" . + " storage get <storage_item> [<amount>]\n" . + " storage gettocart <storage_item> [<amount>]\n" . + " storage desc <storage_item_#>\n". + " storage log\n"); + } + } else { + error T("No information about storage; it has not been opened before in this session\n"); + } +} + +sub cmdStorage_add { + my ($name, $amount) = @_; + + my @items = $char->inventory->getMultiple( $name ); + if ( !@items ) { + error TF( "Inventory item '%s' does not exist.\n", $name ); + return; + } + + transferItems( \@items, $amount, 'inventory' => 'storage' ); +} + +sub cmdStorage_addfromcart { + my ($name, $amount) = @_; + + if (!$char->cart->isReady) { + error T("Error in function 'storage_gettocart' (Cart Management)\nYou do not have a cart.\n"); + return; + } + + my @items = $char->cart->getMultiple( $name ); + if ( !@items ) { + error TF( "Cart item '%s' does not exist.\n", $name ); + return; + } + + transferItems( \@items, $amount, 'cart' => 'storage' ); +} + +sub cmdStorage_get { + my ($name, $amount) = @_; + + my @items = $char->storage->getMultiple( $name ); + if ( !@items ) { + error TF( "Storage item '%s' does not exist.\n", $name ); + return; + } + + transferItems( \@items, $amount, 'storage' => 'inventory' ); +} + +sub cmdStorage_gettocart { + my ($name, $amount) = @_; + + if ( !$char->cart->isReady ) { + error T( "Error in function 'storage_gettocart' (Cart Management)\nYou do not have a cart.\n" ); + return; + } + + my @items = $char->storage->getMultiple( $name ); + if ( !@items ) { + error TF( "Storage item '%s' does not exist.\n", $name ); + return; + } + + transferItems( \@items, $amount, 'storage' => 'cart' ); +} + +sub cmdStorage_close { + $messageSender->sendStorageClose(); +} + +sub cmdStorage_log { + writeStorageLog(1); +} + +sub cmdStorage_desc { + my $items = shift; + my $item = Match::storageItem($items); + if (!$item) { + error TF("Error in function 'storage desc' (Show Storage Item Description)\n" . + "Storage Item %s does not exist.\n", $items); + } else { + printItemDesc($item); + } +} + +sub cmdStore { + my (undef, $args) = @_; + my ($arg1) = $args =~ /^(\w+)/; + my ($arg2) = $args =~ /^\w+ (\d+)/; + + if ($arg1 eq "" && $ai_v{'npc_talk'}{'talk'} ne 'buy_or_sell') { + my $msg = center(TF(" Store List (%s) ", $storeList->{npcName}), 68, '-') ."\n". + T("# Name Type Price Amount\n"); + foreach my $item (@$storeList) { + $msg .= swrite( + "@< @<<<<<<<<<<<<<<<<<<<<<< @<<<<<<<<<<<<<<<<<< @>>>>>>>>>z @<<<<<", + [$item->{binID}, $item->{name}, $itemTypes_lut{$item->{type}}, formatNumber($item->{price}), $item->{amount}]); + } + $msg .= "Store list is empty.\n" if !$storeList->size; + $msg .= ('-'x68) . "\n"; + message $msg, "list"; + + } elsif ($arg1 eq "" && defined $ai_v{'npc_talk'} && exists $ai_v{'npc_talk'}{'talk'} && $ai_v{'npc_talk'}{'talk'} eq 'buy_or_sell' + && ($net && $net->getState() == Network::IN_GAME)) { + $messageSender->sendNPCBuySellList($talk{'ID'}, 0); + + } elsif ($arg1 eq "desc" && $arg2 =~ /\d+/ && !$storeList->get($arg2)) { + error TF("Error in function 'store desc' (Store Item Description)\n" . + "Store item %s does not exist\n", $arg2); + } elsif ($arg1 eq "desc" && $arg2 =~ /\d+/) { + printItemDesc($storeList->get($arg2)); + + } else { + error T("Syntax Error in function 'store' (Store Functions)\n" . + "Usage: store [<desc>] [<store item #>]\n"); + } +} + +sub cmdSwitchConf { + my (undef, $filename) = @_; + if (!defined $filename) { + error T("Syntax Error in function 'switchconf' (Switch Configuration File)\n" . + "Usage: switchconf <filename>\n"); + } elsif (! -f $filename) { + error TF("Syntax Error in function 'switchconf' (Switch Configuration File)\n" . + "File %s does not exist.\n", $filename); + } else { + switchConfigFile($filename); + message TF("Switched config file to \"%s\".\n", $filename), "system"; + } +} + +sub cmdSwitchEquips { + if (!$net || $net->getState() != Network::IN_GAME) { + error TF("You must be logged in the game to use this command '%s'\n", shift); + return; + } + + $messageSender->sendEquipSwitchRun(); +} + +sub cmdTake { + my (undef, $arg1) = @_; + if ($arg1 eq "") { + error T("Syntax Error in function 'take' (Take Item)\n" . + "Usage: take <item #>\n"); + } elsif ($arg1 eq "first" && scalar(keys(%items)) == 0) { + error T("Error in function 'take first' (Take Item)\n" . + "There are no items near.\n"); + } elsif ($arg1 eq "first") { + my @keys = keys %items; + AI::take($keys[0]); + } elsif (!$itemsID[$arg1]) { + error TF("Error in function 'take' (Take Item)\n" . + "Item %s does not exist.\n", $arg1); + } else { + main::take($itemsID[$arg1]); + } +} + +sub cmdTalk { + if (!$net || $net->getState() != Network::IN_GAME) { + error TF("You must be logged in the game to use this command '%s'\n", shift); + return; + } + my (undef, $args) = @_; + + if ($args =~ /^resp$/) { + if (!$talk{'responses'}) { + error T("Error in function 'talk resp' (Respond to NPC)\n" . + "No NPC response list available.\n"); + return; + + } else { + my $msg = center(T(" Responses (").getNPCName($talk{ID}).") ", 40, '-') ."\n" . + TF("# Response\n"); + for (my $i = 0; $i < @{$talk{'responses'}}; $i++) { + $msg .= swrite( + "@< @*", + [$i, $talk{responses}[$i]]); + } + $msg .= ('-'x40) . "\n"; + message $msg, "list"; + return; + } + } + + my @steps = split(/\s*,\s*/, $args); + + if (!@steps) { + error T("Syntax Error in function 'talk' (Talk to NPC)\n" . + "Usage: talk <NPC # | \"NPC name\" | cont | resp | num | text > [<response #>|<number #>]\n"); + return; + } + + my $steps_string = ""; + my $nameID; + foreach my $index (0..$#steps) { + my $step = $steps[$index]; + my $type; + my $arg; + if ($step =~ /^(cont|text|num|resp|\d+|"[^"]+")\s+(\S.*)$/) { + $type = $1; + $arg = $2; + } else { + $type = $step; + } + + my $current_step; + + if ($type =~ /^\d+|"([^"]+)"$/) { + $type = $1 if $1; + if (AI::is("NPC")) { + error "Error in function 'talk' (Talk to NPC)\n" . + "You are already talking with an npc\n"; + return; + + } elsif ($index != 0) { + error "Error in function 'talk' (Talk to NPC)\n" . + "You cannot start a conversation during one\n"; + return; + + } else { + my $npc; + if ($type =~ /^\d+/) { + $npc = $npcsList->get($type); + } else { + $npc = $npcsList->getByName($type); + } + if ($npc) { + $nameID = $npc->{nameID}; + } else { + error "Error in function 'talk' (Talk to NPC)\n" . + "Given npc not found\n"; + return; + } + } + + } elsif (!AI::is("NPC") && !defined $nameID) { + error "Error in function 'talk' (Talk to NPC)\n" . + "You are not talkning to an npc\n"; + return; + + } elsif ($type eq "resp") { + if ($arg =~ /^(\/(.*?)\/(\w?))$/) { + $current_step = 'r~'.$1; + + } elsif ($arg =~ /^\d+$/) { + $current_step = 'r'.$arg; + + } elsif (!$arg) { + error T("Error in function 'talk resp' (Respond to NPC)\n" . + "You must specify a response.\n"); + return; + + } else { + error T("Error in function 'talk resp' (Respond to NPC)\n" . + "Wrong talk resp sintax.\n"); + return; + } + + } elsif ($type eq "num") { + if ($arg eq "") { + error T("Error in function 'talk num' (Respond to NPC)\n" . + "You must specify a number.\n"); + return; + + } elsif ($arg !~ /^-?\d+$/) { + error TF("Error in function 'talk num' (Respond to NPC)\n" . + "%s is not a valid number.\n", $arg); + return; + + } elsif ($arg =~ /^-?\d+$/) { + $current_step = 'd'.$arg; + } + + } elsif ($type eq "text") { + if ($args eq "") { + error T("Error in function 'talk text' (Respond to NPC)\n" . + "You must specify a string.\n"); + return; + + } else { + $current_step = 't='.$arg; + } + + } elsif ($type eq "cont") { + $current_step = 'c'; + + } elsif ($type eq "no") { + $current_step = 'n'; + } + + if (defined $current_step) { + $steps_string .= $current_step; + + } elsif (!(defined $nameID && $index == 0)) { + error T("Syntax Error in function 'talk' (Talk to NPC)\n" . + "Usage: talk <NPC # | \"NPC name\" | cont | resp | num | text > [<response #>|<number #>]\n"); + return; + } + + last if ($index == $#steps); + + } continue { + $steps_string .= " " unless (defined $nameID && $index == 0); + } + if (defined $nameID) { + AI::clear("route"); + AI::queue("NPC", new Task::TalkNPC(type => 'talk', nameID => $nameID, sequence => $steps_string)); + } else { + my $task = $char->args; + $task->addSteps($steps_string); + } +} + +sub cmdTalkNPC { + my (undef, $args) = @_; + + my ($x, $y, $sequence) = $args =~ /^(\d+) (\d+)(?: (.+))?$/; + unless (defined $x) { + error T("Syntax Error in function 'talknpc' (Talk to an NPC)\n" . + "Usage: talknpc <x> <y> <sequence>\n"); + return; + } + + message TF("Talking to NPC at (%d, %d) using sequence: %s\n", $x, $y, $sequence); + main::ai_talkNPC($x, $y, $sequence); +} + +sub cmdTank { + my (undef, $arg) = @_; + $arg =~ s/ .*//; + + if ($arg eq "") { + error T("Syntax Error in function 'tank' (Tank for a Player/Slave)\n" . + "Usage: tank <player #|player name|\@homunculus|\@mercenary>\n"); + + } elsif ($arg eq "stop") { + configModify("tankMode", 0); + + } elsif ($arg =~ /^\d+$/) { + if (!$playersID[$arg]) { + error TF("Error in function 'tank' (Tank for a Player)\n" . + "Player %s does not exist.\n", $arg); + } else { + configModify("tankMode", 1); + configModify("tankModeTarget", $players{$playersID[$arg]}{name}); + } + + } else { + my $name; + for (@$playersList, @$slavesList) { + if (lc $_->{name} eq lc $arg) { + $name = $_->{name}; + last; + } elsif($char->{homunculus} && $_->{ID} eq $char->{homunculus}{ID} && $arg eq '@homunculus' || + $char->{mercenary} && $_->{ID} eq $char->{mercenary}{ID} && $arg eq '@mercenary') { + $name = $arg; + last; + } + } + + if ($name) { + configModify("tankMode", 1); + configModify("tankModeTarget", $name); + } else { + error TF("Error in function 'tank' (Tank for a Player/Slave)\n" . + "Player/Slave %s does not exist.\n", $arg); + } + } +} + +sub cmdTeleport { + if (!$net || $net->getState() != Network::IN_GAME) { + error TF("You must be logged in the game to use this command '%s'\n", shift); + return; + } + my (undef, $args) = @_; + my ($arg1) = $args =~ /^(\d)/; + $arg1 = 1 unless $arg1; + ai_useTeleport($arg1); +} + +sub cmdTestShop { + my @items = main::makeShop(); + return unless @items; + my @shopnames = split(/;;/, $shop{title_line}); + $shop{title} = $shopnames[int rand($#shopnames + 1)]; + $shop{title} = ($config{shopTitleOversize}) ? $shop{title} : substr($shop{title},0,36); + + my $msg = center(" $shop{title} ", 69, '-') ."\n". + T("Name Price Amount\n"); + for my $item (@items) { + $msg .= swrite("@<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< @>>>>>>>>>>>>z @<<<<<", + [$item->{name}, formatNumber($item->{price}), $item->{amount}]); + } + $msg .= "\n" . TF("Total of %d items to sell.\n", binSize(\@items)) . + ('-'x69) . "\n"; + message $msg, "list"; +} + +sub cmdTimeout { + my (undef, $args) = @_; + my ($arg1) = $args =~ /^(\w+)/; + my ($arg2) = $args =~ /^\w+\s+([\s\S]+)\s*$/; + if ($arg1 eq "") { + error T("Syntax Error in function 'timeout' (set a timeout)\n" . + "Usage: timeout <type> [<seconds>]\n"); + } elsif ($timeout{$arg1} eq "") { + error TF("Error in function 'timeout' (set a timeout)\n" . + "Timeout %s doesn't exist\n", $arg1); + } elsif ($arg2 eq "") { + message TF("Timeout '%s' is %s\n", + $arg1, $timeout{$arg1}{timeout}), "info"; + } else { + setTimeout($arg1, $arg2); + } +} + +sub cmdTop10 { + if (!$net || $net->getState() != Network::IN_GAME) { + error TF("You must be logged in the game to use this command '%s'\n", shift); + return; + } + my (undef, $args) = @_; + my ($arg1) = $args; + + if ($arg1 eq "") { + message T("Function 'top10' (Show Top 10 Lists)\n" . + "Usage: top10 <b|a|t|p> | <black|alche|tk|pk> | <blacksmith|alchemist|taekwon|pvp>\n"); + } elsif ($arg1 eq "a" || $arg1 eq "alche" || $arg1 eq "alchemist") { + $messageSender->sendTop10Alchemist(); + } elsif ($arg1 eq "b" || $arg1 eq "black" || $arg1 eq "blacksmith") { + $messageSender->sendTop10Blacksmith(); + } elsif ($arg1 eq "p" || $arg1 eq "pk" || $arg1 eq "pvp") { + $messageSender->sendTop10PK(); + } elsif ($arg1 eq "t" || $arg1 eq "tk" || $arg1 eq "taekwon") { + $messageSender->sendTop10Taekwon(); + } else { + error T("Syntax Error in function 'top10' (Show Top 10 Lists)\n" . + "Usage: top10 <b|a|t|p> |\n" . + " <black|alche|tk|pk> |\n". + " <blacksmith|alchemist|taekwon|pvp>\n"); + } +} + +sub cmdUnequip { + + # unequip an item + my (undef, $args) = @_; + my ($arg1,$arg2) = $args =~ /^(\S+)\s*(.*)/; + my $slot; + my $item; + + if ($arg1 eq "") { + cmdEquip_list(); + return; + } + + if ($arg1 eq "slots") { + # Translation Comment: List of equiped items on each slot + message T("Slots:\n") . join("\n", @Actor::Item::slots). "\n", "list"; + return; + } + + if (!$net || $net->getState() != Network::IN_GAME) { + error TF("You must be logged in the game to use this command '%s'\n", 'eq ' .$args); + return; + } + + if ($arg1 eq "all") { + my @equipment; + # Find all equipped items + for my $item (@{$char->inventory}) { + if ($item->equippable && $item->{type_equip} != 0) { + my %eqp; + $eqp{index} = $item->{ID}; + $eqp{binID} = $item->{binID}; + $eqp{name} = $item->{name}; + $eqp{amount} = $item->{amount}; + $eqp{equipped} = ($item->{type} == 10 || $item->{type} == 16 || $item->{type} == 17 || $item->{type} == 19) ? $item->{amount} . " left" : $equipTypes_lut{$item->{equipped}}; + $eqp{type} = $itemTypes_lut{$item->{type}}; + $eqp{equipped} .= " ($item->{equipped})"; + # Translation Comment: Mark to tell item not identified + $eqp{identified} = " -- " . T("Not Identified") if !$item->{identified}; + if ($item->{equipped}) { + push @equipment, \%eqp; + } + } + } + for my $e (@equipment) { + my $item = Actor::Item::get($e->{name}, undef, 0); + $item->unequip(); + } + return; + } + + if ($equipSlot_rlut{$arg1}) { + $slot = $arg1; + } else { + $arg1 .= " $arg2" if $arg2; + } + + $item = Actor::Item::get(defined $slot ? $arg2 : $arg1, undef, 0); + + if (!$item) { + $args =~ s/^($slot)\s//g if ($slot); + $slot = T("undefined") unless ($slot); + error TF("No such equipped Inventory Item: %s in slot: %s\n", $args, $slot); + return; + } + + if (!$item->{type_equip} && $item->{type} != 10 && $item->{type} != 16 && $item->{type} != 17) { + error TF("Inventory Item %s (%s) can't be unequipped.\n", + $item->{name}, $item->{binID}); + return; + } + if ($slot) { + $item->unequipFromSlot($slot); + } else { + $item->unequip(); + } +} + +sub cmdUnequipSwitch { + + # unequip an item + my (undef, $args) = @_; + my ($arg1,$arg2) = $args =~ /^(\S+)\s*(.*)/; + my $slot; + my $item; + + if ($arg1 eq "") { + cmdEquipsw_list(); + return; + } + + if ($arg1 eq "slots") { + # Translation Comment: List of equiped items on each slot + message T("Slots:\n") . join("\n", @Actor::Item::slots). "\n", "list"; + return; + } + + if (!$net || $net->getState() != Network::IN_GAME) { + error TF("You must be logged in the game to use this command '%s'\n", 'eq ' .$args); + return; + } + + if ($equipSlot_rlut{$arg1}) { + $slot = $arg1; + } else { + $arg1 .= " $arg2" if $arg2; + } + + $item = Actor::Item::get(defined $slot ? $arg2 : $arg1, undef, 0); + + if (!$item) { + $args =~ s/^($slot)\s//g if ($slot); + $slot = T("undefined") unless ($slot); + error TF("No such equipped Inventory Item: %s in slot: %s\n", $args, $slot); + return; + } + + if (!$item->{type_equip} && $item->{type} != 10 && $item->{type} != 16 && $item->{type} != 17) { + error TF("Inventory Item %s (%s) can't be unequipped.\n", + $item->{name}, $item->{binID}); + return; + } + + $item->unequip_switch(); +} + +sub cmdUseItemOnMonster { + if (!$net || $net->getState() != Network::IN_GAME) { + error TF("You must be logged in the game to use this command '%s'\n", shift); + return; + } + my (undef, $args) = @_; + my ($arg1) = $args =~ /^(\d+)/; + my ($arg2) = $args =~ /^\d+ (\d+)/; + + if ($arg1 eq "" || $arg2 eq "") { + error T("Syntax Error in function 'im' (Use Item on Monster)\n" . + "Usage: im <item #> <monster #>\n"); + } elsif (!$char->inventory->get($arg1)) { + error TF("Error in function 'im' (Use Item on Monster)\n" . + "Inventory Item %s does not exist.\n", $arg1); + } elsif ($monstersID[$arg2] eq "") { + error TF("Error in function 'im' (Use Item on Monster)\n" . + "Monster %s does not exist.\n", $arg2); + } else { + $char->inventory->get($arg1)->use($monstersID[$arg2]); + } +} + +sub cmdUseItemOnPlayer { + if (!$net || $net->getState() != Network::IN_GAME) { + error TF("You must be logged in the game to use this command '%s'\n", shift); + return; + } + my (undef, $args) = @_; + my ($arg1) = $args =~ /^(\d+)/; + my ($arg2) = $args =~ /^\d+ (\d+)/; + if ($arg1 eq "" || $arg2 eq "") { + error T("Syntax Error in function 'ip' (Use Item on Player)\n" . + "Usage: ip <item #> <player #>\n"); + } elsif (!$char->inventory->get($arg1)) { + error TF("Error in function 'ip' (Use Item on Player)\n" . + "Inventory Item %s does not exist.\n", $arg1); + } elsif ($playersID[$arg2] eq "") { + error TF("Error in function 'ip' (Use Item on Player)\n" . + "Player %s does not exist.\n", $arg2); + } else { + $char->inventory->get($arg1)->use($playersID[$arg2]); + } +} + +sub cmdUseItemOnSelf { + if (!$net || $net->getState() != Network::IN_GAME) { + error TF("You must be logged in the game to use this command '%s'\n", shift); + return; + } + my (undef, $args) = @_; + if ($args eq "") { + error T("Syntax Error in function 'is' (Use Item on Yourself)\n" . + "Usage: is <item>\n"); + return; + } + my $item = Actor::Item::get($args); + if (!$item) { + error TF("Error in function 'is' (Use Item on Yourself)\n" . + "Inventory Item %s does not exist.\n", $args); + return; + } + $item->use; +} + +sub cmdUseSkill { + if (!$net || $net->getState() != Network::IN_GAME) { + error TF("You must be logged in the game to use this command '%s'\n", shift); + return; + } + my ($cmd, $args_string) = @_; + my ($target, $actorList, $skill, $level) = @_; + my @args = parseArgs($args_string); + my $isStartUseSkill = 0; + + if ($cmd eq 'sl') { + my ($x, $y); + + if (scalar @args < 3) { + $x = $char->position->{x}; + $y = $char->position->{y}; + $level = $args[1]; + } else { + $x = $args[1]; + $y = $args[2]; + $level = $args[3]; + } + + if (@args < 1 || @args > 4) { + error T("Syntax error in function 'sl' (Use Skill on Location)\n" . + "Usage: sl <skill #> [<x> <y>] [level]\n"); + return; + } elsif ($x !~ /^\d+$/ || $y !~ /^\d+/) { + error T("Error in function 'sl' (Use Skill on Location)\n" . + "Invalid coordinates given.\n"); + return; + } else { + $target = { x => $x, y => $y }; + } + + } elsif ($cmd eq 'ss') { + if (defined $args[0] && $args[0] eq 'start') { + if (@args < 2 || @args > 3) { + error T("Syntax error in function 'ss start' (Start Use Skill on Self)\n" . + "Usage: ss start <skill #> [level]\n"); + return; + } + $isStartUseSkill = 1; + $target = $char; + $level = $args[2]; + } elsif (defined $args[0] && $args[0] eq 'stop') { + if (!$char->{last_skill_used_is_continuous}) { + error T("Skill Stop failed (continuous skills not detected)\n"); + return; + } + message T("Sending Skill Stop\n"), "skill"; + $messageSender->sendStopSkillUse($char->{last_continuous_skill_used}); + return; + } elsif (@args < 1 || @args > 2) { + error T("Syntax error in function 'ss' (Use Skill on Self)\n" . + "Usage: ss <skill #> [level]\n"); + return; + } else { + $target = $char; + $level = $args[1]; + } + + } elsif ($cmd eq 'sp') { + if (@args < 2 || @args > 3) { + error T("Syntax error in function 'sp' (Use Skill on Player)\n" . + "Usage: sp <skill #> <player #> [level]\n"); + return; + } else { + $target = Match::player($args[1], 1); + if (!$target) { + error TF("Error in function 'sp' (Use Skill on Player)\n" . + "Player '%s' does not exist.\n", $args[1]); + return; + } + $actorList = $playersList; + $level = $args[2]; + } + + } elsif ($cmd eq 'sm') { + if (@args < 2 || @args > 3) { + error T("Syntax error in function 'sm' (Use Skill on Monster)\n" . + "Usage: sm <skill #> <monster #> [level]\n"); + return; + } else { + $target = $monstersList->get($args[1]); + if (!$target) { + error TF("Error in function 'sm' (Use Skill on Monster)\n" . + "Monster %d does not exist.\n", $args[1]); + return; + } + $actorList = $monstersList; + $level = $args[2]; + } + + } elsif ($cmd eq 'ssl') { + if (@args < 2 || @args > 3) { + error T("Syntax error in function 'ssl' (Use Skill on Slave)\n" . + "Usage: ssl <skill #> <slave #> [level]\n"); + return; + } else { + $target = $slavesList->get($args[1]); + if (!$target) { + error TF("Error in function 'ssl' (Use Skill on Slave)\n" . + "Slave %d does not exist.\n", $args[1]); + return; + } + $actorList = $slavesList; + $level = $args[2]; + } + + } elsif ($cmd eq 'ssp') { + if (@args < 2 || @args > 3) { + error T("Syntax error in function 'ssp' (Use Skill on Area Spell Location)\n" . + "Usage: ssp <skill #> <spell #> [level]\n"); + return; + } + my $targetID = $spellsID[$args[1]]; + if (!$targetID) { + error TF("Spell %d does not exist.\n", $args[1]); + return; + } + my $pos = $spells{$targetID}{pos_to}; + $target = { %{$pos} }; + } + + my $skill_arg = $isStartUseSkill ? $args[1] : $args[0]; + $skill = new Skill(auto => $skill_arg, level => $level); + + if ($char->{skills}{$skill->getHandle()}{lv} == 0) { + error TF("Skill '%s' cannot be used because you have no such skill.\n", $skill->getName()); + return; + } elsif ($char->{skills}{$skill->getHandle()}{lv} < $level) { + error TF("You are trying to use the skill '%s' level %d, but only level %d is available to you.\n", $skill->getName(), $level, $char->{skills}{$skill->getHandle()}{lv}); + return; + } + + require Task::UseSkill; + my $skillTask = new Task::UseSkill( + actor => $skill->getOwner, + target => $target, + actorList => $actorList, + skill => $skill, + isStartUseSkill => $isStartUseSkill, + priority => Task::USER_PRIORITY + ); + my $task = new Task::ErrorReport(task => $skillTask); + $taskManager->add($task); +} + +sub cmdVender { + if (!$net || $net->getState() != Network::IN_GAME) { + error TF("You must be logged in the game to use this command '%s'\n", shift); + return; + } + my (undef, $args) = @_; + + if ($args eq "end") { + $venderItemList->clear; + undef $venderID; + undef $venderCID; + return; + } + + my ($arg1) = $args =~ /^(\d+)/; + my ($arg2) = $args =~ /^\d+ (\d+)/; + my ($arg3) = $args =~ /^\d+ \d+ (\d+)/; + if ($arg1 eq "") { + error T("Syntax error in function 'vender' (Vender Shop)\n" . + "Usage: vender <vender # | end> [<vender_item #> <amount>]\n"); + } elsif ($venderListsID[$arg1] eq "") { + error TF("Error in function 'vender' (Vender Shop)\n" . + "Vender %d does not exist.\n", $arg1); + } elsif ($arg2 eq "") { + $messageSender->sendEnteringVender($venderListsID[$arg1]); + } elsif ($venderListsID[$arg1] ne $venderID) { + error T("Error in function 'vender' (Vender Shop)\n" . + "Vender ID is wrong.\n"); + } elsif (!$venderItemList->get( $arg2 )) { + error TF("Error in function 'vender' (Vender Shop)\n" . + "Item %d does not exist.\n", $arg2); + } else { + $arg3 = 1 if $arg3 <= 0; + my $item = $venderItemList->get( $arg2 ); + $messageSender->sendBuyBulkVender( $venderID, [ { itemIndex => $item->{ID}, amount => $arg3 } ], $venderCID ); + } +} + +sub cmdVenderList { + my $msg = center(T(" Vender List "), 75, '-') ."\n". + T("# Title Coords Owner\n"); + for (my $i = 0; $i < @venderListsID; $i++) { + next if ($venderListsID[$i] eq ""); + my $player = Actor::get($venderListsID[$i]); + # autovivifies $obj->{pos_to} but it doesnt matter + $msg .= sprintf( + "%-3d %-36s (%3s, %3s) %-20s\n", + $i, $venderLists{$venderListsID[$i]}{'title'}, + $player->{pos_to}{x} || '?', $player->{pos_to}{y} || '?', $player->name); + } + $msg .= ('-'x75) . "\n"; + message $msg, "list"; +} + +sub cmdBuyerList { + my $msg = center(T(" Buyer List "), 75, '-') ."\n". + T("# Title Coords Owner\n"); + for (my $i = 0; $i < @buyerListsID; $i++) { + next if ($buyerListsID[$i] eq ""); + my $player = Actor::get($buyerListsID[$i]); + # autovivifies $obj->{pos_to} but it doesnt matter + $msg .= sprintf( + "%-3d %-36s (%3s, %3s) %-20s\n", + $i, $buyerLists{$buyerListsID[$i]}{'title'}, + $player->{pos_to}{x} || '?', $player->{pos_to}{y} || '?', $player->name); + } + $msg .= ('-'x75) . "\n"; + message $msg, "list"; +} + +sub cmdBooking { + if (!$net || $net->getState() != Network::IN_GAME) { + error TF("You must be logged in the game to use this command '%s'\n", shift); + return; + } + + my (undef, $args) = @_; + my ($arg1) = $args =~ /^(\w+)/; + + if ($arg1 eq "search") { + $args =~ /^\w+\s([0-9]+)\s?([0-9]+)?\s?([0-9]+)?\s?([0-9]+)?\s?([0-9]+)?/; + # $1 -> level + # $2 -> MapID + # $3 -> job + # $4 -> ResultCount + # $5 -> LastIndex + + $messageSender->sendPartyBookingReqSearch($1, $2, $3, $4, $5); + } elsif ($arg1 eq "recruit") { + $args =~ /^\w+\s([0-9]+)\s?([0-9]+)?\s?([0-9]+)?\s?([0-9]+)?\s?([0-9]+)?\s?([0-9]+)?\s?([0-9]+)?\s?([0-9]+)?/; + # $1 -> level + # $2 -> MapID + # $3 ~ $8 -> jobs + + if (!$3) { + error T("Syntax Error in function 'booking recruit' (Booking recruit)\n" . + "Usage: booking recruit \"<level>\" \"<MapID>\" \"<job 1 ~ 6x>\"\n"); + return; + } + + # job null = 65535 + my @jobList = (65535) x 6; + $jobList[0] = $3; + $jobList[1] = $4 if ($4); + $jobList[2] = $5 if ($5); + $jobList[3] = $6 if ($6); + $jobList[4] = $7 if ($7); + $jobList[5] = $8 if ($8); + + $messageSender->sendPartyBookingRegister($1, $2, @jobList); + } elsif ($arg1 eq "update") { + $args =~ /^\w+\s([0-9]+)\s?([0-9]+)?\s?([0-9]+)?\s?([0-9]+)?\s?([0-9]+)?\s?([0-9]+)?/; + + # job null = 65535 + my @jobList = (65535) x 6; + $jobList[0] = $1; + $jobList[1] = $2 if ($2); + $jobList[2] = $3 if ($3); + $jobList[3] = $4 if ($4); + $jobList[4] = $5 if ($5); + $jobList[5] = $6 if ($6); + + $messageSender->sendPartyBookingUpdate(@jobList); + } elsif ($arg1 eq "delete") { + $messageSender->sendPartyBookingDelete(); + } else { + error T("Syntax error in function 'booking'\n" . + "Usage: booking [<search | recruit | update | delete>]\n"); + } +} + +sub cmdBuyer { + if (!$net || $net->getState() != Network::IN_GAME) { + error TF("You must be logged in the game to use this command '%s'\n", shift); + return; + } + my (undef, $args) = @_; + + my ($arg1) = $args =~ /^([\d\w]+)/; + my ($arg2) = $args =~ /^[\d\w]+ (\d+)/; + my ($arg3) = $args =~ /^[\d\w]+ \d+ (\d+)/; + if ($arg1 eq "") { + error T("Syntax error in function 'buyer' (Buyer Shop)\n" . + "Usage: buyer <buyer # | end> [<item #> <amount>]\n"); + } elsif ($arg1 eq "end") { + undef $buyerPriceLimit; + undef $buyerID; + undef $buyingStoreID; + $buyerItemList->clear; + + } elsif ($buyerListsID[$arg1] eq "") { + error TF("Error in function 'buyer' (Buyer Shop)\n" . + "buyer %s does not exist.\n", $arg1); + + } elsif ($arg2 eq "") { + undef $buyerPriceLimit; + undef $buyerID; + undef $buyingStoreID; + $buyerItemList->clear; + $messageSender->sendEnteringBuyer($buyerListsID[$arg1]); + + } elsif (!$buyerItemList->get( $arg2 )) { + error TF("Error in function 'buyer' (Buyer Shop)\n" . + "item %s does not exist.\n", $arg2); + + } elsif ($buyerListsID[$arg1] ne $buyerID) { + error T("Error in function 'buyer' (Buyer Shop)\n" . + "Buyer ID is wrong.\n"); + + } else { + if ($arg3 <= 0) { + $arg3 = 1; + } + + my $l_item = $buyerItemList->get( $arg2 ); + + if (!defined $l_item) { + error T("Error in function 'buyer', shop item not defined.\n"); + return; + } + + my $c_item = $char->inventory->getByNameID($l_item->{nameID}); + + if (!defined $c_item) { + error T("Error in function 'buyer', char item not defined.\n"); + return; + } + + my $amount = $arg3; + my $total_zeny = $amount * $l_item->{price}; + + if ($total_zeny > $buyerPriceLimit) { + error T("Error in function 'buyer', trying to sell aboce max price limit.\n"); + return; + } + + $messageSender->sendBuyBulkBuyer($buyerID, [{ID => $c_item->{ID}, itemID => $c_item->{nameID}, amount => $amount}], $buyingStoreID); +} +} + +sub cmdVersion { + message "$Settings::versionText"; +} + +sub cmdWarp { + if (!$net || $net->getState() != Network::IN_GAME) { + error TF("You must be logged in the game to use this command '%s'\n", shift); + return; + } + my (undef, $map) = @_; + + if ($map eq '') { + error T("Error in function 'warp' (Open/List Warp Portal)\n" . + "Usage: warp <map name | map number# | list | cancel>\n"); + + } elsif ($map =~ /^\d+$/) { + if (!$char->{warp}{memo} || !@{$char->{warp}{memo}}) { + error T("You didn't cast warp portal.\n"); + return; + } + + if ($map < 0 || $map > @{$char->{warp}{memo}}) { + error TF("Invalid map number %s.\n", $map); + } else { + my $name = $char->{warp}{memo}[$map]; + my $rsw = "$name.rsw"; + message TF("Attempting to open a warp portal to %s (%s)\n", + $maps_lut{$rsw}, $name), "info"; + $messageSender->sendWarpTele(27,"$name.gat"); + } + + } elsif ($map eq 'list') { + if (!$char->{warp}{memo} || !@{$char->{warp}{memo}}) { + error T("You didn't cast warp portal.\n"); + return; + } + + my $msg = center(T(" Warp Portal "), 50, '-') ."\n". + T("# Place Map\n"); + for (my $i = 0; $i < @{$char->{warp}{memo}}; $i++) { + $msg .= swrite( + "@< @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< @<<<<<<<<<<<<<<<", + [$i, $maps_lut{$char->{warp}{memo}[$i].'.rsw'}, $char->{warp}{memo}[$i]]); + } + $msg .= ('-'x50) . "\n"; + message $msg, "list"; + + } elsif ($map eq 'cancel') { + message T("Attempting to cancel the warp portal\n"), 'info'; + $messageSender->sendWarpTele(27, 'cancel'); + + } elsif (!defined $maps_lut{$map.'.rsw'}) { + error TF("Map '%s' does not exist.\n", $map); + + } else { + my $rsw = "$map.rsw"; + message TF("Attempting to open a warp portal to %s (%s)\n", + $maps_lut{$rsw}, $map), "info"; + $messageSender->sendWarpTele(27,"$map.gat"); + } +} + +sub cmdWeight { + if (!$char) { + error T("Character weight information not yet available.\n"); + return; + } + my (undef, $itemWeight) = @_; + + $itemWeight ||= 1; + + if ($itemWeight !~ /^\d+(\.\d+)?$/) { + error T("Syntax error in function 'weight' (Inventory Weight Info)\n" . + "Usage: weight [item weight]\n"); + return; + } + + my $itemString = $itemWeight == 1 ? '' : "*$itemWeight"; + message TF("Weight: %s/%s (%s\%)\n", $char->{weight}, $char->{weight_max}, sprintf("%.02f", $char->weight_percent)), "list"; + if ($char->weight_percent < 90) { + if ($char->weight_percent < 50) { + my $weight_50 = int((int($char->{weight_max}*0.5) - $char->{weight}) / $itemWeight); + message TF("You can carry %s%s before %s overweight.\n", + $weight_50, $itemString, '50%'), "list"; + } else { + message TF("You are %s overweight.\n", '50%'), "list"; + } + my $weight_90 = int((int($char->{weight_max}*0.9) - $char->{weight}) / $itemWeight); + message TF("You can carry %s%s before %s overweight.\n", + $weight_90, $itemString, '90%'), "list"; + } else { + message TF("You are %s overweight.\n", '90%'); + } +} + +sub cmdWhere { + if (!$char) { + error T("Location not yet available.\n"); + return; + } + my $pos = calcPosition($char); + message TF("Location: %s : (baseName: %s) : %d, %d\n", $field->descString(), $field->baseName(), $pos->{x}, $pos->{y}), "info"; +} + +sub cmdWho { + if (!$net || $net->getState() != Network::IN_GAME) { + error TF("You must be logged in the game to use this command '%s'\n", shift); + return; + } + $messageSender->sendWho(); +} + +sub cmdWhoAmI { + if (!$char) { + error T("Character information not yet available.\n"); + return; + } + my $GID = unpack("V1", $charID); + my $AID = unpack("V1", $accountID); + message TF("Name: %s (Level %s %s %s)\n" . + "Char ID: %s\n" . + "Acct ID: %s\n", + $char->{name}, $char->{lv}, $sex_lut{$char->{sex}}, $jobs_lut{$char->{jobID}}, + $GID, $AID), "list"; +} + +sub cmdMail { + if (!$net || $net->getState() != Network::IN_GAME) { + error TF("You must be logged in the game to use this command '%s'\n", shift); + return; + } + + my (undef, $args_string) = @_; + my @args = parseArgs($args_string, 3); + + if ($args[0] eq 'open') { + if (defined $mailList) { + error T("Your Mailbox is already opened.\n"); + } else { + message T("Sending request to open Mailbox.\n"); + $messageSender->sendMailboxOpen(); + } + + } elsif ($args[0] eq 'refresh') { + $messageSender->sendMailboxOpen(); + + } elsif ($args[0] eq 'read') { + unless ($args[1] =~ /^\d+$/) { + error T("Syntax Error in function 'mail read' (Mailbox)\n" . + "Usage: mail read <mail #>\n"); + } elsif (!defined $mailList) { + warning T("Your Mailbox is not open. Use the command 'mail open'.\n"); + } elsif (!$mailList->[$args[1]]) { + warning TF("No mail found with index: %s\n", $args[1]); + } elsif ($mailList->[$args[1]]->{mailID}) { + $messageSender->sendMailRead($mailList->[$args[1]]->{mailID}); + } + + } elsif ($args[0] eq 'get') { + unless ($args[1] =~ /^\d+$/) { + error T("Syntax Error in function 'mail get' (Mailbox)\n" . + "Usage: mail get <mail #>\n"); + } elsif (!defined $mailList) { + warning T("Your Mailbox is not open. Use the command 'mail open'.\n"); + } elsif (!$mailList->[$args[1]]) { + warning TF("No mail found with index: %s\n", $args[1]); + } elsif ($mailList->[$args[1]]->{mailID}) { + $messageSender->sendMailGetAttach($mailList->[$args[1]]->{mailID}); + } + + } elsif ($args[0] eq 'setzeny') { + if ($args[1] =~ /^\d+$/) { + $messageSender->sendMailSetAttach($args[1], undef); + } elsif ($args[1] eq 'none') { + $messageSender->sendMailOperateWindow(2); + } else { + error T("Syntax Error in function 'mail setzeny' (Mailbox)\n" . + "Usage: mail setzeny <amount|none>\n"); + } + + } elsif ($args[0] eq 'add') { + unless (defined $args[1]) { + error T("Syntax Error in function 'mail add' (Mailbox)\n" . + "Usage: mail add <item #> <amount>\n"); + } elsif ($args[1] eq 'none') { + $messageSender->sendMailOperateWindow(1); + } else { + my $item = Actor::Item::get($args[1]); + if ($item) { + my $amount = $args[2] ? $args[2] : $item->{amount}; + warning TF("Attention: Inventory Item '%s' is equipped.\n", $item->{name}) if ($item->{equipped}); + $messageSender->sendMailSetAttach($amount, $item->{ID}); + } else { + warning TF("Inventory Item '%s' does not exist.\n", $args[2]); + } + } + + } elsif ($args[0] eq 'send') { + unless ($args[1] && $args[2]) { + error T("Syntax Error in function 'mail send' (Mailbox)\n" . + "Usage: mail send <receiver> <title> <body>\n"); + } else { + $messageSender->sendMailSend($args[1], $args[2], $args[3]); + } + + # mail delete (can't delete mail without removing attachment/zeny first) + } elsif ($args[0] eq 'delete') { + unless ($args[1] =~ /^\d+$/) { + error T("Syntax Error in function 'mail delete' (Mailbox)\n" . + "Usage: mail delete <mail #>\n"); + } elsif (!$mailList->[$args[1]]) { + if (@{$mailList}) { + warning TF("No mail found with index: %d. (might need to re-open mailbox)\n", $args[1]); + } else { + warning T("Mailbox has not been opened or is empty.\n"); + } + } else { + $messageSender->sendMailDelete($mailList->[$args[1]]->{mailID}); + delete $mailList->[$args[1]]; + } + + # mail window (almost useless?) + } elsif ($args[0] eq 'write') { + $messageSender->sendMailOperateWindow(0); + + # mail return + } elsif ($args[0] eq 'return') { + unless ($args[1] =~ /^\d+$/) { + error T("Syntax Error in function 'mail retutn' (Mailbox)\n" . + "Usage: mail return <mail #>\n"); + } elsif (!$mailList->[$args[1]]) { + if (@{$mailList}) { + warning TF("No mail found with index: %s. (might need to re-open mailbox)\n", $args[1]); + } else { + warning T("Mailbox has not been opened or is empty.\n"); + } + } else { + $messageSender->sendMailReturn($mailList->[$args[1]]->{mailID}, $mailList->[$args[1]]->{sender}); + } + + } elsif ($args[0] eq 'list') { + if (!defined $mailList) { + error T("Your Mailbox is is closed.\n"); + } elsif (!$mailList) { + message T("Your Mailbox is empty.\n"); + } else { + my $msg = center(" " . T("Inbox") . " ", 86, '-') . "\n"; + # truncating the title from 39 to 34, the user will be able to read the full title when reading the mail + # truncating the date with precision of minutes and leave year out + $msg .= swrite(sprintf("\@> \@ \@%s \@%s \@%s", ('<'x34), ('<'x24), ('<'x19)), + ["#", T("R"), T("Title"), T("Sender"), T("Date")]); + $msg .= sprintf("%s\n", ('-'x86)); + my $index = 0; + foreach my $mail (@{$mailList}) { + if ($mail) { + $msg .= swrite(sprintf("\@> \@ \@%s \@%s \@%s", ('<'x34), ('<'x24), ('<'x19)), + [$index, $mail->{read}, $mail->{title}, $mail->{sender}, getFormattedDate(int($mail->{timestamp}))]); + } else { + $msg .= swrite(sprintf("\@> \@%s", ('<'x83)), [$index, T("the mail was deleted")]); + } + $index++; + } + + $msg .= sprintf("%s\n", ('-'x86)); + message $msg, "list"; + } + + } else { + error T("Syntax Error in function 'mail' (Mailbox)\n" . + "Usage: help mail\n"); + } +} + +sub cmdAuction { + if (!$net || $net->getState() != Network::IN_GAME) { + error TF("You must be logged in the game to use this command '%s'\n", shift); + return; + } + + my ($cmd, $args_string) = @_; + my @args = parseArgs($args_string, 4); + + # auction add item + # TODO: it doesn't seem possible to add more than 1 item? + if ($cmd eq 'aua') { + unless (defined $args[0] && $args[1] =~ /^\d+$/) { + message T("Usage: aua (<item #>|<item name>) <amount>\n"), "info"; + } elsif (my $item = Actor::Item::get($args[0])) { + my $serverIndex = $item->{ID}; + $messageSender->sendAuctionAddItem($serverIndex, $args[1]); + } + # auction remove item + } elsif ($cmd eq 'aur') { + $messageSender->sendAuctionAddItemCancel(); + # auction create (add item first) + } elsif ($cmd eq 'auc') { + unless ($args[0] && $args[1] && $args[2]) { + message T("Usage: auc <current price> <instant buy price> <hours>\n"), "info"; + } else { + my ($price, $buynow, $hours) = ($args[0], $args[1], $args[2]); + $messageSender->sendAuctionCreate($price, $buynow, $hours); + } + # auction create (add item first) + } elsif ($cmd eq 'aub') { + unless (defined $args[0] && $args[1] =~ /^\d+$/) { + message T("Usage: aub <id> <price>\n"), "info"; + } else { + unless ($auctionList->[$args[0]]->{ID}) { + if (@{$auctionList}) { + message TF("No auction item found with index: %s. (might need to re-open auction window)\n", $args[0]), "info"; + } else { + message T("Auction window has not been opened or is empty.\n"), "info"; + } + } else { + $messageSender->sendAuctionBuy($auctionList->[$args[0]]->{ID}, $args[1]); + } + } + # auction info (my) + } elsif ($cmd eq 'aui') { + # funny thing is, we can access this info trough 'aus' aswell + unless ($args[0] eq "selling" || $args[0] eq "buying") { + message T("Usage: aui (selling|buying)\n"), "info"; + } else { + $args[0] = ($args[0] eq "selling") ? 0 : 1; + $messageSender->sendAuctionReqMyInfo($args[0]); + } + # auction delete + } elsif ($cmd eq 'aud') { + unless ($args[0] =~ /^\d+$/) { + message T("Usage: aud <index>\n"), "info"; + } else { + unless ($auctionList->[$args[0]]->{ID}) { + if (@{$auctionList}) { + message TF("No auction item found with index: %s. (might need to re-open auction window)\n", $args[0]), "info"; + } else { + message T("Auction window has not been opened or is empty.\n"), "info"; + } + } else { + $messageSender->sendAuctionCancel($auctionList->[$args[0]]->{ID}); + } + } + # auction end (item gets sold to highest bidder?) + } elsif ($cmd eq 'aue') { + unless ($args[0] =~ /^\d+$/) { + message T("Usage: aue <index>\n"), "info"; + } else { + unless ($auctionList->[$args[0]]->{ID}) { + if (@{$auctionList}) { + message TF("No auction item found with index: %s. (might need to re-open auction window)\n", $args[0]), "info"; + } else { + message T("Auction window has not been opened or is empty.\n"), "info"; + } + } else { + $messageSender->sendAuctionMySellStop($auctionList->[$args[0]]->{ID}); + } + } + # auction search + } elsif ($cmd eq 'aus') { + # TODO: can you in official servers do a query on both a category AND price/text? (eA doesn't allow you to) + unless (defined $args[0]) { + message T("Usage: aus <type> [<price>|<text>]\n" . + " types (0:Armor 1:Weapon 2:Card 3:Misc 4:By Text 5:By Price 6:Sell 7:Buy)\n"), "info"; + # armor, weapon, card, misc, sell, buy + } elsif ($args[0] =~ /^[0-3]$/ || $args[0] =~ /^[6-7]$/) { + $messageSender->sendAuctionItemSearch($args[0]); + # by text + } elsif ($args[0] == 5) { + unless (defined $args[1]) { + message T("Usage: aus 5 <text>\n"), "info"; + } else { + $messageSender->sendAuctionItemSearch($args[0], undef, $args[1]); + } + # by price + } elsif ($args[0] == 6) { + unless ($args[1] =~ /^\d+$/) { + message T("Usage: aus 6 <price>\n"), "info"; + } else { + $messageSender->sendAuctionItemSearch($args[0], $args[1]); + } + } else { + error T("Possible value's for the <type> parameter are:\n" . + "(0:Armor 1:Weapon 2:Card 3:Misc 4:By Text 5:By Price 6:Sell 7:Buy)\n"); + } + # with command auction, list of possebilities: $cmd eq 'au' + } else { + message T("Auction commands: aua, aur, auc, aub, aui, aud, aue, aus\n"), "info"; + } +} + +sub cmdQuest { + if (!$net || $net->getState() != Network::IN_GAME) { + error TF("You must be logged in the game to use this command '%s'\n", shift); + return; + } + my ($cmd, $args_string) = @_; + my @args = parseArgs($args_string, 3); + if ($args[0] eq 'set') { + if ($args[1] =~ /^\d+/) { + # note: we need the questID here now, might be better if we could make it so you only have to insert some questIndex + $messageSender->sendQuestState($args[1], ($args[2] eq 'on')); + } else { + message T("Usage: quest set <questID> <on|off>\n"), "info"; + } + } elsif ($args[0] eq 'list') { + my $k = 0; + my $msg .= center(" " . T("Quest List") . " ", 79, '-') . "\n"; + foreach my $questID (keys %{$questList}) { + my $quest = $questList->{$questID}; + $msg .= swrite(sprintf("\@%s \@%s \@%s \@%s \@%s", ('>'x2), ('<'x5), ('<'x30), ('<'x10), ('<'x24)), + [$k, $questID, $quests_lut{$questID} ? $quests_lut{$questID}{title} : '', $quest->{active} ? T("active") : T("inactive"), $quest->{time_expire} ? scalar localtime $quest->{time_expire} : '']); + foreach my $mobID (keys %{$quest->{missions}}) { + my $mission = $quest->{missions}->{$mobID}; + $msg .= swrite(sprintf("\@%s \@%s \@%s", ('>'x2), ('<'x30), ('<'x30)), + [" -", $mission->{mob_name}, sprintf(defined $mission->{mob_goal} ? '%d/%d' : '%d', @{$mission}{qw(mob_count mob_goal)})]); + } + $k++; + } + $msg .= sprintf("%s\n", ('-'x79)); + message $msg, "list"; + } elsif ($args[0] eq 'info') { + if ($args[1] =~ /^\d+/) { + # note: we need the questID here now, might be better if we could make it so you only have to insert some questIndex + if ($quests_lut{$args[1]}) { + my $msg = center (' ' . ($quests_lut{$args[1]}{title} || T('Quest Info')) . ' ', 79, '-') . "\n"; + $msg .= "$quests_lut{$args[1]}{summary}\n" if $quests_lut{$args[1]}{summary}; + $msg .= TF("Objective: %s\n", $quests_lut{$args[1]}{objective}) if $quests_lut{$args[1]}{objective}; + $msg .= $quests_lut{$args[1]}{active} ? T("active") : T("inactive") . "\n"; + message $msg; + } else { + message T("Unknown quest\n"), "info"; + } + } else { + message T("Usage: quest info <questID>\n"), "info"; + } + } else { + message T("Quest commands: set, list, info\n"), "info"; + } +} + +sub cmdShowEquip { + if (!$net || $net->getState() != Network::IN_GAME) { + error TF("You must be logged in the game to use this command '%s'\n", shift); + return; + } + my ($cmd, $args_string) = @_; + my @args = parseArgs($args_string, 2); + if ($args[0] eq 'p') { + if (my $actor = Match::player($args[1], 1)) { + $messageSender->sendShowEquipPlayer($actor->{ID}); + message TF("Requesting equipment information for: %s\n", $actor->name), "info"; + } elsif ($args[1]) { + message TF("No player found with specified information: %s\n", $args[1]), "info"; + } else { + message T("Usage: showeq p <index|name|partialname>\n"); + } + } elsif ($args[0] eq 'me') { + $messageSender->sendMiscConfigSet(0, $args[1] eq 'on'); + } else { + message T("Usage: showeq [p <index|name|partialname>] | [me <on|off>]\n"), "info"; + } +} + +# Answer to mixing item selection dialog (CZ_REQ_MAKINGITEM). +# 025b <mk type>.W <name id>.W +# mk type: +# 1 = cooking +# 2 = arrow +# 3 = elemental +# 4 = GN_MIX_COOKING +# 5 = GN_MAKEBOMB +# 6 = GN_S_PHARMACY +sub cmdCooking { + if (!$net || $net->getState() != Network::IN_GAME) { + error TF("You must be logged in the game to use this command '%s'\n", shift); + return; + } + + my ($cmd, $arg) = @_; + if ($arg =~ /^\d+/ && defined $cookingList->[$arg]) { # viewID/nameID can be 0 + my $type = 1; + if(defined $currentCookingType && $currentCookingType > 0) { + $type = $currentCookingType; + } + $messageSender->sendCooking($type, $cookingList->[$arg]); # type 1 is for cooking + } elsif (!$arg) { + message TF("Syntax error in function 'cook' (Cook food)\n" . + "Usage: cook [<list index>]\n"); + } else { + message TF("Item with 'Cooking List' index: %s not found.\n", $arg), "info"; + } +} + +sub cmdWeaponRefine { + if (!$net || $net->getState() != Network::IN_GAME) { + error TF("You must be logged in the game to use this command '%s'\n", shift); + return; + } + + my ($cmd, $args) = @_; + + if ($args =~ /^\d+/ && defined $refineList->[$args]) { + $messageSender->sendWeaponRefine($refineList->[$args]); + } elsif($args =~ /^\d+/) { + message TF("Item with 'refine' index: %s not found.\n", $args), "info"; + } else { + error T("Error in function 'refine'\n". + "Usage: refine <index number>\n"); + } +} + +sub cmdAnswerCaptcha { + if ($net->getState() == Network::IN_GAME()) { + $messageSender->sendMacroDetectorAnswer($_[1]); + } else { + $messageSender->sendCaptchaAnswer($_[1]); + } +} + +### CATEGORY: Private functions + +## +# void cmdStorage_list(String list_type) +# list_type: ''|eq|nu|u +# +# Displays the contents of storage, or a subset indicated by switches. +# +# Called by: cmdStorage (not called directly). +sub cmdStorage_list { + my $type = shift; + message "$type\n"; + + my @useable; + my @equipment; + my @non_useable; + my ($i, $display, $index); + + for my $item (@{$char->storage}) { + if ($item->usable) { + push @useable, $item->{binID}; + } elsif ($item->equippable) { + my %eqp; + $eqp{index} = $item->{ID}; + $eqp{binID} = $item->{binID}; + $eqp{nameID} = $item->{nameID}; + $eqp{name} = $item->{name}; + $eqp{amount} = $item->{amount}; + $eqp{identified} = " -- " . T("Not Identified") if !$item->{identified}; + $eqp{type} = $itemTypes_lut{$item->{type}}; + push @equipment, \%eqp; + } else { + push @non_useable, $item->{binID}; + } + } + + my $msg = center(defined $storageTitle ? $storageTitle : T(' Storage '), 50, '-') . "\n"; + + if (!$type || $type eq 'u') { + $msg .= T("-- Usable --\n"); + for (my $i = 0; $i < @useable; $i++) { + $index = $useable[$i]; + my $item = $char->storage->get($index); + my $nameID = "[".$item->{nameID}."]"; + $display = $item->{name}; + $display .= " x $item->{amount}"; + $msg .= swrite( + "@<<< @<<<<<<<< @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<", + [$index, $nameID, $display]); + } + } + + if (!$type || $type eq 'eq') { + $msg .= T("\n-- Equipment --\n"); + foreach my $item (@equipment) { + ## altered to allow for Arrows/Ammo which will are stackable equip. + $display = sprintf("%-3d [%-6d] %s (%s)", $item->{binID}, $item->{nameID}, $item->{name}, $item->{type}); + $display .= " x $item->{amount}" if $item->{amount} > 1; + $display .= $item->{identified}; + $msg .= sprintf("%-57s\n", $display); + } + } + + if (!$type || $type eq 'nu') { + $msg .= T("\n-- Non-Usable --\n"); + for (my $i = 0; $i < @non_useable; $i++) { + $index = $non_useable[$i]; + my $item = $char->storage->get($index); + my $nameID = "[".$item->{nameID}."]"; + $display = $item->{name}; + $display .= " x $item->{amount}"; + $msg .= swrite( + "@<<< @<<<<<<<< @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<", + [$index, $nameID, $display]); + } + } + + $msg .= TF("\nCapacity: %d/%d\n", $char->storage->items, $char->storage->items_max) . + ('-'x50) . "\n"; + message $msg, "list"; +} + +sub cmdDeadTime { + if (!$net || $net->getState() != Network::IN_GAME) { + error TF("You must be logged in the game to use this command '%s'\n", shift); + return; + } + my $msg; + if (@deadTime) { + $msg = center(T(" Dead Time Record "), 50, '-') ."\n"; + my $i = 1; + foreach my $dead (@deadTime) { + $msg .= "[".$i."] ". $dead."\n"; + } + $msg .= ('-'x50) . "\n"; + } else { + $msg = T("You have not died yet.\n"); + } + message $msg, "list"; +} + +sub cmdAchieve { + if (!$net || $net->getState() != Network::IN_GAME) { + error TF("You must be logged in the game to use this command '%s'\n", shift); + return; + } + my ($cmd, $args_string) = @_; + my @args = parseArgs($args_string, 3); + if ($args[0] eq 'list') { + if (!$achievementList) { + error T("'Achievement List' is empty.\n"); + return; + } + + my $msg = center(" " . T("Achievement List") . " ", 79, '-') . "\n"; + my $index = 0; + foreach my $achievementID (keys %{$achievementList}) { + my $achieve = $achievementList->{$achievementID}; + $msg .= swrite(sprintf("\@%s \@%s \@%s \@%s \@%s", ('>'x2), ('<'x7), ('<'x15), ('<'x15), ('<'x32)), [$index, $achievementID, $achieve->{completed} ? T("complete") : T("incomplete"), $achieve->{reward} ? T("rewarded") : T("not rewarded"), $achievements{$achievementID}->{title}]); + $index++; + } + $msg .= sprintf("%s\n", ('-'x79)); + message $msg, "list"; + + } elsif ($args[0] eq 'reward') { + if ($args[1] !~ /^\d+$/) { + error T("Syntax Error in function 'achieve reward' (Receiving an award)\n" . + "Usage: achieve reward <achievementID>\n"); + + } elsif (!exists $achievementList->{$args[1]}) { + error TF("You don't have the achievement %s.\n", $args[1]); + + } elsif ($achievementList->{$args[1]}{completed} != 1) { + error TF("You haven't completed the achievement %s.\n", $args[1]); + + } elsif ($achievementList->{$args[1]}{reward} == 1) { + error TF("You have already claimed the achievement %s reward.\n", $args[1]); + + } else { + message TF("Sending request for reward of achievement %s.\n", $args[1]); + $messageSender->sendAchievementGetReward($args[1]); + } + + } elsif ($args[0] eq 'info' && $args[1] =~ /^\d+$/) { + if(defined($achievements{$args[1]})) { + # status + my $msg; + $msg .= center(" " . T("Achievement Info") . " ", 79, '-') . "\n"; + $msg .= TF("ID: %s - Title: %s\n", $achievements{$args[1]}->{ID}, $achievements{$args[1]}->{title}); + $msg .= TF("Group: %s\n", ($achievements{$args[1]}->{group}) ? $achievements{$args[1]}->{group} : T("N/A")); + $msg .= TF("Summary: %s\n", ($achievements{$args[1]}->{summary}) ? $achievements{$args[1]}->{summary} : T("N/A")); + $msg .= TF("Details: %s\n", ($achievements{$args[1]}->{details}) ? $achievements{$args[1]}->{details} : T("N/A")); + $msg .= T("Rewards:\n"); + $msg .= TF(" Item: %s\n", ($achievements{$args[1]}->{rewards}->{item}) ? itemNameSimple($achievements{$args[1]}->{rewards}->{item}) : T("N/A")); + $msg .= TF(" Buff: %s\n", ($achievements{$args[1]}->{rewards}->{buff}) ? $statusName{$statusHandle{$achievements{$args[1]}->{rewards}->{buff}}} : T("N/A")); + $msg .= TF(" Title: %s\n", ($achievements{$args[1]}->{rewards}->{title}) ? $title_lut{$achievements{$args[1]}->{rewards}->{title}} : T("N/A")); + $msg .= T("Status: "); + if ( defined ( $achievementList->{$args[1]} ) ) { + my $achieve = $achievementList->{$args[1]}; + $msg .= TF("%s %s\n", $achieve->{completed} ? T("complete") : T("incomplete"), $achieve->{reward} ? T("rewarded") : T("not rewarded")); + } else { + $msg .= T("N/A\n"); + } + $msg .= center("", 79, '-') . "\n"; + message $msg; + } else { + warning T("The achievement was not found. Update the 'achievement_list.txt' file\n"); + } + } else { + error T("Syntax Error in function 'achieve'\n" . + "see 'help achieve'\n"); + } +} + +sub cmdRodex { + if (!$net || $net->getState() != Network::IN_GAME) { + error TF("You must be logged in the game to use this command '%s'\n", shift); + return; + } + + my (undef, $args) = @_; + my ($arg1) = $args =~ /^(\w+)/; + my ($arg2) = $args =~ /^\w+\s+(\S.*)/; + + if ($arg1 eq 'open') { + if (defined $rodexList) { + error T("Your rodex mail box is already opened.\n"); + return; + } + my $type = 0; + if($arg2 && $arg2=~/\d+/) { + $type = $arg2; + if($arg2 == 1) { + message T("Sending request to open rodex account mailbox.\n"); + } elsif($arg2 == 2) { + message T("Sending request to open rodex returned mailbox.\n"); + } else { + message T("Sending request to open rodex normal mailbox.\n"); + $type = 0; + } + } else { + message T("Sending request to open rodex normal mailbox.\n"); + } + $rodexCurrentType = $type; + $messageSender->rodex_open_mailbox($type,0,0); + + } elsif ($arg1 eq 'close') { + if (!defined $rodexList) { + error T("Your rodex mail box is closed.\n"); + return; + } elsif (defined $rodexWrite) { + $messageSender->rodex_cancel_write_mail();#we must first close the letter if it is open + } + message T("Your rodex mail box has been closed.\n"); + $messageSender->rodex_close_mailbox(); + + } elsif ($arg1 eq 'list') { + if (!defined $rodexList) { + error T("Your rodex mail box is closed.\n"); + return; + + } elsif (!$rodexList) { + message T("Your rodex mail box is empty.\n"); + return; + } + my $msg = center(" ". T("Rodex Mail List") ." ", 119, '-') . "\n" . + T(" # ID From Att New Expire Title\n"); + + my @list; + foreach my $mail_id (keys %{$rodexList->{mails}}) { + my $mail = $rodexList->{mails}{$mail_id}; + $list[$mail->{page_index}] = swrite("@> @<<<<<<< @<<<<<<<<<<<<<<<<<<<<<< @<<< @<< @>>>>>>> @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<", [$mail->{page_index}, $mail->{mailID1}, $mail->{sender}, $mail->{attach} ? $mail->{attach} : "-", $mail->{isRead} ? T("No") : T("Yes"), $mail->{expireDay} ." ".T("Days"), $mail->{title}]); + } + foreach my $list (@list) { + $msg .= $list; + } + $msg .= ('-'x119) . "\n"; + message $msg, "list"; + + } elsif ($arg1 eq 'maillist') { + if (!defined $rodexList) { + error T("Your rodex mail box is closed.\n"); + return; + } + + my @pages; + foreach my $mail_id (keys %{$rodexList->{mails}}) { + my $mail = $rodexList->{mails}{$mail_id}; + + my $index; + if ($mail->{page} == 0) { + $index = $mail->{page_index}; + } else { + $index = (($mail->{page} * $rodexList->{mails_per_page}) + $mail->{page_index}); + } + $pages[$mail->{page}][$mail->{page_index}] = swrite("@> @<<<<<<< @<<<<<<<<<<<<<<<<<<<<<< @<<< @<< @>>>>>>> @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<", [$index, $mail->{mailID1}, $mail->{sender}, $mail->{attach} ? $mail->{attach} : "-", $mail->{isRead} ? T("No") : T("Yes"), $mail->{expireDay} ." ".T("Days"), $mail->{title}]); + + } + + my $msg; + foreach my $page_index (0..$#pages) { + $msg .= center(" ". TF("Rodex Mail Page %d", $page_index) ." ", 119, '-') . "\n" . + T(" # ID From Att New Expire Title\n"); + + foreach my $mail_msg (@{$pages[$page_index]}) { + $msg .= $mail_msg; + } + } + $msg .= ('-'x119) . "\n"; + message $msg, "list"; + + } elsif ($arg1 eq 'refresh') { + if (!defined $rodexList) { + error T("Your rodex mail box is closed.\n"); + return; + } + + $messageSender->rodex_refresh_maillist($rodexCurrentType,0,0); + + } elsif ($arg1 eq 'read') { + if (!defined $rodexList) { + error T("Your rodex mail box is closed.\n"); + return; + + } elsif ($arg2 eq "" || $arg2 !~ /^\d+$/) { + error T("Syntax Error in function 'rodex read' (Read rodex mail)\n" . + "Usage: rodex read <mail_# | mail_id>\n"); + return; + + } elsif ($arg2 =~/^\d{1,3}$/) { + foreach my $mail_id (keys %{$rodexList->{mails}}) { + my $page_index = $rodexList->{mails}{$mail_id}{page_index}; + if ($page_index == $arg2) { + $arg2 = $mail_id; + last; + } else { + next; + } + } + } + + if (!exists $rodexList->{mails}{$arg2}) { + error TF("The rodex mail of ID '%d' doesn't exist.\n", $arg2); + return; + } + + my $openType = $rodexList->{mails}{$arg2}{openType}; + $messageSender->rodex_read_mail($openType,$arg2,0); + + } elsif ($arg1 eq 'write') { + if (!defined $rodexList) { + error T("Your rodex mail box is closed.\n"); + return; + + } elsif (defined $rodexWrite) { + error T("You are already writing a rodex mail.\n"); + return; + + } elsif ($arg2 eq "self") { + debug "Send rodex mail to yourself\n"; + $arg2 = $char->{'name'}; + } else { + message TF("Opening rodex mail write box. Recipient: %s\n", $arg2); + } + $messageSender->rodex_open_write_mail($arg2); + + } elsif ($arg1 eq 'cancel') { + if (!defined $rodexList) { + error T("Your rodex mail box is closed.\n"); + return; + + } elsif (!defined $rodexWrite) { + error T("You are not writing a rodex mail.\n"); + return; + } + + message T("Closing rodex mail write box.\n"); + $messageSender->rodex_cancel_write_mail(); + + } elsif ($arg1 eq 'settarget') { + if (!defined $rodexList) { + error T("Your rodex mail box is closed.\n"); + return; + + } elsif (!defined $rodexWrite) { + error T("You are not writing a rodex mail.\n"); + return; + + } elsif (exists $rodexWrite->{target}{name}) { + error T("You have already set the mail target.\n"); + return; + + } elsif ($arg2 eq "") { + error T("Syntax Error in function 'rodex settarget' (Set target of rodex mail)\n" . + "Usage: rodex settarget <player_name|self>\n"); + return; + } elsif ($arg2 eq "self") { + debug "Send rodex mail to yourself\n"; + $arg2 = $char->{'name'}; + } + + message TF("Setting target of rodex mail to '%s'.\n", $arg2); + $messageSender->rodex_checkname($arg2); + + } elsif ($arg1 eq 'itemslist') { + if (!defined $rodexList) { + error T("Your rodex mail box is closed.\n"); + return; + + } elsif (!defined $rodexWrite) { + error T("You are not writing a rodex mail.\n"); + return; + + } + + my @useable; + my @equipment; + my @non_useable; + my ($i, $display, $index); + + for my $item (@{$rodexWrite->{items}}) { + if ($item->usable) { + push @useable, $item->{binID}; + } elsif ($item->equippable) { + my %eqp; + $eqp{index} = $item->{ID}; + $eqp{binID} = $item->{binID}; + $eqp{name} = $item->{name}; + $eqp{amount} = $item->{amount}; + $eqp{identified} = " -- " . T("Not Identified") if !$item->{identified}; + $eqp{type} = $itemTypes_lut{$item->{type}}; + push @equipment, \%eqp; + } else { + push @non_useable, $item->{binID}; + } + } + + my $msg = center( " " .T("Rodex mail item list") ." ", 50, '-') ."\n"; + + $msg .= T("-- Usable --\n"); + for (my $i = 0; $i < @useable; $i++) { + $index = $useable[$i]; + my $item = $rodexWrite->{items}->get($index); + $display = $item->{name}; + $display .= " x $item->{amount}"; + $msg .= swrite( + "@<<< @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<", + [$index, $display]); + } + + $msg .= T("\n-- Equipment --\n"); + foreach my $item (@equipment) { + ## altered to allow for Arrows/Ammo which will are stackable equip. + $display = sprintf("%-3d %s (%s)", $item->{binID}, $item->{name}, $item->{type}); + $display .= " x $item->{amount}" if $item->{amount} > 1; + $display .= $item->{identified}; + $msg .= sprintf("%-57s\n", $display); + } + + $msg .= T("\n-- Non-Usable --\n"); + for (my $i = 0; $i < @non_useable; $i++) { + $index = $non_useable[$i]; + my $item = $rodexWrite->{items}->get($index); + $display = $item->{name}; + $display .= " x $item->{amount}"; + $msg .= swrite( + "@<<< @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<", + [$index, $display]); + } + $msg .= sprintf("%s\n", ('-'x50)); + message $msg, "list"; + + } elsif ($arg1 eq 'settitle') { + if (!defined $rodexList) { + error T("Your rodex mail box is closed.\n"); + return; + + } elsif (!defined $rodexWrite) { + error T("You are not writing a rodex mail.\n"); + return; + + } elsif ($arg2 eq "") { + error T("Syntax Error in function 'rodex settitle' (Set title of rodex mail)\n" . + "Usage: rodex settitle <title>\n"); + return; + } elsif (length($arg2) < 4) { + error $msgTable[2597] ? $msgTable[2597] . "\n" : T("The title must be 4 to 24 characters long\n"); + return; + } + + if (exists $rodexWrite->{title}) { + message TF("Changed the rodex mail message title to '%s'.\n", $arg2); + } else { + message TF("Set the rodex mail message title to '%s'.\n", $arg2); + } + + $rodexWrite->{title} = $arg2; + + } elsif ($arg1 eq 'setbody') { + if (!defined $rodexList) { + error T("Your rodex mail box is closed.\n"); + return; + + } elsif (!defined $rodexWrite) { + error T("You are not writing a rodex mail.\n"); + return; + + } elsif ($arg2 eq "") { + error T("Syntax Error in function 'rodex setbody' (Set body of rodex mail)\n" . + "Usage: rodex setbody <body>\n"); + return; + } + + if (exists $rodexWrite->{body}) { + message TF("Changed the rodex mail message body to '%s'.\n", $arg2); + } else { + message TF("Set the rodex mail message body to '%s'.\n", $arg2); + } + + $rodexWrite->{body} = $arg2; + + } elsif ($arg1 eq 'setzeny') { + if (!defined $rodexList) { + error T("Your rodex mail box is closed.\n"); + return; + + } elsif (!defined $rodexWrite) { + error T("You are not writing a rodex mail.\n"); + return; + + } elsif ($arg2 eq "" || $arg2 !~ /^\d+$/) { + error T("Syntax Error in function 'rodex setzeny' (Set zeny of rodex mail)\n" . + "Usage: rodex setzeny <zeny_amount>\n"); + return; + } elsif ($arg2 > $char->{zeny}) { + error T("You can't add more zeny than you have to the rodex mail.\n"); + return; + } + + if (exists $rodexWrite->{zeny}) { + message TF("Changed the rodex mail message zeny to '%d'.\n", $arg2); + } else { + message TF("Set the rodex mail message zeny to '%d'.\n", $arg2); + } + + $rodexWrite->{zeny} = $arg2; + + } elsif ($arg1 eq 'add') { + if (!defined $rodexList) { + error T("Your rodex mail box is closed.\n"); + return; + + } elsif (!defined $rodexWrite) { + error T("You are not writing a rodex mail.\n"); + return; + + } elsif ($arg2 !~ /^\s*(\d+)\s*(\d*)\s*$/) { + error T("Syntax Error in function 'rodex add' (Add item to rodex mail)\n" . + "Usage: rodex add <item #> [<amount>]\n"); + return; + } + + my $max_items = $config{rodexMaxItems} || 5; + if ($rodexWrite->{items}->size >= $max_items) { + error T("You can't add any more items to the rodex mail.\n"); + return; + } + + my ($index, $amount) = parseArgs($arg2); + $amount = defined $amount ? $amount : 1; + my $rodex_item = $rodexWrite->{items}->get($index); + my $item = $char->inventory->get($index); + + if (!$item) { + error TF("Error in function 'rodex add' (Add item to rodex mail)\n" . + "Inventory Item '%s' does not exist.\n", $index); + return; + } elsif ($item->{equipped}) { + error TF("Inventory Item '%s' is equipped.\n", $item); + return; + } elsif ($rodex_item && $rodex_item->{amount} == $item->{amount}) { + error TF("You can't add more of Item '%s' to rodex mail because you have already added all you have of it.\n", $item); + return; + } elsif ($rodex_item) { + my $max_add = ($item->{amount} - $rodex_item->{amount}); + $amount = $max_add if ($amount > $max_add); + } elsif ($amount > $item->{amount}) { + $amount = $item->{amount}; + } + + message TF("Adding amount %d of item '%s' to rodex mail.\n", $amount, $item); + $messageSender->rodex_add_item($item->{ID}, $amount); + + } elsif ($arg1 eq 'draft') { + if (!defined $rodexList) { + error T("Your rodex mail box is closed.\n"); + return; + + } elsif (!defined $rodexWrite) { + error T("You are not writing a rodex mail.\n"); + return; + } + + my $msg = center( " " .TF("Draft mail for %s", $rodexWrite->{target}{name}) ." ", 119, '-') ."\n"; + $msg .= swrite("@>>>>>>>>> @<<<<<<<<<<<<<<<<<<<<<<< @<<<<<<<<<< @<<<", [T("Recepient:"), $rodexWrite->{target}{name}, T("Base Level:"), $rodexWrite->{target}{base_level}]); + $msg .= swrite("@>>>>>>>>> @<<<<<<<<<<<<<<<<<<<<<<< @<<<<< @<<<<<<<<<<<<<<<<<<<<", [T("Char ID:"), $rodexWrite->{target}{char_id}, T("Class:"), $jobs_lut{$rodexWrite->{target}{class}}]); + $msg .= "------\n"; + $msg .= swrite("@<<<<<<<<<< @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<", [T("Title:"), $rodexWrite->{title}]); + $msg .= T("Message:") ." " .$rodexWrite->{body} ."\n"; + $msg .= swrite("@<<<<<<<<<< @<<<<<<<<<", [T("Zeny:"), $rodexWrite->{zeny}]) if ($rodexWrite->{zeny}); + $msg .= ('-'x119) . "\n"; + + message $msg, "list"; + cmdRodex(undef, 'itemslist'); + + } elsif ($arg1 eq 'remove') { + if (!defined $rodexList) { + error T("Your rodex mail box is closed.\n"); + return; + + } elsif (!defined $rodexWrite) { + error T("You are not writing a rodex mail.\n"); + return; + + } elsif ($arg2 !~ /^\s*(\d+)\s*(\d*)\s*$/) { + error T("Syntax Error in function 'rodex remove' (Remove item from rodex mail)\n" . + "Usage: rodex remove <item #> [<amount>]\n"); + return; + } + + my ($index, $amount) = parseArgs($arg2); + + my $item = $rodexWrite->{items}->get($index); + if (!$item) { + error TF("Error in function 'rodex remove' (Remove item from rodex mail)\n" . + "Rodex mail Item '%s' does not exist.\n", $index); + return; + } + if (!$amount || $amount > $item->{amount}) { + $amount = $item->{amount}; + } + + message TF("Removing amount %d of item '%s' from rodex mail.\n", $amount, $item); + $messageSender->rodex_remove_item($item->{ID}, $amount); + + } elsif ($arg1 eq 'send') { + if (!defined $rodexList) { + error T("Your rodex mail box is closed.\n"); + return; + + } elsif (!defined $rodexWrite) { + error T("You are not writing a rodex mail.\n"); + return; + + } elsif (!exists $rodexWrite->{target}) { + error T("Error in function 'rodex send' (Send finished rodex mail)\n" . + "You must set target of rodex mail. Usage: rodex settarget <player_name|self>\n"); + return; + } + + my $zeny_tax = int($rodexWrite->{zeny} / 50); + my $items_tax = ($rodexWrite->{items}->size * 2500); + my $tax = ($zeny_tax + $items_tax); + + if (($rodexWrite->{zeny} + $tax) > $char->{zeny}) { + error TF("The current tax for this rodex mail is %dz, you don't have enough zeny to pay for it.\n", $tax); + return; + } + + message T("Sending rodex mail.\n"); + $messageSender->rodex_send_mail(); + + } elsif ($arg1 eq 'getitems') { + if (!defined $rodexList) { + error T("Your rodex mail box is closed.\n"); + return; + + } elsif (defined $rodexWrite) { + error T("You are writing a rodex mail.\n"); + return; + + } elsif ($arg2 eq "" and !exists $rodexList->{current_read}) { + error T("You are not reading a rodex mail.\n"); + return; + + } elsif ($arg2 ne "" and $arg2 !~ /^\d+$/) { + error T("Syntax Error in function 'rodex getitems' (Get items of rodex mail)\n" . + "Usage: rodex getitems [<mail_# | mail_id>]\n"); + return; + + } elsif ($arg2 =~/^\d{1,3}$/) { + foreach my $mail_id (keys %{$rodexList->{mails}}) { + my $page_index = $rodexList->{mails}{$mail_id}{page_index}; + if ($page_index == $arg2) { + $arg2 = $mail_id; + last; + } else { + next; + } + } + + } else { + $arg2 = $rodexList->{current_read} if ($rodexList->{current_read}); + } + + if (!exists $rodexList->{mails}{$arg2}) { + error TF("The rodex mail of ID '%d' doesn't exist.\n", $arg2); + return; + } elsif (!grep { $_ eq $rodexList->{mails}{$arg2}{attach} } qw(i z+i gift)) { + error TF("The rodex mail '%d' has no items.\n", $arg2); + return; + } + + my $openType = $rodexList->{mails}{$arg2}{openType}; + message TF("Requesting items of rodex mail '%d'.\n", $arg2); + $messageSender->rodex_request_items($arg2, 0, $openType); + + } elsif ($arg1 eq 'getzeny') { + if (!defined $rodexList) { + error T("Your rodex mail box is closed.\n"); + return; + + } elsif (defined $rodexWrite) { + error T("You are writing a rodex mail.\n"); + return; + + } elsif ($arg2 eq "" and !exists $rodexList->{current_read}) { + error T("You are not reading a rodex mail.\n"); + return; + + } elsif ($arg2 ne "" and $arg2 !~ /^\d+$/) { + error T("Syntax Error in function 'rodex getzeny' (Get zeny of rodex mail)\n" . + "Usage: rodex getzeny [<mail_# | mail_id>]\n"); + return; + + } elsif ($arg2 =~/^\d{1,3}$/) { + foreach my $mail_id (keys %{$rodexList->{mails}}) { + my $page_index = $rodexList->{mails}{$mail_id}{page_index}; + if ($page_index == $arg2) { + $arg2 = $mail_id; + last; + } else { + next; + } + } + + } else { + $arg2 = $rodexList->{current_read} if ($rodexList->{current_read}); + } + + if (!exists $rodexList->{mails}{$arg2}) { + error TF("The rodex mail of ID '%d' doesn't exist.\n", $arg2); + return; + } elsif ($rodexList->{mails}{$arg2}{attach} ne 'z' and $rodexList->{mails}{$arg2}{attach} ne 'z+i') { + error TF("The rodex mail '%d' has no zeny.\n", $arg2); + return; + } + + my $openType = $rodexList->{mails}{$arg2}{openType}; + message TF("Requesting zeny of rodex mail '%d'.\n", $arg2); + $messageSender->rodex_request_zeny($arg2, 0, $openType); + + } elsif ($arg1 eq 'nextpage') { + if (!defined $rodexList) { + error T("Your rodex mail box is closed.\n"); + return; + + } elsif (defined $rodexWrite) { + error T("You are writing a rodex mail.\n"); + return; + + } elsif (exists $rodexList->{last_page}) { + error T("You have already reached the last rodex mail page.\n"); + return; + } + + message T("Requesting the next page of rodex mail.\n"); + $messageSender->rodex_next_maillist($rodexCurrentType, $rodexList->{current_page_last_mailID}, 0); + + } elsif ($arg1 eq 'delete') { + if (!defined $rodexList) { + error T("Your rodex mail box is closed.\n"); + return; + + } elsif ($arg2 eq "" || $arg2 !~ /^\d+$/) { + error T("Syntax Error in function 'rodex delete' (Delete rodex mail)\n" . + "Usage: rodex delete <mail_# | mail_id>\n"); + return; + + } elsif ($arg2 =~/^\d{1,3}$/) { + foreach my $mail_id (keys %{$rodexList->{mails}}) { + my $page_index = $rodexList->{mails}{$mail_id}{page_index}; + if ($page_index == $arg2) { + $arg2 = $mail_id; + last; + } else { + next; + } + } + } + + if (!exists $rodexList->{mails}{$arg2}) { + error TF("The rodex mail of ID '%d' doesn't exist.\n", $arg2); + return; + } + + my $openType = $rodexList->{mails}{$arg2}{openType}; + $messageSender->rodex_delete_mail($openType,$arg2,0); + + } else { + error T("Syntax Error in function 'rodex' (rodex mail)\n" . + "Usage: rodex [<open|close|list|refresh|nextpage|maillist|read|getitems|getzeny|delete|write|cancel|settarget|settitle|setbody|setzeny|add|remove|itemslist|draft|send>]\n"); + } +} + +sub cmdRoulette { + my (undef, $args) = @_; + my ($command) = parseArgs( $args ); + + if (!$net || $net->getState() != Network::IN_GAME) { + error TF("You must be logged in the game to use this command '%s'\n", shift); + return; + } + + if ( $command eq "open" ) { + message T("Sending Roulette Open\n"); + $messageSender->sendRouletteWindowOpen(); + $messageSender->sendRouletteInfoRequest(); + } elsif ( $command eq "close" ) { + message T("Roulette System Closed\n"); + $messageSender->sendRouletteClose(); + undef %roulette; + } elsif ( ( $command eq "info" || $command eq "start" || $command eq "claim" ) && !defined($roulette{items}) ) { + error TF("Roulette: Error in command '%s', you must need open Roulette first'\n", $command); + } elsif ( $command eq "info" ) { + message T("Requesting Roulette Info\n"); + $messageSender->sendRouletteInfoRequest(); + } elsif ( $command eq "start" ) { + message T("Sending Roulette Start (roll)\n"); + $messageSender->sendRouletteStart(); + } elsif ( $command eq "claim" ) { + message T("Trying to Claim Roulette Reward\n"); + $messageSender->sendRouletteClaimPrize(); + } else { + error T("Syntax Error in function 'roulette'\n" . + "roulette <open|info|close|start|claim>\n"); + } +} + +sub cmdCancelTransaction { + if (!$net || $net->getState() != Network::IN_GAME) { + error TF("You must be logged in the game to use this command '%s'\n", shift); + return; + } + + if (defined $ai_v{'npc_talk'} && exists $ai_v{'npc_talk'}{'talk'} && ($ai_v{'npc_talk'}{'talk'} eq 'buy_or_sell' || $ai_v{'npc_talk'}{'talk'} eq 'store')) { + cancelNpcBuySell(); + } else { + error T("You are not on a sell or store npc interaction.\n"); + } +} + +## +# 'cm' for Change Material (Genetic) +# 'analysis' for Four Spirit Analysis (Sorcerer) [Untested yet] +# @author [Cydh] +## +sub cmdExchangeItem { + if (!$net || $net->getState() != Network::IN_GAME) { + error TF("You must be logged in the game to use this command '%s'\n", shift); + return; + } + + my ($switch, $args) = @_; + my $type; + my $typename; + + if ($switch eq "cm") { + if ($skillExchangeItem != 1) { # Change Material (2494) + error T("This command only available after using 'Change Material' skill!\n"); + return; + } + $typename = "Change Material"; + } elsif ($switch eq "analysis") { + if ($skillExchangeItem != 2 && $skillExchangeItem != 3) { # Four Spirit Analysis (2462) + error T("This command only available after using 'Four Spirit Analysis' skill!\n"); + return; + } + $typename = "Four Spirit Analysis"; + } else { + error T("Invalid usage!\n"); + return; + } + + if ($args eq "cancel" || $args eq "end" || $args eq "no") { + my @items = (); + message TF("Item Exchange %s is canceled.\n", $typename), "info"; + undef $skillExchangeItem; + $messageSender->sendItemListWindowSelected(0, $type, 0, \@items); # Cancel: 0 + return; + } + $type = $skillExchangeItem-1; + + my ($item1, $amt1) = $args =~ /^(\d+) (\d+)/; + + if ($item1 >= 0 and $amt1 > 0) { + my @list = split(/,/, $args); + my @items = (); + + @list = grep(!/^$/, @list); # Remove empty entries + foreach (@list) { + my ($invIndex, $amt) = $_ =~ /^(\d+) (\d+)/; + my $item = $char->inventory->get($invIndex); + if ($item) { + if ($item->{amount} < $amt) { + warning TF("Invalid amount! Only have %dx %s (%d).\n", $item->{amount}, $item->{name}, $invIndex); + } elsif ($item->{equipped} != 0) { + warning TF("Equipped item was selected %s (%d)!\n", $item->{name}, $invIndex); + } else { + #message TF("Selected: %dx %s invIndex:%d binID:%d\n", $amt, $item->{name}, $invIndex, unpack 'v', (unpack 'v', $item->{ID})); + push(@items,{itemIndex => (unpack 'v', $item->{ID}), amount => $amt, itemName => $item->{name}}); + } + } else { + warning TF("Item in index '%d' is not exists.\n", $invIndex); + } + } + if (@items > 0) { + my $num = scalar @items; + message TF("Number of selected items for %s: %d\n", $typename, $num), "info"; + message T("======== Exchange Item List ========\n"); + map {message "$_->{itemName} $_->{amount}x\n"} @items; + message "==============================\n"; + undef $skillExchangeItem; + $messageSender->sendItemListWindowSelected($num, $type, 1, \@items); # Process: 1 + return; + } else { + error T("No item was selected.\n"); + } + } + + error TF("Syntax Error in function '%s'. Usages:\n". + "Single Item: %s <item #> <amount>\n". + "Combination: %s <item #> <amount>,<item #> <amount>,<item #> <amount>\n", $switch, $switch, $switch); +} + +## +# refineui select [item_index] +# refineui refine [item_index] [material_id] [catalyst_toggle] +# @author [Cydh] +## +sub cmdRefineUI { + if (!$net || $net->getState() != Network::IN_GAME) { + error TF("You must be logged in the game to use this command '%s'\n", shift); + return; + } + my ($cmd, $args_string) = @_; + if (!defined $refineUI) { + error T("Cannot use RefineUI yet.\n"); + return; + } + my @args = parseArgs($args_string, 4); + + # refineui close + # End Refine UI state + if ($args[0] eq "cancel" || $args[0] eq "end" || $args[0] eq "no") { + message T("Closing Refine UI.\n"), "info"; + undef $refineUI; + $messageSender->sendRefineUIClose(); + return; + + # refineui select [item_index] + # Do refine + } elsif ($args[0] eq "select") { + #my ($invIndex) = $args =~ /^(\d+)/; + my $invIndex = $args[1]; + + # Check item + my $item = $char->inventory->get($invIndex); + if (!defined $item) { + warning TF("Item in index '%d' is not exists.\n", $invIndex); + return; + } elsif ($item->{equipped} != 0) { + warning TF("Cannot select equipped %s (%d) item!\n", $item->{name}, $invIndex); + return; + } + $refineUI->{invIndex} = $invIndex; + message TF("Request info for selected item to refine: %s (%d)\n", $item->{name}, $invIndex); + $messageSender->sendRefineUISelect( $item->{ID}); + return; + + # refineui refine [item_index] [material_id] [catalyst_toggle] + # Do refine + } elsif ($args[0] eq "refine") { + #my ($invIndex, $matInvIndex, $catalyst) = $args =~ /^(\d+) (\d+) (\d+|yes|no)/; + my $invIndex = $args[1]; + my $matNameID = $args[2]; + my $catalyst = $args[3]; + + # Check item + my $item = $char->inventory->get($invIndex); + if (!defined $item) { + warning TF("Item in index '%d' is not exists.\n", $invIndex); + return; + } elsif ($item->{equipped} != 0) { + warning TF("Cannot select equipped %s (%d) item!\n", $item->{name}, $invIndex); + return; + } + + # Check material + my $material = $char->inventory->getByNameID($matNameID); + if (!defined $material) { + warning TF("You don't have enough '%s' (%d) as refine material.\n", itemNameSimple($matNameID), $matNameID); + return; + } + # Check if the selected item is valid material + my $valid = 0; + foreach my $mat (@{$refineUI->{materials}}) { + if ($mat->{nameid} == $matNameID) { + $valid = 1; + } + } + if ($valid != 1) { + warning TF("'%s' (%d) is not valid refine material for '%s'.\n", itemNameSimple($matNameID), $matNameID, $item->{name}); + return; + } + + # Check catalyst toggle + my $useCatalyst = 0; + #my $Blacksmith_Blessing = 6635; # 6635,Blacksmith_Blessing,Blacksmith Blessing + my $blessName = itemNameSimple($Blacksmith_Blessing); + if ($refineUI->{bless} > 0 && ($catalyst == 1 || $catalyst eq "yes")) { + my $catalystItem = $char->inventory->getByNameID($Blacksmith_Blessing); + if (!$catalystItem || $catalystItem->{amount} < $refineUI->{bless}) { + warning TF("You don't have %s for RefineUI. Needed: %d!\n", $blessName, $refineUI->{bless}); + return; + } + $useCatalyst = 1; + } + + my $matStr = $material->{name}; + if ($useCatalyst) { + $matStr .= " and ".$refineUI->{bless}."x ".$blessName; + } + message TF("Refining item: %s with material %s.\n", $item->{name}, $matStr); + $messageSender->sendRefineUIRefine($item->{ID}, $matNameID, $useCatalyst); + return; + } else { + error T("Invalid usage!\n"); + return; + } +} + +sub cmdClan { + my (undef, $args_string) = @_; + my (@args) = parseArgs($args_string, 3); + + if (!$net || $net->getState() != Network::IN_GAME) { + error TF("You must be logged in the game to use this command '%s'\n", shift); + return; + } elsif(!$clan{clan_name}) { + error TF("You must be in a Real Clan to use command '%s'\n", shift); + return; + } + + if ($args[0] eq "info" || $args[0] eq "") { + my $msg = center(T(" Clan Information "), 40, '-') ."\n" . + TF("ClanName : %s\n" . + "Clan Master Name : %s\n" . + "Number of Members : %s/%s\n". + "Castles Owned : %s\n" . + "Ally Clan Count : %s\n" . + "Ally Clan Names: %s\n" . + "Hostile Clan Count: %s\n" . + "Hostile Clan Names: %s\n", + $clan{clan_name}, $clan{clan_master}, $clan{onlineuser}, $clan{totalmembers}, $clan{clan_map}, $clan{alliance_count}, $clan{ally_names}, $clan{antagonist_count}, $clan{antagonist_names}); + $msg .= ('-'x40) . "\n"; + message $msg, "info"; + } +} + +sub cmdElemental { + my (undef, $args_string) = @_; + my (@args) = parseArgs($args_string, 3); + + if (!$net || $net->getState() != Network::IN_GAME) { + error TF("You must be logged in the game to use this command '%s'\n", shift); + return; + } + + if ($args[0] eq "info" || $args[0] eq "") { + if(!$char->{elemental}{ID}) { + error TF("You don't have any elemental. Call an Elemental first.\n"); + return; + } + + my $msg = center(T(" Elemental Information "), 50, '-') ."\n" . + TF("ID: %s (%s)\n". + "Name : %s \n". + "HP: %s/%s (%s\%)\n". + "SP: %s/%s (%s\%)\n". + "Position: %s,%s\n", + unpack('V',$char->{elemental}{ID}), getHex($char->{elemental}{ID}), + $char->{elemental}{name}, + $char->{elemental}{hp}, $char->{elemental}{hp_max}, sprintf("%.2f",$char->{elemental}->hp_percent()), + $char->{elemental}{sp}, $char->{elemental}{sp_max}, sprintf("%.2f",$char->{elemental}->sp_percent()), + $char->{elemental}{'pos'}{'x'},$char->{elemental}{'pos'}{'y'}, + ); + $msg .= ('-'x50) . "\n"; + message $msg, "info"; + + } elsif ($args[0] eq "list" && $args[1] =~ /\d+/) { + my $elemental = $elementalsList->get($args[1]); + if(!$elemental) { + error TF("Elemental \"%s\" does not exist.\n", $args[1]); + } else { + my $pos = calcPosition($elemental); + my $mypos = calcPosition($char); + my $dist = sprintf("%.1f", distance($pos, $mypos)); + $dist =~ s/\.0$//; + + my $msg = center(T(" Elemental Info "), 67, '-') ."\n" . + + TF("%s (%s) \n". + "ID: %s (Hex: %s)\n" . + "Position: %s, %s Distance: %-17s\n" . + "Level: %-7d\n" . + "Class: %s\n" . + "Walk speed: %s secs per block\n", + $elemental->{name}, $elemental->{binID}, + unpack('V',$char->{elemental}{ID}), getHex($char->{elemental}{ID}), + $pos->{x}, $pos->{y}, $dist, + $elemental->{lv}, + $jobs_lut{$elemental->{jobID}}, + $elemental->{walk_speed}); + + $msg .= '-' x 67 . "\n"; + message $msg, "info"; + return; + } + } elsif ($args[0] eq "list") { + my $msg = center(T(" Elemental List "), 79, '-') ."\n". + T("# Name Lv Dist Coord\n"); + for my $elemental (@$elementalsList) { + my ($name, $dist, $pos); + $name = $jobs_lut{$elemental->{jobID}}; + my $elementalpos = calcPosition($elemental); + my $mypos = calcPosition($char); + $dist = sprintf("%.1f", distance($elementalpos, $mypos)); + $dist =~ s/\.0$//; + $pos = '(' . $elemental->{pos}{x} . ', ' . $elemental->{pos}{y} . ')'; + $msg .= swrite( + "@<<< @<<<<<<<<<<<<<<<<<< @<<< @<< @<<<<<<<<<<", + [$elemental->{binID}, $name, $elemental->{lv}, $dist, $pos]); + } + + if (my $elementalsTotal = $elementalsList && $elementalsList->size) { + $msg .= TF("Total elementals: %s \n", $elementalsTotal); + } else {$msg .= T("There are no elementals near you.\n");} + + $msg .= '-' x 79 . "\n"; + message $msg, "list"; + + } else { + error T("Error in function 'elemental'\n" . + "Usage: elemental <info|list [<elemental index>]>\n + info: show info from self elemental.\n + list: list all elementals on screen.\n + list <index number> show information about a specific elemental"); + } +} + +sub cmdCreate { + if (!$net || $net->getState() != Network::IN_GAME) { + error TF("You must be logged in the game to use this command '%s'\n", shift); + return; + } + + my ($cmd, $args) = @_; + my @arg = parseArgs($args); + + if ($arg[0] =~ /^\d+/ && defined $makableList->[$arg[0]]) { # viewID/nameID can be 0 + $arg[1] = 0 if !defined $arg[1]; + $arg[2] = 0 if !defined $arg[2]; + $arg[3] = 0 if !defined $arg[3]; + $messageSender->sendMakeItemRequest($makableList->[$arg[0]], $arg[1], $arg[2], $arg[3]); + } elsif($arg[0] =~ /^\d+/) { + message TF("Item with 'create' index: %s not found.\n", $arg[0]), "info"; + } else { + error T("Error in function 'create'\n" . + "Usage: create <index number> <material 1 nameID> <material 2 nameID> <material 3 nameID>\n". + "material # nameID: can be 0 or undefined.\n"); + } + + undef $makableList; +} + +sub cmdSearchStore { + my ($cmd, $args) = @_; + + if (!$net || $net->getState() != Network::IN_GAME) { + error TF("You must be logged in the game to use this command '%s'\n", $cmd); + return; + } + + my @args = parseArgs($args); + + if (!$universalCatalog{open}) { + error T("Error in function 'searchstore' (universal catalog)\n". + "No catalog in use. You can't use this yet.\n"); + return; + } + + if ($args[0] eq "close") { + $messageSender->sendSearchStoreClose(); + message T("Closed search store catalog\n"); + return; + } + + if ($args[0] eq "next") { + if ($universalCatalog{has_next}) { + $messageSender->sendSearchStoreRequestNextPage(); + message T("Requested next page of search store catalog\n"); + return; + } + error T("Error in function 'searchstore' (universal catalog)\n". + "Already reached the end. There's no next page\n"); + return; + } + + if ($args[0] eq "buy") { + if ($universalCatalog{type} == 0) { + error T("Error in function 'searchstore' (universal catalog)\n". + "You cannot buy with the Silver Catalog.\n"); + return; + } + + if ($venderItemList->size() == 0 || !defined $venderID || !defined $venderCID) { + error T("Error in function 'searchstore' (universal catalog)\n". + "No store selected. Please select a store with 'searchstore select' first\n"); + return; + } + + if ($args[1] eq "end") { + $venderItemList->clear; + undef $venderID; + undef $venderCID; + + return; + } + + if ($args[1] eq "view") { + $messageSender->sendEnteringVender($venderID); + return; + } + + if (scalar @args > 1) { + my $item = $venderItemList->get($args[1]); + + if (!$item) { + error TF("Error in function 'searchstore' (universal catalog)\n". + "Item %s does not exist\n", $args[1]); + return; + } + + my $amount = (scalar @args > 2 && $args[2] >= 0) ? $args[2] : 1; + + $messageSender->sendBuyBulkVender( $venderID, [ { itemIndex => $item->{ID}, amount => $amount } ], $venderCID ); + + return; + } + + error T("Error in function 'searchstore buy' (Buy using a Gold Search Catalog\n". + "Syntax: buy [view|end|<item #> [<amount>]]\n"); + } + + if ($args[0] eq "view") { + if (!scalar(@{$universalCatalog{list}})) { + error T("Error in function 'searchstore view' (store search view page)\n". + "No info available yet\n"); + return; + } elsif ($args[1] + 1 > scalar(@{$universalCatalog{list}})) { + error TF("Error in function 'searchstore view' (store search view page)\n". + "Page %d out of bounds (valid bounds: 0..%d)\n", $args[1], scalar(@{$universalCatalog{list}}) - 1); + return; + } else { + Misc::searchStoreInfo($args[1]); + return; + } + } + + if ($args[0] eq "search") { + my $searchMethod; + + if ($args[1] eq "match") { + $searchMethod = \&containsItemNameToIDList; + } elsif ($args[1] eq "exact") { + $searchMethod = \&itemNameToIDList; + } else { + error T("Error in function 'searchstore search' (store search)\n" . + "Syntax: searchstore search [match|exact] \"<item name>\" [card <card name>] [price <min_price>..<max_price>] [sell|buy]\n"); + + return; + } + + my @ids = $searchMethod->($args[2]); + my @cards; + my @price; + my $type = 0; + + if (!scalar(@ids)) { + error TF("Error in function 'searchstore search' (store search)\n" . + "Item '%s' not found\n", $args[2]); + return; + } + + if ($args[3] eq "card") { + @cards = $searchMethod->($args[4]); + + if ($args[5] eq "price") { + @price = split '..', $args[6]; + } + } elsif ($args[3] eq "price") { + @price = split '..', $args[4]; + + if ($args[5] eq "card") { + @cards = $searchMethod->($args[6]); + } + } + + if ($args[-1] eq "buy") { + $type = 1; + } + + # Limit search size + # I'm not sure about the max size, this needs more testing or might be server-specific, but must exist - lututui + if (scalar @ids + scalar @cards > 15) { + error $msgTable[1785] . "\n"; + return; + } + + $messageSender->sendSearchStoreSearch({ + item_list => \@ids, + card_list => \@cards, + min_price => $price[0], + max_price => $price[1], + type => $type + }); + + return; + } + + if ($args[0] eq "select") { + if (scalar @args > 2) { + if ($args[1] > scalar(@{$universalCatalog{list}}) - 1) { + error TF("Error in function 'searchstore select' (store search select store)\n". + "Page %d out of bounds (valid bounds: [0,%d])\n", $args[1], scalar(@{$universalCatalog{list}}) - 1); + return; + } + + if ($args[2]> scalar(${$universalCatalog{list}}[$args[1]]) - 1) { + error TF("Error in function 'searchstore select' (store search select store)\n". + "Item %d out of bounds (valid bounds: [0,%d])\n", $args[1], scalar(${$universalCatalog{list}}[$args[1]]) - 1); + return; + } + + $messageSender->sendSearchStoreSelect({ + accountID => ${$universalCatalog{list}}[$args[1]][$args[2]]{accountID}, + storeID => ${$universalCatalog{list}}[$args[1]][$args[2]]{storeID}, + nameID => ${$universalCatalog{list}}[$args[1]][$args[2]]{nameID}, + }); + + return; + } + + error T("Error in function 'searchstore select' (select store)\n" . + "Syntax: searchstore select <page #> <store #> \n"); + return; + } + + error T("Syntax error in 'searchstore' command (Universal catalog command)\n" . + "searchstore close : Closes search store catalog\n" . + "searchstore next : Requests catalog next page\n" . + "searchstore view <page #> : Shows catalog page # (0-indexed)\n" . + "searchstore search [match|exact] \"<item name>\" [card \"<card name>\"] [price <min_price>..<max_price>] [sell|buy] : Searches for an item\n" . + "searchstore select <page #> <store #> : Selects a store\n" . + "searchstore buy [view|end|<item #> [<amount>]] : Buys from a store using Universal Catalog Gold\n"); +} + +sub cmdRevive { + my ($cmd, $args) = @_; + + if (!$net || $net->getState() != Network::IN_GAME) { + error TF("You must be logged in the game to use this command '%s'\n", $cmd); + return; + } + + if (!$char->{dead}) { + error TF("You must be dead to use this command '%s'\n", $cmd); + return; + } + + my @args = parseArgs($args); + my $item; + + if (scalar @args == 1) { + # User passed an item nameID + if ($args[0] =~ /^\d+$/) { + $item = $char->inventory->getByNameID($args[0]); + } + # User passed an item name + elsif ($args[0] ne "force") { + $item = $char->inventory->getByName($args[0]); + } + } elsif (scalar @args == 0) { + # Try to find Token Of Siegfried + $item = $char->inventory->getByNameID(7621); + } else { + error T("Error in 'revive' command (incorrect syntax)\n". + "revive [force|\"<item_name>\"|<item_ID>]\n"); + return; + } + + if (!$item && $args[0] ne "force") { + error TF("Error in 'revive' command\n". + "Cannot use item '%s' in attempt to revive: item not found in inventory\n", $args[0]); + return; + } + + if ($item && $args[0] ne "force") { + message TF("Trying to use item '%s' to self-revive\n", $item->name()); + } else { + message TF("Trying to self-revive using 'force'\n"); + } + $messageSender->sendAutoRevive(); +} + +sub cmdCashShopBuy { + if (!$net || $net->getState() != Network::IN_GAME) { + error TF("You must be logged in the game to use this command '%s'\n", shift); + return; + } + + if (!$cashList || $cashList->size < 1) { + error T("No cash shop info to buy\n"); + return; + } + + my (undef, $args) = @_; + my ($points, $items) = $args =~ /(\d+) (.*)/; + my @buylist; + my $cost_total = 0; + foreach (split /\,/, $items) { + my($index, $amount) = $_ =~ /^\s*(\d+)\s*(\d*)\s*$/; + if ($index eq "") { + error T("Syntax Error in function 'cashbuy' (Buy Cash Item)\n" . + "Usage: cashbuy <kafra_points> <item #> [<amount>][, <item #> [<amount>]]...\n"); + return; + + } elsif ($amount eq "" || $amount <= 0) { + $amount = 1; + } + my $item = $cashList->get($index); + if (!$item) { + error TF("Error in function 'cashbuy' (Buy Cash Item)\n" . + "Cash Item at index %s does not exist.\n", $index); + return; + } + $cost_total += $item->{price}; + push (@buylist,{itemID => $item->{nameID}, amount => $amount}); + } + + if (!scalar @buylist) { + error T("Syntax Error in function 'cashbuy' (Buy Cash Item)\n" . + "Usage: cashbuy <kafra_points> <item #> [<amount>][, <item #> [<amount>]]...\n"); + return; + } + + + # TODO: Add check to ignore the cost for private servers + #if (!$cashShop{points} || $cost_total > ($cashShop{points}->{cash} + $cashShop{points}->{kafra})) { + # error TF("You dont' have enough cash and points to buy the items. %d > %d + %d\n", $cost_total, $cashShop{points}->{cash}, $cashShop{points}->{kafra}); + # return; + #} + + message TF("Attempt to buy %d items from cash dealer\n", (scalar @buylist)), "info"; + debug "Buying cash ".(scalar @buylist)." items: ".(join ', ', map {"".$_->{amount}."x ".$_->{itemID}.""} @buylist)."\n", "sendPacket"; + $messageSender->sendCashShopBuy($points, \@buylist); +} + + +## +# 'merge' Merge Item +# @author [Cydh] +## +sub cmdMergeItem { + if (!$net || $net->getState() != Network::IN_GAME) { + error TF("You must be logged in the game to use this command '%s'\n", shift); + return; + } + + if (not defined $mergeItemList) { + error T("You cannot use this command yet. Only available after talking with Mergician-like NPC!\n"); + return; + } + + my ($switch, $args) = @_; + my ($mode) = $args =~ /^(\w+)/; + + if ($mode eq "" || $mode eq "list") { + my $title = TF("Available Items to merge"); + my $msg = center(' '. $title . ' ', 50, '-') ."\n". + T ("# Item Name\n"); + foreach my $itemid (keys %{$mergeItemList}) { + $msg .= "-- ".$mergeItemList->{$itemid}->{name}." (".$itemid.") x ".scalar(@{$mergeItemList->{$itemid}->{list}})."\n"; + foreach my $item (@{$mergeItemList->{$itemid}->{list}}) { + my $display = $item->{info}->{name}." x ".$item->{info}->{amount}; + $msg .= swrite( + "@<<<< @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<", + [$item->{info}->{binID}, $display]); + } + } + $msg .= ('-'x50) . "\n"; + message $msg, "list"; + message T("To merge by item id: merge <itemid>\nOr one-by-one: merge <item #>,<item #>[,...]\n"), "info"; + return; + + } elsif ($mode eq "cancel") { + $messageSender->sendMergeItemCancel(); + message TF("Merge Item is canceled.\n"), "info"; + return; + } + + # Merging process + my @list = split(/,/, $args); + my @items = (); + my $merge_itemid = 0; + + @list = grep(!/^$/, @list); # Remove empty entries + foreach (@list) { + my ($id) = $_ =~ /^(\d+)/; + # Merge by item ID + if ((scalar @list) == 1 && $char->inventory->getByNameID($id)) { + debug "Merge item by item ID $id\n"; + foreach my $item (@{$mergeItemList->{$id}->{list}}) { + push @items, $item; + } + last; + } + + # User defined, however must be same item id + my $found = 0; + foreach my $itemid (keys %{$mergeItemList}) { + foreach my $item (@{$mergeItemList->{$itemid}->{list}}) { + if ($item->{info}->{binID} == $id) { + if ($merge_itemid > 0 && $merge_itemid != $item->{info}->{nameID}) { + error TF("Selected item is not same. Index:'%d' nameID:'%d' first selected:'%d'\n", $id, $item->{info}->{nameID}, $merge_itemid); + return; + } elsif ($merge_itemid == 0) { + $merge_itemid = $item->{info}->{nameID}; + } + push @items, $item; + $found = 1; + last; + } + } + last if ($found == 1); + } + if ($found != 1) { + warning TF("Cannot find item with id '%d'.\n", $id); + } + } + + if (@items > 1) { + my $num = scalar @items; + message T("======== Merge Item List ========\n"); + map { message unpack("v2", $_->{ID})." ".$_->{info}->{name}." (".$_->{info}->{binID}.") x ".$_->{info}->{amount}."\n" } @items; + message "==============================\n"; + $mergeItemList = {}; + $messageSender->sendMergeItemRequest($num, \@items); + return; + } + + error T("No item was selected or at least need 2 same items.\n"); + error T("To merge by item id: merge <itemid>\nOr one-by-one: merge <item #>,<item #>[,...]\n"), "info"; +} + +sub cmdMemorialDungeonDestroy { + if (!$net || $net->getState() != Network::IN_GAME) { + error TF("You must be logged in the game to use this command '%s'\n", shift); + return; + } + + my (undef, $args) = @_; + + if ($args eq "destroy") { + $messageSender->sendMemorialDungeonCommand(3); + } +} + +sub cmdNPCCreateRequest { + if (!$net || $net->getState() != Network::IN_GAME) { + error TF("You must be logged in the game to use this command '%s'\n", shift); + return; + } + + my (undef, $args) = @_; + $args = 'GOLDPCCAFE' if (!defined $args); + + $messageSender->sendNPCCreateRequest($args); +} + +sub cmdEden { + if (!$net || $net->getState() != Network::IN_GAME) { + error TF("You must be logged in the game to use this command '%s'\n", shift); + return; + } + my $item = Misc::getEdenGroupMark(); + if ($item) { + $item->use; + } + else { + error "Error in function 'eden' (Use Eden Group Mark)\nInventory Item Eden Group Mark does not exist.\n"; + } +} + +1; diff --git a/openkore_llm_knowledge/core/src/ErrorHandler.pm b/openkore_llm_knowledge/core/src/ErrorHandler.pm new file mode 100644 index 0000000000..647265b0eb --- /dev/null +++ b/openkore_llm_knowledge/core/src/ErrorHandler.pm @@ -0,0 +1,127 @@ +######################################################################### +# OpenKore - Default error handler +# +# Copyright (c) 2006 OpenKore Development Team +# +# This software is open source, licensed under the GNU General Public +# License, version 2. +# Basically, this means that you're allowed to modify and distribute +# this software. However, if you distribute modified versions, you MUST +# also distribute the source code. +# See http://www.gnu.org/licenses/gpl.html for the full license. +######################################################################### +## +# MODULE DESCRIPTION: Default error handler. +# +# This module displays a nice error dialog to the user if the program crashes +# unexpectedly. +# +# To use this feature, simply type 'use ErrorHandler'. +package ErrorHandler; + +use strict; +use Carp; +use Scalar::Util; +use Globals; +use utf8; +use Translation; + +sub showError { + $net->serverDisconnect() if ($net); + if ($bus) { + $bus->close(); + undef $bus; + } + + if (!$Globals::interface || UNIVERSAL::isa($Globals::interface, "Interface::Startup") || UNIVERSAL::isa($Globals::interface, "Interface::Socket")) { + print TF("%s\nPress ENTER to exit this program.\n", $_[0]); + <STDIN>; + } else { + $Globals::interface->errorDialog($_[0]); + } +} + +sub errorHandler { + return unless (defined $^S && $^S == 0); + my $e = $_[0]; + + # Get the error message, and extract file and line number. + my ($file, $line, $errorMessage); + if (UNIVERSAL::isa($e, 'Exception::Class::Base')) { + $file = $e->file; + $line = $e->line; + $errorMessage = $e->message; + } else { + ($file, $line) = $e =~ / at (.+?) line (\d+)\.$/; + # Get rid of the annoying "@INC contains:" + $errorMessage = $e; + $errorMessage =~ s/ \(\@INC contains: .*\)//; + } + $errorMessage =~ s/[\r\n]+$//s; + + # Create the message to be displayed to the user. + my $display = TF("This program has encountered an unexpected problem. This is probably because " . + "of a recent server update, a bug in this program, or in one of the plugins. " . + "We apologize for this problem. You may get support from IRC or the forums.\n\n" . + "A detailed error report has been saved to errors.txt. Before posting a bug " . + "report, please try out the latest release GIT version first. If you are already using the latest release GIT " . + "version, search the forums first to see if your problem had already been solved, " . + "or has already been reported. If you truly believe you have encountered a bug in " . + "the program, please include the contents of the errors.txt in your bug report " . + "(https://github.com/openkore/openkore/issues), or we may not be able to help you!\n\n" . + "The error message is:\n" . + "%s", + $errorMessage); + + # Create the errors.txt error log. + my $log = ''; + $log .= "$Settings::NAME version ${Settings::VERSION}${Settings::SVN}\n" if (defined $Settings::VERSION); + $log .= "\@ai_seq = @Globals::ai_seq\n" if (@Globals::ai_seq); + $log .= "Network state = $Globals::conState\n" if (defined $Globals::conState); + $log .= "Network handler = " . Scalar::Util::blessed($Globals::net) . "\n" if ($Globals::net); + $log .= "Revision: " . Settings::getRevisionString() . "\n"; + if (@Plugins::plugins) { + $log .= "Loaded plugins:\n"; + foreach my $plugin (@Plugins::plugins) { + next if (!defined $plugin); + $log .= " $plugin->{filename} ($plugin->{name}; description: $plugin->{description})\n"; + } + } else { + $log .= "No loaded plugins.\n"; + } + $log .= "\nError message:\n$errorMessage\n\n"; + + # Add stack trace to errors.txt. + if (UNIVERSAL::isa($e, 'Exception::Class::Base')) { + $log .= "Stack trace:\n"; + $log .= $e->trace(); + } elsif (defined &Carp::longmess) { + $log .= "Stack trace:\n"; + my $e = $errorMessage; + $log .= Carp::longmess("$e\n"); + } + $log =~ s/\n+$//s; + + # Find out which line died. + if (defined $file && defined $line && -f $file && open(F, "<", $file)) { + my @lines = <F>; + close F; + + my $msg; + $msg .= " $lines[$line-2]" if ($line - 2 >= 0); + $msg .= "* $lines[$line-1]"; + $msg .= " $lines[$line]" if (@lines > $line); + $msg .= "\n" unless $msg =~ /\n$/s; + $log .= TF("\n\nDied at this line:\n%s\n", $msg); + } + + if (open(F, ">:utf8", "errors.txt")) { + print F $log; + close F; + } + showError($display); +} + +$SIG{__DIE__} = \&errorHandler; + +1; diff --git a/openkore_llm_knowledge/core/src/FileParsers.pm b/openkore_llm_knowledge/core/src/FileParsers.pm new file mode 100644 index 0000000000..35b16aeb25 --- /dev/null +++ b/openkore_llm_knowledge/core/src/FileParsers.pm @@ -0,0 +1,1607 @@ +######################################################################### +# OpenKore - Config File Parsers +# +# This software is open source, licensed under the GNU General Public +# License, version 2. +# Basically, this means that you're allowed to modify and distribute +# this software. However, if you distribute modified versions, you MUST +# also distribute the source code. +# See http://www.gnu.org/licenses/gpl.html for the full license. +# +# $Revision$ +# $Id$ +# +######################################################################### +## +# MODULE DESCRIPTION: Configuration file parsers +# +# This module contains functions for parsing an writing config/* and +# tables/* files. + +package FileParsers; + +use strict; +use File::Spec; +use Exporter; +use base qw(Exporter); +#use utf8; +use Carp; +use Text::Balanced qw(extract_delimited); + +use Utils; +use Utils::TextReader; +use Plugins; +use Settings; +use Log qw(warning error debug); +use Translation qw/T TF/; + +our @EXPORT = qw( + parseAchievementFile + parseAttendanceRewards + parseArrayFile + parseAvoidControl + parseChatResp + parseConfigFile + parseDataFile + parseDataFile_lc + parseDataFile2 + parseEmotionsFile + parseItemsControl + parseList + parseNPCs + parseMonControl + parsePortals + parsePortalsLOS + parsePortalsCommands + parsePortalsSpawns + parsePortalsAirship + parsePriority + parseResponses + parseRecvpackets + parseROLUT + parseRODescLUT + parseROSlotsLUT + parseROQuestsLUT + parseSectionedFile + parseShopControl + parseSkillsSPLUT + parseTimeouts + parseWaypoint + parseItemStackLimit + processUltimate + writeDataFile + writeDataFileIntact + writeDataFileIntact2 + writePortalsLOS + writeSectionedFileIntact + updateMonsterLUT + updatePortalLUT + updatePortalLUT2 + updateNPCLUT +); + +## +# parseAchievementFile(file, achievments) +# file: Filename to parse +# achievments: Return hash +# +# Parses a achievments file. +sub parseAchievementFile { + my $file = shift; + my $r_hash = shift; + + undef %{$r_hash}; + + my $reader = new Utils::TextReader($file); + my $current_id; + + while (!$reader->eof()) { + my $line = $reader->readLine(); + $line =~ s/^\s+|\s+$//g; + if ( $line =~ /^\[(\d+)\]\s+\=\s+\{$/ ) { + $current_id = $1; + $r_hash->{$1}->{ID} = $1; + } elsif ( $line =~ /^group\s+\=\s+\"(.*)\"\,$/ ) { + $r_hash->{$current_id}->{group} = $1; + } elsif ( $line =~ /^title\s+\=\s+\"(.*)\"\,$/ ) { + $r_hash->{$current_id}->{title} = $1; + } elsif ( $line =~ /^content\s+\=\s+\{\s+/ ) { + if ( $line =~ /summary\s+\=\s+\"(.*)\"\,/ ) { + $r_hash->{$current_id}->{summary} = $1; + } + if ( $line =~ /details\s+\=\s+\"(.*)\"/ ) { + $r_hash->{$current_id}->{details} = $1; + } + } elsif ( $line =~ /^summary\s+\=\s+\"(.*)\"\,$/ ) { + $r_hash->{$current_id}->{summary} = $1; + } elsif ( $line =~ /^details\s+\=\s+\"(.*)\"/ ) { + $r_hash->{$current_id}->{details} = $1; + } elsif ( $line =~ /^reward\s+\=\s+\{/ ) { + if ( $line =~ /buff\s+\=\s+(\d+)/ ) { + $r_hash->{$current_id}->{rewards}->{buff} = $1; + } + if ( $line =~ /title\s+\=\s+(\d+)/ ) { + $r_hash->{$current_id}->{rewards}->{title} = $1; + } + if ( $line =~ /item\s+\=\s+(\d+)/ ) { + $r_hash->{$current_id}->{rewards}->{item} = $1; + } + } + } + + return 1; +} + +sub parseArrayFile { + my $file = shift; + my $r_array = shift; + my $options = shift; + undef @{$r_array}; + + my @lines; + my $reader = new Utils::TextReader($file, $options); + while (!$reader->eof()) { + my $line = $reader->readLine(); + $line =~ s/[\r\n\x{FEFF}]//g; + $line =~ s/\#$//g; # support msgstringtable.txt + push @lines, $line; + } + @{$r_array} = scalar(@lines) + 1; + my $i = 1; + foreach (@lines) { + $r_array->[$i] = $_; + $i++; + } + return 1; +} + +sub parseAvoidControl { + my $file = shift; + my $r_hash = shift; + undef %{$r_hash}; + my ($key,@args,$args); + my $reader = new Utils::TextReader($file); + + my $section = ""; + while (!$reader->eof()) { + my $line = $reader->readLine(); + $line =~ s/\x{FEFF}//g; + next if ($line =~ /^#/); + $line =~ s/[\r\n]//g; + $line =~ s/\s+$//g; + + next if ($line eq ""); + + if ($line =~ /^\[(.*)\]$/) { + $section = $1; + next; + + } else { + ($key, $args) = lc($line) =~ /([\s\S]+?)[\s]+(\d+[\s\S]*)/; + @args = split / /,$args; + if ($key ne "") { + $r_hash->{$section}{$key}{disconnect_on_sight} = $args[0]; + $r_hash->{$section}{$key}{teleport_on_sight} = $args[1]; + $r_hash->{$section}{$key}{disconnect_on_chat} = $args[2]; + } + } + } + return 1; +} + +sub parseChatResp { + my $file = shift; + my $r_array = shift; + + my $reader = new Utils::TextReader($file); + while (!$reader->eof()) { + my $line = $reader->readLine(); + $line =~ s/[\r\n\x{FEFF}]//g; + next if ($line eq "" || $line =~ /^#/); + if ($line =~ /^first_resp_/) { + Log::error(Translation::T("The chat_resp.txt format has changed. Please read News.txt and upgrade to the new format.\n")); + return; + } + + my ($key, $value) = split /\t+/, lc($line), 2; + my @input = split /,+/, $key; + my @responses = split /,+/, $value; + + foreach my $word (@input) { + my %args = ( + word => $word, + responses => \@responses + ); + push @{$r_array}, \%args; + } + } + return 1; +} +=pod +sub parseCommandsDescription { + my $file = shift; + my $r_hash = shift; + my $no_undef = shift; + + undef %{$r_hash} unless $no_undef; + my ($key, $commentBlock, $description); + + my $reader = new Utils::TextReader($file); + while (!$reader->eof()) { + my $line = $reader->readLine(); + $line =~ s/\x{FEFF}//g; + next if ($line =~ /^[\s\t]*#/); + $line =~ s/[\r\n]//g; # Remove line endings + $line =~ s/^[\t\s]*//; # Remove leading tabs and whitespace + $line =~ s/\s+$//g; # Remove trailing whitespace + next if ($line eq ""); + + if (!defined $commentBlock && $line =~ /^\/\*/) { + $commentBlock = 1; + next; + + } elsif ($line =~ m/\*\/$/) { + undef $commentBlock; + next; + + } elsif (defined $commentBlock) { + next; + + } elsif ($description) { + $description = 0; + push @{$r_hash->{$key}}, $line; + + } elsif ($line =~ /^\[(\w+)\]$/) { + $key = $1; + $description = 1; + $r_hash->{$key} = []; + + } elsif ($line =~ /^(.*?)\t+(.*)$/) { + push @{$r_hash->{$key}}, [$1, $2]; + + } elsif ($line !~ /\t/) { #if a console command is without any params + push @{$r_hash->{$key}}, ["", $line]; + } + + } + return 1; +} +=cut +sub parseConfigFile { + my $file = shift; + my $r_hash = shift; + my $no_undef = shift; + my $blocks = (shift) || {}; + + undef %{$r_hash} unless $no_undef; + my ($key, $value, $inBlock, $commentBlock); + + my $reader = new Utils::TextReader($file); + while (!$reader->eof()) { + my $line = $reader->readLine(); + $line =~ s/\x{FEFF}//g; + next if ($line =~ /^[\s\t]*#/); + $line =~ s/[\r\n]//g; # Remove line endings + $line =~ s/^[\t\s]*//; # Remove leading tabs and whitespace + $line =~ s/\s+$//g; # Remove trailing whitespace + + if (index($line, '#') != -1) { + warning T("Mid-line comments are not allowed in this file and therefore might cause problems.\n"), "parseConfigFile", 5; + warning T("If the '#' found is not a comment, this can be ignored.\n"), "parseConfigFile", 5; + warning TF("HERE: %s", $line), "parseConfigFile", 5; + } + + next if ($line eq ""); + + if (!defined $commentBlock && $line =~ /^\/\*/) { + $commentBlock = 1; + next; + + } elsif (defined $commentBlock && $line =~ m/\*\/$/) { + undef $commentBlock; + next; + + } elsif (defined $commentBlock) { + next; + + } elsif (!defined $inBlock && $line =~ /{$/) { + # Begin of block + $line =~ s/ *{$//; + ($key, $value) = $line =~ /^(.*?)\s+(.*)/; + $key = $line if ($key eq ''); + + if (!exists $blocks->{$key}) { + $blocks->{$key} = 0; + } else { + $blocks->{$key}++; + } + if ($key ne 'teleportAuto'){ + $inBlock = "${key}_$blocks->{$key}"; + } else { + $inBlock = "${key}"; + } + $r_hash->{$inBlock} = $value; + + } elsif (defined $inBlock && $line eq "}") { + # End of block + undef $inBlock; + + } else { + # Option + ($key, $value) = $line =~ /^(.*?) (.*)/; + if ($key eq "") { + $key = $line; + $key =~ s/ *$//; + } + #$value = $r_hash->{$1} if ($value =~ /^constant\.(.*)/); + $key = "${inBlock}_${key}" if (defined $inBlock); + + if ($key eq "!include") { + # Process special !include directives + # The filename can be relative to the current file + my $f = $value; + if (!File::Spec->file_name_is_absolute($value) && $value !~ /^\//) { + if ($file =~ /[\/\\]/) { + $f = $file; + $f =~ s/(.*)[\/\\].*/$1/; + $f = File::Spec->catfile($f, $value); + } else { + $f = $value; + } + } + if (-f $f) { + my $ret = parseConfigFile($f, $r_hash, 1, $blocks); + return $ret unless $ret; + } else { + error Translation::TF("%s: Include file not found: %s\n", $file, $f); + return 0; + } + + } else { + $r_hash->{$key} = $value; + } + } + } + + if ($inBlock) { + error Translation::TF("%s: Unclosed { at EOF\n", $file); + return 0; + } + return 1; +} + +sub parseEmotionsFile { + my $file = shift; + my $r_hash = shift; + undef %{$r_hash}; + my ($key, $word, $name); + my $reader = new Utils::TextReader($file); + while (!$reader->eof()) { + my $line = $reader->readLine(); + $line =~ s/\x{FEFF}//g; + next if ($line =~ /^#/); + $line =~ s/[\r\n]//g; + $line =~ s/\s+$//g; + + ($key, $word, $name) = $line =~ /^(\d+) (\S+) (.*)$/; + + if ($key ne "") { + $$r_hash{$key}{command} = $word; + $$r_hash{$key}{display} = $name; + } + } + return 1; +} + + +sub parseDataFile { + my $file = shift; + my $r_hash = shift; + undef %{$r_hash}; + my ($key,$value); + my $reader = new Utils::TextReader($file); + while (!$reader->eof()) { + my $line = $reader->readLine(); + $line =~ s/\x{FEFF}//g; + next if ($line =~ /^#/); + $line =~ s/[\r\n]//g; + $line =~ s/\s+$//g; + $line =~ s/\s*#.*// if ($file eq Settings::getControlFilename("routeweights.txt")); + next unless length $line; + ($key, $value) = $line =~ /([\s\S]*) ([\s\S]*?)$/; + if ($key ne "" && $value ne "") { + $$r_hash{$key} = $value; + } + } + return 1; +} + +sub parseDataFile_lc { + my $file = shift; + my $r_hash = shift; + undef %{$r_hash}; + my ($key,$value); + my $reader = new Utils::TextReader($file); + while (!$reader->eof()) { + my $line = $reader->readLine(); + $line =~ s/\x{FEFF}//g; + next if ($line =~ /^#/); + $line =~ s/[\r\n]//g; + $line =~ s/\s+$//g; + $line =~ s/\s*#.*// if ($file eq Settings::getControlFilename('pickupitems.txt') or $file eq Settings::getControlFilename("arrowcraft.txt")); + next unless length $line; + ($key, $value) = $line =~ /([\s\S]*) ([\s\S]*?)$/; + if ($key ne "" && $value ne "") { + $$r_hash{lc($key)} = $value; + } + } + return 1; +} + +sub parseDataFile2 { + my ($file, $r_hash) = @_; + + %{$r_hash} = (); + my $reader = new Utils::TextReader($file); + while (!$reader->eof()) { + my $line = $reader->readLine(); + $line =~ s/\x{FEFF}//g; + next if ($line =~ /^#/); + $line =~ s/[\r\n]//g; + next if (length($line) == 0); + + my ($key, $value) = split / /, $line, 2; + $key =~ s/^(0x[0-9a-f]+)$/hex $1/e; + $r_hash->{$key} = $value; + } + close FILE; + + return 1; +} + +sub parseList { + my $file = shift; + my $r_hash = shift; + + undef %{$r_hash}; + + my $reader = new Utils::TextReader($file); + while (!$reader->eof()) { + my $line = $reader->readLine(); + chomp($line); + $r_hash->{$line} = 1; + } + return 1; +} + +## +# parseShopControl(file, shop) +# file: Filename to parse +# shop: Return hash +# +# Parses a shop control file. The shop control file should have the shop title +# on its first line, followed by "$item\t$price\t$quantity" on subsequent +# lines. Blank lines, or lines starting with "#" are ignored. If $price +# contains commas, then they are checked for syntactical validity (e.g. +# "1,000,000" instead of "1,000,00") to protect the user from entering the +# wrong price. If $quantity is missing, all available will be sold. +# +# Example: +# My Shop! +# Poring Card 1,000 +# Andre Card 1,000,000 3 +# +8 Chain [3] 500,000 +sub parseShopControl { + my ($file, $shop) = @_; + + %{$shop} = (); + my $reader = new Utils::TextReader($file); + + # Read shop items + $shop->{items} = []; + my $linenum = 0; + my @errors = (); + my $line; + + while (!$reader->eof()) { + $line = $reader->readLine(); + $linenum++; + chomp; + $line =~ s/[\r\n\x{FEFF}]//g; + next if $line =~ /^$/ || $line =~ /^#/; + + if (!$shop->{title_line}) { + $shop->{title_line} = $line; + next; + } + + # Strip mid-line comments after parsing the shop title, so shops can use '#' in their titles + $line =~ s/\s*#.*//; + next unless length $line; + + my ($name, $price, $amount) = split(/\t+/, $line); + $price =~ s/^\s+//g; + $amount =~ s/^\s+//g; + my $real_price = $price; + $real_price =~ s/,//g; + + my ($priceMax, $priceCommas, $priceMaxCommas); + if ($real_price =~ /../) { + ($real_price, $priceMax) = split(/\../, $real_price); + ($priceCommas, $priceMaxCommas) = split(/\../, $price); + } + + my $loc = Translation::TF("Line %s: Item '%s'", $linenum, $name); + if ($real_price !~ /^\d+$/) { + push(@errors, Translation::TF("%s has non-integer price: %s", $loc, $price)); + } elsif (!$priceMax && ($price ne $real_price && formatNumber($real_price) ne $price) || + $priceMax && ($priceCommas ne $real_price && formatNumber($real_price) ne $priceCommas || + $priceMaxCommas ne $priceMax && formatNumber($priceMax) ne $priceMaxCommas)) + { + push(@errors, Translation::TF("%s has incorrect comma placement in price: %s", $loc, $price)); + } elsif ($real_price < 0) { + push(@errors, Translation::TF("%s has non-positive price: %s", $loc, $price)); + } elsif ($real_price > 1000000000) { + push(@errors, Translation::TF("%s has price over 1,000,000,000: %s", $loc, $price)); + } elsif ($amount > 30000) { + push(@errors, Translation::TF("%s has amount over 30,000: %s", $loc, $amount)); + } + + push(@{$shop->{items}}, {name => $name, price => $real_price, priceMax => $priceMax, amount => $amount}); + } + + if (@errors) { + error Translation::TF("Errors were found in %s:\n", $file); + foreach (@errors) { error("$_\n"); } + error Translation::TF("Please correct the above errors and type 'reload %s'.\n", $file); + return 0; + } + return 1; +} + +sub parseItemsControl { + my ($file, $r_hash) = @_; + undef %{$r_hash}; + my ($key, $args_text, %cache); + + my $reader = new Utils::TextReader($file); + until ($reader->eof) { + my $line = lc $reader->readLine; + chomp $line; + next if $line =~ /^\s*#/; + + if($line =~ /^[\s0-9]+/) { + ($key, $args_text) = $line =~ /^(\d+)\s(.*)$/; + } elsif(($key, $args_text) = extract_delimited($line) and $key) { + $key =~ s/^.|.$//g; + $args_text =~ s/^\s+//; + } else { + $line =~ s/#.*//; + chomp $line; + my @reverseString = reverse(split(//, $line)); + my $separator = length $line; + + foreach my $c (@reverseString) { + last if $c =~ /[^\s0-9]/; + --$separator; + } + + $args_text = substr($line, $separator); + $args_text =~ s/^\s+//; + $key = substr($line, 0, $separator); + } + + next if $key =~ /^$/; + $args_text =~ s/#.*//; + chomp $args_text; + my @args = split /\s+/, $args_text; + # Cache similar entries to save memory. + $r_hash->{$key} = $cache{$args_text} ||= { map {$_ => shift @args} qw(keep storage sell cart_add cart_get) }; + } + return 1; +} + +sub parseNPCs { + my $file = shift; + my $r_hash = shift; + my ($i, $string); + undef %{$r_hash}; + my ($key, $value, @args); + my $reader = new Utils::TextReader($file); + while (!$reader->eof()) { + my $line = $reader->readLine(); + $line =~ s/\cM|\cJ//g; + $line =~ s/^\s+|\s+$//g; + next if $line =~ /^#/ || $line eq ''; + #izlude 135 78 Charfri + my ($map,$x,$y,$name) = split /\s+/, $line,4; + next unless $name; + $$r_hash{"$map $x $y"} = $name; + } + return 1; +} + +sub parseMonControl { + my $file = shift; + my $r_hash = shift; + undef %{$r_hash}; + my ($key,@args,$args); + + my $reader = new Utils::TextReader($file); + while (!$reader->eof()) { + my $line = $reader->readLine(); + $line =~ s/\x{FEFF}//g; + next if ($line =~ /^#/); + $line =~ s/[\r\n]//g; + $line =~ s/\s+$//g; + $line =~ s/\s*#.*//; + next unless length $line; + + if ($line =~ /\t/) { + ($key, $args) = split /\t+/, lc($line); + } else { + ($key, $args) = lc($line) =~ /([\s\S]+?) ([\-\d\.]+[\s\S]*)/; + } + + @args = split / /, $args; + if ($key ne "") { + $r_hash->{$key}{attack_auto} = $args[0]; + $r_hash->{$key}{teleport_auto} = $args[1]; + $r_hash->{$key}{teleport_search} = $args[2]; + $r_hash->{$key}{skillcancel_auto} = $args[3]; + $r_hash->{$key}{attack_lvl} = $args[4]; + $r_hash->{$key}{attack_jlvl} = $args[5]; + $r_hash->{$key}{attack_hp} = $args[6]; + $r_hash->{$key}{attack_sp} = $args[7]; + $r_hash->{$key}{weight} = $args[8]; + } + } + return 1; +} + +sub parsePortals { + my $file = shift; + my $r_hash = shift; + my $r_array = shift; + undef %{$r_hash}; + undef @{$r_array}; + my $reader = new Utils::TextReader($file); + while (!$reader->eof()) { + my $line = $reader->readLine(); + next if $line =~ /^#/; + $line =~ s/\cM|\cJ//g; + $line =~ s/\s+/ /g; + $line =~ s/^\s+|\s+$//g; + $line =~ s/(.*)[\s\t]+#.*$/$1/; + + if ($line =~ /^([\w|@|-]+)\s(\d{1,3})\s(\d{1,3})\s([\w|@|-]+)\s(\d{1,3})\s(\d{1,3})\s?(.*)/) { + my ($source_map, $source_x, $source_y, $dest_map, $dest_x, $dest_y, $misc) = ($1, $2, $3, $4, $5, $6, $7); + my $portal = "$source_map $source_x $source_y"; + my $dest = "$dest_map $dest_x $dest_y"; + $$r_hash{$portal}{'source'}{'map'} = $source_map; + $$r_hash{$portal}{'source'}{'x'} = $source_x; + $$r_hash{$portal}{'source'}{'y'} = $source_y; + $$r_hash{$portal}{'dest'}{$dest}{'map'} = $dest_map; + $$r_hash{$portal}{'dest'}{$dest}{'x'} = $dest_x; + $$r_hash{$portal}{'dest'}{$dest}{'y'} = $dest_y; + $$r_hash{$portal}{dest}{$dest}{enabled} = 1; # is available permanently (can be used when calculating a route) + #$$r_hash{$portal}{dest}{$dest}{active} = 1; # TODO: is available right now (otherwise, wait until it becomes available) + if ($misc =~ /^(\d+)\s(\d)\s(.*)$/) { # [cost] [allow_ticket] [talk sequence] + $$r_hash{$portal}{'dest'}{$dest}{'cost'} = $1; + $$r_hash{$portal}{'dest'}{$dest}{'allow_ticket'} = $2; + $$r_hash{$portal}{'dest'}{$dest}{'steps'} = $3; + } elsif ($misc =~ /^(\d+)\s(.*)$/) { # [cost] [talk sequence] + $$r_hash{$portal}{'dest'}{$dest}{'cost'} = $1; + $$r_hash{$portal}{'dest'}{$dest}{'allow_ticket'} = 0; + $$r_hash{$portal}{'dest'}{$dest}{'steps'} = $2; + } else { # [talk sequence] + $$r_hash{$portal}{'dest'}{$dest}{'cost'} = 0; + $$r_hash{$portal}{'dest'}{$dest}{'allow_ticket'} = 0; + $$r_hash{$portal}{'dest'}{$dest}{'steps'} = $misc; + } + } + + } + return 1; +} + +sub parsePortalsCommands { + my $file = shift; + my $r_hash = shift; + my $r_array = shift; + undef %{$r_hash}; + undef @{$r_array}; + my $reader = new Utils::TextReader($file); + while (!$reader->eof()) { + my $line = $reader->readLine(); + next if $line =~ /^#/; + $line =~ s/\cM|\cJ//g; + $line =~ s/\s+/ /g; + $line =~ s/^\s+|\s+$//g; + $line =~ s/(.*)[\s\t]+#.*$/$1/; + + if ($line =~ /^(\@go\s\d{1,3})\s([\w|@|-]+)\s(\d{1,3})\s(\d{1,3})/) { + my ($command, $dest_map, $dest_x, $dest_y) = ($1, $2, $3, $4); + my $portal = $command; + my $dest = "$dest_map $dest_x $dest_y"; + $$r_hash{$dest}{'dest'}{$dest}{'command'} = $portal; + $$r_hash{$dest}{'dest'}{$dest}{'map'} = $dest_map; + $$r_hash{$dest}{'dest'}{$dest}{'x'} = $dest_x; + $$r_hash{$dest}{'dest'}{$dest}{'y'} = $dest_y; + $$r_hash{$dest}{dest}{$dest}{enabled} = 1; # is available permanently (can be used when calculating a route) + } + } + return 1; +} + +sub parsePortalsSpawns { + my $file = shift; + my $r_hash = shift; + my $r_array = shift; + undef %{$r_hash}; + undef @{$r_array}; + my $reader = new Utils::TextReader($file); + while (!$reader->eof()) { + my $line = $reader->readLine(); + next if $line =~ /^#/; + $line =~ s/\cM|\cJ//g; + $line =~ s/\s+/ /g; + $line =~ s/^\s+|\s+$//g; + $line =~ s/(.*)[\s\t]+#.*$/$1/; + + if ($line =~ /^savepoint\s+"([\w-]+)"\s*,\s*(\d+)\s*,\s*(\d+)\s*;$/) { + my ($dest_map, $dest_x, $dest_y) = ($1, $2, $3); + my $dest = "$dest_map $dest_x $dest_y"; + $$r_hash{$dest}{'dest'}{$dest}{'map'} = $dest_map; + $$r_hash{$dest}{'dest'}{$dest}{'x'} = $dest_x; + $$r_hash{$dest}{'dest'}{$dest}{'y'} = $dest_y; + $$r_hash{$dest}{dest}{$dest}{enabled} = 1; # is available permanently (can be used when calculating a route) + } + } + return 1; +} + +sub parsePortalsAirship { + my $file = shift; + my $r_hash = shift; + my $r_array = shift; + undef %{$r_hash}; + undef @{$r_array}; + my $reader = new Utils::TextReader($file); + while (!$reader->eof()) { + my $line = $reader->readLine(); + next if $line =~ /^#/; + $line =~ s/\cM|\cJ//g; + $line =~ s/\s+/ /g; + $line =~ s/^\s+|\s+$//g; + $line =~ s/(.*)[\s\t]+#.*$/$1/; + + #airpship "airplane_01","We are heading to Izlude.","izlude",200,73; + if ($line =~ /^airpship\s+"([\w-]+)"\s*,\s*(\d+)\s*,\s*(\d+)\s*,\s*"([^"]+)"\s*,\s*"([\w-]+)"\s*,\s*(\d+)\s*,\s*(\d+)\s*(,\s*(.+)\s*)?;$/) { + my ($source_map, $source_x, $source_y, $message, $dest_map, $dest_x, $dest_y, $steps) = ($1, $2, $3, $4, $5, $6, $7, $9); + my $portal = "$source_map $source_x $source_y"; + my $dest = "$dest_map $dest_x $dest_y"; + $$r_hash{$portal}{'source'}{'map'} = $source_map; + $$r_hash{$portal}{'source'}{'x'} = $source_x; + $$r_hash{$portal}{'source'}{'y'} = $source_y; + $$r_hash{$portal}{'dest'}{$dest}{'map'} = $dest_map; + $$r_hash{$portal}{'dest'}{$dest}{'x'} = $dest_x; + $$r_hash{$portal}{'dest'}{$dest}{'y'} = $dest_y; + $$r_hash{$portal}{'dest'}{$dest}{'message'} = $message; + $$r_hash{$portal}{'dest'}{$dest}{enabled} = 1; # is available permanently (can be used when calculating a route) + if (defined $steps && $steps) { + $$r_hash{$portal}{'dest'}{$dest}{'steps'} = $steps; + } + #$$r_hash{$portal}{'dest'}{$dest}{active} = 0; + } + } + return 1; +} + +sub parsePortalsLOS { + my $file = shift; + my $r_hash = shift; + undef %{$r_hash}; + my $key; + open FILE, "<", $file; + foreach (<FILE>) { + s/\x{FEFF}//g; + next if (/^#/); + s/[\r\n]//g; + s/\s+/ /g; + s/\s+$//g; + my @args = split /\s/, $_; + if (@args) { + my $map = shift @args; + my $x = shift @args; + my $y = shift @args; + for (my $i = 0; $i < @args; $i += 4) { + $$r_hash{"$map $x $y"}{"$args[$i] $args[$i+1] $args[$i+2]"} = $args[$i+3]; + } + } + } + close FILE; + return 1; +} + +sub parsePriority { + my $file = shift; + my $r_hash = shift; + return unless my $reader = new Utils::TextReader($file); + undef %{$r_hash}; + + my @lines; + while (!$reader->eof()) { + push @lines, $reader->readLine(); + } + my $pri = $#lines; + foreach (@lines) { + s/\x{FEFF}//g; + next if (/^#/); + s/[\r\n]//g; + s/\s*#.*//; + next unless length; + $$r_hash{lc($_)} = $pri + 1; + $pri--; + } + return 1; +} + +sub parseResponses { + my $file = shift; + my $r_hash = shift; + undef %{$r_hash}; + my ($i, $key,$value); + my $reader = new Utils::TextReader($file); + while (!$reader->eof()) { + my $line = $reader->readLine(); + $line =~ s/\x{FEFF}//g; + next if ($line =~ /^#/); + $line =~ s/[\r\n]//g; + ($key, $value) = $line =~ /([\s\S]*?) ([\s\S]*)$/; + if ($key ne "" && $value ne "") { + $i = 0; + while ($$r_hash{"$key\_$i"} ne "") { + $i++; + } + $$r_hash{"$key\_$i"} = $value; + } + } + return 1; +} + +sub parseROLUT { + my ($file, $r_hash, $flag, $ext) = @_; + + my %ret = ( + file => $file, + hash => $r_hash + ); + Plugins::callHook('FileParsers::ROLUT', \%ret); + return if ($ret{return}); + + undef %{$r_hash}; + my $reader = new Utils::TextReader($file); + while (!$reader->eof()) { + my $line = $reader->readLine(); + $line =~ s/[\r\n\x{FEFF}]//g; + next if (length($line) == 0 || $line =~ /^\/\// || ($ext && $line !~ /$ext#/)); + + my ($id, $name) = split /$ext#/, $line, 3; + if ($id ne "" && $name ne "") { + $name =~ s/_/ /g unless ($flag == 1); + $name =~ s/^\s+|\s+$//g; + $r_hash->{$id} = $name; + } + } + return 1; +} + +sub parseRODescLUT { + my ($file, $r_hash) = @_; + + my %ret = ( + file => $file, + hash => $r_hash + ); + Plugins::callHook('FileParsers::RODescLUT', \%ret); + return if ($ret{return}); + + undef %{$r_hash}; + my $ID; + my $IDdesc; + my $reader = Utils::TextReader->new($file, { hide_comments => 0 }); + until ($reader->eof) { + $_ = $reader->readLine; + s/\r//g; + if (/^#/) { + $$r_hash{$ID} = $IDdesc; + undef $ID; + undef $IDdesc; + } elsif (!$ID) { + ($ID) = /([\s\S]+)#/; + } else { + $IDdesc .= $_; + $IDdesc =~ s/\^......//g; + $IDdesc =~ s/_/--------------/g; + } + } + return 1; +} + +sub parseROSlotsLUT { + my $file = shift; + my $r_hash = shift; + undef %{$r_hash}; + my $ID; + open FILE, $file; + foreach (<FILE>) { + if (!$ID) { + ($ID) = /(\d+)#/; + } else { + ($$r_hash{$ID}) = /(\d+)#/; + undef $ID; + } + } + close FILE; + return 1; +} + +sub parseROQuestsLUT { + my ($file, $r_hash) = @_; + + my %ret = ( + file => $file, + hash => $r_hash, + ); + Plugins::callHook ('FileParsers::ROQuestsLUT', \%ret); + return if $ret{return}; + + undef %{$r_hash}; + my ($data, $flag); + my $reader = Utils::TextReader->new($file, { hide_comments => 0 }); + until ($reader->eof) { + $_ = $reader->readLine; + s/\r//g; + if (/^(\d+)#([^#]*)#([A-Z_]*)#([A-Z_]*)#/) { + $data = { + id => $1, + title => $2, + image => $3, + unknown1 => $4, + }; + } elsif (defined $data and /^([^#]*)#/) { + unless (defined $data->{summary}) { + $data->{summary} = $1; + $data->{summary} =~ s/\^......//g; + } else { + $data->{objective} = $1; + $flag = 1; + } + } + + if ($flag) { + $$r_hash{$data->{id}} = $data; + undef $data; undef $flag; + } + } + + return 1; +} + +sub parseRecvpackets { + my ($file, $r_hash) = @_; + + %{$r_hash} = (); + my $reader = new Utils::TextReader($file); + while (!$reader->eof()) { + my $line = $reader->readLine(); + $line =~ s/\x{FEFF}//g; + next if ($line =~ /^#/); + $line =~ s/[\r\n]//g; + next if (length($line) == 0); + + my ($packetID, $length, $minLength, $repeat, $function) = split /\s+/, $line, 5; + $packetID =~ s/^(0x[0-9a-f]+)$/hex $1/e; + $r_hash->{$packetID}{length} = $length; + $r_hash->{$packetID}{minLength} = $minLength; + $r_hash->{$packetID}{repeat} = $repeat; # unused + $r_hash->{$packetID}{function} = $function; # can be used as description instead of packetdescriptions.txt, if defined. + #use Log 'warning'; + #warning $r_hash->{$key}." ".$key."\n"; + } + close FILE; + + return 1; +} + +sub parseSectionedFile { + my $file = shift; + my $r_hash = shift; + undef %{$r_hash}; + my $reader = new Utils::TextReader($file); + + my $section = ""; + while (!$reader->eof()) { + my $line = $reader->readLine(); + $line =~ s/\x{FEFF}//g; + next if ($line =~ /^#/); + $line =~ s/[\r\n]//g; + $line =~ s/\s+$//g; + next if ($line eq ""); + + if ($line =~ /^\[(.*)\]$/i) { + $section = $1; + next; + } else { + my ($key, $value); + if ($line =~ / /) { + ($key, $value) = $line =~ /^(.*?) (.*)/; + } else { + $key = $line; + } + $r_hash->{$section}{$key} = $value; + } + } + return 1; +} + +sub parseSkillsSPLUT { + my $file = shift; + my $r_hash = shift; + undef %{$r_hash}; + my $ID; + my $i; + $i = 1; + open(FILE, "< $file"); + foreach (<FILE>) { + if (/^\@/) { + undef $ID; + $i = 1; + } elsif (!$ID) { + ($ID) = /([\s\S]+)#/; + } else { + ($$r_hash{$ID}{$i++}) = /(\d+)#/; + } + } + close FILE; + return 1; +} + +sub parseTimeouts { + my $file = shift; + my $r_hash = shift; + my $reader = new Utils::TextReader($file); + while (!$reader->eof()) { + my $line = $reader->readLine(); + $line =~ s/\x{FEFF}//g; + next if ($line =~ /^#/); + $line =~ s/[\r\n]//g; + $line =~ s/\s*#.*//; + next unless length $line; + + my ($key, $value) = $line =~ /([\s\S]+?) ([\s\S]*?)$/; + my @args = split (/ /, $value); + if ($key ne "") { + $$r_hash{$key}{'timeout'} = $args[0]; + } + } + return 1; +} + +sub parseWaypoint { + my $file = shift; + my $r_array = shift; + @{$r_array} = (); + + open FILE, "< $file"; + foreach (<FILE>) { + s/\x{FEFF}//g; + next if (/^#/ || /^$/); + s/[\r\n]//g; + + my @items = split / +/, $_; + my %point = ( + map => $items[0], + x => $items[1], + y => $items[2] + ); + push @{$r_array}, \%point; + } + close FILE; + return 1; +} + +sub parseItemStackLimit { + my ($file, $r_hash) = @_; + my $reader = Utils::TextReader->new($file); + + while (!$reader->eof()) { + my $line = $reader->readLine(); + $line =~ s/\x{FEFF}//g; + $line =~ s/[\r\n]//g; + $line =~ s/#.*$//g; + $line =~ s/^\s+//g; + + next unless length $line; + + my ($ID, $stack, $mask) = split /\s+/, $line; + + next unless $mask; + + for (my $i = 1; $i < 16; $i = $i << 1) { + next unless $mask & $i; + $r_hash->{$ID}->{$i} = $stack; + } + } + + return 1; +} + +## +# parseAttendanceRewards(file, attendance_rewards) +# file: Filename to parse +# attendance_rewards: Return hash +# +# Parses a attendance_rewards file. The attendance_rewards file should have the start time +# on its first line, should have end time on its second line followed by "$day\t$item_id\t$amount" on subsequent +# lines. Blank lines, or lines starting with "#" are ignored. +# +# Example: +# start 20200212 +# end 20200311 +# 1 14553 5 +# 2 14556 5 +# 3 14559 5 +sub parseAttendanceRewards { + my ($file, $attendance_rewards) = @_; + + %{$attendance_rewards} = (); + my $reader = new Utils::TextReader($file); + + # start attendance rewards vars + $attendance_rewards->{period} = (); + $attendance_rewards->{items} = (); + my $line; + + while (!$reader->eof()) { + $line = $reader->readLine(); + chomp; + + $line =~ s/[\r\n\x{FEFF}]//g; + next if $line =~ /^$/ || $line =~ /^#/; + + # Strip mid-line comments + $line =~ s/\s*#.*//; + next unless length $line; + + if($line =~ /^start\s(\d+)/) { + $attendance_rewards->{period}{start} = $1; + next; + } elsif ($line =~ /^end\s(\d+)/) { + $attendance_rewards->{period}{end} = $1; + next; + } + + my ($day, $item_id, $amount) = split(/ /, $line); + $item_id =~ s/^\s+//g; + $amount =~ s/^\s+//g; + + $attendance_rewards->{items}{$day}{item_id} = $item_id; + $attendance_rewards->{items}{$day}{amount} = $amount; + } + + return 1; +} + + +# The ultimate config file format. This function is a parser and writer in one. +# The config file can be divided in section, example: +# +# foo 1 +# bar 2 +# +# [Options] +# username me +# password p +# +# [Names] +# joe +# mike +# +# Sections can be treated as hashes or arrays. It's defined by $rules. +# If you want [Names] to be an array: +# %rule = (Names => 'list'); +# +# processUltimate("file", \%hash, \%rule) returns: +# { +# foo => 1, +# bar => 2, +# Options => { +# username => 'me', +# password => 'p'; +# }, +# Names => [ +# "joe", +# "mike" +# ] +# } +# +# When in write mode, this function will automatically add new keys and lines, +# while preserving comments. +sub processUltimate { + my ($file, $hash, $rules, $writeMode) = @_; + my $f; + my $secname = ''; + my ($section, $rule, @lines, %written, %sectionsWritten); + + undef %{$hash} if (!$writeMode); + if (-f $file) { + my $reader = new Utils::TextReader($file); + while (!$reader->eof()) { + my $line = $reader->readLine(); + $line =~ s/\x{FEFF}//g; + $line =~ s/[\r\n]//g; + + if ($line eq '' || $line =~ /^[ \t]*#/) { + push @lines, $line if ($writeMode); + next; + } + + if ($line =~ /^\[(.+)\]$/) { + # New section + if ($writeMode) { + # First, finish writing everything in the previous section + my $h = (defined $section) ? $section : $hash; + my @add; + + if ($rule ne 'list') { + foreach my $key (keys %{$h}) { + if (!$written{$key} && !ref($h->{$key})) { + push @add, "$key $h->{$key}"; + } + } + + } else { + foreach (@{$h}) { + push @add, $_ if (!$written{$_}); + } + } + + # Add after the first non-empty line from the end + my $linesFromEnd; + for (my $i = @lines - 1; $i >= 0; $i--) { + if ($lines[$i] ne '') { + $linesFromEnd = @lines - $i - 1; + for (my $j = $i + 1; $j < @lines; $j++) { + delete $lines[$j]; + } + push @lines, @add; + for (my $j = 0; $j < $linesFromEnd; $j++) { + push @lines, ''; + } + last; + } + } + undef %written; + } + + # Parse the new section + $secname = $1; + $rule = $rules->{$secname}; + if ($writeMode) { + $section = $hash->{$secname}; + push @lines, $line; + $sectionsWritten{$secname} = 1; + + } else { + if ($rule ne 'list') { + $section = {}; + } else { + $section = []; + } + $hash->{$secname} = $section; + } + + } elsif ($rule ne 'list') { + # Line is a key-value pair + my ($key, $val) = split / /, $line, 2; + my $h = (defined $section) ? $section : $hash; + + if ($writeMode) { + # Delete line if value doesn't exist + if (exists $h->{$key}) { + if (!defined $h->{$key}) { + push @lines, $key; + } else { + push @lines, "$key $h->{$key}"; + } + $written{$key} = 1; + } + + } else { + $h->{$key} = $val; + } + + } else { + # Line is part of a list + if ($writeMode) { + # Add line only if it exists in the hash + push @lines, $line if (defined(binFind($section, $line))); + $written{$line} = 1; + + } else { + push @{$section}, $line; + } + } + } + } + + if ($writeMode) { + # Add stuff that haven't already been added + my $h = (defined $section) ? $section : $hash; + + if ($rule ne 'list') { + foreach my $key (keys %{$h}) { + if (!$written{$key} && !ref($h->{$key})) { + push @lines, "$key $h->{$key}"; + } + } + + } else { + foreach my $line (@{$h}) { + push @lines, $line if (!$written{$line}); + } + } + + # Write sections that aren't already in the file + foreach my $section (keys %{$hash}) { + next if (!ref($hash->{$section}) || $sectionsWritten{$section}); + push @lines, "", "[$section]"; + if ($rules->{$section} eq 'list') { + push @lines, @{$hash->{$section}}; + } else { + foreach my $key (keys %{$hash->{$section}}) { + push @lines, "$key $hash->{$section}{$key}"; + } + } + } + + open($f, ">:utf8", $file); + print $f join("\n", @lines) . "\n"; + close $f; + } + return 1; +} + +sub writeDataFile { + my $file = shift; + my $r_hash = shift; + my ($key,$value); + open(FILE, ">>:utf8", $file); + foreach (keys %{$r_hash}) { + if ($_ ne "") { + print FILE $_; + print FILE " $$r_hash{$_}" if $$r_hash{$_} ne ''; + print FILE "\n"; + } + } + close FILE; +} + +sub writeDataFileIntact { + my $file = shift; + my $r_hash = shift; + # following args for recursive call (!include) + my $blocks = {}; + my $diffs = {}; + + my (@lines, $key, $value, $inBlock, $commentBlock); + my $reader = new Utils::TextReader($file, { hide_includes => 0, hide_comments => 0 }); + while (!$reader->eof()) { + my $current_file = $reader->currentFile; + my $lines = $reader->readLine; + $lines =~ s/[\r\n]//g; # Remove line endings + if ($lines =~ /^[\s\t]*#/ || $lines =~ /^[\s\t]*$/ || $lines =~ /^\s*\!include( |$)/) { + push @lines, $lines if $current_file eq $file; + next; + } + $lines =~ s/^[\t\s]*//; # Remove leading tabs and whitespace + $lines =~ s/\s+$//g; # Remove trailing whitespace + + if (!defined $commentBlock && $lines =~ /^\/\*/) { + push @lines, "$lines" if $current_file eq $file; + $commentBlock = 1; + next; + + } elsif ($lines =~ m/\*\/$/) { + push @lines, "$lines" if $current_file eq $file; + undef $commentBlock; + next; + + } elsif ($commentBlock) { + push @lines, "$lines" if $current_file eq $file; + next; + + } elsif (!defined $inBlock && $lines =~ /{$/) { + # Begin of block + $lines =~ s/ *{$//; + ($key, $value) = $lines =~ /^(.*?) (.*)/; + $key = $lines if ($key eq ''); + + if (!exists $blocks->{$key}) { + $blocks->{$key} = 0; + } else { + $blocks->{$key}++; + } + if ($key ne 'teleportAuto'){ + $inBlock = "${key}_$blocks->{$key}"; + } else { + $inBlock = "${key}"; + } + + my $line = $key; + $line .= " $r_hash->{$inBlock}" if ($r_hash->{$inBlock} ne ''); + push @lines, "$line {" if $current_file eq $file; + + } elsif (defined $inBlock && $lines eq "}") { + # End of block + undef $inBlock; + push @lines, "}" if $current_file eq $file; + + } else { + # Option + ($key, $value) = $lines =~ /^(.*?) (.*)/; + if ($key eq "") { + $key = $lines; + $key =~ s/ *$//; + } + if (defined $inBlock) { + my $realKey = "${inBlock}_${key}"; + my $line = "\t$key"; + $line .= " $r_hash->{$realKey}" if ($r_hash->{$realKey} ne ''); + push @lines, $line if $current_file eq $file; + } else { + my $line = $key; + $line .= " $r_hash->{$key}" if ($r_hash->{$key} ne ''); + push @lines, $line if $current_file eq $file; + $diffs->{$key} = 1 if $current_file ne $file && $r_hash->{$key} ne $value; + delete $diffs->{$key} if $current_file eq $file || $r_hash->{$key} eq $value; + } + } + } + close FILE; + + debug TF("Saving %s...\n", $file), 'writeFile'; + + # options that are different in memory and file data, but defined in !include'd (read only) files + foreach (sort keys %$diffs) { + push @lines, $r_hash->{$_} ne '' ? "$_ $r_hash->{$_}" : "$_"; + } + + open FILE, ">:utf8", $file; + print FILE join("\n", @lines) . "\n"; + close FILE; +} + +sub writeDataFileIntact2 { + my $file = shift; + my $r_hash = shift; + my $data; + my $key; + + my $reader = new Utils::TextReader($file, { hide_includes => 0, hide_comments => 0 }); + while (!$reader->eof()) { + my $line = $reader->readLine(); + $line =~ s/\x{FEFF}//g; + if ($line =~ /^#/ || $line =~ /^\n/ || $line =~ /^\r/) { + $data .= $line; + next; + } + ($key) = $line =~ /^(\w+)/; + $data .= $key; + $data .= " $$r_hash{$key}{'timeout'}" if $$r_hash{$key}{'timeout'} ne ''; + $data .= "\n"; + } + close FILE; + open(FILE, ">:utf8", $file); + print FILE $data; + close FILE; +} + +sub writePortalsLOS { + my $file = shift; + my $r_hash = shift; + open(FILE, "+> $file"); + foreach my $key (sort keys %{$r_hash}) { + next if (!$$r_hash{$key} || !(keys %{$$r_hash{$key}})); + print FILE $key; + foreach (keys %{$$r_hash{$key}}) { + print FILE " $_ $$r_hash{$key}{$_}"; + } + print FILE "\n"; + } + close FILE; +} + +sub writeSectionedFileIntact { + my $file = shift; + my $r_hash = shift; + my $section = ""; + my @lines; + + my $reader = new Utils::TextReader($file); + while (!$reader->eof()) { + my $line = $reader->readLine(); + $line =~ s/[\r\n]//g; + if ($line =~ /^#/ || $line =~ /^ *$/) { + push @lines, $line; + next; + } + + if ($line =~ /^\[(.*)\]$/) { + $section = $1; + push @lines, $line; + } else { + my ($key, $value); + if ($line =~ / /) { + ($key) = $line =~ /^(.*?) /; + } else { + $key = $line; + } + $value = $r_hash->{$section}{$key}; + push @lines, "$key $value"; + } + } + close FILE; + + open(FILE, ">:utf8", $file); + print FILE join("\n", @lines) . "\n"; + close FILE; +} + +sub updateMonsterLUT { + my $file = shift; + my $ID = shift; + my $name = shift; + open FILE, ">>:utf8", $file; + print FILE "$ID $name\n"; + close FILE; +} + +sub updatePortalLUT { + my ($file, $sourceMap, $sourceX, $sourceY, $destMap, $destX, $destY) = @_; + + my $plugin_args = {file => $file, sourceMap => $sourceMap, sourceX => $sourceX, sourceY => $sourceY, destMap => $destMap, destX => $destX, destY => $destY}; + Plugins::callHook('updatePortalLUT', $plugin_args); + + unless ($plugin_args->{return}) { + open FILE, ">>:utf8", $file; + print FILE "$sourceMap $sourceX $sourceY $destMap $destX $destY\n"; + close FILE; + } +} + +#Add: NPC talk Sequence +sub updatePortalLUT2 { + my ($file, $sourceMap, $sourceX, $sourceY, $destMap, $destX, $destY, $steps) = @_; + + my $plugin_args = {file => $file, sourceMap => $sourceMap, sourceX => $sourceX, sourceY => $sourceY, destMap => $destMap, destX => $destX, destY => $destY, steps => $steps}; + Plugins::callHook('updatePortalLUT2', $plugin_args); + + unless ($plugin_args->{return}) { + open FILE, ">>:utf8", $file; + print FILE "$sourceMap $sourceX $sourceY $destMap $destX $destY $steps\n"; + close FILE; + } +} + +sub updateNPCLUT { + my ($file, $location, $name) = @_; + return unless $name; + open FILE, ">>:utf8", $file; + print FILE "$location $name\n"; + close FILE; +} + +1; diff --git a/openkore_llm_knowledge/core/src/Globals.pm b/openkore_llm_knowledge/core/src/Globals.pm new file mode 100644 index 0000000000..f1e427ad59 --- /dev/null +++ b/openkore_llm_knowledge/core/src/Globals.pm @@ -0,0 +1,634 @@ +######################################################################### +# OpenKore - Global variables +# +# This software is open source, licensed under the GNU General Public +# License, version 2. +# Basically, this means that you're allowed to modify and distribute +# this software. However, if you distribute modified versions, you MUST +# also distribute the source code. +# See http://www.gnu.org/licenses/gpl.html for the full license. +# +# $Revision$ +# $Id$ +# +######################################################################### +## +# MODULE DESCRIPTION: Global variables +# +# This module defines all kinds of global variables. + +package Globals; + +use strict; +use Exporter; +use base qw(Exporter); +use Modules 'register'; +# Do not use any other Kore modules here. It will create circular dependancies. + +our %EXPORT_TAGS = ( + + config => [qw(%arrowcraft_items %avoid @chatResponses %cities_lut %config %consoleColors %directions_lut %equipTypes_lut %equipSlot_rlut %equipSlot_lut %haircolors @headgears_lut @msgTable %items_control %items_lut %itemSlotCount_lut %itemsDesc_lut %itemTypes_lut %itemOptionHandle %itemOption_lut %jobs_lut %maps_lut %masterServers %monsters_lut %npcs_lut %packetDescriptions %portals_lut @portals_lut_missed %responses %sex_lut %shop %banking %buyer_shop %skillsDesc_lut %lookHandle %skillsArea %skillsEncore %spells_lut %emotions_lut %timeout $char %mon_control %priority %routeWeights %pickupitems %rpackets %itemSlots_lut %statusHandle %statusName %effectName %hatEffectHandle %hatEffectName %portals_los %portals_commands %portals_spawns %portals_airships %stateHandle %ailmentHandle %mapTypeHandle %mapPropertyTypeHandle %mapPropertyInfoHandle %elements_lut %mapAlias_lut %quests_lut $Blacksmith_Blessing %itemStackLimit %title_lut %attendance_rewards)], + ai => [qw(@ai_seq @ai_seq_args %ai_v %targetTimeout)], + state => [qw($accountID $cardMergeIndex @cardMergeItemsID $charID @chars @chars_old @friendsID %friends %incomingFriend $field %homunculus $itemsList @itemsID %items $monstersList @monstersID %monsters @npcsID %npcs $npcsList @playersID %players @portalsID @portalsID_old %portals %portals_old $portalsList $storeList $currentChatRoom @currentChatRoomUsers @chatRoomsID %createdChatRoom %chatRooms @skillsID $storageTitle @arrowCraftID %guild %incomingGuild @spellsID %spells @unknownPlayers @unknownNPCs $useArrowCraft %currentDeal %incomingDeal %outgoingDeal @identifyID @partyUsersID %incomingParty @petsID %pets $venderItemList $venderID $venderCID @venderListsID $buyerItemList $buyerPriceLimit @selfBuyerItemList $buyerID $buyingStoreID @buyerListsID @articles $articles %venderLists %buyerLists %monsters_old @monstersID_old %npcs_old %items_old %players_old @playersID_old @servers $sessionID $sessionID2 $accountSex $accountSex2 $map_ip $map_port $KoreStartTime $secureLoginKey $initSync $lastConfChangeTime $petsList $playersList $portalsList %elementals $elementalsList @elementalsID @playerNameCacheIDs %playerNameCache %pet $pvp $cashList $slavesList @slavesID %slaves %cashShop $skillExchangeItem $refineUI %clan %universalCatalog $mergeItemList)], + network => [qw($remote_socket $net $messageSender $charServer $conState $conState_tries $encryptVal $ipc $bus $masterServer $lastSwitch $packetParser $clientPacketHandler $bytesSent $incomingMessages $outgoingClientMessages $enc_val1 $enc_val2 $captcha_state $captcha_image $captcha_image_content $captcha_key $captcha_size)], + interface => [qw($interface)], + misc => [qw($quit $reconnectCount @lastpm %lastpm @privMsgUsers %timeout_ex $shopstarted $buyershopstarted $bankingopened $dmgpsec $totalelasped $elasped $totaldmg %overallAuth %responseVars %talk $startTime_EXP $startingzeny @monsters_Killed $bExpSwitch $jExpSwitch $totalBaseExp $totalJobExp $shopEarned %itemChange $XKore_dontRedirect $monkilltime $monstarttime $startedattack $firstLoginMap $sentWelcomeMessage $versionSearch $monsterBaseExp $monsterJobExp %flags %damageTaken %minimap_indicator_seen $logAppend @sellList $userSeed $taskManager $repairList $mailList $rodexList $rodexWrite $rodexCurrentType $auctionList $questList %achievements $achievementList $hotkeyList $devotionList $cookingList $currentCookingType $makableList %charSvrSet @deadTime $refineList $current_item_list $ignored_all %roulette $in_market @reputation_list_name @reputation_list)], + syncs => [qw($syncSync $syncMapSync)], + cmdqueue => [qw($cmdQueue @cmdQueueList $cmdQueueStartTime $cmdQueueTime @cmdQueuePriority)], +); + +our @EXPORT = ( + @{$EXPORT_TAGS{config}}, + @{$EXPORT_TAGS{ai}}, + @{$EXPORT_TAGS{state}}, + @{$EXPORT_TAGS{network}}, + @{$EXPORT_TAGS{interface}}, + @{$EXPORT_TAGS{misc}}, + @{$EXPORT_TAGS{syncs}}, + @{$EXPORT_TAGS{cmdqueue}}, +); + + +# Configuration variables +our %arrowcraft_items; +our %avoid; +our @chatResponses; +our $char; +our %cities_lut; +our %config; +our %consoleColors; +our %equipTypes_lut; +our %equipSlot_lut = ( + '0' => 'Item', + '1' => 'lowHead', + '2' => 'rightHand', + '4' => 'robe', + '8' => 'rightAccessory', + '16' => 'armor', + '32' => 'leftHand', + '64' => 'shoes', + '128' => 'leftAccessory', + '256' => 'topHead', + '512' => 'midHead', + '1024' => 'costumeTopHead', + '2048' => 'costumeMidHead', + '4096' => 'costumeLowHead', + '8192' => 'costumeRobe', + '16384' => 'costumeFloor', + # 0x2000 => LOCATION_COSTUME_FLOOR, + '32768' => 'arrow', #just use an made up ID since arrow doesn't have any + # 0xffff8000 => LOCATION_ARROW, + '65536' => 'shadowArmor', + '131072' => 'shadowRightHand', + '262144' => 'shadowLeftHand', + '524288' => 'shadowShoes', + '1048576' => 'shadowRightAccessory', + '2097152' => 'shadowLeftAccessory', +); +our %equipSlot_rlut = ( + ( map { $equipSlot_lut{$_} => $_ } keys %equipSlot_lut ), + 'arrow' => '', #arrow seems not to have any ID +); +our %elements_lut; +our %directions_lut; +our %haircolors; +our @headgears_lut; +our @msgTable; +our %items_control; +our %items_lut; +our %itemSlotCount_lut; +our %itemsDesc_lut; +our %itemTypes_lut; +our %itemSlots_lut; +our %itemOption_lut; +our %itemOptionHandle; +our %title_lut; +our %attendance_rewards; +our %roulette; +our %mapAlias_lut; +our %maps_lut; +our %masterServers; +our %mon_control; +our %monsters_lut; +our %npcs_lut; +our %packetDescriptions; +our %portals_los; +our %portals_commands; +our %portals_spawns; +our %portals_airships; +our %portals_lut; +our @portals_lut_missed; +our %priority; +our %responses; +our %routeWeights; +our %rpackets; +our %sex_lut; +our %shop; +our %banking; +our %skillsDesc_lut; +our %skillsArea; +our %skillsEncore; +our ( + # status handles + %statusHandle, %stateHandle, %lookHandle, %ailmentHandle, + %mapTypeHandle, %mapPropertyTypeHandle, %mapPropertyInfoHandle, + # status names + %statusName, + # effect names + %effectName, + # Hat Effect + %hatEffectHandle, + %hatEffectName, +); +our %spells_lut; +our %timeout; +our %itemStackLimit; +our %jobs_lut = ( + 0 => 'Novice', + 1 => 'Swordsman', + 2 => 'Mage', + 3 => 'Archer', + 4 => 'Acolyte', + 5 => 'Merchant', + 6 => 'Thief', + 7 => 'Knight', + 8 => 'Priest', + 9 => 'Wizard', + 10 => 'Blacksmith', + 11 => 'Hunter', + 12 => 'Assassin', + 13 => 'Peco Knight', + 14 => 'Crusader', + 15 => 'Monk', + 16 => 'Sage', + 17 => 'Rogue', + 18 => 'Alchemist', + 19 => 'Bard', + 20 => 'Dancer', + 21 => 'Peco Crusader', + 22 => 'Wedding Suit', + 23 => 'Super Novice', + 24 => 'Gunslinger', + 25 => 'Ninja', + 26 => 'Xmas', + 27 => 'Summer', + + 161 => 'High Novice', + 162 => 'High Swordsman', + 163 => 'High Magician', + 164 => 'High Archer', + 165 => 'High Acolyte', + 166 => 'High Merchant', + 167 => 'High Thief', + 168 => 'Lord Knight', + 169 => 'High Priest', + 170 => 'High Wizard', + 171 => 'Whitesmith', + 172 => 'Sniper', + 173 => 'Assassin Cross', + 174 => 'Peco Lord Knight', + 175 => 'Paladin', + 176 => 'Champion', + 177 => 'Professor', + 178 => 'Stalker', + 179 => 'Creator', + 180 => 'Clown', + 181 => 'Gypsy', + + # Elementals + 2114 => 'Agni [S]', + 2115 => 'Agni [M]', + 2116 => 'Agni [L]', + 2117 => 'Aqua [S]', + 2118 => 'Aqua [M]', + 2119 => 'Aqua [L]', + 2120 => 'Ventus [S]', + 2121 => 'Ventus [M]', + 2122 => 'Ventus [L]', + 2123 => 'Tera [S]', + 2124 => 'Tera [M]', + 2125 => 'Tera [L]', + + 4001 => 'High Novice', + 4002 => 'High Swordsman', + 4003 => 'High Magician', + 4004 => 'High Archer', + 4005 => 'High Acolyte', + 4006 => 'High Merchant', + 4007 => 'High Thief', + 4008 => 'Lord Knight', + 4009 => 'High Priest', + 4010 => 'High Wizard', + 4011 => 'Whitesmith', + 4012 => 'Sniper', + 4013 => 'Assassin Cross', + 4014 => 'Peco Lord Knight', + 4015 => 'Paladin', + 4016 => 'Champion', + 4017 => 'Professor', + 4018 => 'Stalker', + 4019 => 'Creator', + 4020 => 'Clown', + 4021 => 'Gypsy', + 4022 => 'Peco Paladin', + 4023 => 'Baby Novice', + 4024 => 'Baby Swordsman', + 4025 => 'Baby Magician', + 4026 => 'Baby Archer', + 4027 => 'Baby Acolyte', + 4028 => 'Baby Merchant', + 4029 => 'Baby Thief', + 4030 => 'Baby Knight', + 4031 => 'Baby Priest', + 4032 => 'Baby Wizard', + 4033 => 'Baby Blacksmith', + 4034 => 'Baby Hunter', + 4035 => 'Baby Assassin', + 4036 => 'Baby Peco Knight', + 4037 => 'Baby Crusader', + 4038 => 'Baby Monk', + 4039 => 'Baby Sage', + 4040 => 'Baby Rogue', + 4041 => 'Baby Alchemist', + 4042 => 'Baby Bard', + 4043 => 'Baby Dancer', + 4044 => 'Baby Peco Crusader', + 4045 => 'Super Baby', + 4046 => 'Taekwon', + 4047 => 'Star Gladiator', + 4048 => 'Flying Star Gladiator', + 4049 => 'Soul Linker', + 4050 => 'Munak', + 4051 => 'Death Knight', + 4052 => 'Dark Collector', + + 4054 => 'Rune Knight', + 4055 => 'Warlock', + 4056 => 'Ranger', + 4057 => 'Arch Bishop', + 4058 => 'Mechanic', + 4059 => 'Glt. Cross', + 4060 => 'Rune Knight', + 4061 => 'Warlock', + 4062 => 'Ranger', + 4063 => 'Arch Bishop', + 4064 => 'Mechanic', + 4065 => 'Glt. Cross', + 4066 => 'Royal Guard', + 4067 => 'Sorcerer', + 4068 => 'Minstrel', + 4069 => 'Wanderer', + 4070 => 'Sura', + 4071 => 'Genetic', + 4072 => 'Shadow Chaser', + 4073 => 'Royal Guard', + 4074 => 'Sorcerer', + 4075 => 'Minstrel', + 4076 => 'Wanderer', + 4077 => 'Sura', + 4078 => 'Genetic', + 4079 => 'Shadow Chaser', + 4080 => 'Rune Knight', # mounted: Green Dragon + 4081 => 'Rune Knight', # mounted: Green Dragon + 4082 => 'Royal Guard', + 4083 => 'Royal Guard', + 4084 => 'Ranger', + 4085 => 'Ranger', + 4086 => 'Mechanic', + 4087 => 'Mechanic', + 4088 => 'Rune Knight', # mounted: Black Dragon + 4089 => 'Rune Knight', # mounted: Black Dragon + 4090 => 'Rune Knight', # mounted: White Dragon + 4091 => 'Rune Knight', # mounted: White Dragon + 4092 => 'Rune Knight', # mounted: Blue Dragon + 4093 => 'Rune Knight', # mounted: Blue Dragon + 4094 => 'Rune Knight', # mounted: Red Dragon + 4095 => 'Rune Knight', # mounted: Red Dragon + 4096 => 'Baby Rune', + 4097 => 'Baby Warlock', + 4098 => 'Baby Ranger', + 4099 => 'Baby Bishop', + 4100 => 'Baby Mechanic', + 4101 => 'Baby Cross', + 4102 => 'Baby Guard', + 4103 => 'Baby Sorcerer', + 4104 => 'Baby Minstrel', + 4105 => 'Baby Wanderer', + 4106 => 'Baby Sura', + 4107 => 'Baby Genetic', + 4108 => 'Baby Chaser', + 4109 => 'Baby Rune', # mounted + 4110 => 'Baby Guard', # mounted + 4111 => 'Baby Ranger', # mounted + 4112 => 'Baby Mechanic', # mounted + 4190 => 'Super Novice', # expanded + 4191 => 'Super Baby', # expanded + 4211 => 'Kagerou', + 4212 => 'Oboro', + 4215 => 'Rebellion', + 4218 => 'Summoner', + + 4239 => 'Star Emperor', + 4240 => 'Soul Reaper', + 4241 => 'Baby Star Emperor', + 4242 => 'Baby Soul Reaper', + #4243 => 'Star Emperor',#Job_Star_Emperor2: 4243 + #4244 => 'Soul Reaper', #Job_Baby_Star_Emperor2: 4244 + + # 4th + 4252 => 'Dragon Knight', + 4253 => 'Meister', + 4254 => 'Shadow Cross', + 4255 => 'Arch Mage', + 4256 => 'Cardinal', + 4257 => 'Windhawk', + 4258 => 'Imperial Guard', + 4259 => 'Biolo', + 4260 => 'Abyss Chaser', + 4261 => 'Elemental Master', + 4262 => 'Inquisitor', + 4263 => 'Troubadour', + 4264 => 'Trouvere', + + # Homunculus + 6001 => 'Lif', + 6002 => 'Amistr', + 6003 => 'Filir', + 6004 => 'Vanilmirth', + 6005 => 'Lif 2', + 6006 => 'Amistr 2', + 6007 => 'Filir 2', + 6008 => 'Vanilmirth 2', + 6009 => 'High Lif', + 6010 => 'High Amistr', + 6011 => 'High Filir', + 6012 => 'High Vanilmirth', + 6013 => 'High Lif 2', + 6014 => 'High Amistr 2', + 6015 => 'High Filir 2', + 6016 => 'High Vanilmirth 2', + + # Mercenary + 6017 => 'Mercenary Archer 1', + 6018 => 'Mercenary Archer 2', + 6019 => 'Mercenary Archer 3', + 6020 => 'Mercenary Archer 4', + 6021 => 'Mercenary Archer 5', + 6022 => 'Mercenary Archer 6', + 6023 => 'Mercenary Archer 7', + 6024 => 'Mercenary Archer 8', + 6025 => 'Mercenary Archer 9', + 6026 => 'Mercenary Archer 10', + 6027 => 'Mercenary Lancer 1', + 6028 => 'Mercenary Lancer 2', + 6029 => 'Mercenary Lancer 3', + 6030 => 'Mercenary Lancer 4', + 6031 => 'Mercenary Lancer 5', + 6032 => 'Mercenary Lancer 6', + 6033 => 'Mercenary Lancer 7', + 6034 => 'Mercenary Lancer 8', + 6035 => 'Mercenary Lancer 9', + 6036 => 'Mercenary Lancer 10', + 6037 => 'Mercenary Swordman 1', + 6038 => 'Mercenary Swordman 2', + 6039 => 'Mercenary Swordman 3', + 6040 => 'Mercenary Swordman 4', + 6041 => 'Mercenary Swordman 5', + 6042 => 'Mercenary Swordman 6', + 6043 => 'Mercenary Swordman 7', + 6044 => 'Mercenary Swordman 8', + 6045 => 'Mercenary Swordman 9', + 6046 => 'Mercenary Swordman 10', + + 6047 => 'Mercenary Monster', # don't know about this one + + # Homunculus S + 6048 => 'Eira', + 6049 => 'Bayeri', + 6050 => 'Sera', + 6051 => 'Dieter', + 6052 => 'Eleanor', + +); + +# AI +our @ai_seq; +our @ai_seq_args; +our %ai_v; +our %targetTimeout; + +# Game state +our $accountID; +our $cardMergeIndex; +our @cardMergeItemsID; +our @chars; +our @chars_old; +our $field; +our @friendsID; +our %friends; +our %homunculus; +our %incomingFriend; +our $itemsList; +our @itemsID; +our %items; +our $monstersList; +our @monstersID; +our %monsters; +our @npcsID; +our %npcs; +our @playersID; +our %players; +our @portalsID; +our @portalsID_old; +our %portals; +our %portals_old; +our $storeList; +our $in_market; +our $currentChatRoom; +our @currentChatRoomUsers; +our @chatRoomsID; +our %createdChatRoom; +our %chatRooms; +our @skillsID; +our $storageTitle; +our @arrowCraftID; +our %guild; +our %clan; +our %incomingGuild; +our @spellsID; +our %spells; +our @unknownPlayers; +our @unknownNPCs; +our $useArrowCraft; +our %currentDeal; +our %incomingDeal; +our %outgoingDeal; +our @identifyID; +our $playersList; +our $npcsList; +our $petsList; +our %elementals; +our $elementalsList; +our @elementalsID; +our $portalsList; +our $slavesList; +our @slavesID; +our %slaves; +our %cashShop; +our @petsID; +our %pets; +our $pvp; +our $venderItemList; +our $venderID; +our $venderCID; +our @venderListsID; +our $buyerPriceLimit; +our $buyerItemList; +our $buyerID; +our $buyingStoreID; +our @buyerListsID; +our @articles; +our $articles; +our %monsters_old; +our @monstersID_old; +our %npcs_old; +our %items_old; +our %players_old; +our @playersID_old; +our @servers; +our $sessionID; +our $sessionID2; +our $accountSex; +our $accountSex2; +our $map_ip; +our $map_port; +our $KoreStartTime; +our $secureLoginKey; +our $initSync; +our $lastConfChangeTime; +our @playerNameCacheIDs; +our %playerNameCache; +our %pet; +our $cashList; +our $skillExchangeItem; +our $refineUI; +our %universalCatalog; +our $mergeItemList; + +# Network +our $remote_socket; # Unused, but required for outdated plugins +our $net; +our $messageSender; +our $charServer; +our $conState; +our $conState_tries; +our $encryptVal; +our $ipc; +our $bus; +#our $lastPacketTime; # replaced by $packetParser->{lastPacketTime} +our $masterServer; +our $incomingMessages; +our $outgoingClientMessages; +our $lastSwitch; +our $enc_val1; +our $enc_val2; + + +# Interface +our $interface; + +# Misc +our $quit; +our $reconnectCount; +our @lastpm; +our %lastpm; +our @privMsgUsers; +our %timeout_ex; +our %overallAuth; +our $shopstarted; +our $bankingopened; +our $dmgpsec; +our $totalelasped; +our $elasped; +our $totaldmg; +our %responseVars; +our %talk; +our $startTime_EXP; +our $startingzeny; +our @monsters_Killed; +our $bExpSwitch; +our $jExpSwitch; +our $totalBaseExp; +our $totalJobExp; +our $shopEarned; +our %itemChange; +our %damageTaken; +our $XKore_dontRedirect; +our $monkilltime; +our $monstarttime; +our $startedattack; +our $firstLoginMap; +our $sentWelcomeMessage; +our $versionSearch; +our $monsterBaseExp; +our $monsterJobExp; +our %descriptions; +our %flags; +our %minimap_indicator_seen; +our $logAppend; +our @sellList; +our $userSeed; +our $taskManager; +our $refineList; +our $currentCookingType; +our $current_item_list; +our $ignored_all; + + +our $bytesSent = 0; +#our $bytesReceived = 0; # replaced by $packetParser->{bytesProcessed} + +our $syncSync; +our $syncMapSync; + +our $cmdQueue = 0; +our $cmdQueueStartTime; +our $cmdQueueTime = 0; +our @cmdQueueList; +our @cmdQueuePriority = ('ai','aiv','al','debug','chist','dl','exp','friend','g','guild','help','i', + 'ihist','il','ml','nl','p','party','petl','pl','plugin','relog','pml','portals','quit','rc', + 'reload','s','skills','spells','st','stat_add','store','vl','weight'); + +our $repairList; +our $mailList; +our $rodexList; +our $rodexWrite; +our $rodexCurrentType; +our $auctionList; +our $hotkeyList; +our $devotionList; +our $cookingList; +our $makableList; +our %charSvrSet; +our $questList; +our %achievements; +our $achievementList; +our $Blacksmith_Blessing = 6635; +our $captcha_state = 0; +our $captcha_image; +our $captcha_image_content; +our $captcha_key; +our $captcha_size; + +our %quests_lut; + +our @deadTime; + +our @reputation_list_name = ("Orc Village", "Goblin Village", "Grey Wolf Village", "Isgard"); +our @reputation_list; + +END { + undef $interface if defined $interface; +} + +return 1; diff --git a/openkore_llm_knowledge/core/src/Interface.pm b/openkore_llm_knowledge/core/src/Interface.pm new file mode 100644 index 0000000000..29d6cf87f1 --- /dev/null +++ b/openkore_llm_knowledge/core/src/Interface.pm @@ -0,0 +1,250 @@ +######################################################################### +# OpenKore - User interface system +# +# Copyright (c) 2004 OpenKore development team +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +######################################################################### +## +# MODULE DESCRIPTION: User interface system. +# +# In OpenKore, the user interface code is seperated from the core code. +# Each user interface is implemented in a class. The Interface class is an +# abstract base class for all OpenKore user interface classes. + +package Interface; + +use strict; +use warnings; +no warnings 'redefine'; +use Time::HiRes qw(usleep); +use utf8; + +use Modules 'register'; +use Globals qw(%config $quit); +use Translation qw(T TF); +use Utils::Exceptions; + + +## +# Interface->loadInterface(String name) +# name: The class name of the interface to load, excluding the 'Interface::' prefix. +# Returns: The newly created interface object. +# +# Create a new interface of the specified class. +# +# Throws ModuleLoadException if the interface's Perl module cannot be loaded. +# Throws ClassCreateException if the interface class cannot be created. +sub loadInterface { + my ($self, $name) = @_; + + my $module = "Interface::$name"; + eval "use $module;"; + if ($@) { + ModuleLoadException->throw(error => "Cannot load interface $module. Error:\n$@", + module => $module); + } + + my $constructor = UNIVERSAL::can($module, 'new'); + if (!$constructor) { + ClassCreateException->throw(error => "Class $module has no constructor.", class => $module); + } + + my $interface = $constructor->($module); + Modules::register($module); + return $interface; +} + +## +# void $interface->mainLoop() +# +# Enter the interface's main loop. +sub mainLoop { + my $self = shift; + while (!$quit) { + usleep($config{sleepTime} || 10000); + $self->iterate(); + main::mainLoop(); + } +} + +## +# void $interface->iterate() +# +# Process messages in the user interface message queue. +# In other words: make sure the user interface updates itself. +# (redraw controls when necessary, etc.) +sub iterate { +} + +## +# String $interface->getInput(float timeout) +# timeout: Number of second to wait until keyboard data is available. +# Negative numbers will wait forever, 0 will not wait at all. +# Returns: The keyboard data (excluding newline), or undef if there's no +# keyboard data available. +# +# Reads keyboard data. + +## +# String $interface->query(String message, options...) +# message: A message to display when asking for input. +# Returns: The user input, or undef if the user cancelled. +# Requires: defined($message) +# +# Ask the user to enter a one-line input text. +# The following options are allowed: +# `l +# - cancelable - Whether the user is allowed to enter nothing. If this is set to true, +# then the user will be asked the same thing over and over until he +# replies with a non-empty input. The default is true. +# - title - A title to display in the query dialog. The default is "Query". +# - isPassword - Whether this query is a password query. The default is false. +# `l` +sub query { + my $self = shift; + my $message = shift; + my %args = @_; + + $args{title} = T("Query") if (!defined $args{title}); + $args{cancelable} = 1 if (!exists $args{cancelable}); + + my $title = "------------ $args{title} ------------"; + my $footer = '-' x length($title); + $message =~ s/\n+$//s; + $message = "$title\n$message\n$footer\n"; + + while (1) { + $self->writeOutput("message", $message, "input"); + $self->writeOutput("message", T("Enter your answer: "), "input"); + my $mode = $args{isPassword} ? -9 : -1; + my $result = $self->getInput($mode); + if (!defined($result) || $result eq '') { + if ($args{cancelable}) { + return undef; + } + } else { + return $result; + } + } +} + +## +# int $interface->showMenu(String message, Array<String>* choices, options...) +# message: The message to display while asking the user to make a choice. +# choices: The possible choices. +# Returns: The index of the chosen item, or -1 if the user cancelled. +# Requires: +# defined($message) +# defined($choices) +# for all $k in @{$choices}: defined($k) +# Ensures: -1 <= result < @{$choices} +# +# Ask the user to choose an item from a menu of choices. +# +# The following options are allowed: +# `l +# - title - The title to display when presenting the choices to the user. +# The default is 'Menu'. +# - cancelable - Whether the user is allowed to not choose. +# The default is true. +# `l` +sub showMenu { + my $self = shift; + my $message = shift; + my $choices = shift; + my %args = @_; + + $args{title} = "Menu" if (!defined $args{title}); + $args{cancelable} = 1 if (!exists $args{cancelable}); + + # Create a nicely formatted choice list. + my $maxNumberLength = length(@{$choices} + 1); + my $format = "%-" . $maxNumberLength . "s %-s\n"; + my $output = sprintf($format, "#", T("Choice")); + my $i = 0; + foreach my $item (@{$choices}) { + $output .= sprintf($format, $i, $item); + $i++; + } + + $message = "${output}------------------------\n$message"; + + while (1) { + my $choice = $self->query($message, + cancelable => $args{cancelable}, + title => $args{title}); + if (!defined($choice)) { + return -1; + } elsif ($choice !~ /^\d+$/ || $choice < 0 || $choice >= @{$choices}) { + $self->writeOutput("error", TF("'%s' is not a valid choice number.\n", $choice), "default"); + } else { + return $choice; + } + } +} + +## +# void $interface->writeOutput(String type, String message, String domain) +# Requires: defined($type) && defined($message) && defined($domain) +# +# Writes a message to the interface's console. +# This method should not be used directly, use Log::message() instead. +sub writeOutput { + # Do nothing; this is a dummy parent class +} + +## +# void $interface->beep() +# +# Emit a beep on the available audio device. +sub beep { + # Do nothing; this is a dummy parent class +} + +## +# String $interface->title([String title]) +# +# If $title is given, set the interface's window's title to $title. +# If not given, returns the current window title. +sub title { + # Do nothing; this is a dummy parent class +} + +## +# void $interface->errorDialog(String message, [boolean fatal = true]) +# message: The error message to display. +# fatal: Indicate that this is a fatal error (meaning that the application will +# exit after this dialog is closed). If set, the console interfaces +# will warn the user that the app is about to exit. +# Requires: defined($message) +# +# Display an error dialog. This function blocks until the user has closed the +# dialog. +# +# Consider using Log::error() if your message is not a fatal error, because +# Log::error() does not require any user interaction. +sub errorDialog { + my $self = shift; + my $message = shift; + my $fatal = shift; + $fatal = 1 unless defined $fatal; + + $self->writeOutput("error", "$message\n", "error"); + if ($fatal) { + $self->writeOutput("message", Translation::T("Press ENTER to exit this program.\n"), "console") + } else { + $self->writeOutput("message", Translation::T("Press ENTER to continue...\n"), "console") + } + $self->getInput(-1); +} + +1; \ No newline at end of file diff --git a/openkore_llm_knowledge/core/src/Log.pm b/openkore_llm_knowledge/core/src/Log.pm new file mode 100644 index 0000000000..08870e3adc --- /dev/null +++ b/openkore_llm_knowledge/core/src/Log.pm @@ -0,0 +1,458 @@ +######################################################################### +# OpenKore - Message Logging Framework +# +# This software is open source, licensed under the GNU General Public +# License, version 2. +# Basically, this means that you're allowed to modify and distribute +# this software. However, if you distribute modified versions, you MUST +# also distribute the source code. +# See http://www.gnu.org/licenses/gpl.html for the full license. +# +######################################################################### +## +# MODULE DESCRIPTION: Message Logging framework +# +# <h3>What is a logging framework and why is it needed?</h3> +# +# Kore used to print messages to the console using print(). There are several +# problems though: +# `l +# - Messages can only be printed to the console. If you want to print it +# to elsewhere you have to resort to all kinds of hacks (take a look +# at the code for sending console output to X-Kore, for example). +# - The messages have no classification. You have a message, but there's +# no easy way for the program to find out what kind of message it is; +# you don't know it's context. This means that the user can't really control +# what kind of messages he does and doesn't want to see. +# - Debug messages are all in the form of print "bla\n" if ($config{'verbose'}); +# You can either enable all debug messages, or nothing at all. For developers, +# the huge amount of debug messages can make things look cluttered. +# `l` +# +# The logging framework provides a new way to print messages: +# `l +# - You can print messages (of course). +# - You can classify messages: attaching a context (domain) to a message you print. +# - You can intercept messages and decide what else to do with it. You can write to +# a file, send to X-Kore (based on the message's domain), or whatever you want. +# - You can attach certain colors to messages of a certain domains. +# - You can choose what kind of message you do and do not want to see. +# `l` +# +# The most important functions are: +# Log::message(), Log::warning(), Log::error(), Log::debug() +# +# You pass the following arguments to those functions: +# `l +# - message: The message you want to print. +# - domain: The message domain (context). This is used to classify a message. +# - level: The message's verbosity level. The message will only be printed if this number +# is lower than or equal to $config{'verbose'} (or $config{'debug'} if this is a +# debug message). Important messages should have a low verbosity level, +# unimportant/redundant messages should have a high verbosity level. +# `l` + +# Known domains: +# attacked Monster attacks you +# attackedMiss Monster attacks you but miss +# attackMon You attack monster +# attackMonMiss You attack monster but miss +# connection Connection messages +# deal Deal messages +# drop Monster drop related +# emotion Emoticon +# equip Equipment Switching +# gmchat GM chat message +# guildchat Guild chat message +# info View info that's requested by the user (status, guild info, etc.) +# input Waiting for user input +# inventory Inventory related messages +# useItem You used item +# list List of information (monster list, player list, item list, etc.) +# load Loading config files +# menu Menu choices +# npc NPC messages +# party Party/follow related +# partychat Party chat messages +# plugins Messages about plugin handling +# pm Private chat message +# publicchat Public chat message +# route Routing/pathfinding messages +# sold Item sold while vending. +# skill Skill use unrelated to attack +# selfSkill Skills used by yourself +# startup Messages that are printed during startup. +# storage Storage item added/removed +# success An operation succeeded +# syntax Syntax check files +# system System messages +# teleport Teleporting +# xkore X-Kore system messages + +# Debug domains: +# ai_attack +# ai_autoCart +# ai_move +# parseInput +# parseMsg +# parseMsg_damage +# parseMsg_presence +# portalRecord +# sendPacket +# ai +# npc +# route +# useTeleport + +package Log; + +use strict; +use Exporter; +use Time::HiRes; +use base qw(Exporter); + +use Modules 'register'; +use Globals qw(%config $interface %consoleColors $field); +use Utils::DataStructures qw(binAdd existsInList); +use Utils qw(binAdd existsInList getFormattedDate); + +our @EXPORT_OK = qw(message warning error debug); + + +################################# +################################# +# VARIABLES +################################# +################################# + + +# The verbosity level for messages. Messages that have a higher verbosity than this will not be printed. +# Low level = important messages. High level = less important messages. +# If you set the current verbosity higher, you will see more messages. +our $warningVerbosity; +our $errorVerbosity; + +# Enable/disable printing certain domains to console. +# Usage: $messageConsole{$domain} = $enabled +our %messageConsole; +our %warningConsole; +our %errorConsole; +our %debugConsole; + +# Messages can also printed to files. These variables +# contain filenames of the files to print to. +# Usage: @{$messageFiles{$domain}} = (list of filenames) +our %messageFiles; +our %warningFiles; +our %errorFiles; +our %debugFiles; + +# Message hooks are stored here +our @hooks; + +# Enable/disable adding a timestamp to log files. +our $logTimestamp; +# Enable/disable adding a timestamp to chat logs. +our $chatTimestamp; + + +# use SelfLoader; 1; +# __DATA__ + + +################################# +################################# +# PRIVATE FUNCTIONS +################################# +################################# + + +sub MODINIT { + $warningVerbosity = 1; + $errorVerbosity = 1; + $logTimestamp = 1; + $chatTimestamp = 1; +} + +sub processMsg { + my $type = shift; + my $message = shift; + my $domain = (shift or "console"); + my $level = (shift or 0); + my $currentVerbosity = shift; + my $consoleVar = shift; + my $files = shift; + my (undef, undef, undef, $near) = caller(2); + my (undef, undef, undef, $far) = caller(3); + + $currentVerbosity = 1 if ($currentVerbosity eq ""); + + # Beep on certain domains + $interface->beep() if existsInList($config{beepDomains}, $domain) && + !(existsInList($config{beepDomains_notInTown}, $domain) && + $field->isCity); + + # Add timestamp if domain was specified in config.txt/showTimeDomains + if (existsInList($config{showTimeDomains}, $domain)) { + my @tmpdate = localtime(); + $tmpdate[5] += 1900; + $tmpdate[4]++; + for (my $i = 0; $i < @tmpdate; $i++) { + if ($tmpdate[$i] < 10) {$tmpdate[$i] = "0".$tmpdate[$i]}; + } + if (defined (my $format = $config{showTimeDomainsFormat})) { + $format =~ s/H/$tmpdate[2]/g; + $format =~ s/M/$tmpdate[1]/g; + $format =~ s/S/$tmpdate[0]/g; + $format =~ s/y/$tmpdate[5]/g; + $format =~ s/m/$tmpdate[4]/g; + $format =~ s/d/$tmpdate[3]/g; + $message = "$format $message"; + } else { + $message = "[$tmpdate[2]:$tmpdate[1]:$tmpdate[0]] $message"; + } + }; + + # Print to console if the current verbosity is high enough + if ($level <= $currentVerbosity) { + $consoleVar->{$domain} = 1 if (!defined($consoleVar->{$domain})); + if ($consoleVar->{$domain}) { + if ($interface) { + $message = "[$domain] " . $message if ($config{showDomain}); + my (undef, $microseconds) = Time::HiRes::gettimeofday; + $microseconds = substr($microseconds, 0, 2); + my $message2 = "[".getFormattedDate(int(time)).".$microseconds] ".$message; + if ($config{showTime}) { + $interface->writeOutput($type, $message2, $domain); + } else { + $interface->writeOutput($type, $message, $domain); + } + + if ($config{logConsole} && + open(F, ">>:utf8", $Settings::console_log_file)) { + print F $message2; + close(F); + } + } else { + print $message; + } + } + } + + # Print to files + foreach my $file (@{$files->{$domain}}) { + if (open(F, ">>:utf8", "$Settings::logs_folder/$file")) { + print F '['. getFormattedDate(int(time)) .'] ' if ($logTimestamp); + print F $message; + close(F); + } + } + + # Call hooks + foreach (@hooks) { + next if (!defined($_)); + $_->{'func'}->($type, $domain, $level, $currentVerbosity, $message, $_->{'user_data'}, $near, $far); + } +} + + +################################# +################################# +# PUBLIC METHODS +################################# +################################# + + +## +# Log::message(message, [domain], [level]) +# Requires: $message must be encoded in UTF-8. +# +# Prints a normal message. See the description for Log.pm for more details +# about the parameters. +sub message { + my ($message, $domain, $level) = @_; + $domain ||= "console"; + $level = 5 if existsInList($config{squelchDomains}, $domain); + $level = 0 if existsInList($config{verboseDomains}, $domain); + return processMsg("message", # type + $message, + $domain, + $level, + $config{'verbose'}, # currentVerbosity + \%messageConsole, + \%messageFiles); +} + + +## +# Log::warning(message, [domain], [level]) +# +# Prints a warning message. It warns the user that a possible non-fatal error has occured or will occur. +# See the description for Log.pm for more details about the parameters. +sub warning { + my ($message, $domain, $level) = @_; + $domain ||= "console"; + $level = 5 if existsInList($config{squelchDomains}, $domain); + $level = 0 if existsInList($config{verboseDomains}, $domain); + return processMsg("warning", + $message, + $domain, + $level, + $warningVerbosity, + \%warningConsole, + \%warningFiles); +} + + +## +# Log::error(message, [domain], [level]) +# Requires: $message must be encoded in UTF-8. +# +# Prints an error message. It tells the user that a non-recoverable error has +# occured. A "non-recoverable error" could either be a fatal error, or an +# error that prevents the program from performing an action the user requested. +# +# Examples of non-recoverable errors: +# `l +# - Kore receives the "You haven't paid for this account"-packet. The error is +# fatal, so the entire program must exit. +# - The user typed in an invalid/unrecognized command. Kore cannot perform the +# command the user requested, but will not exit because this error is not +# fatal. +# `l` +# See the description for Log.pm for more details about the parameters. +sub error { + my ($message, $domain, $level) = @_; + $domain ||= "console"; + return processMsg("error", + $message, + $domain, + $level, + $errorVerbosity, + \%errorConsole, + \%errorFiles); +} + + +## +# Log::debug(message, [domain], [level]) +# Requires: $message must be encoded in UTF-8. +# +# Prints a debugging message. See the description for Log.pm for more details about the parameters. +sub debug { + my ($message, $domain, $level) = @_; + $domain ||= "console"; + $level = 1 if (!defined $level); + $level = 0 if (existsInList($config{debugDomains}, $_[1])); + $level = 5 if (existsInList($config{squelchDomains}, $_[1])); + return processMsg("debug", + $message, + $domain, + $level, + (defined $config{'debug'}) ? $config{'debug'} : 0, + \%debugConsole, + \%debugFiles); +} + + +## +# Log::addHook(r_func, [user_data]) +# r_func: A reference to the function to call. +# user_data: Additional data to pass to r_func. +# Returns: An ID which you can use to remove this hook. +# +# Adds a hook. Every time Log::message(), Log::warning(), Log::error() or Log::debug() is called, +# r_func is also called, in the following way: +# <pre> +# r_func->($type, $domain, $level, $globalVerbosity, $message, $user_data); +# $type : One of the following: "message", "warning", "error", "debug". +# $domain : The message's domain. +# $level : The message's own verbosity level. +# $globalVerbosity : The global verbosity level. +# $message : The message itself. +# $user_data : The value of user_data, as passed to addHook. +# $near : The function that called "message", "warning", "error" or "debug" +# $far : The function that called $near +# </pre> +# +# See also: Log::delHook() +# +# Example: +# sub hook { +# my $type = shift; # "message" +# my $domain = shift; # "MyDomain" +# my $level = shift; # 2 +# my $globalVerbosity = shift; # 1 (equal to $config{'verbose'}) +# my $message = shift; # "Hello World" +# my $user_data = shift; # "my_user_data" +# my $near = shift; # "Commands::cmdWhere" +# my $far = shift; # "Commands::run" +# # Do whatever you want here +# } +# Log::addHook(\&hook, "my_user_data"); +# +# $config{'verbose'} = 1; +# # Note that the following function will not print anything to screen, +# # because it's verbosity level is higher than the global verbosity +# # level ($config{'verbose'}). +# Log::message("Hello World", "MyDomain", 2); # hook() will now be called +sub addHook { + my ($r_func, $user_data) = @_; + my %hook; + $hook{func} = $r_func; + $hook{user_data} = $user_data; + return binAdd(\@hooks, \%hook); +} + +## +# Log::delHook(ID) +# ID: A hook ID, as returned by addHook(). +# +# Removes a hook. r_func will not be called anymore. +# +# Example: +# my $ID = Log::addHook(\&hook); +# Log::message("Hello World", "MyDomain"); # hook() is called +# Log::delHook($ID); +# Log::message("Hello World", "MyDomain"); # hook() is NOT called +sub delHook { + my $ID = shift; + delete $hooks[$ID]; +} + +## +# Log::parseLogToFile(args,hash) +# +# args has to look like domain=file +# but can look like domain1=file1.txt;domain2=file2.txt,file3.txt +# +# The hash has to be a reference for the output hash. +sub parseLogToFile { + my $args = shift; + my $list = shift; + $args =~ s/\s//g; + my @domains = split (';', $args); + my $files; + foreach my $domain (@domains) { + ($domain,$files) = split ('=', $domain); + my @filesArray = split (',', $files); + $list->{$domain} = []; + foreach my $file (@filesArray) { + push(@{$list->{$domain}}, $file); + } + } +} + +## +# initLogFiles() +# +# This function should be called everytime config.txt is (re)loaded. +sub initLogFiles { + parseLogToFile($config{logToFile_Messages}, \%messageFiles) if $config{logToFile_Messages}; + parseLogToFile($config{logToFile_Warnings}, \%warningFiles) if $config{logToFile_Warnings}; + parseLogToFile($config{logToFile_Errors}, \%errorFiles) if $config{logToFile_Errors}; + parseLogToFile($config{logToFile_Debug}, \%debugFiles) if $config{logToFile_Debug}; +} + + +return 1; diff --git a/openkore_llm_knowledge/core/src/Misc.pm b/openkore_llm_knowledge/core/src/Misc.pm new file mode 100644 index 0000000000..ddc4737b77 --- /dev/null +++ b/openkore_llm_knowledge/core/src/Misc.pm @@ -0,0 +1,5764 @@ +######################################################################### +# OpenKore - Miscellaneous functions +# +# This software is open source, licensed under the GNU General Public +# License, version 2. +# Basically, this means that you're allowed to modify and distribute +# this software. However, if you distribute modified versions, you MUST +# also distribute the source code. +# See http://www.gnu.org/licenses/gpl.html for the full license. +# +# $Revision$ +# $Id$ +# +######################################################################### +## +# MODULE DESCRIPTION: Miscellaneous functions +# +# This module contains functions that do not belong in any other modules. +# The difference between Misc.pm and Utils.pm is that Misc.pm can have +# dependencies on other Kore modules. + +package Misc; + +use strict; +use Exporter; +use Carp::Assert; +use Data::Dumper; +use Compress::Zlib; +use base qw(Exporter); +use utf8; +use Math::Trig; + +use Globals; +use Log qw(message warning error debug); +use Plugins; +use FileParsers; +use Settings; +use Utils; +use Utils::Assert; +use Skill; +use Field; +use Network; +use Network::Send (); +use AI; +use Actor; +use Actor::You; +use Actor::Player; +use Actor::Monster; +use Actor::Party; +use Actor::NPC; +use Actor::Portal; +use Actor::Pet; +use Actor::Slave; +use Actor::Unknown; +use Time::HiRes qw(time usleep); +use Translation; +use Utils::Exceptions; +use Utils::PathFinding; + +our @EXPORT = ( + # Config modifiers + qw/auth + configModify + bulkConfigModify + setTimeout + saveConfigFile/, + + # Debugging + qw/debug_showSpots + visualDump/, + + # Field math + qw/calcRectArea2 + objectInsideSpell + objectInsideCasting + objectIsMovingTowards + objectIsMovingTowardsPlayer + get_dance_position/, + + # Inventory management + qw/inInventory + inventoryItemRemoved + storageGet + transferItems + cardName + itemName + itemNameSimple + itemNameToID + itemNameToIDList + containsItemNameToIDList/, + + # File Parsing and Writing + qw/chatLog + shopLog + monsterLog + playerLog + deadLog + searchStoreInfo/, + + # Logging + qw/itemLog/, + + # OS specific + qw/launchURL/, + + # Misc + qw/ + actorAdded + actorRemoved + actorListClearing + avoidGM_talk + avoidList_talk + avoidList_ID + calcStat + center + charSelectScreen + chatLog_clear + checkAllowedMap + checkFollowMode + isMySlaveID + isNotMySlaveID + is_aggressive + is_aggressive_slave + checkMonsterCleanness + slave_checkMonsterCleanness + createCharacter + deal + dealAddItem + drop + dumpData + getEmotionByCommand + getIDFromChat + getNPCName + getPlayerNameFromCache + getPortalDestName + getResponse + getSpellName + headgearName + itemLog_clear + look + lookAtPosition + lookAtPositionNaturally + getNaturalLookDirections + getClosestWalls + manualMove + meetingPosition + objectAdded + objectRemoved + items_control + pickupitems + mon_control + monsterName + positionNearPlayer + positionNearPortal + printItemDesc + processNameRequestQueue + quit + offlineMode + relog + sendMessage + setSkillUseTimer + setPartySkillTimer + setStatus + countCastOn + stripLanguageCode + switchConfigFile + updateDamageTables + updatePlayerNameCache + canUseTeleport + top10Listing + whenGroundStatus + writeStorageLog + getBestTarget + isSafe + isSafeActorQuery + isCellOccupied/, + + # Actor's Actions Text + qw/attack_string + skillCast_string + skillUse_string + skillUseLocation_string + skillUseNoDamage_string + status_string/, + + # AI Math + qw/lineIntersection + percent_hp + percent_sp + percent_ap + percent_weight/, + + # Misc Functions + qw/avoidGM_near + avoidList_near + compilePortals + compilePortals_check + portalExists + portalExists2 + portalExistsAirship + redirectXKoreMessages + monKilled + getActorName + getActorNames + findPartyUserID + getNPCInfo + skillName + checkSelfCondition + checkPlayerCondition + checkMonsterCondition + makeShop + openShop + closeShop + makeBuyerShop + openBuyerShop + closeBuyerShop + inLockMap + parseReload + setCharDeleteDate + toBase62 + fromBase62 + solveItemLink + solveMessage + solveMSG + absunit + autoNpcTalk/, + + # Npc buy and sell + qw/cancelNpcBuySell + completeNpcSell + completeNpcBuy/, + + # Char login + qw/CharacterLogin/, + ); + + +# use SelfLoader; 1; +# __DATA__ + + + +sub _checkActorHash($$$$) { + my ($name, $hash, $type, $hashName) = @_; + foreach my $actor (values %{$hash}) { + if (!UNIVERSAL::isa($actor, $type)) { + die "$name\nUnblessed item in $hashName list:\n" . + Dumper($hash); + } + } +} + +# Checks whether the internal state of some variables are correct. +sub checkValidity { + return if (!DEBUG || $ENV{OPENKORE_NO_CHECKVALIDITY}); + my ($name) = @_; + $name = "Validity check:" if (!defined $name); + + assertClass($char, 'Actor::You') if ($net && $net->getState() == Network::IN_GAME + && $net->isa('Network::XKore')); + assertClass($char, 'Actor::You') if ($char); + return; + + _checkActorHash($name, \%items, 'Actor::Item', 'item'); + _checkActorHash($name, \%monsters, 'Actor::Monster', 'monster'); + _checkActorHash($name, \%players, 'Actor::Player', 'player'); + _checkActorHash($name, \%pets, 'Actor::Pet', 'pet'); + _checkActorHash($name, \%npcs, 'Actor::NPC', 'NPC'); + _checkActorHash($name, \%portals, 'Actor::Portal', 'portals'); +} + + +####################################### +####################################### +### CATEGORY: Configuration modifiers +####################################### +####################################### + +sub auth { + my $user = shift; + my $flag = shift; + if ($flag) { + message TF("Authorized user '%s' for admin\n", $user), "success"; + } else { + message TF("Revoked admin privilages for user '%s'\n", $user), "success"; + } + $overallAuth{$user} = $flag; + writeDataFile(Settings::getControlFilename("overallAuth.txt"), \%overallAuth); +} + +## +# void configModify(String key, String value, ...) +# key: a key name. +# value: the new value. +# +# Changes the value of the configuration option $key to $value. +# Both %config and config.txt will be updated. +# +# You may also call configModify() with additional optional options: +# `l +# - autoCreate (boolean): Whether the configuration option $key +# should be created if it doesn't already exist. +# The default is true. +# - silent (boolean): By default, output will be printed, notifying the user +# that a config option has been changed. Setting this to +# true will surpress that output. +# `l` +sub configModify { + my $key = shift; + my $val = shift; + my %args; + + if (@_ == 1) { + $args{silent} = $_[0]; + } else { + %args = @_; + } + $args{autoCreate} = 1 if (!exists $args{autoCreate}); + + Plugins::callHook('configModify', { + key => $key, + val => $val, + additionalOptions => \%args + }); + + if (!$args{silent} && $key !~ /password/i) { + my $oldval = $config{$key}; + if (!defined $oldval) { + $oldval = "not set"; + } + + if ($config{$key} eq $val) { + if ($val) { + message TF("Config '%s' is already %s\n", $key, $val), "info"; + }else{ + message TF("Config '%s' is already *None*\n", $key), "info"; + } + return; + } + + if (!defined $val) { + message TF("Config '%s' unset (was %s)\n", $key, $oldval), "info"; + } else { + message TF("Config '%s' set to %s (was %s)\n", $key, $val, $oldval), "info"; + } + } + if ($args{autoCreate} && !exists $config{$key}) { + my $f; + if (open($f, ">>", Settings::getConfigFilename())) { + print $f "$key\n"; + close($f); + } + } + $config{$key} = $val; + Settings::update_log_filenames() if $key =~ /^(username|char|server)$/o; + saveConfigFile(); + + Plugins::callHook('post_configModify'); +} + +## +# bulkConfigModify (r_hash, [silent]) +# r_hash: key => value to change +# silent: if set to 1, do not print a message to the console. +# +# like configModify but for more than one value at the same time. +sub bulkConfigModify { + my $r_hash = shift; + my $silent = shift; + my $oldval; + + + my %create_keys; + foreach my $key (keys %{$r_hash}) { + Plugins::callHook('configModify', { + key => $key, + val => $r_hash->{$key}, + silent => $silent + }); + + $oldval = $config{$key}; + + if (!exists $config{$key}) { + $create_keys{$key} = 1; + } + + $config{$key} = $r_hash->{$key}; + + if ($key =~ /password/i) { + message TF("Config '%s' set to %s (was *not-displayed*)\n", $key, $r_hash->{$key}), "info" unless ($silent); + } else { + message TF("Config '%s' set to %s (was %s)\n", $key, $r_hash->{$key}, $oldval), "info" unless ($silent); + } + } + + if (scalar keys %create_keys > 0) { + my $f; + if (open($f, ">>", Settings::getConfigFilename())) { + foreach my $key (keys %create_keys) { + print $f "$key\n"; + } + close($f); + } + } + + saveConfigFile(); + + Plugins::callHook('post_configModify'); +} + +## +# saveConfigFile() +# +# Writes %config to config.txt. +sub saveConfigFile { + writeDataFileIntact(Settings::getConfigFilename(), \%config); +} + +sub setTimeout { + my $timeout = shift; + my $time = shift; + my $silent = shift; + my %args; + + if (@_ == 1) { + $args{silent} = $_[0]; + } else { + %args = @_; + } + + $args{autoCreate} = 1 if (!exists $args{autoCreate}); + + Plugins::callHook('setTimeout', { + timeout => $timeout, + time => $time, + additionalOptions => \%args + }); + + if (!$args{silent}) { + my $oldtime = $timeout{$timeout}{timeout}; + if (!defined $oldtime) { + $oldtime = "not set"; + } + + if ($timeout{$timeout}{timeout} eq $time) { + if ($time) { + message TF("Timeout '%s' is already %s\n", $timeout, $time), "info" unless ($silent); + } else { + message TF("Timeout '%s' is already *None*\n", $timeout), "info" unless ($silent); + } + return; + } + + if (!defined $time) { + message TF("Timeout '%s' unset (was %s)\n", $timeout, $oldtime), "info" unless ($silent); + } else { + message TF("Timeout '%s' set to %s (was %s)\n", $timeout, $time, $oldtime), "info" unless ($silent); + } + } + if ($args{autoCreate} && !exists $timeout{$timeout}{timeout}) { + my $f; + if (open($f, ">>", Settings::getControlFilename("timeouts.txt"))) { + print $f "$timeout\n"; + close($f); + } + } + + $timeout{$timeout}{timeout} = $time; + writeDataFileIntact2(Settings::getControlFilename("timeouts.txt"), \%timeout); +} + + +####################################### +####################################### +### Category: Debugging +####################################### +####################################### + +our %debug_showSpots_list; + +sub debug_showSpots { + return unless $net->clientAlive(); + my $ID = shift; + my $spots = shift; + my $special = shift; + + if ($debug_showSpots_list{$ID}) { + foreach (@{$debug_showSpots_list{$ID}}) { + my $msg = pack("C*", 0x20, 0x01) . pack("V", $_); + $net->clientSend($msg); + } + } + + my $i = 1554; + $debug_showSpots_list{$ID} = []; + foreach (@{$spots}) { + next if !defined $_; + my $msg = pack("C*", 0x1F, 0x01) + . pack("V*", $i, 1550) + . pack("v*", $_->{x}, $_->{y}) + . pack("C*", 0x93, 0); + $net->clientSend($msg); + $net->clientSend($msg); + push @{$debug_showSpots_list{$ID}}, $i; + $i++; + } + + if ($special) { + my $msg = pack("C*", 0x1F, 0x01) + . pack("V*", 1553, 1550) + . pack("v*", $special->{x}, $special->{y}) + . pack("C*", 0x83, 0); + $net->clientSend($msg); + $net->clientSend($msg); + push @{$debug_showSpots_list{$ID}}, 1553; + } +} + +## +# visualDump(data [, label]) +# +# Show the bytes in $data on screen as hexadecimal. +# Displays the label if provided. +sub visualDump { + my ($msg, $label) = @_; + my $dump; + my $puncations = quotemeta '~!@#$%^&*()_-+=|\"\''; + + # doesn't work right with debugPacket_sent + #no encoding 'utf8'; + #use bytes; + + $dump = "================================================\n"; + if (defined $label) { + $dump .= sprintf("%-15s [%d bytes] %s\n", $label, length($msg), getFormattedDate(int(time))); + } else { + $dump .= sprintf("%d bytes %s\n", length($msg), getFormattedDate(int(time))); + } + + for (my $i = 0; $i < length($msg); $i += 16) { + my $line; + my $data = substr($msg, $i, 16); + my $rawData = ''; + + for (my $j = 0; $j < length($data); $j++) { + my $char = substr($data, $j, 1); + if (ord($char) < 32 || ord($char) > 126) { + $rawData .= '.'; + } else { + $rawData .= substr($data, $j, 1); + } + } + + $line = getHex(substr($data, 0, 8)); + $line .= ' ' . getHex(substr($data, 8)) if (length($data) > 8); + + $line .= ' ' x (50 - length($line)) if (length($line) < 54); + $line .= " $rawData\n"; + $line = sprintf("%3d> ", $i) . $line; + $dump .= $line; + } + message $dump; +} + + +####################################### +####################################### +### CATEGORY: Field math +####################################### +####################################### + +## +# calcRectArea2($x, $y, $radius, $minRange) +# Returns: an array with position hashes. Each has contains an x and a y key. +# +# Creates a rectangle with center ($x,$y) and radius $radius, +# and returns a list of positions inside the rectangle that are +# not closer than $minRange to the center. +sub calcRectArea2 { + my ($cx, $cy, $r, $min) = @_; + + my @rectangle; + for (my $x = $cx - $r; $x <= $cx + $r; $x++) { + for (my $y = $cy - $r; $y <= $cy + $r; $y++) { + next if distance({x => $cx, y => $cy}, {x => $x, y => $y}) < $min; + push(@rectangle, {x => $x, y => $y}); + } + } + return @rectangle; +} + +## +# objectInsideSpell(object, [ignore_party_members = 1]) +# object: reference to a player or monster hash. +# +# Checks whether an object is inside someone else's spell area. +# (Traps are also "area spells"). +sub objectInsideSpell { + my $object = shift; + my $ignore_party_members = shift; + $ignore_party_members = 1 if (!defined $ignore_party_members); + + my ($x, $y) = ($object->{pos_to}{x}, $object->{pos_to}{y}); + foreach (@spellsID) { + my $spell = $spells{$_}; + if ((!$ignore_party_members || !$char->{party}{joined} || !$char->{party}{users}{$spell->{sourceID}}) + && $spell->{sourceID} ne $accountID + && isNotMySlaveID($spell->{sourceID}) + && $spell->{pos}{x} == $x && $spell->{pos}{y} == $y) { + return 1; + } + } + return 0; +} + +sub objectInsideCasting { + my ($monster) = @_; + + # TODO: Is there any situation where we should use calcPosFromPathfinding or calcPosFromTime here? + my $monsterPos = calcPosition($monster); + + foreach my $caster (@$playersList, @$slavesList) { + next if (isMySlaveID($caster->{ID})); + next if ($char->{party} && $char->{party}{joined} && exists $char->{party}{users}{$caster->{ID}}); + + next unless (exists $caster->{casting} && defined $caster->{casting} && $caster->{casting}); + + my $cast = $caster->{casting}; + next unless ($cast->{x} != 0 || $cast->{y} != 0); + + my $skill = $cast->{skill}; + my $skillID = $skill->getIDN(); + my $range = judgeSkillArea($skillID); + + my %coords; + $coords{x} = $cast->{x}; + $coords{y} = $cast->{y}; + + my $distFromCenter = blockDistance($monsterPos, \%coords); + + if ($distFromCenter <= $range) { + #warning TF("Target %s is inside skill %s (range %d | dist %d) from caster %s.\n", $monster, $skill, $range, $distFromCenter, $caster), 'slave_attack'; + return 1; + } + } + return 0; +} + +## +# objectIsMovingTowards(object1, object2, [max_variance]) +# +# Check whether $object1 is moving towards $object2. +sub objectIsMovingTowards { + my $obj = shift; + my $obj2 = shift; + my $max_variance = (shift || 15); + + if (!timeOut($obj->{time_move}, $obj->{time_move_calc})) { + # $obj is still moving + my %vec; + getVector(\%vec, $obj->{pos_to}, $obj->{pos}); + return checkMovementDirection($obj->{pos}, \%vec, $obj2->{pos_to}, $max_variance); + } + return 0; +} + +## +# objectIsMovingTowardsPlayer(object, [ignore_party_members = 1]) +# +# Check whether an object is moving towards a player. +sub objectIsMovingTowardsPlayer { + my $obj = shift; + my $ignore_party_members = shift; + $ignore_party_members = 1 if (!defined $ignore_party_members); + + if (!timeOut($obj->{time_move}, $obj->{time_move_calc}) && @playersID) { + # Monster is still moving, and there are players on screen + my %vec; + getVector(\%vec, $obj->{pos_to}, $obj->{pos}); + + for my $player (@$playersList) { + my $ID = $player->{ID}; + next if ( + ($ignore_party_members && $char->{party}{joined} && $char->{party}{users}{$ID}) + || (defined($player->{name}) && existsInList($config{tankersList}, $player->{name})) + || $player->statusActive('EFFECTSTATE_SPECIALHIDING')); + if (checkMovementDirection($obj->{pos}, \%vec, $player->{pos}, 15)) { + return 1; + } + } + } + return 0; +} + +use constant USE_DIAGONAL => 0; + +## +# get_dance_position(slave, target) +# slave: reference to the slave actor which is dancing. +# target: reference to the target actor which you are attacking. +# +# Returns: reference to a hash containing both x and y coordinates of the next dance position. +# +# Dance algorithm used in attack_dance +# Based on AzzyAI dance +sub get_dance_position { + my ($slave, $target) = @_; + my ($dy, $dx); + + # TODO: Is there any situation where we should use calcPosFromPathfinding or calcPosFromTime here? + my $my_pos = calcPosition($slave); + my $target_pos = calcPosition($target); + + my @possible; + + # same x and y + if ($my_pos->{x} == $target_pos->{x} && $my_pos->{y} == $target_pos->{y}) { + push(@possible,( + { x => 1, y => 0 }, + { x => 0, y => 1 }, + { x => 0, y => -1 }, + { x => -1, y => 0 } + )); + push(@possible,( + { x => 1, y => 1 }, + { x => -1, y => -1 }, + { x => 1, y => -1 }, + { x => -1, y => 1 } + )) if (USE_DIAGONAL == 1); + + } elsif ($my_pos->{x} == $target_pos->{x}) { + $dy = abs($my_pos->{y} - $target_pos->{y}); + if ($my_pos->{y} > $target_pos->{y}) { + # same x, 2 y north + if ($dy == 2) { + push(@possible,( + { x => 0, y => -1 } + )); + push(@possible,( + { x => 1, y => -1 }, + { x => -1, y => -1 } + )) if (USE_DIAGONAL == 1); + # same x, 1 y north + } else { + push(@possible,( + { x => 1, y => 0 }, + { x => -1, y => 0 }, + { x => 0, y => 1 } + )); + } + } else { + # same x, 2 y south + if ($dy == 2) { + push(@possible,( + { x => 0, y => 1 } + )); + push(@possible,( + { x => 1, y => 1 }, + { x => -1, y => 1 } + )) if (USE_DIAGONAL == 1); + # same x, 1 y south + } else { + push(@possible,( + { x => 1, y => 0 }, + { x => -1, y => 0 }, + { x => 0, y => -1 } + )); + } + } + + } elsif ($my_pos->{y} == $target_pos->{y}) { + $dx = abs($my_pos->{x} - $target_pos->{x}); + if ($my_pos->{x} > $target_pos->{x}) { + # same y, 2 x east + if ($dx == 2) { + push(@possible,( + { x => -1, y => 0 } + )); + push(@possible,( + { x => -1, y => 1 }, + { x => -1, y => -1 } + )) if (USE_DIAGONAL == 1); + # same y, 1 x east + } else { + push(@possible,( + { x => 0, y => 1 }, + { x => 0, y => -1 }, + { x => 1, y => 0 } + )); + } + } else { + # same y, 2 x west + if ($dx == 2) { + push(@possible,( + { x => 1, y => 0 } + )); + push(@possible,( + { x => 1, y => 1 }, + { x => 1, y => -1 } + )) if (USE_DIAGONAL == 1); + # same y, 1 x west + } else { + push(@possible,( + { x => 0, y => 1 }, + { x => 0, y => -1 }, + { x => -1, y => 0 } + )); + } + } + + } elsif ($my_pos->{y} > $target_pos->{y}) { + # 1 northeast + if ($my_pos->{x} > $target_pos->{x}) { + push(@possible,( + { x => -1, y => 0 }, + { x => 0, y => -1 } + )); + push(@possible,( + { x => -1, y => 1 }, + { x => 1, y => -1 } + )) if (USE_DIAGONAL == 1); + # 1 northwest + } else { + push(@possible,( + { x => 1, y => 0 }, + { x => 0, y => -1 } + )); + push(@possible,( + { x => 1, y => 1 }, + { x => -1, y => -1 } + )) if (USE_DIAGONAL == 1); + } + + } else { + # 1 southeast + if ($my_pos->{x} > $target_pos->{x}) { + push(@possible,( + { x => -1, y => 0 }, + { x => 0, y => 1 } + )); + push(@possible,( + { x => -1, y => -1 }, + { x => 1, y => 1 } + )) if (USE_DIAGONAL == 1); + # 1 southwest + } else { + push(@possible,( + { x => 1, y => 0 }, + { x => 0, y => 1 } + )); + push(@possible,( + { x => 1, y => -1 }, + { x => -1, y => 1 } + )) if (USE_DIAGONAL == 1); + } + } + + shuffleArray(\@possible); + + my %dance_pos; + foreach my $pos_add (@possible) { + my $x = $my_pos->{x} + $pos_add->{x}; + my $y = $my_pos->{y} + $pos_add->{y}; + next unless ($field->isWalkable($x, $y)); + + $dance_pos{x} = $x; + $dance_pos{y} = $y; + last; + } + + return \%dance_pos; +} + +######################################### +######################################### +### CATEGORY: Logging +######################################### +######################################### + +# TODO: merge? +sub itemLog { + my $crud = shift; + return if (!$config{'itemHistory'}); + open ITEMLOG, ">>:utf8", $Settings::item_log_file; + print ITEMLOG "[".getFormattedDate(int(time))."] $crud"; + close ITEMLOG; +} + +sub chatLog { + my $type = shift; + my $message = shift; + open CHAT, ">>:utf8", $Settings::chat_log_file; + print CHAT "[".getFormattedDate(int(time))."][".uc($type)."] $message"; + close CHAT; +} + +sub shopLog { + my $crud = shift; + open SHOPLOG, ">>:utf8", $Settings::shop_log_file; + print SHOPLOG "[".getFormattedDate(int(time))."] $crud"; + close SHOPLOG; +} + +sub monsterLog { + my $crud = shift; + return if (!$config{'monsterLog'}); + open MONLOG, ">>:utf8", $Settings::monster_log_file; + print MONLOG "[".getFormattedDate(int(time))."] $crud\n"; + close MONLOG; +} + +sub playerLog { + my $crud = shift; + return if (!$config{'playerLog'}); + open PLAYERLOG, ">>:utf8", $Settings::player_log_file; + print PLAYERLOG "[".getFormattedDate(int(time))."] $crud\n"; + close PLAYERLOG; +} + +sub deadLog { + my $crud = shift; + return if (!$config{'logDead'}); + open DEADLOG, ">>:utf8", $Settings::dead_log_file; + print DEADLOG "[DEAD] $crud\n"; + close DEADLOG; +} + +######################################### +######################################### +### CATEGORY: Operating system specific +######################################### +######################################### + + +## +# launchURL(url) +# +# Open $url in the operating system's preferred web browser. +sub launchURL { + my $url = shift; + + if ($^O eq 'MSWin32') { + require Utils::Win32; + Utils::Win32::ShellExecute(0, undef, $url); + + } else { + my $mod = 'use POSIX;'; + eval $mod; + + # This is a script I wrote for the autopackage project + # It autodetects the current desktop environment + my $detectionScript = <<EOF; + function detectDesktop() { + if [[ "\$DISPLAY" = "" ]]; then + return 1 + fi + + local LC_ALL=C + local clients + if ! clients=`xlsclients`; then + return 1 + fi + + if echo "\$clients" | grep -qE '(gnome-panel|nautilus|metacity)'; then + echo gnome + elif echo "\$clients" | grep -qE '(kicker|slicker|karamba|kwin)'; then + echo kde + else + echo other + fi + return 0 + } + detectDesktop +EOF + + my ($r, $w, $desktop); + + my $pid = IPC::Open2::open2($r, $w, '/bin/bash'); + print $w $detectionScript; + close $w; + $desktop = <$r>; + $desktop =~ s/\n//; + close $r; + waitpid($pid, 0); + + sub checkCommand { + foreach (split(/:/, $ENV{PATH})) { + return 1 if (-x "$_/$_[0]"); + } + return 0; + } + + if (checkCommand('xdg-open')) { + launchApp(1, 'xdg-open', $url); + + } elsif ($desktop eq "gnome" && checkCommand('gnome-open')) { + launchApp(1, 'gnome-open', $url); + + } elsif ($desktop eq "kde") { + launchApp(1, 'kfmclient', 'exec', $url); + + } else { + if (checkCommand('firefox')) { + launchApp(1, 'firefox', $url); + } elsif (checkCommand('mozilla')) { + launchApp(1, 'mozilla', $url); + } else { + $interface->errorDialog(TF("No suitable browser detected. Please launch your favorite browser and go to:\n%s", $url)); + } + } + } +} + + +####################################### +####################################### +### CATEGORY: Other functions +####################################### +####################################### + +# TODO: move actorAdded/Removed to Actor? +sub actorAddedRemovedVars { + my ($actor) = @_; + # returns (type, list, hash) + if ($actor->isa ('Actor::Item')) { + return ('item', \@itemsID, \%items); + } elsif ($actor->isa ('Actor::Player')) { + return ('player', \@playersID, \%players); + } elsif ($actor->isa ('Actor::Monster')) { + return ('monster', \@monstersID, \%monsters); + } elsif ($actor->isa ('Actor::Portal')) { + return ('portal', \@portalsID, \%portals); + } elsif ($actor->isa ('Actor::Pet')) { + return ('pet', \@petsID, \%pets); + } elsif ($actor->isa ('Actor::NPC')) { + return ('npc', \@npcsID, \%npcs); + } elsif ($actor->isa ('Actor::Slave')) { + return ('slave', \@slavesID, \%slaves); + } elsif ($actor->isa ('Actor::Elemental')) { + return ('elemental', \@elementalsID, \%elementals); + }else { + return (undef, undef, undef); + } +} + +sub actorAdded { + my (undef, $source, $arg) = @_; + my ($actor, $index) = @{$arg}; + + $actor->{binID} = $index; + + my ($type, $list, $hash) = actorAddedRemovedVars ($actor); + + if (defined $type) { + debug TF("Actor added: %s %s (%s), size %s\n", $type, (unpack 'V', $actor->{ID}), $actor->{binID}, $source->size), 'actorlist', 3; + + if (DEBUG && scalar(keys %{$hash}) + 1 != $source->size()) { + use Data::Dumper; + + my $ol = ''; + my $items = $source->getItems(); + foreach my $item (@{$items}) { + $ol .= $item->nameIdx . "\n"; + } + + die "$type: " . scalar(keys %{$hash}) . " + 1 != " . $source->size() . "\n" . + "List:\n" . + Dumper($list) . "\n" . + "Hash:\n" . + Dumper($hash) . "\n" . + "ObjectList:\n" . + $ol; + } + should(binSize($list) + 1, $source->size()) if DEBUG; + + binAdd($list, $actor->{ID}); + $hash->{$actor->{ID}} = $actor; + objectAdded($type, $actor->{ID}, $actor); + + should(scalar(keys %{$hash}), $source->size()) if DEBUG; + should(binSize($list), $source->size()) if DEBUG; + } else { + warning "Unknown actor type in actorAdded\n", 'actorlist' if DEBUG; + } +} + +sub actorRemoved { + my (undef, $source, $arg) = @_; + my ($actor, $index) = @{$arg}; + + my ($type, $list, $hash) = actorAddedRemovedVars ($actor); + + if (defined $type) { + debug TF("Actor removed: %s %s (%s), size %s\n", $type, (unpack 'V', $actor->{ID}), $actor->{binID}, $source->size), 'actorlist', 3; + + if (DEBUG && scalar(keys %{$hash}) - 1 != $source->size()) { + use Data::Dumper; + + my $ol = ''; + my $items = $source->getItems(); + foreach my $item (@{$items}) { + $ol .= $item->nameIdx . "\n"; + } + + die "$type:" . scalar(keys %{$hash}) . " - 1 != " . $source->size() . "\n" . + "List:\n" . + Dumper($list) . "\n" . + "Hash:\n" . + Dumper($hash) . "\n" . + "ObjectList:\n" . + $ol; + } + should(binSize($list) - 1, $source->size()) if DEBUG; + + binRemove($list, $actor->{ID}); + delete $hash->{$actor->{ID}}; + objectRemoved($type, $actor->{ID}, $actor); + + if ($type eq "player" && $venderLists{ID}) { + binRemove(\@venderListsID, $actor->{ID}); + delete $venderLists{$actor->{ID}}; + } + + if ($type eq "player" && $buyerLists{ID}) { + binRemove(\@buyerListsID, $actor->{ID}); + delete $buyerLists{$actor->{ID}}; + } + + should(scalar(keys %{$hash}), $source->size()) if DEBUG; + should(binSize($list), $source->size()) if DEBUG; + } else { + warning "Unknown actor type in actorRemoved\n", 'actorlist' if DEBUG; + } +} + +sub actorListClearing { + undef %items; + undef %players; + undef %monsters; + undef %portals; + undef %npcs; + undef %pets; + undef %slaves; + undef %elementals; + undef @itemsID; + undef @playersID; + undef @monstersID; + undef @portalsID; + undef @npcsID; + undef @petsID; + undef @slavesID; + undef @elementalsID; +} + +sub calcStat { + my $damage = shift; + $totaldmg += $damage; +} + +## +# center(string, width, [fill]) +# +# This function will center $string within a field $width characters wide, +# using $fill characters for padding on either end of the string for +# centering. If $fill is not specified, a space will be used. +sub center { + my ($string, $width, $fill) = @_; + + $fill ||= ' '; + my $left = int(($width - length($string)) / 2); + my $right = ($width - length($string)) - $left; + return $fill x $left . $string . $fill x $right; +} + +# Returns: 0 if user chose to quit, 1 if user chose a character, 2 if user created or deleted a character +sub charSelectScreen { + my %plugin_args = (autoLogin => shift); + # A list of character names + my @charNames; + # An array which maps an index in @charNames to an index in @chars + my @charNameIndices; + my $mode; + + # Check system version to delete a character + my $charDeleteVersion; + $charDeleteVersion = 1 if ($masterServer->{charBlockSize} >= 132); + + # the client also does this + $questList = {}; + + TOP: { + undef $mode; + @charNames = (); + @charNameIndices = (); + } + + for (my $num = 0; $num < @chars; $num++) { + next unless ($chars[$num] && %{$chars[$num]}); + if (0) { + # The old (more verbose) message + swrite( + T("------- Character \@< ---------\n" . + "Name: \@<<<<<<<<<<<<<<<<<<<<<<<<\n" . + "Job: \@<<<<<<< Job Exp: \@<<<<<<<\n" . + "Lv: \@<<<<<<< Str: \@<<<<<<<<\n" . + "J.Lv: \@<<<<<<< Agi: \@<<<<<<<<\n" . + "Exp: \@<<<<<<< Vit: \@<<<<<<<<\n" . + "HP: \@||||/\@|||| Int: \@<<<<<<<<\n" . + "SP: \@||||/\@|||| Dex: \@<<<<<<<<\n" . + "zeny: \@<<<<<<<<<< Luk: \@<<<<<<<<\n" . + "-------------------------------"), + $num, $chars[$num]{'name'}, $jobs_lut{$chars[$num]{'jobID'}}, $chars[$num]{'exp_job'}, + $chars[$num]{'lv'}, $chars[$num]{'str'}, $chars[$num]{'lv_job'}, $chars[$num]{'agi'}, + $chars[$num]{'exp'}, $chars[$num]{'vit'}, $chars[$num]{'hp'}, $chars[$num]{'hp_max'}, + $chars[$num]{'int'}, $chars[$num]{'sp'}, $chars[$num]{'sp_max'}, $chars[$num]{'dex'}, + $chars[$num]{'zeny'}, $chars[$num]{'luk'}); + } + + my $messageDeleteDate; + if ($chars[$num]{deleteDate}) { + if (int(time) > $chars[$num]{deleteDateTimestamp}) { + $messageDeleteDate = TF("\n -> Deleting is possible since %s.", $chars[$num]{deleteDate}); + } else { + $messageDeleteDate = TF("\n -> It will be deleted lefting %s!", $chars[$num]{deleteDate}); + } + } + + my $messageMapName; + + if (exists $chars[$num]{last_map} && $chars[$num]{last_map}) { + my $map_lut_key = $chars[$num]{last_map} . ".rsw"; + + if (exists $maps_lut{$map_lut_key} && length $maps_lut{$map_lut_key} <= 16) { + $messageMapName = sprintf(", %s", $maps_lut{$map_lut_key}); + } else { + $messageMapName = sprintf(", %s", $chars[$num]{last_map}); + } + } + + push @charNames, TF("Slot %d: %s (%s, %s, level %d/%d%s)%s", + $num, + $chars[$num]{name}, + $jobs_lut{$chars[$num]{'jobID'}}, + $sex_lut{$chars[$num]{sex}}, + $chars[$num]{lv}, + $chars[$num]{lv_job}, + $messageMapName, + $messageDeleteDate); + push @charNameIndices, $num; + } + + return 0 if (exists $charSvrSet{sync_Count} && $charSvrSet{sync_received_characters} < $charSvrSet{sync_Count}); + + if (@charNames) { + my $msg = center(T(" Character List "), 79, '-') ."\n"; + $msg .= join("\n", @charNames) ."\n"; + $msg .= ('-'x79) . "\n"; + message $msg, "connection"; + } + return 1 if ($net->clientAlive && $net->version); + + Plugins::callHook('charSelectScreen', \%plugin_args); + return $plugin_args{pin_return} if ($plugin_args{pin_return}); + return $plugin_args{return} if ($plugin_args{return}); + + if ($plugin_args{autoLogin} && @chars && $config{char} ne "" && $chars[$config{char}]) { + if ($chars[$config{char}]{deleteDate}) { + error TF("Cannot select character \"%s\" that requested for deletion.\n", $chars[$config{char}]{name}); + configModify("char",''); + relog(10); + return 0; + } else { + $messageSender->sendCharLogin($config{char}); + $timeout{charlogin}{time} = time; + return 1; + } + } + + my @choices = @charNames; + push @choices, T('Create a new character'); + if (@chars) { + if ($charDeleteVersion) { + push @choices, T('Delete or cancel the deletion a character'); + } else { + push @choices, T('Delete a character'); + } + } else { + message T("There are no characters on this account.\n"), "connection"; + if ($config{char} ne "switch" && defined($char)) { + message T("Please use the : \"conf char switch\" command, if you are switching your account.\n"), "connection"; + relog(10); + return 0; + } + } + + my $choice = $interface->showMenu( + T("Please choose a character or an action."), \@choices, + title => T("Character selection")); + if ($choice == -1) { + # User cancelled + quit(); + return 0; + + } elsif ($choice < @charNames) { + if ($chars[$charNameIndices[$choice]]{deleteDate}) { + error TF("Cannot select character \"%s\" that requested for deletion.\n", $chars[$charNameIndices[$choice]]{name}); + goto TOP; + } else { + # Character chosen + configModify('char', $charNameIndices[$choice], 1); + $messageSender->sendCharLogin($config{char}); + $timeout{charlogin}{time} = time; + return 1; + } + + } elsif ($choice == @charNames) { + # 'Create character' chosen + $mode = "create"; + + } else { + # 'Delete character' chosen + $mode = "delete"; + } + + if ($mode eq "create") { + while (1) { + my $message; + if ( $messageSender->{char_create_version} == 0x0A39 ) { + $message + = T( "Please enter the desired properties for your characters, in this form:\n" ) + . T( "(slot) \"(name)\" [ (hairstyle) [(haircolor)] ] [(job)] [(sex)]\n" ) + . T( "Job should be one of 'novice' or 'summoner' (default is 'novice').\n" ) + . T( "Sex should be one of 'M' or 'F' (default is 'F').\n" ); + } elsif ($messageSender->{char_create_version}) { + $message = T("Please enter the desired properties for your characters, in this form:\n" . + "(slot) \"(name)\" [ (hairstyle) [(haircolor)] ]"); + } else { + $message = T("Please enter the desired properties for your characters, in this form:\n" . + "(slot) \"(name)\" [ (str) (agi) (vit) (int) (dex) (luk) [ (hairstyle) [(haircolor)] ] ]"); + } + + my $input = $interface->query($message); + unless ($input =~ /\S/) { + goto TOP; + } else { + my @args = parseArgs($input); + if (@args < 2) { + $interface->errorDialog(T("You didn't specify enough parameters."), 0); + next; + } + + message TF("Creating character \"%s\" in slot \"%s\"...\n", $args[1], $args[0]), "connection"; + $timeout{charlogin}{time} = time; + last if (createCharacter(@args)); + } + } + + } elsif ($mode eq "delete") { + my $choice = $interface->showMenu( + T("Select the character you want to delete."), + \@charNames, + title => T("Delete character")); + if ($choice == -1) { + goto TOP; + } + my $charIndex = @charNameIndices[$choice]; + + if ($charDeleteVersion) { + $messageSender->{char_delete_slot} = $charIndex; + + if ($chars[$charIndex]{deleteDate}) { + my $confirm = $interface->showMenu( + TF("Are you ABSOLUTELY SURE you want to delete:\n%s", $charNames[$choice]), + [T("Back"), T("No, don't delete"), T("Yes, delete")], + title => T("Confirm delete")); + + if ($confirm == 0) { + goto TOP; + } elsif ($confirm == 1) { # Request cancel + $chars[$charIndex]{deleteDate} = undef; + $chars[$charIndex]{deleteDateTimestamp} = undef; + message TF("Canceling delete request for character %s...\n", $chars[$charIndex]{name}), "connection"; + $messageSender->sendCharDelete2Cancel($chars[$charIndex]{charID}); + } elsif ($confirm == 2 && int(time) > $chars[$charIndex]{deleteDateTimestamp}) { + my $code = $interface->query(T("Enter your birthdate, deletion code or e-mail.")); + if (!defined($code)) { + goto TOP; + } + + my $confirmation = $interface->showMenu( + TF("Are you ABSOLUTELY SURE you want to delete:\n%s", $charNames[$choice]), + [T("No, don't delete"), T("Yes, delete")], + title => T("Confirm delete")); + if ($confirmation != 1) { + goto TOP; + } + + $messageSender->sendCharDelete2Accept($chars[$charIndex]{charID}, $code); + message TF("Deleting character %s...\n", $chars[$charIndex]{name}), "connection"; + $AI::temp::delIndex = $charIndex; + $timeout{charlogin}{time} = time; + } else { + message TF("Character %s cannot be deleted yet. Please wait until %s\n", $chars[$charIndex]{name}, $chars[$charIndex]{deleteDate}), "info"; + goto TOP; + } + } else { + $messageSender->sendCharDelete2($chars[$charIndex]{charID}); # Request date deletion. + } + } else { + my $email = $interface->query("Enter your email address."); + if (!defined($email)) { + goto TOP; + } + + my $confirmation = $interface->showMenu( + TF("Are you ABSOLUTELY SURE you want to delete:\n%s", $charNames[$choice]), + [T("No, don't delete"), T("Yes, delete")], + title => T("Confirm delete")); + if ($confirmation != 1) { + goto TOP; + } + + $messageSender->sendCharDelete($chars[$charIndex]{charID}, $email); + message TF("Deleting character %s...\n", $chars[$charIndex]{name}), "connection"; + $AI::temp::delIndex = $charIndex; + $timeout{charlogin}{time} = time; + } + } + return 2; +} + +sub chatLog_clear { + if (-f $Settings::chat_log_file) { + unlink($Settings::chat_log_file); + } +} + +## +# checkAllowedMap($map) +# +# Checks whether $map is in $config{allowedMaps}. +# Disconnects if it is not, and $config{allowedMaps_reaction} != 0. +sub checkAllowedMap { + my $map = shift; + + return unless AI::state == AI::AUTO; + return unless $config{allowedMaps}; + return if existsInList($config{allowedMaps}, $map); + return if $config{allowedMaps_reaction} == 0; + + warning TF("The current map (%s) is not on the list of allowed maps.\n", $map); + main::chatLog("k", TF("** The current map (%s) is not on the list of allowed maps.\n", $map)); + main::chatLog("k", T("** Exiting...\n")); + quit(); +} + +## +# checkFollowMode() +# Returns: 1 if in follow mode, 0 if not. +# +# Check whether we're current in follow mode. +sub checkFollowMode { + my $followIndex; + if ($config{follow} && defined($followIndex = AI::findAction("follow"))) { + return 1 if (AI::args($followIndex)->{following}); + } + return 0; +} + +sub isMySlaveID { + my ($ID, $exclude) = @_; + return 0 if (defined $exclude && $ID eq $exclude); + return 0 unless ($char); + return 1 if (exists $char->{homunculus} && exists $char->{homunculus}{ID} && $char->{homunculus}{ID} eq $ID); + return 0 unless ($char->{slaves}); + return 0 unless (exists $char->{slaves}{$ID}); + return 1; +} + +sub isNotMySlaveID { + my ($ID) = @_; + return 0 if ($char && $char->{slaves} && exists $char->{slaves}{$ID}); + return 1; +} + +sub is_aggressive { + my ($monster, $control, $type, $party) = @_; + + my %plugin_args; + $plugin_args{monster} = $monster; + $plugin_args{control} = $control; + $plugin_args{type} = $type; + $plugin_args{party} = $party; + $plugin_args{return} = 0; + Plugins::callHook('ai_check_Aggressiveness' => \%plugin_args); + + if ( + ($plugin_args{return}) || + ($type == 2 && $control->{attack_auto} == 2) || + (($monster->{dmgToYou} || $monster->{missedYou} || $monster->{castOnToYou})) || + ($config{"attackAuto_considerDamagedAggressive"} && $monster->{dmgFromYou} > 0) || + ($party && + ( + $monster->{dmgToParty} + || $monster->{missedToParty} + || $monster->{dmgFromParty} + || scalar(grep { isMySlaveID($_) } keys %{$monster->{missedFromPlayer}}) + || scalar(grep { isMySlaveID($_) } keys %{$monster->{dmgFromPlayer}}) + || scalar(grep { isMySlaveID($_) } keys %{$monster->{castOnByPlayer}}) + || scalar(grep { isMySlaveID($_) } keys %{$monster->{missedToPlayer}}) + || scalar(grep { isMySlaveID($_) } keys %{$monster->{dmgToPlayer}}) + || scalar(grep { isMySlaveID($_) } keys %{$monster->{castOnToPlayer}}) + ) + ) + ) { + return 1; + } + return 0; +} + +sub is_aggressive_slave { + my ($slave, $monster, $control, $type, $party) = @_; + + my %plugin_args; + $plugin_args{slave} = $slave; + $plugin_args{monster} = $monster; + $plugin_args{control} = $control; + $plugin_args{type} = $type; + $plugin_args{return} = 0; + Plugins::callHook('ai_slave_check_Aggressiveness' => \%plugin_args); + + if ( + ($plugin_args{return}) || + ($type && ($control->{attack_auto} == 2)) || + ($monster->{dmgToPlayer}{$slave->{ID}} || $monster->{missedToPlayer}{$slave->{ID}} || $monster->{castOnToPlayer}{$slave->{ID}}) || + ($config{$slave->{configPrefix}."attackAuto_considerDamagedAggressive"} && $monster->{dmgFromPlayer}{$slave->{ID}} > 0) || + ($party && + ( + $monster->{missedFromYou} + || $monster->{dmgFromYou} + || $monster->{castOnByYou} + || $monster->{dmgToYou} + || $monster->{missedYou} + || $monster->{castOnToYou} + || scalar(grep { isMySlaveID($_, $slave->{ID}) } keys %{$monster->{missedFromPlayer}}) + || scalar(grep { isMySlaveID($_, $slave->{ID}) } keys %{$monster->{dmgFromPlayer}}) + || scalar(grep { isMySlaveID($_, $slave->{ID}) } keys %{$monster->{castOnByPlayer}}) + || scalar(grep { isMySlaveID($_, $slave->{ID}) } keys %{$monster->{missedToPlayer}}) + || scalar(grep { isMySlaveID($_, $slave->{ID}) } keys %{$monster->{dmgToPlayer}}) + || scalar(grep { isMySlaveID($_, $slave->{ID}) } keys %{$monster->{castOnToPlayer}}) + ) + ) + ) { + return 1; + } + return 0; +} + +## +# boolean checkMonsterCleanness(Bytes ID) +# ID: the monster's ID. +# Requires: $ID is a valid monster ID. +# +# Checks whether a monster is "clean" (not being attacked by anyone). +sub checkMonsterCleanness { + my ($ID) = @_; + return 1 if (!$config{attackAuto}); + return 1 if $playersList->getByID($ID) || $slavesList->getByID($ID); + my $monster = $monstersList->getByID($ID); + + # If party attacked monster, or if monster attacked/missed party + if ($config{attackAuto_party} && ($monster->{dmgFromParty} > 0 || $monster->{missedFromParty} > 0 || $monster->{dmgToParty} > 0 || $monster->{missedToParty} > 0)) { + return 1; + } + + if ( + scalar(grep { isMySlaveID($_) } keys %{$monster->{missedFromPlayer}}) + || scalar(grep { isMySlaveID($_) } keys %{$monster->{dmgFromPlayer}}) + || scalar(grep { isMySlaveID($_) } keys %{$monster->{castOnByPlayer}}) + || scalar(grep { isMySlaveID($_) } keys %{$monster->{missedToPlayer}}) + || scalar(grep { isMySlaveID($_) } keys %{$monster->{dmgToPlayer}}) + || scalar(grep { isMySlaveID($_) } keys %{$monster->{castOnToPlayer}}) + ) { + return 1; + } + + if ($config{aggressiveAntiKS}) { + # Aggressive anti-KS mode, for people who are paranoid about not kill stealing. + + # If we attacked the monster first, do not drop it, we are being KSed + return 1 if ($monster->{dmgFromYou} || $monster->{missedFromYou} || $monster->{castOnByYou}); + + # If others attacked the monster then always drop it, wether it attacked us or not! + if ( + scalar(grep { isNotMySlaveID($_) } keys %{$monster->{missedFromPlayer}}) + || scalar(grep { isNotMySlaveID($_) } keys %{$monster->{dmgFromPlayer}}) + || scalar(grep { isNotMySlaveID($_) } keys %{$monster->{castOnByPlayer}}) + || scalar(grep { isNotMySlaveID($_) } keys %{$monster->{missedToPlayer}}) + || scalar(grep { isNotMySlaveID($_) } keys %{$monster->{dmgToPlayer}}) + || scalar(grep { isNotMySlaveID($_) } keys %{$monster->{castOnToPlayer}}) + ) { + return 0; + } + } + + # If monster attacked/missed you + return 1 if ($monster->{'dmgToYou'} || $monster->{'missedYou'} || $monster->{'castOnToYou'}); + + # If we're in follow mode + if (defined(my $followIndex = AI::findAction("follow"))) { + my $following = AI::args($followIndex)->{following}; + my $followID = AI::args($followIndex)->{ID}; + + if ($following) { + # And master attacked monster, or the monster attacked/missed master + if ( + $monster->{dmgToPlayer}{$followID} > 0 + || $monster->{missedToPlayer}{$followID} > 0 + || $monster->{dmgFromPlayer}{$followID} > 0 + || $monster->{missedFromPlayer}{$followID} > 0 + || $monster->{castOnToPlayer}{$followID} > 0 + || $monster->{castOnByPlayer}{$followID} > 0 + ) { + return 1; + } + } + } + + if (objectInsideSpell($monster)) { + # Prohibit attacking this monster in the future + $monster->{dmgFromPlayer}{$char->{ID}} = 1; + return 0; + } + + if (objectInsideCasting($monster)) { + # Prohibit attacking this monster in the future + $monster->{dmgFromPlayer}{$char->{ID}} = 1; + return 0; + } + + #check party casting on mob + my $allowed = 1; + if (scalar(keys %{$monster->{castOnByPlayer}}) > 0) + { + foreach my $ID1 (keys %{$monster->{castOnByPlayer}}) + { + my $source = Actor::get($ID1); + unless ( + existsInList($config{tankersList}, $source->{name}) || + isMySlaveID($ID1) || + ($char->{party}{joined} && $char->{party}{users}{$ID1} && %{$char->{party}{users}{$ID1}}) + ) { + $allowed = 0; + last; + } + } + } + + if ( + $allowed + && scalar(grep { isNotMySlaveID($_) } keys %{$monster->{missedFromPlayer}}) == 0 + && scalar(grep { isNotMySlaveID($_) } keys %{$monster->{dmgFromPlayer}}) == 0 + && scalar(grep { isNotMySlaveID($_) } keys %{$monster->{missedToPlayer}}) == 0 + && scalar(grep { isNotMySlaveID($_) } keys %{$monster->{dmgToPlayer}}) == 0 + && scalar(grep { isNotMySlaveID($_) } keys %{$monster->{castOnToPlayer}}) == 0 + ) { + # The monster might be getting lured by another player. + # So we check whether it's walking towards any other player, but only + # if we haven't already attacked the monster. + if ($monster->{dmgFromYou} || $monster->{missedFromYou} || $monster->{castOnByYou}) { + return 1; + } else { + return !objectIsMovingTowardsPlayer($monster); + } + } + + # The monster didn't attack you. + # Other players attacked it, or it attacked other players. + if ($monster->{dmgFromYou} || $monster->{missedFromYou} || $monster->{castOnByYou}) { + # If you have already attacked the monster before, then consider it clean + return 1; + } + # If you haven't attacked the monster yet, it's unclean. + + return 0; +} + +sub slave_checkMonsterCleanness { + my ($slave, $ID) = @_; + return 1 if (!$config{$slave->{configPrefix}.'attackAuto'}); + return 1 if $playersList->getByID($ID) || $slavesList->getByID($ID); + my $monster = $monstersList->getByID($ID); + + # Since openKore considers the slave as a member of the player party we check for attacks against/made by master and/or other slaves + if ( + $config{$slave->{configPrefix}.'attackAuto_party'} && + ( + $monster->{dmgFromYou} + || $monster->{missedFromYou} + || $monster->{castOnByYou} + || $monster->{dmgToYou} + || $monster->{missedYou} + || $monster->{castOnToYou} + || scalar(grep { isMySlaveID($_, $slave->{ID}) } keys %{$monster->{missedFromPlayer}}) + || scalar(grep { isMySlaveID($_, $slave->{ID}) } keys %{$monster->{dmgFromPlayer}}) + || scalar(grep { isMySlaveID($_, $slave->{ID}) } keys %{$monster->{castOnByPlayer}}) + || scalar(grep { isMySlaveID($_, $slave->{ID}) } keys %{$monster->{missedToPlayer}}) + || scalar(grep { isMySlaveID($_, $slave->{ID}) } keys %{$monster->{dmgToPlayer}}) + || scalar(grep { isMySlaveID($_, $slave->{ID}) } keys %{$monster->{castOnToPlayer}}) + ) + ) { + return 1; + } + + if ($config{aggressiveAntiKS}) { + # Aggressive anti-KS mode, for people who are paranoid about not kill stealing. + + # If we attacked the monster first, do not drop it, we are being KSed + return 1 if ($monster->{dmgFromPlayer}{$slave->{ID}} || $monster->{missedFromPlayer}{$slave->{ID}} || $monster->{castOnByPlayer}{$slave->{ID}}); + + # If others attacked the monster then always drop it, wether it attacked us or not! + if ( + scalar(grep { isNotMySlaveID($_) } keys %{$monster->{missedFromPlayer}}) + || scalar(grep { isNotMySlaveID($_) } keys %{$monster->{dmgFromPlayer}}) + || scalar(grep { isNotMySlaveID($_) } keys %{$monster->{castOnByPlayer}}) + || scalar(grep { isNotMySlaveID($_) } keys %{$monster->{missedToPlayer}}) + || scalar(grep { isNotMySlaveID($_) } keys %{$monster->{dmgToPlayer}}) + || scalar(grep { isNotMySlaveID($_) } keys %{$monster->{castOnToPlayer}}) + ) { + return 0; + } + } + + # If monster attacked/missed you + return 1 if ($monster->{dmgToPlayer}{$slave->{ID}} || $monster->{missedToPlayer}{$slave->{ID}} || $monster->{castOnToPlayer}{$slave->{ID}}); + + if (objectInsideSpell($monster)) { + # Prohibit attacking this monster in the future + $monster->{dmgFromPlayer}{$char->{ID}} = 1; + return 0; + } + + if (objectInsideCasting($monster)) { + # Prohibit attacking this monster in the future + $monster->{dmgFromPlayer}{$char->{ID}} = 1; + return 0; + } + + #check party casting on mob + my $allowed = 1; + if (scalar(keys %{$monster->{castOnByPlayer}}) > 0) + { + foreach my $ID1 (keys %{$monster->{castOnByPlayer}}) + { + my $source = Actor::get($ID1); + unless ( + existsInList($config{tankersList}, $source->{name}) || + isMySlaveID($ID1, $slave->{ID}) || + ($char->{party}{joined} && $char->{party}{users}{$ID1} && %{$char->{party}{users}{$ID1}}) + ) { + $allowed = 0; + last; + } + } + } + + # If monster hasn't been attacked by other players + if ( + $allowed + && scalar(grep { isNotMySlaveID($_) } keys %{$monster->{missedFromPlayer}}) == 0 + && scalar(grep { isNotMySlaveID($_) } keys %{$monster->{dmgFromPlayer}}) == 0 + && scalar(grep { isNotMySlaveID($_) } keys %{$monster->{missedToPlayer}}) == 0 + && scalar(grep { isNotMySlaveID($_) } keys %{$monster->{dmgToPlayer}}) == 0 + && scalar(grep { isNotMySlaveID($_) } keys %{$monster->{castOnToPlayer}}) == 0 + ) { + # The monster might be getting lured by another player. + # So we check whether it's walking towards any other player, but only + # if we haven't already attacked the monster. + if ($monster->{dmgFromPlayer}{$slave->{ID}} || $monster->{missedFromPlayer}{$slave->{ID}} || $monster->{castOnByPlayer}{$slave->{ID}}) { + return 1; + } else { + return !objectIsMovingTowardsPlayer($monster); + } + } + + # The monster didn't attack you. + # Other players attacked it, or it attacked other players. + if ($monster->{dmgFromPlayer}{$slave->{ID}} || $monster->{missedFromPlayer}{$slave->{ID}} || $monster->{castOnByPlayer}{$slave->{ID}}) { + # If you have already attacked the monster before, then consider it clean + return 1; + } + # If you haven't attacked the monster yet, it's unclean. + + return 0; +} + +## +# boolean createCharacter(int slot, String name, int [str,agi,vit,int,dex,luk] = 5) +# slot: The slot in which to create the character (1st slot is 0). +# name: The name of the character to create. +# Returns: Whether the parameters are correct. Only a character creation command +# will be sent to the server if all parameters are correct. +# +# Create a new character. You must be currently connected to the character login server. +# +# Observation: From the RagexeRE_2012_03_07f, are no longer the chosen artributos when +# selecting the character! +sub createCharacter { + my $slot = shift; + my $name = shift; + + if ($net->getState() != 3 && $net->getState() != 1.5) { + $interface->errorDialog(T("We're not currently connected to the character login server."), 0); + return 0; + } elsif ($slot !~ /^\d+$/) { + $interface->errorDialog(TF("Slot \"%s\" is not a valid number.", $slot), 0); + return 0; + } elsif (exists $charSvrSet{total_slot} && ($slot < 0 || $slot > $charSvrSet{total_slot})) { + $interface->errorDialog(TF("The slot must be comprised between 0 and %s.", $charSvrSet{total_slot}), 0); + return 0; + } elsif (exists $charSvrSet{normal_slot} && ($slot < 0 || $slot > $charSvrSet{normal_slot})) { + $interface->errorDialog(TF("The slot must be comprised between 0 and %s.", $charSvrSet{normal_slot}), 0); + return 0; + } elsif ($chars[$slot]) { + $interface->errorDialog(TF("Slot %s already contains a character (%s).", $slot, $chars[$slot]{name}), 0); + return 0; + } elsif (length($name) > 23) { + $interface->errorDialog(T("Name must not be longer than 23 characters."), 0); + return 0; + } + + if ( $messageSender->{char_create_version} == 0x0A39 ) { + my $hair_style = shift if @_ && $_[0] =~ /^\d+$/; + my $hair_color = shift if @_ && $_[0] =~ /^\d+$/; + + if ( grep { !/^(novice|summoner|male|female|m|f)$/io } @_ ) { + $interface->errorDialog( T( 'Unknown job or sex.' ), 0 ); + return 0; + } + + my $job_id = scalar( grep {/^summoner$/io} @_ ) ? 4218 : 0; + my $sex = scalar( grep {/^male|m$/io} @_ ) ? 1 : 0; + + $messageSender->sendCharCreate( $slot, $name, $hair_style, $hair_color, $job_id, $sex ); + } elsif ($messageSender->{char_create_version}) { + my ($hair_style, $hair_color) = @_; + + $messageSender->sendCharCreate($slot, $name, + $hair_style, $hair_color); + } else { + my ($str, $agi, $vit, $int, $dex, $luk, $hair_style, $hair_color) = @_; + + if (!@_) { + ($str, $agi, $vit, $int, $dex, $luk) = (5, 5, 5, 5, 5, 5); + } + + for ($str, $agi, $vit, $int, $dex, $luk) { + if ($_ > 9 || $_ < 1) { + $interface->errorDialog(T("Stats must be comprised between 1 and 9."), 0); + return 0; + } + } + + for ($str+$int, $agi+$luk, $vit+$dex) { + if ($_ != 10) { + $interface->errorDialog(T("The sums Str + Int, Agi + Luk and Vit + Dex must all be equal to 10."), 0); + return 0; + } + } + + $messageSender->sendCharCreate($slot, $name, + $str, $agi, $vit, $int, $dex, $luk, + $hair_style, $hair_color); + } + + return 1; +} + +## +# void deal(Actor::Player player) +# Requires: defined($player) +# Ensures: exists $outgoingDeal{ID} +# +# Sends $player a deal request. +sub deal { + my $player = $_[0]; + assert(defined $player, "Can't deal with undef player") if DEBUG; + assertClass($player, 'Actor::Player') if DEBUG; + + $outgoingDeal{ID} = $player->{ID}; + $messageSender->sendDeal($player->{ID}); +} + +## +# dealAddItem($item, $amount) +# +# Adds $amount of $item to the current deal. +sub dealAddItem { + my ($item, $amount) = @_; + + $messageSender->sendDealAddItem($item->{ID}, $amount); + $currentDeal{lastItemAmount} = $amount; +} + +## +# drop(itemIndex, amount) +# +# Drops $amount of the item specified by $itemIndex. If $amount is not specified or too large, it defaults +# to the number of items you have. +sub drop { + my ($itemIndex, $amount) = @_; + my $item = $char->inventory->get($itemIndex); + if ($item) { + if (!$amount || $amount > $item->{amount}) { + $amount = $item->{amount}; + } + $messageSender->sendDrop($item->{ID}, $amount); + } +} + +sub dumpData { + my $msg = shift; + my $silent = shift; + my $desc = shift; + my $dump; + my $puncations = quotemeta '~!@#$%^&*()_+|\"\''; + my $messageID = uc(unpack("H2", substr($msg, 1, 1))) . uc(unpack("H2", substr($msg, 0, 1))); + + $dump = "\n\n================================================\n" . + getFormattedDate(int(time)) . "\n\n" . + ($desc == 1 ? 'Send ' : 'Recv ') . #0 = Recv (default), 1 = Send + $messageID . ' [' . + length($msg) . " bytes]\n\n"; + + for (my $i = 0; $i < length($msg); $i += 16) { + my $line; + my $data = substr($msg, $i, 16); + my $rawData = ''; + + for (my $j = 0; $j < length($data); $j++) { + my $char = substr($data, $j, 1); + + if (($char =~ /\W/ && $char =~ /\S/ && !($char =~ /[$puncations]/)) + || ($char eq chr(10) || $char eq chr(13) || $char eq "\t")) { + $rawData .= '.'; + } else { + $rawData .= substr($data, $j, 1); + } + } + + $line = getHex(substr($data, 0, 8)); + $line .= ' ' . getHex(substr($data, 8)) if (length($data) > 8); + + $line .= ' ' x (50 - length($line)) if (length($line) < 54); + $line .= " $rawData\n"; + $line = sprintf("%3d> ", $i) . $line; + $dump .= $line; + } + + open DUMP, ">> DUMP.txt"; + print DUMP $dump; + close DUMP; + + debug "$dump\n", "parseMsg", 2; + message T("Message Dumped into DUMP.txt!\n"), undef, 1 unless ($silent); +} + +sub getEmotionByCommand { + my $command = shift; + foreach (keys %emotions_lut) { + if (existsInList($emotions_lut{$_}{command}, $command)) { + return $_; + } + } + return undef; +} + +sub getIDFromChat { + my $r_hash = shift; + my $msg_user = shift; + my $match_text = shift; + my $qm; + if ($match_text !~ /\w+/ || $match_text eq "me" || $match_text eq "") { + foreach (keys %{$r_hash}) { + next if ($_ eq ""); + if ($msg_user eq $r_hash->{$_}{name}) { + return $_; + } + } + } else { + foreach (keys %{$r_hash}) { + next if ($_ eq ""); + $qm = quotemeta $match_text; + if ($r_hash->{$_}{name} =~ /$qm/i) { + return $_; + } + } + } + return undef; +} + +## +# getNPCName(ID) +# ID: the packed ID of the NPC +# Returns: the name of the NPC +# +# Find the name of an NPC: could be NPC, monster, or unknown. +sub getNPCName { + my $ID = shift; + if ((my $npc = $npcsList->getByID($ID))) { + return $npc->name; + } elsif ((my $monster = $monstersList->getByID($ID))) { + return $monster->name; + } else { + return T("Unknown #") . unpack("V1", $ID); + } +} + +## +# getPlayerNameFromCache(player) +# player: an Actor::Player object. +# Returns: 1 on success, 0 if the player isn't in cache. +# +# Retrieve a player's name from cache and modify the player object. +sub getPlayerNameFromCache { + my ($player) = @_; + + return if (!$config{cachePlayerNames}); + my $entry = $playerNameCache{$player->{ID}}; + return if (!$entry); + + # Check whether the cache entry is too old or inconsistent. + # Default cache life time: 15 minutes. + if (timeOut($entry->{time}, $config{cachePlayerNames_duration}) || $player->{lv} != $entry->{lv} || $player->{jobID} != $entry->{jobID}) { + binRemove(\@playerNameCacheIDs, $player->{ID}); + delete $playerNameCache{$player->{ID}}; + compactArray(\@playerNameCacheIDs); + return 0; + } + + $player->{name} = $entry->{name}; + $player->{guild} = $entry->{guild} if ($entry->{guild}); + return 1; +} + +sub getPortalDestName { + my $ID = shift; + my %hash; # We only want unique names, so we use a hash + foreach (keys %{$portals_lut{$ID}{'dest'}}) { + my $key = $portals_lut{$ID}{'dest'}{$_}{'map'}; + $hash{$key} = 1; + } + + my @destinations = sort keys %hash; + return join('/', @destinations); +} + +sub getResponse { + my $type = quotemeta shift; + + my @keys; + foreach my $key (keys %responses) { + if ($key =~ /^$type\_\d+$/) { + push @keys, $key; + } + } + + my $msg = $responses{$keys[int(rand(@keys))]}; + $msg =~ s/\%\$(\w+)/$responseVars{$1}/eig; + return $msg; +} + +sub getSpellName { + my $spell = shift; + return $spells_lut{$spell} || "Unknown $spell"; +} + +## +# inInventory($itemName, $quantity = 1) +# +# Returns the item's index (can be 0!) if you have at least $quantity units of the item +# specified by $itemName in your inventory. +# Returns nothing otherwise. +sub inInventory { + my ($itemIndex, $quantity) = @_; + $quantity ||= 1; + + my $item = $char->inventory->getByName($itemIndex); + return if !$item; + return unless $item->{amount} >= $quantity; + return $item->{binID}; +} + +## +# inventoryItemRemoved($binID, $amount) +# +# Removes $amount of $binID from $char->{inventory}. +# Also prints a message saying the item was removed (unless it is an arrow you +# fired). +sub inventoryItemRemoved { + my ($binID, $amount) = @_; + + return if $amount == 0; + my $item = $char->inventory->get($binID); + if (!$char->{arrow} || ($item && $char->{arrow} ne $item->{ID})) { + # This item is not an equipped arrow + message TF("Inventory Item Removed: %s (%d) x %d\n", $item->{name}, $binID, $amount), "inventory"; + } + $item->{amount} -= $amount; + if ($item->{amount} <= 0) { + if ($char->{arrow} && $char->{arrow} eq $item->{ID}) { + message TF("Run out of Arrow/Bullet: %s (%d)\n", $item->{name}, $binID), "inventory"; + delete $char->{equipment}{arrow}; + delete $char->{arrow}; + } + if( defined $item->{'name'}) { + $char->inventory->remove($item); + } else { + warning "Server sended item_removed but we not have this item anymore. binID: ".$binID."\n"; + } + } + $itemChange{$item->{name}} -= $amount; + Plugins::callHook('inventory_item_removed', { + item => $item, + index => $binID, + amount => $amount, + remaining => ($item->{amount} <= 0 ? 0 : $item->{amount}) + }); +} + +## +# storageItemRemoved($binID, $amount) +# +# Removes $amount of $binID from $char->{storage}. +# Also prints a message saying the item was removed +sub storageItemRemoved { + my ($binID, $amount) = @_; + + return if $amount == 0; + my $item = $char->storage->get($binID); + message TF("Storage Item Removed: %s (%d) x %s\n", $item->{name}, $binID, $amount), "storage"; + $item->{amount} -= $amount; + if ($item->{amount} <= 0) { + $char->storage->remove($item); + } + $itemChange{$item->{name}} -= $amount; + Plugins::callHook('storage_item_removed', { + item => $item, + index => $binID, + amount => $amount, + remaining => ($item->{amount} <= 0 ? 0 : $item->{amount}) + }); +} + +## +# cartItemRemoved($binID, $amount) +# +# Removes $amount of $binID from $char->{cart}. +# Also prints a message saying the item was removed +sub cartItemRemoved { + my ($binID, $amount) = @_; + + return if $amount == 0; + my $item = $char->cart->get($binID); + message TF("Cart Item Removed: %s (%d) x %s\n", $item->{name}, $binID, $amount), "cart"; + $item->{amount} -= $amount; + if ($item->{amount} <= 0) { + $char->cart->remove($item); + } + $itemChange{$item->{name}} -= $amount; + Plugins::callHook('cart_item_removed', { + item => $item, + index => $binID, + amount => $amount, + remaining => ($item->{amount} <= 0 ? 0 : $item->{amount}) + }); +} + +# Resolve the name of a card +sub cardName { + my $cardID = shift; + + # If card name is unknown, just return ?number + my $card = $items_lut{$cardID}; + return "?$cardID" if !$card; + $card =~ s/ Card$//; + return $card; +} + +# Resolve the name of a monster +# This function will only look at the data in monsters.txt +# DO NOT USE THIS FUNCTION when you want to get the real name of a monster, +# servers can change this name internally use getNPCName instead. +sub monsterName { + my $ID = shift; + return 'Unknown' unless defined($ID); + return 'None' unless $ID; + return $monsters_lut{$ID} || "Unknown #$ID"; +} + +# Resolve the name of a simple item +sub itemNameSimple { + my $ID = shift; + return T("Unknown") unless defined($ID); + return T("None") unless $ID; + return $items_lut{$ID} || T("Unknown #")."$ID"; +} + +## +# itemName($item) +# +# Resolve the name of an item. $item should be a hash with these keys: +# nameID => integer index into %items_lut +# cards => 8-byte binary data as sent by server +# upgrade => integer upgrade level +sub itemName { + my $item = shift; + + my $name = itemNameSimple($item->{nameID}); + + # Resolve item prefix/suffix (carded or forged) + my $prefix = ""; + my $suffix = ""; + my @cards; + my %cards; + + my $item_len = length($item); + my $card_unpack; + + # FIXME WORKAROUND TO ITEMID 4BYTES + if ($masterServer->{itemListType}) { + $card_unpack = "V"; + } else { + $card_unpack = "v"; + } + + my $card_len = length pack $card_unpack; + for (my $i = 0; $i < 4; $i++) { + my $card = unpack($card_unpack, substr($item->{cards}, $i*$card_len, $card_len)); + next unless $card; + push(@cards, $card); + ($cards{$card} ||= 0) += 1; + } + if ($cards[0] == 254) { + # Alchemist-made potion + # + # Ignore the "cards" inside. + } elsif ($cards[0] == 65280 || $cards[0] == 1) { + # Pet egg + # cards[0] == 65280 + # substr($item->{cards}, 2, 4) = packed pet ID + # cards[3] == 1 if named, 0 if not named + + } elsif ($cards[0] == 255) { + # Forged weapon + # + # Display e.g. "VVS Earth" or "Fire" + my $elementID = $cards[1] % 10; + my $elementName = $elements_lut{$elementID}; + my $starCrumbs = ($cards[1] >> 8) / 5; + + # Translation-friendly + if ($starCrumbs == 1) { + $prefix .= T("VS "); + } elsif ($starCrumbs == 2) { + $prefix .= T("VVS "); + } elsif ($starCrumbs == 3) { + $prefix .= T("VVVS "); + } + + # $prefix .= "$elementName " if ($elementName ne ""); + $suffix = "$elementName" if ($elementName ne ""); + } elsif (@cards) { + # Carded item + # + # List cards in alphabetical order. + # Stack identical cards. + # e.g. "Hydra*2,Mummy*2", "Hydra*3,Mummy" + $suffix = join(':', map { + cardName($_).($cards{$_} > 1 ? "*$cards{$_}" : '') + } sort { cardName($a) cmp cardName($b) } keys %cards); + } + + my $total_options = 0; + + my @options = grep { $_->{type} } map { my @c = unpack 'vvC', $_;{ type => $c[0], value => $c[1], param => $c[2] } } unpack '(a5)*', $item->{options} || ''; + foreach ( @options ) { + if ( $_->{type} ) { + $total_options++; + } + } + + my $numSlots = $itemSlotCount_lut{$item->{nameID}} if ($prefix eq ""); + + my $display = ""; + $display .= T("BROKEN ") if $item->{broken}; + $display .= "+$item->{upgrade} " if $item->{upgrade}; + $display .= $prefix if $prefix; + $display .= $name; + $display .= " [$suffix]" if $suffix; + $display .= " [$numSlots]" if $numSlots; + $display .= " [$total_options Option]" if $total_options; + + return $display; +} + +sub itemNameToID { + my $itemName = lc shift; + return if !$itemName; + $itemName =~ s/^[\t\s]*//; # Remove leading tabs and whitespace + $itemName =~ s/\s+$//g; # Remove trailing whitespace + for my $hashID (keys %items_lut) { + if ($itemName eq lc($items_lut{$hashID})) { + return $hashID; + } + } +} + +sub itemNameToIDList { + my $itemName = lc shift; + return if !$itemName; + $itemName =~ s/^[\t\s]*//; # Remove leading tabs and whitespace + $itemName =~ s/\s+$//g; # Remove trailing whitespace + + my @id_list; + + for my $hashID (keys %items_lut) { + if ($itemName eq lc($items_lut{$hashID})) { + push @id_list, $hashID; + } + } + + return @id_list; +} + +sub containsItemNameToIDList { + my $itemName = lc shift; + return if !$itemName; + $itemName =~ s/^[\t\s]*//; # Remove leading tabs and whitespace + $itemName =~ s/\s+$//g; # Remove trailing whitespace + + my @id_list; + + for my $hashID (keys %items_lut) { + if (index(lc($items_lut{$hashID}), $itemName) != -1) { + push @id_list, $hashID; + } + } + + return @id_list; +} + +## +# DEPRECATED: Use transferItems() instead. +# +# storageGet(items, amount) +# items: reference to an array of storage item hashes. +# amount: the maximum amount to get, for each item, or 0 for unlimited. +# +# Get one or more items from storage. +# +# Example: +# # Get items $a and $b from storage. +# storageGet([$a, $b]); +# # Get items $a and $b from storage, but at most 30 of each item. +# storageGet([$a, $b], 30); +sub storageGet { + my ( $items, $amount ) = @_; + transferItems( $items, $amount, 'storage' => 'inventory' ); +} + +## +# transferItems(items, amount, source, target) +# items: reference to an array of Actor::Items. +# amount: the maximum amount to get, for each item, or 0 for unlimited. +# source: where the items come from; one of 'inventory', 'storage', 'cart' +# target: where the items should go; one of 'inventory', 'storage', 'cart' +# +# Transfer one or more items from their current location to another location. +# +# Example: +# # Get items $a and $b from storage to inventory. +# transferItems([$a, $b], 'storage' => 'inventory'); +# # Send items $a and $b from cart to storage, but at most 30 of each item. +# transferItems([$a, $b], 30, 'cart' => 'storage'); +sub transferItems { + my ( $items, $amount, $source, $target ) = @_; + + AI::queue( + transferItems => { + timeout => $timeout{ai_transfer_items}{timeout} || 0.15, + items => [ map { { item => $_, source => $source, target => $target, amount => $amount } } @$items ], + } + ); + + # Immediately run the AI sequence once. This will remove it from the queue if there's only one item to transfer. + AI::CoreLogic::processTransferItems(); +} + +## +# headgearName(lookID) +# +# Resolves a lookID of a headgear into a human readable string. +# +# A lookID corresponds to a line number in tables/headgears.txt. +# The number on that line is the itemID for the headgear. +sub headgearName { + my ($lookID) = @_; + + return T("Nothing") if $lookID == 0; + + my $itemID = $headgears_lut[$lookID]; + + if (!$itemID or $itemID =~ /^#/) { + warning TF("Unknown lookID_%d. Need to update the file headgears.txt (from data.grf)\n", $lookID); + return T("Unknown lookID_") . $lookID; + } + + warning TF("Unknown item (ID=%d). Need to update the file items.txt or headgears.txt (from data.grf)\n", $itemID) unless $items_lut{$itemID}; + return main::itemName({nameID => $itemID}); +} + +## +# void initUserSeed() +# +# Generate a unique seed for the current user and save it to +# a file, or load the seed from that file if it exists. +=pod +sub initUserSeed { + my $seedFile = "$Settings::logs_folder/seed.txt"; + my $f; + + if (-f $seedFile) { + if (open($f, "<", $seedFile)) { + binmode $f; + $userSeed = <$f>; + $userSeed =~ s/\n.*//s; + close($f); + } else { + $userSeed = '0'; + } + } else { + $userSeed = ''; + for (0..10) { + $userSeed .= rand(2 ** 49); + } + + if (open($f, ">", $seedFile)) { + binmode $f; + print $f $userSeed; + close($f); + } + } +} +=cut + +sub itemLog_clear { + if (-f $Settings::item_log_file) { unlink($Settings::item_log_file); } +} + +## +# look(bodydir, [headdir]) +# bodydir: a number 0-7. See directions.txt. +# headdir: 0 = look directly, 1 = look right, 2 = look left +# +# Look in the given directions. +sub look { + my %args = ( + look_body => shift, + look_head => shift + ); + AI::queue("look", \%args); +} + +## +# lookAtPosition(pos, [headdir]) +# pos: a reference to a coordinate hash. +# headdir: 0 = face directly, 1 = look right, 2 = look left +# +# Turn face and body direction to position %pos. +sub lookAtPosition { + my $pos2 = shift; + my $headdir = shift; + my %vec; + my $direction; + + getVector(\%vec, $pos2, $char->{pos_to}); + $direction = int(sprintf("%.0f", (360 - vectorToDegree(\%vec)) / 45)) % 8; + look($direction, $headdir); +} + +## +# getNaturalLookDirections(from_pos, to_pos, [current_body]) +# from_pos: source position hashref (character) +# to_pos: target position hashref +# current_body: optional current body direction, defaults to $char->{look}{body} +# +# Calculates look change using partial-turn strategy. +# Returns: (body, head) where body is 0-7 and head is 0-2. +sub getNaturalLookDirections { + my ($from_pos, $to_pos, $current_body) = @_; + return unless ($from_pos && $to_pos); + + $current_body = $char->{look}{body} unless defined $current_body; + + my %vec; + getVector(\%vec, $to_pos, $from_pos); + my $target_body = int(sprintf("%.0f", (360 - vectorToDegree(\%vec)) / 45)) % 8; + + my $body = $current_body; + my $head = 0; + my $offset = ($target_body - $body + 8) % 8; + $offset -= 8 if ($offset > 4); + + if ($offset != 0) { + if (abs($offset) <= 1) { + $head = $offset > 0 ? 2 : 1; + } else { + my $step = $offset > 0 ? 1 : -1; + $body = ($target_body - $step + 8) % 8; + $head = $step > 0 ? 2 : 1; + } + } + + return ($body, $head); +} + +## +# lookAtPositionNaturally(from_pos, to_pos, [current_body]) +# from_pos: source position hashref (character) +# to_pos: target position hashref +# current_body: optional current body direction, defaults to $char->{look}{body} +# +# Calculates and executes look change using partial-turn strategy. +# Returns: (body, head) where body is 0-7 and head is 0-2. +sub lookAtPositionNaturally { + my ($from_pos, $to_pos, $current_body) = @_; + my ($body, $head) = getNaturalLookDirections($from_pos, $to_pos, $current_body); + return unless defined $body; + + look($body, $head) if ($body != $char->{look}{body} || $head != $char->{look}{head}); + return ($body, $head); +} + +## +# getClosestWalls(from_pos, wall_range, [field_obj]) +# from_pos: source position hashref +# wall_range: search range around from_pos +# field_obj: optional field object, defaults to current global field +# +# Returns: arrayref with all nearest non-walkable cells found in range. +sub getClosestWalls { + my ($from_pos, $wall_range, $field_obj) = @_; + return [] unless ($from_pos && defined $wall_range && $wall_range > 0); + + $field_obj ||= $field; + return [] unless $field_obj; + + my $closest_distance; + my @closest_walls; + for (my $dx = -$wall_range; $dx <= $wall_range; $dx++) { + for (my $dy = -$wall_range; $dy <= $wall_range; $dy++) { + next if $dx == 0 && $dy == 0; + + my $candidate = { + x => $from_pos->{x} + $dx, + y => $from_pos->{y} + $dy, + }; + next if $field_obj->isWalkable($candidate->{x}, $candidate->{y}); + + my $distance = distance($candidate, $from_pos); + if (!defined $closest_distance || $distance < $closest_distance) { + $closest_distance = $distance; + @closest_walls = ($candidate); + } elsif ($distance == $closest_distance) { + push @closest_walls, $candidate; + } + } + } + + return \@closest_walls; +} + +## +# manualMove(dx, dy) +# +# Moves the character offset from its current position. +sub manualMove { + my ($dx, $dy) = @_; + + # Stop following if necessary + if ($config{'follow'}) { + configModify('follow', 0); + AI::clear('follow'); + } + + # Stop moving if necessary + AI::clear(qw/move route mapRoute/); + main::ai_route($field->baseName, $char->{pos_to}{x} + $dx, $char->{pos_to}{y} + $dy); +} + +## +# meetingPosition(actor, actorType, target_actor, attackMaxDistance, runFromTargetActive) +# actor: current object. +# actorType: 1 - char | 2 - slave +# target_actor: actor to meet. +# attackMaxDistance: attack distance based on attack method. +# runFromTargetActive: Wheter meetingPosition was called by a runFromTarget check, if 2 then use runFromTarget_noAttackMethodFallback values +# +# Returns: the position where the character should go to meet a moving monster. +sub meetingPosition { + my ($actor, $actorType, $target, $attackMaxDistance, $runFromTargetActive) = @_; + + if ($attackMaxDistance < 1) { + error "attackMaxDistance must be positive ($attackMaxDistance).\n"; + return; + } + + my $extra_time_actor = $timeout{'meetingPosition_extra_time_actor'}{'timeout'} ? $timeout{'meetingPosition_extra_time_actor'}{'timeout'} : 0.2; + my $extra_time_target = $timeout{'meetingPosition_extra_time_target'}{'timeout'} ? $timeout{'meetingPosition_extra_time_target'}{'timeout'} : 0.2; + + my $mySpeed = ($actor->{walk_speed} || 0.12); + my $timeSinceActorMoved = time - $actor->{time_move} + $extra_time_actor; + + my $my_solution; + my $timeActorFinishMove; + + my $attackRouteMaxPathDistance; + my $attackCanSnipe; + my $followDistanceMax; + my $master; + my $masterPos; + my $runFromTarget; + my $runFromTarget_dist; + my $runFromTarget_minStep; + my $runFromTarget_maxPathDistance; + + # actor is char + if ($actorType == 1) { + $attackRouteMaxPathDistance = $config{attackRouteMaxPathDistance} || 13; + $runFromTarget_maxPathDistance = $config{runFromTarget_maxPathDistance} || 13; + $runFromTarget = $config{runFromTarget}; + $runFromTarget_dist = $config{runFromTarget_dist}; + if ($runFromTargetActive == 2) { + $runFromTarget_minStep = $config{runFromTarget_noAttackMethodFallback_minStep}; + } else { + $runFromTarget_minStep = $config{runFromTarget_minStep}; + } + $followDistanceMax = $config{followDistanceMax}; + $attackCanSnipe = $config{attackCanSnipe}; + if ($config{follow}) { + foreach (keys %players) { + if ($players{$_}{name} eq $config{followTarget}) { + $master = $players{$_}; + last; + } + } + if ($master) { + $masterPos = 1; + } + } + + # If the actor is the character then we should have already saved the time calc and solution at Receive.pm::character_moves + $my_solution = $char->{solution}; + $timeActorFinishMove = $char->{time_move_calc}; + + # actor is a slave + } elsif ($actorType == 2) { + $attackRouteMaxPathDistance = $config{$actor->{configPrefix}.'attackRouteMaxPathDistance'} || 20; + $runFromTarget_maxPathDistance = $config{$actor->{configPrefix}.'runFromTarget_maxPathDistance'} || 20; + $runFromTarget = $config{$actor->{configPrefix}.'runFromTarget'}; + $runFromTarget_dist = $config{$actor->{configPrefix}.'runFromTarget_dist'}; + if ($runFromTargetActive == 2) { + $runFromTarget_minStep = $config{$actor->{configPrefix}.'runFromTarget_noAttackMethodFallback_minStep'}; + } else { + $runFromTarget_minStep = $config{$actor->{configPrefix}.'runFromTarget_minStep'}; + } + $followDistanceMax = $config{$actor->{configPrefix}.'followDistanceMax'}; + $attackCanSnipe = $config{$actor->{configPrefix}.'attackCanSnipe'}; + $master = $char; + $masterPos = 1; + + $my_solution = get_solution($field, $actor->{pos}, $actor->{pos_to}); + $timeActorFinishMove = calcTimeFromSolution($my_solution, $mySpeed); + } + + my $realMyPos; + # Actor has finished moving and is at PosTo + if ($timeSinceActorMoved >= $timeActorFinishMove) { + $realMyPos = $actor->{pos_to}; + + # Actor is currently moving + } else { + my $steps_walked = calcStepsWalkedFromTimeAndSolution($my_solution, $mySpeed, $timeSinceActorMoved); + $realMyPos = $my_solution->[$steps_walked]; + } + + # Should never happen + unless ($field->isWalkable($realMyPos->{x}, $realMyPos->{y})) { + $realMyPos = $field->closestWalkableSpot($realMyPos, 1); + } + + my $targetSpeed = ($target->{walk_speed} || 0.12); + my $timeSinceTargetMoved = time - $target->{time_move} + $extra_time_target; + + my $target_solution = get_solution($field, $target->{pos}, $target->{pos_to}); + + # Calculate the time target will need to finish moving from pos to pos_to + my $timeTargetFinishMove = calcTimeFromSolution($target_solution, $targetSpeed); + + my $realTargetPos; + my $targetTotalSteps; + my $targetCurrentStep; + + my @target_pos_to_check; + + # Target has finished moving + if ($timeSinceTargetMoved >= $timeTargetFinishMove) { + $realTargetPos = $target->{pos_to}; + $target_pos_to_check[0] = { + targetPosInStep => $realTargetPos + }; + + # Target is currently moving + } else { + $targetTotalSteps = $#{$target_solution}; + $targetCurrentStep = calcStepsWalkedFromTimeAndSolution($target_solution, $targetSpeed, $timeSinceTargetMoved); + $realTargetPos = $target_solution->[$targetCurrentStep]; + + my $steps_count = 0; + foreach my $currentStep ($targetCurrentStep..$targetTotalSteps) { + $target_pos_to_check[$steps_count] = { + targetPosInStep => $target_solution->[$currentStep] + }; + } continue { + $steps_count++; + } + } + + my $master_moving; + my $master_solution; + my $timeSinceMasterMoved; + my $realMasterPos; + my $masterSpeed; + if ($masterPos) { + $masterSpeed = ($master->{walk_speed} || 0.12); + $timeSinceMasterMoved = time - $master->{time_move} + $extra_time_actor; + + $master_solution = get_solution($field, $master->{pos}, $master->{pos_to}); + + # Calculate the time master will need to finish moving from pos to pos_to + my $timeMasterFinishMove = calcTimeFromSolution($master_solution, $masterSpeed); + + # master has finished moving + if ($timeSinceMasterMoved >= $timeMasterFinishMove) { + $master_moving = 0; + $realMasterPos = $master->{pos_to}; + + # master is currently moving + } else { + $master_moving = 1; + } + } + + my $min_destination_dist = 1; + if ($runFromTarget) { + $min_destination_dist = $runFromTarget_minStep; + } + + my $max_path_dist; + if ($runFromTargetActive) { + $max_path_dist = $runFromTarget_maxPathDistance; + } else { + $max_path_dist = $attackRouteMaxPathDistance; + } + # Add 1 here to account for pos from solution so we don't have to do it multiple times later + $max_path_dist += 1; + + my %allspots; + my @blocks = calcRectArea2($realMyPos->{x}, $realMyPos->{y}, $max_path_dist, 0); + foreach my $spot (@blocks) { + $allspots{$spot->{x}}{$spot->{y}} = 1; + } + + my %prohibitedSpots; + foreach my $prohibited_actor (@$playersList, @$monstersList, @$npcsList, @$petsList, @$slavesList, @$elementalsList) { + $prohibitedSpots{$prohibited_actor->{pos_to}{x}}{$prohibited_actor->{pos_to}{y}} = 1; + } + + my $best_spot; + my $best_targetPosInStep; + my $best_dist_to_target; + my $best_time; + + foreach my $x_spot (sort keys %allspots) { + foreach my $y_spot (sort keys %{$allspots{$x_spot}}) { + my $spot; + $spot->{x} = $x_spot; + $spot->{y} = $y_spot; + + next unless ($spot->{x} != $realMyPos->{x} || $spot->{y} != $realMyPos->{y}); + + # Is this spot acceptable? + + # 1. It must be walkable. + next unless ($field->isWalkable($spot->{x}, $spot->{y})); + + # 1.2 It must not be occupied + next if (exists $prohibitedSpots{$spot->{x}} && exists $prohibitedSpots{$spot->{x}}{$spot->{y}}); + + # 2. It must not be close to a portal. + next if (positionNearPortal($spot, $config{'attackMinPortalDistance'})); + + my $time_actor_to_get_to_spot; + + my $solution = get_solution($field, $realMyPos, $spot); + + # 3. It must be reachable. + next if (scalar @{$solution} == 0); + + # 4. It must have at max $max_path_dist of route distance to it from our current position. + next if (scalar @{$solution} > $max_path_dist); + + $time_actor_to_get_to_spot = calcTimeFromSolution($solution, $mySpeed); + + + my $total_time = ($timeSinceTargetMoved+$time_actor_to_get_to_spot); + my $temp_targetCurrentStep = calcStepsWalkedFromTimeAndSolution($target_solution, $targetSpeed, $total_time); + # Position target would be at if it doesn't change route (and is not following us) + my $targetPosInStep = $target_solution->[$temp_targetCurrentStep]; + + # 5. It must not be the same position the target will be in + next unless ($spot->{x} != $targetPosInStep->{x} || $spot->{y} != $targetPosInStep->{y}); + + # 6. We must be able to attack the target from this spot + next unless (canAttack($field, $spot, $targetPosInStep, $attackCanSnipe, $attackMaxDistance, $config{clientSight}) == 1); + + # 7. It must not be too close to the target if we have runfromtarget set + # TODO: Maybe we should assume the target will keep following us after it reaches its destination and take that into consideration when runfromtarget is set + my $dist_to_target = blockDistance($spot, $targetPosInStep); + next unless ($dist_to_target >= $min_destination_dist); + + # 8. It must be within $followDistanceMax of MasterPos, if we have a master. + if ($realMasterPos) { + my $masterPosNow; + if ($master_moving) { + my $totalTime = $timeSinceMasterMoved + $time_actor_to_get_to_spot; + my $master_CurrentStep = calcStepsWalkedFromTimeAndSolution($master_solution, $masterSpeed, $totalTime); + $masterPosNow = $master_solution->[$master_CurrentStep]; + } else { + $masterPosNow = $realMasterPos; + } + next unless ($spot->{x} != $masterPosNow->{x} || $spot->{y} != $masterPosNow->{y}); + next unless (blockDistance($spot, $masterPosNow) <= $followDistanceMax); + next unless (blockDistance($targetPosInStep, $masterPosNow) <= $followDistanceMax); + } + + # 8. We must be able to get to the spot before our target + # TODO: Fix me. The target does not need to get to the spot, but to at least 2 cells away to be able to attack us, so take that into account + if ($runFromTargetActive) { + my $time_target_to_get_to_spot = calcTimeFromPathfinding($field, $realTargetPos, $spot, $targetSpeed); + if ($time_actor_to_get_to_spot > $time_target_to_get_to_spot) { + next; + } + } + + # We then choose the spot which takes the least amount of time to reach + # TODO: Maybe this is not the best idea when runfromtarget is set + if (!defined($best_time) || $time_actor_to_get_to_spot < $best_time) { + $best_time = $time_actor_to_get_to_spot; + $best_spot = $spot; + $best_targetPosInStep = $targetPosInStep; + $best_dist_to_target = $dist_to_target; + } + } + } + + if (defined $best_spot) { + debug "[meetingPosition] Best spot is $best_spot->{x} $best_spot->{y}, mob will be at $best_targetPosInStep->{x} $best_targetPosInStep->{y}, dist $best_dist_to_target, it will take $best_time seconds to get there.\n"; + return $best_spot; + } +} + +sub objectAdded { + my ($type, $ID, $obj) = @_; + + if ($type eq 'player' || $type eq 'slave') { + # Try to retrieve the player name from cache. + if (!getPlayerNameFromCache($obj) && !$obj->{clone}) { + push @unknownPlayers, $ID; + } + + } elsif ($type eq 'npc') { + push @unknownNPCs, $ID; + } + + if ($type eq 'monster') { + if (mon_control($obj->{name},$obj->{nameID})->{teleport_search}) { + $ai_v{temp}{searchMonsters}++; + } + } + + Plugins::callHook('objectAdded', { + type => $type, + ID => $ID, + obj => $obj + }); +} + +sub objectRemoved { + my ($type, $ID, $obj) = @_; + + if ($type eq 'monster') { + # FIXME: what if mon_control was changed since the counter was increased? + if (mon_control($obj->{name},$obj->{nameID})->{teleport_search}) { + $ai_v{temp}{searchMonsters}--; + } + } + + Plugins::callHook('objectRemoved', { + type => $type, + ID => $ID + }); +} + +## +# items_control($name, $nameID) +# +# Returns the items_control.txt settings for item name $name. +# If $name has no specific settings, try using the setting for $nameID. +# If both have no specific setting, use 'all'. +# If 'all' is not set, return "do nothing" (empty hash); +sub items_control { + my ($name, $nameID) = @_; + + return $items_control{lc($name)} || $items_control{$nameID} || $items_control{all} || {}; +} + +## +# mon_control($name, $nameID) +# +# Returns the mon_control.txt settings for monster name $name. +# If $name has no specific settings, try using the setting for $nameID. +# If both have no specific setting, use 'all'. +# If 'all' is not set, return "attack"; +sub mon_control { + my ($name, $nameID) = @_; + + return $mon_control{lc($name)} || $mon_control{$nameID} || $mon_control{all} || { attack_auto => 1 }; +} + +## +# pickupitems($name, $nameID) +# +# Returns the pickupitems.txt settings for item name $name. +# If $name has no specific settings, try using the setting for $nameID. +# If both have no specific setting, use 'all'. +# If 'all' is not set, return "pick up" (1); +sub pickupitems { + my ($name, $nameID) = @_; + + if (exists $pickupitems{lc($name)}) { + return $pickupitems{lc($name)}; + } elsif (exists $pickupitems{$nameID}) { + return $pickupitems{$nameID}; + } elsif (exists $pickupitems{all}) { + return $pickupitems{all}; + } + + return 1; +} + +sub positionNearPlayer { + my $r_hash = shift; + my $dist = shift; + + for my $player (@$playersList) { + my $ID = $player->{ID}; + next if ($char->{party}{joined} && $char->{party}{users}{$ID}); + next if (defined($player->{name}) && existsInList($config{tankersList}, $player->{name})); + return 1 if (blockDistance($r_hash, $player->{pos_to}) <= $dist); + } + return 0; +} + +sub positionNearPortal { + my $r_hash = shift; + my $dist = shift; + + for my $portal (@$portalsList) { + return 1 if (blockDistance($r_hash, $portal->{pos}) <= $dist); + } + return 0; +} + +## +# printItemDesc(obj $item) +# +# Print the description for $item. +sub printItemDesc { + my $item = shift; + + my $description = $itemsDesc_lut{$item->{nameID}} || T("Error: No description available.\n"); + my $msg = center(T(" Item Description "), 79, '-') ."\n"; + $msg .= TF("Item: %s, ID: %s, Amount: %s\n\n", $item->{name}, $item->{nameID}, $item->{amount}), "info"; + my @options = grep { $_->{type} } map { my @c = unpack 'vvC', $_;{ type => $c[0], value => $c[1], param => $c[2] } } unpack '(a5)*', $item->{options} || ''; + my $option_index = 1; + foreach ( @options ) { + if ( $itemOptionHandle{$_->{type}} && $itemOption_lut{$itemOptionHandle{$_->{type}}} ) { + $msg .= TF("OPTION %s: ", $option_index) . sprintf($itemOption_lut{$itemOptionHandle{$_->{type}}}, $_->{value}) . "\n"; + } else { + $msg .= TF("OPTION %s: Option (%d, %d, %d)\n", $option_index, $_->{type}, $_->{value},$_->{param}); + } + $option_index++; + } + $msg .= $description; + $msg .= ('-'x79) . "\n"; + message $msg, "info"; +} + +sub processNameRequestQueue { + my ($queue, $actorLists, $foo) = @_; + + while (@{$queue}) { + my $ID = $queue->[0]; + + my $actor; + foreach my $actorList (@$actorLists) { + last if $actor = $actorList->getByID($ID); + } + + # Some private servers ban you if you request info for an object with + # GM Perfect Hide status + if (!$actor || defined($actor->{info}) || $actor->statusActive('EFFECTSTATE_SPECIALHIDING')) { + shift @{$queue}; + next; + } + + # Remove actors with a distance greater than clientSight. Some private servers (notably Freya) use + # a technique where they send actor_exists packets with ridiculous distances in order to automatically + # ban bots. By removingthose actors, we eliminate that possibility and emulate the client more closely. + if (defined $actor->{pos_to} && (my $block_dist = blockDistance($char->{pos_to}, $actor->{pos_to})) >= ($config{clientSight} || 16)) { + debug "[NameRequestQueue] Removed from list actor at $actor->{pos_to}{x} $actor->{pos_to}{y} (distance: $block_dist)\n"; + shift @{$queue}; + next; + } + + if (defined $actor && $actor->{avoid}) { + debug TF("[NameRequestQueue] Removed from list actor %s (flag avoid).\n", $actor); + shift @{$queue}; + next; + } + + $messageSender->sendGetPlayerInfo($ID) if (isSafeActorQuery($ID) == 1); # Do not Query GM's + $actor = shift @{$queue}; + push @{$queue}, $actor if ($actor); + last; + } +} + +sub quit { + $quit = 1; + message T("Exiting...\n"), "system"; +} + +sub offlineMode { + $net->setState(Network::NOT_CONNECTED) if ($net); + undef $conState_tries; + $net->serverDisconnect() if ($net); + $Settings::no_connect = 1; + message TF("Openkore will stay disconnected. Type \"connect\" in order to connect again.\n"), "connection"; +} + +sub relog { + my $timeout = (shift || 5); + my $silent = shift; + $net->setState(1) if ($net); + undef $conState_tries; + $timeout_ex{'master'}{'time'} = time; + $timeout_ex{'master'}{'timeout'} = $timeout; + $net->serverDisconnect() if ($net); + message TF("Relogging in %d seconds...\n", $timeout), "connection" unless $silent; +} + +## +# sendMessage(String type, String msg, String user) +# type: Specifies what kind of message this is. "c" for public chat, "g" for guild chat, +# "p" for party chat, "pm" for private message, "k" for messages that only the RO +# client will see (in X-Kore mode.) +# msg: The message to send. +# user: +# +# Send a chat message to a user. +sub sendMessage { + my ($sender, $type, $msg, $user) = @_; + my ($j, @msgs, $oldmsg, $amount, $space); + my $msgMaxLen = $config{'message_length_max'} || 80; + + @msgs = split /\\n/, $msg; + for ($j = 0; $j < @msgs; $j++) { + my (@msg, $i); + + @msg = split / /, $msgs[$j]; + undef $msg; + for ($i = 0; $i < @msg; $i++) { + if (!length($msg[$i])) { + $msg[$i] = " "; + $space = 1; + } + if (length($msg[$i]) > $msgMaxLen) { + while (length($msg[$i]) >= $msgMaxLen) { + $oldmsg = $msg; + if (length($msg)) { + $amount = $msgMaxLen; + if ($amount - length($msg) > 0) { + $amount = $msgMaxLen - 1; + $msg .= " " . substr($msg[$i], 0, $amount - length($msg)); + } + } else { + $amount = $msgMaxLen; + $msg .= substr($msg[$i], 0, $amount); + } + sendMessage_send($sender, $type, $msg, $user); + $msg[$i] = substr($msg[$i], $amount - length($oldmsg), length($msg[$i]) - $amount - length($oldmsg)); + undef $msg; + } + } + if (length($msg[$i]) && length($msg) + length($msg[$i]) <= $msgMaxLen) { + if (length($msg)) { + if (!$space) { + $msg .= " " . $msg[$i]; + } else { + $space = 0; + $msg .= $msg[$i]; + } + } else { + $msg .= $msg[$i]; + } + } else { + sendMessage_send($sender, $type, $msg, $user); + $msg = $msg[$i]; + } + if (length($msg) && $i == @msg - 1) { + sendMessage_send($sender, $type, $msg, $user); + } + } + } +} + +sub sendMessage_send { + my ($sender, $type, $msg, $user) = @_; + + if ($type eq "c") { + $sender->sendChat($msg); + } elsif ($type eq "g") { + $sender->sendGuildChat($msg); + } elsif ($type eq "p") { + $sender->sendPartyChat($msg); + } elsif ($type eq "bg") { + $sender->sendBattlegroundChat($msg); + } elsif ($type eq "pm") { + %lastpm = ( + msg => $msg, + user => $user + ); + push @lastpm, {%lastpm} if ($user !~ '#\w+'); + $sender->sendPrivateMsg($user, $msg); + } elsif ($type eq "k") { + $sender->injectMessage($msg); + } elsif ($type eq "cln") { + $sender->sendClanChat($msg); + } +} + +# Keep track of when we last cast a skill +sub setSkillUseTimer { + my ($skillID, $targetID, $wait) = @_; + my $skill = new Skill(idn => $skillID); + my $handle = $skill->getHandle(); + + $char->{skills}{$handle}{time_used} = time; + delete $char->{time_cast}; + delete $char->{cast_cancelled}; + $char->{last_skill_time} = time; + $char->{last_skill_used} = $skillID; + $char->{last_skill_target} = $targetID; + + # increment monsterSkill maxUses counter + if (defined $targetID) { + my $actor = Actor::get($targetID); + $actor->{skillUses}{$skill->getHandle()}++; + } + + # Set encore skill if applicable + $char->{encoreSkill} = $skill if $targetID eq $accountID && $skillsEncore{$skill->getHandle()}; +} + +sub setPartySkillTimer { + my ($skillID, $targetID) = @_; + my $skill = new Skill(idn => $skillID); + my $handle = $skill->getHandle(); + + # set partySkill target_time + my $i = $targetTimeout{$targetID}{$handle}; + $ai_v{"partySkill_${i}_time"} = time if $i ne ""; + $ai_v{"partySkill_${i}_target_time"}{$targetID} = time if $i ne ""; +} + +## +# boolean isCellOccupied(pos) +# pos: position hash. +# +# Returns 1 if there is a player, npc or mob in the cell, otherwise return 0. +# TODO: Check if a character can move to a cell with a pet, elemental, homunculus or mercenary +sub isCellOccupied { + my ($pos) = @_; + foreach my $actor (@$playersList, @$monstersList, @$npcsList, @$petsList, @$slavesList, @$elementalsList) { + return 1 if ($actor->{pos_to}{x} == $pos->{x} && $actor->{pos_to}{y} == $pos->{y}); + } + return 0; +} + +## +# boolean setStatus(Actor actor, opt1, opt2, option) +# opt1: the state information of the actor. +# opt2: the ailment information of the actor. +# option: the "look" information of the actor. +# Returns: Whether the actor should be removed from the actor list. +# +# Sets the state, ailment, and "look" statuses of the actor. +# Does not include skillsstatus.txt items. +# TODO: move to Actor? +sub setStatus { + my ($actor, $opt1, $opt2, $option) = @_; + assert(defined $actor, "Can't set status of undef actor") if DEBUG; + assertClass($actor, 'Actor') if DEBUG; + my $verbosity = $actor->{ID} eq $accountID ? 1 : 2; + my $changed = 0; + + my $match_id = sub {return ($_[0] == $_[1])}; + my $match_bitflag = sub {return (($_[0] & $_[1]) == $_[1])}; + + # TODO: we could possibly make the search faster (binary search?) + for ( + [$opt1, \%stateHandle, $match_id, 'state'], + [$opt2, \%ailmentHandle, $match_bitflag, 'ailment'], + [$option, \%lookHandle, $match_bitflag, 'look'], + ) { + my ($option, $handle, $match, $name) = @$_; + #next unless $option; # skip option 0 (no state, ailment, look has such id or bitflag) (we can't have this, the state resets its statuses using this) + for (keys %$handle) { + if (&$match($option, $_)) { + unless ($actor->{statuses}{$handle->{$_}}) { + $actor->{statuses}{$handle->{$_}}{time} = time; + $actor->{statuses}{$handle->{$_}}{tick} = 0; + message status_string($actor, $name . ': ' . ($statusName{$handle->{$_}} || $handle->{$_}), 'now'), "parseMsg_status$name", $verbosity; + $changed = 1; + } + #last; # stop this for loop if found (we cannot do this because of bit flag match must loop all) + } elsif ($actor->{statuses}{$handle->{$_}}) { + delete $actor->{statuses}{$handle->{$_}}; + message status_string($actor, $name . ': ' . ($statusName{$handle->{$_}} || $handle->{$_}), 'no longer'), "parseMsg_status$name", $verbosity; + $changed = 1; + #last; # stop this for loop if found (we cannot do this because of bit flag match must loop all) + } + } + } +=pod + foreach (keys %stateHandle) { + if ($opt1 == $_) { + if (!$actor->{statuses}{$stateHandle{$_}}) { + $actor->{statuses}{$stateHandle{$_}} = 1; + message TF("%s %s in %s state.\n", $actor, $actor->verb('are', 'is'), $statusName{$stateHandle{$_}} || $stateHandle{$_}), "parseMsg_statuslook", $verbosity; + $changed = 1; + } + } elsif ($actor->{statuses}{$stateHandle{$_}}) { + delete $actor->{statuses}{$stateHandle{$_}}; + message TF("%s %s out of %s state.\n", $actor, $actor->verb('are', 'is'), $statusName{$stateHandle{$_}} || $stateHandle{$_}), "parseMsg_statuslook", $verbosity; + $changed = 1; + } + } + + foreach (keys %ailmentHandle) { + if (($opt2 & $_) == $_) { + if (!$actor->{statuses}{$ailmentHandle{$_}}) { + $actor->{statuses}{$ailmentHandle{$_}} = 1; + if ($actor->isa('Actor::You')) { + message TF("%s have ailment: %s.\n", $actor->nameString(), $statusName{$ailmentHandle{$_}} || $ailmentHandle{$_}), "parseMsg_statuslook", $verbosity; + } else { + message TF("%s has ailment: %s.\n", $actor->nameString(), $statusName{$ailmentHandle{$_}} || $ailmentHandle{$_}), "parseMsg_statuslook", $verbosity; + } + $changed = 1; + } + } elsif ($actor->{statuses}{$ailmentHandle{$_}}) { + delete $actor->{statuses}{$ailmentHandle{$_}}; + message TF("%s %s out of %s ailment.\n", $actor, $actor->verb('are', 'is'), $statusName{$ailmentHandle{$_}} || $ailmentHandle{$_}), "parseMsg_statuslook", $verbosity; + $changed = 1; + } + } + + foreach (keys %lookHandle) { + if (($option & $_) == $_) { + if (!$actor->{statuses}{$lookHandle{$_}}) { + $actor->{statuses}{$lookHandle{$_}} = 1; + if ($actor->isa('Actor::You')) { + message TF("%s have look: %s.\n", $actor->nameString, $statusName{$lookHandle{$_}} || $lookHandle{$_}), "parseMsg_statuslook", $verbosity; + } else { + message TF("%s has look: %s.\n", $actor->nameString, $statusName{$lookHandle{$_}} || $lookHandle{$_}), "parseMsg_statuslook", $verbosity; + } + $changed = 1; + } + } elsif ($actor->{statuses}{$lookHandle{$_}}) { + delete $actor->{statuses}{$lookHandle{$_}}; + message TF("%s %s out of %s look.\n", $actor, $actor->verb('are', 'is'), $statusName{$lookHandle{$_}} || $lookHandle{$_}), "parseMsg_statuslook", $verbosity; + $changed = 1; + } + } +=cut + Plugins::callHook('changed_status',{ + actor => $actor, + changed => $changed + }); + + # Remove perfectly hidden objects + if ($actor->statusActive('EFFECTSTATE_SPECIALHIDING')) { + if (UNIVERSAL::isa($actor, "Actor::Player")) { + message TF("Found perfectly hidden %s\n", $actor->nameString()); + # message TF("Remove perfectly hidden %s\n", $actor->nameString()); + # $playersList->remove($actor); + # Call the hook when a perfectly hidden player is detected + # Plugins::callHook('perfect_hidden_player',undef); + Plugins::callHook('perfect_hidden_player', { + actor => $actor, + changed => $changed + }); + + } elsif (UNIVERSAL::isa($actor, "Actor::Monster")) { + message TF("Found perfectly hidden %s\n", $actor->nameString()); + # message TF("Remove perfectly hidden %s\n", $actor->nameString()); + # $monstersList->remove($actor); + + # NPCs do this on purpose (who knows why) + } elsif (UNIVERSAL::isa($actor, "Actor::NPC")) { + message TF("Found perfectly hidden %s\n", $actor->nameString()); + # message TF("Remove perfectly hidden %s\n", $actor->nameString()); + # $npcsList->remove($actor); + Plugins::callHook('perfect_hidden_npc', { + actor => $actor, + changed => $changed + }); + + } elsif (UNIVERSAL::isa($actor, "Actor::Pet")) { + message TF("Found perfectly hidden %s\n", $actor->nameString()); + # message TF("Remove perfectly hidden %s\n", $actor->nameString()); + # $petsList->remove($actor); + } + return 1; + } else { + return 0; + } +} + + +# Increment counter for monster being casted on +sub countCastOn { + my ($sourceID, $targetID, $skillID, $x, $y) = @_; + return unless defined $targetID; + + my $source = Actor::get($sourceID); + my $target = Actor::get($targetID); + assertClass($source, 'Actor') if DEBUG; + assertClass($target, 'Actor') if DEBUG; + + if ($targetID eq $accountID) { + $source->{castOnToYou}++; + } elsif ($target->isa('Actor::Player') || $target->isa('Actor::Slave')) { + $source->{castOnToPlayer}{$targetID}++; + } elsif ($target->isa('Actor::Monster')) { + $source->{castOnToMonster}{$targetID}++; + } + + if ($sourceID eq $accountID) { + $target->{castOnByYou}++; + } elsif ($source->isa('Actor::Player') || $source->isa('Actor::Slave')) { + $target->{castOnByPlayer}{$sourceID}++; + } elsif ($source->isa('Actor::Monster')) { + $target->{castOnByMonster}{$sourceID}++; + } +} + +## +# boolean stripLanguageCode(String* msg) +# msg: a chat message, as sent by the RO server. +# Returns: whether the language code was stripped. +# +# Strip the language code character from a chat message. +sub stripLanguageCode { + my $r_msg = shift; + if ($masterServer->{chatLangCode}) { + if ($$r_msg =~ /^\|..(.*)/) { + $$r_msg = $1; + return 1; + } elsif ($$r_msg =~ /^(#main : \[.*\] )\|..(.*)/) { + $$r_msg = $1.$2; + return 1; + } + return 0; + } else { + return 0; + } +} + +## +# void switchConf(String filename) +# filename: a configuration file. +# Returns: 1 on success, 0 if $filename does not exist. +# +# Switch to another configuration file. +sub switchConfigFile { + my $filename = shift; + if (! -f $filename) { + error TF("%s does not exist.\n", $filename); + return 0; + } + + Settings::setConfigFilename($filename); + parseConfigFile($filename, \%config); + return 1; +} + +sub updateDamageTables { + my ($sourceID, $targetID, $damage) = @_; + + # Track deltaHp + # + # A player's "deltaHp" initially starts at 0. + # When he takes damage, the damage is subtracted from his deltaHp. + # When he is healed, this amount is added to the deltaHp. + # If the deltaHp becomes positive, it is reset to 0. + # + # Someone with a lot of negative deltaHp is probably in need of healing. + # This allows us to intelligently heal non-party members. + if (my $target = Actor::get($targetID)) { + $target->{deltaHp} -= $damage; + $target->{deltaHp} = 0 if $target->{deltaHp} > 0; + } + + if ($sourceID eq $accountID) { + if ((my $monster = $monstersList->getByID($targetID))) { + # You attack monster + $monster->{dmgTo} += $damage; + $monster->{dmgFromYou} += $damage; + $monster->{numAtkFromYou}++; + if ($damage <= ($config{missDamage} || 0)) { + $monster->{missedFromYou}++; + debug "Incremented missedFromYou count to $monster->{missedFromYou}\n", "attackMonMiss"; + $monster->{atkMiss}++; + } else { + if ($config{attackUpdateMonsterPos} && ($monster->{pos}{x} != $monster->{pos_to}{x} || $monster->{pos}{y} != $monster->{pos_to}{y})) { + my $new_monster_pos = calcPosFromPathfinding($field, $monster); + $monster->{pos} = $new_monster_pos; + $monster->{pos_to} = $new_monster_pos; + $monster->{time_move} = time; + $monster->{time_move_calc} = 0; + debug "Target monster $monster got hit by us during its movement, updating its position to $monster->{pos}{x} $monster->{pos}{y}.\n"; + } + $monster->{atkMiss} = 0; + } + if ($config{teleportAuto_atkMiss} && $monster->{atkMiss} >= $config{teleportAuto_atkMiss}) { + message T("Teleporting because of attack miss\n"), "teleport"; + ai_useTeleport(1); + } + if ($config{teleportAuto_atkCount} && $monster->{numAtkFromYou} >= $config{teleportAuto_atkCount}) { + message TF("Teleporting after attacking a monster %d times\n", $config{teleportAuto_atkCount}), "teleport"; + ai_useTeleport(1); + } + + if (AI::action eq "attack" && mon_control($monster->{name},$monster->{nameID})->{attack_auto} == 3 && $damage) { + # Mob-training, you only need to attack the monster once to provoke it + message TF("%s (%s) has been provoked, searching another monster\n", $monster->{name}, $monster->{binID}); + $char->sendAttackStop; + $char->dequeue; + } + + + } + + } elsif ((my $monster = $monstersList->getByID($sourceID))) { + if (my $player = ($accountID eq $targetID && $char) || $playersList->getByID($targetID) || $slavesList->getByID($targetID)) { + # Monster attacks player or slave + $monster->{dmgFrom} += $damage; + ($accountID eq $targetID ? $monster->{dmgToYou} : $monster->{dmgToPlayer}{$targetID}) += $damage; + $player->{dmgFromMonster}{$sourceID} += $damage; + if ($damage == 0) { + ($accountID eq $targetID ? $monster->{missedYou} : $monster->{missedToPlayer}{$targetID}) += 1; + $player->{missedFromMonster}{$sourceID}++; + } + $accountID eq $targetID && $monster->{attackedYou}++ unless ( + scalar(keys %{$monster->{dmgFromPlayer}}) || + scalar(keys %{$monster->{dmgToPlayer}}) || + $monster->{missedFromPlayer} || + $monster->{missedToPlayer} + ); + if (existsInList($config{tankersList}, $player->{name}) || + ($char->{slaves} && %{$char->{slaves}} && $char->{slaves}{$targetID} && %{$char->{slaves}{$targetID}}) || + ($char->{party}{joined} && $char->{party}{users}{$targetID} && %{$char->{party}{users}{$targetID}})) { + # Monster attacks party member or our slave + $monster->{dmgToParty} += $damage; + $monster->{missedToParty}++ if ($damage == 0); + } + $monster->{target} = $targetID; + OpenKoreMod::updateDamageTables($monster) if (defined &OpenKoreMod::updateDamageTables); + + if (AI::state == AI::AUTO && ($accountID eq $targetID or $char->{slaves} && $char->{slaves}{$targetID})) { + # object under our control + my $teleport = 0; + if (mon_control($monster->{name},$monster->{nameID})->{teleport_auto} == 2 && $damage){ + message TF("%s hit %s. Teleporting...\n", + $monster, $player), "teleport"; + $teleport = 1; + + } elsif ($config{$player->{configPrefix}.'teleportAuto_deadly'} && $damage >= $player->{hp} + && !$player->statusActive('EFST_ILLUSION')) { + message TF("%s can kill %s with the next %d dmg. Teleporting...\n", + $monster, $player, $damage), "teleport"; + $teleport = 1; + + } elsif ($config{$player->{configPrefix}.'teleportAuto_maxDmg'} && $damage >= $config{$player->{configPrefix}.'teleportAuto_maxDmg'} + && !$player->statusActive('EFST_ILLUSION') + && !($config{$player->{configPrefix}.'teleportAuto_maxDmgInLock'} && $field->baseName eq $config{lockMap})) { + message TF("%s hit %s for more than %d dmg. Teleporting...\n", + $monster, $player, $config{$player->{configPrefix}.'teleportAuto_maxDmg'}), "teleport"; + $teleport = 1; + + } elsif ($config{$player->{configPrefix}.'teleportAuto_maxDmgInLock'} && $field->baseName eq $config{lockMap} + && $damage >= $config{$player->{configPrefix}.'teleportAuto_maxDmgInLock'} + && !$player->statusActive('EFST_ILLUSION')) { + message TF("%s hit %s for more than %d dmg in lockMap. Teleporting...\n", + $monster, $player, $config{$player->{configPrefix}.'teleportAuto_maxDmgInLock'}), "teleport"; + $teleport = 1; + + } elsif (($player->{sitting} || AI::inQueue("sitAuto")) + && $config{$player->{configPrefix}.'teleportAuto_attackedWhenSitting'} + && ($damage || $config{$player->{configPrefix}.'teleportAuto_attackedWhenSitting'} == 2)) { + if ($damage) { + message TF("%s hit %s while you are sitting. Teleporting...\n", + $monster, $player), "teleport"; + } else { + message TF("%s attacked %s while you are sitting. Teleporting...\n", + $monster, $player), "teleport"; + } + $teleport = 1; + + } elsif ($config{$player->{configPrefix}.'teleportAuto_totalDmg'} + && ($accountID eq $targetID ? $monster->{dmgToYou} : $monster->{dmgToPlayer}{$targetID}) >= $config{$player->{configPrefix}.'teleportAuto_totalDmg'} + && !$player->statusActive('EFST_ILLUSION') + && !($config{$player->{configPrefix}.'teleportAuto_totalDmgInLock'} && $field->baseName eq $config{lockMap})) { + message TF("%s hit %s for a total of more than %d dmg. Teleporting...\n", + $monster, $player, $config{$player->{configPrefix}.'teleportAuto_totalDmg'}), "teleport"; + $teleport = 1; + + } elsif ($config{$player->{configPrefix}.'teleportAuto_totalDmgInLock'} && $field->baseName eq $config{lockMap} + && ($accountID eq $targetID ? $monster->{dmgToYou} : $monster->{dmgToPlayer}{$targetID}) >= $config{$player->{configPrefix}.'teleportAuto_totalDmgInLock'} + && !$player->statusActive('EFST_ILLUSION')) { + message TF("%s hit %s for a total of more than %d dmg in lockMap. Teleporting...\n", + $monster, $player, $config{$player->{configPrefix}.'teleportAuto_totalDmgInLock'}), "teleport"; + $teleport = 1; + + } elsif ($config{$player->{configPrefix}.'teleportAuto_hp'} && percent_hp($player) <= $config{$player->{configPrefix}.'teleportAuto_hp'}) { + message TF("%s hit %s when %s HP is under %d. Teleporting...\n", + $monster, $player, $player->verb(T('your'), T('its')), $config{$player->{configPrefix}.'teleportAuto_hp'}), "teleport"; + $teleport = 1; + + } elsif ($accountID eq $targetID && $player->action eq "attack" && mon_control($monster->{name}, $monster->{nameID})->{attack_auto} == 3 + && ($monster->{dmgToYou} || $monster->{missedYou} || $monster->{dmgFromYou})) { + + # Mob-training, stop attacking the monster if it has been attacking you + message TF("%s has been provoked, searching another monster\n", $monster); + $player->sendAttackStop; + $player->dequeue; + } + ai_useTeleport(1) if ($teleport); + } + } + + } elsif ((my $player = $playersList->getByID($sourceID) || $slavesList->getByID($sourceID))) { + if ((my $monster = $monstersList->getByID($targetID))) { + # Player or Slave attacks monster + $monster->{dmgTo} += $damage; + $monster->{dmgFromPlayer}{$sourceID} += $damage; + $monster->{lastAttackFrom} = $sourceID; + $player->{dmgToMonster}{$targetID} += $damage; + + if ($damage == 0) { + $monster->{missedFromPlayer}{$sourceID}++; + $player->{missedToMonster}{$targetID}++; + } + + if (existsInList($config{tankersList}, $player->{name}) || ($char->{slaves} && $char->{slaves}{$sourceID}) || + ($char->{party}{joined} && $char->{party}{users}{$sourceID} && %{$char->{party}{users}{$sourceID}})) { + $monster->{dmgFromParty} += $damage; + + if ($damage == 0) { + $monster->{missedFromParty}++; + } + } + OpenKoreMod::updateDamageTables($monster) if (defined &OpenKoreMod::updateDamageTables); + } + } +} + +## +# updatePlayerNameCache(player) +# player: a player actor object. +sub updatePlayerNameCache { + my ($player) = @_; + + return if (!$config{cachePlayerNames}); + + # First, cleanup the cache. Remove entries that are too old. + # Default life time: 15 minutes + my $changed = 1; + for (my $i = 0; $i < @playerNameCacheIDs; $i++) { + my $ID = $playerNameCacheIDs[$i]; + if (timeOut($playerNameCache{$ID}{time}, $config{cachePlayerNames_duration})) { + delete $playerNameCacheIDs[$i]; + delete $playerNameCache{$ID}; + $changed = 1; + } + } + compactArray(\@playerNameCacheIDs) if ($changed); + + # Resize the cache if it's still too large. + # Default cache size: 100 + while (@playerNameCacheIDs > $config{cachePlayerNames_maxSize}) { + my $ID = shift @playerNameCacheIDs; + delete $playerNameCache{$ID}; + } + + # Add this player name to the cache. + my $ID = $player->{ID}; + if (!$playerNameCache{$ID}) { + push @playerNameCacheIDs, $ID; + my %entry = ( + name => $player->{name}, + guild => $player->{guild}, + time => time, + lv => $player->{lv}, + jobID => $player->{jobID} + ); + $playerNameCache{$ID} = \%entry; + } +} + +sub canUseTeleport { + my ($use_lvl) = @_; + + # not in game + return 0 if $net && $net->getState != Network::IN_GAME; # $net check is to not crash test + + # 1 - check for items + my $item; + if($use_lvl == 1) { + if ($config{teleportAuto_item1}) { + $item = $char->inventory->getByName($config{teleportAuto_item1}); + $item = $char->inventory->getByNameID($config{teleportAuto_item1}) if (!($item) && $config{teleportAuto_item1} =~ /^\d{3,}$/); + } + $item = getFlyWing() unless $item; + } else { + if ($config{teleportAuto_item2}) { + $item = $char->inventory->getByName($config{teleportAuto_item2}); + $item = $char->inventory->getByNameID($config{teleportAuto_item2}) if (!($item) && $config{teleportAuto_item2} =~ /^\d{3,}$/); + } + $item = getButterflyWing() unless $item; + } + + return 1 if $item; + + # Mute prevents talking, usage of skills, and commands. + return 0 if $char->{'muted'}; + + # 2 - check for chat command + return 1 if ($config{teleportAuto_useChatCommand} && $use_lvl == 1); + return 1 if ($config{saveMap_warpChatCommand} && $use_lvl == 2); + + # 3 - check for equipments + return 1 if(Actor::Item::scanConfigAndCheck('teleportAuto_equip')); + + # 4 - check for skill + my $skill_level = ($char->{skills}{AL_TELEPORT}{lv}) ? $char->{skills}{AL_TELEPORT}{lv} : 0; + return 1 if($skill_level >= $use_lvl); + + return 0; +} + +## +# top10Listing(args) +# args: a 282 bytes packet representing 10 names followed by 10 ranks +# +# Returns a formatted list of [# ], Name and points +sub top10Listing { + my ($args) = @_; + + my $msg = $args->{RAW_MSG}; + + my @list; + my @points; + my $i; + my $textList = ""; + for ($i = 0; $i < 10; $i++) { + $list[$i] = unpack("Z24", substr($msg, 2 + (24*$i), 24)); + } + for ($i = 0; $i < 10; $i++) { + $points[$i] = unpack("V1", substr($msg, 242 + ($i*4), 4)); + } + for ($i = 0; $i < 10; $i++) { + $textList .= swrite("[@<] @<<<<<<<<<<<<<<<<<<<<<<<< @>>>>>>>>>>", + [$i+1, $list[$i], $points[$i]]); + } + + return $textList; +} + +## +# whenGroundStatus(target, statuses, mine) +# target: coordinates hash +# statuses: a comma-separated list of ground effects e.g. Safety Wall,Pneuma +# mine: if true, only consider ground effects that originated from me +# +# Returns 1 if $target has one of the ground effects specified by $statuses. +sub whenGroundStatus { + my ($pos, $statuses, $mine) = @_; + + my ($x, $y) = ($pos->{x}, $pos->{y}); + for my $ID (@spellsID) { + my $spell; + next unless $spell = $spells{$ID}; + next if $mine && $spell->{sourceID} ne $accountID; + if ($x == $spell->{pos}{x} && + $y == $spell->{pos}{y}) { + return 1 if existsInList($statuses, getSpellName($spell->{type})); + } + } + return 0; +} + +sub writeStorageLog { + my ($show_error_on_fail) = @_; + my $f; + + if (open($f, ">:utf8", $Settings::storage_log_file)) { + print $f TF("---------- Storage %s -----------\n", getFormattedDate(int(time))); + for my $item (@{$char->storage}) { + + my $display = sprintf "%2d [%6d] %s x %s", $item->{binID}, $item->{nameID}, $item->{name}, $item->{amount}; + # Translation Comment: Mark to show not identified items + $display .= " -- " . T("Not Identified") if !$item->{identified}; + # Translation Comment: Mark to show broken items + $display .= " -- " . T("Broken") if $item->{broken}; + print $f "$display\n"; + } + # Translation Comment: Storage Capacity + print $f TF("\nCapacity: %d/%d\n", $char->storage->items, $char->storage->items_max); + print $f "-------------------------------\n"; + close $f; + + message T("Storage logged\n"), "success"; + + } elsif ($show_error_on_fail) { + error TF("Unable to write to %s\n", $Settings::storage_log_file); + } +} + +## +# getBestTarget(possibleTargets, attackCheckLOS, $attackCanSnipe) +# possibleTargets: reference to an array of monsters' IDs +# attackCheckLOS: if set, non-LOS monsters are checked up +# +# Returns ID of the best target +sub getBestTarget { + my ($possibleTargets, $attackCheckLOS, $attackCanSnipe) = @_; + if (!$possibleTargets) { + return; + } + + my $portalDist = $config{'attackMinPortalDistance'} || 4; + my $playerDist = $config{'attackMinPlayerDistance'} || 1; + + my @noLOSMonsters; + my @noLOSMonsters_pos; + # TODO: Is there any situation where we should use calcPosFromPathfinding or calcPosFromTime here? + my $myPos = calcPosFromPathfinding($field, $char); + my ($highestPri, $smallestDist, $bestTarget); + + # First of all we check monsters in LOS, then the rest of monsters + + foreach (@{$possibleTargets}) { + my $monster = $monsters{$_}; + # TODO: Is there any situation where we should use calcPosFromPathfinding or calcPosFromTime here? + my $pos = calcPosFromPathfinding($field, $monster); + next if (positionNearPlayer($pos, $playerDist) + || positionNearPortal($pos, $portalDist) + ); + my $control = mon_control($monster->{name},$monster->{nameID}); + if (defined $control) { + next if ( ($control->{attack_auto} == -1) + || ($control->{attack_lvl} ne "" && $control->{attack_lvl} > $char->{lv}) + || ($control->{attack_jlvl} ne "" && $control->{attack_jlvl} > $char->{lv_job}) + || ($control->{attack_hp} ne "" && $control->{attack_hp} > $char->{hp}) + || ($control->{attack_sp} ne "" && $control->{attack_sp} > $char->{sp}) + || ($control->{attack_auto} == 3 && ($monster->{dmgToYou} || $monster->{missedYou} || $monster->{dmgFromYou})) + || ($control->{attack_auto} == 0 && !($monster->{dmgToYou} || $monster->{missedYou})) + ); + } + + my %plugin_args; + $plugin_args{target} = $monster; + $plugin_args{control} = $control; + $plugin_args{attackCheckLOS} = $attackCheckLOS; + $plugin_args{attackCanSnipe} = $attackCanSnipe; + $plugin_args{return} = 0; + Plugins::callHook('getBestTarget' => \%plugin_args); + next if ($plugin_args{return}); + + if (!$field->checkLOS($myPos, $pos, $attackCanSnipe)) { + push(@noLOSMonsters, $_); + push(@noLOSMonsters_pos, $pos); + next; + } + + my $name = lc $monster->{name}; + my $dist = adjustedBlockDistance($myPos, $pos); + my $priority = $priority{$name} ? $priority{$name} : 0; + + if (!defined($bestTarget) || ($priority > $highestPri)) { + $highestPri = $priority; + $smallestDist = $dist; + $bestTarget = $_; + } + if ((!defined($bestTarget) || $priority == $highestPri) + && (!defined($smallestDist) || $dist < $smallestDist)) { + $highestPri = $priority; + $smallestDist = $dist; + $bestTarget = $_; + } + } + if ($attackCheckLOS && !$bestTarget && scalar(@noLOSMonsters) > 0) { + my $pathfinding = new PathFinding; + my ($min_pathfinding_x, $min_pathfinding_y, $max_pathfinding_x, $max_pathfinding_y) = $field->getSquareEdgesFromCoord($myPos, $config{attackRouteMaxPathDistance}); + foreach my $index (0..$#noLOSMonsters) { + + # The most optimal solution is to include the path lenghts' comparison, however it will take + # more time and CPU resources, so, we use rough solution with priority and distance comparison + + my $monster = $monsters{$noLOSMonsters[$index]}; + # TODO: Is there any situation where we should use calcPosFromPathfinding or calcPosFromTime here? + my $pos = $noLOSMonsters_pos[$index]; + + # avoid get targets away from attackRouteMaxPathDistance + next if(blockDistance($myPos, $pos) >= $config{attackRouteMaxPathDistance}); + + $pathfinding->reset( + start => $myPos, + dest => $pos, + field => $field, + avoidWalls => 0, + randomFactor => 0, + useManhattan => 0, + min_x => $min_pathfinding_x, + max_x => $max_pathfinding_x, + min_y => $min_pathfinding_y, + max_y => $max_pathfinding_y + ); + my $dist = $pathfinding->runcount; + if ($dist <= 0 || $dist > $config{attackRouteMaxPathDistance}) { + $monster->{attack_failedLOS} = time; + next; + } + + my $name = lc $monster->{name}; + my $priority = $priority{$name} ? $priority{$name} : 0; + if (!defined($bestTarget) || ($priority > $highestPri)) { + $highestPri = $priority; + $smallestDist = $dist; + $bestTarget = $noLOSMonsters[$index]; + } + if ((!defined($bestTarget) || $priority == $highestPri) + && (!defined($smallestDist) || $dist < $smallestDist)) { + $highestPri = $priority; + $smallestDist = $dist; + $bestTarget = $noLOSMonsters[$index]; + } + } + } + return $bestTarget; +} + +## +# boolean isSafe() +# +# Returns 1 if there is a player nearby (except party and homunculus) or 0 if not +sub isSafe { + foreach (@playersID) { + if (!$char->{party}{users}{$_}) { + return 0; + } + } + return 1; +} + +## +# boolean isSafeActorQuery(ID) +# +# Returns 1 if we are safe to query actor name by given actor ID. +sub isSafeActorQuery { + my ($ID) = @_; + foreach my $list ($playersList, $monstersList, $npcsList, $petsList, $slavesList) { + my $actor = $list->getByID($ID); + if ($actor) { + # Do not AutoVivify here! + if (defined $actor->{statuses} && %{$actor->{statuses}}) { + if ( $actor->statusActive('EFFECTSTATE_SPECIALHIDING') || ($config{avoidHiddenActors} && ($actor->{type} == 111 || $actor->{type} == 139 || $actor->{type} == 2337)) ) { # HIDDEN_ACTOR TYPES + return 0; + } + } + return 0 if($actor->{avoid}); + } + } + return 1; +} + +####################################### +####################################### +###CATEGORY: Actor's Actions Text +####################################### +####################################### + +## +# String attack_string(Actor source, Actor target, int damage, int delay) +# +# Generates a proper message string for when actor $source attacks actor $target. +sub attack_string { + my ($source, $target, $damage, $delay) = @_; + assertClass($source, 'Actor') if DEBUG; + assertClass($target, 'Actor') if DEBUG; + + return TF("%s %s %s (Dmg: %s) (Delay: %sms)\n", + $source->nameString, + $source->verb(T('attack'), T('attacks')), + $target->nameString($source), + $damage, $delay); +} + +sub skillCast_string { + my ($source, $target, $x, $y, $skillName, $delay) = @_; + assertClass($source, 'Actor') if DEBUG; + assertClass($target, 'Actor') if DEBUG; + + return TF("%s %s %s on %s (Delay: %sms)\n", + $source->nameString(), + $source->verb(T('are casting'), T('is casting')), + $skillName, + ($x != 0 || $y != 0) ? TF("location (%d, %d)", $x, $y) : $target->nameString($source), + $delay); +} + +sub skillUse_string { + my ($source, $target, $skillName, $damage, $level, $delay) = @_; + assertClass($source, 'Actor') if DEBUG; + assertClass($target, 'Actor') if DEBUG; + + return sprintf("%s %s %s%s %s %s%s%s\n", + $source->nameString(), + $source->verb(T('use'), T('uses')), + $skillName, + ($level != 65535 && $level != 4294967295) ? ' ' . TF("(Lv: %s)", $level) : '', + T('on'), + $target->nameString($source), + ($damage != -30000) ? ' ' . TF("(Dmg: %s)", $damage || T('Miss')) : '', + ($delay) ? ' ' . TF("(Delay: %sms)", $delay) : ''); +} + +sub skillUseLocation_string { + my ($source, $skillName, $args) = @_; + assertClass($source, 'Actor') if DEBUG; + + return sprintf("%s %s %s%s %s (%d, %d)\n", + $source->nameString(), + $source->verb(T('use'), T('uses')), + $skillName, + ($args->{lv} != 65535 && $args->{lv} != 4294967295) ? ' ' . TF("(Lv: %s)", $args->{lv}) : '', + T('on location'), + $args->{x}, + $args->{y}); +} + +# TODO: maybe add other healing skill ID's? +sub skillUseNoDamage_string { + my ($source, $target, $skillID, $skillName, $amount) = @_; + assertClass($source, 'Actor') if DEBUG; + assertClass($target, 'Actor') if DEBUG; + + return sprintf("%s %s %s %s %s%s\n", + $source->nameString(), + $source->verb(T('use'), T('uses')), + $skillName, + T('on'), + $target->nameString($source), + ($skillID == 28) ? ' ' . TF("(Gained: %s hp)", $amount) : ($amount && $amount != 65535 && $amount != 4294967295) ? ' ' . TF("(Lv: %s)", $amount) : ''); +} + +sub status_string { + my ($source, $statusName, $mode, $seconds) = @_; + assertClass($source, 'Actor') if DEBUG; + + # Translation Comment: "you/actor" "are/is now/again/nolonger" "status" "(duration)" + TF("%s %s: %s%s\n", + $source->nameString, + ($mode eq 'now') ? $source->verb(T('are now'), T('is now')) + : ($mode eq 'again') ? $source->verb(T('are again'), T('is again')) + : ($mode eq 'no longer') ? $source->verb(T('are no longer'), T('is no longer')) : $mode, + $statusName, + $seconds ? ' ' . TF("(Duration: %ss)", $seconds) : '' + ) +} + +####################################### +####################################### +###CATEGORY: AI Math +####################################### +####################################### + +sub lineIntersection { + my $r_pos1 = shift; + my $r_pos2 = shift; + my $r_pos3 = shift; + my $r_pos4 = shift; + my ($x1, $x2, $x3, $x4, $y1, $y2, $y3, $y4, $result, $result1, $result2); + $x1 = $$r_pos1{'x'}; + $y1 = $$r_pos1{'y'}; + $x2 = $$r_pos2{'x'}; + $y2 = $$r_pos2{'y'}; + $x3 = $$r_pos3{'x'}; + $y3 = $$r_pos3{'y'}; + $x4 = $$r_pos4{'x'}; + $y4 = $$r_pos4{'y'}; + $result1 = ($x4 - $x3)*($y1 - $y3) - ($y4 - $y3)*($x1 - $x3); + $result2 = ($y4 - $y3)*($x2 - $x1) - ($x4 - $x3)*($y2 - $y1); + if ($result2 != 0) { + $result = $result1 / $result2; + } + return $result; +} + +sub percent_hp { + my $r_hash = shift; + if (!$$r_hash{'hp_max'}) { + return undef; + } else { + return ($$r_hash{'hp'} / $$r_hash{'hp_max'} * 100); + } +} + +sub percent_sp { + my $r_hash = shift; + if (!$$r_hash{'sp_max'}) { + return 0; + } else { + return ($$r_hash{'sp'} / $$r_hash{'sp_max'} * 100); + } +} + +sub percent_ap { + my $r_hash = shift; + if (!$$r_hash{'ap_max'}) { + return 0; + } else { + return ($$r_hash{'ap'} / $$r_hash{'ap_max'} * 100); + } +} + +sub percent_weight { + my $r_hash = shift; + if (!$$r_hash{'weight_max'}) { + return 0; + } else { + return ($$r_hash{'weight'} / $$r_hash{'weight_max'} * 100); + } +} + + +####################################### +####################################### +###CATEGORY: Misc Functions +####################################### +####################################### + +sub avoidGM_near { + for my $player (@$playersList) { + # skip this person if we dont know the name + next if (!defined $player->{name}); + + # Check whether this "GM" is on the ignore list in order to prevent false matches + next if (existsInList($config{avoidGM_ignoreList}, $player->{name})); + + # check if this name matches the GM filter + next unless ($config{avoidGM_namePattern} ? $player->{name} =~ /$config{avoidGM_namePattern}/ : $player->{name} =~ /^([a-z]?ro)?-?(Sub)?-?\[?GM\]?/i); + + my %args = ( + name => $player->{name}, + ID => $player->{nameID} + ); + Plugins::callHook('avoidGM_near', \%args); + return 1 if ($args{return}); + + my $msg; + if ($config{avoidGM_near} == 1) { + # Mode 1: teleport & disconnect + ai_useTeleport(1); + $msg = TF("GM '%s' (%d) is nearby (%s), teleport & disconnect for %d seconds", $player->{name}, $player->{nameID}, $field->baseName, $config{avoidGM_reconnect}); + relog($config{avoidGM_reconnect}, 1); + + } elsif ($config{avoidGM_near} == 2) { + # Mode 2: disconnect + $msg = TF("GM '%s' (%d) is nearby (%s), disconnect for %s seconds", $player->{name}, $player->{nameID}, $field->baseName, $config{avoidGM_reconnect}); + relog($config{avoidGM_reconnect}, 1); + + } elsif ($config{avoidGM_near} == 3) { + # Mode 3: teleport + ai_useTeleport(1); + $msg = TF("GM '%s' (%d) is nearby(%s), teleporting", $player->{name}, $player->{nameID}, $field->baseName); + + } elsif ($config{avoidGM_near} == 4) { + # Mode 4: respawn + ai_useTeleport(2); + $msg = TF("GM '%s' (%d) is nearby (%s), respawning", $player->{name}, $player->{nameID}, $field->baseName); + } elsif ($config{avoidGM_near} >= 5) { + # Mode 5: respawn & disconnect + ai_useTeleport(2); + $msg = TF("GM '%s' (%d) is nearby (%s), respawning & disconnect for %d seconds", $player->{name}, $player->{nameID}, $field->baseName, $config{avoidGM_reconnect}); + relog($config{avoidGM_reconnect}, 1); + } + + warning "$msg\n"; + chatLog("k", "*** $msg ***\n"); + + return 1; + } + return 0; +} + +sub avoidGM_talk { + return 0 if ($net->clientAlive() || !$config{avoidGM_talk}); + my ($player_name, $nameID) = @_; + + # Check whether this "GM" is on the ignore list in order to prevent false matches + return 0 if (existsInList($config{avoidGM_ignoreList}, $player_name)); + + return 0 unless ($config{avoidGM_namePattern} ? $player_name =~ /$config{avoidGM_namePattern}/ : $player_name =~ /^([a-z]?ro)?-?(Sub)?-?\[?GM\]?/i); + + my %args = ( + name => $player_name, + ID => $nameID + ); + Plugins::callHook('avoidGM_talk', \%args); + return 1 if ($args{return}); + + my $msg = TF("GM '%s' (%d) talked to you (%s), disconnect for %s seconds", $player_name, $nameID, $field->baseName, $config{avoidGM_reconnect}); + warning "$msg\n"; + chatLog("k", "*** $msg ***\n"); + relog($config{avoidGM_reconnect}, 1); + return 1; +} + +## +# avoidList_near() +# Returns: 1 if someone was detected, 0 if no one was detected. +# +# Checks if any of the surrounding players are on the avoid.txt avoid list. +# Disconnects / teleports if a player is detected. +sub avoidList_near { + my $return = 0; + return if ($config{avoidList_inLockOnly} && $field->baseName ne $config{lockMap}); + + for my $player (@$playersList) { + # skip this person if we dont know the name + next if (!defined $player->{name}); + # Check whether this Player is on the ignore list in order to prevent false matches + next if (existsInList($config{avoidList_ignoreList}, $player->{name})); + + my $avoidPlayer = $avoid{Players}{lc($player->{name})}; + my $avoidID = $avoid{ID}{$player->{nameID}}; + my $avoidJob = $avoid{Jobs}{lc($jobs_lut{$player->{jobID}})}; + + # next if the player is not on the avoid list + next if (!$avoidPlayer and !$avoidID and !$avoidJob); + + my %args = ( + name => $player->{name}, + ID => $player->{nameID}, + jobID => $player->{jobID}, + Jobs => $jobs_lut{$player->{jobID}} + ); + Plugins::callHook('avoidList_near', \%args); + + my $msg; + if ( ($avoidPlayer && $avoidPlayer->{teleport_on_sight} == 1 && $avoidPlayer->{disconnect_on_sight} == 1) + || ($avoidID && $avoidID->{teleport_on_sight} && $avoidID->{disconnect_on_sight}) + || ($avoidJob && $avoidJob->{teleport_on_sight} && $avoidJob->{disconnect_on_sight}) ) { + # like avoidGM_near Mode 1: teleport & disconnect + ai_useTeleport(1); + $msg = TF("Player %s (%d, %s) is nearby (%s), teleport & disconnect for %d seconds", $player->{name}, $player->{nameID}, $jobs_lut{$player->{jobID}}, $field->baseName, $config{avoidList_reconnect}); + relog($config{avoidList_reconnect}, 1); + $return = 1; + + } elsif ( ($avoidPlayer && $avoidPlayer->{teleport_on_sight} && $avoidPlayer->{disconnect_on_sight}) + || ($avoidID && $avoidID->{teleport_on_sight} && $avoidID->{disconnect_on_sight}) + || ($avoidJob && $avoidJob->{teleport_on_sight} && $avoidJob->{disconnect_on_sight}) ) { + # like avoidGM_near Mode 5: respawn & disconnect + ai_useTeleport(2); + $msg = TF("Player %s (%d, %s) is nearby (%s), respawning & disconnect for %d seconds", $player->{name}, $player->{nameID}, $jobs_lut{$player->{jobID}}, $field->baseName, $config{avoidList_reconnect}); + relog($config{avoidList_reconnect}, 1); + $return = 1; + + } elsif ( !$net->clientAlive() && ( ($avoidPlayer && $avoidPlayer->{disconnect_on_sight}) || + ($avoidID && $avoidID->{disconnect_on_sight}) && ($avoidJob && $avoidJob->{disconnect_on_sight}) ) ) { + # like avoidGM_near Mode 2: disconnect + $msg = TF("Player %s (%d, %s) is nearby (%s), disconnect for %s seconds", $player->{name}, $player->{nameID}, $jobs_lut{$player->{jobID}}, $field->baseName, $config{avoidList_reconnect}); + relog($config{avoidList_reconnect}, 1); + $return = 1; + + } elsif ( ($avoidPlayer && $avoidPlayer->{teleport_on_sight} == 1) || ($avoidID && $avoidID->{teleport_on_sight} == 1) || ($avoidJob && $avoidJob->{teleport_on_sight} == 1) ) { + # like avoidGM_near Mode 3: teleport + ai_useTeleport(1); + $msg = TF("Player %s (%d, %s) is nearby (%s), teleporting", $player->{name}, $player->{nameID}, $jobs_lut{$player->{jobID}}, $field->baseName); + $return = 1; + + } elsif ( ($avoidPlayer && $avoidPlayer->{teleport_on_sight} == 2) || ($avoidID && $avoidID->{teleport_on_sight} == 2) || ($avoidJob && $avoidJob->{teleport_on_sight} == 2) ) { + # like avoidGM_near Mode 4: respawn + ai_useTeleport(2); + $msg = TF("Player %s (%d, %s) is nearby (%s), respawning", $player->{name}, $player->{nameID}, $jobs_lut{$player->{jobID}}, $field->baseName); + $return = 1; + } + + if ($msg) { + warning "$msg\n"; + chatLog("k", "*** $msg ***\n"); + } + + } + return $return; +} + +sub avoidList_ID { + return if (!($config{avoidList}) || ($config{avoidList_inLockOnly} && $field->baseName ne $config{lockMap})); + + my $avoidID = unpack("V", shift); + if ($avoid{ID}{$avoidID} && $avoid{ID}{$avoidID}{disconnect_on_sight}) { + my $msg = TF("Player with ID %s is nearby (%s), disconnect for %s seconds", $avoidID, $field->baseName, $config{avoidList_reconnect}); + warning "$msg\n"; + chatLog("k", "*** $msg ***\n"); + relog($config{avoidList_reconnect}, 1); + return 1; + } + return 0; +} + +sub avoidList_talk { + return 0 if ($net->clientAlive() || !$config{avoidList}); + my ($player_name, $nameID) = @_; + my $avoidPlayer = $avoid{Players}{lc($player_name)}; + my $avoidID = $avoid{ID}{$nameID}; + + if ($avoidPlayer->{disconnect_on_chat} || $avoidID->{disconnect_on_chat}) { + my $msg = TF("Player %s (%d) talked to you (%s), disconnect for %d seconds", $player_name, $nameID, $field->baseName, $config{avoidList_reconnect}); + warning "$msg\n"; + chatLog("k", "*** $msg ***\n"); + relog($config{avoidList_reconnect}, 1); + return 1; + } + return 0; +} + +sub compilePortals { + my $checkOnly = shift; + + my %mapPortals; + my %mapSpawns; + my %missingMap; + my $pathfinding; + my @solution; + my $field; + + # Collect portal source and destination coordinates per map + foreach my $portal (keys %portals_lut) { + $mapPortals{$portals_lut{$portal}{source}{map}}{$portal}{x} = $portals_lut{$portal}{source}{x}; + $mapPortals{$portals_lut{$portal}{source}{map}}{$portal}{y} = $portals_lut{$portal}{source}{y}; + foreach my $dest (keys %{$portals_lut{$portal}{dest}}) { + next if $portals_lut{$portal}{dest}{$dest}{map} eq ''; + $mapSpawns{$portals_lut{$portal}{dest}{$dest}{map}}{$dest}{x} = $portals_lut{$portal}{dest}{$dest}{x}; + $mapSpawns{$portals_lut{$portal}{dest}{$dest}{map}}{$dest}{y} = $portals_lut{$portal}{dest}{$dest}{y}; + } + } + + foreach my $portal (keys %portals_commands) { + foreach my $dest (keys %{$portals_commands{$portal}{dest}}) { + next if $portals_commands{$portal}{dest}{$dest}{map} eq ''; + $mapSpawns{$portals_commands{$portal}{dest}{$dest}{map}}{$dest}{x} = $portals_commands{$portal}{dest}{$dest}{x}; + $mapSpawns{$portals_commands{$portal}{dest}{$dest}{map}}{$dest}{y} = $portals_commands{$portal}{dest}{$dest}{y}; + } + } + + foreach my $portal (keys %portals_spawns) { + foreach my $dest (keys %{$portals_spawns{$portal}{dest}}) { + next if $portals_spawns{$portal}{dest}{$dest}{map} eq ''; + $mapSpawns{$portals_spawns{$portal}{dest}{$dest}{map}}{$dest}{x} = $portals_spawns{$portal}{dest}{$dest}{x}; + $mapSpawns{$portals_spawns{$portal}{dest}{$dest}{map}}{$dest}{y} = $portals_spawns{$portal}{dest}{$dest}{y}; + } + } + + foreach my $portal (keys %portals_airships) { + $mapPortals{$portals_airships{$portal}{source}{map}}{$portal}{x} = $portals_airships{$portal}{source}{x}; + $mapPortals{$portals_airships{$portal}{source}{map}}{$portal}{y} = $portals_airships{$portal}{source}{y}; + foreach my $dest (keys %{$portals_airships{$portal}{dest}}) { + next if $portals_airships{$portal}{dest}{$dest}{map} eq ''; + $mapSpawns{$portals_airships{$portal}{dest}{$dest}{map}}{$dest}{x} = $portals_airships{$portal}{dest}{$dest}{x}; + $mapSpawns{$portals_airships{$portal}{dest}{$dest}{map}}{$dest}{y} = $portals_airships{$portal}{dest}{$dest}{y}; + } + } + + $pathfinding = new PathFinding if (!$checkOnly); + + # Calculate LOS values from each spawn point per map to other portals on same map + foreach my $map (sort keys %mapSpawns) { + ($map, undef) = Field::nameToBaseName(undef, $map); # Hack to clean up InstanceID + message TF("Processing map %s...\n", $map), "system" unless $checkOnly; + foreach my $spawn (keys %{$mapSpawns{$map}}) { + foreach my $portal (keys %{$mapPortals{$map}}) { + next if $spawn eq $portal; + next if $portals_los{$spawn}{$portal} ne ''; + return 1 if $checkOnly; + if ((!$field || $field->baseName ne $map) && !$missingMap{$map}) { + eval { + $field = new Field(name => $map); + }; + if ($@) { + $missingMap{$map} = 1; + } + } + + my %start = %{$mapSpawns{$map}{$spawn}}; + my %dest = %{$mapPortals{$map}{$portal}}; + my $closest_start = $field->closestWalkableSpot(\%start, 1); + my $closest_dest = $field->closestWalkableSpot(\%dest, 1); + my $count; + + if (!defined $closest_start || !defined $closest_dest) { + $count = 0; + } else { + $pathfinding->reset( + start => $closest_start, + dest => $closest_dest, + field => $field + ); + $count = $pathfinding->runcount; + } + $portals_los{$spawn}{$portal} = ($count >= 0) ? $count : 0; + debug "LOS in $map from $start{x},$start{y} to $dest{x},$dest{y}: $portals_los{$spawn}{$portal}\n"; + } + } + } + return 0 if $checkOnly; + + # Write new portalsLOS.txt + writePortalsLOS(Settings::getTableFilename("portalsLOS.txt"), \%portals_los); + message TF("Wrote portals Line of Sight table to '%s'\n", Settings::getTableFilename("portalsLOS.txt")), "system"; + + # Print warning for missing fields + if (%missingMap) { + warning T("----------------------------Error Summary----------------------------\n"); + warning TF("Missing: %s.fld2\n", $_) foreach (sort keys %missingMap); + warning T("Note: LOS information for the above listed map(s) will be inaccurate;\n" . + " however it is safe to ignore if those map(s) are not used\n"); + warning "---------------------------------------------------------------------\n"; + } +} + +sub compilePortals_check { + return compilePortals(1); +} + +sub portalExists { + my ($map, $r_pos) = @_; + foreach (keys %portals_lut) { + if ($portals_lut{$_}{source}{map} eq $map + && $portals_lut{$_}{source}{x} == $r_pos->{x} + && $portals_lut{$_}{source}{y} == $r_pos->{y}) { + return $_; + } + } + return; +} + +sub portalExists2 { + my ($src, $src_pos, $dest, $dest_pos) = @_; + my $srcx = $src_pos->{x}; + my $srcy = $src_pos->{y}; + my $destx = $dest_pos->{x}; + my $desty = $dest_pos->{y}; + my $destID = "$dest $destx $desty"; + + foreach (keys %portals_lut) { + my $entry = $portals_lut{$_}; + if ($entry->{source}{map} eq $src + && $entry->{source}{pos}{x} == $srcx + && $entry->{source}{pos}{y} == $srcy + && $entry->{dest}{$destID}) { + return $_; + } + } + return; +} + +sub portalExistsAirship { + my ($map, $r_pos) = @_; + foreach (keys %portals_airships) { + if ($portals_airships{$_}{source}{map} eq $map + && $portals_airships{$_}{source}{x} == $r_pos->{x} + && $portals_airships{$_}{source}{y} == $r_pos->{y}) { + return $_; + } + } + return; +} + +sub redirectXKoreMessages { + my ($type, $domain, $level, $globalVerbosity, $message, $user_data) = @_; + + return if ($config{'XKore_silent'} || $type eq "debug" || $level > 0 || $net->getState() != Network::IN_GAME || $XKore_dontRedirect); + return if ($domain =~ /^(connection|startup|pm|publicchat|guildchat|guildnotice|selfchat|emotion|drop|inventory|deal|storage|input)$/); + return if ($domain =~ /^(attack|skill|list|info|partychat|npc|route)/); + + $message =~ s/\n*$//s; + $message =~ s/\n/\\n/g; + sendMessage($messageSender, "k", $message); +} + +sub monKilled { + $monkilltime = time(); + # if someone kills it + if (($monstarttime == 0) || ($monkilltime < $monstarttime)) { + $monstarttime = 0; + $monkilltime = 0; + } + $elasped = $monkilltime - $monstarttime; + $totalelasped = $totalelasped + $elasped; + if ($totalelasped == 0) { + $dmgpsec = 0 + } else { + $dmgpsec = $totaldmg / $totalelasped; + } +} + +# Resolves a player or monster ID into a name +# Obsoleted by Actor module, don't use this! +sub getActorName { + my $id = shift; + + if (!$id) { + return T("Nothing"); + } else { + my $hash = Actor::get($id); + return $hash->nameString; + } +} + +# Resolves a pair of player/monster IDs into names +sub getActorNames { + my ($sourceID, $targetID, $verb1, $verb2) = @_; + + my $source = getActorName($sourceID); + my $verb = $source eq 'You' ? $verb1 : $verb2; + my $target; + + if ($targetID eq $sourceID) { + if ($targetID eq $accountID) { + $target = 'yourself'; + } else { + $target = 'self'; + } + } else { + $target = getActorName($targetID); + } + + return ($source, $verb, $target); +} + +# return ID based on name if party member is online +sub findPartyUserID { + if ($char->{party}{joined}) { + my $partyUserName = shift; + for (my $j = 0; $j < @partyUsersID; $j++) { + next if ($partyUsersID[$j] eq ""); + if ($partyUserName eq $char->{party}{users}{$partyUsersID[$j]}{name} + && $char->{party}{users}{$partyUsersID[$j]}{online}) { + return $partyUsersID[$j]; + } + } + } + + return undef; +} + +# fill in a hash of NPC information either based on location ("map x y") +sub getNPCInfo { + my $id = shift; + my $return_hash = shift; + + undef %{$return_hash}; + + my ($map, $x, $y) = split(/ +/, $id, 3); + + $$return_hash{map} = $map; + $$return_hash{pos}{x} = $x; + $$return_hash{pos}{y} = $y; + + if (($$return_hash{map} ne "") && ($$return_hash{pos}{x} ne "") && ($$return_hash{pos}{y} ne "")) { + $$return_hash{ok} = 1; + } else { + error TF("Invalid NPC information for autoBuy, autoSell or autoStorage! (%s)\n", $id); + } +} + +sub checkSelfCondition { + my $prefix = shift; + return 0 if (!$prefix); + return 0 if ($config{$prefix . "_disabled"}); + + return 0 if ($config{$prefix."_whenIdle"} && !AI::isIdle); + + return 0 if ($config{$prefix."_whenNotIdle"} && AI::isIdle); + + # TODO: Is there any situation where we should use calcPosFromPathfinding or calcPosFromTime here in these checks? + + # *_manualAI 0 = auto only + # *_manualAI 1 = manual only + # *_manualAI 2 = auto or manual + if ($config{$prefix . "_manualAI"} == 0 || !(defined $config{$prefix . "_manualAI"})) { + return 0 unless AI::state == AI::AUTO; + } elsif ($config{$prefix . "_manualAI"} == 1){ + return 0 unless AI::state == AI::MANUAL; + } else { + return 0 if AI::state == AI::OFF; + } + + if ($config{$prefix . "_hp"}) { + if ($config{$prefix."_hp"} =~ /^(.*)\%$/) { + return 0 if (!inRange($char->hp_percent, $1)); + } else { + return 0 if (!inRange($char->{hp}, $config{$prefix."_hp"})); + } + } + + if ($config{$prefix."_sp"}) { + if ($config{$prefix."_sp"} =~ /^(.*)\%$/) { + return 0 if (!inRange($char->sp_percent, $1)); + } else { + return 0 if (!inRange($char->{sp}, $config{$prefix."_sp"})); + } + } + + if ($config{$prefix . "_ap"}) { + if ($config{$prefix."_ap"} =~ /^(.*)\%$/) { + return 0 if (!inRange($char->ap_percent, $1)); + } else { + return 0 if (!inRange($char->{ap}, $config{$prefix."_ap"})); + } + } + + if ($config{$prefix."_weight"}) { + if ($config{$prefix."_weight"} =~ /^(.*)\%$/) { + return 0 if $char->{weight_max} && !inRange($char->weight_percent, $1); + } else { + return 0 if !inRange($char->{weight}, $config{$prefix."_weight"}); + } + } + + my $has_homunculus = $char->has_homunculus; + + if ($config{$prefix."_homunculus"} =~ /\S/) { + return 0 if (($config{$prefix."_homunculus"} && !$has_homunculus) || (!$config{$prefix."_homunculus"} && $has_homunculus)); + } + + if ($config{$prefix . "_homunculus_hp"}) { + return 0 unless ($has_homunculus); + if ($config{$prefix."_homunculus_hp"} =~ /^(.*)\%$/) { + return 0 if (!inRange($char->{homunculus}->hp_percent, $1)); + } else { + return 0 if (!inRange($char->{homunculus}{hp}, $config{$prefix."_homunculus_hp"})); + } + } + + if ($config{$prefix."_homunculus_sp"}) { + return 0 unless ($has_homunculus); + if ($config{$prefix."_homunculus_sp"} =~ /^(.*)\%$/) { + return 0 if (!inRange($char->{homunculus}->sp_percent, $1)); + } else { + return 0 if (!inRange($char->{homunculus}{sp}, $config{$prefix."_homunculus_sp"})); + } + } + + if ($config{$prefix."_homunculus_onAction"}) { + return 0 unless ($has_homunculus); + return 0 unless (existsInList($config{$prefix . "_homunculus_onAction"}, $char->{homunculus}->action())); + } + + if ($config{$prefix."_homunculus_notOnAction"}) { + return 0 unless ($has_homunculus); + return 0 if (existsInList($config{$prefix . "_homunculus_notOnAction"}, $char->{homunculus}->action())); + } + + if ($config{$prefix."_homunculus_whenIdle"}) { + return 0 unless ($has_homunculus); + return 0 unless ($char->{homunculus}->isIdle); + } + + if ($config{$prefix."_homunculus_whenNotIdle"}) { + return 0 unless ($has_homunculus); + return 0 if ($char->{homunculus}->isIdle); + } + + if ($config{$prefix."_homunculus_dead"} =~ /\S/) { + return 0 if (exists $char->{homunculus_info} && $config{$prefix."_homunculus_dead"} && $char->{homunculus_info}{dead} == 0); + return 0 if (exists $char->{homunculus_info} && !$config{$prefix."_homunculus_dead"} && $char->{homunculus_info}{dead} == 1); + } + + if ($config{$prefix."_homunculus_resting"} =~ /\S/) { + return 0 if (exists $char->{homunculus_info} && $config{$prefix."_homunculus_resting"} && $char->{homunculus_info}{vaporized} == 0); + return 0 if (exists $char->{homunculus_info} && !$config{$prefix."_homunculus_resting"} && $char->{homunculus_info}{vaporized} == 1); + } + + if ($config{$prefix."_homunculus_noinfo_dead"} =~ /\S/) { + return 0 if ($config{$prefix."_homunculus_noinfo_dead"} && exists $char->{homunculus_info} && exists $char->{homunculus_info}{dead} && defined $char->{homunculus_info}{dead}); + return 0 if (!$config{$prefix."_homunculus_noinfo_dead"} && (!exists $char->{homunculus_info} || !exists $char->{homunculus_info}{dead} || !defined $char->{homunculus_info}{dead})); + } + + if ($config{$prefix."_homunculus_noinfo_resting"} =~ /\S/) { + return 0 if ($config{$prefix."_homunculus_noinfo_resting"} && exists $char->{homunculus_info} && exists $char->{homunculus_info}{vaporized} && defined $char->{homunculus_info}{vaporized}); + return 0 if (!$config{$prefix."_homunculus_noinfo_resting"} && (!exists $char->{homunculus_info} || !exists $char->{homunculus_info}{vaporized} || !defined $char->{homunculus_info}{vaporized})); + } + + my $has_mercenary = $char->has_mercenary; + + if ($config{$prefix."_mercenary"} =~ /\S/) { + return 0 if (($config{$prefix."_mercenary"} && !$has_mercenary) || (!$config{$prefix."_mercenary"} && $has_mercenary)); + } + + if ($config{$prefix . "_mercenary_hp"}) { + return 0 unless ($has_mercenary); + if ($config{$prefix."_mercenary_hp"} =~ /^(.*)\%$/) { + return 0 if (!inRange($char->{mercenary}->hp_percent, $1)); + } else { + return 0 if (!inRange($char->{mercenary}{hp}, $config{$prefix."_mercenary_hp"})); + } + } + + if ($config{$prefix."_mercenary_sp"}) { + return 0 unless ($has_mercenary); + if ($config{$prefix."_mercenary_sp"} =~ /^(.*)\%$/) { + return 0 if (!inRange($char->{mercenary}->sp_percent, $1)); + } else { + return 0 if (!inRange($char->{mercenary}{sp}, $config{$prefix."_mercenary_sp"})); + } + } + + if ($config{$prefix . "_mercenary_whenStatusActive"}) { + return 0 unless ($has_mercenary); + return 0 unless $char->{mercenary}->statusActive($config{$prefix . "_mercenary_whenStatusActive"}); + } + if ($config{$prefix . "_mercenary_whenStatusInactive"}) { + return 0 unless ($has_mercenary); + return 0 if $char->{mercenary}->statusActive($config{$prefix . "_mercenary_whenStatusInactive"}); + } + + if ($config{$prefix."_mercenary_onAction"}) { + return 0 unless ($has_mercenary); + return 0 unless (existsInList($config{$prefix . "_mercenary_onAction"}, $char->{mercenary}->action())); + } + + if ($config{$prefix."_mercenary_notOnAction"}) { + return 0 unless ($has_mercenary); + return 0 if (existsInList($config{$prefix . "_mercenary_notOnAction"}, $char->{mercenary}->action())); + } + + if ($config{$prefix."_mercenary_whenIdle"}) { + return 0 unless ($has_mercenary); + return 0 unless ($char->{mercenary}->isIdle); + } + + if ($config{$prefix."_mercenary_whenNotIdle"}) { + return 0 unless ($has_mercenary); + return 0 if ($char->{mercenary}->isIdle); + } + + # check skill use SP if this is a 'use skill' condition + if ($prefix =~ /skill|attackComboSlot/i) { + my $skill = Skill->new(auto => $config{$prefix}); + if ($char->checkSkillOwnership ($skill)) { + return 0 unless ($char->getSkillLevel($skill) + || $config{$prefix."_equip_leftAccessory"} + || $config{$prefix."_equip_rightAccessory"} + || $config{$prefix."_equip_leftHand"} + || $config{$prefix."_equip_rightHand"} + || $config{$prefix."_equip_robe"} + ); + return 0 unless ($char->{sp} >= $skill->getSP($config{$prefix . "_lvl"} || $char->getSkillLevel($skill))); + + } elsif ($has_homunculus && $char->{homunculus}->checkSkillOwnership($skill)) { + return 0 unless ($char->{homunculus}->getSkillLevel($skill)); + + } elsif ($has_mercenary && $char->{mercenary}->checkSkillOwnership($skill)) { + return 0 unless ($char->{mercenary}->getSkillLevel($skill)); + } + } + + if (defined $config{$prefix . "_skill"}) { + foreach my $input (split / *, */, $config{$prefix."_skill"}) { + my ($skillName, $reqLevel) = $input =~ /(.*?)(?:\s+([><]=? *\d+))?$/; + $reqLevel = '>0' if $reqLevel eq ''; + my $skill = Skill->new(auto => $skillName); + my $skillLevel = $char->getSkillLevel($skill); + return 0 if !inRange($skillLevel, $reqLevel); + } + } + + if (defined $config{$prefix . "_aggressives"}) { + return 0 unless (inRange(scalar ai_getAggressives(), $config{$prefix . "_aggressives"})); + } + + if (defined $config{$prefix . "_partyAggressives"}) { + return 0 unless (inRange(scalar ai_getAggressives(undef, 1), $config{$prefix . "_partyAggressives"})); + } + + if ($config{$prefix . "_stopWhenHit"} > 0) { return 0 if (scalar ai_getMonstersAttacking($accountID)); } + + if ($config{$prefix . "_whenFollowing"} && $config{follow}) { + return 0 if (!checkFollowMode()); + } + + if ($config{$prefix . "_whenStatusActive"}) { + return 0 unless $char->statusActive($config{$prefix . "_whenStatusActive"}); + } + if ($config{$prefix . "_whenStatusInactive"}) { + return 0 if $char->statusActive($config{$prefix . "_whenStatusInactive"}); + } + + + if ($config{$prefix . "_inQueue"}) { return 0 unless AI::inQueue($config{$prefix . "_inQueue"}); } + if ($config{$prefix . "_notInQueue"}) { return 0 if AI::inQueue($config{$prefix . "_notInQueue"}); } + if ($config{$prefix . "_onAction"}) { return 0 unless (existsInList($config{$prefix . "_onAction"}, AI::action())); } + if ($config{$prefix . "_notOnAction"}) { return 0 if (existsInList($config{$prefix . "_notOnAction"}, AI::action())); } + if ($config{$prefix . "_spirit"}) {return 0 unless (inRange(defined $char->{spirits} ? $char->{spirits} : 0, $config{$prefix . "_spirit"})); } + if ($config{$prefix . "_amuletType"}) {return 0 unless $config{$prefix . "_amuletType"} eq $char->{amuletType}; } + + if ($config{$prefix . "_timeout"}) { return 0 unless timeOut($ai_v{$prefix . "_time"}, $config{$prefix . "_timeout"}) } + if ($config{$prefix . "_inLockOnly"} > 0) { return 0 unless ($field->baseName eq $config{lockMap}); } + if ($config{$prefix . "_notWhileSitting"} > 0) { return 0 if ($char->{sitting}); } + if ($config{$prefix . "_notWhileCasting"} > 0) { return 0 if (exists $char->{casting}); } + if ($config{$prefix . "_whileCasting"} > 0) { return 0 unless (exists $char->{casting}); } + if ($config{$prefix . "_notInTown"} > 0) { return 0 if ($field->isCity); } + if ($config{$prefix . "_inTown"} > 0) { return 0 unless ($field->isCity); } + if (defined $config{$prefix . "_monstersCount"}) { + my $nowMonsters = $monstersList->size(); + if ($nowMonsters > 0 && $config{$prefix . "_notMonsters"}) { + for my $monster (@$monstersList) { + $nowMonsters-- if (existsInList($config{$prefix . "_notMonsters"}, $monster->{name}) || + existsInList($config{$prefix . "_notMonsters"}, $monster->{nameID}) || + ($config{$prefix."_monstersCountDist"} && !inRange(blockDistance(calcPosition($char), calcPosition($monster)), $config{$prefix."_monstersCountDist"})) + ); + } + } + return 0 unless (inRange($nowMonsters, $config{$prefix . "_monstersCount"})); + } + if ($config{$prefix . "_monsters"} && !($prefix =~ /skillSlot/i) && !($prefix =~ /ComboSlot/i)) { + my $exists; + foreach (ai_getAggressives()) { + if (existsInList($config{$prefix . "_monsters"}, $monsters{$_}->name) || + existsInList($config{$prefix . "_monsters"}, $monsters{$_}->{nameID})) { + $exists = 1; + last; + } + } + return 0 unless $exists; + } + + if ($config{$prefix . "_defendMonsters"}) { + my $exists; + foreach (ai_getMonstersAttacking($accountID)) { + if (existsInList($config{$prefix . "_defendMonsters"}, $monsters{$_}->name) || + existsInList($config{$prefix . "_defendMonsters"}, $monsters{$_}->{nameID})) { + $exists = 1; + last; + } + } + return 0 unless $exists; + } + + if ($config{$prefix . "_notMonsters"} && !($prefix =~ /skillSlot/i) && !($prefix =~ /ComboSlot/i)) { + my $exists; + foreach (ai_getAggressives()) { + if (existsInList($config{$prefix . "_notMonsters"}, $monsters{$_}->name) || + existsInList($config{$prefix . "_notMonsters"}, $monsters{$_}->{nameID})) { + return 0; + } + } + } + + if ($config{$prefix."_inInventory"}) { + return 0 if (!$char->inventory->isReady()); + foreach my $input (split / *, */, $config{$prefix."_inInventory"}) { + my ($itemName, $count) = $input =~ /(.*?)(?:\s+([><]=? *\d+))?$/; + $count = '>0' if $count eq ''; + my $item = $char->inventory->getByName($itemName); + return 0 if !inRange(!$item ? 0 : $item->{amount}, $count); + } + } + + if ($config{$prefix."_inInventoryID"}) { + return 0 if (!$char->inventory->isReady()); + foreach my $input (split / *, */, $config{$prefix."_inInventoryID"}) { + my ($itemName, $count) = $input =~ /^(.*?)(?:\s*([><]=? *\d+))?$/; + $count = '>0' if $count eq ''; + my $item = $char->inventory->getByNameID($itemName); + return 0 if !inRange(!$item ? 0 : $item->{amount}, $count); + } + } + + if ($config{$prefix."_inCart"}) { + return 0 if (!$char->cart->isReady()); + foreach my $input (split / *, */, $config{$prefix."_inCart"}) { + my ($item,$count) = $input =~ /(.*?)(?:\s+([><]=? *\d+))?$/; + $count = '>0' if $count eq ''; + my $item = $char->cart->getByName($item); + return 0 if !inRange(!$item ? 0 : $item->{amount}, $count); + } + } + + if ($config{$prefix."_whenGround"}) { + return 0 unless whenGroundStatus(calcPosition($char), $config{$prefix."_whenGround"}); + } + + if ($config{$prefix."_whenNotGround"}) { + return 0 if whenGroundStatus(calcPosition($char), $config{$prefix."_whenNotGround"}); + } + + if ($config{$prefix."_whenPermitSkill"}) { + return 0 unless $char->{permitSkill} && + $char->{permitSkill}->getIDN == Skill->new(auto => $config{$prefix."_whenPermitSkill"})->getIDN; + } + + if ($config{$prefix."_whenNotPermitSkill"}) { + return 0 if $char->{permitSkill} && + $char->{permitSkill}->getIDN == Skill->new(auto => $config{$prefix."_whenNotPermitSkill"})->getIDN; + } + + if ($config{$prefix."_whenFlag"}) { + return 0 unless $flags{$config{$prefix."_whenFlag"}}; + } + if ($config{$prefix."_whenNotFlag"}) { + return 0 unless !$flags{$config{$prefix."_whenNotFlag"}}; + } + + if ($config{$prefix."_onlyWhenSafe"}) { + return 0 if !isSafe(); + } + + if (exists $config{$prefix."_nearPortal"} && defined $config{$prefix."_nearPortal"}) { + my $is_portal_near = 0; + my $pos = calcPosition($char); + for my $portal (@$portalsList) { + if (blockDistance($pos, $portal->{pos}) <= 2) { + $is_portal_near = 1; + last; + } + } + if ($config{$prefix."_nearPortal"} == 0) { + return 0 if ($is_portal_near); + + } elsif ($config{$prefix."_nearPortal"} == 1) { + return 0 unless ($is_portal_near); + } + } + + if ($config{$prefix."_inMap"}) { + return 0 unless (existsInList($config{$prefix . "_inMap"}, $field->baseName)); + } + + if ($config{$prefix."_notInMap"}) { + return 0 if (existsInList($config{$prefix . "_notInMap"}, $field->baseName)); + } + + if ($config{$prefix."_whenEquipped"}) { + my $item = Actor::Item::get($config{$prefix."_whenEquipped"}); + return 0 unless $item && $item->{equipped}; + } + + if ($config{$prefix."_whenNotEquipped"}) { + my $item = Actor::Item::get($config{$prefix."_whenNotEquipped"}); + return 0 if $item && $item->{equipped}; + } + + if ($config{$prefix."_zeny"}) { + return 0 if (!inRange($char->{zeny}, $config{$prefix."_zeny"})); + } + + if ($config{$prefix."_whenWater"}) { + my $pos = calcPosition($char); + return 0 unless ($field->isWater($pos->{x}, $pos->{y})); + } + + if ($config{$prefix.'_devotees'}) { + return 0 unless inRange(scalar keys %{$devotionList->{$accountID}{targetIDs}}, $config{$prefix.'_devotees'}); + } + + if ($config{$prefix."_whenPartyMembersNear"}) { + # Short circuit if there's not enough players nearby, party members or not + # +1 account for self + return 0 unless inRange(scalar @{$playersList} + 1, $config{$prefix."_whenPartyMembersNear"}); + + # Short circuit if there's not enough players in our party + return 0 unless inRange(scalar @partyUsersID, $config{$prefix."_whenPartyMembersNear"}); + + my $dist; + my $amountInRange = 1; # account for self + + if ($config{$prefix."_whenPartyMembersNearDist"}) { + $dist = $config{$prefix."_whenPartyMembersNearDist"}; + } else { + $dist = "< "; + $dist .= ($config{clientSight} || 15); + } + + foreach my $player (@{$playersList}) { + next unless ($char->{party}{joined} && $char->{party}{users}{$player->{ID}}); + next unless inRange(blockDistance(calcPosition($char), calcPosition($player)), $dist); + + ++$amountInRange; + } + + return 0 unless inRange($amountInRange, $config{$prefix."_whenPartyMembersNear"}); + } + + if ($config{$prefix . "_inParty"}) { + return 0 unless $char->{party}{joined}; + } + + if ($config{$prefix . "_notInParty"}) { + return 0 if $char->{party}{joined}; + } + + return 0 if ($config{$prefix . "_maxBase"} =~ /^\d{1,}$/ && $char->{lv} > $config{$prefix . "_maxBase"}); + return 0 if ($config{$prefix . "_minBase"} =~ /^\d{1,}$/ && $char->{lv} < $config{$prefix . "_minBase"}); + + my %hookArgs; + $hookArgs{prefix} = $prefix; + $hookArgs{return} = 1; + Plugins::callHook('checkSelfCondition', \%hookArgs); + return 0 if (!$hookArgs{return}); + + return 1; +} + +sub checkPlayerCondition { + my ($prefix, $id) = @_; + return 0 if (!$id); + + my $player = Actor::get($id); + return 0 unless ( + UNIVERSAL::isa($player, 'Actor::You') + || UNIVERSAL::isa($player, 'Actor::Player') + || UNIVERSAL::isa($player, 'Actor::Slave') + ); + # my $player = $playersList->getByID($id) || $slavesList->getByID($id); + + if ($config{$prefix . "_timeout"}) { return 0 unless timeOut($ai_v{$prefix . "_time"}{$id}, $config{$prefix . "_timeout"}) } + if ($config{$prefix . "_whenStatusActive"}) { + return 0 unless $player->statusActive($config{$prefix . "_whenStatusActive"}); + } + if ($config{$prefix . "_whenStatusInactive"}) { + return 0 if $player->statusActive($config{$prefix . "_whenStatusInactive"}); + } + if ($config{$prefix . "_notWhileSitting"} > 0) { return 0 if ($player->{sitting}); } + + # TODO: Optimize this + if ($config{$prefix . "_hp"}) { + # Target is Actor::You + if ($char->{ID} eq $id) { + if ($config{$prefix."_hp"} =~ /^(.*)\%$/) { + return 0 if (!inRange($char->hp_percent, $1)); + } else { + return 0 if (!inRange($char->{hp}, $config{$prefix."_hp"})); + } + # Target is Actor::Player in our Party + } elsif ($char->{party}{joined} && $char->{party}{users}{$id}) { + # Fix Heal when Target HP is not set yet. + # return 0 if (!defined($player->{hp}) || $player->{hp} == 0); + return 0 if ($char->{party}{users}{$id}{hp} == 0); + if ($config{$prefix."_hp"} =~ /^(.*)\%$/) { + # return 0 if (!inRange(percent_hp($player), $1)); + return 0 if (!inRange(percent_hp($char->{party}{users}{$id}), $1)); + } else { + # return 0 if (!inRange($player->{hp}, $config{$prefix . "_hp"})); + return 0 if (!inRange($char->{party}{users}{$id}{hp}, $config{$prefix . "_hp"})); + } + # Target is Actor::Slave 'Homunculus' type + } elsif ($char->{homunculus} && $char->{homunculus}{ID} eq $id) { + if ($config{$prefix."_hp"} =~ /^(.*)\%$/) { + return 0 if (!inRange(percent_hp($char->{homunculus}), $1)); + } else { + return 0 if (!inRange($char->{homunculus}{hp}, $config{$prefix . "_hp"})); + } + # Target is Actor::Slave 'Mercenary' type + } elsif ($char->{mercenary} && $char->{mercenary}{ID} eq $id) { + if ($config{$prefix."_hp"} =~ /^(.*)\%$/) { + return 0 if (!inRange(percent_hp($char->{mercenary}), $1)); + } else { + return 0 if (!inRange($char->{mercenary}{hp}, $config{$prefix . "_hp"})); + } + } + } + + if ($config{$prefix."_deltaHp"}){ + return 0 unless inRange($player->{deltaHp}, $config{$prefix."_deltaHp"}); + } + + # check player job class + if ($config{$prefix . "_isJob"}) { return 0 unless (existsInList($config{$prefix . "_isJob"}, $jobs_lut{$player->{jobID}})); } + if ($config{$prefix . "_isNotJob"}) { return 0 if (existsInList($config{$prefix . "_isNotJob"}, $jobs_lut{$player->{jobID}})); } + + if ($config{$prefix . "_aggressives"}) { + return 0 unless (inRange(scalar ai_getPlayerAggressives($id), $config{$prefix . "_aggressives"})); + } + + if ($config{$prefix . "_defendMonsters"}) { + my $exists; + foreach (ai_getMonstersAttacking($id)) { + if (existsInList($config{$prefix . "_defendMonsters"}, $monsters{$_}{name}) || + existsInList($config{$prefix . "_defendMonsters"}, $monsters{$_}{nameID})) { + $exists = 1; + last; + } + } + return 0 unless $exists; + } + + if ($config{$prefix . "_monsters"}) { + my $exists; + foreach (ai_getPlayerAggressives($id)) { + if (existsInList($config{$prefix . "_monsters"}, $monsters{$_}{name}) || + existsInList($config{$prefix . "_monsters"}, $monsters{$_}{nameID})) { + $exists = 1; + last; + } + } + return 0 unless $exists; + } + + if ($config{$prefix."_whenGround"}) { + return 0 unless whenGroundStatus(calcPosition($player), $config{$prefix."_whenGround"}); + } + if ($config{$prefix."_whenNotGround"}) { + return 0 if whenGroundStatus(calcPosition($player), $config{$prefix."_whenNotGround"}); + } + if ($config{$prefix."_dead"}) { + return 0 if !$player->{dead}; + } else { + return 0 if $player->{dead}; + } + + # Note: This will always fail for Actor::Slave + if ($config{$prefix."_whenWeaponEquipped"}) { + return 0 unless $player->{weapon}; + } + + # Note: This will always fail for Actor::Slave + if ($config{$prefix."_whenShieldEquipped"}) { + return 0 unless $player->{shield}; + } + + # Note: This will always fail for Actor::Slave + if ($config{$prefix."_isGuild"}) { + return 0 unless ($player->{guild} && existsInList($config{$prefix . "_isGuild"}, $player->{guild}{name})); + } + + # Note: This will always be true for Actor::Slave + # This will always be true for character that is not in any guild + if ($config{$prefix."_isNotGuild"}) { + return 0 if ($player->{guild} && existsInList($config{$prefix . "_isNotGuild"}, $player->{guild}{name})); + } + + if ($config{$prefix."_dist"}) { + return 0 unless inRange(blockDistance(calcPosition($char), calcPosition($player)), $config{$prefix."_dist"}); + } + + if ($config{$prefix."_isNotMyDevotee"}) { + return 0 if (defined $devotionList->{$accountID}->{targetIDs}->{$id}); + } + + if ($config{$prefix."_spirit"}) { + return 0 unless inRange(defined $player->{spirits} ? $player->{spirits} : 0, $config{$prefix . "_spirit"}); + } + + my %args = ( + player => $player, + prefix => $prefix, + return => 1 + ); + + Plugins::callHook('checkPlayerCondition', \%args); + + return $args{return}; +} + +sub checkMonsterCondition { + my ($prefix, $monster) = @_; + + # TODO: Is there any situation where we should use calcPosFromPathfinding or calcPosFromTime in these checks? + + if ($config{$prefix . "_hp"}) { + if($config{$prefix . "_hp"} =~ /(\d+)%$/) { + if($monster->{hp} && $monster->{hp_max}) { + return 0 unless (inRange(($monster->{hp} * 100 / $monster->{hp_max}), $config{$prefix . "_hp"})); + } elsif($monster->{hp_percent}) { + return 0 unless (inRange($monster->{hp_percent}, $config{$prefix . "_hp"})); + } else { + return 0; + } + } elsif($config{$prefix . "_hp"} =~ /(\d+)$/) { + if($monster->{hp} && $monster->{hp_max}) { + return 0 unless (inRange($monster->{hp}, $config{$prefix . "_hp"})); + } else { + return 0; + } + } else { + return 0; + } + } + + if ($config{$prefix . "_timeout"}) { return 0 unless timeOut($ai_v{$prefix . "_time"}{$monster->{ID}}, $config{$prefix . "_timeout"}) } + + if (my $misses = $config{$prefix . "_misses"}) { + return 0 unless inRange($monster->{atkMiss}, $misses); + } + + if (my $misses = $config{$prefix . "_totalMisses"}) { + return 0 unless inRange($monster->{missedFromYou}, $misses); + } + + if ($config{$prefix . "_whenStatusActive"}) { + return 0 unless $monster->statusActive($config{$prefix . "_whenStatusActive"}); + } + if ($config{$prefix . "_whenStatusInactive"}) { + return 0 if $monster->statusActive($config{$prefix . "_whenStatusInactive"}); + } + + if ($config{$prefix."_whenGround"}) { + return 0 unless whenGroundStatus(calcPosition($monster), $config{$prefix."_whenGround"}); + } + if ($config{$prefix."_whenNotGround"}) { + return 0 if whenGroundStatus(calcPosition($monster), $config{$prefix."_whenNotGround"}); + } + + if ($config{$prefix."_dist"}) { + return 0 unless inRange(blockDistance(calcPosition($char), calcPosition($monster)), $config{$prefix."_dist"}); + } + + if ($config{$prefix."_deltaHp"}){ + return 0 unless inRange($monster->{deltaHp}, $config{$prefix."_deltaHp"}); + } + + # This is only supposed to make sense for players, + # but it has to be here for attackSkillSlot PVP to work + if ($config{$prefix."_whenWeaponEquipped"}) { + return 0 unless $monster->{weapon}; + } + if ($config{$prefix."_whenShieldEquipped"}) { + return 0 unless $monster->{shield}; + } + + my %args = ( + monster => $monster, + prefix => $prefix, + return => 1 + ); + + Plugins::callHook('checkMonsterCondition', \%args); + return $args{return}; +} + +## +# makeShop() +# +# Returns an array of items to sell. The array can be no larger than the +# maximum number of items that the character can vend. Each item is a hash +# reference containing the keys "index", "amount" and "price". +# +# If there is a problem with opening a shop, an error message will be printed +# and nothing will be returned. +sub makeShop { + if ($shopstarted) { + error T("A shop has already been opened.\n"); + return; + } + + return unless $char; + + if (!$char->{skills}{MC_VENDING}{lv}) { + error T("You don't have the Vending skill.\n"); + return; + } + + if (!$char->cartActive) { + error T("You need this with a cart in order to create a shop!\n"); + return; + } + + if (!$shop{title_line}) { + error T("Your shop does not have a title.\n"); + return; + } + + my @items = (); + my $max_items = $char->{skills}{MC_VENDING}{lv} + 2; + + # Iterate through items to be sold + shuffleArray(\@{$shop{items}}) if ($config{'shop_random'} eq "2"); + my %used_items; + for my $sale (@{$shop{items}}) { + my $cart_item; + for my $item (@{$char->cart}) { + next unless $item->{name} eq $sale->{name}; + next if $used_items{$item->{binID}}; + $cart_item = $used_items{$item->{binID}} = $item; + last; + } + next unless ($cart_item); + + # Found item to vend + my $amount = $cart_item->{amount}; + + my %item; + $item{name} = $cart_item->{name}; + $item{ID} = $cart_item->{ID}; + if ($sale->{priceMax}) { + $item{price} = int(rand($sale->{priceMax} - $sale->{price})) + $sale->{price}; + } else { + $item{price} = $sale->{price}; + } + $item{amount} = + $sale->{amount} && $sale->{amount} < $amount ? + $sale->{amount} : $amount; + push(@items, \%item); + + # We can't vend anymore items + last if @items >= $max_items; + } + + if (!@items) { + error T("There are no items to sell.\n"); + return; + } + shuffleArray(\@items) if ($config{'shop_random'} eq "1"); + return @items; +} + +sub openShop { + my @items = makeShop(); + my @shopnames; + return unless @items; + @shopnames = split(/;;/, $shop{title_line}); + $shop{title} = $shopnames[int rand($#shopnames + 1)]; + $shop{title} = ($config{shopTitleOversize}) ? $shop{title} : substr($shop{title},0,36); + message T("Trying to set up shop...\n"), "vending"; + $messageSender->sendOpenShop($shop{title}, \@items); + $shopstarted = 1; + Plugins::callHook('open_shop', { + title => $shop{title}, + items => \@items + }); +} + +sub closeShop { + if (!$shopstarted) { + error T("A shop has not been opened.\n"); + return; + } + + $messageSender->sendCloseShop(); + + $shopstarted = 0; + $articles = 0; + $timeout{'ai_shop'}{'time'} = time; + Plugins::callHook('shop_closed'); + message T("Shop closed.\n"); +} + +sub makeBuyerShop { + if ($buyershopstarted) { + error T("A shop has already been opened.\n"); + return; + } + + return unless $char; + + my $max_items = 2; + my @items = (); + + if($char->inventory->getByNameID(6377)) { + $max_items = 5; + } + + # Iterate through items to be sold + shuffleArray(\@{$buyer_shop{items}}) if ($config{'buyerShop_random'} eq "2"); + my %used_items; + for my $sale (@{$buyer_shop{items}}) { + my $inventory_item; + for my $item (@{$char->inventory}) { + next unless $item->{name} eq $sale->{name}; + next if $used_items{$item->{binID}}; + $inventory_item = $used_items{$item->{binID}} = $item; + last; + } + next unless ($inventory_item); + + my %item; + $item{name} = $inventory_item->{name}; + $item{nameID} = $inventory_item->{nameID}; + if ($sale->{priceMax}) { + $item{price} = int(rand($sale->{priceMax} - $sale->{price})) + $sale->{price}; + } else { + $item{price} = $sale->{price}; + } + $item{amount} = $sale->{amount}; + push(@items, \%item); + + # We can't buy anymore items + last if @items >= $max_items; + } + + if (!@items) { + error T("There are no items to sell.\n"); + return; + } + + shuffleArray(\@items) if ($config{'buyerShop_random'} eq "1"); + + if (!$char->{skills}{ALL_BUYING_STORE}{lv}) { # don't have skill but have the necessary item + my $item = $char->inventory->getByNameID(12548); + if(!$item) { + error T("You don't have the Buying Store skill or Black Market Bulk Buyer Shop License.\n"); + return; + } else { + $item->use; + } + } elsif(!$char->inventory->getByNameID(6377)) { # have skill but don't have the necessary item + error T("You don't have Bulk Buyer Shop License.\n"); + return; + } else { # have skill and item + my $skill = new Skill(auto => "ALL_BUYING_STORE"); + $messageSender->sendSkillUse($skill->getIDN(), $skill->getLevel(), $char->{ID}); + } + + if (!$buyer_shop{title_line}) { + error T("Your buyer shop does not have a title.\n"); + return; + } + + return @items; +} + +sub openBuyerShop { + my @items = makeBuyerShop(); + my @buyershopnames; + my $limitZeny = 0; + return unless @items; + @buyershopnames = split(/;;/, $buyer_shop{title_line}); + $buyer_shop{title} = $buyershopnames[int rand($#buyershopnames + 1)]; + $buyer_shop{title} = ($config{buyerShopTitleOversize}) ? $buyer_shop{title} : substr($buyer_shop{title},0,36); + + foreach my $item (@items) { + $limitZeny += ($item->{amount} * $item->{price}); + } + + if($limitZeny > $char->{zeny}) { + $limitZeny = $char->{zeny}; + } + + Plugins::callHook ('buyer_open_shop', { + title => $buyer_shop{title}, + limitZeny=> $limitZeny, + items => \@items + }); + $messageSender->sendBuyBulkOpenShop($limitZeny, 1, $buyer_shop{title}, \@items); + + message T("Trying to set up buyer shop...\n"), "vending"; + $buyershopstarted = 1; +} + +sub closeBuyerShop { + if (!$buyershopstarted) { + error T("A Buyer Shop has not been opened.\n"); + return; + } + + $messageSender->sendCloseBuyShop(); + + $buyershopstarted = 0; + $timeout{'ai_shop'}{'time'} = time; + Plugins::callHook('buyer_shop_closed'); + message T("Buyer Shop closed.\n"); +} + +## +# inLockMap() +# +# Returns 1 (true) if character is located in its lockmap. +# Returns 0 (false) if character is not located in lockmap. +sub inLockMap { + if ($field->baseName eq $config{'lockMap'}) { + return 1; + } else { + return 0; + } +} + +sub parseReload { + my ($args) = @_; + eval { + my $progressHandler = sub { + my ($filename) = @_; + message TF("Loading %s...\n", $filename); + }; + if ($args eq 'all') { + Settings::loadAll($progressHandler); + } else { + Settings::loadByRegexp(qr/$args/, $progressHandler); + } + Log::initLogFiles(); + message T("All files were loaded\n"), "reload"; + }; + if (my $e = caught('UTF8MalformedException')) { + error TF( + "The file %s must be valid UTF-8 encoded, which it is \n" . + "currently not. To solve this prolem, please use Notepad\n" . + "to save that file as valid UTF-8.", + $e->textfile); + } elsif ($@) { + die $@; + } +} + +sub MODINIT { + OpenKoreMod::initMisc() if (defined(&OpenKoreMod::initMisc)); +} + +# There are 2 types of clients that receive deletion timestamp 'deleteDate' +# 0: As when char can be deleted +# 1: As remaining time +# -> kRO 2013 clients +# -> idRO since 2016-04-06 +sub setCharDeleteDate { + my ($slot, $deleteDate) = @_; + + return if (!$deleteDate); + + if (!defined $chars[$slot]) { + error TF("Invalid char in specified slot %d\n", $slot); + return; + } + + if ($masterServer->{charDeleteDateType} == 1) { # New clients receive deleteTime as 'time remaining' + $deleteDate = int(time) + $deleteDate; + } + + $chars[$slot]{deleteDate} = getFormattedDate($deleteDate); + $chars[$slot]{deleteDateTimestamp} = $deleteDate; +} + +sub cancelNpcBuySell { + undef %talk; + delete $ai_v{'npc_talk'} if (exists $ai_v{'npc_talk'}); + + if ($in_market) { + $messageSender->sendMarketClose; + $messageSender->sendMarketClose; + undef $in_market; + } elsif ($messageSender->{send_sell_buy_complete}) { + $messageSender->sendSellBuyComplete; + } +} + +sub completeNpcSell { + my $items = shift; + + if (@{$items}) { + $messageSender->sendSellBulk($items); + } + + undef %talk; + delete $ai_v{'npc_talk'} if (exists $ai_v{'npc_talk'}); + + if ($messageSender->{send_sell_buy_complete}) { + $messageSender->sendSellBuyComplete; + $messageSender->sendSellBuyComplete; + } +} + +sub completeNpcBuy { + my $items = shift; + + if (@{$items}) { + if ($in_market) { + $messageSender->sendBuyBulkMarket($items); + } else { + $messageSender->sendBuyBulk($items); + } + } + + undef %talk; + delete $ai_v{'npc_talk'} if (exists $ai_v{'npc_talk'}); + + if ($in_market) { + $messageSender->sendMarketClose; + $messageSender->sendMarketClose; + undef $in_market; + } elsif ($messageSender->{send_sell_buy_complete}) { + $messageSender->sendSellBuyComplete; + $messageSender->sendSellBuyComplete; + } +} + +sub CharacterLogin { + if (charSelectScreen(1) == 1) { + $firstLoginMap = 1; + $startingzeny = $chars[$config{'char'}]{'zeny'} unless defined $startingzeny; + $sentWelcomeMessage = 1; + } +} + +sub searchStoreInfo { + my ($page) = @_; + + my $msg = center(T(" Search Store Result "), 79, '-') ."\n"; + $msg .= TF("Page: %d/%d\n", $page + 1, scalar(@{$universalCatalog{list}})); + $msg .= swrite("@<< @<<<<<<<<<<<<<<<<<<< @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< @<<<<<< @>>>>>>>>>>", + ["#", T("Shop Name"), T("Item"), T("Amount"), T("Price")]); + + for (my $i = 0; $i < scalar(@{${$universalCatalog{list}}[$page]}); ++$i) { + $msg .= swrite("@<< @<<<<<<<<<<<<<<<<<<< @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< @<<<<<< @>>>>>>>>>>", + [$i, ${$universalCatalog{list}}[$page][$i]{shopName}, + itemName({ + nameID => ${$universalCatalog{list}}[$page][$i]{nameID}, + cards => ${$universalCatalog{list}}[$page][$i]{cards_nameID}, + upgrade => ${$universalCatalog{list}}[$page][$i]{refine} + }), ${$universalCatalog{list}}[$page][$i]{amount}, formatNumber(${$universalCatalog{list}}[$page][$i]{price})]); + } + $msg .= ('-'x79) . "\n"; + message $msg, "list"; +} + +my @base62_dictionary = qw(0 1 2 3 4 5 6 7 8 9 a b c d e f g h i j k l m n o p q r s t u v w x y z A B C D E F G H I J K L M N O P Q R S T U V W X Y Z); +my %base62_map = map { $base62_dictionary[$_] => $_ } 0..$#base62_dictionary; + +# Convert number to Base62 string +# [lututui] +sub toBase62 { + my ($k) = @_; + my @result; + + return "0" if ($k == 0); + + while ($k != 0) { + use integer; + unshift (@result, $base62_dictionary[$k % 62]); + $k /= 62; + } + + return join "", @result; +} + +# Convert Base62 string to number +# [lututui] +sub fromBase62 { + my ($k) = @_; + + my @arr = split "", $k; + my $base10 = 0; + + for (my $i = 0; $i < scalar @arr; ++$i) { + $base10 += $base62_map{$arr[$i]} * (62**(scalar(@arr) - $i - 1)); + } + + return $base10; +} + +sub solveMSG { + my $msg = shift; + my $id; + + if ($msg =~ /<MSG>(\d+)<\/MSG>/) { + $id = $1 + 1; + if ($msgTable[$id]) { # show message from msgstringtable.txt + debug "Replace the original message with: '$msgTable[$id]'\n"; + $msg = $msgTable[$id]; + } + } elsif ($msg =~ /<MSG>(\d+),(\d+)<\/MSG>/) { + $id = $1 + 1; + if ($msgTable[$id]) { + debug "Replace the original message with: '$msgTable[$id]'\n"; + $msg = sprintf ($msgTable[$id], $2); + } + } + warning TF("Unknown msgid: %d. Need to update the file msgstringtable.txt (from data.grf)\n", --$id) if !$msgTable[$id]; + Plugins::callHook('solveMSG', { msg => \$msg }); + return $msg; +} + +# Solve each <ITEML>.*</ITEML> to kore-style item name +sub solveItemLink { + my ($itemlstr) = @_; + + # Item ID as minimum requirement + if (!($itemlstr =~ /^([\d\w]+)(.*)/)) { + return $itemlstr; + } + + my ($itemstr, $infostr) = ($1, $2); + my ($loc, $showslots, $id) = $itemstr =~ /([\d\w]{5})(\d)([\d\w]+)/; + my ($refine) = $infostr =~ /%([\d\w]+)/; + my ($itemtype) = $infostr =~ /&([\d\w]+)/; + my $item_info = { + nameID => fromBase62($id), + upgrade => fromBase62($refine), + }; + + foreach my $card (map { $_ } $infostr =~ /\(([\d\w]+)/g) { + $item_info->{cards} .= pack('v', fromBase62($card)); + } + + foreach my $opt (map { $_ } $infostr =~ /\*([\d\w\+,]+)/g) { + # The Random Option's order from client is type-param-value, itemName needs type-value-param + my ($type, $param, $value) = $opt =~ /([a-zA-Z0-9]+)\+([a-zA-Z0-9]+),([a-zA-Z0-9]+)/; + $item_info->{options} .= pack 'vvC', ( fromBase62($type), fromBase62($value), fromBase62($param) ); + } + + return "<".itemName($item_info).">"; +} + +# Solve plain message string that contains client's special functions +sub solveMessage { + my ($msg) = @_; + + # Example: + # <ITEML>.*</ITEML> to readable item name + # Sell<ITEML>0000y1kk&0g)00)00)00)00+05,00-01+0B,00-0m</ITEML>500k + # S><ITEML>0000y1nC&05)00)00+0h,00-0C+0F,00-0h</ITEML><ITEML>000021jM&01)00)00+0h,00-0N+0B,00-0g</ITEML><ITEML>000021jM&01)00)00+0f,00-02+0B,00-0e</ITEML> + if ($msg =~ /<ITEML>([a-zA-Z0-9%&(),+\-*]*)<\/ITEML>/) { + $msg =~ s/<ITEML>([a-zA-Z0-9%&(),+\-*]*)<\/ITEML>/solveItemLink($1)/eg; + } + Plugins::callHook('solveMessage', { msg => \$msg }); + return $msg; +} + +sub absunit { + my ($x) = @_; + if ($x == 0) { + return 0; + } elsif ($x > 0) { + return 1; + } else { + return -1; + } +} + +sub autoNpcTalk { + my ($ID, $nameID) = @_; + + return if (defined AI::findAction("NPC")); + + my $routeIndex = AI::findAction("route"); + return if (defined $routeIndex && AI::args($routeIndex)->getSubtask && UNIVERSAL::isa(AI::args($routeIndex)->getSubtask, 'Task::TalkNPC')); + + my $routeIndex = AI::findAction("route", 1); + return if (defined $routeIndex && AI::args($routeIndex)->getSubtask && UNIVERSAL::isa(AI::args($routeIndex)->getSubtask, 'Task::TalkNPC')); + + debug "An unexpected npc conversation has started, auto-creating a TalkNPC Task\n"; + my $task = Task::TalkNPC->new(type => 'autotalk', nameID => $nameID, ID => $ID); + AI::queue("NPC", $task); + # TODO: The following npc_talk hook is only added on activation. + # Make the task module or AI listen to the hook instead + # and wrap up all the logic. + $task->activate; + Plugins::callHook('npc_autotalk', { + task => $task + }); +} + +sub getFlyWing { + # 12887 - Unlimited Fly Wing + # 23280 - Mosquito Wings (only if lv < 99) + # 23338 - [Event] Fly Wing + # 12323 - Novice Fly Wing + # 601 - Fly Wing + for my $id (12887, 23280, 23338, 12323, 601) { + next if $id == 23280 && $char->{lv} >= 99; + my $item = $char->inventory->getByNameID($id); + return $item if $item; + } + return undef; +} + +sub getButterflyWing { + # 12324 - Novice Butterfly Wing + # 602 - Butterfly Wing + for my $id (12324, 602) { + my $item = $char->inventory->getByNameID($id); + return $item if $item; + } + return undef; +} + +sub getEdenGroupMark { + # 22508 - Eden Group Mark + return $char->inventory->getByNameID(22508); +} + +sub print_callers { + message "[print_callers] Printing start\n"; + my @callers; + my $level = 1; + while (my @info = caller($level)) { + my $sub_name = $info[3] ? $info[3] : "TOP in $info[0]"; + push @callers, { + package => $info[0], + file => $info[1], + line => $info[2], + sub_name => $sub_name, + }; + last if @callers >= 7; + $level++; + } + + message "Last " . scalar(@callers) . " callers:\n"; + for my $i (0 .. $#callers) { + message TF("#%d: %s at %s line %d\n", + $i + 1, + $callers[$i]{sub_name}, + $callers[$i]{file}, + $callers[$i]{line}); + } + message "[print_callers] Printing end\n"; +} + +return 1; diff --git a/openkore_llm_knowledge/core/src/Modules.pm b/openkore_llm_knowledge/core/src/Modules.pm new file mode 100644 index 0000000000..01ce96f677 --- /dev/null +++ b/openkore_llm_knowledge/core/src/Modules.pm @@ -0,0 +1,223 @@ +######################################################################### +# OpenKore - Module Support Code +# +# This software is open source, licensed under the GNU General Public +# License, version 2. Basically, this means that you're allowed to +# modify and distribute this software. However, if you distribute +# modified versions, you MUST also distribute the source code. See +# http://www.gnu.org/licenses/gpl.html for the full license. +######################################################################### +## +# MODULE DESCRIPTION: Module support system +# +# The OpenKore source code is split into various files: openkore.pl, +# functions.pl, and some .pm files. These .pm files are modules: source +# code that's part of OpenKore. Modules implement various subsystems. +# +# One of the features of OpenKore is "dynamic code reloading". This +# means that if you've modified source code, you can reload it at +# runtime, without restarting Kore. +# +# This module, Modules.pm, is what makes it possible. It "glues" all the +# other modules together. openkore.pl registers all the other modules, +# and this modules will save that list in memory. +# +# Modules must put initialization code in a function called MODINIT(). +# This function is called at startup. Initialization code must not be +# put elsewhere, because that code will be called again every time the +# module is reloaded, and will overwrite existing values of variables. +# MODINIT() is only called once at startup (during registration), and is +# never called again. + +package Modules; + +use strict; +use warnings; +use Config; +use FindBin; +use File::Spec; + +our %modules; +our @queue; + + +sub import { + my ($class, $arg) = @_; + if ($arg && $arg eq 'register') { + my ($package) = caller(); + register($package); + } +} + +sub getModuleFilename { + my ($moduleName) = @_; + my @nameParts = split /::/, $moduleName; + my $baseName = File::Spec->join(@nameParts) . ".pm"; + + foreach my $dir (@INC) { + my $file = File::Spec->join($dir, $baseName); + if (-f $file) { + return $file; + } + } + return undef; +} + +sub T { + if (defined &Translation::T && defined &Translation::_translate) { + return &Translation::T; + } else { + return $_[0]; + } +} + +sub TF { + if (defined &Translation::TF && defined &Translation::T && defined &Translation::_translate) { + return &Translation::TF; + } else { + my $format = shift; + return sprintf($format, @_); + } +} + +sub error { + if (defined &Log::error) { + &Log::error; + } else { + print STDERR $_[0]; + } +} + +sub message { + if (defined &Log::message) { + &Log::message; + } else { + print STDERR $_[0]; + } +} + +## +# void Modules::register(names...) +# names: the names of the modules to register. +# +# Register modules. Registered modules can be dynamically reloaded. +# Upon registration, the module's MODINIT() function is called. +# +# Nothing will happen on attempts to re-register an already +# registered module. +# +# Example: +# Modules::register("Log", "Interface"); # Registers Log.pm and Interface.pm +sub register { + no strict 'refs'; + foreach my $module (@_) { + if (!$modules{$module}) { + my $func = UNIVERSAL::can($module, 'MODINIT'); + $func->() if ($func); + $modules{$module} = 1; + } + } +} + +## +# void Modules::addToReloadQueue(String namepart) +# namepart: A part of the name of a registered Perl module. +# +# All registered Perl module whose name contain $namepart will be put into the reload queue. +# Those modules are actually reloaded when Modules::reloadAllInQueue() is called. +sub addToReloadQueue { + my ($namepart) = @_; + my $re = quotemeta $namepart; + foreach my $module (keys %modules) { + if ($module =~ /$re/i) { + my $file = getModuleFilename($module); + if ($file) { + push @queue, $file; + } else { + error(TF("Unable to reload code: %s not found\n", $file)); + } + } + } +} + +sub addToReloadQueue2 { + my ($filename) = @_; + my $file = "$FindBin::RealBin/src/".$filename; + if (-e $file) { + push @queue, $file; + } else { + error(TF("Unable to reload code: %s not found\n", $file)); + } +} + +## +# boolean Modules::checkSyntax(String file) +# +# Check whether the file's syntax is correct. +sub checkSyntax { + my ($file) = @_; + my (undef, undef, $baseName) = File::Spec->splitpath($file); + system($Config{perlpath}, + '-I', "$FindBin::RealBin/src", + '-I', "$FindBin::RealBin/src/deps", + '-c', $file); + if ($? == -1) { + error(TF("Failed to execute %s\n", $Config{perlpath})); + return 0; + } elsif ($? & 127) { + error(TF("%s exited abnormally\n", $Config{perlpath})); + return 0; + } elsif (($? >> 8) == 0) { + message(TF("%s passed syntax check.\n", $baseName), "success"); + return 1; + } else { + error(TF("%s contains syntax errors.\n", $baseName)); + return 0; + } +} + +## +# Modules::reloadFile(String filename) +# +# Executes "do $filename" if $filename exists and does not contain syntax +# errors. This function is used internally by Modules::reloadAllInQueue(), do not +# use this directly. +sub reloadFile { + my ($filename) = @_; + my (undef, undef, $baseName) = File::Spec->splitpath($filename); + + if (!-f $Config{perlpath}) { + error(TF("Cannot find Perl interpreter %s\n", $Config{perlpath})); + return; + } + + message(TF("Checking %s for errors...\n", $filename), "info"); + if (checkSyntax($filename)) { + # Translation Comment: Reloading a Kore's module + message(TF("Reloading %s...\n", $baseName), "info"); + { + package main; + if (!do $filename || $@) { + # Translation Comment: Unable to Reload a Kore's module + error(TF("Unable to reload %s\n", $baseName)); + error("$@\n", "syntax", 1) if ($@); + } + } + # Translation Comment: Kore's module reloaded successfully + message(T("Reloaded.\n"), "success"); + } +} + +## +# void Modules::reloadAllInQueue() +# +# Reload all modules in the reload queue. This function is meant to be run in +# Kore's main loop. Do not call this function directly in any other places. +sub reloadAllInQueue { + while (@queue > 0) { + my $file = shift @queue; + reloadFile($file); + } +} + +1; diff --git a/openkore_llm_knowledge/core/src/Network.pm b/openkore_llm_knowledge/core/src/Network.pm new file mode 100644 index 0000000000..1d3c03e24c --- /dev/null +++ b/openkore_llm_knowledge/core/src/Network.pm @@ -0,0 +1,53 @@ +######################################################################### +# OpenKore - Networking subsystem +# This module contains functions for sending packets to the server. +# +# This software is open source, licensed under the GNU General Public +# License, version 2. +# Basically, this means that you're allowed to modify and distribute +# this software. However, if you distribute modified versions, you MUST +# also distribute the source code. +# See http://www.gnu.org/licenses/gpl.html for the full license. +# +# $Revision$ +# $Id$ +# +######################################################################### +package Network; + +use strict; +use Modules 'register'; + +# $conState contains the connection state: +# 1: Not connected to anything (next step -> connect to master server). +# 2: Connected to master server (next step -> connect to login server) +# 3: Connected to login server (next step -> connect to character server) +# 4: Connected to character server (next step -> connect to map server) +# 5: Connected to map server; ready and functional. +# +# Special states: +# 1.2 (set by $masterServer->{gameGuard} == 2): Wait for the server response allowing us +# to continue login +# 1.3 (set by parseMsg()): The server allowed us to continue logging in, continue +# where we left off +# 1.5 (set by plugins): There is a special sequence for login servers and we must +# wait the plugins to finalize before continuing +# 2.5 (set by parseMsg()): Just passed character selection; next 4 bytes will be +# the account ID + +use constant { + NOT_CONNECTED => 1, + CONNECTED_TO_MASTER_SERVER => 2, + CONNECTED_TO_LOGIN_SERVER => 3, + CONNECTED_TO_CHAR_SERVER => 4, + IN_GAME => 5, + + # This can only happen if we're in XKore or XKoreProxy mode. + # It means that the RO client is already logged in the game + # before OpenKore did. Because of this, OpenKore does not have + # enough information (such as the character's name) to be able to + # work properly. + IN_GAME_BUT_UNINITIALIZED => -1 +}; + +1; diff --git a/openkore_llm_knowledge/core/src/Network/PacketParser.pm b/openkore_llm_knowledge/core/src/Network/PacketParser.pm new file mode 100644 index 0000000000..899e49ea6d --- /dev/null +++ b/openkore_llm_knowledge/core/src/Network/PacketParser.pm @@ -0,0 +1,538 @@ +######################################################################### +# OpenKore - Server message parsing +# +# This software is open source, licensed under the GNU General Public +# License, version 2. +# Basically, this means that you're allowed to modify and distribute +# this software. However, if you distribute modified versions, you MUST +# also distribute the source code. +# See http://www.gnu.org/licenses/gpl.html for the full license. +######################################################################### +## +# MODULE DESCRIPTION: Server message parsing +# +# This class is responsible for parsing messages that are sent by the RO +# server to Kore. Information in the messages are stored in global variables +# (in the module Globals). +# +# Please also read <a href="https://openkore.com/wiki/Network_subsystem">the +# network subsystem overview.</a> +package Network::PacketParser; + +use strict; +use utf8; +use base qw(Exporter); +use Carp::Assert; +use Scalar::Util; +use Time::HiRes qw(time); + +use Globals; +#use Settings; +use Log qw(message warning error debug); +#use FileParsers; +use I18N qw(bytesToString stringToBytes); +use Interface; +use Network; +use Network::MessageTokenizer; +use Misc; +use Plugins; +use Utils; +use Utils::Exceptions; +use Utils::Crypton; +use Translation; + +our @EXPORT = qw( + ACTION_ATTACK ACTION_ITEMPICKUP ACTION_SIT ACTION_STAND + ACTION_ATTACK_NOMOTION ACTION_SPLASH ACTION_SKILL ACTION_ATTACK_REPEAT + ACTION_ATTACK_MULTIPLE ACTION_ATTACK_MULTIPLE_NOMOTION + ACTION_ATTACK_CRITICAL ACTION_ATTACK_LUCKY ACTION_TOUCHSKILL + STATUS_STR STATUS_AGI STATUS_VIT STATUS_INT STATUS_DEX STATUS_LUK +); + +### CATEGORY: Ragnarok Online constants + +use constant { + ACTION_ATTACK => 0x0, + ACTION_ITEMPICKUP => 0x1, # pick up item + ACTION_SIT => 0x2, # sit down + ACTION_STAND => 0x3, # stand up + ACTION_ATTACK_NOMOTION => 0x4, # reflected/absorbed damage? + ACTION_SPLASH => 0x5, + ACTION_SKILL => 0x6, + ACTION_ATTACK_REPEAT => 0x7, + ACTION_ATTACK_MULTIPLE => 0x8, # double attack + ACTION_ATTACK_MULTIPLE_NOMOTION => 0x9, # don't display flinch animation (endure) + ACTION_ATTACK_CRITICAL => 0xa, # critical hit + ACTION_ATTACK_LUCKY => 0xb, # lucky dodge + ACTION_TOUCHSKILL => 0xc, + STATUS_STR => 0x0d, + STATUS_AGI => 0x0e, + STATUS_VIT => 0x0f, + STATUS_INT => 0x10, + STATUS_DEX => 0x11, + STATUS_LUK => 0x12, +}; + +### CATEGORY: Hash members + +## +# Hash* {packet_list} +# +# A list of packet handlers and decoding information. +# +# 'packet switch' => ['handler function', 'unpack string', [qw(argument names)]] + +## +# Hash* {packet_lut} +# +# Lookup table for currently used packet switches. +# Used for constructing packets by handler name. +# +# 'handler function' => 'packet switch' + +###################################### +### CATEGORY: Class methods +###################################### + +# Do not call this directly. Use create() instead. +sub new { + my ($class) = @_; + my $self; + + # If you are wondering about those funny strings like 'x2 v1' read http://perldoc.perl.org/functions/pack.html + # and http://perldoc.perl.org/perlpacktut.html + + $self->{packet_list} = {}; + $self->{packet_lut} = {}; + $self->{bytesProcessed} = 0; + + return bless $self, $class; +} + +## +# Network::PacketParser->create(Network net, String serverType) +# net: An object compatible with the '@MODULE(Network)' class. +# serverType: A server type. +# +# Create a new server message parsing object for the specified server type. +# +# Throws FileNotFoundException, ModuleLoadException. +sub create { + my ($base, $net, $serverType) = @_; + + my ($mode, $type, $param) = Settings::parseServerType ($serverType); + my $class = join '::', $base, $type, (($param) || ()); #param like Thor in bRO_Thor + + debug "[$base] $class ". " (mode: " . ($mode ? "new" : "old") .")\n"; + + undef $@; + eval("use $class;"); + if ($@ =~ /^Can't locate /s) { + FileNotFoundException->throw( + TF("Cannot load server message parser for server type '%s'.", $type) + ); + } elsif ($@) { + ModuleLoadException->throw( + TF("An error occured while loading the server message parser for server type '%s':\n%s", + $type, $@) + ); + } + + my $self = $class->new; + $self->shuffle if $self->can( 'shuffle' ); + + $self->{hook_prefix} = $base; + $self->{net} = $net; + $self->{serverType} = $type; # TODO: eliminate {serverType} from there + Modules::register($class); + + return $self; +} + +### CATEGORY: Methods + +## +# Bytes $packetParser->reconstruct(Hash* args) +# +# Reconstructs a raw packet from $args using {packet_list} and {packet_lut}. +# +# $args->{switch} may contain a packet switch or a handler name. +sub reconstruct { + my ($self, $args) = @_; + + my $switch = $args->{switch}; + unless ($switch =~ /^[0-9A-F]{4}$/) { + # lookup by handler name + unless (exists $self->{packet_lut}{$switch}) { + # alternative (if any) isn't set yet, pick the first available + for (sort {$a cmp $b} keys %{$self->{packet_list}}) { + if ($self->{packet_list}{$_} && $self->{packet_list}{$_}[0] eq $switch) { + $self->{packet_lut}{$switch} = $_; + last; + } + } + } + + $switch = $self->{packet_lut}{$switch} || $switch; + } + + unless ($self->{packet_list}{$switch}) { + die "Can't reconstruct unknown packet: $switch"; + } + + my $packet = $self->{packet_list}{$switch}; + my ($name, $packString, $varNames) = @{$packet}; + + if (my $custom_reconstruct = $self->can('reconstruct_'.$name)) { + $self->$custom_reconstruct($args); + } + + if (DEBUG && $config{debugAssertOnNetwork}) { + # check if all values we're going to pack are defined + for (@$varNames) { + assert(defined $args->{$_}, "Argument $_ should be defined for packet $name"); + } + } + + my $packet = pack("v $packString", hex $switch, $packString && @{$args}{@$varNames}); + + if (exists $rpackets{$switch}) { + if ($rpackets{$switch}{length} > 0) { + # fixed length packet, pad/truncate to the correct length + $packet = pack('a'.(0+$rpackets{$switch}{length}), $packet); + } else { + # variable length packet, store its length in the packet + substr($packet, 2, 2) = pack('v', length $packet); + } + } + + return $packet; +} + +## +# Hash* $packetParser->parse(Bytes msg) +# +# Parses a raw packet using {packet_list}. +# +# Result hashref would contain parsed arguments and the following information: +# `l +# - switch: packet switch +# - RAW_MSG: original message passed +# - RAW_MSG_SIZE: length of original message passed +# - KEYS: list of argument names from {packet_list} +# `l` +sub parse { + my ($self, $msg, $handleContainer, @handleArguments) = @_; + + $lastSwitch = Network::MessageTokenizer::getMessageID($msg); + my $handler = $self->{packet_list}{$lastSwitch}; + + unless ($handler) { + unless (existsInList($config{debugPacket_exclude}, $lastSwitch)) { + warning TF("Packet Parser: Unknown switch: %s\n", $lastSwitch); + Misc::visualDump($msg, "<< Received unknown switch") if $config{debugPacket_unparsed}; + } + return undef; + } + + # $handler->[0] may be (re)binded to $switch here for current serverType + # but all the distinct packets need a distinct names for that, even if they share the handler + # like actor_display = actor_exists + actor_connected + actor_moved + # if (DEBUG) { + # unless ($self->{packet_lut}{$handler->[0]} eq $switch) { + # $self->{packet_lut}{$handler->[0]} = $switch; + # if ((grep { $_ && $_->[0] eq $handler->[0] } values %{$self->{packet_list}}) > 1) { + # warning sprintf "Using %s to provide %s\n", $switch, $handler->[0]; + # } + # } + # } + + debug "Received packet: $lastSwitch Handler: $handler->[0]\n", "packetParser", 2; + + # RAW_MSG is the entire message, including packet switch + my %args = ( + switch => $lastSwitch, + RAW_MSG => $msg, + RAW_MSG_SIZE => length($msg), + KEYS => $handler->[2], + ); + if ($handler->[1]) { + @args{@{$handler->[2]}} = unpack("x2 $handler->[1]", $msg); + } + if (my $custom_parse = $self->can('parse_'.$handler->[0])) { + $self->$custom_parse(\%args); + } + + my $callback = $handleContainer->can($handler->[0]); + if ($callback) { + # Hook names can be made more uniform, + # but the ones for Receive must be kept for compatibility anyway. + # TODO: restrict to $Globals::packetParser and $Globals::messageSender? + if ($self->{hook_prefix} eq 'Network::Receive') { + Plugins::callHook("packet_pre/$handler->[0]", \%args); + } else { + Plugins::callHook("$self->{hook_prefix}/packet_pre/$handler->[0]", \%args); + } + Misc::checkValidity("Packet: " . $handler->[0] . " (pre)"); + + # If return is set in a packet_pre handler, the packet will be ignored. + unless($args{return}) { + $handleContainer->$callback(\%args, @handleArguments); + } + + Misc::checkValidity("Packet: " . $handler->[0]); + } else { + $handleContainer->unhandledMessage(\%args, @handleArguments); + } + + if ($self->{hook_prefix} eq 'Network::Receive') { + Plugins::callHook("packet/$handler->[0]", \%args); + } else { + Plugins::callHook("$self->{hook_prefix}/packet/$handler->[0]", \%args); + } + return \%args; +} + +sub unhandledMessage { + my ($self, $args) = @_; + + warning "Packet Parser: Unhandled Packet: $args->{switch} Handler: $self->{packet_list}{$args->{switch}}[0]\n"; + debug ("Unpacked: " . join(', ', @{$args}{@{$args->{KEYS}}}) . "\n"), "packetParser", 2 if $args->{KEYS}; +} + +## +# boolean $packetParser->willMangle(Bytes messageID) +# messageID: a message ID, such as "008A". +# +# Check whether the message with the specified message ID will be mangled. +# If the bot is running in X-Kore mode, then messages that will be mangled will not +# be sent to the RO client. +# +# By default, a message will never be mangled. Plugins can register mangling procedures +# though. This is done by using the following hooks: +# `l +# - "Network::Receive/willMangle" - This hook has arguments 'messageID' (Bytes) and 'name' (String). +# 'name' is a human-readable description of the message, and may be undef. Plugins +# should set the 'return' argument to 1 if they want willMangle() to return 1. +# - "Network::Receive/mangle" - This hook has arguments 'messageArgs' and 'messageName' (the latter may be undef). +# `l` +# The following example demonstrates how this is done: +# <pre class="example"> +# Plugins::addHook("Network::Receive/willMangle", \&willMangle); +# Plugins::addHook("Network::Receive/mangle", \&mangle); +# +# sub willMangle { +# my (undef, $args) = @_; +# if ($args->{messageID} eq '008A') { +# $args->{return} = 1; +# } +# } +# +# sub mangle { +# my (undef, $args) = @_; +# my $message_args = $args->{messageArgs}; +# if ($message_args->{switch} eq '008A') { +# ...Modify $message_args as necessary.... +# $args->{return} = 2; Modify {return} as necessary.... +# } +# } +# </pre> +# +# You can also mangle packets by defining $args->{mangle} in other plugin hooks. The options avalable are: +# `l +# - 0 = no mangle +# - 1 = mangle (change packet and reconstruct) +# - 2 = drop +# `l` +# The following example will drop all public chat messages: +# <pre class="example"> +# Plugins::addHook("packet_pre/public_chat", \&mangleChat); +# +# sub mangleChat +# { +# my(undef, $args) = @_; +# $args->{mangle} = 2; +# } +# </pre> + +sub willMangle { + my ($self, $messageID) = @_; + if (Plugins::hasHook("$self->{hook_prefix}/willMangle")) { + my $packet = $self->{packet_list}{$messageID}; + my $name; + $name = $packet->[0] if ($packet); + + my %args = ( + messageID => $messageID, + name => $name + ); + Plugins::callHook("$self->{hook_prefix}/willMangle", \%args); + return $args{return}; + } else { + return undef; + } +} + +## +# boolean $packetParser->mangle(Array* args) +# +# Calls the appropriate plugin function to mangle the packet, which +# destructively modifies $args. +# Returns false if the packet should be suppressed. +sub mangle { + my ($self, $args) = @_; + + my %hook_args = (messageArgs => $args); + my $entry = $self->{packet_list}{$args->{switch}}; + if ($entry) { + $hook_args{messageName} = $entry->[0]; + } + + Plugins::callHook("$self->{hook_prefix}/mangle", \%hook_args); + return $hook_args{return}; +} + +sub process { + my ($self, $tokenizer, $handleContainer, @handleArguments) = @_; + + my @result; + my $type; + while (my $message = $tokenizer->readNext(\$type)) { + $handleContainer->{bytesProcessed} += length($message); + $handleContainer->{lastPacketTime} = time; + + my $args; + + if ($type == Network::MessageTokenizer::KNOWN_MESSAGE) { + my $switch = Network::MessageTokenizer::getMessageID($message); + + # FIXME? + $self->parse_pre($handleContainer->{hook_prefix}, $switch, $message, \$message); + + my $willMangle = $handleContainer->can('willMangle') && $handleContainer->willMangle($switch); + + if ($args = $self->parse($message, $handleContainer, @handleArguments)) { + if ($willMangle) { + $args->{mangle} = $handleContainer->mangle($args); + } + } else { + $args = { + switch => $switch, + RAW_MSG => $message, + (mangle => 2) x!! $willMangle, + }; + } + + } elsif ($type == Network::MessageTokenizer::ACCOUNT_ID) { + $args = { + RAW_MSG => $message + }; + + } elsif ($type == Network::MessageTokenizer::UNKNOWN_MESSAGE) { + $args = { + switch => Network::MessageTokenizer::getMessageID($message), + RAW_MSG => $message, + # RAW_MSG_SIZE => length($message), + }; + $handleContainer->unknownMessage($args, @handleArguments); + + } else { + die "Packet Tokenizer: Unknown type: $type"; + } + + unless ($args->{mangle}) { + # Packet was not mangled + push @result, $args->{RAW_MSG}; + #$result .= $args->{RAW_MSG}; + } elsif ($args->{mangle} == 1) { + # Packet was mangled + push @result, $self->reconstruct($args); + #$result .= $self->reconstruct($args); + } else { + # Packet was suppressed + } + } + + # If we're running in X-Kore mode, pass messages back to the RO client. + + # It seems like messages can't be just concatenated safely + # (without "use bytes" pragma or messing with unicode stuff) + # http://perldoc.perl.org/perlunicode.html#The-%22Unicode-Bug%22 + return @result; +} + +sub parse_pre { + my ($self, $mode, $switch, $msg, $realMsg) = @_; + my $values = { + 'Network::Receive' => ['<< Received packet:', 'received', 'Recv', 'parseMsg/pre'], + 'Network::ClientReceive' => ['<< Sent by RO client:', 'ro_sent', 'ROSend', 'RO_sendMsg_pre'], + }->{$mode} or return; + my ($title, $config_suffix, $desc_key, $hook) = @$values; + + if ($config{'debugPacket_'.$config_suffix} && !existsInList($config{'debugPacket_exclude'}, $switch) || + $config{'debugPacket_include_dumpMethod'} && existsInList($config{'debugPacket_include'}, $switch)) + { + #my $label = $packetDescriptions{$desc_key}{$switch} ? " - $packetDescriptions{$desc_key}{$switch}" : ''; + my $label = $rpackets{$switch}{function}?" - ".$rpackets{$switch}{function}:($packetDescriptions{$desc_key}{$switch} ? " - $packetDescriptions{$desc_key}{$switch}" : ''); + if ($config{'debugPacket_'.$config_suffix} == 1) { + debug sprintf("%-24s %-4s%s [%2d bytes]%s\n", $title, $switch, $label, length($msg)), 'parseMsg', 0; + } elsif ($config{'debugPacket_'.$config_suffix} == 2) { + Misc::visualDump($msg, sprintf('%-24s %-4s%s', $title, $switch, $label)); + } + if ($config{debugPacket_include_dumpMethod} == 1) { + debug sprintf("%-24s %-4s%s\n", $title, $switch, $label), "parseMsg", 0; + } elsif ($config{debugPacket_include_dumpMethod} == 2) { + Misc::visualDump($msg, sprintf('%-24s %-4s%s', $title, $switch, $label)); + } elsif ($config{debugPacket_include_dumpMethod} == 3) { + Misc::dumpData($msg, 1); + } elsif ($config{debugPacket_include_dumpMethod} == 4) { + open my $dump, '>>', 'DUMP_LINE.txt'; + print $dump unpack('H*', $msg) . "\n"; + } elsif ($config{debugPacket_include_dumpMethod} == 5) { + open my $dump, '>>', 'DUMP_HEAD.txt'; + print $dump sprintf("%-4s %2d %s%s\n", $switch, length($msg), $desc_key, $label); + } + } + + Plugins::callHook($hook, { + switch => $switch, + msg => $msg, + msg_size => length($msg), + realMsg => $realMsg + }); +} + +sub unknownMessage { + my ($self, $args) = @_; + + # Unknown message - ignore it + unless (existsInList($config{debugPacket_exclude}, $args->{switch})) { + warning TF("Packet Tokenizer: Unknown switch: %s\n", $args->{switch}), 'connection'; + Misc::visualDump($args->{RAW_MSG}, "<< Received unknown packet") if $config{debugPacket_unparsed}; + } + + # Pass it along to the client, whatever it is +} + +# Utility methods used by both Receive and Send + +sub parseChat { + my ($self, $args) = @_; + $args->{message} = bytesToString($args->{message}); + if ($args->{message} =~ /^(.*?)\s{1,2}:\s{1,2}(.*)$/) { + $args->{name} = $1; + $args->{message} = $2; + Misc::stripLanguageCode(\$args->{message}); + } + if (exists $args->{ID}) { + $args->{actor} = Actor::get($args->{ID}); + } +} + +sub reconstructChat { + my ($self, $args) = @_; + $args->{message} = '|00' . $args->{message} if $masterServer->{chatLangCode}; + $args->{message} = stringToBytes($char->{name}) . ' : ' . stringToBytes($args->{message}); +} + +1; diff --git a/openkore_llm_knowledge/core/src/Network/Receive.pm b/openkore_llm_knowledge/core/src/Network/Receive.pm new file mode 100644 index 0000000000..a933913e86 --- /dev/null +++ b/openkore_llm_knowledge/core/src/Network/Receive.pm @@ -0,0 +1,12561 @@ +######################################################################### +# OpenKore - Server message parsing +# +# This software is open source, licensed under the GNU General Public +# License, version 2. +# Basically, this means that you're allowed to modify and distribute +# this software. However, if you distribute modified versions, you MUST +# also distribute the source code. +# See http://www.gnu.org/licenses/gpl.html for the full license. +######################################################################### +## +# MODULE DESCRIPTION: Server message parsing +# +# This class is responsible for parsing messages that are sent by the RO +# server to Kore. Information in the messages are stored in global variables +# (in the module Globals). +# +# Please also read <a href="https://openkore.com/wiki/Network_subsystem">the +# network subsystem overview.</a> +package Network::Receive; + +use strict; +use Time::HiRes qw(time); +use Exporter; +use Network::PacketParser; # import +use base qw(Network::PacketParser); +use utf8; +use Carp::Assert; +use Utils::Assert; +use Scalar::Util; +use Socket qw(inet_aton inet_ntoa); +use Compress::Zlib; + +use AI; +use Globals; +use Field; +#use Settings; +use Log qw(message warning error debug); +use FileParsers qw(updateMonsterLUT updateNPCLUT); +use I18N qw(bytesToString stringToBytes); +use Interface; +use Network; +use Network::MessageTokenizer; +use Misc; +use Plugins; +use Skill; +use Utils; +use Utils::Exceptions; +use Utils::Crypton; +use Translation; +use Actor::Slave::Homunculus; +use Actor::Slave::Mercenary; +use Actor::Slave::Unknown; + +our %EXPORT_TAGS = ( + actor_type => [qw(PC_TYPE NPC_TYPE ITEM_TYPE SKILL_TYPE UNKNOWN_TYPE NPC_MOB_TYPE NPC_EVT_TYPE NPC_PET_TYPE NPC_HO_TYPE NPC_MERSOL_TYPE + NPC_ELEMENTAL_TYPE NPC_TYPE2)], + connection => [qw(REFUSE_INVALID_ID REFUSE_INVALID_PASSWD REFUSE_ID_EXPIRED ACCEPT_ID_PASSWD REFUSE_NOT_CONFIRMED REFUSE_INVALID_VERSION + REFUSE_BLOCK_TEMPORARY REFUSE_BILLING_NOT_READY REFUSE_NONSAKRAY_ID_BLOCKED REFUSE_BAN_BY_DBA + REFUSE_EMAIL_NOT_CONFIRMED REFUSE_BAN_BY_GM REFUSE_TEMP_BAN_FOR_DBWORK REFUSE_SELF_LOCK REFUSE_NOT_PERMITTED_GROUP + REFUSE_WAIT_FOR_SAKRAY_ACTIVE REFUSE_NOT_CHANGED_PASSWD REFUSE_BLOCK_INVALID REFUSE_WARNING REFUSE_NOT_OTP_USER_INFO + REFUSE_OTP_AUTH_FAILED REFUSE_SSO_AUTH_FAILED REFUSE_NOT_ALLOWED_IP_ON_TESTING REFUSE_OVER_BANDWIDTH + REFUSE_OVER_USERLIMIT REFUSE_UNDER_RESTRICTION REFUSE_BY_OUTER_SERVER REFUSE_BY_UNIQUESERVER_CONNECTION + REFUSE_BY_AUTHSERVER_CONNECTION REFUSE_BY_BILLSERVER_CONNECTION REFUSE_BY_AUTH_WAITING REFUSE_DELETED_ACCOUNT + REFUSE_ALREADY_CONNECT REFUSE_TEMP_BAN_HACKING_INVESTIGATION REFUSE_TEMP_BAN_BUG_INVESTIGATION + REFUSE_TEMP_BAN_DELETING_CHAR REFUSE_TEMP_BAN_DELETING_SPOUSE_CHAR REFUSE_USER_PHONE_BLOCK + ACCEPT_LOGIN_USER_PHONE_BLOCK ACCEPT_LOGIN_CHILD REFUSE_IS_NOT_FREEUSER REFUSE_INVALID_ONETIMELIMIT + REFUSE_CHANGE_PASSWD_FORCE REFUSE_OUTOFDATE_PASSWORD REFUSE_NOT_CHANGE_ACCOUNTID REFUSE_NOT_CHANGE_CHARACTERID REFUSE_TOKEN_EXPIRED + REFUSE_SSO_AUTH_BLOCK_USER REFUSE_SSO_AUTH_GAME_APPLY REFUSE_SSO_AUTH_INVALID_GAMENUM REFUSE_SSO_AUTH_INVALID_USER + REFUSE_SSO_AUTH_OTHERS REFUSE_SSO_AUTH_INVALID_AGE REFUSE_SSO_AUTH_INVALID_MACADDRESS REFUSE_SSO_AUTH_BLOCK_ETERNAL + REFUSE_SSO_AUTH_BLOCK_ACCOUNT_STEAL REFUSE_SSO_AUTH_BLOCK_BUG_INVESTIGATION REFUSE_SSO_NOT_PAY_USER + REFUSE_SSO_ALREADY_LOGIN_USER REFUSE_SSO_CURRENT_USED_USER REFUSE_SSO_OTHER_1 REFUSE_SSO_DROP_USER + REFUSE_SSO_NOTHING_USER REFUSE_SSO_OTHER_2 REFUSE_SSO_WRONG_RATETYPE_1 REFUSE_SSO_EXTENSION_PCBANG_TIME + REFUSE_SSO_WRONG_RATETYPE_2 REFUSE_UNKNOWN REFUSE_INVALID_ID2 REFUSE_BLOCKED_ID REFUSE_BLOCKED_COUNTRY REFUSE_INVALID_PASSWD2 + REFUSE_EMAIL_NOT_CONFIRMED2 REFUSE_BILLING REFUSE_BILLING2 REFUSE_WEB REFUSE_CHANGE_PASSWD_FORCE2 REFUSE_SERVER_ERROR + REFUSE_SERVER_ERROR2 REFUSE_SERVER_ERROR3 REFUSE_ACCOUNT_NOT_PREMIUM REFUSE_BAN_ACCOUNT)], + stat_info => [qw(VAR_SPEED VAR_EXP VAR_JOBEXP VAR_VIRTUE VAR_HONOR VAR_HP VAR_MAXHP VAR_SP VAR_MAXSP VAR_POINT VAR_HAIRCOLOR VAR_CLEVEL VAR_SPPOINT + VAR_STR VAR_AGI VAR_VIT VAR_INT VAR_DEX VAR_LUK VAR_JOB VAR_MONEY VAR_SEX VAR_MAXEXP VAR_MAXJOBEXP VAR_WEIGHT VAR_MAXWEIGHT VAR_POISON + VAR_STONE VAR_CURSE VAR_FREEZING VAR_SILENCE VAR_CONFUSION VAR_STANDARD_STR VAR_STANDARD_AGI VAR_STANDARD_VIT VAR_STANDARD_INT + VAR_STANDARD_DEX VAR_STANDARD_LUK VAR_ATTACKMT VAR_ATTACKEDMT VAR_NV_BASIC VAR_ATTPOWER VAR_REFININGPOWER VAR_MAX_MATTPOWER + VAR_MIN_MATTPOWER VAR_ITEMDEFPOWER VAR_PLUSDEFPOWER VAR_MDEFPOWER VAR_PLUSMDEFPOWER VAR_HITSUCCESSVALUE VAR_AVOIDSUCCESSVALUE + VAR_PLUSAVOIDSUCCESSVALUE VAR_CRITICALSUCCESSVALUE VAR_ASPD VAR_PLUSASPD VAR_JOBLEVEL VAR_ACCESSORY2 VAR_ACCESSORY3 VAR_HEADPALETTE + VAR_BODYPALETTE VAR_PKHONOR VAR_CURXPOS VAR_CURYPOS VAR_CURDIR VAR_CHARACTERID VAR_ACCOUNTID VAR_MAPID VAR_MAPNAME VAR_ACCOUNTNAME + VAR_CHARACTERNAME VAR_ITEM_COUNT VAR_ITEM_ITID VAR_ITEM_SLOT1 VAR_ITEM_SLOT2 VAR_ITEM_SLOT3 VAR_ITEM_SLOT4 VAR_HEAD VAR_WEAPON + VAR_ACCESSORY VAR_STATE VAR_MOVEREQTIME VAR_GROUPID VAR_ATTPOWERPLUSTIME VAR_ATTPOWERPLUSPERCENT VAR_DEFPOWERPLUSTIME + VAR_DEFPOWERPLUSPERCENT VAR_DAMAGENOMOTIONTIME VAR_BODYSTATE VAR_HEALTHSTATE VAR_RESETHEALTHSTATE VAR_CURRENTSTATE VAR_RESETEFFECTIVE + VAR_GETEFFECTIVE VAR_EFFECTSTATE VAR_SIGHTABILITYEXPIREDTIME VAR_SIGHTRANGE VAR_SIGHTPLUSATTPOWER VAR_STREFFECTIVETIME + VAR_AGIEFFECTIVETIME VAR_VITEFFECTIVETIME VAR_INTEFFECTIVETIME VAR_DEXEFFECTIVETIME VAR_LUKEFFECTIVETIME VAR_STRAMOUNT VAR_AGIAMOUNT + VAR_VITAMOUNT VAR_INTAMOUNT VAR_DEXAMOUNT VAR_LUKAMOUNT VAR_MAXHPAMOUNT VAR_MAXSPAMOUNT VAR_MAXHPPERCENT VAR_MAXSPPERCENT + VAR_HPACCELERATION VAR_SPACCELERATION VAR_SPEEDAMOUNT VAR_SPEEDDELTA VAR_SPEEDDELTA2 VAR_PLUSATTRANGE VAR_DISCOUNTPERCENT + VAR_AVOIDABLESUCCESSPERCENT VAR_STATUSDEFPOWER VAR_PLUSDEFPOWERINACOLYTE VAR_MAGICITEMDEFPOWER VAR_MAGICSTATUSDEFPOWER VAR_CLASS + VAR_PLUSATTACKPOWEROFITEM VAR_PLUSDEFPOWEROFITEM VAR_PLUSMDEFPOWEROFITEM VAR_PLUSARROWPOWEROFITEM VAR_PLUSATTREFININGPOWEROFITEM + VAR_PLUSDEFREFININGPOWEROFITEM VAR_IDENTIFYNUMBER VAR_ISDAMAGED VAR_ISIDENTIFIED VAR_REFININGLEVEL VAR_WEARSTATE VAR_ISLUCKY + VAR_ATTACKPROPERTY VAR_STORMGUSTCNT VAR_MAGICATKPERCENT VAR_MYMOBCOUNT VAR_ISCARTON VAR_GDID VAR_NPCXSIZE VAR_NPCYSIZE VAR_RACE + VAR_SCALE VAR_PROPERTY VAR_PLUSATTACKPOWEROFITEM_RHAND VAR_PLUSATTACKPOWEROFITEM_LHAND VAR_PLUSATTREFININGPOWEROFITEM_RHAND + VAR_PLUSATTREFININGPOWEROFITEM_LHAND VAR_TOLERACE VAR_ARMORPROPERTY VAR_ISMAGICIMMUNE VAR_ISFALCON VAR_ISRIDING VAR_MODIFIED + VAR_FULLNESS VAR_RELATIONSHIP VAR_ACCESSARY VAR_SIZETYPE VAR_SHOES VAR_STATUSATTACKPOWER VAR_BASICAVOIDANCE VAR_BASICHIT + VAR_PLUSASPDPERCENT VAR_CPARTY VAR_ISMARRIED VAR_ISGUILD VAR_ISFALCONON VAR_ISPECOON VAR_ISPARTYMASTER VAR_ISGUILDMASTER + VAR_BODYSTATENORMAL VAR_HEALTHSTATENORMAL VAR_STUN VAR_SLEEP VAR_UNDEAD VAR_BLIND VAR_BLOODING VAR_BSPOINT VAR_ACPOINT VAR_BSRANK + VAR_ACRANK VAR_CHANGESPEED VAR_CHANGESPEEDTIME VAR_MAGICATKPOWER VAR_MER_KILLCOUNT VAR_MER_FAITH VAR_MDEFPERCENT VAR_CRITICAL_DEF + VAR_ITEMPOWER VAR_MAGICDAMAGEREDUCE VAR_STATUSMAGICPOWER VAR_PLUSMAGICPOWEROFITEM VAR_ITEMMAGICPOWER VAR_NAME VAR_FSMSTATE + VAR_ATTMPOWER VAR_CARTWEIGHT VAR_HP_SELF VAR_SP_SELF VAR_COSTUME_BODY VAR_RESET_COSTUMES + VAR_SP_POW VAR_SP_STA VAR_SP_WIS VAR_SP_SPL VAR_SP_CON VAR_SP_CRT + VAR_SP_PATK VAR_SP_SMATK VAR_SP_RES VAR_SP_MRES VAR_SP_HPLUS VAR_SP_CRATE VAR_SP_TRAITPOINT VAR_SP_AP VAR_SP_MAXAP + VAR_SP_UPOW VAR_SP_USTA VAR_SP_UWIS VAR_SP_USPL VAR_SP_UCON VAR_SP_UCRT)], + party_invite => [qw(ANSWER_ALREADY_OTHERGROUPM ANSWER_JOIN_REFUSE ANSWER_JOIN_ACCEPT ANSWER_MEMBER_OVERSIZE ANSWER_DUPLICATE + ANSWER_JOINMSG_REFUSE ANSWER_UNKNOWN_ERROR ANSWER_UNKNOWN_CHARACTER ANSWER_INVALID_MAPPROPERTY)], + party_leave => [qw(GROUPMEMBER_DELETE_LEAVE GROUPMEMBER_DELETE_EXPEL)], + exp_origin => [qw(EXP_FROM_BATTLE EXP_FROM_QUEST)], +); + +our @EXPORT = ( + @{$EXPORT_TAGS{actor_type}}, + @{$EXPORT_TAGS{connection}}, + @{$EXPORT_TAGS{stat_info}}, + @{$EXPORT_TAGS{party_invite}}, + @{$EXPORT_TAGS{party_leave}}, + @{$EXPORT_TAGS{exp_origin}}, +); + +# object_type constants for &actor_display +use constant { + PC_TYPE => 0x0, + NPC_TYPE => 0x1, + ITEM_TYPE => 0x2, + SKILL_TYPE => 0x3, + UNKNOWN_TYPE => 0x4, + NPC_MOB_TYPE => 0x5, + NPC_EVT_TYPE => 0x6, + NPC_PET_TYPE => 0x7, + NPC_HO_TYPE => 0x8, + NPC_MERSOL_TYPE => 0x9, + NPC_ELEMENTAL_TYPE => 0xa, + NPC_TYPE2 => 0xc, +}; + +# connection +use constant { + REFUSE_INVALID_ID => 0x0, + REFUSE_INVALID_PASSWD => 0x1, + REFUSE_ID_EXPIRED => 0x2, + ACCEPT_ID_PASSWD => 0x3, + REFUSE_NOT_CONFIRMED => 0x4, + REFUSE_INVALID_VERSION => 0x5, + REFUSE_BLOCK_TEMPORARY => 0x6, + REFUSE_BILLING_NOT_READY => 0x7, + REFUSE_NONSAKRAY_ID_BLOCKED => 0x8, + REFUSE_BAN_BY_DBA => 0x9, + REFUSE_EMAIL_NOT_CONFIRMED => 0xa, + REFUSE_BAN_BY_GM => 0xb, + REFUSE_TEMP_BAN_FOR_DBWORK => 0xc, + REFUSE_SELF_LOCK => 0xd, + REFUSE_NOT_PERMITTED_GROUP => 0xe, + REFUSE_WAIT_FOR_SAKRAY_ACTIVE => 0xf, + REFUSE_NOT_CHANGED_PASSWD => 0x10, + REFUSE_BLOCK_INVALID => 0x11, + REFUSE_WARNING => 0x12, + REFUSE_NOT_OTP_USER_INFO => 0x13, + REFUSE_OTP_AUTH_FAILED => 0x14, + REFUSE_SSO_AUTH_FAILED => 0x15, + REFUSE_NOT_ALLOWED_IP_ON_TESTING => 0x16, + REFUSE_OVER_BANDWIDTH => 0x17, + REFUSE_OVER_USERLIMIT => 0x18, + REFUSE_UNDER_RESTRICTION => 0x19, + REFUSE_BY_OUTER_SERVER => 0x1a, + REFUSE_BY_UNIQUESERVER_CONNECTION => 0x1b, + REFUSE_BY_AUTHSERVER_CONNECTION => 0x1c, + REFUSE_BY_BILLSERVER_CONNECTION => 0x1d, + REFUSE_BY_AUTH_WAITING => 0x1e, + REFUSE_DELETED_ACCOUNT => 0x63, + REFUSE_ALREADY_CONNECT => 0x64, + REFUSE_TEMP_BAN_HACKING_INVESTIGATION => 0x65, + REFUSE_TEMP_BAN_BUG_INVESTIGATION => 0x66, + REFUSE_TEMP_BAN_DELETING_CHAR => 0x67, + REFUSE_TEMP_BAN_DELETING_SPOUSE_CHAR => 0x68, + REFUSE_USER_PHONE_BLOCK => 0x69, + ACCEPT_LOGIN_USER_PHONE_BLOCK => 0x6a, + ACCEPT_LOGIN_CHILD => 0x6b, + REFUSE_IS_NOT_FREEUSER => 0x6c, + REFUSE_INVALID_ONETIMELIMIT => 0x6d, + REFUSE_CHANGE_PASSWD_FORCE => 0x6e, + REFUSE_OUTOFDATE_PASSWORD => 0x6f, + REFUSE_NOT_CHANGE_ACCOUNTID => 0xf0, + REFUSE_NOT_CHANGE_CHARACTERID => 0xf1, + REFUSE_TOKEN_EXPIRED => 0xf3, + REFUSE_SSO_AUTH_BLOCK_USER => 0x1394, + REFUSE_SSO_AUTH_GAME_APPLY => 0x1395, + REFUSE_SSO_AUTH_INVALID_GAMENUM => 0x1396, + REFUSE_SSO_AUTH_INVALID_USER => 0x1397, + REFUSE_SSO_AUTH_OTHERS => 0x1398, + REFUSE_SSO_AUTH_INVALID_AGE => 0x1399, + REFUSE_SSO_AUTH_INVALID_MACADDRESS => 0x139a, + REFUSE_SSO_AUTH_BLOCK_ETERNAL => 0x13c6, + REFUSE_SSO_AUTH_BLOCK_ACCOUNT_STEAL => 0x13c7, + REFUSE_SSO_AUTH_BLOCK_BUG_INVESTIGATION => 0x13c8, + REFUSE_SSO_NOT_PAY_USER => 0x13ba, + REFUSE_SSO_ALREADY_LOGIN_USER => 0x13bb, + REFUSE_SSO_CURRENT_USED_USER => 0x13bc, + REFUSE_SSO_OTHER_1 => 0x13bd, + REFUSE_SSO_DROP_USER => 0x13be, + REFUSE_SSO_NOTHING_USER => 0x13bf, + REFUSE_SSO_OTHER_2 => 0x13c0, + REFUSE_SSO_WRONG_RATETYPE_1 => 0x13c1, + REFUSE_SSO_EXTENSION_PCBANG_TIME => 0x13c2, + REFUSE_SSO_WRONG_RATETYPE_2 => 0x13c3, + REFUSE_BAN_ACCOUNT => 0x13c6, + + # 0x0AE0 + REFUSE_UNKNOWN => 0x1450, + REFUSE_INVALID_ID2 => 0x1451, + REFUSE_BLOCKED_ID => 0x1452, + REFUSE_BLOCKED_COUNTRY => 0x1453, + REFUSE_INVALID_PASSWD2 => 0x1454, + REFUSE_EMAIL_NOT_CONFIRMED2 => 0x1455, + REFUSE_BILLING => 0x1456, + REFUSE_WEB => 0x1457, + REFUSE_BILLING2 => 0x1458, + REFUSE_CHANGE_PASSWD_FORCE2 => 0x1459, + REFUSE_SERVER_ERROR => 0x145A, + REFUSE_SERVER_ERROR2 => 0x145B, + REFUSE_SERVER_ERROR3 => 0x145C, + REFUSE_ACCOUNT_NOT_PREMIUM => 0x14B5, +}; + +# stat_info +use constant { + VAR_SPEED => 0x0, + VAR_EXP => 0x1, + VAR_JOBEXP => 0x2, + VAR_VIRTUE => 0x3, + VAR_HONOR => 0x4, + VAR_HP => 0x5, + VAR_MAXHP => 0x6, + VAR_SP => 0x7, + VAR_MAXSP => 0x8, + VAR_POINT => 0x9, + VAR_HAIRCOLOR => 0xa, + VAR_CLEVEL => 0xb, + VAR_SPPOINT => 0xc, + VAR_STR => 0xd, + VAR_AGI => 0xe, + VAR_VIT => 0xf, + VAR_INT => 0x10, + VAR_DEX => 0x11, + VAR_LUK => 0x12, + VAR_JOB => 0x13, + VAR_MONEY => 0x14, + VAR_SEX => 0x15, + VAR_MAXEXP => 0x16, + VAR_MAXJOBEXP => 0x17, + VAR_WEIGHT => 0x18, + VAR_MAXWEIGHT => 0x19, + VAR_POISON => 0x1a, + VAR_STONE => 0x1b, + VAR_CURSE => 0x1c, + VAR_FREEZING => 0x1d, + VAR_SILENCE => 0x1e, + VAR_CONFUSION => 0x1f, + VAR_STANDARD_STR => 0x20, + VAR_STANDARD_AGI => 0x21, + VAR_STANDARD_VIT => 0x22, + VAR_STANDARD_INT => 0x23, + VAR_STANDARD_DEX => 0x24, + VAR_STANDARD_LUK => 0x25, + VAR_ATTACKMT => 0x26, + VAR_ATTACKEDMT => 0x27, + VAR_NV_BASIC => 0x28, + VAR_ATTPOWER => 0x29, + VAR_REFININGPOWER => 0x2a, + VAR_MAX_MATTPOWER => 0x2b, + VAR_MIN_MATTPOWER => 0x2c, + VAR_ITEMDEFPOWER => 0x2d, + VAR_PLUSDEFPOWER => 0x2e, + VAR_MDEFPOWER => 0x2f, + VAR_PLUSMDEFPOWER => 0x30, + VAR_HITSUCCESSVALUE => 0x31, + VAR_AVOIDSUCCESSVALUE => 0x32, + VAR_PLUSAVOIDSUCCESSVALUE => 0x33, + VAR_CRITICALSUCCESSVALUE => 0x34, + VAR_ASPD => 0x35, + VAR_PLUSASPD => 0x36, + VAR_JOBLEVEL => 0x37, + VAR_ACCESSORY2 => 0x38, + VAR_ACCESSORY3 => 0x39, + VAR_HEADPALETTE => 0x3a, + VAR_BODYPALETTE => 0x3b, + VAR_PKHONOR => 0x3c, + VAR_CURXPOS => 0x3d, + VAR_CURYPOS => 0x3e, + VAR_CURDIR => 0x3f, + VAR_CHARACTERID => 0x40, + VAR_ACCOUNTID => 0x41, + VAR_MAPID => 0x42, + VAR_MAPNAME => 0x43, + VAR_ACCOUNTNAME => 0x44, + VAR_CHARACTERNAME => 0x45, + VAR_ITEM_COUNT => 0x46, + VAR_ITEM_ITID => 0x47, + VAR_ITEM_SLOT1 => 0x48, + VAR_ITEM_SLOT2 => 0x49, + VAR_ITEM_SLOT3 => 0x4a, + VAR_ITEM_SLOT4 => 0x4b, + VAR_HEAD => 0x4c, + VAR_WEAPON => 0x4d, + VAR_ACCESSORY => 0x4e, + VAR_STATE => 0x4f, + VAR_MOVEREQTIME => 0x50, + VAR_GROUPID => 0x51, + VAR_ATTPOWERPLUSTIME => 0x52, + VAR_ATTPOWERPLUSPERCENT => 0x53, + VAR_DEFPOWERPLUSTIME => 0x54, + VAR_DEFPOWERPLUSPERCENT => 0x55, + VAR_DAMAGENOMOTIONTIME => 0x56, + VAR_BODYSTATE => 0x57, + VAR_HEALTHSTATE => 0x58, + VAR_RESETHEALTHSTATE => 0x59, + VAR_CURRENTSTATE => 0x5a, + VAR_RESETEFFECTIVE => 0x5b, + VAR_GETEFFECTIVE => 0x5c, + VAR_EFFECTSTATE => 0x5d, + VAR_SIGHTABILITYEXPIREDTIME => 0x5e, + VAR_SIGHTRANGE => 0x5f, + VAR_SIGHTPLUSATTPOWER => 0x60, + VAR_STREFFECTIVETIME => 0x61, + VAR_AGIEFFECTIVETIME => 0x62, + VAR_VITEFFECTIVETIME => 0x63, + VAR_INTEFFECTIVETIME => 0x64, + VAR_DEXEFFECTIVETIME => 0x65, + VAR_LUKEFFECTIVETIME => 0x66, + VAR_STRAMOUNT => 0x67, + VAR_AGIAMOUNT => 0x68, + VAR_VITAMOUNT => 0x69, + VAR_INTAMOUNT => 0x6a, + VAR_DEXAMOUNT => 0x6b, + VAR_LUKAMOUNT => 0x6c, + VAR_MAXHPAMOUNT => 0x6d, + VAR_MAXSPAMOUNT => 0x6e, + VAR_MAXHPPERCENT => 0x6f, + VAR_MAXSPPERCENT => 0x70, + VAR_HPACCELERATION => 0x71, + VAR_SPACCELERATION => 0x72, + VAR_SPEEDAMOUNT => 0x73, + VAR_SPEEDDELTA => 0x74, + VAR_SPEEDDELTA2 => 0x75, + VAR_PLUSATTRANGE => 0x76, + VAR_DISCOUNTPERCENT => 0x77, + VAR_AVOIDABLESUCCESSPERCENT => 0x78, + VAR_STATUSDEFPOWER => 0x79, + VAR_PLUSDEFPOWERINACOLYTE => 0x7a, + VAR_MAGICITEMDEFPOWER => 0x7b, + VAR_MAGICSTATUSDEFPOWER => 0x7c, + VAR_CLASS => 0x7d, + VAR_PLUSATTACKPOWEROFITEM => 0x7e, + VAR_PLUSDEFPOWEROFITEM => 0x7f, + VAR_PLUSMDEFPOWEROFITEM => 0x80, + VAR_PLUSARROWPOWEROFITEM => 0x81, + VAR_PLUSATTREFININGPOWEROFITEM => 0x82, + VAR_PLUSDEFREFININGPOWEROFITEM => 0x83, + VAR_IDENTIFYNUMBER => 0x84, + VAR_ISDAMAGED => 0x85, + VAR_ISIDENTIFIED => 0x86, + VAR_REFININGLEVEL => 0x87, + VAR_WEARSTATE => 0x88, + VAR_ISLUCKY => 0x89, + VAR_ATTACKPROPERTY => 0x8a, + VAR_STORMGUSTCNT => 0x8b, + VAR_MAGICATKPERCENT => 0x8c, + VAR_MYMOBCOUNT => 0x8d, + VAR_ISCARTON => 0x8e, + VAR_GDID => 0x8f, + VAR_NPCXSIZE => 0x90, + VAR_NPCYSIZE => 0x91, + VAR_RACE => 0x92, + VAR_SCALE => 0x93, + VAR_PROPERTY => 0x94, + VAR_PLUSATTACKPOWEROFITEM_RHAND => 0x95, + VAR_PLUSATTACKPOWEROFITEM_LHAND => 0x96, + VAR_PLUSATTREFININGPOWEROFITEM_RHAND => 0x97, + VAR_PLUSATTREFININGPOWEROFITEM_LHAND => 0x98, + VAR_TOLERACE => 0x99, + VAR_ARMORPROPERTY => 0x9a, + VAR_ISMAGICIMMUNE => 0x9b, + VAR_ISFALCON => 0x9c, + VAR_ISRIDING => 0x9d, + VAR_MODIFIED => 0x9e, + VAR_FULLNESS => 0x9f, + VAR_RELATIONSHIP => 0xa0, + VAR_ACCESSARY => 0xa1, + VAR_SIZETYPE => 0xa2, + VAR_SHOES => 0xa3, + VAR_STATUSATTACKPOWER => 0xa4, + VAR_BASICAVOIDANCE => 0xa5, + VAR_BASICHIT => 0xa6, + VAR_PLUSASPDPERCENT => 0xa7, + VAR_CPARTY => 0xa8, + VAR_ISMARRIED => 0xa9, + VAR_ISGUILD => 0xaa, + VAR_ISFALCONON => 0xab, + VAR_ISPECOON => 0xac, + VAR_ISPARTYMASTER => 0xad, + VAR_ISGUILDMASTER => 0xae, + VAR_BODYSTATENORMAL => 0xaf, + VAR_HEALTHSTATENORMAL => 0xb0, + VAR_STUN => 0xb1, + VAR_SLEEP => 0xb2, + VAR_UNDEAD => 0xb3, + VAR_BLIND => 0xb4, + VAR_BLOODING => 0xb5, + VAR_BSPOINT => 0xb6, + VAR_ACPOINT => 0xb7, + VAR_BSRANK => 0xb8, + VAR_ACRANK => 0xb9, + VAR_CHANGESPEED => 0xba, + VAR_CHANGESPEEDTIME => 0xbb, + VAR_MAGICATKPOWER => 0xbc, + VAR_MER_KILLCOUNT => 0xbd, + VAR_MER_FAITH => 0xbe, + VAR_MDEFPERCENT => 0xbf, + VAR_CRITICAL_DEF => 0xc0, + VAR_ITEMPOWER => 0xc1, + VAR_MAGICDAMAGEREDUCE => 0xc2, + VAR_STATUSMAGICPOWER => 0xc3, + VAR_PLUSMAGICPOWEROFITEM => 0xc4, + VAR_ITEMMAGICPOWER => 0xc5, + VAR_NAME => 0xc6, + VAR_FSMSTATE => 0xc7, + VAR_ATTMPOWER => 0xc8, + VAR_CARTWEIGHT => 0xc9, + VAR_HP_SELF => 0xca, + VAR_SP_SELF => 0xcb, + VAR_COSTUME_BODY => 0xcc, + VAR_RESET_COSTUMES => 0xcd, + VAR_SP_POW => 0xdb, + VAR_SP_STA => 0xdc, + VAR_SP_WIS => 0xdd, + VAR_SP_SPL => 0xde, + VAR_SP_CON => 0xdf, + VAR_SP_CRT => 0xe0, + VAR_SP_PATK => 0xe1, + VAR_SP_SMATK => 0xe2, + VAR_SP_RES => 0xe3, + VAR_SP_MRES => 0xe4, + VAR_SP_HPLUS => 0xe5, + VAR_SP_CRATE => 0xe6, + VAR_SP_TRAITPOINT => 0xe7, + VAR_SP_AP => 0xe8, + VAR_SP_MAXAP => 0xe9, + VAR_SP_UPOW => 0xf7, + VAR_SP_USTA => 0xf8, + VAR_SP_UWIS => 0xf9, + VAR_SP_USPL => 0xfa, + VAR_SP_UCON => 0xfb, + VAR_SP_UCRT => 0xfc, +}; + +# party invite result +use constant { + ANSWER_ALREADY_OTHERGROUPM => 0x0, + ANSWER_JOIN_REFUSE => 0x1, + ANSWER_JOIN_ACCEPT => 0x2, + ANSWER_MEMBER_OVERSIZE => 0x3, + ANSWER_DUPLICATE => 0x4, + ANSWER_JOINMSG_REFUSE => 0x5, + ANSWER_UNKNOWN_ERROR => 0x6, + ANSWER_UNKNOWN_CHARACTER => 0x7, + ANSWER_INVALID_MAPPROPERTY => 0x8, +}; + +# party leave result +use constant { + GROUPMEMBER_DELETE_LEAVE => 0x0, + GROUPMEMBER_DELETE_EXPEL => 0x1, +}; + +# item list type +use constant { + INVTYPE_INVENTORY => 0x0, + INVTYPE_CART => 0x1, + INVTYPE_STORAGE => 0x2, + INVTYPE_GUILD_STORAGE => 0x3, +}; + +# exp origin +use constant { + EXP_FROM_BATTLE => 0x0, + EXP_FROM_QUEST => 0x1, +}; + +# client UI types +use constant { + BANK_UI => 0x0, + STYLIST_UI => 0x1, + CAPTCHA_UI => 0x2, + MACRO_UI => 0x3, + UI_UNUSED => 0x4, + TIPBOX_UI => 0x5, + RENEWQUEST_UI => 0x6, + ATTENDANCE_UI => 0x7, +}; + +use constant { + LEVELUP_EFFECT => 0x0, + JOBLEVELUP_EFFECT => 0x1, + REFINING_FAIL_EFFECT => 0x2, + REFINING_SUCCESS_EFFECT => 0x3, + GAME_OVER_EFFECT => 0x4, + MAKEITEM_AM_SUCCESS_EFFECT => 0x5, + MAKEITEM_AM_FAIL_EFFECT => 0x6, + LEVELUP_EFFECT2 => 0x7, + JOBLEVELUP_EFFECT2 => 0x8, + LEVELUP_EFFECT3 => 0x9, +}; + +# market buy item result +use constant { + MARKET_BUY_RESULT_ERROR => 0xffff, # -1 + MARKET_BUY_RESULT_SUCCESS => 0, + MARKET_BUY_RESULT_NO_ZENY => 1, + MARKET_BUY_RESULT_OVER_WEIGHT => 2, + MARKET_BUY_RESULT_OUT_OF_SPACE => 3, + MARKET_BUY_RESULT_AMOUNT_TOO_BIG => 9, +}; + +# misc configurations +use constant { + CONFIG_OPEN_EQUIPMENT_WINDOW => 0, + CONFIG_CALL => 1, + CONFIG_PET_AUTOFEED => 2, + CONFIG_HOMUNCULUS_AUTOFEED => 3, +}; + +#expand_inventory_result +use constant { + EXPAND_INVENTORY_RESULT_SUCCESS => 0x0, + EXPAND_INVENTORY_RESULT_FAILED => 0x1, + EXPAND_INVENTORY_RESULT_OTHER_WORK => 0x2, + EXPAND_INVENTORY_RESULT_MISSING_ITEM => 0x3, + EXPAND_INVENTORY_RESULT_MAX_SIZE => 0x4, +}; + +# macro detector ui +use constant { + MCD_TIMEOUT => 0, + MCD_INCORRECT => 1, + MCD_GOOD => 2, +}; + +use constant { + MCR_MONITORING => 0, + MCR_NO_DATA => 1, + MCR_INPROGRESS => 2, +}; + +# dynamicnpc_create_result +use constant { + DYNAMICNPC_RESULT_SUCCESS => 0x0, + DYNAMICNPC_RESULT_UNKNOWN => 0x1, + DYNAMICNPC_RESULT_UNKNOWNNPC => 0x2, + DYNAMICNPC_RESULT_DUPLICATE => 0x3, + DYNAMICNPC_RESULT_OUTOFTIME => 0x4 +}; + +# Display gained exp. +# 07F6 <account id>.L <amount>.L <var id>.W <exp type>.W (ZC_NOTIFY_EXP) +# 0ACC <account id>.L <amount>.Q <var id>.W <exp type>.W (ZC_NOTIFY_EXP2) +# amount: INT32_MIN ~ INT32_MAX +# var id: +# SP_BASEEXP, SP_JOBEXP +# exp type: +# 0 = normal exp gained/lost +# 1 = quest exp gained/lost +# 07F6 (exp) doesn't change any exp information because 00B1 (exp_zeny_info) is always sent with it +sub exp { + my ($self, $args) = @_; + + my $max = {VAR_EXP, $char->{exp_max}, VAR_JOBEXP, $char->{exp_job_max}}->{$args->{type}}; + $args->{percent} = $max ? $args->{val} / $max * 100 : 0; + + if ($args->{flag} == EXP_FROM_BATTLE) { + if ($args->{type} == VAR_EXP) { + message TF("Base Exp gained: %d (%.2f%%)\n", @{$args}{qw(val percent)}), 'exp2', 2; + } elsif ($args->{type} == VAR_JOBEXP) { + message TF("Job Exp gained: %d (%.2f%%)\n", @{$args}{qw(val percent)}), 'exp2', 2; + } else { + message TF("Unknown (type=%d) Exp gained: %d\n", @{$args}{qw(type val)}), 'exp2', 2; + } + } elsif ($args->{flag} == EXP_FROM_QUEST) { + if ($args->{type} == VAR_EXP) { + message TF("Base Quest Exp gained: %d (%.2f%%)\n", @{$args}{qw(val percent)}), 'exp2', 2; + } elsif ($args->{type} == VAR_JOBEXP) { + message TF("Job Quest Exp gained: %d (%.2f%%)\n", @{$args}{qw(val percent)}), 'exp2', 2; + } else { + message TF("Unknown (type=%d) Quest Exp gained: %d\n", @{$args}{qw(type val)}), 'exp2', 2; + } + } else { + if ($args->{type} == VAR_EXP) { + message TF("Base Unknown (flag=%d) Exp gained: %d (%.2f%%)\n", @{$args}{qw(flag val percent)}), 'exp2', 2; + } elsif ($args->{type} == VAR_JOBEXP) { + message TF("Job Unknown (flag=%d) Exp gained: %d (%.2f%%)\n", @{$args}{qw(flag val percent)}), 'exp2', 2; + } else { + message TF("Unknown (type=%d) Unknown (flag=%d) Exp gained: %d\n", @{$args}{qw(type flag val)}), 'exp2', 2; + } + } +} + +###################################### +### CATEGORY: Class methods +###################################### + +# Just a wrapper for SUPER::parse. +sub parse { + my $self = shift; + my $args = $self->SUPER::parse(@_); + + if ($args && $config{debugPacket_received} == 3 && + existsInList($config{'debugPacket_include'}, $args->{switch})) { + my $packet = $self->{packet_list}{$args->{switch}}; + my ($name, $packString, $varNames) = @{$packet}; + + my @vars = (); + for my $varName (@{$varNames}) { + message "$varName = $args->{$varName}\n"; + } + } + + return $args; +} + +####################################### +### CATEGORY: Private class methods +####################################### + +## +# int Network::Receive::queryLoginPinCode([String message]) +# Returns: login PIN code, or undef if cancelled +# Ensures: length(result) in 4..8 +# +# Request login PIN code from user. +sub queryLoginPinCode { + my $message = $_[0] || T("You've never set a login PIN code before.\nPlease enter a new login PIN code:"); + do { + my $input = $interface->query($message, isPassword => 1,); + if (!defined($input)) { + quit(); + return; + } else { + if ($input !~ /^\d+$/) { + $interface->errorDialog(T("The PIN code may only contain digits.")); + } elsif ((length($input) <= 3) || (length($input) >= 9)) { + $interface->errorDialog(T("The PIN code must be between 4 and 9 characters.")); + } else { + return $input; + } + } + } while (1); +} + +## +# boolean Network::Receive->queryAndSaveLoginPinCode([String message]) +# Returns: true on success +# +# Request login PIN code from user and save it in config. +sub queryAndSaveLoginPinCode { + my ($self, $message) = @_; + my $pin = queryLoginPinCode($message); + if (defined $pin) { + configModify('loginPinCode', $pin, silent => 1); + return 1; + } else { + return 0; + } +} + +sub changeToInGameState { + if ($net->version() == 1) { + if ($accountID && UNIVERSAL::isa($char, 'Actor::You')) { + if ($net->getState() != Network::IN_GAME) { + $net->setState(Network::IN_GAME); + } + return 1; + } else { + if ($net->getState() != Network::IN_GAME_BUT_UNINITIALIZED) { + $net->setState(Network::IN_GAME_BUT_UNINITIALIZED); + if ($config{verbose} && $messageSender && !$sentWelcomeMessage) { + $messageSender->injectAdminMessage("Please relogin to enable X-${Settings::NAME}."); + $sentWelcomeMessage = 1; + } + } + return 0; + } + } else { + return 1; + } +} + +### Packet inner struct handlers + +# The block size in the received_characters packet varies from server to server. +# This method may be overrided in other ServerType handlers to return +# the correct block size. +sub received_characters_blockSize { + if ($masterServer && $masterServer->{charBlockSize}) { + return $masterServer->{charBlockSize}; + } else { + # last change: 2020-11-13 + # default in kRO, most of official servers and emulators (rAthena, Hercules) + return 155; + } +} + +# The length must exactly match charBlockSize, as it's used to construct packets. +sub received_characters_unpackString { + my $char_info; + for ($masterServer && $masterServer->{charBlockSize}) { + if ($_ == 175) { # PACKETVER >= 20201007 [hp, hp_max, sp and sp_max are now uint64] + $char_info = { + types => 'a4 V2 V V2 V6 v V2 V2 V2 V2 v2 V v9 Z24 C8 v Z16 V4 C', + keys => [qw(charID exp exp_2 zeny exp_job exp_job_2 lv_job body_state health_state effect_state stance manner status_point hp hp_2 hp_max hp_max_2 sp sp_2 sp_max sp_max_2 walkspeed jobID hair_style weapon lv skill_point head_bottom shield head_top head_mid hair_pallete clothes_color name str agi vit int dex luk slot hair_color is_renamed last_map delete_date robe slot_addon rename_addon sex)], + }; + } elsif ($_ == 155) { # PACKETVER >= 20170830 [base and job exp are now uint64] + $char_info = { + types => 'a4 V2 V V2 V6 v V2 v4 V v9 Z24 C8 v Z16 V4 C', + keys => [qw(charID exp exp_2 zeny exp_job exp_job_2 lv_job body_state health_state effect_state stance manner status_point hp hp_max sp sp_max walkspeed jobID hair_style weapon lv skill_point head_bottom shield head_top head_mid hair_pallete clothes_color name str agi vit int dex luk slot hair_color is_renamed last_map delete_date robe slot_addon rename_addon sex)], + }; + + } elsif ($_ == 147) { # PACKETVER >= 20141022 [iRO Doram Update, walk_speed is now long] + $char_info = { + types => 'a4 V9 v V2 v4 V v9 Z24 C8 v Z16 V4 C', + keys => [qw(charID exp zeny exp_job lv_job body_state health_state effect_state stance manner status_point hp hp_max sp sp_max walkspeed jobID hair_style weapon lv skill_point head_bottom shield head_top head_mid hair_pallete clothes_color name str agi vit int dex luk slot hair_color is_renamed last_map delete_date robe slot_addon rename_addon sex)], + }; + + } elsif ($_ == 146) { # equal to charblocksize 147, but not added sex. (Sep, 2019) + $char_info = { + types => 'a4 V9 v V2 v4 V v9 Z24 C8 v Z16 V4', + keys => [qw(charID exp zeny exp_job lv_job body_state health_state effect_state stance manner status_point hp hp_max sp sp_max walkspeed jobID hair_style weapon lv skill_point head_bottom shield head_top head_mid hair_pallete clothes_color name str agi vit int dex luk slot hair_color is_renamed last_map delete_date robe slot_addon rename_addon)], + }; + + } elsif ($_ == 145) { # PACKETVER >= 20141016 [support to double sex account] + $char_info = { + types => 'a4 V9 v V2 v14 Z24 C8 v Z16 V4 C', + keys => [qw(charID exp zeny exp_job lv_job body_state health_state effect_state stance manner status_point hp hp_max sp sp_max walkspeed jobID hair_style weapon lv skill_point head_bottom shield head_top head_mid hair_pallete clothes_color name str agi vit int dex luk slot hair_color is_renamed last_map delete_date robe slot_addon rename_addon sex)], + }; + + } elsif ($_ == 144) { # PACKETVER >= 20111025 [added rename char feature] + $char_info = { + types => 'a4 V9 v V2 v14 Z24 C8 v Z16 V4', + keys => [qw(charID exp zeny exp_job lv_job body_state health_state effect_state stance manner status_point hp hp_max sp sp_max walkspeed jobID hair_style weapon lv skill_point head_bottom shield head_top head_mid hair_pallete clothes_color name str agi vit int dex luk slot hair_color is_renamed last_map delete_date robe slot_addon rename_addon)], + }; + + } elsif ($_ == 140) { # PACKETVER >= 20110928 [added change slot feature] + $char_info = { + types => 'a4 V9 v V2 v14 Z24 C8 v Z16 V3', + keys => [qw(charID exp zeny exp_job lv_job body_state health_state effect_state stance manner status_point hp hp_max sp sp_max walkspeed jobID hair_style weapon lv skill_point head_bottom shield head_top head_mid hair_pallete clothes_color name str agi vit int dex luk slot hair_color is_renamed last_map delete_date robe slot_addon)], + }; + + } elsif ($_ == 136) { # PACKETVER >= 20110111 [added robe] + $char_info = { + types => 'a4 V9 v V2 v14 Z24 C8 v Z16 V2', + keys => [qw(charID exp zeny exp_job lv_job body_state health_state effect_state stance manner status_point hp hp_max sp sp_max walkspeed jobID hair_style weapon lv skill_point head_bottom shield head_top head_mid hair_pallete clothes_color name str agi vit int dex luk slot hair_color is_renamed last_map delete_date robe)], + }; + + } elsif ($_ == 132) { # PACKETVER >= 20100803 [added delete date] + $char_info = { + types => 'a4 V9 v V2 v14 Z24 C8 v Z16 V', + keys => [qw(charID exp zeny exp_job lv_job body_state health_state effect_state stance manner status_point hp hp_max sp sp_max walkspeed jobID hair_style weapon lv skill_point head_bottom shield head_top head_mid hair_pallete clothes_color name str agi vit int dex luk slot hair_color is_renamed last_map delete_date)], + }; + + } elsif ($_ == 128) { # [Update in last_map size] + $char_info = { + types => 'a4 V9 v V2 v14 Z24 C8 v Z16', + keys => [qw(charID exp zeny exp_job lv_job body_state health_state effect_state stance manner status_point hp hp_max sp sp_max walkspeed jobID hair_style weapon lv skill_point head_bottom shield head_top head_mid hair_pallete clothes_color name str agi vit int dex luk slot hair_color is_renamed last_map)], + }; + + } elsif ($_ == 124) { # PACKETVER >= 20100803 [added last_map, bRO (bitfrost update)] + $char_info = { + types => 'a4 V9 v V2 v14 Z24 C8 v Z12', + keys => [qw(charID exp zeny exp_job lv_job body_state health_state effect_state stance manner status_point hp hp_max sp sp_max walkspeed jobID hair_style weapon lv skill_point head_bottom shield head_top head_mid hair_pallete clothes_color name str agi vit int dex luk slot hair_color is_renamed last_map)], + }; + + } elsif ($_ == 116) { # Unknown change + $char_info = { + types => 'a4 V9 v V2 v14 Z24 C8 v x4', + keys => [qw(charID exp zeny exp_job lv_job body_state health_state effect_state stance manner status_point hp hp_max sp sp_max walkspeed jobID hair_style weapon lv skill_point head_bottom shield head_top head_mid hair_pallete clothes_color name str agi vit int dex luk slot hair_color is_renamed)], + }; + + } elsif ($_ == 112) { # [Added is_renamed] + $char_info = { + types => 'a4 V9 v V2 v14 Z24 C8 v', + keys => [qw(charID exp zeny exp_job lv_job body_state health_state effect_state stance manner status_point hp hp_max sp sp_max walkspeed jobID hair_style weapon lv skill_point head_bottom shield head_top head_mid hair_pallete clothes_color name str agi vit int dex luk slot hair_color is_renamed)], + }; + + } elsif ($_ == 108) { # [Added hair_color] + $char_info = { + types => 'a4 V9 v17 Z24 C6 v2', + keys => [qw(charID exp zeny exp_job lv_job body_state health_state effect_state stance manner status_point hp hp_max sp sp_max walkspeed jobID hair_style weapon lv skill_point head_bottom shield head_top head_mid hair_pallete clothes_color name str agi vit int dex luk slot hair_color)], + }; + + } elsif ($_ == 106) { # PACKETVER >= 2003+ [First known charBlockSize] + $char_info = { + types => 'a4 V9 v17 Z24 C6 v', + keys => [qw(charID exp zeny exp_job lv_job body_state health_state effect_state stance manner status_point hp hp_max sp sp_max walkspeed jobID hair_style weapon lv skill_point head_bottom shield head_top head_mid hair_pallete clothes_color name str agi vit int dex luk slot)], + }; + + } else { + die "Unknown charBlockSize: $_"; + } + return $char_info; + } + die "masterserver or charBlockSize is undefined"; +} + +sub received_characters_slots_info { + return if ($net->getState() == Network::IN_GAME); + my ($self, $args) = @_; + $net->setState(Network::CONNECTED_TO_LOGIN_SERVER); + $charSvrSet{total_slot} = $args->{total_slot} if (exists $args->{total_slot}); + $charSvrSet{premium_start_slot} = $args->{premium_start_slot} if (exists $args->{premium_start_slot}); + $charSvrSet{premium_end_slot} = $args->{premium_end_slot} if (exists $args->{premium_end_slot}); + + $charSvrSet{normal_slot} = $args->{normal_slot} if (exists $args->{normal_slot}); + $charSvrSet{premium_slot} = $args->{premium_slot} if (exists $args->{premium_slot}); + $charSvrSet{billing_slot} = $args->{billing_slot} if (exists $args->{billing_slot}); + + $charSvrSet{producible_slot} = $args->{producible_slot} if (exists $args->{producible_slot}); + $charSvrSet{valid_slot} = $args->{valid_slot} if (exists $args->{valid_slot}); + + undef $conState_tries; + + Plugins::callHook('parseMsg/recvChars', $args->{options}); + if ($args->{options} && exists $args->{options}{charServer}) { + $charServer = $args->{options}{charServer}; + } else { + $charServer = $net->serverPeerHost . ":" . $net->serverPeerPort; + } + + $self->received_characters($args) if ($args->{charInfo}); +} + +# Send to client Characters pages in Char Select Screen +# 099D <size>.W { CHARACTER_INFO_NEO_UNION3 } * (PACKET_HC_ACK_CHARINFO_PER_PAGE) +# CHARACTER_INFO_NEO_UNION3 is based in charblocksize, check sub received_characters_unpackString +sub received_characters { + my ($self, $args) = @_; + my $blockSize = $self->received_characters_blockSize(); + my $char_info = $self->received_characters_unpackString; + + # rAthena and Hercules send all pages + # Official Server send only pages with characters + 1 empty (tested bRO, iRO) Jul-2020 + if (length($args->{charInfo} == 0)) { + $charSvrSet{sync_received_characters} = $charSvrSet{sync_Count} if (exists $charSvrSet{sync_received_characters}); + } else { + $charSvrSet{sync_received_characters}++ if (exists $charSvrSet{sync_received_characters}); + } + + $net->setState(Network::CONNECTED_TO_LOGIN_SERVER) if $net->getState() != Network::CONNECTED_TO_LOGIN_SERVER; + + return unless exists $args->{charInfo}; + + for (my $i = 0; $i < length($args->{charInfo}); $i += $masterServer->{charBlockSize}) { + my $temporary_character; + @{$temporary_character}{@{$char_info->{keys}}} = unpack($char_info->{types}, substr($args->{charInfo}, $i, $masterServer->{charBlockSize})); + + my $character; + + # Re-use existing $char object instead of re-creating it. + # Required because existing AI sequences (eg, route) keep a reference to $char. + if ($char && $char->{ID} eq $accountID && $char->{charID} eq $temporary_character->{charID}) { + $character = $char; + } elsif (exists $chars[$temporary_character->{slot}] && $chars[$temporary_character->{slot}]->{charID} eq $temporary_character->{charID}) { # Re-use existing $char object from $chars if available. + $character = $chars[$temporary_character->{slot}]; + } else { # create new one + $character = new Actor::You; + } + + @{$character}{@{$char_info->{keys}}} = unpack($char_info->{types}, substr($args->{charInfo}, $i, $masterServer->{charBlockSize})); + $character->{ID} = $accountID; + + $character->{name} = bytesToString($character->{name}); + + $character->{lastJobLvl} = $character->{lv_job}; # This is for counting exp + $character->{lastBaseLvl} = $character->{lv}; # This is for counting exp + $character->{headgear}{low} = $character->{head_bottom}; + $character->{headgear}{top} = $character->{head_top}; + $character->{headgear}{mid} = $character->{head_mid}; + + $character->{nameID} = unpack("V", $character->{ID}); + $character->{last_map} =~ s/\.gat.*//g if ($character->{last_map}); + + if ((!exists($character->{sex})) || ($character->{sex} ne "0" && $character->{sex} ne "1")) { $character->{sex} = $accountSex2; } + + $chars[$character->{slot}] = $character; + setCharDeleteDate($character->{slot}, $character->{delete_date}) if $character->{delete_date}; + } + + message T("Received characters from Character Server\n"), "connection"; + + # gradeA says it's supposed to send this packet here, but + # it doesn't work... + # 30 Dec 2005: it didn't work before because it wasn't sending the accountiD -> fixed (kaliwanagan) + $messageSender->sendBanCheck($accountID) if (!$net->clientAlive && $masterServer->{serverType} == 2); + + if ($masterServer->{pinCode}) { + message T("Waiting for PIN code request\n"), "connection"; + $timeout{'charlogin'}{'time'} = time; + + } elsif ($config{pauseCharLogin}) { + return if ($config{XKore} eq 1 || $config{XKore} eq 3); + if (!defined $timeout{'char_login_pause'}{'timeout'}) { + $timeout{'char_login_pause'}{'timeout'} = $config{pauseCharLogin}; + } + $timeout{'char_login_pause'}{'time'} = time; + + } else { + CharacterLogin(); + } +} + +# Tell client how many pages have character selection screen +# 09A0 <total count>.W (PACKET_HC_CHARLIST_NOTIFY) +# total count: Server send from total pages until 1 page +sub sync_received_characters { + my ($self, $args) = @_; + + return unless (UNIVERSAL::isa($net, 'Network::DirectConnection')); + + $charSvrSet{sync_Count} = $args->{sync_Count} if (exists $args->{sync_Count}); + $charSvrSet{sync_received_characters} = 0 if (exists $args->{sync_Count}); + + unless ($net->clientAlive) { + for (1..$args->{sync_Count}) { + $messageSender->sendToServer($messageSender->reconstruct({switch => 'sync_received_characters'})); + } + } +} + +sub reconstruct_received_characters { + my ($self, $args) = @_; + my $char_info = $self->received_characters_unpackString; + + $args->{charInfo} = pack '(a'.$masterServer->{charBlockSize}.')*', map { pack $char_info->{types}, @{$_}{@{$char_info->{keys}}} } @{$args->{chars}}; +} + +sub reconstruct_received_characters_info { + my ($self, $args) = @_; + my $char_info = $self->received_characters_unpackString; + + $args->{charInfo} = pack '(a'.$masterServer->{charBlockSize}.')*', map { pack $char_info->{types}, @{$_}{@{$char_info->{keys}}} } @{$args->{chars}}; +} + +# Notifies client, that character was succesfull created +# 006E { CHARACTER_INFO_NEO_UNION } (PACKET_HC_ACCEPT_MAKECHAR_NEO_UNION) +# CHARACTER_INFO_NEO_UNION is based in charblocksize, check sub received_characters_unpackString +sub character_creation_successful { + my ($self, $args) = @_; + return unless exists $args->{charInfo}; + + my $char_info = $self->received_characters_unpackString; + + my $character = new Actor::You; + @{$character}{@{$char_info->{keys}}} = unpack($char_info->{types}, substr($args->{charInfo}, 0, $masterServer->{charBlockSize})); + $character->{ID} = $accountID; + + $character->{lastJobLvl} = $character->{lv_job}; # This is for counting exp + $character->{lastBaseLvl} = $character->{lv}; # This is for counting exp + $character->{headgear}{low} = $character->{head_bottom}; + $character->{headgear}{top} = $character->{head_top}; + $character->{headgear}{mid} = $character->{head_mid}; + + $character->{nameID} = unpack("V", $character->{ID}); + $character->{name} = bytesToString($character->{name}); + $character->{last_map} = substr($character->{last_map}, 0, length($character->{last_map}) - 4); + + $character->{exp} = 0; + $character->{exp_job} = 0; + + if ((!exists($character->{sex})) || ($character->{sex} ne "0" && $character->{sex} ne "1")) { $character->{sex} = $accountSex2; } + + $chars[$character->{slot}] = $character; + + $net->setState(3); + message TF("Character %s (%d) created.\n", $character->{name}, $character->{slot}), "info"; + + Plugins::callHook('char_created', {char => $character}); + + if ($net->getState() == 3 && charSelectScreen() == 1) { + $firstLoginMap = 1; + $startingzeny = $chars[$config{'char'}]{'zeny'} unless defined $startingzeny; + $sentWelcomeMessage = 1; + } +} + +# Notifies Client that the character was not created +# 006E <error code>.B (PACKET_HC_REFUSE_MAKECHAR) +# code: +# 0x00 = Charname already exists +# 0x01 = You are underaged +# 0x02 = Symbols in Character Names are forbidden +# 0x03 = You are not elegible to open the Character Slot +# 0xFF = Char creation denied +sub character_creation_failed { + my ($self, $args) = @_; + if ($args->{flag} == 0x00) { + message T("Charname already exists.\n"), "info"; + } elsif ($args->{flag} == 0xFF) { + message T("Char creation denied.\n"), "info"; + } elsif ($args->{flag} == 0x01) { + message T("You are underaged.\n"), "info"; + } elsif ($args->{flag} == 0x02) { + message T("Symbols in Character Names are forbidden .\n"), "info"; + } elsif ($args->{flag} == 0x03) { + message T("You are not elegible to open the Character Slot.\n"), "info"; + } else { + message T("Character creation failed. " . + "If you didn't make any mistake, then the name you chose already exists.\n"), "info"; + } + if (charSelectScreen() == 1) { + $net->setState(3); + $firstLoginMap = 1; + $startingzeny = $chars[$config{'char'}]{'zeny'} unless defined $startingzeny; + $sentWelcomeMessage = 1; + } +} + +# Notifies client about account slots info and chars +# 006B <size>.W <total slots>.B <premium start slot>.B <premium end slot>.B <dummy1_beginbilling>.B <code>.L <start time>.L <end time>.L { CHARACTER_INFO_NEO_UNION3 }* (PACKET_HC_ACCEPT_ENTER_NEO_UNION) +# 082D <size>.W <normal slot>.B <premium slot>.B <billing slot>.B <producible slot>.B <valid slot>.B <m_extension>.20B { CHARACTER_INFO_NEO_UNION2 }* (PACKET_HC_ACCEPT2) +# CHARACTER_INFO_NEO_UNION3 and CHARACTER_INFO_NEO_UNION2 are based in charblocksize, check sub received_characters_unpackString +sub received_characters_info { + my ($self, $args) = @_; + Scalar::Util::weaken(my $weak = $self); + my $timeout = {timeout => 6, time => time}; + + $self->{charSelectTimeoutHook} = Plugins::addHook('Network::serverConnect/special' => sub { + if ($weak && timeOut($timeout)) { + $weak->received_characters_slots_info({charInfo => '', RAW_MSG_SIZE => 4}); + } + }); + $self->{charSelectHook} = Plugins::addHook(charSelectScreen => sub { + if ($weak) { + Plugins::delHook(delete $weak->{charSelectTimeoutHook}) if $weak->{charSelectTimeoutHook}; + } + }); + $timeout{charlogin}{time} = time; + $self->received_characters_slots_info($args); +} + +### Parse/reconstruct callbacks and packet handlers + +sub parse_account_server_info { + my ($self, $args) = @_; + my $server_info; + + if ($args->{switch} eq '0B60') { # tRO 2020, twRO 2021 + $server_info = { + len => 164, + types => 'a4 v Z20 v3 a128 V', + keys => [qw(ip port name state users property ip_port unknown)], + }; + + } elsif ($args->{switch} eq '0AC4' || $args->{switch} eq '0B07') { # kRO Zero 2017, kRO ST 201703+, vRO 2021 + $server_info = { + len => 160, + types => 'a4 v Z20 v3 a128', + keys => [qw(ip port name users state property ip_port)], + }; + + } elsif ($args->{switch} eq '0AC9') { # cRO 2017 + $server_info = { + len => 154, + types => 'a20 V v a126', + keys => [qw(name users unknown ip_port)], + }; + } elsif ($args->{switch} eq '0276' && ($masterServer->{serverType} eq "tRO" or $masterServer->{serverType} eq "aRO")) { # tRO 2020 and aRO 2022. Keep this here to future uses + $server_info = { + len => 36, + types => 'a4 v Z20 v5', + keys => [qw(ip port name state users property sid unknown)], + }; + } elsif ($args->{switch} eq '0C32') { + $server_info = { + len => 165, + types => 'a4 v Z20 v3 a128 a5', + keys => [qw(ip port name users state property ip_port unknown)], + }; + } else { # 0069 [default] and 0276 [pRO] + $server_info = { + len => 32, + types => 'a4 v Z20 v3', + keys => [qw(ip port name users display unknown)], + }; + } + + @{$args->{servers}} = map { + my %server; + @server{@{$server_info->{keys}}} = unpack($server_info->{types}, $_); + if ($masterServer && $masterServer->{private}) { + $server{ip} = $masterServer->{ip}; + } elsif (exists $server{ip_port} && $server{ip_port} =~ /.*\:\d+/) { + @server{qw(ip port)} = split (/\:/, $server{ip_port}); + $server{ip} =~ s/^\s+|\s+$//g; + $server{port} =~ tr/0-9//cd; + } else { + $server{ip} = inet_ntoa($server{ip}); + } + $server{name} = bytesToString($server{name}); + \%server + } unpack '(a'.$server_info->{len}.')*', $args->{serverInfo}; + + if (length $args->{lastLoginIP} == 4 && $args->{lastLoginIP} ne "\0"x4) { + $args->{lastLoginIP} = inet_ntoa($args->{lastLoginIP}); + } else { + delete $args->{lastLoginIP}; + } +} + +sub reconstruct_account_server_info { + my ($self, $args) = @_; + $args->{lastLoginIP} = inet_aton($args->{lastLoginIP}); + + my $serverInfo; + + if ($args->{switch} eq '0B60') { # tRO 2020 + $serverInfo = { + len => 164, + types => 'a4 v Z20 v3 a128 V', + keys => [qw(ip port name state users property ip_port unknown)], + }; + + } elsif ($args->{switch} eq "0AC4" || $self->{packet_lut}{$args->{switch}} eq "0AC4" || $args->{switch} eq '0B07') { + $serverInfo = { + len => 160, + types => 'a4 v Z20 v3 a128', + keys => [qw(ip port name users state property ip_port)], + }; + } elsif ($args->{switch} eq "0AC9" || $self->{packet_lut}{$args->{switch}} eq "0AC9") { + $serverInfo = { + len => 154, + types => 'a20 V a2 a126', + keys => [qw(name users unknown ip_port)], + }; + } elsif ($masterServer->{serverType} eq "tRO" && ( $args->{switch} eq "0276" || $self->{packet_lut}{$args->{switch}} eq "0276" )) { + $serverInfo = { + len => 36, + types => 'a4 v Z20 v5', + keys => [qw(ip port name state users property sid unknown)], + }; + } elsif ($args->{switch} eq '0C32') { + $serverInfo = { + len => 165, + types => 'a4 v Z20 v3 a128 a5', + keys => [qw(ip port name users state property ip_port unknown)], + }; + } else { + $serverInfo = { + len => 32, + types => 'a4 v Z20 v2 x2', + keys => [qw(ip port name users display)], + }; + } + + foreach my $server (@{$args->{servers}}) { + $server->{ip} = inet_aton($server->{ip}); + $server->{name} = stringToBytes($server->{name}); + } + + $args->{serverInfo} = pack '(a' . $serverInfo->{len} .')*', map { pack($serverInfo->{types}, @{$_}{@{$serverInfo->{keys}}}) } @{$args->{servers}}; +} + +sub account_server_info { + my ($self, $args) = @_; + $net->setState(2); + undef $conState_tries; + $sessionID = $args->{sessionID}; + $accountID = $args->{accountID}; + $sessionID2 = $args->{sessionID2}; + # Account sex should only be 0 (female) or 1 (male) + # inRO gives female as 2 but expects 0 back + # do modulus of 2 here to fix? + # FIXME: we should check exactly what operation the client does to the number given + $accountSex = $args->{accountSex} % 2; + $accountSex2 = ($config{'sex'} ne "") ? $config{'sex'} : $accountSex; + + # any servers with lastLoginIP lastLoginTime? + # message TF("Last login: %s from %s\n", @{$args}{qw(lastLoginTime lastLoginIP)}) if ...; + + message + center(T(" Account Info "), 34, '-') ."\n" . + swrite( + T("Account ID: \@<<<<<<<<< \@<<<<<<<<<<\n" . + "Sex: \@<<<<<<<<<<<<<<<<<<<<<\n" . + "Session ID: \@<<<<<<<<< \@<<<<<<<<<<\n" . + "SessionID2: \@<<<<<<<<< \@<<<<<<<<<<\n"), + [unpack('V',$accountID), getHex($accountID), $sex_lut{$accountSex}, unpack('V',$sessionID), getHex($sessionID), + unpack('V',$sessionID2), getHex($sessionID2)]) . + ('-'x34) . "\n", 'connection'; + + @servers = @{$args->{servers}}; + my @state = ("Idle", "Normal", "Busy", "Full"); + + my $msg = center(T(" Servers "), 70, '-') ."\n" . + T("# Name Users IP Port SID State\n"); + for (my $num = 0; $num < @servers; $num++) { + $msg .= swrite( + "@<< @<<<<<<<<<<<<<<<<<<<< @<<<<< @<<<<<<<<<<<<<< @<<<<< @<<<<< @<<<<<<", + [$num, $servers[$num]{name}, $servers[$num]{users}, $servers[$num]{ip}, $servers[$num]{port}, ($servers[$num]{sid}) ? $servers[$num]{sid} : 0, defined($servers[$num]{state}) ? $state[$servers[$num]{state}] : 0]); + } + $msg .= ('-'x70) . "\n"; + message $msg, "connection"; + + if ($net->version != 1) { + message T("Closing connection to Account Server\n"), 'connection'; + $net->serverDisconnect(); + if (!$masterServer->{charServer_ip} && $config{server} eq "") { + my @serverList; + foreach my $server (@servers) { + push @serverList, $server->{name}; + } + my $ret = $interface->showMenu( + T("Please select your login server."), + \@serverList, + title => T("Select Login Server")); + if ($ret == -1) { + quit(); + } else { + main::configModify('server', $ret, 1); + } + + } elsif ($masterServer->{charServer_ip}) { + message TF("Forcing connect to char server %s: %s\n", $masterServer->{charServer_ip}, $masterServer->{charServer_port}), 'connection'; + } + } + + # FIXME better support for multiple received_characters packets + undef @chars; + if ($config{'XKore'} eq '1') { + $incomingMessages->nextMessageMightBeAccountID(); + } +} + +sub connection_refused { + my ($self, $args) = @_; + + error TF("The server has denied your connection (error: %d).\n", $args->{error}), 'connection'; +} + +# Notifies the client, that it's connection attempt was accepted. +# 0073 <start time>.L <position>.3B <x size>.B <y size>.B (ZC_ACCEPT_ENTER) +# 02EB <start time>.L <position>.3B <x size>.B <y size>.B <font>.W (ZC_ACCEPT_ENTER2) +# 0A18 <start time>.L <position>.3B <x size>.B <y size>.B <font>.W <sex>.B (ZC_ACCEPT_ENTER3) +sub map_loaded { + my ($self, $args) = @_; + $net->setState(Network::IN_GAME); + undef $conState_tries; + $char = $chars[$config{char}]; + return unless changeToInGameState(); + # assertClass($char, 'Actor::You'); + $syncMapSync = pack('V1',$args->{syncMapSync}); # unused, should we keep this for legacy compatibility? + main::initMapChangeVars(); + %minimap_indicator_seen = (); + + if ($net->version == 1) { + $net->setState(4); + message(T("Waiting for map to load...\n"), "connection"); + ai_clientSuspend(0, $timeout{'ai_clientSuspend'}{'timeout'}); + } else { + $messageSender->sendReqRemainTime() if (grep { $masterServer->{serverType} eq $_ } qw(Zero Sakray)); + + $messageSender->sendMapLoaded(); + + $messageSender->sendSync(1); + + # Request for Guild Information + $messageSender->sendGuildRequestInfo(0) unless (grep { $masterServer->{serverType} eq $_ } qw(twRO ROla)); # some servers does not send this packet + + $messageSender->sendRequestCashItemsList() if (grep { $masterServer->{serverType} eq $_ } qw(bRO idRO_Renewal twRO ROla)); # tested at bRO 2013.11.30, request for cashitemslist + $messageSender->sendCashShopOpen() if ($config{whenInGame_requestCashPoints}); + + # request to unfreeze char - alisonrag + $messageSender->sendBlockingPlayerCancel() if $masterServer->{blockingPlayerCancel} || $self->{blockingPlayerCancel}; + } + + message(T("You are now in the game\n"), "connection"); + Plugins::callHook('in_game'); + $timeout{'ai'}{'time'} = time; + our $quest_generation++; + + $char->{pos} = {}; + makeCoordsDir($char->{pos}, $args->{coords}, \$char->{look}{body}); + $char->{pos_to} = {%{$char->{pos}}}; + message(TF("Your Coordinates: %s, %s\n", $char->{pos}{x}, $char->{pos}{y}), undef, 1); + $char->{time_move} = 0; + $char->{time_move_calc} = 0; + $char->{solution} = []; + push(@{$char->{solution}}, { x => $char->{pos}{x}, y => $char->{pos}{y} }); + + # set initial status from data received from the char server (seems needed on eA, dunno about kRO)} + if ($masterServer->{private}){ setStatus($char, $char->{opt1}, $char->{opt2}, $char->{option}); } + + # ignoreAll + $ignored_all = 0; +} + +# Notifies the client, that it's connection attempt was refused (ZC_REFUSE_ENTER). +# 0074 <error code>.B +# error code: +# 0 = client type mismatch +# 1 = ID mismatch +# 2 = mobile - out of available time +# 3 = mobile - already logged in +# 4 = mobile - waiting state +sub map_load_error { + my ($self, $args) = @_; + + error T("Error while try to login in map-server: "); + if ($args->{error} == 0) { + error TF("Wrong Client Type (%s). \n", $args->{error}); + } elsif ($args->{error} == 1) { + error TF("Wrong ID (%s). \n", $args->{error}); + } elsif ($args->{error} == 2) { + error TF("Timeout (%s). \n", $args->{error}); + } elsif ($args->{error} == 3) { + error TF("Already Logged In (%s). \n", $args->{error}); + } elsif ($args->{error} == 4) { + error TF("Waiting State (%s). \n", $args->{error}); # ?? + } else { + error TF("Unknown Error (%s). \n", $args->{error}); + } + + Plugins::callHook('disconnected'); + if ($config{dcOnDisconnect}) { + error T("Auto disconnecting on Disconnect!\n"); + chatLog("k", T("*** You disconnected, auto disconnect! ***\n")); + $quit = 1; + } + + $net->setState(1); + undef $conState_tries; + + $timeout_ex{'master'}{'time'} = time; + $timeout_ex{'master'}{'timeout'} = $timeout{'reconnect'}{'timeout'}; + $net->serverDisconnect(); +} + +our %stat_info_handlers = ( + VAR_SPEED, sub { $_[0]{walk_speed} = $_[1] / 1000 }, + VAR_EXP, sub { + my ($actor, $value) = @_; + + $actor->{exp_last} = $actor->{exp}; + $actor->{exp} = $value; + + return unless $actor->isa('Actor::You'); +=pod + unless ($bExpSwitch) { + $bExpSwitch = 1; + } else { + if ($actor->{exp_last} > $actor->{exp}) { + $monsterBaseExp = 0; + } else { + $monsterBaseExp = $actor->{exp} - $actor->{exp_last}; + } + $totalBaseExp += $monsterBaseExp; + if ($bExpSwitch == 1) { + $totalBaseExp += $monsterBaseExp; + $bExpSwitch = 2; + } + } +=cut + + if ($actor->{lastBaseLvl} eq $actor->{lv}) { + $monsterBaseExp = $actor->{exp} - $actor->{exp_last}; + } else { + $monsterBaseExp = $actor->{exp_max_last2} - $actor->{exp_last} + $actor->{exp}; + $actor->{lastBaseLvl} = $actor->{lv}; + $actor->{exp_max_last2} = $actor->{exp_max}; + } + + if ($monsterBaseExp > 0) { + $totalBaseExp += $monsterBaseExp; + } + + # no VAR_JOBEXP next - no message? + }, + VAR_JOBEXP, sub { + my ($actor, $value) = @_; + + $actor->{exp_job_last} = $actor->{exp_job}; + $actor->{exp_job} = $value; + + # TODO: message for all actors + return unless $actor->isa('Actor::You'); + # TODO: exp report (statistics) - no globals, move to plugin +=pod + if ($jExpSwitch == 0) { + $jExpSwitch = 1; + } else { + if ($char->{exp_job_last} > $char->{exp_job}) { + $monsterJobExp = 0; + } else { + $monsterJobExp = $char->{exp_job} - $char->{exp_job_last}; + } + $totalJobExp += $monsterJobExp; + if ($jExpSwitch == 1) { + $totalJobExp += $monsterJobExp; + $jExpSwitch = 2; + } + } +=cut + + if ($actor->{lastJobLvl} eq $actor->{lv_job}) { + $monsterJobExp = $actor->{exp_job} - $actor->{exp_job_last}; + } else { + $monsterJobExp = $actor->{exp_job_max_last2} - $actor->{exp_job_last} + $actor->{exp_job}; + $actor->{lastJobLvl} = $actor->{lv_job}; + $actor->{exp_job_max_last2} = $actor->{exp_job_max}; + } + + if ($monsterJobExp > 0) { + $totalJobExp += $monsterJobExp; + } + + my $basePercent = $char->{exp_max} ? + ($monsterBaseExp / $char->{exp_max} * 100) : + 0; + my $jobPercent = $char->{exp_job_max} ? + ($monsterJobExp / $char->{exp_job_max} * 100) : + 0; + message TF("%s have gained %d/%d (%.2f%%/%.2f%%) Exp\n", $char, $monsterBaseExp, $monsterJobExp, $basePercent, $jobPercent), "exp"; + Plugins::callHook('exp_gained'); + }, + #VAR_VIRTUE + VAR_HONOR, sub { + my ($actor, $value) = @_; + + if ($value > 0) { + my $duration = 0xffffffff - $value + 1; + $actor->{mute_period} = $duration * 60; + $actor->{muted} = time; + message sprintf( + $actor->verb(T("%s have been muted for %d minutes\n"), T("%s has been muted for %d minutes\n")), + $actor, $duration + ), "parseMsg_statuslook", $actor->isa('Actor::You') ? 1 : 2; + $actor->setStatus('EFST_MUTED', 1, $actor->{mute_period} * 1000); + } else { + delete $actor->{muted}; + delete $actor->{mute_period}; + message sprintf( + $actor->verb(T("%s are no longer muted."), T("%s is no longer muted.")), $actor + ), "parseMsg_statuslook", $actor->isa('Actor::You') ? 1 : 2; + $actor->setStatus('EFST_MUTED', 0); + } + + return unless $actor->isa('Actor::You'); + + if ($config{dcOnMute} && $actor->{muted}) { + error TF("Auto disconnecting, %s have been muted for %s minutes!\n", $actor, $actor->{mute_period}/60); + chatLog("k", TF("*** %s have been muted for %d minutes, auto disconnect! ***\n", $actor, $actor->{mute_period}/60)); + $messageSender->sendQuit(); + quit(); + } + }, + VAR_HP, sub { + $_[0]{hp} = $_[1]; + }, + VAR_MAXHP, sub { + $_[0]{hp_max} = $_[1]; + }, + VAR_SP, sub { + $_[0]{sp} = $_[1]; + }, + VAR_MAXSP, sub { + $_[0]{sp_max} = $_[1]; + }, + VAR_POINT, sub { $_[0]{points_free} = $_[1] }, + #VAR_HAIRCOLOR + VAR_CLEVEL, sub { + my ($actor, $value) = @_; + + $actor->{lv} = $value; + + message sprintf($actor->verb(T("%s are now level %d\n"), T("%s is now level %d\n")), $actor, $value), "success", $actor->isa('Actor::You') ? 1 : 2; + + return unless $actor->isa('Actor::You'); + + Plugins::callHook('base_level_changed', {level => $actor->{lv}}); + + if ($config{dcOnLevel} && $actor->{lv} >= $config{dcOnLevel}) { + message TF("Disconnecting on level %s!\n", $config{dcOnLevel}); + chatLog("k", TF("Disconnecting on level %s!\n", $config{dcOnLevel})); + quit(); + } + }, + VAR_SPPOINT, sub { $_[0]{points_skill} = $_[1] }, + #VAR_STR + #VAR_AGI + #VAR_VIT + #VAR_INT + #VAR_DEX + #VAR_LUK + #VAR_JOB + VAR_MONEY, sub { + my ($actor, $value) = @_; + + my $change = $value - $actor->{zeny}; + $actor->{zeny} = $value; + + message sprintf( + $change > 0 + ? $actor->verb(T("%s gained %s zeny.\n"), T("%s gained %s zeny.\n")) + : $actor->verb(T("%s lost %s zeny.\n"), T("%s lost %s zeny.\n")), + $actor, formatNumber(abs $change) + ), 'info', $actor->isa('Actor::You') ? 1 : 2 if $change; + + return unless $actor->isa('Actor::You'); + + Plugins::callHook('zeny_change', { + zeny => $actor->{zeny}, + change => $change + }); + + if ($config{dcOnZeny} && $actor->{zeny} <= $config{dcOnZeny}) { + $messageSender->sendQuit(); + error (TF("Auto disconnecting due to zeny lower than %s!\n", $config{dcOnZeny})); + chatLog("k", T("*** You have no money, auto disconnect! ***\n")); + quit(); + } + }, + #VAR_SEX + VAR_MAXEXP, sub { + $_[0]{exp_max_last} = $_[0]{exp_max}; + $_[0]{exp_max_last2} = $_[0]{exp_max} if !$_[0]{exp_max_last2}; + $_[0]{exp_max} = $_[1]; + + if (!$net->clientAlive() && $initSync && $masterServer->{serverType} == 2) { + $messageSender->sendSync(1); + $initSync = 0; + } + }, + VAR_MAXJOBEXP, sub { + $_[0]{exp_job_max_last} = $_[0]{exp_job_max}; + $_[0]{exp_job_max_last2} = $_[0]{exp_job_max} if !$_[0]{exp_job_max_last2}; + $_[0]{exp_job_max} = $_[1]; + #message TF("BaseExp: %s | JobExp: %s\n", $monsterBaseExp, $monsterJobExp), "info", 2 if ($monsterBaseExp); + }, + VAR_WEIGHT, sub { $_[0]{weight} = $_[1] / 10 }, + VAR_MAXWEIGHT, sub { $_[0]{weight_max} = int($_[1] / 10) }, + #VAR_POISON + #VAR_STONE + #VAR_CURSE + #VAR_FREEZING + #VAR_SILENCE + #VAR_CONFUSION + VAR_STANDARD_STR, sub { $_[0]{points_str} = $_[1] }, + VAR_STANDARD_AGI, sub { $_[0]{points_agi} = $_[1] }, + VAR_STANDARD_VIT, sub { $_[0]{points_vit} = $_[1] }, + VAR_STANDARD_INT, sub { $_[0]{points_int} = $_[1] }, + VAR_STANDARD_DEX, sub { $_[0]{points_dex} = $_[1] }, + VAR_STANDARD_LUK, sub { $_[0]{points_luk} = $_[1] }, + #VAR_ATTACKMT + #VAR_ATTACKEDMT + #VAR_NV_BASIC + VAR_ATTPOWER, sub { $_[0]{attack} = $_[1] }, + VAR_REFININGPOWER, sub { $_[0]{attack_bonus} = $_[1] }, + VAR_MAX_MATTPOWER, sub { $_[0]{attack_magic_max} = $_[1] }, + VAR_MIN_MATTPOWER, sub { $_[0]{attack_magic_min} = $_[1] }, + VAR_ITEMDEFPOWER, sub { $_[0]{def} = $_[1] }, + VAR_PLUSDEFPOWER, sub { $_[0]{def_bonus} = $_[1] }, + VAR_MDEFPOWER, sub { $_[0]{def_magic} = $_[1] }, + VAR_PLUSMDEFPOWER, sub { $_[0]{def_magic_bonus} = $_[1] }, + VAR_HITSUCCESSVALUE, sub { $_[0]{hit} = $_[1] }, + VAR_AVOIDSUCCESSVALUE, sub { $_[0]{flee} = $_[1] }, + VAR_PLUSAVOIDSUCCESSVALUE, sub { $_[0]{flee_bonus} = $_[1] }, + VAR_CRITICALSUCCESSVALUE, sub { $_[0]{critical} = $_[1] }, + VAR_ASPD, sub { + $_[0]{attack_delay} = $_[1] >= 10 ? $_[1] : 10; # at least for mercenary + $_[0]{attack_speed} = 200 - $_[0]{attack_delay} / 10; + }, + #VAR_PLUSASPD + VAR_JOBLEVEL, sub { + my ($actor, $value) = @_; + + $actor->{lv_job} = $value; + message sprintf($actor->verb("%s are now job level %d\n", "%s is now job level %d\n"), $actor, $actor->{lv_job}), "success", $actor->isa('Actor::You') ? 1 : 2; + + return unless $actor->isa('Actor::You'); + + Plugins::callHook('job_level_changed', {level => $actor->{lv_job}}); + + if ($config{dcOnJobLevel} && $actor->{lv_job} >= $config{dcOnJobLevel}) { + message TF("Disconnecting on job level %d!\n", $config{dcOnJobLevel}); + chatLog("k", TF("Disconnecting on job level %d!\n", $config{dcOnJobLevel})); + quit(); + } + }, + #... + VAR_MER_KILLCOUNT, sub { $_[0]{kills} = $_[1] }, + VAR_MER_FAITH, sub { $_[0]{faith} = $_[1] }, + #... + VAR_SP_POW, sub { $_[0]{pow} = $_[1] }, + VAR_SP_STA, sub { $_[0]{sta} = $_[1] }, + VAR_SP_WIS, sub { $_[0]{wis} = $_[1] }, + VAR_SP_SPL, sub { $_[0]{spl} = $_[1] }, + VAR_SP_CON, sub { $_[0]{con} = $_[1] }, + VAR_SP_CRT, sub { $_[0]{crt} = $_[1] }, + VAR_SP_PATK, sub { $_[0]{patk} = $_[1] }, + VAR_SP_SMATK, sub { $_[0]{smatk} = $_[1] }, + VAR_SP_RES, sub { $_[0]{res} = $_[1] }, + VAR_SP_MRES, sub { $_[0]{mres} = $_[1] }, + VAR_SP_HPLUS, sub { $_[0]{hplus} = $_[1] }, + VAR_SP_CRATE, sub { $_[0]{crate} = $_[1] }, + VAR_SP_TRAITPOINT, sub { $_[0]{traitpoint} = $_[1] }, + VAR_SP_AP, sub { $_[0]{ap} = $_[1] }, + VAR_SP_MAXAP, sub { $_[0]{ap_max} = $_[1] }, + VAR_SP_UPOW, sub { $_[0]{need_pow} = $_[1] }, + VAR_SP_USTA, sub { $_[0]{need_sta} = $_[1] }, + VAR_SP_UWIS, sub { $_[0]{need_wis} = $_[1] }, + VAR_SP_USPL, sub { $_[0]{need_spl} = $_[1] }, + VAR_SP_UCON, sub { $_[0]{need_con} = $_[1] }, + VAR_SP_UCRT, sub { $_[0]{need_crt} = $_[1] }, +); + +# Notifies client of a character parameter change. +# 00B0 <var id>.W <value>.L (ZC_PAR_CHANGE) +# 00B1 <var id>.W <value>.L (ZC_LONGPAR_CHANGE) +# 00BE <status id>.W <value>.B (ZC_STATUS_CHANGE) +# 0141 <status id>.L <base status>.L <plus status>.L (ZC_COUPLESTATUS) +# 0ACB <var id>.W <value>.Q (ZC_LONGPAR_CHANGE2) +# +# Notifies client of a parameter change of an another player. +# 01AB <account id>.L <var id>.W <value>.L (ZC_PAR_CHANGE_USER) +# +# Notification about a mercenary status parameter change. +# 02A2 <var id>.W <value>.L (ZC_MER_PAR_CHANGE) +# +# Notification about a homunculus status parameter change. +# 07DB <var id>.W <value>.L +sub stat_info { + my ($self, $args) = @_; + + return unless changeToInGameState; + + my $actor = { + '00B0' => $char, + '00B1' => $char, + '00BE' => $char, + '0141' => $char, + '01AB' => exists $args->{ID} && Actor::get($args->{ID}), + '07DB' => $char->{homunculus}, + '0ACB' => $char, + }->{$args->{switch}}; + + if ($args->{switch} eq "081E") { + if (!$char->{elemental}) { + $char->{elemental} = new Actor::Elemental; + } + $actor = $char->{elemental}; # Sorcerer's Spirit + } + + if ($args->{switch} eq "02A2") { + if (!$char->{mercenary}) { + $char->{mercenary} = new Actor::Slave::Mercenary; + } + $actor = $char->{mercenary}; + } + + unless ($actor) { + warning sprintf "Actor is unknown or not ready for stat information (switch %s, type %d, val %d)\n", @{$args}{qw(switch type val)}; + return; + } + + if (exists $stat_info_handlers{$args->{type}}) { + # TODO: introduce Actor->something() to determine per-actor configurable verbosity level? (not only here) + debug "Stat: $args->{type} => $args->{val}\n", "parseMsg", $_[0]->isa('Actor::You') ? 1 : 2; + $stat_info_handlers{$args->{type}}($actor, $args->{val}); + } else { + warning sprintf "Unknown stat (%d => %d) received for %s\n", @{$args}{qw(type val)}, $actor; + } + + if (!$char->{walk_speed}) { + $char->{walk_speed} = 0.15; # This is the default speed, since xkore requires this and eA (And aegis?) do not send this if its default speed + } +} + +# TODO: merge with stat_info +# Notifies the client, about the result of an status change request (ZC_STATUS_CHANGE_ACK). +# 00BC <status id>.W <result>.B <value>.B +# result: +# 0 = failure +# 1 = success +sub stats_added { + my ($self, $args) = @_; + + if ($args->{val} == 207) { # client really checks this and not the result field? + error T("Not enough stat points to add\n"); + } else { + if ($args->{type} == VAR_STR) { + $char->{str} = $args->{val}; + debug "Strength: $args->{val}\n", "parseMsg"; + + } elsif ($args->{type} == VAR_AGI) { + $char->{agi} = $args->{val}; + debug "Agility: $args->{val}\n", "parseMsg"; + + } elsif ($args->{type} == VAR_VIT) { + $char->{vit} = $args->{val}; + debug "Vitality: $args->{val}\n", "parseMsg"; + + } elsif ($args->{type} == VAR_INT) { + $char->{int} = $args->{val}; + debug "Intelligence: $args->{val}\n", "parseMsg"; + + } elsif ($args->{type} == VAR_DEX) { + $char->{dex} = $args->{val}; + debug "Dexterity: $args->{val}\n", "parseMsg"; + + } elsif ($args->{type} == VAR_LUK) { + $char->{luk} = $args->{val}; + debug "Luck: $args->{val}\n", "parseMsg"; + + } elsif ($args->{type} == VAR_SP_POW) { + $char->{pow} = $args->{val}; + debug "Power: $args->{val}\n", "parseMsg"; + + } elsif ($args->{type} == VAR_SP_STA) { + $char->{sta} = $args->{val}; + debug "Stamina: $args->{val}\n", "parseMsg"; + + } elsif ($args->{type} == VAR_SP_WIS) { + $char->{wis} = $args->{val}; + debug "Wisdom: $args->{val}\n", "parseMsg"; + + } elsif ($args->{type} == VAR_SP_SPL) { + $char->{spl} = $args->{val}; + debug "Spell: $args->{val}\n", "parseMsg"; + + } elsif ($args->{type} == VAR_SP_CON) { + $char->{con} = $args->{val}; + debug "Concentration: $args->{val}\n", "parseMsg"; + + } elsif ($args->{type} == VAR_SP_CRT) { + $char->{crt} = $args->{val}; + debug "Creative: $args->{val}\n", "parseMsg"; + + } else { + error "$args->{type}: $args->{val}\n", "parseMsg"; + } + } + Plugins::callHook('packet_charStats', { + type => $args->{type}, + val => $args->{val} + }); +} + +# Character status (ZC_STATUS). +# 00BD <stpoint>.W <str>.B <need str>.B <agi>.B <need agi>.B <vit>.B <need vit>.B <int>.B <need int>.B <dex>.B <need dex>.B <luk>.B <need luk>.B +# <atk>.W <atk2>.W <matk min>.W <matk max>.W <def>.W <def2>.W <mdef>.W <mdef2>.W <hit>.W <flee>.W <flee2>.W <crit>.W <aspd>.W <aspd2>.W +sub stats_info { + my ($self, $args) = @_; + return unless changeToInGameState(); + $char->{points_free} = $args->{points_free}; + $char->{str} = $args->{str}; + $char->{points_str} = $args->{points_str}; + $char->{agi} = $args->{agi}; + $char->{points_agi} = $args->{points_agi}; + $char->{vit} = $args->{vit}; + $char->{points_vit} = $args->{points_vit}; + $char->{int} = $args->{int}; + $char->{points_int} = $args->{points_int}; + $char->{dex} = $args->{dex}; + $char->{points_dex} = $args->{points_dex}; + $char->{luk} = $args->{luk}; + $char->{points_luk} = $args->{points_luk}; + $char->{attack} = $args->{attack}; + $char->{attack_bonus} = $args->{attack_bonus}; + $char->{attack_magic_min} = $args->{attack_magic_min}; + $char->{attack_magic_max} = $args->{attack_magic_max}; + $char->{def} = $args->{def}; + $char->{def_bonus} = $args->{def_bonus}; + $char->{def_magic} = $args->{def_magic}; + $char->{def_magic_bonus} = $args->{def_magic_bonus}; + $char->{hit} = $args->{hit}; + $char->{flee} = $args->{flee}; + $char->{flee_bonus} = $args->{flee_bonus}; + $char->{critical} = $args->{critical}; + debug "Strength: $char->{str} #$char->{points_str}\n" + ."Agility: $char->{agi} #$char->{points_agi}\n" + ."Vitality: $char->{vit} #$char->{points_vit}\n" + ."Intelligence: $char->{int} #$char->{points_int}\n" + ."Dexterity: $char->{dex} #$char->{points_dex}\n" + ."Luck: $char->{luk} #$char->{points_luk}\n" + ."Attack: $char->{attack}\n" + ."Attack Bonus: $char->{attack_bonus}\n" + ."Magic Attack Min: $char->{attack_magic_min}\n" + ."Magic Attack Max: $char->{attack_magic_max}\n" + ."Defense: $char->{def}\n" + ."Defense Bonus: $char->{def_bonus}\n" + ."Magic Defense: $char->{def_magic}\n" + ."Magic Defense Bonus: $char->{def_magic_bonus}\n" + ."Hit: $char->{hit}\n" + ."Flee: $char->{flee}\n" + ."Flee Bonus: $char->{flee_bonus}\n" + ."Critical: $char->{critical}\n" + ."Status Points: $char->{points_free}\n", "parseMsg"; +} + +# Notifies client of a character parameter change. +# 0141 <status id>.L <base status>.L <plus status>.L (ZC_COUPLESTATUS) +sub stat_info2 { + my ($self, $args) = @_; + return unless changeToInGameState(); + my ($type, $val, $val2) = @{$args}{qw(type val val2)}; + if ($type == VAR_STR) { + $char->{str} = $val; + $char->{str_bonus} = $val2; + debug "Strength: $val + $val2\n", "parseMsg"; + } elsif ($type == VAR_AGI) { + $char->{agi} = $val; + $char->{agi_bonus} = $val2; + debug "Agility: $val + $val2\n", "parseMsg"; + } elsif ($type == VAR_VIT) { + $char->{vit} = $val; + $char->{vit_bonus} = $val2; + debug "Vitality: $val + $val2\n", "parseMsg"; + } elsif ($type == VAR_INT) { + $char->{int} = $val; + $char->{int_bonus} = $val2; + debug "Intelligence: $val + $val2\n", "parseMsg"; + } elsif ($type == VAR_DEX) { + $char->{dex} = $val; + $char->{dex_bonus} = $val2; + debug "Dexterity: $val + $val2\n", "parseMsg"; + } elsif ($type == VAR_LUK) { + $char->{luk} = $val; + $char->{luk_bonus} = $val2; + debug "Luck: $val + $val2\n", "parseMsg"; + } + $char->inventory->onStatInfo2() if (!$masterServer->{itemListType}); + +} +# Notifies clients in an area, that an other visible object is walking (ZC_NOTIFY_PLAYERMOVE). +# 0086 <id>.L <walk data>.6B <walk start time>.L +# Note: unit must not be self +*actor_exists = *actor_display_compatibility; +*actor_connected = *actor_display_compatibility; +*actor_moved = *actor_display_compatibility; +*actor_spawned = *actor_display_compatibility; +sub actor_display_compatibility { + my ($self, $args) = @_; + # compatibility; TODO do it in PacketParser->parse? + Plugins::callHook('packet_pre/actor_display', $args); + &actor_display unless $args->{return}; + Plugins::callHook('packet/actor_display', $args); +} + +# This function is a merge of actor_exists, actor_connected, actor_moved, etc... +sub actor_display { + my ($self, $args) = @_; + return unless changeToInGameState(); + my ($actor, $mustAdd); + + #### Initialize #### + + my $nameID = unpack("V", $args->{ID}); + my $name = bytesToString($args->{name}); + $name =~ s/^\s+|\s+$//g; + + if ($args->{switch} eq "0086") { + # Message 0086 contains less information about the actor than other similar + # messages. So we use the existing actor information. + my $coordsArg = $args->{coords}; + my $tickArg = $args->{tick}; + $args = Actor::get($args->{ID})->deepCopy(); + # Here we overwrite the $args data with the 0086 packet data. + $args->{switch} = "0086"; + $args->{coords} = $coordsArg; + $args->{tick} = $tickArg; # lol tickcount what do we do with that? debug "tick: " . $tickArg/1000/3600/24 . "\n"; + } + + my (%coordsFrom, %coordsTo); + if (length $args->{coords} == 6) { + # Actor Moved + makeCoordsFromTo(\%coordsFrom, \%coordsTo, $args->{coords}); # body dir will be calculated using the vector + } else { + # Actor Spawned/Exists + makeCoordsDir(\%coordsTo, $args->{coords}, \$args->{body_dir}); + %coordsFrom = %coordsTo; + } + +=pod + # Zealotus bug + if ($args->{type} == 1200) { + open DUMP, ">> test_Zealotus.txt"; + print DUMP "Zealotus: " . $nameID . "\n"; + print DUMP Dumper($args); + close DUMP; + } +=cut + + #### Step 0: determine object type #### + my $object_class; + if (defined $args->{object_type}) { + if ($args->{type} == 45) { # portals use the same object_type as NPCs + $object_class = 'Actor::Portal'; + } else { + $object_class = { + PC_TYPE, 'Actor::Player', + # NPC_TYPE? # not encountered, NPCs are NPC_EVT_TYPE + # SKILL_TYPE? # not encountered + # UNKNOWN_TYPE? # not encountered + NPC_MOB_TYPE, 'Actor::Monster', + NPC_EVT_TYPE, 'Actor::NPC', # both NPCs and portals + NPC_PET_TYPE, 'Actor::Pet', + NPC_HO_TYPE, 'Actor::Slave::Homunculus', + NPC_MERSOL_TYPE, 'Actor::Slave::Mercenary', + # NPC_ELEMENTAL_TYPE, 'Actor::Elemental', # Sorcerer's Spirit + NPC_TYPE2, 'Actor::NPC', + }->{$args->{object_type}}; + } + + } + + unless (defined $object_class) { + if ($jobs_lut{$args->{type}}) { + if ($args->{type} <= 6000) { + $object_class = 'Actor::Player'; + } elsif (($args->{type} >= 6001 && $args->{type} <= 6016) || ($args->{type} >= 6048 && $args->{type} <= 6052)) { + $object_class = 'Actor::Slave::Homunculus'; + } elsif ($$args->{type} >= 6017 && $$args->{type} <= 6046) { + $object_class = 'Actor::Slave::Mercenary'; + } else { + $object_class = 'Actor::Slave::Unknown'; + } + } elsif ($args->{type} == 45) { + $object_class = 'Actor::Portal'; + + } elsif ($args->{type} >= 1000) { + if ($args->{hair_style} == 0x64) { + $object_class = 'Actor::Pet'; + } else { + $object_class = 'Actor::Monster'; + } + } else { # ($args->{type} < 1000 && $args->{type} != 45 && !$jobs_lut{$args->{type}}) + $object_class = 'Actor::NPC'; + } + } + + #### Step 1: create the actor object #### + + if ($object_class eq 'Actor::Player') { + # Actor is a player + $actor = $playersList->getByID($args->{ID}); + if (!defined $actor) { + $actor = new Actor::Player(); + $actor->{appear_time} = time; + # New actor_display packets include the player's name + $actor->setName($name) if defined $name; + $mustAdd = 1; + } + $actor->{nameID} = $nameID; + } elsif ($object_class eq 'Actor::Slave') { + require ErrorHandler; + die "Unset Actor::Slave type, this shouldn't happen\n"; + } elsif ($object_class eq 'Actor::Slave::Homunculus' || $object_class eq 'Actor::Slave::Mercenary' || $object_class eq 'Actor::Slave::Unknown') { + # Actor is a homunculus or a mercenary + $actor = $slavesList->getByID($args->{ID}); + if (!defined $actor) { + if ($char->{slaves} && $char->{slaves}{$args->{ID}}) { + $actor = $char->{slaves}{$args->{ID}}; + } elsif ($char->{homunculus} && $char->{homunculus}{ID} && $char->{homunculus}{ID} eq $args->{ID}) { + $actor = $char->{homunculus}; + } elsif ($char->{mercenary} && ($char->{mercenary}{ID} && $char->{mercenary}{ID} eq $args->{ID})) { + $actor = $char->{mercenary}; + } else { + $actor = $object_class->new(); + } + + $actor->{appear_time} = time; + $actor->{name_given} = $name if defined $name; + $actor->{jobID} = $args->{type} if exists $args->{type}; + $mustAdd = 1; + } + $actor->{nameID} = $nameID; + } elsif ($object_class eq 'Actor::Portal') { + # Actor is a portal + $actor = $portalsList->getByID($args->{ID}); + if (!defined $actor) { + $actor = new Actor::Portal(); + $actor->{appear_time} = time; + my $exists = portalExists($field->baseName, \%coordsTo); + $actor->{source}{map} = $field->baseName; + if ($exists ne "") { + $actor->setName("$portals_lut{$exists}{source}{map} -> " . getPortalDestName($exists)); + } + $mustAdd = 1; + + # Strangely enough, portals (like all other actors) have names, too. + # We _could_ send a "actor_info_request" packet to find the names of each portal, + # however I see no gain from this. (And it might even provide another way of private + # servers to auto-ban bots.) + } + $actor->{nameID} = $nameID; + } elsif ($object_class eq 'Actor::Pet') { + # Actor is a pet + $actor = $petsList->getByID($args->{ID}); + if (!defined $actor) { + $actor = new Actor::Pet(); + $actor->{appear_time} = time; + $actor->setName($name) if defined $name; +# if ($monsters_lut{$args->{type}}) { +# $actor->setName($monsters_lut{$args->{type}}); +# } + $actor->{name_given} = defined $name ? $name : T("Unknown"); + $mustAdd = 1; + + # Previously identified monsters could suddenly be identified as pets. + if ($monstersList->getByID($args->{ID})) { + $monstersList->removeByID($args->{ID}); + } + + # Why do monsters and pets use nameID as type? + $actor->{nameID} = $args->{type}; + + } + } elsif ($object_class eq 'Actor::Monster') { + $actor = $monstersList->getByID($args->{ID}); + if (!defined $actor) { + $actor = new Actor::Monster(); + $actor->{appear_time} = time; + if ($monsters_lut{$args->{type}}) { + $actor->setName($monsters_lut{$args->{type}}); + } + # New actor_display packets include the Monster name + $actor->setName($name) if defined $name; + $actor->{name_given} = "Unknown"; + $actor->{binType} = $args->{type}; + $mustAdd = 1; + + # Why do monsters and pets use nameID as type? + $actor->{nameID} = $args->{type}; + } + } elsif ($object_class eq 'Actor::NPC') { + # Actor is an NPC + $actor = $npcsList->getByID($args->{ID}); + if (!defined $actor) { + $actor = new Actor::NPC(); + $actor->{appear_time} = time; + $actor->setName($name) if defined $name; + $mustAdd = 1; + } + $actor->{nameID} = $nameID; + } elsif ($object_class eq 'Actor::Elemental') { + # Actor is a Elemental + $actor = $elementalsList->getByID($args->{ID}); + if (!defined $actor) { + $actor = new Actor::Elemental(); + $actor->{appear_time} = time; + $mustAdd = 1; + } + + $actor->setName($jobs_lut{$args->{type}}); + } + + #### Step 2: update actor information #### + $actor->{ID} = $args->{ID}; + $actor->{charID} = $args->{charID} if $args->{charID} && $args->{charID} ne "\0\0\0\0"; + $actor->{jobID} = $args->{type}; + $actor->{type} = $args->{type}; + $actor->{lv} = $args->{lv}; + $actor->{walk_speed} = $args->{walk_speed} / 1000 if (exists $args->{walk_speed} && $args->{switch} ne "0086"); + $actor->{len} = $args->{len} if $args->{len}; + # 0086 would need that? + $actor->{object_type} = $args->{object_type} if (defined $args->{object_type}); + + # Remove actors that are located outside the map + # This may be caused by: + # - server sending us false actors + # - actor packets not being parsed correctly + if (defined $field && ($field->isOffMap($coordsFrom{x}, $coordsFrom{y}) || $field->isOffMap($coordsTo{x}, $coordsTo{y}))) { + warning TF("Ignoring actor with off map coordinates: (%d, %d)->(%d, %d), field max: (%d, %d)\n",$coordsFrom{x},$coordsFrom{y},$coordsTo{x},$coordsTo{y},$field->width(),$field->height()); + $actor->{avoid} = 1; + return; + } + + if ( ($coordsFrom{x} == 0 && $coordsFrom{y} == 0) || ($coordsTo{x} == 0 && $coordsTo{y} == 0) || + (blockDistance(\%coordsFrom, \%coordsTo) > $config{clientSight}) ) { + warning TF("Ignoring bugged actor moved packet (%s) (%d, %d)->(%d, %d)\n", $args->{switch}, $coordsFrom{x}, $coordsFrom{y}, $coordsTo{x}, $coordsTo{y}); + # seems this is just a position bug, lets just ignore the change in position + # $actor->{avoid} = 1; + return; + } + + $actor->{pos} = {%coordsFrom}; + $actor->{pos_to} = {%coordsTo}; + $actor->{time_move} = time; + $actor->{time_move_calc} = calcTime(\%coordsFrom, \%coordsTo, $actor->{walk_speed}); + + + if (UNIVERSAL::isa($actor, "Actor::Player")) { + # None of this stuff should matter if the actor isn't a player... => does matter for a guildflag npc! + + # Interesting note about emblemID. If it is 0 (or none), the Ragnarok + # client will display "Send (Player) a guild invitation" (assuming one has + # invitation priveledges), regardless of whether or not guildID is set. + # I bet that this is yet another brilliant "feature" by GRAVITY's good programmers. + $actor->{emblemID} = $args->{emblemID} if (exists $args->{emblemID}); + $actor->{guildID} = $args->{guildID} if (exists $args->{guildID}); + + if (exists $args->{lowhead}) { + $actor->{headgear}{low} = $args->{lowhead}; + $actor->{headgear}{mid} = $args->{midhead}; + $actor->{headgear}{top} = $args->{tophead}; + $actor->{weapon} = $args->{weapon}; + $actor->{shield} = $args->{shield}; + } + + $actor->{sex} = $args->{sex}; + + if ($args->{act} == 1) { + $actor->{dead} = 1; + } elsif ($args->{act} == 2) { + $actor->{sitting} = 1; + } + + # Monsters don't have hair colors or heads to look around... + $actor->{hair_color} = $args->{hair_color} if (exists $args->{hair_color}); + + } elsif (UNIVERSAL::isa($actor, "Actor::NPC") && $args->{type} == 722) { # guild flag has emblem + # odd fact: "this data can also be found in a strange place: + # (shield OR lowhead) + midhead = emblemID (either shield or lowhead depending on the packet) + # tophead = guildID + $actor->{emblemID} = $args->{emblemID}; + $actor->{guildID} = $args->{guildID}; + } + + # But hair_style is used for pets, and their bodies can look different ways... + $actor->{hair_style} = $args->{hair_style} if (exists $args->{hair_style}); + $actor->{look}{body} = $args->{body_dir} if (exists $args->{body_dir}); + $actor->{look}{head} = $args->{head_dir} if (exists $args->{head_dir}); + + # When stance is non-zero, character is bobbing as if they had just got hit, + # but the cursor also turns to a sword when they are mouse-overed. + #$actor->{stance} = $args->{stance} if (exists $args->{stance}); + + # Visual effects are a set of flags (some of the packets don't have this argument) + $actor->{opt3} = $args->{opt3} if (exists $args->{opt3}); # stackable + + # Known visual effects: + # 0x0001 = Yellow tint (eg, a quicken skill) + # 0x0002 = Red tint (eg, power-thrust) + # 0x0004 = Gray tint (eg, energy coat) + # 0x0008 = Slow lightning (eg, mental strength) + # 0x0010 = Fast lightning (eg, MVP fury) + # 0x0020 = Black non-moving statue (eg, stone curse) + # 0x0040 = Translucent weapon + # 0x0080 = Translucent red sprite (eg, marionette control?) + # 0x0100 = Spaztastic weapon image (eg, mystical amplification) + # 0x0200 = Gigantic glowy sphere-thing + # 0x0400 = Translucent pink sprite (eg, marionette control?) + # 0x0800 = Glowy sprite outline (eg, assumptio) + # 0x1000 = Bright red sprite, slowly moving red lightning (eg, MVP fury?) + # 0x2000 = Vortex-type effect + + # Note that these are flags, and you can mix and match them + # Example: 0x000C (0x0008 & 0x0004) = gray tint with slow lightning + +=pod +typedef enum <unnamed-tag> { + SHOW_EFST_NORMAL = 0x0, + SHOW_EFST_QUICKEN = 0x1, + SHOW_EFST_OVERTHRUST = 0x2, + SHOW_EFST_ENERGYCOAT = 0x4, + SHOW_EFST_EXPLOSIONSPIRITS = 0x8, + SHOW_EFST_STEELBODY = 0x10, + SHOW_EFST_BLADESTOP = 0x20, + SHOW_EFST_AURABLADE = 0x40, + SHOW_EFST_REDBODY = 0x80, + SHOW_EFST_LIGHTBLADE = 0x100, + SHOW_EFST_MOON = 0x200, + SHOW_EFST_PINKBODY = 0x400, + SHOW_EFST_ASSUMPTIO = 0x800, + SHOW_EFST_SUN_WARM = 0x1000, + SHOW_EFST_REFLECT = 0x2000, + SHOW_EFST_BUNSIN = 0x4000, + SHOW_EFST_SOULLINK = 0x8000, + SHOW_EFST_UNDEAD = 0x10000, + SHOW_EFST_CONTRACT = 0x20000, +} <unnamed-tag>; +=cut + + # Save these parameters ... + $actor->{opt1} = $args->{opt1}; # nonstackable + $actor->{opt2} = $args->{opt2}; # stackable + $actor->{option} = $args->{option}; # stackable + + # And use them to set status flags. + if (setStatus($actor, $args->{opt1}, $args->{opt2}, $args->{option})) { + $mustAdd = 0; + } + + #### Step 3: Add actor to actor list #### + if ($mustAdd) { + if (UNIVERSAL::isa($actor, "Actor::Player")) { + $playersList->add($actor); + Plugins::callHook('add_player_list', $actor); + + } elsif (UNIVERSAL::isa($actor, "Actor::Monster")) { + $monstersList->add($actor); + Plugins::callHook('add_monster_list', $actor); + + } elsif (UNIVERSAL::isa($actor, "Actor::Pet")) { + $petsList->add($actor); + Plugins::callHook('add_pet_list', $actor); + + } elsif (UNIVERSAL::isa($actor, "Actor::Portal")) { + $portalsList->add($actor); + Plugins::callHook('add_portal_list', $actor); + + } elsif (UNIVERSAL::isa($actor, "Actor::NPC")) { + my $ID = $args->{ID}; + my $location = $field->baseName . " $actor->{pos}{x} $actor->{pos}{y}"; + if ($npcs_lut{$location}) { + $actor->setName($npcs_lut{$location}); + } + $npcsList->add($actor); + Plugins::callHook('add_npc_list', $actor); + + } elsif (UNIVERSAL::isa($actor, "Actor::Slave")) { + $slavesList->add($actor); + Plugins::callHook('add_slave_list', $actor); + } elsif (UNIVERSAL::isa($actor, "Actor::Elemental")) { + $elementalsList->add($actor); + Plugins::callHook('add_elemental_list', $actor); + + } + } + + #### Packet specific #### + if ($args->{switch} eq "0078" || + $args->{switch} eq "01D8" || + $args->{switch} eq "022A" || + $args->{switch} eq "02EE" || + $args->{switch} eq "07F9" || + $args->{switch} eq "0915" || + $args->{switch} eq "09DD" || + $args->{switch} eq "09FF" || + $packetParser->{packet_list}->{$args->{switch}}[0] eq "actor_exists") { + # Actor Exists (standing) + + if ($actor->isa('Actor::Player')) { + my $domain = existsInList($config{friendlyAID}, unpack("V", $actor->{ID})) ? 'parseMsg_presence' : 'parseMsg_presence/player'; + debug "Player Exists: " . $actor->name . " ($actor->{binID}) Level $actor->{lv} $sex_lut{$actor->{sex}} $jobs_lut{$actor->{jobID}} ($coordsFrom{x}, $coordsFrom{y})\n", $domain; + + playerLog("player " .$actor->{name} ." is near (" .$field->{baseName} .", lvl=" .$actor->{lv} .", job=" .$jobs_lut{$actor->{jobID}} .")") if (!$field->isCity); + Plugins::callHook('player', {player => $actor}); #backwards compatibility + + Plugins::callHook('player_exist', {player => $actor}); + + } elsif ($actor->isa('Actor::NPC')) { + message TF("NPC Exists: %s (%d, %d) (ID %d) - (%d)\n", $actor->name, $actor->{pos_to}{x}, $actor->{pos_to}{y}, $actor->{nameID}, $actor->{binID}), ($config{showDomain_NPC}?$config{showDomain_NPC}:"parseMsg_presence"), 1; + Plugins::callHook('npc_exist', {npc => $actor}); + + } elsif ($actor->isa('Actor::Portal')) { + message TF("Portal Exists: %s (%s, %s) - (%s)\n", $actor->name, $actor->{pos_to}{x}, $actor->{pos_to}{y}, $actor->{binID}), "portals", 1; + Plugins::callHook('portal_exist', {portal => $actor}); + + } elsif ($actor->isa('Actor::Monster')) { + debug sprintf("Monster Exists: %s (%d)\n", $actor->name, $actor->{binID}), "parseMsg_presence", 1; + Plugins::callHook('monster_exist', {monster => $actor}); + + } elsif ($actor->isa('Actor::Pet')) { + debug sprintf("Pet Exists: %s (%d)\n", $actor->name, $actor->{binID}), "parseMsg_presence", 1; + Plugins::callHook('pet_exist', {pet => $actor}); + + } elsif ($actor->isa('Actor::Slave')) { + debug sprintf("Slave Exists: %s (%d)\n", $actor->name, $actor->{binID}), "parseMsg_presence", 1; + Plugins::callHook('slave_exist', {slave => $actor}); + + } elsif ($actor->isa('Actor::Elemental')) { + debug sprintf("Elemental Exists: %s (%d)\n", $actor->name, $actor->{binID}), "parseMsg_presence", 1; + Plugins::callHook('elemental_exist', {elemental => $actor}); + + } else { + debug sprintf("Unknown Actor Exists: %s (%d)\n", $actor->name, $actor->{binID}), "parseMsg_presence", 1; + Plugins::callHook('unknown_exist', {unknown => $actor}); + } + + } elsif ($args->{switch} eq "0079" || + $args->{switch} eq "01DB" || + $args->{switch} eq "022B" || + $args->{switch} eq "02ED" || + $args->{switch} eq "01D9" || + $args->{switch} eq "07F8" || + $args->{switch} eq "0858" || + $args->{switch} eq "090F" || + $args->{switch} eq "09DC" || + $args->{switch} eq "09FE" || + $packetParser->{packet_list}->{$args->{switch}}[0] eq "actor_connected") { + # Actor Connected (new) + + if ($actor->isa('Actor::Player')) { + my $domain = existsInList($config{friendlyAID}, unpack("V", $args->{ID})) ? 'parseMsg_presence' : 'parseMsg_presence/player'; + debug "Player Connected: ".$actor->name." ($actor->{binID}) Level $args->{lv} $sex_lut{$actor->{sex}} $jobs_lut{$actor->{jobID}} ($coordsTo{x}, $coordsTo{y})\n", $domain; + + playerLog("player " .$actor->{name} ." is near (" .$field->{baseName} .", lvl=" .$actor->{lv} .", job=" .$jobs_lut{$actor->{jobID}} .")") if (!$field->isCity); + Plugins::callHook('player', {player => $actor}); #backwards compatibailty + + Plugins::callHook('player_connected', {player => $actor}); + } else { + debug "Unknown Connected: $args->{type} - \n", "parseMsg"; + Plugins::callHook('unknown_connected', {unknown => $actor}); + } + + } elsif ($args->{switch} eq "007B" || + $args->{switch} eq "0086" || + $args->{switch} eq "01DA" || + $args->{switch} eq "022C" || + $args->{switch} eq "02EC" || + $args->{switch} eq "07F7" || + $args->{switch} eq "0856" || + $args->{switch} eq "0914" || + $args->{switch} eq "09DB" || + $args->{switch} eq "09FD" || + $packetParser->{packet_list}->{$args->{switch}}[0] eq "actor_moved") { + # Actor Moved + + # Correct the direction in which they're looking + my %vec; + getVector(\%vec, \%coordsTo, \%coordsFrom); + my $direction = int sprintf("%.0f", (360 - vectorToDegree(\%vec)) / 45); + + $actor->{look}{body} = $direction; + $actor->{look}{head} = 0; + + if ($actor->isa('Actor::Player')) { + debug "Player Moved: " . $actor->name . " ($actor->{binID}) Level $actor->{lv} $sex_lut{$actor->{sex}} $jobs_lut{$actor->{jobID}} - ($coordsFrom{x}, $coordsFrom{y}) -> ($coordsTo{x}, $coordsTo{y})\n", "parseMsg"; + Plugins::callHook('player_moved', $actor); + } elsif ($actor->isa('Actor::Monster')) { + debug "Monster Moved: " . $actor->nameIdx . " - ($coordsFrom{x}, $coordsFrom{y}) -> ($coordsTo{x}, $coordsTo{y})\n", "parseMsg"; + Plugins::callHook('monster_moved', $actor); + } elsif ($actor->isa('Actor::Pet')) { + debug "Pet Moved: " . $actor->nameIdx . " - ($coordsFrom{x}, $coordsFrom{y}) -> ($coordsTo{x}, $coordsTo{y})\n", "parseMsg"; + Plugins::callHook('pet_moved', $actor); + } elsif ($actor->isa('Actor::Slave')) { + debug "Slave Moved: " . $actor->nameIdx . " - ($coordsFrom{x}, $coordsFrom{y}) -> ($coordsTo{x}, $coordsTo{y})\n", "parseMsg"; + Plugins::callHook('slave_moved', $actor); + } elsif ($actor->isa('Actor::Portal')) { + # This can never happen of course. + debug "Portal Moved: " . $actor->nameIdx . " - ($coordsFrom{x}, $coordsFrom{y}) -> ($coordsTo{x}, $coordsTo{y})\n", "parseMsg"; + Plugins::callHook('portal_moved', $actor); + } elsif ($actor->isa('Actor::NPC')) { + # Neither can this. + debug "NPC Moved: " . $actor->nameIdx . " - ($coordsFrom{x}, $coordsFrom{y}) -> ($coordsTo{x}, $coordsTo{y})\n", "parseMsg"; + Plugins::callHook('npc_moved', $actor); + } elsif ($actor->isa('Actor::Elemental')) { + debug "Elemental Moved: " . $actor->nameIdx . " - ($coordsFrom{x}, $coordsFrom{y}) -> ($coordsTo{x}, $coordsTo{y})\n", "parseMsg"; + Plugins::callHook('pet_moved', $actor); + } else { + debug "Unknown Actor Moved: " . $actor->nameIdx . " - ($coordsFrom{x}, $coordsFrom{y}) -> ($coordsTo{x}, $coordsTo{y})\n", "parseMsg"; + Plugins::callHook('unknown_moved', $actor); + } + + } elsif ($args->{switch} eq "007C") { + # Actor Spawned + if ($actor->isa('Actor::Player')) { + debug "Player Spawned: " . $actor->nameIdx . " $sex_lut{$actor->{sex}} $jobs_lut{$actor->{jobID}}\n", "parseMsg"; + Plugins::callHook('player_spawned', {player => $actor}); + } elsif ($actor->isa('Actor::Monster')) { + debug "Monster Spawned: " . $actor->nameIdx . "\n", "parseMsg"; + Plugins::callHook('monster_spawned', {monster => $actor}); + } elsif ($actor->isa('Actor::Pet')) { + debug "Pet Spawned: " . $actor->nameIdx . "\n", "parseMsg"; + Plugins::callHook('pet_spawned', {pet => $actor}); + } elsif ($actor->isa('Actor::Slave')) { + debug "Slave Spawned: " . $actor->nameIdx . " $jobs_lut{$actor->{jobID}}\n", "parseMsg"; + Plugins::callHook('slave_spawned', {slave => $actor}); + } elsif ($actor->isa('Actor::Portal')) { + # Can this happen? + debug "Portal Spawned: " . $actor->nameIdx . "\n", "parseMsg"; + Plugins::callHook('portal_spawned', {portal => $actor}); + } elsif ($actor->isa('Actor::Elemental')) { + debug "Elemental Spawned: " . $actor->nameIdx . "\n", "parseMsg"; + Plugins::callHook('elemental_spawned', {elemental => $actor}); + } elsif ($actor->isa('NPC')) { + debug "NPC Spawned: " . $actor->nameIdx . "\n", "parseMsg"; + Plugins::callHook('npc_spawned', {npc => $actor}); + } else { + debug "Unknown Spawned: " . $actor->nameIdx . "\n", "parseMsg"; + Plugins::callHook('unknown_spawned', {unknown => $actor}); + } + } + + if ($char->{elemental}{ID} eq $actor->{ID}) { + $char->{elemental} = $actor; + } +} + +# Makes a unit (char, npc, mob, homun) disappear to all clients in area (ZC_NOTIFY_VANISH). +# 0080 <id>.L <type>.B +# type: +# 0 = out of sight +# 1 = died +# 2 = logged out +# 3 = teleport +# 4 = trickdead +sub actor_died_or_disappeared { + my ($self,$args) = @_; + return unless changeToInGameState(); + my $ID = $args->{ID}; + avoidList_ID($ID); + + if ($ID eq $accountID) { + message T("You have died\n") if (!$char->{dead}); + Plugins::callHook('self_died'); + closeShop() unless !$shopstarted || $config{'dcOnDeath'} == -1 || AI::state == AI::OFF; + $char->{deathCount}++; + $char->{dead} = 1; + $char->{dead_time} = time; + if ($char->{equipment}{arrow} && $char->{equipment}{arrow}{type} == 19) { + delete $char->{equipment}{arrow}; + } + + } elsif (defined $monstersList->getByID($ID)) { + my $monster = $monstersList->getByID($ID); + if ($args->{type} == 0) { + debug "Monster Disappeared: " . $monster->name . " ($monster->{binID})\n", "parseMsg_presence"; + $monster->{disappeared} = 1; + + } elsif ($args->{type} == 1) { + debug "Monster Died: " . $monster->name . " ($monster->{binID})\n", "parseMsg_damage"; + $monster->{dead} = 1; + + if ((AI::action ne "attack" || AI::args(0)->{ID} eq $ID) && + ($config{itemsTakeAuto_party} && + ($monster->{dmgFromParty} > 0 || + $monster->{dmgFromYou} > 0))) { + AI::clear("items_take"); + ai_items_take($monster->{pos}{x}, $monster->{pos}{y}, + $monster->{pos_to}{x}, $monster->{pos_to}{y}); + } + + } elsif ($args->{type} == 2) { # What's this? + debug "Monster Disappeared: " . $monster->name . " ($monster->{binID})\n", "parseMsg_presence"; + $monster->{disappeared} = 1; + + } elsif ($args->{type} == 3) { + debug "Monster Teleported: " . $monster->name . " ($monster->{binID})\n", "parseMsg_presence"; + $monster->{teleported} = 1; + } + + $monster->{gone_time} = time; + $monsters_old{$ID} = $monster->deepCopy(); + Plugins::callHook('monster_disappeared', {monster => $monster}); + $monstersList->remove($monster); + + } elsif (defined $playersList->getByID($ID)) { + my $player = $playersList->getByID($ID); + if ($args->{type} == 1) { + message TF("Player Died: %s (%d) %s %s\n", $player->name, $player->{binID}, $sex_lut{$player->{sex}}, $jobs_lut{$player->{jobID}}); + $player->{dead} = 1; + $player->{dead_time} = time; + } else { + if ($args->{type} == 0) { + debug "Player Disappeared: " . $player->name . " ($player->{binID}) $sex_lut{$player->{sex}} $jobs_lut{$player->{jobID}} ($player->{pos_to}{x}, $player->{pos_to}{y})\n", "parseMsg_presence"; + $player->{disappeared} = 1; + } elsif ($args->{type} == 2) { + debug "Player Disconnected: ".$player->name." ($player->{binID}) $sex_lut{$player->{sex}} $jobs_lut{$player->{jobID}} ($player->{pos_to}{x}, $player->{pos_to}{y})\n", "parseMsg_presence"; + $player->{disconnected} = 1; + } elsif ($args->{type} == 3) { + debug "Player Teleported: ".$player->name." ($player->{binID}) $sex_lut{$player->{sex}} $jobs_lut{$player->{jobID}} ($player->{pos_to}{x}, $player->{pos_to}{y})\n", "parseMsg_presence"; + $player->{teleported} = 1; + } else { + debug "Player Disappeared in an unknown way: ".$player->name." ($player->{binID}) $sex_lut{$player->{sex}} $jobs_lut{$player->{jobID}}\n", "parseMsg_presence"; + $player->{disappeared} = 1; + } + + if (grep { $ID eq $_ } @venderListsID) { + binRemove(\@venderListsID, $ID); + delete $venderLists{$ID}; + } + + if (grep { $ID eq $_ } @buyerListsID) { + binRemove(\@buyerListsID, $ID); + delete $buyerLists{$ID}; + } + + $player->{gone_time} = time; + $players_old{$ID} = $player->deepCopy(); + Plugins::callHook('player_disappeared', {player => $player}); + + $playersList->remove($player); + } + + } elsif ($players_old{$ID}) { + if ($args->{type} == 2) { + debug "Player Disconnected: " . $players_old{$ID}->name . "\n", "parseMsg_presence"; + $players_old{$ID}{disconnected} = 1; + } elsif ($args->{type} == 3) { + debug "Player Teleported: " . $players_old{$ID}->name . "\n", "parseMsg_presence"; + $players_old{$ID}{teleported} = 1; + } + + } elsif (defined $portalsList->getByID($ID)) { + my $portal = $portalsList->getByID($ID); + debug "Portal Disappeared: " . $portal->name . " ($portal->{binID})\n", "parseMsg"; + $portal->{disappeared} = 1; + $portal->{gone_time} = time; + $portals_old{$ID} = $portal->deepCopy(); + Plugins::callHook('portal_disappeared', {portal => $portal}); + $portalsList->remove($portal); + + } elsif (defined $npcsList->getByID($ID)) { + my $npc = $npcsList->getByID($ID); + debug "NPC Disappeared: " . $npc->name . " ($npc->{nameID})\n", "parseMsg"; + $npc->{disappeared} = 1; + $npc->{gone_time} = time; + $npcs_old{$ID} = $npc->deepCopy(); + Plugins::callHook('npc_disappeared', {npc => $npc}); + $npcsList->remove($npc); + + } elsif (defined $petsList->getByID($ID)) { + my $pet = $petsList->getByID($ID); + debug "Pet Disappeared: " . $pet->name . " ($pet->{binID})\n", "parseMsg"; + $pet->{disappeared} = 1; + $pet->{gone_time} = time; + Plugins::callHook('pet_disappeared', {pet => $pet}); + $petsList->remove($pet); + + } elsif (defined $slavesList->getByID($ID)) { + my $slave = $slavesList->getByID($ID); + if ($args->{type} == 1) { + message TF("Slave Died: %s (%d) %s\n", $slave->name, $slave->{binID}, $slave->{actorType}); + if (isMySlaveID($ID)) { + if ($slave->isa("AI::Slave::Homunculus") || $slave->isa("Actor::Slave::Homunculus")) { + $char->{homunculus_info}{dead} = 1; + AI::SlaveManager::removeSlave($slave) if ($char->has_homunculus); + + } elsif ($slave->isa("AI::Slave::Mercenary") || $slave->isa("Actor::Slave::Mercenary")) { + AI::SlaveManager::removeSlave($slave) if ($char->has_mercenary); + } + } + } else { + if ($args->{type} == 0) { + debug "Slave Disappeared: " . $slave->name . " ($slave->{binID}) $slave->{actorType} ($slave->{pos_to}{x}, $slave->{pos_to}{y})\n", "parseMsg_presence"; + $slave->{disappeared} = 1; + } elsif ($args->{type} == 2) { + debug "Slave Disconnected: ".$slave->name." ($slave->{binID}) $slave->{actorType} ($slave->{pos_to}{x}, $slave->{pos_to}{y})\n", "parseMsg_presence"; + $slave->{disconnected} = 1; + } elsif ($args->{type} == 3) { + debug "Slave Teleported: ".$slave->name." ($slave->{binID}) $slave->{actorType} ($slave->{pos_to}{x}, $slave->{pos_to}{y})\n", "parseMsg_presence"; + $slave->{teleported} = 1; + } else { + debug "Slave Disappeared in an unknown way: ".$slave->name." ($slave->{binID}) $slave->{actorType}\n", "parseMsg_presence"; + $slave->{disappeared} = 1; + } + + $slave->{gone_time} = time; + Plugins::callHook('slave_disappeared', {slave => $slave}); + } + + $slavesList->remove($slave); + + } elsif (defined $elementalsList->getByID($ID)) { + my $elemental = $elementalsList->getByID($ID); + if ($args->{type} == 0) { + message "Elemental Disappeared: " .$elemental->{name}. " ($elemental->{binID}) $elemental->{actorType} ($elemental->{pos_to}{x}, $elemental->{pos_to}{y})\n", "parseMsg_presence"; + $elemental->{disappeared} = 1; + } else { + debug "Elemental Disappeared in an unknown way: ".$elemental->{name}." ($elemental->{binID}) $elemental->{actorType}\n", "parseMsg_presence"; + $elemental->{disappeared} = 1; + } + + $elemental->{gone_time} = time; + Plugins::callHook('elemental_disappeared', {elemental => $elemental}); + + if ($char->{elemental}{ID} eq $ID) { + $char->{elemental} = undef; + } + + $elementalsList->remove($elemental); + + } else { + debug "Unknown Disappeared: ".getHex($ID)."\n", "parseMsg"; + } +} + +sub actor_action { + my ($self,$args) = @_; + return unless changeToInGameState(); + + $args->{damage} = intToSignedShort($args->{damage}); + if ($args->{type} == ACTION_ITEMPICKUP) { + # Take item + my $source = Actor::get($args->{sourceID}); + my $verb = $source->verb('pick up', 'picks up'); + my $target = getActorName($args->{targetID}); + debug "$source $verb $target\n", 'parseMsg_presence'; + + my $item = $itemsList->getByID($args->{targetID}); + $item->{takenBy} = $args->{sourceID} if ($item); + + } elsif ($args->{type} == ACTION_SIT) { + # Sit + my ($source, $verb) = getActorNames($args->{sourceID}, 0, 'are', 'is'); + if ($args->{sourceID} eq $accountID) { + message T("You are sitting.\n") if (!$char->{sitting}); + $char->{sitting} = 1; + if ($ai_v{sitAuto_pendingLook}) { + my $pendingLook = delete $ai_v{sitAuto_pendingLook}; + delete $ai_v{sitAuto_scheduledLook}; + my $delay = ($pendingLook->{delay} && $pendingLook->{delay} > 0) ? $pendingLook->{delay} : 0; + if ($delay > 0) { + $ai_v{sitAuto_scheduledLook} = { + due => time + $delay, + body => $pendingLook->{body}, + head => $pendingLook->{head}, + }; + } elsif (defined $pendingLook->{head}) { + Misc::look($pendingLook->{body}, $pendingLook->{head}); + } elsif (defined $pendingLook->{body}) { + Misc::look($pendingLook->{body}); + } + } + AI::queue("sitAuto") unless (AI::inQueue("sitAuto")) || $ai_v{sitAuto_forcedBySitCommand}; + } else { + message TF("%s is sitting.\n", getActorName($args->{sourceID})), 'parseMsg_statuslook', 2; + my $player = $playersList->getByID($args->{sourceID}); + $player->{sitting} = 1 if ($player); + } + Misc::checkValidity("actor_action (take item)"); + + } elsif ($args->{type} == ACTION_STAND) { + # Stand + my ($source, $verb) = getActorNames($args->{sourceID}, 0, 'are', 'is'); + if ($args->{sourceID} eq $accountID) { + message T("You are standing.\n") if ($char->{sitting}); + if ($config{sitAuto_idle}) { + $timeout{ai_sit_idle}{time} = time; + } + delete $ai_v{sitAuto_forcedBySitCommand} if $ai_v{sitAuto_forcedBySitCommand}; + $char->{sitting} = 0; + } else { + message TF("%s is standing.\n", getActorName($args->{sourceID})), 'parseMsg_statuslook', 2; + my $player = $playersList->getByID($args->{sourceID}); + $player->{sitting} = 0 if ($player); + } + Misc::checkValidity("actor_action (stand)"); + + } else { + # Attack + my $dmgdisplay; + my $totalDamage = $args->{damage} + $args->{dual_wield_damage}; + if ($totalDamage == 0) { + $dmgdisplay = T("Miss!"); + $dmgdisplay .= "!" if ($args->{type} == ACTION_ATTACK_LUCKY); # lucky dodge + } else { + $dmgdisplay = $args->{div} > 1 + ? sprintf '%d*%d', $args->{damage} / $args->{div}, $args->{div} + : $args->{damage} + ; + $dmgdisplay .= "!" if ($args->{type} == ACTION_ATTACK_CRITICAL); # critical hit + $dmgdisplay .= " + $args->{dual_wield_damage}" if $args->{dual_wield_damage}; + } + + Misc::checkValidity("actor_action (attack 1)"); + + updateDamageTables($args->{sourceID}, $args->{targetID}, $totalDamage); + + Misc::checkValidity("actor_action (attack 2)"); + + my $source = Actor::get($args->{sourceID}); + my $target = Actor::get($args->{targetID}); + my $verb = $source->verb('attack', 'attacks'); + + $target->{sitting} = 0 unless $args->{type} == ACTION_ATTACK_NOMOTION || $args->{type} == ACTION_ATTACK_MULTIPLE_NOMOTION || $totalDamage == 0; + + my $msg = attack_string($source, $target, $dmgdisplay, ($args->{src_speed})); + Plugins::callHook('packet_attack', { + sourceID => $args->{sourceID}, + targetID => $args->{targetID}, + msg => \$msg, + dmg => $totalDamage, + type => $args->{type} + }); + + my $status = sprintf("[%3d/%3d]", percent_hp($char), percent_sp($char)); + + Misc::checkValidity("actor_action (attack 3)"); + + if ($args->{sourceID} eq $accountID) { + message("$status $msg", $totalDamage > 0 ? "attackMon" : "attackMonMiss"); + if ($startedattack) { + $monstarttime = time(); + $monkilltime = time(); + $startedattack = 0; + } + Misc::checkValidity("actor_action (attack 4)"); + calcStat($args->{damage}); + Misc::checkValidity("actor_action (attack 5)"); + + } elsif ($args->{targetID} eq $accountID) { + message("$status $msg", $args->{damage} > 0 ? "attacked" : "attackedMiss"); + if ($args->{damage} > 0) { + $damageTaken{$source->{name}}{attack} += $args->{damage}; + } + + } elsif ($char->{slaves} && $char->{slaves}{$args->{sourceID}}) { + message(sprintf("[%3d/%3d]", $char->{slaves}{$args->{sourceID}}->hp_percent, $char->{slaves}{$args->{sourceID}}->sp_percent) . " $msg", $totalDamage > 0 ? "attackMon" : "attackMonMiss"); + + } elsif ($char->{slaves} && $char->{slaves}{$args->{targetID}}) { + message(sprintf("[%3d/%3d]", $char->{slaves}{$args->{targetID}}->hp_percent, $char->{slaves}{$args->{targetID}}->sp_percent) . " $msg", $args->{damage} > 0 ? "attacked" : "attackedMiss"); + + } elsif ($args->{sourceID} eq $args->{targetID}) { + message("$status $msg"); + + } elsif ($config{showAllDamage}) { + message("$status $msg"); + + } else { + debug("$msg", 'parseMsg_damage'); + } + + Misc::checkValidity("actor_action (attack 6)"); + } +} + +sub actor_info { + my ($self, $args) = @_; + return unless changeToInGameState(); + my $name = bytesToString($args->{name}); + $name =~ s/^\s+|\s+$//g; + debug "Received object info: $name\n", "parseMsg_presence/name", 2; + my $player = $playersList->getByID($args->{ID}); + if ($player) { + # 0095: This packet tells us the names of players who aren't in a guild. + # 0195: Receive names of players who are in a guild. + # FIXME: There is more to this packet than just party name and guild name. + # This packet is received when you leave a guild + # (with cryptic party and guild name fields, at least for now) + $player->setName($name); + $player->{info} = 1; + + $player->{party}{name} = bytesToString($args->{partyName}) if defined $args->{partyName}; + $player->{guild}{name} = bytesToString($args->{guildName}) if defined $args->{guildName}; + $player->{guild}{title} = bytesToString($args->{guildTitle}) if defined $args->{guildTitle}; + $player->{title}{ID} = $args->{titleID} if defined $args->{titleID}; + message "Player Info: " . $player->nameIdx . "\n", "parseMsg_presence", 2; + updatePlayerNameCache($player); + playerLog("player " .$player->{name} ." is near (" .$field->{baseName} .", lvl=" .$player->{lv} .", job=" .$jobs_lut{$player->{jobID}} .")") if (!$field->isCity); + Plugins::callHook('charNameUpdate', {player => $player}); + } + + my $monster = $monstersList->getByID($args->{ID}); + if ($monster) { + debug "Monster Info: $name ($monster->{binID})\n", "parseMsg", 2; + $monster->{name_given} = $name; + $monster->{info} = 1; + if ($monsters_lut{$monster->{nameID}} eq "") { + $monster->setName($name); + $monsters_lut{$monster->{nameID}} = $name; + updateMonsterLUT(Settings::getTableFilename("monsters.txt"), $monster->{nameID}, $name); + Plugins::callHook('mobNameUpdate', {monster => $monster}); + } + } + + my $npc = $npcs{$args->{ID}}; + if ($npc) { + $npc->setName($name); + $npc->{info} = 1; + if ($config{debug} >= 2) { + my $binID = binFind(\@npcsID, $args->{ID}); + debug "NPC Info: $npc->{name} ($binID)\n", "parseMsg", 2; + } + + my $location = $field->baseName . " $npc->{pos}{x} $npc->{pos}{y}"; + if (!$npcs_lut{$location}) { + $npcs_lut{$location} = $npc->{name}; + updateNPCLUT(Settings::getTableFilename("npcs.txt"), $location, $npc->{name}); + } + Plugins::callHook('npcNameUpdate', {npc => $npc}); + } + + my $pet = $pets{$args->{ID}}; + if ($pet) { + $pet->{name_given} = $name; + $pet->setName($name); + $pet->{info} = 1; + if ($config{debug} >= 2) { + my $binID = binFind(\@petsID, $args->{ID}); + debug "Pet Info: $pet->{name_given} ($binID)\n", "parseMsg", 2; + } + Plugins::callHook('petNameUpdate', {pet => $pet}); + } + + my $slave = $slavesList->getByID($args->{ID}); + if ($slave) { + $slave->{name_given} = $name; + $slave->setName($name); + $slave->{info} = 1; + my $binID = binFind(\@slavesID, $args->{ID}); + debug "Slave Info: $name ($binID)\n", "parseMsg_presence", 2; + updatePlayerNameCache($slave); + Plugins::callHook('slaveNameUpdate', {slave => $slave}); + } + + my $elemental = $elementals{$args->{ID}}; + if ($elemental) { + $elemental->{name_given} = $name; + $elemental->setName($name); + $elemental->{info} = 1; + if ($config{debug} >= 2) { + my $binID = binFind(\@elementalsID, $args->{ID}); + debug "elemental Info: $elemental->{name_given} ($binID)\n", "parseMsg", 2; + } + Plugins::callHook('elementalNameUpdate', {elemental => $elemental}); + } + + # TODO: $args->{ID} eq $accountID +} + +# Notifies clients in the area about an special/visual effect (ZC_NOTIFY_EFFECT). +# 019B <id>.L <effect id>.L +# effect id: +# 0 = base level up +# 1 = job level up +# 2 = refine failure +# 3 = refine success +# 4 = game over +# 5 = pharmacy success +# 6 = pharmacy failure +# 7 = base level up (super novice) +# 8 = job level up (super novice) +# 9 = base level up (taekwon) +sub unit_levelup { + my ($self, $args) = @_; + + my $ID = $args->{ID}; + my $type = $args->{type}; + my $actor = Actor::get($ID); + if ($type == LEVELUP_EFFECT || $type == LEVELUP_EFFECT2 || $type == LEVELUP_EFFECT3) { + message TF("%s gained a level!\n", $actor); + Plugins::callHook('base_level', {name => $actor}); + } elsif ($type == JOBLEVELUP_EFFECT || $type == JOBLEVELUP_EFFECT2) { + message TF("%s gained a job level!\n", $actor); + Plugins::callHook('job_level', {name => $actor}); + } elsif ($type == REFINING_FAIL_EFFECT) { + message TF("%s failed to refine a weapon!\n", $actor), "refine"; + } elsif ($type == REFINING_SUCCESS_EFFECT) { + message TF("%s successfully refined a weapon!\n", $actor), "refine"; + } elsif ($type == MAKEITEM_AM_SUCCESS_EFFECT) { + message TF("%s successfully created a potion!\n", $actor), "refine"; + } elsif ($type == MAKEITEM_AM_FAIL_EFFECT) { + message TF("%s failed to create a potion!\n", $actor), "refine"; + } elsif ($type == GAME_OVER_EFFECT) { + message TF("%s received GAME OVER!\n", $actor); + } else { + message TF("%s unknown unit_levelup effect (%d)\n", $actor, $type); + } +} + +use constant QTYPE => ( + 0x0 => [0xff, 0xff, 0, 0], + 0x1 => [0xff, 0x80, 0, 0], + 0x2 => [0, 0xff, 0, 0], + 0x3 => [0x80, 0, 0x80, 0], +); + +sub parse_minimap_indicator { + my ($self, $args) = @_; + + $args->{actor} = Actor::get($args->{npcID}); + $args->{show} = $args->{type} != 2; + + unless (defined $args->{red}) { + @{$args}{qw(red green blue alpha)} = @{{QTYPE}->{$args->{qtype}} || [0xff, 0xff, 0xff, 0]}; + } + + # FIXME: packet 0144: coordinates are missing now when clearing indicators; ID is used + # Wx depends on coordinates there +} + +sub account_payment_info { + my ($self, $args) = @_; + my $D_minute = $args->{D_minute}; + my $H_minute = $args->{H_minute}; + + my $D_d = int($D_minute / 1440); + my $D_h = int(($D_minute % 1440) / 60); + my $D_m = int(($D_minute % 1440) % 60); + + my $H_d = int($H_minute / 1440); + my $H_h = int(($H_minute % 1440) / 60); + my $H_m = int(($H_minute % 1440) % 60); + + my $msg = center(T(" Account payment information "), 56, '-') ."\n" . + TF("Pay per day : %s day(s) %s hour(s) and %s minute(s)\n", $D_d, $D_h, $D_m). + TF("Pay per hour : %s day(s) %s hour(s) and %s minute(s)\n", $H_d, $H_h, $H_m); + $msg .= ('-'x56) . "\n"; + message $msg, "info"; +} + +# TODO +sub reconstruct_minimap_indicator { +} + +# Sends information about owned homunculus to the client . [orn] +# 022e <name>.24B <modified>.B <level>.W <hunger>.W <intimacy>.W <equip id>.W <atk>.W <matk>.W <hit>.W <crit>.W <def>.W <mdef>.W <flee>.W <aspd>.W <hp>.W <max hp>.W <sp>.W <max sp>.W <exp>.L <max exp>.L <skill points>.W <atk range>.W (ZC_PROPERTY_HOMUN) +# 09f7 <name>.24B <modified>.B <level>.W <hunger>.W <intimacy>.W <equip id>.W <atk>.W <matk>.W <hit>.W <crit>.W <def>.W <mdef>.W <flee>.W <aspd>.W <hp>.L <max hp>.L <sp>.W <max sp>.W <exp>.L <max exp>.L <skill points>.W <atk range>.W (ZC_PROPERTY_HOMUN_2) +# 0b2f <name>.24B <modified>.B <level>.W <hunger>.W <intimacy>.W <atk>.W <matk>.W <hit>.W <crit>.W <def>.W <mdef>.W <flee>.W <aspd>.W <hp>.L <max hp>.L <sp>.W <max sp>.W <exp>.L <max exp>.L <skill points>.W <atk range>.W (ZC_PROPERTY_HOMUN3) type 1 +# 0b76 <name>.24B <modified>.B <level>.W <hunger>.W <intimacy>.W <atk>.W <matk>.W <hit>.W <crit>.W <def>.W <mdef>.W <flee>.W <aspd>.W <hp>.L <max hp>.L <sp>.L <max sp>.L <exp>.L <max exp>.L <skill points>.W <atk range>.W (ZC_PROPERTY_HOMUN3) type 2 +# 0ba4 <name>.24B <modified>.B <level>.W <hunger>.W <intimacy>.W <atk>.W <matk>.W <hit>.W <crit>.W <def>.W <mdef>.W <flee>.W <aspd>.W <hp>.L <max hp>.L <sp>.L <max sp>.L <exp>.eL <max exp>.eL <skill points>.W <atk range>.W (ZC_PROPERTY_HOMUN4) +sub homunculus_property { + my ($self, $args) = @_; + + return 0 unless enforce_homun_state(); + + my $slave = $char->{homunculus}; + $slave->setName(bytesToString($args->{name})); + + slave_calcproperty_handler($slave, $args); + homunculus_state_handler($slave, $args); + + foreach (@{$args->{KEYS}}) { + $slave->{$_} = $args->{$_}; + } + + # ST0's counterpart for ST kRO, since it attempts to support all servers + # TODO: we do this for homunculus, mercenary and our char... make 1 function and pass actor and attack_range? + # or make function in Actor class + if ($config{homunculus_attackDistanceAuto} && exists $slave->{attack_range}) { + if($config{homunculus_attackDistance} > $slave->{attack_range}) { # decrease attack range if necessary + configModify('homunculus_attackDistance', $slave->{attack_range}, 1); + message TF("Autodetected attackDistance for homunculus = %s\n", $config{homunculus_attackDistance}), "success"; + } + if ($config{homunculus_attackMaxDistance} != $slave->{attack_range}) { # set max distance using information coming from the server + configModify('homunculus_attackMaxDistance', $slave->{attack_range}, 1); + message TF("Autodetected homunculus_attackMaxDistance for homunculus = %s\n", $config{homunculus_attackMaxDistance}), "success"; + } + } +} + +sub enforce_homun_state { + return 0 unless ($char); + if (exists $char->{homunculus} && defined $char->{homunculus}) { + return 1; + } else { + debug "[Homunculus] Received homunculus property without the homunculus objetive existing, creating a temporary one.\n"; + $char->{homunculus} = Actor::new('Actor::Slave::Homunculus'); + return 1; + } +} + +sub homunculus_state_handler { + my ($slave, $args) = @_; + # Homunculus states bit: + # 0 - alive, unnamed and not vaporized + # 1 - named + # 2 - rest + # 4 - dead + + if (!defined $slave->{state}) { + if ($args->{state} & 1) { + $char->{homunculus_info}{renameflag} = 1; + message T("Your Homunculus has already been renamed\n"), 'homunculus'; + } else { + $char->{homunculus_info}{renameflag} = 0; + message T("Your Homunculus has not been renamed\n"), 'homunculus'; + } + + if ($args->{state} & 2) { + $char->{homunculus_info}{vaporized} = 1; + message T("Your Homunculus is vaporized\n"), 'homunculus'; + } else { + $char->{homunculus_info}{vaporized} = 0; + message T("Your Homunculus is not vaporized\n"), 'homunculus'; + } + + if ($args->{state} & 4) { + $char->{homunculus_info}{dead} = 0; + message T("Your Homunculus is not dead\n"), 'homunculus'; + } else { + $char->{homunculus_info}{dead} = 1; + message T("Your Homunculus is dead\n"), 'homunculus'; + } + + } elsif (defined $slave->{state} && $slave->{state} != $args->{state}) { + if (($args->{state} & 1) && !($slave->{state} & 1)) { + $char->{homunculus_info}{renameflag} = 1; + message T("Your Homunculus was renamed\n"), 'homunculus'; + } + + if (($args->{state} & 2) && !($slave->{state} & 2)) { + $char->{homunculus_info}{vaporized} = 1; + message T("Your Homunculus was vaporized!\n"), 'homunculus'; + } + + if (($args->{state} & 4) && !($slave->{state} & 4)) { + $char->{homunculus_info}{dead} = 0; + message T("Your Homunculus was resurrected!\n"), 'homunculus'; + } + + if (!($args->{state} & 1) && ($slave->{state} & 1)) { + $char->{homunculus_info}{renameflag} = 0; + message T("Your Homunculus was un-renamed? lol\n"), 'homunculus'; + } + + if (!($args->{state} & 2) && ($slave->{state} & 2)) { + $char->{homunculus_info}{vaporized} = 0; + message T("Your Homunculus was recalled!\n"), 'homunculus'; + } + + if (!($args->{state} & 4) && ($slave->{state} & 4)) { + $char->{homunculus_info}{dead} = 1; + message T("Your Homunculus died!\n"), 'homunculus'; + } + } +} + +use constant { + HO_PRE_INIT => 0x0, + HO_RELATIONSHIP_CHANGED => 0x1, + HO_FULLNESS_CHANGED => 0x2, + HO_ACCESSORY_CHANGED => 0x3, + HO_HEADTYPE_CHANGED => 0x4, +}; + +# Notification about a change in homunuculus' state (ZC_CHANGESTATE_MER). +# 0230 <type>.B <state>.B <id>.L <data>.L +# type: +# unused +# state: +# 0 = pre-init +# 1 = intimacy +# 2 = hunger +# 3 = accessory? +# ? = ignored +sub homunculus_info { + my ($self, $args) = @_; + debug "homunculus_info type: $args->{type}\n", "homunculus"; + if ($args->{state} == HO_PRE_INIT) { + + # Some servers won't send 'homunculus_property' after a teleport, so we don't delete $char->{homunculus} object + if ($char->{homunculus_info}{dead} == 1) { + debug "[Homunculus] We received a homunculus_info packet while our homunculus is dead, assume it was resurrected.\n"; + $char->{homunculus_info}{dead} = 0; + } + + $char->{homunculus} = Actor::get($args->{ID}) if ($char->{homunculus}{ID} ne $args->{ID}); + $char->{homunculus}{map} = $field->baseName; + unless ($char->{slaves}{$char->{homunculus}{ID}}) { + if ($char->{homunculus}->isa('AI::Slave::Homunculus')) { + # After a teleport the homunculus object is still AI::Slave::Homunculus, but AI::SlaveManager::addSlave requires it to be Actor::Slave::Homunculus, so we change it back + bless $char->{homunculus}, 'Actor::Slave::Homunculus'; + } + if (!$char->has_homunculus) { + debug "[Homunculus] Adding homunculus to SlaveManager after homunculus_info packet.\n"; + AI::SlaveManager::addSlave($char->{homunculus}); + } + $char->{homunculus}{appear_time} = time; + } + } elsif ($args->{state} == HO_RELATIONSHIP_CHANGED) { + $char->{homunculus}{intimacy} = $args->{val} if $char->{homunculus}; + } elsif ($args->{state} == HO_FULLNESS_CHANGED) { + $char->{homunculus}{hunger} = $args->{val} if $char->{homunculus}; + } elsif ($args->{state} == HO_ACCESSORY_CHANGED) { + $char->{homunculus}{accessory} = $args->{val} if $char->{homunculus}; + } elsif ($args->{state} == HO_HEADTYPE_CHANGED) { + # + } +} + +# Marks a position on client's minimap (ZC_COMPASS). +# 0144 <npc id>.L <type>.L <x>.L <y>.L <id>.B <color>.L +# +# Notification about an NPC's quest state (ZC_QUEST_NOTIFY_EFFECT). +# 0446 <npc id>.L <x>.W <y>.W <effect>.W <color>.W +## +# minimap_indicator({bool show, Actor actor, int x, int y, int red, int green, int blue, int alpha [, int effect]}) +# show: whether indicator is shown or cleared +# actor: @MODULE(Actor) who issued the indicator; or which Actor it's binded to +# x, y: indicator coordinates +# red, green, blue, alpha: indicator color +# effect: unknown, may be missing +# +# Minimap indicator. +sub minimap_indicator { + my ($self, $args) = @_; + + my $marker_type = defined $args->{type} + ? "type:$args->{type}" + : "effect:" . (defined $args->{effect} ? $args->{effect} : '-'); + my $marker_key = join(':', + $args->{show} ? 1 : 0, + $marker_type, + map { defined $_ ? $_ : '-' } @{$args}{qw(x y red green blue alpha)} + ); + + if ($minimap_indicator_seen{$marker_key}) { + return; + } + $minimap_indicator_seen{$marker_key} = 1; + + my $color_str = "[R:$args->{red}, G:$args->{green}, B:$args->{blue}, A:$args->{alpha}]"; + my $indicator = T("minimap indicator"); + if (defined $args->{type}) { + unless ($args->{type} == 1 || $args->{type} == 2) { + $indicator .= TF(" (unknown type %d)", $args->{type}); + } + } elsif (defined $args->{effect}) { + if ($args->{effect} == 1) { + $indicator = T("*Quest!*"); + } elsif ($args->{effect} == 9999) { + return; + } elsif ($args->{effect}) { # 0 is no effect + $indicator = TF("unknown effect %d", $args->{effect}); + } + } + + if ($args->{show}) { + message TF("%s shown %s at location %d, %d " . + "with the color %s\n", $args->{actor}, $indicator, @{$args}{qw(x y)}, $color_str), + 'minimap_indicator'; + } else { + message TF("%s cleared %s at location %d, %d " . + "with the color %s\n", $args->{actor}, $indicator, @{$args}{qw(x y)}, $color_str), + 'minimap_indicator'; + } +} + +# 0x01B3 +sub parse_npc_image { + my ($self, $args) = @_; + + $args->{npc_image} = bytesToString($args->{npc_image}); +} + +sub reconstruct_npc_image { + my ($self, $args) = @_; + + $args->{npc_image} = stringToBytes($args->{npc_image}); +} + +# Displays an illustration image. +# 0145 <image name>.16B <type>.B (ZC_SHOW_IMAGE) +# 01b3 <image name>.64B <type>.B (ZC_SHOW_IMAGE2) +sub npc_image { + my ($self, $args) = @_; + + if ($args->{type} == 2) { + message TF("NPC image: %s\n", $args->{npc_image}), 'npc'; + } elsif ($args->{type} == 255) { + debug "Hide NPC image: $args->{npc_image}\n", "parseMsg"; + } else { + message TF("NPC image: %s (unknown type %s)\n", $args->{npc_image}, $args->{type}), 'npc'; + } + + unless ($args->{type} == 255) { + $talk{image} = $args->{npc_image}; + } else { + delete $talk{image}; + } +} + +# Send broadcast message with font formatting (ZC_BROADCAST2). +# 01C3 <packet len>.W <fontColor>.L <fontType>.W <fontSize>.W <fontAlign>.W <fontY>.W <message>.?B +sub local_broadcast { + my ($self, $args) = @_; + my $message = bytesToString($args->{message}); + my $color = uc(sprintf("%06x", $args->{color})); # hex code + stripLanguageCode(\$message); + chatLog("lb", "$message\n") if ($config{logLocalBroadcast}); + message "$message\n", "schat"; + Plugins::callHook('packet_localBroadcast', { + Msg => $message, + color => $color + }); +} + +sub parse_sage_autospell { + my ($self, $args) = @_; + + $args->{skills} = [map { Skill->new(idn => $_) } sort { $a<=>$b } grep {$_} + exists $args->{autoshadowspell_list} + ? (unpack 'v*', $args->{autoshadowspell_list}) + : (unpack 'V*', $args->{autospell_list}) + ]; +} + +sub reconstruct_sage_autospell { + my ($self, $args) = @_; + + my @skillIDs = map { $_->getIDN } $args->{skills}; + $args->{autoshadowspell_list} = pack 'v*', @skillIDs; + $args->{autospell_list} = pack 'V*', @skillIDs; +} + +## +# sage_autospell({arrayref skills, int why}) +# skills: list of @MODULE(Skill) instances +# why: unknown +# +# Skill list for Sage's Hindsight and Shadow Chaser's Auto Shadow Spell. +sub sage_autospell { + my ($self, $args) = @_; + + return unless $self->changeToInGameState; + + my $msg = center(' ' . T('Auto Spell') . ' ', 40, '-') . "\n" + . T(" # Skill\n") + . (join '', map { swrite '@>>> @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<', [$_->getIDN, $_] } @{$args->{skills}}) + . ('-'x40) . "\n"; + + message $msg, 'list'; + + if ($config{autoSpell}) { + my @autoSpells = split /\s*,\s*/, $config{autoSpell}; + for my $autoSpell (@autoSpells) { + my $skill = new Skill(auto => $autoSpell); + message 'Testing autoSpell ' . $autoSpell . "\n"; + if (!$config{autoSpell_safe} || List::Util::first { $_->getIDN == $skill->getIDN } @{$args->{skills}}) { + if (defined $args->{why}) { + $messageSender->sendSkillSelect($skill->getIDN, $args->{why}); + return; + } else { + $messageSender->sendAutoSpell($skill->getIDN); + return; + } + } + } + error TF("Configured autoSpell (%s) not available.\n", $config{autoSpell}); + message T("Disable autoSpell_safe to use it anyway.\n"), 'hint'; + } else { + message T("Configure autoSpell to automatically select skill for Auto Spell.\n"), 'hint'; + } +} + +# Sends info about a player's equipped items. +# 02D7 <packet len>.W <name>.24B <class>.W <hairstyle>.W <up-viewid>.W <mid-viewid>.W <low-viewid>.W <haircolor>.W <cloth-dye>.W <gender>.B {equip item}.26B* (ZC_EQUIPWIN_MICROSCOPE) +# 02D7 <packet len>.W <name>.24B <class>.W <hairstyle>.W <bottom-viewid>.W <mid-viewid>.W <up-viewid>.W <haircolor>.W <cloth-dye>.W <gender>.B {equip item}.28B* (ZC_EQUIPWIN_MICROSCOPE, PACKETVER >= 20100629) +# 0859 <packet len>.W <name>.24B <class>.W <hairstyle>.W <bottom-viewid>.W <mid-viewid>.W <up-viewid>.W <haircolor>.W <cloth-dye>.W <gender>.B {equip item}.28B* (ZC_EQUIPWIN_MICROSCOPE2, PACKETVER >= 20101124) +# 0859 <packet len>.W <name>.24B <class>.W <hairstyle>.W <bottom-viewid>.W <mid-viewid>.W <up-viewid>.W <robe>.W <haircolor>.W <cloth-dye>.W <gender>.B {equip item}.28B* (ZC_EQUIPWIN_MICROSCOPE2, PACKETVER >= 20110111) +# 0997 <packet len>.W <name>.24B <class>.W <hairstyle>.W <bottom-viewid>.W <mid-viewid>.W <up-viewid>.W <robe>.W <haircolor>.W <cloth-dye>.W <gender>.B {equip item}.31B* (ZC_EQUIPWIN_MICROSCOPE_V5, PACKETVER >= 20120925) +# 0A2D <packet len>.W <name>.24B <class>.W <hairstyle>.W <bottom-viewid>.W <mid-viewid>.W <up-viewid>.W <robe>.W <haircolor>.W <cloth-dye>.W <gender>.B {equip item}.57B* (ZC_EQUIPWIN_MICROSCOPE_V6, PACKETVER >= 20150225) +# 0B03 <packet len>.W <name>.24B <class>.W <hairstyle>.W <bottom-viewid>.W <mid-viewid>.W <up-viewid>.W <robe>.W <haircolor>.W <cloth-dye>.W <gender>.B {equip item}.57B* (ZC_EQUIPWIN_MICROSCOPE_V7, PACKETVER >= 201200000) +sub show_eq { + my ($self, $args) = @_; + my $item_info; + my @item; + + if ($args->{switch} eq '02D7') { # PACKETVER DEFAULT + $item_info = { + len => 26, + types => 'a2 v C2 v2 C2 a8 l v', + keys => [qw(ID nameID type identified type_equip equipped broken upgrade cards expire bindOnEquipType)], + }; + + if (exists $args->{robe}) { # PACKETVER >= 20100629 + $item_info->{type} .= 'v'; + $item_info->{len} += 2; + } + + } elsif ($args->{switch} eq '0906') { # PACKETVER >= ?? NOT IMPLEMENTED ON EATHENA BASED EMULATOR + $item_info = { + len => 27, + types => 'v2 C v2 C a8 l v2 C', + keys => [qw(ID nameID type type_equip equipped upgrade cards expire bindOnEquipType sprite_id identified)], + }; + + } elsif ($args->{switch} eq '0859') { # PACKETVER >= 20101124 + $item_info = { + len => 28, + types => 'a2 v C2 v2 C2 a8 l v2', + keys => [qw(ID nameID type identified type_equip equipped broken upgrade cards expire bindOnEquipType sprite_id)], + }; + + } elsif ($args->{switch} eq '0997') { # PACKETVER >= 20120925 + $item_info = { + len => 31, + types => 'a2 v C V2 C a8 l v2 C', + keys => [qw(ID nameID type type_equip equipped upgrade cards expire bindOnEquipType sprite_id identified)], + }; + + } elsif ($args->{switch} eq '0A2D') { # PACKETVER >= 20150226 + $item_info = { + len => 57, + types => 'a2 v C V2 C a8 l v2 C a25 C', + keys => [qw(ID nameID type type_equip equipped upgrade cards expire bindOnEquipType sprite_id num_options options identified)], + }; + } elsif ($args->{switch} eq '0B03') { # PACKETVER >= 20150226 + $item_info = { + len => 67, + types => 'a2 V C V2 C a16 l v2 C a25 C', + keys => [qw(ID nameID type type_equip equipped upgrade cards expire bindOnEquipType sprite_id num_options options identified)], + }; + } else { # this can't happen + return; + } + + my $name = bytesToString($args->{name}); + my $msg = center(" $name " . T("Equip Info") . " ", 50, '-') . "\n"; + for (my $i = 0; $i < length($args->{equips_info}); $i += $item_info->{len}) { + my $item; + @{$item}{@{$item_info->{keys}}} = unpack($item_info->{types}, substr($args->{equips_info}, $i, $item_info->{len})); + $item->{broken} = 0; + $item->{identified} = 1; + $msg .= sprintf("%-20s: %s\n", $equipTypes_lut{$item->{equipped}}, itemName($item)); + } + $msg .= sprintf("%s\n", ('-'x50)); + message($msg, "list"); +} + +# The player's 'Configuration' state, sent during login. +# 0A95 <show_eq flag>.B <call flag>.B +# 0AA8 <show_eq flag>.B <call flag>.B <pet autofeeding flag>.B +# 0ADC <show_eq flag>.B <call flag>.B <pet autofeeding flag>.B <homunculus autofeeding flag>.B +sub misc_config { + my ($self, $args) = @_; + + if (defined ($args->{show_eq_flag})) { + if ($args->{show_eq_flag} == 1) { + message T("Your Equipment information is now open to the public.\n"); + } else { + message T("Your Equipment information is now not open to the public.\n"); + } + } + + if (defined ($args->{call_flag})) { + if ($args->{call_flag} == 1) { + message T("Allowed being summoned by skills: Urgent Call, Marriage Skills, etc.\n"); + } else { + message T("Not Allowed being summoned by skills: Urgent Call, Marriage Skills, etc.\n"); + } + } + + if (defined ($args->{pet_autofeed_flag})) { + if ($args->{pet_autofeed_flag} == 1) { + message T("Pet automatic feeding is ON. (Ragexe Client Feature)\n"); + } else { + message T("Pet automatic feeding is OFF. (Ragexe Client Feature)\n"); + } + } + + if (defined ($args->{homunculus_autofeed_flag})) { + if ($args->{homunculus_autofeed_flag} == 1) { + message T("Homunculus automatic feeding is ON. (Ragexe Client Feature)\n"); + } else { + message T("Homunculus automatic feeding is OFF. (Ragexe Client Feature)\n"); + } + } +} + +# Send configurations (ZC_CONFIG). +# 02D9 <type>.L <value>.L +# type: +# 0 = show equip windows to other players +# 1 = being summoned by skills: Urgent Call, Romantic Rendezvous, Come to me, honey~ & Let's Go, Family! +# 2 = pet autofeeding +# 3 = homunculus autofeeding +# value: +# 0 = disabled +# 1 = enabled +sub misc_config_reply { + my ($self, $args) = @_; + + if ( $args->{type} == CONFIG_OPEN_EQUIPMENT_WINDOW ) { + if ($args->{flag}) { + message T("Your Equipment information is now open to the public.\n"); + } else { + message T("Your Equipment information is now not open to the public.\n"); + } + } elsif ( $args->{type} == CONFIG_CALL ) { + if ($args->{flag}) { + message T("Allowed being summoned by skills: Urgent Call, Marriage Skills, etc.\n"); + } else { + message T("Not Allowed being summoned by skills: Urgent Call, Marriage Skills, etc.\n"); + } + } elsif ( $args->{type} == CONFIG_PET_AUTOFEED ) { + if ($args->{flag}) { + message T("Pet automatic feeding is ON. (Ragexe Client Feature)\n"); + } else { + message T("Pet automatic feeding is OFF. (Ragexe Client Feature)\n"); + } + } elsif ( $args->{type} == CONFIG_HOMUNCULUS_AUTOFEED ) { + if ($args->{flag}) { + message T("Homunculus automatic feeding is ON. (Ragexe Client Feature)\n"); + } else { + message T("Homunculus automatic feeding is OFF. (Ragexe Client Feature)\n"); + } + } else { + message TF("Unknown Config Type: %s, Flag: %s\n", $args->{type}, $args->{flag}); + } +} + +sub show_eq_msg_self { + my ($self, $args) = @_; + if ($args->{type}) { + message T("Your Equipment information is now open to the public.\n"); + } else { + message T("Your Equipment information is now not open to the public.\n"); + } +} + +#08B3 +sub show_script { + my ($self, $args) = @_; + my $ID = $args->{ID}; + my $message = bytesToString($args->{message}); + if (defined $npcsList->getByID($ID)) { + my $npc = $npcsList->getByID($ID); + debug $npc->name . " ($npc->{nameID}): $message\n", 'parseMsg'; + Plugins::callHook('show_script', { + ID => $ID, + message => $message + }); + } +} + +# Skill cooldown display icon (ZC_SKILL_POSTDELAY). +# 043D <skill ID>.W <tick>.L +sub skill_post_delay { + my ($self, $args) = @_; + + my $skillName = (new Skill(idn => $args->{ID}))->getName; + my $status = defined $statusName{'EFST_DELAY'} ? $statusName{'EFST_DELAY'} : 'Delay'; + + $char->setStatus($skillName." ".$status, 1, $args->{time}); +} + +# Skill cooldown display icon List. +# 043E <len>.w { <skill ID>.W <tick>.L }* +# 0985 <len>.w { <skill ID>.W <total time>.L <tick>.L }* +sub skill_post_delaylist { + my ($self, $args) = @_; + + my $skill_post_delay_info; + if ($args->{switch} eq "0985") { # 0985 + $skill_post_delay_info = { + len => 10, + types => 'v V2', + keys => [qw(ID total_time remain_time)], + }; + + } else { # 043E + $skill_post_delay_info = { + len => 6, + types => 'v V', + keys => [qw(ID remain_time)], + }; + } + + for (my $i = 0; $i < length($args->{skill_list}); $i += $skill_post_delay_info->{len}) { + my $skill; + @{$skill}{@{$skill_post_delay_info->{keys}}} = unpack($skill_post_delay_info->{types}, substr($args->{skill_list}, $i, $skill_post_delay_info->{len})); + $skill->{name} = (new Skill(idn => $skill->{ID}))->getName; + my $status = defined $statusName{'EFST_DELAY'} ? $statusName{'EFST_DELAY'} : 'Delay'; + + $char->setStatus($skill->{name}." ".$status, 1, $skill->{remain_time}); + } +} + +# Displays a skill message (thanks to Rayce) (ZC_SKILLMSG). +# 0215 <msg id>.L +# msg id: +# 0x15 = End all negative status (PA_GOSPEL) +# 0x16 = Immunity to all status (PA_GOSPEL) +# 0x17 = MaxHP +100% (PA_GOSPEL) +# 0x18 = MaxSP +100% (PA_GOSPEL) +# 0x19 = All stats +20 (PA_GOSPEL) +# 0x1c = Enchant weapon with Holy element (PA_GOSPEL) +# 0x1d = Enchant armor with Holy element (PA_GOSPEL) +# 0x1e = DEF +25% (PA_GOSPEL) +# 0x1f = ATK +100% (PA_GOSPEL) +# 0x20 = HIT/Flee +50 (PA_GOSPEL) +# 0x28 = Full strip failed because of coating (ST_FULLSTRIP) +# ? = nothing +sub gospel_buff_aligned { + my ($self, $args) = @_; + my $status = unpack("V1", $args->{ID}); + + if ($status == 21) { + message T("All abnormal status effects have been removed.\n"), "info"; + } elsif ($status == 22) { + message T("You will be immune to abnormal status effects for the next minute.\n"), "info"; + } elsif ($status == 23) { + message T("Your Max HP will stay increased for the next minute.\n"), "info"; + } elsif ($status == 24) { + message T("Your Max SP will stay increased for the next minute.\n"), "info"; + } elsif ($status == 25) { + message T("All of your Stats will stay increased for the next minute.\n"), "info"; + } elsif ($status == 28) { + message T("Your weapon will remain blessed with Holy power for the next minute.\n"), "info"; + } elsif ($status == 29) { + message T("Your armor will remain blessed with Holy power for the next minute.\n"), "info"; + } elsif ($status == 30) { + message T("Your Defense will stay increased for the next 10 seconds.\n"), "info"; + } elsif ($status == 31) { + message T("Your Attack strength will stay increased for the next minute.\n"), "info"; + } elsif ($status == 32) { + message T("Your Accuracy and Flee Rate will stay increased for the next minute.\n"), "info"; + } else { + #message T("Unknown buff from Gospel: " . $status . "\n"), "info"; + } +} + +# TODO: known prefixes (chat domains): micc | ssss | blue | tool +# micc = micc<24 characters, this is the sender name. seems like it's null padded><hex color code><message> +# micc = Player Broadcast The struct: micc<23bytes player name+some hex><\x00><colour code><full message> +# The first player name is used for detecting the player name only according to the disassembled client. +# The full message contains the player name at the first 22 bytes +# TODO micc.* is currently unstricted, however .{24} and .{23} do not detect chinese with some reasons, please improve this regex if necessary +sub system_chat { + my ($self, $args) = @_; + my $message = bytesToString($args->{message}); + my $prefix; + my $color; + if ($message =~ s/^ssss//g) { # forces color yellow, or WoE indicator? + $prefix = T('[WoE]'); + } elsif ($message =~ /^micc.*\0\0([0-9a-fA-F]{6})(.*)/s) { #appears in twRO ## [micc][name][\x00\x00][unknown][\x00\x00][color][name][blablabla][message] + ($color, $message) = ($1, $2); + $prefix = T('[S]'); + } elsif ($message =~ /^micc.{12,24}([0-9a-fA-F]{6})(.*)/s) { + ($color, $message) = ($1, $2); + $prefix = T('[S]'); + } elsif ($message =~ s/^blue//g) { # forces color blue + $prefix = T('[S]'); + } elsif ($message =~ /^tool([0-9a-fA-F]{6})(.*)/s) { + ($color, $message) = ($1, $2); + $prefix = T('[S]'); + } else { + $prefix = T('[S]'); + } + $message =~ s/\000//g; # remove null charachters + $message =~ s/^ +//g; $message =~ s/ +$//g; # remove whitespace in the beginning and the end of $message + stripLanguageCode(\$message); + my $parsed_msg = solveMessage($message); + chatLog("s", "$parsed_msg\n") if ($config{logSystemChat}); + # Translation Comment: System/GM chat + message "$prefix $parsed_msg\n", "schat"; + ChatQueue::add('gm', undef, undef, $parsed_msg) if ($config{callSignGM}); + debug "schat: $message\n", "schat", 1; + + Plugins::callHook('packet_sysMsg', { + Msg => $parsed_msg, + RawMsg => $message, + MsgColor => $color, + MsgUser => undef # TODO: implement this value, we can get this from "micc" messages by regex. + }); +} + +sub warp_portal_list { + my ($self, $args) = @_; + + # strip gat extension + ($args->{memo1}) = $args->{memo1} =~ /^(.*)\.gat/; + ($args->{memo2}) = $args->{memo2} =~ /^(.*)\.gat/; + ($args->{memo3}) = $args->{memo3} =~ /^(.*)\.gat/; + ($args->{memo4}) = $args->{memo4} =~ /^(.*)\.gat/; + # Auto-detect saveMap + if ($args->{type} == 26) { + configModify('saveMap', $args->{memo2}) if ($args->{memo2} && $config{'saveMap'} ne $args->{memo2}); + } elsif ($args->{type} == 27) { + configModify('saveMap', $args->{memo1}) if ($args->{memo1} && $config{'saveMap'} ne $args->{memo1}); + configModify( "memo$_", $args->{"memo$_"} ) foreach grep { $args->{"memo$_"} ne $config{"memo$_"} } 1 .. 4; + } + + $char->{warp}{type} = $args->{type}; + undef @{$char->{warp}{memo}}; + push @{$char->{warp}{memo}}, $args->{memo1} if $args->{memo1} ne ""; + push @{$char->{warp}{memo}}, $args->{memo2} if $args->{memo2} ne ""; + push @{$char->{warp}{memo}}, $args->{memo3} if $args->{memo3} ne ""; + push @{$char->{warp}{memo}}, $args->{memo4} if $args->{memo4} ne ""; + + my $msg = center(T(" Warp Portal "), 50, '-') ."\n". + T("# Place Map\n"); + for (my $i = 0; $i < @{$char->{warp}{memo}}; $i++) { + $msg .= swrite( + "@< @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< @<<<<<<<<<<<<<<<", + [$i, $maps_lut{$char->{warp}{memo}[$i].'.rsw'}, $char->{warp}{memo}[$i]]); + } + $msg .= ('-'x50) . "\n"; + message $msg, "list"; + + if ($args->{type} == 26 && AI::inQueue('teleport')) { + # We have already successfully used the Teleport skill. + $messageSender->sendWarpTele(26, AI::args->{lv} == 2 ? "$config{saveMap}.gat" : "Random"); + AI::dequeue; + } +} + +# 0828,14 +sub char_delete2_result { + my ($self, $args) = @_; + my $result = $args->{result}; + my $deleteDate = $args->{deleteDate}; + + if ($result && $deleteDate) { + setCharDeleteDate($messageSender->{char_delete_slot}, $deleteDate); + message TF("Your character will be delete, left %s\n", $chars[$messageSender->{char_delete_slot}]{deleteDate}), "connection"; + } elsif ($result == 0) { + error T("That character already planned to be erased!\n"); + } elsif ($result == 3) { + error T("Error in database of the server!\n"); + } elsif ($result == 4) { + error T("To delete a character you must withdraw from the guild!\n"); + } elsif ($result == 5) { + error T("To delete a character you must withdraw from the party!\n"); + } else { + error TF("Unknown error when trying to delete the character! (Error number: %s)\n", $result); + } + + charSelectScreen; +} + +# 082A,10 +sub char_delete2_accept_result { + my ($self, $args) = @_; + my $charID = $args->{charID}; + my $result = $args->{result}; + + if ($result == 1) { # Success + if (defined $AI::temp::delIndex) { + message TF("Character %s (%d) deleted.\n", $chars[$AI::temp::delIndex]{name}, $AI::temp::delIndex), "info"; + delete $chars[$AI::temp::delIndex]; + undef $AI::temp::delIndex; + for (my $i = 0; $i < @chars; $i++) { + delete $chars[$i] if ($chars[$i] && !scalar(keys %{$chars[$i]})) + } + } else { + message T("Character deleted.\n"), "info"; + } + + if (charSelectScreen() == 1) { + $net->setState(3); + $firstLoginMap = 1; + $startingzeny = $chars[$config{'char'}]{'zeny'} unless defined $startingzeny; + $sentWelcomeMessage = 1; + } + return; + } elsif ($result == 0) { + error T("Enter your 6-digit birthday (YYMMDD) (e.g: 801122).\n"); + } elsif ($result == 2) { + error T("Due to system settings, can not be deleted.\n"); + } elsif ($result == 3) { + error T("A database error has occurred.\n"); + } elsif ($result == 4) { + error T("You cannot delete this character at the moment.\n"); + } elsif ($result == 5) { + error T("Your entered birthday does not match.\n"); + } elsif ($result == 7) { + error T("Character Deletion has failed because you have entered an incorrect e-mail address.\n"); + } else { + error TF("An unknown error has occurred. Error number %d\n", $result); + } + + undef $AI::temp::delIndex; + if (charSelectScreen() == 1) { + $net->setState(3); + $firstLoginMap = 1; + $startingzeny = $chars[$config{'char'}]{'zeny'} unless defined $startingzeny; + $sentWelcomeMessage = 1; + } +} + +# 082C,14 +sub char_delete2_cancel_result { + my ($self, $args) = @_; + my $result = $args->{result}; + + if ($result) { + message T("Character is no longer scheduled to be deleted\n"), "connection"; + $chars[$messageSender->{char_delete_slot}]{deleteDate} = ''; + } elsif ($result == 2) { + error T("Error in database of the server!\n"); + } else { + error TF("Unknown error when trying to cancel the deletion of the character! (Error number: %s)\n", $result); + } + + charSelectScreen; +} + +# 013C +sub arrow_equipped { + my ($self, $args) = @_; + return unless changeToInGameState(); + return unless $args->{ID}; + $char->{arrow} = $args->{ID}; + + my $item = $char->inventory->getByID($args->{ID}); + if ($item && $char->{equipment}{arrow} != $item) { + $char->{equipment}{arrow} = $item; + $item->{equipped} = 32768; + $ai_v{temp}{waitForEquip}-- if $ai_v{temp}{waitForEquip}; + message TF("Arrow/Bullet equipped: %s (%d) x %s\n", $item->{name}, $item->{binID}, $item->{amount}); + Plugins::callHook('equipped_item', { + slot => 'arrow', + item => $item + }); + } +} + +# Notifies the client, about a received inventory item or the result of a pick-up request. +# 00A0 <index>.W <amount>.W <name id>.W <identified>.B <damaged>.B <refine>.B <card1>.W <card2>.W <card3>.W <card4>.W <equip location>.W <item type>.B <result>.B (ZC_ITEM_PICKUP_ACK) +# 029A <index>.W <amount>.W <name id>.W <identified>.B <damaged>.B <refine>.B <card1>.W <card2>.W <card3>.W <card4>.W <equip location>.W <item type>.B <result>.B <expire time>.L (ZC_ITEM_PICKUP_ACK2) +# 02D4 <index>.W <amount>.W <name id>.W <identified>.B <damaged>.B <refine>.B <card1>.W <card2>.W <card3>.W <card4>.W <equip location>.W <item type>.B <result>.B <expire time>.L <bindOnEquipType>.W (ZC_ITEM_PICKUP_ACK3) +# 0990 <index>.W <amount>.W <name id>.W <identified>.B <damaged>.B <refine>.B <card1>.W <card2>.W <card3>.W <card4>.W <equip location>.L <item type>.B <result>.B <expire time>.L <bindOnEquipType>.W (ZC_ITEM_PICKUP_ACK_V5) +# 0A0C <index>.W <amount>.W <name id>.W <identified>.B <damaged>.B <refine>.B <card1>.W <card2>.W <card3>.W <card4>.W <equip location>.L <item type>.B <result>.B <expire time>.L <bindOnEquipType>.W { <option id>.W <option value>.W <option param>.B }*5 (ZC_ITEM_PICKUP_ACK_V6) +# 0A37 <index>.W <amount>.W <name id>.W <identified>.B <damaged>.B <refine>.B <card1>.W <card2>.W <card3>.W <card4>.W <equip location>.L <item type>.B <result>.B <expire time>.L <bindOnEquipType>.W { <option id>.W <option value>.W <option param>.B }*5 <favorite>.B <view id>.W (ZC_ITEM_PICKUP_ACK_V7) +sub inventory_item_added { + my ($self, $args) = @_; + + return unless changeToInGameState(); + + my ($index, $amount, $fail) = ($args->{ID}, $args->{amount}, $args->{fail}); + + if (!$fail) { + my $item = $char->inventory->getByID($index); + if (!$item) { + # Add new item + $item = new Actor::Item(); + $item->{ID} = $index; + $item->{nameID} = $args->{nameID}; + $item->{type} = $args->{type}; + $item->{type_equip} = $args->{type_equip}; + $item->{amount} = $amount; + $item->{identified} = $args->{identified}; + $item->{broken} = $args->{broken}; + $item->{upgrade} = $args->{upgrade}; + $item->{cards} = ($args->{switch} eq '029A') ? $args->{cards} + $args->{cards_ext}: $args->{cards}; + if ($args->{switch} eq '029A') { + $args->{cards} .= $args->{cards_ext}; + } elsif ($args->{switch} eq '02D4') { + $item->{expire} = $args->{expire} if (exists $args->{expire}); #a4 or V1 unpacking? + } + $item->{options} = $args->{options}; + $item->{name} = itemName($item); + $char->inventory->add($item); + } else { + # Add stackable item + $item->{amount} += $amount; + } + + $itemChange{$item->{name}} += $amount; + my $disp = TF("Item added to inventory: %s (%d) x %d - %s", + $item->{name}, $item->{binID}, $amount, $itemTypes_lut{$item->{type}}); + message "$disp\n", "drop"; + $disp .= " (". $field->baseName . ")\n"; + itemLog($disp); + + Plugins::callHook('item_gathered', { + item => $item->{name}, + amount => $amount + }); + + $args->{item} = $item; + + if (AI::state == AI::AUTO) { + # Auto-drop item + if (pickupitems($item->{name}, $item->{nameID}) == -1 && !AI::inQueue('storageAuto', 'buyAuto')) { + $messageSender->sendDrop($item->{ID}, $amount); + message TF("Auto-dropping item: %s (%d) x %d\n", $item->{name}, $item->{binID}, $amount), "drop"; + } + } + + } elsif ($fail == 6) { + message T("Can't loot item...wait...\n"), "drop"; + } elsif ($fail == 2) { + message T("Cannot pickup item (inventory full)\n"), "drop"; + } elsif ($fail == 1) { + message T("Cannot pickup item (you're Frozen?)\n"), "drop"; + } else { + message TF("Cannot pickup item (failure code %d)\n", $fail), "drop"; + } +} + +# 00AF, 07FA +sub inventory_item_removed { + my ($self, $args) = @_; + return unless changeToInGameState(); + my $item = $char->inventory->getByID($args->{ID}); + my $reason = $args->{reason}; + + if ($reason) { + if ($reason == 1) { + debug TF("%s was used to cast the skill\n", $item->{name}), "inventory", 1; + } elsif ($reason == 2) { + debug TF("%s broke due to the refinement failed\n", $item->{name}), "inventory", 1; + } elsif ($reason == 3) { + debug TF("%s used in a chemical reaction\n", $item->{name}), "inventory", 1; + } elsif ($reason == 4) { + debug TF("%s was moved to the storage\n", $item->{name}), "inventory", 1; + } elsif ($reason == 5) { + debug TF("%s was moved to the cart\n", $item->{name}), "inventory", 1; + } elsif ($reason == 6) { + debug TF("%s was sold\n", $item->{name}), "inventory", 1; + } elsif ($reason == 7) { + debug TF("%s was consumed by Four Spirit Analysis skill\n", $item->{name}), "inventory", 1; + } else { + debug TF("%s was consumed by an unknown reason (reason number %s)\n", $item->{name}, $reason), "inventory", 1; + } + } + + if ($item) { + inventoryItemRemoved($item->{binID}, $args->{amount}); + Plugins::callHook('packet_item_removed', {index => $item->{binID}}); + } +} + +# 0299 +sub rental_expired { + my ($self, $args) = @_; + my $item = $char->inventory->getByID($args->{ID}); + message TF("Rental item '%s' has expired!\n", itemNameSimple($args->{nameID})), "info"; + + if ($item) { + inventoryItemRemoved($item->{binID}, 1); + Plugins::callHook('rental_expired', { + index => $item->{binID}, + nameID => $item->{nameID} + }); + } +} + +# 012B +sub cart_off { + $char->cart->close; + message T("Cart released.\n"), "success"; +} + +# 012D +sub shop_skill { + my ($self, $args) = @_; + + # Used the shop skill. + my $number = $args->{number}; + message TF("You can sell %s items!\n", $number); +} + +# Your shop has sold an item -- one packet sent per item sold. +# +sub shop_sold { + my ($self, $args) = @_; + + # sold something + my $number = $args->{number}; + my $amount = $args->{amount}; + + $articles[$number]{sold} += $amount; + my $earned = $amount * $articles[$number]{price}; + $shopEarned += $earned; + $articles[$number]{quantity} -= $amount; + my $msg = TF("Sold: %s x %s - %sz\n", $articles[$number]{name}, $amount, $earned); + shopLog($msg) if $config{logShop}; + message($msg, "sold"); + + # Call hook before we possibly remove $articles[$number] or + # $articles itself as a result of the sale. + Plugins::callHook('vending_item_sold', { + 'vendShopIndex' => $number, + 'amount' => $amount, + 'vendArticle' => $articles[$number], #This is a hash + 'zenyEarned' => $earned, + 'packetType' => "short" + }); + + # Adjust the shop's articles for sale, and notify if the sold + # item and/or the whole shop has been sold out. + if ($articles[$number]{quantity} < 1) { + message TF("Sold out: %s\n", $articles[$number]{name}), "sold"; + Plugins::callHook('vending_item_sold_out', { + 'vendShopIndex' => $number, + 'vendArticle' => $articles[$number] + }); + #$articles[$number] = ""; + if (!--$articles){ + message T("Items have been sold out.\n"), "sold"; + closeShop(); + } + } +} + +sub shop_sold_long { + my ($self, $args) = @_; + + # sold something + my $number = $args->{number}; + my $amount = $args->{amount}; + my $earned = $args->{zeny}; + my $charID = getHex($args->{charID}); + my $when = $args->{time}; + + $articles[$number]{sold} += $amount; + $shopEarned += $earned; + $articles[$number]{quantity} -= $amount; + + my $msg = TF("Sold: %s x %s - %sz (Buyer charID: %s)\n", $articles[$number]{name}, $amount, $earned, $charID); + shopLog($msg) if $config{logShop}; + message("[" . getFormattedDate($when) . "] " . $msg, "sold"); + + # Call hook before we possibly remove $articles[$number] or + # $articles itself as a result of the sale. + Plugins::callHook('vending_item_sold', { + 'vendShopIndex' => $number, + 'amount' => $amount, + 'vendArticle' => $articles[$number], #This is a hash + 'buyerCharID' => $args->{charID}, + 'zenyEarned' => $earned, + 'time' => $when, + 'packetType' => "long" + }); + + # Adjust the shop's articles for sale, and notify if the sold + # item and/or the whole shop has been sold out. + if ($articles[$number]{quantity} < 1) { + message TF("Sold out: %s\n", $articles[$number]{name}), "sold"; + Plugins::callHook('vending_item_sold_out', { + 'vendShopIndex' => $number, + 'vendArticle' => $articles[$number] + }); + #$articles[$number] = ""; + if (!--$articles){ + message T("Items have been sold out.\n"), "sold"; + closeShop(); + } + } +} + +# TODO +sub vending_start { + my ($self, $args) = @_; + + my $item_pack = $self->{vender_items_list_item_pack_self} || $self->{vender_items_list_item_pack} || 'V v2 C v C3 a8'; + my $item_len = length pack $item_pack; + my $item_list_len = length $args->{itemList}; + #started a shop. + message TF("Shop '%s' opened!\n", $shop{title}), "success"; + @articles = (); + # FIXME: why do we need a seperate variable to track how many items are left in the store? + $articles = 0; + + # FIXME: Read the packet the server sends us to determine + # the shop title instead of using $shop{title}. + my $msg = center(" $shop{title} ", 83, '-') . "\n" . + T("# Name Type Price Amount\n"); + for (my $i = 0; $i < $item_list_len; $i += $item_len) { + my $item = {}; + @$item{qw( price number quantity type nameID identified broken upgrade cards options location sprite_id)} = unpack $item_pack, substr $args->{itemList}, $i, $item_len; + $item->{name} = itemName($item); + $articles[delete $item->{number}] = $item; + $articles++; + + debug ("Item added to Vender Store: $item->{name} - $item->{price} z\n", "vending", 2); + + $msg .= swrite( + "@< @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< @<<<<<<<<<<<<<< @>>>>>>>>>>>>z @<<<<<", + [$articles, $item->{name}, $itemTypes_lut{$item->{type}}, formatNumber($item->{price}), formatNumber($item->{quantity})]); + } + $msg .= ('-'x83) . "\n"; + message $msg, "list"; + $shopEarned ||= 0; +} + +sub vender_items_list { + my ($self, $args) = @_; + + $venderID = $args->{venderID}; + $venderCID = $args->{venderCID}; + + my $expireDate = 0; + my $item_pack = $self->{vender_items_list_item_pack} || 'V v2 C v C3 a8'; + my $item_len = length pack $item_pack; + my $item_list_len = length $args->{itemList}; + + my $player = Actor::get($args->{venderID}); + + $venderItemList->clear; + + my $msg = TF("%s\n" . + "# Name Type Price Amount\n", + center(' Vender: ' . $player->nameIdx . ' ', 88, '-')); + for (my $i = 0; $i < $item_list_len; $i+=$item_len) { + my $item = Actor::Item->new; + + @$item{qw( price amount ID type nameID identified broken upgrade cards options location sprite_id )} = unpack $item_pack, substr $args->{itemList}, $i, $item_len; + + $item->{name} = itemName($item); + $venderItemList->add($item); + + debug("Item added to Vender Store: $item->{name} - $item->{price} z\n", "vending", 2); + + Plugins::callHook('packet_vender_store', { item => $item }); + + $msg .= swrite( + "@< @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< @<<<<<<<<<<<<<<<<<<<< @>>>>>>>>>>>>z @<<<<<", + [$item->{binID}, $item->{name}, $itemTypes_lut{$item->{type}}, formatNumber($item->{price}), formatNumber($item->{amount})]); + } + $msg .= ('-'x88) . "\n"; + message $msg, $config{showDomain_Shop} || 'list'; + + if ($args->{expireDate}) { + $expireDate = $args->{expireDate}; + my $date = int(time) + int($args->{expireDate}/1000); + message "Expire Date: ".getFormattedDate($date)."\n"; + } + + Plugins::callHook('packet_vender_store2', { + venderID => $args->{venderID}, + venderCID => $args->{venderCID}, + itemList => $venderItemList, + expireDate => $expireDate + }); +} + +# 01D0 (Monk spirits), 01E1 (Gunslingers coins), 08CF (Kagerou/Oboro amulet spirit) +# Notifies the client of an object's spirits. +# 01D0 <id>.L <amount>.W (ZC_SPIRITS) +# 01E1 <id>.L <amount>.W (ZC_SPIRITS2) +# 08CF <id>.L <type>.W <amount>.W (ZC_SPIRITS3) +# 0B73 <id>.L <amount>.W (ZC_SPIRITS3) +sub revolving_entity { + my ($self, $args) = @_; + + # Monk Spirits or Gunslingers' coins or senior ninja + my $sourceID = $args->{sourceID}; + my $entityNum = $args->{entity}; + my $entityElement = $elements_lut{$args->{type}} if ($args->{type} && $entityNum); + my $entityType; + + my $actor = Actor::get($sourceID); + if ($args->{switch} eq '01D0') { + # Translation Comment: Spirit sphere of the monks + $entityType = T('spirit'); + } elsif ($args->{switch} eq '01E1') { + # Translation Comment: Coin of the gunslinger + $entityType = T('coin'); + } elsif ($args->{switch} eq '08CF') { + # Translation Comment: Amulet of the warlock + $entityType = T('amulet'); + } elsif ($args->{switch} eq '0B73') { + # Translation Comment: Soul Energy or Soul Reaper + $entityType = T('soul energy'); + } else { + $entityType = T('entity unknown'); + } + + if ($sourceID eq $accountID && $entityNum != $char->{spirits}) { + $char->{spirits} = $entityNum; + $char->{amuletType} = $entityElement; + $char->{spiritsType} = $entityType; + $entityElement ? + # Translation Comment: Message displays following: quantity, the name of the entity and its element + message TF("You have %s %s(s) of %s now\n", $entityNum, $entityType, $entityElement), "parseMsg_statuslook", 1 : + # Translation Comment: Message displays following: quantity and the name of the entity + message TF("You have %s %s(s) now\n", $entityNum, $entityType), "parseMsg_statuslook", 1; + } elsif ($entityNum != $actor->{spirits}) { + $actor->{spirits} = $entityNum; + $actor->{amuletType} = $entityElement; + $actor->{spiritsType} = $entityType; + $entityElement ? + # Translation Comment: Message displays following: actor, quantity, the name of the entity and its element + message TF("%s has %s %s(s) of %s now\n", $actor, $entityNum, $entityType, $entityElement), "parseMsg_statuslook", 1 : + # Translation Comment: Message displays following: actor, quantity and the name of the entity + message TF("%s has %s %s(s) now\n", $actor, $entityNum, $entityType), "parseMsg_statuslook", 1; + } +} + +# Changes sprite of an NPC object (ZC_NPCSPRITE_CHANGE). +# 01B0 <id>.L <type>.B <value>.L +# type: +# unused +sub monster_typechange { + my ($self, $args) = @_; + + my $ID = $args->{ID}; + my $type = $args->{type}; + my $monster = $monstersList->getByID($ID); + if ($monster) { + my $oldName = $monster->name; + if ($monsters_lut{$type}) { + $monster->setName($monsters_lut{$type}); + } else { + $monster->setName(undef); + } + $monster->{nameID} = $type; + $monster->{dmgToParty} = 0; + $monster->{dmgFromParty} = 0; + $monster->{missedToParty} = 0; + message TF("Monster %s (%d) changed to %s\n", $oldName, $monster->{binID}, $monster->name); + } +} + +# Show monster HP +# 0977 <id>.L <HP>.L <maxHP>.L (ZC_HP_INFO). +sub monster_hp_info { + my ($self, $args) = @_; + my $monster = $monstersList->getByID($args->{ID}); + if ($monster) { + $monster->{hp} = $args->{hp}; + $monster->{hp_max} = $args->{hp_max}; + + debug TF("Monster %s has hp %s/%s (%s%)\n", $monster->name, $monster->{hp}, $monster->{hp_max}, $monster->{hp} * 100 / $monster->{hp_max}), "parseMsg_damage"; + } +} + +# Show Monster HP bar +# 0A36 <id>.L <HP>.B +sub monster_hp_info_tiny { + my ($self, $args) = @_; + my $monster = $monstersList->getByID($args->{ID}); + if ($monster) { + $monster->{hp_percent} = $args->{hp} * 5; + + debug TF("Monster %s has about %d%% hp left\n", $monster->name, $monster->{hp_percent}), "parseMsg_damage"; + } +} + +## +# account_id({accountID}) +# +# This is for what eA calls PacketVersion 9, they send the AID in a 'proper' packet +sub account_id { + my ($self, $args) = @_; + # the account ID is already unpacked into PLAIN TEXT when it gets to this function... + # So lets not fuckup the $accountID since we need that later... someone will prolly have to fix this later on + my $accountID = $args->{accountID}; + debug sprintf("Account ID: %s (%s)\n", unpack('V',$accountID), getHex($accountID)); +} + +## +# marriage_partner_name({String name}) +# +# Name of the partner character, sent to everyone around right before casting "I miss you". +sub marriage_partner_name { + my ($self, $args) = @_; + + message TF("Marriage partner name: %s\n", bytesToString($args->{name})); +} + +sub login_pin_code_request { + # This is ten second-level password login for 2013/3/29 upgrading of twRO + my ($self, $args) = @_; + + if ($args->{flag} ne 0 && ($config{XKore} eq "1" || $config{XKore} eq "3")) { + $timeout{master}{time} = time; + return; + } + + # tRO "workaround" + # receive pincode means that we already received all character pages + $charSvrSet{sync_received_characters} = $charSvrSet{sync_Count} if (exists $charSvrSet{sync_received_characters} && !$masterServer->{private}); + + # flags: + # 0 - correct + # 1 - requested (already defined) + # 2 - requested (not defined) + # 3 - expired + # 4 - requested (not defined) - private servers + # 5 - invalid (official servers?) + # 7 - disabled? + # 8 - incorrect + if ($args->{flag} == 0) { # removed check for seed 0, eA/rA/brA sends a normal seed. + $timeout{'char_login_pause'}{'time'} = time; + message T("PIN code is correct.\n"), "success"; + } elsif ($args->{flag} == 1) { + # PIN code query request. + $accountID = $args->{accountID}; + debug sprintf("Account ID: %s (%s)\n", unpack('V',$accountID), getHex($accountID)); + + message T("Server requested PIN password in order to select your character.\n"), "connection"; + return if ($config{loginPinCode} eq '' && !($self->queryAndSaveLoginPinCode())); + $messageSender->sendLoginPinCode($args->{seed}, 0); + } elsif ($args->{flag} == 2 or $args->{flag} == 4) { + # PIN code has never been set before, so set it. + warning T("PIN password is not set for this account.\n"), "connection"; + return if ($config{loginPinCode} eq '' && !($self->queryAndSaveLoginPinCode())); + + while ((($config{loginPinCode} =~ /[^0-9]/) || (length($config{loginPinCode}) != 4)) && + !($self->queryAndSaveLoginPinCode("Your PIN should never contain anything but exactly 4 numbers.\n"))) { + error T("Your PIN should never contain anything but exactly 4 numbers.\n"); + } + $messageSender->sendLoginPinCode($args->{seed}, 1); + } elsif ($args->{flag} == 3) { + # should we use the same one again? is it possible? + warning T("PIN password expired.\n"), "connection"; + return if ($config{loginPinCode} eq '' && !($self->queryAndSaveLoginPinCode())); + + while ((($config{loginPinCode} =~ /[^0-9]/) || (length($config{loginPinCode}) != 4)) && + !($self->queryAndSaveLoginPinCode("Your PIN should never contain anything but exactly 4 numbers.\n"))) { + error T("Your PIN should never contain anything but exactly 4 numbers.\n"); + } + $messageSender->sendLoginPinCode($args->{seed}, 1); + } elsif ($args->{flag} == 5) { + # PIN code invalid. + error T("PIN code is invalid, don't use sequences or repeated numbers.\n"); + # configModify('loginPinCode', '', 1); + return if (!($self->queryAndSaveLoginPinCode(T("The login PIN code that you entered is invalid. Please re-enter your login PIN code.")))); + $messageSender->sendLoginPinCode($args->{seed}, 0); + } elsif ($args->{flag} == 7) { + # PIN code disabled. + $accountID = $args->{accountID}; + debug sprintf("Account ID: %s (%s)\n", unpack('V',$accountID), getHex($accountID)); + + # call charSelectScreen + $self->{lockCharScreen} = 0; + $timeout{'char_login_pause'}{'time'} = time; + } elsif ($args->{flag} == 8) { + # PIN code incorrect. + error T("PIN code is incorrect.\n"); + #configModify('loginPinCode', '', 1); + return if (!($self->queryAndSaveLoginPinCode(T("The login PIN code that you entered is incorrect. Please re-enter your login PIN code.")))); + $messageSender->sendLoginPinCode($args->{seed}, 0); + } else { + debug("login_pin_code_request: unknown flag $args->{flag}\n"); + } + + $timeout{master}{time} = time; +} + +sub login_pin_new_code_result { + my ($self, $args) = @_; + + if ($args->{flag} == 2) { + # PIN code invalid. + error T("PIN code is invalid, don't use sequences or repeated numbers.\n"); + #configModify('loginPinCode', '', 1); + return if (!($self->queryAndSaveLoginPinCode(T("PIN code is invalid, don't use sequences or repeated numbers.\n")))); + + # there's a bug in bRO where you can use letters or symbols or even a string as your PIN code. + # as a result this will render you unable to login again (forever?) using the official client + # and this is detectable and can result in a permanent ban. we're using this code in order to + # prevent this. - revok 17.12.2012 + while ((($config{loginPinCode} =~ /[^0-9]/) || (length($config{loginPinCode}) != 4)) && + !($self->queryAndSaveLoginPinCode("Your PIN should never contain anything but exactly 4 numbers.\n"))) { + error T("Your PIN should never contain anything but exactly 4 numbers.\n"); + } + + $messageSender->sendLoginPinCode($args->{seed}, 0); + } +} + +sub actor_status_active { + my ($self, $args) = @_; + return unless changeToInGameState(); + my ($type, $ID, $tick, $unknown1, $unknown2, $unknown3, $unknown4) = @{$args}{qw(type ID tick unknown1 unknown2 unknown3 unknown4)}; + my $flag = (exists $args->{flag}) ? $args->{flag} : 1; + my $status = defined $statusHandle{$type} ? $statusHandle{$type} : "UNKNOWN_STATUS_$type"; + $char->cart->changeType($unknown1) if ($type == 673 && defined $unknown1 && ($ID eq $accountID)); # for Cart active + $args->{skillName} = defined $statusName{$status} ? $statusName{$status} : $status; +# ($args->{actor} = Actor::get($ID))->setStatus($status, 1, $tick == 9999 ? undef : $tick, $args->{unknown1}); # need test for '08FF' + ($args->{actor} = Actor::get($ID))->setStatus($status, $flag, $tick == 9999 ? undef : $tick); + # Rolling Cutter counters. + if ( $type == 0x153 && $char->{spirits} != $unknown1 ) { + $char->{spirits} = $unknown1 || 0; + if ( $ID eq $accountID ) { + message TF( "You have %s %s(s) now\n", $char->{spirits}, 'counters' ), "parseMsg_statuslook", 1; + } else { + message TF( "%s has %s %s(s) now\n", $args->{actor}, $char->{spirits}, 'counters' ), "parseMsg_statuslook", 1; + } + } +} + +#099B +sub map_property3 { + my ($self, $args) = @_; + + if ($config{'status_mapType'}){ + $char->setStatus(@$_) for map {[$_->[1], $args->{type} == $_->[0]]} + grep { $args->{type} == $_->[0] || $char->{statuses}{$_->[1]} } + map {[$_, defined $mapTypeHandle{$_} ? $mapTypeHandle{$_} : "UNKNOWN_MAPTYPE_$_"]} + 0 .. List::Util::max $args->{type}, keys %mapTypeHandle; + + if ($args->{info_table}) { + my $info_table = unpack('V1',$args->{info_table}); + for (my $i = 0; $i < 16; $i++) { + if ($info_table&(1<<$i)) { + $char->setStatus(defined $mapPropertyInfoHandle{$i} ? $mapPropertyInfoHandle{$i} : "UNKNOWN_MAPPROPERTY_INFO_$i",1); + } + } + } + } + + $pvp = {6 => 1, 8 => 2, 19 => 3}->{$args->{type}}; + if ($pvp) { + Plugins::callHook('pvp_mode', {pvp => $pvp});# 1 PvP, 2 GvG, 3 Battleground + } +} + +#011F, 01C9, 08C7 +sub area_spell { + my ($self, $args) = @_; + + # Area effect spell; including traps! + my $ID = $args->{ID}; + my $sourceID = $args->{sourceID}; + my $x = $args->{x}; + my $y = $args->{y}; + my $type = $args->{type}; + my $isVisible = $args->{isVisible}; + my $binID; + + if ($spells{$ID} && $spells{$ID}{'sourceID'} eq $sourceID) { + $binID = binFind(\@spellsID, $ID); + $binID = binAdd(\@spellsID, $ID) if ($binID eq ""); + } else { + $binID = binAdd(\@spellsID, $ID); + } + + $spells{$ID}{'ID'} = $ID; + $spells{$ID}{'sourceID'} = $sourceID; + $spells{$ID}{'pos'}{'x'} = $x; + $spells{$ID}{'pos'}{'y'} = $y; + $spells{$ID}{'pos_to'}{'x'} = $x; + $spells{$ID}{'pos_to'}{'y'} = $y; + $spells{$ID}{'binID'} = $binID; + $spells{$ID}{'type'} = $type; + $spells{$ID}{'isVisible'} = $isVisible; + if ($type == 0x81) { + message TF("%s opened Warp Portal on (%d, %d)\n", getActorName($sourceID), $x, $y), "skill"; + } + debug "Area effect ".getSpellName($type)." ($binID) from ".getActorName($sourceID)." appeared on ($x, $y), isVisible = $isVisible\n", "skill", 2; + + if ($args->{switch} eq "01C9") { + message TF("%s has scribbled: %s on (%d, %d)\n", getActorName($sourceID), $args->{scribbleMsg}, $x, $y); + } + + Plugins::callHook('packet_areaSpell', { + ID => $ID, + sourceID => $sourceID, + x => $x, + y => $y, + type => $type, + isVisible => $isVisible + }); +} + +#099F +sub area_spell_multiple2 { + my ($self, $args) = @_; + + # Area effect spells; including traps! + my $len = $args->{len} - 4; + my $spellInfo = $args->{spellInfo}; + my $msg = ""; + my $binID; + my ($ID, $sourceID, $x, $y, $type, $range, $isVisible); + for (my $i = 0; $i < $len; $i += 18) { + $msg = substr($spellInfo, $i, 18); + ($ID, $sourceID, $x, $y, $type, $range, $isVisible) = unpack('a4 a4 v2 V C2', $msg); + + if ($spells{$ID} && $spells{$ID}{'sourceID'} eq $sourceID) { + $binID = binFind(\@spellsID, $ID); + $binID = binAdd(\@spellsID, $ID) if ($binID eq ""); + } else { + $binID = binAdd(\@spellsID, $ID); + } + + $spells{$ID}{'ID'} = $ID; + $spells{$ID}{'sourceID'} = $sourceID; + $spells{$ID}{'pos'}{'x'} = $x; + $spells{$ID}{'pos'}{'y'} = $y; + $spells{$ID}{'pos_to'}{'x'} = $x; + $spells{$ID}{'pos_to'}{'y'} = $y; + $spells{$ID}{'binID'} = $binID; + $spells{$ID}{'type'} = $type; + $spells{$ID}{'range'} = $range; + $spells{$ID}{'isVisible'} = $isVisible; + if ($type == 0x81) { + message TF("%s opened Warp Portal on (%d, %d)\n", getActorName($sourceID), $x, $y), "skill"; + } + debug "Area effect ".getSpellName($type)." ($binID) from ".getActorName($sourceID)." appeared on ($x, $y), isVisible = $isVisible, range = $range\n", "skill", 2; + } + + Plugins::callHook('packet_areaSpell', { + ID => $ID, + sourceID => $sourceID, + x => $x, + y => $y, + type => $type, + isVisible => $isVisible, + range => $range + }); +} + +#09CA +sub area_spell_multiple3 { + my ($self, $args) = @_; + + # Area effect spells; including traps! + my $len = $args->{len} - 4; + my $spellInfo = $args->{spellInfo}; + my $msg = ""; + my $binID; + my ($ID, $sourceID, $x, $y, $type, $range, $isVisible, $lvl); + for (my $i = 0; $i < $len; $i += 19) { + $msg = substr($spellInfo, $i, 19); + ($ID, $sourceID, $x, $y, $type, $range, $isVisible, $lvl) = unpack('a4 a4 v2 V C3', $msg); + + if ($spells{$ID} && $spells{$ID}{'sourceID'} eq $sourceID) { + $binID = binFind(\@spellsID, $ID); + $binID = binAdd(\@spellsID, $ID) if ($binID eq ""); + } else { + $binID = binAdd(\@spellsID, $ID); + } + + $spells{$ID}{'ID'} = $ID; + $spells{$ID}{'sourceID'} = $sourceID; + $spells{$ID}{'pos'}{'x'} = $x; + $spells{$ID}{'pos'}{'y'} = $y; + $spells{$ID}{'pos_to'}{'x'} = $x; + $spells{$ID}{'pos_to'}{'y'} = $y; + $spells{$ID}{'binID'} = $binID; + $spells{$ID}{'type'} = $type; + $spells{$ID}{'range'} = $range; + $spells{$ID}{'isVisible'} = $isVisible; + $spells{$ID}{'lvl'} = $lvl; + if ($type == 0x81) { + message TF("%s opened Warp Portal on (%d, %d)\n", getActorName($sourceID), $x, $y), "skill"; + } + debug "Area effect ".getSpellName($type)." ($binID) from ".getActorName($sourceID)." appeared on ($x, $y), isVisible = $isVisible, range = $range, lvl = $lvl\n", "skill", 2; + } + + Plugins::callHook('packet_areaSpell', { + ID => $ID, + sourceID => $sourceID, + x => $x, + y => $y, + type => $type, + isVisible => $isVisible, + range => $range, + lvl => $lvl + }); +} + +sub sync_request_ex { + my ($self, $args) = @_; + + return if ($config{XKore} eq 1 || $config{XKore} eq 3); # let the clien hanle this + + # Debug Log + # message "Received Sync Ex : 0x" . $args->{switch} . "\n"; + + # Computing Sync Ex - By Fr3DBr + my $PacketID = $args->{switch}; + + # Getting Sync Ex Reply ID from Table + my $SyncID = $self->{sync_ex_reply}->{$PacketID}; + + # Cleaning Leading Zeros + $PacketID =~ s/^0+//; + + # Cleaning Leading Zeros + $SyncID =~ s/^0+//; + + # Debug Log + #error sprintf("Received Ex Packet ID : 0x%s => 0x%s\n", $PacketID, $SyncID); + + # Converting ID to Hex Number + $SyncID = hex($SyncID); + + # Dispatching Sync Ex Reply + $messageSender->sendReplySyncRequestEx($SyncID); +} + +sub cash_shop_list { + my ($self, $args) = @_; + my $tabcode = $args->{tabcode}; + my $item_pack = $self->{cash_shop_list_pack} || 'v V'; + my $item_len = length pack $item_pack; + my $item_list_len = length $args->{itemInfo}; + # CASHSHOP_TAB_NEW => 0x0, + # CASHSHOP_TAB_POPULAR => 0x1, + # CASHSHOP_TAB_LIMITED => 0x2, + # CASHSHOP_TAB_RENTAL => 0x3, + # CASHSHOP_TAB_PERPETUITY => 0x4, + # CASHSHOP_TAB_BUFF => 0x5, + # CASHSHOP_TAB_RECOVERY => 0x6, + # CASHSHOP_TAB_ETC => 0x7 + # CASHSHOP_TAB_MAX => 8 + my %cashitem_tab = ( + 0 => T('New'), + 1 => T('Popular'), + 2 => T('Limited'), + 3 => T('Rental'), + 4 => T('Perpetuity'), + 5 => T('Buff'), + 6 => T('Recovery'), + 7 => T('Etc'), + ); + debug TF("%s\n" . + "# Name Price\n", + center(' Tab: ' . $cashitem_tab{$tabcode} . ' ', 44, '-')), "list"; + for (my $i = 0; $i < $item_list_len; $i += $item_len) { + my ($ID, $price) = unpack($item_pack, substr($args->{itemInfo}, $i)); + my $name = itemNameSimple($ID); + push(@{$cashShop{list}[$tabcode]}, {item_id => $ID, price => $price}); # add to cashshop + debug(swrite( + "@<< @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< @>>>>>>C", + [$i, $name, formatNumber($price)]), + "list"); + + } +} + +sub cash_shop_open_result { + my ($self, $args) = @_; + #'0845' => ['cash_window_shop_open', 'v2', [qw(cash_points kafra_points)]], + message TF("Cash Points: %sC - Kafra Points: %sC\n", formatNumber ($args->{cash_points}), formatNumber ($args->{kafra_points})); + $cashShop{points} = { + cash => $args->{cash_points}, + kafra => $args->{kafra_points} + }; +} + +sub cash_shop_buy_result { + my ($self, $args) = @_; + # SUCCESS = 0x0, + # WRONG_TAB? = 0x1, // we should take care with this, as it's detectable by the server + # SHORTTAGE_CASH = 0x2, + # UNKONWN_ITEM = 0x3, + # INVENTORY_WEIGHT = 0x4, + # INVENTORY_ITEMCNT = 0x5, + # RUNE_OVERCOUNT = 0x9, + # EACHITEM_OVERCOUNT = 0xa, + # UNKNOWN = 0xb, + # BUSY = 0xc, + my %result = ( + 0 => T('Success'), + 1 => T('Wrong Tab'), + 2 => T('Shorttage cash'), + 3 => T('Unkonwn item'), + 4 => T('Inventory weight'), + 5 => T('Inventory item count'), + 9 => T('Rune overcount'), + 10 => T('Eachitem overcount'), + 11 => T('Unknown'), + 12 => T('Busy'), + ); + if ($args->{result} > 0) { + error TF("Error while buying %s from cash shop. Error code: %d (%s)\n", itemNameSimple($args->{item_id}), $args->{result}, $result{$args->{result}}); + } else { + message TF("Bought %s from cash shop. Current CASH: %d\n", itemNameSimple($args->{item_id}), formatNumber($args->{updated_points})), "success"; + $cashShop{points}->{cash} = $args->{updated_points}; + } + + debug sprintf("Got result ID [%d] while buying %s from CASH Shop. Current CASH: %d \n", $args->{result}, itemNameSimple($args->{item_id}), formatNumber($args->{updated_points})); + +} + +sub sprite_change { + my ($self, $args) = @_; + + my ($ID, $type, $value1, $value2) = @{$args}{qw(ID type value1 value2)}; + my $player = ($ID ne $accountID)? $playersList->getByID($ID) : $char; + return unless $player; + + if ($type == 0) { + $player->{jobID} = $value1; + message TF("%s changed Job to: %s\n", $player, $jobs_lut{$value1}), "parseMsg_statuslook"; + + } elsif ($type == 2) { + if ($value1 ne $player->{weapon}) { + message TF("%s changed Weapon to %s (%d)\n", $player, itemName({nameID => $value1}), $value1), "parseMsg_statuslook", 2; + $player->{weapon} = $value1; + } + if ($value2 ne $player->{shield}) { + message TF("%s changed Shield to %s (%d)\n", $player, itemName({nameID => $value2}), $value2), "parseMsg_statuslook", 2; + $player->{shield} = $value2; + } + } elsif ($type == 3) { + message TF("%s changed Lower headgear to %s (%d)\n", $player, headgearName($value1), $value1), "parseMsg_statuslook"; + $player->{headgear}{low} = $value1; + } elsif ($type == 4) { + message TF("%s changed Upper headgear to %s (%d)\n", $player, headgearName($value1), $value1), "parseMsg_statuslook"; + $player->{headgear}{top} = $value1; + } elsif ($type == 5) { + message TF("%s changed Middle headgear to %s (%d)\n", $player, headgearName($value1), $value1), "parseMsg_statuslook"; + $player->{headgear}{mid} = $value1; + } elsif ($type == 6) { + message TF("%s changed Hair color to: %s (%d)\n", $player, $haircolors{$value1}, $value1), "parseMsg_statuslook"; + $player->{hair_color} = $value1; + } elsif ($type == 9) { + if ($player->{shoes} && $value1 ne $player->{shoes}) { + message TF("%s changed Shoes to: %s\n", $player, itemName({nameID => $value1})), "parseMsg_statuslook", 2; + } + $player->{shoes} = $value1; + } elsif ($type == 12) { + message TF("%s changed Robe to: SPRITE_ROBE_ID=%d\n", $player, $value1, $value1), "parseMsg_statuslook", 2; + } elsif ($type== 7 || $type == 13) { + # Type 7 looks like body palette or body color. Type 13 looks like body2 + debug sprintf("%s changed type= %d. value1=%d, value2=%d\n", $player, $type, $value1, $value2); + } else { + error TF("%s changed unknown sprite type (%d), write about it to OpenKore developer\n", $player, $type), "parseMsg_statuslook"; + } + Plugins::callHook('sprite_job_change'); +} + +sub progress_bar { + my($self, $args) = @_; + message TF("Progress bar loading (time: %d).\n", $args->{time}), 'info'; + $char->{progress_bar} = 1; + $taskManager->add( + new Task::Chained(tasks => [new Task::Wait(seconds => $args->{time}), + new Task::Function(function => sub { + $messageSender->sendProgress(); + message TF("Progress bar finished.\n"), 'info'; + $char->{progress_bar} = 0; + $_[0]->setDone; + })])); +} + +sub progress_bar_stop { + my($self, $args) = @_; + message TF("Progress bar finished.\n", 'info'); +} + +# Sends list of all quest states +# 02b1 <packet len>.W <num>.L { <quest id>.L <active>.B }*num (ZC_ALL_QUEST_LIST) +# 097a <packet len>.W <num>.L { <quest id>.L <active>.B <remaining time>.L <time>.L <count>.W { <mob_id>.L <killed>.W <total>.W <mob name>.24B }*count }*num (ZC_ALL_QUEST_LIST2) +# 09f8 <packet len>.W <num>.L { <quest id>.L <active>.B <remaining time>.L <time>.L <count>.W { <hunt identification>.L <mob type>.L <mob_id>.L <min level>.W <max level>.W <killed>.W <total>.W <mob name>.24B }*count }*num (ZC_ALL_QUEST_LIST3) +sub quest_all_list { + my ( $self, $args ) = @_; + + my $offset = 0; + + my $quest_info; + + if ($args->{switch} eq '02B1') { # DEFAULT PACKET + $quest_info = { + quest_pack => 'V C', + quest_keys => [qw(quest_id active)], + quest_len => 5, + mission_pack => '', + mission_keys => [], + mission_len => 0, + }; + + } elsif ($args->{switch} eq '097A') { # SERVERTYPE >= 20141022 + $quest_info = { + quest_pack => 'V C V2 v', + quest_keys => [qw(quest_id active time_expire time_start mission_amount)], + quest_len => 15, + mission_pack => 'V v2 Z24', + mission_keys => [qw(mob_id mob_count mob_goal mob_name_original)], + mission_len => 32, + }; + + } elsif ($args->{switch} eq '09F8') { # SERVERTYPE >= 20150513 + $quest_info = { + quest_pack => 'V C V2 v', + quest_keys => [qw(quest_id active time_expire time_start mission_amount)], + quest_len => 15, + mission_pack => 'V3 v4 Z24', + mission_keys => [qw(hunt_id mob_type mob_id min_level max_level mob_count mob_goal mob_name_original)], + mission_len => 44, + }; + + } elsif ($args->{switch} eq '0AFF') { # SERVERTYPE >= 20181010 + $quest_info = { + quest_pack => 'V C V2 v', + quest_keys => [qw(quest_id active time_expire time_start mission_amount)], + quest_len => 15, + mission_pack => 'V4 v4 Z24', + mission_keys => [qw(hunt_id hunt_id_cont mob_type mob_id min_level max_level mob_count mob_goal mob_name_original)], + mission_len => 48, + }; + + } else { # this can't happen + return; + } + + # Long quest lists are split up over multiple packets. Only reset the quest list if we've switched maps. + our $quest_generation ||= 0; + our $last_quest_generation ||= 0; + if ( $last_quest_generation != $quest_generation ) { + $last_quest_generation = $quest_generation; + $questList = {}; + } + + for (my $i = 0 ; $i < $args->{quest_amount} ; $i++) { + my $quest; + + @{$quest}{@{$quest_info->{quest_keys}}} = unpack($quest_info->{quest_pack}, substr($args->{message}, $offset, $quest_info->{quest_len})); + + %{$questList->{$quest->{quest_id}}} = %$quest; + + debug "Quest ID: $quest->{quest_id} - active: $quest->{active}\n", "info"; + + $offset += $quest_info->{quest_len}; + + next if !exists $quest->{mission_amount}; + + debug "- Mission amount: $quest->{mission_amount}\n", "info"; + + for ( my $j = 0 ; $j < $quest->{mission_amount}; $j++ ) { + my $mission; + + @{$mission}{@{$quest_info->{mission_keys}}} = unpack($quest_info->{mission_pack}, substr($args->{message}, $offset, $quest_info->{mission_len})); + $mission->{mob_name} = bytesToString($mission->{mob_name_original}); + $mission->{mission_index} = $j; + + %{$questList->{$quest->{quest_id}}->{missions}->{$mission->{mob_id}}} = %$mission; + + debug "- MobID: $mission->{mob_id} - Name: $mission->{mob_name} - Count: $mission->{mob_count} - Goal: $mission->{mob_goal}\n", "info"; + + $offset += $quest_info->{mission_len}; + + Plugins::callHook('quest_mission_added', { + questID => $quest->{quest_id}, + mission_id => $mission->{mob_id} + }); + } + } + + Plugins::callHook('quest_all_list_end'); +} + +# 02b2 <packet len>.W <num>.L { <quest id>.L <start time>.L <expire time>.L <mobs>.W { <mob id>.L <mob count>.W <mob name>.24B }*3 }*num +# note: this packet shows all quests + their missions and has variable length +sub quest_all_mission { + my ($self, $args) = @_; + + my $offset = 0; + + my $quest_info = { + quest_pack => 'V3 v', + quest_keys => [qw(quest_id time_start time_expire mission_amount)], + quest_len => 14, + mission_pack => 'V v Z24', + mission_keys => [qw(mob_id mob_count mob_name_original)], + mission_len => 30, + }; + + for (my $i = 0 ; $i < $args->{mission_amount} ; $i++) { + my $quest; + + @{$quest}{@{$quest_info->{quest_keys}}} = unpack($quest_info->{quest_pack}, substr($args->{message}, $offset, $quest_info->{quest_len})); + + my $char_quest = \%{$questList->{$quest->{quest_id}}}; + + foreach my $key (keys %{$quest}) { + $char_quest->{$key} = $quest->{$key}; + } + + debug "Quest ID: $char_quest->{quest_id} - active: $char_quest->{active}\n", "info"; + + $offset += $quest_info->{quest_len}; + + for ( my $j = 0 ; $j < 3; $j++ ) { + + if ($j >= $char_quest->{mission_amount}) { + $offset += $quest_info->{mission_len}; + next; + } + + my $mission; + + @{$mission}{@{$quest_info->{mission_keys}}} = unpack($quest_info->{mission_pack}, substr($args->{message}, $offset, $quest_info->{mission_len})); + $mission->{mob_name} = bytesToString($mission->{mob_name_original}); + $mission->{mission_index} = $j; + + %{$questList->{$char_quest->{quest_id}}->{missions}->{$mission->{mob_id}}} = %$mission; + + debug "- MobID: $mission->{mob_id} - Name: $mission->{mob_name} - Count: $mission->{mob_count}\n", "info"; + + $offset += $quest_info->{mission_len}; + + Plugins::callHook('quest_mission_added', { + questID => $char_quest->{quest_id}, + mission_id => $mission->{mob_id} + }); + } + } + + Plugins::callHook('quest_all_mission_end'); +} + +# 02b3 <quest id>.L <active>.B <start time>.L <expire time>.L <mobs>.W { <mob id>.L <mob count>.W <mob name>.24B }*3 (ZC_ADD_QUEST) +# 09f9 <quest id>.L <active>.B <start time>.L <expire time>.L <mobs>.W { <hunt identification>.L <mob type>.L <mob id>.L <min level>.W <max level>.W <mob count>.W <mob name>.24B }*3 (ZC_ADD_QUEST_EX) +# note: this packet shows all missions for 1 quest and has fixed length +sub quest_add { + my ($self, $args) = @_; + + my $offset = 0; + + my $quest_info; + + if ($args->{switch} eq '09F9') { # SERVERTYPE >= 20150513 + $quest_info = { + mission_pack => 'V3 v3 Z24', + mission_keys => [qw(hunt_id mob_type mob_id min_level max_level mob_count mob_name_original)], + mission_len => 42, + }; + + } elsif ($args->{switch} eq '0B0C') { # SERVERTYPE >= 20150513 + $quest_info = { + mission_pack => 'V4 v3 Z24', + mission_keys => [qw(hunt_id hunt_id_cont mob_type mob_id min_level max_level mob_count mob_name_original)], + mission_len => 46, + }; + + } else { # DEFAULT PACKET - 02B3 + $quest_info = { + mission_pack => 'V v Z24', + mission_keys => [qw(mob_id mob_count mob_name_original)], + mission_len => 30, + }; + } + + my $quest = \%{$questList->{$args->{questID}}}; + $quest->{quest_id} = $args->{questID}; + $quest->{active} = $args->{active}; + $quest->{time_start} = $args->{time_start}; + $quest->{time_expire} = $args->{time_expire}; + $quest->{mission_amount} = $args->{mission_amount}; + + if ($args->{questID}) { + message TF("Quest: %s has been added.\n", $quests_lut{$args->{questID}} ? "$quests_lut{$args->{questID}}{title} ($args->{questID})" : $args->{questID}), "info"; + } + + for ( my $j = 0 ; $j < 3; $j++ ) { + if ($j >= $quest->{mission_amount}) { + $offset += $quest_info->{mission_len}; + next; + } + my $mission; + + @{$mission}{@{$quest_info->{mission_keys}}} = unpack($quest_info->{mission_pack}, substr($args->{message}, $offset, $quest_info->{mission_len})); + $mission->{mob_name} = bytesToString($mission->{mob_name_original}); + $mission->{mission_index} = $j; + + %{$questList->{$quest->{quest_id}}->{missions}->{$mission->{mob_id}}} = %$mission; + + debug "- MobID: $mission->{mob_id} - Name: $mission->{mob_name} - Count: $mission->{mob_count}\n", "info"; + + $offset += $quest_info->{mission_len}; + + Plugins::callHook('quest_mission_added', { + questID => $quest->{quest_id}, + mission_id => $mission->{mob_id} + }); + } + + Plugins::callHook('quest_added', {questID => $args->{questID}}); +} + +# 02b5 <packet len>.W <mobs>.W { <quest id>.L <mob id>.L <total count>.W <current count>.W }*3 (ZC_UPDATE_MISSION_HUNT) +# 09fa <packet len>.W <mobs>.W { <quest id>.L <hunt identification>.L <total count>.W <current count>.W }*3 (ZC_UPDATE_MISSION_HUNT_EX) (Sends hunt identification which is quest_id * 1000 + mission_id) +sub quest_update_mission_hunt { + my ($self, $args) = @_; + + my $offset = 0; + + my $quest_info; + + if ($args->{switch} eq '09FA') { + $quest_info = { + mission_pack => 'V2 v2', + mission_keys => [qw(questID hunt_id mob_goal mob_count)], + mission_len => 12, + }; + + } elsif ($args->{switch} eq '0AFE') { + $quest_info = { + mission_pack => 'V3 v2', + mission_keys => [qw(questID hunt_id hunt_id_cont mob_goal mob_count)], + mission_len => 16, + }; + } else { # 02B5 and 08FE + $quest_info = { + mission_pack => 'V2 v2', + mission_keys => [qw(questID mob_id mob_goal mob_count)], + mission_len => 12, + }; + } + + # workaround 08FE dont have mission_count + if ($args->{switch} eq '08FE') { + $args->{mission_amount} = (length $args->{message}) / ($quest_info->{mission_len}); + } + + for (my $i = 0; $i < $args->{mission_amount}; $i++) { + my $mission; + + @{$mission}{@{$quest_info->{mission_keys}}} = unpack($quest_info->{mission_pack}, substr($args->{message}, $offset, $quest_info->{mission_len})); + + my $quest = \%{$questList->{$mission->{questID}}}; + + my $mission_id; + + # Mission is saved as hunt_id and server sent hunt_id + if (exists $mission->{hunt_id} && exists $quest->{missions}->{$mission->{hunt_id}}) { + $mission_id = $mission->{hunt_id}; + + # Mission is saved as mob_id and server sent mob_id + } elsif (exists $mission->{mob_id} && exists $quest->{missions}->{$mission->{mob_id}}) { + $mission_id = $mission->{mob_id}; + + # Mission is saved as hunt_id and server sent mob_id + } elsif (exists $mission->{mob_id} && !exists $quest->{missions}->{$mission->{mob_id}}) { + # Search in the quest of a mission with this mob_id + foreach my $current_key (keys %{$quest->{missions}}) { + if (exists $quest->{missions}->{$current_key}{mob_id} && $quest->{missions}->{$current_key}{mob_id} == $mission->{mob_id}) { + $mission_id = $quest->{missions}->{$current_key}{hunt_id}; + last; + } + } + + # Mission is saved as mob_id and server sent hunt_id + } elsif (exists $mission->{hunt_id} && !exists $quest->{missions}->{$mission->{hunt_id}}) { + # Search in the quest of a mission with this hunt_id + foreach my $current_key (keys %{$quest->{missions}}) { + if (exists $quest->{missions}->{$current_key}{hunt_id} && $quest->{missions}->{$current_key}{hunt_id} == $mission->{hunt_id}) { + $mission_id = $quest->{missions}->{$current_key}{mob_id}; + last; + } + } + } + + my $quest_mission = \%{$quest->{missions}->{$mission_id}}; + + $quest_mission->{mob_count} = $mission->{mob_count}; + $quest_mission->{mob_goal} = $mission->{mob_goal}; + + debug "- MobID: $mission->{mob_id} - Name: $mission->{mob_name} - Count: $mission->{mob_count} - Goal: $mission->{mob_goal}\n", "info"; + + if ($config{questDisplayStyle}) { + if ($config{questDisplayStyle} >= 2) { + warning TF("[%s] Quest - defeated [%s] progress (%s/%s)\n", $quests_lut{$mission->{questID}} ? "$quests_lut{$mission->{questID}}{title} ($mission->{questID})" : $mission->{questID}, $quest_mission->{mob_name}, $quest_mission->{mob_count}, $quest_mission->{mob_goal}), "info"; + } else { + warning TF("%s [%s/%s]\n", $quest_mission->{mob_name}, $quest_mission->{mob_count}, $quest_mission->{mob_goal}), "info"; + } + } + + $offset += $quest_info->{mission_len}; + + Plugins::callHook('quest_mission_updated', { + questID => $quest_mission->{questID}, + mission_id => $mission_id, + mobID => $quest_mission->{mob_id}, + count => $quest_mission->{mob_count}, + goal => $quest_mission->{mob_goal} + }); + } + + Plugins::callHook('quest_update_mission_hunt_end'); +} + +# 02B4 +sub quest_delete { + my ($self, $args) = @_; + message TF("Quest: %s has been deleted.\n", $quests_lut{$args->{questID}} ? "$quests_lut{$args->{questID}}{title} ($args->{questID})" : $args->{questID}), "info"; + delete $questList->{$args->{questID}}; + + Plugins::callHook('quest_delete'); +} + +# 02B7 +sub quest_active { + my ($self, $args) = @_; + + message $args->{active} + ? TF("Quest %s is now active.\n", $quests_lut{$args->{questID}} ? "$quests_lut{$args->{questID}}{title} ($args->{questID})" : $args->{questID}) + : TF("Quest %s is now inactive.\n", $quests_lut{$args->{questID}} ? "$quests_lut{$args->{questID}}{title} ($args->{questID})" : $args->{questID}) + , "info"; + + $questList->{$args->{questID}}->{active} = $args->{active}; + + Plugins::callHook('quest_active'); +} + +# 02C1 +sub parse_npc_chat { + my ($self, $args) = @_; + + $args->{actor} = Actor::get($args->{ID}); +} + +sub npc_chat { + my ($self, $args) = @_; + + # like public_chat, but also has color + + my $actor = $args->{actor}; + my $message = $args->{message}; # needs bytesToString or not? + my $position = sprintf("[%s %d, %d]", + $field ? $field->baseName : T("Unknown field,"), + @{$char->{pos_to}}{qw(x y)}); + my $dist; + + if ($message =~ / : /) { + ((my $name), $message) = split / : /, $message, 2; + $dist = 'unknown'; + unless ($actor->isa('Actor::Unknown')) { + $dist = distance($char->{pos_to}, $actor->{pos_to}); + $dist = sprintf("%.1f", $dist) if ($dist =~ /\./); + } + if ($actor->{name} eq $name) { + $name = "$actor"; + } else { + $name = sprintf "%s (%s)", $name, $actor->{binID}; + } + $message = "$name: $message"; + + $position .= sprintf(" [%d, %d] [dist=%s] (%d)", + @{$actor->{pos_to}}{qw(x y)}, + $dist, $actor->{nameID}); + $dist = "[dist=$dist] "; + } + + chatLog("npc", "$position $message\n") if ($config{logChat}); + message TF("%s%s\n", $dist, $message), "npcchat"; + + Plugins::callHook('npc_chat', { + actor => $actor, + ID => $args->{ID}, + message => $message, + }); +} + +# 018d <packet len>.W { <name id>.W { <material id>.W }*3 }* +sub makable_item_list { + my ($self, $args) = @_; + undef $makableList; + my $unpack = $self->{makable_item_list_pack} || 'v4'; + my $len = length pack $unpack; + my $k = 0; + my $msg; + $msg .= center(" " . T("Create Item List") . " ", 79, '-') . "\n"; + for (my $i = 0; $i < length($args->{item_list}); $i += $len) { + my ($nameID, $material_1, $material_2, $material_3) = unpack($unpack, substr($args->{item_list}, $i, $len)); + $makableList->[$k] = $nameID; + $msg .= swrite(sprintf("\@%s \@%s (\@%s)", ('>'x2), ('<'x50), ('<'x6)), [$k, itemNameSimple($nameID), $nameID]); + $k++; + } + $msg .= sprintf("%s\n", ('-'x79)); + message($msg, "list"); + message T("You can now use the 'create' command.\n"), "info"; + + Plugins::callHook('makable_item_list', {item_list => $makableList}); +} + +sub storage_opened { + my ($self, $args) = @_; + $char->storage->open($args); +} + +sub storage_closed { + $char->storage->close(); + message T("Storage closed.\n"), "storage";; + + # Storage log + writeStorageLog(0); + + if ($char->{dcOnEmptyItems} ne "") { + message TF("Disconnecting on empty %s!\n", $char->{dcOnEmptyItems}); + chatLog("k", TF("Disconnecting on empty %s!\n", $char->{dcOnEmptyItems})); + quit(); + } +} + +sub storage_items_stackable { + my ($self, $args) = @_; + + $char->storage->clear; + + $self->_items_list({ + class => 'Actor::Item', + hook => 'packet_storage', + debug_str => 'Stackable Storage Item', + items => [$self->parse_items_stackable($args)], + getter => sub { $char->storage->getByID($_[0]{ID}) }, + adder => sub { $char->storage->add($_[0]) }, + callback => sub { + my ($local_item) = @_; + + $local_item->{amount} = $local_item->{amount} & ~0x80000000; + }, + }); + + $storageTitle = $args->{title} ? $args->{title} : undef; +} + +sub storage_items_nonstackable { + my ($self, $args) = @_; + + $self->_items_list({ + class => 'Actor::Item', + hook => 'packet_storage', + debug_str => 'Non-Stackable Storage Item', + items => [$self->parse_items_nonstackable($args)], + getter => sub { $char->storage->getByID($_[0]{ID}) }, + adder => sub { $char->storage->add($_[0]) }, + }); + + $storageTitle = $args->{title} ? $args->{title} : undef; +} + +sub storage_item_added { + my ($self, $args) = @_; + + my $index = $args->{ID}; + my $amount = $args->{amount}; + + my $item = $char->storage->getByID($index); + if (!$item) { + $item = new Actor::Item(); + $item->{nameID} = $args->{nameID}; + $item->{ID} = $index; + $item->{amount} = $amount; + $item->{type} = $args->{type}; + $item->{identified} = $args->{identified}; + $item->{broken} = $args->{broken}; + $item->{upgrade} = $args->{upgrade}; + $item->{cards} = $args->{cards}; + $item->{options} = $args->{options}; + $item->{name} = itemName($item); + $char->storage->add($item); + } else { + $item->{amount} += $amount; + } + my $disp = TF("Storage Item Added: %s (%d) x %d - %s", + $item->{name}, $item->{binID}, $amount, $itemTypes_lut{$item->{type}}); + message "$disp\n", "drop"; + + $itemChange{$item->{name}} += $amount; + $args->{item} = $item; +} + +sub storage_item_removed { + my ($self, $args) = @_; + + my ($index, $amount) = @{$args}{qw(ID amount)}; + + my $item = $char->storage->getByID($index); + + if ($item) { + Misc::storageItemRemoved($item->{binID}, $amount); + } +} + +sub cart_items_stackable { + my ($self, $args) = @_; + + $self->_items_list({ + class => 'Actor::Item', + hook => 'packet_cart', + debug_str => 'Stackable Cart Item', + items => [$self->parse_items_stackable($args)], + getter => sub { $char->cart->getByID($_[0]{ID}) }, + adder => sub { $char->cart->add($_[0]) }, + }); +} + +sub cart_items_nonstackable { + my ($self, $args) = @_; + + $self->_items_list({ + class => 'Actor::Item', + hook => 'packet_cart', + debug_str => 'Non-Stackable Cart Item', + items => [$self->parse_items_nonstackable($args)], + getter => sub { $char->cart->getByID($_[0]{ID}) }, + adder => sub { $char->cart->add($_[0]) }, + }); +} + +sub cart_item_added { + my ($self, $args) = @_; + + my $index = $args->{ID}; + my $amount = $args->{amount}; + + my $item = $char->cart->getByID($index); + if (!$item) { + $item = new Actor::Item(); + $item->{ID} = $args->{ID}; + $item->{nameID} = $args->{nameID}; + $item->{amount} = $args->{amount}; + $item->{identified} = $args->{identified}; + $item->{broken} = $args->{broken}; + $item->{upgrade} = $args->{upgrade}; + $item->{cards} = $args->{cards}; + $item->{options} = $args->{options}; + $item->{type} = $args->{type} if (exists $args->{type}); + $item->{name} = itemName($item); + $char->cart->add($item); + } else { + $item->{amount} += $args->{amount}; + } + my $disp = TF("Cart Item Added: %s (%d) x %d - %s", + $item->{name}, $item->{binID}, $amount, $itemTypes_lut{$item->{type}}); + message "$disp\n", "drop"; + $itemChange{$item->{name}} += $args->{amount}; + $args->{item} = $item; +} + +sub cart_item_removed { + my ($self, $args) = @_; + + my ($index, $amount) = @{$args}{qw(ID amount)}; + + my $item = $char->cart->getByID($index); + + if ($item) { + Misc::cartItemRemoved($item->{binID}, $amount); + } +} + +# Notifies client of a character parameter change. +# 0121 <current count>.W <max count>.W <current weight>.L <max weight>.L (ZC_NOTIFY_CARTITEM_COUNTINFO) +sub cart_info { + my ($self, $args) = @_; + $char->cart->info($args); + debug "[cart_info] received.\n", "parseMsg"; +} + +sub cart_add_failed { + my ($self, $args) = @_; + + my $reason; + if ($args->{fail} == 0) { + $reason = T('overweight'); + } elsif ($args->{fail} == 1) { + $reason = T('too many items'); + } else { + $reason = TF("Unknown code %s",$args->{fail}); + } + error TF("Can't Add Cart Item (%s)\n", $reason); +} + +sub inventory_items_stackable { + my ($self, $args) = @_; + return unless changeToInGameState(); + + $self->_items_list({ + class => 'Actor::Item', + hook => 'packet_inventory', + debug_str => 'Stackable Inventory Item', + items => [$self->parse_items_stackable($args)], + getter => sub { $char->inventory->getByID($_[0]{ID}) }, + adder => sub { $char->inventory->add($_[0]) }, + callback => sub { + my ($local_item) = @_; + + if (defined $char->{arrow} && $local_item->{ID} eq $char->{arrow}) { + $local_item->{equipped} = 32768; + $char->{equipment}{arrow} = $local_item; + } + } + }); +} + +sub item_list_start { + my ($self, $args) = @_; + $current_item_list = $args->{type}; + + debug "Starting Item List. ID: $args->{type}". ($args->{name} ? " ($args->{name})\n" : "\n"), "info"; + + if ( $args->{type} == INVTYPE_INVENTORY ) { + $char->inventory->onitemListStart(); + } elsif ( $args->{type} == INVTYPE_CART ) { + $char->cart->onitemListStart(); + } elsif ( $args->{type} == INVTYPE_STORAGE || $args->{type} == INVTYPE_GUILD_STORAGE ) { + $char->storage->onitemListStart(); + } else { + warning TF("Unsupported item_list_start type (%s)", $args->{type}), "info"; + } +} + +sub item_list_stackable { + my ($self, $args) = @_; + return unless changeToInGameState(); + + my $arguments = { + class => 'Actor::Item', + debug_str => 'Stackable Item List', + items => [$self->parse_items_stackable($args)], + callback => sub { + my ($local_item) = @_; + + if (defined $char->{arrow} && $local_item->{ID} eq $char->{arrow}) { + $local_item->{equipped} = 32768; + $char->{equipment}{arrow} = $local_item; + } + + } + }; + + if ( $args->{type} == INVTYPE_INVENTORY ) { + $arguments->{hook} = 'packet_inventory'; + $arguments->{getter} = sub { $char->inventory->getByID($_[0]{ID}) }; + $arguments->{adder} = sub { $char->inventory->add($_[0]) }; + } elsif ( $args->{type} == INVTYPE_CART ) { + $arguments->{hook} = 'packet_cart', + $arguments->{getter} = sub { $char->cart->getByID($_[0]{ID}) }, + $arguments->{adder} = sub { $char->cart->add($_[0]) }, + } elsif ( $args->{type} == INVTYPE_STORAGE ) { + $arguments->{hook} = 'packet_storage'; + $arguments->{getter} = sub { $char->storage->getByID($_[0]{ID}) }; + $arguments->{adder} = sub { $char->storage->add($_[0]) }; + } elsif ( $args->{type} == INVTYPE_GUILD_STORAGE ) { + $arguments->{hook} = 'packet_storage'; + $arguments->{getter} = sub { $char->storage->getByID($_[0]{ID}) }; + $arguments->{adder} = sub { $char->storage->add($_[0]) }; + } else { + warning TF("Unsupported item_list_stackable type (%s)", $args->{type}), "info"; + } + + $self->_items_list($arguments); +} + +sub item_list_nonstackable { + my ($self, $args) = @_; + return unless changeToInGameState(); + + my $arguments = { + class => 'Actor::Item', + debug_str => 'Non-Stackable Item List', + items => [$self->parse_items_nonstackable($args)], + callback => sub { + my ($local_item) = @_; + + if ($local_item->{equipped}) { + foreach (%equipSlot_rlut){ + if ($_ & $local_item->{equipped}){ + next if $_ == 10; #work around Arrow bug + next if $_ == 32768; + $char->{equipment}{$equipSlot_lut{$_}} = $local_item; + } + } + } + } + }; + + if ( $args->{type} == INVTYPE_INVENTORY ) { + $arguments->{hook} = 'packet_inventory'; + $arguments->{getter} = sub { $char->inventory->getByID($_[0]{ID}) }; + $arguments->{adder} = sub { $char->inventory->add($_[0]) }; + + } elsif ( $args->{type} == INVTYPE_CART ) { + $arguments->{hook} = 'packet_cart', + $arguments->{getter} = sub { $char->cart->getByID($_[0]{ID}) }, + $arguments->{adder} = sub { $char->cart->add($_[0]) }, + + } elsif ( $args->{type} == INVTYPE_STORAGE ) { + $arguments->{hook} = 'packet_storage'; + $arguments->{getter} = sub { $char->storage->getByID($_[0]{ID}) }; + $arguments->{adder} = sub { $char->storage->add($_[0]) }; + + } elsif ( $args->{type} == INVTYPE_GUILD_STORAGE ) { + $arguments->{hook} = 'packet_storage'; + $arguments->{getter} = sub { $char->storage->getByID($_[0]{ID}) }; + $arguments->{adder} = sub { $char->storage->add($_[0]) }; + + } else { + warning TF("Unsupported item_list_nonstackable type (%s)", $args->{type}), "info"; + } + + $self->_items_list($arguments); +} + +sub item_list_end { + my ($self, $args) = @_; + debug TF("Ending Item List. ID: %s\n", $args->{type}), "info"; + if ( $args->{type} == INVTYPE_INVENTORY ) { + $char->inventory->onitemListEnd(); + } elsif ( $args->{type} == INVTYPE_CART ) { + $char->cart->onitemListEnd(); + } elsif ( $args->{type} == INVTYPE_STORAGE || $args->{type} == INVTYPE_GUILD_STORAGE ) { + $char->storage->onitemListEnd(); + } else { + warning TF("Unsupported item_list_end type (%s)", $args->{type}), "info"; + } + undef $current_item_list; +} + +sub login_error { + my ($self, $args) = @_; + + $net->serverDisconnect(); + if ($args->{type} == REFUSE_INVALID_ID || $args->{type} == REFUSE_INVALID_ID2) { + error TF("Account name [%s] doesn't exist\n", $config{'username'}), "connection"; + if (!$net->clientAlive() && !$config{'ignoreInvalidLogin'} && !UNIVERSAL::isa($net, 'Network::XKoreProxy')) { + my $username = $interface->query(T("Enter your Ragnarok Online username again.")); + if (defined($username)) { + configModify('username', $username, 1); + $timeout_ex{master}{time} = 0; + $conState_tries = 0; + } else { + quit(); + return; + } + } + } elsif ($args->{type} == REFUSE_INVALID_PASSWD || $args->{type} == REFUSE_INVALID_PASSWD2) { + error TF("Password Error for account [%s]\n", $config{'username'}), "connection"; + Plugins::callHook('invalid_password'); + if (!$net->clientAlive() && !$config{'ignoreInvalidLogin'} && !UNIVERSAL::isa($net, 'Network::XKoreProxy')) { + my $password = $interface->query(T("Enter your Ragnarok Online password again."), isPassword => 1); + if (defined($password)) { + configModify('password', $password, 1); + $timeout_ex{master}{time} = 0; + $conState_tries = 0; + } else { + quit(); + return; + } + } + } elsif ($args->{type} == ACCEPT_ID_PASSWD) { + error T("The server has denied your connection.\n"), "connection"; + } elsif ($args->{type} == REFUSE_BAN_BY_GM || $args->{type} == REFUSE_NOT_CONFIRMED) { + $interface->errorDialog(T("Critical Error: Your account has been blocked.")); + $quit = 1 unless ($net->clientAlive()); + } elsif ($args->{type} == REFUSE_INVALID_VERSION) { + my $master = $masterServer; + error TF("Connect failed, something is wrong with the login settings:\n" . + "version: %s\n" . + "master_version: %s\n" . + "serverType: %s\n", $master->{version}, $master->{master_version}, $masterServer->{serverType}), "connection"; + relog(30); + } elsif ($args->{type} == REFUSE_BLOCK_TEMPORARY) { + error TF("The server is temporarily blocking your connection until %s\n", $args->{date}), "connection"; + } elsif ($args->{type} == REFUSE_USER_PHONE_BLOCK) { #Phone lock + error T("Please dial to activate the login procedure.\n"), "connection"; + Plugins::callHook('dial'); + relog(10); + } elsif ($args->{type} == ACCEPT_LOGIN_USER_PHONE_BLOCK) { + error T("Mobile Authentication: Max number of simultaneous IP addresses reached.\n"), "connection"; + } elsif ($args->{type} == REFUSE_EMAIL_NOT_CONFIRMED || $args->{type} == REFUSE_EMAIL_NOT_CONFIRMED2) { + error T("Account email address not confirmed.\n"), "connection"; + Misc::offlineMode() unless $config{ignoreInvalidLogin}; + } elsif ($args->{type} == REFUSE_BLOCKED_ID) { + error TF("The server is blocking connection from this user (%d).\n", $args->{error}), "connection"; + Misc::offlineMode() unless $config{ignoreInvalidLogin}; + } elsif ($args->{type} == REFUSE_BLOCKED_COUNTRY) { + error T("The server is blocking connections from your country.\n"); + Misc::offlineMode() unless $config{ignoreInvalidLogin}; + } elsif ($args->{type} == REFUSE_BILLING || $args->{type} == REFUSE_BILLING2) { + error TF("The server is blocking your connection due to billing issues (%d) (%d).\n", $args->{type}, $args->{error}); + Misc::offlineMode() unless $config{ignoreInvalidLogin}; + } elsif ($args->{type} == REFUSE_CHANGE_PASSWD_FORCE2) { + error T("The server demands a password change for this account.\n"); + error TF("Password Error for account [%s]\n", $config{'username'}), "connection"; + Plugins::callHook('invalid_password'); + if (!$net->clientAlive() && !$config{'ignoreInvalidLogin'} && !UNIVERSAL::isa($net, 'Network::XKoreProxy')) { + my $password = $interface->query(T("Enter your Ragnarok Online password again."), isPassword => 1); + if (defined($password)) { + configModify('password', $password, 1); + $timeout_ex{master}{time} = 0; + $conState_tries = 0; + } else { + quit(); + return; + } + } + } elsif ($args->{type} == REFUSE_ACCOUNT_NOT_PREMIUM) { + error TF("Account [%s] doesn't have access to Premium Server\n", $config{'username'}), "connection"; + quit(); + return; + } elsif ($args->{type} == REFUSE_NOT_ALLOWED_IP_ON_TESTING) { + # this can also mens server under maintenance + error TF("Your connection is currently delayed. You can connect again later.\n"), "connection"; + Misc::offlineMode(); + } elsif ($args->{type} == REFUSE_TOKEN_EXPIRED) { + error TF("Your connection was refused due to expired Token.\n"), "connection"; + } elsif ($args->{type} == REFUSE_BAN_ACCOUNT) { + error TF("Your account has been banned.\n"), "connection"; + Plugins::callHook('account_banned'); + } else { + error TF("The server has denied your connection for unknown reason (%d).\n", $args->{type}), 'connection'; + } + + if ($args->{type} != REFUSE_INVALID_VERSION && $versionSearch) { + $versionSearch = 0; + writeSectionedFileIntact(Settings::getTableFilename("servers.txt"), \%masterServers); + } +} + +sub login_error_game_login_server { + error T("Error logging into Character Server (invalid character specified)...\n"), 'connection'; + $net->setState(1); + undef $conState_tries; + $timeout_ex{master}{time} = time; + $timeout_ex{master}{timeout} = $timeout{'reconnect'}{'timeout'}; + $net->serverDisconnect(); +} + +sub character_deletion_successful { + if (defined $AI::temp::delIndex) { + message TF("Character %s (%d) deleted.\n", $chars[$AI::temp::delIndex]{name}, $AI::temp::delIndex), "info"; + delete $chars[$AI::temp::delIndex]; + undef $AI::temp::delIndex; + for (my $i = 0; $i < @chars; $i++) { + delete $chars[$i] if ($chars[$i] && !scalar(keys %{$chars[$i]})) + } + } else { + message T("Character deleted.\n"), "info"; + } + + if (charSelectScreen() == 1) { + $net->setState(3); + $firstLoginMap = 1; + $startingzeny = $chars[$config{'char'}]{'zeny'} unless defined $startingzeny; + $sentWelcomeMessage = 1; + } +} + +sub character_deletion_failed { + error T("Character cannot be deleted. Your e-mail address was probably wrong.\n"); + undef $AI::temp::delIndex; + if (charSelectScreen() == 1) { + $net->setState(3); + $firstLoginMap = 1; + $startingzeny = $chars[$config{'char'}]{'zeny'} unless defined $startingzeny; + $sentWelcomeMessage = 1; + } +} + +# Notifies the client, that it is walking (ZC_NOTIFY_PLAYERMOVE). +# 0087 <walk start time>.L <walk data>.6B +sub character_moves { + my ($self, $args) = @_; + + return unless changeToInGameState(); + makeCoordsFromTo($char->{pos}, $char->{pos_to}, $args->{coords}); + my $dist = blockDistance($char->{pos}, $char->{pos_to}); + debug "You're moving from ($char->{pos}{x}, $char->{pos}{y}) to ($char->{pos_to}{x}, $char->{pos_to}{y}) - distance $dist\n", "parseMsg_move"; + $char->{time_move} = time; + + my $speed = ($char->{walk_speed} || 0.12); + my $my_solution = get_solution($field, $char->{pos}, $char->{pos_to}); + my $time = calcTimeFromSolution($my_solution, $speed); + $char->{solution} = $my_solution; + $char->{time_move_calc} = $time; + + # Correct the direction in which we're looking + my (%vec, $degree); + getVector(\%vec, $char->{pos_to}, $char->{pos}); + $degree = vectorToDegree(\%vec); + if (defined $degree) { + my $direction = int sprintf("%.0f", (360 - $degree) / 45); + $char->{look}{body} = $direction & 0x07; + $char->{look}{head} = 0; + } + + # Ugly; AI code in network subsystem! This must be fixed. + if (AI::action eq "mapRoute" && $config{route_escape_reachedNoPortal} && $dist eq "0.0"){ + if (!$portalsID[0]) { + if ($config{route_escape_shout} ne "" && !defined($timeout{ai_route_escape}{time})){ + sendMessage("c", $config{route_escape_shout}); + } + $timeout{ai_route_escape}{time} = time; + AI::queue("escape"); + } + } +} + +sub character_name { + my ($self, $args) = @_; + my $name; # Type: String + + $name = bytesToString($args->{name}); + + if ($guild{member}) { + foreach my $guildMember (@{$guild{member}}) { + if ($guildMember->{charID} eq $args->{ID}) { + $guildMember->{name} = $name; + last; + } + } + } + + debug "Character name received: $name\n"; +} + +sub character_status { + my ($self, $args) = @_; + + my $actor = Actor::get($args->{ID}); + + if ($args->{switch} eq '028A') { + $actor->{lv} = $args->{lv}; # TODO: test if it is ok to use this piece of information + $actor->{opt3} = $args->{opt3}; + } elsif ($args->{switch} eq '0229' || $args->{switch} eq '0119') { + $actor->{opt1} = $args->{opt1}; + $actor->{opt2} = $args->{opt2}; + } + + $actor->{option} = $args->{option}; + + setStatus($actor, $args->{opt1}, $args->{opt2}, $args->{option}); +} + +# Whisper ignore list (ZC_WHISPER_LIST). +# 00D4 <packet len>.W { <char name>.24B }* +sub whisper_list { + my ($self, $args) = @_; + + my @whisperList = unpack 'x4' . (' Z24' x (($args->{RAW_MSG_SIZE}-4)/24)), $args->{RAW_MSG}; + + debug "whisper_list: @whisperList\n", "parseMsg"; +} + +# Inform client whether chatroom creation was successful or not (ZC_ACK_CREATE_CHATROOM). +# 00D6 <flag>.B +# flag: +# 0 = Room has been successfully created (opens chat room) +# 1 = Room limit exceeded +# 2 = Same room already exists +sub chat_created { + my ($self, $args) = @_; + + $currentChatRoom = $accountID; + $chatRooms{$accountID} = {%createdChatRoom}; + binAdd(\@chatRoomsID, $accountID); + binAdd(\@currentChatRoomUsers, $char->{name}); + message T("Chat Room Created\n"); + + Plugins::callHook('chat_created', {chat => $chatRooms{$accountID}}); +} + +# Display a chat above the owner (ZC_ROOM_NEWENTRY). +# 00D7 <packet len>.W <owner id>.L <char id>.L <limit>.W <users>.W <type>.B <title>.?B +# type: +# 0 = private (password protected) +# 1 = public +# 2 = arena (npc waiting room) +# 3 = PK zone (non-clickable) +sub chat_info { + my ($self, $args) = @_; + + my $title = bytesToString($args->{title}); + + my $chat = $chatRooms{$args->{ID}}; + if (!$chat || !%{$chat}) { + $chat = $chatRooms{$args->{ID}} = {}; + binAdd(\@chatRoomsID, $args->{ID}); + } + $chat->{len} = $args->{len}; + $chat->{title} = $title; + $chat->{ownerID} = $args->{ownerID}; + $chat->{limit} = $args->{limit}; + $chat->{public} = $args->{public}; + $chat->{num_users} = $args->{num_users}; + + Plugins::callHook('packet_chatinfo', { + chatID => $args->{ID}, + ownerID => $args->{ownerID}, + title => $title, + limit => $args->{limit}, + public => $args->{public}, + num_users => $args->{num_users} + }); +} + +# Notifies the client about entering a chatroom (ZC_ENTER_ROOM). +# 00DB <packet len>.W <chat id>.L { <role>.L <name>.24B }* +# role: +# 0 = owner (menu) +# 1 = normal +sub chat_users { + my ($self, $args) = @_; + + my $msg = $args->{RAW_MSG}; + + my $ID = substr($args->{RAW_MSG},4,4); + $currentChatRoom = $ID; + + my $chat = $chatRooms{$currentChatRoom} ||= {}; + + $chat->{num_users} = 0; + for (my $i = 8; $i < $args->{RAW_MSG_SIZE}; $i += 28) { + my ($type, $chatUser) = unpack('V Z24', substr($msg, $i, 28)); + + $chatUser = bytesToString($chatUser); + + if ($chat->{users}{$chatUser} eq "") { + binAdd(\@currentChatRoomUsers, $chatUser); + if ($type == 0) { + $chat->{users}{$chatUser} = 2; + } else { + $chat->{users}{$chatUser} = 1; + } + $chat->{num_users}++; + } + } + + message TF("You have joined the Chat Room %s\n", $chat->{title}); + + Plugins::callHook('chat_joined', {chat => $chat}); +} + +# Displays messages regarding join chat failures (ZC_REFUSE_ENTER_ROOM). +# 00DA <result>.B +# result: +# 0 = room full +# 1 = wrong password +# 2 = kicked +# 3 = success (no message) +# 4 = no enough zeny +# 5 = too low level +# 6 = too high level +# 7 = unsuitable job class +sub chat_join_result { + my ($self, $args) = @_; + + if ($args->{type} == 0) { + message T("Can't join Chat Room - Room is Full\n"); + } elsif ($args->{type} == 1) { + message T("Can't join Chat Room - Incorrect Password\n"); + } elsif ($args->{type} == 2) { + message T("Can't join Chat Room - You're Kicked\n"); + } elsif ($args->{type} == 2) { + message T("Joined Chat Room\n"); + } elsif ($args->{type} == 2) { + message T("Can't join Chat Room - No Enough Zeny\n"); # ?? + } elsif ($args->{type} == 2) { + message T("Can't join Chat Room - You're Low Level\n"); + } elsif ($args->{type} == 2) { + message T("Can't join Chat Room - You're High Level\n"); + } elsif ($args->{type} == 2) { + message T("Can't join Chat Room - You're Unsuitable Job Class\n"); + } else { + message TF("Can't join Chat Room - Unknown Reason (%s)\n", $args->{type}); + } +} + +# Chatroom properties adjustment (ZC_CHANGE_CHATROOM). +# 00DF <packet len>.W <owner id>.L <chat id>.L <limit>.W <users>.W <type>.B <title>.?B +# type: +# 0 = private (password protected) +# 1 = public +# 2 = arena (npc waiting room) +# 3 = PK zone (non-clickable) +sub chat_modified { + my ($self, $args) = @_; + + my $title = bytesToString($args->{title}); + + my ($ownerID, $chat_ID, $limit, $public, $num_users) = @{$args}{qw(ownerID ID limit public num_users)}; + my $ID; + if ($ownerID eq $accountID) { + $ID = $accountID; + } else { + $ID = $chat_ID; + } + + my %chat = (); + $chat{title} = $title; + $chat{ownerID} = $ownerID; + $chat{limit} = $limit; + $chat{public} = $public; + $chat{num_users} = $num_users; + + Plugins::callHook('chat_modified', { + ID => $ID, + old => $chatRooms{$ID}, + new => \%chat, + }); + + $chatRooms{$ID} = {%chat}; + + message T("Chat Room Properties Modified\n"); +} + +# Announce the new owner (ZC_ROLE_CHANGE). +# 00E1 <role>.L <nick>.24B +# role: +# 0 = owner (menu) +# 1 = normal +sub chat_newowner { + my ($self, $args) = @_; + + my $user = bytesToString($args->{user}); + if ($args->{type} == 0) { + if ($user eq $char->{name}) { + $chatRooms{$currentChatRoom}{ownerID} = $accountID; + } else { + my $player; + for my $p (@$playersList) { + if ($p->{name} eq $user) { + $player = $p; + last; + } + } + + if ($player) { + my $key = $player->{ID}; + $chatRooms{$currentChatRoom}{ownerID} = $key; + } + } + $chatRooms{$currentChatRoom}{users}{$user} = 2; + } else { + $chatRooms{$currentChatRoom}{users}{$user} = 1; + } +} + +# Notifies clients in a chat about a new member (ZC_MEMBER_NEWENTRY). +# 00DC <users>.W <name>.24B +sub chat_user_join { + my ($self, $args) = @_; + + my $user = bytesToString($args->{user}); + if ($currentChatRoom ne "") { + binAdd(\@currentChatRoomUsers, $user); + $chatRooms{$currentChatRoom}{users}{$user} = 1; + $chatRooms{$currentChatRoom}{num_users} = $args->{num_users}; + message TF("%s has joined the Chat Room\n", $user); + } +} + +# Notify about user leaving the chatroom (ZC_MEMBER_EXIT). +# 00DD <users>.W <nick>.24B <flag>.B +# flag: +# 0 = left +# 1 = kicked +sub chat_user_leave { + my ($self, $args) = @_; + + my $user = bytesToString($args->{user}); + delete $chatRooms{$currentChatRoom}{users}{$user}; + binRemove(\@currentChatRoomUsers, $user); + $chatRooms{$currentChatRoom}{num_users} = $args->{num_users}; + if ($user eq $char->{name}) { + binRemove(\@chatRoomsID, $currentChatRoom); + delete $chatRooms{$currentChatRoom}; + undef @currentChatRoomUsers; + $currentChatRoom = ""; + message T("You left the Chat Room\n"); + Plugins::callHook('chat_leave'); + } else { + message TF("%s has left the Chat Room\n", $user); + } +} + +# Removes the chatroom (ZC_DESTROY_ROOM). +# 00D8 <chat id>.L +sub chat_removed { + my ($self, $args) = @_; + + binRemove(\@chatRoomsID, $args->{ID}); + my $chat = delete $chatRooms{ $args->{ID} }; + + Plugins::callHook('chat_removed', { + ID => $args->{ID}, + chat => $chat, + }); +} + +sub deal_add_other { + my ($self, $args) = @_; + + if ($args->{nameID} > 0) { + my $item = $currentDeal{other}{ $args->{nameID} } ||= {}; + $item->{amount} += $args->{amount}; + $item->{nameID} = $args->{nameID}; + $item->{identified} = $args->{identified}; + $item->{broken} = $args->{broken}; + $item->{upgrade} = $args->{upgrade}; + $item->{cards} = $args->{cards}; + $item->{options} = $args->{options}; + $item->{name} = itemName($item); + message TF("%s added Item to Deal: %s x %s\n", $currentDeal{name}, $item->{name}, $args->{amount}), "deal"; + } elsif ($args->{amount} > 0) { + $currentDeal{other_zeny} += $args->{amount}; + my $amount = formatNumber($args->{amount}); + message TF("%s added %s z to Deal\n", $currentDeal{name}, $amount), "deal"; + } +} + +sub deal_begin { + my ($self, $args) = @_; + + if ($args->{type} == 0) { + error T("That person is too far from you to trade.\n"), "deal"; + Plugins::callHook('error_deal', {type => $args->{type}}); + undef %outgoingDeal; + + } elsif ($args->{type} == 2) { + error T("That person is in another deal.\n"), "deal"; + Plugins::callHook('error_deal', {type => $args->{type}}); + undef %outgoingDeal; + + } elsif ($args->{type} == 3) { + if (%incomingDeal) { + $currentDeal{name} = $incomingDeal{name}; + undef %incomingDeal; + } else { + my $ID = $outgoingDeal{ID}; + my $player; + $player = $playersList->getByID($ID) if (defined $ID); + $currentDeal{ID} = $ID; + if ($player) { + $currentDeal{name} = $player->{name}; + } else { + $currentDeal{name} = T('Unknown #') . unpack("V", $ID); + } + undef %outgoingDeal; + } + message TF("Engaged Deal with %s\n", $currentDeal{name}), "deal"; + Plugins::callHook('engaged_deal', {name => $currentDeal{name}}); + + } elsif ($args->{type} == 5) { + error T("That person is opening storage.\n"), "deal"; + Plugins::callHook('error_deal', {type =>$args->{type}}); + undef %outgoingDeal; + + } else { + error TF("Deal request failed (unknown error %s).\n", $args->{type}), "deal"; + Plugins::callHook('error_deal', {type =>$args->{type}}); + undef %outgoingDeal; + + } +} + +sub deal_cancelled { + undef %incomingDeal; + undef %outgoingDeal; + undef %currentDeal; + message T("Deal Cancelled\n"), "deal"; + Plugins::callHook('cancelled_deal'); +} + +sub deal_complete { + undef %outgoingDeal; + undef %incomingDeal; + undef %currentDeal; + message T("Deal Complete\n"), "deal"; + Plugins::callHook('complete_deal'); +} + +sub deal_finalize { + my ($self, $args) = @_; + if ($args->{type} == 1) { + $currentDeal{other_finalize} = 1; + message TF("%s finalized the Deal\n", $currentDeal{name}), "deal"; + Plugins::callHook('finalized_deal', {name => $currentDeal{name}}); + + } else { + $currentDeal{you_finalize} = 1; + # FIXME: shouldn't we do this when we actually complete the deal? + $char->{zeny} -= $currentDeal{you_zeny}; + message T("You finalized the Deal\n"), "deal"; + } +} + +sub deal_request { + my ($self, $args) = @_; + my $level = $args->{level} || 'Unknown'; # TODO: store this info + my $user = bytesToString($args->{user}); + + $incomingDeal{name} = $user; + $timeout{ai_dealAutoCancel}{time} = time; + message TF("%s (level %s) Requests a Deal\n", $user, $level), "deal"; + message T("Type 'deal' to start dealing, or 'deal no' to deny the deal.\n"), "deal"; + Plugins::callHook('incoming_deal', { + name => $user, + level => $level, + ID => $args->{ID} + }); +} + +sub devotion { + my ($self, $args) = @_; + my $msg = ''; + my $source = Actor::get($args->{sourceID}); + + undef $devotionList->{$args->{sourceID}}; + for (my $i = 0; $i < 5; $i++) { + my $ID = substr($args->{targetIDs}, $i*4, 4); + last if unpack("V", $ID) == 0; + $devotionList->{$args->{sourceID}}->{targetIDs}->{$ID} = $i; + my $actor = Actor::get($ID); + #FIXME: Need a better display + $msg .= skillUseNoDamage_string($source, $actor, 0, 'devotion'); + } + $devotionList->{$args->{sourceID}}->{range} = $args->{range}; + + message "$msg", "devotion"; +} + +sub egg_list { + my ($self, $args) = @_; + my $msg = center(T(" Egg Hatch Candidates "), 38, '-') ."\n"; + for (my $i = 4; $i < $args->{RAW_MSG_SIZE}; $i += 2) { + my $index = unpack("a2", substr($args->{RAW_MSG}, $i, 2)); + my $item = $char->inventory->getByID($index); + $msg .= "$item->{binID} $item->{name}\n"; + } + $msg .= ('-'x38) . "\n". + T("Ready to use command 'pet [hatch|h] #'\n"); + message $msg, "list"; +} + +sub emoticon { + my ($self, $args) = @_; + my $emotion = $emotions_lut{$args->{type}}{display} || "<emotion #$args->{type}>"; + + if ($args->{ID} eq $accountID) { + message "$char->{name}: $emotion\n", "emotion"; + chatLog("e", "$char->{name}: $emotion\n") if (existsInList($config{'logEmoticons'}, $args->{type}) || $config{'logEmoticons'} eq "all"); + + } elsif (my $player = $playersList->getByID($args->{ID})) { + my $name = $player->name; + + #my $dist = "unknown"; + my $dist = distance($char->{pos_to}, $player->{pos_to}); + $dist = sprintf("%.1f", $dist) if ($dist =~ /\./); + + # Translation Comment: "[dist=$dist] $name ($player->{binID}): $emotion\n" + message TF("[dist=%s] %s (%d): %s\n", $dist, $name, $player->{binID}, $emotion), "emotion"; + chatLog("e", "$name".": $emotion\n") if (existsInList($config{'logEmoticons'}, $args->{type}) || $config{'logEmoticons'} eq "all"); + + my $index = AI::findAction("follow"); + if ($index ne "") { + my $masterID = AI::args($index)->{ID}; + if ($config{'followEmotion'} && $masterID eq $args->{ID} && + blockDistance($char->{pos_to}, $player->{pos_to}) <= $config{'followEmotion_distance'}) + { + my %args = (); + $args{timeout} = time + rand (1) + 0.75; + + if ($args->{type} == 30) { + $args{emotion} = 31; + } elsif ($args->{type} == 31) { + $args{emotion} = 30; + } else { + $args{emotion} = $args->{type}; + } + + AI::queue("sendEmotion", \%args); + } + } + } elsif (my $monster = $monstersList->getByID($args->{ID}) || $slavesList->getByID($args->{ID})) { + my $dist = distance($char->{pos_to}, $monster->{pos_to}); + $dist = sprintf("%.1f", $dist) if ($dist =~ /\./); + + # Translation Comment: "[dist=$dist] $monster->name ($monster->{binID}): $emotion\n" + message TF("[dist=%s] %s %s (%d): %s\n", $dist, $monster->{actorType}, $monster->name, $monster->{binID}, $emotion), "emotion"; + + } else { + my $actor = Actor::get($args->{ID}); + my $name = $actor->name; + + my $dist = T("unknown"); + if (!$actor->isa('Actor::Unknown')) { + $dist = distance($char->{pos_to}, $actor->{pos_to}); + $dist = sprintf("%.1f", $dist) if ($dist =~ /\./); + } + + message TF("[dist=%s] %s: %s\n", $dist, $actor->nameIdx, $emotion), "emotion"; + chatLog("e", "$name".": $emotion\n") if (existsInList($config{'logEmoticons'}, $args->{type}) || $config{'logEmoticons'} eq "all"); + } + Plugins::callHook('packet_emotion', { + emotion => $emotion, + ID => $args->{ID} + }); +} + +# Notifies the client of a ban or forced disconnect (SC_NOTIFY_BAN). +# 0081 <error code>.B +# error code: +# 0 = BAN_UNFAIR -> "disconnected from server" -> MsgStringTable[3] +# 1 = server closed -> MsgStringTable[4] +# 2 = ID already logged in -> MsgStringTable[5] +# 3 = timeout/too much lag -> MsgStringTable[241] +# 4 = server full -> MsgStringTable[264] +# 5 = underaged -> MsgStringTable[305] +# 8 = Server sill recognizes last connection -> MsgStringTable[441] +# 9 = too many connections from this ip -> MsgStringTable[529] +# 10 = out of available time paid for -> MsgStringTable[530] +# 11 = BAN_PAY_SUSPEND +# 12 = BAN_PAY_CHANGE +# 13 = BAN_PAY_WRONGIP +# 14 = BAN_PAY_PNGAMEROOM +# 15 = disconnected by a GM -> if (servicetype == taiwan) MsgStringTable[579] +# 16 = BAN_JAPAN_REFUSE1 +# 17 = BAN_JAPAN_REFUSE2 +# 18 = BAN_INFORMATION_REMAINED_ANOTHER_ACCOUNT +# 100 = BAN_PC_IP_UNFAIR +# 101 = BAN_PC_IP_COUNT_ALL +# 102 = BAN_PC_IP_COUNT +# 103 = BAN_GRAVITY_MEM_AGREE +# 104 = BAN_GAME_MEM_AGREE +# 105 = BAN_HAN_VALID +# 106 = BAN_PC_IP_LIMIT_ACCESS +# 107 = BAN_OVER_CHARACTER_LIST +# 108 = BAN_IP_BLOCK +# 109 = BAN_INVALID_PWD_CNT +# 110 = BAN_NOT_ALLOWED_JOBCLASS +# 113 = access is restricted between the hours of midnight to 6:00am. +# 115 = You are in game connection ban period. +# ? = disconnected -> MsgStringTable[3] +sub errors { + my ($self, $args) = @_; + + Plugins::callHook('disconnected') if ($net->getState() == Network::IN_GAME); + if ($net->getState() == Network::IN_GAME && + ($config{dcOnDisconnect} > 1 || + ($config{dcOnDisconnect} && + $args->{type} != 3 && + $args->{type} != 10))) { + error T("Auto disconnecting on Disconnect!\n"); + chatLog("k", T("*** You disconnected, auto disconnect! ***\n")); + $quit = 1; + } + + $net->setState(1); + undef $conState_tries; + + $timeout_ex{'master'}{'time'} = time; + $timeout_ex{'master'}{'timeout'} = $timeout{'reconnect'}{'timeout'}; + if (($args->{type} != 0)) { + $net->serverDisconnect(); + } + if ($args->{type} == 0) { + # FIXME BAN_SERVER_SHUTDOWN is 0x1, 0x0 is BAN_UNFAIR + if ($config{'dcOnServerShutDown'} == 1) { + error T("Auto disconnecting on ServerShutDown!\n"); + chatLog("k", T("*** Server shutting down , auto disconnect! ***\n")); + $quit = 1; + } else { + error T("Server shutting down\n"), "connection"; + } + } elsif ($args->{type} == 1) { + if ($config{'dcOnServerClose'} == 1) { + error T("Auto disconnecting on ServerClose!\n"); + chatLog("k", T("*** Server is closed , auto disconnect! ***\n")); + $quit = 1; + } else { + error T("Error: Server is closed\n"), "connection"; + } + } elsif ($args->{type} == 2) { + if ($config{'dcOnDualLogin'} == 1) { + error (TF("Critical Error: Dual login prohibited - Someone trying to login!\n\n" . + "%s will now immediately disconnect.\n", $Settings::NAME)); + chatLog("k", T("*** DualLogin, auto disconnect! ***\n")); + quit(); + } elsif ($config{'dcOnDualLogin'} >= 2) { + error T("Critical Error: Dual login prohibited - Someone trying to login!\n"); + message TF("Reconnecting, wait %s seconds...\n", $config{'dcOnDualLogin'}), "connection"; + $timeout_ex{'master'}{'timeout'} = $config{'dcOnDualLogin'}; + } else { + error T("Critical Error: Dual login prohibited - Someone trying to login!\n"), "connection"; + } + + } elsif ($args->{type} == 3) { + error T("Error: Out of sync with server\n"), "connection"; + } elsif ($args->{type} == 4) { + # fRO: "Your account is not validated, please click on the validation link in your registration mail." + error T("Error: Server is jammed due to over-population.\n"), "connection"; + } elsif ($args->{type} == 5) { + error T("Error: You are underaged and cannot join this server.\n"), "connection"; + } elsif ($args->{type} == 6) { + $interface->errorDialog(T("Critical Error: You must pay to play this account!\n")); + $quit = 1 unless ($net->version == 1); + } elsif ($args->{type} == 8) { + error T("Error: The server still recognizes your last connection\n"), "connection"; + } elsif ($args->{type} == 9) { + error T("Error: IP capacity of this Internet Cafe is full. Would you like to pay the personal base?\n"), "connection"; + } elsif ($args->{type} == 10) { + error T("Error: You are out of available time paid for\n"), "connection"; + } elsif ($args->{type} == 15) { + error T("Error: You have been forced to disconnect by a GM\n"), "connection"; + } elsif ($args->{type} == 101) { + error T("Error: Your account has been suspended until the next maintenance period for possible use of 3rd party programs\n"), "connection"; + } elsif ($args->{type} == 102) { + error T("Error: For an hour, more than 10 connections having same IP address, have made. Please check this matter.\n"), "connection"; + } else { + error TF("Unknown error %s\n", $args->{type}), "connection"; + } +} + +# Sends the whole friends list (ZC_FRIENDS_LIST). +# 0201 <packet len>.W { <account id>.L <char id>.L <name>.24B }* +# 0201 <packet len>.W { <account id>.L <char id>.L }* +sub friend_list { + my ($self, $args) = @_; + + # Friend list + undef @friendsID; + undef %friends; + my $msg = $args->{RAW_MSG}; + my $msg_size = $args->{RAW_MSG_SIZE}; + + my $ID = 0; + for (my $i = 4; $i < $msg_size; $i += 32) { + binAdd(\@friendsID, $ID); + ($friends{$ID}{'accountID'}, + $friends{$ID}{'charID'}, + $friends{$ID}{'name'}) = unpack('a4 a4 Z24', substr($args->{RAW_MSG}, $i, 32)); + + $friends{$ID}{'name'} = bytesToString($friends{$ID}{'name'}); + $friends{$ID}{'online'} = 0; + $ID++; + } +} + +# Toggles a single friend online/offline (ZC_FRIENDS_STATE). +# 0206 <account id>.L <char id>.L <state>.B +# 0206 <account id>.L <char id>.L <state>.B <name>.24B +# state: +# 0 = online +# 1 = offline +sub friend_logon { + my ($self, $args) = @_; + + # Friend In/Out + my $friendAccountID = $args->{friendAccountID}; + my $friendCharID = $args->{friendCharID}; + my $isNotOnline = $args->{isNotOnline}; + + for (my $i = 0; $i < @friendsID; $i++) { + if ($friends{$i}{'accountID'} eq $friendAccountID && $friends{$i}{'charID'} eq $friendCharID) { + $friends{$i}{'online'} = 1 - $isNotOnline; + if ($isNotOnline) { + message TF("Friend %s has disconnected\n", $friends{$i}{name}), undef, 1; + } else { + message TF("Friend %s has connected\n", $friends{$i}{name}), undef, 1; + } + last; + } + } +} + +# Asks a player for permission to be added as friend (ZC_REQ_ADD_FRIENDS). +# 0207 <req account id>.L <req char id>.L <req char name>.24B +sub friend_request { + my ($self, $args) = @_; + + # Incoming friend request + $incomingFriend{'accountID'} = $args->{accountID}; + $incomingFriend{'charID'} = $args->{charID}; + $incomingFriend{'name'} = bytesToString($args->{name}); + message TF("%s wants to be your friend\n", $incomingFriend{'name'}); + message TF("Type 'friend accept' to be friend with %s, otherwise type 'friend reject'\n", $incomingFriend{'name'}); + Plugins::callHook('friend_request', { + accountID => $incomingFriend{'accountID'}, + charID => $incomingFriend{'charID'}, + name => $incomingFriend{'name'} + }); +} + +# Notification about a friend removed (PACKET_ZC_DELETE_FRIENDS). +# 020A <account id>.L <char id>.L +sub friend_removed { + my ($self, $args) = @_; + + # Friend removed + my $friendAccountID = $args->{friendAccountID}; + my $friendCharID = $args->{friendCharID}; + for (my $i = 0; $i < @friendsID; $i++) { + if ($friends{$i}{'accountID'} eq $friendAccountID && $friends{$i}{'charID'} eq $friendCharID) { + message TF("%s is no longer your friend\n", $friends{$i}{'name'}); + binRemove(\@friendsID, $i); + delete $friends{$i}; + last; + } + } +} + +# Notification about the result of a friend add request (ZC_ADD_FRIENDS_LIST). +# 0209 <result>.W <account id>.L <char id>.L <name>.24B +# result: +# 0 = MsgStringTable[821]="You have become friends with (%s)." +# 1 = MsgStringTable[822]="(%s) does not want to be friends with you." +# 2 = MsgStringTable[819]="Your Friend List is full." +# 3 = MsgStringTable[820]="(%s)'s Friend List is full." +sub friend_response { + my ($self, $args) = @_; + + # Response to friend request + my $type = $args->{type}; + my $name = bytesToString($args->{name}); + if ($type == 0) { + my $ID = @friendsID; + binAdd(\@friendsID, $ID); + $friends{$ID}{accountID} = substr($args->{RAW_MSG}, 4, 4); + $friends{$ID}{charID} = substr($args->{RAW_MSG}, 8, 4); + $friends{$ID}{name} = $name; + $friends{$ID}{online} = 1; + message TF("You have become friends with (%s)\n", $name); + } elsif ($type == 1) { + message TF("(%s) does not want to be friends with you\n", $name); + } elsif ($type == 2) { + message T("Your Friend List is full"); + } elsif ($type == 3) { + message TF("%s's Friend List is full\n", $name); + } else { + message TF("%s rejected to be your friend\n", $name); + } +} + +# Result of request to feed a homun/merc (ZC_FEED_MER). +# 022F <result>.B <name id>.W +# result: +# 0 = failure +# 1 = success +sub homunculus_food { + my ($self, $args) = @_; + if ($args->{success}) { + message TF("Fed homunculus with %s\n", itemNameSimple($args->{foodID})), "homunculus"; + } else { + error TF("Failed to feed homunculus with %s: no food in inventory.\n", itemNameSimple($args->{foodID})), "homunculus"; + # auto-vaporize + if ($char->{homunculus} && $char->{homunculus}{hunger} <= 11 && timeOut($char->{homunculus}{vaporize_time}, 5)) { + $messageSender->sendSkillUse(244, 1, $accountID); + $char->{homunculus}{vaporize_time} = time; + error "Critical hunger level reached. Homunculus is put to rest.\n", "homunculus"; + } + } +} + +# TODO: wouldn't it be better if we calculated these only at (first) request after a change in value, if requested at all? +sub slave_calcproperty_handler { + my ($slave, $args) = @_; + # so we don't devide by 0 + # wtf +=pod + $slave->{hp_max} = ($args->{hp_max} > 0) ? $args->{hp_max} : $args->{hp}; + $slave->{sp_max} = ($args->{sp_max} > 0) ? $args->{sp_max} : $args->{sp}; +=cut + + $slave->{attack_speed} = int (200 - (($args->{aspd} < 10) ? 10 : ($args->{aspd} / 10))); +} + +sub EAC_key { + return if ($masterServer->{'ignoreAntiCheatWarning'}); + chatLog("k", T("*** Easy Anti-Cheat Detected ***\n")); + error T("OpenKore don't have support to servers with Easy Anti-Cheat Shield, please read the FAQ (github).\n"); + quit(); +} + +sub gameguard_grant { + my ($self, $args) = @_; + + if ($args->{server} == 0) { + error T("The server Denied the login because GameGuard packets where not replied " . + "correctly or too many time has been spent to send the response.\n" . + "Please verify the version of your poseidon server and try again\n"), "poseidon"; + return; + } elsif ($args->{server} == 1) { + message T("Server granted login request to account server\n"), "poseidon"; + } else { + message T("Server granted login request to char/map server\n"), "poseidon"; + # FIXME + change_to_constate25() if ($masterServer->{'gameGuard'} eq "2"); + } + $net->setState(1.3) if ($net->getState() == 1.2); +} + +sub gameguard_request { + my ($self, $args) = @_; + + return if (($net->version == 1 && $masterServer->{gameGuard} ne '2') || ($masterServer->{gameGuard} == 0)); + Poseidon::Client::getInstance()->query( + substr($args->{RAW_MSG}, 0, $args->{RAW_MSG_SIZE}) + ); + debug "Querying Poseidon\n", "poseidon"; +} + +# Guild alliance and opposition list (ZC_MYGUILD_BASIC_INFO). +# 014C <packet len>.W { <relation>.L <guild id>.L <guild name>.24B }* +sub guild_allies_enemy_list { + my ($self, $args) = @_; + + # Guild Allies/Enemy List + # <len>.w (<type>.l <guildID>.l <guild name>.24B).* + # type=0 Ally + # type=1 Enemy + + # This is the length of the entire packet + my $msg = $args->{RAW_MSG}; + my $len = unpack("v", substr($msg, 2, 2)); + + # clear $guild{enemy} and $guild{ally} otherwise bot will misremember alliances -zdivpsa + $guild{enemy} = {}; $guild{ally} = {}; + + for (my $i = 4; $i < $len; $i += 32) { + my ($type, $guildID, $guildName) = unpack('V2 Z24', substr($msg, $i, 32)); + $guildName = bytesToString($guildName); + if ($type) { + # Enemy guild + $guild{enemy}{$guildID} = $guildName; + } else { + # Allied guild + $guild{ally}{$guildID} = $guildName; + } + debug "Your guild is ".($type ? 'enemy' : 'ally')." with guild $guildID ($guildName)\n", "guild"; + } +} + +# Request for guild alliance (ZC_REQ_ALLY_GUILD). +# 0171 <inviter account id>.L <guild name>.24B +sub guild_ally_request { + my ($self, $args) = @_; + + my $ID = $args->{ID}; # is this a guild ID or account ID? Freya calls it an account ID + my $name = bytesToString($args->{guildName}); # Type: String + + message TF("Incoming Request to Ally Guild '%s'\n", $name); + $incomingGuild{ID} = $ID; + $incomingGuild{Type} = 2; + $timeout{ai_guildAutoDeny}{time} = time; +} + +# Notifies the client about the result of a guild break (ZC_ACK_DISORGANIZE_GUILD_RESULT). +# 015E <reason>.L +# 0 = success +# 1 = invalid key (guild name, @see clif_parse_GuildBreak) +# 2 = there are still members in the guild +sub guild_broken { + my ($self, $args) = @_; + my $flag = $args->{flag}; + + if ($flag == 2) { + error T("Guild can not be undone: there are still members in the guild\n"); + } elsif ($flag == 1) { + error T("Guild can not be undone: invalid key\n"); + } elsif ($flag == 0) { + message T("Guild broken.\n"); + undef %{$char->{guild}}; + undef $char->{guildID}; + undef %guild; + } else { + error TF("Guild can not be undone: unknown reason (flag: %s)\n", $flag); + } +} + +# Guild creation result (ZC_RESULT_MAKE_GUILD). +# 0167 <result>.B +# result: +# 0 = "Guild has been created." +# 1 = "You are already in a Guild." +# 2 = "That Guild Name already exists." +# 3 = "You need the neccessary item to create a Guild." +sub guild_create_result { + my ($self, $args) = @_; + my $type = $args->{type}; + + my %types = ( + 0 => T("Guild create successful.\n"), + 2 => T("Guild create failed: Guild name already exists.\n"), + 3 => T("Guild create failed: Emperium is needed.\n") + ); + if ($types{$type}) { + message $types{$type}; + } else { + message TF("Guild create: Unknown error %s\n", $type); + } +} + +# Guild basic information +# 0150 <guild id>.L <level>.L <member num>.L <member max>.L <exp>.L <max exp>.L <points>.L <honor>.L <virtue>.L <emblem id>.L <name>.24B <master name>.24B <manage land>.16B (ZC_GUILD_INFO) +# 01B6 <guild id>.L <level>.L <member num>.L <member max>.L <exp>.L <max exp>.L <points>.L <honor>.L <virtue>.L <emblem id>.L <name>.24B <master name>.24B <manage land>.16B <zeny>.L (ZC_GUILD_INFO2) +# 0A84 <guild id>.L <level>.L <member num>.L <member max>.L <exp>.L <max exp>.L <points>.L <honor>.L <virtue>.L <emblem id>.L <name>.24B <manage land>.16B <zeny>.L <master char id>.L (ZC_GUILD_INFO3) +# 0B7B +sub guild_info { + my ($self, $args) = @_; + # Guild Info + foreach (@{$args->{KEYS}}) { + $guild{$_} = $args->{$_}; + } + $guild{name} = bytesToString($args->{name}); + $guild{master} = bytesToString($args->{master}) if ($args->{master}); + $guild{members}++; # count ourselves in the guild members count +} + +# Guild member manager information +# 0154 <packet len>.W { <account>.L <char id>.L <hair style>.W <hair color>.W <gender>.W <class>.W <level>.W <contrib exp>.L <state>.L <position>.L <memo>.50B <name>.24B }* (ZC_MEMBERMGR_INFO) +# 0AA5 <packet len>.W { <account>.L <char id>.L <hair style>.W <hair color>.W <gender>.W <class>.W <level>.W <contrib exp>.L <state>.L <position>.L <lastlogin>.L }* +# 0B7D +# state: +# 0 = offline +# 1 = online +sub guild_members_list { + my ($self, $args) = @_; + + my $guild_member_info; + if ($args->{switch} eq "0B7D") { + $guild_member_info = { + len => 58, + types => 'a4 a4 v5 V4 Z24', + keys => [qw(ID charID hair_style hair_color sex jobID lv contribution online position lastLoginTime name)], + }; + + } elsif ($args->{switch} eq "0AA5") { + $guild_member_info = { + len => 34, + types => 'a4 a4 v5 V4', + keys => [qw(ID charID hair_style hair_color sex jobID lv contribution online position lastLoginTime)], + }; + + } else { # 0154, others + $guild_member_info = { + len => 104, + types => 'a4 a4 v5 V3 Z50 Z24', + keys => [qw(ID charID hair_style hair_color sex jobID lv contribution online position memo name)], + }; + } + + delete $guild{member}; + my $index = 0; + + for (my $i = 0; $i < length($args->{member_list}); $i += $guild_member_info->{len}) { + @{$guild{member}[$index]}{@{$guild_member_info->{keys}}} = unpack($guild_member_info->{types}, substr($args->{member_list}, $i, $guild_member_info->{len})); + + $guild{member}[$index]{name} = bytesToString($guild{member}[$index]{name}) if ($guild{member}[$index]{name}); + $messageSender->sendGetCharacterName($guild{member}[$index]{charID}) if ($args->{switch} eq "0AA5"); + $index++; + } +} + +# Reply to invite request (ZC_ACK_REQ_JOIN_GUILD). +# 0169 <answer>.B +# answer: +# 0 = Already in guild. +# 1 = Offer rejected. +# 2 = Offer accepted. +# 3 = Guild full. +sub guild_invite_result { + my ($self, $args) = @_; + + my $type = $args->{type}; + + my %types = ( + 0 => T('Target is already in a guild.'), + 1 => T('Target has denied.'), + 2 => T('Target has accepted.'), + 3 => T('Your guild is full.') + ); + if ($types{$type}) { + message TF("Guild join request: %s\n", $types{$type}); + } else { + message TF("Guild join request: Unknown %s\n", $type); + } +} + +# Guild XY locators (ZC_NOTIFY_POSITION_TO_GUILDM) +# 01EB <account id>.L <x>.W <y>.W +sub guild_location { + my ($self, $args) = @_; + + foreach my $guildMember (@{$guild{member}}) { + # check if char is the online (we can have more then 1 char per account in our guild) + # why use accountID instead of charID? + if ($guildMember->{ID} eq $args->{ID} && $guildMember->{online}) { + last if ($args->{x} == 0 || $args->{y} == 0); + $guildMember->{pos}{x} = $args->{x}; + $guildMember->{pos}{y} = $args->{y}; + $guildMember->{pos_to}{x} = $args->{x}; + $guildMember->{pos_to}{y} = $args->{y}; + last; + } + } +} + +# Notifies clients of a guild of a leaving member (ZC_ACK_LEAVE_GUILD). +# 015A <char name>.24B <reason>.40B +# 0A83 +sub guild_leave { + my ($self, $args) = @_; + my ($name, $msg); + + if ($args->{name}) { + $name = bytesToString($args->{name}); + } elsif ($args->{charID}) { + foreach my $guildMember (@{$guild{member}}) { + if ($guildMember->{charID} eq $args->{charID}) { + $name = $guildMember->{name}; + binRemove(\@{$guild{member}}, $guildMember); + last; + } + } + } + + message TF("%s has left the guild.\n" . + "Reason: %s\n", $name, bytesToString($args->{message})), "guildchat"; +} + +# Notifies clients of a guild of an expelled member. +# 015C <char name>.24B <reason>.40B <account name>.24B (ZC_ACK_BAN_GUILD) +# 0839 <char name>.24B <reason>.40B (ZC_ACK_BAN_GUILD_SSO) +# 0A82 +sub guild_expulsion { + my ($self, $args) = @_; + my $name; + + if ($args->{name}) { + $name = bytesToString($args->{name}); + } elsif ($args->{charID}) { + foreach my $guildMember (@{$guild{member}}) { + if ($guildMember->{charID} eq $args->{charID}) { + $name = $guildMember->{name}; + binRemove(\@{$guild{member}}, $guildMember); + last; + } + } + } + + message TF("%s has been removed from the guild.\n" . + "Reason: %s\n", $name, bytesToString($args->{message})), "guildchat"; +} + +# Guild member login notice. +# 016D <account id>.L <char id>.L <status>.L (ZC_UPDATE_CHARSTAT) +# 01F2 <account id>.L <char id>.L <status>.L <gender>.W <hair style>.W <hair color>.W (ZC_UPDATE_CHARSTAT2) +# status: +# 0 = offline +# 1 = online +# TODO: we can update the following information from this package: sex, hair_style, hair_color +sub guild_member_online_status { + my ($self, $args) = @_; + + foreach my $guildMember (@{$guild{member}}) { + if ($guildMember->{charID} eq $args->{charID}) { + if ($guildMember->{online} = $args->{online}) { + message TF("Guild member %s logged in.\n", $guildMember->{name}), "guildchat"; + } else { + message TF("Guild member %s logged out.\n", $guildMember->{name}), "guildchat"; + } + last; + } + } +} + +# Notifies clients in a guild about updated member position assignments (ZC_ACK_REQ_CHANGE_MEMBERS). +# 0156 <packet len>.W { <account id>.L <char id>.L <position id>.L }* +sub guild_update_member_position { + my ($self, $args) = @_; + + my $guild_position_info = { + len => 12, + types => 'a4 a4 V', + keys => [qw(ID charID position)], + }; + + my $position_info; + for (my $i = 0; $i < length($args->{member_list}); $i += $guild_position_info->{len}) { + @{$position_info}{@{$guild_position_info->{keys}}} = unpack($guild_position_info->{types}, substr($args->{member_list}, $i, $guild_position_info->{len})); + foreach my $guildMember (@{$guild{member}}) { + if ($guildMember->{charID} eq $position_info->{charID}) { + message TF("Guild Member (%s) has the title changed from %s to %s\n",$guildMember->{name}, $guild{positions}[ $guildMember->{position} ]{title}, $guild{positions}[$position_info->{position}]{title}); + $guildMember->{position} = $position_info->{position}; + last; + } + } + } +} + +# Guild position name information (ZC_POSITION_ID_NAME_INFO). +# 0166 <packet len>.W { <position id>.L <position name>.24B }* +sub guild_members_title_list { + my ($self, $args) = @_; + + my $msg = $args->{RAW_MSG}; + my $msg_size = $args->{RAW_MSG_SIZE}; + + my $gtIndex; + for (my $i = 4; $i < $msg_size; $i+=28) { + $gtIndex = unpack('V', substr($msg, $i, 4)); + $guild{positions}[$gtIndex]{title} = bytesToString(unpack('Z24', substr($msg, $i + 4, 24))); + } +} + +# Notifies the client that it is belonging to a guild (ZC_UPDATE_GDID). +# 016C <guild id>.L <emblem id>.L <mode>.L <ismaster>.B <inter sid>.L <guild name>.24B +# mode: +# &0x01 = allow invite +# &0x10 = allow expel +sub guild_name { + my ($self, $args) = @_; + + my $guildID = $args->{guildID}; + my $emblemID = $args->{emblemID}; + my $mode = $args->{mode}; + my $guildName = bytesToString($args->{guildName}); + $char->{guild}{name} = $guildName; + $char->{guildID} = $guildID; + $char->{guild}{emblem} = $emblemID; + + debug "guild name: $guildName\n"; + + # emulate client behavior + if ($masterServer->{serverType} eq 'twRO') { + $messageSender->sendGuildRequestInfo(0); # Requests for Basic Information Guild, Hostile Alliance Information + $messageSender->sendGuildRequestInfo(3); + $messageSender->sendGuildRequestInfo(1); # Requests for Members list, list job title + } elsif ($masterServer->{serverType} eq 'jRO') { + $messageSender->sendGuildRequestInfo(1); # Requests for Members list, list job title + } else { + $messageSender->sendGuildMasterMemberCheck(); + $messageSender->sendGuildRequestInfo(4); # Requests for Expulsion list + $messageSender->sendGuildRequestInfo(0); # Requests for Basic Information Guild, Hostile Alliance Information + $messageSender->sendGuildRequestInfo(1); # Requests for Members list, list job title + $messageSender->sendGuildRequestEmblem($guildID); # Requests for Guild Emblem + # TODO: check if is necessary use PAGE 2 (title information list) + # $messageSender->sendGuildRequestInfo(2); # Requests for List job title, title information list [Guild Title System] + } +} + +# Guild invite (ZC_REQ_JOIN_GUILD). +# 016A <guild id>.L <guild name>.24B +sub guild_request { + my ($self, $args) = @_; + + # Guild request + my $ID = $args->{ID}; + my $name = bytesToString($args->{name}); + message TF("Incoming Request to join Guild '%s'\n", $name); + $incomingGuild{'ID'} = $ID; + $incomingGuild{'Type'} = 1; + $timeout{'ai_guildAutoDeny'}{'time'} = time; +} + +# Bitmask of enabled guild window tabs (ZC_ACK_GUILD_MENUINTERFACE). +# 014E <menu flag>.L +# menu flag: +# 0x00 = Basic Info (always on) +# &0x01 = Member manager +# &0x02 = Positions +# &0x04 = Skills +# &0x10 = Expulsion list +# &0x40 = Unknown (GMENUFLAG_ALLGUILDLIST) +# &0x80 = Notice +sub guild_master_member { + my ($self, $args) = @_; + if ($args->{type} == 0xd7) { + } elsif ($args->{type} == 0x57) { + message T("You are not a guildmaster.\n"), "info"; + return; + } else { + warning TF("Unknown results in %s (type: %s)\n", $self->{packet_list}{$args->{switch}}->[0], $args->{type}); + return; + } + message T("You are a guildmaster.\n"), "info"; +} + +# Notifies the client about the result of a alliance request (ZC_ACK_REQ_ALLY_GUILD). +# 0173 <answer>.B +# answer: +# 0 = Already allied. +# 1 = You rejected the offer. +# 2 = You accepted the offer. +# 3 = They have too any alliances. +# 4 = You have too many alliances. +# 5 = Alliances are disabled. +sub guild_alliance { + my ($self, $args) = @_; + if ($args->{flag} == 0) { + message T("Already allied.\n"), "info"; + } elsif ($args->{flag} == 1) { + message T("You rejected the offer.\n"), "info"; + } elsif ($args->{flag} == 2) { + message T("You accepted the offer.\n"), "info"; + } elsif ($args->{flag} == 3) { + message T("They have too any alliances\n"), "info"; + } elsif ($args->{flag} == 4) { + message T("You have too many alliances.\n"), "info"; + } else { + warning TF("Unknown results in %s (flag: %s)\n", $self->{packet_list}{$args->{switch}}->[0], $args->{flag}); + } +} + +# Guild position information (ZC_POSITION_INFO). +# 0160 <packet len>.W { <position id>.L <mode>.L <ranking>.L <pay rate>.L }* +# mode: +# &0x01 = allow invite +# &0x10 = allow expel +# ranking: +# TODO +sub guild_member_setting_list { + my ($self, $args) = @_; + my $msg = $args->{RAW_MSG}; + my $msg_size = $args->{RAW_MSG_SIZE}; + + for (my $i = 4; $i < $msg_size; $i += 16) { + my ($gtIndex, $invite_punish, $ranking, $freeEXP) = unpack('V4', substr($msg, $i, 16)); # TODO: use ranking + # TODO: isn't there a nyble unpack or something and is this even correct? + $guild{positions}[$gtIndex]{invite} = ($invite_punish & 0x01) ? 1 : ''; + $guild{positions}[$gtIndex]{punish} = ($invite_punish & 0x10) ? 1 : ''; + $guild{positions}[$gtIndex]{gstorage} = ($invite_punish & 0x100) ? 1 : ''; + $guild{positions}[$gtIndex]{feeEXP} = $freeEXP; + } +} + +# Sends guild skills (ZC_GUILD_SKILLINFO). +# 0162 <packet len>.W <skill points>.W { <skill id>.W <type>.L <level>.W <sp cost>.W <atk range>.W <skill name>.24B <upgradable>.B }* +# TODO: merge with skills_list? +sub guild_skills_list { + my ($self, $args) = @_; + my $msg = $args->{RAW_MSG}; + my $msg_size = $args->{RAW_MSG_SIZE}; + for (my $i = 6; $i < $args->{RAW_MSG_SIZE}; $i += 37) { + + my ($skillID, $targetType, $level, $sp, $range, $skillName, $up) = unpack('v V v3 Z24 C', substr($msg, $i, 37)); # TODO: use range + + $skillName = bytesToString($skillName); + $guild{skills}{$skillName}{ID} = $skillID; + $guild{skills}{$skillName}{sp} = $sp; + $guild{skills}{$skillName}{up} = $up; + $guild{skills}{$skillName}{targetType} = $targetType; + if (!$guild{skills}{$skillName}{lv}) { + $guild{skills}{$skillName}{lv} = $level; + } + } +} + +# Guild expulsion list (ZC_BAN_LIST). +# 0163 <packet len>.W { <char name>.24B <account name>.24B <reason>.40B }* +# 0163 <packet len>.W { <char name>.24B <reason>.40B }* (PACKETVER >= 20100803) +# Change 64 to 88 if needed +# 0B7C +sub guild_expulsion_list { + my ($self, $args) = @_; + + my $guild_expulsion_list; + if ($args->{switch} eq "0B7C") { + $guild_expulsion_list = { + len => 68, + types => 'a4 Z40 Z24', + keys => [qw(charID cause name)], + }; + } else { # 0163 + $guild_expulsion_list = { + len => 88, + types => 'Z24 Z24 Z40', + keys => [qw(name acc cause)], + }; + } + + delete $guild{expulsion}; + my $index = 0; + + for (my $i = 0; $i < length($args->{expulsion_list}); $i += $guild_expulsion_list->{len}) { + @{$guild{expulsion}[$index]}{@{$guild_expulsion_list->{keys}}} = unpack($guild_expulsion_list->{types}, substr($args->{expulsion_list}, $i, $guild_expulsion_list->{len})); + $guild{expulsion}[$index]{name} = bytesToString($guild{expulsion}[$index]{name}) if ($guild{expulsion}[$index]{name}); + $guild{expulsion}[$index]{cause} = bytesToString($guild{expulsion}[$index]{cause}) if ($guild{expulsion}[$index]{cause}); + $index++; + } +} + +# Notifies that a member changed the map +# 01EC <account id>.L <char id>.L <status>.L <map name>.16B +sub guild_member_map_change { + my ($self, $args) = @_; + + foreach my $guildMember (@{$guild{member}}) { + if ($guildMember->{charID} eq $args->{charID}) { + $guildMember->{pos} = {}; + $guildMember->{pos_to} = {}; + $guildMember->{map} = bytesToString($args->{mapName}); + debug sprintf("Guild Member: %s changed map to %s\n",$guildMember->{name}, $guildMember->{map}); + last; + } + } +} +# Notifies that a member was added in the guild +# 0182 <account>.L <char id>.L <hair style>.W <hair color>.W <gender>.W <class>.W <level>.W <contrib exp>.L <state>.L <position>.L <memo>.50B <name>.24B +# 0B7E +sub guild_member_add { + my ($self, $args) = @_; + + if ($guild{member}) { + my $index = scalar @{$guild{member}}; + foreach (@{$args->{KEYS}}) { + @{$guild{member}[$index]}{$_} = $args->{$_}; + } + $guild{member}[$index]{name} = bytesToString($guild{member}[$index]{name}) if ($guild{member}[$index]{name}); + } + + my $name = bytesToString($args->{name}); + message TF("Guild member added: %s\n",$name), "guildchat"; +} + +# Sends guild notice to client (ZC_GUILD_NOTICE). +# 016F <subject>.60B <notice>.120B +sub guild_notice { + my ($self, $args) = @_; + stripLanguageCode(\$args->{subject}); + stripLanguageCode(\$args->{notice}); + # don't show the huge guildmessage notice if there is none + # the client does something similar to this... + if ($args->{subject} || $args->{notice}) { + my $msg = TF("---Guild Notice---\n" . + "%s\n\n" . + "%s\n" . + "------------------\n", $args->{subject}, $args->{notice}); + message $msg, "guildnotice"; + } +} + +# Displays special effects (npcs, weather, etc) [Valaris] (ZC_NOTIFY_EFFECT2). +# 01F3 <id>.L <effect id>.L +sub misc_effect { + my ($self, $args) = @_; + + my $actor = Actor::get($args->{ID}); + message sprintf( + $actor->verb(T("%s use effect: %s\n"), T("%s uses effect: %s\n")), + $actor, defined $effectName{$args->{effect}} ? $effectName{$args->{effect}} : T("Unknown #")."$args->{effect}" + ), 'effect' +} + +# Plays/stops a wave sound (ZC_SOUND). +# 01d3 <file name>.24B <act>.B <term>.L <npc id>.L +# file name: +# relative to data\wav +# act: +# 0 = play (once) +# 1 = play (repeat, does not work) +# 2 = stops all sound instances of file name (does not work) +# term: +# unknown purpose, only relevant to act = 1 +# $args->{term} seems like duration or repeat count +sub sound_effect { + my ($self, $args) = @_; + + # continuous sound effects can be implemented as actor statuses + my $actor = exists $args->{ID} && Actor::get($args->{ID}); + message sprintf( + $actor + ? $args->{type} == 0 + ? $actor->verb(T("%2\$s play: %s\n"), T("%2\$s plays: %s\n")) + : $args->{type} == 1 + ? $actor->verb(T("%2\$s are now playing: %s\n"), T("%2\$s is now playing: %s\n")) + : $actor->verb(T("%2\$s stopped playing: %s\n"), T("%2\$s stopped playing: %s\n")) + : T("Now playing: %s\n"), + $args->{name}, $actor), 'effect' +} + +# Presents a list of items that can be identified (ZC_ITEMIDENTIFY_LIST). +# 0177 <packet len>.W { <name id>.W }* +sub identify_list { + my ($self, $args) = @_; + + my $msg = $args->{RAW_MSG}; + my $msg_size = $args->{RAW_MSG_SIZE}; + + undef @identifyID; + for (my $i = 4; $i < $msg_size; $i += 2) { + my $index = unpack("a2", substr($msg, $i, 2)); + my $item = $char->inventory->getByID($index); + binAdd(\@identifyID, $item->{binID}); + } + + my $num = @identifyID; + message TF("Received Possible Identify List (%s item(s)) - type 'identify'\n", $num), 'info'; +} + +# Notifies the client about the result of a item identify request (ZC_ACK_ITEMIDENTIFY). +# 0179 <index>.W <result>.B +sub identify { + my ($self, $args) = @_; + if ($args->{flag} == 0) { + my $item = $char->inventory->getByID($args->{ID}); + $item->{identified} = 1; + $item->{type_equip} = $itemSlots_lut{$item->{nameID}}; + message TF("Item Identified: %s (%d)\n", $item->{name}, $item->{binID}), "info"; + } else { + message T("Item Appraisal has failed.\n"); + } + undef @identifyID; +} + +# TODO: store this state +sub ignore_all_result { + my ($self, $args) = @_; + if ($args->{type} == 0) { + $ignored_all = 1; + message T("All Players ignored\n"); + } elsif ($args->{type} == 1) { + if ($args->{error} == 0) { + message T("All players unignored\n"); + } + } +} + +# TODO: store list of ignored players +sub ignore_player_result { + my ($self, $args) = @_; + if ($args->{type} == 0) { + message T("Player ignored\n"); + } elsif ($args->{type} == 1) { + if ($args->{error} == 0) { + message T("Player unignored\n"); + } + } +} + +sub item_used { + my ($self, $args) = @_; + + my ($index, $itemID, $ID, $remaining, $success) = + @{$args}{qw(ID itemID actorID remaining success)}; + my %hook_args = ( + serverIndex => $index, + itemID => $itemID, + userID => $ID, + remaining => $remaining, + success => $success + ); + + if ($ID eq $accountID) { + my $item = $char->inventory->getByID($index); + if ($item) { + if ($success == 1) { + my $amount = $item->{amount} - $remaining; + + message TF("You used Item: %s (%d) x %d - %d left\n", $item->{name}, $item->{binID}, + $amount, $remaining), "useItem", 1; + + inventoryItemRemoved($item->{binID}, $amount); + + $hook_args{item} = $item; + $hook_args{binID} = $item->{binID}; + $hook_args{name} => $item->{name}; + $hook_args{amount} = $amount; + + } else { + message TF("You failed to use item: %s (%d)\n", $item ? $item->{name} : "#$itemID", $remaining), "useItem", 1; + } + } else { + if ($success == 1) { + message TF("You used unknown item #%d - %d left\n", $itemID, $remaining), "useItem", 1; + } else { + message TF("You failed to use unknown item #%d - %d left\n", $itemID, $remaining), "useItem", 1; + } + } + } else { + my $actor = Actor::get($ID); + my $itemDisplay = itemNameSimple($itemID); + message TF("%s used Item: %s - %s left\n", $actor, $itemDisplay, $remaining), "useItem", 2; + } + Plugins::callHook('packet_useitem', \%hook_args); +} + +sub married { + my ($self, $args) = @_; + + my $actor = Actor::get($args->{ID}); + message TF("%s got married!\n", $actor); +} + +# Makes an item appear on the ground. +# 009E <id>.L <name id>.W <identified>.B <x>.W <y>.W <subX>.B <subY>.B <amount>.W (ZC_ITEM_FALL_ENTRY) +# 084B <id>.L <name id>.W <type>.W <identified>.B <x>.W <y>.W <subX>.B <subY>.B <amount>.W (ZC_ITEM_FALL_ENTRY4) +# 0ADD <id>.L <name id>.W <type>.W <identified>.B <x>.W <y>.W <subX>.B <subY>.B <amount>.W <show drop effect>.B <drop effect mode>.W (ZC_ITEM_FALL_ENTRY5) +sub item_appeared { + my ($self, $args) = @_; + return unless changeToInGameState(); + + my $item = $itemsList->getByID($args->{ID}); + my $mustAdd; + if (!$item) { + $item = new Actor::Item(); + $item->{appear_time} = time; + $item->{amount} = $args->{amount}; + $item->{nameID} = $args->{nameID}; + $item->{identified} = $args->{identified}; + $item->{name} = itemName($item); + $item->{ID} = $args->{ID}; + $mustAdd = 1; + } + $item->{pos}{x} = $args->{x}; + $item->{pos}{y} = $args->{y}; + $item->{pos_to}{x} = $args->{x}; + $item->{pos_to}{y} = $args->{y}; + $itemsList->add($item) if ($mustAdd); + + # Take item as fast as possible + if (AI::state == AI::AUTO && pickupitems($item->{name}, $item->{nameID}) == 2 + && ($config{'itemsTakeAuto'} || $config{'itemsGatherAuto'}) + && (!$config{itemsGatherAuto_notInTown} || !$field->isCity) + && (percent_weight($char) < $config{'itemsMaxWeight'}) + && distance($item->{pos}, $char->{pos_to}) <= 5) { + $messageSender->sendTake($args->{ID}); + } + + message TF("Item Appeared: %s (%d) x %d (%d, %d)\n", $item->{name}, $item->{binID}, $item->{amount}, $args->{x}, $args->{y}), "drop", 1; + + Plugins::callHook('item_appeared', { + item => $item, + type => $args->{type} + }); +} + +sub item_exists { + my ($self, $args) = @_; + return unless changeToInGameState(); + + my $item = $itemsList->getByID($args->{ID}); + my $mustAdd; + if (!$item) { + $item = new Actor::Item(); + $item->{appear_time} = time; + $item->{amount} = $args->{amount}; + $item->{nameID} = $args->{nameID}; + $item->{identified} = $args->{identified}; + $item->{name} = itemName($item); + $item->{ID} = $args->{ID}; + $mustAdd = 1; + } + $item->{pos}{x} = $args->{x}; + $item->{pos}{y} = $args->{y}; + $item->{pos_to}{x} = $args->{x}; + $item->{pos_to}{y} = $args->{y}; + $itemsList->add($item) if ($mustAdd); + + message TF("Item Exists: %s (%d) x %d\n", $item->{name}, $item->{binID}, $item->{amount}), "drop", 1; + + Plugins::callHook('item_exists', { + item => $item, + type => $args->{type}, + show_effect => $args->{show_effect}, + effect_type => $args->{effect_type} + }); +} + +# Makes an item disappear from the ground. +# 00A1 <id>.L (ZC_ITEM_DISAPPEAR) +sub item_disappeared { + my ( $self, $args ) = @_; + return unless changeToInGameState(); + + my $item = $itemsList->getByID( $args->{ID} ); + if ( $item ) { + if ( $config{attackLooters} && AI::action ne "sitAuto" && pickupitems( $item->{name}, $item->{nameID} ) > 0 ) { + for my Actor::Monster $monster ( @$monstersList ) { # attack looter code + if ( my $control = mon_control( $monster->name, $monster->{nameID} ) ) { + next + if ( ( $control->{attack_auto} ne "" && $control->{attack_auto} == -1 ) + || ( $control->{attack_lvl} ne "" && $control->{attack_lvl} > $char->{lv} ) + || ( $control->{attack_jlvl} ne "" && $control->{attack_jlvl} > $char->{lv_job} ) + || ( $control->{attack_hp} ne "" && $control->{attack_hp} > $char->{hp} ) + || ( $control->{attack_sp} ne "" && $control->{attack_sp} > $char->{sp} ) ); + } + if ( distance( $item->{pos}, $monster->{pos} ) <= ( $config{attackLooters_dist} || 0 ) ) { + my %plugin_args; + $plugin_args{monster} = $monster; + $plugin_args{item} = $item; + $plugin_args{return} = 0; + Plugins::callHook( 'check_attackLooter' => \%plugin_args ); + unless ( $plugin_args{return} ) { + message TF( "Looter: %s looted %s - Adding it to looters list to be attacked\n", + $monster->nameIdx, $item->{name} ), + "looter"; + $monster->{attackLooters} = 1; + last; + } + } + } + } + + debug "Item Disappeared: $item->{name} ($item->{binID})\n", "parseMsg_presence"; + my $ID = $args->{ID}; + $items_old{$ID} = $item->deepCopy(); + $items_old{$ID}{disappeared} = 1; + $items_old{$ID}{gone_time} = time; + $itemsList->removeByID( $ID ); + } +} + +sub item_upgrade { + my ($self, $args) = @_; + my ($type, $index, $upgrade) = @{$args}{qw(type ID upgrade)}; + + my $item = $char->inventory->getByID($index); + if ($item) { + $item->{upgrade} = $upgrade; + message TF("Item %s has been upgraded to +%s\n", $item->{name}, $upgrade), "parseMsg/upgrade"; + $item->setName(itemName($item)); + } +} + +# Leap, Snap, Back Slide... Various knockback +sub high_jump { + my ($self, $args) = @_; + return unless changeToInGameState(); + + my $actor = Actor::get ($args->{ID}); + if (!defined $actor) { + $actor = new Actor::Unknown; + $actor->{appear_time} = time; + $actor->{nameID} = unpack ('V', $args->{ID}); + } elsif ($actor->{pos_to}{x} == $args->{x} && $actor->{pos_to}{y} == $args->{y}) { + message TF("%s failed to instantly move\n", $actor->nameString), 'skill'; + return; + } + + $actor->{pos} = {x => $args->{x}, y => $args->{y}}; + $actor->{pos_to} = {x => $args->{x}, y => $args->{y}}; + + message TF("%s instantly moved to %d, %d\n", $actor->nameString, $actor->{pos_to}{x}, $actor->{pos_to}{y}), 'skill', 2; + + $actor->{time_move} = time; + $actor->{time_move_calc} = 0; +} + +sub hp_sp_changed { + my ($self, $args) = @_; + return unless changeToInGameState(); + + my $type = $args->{type}; + my $amount = $args->{amount}; + if ($type == 5) { + $char->{hp} += $amount; + $char->{hp} = $char->{hp_max} if ($char->{hp} > $char->{hp_max}); + } elsif ($type == 7) { + $char->{sp} += $amount; + $char->{sp} = $char->{sp_max} if ($char->{sp} > $char->{sp_max}); + } +} + +# Notifies the client of a position change to coordinates on given map (ZC_NPCACK_MAPMOVE). +# 0091 <map name>.16B <x>.W <y>.W +# Notifies the client of a position change (on air ship) to coordinates on given map (ZC_AIRSHIP_MAPMOVE). +# 0A4B <map name>.16B <x>.W <y>.W +# The difference between map_change and map_changed is that map_change +# represents a map change event on the current map server, while +# map_changed means that you've changed to a different map server. +# map_change also represents teleport events. +sub map_change { + my ($self, $args) = @_; + return unless changeToInGameState(); + + $messageSender->sendStopSkillUse($char->{last_continuous_skill_used}) if $char->{last_skill_used_is_continuous}; + + my $oldMap = $field ? $field->baseName : undef; # Get old Map name without InstanceID + my ($map) = $args->{map} =~ /([\s\S]*)\./; + my $map_noinstance; + ($map_noinstance, undef) = Field::nameToBaseName(undef, $map); # Hack to clean up InstanceID + + checkAllowedMap($map_noinstance); + if (!$field || $map ne $field->name()) { + eval { + $field = new Field(name => $map); + }; + if (my $e = caught('FileNotFoundException', 'IOException')) { + error TF("Cannot load field %s: %s\n", $map_noinstance, $e); + undef $field; + } elsif ($@) { + die $@; + } + } + + if ($ai_v{temp}{clear_aiQueue}) { + AI::clear; + AI::SlaveManager::clear(); + } + + main::initMapChangeVars(); + %minimap_indicator_seen = (); + for (my $i = 0; $i < @ai_seq; $i++) { + ai_setMapChanged($i); + } + AI::SlaveManager::setMapChanged (); + $ai_v{portalTrace_mapChanged} = time; + + my %coords = ( + x => $args->{x}, + y => $args->{y} + ); + $char->{pos} = {%coords}; + $char->{pos_to} = {%coords}; + $char->{time_move} = 0; + $char->{time_move_calc} = 0; + $char->{solution} = []; + push(@{$char->{solution}}, { x => $char->{pos}{x}, y => $char->{pos}{y} }); + message TF("Map Change: %s (%s, %s)\n", $args->{map}, $char->{pos}{x}, $char->{pos}{y}), "connection"; + if ($net->version == 1) { + ai_clientSuspend(0, $timeout{'ai_clientSuspend'}{'timeout'}); + } else { + $messageSender->sendMapLoaded(); + # $messageSender->sendSync(1); + + # request to unfreeze char alisonrag + $messageSender->sendBlockingPlayerCancel() if $masterServer->{blockingPlayerCancel} || $self->{blockingPlayerCancel}; + + $timeout{ai}{time} = time; + } + + Plugins::callHook('Network::Receive::map_changed', { + oldMap => $oldMap, + }); + $timeout{ai}{time} = time; +} + +# Notifies the client of a position change to coordinates on given map, which is on another map-server. +# 0092 <map name>.16B <x>.W <y>.W <ip>.L <port>.W (ZC_NPCACK_SERVERMOVE) +# 0AC7 <map name>.16B <x>.W <y>.W <ip>.L <port>.W <dns host>.128B (ZC_NPCACK_SERVERMOVE2) +sub map_changed { + my ($self, $args) = @_; + $net->setState(4); + + my $oldMap = $field ? $field->baseName : undef; # Get old Map name without InstanceID + my ($map) = $args->{map} =~ /([\s\S]*)\./; + my $map_noinstance; + ($map_noinstance, undef) = Field::nameToBaseName(undef, $map); # Hack to clean up InstanceID + + checkAllowedMap($map_noinstance); + if (!$field || $map ne $field->name()) { + eval { + $field = new Field(name => $map); + }; + if (my $e = caught('FileNotFoundException', 'IOException')) { + error TF("Cannot load field %s: %s\n", $map_noinstance, $e); + undef $field; + } elsif ($@) { + die $@; + } + } + + my %coords = ( + x => $args->{x}, + y => $args->{y} + ); + $char->{pos} = {%coords}; + $char->{pos_to} = {%coords}; + $char->{time_move} = 0; + $char->{time_move_calc} = 0; + $char->{solution} = []; + push(@{$char->{solution}}, { x => $char->{pos}{x}, y => $char->{pos}{y} }); + + undef $conState_tries; + main::initMapChangeVars(); + %minimap_indicator_seen = (); + for (my $i = 0; $i < @ai_seq; $i++) { + ai_setMapChanged($i); + } + AI::SlaveManager::setMapChanged (); + $ai_v{portalTrace_mapChanged} = time; + + if ($args->{'url'} =~ /.*\:\d+/) { + $map_ip = $args->{url}; + $map_ip =~ s/:[0-9\0]+//; + $map_port = $args->{port}; + } else { + $map_ip = makeIP($args->{IP}); + $map_port = $args->{port}; + } + + message(swrite( + "---------Map Info----------", [], + "MAP Name: @<<<<<<<<<<<<<<<<<<", + [$args->{map}], + "MAP IP: @<<<<<<<<<<<<<<<<<<", + [$map_ip], + "MAP Port: @<<<<<<<<<<<<<<<<<<", + [$map_port], + "-------------------------------", []), + "connection"); + + message T("Closing connection to Map Server\n"), "connection"; + $net->serverDisconnect unless ($net->version == 1); + + # Reset item and skill times. The effect of items (like aspd potions) + # and skills (like Twohand Quicken) disappears when we change map server. + # NOTE: with the newer servers, this isn't true anymore + my $i = 0; + while (exists $config{"useSelf_item_$i"}) { + if (!$config{"useSelf_item_$i"}) { + $i++; + next; + } + + $ai_v{"useSelf_item_$i"."_time"} = 0; + $i++; + } + $i = 0; + while (exists $config{"useSelf_skill_$i"}) { + if (!$config{"useSelf_skill_$i"}) { + $i++; + next; + } + + $ai_v{"useSelf_skill_$i"."_time"} = 0; + $i++; + } + $i = 0; + while (exists $config{"doCommand_$i"}) { + if (!$config{"doCommand_$i"}) { + $i++; + next; + } + + $ai_v{"doCommand_$i"."_time"} = 0; + $i++; + } + if ($char) { + delete $char->{statuses}; + $char->{spirits} = 0; + delete $char->{permitSkill}; + delete $char->{encoreSkill}; + } + undef %guild; + if ( $char->cartActive ) { + $char->cart->close; + $char->cart->clear; + } + + Plugins::callHook('Network::Receive::map_changed', { + oldMap => $oldMap, + }); + $timeout{ai}{time} = time; +} + +# Parse 0A3B with structure +# '0A3B' => ['hat_effect', 'v a4 C a*', [qw(len ID flag effect)]], +# Unpack effect info into HatEFID +# @author [Cydh] +sub parse_hat_effect { + my ($self, $args) = @_; + @{$args->{effects}} = map {{ HatEFID => unpack('v', $_) }} unpack '(a2)*', $args->{effect}; + debug "Hat Effect. Flag: ".$args->{flag}." HatEFIDs: ".(join ', ', map {$_->{HatEFID}} @{$args->{effects}})."\n"; +} + +# Display information for player's Hat Effects +# @author [Cydh] +sub hat_effect { + my ($self, $args) = @_; + + my $actor = Actor::get($args->{ID}); + my $hatName; + my $i = 0; + + #TODO: Stores the hat effect into actor for single player's information + for my $hat (@{$args->{effects}}) { + my $hatHandle; + $hatName .= ", " if ($i); + if (defined $hatEffectHandle{$hat->{HatEFID}}) { + $hatHandle = $hatEffectHandle{$hat->{HatEFID}}; + $hatName .= defined $hatEffectName{$hatHandle} ? $hatEffectName{$hatHandle} : $hatHandle; + } else { + $hatName .= T("Unknown #").$hat->{HatEFID}; + } + $i++; + } + + if ($args->{flag} == 1) { + message sprintf( + $actor->verb(T("%s use effect: %s\n"), T("%s uses effect: %s\n")), + $actor, $hatName + ), 'effect'; + } else { + message sprintf( + $actor->verb(T("%s are no longer: %s\n"), T("%s is no longer: %s\n")), + $actor, $hatName + ), 'effect'; + } +} + +# Displays an NPC dialog message (ZC_SAY_DIALOG). +# 00B4 <packet len>.W <npc id>.L <message>.?B +sub npc_talk { + my ($self, $args) = @_; + + my $ID = $args->{ID}; + my $nameID = unpack 'V', $ID; + autoNpcTalk($ID, $nameID); + + $talk{ID} = $args->{ID}; + $talk{nameID} = unpack 'V', $args->{ID}; + my $msg = bytesToString ($args->{msg}); + + # Remove RO color codes + $talk{msg} =~ s/\^[a-fA-F0-9]{6}//g; + $msg =~ s/\^[a-fA-F0-9]{6}//g; + + # Prepend existing conversation. + $talk{msg} .= "\n" if $talk{msg}; + $talk{msg} .= $msg; + + $ai_v{'npc_talk'}{'ID'} = $talk{ID}; + $ai_v{'npc_talk'}{talk} = 'initiated'; + $ai_v{'npc_talk'}{time} = time; + + my $name = getNPCName($talk{ID}); + Plugins::callHook('npc_talk', { + ID => $talk{ID}, + nameID => $talk{nameID}, + name => $name, + msg => $talk{msg}, + }); + message "$name: $msg\n", "npc"; +} + +# Adds a 'close' button to an NPC dialog (ZC_CLOSE_DIALOG). +# 00B6 <npc id>.L +sub npc_talk_close { + my ($self, $args) = @_; + # 00b6: long ID + + if (!exists $ai_v{'npc_talk'} || !defined $ai_v{'npc_talk'} || !exists $ai_v{'npc_talk'}{'ID'} || !defined $ai_v{'npc_talk'}{'ID'}) { + debug "We received an strange 'npc_talk_done', just ignoring it\n", "npc"; + return; + } + + return if (exists $ai_v{'npc_talk'}{'talk'} && $ai_v{'npc_talk'}{'talk'} eq 'buy_or_sell'); + + my $ID = $args->{ID}; + my $name = getNPCName($ID); + + $ai_v{'npc_talk'}{'ID'} = $talk{ID}; + $ai_v{'npc_talk'}{'talk'} = 'close'; + $ai_v{'npc_talk'}{'time'} = time; + undef %talk; + + Plugins::callHook('npc_talk_done', {ID => $ID}); +} + +# Adds a 'next' button to an NPC dialog (ZC_WAIT_DIALOG). +# 00B5 <npc id>.L +sub npc_talk_continue { + my ($self, $args) = @_; + my $ID = substr($args->{RAW_MSG}, 2, 4); + my $name = getNPCName($ID); + + $ai_v{'npc_talk'}{'ID'} = $ID; + $ai_v{'npc_talk'}{'talk'} = 'next'; + $ai_v{'npc_talk'}{'time'} = time; +} + +# Displays an NPC dialog input box for numbers (ZC_OPEN_EDITDLG). +# 0142 <npc id>.L +sub npc_talk_number { + my ($self, $args) = @_; + + my $ID = $args->{ID}; + + my $name = getNPCName($ID); + $ai_v{'npc_talk'}{'ID'} = $ID; + $ai_v{'npc_talk'}{'talk'} = 'number'; + $ai_v{'npc_talk'}{'time'} = time; +} + +# Displays an NPC dialog menu (ZC_MENU_LIST). +# 00B7 <packet len>.W <npc id>.L <menu items>.?B +sub npc_talk_responses { + my ($self, $args) = @_; + + # 00b7: word len, long ID, string str + # A list of selections appeared on the NPC message dialog. + # Each item is divided with ':' + my $msg = $args->{RAW_MSG}; + + my $ID = substr($msg, 4, 4); + my $nameID = unpack 'V', $ID; + autoNpcTalk($ID, $nameID); + + $talk{ID} = $ID; + $talk{nameID} = $nameID; + my $talk = unpack("Z*", substr($msg, 8)); + $talk = substr($msg, 8) if (!defined $talk); + $talk = bytesToString($talk); + + my @preTalkResponses = split /:/, $talk; + + Plugins::callHook('pre/npc_talk_responses', { + responses => \@preTalkResponses, + }); + + $talk{responses} = []; + foreach my $response (@preTalkResponses) { + # Remove RO color codes + $response =~ s/\^[a-fA-F0-9]{6}//g; + if ($response =~ /^\^nItemID\^(\d+)$/) { + $response = itemNameSimple($1); + } + + push @{$talk{responses}}, $response if ($response ne ""); + } + + $talk{responses}[@{$talk{responses}}] = T("Cancel Chat"); + + $ai_v{'npc_talk'}{'ID'} = $talk{ID}; + $ai_v{'npc_talk'}{'talk'} = 'select'; + $ai_v{'npc_talk'}{'time'} = time; + + Commands::run('talk resp'); + + my $name = getNPCName($ID); + Plugins::callHook('npc_talk_responses', { + ID => $ID, + name => $name, + responses => $talk{responses}, + }); +} + +# Displays an NPC dialog input box for numbers (ZC_OPEN_EDITDLGSTR). +# 01D4 <npc id>.L +sub npc_talk_text { + my ($self, $args) = @_; + + my $ID = $args->{ID}; + + my $name = getNPCName($ID); + $ai_v{'npc_talk'}{'ID'} = $ID; + $ai_v{'npc_talk'}{'talk'} = 'text'; + $ai_v{'npc_talk'}{'time'} = time; +} + +# Displays the buy/sell dialog of an NPC shop (ZC_SELECT_DEALTYPE). +# 00C4 <shop id>.L +sub npc_store_begin { + my ($self, $args) = @_; + undef %talk; + $talk{ID} = $args->{ID}; + $ai_v{'npc_talk'}{'ID'} = $talk{ID}; + $ai_v{'npc_talk'}{'talk'} = 'buy_or_sell'; + $ai_v{'npc_talk'}{'time'} = time; + + $storeList->{npcName} = getNPCName($args->{ID}) || T('Unknown'); +} + +# Presents list of items, that can be bought in an NPC shop (ZC_PC_PURCHASE_ITEMLIST). +# 00C6 <packet len>.W { <price>.L <discount price>.L <item type>.B <name id>.W }* +# 00C6 <packet len>.W { <price>.L <discount price>.L <item type>.B <name id>.L }* +# 0B77 <packet len>.W { <name id>.L <price>.L <discount price>.L <item type>.B <viewSprite>.W <location>.L}* +# 2 versions of same packet. $self->{npc_store_info_pack} (ZC_PC_PURCHASE_ITEMLIST_sub) should be changed in own serverType file if needed +sub npc_store_info { + my ($self, $args) = @_; + my $msg = $args->{RAW_MSG}; + my $pack; + my $keys; + + if ($args->{switch} eq '0B77') { + $pack = "V3 C v V"; + $keys = [qw( nameID price _ type sprite_id location )]; + } else { + $pack = $self->{npc_store_info_pack} || 'V V C v'; + $keys = [qw( price _ type nameID )]; + } + + my $len = length pack $pack; + $storeList->clear; + undef %talk; + for (my $i = 4; $i < $args->{RAW_MSG_SIZE}; $i += $len) { + my $item = Actor::Item->new; + @$item{@{$keys}} = unpack $pack, substr $msg, $i, $len; + + # Workaround some npcs that have items appearing more than once in their store list, + # for example the Trader at moc_ruins 90 149 sells only bananas, but 6 times + # + # Usually, $Actor::Item->{ID} is equal to $Actor::Item->{nameID} - that WILL crash + # kore in the event described above + # + # This workaround causes $Actor::Item->{ID} to be equal to $Actor::Item->{binID} and, + # therefore, never overlap + # - lututui & alisonrag - Sep, 2018 + $item->{ID} = $storeList->size; + + $item->{name} = itemName($item); + $storeList->add($item); + + debug "Item added to Store: $item->{name} - $item->{price}z\n", "parseMsg", 2; + } + + $ai_v{'npc_talk'}{talk} = 'store'; + # continue talk sequence now + $ai_v{'npc_talk'}{'time'} = time; + + if (AI::action ne 'buyAuto') { + Commands::run('store'); + } +} + +# Presents list of items, that can be sold to an NPC shop (ZC_PC_SELL_ITEMLIST). +# 00C7 <packet len>.W { <index>.W <price>.L <overcharge price>.L }* +sub npc_sell_list { + my ($self, $args) = @_; + #sell list, similar to buy list + if (length($args->{RAW_MSG}) > 4) { + my $msg = $args->{RAW_MSG}; + } + + debug T("You can sell:\n"), "info"; + for (my $i = 0; $i < length($args->{itemsdata}); $i += 10) { + my ($index, $price, $price_overcharge) = unpack("a2 L L", substr($args->{itemsdata},$i,($i + 10))); + my $item = $char->inventory->getByID($index); + $item->{sellable} = 1; # flag this item as sellable + debug TF("%s x %s for %sz each. \n", $item->{amount}, $item->{name}, $price_overcharge), "info"; + } + + foreach my $item (@{$char->inventory->getItems()}) { + next if ($item->{equipped} || $item->{sellable}); + $item->{unsellable} = 1; # flag this item as unsellable + } + + undef %talk; + message T("Ready to start selling items\n"); + + $ai_v{'npc_talk'}{talk} = 'sell'; + # continue talk sequence now + $ai_v{'npc_talk'}{'time'} = time; +} + +sub npc_clear_dialog { + my ($self, $args) = @_; + my $ID = $args->{ID}; + debug "The dialogue with the NPC " .getHex($ID) ." was closed.\n", "parseMsg"; +} + +# Notification about the result of a purchase attempt from an NPC shop (ZC_PC_PURCHASE_RESULT). +# 00CA <result>.B +# result: +# 0 = "The deal has successfully completed." +# 1 = "You do not have enough zeny." +# 2 = "You are over your Weight Limit." +# 3 = "Out of the maximum capacity, you have too many items." +# 4 = "Item does not exist in store" +# 5 = "Item cannot be exchanged" +# 6 = "Invalid store" +sub buy_result { + my ($self, $args) = @_; + if ($args->{fail} == 0) { + message T("Buy completed.\n"), "success"; + } elsif ($args->{fail} == 1) { + error T("Buy failed (insufficient zeny).\n"); + } elsif ($args->{fail} == 2) { + error T("Buy failed (insufficient weight capacity).\n"); + } elsif ($args->{fail} == 3) { + error T("Buy failed (too many different inventory items).\n"); + } elsif ($args->{fail} == 4) { + error T("Buy failed (item does not exist in store).\n"); + } elsif ($args->{fail} == 5) { + error T("Buy failed (item cannot be exchanged).\n"); + } elsif ($args->{fail} == 6) { + error T("Buy failed (invalid store).\n"); + } else { + error TF("Buy failed (failure code %s).\n", $args->{fail}); + } + if (AI::is("buyAuto")) { + AI::args->{recv_buy_packet} = 1; + } + Plugins::callHook('buy_result', {fail => $args->{fail}}); +} + +# Presents list of items, that can be bought in an NPC MARKET shop (PACKET_ZC_NPC_MARKET_OPEN). +# 09D5 <packet len>.W { <name id>.W <type>.B <price>.L <amount>.L <weight>.W }* +# 09D5 <packet len>.W { <name id>.L <type>.B <price>.L <amount>.L <weight>.W }* +# 2 versions of same packet. $self->{npc_market_info_pack} (PACKET_ZC_NPC_MARKET_OPEN_sub) should be changed in own serverType file if needed +sub npc_market_info { + my ($self, $args) = @_; + my $pack = $self->{npc_market_info_pack} || 'v C V2 v'; + my $len = length pack $pack; + + $storeList->clear; + undef %talk; + + for (my $i = 0; $i < length($args->{itemList}); $i += $len) { + my $item = Actor::Item->new; + @$item{qw( nameID type price amount weight )} = unpack $pack, substr $args->{itemList}, $i, $len; + next if (!$item->{amount}); # Client behavior (dont show the item in market window) + # Workaround some npcs that have items appearing more than once in their store list, + # for example the Trader at moc_ruins 90 149 sells only bananas, but 6 times + # + # Usually, $Actor::Item->{ID} is equal to $Actor::Item->{nameID} - that WILL crash + # kore in the event described above + # + # This workaround causes $Actor::Item->{ID} to be equal to $Actor::Item->{binID} and, + # therefore, never overlap + # - lututui & alisonrag - Sep, 2018 + $item->{ID} = $storeList->size; + + $item->{name} = itemName($item); + + $storeList->add($item); + + debug "Item added to Store: $item->{name} - $item->{price}z\n", "parseMsg", 2; + } + + return if !$storeList->size; + + if (AI::action ne 'buyAuto') { + Commands::run('store'); + } + + $in_market = 1; + + # continue talk sequence now + $ai_v{'npc_talk'}{'talk'} = 'store'; + $ai_v{'npc_talk'}{'time'} = time; +} + +# Show the purchase result update the list of items, that can be bought in an NPC MARKET shop (PACKET_ZC_NPC_MARKET_OPEN). +# 09D7 <packet len>.W <result>.B { <name id>.W <type>.B <price>.L <amount>.L <weight>.W }* +# 09D7 <packet len>.W <result>.B { <name id>.L <type>.B <price>.L <amount>.L <weight>.W }* +# result: +# -1 = error +# 0 = sucess +# 1 = no zeny +# 2 = you are overweight +# 3 = you dont have space in inventory +# 4 = amount too big +sub npc_market_purchase_result { + my ($self, $args) = @_; + + debug "Npc market purchase result: " .$args->{result}. "\n", "parseMsg", 2; + if ( $args->{result} == MARKET_BUY_RESULT_ERROR) { + error T("Error while trying to buy in a Market Store.\n"), "info"; + } elsif ( $args->{result} == MARKET_BUY_RESULT_SUCCESS) { + message T("Item buyed Successfully.\n"), "info"; + } elsif ( $args->{result} == MARKET_BUY_RESULT_NO_ZENY) { + error T("Error Market Store (You don't have the necessary zeny).\n"), "info"; + } elsif ( $args->{result} == MARKET_BUY_RESULT_OVER_WEIGHT) { + error T("Error Market Store (You are Overweight).\n"), "info"; + } elsif ( $args->{result} == MARKET_BUY_RESULT_OUT_OF_SPACE) { + error T("Error Market Store (You dont have space in inventory).\n"), "info"; + } elsif ( $args->{result} == MARKET_BUY_RESULT_AMOUNT_TOO_BIG) { + error T("Error Market Store (You tried to buy a amount higher then NPC is selling).\n"), "info"; + } else { + error TF("Error while trying to buy in a Market Store (Unknown). (%s)\n", $args->{result}), "info"; + } + + if (AI::is("buyAuto")) { + AI::args->{recv_buy_packet} = 1; + } + + my $pack = $self->{npc_market_info_pack} || 'v C V2 v'; + my $len = length pack $pack; + + $storeList->clear; + undef %talk; + + for (my $i = 0; $i < length($args->{itemList}); $i += $len) { + my $item = Actor::Item->new; + @$item{qw( nameID type price amount weight )} = unpack $pack, substr $args->{itemList}, $i, $len; + next if (!$item->{amount}); # Client behavior (dont show the item in market window) + # Workaround some npcs that have items appearing more than once in their store list, + # for example the Trader at moc_ruins 90 149 sells only bananas, but 6 times + # + # Usually, $Actor::Item->{ID} is equal to $Actor::Item->{nameID} - that WILL crash + # kore in the event described above + # + # This workaround causes $Actor::Item->{ID} to be equal to $Actor::Item->{binID} and, + # therefore, never overlap + # - lututui & alisonrag - Sep, 2018 + $item->{ID} = $storeList->size; + + $item->{name} = itemName($item); + + $storeList->add($item); + + debug "Item added to Store: $item->{name} - $item->{price}z\n", "parseMsg", 2; + } + + return if !$storeList->size; + + if (AI::action ne 'buyAuto') { + Commands::run('store'); + } + + $in_market = 1; + + # continue talk sequence now + $ai_v{'npc_talk'}{'talk'} = 'store'; + $ai_v{'npc_talk'}{'time'} = time; +} + +sub deal_add_you { + my ($self, $args) = @_; + + if ($args->{fail} == 1) { + error T("That person is overweight; you cannot trade.\n"), "deal"; + return; + } elsif ($args->{fail} == 2) { + error T("This item cannot be traded.\n"), "deal"; + return; + } elsif ($args->{fail} == 192) { + debug "Unknown status (success).\n", "deal"; + } elsif ($args->{fail}) { + error TF("You cannot trade (fail code %s).\n", $args->{fail}), "deal"; + return; + } + + my $id = unpack('v',$args->{ID}); + + if ($id == 0) { + message "You added Zeny to Deal (suposedly $currentDeal{'you_zeny'} z).\n"; + return; + } + + return unless ($id > 0); + + my $item = $char->inventory->getByID($args->{ID}); + $args->{item} = $item; + # FIXME: quickly add two items => lastItemAmount is lost => inventory corruption; see also Misc::dealAddItem + # FIXME: what will be in case of two items with the same nameID? + # TODO: no info about items is stored + $currentDeal{you_items}++; + $currentDeal{you}{$item->{nameID}}{amount} += $currentDeal{lastItemAmount}; + $currentDeal{you}{$item->{nameID}}{nameID} = $item->{nameID}; + message TF("You added Item to Deal: %s x %s\n", $item->{name}, $currentDeal{lastItemAmount}), "deal"; + inventoryItemRemoved($item->{binID}, $currentDeal{lastItemAmount}); + Plugins::callHook('deal_you_added', { + id => $id, + item => $item + }); +} + +sub skill_exchange_item { + my ($self, $args) = @_; + if ($args->{type} == 0) { + message T("Change Material is ready. Use command 'cm' to continue.\n"), "info"; + } else { + message T("Four Spirit Analysis is ready. Use command 'analysis' to continue.\n"), "info"; + } + ## + # $args->{type} : Type + # 0: Change Material -> 1 + # 1: Elemental Analysis Lv 1 -> 2 + # 2: Elemental Analysis Lv 2 -> 3 + # This value will be added +1 for simple check later + # $args->{val} : ???? + ## + $skillExchangeItem = $args->{type} + 1; +} + +# Allowed to RefineUI by server +# '0AA0' => ['refineui_opened', '' ,[qw()]], +# @author [Cydh] +sub refineui_opened { + my ($self, $args) = @_; + message TF("RefineUI is opened. Type 'i' to check equipment and its index. To continue: refineui select [ItemIdx]\n"), "info"; + $refineUI->{open} = 1; +} + +# Received refine info for selected item +# '0AA2' => ['refineui_info', 'v v C a*' ,[qw(index bless materials)]], +# @param args Packet data +# @author [Cydh] +sub refineui_info { + my ($self, $args) = @_; + + if ($args->{len} > 7) { + $refineUI->{itemIndex} = $args->{index}; + $refineUI->{bless} = $args->{bless}; + + my $item = $char->inventory->[$refineUI->{invIndex}]; + my $bless = $char->inventory->getByNameID($Blacksmith_Blessing); + + message T("========= RefineUI Info =========\n"), "info"; + message TF("Target Equip:\n". + "- Index: %d\n". + "- Name: %s\n", + $refineUI->{invIndex}, $item ? itemName($item) : "Unknown."), + "info"; + + message TF("%s:\n". + "- Needed: %d\n". + "- Owned: %d\n", + #itemNameSimple($Blacksmith_Blessing) + "Blacksmith Blessing", $refineUI->{bless}, $bless ? $bless->{amount} : 0), + "info"; + + @{$refineUI->{materials}} = map { my %r; @r{qw(nameid chance zeny)} = unpack 'v C V', $_; \%r} unpack '(a7)*', $args->{materials}; + + my $msg = center(T(" Possible Materials "), 53, '-') ."\n" . + T("Mat_ID % Zeny Material \n"); + foreach my $mat (@{$refineUI->{materials}}) { + my $myMat = $char->inventory->getByNameID($mat->{nameid}); + my $myMatCount = sprintf("%d ea %s", $myMat ? $myMat->{amount} : 0, itemNameSimple($mat->{nameid})); + $msg .= swrite( + "@>>>>>>>> @>>>>> @>>>>>>>>>>>> @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<", + [$mat->{nameid}, $mat->{chance}, $mat->{zeny}, $myMatCount]); + } + $msg .= ('-'x53) . "\n"; + message $msg, "info"; + message TF("Continue: refineui refine %d [Mat_ID] [catalyst_toggle] to continue.\n", $refineUI->{invIndex}), "info"; + } else { + error T("Equip cannot be refined, try different equipment. Type 'i' to check equipment and its index.\n"); + } +} + +sub refine_status { + my ($self, $args) = @_; + my $msgIndex = $args->{status} ? 3272 : 3273; + my $msg = sprintf($msgTable[$msgIndex], bytesToString($args->{name}), $args->{refine_level}, itemNameSimple($args->{itemID}))."\n"; + warning $msg, "info"; +} + +sub character_ban_list { + my ($self, $args) = @_; + # Header + Len + CharList[character_name(size:24)] +} + +sub flag { + my ($self, $args) = @_; +} + +sub offline_clone_found { + my ($self, $args) = @_; + + my $actor = $playersList->getByID($args->{ID}); + if (!defined $actor) { + $actor = new Actor::Player(); + $actor->{object_type} = 0x0; #player + $actor->{clone} = 1; + $actor->{ID} = $args->{ID}; + $actor->{nameID} = unpack("V", $args->{ID}); + $actor->{name} = bytesToString($args->{name}); + $actor->{appear_time} = time; + $actor->{jobID} = $args->{jobID}; + $actor->{type} = $args->{jobID}; + $actor->{pos}{x} = $args->{coord_x}; + $actor->{pos}{y} = $args->{coord_y}; + $actor->{pos_to}{x} = $args->{coord_x}; + $actor->{pos_to}{y} = $args->{coord_y}; + $actor->{time_move} = time; + $actor->{time_move_calc} = 0; + $actor->{walk_speed} = 1; #hack + $actor->{lv} = 1; + $actor->{robe} = $args->{robe}; + $actor->{clothes_color} = $args->{clothes_color}; + $actor->{headgear}{low} = $args->{lowhead}; + $actor->{headgear}{mid} = $args->{midhead}; + $actor->{headgear}{top} = $args->{tophead}; + $actor->{weapon} = $args->{weapon}; + $actor->{shield} = $args->{shield}; + $actor->{sex} = $args->{sex}; + $actor->{hair_color} = $args->{hair_color} if (exists $args->{hair_color}); + + $playersList->add($actor); + + Plugins::callHook('add_player_list', $actor); + Plugins::callHook('player', {player => $actor}); #backwards compatibility + Plugins::callHook('player_exist', {player => $actor}); + } +} + +sub offline_clone_lost { + my ($self, $args) = @_; + + # remove from player list + if (defined $playersList->getByID($args->{ID})) { + my $player = $playersList->getByID($args->{ID}); + + $player->{gone_time} = time; + $players_old{$args->{ID}} = $player->deepCopy(); + Plugins::callHook('player_disappeared', {player => $player}); + + $playersList->remove($player); + } + + # try to remove from vender list + binRemove(\@venderListsID, $args->{ID}); + delete $venderLists{$args->{ID}}; + + # try to remove from buyer list + binRemove(\@buyerListsID, $args->{ID}); + delete $buyerLists{$args->{ID}}; +} + +sub remain_time_info { + my ($self, $args) = @_; + debug TF("Remain Time - Result: %s - Expiration Date: %s - Time: %s\n", $args->{result}, $args->{expiration_date}, $args->{remain_time}), "console", 1; +} + +sub received_login_token { + my ($self, $args) = @_; + + # Skip in XKore mode 1 / 3 + return if $self->{net}->version == 1; + + my $master = $masterServers{$config{master}}; + my $login_type = $args->{login_type}; + + if ($login_type == 0) { + # rAthena uses 0064 not 0825 + $messageSender->sendTokenToServer( + $config{username}, + $config{password}, + $master->{master_version}, + $master->{version}, + $args->{login_token}, + $args->{len}, + $master->{ip}, + $master->{port} + ); + + } elsif ($login_type == 400 || $login_type == 1000) { + die 'ERROR: otpSeed is not set in config.txt' unless $config{otpSeed}; + + my $otp; + Plugins::callHook('request_otp_login', { otp => \$otp, seed => $config{otpSeed} }); + unless (defined $otp && length $otp) { + error "No Plugin returned a OTP code for account $config{username}\n", 'connection'; + $otp = $interface->query(T(', please enter your OTP: ')); + } + $messageSender->sendOtpToServer($otp); + + } elsif ($login_type == 300) { + error "OTP token malformed for account $config{username}\n", 'connection'; + my $otp = $interface->query(T('Please enter the OTP code: ')); + $messageSender->sendOtpToServer($otp); + + } elsif ($login_type == 500) { + error "Wrong OTP for account $config{username}\n", 'connection'; + my $otp = $interface->query(T('Please enter the OTP code: ')); + $messageSender->sendOtpToServer($otp); + + } elsif ($login_type == 600) { + error "Password Error for account $config{username}\n", 'connection'; + Misc::quit(); + + } elsif ($login_type == 900) { + error "You need to setup your OTP for account $config{username}\n", 'connection'; + Misc::quit(); + } else { + error "Unknown login_type $login_type\n", 'connection'; + Misc::quit(); + } +} + +# this info will be sent to xkore 2 clients +sub hotkeys { + my ($self, $args) = @_; + undef $hotkeyList; + my $msg; + + # TODO: implement this: $hotkeyList->{rotate} = $args->{rotate} if $args->{rotate}; + $msg .= center(" " . T("Hotkeys") . " ", 79, '-') . "\n"; + $msg .= swrite(sprintf("\@%s \@%s \@%s \@%s", ('>'x3), ('<'x30), ('<'x5), ('>'x3)), + ["#", T("Name"), T("Type"), T("Lv")]); + $msg .= sprintf("%s\n", ('-'x79)); + my $j = 0; + for (my $i = 0; $i < length($args->{hotkeys}); $i += 7) { + @{$hotkeyList->[$j]}{qw(type ID lv)} = unpack('C V v', substr($args->{hotkeys}, $i, 7)); + $msg .= swrite(sprintf("\@%s \@%s \@%s \@%s", ('>'x3), ('<'x30), ('<'x5), ('>'x3)), + [$j, $hotkeyList->[$j]->{type} ? Skill->new(idn => $hotkeyList->[$j]->{ID})->getName() : itemNameSimple($hotkeyList->[$j]->{ID}), + $hotkeyList->[$j]->{type} ? T("skill") : T("item"), + $hotkeyList->[$j]->{lv}]); + $j++; + } + $msg .= sprintf("%s\n", ('-'x79)); + debug($msg, "list"); +} + +sub received_character_ID_and_Map { + my ($self, $args) = @_; + message T("Received character ID and Map IP from Character Server\n"), "connection"; + $net->setState(4); + undef $conState_tries; + $charID = $args->{charID}; + + if ($net->version == 1) { + undef $masterServer; + $masterServer = $masterServers{$config{master}} if ($config{master} ne ""); + } + + my ($map) = $args->{mapName} =~ /([\s\S]*)\./; # cut off .gat + my $map_noinstance; + ($map_noinstance, undef) = Field::nameToBaseName(undef, $map); # Hack to clean up InstanceID + if (!$field || $map ne $field->name()) { + eval { + $field = new Field(name => $map); + }; + if (my $e = caught('FileNotFoundException', 'IOException')) { + error TF("Cannot load field %s: %s\n", $map_noinstance, $e); + undef $field; + } elsif ($@) { + die $@; + } + } + + if (exists $args->{mapUrl} && $args->{'mapUrl'} =~ /.*\:\d+/) { + $map_ip = $args->{mapUrl}; + $map_ip =~ s/:[0-9\0]+//; + $map_port = $args->{mapPort}; + } else { + $map_ip = makeIP($args->{mapIP}); + $map_ip = $masterServer->{ip} if ($masterServer && $masterServer->{private}); + $map_port = $args->{mapPort}; + } + + # Workaround. Current xKore 1 is not able to define the $char + if ($config{XKore} == 1) { + foreach my $character (@chars) { + if (getHex($charID) eq getHex($character->{charID})) { + configModify("char", $character->{slot}); + $char = $chars[$character->{slot}]; + } + } + } + + message TF("----------Game Info----------\n" . + "Char ID: %s (%s)\n" . + "MAP Name: %s\n" . + "MAP IP: %s\n" . + "MAP Port: %s\n" . + "-----------------------------\n", getHex($charID), unpack("V1", $charID), + $args->{mapName}, $map_ip, $map_port), "connection"; + checkAllowedMap($map_noinstance); + message(T("Closing connection to Character Server\n"), "connection") unless ($net->version == 1); + $net->serverDisconnect(1); + main::initStatVars(); +} + +sub received_sync { + return unless changeToInGameState(); + debug "Received Sync\n", 'parseMsg', 2; + $timeout{'play'}{'time'} = time; +} + +sub actor_look_at { + my ($self, $args) = @_; + return unless changeToInGameState(); + + my $actor = Actor::get($args->{ID}); + $actor->{look}{head} = $args->{head}; + $actor->{look}{body} = $args->{body}; + debug $actor->nameString . " looks at $args->{body}, $args->{head}\n", "parseMsg"; +} + +# Visually moves(slides) a character to x,y. If the target cell +# isn't walkable, the char doesn't move at all. If the char is +# sitting it will stand up (ZC_STOPMOVE). +# 0088 <id>.L <x>.W <y>.W +# 08CD <id>.L <x>.W <y>.W +sub actor_movement_interrupted { + my ($self, $args) = @_; + return unless changeToInGameState(); + my %coords; + $coords{x} = $args->{x}; + $coords{y} = $args->{y}; + + my $actor = Actor::get($args->{ID}); + $actor->{pos} = {%coords}; + $actor->{pos_to} = {%coords}; + $actor->{time_move} = time; + $actor->{time_move_calc} = 0; + if ($actor->isa('Actor::You') || $actor->isa('Actor::Player')) { + $actor->{sitting} = 0; + } + if ($actor->isa('Actor::You')) { + debug "Movement interrupted, your coordinates: $coords{x}, $coords{y}\n", "parseMsg_move"; + AI::clear("move"); + } + if ($char->{homunculus} && $char->{homunculus}{ID} eq $actor->{ID}) { + AI::clear("move"); + } +} + +sub actor_trapped { + my ($self, $args) = @_; + # original comment was that ID is not a valid ID + # but it seems to be, at least on eAthena/Freya + my $actor = Actor::get($args->{ID}); + debug "$actor->nameString() is trapped.\n"; +} + +sub party_join { + my ($self, $args) = @_; + return unless changeToInGameState(); + my $keys; + my $info; + if ($args->{switch} eq '0104') { # DEFAULT OLD PACKET + $keys = [qw(ID role x y type name user map)]; + } elsif ($args->{switch} eq '01E9') { # PACKETVER >= 2015 + $keys = [qw(ID role x y type name user map lv item_pickup item_share)]; + + } elsif ($args->{switch} eq '0A43') { # PACKETVER >= 2016 + $keys = [qw(ID role jobID lv x y type name user map item_pickup item_share)]; + + } elsif ($args->{switch} eq '0AE4') { # PACKETVER >= 2017 + $keys = [qw(ID charID role jobID lv x y type name user map item_pickup item_share)]; + + } else { # this can't happen + return; + } + + @{$info}{@{$keys}} = @{$args}{@{$keys}}; + + if (!$char->{party}{joined} || !$char->{party}{users}{$info->{ID}} || !%{$char->{party}{users}{$info->{ID}}}) { + binAdd(\@partyUsersID, $info->{ID}) if (binFind(\@partyUsersID, $info->{ID}) eq ""); + if ($info->{ID} eq $accountID) { + message TF("You joined party '%s'\n", bytesToString($info->{name})), undef, 1; + # Some servers receive party_users_info before party_join when logging in + # This is to prevent clearing info already in $char->{party} + $char->{party} = {} unless ref($char->{party}) eq "HASH"; + $char->{party}{joined} = 1; + Plugins::callHook('packet_partyJoin', { partyName => bytesToString($info->{name}) }); + } else { + message TF("%s joined your party '%s'\n", bytesToString($info->{user}), bytesToString($info->{name})), undef, 1; + } + } + + my $actor = $char->{party}{users}{$info->{ID}} && %{$char->{party}{users}{$info->{ID}}} ? $char->{party}{users}{$info->{ID}} : new Actor::Party; + + $actor->{admin} = !$info->{'role'}; + delete $actor->{statuses} unless $actor->{'online'} = !$info->{'type'}; + $actor->{pos}{x} = $info->{'x'}; + $actor->{pos}{y} = $info->{'y'}; + $actor->{map} = $info->{'map'}; + $actor->{name} = bytesToString($info->{'user'}); + $actor->{ID} = $info->{'ID'}; + $actor->{lv} = $info->{'lv'} if $info->{'lv'}; + $actor->{jobID} = $info->{'jobID'} if $info->{'jobID'}; + $actor->{charID} = $info->{'charID'} if $info->{'charID'}; # why now use charID? + $char->{party}{users}{$info->{'ID'}} = $actor; + $char->{party}{name} = bytesToString($info->{'name'}); + $char->{party}{itemPickup} = $info->{'item_pickup'}; + $char->{party}{itemDivision} = $info->{'item_share'}; +} + +# TODO: store this state +sub party_allow_invite { + my ($self, $args) = @_; + + if ($args->{type}) { + message T("Not allowed other player invite to Party\n"), "party", 1; + } else { + message T("Allowed other player invite to Party\n"), "party", 1; + } +} + +sub party_chat { + my ($self, $args) = @_; + my $msg = bytesToString($args->{message}); + + # Type: String + my ($chatMsgUser, $chatMsg) = $msg =~ /(.*?) : (.*)/; + $chatMsgUser =~ s/ $//; + + stripLanguageCode(\$chatMsg); + my $parsed_msg = solveMessage($chatMsg); + # Type: String + my $chat = "$chatMsgUser : $parsed_msg"; + message TF("[Party] %s\n", $chat), "partychat"; + + chatLog("p", "$chat\n") if ($config{'logPartyChat'}); + ChatQueue::add('p', $args->{ID}, $chatMsgUser, $parsed_msg); + debug "partychat: $chatMsg\n", "partychat", 1; + + Plugins::callHook('packet_partyMsg', { + MsgUser => $chatMsgUser, + Msg => $parsed_msg, + RawMsg => $chatMsg, + }); +} + +sub party_exp { + my ($self, $args) = @_; + $char->{party}{share} = $args->{type}; # Always will be there, in 0101 also in 07D8 + if ($args->{type} == 0) { + message T("Party EXP set to Individual Take\n"), "party", 1; + } elsif ($args->{type} == 1) { + message T("Party EXP set to Even Share\n"), "party", 1; + } else { + error T("Error setting party option\n"); + } + if (exists($args->{itemPickup}) || exists($args->{itemDivision})) { + $char->{party}{itemPickup} = $args->{itemPickup}; + $char->{party}{itemDivision} = $args->{itemDivision}; + if ($args->{itemPickup} == 0) { + message T("Party item set to Individual Take\n"), "party", 1; + } elsif ($args->{itemPickup} == 1) { + message T("Party item set to Even Share\n"), "party", 1; + } else { + error T("Error setting party option\n"); + } + if ($args->{itemDivision} == 0) { + message T("Party item division set to Individual Take\n"), "party", 1; + } elsif ($args->{itemDivision} == 1) { + message T("Party item division set to Even Share\n"), "party", 1; + } else { + error T("Error setting party option\n"); + } + } +} + +sub party_leader { + my ($self, $args) = @_; + for (my $i = 0; $i < @partyUsersID; $i++) { + if (unpack("V",$partyUsersID[$i]) eq $args->{old}) { + $char->{party}{users}{$partyUsersID[$i]}{admin} = ''; + } + if (unpack("V",$partyUsersID[$i]) eq $args->{new}) { + $char->{party}{users}{$partyUsersID[$i]}{admin} = 1; + message TF("New party leader: %s\n", $char->{party}{users}{$partyUsersID[$i]}{name}), "party", 1; + } + } +} + +sub party_hp_info { + my ($self, $args) = @_; + my $ID = $args->{ID}; + + if ($char->{party}{users}{$ID}) { + $char->{party}{users}{$ID}{hp} = $args->{hp}; + $char->{party}{users}{$ID}{hp_max} = $args->{hp_max}; + } +} + +sub party_invite { + my ($self, $args) = @_; + my $name = bytesToString($args->{name}); + message TF("Incoming Request to join party '%s'\n", $name); + $incomingParty{ID} = $args->{ID}; + $incomingParty{ACK} = $args->{switch} eq '02C6' ? '02C7' : '00FF'; + $timeout{ai_partyAutoDeny}{time} = time; + Plugins::callHook('party_invite', { + partyID => $args->{ID}, + partyName => $name + }); +} + +sub party_invite_result { + my ($self, $args) = @_; + my $name = bytesToString($args->{name}); + if ($args->{type} == ANSWER_ALREADY_OTHERGROUPM) { + warning TF("Join request failed: %s is already in a party\n", $name); + } elsif ($args->{type} == ANSWER_JOIN_REFUSE) { + warning TF("Join request failed: %s denied request\n", $name); + } elsif ($args->{type} == ANSWER_JOIN_ACCEPT) { + message TF("%s accepted your request\n", $name), "info"; + } elsif ($args->{type} == ANSWER_MEMBER_OVERSIZE) { + message T("Join request failed: Party is full.\n"), "info"; + } elsif ($args->{type} == ANSWER_DUPLICATE) { + message TF("Join request failed: same account of %s allready joined the party.\n", $name), "info"; + } elsif ($args->{type} == ANSWER_JOINMSG_REFUSE) { + message TF("Join request failed: ANSWER_JOINMSG_REFUSE.\n", $name), "info"; + } elsif ($args->{type} == ANSWER_UNKNOWN_ERROR) { + message TF("Join request failed: unknown error.\n", $name), "info"; + } elsif ($args->{type} == ANSWER_UNKNOWN_CHARACTER) { + message TF("Join request failed: the character is not currently online or does not exist.\n", $name), "info"; + } elsif ($args->{type} == ANSWER_INVALID_MAPPROPERTY) { + message TF("Join request failed: ANSWER_INVALID_MAPPROPERTY.\n", $name), "info"; + } +} + +sub party_leave { + my ($self, $args) = @_; + + my $ID = $args->{ID}; + my $actor = $char->{party}{users}{$ID}; # bytesToString($args->{name}) + delete $char->{party}{users}{$ID}; + binRemove(\@partyUsersID, $ID); + if ($ID eq $accountID) { + $actor = $char; + delete $char->{party}; + undef @partyUsersID; + $char->{party}{joined} = 0; + } + + if ($args->{result} == GROUPMEMBER_DELETE_LEAVE) { + message TF("%s left the party\n", $actor); + } elsif ($args->{result} == GROUPMEMBER_DELETE_EXPEL) { + message TF("%s left the party (kicked)\n", $actor); + } else { + message TF("%s left the party (unknown reason: %d)\n", $actor, $args->{result}); + } +} + +sub party_location { + my ($self, $args) = @_; + + my $ID = $args->{ID}; + + if ($char->{party}{users}{$ID}) { + $char->{party}{users}{$ID}{pos}{x} = $args->{x}; + $char->{party}{users}{$ID}{pos}{y} = $args->{y}; + $char->{party}{users}{$ID}{online} = 1; + debug "Party member location: $char->{party}{users}{$ID}{name} - $args->{x}, $args->{y}\n", "parseMsg"; + } +} +sub party_organize_result { + my ($self, $args) = @_; + + unless ($args->{fail}) { + $char->{party}{users}{$accountID}{admin} = 1 if $char->{party}{users}{$accountID}; + } elsif ($args->{fail} == 1) { + warning T("Can't organize party - party name exists\n"); + } elsif ($args->{fail} == 2) { + warning T("Can't organize party - you are already in a party\n"); + } elsif ($args->{fail} == 3) { + warning T("Can't organize party - not allowed in current map\n"); + } else { + warning TF("Can't organize party - unknown (%d)\n", $args->{fail}); + } +} + +sub party_show_picker { + my ($self, $args) = @_; + + # wtf the server sends this packet for your own character? (rRo) + return if $args->{sourceID} eq $accountID; + + my $string = ($char->{party}{users}{$args->{sourceID}} && %{$char->{party}{users}{$args->{sourceID}}}) ? $char->{party}{users}{$args->{sourceID}}->name() : $args->{sourceID}; + my $item = {}; + $item->{nameID} = $args->{nameID}; + $item->{identified} = $args->{identified}; + $item->{upgrade} = $args->{upgrade}; + $item->{cards} = $args->{cards}; + $item->{broken} = $args->{broken}; + message TF("Party member %s has picked up item %s.\n", $string, itemName($item)), "info"; +} + +sub party_users_info { + my ($self, $args) = @_; + return unless changeToInGameState(); + + my $player_info; + + if ($args->{switch} eq '0A44') { # PACKETVER >= 20151007 + $player_info = { + len => 50, + types => 'V Z24 Z16 C2 v2', + keys => [qw(ID name map admin online jobID lv)], + }; + + } elsif ($args->{switch} eq '0AE5') { # PACKETVER >= 20171207 + $player_info = { + len => 54, + types => 'V V Z24 Z16 C2 v2', + keys => [qw(ID GID name map admin online jobID lv)], + }; + + } else { # 00FB - DEFAULT [OLD] + $player_info = { + len => 46, + types => 'V Z24 Z16 C2', + keys => [qw(ID name map admin online)], + }; + } + + $char->{party}{name} = bytesToString($args->{party_name}); + + for (my $i = 0; $i < length($args->{playerInfo}); $i += $player_info->{len}) { + # in 0a43 lasts bytes: { <item pickup rule>.B <item share rule>.B <unknown>.L } + next if (length($args->{playerInfo}) - $i == 6); + + my $ID = substr($args->{playerInfo}, $i, 4); + + if (binFind(\@partyUsersID, $ID) eq "") { + binAdd(\@partyUsersID, $ID); + } + + $char->{party}{users}{$ID} = new Actor::Party(); + @{$char->{party}{users}{$ID}}{@{$player_info->{keys}}} = unpack($player_info->{types}, substr($args->{playerInfo}, $i, $player_info->{len})); + $char->{party}{users}{$ID}{name} = bytesToString($char->{party}{users}{$ID}{name}); + $char->{party}{users}{$ID}{admin} = !$char->{party}{users}{$ID}{admin}; + $char->{party}{users}{$ID}{online} = !$char->{party}{users}{$ID}{online}; + + # If party member return to saveMap out of our screen, the server will send to us party_users_info [iRO-RT 2020-jan] + undef $char->{party}{users}{$ID}{'dead'}; + undef $char->{party}{users}{$ID}{'dead_time'}; + + debug TF("Party Member: %s (%s)\n", $char->{party}{users}{$ID}{name}, $char->{party}{users}{$ID}{map}), "party", 1; + } + Plugins::callHook('party_users_info_ready'); +} + +# Notifies the party members of a character's death or revival. +# 0AB2 <GID>.L <dead>.B +sub party_dead { + my ($self, $args) = @_; + + my $string = ($char->{party}{users}{$args->{ID}} && %{$char->{party}{users}{$args->{ID}}}) ? $char->{party}{users}{$args->{ID}}->name() : $args->{ID}; + + # 0x0 = alive + # 0x1 = dead + if ($args->{isDead} == 1) { + message TF("Party member %s is dead.\n", $string), "info"; + $char->{party}{users}{$args->{ID}}{dead} = 1; + $char->{party}{users}{$args->{ID}}{dead_time} = time; + } else { + message TF("Party member %s is alive.\n", $string), "info"; + undef $char->{party}{users}{$args->{ID}}{'dead'}; + undef $char->{party}{users}{$args->{ID}}{'dead_time'}; + } +} + +sub rodex_mail_list { + my ( $self, $args ) = @_; + + my $mail_info; + + if ($args->{switch} eq '0B5F') { + $mail_info = { + len => 45, + types => 'C V2 C2 Z24 V v x4', + keys => [qw(openType mailID1 mailID2 isRead attach sender expireSecconds Titlelength)], + }; + + } elsif ($args->{switch} eq '0AC2') { + $mail_info = { + len => 41, + types => 'C V2 C2 Z24 V v', + keys => [qw(openType mailID1 mailID2 isRead attach sender expireSecconds Titlelength)], + }; + + } else { # 09F0, 0A7D + $mail_info = { + len => 44, + types => 'V2 C2 Z24 V2 v', + keys => [qw(mailID1 mailID2 isRead attach sender regDateTime expireSecconds Titlelength)], + }; + } + + if ($args->{switch} eq '09F0' || $args->{switch} eq '0A7D') { + $rodexCurrentType = $args->{attach}; + } + + if ($args->{switch} eq '0A7D' || $args->{switch} eq '0AC2' || $args->{switch} eq '0B5F') { + $rodexList->{current_page} = 0; + $rodexList = {}; + $rodexList->{mails} = {}; + } else { + $rodexList->{current_page}++; + } + + if ($args->{isEnd} == 1) { + $rodexList->{last_page} = $rodexList->{current_page}; + } else { + $rodexList->{mails_per_page} = $args->{amount}; + } + + my $mail_len; + my $msg = center(" ". TF("Rodex Mail Page %d", $rodexList->{current_page}) ." ", 119, '-') . "\n" . + T(" # ID From Att New Expire Title\n"); + + my $index = 0; + for (my $i = 0; $i < length($args->{mailList}); $i+=$mail_info->{len}) { + my $mail; + + @{$mail}{@{$mail_info->{keys}}} = unpack($mail_info->{types}, substr($args->{mailList}, $i, $mail_info->{len})); + + $mail->{title} = solveMSG(bytesToString(substr($args->{mailList}, ($i+$mail_info->{len}), $mail->{Titlelength}))); + $mail->{sender} = solveMSG(bytesToString($mail->{sender})); + $mail->{page} = $rodexList->{current_page}; + $mail->{page_index} = $index; + $mail->{expireDay} = int ($mail->{expireSecconds} / 60 / 60 / 24); + + $i+= $mail->{Titlelength}; + + $rodexList->{mails}{$mail->{mailID1}} = $mail; + $rodexList->{current_page_last_mailID} = $mail->{mailID1}; + + my %attach = ( + #0 => '-', # no attach + 2 => T('z'), # only zeny + 4 => T('i'), # only item + 6 => T('z+i'), # zeny + item + 12 => T('gift'),# a gift from the admin + ); + $mail->{attach} = $attach{$mail->{attach}}; + + $msg .= swrite("@> @<<<<<<< @<<<<<<<<<<<<<<<<<<<<<< @<<< @<< @>>>>>>> @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<", [$index, $mail->{mailID1}, $mail->{sender}, $mail->{attach} ? $mail->{attach} : "-", $mail->{isRead} ? T("No") : T("Yes"), $mail->{expireDay} ." ".T("Days"), $mail->{title}]); + + $index++; + } + $msg .= ('-'x119) . "\n"; + message $msg, "list"; + + Plugins::callHook('rodex_mail_list', { + 'mails' => $rodexList->{mails}, + 'current_page' => $rodexList->{current_page}, + 'last_mailID' => $rodexList->{current_page_last_mailID}, + 'isEnd' => $args->{isEnd}, + }); +} + +sub rodex_read_mail { + my ( $self, $args ) = @_; + + my $msg = $args->{RAW_MSG}; + my $msg_size = $args->{RAW_MSG_SIZE}; + my $header_pack = 'v C V2 v V2 C'; + my $header_len = ((length pack $header_pack) + 2); + + my $mail = {}; + + $mail->{body} = bytesToString( substr($msg, $header_len, $args->{text_len}) ); + chomp ($mail->{body}); + $mail->{body} = solveMSG($mail->{body}); + + $mail->{zeny1} = $args->{zeny1}; + $mail->{zeny2} = $args->{zeny2}; + + $mail->{type} = $args->{type}; + my %opentype = ( + 0 => T('Mail from players'), + 1 => T('Account mail'), + 2 => T('Return'), + 3 => T('Unset'), + ); + + my $item_pack = $self->{rodex_read_mail_item_pack} || 'v2 C3 a8 a4 C a4 a25'; + my $item_len = length pack $item_pack; + + my $mail_len; + + $mail->{items} = []; + + my $print_msg = center(" " .TF("Mail %d from %s", $args->{mailID1}, $rodexList->{mails}{$args->{mailID1}}{sender}) ." ", 119, '-') . "\n"; + $print_msg .= swrite("@<<<<<<<<<<< @<<<<<<<<<<<<<<<<", [T("Mail type:"), $opentype{$mail->{type}}]); + $print_msg .= swrite("@<<<<<<<<<<< @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<", [T("Title:"), $rodexList->{mails}{$args->{mailID1}}{title}]); + $print_msg .= T("Message:") ." " .$mail->{body} ."\n"; + message $print_msg, "list"; + + $print_msg = swrite("@<<<<<<<<<<< @<<<<<<", [T("Item count:"), $args->{itemCount}]); + $print_msg .= swrite("@<<<<<<<<<<< @<<<<<<<<<", [T("Zeny:"), $args->{zeny1}]); + + my $index = 0; + for (my $i = ($header_len + $args->{text_len}); $i < $args->{RAW_MSG_SIZE}; $i += $item_len) { + my $item; + ($item->{amount}, + $item->{nameID}, + $item->{identified}, + $item->{broken}, + $item->{upgrade}, + $item->{cards}, + $item->{unknow1}, + $item->{type}, + $item->{unknow2}, + $item->{options}) = unpack($item_pack, substr($msg, $i, $item_len)); + + $item->{name} = itemName($item); + + my $display = $item->{name}; + $display .= " x $item->{amount}"; + + $print_msg .= swrite("@<<< @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<", [$index, $display]); + + push(@{$mail->{items}}, $item); + $index++; + } + + $print_msg .= ('-'x119) . "\n"; + message $print_msg, "list"; + + @{$rodexList->{mails}{$args->{mailID1}}}{qw(body items zeny1 zeny2)} = @{$mail}{qw(body items zeny1 zeny2)}; + + $rodexList->{mails}{$args->{mailID1}}{isRead} = 1; + + $rodexList->{current_read} = $args->{mailID1}; + + Plugins::callHook('rodex_mail', { + 'mailID' => $args->{mailID1}, + 'from' => $rodexList->{mails}{$args->{mailID1}}->{sender}, + 'title' => $rodexList->{mails}{$args->{mailID1}}->{title}, + 'content' => $mail->{body}, + 'zeny' => $args->{zeny1}, + 'itemCount' => $args->{itemCount}, + 'items' => $mail->{items}, + }); +} + +sub unread_rodex { + my ( $self, $args ) = @_; + message T("You have new unread rodex mails.\n"); + Plugins::callHook('rodex_unread_mail'); +} + +sub rodex_remove_item { + my ( $self, $args ) = @_; + + if (!$args->{result}) { + error T("You failed to remove an item from rodex mail.\n"); + return; + } + + my $rodex_item = $rodexWrite->{items}->getByID($args->{ID}); + + my $msg = TF("Item removed from rodex mail message: %s (%d) x %d - %s", + $rodex_item->{name}, $rodex_item->{binID}, $args->{amount}, $itemTypes_lut{$rodex_item->{type}}); + message "$msg\n", "drop"; + + $rodex_item->{amount} -= $args->{amount}; + if ($rodex_item->{amount} <= 0) { + $rodexWrite->{items}->remove($rodex_item); + } +} + +sub rodex_add_item { + my ( $self, $args ) = @_; + + if ($args->{fail} == 1) { + error T("Item attachment has been failed.\n");#RODEX_ADD_ITEM_WEIGHT_ERROR + } elsif ($args->{fail} == 2) { + error T("Item attachment has been failed.\n");#MsgStringTable[2630] + } elsif ($args->{fail} == 3) { + error T("Maximum number of item attachments has been exceeded.\n");#MsgStringTable[2698] + } elsif ($args->{fail} == 4) { + error T("This item is banned to attach.\n");#MsgStringTable[2700] + } elsif ($args->{fail} != 0) { + error TF("Unknown error %s\n", $args->{fail}); + } + return if ($args->{fail}); + + my $rodex_item = $rodexWrite->{items}->getByID($args->{ID}); + + if ($rodex_item) { + $rodex_item->{amount} += $args->{amount}; + } else { + $rodex_item = new Actor::Item(); + $rodex_item->{ID} = $args->{ID}; + $rodex_item->{nameID} = $args->{nameID}; + $rodex_item->{type} = $args->{type}; + $rodex_item->{amount} = $args->{amount}; + $rodex_item->{identified} = $args->{identified}; + $rodex_item->{broken} = $args->{broken}; + $rodex_item->{upgrade} = $args->{upgrade}; + $rodex_item->{cards} = $args->{cards}; + $rodex_item->{options} = $args->{options}; + $rodex_item->{weight} = $args->{weight}; + $rodex_item->{name} = itemName($rodex_item); + + $rodexWrite->{items}->add($rodex_item); + } + + my $msg = TF("Item added to rodex mail message: %s (%d) x %d - %s", + $rodex_item->{name}, $rodex_item->{binID}, $args->{amount}, $itemTypes_lut{$rodex_item->{type}}); + message "$msg\n", "drop"; +} + +sub rodex_open_write { + my ( $self, $args ) = @_; + + $rodexWrite = {}; + + $rodexWrite->{items} = new InventoryList; + if ($args->{name}) { + $rodexWrite->{target}{name} = bytesToString($args->{name}); + $messageSender->rodex_checkname($rodexWrite->{target}{name}); + } + $rodexWrite->{title} = T("TITLE"); + debug "Rodex Mail Target: '$rodexWrite->{target}{name}', Title: '$rodexWrite->{title}'\n"; +} + +sub rodex_check_player { + my ( $self, $args ) = @_; + my $rodex_check_player_unpack; + my $name = bytesToString($args->{name}); + + if (!$args->{char_id}) { + error TF("Could not find player with name '%s'.\n", $name); + delete $rodexWrite->{target}; + return; + } + + if ($args->{switch} eq '0A14') { + $rodex_check_player_unpack = { + target => [qw(char_id class base_level)], + }; + } elsif ($args->{switch} eq '0A51') { + $rodexWrite->{target}{name} = $name; + $rodex_check_player_unpack = { + target => [qw(char_id class base_level name)], + }; + } + + my $print_msg = center( " " .T("Rodex Mail Target") ." ", 62, '-') . "\n"; + $print_msg .= swrite(" @>>>> @<<<<<<<<<<<<<<<<<<<<<<< @<<<<<<<<<< @<<<", [T("Name:"), $rodexWrite->{target}{name}, T("Base Level:"), $args->{base_level}]); + $print_msg .= swrite("@>>>>>>> @<<<<<<<<<<<<<<<<<<<<<<< @<<<<< @<<<<<<<<<<<<<<<<<<<<", [T("Char ID:"), $args->{char_id}, T("Class:"), $jobs_lut{$args->{class}}]); + $print_msg .= ('-'x62) . "\n"; + message $print_msg, "list"; + + @{$rodexWrite->{target}}{@{$rodex_check_player_unpack->{target}}} = @{$args}{@{$rodex_check_player_unpack->{target}}}; +} + +sub rodex_write_result { + my ( $self, $args ) = @_; + + if ($args->{fail}) { + error T("You failed to send the rodex mail.\n"); + return; + } + + message T("Your rodex mail was sent with success.\n"); + undef $rodexWrite; +} + +sub rodex_get_zeny { + my ( $self, $args ) = @_; + + if ($args->{fail}) { + error T("You failed to get the zeny of the rodex mail.\n"); + return; + } + + message T("The zeny of the rodex mail was requested with success.\n"); + + $rodexList->{mails}{ $args->{mailID1} }{zeny1} = 0; + $rodexList->{mails}{ $args->{mailID1} }{attach} = $rodexList->{mails}{ $args->{mailID1} }{attach} eq 'z' ? 0 : 'i'; +} + +sub rodex_get_item { + my ( $self, $args ) = @_; + + if ($args->{fail}) { + error T("You failed to get the items of the rodex mail.\n"); + return; + } + + message T("The items of the rodex mail were requested with success.\n"); + + $rodexList->{mails}{$args->{mailID1}}{items} = []; + $rodexList->{mails}{$args->{mailID1}}{attach} = $rodexList->{mails}{$args->{mailID1}}{attach} eq 'i' ? undef : 'z'; +} + +sub rodex_delete { + my ( $self, $args ) = @_; + + return unless (exists $rodexList->{mails}{$args->{mailID1}}); + + message TF("You have deleted the mail of ID %s.\n", $args->{mailID1}); + + Plugins::callHook('rodex_mail_deleted', { + 'mailID' => $args->{mailID1}, + }); + + delete $rodexList->{mails}{$args->{mailID1}}; +} + +# 0x803 +sub booking_register_request { + my ($self, $args) = @_; + my $result = $args->{result}; + + if ($result == 0) { + message T("Booking successfully created!\n"), "booking"; + } elsif ($result == 2) { + error T("You already got a reservation group active!\n"), "booking"; + } else { + error TF("Unknown error in creating the group booking (Error %s)\n", $result), "booking"; + } +} + +# 0x805 +sub booking_search_request { + my ($self, $args) = @_; + + if (length($args->{innerData}) == 0) { + error T("Without results!\n"), "booking"; + return; + } + + message T("-------------- Booking Search ---------------\n"); + for (my $offset = 0; $offset < length($args->{innerData}); $offset += 48) { + my ($index, $charName, $expireTime, $level, $mapID, @job) = unpack("V Z24 V s8", substr($args->{innerData}, $offset, 48)); + message swrite( + T("Name: \@<<<<<<<<<<<<<<<<<<<<<<<< Index: \@>>>>\n" . + "Created: \@<<<<<<<<<<<<<<<<<<<<< Level: \@>>>\n" . + "MapID: \@<<<<<\n". + "Job: \@<<<< \@<<<< \@<<<< \@<<<< \@<<<<\n" . + "---------------------------------------------"), + [bytesToString($charName), $index, getFormattedDate($expireTime), $level, $mapID, @job]), "booking"; + } +} + +# 0x807 +sub booking_delete_request { + my ($self, $args) = @_; + my $result = $args->{result}; + + if ($result == 0) { + message T("Reserve deleted successfully!\n"), "booking"; + } elsif ($result == 3) { + error T("You're not with a group booking active!\n"), "booking"; + } else { + error TF("Unknown error in deletion of group booking (Error %s)\n", $result), "booking"; + } +} + +# 0x809 +sub booking_insert { + my ($self, $args) = @_; + + message TF("%s has created a new group booking (index: %s)\n", bytesToString($args->{name}), $args->{ID}); +} + +# 0x80A +sub booking_update { + my ($self, $args) = @_; + + message TF("Reserve index of %s has changed its settings\n", $args->{ID}); +} + +# 0x80B +sub booking_delete { + my ($self, $args) = @_; + + message TF("Deleted reserve group index %s\n", $args->{ID}); +} + +sub clan_user { + my ($self, $args) = @_; + foreach (qw(onlineuser totalmembers)) { + $clan{$_} = $args->{$_}; + } + $clan{onlineuser} = $args->{onlineuser}; + $clan{totalmembers} = $args->{totalmembers}; +} + +sub clan_info { + my ($self, $args) = @_; + foreach (qw(clan_ID clan_name clan_master clan_map alliance_count antagonist_count)) { + $clan{$_} = $args->{$_}; + } + + $clan{clan_name} = bytesToString($args->{clan_name}); + $clan{clan_master} = bytesToString($args->{clan_master}); + $clan{clan_map} = bytesToString($args->{clan_map}); + + my $i = 0; + my $count = 0; + $clan{ally_names} = ""; + $clan{antagonist_names} = ""; + + if ($args->{alliance_count} > 0) { + for ($count; $count < $args->{alliance_count}; $count++) { + $clan{ally_names} .= bytesToString(unpack("Z24", substr($args->{ally_antagonist_names}, $i, 24))).", "; + $i += 24; + } + } + + $count = 0; + if ($args->{antagonist_count} > 0) { + for ($count; $count < $args->{antagonist_count}; $count++) { + $clan{antagonist_names} .= bytesToString(unpack("Z24", substr($args->{ally_antagonist_names}, $i, 24))).", "; + $i += 24; + } + } +} + +sub clan_chat { + my ($self, $args) = @_; + my ($chatMsgUser, $chatMsg, $parsed_msg); # Type: String + + return unless changeToInGameState(); + $chatMsgUser = bytesToString($args->{charname}); + $chatMsg = bytesToString($args->{message}); + $parsed_msg = solveMessage($chatMsg); + + chatLog("clan", "$chatMsgUser : $parsed_msg\n") if ($config{'logClanChat'}); + # Translation Comment: Guild Chat + message TF("[Clan]%s %s\n", $chatMsgUser, $parsed_msg), "clanchat"; + # Only queue this if it's a real chat message + ChatQueue::add('clan', 0, $chatMsgUser, $parsed_msg) if ($chatMsgUser); + debug "clanchat: $chatMsg\n", "clanchat", 1; + + Plugins::callHook('packet_clanMsg', { + MsgUser => $chatMsgUser, + Msg => $parsed_msg, + RawMsg => $chatMsg, + }); +} + +sub clan_leave { + my ($self, $args) = @_; + + if ($clan{clan_name}) { + message TF("[Clan] You left %s\n", $clan{clan_name}); + undef %clan; + } +} + +sub change_title { + my ($self, $args) = @_; + #TODO : <result>.B + message TF("You changed Title_ID : %s.\n", $args->{title_id}), "info"; +} + +# 019E +# TODO +# note: this is probably the trigger for the client's slotmachine effect or so. +sub pet_capture_process { + my ($self, $args) = @_; + message T("Attempting to capture pet (slot machine).\n"), "info"; +} + +sub pet_capture_result { + my ($self, $args) = @_; + if ($args->{success}) { + message T("Pet capture success\n"), "info"; + } else { + message T("Pet capture failed\n"), "info"; + } +} + +sub pet_emotion { + my ($self, $args) = @_; + my ($ID, $type) = ($args->{ID}, $args->{type}); + my $emote = $emotions_lut{$type}{display} || "/e$type"; + if ($pets{$ID}) { + message $pets{$ID}->name . " : $emote\n", "emotion"; + } +} + +sub pet_evolution_result { + my ($self, $args) = @_; + if ($args->{result} == 0x0) { + error TF("Pet evolution error.\n"); + #PET_EVOL_NO_CALLPET = 0x1, + #PET_EVOL_NO_PETEGG = 0x2, + } elsif ($args->{result} == 0x3) { + error TF("Unequip pet accessories first to start evolution.\n"); + } elsif ($args->{result} == 0x4) { + error TF("Insufficient materials for evolution.\n"); + } elsif ($args->{result} == 0x5) { + error TF("Loyal Intimacy is required to evolve.\n"); + } elsif ($args->{result} == 0x6) { + message TF("Pet evolution success.\n"), "success"; + } +} + +sub pet_food { + my ($self, $args) = @_; + if ($args->{success}) { + message TF("Fed pet with %s\n", itemNameSimple($args->{foodID})), "pet"; + } else { + error TF("Failed to feed pet with %s: no food in inventory.\n", itemNameSimple($args->{foodID})); + } +} + +sub pet_info { + my ($self, $args) = @_; + $pet{name} = bytesToString($args->{name}); + $pet{renameflag} = $args->{renameflag}; + $pet{level} = $args->{level}; + $pet{hungry} = $args->{hungry}; + $pet{friendly} = $args->{friendly}; + $pet{accessory} = $args->{accessory}; + $pet{type} = $args->{type} if (exists $args->{type}); + debug "Pet status: name=$pet{name} name_set=". ($pet{renameflag} ? 'yes' : 'no') ." level=$pet{level} hungry=$pet{hungry} intimacy=$pet{friendly} accessory=".itemNameSimple($pet{accessory})." type=".($pet{type}||"N/A")."\n", "pet"; +} + +sub pet_info2 { + my ($self, $args) = @_; + my ($type, $ID, $value) = @{$args}{qw(type ID value)}; + + # receive information about your pet + + # related freya functions: clif_pet_equip clif_pet_performance clif_send_petdata + + # these should never happen, pets should spawn like normal actors (at least on Freya) + # this isn't even very useful, do we want random pets with no location info? + #if (!$pets{$ID} || !%{$pets{$ID}}) { + # binAdd(\@petsID, $ID); + # $pets{$ID} = {}; + # %{$pets{$ID}} = %{$monsters{$ID}} if ($monsters{$ID} && %{$monsters{$ID}}); + # $pets{$ID}{'name_given'} = "Unknown"; + # $pets{$ID}{'binID'} = binFind(\@petsID, $ID); + # debug "Pet spawned (unusually): $pets{$ID}{'name'} ($pets{$ID}{'binID'})\n", "parseMsg"; + #} + #if ($monsters{$ID}) { + # if (%{$monsters{$ID}}) { + # objectRemoved('monster', $ID, $monsters{$ID}); + # } + # # always clear these in case + # binRemove(\@monstersID, $ID); + # delete $monsters{$ID}; + #} + + if ($type == 0) { + # You own no pet. + undef $pet{ID}; + + } elsif ($type == 1) { + $pet{friendly} = $value; + debug "Pet friendly: $value\n"; + + } elsif ($type == 2) { + $pet{hungry} = $value; + debug "Pet hungry: $value\n"; + + } elsif ($type == 3) { + # accessory info for any pet in range + $pet{accessory} = $value; + debug "Pet accessory info: $value\n"; + + } elsif ($type == 4) { + # performance info for any pet in range + #debug "Pet performance info: $value\n"; + + } elsif ($type == 5) { + # You own pet with this ID + $pet{ID} = $ID; + } +} + +sub elemental_info { + my ($self, $args) = @_; + + $char->{elemental} = Actor::get($args->{ID}) if ($char->{elemental}{ID} ne $args->{ID}); + if (!defined $char->{elemental}) { + $char->{elemental} = new Actor::Elemental; + } + + foreach (@{$args->{KEYS}}) { + $char->{elemental}{$_} = $args->{$_}; + } +} + +# 0221 +sub upgrade_list { + my ($self, $args) = @_; + undef $refineList; + my $k = 0; + my $msg; + + $msg .= center(" " . T("Upgrade List") . " ", 79, '-') . "\n"; + + for (my $i = 0; $i < length($args->{item_list}); $i += 13) { + my ($index, $nameID) = unpack('a2 x6 C', substr($args->{item_list}, $i, 13)); + my $item = $char->inventory->getByID($index); + $refineList->[$k] = unpack('v', $item->{ID}); + $msg .= swrite(sprintf("\@%s - \@%s (\@%s)", ('<'x2), ('<'x50), ('<'x3)), [$k, itemName($item), $item->{binID}]); + $k++; + } + + $msg .= sprintf("%s\n", ('-'x79)); + + message($msg, "list"); + message T("You can now use the 'refine' command.\n"), "info"; +} + +# 025A +sub cooking_list { + my ($self, $args) = @_; + undef $cookingList; + undef $currentCookingType; + my $k = 0; + my $msg; + $currentCookingType = $args->{type}; + $msg .= center(" " . T("Cooking List") . " ", 79, '-') . "\n"; + for (my $i = 0; $i < length($args->{item_list}); $i += 2) { + my $nameID = unpack('v', substr($args->{item_list}, $i, 2)); + $cookingList->[$k] = $nameID; + $msg .= swrite(sprintf("\@%s \@%s", ('>'x2), ('<'x50)), [$k, itemNameSimple($nameID)]); + $k++; + } + $msg .= sprintf("%s\n", ('-'x79)); + + message($msg, "list"); + message T("You can now use the 'cook' command.\n"), "info"; + + Plugins::callHook('cooking_list', { + cooking_list => $cookingList, + }); +} + +sub refine_result { + my ($self, $args) = @_; + if ($args->{fail} == 0) { + message TF("You successfully refined a weapon (ID %s)!\n", $args->{nameID}); + } elsif ($args->{fail} == 1) { + message TF("You failed to refine a weapon (ID %s)!\n", $args->{nameID}); + } elsif ($args->{fail} == 2) { + message TF("You successfully made a potion (ID %s)!\n", $args->{nameID}); + } elsif ($args->{fail} == 3) { + message TF("You failed to make a potion (ID %s)!\n", $args->{nameID}); + } elsif ($args->{fail} == 6) { + message TF("You successfully cook a item (ID %s)!\n", $args->{nameID}); + } else { + message TF("You tried to refine a weapon (ID %s); result: unknown %s\n", $args->{nameID}, $args->{fail}); + } +} + +# 0223 +sub upgrade_message { + my ($self, $args) = @_; + my $item = itemNameSimple($args->{itemID}); + if ($args->{type} == 0) { # Success + message TF("Weapon upgraded: %s\n", $item), "info"; + } elsif ($args->{type} == 1) { # Fail + message TF("Weapon not upgraded: %s\n", $item), "info"; + # message TF("Weapon upgraded: %s\n", $item), "info"; + } elsif ($args->{type} == 2) { # Fail Lvl + error TF("Cannot upgrade %s until you level up the upgrade weapon skill.\n", $item), "info"; + } elsif ($args->{type} == 3) { # Fail Item + message TF("You lack item %s to upgrade the weapon.\n", $item), "info"; + } +} + +sub open_buying_store_fail { #0x812 + my ($self, $args) = @_; + my $result = $args->{result}; + if ($result == 1){ + error T("Failed to open Purchasing Store.\n"),"info"; + } elsif ($result == 2){ + error T("The total weight of the item exceeds your weight limit. Please reconfigure.\n"), "info"; + } elsif ($result == 8){ + error T("Shop information is incorrect and cannot be opened.\n"), "info"; + } else { + error T("Failed opening your buying store.\n"), "info"; + } + $buyershopstarted = 0; +} + +sub search_store_open { + my ($self, $args) = @_; + + debug TF("Opened %s for searching open vendors in this map.\n", + $args->{type} ? T("Universal Catalog Gold") : T("Universal Catalog Silver")), + 2, "search_store"; + message TF("You can now search open vendors in this map. Searches remaining: %d\n", $args->{amount}); + + $universalCatalog{open} = 1; + $universalCatalog{type} = $args->{type}; +} + +sub search_store_fail { + my ($self, $args) = @_; + + error TF("Search store failed. Reason #%d\n", $args->{reason}); + + if ($args->{reason} == 0) { + error $msgTable[1804] . "\n"; + } elsif ($args->{reason} == 1) { + error $msgTable[1785] . "\n"; + } elsif ($args->{reason} == 2) { + error $msgTable[1799] . "\n"; + } elsif ($args->{reason} == 3) { + error $msgTable[1801] . "\n"; + } elsif ($args->{reason} == 4) { + error $msgTable[1798] . "\n"; + } else { + error "Unknown reason\n"; + } +} + +sub search_store_result { + my ($self, $args) = @_; + my $step = (length($args->{storeInfo}) % 114 == 0) ? 114 : 131; + my $unpackString = "a4 a4 a80 v C V v C a16" . (($step == 114) ? "" : " a17"); + + @{$universalCatalog{list}} = () if $args->{first_page}; + $universalCatalog{has_next} = $args->{has_next}; + + my @universalCatalogPage; + + for (my $i = 0; $i < length($args->{storeInfo}); $i += $step) { + my ($storeID, $accountID, $shopName, $nameID, $itemType, $price, $amount, $refine, $cards, $unknown) = unpack($unpackString, substr($args->{storeInfo}, $i, $step)); + + my @cards = unpack "v4", $cards; + + my $universalCatalogInfo = { + storeID => $storeID, + accountID => $accountID, + shopName => $shopName, + nameID => $nameID, + itemType => $itemType, + price => $price, + amount => $amount, + refine => $refine, + cards_nameID => $cards, + cards => \@cards, + unknown => $unknown + }; + + push(@universalCatalogPage, $universalCatalogInfo); + Plugins::callHook('search_store', $universalCatalogInfo); + } + + return unless scalar @universalCatalogPage; + + push(@{$universalCatalog{list}}, \@universalCatalogPage); + Misc::searchStoreInfo(scalar(@{$universalCatalog{list}}) - 1); +} + +sub search_store_pos { + my ($self, $args) = @_; + + message TF("Selected store is at (%d, %d)\n", $args->{x}, $args->{y}); +} + +sub skill_msg { + my ($self, $args) = @_; + if ($msgTable[++$args->{msgid}]) { # show message from msgstringtable.txt -> [<Skill_Name>] <Message> + my $skill = new Skill(idn => $args->{id}); + message "[".$skill->getName."] $msgTable[$args->{msgid}]\n", "info"; + } else { + warning TF("Unknown skill_msg msgid:%d skill:%d. Need to update the file msgstringtable.txt (from data.grf)\n", $args->{msgid}, $args->{id}); + } +} + +# Display msgstringtable.txt string and fill in a valid for %d format (ZC_MSG_VALUE). +# 07E2 <message>.W <value>.L +# Displays msgstringtable.txt string in a color. (ZC_MSG_COLOR). +# 09CD <msg id>.W <color>.L +# Displays a format string from msgstringtable.txt with a %s value and color (ZC_FORMATSTRING_MSG). +# 0A6F +sub message_string { + my ($self, $args) = @_; + + my $index = ++$args->{index}; + my $param = bytesToString($args->{param}) if $args->{param}; + + if ($msgTable[$index]) { # show message from msgstringtable.txt + if ($param && ($args->{switch} eq '07E2' || $args->{switch} eq '0A6F') ) { + warning sprintf($msgTable[$index], $param)."\n"; + } else { + warning "$msgTable[$index]\n"; + } + } else { + warning TF("Unknown message_string: %s param: %s. Need to update the file msgstringtable.txt (from data.grf)\n", $index, $param); + } + + $self->mercenary_off() if ($index >= 1267 && $index <= 1270); + + Plugins::callHook('packet_message_string', { + index => $index, + val => $param + }); +} + +# TODO: move @skillsID to Actor, per-actor {skills}, Skill::DynamicInfo +sub skills_list { + my ($self, $args) = @_; + + return unless changeToInGameState(); + + my $msg = $args->{RAW_MSG}; + + my $skill_info; + + if ($args->{switch} eq '0B32') { + $skill_info = { + len => 15, + types => 'v V v3 C v', + keys => [qw(ID targetType lv sp range up lv2)], + }; + } else { + $skill_info = { + len => 37, + types => 'v1 V1 v3 Z24 C1', + keys => [qw(ID targetType lv sp range handle up)], + }; + } + + # TODO: per-actor, if needed at all + # Skill::DynamicInfo::clear; + my ($ownerType, $hook, $actor) = @{{ + '010F' => [Skill::OWNER_CHAR, 'packet_charSkills', $char], + '0235' => [Skill::OWNER_HOMUN, 'packet_homunSkills', $char->{homunculus}], + '029D' => [Skill::OWNER_MERC, 'packet_mercSkills', $char->{mercenary}], + '0B32' => [Skill::OWNER_CHAR, 'packet_charSkills', $char], + }->{$args->{switch}}}; + + my $skillsIDref; + if ($ownerType == Skill::OWNER_CHAR) { + $skillsIDref = \@skillsID; + delete @{$char->{skills}}{@$skillsIDref}; + } elsif ($ownerType == Skill::OWNER_HOMUN) { + $skillsIDref = \@{$char->{homunculus}->{slave_skillsID}}; + delete @{$char->{homunculus}->{skills}}{@$skillsIDref}; + } elsif ($ownerType == Skill::OWNER_MERC) { + $skillsIDref = \@{$char->{mercenary}->{slave_skillsID}}; + delete @{$char->{mercenary}->{skills}}{@$skillsIDref}; + } + @$skillsIDref = (); + + for (my $i = 4; $i < $args->{RAW_MSG_SIZE}; $i += $skill_info->{len}) { + my $skill; + @{$skill}{@{$skill_info->{keys}}} = unpack($skill_info->{types}, substr($msg, $i, $skill_info->{len})); + + my $handle = Skill->new(idn => $skill->{ID})->getHandle; + + foreach(@{$skill_info->{keys}}) { + $actor->{skills}{$handle}{$_} = $skill->{$_}; + } + + binAdd($skillsIDref, $handle) unless defined binFind($skillsIDref, $handle); + Skill::DynamicInfo::add($skill->{ID}, $handle, $skill->{lv}, $skill->{sp}, $skill->{range}, $skill->{targetType}, $ownerType); + + Plugins::callHook($hook, { + ID => $skill->{ID}, + handle => $handle, + level => $skill->{lv}, + upgradable => $skill->{up}, + level2 => $skill->{lv2}, + }); + } +} + +# TODO: use $args->{type} if present +sub skill_update { + my ($self, $args) = @_; + + my ($ID, $lv, $sp, $range, $up) = ($args->{skillID}, $args->{lv}, $args->{sp}, $args->{range}, $args->{up}); + + my $skill = new Skill(idn => $ID); + my $handle = $skill->getHandle(); + my $name = $skill->getName(); + $char->{skills}{$handle}{lv} = $lv; + $char->{skills}{$handle}{sp} = $sp; + $char->{skills}{$handle}{range} = $range; + $char->{skills}{$handle}{up} = $up; + + Skill::DynamicInfo::add($ID, $handle, $lv, $sp, $range, $skill->getTargetType(), Skill::OWNER_CHAR); + + Plugins::callHook('packet_charSkills', { + ID => $ID, + handle => $handle, + level => $lv, + upgradable => $up, + level2 => $args->{lv2}, + }); + + debug "Skill $name: $lv\n", "parseMsg"; +} + +#TODO ! +sub overweight_percent { + my ($self, $args) = @_; + debug "Received overweight percent: $args->{percent}\n"; +} + +sub partylv_info { + my ($self, $args) = @_; + my $ID = $args->{ID}; + if ($char->{party}{users}{$ID}) { + $char->{party}{users}{$ID}{job} = $args->{job}; + $char->{party}{users}{$ID}{lv} = $args->{lv}; + } +} + +sub achievement_reward_ack { + my ($self, $args) = @_; + message TF("Received reward for achievement '%s' (%s).\n", ($achievements{$args->{achievementID}}) ? $achievements{$args->{achievementID}}->{title} : "", $args->{achievementID}), "info"; +} + +sub achievement_update { + my ($self, $args) = @_; + + my $achieve; + @{$achieve}{qw(achievementID completed objective1 objective2 objective3 objective4 objective5 objective6 objective7 objective8 objective9 objective10 completed_at reward)} = @{$args}{qw(achievementID completed objective1 objective2 objective3 objective4 objective5 objective6 objective7 objective8 objective9 objective10 completed_at reward)}; + + $achievementList->{$achieve->{achievementID}} = $achieve; + message TF("Achievement '%s' (%s) added or updated.\n", ($achievements{$achieve->{achievementID}}) ? $achievements{$achieve->{achievementID}}->{title} : "", $achieve->{achievementID}), "info"; +} + +sub achievement_list { + my ($self, $args) = @_; + + $achievementList = {}; + + my $msg = $args->{RAW_MSG}; + my $msg_size = $args->{RAW_MSG_SIZE}; + my $headerlen = 22; + my $achieve_pack = 'V C V10 V C'; + my $achieve_len = length pack $achieve_pack; + + for (my $i = $headerlen; $i < $args->{RAW_MSG_SIZE}; $i+=$achieve_len) { + my $achieve; + + ($achieve->{achievementID}, + $achieve->{completed}, + $achieve->{objective1}, + $achieve->{objective2}, + $achieve->{objective3}, + $achieve->{objective4}, + $achieve->{objective5}, + $achieve->{objective6}, + $achieve->{objective7}, + $achieve->{objective8}, + $achieve->{objective9}, + $achieve->{objective10}, + $achieve->{completed_at}, + $achieve->{reward}) = unpack($achieve_pack, substr($msg, $i, $achieve_len)); + + $achievementList->{$achieve->{achievementID}} = $achieve; + message TF("Achievement '%s' (%s) added.\n", ($achievements{$achieve->{achievementID}}) ? $achievements{$achieve->{achievementID}}->{title} : "",$achieve->{achievementID}), "info"; + } +} + +# Notification about the result of a disconnect request (ZC_ACK_REQ_DISCONNECT). +# 018B <result>.W +# result: +# 0 = disconnect (quit) +# 1 = cannot disconnect (wait 10 seconds) +# ? = ignored +sub quit_response { + my ($self, $args) = @_; + if ($args->{fail}) { # NOTDISCONNECTABLE_STATE = 0x1 + error T("Please wait 10 seconds before trying to log out.\n"); # MSI_CANT_EXIT_NOW = 0x1f6 + } else { # DISCONNECTABLE_STATE = 0x0 + message T("Logged out from the server succesfully.\n"), "success"; + } +} + +sub private_airship_type { + my ($self, $args) = @_; + my $result = $args->{type}; + if (!defined $result) { + warning T("Received Private Airship response without a result code.\n"); + return; + } + my $item_id = $char->{last_private_airship_item}; + my $item_name = defined $item_id ? itemNameSimple($item_id) : itemNameSimple(25464); + if ($result == 0) { + message T("Use Private Airship success.\n"), "info"; + } elsif ($result == 1) { + error T("Please try PivateAirship again.\n"); + } elsif ($result == 2) { + error TF("You do not have enough %s to use Private Airship.\n", $item_name); + } elsif ($result == 3) { + error T("Destination map is invalid.\n"); + } elsif ($result == 4) { + error T("Source map is invalid.\n"); + } elsif ($result == 5) { + my $required_item = itemNameSimple(25464); + error TF("%s cannot be used for Private Airship. Please use %s.\n", $item_name, $required_item); + } else { + warning TF("Unknown Private Airship response %d.\n", $result); + } +} + +# 00CB +sub sell_result { + my ($self, $args) = @_; + if ($args->{fail}) { + error T("Sell failed.\n"); + } else { + message TF("Sold %s items.\n", @sellList.""), "success"; + message T("Sell completed.\n"), "success"; + } + @sellList = (); + if (AI::is("sellAuto")) { + AI::args->{recv_sell_packet} = 1; + } +} + +sub GM_req_acc_name { + my ($self, $args) = @_; + message TF("The accountName for ID %s is %s.\n", $args->{targetID}, $args->{accountName}), "info"; +} + +# 0293 +sub boss_map_info { + my ($self, $args) = @_; + my $bossName = bytesToString($args->{name}); + + if ($args->{flag} == 0) { + message T("You cannot find any trace of a Boss Monster in this area.\n"), "info"; + } elsif ($args->{flag} == 1) { + message TF("MVP Boss %s is now on location: (%d, %d)\n", $bossName, $args->{x}, $args->{y}), "info"; + } elsif ($args->{flag} == 2) { + message TF("MVP Boss %s has been detected on this map!\n", $bossName), "info"; + } elsif ($args->{flag} == 3) { + message TF("MVP Boss %s is dead, but will spawn again in %d hour(s) and %d minutes(s).\n", $bossName, $args->{hours}, $args->{minutes}), "info"; + } else { + debug $self->{packet_list}{$args->{switch}}->[0] . " " . join(', ', @{$args}{@{$self->{packet_list}{$args->{switch}}->[2]}}) . "\n"; + warning TF("Unknown results in %s (flag: %s)\n", $self->{packet_list}{$args->{switch}}->[0], $args->{flag}); + } +} + +sub adopt_reply { + my ($self, $args) = @_; + if ($args->{type} == 0) { + message T("You cannot adopt more than 1 child.\n"), "info"; + } elsif ($args->{type} == 1) { + message T("You must be at least character level 70 in order to adopt someone.\n"), "info"; + } elsif ($args->{type} == 2) { + message T("You cannot adopt a married person.\n"), "info"; + } +} + +sub GM_silence { + my ($self, $args) = @_; + if ($args->{flag}) { + message TF("You have been: muted by %s.\n", bytesToString($args->{name})), "info"; + } else { + message TF("You have been: unmuted by %s.\n", bytesToString($args->{name})), "info"; + } +} + +sub guild_storage_log { + my ($self, $args) = @_; + + if ($args->{result} == 0 || $args->{result} == 1) { + my %action = ( + 0 => T('Get'), + 1 => T('Put'), + ); + + my $storage_info = { + len => 83, + types => 'a4 v V C V a8 C v a8 Z24 Z24 C', + keys => [qw(ID nameID amount action upgrade uniqueID identified type_equip cards charName time attribute)], + }; + + my $message = center(T("[ Guild Storage LOG ]"), 80, '-') ."\n". + T("# Name Item-Name Amount Action Time\n"); + + my $index = 0; + for (my $i = 0; $i < length($args->{log}); $i+= $storage_info->{len}) { + my $item; + @{$item}{@{$storage_info->{keys}}} = unpack($storage_info->{types}, substr($args->{log}, $i, $storage_info->{len})); + $item->{charName} = bytesToString($item->{charName}); + $item->{time} = bytesToString($item->{time}); + $message .= swrite(sprintf("\@%s \@%s \@%s \@%s \@%s \@%s", ('<'x2), ('<'x24), ('<'x48), ('<'x6), ('<'x7), ('<'x20)), [$index, $item->{charName}, itemName($item), $item->{amount}, $action{$item->{action}}, $item->{time}]); + $index++; + } + + $message .= sprintf("%s\n", ('-'x80)); + message($message, "list"); + + } elsif ($args->{result} == 2) { + message TF("Guild Storage empty.\n"), "info"; + } elsif ($args->{result} == 3) { + message TF("You are not currently using Guild Storage. Please try later.\n"), "info"; + } +} + +sub skill_delete { + my ( $self, $args ) = @_; + my $skill = new Skill( idn => $args->{skillID} ); + return if !$skill; + return if !$char->{skills}->{ $skill->getHandle }; + + message TF( "Lost skill: %s\n", $skill->getName ), 'skill'; + delete $char->{skills}->{ $skill->getHandle }; + binRemove( \@skillsID, $skill->getHandle ); +} + +# captcha packets from kRO::RagexeRE_2009_09_22a + +# 07E6? +sub captcha_session_ID { + my ($self, $args) = @_; + debug $self->{packet_list}{$args->{switch}}->[0] . " " . join(', ', @{$args}{@{$self->{packet_list}{$args->{switch}}->[2]}}) . "\n"; +} + +# 0x07e8,-1 +# todo: debug + remove debug message +sub captcha_image { + my ($self, $args) = @_; + debug $self->{packet_list}{$args->{switch}}->[0] . " " . join(', ', @{$args}{@{$self->{packet_list}{$args->{switch}}->[2]}}) . "\n"; + + my $hookArgs = {image => $args->{image}}; + Plugins::callHook ('captcha_image', $hookArgs); + return 1 if $hookArgs->{return}; + + my $file = $Settings::logs_folder . "/captcha.bmp"; + open my $DUMP, '>', $file; + print $DUMP $args->{image}; + close $DUMP; + + $hookArgs = {file => $file}; + Plugins::callHook ('captcha_file', $hookArgs); + return 1 if $hookArgs->{return}; + + warning "captcha.bmp has been saved to: " . $Settings::logs_folder . ", open it, solve it and use the command: captcha <text>\n"; +} + +# 0x07e9,5 +# todo: debug + remove debug message +sub captcha_answer { + my ($self, $args) = @_; + debug $self->{packet_list}{$args->{switch}}->[0] . " " . join(', ', @{$args}{@{$self->{packet_list}{$args->{switch}}->[2]}}) . "\n"; + debug ($args->{flag} ? "good" : "bad") . " answer\n"; + $captcha_state = $args->{flag}; + + Plugins::callHook ('captcha_answer', {flag => $args->{flag}}); +} + +sub open_buying_store { + my($self, $args) = @_; + my $amount = $args->{amount}; + message TF("Your buying store can buy %d items \n", $amount); +} + +# TODO +sub buyer_items +{ + my($self, $args) = @_; + + my $BinaryID = $args->{venderID}; + my $Player = Actor::get($BinaryID); + my $Name = $Player->name; + + my $headerlen = 12; + my $Total = unpack('V4', substr($args->{msg}, $headerlen, 4)); + $headerlen += 4; + + for (my $i = $headerlen; $i < $args->{msg_size}; $i+=9) + { + my $Item = {}; + + ($Item->{price}, + $Item->{amount}, + undef, + $Item->{nameID}) = unpack('V v C v', substr($args->{msg}, $i, 9)); + } +} + +sub open_buying_store_item_list { + my ($self, $args) = @_; + + my $msg = $args->{RAW_MSG}; + my $msg_size = $args->{RAW_MSG_SIZE}; + my $headerlen = 12; + my $unpack = $self->{open_buying_store_items_list_pack} || 'V v C v'; + my $len = length pack $unpack; + + undef @selfBuyerItemList; + + #started a shop. + message TF("Buying Shop opened!\n"), "BuyShop"; +# what is: +# @articles = (); +# $articles = 0; + my $index = 0; + + for (my $i = $headerlen; $i < $msg_size; $i += $len) { + my $item = {}; + + ($item->{price}, + $item->{amount}, + $item->{type}, + $item->{nameID}) = unpack($unpack, substr($msg, $i, $len)); + + $item->{name} = itemName($item); + $selfBuyerItemList[$index] = $item; + + Plugins::callHook('packet_open_buying_store', { + name => $item->{name}, + amount => $item->{amount}, + price => $item->{price}, + type => $item->{type} + }); + + $index++; + } + Commands::run('bs'); +} + +sub buying_store_found { + my ($self, $args) = @_; + my $ID = $args->{ID}; + + if (!$buyerLists{$ID} || !%{$buyerLists{$ID}}) { + binAdd(\@buyerListsID, $ID); + Plugins::callHook('packet_buying', {ID => $ID}); + } + $buyerLists{$ID}{title} = bytesToString($args->{title}); + $buyerLists{$ID}{id} = $ID; +} + +sub buying_store_lost { + my ($self, $args) = @_; + + my $ID = $args->{ID}; + binRemove(\@buyerListsID, $ID); + delete $buyerLists{$ID}; +} + +sub buying_store_items_list { + my($self, $args) = @_; + + undef $buyerPriceLimit; + undef $buyerID; + undef $buyingStoreID; + + $buyerItemList->clear; + + $buyerPriceLimit = $args->{zeny}; + $buyerID = $args->{buyerID}; + $buyingStoreID = $args->{buyingStoreID}; + + my $expireDate = 0; + my $player = Actor::get($buyerID); + my $index = 0; + my $pack = $self->{buying_store_items_list_pack} || 'V v C v'; + my $item_len = length pack $pack; + my $item_list_len = length $args->{itemList}; + + my $msg = center(T(" Buyer: ") . $player->nameIdx . ' ', 83, '-') ."\n". + T("# Name Type Price Amount\n"); + + for (my $i = 0; $i < $item_list_len; $i+=$item_len) { + my $item = Actor::Item->new; + + ($item->{price}, + $item->{amount}, + $item->{type}, + $item->{nameID}) = unpack($pack, substr($args->{itemList}, $i, $item_len)); + + $item->{name} = itemName($item); + $item->{ID} = $i; + + $buyerItemList->add($item); + + debug "Item added to Buying Store: $item->{name} - $item->{price} z\n", "buying_store", 2; + + Plugins::callHook('packet_buying_store', { + buyerID => $buyerID, + number => $index, + name => $item->{name}, + amount => $item->{amount}, + price => $item->{price}, + type => $item->{type} + }); + + $msg .= swrite( + "@< @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< @<<<<<<<<<<<<<< @>>>>>>>>>>>>z @<<<<<", + [$index, $item->{name}, $itemTypes_lut{$item->{type}}, formatNumber($item->{price}), formatNumber($item->{amount})]); + + $index++; + } + + $msg .= "\n" . TF("Price limit: %s Zeny\n", formatNumber($buyerPriceLimit)) . ('-'x83) . "\n"; + message $msg, "list"; + + if ($args->{expireDate}) { + $expireDate = $args->{expireDate}; + my $date = int(time) + int($args->{expireDate}/1000); + message "Expire Date: ".getFormattedDate($date)."\n"; + } + + Plugins::callHook('packet_buying_store2', { + buyerID => $buyerID, + buyingStoreID => $buyingStoreID, + itemList => $buyerItemList, + expireDate => $expireDate, + }); +} + +sub buying_store_item_delete { + my($self, $args) = @_; + return unless changeToInGameState(); + my $item = $char->inventory->getByID($args->{ID}); + my $zeny = $args->{amount} * $args->{zeny}; + if ($item) { + inventoryItemRemoved($item->{binID}, $args->{amount}); + } + message TF("You have sold %s. Amount: %s. Total zeny: %sz\n", $item, $args->{amount}, $zeny);# msgstring 1747 +} + +sub buying_store_fail { + my ($self, $args) = @_; + if ($args->{result} == 5) { + error T("The deal has failed.\n");# msgstring 58 + } elsif ($args->{result} == 6) { + error TF("%s item could not be sold because you do not have the wanted amount of items.\n", itemNameSimple($args->{itemID}));# msgstring 1748 + } elsif ($args->{result} == 7) { + error T("Failed to deal because you have not enough Zeny.\n");# msgstring 1746 + } else { + error TF("Unknown 'buying_store_fail' result: %s.\n", $args->{result}); + } +} + +sub buying_store_update { + my($self, $args) = @_; + if (@selfBuyerItemList) { + for(my $i = 0; $i < @selfBuyerItemList; $i++) { + my $item = $selfBuyerItemList[$i]; + if ($item->{nameID} == $args->{itemID}) { + message TF("You bought %s %s\n", $args->{count}, $item->{name}); + $selfBuyerItemList[$i]->{amount} = $item->{amount} - $args->{count}; + } + } + } +} + +sub buyer_found { + my($self, $args) = @_; + my $ID = $args->{ID}; + + if (!$buyerLists{$ID} || !%{$buyerLists{$ID}}) { + binAdd(\@buyerListsID, $ID); + Plugins::callHook('packet_buyer', {ID => $ID}); + } + $buyerLists{$ID}{title} = bytesToString($args->{title}); + $buyerLists{$ID}{id} = $ID; +} + +sub buyer_lost { + my ($self, $args) = @_; + + my $ID = $args->{ID}; + binRemove(\@buyerListsID, $ID); + delete $buyerLists{$ID}; +} + +sub buying_buy_fail { + my ($self, $args) = @_; + if ($args->{result} == 3) { + error T("Failed to buying (insufficient zeny).\n"); + } elsif ($args->{result} == 4) { + $buyershopstarted = 0; + Plugins::callHook('buyer_shop_closed'); + message T("Buying up complete.\n"); + } else { + error TF("Failed to buying (unknown error: %s).\n", $args->{result}); + } +} + +use constant { + TYPE_BOXITEM => 0x0, + TYPE_MONSTER_ITEM => 0x1, +}; + +sub special_item_obtain { + my ($self, $args) = @_; + + my $item_name = itemNameSimple($args->{nameID}); + my $holder = bytesToString($args->{holder}); + my ($source_item_id, $source_name, $msg); + + stripLanguageCode(\$holder); + if ($args->{type} == TYPE_BOXITEM) { + my $c = unpack 'c', $args->{etc}; + my $unpack = ($c == 2) ? 'c/v' : 'c/V'; + @{$args}{qw(box_nameID)} = unpack $unpack, $args->{etc}; + + my $box_item_name = itemNameSimple($args->{box_nameID}); + $source_name = $box_item_name; + $source_item_id = $args->{box_nameID}; + + if ($msgTable[1629]) { + $msg = sprintf($msgTable[1629], $holder, $box_item_name, $item_name)."\n"; + } else { + $msg = TF("%s has got %s from %s.\n", $holder, $item_name, $box_item_name); + } + + chatLog("GM", $msg) if ($config{logSystemChat}); + message $msg, 'schat'; + + } elsif ($args->{type} == TYPE_MONSTER_ITEM) { + @{$args}{qw(len monster_name)} = unpack 'c Z*', $args->{etc}; + my $monster_name = bytesToString($args->{monster_name}); + $source_name = $monster_name; + stripLanguageCode(\$monster_name); + chatLog("GM", "$holder has got $item_name from $monster_name\n") if ($config{logSystemChat}); + $msg = TF("%s has got %s from %s.\n", $holder, $item_name, $monster_name); + message $msg, 'schat'; + + } else { + $msg = TF("%s has got %s (from Unknown type %d).\n", $holder, $item_name, $args->{type}); + warning $msg, 'schat'; + } + + Plugins::callHook('packet_special_item_obtain', { + ObtainType => $args->{type}, + ItemName => $item_name, + ItemID => $args->{nameID}, + Holder => $holder, + SourceItemID => $source_item_id, # ItemID if type (0) TYPE_BOXITEM + SourceName => $source_name, # Monster if type (1) TYPE_MONSTER_ITEM + Msg => $msg, + }); +} + +sub inventory_item_favorite { + my ($self, $args) = @_; + my $item = $char->inventory->getByID($args->{ID}); + if ($args->{flag}) { + message TF("Inventory Item removed from favorite tab: %s\n", $item), "storage"; + } else { + message TF("Inventory Item move to favorite tab: %s\n", $item), "storage"; + } +} + +sub private_message_sent { + my ($self, $args) = @_; + if ($args->{type} == 0) { + message TF("(To %s) : %s\n", $lastpm[0]{'user'}, $lastpm[0]{'msg'}), "pm/sent"; + chatLog("pm", "(To: $lastpm[0]{user}) : $lastpm[0]{msg}\n") if ($config{'logPrivateChat'}); + + Plugins::callHook('packet_sentPM', { + to => $lastpm[0]{user}, + msg => $lastpm[0]{msg} + }); + + } elsif ($args->{type} == 1) { + warning TF("%s is not online\n", $lastpm[0]{user}); + } elsif ($args->{type} == 2) { + warning TF("Player %s ignored your message\n", $lastpm[0]{user}); + } else { + warning TF("Player %s doesn't want to receive messages\n", $lastpm[0]{user}); + } + shift @lastpm; +} + +sub vender_buy_fail { + my ($self, $args) = @_; + + if ($args->{fail} == 1) { + error TF("Failed to buy %s of item #%s from vender (insufficient zeny) (error code %s).\n", $args->{amount}, $args->{ID}, $args->{fail}); + } elsif ($args->{fail} == 2) { + error TF("Failed to buy %s of item #%s from vender (overweight) (error code %s).\n", $args->{amount}, $args->{ID}, $args->{fail}); + } elsif ($args->{fail} == 4) { + error TF("Failed to buy %s of item #%s from vender (requested to purchase more than vender had in stock) (error code %s).\n", $args->{amount}, $args->{ID}, $args->{fail}); + } elsif ($args->{fail} == 6) { + error TF("Failed to buy %s of item #%s from vender (vender refreshed shop before purchase request) (error code %s).\n", $args->{amount}, $args->{ID}, $args->{fail}); + } elsif ($args->{fail} == 8) { + error TF("Failed to buy %s of item #%s from vender (vender would go over max zeny with the purchase) (error code %s).\n", $args->{amount}, $args->{ID}, $args->{fail}); + } else { + error TF("Failed to buy %s of item #%s from vender (unknown error code %s).\n", $args->{amount}, $args->{ID}, $args->{fail}); + } +} + +# Receive list of items from cash shop NPC +# +# ['cash_dealer', 'v V a*', [qw(len cash_points item_list)]] +# ['cash_dealer', 'v V2 a*', [qw(len cash_points kafra_points item_list)]] +sub cash_dealer { + my ($self, $args) = @_; + + undef %talk; + $ai_v{'npc_talk'}{talk} = 'cash'; + # continue talk sequence now + $ai_v{'npc_talk'}{time} = time; + + # Parse item_list => ['V2 C v', [qw(price price_discount type nameid)]] + $cashList->clear; + @{$args->{items}} = map { my %item; @item{qw(price price_discount type nameid)} = unpack 'V2 C v', $_; \%item } unpack '(a11)*', $args->{item_list}; + + # Just keep cash_points and kafra_points locally not as $char->{cashpoint}, $cashShop{points}->{cash}, $cashShop{points}->{kafra} + # private servers can add custom currency that may overwrite the cash & points from cash shop + message TF("------------CashList (Cash Point: %-5d. Kafra Points: %-d)-------------\n" . + "# Name Type Price\n", $args->{cash_points}, $args->{kafra_points}), "list"; + + foreach my $curr_item (@{$args->{items}}) { + my $item = Actor::Item->new; + + @$item{qw(price type nameID)} = ($curr_item->{price}, $curr_item->{type}, $curr_item->{nameid}); + $item->{ID} = $cashList->size; + $item->{name} = itemName($item); + $cashList->add($item); + + debug "Item added to Store: $item->{name} - $item->{price}z\n", "parseMsg", 2; + message(swrite( + "@<<< @<<<<<<<<<<<<<<<<<<<<<< @<<<<<<<<<<<<< @>>>>>>>p", + [$item->{ID}, $item->{name}, $itemTypes_lut{$item->{type}}, $curr_item->{price_discount}]), + "list"); + } + message("-----------------------------------------------------\n", "list"); +} + +## +# 096D <size>.W { <index>.W }* +# @author [Cydh] +## +sub merge_item_open { + my ($self, $args) = @_; + $mergeItemList = {}; + debug "Enable to merge ".(scalar @{$args->{list}})." items\n"; + # Grouping items by ItemID, easier to merge by user later + foreach (@{$args->{list}}) { + my $item = $char->inventory->getByID($_->{ID}); + if (!defined $mergeItemList->{$item->{nameID}}) { + $mergeItemList->{$item->{nameID}}->{name} = $item->{name}; + @{$mergeItemList->{$item->{nameID}}->{list}} = (); + } + push @{$mergeItemList->{$item->{nameID}}->{list}},{ ID => $_->{ID}, info => $item }; + debug "- ".(unpack "v",$_->{ID}).": ".$item->{name}." (".$item->{binID}.") x ".$item->{amount}."\n"; + } + message TF("Received %d items that can be merged. Use 'merge' to continue\n", (scalar @{$args->{list}})), "info"; +} + +sub parse_merge_item_open { + my ($self, $args) = @_; + @{$args->{list}} = map { { ID => $_ } } unpack '(a2)*', $args->{itemList}; # received index from server is +2 +} + +## +# 096F <index>.W <total>.W <result>.B +# @author [Cydh] +## +sub merge_item_result { + my ($self, $args) = @_; + if ($args->{result} == 0) { + # now update inventory data + my $item = $char->inventory->getByID($args->{itemIndex}); + message T("Items were merged successfully!\n"), "info"; + if ($item) { + my $oldAmount = $item->{amount}; + $item->{amount} = $args->{total}; + message TF("Updated amount of item %s (%d): %d -> %d\n", $item->{name}, $item->{binID}, $oldAmount, $item->{amount}); + } else { + error TF("Item was moved during merging process. itemIndex: %d. New amount: %d\n", $args->{index}, $args->{total}); + } + } elsif ($args->{result} == 1) { + error T("Items cannot be merged.\n"); + } elsif ($args->{result} == 2) { + error T("The amount of merged item will be exceed stack limit.\n"); + } else { + error TF("An error occured to merge item. Error:%d\n", $args->{result}); + } + debug "Merge item result: itemIndex:$args->{index} total:$args->{total} result:$args->{result}\n"; +} + +sub parse_merge_item_result { + my ($self, $args) = @_; + $args->{index} = (unpack "(a2)", $args->{itemIndex})-2; +} + +sub memo_success { + my ($self, $args) = @_; + if ($args->{fail}) { + warning T("Memo Failed\n"); + Plugins::callHook('memo_fail', {field => $field->baseName}); + } else { + message T("Memo Succeeded\n"), "success"; + Plugins::callHook('memo_success', {field => $field->baseName}); + } +} + +sub change_to_constate25 { + $net->setState(2.5); + undef $accountID; +} + +# TODO do something with sourceID, targetID? -> tech: maybe your spouses adopt_request will also display this message for you. +sub adopt_request { + my ($self, $args) = @_; + message TF("%s wishes to adopt you. Do you accept?\n", $args->{name}), "info"; +} + +# Updates the fame rank points for the given ranking. +# 097E <RankingType>.W <point>.L <TotalPoint>.L (ZC_UPDATE_RANKING_POINT) +# RankingType: +# 0 = Blacksmith +# 1 = Alchemist +# 2 = Taekwon +sub rank_points { + my ( $self, $args ) = @_; + + $self->blacksmith_points( $args ) if $args->{type} == 0; + $self->alchemist_point( $args ) if $args->{type} == 1; + $self->taekwon_rank( { rank => $args->{total} } ) if $args->{type} == 2; + message "Unknown rank type %s.\n", $args->{type} if $args->{type} > 2; +} + +# Updates the fame rank points for the Blacksmith ranking. +# 021B <points>.L <total points>.L (ZC_BLACKSMITH_POINT) +sub blacksmith_points { + my ($self, $args) = @_; + message TF("[POINT] Blacksmith Ranking Point is increasing by %s. Now, The total is %s points.\n", $args->{points}, $args->{total}, "list"); +} + +# Updates the fame rank points for the Alchemist ranking. +# 021C <points>.L <total points>.L (ZC_ALCHEMIST_POINT) +sub alchemist_point { + my ($self, $args) = @_; + message TF("[POINT] Alchemist Ranking Point is increasing by %s. Now, The total is %s points.\n", $args->{points}, $args->{total}, "list"); +} + +sub area_spell_disappears { + my ($self, $args) = @_; + # The area effect spell with ID dissappears + my $ID = $args->{ID}; + my $spell = $spells{$ID}; + debug "Area effect ".getSpellName($spell->{type})." ($spell->{binID}) from ".getActorName($spell->{sourceID})." disappeared from ($spell->{pos}{x}, $spell->{pos}{y})\n", "skill", 2; + delete $spells{$ID}; + binRemove(\@spellsID, $ID); +} + +sub arrow_none { + my ($self, $args) = @_; + + my $type = $args->{type}; + if ($type == 0) { + delete $char->{'arrow'}; + if ($config{'dcOnEmptyArrow'}) { + error T("Auto disconnecting on EmptyArrow!\n"); + chatLog("k", T("*** Your Arrows is ended, auto disconnect! ***\n")); + $messageSender->sendQuit(); + quit(); + } else { + error T("Please equip arrow first.\n"); + } + } elsif ($type == 1) { + debug "You can't Attack or use Skills because your Weight Limit has been exceeded.\n"; + } elsif ($type == 2) { + debug "You can't use Skills because Weight Limit has been exceeded.\n"; + } elsif ($type == 3) { + debug "Arrow equipped\n"; + } +} + +sub arrowcraft_list { + my ($self, $args) = @_; + + my $msg = $args->{RAW_MSG}; + my $msg_size = $args->{RAW_MSG_SIZE}; + $char->{selected_craft} = 0; + + undef @arrowCraftID; + for (my $i = 4; $i < $msg_size; $i += 2) { + my $ID = unpack("v", substr($msg, $i, 2)); + my $item = $char->inventory->getByNameID($ID); + $char->{last_skill_used} = 2027 if ($config{autoPoison} && $item->{name} eq $config{autoPoison}); + binAdd(\@arrowCraftID, $item->{binID}); + } + + if ($char->{last_skill_used} == 2027) { # GC_POISONINGWEAPON + message T("Received Possible Poison List - type 'poison'\n"); + if ($config{autoPoison}) { + my $item = $char->inventory->getByName($config{autoPoison}); + if ($item) { + $messageSender->sendArrowCraft($item->{nameID}); + $char->{selected_craft} = 1; + } else { + error TF("Configured autoPoison (%s) not available.\n", $config{autoSpell}); + } + } else { + warning T("Configure autoPoison to automatically select skill for Auto Spell.\n"), 'hint'; + } + } else { + message T("Received Possible Item List - type 'arrowcraft' or 'poison'\n"); + } +} + +# Notifies client of a character parameter change. +# 013A <atk range>.W (ZC_ATTACK_RANGE) +sub attack_range { + my ($self, $args) = @_; + + my $type = $args->{type}; + debug "Your attack range is: $type\n"; + return unless changeToInGameState(); + + $char->{attack_range} = $type; + if ($config{attackDistanceAuto}) { + if($config{attackDistance} > $type) { # decrease attack range if necessary + configModify('attackDistance', $type, 1); + message TF("Autodetected attackDistance = %s\n", $config{attackDistance}), "success"; + } + if ($config{attackMaxDistance} != $type) { # set max distance using information coming from the server + configModify('attackMaxDistance', $type, 1) if ($config{attackMaxDistance} != $type); + message TF("Autodetected attackMaxDistance = %s\n", $config{attackMaxDistance}), "success"; + } + } +} + +sub auction_my_sell_stop { + my ($self, $args) = @_; + my $flag = $args->{flag}; + + if ($flag == 0) { + message T("You have ended the auction.\n"), "info"; + } elsif ($flag == 1) { + message T("You cannot end the auction.\n"), "info"; + } elsif ($flag == 2) { + message T("Bid number is incorrect.\n"), "info"; + } else { + warning TF("Unknown results in %s (flag: %s)\n", $self->{packet_list}{$args->{switch}}->[0], $args->{flag}); + } +} + +sub auction_windows { + my ($self, $args) = @_; + if ($args->{flag}) { + message T("Auction window is now closed.\n"), "info"; + } + else { + message T("Auction window is now opened.\n"), "info"; + } +} + +sub auction_add_item { + my ($self, $args) = @_; + if ($args->{fail}) { + message TF("Failed (note: usable items can't be auctioned) to add item with index: %s.\n", $args->{ID}), "info"; + } + else { + message TF("Succeeded to add item with index: %s.\n", $args->{ID}), "info"; + } +} + +sub premium_rates_info { + my ($self, $args) = @_; + message TF("Premium rates: exp %+i%%, death %+i%%, drop %+i%%.\n", $args->{exp}, $args->{death}, $args->{drop}), "info"; +} + +# Transmit personal information to player. (rates) +# 08CB <packet len>.W <exp>.W <death>.W <drop>.W <DETAIL_EXP_INFO>7B (ZC_PERSONAL_INFOMATION) +# <InfoType>.B <Exp>.W <Death>.W <Drop>.W (DETAIL_EXP_INFO 08CB) +# 097B <packet len>.W <exp>.L <death>.L <drop>.L <DETAIL_EXP_INFO>13B (ZC_PERSONAL_INFOMATION2) +# 0981 <packet len>.W <exp>.W <death>.W <drop>.W <activity rate>.W <DETAIL_EXP_INFO>13B (ZC_PERSONAL_INFOMATION_CHN) +# <InfoType>.B <Exp>.L <Death>.L <Drop>.L (DETAIL_EXP_INFO 097B|0981) +sub rates_info2 { + my ($self, $args) = @_; + + my $msg = $args->{RAW_MSG}; + my $msg_size = $args->{RAW_MSG_SIZE}; + my $header_pack = 'v V3'; + my $header_len = ((length pack $header_pack) + 2); + + my $detail_pack = 'C l3'; + my $detail_len = length pack $detail_pack; + + my %rates = ( + exp => { total => $args->{exp}/1000 }, # Value to Percentage => /100 + death => { total => $args->{death}/1000 }, # 1 d.p. => /10 + drop => { total => $args->{drop}/1000 }, + ); + + # get details + for (my $i = $header_len; $i < $args->{RAW_MSG_SIZE}; $i += $detail_len) { + + my ($type, $exp, $death, $drop) = unpack($detail_pack, substr($msg, $i, $detail_len)); + + $rates{exp}{$type} = $exp/1000; + $rates{death}{$type} = $death/1000; + $rates{drop}{$type} = $drop/1000; + } + + # we have 4 kinds of detail: + # $rates{exp or drop or death}{DETAIL_KIND} + # 0 = base server exp (?) + # 1 = premium acc additional exp + # 2 = server additional exp + # 3 = not sure, maybe it's for "extra exp" events? never seen this using the official client (bRO) + message T("=========================== Server Infos ===========================\n"), "info"; + message TF("EXP Rates: %s%% (Base %s%% + Premium %s%% + Server %s%% + Plus %s%%) \n", $rates{exp}{total}, $rates{exp}{0}+100, $rates{exp}{1}, $rates{exp}{2}, $rates{exp}{3}), "info"; + message TF("Drop Rates: %s%% (Base %s%% + Premium %s%% + Server %s%% + Plus %s%%) \n", $rates{drop}{total}, $rates{drop}{0}+100, $rates{drop}{1}, $rates{drop}{2}, $rates{drop}{3}), "info"; + message TF("Death Penalty: %s%% (Base %s%% + Premium %s%% + Server %s%% + Plus %s%%) \n", $rates{death}{total}, $rates{death}{0}+100, $rates{death}{1}, $rates{death}{2}, $rates{death}{3}), "info"; + message "=====================================================================\n", "info"; +} + +sub auction_result { + my ($self, $args) = @_; + my $flag = $args->{flag}; + + if ($flag == 0) { + message T("You have failed to bid into the auction.\n"), "info"; + } elsif ($flag == 1) { + message T("You have successfully bid in the auction.\n"), "info"; + } elsif ($flag == 2) { + message T("The auction has been canceled.\n"), "info"; + } elsif ($flag == 3) { + message T("An auction with at least one bidder cannot be canceled.\n"), "info"; + } elsif ($flag == 4) { + message T("You cannot register more than 5 items in an auction at a time.\n"), "info"; + } elsif ($flag == 5) { + message T("You do not have enough Zeny to pay the Auction Fee.\n"), "info"; + } elsif ($flag == 6) { + message T("You have won the auction.\n"), "info"; + } elsif ($flag == 7) { + message T("You have failed to win the auction.\n"), "info"; + } elsif ($flag == 8) { + message T("You do not have enough Zeny.\n"), "info"; + } elsif ($flag == 9) { + message T("You cannot place more than 5 bids at a time.\n"), "info"; + } else { + warning TF("Unknown results in %s (flag: %s)\n", $self->{packet_list}{$args->{switch}}->[0], $args->{flag}); + } +} + +# 02DC +# TODO +sub battleground_message { + my ($self, $args) = @_; + debug $self->{packet_list}{$args->{switch}}->[0] . " " . join(', ', @{$args}{@{$self->{packet_list}{$args->{switch}}->[2]}}) . "\n"; +} + +# 02DD +# TODO +sub battleground_emblem { + my ($self, $args) = @_; + debug $self->{packet_list}{$args->{switch}}->[0] . " " . join(', ', @{$args}{@{$self->{packet_list}{$args->{switch}}->[2]}}) . "\n"; +} + +# 0152 +# TODO +sub guild_emblem { + my ($self, $args) = @_; + debug $self->{packet_list}{$args->{switch}}->[0] . " " . join(', ', @{$args}{@{$self->{packet_list}{$args->{switch}}->[2]}}) . "\n"; +} + +# 01B4 +# TODO +sub guild_emblem_update { + my ($self, $args) = @_; + debug $self->{packet_list}{$args->{switch}}->[0] . " " . join(', ', @{$args}{@{$self->{packet_list}{$args->{switch}}->[2]}}) . "\n"; +} + +# 0B47 +# TODO +sub char_emblem_update { + my ($self, $args) = @_; + debug $self->{packet_list}{$args->{switch}}->[0] . " " . join(', ', @{$args}{@{$self->{packet_list}{$args->{switch}}->[2]}}) . "\n"; +} + +# 0174 +# TODO +sub guild_position_changed { + my ($self, $args) = @_; + debug $self->{packet_list}{$args->{switch}}->[0] . " " . join(', ', @{$args}{@{$self->{packet_list}{$args->{switch}}->[2]}}) . "\n"; +} + +# 0AFD +# TODO +sub guild_position { + my ($self, $args) = @_; + debug $self->{packet_list}{$args->{switch}}->[0] . " " . join(', ', @{$args}{@{$self->{packet_list}{$args->{switch}}->[2]}}) . "\n"; +} + +# 0184 +# TODO +sub guild_unally { + my ($self, $args) = @_; + debug $self->{packet_list}{$args->{switch}}->[0] . " " . join(', ', @{$args}{@{$self->{packet_list}{$args->{switch}}->[2]}}) . "\n"; +} + +# 0181 +# TODO +sub guild_opposition_result { + my ($self, $args) = @_; + debug $self->{packet_list}{$args->{switch}}->[0] . " " . join(', ', @{$args}{@{$self->{packet_list}{$args->{switch}}->[2]}}) . "\n"; +} + +# 0185 +# TODO: this packet doesn't exist in eA +sub guild_alliance_added { + my ($self, $args) = @_; + debug $self->{packet_list}{$args->{switch}}->[0] . " " . join(', ', @{$args}{@{$self->{packet_list}{$args->{switch}}->[2]}}) . "\n"; +} + +# 0192 +# TODO: add actual functionality, maybe alter field? +sub map_change_cell { + my ($self, $args) = @_; + debug "Cell on ($args->{x}, $args->{y}) has been changed to $args->{type} on $args->{map_name}\n", "info"; +} + +# 01D1 +# TODO: the actual status is sent to us in opt3 +sub blade_stop { + my ($self, $args) = @_; + if ($args->{active} == 0) { + message TF("Blade Stop by %s on %s is deactivated.\n", Actor::get($args->{sourceID})->nameString(), Actor::get($args->{targetID})->nameString()), "info"; + } elsif ($args->{active} == 1) { + message TF("Blade Stop by %s on %s is active.\n", Actor::get($args->{sourceID})->nameString(), Actor::get($args->{targetID})->nameString()), "info"; + } +} + +sub divorced { + my ($self, $args) = @_; + message TF("%s and %s have divorced from each other.\n", $char->{name}, $args->{name}), "info"; # is it $char->{name} or is this packet also used for other players? +} + +sub hack_shield_alarm { + error T("Error: You have been forced to disconnect by a Hack Shield.\n Please check Poseidon.\n"), "connection"; + Commands::run('relog 100000000'); +} + +sub talkie_box { + my ($self, $args) = @_; + message TF("%s's talkie box message: %s.\n", Actor::get($args->{ID})->nameString(), $args->{message}), "info"; +} + +sub manner_message { + my ($self, $args) = @_; + if ($args->{flag} == 0) { + message T("A manner point has been successfully aligned.\n"), "info"; + } elsif ($args->{flag} == 3) { + message T("Chat Block has been applied by GM due to your ill-mannerous action.\n"), "info"; + } elsif ($args->{flag} == 4) { + message T("Automated Chat Block has been applied due to Anti-Spam System.\n"), "info"; + } elsif ($args->{flag} == 5) { + message T("You got a good point.\n"), "info"; + } else { + warning TF("Unknown results in %s (flag: %s)\n", $self->{packet_list}{$args->{switch}}->[0], $args->{flag}); + } +} + +# 02CB +# TODO +# Required to start the instancing information window on Client +# This window re-appear each "refresh" of client automatically until 02CD is send to client. +sub instance_window_start { + my ($self, $args) = @_; + debug $self->{packet_list}{$args->{switch}}->[0] . " " . join(', ', @{$args}{@{$self->{packet_list}{$args->{switch}}->[2]}}) . "\n"; +} + +# 02CC +# TODO +# To announce Instancing queue creation if no maps available +sub instance_window_queue { + my ($self, $args) = @_; + debug $self->{packet_list}{$args->{switch}}->[0] . " " . join(', ', @{$args}{@{$self->{packet_list}{$args->{switch}}->[2]}}) . "\n"; +} + +# 02CD +# TODO +sub instance_window_join { + my ($self, $args) = @_; + debug $self->{packet_list}{$args->{switch}}->[0] . " " . join(', ', @{$args}{@{$self->{packet_list}{$args->{switch}}->[2]}}) . "\n"; + + Plugins::callHook('instance_ready'); +} + +# 02CE +#0 = "The Memorial Dungeon reservation has been canceled/updated." +# Re-innit Window, in some rare cases. +#1 = "The Memorial Dungeon expired; it has been destroyed." +#2 = "The Memorial Dungeon's entry time limit expired; it has been destroyed." +#3 = "The Memorial Dungeon has been removed." +#4 = "A system error has occurred in the Memorial Dungeon. Please relog in to the game to continue playing." +# Just remove the window, maybe party/guild leave. +# TODO: test if correct message displays, no type == 0 ? +sub instance_window_leave { + my ($self, $args) = @_; + + if ($args->{flag} == 0) { # TYPE_NOTIFY = 0x0; Ihis one will pop up Memory Dungeon Window + debug T("Received Memory Dungeon reservation update\n"); + } elsif ($args->{flag} == 1) { # TYPE_DESTROY_LIVE_TIMEOUT = 0x1 + message T("The Memorial Dungeon expired it has been destroyed.\n"), "info"; + } elsif ($args->{flag} == 2) { # TYPE_DESTROY_ENTER_TIMEOUT = 0x2 + message T("The Memorial Dungeon's entry time limit expired it has been destroyed.\n"), "info"; + } elsif ($args->{flag} == 3) { # TYPE_DESTROY_USER_REQUEST = 0x3 + message T("The Memorial Dungeon has been removed.\n"), "info"; + } elsif ($args->{flag} == 4) { # TYPE_CREATE_FAIL = 0x4 + message T("The instance windows has been removed, possibly due to party/guild leave.\n"), "info"; + } else { + warning TF("Unknown results in %s (flag: %s)\n", $self->{packet_list}{$args->{switch}}->[0], $args->{flag}); + } +} + +sub card_merge_list { + my ($self, $args) = @_; + + # You just requested a list of possible items to merge a card into + # The RO client does this when you double click a card + my $msg = $args->{RAW_MSG}; + my ($len) = unpack("x2 v", $msg); + + my $index; + for (my $i = 4; $i < $len; $i += 2) { + $index = unpack("a2", substr($msg, $i, 2)); + my $item = $char->inventory->getByID($index); + binAdd(\@cardMergeItemsID, $item->{binID}); + } + + Commands::run('card mergelist'); +} + +sub card_merge_status { + my ($self, $args) = @_; + + # something about successful compound? + my $item_index = $args->{item_index}; + my $card_index = $args->{card_index}; + my $fail = $args->{fail}; + + if ($fail) { + message T("Card merging failed\n"); + } else { + my $item = $char->inventory->getByID($item_index); + my $card = $char->inventory->getByID($card_index); + message TF("%s has been successfully merged into %s\n", + $card->{name}, $item->{name}), "success"; + + # Remove one of the card + inventoryItemRemoved($card->{binID}, 1); + + # Rename the slotted item now + # FIXME: this is unoptimized + use bytes; + no encoding 'utf8'; + my $newcards = ''; + my $addedcard; + for (my $i = 0; $i < 4; $i++) { + my $cardData = substr($item->{cards}, $i * 2, 2); + if (unpack("v", $cardData)) { + $newcards .= $cardData; + } elsif (!$addedcard) { + $newcards .= pack("v", $card->{nameID}); + $addedcard = 1; + } else { + $newcards .= pack("v", 0); + } + } + $item->{cards} = $newcards; + $item->setName(itemName($item)); + } + + undef @cardMergeItemsID; + undef $cardMergeIndex; +} + +sub combo_delay { + my ($self, $args) = @_; + + $char->{combo_packet} = ($args->{delay}); #* 15) / 100000; + # How was the above formula derived? I think it's better that the manipulation be + # done in functions.pl (or whatever sub that handles this) instead of here. + + $args->{actor} = Actor::get($args->{ID}); + my $verb = $args->{actor}->verb('have', 'has'); + debug "$args->{actor} $verb combo delay $args->{delay}\n", "parseMsg_comboDelay"; +} + +# 0294 +# TODO -> maybe add table file? +sub book_read { + my ($self, $args) = @_; + debug "Reading book: $args->{bookID} page: $args->{page}\n", "info"; +} + +# TODO can we use itemName($actor)? -> tech: don't think so because it seems that this packet is received before the inventory list +sub rental_time { + my ($self, $args) = @_; + message TF("The '%s' item will disappear in %d minutes.\n", itemNameSimple($args->{nameID}), $args->{seconds}/60), "info"; +} + +# 0289 +# TODO +sub cash_buy_fail { + my ($self, $args) = @_; + debug "cash_buy_fail $args->{cash_points} $args->{kafra_points} $args->{fail}\n"; +} + +# Notifies the client about the result of a request to equip an item. +# 00AA <index>.W <equip location>.W <result>.B (ZC_REQ_WEAR_EQUIP_ACK) +# 00AA <index>.W <equip location>.W <view id>.W <result>.B (PACKETVER >= 20100629) +# 08D0 <index>.W <equip location>.W <view id>.W <result>.B (ZC_REQ_WEAR_EQUIP_ACK2) +# 0999 <index>.W <equip location>.L <view id>.W <result>.B (ZC_ACK_WEAR_EQUIP_V5) +# @ok: //inversed forv2 v5 +# 0 = failure +# 1 = success +# 2 = failure due to low level +sub equip_item { + my ($self, $args) = @_; + my $item = $char->inventory->getByID($args->{ID}); + if ((!$args->{success} && $args->{switch} eq "00AA") || ($args->{success} && $args->{switch} eq "0999")) { + message TF("You can't put on %s (%d)\n", $item->{name}, $item->{binID}); + } else { + $item->{equipped} = $args->{type}; + + if ($args->{type} == 10 || $args->{type} == 32768) { + $char->{equipment}{arrow} = $item; + } else { + foreach (%equipSlot_rlut) { + if ($_ & $args->{type}) { + next if $_ == 10; # work around Arrow bug + next if $_ == 32768; + $char->{equipment}{$equipSlot_lut{$_}} = $item; + Plugins::callHook('equipped_item', {slot => $equipSlot_lut{$_}, item => $item}); + } + } + } + message TF("You equip %s (%d) - %s (type %s)\n", $item->{name}, $item->{binID}, $equipTypes_lut{$item->{type_equip}}, $args->{type}), 'inventory'; + } + $ai_v{temp}{waitForEquip}-- if $ai_v{temp}{waitForEquip}; +} + +# Acknowledgement for adding an equip to the equip switch window +# 0A98 <index>.W <position.>.L <flag>.L <= 20170502 +# 0A98 <index>.W <position.>.L <flag>.W +sub equip_item_switch { + my ($self, $args) = @_; + my $item = $char->inventory->getByID($args->{ID}); + if ( $args->{success} == 1 || $args->{success} == 2 ) { + message TF("[Equip Switch] You can't put on %s (%d)\n", $item->{name}, $item->{binID}); + } else { + $item->{eqswitch} = $args->{type}; + + if ($args->{type} == 10 || $args->{type} == 32768) { + $char->{equipment}{arrow} = $item; + } else { + foreach (%equipSlot_rlut) { + if ($_ & $args->{type}) { + next if $_ == 10; # work around Arrow bug + next if $_ == 32768; + $char->{eqswitch}{$equipSlot_lut{$_}} = $item; + Plugins::callHook('equipped_item_sw', {slot => $equipSlot_lut{$_}, item => $item}); + } + } + } + + message TF("[Equip Switch] You equip %s (%d) - %s (type %s)\n", $item->{name}, $item->{binID}, $equipTypes_lut{$item->{type_equip}}, $args->{type}), 'inventory'; + } + $ai_v{temp}{waitForEquip}-- if $ai_v{temp}{waitForEquip}; +} + + +# Acknowledgement packet for the full equip switch +# 0A9D <failed>.W +sub equip_switch_run_res { + my ($self, $args) = @_; + if ($args->{success}) { + message TF("[Equip Switch] Fail !\n"), "info"; + } else { + message TF("[Equip Switch] Success !\n"), "info"; + } +} + +# Set the full list of items in the equip switch window +# 0A9B <length>.W { <index>.W <position>.L }* +sub equip_switch_log { + my ($self, $args) = @_; + for (my $i = 0; $i < length($args->{log}); $i+= 6) { + my ($index, $position) = unpack('a2 V', substr($args->{log}, $i, 6)); + my $item = $char->inventory->getByID($index); + $char->{eqswitch}{$equipSlot_lut{$position}} = $item; + } +} + +# 02EF +# TODO +sub font { + my ($self, $args) = @_; + debug "Account: $args->{ID} is using fontID: $args->{fontID}\n", "info"; +} + +sub initialize_message_id_encryption { + my ($self, $args) = @_; + if ($masterServer->{messageIDEncryption} ne '0') { + $messageSender->sendMessageIDEncryptionInitialized(); + + my @c; + my $shtmp = $args->{param1}; + for (my $i = 8; $i > 0; $i--) { + $c[$i] = $shtmp & 0x0F; + $shtmp >>= 4; + } + my $w = ($c[6]<<12) + ($c[4]<<8) + ($c[7]<<4) + $c[1]; + $enc_val1 = ($c[2]<<12) + ($c[3]<<8) + ($c[5]<<4) + $c[8]; + $enc_val2 = (((($enc_val1 ^ 0x0000F3AC) + $w) << 16) | (($enc_val1 ^ 0x000049DF) + $w)) ^ $args->{param2}; + } +} + +sub mail_delete { + my ($self, $args) = @_; + if ($args->{fail}) { + message TF("Failed to delete mail with ID: %s.\n", $args->{mailID}), "info"; + } + else { + message TF("Succeeded to delete mail with ID: %s.\n", $args->{mailID}), "info"; + } +} + +sub mail_window { + my ($self, $args) = @_; + if ($args->{flag}) { + message T("Mail window is now closed.\n"), "info"; + } + else { + message T("Mail window is now opened.\n"), "info"; + } +} + +sub mail_return { + my ($self, $args) = @_; + ($args->{fail}) ? + error TF("The mail with ID: %s does not exist.\n", $args->{mailID}), "info" : + message TF("The mail with ID: %s is returned to the sender.\n", $args->{mailID}), "info"; +} + +sub mail_read { + my ($self, $args) = @_; + + my $item = {}; + $item->{nameID} = $args->{nameID}; + $item->{upgrade} = $args->{upgrade}; + $item->{cards} = $args->{cards}; + $item->{broken} = $args->{broken}; + $item->{name} = itemName($item); + + my $msg; + $msg .= center(" " . T("Mail") . " ", 119, '-') . "\n"; + $msg .= swrite(TF("Title: \@%s Sender: \@%s", ('<'x39), ('<'x24)), + [bytesToString($args->{title}), bytesToString($args->{sender})]); + $msg .= TF("Message: %s\n", bytesToString($args->{message})); + $msg .= sprintf("%s\n", ('-'x119)); + $msg .= TF( "Item: %s %s\n" . + "Zeny: %sz\n", + $item->{name}, ($args->{amount}) ? "x " . $args->{amount} : "", formatNumber($args->{zeny})); + $msg .= sprintf("%s\n", ('-'x119)); + + message($msg, "info"); +} + +sub mail_refreshinbox { + my ($self, $args) = @_; + + my $old_count = defined $mailList ? scalar(@$mailList) : 0; + undef $mailList; + my $count = $args->{count}; + + if (!$count) { + message T("There is no mail in your inbox.\n"), "info"; + return; + } + + return if ($old_count == $count); + + message TF("You've got %s mail in your Mailbox.\n", $count), "info"; + my $msg; + $msg .= center(" " . T("Inbox") . " ", 86, '-') . "\n"; + # truncating the title from 39 to 34, the user will be able to read the full title when reading the mail + # truncating the date with precision of minutes and leave year out + + $msg .= swrite(sprintf("\@> \@ \@%s \@%s \@%s", ('<'x34), ('<'x24), ('<'x19)), + ["#", T("R"), T("Title"), T("Sender"), T("Date")]); + $msg .= sprintf("%s\n", ('-'x86)); + + my $j = 0; + for (my $i = 8; $i < 8 + $count * 73; $i+=73) { + ($mailList->[$j]->{mailID}, + $mailList->[$j]->{title}, + $mailList->[$j]->{read}, + $mailList->[$j]->{sender}, + $mailList->[$j]->{timestamp}) = unpack('V Z40 C Z24 V', substr($args->{RAW_MSG}, $i, 73)); + + $mailList->[$j]->{title} = bytesToString($mailList->[$j]->{title}); + $mailList->[$j]->{sender} = bytesToString($mailList->[$j]->{sender}); + + $msg .= swrite(sprintf("\@> \@ \@%s \@%s \@%s", ('<'x34), ('<'x24), ('<'x19)), + [$j, $mailList->[$j]->{read}, $mailList->[$j]->{title}, $mailList->[$j]->{sender}, getFormattedDate(int($mailList->[$j]->{timestamp}))]); + $j++; + } + + $msg .= ("%s\n", ('-'x86)); + message($msg . "\n", "list"); +} + +sub mail_getattachment { + my ($self, $args) = @_; + if (!$args->{fail}) { + message T("Successfully added attachment to inventory.\n"), "info"; + } elsif ($args->{fail} == 2) { + error T("Failed to get the attachment to inventory due to your weight.\n"), "info"; + } else { + error T("Failed to get the attachment to inventory.\n"), "info"; + } +} + +sub mail_setattachment { + my ($self, $args) = @_; + + if ($args->{fail}) { + if (defined $AI::temp::mailAttachAmount) { + undef $AI::temp::mailAttachAmount; + } + message TF("Failed to attach %s.\n", ($args->{ID}) ? T("item: ").$char->inventory->getByID($args->{ID}) : T("zeny")), "info"; + } else { + my $item = $char->inventory->getByID($args->{ID}); + if ($item) { + message TF("Succeeded to attach %s.\n", T("item: ").$char->inventory->getByID($args->{ID})), "info"; + if (defined $AI::temp::mailAttachAmount) { + my $change = min($item->{amount},$AI::temp::mailAttachAmount); + inventoryItemRemoved($item->{binID}, $change); + Plugins::callHook('packet_item_removed', {index => $item->{binID}}); + undef $AI::temp::mailAttachAmount; + } + } else { + message TF("Succeeded to attach %s.\n", T("zeny")), "info"; + if (defined $AI::temp::mailAttachAmount) { + my $change = min($char->{zeny},$AI::temp::mailAttachAmount); + $char->{zeny} = $char->{zeny} - $change; + message TF("You lost %s zeny.\n", formatNumber($change)); + } + } + } +} + +sub mail_send { + my ($self, $args) = @_; + ($args->{fail}) ? + error T("Failed to send mail, the recipient does not exist.\n"), "info" : + message T("Mail sent succesfully.\n"), "info"; +} + +sub mail_new { + my ($self, $args) = @_; + message TF("New mail from sender: %s titled: %s.\n", bytesToString($args->{sender}), bytesToString($args->{title})), "info"; +} + +# Top 10 rank +# 097D <RankingType>.W {<CharName>.24B <point>L}*10 <mypoint>L (ZC_ACK_RANKING) +sub top10 { + my ( $self, $args ) = @_; + + if ( $args->{type} == 0 ) { + $self->top10_blacksmith_rank( { RAW_MSG => substr $args->{RAW_MSG}, 2 } ); + } elsif ( $args->{type} == 1 ) { + $self->top10_alchemist_rank( { RAW_MSG => substr $args->{RAW_MSG}, 2 } ); + } elsif ( $args->{type} == 2 ) { + $self->top10_taekwon_rank( { RAW_MSG => substr $args->{RAW_MSG}, 2 } ); + } elsif ( $args->{type} == 3 ) { + $self->top10_pk_rank( { RAW_MSG => substr $args->{RAW_MSG}, 2 } ); + } else { + message "Unknown top10 type %s.\n", $args->{type}; + } +} + +# Alchemist Top 10 rank +# 021A { <name>.24B }*10 { <point>.L }*10 (ZC_ALCHEMIST_RANK) +sub top10_alchemist_rank { + my ($self, $args) = @_; + + my $textList = bytesToString(top10Listing($args)); + message TF("============= ALCHEMIST RANK ================\n" . + "# Name Points\n". + "%s" . + "=============================================\n", $textList), "list"; +} + +# Blacksmith Top 10 rank +# 0219 { <name>.24B }*10 { <point>.L }*10 (ZC_BLACKSMITH_RANK) +sub top10_blacksmith_rank { + my ($self, $args) = @_; + + my $textList = bytesToString(top10Listing($args)); + message TF("============= BLACKSMITH RANK ===============\n" . + "# Name Points\n". + "%s" . + "=============================================\n", $textList), "list"; +} + +# PK Top 10 rank +# 0238 { <name>.24B }*10 { <point>.L }*10 (ZC_KILLER_RANK) +sub top10_pk_rank { + my ($self, $args) = @_; + + my $textList = bytesToString(top10Listing($args)); + message TF("================ PVP RANK ===================\n" . + "# Name Points\n". + "%s" . + "=============================================\n", $textList), "list"; +} + +# Taekwon Top 10 rank +# 0226 { <name>.24B }*10 { <point>.L }*10 (ZC_TAEKWON_RANK) +sub top10_taekwon_rank { + my ($self, $args) = @_; + + my $textList = bytesToString(top10Listing($args)); + message TF("=============== TAEKWON RANK ================\n" . + "# Name Points\n". + "%s" . + "=============================================\n", $textList), "list"; +} + +# TODO test if we must use ID to know if the packets are meant for us. +# ID is monsterID +sub taekwon_packets { + my ($self, $args) = @_; + my $string = ($args->{value} == 1) ? T("Sun") : ($args->{value} == 2) ? T("Moon") : ($args->{value} == 3) ? T("Stars") : TF("Unknown (%d)", $args->{value}); + if ($args->{flag} == 0) { # Info about Star Gladiator save map: Map registered + message TF("You have now marked: %s as Place of the %s.\n", bytesToString($args->{name}), $string), "info"; + } elsif ($args->{flag} == 1) { # Info about Star Gladiator save map: Information + message TF("%s is marked as Place of the %s.\n", bytesToString($args->{name}), $string), "info"; + } elsif ($args->{flag} == 10) { # Info about Star Gladiator hate mob: Register mob + message TF("You have now marked %s as Target of the %s.\n", bytesToString($args->{name}), $string), "info"; + } elsif ($args->{flag} == 11) { # Info about Star Gladiator hate mob: Information + message TF("%s is marked as Target of the %s.\n", bytesToString($args->{name}), $string); + } elsif ($args->{flag} == 20) { #Info about TaeKwon Do TK_MISSION mob + message TF("[TaeKwon Mission] Target Monster : %s (%d%)"."\n", bytesToString($args->{name}), $args->{value}), "info"; + } elsif ($args->{flag} == 30) { #Feel/Hate reset + message T("Your Hate and Feel targets have been resetted.\n"), "info"; + } else { + warning TF("Unknown results in %s (flag: %s)\n", $self->{packet_list}{$args->{switch}}->[0], $args->{flag}); + } +} + +# Updates the fame rank points for the Taekwon ranking. +# 0224 <points>.L <total points>.L (ZC_TAEKWON_POINT) +sub taekwon_rank { + my ($self, $args) = @_; + message T("TaeKwon Mission Rank : ".$args->{rank}."\n"), "info"; +} + +sub storage_password_request { + my ($self, $args) = @_; + + if ($args->{flag} == 0) { + if ($args->{switch} eq '023E') { + message T("Please enter a new character password:\n"); + } else { + if ($config{storageAuto_password} eq '') { + my $input = $interface->query(T("You've never set a storage password before.\nYou must set a storage password before you can use the storage.\nPlease enter a new storage password:"), isPassword => 1); + if (!defined($input)) { + return; + } + configModify('storageAuto_password', $input, 1); + } + } + + my @key = split /[, ]+/, $masterServer->{storageEncryptKey}; + if (!@key) { + error (($args->{switch} eq '023E') ? + T("Unable to send character password. You must set the 'storageEncryptKey' option in servers.txt.\n") : + T("Unable to send storage password. You must set the 'storageEncryptKey' option in servers.txt.\n")); + return; + } + my $crypton = new Utils::Crypton(pack("V*", @key), 32); + my $num = ($args->{switch} eq '023E') ? $config{charSelect_password} : $config{storageAuto_password}; + $num = sprintf("%d%08d", length($num), $num); + my $ciphertextBlock = $crypton->encrypt(pack("V*", $num, 0, 0, 0)); + message TF("Storage password set to: %s\n", $config{storageAuto_password}), "success"; + $messageSender->sendStoragePassword($ciphertextBlock, 2); + $messageSender->sendStoragePassword($ciphertextBlock, 3); + + } elsif ($args->{flag} == 1) { + if ($args->{switch} eq '023E') { + if ($config{charSelect_password} eq '') { + my $input = $interface->query(T("Please enter your character password."), isPassword => 1); + if (!defined($input)) { + return; + } + configModify('charSelect_password', $input, 1); + message TF("Character password set to: %s\n", $input), "success"; + } + } else { + if ($config{storageAuto_password} eq '') { + my $input = $interface->query(T("Please enter your storage password."), isPassword => 1); + if (!defined($input)) { + return; + } + configModify('storageAuto_password', $input, 1); + message TF("Storage password set to: %s\n", $input), "success"; + } + } + + my @key = split /[, ]+/, $masterServer->{storageEncryptKey}; + if (!@key) { + error (($args->{switch} eq '023E') ? + T("Unable to send character password. You must set the 'storageEncryptKey' option in servers.txt.\n") : + T("Unable to send storage password. You must set the 'storageEncryptKey' option in servers.txt.\n")); + return; + } + my $crypton = new Utils::Crypton(pack("V*", @key), 32); + my $num = ($args->{switch} eq '023E') ? $config{charSelect_password} : $config{storageAuto_password}; + $num = sprintf("%d%08d", length($num), $num); + my $ciphertextBlock = $crypton->encrypt(pack("V*", $num, 0, 0, 0)); + $messageSender->sendStoragePassword($ciphertextBlock, 3); + + } elsif ($args->{flag} == 8) { # apparently this flag means that you have entered the wrong password + # too many times, and now the server is blocking you from using storage + error T("You have entered the wrong password 5 times. Please try again later.\n"); + # temporarily disable storageAuto + $config{storageAuto} = 0; + my $index = AI::findAction('storageAuto'); + if (defined $index) { + AI::args($index)->{done} = 1; + while (AI::action ne 'storageAuto') { + AI::dequeue; + } + } + } else { + debug(($args->{switch} eq '023E') ? + "Character password: unknown flag $args->{flag}\n" : + "Storage password: unknown flag $args->{flag}\n"); + } +} + +# TODO +sub storage_password_result { + my ($self, $args) = @_; + + # TODO: + # STORE_PASSWORD_EMPTY = 0x0 + # STORE_PASSWORD_EXIST = 0x1 + # STORE_PASSWORD_CHANGE = 0x2 + # STORE_PASSWORD_CHECK = 0x3 + # STORE_PASSWORD_PANALTY = 0x8 + + if ($args->{type} == 4) { # STORE_PASSWORD_CHANGE_OK = 0x4 + message T("Successfully changed storage password.\n"), "success"; + } elsif ($args->{type} == 5) { # STORE_PASSWORD_CHANGE_NG = 0x5 + error T("Error: Incorrect storage password.\n"); + } elsif ($args->{type} == 6) { # STORE_PASSWORD_CHECK_OK = 0x6 + message T("Successfully entered storage password.\n"), "success"; + } elsif ($args->{type} == 7) { # STORE_PASSWORD_CHECK_NG = 0x7 + error T("Error: Incorrect storage password.\n"); + # disable storageAuto or the Kafra storage will be blocked + configModify("storageAuto", 0); + my $index = AI::findAction('storageAuto'); + if (defined $index) { + AI::args($index)->{done} = 1; + while (AI::action ne 'storageAuto') { + AI::dequeue; + } + } + } else { + #message "Storage password: unknown type $args->{type}\n"; + } + + # $args->{val} + # unknown, what is this for? +} + +# Mercenary base status data (ZC_MER_INIT). +# 029B <id>.L <atk>.W <matk>.W <hit>.W <crit>.W <def>.W <mdef>.W <flee>.W <aspd>.W <name>.24B <level>.W <hp>.L <maxhp>.L <sp>.L <maxsp>.L <expire time>.L <faith>.W <calls>.L <kills>.L <atk range>.W +sub mercenary_init { + my ($self, $args) = @_; + + $char->{mercenary} = Actor::get($args->{ID}) if ($char->{mercenary}{ID} ne $args->{ID}); + $char->{mercenary}{map} = $field->baseName; + + my $slave = $char->{mercenary}; + + foreach (@{$args->{KEYS}}) { + $slave->{$_} = $args->{$_}; + } + $slave->{name} = bytesToString($args->{name}); + + Network::Receive::slave_calcproperty_handler($slave, $args); + + unless ($char->{slaves}{$char->{mercenary}{ID}}) { + if ($char->{mercenary}->isa('AI::Slave::Mercenary')) { + # After a teleport the mercenary object is still AI::Slave::Mercenary, but AI::SlaveManager::addSlave requires it to be Actor::Slave::Mercenary, so we change it back + bless $char->{mercenary}, 'Actor::Slave::Mercenary'; + } + AI::SlaveManager::addSlave($char->{mercenary}) if (!$char->has_mercenary); + } + + # ST0's counterpart for ST kRO, since it attempts to support all servers + # TODO: we do this for homunculus, mercenary and our char... make 1 function and pass actor and attack_range? + if ($config{mercenary_attackDistanceAuto} && exists $slave->{attack_range}) { + if($config{mercenary_attackDistance} > $slave->{attack_range}) { # decrease attack range if necessary + configModify('mercenary_attackDistance', $slave->{attack_range}, 1); + message TF("Autodetected attackDistance for mercenary = %s\n", $config{mercenary_attackDistance}), "success"; + } + if ($config{mercenary_attackMaxDistance} != $slave->{attack_range}) { # set max distance using information coming from the server + configModify('mercenary_attackMaxDistance', $slave->{attack_range}, 1); + message TF("Autodetected mercenary_attackMaxDistance for mercenary = %s\n", $config{mercenary_attackMaxDistance}), "success"; + } + } +} + +# +message_string +sub mercenary_off { + #delete $char->{slaves}{$char->{mercenary}{ID}}; + AI::SlaveManager::removeSlave($char->{mercenary}) if ($char->has_mercenary); + + $slavesList->removeByID($char->{mercenary}{ID}); + delete $char->{mercenary}; +} +# -message_string + +sub monster_ranged_attack { + my ($self, $args) = @_; + + my $ID = $args->{ID}; + my $range = $args->{range}; + + my %coords1; + $coords1{x} = $args->{sourceX}; + $coords1{y} = $args->{sourceY}; + my %coords2; + $coords2{x} = $args->{targetX}; + $coords2{y} = $args->{targetY}; + + my $monster = $monstersList->getByID($ID); + if ($monster) { + $monster->{movetoattack_pos} = {%coords1}; + $monster->{movetoattack_time} = time; + } + + $char->{movetoattack_targetID} = $ID; + + $char->{movetoattack_pos} = {%coords2}; + $char->{movetoattack_time} = time; + debug "Received Failed to attack target - you: $coords2{x},$coords2{y} - monster: $coords1{x},$coords1{y} - range $range\n", "parseMsg_move"; + + Plugins::callHook('monster_ranged_attack', {ID => $ID}); +} + +sub mvp_item { + my ($self, $args) = @_; + my $display = itemNameSimple($args->{itemID}); + message TF("Get MVP item %s\n", $display); + chatLog("k", TF("Get MVP item %s\n", $display)); +} + +sub mvp_other { + my ($self, $args) = @_; + my $display = Actor::get($args->{ID}); + message TF("%s become MVP!\n", $display); + chatLog("k", TF("%s become MVP!\n", $display)); +} + +sub mvp_you { + my ($self, $args) = @_; + my $msg = TF("Congratulations, you are the MVP! Your reward is %s exp!\n", $args->{expAmount}); + message $msg; + chatLog("k", $msg); +} + +sub no_teleport { + my ($self, $args) = @_; + my $fail = $args->{fail}; + + if ($fail == 0) { + error T("Unavailable Area To Teleport\n"); + AI::clear(qw/teleport/); + } elsif ($fail == 1) { + error T("Unavailable Area To Memo\n"); + } else { + error TF("Unavailable Area To Teleport (fail code %s)\n", $fail); + } +} + +sub private_message { + my ($self, $args) = @_; + + return unless changeToInGameState(); + + # Type: String + my $privMsgUser = bytesToString($args->{privMsgUser}); + my $privMsg = bytesToString($args->{privMsg}); + stripLanguageCode(\$privMsg); + my $parsed_msg = solveMessage($privMsg); + + if ($privMsgUser ne "" && binFind(\@privMsgUsers, $privMsgUser) eq "") { + push @privMsgUsers, $privMsgUser; + Plugins::callHook('parseMsg/addPrivMsgUser', { + user => $privMsgUser, + msg => $parsed_msg, + rawMsg => $privMsg, + userList => \@privMsgUsers, + }); + } + + chatLog("pm", TF("(From: %s) : %s\n", $privMsgUser, $parsed_msg)) if ($config{'logPrivateChat'}); + message TF("(From: %s) : %s\n", $privMsgUser, $parsed_msg), "pm"; + + ChatQueue::add('pm', undef, $privMsgUser, $parsed_msg); + Plugins::callHook('packet_privMsg', { + privMsgUser => $privMsgUser, + privMsg => $parsed_msg, + MsgUser => $privMsgUser, + Msg => $parsed_msg, + RawMsg => $privMsg, + }); + + if ($config{dcOnPM} && AI::state == AI::AUTO) { + message T("Auto disconnecting on PM!\n"); + chatLog("k", T("*** You were PM'd, auto disconnect! ***\n")); + $messageSender->sendQuit(); + quit(); + } +} + +sub progress_bar_unit { + my($self, $args) = @_; + debug "Displays progress bar (GID: $args-{GID} time: $args-{time})\n"; +} + +sub pvp_rank { + my ($self, $args) = @_; + + # 9A 01 - 14 bytes long + my $ID = $args->{ID}; + my $rank = $args->{rank}; + my $num = $args->{num};; + if ($rank != $ai_v{temp}{pvp_rank} || + $num != $ai_v{temp}{pvp_num}) { + $ai_v{temp}{pvp_rank} = $rank; + $ai_v{temp}{pvp_num} = $num; + if ($ai_v{temp}{pvp}) { + message TF("Your PvP rank is: %s/%s\n", $rank, $num), "map_event"; + } + } +} + +# Presents a list of items that can be repaired (ZC_REPAIRITEMLIST). +# 01FC <packet len>.W { <index>.W <name id>.W <refine>.B <card1>.W <card2>.W <card3>.W <card4>.W }* +sub repair_list { + my ($self, $args) = @_; + undef $repairList; + my $myself = 1; + my $msg1 = center(T(" Repair List "), 80, '-') ."\n". + T(" # Short name Full name\n"); + my $msg2; + for (my $i = 4; $i < $args->{RAW_MSG_SIZE}; $i += 13) { + my $repairItem = {}; + ($repairItem->{index}, + $repairItem->{nameID}, + $repairItem->{upgrade}, + $repairItem->{cards}, + ) = unpack('v2 C a8', substr($args->{RAW_MSG}, $i, 13)); + my $ID = $repairItem->{index} + 2; + $ID = pack("v", $ID); + my $item = $char->inventory->getByID($ID); + $repairItem->{name} = $item->{name}; + + #dirty hack - if the item ID does not match, then we repair other people's items + if ($repairItem->{nameID} ne $item->{nameID}) { + debug "Received 'Repair list' belongs to another player\n", 1; + $myself = 0; + last; + } + + $repairList->[$item->{binID}] = $repairItem; + my $shortName = itemNameSimple($repairItem->{nameID}); + $msg2 .= sprintf("%4d %-30s %s\n", $item->{binID}, $shortName, $item->{name}); + } + + if (!$myself) { + # then we repair other people's items + # we need to rebuild the entire array + undef $repairList; + undef $msg2; + for (my $i = 4; $i < $args->{RAW_MSG_SIZE}; $i += 13) { + my $repairItem = {}; + ($repairItem->{index}, + $repairItem->{nameID}, + $repairItem->{upgrade}, + $repairItem->{cards}, + ) = unpack('v2 C a8', substr($args->{RAW_MSG}, $i, 13)); + my $shortName = itemNameSimple($repairItem->{nameID}); + my $fullName = itemName($repairItem); + $repairItem->{name} = $fullName; + + $repairList->[$repairItem->{index}] = $repairItem; + $msg2 .= sprintf("%4d %-30s %s\n", $repairItem->{index}, $shortName, $fullName); + } + } + $msg2 .= ('-'x80) . "\n"; + message $msg1.$msg2, "list"; +} + +# Notifies the client about the result of a item repair request (ZC_ACK_ITEMREPAIR). +# 01FE <index>.W <result>.B +# index: +# ignored (inventory index) +# result: +# 0 = Item repair success. +# 1 = Item repair failure. +sub repair_result { + my ($self, $args) = @_; + + my $index = $args->{index} - 2; + my $item = $char->inventory->getByID($index); + + if ($args->{flag}) { + message TF("Repair of %s failed.\n", $repairList->[$index]->{name}); + } else { + message TF("Successfully repaired '%s'.\n", $repairList->[$index]->{name}); + } + undef $repairList; +} + +sub resurrection { + my ($self, $args) = @_; + + my $targetID = $args->{targetID}; + my $player = $playersList->getByID($targetID); + my $type = $args->{type}; + + if ($targetID eq $accountID) { + message T("You have been resurrected\n"), "info"; + undef $char->{'dead'}; + undef $char->{'dead_time'}; + $char->{'resurrected'} = 1; + + } else { + if ($player) { + undef $player->{'dead'}; + $player->{deltaHp} = 0; + } + + if (isMySlaveID($targetID)) { + enforce_homun_state(); + my $slave = Actor::get($targetID); + if ($slave->isa("AI::Slave::Homunculus") || $slave->isa("Actor::Slave::Homunculus")) { + message TF("Slave Resurrected: %s\n", $slave); + $char->{homunculus_info}{dead} = 0; + if (!$char->has_homunculus) { + debug "[Homunculus] Adding homunculus to SlaveManager after homunculus_info packet.\n"; + bless $char->{homunculus}, 'Actor::Slave::Homunculus'; + AI::SlaveManager::addSlave($slave); + } + } + } + message TF("%s has been resurrected\n", getActorName($targetID)), "info"; + } +} + +sub secure_login_key { + my ($self, $args) = @_; + $secureLoginKey = $args->{secure_key}; + debug sprintf("Secure login key: %s\n", getHex($args->{secure_key})), 'connection'; +} + +sub self_chat { + my ($self, $args) = @_; + my ($message, $chatMsgUser, $chatMsg); # Type: String + + $message = bytesToString($args->{message}); + + ($chatMsgUser, $chatMsg) = $message =~ /([\s\S]*?) : ([\s\S]*)/; + # Note: $chatMsgUser/Msg may be undefined. This is the case on + # eAthena servers: it uses this packet for non-chat server messages. + + if (defined $chatMsgUser) { + stripLanguageCode(\$chatMsg); + my $parsed_msg = solveMessage($chatMsg); + $message = $chatMsgUser . " : " . $parsed_msg; + } + + chatLog("c", "$message\n") if ($config{'logChat'}); + message "$message\n", "selfchat"; + + Plugins::callHook('packet_selfChat', { + user => $chatMsgUser, + msg => $chatMsg + }); +} + +sub sync_request { + my ($self, $args) = @_; + + # 0187 - long ID + # I'm not sure what this is. In inRO this seems to have something + # to do with logging into the game server, while on + # oRO it has got something to do with the sync packet. + if ($masterServer->{serverType} == 1) { + my $ID = $args->{ID}; + if ($ID == $accountID) { + $timeout{ai_sync}{time} = time; + $messageSender->sendSync() unless ($net->clientAlive); + debug "Sync packet requested\n", "connection"; + } else { + warning T("Sync packet requested for wrong ID\n"); + } + } +} + +sub sense_result { + my ($self, $args) = @_; + # nameID level size hp def race mdef element ice earth fire wind poison holy dark spirit undead + my @race_lut = qw(Formless Undead Beast Plant Insect Fish Demon Demi-Human Angel Dragon Boss Non-Boss); + my @size_lut = qw(Small Medium Large); + message TF("=====================Sense========================\n" . + "Monster: %-16s Level: %-12s\n" . + "Size: %-16s Race: %-12s\n" . + "Def: %-16s MDef: %-12s\n" . + "Element: %-16s HP: %-12s\n" . + "=================Damage Modifiers=================\n" . + "Ice: %-3s Earth: %-3s Fire: %-3s Wind: %-3s\n" . + "Poison: %-3s Holy: %-3s Dark: %-3s Spirit: %-3s\n" . + "Undead: %-3s\n" . + "==================================================\n", + $monsters_lut{$args->{nameID}}, $args->{level}, $size_lut[$args->{size}], $race_lut[$args->{race}], + $args->{def}, $args->{mdef}, $elements_lut{$args->{element}}, $args->{hp}, + $args->{ice}, $args->{earth}, $args->{fire}, $args->{wind}, $args->{poison}, $args->{holy}, $args->{dark}, + $args->{spirit}, $args->{undead}), "list"; +} + +# TODO: +# Add 'dispose' support +sub skill_cast { + my ($self, $args) = @_; + + return unless changeToInGameState(); + my $sourceID = $args->{sourceID}; + my $targetID = $args->{targetID}; + my $x = $args->{x}; + my $y = $args->{y}; + my $skillID = $args->{skillID}; + my $type = $args->{type}; + my $wait = $args->{wait}; + my ($dist, %coords); + + # Resolve source and target + my $source = Actor::get($sourceID); + my $target = Actor::get($targetID); + my $verb = $source->verb('are casting', 'is casting'); + + Misc::checkValidity("skill_cast part 1"); + + my $skill = new Skill(idn => $skillID); + $source->{casting} = { + skill => $skill, + target => $target, + x => $x, + y => $y, + startTime => time, + castTime => $wait + }; + # Since we may have a circular reference, weaken this reference + # to prevent memory leaks. + Scalar::Util::weaken($source->{casting}{target}); + + my $targetString; + if ($x != 0 || $y != 0) { + # If $dist is positive we are in range of the attack? + $coords{x} = $x; + $coords{y} = $y; + $dist = judgeSkillArea($skillID) - blockDistance($char->{pos_to}, \%coords); + $targetString = "location ($x, $y)"; + undef $targetID; + } else { + $targetString = $target->nameString($source); + } + + # Perform trigger actions + if ($sourceID eq $accountID) { + $char->{time_cast} = time; + $char->{time_cast_wait} = $wait / 1000; + delete $char->{cast_cancelled}; + } + countCastOn($sourceID, $targetID, $skillID, $x, $y); + + Misc::checkValidity("skill_cast part 2"); + + my $domain = ($sourceID eq $accountID) ? "selfSkill" : "skill"; + my $disp = skillCast_string($source, $target, $x, $y, $skill->getName(), $wait); + message $disp, $domain, 1; + + Plugins::callHook('is_casting', { + sourceID => $sourceID, + targetID => $targetID, + source => $source, + target => $target, + skillID => $skillID, + skill => $skill, + time => $source->{casting}{time}, + castTime => $wait, + x => $x, + y => $y + }); + + Misc::checkValidity("skill_cast part 3"); + + # Skill Cancel + my $monster = $monstersList->getByID($sourceID); + my $control; + $control = mon_control($monster->name,$monster->{nameID}) if ($monster); + if (AI::state == AI::AUTO && $control->{skillcancel_auto}) { + if ($targetID eq $accountID || $dist > 0 || (AI::action eq "attack" && AI::args->{ID} ne $sourceID)) { + message TF( "Monster Skill - %s (%d) - Adding it to monsterSkillCancel list to be attacked\n", + $monster->name, $monster->{binID} ); + $monster->{monsterSkillCancel} = 1; + } + + # Skill area casting -> running to monster's back + my $ID; + if ($dist > 0 && AI::action eq "attack" && ($ID = AI::args->{ID}) && (my $monster2 = $monstersList->getByID($ID))) { + # Calculate X axis + if ($char->{pos_to}{x} - $monster2->{pos_to}{x} < 0) { + $coords{x} = $monster2->{pos_to}{x} + 3; + } else { + $coords{x} = $monster2->{pos_to}{x} - 3; + } + # Calculate Y axis + if ($char->{pos_to}{y} - $monster2->{pos_to}{y} < 0) { + $coords{y} = $monster2->{pos_to}{y} + 3; + } else { + $coords{y} = $monster2->{pos_to}{y} - 3; + } + + my (%vec, %pos); + getVector(\%vec, \%coords, $char->{pos_to}); + moveAlongVector(\%pos, $char->{pos_to}, \%vec, distance($char->{pos_to}, \%coords)); + ai_route($field->baseName, $pos{x}, $pos{y}, + maxRouteDistance => $config{attackRouteMaxPathDistance}, + maxRouteTime => $config{attackMaxRouteTime}, + noMapRoute => 1); + message TF("Avoid casting Skill - switch position to : %s,%s\n", $pos{x}, $pos{y}), 1; + } + + Misc::checkValidity("skill_cast part 4"); + } +} + +# Notifies clients in area, that an object canceled casting (ZC_DISPEL). +# 01B9 <id>.L +sub cast_cancelled { + my ($self, $args) = @_; + + # Cast is cancelled + my $ID = $args->{ID}; + + my $source = Actor::get($ID); + $source->{cast_cancelled} = time; + my $skill = $source->{casting}->{skill}; + my $skillName = $skill ? $skill->getName() : T('Unknown'); + my $domain = ($ID eq $accountID) ? "selfSkill" : "skill"; + message sprintf($source->verb(T("%s failed to cast %s\n"), T("%s failed to cast %s\n")), $source, $skillName), $domain; + Plugins::callHook('packet_castCancelled', { + sourceID => $ID + }); + delete $source->{casting}; +} + +# Notifies the client, whether it can disconnect and change servers (ZC_RESTART_ACK). +# 00B3 <type>.B +# type: +# 1 = disconnect, char-select +# ? = nothing +# TODO: add real client messages and logic? +# ClientLogic: LoginStartMode = 5; ShowLoginScreen; +sub switch_character { + my ($self, $args) = @_; + # User is switching characters in X-Kore + $net->setState(Network::CONNECTED_TO_MASTER_SERVER); + $net->serverDisconnect() if(UNIVERSAL::isa($net, 'Network::DirectConnection')); + + # FIXME better support for multiple received_characters packets + undef @chars; + + debug "result: $args->{result}\n"; +} + +# Notifies the client about the result of a request to take off an item. +# 00AC <index>.W <equip location>.W <result>.B (ZC_REQ_TAKEOFF_EQUIP_ACK) +# 08D1 <index>.W <equip location>.W <result>.B (ZC_REQ_TAKEOFF_EQUIP_ACK2) +# 099A <index>.W <equip location>.L <result>.B (ZC_ACK_TAKEOFF_EQUIP_V5) +# @ok : //inversed for v2 v5 +# 0 = failure +# 1 = success +sub unequip_item { + my ($self, $args) = @_; + + return unless changeToInGameState(); + my $item = $char->inventory->getByID($args->{ID}); + delete $item->{equipped}; + + if ($args->{type} == 10 || $args->{type} == 32768) { + delete $char->{equipment}{arrow}; + delete $char->{arrow}; + } else { + foreach (%equipSlot_rlut){ + if ($_ & $args->{type}){ + next if $_ == 10; #work around Arrow bug + next if $_ == 32768; + delete $char->{equipment}{$equipSlot_lut{$_}}; + Plugins::callHook('unequipped_item', { + slot => $equipSlot_lut{$_}, + item => $item + }); + } + } + } + + if ($item) { + message TF("You unequip %s (%d) - %s\n",$item->{name}, $item->{binID},$equipTypes_lut{$item->{type_equip}}), 'inventory'; + } +} + +# Acknowledgement for removing an equip to the equip switch window +# 0A9A <index>.W <position.>.L <failure>.W +sub unequip_item_switch { + my ($self, $args) = @_; + + return unless changeToInGameState(); + my $item = $char->inventory->getByID($args->{ID}); + delete $item->{eqswitch}; + + if ($args->{type} == 10 || $args->{type} == 32768) { + delete $char->{eqswitch}{arrow}; + } else { + foreach (%equipSlot_rlut){ + if ($_ & $args->{type}){ + next if $_ == 10; #work around Arrow bug + next if $_ == 32768; + + delete $char->{eqswitch}{$equipSlot_lut{$_}}; + Plugins::callHook('unequipped_item_sw', { + slot => $equipSlot_lut{$_}, + item => $item + }); + } + } + } + + if ($item) { + message TF("[Equip Switch] You unequip %s (%d) - %s\n",$item->{name}, $item->{binID},$equipTypes_lut{$item->{type_equip}}), 'inventory'; + } +} + +# TODO: only used to report failure? $args->{success} +sub use_item { + my ($self, $args) = @_; + return unless changeToInGameState(); + my $item = $char->inventory->getByID($args->{ID}); + if ($item) { + message TF("You used Item: %s (%d) x %s\n", $item->{name}, $item->{binID}, $args->{amount}), "useItem"; + inventoryItemRemoved($item->{binID}, $args->{amount}); + } +} + +sub users_online { + my ($self, $args) = @_; + message TF("There are currently %s users online\n", $args->{users}), "info"; +} + +# You see a vender! Add them to the visible venders list. +sub vender_found { + my ($self, $args) = @_; + my $ID = $args->{ID}; + + if (!$venderLists{$ID} || !%{$venderLists{$ID}}) { + binAdd(\@venderListsID, $ID); + Plugins::callHook('packet_vender', { + ID => $ID, + title => bytesToString($args->{title}) + }); + } + $venderLists{$ID}{title} = bytesToString($args->{title}); + $venderLists{$ID}{id} = $ID; +} + +sub vender_lost { + my ($self, $args) = @_; + + my $ID = $args->{ID}; + binRemove(\@venderListsID, $ID); + delete $venderLists{$ID}; +} + +sub skill_add { + my ($self, $args) = @_; + + return unless changeToInGameState(); + my $handle = ($args->{name}) ? $args->{name} : Skill->new(idn => $args->{skillID})->getHandle(); + + $char->{skills}{$handle}{ID} = $args->{skillID}; + $char->{skills}{$handle}{sp} = $args->{sp}; + $char->{skills}{$handle}{range} = $args->{range}; + $char->{skills}{$handle}{up} = $args->{upgradable}; + $char->{skills}{$handle}{targetType} = $args->{target}; + $char->{skills}{$handle}{lv} = $args->{lv}; + $char->{skills}{$handle}{new} = 1; + + #Fix bug , receive status "Night" 2 time + binAdd(\@skillsID, $handle) if (binFind(\@skillsID, $handle) eq ""); + + Skill::DynamicInfo::add($args->{skillID}, $handle, $args->{lv}, $args->{sp}, $args->{target}, $args->{target}, Skill::OWNER_CHAR); + + Plugins::callHook('packet_charSkills', { + ID => $args->{skillID}, + handle => $handle, + level => $args->{lv}, + upgradable => $args->{upgradable}, + level2 => $args->{lv2}, + }); +} + +sub isvr_disconnect { + debug "Received the package 'isvr_disconnect'\n"; +} + +sub skill_use_failed { + my ($self, $args) = @_; + + my %basefailtype = ( + 0 => $msgTable[160],#"skill failed" + 1 => $msgTable[161],#"no emotions" + 2 => $msgTable[162],#"no sit" + 3 => $msgTable[163],#"no chat" + 4 => $msgTable[164],#"no party" + 5 => $msgTable[165],#"no shout" + 6 => $msgTable[166],#"no PKing" + 7 => $msgTable[384],#"no aligning" + #? = ignored + ); + + my %failtype = ( + 0 => T( 'Basic' ), + 1 => T( 'Insufficient SP' ), + 2 => T( 'Insufficient HP' ), + 3 => T( 'No Memo' ), + 4 => T( 'Mid-Delay' ), + 5 => T( 'No Zeny' ), + 6 => T( 'Wrong Weapon Type' ), + 7 => T( 'Red Gem Needed' ), + 8 => T( 'Blue Gem Needed' ), + 9 => TF( '%s Overweight', '90%' ), + 10 => T( 'Requirement' ), + 11 => T( 'Failed to use in Target' ), + 12 => T( 'Maximum Ancilla exceed' ), + 13 => T( 'Need this within the Holy water' ), + 14 => T( 'Missing Ancilla' ), + 19 => T( 'Full Amulet' ), + 24 => T( '[Purchase Street Stall License] need 1' ), + 26 => T( 'Position error' ), + 29 => TF( 'Must have at least %s of base XP', '1%' ), + 30 => T( 'Insufficient SP' ), + 33 => T( 'Failed to use Madogear' ), + 34 => T( 'Kunai is Required' ), + 37 => T( 'Canon ball is Required' ), + 43 => T( 'Failed to use Guillotine Poison' ), + 50 => T( 'Failed to use Madogear' ), + 71 => T( 'Missing Required Item' ), # (item name) required x amount + 72 => T( 'Equipment is required' ), + 73 => T( 'Combo Skill Failed' ), + 76 => T( 'Too many HP' ), + 77 => T( 'Need Royal Guard Branding' ), + 78 => T( 'Required Equiped Weapon Class' ), + 83 => T( 'Location not allowed to create chatroom/market' ), + 84 => T( 'Need more bullet' ), + ); + + my $errorMessage; + if ($args->{skillID} == 1 && $args->{cause} == 0 && exists $basefailtype{$args->{btype}}) { + $errorMessage = $basefailtype{$args->{btype}}; + } elsif (exists $failtype{$args->{cause}}) { + $errorMessage = $failtype{$args->{cause}}; + if ($args->{cause} == 71) { + $errorMessage .= T(' - item ').$args->{itemId}; + } + } else { + $errorMessage = T('Unknown error'); + } + + delete $char->{casting}; + + my %hookArgs; + $hookArgs{skillID} = $args->{skillID}; + $hookArgs{btype} = $args->{btype}; + $hookArgs{itemId} = $args->{itemId}; + $hookArgs{flag} = $args->{flag}; + $hookArgs{cause} = $args->{cause}; + $hookArgs{failMessage} = $errorMessage; + $hookArgs{warn} = 1; + + Plugins::callHook('packet_skillfail', \%hookArgs); + + warning(TF("Skill %s failed: %s (error number %s)\n", Skill->new(idn => $args->{skillID})->getName(), $errorMessage, $args->{cause}), "skill") if ($hookArgs{warn}); + + # Ressurect Homunculus failed - which means we have no dead homunculus + if ($args->{skillID} == 247 && $args->{cause} == 0) { + debug "[Homunculus] Ressurect Homunculus failed - which means we have no dead homunculus.\n"; + $char->{homunculus_info}{dead} = 0; + } + + # Call Homunculus failed - which means we have no vaporized homunculus + if ($args->{skillID} == 243) { + if ($args->{cause} == 0) { + debug "[Homunculus] Call Homunculus failed - which means we have no vaporized homunculus.\n"; + $char->{homunculus_info}{vaporized} = 0; + } elsif ($args->{cause} == 71) { + debug "[Homunculus] Call Homunculus failed because of missing item - which means we have a vaporized homunculus.\n"; + $char->{homunculus_info}{vaporized} = 1; + } + } +} + +sub open_store_status { + my ($self, $args) = @_; + + if ($args->{flag} == 0) { + message T("Store set up succesfully\n"), 'success'; + + Plugins::callHook('open_store_success'); + } else { + error TF("Failed setting up shop with error code %d\n", $args->{flag}); + + Plugins::callHook('open_store_fail', { flag => $args->{flag} }); + } +} + +sub stylist_res { + my ($self, $args) = @_; + + if ($args->{res}) { + message T("[Stylist UI] Success.\n"), "info"; + } else { + error T("[Stylist UI] Fail.\n"); + } +} + +## +# User Interface (open system) +## + +# Opens an UI window of the given type and initializes it with the given data +# 0AE2 <type>.B <data>.L +# type: +# 0x0 = BANK_UI +# 0x1 = STYLIST_UI +# 0x2 = CAPTCHA_UI +# 0x3 = MACRO_UI +# 0x4 = UI_UNUSED +# 0x5 = TIPBOX_UI +# 0x6 = RENEWQUEST_UI +# 0x7 = ATTENDANCE_UI +sub open_ui { + my ($self, $args) = @_; + + debug TF("Received request from server to open UI: %s\n", $args->{type}); + + if ($args->{type} == BANK_UI) { # TODO: implement bank system and add Bank open Request + message T("Server requested to open Bank UI.\n"); + } elsif ($args->{type} == STYLIST_UI) { # TODO: implement Stylist system and add Stylist open Request + message T("Server requested to open Stylist UI.\n"); + } elsif ($args->{type} == CAPTCHA_UI) { + message T("Server requested to open Captcha UI.\n"); + } elsif ($args->{type} == MACRO_UI) { + message T("Server requested to open Macro Recorder UI.\n"); + } elsif ($args->{type} == UI_UNUSED) { + message T("Server requested to open Unused UI.\n"); # why? + } elsif ($args->{type} == TIPBOX_UI) { + message T("Server requested to open Tip Box UI.\n"); + } elsif ($args->{type} == RENEWQUEST_UI) { + message T("Server requested to open Quest UI.\n"); + } elsif ($args->{type} == ATTENDANCE_UI) { + message T("Server requested to open Attendance UI.\n"); + $self->attendance_ui($args); + } else { + error TF("Received request from server to open unknown UI: %s\n", $args->{type}); + } +} + +# Response for UI request +# 0AF0 <type>.L <data>.L (PACKET_ZC_UI_ACTION) +# type: +# 0x0 = close current UI +sub action_ui { + my ($self, $args) = @_; + + debug TF("Received request from server to close UI: %s\n", $args->{type}); +} + +## +# Attendance System +## + +# Opens an ATTENDANCE UI window and initializes it with the given data +# 0AE2 <type>.B <data>.L +# type = 0x7 +sub attendance_ui { + my ($self, $args) = @_; + + if (defined $attendance_rewards{period}) { + my $date = getFormattedDateShort(time, 3); + + if ($date >= $attendance_rewards{period}{start} && $date <= $attendance_rewards{period}{end}) { + my $already_requested = $args->{data}%10; + my $attendance_count = int($args->{data}/10) + 1 - $already_requested; + my $attendanceAuto; + my $msg = center(T(" Attendance "), 54, '-') ."\n"; + $msg .= TF("Start: %s End: %s Day: %s\n", $attendance_rewards{period}{start}, $attendance_rewards{period}{end}, $attendance_count); + + $msg .= T("Day Item Amount Requested\n"); + for (my $i = 1; $i <= 20; $i++) { + my $requested = ($attendance_count >= $i) ? T("yes") : T("no"); + if ($attendance_count == $i && !$already_requested) { + $requested = T("can"); + $attendanceAuto = 1 if $config{'attendanceAuto'}; + } + $msg .= swrite(sprintf("\@%s \@%s \@%s \@%s", ('<'x3), ('<'x30), ('<'x6), ('<'x9)), + [$i, itemNameSimple($attendance_rewards{items}{$i}{item_id}), $attendance_rewards{items}{$i}{amount}, $requested]); + } + + $msg .= ('-'x54) . "\n"; + message $msg, "info"; + + if ($attendanceAuto) { + Commands::run('attendance request'); + message T("Run command: 'attendance request'\n"); + } + } else { + warning T("attendance_rewards.txt is outdated\n"), "info"; + } + } else { + error T("attendance_rewards.txt not exist\n"); + } +} + +# Notifies a movement interrupted +# 0AB8 +sub move_interrupt { + my ($self, $args) = @_; + debug "Movement interrupted by casting a skill/fleeing a mob/etc\n"; +} + +## +# Banking System +## + +# Display how much we have in bank +# 09A6 <Bank_Vault>Q <Reason>W (PACKET_ZC_BANKING_CHECK) +# Reason: +# 1 = mark opening and closing +sub banking_check { + my ($self, $args) = @_; + + $bankingopened = 1; + $banking{zeny} = $args->{zeny}; + + message center(T("[Zeny Storage (Bank)]"), 40, '-') ."\n", "info"; + message TF("In Bank : %s z\n", $args->{zeny}), "info"; + message TF("On Hand : %s z\n", $char->{zeny}), "info"; + message ('-'x40) . "\n", "info"; + + Plugins::callHook('banking_opened'); +} + +# Acknowledge of deposit some money in bank +# 09A8 <Reason>W <Money>Q <balance>L (PACKET_ZC_ACK_BANKING_DEPOSIT) +# reason: +# BDA_SUCCESS = 0x0 +# BDA_ERROR = 0x1 +# BDA_NO_MONEY = 0x2 +# BDA_OVERFLOW = 0x3 +sub banking_deposit { + my ($self, $args) = @_; + + if ($args->{reason} == 0x0) { + message T("Bank: Deposit Success.\n"), "success"; + $char->{zeny} = $args->{balance}; # TODO: check if 'stat_info' is received (if yes, delete this line) + Plugins::callHook('banking_deposit_success'); + return; + } elsif ($args->{reason} == 0x1) { + error T("Bank: Deposit Error (Try it again).\n"); + } elsif ($args->{reason} == 0x2) { + error T("Bank: No Money For Deposit.\n"); + } elsif ($args->{reason} == 0x3) { + error T("Bank: Money in the bank overflow.\n"); + } + Plugins::callHook('banking_deposit_failed', {'reason' => $args->{reason}}); +} + +# Acknowledge of withdrawing some money from bank +# 09AA <Reason>W <Money>Q <balance>L (PACKET_ZC_ACK_BANKING_WITHDRAW) +# reason: +# BWA_SUCCESS = 0x0 +# BWA_NO_MONEY = 0x1 +# BWA_UNKNOWN_ERROR = 0x2 +sub banking_withdraw { + my ($self, $args) = @_; + + if ($args->{reason} == 0x0) { + message T("Bank: Withdraw Success \n"),"success"; + $char->{zeny} = $args->{balance}; # TODO: check if 'stat_info' is received (if yes, delete this line) + Plugins::callHook('banking_withdraw_success'); + return; + } elsif ($args->{reason} == 0x1) { + error T("Bank: No Money for Withdraw.\n"); + } elsif ($args->{reason} == 0x2) { + error T("Bank: Money in the bank overflow.\n"); + } + Plugins::callHook('banking_withdraw_failed', {'reason' => $args->{reason}}); +} + +## +# Navigation System +## + +# start a navigation to designed location/map +# 08E2 <type>.B <flag>.B <hide>.B <map>.16B <x pos>.W <y pos>.W <mob id>.W +# TODO: document type and flag +sub navigate_to { + my ($self, $args) = @_; + + if ($args->{mob_id}) { + message TF("Server asked us to navigate to %s map and look for monster with ID %s\n", $args->{map}, $args->{mob_id}), "info"; + } else { + message TF("Server asked us to navigate to %s (%s,%s)\n", $args->{map}, $args->{x}, $args->{y}), "info"; + } + + Plugins::callHook('navigate_to', $args); +} + +## +# Roulette System +## +# Opens the roulette window +# 0A1A <result>.B <serial>.L <stage>.B <price index>.B <additional item id>.W <gold>.L <silver>.L <bronze>.L (ZC_ACK_OPEN_ROULETTE) +sub roulette_window { + my ($self, $args) = @_; + my @result_lut = qw(Success Failed No_Enought_Point Losing); + + foreach (@{$args->{KEYS}}) { + $roulette{$_} = $args->{$_}; + } + + if ($args->{result} == 1) { + warning T("Roulette: Something went wrong\n"); + return; + } elsif ($args->{result} == 2) { + warning T("Roulette: No enough Point (coin) to roll\n"); + return; + } + + message center(T("[Roulette] - " . $args->{serial}), 60, '-') ."\n", "info"; + message TF("Result: %s Row: %s Column: %s Bonus Item: %s\n", $result_lut[$args->{result}], $args->{stage}, $args->{price}, itemNameSimple($args->{additional_item})), "info"; + message T("Coins:\n"), "info"; + message TF("Gold: %s Silver: %s Bronze: %s\n", $args->{gold}, $args->{silver}, $args->{bronze}, itemNameSimple($args->{additional_item})), "info"; + message center(T("-"), 60, '-') . "\n", "info"; + + if ($args->{stage} == 6) { + warning T("Please Claim Your Prize this was the last roll in this round. (you will lost the gold and the item)\n"); + } +} + +# Sends the info about the available roulette rewards to the client +# 0A1C <length>.W <serial>.L { { <level>.W <column>.W <item>.W <amount>.W } * MAX_ROULETTE_COLUMNS } * MAX_ROULETTE_LEVEL (ZC_ACK_ROULEITTE_INFO) +# 0A1C <length>.W <serial>.L { { <level>.W <column>.W <item>.L <amount>.L } * MAX_ROULETTE_COLUMNS } * MAX_ROULETTE_LEVEL (ZC_ACK_ROULEITTE_INFO) >= 20180516 +sub roulette_info { + my ($self, $args) = @_; + + my $item_info = { + len => 8, # or 12 + types => 'v4', # or v2 V2 + keys => [qw(level column item_id amount)], + }; + + for (my $i = 0; $i < length($args->{roulette_info}); $i += $item_info->{len}) { + my $item; + @{$item}{@{$item_info->{keys}}} = unpack($item_info->{types}, substr($args->{roulette_info}, $i, $item_info->{len})); + $item->{name} = itemNameSimple($item->{item_id}); + $roulette{items}{$item->{level}}{$item->{column}} = $item; + debug TF("Level: %s Column: %s Item: %s\n", $item->{level}, $item->{column}, $item->{name}); + } +} + +# Response to a item reward request +# 0A22 <type>.B <bonus item>.W (ZC_RECV_ROULETTE_ITEM) +sub roulette_recv_item { + my ($self, $args) = @_; + message TF("Roulette Bonus - Type: %s Bonus Item: %s\n", $args->{type}, itemNameSimple($args->{item_id})), "info"; + +} + +# Update Roulette window with current stats +# 0A20 <result>.B <stage>.W <price index>.W <bonus item>.W <gold>.L <silver>.L <bronze>.L (ZC_ACK_GENERATE_ROULETTE) +sub roulette_window_update { + my ($self, $args) = @_; + my @result_lut = qw(Success Failed No_Enought_Point Losing); + + foreach (@{$args->{KEYS}}) { + $roulette{$_} = $args->{$_}; + } + + if ($args->{result} == 1) { + warning T("Roulette: Something went wrong\n"); + return; + } elsif ($args->{result} == 2) { + warning T("Roulette: No enough Point (coin) to roll\n"); + return; + } + + message center(T("[Roulette] - " . $roulette{serial}), 60, '-') ."\n", "info"; + message TF("Result: %s Row: %s Column: %s Bonus Item: %s\n", $result_lut[$args->{result}], $args->{stage}, $args->{price}, itemNameSimple($args->{additional_item})), "info"; + message T("Coins:\n"), "info"; + message TF("Gold: %s Silver: %s Bronze: %s\n", $args->{gold}, $args->{silver}, $args->{bronze}, itemNameSimple($args->{additional_item})), "info"; + message T("Result:\n"), "info"; + message T(">> ".$roulette{items}{$args->{stage}}{$args->{price}}->{name}." << \n"), "info"; + message center(T("-"), 60, '-') . "\n", "info"; + + if ($args->{stage} == 6) { + warning T("Please Claim Your Prize this was the last roll in this round. (you will lost the gold and the item)\n"); + } +} + +# Allow Client Shortcut/Keys Input +# 0B01 +sub load_confirm { + my ($self, $args) = @_; + debug TF("You are allowed to use Keyboard\n"); # this only matter in ragexe client +} + +# Inventory Expansion Result +# 0B18 <Result>W +# result: +# EXPAND_INVENTORY_RESULT_SUCCESS = 0x0 +# EXPAND_INVENTORY_RESULT_FAILED = 0x1 +# EXPAND_INVENTORY_RESULT_OTHER_WORK = 0x2 +# EXPAND_INVENTORY_RESULT_MISSING_ITEM = 0x3 +# EXPAND_INVENTORY_RESULT_MAX_SIZE = 0x4 +sub inventory_expansion_result { + my($self, $args) = @_; + + #msgstringtable + if ($args->{result} == EXPAND_INVENTORY_RESULT_SUCCESS) { + message TF("You have successfully expanded the possession limit.\n"),"info"; + } elsif ($args->{result} == EXPAND_INVENTORY_RESULT_FAILED) { + message TF("Failed to expand the maximum possession limit.\n"),"info"; + } elsif ($args->{result} == EXPAND_INVENTORY_RESULT_OTHER_WORK) { + message TF("To expand the possession limit, please close other windows.\n"),"info"; + } elsif ($args->{result} == EXPAND_INVENTORY_RESULT_MISSING_ITEM) { + message TF("Failed to expand the maximum possession limit, insufficient required item.\n"),"info"; + } elsif ($args->{result} == EXPAND_INVENTORY_RESULT_MAX_SIZE) { + message TF("You can no longer expand the maximum possession limit.\n"),"info"; + } else { + message TF("Unknown result in inventory expansion (%s).\n", $args->{result}),"info"; + } +} + +sub item_preview { + my ($self, $args) = @_; + my $item = $char->inventory->getByID($args->{index}); + if ($item) { + $item->{broken} = $args->{broken} if (defined $args->{broken}); + $item->{upgrade} = $args->{upgrade}; + $item->{cards} = $args->{cards}; + $item->{options} = $args->{options}; + $item->setName(itemName($item)); + + } +} + +# 0B1D (PACKET_ZC_PING) +sub ping { + return if ($config{XKore} eq 1 || $config{XKore} eq 3); + $messageSender->sendPing(); +} + +# 0253 - ZC_STARPLACE +# Star Gladiator's Feeling map confirmation prompt +sub starplace { + my ($self, $args) = @_; + message TF("Wich: %s\n", $args->{which}); +} + +### +# +# Captcha System ( macro detector ) +# 4 parts: Macro Register UI ( /macro_register ), Macro Detector UI ( player ), Macro Reporter UI ( /macro_detector ) and Captcha Preview UI ( /macro_preview ) +# +### + +# 0A53 - PACKET_ZC_CAPTCHA_UPLOAD_REQUEST +# Captcha Upload Image UI +sub captcha_upload_request { + my ($self, $args) = @_; + if ($args->{status} == 0) { + message T("Captcha Register - Now you can upload the image\n"); + } elsif ($args->{status} == 1) { + message T("Captcha Register - Failed to upload the image\n"); + } else { + message TF("Captcha Register - Unknown status: %s\n", $args->{status}); + } + + return unless (UNIVERSAL::isa($net, 'Network::DirectConnection')); +} + +# 0A55 - PACKET_ZC_CAPTCHA_UPLOAD_REQUEST_STATUS +# Result of Captcha Upload +sub captcha_upload_request_status { + message T("Captcha Register - Image uploaded succesfully\n"); +} + +# 0A57 - PACKET_ZC_MACRO_REPORTER_STATUS +# Status of Macro Reporter +sub macro_reporter_status { + my ($self, $args) = @_; + my $status = T("Unknown"); + + if ($args->{status} == MCR_MONITORING) { + $status = T("Monitoring"); + } elsif ($args->{status} == MCR_NO_DATA) { + $status = T("No Data"); + } elsif ($args->{status} == MCR_INPROGRESS) { + $status = T("In Progress"); + } + + message TF("Macro Reporter - Status: %s \n", $status), "captcha"; +} + +# 0A58 - PACKET_ZC_MACRO_DETECTOR_REQUEST +# Macro Detector Image info +sub macro_detector { + my ($self, $args) = @_; + debug TF("Macro Detector - image_size: %s bytes - captcha_key: %s\n", $args->{image_size}, $args->{captcha_key}), "captcha"; + $captcha_size = $args->{image_size}; + $captcha_key = $args->{captcha_key}; +} + +# 0A59 - PACKET_ZC_MACRO_DETECTOR_REQUEST_DOWNLOAD +# Macro DDetector Captcha Image +# captcha_image is sended in chunks +sub macro_detector_image { + my ($self, $args) = @_; + + $captcha_image .= $args->{captcha_image}; + + if (length($captcha_image) >= $captcha_size) { + my $image = uncompress($captcha_image); + my $imageHex = unpack("H*", $image); + my $byte1; my $byte2; my $byte3; + for (my $i = 102; $i < 3564; $i += 6) { + $byte1 = hex(substr($imageHex, $i, 2)); + $byte2 = substr($imageHex, $i + 2, 2); + $byte3 = hex(substr($imageHex, $i + 4, 2)); + + if ($byte1 > 250 && $byte2 eq '00' && $byte3 > 250) { + substr($imageHex, $i + 2, 2) = 'FF'; + } + } + + my $file = $Settings::logs_folder . "/captcha_$captcha_key.bmp"; + my $final_image = pack("H*", $imageHex); + $captcha_image_content = $final_image; + open my $DUMP, '>:raw', $file; + print $DUMP $final_image; + close $DUMP; + + + my $hookArgs = {captcha_image => $final_image}; + Plugins::callHook ('captcha_image', $hookArgs); + Plugins::callHook ('captcha_file', {file => $file}); + return 1 if $hookArgs->{return}; + + warning TF("Macro Detector - captcha has been saved in: %s, open it, solve it and use the command: captcha <text>\n", $file), "captcha"; + + $captcha_image = ""; + $captcha_size = undef; + $captcha_key = undef; + $messageSender->sendMacroDetectorDownload() if (UNIVERSAL::isa($net, 'Network::DirectConnection')); + } +} + +# 0A5B - PACKET_ZC_MACRO_DETECTOR_SHOW +# Macro Detector UI +sub macro_detector_show { + my ($self, $args) = @_; + message T("Macro Detector\n"), "captcha"; + message TF("Remaining Chances: %s - Remaining Time: %s seconds\n", $args->{remaining_chances}, $args->{remaining_time} / 1000), "captcha"; + return unless (UNIVERSAL::isa($net, 'Network::DirectConnection')); + # TODO: check request image? +} + +# 0A5D - PACKET_ZC_MACRO_DETECTOR_STATUS +# Status of Macro Detector +sub macro_detector_status { + my ($self, $args) = @_; + my $status = T("Unknown"); + + if ($args->{status} == MCD_TIMEOUT) { + $status = T("Timeout"); + } elsif ($args->{status} == MCD_INCORRECT) { + $status = T("Incorrect"); + } elsif ($args->{status} == MCD_GOOD) { + $status = T("Correct"); + } + + message TF("Macro Detector Status: %s \n", $status), "captcha"; +} + +# 0A6A - PACKET_ZC_CAPTCHA_PREVIEW_REQUEST +# Status of Preview Captcha Image Request +sub captcha_preview { + my ($self, $args) = @_; + + $captcha_size = $args->{image_size}; + $captcha_key = $args->{captcha_key}; + + if ($args->{status} == 0) { + message T("Captcha Preview - Now you can download the image\n"); + } elsif ($args->{status} == 1) { + message T("Captcha Preview - Failed to Request Captcha (ID is out of range)\n"); + } else { + message TF("Captcha Preview - Unknown status: %s\n", $args->{status}); + } + debug TF("Captcha Preview - image_size: %s bytes - captcha_key: %s\n", $args->{image_size}, $args->{captcha_key}), "captcha"; +} + +# 0A6B - PACKET_ZC_CAPTCHA_PREVIEW_REQUEST_DOWNLOAD +# Preview a captcha image +sub captcha_preview_image { + my ($self, $args) = @_; + + $captcha_image .= $args->{captcha_image}; + + if (length($captcha_image) >= $captcha_size) { + my $image = uncompress($captcha_image); + my $imageHex = unpack("H*", $image); + + my $file = $Settings::logs_folder . "/captcha_preview_".$char->{name}."_".$captcha_key.".bmp"; + open my $DUMP, '>:raw', $file; + print $DUMP pack("H*", $imageHex); + close $DUMP; + + message TF("Captcha Preview - captcha has been saved in: %s\n", $file), "captcha"; + $captcha_image = ""; + $captcha_size = undef; + $captcha_key = undef; + } +} + +# 0A6D - PACKET_ZC_MACRO_REPORTER_SELECT +# Player List +sub macro_reporter_select { + my ($self, $args) = @_; + + message T("Macro Reporter - Account List:\n"); + for (my $i = 0; $i < length($args->{account_list}); $i += 4) { + my $accID = unpack("a4", substr($args->{account_list}, $i, 4)); + my $player = $playersList->getByID($accID); + message TF("%s\n", $player->{name}); + } +} + +# 0B8D - PACKET_ZC_REPUTE_INFO +sub repute_info { + my ($self, $args) = @_; + + @reputation_list = (); + + my $unpack = { + len => 16, + types => 'V4', + keys => [qw(type type2 points points2)], + }; + my $length = length $args->{reputeInfo}; + for (my $i = 0; $i < $length; $i += $unpack->{len}) { + my $repute; + @{$repute}{@{$unpack->{keys}}} = unpack($unpack->{types}, substr($args->{reputeInfo}, $i, $unpack->{len})); + + push @reputation_list, $repute; + } +} + +# 0A15 - PACKET_ZC_GOLDPCCAFE_POINT +# TODO: this package is not supported yet. +sub gold_pc_cafe_point { + my ($self, $args) = @_; + debug TF("[gold_pc_cafe_point] isActive=%d, mode=%d, point=%d, playedTime=%d\n", $args->{isActive}, $args->{mode}, $args->{point}, $args->{playedTime}); +} + +# 0A17 - PACKET_ZC_DYNAMICNPC_CREATE_RESULT +sub dynamicnpc_create_result { + my ($self, $args) = @_; + my $status; + + if ($args->{result} == DYNAMICNPC_RESULT_SUCCESS ) { + $status = T("Success"); + } elsif ($args->{result} == DYNAMICNPC_RESULT_UNKNOWN) { + $status = T("Unknown"); + } elsif ($args->{result} == DYNAMICNPC_RESULT_UNKNOWNNPC) { + $status = T("Unknown NPC"); + } elsif ($args->{result} == DYNAMICNPC_RESULT_DUPLICATE) { + $status = T("Duplicate"); + } elsif ($args->{result} == DYNAMICNPC_RESULT_OUTOFTIME) { + $status = T("Out of time"); + } + + message TF("Dynamic NPC create result - Status: %s\n", $status); +} + +# 0840 - PACKET_HC_NOTIFY_ACCESSIBLE_MAPNAME +sub parse_notify_accessible_mapname { + my ($self, $args) = @_; + + my $mapList = { + len => 20, + types => 'V Z16', + keys => [qw(unknown map_name)], + }; + + @{$args->{map_list}} = map { + my %map; + @map{@{$mapList->{keys}}} = unpack($mapList->{types}, $_); + \%map; + } unpack "(a$mapList->{len})*", $args->{mapList}; +} + +sub notify_accessible_mapname { + my ($self, $args) = @_; + my $map_index = 0; + + foreach my $i (0 .. $#{$args->{map_list}}) { + my $map = $args->{map_list}[$i]; + error("[notify_accessible_mapname] unknown = $map->{unknown}, name = $map->{map_name}\n"); + if (defined $config{saveMap} && $map->{map_name} =~ /$config{saveMap}/) { + $map_index = $i; + } + } + + $messageSender->sendSelectAccessibleMapname($map_index); +} + +1; diff --git a/openkore_llm_knowledge/core/src/Network/Send.pm b/openkore_llm_knowledge/core/src/Network/Send.pm new file mode 100644 index 0000000000..1af920bb73 --- /dev/null +++ b/openkore_llm_knowledge/core/src/Network/Send.pm @@ -0,0 +1,3589 @@ +######################################################################### +# OpenKore - Message sending +# This module contains functions for sending messages to the RO server. +# +# This software is open source, licensed under the GNU General Public +# License, version 2. +# Basically, this means that you're allowed to modify and distribute +# this software. However, if you distribute modified versions, you MUST +# also distribute the source code. +# See http://www.gnu.org/licenses/gpl.html for the full license. +# +# $Revision$ +# $Id$ +# +######################################################################### +## +# MODULE DESCRIPTION: Sending messages to RO server +# +# This class contains convenience methods for sending messages to the RO +# server. +# +# Please also read <a href="https://openkore.com/wiki/Network_subsystem">the +# network subsystem overview.</a> +package Network::Send; + +use strict; +use Time::HiRes qw(time); +use Network::PacketParser; # import +use base qw(Network::PacketParser); +use utf8; +use Carp::Assert; +use Digest::MD5; +use Math::BigInt; + +# TODO: remove 'use Globals' from here, instead pass vars on +use Globals qw(%ai_v %config $bytesSent %packetDescriptions $enc_val1 $enc_val2 $char $masterServer $syncSync $accountID %timeout %talk $skillExchangeItem $net $rodexList $rodexWrite %universalCatalog %rpackets $mergeItemList $repairList %cashShop); + +use I18N qw(bytesToString stringToBytes); +use Utils qw(existsInList getHex getTickCount getCoordString makeCoordsDir); +use Misc; +use Log qw(debug); + +sub new { + my ( $class ) = @_; + my $self = $class->SUPER::new( @_ ); + + my $cryptKeys = $masterServer->{sendCryptKeys}; + if ( $cryptKeys && $cryptKeys =~ /^(0x[0-9A-Fa-f]{8})\s*,\s*(0x[0-9A-Fa-f]{8})\s*,\s*(0x[0-9A-Fa-f]{8})$/ ) { + $self->cryptKeys( hex $1, hex $2, hex $3 ); + } + + return $self; +} + +sub import { + # This code is for backward compatibility reasons, so that you can still + # write: + # sendFoo(\$remote_socket, args); + + my ($package) = caller; + # This is necessary for some weird reason. + return if ($package =~ /^Network::Send/); + + package Network::Send::Compatibility; + require Exporter; + our @ISA = qw(Exporter); + require Network::Send::ServerType0; + no strict 'refs'; + + our @EXPORT_OK; + @EXPORT_OK = (); + + my $class = shift; + if (@_) { + @EXPORT_OK = @_; + } else { + @EXPORT_OK = grep {/^send/} keys(%{Network::Send::ServerType0::}); + } + + foreach my $symbol (@EXPORT_OK) { + *{$symbol} = sub { + my $remote_socket = shift; + my $func = $Globals::messageSender->can($symbol); + if (!$func) { + die "No such function: $symbol"; + } else { + return $func->($Globals::messageSender, @_); + } + }; + } + Network::Send::Compatibility->export_to_level(1, undef, @EXPORT_OK); +} + +### CATEGORY: Class methods + +### CATEGORY: Methods + +## +# void $messageSender->encryptMessageID(r_message) +sub encryptMessageID { + my ($self, $r_message) = @_; + my $messageID = unpack("v", $$r_message); + my $messageID2 = uc(unpack("H2", substr($$r_message, 1, 1))) . uc(unpack("H2", substr($$r_message, 0, 1))); + + if ($self->{encryption}->{crypt_key_3}) { + if (sprintf("%04X",$messageID) eq $self->{packet_lut}{map_login}) { + $self->{encryption}->{crypt_key} = $self->{encryption}->{crypt_key_1}; + } elsif ($self->{net}->getState() != Network::IN_GAME) { + # Turn off keys + $self->{encryption}->{crypt_key} = 0; return; + } + + # Checking if Encryption is Activated + if ($self->{encryption}->{crypt_key} > 0) { + # Saving Last Informations for Debug Log + my $oldMID = $messageID; + my $oldKey = ($self->{encryption}->{crypt_key} >> 16) & 0x7FFF; + + # Calculating the Encryption Key + $self->{encryption}->{crypt_key} = ($self->{encryption}->{crypt_key} * $self->{encryption}->{crypt_key_3} + $self->{encryption}->{crypt_key_2}) & 0xFFFFFFFF; + + # Xoring the Message ID + $messageID = ($messageID ^ (($self->{encryption}->{crypt_key} >> 16) & 0x7FFF)) & 0xFFFF; + $$r_message = pack("v", $messageID) . substr($$r_message, 2); + + # Debug Log + debug (sprintf("Encrypted MID : [%04X]->[%04X] / KEY : [0x%04X]->[0x%04X]\n", $oldMID, $messageID, $oldKey, ($self->{encryption}->{crypt_key} >> 16) & 0x7FFF), "sendPacket", 0) if ($config{debugPacket_sent} || ($config{'debugPacket_include_dumpMethod'} && !existsInList($config{debugPacket_exclude}, $messageID2) && existsInList($config{'debugPacket_include'}, $messageID2))); + } + } else { + use bytes; + if ($self->{net}->getState() != Network::IN_GAME) { + $enc_val1 = 0; + $enc_val2 = 0; + return; + } + + my $messageID = unpack("v", $$r_message); + if ($enc_val1 != 0 && $enc_val2 != 0) { + # Prepare encryption + $enc_val1 = ((0x000343FD * $enc_val1) + $enc_val2)& 0xFFFFFFFF; + debug (sprintf("enc_val1 = %x", $enc_val1) . "\n", "sendPacket", 2); + # Encrypt message ID + $messageID = ($messageID ^ (($enc_val1 >> 16) & 0x7FFF)) & 0xFFFF; + $$r_message = pack("v", $messageID) . substr($$r_message, 2); + } + } +} + +sub cryptKeys { + my $self = shift; + $self->{encryption} = { + 'crypt_key_1' => Math::BigInt->new(shift), + 'crypt_key_2' => Math::BigInt->new(shift), + 'crypt_key_3' => Math::BigInt->new(shift), + }; +} + +## +# void $messageSender->injectMessage(String message) +# +# Send text message to the connected client's party chat. +sub injectMessage { + my ($self, $message) = @_; + my $name = stringToBytes("|"); + my $msg .= $name . stringToBytes(" : $message") . chr(0); + + # Packet Prefix Encryption Support + #$self->encryptMessageID(\$msg); + + $msg = pack("C*", 0x09, 0x01) . pack("v*", length($name) + length($message) + 12) . pack("C*",0,0,0,0) . $msg; + $self->{net}->clientSend($msg); +} + +## +# void $messageSender->injectAdminMessage(String message) +# +# Send text message to the connected client's system chat. +sub injectAdminMessage { + my ($self, $message) = @_; + $message = stringToBytes($message); + $message = pack("C*",0x9A, 0x00) . pack("v*", length($message)+5) . $message .chr(0); + + # Packet Prefix Encryption Support + #$self->encryptMessageID(\$message); + $self->{net}->clientSend($message); +} + +## +# String pinEncode(int seed, int pin) +# pin: the PIN code +# key: the encryption seed/key +# +# Another version of the PIN Encode Function, used to hide the real PIN code, using seed/key. +sub pinEncode { + # randomizePin function/algorithm by Kurama, ever_boy_, kLabMouse and Iniro. cleanups by Revok + my ($seed, $pin) = @_; + + $seed = Math::BigInt->new($seed); + my $mulfactor = 0x3498; + my $addfactor = 0x881234; + my @keypad_keys_order = ('0'..'9'); + + # calculate keys order (they are randomized based on seed value) + if (@keypad_keys_order >= 1) { + my $k = 2; + for (my $pos = 1; $pos < @keypad_keys_order; $pos++) { + $seed = $addfactor + $seed * $mulfactor & 0xFFFFFFFF; # calculate next seed value + my $replace_pos = $seed % $k; + if ($pos != $replace_pos) { + my $old_value = $keypad_keys_order[$pos]; + $keypad_keys_order[$pos] = $keypad_keys_order[$replace_pos]; + $keypad_keys_order[$replace_pos] = $old_value; + } + $k++; + } + } + # associate keys values with their position using a hash + my %keypad; + for (my $pos = 0; $pos < @keypad_keys_order; $pos++) { $keypad{@keypad_keys_order[$pos]} = $pos; } + my $pin_reply = ''; + my @pin_numbers = split('',$pin); + foreach (@pin_numbers) { $pin_reply .= $keypad{$_}; } + return $pin_reply; +} + +## +# void $messageSender->sendToServer(Bytes msg) +# +# Send a raw data to the server. +sub sendToServer { + my ($self, $msg) = @_; + my $net = $self->{net}; + + shouldnt(length($msg), 0); + return unless ($net->serverAlive); + + my $messageID = uc(unpack("H2", substr($msg, 1, 1))) . uc(unpack("H2", substr($msg, 0, 1))); + + my $hookName = "packet_send/$messageID"; + if (Plugins::hasHook($hookName)) { + my %args = ( + switch => $messageID, + data => $msg + ); + Plugins::callHook($hookName, \%args); + return if ($args{return}); + } + + # Packet Prefix Encryption Support + $self->encryptMessageID(\$msg); + + $net->serverSend($msg); + $bytesSent += length($msg); + + if ($config{debugPacket_sent} && !existsInList($config{debugPacket_exclude}, $messageID) && $config{debugPacket_include_dumpMethod} < 3) { + my $label = $packetDescriptions{Send}{$messageID} ? + "[$packetDescriptions{Send}{$messageID}]" : ''; + if ($config{debugPacket_sent} == 1) { + debug(sprintf("Sent packet : %-4s [%2d bytes] %s\n", $messageID, length($msg), $label), "sendPacket", 0); + } else { + Misc::visualDump($msg, ">> Sent packet: $messageID $label"); + } + } + + if ($config{'debugPacket_include_dumpMethod'} && !existsInList($config{debugPacket_exclude}, $messageID) && existsInList($config{'debugPacket_include'}, $messageID)) { + my $label = $packetDescriptions{Send}{$messageID} ? + "[$packetDescriptions{Send}{$messageID}]" : ''; + if ($config{debugPacket_include_dumpMethod} == 2) { + Misc::visualDump($msg, ">> Sent packet: $messageID $label"); + } elsif ($config{debugPacket_include_dumpMethod} == 3 && existsInList($config{'debugPacket_include'}, $messageID)) { + #Security concern: Dump only when you included the header in config + Misc::dumpData($msg, 1, 1); + } elsif ($config{debugPacket_include_dumpMethod} == 4) { + open my $dump, '>>', 'DUMP_LINE.txt'; + print $dump unpack('H*', $msg) . "\n"; + } elsif ($config{debugPacket_include_dumpMethod} == 5 && existsInList($config{'debugPacket_include'}, $messageID)) { + #Security concern: Dump only when you included the header in config + open my $dump, '>>', 'DUMP_HEAD.txt'; + print $dump sprintf("%-4s %2d %s%s\n", $messageID, length($msg), 'Send', $label); + } + } +} + +## +# void $messageSender->sendRaw(String raw) +# raw: space-delimited list of hex byte values +# +# Send a raw data to the server. +sub sendRaw { + my ($self, $raw) = @_; + my $msg; + my @raw = split / /, $raw; + foreach (@raw) { + $msg .= pack("C", hex($_)); + } + $self->sendToServer($msg); + debug "Sent Raw Packet: @raw\n", "sendPacket", 2; +} + +# parse/reconstruct callbacks and send* subs left for compatibility + +sub parse_master_login { + my ($self, $args) = @_; + + if (exists $args->{password_md5_hex}) { + $args->{password_md5} = pack 'H*', $args->{password_md5_hex}; + } + + if (exists $args->{password_rijndael}) { + my $key = pack('C24', (6, 169, 33, 64, 54, 184, 161, 91, 81, 46, 3, 213, 52, 18, 0, 6, 61, 175, 186, 66, 157, 158, 180, 48)); + my $chain = pack('C24', (61, 175, 186, 66, 157, 158, 180, 48, 180, 34, 218, 128, 44, 159, 172, 65, 1, 2, 4, 8, 16, 32, 128)); + my $in = pack('a24', $args->{password_rijndael}); + my $rijndael = Utils::Rijndael->new; + $rijndael->MakeKey($key, $chain, 24, 24); + $args->{password} = unpack("Z24", $rijndael->Decrypt($in, undef, 24, 0)); + } +} + +sub reconstruct_master_login { + my ($self, $args) = @_; + + $args->{ip} = sprintf("192.168.%02d.%02d", (map { int(rand(255)) } 1..2)) unless exists $args->{ip}; + unless (exists $args->{mac}) { + $args->{mac} = $config{macAddress} || sprintf("E0311E%02X%02X%02X", (map { int(rand(256)) } 1..3)); + $args->{mac} = uc($args->{mac}); + $args->{mac_hyphen_separated} = join '-', $args->{mac} =~ /(..)/g; + } + $args->{isGravityID} = 0 unless exists $args->{isGravityID}; + + if (exists $args->{password}) { + for (Digest::MD5->new) { + $_->add($args->{password}); + $args->{password_md5} = $_->clone->digest; + $args->{password_md5_hex} = $_->hexdigest; + } + + my $key = pack('C24', (6, 169, 33, 64, 54, 184, 161, 91, 81, 46, 3, 213, 52, 18, 0, 6, 61, 175, 186, 66, 157, 158, 180, 48)); + my $chain = pack('C24', (61, 175, 186, 66, 157, 158, 180, 48, 180, 34, 218, 128, 44, 159, 172, 65, 1, 2, 4, 8, 16, 32, 128)); + my $in = pack('a24', $args->{password}); + my $rijndael = Utils::Rijndael->new; + $rijndael->MakeKey($key, $chain, 24, 24); + $args->{password_rijndael} = $rijndael->Encrypt($in, undef, 24, 0); + } +} + +sub sendMasterLogin { + my ($self, $username, $password, $master_version, $version) = @_; + my $msg; + + if ( + $masterServer->{masterLogin_packet} eq '' + # TODO a way to select any packet, handled globally, something like "packet_<handler> <switch>"? + or $self->{packet_list}{$masterServer->{masterLogin_packet}} + && $self->{packet_list}{$masterServer->{masterLogin_packet}}[0] eq 'master_login' + && ($self->{packet_lut}{master_login} = $masterServer->{masterLogin_packet}) + ) { + $self->sendClientMD5Hash() unless $masterServer->{clientHash} eq ''; # this is a hack, just for testing purposes, it should be moved to the login algo later on + + $msg = $self->reconstruct({ + switch => 'master_login', + version => $version || $self->version, + master_version => $master_version, + username => $username, + password => $password, + game_code => '0011', # kRO Ragnarok game code + flag => 'G000', # Maybe this say that we are connecting from client + }); + } else { + $msg = pack("v1 V", hex($masterServer->{masterLogin_packet}) || 0x64, $version || $self->version) . + pack("a24", $username) . + pack("a24", $password) . + pack("C*", $master_version); + } + + $self->sendToServer($msg); + debug "Sent sendMasterLogin\n", "sendPacket", 2; +} + +sub secureLoginHash { + my ($self, $password, $salt, $type) = @_; + my $md5 = Digest::MD5->new; + + $password = stringToBytes($password); + if ($type % 2) { + $salt = $salt . $password; + } else { + $salt = $password . $salt; + } + $md5->add($salt); + + $md5->digest +} + +sub sendMasterSecureLogin { + my ($self, $username, $password, $salt, $version, $master_version, $type, $account) = @_; + + $self->{packet_lut}{master_login} ||= $type < 3 ? '01DD' : '01FA'; + + $self->sendToServer($self->reconstruct({ + switch => 'master_login', + version => $version || $self->version, + master_version => $master_version, + username => $username, + password_salted_md5 => $self->secureLoginHash($password, $salt, $type), + clientInfo => $account > 0 ? $account - 1 : 0, + })); +} + +sub reconstruct_game_login { + my ($self, $args) = @_; + $args->{userLevel} = 0 unless exists $args->{userLevel}; + ($args->{iAccountSID}) = $masterServer->{ip} =~ /\d+\.\d+\.\d+\.(\d+)/ unless exists $args->{iAccountSID}; + + if (exists $args->{mac}) { + my $key = pack('C16', (0x06, 0xA9, 0x21, 0x40, 0x36, 0xB8, 0xA1, 0x5B, 0x51, 0x2E, 0x03, 0xD5, 0x34, 0x12, 0x00, 0x06)); + my $chain = pack('C16', (0x3D, 0xAF, 0xBA, 0x42, 0x9D, 0x9E, 0xB4, 0x30, 0xB4, 0x22, 0xDA, 0x80, 0x2C, 0x9F, 0xAC, 0x41)); + my $mac = $config{macAddress} || sprintf("E0311E%02X%02X%02X", (map { int(rand(256)) } 1..3)); + $mac = uc($mac); + my $in = pack('a16', $mac); + + my $rijndael = Utils::Rijndael->new; + $rijndael->MakeKey($key, $chain, 16, 16); + $args->{mac} = $rijndael->Encrypt($in, undef, 16, 0); + } +} + +# TODO: $masterServer->{gameLogin_packet}? +sub sendGameLogin { + my ($self, $accountID, $sessionID, $sessionID2, $sex) = @_; + $self->sendToServer($self->reconstruct({ + switch => 'game_login', + accountID => $accountID, + sessionID => $sessionID, + sessionID2 => $sessionID2, + mac => '111111111111', + accountSex => $sex, + })); + debug "Sent sendGameLogin\n", "sendPacket", 2; +} + +sub sendCharLogin { + my ($self, $char) = @_; + $self->sendToServer($self->reconstruct({switch => 'char_login', slot => $char})); + debug "Sent sendCharLogin\n", "sendPacket", 2; +} + +sub sendMapLogin { + my ($self, $accountID, $charID, $sessionID, $sex) = @_; + my $msg; + $sex = 0 if ($sex > 1 || $sex < 0); # Sex can only be 0 (female) or 1 (male) + + if ($self->{serverType} == 0 || $self->{serverType} == 17 || $self->{serverType} == 18 || $self->{serverType} == 19 || + $self->{serverType} == 20 || $self->{serverType} == 21 || $self->{serverType} == 22) { + + $msg = $self->reconstruct({ + switch => 'map_login', + accountID => $accountID, + charID => $charID, + sessionID => $sessionID, + tick => getTickCount, + sex => $sex, + }); + + } else { #oRO and pRO + my $key; + + $key = pack("C*", 0xFA, 0x12, 0, 0x50, 0x83); + $msg = pack("C*", 0x72, 0, 0, 0, 0) . + $accountID . + $key . + $charID . + pack("C*", 0xFF, 0xFF) . + $sessionID . + pack("V", getTickCount()) . + pack("C", $sex); + } + + $self->sendToServer($msg); + debug "Sent sendMapLogin\n", "sendPacket", 2; +} + +sub sendMapLoaded { + my $self = shift; + $syncSync = pack("V", getTickCount()); + debug "Sending Map Loaded\n", "sendPacket"; + $self->sendToServer($self->reconstruct({switch => 'map_loaded'})); + Plugins::callHook('packet/sendMapLoaded'); +} + +sub reconstruct_sync { + my ($self, $args) = @_; + $args->{time} = getTickCount; +} + +sub sendSync { + my ($self, $initialSync) = @_; + # XKore mode 1 lets the client take care of syncing. + return if ($self->{net}->version == 1); + + $self->sendToServer($self->reconstruct({switch => 'sync'})); + debug "Sent Sync\n", "sendPacket", 2; +} + +sub parse_character_move { + my ($self, $args) = @_; + makeCoordsDir($args, $args->{coords}); +} + +sub reconstruct_character_move { + my ($self, $args) = @_; + + $args->{no_padding} = exists $args->{no_padding} ? $args->{no_padding} : $masterServer->{serverType} == 0; + + $args->{coords} = getCoordString(@{$args}{qw(x y)}, $args->{no_padding}); +} + +sub sendMove { + my ($self, $x, $y) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'character_move', + x => $x, + y => $y + })); + + debug "Sent move to: $x, $y\n", "sendPacket", 2; +} + +sub sendAction { # flag: 0 attack (once), 7 attack (continuous), 2 sit, 3 stand + my ($self, $monID, $flag) = @_; + + my %args; + $args{monID} = $monID; + $args{flag} = $flag; + # eventually we'll trow this hooking out so... + Plugins::callHook('packet_pre/sendAttack', \%args) if $flag == ACTION_ATTACK || $flag == ACTION_ATTACK_REPEAT; + Plugins::callHook('packet_pre/sendSit', \%args) if $flag == ACTION_SIT || $flag == ACTION_STAND; + if ($args{return}) { + $self->sendToServer($args{msg}); + return; + } + + $self->sendToServer($self->reconstruct({switch => 'actor_action', targetID => $monID, type => $flag})); + debug "Sent Action: " .$flag. " on: " .getHex($monID)."\n", "sendPacket", 2; +} + +sub parse_public_chat { + my ($self, $args) = @_; + $self->parseChat($args); +} + +sub reconstruct_public_chat { + my ($self, $args) = @_; + $self->reconstructChat($args); +} + +sub sendChat { + my ($self, $message) = @_; + $self->sendToServer($self->reconstruct({switch => 'public_chat', message => $message})); +} + +sub sendGetPlayerInfo { + my ($self, $ID) = @_; + $self->sendToServer($self->reconstruct({switch => 'actor_info_request', ID => $ID})); + debug "Sent get player info: ID - ".getHex($ID)."\n", "sendPacket", 2; +} + +sub sendGetCharacterName { + my ($self, $ID) = @_; + $self->sendToServer($self->reconstruct({switch => 'actor_name_request', ID => $ID})); + debug "Sent get character name: ID - ".getHex($ID)."\n", "sendPacket", 2; +} + +sub sendTalk { + my ($self, $ID) = @_; + delete $talk{msg}; + delete $talk{image}; + $self->sendToServer($self->reconstruct({switch => 'npc_talk', ID => $ID, type => 1})); + debug "Sent talk: ".getHex($ID)."\n", "sendPacket", 2; +} + +sub sendTalkCancel { + my ($self, $ID) = @_; + undef %talk; + delete $ai_v{'npc_talk'} if (exists $ai_v{'npc_talk'}); + $self->sendToServer($self->reconstruct({switch => 'npc_talk_cancel', ID => $ID})); + debug "Sent talk cancel: ".getHex($ID)."\n", "sendPacket", 2; +} + +sub sendTalkContinue { + my ($self, $ID) = @_; + $self->sendToServer($self->reconstruct({switch => 'npc_talk_continue', ID => $ID})); + debug "Sent talk continue: ".getHex($ID)."\n", "sendPacket", 2; +} + +sub sendTalkResponse { + my ($self, $ID, $response) = @_; + delete $talk{msg}; + delete $talk{image}; + $self->sendToServer($self->reconstruct({switch => 'npc_talk_response', ID => $ID, response => $response})); + debug "Sent talk respond: ".getHex($ID).", $response\n", "sendPacket", 2; +} + +sub sendTalkNumber { + my ($self, $ID, $number) = @_; + delete $talk{msg}; + delete $talk{image}; + $self->sendToServer($self->reconstruct({switch => 'npc_talk_number', ID => $ID, value => $number})); + debug "Sent talk number: ".getHex($ID).", $number\n", "sendPacket", 2; +} + +sub sendTalkText { + my ($self, $ID, $input) = @_; + delete $talk{msg}; + delete $talk{image}; + $input = stringToBytes($input); + $self->sendToServer($self->reconstruct({ + switch => 'npc_talk_text', + len => length($input)+length($ID)+5, + ID => $ID, + text => $input + })); + debug "Sent talk text: ".getHex($ID).", $input\n", "sendPacket", 2; +} + +sub parse_private_message { + my ($self, $args) = @_; + $args->{privMsg} = bytesToString($args->{privMsg}); + Misc::stripLanguageCode(\$args->{privMsg}); + $args->{privMsgUser} = bytesToString($args->{privMsgUser}); +} + +sub reconstruct_private_message { + my ($self, $args) = @_; + $args->{privMsg} = '|00' . $args->{privMsg} if $masterServer->{chatLangCode}; + $args->{privMsg} = stringToBytes($args->{privMsg}); + $args->{privMsgUser} = stringToBytes($args->{privMsgUser}); +} + +sub sendPrivateMsg { + my ($self, $user, $message) = @_; + $self->sendToServer($self->reconstruct({ switch => 'private_message', privMsg => $message, privMsgUser => $user, })); +} + +sub sendLook { + my ($self, $body, $head) = @_; + $self->sendToServer($self->reconstruct({switch => 'actor_look_at', body => $body, head => $head})); + debug "Sent look: $body $head\n", "sendPacket", 2; + @{$char->{look}}{qw(body head)} = ($body, $head); +} + +sub sendTake { + my ($self, $itemID) = @_; + $self->sendToServer($self->reconstruct({switch => 'item_take', ID => $itemID})); + debug "Sent take\n", "sendPacket", 2; +} + +sub sendDrop { + my ($self, $ID, $amount) = @_; + $self->sendToServer($self->reconstruct({switch => 'item_drop', ID => $ID, amount => $amount})); + debug sprintf("Sent drop: %s x $amount\n", unpack('v', $ID)), "sendPacket", 2; +} + +sub sendItemUse { + my ($self, $ID, $targetID) = @_; + $self->sendToServer($self->reconstruct({switch => 'item_use', ID => $ID, targetID => $targetID})); + debug sprintf("Item Use: %s\n", unpack('v', $ID)), "sendPacket", 2; +} + +# for old plugin compatibility, use sendRestart instead! +sub sendRespawn { $_[0]->sendRestart(0) } + +# for old plugin compatibility, use sendRestart instead! +sub sendQuitToCharSelect { $_[0]->sendRestart(1) } + +# 0x00b2,3,restart,2 +# type: 0=respawn ; 1=return to char select +sub sendRestart { + my ($self, $type) = @_; + $self->sendToServer($self->reconstruct({switch => 'restart', type => $type})); + debug "Sent Restart: " . ($type ? 'Quit To Char Selection' : 'Respawn') . "\n", "sendPacket", 2; +} + +sub sendStorageAdd { + my ($self, $ID, $amount) = @_; + if ($config{storageAuto_type} == 1) { + $self->sendToServer($self->reconstruct({switch => 'guild_storage_item_add', ID => $ID, amount => $amount})); + } else { + $self->sendToServer($self->reconstruct({switch => 'storage_item_add', ID => $ID, amount => $amount})); + } + debug sprintf("Sent Storage Add: %s x $amount\n", unpack('v', $ID)), "sendPacket", 2; +} + +sub sendStorageGet { + my ($self, $ID, $amount) = @_; + if ($config{storageAuto_type} == 1) { + $self->sendToServer($self->reconstruct({switch => 'guild_storage_item_remove', ID => $ID, amount => $amount})); + } else { + $self->sendToServer($self->reconstruct({switch => 'storage_item_remove', ID => $ID, amount => $amount})); + } + debug sprintf("Sent Storage Get: %s x $amount\n", unpack('v', $ID)), "sendPacket", 2; +} + +sub sendStoragePassword { + my ($self, $pass, $type) = @_; + + # $pass -> 16 byte packed hex data + + $self->sendToServer($self->reconstruct({ + switch => 'storage_password', + type => $type, + pass => $pass, + })); +} + +sub reconstruct_storage_password { + my ($self, $args) = @_; + + my $aux = pack "H*", "EC62E539BB6BBC811A60C06FACCB7EC8"; + + # $type == 2 -> change password + # $type == 3 -> check password + + if ($args->{type} == 3) { + $args->{data} = pack '(a*)*', $args->{pass}, $aux; + } elsif ($args->{type} == 2) { + $args->{data} = pack '(a*)*', $aux, $args->{pass}; + } else { + ArgumentException->throw("The 'type' argument has invalid value ($args->{type})."); + } +} + +sub parse_party_chat { + my ($self, $args) = @_; + $self->parseChat($args); +} + +sub reconstruct_party_chat { + my ($self, $args) = @_; + $self->reconstructChat($args); +} + +sub sendPartyChat { + my ($self, $message) = @_; + $self->sendToServer($self->reconstruct({switch => 'party_chat', message => $message})); +} + + +sub parse_buy_bulk_vender { + my ($self, $args) = @_; + @{$args->{items}} = map {{ amount => unpack('v', $_), itemIndex => unpack('x2 v', $_) }} unpack '(a4)*', $args->{itemInfo}; +} + +sub reconstruct_buy_bulk_vender { + my ($self, $args) = @_; + # ITEM index. There were any other indexes expected to be in item buying packet? + $args->{itemInfo} = pack '(a4)*', map { pack 'v2', @{$_}{qw(amount itemIndex)} } @{$args->{items}}; +} + +# not "buy", it sells items! +sub sendBuyBulkVender { + my ($self, $venderID, $r_array, $venderCID) = @_; + $self->sendToServer($self->reconstruct({ + switch => 'buy_bulk_vender', + venderID => $venderID, + venderCID => $venderCID, + items => $r_array, + })); + debug "Sent bulk buy vender: ".(join ', ', map {"$_->{itemIndex} x $_->{amount}"} @$r_array)."\n", "sendPacket"; +} + +sub reconstruct_buy_bulk_buyer { + my ($self, $args) = @_; + my $packet_size = $self->{buy_bulk_buyer_size} || '(a6)*'; + my $packet_unpack = $self->{buy_bulk_buyer_size_unpack} || 'a2 v2'; + $args->{itemInfo} = pack($packet_size, map { pack $packet_unpack, @{$_}{qw(ID itemID amount)} } @{$args->{items}}); +} + +sub sendBuyBulkBuyer { + my ($self, $buyerID, $r_array, $buyingStoreID) = @_; + + my $len = 12 + (scalar @{$r_array} * 8); + + $self->sendToServer($self->reconstruct({ + switch => 'buy_bulk_buyer', + len => $len, + buyerID => $buyerID, + buyingStoreID => $buyingStoreID, + items => $r_array, + })); +} + +sub sendEnteringBuyer { + my ($self, $ID) = @_; + $self->sendToServer($self->reconstruct({switch => 'buy_bulk_request', ID => $ID})); + debug "Sent Entering Buyer: ID - ".getHex($ID)."\n", "sendPacket", 2; +} + +sub sendBuyBulkOpenShop { + my ($self, $limitZeny, $result, $storeName, @items) = @_; + + my $len = 89 + (($#items + 1) * 8); + + $self->sendToServer($self->reconstruct({ + switch => 'buy_bulk_openShop', + len => $len, + limitZeny => $limitZeny, + result => $result, + storeName => $storeName, + items => @items, + })); + + debug "Sent Buyer openShop Request\n", "sendPacket", 2; +} + +sub reconstruct_buy_bulk_openShop { + my ($self, $args) = @_; + my $packet_size = $self->{buy_bulk_openShop_size} || '(a8)*'; + my $packet_unpack = $self->{buy_bulk_openShop_size_unpack} || 'v2 V'; + $args->{itemInfo} = pack $packet_size, map { pack $packet_unpack, @{$_}{qw(nameID amount price)} } @{$args->{items}}; +} + +sub sendSkillUse { + my ($self, $ID, $lv, $targetID) = @_; +### need to check Hook### + my %args; + Plugins::callHook('packet_pre/sendSkillUse', \%args); + if ($args{return}) { + $self->sendToServer($args{msg}); + return; + } +########################## + $self->sendToServer($self->reconstruct({switch => 'skill_use', lv => $lv, skillID => $ID, targetID => $targetID})); + debug "Skill Use: $ID\n", "sendPacket", 2; +} + +sub sendSkillUseLoc { + my ($self, $ID, $lv, $x, $y) = @_; + $self->sendToServer($self->reconstruct({switch => 'skill_use_location', lv => $lv, skillID => $ID, x => $x, y => $y})); + debug "Skill Use on Location: $ID, ($x, $y)\n", "sendPacket", 2; +} + +sub sendGuildMasterMemberCheck { + my ($self, $ID) = @_; + $self->sendToServer($self->reconstruct({switch => 'guild_check'})); + debug "Sent Guild Master/Member Check.\n", "sendPacket"; +} + +sub sendGuildRequestInfo { + my ($self, $page) = @_; # page 0-4 + $self->sendToServer($self->reconstruct({ + switch => 'guild_info_request', + type => $page, + })); + debug "Sent Guild Request Page : ".$page."\n", "sendPacket"; +} + +sub parse_guild_chat { + my ($self, $args) = @_; + $self->parseChat($args); +} + +sub reconstruct_guild_chat { + my ($self, $args) = @_; + $self->reconstructChat($args); +} + +sub sendGuildChat { + my ($self, $message) = @_; + $self->sendToServer($self->reconstruct({switch => 'guild_chat', message => $message})); +} + +sub sendQuit { + my $self = shift; + $self->sendToServer($self->reconstruct({switch => 'quit_request', type => 0})); + debug "Sent Quit\n", "sendPacket", 2; +} + +sub sendCloseShop { + my $self = shift; + $self->sendToServer($self->reconstruct({switch => 'shop_close'})); + debug "Shop Closed\n", "sendPacket", 2; +} + +# 0x0102,6,partychangeoption,2:4 +# 0x07D7 +# note: item share changing seems disabled in newest clients +sub sendPartyOption { + my ($self, $exp, $itemPickup, $itemDivision) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'party_setting', + exp => $exp, + itemPickup => $itemPickup, + itemDivision => $itemDivision, + })); + debug "Sent Party Option\n", "sendPacket", 2; +} + +# 0x7DA +sub sendPartyLeader { + my ($self, $ID) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'party_leader', + accountID => $ID, + })); + + debug "Sent Change Party Leader ".getHex($ID)."\n", "sendPacket", 2; +} + +# 0x802 +sub sendPartyBookingRegister { + my ($self, $level, $MapID, @jobList) = @_; + + $self->sendToServer($self->reconstruct({switch => 'booking_register', level => $level, MapID => $MapID, + job0 => @jobList[0], job1 => @jobList[1], job2 => @jobList[2], + job3 => @jobList[3], job4 => @jobList[4], job5 => @jobList[5]})); + + debug "Sent Booking Register\n", "sendPacket", 2; +} + +# 0x804 +sub sendPartyBookingReqSearch { + my ($self, $level, $MapID, $job, $LastIndex, $ResultCount) = @_; + + $job = "65535" if ($job == 0); # job null = 65535 + $ResultCount = "10" if ($ResultCount == 0); # ResultCount defaut = 10 + + $self->sendToServer($self->reconstruct({switch => 'booking_search', level => $level, MapID => $MapID, job => $job, LastIndex => $LastIndex, ResultCount => $ResultCount})); + debug "Sent Booking Search\n", "sendPacket", 2; +} + +# 0x806 +sub sendPartyBookingDelete { + my $self = shift; + $self->sendToServer($self->reconstruct({switch => 'booking_delete'})); + debug "Booking Deleted\n", "sendPacket", 2; +} + +# 0x808 +sub sendPartyBookingUpdate { + my ($self, @jobList) = @_; + + $self->sendToServer($self->reconstruct({switch => 'booking_update', job0 => @jobList[0], + job1 => @jobList[1], job2 => @jobList[2], job3 => @jobList[3], + job4 => @jobList[4], job5 => @jobList[5]})); + + debug "Sent Booking Update\n", "sendPacket", 2; +} + +# 0x0827,6 +sub sendCharDelete2 { + my ($self, $charID) = @_; + + $self->sendToServer($self->reconstruct({switch => 'char_delete2', charID => $charID})); + debug "Sent sendCharDelete2\n", "sendPacket", 2; +} + +## +# switch: 0x0829,12: '0829' => ['char_delete2_accept', 'a4 a6', [qw(charID code)]], # 12 -> kRO +# switch: 0x098F,-1: '098f' => ['char_delete2_accept', 'v a4 a*', [qw(length charID code)]], -> idRO, iRO Renewal +sub sendCharDelete2Accept { + my ($self, $charID, $code) = @_; + + $self->sendToServer($self->reconstruct({switch => 'char_delete2_accept', charID => $charID, code => $code})); +} + +sub reconstruct_char_delete2_accept { + my ($self, $args) = @_; + debug "Sent sendCharDelete2Accept. CharID: $args->{charID}, Code: $args->{code}\n", "sendPacket", 2; +} + +# 0x082B,6 +sub sendCharDelete2Cancel { + my ($self, $charID) = @_; + + $self->sendToServer($self->reconstruct({switch => 'char_delete2_cancel', charID => $charID})); + debug "Sent sendCharDelete2Cancel\n", "sendPacket", 2; +} + +sub reconstruct_client_hash { + my ($self, $args) = @_; + + if (defined $args->{code}) { + # FIXME there's packet switch in that code. How to handle it correctly? + my $code = $args->{code}; + $code =~ s/^02 04 //; + + $args->{hash} = pack 'C*', map hex, split / /, $code; + + } elsif ($args->{type}) { + if ($args->{type} == 1) { + $args->{hash} = pack('C*', 0x7B, 0x8A, 0xA8, 0x90, 0x2F, 0xD8, 0xE8, 0x30, 0xF8, 0xA5, 0x25, 0x7A, 0x0D, 0x3B, 0xCE, 0x52); + } elsif ($args->{type} == 2) { + $args->{hash} = pack('C*', 0x27, 0x6A, 0x2C, 0xCE, 0xAF, 0x88, 0x01, 0x87, 0xCB, 0xB1, 0xFC, 0xD5, 0x90, 0xC4, 0xED, 0xD2); + } elsif ($args->{type} == 3) { + $args->{hash} = pack('C*', 0x42, 0x00, 0xB0, 0xCA, 0x10, 0x49, 0x3D, 0x89, 0x49, 0x42, 0x82, 0x57, 0xB1, 0x68, 0x5B, 0x85); + } elsif ($args->{type} == 4) { + $args->{hash} = pack('C*', 0x22, 0x37, 0xD7, 0xFC, 0x8E, 0x9B, 0x05, 0x79, 0x60, 0xAE, 0x02, 0x33, 0x6D, 0x0D, 0x82, 0xC6); + } elsif ($args->{type} == 5) { + $args->{hash} = pack('C*', 0xc7, 0x0A, 0x94, 0xC2, 0x7A, 0xCC, 0x38, 0x9A, 0x47, 0xF5, 0x54, 0x39, 0x7C, 0xA4, 0xD0, 0x39); + } + } +} + +# TODO: clientHash and secureLogin_requestCode is almost the same, merge +sub sendClientMD5Hash { + my ($self) = @_; + $self->sendToServer($self->reconstruct({ + switch => 'client_hash', + hash => pack('H32', $masterServer->{clientHash}), # ex 82d12c914f5ad48fd96fcf7ef4cc492d (kRO sakray != kRO main) + })); +} + +sub parse_actor_move { + my ($self, $args) = @_; + makeCoordsDir($args, $args->{coords}); +} + +sub reconstruct_actor_move { + my ($self, $args) = @_; + + $args->{no_padding} = exists $args->{no_padding} ? $args->{no_padding} : !($masterServer->{serverType} > 0); + + $args->{coords} = getCoordString(@{$args}{qw(x y)}, $args->{no_padding}); +} + +sub sendSlaveMove { + my ($self, $ID, $x, $y) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'actor_move', + ID => $ID, + x => $x, + y => $y, + })); + + debug sprintf("Sent %s move to: %d, %d\n", Actor::get($ID), $x, $y), "sendPacket", 2; +} + +sub sendFriendListReply { + my ($self, $accountID, $charID, $flag) = @_; + $self->sendToServer($self->reconstruct({ + switch => 'friend_response', + friendAccountID => $accountID, + friendCharID => $charID, + type => $flag, + })); + debug "Sent Reject friend request\n", "sendPacket"; +} + +sub sendFriendRequest { + my ($self, $name) = @_; + my $binName = stringToBytes($name); + $binName = substr($binName, 0, 24) if (length($binName) > 24); + $binName = $binName . chr(0) x (24 - length($binName)); + $self->sendToServer($self->reconstruct({ + switch => 'friend_request', + username => $binName, + + })); + debug "Sent Request to be a friend: $name\n", "sendPacket"; +} + +sub sendHomunculusCommand { + my ($self, $command, $type) = @_; # $type is ignored, $command can be 0:get stats, 1:feed or 2:fire + $self->sendToServer($self->reconstruct({ + switch => 'homunculus_command', + commandType => $type, + commandID => $command, + })); + debug "Sent Homunculus Command $command", "sendPacket", 2; +} + +sub sendPartyJoinRequestByName +{ + my ($self, $name) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'party_join_request_by_name', + partyName => stringToBytes ($name), + })); + + debug "Sent Request Join Party (by name): $name\n", "sendPacket", 2; +} + +sub sendSkillSelect { + my ($self, $skillID, $why) = @_; + $self->sendToServer($self->reconstruct({ + switch => 'skill_select', + skillID => $skillID, + why => $why, + })); + debug sprintf("Sent Skill Select (skillID: %d, why: %d)", $skillID, $why), 'sendPacket', 2; +} + +sub sendReplySyncRequestEx { + my ($self, $SyncID) = @_; + # Packing New Message and Dispatching + + $self->sendToServer(pack("v", $SyncID)); + # Debug Log + # print "Dispatching Sync Ex Reply : 0x" . $pid . "\n"; + # Debug Log + debug "Sent Reply Sync Request Ex\n", "sendPacket", 2; +} + +sub sendLoginPinCode { + my ($self, $seed, $type) = @_; + + my $pin = pinEncode($seed, $config{loginPinCode}); + my $msg; + if ($type == 0) { + $msg = $self->reconstruct({ + switch => 'send_pin_password', + accountID => $accountID, + pin => $pin, + }); + } elsif ($type == 1) { + $msg = $self->reconstruct({ + switch => 'new_pin_password', + accountID => $accountID, + pin => $pin, + }); + } + $self->sendToServer($msg); + $timeout{charlogin}{time} = time; + debug "Sent loginPinCode\n", "sendPacket", 2; +} + +sub sendCloseBuyShop { + my $self = shift; + $self->sendToServer($self->reconstruct({switch => 'buy_bulk_closeShop'})); + debug "Buying Shop Closed\n", "sendPacket", 2; +} + +sub sendRequestCashItemsList { + my $self = shift; + $self->sendToServer($self->reconstruct({switch => 'request_cashitems'})); + debug "Requesting cashItemsList\n", "sendPacket", 2; +} + +sub sendCashShopOpen { + my ($self) = @_; + $self->sendToServer($self->reconstruct({switch => 'cash_shop_open'})); + debug "Requesting sendCashShopOpen\n", "sendPacket", 2; +} + +sub sendCashShopClose { + my ($self) = @_; + $self->sendToServer($self->reconstruct({switch => 'cash_shop_close'})); + undef $cashShop{points}; + debug "Requesting sendCashShopClose\n", "sendPacket", 2; +} + +sub sendCashBuy { + my ($self, $kafra_points, $items) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'cash_shop_buy', + kafra_points => $kafra_points, + count => scalar @{$items}, + items => $items, + })); + + debug "Requesting cash shop buy\n", "sendPacket", 2; +} + +sub reconstruct_cash_shop_buy { + my ($self, $args) = @_; + + $args->{buy_info} = pack '(a*)*', map { pack 'V V v', $_->{nameID}, $_->{amount}, $_->{tab} } @{$args->{items}}; + # Some older clients (prior to 2013, I don't know the exact date) use 'v3' instead of 'V2 v' - lututui + # $args->{buy_info} = pack '(a*)*', map { pack 'v v v', $_->{nameID}, $_->{amount}, $_->{tab} } @{$args->{items}}; +} + +sub sendShowEquipPlayer { + my ($self, $ID) = @_; + $self->sendToServer($self->reconstruct({ + switch => 'view_player_equip_request', + ID => $ID + } + ) + ); + debug "Sent Show Equip Player.\n", "sendPacket", 2; +} + +# Send configurations (CZ_CONFIG). +# 02D8 <type>.L <value>.L +# type: +# 0 = show equip windows to other players +# 1 = being summoned by skills: Urgent Call, Romantic Rendezvous, Come to me, honey~ & Let's Go, Family! +# 2 = pet autofeeding +# 3 = homunculus autofeeding +# value: +# 0 = disabled +# 1 = enabled +sub sendMiscConfigSet { + my ($self, $type, $flag) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'misc_config_set', + type => $type, + flag => $flag + } + ) + ); + + debug sprintf("Sent Misc Config Type: %s Flag: %s.\n", $type, $flag), "sendPacket", 2; +} + +sub sendSlaveAttack { + my $self = shift; + my $slaveID = shift; + my $targetID = shift; + my $flag = shift; + $self->sendToServer($self->reconstruct({ + switch => 'slave_attack', + slaveID => $slaveID, + targetID => $targetID, + flag => $flag + } + ) + ); + debug "Sent Slave attack: ".getHex($targetID)."\n", "sendPacket", 2; +} + +sub sendSlaveStandBy { + my $self = shift; + my $slaveID = shift; + $self->sendToServer($self->reconstruct({ + switch => 'slave_move_to_master', + slaveID => $slaveID + } + ) + ); + debug "Sent Slave standby\n", "sendPacket", 2; +} + +# Request to equip an item +# 00A9 <index>.W <position>.W (CZ_REQ_WEAR_EQUIP). +# 0998 <index>.W <position>.L (CZ_REQ_WEAR_EQUIP_V5) +sub sendEquip { + my ($self, $ID, $type) = @_; + $self->sendToServer($self->reconstruct({ + switch => 'send_equip', + ID => $ID, + type => $type + } + ) + ); + debug sprintf("Sent Equip: %s Type: $type\n", unpack('v', $ID)), 2; +} + +# Request to add an equip to the equip switch window +# 0A97 <index>.W <position>.L +sub sendEquipSwitchAdd { + my ($self, $ID, $position) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'equip_switch_add', + ID => $ID, + position => $position, + })); + + debug sprintf("Sent Equip Switch Add Item: %s\n", unpack('v', $ID)), "sendPacket", 2; +} + +# Request to remove an equip from the equip switch window +# 0A99 <index>.W <position>.L <= 20170502 +# 0A99 <index>.W +sub sendEquipSwitchRemove { + my ($self, $ID, $position) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'equip_switch_remove', + ID => $ID, + position => $position, + })); + + debug sprintf("Sent Equip Switch Remove Item: %s\n", unpack('v', $ID)), "sendPacket", 2; +} + +# Request to do a full equip switch +# 0A9C +sub sendEquipSwitchRun { + my ($self) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'equip_switch_run' + })); + + debug "Sent Equip Switch All\n", "sendPacket", 2; +} + +# Request to do a single equip switch +# 0ACE <index>.W +sub sendEquipSwitchSingle { + my ($self, $ID) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'equip_switch_single', + ID => $ID + })); + + debug sprintf("Sent Equip Switch Single Item: %s\n", unpack('v', $ID)), "sendPacket", 2; +} + +sub sendProgress { + my ($self) = @_; + + $self->sendToServer($self->reconstruct({switch => 'notify_progress_bar_complete'})); + + debug "Sent Progress Bar Finish\n", "sendPacket", 2; +} + +sub sendDealAddItem { + my ($self, $ID, $amount) = @_; + $self->sendToServer($self->reconstruct({ + switch => 'deal_item_add', + ID => $ID, + amount => $amount + })); + debug sprintf("Sent Deal Add Item: %s, $amount\n", unpack('v', $ID)), "sendPacket", 2; +} + +## +# sendItemListWindowSelected +# @param num Number of items +# @param type 0: Change Material +# 1: Elemental Analysis (Level 1: Pure to Rough) +# 2: Elemental Analysis (Level 1: Rough to Pure) +# @param act 0: Cancel +# 1: Process +# @param items List of items [itemIndex,amount,itemName] +# @author [Cydh] +## +sub sendItemListWindowSelected { + my ($self, $num, $type, $act, $items) = @_; + my $len = ($num * 4) + 12; + $self->sendToServer($self->reconstruct({ + switch => 'item_list_window_selected', + len => $len, + type => $type, + act => $act, + items => $items, + })); + if ($act == 1) { + debug "Selected ".(scalar @{$items})." items: ".(join ', ', map {"".$_->{amount}." x ".$_->{itemName}." (binID:".$_->{itemIndex}.")"} @{$items})."\n", "sendPacket"; + } else { + debug "Selected items were canceled.\n", "sendPacket"; + } + undef $skillExchangeItem; +} + +sub reconstruct_item_list_window_selected { + my ($self, $args) = @_; + $args->{itemInfo} = pack '(a4)*', map { pack 'v2', @{$_}{qw(itemIndex amount)} } @{$args->{items}}; +} + +# Select equip for refining +# '0AA1' => ['refineui_select', 'a2' ,[qw(index)]], +# @param itemIndex OpenKore's Inventory Item Index +# @author [Cydh] +sub sendRefineUISelect { + my ($self, $itemIndex) = @_; + $self->sendToServer($self->reconstruct({ + switch => 'refineui_select', + index => $itemIndex, + })); + debug "Checking item for RefineUI\n", "sendPacket"; +} + +# Continue to refine equip +# '0AA3' => ['refineui_refine', 'a2 v C' ,[qw(index catalyst bless)]], +# @param itemIndex OpenKore's Inventory Item Index +# @param materialNameIDMaterial's NameID +# @param useCatalyst Catalyst (Blacksmith Blessing) toggle. 0 = Not using, 1 = Use catalyst +# @author [Cydh] +sub sendRefineUIRefine { + my ($self, $itemIndex, $materialNameID, $useCatalyst) = @_; + $self->sendToServer($self->reconstruct({ + switch => 'refineui_refine', + index => $itemIndex, + catalyst => $materialNameID, + bless => $useCatalyst, + })); + debug "Refining using RefineUI\n", "sendPacket"; +} + +# Cancel RefineUI usage +# '0AA4' => ['refineui_close', '' ,[qw()]], +# @author [Cydh] +sub sendRefineUIClose { + my $self = shift; + $self->sendToServer($self->reconstruct({switch => 'refineui_close'})); + debug "Closing RefineUI\n", "sendPacket"; +} + +sub sendTokenToServer { + my ($self, $username, $password, $master_version, $version, $token, $length, $ip, $port) = @_; + my $len = $length + 92; + + my $password_rijndael = $self->encrypt_password($password); + my $ip = '192.168.0.14'; + my $mac = '20CF3095572A'; + my $mac_hyphen_separated = join '-', $mac =~ /(..)/g; + + $net->serverDisconnect(); + $net->serverConnect($ip, $port);# OTP - One Time Password + + my $msg = $self->reconstruct({ + switch => 'token_login', + len => $len, # size of packet + version => $version || $self->version, + master_version => $master_version, + username => $username, + password => $password, + password_rijndael => $password_rijndael, + mac => $mac_hyphen_separated, + ip => $ip, + token => $token, + }); + + $self->sendToServer($msg); + + debug "Sent sendTokenLogin\n", "sendPacket", 2; +} + +# Send OTP code for authentication (packet 0C23) +# 0C23 <otp>.a6 <padding>.C +# otp: 6-digit One-Time Password (TOTP) +# padding: 0x00 (always 0) +sub sendOtpToServer { + my ($self, $otp) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'send_otp_login', + otp => $otp, + padding => 0x00, + })); + + debug "Sent OTP login packet: $otp\n", "sendPacket", 2; +} + +# encrypt password kRO/cRO version 2017-2018 +sub encrypt_password { + my ($self, $password) = @_; + my $password_rijndael; + if (defined $password) { + my $key = pack('C32', (0x06, 0xA9, 0x21, 0x40, 0x36, 0xB8, 0xA1, 0x5B, 0x51, 0x2E, 0x03, 0xD5, 0x34, 0x12, 0x00, 0x06, 0x06, 0xA9, 0x21, 0x40, 0x36, 0xB8, 0xA1, 0x5B, 0x51, 0x2E, 0x03, 0xD5, 0x34, 0x12, 0x00, 0x06)); + my $chain = pack('C32', (0x3D, 0xAF, 0xBA, 0x42, 0x9D, 0x9E, 0xB4, 0x30, 0xB4, 0x22, 0xDA, 0x80, 0x2C, 0x9F, 0xAC, 0x41, 0x3D, 0xAF, 0xBA, 0x42, 0x9D, 0x9E, 0xB4, 0x30, 0xB4, 0x22, 0xDA, 0x80, 0x2C, 0x9F, 0xAC, 0x41)); + my $in = pack('a32', $password); + my $rijndael = Utils::Rijndael->new; + $rijndael->MakeKey($key, $chain, 32, 32); + $password_rijndael = unpack("Z32", $rijndael->Encrypt($in, undef, 32, 0)); + return $password_rijndael; + } else { + error("Password is not configured"); + } +} + +sub sendReqRemainTime { + my ($self) = @_; + + my $msg = $self->reconstruct({ + switch => 'request_remain_time', + }); + + $self->sendToServer($msg); +} + +sub sendBlockingPlayerCancel { + my ($self) = @_; + # XKore mode 1 / 3. + return if ($self->{net}->version == 1); + my $msg = $self->reconstruct({ + switch => 'blocking_play_cancel', + }); + + $self->sendToServer($msg); +} + + +sub rodex_delete_mail { + my ($self, $type, $mailID1, $mailID2) = @_; + $self->sendToServer($self->reconstruct({ + switch => 'rodex_delete_mail', + type => $type, + mailID1 => $mailID1, + mailID2 => $mailID2, + })); +} + +sub rodex_request_zeny { + my ($self, $mailID1, $mailID2, $type) = @_; + $self->sendToServer($self->reconstruct({ + switch => 'rodex_request_zeny', + mailID1 => $mailID1, + mailID2 => $mailID2, + type => $type, + })); +} + +sub rodex_request_items { + my ($self, $mailID1, $mailID2, $type) = @_; + $self->sendToServer($self->reconstruct({ + switch => 'rodex_request_items', + mailID1 => $mailID1, + mailID2 => $mailID2, + type => $type, + })); +} + +sub rodex_cancel_write_mail { + my ($self) = @_; + $self->sendToServer($self->reconstruct({ + switch => 'rodex_cancel_write_mail', + })); + undef $rodexWrite; +} + +sub rodex_add_item { + my ($self, $ID, $amount) = @_; + $self->sendToServer($self->reconstruct({ + switch => 'rodex_add_item', + ID => $ID, + amount => $amount, + })); +} + +sub rodex_remove_item { + my ($self, $ID, $amount) = @_; + $self->sendToServer($self->reconstruct({ + switch => 'rodex_remove_item', + ID => $ID, + amount => $amount, + })); +} + +sub rodex_open_write_mail { + my ($self, $name) = @_; + $self->sendToServer($self->reconstruct({ + switch => 'rodex_open_write_mail', + name => stringToBytes($name), + })); +} + +sub rodex_checkname { + my ($self, $name) = @_; + $self->sendToServer($self->reconstruct({ + switch => 'rodex_checkname', + name => stringToBytes($name), + })); +} + +#note ! +#merge sub of 0A6E / 09EC ? [sctnightcore] +sub rodex_send_mail { + my ($self) = @_; + + my $title = stringToBytes($rodexWrite->{title}) . chr(0); + my $body = stringToBytes($rodexWrite->{body}) . chr(0); + my $pack = $self->reconstruct({ + switch => 'rodex_send_mail', + receiver => $rodexWrite->{target}{name}, + sender => stringToBytes($char->{name}), + zeny1 => $rodexWrite->{zeny}, + zeny2 => 0, + title_len => length $title, + body_len => length $body, + char_id => $rodexWrite->{target}{char_id}, + title => $title, + body => $body, + }); + + $self->sendToServer($pack); +} + +sub rodex_refresh_maillist { + my ($self, $type, $mailID1, $mailID2) = @_; + $self->sendToServer($self->reconstruct({ + switch => 'rodex_refresh_maillist', + type => $type, + mailID1 => $mailID1, + mailID2 => $mailID2, + # seems that is not current used by client/server 2019-09-16 + mailReturnID1 => 0, + mailReturnID2 => 0, + mailAccountID1 => 0, + mailAccountID2 => 0, + })); +} + +sub rodex_read_mail { + my ($self, $type, $mailID1, $mailID2) = @_; + $self->sendToServer($self->reconstruct({ + switch => 'rodex_read_mail', + type => $type, + mailID1 => $mailID1, + mailID2 => $mailID2, + })); +} + +sub rodex_next_maillist { + my ($self, $type, $mailID1, $mailID2) = @_; + $self->sendToServer($self->reconstruct({ + switch => 'rodex_next_maillist', + type => $type, + mailID1 => $mailID1, + mailID2 => $mailID2, + })); +} + +sub rodex_open_mailbox { + my ($self, $type, $mailID1, $mailID2) = @_; + $self->sendToServer($self->reconstruct({ + switch => 'rodex_open_mailbox', + type => $type, + mailID1 => $mailID1, + mailID2 => $mailID2, + # seems that is not current used by client/server 2019-09-16 + mailReturnID1 => 0, + mailReturnID2 => 0, + mailAccountID1 => 0, + mailAccountID2 => 0, + })); +} + +sub rodex_close_mailbox { + my ($self) = @_; + $self->sendToServer($self->reconstruct({ + switch => 'rodex_close_mailbox', + })); + undef $rodexList; +} + +sub sendEnteringVender { + my ($self, $accountID) = @_; + $self->sendToServer($self->reconstruct({ + switch => 'send_entering_vending', + accountID => $accountID, + })); +} + +sub sendUnequip { + my ($self, $ID) = @_; + $self->sendToServer($self->reconstruct({ + switch => 'send_unequip_item', + ID => $ID, + })); +} + +sub sendAddStatusPoint { + my ($self, $ID,$Amount) = @_; + $self->sendToServer($self->reconstruct({ + switch => 'send_add_status_point', + statusID => $ID, + Amount => '1', + })); +} + +sub sendAddSkillPoint { + my ($self, $skillID) = @_; + $self->sendToServer($self->reconstruct({ + switch => 'send_add_skill_point', + skillID => $skillID, + })); +} + +sub sendHotKeyChange { + my ($self, $args) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'hotkey_change', + idx => $args->{idx}, + type => $args->{type}, + id => $args->{id}, + lvl => $args->{lvl}, + })); +} + +sub sendQuestState { + my ($self, $questID,$state) = @_; + $self->sendToServer($self->reconstruct({ + switch => 'send_quest_state', + questID => $questID, + state => $state, #TODO:[active=0x00],[inactive=0x01] + })); + debug "Sent Quest State.\n", "sendPacket", 2; +} + +sub sendClanChat { + my ($self, $message) = @_; + $message = $char->{name}." : ".$message; + $self->sendToServer($self->reconstruct({switch => 'clan_chat', len => length($message) + 4,message => $message})); +} + +sub sendchangetitle { + my ($self, $title_id) = @_; + $self->sendToServer($self->reconstruct({ + switch => 'send_change_title', + ID => $title_id, + })); + debug "Sent Change Title.\n", "sendPacket", 2; +} + +sub sendRecallSso { + my ($self, $accountID) = @_; + $self->sendToServer($self->reconstruct({ + switch => 'recall_sso', + ID => $accountID, + })); +} + +sub sendRemoveAidSso { + my ($self, $accountID) = @_; + $self->sendToServer($self->reconstruct({ + switch => 'remove_aid_sso', + ID => $accountID, + })); +} + +sub sendMacroStart { + my ($self) = @_; + $self->sendToServer($self->reconstruct({ + switch => 'macro_start', + })); +} + +sub sendMacroStop { + my ($self) = @_; + $self->sendToServer($self->reconstruct({ + switch => 'macro_stop', + })); +} + +sub sendReqCashTabCode { + my ($self, $tabID) = @_; + $self->sendToServer($self->reconstruct({ + switch => 'req_cash_tabcode', + ID => $tabID, + })); +} + +sub parse_pet_evolution { + my ($self, $args) = @_; + @{$args->{items}} = map {{ itemIndex => unpack('v', $_), amount => unpack('x2 v', $_) }} unpack '(a4)*', $args->{itemInfo}; +} + +sub reconstruct_pet_evolution { + my ($self, $args) = @_; + $args->{itemInfo} = pack '(a4)*', map { pack 'v2', @{$_}{qw(itemIndex amount)} } @{$args->{items}}; +} + +sub sendPetEvolution { + my ($self, $peteggid, $r_array) = @_; + $self->sendToServer($self->reconstruct({ + switch => 'pet_evolution', + ID => $peteggid, + items => $r_array, + })); +} + +sub sendWeaponRefine { + my ($self, $ID) = @_; + + my $msg = $self->reconstruct({ + switch => 'refine_item', + ID => $ID, + }); + + $self->sendToServer($msg); + + debug "Sent Weapon Refine.\n", "sendPacket", 2; +} + +sub sendCooking { + my ($self, $type, $nameID) = @_; + $self->sendToServer($self->reconstruct({ + switch => 'cook_request', + nameID => $nameID, + type => $type, + })); + debug "Sent Cooking.\n", "sendPacket", 2; +} + +sub sendMakeItemRequest { + my ($self, $nameID, $material_nameID1, $material_nameID2, $material_nameID3) = @_; + $self->sendToServer($self->reconstruct({ + switch => 'make_item_request', + nameID => $nameID, + material_nameID1 => $material_nameID1, + material_nameID2 => $material_nameID2, + material_nameID3 => $material_nameID3, + })); + debug "Sent Make Item Request.\n", "sendPacket", 2; +} + +sub sendSearchStoreClose { + my ($self, $args) = @_; + + $self->sendToServer($self->reconstruct({switch => 'search_store_close'})); + + $universalCatalog{open} = 0; + + debug "Sent search store close\n", "sendPacket", 2; +} + +sub sendSearchStoreSearch { + my ($self, $args) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'search_store_info', + type => $args->{type}, + max_price => $args->{max_price}, + min_price => $args->{min_price}, + item_list => \@{$args->{item_list}}, + card_list => \@{$args->{card_list}}, + })); + + debug "Sent search store search\n", "sendPacket", 2; +} + +sub reconstruct_search_store_info { + my ($self, $args) = @_; + + $args->{item_count} = scalar(@{$args->{item_list}}); + $args->{card_count} = scalar(@{$args->{card_list}}); + + my @id_list = (@{$args->{item_list}}, @{$args->{card_list}}); + + $args->{item_card_list} = pack "(a*)*", map { pack "v", $_ } @id_list; +} + +sub sendSearchStoreRequestNextPage { + my ($self, $args) = @_; + + $self->sendToServer($self->reconstruct({switch => 'search_store_request_next_page'})); + + debug "Sent search store next page request\n", "sendPacket", 2; +} + +sub sendSearchStoreSelect { + my ($self, $args) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'search_store_select', + accountID => $args->{accountID}, + storeID => $args->{storeID}, + nameID => $args->{nameID}, + })); + + debug "Sent search store select request\n", "sendPacket", 2; +} + +sub sendNoviceDoriDori { + my ($self, $args) = @_; + + $self->sendToServer($self->reconstruct({switch => 'novice_dori_dori'})); + + debug "Sent Novice Dori Dori\n", "sendPacket", 2; +} + +sub sendChangeDress { + my ($self, $args) = @_; + + $self->sendToServer($self->reconstruct({switch => 'change_dress'})); + + debug "Sent Change Dress\n", "sendPacket", 2; +} + +sub sendFriendRemove { + my ($self, $accountID, $charID) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'friend_remove', + accountID => $accountID, + charID => $charID, + })); + + debug "Sent Remove a friend\n", "sendPacket"; +} + +sub sendRepairItem { + my ($self, $args) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'repair_item', + index => $args->{index}, + nameID => $args->{nameID}, + upgrade => $args->{upgrade}, + cards => $args->{cards}, + })); + debug ("Sent repair item index: ".$args->{index}."\n", "sendPacket", 2); +} + +sub sendAdoptRequest { + my ($self, $ID) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'adopt_request', + ID => $ID, + })); + + debug "Sent Adoption Request.\n", "sendPacket", 2; +} + +sub sendAdoptReply { + my ($self, $parentID1, $parentID2, $result) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'adopt_reply_request', + parentID1 => $parentID1, + parentID2 => $parentID2, + result => $result + })); + + debug "Sent Adoption Reply.\n", "sendPacket", 2; +} + +sub sendPrivateAirshipRequest { + my ($self, $map_name, $nameID) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'private_airship_request', + map_name => stringToBytes($map_name), + nameID => $nameID, + })); +} + +sub sendNoviceExplosionSpirits { + my ($self) = @_; + + $self->sendToServer($self->reconstruct({switch => 'novice_explosion_spirits'})); + + debug "Sent Novice Explosion Spirits\n", "sendPacket", 2; +} + +sub sendBanCheck { + my ($self, $ID) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'ban_check', + accountID => $ID, + })); + + debug "Sent Account Ban Check Request : " . getHex($ID) . "\n", "sendPacket", 2; +} + +sub sendChangeCart { + my ($self, $lvl) = @_; + + # lvl: 1..5 + $self->sendToServer($self->reconstruct({ + switch => 'change_cart', + lvl => $lvl, + })); + + debug "Sent Cart Change to : $lvl\n", "sendPacket", 2; +} + +sub sendArrowCraft { + my ($self, $nameID) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'make_arrow', + nameID => $nameID, + })); + + debug "Sent Arrowmake: $nameID\n", "sendPacket", 2; +} + +sub sendAutoSpell { + my ($self, $ID) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'auto_spell', + ID => $ID, + })); +} + +sub sendEmotion { + my ($self, $ID) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'send_emotion', + ID => $ID, + })); + + debug "Sent Emotion\n", "sendPacket", 2; +} + +sub sendWho { + my ($self) = @_; + + $self->sendToServer($self->reconstruct({switch => 'request_user_count'})); + debug "Sent Who (User Count)\n", "sendPacket", 2; +} + +sub sendNPCBuySellList { + my ($self, $ID, $type) = @_; + + # type: 0 get store list + # type: 1 get sell list + $self->sendToServer($self->reconstruct({ + switch => 'request_buy_sell_list', + ID => $ID, + type => $type, + })); + + debug "Sent get ".($type ? "buy" : "sell")." list to NPC: ".getHex($ID)."\n", "sendPacket", 2; +} + +sub sendIgnore { + my ($self, $name, $flag) = @_; + + my $nameToBytes = stringToBytes($name); + + $self->sendToServer($self->reconstruct({ + switch => 'ignore_player', + name => $nameToBytes, + flag => $flag, + })); + + debug "Sent Ignore: $name, $flag\n", "sendPacket", 2; +} + +sub sendIgnoreAll { + my ($self, $flag) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'ignore_all', + flag => $flag, + })); + + debug "Sent Ignore All: $flag\n", "sendPacket", 2; +} + +sub sendGetIgnoreList { + my ($self) = @_; + + $self->sendToServer($self->reconstruct({switch => 'get_ignore_list'})); + + debug "Sent get Ignore List.\n", "sendPacket", 2; +} + +sub sendChatRoomCreate { + my ($self, $title, $limit, $public, $password) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'chat_room_create', + limit => $limit, + public => $public, + password => stringToBytes($password), + title => stringToBytes($title), + })); + + debug "Sent Create Chat Room: $title, $limit, $public, $password\n", "sendPacket", 2; +} + +sub sendChatRoomJoin { + my ($self, $ID, $password) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'chat_room_join', + ID => $ID, + password => stringToBytes($password), + })); + + debug "Sent Join Chat Room: ".getHex($ID).", $password\n", "sendPacket", 2; +} + +sub sendChatRoomChange { + my ($self, $title, $limit, $public, $password) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'chat_room_change', + limit => $limit, + public => $public, + password => stringToBytes($password), + title => stringToBytes($title), + })); + + debug "Sent Change Chat Room: $title, $limit, $public, $password\n", "sendPacket", 2; +} + +sub sendChatRoomBestow { + my ($self, $name) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'chat_room_bestow', + name => stringToBytes($name), + + # There are two roles: + # 0 means 'admin' + # 1 means 'normal (not-admin)' + # + # Weirdly, you can only bestow the chat window if you are admin (role 0), + # and in the official client you cannot try to bestow the chat window UNLESS + # you're admin - so it always sends role 0 + # In rA and Hercules, this info is not used at all, instead it's checked whether + # you're actually the chat window admin or not. This might be exploitable in + # official servers (by lying that you're admin when you're not) but I never cared + # enough to test - lututui, Aug 2018 + role => 0, + })); + + debug "Sent Chat Room Bestow: $name\n", "sendPacket", 2; +} + +sub sendChatRoomKick { + my ($self, $name) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'chat_room_kick', + name => stringToBytes($name), + })); + + debug "Sent Chat Room Kick: $name\n", "sendPacket", 2; +} + +sub sendChatRoomLeave { + my ($self) = @_; + + $self->sendToServer($self->reconstruct({switch => 'chat_room_leave'})); + + debug "Sent Leave Chat Room\n", "sendPacket", 2; +} + +sub sendDeal { + my ($self, $ID) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'deal_initiate', + ID => $ID, + })); + + debug "Sent Initiate Deal: ".getHex($ID)."\n", "sendPacket", 2; +} + +sub sendDealReply { + my ($self, $action) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'deal_reply', + + # Action values: + # 0: Char is too far + # 1: Character does not exist + # 2: Trade failed + # 3: Accept + # 4: Cancel + # + # Weird enough, the client should only send 3/4 + # and the server is the one that can reply 0~2 - technologyguild, Dec 2009 + action => $action, + })); + + debug "Sent Deal Reply (Action: $action)\n", "sendPacket", 2; +} + +sub sendDealFinalize { + my ($self) = @_; + + $self->sendToServer($self->reconstruct({switch => 'deal_finalize'})); + + debug "Sent Deal Finalize\n", "sendPacket", 2; +} + +sub sendCurrentDealCancel { + my ($self) = @_; + + $self->sendToServer($self->reconstruct({switch => 'deal_cancel'})); + + debug "Sent Cancel Current Deal\n", "sendPacket", 2; +} + +sub sendDealTrade { + my ($self) = @_; + + $self->sendToServer($self->reconstruct({switch => 'deal_trade'})); + + debug "Sent Deal Trade\n", "sendPacket", 2; +} + +sub sendStorageClose { + my ($self) = @_; + + $self->sendToServer($self->reconstruct({switch => 'storage_close'})); + + debug "Sent Storage Close\n", "sendPacket", 2; +} + +sub sendPartyJoinRequest { + my ($self, $ID) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'party_join_request', + ID => $ID, + })); + + debug "Sent Party Request Join: ".getHex($ID)."\n", "sendPacket", 2; +} + +sub sendPartyJoin { + my ($self, $ID, $flag) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'party_join', + ID => $ID, + flag => $flag, + })); + + debug "Sent Party Join: ".getHex($ID).", $flag\n", "sendPacket", 2; +} + +sub sendPartyLeave { + my ($self) = @_; + + $self->sendToServer($self->reconstruct({switch => 'party_leave'})); + + debug "Sent Party Leave\n", "sendPacket", 2; +} + +sub sendPartyKick { + my ($self, $ID, $name) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'party_kick', + ID => $ID, + name => stringToBytes($name), + })); + + debug "Sent Party Kick: ".getHex($ID).", $name\n", "sendPacket", 2; +} + +sub sendMemo { + my ($self) = @_; + + $self->sendToServer($self->reconstruct({switch => 'memo_request'})); + + debug "Sent Memo\n", "sendPacket", 2; +} + +sub sendCompanionRelease { + my ($self) = @_; + + $self->sendToServer($self->reconstruct({switch => 'companion_release'})); + + debug "Sent Companion Release (Cart, Falcon or Pecopeco)\n", "sendPacket", 2; +} + +sub sendCartAdd { + my ($self, $ID, $amount) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'cart_add', + ID => $ID, + amount => $amount, + })); + + debug "Sent Cart Add: " . getHex($ID) . " x $amount\n", "sendPacket", 2; +} + +sub sendCartGet { + my ($self, $ID, $amount) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'cart_get', + ID => $ID, + amount => $amount, + })); + + debug "Sent Cart Get: " . getHex($ID) . " x $amount\n", "sendPacket", 2; +} + +sub sendIdentify { + my ($self, $ID) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'identify', + ID => $ID, + })); + + debug "Sent Identify: ".getHex($ID)."\n", "sendPacket", 2; +} + +sub sendCardMergeRequest { + my ($self, $cardID) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'card_merge_request', + cardID => $cardID, + })); + + debug "Sent Card Merge Request: " . getHex($cardID) . "\n", "sendPacket", 2; +} + +sub sendCardMerge { + my ($self, $cardID, $itemID) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'card_merge', + cardID => $cardID, + itemID => $itemID, + })); + + debug "Sent Card Merge: " . getHex($cardID) . ", " . getHex($itemID) . "\n", "sendPacket", 2; +} + +sub sendCharCreate { + my $self = shift; + my ($slot, $name, $str, $agi, $vit, $int, $dex, $luk, $hair_style, $hair_color, $job_id, $sex); + + if ($self->{packet_lut}{char_create} eq '0067') { + ($slot, $name, $str, $agi, $vit, $int, $dex, $luk, $hair_style, $hair_color) = @_; + } elsif ($self->{packet_lut}{char_create} eq '0970') { + ($slot, $name, $hair_style, $hair_color) = @_; + } elsif ($self->{packet_lut}{char_create} eq '0A39') { + ($slot, $name, $hair_style, $hair_color, $job_id, $sex) = @_; + $job_id ||= 0; # novice + $sex ||= 0; # female + } + $hair_color ||= 1; + $hair_style ||= 0; + + $self->sendToServer($self->reconstruct({ + switch => 'char_create', + name => stringToBytes($name), + str => $str, + agi => $agi, + vit => $vit, + int => $int, + dex => $dex, + luk => $luk, + slot => $slot, + hair_color => $hair_color, + hair_style => $hair_style, + job_id => $job_id, + sex => $sex + })); + + debug "Sent Char Create\n", "sendPacket", 2; +} + +sub sendCharDelete { + my ($self, $charID, $email) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'char_delete', + charID => $charID, + email => stringToBytes($email), + })); + + debug "Sent Char Delete\n", "sendPacket", 2; +} + +sub sendGuildAlly { + my ($self, $ID, $flag) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'guild_alliance_reply', + ID => $ID, + flag => $flag, + })); + + debug "Sent Ally Guild : ".getHex($ID).", $flag\n", "sendPacket", 2; +} + +sub sendGuildRequestEmblem { + my ($self, $guildID) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'guild_emblem_request', + guildID => $guildID, + })); + + debug "Sent Guild Request Emblem.\n", "sendPacket"; +} + +sub sendGuildBreak { + my ($self, $guildName) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'guild_break', + guildName => stringToBytes($guildName), + })); + + debug "Sent Guild Break: $guildName\n", "sendPacket", 2; +} + +sub sendWarpTele { + my ($self, $skillID, $map) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'warp_select', + # skillID: + # 26 => Teleport (Respawn/Random) + # 27 => Open Warp + skillID => $skillID, + mapName => stringToBytes($map), + })); + + debug "Sent ". ($skillID == 26 ? "Teleport" : "Open Warp") . "\n", "sendPacket", 2 +} + +sub sendStorageGetToCart { + my ($self, $ID, $amount) = @_; + if ($config{storageAuto_type} == 1) { + $self->sendToServer($self->reconstruct({ + switch => 'guild_storage_to_cart', + ID => $ID, + amount => $amount, + })); + } else { + $self->sendToServer($self->reconstruct({ + switch => 'storage_to_cart', + ID => $ID, + amount => $amount, + })); + } + + debug "Sent Storage Get From Cart: " . getHex($ID) . " x $amount\n", "sendPacket", 2; +} + +sub sendStorageAddFromCart { + my ($self, $ID, $amount) = @_; + if ($config{storageAuto_type} == 1) { + $self->sendToServer($self->reconstruct({ + switch => 'cart_to_guild_storage', + ID => $ID, + amount => $amount, + })); + } else { + $self->sendToServer($self->reconstruct({ + switch => 'cart_to_storage', + ID => $ID, + amount => $amount, + })); + } + + debug "Sent Storage Add From Cart: " . getHex($ID) . " x $amount\n", "sendPacket", 2; +} + +sub sendHomunculusName { + my ($self, $name) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'homunculus_name', + name => stringToBytes($name), + })); + + debug "Sent Homunculus Rename: $name\n", "sendPacket", 2; +} + +sub sendGuildLeave { + my ($self, $reason, $guildID, $charID) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'guild_leave', + guildID => $guildID, + accountID => $accountID, + charID => $charID, + reason => stringToBytes($reason), + })); + + debug "Sent Guild Leave: $reason\n", "sendPacket"; +} + +sub sendGuildMemberKick { + my ($self, $guildID, $accountID, $charID, $reason) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'guild_kick', + guildID => $guildID, + charID => $charID, + accountID => $accountID, + reason => stringToBytes($reason), + })); + + debug "Sent Guild Kick: ".getHex($charID)."\n", "sendPacket"; +} + +sub sendGuildCreate { + my ($self, $name, $charID) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'guild_create', + charID => $charID, + guildName => stringToBytes($name), + })); + + debug "Sent Guild Create: $name\n", "sendPacket", 2; +} + +sub sendGuildJoin { + my ($self, $ID, $flag) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'guild_join', + ID => $ID, + flag => $flag, + })); + + debug "Sent Join Guild : ".getHex($ID).", $flag\n", "sendPacket"; +} + +sub sendGuildJoinRequest { + my ($self, $ID, $charID) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'guild_join_request', + ID => $ID, + accountID => $accountID, + charID => $charID, + })); + + debug "Sent Request Join Guild: ".getHex($ID)."\n", "sendPacket"; +} + +sub sendGuildNotice { + my ($self, $guildID, $name, $notice) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'guild_notice', + guildID => $guildID, + name => stringToBytes($name), + notice => stringToBytes($notice), + })); + + debug "Sent Change Guild Notice: $notice\n", "sendPacket", 2; +} + +sub sendGuildSetAlly { + my ($self, $targetAID, $myAID, $charID) = @_; + + # this packet is for guildmaster asking to set alliance with another guildmaster + # the other sub for sendGuildAlly are responses to this sub + # kept the parameters open, but everything except $targetAID could be replaced with Global variables + # unless you plan to mess around with the alliance packet, no exploits though, I tried ;-) + # -zdivpsa + + $self->sendToServer($self->reconstruct({ + switch => 'guild_alliance_request', + targetAccountID => $targetAID, + accountID => $myAID, + charID => $charID, + })); + + debug "Sent Guild Alliance Request\n", "sendPacket", 2; +} + +sub sendPetCapture { + my ($self, $monID) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'pet_capture', + ID => $monID, + })); + debug "Sent pet capture: ".getHex($monID)."\n", "sendPacket", 2; +} + +sub sendPetMenu { + my ($self, $type) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'pet_menu', + + # Action + # 0 => info + # 1 => feed + # 2 => performance + # 3 => return to egg + # 4 => unequip accessory + action => $type, + })); + + debug "Sent Pet Menu\n", "sendPacket", 2; +} + +sub sendPetHatch { + my ($self, $ID) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'pet_hatch', + ID => $ID, + })); + + debug "Sent Incubator hatch: " . getHex($ID) . "\n", "sendPacket", 2; +} + +sub sendPetName { + my ($self, $name) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'pet_name', + name => stringToBytes($name), + })); + + debug "Sent Pet Rename: $name\n", "sendPacket", 2; +} + +sub sendPetEmotion { + my ($self, $ID) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'pet_emotion', + ID => $ID, + })); + + debug "Sent Pet Emotion: $ID\n", "sendPacket", 2; +} + + +sub sendBuyBulk { + my ($self, $r_array) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'buy_bulk', + items => \@{$r_array}, + })); + + debug("Sent bulk buy: $_->{itemID} x $_->{amount}\n", "sendPacket", 2) foreach (@{$r_array}); +} + +sub reconstruct_buy_bulk { + my ($self, $args) = @_; + my $pack = $self->{send_buy_bulk_pack} || "v2"; + + $args->{buyInfo} = pack "(a*)*", map { pack $pack, $_->{amount}, $_->{itemID} } @{$args->{items}}; +} + +sub sendSellBulk { + my ($self, $r_array) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'sell_bulk', + items => \@{$r_array}, + })); + + debug("Sent bulk sell: " . getHex($_->{ID}) . " x $_->{amount}\n", "sendPacket", 2) foreach (@{$r_array}); +} + +sub reconstruct_sell_bulk { + my ($self, $args) = @_; + + $args->{sellInfo} = pack "(a*)*", map { pack "a2 v", $_->{ID}, $_->{amount} } @{$args->{items}}; +} + +sub sendAchievementGetReward { + my ($self, $achievementID) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'achievement_get_reward', + achievementID => $achievementID, + })); +} + +sub sendTop10Alchemist { + my ($self) = @_; + + if (!$masterServer->{rankingSystemType}) { + $self->sendToServer($self->reconstruct({switch => 'rank_alchemist'})); + } else { + $self->sendTop10(1); + } + + debug "Sent Top 10 Alchemist request\n", "sendPacket", 2; +} + +sub sendTop10Blacksmith { + my ($self) = @_; + + if (!$masterServer->{rankingSystemType}) { + $self->sendToServer($self->reconstruct({switch => 'rank_blacksmith'})); + } else { + $self->sendTop10(0); + } + + debug "Sent Top 10 Blacksmith request\n", "sendPacket", 2; +} + +sub sendTop10PK { + my ($self) = @_; + + if (!$masterServer->{rankingSystemType}) { + $self->sendToServer($self->reconstruct({switch => 'rank_killer'})); + } else { + $self->sendTop10(3); + } + + debug "Sent Top 10 PK request\n", "sendPacket", 2; +} + +sub sendTop10Taekwon { + my ($self) = @_; + + if (!$masterServer->{rankingSystemType}) { + $self->sendToServer($self->reconstruct({switch => 'rank_taekwon'})); + } else { + $self->sendTop10(2); + } + + debug "Sent Top 10 Taekwon request\n", "sendPacket", 2; +} + +sub sendTop10 { + my ($self, $type) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'rank_general', + + # Type: + # 0 => Blacksmith + # 1 => Alchemist + # 2 => Taekwon + # 3 => PK + type => $type, + })); +} + +sub sendGMSummon { + my ($self, $playerName) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'gm_summon_player', + playerName => stringToBytes($playerName), + })); +} + +sub sendGMBroadcast { + my ($self, $message) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'gm_broadcast', + + # to colorize, add in front of message: micc | ssss | blue | tool ? + message => stringToBytes($message), + })); +} + +sub sendGMKick { + my ($self, $accountID) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'gm_kick', + targetAccountID => $accountID, + })); +} + +sub sendGMKickAll { + my ($self) = @_; + + $self->sendToServer($self->reconstruct({switch => 'gm_kick_all'})); +} + +sub sendGMMonsterItem { + my ($self, $name) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'gm_item_mob_create', + name => stringToBytes($name), + })); +} + +sub sendGMMapMove { + my ($self, $name, $x, $y) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'gm_move_to_map', + mapName => stringToBytes($name), + x => $x, + y => $y, + })); +} + +sub sendGMResetStateSkill { + my ($self, $type) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'gm_reset_state_skill', + + # type + # 0 => status + # 1 => skills + type => $type, + })); + + debug "Sent GM Reset State/Skill.\n", "sendPacket", 2; +} + +sub sendGMChangeMapType { + my ($self, $x, $y, $type) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'gm_change_cell_type', + x => $x, + y => $y, + + # type + # 0 => not walkable + # 1 => walkable + type => $type, + })); + + debug "Sent GM Change Map Type.\n", "sendPacket", 2; +} + +sub sendGMBroadcastLocal { + my ($self, $message) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'gm_broadcast_local', + message => stringToBytes($message), + })); +} + +sub sendGMChangeEffectState { + my ($self, $effect_state) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'gm_change_effect_state', + effect_state => $effect_state + })); + + debug "Sent GM Hide.\n", "sendPacket", 2; +} + +sub sendGMRemove { + my ($self, $playerName) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'gm_remove', + playerName => stringToBytes($playerName), + })); +} + +sub sendGMShift { + my ($self, $playerName) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'gm_shift', + playerName => stringToBytes($playerName), + })); +} + +sub sendGMRecall { + my ($self, $playerName) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'gm_recall', + playerName => stringToBytes($playerName), + })); +} + +sub sendAlignment { + my ($self, $ID, $alignment, $point) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'alignment', + targetID => $ID, + type => $alignment, + point => $point, + })); + + debug "Sent Alignment: ".getHex($ID).", $alignment\n", "sendPacket", 2; +} + +sub sendOpenShop { + my ($self, $title, $items) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'shop_open', + title => stringToBytes($title), + result => 1, + items => $items, + })); +} + +sub reconstruct_shop_open { + my ($self, $args) = @_; + + $args->{vendingInfo} = pack "(a*)*", map { pack "a2 v V", $_->{ID}, $_->{amount}, $_->{price} } @{$args->{items}}; +} + +sub sendMailboxOpen { + my ($self) = @_; + + $self->sendToServer($self->reconstruct({switch => 'mailbox_open'})); + + debug "Sent mailbox open.\n", "sendPacket", 2; +} + +sub sendMailRead { + my ($self, $mailID) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'mail_read', + mailID => $mailID, + })); + + debug "Sent read mail.\n", "sendPacket", 2; +} + +sub sendMailDelete { + my ($self, $mailID) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'mail_delete', + mailID => $mailID, + })); + + debug "Sent delete mail.\n", "sendPacket", 2; +} + +sub sendMailGetAttach { + my ($self, $mailID) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'mail_attachment_get', + mailID => $mailID, + })); + + debug "Sent mail get attachment.\n", "sendPacket", 2; +} + +sub sendMailOperateWindow { + my ($self, $flag) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'mail_remove', + flag => $flag, + })); + + debug "Sent mail remove item/zeny.\n", "sendPacket", 2; +} + +sub sendMailSetAttach { + my ($self, $amount, $ID) = @_; + + # Before setting an attachment, we must remove any zeny/item that was attached but the mail wasn't sent + # Otherwise the attachment will be lost + if ($ID) { + $self->sendMailOperateWindow(1); + } else { + $self->sendMailOperateWindow(2); + } + + $AI::temp::mailAttachAmount = $amount; + $self->sendToServer($self->reconstruct({ + switch => 'mail_attachment_set', + ID => $ID, + amount => $amount, + })); + + debug "Sent mail set attachment.\n", "sendPacket", 2; +} + +sub sendMailSend { + my ($self, $receiver, $title, $message) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'mail_send', + recipient => stringToBytes($receiver), + title => stringToBytes($title), + body_len => length $message > 255 ? 255 : length $message, + body => $message, + })); + + debug "Sent mail send.\n", "sendPacket", 2; +} + +sub reconstruct_mail_send { + my ($self, $args) = @_; + + $args->{body} = pack "Z" . $args->{body_len}, stringToBytes($args->{body}); +} + +sub sendMailReturn { + my ($self, $mailID, $sender) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'mail_return', + mailID => $mailID, + sender => $sender, + })); + + debug "Sent return mail.\n", "sendPacket", 2; +} + +sub sendAuctionAddItemCancel { + my ($self, $flag) = @_; + + $flag ||= 1; + + $self->sendToServer($self->reconstruct({ + switch => 'auction_add_item_cancel', + flag => $flag, + })); + + debug "Sent Auction Add Item Cancel.\n", "sendPacket", 2; +} + +sub sendAuctionAddItem { + my ($self, $ID, $amount) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'auction_add_item', + ID => $ID, + amount => $amount, + })); + + debug "Sent Auction Add Item.\n", "sendPacket", 2; +} + +sub sendAuctionCreate { + my ($self, $now_price, $max_price, $delete_time) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'auction_create', + now_price => $now_price, + max_price => $max_price, + delete_time => $delete_time, + })); + + debug "Sent Auction Create.\n", "sendPacket", 2; +} + +sub sendAuctionCancel { + my ($self, $ID) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'auction_cancel', + ID => $ID, + })); + + debug "Sent Auction Cancel.\n", "sendPacket", 2; +} + +sub sendAuctionBuy { + my ($self, $ID, $price) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'auction_buy', + ID => $ID, + price => $price, + })); + + debug "Sent Auction Buy.\n", "sendPacket", 2; +} + +sub sendAuctionItemSearch { + my ($self, $type, $price, $search_string, $page) = @_; + $page ||= 1; + + $self->sendToServer($self->reconstruct({ + switch => 'auction_search', + price => $price, + search_string => stringToBytes($search_string), + page => $page, + + # type + # 0 => armor + # 1 => weapon + # 2 => card + # 3 => misc + # 4 => name + # 5 => auction id + type => $type, + })); + + debug "Sent Auction Item Search.\n", "sendPacket", 2; +} + +sub sendAuctionReqMyInfo { + my ($self, $type) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'auction_info_self', + type => $type, + })); + + debug "Sent Auction Request My Info.\n", "sendPacket", 2; +} + +sub sendAuctionMySellStop { + my ($self, $ID) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'auction_sell_stop', + ID => $ID, + })); + + debug "Sent My Sell Stop.\n", "sendPacket", 2; +} + +sub sendPartyJoinRequestByNameReply { + my ($self, $accountID, $flag) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'party_join_request_by_name_reply', + accountID => $accountID, + flag => $flag, + })); + + debug "Sent reply Party Invite.\n", "sendPacket", 2; +} + +sub sendAutoRevive { + my ($self) = @_; + + $self->sendToServer($self->reconstruct({switch => 'auto_revive'})); + + debug "Sent Auto Revive.\n", "sendPacket", 2; +} + +sub sendBattlegroundChat { + my ($self, $message) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'battleground_chat', + message => ($masterServer->{chatLangCode}) ? stringToBytes("|00" . $message) : stringToBytes($message), + })); + + debug "Sent Battleground chat.\n", "sendPacket", 2; +} + +sub sendMercenaryCommand { + my ($self, $command) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'mercenary_command', + + # 0x0 => COMMAND_REQ_NONE + # 0x1 => COMMAND_REQ_PROPERTY + # 0x2 => COMMAND_REQ_DELETE + flag => $command + })); + + debug "Sent Mercenary Command $command", "sendPacket", 2; +} + +sub sendSkillUseLocInfo { + my ($self, $ID, $lvl, $x, $y, $moreinfo) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'skill_use_location_text', + lvl => $lvl, + ID => $ID, + x => $x, + y => $y, + info => $moreinfo + })); + + debug "Skill Use on Location: $ID, ($x, $y)\n", "sendPacket", 2; +} + +sub sendGMGiveMannerByName { + my ($self, $playerName) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'manner_by_name', + playerName => stringToBytes($playerName), + })); +} + +sub sendGMRequestStatus { + my ($self, $playerName) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'gm_request_status', + playerName => stringToBytes($playerName), + })); +} + +sub sendFeelSaveOk { + my ($self, $flag) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'starplace_agree', + flag => $flag, + })); + + debug "Sent FeelSaveOk.\n", "sendPacket", 2; +} + +sub sendGMReqAccName { + my ($self, $targetID) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'gm_request_account_name', + targetID => $targetID, + })); + + debug "Sent GM Request Account Name.\n", "sendPacket", 2; +} + +sub sendClientVersion { + my ($self, $version) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'client_version', + clientVersion => $version, + })); +} + +sub sendCaptchaAnswer { + my ($self, $answer) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'captcha_answer', + accountID => $accountID, + answer => $answer, + + # Strangely, this packet has fixed length (dec 32, or hex 0x20) but has it padded into it - lututui + len => (exists $rpackets{'07E7'}{length}) ? $rpackets{'07E7'}{length} : 32, + })); +} + +# kRO Client before 2010-08-03 only allow 1 item per transaction +# idRO_Renewal and iRO Chaos use this packet +# +# Since RagexeRE_2010_08_03a it's allowed for multiple items at once see Network/Send/kRO/RagexeRE_2010_08_03a.pm +sub sendCashShopBuy { + my ($self, $points, $items) = @_; + + if (scalar @{$items}) { + debug sprintf("Sent buying request from cashshop for %d items.\n", scalar @{$items}), "sendPacket", 2; + foreach my $item (@{$items}) { + $self->sendToServer($self->reconstruct({ + switch => 'cash_dealer_buy', + itemid => $item->{itemID}, + amount => $item->{amount}, + kafra_points => $points, + })); + } + } +} + +sub sendStartSkillUse { + my ($self, $ID, $lv, $targetID) = @_; + $char->{last_skill_used_is_continuous} = 1; + $char->{last_continuous_skill_used} = $ID; + $self->sendToServer($self->reconstruct({switch => 'start_skill_use', lv => $lv, skillID => $ID, targetID => $targetID})); + debug "Start Skill Use: $ID\n", "sendPacket", 2; +} + +sub sendStopSkillUse { + my ($self, $ID) = @_; + $char->{last_skill_used_is_continuous} = 0; + $char->{last_continuous_skill_used} = 0; + $self->sendToServer($self->reconstruct({switch => 'stop_skill_use',skillID => $ID})); + debug "Stop Skill Use: $ID\n", "sendPacket", 2; +} + +## +# Request to merge item +# 096E <size>.W { <index>.W }* +# @author [Cydh] +## +sub sendMergeItemRequest { + my ($self, $num, $items) = @_; + #my $len = ($num * 4) + 12; + $self->sendToServer($self->reconstruct({ + switch => 'merge_item_request', + #len => $len, + items => $items, + })); + debug "Sent merge item request: ".(join ', ', map { $_->{info}->{binID}." x ".$_->{info}->{amount} } @$items)."\n", "sendPacket"; +} + +sub reconstruct_merge_item_request { + my ($self, $args) = @_; + $args->{itemList} = pack '(a2)*', map { $_->{ID} } @{$args->{items}}; +} + +## +# Request to cancel merge item +# 0974 +# @author [Cydh] +## +sub sendMergeItemCancel { + my ($self, $args) = @_; + $self->sendToServer($self->reconstruct({ switch => 'merge_item_cancel' })); + debug "Cancel Merge item\n", "sendPacket"; + $mergeItemList = {}; +} + +#sub reconstruct_merge_item_cancel { +# my ($self, $args) = @_; +#} + +sub sendStylistChange { + my ($self, $hair_color, $hair_style, $cloth_color, $head_top, $head_mid, $head_bottom ) = @_; + $self->sendToServer($self->reconstruct({ + switch => 'stylist_change', + hair_color => $hair_color, + hair_style => $hair_style, + cloth_color => $cloth_color, + head_top => $head_top, + head_mid => $head_mid, + head_bottom => $head_bottom + })); +} + +## +# UI System +## + +# Request to open an UI window of the given type +# 0A68 <type>.B +sub sendOpenUIRequest { + my ($self, $UIType) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'open_ui_request', + UIType => $UIType, + })); + + debug "Sent Open UI Request (".$UIType.")\n", "sendPacket"; +} + +## +# Attendance System +## + +# Request from the client to retrieve today's attendance reward +# 0AEF + +sub sendAttendanceRewardRequest { + my ($self) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'attendance_reward_request', + })); + + debug "Sent Attendance Reward Request\n", "sendPacket"; +} + + +## +# Banking System +## + +# Requesting the data in bank +# 09AB <aid>L (PACKET_CZ_REQ_BANKING_CHECK) +sub sendBankingCheck { + my ($self, $accountID) = @_; + $self->sendToServer($self->reconstruct({ + switch => 'banking_check_request', + accountID => $accountID, + })); +} + +# Request Withdrawing some money from bank +# 09A9 <AID>L <Money>L (PACKET_CZ_REQ_BANKING_WITHDRAW) +sub sendBankingWithdraw { + my ($self, $accountID , $zeny) = @_; + $self->sendToServer($self->reconstruct({ + switch => 'banking_withdraw_request', + accountID => $accountID, + zeny => $zeny, + })); +} + +# Request saving some money in bank +# 09A7 <AID>L <Money>L (PACKET_CZ_REQ_BANKING_DEPOSIT) +sub sendBankingDeposit { + my ($self, $accountID , $zeny) = @_; + $self->sendToServer($self->reconstruct({ + switch => 'banking_deposit_request', + accountID => $accountID, + zeny => $zeny, + })); +} + +## +# Roulette System +## + +# Request to open the roulette window +# 0A19 (CZ_REQ_OPEN_ROULETTE) +sub sendRouletteWindowOpen { + my ($self) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'roulette_window_open', + })); + + debug "Sent Roulette Window Open\n", "sendPacket"; +} + +# Request the roulette reward data +# 0A1B (CZ_REQ_ROULETTE_INFO) +sub sendRouletteInfoRequest { + my ($self) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'roulette_info_request', + })); + + debug "Sent Roulette Info Request\n", "sendPacket"; +} + +# Notification of the client that the roulette window was closed +# 0A1D (CZ_REQ_CLOSE_ROULETTE) +sub sendRouletteClose { + my ($self) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'roulette_close', + })); + + debug "Sent Roulette Close\n", "sendPacket"; +} + +# Request to start the roulette +# 0A1F (CZ_REQ_GENERATE_ROULETTE) +sub sendRouletteStart { + my ($self) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'roulette_start', + })); + + debug "Sent Roulette Start\n", "sendPacket"; +} + +# Request to claim a prize +# 0A21 (CZ_RECV_ROULETTE_ITEM) +sub sendRouletteClaimPrize { + my ($self) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'roulette_claim_prize', + })); + + debug "Sent Roulette Claim Prize\n", "sendPacket"; +} + +## +# Market System +## + +# Send to Server confirmation that we already close NPC shop +# 09D4 +sub sendSellBuyComplete { + my ($self) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'sell_buy_complete', + })); + + debug "Sent Sell/Buy Complete\n", "sendPacket"; +} + +# Buy item from Market +# 09D6 +sub sendBuyBulkMarket { + my ($self, $r_array) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'buy_bulk_market', + items => \@{$r_array}, + })); + + debug("Sent bulk buy market: $_->{itemID} x $_->{amount}\n", "sendPacket", 2) foreach (@{$r_array}); +} + +sub reconstruct_buy_bulk_market { + my ($self, $args) = @_; + my $pack = $self->{send_buy_bulk_market_pack} || "v V"; + + $args->{buyInfo} = pack "(a*)*", map { pack $pack, $_->{itemID}, $_->{amount} } @{$args->{items}}; +} + +# Request to close current Market +# 09D8 +sub sendMarketClose { + my ($self) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'market_close', + })); + + debug "Sent Market Close\n", "sendPacket"; +} + +# Request Inventory Expansion +# 0B14 +sub sendInventoryExpansionRequest { + my ($self, $args) = @_; + $self->sendToServer($self->reconstruct({ switch => 'inventory_expansion_request' })); +} + +# Reject Inventory Expansion +# 0B19 +sub sendInventoryExpansionRejected { + my ($self, $args) = @_; + $self->sendToServer($self->reconstruct({ switch => 'inventory_expansion_rejected' })); +} + +# 0B1C (PACKET_CZ_PING) +sub sendPing { + my ($self, $args) = @_; + $self->sendToServer($self->reconstruct({ switch => 'ping' })); +} + +# 0A5A - PACKET_CZ_MACRO_DETECTOR_DOWNLOAD +# Let Server know that we already downloaded Captcha Image +sub sendMacroDetectorDownload { + my ($self) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'macro_detector_download', + })); +} + +# 0A5C - PACKET_CZ_MACRO_DETECTOR_ANSWER +# Send Captcha Answer +sub sendMacroDetectorAnswer { + my ($self, $answer) = @_; + + my $answer_bytes = stringToBytes($answer); + + $self->sendToServer($self->reconstruct({ + switch => 'macro_detector_answer', + answer => $answer_bytes, + })); +} + +# 0A69 - PACKET_CZ_CAPTCHA_PREVIEW_REQUEST +# Request to preview a captcha (privilege is required) +sub sendCaptchaPreviewRequest { + my ($self, $captcha_key) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'captcha_preview_request', + captcha_key => $captcha_key, + })); +} + +# 02CF CZ_MEMORIALDUNGEON_COMMAND +# Destroy an instance from the status window +sub sendMemorialDungeonCommand { + my ($self, $command) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'memorial_dungeon_command', + command => $command, + })); + + debug "Sent Memorial Dungeon Command\n", "sendPacket"; +} + +# 0A16 CZ_DYNAMICNPC_CREATE_REQUEST +sub sendNPCCreateRequest { + my ($self, $name) = @_; + $self->sendToServer($self->reconstruct({switch => 'dynamicnpc_create_request', ID => $name})); + debug "Sent request to create NPC by name: $name\n", "sendPacket", 2; +} + +# 0841 CH_SELECT_ACCESSIBLE_MAPNAME +sub sendSelectAccessibleMapname { + my ($self, $map_slot) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'select_accessible_mapname', + char_slot => $config{char}, + map_slot => $map_slot, + })); +} + +# 0BAF CZ_USE_PACKAGEITEM +sub sendUsePackageItem { + my ($self, $index, $itemID, $boxIndex) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'use_packageitem', + index => $index, + accountID => $accountID, + itemID => $itemID, + boxIndex => $boxIndex, + })); +} + +1; diff --git a/openkore_llm_knowledge/core/src/Plugins.pm b/openkore_llm_knowledge/core/src/Plugins.pm new file mode 100644 index 0000000000..918fad07d6 --- /dev/null +++ b/openkore_llm_knowledge/core/src/Plugins.pm @@ -0,0 +1,554 @@ +######################################################################### +# OpenKore - Plugin system +# +# This software is open source, licensed under the GNU General Public +# License, version 2. +# Basically, this means that you're allowed to modify and distribute +# this software. However, if you distribute modified versions, you MUST +# also distribute the source code. +# See http://www.gnu.org/licenses/gpl.html for the full license. +# +# $Revision$ +# $Id$ +# +######################################################################### +## +# MODULE DESCRIPTION: Plugin system +# +# This module provides an interface for handling plugins. +# See the <a href="https://openkore.com/wiki/How_to_write_plugins_for_OpenKore">Plugin Writing Tutorial</a> for more information about plugins. +# +# NOTE: Do not confuse plugins with modules! See Modules.pm for more information. + +# TODO: use events instead of printing log information directly. + +package Plugins; + +use strict; +use warnings; +use Time::HiRes qw(time sleep); +use Exception::Class ('Plugin::LoadException', 'Plugin::DeniedException'); + +use Modules 'register'; +use Globals; +use Utils qw(stringToQuark quarkToString); +use Utils::DataStructures qw(binAdd existsInList); +use Utils::ObjectList; +use Utils::Exceptions; +use Log qw(message warning); +use Translation qw(T TF); +use Settings qw(%sys); + + +############################# +### CATEGORY: Variables +############################# + +## +# String $Plugins::current_plugin +# +# When a plugin is being (re)loaded, the filename of the plugin is set in this variable. +our $current_plugin; + +## +# String $Plugins::current_plugin_folder +# +# When a plugin is being (re)loaded, the the plugin's folder is set in this variable. +our $current_plugin_folder; + +our @plugins; +our %hooks; + +use enum qw(HOOKNAME INDEX); +use enum qw(CALLBACK USER_DATA); + + +############################# +### CATEGORY: Functions +############################# + +sub c_loadAll { return 1 }; +sub c_loadSelected { return existsInList($sys{'loadPlugins_list'}, shift)}; +sub c_loadNotSelected { return !existsInList($sys{'skipPlugins_list'}, shift)}; + +sub getFilesFromDirs { + my ($dirs, $f_exts, $d_ignores, $recurse_lv) = @_; + my @files; + foreach my $dir (@{$dirs}) { + next if (!opendir(DIR, $dir)); + my @items = readdir DIR; + closedir DIR; + foreach my $item (@items) { + if (-f "$dir/$item" && $item =~ /(.*)(\.($f_exts))$/) { + push @files, {dir => $dir, name => $1, ext => $2}; + #message "dir:$dir file:$1 ext:$2\n"; + } elsif (-d "$dir/$item" && $item !~ /^(\.|$d_ignores$)/i && $recurse_lv) { + push @files, getFilesFromDirs(["$dir/$item"], $f_exts, $d_ignores, $recurse_lv-1); + } + } + } + return @files; +} + +sub getPluginsFiles { + my @folders = Settings::getPluginsFolders(); + my @files = getFilesFromDirs(\@folders, 'pl', 'cvs', 1); + return @files; +} + +sub getCondition { + my $condition; + if (!exists $sys{'loadPlugins'}) { + message T("Loading all plugins (by default)...\n", 'plugins'); + $condition = \&c_loadAll; + } elsif (!$sys{'loadPlugins'}) { + message T("Automatic loading of plugins disabled\n", 'plugins'); + return; + } elsif ($sys{'loadPlugins'} eq '1') { + message T("Loading all plugins...\n", 'plugins'); + $condition = \&c_loadAll; + } elsif ($sys{'loadPlugins'} eq '2') { + message T("Selectively loading plugins...\n", 'plugins'); + $condition = \&c_loadSelected; + } elsif ($sys{'loadPlugins'} eq '3') { + message T("Selectively skipping plugins...\n", 'plugins'); + $condition = \&c_loadNotSelected; + } + return $condition; +} + +## +# void Plugins::unloadByRegexp(regex) +# +# Unloads all registered plugins that match the regex. +sub unloadByRegexp { + my ($regexp) = @_; + my @unloadPlugins; + foreach my $plugin (@plugins) { + next if (!$plugin); + push(@unloadPlugins, $plugin) if ($plugin->{name} =~ /$regexp/); + } + unloadPlugins(\@unloadPlugins); +} + +## +# void Plugins::unloadAll() +# +# Unloads all registered plugins. +sub unloadAll { + unloadPlugins(\@plugins); + @plugins = (); +} + +## +# void Plugins::unloadPlugins(plugins) +# +# Unloads all registered plugins given in 'plugins'. +# Use this method to unload a specific list of plugins. +sub unloadPlugins { + my $unloadPlugins = shift; + my $found; + foreach my $plugin (@$unloadPlugins) { + next if (!$plugin); + unload($plugin->{name}); + $found = 1; + } + warning T("Error in function 'plugin unload' (Unload Plugin)\n" . + "The specified plugin do not exist.\n") unless $found; +} + +## +# boolean Plugins::unload(name) +# name: The name of the plugin to unload. +# Returns: 1 if the plugin has been successfully unloaded, 0 if the plugin isn't registered. +# +# Unloads a registered plugin. +sub unload { + my $name = shift; + return 0 if (!defined $name); + my $i = 0; + foreach my $plugin (@plugins) { + if ($plugin && $plugin->{name} && $plugin->{name} eq $name) { + $plugin->{unload_callback}->() if (defined $plugin->{unload_callback}); + delete $plugins[$i]; + message TF("Plugin %s unloaded.\n", $name), "system"; + return 1; + } + $i++; + } + return 0; +} + +## +# void Plugins::loadByRegexp(regex) +# +# Loads all plugins that match the regex from the plugins folder, and all plugins that +# match the regex and are one subfolder below the plugins folder. +# Plugins must have the .pl extension. +# This function ignores "loadPlugins" from sys.txt file. +sub loadByRegexp { + my ($regexp) = @_; + my @files = getPluginsFiles(); + @files = grep {$_->{name} =~ /$regexp/} @files; + my @load_files; + foreach my $file (@files) { + push(@load_files, "$file->{dir}/$file->{name}$file->{ext}"); + return if $quit; + } + loadPlugins(\@load_files); +} + +## +# void Plugins::loadAll() +# +# Loads all plugins from the plugins folder, and all plugins that are one subfolder below +# the plugins folder. Plugins must have the .pl extension. +# This function respects "loadPlugins" from sys.txt file. +sub loadAll { + my $condition = getCondition(); + return if (!$condition); + + my @files = getPluginsFiles(); + my @load_files; + foreach my $file (@files) { + push(@load_files, "$file->{dir}/$file->{name}$file->{ext}") if (&$condition($file->{name})); + return if $quit; + } + loadPlugins(\@load_files); +} + +## +# void Plugins::loadPlugins(filenames) +# +# Loads all plugins given in 'filenames'. +# Use this method to unload a specific list of plugins. +sub loadPlugins { + my $filenames = shift; + foreach my $filename (@$filenames) { + load($filename); + } +} + +## +# void Plugins::load(String file) +# file: The filename of a plugin. +# +# Loads a plugin. +# +# Throws Plugin::LoadException if it failed to load. +# Throws Plugin::DeniedException if the plugin system refused to load this plugin. This can +# happen, for example, if it detects that a plugin is incompatible. +sub load { + my $file = shift; + message(TF("Loading plugin %s...\n", $file), "plugins"); + + if (! -f $file) { + warning TF("File %s does not exist. (usage ex: plugin load plugins/macro/macro.pl)\n", $file); + } elsif ($file =~ /(^|\/)ropp\.pl$/i) { + Plugin::DeniedException->throw(TF("The ROPP plugin (ropp.pl) is obsolete and is " . + "no longer necessary. Please remove it, or %s will not work correctly.", + $Settings::NAME || "OpenKore")); + } else { + $current_plugin = $file; + $current_plugin_folder = $file; + $current_plugin_folder =~ s/(.*)[\/\\].*/$1/; + + undef $!; + undef $@; + if (!defined(do $file)) { + if ($@) { + Plugin::LoadException->throw(TF("Plugin contains syntax errors:\n%s", $@)); + } else { + Plugin::LoadException->throw("$!"); + } + } + undef $current_plugin; + undef $current_plugin_folder; + } +} + +## +# void Plugins::reloadByRegexp(regex) +# +# Reloads all registered plugins that match the regex. +sub reloadByRegexp { + my ($regexp) = @_; + my @reloadPlugins; + foreach my $plugin (@plugins) { + next if (!$plugin); + push(@reloadPlugins, $plugin) if ($plugin->{name} =~ /$regexp/); +} + reloadPlugins(\@reloadPlugins); +} + +## +# void Plugins::reloadAll() +# +# Reloads all registered plugins. +sub reloadAll { + reloadPlugins(\@plugins); +} + +## +# void Plugins::reloadPlugins(plugins) +# +# Reloads all registered plugins given in 'plugins'. +# Use this method to reload a specific list of plugins. +sub reloadPlugins { + my $reloadPlugins = shift; + my $found; + foreach my $plugin (@$reloadPlugins) { + next if (!$plugin); + reload($plugin->{name}); + $found = 1; + } + warning T("Error in function 'plugin reload' (Reload Plugin)\n" . + "The specified plugin do not exist.\n") unless $found; +} + +## +# boolean Plugins::reload(String name) +# name: The name of the plugin to reload. +# Returns: 1 on success, 0 if the plugin isn't registered. +# +# Reload a plugin. +# +# Throws Plugin::LoadException if it failed to load. +sub reload { + my $name = shift; + return 0 if (!defined $name); + my $i = 0; + foreach my $plugin (@plugins) { + if ($plugin && $plugin->{name} && $plugin->{name} eq $name) { + my $filename = $plugin->{filename}; + + if (defined $plugin->{reload_callback}) { + $plugin->{reload_callback}->() + } elsif (defined $plugin->{unload_callback}) { + $plugin->{unload_callback}->(); + } + + undef %{$plugin}; + delete $plugins[$i]; + load($filename); + return 1; + } + $i++; + } + return 0; +} + + +## +# void Plugins::register(String name, String description, [unload_callback, reload_callback]) +# name: The plugin's name. +# description: A short one-line description of the plugin. +# unload_callback: Reference to a function that will be called when the plugin is being unloaded. +# reload_callback: Reference to a function that will be called when the plugin is being reloaded. +# Returns: 1 if the plugin has been successfully registered, 0 if a plugin with the same name is already registered. +# +# Plugins should call this function when they are loaded. This function registers +# the plugin in the plugin database. Registered plugins can be unloaded by the user. +# +# In the unload/reload callback functions, plugins should delete any hook functions they added. +# See also: Plugins::addHook(), Plugins::delHook() +sub register { + my $name = shift; + return 0 if registered($name); + + my %plugin_info = ( + name => $name, + description => shift, + unload_callback => shift, + reload_callback => shift, + filename => $current_plugin + ); + + binAdd(\@plugins, \%plugin_info); + return 1; +} + + +## +# boolean Plugins::registered(String name) +# name: The plugin's name. +# Returns: 1 if the plugin's registered, 0 if it isn't. +# +# Checks whether a plugin is registered. +sub registered { + my $name = shift; + return 0 if (!defined $name); + foreach (@plugins) { + return 1 if ($_ && $_->{name} && $_->{name} eq $name); + } + return 0; +} + +## +# Plugins::addHook(String hookname, callback, [user_data]) +# hookname: Name of a hook. +# callback: Reference to the function to call. +# user_data: Additional data to pass to callback. +# Returns: A handle which can be used to remove this hook. +# +# Add a hook for $hookname. Whenever Kore calls Plugins::callHook('foo'), +# callback is also called. +# +# See also Plugins::callHook() for information about how callback is called. +# +# Example: +# # Somewhere in your plugin: +# use Plugins; +# use Log; +# +# my $hook = Plugins::addHook('AI_pre', \&ai_called); +# +# sub ai_called { +# Log::message("Kore's AI() function has been called.\n"); +# } +# +# # Somewhere in the Kore source code: +# sub AI { +# ... +# Plugins::callHook('AI_pre'); # <-- ai_called() is now also called. +# ... +# } +sub addHook { + my ($hookName, $callback, $user_data) = @_; + my $hookList = $hooks{$hookName} ||= new ObjectList(); + + my @entry; + $entry[CALLBACK] = $callback; + $entry[USER_DATA] = $user_data if defined($user_data); + + my @handle; + $handle[HOOKNAME] = stringToQuark($hookName); + $handle[INDEX] = $hookList->add(bless(\@entry, "Plugins::HookEntry")); + return bless(\@handle, 'Plugins::HookHandle'); +} + +## +# Plugins::addHooks( [hookName, callback, user_data], ... ) +# Returns: A handle, which can be used with Plugins::delHook() +# +# A convenience function for adding many hooks with one function. +# +# See also: Plugins::addHook(), Plugins::delHook() +# +# Example: +# $hooks = Plugins::addHooks( +# ['AI_pre', \&onAI_pre], +# ['mainLoop_pre', \&onMainLoop_pre, $some_user_data] +# ); +# Plugins::delHook($hooks); +# +# # The above is the same as: +# $hook1 = Plugins::addHook('AI_pre', \&onAI_pre); +# $hook2 = Plugins::addHook('mainLoop_pre', \&onMainLoop_pre); +# Plugins::delHook($hook1); +# Plugins::delHook($hook2); +sub addHooks { + my @hooks; + foreach my $params (@_) { + push @hooks, addHook(@{$params}); + } + return bless(\@hooks, "Plugins::HookHandles"); +} + +## +# Plugins::delHook(hookname, handle) +# hookname: Name of a hook. +# handle: A hook handle, as returned by Plugins::addHook() +# +# Removes a registered hook. $callback will not be called anymore. +# +# See also: Plugins::addHook() +# +# Example: +# Plugins::register('example', 'Example Plugin', \&on_unload, \&on_reload); +# my $hook = Plugins::addHook('AI_pre', \&ai_called); +# +# sub on_unload { +# Plugins::delHook($hook); +# Log::message "Example plugin unloaded.\n"; +# } +sub delHook { + my ($handle) = @_; + if (@_ > 1) { + # More than one parameter was passed. This means that the plugin + # is still using the old API. Make sure things are backwards + # compatible. + shift; + ($handle) = @_; + } + + if (UNIVERSAL::isa($handle, 'Plugins::HookHandles')) { + foreach my $singleHandle (@{$handle}) { + delHook($singleHandle); + } + undef @{$handle}; + + } elsif (UNIVERSAL::isa($handle, 'Plugins::HookHandle') && defined $handle->[HOOKNAME]) { + my $hookName = quarkToString($handle->[HOOKNAME]); + my $hookList = $hooks{$hookName}; + if ($hookList) { + my $entry = $hookList->get($handle->[INDEX]); + $hookList->remove($entry); + } + delete $handle->[HOOKNAME]; + delete $handle->[INDEX]; + undef $handle; + + if ($hookList && $hookList->size() == 0) { + delete $hooks{$hookName}; + } + + } else { + ArgumentException->throw("Invalid hook handle passed to Plugins::delHook()."); + } +} + +## +# Plugins::delHooks(hooks) +# +# An alias for Plugins::delHook(), for backwards compatibility reasons. +sub delHooks { + &delHook; +} + + +## +# void Plugins::callHook(String hookName, [argument]) +# hookName: Name of the hook. +# argument: An argument to pass to the hook's callback functions. +# +# Call all callback functions which are associated with the hook $hookName. +# Adding or removing callbacks during callHook will not affect the current call. +# +# The hook's callback function is called as follows: +# <pre class="example"> +# $callback->($hookName, $argument, userdata as passed to addHook); +# </pre> +# +# See also: Plugins::addHook() +sub callHook { + my ($hookName, $argument) = @_; + my $hookList = $hooks{$hookName}; + if ($hookList) { + my @items = @{ $hookList->getItems }; + foreach my $entry (@items) { + $entry->[CALLBACK]->($hookName, $argument, $entry->[USER_DATA]); + } + } +} + +## +# boolean Plugins::hasHook(String hookName) +# +# Check whether there are any hooks registered for the specified hook name. +sub hasHook { + my ($hookName) = @_; + return defined $hooks{$hookName}; +} + +1; diff --git a/openkore_llm_knowledge/core/src/Settings.pm b/openkore_llm_knowledge/core/src/Settings.pm new file mode 100644 index 0000000000..800030b453 --- /dev/null +++ b/openkore_llm_knowledge/core/src/Settings.pm @@ -0,0 +1,917 @@ +######################################################################### +# OpenKore - Settings +# Copyright (c) 2007 OpenKore Developers +# +# This software is open source, licensed under the GNU General Public +# License, version 2. +# Basically, this means that you're allowed to modify and distribute +# this software. However, if you distribute modified versions, you MUST +# also distribute the source code. +# See http://www.gnu.org/licenses/gpl.html for the full license. +# +# $Revision$ +# $Id$ +# +######################################################################### +## +# MODULE DESCRIPTION: Settings and configuration files management. +# +# Core OpenKore settings, such as OpenKore's program name and version number, +# are stored by this module. +# +# OpenKore uses two kinds of data files: +# `l +# - Control files. These configuration files define character-specific +# behavior and can be changed at any time. +# - Table files. These files contain character-independent, but server-specific +# information that OpenKore needs. These files are read-mostly (don't change +# often). +# `l` +# This module is also responsible for data file management. It allows one to: +# `l +# - Register control or table files by name. +# - Locate control or table files from multiple possible locations. +# - (Re)load control or table files based on some search criteria. +# `l` +# Most of the functions for parsing configuration files are located in +# FileParsers.pm, while the variables which contain the configuration data are +# in Globals.pm. +# +# Finally, the Settings module provides support functions for commandline +# argument parsing. +package Settings; + +use strict; +use lib 'src/deps'; +use lib 'src'; +use Exporter; +use base qw(Exporter); +use Carp::Assert; +use FindBin qw($RealBin); +use Getopt::Long; +use File::Path qw/make_path/; +use File::Spec; +use Translation qw(T TF); +use Utils::ObjectList; +use Utils::Exceptions; +use List::MoreUtils qw( uniq ); +use Globals qw( %config @servers ); + +use enum qw(CONTROL_FILE_TYPE TABLE_FILE_TYPE); + + +################################### +### CATEGORY: Constants +################################### + +## +# String $Settings::NAME +# +# The name of this program, usually "OpenKore". + +## +# String $Settings::VERSION +# +# The version number of this program. + +# Translation Comment: Strings for the name and version of the application +our $NAME = 'OpenKore'; +our $VERSION = 'what-will-become-2.1'; +# Translation Comment: Version String +#our $SVN = T(" (SVN Version) "); +our $WEBSITE = 'https://openkore.com/'; +# Translation Comment: Version String +our $versionText = "*** $NAME ${VERSION} ( version " . getRevisionString() . ' ) - ' . T("Custom Ragnarok Online client") . " ***\n*** $WEBSITE ***\n"; +our $welcomeText = TF("Welcome to %s.", $NAME); + + +# Data file folders. +our @controlFolders; +our @tablesFolders; +our @pluginsFolders; +# The registered data files. +our $files; +# System configuration. +our %sys; +our %options; + +our $fields_folder; +our $logs_folder; +our $maps_folder; + +our $config_file; +our $mon_control_file; +our $items_control_file; +our $shop_file; +our $buyer_shop_file; +our $recvpackets_name; + +# The base log file names, as set by the command line. +our $base_chat_log_file; +our $base_console_log_file; +our $base_storage_log_file; +our $base_shop_log_file; +our $base_monster_log_file; +our $base_player_log_file; +our $base_item_log_file; +our $base_dead_log_file; + +# The final log file names, as modified by logAppendUsername, etc. +our $chat_log_file; +our $console_log_file; +our $storage_log_file; +our $shop_log_file; +our $monster_log_file; +our $player_log_file; +our $item_log_file; +our $dead_log_file; + +our $sys_file; + +our $interface; +our $lockdown; +our $starting_ai; +our $command; +our $no_connect; + + +my $pathDelimiter = ($^O eq 'MSWin32') ? ';' : ':'; + +sub MODINIT { + $files = new ObjectList(); +} +use Modules 'register'; +our @EXPORT_OK = qw(%sys %options); + + +################################### +### CATEGORY: Public functions +################################### + +## +# int Settings::parseArguments() +# Returns: 1 on success, 0 if a 'usage' text should be displayed. +# +# Parse commandline arguments. Various variables within the Settings +# module will be filled with values. +# +# This function will also attempt to create necessary folders. If +# one of the folders cannot be created, then an IOException is thrown, +# although the variables are already filled. +# +# If the arguments are not correct, then an ArgumentException is thrown. +sub parseArguments { + # my %options; + + undef $fields_folder; + undef $logs_folder; + undef $maps_folder; + undef $config_file; + undef $mon_control_file; + undef $items_control_file; + undef $shop_file; + undef $buyer_shop_file; + undef $chat_log_file; + undef $console_log_file; + undef $storage_log_file; + undef $sys_file; + undef $interface; + undef $lockdown; + + # Allow plugins to have their own command line options. + Getopt::Long::Configure( 'pass_through' ); + + GetOptions( + 'control=s', \$options{control}, + 'tables=s', \$options{tables}, + 'plugins=s', \$options{plugins}, + 'fields=s', \$fields_folder, + 'logs=s', \$logs_folder, + 'maps=s', \$maps_folder, + + 'config=s', \$config_file, + 'mon_control=s', \$mon_control_file, + 'items_control=s', \$items_control_file, + 'shop=s', \$shop_file, + 'buyer_shop=s', \$buyer_shop_file, + 'chat-log=s', \$base_chat_log_file, + 'console-log=s', \$base_console_log_file, + 'storage-log=s', \$base_storage_log_file, + 'sys=s', \$sys_file, + + 'interface=s', \$interface, + 'lockdown', \$lockdown, + 'ai=s', \$starting_ai, + 'command=s', \$command, + 'help', \$options{help}, + 'version|v', \$options{version}, + + 'no-connect', \$no_connect + ); + + if ($options{control}) { + setControlFolders(split($pathDelimiter, $options{control})); + } else { + setControlFolders("control"); + } + if ($options{tables}) { + setTablesFolders(split($pathDelimiter, $options{tables})); + } else { + setTablesFolders("tables"); + } + if ($options{plugins}) { + setPluginsFolders(split($pathDelimiter, $options{plugins})); + } else { + setPluginsFolders("plugins"); + } + + $fields_folder = "fields" if (!defined $fields_folder); + $logs_folder = "logs" if (!defined $logs_folder); + $maps_folder = "map" unless defined $maps_folder; + $base_chat_log_file ||= File::Spec->catfile($logs_folder, "chat.txt"); + $base_console_log_file ||= File::Spec->catfile($logs_folder, "console.txt"); + $base_storage_log_file ||= File::Spec->catfile($logs_folder, "storage.txt"); + $base_shop_log_file = File::Spec->catfile($logs_folder, "shop_log.txt"); + $base_monster_log_file = File::Spec->catfile($logs_folder, "monster_log.txt"); + $base_player_log_file = File::Spec->catfile($logs_folder, "player_log.txt"); + $base_item_log_file = File::Spec->catfile($logs_folder, "item_log.txt"); + $base_dead_log_file = File::Spec->catfile($logs_folder, "dead_log.txt"); + update_log_filenames(); + + if (!defined $interface) { + if ($ENV{OPENKORE_DEFAULT_INTERFACE} && $ENV{OPENKORE_DEFAULT_INTERFACE} ne "") { + $interface = $ENV{OPENKORE_DEFAULT_INTERFACE}; + } else { + $interface = "Console" + } + } + if ($starting_ai) { + $AI::AI = AI::AUTO() if $starting_ai =~ /^(on|auto)$/; + $AI::AI = AI::MANUAL() if $starting_ai =~ /^manual$/; + $AI::AI = AI::OFF() if $starting_ai =~ /^off$/; + } + + return 0 if ($options{help}); + return 0 if ($options{version}); + if (! -d $logs_folder) { + #if (!mkdir($logs_folder)) { + if (! make_path($logs_folder)) { + IOException->throw("Unable to create folder $logs_folder ($!)"); + } + } + return 1; +} + +sub update_log_filenames { + my @logAppend; + push @logAppend, "_$config{username}_$config{char}" if $config{logAppendUsername} && $config{username}; + push @logAppend, "_$servers[$config{server}]{name}" if $config{logAppendServer} && $config{server}; + my $logAppend = join '', @logAppend; + + $chat_log_file = substr( $base_chat_log_file, 0, length( $base_chat_log_file ) - 4 ) . "$logAppend.txt"; + $console_log_file = substr( $base_console_log_file, 0, length( $base_console_log_file ) - 4 ) . "$logAppend.txt"; + $storage_log_file = substr( $base_storage_log_file, 0, length( $base_storage_log_file ) - 4 ) . "$logAppend.txt"; + $shop_log_file = substr( $base_shop_log_file, 0, length( $base_shop_log_file ) - 4 ) . "$logAppend.txt"; + $monster_log_file = substr( $base_monster_log_file, 0, length( $base_monster_log_file ) - 4 ) . "$logAppend.txt"; + $player_log_file = substr( $base_player_log_file, 0, length( $base_player_log_file ) - 4 ) . "$logAppend.txt"; + $item_log_file = substr( $base_item_log_file, 0, length( $base_item_log_file ) - 4 ) . "$logAppend.txt"; + $dead_log_file = substr( $base_dead_log_file, 0, length( $base_dead_log_file ) - 4 ) . "$logAppend.txt"; +} + +## +sub parseServerType { + my $type = shift; + + my $mode = 0; # Mode is Old by Default + my $class = "ServerType0"; + my $param; + + # Remove Blanks + $type =~ s/^\s*//; + $type =~ s/\s*$//; + + $type = 0 if $type eq ''; + + # Type checking + if ($type =~ /^([0-9_]+)/) { + # Old ServerType + ($type) = $type =~ /^([0-9_]+)/; + $class = "ServerType" . $type; + } else { + # New ServerType based on Server name + ($class, $param) = $type =~ /^([a-zA-Z0-9]+)(?:_([a-zA-Z0-9_]+))?/; + $mode = 1; + } + + return ($mode, $class, $param); +} + +## +# String Settings::getUsageText() +# +# Return the usage text that should be displayed. +sub getUsageText { + my $text = qq{ + Usage: openkore.pl [options...] + + General path options: + --control=PATHS Specify folders in which to look for control files. + --tables=PATHS Specify folders in which to look for table files. + --plugins=PATH Specify folders in which to look for plugins. + For the above options, you can specify multiple paths, delimited by '$pathDelimiter'. + + --fields=PATH Specify the folder in which to look for field files. + --logs=PATH Save log files in the specified folder. + + Control files lookup options: + --config=FILENAME Which config.txt to use. + --mon_control=FILENAME Which mon_control.txt to use. + --items_control=FILENAME Which items_control.txt to use. + --shop=FILENAME Which shop.txt to use. + --chat-log=FILENAME Which chat log file to use. + --console-log=FILENAME Which console log file to use. + --storage-log=FILENAME Which storage log file to use. + --sys=FILENAME Which sys.txt to use. + + Other options: + --interface=NAME Which interface to use at startup. + --lockdown Disable potentially insecure features. + --ai Starting AI mode (on, manual, off) (default: on) + --command=COMMAND Initial command to place on the AI queue + --help Displays this help message. + --version Displays the program version. + + Developer options: + --no-connect Do not connect to any servers. + }; + my $data = { options => [] }; + Plugins::callHook( usage => $data ); + if ( @{ $data->{options} } ) { + foreach my $plugin ( uniq sort map { $_->{plugin} || 'unknown' } @{ $data->{options} } ) { + $text .= "\nOptions for the '$plugin' plugin:\n"; + foreach ( grep { $plugin eq ( $_->{plugin} || 'unknown' ) } @{ $data->{options} } ) { + $text .= sprintf "%-2s %-22s %s\n", $_->{short} || '', $_->{long} || '', $_->{description} || ''; + } + } + } + $text =~ s/\n*$/\n/s; + $text =~ s/^\n//s; + $text =~ s/^\t\t?//gm; + return $text; +} + +## +# void Settings::setControlFolders(Array<String> folders) +# +# Set the folders in which to look for control files. +sub setControlFolders { + @controlFolders = @_; +} + +sub getControlFolders { + return @controlFolders; +} + +## +# void Settings::setTablesFolders(Array<String> folders) +# +# Set the folders in which to look for table files. +sub setTablesFolders { + @tablesFolders = @_; +} + +# FIXME: should be f(Array<String> folders), not f(String folders)? +sub addTablesFolders { + if (defined(my $root = getTablepackFolder())) { + unshift @tablesFolders, grep -d, map { File::Spec->catdir($root, $_) } split ';', shift + } +} + +sub getTablesFolders { + return @tablesFolders; +} + +# undef if there is folder(s) specified in --tables, otherwise tables path +# TODO: way to reconfigure only that path? (by now it's always 'tables') +sub getTablepackFolder { + return $tablesFolders[$#tablesFolders]; +} + +## +# void Settings::setPluginsFolders(Array<String> folders) +# +# Set the folders in which to look for plugins. +sub setPluginsFolders { + @pluginsFolders = @_; +} + +## +# Array<String> Settings::getPluginsFolders() +# +# Get the folders in which to look for plugins. +sub getPluginsFolders { + return @pluginsFolders; +} + +## +# Settings::addControlFile(String name, options...) +# Returns: A handle for this data file, which can be used by Settings::removeFile() or Settings::loadByHandle(). +# +# Register a control file. This file will be eligable for (re)loading +# when one of the load() functions is called. +# +# The following options are allowed: +# `l +# - loader (required): must be either a reference to a function, or +# be an array in which the first element is a function reference. +# This function will be used to load this control file. In case +# of an array, all but the first element of that array will be passed +# to the load function as additional parameters. +# - autoSearch (boolean): whether the full filename of this control file +# should be looked up by looking into one of the folders specified by +# Settings::setControlFolders(). If disabled, it will be assumed that +# $name is a correct absolute or relative path. The default is enabled. +# `l` +sub addControlFile { + my $name = shift; + return _addFile($name, CONTROL_FILE_TYPE, @_); +} + +## +# Settings::addTableFile(String name, options...) +# +# This is like Settings::addControlFile(), but for table files. +sub addTableFile { + my $name = shift; + return _addFile($name, TABLE_FILE_TYPE, @_); +} + +## +# void Settings::removeFile(handle) +# +# Unregister a file that was registered by Settings::addControlFile() +# or Settings::addTableFile(). +sub removeFile { + my ($handle) = @_; + $files->remove($files->get($handle)); +} + +## +# void loadByHandle(handle, [Function progressHandler]) +# handle: A handle, as returned by Settings::addControlFile() or +# Settings::addTableFile(). +# progressHandler: A function which will be called when the filename +# resolved. +# +# Load or reload a data file as specified by the given data file handle. +# Throws FileNotFoundException if the file cannot be found in any of the +# search locations. +# Note that the data file loader function may throw additional exceptions. +# +# The progress handler function, if specified, will be called when the +# full filename of this data file has been resolved (that is, it has been +# found in one of the search locations), but before the file is actually +# loaded. It is useful for displaying progress reports. +# +# The progress handler function is called with two arguments: the filename, +# and the data file's type (which can be Settings::CONTROL_FILE_TYPE or +# Settings::TABLE_FILE_TYPE). +# For example: +# <pre class="example"> +# Settings::loadByHandle($handle, sub { +# my ($filename, $type) = @_; +# print "$_[0] is about to be loaded!\n"; +# if ($type == Settings::CONTROL_FILE_TYPE) { +# print "And it's a control file.\n"; +# } else { +# print "And it's a table file.\n"; +# } +# }); +# </pre> +sub loadByHandle { + my ($handle, $progressHandler) = @_; + assert(defined $handle, "Can't load by handle with undefined handle") if DEBUG; + my $object = $files->get($handle); + assert(defined $object, "Can't retrieve file in list from handle") if DEBUG; + + my $filename; + my $internalFilename = $object->{internalName} || $object->{name}; + + #hooks of type 'pre_load_' make it possible to change the filename before openkore searches for it (this filename must contain file name and file path) + my $pre_load = {internalFilename => $internalFilename, filename => $filename}; + Plugins::callHook('pre_load_'.$internalFilename, $pre_load); + if ($pre_load->{return}) { + $filename = $pre_load->{filename}; + } elsif ($object->{autoSearch} && $object->{type} == CONTROL_FILE_TYPE) { + $filename = _findFileFromFolders($object->{name}, \@controlFolders); + } elsif ($object->{autoSearch} && $object->{type} == TABLE_FILE_TYPE) { + $filename = _findFileFromFolders($object->{name}, \@tablesFolders); + } elsif (!$object->{autoSearch}) { + $filename = $object->{name}; + } + + # If we should auto-create this file, do so. + # Prefer the default folder, which is the last folder in the list of folders. + if ( ( !defined( $filename ) || !-f $filename ) && $object->{createIfMissing} ) { + my @dirs = $object->{type} == CONTROL_FILE_TYPE ? @controlFolders : @tablesFolders; + my $dir = ( grep { -d $_ } @dirs )[-1]; + my $fp; + if ( $dir && open $fp, '>>', "$dir/$object->{name}" ) { + close $fp; + $filename = "$dir/$object->{name}"; + } + } + + if (!defined($filename) || ! -f $filename) { + return unless $object->{mustExist}; + + $filename = $object->{name} || $object->{internalName} if (!defined $filename); + if ($object->{type} == CONTROL_FILE_TYPE) { + FileNotFoundException->throw( + message => TF("Cannot load control file %s", $filename), + filename => $filename); + } else { + FileNotFoundException->throw( + message => TF("Cannot load table file %s", $filename), + filename => $filename); + } + } elsif ($progressHandler) { + $progressHandler->($filename, $object->{type}); + } + + #hooks of type 'load_' make it possible to change file loader so plugins can change the parsing method of a file + #hooks of type 'pos_load_' make it possible to manipulate the extracted data after the parsing is over for a given file or simply do something when it ends. + my $load = {filename => $filename}; + my $pos_load = {filename => $filename}; + if (ref($object->{loader}) eq 'ARRAY') { + my @array = @{$object->{loader}}; + my $loader = shift @array; + $load->{args} = \@array; + Plugins::callHook('load_'.$internalFilename, $load); + unless ($load->{return}) { + $loader->($filename, @array); + } + $pos_load->{args} = \@array; + Plugins::callHook('pos_load_'.$internalFilename, $pos_load); + } else { + Plugins::callHook('load_'.$internalFilename, $load); + unless ($load->{return}) { + $object->{loader}->($filename); + } + Plugins::callHook('pos_load_'.$internalFilename, $pos_load); + } + + # Call onLoaded Handler after file beeng loaded without exceptions. + # onLoaded must be REF to CODE (sub, function, etc.) + if (defined $object->{onLoaded}) { + if (ref($object->{onLoaded}) eq 'CODE') { + $object->{onLoaded}->($filename); + } + } + $object->{path} = $filename if defined $filename; +} + +## +# void Settings::loadByRegexp(regexp, [Function progressHandler]) +# +# Calls 'loadFiles' with the list of registered data files whose name matches the given regular expression. +sub loadByRegexp { + my ($regexp, $progressHandler) = @_; + loadFiles([grep { $_->{path} =~ /$regexp/ } @{$files->getItems}], $progressHandler); +} + +## +# void Settings::loadAll([Function progressHandler]) +# +# Calls 'loadFiles' with the list of all registered data files. +sub loadAll { + my ($progressHandler) = @_; + loadFiles($files->getItems, $progressHandler); +} + +## +# void Settings::loadFiles(files, [Function progressHandler]) +# +# (Re)loads all registered data files given in 'files'. +# Use this method to load a specific list of files. +# This method follows the same contract as +# Settings::loadByHandle(), so see that method for parameter descriptions +# and exceptions. +sub loadFiles { + my ($files, $progressHandler) = @_; + + Plugins::callHook('preloadfiles', {files => $files}); + + my $i = 1; + foreach my $object (@$files) { + Plugins::callHook('loadfiles', { + files => $files, + current => $i + }); + loadByHandle($object->{index}, $progressHandler); + $i++; + } + + Plugins::callHook('postloadfiles', {files => $files}); +} + +## +# str Settings::getRevisionString() +# +# Return OpenKore's revision as a string to be displayed to the user. +sub getRevisionString { + my @revisions; + + # The best and most accurate version is the git commit sha, if available. + my $git = getGitRevision(); + if ( $git ) { + push @revisions, "git:$git"; + } + + # "Download ZIP" on github sets the file creation times to (around) the + # last time a commit was uploaded to github. This can help make a good + # guess about what version is in use. + my $time = ( stat( __FILE__ ) )[10] || ( stat( _ ) )[9]; + if ( $time ) { + my ( $sec, $min, $hour, $day, $month, $year ) = gmtime( $time ); + push @revisions, sprintf 'ctime:%04d_%02d_%02d', $year + 1900, $month + 1, $day; + } + + push @revisions, '?' if !@revisions; + + join ' ', @revisions; +} + +## +# int Settings::getGitRevision() +# +# Return OpenKore's Git revision number, or undef if that information cannot be retrieved. +sub getGitRevision { + use Git; + my $git = Git::detect_git( $RealBin ); + return if !$git; + my ( $sec, $min, $hour, $day, $month, $year ) = gmtime( $git->{timestamp} ); + sprintf '%7.7s_%04d-%02d-%02d', $git->{sha}, $year + 1900, $month + 1, $day; +} + +sub loadSysConfig { + _processSysConfig(0); +} + +sub writeSysConfig { + _processSysConfig(1); +} + + +########################################## +### CATEGORY: Data file lookup functions +########################################## + +## +# String Settings::getControlFilename(String name) +# name: A valid base file name. +# Returns: A valid filename, or undef if not found. +# Ensures: if defined($result): -f $result +# +# Get a control file by its name. This file will be looked up +# in all possible locations, as specified by earlier calls +# to Settings::setControlFolders(). +sub getControlFilename { + return _findFileFromFolders($_[0], \@controlFolders); +} + +## +# String Settings::getTableFilename(String name) +# name: A valid base file name. +# Ensures: if defined($result): -f $result +# +# Get a table file by its name. This file will be looked up +# in all possible locations, as specified by earlier calls +# to Settings::setTablesFolders(). +sub getTableFilename { + return _findFileFromFolders($_[0], \@tablesFolders); +} + +sub getConfigFilename { + if (defined $config_file) { + return $config_file; + } else { + return getControlFilename("config.txt"); + } +} + +sub setConfigFilename { + my ($new_filename) = @_; + my $current_filename = getConfigFilename(); + foreach my $object (@{$files->getItems()}) { + if ($object->{name} eq $current_filename) { + $object->{name} = $new_filename; + last; + } + } + $config_file = $new_filename; +} + +sub getMonControlFilename { + if (defined $mon_control_file) { + return $mon_control_file; + } else { + return getControlFilename("mon_control.txt"); + } +} + +sub setMonControlFilename { + my ($new_filename) = @_; + my $current_filename = getMonControlFilename(); + foreach my $object (@{$files->getItems()}) { + if ($object->{name} eq $current_filename) { + $object->{name} = $new_filename; + last; + } + } + $mon_control_file = $new_filename; +} + +sub getItemsControlFilename { + if (defined $items_control_file) { + return $items_control_file; + } else { + return getControlFilename("items_control.txt"); + } +} + +sub setItemsControlFilename { + my ($new_filename) = @_; + my $current_filename = getItemsControlFilename(); + foreach my $object (@{$files->getItems()}) { + if ($object->{name} eq $current_filename) { + $object->{name} = $new_filename; + last; + } + } + $items_control_file = $new_filename; +} + +sub getShopFilename { + if (defined $shop_file) { + return $shop_file; + } else { + return getControlFilename("shop.txt"); + } +} + +sub getBuyerShopFilename { + if (defined $buyer_shop_file) { + return $buyer_shop_file; + } else { + return getControlFilename("buyer_shop.txt"); + } +} + +sub setShopFilename { + my ($new_filename) = @_; + my $current_filename = getShopFilename(); + foreach my $object (@{$files->getItems()}) { + if ($object->{name} eq $current_filename) { + $object->{name} = $new_filename; + last; + } + } + $shop_file = $new_filename; +} + +sub setBuyerShopFilename { + my ($new_filename) = @_; + my $current_filename = getBuyerShopFilename(); + foreach my $object (@{$files->getItems()}) { + if ($object->{name} eq $current_filename) { + $object->{name} = $new_filename; + last; + } + } + $buyer_shop_file = $new_filename; +} + + +sub getSysFilename { + if (defined $sys_file) { + return $sys_file; + } else { + return getControlFilename("sys.txt"); + } +} + +sub getRecvPacketsFilename { + return ($recvpackets_name || "recvpackets.txt"); +} + +sub setRecvPacketsName { + my ($new_name) = @_; + if ($recvpackets_name ne $new_name) { + my $current_filename = getRecvPacketsFilename(); + foreach my $object (@{$files->getItems()}) { + if ($object->{name} eq $current_filename) { + $object->{name} = ($new_name || "recvpackets.txt"); + $recvpackets_name = $object->{name}; + return 1; + # last; + } + } + } + return undef; +} + +########################## +# Private methods +########################## + +sub _assertNameIsBasename { + my (undef, undef, $file) = File::Spec->splitpath($_[0]); + if ($file ne $_[0]) { + ArgumentException->throw("Name must be a valid file base name."); + } +} + +sub _findFileFromFolders { + my ($name, $folders) = @_; + _assertNameIsBasename($name); + foreach my $dir (@{$folders}) { + my $filename = File::Spec->catfile($dir, $name); + if (-f $filename) { + return $filename; + } + } + return undef; +} + +sub _addFile { + my $name = shift; + my $type = shift; + my %options = @_; + if (!$options{loader}) { + ArgumentException->throw("The 'loader' option must be specified."); + } + my $object = { + type => $type, + name => $name, + mustExist => exists($options{mustExist}) ? $options{mustExist} : 1, + createIfMissing => $options{createIfMissing}, + autoSearch => exists($options{autoSearch}) ? $options{autoSearch} : 1, + onLoaded => exists($options{onLoaded}) ? $options{onLoaded} : undef, + internalName => exists($options{internalName}) ? $options{internalName} : undef, + loader => $options{loader} + }; + my $index = $files->add(bless($object, 'Settings::Handle')); + $object->{index} = $index; + return $index; +} + +sub _processSysConfig { + my ($writeMode) = @_; + my ($f, @lines, %keysNotWritten); + my $sysFile = getSysFilename(); + return if (!$sysFile || !open($f, "<:utf8", $sysFile)); + + if ($writeMode) { + foreach my $key (keys %sys) { + $keysNotWritten{$key} = 1; + } + } + + while (!eof($f)) { + my ($line, $key, $val); + $line = <$f>; + $line =~ s/[\r\n]//g; + + if ($line eq '' || $line =~ /^#/) { + if ($writeMode) { + push @lines, $line; + } else { + next; + } + } + + ($key, $val) = split / /, $line, 2; + if ($writeMode) { + if (exists $sys{$key}) { + push @lines, "$key $sys{$key}"; + delete $keysNotWritten{$key}; + } + } else { + $sys{$key} = $val; + } + } + close $f; + + if ($writeMode && open($f, ">:utf8", $sysFile)) { + foreach my $line (@lines) { + print $f "$line\n"; + } + foreach my $key (keys %keysNotWritten) { + print $f "$key $sys{$key}\n"; + } + close $f; + } +} + +1; diff --git a/openkore_llm_knowledge/core/src/Task.pm b/openkore_llm_knowledge/core/src/Task.pm new file mode 100644 index 0000000000..978a9385ed --- /dev/null +++ b/openkore_llm_knowledge/core/src/Task.pm @@ -0,0 +1,413 @@ +######################################################################### +# OpenKore - Task framework +# Copyright (c) 2006 OpenKore Team +# +# This software is open source, licensed under the GNU General Public +# License, version 2. +# Basically, this means that you're allowed to modify and distribute +# this software. However, if you distribute modified versions, you MUST +# also distribute the source code. +# See http://www.gnu.org/licenses/gpl.html for the full license. +######################################################################### +## +# MODULE DESCRIPTION: Abstract task base class. +# +# This is the abstract base class for all tasks. Please read +# <a href="https://openkore.com/wiki/AI_subsystem_and_task_framework_overview"> +# the AI subsystem and task framework overview +# </a> +# for an overview. +# +# <h3>Notes on priority constants</h3> +# The only things you may assume about the values of priority contants are: +# `l +# - Each priority constant differ at least a value of 100 from other priority constants. +# That is, <tt>abs(c1 - c2) >= 300</tt> if c1 and c2 are two random priority constants. +# - A higher value means a higher priority. +# `l` +package Task; + +use strict; +use Carp; +use Carp::Assert; +use Modules 'register'; +use Utils::CallbackList; +use Utils::Set; + + +#################################### +### CATEGORY: Priority constants +################################### + +## +# Task::LOW_PRIORITY +# +# Indicates a low task priority. +use constant LOW_PRIORITY => 100; + +## +# Task::NORMAL_PRIORITY +# +# Indicates a normal task priority. +use constant NORMAL_PRIORITY => 500; + +## +# Task::HIGH_PRIORITY +# +# Indicates a high task priority. +use constant HIGH_PRIORITY => 1000; + +## +# Task::USER_PRIORITY +# +# Priority used for user-invoked commands. +use constant USER_PRIORITY => 5000; + + +################################### +### CATEGORY: Status constants +################################### + +## +# Task::INACTIVE +# +# Indicates that the task has just been created. +use constant INACTIVE => 0; + +## +# Task::RUNNING +# +# Indicates that the task is running. +use constant RUNNING => 1; + +## +# Task::INTERRUPTED +# +# Indicates that the task is interrupted, and not running. +use constant INTERRUPTED => 2; + +## +# Task::STOPPED +# +# Indicates that the task is stopped. A stopped task cannot resume. +use constant STOPPED => 3; + +## +# Task::DONE +# +# Indicates that the task is completed. A completed task cannot be stopped or interrupted. +use constant DONE => 4; + + +#################################### +### CATEGORY: Constructor +################################### + +## +# Task->new(options...) +# Ensures: result->getStatus() == Task::INACTIVE +# +# Create a new Task object. The following options are allowed: +# `l +# - <tt>name</tt> - A name for this task. $Task->getName() will return this name. +# If not specified, the class's name (excluding the "Task::" prefix) will be used as name. +# - <tt>priority</tt> - A priority for this task. $Task->getPriority() will return this value. +# The default priority is Task::NORMAL_PRIORITY +# - <tt>mutexes</tt> - A reference to an array of mutexes. $Task->getMutexes() will return this value. +# The default is an empty mutex list. +# `l` +sub new { + my $class = shift; + my %args = @_; + my $allowed = new Set("name", "priority", "mutexes"); + my %self; + + foreach my $key (keys %args) { + if ($allowed->has($key)) { + $self{"T_$key"} = $args{$key}; + } + } + + if (!defined $self{T_name}) { + $self{T_name} = $class; + $self{T_name} =~ s/.*:://; + } + $self{T_status} = INACTIVE; + $self{T_priority} = NORMAL_PRIORITY if (!defined $self{T_priority}); + $self{T_mutexes} = [] if (!defined $self{T_mutexes}); + + $self{T_onMutexesChanged} = new CallbackList("onMutexesChanged"); + $self{T_onStop} = new CallbackList("onStop"); + + return bless \%self, $class; +} + +sub DESTROY { + my ($self) = @_; +} + +sub _getStatusName { + my ($status) = @_; + if ($status == INACTIVE) { + return 'INACTIVE'; + } elsif ($status == RUNNING) { + return 'RUNNING'; + } elsif ($status == INTERRUPTED) { + return 'INTERRUPTED'; + } elsif ($status == STOPPED) { + return 'STOPPED'; + } elsif ($status == DONE) { + return 'DONE'; + } else { + return $status; + } +} + +sub _assertStatus { + my $self = shift; + my $currentStatus = $self->{T_status}; + foreach my $status (@_) { + if ($status == $currentStatus) { + return; + } + } + + my @expectedStatuses = map { _getStatusName($_) } @_; + Carp::confess("The current task's status should be one of: (" . join(',', @expectedStatuses) . ")\n" . + "But it's actually: " . _getStatusName($currentStatus) . "\n"); +} + + +############################ +### CATEGORY: Queries +############################ + +## +# String $Task->getName() +# Ensures: $result ne "" +# +# Returns a human-readable name for this task. +sub getName { + return $_[0]->{T_name}; +} + +## +# $Task->getStatus() +# +# Returns the task's status. This is one of Task::RUNNING, Task::INTERRUPTED, Task::STOPPED or Task::DONE. +sub getStatus { + return $_[0]->{T_status}; +} + +## +# Hash* $Task->getError() +# Requires: $self->getStatus() == Task::DONE +# +# If the status is Task::DONE, then return information about the error (if the task +# completed with an error). +# Otherwise (if the task completed successfully), return undef. +# +# The error information is a reference to a hash, containing two items: +# `l +# - code - The error code. +# - message - The error message. +# `l` +sub getError { + $_[0]->_assertStatus(DONE) if DEBUG; + return $_[0]->{T_error}; +} + +## +# Array<String>* $Task->getMutexes() +# Ensures: defined(result) +# +# Returns a reference to an array of mutexes for this task. Note that the mutex list may +# change during a Task's life time. This list must not be modified outside the Task object. +# +# If you override this method, then you <b>must</b> ensure that when the mutex list changes, +# you trigger a onMutexesChanged event. Otherwise the task manager will not behave correctly. +sub getMutexes { + return $_[0]->{T_mutexes}; +} + +## +# int $Task->getPriority() +# +# Get the priority for this task. This priority is guaranteed to never change during a Task's +# life time. +sub getPriority { + return $_[0]->{T_priority}; +} + + +##################################### +### CATEGORY: Events +##################################### + +## +# CallbackList $Task->onMutexesChanged() +# +# This event is triggered when the mutex list for this task has changed. +sub onMutexesChanged { + return $_[0]->{T_onMutexesChanged}; +} + +## +# CallbackList $Task->onStop() +# +# This event is triggered when the task's status has been set to Task::STOPPED. +sub onStop { + return $_[0]->{T_onStop}; +} + + +##################################### +### CATEGORY: Protected commands +##################################### + +## +# void $Task->setError(code, message) +# code: An error code. +# message: An error message. +# Requires: $self->getStatus() == Task::INACTIVE or Task::RUNNING +# +# Indicate that the task has been completed, but with an error. +# The status will be set to Task::DONE, and $Task->getError() will return +# the error information passed to this method. +# +# Do not call this method outside $Task->iterate(), or bad things will happen! +sub setError { + my ($self, $code, $message) = @_; + $self->_assertStatus(INACTIVE, RUNNING) if DEBUG; + $self->{T_error} = { + code => $code, + message => $message + }; + $self->{T_status} = DONE; +} + +## +# void $Task->setDone() +# Requires: $self->getStatus() == Task::INACTIVE or Task::RUNNING +# +# Indicate that the task has been completed, without error. The +# status will be set to Task::DONE. +# +# Do not call this method outside $Task->iterate(), or bad things will happen! +sub setDone { + my ($self) = @_; + $self->_assertStatus(INACTIVE, RUNNING) if DEBUG; + $self->{T_status} = DONE; +} + +## +# void $Task->setStopped() +# Requires: $self->getStatus() == Task::RUNNING, Task::INACTIVE or Task::INTERRUPTED +# Ensures: $self->getStatus() == Task::STOPPED +# +# Set the task's status to Task::STOPPED and trigger an onStop event. +# This is useful for tasks that cannot stop immediately +# when stop() is called: they can mark the task as stopped when appropriate. +sub setStopped { + my ($self) = @_; + $self->_assertStatus(INACTIVE, RUNNING, INTERRUPTED) if DEBUG; + $self->{T_status} = STOPPED; + $self->{T_onStop}->call($self); +} + +## +# void $Task->setMutexes(mutexes...) +# +# Set the currently active mutexes for this task. This will trigger an +# onMutexesChanged event. +# +# You should only call this method inside the class's iterate() method, +# or during initialization. Otherwise you may confuse the task manager. +sub setMutexes { + my $self = shift; + $self->{T_mutexes} = \@_; + $self->{T_onMutexesChanged}->call($self); +} + + +##################################### +### CATEGORY: Public commands +##################################### + +## +# void $Task->activate() +# Requires: $self->getStatus() == Task::INACTIVE +# Ensures: $self->getStatus() == Task::RUNNING +# +# Notify a task that it will be activated. Activation happens only once: +# just before iterate() is called, but only if the task has just been created. +# This allows the task to perform initialization. +# +# This method will be called by the task manager. +sub activate { + $_[0]->_assertStatus(INACTIVE) if DEBUG; + $_[0]->{T_status} = RUNNING; +} + +## +# void $Task->interrupt() +# Requires: $self->getStatus() == Task::RUNNING +# Ensures: $self->getStatus() == Task::INTERRUPTED +# +# Notify a (running) task that it is about to be interrupted. The task may take necessary actions +# (like updating internal timers) in preparation for interruption. The task must immediately +# cease all actions, and set the status to Task::INTERRUPTED. +# +# This method should only be called by the task manager. +# +# Task implementors may override this method to implement code for interruption handling. +sub interrupt { + $_[0]->_assertStatus(RUNNING) if DEBUG; + $_[0]->{T_status} = INTERRUPTED; +} + +## +# void $Task->resume() +# Requires: $self->getStatus() == Task::INTERRUPTED +# Ensures: $self->getStatus() == Task::RUNNING +# +# Notify an (interrupted) task that it is about to be resumed. The task may take +# necessary actions in prepration for resuming. The task must set its status to +# Task::RUNNING. +# +# This method should only be called by the task manager. +# +# Task implementors may override this method to implement code for resume handling. +sub resume { + $_[0]->_assertStatus(INTERRUPTED) if DEBUG; + $_[0]->{T_status} = RUNNING; +} + +## +# void $Task->stop() +# Requires: $self->getStatus() == Task::RUNNING, Task::INACTIVE or Task::INTERRUPTED +# +# Notify a task that it must completely stop. When the task is actually stopped, +# the status must be set to Task::STOPPED. +# +# The default behavior is to immediate set the status to Task::STOPPED +# by calling $Task->setStopped(), thereby triggering an onStop event. +# Task implementors may override this method to implement custom stop handling. +# You may choose to stop the task after a period of time, instead of immediately. +# +# This method may be called by anybody, not just the task manager. +sub stop { + $_[0]->setStopped(); +} + +## +# void $Task->iterate() +# Requires: $self->getStatus() == Task::RUNNING +# +# Run one iteration of this task. Task implementors must override this method to +# implement task code. +sub iterate { + $_[0]->_assertStatus(RUNNING) if DEBUG; +} + +1; diff --git a/openkore_llm_knowledge/core/src/Task/Move.pm b/openkore_llm_knowledge/core/src/Task/Move.pm new file mode 100644 index 0000000000..d4b2a01a00 --- /dev/null +++ b/openkore_llm_knowledge/core/src/Task/Move.pm @@ -0,0 +1,197 @@ +######################################################################### +# OpenKore - Simple movement task +# Copyright (c) 2006 OpenKore Team +# +# This software is open source, licensed under the GNU General Public +# License, version 2. +# Basically, this means that you're allowed to modify and distribute +# this software. However, if you distribute modified versions, you MUST +# also distribute the source code. +# See http://www.gnu.org/licenses/gpl.html for the full license. +######################################################################### +## +# MODULE DESCRIPTION: Simple movement task. +# +# The Move task is responsible for moving a single step. That is: to +# move to a near place on the same map, that can be reached by clicking +# one time inside the RO client. +# +# This task will keep sending the 'move' message to the server until the +# character has moved, or until a specific amount of time has passed. +# Furthermore, this task will also make sure that the character first +# stands up, if the character is sitting. +# +# You should take a look at the Route task instead, for movements which +# involve a longer route for which multiple steps are required. +package Task::Move; + +use strict; +use Time::HiRes qw(time); +use Scalar::Util; + +use Modules 'register'; +use Task::WithSubtask; +use base qw(Task::WithSubtask); +use Task::SitStand; +use Globals qw(%timeout $net); +use Plugins; +use Network; +use Log qw(warning debug); +use Translation qw(T TF); +use Utils qw(timeOut); +use Utils::Exceptions; + +# Error constants. +use enum qw( + TOO_LONG + NO_SIT_STAND_SKILL + UNKNOWN_ERROR +); + +# Mutexes used by this task. +use constant MUTEXES => Task::SitStand::MUTEXES; + + +## +# Task::Move->new(options...) +# +# Create a new Task::Move object. The following options are allowed: +# `l +# - All options allowed by Task->new(), except 'movement', 'autostop' and 'autofail'. +# - <tt>actor</tt> (required) - Which Actor this task should move. +# - <tt>x</tt> (required) - The X-coordinate that you want to move to. +# - <tt>y</tt> (required) - The Y-coordinate that you want to move to. +# - <tt>retryTime</tt> - After a 'move' message has been sent, if the character does not +# move within the specified amount of time, then this task will re-sent +# a 'move' message. The default is 0.5. +# - <tt>giveupTime</tt> - If the character still hasn't moved after the specified amount of time, +# then this task will give up and complete with an error. +# `l` +# +# x and y may not be 0 or undef. Otherwise, an ArgumentException will be thrown. +sub new { + my $class = shift; + my %args = @_; + my $self = $class->SUPER::new(@_, autostop => 1, autofail => 1, mutexes => MUTEXES); + + unless ($args{actor}->isa('Actor') and $args{x} != 0 and $args{y} != 0) { + ArgumentException->throw(error => "Invalid arguments."); + } + + $self->{$_} = $args{$_} for qw(actor x y); + + # Pass a weak reference of mercenary/homunculus to ourselves in order to avoid circular references (memory leaks). + if ($self->{actor}->isa("AI::Slave::Homunculus") || $self->{actor}->isa("Actor::Slave::Homunculus") || $self->{actor}->isa("AI::Slave::Mercenary") || $self->{actor}->isa("Actor::Slave::Mercenary")) { + Scalar::Util::weaken($self->{actor}); + } + + $self->{retry}{timeout} = $args{retryTime} || $timeout{ai_move_retry}{timeout} || 0.5; + $self->{retry}{count} = 0; + $self->{giveup}{timeout} = $args{giveupTime} || $timeout{ai_move_giveup}{timeout} || 3; + + # Watch for map change events. Pass a weak reference to ourselves in order + # to avoid circular references (memory leaks). + my @holder = ($self); + Scalar::Util::weaken($holder[0]); + $self->{mapChangedHook} = Plugins::addHook('Network::Receive::map_changed', \&mapChanged, \@holder); + + return $self; +} + +sub DESTROY { + my ($self) = @_; + Plugins::delHook($self->{mapChangedHook}) if $self->{mapChangedHook}; +} + +# Overrided method. +sub activate { + my ($self) = @_; + $self->SUPER::activate(); + $self->{giveup}{time} = time; + $self->{start_time} = time; +} + +# Overrided method. +sub interrupt { + my ($self) = @_; + $self->SUPER::interrupt(); + $self->{interruptionTime} = time; +} + +# Overrided method. +sub resume { + my ($self) = @_; + $self->SUPER::resume(); + $self->{giveup}{time} += time - $self->{interruptionTime}; + $self->{retry}{time} += time - $self->{interruptionTime}; +} + +# Overrided method. +sub iterate { + my ($self) = @_; + return if (!$self->SUPER::iterate()); + return if ($net->getState() != Network::IN_GAME); + + # If we're sitting, wait until we've stood up. + if ($self->{actor}{sitting}) { + debug "Move $self->{actor} (to $self->{x} $self->{y}) - trying to stand\n", "move"; + my $task = new Task::SitStand(actor => $self->{actor}, mode => 'stand'); + $self->setSubtask($task); + + # Stop if the map changed. + } elsif ($self->{mapChanged}) { + debug "Move $self->{actor} (to $self->{x} $self->{y}) - map change detected\n", "move"; + $self->setDone(); + + # Stop if we've moved. + } elsif ($self->{actor}{time_move} > $self->{start_time} && $self->{actor}{pos_to}{x} == $self->{x} && $self->{actor}{pos_to}{y} == $self->{y}) { + debug "Move $self->{actor} (to $self->{x} $self->{y}) - done\n", "move"; + $self->setDone(); + + # Stop if we've timed out. + } elsif (timeOut($self->{giveup})) { + debug "Move $self->{actor} (to $self->{x} $self->{y}) - timeout\n", "move"; + $self->setError(TOO_LONG, TF("%s tried too long to move", $self->{actor})); + + } elsif (timeOut($self->{retry})) { + $self->{actor}->sendStopSkillUse() if $self->{actor}->{last_skill_used_is_continuous}; # avoid walk while using continuos skill (GC_ROLLINGCUTTER) + $self->{retry}{count}++; + debug "Move $self->{actor} (to $self->{x} $self->{y}) - trying ($self->{retry}{count})\n", "move"; + $self->{actor}->sendMove(@{$self}{qw(x y)}); + if ($self->{sendAttack}) { + debug "[Test Move Attack Buffer] Sending attack with move.\n", "move"; + $self->{actor}->sendAttack($self->{attackID}); + } + $self->{retry}{time} = time; + } +} + +# Overrided method. +sub subtaskDone { + my ($self, $task) = @_; + if (!$task->getError()) { + $self->{start_time} = time; + $self->{giveup}{time} = time; + } +} + +# Overrided method. +sub translateSubtaskError { + my ($self, $task, $error) = @_; + my $code; + if ($task->isa('Task::SitStand') && $error->{code} == Task::SitStand::NO_SIT_STAND_SKILL) { + $code = NO_SIT_STAND_SKILL; + } + if (!defined $code) { + $code = UNKNOWN_ERROR; + } + return { code => $code, message => $error->{message} }; +} + +sub mapChanged { + my (undef, undef, $holder) = @_; + my $self = $holder->[0]; + $self->{mapChanged} = 1; +} + +1; \ No newline at end of file diff --git a/openkore_llm_knowledge/core/src/Task/Route.pm b/openkore_llm_knowledge/core/src/Task/Route.pm new file mode 100644 index 0000000000..a5ca182e60 --- /dev/null +++ b/openkore_llm_knowledge/core/src/Task/Route.pm @@ -0,0 +1,847 @@ +######################################################################### +# OpenKore - Long intra-map movement task +# Copyright (c) 2006 OpenKore Team +# +# This software is open source, licensed under the GNU General Public +# License, version 2. +# Basically, this means that you're allowed to modify and distribute +# this software. However, if you distribute modified versions, you MUST +# also distribute the source code. +# See http://www.gnu.org/licenses/gpl.html for the full license. +######################################################################### +## +# MODULE DESCRIPTION: Long intra-map movement task. +# +# This task is able to move long distances within the same map. Unlike +# the Move task, this task can walk to destinations which are outside the +# character's screen. +package Task::Route; + +use strict; +use Time::HiRes qw(time); +use Scalar::Util; +use Carp::Assert; +use Utils::Assert; + +use Modules 'register'; +use Task::WithSubtask; +use base qw(Task::WithSubtask); +use Task::Move; + +use Globals qw($field $net %config %timeout $npcsList); +use AI qw(ai_useTeleport); +use Log qw(message error debug warning); +use Network; +use Field; +use Translation qw(T TF); +use Misc; +use Utils qw(timeOut adjustedBlockDistance distance blockDistance calcPosFromPathfinding existsInList); +use Utils::Exceptions; +use Utils::Set; +use Utils::PathFinding; + +# Stage constants. +use constant { + NOT_INITIALIZED => 1, + CALCULATE_ROUTE => 2, + ROUTE_SOLUTION_READY => 3, + WALK_ROUTE_SOLUTION => 4 +}; + +# Error code constants. +use enum qw( + TOO_MUCH_TIME + CANNOT_CALCULATE_ROUTE + STUCK + UNEXPECTED_STATE +); + +# TODO: Add Homunculus support + + +## +# Task::Route->new(options...) +# +# Create a new Task::Route object. The following options are allowed: +# - All options allowed by Task::WithSubtask->new(), except 'mutexes', 'autostop' and 'autofail'. +# +# Required arguments: +# `l +# - actor - The Actor object which this task should move. +# - x - The X-coordinate that you want to move to. +# - y - The Y-coordinate that you want to move to. +# - field: The Field object of the map that you want to move to. +# `l` +# +# Optional arguments: +# `l` +# - maxDistance - The maximum distance (in blocks) that the route may be. If +# not specified, then there is no limit. +# - maxTime - The maximum time that may be spent on walking the route. If not +# specified, then there is no time limit. +# - distFromGoal - Stop walking if we're within the specified distance (in blocks) +# from the goal. If not specified, then we'll walk until the +# destination is reached. +# - pyDistFromGoal - Same as distFromGoal, but this allows you to specify the +# Pythagorian distance instead of block distance. +# - avoidWalls - Whether to avoid walls. The default is yes. +# - notifyUponArrival - Whether to print a message when we've reached the destination. +# The default is no. +# `l` +# +# x and y may not be lower than 0 or undef. Otherwise, an ArgumentException will be thrown. +sub new { + my $class = shift; + my %args = @_; + my $self = $class->SUPER::new(@_, autostop => 1, autofail => 0, mutexes => ['movement']); + + unless ($args{field}->isa('Field')) { + ArgumentException->throw(error => "Invalid Field argument."); + } + + unless ($args{actor}->isa('Actor')) { + ArgumentException->throw(error => "Invalid Actor argument."); + } + + if (!defined $args{x} || !defined $args{y} || $args{x} < 0 || $args{y} < 0) { + ArgumentException->throw(error => "Invalid Coordinates argument."); + } + + my $allowed = new Set(qw(targetNpcPos maxDistance maxTime distFromGoal pyDistFromGoal avoidWalls randomFactor useManhattan notifyUponArrival attackID sendAttackWithMove attackOnRoute noSitAuto LOSSubRoute meetingSubRoute isRandomWalk isFollow isIdleWalk isSlaveRescue isMoveNearSlave isEscape isItemTake isItemGather isDeath isToLockMap runFromTarget)); + foreach my $key (keys %args) { + if ($allowed->has($key) && defined($args{$key})) { + $self->{$key} = $args{$key}; + } + } + + $self->{actor} = $args{actor}; + + # Pass a weak reference of mercenary/homunculus to ourselves in order to avoid circular references (memory leaks). + if ($self->{actor}->isa("AI::Slave::Homunculus") || $self->{actor}->isa("Actor::Slave::Homunculus") || $self->{actor}->isa("AI::Slave::Mercenary") || $self->{actor}->isa("Actor::Slave::Mercenary")) { + Scalar::Util::weaken($self->{actor}); + } + + $self->{dest}{map} = $args{field}; + $self->{dest}{pos}{x} = $args{x}; + $self->{dest}{pos}{y} = $args{y}; + if ($config{$self->{actor}{configPrefix}.'route_avoidWalls'}) { + if (!defined $self->{avoidWalls}) { + $self->{avoidWalls} = 1; + } + } else { + $self->{avoidWalls} = 0; + } + + if ($config{$self->{actor}{configPrefix}.'route_randomFactor'}) { + if (!defined $self->{randomFactor}) { + $self->{randomFactor} = $config{$self->{actor}{configPrefix}.'route_randomFactor'}; + } + } else { + $self->{randomFactor} = 0; + } + if (!defined $self->{useManhattan}) { + $self->{useManhattan} = 0; + } + + $self->{solution} = []; + $self->{stage} = NOT_INITIALIZED; + + # Watch for map change events. Pass a weak reference to ourselves in order + # to avoid circular references (memory leaks). + my @holder = ($self); + Scalar::Util::weaken($holder[0]); + $self->{mapChangedHook} = Plugins::addHook('Network::Receive::map_changed', \&mapChanged, \@holder); + + return $self; +} + +sub DESTROY { + my ($self) = @_; + Plugins::delHook($self->{mapChangedHook}) if $self->{mapChangedHook}; + $self->SUPER::DESTROY(); +} + +## +# Hash* $Task_Route->destCoords() +# +# Returns the destination coordinates. The result is a hash with the items 'x' and 'y'. +sub destCoords { + return $_[0]->{dest}{pos}; +} + +# Overrided method. +sub activate { + my ($self) = @_; + $self->SUPER::activate(); + $self->{stage} = CALCULATE_ROUTE; + $self->{time_start} = time; +} + +# Overrided method. +sub interrupt { + my ($self) = @_; + $self->SUPER::interrupt(); + $self->{interruptionTime} = time; +} + +# Overrided method. +sub resume { + my ($self) = @_; + $self->SUPER::resume(); + $self->{time_start} += time - $self->{interruptionTime}; + undef $self->{time_step}; +} + +# Overrided method. +sub iterate { + my ($self) = @_; + return unless ($self->SUPER::iterate() && $net->getState() == Network::IN_GAME); + return unless $field && defined $self->{actor}{pos_to} && defined $self->{actor}{pos_to}{x} && defined $self->{actor}{pos_to}{y}; + + if ( $self->{maxTime} && timeOut($self->{time_start}, $self->{maxTime})) { + # We spent too much time + debug "Route $self->{actor} - we spent too much time; bailing out.\n", "route"; + $self->setError(TOO_MUCH_TIME, "Too much time spent on walking."); + + } elsif ($field->baseName ne $self->{dest}{map}->baseName) { + debug "Map changed: ".$self->{dest}{map}->baseName." -> ".$field->baseName."\n", "route"; + $self->setDone(); + + } elsif ($self->{mapChanged}) { + debug "Route $self->{actor}: Map changed within same map; recalculating route.\n", "route"; + undef $self->{sentTeleport}; + undef $self->{mapChanged}; + $self->resetRoute(); + + } elsif ($self->{stage} == CALCULATE_ROUTE) { + my $pos = $self->{actor}{pos}; + my $pos_to = $self->{actor}{pos_to}; + + my $calc_pos = calcPosFromPathfinding($field, $self->{actor}); + + my $walk = 1; + if ($config{route_teleport} == 2 + && !$self->{isRandomWalk} + && !$self->{disableOnMapTeleport} + && !$field->isCity + && !existsInList($config{route_teleport_notInMaps}, $field->baseName) + && (!$config{route_teleport_maxTries} || $self->{teleportTries} <= $config{route_teleport_maxTries})) { + my $minDist = $config{route_teleport_minDistance}; + + if ($self->{mapChanged}) { + undef $self->{sentTeleport}; + undef $self->{mapChanged}; + } + + if (!$self->{sentTeleport}) { + my $dist = new PathFinding( + start => $self->{actor}{pos_to}, + dest => $self->{dest}{pos}, + field => $field + )->runcount; + debug "Distance to destination ($self->{dest}{pos}{x},$self->{dest}{pos}{y}) is $dist\n", "route"; + + if ($dist < 0 || $dist > $minDist) { + if ($dist > 0 && $config{route_teleport_maxTries} && $self->{teleportTries} >= $config{route_teleport_maxTries}) { + debug "Teleported $config{route_teleport_maxTries} times on same-map route. Falling back to walking.\n", "route"; + } else { + message TF("Attempting to teleport near destination, try #%s\n", ($self->{teleportTries} + 1)), "route"; + if (!canUseTeleport(1)) { + $self->{disableOnMapTeleport} = 1; + } else { + ai_useTeleport(1); + $walk = 0; + $self->{sentTeleport} = 1; + $self->{teleportTime} = time; + $self->{teleportTries}++; + } + } + } + } elsif (timeOut($self->{teleportTime}, 4)) { + debug "Unable to teleport; falling back to walking.\n", "route"; + $self->{disableOnMapTeleport} = 1; + } else { + $walk = 0; + } + } + return unless $walk; + + debug "Route $self->{actor}: Calculating. Your pos ($pos->{x} $pos->{y}). Your pos_to ($pos_to->{x} $pos_to->{y}). calcPosFromPathfinding ($calc_pos->{x} $calc_pos->{y})\n", "route"; + + my $begin = time; + + if (!$self->{meetingSubRoute} && !$self->{LOSSubRoute} && $pos_to->{x} == $self->{dest}{pos}{x} && $pos_to->{y} == $self->{dest}{pos}{y}) { + debug "Route $self->{actor}: Current position and destination are the same.\n", "route"; + $self->setDone(); + + } elsif ($self->getRoute($self->{solution}, $self->{dest}{map}, $calc_pos, $self->{dest}{pos}, $self->{avoidWalls}, $self->{randomFactor}, $self->{useManhattan}, 1)) { + $self->{stage} = ROUTE_SOLUTION_READY; + + @{$self->{last_pos}}{qw(x y)} = @{$calc_pos}{qw(x y)}; + @{$self->{last_pos_to}}{qw(x y)} = @{$pos_to}{qw(x y)}; + $self->{start} = 1; + $self->{confirmed_correct_vector} = 0; + + if ($self->{pyDistFromGoal} || $self->{distFromGoal}) { + $self->{anyDistFromGoal} = 1; + + my $current_i = $#{$self->{solution}}; + + while (1) { + my $dest = $self->{solution}[$current_i]; + + if ($self->{distFromGoal}) { + if (blockDistance($dest, $self->{dest}{pos}) <= $self->{distFromGoal}) { + $self->{solution}[$current_i]{closeToEnd} = 1; + } else { + $self->{solution}[$current_i]{closeToEnd} = 0; + last; + } + + } elsif ($self->{pyDistFromGoal}) { + if (distance($dest, $self->{dest}{pos}) <= $self->{pyDistFromGoal}) { + $self->{solution}[$current_i]{closeToEnd} = 1; + } else { + $self->{solution}[$current_i]{closeToEnd} = 0; + last; + } + } + last if ($current_i == 0); + } continue { + $current_i--; + } + + } else { + $self->{anyDistFromGoal} = 0; + } + + debug "Route $self->{actor} Solution Ready! Found path on ".$self->{dest}{map}->baseName." from ".$calc_pos->{x}." ".$calc_pos->{y}." to ".$self->{dest}{pos}{x}." ".$self->{dest}{pos}{y}.". Size: ".@{$self->{solution}}." steps.\n", "route"; + + $self->iterate(); + + } else { + debug "Something's wrong; there is no path from " . $self->{dest}{map}->baseName . "($calc_pos->{x},$calc_pos->{y}) to " . $self->{dest}{map}->baseName . "($self->{dest}{pos}{x},$self->{dest}{pos}{y}).\n", "route"; + $self->setError(CANNOT_CALCULATE_ROUTE, "Unable to calculate a route."); + } + + } elsif ($self->{stage} == ROUTE_SOLUTION_READY) { + my $begin = time; + my $solution = $self->{solution}; + + # TODO: What is this Fractional route motion bellow? + if ($self->{maxDistance} > 0 && $self->{maxDistance} < 1) { + # Fractional route motion + $self->{maxDistance} = int($self->{maxDistance} * scalar(@{$solution})); + } + if ($self->{maxDistance} && $self->{maxDistance} < @{$solution}) { + splice(@{$solution}, 1 + $self->{maxDistance}); + } + + undef $self->{mapChanged}; + undef $self->{step_index}; + undef $self->{decreasing_step_index}; + #undef $self->{last_pos}; + #undef $self->{last_pos_to}; + #undef $self->{start}; + #undef $self->{confirmed_correct_vector}; + undef $self->{last_best_pos_step}; + undef $self->{last_best_pos_to_step}; + undef $self->{next_pos}; + undef $self->{time_step}; + + $self->{stage} = WALK_ROUTE_SOLUTION; + + if (@{$self->{solution}} == 0) { + debug "Route $self->{actor}: DistFromGoal|pyDistFromGoal trimmed all solution steps.\n", "route"; + $self->setDone(); + } else { + $self->iterate(); + } + + # Actual walking algorithm + } elsif ($self->{stage} == WALK_ROUTE_SOLUTION) { + my $solution = $self->{solution}; + $self->{route_out_time} = time if !exists $self->{route_out_time}; + + if (!defined $self->{step_index}) { + $self->{step_index} = $config{$self->{actor}{configPrefix}.'route_step'}; + } + + my ($current_pos, $current_pos_to, $current_calc_pos); + + # $actor->{pos} is the position the character moved FROM in the last move packet received + @{$current_pos}{qw(x y)} = @{$self->{actor}{pos}}{qw(x y)}; + + # $actor->{pos_to} is the position the character moved TO in the last move packet received + @{$current_pos_to}{qw(x y)} = @{$self->{actor}{pos_to}}{qw(x y)}; + + $current_calc_pos = calcPosFromPathfinding($field, $self->{actor}); + + if ($current_calc_pos->{x} == $solution->[$#{$solution}]{x} && $current_calc_pos->{y} == $solution->[$#{$solution}]{y}) { + # Actor position is the destination; we've arrived at the destination + if ($self->{notifyUponArrival}) { + message TF("%s reached the destination.\n", $self->{actor}), "route"; + } else { + debug "$self->{actor} reached the destination.\n", "route"; + } + + Plugins::callHook('route', {status => 'success'}); + $self->setDone(); + return; + + } else { + # Failsafe + my $lookahead_failsafe_max = $self->{step_index} + 1; + my $lookahead_failsafe_count; + + # This looks ahead in the solution array and finds the closest position in it to our current {pos}, then it looks $lookahead_failsafe_max further, just to be sure + + $lookahead_failsafe_count = 0; + my $best_index; + my $best_dist; + + foreach my $step_i (0..$#{$solution}) { + my $step = $solution->[$step_i]; + if ($step->{x} == $current_calc_pos->{x} && $step->{y} == $current_calc_pos->{y}) { + $best_index = $step_i; + $best_dist = 0; + last; + } + my $dist = adjustedBlockDistance($current_calc_pos, $step); + if (!defined $best_dist || $best_dist > $dist) { + $best_index = $step_i; + $best_dist = $dist; + $lookahead_failsafe_count = 0; + } else { + $lookahead_failsafe_count++; + } + if ($lookahead_failsafe_count == $lookahead_failsafe_max) { + last; + } + } + my $best_pos_step = $best_index; + + # This does the same, but with {pos_to} + undef $best_index; + undef $best_dist; + foreach my $step_i (0..$#{$solution}) { + my $step = $solution->[$step_i]; + if ($step->{x} == $current_pos_to->{x} && $step->{y} == $current_pos_to->{y}) { + $best_index = $step_i; + $best_dist = 0; + last; + } + my $dist = adjustedBlockDistance($current_pos_to, $step); + if (!defined $best_dist || $best_dist > $dist) { + $best_index = $step_i; + $best_dist = $dist; + $lookahead_failsafe_count = 0; + } else { + $lookahead_failsafe_count++; + } + if ($lookahead_failsafe_count == $lookahead_failsafe_max) { + last; + } + } + my $best_pos_to_step = $best_index; + + # Here there may be the need to check if 'pos' has changed yet best_pos_step is still the same, creating a lag in the movement + + # Last change in pos and pos_to put us in a walk path oposite to the desired one + # TODO: This confirmed_correct_vector could probably be removed + if ($best_pos_step > $best_pos_to_step) { + if ($self->{confirmed_correct_vector}) { + debug "Route $self->{actor} - movement interrupted: reset route (last change in pos and pos_to put us in a walk path oposite to the desired one)\n", "route"; + $self->{solution} = []; + $self->{stage} = CALCULATE_ROUTE; + return; + } + } elsif (!$self->{confirmed_correct_vector}) { + debug "Route $self->{actor} - movement vector confirmed.\n", "route"; + $self->{confirmed_correct_vector} = 1; + } + + # Last move was to the cell we are already at, lag?, buggy code? + if ($self->{start}) { + debug "Route $self->{actor} - not trimming down solution (" . @{$solution} . ") because we have not moved yet.\n", "route", 2; + + } elsif ($best_pos_step == 0) { + debug "Route $self->{actor} - not trimming down solution (" . @{$solution} . ") because best_pos_step is 0.\n", "route", 2; + + } else { + # Should we trimm only the known walk ones ($best_pos_step) or the known + the guessed (calcStepsWalkedFromTimeAndRoute)? Default was both. + + # Currently testing delete up to known + the guessed + debug "Route $self->{actor} - trimming down solution (" . @{$solution} . ") by ".($best_pos_step)." steps\n", "route"; + + splice(@{$solution}, 0, $best_pos_step); + } + + $self->{last_best_pos_step} = $best_pos_step; + $self->{last_best_pos_to_step} = $best_pos_to_step; + } + + my $pos_changed; + if ($self->{last_current_calc_pos}{x} == $current_calc_pos->{x} && $self->{last_current_calc_pos}{y} == $current_calc_pos->{y}) { + $pos_changed = 0; + } else { + $pos_changed = 1; + } + + my $stepsleft = @{$solution}; + + $self->{lastStep} = 0; + + if ($stepsleft == 0) { + # No more points to cover; we've arrived at the destination + if ($self->{notifyUponArrival}) { + message TF("%s reached the destination.\n", $self->{actor}), "route"; + } else { + debug "$self->{actor} reached the destination.\n", "route"; + } + + Plugins::callHook('route', {status => 'success'}); + $self->setDone(); + + } elsif ($stepsleft == 2 && isCellOccupied($solution->[-1]) && !$self->{meetingSubRoute}) { + # 2 more steps to cover (current position and the destination) + debug "Stoping 1 cell away from destination because there is an obstacle in it.\n", "route"; + if ($self->{notifyUponArrival}) { + message TF("%s reached the destination.\n", $self->{actor}), "route"; + } else { + debug "$self->{actor} reached the destination.\n", "route"; + } + + Plugins::callHook('route', {status => 'success'}); + $self->setDone(); + } elsif ($stepsleft <= 2 && isCellOccupied($solution->[-1]) && $self->{attackID}) { + # If the destination cell is occupied, then we can't walk there but we need to attack + + # Get the cells around the destination cell + my @cells = calcRectArea2($solution->[-1]{x}, $solution->[-1]{y}, 1, 1); + my $walk_pos; + my $index; + while (@cells) { + $index = int(rand(@cells)); + my $cell = $cells[$index]; + next if ((!$field->isWalkable($cell->{x}, $cell->{y})) || ($field->isCellOccupied($cell))); + + $walk_pos = $cell; + last; + } continue { + splice(@cells, $index, 1); + } + # If the cells around the destination cell are all occupied, then we can't walk there + if (!(defined $walk_pos)) { + # Log error message + error TF("Destination cell (%d,%d) is occupied and there are no walkable cells around it.\n", + $solution->[-1]{x}, $solution->[-1]{y}), "route"; + # Emit error message + $self->setError(STUCK, T("Stuck during route.")); + Plugins::callHook('route', {status => 'stuck'}); + } else { + # If we have a walkable cell, then walk there + warning TF("Destination cell (%d,%d) is occupied, replacing it with (%d,%d).\n", + $solution->[-1]{x}, $solution->[-1]{y}, $walk_pos->{x}, $walk_pos->{y}), "route"; + # + $self->{dest}{pos}{x} = $walk_pos->{x}; + $self->{dest}{pos}{y} = $walk_pos->{y}; + # + $self->{route_out_time} = time; + $self->resetRoute(); + } + } elsif (timeOut($self->{route_out_time}, 3)) { + # Because of attack monster, get item or something else we are out of our route for a long time + # recalculate again + debug "We are out of our route for a long time, recalculating...\n", "route"; + $self->{route_out_time} = time; + $self->resetRoute(); + } elsif (!$self->{start} && $pos_changed == 0 && defined $self->{time_step} && timeOut($self->{time_step}, $timeout{ai_route_unstuck}{timeout})) { + # We tried to move for 3 seconds, but we are still on the same spot, decrease step size. + # However, if $self->{step_index} was already 0, then that means we were almost at the destination (only 1 more step is needed). + # But we got interrupted (by auto-attack for example). Don't count that as stuck. + $self->{decreasing_step_index}++; + $self->{step_index}--; + if ($self->{step_index} > 0) { + debug "Route $self->{actor} - not moving, decreasing step size to $self->{step_index}\n", "route"; + if ($stepsleft) { + # If we still have more points to cover, walk to next point + if ($self->{step_index} >= $stepsleft) { + $self->{step_index} = $stepsleft - 1; + $self->{lastStep} = 1; + } + @{$self->{next_pos}}{qw(x y)} = @{$solution->[$self->{step_index}]}{qw(x y)}; + $self->{time_step} = time; + $self->setMove(); + } + + } else { + # We're stuck + my $msg = TF("Stuck at %s (%d,%d), while walking from (%d,%d) to (%d,%d).", + $self->{dest}{map}->baseName, @{$self->{actor}{pos_to}}{qw(x y)}, + $current_calc_pos->{x}, $current_calc_pos->{y}, $self->{dest}{pos}{x}, $self->{dest}{pos}{y} + ); + $msg .= T(" Teleporting to unstuck.") if ($config{$self->{actor}{configPrefix}.'teleportAuto_unstuck'}); + $msg .= "\n"; + warning $msg, "route"; + ai_useTeleport(1) if $config{$self->{actor}{configPrefix}.'teleportAuto_unstuck'}; + $self->setError(STUCK, T("Stuck during route.")); + Plugins::callHook('route', {status => 'stuck'}); + } + + } else { + # We're either starting to move or already moving, so send out more + # move commands periodically to keep moving and updating our position + my $begin = time; + + if ($self->{decreasing_step_index}) { + if ($pos_changed) { + debug "Route $self->{actor} - started moving again, increasing step size by $self->{decreasing_step_index} (from ".($self->{step_index})." to ".($self->{step_index}+$self->{decreasing_step_index}).")\n", "route"; + $self->{step_index} += $self->{decreasing_step_index}; + $self->{decreasing_step_index} = 0; + } else { + debug "Route $self->{actor} - won't increase step size because pos did not change ($current_pos->{x} $current_pos->{y})\n", "route"; + } + } + + # If there are less steps to cover than the step size move to the last step (the destination). + if ($self->{step_index} >= $stepsleft) { + $self->{step_index} = $stepsleft - 1; + $self->{lastStep} = 1; + } + + # Here maybe we should also use pos_to (in the form of best_pos_to_step) to decide the next step index, as it can make the routing way more responsive + + + if ($self->{anyDistFromGoal}) { + my $step = $solution->[$self->{step_index}]; + # We are close enough to the destination + if (exists $step->{closeToEnd} && $step->{closeToEnd}) { + my $current_i = $self->{step_index}; + while (1) { + last if ($current_i == 0); + last if ($solution->[($current_i-1)]{closeToEnd} == 0); + $current_i--; + } + $self->{step_index} = $current_i; + } + } + @{$self->{next_pos}}{qw(x y)} = @{$solution->[$self->{step_index}]}{qw(x y)}; + + # But first, check whether the distance of the next point isn't abnormally large. + # If it is, then we've moved to an unexpected place. This could be caused by auto-attack, for example. + # TODO: This should be calcDistFromPath or something like that + my %nextPos = (x => $self->{next_pos}{x}, y => $self->{next_pos}{y}); + if (blockDistance(\%nextPos, $current_calc_pos) > 17) { + debug "Route $self->{actor} - movement interrupted: reset route (the distance of the next point is abnormally large ($current_calc_pos->{x} $current_calc_pos->{y} -> $nextPos{x} $nextPos{y}))\n", "route"; + $self->{solution} = []; + $self->{stage} = CALCULATE_ROUTE; + + } else { + + if ($self->{targetNpcPos}) { + my $found = 0; + foreach my $actor (@{$npcsList->getItems()}) { + my $pos = $actor->{pos}; + next if ($actor->{statuses}->{EFFECTSTATE_BURROW}); + if ($pos->{x} == $self->{dest}{pos}{x} && $pos->{y} == $self->{dest}{pos}{y}) { + if (defined $actor->{name}) { + $found = 1; + last; + } + } + } + if ($found) { + debug "[Route] [targetNpcPos] Found target npc.\n", "route"; + if ($self->{pyDistFromGoal} || $self->{distFromGoal}) { + if ($self->{distFromGoal} && blockDistance($self->{dest}{pos}, $current_calc_pos) <= $self->{distFromGoal}) { + debug "[Route] [targetNpcPos] [distFromGoal] Target npc is already close enough, ending movement.\n", "route"; + $self->setDone(); + return; + + } elsif ($self->{pyDistFromGoal} && distance($self->{dest}{pos}, $current_calc_pos) <= $self->{pyDistFromGoal}) { + debug "[Route] [targetNpcPos] [pyDistFromGoal] Target npc is already close enough, ending movement.\n", "route"; + $self->setDone(); + return; + } + } else { + debug "[Route] [targetNpcPos] Target npc is already on screen, ending movement.\n", "route"; + $self->setDone(); + return; + } + } + + } elsif ($self->{pyDistFromGoal} || $self->{distFromGoal}) { + if ($self->{distFromGoal} && blockDistance($self->{dest}{pos}, $current_calc_pos) <= $self->{distFromGoal}) { + debug "[Route] [distFromGoal] Target cell is already close enough, ending movement.\n", "route"; + $self->setDone(); + return; + + } elsif ($self->{pyDistFromGoal} && distance($self->{dest}{pos}, $current_calc_pos) <= $self->{pyDistFromGoal}) { + debug "[Route] [pyDistFromGoal] Target cell is already close enough, ending movement.\n", "route"; + $self->setDone(); + return; + } + } + + my %hookArgs; + $hookArgs{args} = $self; + $hookArgs{pos} = $current_calc_pos; + Plugins::callHook("route_before_move", \%hookArgs); + return if ($hookArgs{return}); + + if (!$self->{start} && $current_pos_to->{x} == $self->{next_pos}{x} && $current_pos_to->{y} == $self->{next_pos}{y}) { + debug "[Route] Not sending next step ($self->{next_pos}{x}, $self->{next_pos}{y}) because our pos_to is the same as it.\n", "route", 2; + if ($self->{lastStep} == 1 && !$self->{sendAttackWithMove} && $self->{meetingSubRoute}) { + debug "[Route] Also ending task now ang giving back control to AI::Attack.\n", "route"; + Plugins::callHook('route', {status => 'success'}); + $self->setDone(); + } + return; + } + + if ($self->{actor}->isa('Actor::You') && $self->{isRandomWalk} && $self->{actor}{slaves}) { + my $slave = AI::SlaveManager::mustWaitMinDistance(); + if (defined $slave) { + debug TF("Waiting for slave %s before next randomWalk step.\n", $slave), 'route', 2; + return; + } + } + + if ($self->{start} || ($self->{last_pos}{x} != $current_pos->{x} || $self->{last_pos}{y} != $current_pos->{y})) { + $self->{time_step} = time; + } + + $self->{start} = 0; + + @{$self->{last_pos}}{qw(x y)} = @{$current_pos}{qw(x y)}; + @{$self->{last_pos_to}}{qw(x y)} = @{$current_pos_to}{qw(x y)}; + @{$self->{last_current_calc_pos}}{qw(x y)} = @{$current_calc_pos}{qw(x y)}; + + debug "Route $self->{actor} - next step moving to ($self->{next_pos}{x}, $self->{next_pos}{y}), index $self->{step_index}, $stepsleft steps left\n", "route"; + + $self->setMove(); + } + } + $self->{route_out_time} = time; + } else { + # This statement should never be reached. + debug "Unexpected route stage [".$self->{stage}."] occured.\n", "route"; + $self->setError(UNEXPECTED_STATE, "Unexpected route stage [".$self->{stage}."] occured.\n"); + } +} + +sub setMove { + my ($self) = @_; + + my $task = new Task::Move( + actor => $self->{actor}, + x => $self->{next_pos}{x}, + y => $self->{next_pos}{y} + ); + + if ($self->{lastStep} == 1 && $self->{attackID} && $self->{sendAttackWithMove}) { + $task->{sendAttack} = 1; + $task->{attackID} = $self->{attackID}; + } else { + $task->{sendAttack} = 0; + } + + $self->setSubtask($task); + $self->iterate(); +} + +sub resetRoute { + my ($self) = @_; + $self->{solution} = []; + $self->{stage} = CALCULATE_ROUTE; +} + +## +# boolean Task::Route->getRoute(Array* solution, Field field, Hash* start, Hash* dest, [boolean avoidWalls = true], [boolean self_call = false]) +# $solution: The route solution will be stored in here. +# field: the field on which a route must be calculated. +# start: The is the start coordinate. +# dest: The destination coordinate. +# avoidWalls: 0 if you don't want to avoid walls on route. +# self_call: 1 if it was called from inside this module. +# Returns: 1 if the calculation succeeded, 0 if not. +# +# Calculate how to walk from $start to $dest on field $field, or check whether there +# is a path from $start to $dest on field $field. +# +# If $solution is given, then the blocks you have to walk on in order to get to $dest +# are stored in there. +# +# This function is a convenience wrapper function for the stuff +# in Utils/PathFinding.pm +sub getRoute { + my ($class, $solution, $field, $start, $dest, $avoidWalls, $randomFactor, $useManhattan, $self_call) = @_; + assertClass($field, 'Field') if DEBUG; + if (!defined $dest->{x} || $dest->{y} eq '') { + @{$solution} = () if ($solution); + return 1; + } + + # The exact destination may not be a spot that we can walk on. + # So we find a nearby spot that is walkable. + my %start = %{$start}; + my %dest = %{$dest}; + + my $closest_start = $field->closestWalkableSpot(\%start, 1); + my $closest_dest = $field->closestWalkableSpot(\%dest, 1); + $closest_dest = $field->closestWalkableSpot(\%dest, 10) if(!$closest_dest); # can't find a closest walkable spot + + if (!defined $closest_start || !defined $closest_dest) { + return 0; + } + + my %plugin_args; + $plugin_args{self} = $class; + $plugin_args{self_call} = $self_call; + $plugin_args{start} = $closest_start; + $plugin_args{dest} = $closest_dest; + $plugin_args{field} = $field; + $plugin_args{avoidWalls} = $avoidWalls; + $plugin_args{randomFactor} = $randomFactor; + $plugin_args{useManhattan} = $useManhattan; + $plugin_args{return} = 0; + + Plugins::callHook('getRoute' => \%plugin_args); + + my $pathfinding; + if ($plugin_args{return}) { + $pathfinding = $plugin_args{pathfinding}; + } else { + $pathfinding = new PathFinding(); + } + + # Calculate path + $pathfinding->reset( + start => $closest_start, + dest => $closest_dest, + field => $field, + avoidWalls => $avoidWalls, + randomFactor => $randomFactor, + useManhattan => $useManhattan, + getRoute => 1 + ); + return undef if (!$pathfinding); + + my $ret; + if ($solution) { + $ret = $pathfinding->run($solution); + } else { + $ret = $pathfinding->runcount(); + } + + return ($ret >= 0 ? 1 : 0); +} + +sub mapChanged { + my (undef, undef, $holder) = @_; + my $self = $holder->[0]; + $self->{mapChanged} = 1; +} + +1; diff --git a/openkore_llm_knowledge/core/src/Task/TalkNPC.pm b/openkore_llm_knowledge/core/src/Task/TalkNPC.pm new file mode 100644 index 0000000000..7069724e0a --- /dev/null +++ b/openkore_llm_knowledge/core/src/Task/TalkNPC.pm @@ -0,0 +1,977 @@ +######################################################################### +# OpenKore - NPC talking task +# Copyright (c) 2004-2006 OpenKore Developers +# +# This software is open source, licensed under the GNU General Public +# License, version 2. +# Basically, this means that you're allowed to modify and distribute +# this software. However, if you distribute modified versions, you MUST +# also distribute the source code. +# See http://www.gnu.org/licenses/gpl.html for the full license. +######################################################################### +# This task is responsible for automatically talking to NPCs, using a +# pre-defined NPC talking sequence. +package Task::TalkNPC; + +use strict; +use Time::HiRes qw(time); +use Scalar::Util; +use utf8; + +use Modules 'register'; +use Task; +use AI; +use base qw(Task); +use Globals qw($char %timeout $npcsList $monstersList $portalsList %ai_v $messageSender %config $storeList $net %talk); +use Log qw(message debug error warning); +use Utils; +use Commands; +use Network; +use Misc; +use Plugins; +use Translation qw(T TF); + +# Error codes: +use enum qw( + NPC_NOT_FOUND + NPC_NO_RESPONSE + NO_SHOP_ITEM + WRONG_NPC_INSTRUCTIONS + NPC_TIMEOUT_AFTER_ASWER + STEPS_AFTER_AFTER_NPC_CLOSE + STEPS_AFTER_BUY_OR_SELL + WRONG_SYNTAX_IN_STEPS +); + +# Mutexes used by this task. +use constant MUTEXES => ['npc']; + +use enum qw( + NOT_STARTED + TALKING_TO_NPC + AFTER_NPC_CLOSE + AFTER_NPC_CANCEL +); + + +## +# Task::TalkNPC->new(options...) +# +# Create a new Task::TalkNPC object. The following options are allowed: +# `l +# - All options allowed in Task->new(), except 'mutexes'. +# - <tt>x</tt> (required): The X-coordinate of the NPC to talk to. +# - <tt>y</tt> (required): The Y-coordinate of the NPC to talk to. +# - <tt>nameID</tt> (required): The nameID of the NPC to talk to (you may use this instead of x and y). +# - <tt>sequence</tt> (required): A string which describes how to talk to the NPC. +# `l` +# Note that the NPC is assumed to be on the same map as where the character currently is. +# +# <tt>sequence</tt> is a string of whitespace-separated instructions: +# ~l +# - c : Continue +# - r# : Select option # from menu. +# - n : Stop talking to NPC. +# - b : Send the "Show shop item list" (Buy) packet. +# - w# : Wait # seconds. +# - x : Initialize conversation with NPC. Useful to perform multiple transaction with a single NPC. +# - t="str" : send the text str to NPC, double quote is needed only if the string contains space +# ~l~ +sub new { + my $class = shift; + my %args = @_; + my $self = $class->SUPER::new(@_, mutexes => MUTEXES); + + $self->{type} = $args{type}; + $self->{x} = $args{x}; + $self->{y} = $args{y}; + $self->{ID} = $args{ID}; + $self->{nameID} = $args{nameID}; + $self->{sequence} = $args{sequence}; + $self->{sequence} =~ s/^ +| +$//g; + $self->{steps} = []; + $self->{trying_to_cancel} = 0; + $self->{sent_talk_response_cancel} = 0; + $self->{wait_for_answer} = 0; + $self->{error_code} = undef; + $self->{error_message} = undef; + $self->{map_change} = 0; + $self->{disconnected} = 0; + + $self->{validatedAddSequence} = 0; + + debug "Task::TalkNPC::new has been called with sequence '".$self->{sequence}."'.\n", "ai_npcTalk"; + + return $self; +} + +sub handleNPCTalk { + my ($hook_name, $args, $holder) = @_; + my $self = $holder->[0]; + + # TODO: maybe better create a new task + if ($self->{stage} == AFTER_NPC_CANCEL) { + debug "Npc has restarted conversation after talk cancel was sent.\n", "ai_npcTalk"; + + if ($self->noMoreSteps) { + debug "Continuing the talk within the same task, no conversation steps left.\n", "ai_npcTalk"; + } else { + debug "Continuing the talk within the same task and remaining conversation steps.\n", "ai_npcTalk"; + } + + $self->find_and_set_target; + $self->{stage} = TALKING_TO_NPC; + $self->{time} = time; + } + + if ($hook_name eq 'npc_talk_done') { + if ($self->{stage} == NOT_STARTED) { + debug "[TalkNPC] Npc which started autotalk has automatically sent a 'npc_talk_done'.\n", "ai_npcTalk"; + return; + + } elsif ($self->{stage} != TALKING_TO_NPC || !$self->{target}) { + debug "[TalkNPC] We received an strange 'npc_talk_done', ignoring it.\n", "ai_npcTalk"; + return; + } + $self->{stage} = AFTER_NPC_CLOSE; + message TF("[TalkNPC] %s: Done talking (close)\n", $self->{target}), "ai_npcTalk"; + + } elsif ($self->noMoreSteps) { + if ($hook_name eq 'packet/npc_talk_continue') { + message TF("%s: Type 'talk cont' to continue talking\n", $self->{target}), "ai_npcTalk"; + + } elsif ($hook_name eq 'packet/npc_talk_number') { + message TF("%s: Type 'talk num <number #>' to input a number.\n", $self->{target}), "ai_npcTalk"; + + } elsif ($hook_name eq 'npc_talk_responses') { + message TF("%s: Type 'talk resp #' to choose a response.\n", $self->{target}), "ai_npcTalk"; + + } elsif ($hook_name eq 'packet/npc_store_begin') { + message TF("%s: Type 'store' to start buying, type 'sell' to start selling or type 'canceltransaction' to cancel\n", $self->{target}), "ai_npcTalk"; + + } elsif ($hook_name eq 'packet/npc_talk_text') { + message TF("%s: Type 'talk text' (Respond to NPC)\n", $self->{target}), "ai_npcTalk"; + + } elsif ($hook_name eq 'packet/cash_dealer') { + message TF("%s: Type 'cashbuy' to start buying\n", $self->{target}), "ai_npcTalk"; + } + } + $self->{time} = time; + $self->{sent_talk_response_cancel} = 0; + $self->{wait_for_answer} = 0; +} + +sub delHooks { + my ($self) = @_; + + Plugins::delHooks($_) for @{$self->{hookHandles}}; + delete $self->{hookHandles}; + + Plugins::delHook($self->{mapChangedHook}) if $self->{mapChangedHook}; + delete $self->{mapChangedHook}; + + Plugins::delHook($self->{disconnectedHook}) if $self->{disconnectedHook}; + delete $self->{disconnectedHook}; +} + +sub DESTROY { + my ($self) = @_; + debug "$self->{target}: Task::TalkNPC::DESTROY was called\n", "ai_npcTalk"; + $self->delHooks; + $self->SUPER::DESTROY; +} + +# Overrided method. +sub activate { + my ($self) = @_; + $self->SUPER::activate(); # Do not forget to call this! + $self->{time} = time; + $self->{stage} = NOT_STARTED; + + my @holder = ($self); + Scalar::Util::weaken($holder[0]); + + push @{$self->{hookHandles}}, Plugins::addHooks( + ['npc_talk', \&handleNPCTalk, \@holder], + ['packet/npc_talk_continue', \&handleNPCTalk, \@holder], + ['npc_talk_done', \&handleNPCTalk, \@holder], + ['npc_talk_responses', \&handleNPCTalk, \@holder], + ['packet/npc_talk_number', \&handleNPCTalk, \@holder], + ['packet/npc_talk_text', \&handleNPCTalk, \@holder], + ['packet/npc_store_begin', \&handleNPCTalk, \@holder], + ['packet/npc_store_info', \&handleNPCTalk, \@holder], + ['packet/npc_sell_list', \&handleNPCTalk, \@holder], + ['packet/cash_dealer', \&handleNPCTalk, \@holder], + ['packet/npc_market_info', \&handleNPCTalk, \@holder], + ['packet/npc_market_purchase_result', \&handleNPCTalk, \@holder] + ); + + $self->{mapChangedHook} = Plugins::addHook('Network::Receive::map_changed', \&mapChanged, \@holder); + $self->{disconnectedHook} = Plugins::addHook('serverDisconnect/success', \&serverDisconnectSuccess, \@holder); +} + +sub mapChanged { + my (undef, undef, $holder) = @_; + my $self = $holder->[0]; + $self->{map_change} = 1; +} + +sub serverDisconnectSuccess { + my (undef, undef, $holder) = @_; + return if $holder->[0]->{disconnected}; + + debug "Disconnected during TalkNPC, cancelling task...\n", "ai_npcTalk"; + $holder->[0]->{disconnected} = 1; +} + +# Overrided method. +sub stop { + my ($self) = @_; + + $self->delHooks; + + $self->SUPER::stop; +} + +sub setTarget { + my ($self, $target) = @_; + + if ($target) { + message "[TalkNPC] Set to start talking with $target at ($target->{pos}{x},$target->{pos}{y}), ID ".getHex($target->{ID}).", sequence '".($self->{sequence})."'\n", "ai_npcTalk"; + $self->{target} = $target; + $self->{ID} = $target->{ID}; + } + + # FIXME: We probably need to look at the target->pos_to (if defined), + # not at self, as coordinates can be omitted. + if (defined $self->{x} && defined $self->{y}) { + lookAtPosition($self) unless (%talk); + } + + return 1; +} + +sub find_and_set_target { + my ($self) = @_; + my $target = $self->findTarget($npcsList) || $self->findTarget($monstersList) || $self->findTarget($portalsList); + + if ($target) { + return unless $self->setTarget($target); + } elsif (exists $talk{nameID} && $ai_v{'npc_talk'}{'talk'} ne 'buy_or_sell') {#check if this is really necessary + $self->{ID} = $talk{ID}; + $self->{target} = Actor::NPC->new; + $self->{target}->{appear_time} = time; + $self->{target}->{name} = 'Unknown'; + } + + return $target; +} + +# Overrided method. +sub iterate { + my ($self) = @_; + $self->SUPER::iterate(); # Do not forget to call this! + return unless ($net->getState() == Network::IN_GAME); + my $timeResponse = ($config{npcTimeResponse} >= 5) ? $config{npcTimeResponse}:5; + my $ai_npc_talk_wait_to_answer = $timeout{'ai_npc_talk_wait_to_answer'}{'timeout'} ? $timeout{'ai_npc_talk_wait_to_answer'}{'timeout'} : 1.5; + my $ai_npc_talk_wait_after_close_to_cancel = $timeout{'ai_npc_talk_wait_after_close_to_cancel'}{'timeout'} ? $timeout{'ai_npc_talk_wait_after_close_to_cancel'}{'timeout'} : 0.5; + my $ai_npc_talk_wait_after_cancel_to_destroy = $timeout{'ai_npc_talk_wait_after_cancel_to_destroy'}{'timeout'} ? $timeout{'ai_npc_talk_wait_after_cancel_to_destroy'}{'timeout'} : 0.5; + my $ai_npc_talk_wait_before_continue = $timeout{'ai_npc_talk_wait_before_continue'}{'timeout'} ? $timeout{'ai_npc_talk_wait_before_continue'}{'timeout'} : 0.7; + if ($self->{map_change} || $self->{disconnected}) { + + #A conversation started right after mapchange/disconnection (eg. payon guards) + if (%talk) { + debug "[TalkNPC] Done talking with $self->{target}, but another NPC initiated a talk instantly\n", "ai_npcTalk"; + # TODO: maybe better create a new task and pass remaining steps to it + debug "[TalkNPC] Continuing the talk within the same task and remaining conversation steps\n", "ai_npcTalk"; + $self->{map_change} = 0; + $self->{disconnected} = 0; + $self->find_and_set_target; + $self->{stage} = TALKING_TO_NPC; + $self->{time} = time; + + #If there's no conversation clear this task + } else { + debug "[TalkNPC] Ending Task::TalkNPC due to mapchange or disconnection, ", "ai_npcTalk"; + + if ($self->{stage} == TALKING_TO_NPC) { + debug "conversation interrupted and finished.\n"; + } elsif ($self->{stage} == AFTER_NPC_CLOSE) { + debug "talk cancel won't be sent.\n"; + } elsif ($self->{stage} == AFTER_NPC_CANCEL) { + debug "ending task before timeout.\n"; + } elsif ($self->{stage} == NOT_STARTED) { + debug "ending task before conversation started.\n"; + } else { + debug "conversation ended during unhandled stage ". $self->{stage} . ".\n"; + } + + $self->conversation_end; + } + + } elsif ($self->{stage} == NOT_STARTED) { + + if (!$self->{validatedAddSequence}) { + if (defined $self->{sequence}) { + if (!$self->addSteps($self->{sequence})) { + $self->manage_wrong_sequence(TF("Failed to add NPC talk sequence.")); + return; + } + } + $self->{validatedAddSequence} = 1; + } + + if ((!%talk || $ai_v{'npc_talk'}{'talk'} eq 'close') && $self->{type} eq 'autotalk') { + debug "Talking was initiated by the other side and finished instantly\n", "ai_npcTalk"; + #We must still send talk cancel or otherwise the character can't move. + $self->{stage} = AFTER_NPC_CLOSE; + $self->find_and_set_target; + $self->{time} = time; + return; + + } elsif (!timeOut($char->{time_move}, $char->{time_move_calc} + 0.2)) { + # Wait for us to stop moving before talking. + return; + + } elsif (timeOut($self->{time}, $timeResponse)) { + if ($self->{nameID}) { + $self->setError(NPC_NOT_FOUND, TF("Could not find an NPC with id (%d).", + $self->{nameID})); + } else { + $self->setError(NPC_NOT_FOUND, TF("Could not find an NPC at location (%d,%d).", + $self->{x}, $self->{y})); + } + + } elsif (defined $self->{error_code}) { + debug "Can't talk with $self->{target}, because of errors\n", "ai_npcTalk"; + $self->setError($self->{error_code}, $self->{error_message}); + + } else { + + my $target = $self->find_and_set_target; + return if ($self->getStatus() == Task::DONE); + + if (!%talk) { + debug "[TalkNPC] talk is not defined, setting to start conversation.\n", "ai_npcTalk"; + unless ($self->{steps}[0] eq 'x' || $self->{steps}[0] eq 'k') { + $self->addSteps('x', 1); + } + undef $ai_v{'npc_talk'}{'time'}; + undef $ai_v{'npc_talk'}{'talk'}; + + } elsif ($talk{nameID} eq $target->{nameID}) { + debug "[TalkNPC] talk is defined and nameID is right, just adding steps.\n", "ai_npcTalk"; + } else { + debug "[TalkNPC] talk is defined and nameID is wrong, using manage_wrong_sequence.\n", "ai_npcTalk"; + $self->manage_wrong_sequence(TF("Talking to wrong npc.")); + } + + return if ($self->getStatus() == Task::DONE); + + if ($target || %talk) { + $self->{stage} = TALKING_TO_NPC; + $self->{time} = time; + + } else { + + if (!exists $self->{plugin_retry}) { + $self->{plugin_retry} = 0; + } + my %plugin_args = ( + 'x' => $self->{x}, + 'y' => $self->{y}, + 'nameID' => $self->{nameID}, + 'sequence' => $self->{sequence}, + 'plugin_retry' => $self->{plugin_retry}, + 'return' => 0 + ); + + Plugins::callHook('TalkNPC_npc_missing' => \%plugin_args); + + if ($plugin_args{return}) { + $self->{plugin_retry}++; + $self->{x} = $plugin_args{x}; + $self->{y} = $plugin_args{y}; + $self->{nameID} = $plugin_args{nameID}; + $self->{sequence} = $plugin_args{sequence}; + warning "[TalkNPC] Could not find NPC, retry set by hookcall.\n", "ai_npcTalk"; + + } else { + $self->setError(NPC_NOT_FOUND, TF("Could not find an NPC.")); + } + } + } + + # This is where things may bug in npcs which have no chat (private healers) + } elsif (!$ai_v{'npc_talk'}{'time'} && timeOut($self->{time}, $timeResponse)) { + # If NPC does not respond before timing out, then by default, it's + # a failure. + $messageSender->sendTalkCancel($self->{ID}); + $self->setError(NPC_NO_RESPONSE, T("The NPC did not respond.")); + + } elsif ($self->{stage} == TALKING_TO_NPC) { + if (%talk && $ai_v{'npc_talk'}{'talk'} eq 'initiated') { + debug "Spining until a response is needed from us\n", "ai_npcTalk"; + return; + } + + #In theory after the talk_response_cancel is sent we shouldn't receive anything, so just wait the timer and assume it's over + if ($self->{sent_talk_response_cancel}) { + return unless (timeOut($self->{sent_talk_resp_cancel_time})); + undef %talk; + if (defined $self->{error_code}) { + debug "Done talking with $self->{target}, but with conversation sequence errors\n", "ai_npcTalk"; + $self->setError($self->{error_code}, $self->{error_message}); + } else { + $self->conversation_end; + } + return; + + #This will try to get out of this conversation as much as possible + } elsif ($self->{trying_to_cancel}) { + $ai_v{'npc_talk'}{'time'} = time + $timeResponse; + $self->{time} = time; + $self->cancelTalk; + return; + } + + #We must always wait for the last sent step to be answered, if it hasn't then cancel this task. + if ($self->{wait_for_answer}) { + if (${self}->{progress_bar}) { + $ai_v{'npc_talk'}{'time'} = time; + return; + } + if (timeOut($ai_v{'npc_talk'}{'time'}, $timeResponse)) { + $self->{error_code} = NPC_TIMEOUT_AFTER_ASWER; + $self->{error_message} = "We have waited for too long after we sent a response to the npc."; + $self->{trying_to_cancel} = 1; + return; + } + return; + } + + return unless (timeOut($ai_v{'npc_talk'}{'time'}, $ai_npc_talk_wait_before_continue)); + + # Wait x seconds. + if ($self->{steps}[0] =~ /^w(\d+)/i) { + my $time = $1; + debug "$self->{target}: Waiting for $time seconds...\n", "ai_npcTalk"; + $ai_v{'npc_talk'}{'time'} = time + $time; + $self->{time} = time + $time; + shift @{$self->{steps}}; + return; + + # Run a command. + } elsif ($self->{steps}[0] =~ /^a=(.*)/i) { + my $command = $1; + my $timeout = $timeResponse - 4; + $timeout = 0 if $timeout < 0; + $ai_v{'npc_talk'}{'time'} = time + $timeout; + $self->{time} = time + $timeout; + Commands::run($command); + shift @{$self->{steps}}; + return; + } + + if ($ai_v{'npc_talk'}{'talk'} ne 'next') { + while ($self->{steps}[0] =~ /^c/i) { + warning "Ignoring excessive use 'c' in conversation with npc.\n"; + shift(@{$self->{steps}}); + } + + #This is to make non-autotalkcont sequences compatible with autotalkcont ones + } elsif ($ai_v{'npc_talk'}{'talk'} eq 'next' && $config{autoTalkCont}) { + if ( $self->noMoreSteps || $self->{steps}[0] !~ /^c/i ) { + unshift(@{$self->{steps}}, 'c'); + } + debug "$self->{target}: Auto-continuing talking\n", "ai_npcTalk"; + } + + #This is done to restart the conversation (check if this is necessary) + if ($ai_v{'npc_talk'}{'talk'} eq 'close' && $self->{steps}[0] =~ /x/i) { + undef $ai_v{'npc_talk'}{'talk'}; + } + + if ($self->noMoreSteps) { + # We arrived at a buy or sell selection, but there are no more steps regarding this, so end the conversation + if ($ai_v{'npc_talk'}{'talk'} =~ /^(buy_or_sell|store|sell|cash)$/) { + $self->conversation_end; + } + #Wait for more commands + return; + + #We give the NPC some time to respond. This time will be reset once the NPC responds. + } else { + $ai_v{'npc_talk'}{'time'} = time + $timeResponse; + $self->{time} = time; + } + + my $step = $self->{steps}[0]; + my $current_talk_step = $ai_v{'npc_talk'}{'talk'}; + + while ( $step =~ /^if~\/(.*?)\/,(.*)/i ) { + my ( $regex, $code ) = ( $1, $2 ); + if ( "$talk{msg}:$talk{image}" =~ /$regex/s ) { + $step = $code; + } else { + shift @{ $self->{steps} }; + $step = $self->{steps}->[0]; + } + } + + debug "Iteration at Task::TalkNPC, current_talk_step '".$current_talk_step."', next step '".$step."'.\n", "ai_npcTalk", 2; + + # Apprach the NPC + if ( $step =~ /^k/i ) { + debug "$self->{target}: Initiating the talk by approaching\n", "ai_npcTalk"; + ai_route( + $self->{target}{map}, $self->{target}{pos}{x}, $self->{target}{pos}{y}, + targetNpcPos => 1, + ); + + # Initiate NPC conversation. + } elsif ( $step =~ /^x/i ) { + debug "$self->{target}: Initiating the talk\n", "ai_npcTalk"; + + my $target_pos = $self->{target} ? ($self->{target}{pos} || $self->{target}{pos_to}) : undef; + my $char_pos = $char->{pos} || $char->{pos_to}; + if ($target_pos && $char_pos) { + lookAtPositionNaturally($char_pos, $target_pos, $char->{look}{body}); + } + + $self->{target}->sendTalk; + + # Select an answer + } elsif ($current_talk_step eq 'select') { + + if ( $step =~ /^r(?:(\d+)|=(.+)|~\/(.*?)\/(i?))/i ) { + my $choice = $1; + + # Regex or text match + if ($2 || $3) { + # Choose a menu item by matching options against a regular expression. + my $pattern = $2 ? "^\Q$2\E\$" : $3; + my $postCondition = $4; + ( $choice ) = grep { $postCondition ? $talk{responses}[$_] =~ /$pattern/i : $talk{responses}[$_] =~ /$pattern/ } 0..$#{$talk{responses}}; + + # Found valid response + if (defined $choice && $choice < $#{$talk{responses}}) { + $messageSender->sendTalkResponse($talk{ID}, $choice + 1); + + # Found response is fake 'Cancel Chat' + } elsif (defined $choice) { + $self->{trying_to_cancel} = 1; + return; + + # No match was found + } else { + $self->manage_wrong_sequence(TF("According to the given NPC instructions, a menu " . + "item matching '%s' must now be selected, but no " . + "such menu item exists.", $pattern)); + return; + } + + #Normal number response + } else { + + #Normal number response is valid + if ($choice < $#{$talk{responses}}) { + debug "$self->{target}: Sending talk response #$choice\n", "ai_npcTalk"; + $messageSender->sendTalkResponse($talk{ID}, $choice + 1); + + #Normal number response is a fake "Cancel Chat" response. + } elsif ($choice == $#{$talk{responses}}) { + $self->{trying_to_cancel} = 1; + return; + + #Normal number response is not valid + } else { + $self->manage_wrong_sequence(TF("According to the given NPC instructions, menu item %d must " . + "now be selected, but there are only %d menu items.", + $choice, @{$talk{responses}} - 1)); + return; + } + } + + # Wrong sequence + } else { + $self->manage_wrong_sequence(TF("NPC requires a response to be selected, but the given instructions don't match that (current step: %s).", $step)); + return; + } + + # Click Next. + } elsif ($current_talk_step eq 'next') { + if ($step =~ /^c/i) { + debug "$self->{target}: Sending talk continue (next)\n", "ai_npcTalk"; + $messageSender->sendTalkContinue($talk{ID}); + + # Wrong sequence + } else { + $self->manage_wrong_sequence(TF("NPC requires the next button to be pressed now, but the given instructions don't match that (current step: %s).", $step)); + return; + } + + # Send NPC talk number. + } elsif ($current_talk_step eq 'number') { + if ( $step =~ /^d(\d+)/i ) { + my $number = $1; + debug "$self->{target}: Sending the number: $number\n", "ai_npcTalk"; + $messageSender->sendTalkNumber($talk{ID}, $number); + + # Wrong sequence + } else { + $self->manage_wrong_sequence(TF("NPC requires a number to be sent now, but the given instructions don't match that (current step: %s).", $step)); + return; + } + + # Send NPC talk text. + } elsif ($current_talk_step eq 'text') { + if ( $step =~ /^t=(.*)/i ) { + my $text = $1; + debug "$self->{target}: Sending the text: $text\n", "ai_npcTalk"; + $messageSender->sendTalkText($talk{ID}, $text); + + # Wrong sequence + } else { + $self->manage_wrong_sequence(TF("NPC requires a text to be sent now, but the given instructions don't match that (current step: %s).", $step)); + return; + } + + # Get the sell or buy list in a shop. + } elsif ( $current_talk_step eq 'buy_or_sell' ) { + + # Get the sell list in a shop. + if ( $step =~ /^s/i ) { + $messageSender->sendNPCBuySellList($talk{ID}, 1); + + # Get the buy list in a shop. + } elsif ($step =~ /^b$/i) { + $messageSender->sendNPCBuySellList($talk{ID}, 0); + + # Click the cancel button in a shop. + } elsif ($step =~ /^e$/i) { + cancelNpcBuySell(); + $ai_v{'npc_talk'}{'talk'} = 'close'; + + if ($self->noMoreSteps) { + $self->conversation_end; + } else { + $self->{time} = time + 2; + } + + # Wrong sequence + } else { + $self->manage_wrong_sequence(TF("This npc requires the sell, buy or cancel button to be pressed, but the given instructions don't match that (current step: %s).", $step)); + return; + } + + } elsif ( $current_talk_step eq 'store' ) { + + # Buy Items + if ($step =~ /^b(\d+),(\d+)/i) { + my @bulkitemlist; + while ($self->{steps}[0] =~ /^b(\d+),(\d+)/i){ + my $index = $1; + my $amount = $2; + if ($storeList->get($index)) { + # support to market + my $item = $storeList->get($index); + + if ($item->{amount} && $item->{amount} < $amount) { + $amount = $item->{amount}; + } + + my $itemID = $storeList->get($index)->{nameID}; + push (@bulkitemlist,{itemID => $itemID, amount => $amount}); + } else { + # ? Maybe better to use something else, but not error? + error TF("Shop item %s not found.\n", $index), "ai_npcTalk"; + } + shift @{$self->{steps}}; + } + completeNpcBuy(\@bulkitemlist); + # We give some time to get inventory_item_added packet from server. + # And skip this itteration. + if ($self->noMoreSteps) { + $self->conversation_end; + } else { + $ai_v{'npc_talk'}{'talk'} = 'close'; + $self->{time} = time + 2; + } + return; + + # Click the cancel button in a shop. + } elsif ($step =~ /^e$/i) { + my @bulkitemlist; + completeNpcBuy(\@bulkitemlist); + + if ($self->noMoreSteps) { + $self->conversation_end; + } else { + $ai_v{'npc_talk'}{'talk'} = 'close'; + $self->{time} = time + 2; + } + + return; + + # Wrong sequence + } else { + $self->manage_wrong_sequence(TF("NPC requires the buy or cancel button to be pressed, but the given instructions don't match that (current step: %s).", $step)); + return; + } + + } elsif ( $current_talk_step eq 'sell' ) { + $self->conversation_end; + + } else { + if ( $step =~ /^n$/i ) { + #Here for backwards compatibility + shift @{$self->{steps}}; + + } else { + $self->manage_wrong_sequence(T("According to the given NPC instructions, a npc conversation code ") . + TF("should be used (%s), but it doesn't exist.", $step)); + return; + } + } + + $self->{wait_for_answer} = 1; + shift @{$self->{steps}}; + + # After a 'npc_talk_done' hook we must always send a 'npc_talk_cancel' after a timeout + # I noticed that the RO client doesn't send a 'talk cancel' packet + # when it receives a 'npc_talk_closed' packet from the server'. + # But on pRO Thor (with Kapra password) this is required in order to + # open the storage. + # + # UPDATE: not sending 'talk cancel' breaks autostorage on iRO. + # This needs more investigation. + } elsif ($self->{stage} == AFTER_NPC_CLOSE) { + return unless (timeOut($self->{time}, $ai_npc_talk_wait_after_close_to_cancel)); + #Now 'n' step is totally unnecessary as we always send it but this must be done for backwards compatibility + if ( $self->{steps}[0] =~ /^n/i ) { + shift(@{$self->{steps}}); + } + $self->{time} = time; + $self->{stage} = AFTER_NPC_CANCEL; + + my $id = $ai_v{'npc_talk'}{'ID'}; + debug "[TalkNPC] $self->{target}: Sending talk cancel [id '".(unpack ('V', $id))."'] after NPC has done talking\n", "ai_npcTalk"; + $messageSender->sendTalkCancel($id); + + # After a 'npc_talk_cancel' and a timeout we decide what to do next + } elsif ($self->{stage} == AFTER_NPC_CANCEL) { + return unless (timeOut($self->{time}, $ai_npc_talk_wait_after_cancel_to_destroy)); + + if (defined $self->{error_code}) { + $self->setError($self->{error_code}, $self->{error_message}); + debug $self->{error_message} . "\n", "ai_npcTalk"; + return; + } + + # No more steps to be sent + # Usual end of a conversation + if ($self->noMoreSteps && !%talk) { + $self->conversation_end; + + # There are more steps but no conversation with npc + } elsif (!%talk) { + # Usual 'x' step + if ($self->{steps}[0] =~ /x/i) { + debug "$self->{target}: Reinitiating the talk\n", "ai_npcTalk"; + $self->{stage} = TALKING_TO_NPC; + $self->{time} = time; + + # Too many steps + } else { + if ( scalar @{$self->{steps}} == 1 && $self->{steps}[0] =~ /^n$/i ) { + #Here for backwards compatibility + $self->conversation_end; + + } else { + # TODO: maybe just warn about remaining steps and do not set error flag? + $self->setError(STEPS_AFTER_AFTER_NPC_CLOSE, "There are still steps to be done but the conversation has already ended (current step: ".$self->{steps}[0].")."); + } + } + } + } +} + +sub manage_wrong_sequence { + my ( $self, $error_message ) = @_; + + $self->{error_code} = WRONG_NPC_INSTRUCTIONS; + $self->{error_message} = $error_message; + error $self->{error_message}."\n"; + + my $method = (defined $config{'npcWrongStepsMethod'} ? $config{'npcWrongStepsMethod'} : 0); + warning "Using method '".$method."' defined on config key 'npcWrongStepsMethod' to deal with the error.\n", "ai_npcTalk"; + + # Will clean all remaining steps and wait for command + if ($method == 0) { + warning "Cleaning all remaining conversation steps, please input more steps using commands.\n", "ai_npcTalk"; + $self->{steps} = []; + + # Will move to the next step + } elsif ($method == 1) { + warning "Cleaning the current wrong step and moving to the next in queue.\n", "ai_npcTalk"; + shift @{$self->{steps}}; + + # Will try to end the conversation using a custom logic + } elsif ($method == 2) { + warning "Now openkore will try to auto-end this npc conversation.\n", "ai_npcTalk"; + $self->{trying_to_cancel} = 1; + + # Will relog to get out of the npc conversation + } elsif ($method == 3) { + warning "Now openkore will relog to try to end this conversation.\n", "ai_npcTalk"; + relog(); + } +} + +sub conversation_end { + my ($self) = @_; + $self->delHooks; + $self->setDone(); + debug "Task::TalkNPC::conversation_end called at ai npc_talk '".$ai_v{'npc_talk'}{'talk'}."'.\n", "ai_npcTalk"; + message TF("[TalkNPC] Done talking with %s (end)\n", $self->{target}), "ai_npcTalk"; +} + +## +# Actor $Task_TalkNPC->target() +# Requires: $self->getStatus() == Task::DONE && !defined($self->getError()) +# Ensures: defined(result) +# +# Returns the target Actor object. +sub target { + my ($self) = @_; + return $self->{target}; +} + +#only for testing +my $default_text = "eyelol"; +my $default_number = 1234; + +sub cancelTalk { + my ($self) = @_; + + if (defined $self->{error_message}) { + debug "[TalkNPC] Trying to auto close the conversation due to error.\n", "ai_npcTalk"; + } + + if ($ai_v{'npc_talk'}{'talk'} eq 'select') { + $messageSender->sendTalkResponse($talk{ID}, $#{$talk{responses}}); + $self->{sent_talk_response_cancel} = 1; + $self->{sent_talk_resp_cancel_time}{time} = time; + $self->{sent_talk_resp_cancel_time}{timeout} = 5; + + } elsif ($ai_v{'npc_talk'}{'talk'} eq 'next') { + $messageSender->sendTalkContinue($talk{ID}); + + } elsif ($ai_v{'npc_talk'}{'talk'} eq 'number') { + $messageSender->sendTalkNumber($talk{ID}, $default_number); + + } elsif ($ai_v{'npc_talk'}{'talk'} eq 'text') { + $messageSender->sendTalkText($talk{ID}, $default_text); + + } elsif ( $ai_v{'npc_talk'}{'talk'} eq 'buy_or_sell' ) { + $self->conversation_end; + $ai_v{'npc_talk'}{'talk'} = 'close'; + } elsif ( $ai_v{'npc_talk'}{'talk'} eq 'cash' ) { + $self->conversation_end; + $ai_v{'npc_talk'}{'talk'} = 'close'; + + } elsif ( $ai_v{'npc_talk'}{'talk'} eq 'store' ) { + $self->conversation_end; + $ai_v{'npc_talk'}{'talk'} = 'close'; + + } elsif ( $ai_v{'npc_talk'}{'talk'} eq 'sell' ) { + $self->conversation_end; + $ai_v{'npc_talk'}{'talk'} = 'close'; + + } elsif (!$ai_v{'npc_talk'}{'talk'}) { + $self->conversation_end; + $ai_v{'npc_talk'}{'talk'} = 'close'; + + } + +} + +# Actor findTarget(ActorList actorList) +# +# Check whether the target as specified in $self->{x} and $self->{y} is in the given +# actor list. Or if the target as specified in $self->{nameID} is in the given actor list. +# Returns the actor object if it's currently on screen and has a name, undef otherwise. +# +# Note: we require that the NPC's name is known, because otherwise talking +# may fail (TODO: what's the case exactly?). +sub findTarget { + my ($self, $actorList) = @_; + if ($self->{nameID}) { + my ($actor) = grep { $self->{nameID} eq $_->{nameID} } @{$actorList->getItems}; + if ( $actor && + ( $actor->{statuses}->{EFFECTSTATE_BURROW} || ($config{avoidHiddenActors} && ($actor->{type} == 111 || $actor->{type} == 139 || $actor->{type} == 2337)) ) && # HIDDEN_ACTOR TYPES + $self->{type} ne 'autotalk' ) + { + $self->setError(NPC_NOT_FOUND, T("Talk with a hidden NPC prevented.")); + return; + } + return $actor; + } + foreach my $actor (@{$actorList->getItems()}) { + my $pos = ($actor->isa('Actor::NPC')) ? $actor->{pos} : $actor->{pos_to}; + next if ($actor->{statuses}->{EFFECTSTATE_BURROW}); + next if ($config{avoidHiddenActors} && ($actor->{type} == 111 || $actor->{type} == 139 || $actor->{type} == 2337)); # HIDDEN_ACTOR TYPES + if ($pos->{x} == $self->{x} && $pos->{y} == $self->{y}) { + if (defined $actor->{name}) { + return $actor; + } else { + return undef; + } + } + } + return undef; +} + +sub noMoreSteps { + my ($self) = @_; + return (@{$self->{steps}} ? 0 : 1); +} + +sub waitingForSteps { + my ($self) = @_; + return 0 unless ($self->{stage} == TALKING_TO_NPC); + return 0 unless ($self->noMoreSteps); + return 0 if ($ai_v{'npc_talk'}{'talk'} eq 'next' && $config{autoTalkCont}); + my $ai_npc_talk_wait_to_answer = $timeout{'ai_npc_talk_wait_to_answer'}{'timeout'} ? $timeout{'ai_npc_talk_wait_to_answer'}{'timeout'} : 1.5; + return 0 unless (timeOut($ai_v{'npc_talk'}{'time'}, $ai_npc_talk_wait_to_answer)); + return 1; +} + +sub addSteps { + my ($self, $steps, $unshift) = @_; + + my @new_steps = parse_portal_conversation_args($steps); + + debug "Task::TalkNPC::addSteps has been called with value '".$steps."'.\n", "ai_npcTalk"; + + foreach my $step (@new_steps) { + return 0 unless $self->validateStep($step); + } + if ($unshift) { + unshift(@{$self->{steps}}, @new_steps); + } else { + push(@{$self->{steps}}, @new_steps); + } + return 1; +} + +sub validateStep { + my ($self, $step) = @_; + return 1 if ($step =~ /^(?:c|w\d+|n|t=.+|d\d+|a=.+|r(?:\d+|=.+|~\/.*?\/i?)|x|s|b|e|b\d+,\d+|k)$/); + $self->{error_code} = WRONG_SYNTAX_IN_STEPS; + $self->{error_message} = TF("Invalid NPC conversation code: %s.", $step); + return 0; +} + +1; diff --git a/openkore_llm_knowledge/core/src/TaskManager.pm b/openkore_llm_knowledge/core/src/TaskManager.pm new file mode 100644 index 0000000000..09adb7b3e2 --- /dev/null +++ b/openkore_llm_knowledge/core/src/TaskManager.pm @@ -0,0 +1,500 @@ +######################################################################### +# OpenKore - Task framework +# Copyright (c) 2006 OpenKore Team +# +# This software is open source, licensed under the GNU General Public +# License, version 2. +# Basically, this means that you're allowed to modify and distribute +# this software. However, if you distribute modified versions, you MUST +# also distribute the source code. +# See http://www.gnu.org/licenses/gpl.html for the full license. +######################################################################### +## +# MODULE DESCRIPTION: Task manager. +# +# Please read +# <a href="https://openkore.com/wiki/AI_subsystem_and_task_framework_overview"> +# the AI subsystem and task framework overview +# </a> +# for an overview. +package TaskManager; + +use strict; +use Carp::Assert; +use Modules 'register'; +use Task; +use Utils::Set; +use Utils::CallbackList; + +## +# TaskManager->new() +# +# Create a new TaskManager. +sub new { + my ($class) = @_; + my %self = ( + # Set<Task> + # Indexed set of currently active tasks. + # Invariant: + # for all $task in activeTasks: + # $task->getStatus() == Task::RUNNING or Task::STOPPED + # !$inactiveTasks->has($task) + # if $task is not in $grayTasks: + # $task owns all its mutexes. + activeTasks => new Set(), + + # Set<Task> + # Indexed set of currently inactive tasks. + # Invariant: + # for all $task in inactiveTasks: + # $task->getStatus() == Task::INTERRUPTED, Task::INACTIVE, or Task::STOPPED + # !$activeTasks->has($task) + # $task owns none of its mutexes. + inactiveTasks => new Set(), + + # Hash<String, Task> + # + # Currently active mutexes. The keys are the mutex names, and the + # values are the tasks that have a lock on the mutex (the mutex owner). + # + # Invariant: all tasks in $activeMutexes appear in $activeTasks. + activeMutexes => {}, + + # Set<Task> + # Indexed set of tasks for which the mutex list has changed. These tasks + # must be re-scheduled. + # Invariant: + # for all $task in grayTasks: + # $task->getStatus() == Task::RUNNING + # $activeTasks->has($task) + # !$inactiveTasks->has($task) + grayTasks => new Set(), + + # Hash<String, int> + # This variable remembers the number of instances for each task name. + # + # Invariant: + # All task names in $activeTasks and $inactiveTasks are in $tasksByName. + # for all $value in $tasksByName: + # defined($value) && $value > 0 + tasksByName => {}, + + # Maps a Task to an array of callback IDs. Used to unregister callbacks. + # Invariant: Every task in $activeTasks and $inactiveTasks is in $events. + events => {}, + + # Whether tasks should be rescheduled on the + # next iteration. + shouldReschedule => 0, + + onTaskFinished => new CallbackList() + ); + return bless \%self, $class; +} + +## +# void $TaskManager->add(Task task) +# Requires: $task->getStatus() == Task::INACTIVE +# +# Add a new task to this task manager. +sub add { + my ($self, $task) = @_; + assert(defined $task, "Can't add undefined task to Task Manager") if DEBUG; + assert($task->getStatus() == Task::INACTIVE, "Can only add inactive tasks to Task Manager (Current status: " . $task->getStatus() . ")") if DEBUG; + $self->{inactiveTasks}->add($task); + $self->{tasksByName}{$task->getName()}++; + $self->{shouldReschedule} = 1; + + my $ID1 = $task->onMutexesChanged->add($self, \&onMutexesChanged); + my $ID2 = $task->onStop->add($self, \&onStop); + $self->{events}{$task} = [$ID1, $ID2]; +} + +# Reschedule tasks. Do not call this method directly! +sub reschedule { + my ($self) = @_; + my $activeTasks = $self->{activeTasks}; + my $inactiveTasks = $self->{inactiveTasks}; + my $grayTasks = $self->{grayTasks}; + my $activeMutexes = $self->{activeMutexes}; + my $oldActiveTasks = $activeTasks->deepCopy(); + my $oldInactiveTasks = $inactiveTasks->deepCopy(); + + # The algorithm produces the following result: + # All active tasks do not conflict with each other, such tasks with higher + # priority will be active compared to conflicting tasks with lower priority. + # + # This algorithm does not produce the optimal result as that would take + # far too much time, but the result should be good enough in most cases. + + # Deactivate gray tasks that conflict with active mutexes. + while (@{$grayTasks} > 0) { + my $task = $grayTasks->get(0); + my $hasConflict = 0; + foreach my $mutex (@{$task->getMutexes()}) { + if (exists $activeMutexes->{$mutex}) { + $hasConflict = 1; + last; + } + } + + if ($hasConflict) { + # There is a conflict, so make this task inactive. + $self->deactivateTask($activeTasks, $inactiveTasks, + $grayTasks, $activeMutexes, $self->{tasksByName}, + $task); + } else { + # No conflict, so assign mutex locks to this task + # and remove its "gray" mark. + foreach my $mutex (@{$task->getMutexes()}) { + $activeMutexes->{$mutex} = $task; + } + shift @{$grayTasks}; + } + } + + # Activate inactive tasks such that active tasks don't conflict with each other. + for (my $i = 0; $i < @{$inactiveTasks}; $i++) { + my $task = $inactiveTasks->get($i); + my @conflictingMutexes; + + # If this task is stopped then we just throw it away. + if ($task->getStatus() == Task::STOPPED) { + $inactiveTasks->remove($task); + $i--; + + # Check whether this task conflicts with the currently locked mutexes. + } elsif ((@conflictingMutexes = intersect($activeMutexes, $task->getMutexes())) == 0) { + # No conflicts, we can activate this task. + $activeTasks->add($task); + $inactiveTasks->remove($task); + $i--; + foreach my $mutex (@{$task->getMutexes()}) { + $activeMutexes->{$mutex} = $task; + } + + } elsif (higherPriority($task, $activeMutexes, \@conflictingMutexes)) { + # There are conflicts. Does this task have a higher priority + # than all tasks specified by the conflicting mutexes? + # If yes, let it steal the mutex, activate it and deactivate + # the previous mutex owner. + + $activeTasks->add($task); + $inactiveTasks->remove($task); + $i--; + + foreach my $mutex (@{$task->getMutexes()}) { + my $oldTask = $activeMutexes->{$mutex}; + if ($oldTask) { + # Mutex was locked by lower priority task. + # Deactivate old task. + $self->deactivateTask($activeTasks, $inactiveTasks, + $grayTasks, $activeMutexes, $self->{tasksByName}, + $oldTask); + } + $activeMutexes->{$mutex} = $task; + } + } + } + + # Resume/activate newly activated tasks. + foreach my $task (@{$activeTasks}) { + if (!$oldActiveTasks->has($task)) { + my $status = $task->getStatus(); + if ($status == Task::INACTIVE) { + $task->activate(); + } elsif ($status == Task::INTERRUPTED) { + $task->resume(); + } + } + } + + # Interrupt newly deactivated tasks. + foreach my $task (@{$inactiveTasks}) { + if (!$oldInactiveTasks->has($task)) { + $task->interrupt(); + } + } + + $self->{shouldReschedule} = 0; +} + +## +# void $TaskManager->checkValidity() +# +# Check whether the internal invariants are correct. Dies if that is not the case. +sub checkValidity { + my ($self) = @_; + my $activeTasks = $self->{activeTasks}; + my $inactiveTasks = $self->{inactiveTasks}; + my $grayTasks = $self->{grayTasks}; + my $activeMutexes = $self->{activeMutexes}; + + foreach my $task (@{$activeTasks}) { + assert($task->getStatus() == Task::RUNNING || $task->getStatus() == Task::STOPPED, "Active task has wrong status (Current status: ". $task->getStatus() . ")"); + assert(!$inactiveTasks->has($task), "Task is both in the inactive and active tasks lists"); + if (!$grayTasks->has($task)) { + foreach my $mutex (@{$task->getMutexes()}) { + assert($activeMutexes->{$mutex} == $task, "Active mutex hash has invalid content"); + } + } + } + foreach my $task (@{$inactiveTasks}) { + my $status = $task->getStatus(); + assert($status = Task::INTERRUPTED || $status == Task::INACTIVE || $status == Task::STOPPED, "Inactive task has wrong status (Current status: ". $task->getStatus() . ")"); + assert(!$activeTasks->has($task), "Task is both in the inactive and active tasks lists"); + foreach my $mutex (@{$task->getMutexes()}) { + assert($activeMutexes->{$mutex} != $task, "Active mutex hash has invalid content"); + } + } + foreach my $task (@{$grayTasks}) { + assert($activeTasks->has($task), "Active task set has invalid content"); + assert(!$inactiveTasks->has($task), "Inactive task set has invalid content"); + } + + my $activeMutexes = $self->{activeMutexes}; + foreach my $mutex (keys %{$activeMutexes}) { + my $owner = $activeMutexes->{$mutex}; + assert($activeTasks->has($owner), "Active task set has invalid content"); + } + + my $tasksByName = $self->{tasksByName}; + foreach my $value (values %{$tasksByName}) { + assert(defined $value, "Task in tasksByName hash has undefined value"); + assert($value > 0, "Task in tasksByName hash has zero value"); + } +} + +## +# void $TaskManager->iterate() +# +# Reschedule tasks if necessary, and run one iteration of every active task. +sub iterate { + my ($self) = @_; + + $self->checkValidity() if DEBUG; + $self->reschedule() if ($self->{shouldReschedule}); + $self->checkValidity() if DEBUG; + + my $activeTasks = $self->{activeTasks}; + my $activeMutexes = $self->{activeMutexes}; + for (my $i = 0; $i < @{$activeTasks}; $i++) { + my $task = $activeTasks->get($i); + my $status = $task->getStatus(); + if ($status != Task::STOPPED) { + $task->iterate(); + $status = $task->getStatus(); + } + + # Remove tasks that are stopped or done. + my $status = $task->getStatus(); + if ($status == Task::DONE || $status == Task::STOPPED) { + $self->deactivateTask($activeTasks, $self->{inactiveTasks}, + $self->{grayTasks}, $activeMutexes, $self->{tasksByName}, + $task); + + # Remove the callbacks that we registered in this task. + my $IDs = $self->{events}{$task}; + $task->onMutexesChanged->remove($IDs->[0]); + $task->onStop->remove($IDs->[1]); + + $i--; + $self->{shouldReschedule} = 1; + } + } + $self->checkValidity() if DEBUG; +} + +## +# void $Taskmanager->stopAll() +# +# Tell all tasks (whether active or inactive) to stop. +sub stopAll { + my ($self) = @_; + foreach my $task (@{$self->{activeTasks}}, @{$self->{inactiveTasks}}) { + $task->stop(); + if ($task->getStatus() == Task::STOPPED) { + $self->{shouldReschedule} = 1; + } + # If the task does not stop immediately, then we'll + # be notified by the onStop event once it's stopped. + } +} + +## +# int $TaskManager->countTasksByName(String name) +# Ensures: result >= 0 +# +# Count the number of tasks that have the specified name. +sub countTasksByName { + my ($self, $name) = @_; + my $result = $self->{tasksByName}{$name}; + $result = 0 if (!defined $result); + return $result; +} + +## +# String $TaskManager->activeTasksString() +# +# Returns a string which describes the current active tasks. +sub activeTasksString { + my ($self) = @_; + return getTaskSetString($self->{activeTasks}); +} + +## +# String $TaskManager->inactiveTasksString() +# +# Returns a string which describes the currently inactive tasks. +sub inactiveTasksString { + my ($self) = @_; + return getTaskSetString($self->{inactiveTasks}); +} + +## +# String $TaskManager->activeMutexesString() +# +# Returns a string which describes the currently active mutexes. +sub activeMutexesString { + my ($self) = @_; + my $activeMutexes = $self->{activeMutexes}; + my @entries; + foreach my $mutex (keys %{$activeMutexes}) { + push @entries, "$mutex (<- " . $activeMutexes->{$mutex}->getName . ")"; + } + return join(', ', sort @entries); +} + +sub getTaskSetString { + my ($set) = @_; + if (@{$set}) { + my @names; + foreach my $task (@{$set}) { + push @names, $task->getName(); + } + return join(', ', @names); + } else { + return '-'; + } +} + +## +# CallbackList $TaskManager->onTaskFinished() +# +# This event is triggered when a task is finished, either successfully +# or with an error. +# +# The event argument is a hash containing this item:<br> +# <tt>task</tt> - The task that was finished. +sub onTaskFinished { + return $_[0]->{onTaskFinished}; +} + + +########## Private functions and callback handlers ########## + + +sub onMutexesChanged { + my ($self, $task) = @_; + if ($task->getStatus() == Task::RUNNING) { + $self->{grayTasks}->add($task); + + # Release its mutex locks. + my $activeMutexes = $self->{activeMutexes}; + foreach my $mutex (keys %{$activeMutexes}) { + if ($activeMutexes->{$mutex} == $task) { + delete $activeMutexes->{$mutex}; + } + } + } + $self->{shouldReschedule} = 1; +} + +sub onStop { + my ($self, $task) = @_; + if ($self->{inactiveTasks}->has($task)) { + $self->{shouldReschedule} = 1; + } +} + +# Return the intersection of the given sets. +# +# set1: A reference to a hash whose keys are the set elements. +# set2: A reference to an array which contains the elements in the set. +# Returns: An array containing the intersect elements. +sub intersect { + my ($set1, $set2) = @_; + my @result; + foreach my $element (@{$set2}) { + if (exists $set1->{$element}) { + push @result, $element; + } + } + return @result; +} + +# Check whether $task has a higher priority than all tasks specified +# by the given mutexes. +# +# task: The task to check. +# mutexTaskMapper: A hash which maps a mutex name to a task that owns that mutex. +# mutexes: A list of mutexes to check. +# Requires: All elements in $mutexes can be successfully mapped by $mutexTaskMapper. +sub higherPriority { + my ($task, $mutexTaskMapper, $mutexes) = @_; + my $priority = $task->getPriority(); + my $result = 1; + for (my $i = 0; $i < @{$mutexes} && $result; $i++) { + my $task2 = $mutexTaskMapper->{$mutexes->[$i]}; + $result = $result && $priority > $task2->getPriority(); + } + return $result; +} + +# Deactivate an active task by removing it from the active task list +# and the gray list, and removing its mutex locks. If the task isn't +# completed or stopped, then it will be added to the inactive task list. +sub deactivateTask { + my ($self, $activeTasks, $inactiveTasks, $grayTasks, $activeMutexes, $tasksByName, $task) = @_; + + my $status = $task->getStatus(); + if ($status != Task::DONE && $status != Task::STOPPED) { + $inactiveTasks->add($task); + } else { + my $name = $task->getName(); + $tasksByName->{$name}--; + assert($tasksByName->{$name} >= 0, "Task isn't registered in tasksByName hash") if DEBUG; + if ($tasksByName->{$name} == 0) { + delete $tasksByName->{$name}; + } + + $self->{onTaskFinished}->call($self, { task => $task }); + } + $activeTasks->remove($task); + $grayTasks->remove($task); + foreach my $mutex (@{$task->getMutexes()}) { + if ($activeMutexes->{$mutex} == $task) { + delete $activeMutexes->{$mutex}; + } + } +} + +# sub printTaskSet { +# my ($set, $name) = @_; +# my @names; +# foreach my $task (@{$set}) { +# push @names, $task->getName(); +# } +# print "$name = " . join(',', @names) . "\n"; +# } +# +# sub printActiveMutexes { +# my ($activeMutexes) = @_; +# my @entries; +# foreach my $mutex (keys %{$activeMutexes}) { +# push @entries, "$mutex (owned by " . $activeMutexes->{$mutex}->getName . ")"; +# } +# print "Active mutexes: " . join(', ', @entries) . "\n"; +# } + +1; diff --git a/openkore_llm_knowledge/core/src/doc/modules.txt b/openkore_llm_knowledge/core/src/doc/modules.txt new file mode 100644 index 0000000000..74620a732e --- /dev/null +++ b/openkore_llm_knowledge/core/src/doc/modules.txt @@ -0,0 +1,66 @@ +Actor +Actor::Monster +Actor::Player +Actor::You +Actor::Item +ActorList +AI +I18N +Interface +InventoryList +ChatQueue +Commands +Log +Field +Settings +Translation +Task +Task::Chained +Task::Function +Task::Timeout +Task::Wait +TaskManager +Modules +Network::ClientReceive +Network::DirectConnection +Network::PacketParser +Network::Receive +Network::Send +Network::XKore +Network::XKore2 +Network::XKoreProxy +Plugins +Skill +Match +Misc + +Base::Server +Base::Server::Client +Base::WebServer +Base::WebServer::Process + +Utils +Utils::AppLauncher +Utils::Benchmark +Utils::CallbackList +Utils::Crypton +Utils::DataStructures +Utils::Exceptions +Utils::LockFile +Utils::PerlLauncher +Utils::ObjectList +Utils::Set +Utils::StartupNotification::Launcher +Utils::StartupNotification::Launchee +Utils::StartupNotification::CreateSocketException +Utils::TextReader +Utils::PathFinding +Utils::Unix +Utils::Whirlpool +Utils::Win32 + +Bus::Messages + +Interface::Wx::Console +Interface::Wx::DockNotebook +Interface::Wx::DockNotebook::Page diff --git a/openkore_llm_knowledge/core/src/functions.pl b/openkore_llm_knowledge/core/src/functions.pl new file mode 100644 index 0000000000..4299d0df30 --- /dev/null +++ b/openkore_llm_knowledge/core/src/functions.pl @@ -0,0 +1,1120 @@ +######################################################################### +# This software is open source, licensed under the GNU General Public +# License, version 2. +# Basically, this means that you're allowed to modify and distribute +# this software. However, if you distribute modified versions, you MUST +# also distribute the source code. +# See http://www.gnu.org/licenses/gpl.html for the full license. +######################################################################### + +package main; +use strict; +use Time::HiRes qw(time usleep); +use IO::Socket; +use Text::ParseWords; +use Carp::Assert; +use Config; +use utf8; + +use Globals; +use Modules; +use Settings qw(%sys %options); +use Log qw(message warning error debug); +use Interface; +use Misc; +use Network::Receive; +use Network::Send (); +use Network::ClientReceive; +use Network::PaddedPackets; +use Network::MessageTokenizer; +use Commands; +use Plugins; +use Utils; +use ChatQueue; +use I18N; +use Utils::Benchmark; +use Utils::HttpReader; + + +####################################### +# PROGRAM INITIALIZATION +####################################### + +use constant { + STATE_LOAD_PLUGINS => 0, + STATE_LOAD_DATA_FILES => 1, + STATE_INIT_NETWORKING => 2, + STATE_INIT_PORTALS_DATABASE => 3, + STATE_PROMPT => 4, + STATE_FINAL_INIT => 5, + STATE_INITIALIZED => 6 +}; + +our $state; + +sub mainLoop { + Benchmark::begin('mainLoop') if DEBUG; + $state = STATE_LOAD_PLUGINS if (!defined $state); + + # Parse command input + my $input; + if (defined($input = $interface->getInput(0))) { + Misc::checkValidity("parseInput (pre)"); + parseInput($input); + Misc::checkValidity("parseInput"); + } + + + if ($state == STATE_INITIALIZED && $Settings::command) { + Commands::run($Settings::command); + $Settings::command = undef; + } + + if ($state == STATE_INITIALIZED) { + Plugins::callHook('mainLoop_pre'); + mainLoop_initialized(); + Plugins::callHook('mainLoop_post'); + + } elsif ($state == STATE_LOAD_PLUGINS) { + Log::message("$Settings::versionText\n"); + loadPlugins(); + return if $quit; + Log::message("\n"); + Plugins::callHook('start'); + $state = STATE_LOAD_DATA_FILES; + + } elsif ($state == STATE_LOAD_DATA_FILES) { + loadDataFiles(); + $state = STATE_INIT_NETWORKING; + + } elsif ($state == STATE_INIT_NETWORKING) { + initNetworking(); + $state = STATE_INIT_PORTALS_DATABASE; + + } elsif ($state == STATE_INIT_PORTALS_DATABASE) { + initPortalsDatabase(); + $state = STATE_PROMPT; + + } elsif ($state == STATE_PROMPT) { + promptFirstTimeInformation(); + $state = STATE_FINAL_INIT; + + } elsif ($state == STATE_FINAL_INIT) { + finalInitialization(); + $state = STATE_INITIALIZED; + + } else { + die "Unknown state $state."; + } + + Benchmark::end('mainLoop') if DEBUG; + # Reload any modules that requested to be reloaded + Modules::reloadAllInQueue(); +} + +sub loadPlugins { + eval { + Plugins::loadAll(); + }; + my $e; + if ($e = caught('Plugin::LoadException')) { + $interface->errorDialog(TF("This plugin cannot be loaded because of a problem in the plugin. " . + "Please notify the plugin's author about this problem, " . + "or remove the plugin so %s can start.\n\n" . + "The error message is:\n" . + "%s", + $Settings::NAME, $e->message)); + $quit = 1; + } elsif ($e = caught('Plugin::DeniedException')) { + $interface->errorDialog($e->message); + $quit = 1; + } elsif ($@) { + die $@; + } + + # Allow plugins to use command line arguments. + Plugins::callHook('parse_command_line'); + main::checkEmptyArguments(); +} + +sub loadDataFiles { + # These pragmas are necessary in order to support non-ASCII filenames. + # If we use UTF-8 strings then Perl will think the file doesn't exist, + # if $Settings::control_folder or $Settings::tables_folder contains + # non-ASCII characters. + no encoding 'utf8'; + + # Add loading of Control files + Settings::addControlFile(Settings::getConfigFilename(), + loader => [\&parseConfigFile, \%config], + internalName => 'config.txt', + autoSearch => 0); + Settings::addControlFile(Settings::getControlFilename("consolecolors.txt"), + internalName => 'consolecolors.txt', + loader => [\&parseSectionedFile, \%consoleColors], + autoSearch => 0); + Settings::addControlFile(Settings::getMonControlFilename(), + loader => [\&parseMonControl, \%mon_control], + internalName => 'mon_control.txt', + autoSearch => 0); + Settings::addControlFile(Settings::getItemsControlFilename(), + loader => [\&parseItemsControl, \%items_control], + internalName => 'items_control.txt', + autoSearch => 0); + Settings::addControlFile(Settings::getBuyerShopFilename(), + loader => [\&parseShopControl, \%buyer_shop], + internalName => 'buyer_shop.txt', + autoSearch => 0); + Settings::addControlFile(Settings::getShopFilename(), + loader => [\&parseShopControl, \%shop], + internalName => 'shop.txt', + autoSearch => 0); + Settings::addControlFile(Settings::getControlFilename('overallAuth.txt'), + internalName => 'overallAuth.txt', + loader => [\&parseDataFile, \%overallAuth], + autoSearch => 0); + Settings::addControlFile(Settings::getControlFilename('pickupitems.txt'), + internalName => 'pickupitems.txt', + loader => [\&parseDataFile_lc, \%pickupitems], + autoSearch => 0); + Settings::addControlFile(Settings::getControlFilename('responses.txt'), + internalName => 'responses.txt', + loader => [\&parseResponses, \%responses], + autoSearch => 0); + Settings::addControlFile(Settings::getControlFilename('timeouts.txt'), + internalName => 'timeouts.txt', + loader => [\&parseTimeouts, \%timeout], + autoSearch => 0); + Settings::addControlFile(Settings::getControlFilename('chat_resp.txt'), + internalName => 'chat_resp.txt', + loader => [\&parseChatResp, \@chatResponses], + autoSearch => 0); + Settings::addControlFile(Settings::getControlFilename('avoid.txt'), + internalName => 'avoid.txt', + loader => [\&parseAvoidControl, \%avoid], + autoSearch => 0); + Settings::addControlFile(Settings::getControlFilename('priority.txt'), + internalName => 'priority.txt', + loader => [\&parsePriority, \%priority], + autoSearch => 0); + Settings::addControlFile(Settings::getControlFilename('routeweights.txt'), + internalName => 'routeweights.txt', + loader => [\&parseDataFile, \%routeWeights], + autoSearch => 0); + Settings::addControlFile(Settings::getControlFilename('arrowcraft.txt'), + internalName => 'arrowcraft.txt', + loader => [\&parseDataFile_lc, \%arrowcraft_items], + autoSearch => 0); + + # Loading of Table files + # Load Servers.txt first + Settings::addTableFile('servers.txt', + internalName => 'servers.txt', + loader => [\&parseSectionedFile, \%masterServers], + onLoaded => \&processServerSettings ); + # Load RecvPackets.txt second + Settings::addTableFile(Settings::getRecvPacketsFilename(), + internalName => 'recvpackets.txt', + loader => [\&parseRecvpackets, \%rpackets]); + + # Add 'Old' table pack, if user set + if ( $sys{locale_compat} == 1) { + # Holder for new path + my @new_tables; + my $pathDelimiter = ($^O eq 'MSWin32') ? ';' : ':'; + if ($options{tables}) { + foreach my $dir ( split($pathDelimiter, $options{tables}) ) { + push @new_tables, $dir . '/Old'; + } + } else { + push @new_tables, 'tables/Old'; + } + # now set up new path to table folder + Settings::setTablesFolders(@new_tables, Settings::getTablesFolders()); + } + + # Load all other tables + Settings::addTableFile('cities.txt', + internalName => 'cities.txt', + loader => [\&parseROLUT, \%cities_lut]); + Settings::addTableFile('directions.txt', + internalName => 'directions.txt', + loader => [\&parseDataFile2, \%directions_lut]); + Settings::addTableFile('elements.txt', + internalName => 'elements.txt', + loader => [\&parseROLUT, \%elements_lut]); + Settings::addTableFile('emotions.txt', + internalName => 'emotions.txt', + loader => [\&parseEmotionsFile, \%emotions_lut]); + Settings::addTableFile('equiptypes.txt', + internalName => 'equiptypes.txt', + loader => [\&parseDataFile2, \%equipTypes_lut]); + Settings::addTableFile('haircolors.txt', + internalName => 'haircolors.txt', + loader => [\&parseDataFile2, \%haircolors]); + Settings::addTableFile('headgears.txt', + internalName => 'headgears.txt', + loader => [\&parseArrayFile, \@headgears_lut, { hide_comments => 0 }]); + Settings::addTableFile('items.txt', + internalName => 'items.txt', + loader => [\&parseROLUT, \%items_lut]); + Settings::addTableFile('itemsdescriptions.txt', + internalName => 'itemsdescriptions.txt', + loader => [\&parseRODescLUT, \%itemsDesc_lut], mustExist => 0); + Settings::addTableFile('itemslots.txt', + internalName => 'itemslots.txt', + loader => [\&parseROSlotsLUT, \%itemSlots_lut]); + Settings::addTableFile('itemslotcounttable.txt', + internalName => 'itemslotcounttable.txt', + loader => [\&parseROLUT, \%itemSlotCount_lut]); + Settings::addTableFile('itemtypes.txt', + internalName => 'itemtypes.txt', + loader => [\&parseDataFile2, \%itemTypes_lut]); + Settings::addTableFile('resnametable.txt', + internalName => 'resnametable.txt', + loader => [\&parseROLUT, \%mapAlias_lut, 1, ".gat"]); + Settings::addTableFile('maps.txt', + internalName => 'maps.txt', + loader => [\&parseROLUT, \%maps_lut]); + Settings::addTableFile('monsters.txt', + internalName => 'monsters.txt', + loader => [\&parseDataFile2, \%monsters_lut], createIfMissing => 1); + Settings::addTableFile('npcs.txt', + internalName => 'npcs.txt', + loader => [\&parseNPCs, \%npcs_lut], createIfMissing => 1); + Settings::addTableFile('packetdescriptions.txt', + internalName => 'packetdescriptions.txt', + loader => [\&parseSectionedFile, \%packetDescriptions], mustExist => 0); + Settings::addTableFile('portals.txt', + internalName => 'portals.txt', + loader => [\&parsePortals, \%portals_lut, \@portals_lut_missed]); + Settings::addTableFile('portals_commands.txt', + internalName => 'portals_commands.txt', + loader => [\&parsePortalsCommands, \%portals_commands], mustExist => 0); + Settings::addTableFile('portals_spawns.txt', + internalName => 'portals_spawns.txt', + loader => [\&parsePortalsSpawns, \%portals_spawns], mustExist => 0); + Settings::addTableFile('portals_airship.txt', + internalName => 'portals_airship.txt', + loader => [\&parsePortalsAirship, \%portals_airships], mustExist => 0); + Settings::addTableFile('portalsLOS.txt', + internalName => 'portalsLOS.txt', + loader => [\&parsePortalsLOS, \%portals_los], createIfMissing => 1); + Settings::addTableFile('sex.txt', + internalName => 'sex.txt', + loader => [\&parseDataFile2, \%sex_lut]); + Settings::addTableFile('SKILL_id_handle.txt', + internalName => 'SKILL_id_handle.txt', + loader => \&Skill::StaticInfo::parseSkillsDatabase_id2handle); + Settings::addTableFile('skillnametable.txt', + internalName => 'skillnametable.txt', + loader => \&Skill::StaticInfo::parseSkillsDatabase_handle2name, mustExist => 0); + Settings::addTableFile('spells.txt', + internalName => 'spells.txt', + loader => [\&parseDataFile2, \%spells_lut]); + Settings::addTableFile('skillsdescriptions.txt', + internalName => 'skillsdescriptions.txt', + loader => [\&parseRODescLUT, \%skillsDesc_lut], mustExist => 0); + Settings::addTableFile('skillssp.txt', + internalName => 'skillssp.txt', + loader => \&Skill::StaticInfo::parseSPDatabase); + Settings::addTableFile('STATUS_id_handle.txt', + internalName => 'STATUS_id_handle.txt', + loader => [\&parseDataFile2, \%statusHandle]); + Settings::addTableFile('STATE_id_handle.txt', + internalName => 'STATE_id_handle.txt', + loader => [\&parseDataFile2, \%stateHandle]); + Settings::addTableFile('LOOK_id_handle.txt', + internalName => 'LOOK_id_handle.txt', + loader => [\&parseDataFile2, \%lookHandle]); + Settings::addTableFile('AILMENT_id_handle.txt', + internalName => 'AILMENT_id_handle.txt', + loader => [\&parseDataFile2, \%ailmentHandle]); + Settings::addTableFile('MAPTYPE_id_handle.txt', + internalName => 'MAPTYPE_id_handle.txt', + loader => [\&parseDataFile2, \%mapTypeHandle]); + Settings::addTableFile('MAPPROPERTY_TYPE_id_handle.txt', + internalName => 'MAPPROPERTY_TYPE_id_handle.txt', + loader => [\&parseDataFile2, \%mapPropertyTypeHandle]); + Settings::addTableFile('MAPPROPERTY_INFO_id_handle.txt', + internalName => 'MAPPROPERTY_INFO_id_handle.txt', + loader => [\&parseDataFile2, \%mapPropertyInfoHandle]); + Settings::addTableFile('statusnametable.txt', + internalName => 'statusnametable.txt', + loader => [\&parseDataFile2, \%statusName], mustExist => 0); + Settings::addTableFile('skillsarea.txt', + internalName => 'skillsarea.txt', + loader => [\&parseDataFile2, \%skillsArea]); + Settings::addTableFile('skillsencore.txt', + internalName => 'skillsencore.txt', + loader => [\&parseList, \%skillsEncore]); + Settings::addTableFile('quests.txt', + internalName => 'quests.txt', + loader => [\&parseROQuestsLUT, \%quests_lut], mustExist => 0); + Settings::addTableFile('effects.txt', + internalName => 'effects.txt', + loader => [\&parseDataFile2, \%effectName], mustExist => 0); + Settings::addTableFile('msgstringtable.txt', + internalName => 'msgstringtable.txt', + loader => [\&parseArrayFile, \@msgTable, { hide_comments => 0 }], mustExist => 0); + Settings::addTableFile('hateffect_id_handle.txt', + internalName => 'hateffect_id_handle.txt', + loader => [\&parseDataFile2, \%hatEffectHandle]); + Settings::addTableFile('hateffect_name.txt', + internalName => 'hateffect_name.txt', + loader => [\&parseDataFile2, \%hatEffectName], mustExist => 0); + Settings::addTableFile('item_stack_limit.txt', + internalName => 'item_stack_limit.txt', + loader => [\&parseItemStackLimit, \%itemStackLimit]); + Settings::addTableFile('ITEMOPTION_id_handle.txt', + internalName => 'ITEMOPTION_id_handle.txt', + loader => [\&parseDataFile2, \%itemOptionHandle], mustExist => 0); + Settings::addTableFile('item_options.txt', + internalName => 'item_options.txt', + loader => [\&parseROLUT, \%itemOption_lut], mustExist => 0); + Settings::addTableFile('title_name.txt', + internalName => 'title_name.txt', + loader => [\&parseDataFile2, \%title_lut], mustExist => 0); + Settings::addTableFile('attendance_rewards.txt', + internalName => 'attendance_rewards.txt', + loader => [\&parseAttendanceRewards, \%attendance_rewards], mustExist => 0); + Settings::addTableFile('achievement_list.txt', + internalName => 'achievement_list.txt', + loader => [\&parseAchievementFile, \%achievements], mustExist => 0); + + use utf8; + + Plugins::callHook('start2'); + eval { + my $progressHandler = sub { + my ($filename) = @_; + message TF("Loading %s...\n", $filename); + }; + Settings::loadAll($progressHandler); + }; + my $e; + if ($e = caught('UTF8MalformedException')) { + $interface->errorDialog(TF( + "The file %s must be in UTF-8 encoding.", + $e->textfile)); + $quit = 1; + } elsif ($e = caught('FileNotFoundException')) { + $interface->errorDialog(TF("Unable to load the file %s.", $e->filename)); + $quit = 1; + } elsif ($@) { + die $@; + } + return if $quit; + + Settings::update_log_filenames(); + + Plugins::callHook('start3'); + + if ($config{'secureAdminPassword'} eq '1') { + # This is where we induldge the paranoid and let them have session generated admin passwords + Log::message(T("\nGenerating session Admin Password...\n")); + configModify("adminPassword", vocalString(8)); + } + #} elsif ($config{'adminPassword'} eq '') { + # # This is where we protect the stupid from having a blank admin password + # Log::message(T("\nAuto-generating Admin Password due to blank...\n")); + # configModify("adminPassword", vocalString(8)); + #} +} + +sub initNetworking { + our $XKore_dontRedirect = 0; + my $XKore_version = $config{XKore}; + eval { + $clientPacketHandler = Network::ClientReceive->new; + + if ($XKore_version eq "1") { + # Inject DLL to running Ragnarok process + require Network::XKore; + $net = new Network::XKore; + } elsif ($XKore_version eq "2") { + # Run as a proxy bot, allowing Ragnarok to connect while botting + require Network::DirectConnection; + require Network::XKore2; + $net = new Network::DirectConnection; + Network::XKore2::start(); + } elsif ($XKore_version eq "3") { + # Proxy Ragnarok client connection + require Network::XKoreProxy; + $net = new Network::XKoreProxy; + } else { + # Run as a standalone bot, with no interface to the official RO client + require Network::DirectConnection; + $net = new Network::DirectConnection; + } + }; + if ($@) { + # Problem with networking. + $interface->errorDialog($@); + $quit = 1; + return; + } + + if ($sys{bus}) { + require Bus::Client; + require Bus::Handlers; + my $host = $sys{bus_server_host}; + my $port = $sys{bus_server_port}; + my $userAgent = $sys{bus_userAgent}; + $host = undef if ($host eq ''); + $port = undef if ($port eq ''); + $bus = new Bus::Client(host => $host, port => $port, userAgent => $userAgent); + our $busMessageHandler = new Bus::Handlers($bus); + } + + Network::PaddedPackets::init(); +} + +sub initPortalsDatabase { + # $config{portalCompile} + # -1: skip compile + # 0: ask user + # 1: auto compile + + # TODO: detect when another instance already compiles portals? + + return if $config{portalCompile} < 0; + + Log::message(T("Checking for new portals... ")); + if (compilePortals_check()) { + Log::message(T("found new portals!\n")); + my $choice = $config{portalCompile} ? 0 : $interface->showMenu( + T("New portals have been added to the portals database. " . + "The portals database must be compiled before the new portals can be used. " . + "Would you like to compile portals now?\n"), + [T("Yes, compile now."), T("No, don't compile it.")], + title => T("Compile portals?")); + if ($choice == 0) { + Log::message(T("compiling portals") . "\n\n"); + compilePortals(); + } else { + Log::message(T("skipping compile") . "\n\n"); + } + } else { + Log::message(T("none found\n\n")); + } +} + +sub promptFirstTimeInformation { + if ($net->version != 1) { + my $msg; + if (!$config{username}) { + $msg = $interface->query(T("Please enter your Ragnarok Online username.")); + if (!defined($msg)) { + $quit = 1; + return; + } + configModify('username', $msg, 1); + } + if (!$config{password}) { + $msg = $interface->query(T("Please enter your Ragnarok Online password."), isPassword => 1); + if (!defined($msg)) { + $quit = 1; + return; + } + configModify('password', $msg, 1); + } + } +} + +sub processServerSettings { + my $filename = shift; + # Select Master server on Demand + + if ($config{master} eq "" || $config{master} =~ /^\d+$/ || !exists $masterServers{$config{master}}) { + my @servers = sort { lc($a) cmp lc($b) } keys(%masterServers); + @servers = grep { not $masterServers{$_}{dead} } @servers; + my $choice = $interface->showMenu( + T("Please choose a master server to connect to."), + [map { $masterServers{$_}{title} || $_ } @servers], + title => T("Master servers")); + if ($choice == -1) { + $quit = 1; + return; + } else { + bulkConfigModify({ + master => $servers[$choice], + # ask for server and character if we're connected to "new" master server + server => '', + char => '', + }, 1); + } + } + + # Parse server settings + my $master = $masterServer = $masterServers{$config{master}}; + + # Stop if server now marked as dead + if ($master->{dead}) { + $interface->errorDialog($master->{dead_message} || TF("Server you've selected (%s) is now marked as dead.", $master->{title} || $config{master})); + $quit = 1; + return; + } + + # Check for required options + my @options; + if ($config{'XKore'} eq "1") { + @options = 'serverType'; + } else { + @options = qw(ip port master_version version serverType); + } + if (my @missingOptions = grep { $master->{$_} eq '' } @options) { + $interface->errorDialog(TF("Required server options are not set: %s\n", "@missingOptions")); + $quit = 1; + return; + } + + # Process adding Custom Table folders + if($masterServer->{addTableFolders}) { + Settings::addTablesFolders($masterServer->{addTableFolders}); + } + + # Process setting custom recvpackets option + Settings::setRecvPacketsName($masterServer->{recvpackets} && $masterServer->{recvpackets} ne '' ? $masterServer->{recvpackets} : Settings::getRecvPacketsFilename() ); +} + +sub finalInitialization { + $incomingMessages = new Network::MessageTokenizer(\%rpackets); + $outgoingClientMessages = new Network::MessageTokenizer(\%rpackets); + + $KoreStartTime = time; + $conState = 1; + our $nextConfChangeTime; + $bExpSwitch = 2; + $jExpSwitch = 2; + $totalBaseExp = 0; + $totalJobExp = 0; + $startTime_EXP = time; + $taskManager = new TaskManager(); + + if (DEBUG) { + # protect various stuff from autovivification + + require Utils::BlessedRefTie; + tie $char, 'Tie::BlessedRef'; + + require Utils::ActorHashTie; + tie %items, 'Tie::ActorHash'; + tie %monsters, 'Tie::ActorHash'; + tie %players, 'Tie::ActorHash'; + tie %pets, 'Tie::ActorHash'; + tie %npcs, 'Tie::ActorHash'; + tie %portals, 'Tie::ActorHash'; + tie %slaves, 'Tie::ActorHash'; + tie %elementals, 'Tie::ActorHash'; + } + + $itemsList = new ActorList('Actor::Item'); + $monstersList = new ActorList('Actor::Monster'); + $playersList = new ActorList('Actor::Player'); + $petsList = new ActorList('Actor::Pet'); + $npcsList = new ActorList('Actor::NPC'); + $portalsList = new ActorList('Actor::Portal'); + $slavesList = new ActorList('Actor::Slave'); + $elementalsList = new ActorList('Actor::Elemental'); + $venderItemList = InventoryList->new; + $buyerItemList = InventoryList->new; + $storeList = InventoryList->new; + $cashList = InventoryList->new; + foreach my $list ($itemsList, $monstersList, $playersList, $petsList, $npcsList, $portalsList, $slavesList, $elementalsList) { + $list->onAdd()->add(undef, \&actorAdded); + $list->onRemove()->add(undef, \&actorRemoved); + $list->onClearBegin()->add(undef, \&actorListClearing); + } + + StdHttpReader::init(); + initStatVars(); + initRandomRestart(); +# initUserSeed(); + initConfChange(); + Log::initLogFiles(); + $timeout{'injectSync'}{'time'} = time; + + Log::message("\n"); + + Log::message("Initialized, use 'connect' to continue\n") if $Settings::no_connect; + + Plugins::callHook('initialized'); + XSTools::initVersion(); +} + + +####################################### +# VARIABLE INITIALIZATION FUNCTIONS +####################################### + +# Calculate next random restart time. +# The restart time will be autoRestartMin + rand(autoRestartSeed) +sub initRandomRestart { + if ($config{'autoRestart'}) { + my $autoRestart = $config{'autoRestartMin'} + int(rand $config{'autoRestartSeed'}); + message TF("Next restart in %s\n", timeConvert($autoRestart)), "system"; + configModify("autoRestart", $autoRestart, 1); + } +} + +# Initialize random configuration switching time +sub initConfChange { + my $i = 0; + while (exists $ai_v{"autoConfChange_${i}_timeout"}) { + delete $ai_v{"autoConfChange_${i}_timeout"}; + $i++; + } + + $i = 0; + while (exists $config{"autoConfChange_$i"}) { + $ai_v{"autoConfChange_${i}_timeout"} = $config{"autoConfChange_${i}_minTime"} + + int(rand($config{"autoConfChange_${i}_varTime"})); + $i++; + } + $lastConfChangeTime = time; +} + +# Initialize variables when you start a connection to a map server +sub initConnectVars { + # we must use $chars[$config{char}] here because $char may not be set + initMapChangeVars(); + if ($char) { + $char->{skills} = {}; + delete $char->{spirits}; + delete $char->{mute_period}; + delete $char->{muted}; + delete $char->{party}; + delete $char->{statuses}; + $char->{party}{joined} = 0; + } + undef @skillsID; + undef @partyUsersID; + undef %cashShop; +} + +# Initialize variables when you change map (after a teleport or after you walked into a portal) +sub initMapChangeVars { + # we must use $chars[$config{char}] here because $char may not be set + @portalsID_old = @portalsID; + %portals_old = %portals; + foreach (@portalsID_old) { + next if (!$_ || !$portals_old{$_}); + $portals_old{$_}{gone_time} = time if (!$portals_old{$_}{gone_time}); + } + + # this is just used for portalRecord (add opposite portal by guessing method) + if ($char) { + $char->{old_pos_to} = {%{$char->{pos_to}}} if ($char->{pos_to}); + delete $char->{sitting}; + delete $char->{dead}; + delete $char->{warp}; + delete $char->{casting}; + delete $char->{homunculus}{appear_time} if $char->{homunculus}; + $char->inventory->onMapChange(); + $char->cart->onMapChange(); # Clear the cart but do not close it. + $char->storage->close() if ($char->storage->isReady()); + } + $timeout{play}{time} = time; + $timeout{ai_sync}{time} = time; + $timeout{ai_sit_idle}{time} = time; + $timeout{ai_teleport}{time} = time; + $timeout{ai_teleport_idle}{time} = time; + $timeout{ai_teleport_safe_force}{time} = time; + + delete $timeout{ai_teleport_retry}{time}; + delete $timeout{ai_teleport_delay}{time}; + + undef %incomingDeal; + undef %outgoingDeal; + undef %currentDeal; + undef $currentChatRoom; + undef @currentChatRoomUsers; + undef @itemsID; + undef @identifyID; + undef @spellsID; + undef @arrowCraftID; + undef %items; + undef %spells; + undef %incomingParty; + undef %talk; + delete $ai_v{'npc_talk'} if (exists $ai_v{'npc_talk'}); + $ai_v{temp} = {}; + undef $venderID; + undef $venderCID; + undef @venderListsID; + undef %venderLists; + undef $buyerID; + undef $buyingStoreID; + undef @buyerListsID; + undef %buyerLists; + undef %incomingGuild; + undef @chatRoomsID; + undef %chatRooms; + undef %createdChatRoom; + undef @lastpm; + undef %incomingFriend; + undef $repairList; + undef $devotionList; + undef $cookingList; + undef $makableList; + undef $mailList; + undef $rodexList; + undef $rodexWrite; + undef $skillExchangeItem; + undef $refineUI; + undef $currentCookingType; + undef $mergeItemList; + $captcha_state = 0; + $universalCatalog{open} = 0; + $universalCatalog{has_next} = 0; + delete $universalCatalog{type}; + + $itemsList->clear(); + $monstersList->clear(); + $playersList->clear(); + $petsList->clear(); + $portalsList->clear(); + $npcsList->clear(); + $slavesList->clear(); + $elementalsList->clear(); + $venderItemList->clear; + $buyerItemList->clear; + $storeList->clear; + $cashList->clear; + + @{$universalCatalog{list}} = (); + @unknownPlayers = (); + @unknownNPCs = (); + @sellList = (); + + $shopstarted = 0; + $buyershopstarted = 0; + $bankingopened = 0; + $timeout{ai_shop}{time} = time; + $timeout{ai_storageAuto}{time} = time + 5; + $timeout{ai_buyAuto}{time} = time + 5; + $timeout{ai_shop}{time} = time; + + AI::clear(qw(attack move)); + AI::SlaveManager::clear("attack", "route", "move"); + ChatQueue::clear; + + Plugins::callHook('packet_mapChange'); + + Settings::update_log_filenames(); +} + +# Initialize variables when your character logs in +sub initStatVars { + $totaldmg = 0; + $dmgpsec = 0; + $startedattack = 0; + $monstarttime = 0; + $monkilltime = 0; + $elasped = 0; + $totalelasped = 0; +} + + +##################################################### +# MISC. MAIN LOOP FUNCTIONS +##################################################### + + +# This function is called every time in the main loop, when OpenKore has been +# fully initialized. +sub mainLoop_initialized { + Benchmark::begin("mainLoop_part1") if DEBUG; + + # Handle connection states + $net->checkConnection(); + + if (defined $timeout{'char_login_pause'}{'time'} && timeOut($timeout{'char_login_pause'})) { + CharacterLogin(); + undef $timeout{'char_login_pause'}{'time'}; + } + + # Receive and handle data from the RO server + my $data = $net->serverRecv; + if (defined($data) && length($data) > 0) { + Benchmark::begin("parseMsg") if DEBUG; + + $incomingMessages->add($data); + $net->clientSend($_) for $packetParser->process( + $incomingMessages, $packetParser + ); + $net->clientFlush() if (UNIVERSAL::isa($net, 'Network::XKoreProxy')); + Benchmark::end("parseMsg") if DEBUG; + } + + # Receive and handle data from the RO client + $data = $net->clientRecv; + if (defined($data) && length($data) > 0) { + my $type; + #$messageSender->encryptMessageID(\$data); + $outgoingClientMessages->add($data); + $messageSender->sendToServer($_) for $messageSender->process( + $outgoingClientMessages, $clientPacketHandler + ); + } + + # GameGuard support + if ($masterServer->{gameGuard} && ($net->version != 1 || ($net->version == 1 && $masterServer->{gameGuard} eq '2'))) { + my $result = Poseidon::Client::getInstance()->getResult(); + if (defined($result)) { + debug "Received Poseidon result.\n", "poseidon"; + #$messageSender->encryptMessageID(\$result, unpack("v", $result)); + $messageSender->sendToServer($result); + } + } + + Benchmark::end("mainLoop_part1") if DEBUG; + Benchmark::begin("mainLoop_part2") if DEBUG; + + # Process AI + if ($net->getState() == Network::IN_GAME && timeOut($timeout{ai}) && $net->serverAlive()) { + Misc::checkValidity("AI (pre)"); + Benchmark::begin("ai") if DEBUG; + AI::CoreLogic::iterate(); + Benchmark::end("ai") if DEBUG; + Benchmark::begin("ai_homunculus") if DEBUG; + AI::SlaveManager::iterate(); + Benchmark::end("ai_homunculus") if DEBUG; + Misc::checkValidity("AI"); + return if $quit; + } + Misc::checkValidity("mainLoop_part2.1"); + $taskManager->iterate(); + + Benchmark::end("mainLoop_part2") if DEBUG; + Benchmark::begin("mainLoop_part3") if DEBUG; + + # Process bus events. + $bus->iterate() if ($bus); + Misc::checkValidity("mainLoop_part2.2"); + + + ###### Other stuff that's run in the main loop ##### + + if ($config{'autoRestart'} && time - $KoreStartTime > $config{'autoRestart'} + && $net->getState() == Network::IN_GAME && !AI::inQueue(qw/attack take items_take/)) { + message T("\nAuto-restarting!!\n"), "system"; + + if ($config{'autoRestartSleep'}) { + my $sleeptime = $config{'autoSleepMin'} + int(rand $config{'autoSleepSeed'}); + $timeout_ex{'master'}{'timeout'} = $sleeptime; + $sleeptime = $timeout{'reconnect'}{'timeout'} if ($sleeptime < $timeout{'reconnect'}{'timeout'}); + message TF("Sleeping for %s\n", timeConvert($sleeptime)), "system"; + } else { + $timeout_ex{'master'}{'timeout'} = $timeout{'reconnect'}{'timeout'}; + } + + $timeout_ex{'master'}{'time'} = time; + $KoreStartTime = time + $timeout_ex{'master'}{'timeout'}; + AI::clear(); + AI::SlaveManager::clear(); + undef %ai_v; + $net->serverDisconnect; + $net->setState(Network::NOT_CONNECTED); + undef $conState_tries; + initRandomRestart(); + } + + Misc::checkValidity("mainLoop_part2.3"); + + # Automatically switch to a different config file + # based on certain conditions + if ($net->getState() == Network::IN_GAME && timeOut($AI::Timeouts::autoConfChangeTime, 0.5) + && !AI::inQueue(qw/attack take items_take/)) { + my $selected; + my $i = 0; + while (exists $config{"autoConfChange_$i"}) { + if ($config{"autoConfChange_$i"} + && ( !$config{"autoConfChange_${i}_minTime"} || timeOut($lastConfChangeTime, $ai_v{"autoConfChange_${i}_timeout"}) ) + && inRange($char->{lv}, $config{"autoConfChange_${i}_lvl"}) + && inRange($char->{lv_job}, $config{"autoConfChange_${i}_joblvl"}) + && ( !$config{"autoConfChange_${i}_isJob"} || $jobs_lut{$char->{jobID}} eq $config{"autoConfChange_${i}_isJob"} ) + ) { + $selected = $config{"autoConfChange_$i"}; + last; + } + $i++; + } + + if ($selected) { + # Choose a random configuration file + my @files = split(/,+/, $selected); + my $file = $files[rand(@files)]; + message TF("Changing configuration file (from \"%s\" to \"%s\")...\n", $Settings::config_file, $file), "system"; + + # A relogin is necessary if the server host/port, username + # or char is different. + my $oldMaster = $masterServer; + my $oldUsername = $config{'username'}; + my $oldChar = $config{'char'}; + + switchConfigFile($file); + + my $master = $masterServer = $masterServers{$config{'master'}}; + if ($net->version != 1 + && $oldMaster->{ip} ne $master->{ip} + || $oldMaster->{port} ne $master->{port} + || $oldMaster->{master_version} ne $master->{master_version} + || $oldMaster->{version} ne $master->{version} + || $oldUsername ne $config{'username'} + || $oldChar ne $config{'char'}) { + AI::clear; + AI::SlaveManager::clear(); + relog(); + } else { + AI::clear("move", "route", "mapRoute"); + AI::SlaveManager::clear("move", "route", "mapRoute"); + } + + initConfChange(); + } + + $AI::Timeouts::autoConfChangeTime = time; + } + + #processStatisticsReporting() unless ($sys{sendAnonymousStatisticReport} eq "0"); + + Misc::checkValidity("mainLoop_part2.4"); + + # Set interface title + my $charName; + my $title; + $charName = "$char->{name}: " if ($char); + if ($net->getState() == Network::IN_GAME) { + my ($basePercent, $jobPercent, $weight, $pos); + + assert(defined $char); + $basePercent = sprintf("%.2f", $char->exp_base_percent); + $jobPercent = sprintf("%.2f",$char->exp_job_percent); + $weight = int($char->weight_percent) . "%"; + $pos = " : $char->{pos_to}{x},$char->{pos_to}{y} " . $field->name if ($char->{pos_to} && $field); + my $aiSeq = join(",", @ai_seq); + # Translation Comment: Interface Title with character status + $title = TF("%s B%s (%s), J%s (%s) : w%s%s [%s] - %s", + $charName, $char->{lv}, $basePercent . '%', + $char->{lv_job}, $jobPercent . '%', + $weight, $pos, $aiSeq, $Settings::NAME); + + } elsif ($net->getState() == Network::NOT_CONNECTED) { + # Translation Comment: Interface Title + $title = TF("%sNot connected - %s", $charName, $Settings::NAME); + } else { + # Translation Comment: Interface Title + $title = TF("%sConnecting - %s", $charName, $Settings::NAME); + } + my %args = (return => $title); + Plugins::callHook('mainLoop::setTitle',\%args); + $interface->title($args{return}); + + Misc::checkValidity("mainLoop_part3"); + Benchmark::end("mainLoop_part3") if DEBUG; +} + +=pod +# Anonymous statistics reporting. This gives us insight about +# servers that our users bot on. +sub processStatisticsReporting { + our %statisticsReporting; + if (!$statisticsReporting{reported} && $config{master} && $config{username}) { + if (!$statisticsReporting{http}) { + use Utils qw(urlencode); + import Utils::Whirlpool qw(whirlpool_hex); + + # Note that ABSOLUTELY NO SENSITIVE INFORMATION about the + # user is sent. The username is filtered through an + # irreversible hashing algorithm before it is sent to the + # server. It is impossible to deduce the user's username + # from the data sent to the server. + # + # If you're still not convinced about the security of this, + # please read the following web pages for more details and explanation: + # http://www.openkore.com/statistics.php + # -and- + # http://forums.openkore.com/viewtopic.php?t=28044 + my $url = "http://www.openkore.com/statistics.php"; + my $post = "server=" . urlencode($config{master}); + $post .= "&product=" . urlencode($Settings::NAME); + $post .= "&version=" . urlencode($Settings::VERSION); + $post .= "&uid=" . urlencode(whirlpool_hex($config{master} . $config{username} . $userSeed)); + $statisticsReporting{http} = new StdHttpReader($url, $post); + debug "Posting anonymous usage statistics to $url\n", "statisticsReporting"; + } + + my $http = $statisticsReporting{http}; + if ($http->getStatus() == HttpReader::DONE) { + $statisticsReporting{reported} = 1; + delete $statisticsReporting{http}; + debug "Statistics posting completed.\n", "statisticsReporting"; + + } elsif ($http->getStatus() == HttpReader::ERROR) { + $statisticsReporting{reported} = 1; + delete $statisticsReporting{http}; + debug "Statistics posting failed: " . $http->getError() . "\n", "statisticsReporting"; + } + + } elsif (!$statisticsReporting{infoPosted} && $masterServer && $masterServer->{ip} + && $config{master} && $net && $net->getState() == Network::IN_GAME && $monstarttime) { + if (!$statisticsReporting{http}) { + my $url = "http://www.openkore.com/server-info.php"; + my $serverData = ""; + foreach my $key (sort keys %{$masterServer}) { + $serverData .= "$key $masterServer->{$key}\n"; + } + my $post = "server=" . urlencode($config{master}) . "&data=" . urlencode($serverData); + $statisticsReporting{http} = new StdHttpReader($url, $post); + debug "Posting server info to $url\n", "statisticsReporting"; + } + + my $http = $statisticsReporting{http}; + if ($http->getStatus() == HttpReader::DONE) { + $statisticsReporting{infoPosted} = 1; + delete $statisticsReporting{http}; + debug "Server info posting completed.\n", "statisticsReporting"; + + } elsif ($http->getStatus() == HttpReader::ERROR) { + $statisticsReporting{infoPosted} = 1; + delete $statisticsReporting{http}; + debug "Server info posting failed: " . $http->getError() . "\n", "statisticsReporting"; + } + } +} +=cut + +sub parseInput { + my $input = shift; + my $printType; + my ($hook, $msg); + $printType = shift if ($net && $net->clientAlive); + + debug("Input: $input\n", "parseInput", 2); + + if ($printType) { + my $hookOutput = sub { + my ($type, $domain, $level, $globalVerbosity, $message, $user_data) = @_; + $msg .= $message if ($type ne 'debug' && $level <= $globalVerbosity); + }; + $hook = Log::addHook($hookOutput); + $interface->writeOutput("console", "$input\n"); + } + $XKore_dontRedirect = 1; + + Commands::run($input); + + if ($printType) { + Log::delHook($hook); + if (defined $msg && $net->getState() == Network::IN_GAME && $config{XKore_silent}) { + $msg =~ s/\n*$//s; + $msg =~ s/\n/\\n/g; + sendMessage($messageSender, "k", $msg); + } + } + $XKore_dontRedirect = 0; +} + +return 1; diff --git a/openkore_llm_knowledge/knowledge/architecture_overview.md b/openkore_llm_knowledge/knowledge/architecture_overview.md new file mode 100644 index 0000000000..4bdb2ce37b --- /dev/null +++ b/openkore_llm_knowledge/knowledge/architecture_overview.md @@ -0,0 +1,27 @@ +# OpenKore Core Architecture (Curated Bundle) + +This knowledge bundle focuses on **core runtime architecture** from `src/` only. + +## Runtime flow +1. `openkore.pl` bootstraps module paths and loads core modules. +2. `src/functions.pl` drives the startup state machine: + - load plugins + - load control/table data + - initialize networking + - finalize initialization + - enter steady `mainLoop` +3. Core behavior then cycles through AI, task scheduling, command handling, and packet IO. + +## Included core layers +- **Boot + orchestration**: `src/functions.pl`, `src/Modules.pm`, `src/Globals.pm` +- **Configuration/parsing**: `src/Settings.pm`, `src/FileParsers.pm` +- **AI/task execution**: `src/AI.pm`, `src/AI/CoreLogic.pm`, `src/AI/Attack.pm`, `src/Task*.pm` +- **Actor model**: `src/Actor.pm`, `src/ActorList.pm` +- **Networking core**: `src/Network.pm`, `src/Network/{PacketParser,Receive,Send}.pm` +- **Command/log/debug glue**: `src/Commands.pm`, `src/Misc.pm`, `src/Log.pm`, `src/ErrorHandler.pm`, `src/Interface.pm` +- **Plugin framework dependency** (core extensibility context): `src/Plugins.pm` + +## Curation constraints applied +- Source scope restricted to `src/` content for copied code files. +- No binaries, archives, build artifacts, logs, or temp assets were included. +- Bundle is intentionally small and architecture-first for public GPT ingestion. diff --git a/openkore_llm_knowledge/knowledge/code_index_core.md b/openkore_llm_knowledge/knowledge/code_index_core.md new file mode 100644 index 0000000000..19c50291ff --- /dev/null +++ b/openkore_llm_knowledge/knowledge/code_index_core.md @@ -0,0 +1,41 @@ +# Code Index (Core Bundle) + +## Boot and lifecycle +- `core/src/functions.pl` — startup state machine and main loop phases. +- `core/src/Modules.pm` — module registration/reload support. +- `core/src/Globals.pm` — shared runtime/global state. + +## Configuration and parsing +- `core/src/Settings.pm` — control/table resolution and file registration. +- `core/src/FileParsers.pm` — parsing routines for config/table data. + +## AI and tasks +- `core/src/AI.pm` +- `core/src/AI/CoreLogic.pm` +- `core/src/AI/Attack.pm` +- `core/src/Task.pm` +- `core/src/TaskManager.pm` +- `core/src/Task/Route.pm` +- `core/src/Task/Move.pm` +- `core/src/Task/TalkNPC.pm` + +## Game entities +- `core/src/Actor.pm` +- `core/src/ActorList.pm` + +## Networking core +- `core/src/Network.pm` +- `core/src/Network/PacketParser.pm` +- `core/src/Network/Receive.pm` +- `core/src/Network/Send.pm` + +## Runtime support and diagnostics +- `core/src/Commands.pm` +- `core/src/Misc.pm` +- `core/src/Log.pm` +- `core/src/ErrorHandler.pm` +- `core/src/Interface.pm` +- `core/src/Plugins.pm` + +## Reference docs from `src/` +- `core/src/doc/modules.txt` From ce1d42b9abbee5e67feb36e7198f2ae7dd259eca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Bosc=C3=A1?= <juan_bosca@live.com> Date: Tue, 10 Mar 2026 18:13:18 -0300 Subject: [PATCH 04/18] Refine src-only core knowledge bundle and subsystem docs --- openkore_llm_knowledge/core/{src => }/AI.pm | 0 .../core/{src => }/AI/CoreLogic.pm | 0 .../core/{src => }/Actor.pm | 0 .../core/{src => }/ActorList.pm | 0 .../core/{src => }/Commands.pm | 0 .../core/{src => }/FileParsers.pm | 0 .../core/{src => }/Globals.pm | 0 .../core/{src => }/Modules.pm | 0 .../core/{src => }/Network.pm | 0 .../core/{src => }/Network/PacketParser.pm | 0 .../core/{src => }/Network/Receive.pm | 0 .../core/{src => }/Settings.pm | 0 openkore_llm_knowledge/core/{src => }/Task.pm | 0 .../core/{src => }/Task/Move.pm | 0 .../core/{src => }/Task/Route.pm | 0 .../core/{src => }/Task/TalkNPC.pm | 0 .../core/{src => }/TaskManager.pm | 0 .../core/{src => }/functions.pl | 0 openkore_llm_knowledge/core/src/AI/Attack.pm | 860 --- .../core/src/ErrorHandler.pm | 127 - openkore_llm_knowledge/core/src/Interface.pm | 250 - openkore_llm_knowledge/core/src/Log.pm | 458 -- openkore_llm_knowledge/core/src/Misc.pm | 5764 ----------------- .../core/src/Network/Send.pm | 3589 ---------- openkore_llm_knowledge/core/src/Plugins.pm | 554 -- .../core/src/doc/modules.txt | 66 - .../knowledge/architecture_overview.md | 36 +- .../knowledge/code_index_core.md | 41 - .../knowledge/core_module_index.md | 20 + .../knowledge/core_subsystems.md | 27 + .../knowledge/execution_flows.md | 26 + 31 files changed, 85 insertions(+), 11733 deletions(-) rename openkore_llm_knowledge/core/{src => }/AI.pm (100%) rename openkore_llm_knowledge/core/{src => }/AI/CoreLogic.pm (100%) rename openkore_llm_knowledge/core/{src => }/Actor.pm (100%) rename openkore_llm_knowledge/core/{src => }/ActorList.pm (100%) rename openkore_llm_knowledge/core/{src => }/Commands.pm (100%) rename openkore_llm_knowledge/core/{src => }/FileParsers.pm (100%) rename openkore_llm_knowledge/core/{src => }/Globals.pm (100%) rename openkore_llm_knowledge/core/{src => }/Modules.pm (100%) rename openkore_llm_knowledge/core/{src => }/Network.pm (100%) rename openkore_llm_knowledge/core/{src => }/Network/PacketParser.pm (100%) rename openkore_llm_knowledge/core/{src => }/Network/Receive.pm (100%) rename openkore_llm_knowledge/core/{src => }/Settings.pm (100%) rename openkore_llm_knowledge/core/{src => }/Task.pm (100%) rename openkore_llm_knowledge/core/{src => }/Task/Move.pm (100%) rename openkore_llm_knowledge/core/{src => }/Task/Route.pm (100%) rename openkore_llm_knowledge/core/{src => }/Task/TalkNPC.pm (100%) rename openkore_llm_knowledge/core/{src => }/TaskManager.pm (100%) rename openkore_llm_knowledge/core/{src => }/functions.pl (100%) delete mode 100644 openkore_llm_knowledge/core/src/AI/Attack.pm delete mode 100644 openkore_llm_knowledge/core/src/ErrorHandler.pm delete mode 100644 openkore_llm_knowledge/core/src/Interface.pm delete mode 100644 openkore_llm_knowledge/core/src/Log.pm delete mode 100644 openkore_llm_knowledge/core/src/Misc.pm delete mode 100644 openkore_llm_knowledge/core/src/Network/Send.pm delete mode 100644 openkore_llm_knowledge/core/src/Plugins.pm delete mode 100644 openkore_llm_knowledge/core/src/doc/modules.txt delete mode 100644 openkore_llm_knowledge/knowledge/code_index_core.md create mode 100644 openkore_llm_knowledge/knowledge/core_module_index.md create mode 100644 openkore_llm_knowledge/knowledge/core_subsystems.md create mode 100644 openkore_llm_knowledge/knowledge/execution_flows.md diff --git a/openkore_llm_knowledge/core/src/AI.pm b/openkore_llm_knowledge/core/AI.pm similarity index 100% rename from openkore_llm_knowledge/core/src/AI.pm rename to openkore_llm_knowledge/core/AI.pm diff --git a/openkore_llm_knowledge/core/src/AI/CoreLogic.pm b/openkore_llm_knowledge/core/AI/CoreLogic.pm similarity index 100% rename from openkore_llm_knowledge/core/src/AI/CoreLogic.pm rename to openkore_llm_knowledge/core/AI/CoreLogic.pm diff --git a/openkore_llm_knowledge/core/src/Actor.pm b/openkore_llm_knowledge/core/Actor.pm similarity index 100% rename from openkore_llm_knowledge/core/src/Actor.pm rename to openkore_llm_knowledge/core/Actor.pm diff --git a/openkore_llm_knowledge/core/src/ActorList.pm b/openkore_llm_knowledge/core/ActorList.pm similarity index 100% rename from openkore_llm_knowledge/core/src/ActorList.pm rename to openkore_llm_knowledge/core/ActorList.pm diff --git a/openkore_llm_knowledge/core/src/Commands.pm b/openkore_llm_knowledge/core/Commands.pm similarity index 100% rename from openkore_llm_knowledge/core/src/Commands.pm rename to openkore_llm_knowledge/core/Commands.pm diff --git a/openkore_llm_knowledge/core/src/FileParsers.pm b/openkore_llm_knowledge/core/FileParsers.pm similarity index 100% rename from openkore_llm_knowledge/core/src/FileParsers.pm rename to openkore_llm_knowledge/core/FileParsers.pm diff --git a/openkore_llm_knowledge/core/src/Globals.pm b/openkore_llm_knowledge/core/Globals.pm similarity index 100% rename from openkore_llm_knowledge/core/src/Globals.pm rename to openkore_llm_knowledge/core/Globals.pm diff --git a/openkore_llm_knowledge/core/src/Modules.pm b/openkore_llm_knowledge/core/Modules.pm similarity index 100% rename from openkore_llm_knowledge/core/src/Modules.pm rename to openkore_llm_knowledge/core/Modules.pm diff --git a/openkore_llm_knowledge/core/src/Network.pm b/openkore_llm_knowledge/core/Network.pm similarity index 100% rename from openkore_llm_knowledge/core/src/Network.pm rename to openkore_llm_knowledge/core/Network.pm diff --git a/openkore_llm_knowledge/core/src/Network/PacketParser.pm b/openkore_llm_knowledge/core/Network/PacketParser.pm similarity index 100% rename from openkore_llm_knowledge/core/src/Network/PacketParser.pm rename to openkore_llm_knowledge/core/Network/PacketParser.pm diff --git a/openkore_llm_knowledge/core/src/Network/Receive.pm b/openkore_llm_knowledge/core/Network/Receive.pm similarity index 100% rename from openkore_llm_knowledge/core/src/Network/Receive.pm rename to openkore_llm_knowledge/core/Network/Receive.pm diff --git a/openkore_llm_knowledge/core/src/Settings.pm b/openkore_llm_knowledge/core/Settings.pm similarity index 100% rename from openkore_llm_knowledge/core/src/Settings.pm rename to openkore_llm_knowledge/core/Settings.pm diff --git a/openkore_llm_knowledge/core/src/Task.pm b/openkore_llm_knowledge/core/Task.pm similarity index 100% rename from openkore_llm_knowledge/core/src/Task.pm rename to openkore_llm_knowledge/core/Task.pm diff --git a/openkore_llm_knowledge/core/src/Task/Move.pm b/openkore_llm_knowledge/core/Task/Move.pm similarity index 100% rename from openkore_llm_knowledge/core/src/Task/Move.pm rename to openkore_llm_knowledge/core/Task/Move.pm diff --git a/openkore_llm_knowledge/core/src/Task/Route.pm b/openkore_llm_knowledge/core/Task/Route.pm similarity index 100% rename from openkore_llm_knowledge/core/src/Task/Route.pm rename to openkore_llm_knowledge/core/Task/Route.pm diff --git a/openkore_llm_knowledge/core/src/Task/TalkNPC.pm b/openkore_llm_knowledge/core/Task/TalkNPC.pm similarity index 100% rename from openkore_llm_knowledge/core/src/Task/TalkNPC.pm rename to openkore_llm_knowledge/core/Task/TalkNPC.pm diff --git a/openkore_llm_knowledge/core/src/TaskManager.pm b/openkore_llm_knowledge/core/TaskManager.pm similarity index 100% rename from openkore_llm_knowledge/core/src/TaskManager.pm rename to openkore_llm_knowledge/core/TaskManager.pm diff --git a/openkore_llm_knowledge/core/src/functions.pl b/openkore_llm_knowledge/core/functions.pl similarity index 100% rename from openkore_llm_knowledge/core/src/functions.pl rename to openkore_llm_knowledge/core/functions.pl diff --git a/openkore_llm_knowledge/core/src/AI/Attack.pm b/openkore_llm_knowledge/core/src/AI/Attack.pm deleted file mode 100644 index c580c8ac6e..0000000000 --- a/openkore_llm_knowledge/core/src/AI/Attack.pm +++ /dev/null @@ -1,860 +0,0 @@ -######################################################################### -# OpenKore - Attack AI -# Copyright (c) 2006 OpenKore Team -# -# This software is open source, licensed under the GNU General Public -# License, version 2. -# Basically, this means that you're allowed to modify and distribute -# this software. However, if you distribute modified versions, you MUST -# also distribute the source code. -# See http://www.gnu.org/licenses/gpl.html for the full license. -# -# $Revision: 4286 $ -# $Id: Commands.pm 4286 2006-04-17 14:02:27Z illusion_kore $ -# -######################################################################### -# -# This module contains the attack AI's code. -package AI::Attack; - -use strict; -use Carp::Assert; -use Time::HiRes qw(time); - -use Globals; -use AI; -use Actor; -use Field; -use Log qw(message debug warning); -use Translation qw(T TF); -use Misc; -use Network::Send (); -use Skill; -use Utils; -use Utils::Benchmark; -use Utils::PathFinding; -use Data::Dumper; -$Data::Dumper::Sortkeys = 1; - -use constant { - MOVING_TO_ATTACK => 1, - ATTACKING => 2, -}; - -sub process { - Benchmark::begin("ai_attack") if DEBUG; - my $args = AI::args; - my $action = AI::action; - - if (shouldAttack($action, $args)) { - my $ID; - my $ataqArgs; - my $stage; # 1 - moving to attack | 2 - attacking - if (AI::action eq "attack") { - $ID = $args->{ID}; - $ataqArgs = AI::args(0); - $stage = ATTACKING; - } else { - if (AI::action(1) eq "attack") { - $ataqArgs = AI::args(1); - - } elsif (AI::action(2) eq "attack") { - $ataqArgs = AI::args(2); - } - $ID = $args->{attackID}; - $stage = MOVING_TO_ATTACK; - } - - if (targetGone($ataqArgs, $ID)) { - finishAttacking($ataqArgs, $ID); - return; - } elsif (shouldGiveUp($ataqArgs, $ID)) { - giveUp($ataqArgs, $ID, 0); - return; - } - - my $target = Actor::get($ID); - unless ($target && $target->{type} ne 'Unknown') { - finishAttacking($ataqArgs, $ID); - return; - } - - my $party = $config{'attackAuto_party'} ? 1 : 0; - my $target_is_aggressive = is_aggressive($target, undef, 0, $party); - - if ($config{attackChangeTarget}) { - my $routeIndex = AI::findAction("route"); - $routeIndex = AI::findAction("mapRoute") if (!defined $routeIndex); - my $attackOnRoute; - if (defined $routeIndex) { - $attackOnRoute = AI::args($routeIndex)->{attackOnRoute}; - } else { - $attackOnRoute = 2; - } - - my @aggressives = ai_getAggressives($attackOnRoute, $party); - - if (!$target_is_aggressive && @aggressives) { - my $attackTarget = getBestTarget(\@aggressives, $config{attackCheckLOS}, $config{attackCanSnipe}); - if ($attackTarget && $attackTarget ne $target->{ID}) { - $char->sendAttackStop; - AI::dequeue while ( AI::inQueue("attack") ); - ai_setSuspend(0); - my $new_target = Actor::get($attackTarget); - warning TF("Your target is not aggressive: %s, changing target to aggressive: %s.\n", $target, $new_target), 'ai_attack'; - $target->{droppedForAggressive} = 1; - $char->attack($attackTarget); - AI::Attack::process(); - return; - } - } - } - - my $cleanMonster = checkMonsterCleanness($ID); - if (!$cleanMonster) { - message TF("Dropping target %s - will not kill steal others\n", $target), 'ai_attack'; - $char->sendAttackStop; - $target->{ignore} = 1; - AI::dequeue while (AI::inQueue("attack")); - if ($config{teleportAuto_dropTargetKS}) { - message T("Teleport due to dropping attack target\n"), "teleport"; - ai_useTeleport(1); - } - return; - } - - my $control = mon_control($target->{name},$target->{nameID}); - if ($control->{attack_auto} == 3 && ($target->{dmgToYou} || $target->{missedYou} || $target->{dmgFromYou})) { - message TF("Dropping target - %s (%s) has been provoked\n", $target->{name}, $target->{binID}); - $char->sendAttackStop; - $target->{ignore} = 1; - AI::dequeue while (AI::inQueue("attack")); - return; - } - - my %plugin_args; - $plugin_args{target} = $target; - $plugin_args{control} = $control; - $plugin_args{stage} = $stage; - $plugin_args{party} = $party; - $plugin_args{target_is_aggressive} = $target_is_aggressive; - $plugin_args{return} = 0; - Plugins::callHook('AI::Attack::process' => \%plugin_args); - return if ($plugin_args{return}); - - if ($stage == MOVING_TO_ATTACK) { - # Check for hidden monsters - if (($target->{statuses}->{EFFECTSTATE_BURROW} || $target->{statuses}->{EFFECTSTATE_HIDING}) && $config{avoidHiddenMonsters}) { - message TF("Dropping target %s - will not attack hidden monsters\n", $target), 'ai_attack'; - $char->sendAttackStop; - $target->{ignore} = 1; - - AI::dequeue while (AI::inQueue("attack")); - if ($config{teleportAuto_dropTargetHidden}) { - message T("Teleport due to dropping hidden target\n"); - ai_useTeleport(1); - } - return; - } - - # We're on route to the monster; check whether the monster has moved - if ($args->{attackID} && timeOut($timeout{ai_attack_route_adjust})) { - if ( - $target->{type} ne 'Unknown' && - $ataqArgs->{monsterLastMoveTime} && - $ataqArgs->{monsterLastMoveTime} != $target->{time_move} - ) { - if ( - ($args->{monsterLastMovePosTo}{x} == $target->{pos_to}{x} && $args->{monsterLastMovePosTo}{y} == $target->{pos_to}{y}) - ) { - $args->{monsterLastMoveTime} = $target->{time_move}; - $args->{monsterLastMovePosTo}{x} = $target->{pos_to}{x}; - $args->{monsterLastMovePosTo}{y} = $target->{pos_to}{y}; - } else { - # Monster has moved; stop moving and let the attack AI readjust route - debug "Target $target has moved since we started routing to it - Adjusting route\n", "ai_attack"; - AI::dequeue while (AI::is("move", "route")); - - $ataqArgs->{ai_attack_giveup}{time} = time; - $ataqArgs->{sentApproach} = 0; - undef $args->{unstuck}{time}; - undef $args->{avoiding}; - undef $args->{move_start}; - } - } else { - $timeout{ai_attack_route_adjust}{time} = time; - } - } - } - - if ($stage == ATTACKING) { - if (AI::args->{suspended}) { - $args->{ai_attack_giveup}{time} += time - $args->{suspended}; - delete $args->{suspended}; - - # We've just finished moving to the monster. - # Don't count the time we spent on moving - } elsif ($args->{move_start}) { - $args->{ai_attack_giveup}{time} += time - $args->{move_start}; - undef $args->{unstuck}{time}; - undef $args->{move_start}; - - } elsif ($args->{avoiding}) { - $args->{ai_attack_giveup}{time} = time; - undef $args->{avoiding}; - debug "Finished avoiding movement from target $target, updating ai_attack_giveup\n", "ai_attack"; - } - - if (timeOut($timeout{ai_attack_main})) { - if ($char->{sitting}) { - ai_setSuspend(0); - stand(); - } else { - main(); - } - $timeout{ai_attack_main}{time} = time; - } - - } - } - - Benchmark::end("ai_attack") if DEBUG; -} - -sub shouldAttack { - my ($action, $args) = @_; - return ( - ($action eq "attack" && $args->{ID}) || - ($action eq "route" && AI::action(1) eq "attack" && $args->{attackID}) || - ($action eq "move" && AI::action(2) eq "attack" && $args->{attackID}) - ); -} - -sub shouldGiveUp { - my ($args, $ID) = @_; - return !$config{attackNoGiveup} && (timeOut($args->{ai_attack_giveup}) || $args->{unstuck}{count} > 5); -} - -sub giveUp { - my ($args, $ID, $LOS) = @_; - my $target = Actor::get($ID); - if ($monsters{$ID}) { - if ($LOS) { - $target->{attack_failedLOS} = time; - } else { - $target->{attack_failed} = time; - } - } - $target->{dmgFromYou} = 0; # Hack | TODO: Fix me - AI::dequeue while (AI::inQueue("attack")); - message T("Can't reach or damage target, dropping target\n"), "ai_attack"; - if ($config{'teleportAuto_dropTarget'}) { - message T("Teleport due to dropping attack target\n"); - ai_useTeleport(1); - } -} - -sub targetGone { - my ($args, $ID) = @_; - my $target = Actor::get($ID, 1); - unless ($target) { - return 1; - } - if (exists $target->{dead} && $target->{dead} == 1) { - return 1; - } - return 0; -} - -sub finishAttacking { - my ($args, $ID) = @_; - $timeout{'ai_attack'}{'time'} -= $timeout{'ai_attack'}{'timeout'}; - AI::dequeue while (AI::inQueue("attack")); - message TF( "Finished attacking\n"), "ai_attack"; - if ($monsters_old{$ID} && $monsters_old{$ID}{dead}) { - message TF("Target %s died\n", $monsters_old{$ID}), "ai_attack"; - Plugins::callHook('target_died', {monster => $monsters_old{$ID}}); - monKilled(); - - # Pickup loot when monster's dead - if (AI::state == AI::AUTO && $config{'itemsTakeAuto'} && $monsters_old{$ID}{dmgFromYou} > 0 && !$monsters_old{$ID}{ignore}) { - AI::clear("items_take"); - ai_items_take($monsters_old{$ID}{pos}{x}, $monsters_old{$ID}{pos}{y}, - $monsters_old{$ID}{pos_to}{x}, $monsters_old{$ID}{pos_to}{y}); - } else { - # Cheap way to suspend all movement to make it look real - ai_clientSuspend(0, $timeout{'ai_attack_waitAfterKill'}{'timeout'}); - } - - ## kokal start - ## mosters counting - my $i = 0; - my $found = 0; - while ($monsters_Killed[$i]) { - if ($monsters_Killed[$i]{'nameID'} eq $monsters_old{$ID}{'nameID'}) { - $monsters_Killed[$i]{'count'}++; - monsterLog($monsters_Killed[$i]{'name'}); - $found = 1; - last; - } - $i++; - } - if (!$found) { - $monsters_Killed[$i]{'nameID'} = $monsters_old{$ID}{'nameID'}; - $monsters_Killed[$i]{'name'} = $monsters_old{$ID}{'name'}; - $monsters_Killed[$i]{'count'} = 1; - monsterLog($monsters_Killed[$i]{'name'}) - } - ## kokal end - - } elsif ($config{teleportAuto_lostTarget}) { - message T("Target lost, teleporting.\n"), "ai_attack"; - ai_useTeleport(1); - } else { - message T("Target lost\n"), "ai_attack"; - } - - $messageSender->sendStopSkillUse($char->{last_continuous_skill_used}) if $char->{last_skill_used_is_continuous}; - Plugins::callHook('attack_end', {ID => $ID}) -} - -sub find_kite_position { - my ($args, $inAdvance, $target, $realMyPos, $realMonsterPos, $noAttackMethodFallback_runFromTarget) = @_; - - my $maxDistance; - if (!$noAttackMethodFallback_runFromTarget && defined $args->{attackMethod}{type} && defined $args->{attackMethod}{maxDistance}) { - $maxDistance = $args->{attackMethod}{maxDistance}; - } elsif ($noAttackMethodFallback_runFromTarget) { - $maxDistance = $config{'runFromTarget_noAttackMethodFallback_attackMaxDist'}; - } else { - # Should never happen. - return 0; - } - - # We try to find a position to kite from at least runFromTarget_minStep away from the target but at maximun {attackMethod}{maxDistance} away from it - my $pos = meetingPosition($char, 1, $target, $maxDistance, ($noAttackMethodFallback_runFromTarget ? 2 : 1)); - if ($pos) { - if ($inAdvance) { - debug TF("[runFromTarget_inAdvance] %s kiting in advance (%d %d) to (%d %d), mob at (%d %d).\n", $char, $realMyPos->{x}, $realMyPos->{y}, $pos->{x}, $pos->{y}, $realMonsterPos->{x}, $realMonsterPos->{y}), 'ai_attack'; - } elsif ($noAttackMethodFallback_runFromTarget) { - debug TF("[runFromTarget_noAttackMethodFallback] %s kiting in advance (%d %d) to (%d %d), mob at (%d %d).\n", $char, $realMyPos->{x}, $realMyPos->{y}, $pos->{x}, $pos->{y}, $realMonsterPos->{x}, $realMonsterPos->{y}), 'ai_attack'; - } else { - debug TF("[runFromTarget] (attackmaxDistance %s) %s kiteing from (%d %d) to (%d %d), mob at (%d %d).\n", $maxDistance, $char, $realMyPos->{x}, $realMyPos->{y}, $pos->{x}, $pos->{y}, $realMonsterPos->{x}, $realMonsterPos->{y}), 'ai_attack'; - } - $args->{avoiding} = 1; - $char->route( - undef, - @{$pos}{qw(x y)}, - noMapRoute => 1, - avoidWalls => 0, - randomFactor => 0, - useManhattan => 1, - runFromTarget => 1 - ); - return 1; - - } else { - if ($inAdvance) { - debug TF("[runFromTarget_inAdvance] %s no acceptable place to kite in advance from (%d %d), mob at (%d %d).\n", $char, $realMyPos->{x}, $realMyPos->{y}, $realMonsterPos->{x}, $realMonsterPos->{y}), 'ai_attack'; - } elsif ($noAttackMethodFallback_runFromTarget) { - debug TF("[runFromTarget_noAttackMethodFallback] %s no acceptable place to kite from (%d %d), mob at (%d %d).\n", $char, $realMyPos->{x}, $realMyPos->{y}, $realMonsterPos->{x}, $realMonsterPos->{y}), 'ai_attack'; - } else { - debug TF("[runFromTarget] %s no acceptable place to kite from (%d %d), mob at (%d %d).\n", $char, $realMyPos->{x}, $realMyPos->{y}, $realMonsterPos->{x}, $realMonsterPos->{y}), 'ai_attack'; - } - return 0; - } -} - -sub main { - my $args = AI::args; - - Benchmark::begin("ai_attack (part 1)") if DEBUG; - Benchmark::begin("ai_attack (part 1.1)") if DEBUG; - # The attack sequence hasn't timed out and the monster is on screen - - # Update information about the monster and the current situation - my $args = AI::args; - my $ID = $args->{ID}; - - if (!defined $ID) { - warning "[attack main] Bug where ID is undefined found.\n"; - warning "Args Dump: " . Dumper($args); - warning "ML Dump: " . Dumper(\@$monstersList); - warning "ai_seq Dump: " . Dumper(\@ai_seq); - warning "ai_seq_args Dump: " . Dumper(\@ai_seq_args); - Plugins::callHook('undefined_object_id'); - } - - my $target = Actor::get($ID); - - if (!exists $args->{temporary_extra_range} || !defined $args->{temporary_extra_range}) { - $args->{temporary_extra_range} = 0; - } - - if (exists $char->{movetoattack_pos}) { - if (!exists $char->{movetoattack_targetID} || $char->{movetoattack_targetID} ne $ID || $char->{time_move} > $char->{movetoattack_time}) { - delete $char->{movetoattack_pos}; - } else { - my $time_since_char_moved = time - $char->{time_move}; - if ($time_since_char_moved > $char->{time_move_calc}) { - debug "[Attack] [Char] Fixing failed to attack target, setting char position to: $char->{movetoattack_pos}{x} $char->{movetoattack_pos}{y}\n", "ai_attack"; - $char->{pos}{x} = $char->{movetoattack_pos}{x}; - $char->{pos}{y} = $char->{movetoattack_pos}{y}; - $char->{pos_to}{x} = $char->{movetoattack_pos}{x}; - $char->{pos_to}{y} = $char->{movetoattack_pos}{y}; - $char->{time_move} = time; - $char->{time_move_calc} = 0; - delete $char->{movetoattack_pos}; - } - } - } - - if (exists $target->{movetoattack_pos}) { - if ($target->{time_move} > $target->{movetoattack_time}) { - delete $target->{movetoattack_pos}; - } else { - my $target_solution = get_solution($field, $target->{pos}, $target->{pos_to}); - my $target_time_to_move = calcTimeFromSolution($target_solution, $target->{walk_speed}); - my $time_since_target_moved = time - $target->{time_move}; - if ($time_since_target_moved > $target_time_to_move) { - debug "[Attack] [Target] Fixing failed to attack target, setting target $target position to: $target->{movetoattack_pos}{x} $target->{movetoattack_pos}{y}\n", "ai_attack"; - $target->{pos}{x} = $target->{movetoattack_pos}{x}; - $target->{pos}{y} = $target->{movetoattack_pos}{y}; - $target->{pos_to}{x} = $target->{movetoattack_pos}{x}; - $target->{pos_to}{y} = $target->{movetoattack_pos}{y}; - $target->{time_move} = time; - $target->{time_move_calc} = 0; - delete $target->{movetoattack_pos}; - } - } - } - - my $myPos = $char->{pos_to}; - my $monsterPos = $target->{pos_to}; - my $monsterDist = blockDistance($myPos, $monsterPos); - - my $realMyPos = calcPosFromPathfinding($field, $char); - my $realMonsterPos = calcPosFromPathfinding($field, $target); - - my $realMonsterDist = blockDistance($realMyPos, $realMonsterPos); - my $clientDist = getClientDist($realMyPos, $realMonsterPos); - - - # If the damage numbers have changed, update the giveup time so we don't timeout - if ($args->{dmgToYou_last} != $target->{dmgToYou} - || $args->{missedYou_last} != $target->{missedYou} - || $args->{dmgFromYou_last} != $target->{dmgFromYou} - || $args->{lastSkillTime} != $char->{last_skill_time}) { - $args->{ai_attack_giveup}{time} = time; - debug "Update attack giveup time\n", "ai_attack", 2; - } - - if (!exists $args->{firstLoop}) { - $args->{firstLoop} = 1; - } else { - $args->{firstLoop} = 0; - } - - my $hitYou = ($args->{dmgToYou_last} != $target->{dmgToYou} || $args->{missedYou_last} != $target->{missedYou}); - my $youHitTarget = ($args->{dmgFromYou_last} != $target->{dmgFromYou} || $args->{missedFromYou_last} != $target->{missedFromYou}); - - $args->{dmgToYou_last} = $target->{dmgToYou}; - $args->{missedYou_last} = $target->{missedYou}; - $args->{dmgFromYou_last} = $target->{dmgFromYou}; - $args->{missedFromYou_last} = $target->{missedFromYou}; - - $args->{lastSkillTime} = $char->{last_skill_time}; - - Benchmark::end("ai_attack (part 1.1)") if DEBUG; - Benchmark::begin("ai_attack (part 1.2)") if DEBUG; - - # Determine what combo skill to use - delete $args->{attackMethod}; - - my $i = 0; - while (exists $config{"attackComboSlot_$i"}) { - next unless (defined $config{"attackComboSlot_$i"}); - - next unless (checkSelfCondition("attackComboSlot_$i")); - next unless ($config{"attackComboSlot_${i}_afterSkill"}); - next unless (Skill->new(auto => $config{"attackComboSlot_${i}_afterSkill"})->getIDN == $char->{last_skill_used}); - next unless (( !$config{"attackComboSlot_${i}_maxUses"} || $args->{attackComboSlot_uses}{$i} < $config{"attackComboSlot_${i}_maxUses"} )); - next unless (( !$config{"attackComboSlot_${i}_autoCombo"} || ($char->{combo_packet} && $config{"attackComboSlot_${i}_autoCombo"}) )); - next unless (( !defined($args->{ID}) || $args->{ID} eq $char->{last_skill_target} || !$config{"attackComboSlot_${i}_isSelfSkill"})); - next unless ((!$config{"attackComboSlot_${i}_monsters"} || existsInList($config{"attackComboSlot_${i}_monsters"}, $target->{name}) || existsInList($config{"attackComboSlot_${i}_monsters"}, $target->{nameID}))); - next unless ((!$config{"attackComboSlot_${i}_notMonsters"} || !(existsInList($config{"attackComboSlot_${i}_notMonsters"}, $target->{name}) || existsInList($config{"attackComboSlot_${i}_notMonsters"}, $target->{nameID})))); - next unless (checkMonsterCondition("attackComboSlot_${i}_target", $target)); - - $args->{attackComboSlot_uses}{$i}++; - delete $char->{last_skill_used}; - if ($config{"attackComboSlot_${i}_autoCombo"}) { - $char->{combo_packet} = 1500 if ($char->{combo_packet} > 1500); - # eAthena seems to have a bug where the combo_packet overflows and gives an - # abnormally high number. This causes kore to get stuck in a waitBeforeUse timeout. - $config{"attackComboSlot_${i}_waitBeforeUse"} = ($char->{combo_packet} / 1000); - } - delete $char->{combo_packet}; - $args->{attackMethod}{type} = "combo"; - $args->{attackMethod}{comboSlot} = $i; - $args->{attackMethod}{distance} = $config{"attackComboSlot_${i}_dist"}; - $args->{attackMethod}{maxDistance} = $config{"attackComboSlot_${i}_maxDist"} || $config{"attackComboSlot_${i}_dist"}; - last; - } continue { - $i++; - } - - # Determine what skill to use to attack - if (!$args->{attackMethod}{type}) { - if ($config{'attackUseWeapon'}) { - $args->{attackMethod}{type} = "weapon"; - $args->{attackMethod}{distance} = $config{'attackDistance'}; - $args->{attackMethod}{maxDistance} = $config{'attackMaxDistance'}; - } else { - undef $args->{attackMethod}{type}; - $args->{attackMethod}{distance} = 1; - $args->{attackMethod}{maxDistance} = 1; - } - - $i = 0; - while (exists $config{"attackSkillSlot_$i"}) { - next unless (defined $config{"attackSkillSlot_$i"}); - - my $skill = new Skill(auto => $config{"attackSkillSlot_$i"}); - next unless ($skill); - next unless ($skill->getOwnerType == Skill::OWNER_CHAR); - - my $handle = $skill->getHandle(); - - next unless (checkSelfCondition("attackSkillSlot_$i")); - next unless ((!$config{"attackSkillSlot_$i"."_maxUses"} || $target->{skillUses}{$handle} < $config{"attackSkillSlot_$i"."_maxUses"})); - next unless ((!$config{"attackSkillSlot_$i"."_maxAttempts"} || $args->{attackSkillSlot_attempts}{$i} < $config{"attackSkillSlot_$i"."_maxAttempts"})); - next unless ((!$config{"attackSkillSlot_$i"."_monsters"} || existsInList($config{"attackSkillSlot_$i"."_monsters"}, $target->{'name'}) || existsInList($config{"attackSkillSlot_$i"."_monsters"}, $target->{nameID}))); - next unless ((!$config{"attackSkillSlot_$i"."_notMonsters"} || !(existsInList($config{"attackSkillSlot_$i"."_notMonsters"}, $target->{'name'}) ||existsInList($config{"attackSkillSlot_$i"."_notMonsters"}, $target->{nameID})))); - next unless ((!$config{"attackSkillSlot_$i"."_previousDamage"} || inRange($target->{dmgTo}, $config{"attackSkillSlot_$i"."_previousDamage"}))); - next unless (checkMonsterCondition("attackSkillSlot_${i}_target", $target)); - - $args->{attackMethod}{type} = "skill"; - $args->{attackMethod}{skillSlot} = $i; - $args->{attackMethod}{distance} = $config{"attackSkillSlot_$i"."_dist"}; - $args->{attackMethod}{maxDistance} = $config{"attackSkillSlot_$i"."_maxDist"} || $config{"attackSkillSlot_$i"."_dist"}; - last; - } continue { - $i++; - } - } - - $args->{attackMethod}{maxDistance} ||= $config{attackMaxDistance}; - $args->{attackMethod}{distance} ||= $config{attackDistance}; - - if ($args->{attackMethod}{maxDistance} < $args->{attackMethod}{distance}) { - $args->{attackMethod}{maxDistance} = $args->{attackMethod}{distance}; - } - - Benchmark::end("ai_attack (part 1.2)") if DEBUG; - Benchmark::end("ai_attack (part 1)") if DEBUG; - - if (defined $args->{attackMethod}{type} && exists $args->{ai_attack_failed_give_up} && defined $args->{ai_attack_failed_give_up}{time}) { - debug "Deleting ai_attack_failed_give_up time.\n"; - delete $args->{ai_attack_failed_give_up}{time}; - - } - - $args->{attackMethod}{maxDistance} += $args->{temporary_extra_range}; - - # -2: undefined attackMethod - # -1: No LOS - # 0: out of range - # 1: sucess - my $canAttack; - if (defined $args->{attackMethod}{type} && defined $args->{attackMethod}{maxDistance}) { - $canAttack = canAttack($field, $realMyPos, $realMonsterPos, $config{attackCanSnipe}, $args->{attackMethod}{maxDistance}, $config{clientSight}); - } else { - $canAttack = -2; - } - - my $canAttack_fail_string = (($canAttack == -2) ? "No Method" : (($canAttack == -1) ? "No LOS" : (($canAttack == 0) ? "No Range" : "OK"))); - - # Here we check if the monster which we are waiting to get closer to us is in fact close enough - # If it is close enough delete the ai_attack_failed_waitForAgressive_give_up keys and loop attack logic - if ( - $config{"attackBeyondMaxDistance_waitForAgressive"} && - $target->{dmgFromYou} > 0 && - $canAttack == 1 && - exists $args->{ai_attack_failed_waitForAgressive_give_up} && - defined $args->{ai_attack_failed_waitForAgressive_give_up}{time} - ) { - debug "Deleting ai_attack_failed_waitForAgressive_give_up time.\n"; - delete $args->{ai_attack_failed_waitForAgressive_give_up}{time}; - } - - # Here we check if we have finished moving to the meeting position to attack our target, only checks this if attackWaitApproachFinish is set to 1 in config - # If so sets sentApproach to 0 - if ($args->{sentApproach}) { - if ($config{"attackWaitApproachFinish"}) { - if (!timeOut($char->{time_move}, $char->{time_move_calc})) { - debug TF("[attackWaitApproachFinish - Waiting] %s (%d %d), target %s (%d %d), distance %d, maxDistance %d, dmgFromYou %d.\n", $char, $realMyPos->{x}, $realMyPos->{y}, $target, $realMonsterPos->{x}, $realMonsterPos->{y}, $realMonsterDist, $args->{attackMethod}{maxDistance}, $target->{dmgFromYou}), 'ai_attack'; - return; - } else { - debug TF("[attackWaitApproachFinish - Ended Approaching] %s (%d %d), target %s (%d %d), distance %d, maxDistance %d, dmgFromYou %d.\n", $char, $realMyPos->{x}, $realMyPos->{y}, $target, $realMonsterPos->{x}, $realMonsterPos->{y}, $realMonsterDist, $args->{attackMethod}{maxDistance}, $target->{dmgFromYou}), 'ai_attack'; - $args->{sentApproach} = 0; - } - } else { - if ($canAttack == 2) { - debug TF("[Approaching - Can now attack] %s (%d %d), target %s (%d %d), distance %d, maxDistance %d, dmgFromYou %d.\n", $char, $realMyPos->{x}, $realMyPos->{y}, $target, $realMonsterPos->{x}, $realMonsterPos->{y}, $realMonsterDist, $args->{attackMethod}{maxDistance}, $target->{dmgFromYou}), 'ai_attack'; - $args->{sentApproach} = 0; - } elsif (timeOut($char->{time_move}, $char->{time_move_calc})) { - debug TF("[Approaching - Ended] Still no LOS/Range - %s (%d %d), target %s (%d %d), distance %d, maxDistance %d, dmgFromYou %d.\n", $char, $realMyPos->{x}, $realMyPos->{y}, $target, $realMonsterPos->{x}, $realMonsterPos->{y}, $realMonsterDist, $args->{attackMethod}{maxDistance}, $target->{dmgFromYou}), 'ai_attack'; - $args->{sentApproach} = 0; - } - } - } - - my $found_action = 0; - my $failed_runFromTarget = 0; - my $hitTarget_when_not_possible = 0; - - # Here, if runFromTarget is active, we check if the target mob is closer to us than the minimun distance specified in runFromTarget_dist - # If so try to kite it - if ( - !$found_action && - $config{"runFromTarget"} && - $realMonsterDist < $config{"runFromTarget_dist"} - ) { - my $try_runFromTarget = find_kite_position($args, 0, $target, $realMyPos, $realMonsterPos, 0); - if ($try_runFromTarget) { - $found_action = 1; - } else { - $failed_runFromTarget = 1; - } - } - - # Here, if runFromTarget is active, and we can't attack right now (eg. all skills in cooldown) we check if the target mob is closer to us than the minimun distance specified in runFromTarget_noAttackMethodFallback_minStep - # If so try to kite it using maxdistance of runFromTarget_noAttackMethodFallback_attackMaxDist - if ( - !$found_action && - $canAttack == -2 && - #$config{"runFromTarget"} && - $config{'runFromTarget_noAttackMethodFallback'} && - $realMonsterDist < $config{'runFromTarget_noAttackMethodFallback_minStep'} - ) { - my $try_runFromTarget = find_kite_position($args, 0, $target, $realMyPos, $realMonsterPos, 1); - if ($try_runFromTarget) { - $found_action = 1; - } - } - - if ( - !$found_action && - $canAttack == -2 - ) { - debug T("Can't determine a attackMethod (check attackUseWeapon and Skills blocks)\n"), "ai_attack"; - $args->{ai_attack_failed_give_up}{timeout} = 6 if !$args->{ai_attack_failed_give_up}{timeout}; - $args->{ai_attack_failed_give_up}{time} = time if !$args->{ai_attack_failed_give_up}{time}; - if (timeOut($args->{ai_attack_failed_give_up})) { - delete $args->{ai_attack_failed_give_up}{time}; - warning T("Unable to determine a attackMethod (check attackUseWeapon and Skills blocks), dropping target.\n"), "ai_attack"; - $found_action = 1; - giveUp($args, $ID, 0); - } - } - - if (!$args->{firstLoop} && $canAttack == 0 && $youHitTarget) { - debug TF("[%s] We were able to hit target even though it is out of range or LOS, accepting and continuing. (you %s (%d %d), target %s (%d %d) [(%d %d) -> (%d %d)], distance %d, maxDistance %d, dmgFromYou %d)\n", $canAttack_fail_string, $char, $realMyPos->{x}, $realMyPos->{y}, $target, $realMonsterPos->{x}, $realMonsterPos->{y}, $target->{pos}{x}, $target->{pos}{y}, $target->{pos_to}{x}, $target->{pos_to}{y}, $realMonsterDist, $args->{attackMethod}{maxDistance}, $target->{dmgFromYou}), 'ai_attack'; - if ($clientDist > $args->{attackMethod}{maxDistance} && $clientDist <= ($args->{attackMethod}{maxDistance} + 1) && $args->{temporary_extra_range} == 0) { - debug TF("[%s] Probably extra range provided by the server due to chasing, increasing range by 1.\n", $canAttack_fail_string), 'ai_attack'; - $args->{temporary_extra_range} = 1; - $args->{attackMethod}{maxDistance} += $args->{temporary_extra_range}; - $canAttack = canAttack($field, $realMyPos, $realMonsterPos, $config{attackCanSnipe}, $args->{attackMethod}{maxDistance}, $config{clientSight}); - } else { - debug TF("[%s] Reason unknown, allowing once.\n", $canAttack_fail_string), 'ai_attack'; - $hitTarget_when_not_possible = 1; - } - if ( - $config{"attackBeyondMaxDistance_waitForAgressive"} && - exists $args->{ai_attack_failed_waitForAgressive_give_up} && - defined $args->{ai_attack_failed_waitForAgressive_give_up}{time} - ) { - debug "[Accepting] Deleting ai_attack_failed_waitForAgressive_give_up time.\n"; - delete $args->{ai_attack_failed_waitForAgressive_give_up}{time};; - } - } - - # Here we decide what to do when a mob we have already hit is no longer in range or we have no LOS to it - # We also check if we have waited too long for the monster which we are waiting to get closer to us to approach - # TODO: Maybe we should separate this into 2 sections, one for out of range and another for no LOS - low priority - if ( - !$found_action && - $config{"attackBeyondMaxDistance_waitForAgressive"} && - $target->{dmgFromYou} > 0 && - ($canAttack == 0 || $canAttack == -1) && - !$hitTarget_when_not_possible - ) { - $args->{ai_attack_failed_waitForAgressive_give_up}{timeout} = 6 if !$args->{ai_attack_failed_waitForAgressive_give_up}{timeout}; - $args->{ai_attack_failed_waitForAgressive_give_up}{time} = time if !$args->{ai_attack_failed_waitForAgressive_give_up}{time}; - if (timeOut($args->{ai_attack_failed_waitForAgressive_give_up})) { - delete $args->{ai_attack_failed_waitForAgressive_give_up}{time}; - warning TF("[%s] Waited too long for target to get closer, dropping target. (you %s (%d %d), target %s (%d %d) [(%d %d) -> (%d %d)], distance %d, maxDistance %d, dmgFromYou %d)\n", $canAttack_fail_string, $char, $realMyPos->{x}, $realMyPos->{y}, $target, $realMonsterPos->{x}, $realMonsterPos->{y}, $target->{pos}{x}, $target->{pos}{y}, $target->{pos_to}{x}, $target->{pos_to}{y}, $realMonsterDist, $args->{attackMethod}{maxDistance}, $target->{dmgFromYou}), 'ai_attack'; - giveUp($args, $ID, 0); - } else { - $messageSender->sendAction($ID, ($config{'tankMode'}) ? 0 : 7) if ($config{"attackBeyondMaxDistance_sendAttackWhileWaiting"}); - debug TF("[%s - Waiting] %s (%d %d), target %s (%d %d) [(%d %d) -> (%d %d)], distance %d, maxDistance %d, dmgFromYou %d.\n", $canAttack_fail_string, $char, $realMyPos->{x}, $realMyPos->{y}, $target, $realMonsterPos->{x}, $realMonsterPos->{y}, $target->{pos}{x}, $target->{pos}{y}, $target->{pos_to}{x}, $target->{pos_to}{y}, $realMonsterDist, $args->{attackMethod}{maxDistance}, $target->{dmgFromYou}), 'ai_attack'; - } - $found_action = 1; - } - - # Here we decide what to do with a mob which is out of range or we have no LOS to - if ( - !$found_action && - ($canAttack == 0 || $canAttack == -1) && - !$hitTarget_when_not_possible - ) { - debug "Attack $char ($realMyPos->{x} $realMyPos->{y}) - target $target ($realMonsterPos->{x} $realMonsterPos->{y})\n"; - if ($canAttack == 0) { - debug "[Attack] [No range] Too far from us to attack, distance is $realMonsterDist, attack maxDistance is $args->{attackMethod}{maxDistance}\n", 'ai_attack'; - - } elsif ($canAttack == -1) { - debug "[Attack] [No LOS] No LOS from player to mob\n", 'ai_attack'; - } - - my $pos = meetingPosition($char, 1, $target, $args->{attackMethod}{maxDistance}); - if ($pos) { - debug "Attack $char ($realMyPos->{x} $realMyPos->{y}) - moving to meeting position ($pos->{x} $pos->{y})\n", 'ai_attack'; - - $args->{move_start} = time; - $args->{monsterLastMoveTime} = $target->{time_move}; - $args->{sentApproach} = 1; - - my $sendAttackWithMove = 0; - if ($config{"attackSendAttackWithMove"} && $args->{attackMethod}{type} eq "weapon") { - $sendAttackWithMove = 1; - } - - $char->route( - undef, - @{$pos}{qw(x y)}, - maxRouteTime => $config{'attackMaxRouteTime'}, - attackID => $ID, - sendAttackWithMove => $sendAttackWithMove, - avoidWalls => 0, - randomFactor => 0, - useManhattan => 1, - meetingSubRoute => 1, - noMapRoute => 1 - ); - } else { - message T("Unable to calculate a meetingPosition to target, dropping target\n"), "ai_attack"; - giveUp($args, $ID, 1); - } - $found_action = 1; - } - - if ( - !$found_action && - (!$config{"runFromTarget"} || $realMonsterDist >= $config{"runFromTarget_dist"} || $failed_runFromTarget) && - (!$config{"tankMode"} || !$target->{dmgFromYou}) - ) { - # Attack the target. In case of tanking, only attack if it hasn't been hit once. - if (!$args->{firstAttack}) { - $args->{firstAttack} = 1; - debug "Ready to attack target $target ($realMonsterPos->{x} $realMonsterPos->{y}) ($realMonsterDist blocks away); we're at ($realMyPos->{x} $realMyPos->{y})\n", "ai_attack"; - } - - $args->{unstuck}{time} = time if (!$args->{unstuck}{time}); - if (!$target->{dmgFromYou} && timeOut($args->{unstuck})) { - # We are close enough to the target, and we're trying to attack it, - # but some time has passed and we still haven't dealed any damage. - # Our recorded position might be out of sync, so try to unstuck - $args->{unstuck}{time} = time; - debug("Attack - trying to unstuck\n", "ai_attack"); - $char->move(@{$myPos}{qw(x y)}); - $args->{unstuck}{count}++; - } - - # Attack with weapon logic - if ($args->{attackMethod}{type} eq "weapon" && timeOut($timeout{ai_attack}) && timeOut($timeout{ai_attack_after_skill})) { - if (Actor::Item::scanConfigAndCheck("attackEquip")) { - #check if item needs to be equipped - Actor::Item::scanConfigAndEquip("attackEquip"); - } else { - debug "[Attack] Sending attack target $target ($realMonsterPos->{x} $realMonsterPos->{y}) ($realMonsterDist blocks away); we're at ($realMyPos->{x} $realMyPos->{y})\n", "ai_attack"; - $messageSender->sendAction($ID, ($config{'tankMode'}) ? 0 : 7); - $timeout{ai_attack}{time} = time; - delete $args->{attackMethod}; - - if ($config{"runFromTarget"} && $config{"runFromTarget_inAdvance"} && $realMonsterDist < $config{"runFromTarget_minStep"}) { - find_kite_position($args, 1, $target, $realMyPos, $realMonsterPos, 0); - } - } - $found_action = 1; - - # Attack with skill logic - } elsif ($args->{attackMethod}{type} eq "skill") { - my $slot = $args->{attackMethod}{skillSlot}; - delete $args->{attackMethod}; - - $ai_v{"attackSkillSlot_${slot}_time"} = time; - $ai_v{"attackSkillSlot_${slot}_target_time"}{$ID} = time; - - $args->{attackSkillSlot_attempts}{$slot}++; - - ai_setSuspend(0); - my $skill = new Skill(auto => $config{"attackSkillSlot_$slot"}); - my $skill_lvl = $config{"attackSkillSlot_${slot}_lvl"} || $char->getSkillLevel($skill); - ai_skillUse2( - $skill, - $skill_lvl, - $config{"attackSkillSlot_${slot}_maxCastTime"}, - $config{"attackSkillSlot_${slot}_minCastTime"}, - $config{"attackSkillSlot_${slot}_isSelfSkill"} ? $char : $target, - "attackSkillSlot_${slot}", - undef, - "attackSkill", - $config{"attackSkillSlot_${slot}_isStartSkill"} ? 1 : 0, - ); - debug "[attackSkillSlot] Auto-skill on monster ".getActorName($ID).": ".qq~$config{"attackSkillSlot_$slot"} (lvl $skill_lvl)\n~, "ai_attack"; - # TODO: We sould probably add a runFromTarget_inAdvance logic here also, we could want to kite using skills, but only instant cast ones like double strafe I believe - - $timeout{ai_attack_after_skill}{time} = time; - - $args->{monsterID} = $ID; - $found_action = 1; - - # Attack with combo logic - } elsif ($args->{attackMethod}{type} eq "combo") { - my $slot = $args->{attackMethod}{comboSlot}; - delete $args->{attackMethod}; - - $ai_v{"attackComboSlot_${slot}_time"} = time; - $ai_v{"attackComboSlot_${slot}_target_time"}{$ID} = time; - - my $skill = Skill->new(auto => $config{"attackComboSlot_$slot"}); - my $skill_lvl = $config{"attackComboSlot_${slot}_lvl"} || $char->getSkillLevel($skill); - ai_skillUse2( - $skill, - $skill_lvl, - $config{"attackComboSlot_${slot}_maxCastTime"}, - $config{"attackComboSlot_${slot}_minCastTime"}, - $config{"attackComboSlot_${slot}_isSelfSkill"} ? $char : $target, - undef, - $config{"attackComboSlot_${slot}_waitBeforeUse"}, - ); - - $args->{monsterID} = $ID; - $found_action = 1; - } - - } - - if (!$found_action && $config{tankMode}) { - if ($args->{dmgTo_last} != $target->{dmgTo}) { - $args->{ai_attack_giveup}{time} = time; - $char->sendAttackStop; - } - $args->{dmgTo_last} = $target->{dmgTo}; - $found_action = 1; - } - - Plugins::callHook('AI::Attack::main', {target => $target}) -} - -1; diff --git a/openkore_llm_knowledge/core/src/ErrorHandler.pm b/openkore_llm_knowledge/core/src/ErrorHandler.pm deleted file mode 100644 index 647265b0eb..0000000000 --- a/openkore_llm_knowledge/core/src/ErrorHandler.pm +++ /dev/null @@ -1,127 +0,0 @@ -######################################################################### -# OpenKore - Default error handler -# -# Copyright (c) 2006 OpenKore Development Team -# -# This software is open source, licensed under the GNU General Public -# License, version 2. -# Basically, this means that you're allowed to modify and distribute -# this software. However, if you distribute modified versions, you MUST -# also distribute the source code. -# See http://www.gnu.org/licenses/gpl.html for the full license. -######################################################################### -## -# MODULE DESCRIPTION: Default error handler. -# -# This module displays a nice error dialog to the user if the program crashes -# unexpectedly. -# -# To use this feature, simply type 'use ErrorHandler'. -package ErrorHandler; - -use strict; -use Carp; -use Scalar::Util; -use Globals; -use utf8; -use Translation; - -sub showError { - $net->serverDisconnect() if ($net); - if ($bus) { - $bus->close(); - undef $bus; - } - - if (!$Globals::interface || UNIVERSAL::isa($Globals::interface, "Interface::Startup") || UNIVERSAL::isa($Globals::interface, "Interface::Socket")) { - print TF("%s\nPress ENTER to exit this program.\n", $_[0]); - <STDIN>; - } else { - $Globals::interface->errorDialog($_[0]); - } -} - -sub errorHandler { - return unless (defined $^S && $^S == 0); - my $e = $_[0]; - - # Get the error message, and extract file and line number. - my ($file, $line, $errorMessage); - if (UNIVERSAL::isa($e, 'Exception::Class::Base')) { - $file = $e->file; - $line = $e->line; - $errorMessage = $e->message; - } else { - ($file, $line) = $e =~ / at (.+?) line (\d+)\.$/; - # Get rid of the annoying "@INC contains:" - $errorMessage = $e; - $errorMessage =~ s/ \(\@INC contains: .*\)//; - } - $errorMessage =~ s/[\r\n]+$//s; - - # Create the message to be displayed to the user. - my $display = TF("This program has encountered an unexpected problem. This is probably because " . - "of a recent server update, a bug in this program, or in one of the plugins. " . - "We apologize for this problem. You may get support from IRC or the forums.\n\n" . - "A detailed error report has been saved to errors.txt. Before posting a bug " . - "report, please try out the latest release GIT version first. If you are already using the latest release GIT " . - "version, search the forums first to see if your problem had already been solved, " . - "or has already been reported. If you truly believe you have encountered a bug in " . - "the program, please include the contents of the errors.txt in your bug report " . - "(https://github.com/openkore/openkore/issues), or we may not be able to help you!\n\n" . - "The error message is:\n" . - "%s", - $errorMessage); - - # Create the errors.txt error log. - my $log = ''; - $log .= "$Settings::NAME version ${Settings::VERSION}${Settings::SVN}\n" if (defined $Settings::VERSION); - $log .= "\@ai_seq = @Globals::ai_seq\n" if (@Globals::ai_seq); - $log .= "Network state = $Globals::conState\n" if (defined $Globals::conState); - $log .= "Network handler = " . Scalar::Util::blessed($Globals::net) . "\n" if ($Globals::net); - $log .= "Revision: " . Settings::getRevisionString() . "\n"; - if (@Plugins::plugins) { - $log .= "Loaded plugins:\n"; - foreach my $plugin (@Plugins::plugins) { - next if (!defined $plugin); - $log .= " $plugin->{filename} ($plugin->{name}; description: $plugin->{description})\n"; - } - } else { - $log .= "No loaded plugins.\n"; - } - $log .= "\nError message:\n$errorMessage\n\n"; - - # Add stack trace to errors.txt. - if (UNIVERSAL::isa($e, 'Exception::Class::Base')) { - $log .= "Stack trace:\n"; - $log .= $e->trace(); - } elsif (defined &Carp::longmess) { - $log .= "Stack trace:\n"; - my $e = $errorMessage; - $log .= Carp::longmess("$e\n"); - } - $log =~ s/\n+$//s; - - # Find out which line died. - if (defined $file && defined $line && -f $file && open(F, "<", $file)) { - my @lines = <F>; - close F; - - my $msg; - $msg .= " $lines[$line-2]" if ($line - 2 >= 0); - $msg .= "* $lines[$line-1]"; - $msg .= " $lines[$line]" if (@lines > $line); - $msg .= "\n" unless $msg =~ /\n$/s; - $log .= TF("\n\nDied at this line:\n%s\n", $msg); - } - - if (open(F, ">:utf8", "errors.txt")) { - print F $log; - close F; - } - showError($display); -} - -$SIG{__DIE__} = \&errorHandler; - -1; diff --git a/openkore_llm_knowledge/core/src/Interface.pm b/openkore_llm_knowledge/core/src/Interface.pm deleted file mode 100644 index 29d6cf87f1..0000000000 --- a/openkore_llm_knowledge/core/src/Interface.pm +++ /dev/null @@ -1,250 +0,0 @@ -######################################################################### -# OpenKore - User interface system -# -# Copyright (c) 2004 OpenKore development team -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -######################################################################### -## -# MODULE DESCRIPTION: User interface system. -# -# In OpenKore, the user interface code is seperated from the core code. -# Each user interface is implemented in a class. The Interface class is an -# abstract base class for all OpenKore user interface classes. - -package Interface; - -use strict; -use warnings; -no warnings 'redefine'; -use Time::HiRes qw(usleep); -use utf8; - -use Modules 'register'; -use Globals qw(%config $quit); -use Translation qw(T TF); -use Utils::Exceptions; - - -## -# Interface->loadInterface(String name) -# name: The class name of the interface to load, excluding the 'Interface::' prefix. -# Returns: The newly created interface object. -# -# Create a new interface of the specified class. -# -# Throws ModuleLoadException if the interface's Perl module cannot be loaded. -# Throws ClassCreateException if the interface class cannot be created. -sub loadInterface { - my ($self, $name) = @_; - - my $module = "Interface::$name"; - eval "use $module;"; - if ($@) { - ModuleLoadException->throw(error => "Cannot load interface $module. Error:\n$@", - module => $module); - } - - my $constructor = UNIVERSAL::can($module, 'new'); - if (!$constructor) { - ClassCreateException->throw(error => "Class $module has no constructor.", class => $module); - } - - my $interface = $constructor->($module); - Modules::register($module); - return $interface; -} - -## -# void $interface->mainLoop() -# -# Enter the interface's main loop. -sub mainLoop { - my $self = shift; - while (!$quit) { - usleep($config{sleepTime} || 10000); - $self->iterate(); - main::mainLoop(); - } -} - -## -# void $interface->iterate() -# -# Process messages in the user interface message queue. -# In other words: make sure the user interface updates itself. -# (redraw controls when necessary, etc.) -sub iterate { -} - -## -# String $interface->getInput(float timeout) -# timeout: Number of second to wait until keyboard data is available. -# Negative numbers will wait forever, 0 will not wait at all. -# Returns: The keyboard data (excluding newline), or undef if there's no -# keyboard data available. -# -# Reads keyboard data. - -## -# String $interface->query(String message, options...) -# message: A message to display when asking for input. -# Returns: The user input, or undef if the user cancelled. -# Requires: defined($message) -# -# Ask the user to enter a one-line input text. -# The following options are allowed: -# `l -# - cancelable - Whether the user is allowed to enter nothing. If this is set to true, -# then the user will be asked the same thing over and over until he -# replies with a non-empty input. The default is true. -# - title - A title to display in the query dialog. The default is "Query". -# - isPassword - Whether this query is a password query. The default is false. -# `l` -sub query { - my $self = shift; - my $message = shift; - my %args = @_; - - $args{title} = T("Query") if (!defined $args{title}); - $args{cancelable} = 1 if (!exists $args{cancelable}); - - my $title = "------------ $args{title} ------------"; - my $footer = '-' x length($title); - $message =~ s/\n+$//s; - $message = "$title\n$message\n$footer\n"; - - while (1) { - $self->writeOutput("message", $message, "input"); - $self->writeOutput("message", T("Enter your answer: "), "input"); - my $mode = $args{isPassword} ? -9 : -1; - my $result = $self->getInput($mode); - if (!defined($result) || $result eq '') { - if ($args{cancelable}) { - return undef; - } - } else { - return $result; - } - } -} - -## -# int $interface->showMenu(String message, Array<String>* choices, options...) -# message: The message to display while asking the user to make a choice. -# choices: The possible choices. -# Returns: The index of the chosen item, or -1 if the user cancelled. -# Requires: -# defined($message) -# defined($choices) -# for all $k in @{$choices}: defined($k) -# Ensures: -1 <= result < @{$choices} -# -# Ask the user to choose an item from a menu of choices. -# -# The following options are allowed: -# `l -# - title - The title to display when presenting the choices to the user. -# The default is 'Menu'. -# - cancelable - Whether the user is allowed to not choose. -# The default is true. -# `l` -sub showMenu { - my $self = shift; - my $message = shift; - my $choices = shift; - my %args = @_; - - $args{title} = "Menu" if (!defined $args{title}); - $args{cancelable} = 1 if (!exists $args{cancelable}); - - # Create a nicely formatted choice list. - my $maxNumberLength = length(@{$choices} + 1); - my $format = "%-" . $maxNumberLength . "s %-s\n"; - my $output = sprintf($format, "#", T("Choice")); - my $i = 0; - foreach my $item (@{$choices}) { - $output .= sprintf($format, $i, $item); - $i++; - } - - $message = "${output}------------------------\n$message"; - - while (1) { - my $choice = $self->query($message, - cancelable => $args{cancelable}, - title => $args{title}); - if (!defined($choice)) { - return -1; - } elsif ($choice !~ /^\d+$/ || $choice < 0 || $choice >= @{$choices}) { - $self->writeOutput("error", TF("'%s' is not a valid choice number.\n", $choice), "default"); - } else { - return $choice; - } - } -} - -## -# void $interface->writeOutput(String type, String message, String domain) -# Requires: defined($type) && defined($message) && defined($domain) -# -# Writes a message to the interface's console. -# This method should not be used directly, use Log::message() instead. -sub writeOutput { - # Do nothing; this is a dummy parent class -} - -## -# void $interface->beep() -# -# Emit a beep on the available audio device. -sub beep { - # Do nothing; this is a dummy parent class -} - -## -# String $interface->title([String title]) -# -# If $title is given, set the interface's window's title to $title. -# If not given, returns the current window title. -sub title { - # Do nothing; this is a dummy parent class -} - -## -# void $interface->errorDialog(String message, [boolean fatal = true]) -# message: The error message to display. -# fatal: Indicate that this is a fatal error (meaning that the application will -# exit after this dialog is closed). If set, the console interfaces -# will warn the user that the app is about to exit. -# Requires: defined($message) -# -# Display an error dialog. This function blocks until the user has closed the -# dialog. -# -# Consider using Log::error() if your message is not a fatal error, because -# Log::error() does not require any user interaction. -sub errorDialog { - my $self = shift; - my $message = shift; - my $fatal = shift; - $fatal = 1 unless defined $fatal; - - $self->writeOutput("error", "$message\n", "error"); - if ($fatal) { - $self->writeOutput("message", Translation::T("Press ENTER to exit this program.\n"), "console") - } else { - $self->writeOutput("message", Translation::T("Press ENTER to continue...\n"), "console") - } - $self->getInput(-1); -} - -1; \ No newline at end of file diff --git a/openkore_llm_knowledge/core/src/Log.pm b/openkore_llm_knowledge/core/src/Log.pm deleted file mode 100644 index 08870e3adc..0000000000 --- a/openkore_llm_knowledge/core/src/Log.pm +++ /dev/null @@ -1,458 +0,0 @@ -######################################################################### -# OpenKore - Message Logging Framework -# -# This software is open source, licensed under the GNU General Public -# License, version 2. -# Basically, this means that you're allowed to modify and distribute -# this software. However, if you distribute modified versions, you MUST -# also distribute the source code. -# See http://www.gnu.org/licenses/gpl.html for the full license. -# -######################################################################### -## -# MODULE DESCRIPTION: Message Logging framework -# -# <h3>What is a logging framework and why is it needed?</h3> -# -# Kore used to print messages to the console using print(). There are several -# problems though: -# `l -# - Messages can only be printed to the console. If you want to print it -# to elsewhere you have to resort to all kinds of hacks (take a look -# at the code for sending console output to X-Kore, for example). -# - The messages have no classification. You have a message, but there's -# no easy way for the program to find out what kind of message it is; -# you don't know it's context. This means that the user can't really control -# what kind of messages he does and doesn't want to see. -# - Debug messages are all in the form of print "bla\n" if ($config{'verbose'}); -# You can either enable all debug messages, or nothing at all. For developers, -# the huge amount of debug messages can make things look cluttered. -# `l` -# -# The logging framework provides a new way to print messages: -# `l -# - You can print messages (of course). -# - You can classify messages: attaching a context (domain) to a message you print. -# - You can intercept messages and decide what else to do with it. You can write to -# a file, send to X-Kore (based on the message's domain), or whatever you want. -# - You can attach certain colors to messages of a certain domains. -# - You can choose what kind of message you do and do not want to see. -# `l` -# -# The most important functions are: -# Log::message(), Log::warning(), Log::error(), Log::debug() -# -# You pass the following arguments to those functions: -# `l -# - message: The message you want to print. -# - domain: The message domain (context). This is used to classify a message. -# - level: The message's verbosity level. The message will only be printed if this number -# is lower than or equal to $config{'verbose'} (or $config{'debug'} if this is a -# debug message). Important messages should have a low verbosity level, -# unimportant/redundant messages should have a high verbosity level. -# `l` - -# Known domains: -# attacked Monster attacks you -# attackedMiss Monster attacks you but miss -# attackMon You attack monster -# attackMonMiss You attack monster but miss -# connection Connection messages -# deal Deal messages -# drop Monster drop related -# emotion Emoticon -# equip Equipment Switching -# gmchat GM chat message -# guildchat Guild chat message -# info View info that's requested by the user (status, guild info, etc.) -# input Waiting for user input -# inventory Inventory related messages -# useItem You used item -# list List of information (monster list, player list, item list, etc.) -# load Loading config files -# menu Menu choices -# npc NPC messages -# party Party/follow related -# partychat Party chat messages -# plugins Messages about plugin handling -# pm Private chat message -# publicchat Public chat message -# route Routing/pathfinding messages -# sold Item sold while vending. -# skill Skill use unrelated to attack -# selfSkill Skills used by yourself -# startup Messages that are printed during startup. -# storage Storage item added/removed -# success An operation succeeded -# syntax Syntax check files -# system System messages -# teleport Teleporting -# xkore X-Kore system messages - -# Debug domains: -# ai_attack -# ai_autoCart -# ai_move -# parseInput -# parseMsg -# parseMsg_damage -# parseMsg_presence -# portalRecord -# sendPacket -# ai -# npc -# route -# useTeleport - -package Log; - -use strict; -use Exporter; -use Time::HiRes; -use base qw(Exporter); - -use Modules 'register'; -use Globals qw(%config $interface %consoleColors $field); -use Utils::DataStructures qw(binAdd existsInList); -use Utils qw(binAdd existsInList getFormattedDate); - -our @EXPORT_OK = qw(message warning error debug); - - -################################# -################################# -# VARIABLES -################################# -################################# - - -# The verbosity level for messages. Messages that have a higher verbosity than this will not be printed. -# Low level = important messages. High level = less important messages. -# If you set the current verbosity higher, you will see more messages. -our $warningVerbosity; -our $errorVerbosity; - -# Enable/disable printing certain domains to console. -# Usage: $messageConsole{$domain} = $enabled -our %messageConsole; -our %warningConsole; -our %errorConsole; -our %debugConsole; - -# Messages can also printed to files. These variables -# contain filenames of the files to print to. -# Usage: @{$messageFiles{$domain}} = (list of filenames) -our %messageFiles; -our %warningFiles; -our %errorFiles; -our %debugFiles; - -# Message hooks are stored here -our @hooks; - -# Enable/disable adding a timestamp to log files. -our $logTimestamp; -# Enable/disable adding a timestamp to chat logs. -our $chatTimestamp; - - -# use SelfLoader; 1; -# __DATA__ - - -################################# -################################# -# PRIVATE FUNCTIONS -################################# -################################# - - -sub MODINIT { - $warningVerbosity = 1; - $errorVerbosity = 1; - $logTimestamp = 1; - $chatTimestamp = 1; -} - -sub processMsg { - my $type = shift; - my $message = shift; - my $domain = (shift or "console"); - my $level = (shift or 0); - my $currentVerbosity = shift; - my $consoleVar = shift; - my $files = shift; - my (undef, undef, undef, $near) = caller(2); - my (undef, undef, undef, $far) = caller(3); - - $currentVerbosity = 1 if ($currentVerbosity eq ""); - - # Beep on certain domains - $interface->beep() if existsInList($config{beepDomains}, $domain) && - !(existsInList($config{beepDomains_notInTown}, $domain) && - $field->isCity); - - # Add timestamp if domain was specified in config.txt/showTimeDomains - if (existsInList($config{showTimeDomains}, $domain)) { - my @tmpdate = localtime(); - $tmpdate[5] += 1900; - $tmpdate[4]++; - for (my $i = 0; $i < @tmpdate; $i++) { - if ($tmpdate[$i] < 10) {$tmpdate[$i] = "0".$tmpdate[$i]}; - } - if (defined (my $format = $config{showTimeDomainsFormat})) { - $format =~ s/H/$tmpdate[2]/g; - $format =~ s/M/$tmpdate[1]/g; - $format =~ s/S/$tmpdate[0]/g; - $format =~ s/y/$tmpdate[5]/g; - $format =~ s/m/$tmpdate[4]/g; - $format =~ s/d/$tmpdate[3]/g; - $message = "$format $message"; - } else { - $message = "[$tmpdate[2]:$tmpdate[1]:$tmpdate[0]] $message"; - } - }; - - # Print to console if the current verbosity is high enough - if ($level <= $currentVerbosity) { - $consoleVar->{$domain} = 1 if (!defined($consoleVar->{$domain})); - if ($consoleVar->{$domain}) { - if ($interface) { - $message = "[$domain] " . $message if ($config{showDomain}); - my (undef, $microseconds) = Time::HiRes::gettimeofday; - $microseconds = substr($microseconds, 0, 2); - my $message2 = "[".getFormattedDate(int(time)).".$microseconds] ".$message; - if ($config{showTime}) { - $interface->writeOutput($type, $message2, $domain); - } else { - $interface->writeOutput($type, $message, $domain); - } - - if ($config{logConsole} && - open(F, ">>:utf8", $Settings::console_log_file)) { - print F $message2; - close(F); - } - } else { - print $message; - } - } - } - - # Print to files - foreach my $file (@{$files->{$domain}}) { - if (open(F, ">>:utf8", "$Settings::logs_folder/$file")) { - print F '['. getFormattedDate(int(time)) .'] ' if ($logTimestamp); - print F $message; - close(F); - } - } - - # Call hooks - foreach (@hooks) { - next if (!defined($_)); - $_->{'func'}->($type, $domain, $level, $currentVerbosity, $message, $_->{'user_data'}, $near, $far); - } -} - - -################################# -################################# -# PUBLIC METHODS -################################# -################################# - - -## -# Log::message(message, [domain], [level]) -# Requires: $message must be encoded in UTF-8. -# -# Prints a normal message. See the description for Log.pm for more details -# about the parameters. -sub message { - my ($message, $domain, $level) = @_; - $domain ||= "console"; - $level = 5 if existsInList($config{squelchDomains}, $domain); - $level = 0 if existsInList($config{verboseDomains}, $domain); - return processMsg("message", # type - $message, - $domain, - $level, - $config{'verbose'}, # currentVerbosity - \%messageConsole, - \%messageFiles); -} - - -## -# Log::warning(message, [domain], [level]) -# -# Prints a warning message. It warns the user that a possible non-fatal error has occured or will occur. -# See the description for Log.pm for more details about the parameters. -sub warning { - my ($message, $domain, $level) = @_; - $domain ||= "console"; - $level = 5 if existsInList($config{squelchDomains}, $domain); - $level = 0 if existsInList($config{verboseDomains}, $domain); - return processMsg("warning", - $message, - $domain, - $level, - $warningVerbosity, - \%warningConsole, - \%warningFiles); -} - - -## -# Log::error(message, [domain], [level]) -# Requires: $message must be encoded in UTF-8. -# -# Prints an error message. It tells the user that a non-recoverable error has -# occured. A "non-recoverable error" could either be a fatal error, or an -# error that prevents the program from performing an action the user requested. -# -# Examples of non-recoverable errors: -# `l -# - Kore receives the "You haven't paid for this account"-packet. The error is -# fatal, so the entire program must exit. -# - The user typed in an invalid/unrecognized command. Kore cannot perform the -# command the user requested, but will not exit because this error is not -# fatal. -# `l` -# See the description for Log.pm for more details about the parameters. -sub error { - my ($message, $domain, $level) = @_; - $domain ||= "console"; - return processMsg("error", - $message, - $domain, - $level, - $errorVerbosity, - \%errorConsole, - \%errorFiles); -} - - -## -# Log::debug(message, [domain], [level]) -# Requires: $message must be encoded in UTF-8. -# -# Prints a debugging message. See the description for Log.pm for more details about the parameters. -sub debug { - my ($message, $domain, $level) = @_; - $domain ||= "console"; - $level = 1 if (!defined $level); - $level = 0 if (existsInList($config{debugDomains}, $_[1])); - $level = 5 if (existsInList($config{squelchDomains}, $_[1])); - return processMsg("debug", - $message, - $domain, - $level, - (defined $config{'debug'}) ? $config{'debug'} : 0, - \%debugConsole, - \%debugFiles); -} - - -## -# Log::addHook(r_func, [user_data]) -# r_func: A reference to the function to call. -# user_data: Additional data to pass to r_func. -# Returns: An ID which you can use to remove this hook. -# -# Adds a hook. Every time Log::message(), Log::warning(), Log::error() or Log::debug() is called, -# r_func is also called, in the following way: -# <pre> -# r_func->($type, $domain, $level, $globalVerbosity, $message, $user_data); -# $type : One of the following: "message", "warning", "error", "debug". -# $domain : The message's domain. -# $level : The message's own verbosity level. -# $globalVerbosity : The global verbosity level. -# $message : The message itself. -# $user_data : The value of user_data, as passed to addHook. -# $near : The function that called "message", "warning", "error" or "debug" -# $far : The function that called $near -# </pre> -# -# See also: Log::delHook() -# -# Example: -# sub hook { -# my $type = shift; # "message" -# my $domain = shift; # "MyDomain" -# my $level = shift; # 2 -# my $globalVerbosity = shift; # 1 (equal to $config{'verbose'}) -# my $message = shift; # "Hello World" -# my $user_data = shift; # "my_user_data" -# my $near = shift; # "Commands::cmdWhere" -# my $far = shift; # "Commands::run" -# # Do whatever you want here -# } -# Log::addHook(\&hook, "my_user_data"); -# -# $config{'verbose'} = 1; -# # Note that the following function will not print anything to screen, -# # because it's verbosity level is higher than the global verbosity -# # level ($config{'verbose'}). -# Log::message("Hello World", "MyDomain", 2); # hook() will now be called -sub addHook { - my ($r_func, $user_data) = @_; - my %hook; - $hook{func} = $r_func; - $hook{user_data} = $user_data; - return binAdd(\@hooks, \%hook); -} - -## -# Log::delHook(ID) -# ID: A hook ID, as returned by addHook(). -# -# Removes a hook. r_func will not be called anymore. -# -# Example: -# my $ID = Log::addHook(\&hook); -# Log::message("Hello World", "MyDomain"); # hook() is called -# Log::delHook($ID); -# Log::message("Hello World", "MyDomain"); # hook() is NOT called -sub delHook { - my $ID = shift; - delete $hooks[$ID]; -} - -## -# Log::parseLogToFile(args,hash) -# -# args has to look like domain=file -# but can look like domain1=file1.txt;domain2=file2.txt,file3.txt -# -# The hash has to be a reference for the output hash. -sub parseLogToFile { - my $args = shift; - my $list = shift; - $args =~ s/\s//g; - my @domains = split (';', $args); - my $files; - foreach my $domain (@domains) { - ($domain,$files) = split ('=', $domain); - my @filesArray = split (',', $files); - $list->{$domain} = []; - foreach my $file (@filesArray) { - push(@{$list->{$domain}}, $file); - } - } -} - -## -# initLogFiles() -# -# This function should be called everytime config.txt is (re)loaded. -sub initLogFiles { - parseLogToFile($config{logToFile_Messages}, \%messageFiles) if $config{logToFile_Messages}; - parseLogToFile($config{logToFile_Warnings}, \%warningFiles) if $config{logToFile_Warnings}; - parseLogToFile($config{logToFile_Errors}, \%errorFiles) if $config{logToFile_Errors}; - parseLogToFile($config{logToFile_Debug}, \%debugFiles) if $config{logToFile_Debug}; -} - - -return 1; diff --git a/openkore_llm_knowledge/core/src/Misc.pm b/openkore_llm_knowledge/core/src/Misc.pm deleted file mode 100644 index ddc4737b77..0000000000 --- a/openkore_llm_knowledge/core/src/Misc.pm +++ /dev/null @@ -1,5764 +0,0 @@ -######################################################################### -# OpenKore - Miscellaneous functions -# -# This software is open source, licensed under the GNU General Public -# License, version 2. -# Basically, this means that you're allowed to modify and distribute -# this software. However, if you distribute modified versions, you MUST -# also distribute the source code. -# See http://www.gnu.org/licenses/gpl.html for the full license. -# -# $Revision$ -# $Id$ -# -######################################################################### -## -# MODULE DESCRIPTION: Miscellaneous functions -# -# This module contains functions that do not belong in any other modules. -# The difference between Misc.pm and Utils.pm is that Misc.pm can have -# dependencies on other Kore modules. - -package Misc; - -use strict; -use Exporter; -use Carp::Assert; -use Data::Dumper; -use Compress::Zlib; -use base qw(Exporter); -use utf8; -use Math::Trig; - -use Globals; -use Log qw(message warning error debug); -use Plugins; -use FileParsers; -use Settings; -use Utils; -use Utils::Assert; -use Skill; -use Field; -use Network; -use Network::Send (); -use AI; -use Actor; -use Actor::You; -use Actor::Player; -use Actor::Monster; -use Actor::Party; -use Actor::NPC; -use Actor::Portal; -use Actor::Pet; -use Actor::Slave; -use Actor::Unknown; -use Time::HiRes qw(time usleep); -use Translation; -use Utils::Exceptions; -use Utils::PathFinding; - -our @EXPORT = ( - # Config modifiers - qw/auth - configModify - bulkConfigModify - setTimeout - saveConfigFile/, - - # Debugging - qw/debug_showSpots - visualDump/, - - # Field math - qw/calcRectArea2 - objectInsideSpell - objectInsideCasting - objectIsMovingTowards - objectIsMovingTowardsPlayer - get_dance_position/, - - # Inventory management - qw/inInventory - inventoryItemRemoved - storageGet - transferItems - cardName - itemName - itemNameSimple - itemNameToID - itemNameToIDList - containsItemNameToIDList/, - - # File Parsing and Writing - qw/chatLog - shopLog - monsterLog - playerLog - deadLog - searchStoreInfo/, - - # Logging - qw/itemLog/, - - # OS specific - qw/launchURL/, - - # Misc - qw/ - actorAdded - actorRemoved - actorListClearing - avoidGM_talk - avoidList_talk - avoidList_ID - calcStat - center - charSelectScreen - chatLog_clear - checkAllowedMap - checkFollowMode - isMySlaveID - isNotMySlaveID - is_aggressive - is_aggressive_slave - checkMonsterCleanness - slave_checkMonsterCleanness - createCharacter - deal - dealAddItem - drop - dumpData - getEmotionByCommand - getIDFromChat - getNPCName - getPlayerNameFromCache - getPortalDestName - getResponse - getSpellName - headgearName - itemLog_clear - look - lookAtPosition - lookAtPositionNaturally - getNaturalLookDirections - getClosestWalls - manualMove - meetingPosition - objectAdded - objectRemoved - items_control - pickupitems - mon_control - monsterName - positionNearPlayer - positionNearPortal - printItemDesc - processNameRequestQueue - quit - offlineMode - relog - sendMessage - setSkillUseTimer - setPartySkillTimer - setStatus - countCastOn - stripLanguageCode - switchConfigFile - updateDamageTables - updatePlayerNameCache - canUseTeleport - top10Listing - whenGroundStatus - writeStorageLog - getBestTarget - isSafe - isSafeActorQuery - isCellOccupied/, - - # Actor's Actions Text - qw/attack_string - skillCast_string - skillUse_string - skillUseLocation_string - skillUseNoDamage_string - status_string/, - - # AI Math - qw/lineIntersection - percent_hp - percent_sp - percent_ap - percent_weight/, - - # Misc Functions - qw/avoidGM_near - avoidList_near - compilePortals - compilePortals_check - portalExists - portalExists2 - portalExistsAirship - redirectXKoreMessages - monKilled - getActorName - getActorNames - findPartyUserID - getNPCInfo - skillName - checkSelfCondition - checkPlayerCondition - checkMonsterCondition - makeShop - openShop - closeShop - makeBuyerShop - openBuyerShop - closeBuyerShop - inLockMap - parseReload - setCharDeleteDate - toBase62 - fromBase62 - solveItemLink - solveMessage - solveMSG - absunit - autoNpcTalk/, - - # Npc buy and sell - qw/cancelNpcBuySell - completeNpcSell - completeNpcBuy/, - - # Char login - qw/CharacterLogin/, - ); - - -# use SelfLoader; 1; -# __DATA__ - - - -sub _checkActorHash($$$$) { - my ($name, $hash, $type, $hashName) = @_; - foreach my $actor (values %{$hash}) { - if (!UNIVERSAL::isa($actor, $type)) { - die "$name\nUnblessed item in $hashName list:\n" . - Dumper($hash); - } - } -} - -# Checks whether the internal state of some variables are correct. -sub checkValidity { - return if (!DEBUG || $ENV{OPENKORE_NO_CHECKVALIDITY}); - my ($name) = @_; - $name = "Validity check:" if (!defined $name); - - assertClass($char, 'Actor::You') if ($net && $net->getState() == Network::IN_GAME - && $net->isa('Network::XKore')); - assertClass($char, 'Actor::You') if ($char); - return; - - _checkActorHash($name, \%items, 'Actor::Item', 'item'); - _checkActorHash($name, \%monsters, 'Actor::Monster', 'monster'); - _checkActorHash($name, \%players, 'Actor::Player', 'player'); - _checkActorHash($name, \%pets, 'Actor::Pet', 'pet'); - _checkActorHash($name, \%npcs, 'Actor::NPC', 'NPC'); - _checkActorHash($name, \%portals, 'Actor::Portal', 'portals'); -} - - -####################################### -####################################### -### CATEGORY: Configuration modifiers -####################################### -####################################### - -sub auth { - my $user = shift; - my $flag = shift; - if ($flag) { - message TF("Authorized user '%s' for admin\n", $user), "success"; - } else { - message TF("Revoked admin privilages for user '%s'\n", $user), "success"; - } - $overallAuth{$user} = $flag; - writeDataFile(Settings::getControlFilename("overallAuth.txt"), \%overallAuth); -} - -## -# void configModify(String key, String value, ...) -# key: a key name. -# value: the new value. -# -# Changes the value of the configuration option $key to $value. -# Both %config and config.txt will be updated. -# -# You may also call configModify() with additional optional options: -# `l -# - autoCreate (boolean): Whether the configuration option $key -# should be created if it doesn't already exist. -# The default is true. -# - silent (boolean): By default, output will be printed, notifying the user -# that a config option has been changed. Setting this to -# true will surpress that output. -# `l` -sub configModify { - my $key = shift; - my $val = shift; - my %args; - - if (@_ == 1) { - $args{silent} = $_[0]; - } else { - %args = @_; - } - $args{autoCreate} = 1 if (!exists $args{autoCreate}); - - Plugins::callHook('configModify', { - key => $key, - val => $val, - additionalOptions => \%args - }); - - if (!$args{silent} && $key !~ /password/i) { - my $oldval = $config{$key}; - if (!defined $oldval) { - $oldval = "not set"; - } - - if ($config{$key} eq $val) { - if ($val) { - message TF("Config '%s' is already %s\n", $key, $val), "info"; - }else{ - message TF("Config '%s' is already *None*\n", $key), "info"; - } - return; - } - - if (!defined $val) { - message TF("Config '%s' unset (was %s)\n", $key, $oldval), "info"; - } else { - message TF("Config '%s' set to %s (was %s)\n", $key, $val, $oldval), "info"; - } - } - if ($args{autoCreate} && !exists $config{$key}) { - my $f; - if (open($f, ">>", Settings::getConfigFilename())) { - print $f "$key\n"; - close($f); - } - } - $config{$key} = $val; - Settings::update_log_filenames() if $key =~ /^(username|char|server)$/o; - saveConfigFile(); - - Plugins::callHook('post_configModify'); -} - -## -# bulkConfigModify (r_hash, [silent]) -# r_hash: key => value to change -# silent: if set to 1, do not print a message to the console. -# -# like configModify but for more than one value at the same time. -sub bulkConfigModify { - my $r_hash = shift; - my $silent = shift; - my $oldval; - - - my %create_keys; - foreach my $key (keys %{$r_hash}) { - Plugins::callHook('configModify', { - key => $key, - val => $r_hash->{$key}, - silent => $silent - }); - - $oldval = $config{$key}; - - if (!exists $config{$key}) { - $create_keys{$key} = 1; - } - - $config{$key} = $r_hash->{$key}; - - if ($key =~ /password/i) { - message TF("Config '%s' set to %s (was *not-displayed*)\n", $key, $r_hash->{$key}), "info" unless ($silent); - } else { - message TF("Config '%s' set to %s (was %s)\n", $key, $r_hash->{$key}, $oldval), "info" unless ($silent); - } - } - - if (scalar keys %create_keys > 0) { - my $f; - if (open($f, ">>", Settings::getConfigFilename())) { - foreach my $key (keys %create_keys) { - print $f "$key\n"; - } - close($f); - } - } - - saveConfigFile(); - - Plugins::callHook('post_configModify'); -} - -## -# saveConfigFile() -# -# Writes %config to config.txt. -sub saveConfigFile { - writeDataFileIntact(Settings::getConfigFilename(), \%config); -} - -sub setTimeout { - my $timeout = shift; - my $time = shift; - my $silent = shift; - my %args; - - if (@_ == 1) { - $args{silent} = $_[0]; - } else { - %args = @_; - } - - $args{autoCreate} = 1 if (!exists $args{autoCreate}); - - Plugins::callHook('setTimeout', { - timeout => $timeout, - time => $time, - additionalOptions => \%args - }); - - if (!$args{silent}) { - my $oldtime = $timeout{$timeout}{timeout}; - if (!defined $oldtime) { - $oldtime = "not set"; - } - - if ($timeout{$timeout}{timeout} eq $time) { - if ($time) { - message TF("Timeout '%s' is already %s\n", $timeout, $time), "info" unless ($silent); - } else { - message TF("Timeout '%s' is already *None*\n", $timeout), "info" unless ($silent); - } - return; - } - - if (!defined $time) { - message TF("Timeout '%s' unset (was %s)\n", $timeout, $oldtime), "info" unless ($silent); - } else { - message TF("Timeout '%s' set to %s (was %s)\n", $timeout, $time, $oldtime), "info" unless ($silent); - } - } - if ($args{autoCreate} && !exists $timeout{$timeout}{timeout}) { - my $f; - if (open($f, ">>", Settings::getControlFilename("timeouts.txt"))) { - print $f "$timeout\n"; - close($f); - } - } - - $timeout{$timeout}{timeout} = $time; - writeDataFileIntact2(Settings::getControlFilename("timeouts.txt"), \%timeout); -} - - -####################################### -####################################### -### Category: Debugging -####################################### -####################################### - -our %debug_showSpots_list; - -sub debug_showSpots { - return unless $net->clientAlive(); - my $ID = shift; - my $spots = shift; - my $special = shift; - - if ($debug_showSpots_list{$ID}) { - foreach (@{$debug_showSpots_list{$ID}}) { - my $msg = pack("C*", 0x20, 0x01) . pack("V", $_); - $net->clientSend($msg); - } - } - - my $i = 1554; - $debug_showSpots_list{$ID} = []; - foreach (@{$spots}) { - next if !defined $_; - my $msg = pack("C*", 0x1F, 0x01) - . pack("V*", $i, 1550) - . pack("v*", $_->{x}, $_->{y}) - . pack("C*", 0x93, 0); - $net->clientSend($msg); - $net->clientSend($msg); - push @{$debug_showSpots_list{$ID}}, $i; - $i++; - } - - if ($special) { - my $msg = pack("C*", 0x1F, 0x01) - . pack("V*", 1553, 1550) - . pack("v*", $special->{x}, $special->{y}) - . pack("C*", 0x83, 0); - $net->clientSend($msg); - $net->clientSend($msg); - push @{$debug_showSpots_list{$ID}}, 1553; - } -} - -## -# visualDump(data [, label]) -# -# Show the bytes in $data on screen as hexadecimal. -# Displays the label if provided. -sub visualDump { - my ($msg, $label) = @_; - my $dump; - my $puncations = quotemeta '~!@#$%^&*()_-+=|\"\''; - - # doesn't work right with debugPacket_sent - #no encoding 'utf8'; - #use bytes; - - $dump = "================================================\n"; - if (defined $label) { - $dump .= sprintf("%-15s [%d bytes] %s\n", $label, length($msg), getFormattedDate(int(time))); - } else { - $dump .= sprintf("%d bytes %s\n", length($msg), getFormattedDate(int(time))); - } - - for (my $i = 0; $i < length($msg); $i += 16) { - my $line; - my $data = substr($msg, $i, 16); - my $rawData = ''; - - for (my $j = 0; $j < length($data); $j++) { - my $char = substr($data, $j, 1); - if (ord($char) < 32 || ord($char) > 126) { - $rawData .= '.'; - } else { - $rawData .= substr($data, $j, 1); - } - } - - $line = getHex(substr($data, 0, 8)); - $line .= ' ' . getHex(substr($data, 8)) if (length($data) > 8); - - $line .= ' ' x (50 - length($line)) if (length($line) < 54); - $line .= " $rawData\n"; - $line = sprintf("%3d> ", $i) . $line; - $dump .= $line; - } - message $dump; -} - - -####################################### -####################################### -### CATEGORY: Field math -####################################### -####################################### - -## -# calcRectArea2($x, $y, $radius, $minRange) -# Returns: an array with position hashes. Each has contains an x and a y key. -# -# Creates a rectangle with center ($x,$y) and radius $radius, -# and returns a list of positions inside the rectangle that are -# not closer than $minRange to the center. -sub calcRectArea2 { - my ($cx, $cy, $r, $min) = @_; - - my @rectangle; - for (my $x = $cx - $r; $x <= $cx + $r; $x++) { - for (my $y = $cy - $r; $y <= $cy + $r; $y++) { - next if distance({x => $cx, y => $cy}, {x => $x, y => $y}) < $min; - push(@rectangle, {x => $x, y => $y}); - } - } - return @rectangle; -} - -## -# objectInsideSpell(object, [ignore_party_members = 1]) -# object: reference to a player or monster hash. -# -# Checks whether an object is inside someone else's spell area. -# (Traps are also "area spells"). -sub objectInsideSpell { - my $object = shift; - my $ignore_party_members = shift; - $ignore_party_members = 1 if (!defined $ignore_party_members); - - my ($x, $y) = ($object->{pos_to}{x}, $object->{pos_to}{y}); - foreach (@spellsID) { - my $spell = $spells{$_}; - if ((!$ignore_party_members || !$char->{party}{joined} || !$char->{party}{users}{$spell->{sourceID}}) - && $spell->{sourceID} ne $accountID - && isNotMySlaveID($spell->{sourceID}) - && $spell->{pos}{x} == $x && $spell->{pos}{y} == $y) { - return 1; - } - } - return 0; -} - -sub objectInsideCasting { - my ($monster) = @_; - - # TODO: Is there any situation where we should use calcPosFromPathfinding or calcPosFromTime here? - my $monsterPos = calcPosition($monster); - - foreach my $caster (@$playersList, @$slavesList) { - next if (isMySlaveID($caster->{ID})); - next if ($char->{party} && $char->{party}{joined} && exists $char->{party}{users}{$caster->{ID}}); - - next unless (exists $caster->{casting} && defined $caster->{casting} && $caster->{casting}); - - my $cast = $caster->{casting}; - next unless ($cast->{x} != 0 || $cast->{y} != 0); - - my $skill = $cast->{skill}; - my $skillID = $skill->getIDN(); - my $range = judgeSkillArea($skillID); - - my %coords; - $coords{x} = $cast->{x}; - $coords{y} = $cast->{y}; - - my $distFromCenter = blockDistance($monsterPos, \%coords); - - if ($distFromCenter <= $range) { - #warning TF("Target %s is inside skill %s (range %d | dist %d) from caster %s.\n", $monster, $skill, $range, $distFromCenter, $caster), 'slave_attack'; - return 1; - } - } - return 0; -} - -## -# objectIsMovingTowards(object1, object2, [max_variance]) -# -# Check whether $object1 is moving towards $object2. -sub objectIsMovingTowards { - my $obj = shift; - my $obj2 = shift; - my $max_variance = (shift || 15); - - if (!timeOut($obj->{time_move}, $obj->{time_move_calc})) { - # $obj is still moving - my %vec; - getVector(\%vec, $obj->{pos_to}, $obj->{pos}); - return checkMovementDirection($obj->{pos}, \%vec, $obj2->{pos_to}, $max_variance); - } - return 0; -} - -## -# objectIsMovingTowardsPlayer(object, [ignore_party_members = 1]) -# -# Check whether an object is moving towards a player. -sub objectIsMovingTowardsPlayer { - my $obj = shift; - my $ignore_party_members = shift; - $ignore_party_members = 1 if (!defined $ignore_party_members); - - if (!timeOut($obj->{time_move}, $obj->{time_move_calc}) && @playersID) { - # Monster is still moving, and there are players on screen - my %vec; - getVector(\%vec, $obj->{pos_to}, $obj->{pos}); - - for my $player (@$playersList) { - my $ID = $player->{ID}; - next if ( - ($ignore_party_members && $char->{party}{joined} && $char->{party}{users}{$ID}) - || (defined($player->{name}) && existsInList($config{tankersList}, $player->{name})) - || $player->statusActive('EFFECTSTATE_SPECIALHIDING')); - if (checkMovementDirection($obj->{pos}, \%vec, $player->{pos}, 15)) { - return 1; - } - } - } - return 0; -} - -use constant USE_DIAGONAL => 0; - -## -# get_dance_position(slave, target) -# slave: reference to the slave actor which is dancing. -# target: reference to the target actor which you are attacking. -# -# Returns: reference to a hash containing both x and y coordinates of the next dance position. -# -# Dance algorithm used in attack_dance -# Based on AzzyAI dance -sub get_dance_position { - my ($slave, $target) = @_; - my ($dy, $dx); - - # TODO: Is there any situation where we should use calcPosFromPathfinding or calcPosFromTime here? - my $my_pos = calcPosition($slave); - my $target_pos = calcPosition($target); - - my @possible; - - # same x and y - if ($my_pos->{x} == $target_pos->{x} && $my_pos->{y} == $target_pos->{y}) { - push(@possible,( - { x => 1, y => 0 }, - { x => 0, y => 1 }, - { x => 0, y => -1 }, - { x => -1, y => 0 } - )); - push(@possible,( - { x => 1, y => 1 }, - { x => -1, y => -1 }, - { x => 1, y => -1 }, - { x => -1, y => 1 } - )) if (USE_DIAGONAL == 1); - - } elsif ($my_pos->{x} == $target_pos->{x}) { - $dy = abs($my_pos->{y} - $target_pos->{y}); - if ($my_pos->{y} > $target_pos->{y}) { - # same x, 2 y north - if ($dy == 2) { - push(@possible,( - { x => 0, y => -1 } - )); - push(@possible,( - { x => 1, y => -1 }, - { x => -1, y => -1 } - )) if (USE_DIAGONAL == 1); - # same x, 1 y north - } else { - push(@possible,( - { x => 1, y => 0 }, - { x => -1, y => 0 }, - { x => 0, y => 1 } - )); - } - } else { - # same x, 2 y south - if ($dy == 2) { - push(@possible,( - { x => 0, y => 1 } - )); - push(@possible,( - { x => 1, y => 1 }, - { x => -1, y => 1 } - )) if (USE_DIAGONAL == 1); - # same x, 1 y south - } else { - push(@possible,( - { x => 1, y => 0 }, - { x => -1, y => 0 }, - { x => 0, y => -1 } - )); - } - } - - } elsif ($my_pos->{y} == $target_pos->{y}) { - $dx = abs($my_pos->{x} - $target_pos->{x}); - if ($my_pos->{x} > $target_pos->{x}) { - # same y, 2 x east - if ($dx == 2) { - push(@possible,( - { x => -1, y => 0 } - )); - push(@possible,( - { x => -1, y => 1 }, - { x => -1, y => -1 } - )) if (USE_DIAGONAL == 1); - # same y, 1 x east - } else { - push(@possible,( - { x => 0, y => 1 }, - { x => 0, y => -1 }, - { x => 1, y => 0 } - )); - } - } else { - # same y, 2 x west - if ($dx == 2) { - push(@possible,( - { x => 1, y => 0 } - )); - push(@possible,( - { x => 1, y => 1 }, - { x => 1, y => -1 } - )) if (USE_DIAGONAL == 1); - # same y, 1 x west - } else { - push(@possible,( - { x => 0, y => 1 }, - { x => 0, y => -1 }, - { x => -1, y => 0 } - )); - } - } - - } elsif ($my_pos->{y} > $target_pos->{y}) { - # 1 northeast - if ($my_pos->{x} > $target_pos->{x}) { - push(@possible,( - { x => -1, y => 0 }, - { x => 0, y => -1 } - )); - push(@possible,( - { x => -1, y => 1 }, - { x => 1, y => -1 } - )) if (USE_DIAGONAL == 1); - # 1 northwest - } else { - push(@possible,( - { x => 1, y => 0 }, - { x => 0, y => -1 } - )); - push(@possible,( - { x => 1, y => 1 }, - { x => -1, y => -1 } - )) if (USE_DIAGONAL == 1); - } - - } else { - # 1 southeast - if ($my_pos->{x} > $target_pos->{x}) { - push(@possible,( - { x => -1, y => 0 }, - { x => 0, y => 1 } - )); - push(@possible,( - { x => -1, y => -1 }, - { x => 1, y => 1 } - )) if (USE_DIAGONAL == 1); - # 1 southwest - } else { - push(@possible,( - { x => 1, y => 0 }, - { x => 0, y => 1 } - )); - push(@possible,( - { x => 1, y => -1 }, - { x => -1, y => 1 } - )) if (USE_DIAGONAL == 1); - } - } - - shuffleArray(\@possible); - - my %dance_pos; - foreach my $pos_add (@possible) { - my $x = $my_pos->{x} + $pos_add->{x}; - my $y = $my_pos->{y} + $pos_add->{y}; - next unless ($field->isWalkable($x, $y)); - - $dance_pos{x} = $x; - $dance_pos{y} = $y; - last; - } - - return \%dance_pos; -} - -######################################### -######################################### -### CATEGORY: Logging -######################################### -######################################### - -# TODO: merge? -sub itemLog { - my $crud = shift; - return if (!$config{'itemHistory'}); - open ITEMLOG, ">>:utf8", $Settings::item_log_file; - print ITEMLOG "[".getFormattedDate(int(time))."] $crud"; - close ITEMLOG; -} - -sub chatLog { - my $type = shift; - my $message = shift; - open CHAT, ">>:utf8", $Settings::chat_log_file; - print CHAT "[".getFormattedDate(int(time))."][".uc($type)."] $message"; - close CHAT; -} - -sub shopLog { - my $crud = shift; - open SHOPLOG, ">>:utf8", $Settings::shop_log_file; - print SHOPLOG "[".getFormattedDate(int(time))."] $crud"; - close SHOPLOG; -} - -sub monsterLog { - my $crud = shift; - return if (!$config{'monsterLog'}); - open MONLOG, ">>:utf8", $Settings::monster_log_file; - print MONLOG "[".getFormattedDate(int(time))."] $crud\n"; - close MONLOG; -} - -sub playerLog { - my $crud = shift; - return if (!$config{'playerLog'}); - open PLAYERLOG, ">>:utf8", $Settings::player_log_file; - print PLAYERLOG "[".getFormattedDate(int(time))."] $crud\n"; - close PLAYERLOG; -} - -sub deadLog { - my $crud = shift; - return if (!$config{'logDead'}); - open DEADLOG, ">>:utf8", $Settings::dead_log_file; - print DEADLOG "[DEAD] $crud\n"; - close DEADLOG; -} - -######################################### -######################################### -### CATEGORY: Operating system specific -######################################### -######################################### - - -## -# launchURL(url) -# -# Open $url in the operating system's preferred web browser. -sub launchURL { - my $url = shift; - - if ($^O eq 'MSWin32') { - require Utils::Win32; - Utils::Win32::ShellExecute(0, undef, $url); - - } else { - my $mod = 'use POSIX;'; - eval $mod; - - # This is a script I wrote for the autopackage project - # It autodetects the current desktop environment - my $detectionScript = <<EOF; - function detectDesktop() { - if [[ "\$DISPLAY" = "" ]]; then - return 1 - fi - - local LC_ALL=C - local clients - if ! clients=`xlsclients`; then - return 1 - fi - - if echo "\$clients" | grep -qE '(gnome-panel|nautilus|metacity)'; then - echo gnome - elif echo "\$clients" | grep -qE '(kicker|slicker|karamba|kwin)'; then - echo kde - else - echo other - fi - return 0 - } - detectDesktop -EOF - - my ($r, $w, $desktop); - - my $pid = IPC::Open2::open2($r, $w, '/bin/bash'); - print $w $detectionScript; - close $w; - $desktop = <$r>; - $desktop =~ s/\n//; - close $r; - waitpid($pid, 0); - - sub checkCommand { - foreach (split(/:/, $ENV{PATH})) { - return 1 if (-x "$_/$_[0]"); - } - return 0; - } - - if (checkCommand('xdg-open')) { - launchApp(1, 'xdg-open', $url); - - } elsif ($desktop eq "gnome" && checkCommand('gnome-open')) { - launchApp(1, 'gnome-open', $url); - - } elsif ($desktop eq "kde") { - launchApp(1, 'kfmclient', 'exec', $url); - - } else { - if (checkCommand('firefox')) { - launchApp(1, 'firefox', $url); - } elsif (checkCommand('mozilla')) { - launchApp(1, 'mozilla', $url); - } else { - $interface->errorDialog(TF("No suitable browser detected. Please launch your favorite browser and go to:\n%s", $url)); - } - } - } -} - - -####################################### -####################################### -### CATEGORY: Other functions -####################################### -####################################### - -# TODO: move actorAdded/Removed to Actor? -sub actorAddedRemovedVars { - my ($actor) = @_; - # returns (type, list, hash) - if ($actor->isa ('Actor::Item')) { - return ('item', \@itemsID, \%items); - } elsif ($actor->isa ('Actor::Player')) { - return ('player', \@playersID, \%players); - } elsif ($actor->isa ('Actor::Monster')) { - return ('monster', \@monstersID, \%monsters); - } elsif ($actor->isa ('Actor::Portal')) { - return ('portal', \@portalsID, \%portals); - } elsif ($actor->isa ('Actor::Pet')) { - return ('pet', \@petsID, \%pets); - } elsif ($actor->isa ('Actor::NPC')) { - return ('npc', \@npcsID, \%npcs); - } elsif ($actor->isa ('Actor::Slave')) { - return ('slave', \@slavesID, \%slaves); - } elsif ($actor->isa ('Actor::Elemental')) { - return ('elemental', \@elementalsID, \%elementals); - }else { - return (undef, undef, undef); - } -} - -sub actorAdded { - my (undef, $source, $arg) = @_; - my ($actor, $index) = @{$arg}; - - $actor->{binID} = $index; - - my ($type, $list, $hash) = actorAddedRemovedVars ($actor); - - if (defined $type) { - debug TF("Actor added: %s %s (%s), size %s\n", $type, (unpack 'V', $actor->{ID}), $actor->{binID}, $source->size), 'actorlist', 3; - - if (DEBUG && scalar(keys %{$hash}) + 1 != $source->size()) { - use Data::Dumper; - - my $ol = ''; - my $items = $source->getItems(); - foreach my $item (@{$items}) { - $ol .= $item->nameIdx . "\n"; - } - - die "$type: " . scalar(keys %{$hash}) . " + 1 != " . $source->size() . "\n" . - "List:\n" . - Dumper($list) . "\n" . - "Hash:\n" . - Dumper($hash) . "\n" . - "ObjectList:\n" . - $ol; - } - should(binSize($list) + 1, $source->size()) if DEBUG; - - binAdd($list, $actor->{ID}); - $hash->{$actor->{ID}} = $actor; - objectAdded($type, $actor->{ID}, $actor); - - should(scalar(keys %{$hash}), $source->size()) if DEBUG; - should(binSize($list), $source->size()) if DEBUG; - } else { - warning "Unknown actor type in actorAdded\n", 'actorlist' if DEBUG; - } -} - -sub actorRemoved { - my (undef, $source, $arg) = @_; - my ($actor, $index) = @{$arg}; - - my ($type, $list, $hash) = actorAddedRemovedVars ($actor); - - if (defined $type) { - debug TF("Actor removed: %s %s (%s), size %s\n", $type, (unpack 'V', $actor->{ID}), $actor->{binID}, $source->size), 'actorlist', 3; - - if (DEBUG && scalar(keys %{$hash}) - 1 != $source->size()) { - use Data::Dumper; - - my $ol = ''; - my $items = $source->getItems(); - foreach my $item (@{$items}) { - $ol .= $item->nameIdx . "\n"; - } - - die "$type:" . scalar(keys %{$hash}) . " - 1 != " . $source->size() . "\n" . - "List:\n" . - Dumper($list) . "\n" . - "Hash:\n" . - Dumper($hash) . "\n" . - "ObjectList:\n" . - $ol; - } - should(binSize($list) - 1, $source->size()) if DEBUG; - - binRemove($list, $actor->{ID}); - delete $hash->{$actor->{ID}}; - objectRemoved($type, $actor->{ID}, $actor); - - if ($type eq "player" && $venderLists{ID}) { - binRemove(\@venderListsID, $actor->{ID}); - delete $venderLists{$actor->{ID}}; - } - - if ($type eq "player" && $buyerLists{ID}) { - binRemove(\@buyerListsID, $actor->{ID}); - delete $buyerLists{$actor->{ID}}; - } - - should(scalar(keys %{$hash}), $source->size()) if DEBUG; - should(binSize($list), $source->size()) if DEBUG; - } else { - warning "Unknown actor type in actorRemoved\n", 'actorlist' if DEBUG; - } -} - -sub actorListClearing { - undef %items; - undef %players; - undef %monsters; - undef %portals; - undef %npcs; - undef %pets; - undef %slaves; - undef %elementals; - undef @itemsID; - undef @playersID; - undef @monstersID; - undef @portalsID; - undef @npcsID; - undef @petsID; - undef @slavesID; - undef @elementalsID; -} - -sub calcStat { - my $damage = shift; - $totaldmg += $damage; -} - -## -# center(string, width, [fill]) -# -# This function will center $string within a field $width characters wide, -# using $fill characters for padding on either end of the string for -# centering. If $fill is not specified, a space will be used. -sub center { - my ($string, $width, $fill) = @_; - - $fill ||= ' '; - my $left = int(($width - length($string)) / 2); - my $right = ($width - length($string)) - $left; - return $fill x $left . $string . $fill x $right; -} - -# Returns: 0 if user chose to quit, 1 if user chose a character, 2 if user created or deleted a character -sub charSelectScreen { - my %plugin_args = (autoLogin => shift); - # A list of character names - my @charNames; - # An array which maps an index in @charNames to an index in @chars - my @charNameIndices; - my $mode; - - # Check system version to delete a character - my $charDeleteVersion; - $charDeleteVersion = 1 if ($masterServer->{charBlockSize} >= 132); - - # the client also does this - $questList = {}; - - TOP: { - undef $mode; - @charNames = (); - @charNameIndices = (); - } - - for (my $num = 0; $num < @chars; $num++) { - next unless ($chars[$num] && %{$chars[$num]}); - if (0) { - # The old (more verbose) message - swrite( - T("------- Character \@< ---------\n" . - "Name: \@<<<<<<<<<<<<<<<<<<<<<<<<\n" . - "Job: \@<<<<<<< Job Exp: \@<<<<<<<\n" . - "Lv: \@<<<<<<< Str: \@<<<<<<<<\n" . - "J.Lv: \@<<<<<<< Agi: \@<<<<<<<<\n" . - "Exp: \@<<<<<<< Vit: \@<<<<<<<<\n" . - "HP: \@||||/\@|||| Int: \@<<<<<<<<\n" . - "SP: \@||||/\@|||| Dex: \@<<<<<<<<\n" . - "zeny: \@<<<<<<<<<< Luk: \@<<<<<<<<\n" . - "-------------------------------"), - $num, $chars[$num]{'name'}, $jobs_lut{$chars[$num]{'jobID'}}, $chars[$num]{'exp_job'}, - $chars[$num]{'lv'}, $chars[$num]{'str'}, $chars[$num]{'lv_job'}, $chars[$num]{'agi'}, - $chars[$num]{'exp'}, $chars[$num]{'vit'}, $chars[$num]{'hp'}, $chars[$num]{'hp_max'}, - $chars[$num]{'int'}, $chars[$num]{'sp'}, $chars[$num]{'sp_max'}, $chars[$num]{'dex'}, - $chars[$num]{'zeny'}, $chars[$num]{'luk'}); - } - - my $messageDeleteDate; - if ($chars[$num]{deleteDate}) { - if (int(time) > $chars[$num]{deleteDateTimestamp}) { - $messageDeleteDate = TF("\n -> Deleting is possible since %s.", $chars[$num]{deleteDate}); - } else { - $messageDeleteDate = TF("\n -> It will be deleted lefting %s!", $chars[$num]{deleteDate}); - } - } - - my $messageMapName; - - if (exists $chars[$num]{last_map} && $chars[$num]{last_map}) { - my $map_lut_key = $chars[$num]{last_map} . ".rsw"; - - if (exists $maps_lut{$map_lut_key} && length $maps_lut{$map_lut_key} <= 16) { - $messageMapName = sprintf(", %s", $maps_lut{$map_lut_key}); - } else { - $messageMapName = sprintf(", %s", $chars[$num]{last_map}); - } - } - - push @charNames, TF("Slot %d: %s (%s, %s, level %d/%d%s)%s", - $num, - $chars[$num]{name}, - $jobs_lut{$chars[$num]{'jobID'}}, - $sex_lut{$chars[$num]{sex}}, - $chars[$num]{lv}, - $chars[$num]{lv_job}, - $messageMapName, - $messageDeleteDate); - push @charNameIndices, $num; - } - - return 0 if (exists $charSvrSet{sync_Count} && $charSvrSet{sync_received_characters} < $charSvrSet{sync_Count}); - - if (@charNames) { - my $msg = center(T(" Character List "), 79, '-') ."\n"; - $msg .= join("\n", @charNames) ."\n"; - $msg .= ('-'x79) . "\n"; - message $msg, "connection"; - } - return 1 if ($net->clientAlive && $net->version); - - Plugins::callHook('charSelectScreen', \%plugin_args); - return $plugin_args{pin_return} if ($plugin_args{pin_return}); - return $plugin_args{return} if ($plugin_args{return}); - - if ($plugin_args{autoLogin} && @chars && $config{char} ne "" && $chars[$config{char}]) { - if ($chars[$config{char}]{deleteDate}) { - error TF("Cannot select character \"%s\" that requested for deletion.\n", $chars[$config{char}]{name}); - configModify("char",''); - relog(10); - return 0; - } else { - $messageSender->sendCharLogin($config{char}); - $timeout{charlogin}{time} = time; - return 1; - } - } - - my @choices = @charNames; - push @choices, T('Create a new character'); - if (@chars) { - if ($charDeleteVersion) { - push @choices, T('Delete or cancel the deletion a character'); - } else { - push @choices, T('Delete a character'); - } - } else { - message T("There are no characters on this account.\n"), "connection"; - if ($config{char} ne "switch" && defined($char)) { - message T("Please use the : \"conf char switch\" command, if you are switching your account.\n"), "connection"; - relog(10); - return 0; - } - } - - my $choice = $interface->showMenu( - T("Please choose a character or an action."), \@choices, - title => T("Character selection")); - if ($choice == -1) { - # User cancelled - quit(); - return 0; - - } elsif ($choice < @charNames) { - if ($chars[$charNameIndices[$choice]]{deleteDate}) { - error TF("Cannot select character \"%s\" that requested for deletion.\n", $chars[$charNameIndices[$choice]]{name}); - goto TOP; - } else { - # Character chosen - configModify('char', $charNameIndices[$choice], 1); - $messageSender->sendCharLogin($config{char}); - $timeout{charlogin}{time} = time; - return 1; - } - - } elsif ($choice == @charNames) { - # 'Create character' chosen - $mode = "create"; - - } else { - # 'Delete character' chosen - $mode = "delete"; - } - - if ($mode eq "create") { - while (1) { - my $message; - if ( $messageSender->{char_create_version} == 0x0A39 ) { - $message - = T( "Please enter the desired properties for your characters, in this form:\n" ) - . T( "(slot) \"(name)\" [ (hairstyle) [(haircolor)] ] [(job)] [(sex)]\n" ) - . T( "Job should be one of 'novice' or 'summoner' (default is 'novice').\n" ) - . T( "Sex should be one of 'M' or 'F' (default is 'F').\n" ); - } elsif ($messageSender->{char_create_version}) { - $message = T("Please enter the desired properties for your characters, in this form:\n" . - "(slot) \"(name)\" [ (hairstyle) [(haircolor)] ]"); - } else { - $message = T("Please enter the desired properties for your characters, in this form:\n" . - "(slot) \"(name)\" [ (str) (agi) (vit) (int) (dex) (luk) [ (hairstyle) [(haircolor)] ] ]"); - } - - my $input = $interface->query($message); - unless ($input =~ /\S/) { - goto TOP; - } else { - my @args = parseArgs($input); - if (@args < 2) { - $interface->errorDialog(T("You didn't specify enough parameters."), 0); - next; - } - - message TF("Creating character \"%s\" in slot \"%s\"...\n", $args[1], $args[0]), "connection"; - $timeout{charlogin}{time} = time; - last if (createCharacter(@args)); - } - } - - } elsif ($mode eq "delete") { - my $choice = $interface->showMenu( - T("Select the character you want to delete."), - \@charNames, - title => T("Delete character")); - if ($choice == -1) { - goto TOP; - } - my $charIndex = @charNameIndices[$choice]; - - if ($charDeleteVersion) { - $messageSender->{char_delete_slot} = $charIndex; - - if ($chars[$charIndex]{deleteDate}) { - my $confirm = $interface->showMenu( - TF("Are you ABSOLUTELY SURE you want to delete:\n%s", $charNames[$choice]), - [T("Back"), T("No, don't delete"), T("Yes, delete")], - title => T("Confirm delete")); - - if ($confirm == 0) { - goto TOP; - } elsif ($confirm == 1) { # Request cancel - $chars[$charIndex]{deleteDate} = undef; - $chars[$charIndex]{deleteDateTimestamp} = undef; - message TF("Canceling delete request for character %s...\n", $chars[$charIndex]{name}), "connection"; - $messageSender->sendCharDelete2Cancel($chars[$charIndex]{charID}); - } elsif ($confirm == 2 && int(time) > $chars[$charIndex]{deleteDateTimestamp}) { - my $code = $interface->query(T("Enter your birthdate, deletion code or e-mail.")); - if (!defined($code)) { - goto TOP; - } - - my $confirmation = $interface->showMenu( - TF("Are you ABSOLUTELY SURE you want to delete:\n%s", $charNames[$choice]), - [T("No, don't delete"), T("Yes, delete")], - title => T("Confirm delete")); - if ($confirmation != 1) { - goto TOP; - } - - $messageSender->sendCharDelete2Accept($chars[$charIndex]{charID}, $code); - message TF("Deleting character %s...\n", $chars[$charIndex]{name}), "connection"; - $AI::temp::delIndex = $charIndex; - $timeout{charlogin}{time} = time; - } else { - message TF("Character %s cannot be deleted yet. Please wait until %s\n", $chars[$charIndex]{name}, $chars[$charIndex]{deleteDate}), "info"; - goto TOP; - } - } else { - $messageSender->sendCharDelete2($chars[$charIndex]{charID}); # Request date deletion. - } - } else { - my $email = $interface->query("Enter your email address."); - if (!defined($email)) { - goto TOP; - } - - my $confirmation = $interface->showMenu( - TF("Are you ABSOLUTELY SURE you want to delete:\n%s", $charNames[$choice]), - [T("No, don't delete"), T("Yes, delete")], - title => T("Confirm delete")); - if ($confirmation != 1) { - goto TOP; - } - - $messageSender->sendCharDelete($chars[$charIndex]{charID}, $email); - message TF("Deleting character %s...\n", $chars[$charIndex]{name}), "connection"; - $AI::temp::delIndex = $charIndex; - $timeout{charlogin}{time} = time; - } - } - return 2; -} - -sub chatLog_clear { - if (-f $Settings::chat_log_file) { - unlink($Settings::chat_log_file); - } -} - -## -# checkAllowedMap($map) -# -# Checks whether $map is in $config{allowedMaps}. -# Disconnects if it is not, and $config{allowedMaps_reaction} != 0. -sub checkAllowedMap { - my $map = shift; - - return unless AI::state == AI::AUTO; - return unless $config{allowedMaps}; - return if existsInList($config{allowedMaps}, $map); - return if $config{allowedMaps_reaction} == 0; - - warning TF("The current map (%s) is not on the list of allowed maps.\n", $map); - main::chatLog("k", TF("** The current map (%s) is not on the list of allowed maps.\n", $map)); - main::chatLog("k", T("** Exiting...\n")); - quit(); -} - -## -# checkFollowMode() -# Returns: 1 if in follow mode, 0 if not. -# -# Check whether we're current in follow mode. -sub checkFollowMode { - my $followIndex; - if ($config{follow} && defined($followIndex = AI::findAction("follow"))) { - return 1 if (AI::args($followIndex)->{following}); - } - return 0; -} - -sub isMySlaveID { - my ($ID, $exclude) = @_; - return 0 if (defined $exclude && $ID eq $exclude); - return 0 unless ($char); - return 1 if (exists $char->{homunculus} && exists $char->{homunculus}{ID} && $char->{homunculus}{ID} eq $ID); - return 0 unless ($char->{slaves}); - return 0 unless (exists $char->{slaves}{$ID}); - return 1; -} - -sub isNotMySlaveID { - my ($ID) = @_; - return 0 if ($char && $char->{slaves} && exists $char->{slaves}{$ID}); - return 1; -} - -sub is_aggressive { - my ($monster, $control, $type, $party) = @_; - - my %plugin_args; - $plugin_args{monster} = $monster; - $plugin_args{control} = $control; - $plugin_args{type} = $type; - $plugin_args{party} = $party; - $plugin_args{return} = 0; - Plugins::callHook('ai_check_Aggressiveness' => \%plugin_args); - - if ( - ($plugin_args{return}) || - ($type == 2 && $control->{attack_auto} == 2) || - (($monster->{dmgToYou} || $monster->{missedYou} || $monster->{castOnToYou})) || - ($config{"attackAuto_considerDamagedAggressive"} && $monster->{dmgFromYou} > 0) || - ($party && - ( - $monster->{dmgToParty} - || $monster->{missedToParty} - || $monster->{dmgFromParty} - || scalar(grep { isMySlaveID($_) } keys %{$monster->{missedFromPlayer}}) - || scalar(grep { isMySlaveID($_) } keys %{$monster->{dmgFromPlayer}}) - || scalar(grep { isMySlaveID($_) } keys %{$monster->{castOnByPlayer}}) - || scalar(grep { isMySlaveID($_) } keys %{$monster->{missedToPlayer}}) - || scalar(grep { isMySlaveID($_) } keys %{$monster->{dmgToPlayer}}) - || scalar(grep { isMySlaveID($_) } keys %{$monster->{castOnToPlayer}}) - ) - ) - ) { - return 1; - } - return 0; -} - -sub is_aggressive_slave { - my ($slave, $monster, $control, $type, $party) = @_; - - my %plugin_args; - $plugin_args{slave} = $slave; - $plugin_args{monster} = $monster; - $plugin_args{control} = $control; - $plugin_args{type} = $type; - $plugin_args{return} = 0; - Plugins::callHook('ai_slave_check_Aggressiveness' => \%plugin_args); - - if ( - ($plugin_args{return}) || - ($type && ($control->{attack_auto} == 2)) || - ($monster->{dmgToPlayer}{$slave->{ID}} || $monster->{missedToPlayer}{$slave->{ID}} || $monster->{castOnToPlayer}{$slave->{ID}}) || - ($config{$slave->{configPrefix}."attackAuto_considerDamagedAggressive"} && $monster->{dmgFromPlayer}{$slave->{ID}} > 0) || - ($party && - ( - $monster->{missedFromYou} - || $monster->{dmgFromYou} - || $monster->{castOnByYou} - || $monster->{dmgToYou} - || $monster->{missedYou} - || $monster->{castOnToYou} - || scalar(grep { isMySlaveID($_, $slave->{ID}) } keys %{$monster->{missedFromPlayer}}) - || scalar(grep { isMySlaveID($_, $slave->{ID}) } keys %{$monster->{dmgFromPlayer}}) - || scalar(grep { isMySlaveID($_, $slave->{ID}) } keys %{$monster->{castOnByPlayer}}) - || scalar(grep { isMySlaveID($_, $slave->{ID}) } keys %{$monster->{missedToPlayer}}) - || scalar(grep { isMySlaveID($_, $slave->{ID}) } keys %{$monster->{dmgToPlayer}}) - || scalar(grep { isMySlaveID($_, $slave->{ID}) } keys %{$monster->{castOnToPlayer}}) - ) - ) - ) { - return 1; - } - return 0; -} - -## -# boolean checkMonsterCleanness(Bytes ID) -# ID: the monster's ID. -# Requires: $ID is a valid monster ID. -# -# Checks whether a monster is "clean" (not being attacked by anyone). -sub checkMonsterCleanness { - my ($ID) = @_; - return 1 if (!$config{attackAuto}); - return 1 if $playersList->getByID($ID) || $slavesList->getByID($ID); - my $monster = $monstersList->getByID($ID); - - # If party attacked monster, or if monster attacked/missed party - if ($config{attackAuto_party} && ($monster->{dmgFromParty} > 0 || $monster->{missedFromParty} > 0 || $monster->{dmgToParty} > 0 || $monster->{missedToParty} > 0)) { - return 1; - } - - if ( - scalar(grep { isMySlaveID($_) } keys %{$monster->{missedFromPlayer}}) - || scalar(grep { isMySlaveID($_) } keys %{$monster->{dmgFromPlayer}}) - || scalar(grep { isMySlaveID($_) } keys %{$monster->{castOnByPlayer}}) - || scalar(grep { isMySlaveID($_) } keys %{$monster->{missedToPlayer}}) - || scalar(grep { isMySlaveID($_) } keys %{$monster->{dmgToPlayer}}) - || scalar(grep { isMySlaveID($_) } keys %{$monster->{castOnToPlayer}}) - ) { - return 1; - } - - if ($config{aggressiveAntiKS}) { - # Aggressive anti-KS mode, for people who are paranoid about not kill stealing. - - # If we attacked the monster first, do not drop it, we are being KSed - return 1 if ($monster->{dmgFromYou} || $monster->{missedFromYou} || $monster->{castOnByYou}); - - # If others attacked the monster then always drop it, wether it attacked us or not! - if ( - scalar(grep { isNotMySlaveID($_) } keys %{$monster->{missedFromPlayer}}) - || scalar(grep { isNotMySlaveID($_) } keys %{$monster->{dmgFromPlayer}}) - || scalar(grep { isNotMySlaveID($_) } keys %{$monster->{castOnByPlayer}}) - || scalar(grep { isNotMySlaveID($_) } keys %{$monster->{missedToPlayer}}) - || scalar(grep { isNotMySlaveID($_) } keys %{$monster->{dmgToPlayer}}) - || scalar(grep { isNotMySlaveID($_) } keys %{$monster->{castOnToPlayer}}) - ) { - return 0; - } - } - - # If monster attacked/missed you - return 1 if ($monster->{'dmgToYou'} || $monster->{'missedYou'} || $monster->{'castOnToYou'}); - - # If we're in follow mode - if (defined(my $followIndex = AI::findAction("follow"))) { - my $following = AI::args($followIndex)->{following}; - my $followID = AI::args($followIndex)->{ID}; - - if ($following) { - # And master attacked monster, or the monster attacked/missed master - if ( - $monster->{dmgToPlayer}{$followID} > 0 - || $monster->{missedToPlayer}{$followID} > 0 - || $monster->{dmgFromPlayer}{$followID} > 0 - || $monster->{missedFromPlayer}{$followID} > 0 - || $monster->{castOnToPlayer}{$followID} > 0 - || $monster->{castOnByPlayer}{$followID} > 0 - ) { - return 1; - } - } - } - - if (objectInsideSpell($monster)) { - # Prohibit attacking this monster in the future - $monster->{dmgFromPlayer}{$char->{ID}} = 1; - return 0; - } - - if (objectInsideCasting($monster)) { - # Prohibit attacking this monster in the future - $monster->{dmgFromPlayer}{$char->{ID}} = 1; - return 0; - } - - #check party casting on mob - my $allowed = 1; - if (scalar(keys %{$monster->{castOnByPlayer}}) > 0) - { - foreach my $ID1 (keys %{$monster->{castOnByPlayer}}) - { - my $source = Actor::get($ID1); - unless ( - existsInList($config{tankersList}, $source->{name}) || - isMySlaveID($ID1) || - ($char->{party}{joined} && $char->{party}{users}{$ID1} && %{$char->{party}{users}{$ID1}}) - ) { - $allowed = 0; - last; - } - } - } - - if ( - $allowed - && scalar(grep { isNotMySlaveID($_) } keys %{$monster->{missedFromPlayer}}) == 0 - && scalar(grep { isNotMySlaveID($_) } keys %{$monster->{dmgFromPlayer}}) == 0 - && scalar(grep { isNotMySlaveID($_) } keys %{$monster->{missedToPlayer}}) == 0 - && scalar(grep { isNotMySlaveID($_) } keys %{$monster->{dmgToPlayer}}) == 0 - && scalar(grep { isNotMySlaveID($_) } keys %{$monster->{castOnToPlayer}}) == 0 - ) { - # The monster might be getting lured by another player. - # So we check whether it's walking towards any other player, but only - # if we haven't already attacked the monster. - if ($monster->{dmgFromYou} || $monster->{missedFromYou} || $monster->{castOnByYou}) { - return 1; - } else { - return !objectIsMovingTowardsPlayer($monster); - } - } - - # The monster didn't attack you. - # Other players attacked it, or it attacked other players. - if ($monster->{dmgFromYou} || $monster->{missedFromYou} || $monster->{castOnByYou}) { - # If you have already attacked the monster before, then consider it clean - return 1; - } - # If you haven't attacked the monster yet, it's unclean. - - return 0; -} - -sub slave_checkMonsterCleanness { - my ($slave, $ID) = @_; - return 1 if (!$config{$slave->{configPrefix}.'attackAuto'}); - return 1 if $playersList->getByID($ID) || $slavesList->getByID($ID); - my $monster = $monstersList->getByID($ID); - - # Since openKore considers the slave as a member of the player party we check for attacks against/made by master and/or other slaves - if ( - $config{$slave->{configPrefix}.'attackAuto_party'} && - ( - $monster->{dmgFromYou} - || $monster->{missedFromYou} - || $monster->{castOnByYou} - || $monster->{dmgToYou} - || $monster->{missedYou} - || $monster->{castOnToYou} - || scalar(grep { isMySlaveID($_, $slave->{ID}) } keys %{$monster->{missedFromPlayer}}) - || scalar(grep { isMySlaveID($_, $slave->{ID}) } keys %{$monster->{dmgFromPlayer}}) - || scalar(grep { isMySlaveID($_, $slave->{ID}) } keys %{$monster->{castOnByPlayer}}) - || scalar(grep { isMySlaveID($_, $slave->{ID}) } keys %{$monster->{missedToPlayer}}) - || scalar(grep { isMySlaveID($_, $slave->{ID}) } keys %{$monster->{dmgToPlayer}}) - || scalar(grep { isMySlaveID($_, $slave->{ID}) } keys %{$monster->{castOnToPlayer}}) - ) - ) { - return 1; - } - - if ($config{aggressiveAntiKS}) { - # Aggressive anti-KS mode, for people who are paranoid about not kill stealing. - - # If we attacked the monster first, do not drop it, we are being KSed - return 1 if ($monster->{dmgFromPlayer}{$slave->{ID}} || $monster->{missedFromPlayer}{$slave->{ID}} || $monster->{castOnByPlayer}{$slave->{ID}}); - - # If others attacked the monster then always drop it, wether it attacked us or not! - if ( - scalar(grep { isNotMySlaveID($_) } keys %{$monster->{missedFromPlayer}}) - || scalar(grep { isNotMySlaveID($_) } keys %{$monster->{dmgFromPlayer}}) - || scalar(grep { isNotMySlaveID($_) } keys %{$monster->{castOnByPlayer}}) - || scalar(grep { isNotMySlaveID($_) } keys %{$monster->{missedToPlayer}}) - || scalar(grep { isNotMySlaveID($_) } keys %{$monster->{dmgToPlayer}}) - || scalar(grep { isNotMySlaveID($_) } keys %{$monster->{castOnToPlayer}}) - ) { - return 0; - } - } - - # If monster attacked/missed you - return 1 if ($monster->{dmgToPlayer}{$slave->{ID}} || $monster->{missedToPlayer}{$slave->{ID}} || $monster->{castOnToPlayer}{$slave->{ID}}); - - if (objectInsideSpell($monster)) { - # Prohibit attacking this monster in the future - $monster->{dmgFromPlayer}{$char->{ID}} = 1; - return 0; - } - - if (objectInsideCasting($monster)) { - # Prohibit attacking this monster in the future - $monster->{dmgFromPlayer}{$char->{ID}} = 1; - return 0; - } - - #check party casting on mob - my $allowed = 1; - if (scalar(keys %{$monster->{castOnByPlayer}}) > 0) - { - foreach my $ID1 (keys %{$monster->{castOnByPlayer}}) - { - my $source = Actor::get($ID1); - unless ( - existsInList($config{tankersList}, $source->{name}) || - isMySlaveID($ID1, $slave->{ID}) || - ($char->{party}{joined} && $char->{party}{users}{$ID1} && %{$char->{party}{users}{$ID1}}) - ) { - $allowed = 0; - last; - } - } - } - - # If monster hasn't been attacked by other players - if ( - $allowed - && scalar(grep { isNotMySlaveID($_) } keys %{$monster->{missedFromPlayer}}) == 0 - && scalar(grep { isNotMySlaveID($_) } keys %{$monster->{dmgFromPlayer}}) == 0 - && scalar(grep { isNotMySlaveID($_) } keys %{$monster->{missedToPlayer}}) == 0 - && scalar(grep { isNotMySlaveID($_) } keys %{$monster->{dmgToPlayer}}) == 0 - && scalar(grep { isNotMySlaveID($_) } keys %{$monster->{castOnToPlayer}}) == 0 - ) { - # The monster might be getting lured by another player. - # So we check whether it's walking towards any other player, but only - # if we haven't already attacked the monster. - if ($monster->{dmgFromPlayer}{$slave->{ID}} || $monster->{missedFromPlayer}{$slave->{ID}} || $monster->{castOnByPlayer}{$slave->{ID}}) { - return 1; - } else { - return !objectIsMovingTowardsPlayer($monster); - } - } - - # The monster didn't attack you. - # Other players attacked it, or it attacked other players. - if ($monster->{dmgFromPlayer}{$slave->{ID}} || $monster->{missedFromPlayer}{$slave->{ID}} || $monster->{castOnByPlayer}{$slave->{ID}}) { - # If you have already attacked the monster before, then consider it clean - return 1; - } - # If you haven't attacked the monster yet, it's unclean. - - return 0; -} - -## -# boolean createCharacter(int slot, String name, int [str,agi,vit,int,dex,luk] = 5) -# slot: The slot in which to create the character (1st slot is 0). -# name: The name of the character to create. -# Returns: Whether the parameters are correct. Only a character creation command -# will be sent to the server if all parameters are correct. -# -# Create a new character. You must be currently connected to the character login server. -# -# Observation: From the RagexeRE_2012_03_07f, are no longer the chosen artributos when -# selecting the character! -sub createCharacter { - my $slot = shift; - my $name = shift; - - if ($net->getState() != 3 && $net->getState() != 1.5) { - $interface->errorDialog(T("We're not currently connected to the character login server."), 0); - return 0; - } elsif ($slot !~ /^\d+$/) { - $interface->errorDialog(TF("Slot \"%s\" is not a valid number.", $slot), 0); - return 0; - } elsif (exists $charSvrSet{total_slot} && ($slot < 0 || $slot > $charSvrSet{total_slot})) { - $interface->errorDialog(TF("The slot must be comprised between 0 and %s.", $charSvrSet{total_slot}), 0); - return 0; - } elsif (exists $charSvrSet{normal_slot} && ($slot < 0 || $slot > $charSvrSet{normal_slot})) { - $interface->errorDialog(TF("The slot must be comprised between 0 and %s.", $charSvrSet{normal_slot}), 0); - return 0; - } elsif ($chars[$slot]) { - $interface->errorDialog(TF("Slot %s already contains a character (%s).", $slot, $chars[$slot]{name}), 0); - return 0; - } elsif (length($name) > 23) { - $interface->errorDialog(T("Name must not be longer than 23 characters."), 0); - return 0; - } - - if ( $messageSender->{char_create_version} == 0x0A39 ) { - my $hair_style = shift if @_ && $_[0] =~ /^\d+$/; - my $hair_color = shift if @_ && $_[0] =~ /^\d+$/; - - if ( grep { !/^(novice|summoner|male|female|m|f)$/io } @_ ) { - $interface->errorDialog( T( 'Unknown job or sex.' ), 0 ); - return 0; - } - - my $job_id = scalar( grep {/^summoner$/io} @_ ) ? 4218 : 0; - my $sex = scalar( grep {/^male|m$/io} @_ ) ? 1 : 0; - - $messageSender->sendCharCreate( $slot, $name, $hair_style, $hair_color, $job_id, $sex ); - } elsif ($messageSender->{char_create_version}) { - my ($hair_style, $hair_color) = @_; - - $messageSender->sendCharCreate($slot, $name, - $hair_style, $hair_color); - } else { - my ($str, $agi, $vit, $int, $dex, $luk, $hair_style, $hair_color) = @_; - - if (!@_) { - ($str, $agi, $vit, $int, $dex, $luk) = (5, 5, 5, 5, 5, 5); - } - - for ($str, $agi, $vit, $int, $dex, $luk) { - if ($_ > 9 || $_ < 1) { - $interface->errorDialog(T("Stats must be comprised between 1 and 9."), 0); - return 0; - } - } - - for ($str+$int, $agi+$luk, $vit+$dex) { - if ($_ != 10) { - $interface->errorDialog(T("The sums Str + Int, Agi + Luk and Vit + Dex must all be equal to 10."), 0); - return 0; - } - } - - $messageSender->sendCharCreate($slot, $name, - $str, $agi, $vit, $int, $dex, $luk, - $hair_style, $hair_color); - } - - return 1; -} - -## -# void deal(Actor::Player player) -# Requires: defined($player) -# Ensures: exists $outgoingDeal{ID} -# -# Sends $player a deal request. -sub deal { - my $player = $_[0]; - assert(defined $player, "Can't deal with undef player") if DEBUG; - assertClass($player, 'Actor::Player') if DEBUG; - - $outgoingDeal{ID} = $player->{ID}; - $messageSender->sendDeal($player->{ID}); -} - -## -# dealAddItem($item, $amount) -# -# Adds $amount of $item to the current deal. -sub dealAddItem { - my ($item, $amount) = @_; - - $messageSender->sendDealAddItem($item->{ID}, $amount); - $currentDeal{lastItemAmount} = $amount; -} - -## -# drop(itemIndex, amount) -# -# Drops $amount of the item specified by $itemIndex. If $amount is not specified or too large, it defaults -# to the number of items you have. -sub drop { - my ($itemIndex, $amount) = @_; - my $item = $char->inventory->get($itemIndex); - if ($item) { - if (!$amount || $amount > $item->{amount}) { - $amount = $item->{amount}; - } - $messageSender->sendDrop($item->{ID}, $amount); - } -} - -sub dumpData { - my $msg = shift; - my $silent = shift; - my $desc = shift; - my $dump; - my $puncations = quotemeta '~!@#$%^&*()_+|\"\''; - my $messageID = uc(unpack("H2", substr($msg, 1, 1))) . uc(unpack("H2", substr($msg, 0, 1))); - - $dump = "\n\n================================================\n" . - getFormattedDate(int(time)) . "\n\n" . - ($desc == 1 ? 'Send ' : 'Recv ') . #0 = Recv (default), 1 = Send - $messageID . ' [' . - length($msg) . " bytes]\n\n"; - - for (my $i = 0; $i < length($msg); $i += 16) { - my $line; - my $data = substr($msg, $i, 16); - my $rawData = ''; - - for (my $j = 0; $j < length($data); $j++) { - my $char = substr($data, $j, 1); - - if (($char =~ /\W/ && $char =~ /\S/ && !($char =~ /[$puncations]/)) - || ($char eq chr(10) || $char eq chr(13) || $char eq "\t")) { - $rawData .= '.'; - } else { - $rawData .= substr($data, $j, 1); - } - } - - $line = getHex(substr($data, 0, 8)); - $line .= ' ' . getHex(substr($data, 8)) if (length($data) > 8); - - $line .= ' ' x (50 - length($line)) if (length($line) < 54); - $line .= " $rawData\n"; - $line = sprintf("%3d> ", $i) . $line; - $dump .= $line; - } - - open DUMP, ">> DUMP.txt"; - print DUMP $dump; - close DUMP; - - debug "$dump\n", "parseMsg", 2; - message T("Message Dumped into DUMP.txt!\n"), undef, 1 unless ($silent); -} - -sub getEmotionByCommand { - my $command = shift; - foreach (keys %emotions_lut) { - if (existsInList($emotions_lut{$_}{command}, $command)) { - return $_; - } - } - return undef; -} - -sub getIDFromChat { - my $r_hash = shift; - my $msg_user = shift; - my $match_text = shift; - my $qm; - if ($match_text !~ /\w+/ || $match_text eq "me" || $match_text eq "") { - foreach (keys %{$r_hash}) { - next if ($_ eq ""); - if ($msg_user eq $r_hash->{$_}{name}) { - return $_; - } - } - } else { - foreach (keys %{$r_hash}) { - next if ($_ eq ""); - $qm = quotemeta $match_text; - if ($r_hash->{$_}{name} =~ /$qm/i) { - return $_; - } - } - } - return undef; -} - -## -# getNPCName(ID) -# ID: the packed ID of the NPC -# Returns: the name of the NPC -# -# Find the name of an NPC: could be NPC, monster, or unknown. -sub getNPCName { - my $ID = shift; - if ((my $npc = $npcsList->getByID($ID))) { - return $npc->name; - } elsif ((my $monster = $monstersList->getByID($ID))) { - return $monster->name; - } else { - return T("Unknown #") . unpack("V1", $ID); - } -} - -## -# getPlayerNameFromCache(player) -# player: an Actor::Player object. -# Returns: 1 on success, 0 if the player isn't in cache. -# -# Retrieve a player's name from cache and modify the player object. -sub getPlayerNameFromCache { - my ($player) = @_; - - return if (!$config{cachePlayerNames}); - my $entry = $playerNameCache{$player->{ID}}; - return if (!$entry); - - # Check whether the cache entry is too old or inconsistent. - # Default cache life time: 15 minutes. - if (timeOut($entry->{time}, $config{cachePlayerNames_duration}) || $player->{lv} != $entry->{lv} || $player->{jobID} != $entry->{jobID}) { - binRemove(\@playerNameCacheIDs, $player->{ID}); - delete $playerNameCache{$player->{ID}}; - compactArray(\@playerNameCacheIDs); - return 0; - } - - $player->{name} = $entry->{name}; - $player->{guild} = $entry->{guild} if ($entry->{guild}); - return 1; -} - -sub getPortalDestName { - my $ID = shift; - my %hash; # We only want unique names, so we use a hash - foreach (keys %{$portals_lut{$ID}{'dest'}}) { - my $key = $portals_lut{$ID}{'dest'}{$_}{'map'}; - $hash{$key} = 1; - } - - my @destinations = sort keys %hash; - return join('/', @destinations); -} - -sub getResponse { - my $type = quotemeta shift; - - my @keys; - foreach my $key (keys %responses) { - if ($key =~ /^$type\_\d+$/) { - push @keys, $key; - } - } - - my $msg = $responses{$keys[int(rand(@keys))]}; - $msg =~ s/\%\$(\w+)/$responseVars{$1}/eig; - return $msg; -} - -sub getSpellName { - my $spell = shift; - return $spells_lut{$spell} || "Unknown $spell"; -} - -## -# inInventory($itemName, $quantity = 1) -# -# Returns the item's index (can be 0!) if you have at least $quantity units of the item -# specified by $itemName in your inventory. -# Returns nothing otherwise. -sub inInventory { - my ($itemIndex, $quantity) = @_; - $quantity ||= 1; - - my $item = $char->inventory->getByName($itemIndex); - return if !$item; - return unless $item->{amount} >= $quantity; - return $item->{binID}; -} - -## -# inventoryItemRemoved($binID, $amount) -# -# Removes $amount of $binID from $char->{inventory}. -# Also prints a message saying the item was removed (unless it is an arrow you -# fired). -sub inventoryItemRemoved { - my ($binID, $amount) = @_; - - return if $amount == 0; - my $item = $char->inventory->get($binID); - if (!$char->{arrow} || ($item && $char->{arrow} ne $item->{ID})) { - # This item is not an equipped arrow - message TF("Inventory Item Removed: %s (%d) x %d\n", $item->{name}, $binID, $amount), "inventory"; - } - $item->{amount} -= $amount; - if ($item->{amount} <= 0) { - if ($char->{arrow} && $char->{arrow} eq $item->{ID}) { - message TF("Run out of Arrow/Bullet: %s (%d)\n", $item->{name}, $binID), "inventory"; - delete $char->{equipment}{arrow}; - delete $char->{arrow}; - } - if( defined $item->{'name'}) { - $char->inventory->remove($item); - } else { - warning "Server sended item_removed but we not have this item anymore. binID: ".$binID."\n"; - } - } - $itemChange{$item->{name}} -= $amount; - Plugins::callHook('inventory_item_removed', { - item => $item, - index => $binID, - amount => $amount, - remaining => ($item->{amount} <= 0 ? 0 : $item->{amount}) - }); -} - -## -# storageItemRemoved($binID, $amount) -# -# Removes $amount of $binID from $char->{storage}. -# Also prints a message saying the item was removed -sub storageItemRemoved { - my ($binID, $amount) = @_; - - return if $amount == 0; - my $item = $char->storage->get($binID); - message TF("Storage Item Removed: %s (%d) x %s\n", $item->{name}, $binID, $amount), "storage"; - $item->{amount} -= $amount; - if ($item->{amount} <= 0) { - $char->storage->remove($item); - } - $itemChange{$item->{name}} -= $amount; - Plugins::callHook('storage_item_removed', { - item => $item, - index => $binID, - amount => $amount, - remaining => ($item->{amount} <= 0 ? 0 : $item->{amount}) - }); -} - -## -# cartItemRemoved($binID, $amount) -# -# Removes $amount of $binID from $char->{cart}. -# Also prints a message saying the item was removed -sub cartItemRemoved { - my ($binID, $amount) = @_; - - return if $amount == 0; - my $item = $char->cart->get($binID); - message TF("Cart Item Removed: %s (%d) x %s\n", $item->{name}, $binID, $amount), "cart"; - $item->{amount} -= $amount; - if ($item->{amount} <= 0) { - $char->cart->remove($item); - } - $itemChange{$item->{name}} -= $amount; - Plugins::callHook('cart_item_removed', { - item => $item, - index => $binID, - amount => $amount, - remaining => ($item->{amount} <= 0 ? 0 : $item->{amount}) - }); -} - -# Resolve the name of a card -sub cardName { - my $cardID = shift; - - # If card name is unknown, just return ?number - my $card = $items_lut{$cardID}; - return "?$cardID" if !$card; - $card =~ s/ Card$//; - return $card; -} - -# Resolve the name of a monster -# This function will only look at the data in monsters.txt -# DO NOT USE THIS FUNCTION when you want to get the real name of a monster, -# servers can change this name internally use getNPCName instead. -sub monsterName { - my $ID = shift; - return 'Unknown' unless defined($ID); - return 'None' unless $ID; - return $monsters_lut{$ID} || "Unknown #$ID"; -} - -# Resolve the name of a simple item -sub itemNameSimple { - my $ID = shift; - return T("Unknown") unless defined($ID); - return T("None") unless $ID; - return $items_lut{$ID} || T("Unknown #")."$ID"; -} - -## -# itemName($item) -# -# Resolve the name of an item. $item should be a hash with these keys: -# nameID => integer index into %items_lut -# cards => 8-byte binary data as sent by server -# upgrade => integer upgrade level -sub itemName { - my $item = shift; - - my $name = itemNameSimple($item->{nameID}); - - # Resolve item prefix/suffix (carded or forged) - my $prefix = ""; - my $suffix = ""; - my @cards; - my %cards; - - my $item_len = length($item); - my $card_unpack; - - # FIXME WORKAROUND TO ITEMID 4BYTES - if ($masterServer->{itemListType}) { - $card_unpack = "V"; - } else { - $card_unpack = "v"; - } - - my $card_len = length pack $card_unpack; - for (my $i = 0; $i < 4; $i++) { - my $card = unpack($card_unpack, substr($item->{cards}, $i*$card_len, $card_len)); - next unless $card; - push(@cards, $card); - ($cards{$card} ||= 0) += 1; - } - if ($cards[0] == 254) { - # Alchemist-made potion - # - # Ignore the "cards" inside. - } elsif ($cards[0] == 65280 || $cards[0] == 1) { - # Pet egg - # cards[0] == 65280 - # substr($item->{cards}, 2, 4) = packed pet ID - # cards[3] == 1 if named, 0 if not named - - } elsif ($cards[0] == 255) { - # Forged weapon - # - # Display e.g. "VVS Earth" or "Fire" - my $elementID = $cards[1] % 10; - my $elementName = $elements_lut{$elementID}; - my $starCrumbs = ($cards[1] >> 8) / 5; - - # Translation-friendly - if ($starCrumbs == 1) { - $prefix .= T("VS "); - } elsif ($starCrumbs == 2) { - $prefix .= T("VVS "); - } elsif ($starCrumbs == 3) { - $prefix .= T("VVVS "); - } - - # $prefix .= "$elementName " if ($elementName ne ""); - $suffix = "$elementName" if ($elementName ne ""); - } elsif (@cards) { - # Carded item - # - # List cards in alphabetical order. - # Stack identical cards. - # e.g. "Hydra*2,Mummy*2", "Hydra*3,Mummy" - $suffix = join(':', map { - cardName($_).($cards{$_} > 1 ? "*$cards{$_}" : '') - } sort { cardName($a) cmp cardName($b) } keys %cards); - } - - my $total_options = 0; - - my @options = grep { $_->{type} } map { my @c = unpack 'vvC', $_;{ type => $c[0], value => $c[1], param => $c[2] } } unpack '(a5)*', $item->{options} || ''; - foreach ( @options ) { - if ( $_->{type} ) { - $total_options++; - } - } - - my $numSlots = $itemSlotCount_lut{$item->{nameID}} if ($prefix eq ""); - - my $display = ""; - $display .= T("BROKEN ") if $item->{broken}; - $display .= "+$item->{upgrade} " if $item->{upgrade}; - $display .= $prefix if $prefix; - $display .= $name; - $display .= " [$suffix]" if $suffix; - $display .= " [$numSlots]" if $numSlots; - $display .= " [$total_options Option]" if $total_options; - - return $display; -} - -sub itemNameToID { - my $itemName = lc shift; - return if !$itemName; - $itemName =~ s/^[\t\s]*//; # Remove leading tabs and whitespace - $itemName =~ s/\s+$//g; # Remove trailing whitespace - for my $hashID (keys %items_lut) { - if ($itemName eq lc($items_lut{$hashID})) { - return $hashID; - } - } -} - -sub itemNameToIDList { - my $itemName = lc shift; - return if !$itemName; - $itemName =~ s/^[\t\s]*//; # Remove leading tabs and whitespace - $itemName =~ s/\s+$//g; # Remove trailing whitespace - - my @id_list; - - for my $hashID (keys %items_lut) { - if ($itemName eq lc($items_lut{$hashID})) { - push @id_list, $hashID; - } - } - - return @id_list; -} - -sub containsItemNameToIDList { - my $itemName = lc shift; - return if !$itemName; - $itemName =~ s/^[\t\s]*//; # Remove leading tabs and whitespace - $itemName =~ s/\s+$//g; # Remove trailing whitespace - - my @id_list; - - for my $hashID (keys %items_lut) { - if (index(lc($items_lut{$hashID}), $itemName) != -1) { - push @id_list, $hashID; - } - } - - return @id_list; -} - -## -# DEPRECATED: Use transferItems() instead. -# -# storageGet(items, amount) -# items: reference to an array of storage item hashes. -# amount: the maximum amount to get, for each item, or 0 for unlimited. -# -# Get one or more items from storage. -# -# Example: -# # Get items $a and $b from storage. -# storageGet([$a, $b]); -# # Get items $a and $b from storage, but at most 30 of each item. -# storageGet([$a, $b], 30); -sub storageGet { - my ( $items, $amount ) = @_; - transferItems( $items, $amount, 'storage' => 'inventory' ); -} - -## -# transferItems(items, amount, source, target) -# items: reference to an array of Actor::Items. -# amount: the maximum amount to get, for each item, or 0 for unlimited. -# source: where the items come from; one of 'inventory', 'storage', 'cart' -# target: where the items should go; one of 'inventory', 'storage', 'cart' -# -# Transfer one or more items from their current location to another location. -# -# Example: -# # Get items $a and $b from storage to inventory. -# transferItems([$a, $b], 'storage' => 'inventory'); -# # Send items $a and $b from cart to storage, but at most 30 of each item. -# transferItems([$a, $b], 30, 'cart' => 'storage'); -sub transferItems { - my ( $items, $amount, $source, $target ) = @_; - - AI::queue( - transferItems => { - timeout => $timeout{ai_transfer_items}{timeout} || 0.15, - items => [ map { { item => $_, source => $source, target => $target, amount => $amount } } @$items ], - } - ); - - # Immediately run the AI sequence once. This will remove it from the queue if there's only one item to transfer. - AI::CoreLogic::processTransferItems(); -} - -## -# headgearName(lookID) -# -# Resolves a lookID of a headgear into a human readable string. -# -# A lookID corresponds to a line number in tables/headgears.txt. -# The number on that line is the itemID for the headgear. -sub headgearName { - my ($lookID) = @_; - - return T("Nothing") if $lookID == 0; - - my $itemID = $headgears_lut[$lookID]; - - if (!$itemID or $itemID =~ /^#/) { - warning TF("Unknown lookID_%d. Need to update the file headgears.txt (from data.grf)\n", $lookID); - return T("Unknown lookID_") . $lookID; - } - - warning TF("Unknown item (ID=%d). Need to update the file items.txt or headgears.txt (from data.grf)\n", $itemID) unless $items_lut{$itemID}; - return main::itemName({nameID => $itemID}); -} - -## -# void initUserSeed() -# -# Generate a unique seed for the current user and save it to -# a file, or load the seed from that file if it exists. -=pod -sub initUserSeed { - my $seedFile = "$Settings::logs_folder/seed.txt"; - my $f; - - if (-f $seedFile) { - if (open($f, "<", $seedFile)) { - binmode $f; - $userSeed = <$f>; - $userSeed =~ s/\n.*//s; - close($f); - } else { - $userSeed = '0'; - } - } else { - $userSeed = ''; - for (0..10) { - $userSeed .= rand(2 ** 49); - } - - if (open($f, ">", $seedFile)) { - binmode $f; - print $f $userSeed; - close($f); - } - } -} -=cut - -sub itemLog_clear { - if (-f $Settings::item_log_file) { unlink($Settings::item_log_file); } -} - -## -# look(bodydir, [headdir]) -# bodydir: a number 0-7. See directions.txt. -# headdir: 0 = look directly, 1 = look right, 2 = look left -# -# Look in the given directions. -sub look { - my %args = ( - look_body => shift, - look_head => shift - ); - AI::queue("look", \%args); -} - -## -# lookAtPosition(pos, [headdir]) -# pos: a reference to a coordinate hash. -# headdir: 0 = face directly, 1 = look right, 2 = look left -# -# Turn face and body direction to position %pos. -sub lookAtPosition { - my $pos2 = shift; - my $headdir = shift; - my %vec; - my $direction; - - getVector(\%vec, $pos2, $char->{pos_to}); - $direction = int(sprintf("%.0f", (360 - vectorToDegree(\%vec)) / 45)) % 8; - look($direction, $headdir); -} - -## -# getNaturalLookDirections(from_pos, to_pos, [current_body]) -# from_pos: source position hashref (character) -# to_pos: target position hashref -# current_body: optional current body direction, defaults to $char->{look}{body} -# -# Calculates look change using partial-turn strategy. -# Returns: (body, head) where body is 0-7 and head is 0-2. -sub getNaturalLookDirections { - my ($from_pos, $to_pos, $current_body) = @_; - return unless ($from_pos && $to_pos); - - $current_body = $char->{look}{body} unless defined $current_body; - - my %vec; - getVector(\%vec, $to_pos, $from_pos); - my $target_body = int(sprintf("%.0f", (360 - vectorToDegree(\%vec)) / 45)) % 8; - - my $body = $current_body; - my $head = 0; - my $offset = ($target_body - $body + 8) % 8; - $offset -= 8 if ($offset > 4); - - if ($offset != 0) { - if (abs($offset) <= 1) { - $head = $offset > 0 ? 2 : 1; - } else { - my $step = $offset > 0 ? 1 : -1; - $body = ($target_body - $step + 8) % 8; - $head = $step > 0 ? 2 : 1; - } - } - - return ($body, $head); -} - -## -# lookAtPositionNaturally(from_pos, to_pos, [current_body]) -# from_pos: source position hashref (character) -# to_pos: target position hashref -# current_body: optional current body direction, defaults to $char->{look}{body} -# -# Calculates and executes look change using partial-turn strategy. -# Returns: (body, head) where body is 0-7 and head is 0-2. -sub lookAtPositionNaturally { - my ($from_pos, $to_pos, $current_body) = @_; - my ($body, $head) = getNaturalLookDirections($from_pos, $to_pos, $current_body); - return unless defined $body; - - look($body, $head) if ($body != $char->{look}{body} || $head != $char->{look}{head}); - return ($body, $head); -} - -## -# getClosestWalls(from_pos, wall_range, [field_obj]) -# from_pos: source position hashref -# wall_range: search range around from_pos -# field_obj: optional field object, defaults to current global field -# -# Returns: arrayref with all nearest non-walkable cells found in range. -sub getClosestWalls { - my ($from_pos, $wall_range, $field_obj) = @_; - return [] unless ($from_pos && defined $wall_range && $wall_range > 0); - - $field_obj ||= $field; - return [] unless $field_obj; - - my $closest_distance; - my @closest_walls; - for (my $dx = -$wall_range; $dx <= $wall_range; $dx++) { - for (my $dy = -$wall_range; $dy <= $wall_range; $dy++) { - next if $dx == 0 && $dy == 0; - - my $candidate = { - x => $from_pos->{x} + $dx, - y => $from_pos->{y} + $dy, - }; - next if $field_obj->isWalkable($candidate->{x}, $candidate->{y}); - - my $distance = distance($candidate, $from_pos); - if (!defined $closest_distance || $distance < $closest_distance) { - $closest_distance = $distance; - @closest_walls = ($candidate); - } elsif ($distance == $closest_distance) { - push @closest_walls, $candidate; - } - } - } - - return \@closest_walls; -} - -## -# manualMove(dx, dy) -# -# Moves the character offset from its current position. -sub manualMove { - my ($dx, $dy) = @_; - - # Stop following if necessary - if ($config{'follow'}) { - configModify('follow', 0); - AI::clear('follow'); - } - - # Stop moving if necessary - AI::clear(qw/move route mapRoute/); - main::ai_route($field->baseName, $char->{pos_to}{x} + $dx, $char->{pos_to}{y} + $dy); -} - -## -# meetingPosition(actor, actorType, target_actor, attackMaxDistance, runFromTargetActive) -# actor: current object. -# actorType: 1 - char | 2 - slave -# target_actor: actor to meet. -# attackMaxDistance: attack distance based on attack method. -# runFromTargetActive: Wheter meetingPosition was called by a runFromTarget check, if 2 then use runFromTarget_noAttackMethodFallback values -# -# Returns: the position where the character should go to meet a moving monster. -sub meetingPosition { - my ($actor, $actorType, $target, $attackMaxDistance, $runFromTargetActive) = @_; - - if ($attackMaxDistance < 1) { - error "attackMaxDistance must be positive ($attackMaxDistance).\n"; - return; - } - - my $extra_time_actor = $timeout{'meetingPosition_extra_time_actor'}{'timeout'} ? $timeout{'meetingPosition_extra_time_actor'}{'timeout'} : 0.2; - my $extra_time_target = $timeout{'meetingPosition_extra_time_target'}{'timeout'} ? $timeout{'meetingPosition_extra_time_target'}{'timeout'} : 0.2; - - my $mySpeed = ($actor->{walk_speed} || 0.12); - my $timeSinceActorMoved = time - $actor->{time_move} + $extra_time_actor; - - my $my_solution; - my $timeActorFinishMove; - - my $attackRouteMaxPathDistance; - my $attackCanSnipe; - my $followDistanceMax; - my $master; - my $masterPos; - my $runFromTarget; - my $runFromTarget_dist; - my $runFromTarget_minStep; - my $runFromTarget_maxPathDistance; - - # actor is char - if ($actorType == 1) { - $attackRouteMaxPathDistance = $config{attackRouteMaxPathDistance} || 13; - $runFromTarget_maxPathDistance = $config{runFromTarget_maxPathDistance} || 13; - $runFromTarget = $config{runFromTarget}; - $runFromTarget_dist = $config{runFromTarget_dist}; - if ($runFromTargetActive == 2) { - $runFromTarget_minStep = $config{runFromTarget_noAttackMethodFallback_minStep}; - } else { - $runFromTarget_minStep = $config{runFromTarget_minStep}; - } - $followDistanceMax = $config{followDistanceMax}; - $attackCanSnipe = $config{attackCanSnipe}; - if ($config{follow}) { - foreach (keys %players) { - if ($players{$_}{name} eq $config{followTarget}) { - $master = $players{$_}; - last; - } - } - if ($master) { - $masterPos = 1; - } - } - - # If the actor is the character then we should have already saved the time calc and solution at Receive.pm::character_moves - $my_solution = $char->{solution}; - $timeActorFinishMove = $char->{time_move_calc}; - - # actor is a slave - } elsif ($actorType == 2) { - $attackRouteMaxPathDistance = $config{$actor->{configPrefix}.'attackRouteMaxPathDistance'} || 20; - $runFromTarget_maxPathDistance = $config{$actor->{configPrefix}.'runFromTarget_maxPathDistance'} || 20; - $runFromTarget = $config{$actor->{configPrefix}.'runFromTarget'}; - $runFromTarget_dist = $config{$actor->{configPrefix}.'runFromTarget_dist'}; - if ($runFromTargetActive == 2) { - $runFromTarget_minStep = $config{$actor->{configPrefix}.'runFromTarget_noAttackMethodFallback_minStep'}; - } else { - $runFromTarget_minStep = $config{$actor->{configPrefix}.'runFromTarget_minStep'}; - } - $followDistanceMax = $config{$actor->{configPrefix}.'followDistanceMax'}; - $attackCanSnipe = $config{$actor->{configPrefix}.'attackCanSnipe'}; - $master = $char; - $masterPos = 1; - - $my_solution = get_solution($field, $actor->{pos}, $actor->{pos_to}); - $timeActorFinishMove = calcTimeFromSolution($my_solution, $mySpeed); - } - - my $realMyPos; - # Actor has finished moving and is at PosTo - if ($timeSinceActorMoved >= $timeActorFinishMove) { - $realMyPos = $actor->{pos_to}; - - # Actor is currently moving - } else { - my $steps_walked = calcStepsWalkedFromTimeAndSolution($my_solution, $mySpeed, $timeSinceActorMoved); - $realMyPos = $my_solution->[$steps_walked]; - } - - # Should never happen - unless ($field->isWalkable($realMyPos->{x}, $realMyPos->{y})) { - $realMyPos = $field->closestWalkableSpot($realMyPos, 1); - } - - my $targetSpeed = ($target->{walk_speed} || 0.12); - my $timeSinceTargetMoved = time - $target->{time_move} + $extra_time_target; - - my $target_solution = get_solution($field, $target->{pos}, $target->{pos_to}); - - # Calculate the time target will need to finish moving from pos to pos_to - my $timeTargetFinishMove = calcTimeFromSolution($target_solution, $targetSpeed); - - my $realTargetPos; - my $targetTotalSteps; - my $targetCurrentStep; - - my @target_pos_to_check; - - # Target has finished moving - if ($timeSinceTargetMoved >= $timeTargetFinishMove) { - $realTargetPos = $target->{pos_to}; - $target_pos_to_check[0] = { - targetPosInStep => $realTargetPos - }; - - # Target is currently moving - } else { - $targetTotalSteps = $#{$target_solution}; - $targetCurrentStep = calcStepsWalkedFromTimeAndSolution($target_solution, $targetSpeed, $timeSinceTargetMoved); - $realTargetPos = $target_solution->[$targetCurrentStep]; - - my $steps_count = 0; - foreach my $currentStep ($targetCurrentStep..$targetTotalSteps) { - $target_pos_to_check[$steps_count] = { - targetPosInStep => $target_solution->[$currentStep] - }; - } continue { - $steps_count++; - } - } - - my $master_moving; - my $master_solution; - my $timeSinceMasterMoved; - my $realMasterPos; - my $masterSpeed; - if ($masterPos) { - $masterSpeed = ($master->{walk_speed} || 0.12); - $timeSinceMasterMoved = time - $master->{time_move} + $extra_time_actor; - - $master_solution = get_solution($field, $master->{pos}, $master->{pos_to}); - - # Calculate the time master will need to finish moving from pos to pos_to - my $timeMasterFinishMove = calcTimeFromSolution($master_solution, $masterSpeed); - - # master has finished moving - if ($timeSinceMasterMoved >= $timeMasterFinishMove) { - $master_moving = 0; - $realMasterPos = $master->{pos_to}; - - # master is currently moving - } else { - $master_moving = 1; - } - } - - my $min_destination_dist = 1; - if ($runFromTarget) { - $min_destination_dist = $runFromTarget_minStep; - } - - my $max_path_dist; - if ($runFromTargetActive) { - $max_path_dist = $runFromTarget_maxPathDistance; - } else { - $max_path_dist = $attackRouteMaxPathDistance; - } - # Add 1 here to account for pos from solution so we don't have to do it multiple times later - $max_path_dist += 1; - - my %allspots; - my @blocks = calcRectArea2($realMyPos->{x}, $realMyPos->{y}, $max_path_dist, 0); - foreach my $spot (@blocks) { - $allspots{$spot->{x}}{$spot->{y}} = 1; - } - - my %prohibitedSpots; - foreach my $prohibited_actor (@$playersList, @$monstersList, @$npcsList, @$petsList, @$slavesList, @$elementalsList) { - $prohibitedSpots{$prohibited_actor->{pos_to}{x}}{$prohibited_actor->{pos_to}{y}} = 1; - } - - my $best_spot; - my $best_targetPosInStep; - my $best_dist_to_target; - my $best_time; - - foreach my $x_spot (sort keys %allspots) { - foreach my $y_spot (sort keys %{$allspots{$x_spot}}) { - my $spot; - $spot->{x} = $x_spot; - $spot->{y} = $y_spot; - - next unless ($spot->{x} != $realMyPos->{x} || $spot->{y} != $realMyPos->{y}); - - # Is this spot acceptable? - - # 1. It must be walkable. - next unless ($field->isWalkable($spot->{x}, $spot->{y})); - - # 1.2 It must not be occupied - next if (exists $prohibitedSpots{$spot->{x}} && exists $prohibitedSpots{$spot->{x}}{$spot->{y}}); - - # 2. It must not be close to a portal. - next if (positionNearPortal($spot, $config{'attackMinPortalDistance'})); - - my $time_actor_to_get_to_spot; - - my $solution = get_solution($field, $realMyPos, $spot); - - # 3. It must be reachable. - next if (scalar @{$solution} == 0); - - # 4. It must have at max $max_path_dist of route distance to it from our current position. - next if (scalar @{$solution} > $max_path_dist); - - $time_actor_to_get_to_spot = calcTimeFromSolution($solution, $mySpeed); - - - my $total_time = ($timeSinceTargetMoved+$time_actor_to_get_to_spot); - my $temp_targetCurrentStep = calcStepsWalkedFromTimeAndSolution($target_solution, $targetSpeed, $total_time); - # Position target would be at if it doesn't change route (and is not following us) - my $targetPosInStep = $target_solution->[$temp_targetCurrentStep]; - - # 5. It must not be the same position the target will be in - next unless ($spot->{x} != $targetPosInStep->{x} || $spot->{y} != $targetPosInStep->{y}); - - # 6. We must be able to attack the target from this spot - next unless (canAttack($field, $spot, $targetPosInStep, $attackCanSnipe, $attackMaxDistance, $config{clientSight}) == 1); - - # 7. It must not be too close to the target if we have runfromtarget set - # TODO: Maybe we should assume the target will keep following us after it reaches its destination and take that into consideration when runfromtarget is set - my $dist_to_target = blockDistance($spot, $targetPosInStep); - next unless ($dist_to_target >= $min_destination_dist); - - # 8. It must be within $followDistanceMax of MasterPos, if we have a master. - if ($realMasterPos) { - my $masterPosNow; - if ($master_moving) { - my $totalTime = $timeSinceMasterMoved + $time_actor_to_get_to_spot; - my $master_CurrentStep = calcStepsWalkedFromTimeAndSolution($master_solution, $masterSpeed, $totalTime); - $masterPosNow = $master_solution->[$master_CurrentStep]; - } else { - $masterPosNow = $realMasterPos; - } - next unless ($spot->{x} != $masterPosNow->{x} || $spot->{y} != $masterPosNow->{y}); - next unless (blockDistance($spot, $masterPosNow) <= $followDistanceMax); - next unless (blockDistance($targetPosInStep, $masterPosNow) <= $followDistanceMax); - } - - # 8. We must be able to get to the spot before our target - # TODO: Fix me. The target does not need to get to the spot, but to at least 2 cells away to be able to attack us, so take that into account - if ($runFromTargetActive) { - my $time_target_to_get_to_spot = calcTimeFromPathfinding($field, $realTargetPos, $spot, $targetSpeed); - if ($time_actor_to_get_to_spot > $time_target_to_get_to_spot) { - next; - } - } - - # We then choose the spot which takes the least amount of time to reach - # TODO: Maybe this is not the best idea when runfromtarget is set - if (!defined($best_time) || $time_actor_to_get_to_spot < $best_time) { - $best_time = $time_actor_to_get_to_spot; - $best_spot = $spot; - $best_targetPosInStep = $targetPosInStep; - $best_dist_to_target = $dist_to_target; - } - } - } - - if (defined $best_spot) { - debug "[meetingPosition] Best spot is $best_spot->{x} $best_spot->{y}, mob will be at $best_targetPosInStep->{x} $best_targetPosInStep->{y}, dist $best_dist_to_target, it will take $best_time seconds to get there.\n"; - return $best_spot; - } -} - -sub objectAdded { - my ($type, $ID, $obj) = @_; - - if ($type eq 'player' || $type eq 'slave') { - # Try to retrieve the player name from cache. - if (!getPlayerNameFromCache($obj) && !$obj->{clone}) { - push @unknownPlayers, $ID; - } - - } elsif ($type eq 'npc') { - push @unknownNPCs, $ID; - } - - if ($type eq 'monster') { - if (mon_control($obj->{name},$obj->{nameID})->{teleport_search}) { - $ai_v{temp}{searchMonsters}++; - } - } - - Plugins::callHook('objectAdded', { - type => $type, - ID => $ID, - obj => $obj - }); -} - -sub objectRemoved { - my ($type, $ID, $obj) = @_; - - if ($type eq 'monster') { - # FIXME: what if mon_control was changed since the counter was increased? - if (mon_control($obj->{name},$obj->{nameID})->{teleport_search}) { - $ai_v{temp}{searchMonsters}--; - } - } - - Plugins::callHook('objectRemoved', { - type => $type, - ID => $ID - }); -} - -## -# items_control($name, $nameID) -# -# Returns the items_control.txt settings for item name $name. -# If $name has no specific settings, try using the setting for $nameID. -# If both have no specific setting, use 'all'. -# If 'all' is not set, return "do nothing" (empty hash); -sub items_control { - my ($name, $nameID) = @_; - - return $items_control{lc($name)} || $items_control{$nameID} || $items_control{all} || {}; -} - -## -# mon_control($name, $nameID) -# -# Returns the mon_control.txt settings for monster name $name. -# If $name has no specific settings, try using the setting for $nameID. -# If both have no specific setting, use 'all'. -# If 'all' is not set, return "attack"; -sub mon_control { - my ($name, $nameID) = @_; - - return $mon_control{lc($name)} || $mon_control{$nameID} || $mon_control{all} || { attack_auto => 1 }; -} - -## -# pickupitems($name, $nameID) -# -# Returns the pickupitems.txt settings for item name $name. -# If $name has no specific settings, try using the setting for $nameID. -# If both have no specific setting, use 'all'. -# If 'all' is not set, return "pick up" (1); -sub pickupitems { - my ($name, $nameID) = @_; - - if (exists $pickupitems{lc($name)}) { - return $pickupitems{lc($name)}; - } elsif (exists $pickupitems{$nameID}) { - return $pickupitems{$nameID}; - } elsif (exists $pickupitems{all}) { - return $pickupitems{all}; - } - - return 1; -} - -sub positionNearPlayer { - my $r_hash = shift; - my $dist = shift; - - for my $player (@$playersList) { - my $ID = $player->{ID}; - next if ($char->{party}{joined} && $char->{party}{users}{$ID}); - next if (defined($player->{name}) && existsInList($config{tankersList}, $player->{name})); - return 1 if (blockDistance($r_hash, $player->{pos_to}) <= $dist); - } - return 0; -} - -sub positionNearPortal { - my $r_hash = shift; - my $dist = shift; - - for my $portal (@$portalsList) { - return 1 if (blockDistance($r_hash, $portal->{pos}) <= $dist); - } - return 0; -} - -## -# printItemDesc(obj $item) -# -# Print the description for $item. -sub printItemDesc { - my $item = shift; - - my $description = $itemsDesc_lut{$item->{nameID}} || T("Error: No description available.\n"); - my $msg = center(T(" Item Description "), 79, '-') ."\n"; - $msg .= TF("Item: %s, ID: %s, Amount: %s\n\n", $item->{name}, $item->{nameID}, $item->{amount}), "info"; - my @options = grep { $_->{type} } map { my @c = unpack 'vvC', $_;{ type => $c[0], value => $c[1], param => $c[2] } } unpack '(a5)*', $item->{options} || ''; - my $option_index = 1; - foreach ( @options ) { - if ( $itemOptionHandle{$_->{type}} && $itemOption_lut{$itemOptionHandle{$_->{type}}} ) { - $msg .= TF("OPTION %s: ", $option_index) . sprintf($itemOption_lut{$itemOptionHandle{$_->{type}}}, $_->{value}) . "\n"; - } else { - $msg .= TF("OPTION %s: Option (%d, %d, %d)\n", $option_index, $_->{type}, $_->{value},$_->{param}); - } - $option_index++; - } - $msg .= $description; - $msg .= ('-'x79) . "\n"; - message $msg, "info"; -} - -sub processNameRequestQueue { - my ($queue, $actorLists, $foo) = @_; - - while (@{$queue}) { - my $ID = $queue->[0]; - - my $actor; - foreach my $actorList (@$actorLists) { - last if $actor = $actorList->getByID($ID); - } - - # Some private servers ban you if you request info for an object with - # GM Perfect Hide status - if (!$actor || defined($actor->{info}) || $actor->statusActive('EFFECTSTATE_SPECIALHIDING')) { - shift @{$queue}; - next; - } - - # Remove actors with a distance greater than clientSight. Some private servers (notably Freya) use - # a technique where they send actor_exists packets with ridiculous distances in order to automatically - # ban bots. By removingthose actors, we eliminate that possibility and emulate the client more closely. - if (defined $actor->{pos_to} && (my $block_dist = blockDistance($char->{pos_to}, $actor->{pos_to})) >= ($config{clientSight} || 16)) { - debug "[NameRequestQueue] Removed from list actor at $actor->{pos_to}{x} $actor->{pos_to}{y} (distance: $block_dist)\n"; - shift @{$queue}; - next; - } - - if (defined $actor && $actor->{avoid}) { - debug TF("[NameRequestQueue] Removed from list actor %s (flag avoid).\n", $actor); - shift @{$queue}; - next; - } - - $messageSender->sendGetPlayerInfo($ID) if (isSafeActorQuery($ID) == 1); # Do not Query GM's - $actor = shift @{$queue}; - push @{$queue}, $actor if ($actor); - last; - } -} - -sub quit { - $quit = 1; - message T("Exiting...\n"), "system"; -} - -sub offlineMode { - $net->setState(Network::NOT_CONNECTED) if ($net); - undef $conState_tries; - $net->serverDisconnect() if ($net); - $Settings::no_connect = 1; - message TF("Openkore will stay disconnected. Type \"connect\" in order to connect again.\n"), "connection"; -} - -sub relog { - my $timeout = (shift || 5); - my $silent = shift; - $net->setState(1) if ($net); - undef $conState_tries; - $timeout_ex{'master'}{'time'} = time; - $timeout_ex{'master'}{'timeout'} = $timeout; - $net->serverDisconnect() if ($net); - message TF("Relogging in %d seconds...\n", $timeout), "connection" unless $silent; -} - -## -# sendMessage(String type, String msg, String user) -# type: Specifies what kind of message this is. "c" for public chat, "g" for guild chat, -# "p" for party chat, "pm" for private message, "k" for messages that only the RO -# client will see (in X-Kore mode.) -# msg: The message to send. -# user: -# -# Send a chat message to a user. -sub sendMessage { - my ($sender, $type, $msg, $user) = @_; - my ($j, @msgs, $oldmsg, $amount, $space); - my $msgMaxLen = $config{'message_length_max'} || 80; - - @msgs = split /\\n/, $msg; - for ($j = 0; $j < @msgs; $j++) { - my (@msg, $i); - - @msg = split / /, $msgs[$j]; - undef $msg; - for ($i = 0; $i < @msg; $i++) { - if (!length($msg[$i])) { - $msg[$i] = " "; - $space = 1; - } - if (length($msg[$i]) > $msgMaxLen) { - while (length($msg[$i]) >= $msgMaxLen) { - $oldmsg = $msg; - if (length($msg)) { - $amount = $msgMaxLen; - if ($amount - length($msg) > 0) { - $amount = $msgMaxLen - 1; - $msg .= " " . substr($msg[$i], 0, $amount - length($msg)); - } - } else { - $amount = $msgMaxLen; - $msg .= substr($msg[$i], 0, $amount); - } - sendMessage_send($sender, $type, $msg, $user); - $msg[$i] = substr($msg[$i], $amount - length($oldmsg), length($msg[$i]) - $amount - length($oldmsg)); - undef $msg; - } - } - if (length($msg[$i]) && length($msg) + length($msg[$i]) <= $msgMaxLen) { - if (length($msg)) { - if (!$space) { - $msg .= " " . $msg[$i]; - } else { - $space = 0; - $msg .= $msg[$i]; - } - } else { - $msg .= $msg[$i]; - } - } else { - sendMessage_send($sender, $type, $msg, $user); - $msg = $msg[$i]; - } - if (length($msg) && $i == @msg - 1) { - sendMessage_send($sender, $type, $msg, $user); - } - } - } -} - -sub sendMessage_send { - my ($sender, $type, $msg, $user) = @_; - - if ($type eq "c") { - $sender->sendChat($msg); - } elsif ($type eq "g") { - $sender->sendGuildChat($msg); - } elsif ($type eq "p") { - $sender->sendPartyChat($msg); - } elsif ($type eq "bg") { - $sender->sendBattlegroundChat($msg); - } elsif ($type eq "pm") { - %lastpm = ( - msg => $msg, - user => $user - ); - push @lastpm, {%lastpm} if ($user !~ '#\w+'); - $sender->sendPrivateMsg($user, $msg); - } elsif ($type eq "k") { - $sender->injectMessage($msg); - } elsif ($type eq "cln") { - $sender->sendClanChat($msg); - } -} - -# Keep track of when we last cast a skill -sub setSkillUseTimer { - my ($skillID, $targetID, $wait) = @_; - my $skill = new Skill(idn => $skillID); - my $handle = $skill->getHandle(); - - $char->{skills}{$handle}{time_used} = time; - delete $char->{time_cast}; - delete $char->{cast_cancelled}; - $char->{last_skill_time} = time; - $char->{last_skill_used} = $skillID; - $char->{last_skill_target} = $targetID; - - # increment monsterSkill maxUses counter - if (defined $targetID) { - my $actor = Actor::get($targetID); - $actor->{skillUses}{$skill->getHandle()}++; - } - - # Set encore skill if applicable - $char->{encoreSkill} = $skill if $targetID eq $accountID && $skillsEncore{$skill->getHandle()}; -} - -sub setPartySkillTimer { - my ($skillID, $targetID) = @_; - my $skill = new Skill(idn => $skillID); - my $handle = $skill->getHandle(); - - # set partySkill target_time - my $i = $targetTimeout{$targetID}{$handle}; - $ai_v{"partySkill_${i}_time"} = time if $i ne ""; - $ai_v{"partySkill_${i}_target_time"}{$targetID} = time if $i ne ""; -} - -## -# boolean isCellOccupied(pos) -# pos: position hash. -# -# Returns 1 if there is a player, npc or mob in the cell, otherwise return 0. -# TODO: Check if a character can move to a cell with a pet, elemental, homunculus or mercenary -sub isCellOccupied { - my ($pos) = @_; - foreach my $actor (@$playersList, @$monstersList, @$npcsList, @$petsList, @$slavesList, @$elementalsList) { - return 1 if ($actor->{pos_to}{x} == $pos->{x} && $actor->{pos_to}{y} == $pos->{y}); - } - return 0; -} - -## -# boolean setStatus(Actor actor, opt1, opt2, option) -# opt1: the state information of the actor. -# opt2: the ailment information of the actor. -# option: the "look" information of the actor. -# Returns: Whether the actor should be removed from the actor list. -# -# Sets the state, ailment, and "look" statuses of the actor. -# Does not include skillsstatus.txt items. -# TODO: move to Actor? -sub setStatus { - my ($actor, $opt1, $opt2, $option) = @_; - assert(defined $actor, "Can't set status of undef actor") if DEBUG; - assertClass($actor, 'Actor') if DEBUG; - my $verbosity = $actor->{ID} eq $accountID ? 1 : 2; - my $changed = 0; - - my $match_id = sub {return ($_[0] == $_[1])}; - my $match_bitflag = sub {return (($_[0] & $_[1]) == $_[1])}; - - # TODO: we could possibly make the search faster (binary search?) - for ( - [$opt1, \%stateHandle, $match_id, 'state'], - [$opt2, \%ailmentHandle, $match_bitflag, 'ailment'], - [$option, \%lookHandle, $match_bitflag, 'look'], - ) { - my ($option, $handle, $match, $name) = @$_; - #next unless $option; # skip option 0 (no state, ailment, look has such id or bitflag) (we can't have this, the state resets its statuses using this) - for (keys %$handle) { - if (&$match($option, $_)) { - unless ($actor->{statuses}{$handle->{$_}}) { - $actor->{statuses}{$handle->{$_}}{time} = time; - $actor->{statuses}{$handle->{$_}}{tick} = 0; - message status_string($actor, $name . ': ' . ($statusName{$handle->{$_}} || $handle->{$_}), 'now'), "parseMsg_status$name", $verbosity; - $changed = 1; - } - #last; # stop this for loop if found (we cannot do this because of bit flag match must loop all) - } elsif ($actor->{statuses}{$handle->{$_}}) { - delete $actor->{statuses}{$handle->{$_}}; - message status_string($actor, $name . ': ' . ($statusName{$handle->{$_}} || $handle->{$_}), 'no longer'), "parseMsg_status$name", $verbosity; - $changed = 1; - #last; # stop this for loop if found (we cannot do this because of bit flag match must loop all) - } - } - } -=pod - foreach (keys %stateHandle) { - if ($opt1 == $_) { - if (!$actor->{statuses}{$stateHandle{$_}}) { - $actor->{statuses}{$stateHandle{$_}} = 1; - message TF("%s %s in %s state.\n", $actor, $actor->verb('are', 'is'), $statusName{$stateHandle{$_}} || $stateHandle{$_}), "parseMsg_statuslook", $verbosity; - $changed = 1; - } - } elsif ($actor->{statuses}{$stateHandle{$_}}) { - delete $actor->{statuses}{$stateHandle{$_}}; - message TF("%s %s out of %s state.\n", $actor, $actor->verb('are', 'is'), $statusName{$stateHandle{$_}} || $stateHandle{$_}), "parseMsg_statuslook", $verbosity; - $changed = 1; - } - } - - foreach (keys %ailmentHandle) { - if (($opt2 & $_) == $_) { - if (!$actor->{statuses}{$ailmentHandle{$_}}) { - $actor->{statuses}{$ailmentHandle{$_}} = 1; - if ($actor->isa('Actor::You')) { - message TF("%s have ailment: %s.\n", $actor->nameString(), $statusName{$ailmentHandle{$_}} || $ailmentHandle{$_}), "parseMsg_statuslook", $verbosity; - } else { - message TF("%s has ailment: %s.\n", $actor->nameString(), $statusName{$ailmentHandle{$_}} || $ailmentHandle{$_}), "parseMsg_statuslook", $verbosity; - } - $changed = 1; - } - } elsif ($actor->{statuses}{$ailmentHandle{$_}}) { - delete $actor->{statuses}{$ailmentHandle{$_}}; - message TF("%s %s out of %s ailment.\n", $actor, $actor->verb('are', 'is'), $statusName{$ailmentHandle{$_}} || $ailmentHandle{$_}), "parseMsg_statuslook", $verbosity; - $changed = 1; - } - } - - foreach (keys %lookHandle) { - if (($option & $_) == $_) { - if (!$actor->{statuses}{$lookHandle{$_}}) { - $actor->{statuses}{$lookHandle{$_}} = 1; - if ($actor->isa('Actor::You')) { - message TF("%s have look: %s.\n", $actor->nameString, $statusName{$lookHandle{$_}} || $lookHandle{$_}), "parseMsg_statuslook", $verbosity; - } else { - message TF("%s has look: %s.\n", $actor->nameString, $statusName{$lookHandle{$_}} || $lookHandle{$_}), "parseMsg_statuslook", $verbosity; - } - $changed = 1; - } - } elsif ($actor->{statuses}{$lookHandle{$_}}) { - delete $actor->{statuses}{$lookHandle{$_}}; - message TF("%s %s out of %s look.\n", $actor, $actor->verb('are', 'is'), $statusName{$lookHandle{$_}} || $lookHandle{$_}), "parseMsg_statuslook", $verbosity; - $changed = 1; - } - } -=cut - Plugins::callHook('changed_status',{ - actor => $actor, - changed => $changed - }); - - # Remove perfectly hidden objects - if ($actor->statusActive('EFFECTSTATE_SPECIALHIDING')) { - if (UNIVERSAL::isa($actor, "Actor::Player")) { - message TF("Found perfectly hidden %s\n", $actor->nameString()); - # message TF("Remove perfectly hidden %s\n", $actor->nameString()); - # $playersList->remove($actor); - # Call the hook when a perfectly hidden player is detected - # Plugins::callHook('perfect_hidden_player',undef); - Plugins::callHook('perfect_hidden_player', { - actor => $actor, - changed => $changed - }); - - } elsif (UNIVERSAL::isa($actor, "Actor::Monster")) { - message TF("Found perfectly hidden %s\n", $actor->nameString()); - # message TF("Remove perfectly hidden %s\n", $actor->nameString()); - # $monstersList->remove($actor); - - # NPCs do this on purpose (who knows why) - } elsif (UNIVERSAL::isa($actor, "Actor::NPC")) { - message TF("Found perfectly hidden %s\n", $actor->nameString()); - # message TF("Remove perfectly hidden %s\n", $actor->nameString()); - # $npcsList->remove($actor); - Plugins::callHook('perfect_hidden_npc', { - actor => $actor, - changed => $changed - }); - - } elsif (UNIVERSAL::isa($actor, "Actor::Pet")) { - message TF("Found perfectly hidden %s\n", $actor->nameString()); - # message TF("Remove perfectly hidden %s\n", $actor->nameString()); - # $petsList->remove($actor); - } - return 1; - } else { - return 0; - } -} - - -# Increment counter for monster being casted on -sub countCastOn { - my ($sourceID, $targetID, $skillID, $x, $y) = @_; - return unless defined $targetID; - - my $source = Actor::get($sourceID); - my $target = Actor::get($targetID); - assertClass($source, 'Actor') if DEBUG; - assertClass($target, 'Actor') if DEBUG; - - if ($targetID eq $accountID) { - $source->{castOnToYou}++; - } elsif ($target->isa('Actor::Player') || $target->isa('Actor::Slave')) { - $source->{castOnToPlayer}{$targetID}++; - } elsif ($target->isa('Actor::Monster')) { - $source->{castOnToMonster}{$targetID}++; - } - - if ($sourceID eq $accountID) { - $target->{castOnByYou}++; - } elsif ($source->isa('Actor::Player') || $source->isa('Actor::Slave')) { - $target->{castOnByPlayer}{$sourceID}++; - } elsif ($source->isa('Actor::Monster')) { - $target->{castOnByMonster}{$sourceID}++; - } -} - -## -# boolean stripLanguageCode(String* msg) -# msg: a chat message, as sent by the RO server. -# Returns: whether the language code was stripped. -# -# Strip the language code character from a chat message. -sub stripLanguageCode { - my $r_msg = shift; - if ($masterServer->{chatLangCode}) { - if ($$r_msg =~ /^\|..(.*)/) { - $$r_msg = $1; - return 1; - } elsif ($$r_msg =~ /^(#main : \[.*\] )\|..(.*)/) { - $$r_msg = $1.$2; - return 1; - } - return 0; - } else { - return 0; - } -} - -## -# void switchConf(String filename) -# filename: a configuration file. -# Returns: 1 on success, 0 if $filename does not exist. -# -# Switch to another configuration file. -sub switchConfigFile { - my $filename = shift; - if (! -f $filename) { - error TF("%s does not exist.\n", $filename); - return 0; - } - - Settings::setConfigFilename($filename); - parseConfigFile($filename, \%config); - return 1; -} - -sub updateDamageTables { - my ($sourceID, $targetID, $damage) = @_; - - # Track deltaHp - # - # A player's "deltaHp" initially starts at 0. - # When he takes damage, the damage is subtracted from his deltaHp. - # When he is healed, this amount is added to the deltaHp. - # If the deltaHp becomes positive, it is reset to 0. - # - # Someone with a lot of negative deltaHp is probably in need of healing. - # This allows us to intelligently heal non-party members. - if (my $target = Actor::get($targetID)) { - $target->{deltaHp} -= $damage; - $target->{deltaHp} = 0 if $target->{deltaHp} > 0; - } - - if ($sourceID eq $accountID) { - if ((my $monster = $monstersList->getByID($targetID))) { - # You attack monster - $monster->{dmgTo} += $damage; - $monster->{dmgFromYou} += $damage; - $monster->{numAtkFromYou}++; - if ($damage <= ($config{missDamage} || 0)) { - $monster->{missedFromYou}++; - debug "Incremented missedFromYou count to $monster->{missedFromYou}\n", "attackMonMiss"; - $monster->{atkMiss}++; - } else { - if ($config{attackUpdateMonsterPos} && ($monster->{pos}{x} != $monster->{pos_to}{x} || $monster->{pos}{y} != $monster->{pos_to}{y})) { - my $new_monster_pos = calcPosFromPathfinding($field, $monster); - $monster->{pos} = $new_monster_pos; - $monster->{pos_to} = $new_monster_pos; - $monster->{time_move} = time; - $monster->{time_move_calc} = 0; - debug "Target monster $monster got hit by us during its movement, updating its position to $monster->{pos}{x} $monster->{pos}{y}.\n"; - } - $monster->{atkMiss} = 0; - } - if ($config{teleportAuto_atkMiss} && $monster->{atkMiss} >= $config{teleportAuto_atkMiss}) { - message T("Teleporting because of attack miss\n"), "teleport"; - ai_useTeleport(1); - } - if ($config{teleportAuto_atkCount} && $monster->{numAtkFromYou} >= $config{teleportAuto_atkCount}) { - message TF("Teleporting after attacking a monster %d times\n", $config{teleportAuto_atkCount}), "teleport"; - ai_useTeleport(1); - } - - if (AI::action eq "attack" && mon_control($monster->{name},$monster->{nameID})->{attack_auto} == 3 && $damage) { - # Mob-training, you only need to attack the monster once to provoke it - message TF("%s (%s) has been provoked, searching another monster\n", $monster->{name}, $monster->{binID}); - $char->sendAttackStop; - $char->dequeue; - } - - - } - - } elsif ((my $monster = $monstersList->getByID($sourceID))) { - if (my $player = ($accountID eq $targetID && $char) || $playersList->getByID($targetID) || $slavesList->getByID($targetID)) { - # Monster attacks player or slave - $monster->{dmgFrom} += $damage; - ($accountID eq $targetID ? $monster->{dmgToYou} : $monster->{dmgToPlayer}{$targetID}) += $damage; - $player->{dmgFromMonster}{$sourceID} += $damage; - if ($damage == 0) { - ($accountID eq $targetID ? $monster->{missedYou} : $monster->{missedToPlayer}{$targetID}) += 1; - $player->{missedFromMonster}{$sourceID}++; - } - $accountID eq $targetID && $monster->{attackedYou}++ unless ( - scalar(keys %{$monster->{dmgFromPlayer}}) || - scalar(keys %{$monster->{dmgToPlayer}}) || - $monster->{missedFromPlayer} || - $monster->{missedToPlayer} - ); - if (existsInList($config{tankersList}, $player->{name}) || - ($char->{slaves} && %{$char->{slaves}} && $char->{slaves}{$targetID} && %{$char->{slaves}{$targetID}}) || - ($char->{party}{joined} && $char->{party}{users}{$targetID} && %{$char->{party}{users}{$targetID}})) { - # Monster attacks party member or our slave - $monster->{dmgToParty} += $damage; - $monster->{missedToParty}++ if ($damage == 0); - } - $monster->{target} = $targetID; - OpenKoreMod::updateDamageTables($monster) if (defined &OpenKoreMod::updateDamageTables); - - if (AI::state == AI::AUTO && ($accountID eq $targetID or $char->{slaves} && $char->{slaves}{$targetID})) { - # object under our control - my $teleport = 0; - if (mon_control($monster->{name},$monster->{nameID})->{teleport_auto} == 2 && $damage){ - message TF("%s hit %s. Teleporting...\n", - $monster, $player), "teleport"; - $teleport = 1; - - } elsif ($config{$player->{configPrefix}.'teleportAuto_deadly'} && $damage >= $player->{hp} - && !$player->statusActive('EFST_ILLUSION')) { - message TF("%s can kill %s with the next %d dmg. Teleporting...\n", - $monster, $player, $damage), "teleport"; - $teleport = 1; - - } elsif ($config{$player->{configPrefix}.'teleportAuto_maxDmg'} && $damage >= $config{$player->{configPrefix}.'teleportAuto_maxDmg'} - && !$player->statusActive('EFST_ILLUSION') - && !($config{$player->{configPrefix}.'teleportAuto_maxDmgInLock'} && $field->baseName eq $config{lockMap})) { - message TF("%s hit %s for more than %d dmg. Teleporting...\n", - $monster, $player, $config{$player->{configPrefix}.'teleportAuto_maxDmg'}), "teleport"; - $teleport = 1; - - } elsif ($config{$player->{configPrefix}.'teleportAuto_maxDmgInLock'} && $field->baseName eq $config{lockMap} - && $damage >= $config{$player->{configPrefix}.'teleportAuto_maxDmgInLock'} - && !$player->statusActive('EFST_ILLUSION')) { - message TF("%s hit %s for more than %d dmg in lockMap. Teleporting...\n", - $monster, $player, $config{$player->{configPrefix}.'teleportAuto_maxDmgInLock'}), "teleport"; - $teleport = 1; - - } elsif (($player->{sitting} || AI::inQueue("sitAuto")) - && $config{$player->{configPrefix}.'teleportAuto_attackedWhenSitting'} - && ($damage || $config{$player->{configPrefix}.'teleportAuto_attackedWhenSitting'} == 2)) { - if ($damage) { - message TF("%s hit %s while you are sitting. Teleporting...\n", - $monster, $player), "teleport"; - } else { - message TF("%s attacked %s while you are sitting. Teleporting...\n", - $monster, $player), "teleport"; - } - $teleport = 1; - - } elsif ($config{$player->{configPrefix}.'teleportAuto_totalDmg'} - && ($accountID eq $targetID ? $monster->{dmgToYou} : $monster->{dmgToPlayer}{$targetID}) >= $config{$player->{configPrefix}.'teleportAuto_totalDmg'} - && !$player->statusActive('EFST_ILLUSION') - && !($config{$player->{configPrefix}.'teleportAuto_totalDmgInLock'} && $field->baseName eq $config{lockMap})) { - message TF("%s hit %s for a total of more than %d dmg. Teleporting...\n", - $monster, $player, $config{$player->{configPrefix}.'teleportAuto_totalDmg'}), "teleport"; - $teleport = 1; - - } elsif ($config{$player->{configPrefix}.'teleportAuto_totalDmgInLock'} && $field->baseName eq $config{lockMap} - && ($accountID eq $targetID ? $monster->{dmgToYou} : $monster->{dmgToPlayer}{$targetID}) >= $config{$player->{configPrefix}.'teleportAuto_totalDmgInLock'} - && !$player->statusActive('EFST_ILLUSION')) { - message TF("%s hit %s for a total of more than %d dmg in lockMap. Teleporting...\n", - $monster, $player, $config{$player->{configPrefix}.'teleportAuto_totalDmgInLock'}), "teleport"; - $teleport = 1; - - } elsif ($config{$player->{configPrefix}.'teleportAuto_hp'} && percent_hp($player) <= $config{$player->{configPrefix}.'teleportAuto_hp'}) { - message TF("%s hit %s when %s HP is under %d. Teleporting...\n", - $monster, $player, $player->verb(T('your'), T('its')), $config{$player->{configPrefix}.'teleportAuto_hp'}), "teleport"; - $teleport = 1; - - } elsif ($accountID eq $targetID && $player->action eq "attack" && mon_control($monster->{name}, $monster->{nameID})->{attack_auto} == 3 - && ($monster->{dmgToYou} || $monster->{missedYou} || $monster->{dmgFromYou})) { - - # Mob-training, stop attacking the monster if it has been attacking you - message TF("%s has been provoked, searching another monster\n", $monster); - $player->sendAttackStop; - $player->dequeue; - } - ai_useTeleport(1) if ($teleport); - } - } - - } elsif ((my $player = $playersList->getByID($sourceID) || $slavesList->getByID($sourceID))) { - if ((my $monster = $monstersList->getByID($targetID))) { - # Player or Slave attacks monster - $monster->{dmgTo} += $damage; - $monster->{dmgFromPlayer}{$sourceID} += $damage; - $monster->{lastAttackFrom} = $sourceID; - $player->{dmgToMonster}{$targetID} += $damage; - - if ($damage == 0) { - $monster->{missedFromPlayer}{$sourceID}++; - $player->{missedToMonster}{$targetID}++; - } - - if (existsInList($config{tankersList}, $player->{name}) || ($char->{slaves} && $char->{slaves}{$sourceID}) || - ($char->{party}{joined} && $char->{party}{users}{$sourceID} && %{$char->{party}{users}{$sourceID}})) { - $monster->{dmgFromParty} += $damage; - - if ($damage == 0) { - $monster->{missedFromParty}++; - } - } - OpenKoreMod::updateDamageTables($monster) if (defined &OpenKoreMod::updateDamageTables); - } - } -} - -## -# updatePlayerNameCache(player) -# player: a player actor object. -sub updatePlayerNameCache { - my ($player) = @_; - - return if (!$config{cachePlayerNames}); - - # First, cleanup the cache. Remove entries that are too old. - # Default life time: 15 minutes - my $changed = 1; - for (my $i = 0; $i < @playerNameCacheIDs; $i++) { - my $ID = $playerNameCacheIDs[$i]; - if (timeOut($playerNameCache{$ID}{time}, $config{cachePlayerNames_duration})) { - delete $playerNameCacheIDs[$i]; - delete $playerNameCache{$ID}; - $changed = 1; - } - } - compactArray(\@playerNameCacheIDs) if ($changed); - - # Resize the cache if it's still too large. - # Default cache size: 100 - while (@playerNameCacheIDs > $config{cachePlayerNames_maxSize}) { - my $ID = shift @playerNameCacheIDs; - delete $playerNameCache{$ID}; - } - - # Add this player name to the cache. - my $ID = $player->{ID}; - if (!$playerNameCache{$ID}) { - push @playerNameCacheIDs, $ID; - my %entry = ( - name => $player->{name}, - guild => $player->{guild}, - time => time, - lv => $player->{lv}, - jobID => $player->{jobID} - ); - $playerNameCache{$ID} = \%entry; - } -} - -sub canUseTeleport { - my ($use_lvl) = @_; - - # not in game - return 0 if $net && $net->getState != Network::IN_GAME; # $net check is to not crash test - - # 1 - check for items - my $item; - if($use_lvl == 1) { - if ($config{teleportAuto_item1}) { - $item = $char->inventory->getByName($config{teleportAuto_item1}); - $item = $char->inventory->getByNameID($config{teleportAuto_item1}) if (!($item) && $config{teleportAuto_item1} =~ /^\d{3,}$/); - } - $item = getFlyWing() unless $item; - } else { - if ($config{teleportAuto_item2}) { - $item = $char->inventory->getByName($config{teleportAuto_item2}); - $item = $char->inventory->getByNameID($config{teleportAuto_item2}) if (!($item) && $config{teleportAuto_item2} =~ /^\d{3,}$/); - } - $item = getButterflyWing() unless $item; - } - - return 1 if $item; - - # Mute prevents talking, usage of skills, and commands. - return 0 if $char->{'muted'}; - - # 2 - check for chat command - return 1 if ($config{teleportAuto_useChatCommand} && $use_lvl == 1); - return 1 if ($config{saveMap_warpChatCommand} && $use_lvl == 2); - - # 3 - check for equipments - return 1 if(Actor::Item::scanConfigAndCheck('teleportAuto_equip')); - - # 4 - check for skill - my $skill_level = ($char->{skills}{AL_TELEPORT}{lv}) ? $char->{skills}{AL_TELEPORT}{lv} : 0; - return 1 if($skill_level >= $use_lvl); - - return 0; -} - -## -# top10Listing(args) -# args: a 282 bytes packet representing 10 names followed by 10 ranks -# -# Returns a formatted list of [# ], Name and points -sub top10Listing { - my ($args) = @_; - - my $msg = $args->{RAW_MSG}; - - my @list; - my @points; - my $i; - my $textList = ""; - for ($i = 0; $i < 10; $i++) { - $list[$i] = unpack("Z24", substr($msg, 2 + (24*$i), 24)); - } - for ($i = 0; $i < 10; $i++) { - $points[$i] = unpack("V1", substr($msg, 242 + ($i*4), 4)); - } - for ($i = 0; $i < 10; $i++) { - $textList .= swrite("[@<] @<<<<<<<<<<<<<<<<<<<<<<<< @>>>>>>>>>>", - [$i+1, $list[$i], $points[$i]]); - } - - return $textList; -} - -## -# whenGroundStatus(target, statuses, mine) -# target: coordinates hash -# statuses: a comma-separated list of ground effects e.g. Safety Wall,Pneuma -# mine: if true, only consider ground effects that originated from me -# -# Returns 1 if $target has one of the ground effects specified by $statuses. -sub whenGroundStatus { - my ($pos, $statuses, $mine) = @_; - - my ($x, $y) = ($pos->{x}, $pos->{y}); - for my $ID (@spellsID) { - my $spell; - next unless $spell = $spells{$ID}; - next if $mine && $spell->{sourceID} ne $accountID; - if ($x == $spell->{pos}{x} && - $y == $spell->{pos}{y}) { - return 1 if existsInList($statuses, getSpellName($spell->{type})); - } - } - return 0; -} - -sub writeStorageLog { - my ($show_error_on_fail) = @_; - my $f; - - if (open($f, ">:utf8", $Settings::storage_log_file)) { - print $f TF("---------- Storage %s -----------\n", getFormattedDate(int(time))); - for my $item (@{$char->storage}) { - - my $display = sprintf "%2d [%6d] %s x %s", $item->{binID}, $item->{nameID}, $item->{name}, $item->{amount}; - # Translation Comment: Mark to show not identified items - $display .= " -- " . T("Not Identified") if !$item->{identified}; - # Translation Comment: Mark to show broken items - $display .= " -- " . T("Broken") if $item->{broken}; - print $f "$display\n"; - } - # Translation Comment: Storage Capacity - print $f TF("\nCapacity: %d/%d\n", $char->storage->items, $char->storage->items_max); - print $f "-------------------------------\n"; - close $f; - - message T("Storage logged\n"), "success"; - - } elsif ($show_error_on_fail) { - error TF("Unable to write to %s\n", $Settings::storage_log_file); - } -} - -## -# getBestTarget(possibleTargets, attackCheckLOS, $attackCanSnipe) -# possibleTargets: reference to an array of monsters' IDs -# attackCheckLOS: if set, non-LOS monsters are checked up -# -# Returns ID of the best target -sub getBestTarget { - my ($possibleTargets, $attackCheckLOS, $attackCanSnipe) = @_; - if (!$possibleTargets) { - return; - } - - my $portalDist = $config{'attackMinPortalDistance'} || 4; - my $playerDist = $config{'attackMinPlayerDistance'} || 1; - - my @noLOSMonsters; - my @noLOSMonsters_pos; - # TODO: Is there any situation where we should use calcPosFromPathfinding or calcPosFromTime here? - my $myPos = calcPosFromPathfinding($field, $char); - my ($highestPri, $smallestDist, $bestTarget); - - # First of all we check monsters in LOS, then the rest of monsters - - foreach (@{$possibleTargets}) { - my $monster = $monsters{$_}; - # TODO: Is there any situation where we should use calcPosFromPathfinding or calcPosFromTime here? - my $pos = calcPosFromPathfinding($field, $monster); - next if (positionNearPlayer($pos, $playerDist) - || positionNearPortal($pos, $portalDist) - ); - my $control = mon_control($monster->{name},$monster->{nameID}); - if (defined $control) { - next if ( ($control->{attack_auto} == -1) - || ($control->{attack_lvl} ne "" && $control->{attack_lvl} > $char->{lv}) - || ($control->{attack_jlvl} ne "" && $control->{attack_jlvl} > $char->{lv_job}) - || ($control->{attack_hp} ne "" && $control->{attack_hp} > $char->{hp}) - || ($control->{attack_sp} ne "" && $control->{attack_sp} > $char->{sp}) - || ($control->{attack_auto} == 3 && ($monster->{dmgToYou} || $monster->{missedYou} || $monster->{dmgFromYou})) - || ($control->{attack_auto} == 0 && !($monster->{dmgToYou} || $monster->{missedYou})) - ); - } - - my %plugin_args; - $plugin_args{target} = $monster; - $plugin_args{control} = $control; - $plugin_args{attackCheckLOS} = $attackCheckLOS; - $plugin_args{attackCanSnipe} = $attackCanSnipe; - $plugin_args{return} = 0; - Plugins::callHook('getBestTarget' => \%plugin_args); - next if ($plugin_args{return}); - - if (!$field->checkLOS($myPos, $pos, $attackCanSnipe)) { - push(@noLOSMonsters, $_); - push(@noLOSMonsters_pos, $pos); - next; - } - - my $name = lc $monster->{name}; - my $dist = adjustedBlockDistance($myPos, $pos); - my $priority = $priority{$name} ? $priority{$name} : 0; - - if (!defined($bestTarget) || ($priority > $highestPri)) { - $highestPri = $priority; - $smallestDist = $dist; - $bestTarget = $_; - } - if ((!defined($bestTarget) || $priority == $highestPri) - && (!defined($smallestDist) || $dist < $smallestDist)) { - $highestPri = $priority; - $smallestDist = $dist; - $bestTarget = $_; - } - } - if ($attackCheckLOS && !$bestTarget && scalar(@noLOSMonsters) > 0) { - my $pathfinding = new PathFinding; - my ($min_pathfinding_x, $min_pathfinding_y, $max_pathfinding_x, $max_pathfinding_y) = $field->getSquareEdgesFromCoord($myPos, $config{attackRouteMaxPathDistance}); - foreach my $index (0..$#noLOSMonsters) { - - # The most optimal solution is to include the path lenghts' comparison, however it will take - # more time and CPU resources, so, we use rough solution with priority and distance comparison - - my $monster = $monsters{$noLOSMonsters[$index]}; - # TODO: Is there any situation where we should use calcPosFromPathfinding or calcPosFromTime here? - my $pos = $noLOSMonsters_pos[$index]; - - # avoid get targets away from attackRouteMaxPathDistance - next if(blockDistance($myPos, $pos) >= $config{attackRouteMaxPathDistance}); - - $pathfinding->reset( - start => $myPos, - dest => $pos, - field => $field, - avoidWalls => 0, - randomFactor => 0, - useManhattan => 0, - min_x => $min_pathfinding_x, - max_x => $max_pathfinding_x, - min_y => $min_pathfinding_y, - max_y => $max_pathfinding_y - ); - my $dist = $pathfinding->runcount; - if ($dist <= 0 || $dist > $config{attackRouteMaxPathDistance}) { - $monster->{attack_failedLOS} = time; - next; - } - - my $name = lc $monster->{name}; - my $priority = $priority{$name} ? $priority{$name} : 0; - if (!defined($bestTarget) || ($priority > $highestPri)) { - $highestPri = $priority; - $smallestDist = $dist; - $bestTarget = $noLOSMonsters[$index]; - } - if ((!defined($bestTarget) || $priority == $highestPri) - && (!defined($smallestDist) || $dist < $smallestDist)) { - $highestPri = $priority; - $smallestDist = $dist; - $bestTarget = $noLOSMonsters[$index]; - } - } - } - return $bestTarget; -} - -## -# boolean isSafe() -# -# Returns 1 if there is a player nearby (except party and homunculus) or 0 if not -sub isSafe { - foreach (@playersID) { - if (!$char->{party}{users}{$_}) { - return 0; - } - } - return 1; -} - -## -# boolean isSafeActorQuery(ID) -# -# Returns 1 if we are safe to query actor name by given actor ID. -sub isSafeActorQuery { - my ($ID) = @_; - foreach my $list ($playersList, $monstersList, $npcsList, $petsList, $slavesList) { - my $actor = $list->getByID($ID); - if ($actor) { - # Do not AutoVivify here! - if (defined $actor->{statuses} && %{$actor->{statuses}}) { - if ( $actor->statusActive('EFFECTSTATE_SPECIALHIDING') || ($config{avoidHiddenActors} && ($actor->{type} == 111 || $actor->{type} == 139 || $actor->{type} == 2337)) ) { # HIDDEN_ACTOR TYPES - return 0; - } - } - return 0 if($actor->{avoid}); - } - } - return 1; -} - -####################################### -####################################### -###CATEGORY: Actor's Actions Text -####################################### -####################################### - -## -# String attack_string(Actor source, Actor target, int damage, int delay) -# -# Generates a proper message string for when actor $source attacks actor $target. -sub attack_string { - my ($source, $target, $damage, $delay) = @_; - assertClass($source, 'Actor') if DEBUG; - assertClass($target, 'Actor') if DEBUG; - - return TF("%s %s %s (Dmg: %s) (Delay: %sms)\n", - $source->nameString, - $source->verb(T('attack'), T('attacks')), - $target->nameString($source), - $damage, $delay); -} - -sub skillCast_string { - my ($source, $target, $x, $y, $skillName, $delay) = @_; - assertClass($source, 'Actor') if DEBUG; - assertClass($target, 'Actor') if DEBUG; - - return TF("%s %s %s on %s (Delay: %sms)\n", - $source->nameString(), - $source->verb(T('are casting'), T('is casting')), - $skillName, - ($x != 0 || $y != 0) ? TF("location (%d, %d)", $x, $y) : $target->nameString($source), - $delay); -} - -sub skillUse_string { - my ($source, $target, $skillName, $damage, $level, $delay) = @_; - assertClass($source, 'Actor') if DEBUG; - assertClass($target, 'Actor') if DEBUG; - - return sprintf("%s %s %s%s %s %s%s%s\n", - $source->nameString(), - $source->verb(T('use'), T('uses')), - $skillName, - ($level != 65535 && $level != 4294967295) ? ' ' . TF("(Lv: %s)", $level) : '', - T('on'), - $target->nameString($source), - ($damage != -30000) ? ' ' . TF("(Dmg: %s)", $damage || T('Miss')) : '', - ($delay) ? ' ' . TF("(Delay: %sms)", $delay) : ''); -} - -sub skillUseLocation_string { - my ($source, $skillName, $args) = @_; - assertClass($source, 'Actor') if DEBUG; - - return sprintf("%s %s %s%s %s (%d, %d)\n", - $source->nameString(), - $source->verb(T('use'), T('uses')), - $skillName, - ($args->{lv} != 65535 && $args->{lv} != 4294967295) ? ' ' . TF("(Lv: %s)", $args->{lv}) : '', - T('on location'), - $args->{x}, - $args->{y}); -} - -# TODO: maybe add other healing skill ID's? -sub skillUseNoDamage_string { - my ($source, $target, $skillID, $skillName, $amount) = @_; - assertClass($source, 'Actor') if DEBUG; - assertClass($target, 'Actor') if DEBUG; - - return sprintf("%s %s %s %s %s%s\n", - $source->nameString(), - $source->verb(T('use'), T('uses')), - $skillName, - T('on'), - $target->nameString($source), - ($skillID == 28) ? ' ' . TF("(Gained: %s hp)", $amount) : ($amount && $amount != 65535 && $amount != 4294967295) ? ' ' . TF("(Lv: %s)", $amount) : ''); -} - -sub status_string { - my ($source, $statusName, $mode, $seconds) = @_; - assertClass($source, 'Actor') if DEBUG; - - # Translation Comment: "you/actor" "are/is now/again/nolonger" "status" "(duration)" - TF("%s %s: %s%s\n", - $source->nameString, - ($mode eq 'now') ? $source->verb(T('are now'), T('is now')) - : ($mode eq 'again') ? $source->verb(T('are again'), T('is again')) - : ($mode eq 'no longer') ? $source->verb(T('are no longer'), T('is no longer')) : $mode, - $statusName, - $seconds ? ' ' . TF("(Duration: %ss)", $seconds) : '' - ) -} - -####################################### -####################################### -###CATEGORY: AI Math -####################################### -####################################### - -sub lineIntersection { - my $r_pos1 = shift; - my $r_pos2 = shift; - my $r_pos3 = shift; - my $r_pos4 = shift; - my ($x1, $x2, $x3, $x4, $y1, $y2, $y3, $y4, $result, $result1, $result2); - $x1 = $$r_pos1{'x'}; - $y1 = $$r_pos1{'y'}; - $x2 = $$r_pos2{'x'}; - $y2 = $$r_pos2{'y'}; - $x3 = $$r_pos3{'x'}; - $y3 = $$r_pos3{'y'}; - $x4 = $$r_pos4{'x'}; - $y4 = $$r_pos4{'y'}; - $result1 = ($x4 - $x3)*($y1 - $y3) - ($y4 - $y3)*($x1 - $x3); - $result2 = ($y4 - $y3)*($x2 - $x1) - ($x4 - $x3)*($y2 - $y1); - if ($result2 != 0) { - $result = $result1 / $result2; - } - return $result; -} - -sub percent_hp { - my $r_hash = shift; - if (!$$r_hash{'hp_max'}) { - return undef; - } else { - return ($$r_hash{'hp'} / $$r_hash{'hp_max'} * 100); - } -} - -sub percent_sp { - my $r_hash = shift; - if (!$$r_hash{'sp_max'}) { - return 0; - } else { - return ($$r_hash{'sp'} / $$r_hash{'sp_max'} * 100); - } -} - -sub percent_ap { - my $r_hash = shift; - if (!$$r_hash{'ap_max'}) { - return 0; - } else { - return ($$r_hash{'ap'} / $$r_hash{'ap_max'} * 100); - } -} - -sub percent_weight { - my $r_hash = shift; - if (!$$r_hash{'weight_max'}) { - return 0; - } else { - return ($$r_hash{'weight'} / $$r_hash{'weight_max'} * 100); - } -} - - -####################################### -####################################### -###CATEGORY: Misc Functions -####################################### -####################################### - -sub avoidGM_near { - for my $player (@$playersList) { - # skip this person if we dont know the name - next if (!defined $player->{name}); - - # Check whether this "GM" is on the ignore list in order to prevent false matches - next if (existsInList($config{avoidGM_ignoreList}, $player->{name})); - - # check if this name matches the GM filter - next unless ($config{avoidGM_namePattern} ? $player->{name} =~ /$config{avoidGM_namePattern}/ : $player->{name} =~ /^([a-z]?ro)?-?(Sub)?-?\[?GM\]?/i); - - my %args = ( - name => $player->{name}, - ID => $player->{nameID} - ); - Plugins::callHook('avoidGM_near', \%args); - return 1 if ($args{return}); - - my $msg; - if ($config{avoidGM_near} == 1) { - # Mode 1: teleport & disconnect - ai_useTeleport(1); - $msg = TF("GM '%s' (%d) is nearby (%s), teleport & disconnect for %d seconds", $player->{name}, $player->{nameID}, $field->baseName, $config{avoidGM_reconnect}); - relog($config{avoidGM_reconnect}, 1); - - } elsif ($config{avoidGM_near} == 2) { - # Mode 2: disconnect - $msg = TF("GM '%s' (%d) is nearby (%s), disconnect for %s seconds", $player->{name}, $player->{nameID}, $field->baseName, $config{avoidGM_reconnect}); - relog($config{avoidGM_reconnect}, 1); - - } elsif ($config{avoidGM_near} == 3) { - # Mode 3: teleport - ai_useTeleport(1); - $msg = TF("GM '%s' (%d) is nearby(%s), teleporting", $player->{name}, $player->{nameID}, $field->baseName); - - } elsif ($config{avoidGM_near} == 4) { - # Mode 4: respawn - ai_useTeleport(2); - $msg = TF("GM '%s' (%d) is nearby (%s), respawning", $player->{name}, $player->{nameID}, $field->baseName); - } elsif ($config{avoidGM_near} >= 5) { - # Mode 5: respawn & disconnect - ai_useTeleport(2); - $msg = TF("GM '%s' (%d) is nearby (%s), respawning & disconnect for %d seconds", $player->{name}, $player->{nameID}, $field->baseName, $config{avoidGM_reconnect}); - relog($config{avoidGM_reconnect}, 1); - } - - warning "$msg\n"; - chatLog("k", "*** $msg ***\n"); - - return 1; - } - return 0; -} - -sub avoidGM_talk { - return 0 if ($net->clientAlive() || !$config{avoidGM_talk}); - my ($player_name, $nameID) = @_; - - # Check whether this "GM" is on the ignore list in order to prevent false matches - return 0 if (existsInList($config{avoidGM_ignoreList}, $player_name)); - - return 0 unless ($config{avoidGM_namePattern} ? $player_name =~ /$config{avoidGM_namePattern}/ : $player_name =~ /^([a-z]?ro)?-?(Sub)?-?\[?GM\]?/i); - - my %args = ( - name => $player_name, - ID => $nameID - ); - Plugins::callHook('avoidGM_talk', \%args); - return 1 if ($args{return}); - - my $msg = TF("GM '%s' (%d) talked to you (%s), disconnect for %s seconds", $player_name, $nameID, $field->baseName, $config{avoidGM_reconnect}); - warning "$msg\n"; - chatLog("k", "*** $msg ***\n"); - relog($config{avoidGM_reconnect}, 1); - return 1; -} - -## -# avoidList_near() -# Returns: 1 if someone was detected, 0 if no one was detected. -# -# Checks if any of the surrounding players are on the avoid.txt avoid list. -# Disconnects / teleports if a player is detected. -sub avoidList_near { - my $return = 0; - return if ($config{avoidList_inLockOnly} && $field->baseName ne $config{lockMap}); - - for my $player (@$playersList) { - # skip this person if we dont know the name - next if (!defined $player->{name}); - # Check whether this Player is on the ignore list in order to prevent false matches - next if (existsInList($config{avoidList_ignoreList}, $player->{name})); - - my $avoidPlayer = $avoid{Players}{lc($player->{name})}; - my $avoidID = $avoid{ID}{$player->{nameID}}; - my $avoidJob = $avoid{Jobs}{lc($jobs_lut{$player->{jobID}})}; - - # next if the player is not on the avoid list - next if (!$avoidPlayer and !$avoidID and !$avoidJob); - - my %args = ( - name => $player->{name}, - ID => $player->{nameID}, - jobID => $player->{jobID}, - Jobs => $jobs_lut{$player->{jobID}} - ); - Plugins::callHook('avoidList_near', \%args); - - my $msg; - if ( ($avoidPlayer && $avoidPlayer->{teleport_on_sight} == 1 && $avoidPlayer->{disconnect_on_sight} == 1) - || ($avoidID && $avoidID->{teleport_on_sight} && $avoidID->{disconnect_on_sight}) - || ($avoidJob && $avoidJob->{teleport_on_sight} && $avoidJob->{disconnect_on_sight}) ) { - # like avoidGM_near Mode 1: teleport & disconnect - ai_useTeleport(1); - $msg = TF("Player %s (%d, %s) is nearby (%s), teleport & disconnect for %d seconds", $player->{name}, $player->{nameID}, $jobs_lut{$player->{jobID}}, $field->baseName, $config{avoidList_reconnect}); - relog($config{avoidList_reconnect}, 1); - $return = 1; - - } elsif ( ($avoidPlayer && $avoidPlayer->{teleport_on_sight} && $avoidPlayer->{disconnect_on_sight}) - || ($avoidID && $avoidID->{teleport_on_sight} && $avoidID->{disconnect_on_sight}) - || ($avoidJob && $avoidJob->{teleport_on_sight} && $avoidJob->{disconnect_on_sight}) ) { - # like avoidGM_near Mode 5: respawn & disconnect - ai_useTeleport(2); - $msg = TF("Player %s (%d, %s) is nearby (%s), respawning & disconnect for %d seconds", $player->{name}, $player->{nameID}, $jobs_lut{$player->{jobID}}, $field->baseName, $config{avoidList_reconnect}); - relog($config{avoidList_reconnect}, 1); - $return = 1; - - } elsif ( !$net->clientAlive() && ( ($avoidPlayer && $avoidPlayer->{disconnect_on_sight}) || - ($avoidID && $avoidID->{disconnect_on_sight}) && ($avoidJob && $avoidJob->{disconnect_on_sight}) ) ) { - # like avoidGM_near Mode 2: disconnect - $msg = TF("Player %s (%d, %s) is nearby (%s), disconnect for %s seconds", $player->{name}, $player->{nameID}, $jobs_lut{$player->{jobID}}, $field->baseName, $config{avoidList_reconnect}); - relog($config{avoidList_reconnect}, 1); - $return = 1; - - } elsif ( ($avoidPlayer && $avoidPlayer->{teleport_on_sight} == 1) || ($avoidID && $avoidID->{teleport_on_sight} == 1) || ($avoidJob && $avoidJob->{teleport_on_sight} == 1) ) { - # like avoidGM_near Mode 3: teleport - ai_useTeleport(1); - $msg = TF("Player %s (%d, %s) is nearby (%s), teleporting", $player->{name}, $player->{nameID}, $jobs_lut{$player->{jobID}}, $field->baseName); - $return = 1; - - } elsif ( ($avoidPlayer && $avoidPlayer->{teleport_on_sight} == 2) || ($avoidID && $avoidID->{teleport_on_sight} == 2) || ($avoidJob && $avoidJob->{teleport_on_sight} == 2) ) { - # like avoidGM_near Mode 4: respawn - ai_useTeleport(2); - $msg = TF("Player %s (%d, %s) is nearby (%s), respawning", $player->{name}, $player->{nameID}, $jobs_lut{$player->{jobID}}, $field->baseName); - $return = 1; - } - - if ($msg) { - warning "$msg\n"; - chatLog("k", "*** $msg ***\n"); - } - - } - return $return; -} - -sub avoidList_ID { - return if (!($config{avoidList}) || ($config{avoidList_inLockOnly} && $field->baseName ne $config{lockMap})); - - my $avoidID = unpack("V", shift); - if ($avoid{ID}{$avoidID} && $avoid{ID}{$avoidID}{disconnect_on_sight}) { - my $msg = TF("Player with ID %s is nearby (%s), disconnect for %s seconds", $avoidID, $field->baseName, $config{avoidList_reconnect}); - warning "$msg\n"; - chatLog("k", "*** $msg ***\n"); - relog($config{avoidList_reconnect}, 1); - return 1; - } - return 0; -} - -sub avoidList_talk { - return 0 if ($net->clientAlive() || !$config{avoidList}); - my ($player_name, $nameID) = @_; - my $avoidPlayer = $avoid{Players}{lc($player_name)}; - my $avoidID = $avoid{ID}{$nameID}; - - if ($avoidPlayer->{disconnect_on_chat} || $avoidID->{disconnect_on_chat}) { - my $msg = TF("Player %s (%d) talked to you (%s), disconnect for %d seconds", $player_name, $nameID, $field->baseName, $config{avoidList_reconnect}); - warning "$msg\n"; - chatLog("k", "*** $msg ***\n"); - relog($config{avoidList_reconnect}, 1); - return 1; - } - return 0; -} - -sub compilePortals { - my $checkOnly = shift; - - my %mapPortals; - my %mapSpawns; - my %missingMap; - my $pathfinding; - my @solution; - my $field; - - # Collect portal source and destination coordinates per map - foreach my $portal (keys %portals_lut) { - $mapPortals{$portals_lut{$portal}{source}{map}}{$portal}{x} = $portals_lut{$portal}{source}{x}; - $mapPortals{$portals_lut{$portal}{source}{map}}{$portal}{y} = $portals_lut{$portal}{source}{y}; - foreach my $dest (keys %{$portals_lut{$portal}{dest}}) { - next if $portals_lut{$portal}{dest}{$dest}{map} eq ''; - $mapSpawns{$portals_lut{$portal}{dest}{$dest}{map}}{$dest}{x} = $portals_lut{$portal}{dest}{$dest}{x}; - $mapSpawns{$portals_lut{$portal}{dest}{$dest}{map}}{$dest}{y} = $portals_lut{$portal}{dest}{$dest}{y}; - } - } - - foreach my $portal (keys %portals_commands) { - foreach my $dest (keys %{$portals_commands{$portal}{dest}}) { - next if $portals_commands{$portal}{dest}{$dest}{map} eq ''; - $mapSpawns{$portals_commands{$portal}{dest}{$dest}{map}}{$dest}{x} = $portals_commands{$portal}{dest}{$dest}{x}; - $mapSpawns{$portals_commands{$portal}{dest}{$dest}{map}}{$dest}{y} = $portals_commands{$portal}{dest}{$dest}{y}; - } - } - - foreach my $portal (keys %portals_spawns) { - foreach my $dest (keys %{$portals_spawns{$portal}{dest}}) { - next if $portals_spawns{$portal}{dest}{$dest}{map} eq ''; - $mapSpawns{$portals_spawns{$portal}{dest}{$dest}{map}}{$dest}{x} = $portals_spawns{$portal}{dest}{$dest}{x}; - $mapSpawns{$portals_spawns{$portal}{dest}{$dest}{map}}{$dest}{y} = $portals_spawns{$portal}{dest}{$dest}{y}; - } - } - - foreach my $portal (keys %portals_airships) { - $mapPortals{$portals_airships{$portal}{source}{map}}{$portal}{x} = $portals_airships{$portal}{source}{x}; - $mapPortals{$portals_airships{$portal}{source}{map}}{$portal}{y} = $portals_airships{$portal}{source}{y}; - foreach my $dest (keys %{$portals_airships{$portal}{dest}}) { - next if $portals_airships{$portal}{dest}{$dest}{map} eq ''; - $mapSpawns{$portals_airships{$portal}{dest}{$dest}{map}}{$dest}{x} = $portals_airships{$portal}{dest}{$dest}{x}; - $mapSpawns{$portals_airships{$portal}{dest}{$dest}{map}}{$dest}{y} = $portals_airships{$portal}{dest}{$dest}{y}; - } - } - - $pathfinding = new PathFinding if (!$checkOnly); - - # Calculate LOS values from each spawn point per map to other portals on same map - foreach my $map (sort keys %mapSpawns) { - ($map, undef) = Field::nameToBaseName(undef, $map); # Hack to clean up InstanceID - message TF("Processing map %s...\n", $map), "system" unless $checkOnly; - foreach my $spawn (keys %{$mapSpawns{$map}}) { - foreach my $portal (keys %{$mapPortals{$map}}) { - next if $spawn eq $portal; - next if $portals_los{$spawn}{$portal} ne ''; - return 1 if $checkOnly; - if ((!$field || $field->baseName ne $map) && !$missingMap{$map}) { - eval { - $field = new Field(name => $map); - }; - if ($@) { - $missingMap{$map} = 1; - } - } - - my %start = %{$mapSpawns{$map}{$spawn}}; - my %dest = %{$mapPortals{$map}{$portal}}; - my $closest_start = $field->closestWalkableSpot(\%start, 1); - my $closest_dest = $field->closestWalkableSpot(\%dest, 1); - my $count; - - if (!defined $closest_start || !defined $closest_dest) { - $count = 0; - } else { - $pathfinding->reset( - start => $closest_start, - dest => $closest_dest, - field => $field - ); - $count = $pathfinding->runcount; - } - $portals_los{$spawn}{$portal} = ($count >= 0) ? $count : 0; - debug "LOS in $map from $start{x},$start{y} to $dest{x},$dest{y}: $portals_los{$spawn}{$portal}\n"; - } - } - } - return 0 if $checkOnly; - - # Write new portalsLOS.txt - writePortalsLOS(Settings::getTableFilename("portalsLOS.txt"), \%portals_los); - message TF("Wrote portals Line of Sight table to '%s'\n", Settings::getTableFilename("portalsLOS.txt")), "system"; - - # Print warning for missing fields - if (%missingMap) { - warning T("----------------------------Error Summary----------------------------\n"); - warning TF("Missing: %s.fld2\n", $_) foreach (sort keys %missingMap); - warning T("Note: LOS information for the above listed map(s) will be inaccurate;\n" . - " however it is safe to ignore if those map(s) are not used\n"); - warning "---------------------------------------------------------------------\n"; - } -} - -sub compilePortals_check { - return compilePortals(1); -} - -sub portalExists { - my ($map, $r_pos) = @_; - foreach (keys %portals_lut) { - if ($portals_lut{$_}{source}{map} eq $map - && $portals_lut{$_}{source}{x} == $r_pos->{x} - && $portals_lut{$_}{source}{y} == $r_pos->{y}) { - return $_; - } - } - return; -} - -sub portalExists2 { - my ($src, $src_pos, $dest, $dest_pos) = @_; - my $srcx = $src_pos->{x}; - my $srcy = $src_pos->{y}; - my $destx = $dest_pos->{x}; - my $desty = $dest_pos->{y}; - my $destID = "$dest $destx $desty"; - - foreach (keys %portals_lut) { - my $entry = $portals_lut{$_}; - if ($entry->{source}{map} eq $src - && $entry->{source}{pos}{x} == $srcx - && $entry->{source}{pos}{y} == $srcy - && $entry->{dest}{$destID}) { - return $_; - } - } - return; -} - -sub portalExistsAirship { - my ($map, $r_pos) = @_; - foreach (keys %portals_airships) { - if ($portals_airships{$_}{source}{map} eq $map - && $portals_airships{$_}{source}{x} == $r_pos->{x} - && $portals_airships{$_}{source}{y} == $r_pos->{y}) { - return $_; - } - } - return; -} - -sub redirectXKoreMessages { - my ($type, $domain, $level, $globalVerbosity, $message, $user_data) = @_; - - return if ($config{'XKore_silent'} || $type eq "debug" || $level > 0 || $net->getState() != Network::IN_GAME || $XKore_dontRedirect); - return if ($domain =~ /^(connection|startup|pm|publicchat|guildchat|guildnotice|selfchat|emotion|drop|inventory|deal|storage|input)$/); - return if ($domain =~ /^(attack|skill|list|info|partychat|npc|route)/); - - $message =~ s/\n*$//s; - $message =~ s/\n/\\n/g; - sendMessage($messageSender, "k", $message); -} - -sub monKilled { - $monkilltime = time(); - # if someone kills it - if (($monstarttime == 0) || ($monkilltime < $monstarttime)) { - $monstarttime = 0; - $monkilltime = 0; - } - $elasped = $monkilltime - $monstarttime; - $totalelasped = $totalelasped + $elasped; - if ($totalelasped == 0) { - $dmgpsec = 0 - } else { - $dmgpsec = $totaldmg / $totalelasped; - } -} - -# Resolves a player or monster ID into a name -# Obsoleted by Actor module, don't use this! -sub getActorName { - my $id = shift; - - if (!$id) { - return T("Nothing"); - } else { - my $hash = Actor::get($id); - return $hash->nameString; - } -} - -# Resolves a pair of player/monster IDs into names -sub getActorNames { - my ($sourceID, $targetID, $verb1, $verb2) = @_; - - my $source = getActorName($sourceID); - my $verb = $source eq 'You' ? $verb1 : $verb2; - my $target; - - if ($targetID eq $sourceID) { - if ($targetID eq $accountID) { - $target = 'yourself'; - } else { - $target = 'self'; - } - } else { - $target = getActorName($targetID); - } - - return ($source, $verb, $target); -} - -# return ID based on name if party member is online -sub findPartyUserID { - if ($char->{party}{joined}) { - my $partyUserName = shift; - for (my $j = 0; $j < @partyUsersID; $j++) { - next if ($partyUsersID[$j] eq ""); - if ($partyUserName eq $char->{party}{users}{$partyUsersID[$j]}{name} - && $char->{party}{users}{$partyUsersID[$j]}{online}) { - return $partyUsersID[$j]; - } - } - } - - return undef; -} - -# fill in a hash of NPC information either based on location ("map x y") -sub getNPCInfo { - my $id = shift; - my $return_hash = shift; - - undef %{$return_hash}; - - my ($map, $x, $y) = split(/ +/, $id, 3); - - $$return_hash{map} = $map; - $$return_hash{pos}{x} = $x; - $$return_hash{pos}{y} = $y; - - if (($$return_hash{map} ne "") && ($$return_hash{pos}{x} ne "") && ($$return_hash{pos}{y} ne "")) { - $$return_hash{ok} = 1; - } else { - error TF("Invalid NPC information for autoBuy, autoSell or autoStorage! (%s)\n", $id); - } -} - -sub checkSelfCondition { - my $prefix = shift; - return 0 if (!$prefix); - return 0 if ($config{$prefix . "_disabled"}); - - return 0 if ($config{$prefix."_whenIdle"} && !AI::isIdle); - - return 0 if ($config{$prefix."_whenNotIdle"} && AI::isIdle); - - # TODO: Is there any situation where we should use calcPosFromPathfinding or calcPosFromTime here in these checks? - - # *_manualAI 0 = auto only - # *_manualAI 1 = manual only - # *_manualAI 2 = auto or manual - if ($config{$prefix . "_manualAI"} == 0 || !(defined $config{$prefix . "_manualAI"})) { - return 0 unless AI::state == AI::AUTO; - } elsif ($config{$prefix . "_manualAI"} == 1){ - return 0 unless AI::state == AI::MANUAL; - } else { - return 0 if AI::state == AI::OFF; - } - - if ($config{$prefix . "_hp"}) { - if ($config{$prefix."_hp"} =~ /^(.*)\%$/) { - return 0 if (!inRange($char->hp_percent, $1)); - } else { - return 0 if (!inRange($char->{hp}, $config{$prefix."_hp"})); - } - } - - if ($config{$prefix."_sp"}) { - if ($config{$prefix."_sp"} =~ /^(.*)\%$/) { - return 0 if (!inRange($char->sp_percent, $1)); - } else { - return 0 if (!inRange($char->{sp}, $config{$prefix."_sp"})); - } - } - - if ($config{$prefix . "_ap"}) { - if ($config{$prefix."_ap"} =~ /^(.*)\%$/) { - return 0 if (!inRange($char->ap_percent, $1)); - } else { - return 0 if (!inRange($char->{ap}, $config{$prefix."_ap"})); - } - } - - if ($config{$prefix."_weight"}) { - if ($config{$prefix."_weight"} =~ /^(.*)\%$/) { - return 0 if $char->{weight_max} && !inRange($char->weight_percent, $1); - } else { - return 0 if !inRange($char->{weight}, $config{$prefix."_weight"}); - } - } - - my $has_homunculus = $char->has_homunculus; - - if ($config{$prefix."_homunculus"} =~ /\S/) { - return 0 if (($config{$prefix."_homunculus"} && !$has_homunculus) || (!$config{$prefix."_homunculus"} && $has_homunculus)); - } - - if ($config{$prefix . "_homunculus_hp"}) { - return 0 unless ($has_homunculus); - if ($config{$prefix."_homunculus_hp"} =~ /^(.*)\%$/) { - return 0 if (!inRange($char->{homunculus}->hp_percent, $1)); - } else { - return 0 if (!inRange($char->{homunculus}{hp}, $config{$prefix."_homunculus_hp"})); - } - } - - if ($config{$prefix."_homunculus_sp"}) { - return 0 unless ($has_homunculus); - if ($config{$prefix."_homunculus_sp"} =~ /^(.*)\%$/) { - return 0 if (!inRange($char->{homunculus}->sp_percent, $1)); - } else { - return 0 if (!inRange($char->{homunculus}{sp}, $config{$prefix."_homunculus_sp"})); - } - } - - if ($config{$prefix."_homunculus_onAction"}) { - return 0 unless ($has_homunculus); - return 0 unless (existsInList($config{$prefix . "_homunculus_onAction"}, $char->{homunculus}->action())); - } - - if ($config{$prefix."_homunculus_notOnAction"}) { - return 0 unless ($has_homunculus); - return 0 if (existsInList($config{$prefix . "_homunculus_notOnAction"}, $char->{homunculus}->action())); - } - - if ($config{$prefix."_homunculus_whenIdle"}) { - return 0 unless ($has_homunculus); - return 0 unless ($char->{homunculus}->isIdle); - } - - if ($config{$prefix."_homunculus_whenNotIdle"}) { - return 0 unless ($has_homunculus); - return 0 if ($char->{homunculus}->isIdle); - } - - if ($config{$prefix."_homunculus_dead"} =~ /\S/) { - return 0 if (exists $char->{homunculus_info} && $config{$prefix."_homunculus_dead"} && $char->{homunculus_info}{dead} == 0); - return 0 if (exists $char->{homunculus_info} && !$config{$prefix."_homunculus_dead"} && $char->{homunculus_info}{dead} == 1); - } - - if ($config{$prefix."_homunculus_resting"} =~ /\S/) { - return 0 if (exists $char->{homunculus_info} && $config{$prefix."_homunculus_resting"} && $char->{homunculus_info}{vaporized} == 0); - return 0 if (exists $char->{homunculus_info} && !$config{$prefix."_homunculus_resting"} && $char->{homunculus_info}{vaporized} == 1); - } - - if ($config{$prefix."_homunculus_noinfo_dead"} =~ /\S/) { - return 0 if ($config{$prefix."_homunculus_noinfo_dead"} && exists $char->{homunculus_info} && exists $char->{homunculus_info}{dead} && defined $char->{homunculus_info}{dead}); - return 0 if (!$config{$prefix."_homunculus_noinfo_dead"} && (!exists $char->{homunculus_info} || !exists $char->{homunculus_info}{dead} || !defined $char->{homunculus_info}{dead})); - } - - if ($config{$prefix."_homunculus_noinfo_resting"} =~ /\S/) { - return 0 if ($config{$prefix."_homunculus_noinfo_resting"} && exists $char->{homunculus_info} && exists $char->{homunculus_info}{vaporized} && defined $char->{homunculus_info}{vaporized}); - return 0 if (!$config{$prefix."_homunculus_noinfo_resting"} && (!exists $char->{homunculus_info} || !exists $char->{homunculus_info}{vaporized} || !defined $char->{homunculus_info}{vaporized})); - } - - my $has_mercenary = $char->has_mercenary; - - if ($config{$prefix."_mercenary"} =~ /\S/) { - return 0 if (($config{$prefix."_mercenary"} && !$has_mercenary) || (!$config{$prefix."_mercenary"} && $has_mercenary)); - } - - if ($config{$prefix . "_mercenary_hp"}) { - return 0 unless ($has_mercenary); - if ($config{$prefix."_mercenary_hp"} =~ /^(.*)\%$/) { - return 0 if (!inRange($char->{mercenary}->hp_percent, $1)); - } else { - return 0 if (!inRange($char->{mercenary}{hp}, $config{$prefix."_mercenary_hp"})); - } - } - - if ($config{$prefix."_mercenary_sp"}) { - return 0 unless ($has_mercenary); - if ($config{$prefix."_mercenary_sp"} =~ /^(.*)\%$/) { - return 0 if (!inRange($char->{mercenary}->sp_percent, $1)); - } else { - return 0 if (!inRange($char->{mercenary}{sp}, $config{$prefix."_mercenary_sp"})); - } - } - - if ($config{$prefix . "_mercenary_whenStatusActive"}) { - return 0 unless ($has_mercenary); - return 0 unless $char->{mercenary}->statusActive($config{$prefix . "_mercenary_whenStatusActive"}); - } - if ($config{$prefix . "_mercenary_whenStatusInactive"}) { - return 0 unless ($has_mercenary); - return 0 if $char->{mercenary}->statusActive($config{$prefix . "_mercenary_whenStatusInactive"}); - } - - if ($config{$prefix."_mercenary_onAction"}) { - return 0 unless ($has_mercenary); - return 0 unless (existsInList($config{$prefix . "_mercenary_onAction"}, $char->{mercenary}->action())); - } - - if ($config{$prefix."_mercenary_notOnAction"}) { - return 0 unless ($has_mercenary); - return 0 if (existsInList($config{$prefix . "_mercenary_notOnAction"}, $char->{mercenary}->action())); - } - - if ($config{$prefix."_mercenary_whenIdle"}) { - return 0 unless ($has_mercenary); - return 0 unless ($char->{mercenary}->isIdle); - } - - if ($config{$prefix."_mercenary_whenNotIdle"}) { - return 0 unless ($has_mercenary); - return 0 if ($char->{mercenary}->isIdle); - } - - # check skill use SP if this is a 'use skill' condition - if ($prefix =~ /skill|attackComboSlot/i) { - my $skill = Skill->new(auto => $config{$prefix}); - if ($char->checkSkillOwnership ($skill)) { - return 0 unless ($char->getSkillLevel($skill) - || $config{$prefix."_equip_leftAccessory"} - || $config{$prefix."_equip_rightAccessory"} - || $config{$prefix."_equip_leftHand"} - || $config{$prefix."_equip_rightHand"} - || $config{$prefix."_equip_robe"} - ); - return 0 unless ($char->{sp} >= $skill->getSP($config{$prefix . "_lvl"} || $char->getSkillLevel($skill))); - - } elsif ($has_homunculus && $char->{homunculus}->checkSkillOwnership($skill)) { - return 0 unless ($char->{homunculus}->getSkillLevel($skill)); - - } elsif ($has_mercenary && $char->{mercenary}->checkSkillOwnership($skill)) { - return 0 unless ($char->{mercenary}->getSkillLevel($skill)); - } - } - - if (defined $config{$prefix . "_skill"}) { - foreach my $input (split / *, */, $config{$prefix."_skill"}) { - my ($skillName, $reqLevel) = $input =~ /(.*?)(?:\s+([><]=? *\d+))?$/; - $reqLevel = '>0' if $reqLevel eq ''; - my $skill = Skill->new(auto => $skillName); - my $skillLevel = $char->getSkillLevel($skill); - return 0 if !inRange($skillLevel, $reqLevel); - } - } - - if (defined $config{$prefix . "_aggressives"}) { - return 0 unless (inRange(scalar ai_getAggressives(), $config{$prefix . "_aggressives"})); - } - - if (defined $config{$prefix . "_partyAggressives"}) { - return 0 unless (inRange(scalar ai_getAggressives(undef, 1), $config{$prefix . "_partyAggressives"})); - } - - if ($config{$prefix . "_stopWhenHit"} > 0) { return 0 if (scalar ai_getMonstersAttacking($accountID)); } - - if ($config{$prefix . "_whenFollowing"} && $config{follow}) { - return 0 if (!checkFollowMode()); - } - - if ($config{$prefix . "_whenStatusActive"}) { - return 0 unless $char->statusActive($config{$prefix . "_whenStatusActive"}); - } - if ($config{$prefix . "_whenStatusInactive"}) { - return 0 if $char->statusActive($config{$prefix . "_whenStatusInactive"}); - } - - - if ($config{$prefix . "_inQueue"}) { return 0 unless AI::inQueue($config{$prefix . "_inQueue"}); } - if ($config{$prefix . "_notInQueue"}) { return 0 if AI::inQueue($config{$prefix . "_notInQueue"}); } - if ($config{$prefix . "_onAction"}) { return 0 unless (existsInList($config{$prefix . "_onAction"}, AI::action())); } - if ($config{$prefix . "_notOnAction"}) { return 0 if (existsInList($config{$prefix . "_notOnAction"}, AI::action())); } - if ($config{$prefix . "_spirit"}) {return 0 unless (inRange(defined $char->{spirits} ? $char->{spirits} : 0, $config{$prefix . "_spirit"})); } - if ($config{$prefix . "_amuletType"}) {return 0 unless $config{$prefix . "_amuletType"} eq $char->{amuletType}; } - - if ($config{$prefix . "_timeout"}) { return 0 unless timeOut($ai_v{$prefix . "_time"}, $config{$prefix . "_timeout"}) } - if ($config{$prefix . "_inLockOnly"} > 0) { return 0 unless ($field->baseName eq $config{lockMap}); } - if ($config{$prefix . "_notWhileSitting"} > 0) { return 0 if ($char->{sitting}); } - if ($config{$prefix . "_notWhileCasting"} > 0) { return 0 if (exists $char->{casting}); } - if ($config{$prefix . "_whileCasting"} > 0) { return 0 unless (exists $char->{casting}); } - if ($config{$prefix . "_notInTown"} > 0) { return 0 if ($field->isCity); } - if ($config{$prefix . "_inTown"} > 0) { return 0 unless ($field->isCity); } - if (defined $config{$prefix . "_monstersCount"}) { - my $nowMonsters = $monstersList->size(); - if ($nowMonsters > 0 && $config{$prefix . "_notMonsters"}) { - for my $monster (@$monstersList) { - $nowMonsters-- if (existsInList($config{$prefix . "_notMonsters"}, $monster->{name}) || - existsInList($config{$prefix . "_notMonsters"}, $monster->{nameID}) || - ($config{$prefix."_monstersCountDist"} && !inRange(blockDistance(calcPosition($char), calcPosition($monster)), $config{$prefix."_monstersCountDist"})) - ); - } - } - return 0 unless (inRange($nowMonsters, $config{$prefix . "_monstersCount"})); - } - if ($config{$prefix . "_monsters"} && !($prefix =~ /skillSlot/i) && !($prefix =~ /ComboSlot/i)) { - my $exists; - foreach (ai_getAggressives()) { - if (existsInList($config{$prefix . "_monsters"}, $monsters{$_}->name) || - existsInList($config{$prefix . "_monsters"}, $monsters{$_}->{nameID})) { - $exists = 1; - last; - } - } - return 0 unless $exists; - } - - if ($config{$prefix . "_defendMonsters"}) { - my $exists; - foreach (ai_getMonstersAttacking($accountID)) { - if (existsInList($config{$prefix . "_defendMonsters"}, $monsters{$_}->name) || - existsInList($config{$prefix . "_defendMonsters"}, $monsters{$_}->{nameID})) { - $exists = 1; - last; - } - } - return 0 unless $exists; - } - - if ($config{$prefix . "_notMonsters"} && !($prefix =~ /skillSlot/i) && !($prefix =~ /ComboSlot/i)) { - my $exists; - foreach (ai_getAggressives()) { - if (existsInList($config{$prefix . "_notMonsters"}, $monsters{$_}->name) || - existsInList($config{$prefix . "_notMonsters"}, $monsters{$_}->{nameID})) { - return 0; - } - } - } - - if ($config{$prefix."_inInventory"}) { - return 0 if (!$char->inventory->isReady()); - foreach my $input (split / *, */, $config{$prefix."_inInventory"}) { - my ($itemName, $count) = $input =~ /(.*?)(?:\s+([><]=? *\d+))?$/; - $count = '>0' if $count eq ''; - my $item = $char->inventory->getByName($itemName); - return 0 if !inRange(!$item ? 0 : $item->{amount}, $count); - } - } - - if ($config{$prefix."_inInventoryID"}) { - return 0 if (!$char->inventory->isReady()); - foreach my $input (split / *, */, $config{$prefix."_inInventoryID"}) { - my ($itemName, $count) = $input =~ /^(.*?)(?:\s*([><]=? *\d+))?$/; - $count = '>0' if $count eq ''; - my $item = $char->inventory->getByNameID($itemName); - return 0 if !inRange(!$item ? 0 : $item->{amount}, $count); - } - } - - if ($config{$prefix."_inCart"}) { - return 0 if (!$char->cart->isReady()); - foreach my $input (split / *, */, $config{$prefix."_inCart"}) { - my ($item,$count) = $input =~ /(.*?)(?:\s+([><]=? *\d+))?$/; - $count = '>0' if $count eq ''; - my $item = $char->cart->getByName($item); - return 0 if !inRange(!$item ? 0 : $item->{amount}, $count); - } - } - - if ($config{$prefix."_whenGround"}) { - return 0 unless whenGroundStatus(calcPosition($char), $config{$prefix."_whenGround"}); - } - - if ($config{$prefix."_whenNotGround"}) { - return 0 if whenGroundStatus(calcPosition($char), $config{$prefix."_whenNotGround"}); - } - - if ($config{$prefix."_whenPermitSkill"}) { - return 0 unless $char->{permitSkill} && - $char->{permitSkill}->getIDN == Skill->new(auto => $config{$prefix."_whenPermitSkill"})->getIDN; - } - - if ($config{$prefix."_whenNotPermitSkill"}) { - return 0 if $char->{permitSkill} && - $char->{permitSkill}->getIDN == Skill->new(auto => $config{$prefix."_whenNotPermitSkill"})->getIDN; - } - - if ($config{$prefix."_whenFlag"}) { - return 0 unless $flags{$config{$prefix."_whenFlag"}}; - } - if ($config{$prefix."_whenNotFlag"}) { - return 0 unless !$flags{$config{$prefix."_whenNotFlag"}}; - } - - if ($config{$prefix."_onlyWhenSafe"}) { - return 0 if !isSafe(); - } - - if (exists $config{$prefix."_nearPortal"} && defined $config{$prefix."_nearPortal"}) { - my $is_portal_near = 0; - my $pos = calcPosition($char); - for my $portal (@$portalsList) { - if (blockDistance($pos, $portal->{pos}) <= 2) { - $is_portal_near = 1; - last; - } - } - if ($config{$prefix."_nearPortal"} == 0) { - return 0 if ($is_portal_near); - - } elsif ($config{$prefix."_nearPortal"} == 1) { - return 0 unless ($is_portal_near); - } - } - - if ($config{$prefix."_inMap"}) { - return 0 unless (existsInList($config{$prefix . "_inMap"}, $field->baseName)); - } - - if ($config{$prefix."_notInMap"}) { - return 0 if (existsInList($config{$prefix . "_notInMap"}, $field->baseName)); - } - - if ($config{$prefix."_whenEquipped"}) { - my $item = Actor::Item::get($config{$prefix."_whenEquipped"}); - return 0 unless $item && $item->{equipped}; - } - - if ($config{$prefix."_whenNotEquipped"}) { - my $item = Actor::Item::get($config{$prefix."_whenNotEquipped"}); - return 0 if $item && $item->{equipped}; - } - - if ($config{$prefix."_zeny"}) { - return 0 if (!inRange($char->{zeny}, $config{$prefix."_zeny"})); - } - - if ($config{$prefix."_whenWater"}) { - my $pos = calcPosition($char); - return 0 unless ($field->isWater($pos->{x}, $pos->{y})); - } - - if ($config{$prefix.'_devotees'}) { - return 0 unless inRange(scalar keys %{$devotionList->{$accountID}{targetIDs}}, $config{$prefix.'_devotees'}); - } - - if ($config{$prefix."_whenPartyMembersNear"}) { - # Short circuit if there's not enough players nearby, party members or not - # +1 account for self - return 0 unless inRange(scalar @{$playersList} + 1, $config{$prefix."_whenPartyMembersNear"}); - - # Short circuit if there's not enough players in our party - return 0 unless inRange(scalar @partyUsersID, $config{$prefix."_whenPartyMembersNear"}); - - my $dist; - my $amountInRange = 1; # account for self - - if ($config{$prefix."_whenPartyMembersNearDist"}) { - $dist = $config{$prefix."_whenPartyMembersNearDist"}; - } else { - $dist = "< "; - $dist .= ($config{clientSight} || 15); - } - - foreach my $player (@{$playersList}) { - next unless ($char->{party}{joined} && $char->{party}{users}{$player->{ID}}); - next unless inRange(blockDistance(calcPosition($char), calcPosition($player)), $dist); - - ++$amountInRange; - } - - return 0 unless inRange($amountInRange, $config{$prefix."_whenPartyMembersNear"}); - } - - if ($config{$prefix . "_inParty"}) { - return 0 unless $char->{party}{joined}; - } - - if ($config{$prefix . "_notInParty"}) { - return 0 if $char->{party}{joined}; - } - - return 0 if ($config{$prefix . "_maxBase"} =~ /^\d{1,}$/ && $char->{lv} > $config{$prefix . "_maxBase"}); - return 0 if ($config{$prefix . "_minBase"} =~ /^\d{1,}$/ && $char->{lv} < $config{$prefix . "_minBase"}); - - my %hookArgs; - $hookArgs{prefix} = $prefix; - $hookArgs{return} = 1; - Plugins::callHook('checkSelfCondition', \%hookArgs); - return 0 if (!$hookArgs{return}); - - return 1; -} - -sub checkPlayerCondition { - my ($prefix, $id) = @_; - return 0 if (!$id); - - my $player = Actor::get($id); - return 0 unless ( - UNIVERSAL::isa($player, 'Actor::You') - || UNIVERSAL::isa($player, 'Actor::Player') - || UNIVERSAL::isa($player, 'Actor::Slave') - ); - # my $player = $playersList->getByID($id) || $slavesList->getByID($id); - - if ($config{$prefix . "_timeout"}) { return 0 unless timeOut($ai_v{$prefix . "_time"}{$id}, $config{$prefix . "_timeout"}) } - if ($config{$prefix . "_whenStatusActive"}) { - return 0 unless $player->statusActive($config{$prefix . "_whenStatusActive"}); - } - if ($config{$prefix . "_whenStatusInactive"}) { - return 0 if $player->statusActive($config{$prefix . "_whenStatusInactive"}); - } - if ($config{$prefix . "_notWhileSitting"} > 0) { return 0 if ($player->{sitting}); } - - # TODO: Optimize this - if ($config{$prefix . "_hp"}) { - # Target is Actor::You - if ($char->{ID} eq $id) { - if ($config{$prefix."_hp"} =~ /^(.*)\%$/) { - return 0 if (!inRange($char->hp_percent, $1)); - } else { - return 0 if (!inRange($char->{hp}, $config{$prefix."_hp"})); - } - # Target is Actor::Player in our Party - } elsif ($char->{party}{joined} && $char->{party}{users}{$id}) { - # Fix Heal when Target HP is not set yet. - # return 0 if (!defined($player->{hp}) || $player->{hp} == 0); - return 0 if ($char->{party}{users}{$id}{hp} == 0); - if ($config{$prefix."_hp"} =~ /^(.*)\%$/) { - # return 0 if (!inRange(percent_hp($player), $1)); - return 0 if (!inRange(percent_hp($char->{party}{users}{$id}), $1)); - } else { - # return 0 if (!inRange($player->{hp}, $config{$prefix . "_hp"})); - return 0 if (!inRange($char->{party}{users}{$id}{hp}, $config{$prefix . "_hp"})); - } - # Target is Actor::Slave 'Homunculus' type - } elsif ($char->{homunculus} && $char->{homunculus}{ID} eq $id) { - if ($config{$prefix."_hp"} =~ /^(.*)\%$/) { - return 0 if (!inRange(percent_hp($char->{homunculus}), $1)); - } else { - return 0 if (!inRange($char->{homunculus}{hp}, $config{$prefix . "_hp"})); - } - # Target is Actor::Slave 'Mercenary' type - } elsif ($char->{mercenary} && $char->{mercenary}{ID} eq $id) { - if ($config{$prefix."_hp"} =~ /^(.*)\%$/) { - return 0 if (!inRange(percent_hp($char->{mercenary}), $1)); - } else { - return 0 if (!inRange($char->{mercenary}{hp}, $config{$prefix . "_hp"})); - } - } - } - - if ($config{$prefix."_deltaHp"}){ - return 0 unless inRange($player->{deltaHp}, $config{$prefix."_deltaHp"}); - } - - # check player job class - if ($config{$prefix . "_isJob"}) { return 0 unless (existsInList($config{$prefix . "_isJob"}, $jobs_lut{$player->{jobID}})); } - if ($config{$prefix . "_isNotJob"}) { return 0 if (existsInList($config{$prefix . "_isNotJob"}, $jobs_lut{$player->{jobID}})); } - - if ($config{$prefix . "_aggressives"}) { - return 0 unless (inRange(scalar ai_getPlayerAggressives($id), $config{$prefix . "_aggressives"})); - } - - if ($config{$prefix . "_defendMonsters"}) { - my $exists; - foreach (ai_getMonstersAttacking($id)) { - if (existsInList($config{$prefix . "_defendMonsters"}, $monsters{$_}{name}) || - existsInList($config{$prefix . "_defendMonsters"}, $monsters{$_}{nameID})) { - $exists = 1; - last; - } - } - return 0 unless $exists; - } - - if ($config{$prefix . "_monsters"}) { - my $exists; - foreach (ai_getPlayerAggressives($id)) { - if (existsInList($config{$prefix . "_monsters"}, $monsters{$_}{name}) || - existsInList($config{$prefix . "_monsters"}, $monsters{$_}{nameID})) { - $exists = 1; - last; - } - } - return 0 unless $exists; - } - - if ($config{$prefix."_whenGround"}) { - return 0 unless whenGroundStatus(calcPosition($player), $config{$prefix."_whenGround"}); - } - if ($config{$prefix."_whenNotGround"}) { - return 0 if whenGroundStatus(calcPosition($player), $config{$prefix."_whenNotGround"}); - } - if ($config{$prefix."_dead"}) { - return 0 if !$player->{dead}; - } else { - return 0 if $player->{dead}; - } - - # Note: This will always fail for Actor::Slave - if ($config{$prefix."_whenWeaponEquipped"}) { - return 0 unless $player->{weapon}; - } - - # Note: This will always fail for Actor::Slave - if ($config{$prefix."_whenShieldEquipped"}) { - return 0 unless $player->{shield}; - } - - # Note: This will always fail for Actor::Slave - if ($config{$prefix."_isGuild"}) { - return 0 unless ($player->{guild} && existsInList($config{$prefix . "_isGuild"}, $player->{guild}{name})); - } - - # Note: This will always be true for Actor::Slave - # This will always be true for character that is not in any guild - if ($config{$prefix."_isNotGuild"}) { - return 0 if ($player->{guild} && existsInList($config{$prefix . "_isNotGuild"}, $player->{guild}{name})); - } - - if ($config{$prefix."_dist"}) { - return 0 unless inRange(blockDistance(calcPosition($char), calcPosition($player)), $config{$prefix."_dist"}); - } - - if ($config{$prefix."_isNotMyDevotee"}) { - return 0 if (defined $devotionList->{$accountID}->{targetIDs}->{$id}); - } - - if ($config{$prefix."_spirit"}) { - return 0 unless inRange(defined $player->{spirits} ? $player->{spirits} : 0, $config{$prefix . "_spirit"}); - } - - my %args = ( - player => $player, - prefix => $prefix, - return => 1 - ); - - Plugins::callHook('checkPlayerCondition', \%args); - - return $args{return}; -} - -sub checkMonsterCondition { - my ($prefix, $monster) = @_; - - # TODO: Is there any situation where we should use calcPosFromPathfinding or calcPosFromTime in these checks? - - if ($config{$prefix . "_hp"}) { - if($config{$prefix . "_hp"} =~ /(\d+)%$/) { - if($monster->{hp} && $monster->{hp_max}) { - return 0 unless (inRange(($monster->{hp} * 100 / $monster->{hp_max}), $config{$prefix . "_hp"})); - } elsif($monster->{hp_percent}) { - return 0 unless (inRange($monster->{hp_percent}, $config{$prefix . "_hp"})); - } else { - return 0; - } - } elsif($config{$prefix . "_hp"} =~ /(\d+)$/) { - if($monster->{hp} && $monster->{hp_max}) { - return 0 unless (inRange($monster->{hp}, $config{$prefix . "_hp"})); - } else { - return 0; - } - } else { - return 0; - } - } - - if ($config{$prefix . "_timeout"}) { return 0 unless timeOut($ai_v{$prefix . "_time"}{$monster->{ID}}, $config{$prefix . "_timeout"}) } - - if (my $misses = $config{$prefix . "_misses"}) { - return 0 unless inRange($monster->{atkMiss}, $misses); - } - - if (my $misses = $config{$prefix . "_totalMisses"}) { - return 0 unless inRange($monster->{missedFromYou}, $misses); - } - - if ($config{$prefix . "_whenStatusActive"}) { - return 0 unless $monster->statusActive($config{$prefix . "_whenStatusActive"}); - } - if ($config{$prefix . "_whenStatusInactive"}) { - return 0 if $monster->statusActive($config{$prefix . "_whenStatusInactive"}); - } - - if ($config{$prefix."_whenGround"}) { - return 0 unless whenGroundStatus(calcPosition($monster), $config{$prefix."_whenGround"}); - } - if ($config{$prefix."_whenNotGround"}) { - return 0 if whenGroundStatus(calcPosition($monster), $config{$prefix."_whenNotGround"}); - } - - if ($config{$prefix."_dist"}) { - return 0 unless inRange(blockDistance(calcPosition($char), calcPosition($monster)), $config{$prefix."_dist"}); - } - - if ($config{$prefix."_deltaHp"}){ - return 0 unless inRange($monster->{deltaHp}, $config{$prefix."_deltaHp"}); - } - - # This is only supposed to make sense for players, - # but it has to be here for attackSkillSlot PVP to work - if ($config{$prefix."_whenWeaponEquipped"}) { - return 0 unless $monster->{weapon}; - } - if ($config{$prefix."_whenShieldEquipped"}) { - return 0 unless $monster->{shield}; - } - - my %args = ( - monster => $monster, - prefix => $prefix, - return => 1 - ); - - Plugins::callHook('checkMonsterCondition', \%args); - return $args{return}; -} - -## -# makeShop() -# -# Returns an array of items to sell. The array can be no larger than the -# maximum number of items that the character can vend. Each item is a hash -# reference containing the keys "index", "amount" and "price". -# -# If there is a problem with opening a shop, an error message will be printed -# and nothing will be returned. -sub makeShop { - if ($shopstarted) { - error T("A shop has already been opened.\n"); - return; - } - - return unless $char; - - if (!$char->{skills}{MC_VENDING}{lv}) { - error T("You don't have the Vending skill.\n"); - return; - } - - if (!$char->cartActive) { - error T("You need this with a cart in order to create a shop!\n"); - return; - } - - if (!$shop{title_line}) { - error T("Your shop does not have a title.\n"); - return; - } - - my @items = (); - my $max_items = $char->{skills}{MC_VENDING}{lv} + 2; - - # Iterate through items to be sold - shuffleArray(\@{$shop{items}}) if ($config{'shop_random'} eq "2"); - my %used_items; - for my $sale (@{$shop{items}}) { - my $cart_item; - for my $item (@{$char->cart}) { - next unless $item->{name} eq $sale->{name}; - next if $used_items{$item->{binID}}; - $cart_item = $used_items{$item->{binID}} = $item; - last; - } - next unless ($cart_item); - - # Found item to vend - my $amount = $cart_item->{amount}; - - my %item; - $item{name} = $cart_item->{name}; - $item{ID} = $cart_item->{ID}; - if ($sale->{priceMax}) { - $item{price} = int(rand($sale->{priceMax} - $sale->{price})) + $sale->{price}; - } else { - $item{price} = $sale->{price}; - } - $item{amount} = - $sale->{amount} && $sale->{amount} < $amount ? - $sale->{amount} : $amount; - push(@items, \%item); - - # We can't vend anymore items - last if @items >= $max_items; - } - - if (!@items) { - error T("There are no items to sell.\n"); - return; - } - shuffleArray(\@items) if ($config{'shop_random'} eq "1"); - return @items; -} - -sub openShop { - my @items = makeShop(); - my @shopnames; - return unless @items; - @shopnames = split(/;;/, $shop{title_line}); - $shop{title} = $shopnames[int rand($#shopnames + 1)]; - $shop{title} = ($config{shopTitleOversize}) ? $shop{title} : substr($shop{title},0,36); - message T("Trying to set up shop...\n"), "vending"; - $messageSender->sendOpenShop($shop{title}, \@items); - $shopstarted = 1; - Plugins::callHook('open_shop', { - title => $shop{title}, - items => \@items - }); -} - -sub closeShop { - if (!$shopstarted) { - error T("A shop has not been opened.\n"); - return; - } - - $messageSender->sendCloseShop(); - - $shopstarted = 0; - $articles = 0; - $timeout{'ai_shop'}{'time'} = time; - Plugins::callHook('shop_closed'); - message T("Shop closed.\n"); -} - -sub makeBuyerShop { - if ($buyershopstarted) { - error T("A shop has already been opened.\n"); - return; - } - - return unless $char; - - my $max_items = 2; - my @items = (); - - if($char->inventory->getByNameID(6377)) { - $max_items = 5; - } - - # Iterate through items to be sold - shuffleArray(\@{$buyer_shop{items}}) if ($config{'buyerShop_random'} eq "2"); - my %used_items; - for my $sale (@{$buyer_shop{items}}) { - my $inventory_item; - for my $item (@{$char->inventory}) { - next unless $item->{name} eq $sale->{name}; - next if $used_items{$item->{binID}}; - $inventory_item = $used_items{$item->{binID}} = $item; - last; - } - next unless ($inventory_item); - - my %item; - $item{name} = $inventory_item->{name}; - $item{nameID} = $inventory_item->{nameID}; - if ($sale->{priceMax}) { - $item{price} = int(rand($sale->{priceMax} - $sale->{price})) + $sale->{price}; - } else { - $item{price} = $sale->{price}; - } - $item{amount} = $sale->{amount}; - push(@items, \%item); - - # We can't buy anymore items - last if @items >= $max_items; - } - - if (!@items) { - error T("There are no items to sell.\n"); - return; - } - - shuffleArray(\@items) if ($config{'buyerShop_random'} eq "1"); - - if (!$char->{skills}{ALL_BUYING_STORE}{lv}) { # don't have skill but have the necessary item - my $item = $char->inventory->getByNameID(12548); - if(!$item) { - error T("You don't have the Buying Store skill or Black Market Bulk Buyer Shop License.\n"); - return; - } else { - $item->use; - } - } elsif(!$char->inventory->getByNameID(6377)) { # have skill but don't have the necessary item - error T("You don't have Bulk Buyer Shop License.\n"); - return; - } else { # have skill and item - my $skill = new Skill(auto => "ALL_BUYING_STORE"); - $messageSender->sendSkillUse($skill->getIDN(), $skill->getLevel(), $char->{ID}); - } - - if (!$buyer_shop{title_line}) { - error T("Your buyer shop does not have a title.\n"); - return; - } - - return @items; -} - -sub openBuyerShop { - my @items = makeBuyerShop(); - my @buyershopnames; - my $limitZeny = 0; - return unless @items; - @buyershopnames = split(/;;/, $buyer_shop{title_line}); - $buyer_shop{title} = $buyershopnames[int rand($#buyershopnames + 1)]; - $buyer_shop{title} = ($config{buyerShopTitleOversize}) ? $buyer_shop{title} : substr($buyer_shop{title},0,36); - - foreach my $item (@items) { - $limitZeny += ($item->{amount} * $item->{price}); - } - - if($limitZeny > $char->{zeny}) { - $limitZeny = $char->{zeny}; - } - - Plugins::callHook ('buyer_open_shop', { - title => $buyer_shop{title}, - limitZeny=> $limitZeny, - items => \@items - }); - $messageSender->sendBuyBulkOpenShop($limitZeny, 1, $buyer_shop{title}, \@items); - - message T("Trying to set up buyer shop...\n"), "vending"; - $buyershopstarted = 1; -} - -sub closeBuyerShop { - if (!$buyershopstarted) { - error T("A Buyer Shop has not been opened.\n"); - return; - } - - $messageSender->sendCloseBuyShop(); - - $buyershopstarted = 0; - $timeout{'ai_shop'}{'time'} = time; - Plugins::callHook('buyer_shop_closed'); - message T("Buyer Shop closed.\n"); -} - -## -# inLockMap() -# -# Returns 1 (true) if character is located in its lockmap. -# Returns 0 (false) if character is not located in lockmap. -sub inLockMap { - if ($field->baseName eq $config{'lockMap'}) { - return 1; - } else { - return 0; - } -} - -sub parseReload { - my ($args) = @_; - eval { - my $progressHandler = sub { - my ($filename) = @_; - message TF("Loading %s...\n", $filename); - }; - if ($args eq 'all') { - Settings::loadAll($progressHandler); - } else { - Settings::loadByRegexp(qr/$args/, $progressHandler); - } - Log::initLogFiles(); - message T("All files were loaded\n"), "reload"; - }; - if (my $e = caught('UTF8MalformedException')) { - error TF( - "The file %s must be valid UTF-8 encoded, which it is \n" . - "currently not. To solve this prolem, please use Notepad\n" . - "to save that file as valid UTF-8.", - $e->textfile); - } elsif ($@) { - die $@; - } -} - -sub MODINIT { - OpenKoreMod::initMisc() if (defined(&OpenKoreMod::initMisc)); -} - -# There are 2 types of clients that receive deletion timestamp 'deleteDate' -# 0: As when char can be deleted -# 1: As remaining time -# -> kRO 2013 clients -# -> idRO since 2016-04-06 -sub setCharDeleteDate { - my ($slot, $deleteDate) = @_; - - return if (!$deleteDate); - - if (!defined $chars[$slot]) { - error TF("Invalid char in specified slot %d\n", $slot); - return; - } - - if ($masterServer->{charDeleteDateType} == 1) { # New clients receive deleteTime as 'time remaining' - $deleteDate = int(time) + $deleteDate; - } - - $chars[$slot]{deleteDate} = getFormattedDate($deleteDate); - $chars[$slot]{deleteDateTimestamp} = $deleteDate; -} - -sub cancelNpcBuySell { - undef %talk; - delete $ai_v{'npc_talk'} if (exists $ai_v{'npc_talk'}); - - if ($in_market) { - $messageSender->sendMarketClose; - $messageSender->sendMarketClose; - undef $in_market; - } elsif ($messageSender->{send_sell_buy_complete}) { - $messageSender->sendSellBuyComplete; - } -} - -sub completeNpcSell { - my $items = shift; - - if (@{$items}) { - $messageSender->sendSellBulk($items); - } - - undef %talk; - delete $ai_v{'npc_talk'} if (exists $ai_v{'npc_talk'}); - - if ($messageSender->{send_sell_buy_complete}) { - $messageSender->sendSellBuyComplete; - $messageSender->sendSellBuyComplete; - } -} - -sub completeNpcBuy { - my $items = shift; - - if (@{$items}) { - if ($in_market) { - $messageSender->sendBuyBulkMarket($items); - } else { - $messageSender->sendBuyBulk($items); - } - } - - undef %talk; - delete $ai_v{'npc_talk'} if (exists $ai_v{'npc_talk'}); - - if ($in_market) { - $messageSender->sendMarketClose; - $messageSender->sendMarketClose; - undef $in_market; - } elsif ($messageSender->{send_sell_buy_complete}) { - $messageSender->sendSellBuyComplete; - $messageSender->sendSellBuyComplete; - } -} - -sub CharacterLogin { - if (charSelectScreen(1) == 1) { - $firstLoginMap = 1; - $startingzeny = $chars[$config{'char'}]{'zeny'} unless defined $startingzeny; - $sentWelcomeMessage = 1; - } -} - -sub searchStoreInfo { - my ($page) = @_; - - my $msg = center(T(" Search Store Result "), 79, '-') ."\n"; - $msg .= TF("Page: %d/%d\n", $page + 1, scalar(@{$universalCatalog{list}})); - $msg .= swrite("@<< @<<<<<<<<<<<<<<<<<<< @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< @<<<<<< @>>>>>>>>>>", - ["#", T("Shop Name"), T("Item"), T("Amount"), T("Price")]); - - for (my $i = 0; $i < scalar(@{${$universalCatalog{list}}[$page]}); ++$i) { - $msg .= swrite("@<< @<<<<<<<<<<<<<<<<<<< @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< @<<<<<< @>>>>>>>>>>", - [$i, ${$universalCatalog{list}}[$page][$i]{shopName}, - itemName({ - nameID => ${$universalCatalog{list}}[$page][$i]{nameID}, - cards => ${$universalCatalog{list}}[$page][$i]{cards_nameID}, - upgrade => ${$universalCatalog{list}}[$page][$i]{refine} - }), ${$universalCatalog{list}}[$page][$i]{amount}, formatNumber(${$universalCatalog{list}}[$page][$i]{price})]); - } - $msg .= ('-'x79) . "\n"; - message $msg, "list"; -} - -my @base62_dictionary = qw(0 1 2 3 4 5 6 7 8 9 a b c d e f g h i j k l m n o p q r s t u v w x y z A B C D E F G H I J K L M N O P Q R S T U V W X Y Z); -my %base62_map = map { $base62_dictionary[$_] => $_ } 0..$#base62_dictionary; - -# Convert number to Base62 string -# [lututui] -sub toBase62 { - my ($k) = @_; - my @result; - - return "0" if ($k == 0); - - while ($k != 0) { - use integer; - unshift (@result, $base62_dictionary[$k % 62]); - $k /= 62; - } - - return join "", @result; -} - -# Convert Base62 string to number -# [lututui] -sub fromBase62 { - my ($k) = @_; - - my @arr = split "", $k; - my $base10 = 0; - - for (my $i = 0; $i < scalar @arr; ++$i) { - $base10 += $base62_map{$arr[$i]} * (62**(scalar(@arr) - $i - 1)); - } - - return $base10; -} - -sub solveMSG { - my $msg = shift; - my $id; - - if ($msg =~ /<MSG>(\d+)<\/MSG>/) { - $id = $1 + 1; - if ($msgTable[$id]) { # show message from msgstringtable.txt - debug "Replace the original message with: '$msgTable[$id]'\n"; - $msg = $msgTable[$id]; - } - } elsif ($msg =~ /<MSG>(\d+),(\d+)<\/MSG>/) { - $id = $1 + 1; - if ($msgTable[$id]) { - debug "Replace the original message with: '$msgTable[$id]'\n"; - $msg = sprintf ($msgTable[$id], $2); - } - } - warning TF("Unknown msgid: %d. Need to update the file msgstringtable.txt (from data.grf)\n", --$id) if !$msgTable[$id]; - Plugins::callHook('solveMSG', { msg => \$msg }); - return $msg; -} - -# Solve each <ITEML>.*</ITEML> to kore-style item name -sub solveItemLink { - my ($itemlstr) = @_; - - # Item ID as minimum requirement - if (!($itemlstr =~ /^([\d\w]+)(.*)/)) { - return $itemlstr; - } - - my ($itemstr, $infostr) = ($1, $2); - my ($loc, $showslots, $id) = $itemstr =~ /([\d\w]{5})(\d)([\d\w]+)/; - my ($refine) = $infostr =~ /%([\d\w]+)/; - my ($itemtype) = $infostr =~ /&([\d\w]+)/; - my $item_info = { - nameID => fromBase62($id), - upgrade => fromBase62($refine), - }; - - foreach my $card (map { $_ } $infostr =~ /\(([\d\w]+)/g) { - $item_info->{cards} .= pack('v', fromBase62($card)); - } - - foreach my $opt (map { $_ } $infostr =~ /\*([\d\w\+,]+)/g) { - # The Random Option's order from client is type-param-value, itemName needs type-value-param - my ($type, $param, $value) = $opt =~ /([a-zA-Z0-9]+)\+([a-zA-Z0-9]+),([a-zA-Z0-9]+)/; - $item_info->{options} .= pack 'vvC', ( fromBase62($type), fromBase62($value), fromBase62($param) ); - } - - return "<".itemName($item_info).">"; -} - -# Solve plain message string that contains client's special functions -sub solveMessage { - my ($msg) = @_; - - # Example: - # <ITEML>.*</ITEML> to readable item name - # Sell<ITEML>0000y1kk&0g)00)00)00)00+05,00-01+0B,00-0m</ITEML>500k - # S><ITEML>0000y1nC&05)00)00+0h,00-0C+0F,00-0h</ITEML><ITEML>000021jM&01)00)00+0h,00-0N+0B,00-0g</ITEML><ITEML>000021jM&01)00)00+0f,00-02+0B,00-0e</ITEML> - if ($msg =~ /<ITEML>([a-zA-Z0-9%&(),+\-*]*)<\/ITEML>/) { - $msg =~ s/<ITEML>([a-zA-Z0-9%&(),+\-*]*)<\/ITEML>/solveItemLink($1)/eg; - } - Plugins::callHook('solveMessage', { msg => \$msg }); - return $msg; -} - -sub absunit { - my ($x) = @_; - if ($x == 0) { - return 0; - } elsif ($x > 0) { - return 1; - } else { - return -1; - } -} - -sub autoNpcTalk { - my ($ID, $nameID) = @_; - - return if (defined AI::findAction("NPC")); - - my $routeIndex = AI::findAction("route"); - return if (defined $routeIndex && AI::args($routeIndex)->getSubtask && UNIVERSAL::isa(AI::args($routeIndex)->getSubtask, 'Task::TalkNPC')); - - my $routeIndex = AI::findAction("route", 1); - return if (defined $routeIndex && AI::args($routeIndex)->getSubtask && UNIVERSAL::isa(AI::args($routeIndex)->getSubtask, 'Task::TalkNPC')); - - debug "An unexpected npc conversation has started, auto-creating a TalkNPC Task\n"; - my $task = Task::TalkNPC->new(type => 'autotalk', nameID => $nameID, ID => $ID); - AI::queue("NPC", $task); - # TODO: The following npc_talk hook is only added on activation. - # Make the task module or AI listen to the hook instead - # and wrap up all the logic. - $task->activate; - Plugins::callHook('npc_autotalk', { - task => $task - }); -} - -sub getFlyWing { - # 12887 - Unlimited Fly Wing - # 23280 - Mosquito Wings (only if lv < 99) - # 23338 - [Event] Fly Wing - # 12323 - Novice Fly Wing - # 601 - Fly Wing - for my $id (12887, 23280, 23338, 12323, 601) { - next if $id == 23280 && $char->{lv} >= 99; - my $item = $char->inventory->getByNameID($id); - return $item if $item; - } - return undef; -} - -sub getButterflyWing { - # 12324 - Novice Butterfly Wing - # 602 - Butterfly Wing - for my $id (12324, 602) { - my $item = $char->inventory->getByNameID($id); - return $item if $item; - } - return undef; -} - -sub getEdenGroupMark { - # 22508 - Eden Group Mark - return $char->inventory->getByNameID(22508); -} - -sub print_callers { - message "[print_callers] Printing start\n"; - my @callers; - my $level = 1; - while (my @info = caller($level)) { - my $sub_name = $info[3] ? $info[3] : "TOP in $info[0]"; - push @callers, { - package => $info[0], - file => $info[1], - line => $info[2], - sub_name => $sub_name, - }; - last if @callers >= 7; - $level++; - } - - message "Last " . scalar(@callers) . " callers:\n"; - for my $i (0 .. $#callers) { - message TF("#%d: %s at %s line %d\n", - $i + 1, - $callers[$i]{sub_name}, - $callers[$i]{file}, - $callers[$i]{line}); - } - message "[print_callers] Printing end\n"; -} - -return 1; diff --git a/openkore_llm_knowledge/core/src/Network/Send.pm b/openkore_llm_knowledge/core/src/Network/Send.pm deleted file mode 100644 index 1af920bb73..0000000000 --- a/openkore_llm_knowledge/core/src/Network/Send.pm +++ /dev/null @@ -1,3589 +0,0 @@ -######################################################################### -# OpenKore - Message sending -# This module contains functions for sending messages to the RO server. -# -# This software is open source, licensed under the GNU General Public -# License, version 2. -# Basically, this means that you're allowed to modify and distribute -# this software. However, if you distribute modified versions, you MUST -# also distribute the source code. -# See http://www.gnu.org/licenses/gpl.html for the full license. -# -# $Revision$ -# $Id$ -# -######################################################################### -## -# MODULE DESCRIPTION: Sending messages to RO server -# -# This class contains convenience methods for sending messages to the RO -# server. -# -# Please also read <a href="https://openkore.com/wiki/Network_subsystem">the -# network subsystem overview.</a> -package Network::Send; - -use strict; -use Time::HiRes qw(time); -use Network::PacketParser; # import -use base qw(Network::PacketParser); -use utf8; -use Carp::Assert; -use Digest::MD5; -use Math::BigInt; - -# TODO: remove 'use Globals' from here, instead pass vars on -use Globals qw(%ai_v %config $bytesSent %packetDescriptions $enc_val1 $enc_val2 $char $masterServer $syncSync $accountID %timeout %talk $skillExchangeItem $net $rodexList $rodexWrite %universalCatalog %rpackets $mergeItemList $repairList %cashShop); - -use I18N qw(bytesToString stringToBytes); -use Utils qw(existsInList getHex getTickCount getCoordString makeCoordsDir); -use Misc; -use Log qw(debug); - -sub new { - my ( $class ) = @_; - my $self = $class->SUPER::new( @_ ); - - my $cryptKeys = $masterServer->{sendCryptKeys}; - if ( $cryptKeys && $cryptKeys =~ /^(0x[0-9A-Fa-f]{8})\s*,\s*(0x[0-9A-Fa-f]{8})\s*,\s*(0x[0-9A-Fa-f]{8})$/ ) { - $self->cryptKeys( hex $1, hex $2, hex $3 ); - } - - return $self; -} - -sub import { - # This code is for backward compatibility reasons, so that you can still - # write: - # sendFoo(\$remote_socket, args); - - my ($package) = caller; - # This is necessary for some weird reason. - return if ($package =~ /^Network::Send/); - - package Network::Send::Compatibility; - require Exporter; - our @ISA = qw(Exporter); - require Network::Send::ServerType0; - no strict 'refs'; - - our @EXPORT_OK; - @EXPORT_OK = (); - - my $class = shift; - if (@_) { - @EXPORT_OK = @_; - } else { - @EXPORT_OK = grep {/^send/} keys(%{Network::Send::ServerType0::}); - } - - foreach my $symbol (@EXPORT_OK) { - *{$symbol} = sub { - my $remote_socket = shift; - my $func = $Globals::messageSender->can($symbol); - if (!$func) { - die "No such function: $symbol"; - } else { - return $func->($Globals::messageSender, @_); - } - }; - } - Network::Send::Compatibility->export_to_level(1, undef, @EXPORT_OK); -} - -### CATEGORY: Class methods - -### CATEGORY: Methods - -## -# void $messageSender->encryptMessageID(r_message) -sub encryptMessageID { - my ($self, $r_message) = @_; - my $messageID = unpack("v", $$r_message); - my $messageID2 = uc(unpack("H2", substr($$r_message, 1, 1))) . uc(unpack("H2", substr($$r_message, 0, 1))); - - if ($self->{encryption}->{crypt_key_3}) { - if (sprintf("%04X",$messageID) eq $self->{packet_lut}{map_login}) { - $self->{encryption}->{crypt_key} = $self->{encryption}->{crypt_key_1}; - } elsif ($self->{net}->getState() != Network::IN_GAME) { - # Turn off keys - $self->{encryption}->{crypt_key} = 0; return; - } - - # Checking if Encryption is Activated - if ($self->{encryption}->{crypt_key} > 0) { - # Saving Last Informations for Debug Log - my $oldMID = $messageID; - my $oldKey = ($self->{encryption}->{crypt_key} >> 16) & 0x7FFF; - - # Calculating the Encryption Key - $self->{encryption}->{crypt_key} = ($self->{encryption}->{crypt_key} * $self->{encryption}->{crypt_key_3} + $self->{encryption}->{crypt_key_2}) & 0xFFFFFFFF; - - # Xoring the Message ID - $messageID = ($messageID ^ (($self->{encryption}->{crypt_key} >> 16) & 0x7FFF)) & 0xFFFF; - $$r_message = pack("v", $messageID) . substr($$r_message, 2); - - # Debug Log - debug (sprintf("Encrypted MID : [%04X]->[%04X] / KEY : [0x%04X]->[0x%04X]\n", $oldMID, $messageID, $oldKey, ($self->{encryption}->{crypt_key} >> 16) & 0x7FFF), "sendPacket", 0) if ($config{debugPacket_sent} || ($config{'debugPacket_include_dumpMethod'} && !existsInList($config{debugPacket_exclude}, $messageID2) && existsInList($config{'debugPacket_include'}, $messageID2))); - } - } else { - use bytes; - if ($self->{net}->getState() != Network::IN_GAME) { - $enc_val1 = 0; - $enc_val2 = 0; - return; - } - - my $messageID = unpack("v", $$r_message); - if ($enc_val1 != 0 && $enc_val2 != 0) { - # Prepare encryption - $enc_val1 = ((0x000343FD * $enc_val1) + $enc_val2)& 0xFFFFFFFF; - debug (sprintf("enc_val1 = %x", $enc_val1) . "\n", "sendPacket", 2); - # Encrypt message ID - $messageID = ($messageID ^ (($enc_val1 >> 16) & 0x7FFF)) & 0xFFFF; - $$r_message = pack("v", $messageID) . substr($$r_message, 2); - } - } -} - -sub cryptKeys { - my $self = shift; - $self->{encryption} = { - 'crypt_key_1' => Math::BigInt->new(shift), - 'crypt_key_2' => Math::BigInt->new(shift), - 'crypt_key_3' => Math::BigInt->new(shift), - }; -} - -## -# void $messageSender->injectMessage(String message) -# -# Send text message to the connected client's party chat. -sub injectMessage { - my ($self, $message) = @_; - my $name = stringToBytes("|"); - my $msg .= $name . stringToBytes(" : $message") . chr(0); - - # Packet Prefix Encryption Support - #$self->encryptMessageID(\$msg); - - $msg = pack("C*", 0x09, 0x01) . pack("v*", length($name) + length($message) + 12) . pack("C*",0,0,0,0) . $msg; - $self->{net}->clientSend($msg); -} - -## -# void $messageSender->injectAdminMessage(String message) -# -# Send text message to the connected client's system chat. -sub injectAdminMessage { - my ($self, $message) = @_; - $message = stringToBytes($message); - $message = pack("C*",0x9A, 0x00) . pack("v*", length($message)+5) . $message .chr(0); - - # Packet Prefix Encryption Support - #$self->encryptMessageID(\$message); - $self->{net}->clientSend($message); -} - -## -# String pinEncode(int seed, int pin) -# pin: the PIN code -# key: the encryption seed/key -# -# Another version of the PIN Encode Function, used to hide the real PIN code, using seed/key. -sub pinEncode { - # randomizePin function/algorithm by Kurama, ever_boy_, kLabMouse and Iniro. cleanups by Revok - my ($seed, $pin) = @_; - - $seed = Math::BigInt->new($seed); - my $mulfactor = 0x3498; - my $addfactor = 0x881234; - my @keypad_keys_order = ('0'..'9'); - - # calculate keys order (they are randomized based on seed value) - if (@keypad_keys_order >= 1) { - my $k = 2; - for (my $pos = 1; $pos < @keypad_keys_order; $pos++) { - $seed = $addfactor + $seed * $mulfactor & 0xFFFFFFFF; # calculate next seed value - my $replace_pos = $seed % $k; - if ($pos != $replace_pos) { - my $old_value = $keypad_keys_order[$pos]; - $keypad_keys_order[$pos] = $keypad_keys_order[$replace_pos]; - $keypad_keys_order[$replace_pos] = $old_value; - } - $k++; - } - } - # associate keys values with their position using a hash - my %keypad; - for (my $pos = 0; $pos < @keypad_keys_order; $pos++) { $keypad{@keypad_keys_order[$pos]} = $pos; } - my $pin_reply = ''; - my @pin_numbers = split('',$pin); - foreach (@pin_numbers) { $pin_reply .= $keypad{$_}; } - return $pin_reply; -} - -## -# void $messageSender->sendToServer(Bytes msg) -# -# Send a raw data to the server. -sub sendToServer { - my ($self, $msg) = @_; - my $net = $self->{net}; - - shouldnt(length($msg), 0); - return unless ($net->serverAlive); - - my $messageID = uc(unpack("H2", substr($msg, 1, 1))) . uc(unpack("H2", substr($msg, 0, 1))); - - my $hookName = "packet_send/$messageID"; - if (Plugins::hasHook($hookName)) { - my %args = ( - switch => $messageID, - data => $msg - ); - Plugins::callHook($hookName, \%args); - return if ($args{return}); - } - - # Packet Prefix Encryption Support - $self->encryptMessageID(\$msg); - - $net->serverSend($msg); - $bytesSent += length($msg); - - if ($config{debugPacket_sent} && !existsInList($config{debugPacket_exclude}, $messageID) && $config{debugPacket_include_dumpMethod} < 3) { - my $label = $packetDescriptions{Send}{$messageID} ? - "[$packetDescriptions{Send}{$messageID}]" : ''; - if ($config{debugPacket_sent} == 1) { - debug(sprintf("Sent packet : %-4s [%2d bytes] %s\n", $messageID, length($msg), $label), "sendPacket", 0); - } else { - Misc::visualDump($msg, ">> Sent packet: $messageID $label"); - } - } - - if ($config{'debugPacket_include_dumpMethod'} && !existsInList($config{debugPacket_exclude}, $messageID) && existsInList($config{'debugPacket_include'}, $messageID)) { - my $label = $packetDescriptions{Send}{$messageID} ? - "[$packetDescriptions{Send}{$messageID}]" : ''; - if ($config{debugPacket_include_dumpMethod} == 2) { - Misc::visualDump($msg, ">> Sent packet: $messageID $label"); - } elsif ($config{debugPacket_include_dumpMethod} == 3 && existsInList($config{'debugPacket_include'}, $messageID)) { - #Security concern: Dump only when you included the header in config - Misc::dumpData($msg, 1, 1); - } elsif ($config{debugPacket_include_dumpMethod} == 4) { - open my $dump, '>>', 'DUMP_LINE.txt'; - print $dump unpack('H*', $msg) . "\n"; - } elsif ($config{debugPacket_include_dumpMethod} == 5 && existsInList($config{'debugPacket_include'}, $messageID)) { - #Security concern: Dump only when you included the header in config - open my $dump, '>>', 'DUMP_HEAD.txt'; - print $dump sprintf("%-4s %2d %s%s\n", $messageID, length($msg), 'Send', $label); - } - } -} - -## -# void $messageSender->sendRaw(String raw) -# raw: space-delimited list of hex byte values -# -# Send a raw data to the server. -sub sendRaw { - my ($self, $raw) = @_; - my $msg; - my @raw = split / /, $raw; - foreach (@raw) { - $msg .= pack("C", hex($_)); - } - $self->sendToServer($msg); - debug "Sent Raw Packet: @raw\n", "sendPacket", 2; -} - -# parse/reconstruct callbacks and send* subs left for compatibility - -sub parse_master_login { - my ($self, $args) = @_; - - if (exists $args->{password_md5_hex}) { - $args->{password_md5} = pack 'H*', $args->{password_md5_hex}; - } - - if (exists $args->{password_rijndael}) { - my $key = pack('C24', (6, 169, 33, 64, 54, 184, 161, 91, 81, 46, 3, 213, 52, 18, 0, 6, 61, 175, 186, 66, 157, 158, 180, 48)); - my $chain = pack('C24', (61, 175, 186, 66, 157, 158, 180, 48, 180, 34, 218, 128, 44, 159, 172, 65, 1, 2, 4, 8, 16, 32, 128)); - my $in = pack('a24', $args->{password_rijndael}); - my $rijndael = Utils::Rijndael->new; - $rijndael->MakeKey($key, $chain, 24, 24); - $args->{password} = unpack("Z24", $rijndael->Decrypt($in, undef, 24, 0)); - } -} - -sub reconstruct_master_login { - my ($self, $args) = @_; - - $args->{ip} = sprintf("192.168.%02d.%02d", (map { int(rand(255)) } 1..2)) unless exists $args->{ip}; - unless (exists $args->{mac}) { - $args->{mac} = $config{macAddress} || sprintf("E0311E%02X%02X%02X", (map { int(rand(256)) } 1..3)); - $args->{mac} = uc($args->{mac}); - $args->{mac_hyphen_separated} = join '-', $args->{mac} =~ /(..)/g; - } - $args->{isGravityID} = 0 unless exists $args->{isGravityID}; - - if (exists $args->{password}) { - for (Digest::MD5->new) { - $_->add($args->{password}); - $args->{password_md5} = $_->clone->digest; - $args->{password_md5_hex} = $_->hexdigest; - } - - my $key = pack('C24', (6, 169, 33, 64, 54, 184, 161, 91, 81, 46, 3, 213, 52, 18, 0, 6, 61, 175, 186, 66, 157, 158, 180, 48)); - my $chain = pack('C24', (61, 175, 186, 66, 157, 158, 180, 48, 180, 34, 218, 128, 44, 159, 172, 65, 1, 2, 4, 8, 16, 32, 128)); - my $in = pack('a24', $args->{password}); - my $rijndael = Utils::Rijndael->new; - $rijndael->MakeKey($key, $chain, 24, 24); - $args->{password_rijndael} = $rijndael->Encrypt($in, undef, 24, 0); - } -} - -sub sendMasterLogin { - my ($self, $username, $password, $master_version, $version) = @_; - my $msg; - - if ( - $masterServer->{masterLogin_packet} eq '' - # TODO a way to select any packet, handled globally, something like "packet_<handler> <switch>"? - or $self->{packet_list}{$masterServer->{masterLogin_packet}} - && $self->{packet_list}{$masterServer->{masterLogin_packet}}[0] eq 'master_login' - && ($self->{packet_lut}{master_login} = $masterServer->{masterLogin_packet}) - ) { - $self->sendClientMD5Hash() unless $masterServer->{clientHash} eq ''; # this is a hack, just for testing purposes, it should be moved to the login algo later on - - $msg = $self->reconstruct({ - switch => 'master_login', - version => $version || $self->version, - master_version => $master_version, - username => $username, - password => $password, - game_code => '0011', # kRO Ragnarok game code - flag => 'G000', # Maybe this say that we are connecting from client - }); - } else { - $msg = pack("v1 V", hex($masterServer->{masterLogin_packet}) || 0x64, $version || $self->version) . - pack("a24", $username) . - pack("a24", $password) . - pack("C*", $master_version); - } - - $self->sendToServer($msg); - debug "Sent sendMasterLogin\n", "sendPacket", 2; -} - -sub secureLoginHash { - my ($self, $password, $salt, $type) = @_; - my $md5 = Digest::MD5->new; - - $password = stringToBytes($password); - if ($type % 2) { - $salt = $salt . $password; - } else { - $salt = $password . $salt; - } - $md5->add($salt); - - $md5->digest -} - -sub sendMasterSecureLogin { - my ($self, $username, $password, $salt, $version, $master_version, $type, $account) = @_; - - $self->{packet_lut}{master_login} ||= $type < 3 ? '01DD' : '01FA'; - - $self->sendToServer($self->reconstruct({ - switch => 'master_login', - version => $version || $self->version, - master_version => $master_version, - username => $username, - password_salted_md5 => $self->secureLoginHash($password, $salt, $type), - clientInfo => $account > 0 ? $account - 1 : 0, - })); -} - -sub reconstruct_game_login { - my ($self, $args) = @_; - $args->{userLevel} = 0 unless exists $args->{userLevel}; - ($args->{iAccountSID}) = $masterServer->{ip} =~ /\d+\.\d+\.\d+\.(\d+)/ unless exists $args->{iAccountSID}; - - if (exists $args->{mac}) { - my $key = pack('C16', (0x06, 0xA9, 0x21, 0x40, 0x36, 0xB8, 0xA1, 0x5B, 0x51, 0x2E, 0x03, 0xD5, 0x34, 0x12, 0x00, 0x06)); - my $chain = pack('C16', (0x3D, 0xAF, 0xBA, 0x42, 0x9D, 0x9E, 0xB4, 0x30, 0xB4, 0x22, 0xDA, 0x80, 0x2C, 0x9F, 0xAC, 0x41)); - my $mac = $config{macAddress} || sprintf("E0311E%02X%02X%02X", (map { int(rand(256)) } 1..3)); - $mac = uc($mac); - my $in = pack('a16', $mac); - - my $rijndael = Utils::Rijndael->new; - $rijndael->MakeKey($key, $chain, 16, 16); - $args->{mac} = $rijndael->Encrypt($in, undef, 16, 0); - } -} - -# TODO: $masterServer->{gameLogin_packet}? -sub sendGameLogin { - my ($self, $accountID, $sessionID, $sessionID2, $sex) = @_; - $self->sendToServer($self->reconstruct({ - switch => 'game_login', - accountID => $accountID, - sessionID => $sessionID, - sessionID2 => $sessionID2, - mac => '111111111111', - accountSex => $sex, - })); - debug "Sent sendGameLogin\n", "sendPacket", 2; -} - -sub sendCharLogin { - my ($self, $char) = @_; - $self->sendToServer($self->reconstruct({switch => 'char_login', slot => $char})); - debug "Sent sendCharLogin\n", "sendPacket", 2; -} - -sub sendMapLogin { - my ($self, $accountID, $charID, $sessionID, $sex) = @_; - my $msg; - $sex = 0 if ($sex > 1 || $sex < 0); # Sex can only be 0 (female) or 1 (male) - - if ($self->{serverType} == 0 || $self->{serverType} == 17 || $self->{serverType} == 18 || $self->{serverType} == 19 || - $self->{serverType} == 20 || $self->{serverType} == 21 || $self->{serverType} == 22) { - - $msg = $self->reconstruct({ - switch => 'map_login', - accountID => $accountID, - charID => $charID, - sessionID => $sessionID, - tick => getTickCount, - sex => $sex, - }); - - } else { #oRO and pRO - my $key; - - $key = pack("C*", 0xFA, 0x12, 0, 0x50, 0x83); - $msg = pack("C*", 0x72, 0, 0, 0, 0) . - $accountID . - $key . - $charID . - pack("C*", 0xFF, 0xFF) . - $sessionID . - pack("V", getTickCount()) . - pack("C", $sex); - } - - $self->sendToServer($msg); - debug "Sent sendMapLogin\n", "sendPacket", 2; -} - -sub sendMapLoaded { - my $self = shift; - $syncSync = pack("V", getTickCount()); - debug "Sending Map Loaded\n", "sendPacket"; - $self->sendToServer($self->reconstruct({switch => 'map_loaded'})); - Plugins::callHook('packet/sendMapLoaded'); -} - -sub reconstruct_sync { - my ($self, $args) = @_; - $args->{time} = getTickCount; -} - -sub sendSync { - my ($self, $initialSync) = @_; - # XKore mode 1 lets the client take care of syncing. - return if ($self->{net}->version == 1); - - $self->sendToServer($self->reconstruct({switch => 'sync'})); - debug "Sent Sync\n", "sendPacket", 2; -} - -sub parse_character_move { - my ($self, $args) = @_; - makeCoordsDir($args, $args->{coords}); -} - -sub reconstruct_character_move { - my ($self, $args) = @_; - - $args->{no_padding} = exists $args->{no_padding} ? $args->{no_padding} : $masterServer->{serverType} == 0; - - $args->{coords} = getCoordString(@{$args}{qw(x y)}, $args->{no_padding}); -} - -sub sendMove { - my ($self, $x, $y) = @_; - - $self->sendToServer($self->reconstruct({ - switch => 'character_move', - x => $x, - y => $y - })); - - debug "Sent move to: $x, $y\n", "sendPacket", 2; -} - -sub sendAction { # flag: 0 attack (once), 7 attack (continuous), 2 sit, 3 stand - my ($self, $monID, $flag) = @_; - - my %args; - $args{monID} = $monID; - $args{flag} = $flag; - # eventually we'll trow this hooking out so... - Plugins::callHook('packet_pre/sendAttack', \%args) if $flag == ACTION_ATTACK || $flag == ACTION_ATTACK_REPEAT; - Plugins::callHook('packet_pre/sendSit', \%args) if $flag == ACTION_SIT || $flag == ACTION_STAND; - if ($args{return}) { - $self->sendToServer($args{msg}); - return; - } - - $self->sendToServer($self->reconstruct({switch => 'actor_action', targetID => $monID, type => $flag})); - debug "Sent Action: " .$flag. " on: " .getHex($monID)."\n", "sendPacket", 2; -} - -sub parse_public_chat { - my ($self, $args) = @_; - $self->parseChat($args); -} - -sub reconstruct_public_chat { - my ($self, $args) = @_; - $self->reconstructChat($args); -} - -sub sendChat { - my ($self, $message) = @_; - $self->sendToServer($self->reconstruct({switch => 'public_chat', message => $message})); -} - -sub sendGetPlayerInfo { - my ($self, $ID) = @_; - $self->sendToServer($self->reconstruct({switch => 'actor_info_request', ID => $ID})); - debug "Sent get player info: ID - ".getHex($ID)."\n", "sendPacket", 2; -} - -sub sendGetCharacterName { - my ($self, $ID) = @_; - $self->sendToServer($self->reconstruct({switch => 'actor_name_request', ID => $ID})); - debug "Sent get character name: ID - ".getHex($ID)."\n", "sendPacket", 2; -} - -sub sendTalk { - my ($self, $ID) = @_; - delete $talk{msg}; - delete $talk{image}; - $self->sendToServer($self->reconstruct({switch => 'npc_talk', ID => $ID, type => 1})); - debug "Sent talk: ".getHex($ID)."\n", "sendPacket", 2; -} - -sub sendTalkCancel { - my ($self, $ID) = @_; - undef %talk; - delete $ai_v{'npc_talk'} if (exists $ai_v{'npc_talk'}); - $self->sendToServer($self->reconstruct({switch => 'npc_talk_cancel', ID => $ID})); - debug "Sent talk cancel: ".getHex($ID)."\n", "sendPacket", 2; -} - -sub sendTalkContinue { - my ($self, $ID) = @_; - $self->sendToServer($self->reconstruct({switch => 'npc_talk_continue', ID => $ID})); - debug "Sent talk continue: ".getHex($ID)."\n", "sendPacket", 2; -} - -sub sendTalkResponse { - my ($self, $ID, $response) = @_; - delete $talk{msg}; - delete $talk{image}; - $self->sendToServer($self->reconstruct({switch => 'npc_talk_response', ID => $ID, response => $response})); - debug "Sent talk respond: ".getHex($ID).", $response\n", "sendPacket", 2; -} - -sub sendTalkNumber { - my ($self, $ID, $number) = @_; - delete $talk{msg}; - delete $talk{image}; - $self->sendToServer($self->reconstruct({switch => 'npc_talk_number', ID => $ID, value => $number})); - debug "Sent talk number: ".getHex($ID).", $number\n", "sendPacket", 2; -} - -sub sendTalkText { - my ($self, $ID, $input) = @_; - delete $talk{msg}; - delete $talk{image}; - $input = stringToBytes($input); - $self->sendToServer($self->reconstruct({ - switch => 'npc_talk_text', - len => length($input)+length($ID)+5, - ID => $ID, - text => $input - })); - debug "Sent talk text: ".getHex($ID).", $input\n", "sendPacket", 2; -} - -sub parse_private_message { - my ($self, $args) = @_; - $args->{privMsg} = bytesToString($args->{privMsg}); - Misc::stripLanguageCode(\$args->{privMsg}); - $args->{privMsgUser} = bytesToString($args->{privMsgUser}); -} - -sub reconstruct_private_message { - my ($self, $args) = @_; - $args->{privMsg} = '|00' . $args->{privMsg} if $masterServer->{chatLangCode}; - $args->{privMsg} = stringToBytes($args->{privMsg}); - $args->{privMsgUser} = stringToBytes($args->{privMsgUser}); -} - -sub sendPrivateMsg { - my ($self, $user, $message) = @_; - $self->sendToServer($self->reconstruct({ switch => 'private_message', privMsg => $message, privMsgUser => $user, })); -} - -sub sendLook { - my ($self, $body, $head) = @_; - $self->sendToServer($self->reconstruct({switch => 'actor_look_at', body => $body, head => $head})); - debug "Sent look: $body $head\n", "sendPacket", 2; - @{$char->{look}}{qw(body head)} = ($body, $head); -} - -sub sendTake { - my ($self, $itemID) = @_; - $self->sendToServer($self->reconstruct({switch => 'item_take', ID => $itemID})); - debug "Sent take\n", "sendPacket", 2; -} - -sub sendDrop { - my ($self, $ID, $amount) = @_; - $self->sendToServer($self->reconstruct({switch => 'item_drop', ID => $ID, amount => $amount})); - debug sprintf("Sent drop: %s x $amount\n", unpack('v', $ID)), "sendPacket", 2; -} - -sub sendItemUse { - my ($self, $ID, $targetID) = @_; - $self->sendToServer($self->reconstruct({switch => 'item_use', ID => $ID, targetID => $targetID})); - debug sprintf("Item Use: %s\n", unpack('v', $ID)), "sendPacket", 2; -} - -# for old plugin compatibility, use sendRestart instead! -sub sendRespawn { $_[0]->sendRestart(0) } - -# for old plugin compatibility, use sendRestart instead! -sub sendQuitToCharSelect { $_[0]->sendRestart(1) } - -# 0x00b2,3,restart,2 -# type: 0=respawn ; 1=return to char select -sub sendRestart { - my ($self, $type) = @_; - $self->sendToServer($self->reconstruct({switch => 'restart', type => $type})); - debug "Sent Restart: " . ($type ? 'Quit To Char Selection' : 'Respawn') . "\n", "sendPacket", 2; -} - -sub sendStorageAdd { - my ($self, $ID, $amount) = @_; - if ($config{storageAuto_type} == 1) { - $self->sendToServer($self->reconstruct({switch => 'guild_storage_item_add', ID => $ID, amount => $amount})); - } else { - $self->sendToServer($self->reconstruct({switch => 'storage_item_add', ID => $ID, amount => $amount})); - } - debug sprintf("Sent Storage Add: %s x $amount\n", unpack('v', $ID)), "sendPacket", 2; -} - -sub sendStorageGet { - my ($self, $ID, $amount) = @_; - if ($config{storageAuto_type} == 1) { - $self->sendToServer($self->reconstruct({switch => 'guild_storage_item_remove', ID => $ID, amount => $amount})); - } else { - $self->sendToServer($self->reconstruct({switch => 'storage_item_remove', ID => $ID, amount => $amount})); - } - debug sprintf("Sent Storage Get: %s x $amount\n", unpack('v', $ID)), "sendPacket", 2; -} - -sub sendStoragePassword { - my ($self, $pass, $type) = @_; - - # $pass -> 16 byte packed hex data - - $self->sendToServer($self->reconstruct({ - switch => 'storage_password', - type => $type, - pass => $pass, - })); -} - -sub reconstruct_storage_password { - my ($self, $args) = @_; - - my $aux = pack "H*", "EC62E539BB6BBC811A60C06FACCB7EC8"; - - # $type == 2 -> change password - # $type == 3 -> check password - - if ($args->{type} == 3) { - $args->{data} = pack '(a*)*', $args->{pass}, $aux; - } elsif ($args->{type} == 2) { - $args->{data} = pack '(a*)*', $aux, $args->{pass}; - } else { - ArgumentException->throw("The 'type' argument has invalid value ($args->{type})."); - } -} - -sub parse_party_chat { - my ($self, $args) = @_; - $self->parseChat($args); -} - -sub reconstruct_party_chat { - my ($self, $args) = @_; - $self->reconstructChat($args); -} - -sub sendPartyChat { - my ($self, $message) = @_; - $self->sendToServer($self->reconstruct({switch => 'party_chat', message => $message})); -} - - -sub parse_buy_bulk_vender { - my ($self, $args) = @_; - @{$args->{items}} = map {{ amount => unpack('v', $_), itemIndex => unpack('x2 v', $_) }} unpack '(a4)*', $args->{itemInfo}; -} - -sub reconstruct_buy_bulk_vender { - my ($self, $args) = @_; - # ITEM index. There were any other indexes expected to be in item buying packet? - $args->{itemInfo} = pack '(a4)*', map { pack 'v2', @{$_}{qw(amount itemIndex)} } @{$args->{items}}; -} - -# not "buy", it sells items! -sub sendBuyBulkVender { - my ($self, $venderID, $r_array, $venderCID) = @_; - $self->sendToServer($self->reconstruct({ - switch => 'buy_bulk_vender', - venderID => $venderID, - venderCID => $venderCID, - items => $r_array, - })); - debug "Sent bulk buy vender: ".(join ', ', map {"$_->{itemIndex} x $_->{amount}"} @$r_array)."\n", "sendPacket"; -} - -sub reconstruct_buy_bulk_buyer { - my ($self, $args) = @_; - my $packet_size = $self->{buy_bulk_buyer_size} || '(a6)*'; - my $packet_unpack = $self->{buy_bulk_buyer_size_unpack} || 'a2 v2'; - $args->{itemInfo} = pack($packet_size, map { pack $packet_unpack, @{$_}{qw(ID itemID amount)} } @{$args->{items}}); -} - -sub sendBuyBulkBuyer { - my ($self, $buyerID, $r_array, $buyingStoreID) = @_; - - my $len = 12 + (scalar @{$r_array} * 8); - - $self->sendToServer($self->reconstruct({ - switch => 'buy_bulk_buyer', - len => $len, - buyerID => $buyerID, - buyingStoreID => $buyingStoreID, - items => $r_array, - })); -} - -sub sendEnteringBuyer { - my ($self, $ID) = @_; - $self->sendToServer($self->reconstruct({switch => 'buy_bulk_request', ID => $ID})); - debug "Sent Entering Buyer: ID - ".getHex($ID)."\n", "sendPacket", 2; -} - -sub sendBuyBulkOpenShop { - my ($self, $limitZeny, $result, $storeName, @items) = @_; - - my $len = 89 + (($#items + 1) * 8); - - $self->sendToServer($self->reconstruct({ - switch => 'buy_bulk_openShop', - len => $len, - limitZeny => $limitZeny, - result => $result, - storeName => $storeName, - items => @items, - })); - - debug "Sent Buyer openShop Request\n", "sendPacket", 2; -} - -sub reconstruct_buy_bulk_openShop { - my ($self, $args) = @_; - my $packet_size = $self->{buy_bulk_openShop_size} || '(a8)*'; - my $packet_unpack = $self->{buy_bulk_openShop_size_unpack} || 'v2 V'; - $args->{itemInfo} = pack $packet_size, map { pack $packet_unpack, @{$_}{qw(nameID amount price)} } @{$args->{items}}; -} - -sub sendSkillUse { - my ($self, $ID, $lv, $targetID) = @_; -### need to check Hook### - my %args; - Plugins::callHook('packet_pre/sendSkillUse', \%args); - if ($args{return}) { - $self->sendToServer($args{msg}); - return; - } -########################## - $self->sendToServer($self->reconstruct({switch => 'skill_use', lv => $lv, skillID => $ID, targetID => $targetID})); - debug "Skill Use: $ID\n", "sendPacket", 2; -} - -sub sendSkillUseLoc { - my ($self, $ID, $lv, $x, $y) = @_; - $self->sendToServer($self->reconstruct({switch => 'skill_use_location', lv => $lv, skillID => $ID, x => $x, y => $y})); - debug "Skill Use on Location: $ID, ($x, $y)\n", "sendPacket", 2; -} - -sub sendGuildMasterMemberCheck { - my ($self, $ID) = @_; - $self->sendToServer($self->reconstruct({switch => 'guild_check'})); - debug "Sent Guild Master/Member Check.\n", "sendPacket"; -} - -sub sendGuildRequestInfo { - my ($self, $page) = @_; # page 0-4 - $self->sendToServer($self->reconstruct({ - switch => 'guild_info_request', - type => $page, - })); - debug "Sent Guild Request Page : ".$page."\n", "sendPacket"; -} - -sub parse_guild_chat { - my ($self, $args) = @_; - $self->parseChat($args); -} - -sub reconstruct_guild_chat { - my ($self, $args) = @_; - $self->reconstructChat($args); -} - -sub sendGuildChat { - my ($self, $message) = @_; - $self->sendToServer($self->reconstruct({switch => 'guild_chat', message => $message})); -} - -sub sendQuit { - my $self = shift; - $self->sendToServer($self->reconstruct({switch => 'quit_request', type => 0})); - debug "Sent Quit\n", "sendPacket", 2; -} - -sub sendCloseShop { - my $self = shift; - $self->sendToServer($self->reconstruct({switch => 'shop_close'})); - debug "Shop Closed\n", "sendPacket", 2; -} - -# 0x0102,6,partychangeoption,2:4 -# 0x07D7 -# note: item share changing seems disabled in newest clients -sub sendPartyOption { - my ($self, $exp, $itemPickup, $itemDivision) = @_; - - $self->sendToServer($self->reconstruct({ - switch => 'party_setting', - exp => $exp, - itemPickup => $itemPickup, - itemDivision => $itemDivision, - })); - debug "Sent Party Option\n", "sendPacket", 2; -} - -# 0x7DA -sub sendPartyLeader { - my ($self, $ID) = @_; - - $self->sendToServer($self->reconstruct({ - switch => 'party_leader', - accountID => $ID, - })); - - debug "Sent Change Party Leader ".getHex($ID)."\n", "sendPacket", 2; -} - -# 0x802 -sub sendPartyBookingRegister { - my ($self, $level, $MapID, @jobList) = @_; - - $self->sendToServer($self->reconstruct({switch => 'booking_register', level => $level, MapID => $MapID, - job0 => @jobList[0], job1 => @jobList[1], job2 => @jobList[2], - job3 => @jobList[3], job4 => @jobList[4], job5 => @jobList[5]})); - - debug "Sent Booking Register\n", "sendPacket", 2; -} - -# 0x804 -sub sendPartyBookingReqSearch { - my ($self, $level, $MapID, $job, $LastIndex, $ResultCount) = @_; - - $job = "65535" if ($job == 0); # job null = 65535 - $ResultCount = "10" if ($ResultCount == 0); # ResultCount defaut = 10 - - $self->sendToServer($self->reconstruct({switch => 'booking_search', level => $level, MapID => $MapID, job => $job, LastIndex => $LastIndex, ResultCount => $ResultCount})); - debug "Sent Booking Search\n", "sendPacket", 2; -} - -# 0x806 -sub sendPartyBookingDelete { - my $self = shift; - $self->sendToServer($self->reconstruct({switch => 'booking_delete'})); - debug "Booking Deleted\n", "sendPacket", 2; -} - -# 0x808 -sub sendPartyBookingUpdate { - my ($self, @jobList) = @_; - - $self->sendToServer($self->reconstruct({switch => 'booking_update', job0 => @jobList[0], - job1 => @jobList[1], job2 => @jobList[2], job3 => @jobList[3], - job4 => @jobList[4], job5 => @jobList[5]})); - - debug "Sent Booking Update\n", "sendPacket", 2; -} - -# 0x0827,6 -sub sendCharDelete2 { - my ($self, $charID) = @_; - - $self->sendToServer($self->reconstruct({switch => 'char_delete2', charID => $charID})); - debug "Sent sendCharDelete2\n", "sendPacket", 2; -} - -## -# switch: 0x0829,12: '0829' => ['char_delete2_accept', 'a4 a6', [qw(charID code)]], # 12 -> kRO -# switch: 0x098F,-1: '098f' => ['char_delete2_accept', 'v a4 a*', [qw(length charID code)]], -> idRO, iRO Renewal -sub sendCharDelete2Accept { - my ($self, $charID, $code) = @_; - - $self->sendToServer($self->reconstruct({switch => 'char_delete2_accept', charID => $charID, code => $code})); -} - -sub reconstruct_char_delete2_accept { - my ($self, $args) = @_; - debug "Sent sendCharDelete2Accept. CharID: $args->{charID}, Code: $args->{code}\n", "sendPacket", 2; -} - -# 0x082B,6 -sub sendCharDelete2Cancel { - my ($self, $charID) = @_; - - $self->sendToServer($self->reconstruct({switch => 'char_delete2_cancel', charID => $charID})); - debug "Sent sendCharDelete2Cancel\n", "sendPacket", 2; -} - -sub reconstruct_client_hash { - my ($self, $args) = @_; - - if (defined $args->{code}) { - # FIXME there's packet switch in that code. How to handle it correctly? - my $code = $args->{code}; - $code =~ s/^02 04 //; - - $args->{hash} = pack 'C*', map hex, split / /, $code; - - } elsif ($args->{type}) { - if ($args->{type} == 1) { - $args->{hash} = pack('C*', 0x7B, 0x8A, 0xA8, 0x90, 0x2F, 0xD8, 0xE8, 0x30, 0xF8, 0xA5, 0x25, 0x7A, 0x0D, 0x3B, 0xCE, 0x52); - } elsif ($args->{type} == 2) { - $args->{hash} = pack('C*', 0x27, 0x6A, 0x2C, 0xCE, 0xAF, 0x88, 0x01, 0x87, 0xCB, 0xB1, 0xFC, 0xD5, 0x90, 0xC4, 0xED, 0xD2); - } elsif ($args->{type} == 3) { - $args->{hash} = pack('C*', 0x42, 0x00, 0xB0, 0xCA, 0x10, 0x49, 0x3D, 0x89, 0x49, 0x42, 0x82, 0x57, 0xB1, 0x68, 0x5B, 0x85); - } elsif ($args->{type} == 4) { - $args->{hash} = pack('C*', 0x22, 0x37, 0xD7, 0xFC, 0x8E, 0x9B, 0x05, 0x79, 0x60, 0xAE, 0x02, 0x33, 0x6D, 0x0D, 0x82, 0xC6); - } elsif ($args->{type} == 5) { - $args->{hash} = pack('C*', 0xc7, 0x0A, 0x94, 0xC2, 0x7A, 0xCC, 0x38, 0x9A, 0x47, 0xF5, 0x54, 0x39, 0x7C, 0xA4, 0xD0, 0x39); - } - } -} - -# TODO: clientHash and secureLogin_requestCode is almost the same, merge -sub sendClientMD5Hash { - my ($self) = @_; - $self->sendToServer($self->reconstruct({ - switch => 'client_hash', - hash => pack('H32', $masterServer->{clientHash}), # ex 82d12c914f5ad48fd96fcf7ef4cc492d (kRO sakray != kRO main) - })); -} - -sub parse_actor_move { - my ($self, $args) = @_; - makeCoordsDir($args, $args->{coords}); -} - -sub reconstruct_actor_move { - my ($self, $args) = @_; - - $args->{no_padding} = exists $args->{no_padding} ? $args->{no_padding} : !($masterServer->{serverType} > 0); - - $args->{coords} = getCoordString(@{$args}{qw(x y)}, $args->{no_padding}); -} - -sub sendSlaveMove { - my ($self, $ID, $x, $y) = @_; - - $self->sendToServer($self->reconstruct({ - switch => 'actor_move', - ID => $ID, - x => $x, - y => $y, - })); - - debug sprintf("Sent %s move to: %d, %d\n", Actor::get($ID), $x, $y), "sendPacket", 2; -} - -sub sendFriendListReply { - my ($self, $accountID, $charID, $flag) = @_; - $self->sendToServer($self->reconstruct({ - switch => 'friend_response', - friendAccountID => $accountID, - friendCharID => $charID, - type => $flag, - })); - debug "Sent Reject friend request\n", "sendPacket"; -} - -sub sendFriendRequest { - my ($self, $name) = @_; - my $binName = stringToBytes($name); - $binName = substr($binName, 0, 24) if (length($binName) > 24); - $binName = $binName . chr(0) x (24 - length($binName)); - $self->sendToServer($self->reconstruct({ - switch => 'friend_request', - username => $binName, - - })); - debug "Sent Request to be a friend: $name\n", "sendPacket"; -} - -sub sendHomunculusCommand { - my ($self, $command, $type) = @_; # $type is ignored, $command can be 0:get stats, 1:feed or 2:fire - $self->sendToServer($self->reconstruct({ - switch => 'homunculus_command', - commandType => $type, - commandID => $command, - })); - debug "Sent Homunculus Command $command", "sendPacket", 2; -} - -sub sendPartyJoinRequestByName -{ - my ($self, $name) = @_; - - $self->sendToServer($self->reconstruct({ - switch => 'party_join_request_by_name', - partyName => stringToBytes ($name), - })); - - debug "Sent Request Join Party (by name): $name\n", "sendPacket", 2; -} - -sub sendSkillSelect { - my ($self, $skillID, $why) = @_; - $self->sendToServer($self->reconstruct({ - switch => 'skill_select', - skillID => $skillID, - why => $why, - })); - debug sprintf("Sent Skill Select (skillID: %d, why: %d)", $skillID, $why), 'sendPacket', 2; -} - -sub sendReplySyncRequestEx { - my ($self, $SyncID) = @_; - # Packing New Message and Dispatching - - $self->sendToServer(pack("v", $SyncID)); - # Debug Log - # print "Dispatching Sync Ex Reply : 0x" . $pid . "\n"; - # Debug Log - debug "Sent Reply Sync Request Ex\n", "sendPacket", 2; -} - -sub sendLoginPinCode { - my ($self, $seed, $type) = @_; - - my $pin = pinEncode($seed, $config{loginPinCode}); - my $msg; - if ($type == 0) { - $msg = $self->reconstruct({ - switch => 'send_pin_password', - accountID => $accountID, - pin => $pin, - }); - } elsif ($type == 1) { - $msg = $self->reconstruct({ - switch => 'new_pin_password', - accountID => $accountID, - pin => $pin, - }); - } - $self->sendToServer($msg); - $timeout{charlogin}{time} = time; - debug "Sent loginPinCode\n", "sendPacket", 2; -} - -sub sendCloseBuyShop { - my $self = shift; - $self->sendToServer($self->reconstruct({switch => 'buy_bulk_closeShop'})); - debug "Buying Shop Closed\n", "sendPacket", 2; -} - -sub sendRequestCashItemsList { - my $self = shift; - $self->sendToServer($self->reconstruct({switch => 'request_cashitems'})); - debug "Requesting cashItemsList\n", "sendPacket", 2; -} - -sub sendCashShopOpen { - my ($self) = @_; - $self->sendToServer($self->reconstruct({switch => 'cash_shop_open'})); - debug "Requesting sendCashShopOpen\n", "sendPacket", 2; -} - -sub sendCashShopClose { - my ($self) = @_; - $self->sendToServer($self->reconstruct({switch => 'cash_shop_close'})); - undef $cashShop{points}; - debug "Requesting sendCashShopClose\n", "sendPacket", 2; -} - -sub sendCashBuy { - my ($self, $kafra_points, $items) = @_; - - $self->sendToServer($self->reconstruct({ - switch => 'cash_shop_buy', - kafra_points => $kafra_points, - count => scalar @{$items}, - items => $items, - })); - - debug "Requesting cash shop buy\n", "sendPacket", 2; -} - -sub reconstruct_cash_shop_buy { - my ($self, $args) = @_; - - $args->{buy_info} = pack '(a*)*', map { pack 'V V v', $_->{nameID}, $_->{amount}, $_->{tab} } @{$args->{items}}; - # Some older clients (prior to 2013, I don't know the exact date) use 'v3' instead of 'V2 v' - lututui - # $args->{buy_info} = pack '(a*)*', map { pack 'v v v', $_->{nameID}, $_->{amount}, $_->{tab} } @{$args->{items}}; -} - -sub sendShowEquipPlayer { - my ($self, $ID) = @_; - $self->sendToServer($self->reconstruct({ - switch => 'view_player_equip_request', - ID => $ID - } - ) - ); - debug "Sent Show Equip Player.\n", "sendPacket", 2; -} - -# Send configurations (CZ_CONFIG). -# 02D8 <type>.L <value>.L -# type: -# 0 = show equip windows to other players -# 1 = being summoned by skills: Urgent Call, Romantic Rendezvous, Come to me, honey~ & Let's Go, Family! -# 2 = pet autofeeding -# 3 = homunculus autofeeding -# value: -# 0 = disabled -# 1 = enabled -sub sendMiscConfigSet { - my ($self, $type, $flag) = @_; - - $self->sendToServer($self->reconstruct({ - switch => 'misc_config_set', - type => $type, - flag => $flag - } - ) - ); - - debug sprintf("Sent Misc Config Type: %s Flag: %s.\n", $type, $flag), "sendPacket", 2; -} - -sub sendSlaveAttack { - my $self = shift; - my $slaveID = shift; - my $targetID = shift; - my $flag = shift; - $self->sendToServer($self->reconstruct({ - switch => 'slave_attack', - slaveID => $slaveID, - targetID => $targetID, - flag => $flag - } - ) - ); - debug "Sent Slave attack: ".getHex($targetID)."\n", "sendPacket", 2; -} - -sub sendSlaveStandBy { - my $self = shift; - my $slaveID = shift; - $self->sendToServer($self->reconstruct({ - switch => 'slave_move_to_master', - slaveID => $slaveID - } - ) - ); - debug "Sent Slave standby\n", "sendPacket", 2; -} - -# Request to equip an item -# 00A9 <index>.W <position>.W (CZ_REQ_WEAR_EQUIP). -# 0998 <index>.W <position>.L (CZ_REQ_WEAR_EQUIP_V5) -sub sendEquip { - my ($self, $ID, $type) = @_; - $self->sendToServer($self->reconstruct({ - switch => 'send_equip', - ID => $ID, - type => $type - } - ) - ); - debug sprintf("Sent Equip: %s Type: $type\n", unpack('v', $ID)), 2; -} - -# Request to add an equip to the equip switch window -# 0A97 <index>.W <position>.L -sub sendEquipSwitchAdd { - my ($self, $ID, $position) = @_; - - $self->sendToServer($self->reconstruct({ - switch => 'equip_switch_add', - ID => $ID, - position => $position, - })); - - debug sprintf("Sent Equip Switch Add Item: %s\n", unpack('v', $ID)), "sendPacket", 2; -} - -# Request to remove an equip from the equip switch window -# 0A99 <index>.W <position>.L <= 20170502 -# 0A99 <index>.W -sub sendEquipSwitchRemove { - my ($self, $ID, $position) = @_; - - $self->sendToServer($self->reconstruct({ - switch => 'equip_switch_remove', - ID => $ID, - position => $position, - })); - - debug sprintf("Sent Equip Switch Remove Item: %s\n", unpack('v', $ID)), "sendPacket", 2; -} - -# Request to do a full equip switch -# 0A9C -sub sendEquipSwitchRun { - my ($self) = @_; - - $self->sendToServer($self->reconstruct({ - switch => 'equip_switch_run' - })); - - debug "Sent Equip Switch All\n", "sendPacket", 2; -} - -# Request to do a single equip switch -# 0ACE <index>.W -sub sendEquipSwitchSingle { - my ($self, $ID) = @_; - - $self->sendToServer($self->reconstruct({ - switch => 'equip_switch_single', - ID => $ID - })); - - debug sprintf("Sent Equip Switch Single Item: %s\n", unpack('v', $ID)), "sendPacket", 2; -} - -sub sendProgress { - my ($self) = @_; - - $self->sendToServer($self->reconstruct({switch => 'notify_progress_bar_complete'})); - - debug "Sent Progress Bar Finish\n", "sendPacket", 2; -} - -sub sendDealAddItem { - my ($self, $ID, $amount) = @_; - $self->sendToServer($self->reconstruct({ - switch => 'deal_item_add', - ID => $ID, - amount => $amount - })); - debug sprintf("Sent Deal Add Item: %s, $amount\n", unpack('v', $ID)), "sendPacket", 2; -} - -## -# sendItemListWindowSelected -# @param num Number of items -# @param type 0: Change Material -# 1: Elemental Analysis (Level 1: Pure to Rough) -# 2: Elemental Analysis (Level 1: Rough to Pure) -# @param act 0: Cancel -# 1: Process -# @param items List of items [itemIndex,amount,itemName] -# @author [Cydh] -## -sub sendItemListWindowSelected { - my ($self, $num, $type, $act, $items) = @_; - my $len = ($num * 4) + 12; - $self->sendToServer($self->reconstruct({ - switch => 'item_list_window_selected', - len => $len, - type => $type, - act => $act, - items => $items, - })); - if ($act == 1) { - debug "Selected ".(scalar @{$items})." items: ".(join ', ', map {"".$_->{amount}." x ".$_->{itemName}." (binID:".$_->{itemIndex}.")"} @{$items})."\n", "sendPacket"; - } else { - debug "Selected items were canceled.\n", "sendPacket"; - } - undef $skillExchangeItem; -} - -sub reconstruct_item_list_window_selected { - my ($self, $args) = @_; - $args->{itemInfo} = pack '(a4)*', map { pack 'v2', @{$_}{qw(itemIndex amount)} } @{$args->{items}}; -} - -# Select equip for refining -# '0AA1' => ['refineui_select', 'a2' ,[qw(index)]], -# @param itemIndex OpenKore's Inventory Item Index -# @author [Cydh] -sub sendRefineUISelect { - my ($self, $itemIndex) = @_; - $self->sendToServer($self->reconstruct({ - switch => 'refineui_select', - index => $itemIndex, - })); - debug "Checking item for RefineUI\n", "sendPacket"; -} - -# Continue to refine equip -# '0AA3' => ['refineui_refine', 'a2 v C' ,[qw(index catalyst bless)]], -# @param itemIndex OpenKore's Inventory Item Index -# @param materialNameIDMaterial's NameID -# @param useCatalyst Catalyst (Blacksmith Blessing) toggle. 0 = Not using, 1 = Use catalyst -# @author [Cydh] -sub sendRefineUIRefine { - my ($self, $itemIndex, $materialNameID, $useCatalyst) = @_; - $self->sendToServer($self->reconstruct({ - switch => 'refineui_refine', - index => $itemIndex, - catalyst => $materialNameID, - bless => $useCatalyst, - })); - debug "Refining using RefineUI\n", "sendPacket"; -} - -# Cancel RefineUI usage -# '0AA4' => ['refineui_close', '' ,[qw()]], -# @author [Cydh] -sub sendRefineUIClose { - my $self = shift; - $self->sendToServer($self->reconstruct({switch => 'refineui_close'})); - debug "Closing RefineUI\n", "sendPacket"; -} - -sub sendTokenToServer { - my ($self, $username, $password, $master_version, $version, $token, $length, $ip, $port) = @_; - my $len = $length + 92; - - my $password_rijndael = $self->encrypt_password($password); - my $ip = '192.168.0.14'; - my $mac = '20CF3095572A'; - my $mac_hyphen_separated = join '-', $mac =~ /(..)/g; - - $net->serverDisconnect(); - $net->serverConnect($ip, $port);# OTP - One Time Password - - my $msg = $self->reconstruct({ - switch => 'token_login', - len => $len, # size of packet - version => $version || $self->version, - master_version => $master_version, - username => $username, - password => $password, - password_rijndael => $password_rijndael, - mac => $mac_hyphen_separated, - ip => $ip, - token => $token, - }); - - $self->sendToServer($msg); - - debug "Sent sendTokenLogin\n", "sendPacket", 2; -} - -# Send OTP code for authentication (packet 0C23) -# 0C23 <otp>.a6 <padding>.C -# otp: 6-digit One-Time Password (TOTP) -# padding: 0x00 (always 0) -sub sendOtpToServer { - my ($self, $otp) = @_; - - $self->sendToServer($self->reconstruct({ - switch => 'send_otp_login', - otp => $otp, - padding => 0x00, - })); - - debug "Sent OTP login packet: $otp\n", "sendPacket", 2; -} - -# encrypt password kRO/cRO version 2017-2018 -sub encrypt_password { - my ($self, $password) = @_; - my $password_rijndael; - if (defined $password) { - my $key = pack('C32', (0x06, 0xA9, 0x21, 0x40, 0x36, 0xB8, 0xA1, 0x5B, 0x51, 0x2E, 0x03, 0xD5, 0x34, 0x12, 0x00, 0x06, 0x06, 0xA9, 0x21, 0x40, 0x36, 0xB8, 0xA1, 0x5B, 0x51, 0x2E, 0x03, 0xD5, 0x34, 0x12, 0x00, 0x06)); - my $chain = pack('C32', (0x3D, 0xAF, 0xBA, 0x42, 0x9D, 0x9E, 0xB4, 0x30, 0xB4, 0x22, 0xDA, 0x80, 0x2C, 0x9F, 0xAC, 0x41, 0x3D, 0xAF, 0xBA, 0x42, 0x9D, 0x9E, 0xB4, 0x30, 0xB4, 0x22, 0xDA, 0x80, 0x2C, 0x9F, 0xAC, 0x41)); - my $in = pack('a32', $password); - my $rijndael = Utils::Rijndael->new; - $rijndael->MakeKey($key, $chain, 32, 32); - $password_rijndael = unpack("Z32", $rijndael->Encrypt($in, undef, 32, 0)); - return $password_rijndael; - } else { - error("Password is not configured"); - } -} - -sub sendReqRemainTime { - my ($self) = @_; - - my $msg = $self->reconstruct({ - switch => 'request_remain_time', - }); - - $self->sendToServer($msg); -} - -sub sendBlockingPlayerCancel { - my ($self) = @_; - # XKore mode 1 / 3. - return if ($self->{net}->version == 1); - my $msg = $self->reconstruct({ - switch => 'blocking_play_cancel', - }); - - $self->sendToServer($msg); -} - - -sub rodex_delete_mail { - my ($self, $type, $mailID1, $mailID2) = @_; - $self->sendToServer($self->reconstruct({ - switch => 'rodex_delete_mail', - type => $type, - mailID1 => $mailID1, - mailID2 => $mailID2, - })); -} - -sub rodex_request_zeny { - my ($self, $mailID1, $mailID2, $type) = @_; - $self->sendToServer($self->reconstruct({ - switch => 'rodex_request_zeny', - mailID1 => $mailID1, - mailID2 => $mailID2, - type => $type, - })); -} - -sub rodex_request_items { - my ($self, $mailID1, $mailID2, $type) = @_; - $self->sendToServer($self->reconstruct({ - switch => 'rodex_request_items', - mailID1 => $mailID1, - mailID2 => $mailID2, - type => $type, - })); -} - -sub rodex_cancel_write_mail { - my ($self) = @_; - $self->sendToServer($self->reconstruct({ - switch => 'rodex_cancel_write_mail', - })); - undef $rodexWrite; -} - -sub rodex_add_item { - my ($self, $ID, $amount) = @_; - $self->sendToServer($self->reconstruct({ - switch => 'rodex_add_item', - ID => $ID, - amount => $amount, - })); -} - -sub rodex_remove_item { - my ($self, $ID, $amount) = @_; - $self->sendToServer($self->reconstruct({ - switch => 'rodex_remove_item', - ID => $ID, - amount => $amount, - })); -} - -sub rodex_open_write_mail { - my ($self, $name) = @_; - $self->sendToServer($self->reconstruct({ - switch => 'rodex_open_write_mail', - name => stringToBytes($name), - })); -} - -sub rodex_checkname { - my ($self, $name) = @_; - $self->sendToServer($self->reconstruct({ - switch => 'rodex_checkname', - name => stringToBytes($name), - })); -} - -#note ! -#merge sub of 0A6E / 09EC ? [sctnightcore] -sub rodex_send_mail { - my ($self) = @_; - - my $title = stringToBytes($rodexWrite->{title}) . chr(0); - my $body = stringToBytes($rodexWrite->{body}) . chr(0); - my $pack = $self->reconstruct({ - switch => 'rodex_send_mail', - receiver => $rodexWrite->{target}{name}, - sender => stringToBytes($char->{name}), - zeny1 => $rodexWrite->{zeny}, - zeny2 => 0, - title_len => length $title, - body_len => length $body, - char_id => $rodexWrite->{target}{char_id}, - title => $title, - body => $body, - }); - - $self->sendToServer($pack); -} - -sub rodex_refresh_maillist { - my ($self, $type, $mailID1, $mailID2) = @_; - $self->sendToServer($self->reconstruct({ - switch => 'rodex_refresh_maillist', - type => $type, - mailID1 => $mailID1, - mailID2 => $mailID2, - # seems that is not current used by client/server 2019-09-16 - mailReturnID1 => 0, - mailReturnID2 => 0, - mailAccountID1 => 0, - mailAccountID2 => 0, - })); -} - -sub rodex_read_mail { - my ($self, $type, $mailID1, $mailID2) = @_; - $self->sendToServer($self->reconstruct({ - switch => 'rodex_read_mail', - type => $type, - mailID1 => $mailID1, - mailID2 => $mailID2, - })); -} - -sub rodex_next_maillist { - my ($self, $type, $mailID1, $mailID2) = @_; - $self->sendToServer($self->reconstruct({ - switch => 'rodex_next_maillist', - type => $type, - mailID1 => $mailID1, - mailID2 => $mailID2, - })); -} - -sub rodex_open_mailbox { - my ($self, $type, $mailID1, $mailID2) = @_; - $self->sendToServer($self->reconstruct({ - switch => 'rodex_open_mailbox', - type => $type, - mailID1 => $mailID1, - mailID2 => $mailID2, - # seems that is not current used by client/server 2019-09-16 - mailReturnID1 => 0, - mailReturnID2 => 0, - mailAccountID1 => 0, - mailAccountID2 => 0, - })); -} - -sub rodex_close_mailbox { - my ($self) = @_; - $self->sendToServer($self->reconstruct({ - switch => 'rodex_close_mailbox', - })); - undef $rodexList; -} - -sub sendEnteringVender { - my ($self, $accountID) = @_; - $self->sendToServer($self->reconstruct({ - switch => 'send_entering_vending', - accountID => $accountID, - })); -} - -sub sendUnequip { - my ($self, $ID) = @_; - $self->sendToServer($self->reconstruct({ - switch => 'send_unequip_item', - ID => $ID, - })); -} - -sub sendAddStatusPoint { - my ($self, $ID,$Amount) = @_; - $self->sendToServer($self->reconstruct({ - switch => 'send_add_status_point', - statusID => $ID, - Amount => '1', - })); -} - -sub sendAddSkillPoint { - my ($self, $skillID) = @_; - $self->sendToServer($self->reconstruct({ - switch => 'send_add_skill_point', - skillID => $skillID, - })); -} - -sub sendHotKeyChange { - my ($self, $args) = @_; - - $self->sendToServer($self->reconstruct({ - switch => 'hotkey_change', - idx => $args->{idx}, - type => $args->{type}, - id => $args->{id}, - lvl => $args->{lvl}, - })); -} - -sub sendQuestState { - my ($self, $questID,$state) = @_; - $self->sendToServer($self->reconstruct({ - switch => 'send_quest_state', - questID => $questID, - state => $state, #TODO:[active=0x00],[inactive=0x01] - })); - debug "Sent Quest State.\n", "sendPacket", 2; -} - -sub sendClanChat { - my ($self, $message) = @_; - $message = $char->{name}." : ".$message; - $self->sendToServer($self->reconstruct({switch => 'clan_chat', len => length($message) + 4,message => $message})); -} - -sub sendchangetitle { - my ($self, $title_id) = @_; - $self->sendToServer($self->reconstruct({ - switch => 'send_change_title', - ID => $title_id, - })); - debug "Sent Change Title.\n", "sendPacket", 2; -} - -sub sendRecallSso { - my ($self, $accountID) = @_; - $self->sendToServer($self->reconstruct({ - switch => 'recall_sso', - ID => $accountID, - })); -} - -sub sendRemoveAidSso { - my ($self, $accountID) = @_; - $self->sendToServer($self->reconstruct({ - switch => 'remove_aid_sso', - ID => $accountID, - })); -} - -sub sendMacroStart { - my ($self) = @_; - $self->sendToServer($self->reconstruct({ - switch => 'macro_start', - })); -} - -sub sendMacroStop { - my ($self) = @_; - $self->sendToServer($self->reconstruct({ - switch => 'macro_stop', - })); -} - -sub sendReqCashTabCode { - my ($self, $tabID) = @_; - $self->sendToServer($self->reconstruct({ - switch => 'req_cash_tabcode', - ID => $tabID, - })); -} - -sub parse_pet_evolution { - my ($self, $args) = @_; - @{$args->{items}} = map {{ itemIndex => unpack('v', $_), amount => unpack('x2 v', $_) }} unpack '(a4)*', $args->{itemInfo}; -} - -sub reconstruct_pet_evolution { - my ($self, $args) = @_; - $args->{itemInfo} = pack '(a4)*', map { pack 'v2', @{$_}{qw(itemIndex amount)} } @{$args->{items}}; -} - -sub sendPetEvolution { - my ($self, $peteggid, $r_array) = @_; - $self->sendToServer($self->reconstruct({ - switch => 'pet_evolution', - ID => $peteggid, - items => $r_array, - })); -} - -sub sendWeaponRefine { - my ($self, $ID) = @_; - - my $msg = $self->reconstruct({ - switch => 'refine_item', - ID => $ID, - }); - - $self->sendToServer($msg); - - debug "Sent Weapon Refine.\n", "sendPacket", 2; -} - -sub sendCooking { - my ($self, $type, $nameID) = @_; - $self->sendToServer($self->reconstruct({ - switch => 'cook_request', - nameID => $nameID, - type => $type, - })); - debug "Sent Cooking.\n", "sendPacket", 2; -} - -sub sendMakeItemRequest { - my ($self, $nameID, $material_nameID1, $material_nameID2, $material_nameID3) = @_; - $self->sendToServer($self->reconstruct({ - switch => 'make_item_request', - nameID => $nameID, - material_nameID1 => $material_nameID1, - material_nameID2 => $material_nameID2, - material_nameID3 => $material_nameID3, - })); - debug "Sent Make Item Request.\n", "sendPacket", 2; -} - -sub sendSearchStoreClose { - my ($self, $args) = @_; - - $self->sendToServer($self->reconstruct({switch => 'search_store_close'})); - - $universalCatalog{open} = 0; - - debug "Sent search store close\n", "sendPacket", 2; -} - -sub sendSearchStoreSearch { - my ($self, $args) = @_; - - $self->sendToServer($self->reconstruct({ - switch => 'search_store_info', - type => $args->{type}, - max_price => $args->{max_price}, - min_price => $args->{min_price}, - item_list => \@{$args->{item_list}}, - card_list => \@{$args->{card_list}}, - })); - - debug "Sent search store search\n", "sendPacket", 2; -} - -sub reconstruct_search_store_info { - my ($self, $args) = @_; - - $args->{item_count} = scalar(@{$args->{item_list}}); - $args->{card_count} = scalar(@{$args->{card_list}}); - - my @id_list = (@{$args->{item_list}}, @{$args->{card_list}}); - - $args->{item_card_list} = pack "(a*)*", map { pack "v", $_ } @id_list; -} - -sub sendSearchStoreRequestNextPage { - my ($self, $args) = @_; - - $self->sendToServer($self->reconstruct({switch => 'search_store_request_next_page'})); - - debug "Sent search store next page request\n", "sendPacket", 2; -} - -sub sendSearchStoreSelect { - my ($self, $args) = @_; - - $self->sendToServer($self->reconstruct({ - switch => 'search_store_select', - accountID => $args->{accountID}, - storeID => $args->{storeID}, - nameID => $args->{nameID}, - })); - - debug "Sent search store select request\n", "sendPacket", 2; -} - -sub sendNoviceDoriDori { - my ($self, $args) = @_; - - $self->sendToServer($self->reconstruct({switch => 'novice_dori_dori'})); - - debug "Sent Novice Dori Dori\n", "sendPacket", 2; -} - -sub sendChangeDress { - my ($self, $args) = @_; - - $self->sendToServer($self->reconstruct({switch => 'change_dress'})); - - debug "Sent Change Dress\n", "sendPacket", 2; -} - -sub sendFriendRemove { - my ($self, $accountID, $charID) = @_; - - $self->sendToServer($self->reconstruct({ - switch => 'friend_remove', - accountID => $accountID, - charID => $charID, - })); - - debug "Sent Remove a friend\n", "sendPacket"; -} - -sub sendRepairItem { - my ($self, $args) = @_; - - $self->sendToServer($self->reconstruct({ - switch => 'repair_item', - index => $args->{index}, - nameID => $args->{nameID}, - upgrade => $args->{upgrade}, - cards => $args->{cards}, - })); - debug ("Sent repair item index: ".$args->{index}."\n", "sendPacket", 2); -} - -sub sendAdoptRequest { - my ($self, $ID) = @_; - - $self->sendToServer($self->reconstruct({ - switch => 'adopt_request', - ID => $ID, - })); - - debug "Sent Adoption Request.\n", "sendPacket", 2; -} - -sub sendAdoptReply { - my ($self, $parentID1, $parentID2, $result) = @_; - - $self->sendToServer($self->reconstruct({ - switch => 'adopt_reply_request', - parentID1 => $parentID1, - parentID2 => $parentID2, - result => $result - })); - - debug "Sent Adoption Reply.\n", "sendPacket", 2; -} - -sub sendPrivateAirshipRequest { - my ($self, $map_name, $nameID) = @_; - - $self->sendToServer($self->reconstruct({ - switch => 'private_airship_request', - map_name => stringToBytes($map_name), - nameID => $nameID, - })); -} - -sub sendNoviceExplosionSpirits { - my ($self) = @_; - - $self->sendToServer($self->reconstruct({switch => 'novice_explosion_spirits'})); - - debug "Sent Novice Explosion Spirits\n", "sendPacket", 2; -} - -sub sendBanCheck { - my ($self, $ID) = @_; - - $self->sendToServer($self->reconstruct({ - switch => 'ban_check', - accountID => $ID, - })); - - debug "Sent Account Ban Check Request : " . getHex($ID) . "\n", "sendPacket", 2; -} - -sub sendChangeCart { - my ($self, $lvl) = @_; - - # lvl: 1..5 - $self->sendToServer($self->reconstruct({ - switch => 'change_cart', - lvl => $lvl, - })); - - debug "Sent Cart Change to : $lvl\n", "sendPacket", 2; -} - -sub sendArrowCraft { - my ($self, $nameID) = @_; - - $self->sendToServer($self->reconstruct({ - switch => 'make_arrow', - nameID => $nameID, - })); - - debug "Sent Arrowmake: $nameID\n", "sendPacket", 2; -} - -sub sendAutoSpell { - my ($self, $ID) = @_; - - $self->sendToServer($self->reconstruct({ - switch => 'auto_spell', - ID => $ID, - })); -} - -sub sendEmotion { - my ($self, $ID) = @_; - - $self->sendToServer($self->reconstruct({ - switch => 'send_emotion', - ID => $ID, - })); - - debug "Sent Emotion\n", "sendPacket", 2; -} - -sub sendWho { - my ($self) = @_; - - $self->sendToServer($self->reconstruct({switch => 'request_user_count'})); - debug "Sent Who (User Count)\n", "sendPacket", 2; -} - -sub sendNPCBuySellList { - my ($self, $ID, $type) = @_; - - # type: 0 get store list - # type: 1 get sell list - $self->sendToServer($self->reconstruct({ - switch => 'request_buy_sell_list', - ID => $ID, - type => $type, - })); - - debug "Sent get ".($type ? "buy" : "sell")." list to NPC: ".getHex($ID)."\n", "sendPacket", 2; -} - -sub sendIgnore { - my ($self, $name, $flag) = @_; - - my $nameToBytes = stringToBytes($name); - - $self->sendToServer($self->reconstruct({ - switch => 'ignore_player', - name => $nameToBytes, - flag => $flag, - })); - - debug "Sent Ignore: $name, $flag\n", "sendPacket", 2; -} - -sub sendIgnoreAll { - my ($self, $flag) = @_; - - $self->sendToServer($self->reconstruct({ - switch => 'ignore_all', - flag => $flag, - })); - - debug "Sent Ignore All: $flag\n", "sendPacket", 2; -} - -sub sendGetIgnoreList { - my ($self) = @_; - - $self->sendToServer($self->reconstruct({switch => 'get_ignore_list'})); - - debug "Sent get Ignore List.\n", "sendPacket", 2; -} - -sub sendChatRoomCreate { - my ($self, $title, $limit, $public, $password) = @_; - - $self->sendToServer($self->reconstruct({ - switch => 'chat_room_create', - limit => $limit, - public => $public, - password => stringToBytes($password), - title => stringToBytes($title), - })); - - debug "Sent Create Chat Room: $title, $limit, $public, $password\n", "sendPacket", 2; -} - -sub sendChatRoomJoin { - my ($self, $ID, $password) = @_; - - $self->sendToServer($self->reconstruct({ - switch => 'chat_room_join', - ID => $ID, - password => stringToBytes($password), - })); - - debug "Sent Join Chat Room: ".getHex($ID).", $password\n", "sendPacket", 2; -} - -sub sendChatRoomChange { - my ($self, $title, $limit, $public, $password) = @_; - - $self->sendToServer($self->reconstruct({ - switch => 'chat_room_change', - limit => $limit, - public => $public, - password => stringToBytes($password), - title => stringToBytes($title), - })); - - debug "Sent Change Chat Room: $title, $limit, $public, $password\n", "sendPacket", 2; -} - -sub sendChatRoomBestow { - my ($self, $name) = @_; - - $self->sendToServer($self->reconstruct({ - switch => 'chat_room_bestow', - name => stringToBytes($name), - - # There are two roles: - # 0 means 'admin' - # 1 means 'normal (not-admin)' - # - # Weirdly, you can only bestow the chat window if you are admin (role 0), - # and in the official client you cannot try to bestow the chat window UNLESS - # you're admin - so it always sends role 0 - # In rA and Hercules, this info is not used at all, instead it's checked whether - # you're actually the chat window admin or not. This might be exploitable in - # official servers (by lying that you're admin when you're not) but I never cared - # enough to test - lututui, Aug 2018 - role => 0, - })); - - debug "Sent Chat Room Bestow: $name\n", "sendPacket", 2; -} - -sub sendChatRoomKick { - my ($self, $name) = @_; - - $self->sendToServer($self->reconstruct({ - switch => 'chat_room_kick', - name => stringToBytes($name), - })); - - debug "Sent Chat Room Kick: $name\n", "sendPacket", 2; -} - -sub sendChatRoomLeave { - my ($self) = @_; - - $self->sendToServer($self->reconstruct({switch => 'chat_room_leave'})); - - debug "Sent Leave Chat Room\n", "sendPacket", 2; -} - -sub sendDeal { - my ($self, $ID) = @_; - - $self->sendToServer($self->reconstruct({ - switch => 'deal_initiate', - ID => $ID, - })); - - debug "Sent Initiate Deal: ".getHex($ID)."\n", "sendPacket", 2; -} - -sub sendDealReply { - my ($self, $action) = @_; - - $self->sendToServer($self->reconstruct({ - switch => 'deal_reply', - - # Action values: - # 0: Char is too far - # 1: Character does not exist - # 2: Trade failed - # 3: Accept - # 4: Cancel - # - # Weird enough, the client should only send 3/4 - # and the server is the one that can reply 0~2 - technologyguild, Dec 2009 - action => $action, - })); - - debug "Sent Deal Reply (Action: $action)\n", "sendPacket", 2; -} - -sub sendDealFinalize { - my ($self) = @_; - - $self->sendToServer($self->reconstruct({switch => 'deal_finalize'})); - - debug "Sent Deal Finalize\n", "sendPacket", 2; -} - -sub sendCurrentDealCancel { - my ($self) = @_; - - $self->sendToServer($self->reconstruct({switch => 'deal_cancel'})); - - debug "Sent Cancel Current Deal\n", "sendPacket", 2; -} - -sub sendDealTrade { - my ($self) = @_; - - $self->sendToServer($self->reconstruct({switch => 'deal_trade'})); - - debug "Sent Deal Trade\n", "sendPacket", 2; -} - -sub sendStorageClose { - my ($self) = @_; - - $self->sendToServer($self->reconstruct({switch => 'storage_close'})); - - debug "Sent Storage Close\n", "sendPacket", 2; -} - -sub sendPartyJoinRequest { - my ($self, $ID) = @_; - - $self->sendToServer($self->reconstruct({ - switch => 'party_join_request', - ID => $ID, - })); - - debug "Sent Party Request Join: ".getHex($ID)."\n", "sendPacket", 2; -} - -sub sendPartyJoin { - my ($self, $ID, $flag) = @_; - - $self->sendToServer($self->reconstruct({ - switch => 'party_join', - ID => $ID, - flag => $flag, - })); - - debug "Sent Party Join: ".getHex($ID).", $flag\n", "sendPacket", 2; -} - -sub sendPartyLeave { - my ($self) = @_; - - $self->sendToServer($self->reconstruct({switch => 'party_leave'})); - - debug "Sent Party Leave\n", "sendPacket", 2; -} - -sub sendPartyKick { - my ($self, $ID, $name) = @_; - - $self->sendToServer($self->reconstruct({ - switch => 'party_kick', - ID => $ID, - name => stringToBytes($name), - })); - - debug "Sent Party Kick: ".getHex($ID).", $name\n", "sendPacket", 2; -} - -sub sendMemo { - my ($self) = @_; - - $self->sendToServer($self->reconstruct({switch => 'memo_request'})); - - debug "Sent Memo\n", "sendPacket", 2; -} - -sub sendCompanionRelease { - my ($self) = @_; - - $self->sendToServer($self->reconstruct({switch => 'companion_release'})); - - debug "Sent Companion Release (Cart, Falcon or Pecopeco)\n", "sendPacket", 2; -} - -sub sendCartAdd { - my ($self, $ID, $amount) = @_; - - $self->sendToServer($self->reconstruct({ - switch => 'cart_add', - ID => $ID, - amount => $amount, - })); - - debug "Sent Cart Add: " . getHex($ID) . " x $amount\n", "sendPacket", 2; -} - -sub sendCartGet { - my ($self, $ID, $amount) = @_; - - $self->sendToServer($self->reconstruct({ - switch => 'cart_get', - ID => $ID, - amount => $amount, - })); - - debug "Sent Cart Get: " . getHex($ID) . " x $amount\n", "sendPacket", 2; -} - -sub sendIdentify { - my ($self, $ID) = @_; - - $self->sendToServer($self->reconstruct({ - switch => 'identify', - ID => $ID, - })); - - debug "Sent Identify: ".getHex($ID)."\n", "sendPacket", 2; -} - -sub sendCardMergeRequest { - my ($self, $cardID) = @_; - - $self->sendToServer($self->reconstruct({ - switch => 'card_merge_request', - cardID => $cardID, - })); - - debug "Sent Card Merge Request: " . getHex($cardID) . "\n", "sendPacket", 2; -} - -sub sendCardMerge { - my ($self, $cardID, $itemID) = @_; - - $self->sendToServer($self->reconstruct({ - switch => 'card_merge', - cardID => $cardID, - itemID => $itemID, - })); - - debug "Sent Card Merge: " . getHex($cardID) . ", " . getHex($itemID) . "\n", "sendPacket", 2; -} - -sub sendCharCreate { - my $self = shift; - my ($slot, $name, $str, $agi, $vit, $int, $dex, $luk, $hair_style, $hair_color, $job_id, $sex); - - if ($self->{packet_lut}{char_create} eq '0067') { - ($slot, $name, $str, $agi, $vit, $int, $dex, $luk, $hair_style, $hair_color) = @_; - } elsif ($self->{packet_lut}{char_create} eq '0970') { - ($slot, $name, $hair_style, $hair_color) = @_; - } elsif ($self->{packet_lut}{char_create} eq '0A39') { - ($slot, $name, $hair_style, $hair_color, $job_id, $sex) = @_; - $job_id ||= 0; # novice - $sex ||= 0; # female - } - $hair_color ||= 1; - $hair_style ||= 0; - - $self->sendToServer($self->reconstruct({ - switch => 'char_create', - name => stringToBytes($name), - str => $str, - agi => $agi, - vit => $vit, - int => $int, - dex => $dex, - luk => $luk, - slot => $slot, - hair_color => $hair_color, - hair_style => $hair_style, - job_id => $job_id, - sex => $sex - })); - - debug "Sent Char Create\n", "sendPacket", 2; -} - -sub sendCharDelete { - my ($self, $charID, $email) = @_; - - $self->sendToServer($self->reconstruct({ - switch => 'char_delete', - charID => $charID, - email => stringToBytes($email), - })); - - debug "Sent Char Delete\n", "sendPacket", 2; -} - -sub sendGuildAlly { - my ($self, $ID, $flag) = @_; - - $self->sendToServer($self->reconstruct({ - switch => 'guild_alliance_reply', - ID => $ID, - flag => $flag, - })); - - debug "Sent Ally Guild : ".getHex($ID).", $flag\n", "sendPacket", 2; -} - -sub sendGuildRequestEmblem { - my ($self, $guildID) = @_; - - $self->sendToServer($self->reconstruct({ - switch => 'guild_emblem_request', - guildID => $guildID, - })); - - debug "Sent Guild Request Emblem.\n", "sendPacket"; -} - -sub sendGuildBreak { - my ($self, $guildName) = @_; - - $self->sendToServer($self->reconstruct({ - switch => 'guild_break', - guildName => stringToBytes($guildName), - })); - - debug "Sent Guild Break: $guildName\n", "sendPacket", 2; -} - -sub sendWarpTele { - my ($self, $skillID, $map) = @_; - - $self->sendToServer($self->reconstruct({ - switch => 'warp_select', - # skillID: - # 26 => Teleport (Respawn/Random) - # 27 => Open Warp - skillID => $skillID, - mapName => stringToBytes($map), - })); - - debug "Sent ". ($skillID == 26 ? "Teleport" : "Open Warp") . "\n", "sendPacket", 2 -} - -sub sendStorageGetToCart { - my ($self, $ID, $amount) = @_; - if ($config{storageAuto_type} == 1) { - $self->sendToServer($self->reconstruct({ - switch => 'guild_storage_to_cart', - ID => $ID, - amount => $amount, - })); - } else { - $self->sendToServer($self->reconstruct({ - switch => 'storage_to_cart', - ID => $ID, - amount => $amount, - })); - } - - debug "Sent Storage Get From Cart: " . getHex($ID) . " x $amount\n", "sendPacket", 2; -} - -sub sendStorageAddFromCart { - my ($self, $ID, $amount) = @_; - if ($config{storageAuto_type} == 1) { - $self->sendToServer($self->reconstruct({ - switch => 'cart_to_guild_storage', - ID => $ID, - amount => $amount, - })); - } else { - $self->sendToServer($self->reconstruct({ - switch => 'cart_to_storage', - ID => $ID, - amount => $amount, - })); - } - - debug "Sent Storage Add From Cart: " . getHex($ID) . " x $amount\n", "sendPacket", 2; -} - -sub sendHomunculusName { - my ($self, $name) = @_; - - $self->sendToServer($self->reconstruct({ - switch => 'homunculus_name', - name => stringToBytes($name), - })); - - debug "Sent Homunculus Rename: $name\n", "sendPacket", 2; -} - -sub sendGuildLeave { - my ($self, $reason, $guildID, $charID) = @_; - - $self->sendToServer($self->reconstruct({ - switch => 'guild_leave', - guildID => $guildID, - accountID => $accountID, - charID => $charID, - reason => stringToBytes($reason), - })); - - debug "Sent Guild Leave: $reason\n", "sendPacket"; -} - -sub sendGuildMemberKick { - my ($self, $guildID, $accountID, $charID, $reason) = @_; - - $self->sendToServer($self->reconstruct({ - switch => 'guild_kick', - guildID => $guildID, - charID => $charID, - accountID => $accountID, - reason => stringToBytes($reason), - })); - - debug "Sent Guild Kick: ".getHex($charID)."\n", "sendPacket"; -} - -sub sendGuildCreate { - my ($self, $name, $charID) = @_; - - $self->sendToServer($self->reconstruct({ - switch => 'guild_create', - charID => $charID, - guildName => stringToBytes($name), - })); - - debug "Sent Guild Create: $name\n", "sendPacket", 2; -} - -sub sendGuildJoin { - my ($self, $ID, $flag) = @_; - - $self->sendToServer($self->reconstruct({ - switch => 'guild_join', - ID => $ID, - flag => $flag, - })); - - debug "Sent Join Guild : ".getHex($ID).", $flag\n", "sendPacket"; -} - -sub sendGuildJoinRequest { - my ($self, $ID, $charID) = @_; - - $self->sendToServer($self->reconstruct({ - switch => 'guild_join_request', - ID => $ID, - accountID => $accountID, - charID => $charID, - })); - - debug "Sent Request Join Guild: ".getHex($ID)."\n", "sendPacket"; -} - -sub sendGuildNotice { - my ($self, $guildID, $name, $notice) = @_; - - $self->sendToServer($self->reconstruct({ - switch => 'guild_notice', - guildID => $guildID, - name => stringToBytes($name), - notice => stringToBytes($notice), - })); - - debug "Sent Change Guild Notice: $notice\n", "sendPacket", 2; -} - -sub sendGuildSetAlly { - my ($self, $targetAID, $myAID, $charID) = @_; - - # this packet is for guildmaster asking to set alliance with another guildmaster - # the other sub for sendGuildAlly are responses to this sub - # kept the parameters open, but everything except $targetAID could be replaced with Global variables - # unless you plan to mess around with the alliance packet, no exploits though, I tried ;-) - # -zdivpsa - - $self->sendToServer($self->reconstruct({ - switch => 'guild_alliance_request', - targetAccountID => $targetAID, - accountID => $myAID, - charID => $charID, - })); - - debug "Sent Guild Alliance Request\n", "sendPacket", 2; -} - -sub sendPetCapture { - my ($self, $monID) = @_; - - $self->sendToServer($self->reconstruct({ - switch => 'pet_capture', - ID => $monID, - })); - debug "Sent pet capture: ".getHex($monID)."\n", "sendPacket", 2; -} - -sub sendPetMenu { - my ($self, $type) = @_; - - $self->sendToServer($self->reconstruct({ - switch => 'pet_menu', - - # Action - # 0 => info - # 1 => feed - # 2 => performance - # 3 => return to egg - # 4 => unequip accessory - action => $type, - })); - - debug "Sent Pet Menu\n", "sendPacket", 2; -} - -sub sendPetHatch { - my ($self, $ID) = @_; - - $self->sendToServer($self->reconstruct({ - switch => 'pet_hatch', - ID => $ID, - })); - - debug "Sent Incubator hatch: " . getHex($ID) . "\n", "sendPacket", 2; -} - -sub sendPetName { - my ($self, $name) = @_; - - $self->sendToServer($self->reconstruct({ - switch => 'pet_name', - name => stringToBytes($name), - })); - - debug "Sent Pet Rename: $name\n", "sendPacket", 2; -} - -sub sendPetEmotion { - my ($self, $ID) = @_; - - $self->sendToServer($self->reconstruct({ - switch => 'pet_emotion', - ID => $ID, - })); - - debug "Sent Pet Emotion: $ID\n", "sendPacket", 2; -} - - -sub sendBuyBulk { - my ($self, $r_array) = @_; - - $self->sendToServer($self->reconstruct({ - switch => 'buy_bulk', - items => \@{$r_array}, - })); - - debug("Sent bulk buy: $_->{itemID} x $_->{amount}\n", "sendPacket", 2) foreach (@{$r_array}); -} - -sub reconstruct_buy_bulk { - my ($self, $args) = @_; - my $pack = $self->{send_buy_bulk_pack} || "v2"; - - $args->{buyInfo} = pack "(a*)*", map { pack $pack, $_->{amount}, $_->{itemID} } @{$args->{items}}; -} - -sub sendSellBulk { - my ($self, $r_array) = @_; - - $self->sendToServer($self->reconstruct({ - switch => 'sell_bulk', - items => \@{$r_array}, - })); - - debug("Sent bulk sell: " . getHex($_->{ID}) . " x $_->{amount}\n", "sendPacket", 2) foreach (@{$r_array}); -} - -sub reconstruct_sell_bulk { - my ($self, $args) = @_; - - $args->{sellInfo} = pack "(a*)*", map { pack "a2 v", $_->{ID}, $_->{amount} } @{$args->{items}}; -} - -sub sendAchievementGetReward { - my ($self, $achievementID) = @_; - - $self->sendToServer($self->reconstruct({ - switch => 'achievement_get_reward', - achievementID => $achievementID, - })); -} - -sub sendTop10Alchemist { - my ($self) = @_; - - if (!$masterServer->{rankingSystemType}) { - $self->sendToServer($self->reconstruct({switch => 'rank_alchemist'})); - } else { - $self->sendTop10(1); - } - - debug "Sent Top 10 Alchemist request\n", "sendPacket", 2; -} - -sub sendTop10Blacksmith { - my ($self) = @_; - - if (!$masterServer->{rankingSystemType}) { - $self->sendToServer($self->reconstruct({switch => 'rank_blacksmith'})); - } else { - $self->sendTop10(0); - } - - debug "Sent Top 10 Blacksmith request\n", "sendPacket", 2; -} - -sub sendTop10PK { - my ($self) = @_; - - if (!$masterServer->{rankingSystemType}) { - $self->sendToServer($self->reconstruct({switch => 'rank_killer'})); - } else { - $self->sendTop10(3); - } - - debug "Sent Top 10 PK request\n", "sendPacket", 2; -} - -sub sendTop10Taekwon { - my ($self) = @_; - - if (!$masterServer->{rankingSystemType}) { - $self->sendToServer($self->reconstruct({switch => 'rank_taekwon'})); - } else { - $self->sendTop10(2); - } - - debug "Sent Top 10 Taekwon request\n", "sendPacket", 2; -} - -sub sendTop10 { - my ($self, $type) = @_; - - $self->sendToServer($self->reconstruct({ - switch => 'rank_general', - - # Type: - # 0 => Blacksmith - # 1 => Alchemist - # 2 => Taekwon - # 3 => PK - type => $type, - })); -} - -sub sendGMSummon { - my ($self, $playerName) = @_; - - $self->sendToServer($self->reconstruct({ - switch => 'gm_summon_player', - playerName => stringToBytes($playerName), - })); -} - -sub sendGMBroadcast { - my ($self, $message) = @_; - - $self->sendToServer($self->reconstruct({ - switch => 'gm_broadcast', - - # to colorize, add in front of message: micc | ssss | blue | tool ? - message => stringToBytes($message), - })); -} - -sub sendGMKick { - my ($self, $accountID) = @_; - - $self->sendToServer($self->reconstruct({ - switch => 'gm_kick', - targetAccountID => $accountID, - })); -} - -sub sendGMKickAll { - my ($self) = @_; - - $self->sendToServer($self->reconstruct({switch => 'gm_kick_all'})); -} - -sub sendGMMonsterItem { - my ($self, $name) = @_; - - $self->sendToServer($self->reconstruct({ - switch => 'gm_item_mob_create', - name => stringToBytes($name), - })); -} - -sub sendGMMapMove { - my ($self, $name, $x, $y) = @_; - - $self->sendToServer($self->reconstruct({ - switch => 'gm_move_to_map', - mapName => stringToBytes($name), - x => $x, - y => $y, - })); -} - -sub sendGMResetStateSkill { - my ($self, $type) = @_; - - $self->sendToServer($self->reconstruct({ - switch => 'gm_reset_state_skill', - - # type - # 0 => status - # 1 => skills - type => $type, - })); - - debug "Sent GM Reset State/Skill.\n", "sendPacket", 2; -} - -sub sendGMChangeMapType { - my ($self, $x, $y, $type) = @_; - - $self->sendToServer($self->reconstruct({ - switch => 'gm_change_cell_type', - x => $x, - y => $y, - - # type - # 0 => not walkable - # 1 => walkable - type => $type, - })); - - debug "Sent GM Change Map Type.\n", "sendPacket", 2; -} - -sub sendGMBroadcastLocal { - my ($self, $message) = @_; - - $self->sendToServer($self->reconstruct({ - switch => 'gm_broadcast_local', - message => stringToBytes($message), - })); -} - -sub sendGMChangeEffectState { - my ($self, $effect_state) = @_; - - $self->sendToServer($self->reconstruct({ - switch => 'gm_change_effect_state', - effect_state => $effect_state - })); - - debug "Sent GM Hide.\n", "sendPacket", 2; -} - -sub sendGMRemove { - my ($self, $playerName) = @_; - - $self->sendToServer($self->reconstruct({ - switch => 'gm_remove', - playerName => stringToBytes($playerName), - })); -} - -sub sendGMShift { - my ($self, $playerName) = @_; - - $self->sendToServer($self->reconstruct({ - switch => 'gm_shift', - playerName => stringToBytes($playerName), - })); -} - -sub sendGMRecall { - my ($self, $playerName) = @_; - - $self->sendToServer($self->reconstruct({ - switch => 'gm_recall', - playerName => stringToBytes($playerName), - })); -} - -sub sendAlignment { - my ($self, $ID, $alignment, $point) = @_; - - $self->sendToServer($self->reconstruct({ - switch => 'alignment', - targetID => $ID, - type => $alignment, - point => $point, - })); - - debug "Sent Alignment: ".getHex($ID).", $alignment\n", "sendPacket", 2; -} - -sub sendOpenShop { - my ($self, $title, $items) = @_; - - $self->sendToServer($self->reconstruct({ - switch => 'shop_open', - title => stringToBytes($title), - result => 1, - items => $items, - })); -} - -sub reconstruct_shop_open { - my ($self, $args) = @_; - - $args->{vendingInfo} = pack "(a*)*", map { pack "a2 v V", $_->{ID}, $_->{amount}, $_->{price} } @{$args->{items}}; -} - -sub sendMailboxOpen { - my ($self) = @_; - - $self->sendToServer($self->reconstruct({switch => 'mailbox_open'})); - - debug "Sent mailbox open.\n", "sendPacket", 2; -} - -sub sendMailRead { - my ($self, $mailID) = @_; - - $self->sendToServer($self->reconstruct({ - switch => 'mail_read', - mailID => $mailID, - })); - - debug "Sent read mail.\n", "sendPacket", 2; -} - -sub sendMailDelete { - my ($self, $mailID) = @_; - - $self->sendToServer($self->reconstruct({ - switch => 'mail_delete', - mailID => $mailID, - })); - - debug "Sent delete mail.\n", "sendPacket", 2; -} - -sub sendMailGetAttach { - my ($self, $mailID) = @_; - - $self->sendToServer($self->reconstruct({ - switch => 'mail_attachment_get', - mailID => $mailID, - })); - - debug "Sent mail get attachment.\n", "sendPacket", 2; -} - -sub sendMailOperateWindow { - my ($self, $flag) = @_; - - $self->sendToServer($self->reconstruct({ - switch => 'mail_remove', - flag => $flag, - })); - - debug "Sent mail remove item/zeny.\n", "sendPacket", 2; -} - -sub sendMailSetAttach { - my ($self, $amount, $ID) = @_; - - # Before setting an attachment, we must remove any zeny/item that was attached but the mail wasn't sent - # Otherwise the attachment will be lost - if ($ID) { - $self->sendMailOperateWindow(1); - } else { - $self->sendMailOperateWindow(2); - } - - $AI::temp::mailAttachAmount = $amount; - $self->sendToServer($self->reconstruct({ - switch => 'mail_attachment_set', - ID => $ID, - amount => $amount, - })); - - debug "Sent mail set attachment.\n", "sendPacket", 2; -} - -sub sendMailSend { - my ($self, $receiver, $title, $message) = @_; - - $self->sendToServer($self->reconstruct({ - switch => 'mail_send', - recipient => stringToBytes($receiver), - title => stringToBytes($title), - body_len => length $message > 255 ? 255 : length $message, - body => $message, - })); - - debug "Sent mail send.\n", "sendPacket", 2; -} - -sub reconstruct_mail_send { - my ($self, $args) = @_; - - $args->{body} = pack "Z" . $args->{body_len}, stringToBytes($args->{body}); -} - -sub sendMailReturn { - my ($self, $mailID, $sender) = @_; - - $self->sendToServer($self->reconstruct({ - switch => 'mail_return', - mailID => $mailID, - sender => $sender, - })); - - debug "Sent return mail.\n", "sendPacket", 2; -} - -sub sendAuctionAddItemCancel { - my ($self, $flag) = @_; - - $flag ||= 1; - - $self->sendToServer($self->reconstruct({ - switch => 'auction_add_item_cancel', - flag => $flag, - })); - - debug "Sent Auction Add Item Cancel.\n", "sendPacket", 2; -} - -sub sendAuctionAddItem { - my ($self, $ID, $amount) = @_; - - $self->sendToServer($self->reconstruct({ - switch => 'auction_add_item', - ID => $ID, - amount => $amount, - })); - - debug "Sent Auction Add Item.\n", "sendPacket", 2; -} - -sub sendAuctionCreate { - my ($self, $now_price, $max_price, $delete_time) = @_; - - $self->sendToServer($self->reconstruct({ - switch => 'auction_create', - now_price => $now_price, - max_price => $max_price, - delete_time => $delete_time, - })); - - debug "Sent Auction Create.\n", "sendPacket", 2; -} - -sub sendAuctionCancel { - my ($self, $ID) = @_; - - $self->sendToServer($self->reconstruct({ - switch => 'auction_cancel', - ID => $ID, - })); - - debug "Sent Auction Cancel.\n", "sendPacket", 2; -} - -sub sendAuctionBuy { - my ($self, $ID, $price) = @_; - - $self->sendToServer($self->reconstruct({ - switch => 'auction_buy', - ID => $ID, - price => $price, - })); - - debug "Sent Auction Buy.\n", "sendPacket", 2; -} - -sub sendAuctionItemSearch { - my ($self, $type, $price, $search_string, $page) = @_; - $page ||= 1; - - $self->sendToServer($self->reconstruct({ - switch => 'auction_search', - price => $price, - search_string => stringToBytes($search_string), - page => $page, - - # type - # 0 => armor - # 1 => weapon - # 2 => card - # 3 => misc - # 4 => name - # 5 => auction id - type => $type, - })); - - debug "Sent Auction Item Search.\n", "sendPacket", 2; -} - -sub sendAuctionReqMyInfo { - my ($self, $type) = @_; - - $self->sendToServer($self->reconstruct({ - switch => 'auction_info_self', - type => $type, - })); - - debug "Sent Auction Request My Info.\n", "sendPacket", 2; -} - -sub sendAuctionMySellStop { - my ($self, $ID) = @_; - - $self->sendToServer($self->reconstruct({ - switch => 'auction_sell_stop', - ID => $ID, - })); - - debug "Sent My Sell Stop.\n", "sendPacket", 2; -} - -sub sendPartyJoinRequestByNameReply { - my ($self, $accountID, $flag) = @_; - - $self->sendToServer($self->reconstruct({ - switch => 'party_join_request_by_name_reply', - accountID => $accountID, - flag => $flag, - })); - - debug "Sent reply Party Invite.\n", "sendPacket", 2; -} - -sub sendAutoRevive { - my ($self) = @_; - - $self->sendToServer($self->reconstruct({switch => 'auto_revive'})); - - debug "Sent Auto Revive.\n", "sendPacket", 2; -} - -sub sendBattlegroundChat { - my ($self, $message) = @_; - - $self->sendToServer($self->reconstruct({ - switch => 'battleground_chat', - message => ($masterServer->{chatLangCode}) ? stringToBytes("|00" . $message) : stringToBytes($message), - })); - - debug "Sent Battleground chat.\n", "sendPacket", 2; -} - -sub sendMercenaryCommand { - my ($self, $command) = @_; - - $self->sendToServer($self->reconstruct({ - switch => 'mercenary_command', - - # 0x0 => COMMAND_REQ_NONE - # 0x1 => COMMAND_REQ_PROPERTY - # 0x2 => COMMAND_REQ_DELETE - flag => $command - })); - - debug "Sent Mercenary Command $command", "sendPacket", 2; -} - -sub sendSkillUseLocInfo { - my ($self, $ID, $lvl, $x, $y, $moreinfo) = @_; - - $self->sendToServer($self->reconstruct({ - switch => 'skill_use_location_text', - lvl => $lvl, - ID => $ID, - x => $x, - y => $y, - info => $moreinfo - })); - - debug "Skill Use on Location: $ID, ($x, $y)\n", "sendPacket", 2; -} - -sub sendGMGiveMannerByName { - my ($self, $playerName) = @_; - - $self->sendToServer($self->reconstruct({ - switch => 'manner_by_name', - playerName => stringToBytes($playerName), - })); -} - -sub sendGMRequestStatus { - my ($self, $playerName) = @_; - - $self->sendToServer($self->reconstruct({ - switch => 'gm_request_status', - playerName => stringToBytes($playerName), - })); -} - -sub sendFeelSaveOk { - my ($self, $flag) = @_; - - $self->sendToServer($self->reconstruct({ - switch => 'starplace_agree', - flag => $flag, - })); - - debug "Sent FeelSaveOk.\n", "sendPacket", 2; -} - -sub sendGMReqAccName { - my ($self, $targetID) = @_; - - $self->sendToServer($self->reconstruct({ - switch => 'gm_request_account_name', - targetID => $targetID, - })); - - debug "Sent GM Request Account Name.\n", "sendPacket", 2; -} - -sub sendClientVersion { - my ($self, $version) = @_; - - $self->sendToServer($self->reconstruct({ - switch => 'client_version', - clientVersion => $version, - })); -} - -sub sendCaptchaAnswer { - my ($self, $answer) = @_; - - $self->sendToServer($self->reconstruct({ - switch => 'captcha_answer', - accountID => $accountID, - answer => $answer, - - # Strangely, this packet has fixed length (dec 32, or hex 0x20) but has it padded into it - lututui - len => (exists $rpackets{'07E7'}{length}) ? $rpackets{'07E7'}{length} : 32, - })); -} - -# kRO Client before 2010-08-03 only allow 1 item per transaction -# idRO_Renewal and iRO Chaos use this packet -# -# Since RagexeRE_2010_08_03a it's allowed for multiple items at once see Network/Send/kRO/RagexeRE_2010_08_03a.pm -sub sendCashShopBuy { - my ($self, $points, $items) = @_; - - if (scalar @{$items}) { - debug sprintf("Sent buying request from cashshop for %d items.\n", scalar @{$items}), "sendPacket", 2; - foreach my $item (@{$items}) { - $self->sendToServer($self->reconstruct({ - switch => 'cash_dealer_buy', - itemid => $item->{itemID}, - amount => $item->{amount}, - kafra_points => $points, - })); - } - } -} - -sub sendStartSkillUse { - my ($self, $ID, $lv, $targetID) = @_; - $char->{last_skill_used_is_continuous} = 1; - $char->{last_continuous_skill_used} = $ID; - $self->sendToServer($self->reconstruct({switch => 'start_skill_use', lv => $lv, skillID => $ID, targetID => $targetID})); - debug "Start Skill Use: $ID\n", "sendPacket", 2; -} - -sub sendStopSkillUse { - my ($self, $ID) = @_; - $char->{last_skill_used_is_continuous} = 0; - $char->{last_continuous_skill_used} = 0; - $self->sendToServer($self->reconstruct({switch => 'stop_skill_use',skillID => $ID})); - debug "Stop Skill Use: $ID\n", "sendPacket", 2; -} - -## -# Request to merge item -# 096E <size>.W { <index>.W }* -# @author [Cydh] -## -sub sendMergeItemRequest { - my ($self, $num, $items) = @_; - #my $len = ($num * 4) + 12; - $self->sendToServer($self->reconstruct({ - switch => 'merge_item_request', - #len => $len, - items => $items, - })); - debug "Sent merge item request: ".(join ', ', map { $_->{info}->{binID}." x ".$_->{info}->{amount} } @$items)."\n", "sendPacket"; -} - -sub reconstruct_merge_item_request { - my ($self, $args) = @_; - $args->{itemList} = pack '(a2)*', map { $_->{ID} } @{$args->{items}}; -} - -## -# Request to cancel merge item -# 0974 -# @author [Cydh] -## -sub sendMergeItemCancel { - my ($self, $args) = @_; - $self->sendToServer($self->reconstruct({ switch => 'merge_item_cancel' })); - debug "Cancel Merge item\n", "sendPacket"; - $mergeItemList = {}; -} - -#sub reconstruct_merge_item_cancel { -# my ($self, $args) = @_; -#} - -sub sendStylistChange { - my ($self, $hair_color, $hair_style, $cloth_color, $head_top, $head_mid, $head_bottom ) = @_; - $self->sendToServer($self->reconstruct({ - switch => 'stylist_change', - hair_color => $hair_color, - hair_style => $hair_style, - cloth_color => $cloth_color, - head_top => $head_top, - head_mid => $head_mid, - head_bottom => $head_bottom - })); -} - -## -# UI System -## - -# Request to open an UI window of the given type -# 0A68 <type>.B -sub sendOpenUIRequest { - my ($self, $UIType) = @_; - - $self->sendToServer($self->reconstruct({ - switch => 'open_ui_request', - UIType => $UIType, - })); - - debug "Sent Open UI Request (".$UIType.")\n", "sendPacket"; -} - -## -# Attendance System -## - -# Request from the client to retrieve today's attendance reward -# 0AEF - -sub sendAttendanceRewardRequest { - my ($self) = @_; - - $self->sendToServer($self->reconstruct({ - switch => 'attendance_reward_request', - })); - - debug "Sent Attendance Reward Request\n", "sendPacket"; -} - - -## -# Banking System -## - -# Requesting the data in bank -# 09AB <aid>L (PACKET_CZ_REQ_BANKING_CHECK) -sub sendBankingCheck { - my ($self, $accountID) = @_; - $self->sendToServer($self->reconstruct({ - switch => 'banking_check_request', - accountID => $accountID, - })); -} - -# Request Withdrawing some money from bank -# 09A9 <AID>L <Money>L (PACKET_CZ_REQ_BANKING_WITHDRAW) -sub sendBankingWithdraw { - my ($self, $accountID , $zeny) = @_; - $self->sendToServer($self->reconstruct({ - switch => 'banking_withdraw_request', - accountID => $accountID, - zeny => $zeny, - })); -} - -# Request saving some money in bank -# 09A7 <AID>L <Money>L (PACKET_CZ_REQ_BANKING_DEPOSIT) -sub sendBankingDeposit { - my ($self, $accountID , $zeny) = @_; - $self->sendToServer($self->reconstruct({ - switch => 'banking_deposit_request', - accountID => $accountID, - zeny => $zeny, - })); -} - -## -# Roulette System -## - -# Request to open the roulette window -# 0A19 (CZ_REQ_OPEN_ROULETTE) -sub sendRouletteWindowOpen { - my ($self) = @_; - - $self->sendToServer($self->reconstruct({ - switch => 'roulette_window_open', - })); - - debug "Sent Roulette Window Open\n", "sendPacket"; -} - -# Request the roulette reward data -# 0A1B (CZ_REQ_ROULETTE_INFO) -sub sendRouletteInfoRequest { - my ($self) = @_; - - $self->sendToServer($self->reconstruct({ - switch => 'roulette_info_request', - })); - - debug "Sent Roulette Info Request\n", "sendPacket"; -} - -# Notification of the client that the roulette window was closed -# 0A1D (CZ_REQ_CLOSE_ROULETTE) -sub sendRouletteClose { - my ($self) = @_; - - $self->sendToServer($self->reconstruct({ - switch => 'roulette_close', - })); - - debug "Sent Roulette Close\n", "sendPacket"; -} - -# Request to start the roulette -# 0A1F (CZ_REQ_GENERATE_ROULETTE) -sub sendRouletteStart { - my ($self) = @_; - - $self->sendToServer($self->reconstruct({ - switch => 'roulette_start', - })); - - debug "Sent Roulette Start\n", "sendPacket"; -} - -# Request to claim a prize -# 0A21 (CZ_RECV_ROULETTE_ITEM) -sub sendRouletteClaimPrize { - my ($self) = @_; - - $self->sendToServer($self->reconstruct({ - switch => 'roulette_claim_prize', - })); - - debug "Sent Roulette Claim Prize\n", "sendPacket"; -} - -## -# Market System -## - -# Send to Server confirmation that we already close NPC shop -# 09D4 -sub sendSellBuyComplete { - my ($self) = @_; - - $self->sendToServer($self->reconstruct({ - switch => 'sell_buy_complete', - })); - - debug "Sent Sell/Buy Complete\n", "sendPacket"; -} - -# Buy item from Market -# 09D6 -sub sendBuyBulkMarket { - my ($self, $r_array) = @_; - - $self->sendToServer($self->reconstruct({ - switch => 'buy_bulk_market', - items => \@{$r_array}, - })); - - debug("Sent bulk buy market: $_->{itemID} x $_->{amount}\n", "sendPacket", 2) foreach (@{$r_array}); -} - -sub reconstruct_buy_bulk_market { - my ($self, $args) = @_; - my $pack = $self->{send_buy_bulk_market_pack} || "v V"; - - $args->{buyInfo} = pack "(a*)*", map { pack $pack, $_->{itemID}, $_->{amount} } @{$args->{items}}; -} - -# Request to close current Market -# 09D8 -sub sendMarketClose { - my ($self) = @_; - - $self->sendToServer($self->reconstruct({ - switch => 'market_close', - })); - - debug "Sent Market Close\n", "sendPacket"; -} - -# Request Inventory Expansion -# 0B14 -sub sendInventoryExpansionRequest { - my ($self, $args) = @_; - $self->sendToServer($self->reconstruct({ switch => 'inventory_expansion_request' })); -} - -# Reject Inventory Expansion -# 0B19 -sub sendInventoryExpansionRejected { - my ($self, $args) = @_; - $self->sendToServer($self->reconstruct({ switch => 'inventory_expansion_rejected' })); -} - -# 0B1C (PACKET_CZ_PING) -sub sendPing { - my ($self, $args) = @_; - $self->sendToServer($self->reconstruct({ switch => 'ping' })); -} - -# 0A5A - PACKET_CZ_MACRO_DETECTOR_DOWNLOAD -# Let Server know that we already downloaded Captcha Image -sub sendMacroDetectorDownload { - my ($self) = @_; - - $self->sendToServer($self->reconstruct({ - switch => 'macro_detector_download', - })); -} - -# 0A5C - PACKET_CZ_MACRO_DETECTOR_ANSWER -# Send Captcha Answer -sub sendMacroDetectorAnswer { - my ($self, $answer) = @_; - - my $answer_bytes = stringToBytes($answer); - - $self->sendToServer($self->reconstruct({ - switch => 'macro_detector_answer', - answer => $answer_bytes, - })); -} - -# 0A69 - PACKET_CZ_CAPTCHA_PREVIEW_REQUEST -# Request to preview a captcha (privilege is required) -sub sendCaptchaPreviewRequest { - my ($self, $captcha_key) = @_; - - $self->sendToServer($self->reconstruct({ - switch => 'captcha_preview_request', - captcha_key => $captcha_key, - })); -} - -# 02CF CZ_MEMORIALDUNGEON_COMMAND -# Destroy an instance from the status window -sub sendMemorialDungeonCommand { - my ($self, $command) = @_; - - $self->sendToServer($self->reconstruct({ - switch => 'memorial_dungeon_command', - command => $command, - })); - - debug "Sent Memorial Dungeon Command\n", "sendPacket"; -} - -# 0A16 CZ_DYNAMICNPC_CREATE_REQUEST -sub sendNPCCreateRequest { - my ($self, $name) = @_; - $self->sendToServer($self->reconstruct({switch => 'dynamicnpc_create_request', ID => $name})); - debug "Sent request to create NPC by name: $name\n", "sendPacket", 2; -} - -# 0841 CH_SELECT_ACCESSIBLE_MAPNAME -sub sendSelectAccessibleMapname { - my ($self, $map_slot) = @_; - - $self->sendToServer($self->reconstruct({ - switch => 'select_accessible_mapname', - char_slot => $config{char}, - map_slot => $map_slot, - })); -} - -# 0BAF CZ_USE_PACKAGEITEM -sub sendUsePackageItem { - my ($self, $index, $itemID, $boxIndex) = @_; - - $self->sendToServer($self->reconstruct({ - switch => 'use_packageitem', - index => $index, - accountID => $accountID, - itemID => $itemID, - boxIndex => $boxIndex, - })); -} - -1; diff --git a/openkore_llm_knowledge/core/src/Plugins.pm b/openkore_llm_knowledge/core/src/Plugins.pm deleted file mode 100644 index 918fad07d6..0000000000 --- a/openkore_llm_knowledge/core/src/Plugins.pm +++ /dev/null @@ -1,554 +0,0 @@ -######################################################################### -# OpenKore - Plugin system -# -# This software is open source, licensed under the GNU General Public -# License, version 2. -# Basically, this means that you're allowed to modify and distribute -# this software. However, if you distribute modified versions, you MUST -# also distribute the source code. -# See http://www.gnu.org/licenses/gpl.html for the full license. -# -# $Revision$ -# $Id$ -# -######################################################################### -## -# MODULE DESCRIPTION: Plugin system -# -# This module provides an interface for handling plugins. -# See the <a href="https://openkore.com/wiki/How_to_write_plugins_for_OpenKore">Plugin Writing Tutorial</a> for more information about plugins. -# -# NOTE: Do not confuse plugins with modules! See Modules.pm for more information. - -# TODO: use events instead of printing log information directly. - -package Plugins; - -use strict; -use warnings; -use Time::HiRes qw(time sleep); -use Exception::Class ('Plugin::LoadException', 'Plugin::DeniedException'); - -use Modules 'register'; -use Globals; -use Utils qw(stringToQuark quarkToString); -use Utils::DataStructures qw(binAdd existsInList); -use Utils::ObjectList; -use Utils::Exceptions; -use Log qw(message warning); -use Translation qw(T TF); -use Settings qw(%sys); - - -############################# -### CATEGORY: Variables -############################# - -## -# String $Plugins::current_plugin -# -# When a plugin is being (re)loaded, the filename of the plugin is set in this variable. -our $current_plugin; - -## -# String $Plugins::current_plugin_folder -# -# When a plugin is being (re)loaded, the the plugin's folder is set in this variable. -our $current_plugin_folder; - -our @plugins; -our %hooks; - -use enum qw(HOOKNAME INDEX); -use enum qw(CALLBACK USER_DATA); - - -############################# -### CATEGORY: Functions -############################# - -sub c_loadAll { return 1 }; -sub c_loadSelected { return existsInList($sys{'loadPlugins_list'}, shift)}; -sub c_loadNotSelected { return !existsInList($sys{'skipPlugins_list'}, shift)}; - -sub getFilesFromDirs { - my ($dirs, $f_exts, $d_ignores, $recurse_lv) = @_; - my @files; - foreach my $dir (@{$dirs}) { - next if (!opendir(DIR, $dir)); - my @items = readdir DIR; - closedir DIR; - foreach my $item (@items) { - if (-f "$dir/$item" && $item =~ /(.*)(\.($f_exts))$/) { - push @files, {dir => $dir, name => $1, ext => $2}; - #message "dir:$dir file:$1 ext:$2\n"; - } elsif (-d "$dir/$item" && $item !~ /^(\.|$d_ignores$)/i && $recurse_lv) { - push @files, getFilesFromDirs(["$dir/$item"], $f_exts, $d_ignores, $recurse_lv-1); - } - } - } - return @files; -} - -sub getPluginsFiles { - my @folders = Settings::getPluginsFolders(); - my @files = getFilesFromDirs(\@folders, 'pl', 'cvs', 1); - return @files; -} - -sub getCondition { - my $condition; - if (!exists $sys{'loadPlugins'}) { - message T("Loading all plugins (by default)...\n", 'plugins'); - $condition = \&c_loadAll; - } elsif (!$sys{'loadPlugins'}) { - message T("Automatic loading of plugins disabled\n", 'plugins'); - return; - } elsif ($sys{'loadPlugins'} eq '1') { - message T("Loading all plugins...\n", 'plugins'); - $condition = \&c_loadAll; - } elsif ($sys{'loadPlugins'} eq '2') { - message T("Selectively loading plugins...\n", 'plugins'); - $condition = \&c_loadSelected; - } elsif ($sys{'loadPlugins'} eq '3') { - message T("Selectively skipping plugins...\n", 'plugins'); - $condition = \&c_loadNotSelected; - } - return $condition; -} - -## -# void Plugins::unloadByRegexp(regex) -# -# Unloads all registered plugins that match the regex. -sub unloadByRegexp { - my ($regexp) = @_; - my @unloadPlugins; - foreach my $plugin (@plugins) { - next if (!$plugin); - push(@unloadPlugins, $plugin) if ($plugin->{name} =~ /$regexp/); - } - unloadPlugins(\@unloadPlugins); -} - -## -# void Plugins::unloadAll() -# -# Unloads all registered plugins. -sub unloadAll { - unloadPlugins(\@plugins); - @plugins = (); -} - -## -# void Plugins::unloadPlugins(plugins) -# -# Unloads all registered plugins given in 'plugins'. -# Use this method to unload a specific list of plugins. -sub unloadPlugins { - my $unloadPlugins = shift; - my $found; - foreach my $plugin (@$unloadPlugins) { - next if (!$plugin); - unload($plugin->{name}); - $found = 1; - } - warning T("Error in function 'plugin unload' (Unload Plugin)\n" . - "The specified plugin do not exist.\n") unless $found; -} - -## -# boolean Plugins::unload(name) -# name: The name of the plugin to unload. -# Returns: 1 if the plugin has been successfully unloaded, 0 if the plugin isn't registered. -# -# Unloads a registered plugin. -sub unload { - my $name = shift; - return 0 if (!defined $name); - my $i = 0; - foreach my $plugin (@plugins) { - if ($plugin && $plugin->{name} && $plugin->{name} eq $name) { - $plugin->{unload_callback}->() if (defined $plugin->{unload_callback}); - delete $plugins[$i]; - message TF("Plugin %s unloaded.\n", $name), "system"; - return 1; - } - $i++; - } - return 0; -} - -## -# void Plugins::loadByRegexp(regex) -# -# Loads all plugins that match the regex from the plugins folder, and all plugins that -# match the regex and are one subfolder below the plugins folder. -# Plugins must have the .pl extension. -# This function ignores "loadPlugins" from sys.txt file. -sub loadByRegexp { - my ($regexp) = @_; - my @files = getPluginsFiles(); - @files = grep {$_->{name} =~ /$regexp/} @files; - my @load_files; - foreach my $file (@files) { - push(@load_files, "$file->{dir}/$file->{name}$file->{ext}"); - return if $quit; - } - loadPlugins(\@load_files); -} - -## -# void Plugins::loadAll() -# -# Loads all plugins from the plugins folder, and all plugins that are one subfolder below -# the plugins folder. Plugins must have the .pl extension. -# This function respects "loadPlugins" from sys.txt file. -sub loadAll { - my $condition = getCondition(); - return if (!$condition); - - my @files = getPluginsFiles(); - my @load_files; - foreach my $file (@files) { - push(@load_files, "$file->{dir}/$file->{name}$file->{ext}") if (&$condition($file->{name})); - return if $quit; - } - loadPlugins(\@load_files); -} - -## -# void Plugins::loadPlugins(filenames) -# -# Loads all plugins given in 'filenames'. -# Use this method to unload a specific list of plugins. -sub loadPlugins { - my $filenames = shift; - foreach my $filename (@$filenames) { - load($filename); - } -} - -## -# void Plugins::load(String file) -# file: The filename of a plugin. -# -# Loads a plugin. -# -# Throws Plugin::LoadException if it failed to load. -# Throws Plugin::DeniedException if the plugin system refused to load this plugin. This can -# happen, for example, if it detects that a plugin is incompatible. -sub load { - my $file = shift; - message(TF("Loading plugin %s...\n", $file), "plugins"); - - if (! -f $file) { - warning TF("File %s does not exist. (usage ex: plugin load plugins/macro/macro.pl)\n", $file); - } elsif ($file =~ /(^|\/)ropp\.pl$/i) { - Plugin::DeniedException->throw(TF("The ROPP plugin (ropp.pl) is obsolete and is " . - "no longer necessary. Please remove it, or %s will not work correctly.", - $Settings::NAME || "OpenKore")); - } else { - $current_plugin = $file; - $current_plugin_folder = $file; - $current_plugin_folder =~ s/(.*)[\/\\].*/$1/; - - undef $!; - undef $@; - if (!defined(do $file)) { - if ($@) { - Plugin::LoadException->throw(TF("Plugin contains syntax errors:\n%s", $@)); - } else { - Plugin::LoadException->throw("$!"); - } - } - undef $current_plugin; - undef $current_plugin_folder; - } -} - -## -# void Plugins::reloadByRegexp(regex) -# -# Reloads all registered plugins that match the regex. -sub reloadByRegexp { - my ($regexp) = @_; - my @reloadPlugins; - foreach my $plugin (@plugins) { - next if (!$plugin); - push(@reloadPlugins, $plugin) if ($plugin->{name} =~ /$regexp/); -} - reloadPlugins(\@reloadPlugins); -} - -## -# void Plugins::reloadAll() -# -# Reloads all registered plugins. -sub reloadAll { - reloadPlugins(\@plugins); -} - -## -# void Plugins::reloadPlugins(plugins) -# -# Reloads all registered plugins given in 'plugins'. -# Use this method to reload a specific list of plugins. -sub reloadPlugins { - my $reloadPlugins = shift; - my $found; - foreach my $plugin (@$reloadPlugins) { - next if (!$plugin); - reload($plugin->{name}); - $found = 1; - } - warning T("Error in function 'plugin reload' (Reload Plugin)\n" . - "The specified plugin do not exist.\n") unless $found; -} - -## -# boolean Plugins::reload(String name) -# name: The name of the plugin to reload. -# Returns: 1 on success, 0 if the plugin isn't registered. -# -# Reload a plugin. -# -# Throws Plugin::LoadException if it failed to load. -sub reload { - my $name = shift; - return 0 if (!defined $name); - my $i = 0; - foreach my $plugin (@plugins) { - if ($plugin && $plugin->{name} && $plugin->{name} eq $name) { - my $filename = $plugin->{filename}; - - if (defined $plugin->{reload_callback}) { - $plugin->{reload_callback}->() - } elsif (defined $plugin->{unload_callback}) { - $plugin->{unload_callback}->(); - } - - undef %{$plugin}; - delete $plugins[$i]; - load($filename); - return 1; - } - $i++; - } - return 0; -} - - -## -# void Plugins::register(String name, String description, [unload_callback, reload_callback]) -# name: The plugin's name. -# description: A short one-line description of the plugin. -# unload_callback: Reference to a function that will be called when the plugin is being unloaded. -# reload_callback: Reference to a function that will be called when the plugin is being reloaded. -# Returns: 1 if the plugin has been successfully registered, 0 if a plugin with the same name is already registered. -# -# Plugins should call this function when they are loaded. This function registers -# the plugin in the plugin database. Registered plugins can be unloaded by the user. -# -# In the unload/reload callback functions, plugins should delete any hook functions they added. -# See also: Plugins::addHook(), Plugins::delHook() -sub register { - my $name = shift; - return 0 if registered($name); - - my %plugin_info = ( - name => $name, - description => shift, - unload_callback => shift, - reload_callback => shift, - filename => $current_plugin - ); - - binAdd(\@plugins, \%plugin_info); - return 1; -} - - -## -# boolean Plugins::registered(String name) -# name: The plugin's name. -# Returns: 1 if the plugin's registered, 0 if it isn't. -# -# Checks whether a plugin is registered. -sub registered { - my $name = shift; - return 0 if (!defined $name); - foreach (@plugins) { - return 1 if ($_ && $_->{name} && $_->{name} eq $name); - } - return 0; -} - -## -# Plugins::addHook(String hookname, callback, [user_data]) -# hookname: Name of a hook. -# callback: Reference to the function to call. -# user_data: Additional data to pass to callback. -# Returns: A handle which can be used to remove this hook. -# -# Add a hook for $hookname. Whenever Kore calls Plugins::callHook('foo'), -# callback is also called. -# -# See also Plugins::callHook() for information about how callback is called. -# -# Example: -# # Somewhere in your plugin: -# use Plugins; -# use Log; -# -# my $hook = Plugins::addHook('AI_pre', \&ai_called); -# -# sub ai_called { -# Log::message("Kore's AI() function has been called.\n"); -# } -# -# # Somewhere in the Kore source code: -# sub AI { -# ... -# Plugins::callHook('AI_pre'); # <-- ai_called() is now also called. -# ... -# } -sub addHook { - my ($hookName, $callback, $user_data) = @_; - my $hookList = $hooks{$hookName} ||= new ObjectList(); - - my @entry; - $entry[CALLBACK] = $callback; - $entry[USER_DATA] = $user_data if defined($user_data); - - my @handle; - $handle[HOOKNAME] = stringToQuark($hookName); - $handle[INDEX] = $hookList->add(bless(\@entry, "Plugins::HookEntry")); - return bless(\@handle, 'Plugins::HookHandle'); -} - -## -# Plugins::addHooks( [hookName, callback, user_data], ... ) -# Returns: A handle, which can be used with Plugins::delHook() -# -# A convenience function for adding many hooks with one function. -# -# See also: Plugins::addHook(), Plugins::delHook() -# -# Example: -# $hooks = Plugins::addHooks( -# ['AI_pre', \&onAI_pre], -# ['mainLoop_pre', \&onMainLoop_pre, $some_user_data] -# ); -# Plugins::delHook($hooks); -# -# # The above is the same as: -# $hook1 = Plugins::addHook('AI_pre', \&onAI_pre); -# $hook2 = Plugins::addHook('mainLoop_pre', \&onMainLoop_pre); -# Plugins::delHook($hook1); -# Plugins::delHook($hook2); -sub addHooks { - my @hooks; - foreach my $params (@_) { - push @hooks, addHook(@{$params}); - } - return bless(\@hooks, "Plugins::HookHandles"); -} - -## -# Plugins::delHook(hookname, handle) -# hookname: Name of a hook. -# handle: A hook handle, as returned by Plugins::addHook() -# -# Removes a registered hook. $callback will not be called anymore. -# -# See also: Plugins::addHook() -# -# Example: -# Plugins::register('example', 'Example Plugin', \&on_unload, \&on_reload); -# my $hook = Plugins::addHook('AI_pre', \&ai_called); -# -# sub on_unload { -# Plugins::delHook($hook); -# Log::message "Example plugin unloaded.\n"; -# } -sub delHook { - my ($handle) = @_; - if (@_ > 1) { - # More than one parameter was passed. This means that the plugin - # is still using the old API. Make sure things are backwards - # compatible. - shift; - ($handle) = @_; - } - - if (UNIVERSAL::isa($handle, 'Plugins::HookHandles')) { - foreach my $singleHandle (@{$handle}) { - delHook($singleHandle); - } - undef @{$handle}; - - } elsif (UNIVERSAL::isa($handle, 'Plugins::HookHandle') && defined $handle->[HOOKNAME]) { - my $hookName = quarkToString($handle->[HOOKNAME]); - my $hookList = $hooks{$hookName}; - if ($hookList) { - my $entry = $hookList->get($handle->[INDEX]); - $hookList->remove($entry); - } - delete $handle->[HOOKNAME]; - delete $handle->[INDEX]; - undef $handle; - - if ($hookList && $hookList->size() == 0) { - delete $hooks{$hookName}; - } - - } else { - ArgumentException->throw("Invalid hook handle passed to Plugins::delHook()."); - } -} - -## -# Plugins::delHooks(hooks) -# -# An alias for Plugins::delHook(), for backwards compatibility reasons. -sub delHooks { - &delHook; -} - - -## -# void Plugins::callHook(String hookName, [argument]) -# hookName: Name of the hook. -# argument: An argument to pass to the hook's callback functions. -# -# Call all callback functions which are associated with the hook $hookName. -# Adding or removing callbacks during callHook will not affect the current call. -# -# The hook's callback function is called as follows: -# <pre class="example"> -# $callback->($hookName, $argument, userdata as passed to addHook); -# </pre> -# -# See also: Plugins::addHook() -sub callHook { - my ($hookName, $argument) = @_; - my $hookList = $hooks{$hookName}; - if ($hookList) { - my @items = @{ $hookList->getItems }; - foreach my $entry (@items) { - $entry->[CALLBACK]->($hookName, $argument, $entry->[USER_DATA]); - } - } -} - -## -# boolean Plugins::hasHook(String hookName) -# -# Check whether there are any hooks registered for the specified hook name. -sub hasHook { - my ($hookName) = @_; - return defined $hooks{$hookName}; -} - -1; diff --git a/openkore_llm_knowledge/core/src/doc/modules.txt b/openkore_llm_knowledge/core/src/doc/modules.txt deleted file mode 100644 index 74620a732e..0000000000 --- a/openkore_llm_knowledge/core/src/doc/modules.txt +++ /dev/null @@ -1,66 +0,0 @@ -Actor -Actor::Monster -Actor::Player -Actor::You -Actor::Item -ActorList -AI -I18N -Interface -InventoryList -ChatQueue -Commands -Log -Field -Settings -Translation -Task -Task::Chained -Task::Function -Task::Timeout -Task::Wait -TaskManager -Modules -Network::ClientReceive -Network::DirectConnection -Network::PacketParser -Network::Receive -Network::Send -Network::XKore -Network::XKore2 -Network::XKoreProxy -Plugins -Skill -Match -Misc - -Base::Server -Base::Server::Client -Base::WebServer -Base::WebServer::Process - -Utils -Utils::AppLauncher -Utils::Benchmark -Utils::CallbackList -Utils::Crypton -Utils::DataStructures -Utils::Exceptions -Utils::LockFile -Utils::PerlLauncher -Utils::ObjectList -Utils::Set -Utils::StartupNotification::Launcher -Utils::StartupNotification::Launchee -Utils::StartupNotification::CreateSocketException -Utils::TextReader -Utils::PathFinding -Utils::Unix -Utils::Whirlpool -Utils::Win32 - -Bus::Messages - -Interface::Wx::Console -Interface::Wx::DockNotebook -Interface::Wx::DockNotebook::Page diff --git a/openkore_llm_knowledge/knowledge/architecture_overview.md b/openkore_llm_knowledge/knowledge/architecture_overview.md index 4bdb2ce37b..4114739c4e 100644 --- a/openkore_llm_knowledge/knowledge/architecture_overview.md +++ b/openkore_llm_knowledge/knowledge/architecture_overview.md @@ -1,27 +1,15 @@ -# OpenKore Core Architecture (Curated Bundle) +# OpenKore Core Architecture Overview -This knowledge bundle focuses on **core runtime architecture** from `src/` only. +This bundle captures only the **core architecture** from `src/` for public AI-assistant knowledge use. -## Runtime flow -1. `openkore.pl` bootstraps module paths and loads core modules. -2. `src/functions.pl` drives the startup state machine: - - load plugins - - load control/table data - - initialize networking - - finalize initialization - - enter steady `mainLoop` -3. Core behavior then cycles through AI, task scheduling, command handling, and packet IO. +## Structural layers +- **Runtime orchestration**: `core/functions.pl` controls initialization stages and the recurring main loop. +- **State and configuration**: `core/Globals.pm`, `core/Settings.pm`, `core/FileParsers.pm`. +- **Behavior engine**: `core/AI.pm`, `core/AI/CoreLogic.pm`. +- **World model**: `core/Actor.pm`, `core/ActorList.pm`. +- **Task execution**: `core/Task.pm`, `core/TaskManager.pm`, `core/Task/{Route,Move,TalkNPC}.pm`. +- **Packet/network core**: `core/Network.pm`, `core/Network/{PacketParser,Receive}.pm`. +- **Command surface**: `core/Commands.pm`. -## Included core layers -- **Boot + orchestration**: `src/functions.pl`, `src/Modules.pm`, `src/Globals.pm` -- **Configuration/parsing**: `src/Settings.pm`, `src/FileParsers.pm` -- **AI/task execution**: `src/AI.pm`, `src/AI/CoreLogic.pm`, `src/AI/Attack.pm`, `src/Task*.pm` -- **Actor model**: `src/Actor.pm`, `src/ActorList.pm` -- **Networking core**: `src/Network.pm`, `src/Network/{PacketParser,Receive,Send}.pm` -- **Command/log/debug glue**: `src/Commands.pm`, `src/Misc.pm`, `src/Log.pm`, `src/ErrorHandler.pm`, `src/Interface.pm` -- **Plugin framework dependency** (core extensibility context): `src/Plugins.pm` - -## Curation constraints applied -- Source scope restricted to `src/` content for copied code files. -- No binaries, archives, build artifacts, logs, or temp assets were included. -- Bundle is intentionally small and architecture-first for public GPT ingestion. +## Why this is “core” +The selected modules define startup, state, decision logic, movement/NPC flows, and packet ingestion—the minimum architecture needed to explain how OpenKore runs end-to-end without pulling in plugin- or UI-heavy areas. diff --git a/openkore_llm_knowledge/knowledge/code_index_core.md b/openkore_llm_knowledge/knowledge/code_index_core.md deleted file mode 100644 index 19c50291ff..0000000000 --- a/openkore_llm_knowledge/knowledge/code_index_core.md +++ /dev/null @@ -1,41 +0,0 @@ -# Code Index (Core Bundle) - -## Boot and lifecycle -- `core/src/functions.pl` — startup state machine and main loop phases. -- `core/src/Modules.pm` — module registration/reload support. -- `core/src/Globals.pm` — shared runtime/global state. - -## Configuration and parsing -- `core/src/Settings.pm` — control/table resolution and file registration. -- `core/src/FileParsers.pm` — parsing routines for config/table data. - -## AI and tasks -- `core/src/AI.pm` -- `core/src/AI/CoreLogic.pm` -- `core/src/AI/Attack.pm` -- `core/src/Task.pm` -- `core/src/TaskManager.pm` -- `core/src/Task/Route.pm` -- `core/src/Task/Move.pm` -- `core/src/Task/TalkNPC.pm` - -## Game entities -- `core/src/Actor.pm` -- `core/src/ActorList.pm` - -## Networking core -- `core/src/Network.pm` -- `core/src/Network/PacketParser.pm` -- `core/src/Network/Receive.pm` -- `core/src/Network/Send.pm` - -## Runtime support and diagnostics -- `core/src/Commands.pm` -- `core/src/Misc.pm` -- `core/src/Log.pm` -- `core/src/ErrorHandler.pm` -- `core/src/Interface.pm` -- `core/src/Plugins.pm` - -## Reference docs from `src/` -- `core/src/doc/modules.txt` diff --git a/openkore_llm_knowledge/knowledge/core_module_index.md b/openkore_llm_knowledge/knowledge/core_module_index.md new file mode 100644 index 0000000000..0c04d08687 --- /dev/null +++ b/openkore_llm_knowledge/knowledge/core_module_index.md @@ -0,0 +1,20 @@ +# Core Module Index + +- `core/functions.pl` — startup states, initialization sequence, and main loop transitions. +- `core/Modules.pm` — module registration and reload mechanics. +- `core/Globals.pm` — shared globals and runtime containers. +- `core/Settings.pm` — path/file registration and load policies. +- `core/FileParsers.pm` — parsers for core config/table content. +- `core/Commands.pm` — runtime command entrypoints. +- `core/AI.pm` — AI framework control. +- `core/AI/CoreLogic.pm` — primary decision-cycle logic. +- `core/Actor.pm` — actor base model. +- `core/ActorList.pm` — actor indexing/lookup list. +- `core/Task.pm` — task primitive. +- `core/TaskManager.pm` — task scheduler/runner. +- `core/Task/Route.pm` — route planning task. +- `core/Task/Move.pm` — movement task execution. +- `core/Task/TalkNPC.pm` — NPC conversation task. +- `core/Network.pm` — network state machine abstraction. +- `core/Network/PacketParser.pm` — packet decode/shape logic. +- `core/Network/Receive.pm` — incoming packet dispatch and handlers. diff --git a/openkore_llm_knowledge/knowledge/core_subsystems.md b/openkore_llm_knowledge/knowledge/core_subsystems.md new file mode 100644 index 0000000000..2be26062db --- /dev/null +++ b/openkore_llm_knowledge/knowledge/core_subsystems.md @@ -0,0 +1,27 @@ +# Core Subsystems + +## AI +- `core/AI.pm` hosts AI stack control and high-level bot behavior gates. +- `core/AI/CoreLogic.pm` contains central decision logic used every loop tick. + +## Actor +- `core/Actor.pm` defines the base actor abstraction. +- `core/ActorList.pm` manages indexed actor collections and lookups. + +## Network +- `core/Network.pm` defines connection state abstraction. +- `core/Network/PacketParser.pm` performs packet-level extraction/reconstruction logic. +- `core/Network/Receive.pm` maps and dispatches incoming packet handlers. + +## Tasks +- `core/Task.pm` is the task base abstraction. +- `core/TaskManager.pm` queues/runs tasks. +- `core/Task/Route.pm` and `core/Task/Move.pm` handle routing and movement execution. +- `core/Task/TalkNPC.pm` encapsulates NPC interaction task flow. + +## Commands +- `core/Commands.pm` provides command parsing/dispatch used by console and automation. + +## File parsing +- `core/Settings.pm` registers and resolves data/control files. +- `core/FileParsers.pm` parses key config/table formats into runtime structures. diff --git a/openkore_llm_knowledge/knowledge/execution_flows.md b/openkore_llm_knowledge/knowledge/execution_flows.md new file mode 100644 index 0000000000..95982978b1 --- /dev/null +++ b/openkore_llm_knowledge/knowledge/execution_flows.md @@ -0,0 +1,26 @@ +# Execution Flows + +## AI decision loop +1. `core/functions.pl` runs the main loop and enters initialized state. +2. AI stack calls into `core/AI.pm` and `core/AI/CoreLogic.pm`. +3. Decisions enqueue/update tasks via `core/TaskManager.pm`. + +## NPC interaction flow +1. AI/commands request NPC interaction. +2. `core/Task/TalkNPC.pm` drives conversation steps. +3. Packet responses are consumed via `core/Network/Receive.pm` handlers and reflected back into task state. + +## Packet receive flow +1. Raw network data is managed through `core/Network.pm` runtime state. +2. `core/Network/PacketParser.pm` resolves packet framing and decode shape. +3. `core/Network/Receive.pm` dispatches switch-specific logic updating globals/actors/tasks. + +## Routing and movement flow +1. AI or commands schedule movement-related tasks. +2. `core/Task/Route.pm` computes route-level progression. +3. `core/Task/Move.pm` executes movement steps and reacts to packet/position updates. + +## Actor state updates +1. Incoming packets are decoded and mapped in `core/Network/Receive.pm`. +2. Actor entities from `core/Actor.pm` are created/updated. +3. Collections in `core/ActorList.pm` are synchronized and used by AI/task decisions. From 9c15fadfba118e5f6fb71cb6d038d1ba987e97ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Bosc=C3=A1?= <juan_bosca@live.com> Date: Tue, 10 Mar 2026 18:21:44 -0300 Subject: [PATCH 05/18] Curate plugin and configuration knowledge bundle --- openkore_llm_knowledge/config/arrowcraft.txt | 15 + openkore_llm_knowledge/config/avoid.txt | 66 + openkore_llm_knowledge/config/buyer_shop.txt | 14 + openkore_llm_knowledge/config/chat_resp.txt | 18 + openkore_llm_knowledge/config/config.txt | 1035 +++++++ .../config/consolecolors.txt | 55 + .../config/items_control.txt | 1757 ++++++++++++ openkore_llm_knowledge/config/mon_control.txt | 222 ++ openkore_llm_knowledge/config/overallAuth.txt | 0 openkore_llm_knowledge/config/pickupitems.txt | 13 + openkore_llm_knowledge/config/poseidon.txt | 21 + openkore_llm_knowledge/config/priority.txt | 13 + openkore_llm_knowledge/config/responses.txt | 109 + .../config/routeweights.txt | 51 + openkore_llm_knowledge/config/shop.txt | 22 + openkore_llm_knowledge/config/sys.txt | 61 + openkore_llm_knowledge/config/timeouts.txt | 220 ++ .../knowledge/config_system.md | 27 + .../knowledge/macro_system.md | 31 + .../knowledge/plugin_system.md | 41 + .../plugins/eventMacro/README.md | 10 + .../plugins/eventMacro/eventMacro.pl | 638 +++++ .../eventMacro/eventMacro/Automacro.pm | 306 ++ .../plugins/eventMacro/eventMacro/Core.pm | 1827 ++++++++++++ .../plugins/eventMacro/eventMacro/Data.pm | 83 + .../eventMacro/eventMacro/FileParser.pm | 289 ++ .../plugins/eventMacro/eventMacro/Lists.pm | 95 + .../plugins/eventMacro/eventMacro/Macro.pm | 25 + .../plugins/eventMacro/eventMacro/Runner.pm | 2473 +++++++++++++++++ .../eventMacro/eventMacro/Utilities.pm | 744 +++++ .../plugins/macro/Macro/Automacro.pm | 825 ++++++ .../plugins/macro/Macro/Data.pm | 102 + .../plugins/macro/Macro/Parser.pm | 375 +++ .../plugins/macro/Macro/Script.pm | 1016 +++++++ .../plugins/macro/Macro/Utilities.pm | 536 ++++ openkore_llm_knowledge/plugins/macro/macro.pl | 307 ++ openkore_llm_knowledge/plugins/map/map.pl | 314 +++ .../plugins/profiles/profiles.pl | 220 ++ .../plugins/reconnect/reconnect.pl | 61 + 39 files changed, 14037 insertions(+) create mode 100644 openkore_llm_knowledge/config/arrowcraft.txt create mode 100644 openkore_llm_knowledge/config/avoid.txt create mode 100644 openkore_llm_knowledge/config/buyer_shop.txt create mode 100644 openkore_llm_knowledge/config/chat_resp.txt create mode 100644 openkore_llm_knowledge/config/config.txt create mode 100644 openkore_llm_knowledge/config/consolecolors.txt create mode 100644 openkore_llm_knowledge/config/items_control.txt create mode 100644 openkore_llm_knowledge/config/mon_control.txt create mode 100644 openkore_llm_knowledge/config/overallAuth.txt create mode 100644 openkore_llm_knowledge/config/pickupitems.txt create mode 100644 openkore_llm_knowledge/config/poseidon.txt create mode 100644 openkore_llm_knowledge/config/priority.txt create mode 100644 openkore_llm_knowledge/config/responses.txt create mode 100644 openkore_llm_knowledge/config/routeweights.txt create mode 100644 openkore_llm_knowledge/config/shop.txt create mode 100644 openkore_llm_knowledge/config/sys.txt create mode 100644 openkore_llm_knowledge/config/timeouts.txt create mode 100644 openkore_llm_knowledge/knowledge/config_system.md create mode 100644 openkore_llm_knowledge/knowledge/macro_system.md create mode 100644 openkore_llm_knowledge/knowledge/plugin_system.md create mode 100644 openkore_llm_knowledge/plugins/eventMacro/README.md create mode 100644 openkore_llm_knowledge/plugins/eventMacro/eventMacro.pl create mode 100644 openkore_llm_knowledge/plugins/eventMacro/eventMacro/Automacro.pm create mode 100644 openkore_llm_knowledge/plugins/eventMacro/eventMacro/Core.pm create mode 100644 openkore_llm_knowledge/plugins/eventMacro/eventMacro/Data.pm create mode 100644 openkore_llm_knowledge/plugins/eventMacro/eventMacro/FileParser.pm create mode 100644 openkore_llm_knowledge/plugins/eventMacro/eventMacro/Lists.pm create mode 100644 openkore_llm_knowledge/plugins/eventMacro/eventMacro/Macro.pm create mode 100644 openkore_llm_knowledge/plugins/eventMacro/eventMacro/Runner.pm create mode 100644 openkore_llm_knowledge/plugins/eventMacro/eventMacro/Utilities.pm create mode 100644 openkore_llm_knowledge/plugins/macro/Macro/Automacro.pm create mode 100644 openkore_llm_knowledge/plugins/macro/Macro/Data.pm create mode 100644 openkore_llm_knowledge/plugins/macro/Macro/Parser.pm create mode 100644 openkore_llm_knowledge/plugins/macro/Macro/Script.pm create mode 100644 openkore_llm_knowledge/plugins/macro/Macro/Utilities.pm create mode 100644 openkore_llm_knowledge/plugins/macro/macro.pl create mode 100644 openkore_llm_knowledge/plugins/map/map.pl create mode 100644 openkore_llm_knowledge/plugins/profiles/profiles.pl create mode 100644 openkore_llm_knowledge/plugins/reconnect/reconnect.pl diff --git a/openkore_llm_knowledge/config/arrowcraft.txt b/openkore_llm_knowledge/config/arrowcraft.txt new file mode 100644 index 0000000000..a496692cf9 --- /dev/null +++ b/openkore_llm_knowledge/config/arrowcraft.txt @@ -0,0 +1,15 @@ +# Archers have the special skill Arrow Craft, which allows you to create +# arrows using certain items. +# If you want Kore to automatically create arrows with Arrow Craft, then +# you can do that by editing this file. +# +# In this file, specify the ingredients that you need to create arrows. +# The format is as follows: +# (item name) (amount) +# +# For example: +# Red Blood 1 +# Garlet +# +# This will allow Kore to create Fire Arrows and Iron Arrows by using +# the above items. diff --git a/openkore_llm_knowledge/config/avoid.txt b/openkore_llm_knowledge/config/avoid.txt new file mode 100644 index 0000000000..3b9166ff7f --- /dev/null +++ b/openkore_llm_knowledge/config/avoid.txt @@ -0,0 +1,66 @@ +# avoid.txt: Avoid certain players +# WARNING: The commented players and IDs here are for iRO GMs/bot-hunters and are probably outdated. + +# Format: <name> (TAB(s)) <disconnect when in sight> <teleport when in sight> <disconnect on chat> +[Players] +# Examples: +[GM]Example 1 0 1 +# Sir Kills A Lot 1 0 1 +# GOD-POING 1 0 1 +# Petite Christy 1 0 1 +# My Christy 1 0 1 +# pusher 1 0 1 +# Aerok 1 0 1 +# Newbie 1 0 1 +# Queenstown 1 0 1 +# karlina 1 0 1 +# Style 1 0 1 +# -Aerok- 1 0 1 +# Kross 1 0 1 +# ~Jinx~ 1 0 1 +# Rose 1 0 1 +# Ebi-chu 1 0 1 +# MeloColmECA 1 0 1 +# Leileil 1 0 1 +# Angel 1 0 1 + +[ID] +# Same as above, but uses player IDs instead of names +# There is no disconnect on chat for this section. +# Examples: +000001 1 0 +# 100001 1 0 +# 100002 1 0 +# 100003 1 0 +# 231656 1 0 +# 483461 1 0 +# 483502 1 0 +# 483527 1 0 +# 527195 1 0 +# 527197 1 0 +# 527198 1 0 +# 527199 1 0 +# 527206 1 0 +# 527207 1 0 +# 527210 1 0 +# 543887 1 0 +# 650874 1 0 +# 650882 1 0 +# 650884 1 0 +# 650886 1 0 +# 650887 1 0 +# 527205 1 0 +# 483471 1 0 +# 527211 1 0 + +[Jobs] +# Same as above, but uses player jobs instead of names +# There is no disconnect on chat for this section. +# Examples: +# see https://github.com/OpenKore/openkore/blob/master/src/Globals.pm#L141 +#Acolyte 0 1 +#Priest 0 1 +#Monk 0 1 +#Thief 0 1 +#Assassin 0 1 +#Rogue 0 1 \ No newline at end of file diff --git a/openkore_llm_knowledge/config/buyer_shop.txt b/openkore_llm_knowledge/config/buyer_shop.txt new file mode 100644 index 0000000000..f34de56ef7 --- /dev/null +++ b/openkore_llm_knowledge/config/buyer_shop.txt @@ -0,0 +1,14 @@ +# This file controls which items to put into your buyer shop. +# To enable Buyer Shop, set the 'buyerShopAuto' configuration option in +# config.txt to 1. +# +# The first line of the file is your shop title. +# Subsequent lines are of this format (tab-delimited): +# <name> <price> <amount> +# +# You must have ALL_BUYING_STORE and Bulk Buyer Shop License or Black Market Buyer Shop License. +# +# Example: +# My Buyer Shop;;My cool Buyer shop;;My nice Buyer shop +# Jellopy 1,000 5 +# Honey 3,500 30 diff --git a/openkore_llm_knowledge/config/chat_resp.txt b/openkore_llm_knowledge/config/chat_resp.txt new file mode 100644 index 0000000000..a4b4ffa19b --- /dev/null +++ b/openkore_llm_knowledge/config/chat_resp.txt @@ -0,0 +1,18 @@ +# Kore can reply with a message when someone talks to you. In this file, +# you can configure what Kore should say when someone says something. +# +# Each line has the following format: +# (words) TAB (answers) +# +# (words) is a comma-seperated list of words. When the message (of the +# one who talks to Kore) contains a word that's in this list, Kore will +# pick a random answer from (answers), which is a comma-seperated list +# of possible answers. +# +# For example: +# bot,botter no,I'm not a bot,huh? +# +# When someone says something that contains the word "bot" or "botter", +# Kore will reply with "no", "I'm not a bot" or "huh?". + +bot,botter no,I'm not a bot,huh? diff --git a/openkore_llm_knowledge/config/config.txt b/openkore_llm_knowledge/config/config.txt new file mode 100644 index 0000000000..8520558550 --- /dev/null +++ b/openkore_llm_knowledge/config/config.txt @@ -0,0 +1,1035 @@ +# Please Read the Users Manual +# The Manual is located at https://openkore.com/wiki/Manual + +######## Login options and server-specific options ######## + +master +server +username +password +loginPinCode +char +otpSeed + +# Poseidon Settings: https://openkore.com/wiki/Poseidon +# They must be the same as Query Server config in Poseidon.txt +poseidonServer 127.0.0.1 +poseidonPort 24390 +poseidonTimeout 5 + +bindIp +forceMapIP + +# 1 = hook into RO client, 2 = Act as stand-alone proxy, proxy = act as true proxy +# https://openkore.com/wiki/XKore +XKore 0 +XKore_port 2350 +XKore_dll NetRedirect.dll +XKore_injectDLL 1 +XKore_autoAttachIfOneExe 1 +XKore_silent 1 +XKore_bypassBotDetection 0 +XKore_exeName ragexe.exe + +# XKore 2 / Proxy configuration +XKore_listenIp 127.0.0.1 +XKore_listenPort 6901 +XKore_publicIp 127.0.0.1 +XKore_ID + +# It is not advised to set secureAdminPassword if you're using Xkore 2 +secureAdminPassword 1 +adminPassword +callSign +commandPrefix ; +callSignGM 0 +inGameAuth 0 + +macAddress + +pauseCharLogin 2 +pauseCharServer 0 +pauseMapServer 0 +ignoreInvalidLogin 0 + +# Opening cash shop when connected to map server (recv/ST0) +whenInGame_requestCashPoints 0 + +message_length_max 80 + +######## Main configuration ######## + +alias_heal sp 28 + +allowedMaps +allowedMaps_reaction 1 + +attackAuto 2 +attackAuto_party 1 +attackAuto_onlyWhenSafe 0 +attackAuto_followTarget 1 +attackAuto_inLockOnly 1 +attackAuto_notInTown 1 +attackAuto_notWhile_storageAuto 1 +attackAuto_notWhile_buyAuto 1 +attackAuto_notWhile_sellAuto 1 +attackAuto_considerDamagedAggressive 0 +attackDistance 1 +attackDistanceAuto 1 +attackMaxDistance 1 +attackMaxRouteTime 4 +attackMinPlayerDistance 2 +attackMinPortalDistance 4 +attackUseWeapon 1 +attackNoGiveup 0 +attackCanSnipe 0 +attackCheckLOS 1 +attackRouteMaxPathDistance 20 +attackLooters 0 +attackLooters_dist 1 +attackChangeTarget 1 +aggressiveAntiKS 0 + +attackUpdateMonsterPos 1 + +# the following attack keys are meant to work only in private server (rAthena/Hercules) +# if you enable this in a official server you character will act weird, because character can't attack beyond the range +attackBeyondMaxDistance_waitForAgressive 0 +attackBeyondMaxDistance_sendAttackWhileWaiting 0 +attackSendAttackWithMove 0 +attackWaitApproachFinish 1 + +autoMoveOnDeath 0 +autoMoveOnDeath_x +autoMoveOnDeath_y +autoMoveOnDeath_map + +attackEquip_topHead +attackEquip_midHead +attackEquip_lowHead +attackEquip_leftHand +attackEquip_rightHand +attackEquip_leftAccessory +attackEquip_rightAccessory +attackEquip_robe +attackEquip_armor +attackEquip_shoes +attackEquip_arrow + +# You need the breakTime plugin: https://openkore.com/wiki/BreakTime +autoBreakTime { + startTime + stopTime +} + +autoConfChange { + minTime + varTime + lvl + joblvl +} + +autoMakeArrows 0 + +autoRestart 0 + +autoRestartMin 10800 +autoRestartSeed 3600 + +autoRestartSleep 1 +autoSleepMin 900 +autoSleepSeed 900 + +autoResponse 0 +autoResponseOnHeal 0 + +autoSpell +autoSpell_safe +autoPoison + +avoidGM_namePattern +avoidGM_near 0 +avoidGM_near_inTown 0 +avoidGM_talk 0 +avoidGM_reconnect 1800 +avoidGM_ignoreList + +avoidList 1 +avoidList_inLockOnly 0 +avoidList_reconnect 1800 +avoidList_ignoreList + +avoidHiddenActors 0 +avoidHiddenMonsters 0 + +cachePlayerNames 1 +cachePlayerNames_duration 900 +cachePlayerNames_maxSize 100 + +clientSight 17 + +dcPause 1 +dcOnDeath 0 +dcOnDualLogin 0 +dcOnDisconnect 0 +dcOnEmptyArrow 0 +dcOnMaxReconnections 0 +dcOnMute 0 +dcOnPM 0 +dcOnZeny 0 +dcOnStorageFull 1 +dcOnPlayer 0 +dcOnServerShutDown 0 +dcOnServerClose 0 +dcOnJobLevel +dcOnLevel + +follow 0 +followTarget +followEmotion 1 +followEmotion_distance 4 +followFaceDirection 0 +followDistanceMax 6 +followDistanceMin 3 +followLostStep 12 +followSitAuto 0 +followBot 0 + +itemsTakeAuto 2 +itemsTakeAuto_party 0 +itemsGatherAuto 2 +itemsGatherAuto_notInTown 0 +itemsGatherAutoMinPlayerDistance 6 +itemsGatherAutoMinPortalDistance 5 +itemsMaxWeight 89 +itemsMaxWeight_sellOrStore 48 +itemsMaxNum_sellOrStore 99 +cartMaxWeight 7900 +itemsTakeGreed 0 +itemsCheckWeight 1 + +lockMap +lockMap_x +lockMap_y +lockMap_randX +lockMap_randY + +route_escape_reachedNoPortal 1 +route_escape_randomWalk 1 +route_escape_shout +route_avoidWalls 1 +route_randomWalk 1 +route_randomWalk_inTown 0 +route_randomWalk_maxRouteTime 75 +route_maxWarpFee +route_maxNpcTries 5 +route_teleport 0 +route_teleport_minDistance 75 +route_teleport_maxTries 8 +route_teleport_notInMaps +route_step 10 +route_removeMissingPortals_NPC 1 +route_removeMissingPortals 0 +route_tryToGuessMissingPortalByDistance 1 +route_reAddMissingPortals 1 +route_randomFactor 0 + +# Maximum walking path distance (client setting). Default: 17 +# This corresponds to max_walk_path in server configuration +maxWalkPathDistance 17 + +runFromTarget 0 +runFromTarget_inAdvance 0 +runFromTarget_dist 5 +runFromTarget_minStep 7 +runFromTarget_maxPathDistance 13 +runFromTarget_noAttackMethodFallback 0 +runFromTarget_noAttackMethodFallback_attackMaxDist 14 +runFromTarget_noAttackMethodFallback_minStep 8 + +saveMap +saveMap_x +saveMap_y +saveMap_warp 0 +saveMap_warpToBuyOrSell 1 +saveMap_warp_minDistance +saveMap_warpChatCommand +memo1 +memo2 +memo3 +memo4 + +shopAuto_open 0 +shop_random 0 +shop_useSkill 1 + +buyerShopAuto_open 0 +buyerShop_random 0 + +sitAuto_hp_lower 40 +sitAuto_hp_upper 100 +sitAuto_sp_lower 0 +sitAuto_sp_upper 0 +sitAuto_follow 0 +sitAuto_over_50 0 +sitAuto_idle 1 +sitAuto_look +sitAuto_look_from_wall +sitAuto_look_delay +sitTensionRelax 0 + +statsAddAuto 0 +statsAddAuto_list +statsAddAuto_dontUseBonus 0 +statsAdd_over_99 1 + +skillsAddAuto 0 +skillsAddAuto_list + +tankMode 0 +tankModeTarget + +teleportAuto_hp 10 +teleportAuto_sp 0 +teleportAuto_idle 0 +teleportAuto_portal 0 +teleportAuto_search 0 +teleportAuto_minAggressives 0 +teleportAuto_minAggressivesInLock 0 +teleportAuto_onlyWhenSafe 0 +teleportAuto_maxDmg 500 +teleportAuto_maxDmgInLock 0 +teleportAuto_deadly 1 +teleportAuto_useSkill 1 +teleportAuto_useChatCommand +teleportAuto_allPlayers 0 +teleportAuto_notPlayers +teleportAuto_atkCount 0 +teleportAuto_atkMiss 10 +teleportAuto_unstuck 0 +teleportAuto_lostTarget 0 +teleportAuto_dropTarget 0 +teleportAuto_dropTargetKS 0 +teleportAuto_dropTargetHidden 0 +teleportAuto_attackedWhenSitting 0 +teleportAuto_totalDmg 0 +teleportAuto_totalDmgInLock 0 +teleportAuto_equip_leftAccessory +teleportAuto_equip_rightAccessory +teleportAuto_lostHomunculus +teleportAuto_useItemForRespawn +teleportAuto_item1 +teleportAuto_item2 + +dealAuto 1 +dealAuto_names +partyAuto 1 +partyAutoShare 0 +partyAutoShareItem 0 +partyAutoShareItemDiv 0 +guildAutoDeny 1 +attendanceAuto 1 + +verbose 1 +showDomain 0 +showDomain_NPC parseMsg_presence +showDomain_Shop list +squelchDomains +verboseDomains +beepDomains +beepDomains_notInTown +friendlyAID +showTime +showTimeDomains +showTimeDomainsFormat +wx_map_maxAutoSize 300 +wx_map_monsterSticking 1 +wx_map_npcSticking 1 +wx_map_playersSticking 1 +wx_map_portalSticking 5 +wx_map_route +wx_npcTalk +wx_captcha +showAllDamage 0 +manualURL https://openkore.com/wiki/Manual +forumURL https://forums.openkore.com + +logChat 1 +logPrivateChat 1 +logPartyChat 1 +logGuildChat 1 +logSystemChat 1 +logLocalBroadcast 1 +logShop 1 +logEmoticons +logConsole 1 +logAppendUsername 1 +logAppendServer 0 +monsterLog 0 +playerLog 0 +logDead 1 + +questDisplayStyle 2 + +chatTitleOversize 0 +shopTitleOversize 0 +buyerShopTitleOversize 0 + +sleepTime 10000 + +ignoreAll 0 +itemHistory 0 +autoTalkCont 1 +noAutoSkill 0 +portalCompile 1 +portalRecord 2 +portalRecord_recompileAfter 1 +missDamage 0 + +tankersList + +repairAuto 0 +repairAuto_list +repairAuto_npc +repairAuto_standpoint +repairAuto_distance 3 +repairAuto_maxDistance +repairAuto_npc_steps c r0 c c r0 n +repairAuto_useSkill 0 +repairAuto_equipAfter 1 +repairAuto_warp 0 +repairAuto_inTownOnly 0 + +status_mapProperty 0 +status_mapType 0 + +monster_filter + +######## Mercenary Support ######## + +mercenary_attackAuto 2 +mercenary_attackAuto_party 1 +mercenary_attackAuto_notInTown 1 +mercenary_attackAuto_inLockOnly 1 +mercenary_attackAuto_notWhile_storageAuto 1 +mercenary_attackAuto_notWhile_buyAuto 1 +mercenary_attackAuto_notWhile_sellAuto 1 +mercenary_attackAuto_considerDamagedAggressive 0 +mercenary_attackAuto_onlyWhenSafe 0 +mercenary_attackAuto_duringRandomWalk 0 +mercenary_attackAuto_duringItemsTake 0 +mercenary_attackDistance 1 +mercenary_attackMaxDistance 1 +mercenary_attackDistanceAuto 1 +mercenary_attackMaxRouteTime 4 +mercenary_attackCanSnipe 0 +mercenary_attackCheckLOS 1 +mercenary_attackRouteMaxPathDistance 20 +mercenary_attackUseWeapon 1 +mercenary_attackNoGiveup 0 +mercenary_attackChangeTarget 1 +mercenary_attack_dance_melee 0 +mercenary_attack_dance_ranged 0 + +mercenary_attackBeyondMaxDistance_waitForAgressive 0 +mercenary_attackBeyondMaxDistance_sendAttackWhileWaiting 0 +mercenary_attackSendAttackWithMove 0 +mercenary_attackWaitApproachFinish 1 + +mercenary_lost_teleportToMaster_maxTries 6 + +mercenary_route_randomWalk_rescueWhenLost 0 +mercenary_route_randomWalk_stopDuringAttack 0 +mercenary_route_randomWalk_waitMinDistance 0 + +mercenary_runFromTarget 0 +mercenary_runFromTarget_inAdvance 0 +mercenary_runFromTarget_dist 5 +mercenary_runFromTarget_minStep 7 +mercenary_runFromTarget_maxPathDistance 20 +mercenary_runFromTarget_noAttackMethodFallback 0 +mercenary_runFromTarget_noAttackMethodFallback_attackMaxDist 14 +mercenary_runFromTarget_noAttackMethodFallback_minStep 8 + +mercenary_idleWalkType 1 +mercenary_followDistanceMin 3 +mercenary_followDistanceMax 12 + +mercenary_moveNearWhenIdle 1 +mercenary_moveNearWhenIdle_minDistance 3 +mercenary_moveNearWhenIdle_maxDistance 8 + +mercenary_route_step 10 + +mercenary_tankMode 0 +mercenary_tankModeTarget + +mercenary_teleportAuto_hp 10 +mercenary_teleportAuto_maxDmg 500 +mercenary_teleportAuto_maxDmgInLock 0 +mercenary_teleportAuto_deadly 1 +mercenary_teleportAuto_unstuck 0 +mercenary_teleportAuto_dropTarget 0 +mercenary_teleportAuto_dropTargetKS 0 +mercenary_teleportAuto_totalDmg 0 +mercenary_teleportAuto_totalDmgInLock 0 +mercenary_teleportAuto_attackedWhenSitting 0 + +######## Homunculus Support ######## + +homunculus_attackAuto 2 +homunculus_attackAuto_party 1 +homunculus_attackAuto_notInTown 1 +homunculus_attackAuto_inLockOnly 1 +homunculus_attackAuto_notWhile_storageAuto 1 +homunculus_attackAuto_notWhile_buyAuto 1 +homunculus_attackAuto_notWhile_sellAuto 1 +homunculus_attackAuto_considerDamagedAggressive 0 +homunculus_attackAuto_onlyWhenSafe 0 +homunculus_attackAuto_duringRandomWalk 0 +homunculus_attackAuto_duringItemsTake 0 +homunculus_attackDistance 1 +homunculus_attackMaxDistance 1 +homunculus_attackDistanceAuto 1 +homunculus_attackMaxRouteTime 4 +homunculus_attackCanSnipe 0 +homunculus_attackCheckLOS 1 +homunculus_attackRouteMaxPathDistance 20 +homunculus_attackUseWeapon 1 +homunculus_attackNoGiveup 0 +homunculus_attackChangeTarget 1 +homunculus_attack_dance_melee 0 + +homunculus_attackBeyondMaxDistance_waitForAgressive 0 +homunculus_attackBeyondMaxDistance_sendAttackWhileWaiting 0 +homunculus_attackSendAttackWithMove 0 +homunculus_attackWaitApproachFinish 1 + +homunculus_lost_teleportToMaster_maxTries 6 + +homunculus_route_randomWalk_rescueWhenLost 0 +homunculus_route_randomWalk_stopDuringAttack 0 +homunculus_route_randomWalk_waitMinDistance 0 + +homunculus_runFromTarget 0 +homunculus_runFromTarget_dist 5 +homunculus_runFromTarget_minStep 7 +homunculus_runFromTarget_maxPathDistance 20 +homunculus_runFromTarget_noAttackMethodFallback 0 +homunculus_runFromTarget_noAttackMethodFallback_attackMaxDist 14 +homunculus_runFromTarget_noAttackMethodFallback_minStep 8 + +homunculus_idleWalkType 1 +homunculus_followDistanceMin 3 +homunculus_followDistanceMax 12 + +homunculus_moveNearWhenIdle 1 +homunculus_moveNearWhenIdle_minDistance 3 +homunculus_moveNearWhenIdle_maxDistance 8 + +homunculus_route_step 10 + +homunculus_tankMode 0 +homunculus_tankModeTarget + +homunculus_StandByAuto 0 +homunculus_teleportAuto_hp 10 +homunculus_teleportAuto_maxDmg 500 +homunculus_teleportAuto_maxDmgInLock 0 +homunculus_teleportAuto_deadly 1 +homunculus_teleportAuto_unstuck 0 +homunculus_teleportAuto_dropTarget 0 +homunculus_teleportAuto_dropTargetKS 0 +homunculus_teleportAuto_totalDmg 0 +homunculus_teleportAuto_totalDmgInLock 0 +homunculus_teleportAuto_attackedWhenSitting 0 + +# Turn on/off homunculus autofeeding +homunculus_autoFeed 1 +# Feed homunculus when meet the hunger value (homunculus_hunger > homunculus_return) +homunculus_hunger 15 +# Return homunculus when meet the hunger value +homunculus_return 11 +# In Wich maps should we allow feeding? (leave empty for any map) +homunculus_autoFeedAllowedMaps + +# Turn on/off pet autofeeding +pet_autoFeed 1 +# Feed pet when meet the hunger value +pet_hunger 25 +# Return pet when meet the hunger value +pet_return 20 + +######## Block options ######## +# You can copy & paste any block multiple times. So if you want to +# configure two attack skills, just duplicate the attackSkillSlot block. + +attackSkillSlot { + lvl + dist 1 + maxDist 1 + maxCastTime 0 + minCastTime 0 + hp + sp > 10 + ap + homunculus + homunculus_hp + homunculus_sp + homunculus_dead + homunculus_resting + homunculus_noinfo_dead + homunculus_noinfo_resting + homunculus_onAction + homunculus_notOnAction + homunculus_whenIdle + homunculus_whenNotIdle + mercenary + mercenary_hp + mercenary_sp + mercenary_whenStatusActive + mercenary_whenStatusInactive + mercenary_onAction + mercenary_notOnAction + mercenary_whenIdle + mercenary_whenNotIdle + onAction + inQueue + notInQueue + whenStatusActive + whenStatusInactive + whenFollowing + spirit + amuletType + aggressives + previousDamage + stopWhenHit 0 + inLockOnly 0 + notInTown 0 + timeout 0 + disabled 0 + monsters + notMonsters + monstersCount + monstersCountDist + maxAttempts 0 + maxUses 0 + target_hp + target_whenStatusActive + target_whenStatusInactive + target_deltaHp + whenPartyMembersNear + whenPartyMembersNearDist + inInventory + isSelfSkill 0 + isStartSkill 0 + equip_topHead + equip_midHead + equip_lowHead + equip_leftHand + equip_rightHand + equip_leftAccessory + equip_rightAccessory + equip_robe + equip_armor + equip_shoes + equip_arrow + manualAI 0 +} + +attackComboSlot { + afterSkill + waitBeforeUse + dist 1 + maxDist 1 + isSelfSkill 1 + target_hp + target_deltaHp + monsters + notMonsters + monstersCount + monstersCountDist + whenPartyMembersNear + whenPartyMembersNearDist +} + +doCommand { + hp + sp + ap + homunculus + homunculus_hp + homunculus_sp + homunculus_dead + homunculus_resting + homunculus_noinfo_dead + homunculus_noinfo_resting + homunculus_onAction + homunculus_notOnAction + homunculus_whenIdle + homunculus_whenNotIdle + mercenary + mercenary_hp + mercenary_sp + mercenary_whenStatusActive + mercenary_whenStatusInactive + mercenary_onAction + mercenary_notOnAction + mercenary_whenIdle + mercenary_whenNotIdle + onAction + inQueue + notInQueue + whenStatusActive + whenStatusInactive + whenFollowing + spirit + amuletType + aggressives + monsters + notMonsters + monstersCount + monstersCountDist + stopWhenHit 0 + inLockOnly 0 + notWhileSitting 0 + notInTown 0 + timeout + disabled 0 + whenPartyMembersNear + whenPartyMembersNearDist + inInventory + inCart + inMap + manualAI 0 +} + +useSelf_skill { + lvl + maxCastTime 0 + minCastTime 0 + hp + sp + ap + homunculus + homunculus_hp + homunculus_sp + homunculus_dead + homunculus_resting + homunculus_noinfo_dead + homunculus_noinfo_resting + homunculus_onAction + homunculus_notOnAction + homunculus_whenIdle + homunculus_whenNotIdle + mercenary + mercenary_hp + mercenary_sp + mercenary_whenStatusActive + mercenary_whenStatusInactive + mercenary_onAction + mercenary_notOnAction + mercenary_whenIdle + mercenary_whenNotIdle + onAction + inQueue + notInQueue + whenStatusActive + whenStatusInactive + whenFollowing + spirit + amuletType + aggressives + monsters + notMonsters + monstersCount + monstersCountDist + stopWhenHit 0 + inLockOnly 0 + notWhileSitting 0 + notInTown 0 + timeout 0 + disabled 0 + whenPartyMembersNear + whenPartyMembersNearDist + inInventory + manualAI 0 +} + +useSelf_skill_smartHeal 1 + +partySkillDistance 0..8 + +partySkill { + lvl + dist 1 + maxDist 8 + maxCastTime 0 + minCastTime 0 + hp + sp + ap + homunculus + homunculus_hp + homunculus_sp + homunculus_dead + homunculus_resting + homunculus_noinfo_dead + homunculus_noinfo_resting + homunculus_onAction + homunculus_notOnAction + homunculus_whenIdle + homunculus_whenNotIdle + mercenary + mercenary_hp + mercenary_sp + mercenary_whenStatusActive + mercenary_whenStatusInactive + mercenary_onAction + mercenary_notOnAction + mercenary_whenIdle + mercenary_whenNotIdle + onAction + inQueue + notInQueue + whenStatusActive + whenStatusInactive + whenFollowing + spirit + amuletType + aggressives + monsters + notMonsters + monstersCount + monstersCountDist + stopWhenHit 0 + inLockOnly 0 + notWhileSitting 0 + notInTown 0 + timeout 0 + disabled 0 + manualAI 0 + target + target_hp + target_isJob + target_isNotJob + target_whenStatusActive + target_whenStatusInactive + target_aggressives + target_monsters + target_timeout 0 + target_deltaHp + target_dead 0 + whenPartyMembersNear + whenPartyMembersNearDist + inInventory + isSelfSkill 0 +} + +monsterSkill { + target + maxUses + whenPartyMembersNear + whenPartyMembersNearDist + # Skill Use Conditions, including isSelfSkill + # Self Conditions + # Target Monster Conditions +} + +autoSwitch_default_rightHand +autoSwitch_default_leftHand +autoSwitch_default_arrow + +# NOTE: In the case of two handed weapons, or no Shield, +# duplicate the weapon name for 'rightHand' +# To attack with bare hands, specify "[NONE]" (without the quotes) for rightHand + +autoSwitch { + rightHand + leftHand + arrow + distance + useWeapon +} + +equipAuto { + topHead + midHead + lowHead + leftHand + rightHand + leftAccessory + rightAccessory + robe + armor + shoes + arrow + monsters + notMonsters + monstersCount + monstersCountDist + weight 0 + whileSitting 0 + hp + sp + ap + homunculus + homunculus_hp + homunculus_sp + homunculus_dead + homunculus_resting + homunculus_noinfo_dead + homunculus_noinfo_resting + homunculus_onAction + homunculus_notOnAction + homunculus_whenIdle + homunculus_whenNotIdle + mercenary + mercenary_hp + mercenary_sp + mercenary_whenStatusActive + mercenary_whenStatusInactive + mercenary_onAction + mercenary_notOnAction + mercenary_whenIdle + mercenary_whenNotIdle + onAction + inQueue + notInQueue + whenStatusActive + whenStatusInactive + whenFollowing + spirit + amuletType + aggressives + stopWhenHit 0 + inLockOnly 0 + notWhileSitting 0 + notInTown 0 + timeout 0 + disabled 0 + whenPartyMembersNear + whenPartyMembersNearDist + inInventory + manualAI 0 +} + +useSelf_item { + hp + sp + ap + homunculus + homunculus_hp + homunculus_sp + homunculus_dead + homunculus_resting + homunculus_noinfo_dead + homunculus_noinfo_resting + homunculus_onAction + homunculus_notOnAction + homunculus_whenIdle + homunculus_whenNotIdle + mercenary + mercenary_hp + mercenary_sp + mercenary_whenStatusActive + mercenary_whenStatusInactive + mercenary_onAction + mercenary_notOnAction + mercenary_whenIdle + mercenary_whenNotIdle + onAction + inQueue + notInQueue + whenStatusActive + whenStatusInactive + whenFollowing + spirit + amuletType + aggressives + monsters + notMonsters + monstersCount + monstersCountDist + stopWhenHit 0 + inLockOnly 0 + notWhileSitting 0 + notInTown 0 + timeout 0 + disabled 0 + whenPartyMembersNear + whenPartyMembersNearDist + inInventory + manualAI 0 +} + +######## Autostorage/autosell ######## + +buyAuto { + npc + npc_steps b + isMarket 0 + standpoint + distance 3 + price + minAmount 2 + maxAmount 3 + batchSize + onlyIdentified + disabled 0 + maxBase + minBase +} + +sellAuto 0 +sellAuto_npc +sellAuto_standpoint +sellAuto_distance 3 +sellAuto_maxDistance +sellAuto_npc_steps s + +storageAuto 0 +storageAuto_onStart 0 +storageAuto_npc +storageAuto_standpoint +storageAuto_distance 3 +storageAuto_maxDistance +storageAuto_npc_type 1 +storageAuto_type 0 +storageAuto_npc_steps +storageAuto_password +storageAuto_keepOpen 0 +storageAuto_useChatCommand +storageAuto_useItem 0 +storageAuto_useItem_item +storageAuto_notAfterDeath +relogAfterStorage 0 +minStorageZeny 50 + +npcTimeResponse +npcWrongStepsMethod 0 + +getAuto { + minAmount + maxAmount + batchSize + passive + disabled 0 +} + +######## Debugging options; only useful for developers ######## + +debug 0 +debugPacket_unparsed 0 +debugPacket_received 0 +debugPacket_ro_sent 0 +debugPacket_sent 0 +debugPacket_exclude +debugPacket_include +debugPacket_include_dumpMethod +debugDomains +logToFile_Debug +logToFile_Errors +logToFile_Messages +logToFile_Warnings +history_max 50 diff --git a/openkore_llm_knowledge/config/consolecolors.txt b/openkore_llm_knowledge/config/consolecolors.txt new file mode 100644 index 0000000000..ae51a2023e --- /dev/null +++ b/openkore_llm_knowledge/config/consolecolors.txt @@ -0,0 +1,55 @@ +# This file controls the colors used to highlight messages in the +# console. +# Valid colors are: +# black, darkgrey, grey, white +# red, darkred, yellow, brown, green, darkgreen, +# cyan, darkcyan, blue, darkblue, magenta, darkmagenta, +# default +# +# Format: +# [Message type] +# <message domain> [foreground color][/background color] + +# Set to 0 to disable colors +useColors 1 + +[message] +attackMon cyan +attackMonMiss cyan +attacked magenta +attackedMiss magenta + +connection darkgreen +startup darkgreen +menu yellow +drop blue +useItem blue + +skill green +selfSkill green + +success green +system yellow +pm yellow +pm/sent yellow +publicchat green +guildchat darkgreen +partychat brown +selfchat green +schat yellow +list white +info white +equip grey +teleport yellow +plugin gray +route green + +[error] +default red + +[warning] +default yellow +info yellow + +[debug] +default gray diff --git a/openkore_llm_knowledge/config/items_control.txt b/openkore_llm_knowledge/config/items_control.txt new file mode 100644 index 0000000000..60c5144c9c --- /dev/null +++ b/openkore_llm_knowledge/config/items_control.txt @@ -0,0 +1,1757 @@ +# In this file you can control which items to put in storage, +# to sell, or to leave in the inventory. +# +# Syntax: +# (item name) (minimum) (auto-store) (auto-sell) [put in cart] [get from cart] +# item name : Name of the item. +# miminum : The minimum amount of this item that you want to keep in inventory. +# autostore : Set to 1 to walk back to a Kapra NPC to put in storage. +# autosell : Set to 1 to sell item at a buy/sell NPC. +# put in cart : Set to 1 to automatically put this item in cart. +# get from cart : Set to 1 to automatically get this item from cart. +# +# The example below will sell all jellopies, store all knifes and puts +# all flowers in cart: +# Jellopy 0 0 1 +# Knife 0 1 0 +# Flower 0 0 0 1 +# +# This example will get Red Potions from cart if you have less than 25 +# of them in your inventory: +# Red Potion 25 0 0 0 1 +# +# The item name isn't case sensitive. Only items found in this file will +# be stored or sold. +# +# If auto-store and auto-sell are 1, Kore will do storage first (unless +# storage is full) then sell. +# +# Kore will not sell or store equipped items. For example, if you're using +# a knife and you have a line to auto-sell all knives, kore will sell any +# extras it picks up, not the knife it's using. +# +# Don't forget to set the storageAuto, sellAuto and related configuration +# options in config.txt, or this file won't have any effect. +# +# An auto-sell list and auto-storage list is provided by default, modify it +# as you see fit. + + +# All items not mentioned in this file will be automatically stored in storage. +all 0 1 0 + + +##### HEAL/SUPPORT ITEMS ##### +# Adjust these to your needs + +602 10 1 0 # Butterfly Wing +601 100 1 0 # Fly Wing + +505 0 1 0 # Blue Potion +545 50 1 0 # Condensed Red Potion +547 50 1 0 # Condensed White Potion +546 50 1 0 # Condensed Yellow Potion +506 0 1 0 # Green Potion +501 25 1 0 # Red Potion +502 25 1 0 # Orange Potion +504 25 1 0 # White Potion +503 25 1 0 # Yellow potion + +##### ARROWS ##### +# You may want to modify these if you're an archer/hunter + +Arrow 3000 1 0 + + + +######################### +##### SELLING ITEMS ##### +######################### + + +##### WORTHLESS GEARS ##### +# Worthless gears which are sold by default + +Adventurer's Suit 0 0 1 # 2305 +Bandana 0 0 1 # 2211 +Biretta 0 0 1 # 2217 +Boots 0 0 1 # 2405 +Buckler 0 0 1 # 2103 +Cap 0 0 1 # 2227 +Chain Mail 0 0 1 # 2314 +Circlet 0 0 1 # 2233 +Coat 0 0 1 # 2309 +"Coat [1]" 0 0 1 # 2310 +Cotton Shirt 0 0 1 # 2302 +"Cotton Shirt [1]" 0 0 1 +#Crystal Pumps 0 0 1 +Diamond Ring 0 0 1 # 2613 +Diver Goggles 0 0 1 # 2205 +Eye Patch 0 0 1 # 2212 +Flower Ring 0 0 1 # 2612 +Flu Mask 0 0 1 # 2219 +Full Plate 0 0 1 # 2316 +Gemmed Sallet 0 0 1 # 2231 +Glasses 0 0 1 # 2204 +Gold Ring 0 0 1 # 2610 +Guard 0 0 1 # 2101 +Hair Band 0 0 1 # 2210 +Hat 0 0 1 # 2221 +Helm 0 0 1 # 2228 +High Heels 0 0 1 # 2409 +Hood 0 0 1 # 2501 +"Hood [1]" 0 0 1 # 2502 +Jacket 0 0 1 # 2303 +"Jacket [1]" 0 0 1 # 2304 +Manteau 0 0 1 # 2505 +Mantle 0 0 1 # 2307 +"Mantle [1]" 0 0 1 # 2308 +Mirror Shield 0 0 1 # 2107 +Mr. Smile 0 0 1 # 2278 +Muffler 0 0 1 # 2503 +Padded Armor 0 0 1 # 2313 +Pantie 0 0 1 # 2371 +Ribbon 0 0 1 # 2208 +"Ribbon [1]" 0 0 1 # 2209 +Rosary 0 0 1 # 2840 +Saint's Robe 0 0 1 # 2325 +Sandals 0 0 1 # 2401 +"Sandals [1]" 0 0 1 # 2402 +Scapulare 0 0 1 # 2323 +Shackles 0 0 1 # 2408 +Shield 0 0 1 # 2105 +Shoes 0 0 1 # 2403 +Silk Robe 0 0 1 # 2321 +Silver Ring 0 0 1 # 2611 +Silver Robe 0 0 1 # 2332 +"Silver Robe [1]" 0 0 1 # 2333 +Skull Ring 0 0 1 # 2715 +Thief Clothes 0 0 1 # 2335 +Tights 0 0 1 # 2330 +Turban 0 0 1 # 2223 +Wooden Mail 0 0 1 # 2328 +"Wooden Mail [1]" 0 0 1 # 2329 + + +##### WORTHLESS WEAPONS ##### +# Worthless weapons which are sold by default + +"Arbalest Bow [1]" 0 0 1 +Arc Wand 0 0 1 # 1612 +"Arc Wand [1]" 0 0 1 # 1610 +Axe 0 0 1 # 1303 +"Axe [3]" 0 0 1 # 1301 +"Axe [4]" 0 0 1 # 1302 +Bastard Sword 0 0 1 # 1156 +"Bastard Sword [2]" 0 0 1 # 1154 +Battle Axe 0 0 1 # 1353 +"Battle Axe [3]" 0 0 1 # 1351 +"Battle Axe [4]" 0 0 1 # 1352 +"Belt [1]" 0 0 1 # 2627 +#"Bible [2]" 0 0 1 +Bill Guisarme 0 0 1 # 1467 +Blade 0 0 1 # 1109 +"Blade [3]" 0 0 1 # 1107 +"Blade [4]" 0 0 1 # 1108 +Bow 0 0 1 # 1703 +"Bow [3]" 0 0 1 # 1701 +"Bow [4]" 0 0 1 # 1702 +"Broad Sword [1]" 0 0 1 # 1160 +Buster 0 0 1 # 1359 +"Buster [1]" 0 0 1 # 1357 +"Chain [2]" 0 0 1 # 1519 +#Chemeti Whip 0 0 1 +"Claw [1]" 0 0 1 # 1809 +"Claw [2]" 0 0 1 # 1810 +Claymore 0 0 1 # 1172 +Club 0 0 1 # 1503 +"Club [3]" 0 0 1 # 1501 +"Club [4]" 0 0 1 # 1502 +Composite Bow 0 0 1 # 1706 +"Composite Bow [3]" 0 0 1 # 1704 +Cross Bow 0 0 1 # 1712 +"Cross Bow [2]" 0 0 1 +Cutter 0 0 1 # 1206 +"Cutter [3]" 0 0 1 # 1204 +"Cutter [4]" 0 0 1 # 1205 +Dagger 0 0 1 # 1215 +"Dagger [2]" 0 0 1 # 1213 +"Dagger [3]" 0 0 1 # 1214 +"Damascus [1]" 0 0 1 # 1222 +Dirk 0 0 1 # 1212 +"Dirk [2]" 0 0 1 # 1210 +"Dirk [3]" 0 0 1 # 1211 +Falchion 0 0 1 # 1106 +"Falchion [3]" 0 0 1 # 1104 +"Finger [1]" 0 0 1 # 1811 +"Finger [2]" 0 0 1 # 1812 +Fist 0 0 1 # 1808 +"Fist [1]" 0 0 1 # 1808 +"Flail [2]" 0 0 1 # 1510 +"Flail [3]" 0 0 1 # 1511 +Flamberge 0 0 1 # 1149 +"Gakkung Bow [1]" 0 0 1 # 1714 +#"Girl's Diary [1]" 0 0 1 +Gladius 0 0 1 # 1221 +"Gladius [2]" 0 0 1 # 1219 +Glaive 0 0 1 # 1456 +"Glaive [2]" 0 0 1 # 1454 +"Glaive [3]" 0 0 1 # 1455 +Great Bow 0 0 1 # 1709 +"Great Bow [2]" 0 0 1 # 1707 +"Great Bow [3]" 0 0 1 # 1708 +Guisarme 0 0 1 # 1453 +"Guisarme [2]" 0 0 1 # 1451 +Guitar 0 0 1 # 1908 +"Guitar [1]" 0 0 1 # 1908 +"Gumoongoh [1]" 0 0 1 # 1911 +"Gumoongoh [2]" 0 0 1 # 1912 +"Haedonggum [1]" 0 0 1 # 1123 +"Hallberd [1]" 0 0 1 +"Hallberd [2]" 0 0 1 +Hammer 0 0 1 # 1356 +"Hammer [2]" 0 0 1 # 1354 +"Hammer [3]" 0 0 1 # 1355 +"Harp [1]" 0 0 1 # 1909 +"Harp [2]" 0 0 1 # 1910 +Jamadhar 0 0 1 # 1255 +Javelin 0 0 1 # 1403 +"Javelin [3]" 0 0 1 # 1401 +"Javelin [4]" 0 0 1 # 1402 +"Jur [2]" 0 0 1 # 1250 +Katana 0 0 1 # 1118 +"Katana [3]" 0 0 1 # 1116 +"Katana [4]" 0 0 1 # 1117 +"Katar [1]" 0 0 1 # 1252 +Knife 0 0 1 # 1203 +"Knife [3]" 0 0 1 # 1201 +"Knife [4]" 0 0 1 # 1202 +"Knuckle Dusters [2]" 0 0 1 # 1803 +"Knuckle Dusters [3]" 0 0 1 # 1804 +Lance 0 0 1 # 1412 +#Lariat Whip 0 0 1 +"Lute [2]" 0 0 1 # 1905 +"Lute [3]" 0 0 1 # 1906 +Mace 0 0 1 # 1506 +"Mace [3]" 0 0 1 # 1504 +#"Mace [4]" 0 0 1 +Main Gauche 0 0 1 # 1209 +"Main Gauche [3]" 0 0 1 # 1207 +"Mandolin [2]" 0 0 1 # 1903 +"Mandolin [3]" 0 0 1 # 1904 +"Morning Star [1]" 0 0 1 # 1513 +"Morning Star [2]" 0 0 1 # 1514 +"Novice Armlet [1]" 0 0 1 # 2628 +"Novice Breastplate [1]" 0 0 1 # 2340 +Orcish Axe 0 0 1 # 1309 +Orcish Sword 0 0 1 # 1124 +Partizan 0 0 1 # 1459 +"Partizan [1]" 0 0 1 # 1457 +"Partizan [2]" 0 0 1 # 1458 +Pike 0 0 1 # 1409 +"Pike [3]" 0 0 1 # 1407 +Rante Whip 0 0 1 # 1957 +"Rante Whip [1]" 0 0 1 # 1957 +Rapier 0 0 1 # 1112 +"Rapier [2]" 0 0 1 # 1110 +"Rapier [3]" 0 0 1 # 1111 +"Repeating Crossbow [1]" 0 0 1 # 1721 +"Ring Pommel Saber [2]" 0 0 1 # 1122 +Rod 0 0 1 # 1603 +"Rod [3]" 0 0 1 # 1601 +#"Rod [4]" 0 0 1 +"Rope [3]" 0 0 1 # 1950 +#"Rope [4]" 0 0 1 +"Saber [2]" 0 0 1 # 1126 +"Scimiter [2]" 0 0 1 +"Scimiter [3]" 0 0 1 +Slayer 0 0 1 # 1153 +"Slayer [2]" 0 0 1 # 1151 +"Slayer [3]" 0 0 1 # 1152 +"Smasher [2]" 0 0 1 # 1507 +"Smasher [3]" 0 0 1 # 1508 +Spear 0 0 1 # 1406 +"Spear [3]" 0 0 1 # 1404 +"Spear [4]" 0 0 1 # 1405 +Staff 0 0 1 # 1609 +"Staff [2]" 0 0 1 # 1607 +Stiletto 0 0 1 # 1218 +"Stiletto [2]" 0 0 1 # 1216 +"Stiletto [3]" 0 0 1 # 1217 +"Studded Knuckles [2]" 0 0 1 # 1805 +"Studded Knuckles [3]" 0 0 1 # 1806 +Sword 0 0 1 # 1103 +"Sword [3]" 0 0 1 # 1101 +"Sword [4]" 0 0 1 # 1102 +Sword Mace 0 0 1 # 1518 +"Tail Whip [1]" 0 0 1 # 1958 +"Tail Whip [2]" 0 0 1 # 1959 +Trident 0 0 1 # 1462 +"Trident [2]" 0 0 1 # 1460 +Tsurugi 0 0 1 # 1121 +"Tsurugi [1]" 0 0 1 # 1119 +"Tsurugi [2]" 0 0 1 # 1120 +Two-handed Axe 0 0 1 # 1362 +"Two-handed Axe [1]" 0 0 1 +Two-handed Sword 0 0 1 # 1159 +"Two-handed Sword [1]" 0 0 1 +"Violin [3]" 0 0 1 # 1901 +#"Violin [4]" 0 0 1 +"Waghnak [3]" 0 0 1 # 1801 +"Waghnak [4]" 0 0 1 # 1802 +Wand 0 0 1 # 1606 +"Wand [2]" 0 0 1 # 1604 +"Wand [3]" 0 0 1 # 1605 +#"War Axe [1]" 0 0 1 +"Whip [1]" 0 0 1 # 1960 +"Whip [2]" 0 0 1 # 1961 +"Whip [3]" 0 0 1 # 1953 +"Wire Whip [2]" 0 0 1 # 1954 +"Wire Whip [3]" 0 0 1 # 1955 + + +##### STANDARD LOOT ##### +# Useless items that are sold by default + +1026 0 0 1 # Acorn +1054 0 0 1 # Ancient Lips +1053 0 0 1 # Ancient Tooth +Animal Poop 0 0 1 +919 0 0 1 # Animal Skin +7003 0 0 1 # Anolian Skin +7106 0 0 1 # Antelope Horn +7107 0 0 1 # Antelope Skin +1014 0 0 1 # Ant Jaw +512 0 0 1 # Apple +531 0 0 1 # Apple Juice +7119 0 0 1 # Bacillus +513 0 0 1 # Banana +532 0 0 1 # Banana Juice +1068 0 0 1 # Barren Trunk +948 0 0 1 # Bear's Footskin +939 0 0 1 # Bee Sting +925 0 0 1 # Bill of Birds +1020 0 0 1 # Black Hair +737 0 0 1 # Black Ladle +1034 0 0 1 # Blue Hair +744 0 0 1 # Bouquet +7054 0 0 1 # Brigan +7158 0 0 1 # Broken Liquor Jar +7070 0 0 1 # Broken Shell +7156 0 0 1 # Broken Shuriken +7110 0 0 1 # Broken Sword +7062 0 0 1 # Broken Turtle Shell +1042 0 0 1 # Bug Leg +7122 0 0 1 # Burning Hair +7097 0 0 1 # Burning Heart +7120 0 0 1 # Burning Horseshoe +7068 0 0 1 # Burnt Tree +952 0 0 1 # Cactus Needle +515 0 0 1 # Carrot +534 0 0 1 # Carrot Juice +548 0 0 1 # Cheese +736 0 0 1 # China +742 0 0 1 # Chonchon Doll +915 0 0 1 # Chrysalis +735 0 0 1 # Chung Jah +966 0 0 1 # Clam Flesh +965 0 0 1 # Clam Shell +7030 0 0 1 # Claw of Desert Wolf +7011 0 0 1 # Claw of Monkey +7007 0 0 1 # Claw of Rat +705 0 0 1 # Clover +1025 0 0 1 # Cobweb +7093 0 0 1 # Cogwheel +961 0 0 1 # Conch +7013 0 0 1 # Coral Reef +7050 0 0 1 # Cotton Mat +7037 0 0 1 # Coupon +964 0 0 1 # Crab Shell +747 0 0 1 # Crystal Mirror +1045 0 0 1 # Cultish Masque +7053 0 0 1 # Cyfar +901 0 0 1 # Daenggie +7157 0 0 1 # Dark Mask +1047 0 0 1 # Dead Medusa +957 0 0 1 # Decayed Nail +7069 0 0 1 # Destroyed Armor +1051 0 0 1 # Detonator +1021 0 0 1 # Dokebi Horn +1035 0 0 1 # Dragon Canine +1036 0 0 1 # Dragon Scale +7123 0 0 1 # Dragon Skin +1037 0 0 1 # Dragon Tail +1055 0 0 1 # Earthworm Peeling +1040 0 0 1 # Elder Pixie's Moustache +1011 0 0 1 # Emveretarcon +540 0 0 1 # Falcon Food +1063 0 0 1 # Fang +949 0 0 1 # Feather +916 0 0 1 # Feather of Birds +951 0 0 1 # Fin +1066 0 0 1 # Fine-grained Trunk +7041 0 0 1 # Fine Grit +7043 0 0 1 # Fine Sand +1023 0 0 1 # Fish Tail +712 0 0 1 # Flower +914 0 0 1 # Fluff +7094 0 0 1 # Fragment +1012 0 0 1 # Frill +749 0 0 1 # Frozen Rose +#Garlet 0 0 1 +956 0 0 1 # Gill +746 0 0 1 # Glass Bead +7009 0 0 1 # Glitter Shell +7152 0 0 1 # Glossy Hair +1060 0 0 1 # Golden Hair +940 0 0 1 # Grasshopper's Leg +1056 0 0 1 # Grit +7115 0 0 1 # Harpy Feather +7116 0 0 1 # Harpy Talon +950 0 0 1 # Heart of Mermaid +947 0 0 1 # Horn +1048 0 0 1 # Horrendous Hair +958 0 0 1 # Horrendous Mouth +944 0 0 1 # Horseshoe +7066 0 0 1 # Ice Cubic +929 0 0 1 # Immortal Heart +928 0 0 1 # Insect Feeler +7028 0 0 1 # Invite for Duel +1062 0 0 1 # Jack o' Pumpkin +7147 0 0 1 # Jasmine +909 0 0 1 # Jellopy +1041 0 0 1 # Lantern +7126 0 0 1 # Large Jellopy +7096 0 0 1 # Lava +7098 0 0 1 # Live Coal +1098 0 0 1 # Manacles +1028 0 0 1 # Mane +1032 0 0 1 # Maneater Blossom +1033 0 0 1 # Maneater Root +1031 0 0 1 # Mantis Scythe +7035 0 0 1 # Matchstick +517 0 0 1 # Meat +934 0 0 1 # Memento +7497 0 0 1 # Metal Fragment +519 0 0 1 # Milk +7001 0 0 1 # Mould Powder +1018 0 0 1 # Mole Claw +1017 0 0 1 # Mole Whiskers +528 0 0 1 # Monster's Feed +1057 0 0 1 # Moth Dust +7148 0 0 1 # Mother's Letter +1058 0 0 1 # Moth Wings +7004 0 0 1 # Mud Lump +921 0 0 1 # Mushroom Spore +1095 0 0 1 # Needle of Alarm +7102 0 0 1 # Nightmare +1022 0 0 1 # Nine Tails +960 0 0 1 # Nipper +941 0 0 1 # Nose Ring +5037 0 0 1 # Nut Shell +7002 0 0 1 # Ogre Tooth +7151 0 0 1 # Oil Paper +7031 0 0 1 # Old Frying Pan +7072 0 0 1 # Old Shuriken +1043 0 0 1 # Orc Claw +931 0 0 1 # Orcish Voucher +922 0 0 1 # Orc's Fang +751 0 0 1 # Osiris Doll +7101 0 0 1 # PecoPeco Feather +541 0 0 1 # PecoPeco Food +738 0 0 1 # Pencil Case +1010 0 0 1 # Phracon +7150 0 0 1 # Piece of Bamboo +7032 0 0 1 # Piece of Egg Shell +7108 0 0 1 # Piece of Shield +906 0 0 1 # Pointed Scale +7154 0 0 1 # Poisonous Powder +7155 0 0 1 # Poisonous Toad Skin +7033 0 0 1 # Poison Spore +1027 0 0 1 # Porcupine Quill +741 0 0 1 # Poring Doll +924 0 0 1 # Powder of Butterfly +535 0 0 1 # Pumpkin +740 0 0 1 # Puppet +945 0 0 1 # Raccoon Leaf +754 0 0 1 # Racoon Doll +7145 0 0 1 # Ragnarok T-shirt +1013 0 0 1 # Rainbow Shell +1016 0 0 1 # Rat Tail +544 0 0 1 # Raw Fish +734 0 0 1 # Red Frame +#Red Stocking 0 0 1 +1064 0 0 1 # Reins +903 0 0 1 # Reptile Tongue +907 0 0 1 # Resin +550 0 0 1 # Rice Cake +752 0 0 1 # Rocker Doll +7082 0 0 1 # Root of Stone +930 0 0 1 # Rotten Bandage +739 0 0 1 # Rouge +1096 0 0 1 # Round Shell +7124 0 0 1 # Sand Clump +936 0 0 1 # Scale Shell +#Scell 0 0 1 +7125 0 0 1 # Scorpion Claw +1046 0 0 1 # Scorpion Nipper +904 0 0 1 # Scorpion Tail +7065 0 0 1 # Sea-otter Fur +7100 0 0 1 # Sharp Leaf +7112 0 0 1 # Sharp Paper +Sharp scale 0 0 1 +935 0 0 1 # Shell +954 0 0 1 # Shining Scale +7109 0 0 1 # Shining Spear Blade +711 0 0 1 # Shoot +1094 0 0 1 # Short Daenggie +7051 0 0 1 # Silk Mat +1052 0 0 1 # Single Cell +932 0 0 1 # Skel-Bone +7420 0 0 1 # Skull +7111 0 0 1 # Slick Paper +946 0 0 1 # Snail's Shell +926 0 0 1 # Snake Scale +661 0 0 1 # Soft Apron +943 0 0 1 # Solid Shell +1067 0 0 1 # Solid Trunk +Songpyun 0 0 1 +908 0 0 1 # Spawn +7016 0 0 1 # Spoon Stub +743 0 0 1 # Spore Doll +1024 0 0 1 # Squid Ink +905 0 0 1 # Stem +938 0 0 1 # Sticky Mucus +918 0 0 1 # Sticky Webfoot +7008 0 0 1 # Stiff Horn +959 0 0 1 # Stinky Scale +7049 0 0 1 # Stone +7067 0 0 1 # Stone Fragment +953 0 0 1 # Stone Heart +551 0 0 1 # Sushi +7010 0 0 1 # Tail of Steel Scorpion +917 0 0 1 # Talon +7071 0 0 1 # Tattered Clothes +1050 0 0 1 # Tendon +7159 0 0 1 # Tengu Nose +962 0 0 1 # Tentacle +1029 0 0 1 # Tiger Skin +1015 0 0 1 # Tongue +913 0 0 1 # Tooth of Bat +7012 0 0 1 # Tough Scalelike Stem +902 0 0 1 # Tree Root +#Tribal Solidarity 0 0 1 +967 0 0 1 # Turtle Shell +937 0 0 1 # Venom Canine +745 0 0 1 # Wedding Bouquet +7064 0 0 1 # Wing of Dragonfly +7006 0 0 1 # Wing of Red Bat +748 0 0 1 # Witherless Rose +920 0 0 1 # Wolf Claw +955 0 0 1 # Worm Peeling +7153 0 0 1 # Worn-out Kimono +1097 0 0 1 # Worn Out Page +1099 0 0 1 # Worn-out Prison Uniform +549 0 0 1 # Yam +7038 0 0 1 # Yarn +7149 0 0 1 # Yellow Plate +7018 0 0 1 # Young Twig +753 0 0 1 # Yoyo Doll +942 0 0 1 # Yoyo Tail +#Zargon 0 0 1 +1044 0 0 1 # Zenorc's Fang + +##### UNSTORAGEABLE ITEMS ##### + +Bowman Scroll 1 0 0 0 +Bowman Scroll 2 0 0 0 +Bowman Scroll 3 0 0 0 +Bowman Scroll 4 0 0 0 +Bowman Scroll 5 0 0 0 +Bowman Scroll 6 0 0 0 +Bowman Scroll 7 0 0 0 +Bowman Scroll 8 0 0 0 +Bowman Scroll 9 0 0 0 +Bowman Scroll 10 0 0 0 +Fencer Scroll 1 0 0 0 +Fencer Scroll 2 0 0 0 +Fencer Scroll 3 0 0 0 +Fencer Scroll 4 0 0 0 +Fencer Scroll 5 0 0 0 +Fencer Scroll 6 0 0 0 +Fencer Scroll 7 0 0 0 +Fencer Scroll 8 0 0 0 +Fencer Scroll 9 0 0 0 +Fencer Scroll 10 0 0 0 +Spearman Scroll 1 0 0 0 +Spearman Scroll 2 0 0 0 +Spearman Scroll 3 0 0 0 +Spearman Scroll 4 0 0 0 +Spearman Scroll 5 0 0 0 +Spearman Scroll 6 0 0 0 +Spearman Scroll 7 0 0 0 +Spearman Scroll 8 0 0 0 +Spearman Scroll 9 0 0 0 +Spearman Scroll 10 0 0 0 + + +671 0 0 0 0 0 # Gold_Coin +675 0 0 0 0 0 # Silver_Coin +1173 0 0 0 0 0 # Muramasa_C +1174 0 0 0 0 0 # Executioner_C +1183 0 0 0 0 0 # BF_Two_Handed_Sword1 +1184 0 0 0 0 0 # BF_Two_Handed_Sword2 +1187 0 0 0 0 0 # Krieger_Twohand_Sword1 +1190 0 0 0 0 0 # Claymore_C +1192 0 0 0 0 0 # P_Slayer1 +1193 0 0 0 0 0 # P_Slayer2 +1197 0 0 0 0 0 # P_Slayer3 +1198 0 0 0 0 0 # Hairtail +1243 0 0 0 0 0 # Novice_Knife +1267 0 0 0 0 0 # Infiltrator_C +1273 0 0 0 0 0 # Bloody_Roar_C +1274 0 0 0 0 0 # Unholy_Touch_C +1279 0 0 0 0 0 # BF_Katar1 +1280 0 0 0 0 0 # BF_Katar2 +1281 0 0 0 0 0 # Krieger_Katar1 +1282 0 0 0 0 0 # Krieger_Katar2 +1283 0 0 0 0 0 # Katar_Of_Speed +1286 0 0 0 0 0 # Jamadhar_C +1288 0 0 0 0 0 # Bloody_Fear_C +1289 0 0 0 0 0 # P_Katar1 +1296 0 0 0 0 0 # Metal_Katar +1299 0 0 0 0 0 # TE_Woe_Katar +1310 0 0 0 0 0 # Krieger_Onehand_Axe1 +1312 0 0 0 0 0 # Orcish_Axe_C +1313 0 0 0 0 0 # Tourist_Axe +1319 0 0 0 0 0 # TE_Woe_Axe +1372 0 0 0 0 0 # Right_Epsilon_C +1373 0 0 0 0 0 # Brood_Axe_C +1374 0 0 0 0 0 # Tomahawk_C +1378 0 0 0 0 0 # Great_Axe_C +1379 0 0 0 0 0 # BF_Two_Handed_Axe1 +1380 0 0 0 0 0 # BF_Two_Handed_Axe2 +1382 0 0 0 0 0 # Krieger_Twohand_Axe1 +1386 0 0 0 0 0 # Doom_Slayer_I +1388 0 0 0 0 0 # Two_Handed_Axe_C +1391 0 0 0 0 0 # P_Two_Handed_Axe1 +1398 0 0 0 0 0 # Metal_Two_Handed_Axe +1399 0 0 0 0 0 # TE_Woe_Two_Handed_Axe +1419 0 0 0 0 0 # Pole_Axe_C +1424 0 0 0 0 0 # Skewer_C +1425 0 0 0 0 0 # BF_Spear1 +1426 0 0 0 0 0 # Krieger_Onehand_Spear1 +1427 0 0 0 0 0 # Spear_Of_Excellent +1430 0 0 0 0 0 # Pike_C +1434 0 0 0 0 0 # P_Spear1 +1437 0 0 0 0 0 # TE_Woe_Pike +1482 0 0 0 0 0 # BF_Lance1 +1486 0 0 0 0 0 # Krieger_Twohand_Spear1 +1487 0 0 0 0 0 # Lance_C +1488 0 0 0 0 0 # Ahlspiess_C +1489 0 0 0 0 0 # Spearfish_ +1493 0 0 0 0 0 # Metal_Lance +1495 0 0 0 0 0 # TE_Woe_Lance +1533 0 0 0 0 0 # Warrior_Balmung +1534 0 0 0 0 0 # Spanner_C +1537 0 0 0 0 0 # Quadrille_C +1542 0 0 0 0 0 # BF_Morning_Star1 +1543 0 0 0 0 0 # BF_Morning_Star2 +1546 0 0 0 0 0 # Krieger_Onehand_Mace1 +1547 0 0 0 0 0 # Mace_Of_Madness +1563 0 0 0 0 0 # Diary_Of_Great_Sage_C +1567 0 0 0 0 0 # Hardback_C +1574 0 0 0 0 0 # BF_Book1 +1575 0 0 0 0 0 # BF_Book2 +1576 0 0 0 0 0 # Krieger_Book1 +1577 0 0 0 0 0 # Krieger_Book2 +1578 0 0 0 0 0 # Book_Of_Prayer +1580 0 0 0 0 0 # Encyclopedia_C +1583 0 0 0 0 0 # P_Book1 +1588 0 0 0 0 0 # Metal_Book +1591 0 0 0 0 0 # TE_Woe_Book +1623 0 0 0 0 0 # Mighty_Staff_C +1628 0 0 0 0 0 # Survival_Rod_C +1632 0 0 0 0 0 # BF_Staff1 +1633 0 0 0 0 0 # BF_Staff2 +1634 0 0 0 0 0 # BF_Staff3 +1635 0 0 0 0 0 # BF_Staff4 +1638 0 0 0 0 0 # Healing_Staff_C +1640 0 0 0 0 0 # Krieger_Onehand_Staff1 +1641 0 0 0 0 0 # Krieger_Onehand_Staff2 +1642 0 0 0 0 0 # Staff_Of_Darkness +1650 0 0 0 0 0 # P_Staff1 +1651 0 0 0 0 0 # P_Staff2 +1652 0 0 0 0 0 # Tourist_Staff +1653 0 0 0 0 0 # Staff_Of_Healing_C +1658 0 0 0 0 0 # P_Staff3 +1667 0 0 0 0 0 # TE_Woe_Staff +1699 0 0 0 0 0 # Paradise_Foxtail_Staff_I +1703 0 0 0 0 0 # Bow__ +1728 0 0 0 0 0 # Balistar_C +1729 0 0 0 0 0 # Bow_Of_Rudra_C +1738 0 0 0 0 0 # BF_Bow1 +1739 0 0 0 0 0 # BF_Bow2 +1743 0 0 0 0 0 # Krieger_Bow1 +1744 0 0 0 0 0 # Bow_Of_Evil +1747 0 0 0 0 0 # P_Bow1 +1748 0 0 0 0 0 # P_Bow2 +1749 0 0 0 0 0 # Tourist_Bow +1817 0 0 0 0 0 # Kaiser_Knuckle_C +1823 0 0 0 0 0 # BF_Knuckle1 +1824 0 0 0 0 0 # BF_Knuckle2 +1826 0 0 0 0 0 # Krieger_Knuckle1 +1827 0 0 0 0 0 # Krieger_Knuckle2 +1828 0 0 0 0 0 # Monk_Knuckle +1829 0 0 0 0 0 # Fist_C +1831 0 0 0 0 0 # P_Knuckle1 +1834 0 0 0 0 0 # TE_Woe_Fist +1923 0 0 0 0 0 # BF_Instrument1 +1924 0 0 0 0 0 # BF_Instrument2 +1927 0 0 0 0 0 # Krieger_Instrument1 +1928 0 0 0 0 0 # Berserk_Guitar_I +1929 0 0 0 0 0 # Guitar_C +1931 0 0 0 0 0 # P_Guitar1 +1932 0 0 0 0 0 # TE_Woe_Guitar +1977 0 0 0 0 0 # BF_Whip1 +1978 0 0 0 0 0 # BF_Whip2 +1981 0 0 0 0 0 # Krieger_Whip1 +1982 0 0 0 0 0 # Phenomena_Whip +1983 0 0 0 0 0 # Rante_C +1986 0 0 0 0 0 # P_Tail1 +1987 0 0 0 0 0 # TE_Woe_Rope +2002 0 0 0 0 0 # Krieger_Twohand_Staff1 +2018 0 0 0 0 0 # Metal_Staff +2019 0 0 0 0 0 # TE_Woe_Two_Hand_Staff +2112 0 0 0 0 0 # Novice_Guard +2132 0 0 0 0 0 # Shelter_Resistance +2136 0 0 0 0 0 # Cracked_Buckler +2137 0 0 0 0 0 # Valkyrja +2140 0 0 0 0 0 # Energy_Rune_Guard +2152 0 0 0 0 0 # Anti_Demon_Shield_C +2178 0 0 0 0 0 # TE_Woe_Buckler +2179 0 0 0 0 0 # TE_Woe_Shield +2180 0 0 0 0 0 # TE_Woe_Magic_Guard +2352 0 0 0 0 0 # Novice_Plate +2369 0 0 0 0 0 # Freyja_Overcoat +2376 0 0 0 0 0 # Assaulter_Plate +2377 0 0 0 0 0 # Elite_Engineer_Armor +2378 0 0 0 0 0 # Assassin_Robe +2379 0 0 0 0 0 # Warlock_Battle_Robe +2380 0 0 0 0 0 # Medic_Robe +2381 0 0 0 0 0 # Elite_Archer_Suit +2382 0 0 0 0 0 # Elite_Shooter_Suit +2384 0 0 0 0 0 # Spritual_Tunic +2385 0 0 0 0 0 # Recuperative_Armor +2392 0 0 0 0 0 # Old_Pant +2393 0 0 0 0 0 # N_Adventurer +2394 0 0 0 0 0 # Krieger_Suit1 +2395 0 0 0 0 0 # Krieger_Suit2 +2396 0 0 0 0 0 # Krieger_Suit3 +2414 0 0 0 0 0 # Novice_Boots +2428 0 0 0 0 0 # Freyja_Boots +2429 0 0 0 0 0 # Iron_Boots01 +2430 0 0 0 0 0 # Iron_Boots02 +2435 0 0 0 0 0 # Battle_Greave +2436 0 0 0 0 0 # Combat_Boots +2437 0 0 0 0 0 # Battle_Boots +2439 0 0 0 0 0 # Refresh_Shoes +2442 0 0 0 0 0 # Boots_Perforated +2443 0 0 0 0 0 # Fish_Shoes +2444 0 0 0 0 0 # Krieger_Shoes1 +2445 0 0 0 0 0 # Krieger_Shoes2 +2446 0 0 0 0 0 # Krieger_Shoes3 +2456 0 0 0 0 0 # Para_Team_Boots1 +2457 0 0 0 0 0 # Para_Team_Boots2 +2458 0 0 0 0 0 # Para_Team_Boots3 +2463 0 0 0 0 0 # Feral_Boots +2473 0 0 0 0 0 # Para_Team_Boots4 +2496 0 0 0 0 0 # TE_Woe_Shoes +2497 0 0 0 0 0 # TE_Woe_Boots +2498 0 0 0 0 0 # TE_Woe_Magic_Sandal +2510 0 0 0 0 0 # Novice_Hood +2533 0 0 0 0 0 # Freyja_Cape +2535 0 0 0 0 0 # Cloak_Of_Survival_C +2538 0 0 0 0 0 # Commander_Manteau +2539 0 0 0 0 0 # Commander_Manteau_ +2540 0 0 0 0 0 # Sheriff_Manteau +2543 0 0 0 0 0 # Sylphid_Manteau +2547 0 0 0 0 0 # Cheap_Running_Shirts +2548 0 0 0 0 0 # Muffler_C +2549 0 0 0 0 0 # Krieger_Muffler1 +2560 0 0 0 0 0 # Para_Team_Manteau1 +2564 0 0 0 0 0 # Feral_Tail +2566 0 0 0 0 0 # Half_Asprika +2571 0 0 0 0 0 # Para_Team_Manteau2 +2634 0 0 0 0 0 # Bridegroom_Ring +2635 0 0 0 0 0 # Bride_Ring +2642 0 0 0 0 0 # Serin +2644 0 0 0 0 0 # The_Sign_ +2647 0 0 0 0 0 # Nile_Rose +2668 0 0 0 0 0 # Women +2673 0 0 0 0 0 # Shining_Ring +2674 0 0 0 0 0 # Honor_Ring +2676 0 0 0 0 0 # Hunter_Earring +2681 0 0 0 0 0 # Republic_Ring +2686 0 0 0 0 0 # Elven_Ears_C +2687 0 0 0 0 0 # Steel_Flower_C +2688 0 0 0 0 0 # Critical_Ring_C +2689 0 0 0 0 0 # Earring_C +2690 0 0 0 0 0 # Ring_C +2691 0 0 0 0 0 # Necklace_C +2692 0 0 0 0 0 # Glove_C +2693 0 0 0 0 0 # Brooch_C +2694 0 0 0 0 0 # Rosary_C +2695 0 0 0 0 0 # Safety_Ring_C +2696 0 0 0 0 0 # Vesper_Core01_C +2697 0 0 0 0 0 # Vesper_Core02_C +2698 0 0 0 0 0 # Vesper_Core03_C +2699 0 0 0 0 0 # Vesper_Core04_C +2706 0 0 0 0 0 # Handcuff +2709 0 0 0 0 0 # 5_Anniversary_Coin +2710 0 0 0 0 0 # Bloody_Iron_Ball_C +2711 0 0 0 0 0 # Spiritual_Ring_C +2713 0 0 0 0 0 # Certificate_TW +2720 0 0 0 0 0 # Medal_Swordman +2721 0 0 0 0 0 # Medal_Thief +2722 0 0 0 0 0 # Medal_Acolyte +2723 0 0 0 0 0 # Medal_Mage +2724 0 0 0 0 0 # Medal_Archer +2725 0 0 0 0 0 # Medal_Merchant +2733 0 0 0 0 0 # Medal_Gunner +2734 0 0 0 0 0 # Directive_A +2735 0 0 0 0 0 # Directive_B +2738 0 0 0 0 0 # Shiny_Coin +2739 0 0 0 0 0 # Ordinary_Coin +2740 0 0 0 0 0 # Rusty_Coin +2741 0 0 0 0 0 # All_In_One_Ring +2742 0 0 0 0 0 # Lucky_Clip +2750 0 0 0 0 0 # Summer_Night_Dream +2752 0 0 0 0 0 # Praxinus_C +2753 0 0 0 0 0 # Beholder_Ring +2754 0 0 0 0 0 # Hallow_Ring +2755 0 0 0 0 0 # Clamorous_Ring +2756 0 0 0 0 0 # Chemical_Ring +2757 0 0 0 0 0 # Insecticide_Ring +2758 0 0 0 0 0 # Fisher_Ring +2759 0 0 0 0 0 # Decussate_Ring +2760 0 0 0 0 0 # Bloody_Ring +2761 0 0 0 0 0 # Satanic_Ring +2762 0 0 0 0 0 # Dragoon_Ring +2763 0 0 0 0 0 # Skul_Ring_C +2766 0 0 0 0 0 # Swordman_Figure +2767 0 0 0 0 0 # Acolyte_Figure +2768 0 0 0 0 0 # Mage_Figure +2769 0 0 0 0 0 # Archer_Figure +2770 0 0 0 0 0 # Thief_Figure +2771 0 0 0 0 0 # Merchant_Figure +2772 0 0 0 0 0 # Krieger_Ring1 +2773 0 0 0 0 0 # Krieger_Ring2 +2774 0 0 0 0 0 # Krieger_Ring3 +2792 0 0 0 0 0 # Ring_Of_Flame_Lord_I +2793 0 0 0 0 0 # Ring_Of_Resonance_I +2794 0 0 0 0 0 # Magic_Stone_Ring +2795 0 0 0 0 0 # Green_Apple_Ring +2796 0 0 0 0 0 # Magical_Stone +2797 0 0 0 0 0 # Magical_Stone_ +2798 0 0 0 0 0 # Will_Of_Exhausted_Angel +2816 0 0 0 0 0 # Radar_Ring1 +2817 0 0 0 0 0 # Radar_Ring2 +2818 0 0 0 0 0 # Radar_Ring3 +2819 0 0 0 0 0 # Swordman_Manual +2820 0 0 0 0 0 # Thief_Manual +2821 0 0 0 0 0 # Acolyte_Manual +2822 0 0 0 0 0 # Archer_Manual +2823 0 0 0 0 0 # Merchant_Manual +2823 0 0 0 0 0 # Merchant_Manual +2824 0 0 0 0 0 # Mage_Manual +2832 0 0 0 0 0 # Freyja_Ring +2841 0 0 0 0 0 # Caracas_Ring +2843 0 0 0 0 0 # Gold_Trickle +2856 0 0 0 0 0 # Half_Megin +2857 0 0 0 0 0 # Half_Brysing +2863 0 0 0 0 0 # Ring_Of_Valkyrie +2864 0 0 0 0 0 # Light_Of_Cure +2865 0 0 0 0 0 # Seal_Of_Cathedral +2866 0 0 0 0 0 # Ring_Of_Archbishop +2885 0 0 0 0 0 # Mother_Heart +2885 0 0 0 0 0 # Mother_Heart +2940 0 0 0 0 0 # Ninja_Manual +2941 0 0 0 0 0 # Gunslinger_Manual +2942 0 0 0 0 0 # Taekwon_Manual +2944 0 0 0 0 0 # TE_Ring_Of_Protection +2945 0 0 0 0 0 # TE_Ring_Of_Rage +2946 0 0 0 0 0 # TE_Ring_Of_Defiance +2950 0 0 0 0 0 # Rune_Ring +5147 0 0 0 0 0 # Baseball_Cap +5149 0 0 0 0 0 # Silver_Tiara +5179 0 0 0 0 0 # Gold_Tiara +5265 0 0 0 0 0 # Apple_Of_Archer_C +5266 0 0 0 0 0 # Bunny_Band_C +5267 0 0 0 0 0 # Sahkkat_C +5268 0 0 0 0 0 # Lord_Circlet_C +5269 0 0 0 0 0 # Flying_Angel_ +5279 0 0 0 0 0 # Drooping_Kitty_C +5280 0 0 0 0 0 # Magestic_Goat_C +5281 0 0 0 0 0 # Deviruchi_Cap_C +5286 0 0 0 0 0 # Pecopeco_Hairband +5288 0 0 0 0 0 # Red_Glasses +5293 0 0 0 0 0 # Ramen_Hat +5294 0 0 0 0 0 # Whisper_Mask +5306 0 0 0 0 0 # Freyja_Crown +5314 0 0 0 0 0 # Hockey_Mask +5315 0 0 0 0 0 # Observer +5326 0 0 0 0 0 # Masquerade_C +5327 0 0 0 0 0 # Orc_Hero_Helm_C +5328 0 0 0 0 0 # Evil_Wing_Ears_C +5329 0 0 0 0 0 # Dark_Blindfold_C +5330 0 0 0 0 0 # kRO_Drooping_Kitty_C +5331 0 0 0 0 0 # Corsair_C +5336 0 0 0 0 0 # Guildsman_Recruiter +5337 0 0 0 0 0 # Party_Recruiter_Hat +5338 0 0 0 0 0 # Bf_Recruiter_Hat +5339 0 0 0 0 0 # Friend_Recruiter_Hat +5346 0 0 0 0 0 # Gf_Recruiter_Hat +5391 0 0 0 0 0 # Toast_C +5394 0 0 0 0 0 # Bubblegum_Lower +5408 0 0 0 0 0 # Old_Bandanna +5410 0 0 0 0 0 # Bread_Bag2 +5460 0 0 0 0 0 # Adv_Dragon_Skull +5461 0 0 0 0 0 # Adv_Whisper_Mask +5462 0 0 0 0 0 # Spiked_Scarf +5463 0 0 0 0 0 # Rainbow_Scarf +5492 0 0 0 0 0 # Boy +5493 0 0 0 0 0 # Ulle_Cap_I +5494 0 0 0 0 0 # Spinx_Helm_I +5521 0 0 0 0 0 # Angry_Mouth_C +5536 0 0 0 0 0 # Spare_Card +5551 0 0 0 0 0 # Holy_Egg_Hat +5583 0 0 0 0 0 # Para_Team_Hat1 +5587 0 0 0 0 0 # Mosquito_Coil_1Use +5596 0 0 0 0 0 # 4Leaf_Clover_In_Mouth +5597 0 0 0 0 0 # Bubble_Gum_In_Mouth +5668 0 0 0 0 0 # Weird_Pumpkin_Hat +5793 0 0 0 0 0 # Ribbon_Of_Life +5794 0 0 0 0 0 # 3D_Glasses_ +5796 0 0 0 0 0 # Cheer_Scarf +5797 0 0 0 0 0 # Cheer_Scarf2 +5798 0 0 0 0 0 # Cheer_Scarf3 +5799 0 0 0 0 0 # Cheer_Scarf4 +5812 0 0 0 0 0 # Hat_Of_Expert +5814 0 0 0 0 0 # Ati_Atihan_Hat3 +5817 0 0 0 0 0 # Valentine_Pledge +5818 0 0 0 0 0 # Carnival_Hat +5819 0 0 0 0 0 # Carnival_Circlet +5822 0 0 0 0 0 # Love_Chick_Hat +5855 0 0 0 0 0 # Fishing_Rod +6006 0 0 0 0 0 # Rice_Cake_Delivery_Box +6007 0 0 0 0 0 # New_Year_Rice_Cake_Soup +6009 0 0 0 0 0 # Large_Magical_Fan +6025 0 0 0 0 0 # Towel_Of_Memory +6027 0 0 0 0 0 # Crystal_Of_Feardoom +6028 0 0 0 0 0 # Seal_Scroll +6029 0 0 0 0 0 # Morocc_Tracing_Log +6034 0 0 0 0 0 # Weird_Part +6035 0 0 0 0 0 # Decaying_Stem +6036 0 0 0 0 0 # Invite_To_Meeting +6037 0 0 0 0 0 # Rough_File +6038 0 0 0 0 0 # Neat_Report +6039 0 0 0 0 0 # Piece_Of_Fish +6040 0 0 0 0 0 # Some_Of_Report +6042 0 0 0 0 0 # Ordinary_Branch +6043 0 0 0 0 0 # Letter_From_Lugen +6044 0 0 0 0 0 # Letter_From_Otto +6045 0 0 0 0 0 # Supply_Box +6048 0 0 0 0 0 # Unidentified_Mineral +6049 0 0 0 0 0 # Marlin +6051 0 0 0 0 0 # Gray_Hollow +6052 0 0 0 0 0 # Ornamental_Hairpin +6060 0 0 0 0 0 # Moon_Admin_Ticket +6074 0 0 0 0 0 # Bazett +6076 0 0 0 0 0 # Portable_Toolbox +6077 0 0 0 0 0 # Rough_Mineral +6078 0 0 0 0 0 # Stone_Fragments +6079 0 0 0 0 0 # Flower_Of_Alfheim +6080 0 0 0 0 0 # Manuk_Coin +6081 0 0 0 0 0 # Splendide_Coin +6082 0 0 0 0 0 # Spirit_Of_Alfheim +6084 0 0 0 0 0 # Bradium_Fragments +6085 0 0 0 0 0 # Shaggy_Muffler +6093 0 0 0 0 0 # Egg_Of_Draco +6101 0 0 0 0 0 # Attendance_Card +6102 0 0 0 0 0 # Report_On_Splendide +6103 0 0 0 0 0 # Report_On_Manuk +6116 0 0 0 0 0 # Succu_Pet_Coupon +6117 0 0 0 0 0 # Imp_Pet_Coupon +6118 0 0 0 0 0 # Chung_E_Pet_Coupon +6121 0 0 0 0 0 # Makeover_Brush +6122 0 0 0 0 0 # Paint_Brush +6124 0 0 0 0 0 # Wolf +6125 0 0 0 0 0 # Lucky_Box +6126 0 0 0 0 0 # Happy_Box +6127 0 0 0 0 0 # Purification_Stone +6129 0 0 0 0 0 # Ticket_Nightmare +6130 0 0 0 0 0 # Ticket_Loli_Ruri +6132 0 0 0 0 0 # Ticket_Incubus +6133 0 0 0 0 0 # Ticket_Miyabi_Ningyo +6134 0 0 0 0 0 # Ticket_Whisper +6135 0 0 0 0 0 # Ticket_Wicked_Nymph +6136 0 0 0 0 0 # Ticket_Medusa +6137 0 0 0 0 0 # Ticket_Stoneshooter +6138 0 0 0 0 0 # Ticket_Marionette +6139 0 0 0 0 0 # Ticket_Leafcat +6140 0 0 0 0 0 # Ticket_Dullahan +6141 0 0 0 0 0 # Ticket_Shinobi +6142 0 0 0 0 0 # Ticket_Golem +6143 0 0 0 0 0 # Ticket_Civil_Servant +6148 0 0 0 0 0 # Chocolate_Of_Eternity +6149 0 0 0 0 0 # Plain_Chocolate +6150 0 0 0 0 0 # Key_Of_The_Mansion +6151 0 0 0 0 0 # Peice_Of_Great_Bradium +6152 0 0 0 0 0 # Glittering_Crystal +6153 0 0 0 0 0 # Special_Exchange_Coupon +6154 0 0 0 0 0 # Broken_Horn_Pipe +6156 0 0 0 0 0 # Approval_Report +6206 0 0 0 0 0 # I_Love_You +6207 0 0 0 0 0 # Thank_You +6208 0 0 0 0 0 # I_Respect_You +6218 0 0 0 0 0 # Disin_Delivery_Box +6219 0 0 0 0 0 # Para_Team_Mark +6236 0 0 0 0 0 # Blue_Card_7 +6237 0 0 0 0 0 # Guarana_Fruit +6266 0 0 0 0 0 # Cheat_Key +6267 0 0 0 0 0 # Virtual_Key +6268 0 0 0 0 0 # Mirth_Key +6269 0 0 0 0 0 # Master_Brush +6270 0 0 0 0 0 # Mins_Picture +6271 0 0 0 0 0 # Mins_Receipt +6272 0 0 0 0 0 # Experiment_Seed +6273 0 0 0 0 0 # Altered_Seed +6274 0 0 0 0 0 # Saint_Cloth_Piece +6275 0 0 0 0 0 # King_Shield +6276 0 0 0 0 0 # Clear_Reagent +6277 0 0 0 0 0 # Red_Reagent +6278 0 0 0 0 0 # Black_Reagent +6296 0 0 0 0 0 # RF_Taining_Notice +6298 0 0 0 0 0 # Pumpkin_Head_Crushed +6299 0 0 0 0 0 # Worn_Cloth_Piece +6305 0 0 0 0 0 # Frozen_Skin_Piece +6306 0 0 0 0 0 # Solid_Bloodstain +6307 0 0 0 0 0 # Suspicious_Magic_Stone +6345 0 0 0 0 0 # Love_Ball +6347 0 0 0 0 0 # Bless_Word_Paper1 +6348 0 0 0 0 0 # Bless_Word_Paper2 +6349 0 0 0 0 0 # Bless_Word_Paper3 +6350 0 0 0 0 0 # Bless_Word_Paper4 +6351 0 0 0 0 0 # Bless_Word_Paper5 +6352 0 0 0 0 0 # Bless_Word_Paper6 +6353 0 0 0 0 0 # Bless_Word_Paper7 +6354 0 0 0 0 0 # Bless_Word_Paper8 +6355 0 0 0 0 0 # Bless_Word_Paper9 +6356 0 0 0 0 0 # Bless_Word_Paper10 +6357 0 0 0 0 0 # Fortune_Cookie_Fail +6358 0 0 0 0 0 # Free_Cash_Coupon +6359 0 0 0 0 0 # Guidebook_Exchange +6378 0 0 0 0 0 # Winning_Mark +6383 0 0 0 0 0 # Clue_Of_Lope +6384 0 0 0 0 0 # Ring_Of_Lope +6385 0 0 0 0 0 # Research_Tool_Bag +6386 0 0 0 0 0 # Bathtub_R_Sample +6387 0 0 0 0 0 # Teeth_Sample +6388 0 0 0 0 0 # Scale_Sample +6389 0 0 0 0 0 # Puddle_R_Sample +6390 0 0 0 0 0 # Small_Pocket +6391 0 0 0 0 0 # Splendid_Supply_Kit +6392 0 0 0 0 0 # Bradium_Box +6397 0 0 0 0 0 # PR_Team_Ticket +6398 0 0 0 0 0 # Develop_Team_Ticket +6399 0 0 0 0 0 # Marketing_Team_Ticket +6400 0 0 0 0 0 # Operating_Team_Ticket +6401 0 0 0 0 0 # Palm_O +6402 0 0 0 0 0 # Oil_Palm_F +6411 0 0 0 0 0 # Ripe_Watermelon +6412 0 0 0 0 0 # Special_Medal +6415 0 0 0 0 0 # Strange_Embryo +6416 0 0 0 0 0 # Pet_Exchange +6418 0 0 0 0 0 # Agrade_Coin +6419 0 0 0 0 0 # Bgrade_Coin +6420 0 0 0 0 0 # Cgrade_Coin +6421 0 0 0 0 0 # Dgrade_Coin +6422 0 0 0 0 0 # Egrade_Coin +6424 0 0 0 0 0 # Halloween_Fragment +6425 0 0 0 0 0 # Halloween_Certificate +6426 0 0 0 0 0 # Bad_Can +6427 0 0 0 0 0 # Bad_Can_Sack +6428 0 0 0 0 0 # Bravery_Card_A +6429 0 0 0 0 0 # Bravery_Card_B +6430 0 0 0 0 0 # Picture_Piece +6431 0 0 0 0 0 # Bucket +6432 0 0 0 0 0 # Full_Bucket +6433 0 0 0 0 0 # Clean_Brush +6434 0 0 0 0 0 # Fix_Kit +6435 0 0 0 0 0 # Fresh_Fruit +6436 0 0 0 0 0 # Ptotection_Seagod +6442 0 0 0 0 0 # Octopus_Hunt_Stick +6446 0 0 0 0 0 # Green_Paper +6447 0 0 0 0 0 # Red_Paper +6448 0 0 0 0 0 # White_Paper +6449 0 0 0 0 0 # Casual_Diary +6450 0 0 0 0 0 # Honest_Diary +6451 0 0 0 0 0 # Unknown_Fish +6452 0 0 0 0 0 # Etoile_Ring +6464 0 0 0 0 0 # Hate_Bundle +6480 0 0 0 0 0 # Event_coin +6500 0 0 0 0 0 # Sharp_Bamboo +6501 0 0 0 0 0 # Salt_Bag +6502 0 0 0 0 0 # Silver_Cross +6504 0 0 0 0 0 # Cast_Iron_Caldron +6505 0 0 0 0 0 # Purified_Bone +6506 0 0 0 0 0 # Memorial_Bouquet +6507 0 0 0 0 0 # Evil_Bone +6539 0 0 0 0 0 # Old_Left_Lapine +6540 0 0 0 0 0 # Golden_Leaf +6541 0 0 0 0 0 # Avant_Research_Data +6543 0 0 0 0 0 # Lv110_Achieved_Coin +6544 0 0 0 0 0 # Lv120_Achieved_Coin +6545 0 0 0 0 0 # Firm_Hair +6546 0 0 0 0 0 # Younger_Bro_Letter +6547 0 0 0 0 0 # Stained_Research_Book +6548 0 0 0 0 0 # Piece_Of_Lapine_Wing +6549 0 0 0 0 0 # Courtesy_Ticket +6552 0 0 0 0 0 # Mail_Package +6553 0 0 0 0 0 # Leaf_Made_Wood +6554 0 0 0 0 0 # Seed_Box +6555 0 0 0 0 0 # Birthday_Candle +6556 0 0 0 0 0 # Nespresso_Ticket +6562 0 0 0 0 0 # Tiny_Mouse_Tail +6563 0 0 0 0 0 # Weeds +6566 0 0 0 0 0 # Cacao99_Recipe +6567 0 0 0 0 0 # Choco_Drink_Recipe +6593 0 0 0 0 0 # Cryptura_Hair_Coupon +6654 0 0 0 0 0 # Needle_And_Thread +6655 0 0 0 0 0 # Firm_Pumpkin +6656 0 0 0 0 0 # Goast_Free_Charm +6657 0 0 0 0 0 # Memory_Of_Jack +6673 0 0 0 0 0 # Bossnia_Tickets +6674 0 0 0 0 0 # GM_Coin +6682 0 0 0 0 0 # Bag_Of_Selling_Goods +6685 0 0 0 0 0 # Morocc_Certification +6686 0 0 0 0 0 # Brick +6687 0 0 0 0 0 # Rope +6688 0 0 0 0 0 # Wood +6698 0 0 0 0 0 # Wooden_Axe +6699 0 0 0 0 0 # Faith_Silence +6700 0 0 0 0 0 # White_Snake_Scale +6701 0 0 0 0 0 # Treasure_Dwarf +6702 0 0 0 0 0 # Sweat_Dwarf +6703 0 0 0 0 0 # Warrior_Tears +6704 0 0 0 0 0 # Warrior_Anger +6705 0 0 0 0 0 # Warrior_Certificate +6706 0 0 0 0 0 # Guardian_Flowers +6708 0 0 0 0 0 # Magic_Crystal +6709 0 0 0 0 0 # Crisp_Silk +6710 0 0 0 0 0 # Tied_Snake +6713 0 0 0 0 0 # Heart_of_Soul +6714 0 0 0 0 0 # Sheenas_Soul +6720 0 0 0 0 0 # Status_Reset_Coupon +6721 0 0 0 0 0 # Status_Reset_Coupon_ +6722 0 0 0 0 0 # Delicious_Clam_Flesh +6723 0 0 0 0 0 # Delicious_Canned_Food +6724 0 0 0 0 0 # Savage_Box +6725 0 0 0 0 0 # Grand_Peco_Box +6726 0 0 0 0 0 # Desert_Wolf_Box +6727 0 0 0 0 0 # Arranged_Photo_Album +6733 0 0 0 0 0 # Tingly_Feather +6746 0 0 0 0 0 # Iron_Artifacts +6747 0 0 0 0 0 # Steel_Workpiece +6748 0 0 0 0 0 # Daily_Report_He_And_His_Team +6749 0 0 0 0 0 # Operation_Control_Device +6753 0 0 0 0 0 # Token_Of_Destruction +6754 0 0 0 0 0 # Collected_Medicinal_Herbs +6766 0 0 0 0 0 # Ice_World_Ticket +6767 0 0 0 0 0 # Summer_Fes_Coin +6768 0 0 0 0 0 # Red_Beans_Of_Ice +6769 0 0 0 0 0 # Sweet_Rice_Cake +6784 0 0 0 0 0 # Squid_Skewer +6785 0 0 0 0 0 # Fantastic_Sauce +6786 0 0 0 0 0 # Squid_Of_Bbq +6787 0 0 0 0 0 # Good_Firewood +6788 0 0 0 0 0 # Rose_Knife +6789 0 0 0 0 0 # Custom_Pan +6824 0 0 0 0 0 # Experimental_Dong_Memory_Record +6825 0 0 0 0 0 # Air_Cleaner_Box +6826 0 0 0 0 0 # Fresh_Grapes +6827 0 0 0 0 0 # Complete_Machine_Parts +6836 0 0 0 0 0 # Talisman_Of_Soul +6837 0 0 0 0 0 # Piece_Of_Soul_Mouse +6838 0 0 0 0 0 # Secret_Orders_Of_Prophet_K +6842 0 0 0 0 0 # Huge_Jewery +6843 0 0 0 0 0 # Thin_Ring +6844 0 0 0 0 0 # Mild_Stone +6845 0 0 0 0 0 # That_Thing +6847 0 0 0 0 0 # Very_Small_Box +6848 0 0 0 0 0 # Light_Box +6849 0 0 0 0 0 # Request_Complete_Certificate +6850 0 0 0 0 0 # Questionable_Box +6851 0 0 0 0 0 # Questionable_Document +6852 0 0 0 0 0 # Super_Corrector +6853 0 0 0 0 0 # Thanks_Bouquet +6854 0 0 0 0 0 # Novus_Captured +6855 0 0 0 0 0 # Tatacho_Captured +6856 0 0 0 0 0 # Manuscript_Written_By_Pigoreum +6857 0 0 0 0 0 # Very_Hard_Stone +6858 0 0 0 0 0 # Lude_Captured +6859 0 0 0 0 0 # Wanderer_Captured +6860 0 0 0 0 0 # Piece_Of_Soul_Cow +6862 0 0 0 0 0 # Piece_Of_Soul_Tiger +6863 0 0 0 0 0 # Strong_Piece_Of_Soul_Tiger +6866 0 0 0 0 0 # Piece_Of_Soul_Rabbit +6867 0 0 0 0 0 # Large_Insect_Barrel +6868 0 0 0 0 0 # Medium_Insect_Barrel +6869 0 0 0 0 0 # Dust +6885 0 0 0 0 0 # Piece_Of_Soul_Dragon +6886 0 0 0 0 0 # Strong_Piece_Of_Soul_Dragon +6904 0 0 0 0 0 # Piece_Of_Soul_Snake +6907 0 0 0 0 0 # Piece_Of_Soul_Horse +6912 0 0 0 0 0 # Piece_Of_Soul_Sheep +6913 0 0 0 0 0 # Holy_Rosary +6914 0 0 0 0 0 # Black_Horn +6915 0 0 0 0 0 # Captured_Soul +6916 0 0 0 0 0 # Piece_Of_Soul_Monkey +6917 0 0 0 0 0 # Piece_Of_Soul_Chicken +6919 0 0 0 0 0 # Honor_Proof +6923 0 0 0 0 0 # Bright_Fire_Lights +6925 0 0 0 0 0 # Letter_Of_Prisoner +6926 0 0 0 0 0 # Rune +6927 0 0 0 0 0 # Stone_That_Contained_Sea +6928 0 0 0 0 0 # Poring_Scourer +6929 0 0 0 0 0 # Letter_With_Stamped_Seal +6930 0 0 0 0 0 # Samples_New_Business_Items +6931 0 0 0 0 0 # Secret_Documents +6932 0 0 0 0 0 # Rare_Book +6933 0 0 0 0 0 # Banquet_Invitation_Letter +6934 0 0 0 0 0 # Magnificent_Dish +6935 0 0 0 0 0 # Advanced_Dish +6954 0 0 0 0 0 # Piece_Of_Soul_Dog +6955 0 0 0 0 0 # Piece_Of_Soul_Pig +6956 0 0 0 0 0 # Captured_Sheep +6957 0 0 0 0 0 # Lamb_Fleece +7059 0 0 0 0 0 # Cargo_Free_Ticket +7060 0 0 0 0 0 # Warp_Free_Ticket +7061 0 0 0 0 0 # Cart_Free_Ticket +7177 0 0 0 0 0 # Part_Of_Star +7178 0 0 0 0 0 # Star +7275 0 0 0 0 0 # Ancient_Document +7276 0 0 0 0 0 # Picture_Letter +7304 0 0 0 0 0 # Witch +7305 0 0 0 0 0 # Authority_Of_Nine_World +7306 0 0 0 0 0 # Fragment_Of_Soul +7307 0 0 0 0 0 # Whisper_Of_Soul +7308 0 0 0 0 0 # Witch +7309 0 0 0 0 0 # Wing_Of_Crow +7314 0 0 0 0 0 # The_Sign +7332 0 0 0 0 0 # Slate +7333 0 0 0 0 0 # Piece_Of_Slate_1 +7334 0 0 0 0 0 # Piece_Of_Slate_2 +7335 0 0 0 0 0 # Piece_Of_Slate_3 +7336 0 0 0 0 0 # Piece_Of_Slate_4 +7339 0 0 0 0 0 # RO_Transportation_Card_ +7341 0 0 0 0 0 # Worn_Out_Pendant +7342 0 0 0 0 0 # File01 +7343 0 0 0 0 0 # File02 +7344 0 0 0 0 0 # File03 +7346 0 0 0 0 0 # Pile_Of_Ymir_Heart +7349 0 0 0 0 0 # Indication_Of_Member02 +7351 0 0 0 0 0 # Friend +7368 0 0 0 0 0 # Lotto08 +7420 0 0 0 0 0 # Skull_ +7421 0 0 0 0 0 # Key_Red +7422 0 0 0 0 0 # Key_Yellow +7423 0 0 0 0 0 # Key_Blue +7424 0 0 0 0 0 # Key_Green +7425 0 0 0 0 0 # Key_Black +7426 0 0 0 0 0 # Magic_Gem_Red +7427 0 0 0 0 0 # Magic_Gem_Yellow +7428 0 0 0 0 0 # Magic_Gem_Blue +7429 0 0 0 0 0 # Magic_Gem_Green +7430 0 0 0 0 0 # Magic_Gem_Black +7431 0 0 0 0 0 # Several_Books +7432 0 0 0 0 0 # Leather_Pouch +7484 0 0 0 0 0 # Warrior_Symbol +7485 0 0 0 0 0 # 2nd_Floor_Pass +7486 0 0 0 0 0 # 3rd_Floor_Pass +7487 0 0 0 0 0 # Tavern_Wine +7488 0 0 0 0 0 # Delivery_Box +7489 0 0 0 0 0 # Villa_Spare_Key +7490 0 0 0 0 0 # Kyll_Hire_Letter +7491 0 0 0 0 0 # Iron_Box +7492 0 0 0 0 0 # Yellow_Key_Card +7493 0 0 0 0 0 # Golden_Key +7494 0 0 0 0 0 # Kiel_Button +7495 0 0 0 0 0 # Blue_Key_Card +7496 0 0 0 0 0 # Red_Key_Card +7497 0 0 0 0 0 # Steel_Piece +7498 0 0 0 0 0 # Rosimier_Key +7499 0 0 0 0 0 # Family_Portrait +7500 0 0 0 0 0 # Elysia_Portrait +7501 0 0 0 0 0 # Kyll_Hire_Letter2 +7502 0 0 0 0 0 # Piece_Memo_Of_James +7503 0 0 0 0 0 # Man_Portrait +7504 0 0 0 0 0 # Toy_Motor +7505 0 0 0 0 0 # Toy_Key +7506 0 0 0 0 0 # Black_Key_Card +7508 0 0 0 0 0 # Elysia_Ring +7515 0 0 0 0 0 # Marvelous_Medal +7516 0 0 0 0 0 # Green_Key_Card +7518 0 0 0 0 0 # Women +7527 0 0 0 0 0 # Life_Book +7530 0 0 0 0 0 # Travel_Brochure_01 +7531 0 0 0 0 0 # Travel_Brochure_02 +7532 0 0 0 0 0 # Travel_Brochure_03 +7533 0 0 0 0 0 # Travel_Brochure_04 +7534 0 0 0 0 0 # Photo_Album_01 +7535 0 0 0 0 0 # Photo_Album_02 +7536 0 0 0 0 0 # Photo_Album_03 +7537 0 0 0 0 0 # Photo_Album_04 +7538 0 0 0 0 0 # Sifted_Sand +7539 0 0 0 0 0 # Poring_Coin +7547 0 0 0 0 0 # Soccer_Ball +7548 0 0 0 0 0 # Soccer_Shoes +7549 0 0 0 0 0 # Brazilian_Flag +7550 0 0 0 0 0 # Ticket01 +7551 0 0 0 0 0 # Ticket02 +7552 0 0 0 0 0 # Ticket03 +7553 0 0 0 0 0 # Lotus_Flower +7554 0 0 0 0 0 # Striped_Candle +7555 0 0 0 0 0 # Green_Incense +7575 0 0 0 0 0 # Red_Jewel_ +7575 0 0 0 0 0 # Red_Jewel_ +7576 0 0 0 0 0 # Blue_Jewel_ +7576 0 0 0 0 0 # Blue_Jewel_ +7577 0 0 0 0 0 # Golden_Jewel_ +7579 0 0 0 0 0 # Silk_Handkerchief +7580 0 0 0 0 0 # Black_Bead +7583 0 0 0 0 0 # Evil_Mind +7584 0 0 0 0 0 # Proof_Of_Guard1 +7585 0 0 0 0 0 # Proof_Of_Guard2 +7586 0 0 0 0 0 # Proof_Of_Guard3 +7587 0 0 0 0 0 # Proof_Of_Guard4 +7588 0 0 0 0 0 # IPOD_Ticker +7603 0 0 0 0 0 # RO_Party_Ticket +7614 0 0 0 0 0 # Special_Packing_Paper +7618 0 0 0 0 0 # Monster_Crystal +7623 0 0 0 0 0 # Name_Change_Coupon +7625 0 0 0 0 0 # Registration_Ticket +7630 0 0 0 0 0 # Clean_Beach_Brush +7631 0 0 0 0 0 # Trash_Debris +7641 0 0 0 0 0 # Medical_Cure_Box +7701 0 0 0 0 0 # Dragon_Spirit +7704 0 0 0 0 0 # Broken_Thermometer +7705 0 0 0 0 0 # Note_Of_Geologist +7715 0 0 0 0 0 # Handmade_Choco_Recipe +7716 0 0 0 0 0 # Strawberry_Choco_Recipe +7717 0 0 0 0 0 # Choco_Tart_Recipe +7722 0 0 0 0 0 # Debt_Note +7723 0 0 0 0 0 # Diamond_Of_Ruin +7724 0 0 0 0 0 # Forbidden_Secret_Art +7725 0 0 0 0 0 # Unlucky_Emerald +7726 0 0 0 0 0 # Token_Of_King +7727 0 0 0 0 0 # HP_Doctor_Ticket +7728 0 0 0 0 0 # SP_Doctor_Ticket +7729 0 0 0 0 0 # Rok_Star_Badge +7755 0 0 0 0 0 # Research_Note +7759 0 0 0 0 0 # Geology_Report +7763 0 0 0 0 0 # Sticky_Herb +7764 0 0 0 0 0 # High_Strength_Adhesive +7765 0 0 0 0 0 # Yaga_Secret_Medicine +7773 0 0 0 0 0 # War_Badge +7801 0 0 0 0 0 # Girl_Fan_Letter +7802 0 0 0 0 0 # Autograph_Book +7820 0 0 0 0 0 # Morroc_Skin +7826 0 0 0 0 0 # Continental_Guard_Paper +7827 0 0 0 0 0 # Mineral_Report +7828 0 0 0 0 0 # BF_Badge1 +7829 0 0 0 0 0 # BF_Badge2 +7839 0 0 0 0 0 # Crystal_Key +7840 0 0 0 0 0 # Valkyrie_Gift +7841 0 0 0 0 0 # Spotted_Paper +7842 0 0 0 0 0 # Torn_Paper +7843 0 0 0 0 0 # Old_Paper +7844 0 0 0 0 0 # Burnt_Paper +7865 0 0 0 0 0 # Gold_Pouch +7866 0 0 0 0 0 # Certificate +7876 0 0 0 0 0 # Gold_Key +7877 0 0 0 0 0 # Red_Ring +7878 0 0 0 0 0 # Lusalka_Hair +7879 0 0 0 0 0 # Golden_Thread +7880 0 0 0 0 0 # Babayaga_Silver_Spoon +7881 0 0 0 0 0 # Book_Of_Magic +7882 0 0 0 0 0 # Pointed_Branch +7883 0 0 0 0 0 # Pointed_Wooden_Flute +7895 0 0 0 0 0 # TRO_Memory_Book01 +7896 0 0 0 0 0 # TRO_Memory_Book02 +7897 0 0 0 0 0 # TRO_Memory_Book03 +7898 0 0 0 0 0 # VVS_Balmung +7899 0 0 0 0 0 # Spiritualist_Dagger +7900 0 0 0 0 0 # Jenoss_Ring1 +7901 0 0 0 0 0 # Jenoss_Ring2 +7902 0 0 0 0 0 # Jenoss_Ring3 +7903 0 0 0 0 0 # Jenoss_Ring4 +7904 0 0 0 0 0 # Piano_Key +7906 0 0 0 0 0 # Poppy_Wreath +7907 0 0 0 0 0 # Bobbin_Of_Goddess +7909 0 0 0 0 0 # Stolen_Cookie +7910 0 0 0 0 0 # Stolen_Candy +7912 0 0 0 0 0 # Portable_Snowman +7913 0 0 0 0 0 # Test_Certificate +7914 0 0 0 0 0 # Ancient_Document_TW +7917 0 0 0 0 0 # Magic_Potion +7919 0 0 0 0 0 # Festival_Ticket +7920 0 0 0 0 0 # Hero +7923 0 0 0 0 0 # KRATHONG_ +7928 0 0 0 0 0 # Brazilian_Flag_ +7929 0 0 0 0 0 # Golden_Coin_ +7930 0 0 0 0 0 # Cowking +7949 0 0 0 0 0 # Woven_Wool +7950 0 0 0 0 0 # Ayothaya_Ticket +7951 0 0 0 0 0 # Gold_Tulip +7952 0 0 0 0 0 # Gift_From_Romiros +7953 0 0 0 0 0 # Gift_From_Juliedge +7954 0 0 0 0 0 # Festival_Ticket_ +7961 0 0 0 0 0 # Weapon_Exchange +7962 0 0 0 0 0 # Treasure_Map1 +7963 0 0 0 0 0 # Treasure_Map2 +7964 0 0 0 0 0 # Treasure_Map3 +7965 0 0 0 0 0 # Treasure_Map4 +7966 0 0 0 0 0 # Weird_Parchment1 +7967 0 0 0 0 0 # Weird_Parchment2 +7968 0 0 0 0 0 # Weird_Parchment3 +7969 0 0 0 0 0 # Weird_Parchment4 +11011 0 0 0 0 0 # Barmund_Note +11012 0 0 0 0 0 # Expedition_Report +11013 0 0 0 0 0 # Expedition_Report_Vol1 +11014 0 0 0 0 0 # Expedition_Report_Vol2 +11015 0 0 0 0 0 # Expedition_Report_Vol3 +11016 0 0 0 0 0 # Expedition_Report_Vol4 +11551 0 0 0 0 0 # Savory_Herb_Salad +11552 0 0 0 0 0 # Apple_Carrot_Salad +11553 0 0 0 0 0 # Casual_Stew +11554 0 0 0 0 0 # Golden_Roasted_Apple +12142 0 0 0 0 0 # Magic_Book +12153 0 0 0 0 0 # Bow_Mercenary_Scroll1 +12154 0 0 0 0 0 # Bow_Mercenary_Scroll2 +12155 0 0 0 0 0 # Bow_Mercenary_Scroll3 +12156 0 0 0 0 0 # Bow_Mercenary_Scroll4 +12157 0 0 0 0 0 # Bow_Mercenary_Scroll5 +12158 0 0 0 0 0 # Bow_Mercenary_Scroll6 +12159 0 0 0 0 0 # Bow_Mercenary_Scroll7 +12160 0 0 0 0 0 # Bow_Mercenary_Scroll8 +12161 0 0 0 0 0 # Bow_Mercenary_Scroll9 +12162 0 0 0 0 0 # Bow_Mercenary_Scroll10 +12163 0 0 0 0 0 # SwordMercenary_Scroll1 +12164 0 0 0 0 0 # SwordMercenary_Scroll2 +12165 0 0 0 0 0 # SwordMercenary_Scroll3 +12166 0 0 0 0 0 # SwordMercenary_Scroll4 +12167 0 0 0 0 0 # SwordMercenary_Scroll5 +12168 0 0 0 0 0 # SwordMercenary_Scroll6 +12169 0 0 0 0 0 # SwordMercenary_Scroll7 +12170 0 0 0 0 0 # SwordMercenary_Scroll8 +12171 0 0 0 0 0 # SwordMercenary_Scroll9 +12172 0 0 0 0 0 # SwordMercenary_Scroll10 +12173 0 0 0 0 0 # SpearMercenary_Scroll1 +12174 0 0 0 0 0 # SpearMercenary_Scroll2 +12175 0 0 0 0 0 # SpearMercenary_Scroll3 +12176 0 0 0 0 0 # SpearMercenary_Scroll4 +12177 0 0 0 0 0 # SpearMercenary_Scroll5 +12178 0 0 0 0 0 # SpearMercenary_Scroll6 +12179 0 0 0 0 0 # SpearMercenary_Scroll7 +12180 0 0 0 0 0 # SpearMercenary_Scroll8 +12181 0 0 0 0 0 # SpearMercenary_Scroll9 +12182 0 0 0 0 0 # SpearMercenary_Scroll10 +12199 0 0 0 0 0 # Rice_Scroll +12258 0 0 0 0 0 # Bombring_Box +12259 0 0 0 0 0 # Miracle_Medicine +12261 0 0 0 0 0 # Secret_Medicine +12262 0 0 0 0 0 # Inspector_Certificate_ +12263 0 0 0 0 0 # Comp_Battle_Manual +12266 0 0 0 0 0 # Sesame_Pastry_ +12267 0 0 0 0 0 # Honey_Pastry_ +12268 0 0 0 0 0 # Rainbow_Cake_ +12274 0 0 0 0 0 # Gold_Pill_1 +12275 0 0 0 0 0 # Gold_Pill_2 +12287 0 0 0 0 0 # Love_Angel +12288 0 0 0 0 0 # Squirrel +12289 0 0 0 0 0 # Gogo +12294 0 0 0 0 0 # PC_Bang_Coin_Box1 +12295 0 0 0 0 0 # PC_Bang_Coin_Box2 +12296 0 0 0 0 0 # PC_Bang_Coin_Box3 +12297 0 0 0 0 0 # PC_Bang_Coin_Box4 +12298 0 0 0 0 0 # SP_Potion +12299 0 0 0 0 0 # Mega_Resist_Potion +12300 0 0 0 0 0 # Wild_Rose_Scroll +12301 0 0 0 0 0 # Doppelganger_Scroll +12302 0 0 0 0 0 # Ygnizem_Scroll +12304 0 0 0 0 0 # Picture_Diary +12305 0 0 0 0 0 # Mini_Heart +12306 0 0 0 0 0 # Newcomer +12307 0 0 0 0 0 # Kid +12308 0 0 0 0 0 # Magic_Castle +12309 0 0 0 0 0 # Bulging_Head +12313 0 0 0 0 0 # Protection_Of_Angel +12314 0 0 0 0 0 # Noive_Box +12322 0 0 0 0 0 # Chocolate_Pie +12323 0 0 0 0 0 # N_Fly_Wing +12324 0 0 0 0 0 # N_Butterfly_Wing +12325 0 0 0 0 0 # N_Magnifier +12331 0 0 0 0 0 # Ginseng +12333 0 0 0 0 0 # Ansila +12333 0 0 0 0 0 # Ansila +12340 0 0 0 0 0 # Mysterious_Rice_Powder +12384 0 0 0 0 0 # Rainbow_Ruby_Water +12385 0 0 0 0 0 # Rainbow_Ruby_Fire +12386 0 0 0 0 0 # Rainbow_Ruby_Wind +12387 0 0 0 0 0 # Rainbow_Ruby_Earth +12388 0 0 0 0 0 # Runstone_Crush +12389 0 0 0 0 0 # Runstone_Storm +12390 0 0 0 0 0 # Runstone_Millennium +12398 0 0 0 0 0 # PCBang_Gift_Box +12478 0 0 0 0 0 # Chance_Box +12479 0 0 0 0 0 # Caracas_Ring_Box +12540 0 0 0 0 0 # GM_Warp_Box +12541 0 0 0 0 0 # Fortune_Cookie1 +12542 0 0 0 0 0 # Fortune_Cookie2 +12543 0 0 0 0 0 # Fortune_Cookie3 +12565 0 0 0 0 0 # Cheer_Scarf_Box +12566 0 0 0 0 0 # Cheer_Scarf2_Box +12567 0 0 0 0 0 # Cheer_Scarf3_Box +12568 0 0 0 0 0 # Cheer_Scarf4_Box +12569 0 0 0 0 0 # Cheer_Scarf6_Box +12570 0 0 0 0 0 # Cheer_Scarf8_Box +12571 0 0 0 0 0 # Cheer_Scarf10_Box +12572 0 0 0 0 0 # Cheer_Scarf10_Box2 +12579 0 0 0 0 0 # Ring_Of_Valkyrie_Box +12583 0 0 0 0 0 # PR_Team_Box +12584 0 0 0 0 0 # Develop_Team_box +12585 0 0 0 0 0 # Marketing_Team_Box +12586 0 0 0 0 0 # Operating_Team_Box +12587 0 0 0 0 0 # Summer_Night_box +12588 0 0 0 0 0 # Summer_Night_box2 +12589 0 0 0 0 0 # Summer_Night_box3 +12600 0 0 0 0 0 # Treasure_Box_Scroll +12607 0 0 0 0 0 # Lolli_Pop_Box +12622 0 0 0 0 0 # Boarding_Halter +12649 0 0 0 0 0 # Lv70_Imperial_Gift +12650 0 0 0 0 0 # Lv90_Imperial_Gift +12651 0 0 0 0 0 # Lv110_Imperial_Gift +12652 0 0 0 0 0 # Lv130_Imperial_Gift +12653 0 0 0 0 0 # Lv150_Imperial_Gift +12672 0 0 0 0 0 # Start_New_Box +12715 0 0 0 0 0 # Black_Treasure_Box +12740 0 0 0 0 0 # Inc_Str_Scroll +12741 0 0 0 0 0 # Inc_Int_Scroll +12745 0 0 0 0 0 # Skull_Scroll +12766 0 0 0 0 0 # Reward_Job_BM25 +12802 0 0 0 0 0 # Time_Guardian_Box +12803 0 0 0 0 0 # Beginner_Kit_Box +12807 0 0 0 0 0 # Mercenary_Casting_ +12808 0 0 0 0 0 # Mother_Love_Box +12809 0 0 0 0 0 # Level_Up_Box +12810 0 0 0 0 0 # Event_Gift_Box +12816 0 0 0 0 0 # Old_Ore_Box_ +12817 0 0 0 0 0 # Old_Card_Album_ +12818 0 0 0 0 0 # High_Weapon_Box_ +12819 0 0 0 0 0 # Zherlthsh_Tck_Box_ +12845 0 0 0 0 0 # WOB_Amatsu +12847 0 0 0 0 0 # Old_Equipment_Box +12884 0 0 0 0 0 # Infinite_Concentration_Potion +12885 0 0 0 0 0 # Infinite_Awakening_Potion +12886 0 0 0 0 0 # Infinite_Berserk_Potion +12887 0 0 0 0 0 # C_Wing_Of_Fly +12888 0 0 0 0 0 # Siege_Kit_Box +13020 0 0 0 0 0 # Warrior_Balmung_ +13021 0 0 0 0 0 # Combat_Knife_C +13022 0 0 0 0 0 # Counter_Dagger_C +13023 0 0 0 0 0 # Asura_C +13024 0 0 0 0 0 # Sword_Breaker_C +13025 0 0 0 0 0 # Mail_Breaker_C +13026 0 0 0 0 0 # Moonlight_Sword_C +13036 0 0 0 0 0 # BF_Dagger1 +13037 0 0 0 0 0 # BF_Dagger2 +13042 0 0 0 0 0 # Krieger_Dagger1 +13043 0 0 0 0 0 # Fortune_Sword_I +13044 0 0 0 0 0 # House_Auger_I +13045 0 0 0 0 0 # Kamaitachi_I +13048 0 0 0 0 0 # Damascus_C +13050 0 0 0 0 0 # P_Dagger1 +13051 0 0 0 0 0 # P_Dagger2 +13052 0 0 0 0 0 # Tourist_Dagger +13066 0 0 0 0 0 # P_Dagger3 +13068 0 0 0 0 0 # Saurel +13074 0 0 0 0 0 # Ninja_Cutter +13079 0 0 0 0 0 # Metal_Dagger +13083 0 0 0 0 0 # TE_Woe_Knife +13108 0 0 0 0 0 # BF_Pistol1 +13109 0 0 0 0 0 # Wasteland_Outlaw_C +13110 0 0 0 0 0 # Krieger_Pistol1 +13111 0 0 0 0 0 # Sharpshooter_Revolver +13112 0 0 0 0 0 # P_Revolver1 +13113 0 0 0 0 0 # P_Revolver2 +13114 0 0 0 0 0 # P_Revolver3 +13117 0 0 0 0 0 # TE_Woe_Pistol +13118 0 0 0 0 0 # Spark_Light +13125 0 0 0 0 0 # Metal_Revolver +13171 0 0 0 0 0 # BF_Rifle1 +13172 0 0 0 0 0 # BF_Gatling_Gun1 +13173 0 0 0 0 0 # BF_Shotgun1 +13174 0 0 0 0 0 # BF_Launcher1 +13175 0 0 0 0 0 # Lever_Action_Rifle_C +13176 0 0 0 0 0 # Krieger_Rifle1 +13177 0 0 0 0 0 # Krieger_Gatling1 +13178 0 0 0 0 0 # Krieger_Shotgun1 +13179 0 0 0 0 0 # Krieger_Launcher1 +13184 0 0 0 0 0 # TE_Woe_Rifle +13185 0 0 0 0 0 # TE_Woe_Gatling +13186 0 0 0 0 0 # TE_Woe_Shotgun +13187 0 0 0 0 0 # TE_Woe_Grenade +13295 0 0 0 0 0 # Light_Shuriken +13305 0 0 0 0 0 # BF_Huuma_Shuriken1 +13306 0 0 0 0 0 # BF_Huuma_Shuriken2 +13307 0 0 0 0 0 # Krieger_Huuma_Shuriken1 +13308 0 0 0 0 0 # Huuma_Blaze_I +13309 0 0 0 0 0 # Huuma_Giant_Wheel_C +13310 0 0 0 0 0 # P_Huuma_Suriken1 +13312 0 0 0 0 0 # Huuma_Job_Test +13317 0 0 0 0 0 # TE_Woe_Huuma +13322 0 0 0 0 0 # Huuma_Metal_Shuriken +13401 0 0 0 0 0 # Excalibur_C +13402 0 0 0 0 0 # Cutlas_C +13403 0 0 0 0 0 # Solar_Sword_C +13407 0 0 0 0 0 # Nagan_C +13408 0 0 0 0 0 # Fire_Brand_C +13409 0 0 0 0 0 # Immaterial_Sword_C +13410 0 0 0 0 0 # BF_Sword1 +13411 0 0 0 0 0 # BF_Sword2 +13416 0 0 0 0 0 # Krieger_Onehand_Sword1 +13417 0 0 0 0 0 # Krieger_Onehand_Sword2 +13418 0 0 0 0 0 # Krieger_Onehand_Sword3 +13419 0 0 0 0 0 # Holy_Saber +13422 0 0 0 0 0 # Flamberge_C +13423 0 0 0 0 0 # P_Sabre1 +13424 0 0 0 0 0 # P_Sabre2 +13425 0 0 0 0 0 # Tourist_Sword +13434 0 0 0 0 0 # P_Saber3 +13439 0 0 0 0 0 # TE_Woe_Sword +13763 0 0 0 0 0 # 5_Anniversary_Coin_Box +13765 0 0 0 0 0 # Certificate_TW_Box +13859 0 0 0 0 0 # Directive_A_Envelope +13860 0 0 0 0 0 # Directive_B_Envelope +14505 0 0 0 0 0 # Dungeon_1_hour_Ticket +14506 0 0 0 0 0 # Dungeon_Ticket +14529 0 0 0 0 0 # Greed_Scroll +14595 0 0 0 0 0 # Unsealed_Magic_Spell +15001 0 0 0 0 0 # Odin +15002 0 0 0 0 0 # Rune_Plate +15009 0 0 0 0 0 # Para_Team_Uniform1 +15010 0 0 0 0 0 # Para_Team_Uniform2 +15011 0 0 0 0 0 # Para_Team_Uniform3 +15023 0 0 0 0 0 # Half_Brynhild +15031 0 0 0 0 0 # Para_Team_Armor +15033 0 0 0 0 0 # Tutorial_Mattle +15034 0 0 0 0 0 # Tutorial_Mattle_ +15062 0 0 0 0 0 # TE_Woe_Coat +15063 0 0 0 0 0 # TE_Woe_Chain_Mail +15064 0 0 0 0 0 # TE_Woe_Mage_Coat +15067 0 0 0 0 0 # Rune_Suit +16002 0 0 0 0 0 # Stunner_C +16004 0 0 0 0 0 # P_Mace1 +16005 0 0 0 0 0 # P_Mace2 +16006 0 0 0 0 0 # Tourist_Mace +16014 0 0 0 0 0 # P_Mace3 +16016 0 0 0 0 0 # Tuna +16023 0 0 0 0 0 # Metal_Mace +16025 0 0 0 0 0 # TE_Woe_Mace +16405 0 0 0 0 0 # Midgard_Coin_Box +16406 0 0 0 0 0 # FMidgard_Coin_Box +18100 0 0 0 0 0 # Shooting_Star_C +18106 0 0 0 0 0 # P_Bow3 +18107 0 0 0 0 0 # Malang_Snow_Crab +18108 0 0 0 0 0 # Brindle_Eel +18116 0 0 0 0 0 # Metal_Bow +18500 0 0 0 0 0 # Cheer_Scarf6 +18501 0 0 0 0 0 # Cheer_Scarf8 +18502 0 0 0 0 0 # Cheer_Scarf10 +18505 0 0 0 0 0 # Umbala_Spirit +18514 0 0 0 0 0 # Para_Team_Hat2 +18526 0 0 0 0 0 # Yummy_Lollipop +18556 0 0 0 0 0 # Angel_Helmet +18557 0 0 0 0 0 # Devil_Helmet +18566 0 0 0 0 0 # Nut_Donut_In_Mouth +19052 0 0 0 0 0 # Sigruns_Wing +19853 0 0 0 0 0 # C_Filir +20135 0 0 0 0 0 # C_12_Anniversary_Crown_Of_Saint +20136 0 0 0 0 0 # C_12_Anniversary_Elf_Ears +21005 0 0 0 0 0 # Metal_Two_Hand_Sword +22508 0 0 0 0 0 # Para_Team_Mark_ +22513 0 0 0 0 0 # King_of_Gift_Box +22517 0 0 0 0 0 # Rocker_Summoning_Scroll +22521 0 0 0 0 0 # Level_Up_Box +22522 0 0 0 0 0 # Level_Up_Box100 +22523 0 0 0 0 0 # Level_Up_Box120 +22524 0 0 0 0 0 # Level_Up_Box130 +22525 0 0 0 0 0 # Level_Up_Box140 +22526 0 0 0 0 0 # Level_Up_Box150 +22527 0 0 0 0 0 # Level_Up_Box160 +22533 0 0 0 0 0 # New_Year_Gift_Box +22535 0 0 0 0 0 # Scroll_Summoning_Workers +22536 0 0 0 0 0 # Scroll_Summoning_Workers +22566 0 0 0 0 0 # Last_Years_Frost +22569 0 0 0 0 0 # Gift_New_start +22589 0 0 0 0 0 # Savage_Ora_Ora +22590 0 0 0 0 0 # Grand_Peco_Ora_Ora +22591 0 0 0 0 0 # Desert_Wolf_Ora_Ora +22592 0 0 0 0 0 # Happy_Call_Box +22610 0 0 0 0 0 # New_Beginnings_Box +22614 0 0 0 0 0 # Premium_Manual +22619 0 0 0 0 0 # Ghost_Summon_Scroll +22623 0 0 0 0 0 # New_Start_Box +22691 0 0 0 0 0 # Record_Fragment1 +22692 0 0 0 0 0 # Record_Fragment2 +22693 0 0 0 0 0 # Record_Fragment3 +22694 0 0 0 0 0 # Record_Fragment4 +22695 0 0 0 0 0 # Record_Fragment5 +22700 0 0 0 0 0 # Jumping_Support_Box +22708 0 0 0 0 0 # Thrilling_Box +22717 0 0 0 0 0 # Wanderer_Ball +22718 0 0 0 0 0 # Lude_Ball +22719 0 0 0 0 0 # Tatacho_Ball +22720 0 0 0 0 0 # Novus_Ball +22734 0 0 0 0 0 # Revolution_Quiz_Box +22760 0 0 0 0 0 # Argiope_Transportin +22761 0 0 0 0 0 # Luciola_Vespa_Transportin +22762 0 0 0 0 0 # Centipede_Transportin +22837 0 0 0 0 0 # Integer_Time +22869 0 0 0 0 0 # Lucky_Roulette_Tickets +22881 0 0 0 0 0 # Rope_Gallows +22887 0 0 0 0 0 # PC +22894 0 0 0 0 0 +22895 0 0 0 0 0 +22896 0 0 0 0 0 +22901 0 0 0 0 0 # Question_Old_Blue_Box +25043 0 0 0 0 0 # Thorny_Vine_Flute +25045 0 0 0 0 0 # Luxurious_Cloth +25046 0 0 0 0 0 # Boarding_Pass +25047 0 0 0 0 0 # Kahlunac +25048 0 0 0 0 0 # Hearty_Lunchbox +25049 0 0 0 0 0 # Basilac_Clam +25132 0 0 0 0 0 # Pumpkin_Deco +25133 0 0 0 0 0 # Dried_White_Stem +25143 0 0 0 0 0 # Gift_Stuffed_Doll +25144 0 0 0 0 0 # Bridge_Postured_Doll +25145 0 0 0 0 0 # Burnt_Spector_Doll +25146 0 0 0 0 0 # Cold_Blooded_Queen_Doll +25147 0 0 0 0 0 # Well_Eatenl_Rabbit_Doll +25148 0 0 0 0 0 # Cute_Starved_Demon_Doll +25149 0 0 0 0 0 # Doll_With_Warm_Scarf +25150 0 0 0 0 0 # Hugging_Alice_Pilow +26100 0 0 0 0 0 # Paradise_Foxtail_Staff_II +26101 0 0 0 0 0 # Paradise_Foxtail_Staff_III diff --git a/openkore_llm_knowledge/config/mon_control.txt b/openkore_llm_knowledge/config/mon_control.txt new file mode 100644 index 0000000000..e8662bdaff --- /dev/null +++ b/openkore_llm_knowledge/config/mon_control.txt @@ -0,0 +1,222 @@ +# This file allows specific AI control for certain monsters +# +# Syntax: +# <monster> <attack> <teleport> <search> <skillcancel> <lv> <joblv> <hp> <sp> <weight> +# +# <monster>: Name of monster as found in monsters.txt (not case sensitive) +# +# <attack>: +# -1 means to leave the monster alone, even if it attacks you. +# 0 means to leave the monster alone, unless it attacks you. +# 1 means to always auto-attack this monster. +# 2 means always aggressive, auto-attack this monster when it appears, even if sitting. +# 3 means to attack the monster once (provoke) then leave it, useful for mobbing. +# +# <teleport>: +# < 0 (-1, -2, etc.) to set exact critical distance for this monster. Teleport, if the monster reaches it. +# 1 to teleport if the monster is on the screen. +# 2 to teleport if the monster attacks you. +# -> This is only used in auto-attack mode! +# 3 to disconnect for 30 seconds if the monster is on your screen. +# >= 4 (4, 5, etc.) to set the time that will be disconnected (in seconds) if the monster is on your screen. +# +# <search>: Put a 1 to only attack the monster in the search mode. +# This is only used in auto-attack mode. +# +# <skillcancel>: Set to 1 if you want to interrupt spells casted by this +# monster. +# +# <lv>: Only auto-attack this monster if your level is higher than the +# specified level. +# +# <joblv>: Only auto-attack this monster if your job level is higher than +# the specified level. +# +# <hp>: Only auto-attack this monster if your HP is higher than the +# specified amount. The HP is not specified in percentage. +# +# <sp>: Only auto-attack this monster if your SP is higher than the +# specified amount. The SP is not specified in percentage. +# +# <weight>: Counts this monster as the specified amount aggressives. Supports floating point numbers (eg 1.8237402). +# Example: +# (config.txt) +# teleportAuto_minAggressives 6 +# teleportAuto_minAggressivesInLock 6 +# +# (mon_control.txt) +# Hydra 1 0 0 0 0 0 0 0 0.2 +# Merman 1 0 0 0 0 0 0 0 2 +# +# If there's five hydras and two sword fish attacks the bot, it won't +# teleport away since the aggressives are counted as 5*0.2 + 2*1 = 3 +# However, two marcs and two merman will make it tele away because +# it sees 2*1* + 2*2 = 6 aggressives. +# +# +# Monsters not found in this file, or flags not specified will default to: +# <attack> = 1 + + +##### Eggs ##### +Ant's Egg 0 0 0 +PecoPeco's Egg 0 0 0 +Pupa 0 0 0 +Thief Bug's Egg 0 0 0 + +##### Alchemist Summons #### +# Summoned Parasite +1555 0 0 0 + +# Summoned Flora +1575 0 0 0 + +# Summoned Hydra +1579 0 0 0 + +# Summoned Mandragora +1589 0 0 0 + +# Summoned Geographer +1590 0 0 0 + +##### Plants ##### +Black Mushroom 0 0 0 +Blue Plant 0 0 0 +Green Plant 0 0 0 +Red Mushroom 0 0 0 +Red Plant 0 0 0 +Shining Plant 0 0 0 +White Plant 0 0 0 +Yellow Plant 0 0 0 + + +##### Homunculus ##### +6001 -1 0 0 +6002 -1 0 0 +6003 -1 0 0 +6004 -1 0 0 +6005 -1 0 0 +6006 -1 0 0 +6007 -1 0 0 +6008 -1 0 0 +6009 -1 0 0 +6010 -1 0 0 +6011 -1 0 0 +6012 -1 0 0 +6013 -1 0 0 +6014 -1 0 0 +6015 -1 0 0 +6016 -1 0 0 + +##### Mercenary ##### +6017 -1 0 0 +6018 -1 0 0 +6019 -1 0 0 +6020 -1 0 0 +6021 -1 0 0 +6022 -1 0 0 +6023 -1 0 0 +6024 -1 0 0 +6025 -1 0 0 +6026 -1 0 0 +6027 -1 0 0 +6028 -1 0 0 +6029 -1 0 0 +6030 -1 0 0 +6031 -1 0 0 +6032 -1 0 0 +6033 -1 0 0 +6034 -1 0 0 +6035 -1 0 0 +6036 -1 0 0 +6037 -1 0 0 +6038 -1 0 0 +6039 -1 0 0 +6040 -1 0 0 +6041 -1 0 0 +6042 -1 0 0 +6043 -1 0 0 +6044 -1 0 0 +6045 -1 0 0 +6046 -1 0 0 + + +##### MVPs and Dangerous Monsters ##### +##MOB ID : MVP +1511 -1 1 0 #Amon Ra +2476 -1 1 0 #Amdarias +1388 -1 1 0 #Archangeling +1647 -1 1 0 #Assassin Cross Eremes +1785 -1 1 0 #Atroce +1039 -1 1 0 #Baphomet +1630 -1 1 0 #Bacsojin +1874 -1 1 0 #Beelzebub +2068 -1 1 0 #Boitata +2319 -1 1 0 #Buwaya +2238 -1 1 0 #Champion Chen +2240 -1 1 0 #Clown Alphoccio +2236 -1 1 0 #Creator Flamel +2253 -1 1 0 #Daehyon +1302 -1 1 0 #Dark Illusion +1272 -1 1 0 #Dark Lord +1719 -1 1 0 #Detale +1046 -1 1 0 #Doppelganger +1389 -1 1 0 #Dracula +1112 -1 1 0 #Drake +1115 -1 1 0 #Eddga +1658 -1 1 0 #Egnigem Cenia +1418 -1 1 0 #Evil Snake Lord +1871 -1 1 0 #Fallen Bishop +1252 -1 1 0 #Garm +2251 -1 1 0 #Gioia +1768 -1 1 0 #Gloom Under Night +2165 -1 1 0 #Gold Queen Scaraba +1086 -1 1 0 #Golden Thief Bug +1885 -1 1 0 #Gopinich +2241 -1 1 0 #Gypsy Trentini +1649 -1 1 0 #High Priest Magaleta +1651 -1 1 0 #High Wizard Katrinn +1832 -1 1 0 #Ifrit +1492 -1 1 0 #Incantation Samurai +2255 -1 1 0 #Kades +1734 -1 1 0 #Kiel D-01 +2202 -1 1 0 #Kraken +1779 -1 1 0 #Ktullanux +1980 -1 1 0 #Kubkin +1688 -1 1 0 #Lady Tany +2156 -1 1 0 #Leak +1646 -1 1 0 #Lord Knight Seyren +1373 -1 1 0 #Lord of Death +2131 -1 1 0 #Lost Dragon +1147 -1 1 0 #Maya +1289 -1 1 0 #Maya Purple +1059 -1 1 0 #Mistress +1150 -1 1 0 #Moonlight +2022 -1 1 0 #Nidhoggr's Shadow +2362 -1 1 0 #Nightmare Amon Ra +1262 -1 1 0 #Mutant Dragon +1087 -1 1 0 #Orc Hero +1190 -1 1 0 #Orc Lord +1038 -1 1 0 #Osiris +2235 -1 1 0 #Paladin Randel +1157 -1 1 0 #Pharaoh +1159 -1 1 0 #Phreeoni +1502 -1 1 0 #Pori Pori +2237 -1 1 0 #Professor Celia +2249 -1 1 0 #Pyuriel +2087 -1 1 0 #Queen Scaraba +2475 -1 1 0 #Root of Corruption +1623 -1 1 0 #RSX-0806 +2341 -1 1 0 #RWC Boss +1650 -1 1 0 #Sniper Shecil +2239 -1 1 0 #Stalker Gertie +1251 -1 1 0 #Stormy Knight +1583 -1 1 0 #Tao Gunka +1708 -1 1 0 #Thanatos +1312 -1 1 0 #Turtle General +1751 -1 1 0 #Valkyrie Randgris +1685 -1 1 0 #Vesper +1648 -1 1 0 #Whitesmith Harword +1917 -1 1 0 #Wounded Morroc +1658 -1 1 0 #Ygnizem diff --git a/openkore_llm_knowledge/config/overallAuth.txt b/openkore_llm_knowledge/config/overallAuth.txt new file mode 100644 index 0000000000..e69de29bb2 diff --git a/openkore_llm_knowledge/config/pickupitems.txt b/openkore_llm_knowledge/config/pickupitems.txt new file mode 100644 index 0000000000..6db0211d48 --- /dev/null +++ b/openkore_llm_knowledge/config/pickupitems.txt @@ -0,0 +1,13 @@ +# Tell Kore to pickup (or not to pickup) certain items. +# Format: <item name> <flag> +# <item name> - name of item (not case sensitive) +# <flag> - -1 to drop the item when it appears in inventory +# (useful for Rogues with auto-steal), +# 0 to not pick up the item, +# 1 to pickup the item, +# 2 to take the item as fast as it can +# +#Use the item name "all" to tell kore to pickup or not pickup all items by default +all 1 +# jellopy 0 +# fluff 0 \ No newline at end of file diff --git a/openkore_llm_knowledge/config/poseidon.txt b/openkore_llm_knowledge/config/poseidon.txt new file mode 100644 index 0000000000..be2cc5cda0 --- /dev/null +++ b/openkore_llm_knowledge/config/poseidon.txt @@ -0,0 +1,21 @@ +# Ragnarok Server +# Here you'll define the IP Address and the Port where Poseidon +# will keep waiting for your ragnarok online client to connect. +ragnarokserver_ip=127.0.0.1 +ragnarokserver_port=6900 +fake_ip= + +# Query Server +# Here you'll define the IP Address and the Port where Poseidon +# will keep waiting for open kore to connect and send the GG/HS queries. +queryserver_ip=127.0.0.1 +queryserver_port=24390 + +# Server Type +# Here you have to specify your current server type in order +# to the poseidon operate properly ! +# The available server types for now are : Default, bRO_.* (check servertypes.txt) +# You should modify this if you're having problems with char list. +server_type=Default + +debug=0 diff --git a/openkore_llm_knowledge/config/priority.txt b/openkore_llm_knowledge/config/priority.txt new file mode 100644 index 0000000000..c8f0b0043b --- /dev/null +++ b/openkore_llm_knowledge/config/priority.txt @@ -0,0 +1,13 @@ +# Put the names of the monsters that have a high priority in this file. +# (This list is case-insensitive.) +# Upper entries have more priority than lower entries. +# +# The following example gives Hydra the most priority. If you're attacked +# by both Obeaunes and Hydras, you'll attack Hydras first. Obeaunes have +# more priority than all monsters not listed in this file, so you'll +# attack Obeaunes first if you get mobbed by Obeaunes and something other +# than Hydras. +# +# Example (remove the comment character to activate them): +#Hydra +#Obeaune diff --git a/openkore_llm_knowledge/config/responses.txt b/openkore_llm_knowledge/config/responses.txt new file mode 100644 index 0000000000..59e877a5c8 --- /dev/null +++ b/openkore_llm_knowledge/config/responses.txt @@ -0,0 +1,109 @@ +#See the documentation for how to add responses + +authS You are now an admin %$cmd_user. Go nuts! ^^ +authF You're already an admin %$cmd_user! + +confS1 That variable %$key is...%$value +confS2 *beep* +confS2 *boop* +confS2 All done. +confF1 Huh? +confF2 I don't see that config variable +confF3 uhh, nice try %$cmd_user +confF3 I don't think so + +dateS Lay off the RO %$cmd_user. It's %$date +dateS What am I? A clock?? (BTW it's %$date) + +expS Botting time : %$time\nBaseExp : %$bExp %$percentBExp\nJobExp : %$jExp %$percentJExp\nBaseExp/Hour : %$bExpPerHour %$percentBExpPerHour\nJobExp/Hour : %$jExpPerHour %$percentJExpPerHour\nBase Levelup Time Estimation : %$bLvlUp\nJob Levelup Time Estimation : %$jLvlUp\nDied : %$numDeaths +expF Huh? +expMonsterS1 No Name Count +expMonsterS2 %$killedMonsters +expMonsterS3 Total number of killed monsters: %$numKilledMonsters +expItemS1 Name Count +expItemS2 %$gotItems + +followS Right away! +followS For freedom! +followS Yes sah! +followF I have no idea what you're saying %$user +followF That makes no sense >< + +followStopS I get the idea... +followStopS Am I that annoying? +followStopF You're so paranoid +followStopF Like I'd be stalking YOU + +moveS Right away! +moveS For freedom! +moveS Yes sah! +moveF I have no idea what you're saying %$user +moveF That makes no sense >< + +quitS Ciao +quitS I'm outta here, lamers +quitS Off I go! + +reloadS *beep* +reloadS *boop* +reloadS All done. + +relogS brb +relogS k, be back.. + +sitS *boof* +sitS *plop* + +skillgoodM Thank, you. +skillgoodM You are too kind. +skillgoodM Oh no. That's enough. =) +skillgoodM +skillgoodM +skillgoodM + +skillbadM What did you do that for? +skillbadM Please stop! +skillbadM I said stop it! +skillbadM ... +skillbadM ... +skillbadM +skillbadM + +standS Okie ^^ +standS Right +standS ok %$cmd_user, like this? + +statusS HP: %$char_hp / %$char_hp_max SP: %$char_sp / %$char_sp_max\nBase: %$char_lv | %$char_exp / %$char_exp_max\nJob: %$char_lv_job | %$char_exp_job / %$char_exp_job_max\nWeight: %$char_weight / %$char_weight_max Zeny: %$zeny + +tankS Leecher +tankF huh? + +tankStopS That was fun... +tankStopF Does it look like I'm tanking? + +thankS np ^^ +thankS No problem +thankS Always a pleasure, master %$cmd_user +thankS ^^ + +timeoutS1 I think the timeout %$key is %$value +timeoutS2 *beep* +timeoutS2 *boop* +timeoutS2 All done. +timeoutF1 Huh? +timeoutF2 I don't see that timeout + +verboseOnS Ah, finally. +verboseOnS *gasp* Oh thank you master...ass. +verboseOnF Does it look like I can't talk? +verboseOnF You twit, I can speak fine... + +verboseOffS You ass...fine. +verboseOffS OMG, whatever +verboseOffF Screw off, im already staying quiet +verboseOffF Ya ya..I get it already + +versionS It's %$ver + +whereS Lost me? I'm at %$map: %$x, %$y +whereS Here I am! %$map: %$x, %$y diff --git a/openkore_llm_knowledge/config/routeweights.txt b/openkore_llm_knowledge/config/routeweights.txt new file mode 100644 index 0000000000..f63b88889d --- /dev/null +++ b/openkore_llm_knowledge/config/routeweights.txt @@ -0,0 +1,51 @@ +# +# Map routing weights +# +# This control file allows you to modify Kore's map routing by specifying +# weights for maps that Kore is walking through to reach the destination. +# Kore generally tries to use the shortest path to get to the desired +# destination map. The path is counted in number of steps (tiles) the +# character would have to walk. +# Map routing weights can be seen as additional steps that Kore thinks +# it would have to do on a specific map. Positive weights makes Kore +# avoid a map, negative weights makes it prefer a map. +# +# Example: +# Specifying a weight of 500 for prt_fild08 makes Kore avoid walking +# through prt_fild08, as long as there is an alternative path that has +# not more than 500 additional steps. +# Therefore you could say Kore prefers to walk up to 500 additional steps +# rather than walking through prt_fild08 +# +# Using a weight of 10000 makes it pretty sure to completely avoid a map. +# + +# Portal weight. This can be used to make Kore avoid/prefer walking through +# map portals. Only change this, if you exactly know what you're doing! +PORTAL 20 + +# NPC weight. This can be used to avoid/prefer going to the destination +# by using a NPC. Only change this, if you exactly know what you're doing! +NPC 200 + +# Command @go weight. This can be used to avoid/prefer going to the destination +# by using a '@go' Command. Only change this, if you exactly know what you're doing! +COMMAND 20 + +# Use teleport lv 2 or butterfly wing. This can be used to avoid/prefer going to the destination +# by using a teleport lv 2 or butterfly wing. Only change this, if you exactly know what you're doing! +WARPTOSAVEMAP 200 + +# Use airship +AIRSHIP 2000 + +# Maps where you can exit to the same place you came from, +# which confuses routing if added to portals thoughtlessly. +bat_room 10000 +moc_para01 10000 +prt_maze01 10000 + +# Avoid trying to route through prt_fild08a (which may not even exist) unless required to do so. +prt_fild08a 10000 + +# Add your map weights here. Format: <mapname> <weight> diff --git a/openkore_llm_knowledge/config/shop.txt b/openkore_llm_knowledge/config/shop.txt new file mode 100644 index 0000000000..4538950287 --- /dev/null +++ b/openkore_llm_knowledge/config/shop.txt @@ -0,0 +1,22 @@ +# This file controls which items to put into your shop, when you are +# vending. To enable vending, set the 'shopAuto' configuration option in +# config.txt to 1. +# +# The first line of the file is your shop title. +# Subsequent lines are of this format (tab-delimited): +# <name> <price>[..<max price>] [<amount>] +# +# Attention: Report the max price is optional. If not informed, the item +# will be always sold at the same price of field "price". If informed, +# the item will be sold between "price" and "max price". +# +# If <price> has commas in it, they must appear in the right places. +# This can be useful for preventing accidentally vending an item +# for the wrong price. +# +# Random shop name: +# My Shop;;My cool shop;;My nice shop +# +# Jellopy 3 +# Andre Card 200,000 5 +# Coconut 1,000..2,000 2 \ No newline at end of file diff --git a/openkore_llm_knowledge/config/sys.txt b/openkore_llm_knowledge/config/sys.txt new file mode 100644 index 0000000000..05807b7518 --- /dev/null +++ b/openkore_llm_knowledge/config/sys.txt @@ -0,0 +1,61 @@ +###### Localization settings ###### +locale + +###### Localization compatibility ###### +# Enable to make Kore compatible with old 2.0 configs. +locale_compat 0 + +###### Wx interface settings ###### +wxHideConsole 1 +wxFont + + +###### Bus system settings ###### +# Whether to enable the bus system. +bus 0 + +# If you want to connect to an already running bus server on a different +# computer, specify the host and port here. +bus_server_host +bus_server_port +bus_userAgent + + +###### Vx interface settings ###### +panelTwo_domains publicchat, pm, guildchat, partychat, pm/sent, list, info, selfchat, schat, error, warning +panelOne_lineLimit 900 +panelTwo_lineLimit 100 +panelFont Verdana +menuFont Lucida Console +sbarFont Arial +panelOne_height 8 +panelOne_width 60 +panelOne_side top +panelOne_fontsize 8 +panelTwo_height 4 +panelTwo_width 40 +panelTwo_side bottom +panelTwo_fontsize 8 + + +###### Plugin settings ###### +# loadPlugins <0|1|2|3> +# this option controls loading of plugins at startup or when the "plugin load all" command is used. +# 0 : do not load plugins +# 1 : load all plugins +# 2 : only load plugins that are listed in loadPlugins_list +# 3 : load all plugins except those listed in skipPlugins_list +loadPlugins 2 + +# loadPlugins_list <list> +# if loadPlugins is set to 2, this comma-separated list of plugin names (filename without the extension) +# specifies which plugin files to load at startup or when the "plugin load all" command is used. +loadPlugins_list macro,profiles,breakTime,raiseStat,raiseSkill,map,reconnect,eventMacro,item_weight_recorder,xconf,OTP,LatamChecksum,AdventureAgency,LATAMTranslate + +# skipPlugins_list <list> +# if loadPlugins is set to 3, this comma-separated list of plugin names (filename without the extension) +# specifies which plugin files to skip at startup or when the "plugin load all" command is used. +skipPlugins_list + +###### Miscellaneous ###### +sendAnonymousStatisticReport 0 diff --git a/openkore_llm_knowledge/config/timeouts.txt b/openkore_llm_knowledge/config/timeouts.txt new file mode 100644 index 0000000000..63a2e36617 --- /dev/null +++ b/openkore_llm_knowledge/config/timeouts.txt @@ -0,0 +1,220 @@ +# You shouldn't need to modify these variables +# If you want to really mess up the bot, go ahead :) +# +# The value of each variable is reffered as "x". + +# Server connection timeouts +master 12 +gamelogin 12 +charlogin 12 +maplogin 12 +play 40 +# When disconnected, wait x seconds before reconnecting again +reconnect 30 + +# After repeated disconnects, wait longer before reconnecting, to reduce load on the server. +# The first value in this list overrides the "reconnect" timeout above. +reconnect_backoff 30,60,120,180,300,600,600,900,900,1800 + +# Add a random amount of seconds to reconnect time, up to a maximum of reconnect_random seconds. +reconnect_random 20 + +# Wait x seconds for a poseidon reply before disconnecting +# Ignore this if you don't use Poseidon Server +poseidon_wait_reply 15 + +# Activate AI after x seconds after the map's loaded +ai 1 + +ai_move_retry 0.4 +ai_move_giveup 1.2 + +ai_homunculus_standby 0.5 +ai_mercenary_standby 0.5 + +# Send the attack packet every x seconds, if it hasn't been send already +ai_attack 1 +ai_homunculus_attack 1 +ai_mercenary_attack 1 + +ai_attack_after_skill 0.5 + +ai_homunculus_dance_attack_melee 0.2 + +ai_mercenary_dance_attack_melee 0.2 +ai_mercenary_dance_attack_ranged 0.2 + +# Check for monsters to attack every x seconds +ai_attack_auto 0.5 +ai_homunculus_attack_auto 0.5 +ai_mercenary_attack_auto 0.5 + +ai_attack_route_adjust 0.3 +ai_homunculus_route_adjust 0.3 +ai_mercenary_route_adjust 0.3 + +# Ignore monster for x seconds if there is no route to it +ai_attack_failedLOS 12 + +# Give up attacking a monster if it can't be reached within x seconds +ai_attack_giveup 6 + +ai_homunculus_check_monster_auto 0.2 +ai_mercenary_check_monster_auto 0.2 + +# If you've just killed a monster, and there are no aggressives, +# and you're not picking up any items, wait x seconds before doing +# anything else. +ai_attack_waitAfterKill 0.3 +ai_homunculus_attack_waitAfterKill 0.3 +ai_mercenary_attack_waitAfterKill 0.3 + +# Every x seconds loop the attack logic routine (send move, attack, skill, avoid, etc) +ai_attack_main 0.1 +ai_homunculus_attack_main 0.1 +ai_mercenary_attack_main 0.1 + +ai_attack_unstuck 2.75 +ai_attack_unfail 12 + +ai_route_unstuck 2 + +# Pause for the specified number of seconds after taking something, +# it increases the interval between taking several items and between +# taking the last item and continuing the actions. +ai_items_take_delay 1 + +# When your monster died, start checking for loot after x seconds +ai_items_take_start 0.3 + +# Stop checking for loot x seconds after it has begun checking +ai_items_take_end 0.6 + +# When standing near an item, send the 'take' packet every x seconds until +# the item has been taken. +ai_take .4 + +# Give up if unable to pickup item after x seconds +ai_take_giveup 3 +ai_items_gather_giveup 3 + +# Every x seconds, check items for gathering +ai_items_gather_auto .3 + +# Only gather items that have been more than x seconds on screen +ai_items_gather_start .3 + +# Delay between items when tranferring in batches. +ai_transfer_items 0.15 + +# Delay between map_loaded and send ignoreAll command +ai_ignoreAll 3 + +ai_follow_lost_end 10 +ai_getInfo 2 +ai_thanks_set 8 +ai_dealAuto 3 +ai_dealAutoCancel 5 +ai_partyAutoDeny 3 +ai_guildAutoDeny 3 +ai_dead_respawn 4 +ai_wipe_old 200 +ai_wipe_check 30 + +# Every x seconds, check party share settings +ai_partyShareCheck 60 +# Every x seconds, check pet hungry for feeding +ai_petFeed 60 +# Every x seconds, check homun hungry for feeding +ai_homunFeed 60 + +# Send the sit/stand packet at most every x second +ai_sit 1 +# Sit after having idled for x seconds +ai_sit_idle 10 +# Stand after x seconds, after having typed the 'stand' command +ai_stand_wait 0 +# Sit after x seconds, after having typed the "sit" command +ai_sit_wait 0 +# If there are non-party players around and you are to stand up due to the +# hp/sp sufficiency, do so when the specified number of seconds elapsed +ai_safe_stand_up 2 + +ai_skill_use 0.75 +ai_skill_use_giveup 1 +ai_item_use_auto 0.5 +ai_item_equip_auto 0.75 +ai_equipAuto_skilluse_giveup 5 +ai_equip_giveup 2 + +ai_teleport 1 +ai_teleport_away 3 +ai_teleport_idle 4 +ai_teleport_portal 2 +ai_teleport_hp 3 +ai_teleport_safe_force 120 + +ai_teleport_retry 0.5 +ai_teleport_delay 0.5 + +ai_portal_wait 0.5 + +# These timeouts are used in missing portals logic +ai_portal_give_up 10 +ai_portal_re_add_missed 3600 + +# You probably don't ever have to change the following timeouts +ai_route_calcRoute 1 +ai_route_npcTalk 10 + +# These timeouts are used in npc conversation (Task::TalkNPC) +ai_npc_talk_wait_after_close_to_cancel 0.5 +ai_npc_talk_wait_after_cancel_to_destroy 0.5 + +ai_npc_talk_wait_before_continue 0.7 + +ai_buyAuto 5 +ai_buyAuto_wait 2 +ai_buyAuto_wait_giveup_npc 15 +ai_buyAuto_wait_before_buy 2 +ai_buyAuto_wait_after_packet_giveup 15 +ai_buyAuto_wait_after_restart 2 + +ai_sellAuto 2 +ai_sellAuto_wait_giveup_npc 15 +ai_sellAuto_wait_before_sell 2 +ai_sellAuto_wait_after_packet_giveup 15 + +ai_storageAuto 2 +ai_storageAuto_giveup 15 +ai_storageAuto_useItem 2 +ai_storageAuto_wait_before_action 2 +# delay between sending cart item add/get packets +ai_cartAuto 0.15 + +# delay between checking if we need to do any cartAuto functions +ai_cartAutoCheck 3 +ai_avoidcheck 0.5 +ai_shop 4 +ai_shop_useskill_delay 5 +ai_buyer_shopCheck 60 + +# delay between repairAuto +ai_repair 4 + +# delay before starting escape sequence +ai_route_escape 8 + +# Don't change the following timeouts! +ai_sync 12 + +injectSync 5 +injectKeepAlive 12 +welcomeText 4 +patchserver 120 + +# Time to wait before load map in xKore mode +ai_clientSuspend 1 + +meetingPosition_extra_time_actor 0.2 +meetingPosition_extra_time_target 0.5 diff --git a/openkore_llm_knowledge/knowledge/config_system.md b/openkore_llm_knowledge/knowledge/config_system.md new file mode 100644 index 0000000000..c81528729b --- /dev/null +++ b/openkore_llm_knowledge/knowledge/config_system.md @@ -0,0 +1,27 @@ +# Configuration System (Curated from `control/`) + +Configuration files in `control/` define runtime behavior and policies. This bundle copies the full `control/*.txt` set into `openkore_llm_knowledge/config/`. + +## `config.txt` +Primary behavior configuration file. It contains the main bot options (AI behavior, combat options, looting, movement, network/runtime preferences, plugin-related keys, etc.). + +Use `config.txt` to set high-level behavior and feature toggles. + +## `items_control.txt` +Item-level policy file controlling item handling rules. +Typical usage includes deciding whether items should be picked up, stored, sold, or ignored based on names/IDs and rule flags. + +Use `items_control.txt` for inventory and looting policy tuning. + +## `mon_control.txt` +Monster-level AI policy file. +It defines per-monster behavior such as attack mode, teleport/disconnect response, and conditional thresholds. + +Use `mon_control.txt` to tune combat risk handling and target strategy. + +## Practical model +- `config.txt` = global behavior defaults +- `items_control.txt` = item policy overrides +- `mon_control.txt` = monster policy overrides + +Together, these provide most day-to-day operational control without changing source modules. diff --git a/openkore_llm_knowledge/knowledge/macro_system.md b/openkore_llm_knowledge/knowledge/macro_system.md new file mode 100644 index 0000000000..e1bc339631 --- /dev/null +++ b/openkore_llm_knowledge/knowledge/macro_system.md @@ -0,0 +1,31 @@ +# Macro System + +## `macros.txt` +The macro plugin (`plugins/macro/macro.pl`) loads a macro file from config key `macro_file`, defaulting to `macros.txt`. + +What it provides: +- macro definitions (named scripts/blocks) +- automacro triggers +- runtime command control via `macro` command + +The plugin reparses the configured macro file when `macro_file` changes and hooks runtime events according to defined automacro conditions. + +## `eventMacros.txt` +The eventMacro plugin (`plugins/eventMacro/eventMacro.pl`) loads from config key `eventMacro_file`, defaulting to `eventMacros.txt`. + +What it provides: +- event-driven macro model +- condition-based automacro activation +- runtime control via `eventMacro` / `emacro` commands + +If no event macro file is present, the plugin disables itself in startup logic. + +## Automacro behavior (conceptual) +Across both macro systems, automacros generally follow this pattern: +1. Parse macro file and build macro/automacro structures. +2. Subscribe to relevant hooks/events. +3. Evaluate conditions (state/event/regex/numeric checks). +4. Queue or execute macro actions when conditions are satisfied. +5. Respect timeout/run-once/enable-disable states and user commands. + +This makes macro automation configurable from text files rather than source edits. diff --git a/openkore_llm_knowledge/knowledge/plugin_system.md b/openkore_llm_knowledge/knowledge/plugin_system.md new file mode 100644 index 0000000000..14d0fdda87 --- /dev/null +++ b/openkore_llm_knowledge/knowledge/plugin_system.md @@ -0,0 +1,41 @@ +# Plugin System + +## Architecture (as seen in representative plugins) +OpenKore plugins are Perl packages under `plugins/` with an entrypoint `.pl` file. A plugin typically: +1. imports core APIs (`Plugins`, `Commands`, `Settings`, `Globals`), +2. registers itself, +3. subscribes to runtime hooks, +4. optionally registers console commands, +5. cleans up on unload. + +Representative curated files: +- `plugins/macro/macro.pl` +- `plugins/eventMacro/eventMacro.pl` +- `plugins/reconnect/reconnect.pl` +- `plugins/profiles/profiles.pl` +- `plugins/map/map.pl` + +## Hook system +Representative plugins use hook APIs such as: +- `Plugins::addHooks(...)` for multiple hook subscriptions. +- Event points like `start3`, `mainLoop_pre`, and `configModify`. +- Hook callbacks to react to runtime state or packets. + +In the macro/eventMacro plugins, hooks are used to load macro configs at startup, react to config changes, and run checks/actions each loop cycle. + +## Plugin lifecycle +Common lifecycle pattern: +- `Plugins::register(name, description, unload_cb[, reload_cb])` +- startup callback(s) load plugin state and files +- runtime callbacks handle logic +- unload callback removes hooks/commands and clears state + +The macro plugin includes both unload and reload behavior; eventMacro includes explicit unload cleanup and file reparse logic. + +## Command registration +Plugins can expose user commands via `Commands::register`. +Examples in this bundle: +- macro plugin: `macro ...` +- eventMacro plugin: `eventMacro ...` and `emacro ...` + +These command handlers provide runtime control (status, list, enable/disable, variable ops, etc.) without modifying core code. diff --git a/openkore_llm_knowledge/plugins/eventMacro/README.md b/openkore_llm_knowledge/plugins/eventMacro/README.md new file mode 100644 index 0000000000..e80f329006 --- /dev/null +++ b/openkore_llm_knowledge/plugins/eventMacro/README.md @@ -0,0 +1,10 @@ +# eventMacro +My personal rework of openkore's macro plugin + +TODO: + +1 - Create more conditions, at least the same number as the old macro plugin had. +2 - Hook AI only when we are sure an automacro is to be activated. +3 - Make slot system for automacros and macros, so in each slot a macro can be run and a group of automacros can be checked (so multiple macros can be run at the same time). +4 - Transfer macro code check to parsing time. +5 - Review macro condition code parsing (old Parser.pm and Script.pm) diff --git a/openkore_llm_knowledge/plugins/eventMacro/eventMacro.pl b/openkore_llm_knowledge/plugins/eventMacro/eventMacro.pl new file mode 100644 index 0000000000..5d15435377 --- /dev/null +++ b/openkore_llm_knowledge/plugins/eventMacro/eventMacro.pl @@ -0,0 +1,638 @@ +package eventMacro; + +use lib $Plugins::current_plugin_folder; + +use strict; +use Getopt::Long qw( GetOptionsFromArray ); +use Time::HiRes qw( &time ); +use Plugins; +use Settings; +use Globals; +use Utils; +use Misc; +use Log qw(message error warning debug); +use Translation qw( T TF ); +use AI; + +use eventMacro::Core; +use eventMacro::Data; +use eventMacro::Lists; +use eventMacro::Automacro; +use eventMacro::FileParser; +use eventMacro::Macro; +use eventMacro::Runner; +use eventMacro::Utilities qw(find_variable); + + +Plugins::register('eventMacro', 'allows usage of eventMacros', \&Unload); + +my $hooks = Plugins::addHooks( + ['configModify', \&onConfigModify, undef], + ['start3', \&onstart3, undef], + ['pos_load_config.txt', \&checkConfig, undef], + ['check_triggered_automacros', \&manage_check_triggered_automacros, undef], +); + +my $chooks = Commands::register( + ['eventMacro', "eventMacro plugin", \&commandHandler], + ['emacro', "eventMacro plugin", \&commandHandler] +); + +my $file_handle; +my $file; +my $parseAndHook_called; + +sub Unload { + message "[eventMacro] Plugin unloading\n", "system"; + Settings::removeFile($file_handle) if defined $file_handle; + undef $file_handle; + undef $file; + if (defined $eventMacro) { + $eventMacro->unload(); + undef $eventMacro; + } + Plugins::delHooks($hooks); + Commands::unregister($chooks); +} + +sub manage_check_triggered_automacros { + my ($hook, $args) = @_; + + if (!defined $eventMacro) { + $args->{return} = 0; + return; + } elsif ($eventMacro->{number_of_triggered_automacros} == 0) { + $args->{return} = 0; + return; + } + + foreach my $array_member (@{$eventMacro->{triggered_prioritized_automacros_index_list}}) { + + my $automacro = $eventMacro->{Automacro_List}->get($array_member->{index}); + + next unless $automacro->is_timed_out; + + $args->{return} = 1; + return; + } + + $args->{return} = 0; + return; +} + +sub checkConfig { + $timeout{eventMacro_delay}{timeout} = 1 unless defined $timeout{eventMacro_delay}; + $config{eventMacro_orphans} = 'terminate' unless defined $config{eventMacro_orphans}; + $config{eventMacro_CheckOnAI} = 'auto' unless defined $config{eventMacro_CheckOnAI}; + $file = (defined $config{eventMacro_file}) ? $config{eventMacro_file} : "eventMacros.txt"; + return 1; +} + +sub onstart3 { + debug "[eventMacro] Loading start\n", "eventMacro", 2; + $file_handle = Settings::addControlFile($file,loader => [\&parseAndHook], mustExist => 0); + $parseAndHook_called = 0; + Settings::loadByHandle($file_handle); + if (!$parseAndHook_called) { + warning "[eventMacro] No control/eventMacros.txt file was found. Plugin disabled.\n"; + Commands::run('plugin unload eventMacro'); + } +} + +sub onConfigModify { + my (undef, $args) = @_; + if ($args->{key} eq 'eventMacro_file') { + Settings::removeFile($file_handle); + $file_handle = Settings::addControlFile($args->{val}, loader => [ \&parseAndHook]); + Settings::loadByHandle($file_handle); + } +} + +sub parseAndHook { + my $file = shift; + $parseAndHook_called++; + debug "[eventMacro] Starting to parse file '$file'\n", "eventMacro", 2; + if (defined $eventMacro) { + debug "[eventMacro] Plugin global variable '\$eventMacro' is already defined, this must be a file reload. Unloading all current config.\n", "eventMacro", 2; + $eventMacro->unload(); + undef $eventMacro; + debug "[eventMacro] Plugin global variable '\$eventMacro' was set to undefined.\n", "eventMacro", 2; + } + $eventMacro = new eventMacro::Core($file); + if ($eventMacro->{parse_failed}) { + debug "[eventMacro] Loading error\n", "eventMacro", 2; + return; + } else { + debug "[eventMacro] Loading success\n", "eventMacro", 2; + } + + if ($char && $net && $net->getState() == Network::IN_GAME) { + $eventMacro->check_all_conditions(); + } +} + +sub commandHandler { + ### no parameter given + if (!defined $_[1]) { + message "usage: eventMacro [MACRO|auto|list|status|check|stop|pause|unpause|var_get|var_set|enable|disable|include] [extras]\n", "list"; + message + "eventMacro MACRO: Run macro MACRO\n". + "eventMacro auto AUTOMACRO: Get info on an automacro and it's conditions\n". + "eventMacro list: Lists available macros and automacros\n". + "eventMacro status [macro|automacro]: Shows current status of automacro, macro or both\n". + "eventMacro check [force_stop|force_start|resume]: Sets the state of automacros checking\n". + "eventMacro stop: Stops current running macro\n". + "eventMacro pause: Pauses current running macro\n". + "eventMacro unpause: Unpauses current running macro\n". + "eventMacro var_get: Shows the value of one or all variables\n". + "eventMacro var_set: Set the value of a variable\n". + "eventMacro enable [automacro]: Enable one or all automacros\n". + "eventMacro disable [automacro]: Disable one or all automacros\n". + "eventMacro include [on|off|list] <filename or pattern>: Enables or disables !include in eventMacros file\n"; + return; + } + my ( $arg, @params ) = parseArgs( $_[1] ); + + if ($arg eq 'auto') { + my $automacro = $eventMacro->{Automacro_List}->getByName($params[0]); + if (!$automacro) { + error "[eventMacro] Automacro '".$params[0]."' not found.\n" + } else { + my $message = "[eventMacro] Printing information about automacro '".$automacro->get_name."'.\n"; + my $condition_list = $automacro->{conditionList}; + my $size = $condition_list->size; + my $is_event = $automacro->has_event_type_condition; + $message .= "Number of conditions: '".$size."'\n"; + $message .= "Has event type condition: '". ($is_event ? 'yes' : 'no') ."'\n"; + $message .= "Number of true conditions: '".($size - $automacro->{number_of_false_conditions} - $is_event)."'\n"; + $message .= "Number of false conditions: '".$automacro->{number_of_false_conditions}."'\n"; + $message .= "Is triggered: '".$automacro->running_status."'\n"; + + $message .= "---- Parameters ----\n"; + my $counter = 1; + foreach my $parameter (keys %{$automacro->{parameters}}) { + $message .= $counter." - ".$parameter.": '".$automacro->{parameters}->{$parameter}."'\n"; + } continue { + $counter++; + } + + $message .= "---- Conditions ----\n"; + $counter = 1; + foreach my $condition (@{$condition_list->getItems}) { + if ($condition->condition_type == EVENT_TYPE) { + $message .= $counter." - ".$condition->get_name.": event type condition\n"; + } else { + $message .= $counter." - ".$condition->get_name.": '". ($condition->is_fulfilled ? 'true' : 'false') ."'\n"; + } + } continue { + $counter++; + } + + + my $check_state = $eventMacro->{automacros_index_to_AI_check_state}{$automacro->get_index}; + $message .= "---- AI check states ----\n"; + $message .= "Check on AI off: '". ($check_state->{AI::OFF} ? 'yes' : 'no') ."'\n"; + $message .= "Check on AI manual: '". ($check_state->{AI::MANUAL} ? 'yes' : 'no') ."'\n"; + $message .= "Check on AI auto: '". ($check_state->{AI::AUTO} ? 'yes' : 'no') ."'\n"; + + $message .= "---- End ----\n"; + + message $message; + } + + + ### parameter: list + } elsif ($arg eq 'list') { + message( "The following macros are available:\n" ); + + message( center( T( ' Macros ' ), 25, '-' ) . "\n", 'list' ); + message( $_->get_name . "\n" ) foreach sort { $a->get_name cmp $b->get_name } @{ $eventMacro->{Macro_List}->getItems }; + + message( center( T( ' Auto Macros ' ), 25, '-' ) . "\n", 'list' ); + message( $_->get_name . "\n" ) foreach sort { $a->get_name cmp $b->get_name } @{ $eventMacro->{Automacro_List}->getItems }; + + message( center( T( ' Perl Subs ' ), 25, '-' ) . "\n", 'list' ); + message( "$_\n" ) foreach sort @perl_name; + + message( center( '', 25, '-' ) . "\n", 'list' ); + + + ### parameter: status + } elsif ($arg eq 'status') { + if (defined $params[0] && $params[0] ne 'macro' && $params[0] ne 'automacro') { + message "[eventMacro] '".$params[0]."' is not a valid option\n"; + return; + } + if (!defined $params[0] || $params[0] eq 'macro') { + my $macro = $eventMacro->{Macro_Runner}; + if ( $macro ) { + message( "There's a macro currently running\n", "list" ); + message( sprintf( "Paused: %s\n", $macro->is_paused ? "yes" : "no" ) ); + + my $macro_tree_message = "Macro tree: '".$macro->get_name."'"; + my $submacro = $macro; + while (defined $submacro->{subcall}) { + $submacro = $submacro->{subcall}; + $macro_tree_message .= " --> '".$submacro->get_name."'"; + } + $macro_tree_message .= ".\n"; + message( $macro_tree_message, "list" ); + + while () { + message( center( " Macro ", 25, '-' ) . "\n", 'list' ); + message( sprintf( "Macro name: %s\n", $macro->get_name ), "list" ); + message( sprintf( "overrideAI: %s\n", $macro->overrideAI ), "list" ); + message( sprintf( "interruptible: %s\n", $macro->interruptible ), "list" ); + message( sprintf( "orphan method: %s\n", $macro->orphan ), "list" ); + message( sprintf( "remaining repeats: %s\n", $macro->repeat ), "list" ); + message( sprintf( "macro delay: %s\n", $macro->macro_delay ), "list" ); + + message( sprintf( "current command: %s\n", $macro->{current_line} ), "list" ); + + my $time_until_next_command = (($macro->timeout->{time} + $macro->timeout->{timeout}) - time); + message( sprintf( "time until next command: %s\n", $macro->macro_delay ), "list" ) if ($time_until_next_command > 0); + + message "\n"; + + last if (!defined $macro->{subcall}); + $macro = $macro->{subcall}; + } + } else { + message "There's no macro currently running.\n"; + } + } + if (!defined $params[0] || $params[0] eq 'automacro') { + my $status = $eventMacro->get_automacro_checking_status(); + if ($status == CHECKING_AUTOMACROS) { + message "Automacros are being checked normally.\n"; + } elsif ($status == PAUSED_BY_EXCLUSIVE_MACRO) { + message "Automacros are not being checked because there's an uninterruptible macro running ('".$eventMacro->{Macro_Runner}->last_subcall_name."').\n"; + } elsif ($status == PAUSE_FORCED_BY_USER) { + message "Automacros checking is stopped because the user forced it.\n"; + } else { + message "Automacros checking is active because the user forced it.\n"; + } + } + + ### parameter: check + } elsif ($arg eq 'check') { + if (!defined $params[0] || (defined $params[0] && $params[0] ne 'force_stop' && $params[0] ne 'force_start' && $params[0] ne 'resume')) { + message "usage: eventMacro check [force_stop|force_start|resume]\n", "list"; + message + "eventMacro check force_stop: forces the stop of automacros checking\n". + "eventMacro check force_start: forces the start of automacros checking\n". + "eventMacro check resume: return automacros checking to the normal state\n"; + return; + } + my $status = $eventMacro->get_automacro_checking_status(); + debug "[eventMacro] Command 'check' used with parameter '".$params[0]."'.\n", "eventMacro", 2; + debug "[eventMacro] Previous checking status '".$status."'.\n", "eventMacro", 2; + if ($params[0] eq 'force_stop') { + if ($status == CHECKING_AUTOMACROS) { + message "[eventMacro] Automacros checking forcely stopped.\n"; + $eventMacro->set_automacro_checking_status(PAUSE_FORCED_BY_USER); + } elsif ($status == PAUSED_BY_EXCLUSIVE_MACRO) { + message "[eventMacro] Automacros were not being checked because there's an uninterruptible macro running ('".$eventMacro->{Macro_Runner}->last_subcall_name."').". + "Now they will be forcely stopped even after macro ends (caution).\n"; + $eventMacro->set_automacro_checking_status(PAUSE_FORCED_BY_USER); + } elsif ($status == PAUSE_FORCED_BY_USER) { + message "[eventMacro] Automacros checking is already forcely stopped.\n"; + } else { + message "[eventMacro] Automacros checking is forcely active, now it will be forcely stopped.\n"; + $eventMacro->set_automacro_checking_status(PAUSE_FORCED_BY_USER); + } + } elsif ($params[0] eq 'force_start') { + if ($status == CHECKING_AUTOMACROS) { + message "[eventMacro] Automacros are already being checked, now it will be forcely kept this way.\n"; + $eventMacro->set_automacro_checking_status(CHECKING_FORCED_BY_USER); + } elsif ($status == PAUSED_BY_EXCLUSIVE_MACRO) { + message "[eventMacro] Automacros were not being checked because there's an uninterruptible macro running ('".$eventMacro->{Macro_Runner}->last_subcall_name."').". + "Now automacros checking will be forcely activated (caution).\n"; + $eventMacro->set_automacro_checking_status(CHECKING_FORCED_BY_USER); + } elsif ($status == PAUSE_FORCED_BY_USER) { + message "[eventMacro] Automacros checking is forcely stopped, now it will be forcely activated.\n"; + $eventMacro->set_automacro_checking_status(CHECKING_FORCED_BY_USER); + } else { + message "[eventMacro] Automacros checking is already forcely active.\n"; + } + } else { + if ($status == CHECKING_AUTOMACROS || $status == PAUSED_BY_EXCLUSIVE_MACRO) { + message "[eventMacro] Automacros checking is not forced by the user to be able to resume.\n"; + } else { + if (!defined $eventMacro->{Macro_Runner}) { + message "[eventMacro] Since there's no macro in execution automacros will resume to being normally checked.\n"; + $eventMacro->set_automacro_checking_status(CHECKING_AUTOMACROS); + } elsif ($eventMacro->{Macro_Runner}->last_subcall_interruptible == 1) { + message "[eventMacro] Since there's a macro in execution, and it is interruptible, automacros will resume to being normally checked.\n"; + $eventMacro->set_automacro_checking_status(CHECKING_AUTOMACROS); + } elsif ($eventMacro->{Macro_Runner}->last_subcall_interruptible == 0) { + message "[eventMacro] Since there's a macro in execution ('".$eventMacro->{Macro_Runner}->last_subcall_name."') , and it is not interruptible, automacros won't resume to being checked until it ends.\n"; + $eventMacro->set_automacro_checking_status(PAUSED_BY_EXCLUSIVE_MACRO); + } + } + } + + + ### parameter: stop + } elsif ($arg eq 'stop') { + my $macro = $eventMacro->{Macro_Runner}; + if ( $macro ) { + message "Stopping macro '".$eventMacro->{Macro_Runner}->last_subcall_name."'.\n"; + $eventMacro->clear_queue(); + } else { + message "There's no macro currently running.\n"; + } + + + ### parameter: pause + } elsif ($arg eq 'pause') { + my $macro = $eventMacro->{Macro_Runner}; + if ( $macro ) { + if ($macro->is_paused()) { + message "Macro '".$eventMacro->{Macro_Runner}->last_subcall_name."' is already paused.\n"; + } else { + message "Pausing macro '".$eventMacro->{Macro_Runner}->last_subcall_name."'.\n"; + $eventMacro->{Macro_Runner}->pause(); + } + } else { + message "There's no macro currently running.\n"; + } + + + ### parameter: unpause + } elsif ($arg eq 'unpause') { + my $macro = $eventMacro->{Macro_Runner}; + if ( $macro ) { + if ($macro->is_paused()) { + message "Unpausing macro '".$eventMacro->{Macro_Runner}->last_subcall_name."'.\n"; + $eventMacro->{Macro_Runner}->unpause(); + } else { + message "Macro '".$eventMacro->{Macro_Runner}->last_subcall_name."' is not paused.\n"; + } + } else { + message "There's no macro currently running.\n"; + } + + + ### parameter: var_get + } elsif ($arg eq 'var_get') { + if (!defined $params[0]) { + my $counter; + message "[eventMacro] Printing values off all variables\n", "menu"; + + $counter = 1; + message( center( " Scalars ", 25, '-' ) . "\n", 'list' ); + foreach my $scalar_name (keys %{$eventMacro->{Scalar_Variable_List_Hash}}) { + my $value = $eventMacro->{Scalar_Variable_List_Hash}{$scalar_name}; + $value = 'undef' unless (defined $value); + message $counter." - '\$".$scalar_name."' = '".$value."'\n", "menu"; + } continue { + $counter++; + } + + $counter = 1; + message( center( " Arrays ", 25, '-' ) . "\n", 'list' ); + foreach my $array_name (keys %{$eventMacro->{Array_Variable_List_Hash}}) { + message $counter." - '@".$array_name."'\n", "menu"; + foreach my $index (0..$#{$eventMacro->{Array_Variable_List_Hash}{$array_name}}) { + my $value = $eventMacro->{Array_Variable_List_Hash}{$array_name}[$index]; + $value = 'undef' unless (defined $value); + message " '\$".$array_name."[".$index."]' = '".$value."'\n", "menu"; + } + } continue { + $counter++; + } + + $counter = 1; + message( center( " Hashes ", 25, '-' ) . "\n", 'list' ); + foreach my $hash_name (keys %{$eventMacro->{Hash_Variable_List_Hash}}) { + message $counter." - '%".$hash_name."'\n", "menu"; + my $hash = $eventMacro->{Hash_Variable_List_Hash}{$hash_name}; + foreach my $key (keys %{$hash}) { + my $value = $eventMacro->{Hash_Variable_List_Hash}{$hash_name}{$key}; + $value = 'undef' unless (defined $value); + message " '\$".$hash_name."{".$key."}' = '".$value."'\n", "menu"; + } + } continue { + $counter++; + } + + } else { + if (my $var = find_variable($params[0])) { + if ($var->{type} eq 'scalar') { + if (exists $eventMacro->{Scalar_Variable_List_Hash}{$var->{real_name}}) { + my $var_value = $eventMacro->get_scalar_var($var->{real_name}); + $var_value = 'undef' unless (defined $var_value); + message "'[eventMacro] '".$var->{display_name}."' = '".$var_value."'\n", "menu"; + + } else { + message "[eventMacro] Scalar variable '".$var->{display_name}."' doesn't exist\n"; + } + + } elsif ($var->{type} eq 'accessed_array') { + if (exists $eventMacro->{Array_Variable_List_Hash}{$var->{real_name}}) { + my $var_value = $eventMacro->get_array_var($var->{real_name}, $var->{complement}); + $var_value = 'undef' unless (defined $var_value); + message "'[eventMacro] '".$var->{display_name}."' = '".$var_value."'\n", "menu"; + + } else { + message "[eventMacro] Array variable '".$var->{display_name}."' doesn't exist\n"; + } + + } elsif ($var->{type} eq 'array') { + if (exists $eventMacro->{Array_Variable_List_Hash}{$var->{real_name}}) { + message "[eventMacro] '".$var->{display_name}."'\n"; + + foreach my $index (0..$#{$eventMacro->{Array_Variable_List_Hash}{$var->{real_name}}}) { + my $value = $eventMacro->{Array_Variable_List_Hash}{$var->{real_name}}[$index]; + $value = 'undef' unless (defined $value); + message "[eventMacro] '\$".$var->{real_name}."[".$index."]' = '".$value."'\n", "menu"; + } + + } else { + message "[eventMacro] Array variable '".$var->{display_name}."' doesn't exist\n"; + } + + } elsif ($var->{type} eq 'accessed_hash') { + if (exists $eventMacro->{Hash_Variable_List_Hash}{$var->{real_name}}) { + my $var_value = $eventMacro->get_hash_var($var->{real_name}, $var->{complement}); + $var_value = 'undef' unless (defined $var_value); + message "'[eventMacro] '".$var->{display_name}."' = '".$var_value."'\n", "menu"; + + } else { + message "[eventMacro] Hash variable '".$var->{display_name}."' doesn't exist\n"; + } + + } elsif ($var->{type} eq 'hash') { + if (exists $eventMacro->{Hash_Variable_List_Hash}{$var->{real_name}}) { + message "[eventMacro] '".$var->{display_name}."'\n"; + my $hash = $eventMacro->{Hash_Variable_List_Hash}{$var->{real_name}}; + foreach my $key (keys %{$hash}) { + my $value = $eventMacro->{Hash_Variable_List_Hash}{$var->{real_name}}{$key}; + $value = 'undef' unless (defined $value); + message "[eventMacro] '\$".$var->{real_name}."{".$key."}' = '".$value."'\n", "menu"; + } + + } else { + message "[eventMacro] Hash variable '".$var->{display_name}."' doesn't exist\n"; + } + } + + } else { + message "[eventMacro] '".$params[0]."' is not a valid variable name syntax\n"; + } + } + + ### parameter: var_set + } elsif ($arg eq 'var_set') { + if (!defined $params[0] || !defined $params[1]) { + message "usage: eventMacro var_set [variable name] [variable value]\n", "list"; + + } else { + if (my $var = find_variable($params[0])) { + if ($var->{real_name} =~ /^\./) { + error "[eventMacro] System variables cannot be set by hand (The ones starting with a dot '.')\n"; + + } elsif ($var->{type} eq 'scalar') { + message "[eventMacro] Setting the value of scalar variable '".$var->{display_name}."' to '".$params[1]."'.\n"; + $eventMacro->set_scalar_var($var->{real_name}, $params[1]); + + } elsif ($var->{type} eq 'accessed_array') { + message "[eventMacro] Setting the value of array variable '".$var->{display_name}."' to '".$params[1]."'.\n"; + $eventMacro->set_array_var($var->{real_name}, $var->{complement}, $params[1]); + + } elsif ($var->{type} eq 'array') { + my $value = join('', @params[1..$#params]); + if ($value =~ /^\((.*)\)$/i) { + message "[eventMacro] Setting the value of array variable '".$var->{display_name}."' to '".$value."'.\n"; + my @members = split(/\s*,\s*/, $1); + $eventMacro->set_full_array($var->{real_name}, \@members); + + } else { + message "[eventMacro] '".$params[1]."' is not a valid array value syntax. Correct syntax:\n". + "\@array_name (member1,member2,member3).\n"; + } + + } elsif ($var->{type} eq 'accessed_hash') { + message "[eventMacro] Setting the value of hash variable '".$var->{display_name}."' to '".$params[1]."'.\n"; + $eventMacro->set_hash_var($var->{real_name}, $var->{complement}, $params[1]); + + } elsif ($var->{type} eq 'hash') { + my $value = join('', @params[1..$#params]); + if ($value =~ /^\((.*)\)$/i) { + message "[eventMacro] Setting the value of hash variable '".$var->{display_name}."' to '".$value."'.\n"; + my @members = split(/\s*,\s*/, $1); + my %hash; + foreach my $hash_member (@members) { + my ($key, $value) = split(/\s*=>\s*/, $hash_member); + if ($hash_member =~ /(.+)\s*=>\s*(.+)/) { + my $key = $1; + my $value = $2; + $hash{$key} = $value; + } else { + message "[eventMacro] '".$params[1]."' is not a valid hash key/value pair syntax. Correct syntax:\n". + "key1 => value1\n"; + return; + } + } + $eventMacro->set_full_hash($var->{real_name}, \%hash); + + } else { + message "[eventMacro] '".$params[1]."' is not a valid hash value syntax. Correct syntax:\n". + "\%hash_name (key1 => value1, key2 => value2).\n"; + } + } + } else { + message "[eventMacro] '".$params[0]."' is not a valid variable name syntax\n"; + } + } + + + ### parameter: enable + } elsif ($arg eq 'enable') { + if (!defined $params[0] || $params[0] eq 'all') { + $eventMacro->enable_all_automacros(); + message "[eventMacro] All automacros were enabled.\n"; + return; + } + for my $automacro_name (@params) { + my $automacro = $eventMacro->{Automacro_List}->getByName($automacro_name); + if (!$automacro) { + error "[eventMacro] Automacro '".$automacro_name."' not found.\n" + } else { + message "[eventMacro] Enabled automacro '".$automacro_name."'.\n"; + $eventMacro->enable_automacro($automacro); + } + } + + + ### parameter: disable + } elsif ($arg eq 'disable') { + if (!defined $params[0] || $params[0] eq 'all') { + $eventMacro->disable_all_automacros(); + message "[eventMacro] All automacros were disabled.\n"; + return; + } + for my $automacro_name (@params) { + my $automacro = $eventMacro->{Automacro_List}->getByName($automacro_name); + if (!$automacro) { + error "[eventMacro] Automacro '".$automacro_name."' not found.\n" + } else { + message "[eventMacro] Disabled automacro '".$automacro_name."'.\n"; + $eventMacro->disable_automacro($automacro); + } + } + + ### parameter: include + } elsif ($arg eq 'include') { + + if ( + ($params[0] ne 'list' && $params[0] ne 'on' && $params[0] ne 'off') || + ($params[0] eq 'list' && @params > 1) || + (($params[0] eq 'on' || $params[0] eq 'off') && @params < 2) || + (@params > 2 || @params < 1) + ) { + message "[eventMacro] Usage:\n". + "eventMacro include on <filename or pattern>\n". + "eventMacro include on all\n". + "eventMacro include off <filename or pattern>\n". + "eventMacro include off all\n". + "eventMacro include list\n", 'list'; + return; + } + $eventMacro->include($params[0], $params[1]); + + ### if nothing triggered until here it's probably a macro name + } elsif ( !$eventMacro->{Macro_List}->getByName( $arg ) ) { + error "[eventMacro] Macro $arg not found\n"; + } elsif ( $eventMacro->{Macro_Runner} ) { + warning "[eventMacro] A macro is already running. Wait until the macro has finished or call 'eventMacro stop'\n"; + return; + } else { + my $opt = {}; + GetOptionsFromArray( \@params, $opt, 'repeat|r=i', 'overrideAI', 'exclusive', 'macro_delay=f', 'orphan=s' ); + + $eventMacro->set_full_array( ".param", \@params ); + + $eventMacro->{Macro_Runner} = new eventMacro::Runner( + $arg, + 'command', + defined $opt->{repeat} ? $opt->{repeat} : 1, + defined $opt->{exclusive} ? $opt->{exclusive} ? 0 : 1 : undef, + 0, # is self_interruptible? (in this case is always negative) + defined $opt->{overrideAI} ? $opt->{overrideAI} : undef, + defined $opt->{orphan} ? $opt->{orphan} : undef, + undef, + defined $opt->{macro_delay} ? $opt->{macro_delay} : undef, + 0 # is subcall? (in this case is always negative) + ); + + if ( defined $eventMacro->{Macro_Runner} ) { + $eventMacro->{AI_start_Macros_Running_Hook_Handle} = Plugins::addHook( 'AI_start', sub { $eventMacro->iterate_macro }, undef ); + } else { + error "[eventMacro] unable to create macro queue.\n"; + } + } +} + +1; \ No newline at end of file diff --git a/openkore_llm_knowledge/plugins/eventMacro/eventMacro/Automacro.pm b/openkore_llm_knowledge/plugins/eventMacro/eventMacro/Automacro.pm new file mode 100644 index 0000000000..95b696ea75 --- /dev/null +++ b/openkore_llm_knowledge/plugins/eventMacro/eventMacro/Automacro.pm @@ -0,0 +1,306 @@ +package eventMacro::Automacro; + +use strict; +use Globals qw( %config %timeout ); +use Log qw(message error warning debug); +use Utils qw( timeOut ); + +use eventMacro::Condition; +use eventMacro::Data qw( EVENT_TYPE ); + +sub new { + my ($class, $name, $parameters) = @_; + my $self = bless {}, $class; + + $self->{name} = $name; + + $self->{conditionList} = new eventMacro::Lists; + $self->{event_type_condition_index} = undef; + $self->{hooks} = {}; + $self->{scalar_variables} = {}; + $self->{array_variables} = {}; + $self->{accessed_array_variables} = {}; + $self->{hash_variables} = {}; + $self->{accessed_hash_variables} = {}; + $self->{parameters} = {}; + $self->{running_status} = 0; + $self->set_parameters( $parameters ); + + $self->{check_on_ai_state} = {}; + $self->parse_CheckOnAI; + + return $self; +} + +sub parse_and_create_conditions { + my ($self, $conditions) = @_; + $self->create_conditions_list( $conditions ); + $self->{number_of_false_conditions} = $self->{conditionList}->size; + if (defined $self->{event_type_condition_index}) { + $self->{number_of_false_conditions}--; + } +} + +sub running_status { + my ($self, $new_status) = @_; + if (defined $new_status) { + $self->{running_status} = $new_status; + } + return $self->{running_status}; +} + +sub get_index { + my ($self) = @_; + return $self->{listIndex}; +} + +sub get_hooks { + my ($self) = @_; + return $self->{hooks}; +} + +sub get_name { + my ($self) = @_; + return $self->{name}; +} + +sub set_timeout_time { + my ($self, $time) = @_; + $self->{parameters}{time} = $time; +} + +sub disable { + my ($self) = @_; + $self->{parameters}{disabled} = 1; + debug "[eventMacro] Disabling ".$self->get_name()."\n", "eventMacro", 2; +} + +sub enable { + my ($self) = @_; + $self->{parameters}{disabled} = 0; + debug "[eventMacro] Enabling ".$self->get_name()."\n", "eventMacro", 2; +} + +sub get_parameter { + my ($self, $parameter) = @_; + return $self->{parameters}{$parameter}; +} + +sub set_call { + my ($self, $parameters, $macro_name) = @_; + $self->{parameters}{'call'} = $macro_name; +} + +sub set_parameters { + my ($self, $parameters) = @_; + foreach (keys %{$parameters}) { + my $key = $_; + my $value = $parameters->{$_}; + $self->{parameters}{$key} = $value; + } + #all parameters must be defined + if (!defined $self->{parameters}{'timeout'}) { + $self->{parameters}{'timeout'} = 0; + } + if (!defined $self->{parameters}{'delay'}) { + $self->{parameters}{'delay'} = 0; + } + if (!defined $self->{parameters}{'run-once'}) { + $self->{parameters}{'run-once'} = 0; + } + if (!defined $self->{parameters}{'CheckOnAI'}) { + $self->{parameters}{'CheckOnAI'} = $config{eventMacro_CheckOnAI}; + } + if (!defined $self->{parameters}{'disabled'}) { + $self->{parameters}{'disabled'} = 0; + } + if (!defined $self->{parameters}{'overrideAI'}) { + $self->{parameters}{'overrideAI'} = 0; + } + if (!defined $self->{parameters}{'orphan'}) { + $self->{parameters}{'orphan'} = $config{eventMacro_orphans}; + } + if (!defined $self->{parameters}{'macro_delay'}) { + $self->{parameters}{'macro_delay'} = $timeout{eventMacro_delay}{timeout}; + } + if (!defined $self->{parameters}{'priority'}) { + $self->{parameters}{'priority'} = 0; + } + if (!defined $self->{parameters}{'exclusive'}) { + $self->{parameters}{'exclusive'} = 0; + } + if (!defined $self->{parameters}{'self_interruptible'}) { + $self->{parameters}{'self_interruptible'} = 0; + } + if (!defined $self->{parameters}{'repeat'}) { + $self->{parameters}{'repeat'} = 1; + } + $self->{parameters}{time} = 0; +} + +sub parse_CheckOnAI { + my ($self) = @_; + my @ai_states = split(/\s*,\s*/, $self->{parameters}{'CheckOnAI'}); + + foreach my $state (@ai_states) { + if ($state ne 'auto' && $state ne 'manual' && $state ne 'off') { + error "[eventMacro] Parameter 'CheckOnAI' on automacro '".$self->{name}."' has a non-valid value '".$state."'. Ignoring it.\n"; + } else { + $self->{check_on_ai_state}{$state} = undef; + } + } +} + +sub create_conditions_list { + my ($self, $conditions) = @_; + foreach (keys %{$conditions}) { + my $module = $_; + my $conditionsText = $conditions->{$_}; + eval "use $module"; + foreach my $newConditionText ( @{$conditionsText} ) { + my $cond = $module->new( $newConditionText, $self->{listIndex} ); + $self->{conditionList}->add( $cond ); + my $cond_index = $cond->get_index; + foreach my $hook ( @{ $cond->get_hooks() } ) { + push ( @{ $self->{hooks}{$hook} }, $cond_index ); + } + foreach my $variable ( @{ $cond->get_variables() } ) { + $self->define_var_types($variable, $cond_index); + } + if ($cond->condition_type == EVENT_TYPE) { + $self->{event_type_condition_index} = $cond_index; + } + } + } +} + +sub define_var_types { + my ($self, $variable, $cond_index) = @_; + if ($variable->{type} eq 'scalar') { + push ( @{ $self->{scalar_variables}{$variable->{real_name}} }, $cond_index ); + + } elsif ($variable->{type} eq 'array') { + push ( @{ $self->{array_variables}{$variable->{real_name}}}, $cond_index ); + + } elsif ($variable->{type} eq 'accessed_array') { + push ( @{ $self->{accessed_array_variables}{$variable->{real_name}}{$variable->{complement}} }, $cond_index ); + + } elsif ($variable->{type} eq 'hash') { + push ( @{ $self->{hash_variables}{$variable->{real_name}}}, $cond_index ); + + } elsif ($variable->{type} eq 'accessed_hash') { + push ( @{ $self->{accessed_hash_variables}{$variable->{real_name}}{$variable->{complement}} }, $cond_index ); + } +} + +sub get_scalar_variables { + my ($self) = @_; + return $self->{scalar_variables}; +} + +sub get_array_variables { + my ($self) = @_; + return $self->{array_variables}; +} + +sub get_accessed_array_variables { + my ($self) = @_; + return $self->{accessed_array_variables}; +} + +sub get_hash_variables { + my ($self) = @_; + return $self->{hash_variables}; +} + +sub get_accessed_hash_variables { + my ($self) = @_; + return $self->{accessed_hash_variables}; +} + +sub has_event_type_condition { + my ($self) = @_; + return defined $self->{event_type_condition_index}; +} + +sub get_event_type_condition_index { + my ($self) = @_; + return $self->{event_type_condition_index}; +} + +sub check_state_type_condition { + my ($self, $condition_index, $callback_type, $callback_name, $args) = @_; + + my $condition = $self->{conditionList}->get($condition_index); + + my $pre_check_status = $condition->is_fulfilled; + + my $pos_check_status = $condition->validate_condition($callback_type, $callback_name, $args); + + debug "[eventMacro] Checking condition '".$condition->get_name()."' of index '".$condition->get_index."' in automacro '".$self->{name}."', fulfilled value before: '".$pre_check_status."', fulfilled value after: '".$pos_check_status."'.\n", "eventMacro", 3; + + if ($pre_check_status == 1 && $condition->is_fulfilled == 0) { + $self->{number_of_false_conditions}++; + } elsif ($pre_check_status == 0 && $condition->is_fulfilled == 1) { + $self->{number_of_false_conditions}--; + } + return $pos_check_status; +} + +sub check_event_type_condition { + my ($self, $callback_type, $callback_name, $args) = @_; + + my $condition = $self->{conditionList}->get($self->{event_type_condition_index}); + + my $return = $condition->validate_condition($callback_type, $callback_name, $args); + + debug "[eventMacro] Checking event type condition '".$condition->get_name()."' of index '".$condition->get_index."' in automacro '".$self->{name}."', fulfilled value: '".$return."'.\n", "eventMacro", 3; + + return $return; +} + +sub are_conditions_fulfilled { + my ($self) = @_; + $self->{number_of_false_conditions} == 0; +} + +sub is_disabled { + my ($self) = @_; + return $self->{parameters}{disabled}; +} + +sub is_timed_out { + my ($self) = @_; + return 1 unless ( $self->{parameters}{'timeout'} ); + return 1 if ( timeOut( { timeout => $self->{parameters}{'timeout'}, time => $self->{parameters}{time} } ) ); + return 0; +} + +sub can_be_added_to_queue { + my ($self) = @_; + return 1 if ($self->are_conditions_fulfilled && !$self->is_disabled && !$self->running_status && !$self->has_event_type_condition); + return 0; +} + +sub can_be_run_from_event { + my ($self) = @_; + return 1 if ($self->are_conditions_fulfilled && !$self->is_disabled && $self->is_timed_out); + return 0; +} + +sub get_new_macro_variables { + my ($self) = @_; + my %new_variables; + foreach my $condition (@{$self->{conditionList}->getItems()}) { + my $new_variables = $condition->get_new_variable_list; + my @variable_names = keys %{ $new_variables }; + foreach my $variable_name (@variable_names) { + my $variable_value = $new_variables->{$variable_name}; + $new_variables{$variable_name} = $variable_value; + } + } + $new_variables{'.caller'} = $self->{name}; + return \%new_variables; +} + +1; \ No newline at end of file diff --git a/openkore_llm_knowledge/plugins/eventMacro/eventMacro/Core.pm b/openkore_llm_knowledge/plugins/eventMacro/eventMacro/Core.pm new file mode 100644 index 0000000000..06a6c89526 --- /dev/null +++ b/openkore_llm_knowledge/plugins/eventMacro/eventMacro/Core.pm @@ -0,0 +1,1827 @@ +package eventMacro::Core; + +use strict; +use Globals; +use Log qw(message error warning debug); +use Utils; +use AI; + +use eventMacro::Core; +use eventMacro::Data; +use eventMacro::Lists; +use eventMacro::Automacro; +use eventMacro::FileParser; +use eventMacro::Macro; +use eventMacro::Runner; +use eventMacro::Condition; +use eventMacro::Utilities qw(find_variable get_key_or_index); + +sub new { + my ($class, $file) = @_; + my $self = bless {}, $class; + + $self->{file} = $file; + + $self->{Macro_List} = new eventMacro::Lists; + $self->{Automacro_List} = new eventMacro::Lists; + $self->{Condition_Modules_Loaded} = {}; + $self->{automacros_index_to_AI_check_state} = {}; + + $self->{Event_Related_Hooks} = {}; + $self->{Hook_Handles} = {}; + $self->{Log_Hook_Handles} = {}; + + $self->{Event_Related_Static_Variables} = {}; + + $self->{Dynamic_Variable_Complements} = {}; + $self->{Dynamic_Variable_Sub_Callbacks} = {}; + $self->{Event_Related_Dynamic_Variables} = {}; + + $self->{Scalar_Variable_List_Hash} = {}; + $self->{Array_Variable_List_Hash} = {}; + $self->{Hash_Variable_List_Hash} = {}; + + #must add a sorting algorithm here later + $self->{triggered_prioritized_automacros_index_list} = []; + + $self->{automacro_index_to_queue_index} = {}; + + my $parse_result = parseMacroFile($file, 0); + if ( !$parse_result ) { + $self->{parse_failed} = 1; + return $self; + } + + $self->create_macro_list($parse_result->{macros}); + + $self->create_automacro_list($parse_result->{automacros}); + + $self->{subs_list} = $parse_result->{subs}; + + $self->define_automacro_check_state; + + $self->{AI_state_change_Hook_Handle} = Plugins::addHook( 'AI_state_change', sub { my $state = $_[1]->{new}; $self->adapt_to_AI_state($state); }, undef ); + + $self->{Currently_AI_state_Adapted_Automacros} = undef; + + $self->adapt_to_AI_state(AI::state); + + $self->{AI_start_Macros_Running_Hook_Handle} = undef; + $self->{AI_start_Automacros_Check_Hook_Handle} = undef; + $self->set_automacro_checking_status(); + + $self->create_callbacks(); + + $self->{Macro_Runner} = undef; + + $self->{number_of_triggered_automacros} = 0; + + $self->set_arrays_size_to_zero(); + $self->set_hashes_size_to_zero(); + + return $self; +} + +sub get_log_hook_sub { + my ($self) = @_; + + return $self->{Log_Event_Sub} if (exists $self->{Log_Event_Sub}); + + $self->{Log_Event_Sub} = sub { + my ($type, $domain, $level, $currentVerbosity, $message, $user_data, $near, $far) = @_; + return if (defined $domain && $domain eq 'eventMacro'); + return if (defined $level && defined $currentVerbosity && $level > $currentVerbosity); + $message =~ s/[\r\n]+$//; + + my $args = { + type => $type, + domain => $domain, + level => $level, + currentVerbosity => $currentVerbosity, + message => $message, + user_data => $user_data, + near => $near, + far => $far, + }; + + my $check_list_hash = $self->{Event_Related_Hooks}{log}; + $self->manage_event_callbacks('hook', 'log', $args, $check_list_hash); + }; + + return $self->{Log_Event_Sub}; +} + +sub adapt_to_AI_state { + my ($self, $state) = @_; + + $self->{Currently_AI_state_Adapted_Automacros} = undef; + + foreach my $automacro (@{$self->{Automacro_List}->getItems()}) { + my $automacro_index = $automacro->get_index; + + if ($self->{automacros_index_to_AI_check_state}{$automacro_index}{$state} == 1) { + + $self->{Currently_AI_state_Adapted_Automacros}{$automacro_index} = 1; + if (!$automacro->running_status && $automacro->can_be_added_to_queue) { + $self->add_to_triggered_prioritized_automacros_index_list($automacro); + } + + } else { + if ($automacro->running_status) { + $self->remove_from_triggered_prioritized_automacros_index_list($automacro); + } + } + } +} + +sub unload { + my ($self) = @_; + $self->clear_queue(); + $self->clean_hooks(); + Plugins::delHook($self->{AI_start_Automacros_Check_Hook_Handle}) if ($self->{AI_start_Automacros_Check_Hook_Handle}); + Plugins::delHook($self->{AI_state_change_Hook_Handle}) if ($self->{AI_state_change_Hook_Handle}); +} + +sub clean_hooks { + my ($self) = @_; + foreach (values %{$self->{Hook_Handles}}) {Plugins::delHook($_)} + foreach (values %{$self->{Log_Hook_Handles}}) {Log::delHook($_)} +} + +sub set_automacro_checking_status { + my ($self, $status) = @_; + + if (!defined $self->{Automacros_Checking_Status}) { + debug "[eventMacro] Initializing automacro checking by default.\n", "eventMacro", 2; + $self->{Automacros_Checking_Status} = CHECKING_AUTOMACROS; + $self->{AI_start_Automacros_Check_Hook_Handle} = Plugins::addHook( 'AI_start', sub { my $state = $_[1]->{state}; $self->AI_start_checker($state); }, undef ); + return; + } elsif ($self->{Automacros_Checking_Status} == $status) { + debug "[eventMacro] automacro checking status is already $status.\n", "eventMacro", 2; + } else { + debug "[eventMacro] Changing automacro checking status from '".$self->{Automacros_Checking_Status}."' to '".$status."'.\n", "eventMacro", 2; + if ( + ($self->{Automacros_Checking_Status} == CHECKING_AUTOMACROS || $self->{Automacros_Checking_Status} == CHECKING_FORCED_BY_USER) && + ($status == PAUSED_BY_EXCLUSIVE_MACRO || $status == PAUSE_FORCED_BY_USER) + ) { + if (defined $self->{AI_start_Automacros_Check_Hook_Handle}) { + debug "[eventMacro] Deleting AI_start hook.\n", "eventMacro", 2; + Plugins::delHook($self->{AI_start_Automacros_Check_Hook_Handle}); + $self->{AI_start_Automacros_Check_Hook_Handle} = undef; + } else { + error "[eventMacro] Tried to delete AI_start hook and for some reason it is already undefined.\n"; + } + } elsif ( + ($self->{Automacros_Checking_Status} == PAUSED_BY_EXCLUSIVE_MACRO || $self->{Automacros_Checking_Status} == PAUSE_FORCED_BY_USER) && + ($status == CHECKING_AUTOMACROS || $status == CHECKING_FORCED_BY_USER) + ) { + if (defined $self->{AI_start_Automacros_Check_Hook_Handle}) { + error "[eventMacro] Tried to add AI_start hook and for some reason it is already defined.\n"; + } else { + debug "[eventMacro] Adding AI_start hook.\n", "eventMacro", 2; + $self->{AI_start_Automacros_Check_Hook_Handle} = Plugins::addHook( 'AI_start', sub { my $state = $_[1]->{state}; $self->AI_start_checker($state); }, undef ); + } + } + $self->{Automacros_Checking_Status} = $status; + } +} + +sub get_automacro_checking_status { + my ($self) = @_; + return $self->{Automacros_Checking_Status}; +} + +sub create_macro_list { + my ($self, $macro) = @_; + foreach my $name (keys %{$macro}) { + #################################### + #####Bad Name Check + #################################### + if ($name =~ /\s/) { + error "[eventMacro] Ignoring macro '$name'. You cannot use spaces in macro names.\n"; + next; + } + + #################################### + #####Duplicated Name Check + #################################### + if (exists $macro->{$name}{'duplicatedMacro'}) { + error "[eventMacro] Ignoring macro '$name'. Macros can't have same name.\n"; + next; + } + my $currentMacro = new eventMacro::Macro($name, $macro->{$name}{lines}); + $self->{Macro_List}->add($currentMacro); + } +} + +sub create_automacro_list { + my ($self, $automacro) = @_; + my %modulesLoaded; + AUTOMACRO: while (my ($name,$value) = each %{$automacro}) { + my ($currentAutomacro, %currentConditions, %currentParameters, $has_event_type_condition, $event_type_condition_name); + $has_event_type_condition = 0; + $event_type_condition_name = undef; + + #################################### + #####Bad Name Check + #################################### + if ($name =~ /\s/) { + error "[eventMacro] Ignoring automacro '$name'. You cannot use spaces in automacro names.\n"; + next AUTOMACRO; + } + + #################################### + #####No Conditions Check + #################################### + if (!exists $value->{'conditions'} || !@{$value->{'conditions'}}) { + error "[eventMacro] Ignoring automacro '$name'. There are no conditions set it in\n"; + next AUTOMACRO; + } + + #################################### + #####No Parameters Check + #################################### + if (!exists $value->{'parameters'} || !@{$value->{'parameters'}}) { + error "[eventMacro] Ignoring automacro '$name'. There are no parameters set in it\n"; + next AUTOMACRO; + } + + ###################################### + #####Duplicated name Check + ###################################### + if (exists $value->{'duplicatedAutomacro'}) { + error "[eventMacro] Ignoring automacro '$name'. Automacros can't have same name\n"; + next AUTOMACRO; + } + + PARAMETER: foreach my $parameter (@{$value->{'parameters'}}) { + ###Check Duplicate Parameter + if (exists $currentParameters{$parameter->{'key'}}) { + error "[eventMacro] Ignoring automacro '$name' (parameter ".$parameter->{'key'}." duplicate)\n"; + next AUTOMACRO; + } + ###Parameter: call with or without param + if ($parameter->{'key'} eq "call" && $parameter->{'value'} =~ /(\S+)\s+(.*)?/) { + my ($macro_name, $params) = ($1 , $2); + + if (!$self->{Macro_List}->getByName($macro_name) ) { + error "[eventMacro] Ignoring automacro '$name' (call '".$macro_name."' is not a valid macro name)\n"; + next AUTOMACRO; + } else { + unless (defined $params) { + $parameter->{'value'} = $macro_name; + } + $currentParameters{$parameter->{'key'}} = $parameter->{'value'}; + } + ###Parameter: delay + } elsif ($parameter->{'key'} eq "delay" && $parameter->{'value'} !~ /^[\d\.]*\d+$/) { + error "[eventMacro] Ignoring automacro '$name' (delay parameter should be a number)\n"; + next AUTOMACRO; + ###Parameter: run-once + } elsif ($parameter->{'key'} eq "run-once" && $parameter->{'value'} !~ /^[01]$/) { + error "[eventMacro] Ignoring automacro '$name' (run-once parameter should be '0' or '1')\n"; + next AUTOMACRO; + + ###Parameter: CheckOnAI + } elsif ($parameter->{'key'} eq "CheckOnAI" && $parameter->{'value'} !~ /^(auto|off|manual)(\s*,\s*(auto|off|manual))*$/) { + error "[eventMacro] Ignoring automacro '$name' (CheckOnAI parameter should be a list containing only the values 'auto', 'manual' and 'off')\n"; + next AUTOMACRO; + + ###Parameter: disabled + } elsif ($parameter->{'key'} eq "disabled" && $parameter->{'value'} !~ /^[01]$/) { + error "[eventMacro] Ignoring automacro '$name' (disabled parameter should be '0' or '1')\n"; + next AUTOMACRO; + + ###Parameter: overrideAI + } elsif ($parameter->{'key'} eq "overrideAI" && $parameter->{'value'} !~ /^[01]$/) { + error "[eventMacro] Ignoring automacro '$name' (overrideAI parameter should be '0' or '1')\n"; + next AUTOMACRO; + + ###Parameter: exclusive + } elsif ($parameter->{'key'} eq "exclusive" && $parameter->{'value'} !~ /^[01]$/) { + error "[eventMacro] Ignoring automacro '$name' (exclusive parameter should be '0' or '1')\n"; + next AUTOMACRO; + + ###Parameter: self_interruptible + } elsif ($parameter->{'key'} eq "self_interruptible" && $parameter->{'value'} !~ /^[01]$/) { + error "[eventMacro] Ignoring automacro '$name' (self_interruptible parameter should be '0' or '1')\n"; + next AUTOMACRO; + + ###Parameter: priority + } elsif ($parameter->{'key'} eq "priority" && $parameter->{'value'} !~ /^\d+$/) { + error "[eventMacro] Ignoring automacro '$name' (priority parameter should be a number)\n"; + next AUTOMACRO; + + ###Parameter: macro_delay + } elsif ($parameter->{'key'} eq "macro_delay" && $parameter->{'value'} !~ /^[\d\.]*\d+$/) { + error "[eventMacro] Ignoring automacro '$name' (macro_delay parameter should be a number (decimals are accepted))\n"; + next AUTOMACRO; + + ###Parameter: orphan + } elsif ($parameter->{'key'} eq "orphan" && $parameter->{'value'} !~ /^(terminate|terminate_last_call|reregister|reregister_safe)$/) { + error "[eventMacro] Ignoring automacro '$name' (orphan parameter should be 'terminate', 'terminate_last_call', 'reregister' or 'reregister_safe'. Given value: '$parameter->{'value'}')\n"; + next AUTOMACRO; + ###Parameter: repeat + } elsif ($parameter->{'key'} eq "repeat" && $parameter->{'value'} !~ /^\d+$/) { + error "[eventMacro] Ignoring automacro '$name' (repeat parameter should be a number)\n"; + next AUTOMACRO; + } else { + $currentParameters{$parameter->{'key'}} = $parameter->{'value'}; + } + } + + ###Recheck Parameter call + if (!exists $currentParameters{'call'}) { + error "[eventMacro] Ignoring automacro '$name' (all automacros must have a macro call)\n"; + next AUTOMACRO; + } + + #################################### + #####Conditions Check + #################################### + CONDITION: foreach my $condition (@{$value->{'conditions'}}) { + + my ($condition_object, $condition_module); + + $condition_module = "eventMacro::Condition::".$condition->{'key'}; + + if (!exists $self->{Condition_Modules_Loaded}{$condition_module}) { + unless ($self->load_condition_module($condition_module)) { + warning "[eventMacro] Ignoring automacro '".$name."' (could not load condition module)\n"; + next AUTOMACRO; + } + } + + $condition_object = $condition_module->new($condition->{'value'}); + + if (defined $condition_object->error) { + warning "[eventMacro] Ignoring automacro '".$name."'\n". + "[eventMacro] Error in condition '".$condition->{'key'}."'\n". + "[eventMacro] Error type: Wrong condition syntax ('".$condition->{'value'}."')\n". + "[eventMacro] Error code: '".$condition_object->error."'.\n"; + next AUTOMACRO; + } + + if (exists $currentConditions{$condition_module} && $condition_object->is_unique_condition()) { + error "[eventMacro] Condition '".$condition->{'key'}."' cannot be used more than once in an automacro. It was used twice (or more) in automacro '".$name."'\n"; + warning "[eventMacro] Ignoring automacro '$name' (multiple unique condition)\n"; + next AUTOMACRO; + } + + if ($condition_object->condition_type == EVENT_TYPE) { + if ($has_event_type_condition) { + error "[eventMacro] Conditions '".$condition->{'key'}."' and '".$event_type_condition_name."' are of the event type and can only be used once per automacro.\n"; + warning "[eventMacro] Ignoring automacro '$name' (multiple event type conditions)\n"; + next AUTOMACRO; + } else { + $has_event_type_condition = 1; + $event_type_condition_name = $condition->{'key'}; + } + } + + push( @{ $currentConditions{$condition_module} }, $condition->{'value'} ); + + } + + #################################### + #####Automacro Object Creation + #################################### + $currentAutomacro = new eventMacro::Automacro($name, \%currentParameters); + my $new_index = $self->{Automacro_List}->add($currentAutomacro); + $self->{Automacro_List}->get($new_index)->parse_and_create_conditions(\%currentConditions); + } +} + +sub load_condition_module { + my ($self, $condition_module) = @_; + undef $@; + debug "[eventMacro] Loading module '".$condition_module."'\n", "eventMacro", 2; + eval "use $condition_module"; + if ($@ =~ /^Can't locate /s) { + FileNotFoundException->throw("Cannot locate automacro module ".$condition_module."."); + } elsif ($@) { + ModuleLoadException->throw("An error occured while loading condition module ".$condition_module.":".$@."."); + } else { + $self->{Condition_Modules_Loaded}{$condition_module} = 1; + } +} + +sub define_automacro_check_state { + my ($self) = @_; + foreach my $automacro (@{$self->{Automacro_List}->getItems()}) { + my $automacro_index = $automacro->get_index; + my $parameter = $automacro->{check_on_ai_state}; + $self->{automacros_index_to_AI_check_state}{$automacro_index}{AI::OFF} = exists $parameter->{'off'} ? 1 : 0; + $self->{automacros_index_to_AI_check_state}{$automacro_index}{AI::MANUAL} = exists $parameter->{'manual'} ? 1 : 0; + $self->{automacros_index_to_AI_check_state}{$automacro_index}{AI::AUTO} = exists $parameter->{'auto'} ? 1 : 0; + } +} + +sub create_callbacks { + my ($self) = @_; + + debug "[eventMacro] create_callback called\n", "eventMacro", 2; + + AUTO: foreach my $automacro (@{$self->{Automacro_List}->getItems()}) { + + debug "[eventMacro] Creating callback for automacro '".$automacro->get_name()."'\n", "eventMacro", 2; + + my $automacro_index = $automacro->get_index; + + # Hooks + foreach my $hook_name ( keys %{ $automacro->get_hooks() } ) { + my $conditions_indexes = $automacro->{hooks}->{$hook_name}; + foreach my $condition_index (@{$conditions_indexes}) { + $self->{Event_Related_Hooks}{$hook_name}{$automacro_index}{$condition_index} = 1; + } + + } + + # Scalars + foreach my $var ( keys %{ $automacro->get_scalar_variables } ) { + my $conditions_indexes = $automacro->{scalar_variables}->{$var}; + foreach my $condition_index (@{$conditions_indexes}) { + $self->{Event_Related_Static_Variables}{scalar}{$var}{$automacro_index}{$condition_index} = 1; + } + } + + # Arrays + foreach my $var ( keys %{ $automacro->get_array_variables } ) { + my $conditions_indexes = $automacro->{array_variables}->{$var}; + foreach my $condition_index (@{$conditions_indexes}) { + $self->{Event_Related_Static_Variables}{array}{$var}{$automacro_index}{$condition_index} = 1; + } + } + + # Hashes + foreach my $var ( keys %{ $automacro->get_hash_variables } ) { + my $conditions_indexes = $automacro->{hash_variables}->{$var}; + foreach my $condition_index (@{$conditions_indexes}) { + $self->{Event_Related_Static_Variables}{hash}{$var}{$automacro_index}{$condition_index} = 1; + } + } + + # Accessed arrays + foreach my $var ( keys %{ $automacro->get_accessed_array_variables } ) { + + my $array = $automacro->{accessed_array_variables}->{$var}; + + foreach my $array_complement (keys %{$array}) { + my $cond_indexes = $array->{$array_complement}; + + next unless (defined $cond_indexes); + + if ($array_complement =~ /^\d+$/) { + foreach my $condition_index (@{$cond_indexes}) { + $self->{Event_Related_Static_Variables}{accessed_array}{$var}{$array_complement}{$automacro_index}{$condition_index} = 1; + } + + } elsif (my $complement_var = find_variable($array_complement)) { + my @nested_array; + push(@nested_array, {type => 'accessed_array', name => $var, complement => $array_complement}); + + while ($complement_var) { + if (exists $complement_var->{complement}) { + $nested_array[-1]{complement_is_var} = 1; + push(@nested_array, {type => $complement_var->{type}, name => $complement_var->{real_name}, complement => $complement_var->{complement}}); + $complement_var = find_variable($complement_var->{complement}); + } else { + $nested_array[-1]{complement_is_var} = 1; + push(@nested_array, {type => $complement_var->{type}, name => $complement_var->{real_name}}); + last; + } + } + + $self->manage_nested_automacro_var(\@nested_array, $automacro_index, $cond_indexes); + + } else { + error "[eventMacro] '".$array_complement."' is not a valid array index in array '".$var."'. Ignoring automacro '".$automacro->get_name()."'.\n"; + next AUTO; + } + } + } + + # Accessed hashes + foreach my $var ( keys %{ $automacro->get_accessed_hash_variables } ) { + my $hash = $automacro->{accessed_hash_variables}->{$var}; + foreach my $hash_complement (keys %{$hash}) { + my $cond_indexes = $hash->{$hash_complement}; + + next unless (defined $cond_indexes); + + if ($hash_complement =~ /^[a-zA-Z\d_]+$/) { + foreach my $condition_index (@{$cond_indexes}) { + $self->{Event_Related_Static_Variables}{accessed_hash}{$var}{$hash_complement}{$automacro_index}{$condition_index} = 1; + } + + } elsif (my $complement_var = find_variable($hash_complement)) { + my @nested_array; + push(@nested_array, {type => 'accessed_hash', name => $var, complement => $hash_complement}); + + while ($complement_var) { + if (exists $complement_var->{complement}) { + $nested_array[-1]{complement_is_var} = 1; + push(@nested_array, {type => $complement_var->{type}, name => $complement_var->{real_name}, complement => $complement_var->{complement}}); + $complement_var = find_variable($complement_var->{complement}); + } else { + $nested_array[-1]{complement_is_var} = 1; + push(@nested_array, {type => $complement_var->{type}, name => $complement_var->{real_name}}); + last; + } + } + + $self->manage_nested_automacro_var(\@nested_array, $automacro_index, $cond_indexes); + + } else { + error "[eventMacro] '".$hash_complement."' is not a valid hash key in hash '".$var."'. Ignoring automacro '".$automacro->get_name()."'.\n"; + next AUTO; + } + } + } + + } + my $event_sub = sub { + my $name = shift; + my $args = shift; + my $check_list_hash = $self->{Event_Related_Hooks}{$name}; + $self->manage_event_callbacks('hook', $name, $args, $check_list_hash); + }; + foreach my $hook_name (keys %{$self->{Event_Related_Hooks}}) { + if ($hook_name eq 'log') { + $self->{Log_Hook_Handles}{$hook_name} = Log::addHook( $self->get_log_hook_sub ); + } else { + $self->{Hook_Handles}{$hook_name} = Plugins::addHook( $hook_name, $event_sub, undef ); + } + } +} + +sub manage_nested_automacro_var { + my ($self, $array, $automacro_index, $cond_indexes) = @_; + + foreach my $nest_index (0..$#{$array}) { + my $variable = $array->[$nest_index]; + + if (exists $variable->{complement}) { + $self->{Dynamic_Variable_Complements}{$variable->{type}}{$variable->{name}}{$variable->{complement}}{defined} = 0; + $self->{Dynamic_Variable_Complements}{$variable->{type}}{$variable->{name}}{$variable->{complement}}{full_nest} = '$'.$variable->{name}.($variable->{type} eq 'accessed_array' ? '[' : '{').$variable->{complement}.($variable->{type} eq 'accessed_array' ? ']' : '}'); + + if ($nest_index == 0) { + $self->{Dynamic_Variable_Complements}{$variable->{type}}{$variable->{name}}{$variable->{complement}}{last_nested} = 1; + foreach my $condition_index (@{$cond_indexes}) { + $self->{Dynamic_Variable_Complements}{$variable->{type}}{$variable->{name}}{$variable->{complement}}{auto_indexes}{$automacro_index}{$condition_index} = 1; + } + } else { + $self->{Dynamic_Variable_Complements}{$variable->{type}}{$variable->{name}}{$variable->{complement}}{last_nested} = 0; + my $next_var = $array->[$nest_index-1]; + push(@{$self->{Dynamic_Variable_Complements}{$variable->{type}}{$variable->{name}}{$variable->{complement}}{call_to}}, {type => $next_var->{type}, name => $next_var->{name}, complement => $next_var->{complement}}); + } + + if (!exists $variable->{complement_is_var}) { + $self->{Dynamic_Variable_Sub_Callbacks}{$variable->{type}}{$variable->{name}}{$variable->{complement}} = 1; + } + + } else { + $self->{Dynamic_Variable_Complements}{$variable->{type}}{$variable->{name}}{last_nested} = 0; + $self->{Dynamic_Variable_Complements}{$variable->{type}}{$variable->{name}}{defined} = 0; + $self->{Dynamic_Variable_Complements}{$variable->{type}}{$variable->{name}}{full_nest} = '$'.$variable->{name}; + my $next_var = $array->[$nest_index-1]; + push(@{$self->{Dynamic_Variable_Complements}{$variable->{type}}{$variable->{name}}{call_to}}, {type => $next_var->{type}, name => $next_var->{name}, complement => $next_var->{complement}}); + + $self->{Dynamic_Variable_Sub_Callbacks}{$variable->{type}}{$variable->{name}} = 1; + } + } +} + +sub sub_callback_variable_event { + my ($self, $variable_type, $variable_name, $before_value, $value, $complement) = @_; + return unless (exists $self->{Dynamic_Variable_Sub_Callbacks}{$variable_type}); + return unless (exists $self->{Dynamic_Variable_Sub_Callbacks}{$variable_type}{$variable_name}); + + my $dynamic_complements; + if (defined $complement) { + return unless (exists $self->{Dynamic_Variable_Sub_Callbacks}{$variable_type}{$variable_name}{$complement}); + + foreach my $sub_complement (values %{$self->{Dynamic_Variable_Sub_Callbacks}{$variable_type}{$variable_name}{$complement}}) { + $dynamic_complements = $self->{Dynamic_Variable_Complements}{$variable_type}{$variable_name}{$sub_complement}; + my $pre_defined = $dynamic_complements->{defined}; + if (defined $value) { + if ($pre_defined) { + $self->change_sub_callback($variable_type, $variable_name, $before_value, $value, $complement, $sub_complement); + } else { + $self->activated_sub_callback($variable_type, $variable_name, $value, $complement, $sub_complement); + } + } else { + if ($pre_defined) { + $self->deactivated_sub_callback($variable_type, $variable_name, $before_value, $complement, $sub_complement); + } + } + } + + } else { + $dynamic_complements = $self->{Dynamic_Variable_Complements}{$variable_type}{$variable_name}; + my $pre_defined = $dynamic_complements->{defined}; + if (defined $value) { + if ($pre_defined) { + $self->change_sub_callback($variable_type, $variable_name, $before_value, $value); + } else { + $self->activated_sub_callback($variable_type, $variable_name, $value); + } + } else { + if ($pre_defined) { + $self->deactivated_sub_callback($variable_type, $variable_name, $before_value); + } + } + } +} + +sub change_sub_callback { + my ($self, $variable_type, $variable_name, $before_value, $value, $complement, $nest_complement) = @_; + + $self->deactivated_sub_callback($variable_type, $variable_name, $before_value, $complement, $nest_complement); + $self->activated_sub_callback($variable_type, $variable_name, $value, $complement, $nest_complement); +} + +sub activated_sub_callback { + my ($self, $variable_type, $variable_name, $value, $complement, $nest_complement) = @_; + + my $var_hash = $self->{Dynamic_Variable_Complements}{$variable_type}{$variable_name}; + if (defined $complement) { + $var_hash = $var_hash->{$nest_complement}; + } + + $var_hash->{defined} = 1; + $var_hash->{value} = $value; + my $calls = $var_hash->{call_to}; + + if ($var_hash->{last_nested}) { + my @auto_indexes = keys %{$var_hash->{auto_indexes}}; + foreach my $automacro_index (@auto_indexes) { + my @cond_indexes = keys %{$var_hash->{auto_indexes}{$automacro_index}}; + foreach my $condition_index (@cond_indexes) { + $self->{Event_Related_Dynamic_Variables}{$variable_type}{$variable_name}{$complement}{$nest_complement}{$automacro_index}{$condition_index} = 1; + } + } + $self->manage_event_callbacks('variable', $variable_name, $value, $self->{Event_Related_Dynamic_Variables}{$variable_type}{$variable_name}{$complement}{$nest_complement}, $variable_type, $nest_complement); + return; + } + + foreach my $call (@{$calls}) { + + if ($call->{type} eq 'accessed_array') { + next if ($value !~ /^\d+$/); + } elsif ($call->{type} eq 'accessed_hash') { + next if ($value !~ /^[a-zA-Z\d_]+$/); + } + + my $call_complements = $self->{Dynamic_Variable_Complements}{$call->{type}}{$call->{name}}{$call->{complement}}; + $call_complements->{complement_defined} = 1; + + my $sub_callback_index = ((scalar keys %{$self->{Dynamic_Variable_Sub_Callbacks}{$call->{type}}{$call->{name}}{$value}}) + 1); + $call->{sub_callback_index} = $sub_callback_index; + $self->{Dynamic_Variable_Sub_Callbacks}{$call->{type}}{$call->{name}}{$value}{$sub_callback_index} = $call->{complement}; + + if ($self->defined_var($call->{type}, $call->{name}, $value)) { + my $new_value = $self->get_var($call->{type}, $call->{name}, $value); + $self->activated_sub_callback($call->{type}, $call->{name}, $new_value, $value, $var_hash->{full_nest}); + } + } +} + +use Data::Dumper; + +sub deactivated_sub_callback { + my ($self, $variable_type, $variable_name, $before_value, $complement, $nest_complement) = @_; + + my $var_hash = $self->{Dynamic_Variable_Complements}{$variable_type}{$variable_name}; + if (defined $complement) { + $var_hash = $var_hash->{$nest_complement}; + } + + $var_hash->{defined} = 0; + delete $var_hash->{value}; + my $calls = $var_hash->{call_to}; + + if ($var_hash->{last_nested}) { + my @auto_indexes = keys %{$var_hash->{auto_indexes}}; + foreach my $automacro_index (@auto_indexes) { + my @cond_indexes = keys %{$var_hash->{auto_indexes}{$automacro_index}}; + foreach my $condition_index (@cond_indexes) { + delete $self->{Event_Related_Dynamic_Variables}{$variable_type}{$variable_name}{$complement}{$nest_complement}{$automacro_index}{$condition_index}; + } + unless (scalar keys %{$self->{Event_Related_Dynamic_Variables}{$variable_type}{$variable_name}{$complement}{$nest_complement}{$automacro_index}}) { + delete $self->{Event_Related_Dynamic_Variables}{$variable_type}{$variable_name}{$complement}{$nest_complement}{$automacro_index}; + } + } + unless (scalar keys %{$self->{Event_Related_Dynamic_Variables}{$variable_type}{$variable_name}{$complement}{$nest_complement}}) { + delete $self->{Event_Related_Dynamic_Variables}{$variable_type}{$variable_name}{$complement}{$nest_complement}; + unless (scalar keys %{$self->{Event_Related_Dynamic_Variables}{$variable_type}{$variable_name}{$complement}}) { + delete $self->{Event_Related_Dynamic_Variables}{$variable_type}{$variable_name}{$complement}; + unless (scalar keys %{$self->{Event_Related_Dynamic_Variables}{$variable_type}{$variable_name}}) { + delete $self->{Event_Related_Dynamic_Variables}{$variable_type}{$variable_name}; + unless (scalar keys %{$self->{Event_Related_Dynamic_Variables}{$variable_type}}) { + delete $self->{Event_Related_Dynamic_Variables}{$variable_type}; + } + } + } + } + return; + } + + foreach my $call (@{$calls}) { + my $subcall_index = delete $call->{sub_callback_index}; + + my $call_complements = $self->{Dynamic_Variable_Complements}{$call->{type}}{$call->{name}}{$call->{complement}}; + delete $call_complements->{complement_defined}; + + my $sub_callbacks = $self->{Dynamic_Variable_Sub_Callbacks}; + + delete $sub_callbacks->{$call->{type}}{$call->{name}}{$before_value}{$subcall_index}; + unless (scalar keys %{$sub_callbacks->{$call->{type}}{$call->{name}}{$before_value}}) { + delete $sub_callbacks->{$call->{type}}{$call->{name}}{$before_value}; + unless (scalar keys %{$sub_callbacks->{$call->{type}}{$call->{name}}}) { + delete $sub_callbacks->{$call->{type}}{$call->{name}}; + unless (scalar keys %{$sub_callbacks->{$call->{type}}}) { + delete $sub_callbacks->{$call->{type}}; + } + } + } + + if ($call_complements->{defined}) { + $self->deactivated_sub_callback($call->{type}, $call->{name}, $call_complements->{value}, $before_value, $var_hash->{full_nest}); + } + } +} + +sub set_arrays_size_to_zero { + my ($self) = @_; + foreach my $array_name (keys %{$self->{Event_Related_Static_Variables}{array}}) { + $self->array_size_change($array_name); + } +} + +sub set_hashes_size_to_zero { + my ($self) = @_; + foreach my $hash_name (keys %{$self->{Event_Related_Static_Variables}{hash}}) { + $self->hash_size_change($hash_name); + } +} + +sub check_all_conditions { + my ($self) = @_; + debug "[eventMacro] Starting to check all state type conditions\n", "eventMacro", 2; + my @automacros = @{ $self->{Automacro_List}->getItems() }; + foreach my $automacro (@automacros) { + debug "[eventMacro] Checking all state type conditions in automacro '".$automacro->get_name."'\n", "eventMacro", 2; + my @conditions = @{ $automacro->{conditionList}->getItems() }; + foreach my $condition (@conditions) { + next if ($condition->condition_type == EVENT_TYPE); + debug "[eventMacro] Checking condition of index '".$condition->get_index."' in automacro '".$automacro->get_name."'\n", "eventMacro", 2; + $automacro->check_state_type_condition($condition->get_index, 'recheck') + } + if (exists $self->{Currently_AI_state_Adapted_Automacros}{$automacro->get_index} && $automacro->can_be_added_to_queue) { + $self->add_to_triggered_prioritized_automacros_index_list($automacro); + } + } +} + +# Given the results of Utilities::find_variable(), return the variable data. +sub get_split_var { + my ( $self, $var ) = @_; + $self->get_var( $var->{type}, $var->{real_name}, $var->{complement} ); +} + +# Generic variable functions +sub get_var { + my ($self, $type, $variable_name, $complement) = @_; + + if ($type eq 'scalar') { + return ($self->get_scalar_var($variable_name)); + + } elsif ($type eq 'accessed_array') { + return ($self->get_array_var($variable_name, $complement)); + + } elsif ($type eq 'accessed_hash') { + return ($self->get_hash_var($variable_name, $complement)); + + } else { + error "[eventMacro] You can't call get_var with a variable type other than 'scalar', 'accessed_array' or 'accessed_hash'.\n"; + return undef; + } +} + +sub set_var { + my ($self, $type, $variable_name, $variable_value, $check_callbacks, $complement) = @_; + + if ($type eq 'scalar') { + return ($self->set_scalar_var($variable_name, $variable_value, $check_callbacks)); + + } elsif ($type eq 'accessed_array') { + return ($self->set_array_var($variable_name, $complement, $variable_value, $check_callbacks)); + + } elsif ($type eq 'accessed_hash') { + return ($self->set_hash_var($variable_name, $complement, $variable_value, $check_callbacks)); + + } else { + error "[eventMacro] You can't call set_var with a variable type other than 'scalar', 'accessed_array' or 'accessed_hash'.\n"; + return undef; + } +} + +sub defined_var { + my ($self, $type, $variable_name, $complement) = @_; + + if ($type eq 'scalar') { + return ($self->is_scalar_var_defined($variable_name)); + + } elsif ($type eq 'accessed_array') { + return ($self->is_array_var_defined($variable_name, $complement)); + + } elsif ($type eq 'accessed_hash') { + return ($self->is_hash_var_defined($variable_name, $complement)); + + } else { + error "[eventMacro] You can't call defined_var with a variable type other than 'scalar', 'accessed_array' or 'accessed_hash'.\n"; + return undef; + } +} + +# Scalars +sub get_scalar_var { + my ($self, $variable_name) = @_; + + # Handle special variables. + if ( substr( $variable_name, 0, 1 ) eq '.' ) { + + # Time-related variables. + if ( $variable_name eq '.time' ) { return time; } + elsif ( $variable_name eq '.datetime' ) { return scalar localtime; } + elsif ( $variable_name eq '.second' ) { return ( localtime() )[0]; } + elsif ( $variable_name eq '.minute' ) { return ( localtime() )[1]; } + elsif ( $variable_name eq '.hour' ) { return ( localtime() )[2]; } + elsif ( $variable_name eq '.dayofmonth' ) { return ( localtime() )[3]; } + elsif ( $variable_name eq '.dayofweek' ) { + my @wday = qw/Monday Tuesday Wednesday Thursday Friday Saturday Sunday+/; + return $wday[ (localtime())[6] - 1 ]; + } + + # Field-related variables. + elsif ( $variable_name eq '.map' ) { return $field ? $field->baseName : ''; } + elsif ( $variable_name eq '.incity' ) { return $field && $field->isCity ? 1 : 0; } + elsif ( $variable_name eq '.inlockmap') { return $field && $field->baseName eq $config{lockMap} ? 1 : 0; } + + # Character-related variables. + elsif ( $variable_name eq '.job' ) { return $char && $jobs_lut{ $char->{jobID} } || ''; } + elsif ( $variable_name eq '.pos' ) { return $char ? sprintf( '%d %d', @{ calcPosition( $char ) }{ 'x', 'y' } ) : ''; } + elsif ( $variable_name eq '.name' ) { return $char && $char->{name} || 0; } + elsif ( $variable_name eq '.hp' ) { return $char && $char->{hp} || 0; } + elsif ( $variable_name eq '.sp' ) { return $char && $char->{sp} || 0; } + elsif ( $variable_name eq '.lvl' ) { return $char && $char->{lv} || 0; } + elsif ( $variable_name eq '.joblvl' ) { return $char && $char->{lv_job} || 0; } + elsif ( $variable_name eq '.spirits' ) { return $char && $char->{spirits} || 0; } + elsif ( $variable_name eq '.zeny' ) { return $char && $char->{zeny} || 0; } + elsif ( $variable_name eq '.weight' ) { return $char && $char->{weight} || 0; } + elsif ( $variable_name eq '.weightpercent') {return $char && int $char->{weight} * 100 / int $char->{weight_max} || 0; } + elsif ( $variable_name eq '.maxweight' ) { return $char && $char->{weight_max} || 0; } + elsif ( $variable_name eq '.status' ) { + return '' if !$char; + return join ',', sort( ( $char->{muted} ? 'muted' : () ), ( $char->{dead} ? 'dead' : () ), map { $statusName{$_} || $_ } keys %{ $char->{statuses} } ); + } + elsif ( $variable_name eq '.statushandle') { + return '' if !$char; + return join ',', keys %{ $char->{statuses} } ; + } + + # Inventory-related variables. + elsif( $variable_name eq '.inventoryitems' ) { return $char && $char->inventory->isReady ? $char->inventory->size : 0; } + + # Cart-related variables. + elsif ( $variable_name eq '.cartweight' ) { return $char && $char->cart->isReady ? $char->cart->{weight} : 0; } + elsif ( $variable_name eq '.cartweightpercent') { return $char && $char->cart->isReady ? int $char->cart->{weight} * 100 / int $char->cart->{weight_max} : 0;} + elsif ( $variable_name eq '.cartmaxweight' ) { return $char && $char->cart->isReady ? $char->cart->{weight_max} : 0; } + elsif ( $variable_name eq '.cartitems' ) { return $char && $char->cart->isReady ? $char->cart->items : 0; } + elsif ( $variable_name eq '.cartmaxitems' ) { return $char && $char->cart->isReady ? $char->cart->items_max : 0; } + elsif ( $variable_name eq '.shopopen' ) { return $char && $shopstarted ? 1 : 0} + + # Storage-related variables. + elsif ( $variable_name eq '.storageopen' ) { return $char && $char->storage->isReady ? 1 : 0; } + elsif ( $variable_name eq '.storageitems' ) { return $char && $char->storage->wasOpenedThisSession ? $char->storage->items : 0; } + elsif ( $variable_name eq '.storagemaxitems' ) { return $char && $char->storage->wasOpenedThisSession ? $char->storage->items_max : 0; } + } + + return $self->{Scalar_Variable_List_Hash}{$variable_name} if (exists $self->{Scalar_Variable_List_Hash}{$variable_name}); + return undef; +} + +sub set_scalar_var { + my ($self, $variable_name, $variable_value, $check_callbacks) = @_; + + my $before_value = $self->get_scalar_var($variable_name); + + if ($variable_value eq 'undef') { + undef $variable_value; + delete $self->{Scalar_Variable_List_Hash}{$variable_name}; + } else { + $self->{Scalar_Variable_List_Hash}{$variable_name} = $variable_value; + } + + return if (defined $check_callbacks && $check_callbacks == 0); + $self->manage_variables_callbacks('scalar', $variable_name, $before_value, $variable_value); +} + +sub is_scalar_var_defined { + my ($self, $variable_name) = @_; + return ((exists $self->{Scalar_Variable_List_Hash}{$variable_name} && defined $self->{Scalar_Variable_List_Hash}{$variable_name}) ? 1 : 0); +} +######### + +# Arrays +sub set_full_array { + my ($self, $variable_name, $list) = @_; + + my @old_array = (exists $self->{Array_Variable_List_Hash}{$variable_name} ? (@{$self->{Array_Variable_List_Hash}{$variable_name}}) : ([])); + my $old_last_index = $#old_array; + my $new_last_index = $#{$list}; + + debug "[eventMacro] Setting array '@".$variable_name."'\n", "eventMacro"; + foreach my $member_index (0..$new_last_index) { + my $member_value = $list->[$member_index]; + if ($member_value eq 'undef') { + undef $member_value; + } + $self->{Array_Variable_List_Hash}{$variable_name}[$member_index] = $member_value; + my $old_member = $old_array[$member_index]; + $self->manage_variables_callbacks('accessed_array', $variable_name, $old_member, $member_value, $member_index); + } + if ($new_last_index < $old_last_index) { + splice(@{$self->{Array_Variable_List_Hash}{$variable_name}}, ($new_last_index+1)); + if ((exists $self->{Event_Related_Static_Variables}{accessed_array} && exists $self->{Event_Related_Static_Variables}{accessed_array}{$variable_name}) || (exists $self->{Event_Related_Dynamic_Variables}{accessed_array} && exists $self->{Event_Related_Dynamic_Variables}{accessed_array}{$variable_name})) { + foreach my $old_member_index (($new_last_index+1)..$old_last_index) { + my $old_member = $old_array[$old_member_index]; + $self->manage_variables_callbacks('accessed_array', $variable_name, $old_member, undef, $old_member_index); + } + } + } + $self->array_size_change($variable_name, $old_last_index) if ($new_last_index != $old_last_index); +} + +sub clear_array { + my ($self, $variable_name) = @_; + if (exists $self->{Array_Variable_List_Hash}{$variable_name}) { + debug "[eventMacro] Clearing array '@".$variable_name."'\n", "eventMacro"; + my @old_array = @{$self->{Array_Variable_List_Hash}{$variable_name}}; + delete $self->{Array_Variable_List_Hash}{$variable_name}; + if ((exists $self->{Event_Related_Static_Variables}{accessed_array} && exists $self->{Event_Related_Static_Variables}{accessed_array}{$variable_name}) || (exists $self->{Event_Related_Dynamic_Variables}{accessed_array} && exists $self->{Event_Related_Dynamic_Variables}{accessed_array}{$variable_name})) { + foreach my $old_member_index (0..$#old_array) { + my $old_member = $old_array[$old_member_index]; + $self->manage_variables_callbacks('accessed_array', $variable_name, $old_member, undef, $old_member_index); + } + } + $self->array_size_change($variable_name, $#old_array); + } +} + +sub push_array { + my ($self, $variable_name, $new_member) = @_; + + if ($new_member eq 'undef') { + undef $new_member; + } + + push(@{$self->{Array_Variable_List_Hash}{$variable_name}}, $new_member); + my $index = $#{$self->{Array_Variable_List_Hash}{$variable_name}}; + + debug "[eventMacro] 'push' was used in array '@".$variable_name."' to add list member '".$new_member."' into position '".$index."'\n", "eventMacro"; + + $self->manage_variables_callbacks('accessed_array', $variable_name, undef, $new_member, $index); + $self->array_size_change($variable_name, ($index - 1)); + + return (scalar @{$self->{Array_Variable_List_Hash}{$variable_name}}); +} + +sub unshift_array { + my ($self, $variable_name, $new_member) = @_; + + if ($new_member eq 'undef') { + undef $new_member; + } + + my @old_array = @{$self->{Array_Variable_List_Hash}{$variable_name}} if $self->{Array_Variable_List_Hash}{$variable_name}; + unshift(@{$self->{Array_Variable_List_Hash}{$variable_name}}, $new_member); + my $index = $#{$self->{Array_Variable_List_Hash}{$variable_name}}; + + debug "[eventMacro] 'unshift' was used in array '@".$variable_name."' to add list member '".$new_member."' into position '0'\n", "eventMacro"; + + foreach my $member_index (0..$index) { + my $member = ${$self->{Array_Variable_List_Hash}{$variable_name}}[$member_index]; + my $old_member = $old_array[$member_index]; + $self->manage_variables_callbacks('accessed_array', $variable_name, $old_member, $member, $member_index); + } + $self->array_size_change($variable_name, ($index - 1)); + + return (scalar @{$self->{Array_Variable_List_Hash}{$variable_name}}); +} + +sub pop_array { + my ($self, $variable_name) = @_; + + return unless (exists $self->{Array_Variable_List_Hash}{$variable_name}); + return unless (scalar @{$self->{Array_Variable_List_Hash}{$variable_name}} > 0); + + my $index = $#{$self->{Array_Variable_List_Hash}{$variable_name}}; + my $poped = pop(@{$self->{Array_Variable_List_Hash}{$variable_name}}); + + debug "[eventMacro] 'pop' was used in array '@".$variable_name."' to remove member '".$poped."' from position '".$index."'\n", "eventMacro"; + + + $self->manage_variables_callbacks('accessed_array', $variable_name, $poped, undef, $index); + $self->array_size_change($variable_name, $index); + + return $poped; +} + +sub shift_array { + my ($self, $variable_name) = @_; + + return unless (exists $self->{Array_Variable_List_Hash}{$variable_name}); + return unless (scalar @{$self->{Array_Variable_List_Hash}{$variable_name}} > 0); + + my $index = $#{$self->{Array_Variable_List_Hash}{$variable_name}}; + my @old_array = @{$self->{Array_Variable_List_Hash}{$variable_name}}; + my $shifted = shift(@{$self->{Array_Variable_List_Hash}{$variable_name}}); + + debug "[eventMacro] 'shift' was used in array '@".$variable_name."' to remove member '".$shifted."' from position '0'\n", "eventMacro"; + + foreach my $member_index (0..$#{$self->{Array_Variable_List_Hash}{$variable_name}}) { + my $member = ${$self->{Array_Variable_List_Hash}{$variable_name}}[$member_index]; + my $old_member = $old_array[$member_index]; + $self->manage_variables_callbacks('accessed_array', $variable_name, $old_member, $member, $member_index); + } + + $self->manage_variables_callbacks('accessed_array', $variable_name, $shifted, undef, $index); + $self->array_size_change($variable_name, $index); + + return $shifted; +} + +sub get_array_var { + my ($self, $variable_name, $index) = @_; + return ($self->{Array_Variable_List_Hash}{$variable_name}[$index]) if (exists $self->{Array_Variable_List_Hash}{$variable_name} && defined $self->{Array_Variable_List_Hash}{$variable_name}[$index]); + return undef; +} + +sub set_array_var { + my ($self, $variable_name, $index, $variable_value, $check_callbacks) = @_; + + my $before_value = $self->get_array_var($variable_name, $index); + my $before_size = $self->get_array_size($variable_name); + + if ($variable_value eq 'undef') { + undef $variable_value; + $self->{Array_Variable_List_Hash}{$variable_name}[$index] = undef; + } else { + $self->{Array_Variable_List_Hash}{$variable_name}[$index] = $variable_value; + } + return if (defined $check_callbacks && $check_callbacks == 0); + $self->manage_variables_callbacks('accessed_array', $variable_name, $before_value, $variable_value, $index); + $self->array_size_change($variable_name, $before_size); +} + +sub array_size_change { + my ($self, $variable_name, $before_size) = @_; + my $size = ((exists $self->{Array_Variable_List_Hash}{$variable_name}) ? (scalar @{$self->{Array_Variable_List_Hash}{$variable_name}}) : 0); + debug "[eventMacro] Size of array '@".$variable_name."' change from '".$before_size."' to '".$size."'\n", "eventMacro"; + + $self->manage_variables_callbacks('array', $variable_name, $before_size, $size); +} + +sub get_array_size { + my ($self, $variable_name) = @_; + if (exists $self->{Array_Variable_List_Hash}{$variable_name}) { + return (scalar @{$self->{Array_Variable_List_Hash}{$variable_name}}); + } + return 0; +} + +sub is_array_var_defined { + my ($self, $variable_name, $index) = @_; + return ((exists $self->{Array_Variable_List_Hash}{$variable_name} && defined $self->{Array_Variable_List_Hash}{$variable_name}[$index]) ? 1 : 0); +} +####### + +# Hahes +sub set_full_hash { + my ($self, $variable_name, $hash) = @_; + + my %old_hash = (exists $self->{Hash_Variable_List_Hash}{$variable_name} ? (%{$self->{Hash_Variable_List_Hash}{$variable_name}}) : ({})); + + debug "[eventMacro] Setting hash '%".$variable_name."'\n", "eventMacro"; + foreach my $member_key (keys %{$hash}) { + my $member_value = $hash->{$member_key}; + if ($member_value eq 'undef') { + undef $member_value; + } + $self->{Hash_Variable_List_Hash}{$variable_name}{$member_key} = $member_value; + my $old_member = $old_hash{$member_key}; + $self->manage_variables_callbacks('accessed_hash', $variable_name, $old_member, $member_value, $member_key); + } + + if ((exists $self->{Event_Related_Static_Variables}{accessed_hash} && exists $self->{Event_Related_Static_Variables}{accessed_hash}{$variable_name}) || (exists $self->{Event_Related_Dynamic_Variables}{accessed_hash} && exists $self->{Event_Related_Dynamic_Variables}{accessed_hash}{$variable_name})) { + foreach my $old_member_key (keys %old_hash) { + if (!exists $self->{Hash_Variable_List_Hash}{$variable_name}{$old_member_key}) { + my $old_member = $old_hash{$old_member_key}; + $self->manage_variables_callbacks('accessed_hash', $variable_name, $old_member, undef, $old_member_key); + } + } + } + $self->hash_size_change($variable_name, (scalar keys %old_hash)) if ((scalar keys %old_hash) != (scalar keys %{$self->{Hash_Variable_List_Hash}{$variable_name}})); +} + +sub clear_hash { + my ($self, $variable_name) = @_; + if (exists $self->{Hash_Variable_List_Hash}{$variable_name}) { + debug "[eventMacro] Clearing hash '%".$variable_name."'\n", "eventMacro"; + my %old_hash = %{$self->{Hash_Variable_List_Hash}{$variable_name}}; + delete $self->{Hash_Variable_List_Hash}{$variable_name}; + if ((exists $self->{Event_Related_Static_Variables}{accessed_hash} && exists $self->{Event_Related_Static_Variables}{accessed_hash}{$variable_name}) || (exists $self->{Event_Related_Dynamic_Variables}{accessed_hash} && exists $self->{Event_Related_Dynamic_Variables}{accessed_hash}{$variable_name})) { + foreach my $old_member_key (keys %old_hash) { + my $old_member = $old_hash{$old_member_key}; + $self->manage_variables_callbacks('accessed_hash', $variable_name, $old_member, undef, $old_member_key); + } + } + $self->hash_size_change($variable_name, (scalar keys %old_hash)); + } +} + +sub get_hash_keys { + my ($self, $variable_name) = @_; + my @keys = (exists $self->{Hash_Variable_List_Hash}{$variable_name} ? (keys %{$self->{Hash_Variable_List_Hash}{$variable_name}}) : ([])); + return \@keys; +} + +sub get_hash_values { + my ($self, $variable_name) = @_; + my @values = (exists $self->{Hash_Variable_List_Hash}{$variable_name} ? (values %{$self->{Hash_Variable_List_Hash}{$variable_name}}) : ([])); + return \@values; +} + +sub exists_hash { + my ($self, $variable_name, $key) = @_; + if (exists $self->{Hash_Variable_List_Hash}{$variable_name} && exists $self->{Hash_Variable_List_Hash}{$variable_name}{$key}) { + return 1; + } + return 0; +} + +sub delete_key { + my ($self, $variable_name, $key) = @_; + if (exists $self->{Hash_Variable_List_Hash}{$variable_name} && exists $self->{Hash_Variable_List_Hash}{$variable_name}{$key}) { + my $old_size = (scalar keys %{$self->{Hash_Variable_List_Hash}{$variable_name}}); + my $deleted = delete $self->{Hash_Variable_List_Hash}{$variable_name}{$key}; + $self->manage_variables_callbacks('accessed_hash', $variable_name, $deleted, undef, $key); + $self->hash_size_change($variable_name, $old_size); + return $deleted; + } +} + +sub get_hash_var { + my ($self, $variable_name, $key) = @_; + return $self->{Hash_Variable_List_Hash}{$variable_name}{$key} if (exists $self->{Hash_Variable_List_Hash}{$variable_name} && exists $self->{Hash_Variable_List_Hash}{$variable_name}{$key}); + return undef; +} + +sub set_hash_var { + my ($self, $variable_name, $key, $variable_value, $check_callbacks) = @_; + + my $before_value = $self->get_hash_var($variable_name, $key); + my $before_size = $self->get_hash_size($variable_name); + + if ($variable_value eq 'undef') { + undef $variable_value; + } + + $self->{Hash_Variable_List_Hash}{$variable_name}{$key} = $variable_value; + + return if (defined $check_callbacks && $check_callbacks == 0); + $self->manage_variables_callbacks('accessed_hash', $variable_name, $before_value, $variable_value, $key); + $self->hash_size_change($variable_name, $before_size); +} + +sub hash_size_change { + my ($self, $variable_name, $old_size) = @_; + my $size = ((exists $self->{Hash_Variable_List_Hash}{$variable_name}) ? (scalar keys %{$self->{Hash_Variable_List_Hash}{$variable_name}}) : 0); + debug "[eventMacro] Size of hash '%".$variable_name."' change from '".$old_size."' to '".$size."'\n", "eventMacro"; + + $self->manage_variables_callbacks('hash', $variable_name, $old_size, $size); +} + +sub get_hash_size { + my ($self, $variable_name) = @_; + if (exists $self->{Hash_Variable_List_Hash}{$variable_name}) { + return (scalar keys %{$self->{Hash_Variable_List_Hash}{$variable_name}}); + } + return 0; +} + +sub is_hash_var_defined { + my ($self, $variable_name, $key) = @_; + return ((exists $self->{Hash_Variable_List_Hash}{$variable_name} && exists $self->{Hash_Variable_List_Hash}{$variable_name}{$key} && defined $self->{Hash_Variable_List_Hash}{$variable_name}{$key}) ? 1 : 0); +} +######## + +sub manage_variables_callbacks { + my ($self, $variable_type, $variable_name, $before_value, $value, $complement) = @_; + + $self->sub_callback_variable_event($variable_type, $variable_name, $before_value, $value, $complement); + + if ($variable_type eq 'scalar') { + + if (exists $self->{Event_Related_Static_Variables}{scalar} && exists $self->{Event_Related_Static_Variables}{scalar}{$variable_name}) { + $self->manage_event_callbacks('variable', $variable_name, $value, $self->{Event_Related_Static_Variables}{scalar}{$variable_name}, $variable_type, $complement); + } + + } elsif ($variable_type eq 'array') { + + if (exists $self->{Event_Related_Static_Variables}{array} && exists $self->{Event_Related_Static_Variables}{array}{$variable_name}) { + $self->manage_event_callbacks('variable', $variable_name, $value, $$self->{Event_Related_Static_Variables}{array}{$variable_name}, $variable_type, $complement); + } + + } elsif ($variable_type eq 'hash') { + + if (exists $self->{Event_Related_Static_Variables}{hash} && exists $self->{Event_Related_Static_Variables}{hash}{$variable_name}) { + $self->manage_event_callbacks('variable', $variable_name, $value, $self->{Event_Related_Static_Variables}{hash}{$variable_name}, $variable_type, $complement); + } + + } elsif ($variable_type eq 'accessed_array') { + + if (exists $self->{Event_Related_Static_Variables}{accessed_array} && exists $self->{Event_Related_Static_Variables}{accessed_array}{$variable_name} && exists $self->{Event_Related_Static_Variables}{accessed_array}{$variable_name}{$complement}) { + $self->manage_event_callbacks('variable', $variable_name, $value, $self->{Event_Related_Static_Variables}{accessed_array}{$variable_name}{$complement}, $variable_type, $complement); + } + if (exists $self->{Event_Related_Dynamic_Variables}{accessed_array} && exists $self->{Event_Related_Dynamic_Variables}{accessed_array}{$variable_name} && exists $self->{Event_Related_Dynamic_Variables}{accessed_array}{$variable_name}{$complement}) { + foreach my $sub_complement (keys %{$self->{Event_Related_Dynamic_Variables}{$variable_type}{$variable_name}{$complement}}) { + $self->manage_event_callbacks('variable', $variable_name, $value, $self->{Event_Related_Dynamic_Variables}{$variable_type}{$variable_name}{$complement}{$sub_complement}, $variable_type, $sub_complement); + } + } + + } elsif ($variable_type eq 'accessed_hash') { + + if (exists $self->{Event_Related_Static_Variables}{accessed_hash} && exists $self->{Event_Related_Static_Variables}{accessed_hash}{$variable_name} && exists $self->{Event_Related_Static_Variables}{accessed_hash}{$variable_name}{$complement}) { + $self->manage_event_callbacks('variable', $variable_name, $value, $self->{Event_Related_Static_Variables}{accessed_hash}{$variable_name}{$complement}, $variable_type, $complement); + } + if (exists $self->{Event_Related_Dynamic_Variables}{accessed_hash} && exists $self->{Event_Related_Dynamic_Variables}{accessed_hash}{$variable_name} && exists $self->{Event_Related_Dynamic_Variables}{accessed_hash}{$variable_name}{$complement}) { + foreach my $sub_complement (keys %{$self->{Event_Related_Dynamic_Variables}{$variable_type}{$variable_name}{$complement}}) { + $self->manage_event_callbacks('variable', $variable_name, $value, $self->{Event_Related_Dynamic_Variables}{$variable_type}{$variable_name}{$complement}{$sub_complement}, $variable_type, $sub_complement); + } + } + } +} + +sub add_to_triggered_prioritized_automacros_index_list { + my ($self, $automacro) = @_; + my $priority = $automacro->get_parameter('priority'); + my $index = $automacro->get_index; + + my $list = $self->{triggered_prioritized_automacros_index_list} ||= []; + + my $index_hash = $self->{automacro_index_to_queue_index}; + + # Find where we should insert this item. + my $new_index; + for ($new_index = 0 ; $new_index < @$list && @$list[$new_index]->{priority} <= $priority ; $new_index++) {} + + # Insert. + splice @$list, $new_index, 0, { index => $index, priority => $priority }; + + # Update indexes. + foreach my $auto_index_in_queue ($new_index .. $#{$list}) { + $index_hash->{$list->[$auto_index_in_queue]->{index}} = $auto_index_in_queue; + } + + $self->{number_of_triggered_automacros}++; + $automacro->running_status(1); + + debug "[eventMacro] Automacro '".$automacro->get_name()."' met it's conditions. Adding it to running queue in position '".$new_index."'.\n", "eventMacro"; + + # Return the insertion index. + return $new_index; +} + +sub remove_from_triggered_prioritized_automacros_index_list { + my ($self, $automacro) = @_; + my $priority = $automacro->get_parameter('priority'); + my $index = $automacro->get_index; + + my $list = $self->{triggered_prioritized_automacros_index_list}; + + my $index_hash = $self->{automacro_index_to_queue_index}; + + # Find from where we should delete this item. + my $queue_index = delete $index_hash->{$index}; + + # remove. + splice (@$list, $queue_index, 1); + + # Update indexes. + foreach my $auto_index_in_queue ($queue_index .. $#{$list}) { + $index_hash->{$list->[$auto_index_in_queue]->{index}} = $auto_index_in_queue; + } + + $self->{number_of_triggered_automacros}--; + $automacro->running_status(0); + + debug "[eventMacro] Automacro '".$automacro->get_name()."' no longer meets it's conditions. Removing it from running queue from position '".$queue_index."'.\n", "eventMacro"; + + # Return the removal index. + return $queue_index; +} + +sub manage_event_callbacks { + my $self = shift; + my $callback_type = shift; + my $callback_name = shift; + my $callback_args = shift; + my $check_list_hash = shift; + + my $debug_message = "[eventMacro] Callback Happenned, type: '".$callback_type."'"; + + $debug_message .= ", name: '".$callback_name."'"; + + if ($callback_type eq 'variable') { + my $sub_type = shift; + my $complement = shift; + $debug_message .= ", variable type: '".$sub_type."'"; + + if ($sub_type eq 'scalar') { + $callback_name = '$'.$callback_name; + + } elsif ($sub_type eq 'array') { + $callback_name = '@'.$callback_name; + + } elsif ($sub_type eq 'accessed_array') { + $callback_name = '$'.$callback_name.'['.$complement.']'; + $debug_message .= ", array index: '".$complement."'"; + + } elsif ($sub_type eq 'hash') { + $callback_name = '%'.$callback_name; + + } elsif ($sub_type eq 'accessed_hash') { + $callback_name = '$'.$callback_name.'{'.$complement.'}'; + $debug_message .= ", hash key: '".$complement."'"; + } + $debug_message .= ", variable value: '".$callback_args."'"; + } + + debug $debug_message."\n", "eventMacro", 2; + + my ($event_type_automacro_call_index, $event_type_automacro_call_priority); + + foreach my $automacro_index (keys %{$check_list_hash}) { + my ($automacro, $conditions_indexes_hash, $check_event_type) = ($self->{Automacro_List}->get($automacro_index), $check_list_hash->{$automacro_index}, 0); + + debug "[eventMacro] Conditions of state type will be checked in automacro '".$automacro->get_name()."'.\n", "eventMacro", 2; + + my @conditions_indexes_array = keys %{ $conditions_indexes_hash }; + + foreach my $condition_index (@conditions_indexes_array) { + my $condition = $automacro->{conditionList}->get($condition_index); + + if ($condition->condition_type == EVENT_TYPE) { + debug "[eventMacro] Skipping condition '".$condition->get_name."' of index '".$condition->get_index."' because it is of the event type.\n", "eventMacro", 3; + $check_event_type = 1; + next; + } else { + debug "[eventMacro] Variable value will be updated in condition of state type in automacro '".$automacro->get_name()."'.\n", "eventMacro", 3 if ($callback_type eq 'variable'); + + my $result = $automacro->check_state_type_condition($condition_index, $callback_type, $callback_name, $callback_args); + + #add to running queue + if (!$result && $automacro->running_status) { + $self->remove_from_triggered_prioritized_automacros_index_list($automacro); + + #remove from running queue + } elsif ($result && exists $self->{Currently_AI_state_Adapted_Automacros}{$automacro_index} && $automacro->can_be_added_to_queue) { + $self->add_to_triggered_prioritized_automacros_index_list($automacro); + + } + } + } + + if ($check_event_type) { + + if ($callback_type eq 'variable') { + debug "[eventMacro] Variable value will be updated in condition of event type in automacro '".$automacro->get_name()."'.\n", "eventMacro", 3; + $automacro->check_event_type_condition($callback_type, $callback_name, $callback_args); + + } elsif (exists $self->{Currently_AI_state_Adapted_Automacros}{$automacro_index} && ($self->get_automacro_checking_status == CHECKING_AUTOMACROS || $self->get_automacro_checking_status == CHECKING_FORCED_BY_USER) && $automacro->can_be_run_from_event) { + debug "[eventMacro] Condition of event type will be checked in automacro '".$automacro->get_name()."'.\n", "eventMacro", 3; + + if ($automacro->check_event_type_condition($callback_type, $callback_name, $callback_args)) { + debug "[eventMacro] Condition of event type was fulfilled.\n", "eventMacro", 3; + + if (!defined $event_type_automacro_call_priority) { + debug "[eventMacro] Automacro '".$automacro->get_name."' of priority '".$automacro->get_parameter('priority')."' was added to the top of queue.\n", "eventMacro", 3; + $event_type_automacro_call_index = $automacro_index; + $event_type_automacro_call_priority = $automacro->get_parameter('priority'); + + } elsif ($event_type_automacro_call_priority >= $automacro->get_parameter('priority')) { + debug "[eventMacro] Automacro '".$automacro->get_name."' of priority '".$automacro->get_parameter('priority')."' was added to the top of queue and took place of automacro '".$self->{Automacro_List}->get($event_type_automacro_call_index)->get_name."' which has priority '".$event_type_automacro_call_priority."'.\n", "eventMacro", 3; + $event_type_automacro_call_index = $automacro_index; + $event_type_automacro_call_priority = $automacro->get_parameter('priority'); + + } else { + debug "[eventMacro] Automacro '".$automacro->get_name()."' was not added to running queue because there already is a higher priority event only automacro in it (automacro '".$self->{Automacro_List}->get($event_type_automacro_call_index)->get_name."' which has priority '".$event_type_automacro_call_priority."').\n", "eventMacro", 3; + + } + + } else { + debug "[eventMacro] Condition of event type was not fulfilled.\n", "eventMacro", 3; + + } + + } else { + debug "[eventMacro] Condition of event type will not be checked in automacro '".$automacro->get_name()."' because it is not necessary.\n", "eventMacro", 3; + + } + } + } + + if (defined $event_type_automacro_call_index) { + + my %hookArgs; + Plugins::callHook("eventMacro_before_call_check", \%hookArgs); + return if ($hookArgs{return}); + + my $automacro = $self->{Automacro_List}->get($event_type_automacro_call_index); + + message "[eventMacro] Event of type '".$callback_type."', and of name '".$callback_name."' activated automacro '".$automacro->get_name()."', calling macro '".$automacro->get_parameter('call')."'\n", "eventMacro"; + + $self->call_macro($automacro); + } +} + +# For '$add_or_delete' value '0' is for delete and '1' is for add. +sub manage_dynamic_hook_add_and_delete { + my ($self, $hook_name, $automacro_index, $condition_index, $add_or_delete) = @_; + + my $automacro = $self->{Automacro_List}->get($automacro_index); + + my $condition = $automacro->{conditionList}->get($condition_index); + + if ($add_or_delete == 1) { + if (exists $self->{Event_Related_Hooks}{$hook_name}{$automacro_index}{$condition_index}) { + error "[eventMacro] Condition '".$condition->get_name()."', of index '".$condition_index."' on automacro '".$automacro->get_name()."' tried to add hook '".$hook_name."' to callbacks but it already is in it.\n"; + return; + } + + debug "[eventMacro] Condition '".$condition->get_name()."', of index '".$condition_index."' on automacro '".$automacro->get_name()."' added hook '".$hook_name."' to callbacks.\n", "eventMacro", 3; + $self->{Event_Related_Hooks}{$hook_name}{$automacro_index}{$condition_index} = undef; + + unless (exists $self->{Hook_Handles}{$hook_name} || exists $self->{Log_Hook_Handles}{$hook_name}) { + if ($hook_name eq 'log') { + $self->{Log_Hook_Handles}{$hook_name} = Log::addHook( $self->get_log_hook_sub ); + } else { + my $event_sub = sub { + my $name = shift; + my $args = shift; + my $check_list_hash = $self->{Event_Related_Hooks}{$name}; + $self->manage_event_callbacks('hook', $name, $args, $check_list_hash); + }; + $self->{Hook_Handles}{$hook_name} = Plugins::addHook( $hook_name, $event_sub, undef ); + } + } + + } else { + if (!exists $self->{Event_Related_Hooks}{$hook_name}{$automacro_index}{$condition_index}) { + error "[eventMacro] Condition '".$condition->get_name()."', of index '".$condition_index."' on automacro '".$automacro->get_name()."' tried to delete hook '".$hook_name."' from callbacks but it isn't in it.\n"; + return; + } + + debug "[eventMacro] Condition '".$condition->get_name()."', of index '".$condition_index."' on automacro '".$automacro->get_name()."' deleted hook '".$hook_name."' from callbacks.\n", "eventMacro", 3; + delete $self->{Event_Related_Hooks}{$hook_name}{$automacro_index}{$condition_index}; + + unless (scalar keys %{$self->{Event_Related_Hooks}{$hook_name}{$automacro_index}}) { + delete $self->{Event_Related_Hooks}{$hook_name}{$automacro_index}; + unless (scalar keys %{$self->{Event_Related_Hooks}{$hook_name}}) { + delete $self->{Event_Related_Hooks}{$hook_name}; + if ($hook_name eq 'log') { + Log::delHook($self->{Log_Hook_Handles}{$hook_name}); + delete $self->{Log_Hook_Handles}{$hook_name}; + } else { + Plugins::delHook($self->{Hook_Handles}{$hook_name}); + delete $self->{Hook_Handles}{$hook_name}; + } + } + } + } +} + +sub AI_start_checker { + my ($self, $state) = @_; + + foreach my $array_member (@{$self->{triggered_prioritized_automacros_index_list}}) { + + my $automacro = $self->{Automacro_List}->get($array_member->{index}); + + next unless $automacro->is_timed_out; + + if (!$automacro->get_parameter('self_interruptible') && defined $self->{Macro_Runner} && !$self->{Macro_Runner}->self_interruptible && $self->{Macro_Runner}->get_caller_name eq $automacro->get_name()) { + next; + } + + my %hookArgs; + Plugins::callHook("eventMacro_before_call_check", \%hookArgs); + return if ($hookArgs{return}); + + message "[eventMacro] Conditions met for automacro '".$automacro->get_name()."', calling macro '".$automacro->get_parameter('call')."'\n", "system"; + + $self->call_macro($automacro); + + return; + } +} + +sub disable_all_automacros { + my ($self) = @_; + foreach my $automacro (@{$self->{Automacro_List}->getItems()}) { + $self->disable_automacro($automacro); + } +} + +sub enable_all_automacros { + my ($self) = @_; + foreach my $automacro (@{$self->{Automacro_List}->getItems()}) { + $self->enable_automacro($automacro); + } +} + +sub disable_automacro { + my ($self, $automacro) = @_; + $automacro->disable; + if ($automacro->running_status) { + $self->remove_from_triggered_prioritized_automacros_index_list($automacro); + } +} + +sub enable_automacro { + my ($self, $automacro) = @_; + $automacro->enable; + if ($automacro->can_be_added_to_queue) { + $self->add_to_triggered_prioritized_automacros_index_list($automacro); + } +} + +sub call_macro { + my ($self, $automacro) = @_; + if (defined $self->{Macro_Runner}) { + $self->clear_queue(); + } + + if ($automacro->get_parameter('call') =~ /\s+/) { + + #here the macro name and the params are together in get_parameter, time to split + my ($macro_name, @params) = parseArgs($automacro->get_parameter('call')); + + # Update $.param[0] with the values from the call. + $eventMacro->set_full_array( ".param", \@params); + + $automacro->set_call('call', $macro_name); + } + + $automacro->set_timeout_time(time); + if ($automacro->get_parameter('run-once')) { + $self->disable_automacro($automacro); + } + + my $new_variables = $automacro->get_new_macro_variables; + + my @variable_names = keys %{ $new_variables }; + + foreach my $variable_name (@variable_names) { + my $variable_value = $new_variables->{$variable_name}; + $self->set_scalar_var($variable_name, $variable_value, 0); + } + + $self->{Macro_Runner} = new eventMacro::Runner( + $automacro->get_parameter('call'), + $automacro->get_name, + $automacro->get_parameter('repeat'), + $automacro->get_parameter('exclusive') ? 0 : 1, + $automacro->get_parameter('self_interruptible'), + $automacro->get_parameter('overrideAI'), + $automacro->get_parameter('orphan'), + $automacro->get_parameter('delay'), + $automacro->get_parameter('macro_delay'), + 0 + ); + + if (defined $self->{Macro_Runner}) { + my $iterate_macro_sub = sub { $self->iterate_macro(); }; + $self->{AI_start_Macros_Running_Hook_Handle} = Plugins::addHook( 'AI_start', $iterate_macro_sub, undef ); + } else { + error "[eventMacro] unable to create macro queue.\n" + } +} + +# Function responsible for actually running the macro script +sub iterate_macro { + my $self = shift; + + # These two cheks are actually not necessary, but they can prevent future code bugs. + if ( !defined $self->{Macro_Runner} ) { + debug "[eventMacro] For some reason the running macro object got undefined, clearing queue to prevent errors.\n", "eventMacro", 2; + $self->clear_queue(); + return; + } elsif ($self->{Macro_Runner}->finished) { + debug "[eventMacro] For some reason macro '".$self->{Macro_Runner}->get_name()."' finished but 'processCmd' did not clear it, clearing queue to prevent errors.\n", "eventMacro", 2; + $self->clear_queue(); + return; + } + + return if $self->{Macro_Runner}->is_paused(); + + my $macro_timeout = $self->{Macro_Runner}->timeout; + + if (timeOut($macro_timeout) && $self->ai_is_eventMacro) { + do { + last unless ( $self->processCmd( $self->{Macro_Runner}->next ) ); + } while ($self->{Macro_Runner} && !$self->{Macro_Runner}->is_paused() && $self->{Macro_Runner}->macro_block); + } +} + +sub ai_is_eventMacro { + my $self = shift; + return 1 if $self->{Macro_Runner}->last_subcall_overrideAI; + + # now check for orphaned script object + # may happen when messing around with "ai clear" and stuff. + $self->enforce_orphan if (defined $self->{Macro_Runner} && !AI::inQueue('eventMacro')); + + return 1 if (AI::is('eventMacro', 'deal')); + return 1 if (AI::is('NPC') && $char->args->waitingForSteps); + return 0; +} + +sub enforce_orphan { + my $self = shift; + my $method = $self->{Macro_Runner}->last_subcall_orphan; + message "[eventMacro] Running macro '".$self->{Macro_Runner}->last_subcall_name."' got orphaned, its orphan method is '".$method."'.\n"; + + # 'terminate' undefs the whole macro tree and returns "ai is not idle" + if ($method eq 'terminate') { + $self->clear_queue(); + return 0; + + # 'terminate_last_call' undefs only the specific macro call that got orphaned, keeping the rest of the macro call tree. + } elsif ($method eq 'terminate_last_call') { + my $macro = $self->{Macro_Runner}; + if (defined $macro->{subcall}) { + while (defined $macro->{subcall}) { + #cheap way of stopping on the second to last subcall + last if (!defined $macro->{subcall}->{subcall}); + $macro = $macro->{subcall}; + } + $macro->clear_subcall; + } else { + #since there was no subcall we delete all macro tree + $self->clear_queue(); + } + return 0; + + # 'reregister' re-inserts "eventMacro" in ai_queue at the first position + } elsif ($method eq 'reregister') { + my $macro = $self->{Macro_Runner}; + while (defined $macro->{subcall}) { + $macro = $macro->{subcall}; + } + $macro->register; + return 1; + + # 'reregister_safe' waits until AI is idle then re-inserts "eventMacro" + } elsif ($method eq 'reregister_safe') { + if (AI::isIdle || AI::is('deal')) { + my $macro = $self->{Macro_Runner}; + while (defined $macro->{subcall}) { + $macro = $macro->{subcall}; + } + $macro->register; + return 1 + } + return 0; + + } else { + error "[eventMacro] Unknown orphan method '".$method."'. terminating whole macro tree\n", "eventMacro"; + $self->clear_queue(); + return 0; + } +} + +sub processCmd { + my ($self, $command) = @_; + my $macro_name = $self->{Macro_Runner}->last_subcall_name; + if (defined $command) { + if ($command ne '') { + unless (Commands::run($command)) { + my $error_message = sprintf("[eventMacro] %s failed with %s\n", $macro_name, $command); + + error $error_message, "eventMacro"; + $self->clear_queue(); + return; + } + } + if (defined $self->{Macro_Runner} && $self->{Macro_Runner}->finished) { + $self->clear_queue(); + + } elsif (!defined $self->{Macro_Runner}) { + debug "[eventMacro] Macro runner object got undefined during a command.\n", "eventMacro", 2; + return; + + } else { + $self->{Macro_Runner}->ok; + } + } else { + my $macro = $self->{Macro_Runner}; + while (defined $macro->{subcall}) { + $macro = $macro->{subcall}; + } + my $error_message = $macro->error_message; + + error $error_message, "eventMacro"; + $self->clear_queue(); + return; + } + + return 1; +} + +sub clear_queue { + my ($self) = @_; + if ( defined $self->{Macro_Runner} ) { + message "[eventMacro] Macro '".$self->{Macro_Runner}->last_subcall_name."' ended.\n", "system"; + } else { + message "[eventMacro] Undefined macro ended.\n", "system"; + } + + debug "[eventMacro] Clearing queue\n", "eventMacro", 2; + if ( defined $self->{Macro_Runner} && $self->get_automacro_checking_status() == PAUSED_BY_EXCLUSIVE_MACRO ) { + debug "[eventMacro] Uninterruptible macro '".$self->{Macro_Runner}->last_subcall_name."' ended. Automacros will return to being checked.\n", "eventMacro", 2; + $self->set_automacro_checking_status(CHECKING_AUTOMACROS); + } + $self->{Macro_Runner} = undef; + Plugins::delHook($self->{AI_start_Macros_Running_Hook_Handle}) if (defined $self->{AI_start_Macros_Running_Hook_Handle}); + $self->{AI_start_Macros_Running_Hook_Handle} = undef; +} + +sub include { + my ($self, $key, $param) = @_; + + unless ( open( IN, "<:utf8", $self->{file} ) ) { + error "[eventMacro] Could not open eventMacro file for include operation.\n", "eventMacro"; + return; + } + my @lines = <IN>; + close(IN); + + my $on = "\n------on-------\n"; + my $off = "\n------off------\n"; + my $needrewrite = 0; + + foreach my $line (@lines) { + if ($line =~ /^\s*(!include\s.+)$/) { + my $include = $1; + if ($key eq 'list') { + $on .= $include."\n"; + + } elsif ($key eq 'off') { + if ($param eq 'all' || $include =~ /.*$param.*/) { + $line =~ s/!include/#!include/g; + $needrewrite = 1; + my ($file) = $include =~ /!include\s+(.+)$/; + message "[eventMacro] Removed ".$file."\n", 'list'; + } + } + + } elsif ($line =~ /^\s*(#!include\s.+)$/) { + my $include = $1; + if ($key eq 'list') { + $off .= $include."\n"; + + } elsif ($key eq 'on') { + if ($param eq 'all' || $include =~ /.*$param.*/) { + $line =~ s/#!include/!include/g; + $needrewrite = 1; + my ($file) = $include =~ /#!include\s+(.+)$/; + message "[eventMacro] Added ".$file."\n", 'list'; + } + } + } + } + message "$on$off", 'list' if ($key eq 'list'); + + if ($needrewrite) { + open (IN, ">:utf8", $self->{file}); + print IN join ("", @lines); + close(IN); + } +} + + +1; \ No newline at end of file diff --git a/openkore_llm_knowledge/plugins/eventMacro/eventMacro/Data.pm b/openkore_llm_knowledge/plugins/eventMacro/eventMacro/Data.pm new file mode 100644 index 0000000000..931e437f81 --- /dev/null +++ b/openkore_llm_knowledge/plugins/eventMacro/eventMacro/Data.pm @@ -0,0 +1,83 @@ +package eventMacro::Data; + +use strict; + +require Exporter; +our @ISA = qw(Exporter); +our @EXPORT = qw($eventMacro @perl_name $valid_var_characters $general_variable_qr $general_wider_variable_qr $scalar_variable_qr $array_variable_qr $accessed_array_variable_qr $hash_variable_qr $accessed_hash_variable_qr $macro_keywords_character %parameters $macroKeywords CHECKING_AUTOMACROS PAUSED_BY_EXCLUSIVE_MACRO PAUSE_FORCED_BY_USER CHECKING_FORCED_BY_USER STATE_TYPE EVENT_TYPE); + +our $eventMacro; +our @perl_name; + +our $valid_var_characters = qr/\.?[a-zA-Z][a-zA-Z\d_]*/; + +our $general_variable_qr = qr/(?:\$$valid_var_characters(?:\[\d+\]|\{[a-zA-Z\d_]+\})?|\@$valid_var_characters|\%$valid_var_characters)/; + +our $general_wider_variable_qr = qr/(?:\$$valid_var_characters(?:\[.+?\]|\{.+?\})?|\@$valid_var_characters|\%$valid_var_characters)/; + +our $scalar_variable_qr = qr/\$$valid_var_characters/; + +our $array_variable_qr = qr/\@$valid_var_characters/; +our $accessed_array_variable_qr = qr/\$$valid_var_characters\[\d+\]/; + +our $hash_variable_qr = qr/\%$valid_var_characters/; +our $accessed_hash_variable_qr = qr/\$$valid_var_characters\{[a-zA-Z\d_]+\}/; + +our $macro_keywords_character = '&'; + +use constant { + CHECKING_AUTOMACROS => 0, + PAUSED_BY_EXCLUSIVE_MACRO => 1, + PAUSE_FORCED_BY_USER => 2, + CHECKING_FORCED_BY_USER => 3 +}; + +use constant { + STATE_TYPE => 1, + EVENT_TYPE => 2 +}; + +our %parameters = ( + 'timeout' => 1, # setting: re-check timeout + 'delay' => 1, # option: delay before the macro starts + 'run-once' => 1, # option: run automacro only once + 'disabled' => 1, # option: automacro disabled + 'call' => 1, # setting: macro to be called + 'overrideAI' => 1, # option: override AI + 'orphan' => 1, # option: orphan handling + 'macro_delay' => 1, # option: default macro delay + 'priority' => 1, # option: automacro priority + 'exclusive' => 1, # option: is macro interruptible by other automacros + 'self_interruptible' => 1, # option: is macro interruptible by its own caller automacro + 'repeat' => 1, # option: the number of times the called macro will repeat itself + 'CheckOnAI' => 1, # option: on which AI state the automacro will be checked +); + +our $macroKeywords = join '|', qw( + arg listlength + cartamount cart Cart + itemCard itemCardAmount itemOption itemOptAmount + config + defined + eval + exists + delete + invamount inventory Inventory InventoryType + keys + monster + nick + npc + player + push pop unshift shift + random rand + shopamount + split + store storamount + storage Storage + strip + questStatus questInactiveCount questIncompleteCount questCompleteCount + values + vender venderitem venderprice venderamount +); + +1; \ No newline at end of file diff --git a/openkore_llm_knowledge/plugins/eventMacro/eventMacro/FileParser.pm b/openkore_llm_knowledge/plugins/eventMacro/eventMacro/FileParser.pm new file mode 100644 index 0000000000..05f0671b88 --- /dev/null +++ b/openkore_llm_knowledge/plugins/eventMacro/eventMacro/FileParser.pm @@ -0,0 +1,289 @@ +package eventMacro::FileParser; + +use strict; + +require Exporter; +our @ISA = qw(Exporter); +our @EXPORT = qw(parseMacroFile isNewCommandBlock); +our @EKSPORT_OK = qw(isNewCommandBlock); + +use Globals; +use Utils qw/existsInList/; +use Utils::Exceptions; +use List::Util qw(max min sum); +use Log qw(message error warning debug); +use Text::Balanced qw/extract_bracketed/; +use Translation qw( T TF ); + +use eventMacro::Core; +use eventMacro::Data; +use eventMacro::Lists; +use eventMacro::Automacro; +use eventMacro::FileParser; +use eventMacro::Macro; + +my %macro; +my %automacro; + +sub parseMacroFile { + my ( $file, $recursive ) = @_; + unless ( $recursive ) { + undef %macro; + undef %automacro; + undef @perl_name; + } + + my %block; + my $inBlock = 0; + my $macroCountOpenBlock = 0; + my ( $macro_subs, @perl_lines ); + my $reader = Utils::TextReader->new( $file, { debug => 1 } ); + while ( $_ = $reader->readLine ) { + s/\s+#.*$//os; # remove last comments + s/^\s+|\s+$//gos; # trim leading and trailing whitespace + s/ +/ /g; # trim down spaces - very cool for user's string data? + next unless ( $_ ); + if ( !%block && /{$/ ) { + my ( $key, $value ) = $_ =~ /^(.*?)\s+(.*?)\s*{$/; + if ( $key eq 'macro' ) { + %block = ( name => $value, type => "macro" ); + if ( exists $macro{$value} ) { + $macro{$value}{'duplicatedMacro'} = 1; + } else { + $macro{$value} = {}; + } + } elsif ( $key eq 'automacro' ) { + if ( exists $automacro{$value} ) { + + #this is to detect automacros that have same name + $automacro{$value}{'duplicatedAutomacro'} = 1; + } + %block = ( name => $value, type => "automacro" ); + } elsif ( $key eq 'sub' ) { + %block = ( name => $value, type => "sub" ); + } else { + %block = ( type => "bogus" ); + error "$file: ignoring line '$_' in line $. (munch, munch, strange block)\n"; + } + next; + + } elsif ( %block && $block{type} eq "bogus" ) { + if ( $_ eq "}" ) { undef %block } + next; + + } elsif ( %block && $block{type} eq "macro" ) { + if ( $_ eq "}" ) { + if ( $macroCountOpenBlock ) { + push( @{ $macro{ $block{name} }{lines} }, '}' ); + $macroCountOpenBlock--; + } else { + undef %block; + } + } else { + if ( isNewCommandBlock( $_ ) ) { + $macroCountOpenBlock++; + } elsif ( !$macroCountOpenBlock && isNewWrongCommandBlock( $_ ) ) { + error "$file: ignoring '$_' in line $. (munch, munch, not found the open block command)\n"; + next; + } + push( @{ $macro{ $block{name} }{lines} }, $_ ); + } + + next; + + } elsif ( %block && $block{type} eq "automacro" ) { + if ( $_ eq "}" ) { + if ( $block{loadmacro} ) { + if ( $macroCountOpenBlock ) { + push( @{ $macro{ $block{loadmacro_name} }{lines} }, '}' ); + + if ( $macroCountOpenBlock ) { + $macroCountOpenBlock--; + } + } else { + undef $block{loadmacro}; + } + } else { + undef %block; + } + + } elsif ( /call [^{]/ && !$macro{ $block{loadmacro_name} } ) { + my ( $key, $value, $param ) = $_ =~ /^(call)\s+(\S+)(?:\s*(.*))?/; + if ( !defined $key || !defined $value ) { + error "$file: ignoring '$_' in line $. (munch, munch, not a pair)\n"; + next; + } + + #check if macro is being called with params or not + if ( defined $param ) { + $value = join( ' ', $value, $param ); + } + push( @{ $automacro{ $block{name} }{parameters} }, { key => 'call', value => $value } ); + } elsif ( $_ eq "call {" ) { + $block{loadmacro} = 1; + $block{loadmacro_name} = "automacro_" . $block{name} . "_call_block"; + push( @{ $automacro{ $block{name} }{parameters} }, { key => 'call', value => $block{loadmacro_name} } ); + $macro{ $block{loadmacro_name} } = {}; + } elsif ( $block{loadmacro} ) { + if ( isNewCommandBlock( $_ ) ) { + $macroCountOpenBlock++; + } elsif ( !$macroCountOpenBlock && isNewWrongCommandBlock( $_ ) ) { + error "$file: ignoring '$_' in line $. (munch, munch, not found the open block command)\n"; + next; + } + + push( @{ $macro{ $block{loadmacro_name} }{lines} }, $_ ); + } else { + my ( $key, $value ) = $_ =~ /^(.*?)\s+(.*)/; + my $blockName = $block{name}; + + if ( defined $value && $value =~ /\{\s*$/ ) { + my $block = $value . "\n"; + my $depth = 1; + + while ( defined( my $line = $reader->readLine ) ) { + my $in_str = 0; + my $str_char = ''; + my $escaped = 0; + + my $line_depth = $depth; + + for ( my $i = 0 ; $i < length( $line ) ; $i++ ) { + my $c = substr( $line, $i, 1 ); + + if ( $in_str ) { + if ( $c eq '\\' ) { + $escaped = !$escaped; + } elsif ( $c eq $str_char && !$escaped ) { + $in_str = 0; + $str_char = ''; + } else { + $escaped = 0; + } + } else { + if ( $c eq '"' || $c eq "'" ) { + $in_str = 1; + $str_char = $c; + } elsif ( $c eq '#' && !$in_str ) { + last; # comentário + } elsif ( substr( $line, $i, 2 ) eq 'q{' ) { + $line_depth++; + $i++; # pula o { + } elsif ( $c eq '{' ) { + $line_depth++; + } elsif ( $c eq '}' ) { + $line_depth--; + } + } + } + + $depth = $line_depth; + + # If depth is zero, do not include this last line (it contained the closing brace) + last if $depth == 0; + + $block .= "$line\n"; + } + + if ( $depth != 0 ) { + error "[$file] [$blockName] [$key]: unterminated or misbalanced block at line $."; + next; + } + + $value = $block . "}"; + } + + if ( !defined $key || !defined $value ) { + error "$file: ignoring '$_' in line $. (munch, munch, not a pair)\n"; + next; + } + if ( exists $parameters{$key} ) { + push( @{ $automacro{ $block{name} }{parameters} }, { key => $key, value => $value } ); + } else { + push( @{ $automacro{ $block{name} }{conditions} }, { key => $key, value => $value } ); + } + } + + next; + + } elsif ( %block && $block{type} eq "sub" ) { + if ( $_ eq "}" ) { + if ( $inBlock > 0 ) { + push( @perl_lines, $_ ); + $inBlock--; + next; + } + $macro_subs = join( '', @perl_lines ); + sub_execute( $block{name}, $macro_subs ); + push( @perl_name, $block{name} ) unless existsInList( join( ',', @perl_name ), $block{name} ); + undef %block; + undef @perl_lines; + undef $macro_subs; + $inBlock = 0; + } elsif ( $_ =~ /^}.*?{$/ && $inBlock > 0 ) { + push( @perl_lines, $_ ); + } elsif ( $_ =~ /{$/ ) { + $inBlock++; + push( @perl_lines, $_ ); + } elsif ( $_ =~ /^}.*/ && $inBlock > 0 ) { + $inBlock--; + push( @perl_lines, $_ ); + } else { + push( @perl_lines, $_ ); + } + next; + + } + + my ( $key, $value ) = $_ =~ /(?:^(.*?)\s|})+(.*)/; + unless ( defined $key ) { + error "$file: ignoring '$_' in line $. (munch, munch, strange food)\n"; + next; + } + } + + if ( %block ) { + error TF( "%s: unclosed %s block '%s'\n", $file, $block{type}, $block{name} ); + return 0; + } + return { macros => \%macro, automacros => \%automacro, subs => \@perl_name }; +} + +sub sub_execute { + return if $Settings::lockdown; + + my ( $name, $arg ) = @_; + my $run = "sub " . $name . " {" . $arg . "}"; + eval( $run ); # cycle the macro sub between macros only + $run = "eval " . $run; + + # exporting sub to the &main::sub, becarefull on your sub name + # dont name your new sub equal to any &main::sub, you should take + # the risk yourself. + Commands::run( $run ); + + debug "[eventMacro] registering sub '" . $name . "'.\n", "menu"; +} + +# check if on the line there commands that open new command blocks +sub isNewCommandBlock { + my ( $line ) = @_; + + if ( $line =~ /^if.*{$/ || $line =~ /^case.*{$/ || $line =~ /^switch.*{$/ || $line =~ /^else.*{$/ || $line =~ /^while.*{$/ ) { + return 1; + } else { + return 0; + } +} + +sub isNewWrongCommandBlock { + my ( $line ) = @_; + + if ( $_ =~ /^}\s*else\s*{$/ || $_ =~ /}\s*elsif.*{$/ || $_ =~ /^case.*{$/ || $_ =~ /^else*{$/ ) { + return 1; + } else { + return 0; + } +} + +1; diff --git a/openkore_llm_knowledge/plugins/eventMacro/eventMacro/Lists.pm b/openkore_llm_knowledge/plugins/eventMacro/eventMacro/Lists.pm new file mode 100644 index 0000000000..10f1471113 --- /dev/null +++ b/openkore_llm_knowledge/plugins/eventMacro/eventMacro/Lists.pm @@ -0,0 +1,95 @@ +package eventMacro::Lists; + +use strict; +use Carp::Assert; +use Utils::ObjectList; +use base qw(ObjectList); + +sub new { + my ($class) = @_; + my $self = $class->SUPER::new(); + + $self->{nameIndex} = {}; + + return $self; +} + +sub DESTROY { + my ($self) = @_; + $self->clear(); +} + +sub add { + my ($self, $member) = @_; + + my $listIndex = $self->SUPER::add($member); + $member->{listIndex} = $listIndex; + + my $indexSlot = $self->getNameIndexSlot($member->get_name()); + push @{$indexSlot}, $listIndex; + + return $listIndex; +} + +sub getByName { + my ($self, $name) = @_; + my $indexSlot = $self->{nameIndex}{lc($name)}; + if ($indexSlot) { + return $self->get($indexSlot->[0]); + } else { + return undef; + } +} + +sub remove { + my ($self, $member) = @_; + + my $result = $self->SUPER::remove($member); + if ($result) { + my $indexSlot = $self->getNameIndexSlot($member->get_name()); + for (my $i = 0; $i < @{$indexSlot}; $i++) { + if ($indexSlot->[$i] == $member->{listIndex}) { + splice(@{$indexSlot}, $i, 1); + last; + } + } + if (@{$indexSlot} == 0) { + delete $self->{nameIndex}{lc($member->get_name())}; + } + } + return $result; +} + +sub removeByName { + my ($self, $name) = @_; + my $member = $self->getByName($name); + if (defined $member) { + return $self->remove($member); + } else { + return 0; + } +} + +# overloaded +sub doClear { + my ($self) = @_; + $self->SUPER::doClear(); + $self->{nameIndex} = {}; +} + +# overloaded +sub checkValidity { + my ($self) = @_; + $self->SUPER::checkValidity(); + foreach my $k (keys %{$self->{nameIndex}}) { + should(lc($self->getByName($k)->get_name()), $k); + should(lc $k, $k); + } +} + +sub getNameIndexSlot { + my ($self, $name) = @_; + return $self->{nameIndex}{lc($name)} ||= []; +} + +1; diff --git a/openkore_llm_knowledge/plugins/eventMacro/eventMacro/Macro.pm b/openkore_llm_knowledge/plugins/eventMacro/eventMacro/Macro.pm new file mode 100644 index 0000000000..5862a0f6bc --- /dev/null +++ b/openkore_llm_knowledge/plugins/eventMacro/eventMacro/Macro.pm @@ -0,0 +1,25 @@ +package eventMacro::Macro; + +use strict; + +sub new { + my ($class, $name, $lines) = @_; + my $self = bless {}, $class; + + $self->{name} = $name; + $self->{Lines} = $lines; + + return $self; +} + +sub get_lines { + my ($self) = @_; + return $self->{Lines}; +} + +sub get_name { + my ($self) = @_; + return $self->{name}; +} + +1; \ No newline at end of file diff --git a/openkore_llm_knowledge/plugins/eventMacro/eventMacro/Runner.pm b/openkore_llm_knowledge/plugins/eventMacro/eventMacro/Runner.pm new file mode 100644 index 0000000000..b372190bb9 --- /dev/null +++ b/openkore_llm_knowledge/plugins/eventMacro/eventMacro/Runner.pm @@ -0,0 +1,2473 @@ +package eventMacro::Runner; + +use strict; + +require Exporter; +our @ISA = qw(Exporter); + +use Time::HiRes qw( &time ); +use Globals; +use AI; +use Log qw(message error warning debug); +use Text::Balanced qw/extract_bracketed/; +use Utils qw/existsInList parseArgs/; +use List::Util qw(max min sum); + +use eventMacro::Data; +use eventMacro::Core; +use eventMacro::FileParser qw(isNewCommandBlock); +use eventMacro::Utilities qw(cmpr getnpcID getItemIDs getItemPrice getStorageIDs getInventoryIDs getInventoryTypeIDs + getPlayerID getMonsterID getVenderID getRandom getRandomRange getInventoryAmount getCartAmount getShopAmount + getStorageAmount getVendAmount getConfig getWord q4rx q4rx2 getArgFromList get_pattern find_variable get_key_or_index getQuestStatus + find_hash_and_get_keys find_hash_and_get_values getEquipCards getEquipCardAmount getEquipOptions getEquipOptionsAmount); +use eventMacro::Automacro; + +# Creates the object +sub new { + my ($class, $name, $caller_name, $repeat, $interruptible, $self_interruptible, $overrideAI, $orphan, $delay, $macro_delay, $is_submacro) = @_; + + return undef unless ($eventMacro->{Macro_List}->getByName($name)); + + my $self = bless {}, $class; + + $self->{name} = $name; + $self->{caller_name} = $caller_name; + $self->{Paused} = 0; + $self->{registered} = 0; + $self->{finished} = 0; + $self->{macro_block} = 0; + + $self->{lines_array} = $eventMacro->{Macro_List}->getByName($name)->get_lines(); + $self->{line_index} = 0; + + $self->{label} = {scanLabels($self->{lines_array})}; + $self->{block} = scanBlocks($self->{lines_array}); + + $self->{time} = time; + + $self->{current_line} = undef; + $self->{subcall} = undef; + $self->{error} = undef; + $self->{last_subcall_overrideAI} = undef; + $self->{last_subcall_interruptible} = undef; + $self->{last_subcall_orphan} = undef; + + debug "[eventMacro] Macro object '".$self->{name}."' created.\n", "eventMacro", 2; + + if ($is_submacro) { + $self->{submacro} = 1; + $eventMacro->{Macro_Runner}->last_subcall_name($self->get_name); + } else { + $self->{submacro} = 0; + $self->last_subcall_name($self->get_name); + } + + if (defined $repeat && $repeat =~ /^\d+$/) { + $self->repeat($repeat); + } else { + $self->repeat(1); + } + + if (defined $interruptible && $interruptible =~ /^[01]$/) { + $self->interruptible($interruptible); + } else { + $self->interruptible(1); + } + + if (defined $self_interruptible && $self_interruptible =~ /^[01]$/) { + $self->self_interruptible($self_interruptible); + } else { + $self->self_interruptible(0); + } + + if (defined $overrideAI && $overrideAI =~ /^[01]$/) { + $self->overrideAI($overrideAI); + } else { + $self->overrideAI(0); + } + + if (defined $orphan && $orphan =~ /^(?:terminate(?:_last_call)?|reregister(?:_safe)?)$/) { + $self->orphan($orphan); + } else { + $self->orphan($config{eventMacro_orphans}); + } + + if (defined $delay && $delay =~ /^[\d\.]*\d+$/) { + $self->timeout($delay); + } else { + $self->timeout(0); + } + + if (defined $macro_delay && $macro_delay =~ /^[\d\.]*\d+$/) { + $self->macro_delay($macro_delay); + } else { + $self->macro_delay($timeout{eventMacro_delay}{timeout}); + } + + return $self +} + +# Sets/Gets the overrideAI value of the last subcall +sub last_subcall_overrideAI { + my ($self, $overrideAI) = @_; + if (defined $overrideAI) { + $self->{last_subcall_overrideAI} = $overrideAI; + } + return $self->{last_subcall_overrideAI}; +} + +# Sets/Gets the interruptible value of the last subcall +sub last_subcall_interruptible { + my ($self, $interruptible) = @_; + if (defined $interruptible) { + $self->{last_subcall_interruptible} = $interruptible; + } + return $self->{last_subcall_interruptible}; +} + +# Sets/Gets the orphan method of the last subcall +sub last_subcall_orphan { + my ($self, $orphan) = @_; + if (defined $orphan) { + $self->{last_subcall_orphan} = $orphan; + } + return $self->{last_subcall_orphan}; +} + +# Sets/Gets the name of the last subcall +sub last_subcall_name { + my ($self, $name) = @_; + if (defined $name) { + $self->{last_subcall_name} = $name; + } + return $self->{last_subcall_name}; +} + +# Sets/Gets the current interruptible flag +sub interruptible { + my ($self, $interruptible) = @_; + + if (defined $interruptible) { + + if (defined $self->{interruptible} && $self->{interruptible} == $interruptible) { + debug "[eventMacro] Macro '".$self->{name}."' interruptible state is already '".$interruptible."'.\n", "eventMacro", 2; + } else { + debug "[eventMacro] Now macro '".$self->{name}."' interruptible state is '".$interruptible."'.\n", "eventMacro", 2; + $self->{interruptible} = $interruptible; + } + + if (!defined $self->{subcall}) { + debug "[eventMacro] Since this macro is the last in the macro tree we will validate automacro checking to interruptible.\n", "eventMacro", 2; + $self->validate_automacro_checking_to_interruptible($interruptible); + } + + } + return $self->{interruptible}; +} + +# Sets/Gets the current self_interruptible flag +sub self_interruptible { + my ($self, $self_interruptible) = @_; + + if (defined $self_interruptible) { + + if (defined $self->{self_interruptible} && $self->{self_interruptible} == $self_interruptible) { + debug "[eventMacro] Macro '".$self->{name}."' self_interruptible state is already '".$self_interruptible."'.\n", "eventMacro", 2; + } else { + debug "[eventMacro] Now macro '".$self->{name}."' self_interruptible state is '".$self_interruptible."'.\n", "eventMacro", 2; + $self->{self_interruptible} = $self_interruptible; + } + + } + return $self->{self_interruptible}; +} + +# Makes sure the automacro checking state is compatible with this macro interruptible +sub validate_automacro_checking_to_interruptible { + my ($self, $interruptible) = @_; + + my $checking_status = $eventMacro->get_automacro_checking_status(); + + if (!$self->{submacro}) { + $self->last_subcall_interruptible($interruptible); + } else { + $eventMacro->{Macro_Runner}->last_subcall_interruptible($interruptible); + } + + if (($checking_status == CHECKING_AUTOMACROS && $interruptible == 1) || ($checking_status == PAUSED_BY_EXCLUSIVE_MACRO && $interruptible == 0)) { + debug "[eventMacro] No need to change automacro checking status because it already is compatible with this macro.\n", "eventMacro", 2; + return; + } + + if ($checking_status != CHECKING_AUTOMACROS && $checking_status != PAUSED_BY_EXCLUSIVE_MACRO) { + debug "[eventMacro] Macro '".$self->{name}."' cannot change automacro checking state because the user forced it into another state.\n", "eventMacro", 2; + return; + } + + if ($interruptible == 0) { + debug "[eventMacro] Macro '".$self->{name}."' is now stopping automacro checking..\n", "eventMacro", 2; + $eventMacro->set_automacro_checking_status(PAUSED_BY_EXCLUSIVE_MACRO); + + } elsif ($interruptible == 1) { + debug "[eventMacro] Macro '".$self->{name}."' is now starting automacro checking..\n", "eventMacro", 2; + $eventMacro->set_automacro_checking_status(CHECKING_AUTOMACROS); + } +} + +# Sets/Gets the current override AI value +sub overrideAI { + my ($self, $overrideAI) = @_; + + if (defined $overrideAI) { + + if (defined $self->{overrideAI} && $self->{overrideAI} == $overrideAI) { + debug "[eventMacro] Macro '".$self->{name}."' overrideAI state is already '".$overrideAI."'.\n", "eventMacro", 2; + } else { + debug "[eventMacro] Now macro '".$self->{name}."' overrideAI state is '".$overrideAI."'.\n", "eventMacro", 2; + $self->{overrideAI} = $overrideAI; + } + + if (!defined $self->{subcall}) { + debug "[eventMacro] Since this macro is the last in the macro tree we will validate AI queue to overrideAI.\n", "eventMacro", 2; + $self->validate_AI_queue_to_overrideAI($overrideAI); + } + + } + return $self->{overrideAI}; +} + +# Makes sure the AI queue state is compatible with this macro overrideAI +sub validate_AI_queue_to_overrideAI { + my ($self, $overrideAI) = @_; + + my $is_in_AI_queue = AI::inQueue('eventMacro'); + + if (!$self->{submacro}) { + $self->last_subcall_overrideAI($overrideAI); + } else { + $eventMacro->{Macro_Runner}->last_subcall_overrideAI($overrideAI); + } + + if (($is_in_AI_queue && $overrideAI == 0) || (!$is_in_AI_queue && $overrideAI == 1)) { + debug "[eventMacro] No need to add/clear AI_queue because it already is compatible with this macro.\n", "eventMacro", 2; + return; + } + + if ($overrideAI == 0) { + $self->register; + + } elsif ($overrideAI == 1) { + $self->unregister; + } +} + +# Registers to AI queue +sub register { + my ($self) = @_; + debug "[eventMacro] Macro '".$self->{name}."' is now registering itself to AI queue.\n", "eventMacro", 2; + if (AI::is("NPC")) { + splice(@AI::ai_seq, 1, 0, 'eventMacro'); + splice(@AI::ai_seq_args, 1, 0, {}); + } else { + AI::queue('eventMacro'); + } + $self->{registered} = 1; +} + +# Unregisters from AI queue +sub unregister { + my ($self) = @_; + debug "[eventMacro] Macro '".$self->{name}."' is now deleting itself from AI queue.\n", "eventMacro", 2; + AI::clear('eventMacro'); + $self->{registered} = 0; +} + +# Sets/Gets the current orphan method +sub orphan { + my ($self, $orphan) = @_; + + if (defined $orphan) { + + if (defined $self->{orphan} && $self->{orphan} eq $orphan) { + debug "[eventMacro] Macro '".$self->{name}."' orphan method is already '".$orphan."'.\n", "eventMacro", 2; + } else { + debug "[eventMacro] Now macro '".$self->{name}."' orphan method is '".$orphan."'.\n", "eventMacro", 2; + $self->{orphan} = $orphan; + } + + if (!defined $self->{subcall}) { + if (!$self->{submacro}) { + $self->last_subcall_orphan($orphan); + } else { + $eventMacro->{Macro_Runner}->last_subcall_orphan($orphan); + } + } + } + return $self->{orphan}; +} + +# Sets/Gets the current timeout +sub timeout { + my ($self, $timeout) = @_; + if (defined $timeout) { + $self->{timeout} = $timeout; + } + return { time => $self->{time}, timeout => $self->{timeout} }; +} + +# Sets/Gets the current macro delay +sub macro_delay { + my ($self, $macro_delay) = @_; + if (defined $macro_delay) { + $self->{macro_delay} = $macro_delay; + } + return $self->{macro_delay}; +} + +# Returns true if the macro is registered to AI queue +sub registered { + my ($self) = @_; + return $self->{registered}; +} + +# Sets/Gets the current repeat count +sub repeat { + my ($self, $repeat) = @_; + if (defined $repeat) { + debug "[eventMacro] Now macro '".$self->{name}."' will repeat itself '".$repeat."' times.\n", "eventMacro", 2; + $self->{repeat} = $repeat; + } + return $self->{repeat}; +} + +# Pauses the macro +sub pause { + my ($self) = @_; + $self->{Paused} = 1; +} + +# Unpauses the macro +sub unpause { + my ($self) = @_; + $self->{Paused} = 0; +} + +# Returns true if the macro is paused +sub is_paused { + my ($self) = @_; + return $self->{Paused}; +} + +# Returns the macro name +sub get_name { + my ($self) = @_; + return $self->{name}; +} + +# Returns the name of the automacro that called this macro +sub get_caller_name { + my ($self) = @_; + return $self->{caller_name}; +} + +# Deletes the subcall object +sub clear_subcall { + my ($self) = @_; + debug "[eventMacro] Clearing submacro '".$self->{subcall}->{name}."' from macro '".$self->{name}."'.\n", "eventMacro", 2; + $self->validate_automacro_checking_to_interruptible($self->interruptible); + $self->validate_AI_queue_to_overrideAI($self->overrideAI); + #since we do not need a validate_orphan function we do it here + if ($self->{subcall}->orphan ne $self->orphan) { + debug "[eventMacro] Returning orphan method from '".$self->{subcall}->orphan."' to '".$self->orphan."'.\n", "eventMacro", 2; + if (!$self->{submacro}) { + $self->last_subcall_orphan($self->orphan); + } else { + $eventMacro->{Macro_Runner}->last_subcall_orphan($self->orphan); + } + } else { + debug "[eventMacro] No need to change orphan method because it already is compatible with this macro.\n", "eventMacro", 2; + } + if ($self->{submacro}) { + $eventMacro->{Macro_Runner}->last_subcall_name($self->get_name); + } else { + $self->last_subcall_name($self->get_name); + } + undef $self->{subcall}; +} + +# Creates a subcall object +sub create_subcall { + my ($self, $name, $repeat) = @_; + debug "[eventMacro] Creating submacro '".$name."' on macro '".$self->{name}."'.\n", "eventMacro", 2; + $self->{subcall} = new eventMacro::Runner($name, $self->get_caller_name, $repeat, $self->interruptible, $self->self_interruptible, $self->overrideAI, $self->orphan, undef, $self->macro_delay, 1); +} + +# destructor +sub DESTROY { + my ($self) = @_; + $self->unregister if (AI::inQueue('eventMacro') && !$self->{submacro}); +} + +# TODO: Check this +# sets or gets macro block flag +sub macro_block { + my ($self, $macro_block) = @_; + + my $value; + if (defined $macro_block) { + $self->{macro_block} = $macro_block; + $value = $self->{macro_block}; + + } else { + if (defined $self->{subcall}) { + $value = $self->{subcall}->macro_block; + } else { + $value = $self->{macro_block}; + } + } + + return $value; +} + +# TODO: Check this +# returns whether or not the macro finished +sub finished { + my ($self) = @_; + return $self->{finished} +} + +# TODO: Check this +# re-sets the timer +sub ok { + my ($self) = @_; + $self->{time} = time +} + +# TODO: Check this +# Scans the script for labels +sub scanLabels { + my $script = $_[0]; + my %labels; + for (my $line = 0; $line < @{$script}; $line++) { + if (${$script}[$line] =~ /^:/) { + my ($label) = ${$script}[$line] =~ /^:(.*)/; + $labels{$label} = $line + } + } + return %labels +} + +# Scans the script for blocks +sub scanBlocks { + my $script = $_[0]; + my $blocks = {}; + my $block_starts = []; + + for (my $line = 0; $line < @{$script}; $line++) { + if ($script->[$line] =~ /^(if|switch|while|case)\s+\(.*\)\s+{$|^(else)\s+.*{/) { + push @$block_starts, { type => $1 || $2, start => $line }; + } elsif ($script->[$line] eq '}') { + my $block = pop @$block_starts; + $block->{end} = $line; + $blocks->{end_to_start}{$line} = $block; + $blocks->{start_to_end}{$block->{start}} = $block; + } + } + $blocks; +} + +# Decides what to do when we get to the end of a macro script +sub manage_script_end { + my ($self) = @_; + debug "[eventMacro] Macro '".$self->{name}."' got to the end of its script.\n", "eventMacro", 2; + if ($self->{repeat} > 1) { + $self->{repeat}--; + $self->{line_index} = 0; + debug "[eventMacro] Repeating macro '".$self->{name}."'. Remaining repeats: '".$self->{repeat}."'.\n", "eventMacro", 2; + } else { + $self->{finished} = 1; + debug "[eventMacro] Macro '".$self->{name}."' finished.\n", "eventMacro", 2; + } +} + +# Makes sure the subcall is over before continuing with this macro +sub manage_subcall { + my ($self) = @_; + my $subcall_return = $self->{subcall}->next; + if (defined $subcall_return) { + my $subcall_timeout = $self->{subcall}->timeout; + $self->timeout($subcall_timeout->{timeout}); + $self->{time} = $subcall_timeout->{time}; + if ($self->{subcall}->finished) { + $self->clear_subcall; + } + return $subcall_return; + } else { + #if subcall->next returned undef an error was set + $self->error($self->{subcall}->error); + return; + } +} + +# Sets/Gets the current line index +sub line_index { + my ($self, $line_index) = @_; + if (defined $line_index) { + $self->{line_index} = $line_index; + } + return $self->{line_index}; +} + +# Gets the script of the given line +sub line_script { + my ($self, $line_index) = @_; + return @{$self->{lines_array}}[$line_index]; +} + +# Advances a line +sub next_line { + my ($self) = @_; + $self->{line_index}++; +} + +# Sets/Gets the error message +sub error { + my ($self, $error) = @_; + if (defined $error) { + $self->{error} = $error; + } + return $self->{error}; +} + +# Returns an informative error message +sub error_message { + my ($self) = @_; + my $error_message = + "[eventMacro] Error in macro '".$self->{name}."'\n". + "[eventMacro] Line index of the error '".$self->line_index."'\n". + "[eventMacro] Script of the line '".$self->line_script($self->line_index)."'\n"; + + $error_message .= "[eventMacro] Error message '".$self->error."'\n"; + return $error_message; +} + +# Decides the next script to be read +sub define_current_line { + my ($self) = @_; + + #End of script + if ( $self->{line_index} == scalar (@{$self->{lines_array}}) ) { + $self->manage_script_end(); + if ($self->{finished}) { + $self->{current_line} = undef; + return; + } + $self->define_current_line; + + #Normal script + } else { + $self->{current_line} = $self->line_script($self->line_index); + } +} + +# This loop is responsible for getting the next macro command script. +# All 'if', 'else', 'goto', 'while', etc, will checked here. +sub define_next_valid_command { + my ($self) = @_; + + my $check_need = 1; + DEFINE_COMMAND: while () { + + ###################################### + # Get next script line + ###################################### + if ($check_need) { + $self->define_current_line; + return "" if ($self->{finished}); + debug "[eventMacro] Checking macro '".$self->{name}."', line index '".$self->line_index."' for a macro command.\n", "eventMacro", 3; + debug "[eventMacro] Script '".$self->{current_line}."'.\n", "eventMacro", 3; + } else { + debug "[eventMacro] Rechecking macro '".$self->{name}."', line index '".$self->line_index."' for a macro command after it was cleaned.\n", "eventMacro", 3; + debug "[eventMacro] New cleaned script '".$self->{current_line}."'.\n", "eventMacro", 3; + $check_need = 1; + } + + ###################################### + # While statement: while (foo <= bar) { + ###################################### + if ($self->{current_line} =~ /^while\s*\(/) { + my ($condition_text) = $self->{current_line} =~ /^while\s*(\(.*\))\s+{$/; + + debug "[eventMacro] Script is the start of a while 'block'.\n", "eventMacro", 3; + + my ($result) = $self->parse_and_check_condition_text($condition_text); + return if (defined $self->error); + + if ($result == 1) { + debug "[eventMacro] Condition of 'while' is true.\n", "eventMacro", 3; + debug "[eventMacro] Entering true 'while' 'block'.\n", "eventMacro", 3; + } else { + debug "[eventMacro] Condition of 'while' is false.\n", "eventMacro", 3; + debug "[eventMacro] Moving to the end of 'while' loop.\n", "eventMacro", 3; + $self->line_index($self->{block}{start_to_end}{$self->line_index}{end}); + } + $self->next_line; + + ###################################### + # Postfix 'if' + ###################################### + } elsif ($self->{current_line} =~ /.+\s+if\s*\(.*\)$/) { + my ($condition_text) = $self->{current_line} =~ /.+\s+if\s*(\(.*\))$/; + + debug "[eventMacro] Script is a command with a postfixed 'if'.\n", "eventMacro", 3; + + my ($result) = $self->parse_and_check_condition_text($condition_text); + return if (defined $self->error); + if ($result) { + debug "[eventMacro] Condition of 'if' is true, cleaning 'if' and rechecking line.\n", "eventMacro", 3; + $self->{current_line} =~ s/\s+if\s*\(.*\)$//; + $check_need = 0; + next DEFINE_COMMAND; + } else { + debug "[eventMacro] Condition of 'if' is false, ignoring command.\n", "eventMacro", 3; + $self->next_line; + } + + ###################################### + # Initial 'if' + ###################################### + } elsif ($self->{current_line} =~ /^if\s*\(/) { + + debug "[eventMacro] Script is a 'if' condition.\n", "eventMacro", 3; + + my ($result, $post_if) = $self->parse_and_check_condition_text($self->{current_line}); + return if (defined $self->error); + if ($result == 1) { + debug "[eventMacro] Condition of 'if' is true.\n", "eventMacro", 3; + if ($post_if ne "{") { + debug "[eventMacro] Code after the 'if' is a command, cleaning 'if' and rechecking line.\n", "eventMacro", 3; + $self->{current_line} = $post_if; + $check_need = 0; + next DEFINE_COMMAND; + } else { + debug "[eventMacro] Entering true 'if' block.\n", "eventMacro", 3; + } + + } else { + debug "[eventMacro] Condition of 'if' is false.\n", "eventMacro", 3; + if ($post_if eq "{") { + debug "[eventMacro] There's a block after it to be cleaned.\n", "eventMacro", 3; + + my $block_count = 1; + CHECK_IF: while ($block_count > 0) { + + $self->next_line; + $self->define_current_line; + + if ($self->{finished}) { + $self->{finished} = 0; + $self->error("All 'if' blocks must be closed before the end of the macro)"); + return; + } + + #Start of another if/switch/case/while block + if ( $self->{current_line} =~ /^(if|switch|case|while|else).*{$/ ) { + $block_count++; + + #End of an if block or start of else block + } elsif ($self->{current_line} =~ /^}\s*else\s*{$/ && $block_count == 1) { + debug "[eventMacro] Entering true 'else' block after false 'if' block.\n", "eventMacro", 3; + last CHECK_IF; + + #End of an if block or start of else block + } elsif ($self->{current_line} eq '}') { + $block_count--; + + #Elsif check + } elsif ( $self->{current_line} =~ /^}\s*elsif\s*(\(.*\)).*{$/ && $block_count == 1 ) { + ($result) = $self->parse_and_check_condition_text($1); + return if (defined $self->error); + debug "[eventMacro] Found an 'elsif' block inside an 'if' block.\n", "eventMacro", 3; + debug "[eventMacro] Script of 'elsif' block: '".$self->{current_line}."'.\n", "eventMacro", 3; + + if ($result) { + debug "[eventMacro] Condition of 'elsif' is true, entering 'elsif' block.\n", "eventMacro", 3; + last CHECK_IF; + } else { + debug "[eventMacro] Condition of 'elsif' is false, cleaning 'elsif' block.\n", "eventMacro", 3; + next; + } + } + + debug "[eventMacro] Cleaning line '".$self->{current_line}."' inside 'if' block.\n", "eventMacro", 3; + + } + } else { + debug "[eventMacro] Code after 'if' is a command, ignoring it and moving to next line\n", "eventMacro", 3; + } + } + $self->next_line; + + ###################################### + # Switch statement + ###################################### + } elsif ($self->{current_line} =~ /^switch.*{$/) { + + # this regex may look wrong, but it's not + # when the line is "switch ( $name ) {" for example, i want to get only "( $name" whitout the closing parenthesis + # the reason is because the closing parenthesis will come from $second_part on case block :D + my ($first_part) = $self->{current_line} =~ /^switch\s*(\(.*)\)\s*{$/; + + debug "[eventMacro] Script is a 'switch' block, searching all 'case' and 'else' blocks.\n", "eventMacro", 3; + + SWITCH: while () { + $self->next_line; + $self->define_current_line; + + if ($self->{finished}) { + $self->{finished} = 0; + $self->error("All 'switch' blocks must be closed before the end of the macro)"); + return; + } + + debug "[eventMacro] Script inside 'switch' block is '".$self->{current_line}."'.\n", "eventMacro", 3; + + #Else on switch + if ($self->{current_line} =~ /^else/) { + my ($after_else) = $self->{current_line} =~ /^else\s*(.*)/; + debug "[eventMacro] Found valid 'else' inside 'switch' block.\n", "eventMacro", 3; + + if ($after_else ne "{") { + debug "[eventMacro] Code after the 'else' is a command, cleaning 'else' and rechecking line.\n", "eventMacro", 3; + $self->{current_line} =~ s/^else\s*//; + $check_need = 0; + next DEFINE_COMMAND; + } else { + debug "[eventMacro] Entering true 'else' block inside 'switch' block.\n", "eventMacro", 3; + $self->next_line; + last SWITCH; + } + + #Case on switch + } elsif ($self->{current_line} =~ /^case/) { + + debug "[eventMacro] Found a 'case' block inside a 'switch' block.\n", "eventMacro", 3; + + # and on this part i am not getting the opening parenthsis, just the closing one + # example: "case (= nipodemos) {" the regex will get only "= nipodemos) {" + # the reason is because the opening parenthsis are coming from first part on switch block + # creating the complete sentence "($name = nipodemos) {" + my ($second_part) = $self->{current_line} =~ /^case\s*\(\s*(.*)/; + + unless ($second_part) { + $self->error("All 'case' blocks must have a condition"); + return; + } + + my $complete_condition = $first_part . $second_part ; + debug "[eventMacro] complete condition is: '".$complete_condition."'.\n", "eventMacro", 3; + my ($result, $after_case) = $self->parse_and_check_condition_text($complete_condition); + return if (defined $self->error); + unless ($after_case) { + $self->error("All 'case' blocks must have a macro command or a block after it"); + return; + } + + #True case check + if ($result == 1) { + debug "[eventMacro] Condition of 'case' is true.\n", "eventMacro", 3; + if ($after_case ne "{") { + debug "[eventMacro] Code after the 'case' is a command, cleaning 'case' and rechecking line.\n", "eventMacro", 3; + $self->{current_line} =~ s/^case\s*\(.*\)\s*//; + $check_need = 0; + next DEFINE_COMMAND; + } else { + debug "[eventMacro] Entering true 'case' block.\n", "eventMacro", 3; + $self->next_line; + last SWITCH; + } + + } else { + debug "[eventMacro] Condition of 'case' is false.\n", "eventMacro", 3; + if ($after_case eq "{") { + debug "[eventMacro] There's a 'case' block to be cleaned.\n", "eventMacro", 3; + my $block_count = 1; + while ($block_count > 0) { + $self->next_line; + $self->define_current_line; + + if ($self->{finished}) { + $self->{finished} = 0; + $self->error("All 'case' blocks must be closed before the end of the macro"); + return; + } + + debug "[eventMacro] Cleaning line '".$self->{current_line}."' inside 'case' block.\n", "eventMacro", 3; + + if (isNewCommandBlock($self->{current_line})) { + $block_count++; + } elsif ($self->{current_line} eq '}') { + $block_count--; + } + } + + } else { + debug "[eventMacro] There is a command after case, ignoring it\n", "eventMacro", 3; + } + + #if after clean case block exist a '}', that means end of switch block + if ($self->line_script(($self->line_index + 1)) eq '}') { + debug "[eventMacro] End of 'switch' block\n", "eventMacro", 3; + last SWITCH; + } + } + } else { + $self->error("Only 'else' and 'case' blocks are allowed inside switch blocks"); + return; + } + } + + ###################################### + # If arriving at a line 'else' or 'elsif' + ###################################### + } elsif ($self->{current_line} =~ /^}\s*else\s*{/ || $self->{current_line} =~ /^}\s*elsif.*{$/) { + + debug "[eventMacro] Script is a not important condition block ('else' or 'elsif') after an 'if' block, cleaning it.\n", "eventMacro", 3; + + my $open_blocks = 1; + while ($open_blocks > 0) { + + $self->next_line; + $self->define_current_line; + + if ($self->{finished}) { + $self->{finished} = 0; + $self->error("All 'else' and 'elsif' blocks must be closed before the end of the macro)"); + return; + } + + debug "[eventMacro] Cleaning line '".$self->{current_line}."' inside 'else' or 'elsif' block.\n", "eventMacro", 3; + + if (isNewCommandBlock($self->{current_line})) { + $open_blocks++; + } elsif ($self->{current_line} eq '}') { + $open_blocks--; + } + } + $self->next_line; + + ###################################### + # Switch arriving at a line 'else' or 'case' + ###################################### + } elsif ($self->{current_line} =~ /^case/ || $self->{current_line} =~ /^else/) { + my (undef, $after_case) = $self->{current_line} =~ /^(case\s*\(.*\)|else)\s*(.*)/; + + debug "[eventMacro] Script is a not important condition block ('else' or 'case') after an 'switch' block, cleaning it.\n", "eventMacro", 3; + + if ($after_case eq "{") { + debug "[eventMacro] There's a 'case' or 'else' block to be cleaned.\n", "eventMacro", 3; + my $block_count = 1; + while ($block_count > 0) { + $self->next_line; + $self->define_current_line; + + if ($self->{finished}) { + $self->{finished} = 0; + $self->error("All 'case' and 'else' blocks must be closed before the end of the macro"); + return; + } + + debug "[eventMacro] Cleaning line '".$self->{current_line}."' inside 'case' or 'else' block.\n", "eventMacro", 3; + + if (isNewCommandBlock($self->{current_line})) { + $block_count++; + } elsif ($self->{current_line} eq '}') { + $block_count--; + } + } + } else { + debug "[eventMacro] After 'case' or 'else' there is a command, ignoring it.\n", "eventMacro", 3; + } + $self->next_line; + + ###################################### + # Macro block: begin + ###################################### + } elsif ($self->{current_line} eq '[') { + debug "[eventMacro] Script is the start of a macro block.\n", "eventMacro", 3; + $self->{macro_block} = 1; + $self->next_line; + + ###################################### + # Macro block: end + ###################################### + } elsif ($self->{current_line} eq ']') { + debug "[eventMacro] Script is the end of a macro block.\n", "eventMacro", 3; + $self->{macro_block} = 0; + $self->next_line; + + ###################################### + # End block of "if", "switch" or "while" + ###################################### + } elsif ($self->{current_line} eq '}') { + if ($self->{block}{end_to_start}{$self->line_index}{type} eq 'while') { + debug "[eventMacro] Script is the end of a while 'block', moving to its start.\n", "eventMacro", 3; + $self->line_index($self->{block}{end_to_start}{$self->line_index}{start}); + } else { + debug "[eventMacro] Script is the end of a not important block (if or switch).\n", "eventMacro", 3; + $self->next_line; + } + + ###################################### + # Label statement + ###################################### + } elsif ($self->{current_line} =~ /^:/) { + debug "[eventMacro] Script is a label definition.\n", "eventMacro", 3; + $self->next_line; + + ###################################### + # Goto flow command + ###################################### + } elsif ($self->{current_line} =~ /^goto\s/) { + my ($label) = $self->{current_line} =~ /^goto\s+([a-zA-Z][a-zA-Z\d_]*)/; + if (exists $self->{label}->{$label}) { + debug "[eventMacro] Script is a goto flow command.\n", "eventMacro", 3; + $self->line_index($self->{label}->{$label}); + } else { + $self->error("Cannot find label '$label'"); + return; + } + + ###################################### + # End (Should be a command) + ###################################### + } else { + debug "[eventMacro] Next valid macro command found: '".$self->{current_line}."'.\n", "eventMacro", 3; + last DEFINE_COMMAND; + } + } +} + +# Processes next line of macro script +sub next { + my $self = $_[0]; + + #We must finish the subcall before returning to this macro + return $self->manage_subcall if (defined $self->{subcall}); + + # All non command lines must be checked and parsed in only one 'next' cycle + # define_next_valid_command makes sure the current line is a valid macro command + # all flow control ('if', 'else', 'goto', 'while', etc) must be parsed by it. + $self->define_next_valid_command; + return if (defined $self->error); + return "" if ($self->{finished}); + + #Some debug messages + debug "[eventMacro] Executing macro '".$self->{name}."', line index '".$self->line_index.".\n", "eventMacro", 2; + debug "[eventMacro] Line script '".$self->{current_line}."'.\n", "eventMacro", 2; + + ########################################## + # set variable: variable = value + if ($self->{current_line} =~ /^$general_variable_qr/i) { + my $line = $self->{current_line}; + + my $variable; + my $value; + if ($line =~ /^($general_wider_variable_qr)\s*([+-]{2}|=\s*.*)/) { + $variable = $1; + $value = $2; + if ($variable =~ /^[\$\@\%]\./) { + $self->error("You can't change the value of read-only variables"); + return; + } + } else { + $self->error("Could not separate variable name from value"); + return; + } + + my $var; + my $display_name; + + if (my $var_hash = $self->find_and_define_key_index($variable)) { + return if (defined $self->error); + $var = $var_hash->{var}; + + } else { + return if (defined $self->error); + $var = find_variable($1); + if (defined $self->error) { + return; + } elsif (!defined $var) { + $self->error("Could not define variable type"); + return; + } + } + + if ($var->{type} eq 'scalar' || $var->{type} eq 'accessed_array' || $var->{type} eq 'accessed_hash') { + + my $complement = (exists $var->{complement} ? $var->{complement} : undef); + + if ($value =~ /^=\s*(.*)/i) { + my $val = $self->parse_command($1); + + if (defined $self->error) { + return; + + } elsif (!defined $val) { + $self->error("$val failed"); + return; + + } else { + $eventMacro->set_var( + $var->{type}, + $var->{real_name}, + ($val =~ /^\s*(?:undef|unset)\s*$/i ? ('undef'):($val)), + 1, + $complement + ); + } + + } elsif ($value =~ /^([+-]{2})$/i) { + my $change = (($1 eq '++') ? (1) : (-1)); + + my $old_value = ($eventMacro->defined_var($var->{type}, $var->{real_name}, $complement) ? ($eventMacro->get_var($var->{type}, $var->{real_name}, $complement)) : 0); + $eventMacro->set_var( + $var->{type}, + $var->{real_name}, + ($old_value + $change), + 1, + $complement + ); + + } else { + $self->error("unrecognized assignment"); + return; + } + + } elsif ($var->{type} eq 'array' || $var->{type} eq 'hash') { + if ($value =~ /^=\s*(.*)/i) { + my $value = $1; + + if ($value =~ /(?:undef|unset)/) { + $eventMacro->clear_array($var->{real_name}) if ($var->{type} eq 'array'); + $eventMacro->clear_hash($var->{real_name}) if ($var->{type} eq 'hash'); + + } elsif ($value =~ /^\((.*)\)$/) { + my @members = split(/\s*,\s*/, $1); + + if ($var->{type} eq 'array') { + $eventMacro->set_full_array($var->{real_name}, \@members); + + } else { + my %hash; + foreach my $hash_member (@members) { + if ($hash_member =~ /(.*\S)\s*=>\s*(\S.*)/) { + my $key = $1; + my $value = $2; + $hash{$key} = $value; + } else { + $self->error("Bad syntax in hash key definition"); + return; + } + } + $eventMacro->set_full_hash($var->{real_name}, \%hash); + } + + } elsif ($value =~ /\w+\s*\(.*\)$/) { + my $real_value = $self->parse_command($value); + if ( (ref($real_value) eq 'ARRAY' || ref($real_value) eq 'HASH') && $var->{type} eq 'hash') { + + #if is a array ref, have to convert into a hash ref + if (ref($real_value) eq 'ARRAY') { + my %hash = @{$real_value}; + $real_value = \%hash; + } + $eventMacro->set_full_hash($var->{real_name}, $real_value); + + } elsif (ref($real_value) eq '' && $real_value && $var->{type} eq 'array') { + #elsif real value is defined is because something were returned, so it will make an array of that + my @array = split (/,/, $real_value); + $eventMacro->set_full_array($var->{real_name}, \@array); + + } elsif((ref($real_value) eq 'ARRAY' || ref($real_value) eq 'SCALAR') && $var->{type} eq 'array') { + $eventMacro->set_full_array($var->{real_name}, $real_value); + + } else { + # $real_value not defined, some error happened + $self->error("Unable to set array or hash, empty value! (value: '$real_value')"); + return; + } + } + + } else { + $self->error("unrecognized assignment"); + return; + } + } + $self->next_line; + $self->timeout(0); + + ########################################## + # manage array: push|unshift|shift|pop(@array[,new_member]) + } elsif ($self->{current_line} =~ /^$macro_keywords_character(push|unshift|pop|shift)/i) { + $self->parse_command($self->{current_line}); + return if (defined $self->error); + $self->timeout(0); + $self->next_line; + + ########################################## + # manage hash: delete($hash{new_member}) + } elsif ($self->{current_line} =~ /^$macro_keywords_character(delete)/i) { + $self->parse_command($self->{current_line}); + return if (defined $self->error); + $self->timeout(0); + $self->next_line; + + ########################################## + # returns command: do whatever + } elsif ($self->{current_line} =~ /^do\s/) { + my ($do_command) = $self->{current_line} =~ /^do\s+(.*)/; + my $result = $self->parse_do($do_command); + return $result if (defined $result); + + ########################################## + # log command + } elsif ($self->{current_line} =~ /^(log|warning|error)\s+(.*)/) { + my ($type, $log_text) = ($1, $2); + $self->parse_log($type, $log_text); + + ########################################## + # pause command + } elsif ($self->{current_line} =~ /^pause/) { + my ($pause_command) = $self->{current_line} =~ /^pause\s*(.*)/; + $self->parse_pause($pause_command); + + ########################################## + # stop command + } elsif ($self->{current_line} eq "stop") { + $self->stop_command(); + + ########################################## + # release command + } elsif ($self->{current_line} =~ /^release\s+/) { + my ($release_command) = $self->{current_line} =~ /^release\s+(.*)/; + $self->parse_release_and_lock($release_command, 2); + + ########################################## + # lock command + } elsif ($self->{current_line} =~ /^lock\s+/) { + my ($lock_command) = $self->{current_line} =~ /^lock\s+(.*)/; + $self->parse_release_and_lock($lock_command, 1); + + ########################################## + # call command + } elsif ($self->{current_line} =~ /^call\s+/) { + my ($call_command) = $self->{current_line} =~ /^call\s+(.*)/; + $self->parse_call($call_command); + $self->timeout(0); + + ########################################## + # set command + } elsif ($self->{current_line} =~ /^set\s+/) { + my ($parameter, $new_value) = $self->{current_line} =~ /^set\s+(\w+)\s+(.*)$/; + $self->parse_set($parameter, $new_value); + + ########################################## + # include command + } elsif ($self->{current_line} =~ /^include\s+/) { + my ($key, $param) = $self->{current_line} =~ /^include\s+(\w+)\s+(.*)$/; + $self->parse_include($key, $param); + + ########################################## + # sub-routine command, still figuring out how to include unclever/fail sub-routine into the error msg + } elsif ($self->{current_line} =~ /^(?:\w+)\s*\(.*?\)/) { + $self->perl_sub_command; + + ########################################## + # unrecognized line + } else { + $self->error("Unrecognized macro command"); + } + + ########################################## + # For some reason returning undef is an error while returning an empty string is fine. + if (defined $self->error) { + return; + } else { + return ""; + } +} + +sub parse_and_check_condition_text { + my ($self, $line_script) = @_; + + #think on this possible statment: + # if ( ( 1 = 1 ) && ( foo != bar ) ) { + #this code will create an array of groups os parenthesis, starting by the smaller, until the bigger + #so it will be like: + #$group[0] = "( 1 = 1 )" + #$group[1] = "( foo != bar )" + #$group[2] = "( ( 1 = 1 ) && ( foo != bar) )" + #so each group will be treated separetedly + my @characters = split //, $line_script; + + my ($parenthesis_count, + $in_regex, + $in_quote, + @start_of_group, + $token, + $ignore_next_closing_parenthesis, + $start_of_macro_keyword, + @groups, + $start_of_variable, + ); + + CHAR: for (my $i = 0; $i < @characters ; $i++) { + my $previous_char = $characters[$i-1]; + my $current_char = $characters[$i]; + my $next_char = $characters[$i+1]; + + #end of regex if found a '/' and it's not a \/ + if (defined $in_regex) { + if ($current_char eq '/' && $previous_char ne "\\") { + undef $in_regex; + } + + #end of quoted string if found a " and it's not a \" + } elsif (defined $in_quote) { + if ($current_char eq '"' && $previous_char ne "\\") { + undef $in_quote; + } + + #ignore if is inside a regex or if found a escaped \" + } elsif (!defined $in_quote && $current_char eq '"') { + unless ($previous_char eq "\\" || defined $in_regex) { + $in_quote = 1; + } + + #ignore if is inside a quoted_string or if found a escaped one '\/' + } elsif (!defined $in_regex && $current_char eq '/') { + unless ($previous_char eq '\\' || defined $in_quote) { + $in_regex = 1; + } + + #save every char on $token, until a space or special charecter appear + #eventually it will become a macro_keyword or sub + } elsif ( $current_char =~ /\w/) { + if (!defined $start_of_variable) { + if ($next_char =~ /\W/ && length($token) < 2) { + undef $token if defined $token; + next CHAR; + } else { + $token .= $current_char; + } + } + + } elsif ($current_char =~ /\s/) { + + # if a token is defined, then it can be a macro_keyword or a sub + # but is treated on other case statement + # if not, then undef token and start_of_macro_keyword and start_of_variable + unless (defined $token && length($token) >= 3 && $next_char eq '(') { + undef $start_of_macro_keyword; + undef $token; + undef $start_of_variable; + } + + #this case block is only to prevent $token to save variables, which is useless + #$token is meant to save only macro_keywords and subs + } elsif ($current_char eq '$' || $current_char eq '@' || $current_char eq '%') { + unless ($previous_char eq "\\") { + $start_of_variable = 1; + } + + } elsif ($current_char eq '&' ) { + + #ignore if is quoted string or regex or found a && + if ($previous_char eq '&' || $next_char eq '&') { + undef $token if defined $token; + undef $start_of_macro_keyword if defined $start_of_macro_keyword; + + #if is a & followed by a letter or number, is problably a macro keyword + } elsif ($previous_char ne '&' && $next_char =~ /\w/) { + undef $token; + $start_of_macro_keyword = 1; + + # throw error + } else { + $self->error("unsupported '&' in statment (maybe you put just only one '&' instead of two?)"); + return; + } + + } elsif ( $current_char eq '|' ) { + + #ignore if inside a quoted string or a regex + if ($previous_char eq '|' || $next_char eq '|') { + undef $token if defined $token; + undef $start_of_macro_keyword if defined $start_of_macro_keyword; + + #else throw error + } else { + $self->error("unsupported '|' in statment (maybe you put only one '|' instead of two?)"); + return; + } + } elsif ( $current_char eq ')' ) { + + undef $start_of_macro_keyword if defined $start_of_macro_keyword; + undef $token if defined $token; + + #this is when the ) is a closing pharenthesis of a macrokeyword or a sub + #example: &npc(154 89) <--- this closing pharentesis will not be considered as a group end + if ($ignore_next_closing_parenthesis > 0) { + $ignore_next_closing_parenthesis--; + next CHAR; + } + + #end of the group + $parenthesis_count--; + if ($parenthesis_count < 0) { + $self->error("missing at least one opening parenthesis or too many closing parenthesis"); + return; + } + + #currently there is something that have to change: + #start, end and lenght of group is only used on the main group (the entire statement) + #the other groups those informations are irrelevant, so it shouldn't be stored + my $lenght = ($i + 1) - $start_of_group[-1]; + my $script = substr ($line_script, $start_of_group[-1], $lenght); + push @groups, { + start => pop @start_of_group, #get last value of array + end => $i+1, + length => $lenght, + script => $script + }; + + #end of entire statement + if ($parenthesis_count == 0) { + last CHAR; + } + + } elsif ($current_char eq '(' ) { + + #oh, this might be a macro keyword or sub! + if ($token && length($token) >= 3) { + + #probably a macro_keyword + if (defined $start_of_macro_keyword) { + #yeah it is + if ($macroKeywords =~ /\b$token\b/) { + $ignore_next_closing_parenthesis++; + undef $token; + undef $start_of_macro_keyword; + next CHAR; + + #macro_keyword not found, probably typo + } else { + $self->error("unknown macro_keyword '&$token' (maybe you typed wrong?)"); + return; + } + + #problably is a sub + } else { + my $sub_found; + foreach (@{ $eventMacro->{subs_list} }) { + + #it is a sub! + if ( $token eq $_) { #sub name + $sub_found = 1; + $ignore_next_closing_parenthesis++; + undef $token; + next CHAR; + } + } + + if (!$sub_found) { + $self->error("unknown sub '$token' (maybe you typed wrong?)"); + return; + } + } + undef $start_of_macro_keyword; + undef $token; + + #it is nothing, so it will be ignored + } else { + undef $token; + undef $start_of_macro_keyword; + } + + push (@start_of_group, $i); + $parenthesis_count++; + } + } #for loop end + + #its error time + #multiple checks of commom errors + if (defined $in_regex) { + $self->error("unclosed regex found, please check your code\n"); + return; + } elsif (defined $in_quote) { + $self->error("unclosed quote found, please check your code\n"); + return; + } elsif (defined $start_of_macro_keyword) { + $self->error("unclosed macro_keyword found, please check your code"); + return; + } elsif ($parenthesis_count > 0) { + $self->error("unclosed parenthesis found, please check your code"); + return; + } + + #the last group in the array is always the full statement + #so post_if is what lies after end of main group + my $post_if = substr ($line_script, $groups[-1]{end}+1); + $post_if =~ s/^\s+|\s+$//g; + my $result = $self->parse_condition_text_groups(\@groups); + return if defined $self->error; + + return $result, $post_if; +} + +sub parse_condition_text_groups { + my ($self, $groups) = @_; + my $result; + + #when it is a simple statemente, with only one group, then parse it 1 time only + if ( scalar @{$groups} == 1) { + $result = $self->parse_single_group($groups->[-1]->{script}); + return if (defined $self->error); + + #when it is a complex statement, with more than one group, parse each one of them + #then replace the group byt its result on entire statement + } else { + my $entire_statement = pop @{$groups}; #original unparsed statement + + #now we will parse one group at a time, since it has more than one + foreach my $group ( @{ $groups } ) { + my $script = $group->{script}; + my $parsed = $self->parse_single_group($script); + return if defined $self->error; + #after parse a group, remove it from original script and replace by it's result + $entire_statement->{script} =~ s/\Q$script\E/$parsed/; + + } + #now that were parsed other groups inside result, lets parse result itself + $result = $self->parse_single_group($entire_statement->{script}); + return if defined $self->error; + } + + return $result; +} + +sub parse_single_group { + my ($self, $script) = @_; + my @characters = split (//, $script); + my ($negate, $first, $condition, $second); + my $in_quote; + my $in_regex; + + CHAR: for (my $i = 0; $i < @characters; $i++) { + my $previous_char = $characters[$i-1]; + my $current_char = $characters[$i]; + my $next_char = $characters[$i+1]; + my $result; + + #end of regex if found a '/' and it's not a \/ + if (defined $in_regex) { + if ($current_char eq '/' && $previous_char ne "\\") { + undef $in_regex; + } + + #end of quoted string if found a " and it's not a \" + } elsif (defined $in_quote) { + if ($current_char eq '"' && $previous_char ne "\\") { + undef $in_quote; + } + + #ignore if is inside a regex or if found a escaped\" + } elsif (!defined $in_quote && $current_char eq '"') { + unless ($previous_char eq "\\" || defined $in_regex) { + $in_quote = 1; + } + + #ignore if is inside a quoted_string or if found a escaped one '\/' + } elsif (!defined $in_regex && $current_char eq '/') { + unless ($previous_char eq '\\' || defined $in_quote) { + $in_regex = 1; + } + + #if there is no other char after ')', that is really end of group + #it will be parsed unless is inside a quoted string or a regex + } elsif ($current_char eq ')' && !defined $next_char) { + #end of group that is being parsed + $result = $self->resolve_statement($negate, $first, $condition, $second); + return if (defined $self->error); + + $script =~ s/\Q$negate$first$condition$second\E/$result/ + or $self->error("NOT POSSIBLE TO MAKE SUBSTITUTION of '$negate$first$condition$second' in '$script', report to creators of eventMacro\n"); + return if (defined $self->error); + + + $script = $self->resolve_multi_and_remove_parenthesis($script); + return if (defined $self->error); + undef $negate; + undef $first; + undef $condition; + undef $second; + + return $script; + + #means it is the beginning of script, always a '('' + } elsif ( $current_char eq '(' && $i == 0) { + next CHAR; + + #if next char is also a & means that were found a '&&', that means that there is one statement to be parsed + #if next char is also a | means that were found a '||', that means that there is one statement to be parsed + } elsif ( ($current_char eq '&' && $next_char eq '&') || ($current_char eq '|' && $next_char eq '|') ) { + $result = $self->resolve_statement($negate, $first, $condition, $second); + return if (defined $self->error); + + $script =~ s/\Q$negate$first$condition$second\E/$result/ + or $self->error("NOT POSSIBLE TO MAKE SUBSTITUTION of '$negate$first$condition$second' in '$script', report to creators of eventMacro\n"); + return if (defined $self->error); + + undef $negate; + undef $first; + undef $condition; + undef $second; + $i++; #intentional, it is to skip the next & + next CHAR; + + #special characters found, problably is $condition + } elsif ( $current_char =~ /[=!~><]/ ) { + + if ( !defined $first || $first =~ /^\s+$/) { + #if found a special character before first be defined, can only be two things + #its the $negate char or it's a typo + if ($current_char eq '!') { + $negate = $first . '!'; #first is here only to get the whitespace + undef $first; #clean spaces inside $first, they were passed to $negate + next CHAR; + } else { + $self->error("Error in statement '$script': not expected '$current_char' here (use quotes if condition have special characters)"); + return; + } + + #a condition + } elsif ( defined $first && !defined $condition ) { + $condition .= $current_char; + if ($next_char =~ /[=!~><]/ ) { + $condition .= $next_char; + $i++; + } + next CHAR; + + #if there is already a condition, then is certainly an error + } elsif ( defined $first && defined $condition) { + $self->{errror} = "Error in statement '$script': not expected to have '$current_char' at index '$i' (use quotes if condition have special characters)"; + return; + } + } + + + if (!defined $condition) { + $first .= $current_char; + } elsif ( defined $first && defined $condition ) { + $second .= $current_char; + } + + } #end of for loop + + #in theory should never gets here, so it's better to leave a error message case it gets here + $self->error("unknown error found while parsing at sub parse_single_group, please create an issue on github about this"); + return; +} + +sub parse_include { + my ($self, $key, $param) = @_; + + my $parsed_key = $self->parse_command($key); + my $parsed_param = $self->parse_command($param); + return if (defined $self->error); + + if (!defined $parsed_key || !defined $parsed_param) { + $self->error("Could not define include command"); + return; + } + + if ( + ($parsed_key ne 'list' && $parsed_key ne 'on' && $parsed_key ne 'off') || + ($parsed_key eq 'list' && defined $parsed_param) || + (($parsed_key eq 'on' || $parsed_key eq 'off') && !defined $parsed_param) + ) { + $self->error("Invalid include syntax"); + return; + } + $eventMacro->include($parsed_key, $parsed_param); + + $self->timeout($self->macro_delay); + $self->next_line; +} + +sub parse_do { + my ($self, $do_command) = @_; + my $parsed_command = $self->parse_command($do_command); + return if (defined $self->error); + + unless (defined $parsed_command) { + $self->error("Could not define do command"); + return; + } + + if ($parsed_command =~ /^eventMacro\s+/) { + $self->error("Do not use command 'eventMacro' inside macros"); + } elsif ($parsed_command =~ /^ai\s+clear$/) { + $self->error("do not use 'ai clear' inside macros"); + } + return if (defined $self->error); + $self->timeout($self->macro_delay); + $self->next_line; + return $parsed_command; +} + +#From here functions are intended to parse/execute macro commands +sub parse_log { + my ($self, $type, $log_text) = @_; + my $parsed_log = $self->parse_command($log_text); + #type can be message, warning or error + #log_text have the text that will be printed + + return if (defined $self->error); + unless (defined $parsed_log) { + $self->error("Could not define log value"); + } else { + $type = "message" if $type eq "log"; + if (my $sub_ref = Log->can($type)) { + $sub_ref->("[eventmacro $type] $parsed_log\n", "eventMacro"); + } else { + $self->error("Unknown error found while trying to print '$type' log. Report to developers"); + return; + } + } + $self->timeout($self->macro_delay); + $self->next_line; +} + +sub perl_sub_command { + my ($self) = @_; + $self->parse_command($self->{current_line}); + return if (defined $self->error); + $self->timeout(0); + $self->next_line; +} + +sub parse_set { + my ($self, $parameter, $new_value) = @_; + if ($parameter eq 'macro_delay') { + if ($new_value !~ /^[\d\.]*\d+$/) { + $self->error("macro_delay parameter should be a number (decimals are accepted). Given value: '$new_value'"); + } else { + $self->macro_delay($new_value); + } + } elsif ($parameter eq 'repeat') { + if ($new_value !~ /^\d+$/) { + $self->error("repeat parameter should be a number. Given value: '$new_value'"); + } else { + $self->repeat($new_value); + } + } elsif ($parameter eq 'overrideAI') { + if ($new_value !~ /^[01]$/) { + $self->error("overrideAI parameter should be '0' or '1'. Given value: '$new_value'"); + } else { + $self->overrideAI($new_value); + } + } elsif ($parameter eq 'exclusive') { + if ($new_value !~ /^[01]$/) { + $self->error("exclusive parameter should be '0' or '1'. Given value: '$new_value'"); + } else { + $self->interruptible($new_value?0:1); + } + } elsif ($parameter eq 'self_interruptible') { + if ($new_value !~ /^[01]$/) { + $self->error("self_interruptible parameter should be '0' or '1'. Given value: '$new_value'"); + } else { + $self->self_interruptible($new_value); + } + } elsif ($parameter eq 'orphan') { + if ($new_value !~ /^(terminate|terminate_last_call|reregister|reregister_safe)$/) { + $self->error("orphan parameter should be 'terminate', 'terminate_last_call', 'reregister' or 'reregister_safe'. Given value: '$new_value'"); + } else { + $self->orphan($new_value); + } + } else { + $self->error("Unrecognized parameter (supported parameters: 'macro_delay', 'repeat', 'overrideAI', 'exclusive', 'orphan')"); + } + return if (defined $self->error); + $self->timeout(0); + $self->next_line; +} + +sub stop_command { + my ($self) = @_; + debug "[eventMacro] Stopping macro '".$self->{name}."' because of stop command in macro script.\n", "eventMacro", 2; + $self->{finished} = 1; +} + +sub parse_pause { + my ($self, $pause_command) = @_; + if (defined $pause_command) { + my $parsed_pause_command = $self->parse_command($pause_command); + return if (defined $self->error); + if (!defined $parsed_pause_command) { + $self->error("pause value could not be defined"); + } elsif ($parsed_pause_command !~ /^\d+(?:\.\d+)?$/) { + $self->error("pause value '$parsed_pause_command' must be numeric"); + } else { + $self->timeout($parsed_pause_command); + } + } else { + $self->timeout($self->macro_delay); + } + $self->next_line; +} + +#Type 1 is lock +#Type 2 is release +sub parse_release_and_lock { + my ($self, $release_command, $type) = @_; + + my $parsed_automacro_name = $self->parse_command($release_command); + return if (defined $self->error); + + if (!defined $parsed_automacro_name) { + $self->error("automacro name could not be defined"); + } + return if (defined $self->error); + + if ($parsed_automacro_name eq 'all') { + if ($type == 1) { + $eventMacro->disable_all_automacros(); + } else { + $eventMacro->enable_all_automacros(); + } + + } else { + my $automacro = $eventMacro->{Automacro_List}->getByName($parsed_automacro_name); + if (!defined $automacro) { + $self->error("could not find automacro with name '$parsed_automacro_name'"); + } + + if ($type == 1) { + $eventMacro->disable_automacro($automacro); + } else { + $eventMacro->enable_automacro($automacro); + } + } + + $self->timeout(0); + $self->next_line; +} + +sub parse_call { + my ($self, $call_command) = @_; + + # Perform substitutions on the macro, so that macros can be called by variable. + # For example: + # $macro = foo + # $value = bar + # call $macro $value baz + $call_command = $self->substitue_variables( $call_command ); + + my $macro_name = $call_command; + my $repeat_times = 1; + if ( $call_command =~ /\s+/ ) { + my @params; + ( $macro_name, @params ) = parseArgs( $call_command ); + + # Update $.param[n] with the values from the call. + $eventMacro->set_full_array( ".param", \@params); + } + + my $parsed_macro_name = $self->parse_command($macro_name); + return if (defined $self->error); + + if (!defined $parsed_macro_name) { + $self->error("macro name could not be defined"); + } elsif (!defined $eventMacro->{Macro_List}->getByName($parsed_macro_name)) { + $self->error("could not find macro with name '$parsed_macro_name'"); + } + return if (defined $self->error); + + $self->create_subcall($parsed_macro_name, $repeat_times); + + unless (defined $self->{subcall}) { + $self->error("failed to create subcall '$parsed_macro_name'"); + return; + } + + $self->timeout($self->macro_delay); + $self->next_line; +} + +sub resolve_statement { + my ($self, $negate, $first, $cond, $last) = @_; + + #remove trailing whitespaces of beggining and end + $first =~ s/^\s+|\s+$//g; + $cond =~ s/^\s+|\s+$//g; + $last =~ s/^\s+|\s+$//g; + + #remove quotes + $first =~ s/^"(.+)"$/\1/; + $last =~ s/^"(.+)"$/\1/; + + #remove backslashes + $first =~ s/\\([\/\"\'])/\1/; + $last =~ s/\\([\/\"\'])/\1/; + + my ($parsed_first, $parsed_last); + + + #if none of them are defined, throw error + if ( $first eq '' && $cond eq '' && $last eq '' ) { + $self->error("syntax error in if statement (is empty?)"); + return; + + #if $first and $cond are defined and missing $last, throw error + } elsif ( $first ne '' && $cond ne '' && $last eq '' ) { + $self->error("Last argument doesn't exist (1st: '$first', cond: '$cond', last: '$last')"); + return; + + #if $cond and $last are defined, but missing $first, throw error + } elsif ( $first eq '' && $cond ne '' && $last ne '' ) { + $self->error("First argument doesn't exist (1st: '$first', cond: '$cond', last: '$last')"); + return; + + #if only first is defined, parse only first then + } elsif ( $first ne '' && $cond eq '' && $last eq '' ) { + $parsed_first = $self->parse_command($first); + return if (defined $self->error); + + #if all of them are defined, then is ok and the statement will be tested + } elsif ($first ne '' && $cond ne '' && $last ne '') { + $parsed_first = $self->parse_command($first); + $parsed_last = $self->parse_command($last); + return if (defined $self->error); + } + my $result = cmpr($parsed_first, $cond, $parsed_last); + + #if sub cmpr gave some error, result will be empty + #else will be 0 or 1 + if ($result eq '') { + $self->error("error shown above while comparing values on sub resolve_statement"); + return; + } + + #if $negate is defined, then return the opposite result + if (defined $negate) { + return ($result ? 0 : 1); + } else { + return $result; + } +} + +sub resolve_multi_and_remove_parenthesis { + my ($self, $text) = @_; + my ($i, $n) = (0, 0); + my %save; + $text =~ s/\(|\)//g; #here we dont need the parenthesis anymore + while ($text =~ /(\|{2}|\&{2})/g) { + # Check if we put the wrong '&&' or '||' in the statement + # Logically, each clean statement(none-bracket statement), + # cant use the simbol '&&' or '||' together. Infact, it must be saperated + # by using round brackets '(' and ')' in the 1st place + + $save{$i} = $1; + if ($i > 0) { + $n = $i - 1; + if ($save{$i} ne $save{$n}) { + my $s = $text; + #$s =~ s/($save{$i})/\($1\) <-- HERE/g; # Later maybe? ;p + $self->error("Wrong Conditions: ($save{$n} vs $save{$i})"); + return; + } + } + $i++ + } + + if ($save{$n} eq "||" && $i > 0) { + my @split = split(/\s*\|{2}\s*/, $text); + foreach my $e (@split) { + next if $e eq "0"; + return 1 if $e eq "1"; + } + return 0 + + } elsif ($save{$n} eq "&&" && $i > 0) { + my @split = split(/\s*\&{2}\s*/, $text); + foreach my $e (@split) { + next if $e eq "1"; + return 0 if $e eq "0"; + return 0 + } + return 1 + + } elsif ($i == 0) { + return $text if $text =~ /^[0-1]$/; + + } else { + $self->error("Could not resolve multi, report this error on github\n"); + return; + } +} + +#From here functions are meant to parse code and check order (I haven't even looked at them yet) +sub refined_macroKeywords { + # To make sure if there is really no more &special keywords + + my @pair = $_[0] =~ /$macro_keywords_character($macroKeywords)\s*\(\s*(.*)\s*\)/i; + return $_[0] unless @pair; + + $pair[1] = parse_command($pair[1]); + my $new = $macro_keywords_character.$pair[0]."(".$pair[1].")"; + return $new; +} + +# parses all macro perl sub-routine found in the macro script +sub parse_perl_subs { + my ($command) = @_; + my @full = $command =~ /(?:^|\s+)(\w+)\s*(\(\s*(.*?)\s*\).*)$/i; + my @pair = ($full[0]); + my ($bracketed) = extract_bracketed ($full[1], '()'); + return unless $bracketed; + push @pair, substr ($bracketed, 1, -1); + + return unless @pair; + + while ($pair[1] =~ /(?:^|\s+)(\w+)\s*\(/) { + @pair = parse_perl_subs ($pair[1]) + } + + return @pair +} + +# Returns 0 if no key of index was found, otherwise return a hash of the format: +# %hash = (real_name => parsed_var_name, original_name => original_var_name, var => var) +sub find_and_define_key_index { + my ($self, $text) = @_; + + if ($text =~ /(?:^|(?<=[^\\]))\$($valid_var_characters)(\[|\{)(.+)$/) { + my $name = $1; + my $open_bracket = $2; + + my $type = ($open_bracket eq '[' ? 'array' : 'hash'); + my $close_bracket = (($type eq 'hash') ? '}' : ']'); + + my $rest = $3; + + my $key_index = get_key_or_index($open_bracket, $close_bracket, $rest); + if (!defined $key_index) { + $self->error("Could not define key of hash or index of array"); + return; + + } elsif ($key_index eq '') { + $self->error("Empty key of hash or index of array"); + return; + } + + my $parsed_key_index = $self->parse_command($key_index); + if (defined $self->error) { + return; + } elsif (!defined $parsed_key_index) { + $self->error("Could not parse key or index code"); + return; + + } elsif ($parsed_key_index eq '') { + $self->error("Empty key of hash or index of array after parsing"); + return; + + } elsif ($type eq 'hash' && $parsed_key_index !~ /[a-zA-Z\d_]+/) { + $self->error("Invalid syntax in key of hash (only use letters and numbers)"); + return; + + } elsif ($type eq 'array' && $parsed_key_index !~ /\d+/) { + $self->error("Invalid syntax in index of array (only use numbers)"); + return; + } + + my $real_name = ('$'.$name.$open_bracket.$parsed_key_index.$close_bracket); + + my $original_name = ('$'.$name.$open_bracket.$key_index.$close_bracket); + + my $var = find_variable($real_name); + if (!defined $var) { + $self->error("Could not define variable type"); + return; + } + return {real_name => $real_name, original_name => $original_name, var => $var}; + } +} + +# substitute variables +sub substitue_variables { + my ($self, $received, $get_entire_array_or_hash) = @_; + + my $remaining = $received; + my $substituted; + + VAR: while ($remaining =~ /(?:^|(?<=[^\\]))$general_variable_qr/) { + + #accessed arrays and hashes + if (my $var_hash = $self->find_and_define_key_index($remaining)) { + return if (defined $self->error); + my $var = $var_hash->{var}; + my $regex_name = quotemeta($var_hash->{original_name}); + + if ($remaining =~ /^(.*?)(?:^|(?<=[^\\]))$regex_name(.*?)$/) { + my $before_var = $1; + my $after_var = $2; + my $var_value = $eventMacro->get_var($var->{type}, $var->{real_name}, $var->{complement}); + $var_value = '' unless (defined $var_value); + + $remaining = $before_var.$var_value.$after_var; + + } else { + $self->error("Could not find detected variable in code"); + return; + } + next VAR; + + } elsif ($remaining =~ /(?:^|(?<=[^\\]))($scalar_variable_qr|$array_variable_qr|$hash_variable_qr)/) { + return if (defined $self->error); + my $var = find_variable($1); + + my $regex_name = quotemeta($var->{display_name}); + if ($remaining =~ /^(.*?)(?:^|(?<=[^\\]))$regex_name(.*?)$/) { + my $before_var = $1; + my $after_var = $2; + my $var_value; + if ($var->{type} eq 'scalar') { + $var_value = $eventMacro->get_scalar_var($var->{real_name}); + + } elsif ($var->{type} eq 'array') { + if ($get_entire_array_or_hash) { + $var_value = $var->{display_name}; + } else { + $var_value = $eventMacro->get_array_size($var->{real_name}); + } + + } elsif ($var->{type} eq 'hash') { + if ($get_entire_array_or_hash) { + $var_value = $var->{display_name}; + } else { + $var_value = $eventMacro->get_hash_size($var->{real_name}); + } + } + $var_value = '' unless (defined $var_value); + + $substituted = $substituted . $before_var . $var_value; + $remaining = $after_var; + + } else { + $self->error("Could not find detected variable in code"); + return; + } + next VAR; + } + } + + $substituted .= $remaining; + + #Remove backslashes + $substituted =~ s/\\($scalar_variable_qr|$array_variable_qr|$hash_variable_qr)/$1/g; + + return $substituted; +} + + + +sub parse_keywords { + my ($command) = @_; + my @full = $command =~ /$macro_keywords_character($macroKeywords)\s*(\(\s*(.*?)\s*\).*)$/i; + my @pair = ($full[0]); + my ($bracketed) = extract_bracketed ($full[1], '()'); + return unless $bracketed; + push @pair, substr ($bracketed, 1, -1); + + return unless @pair; + if ($pair[0] eq 'arg') { + return $command =~ /$macro_keywords_character(arg)\s*\(\s*(".*?",\s*(\d+|\$[a-zA-Z][a-zA-Z\d_]*))\s*\)/ + } elsif ($pair[0] eq 'random') { + return $command =~ /$macro_keywords_character(random)\s*\(\s*(".*?")\s*\)/ + } + while ($pair[1] =~ /$macro_keywords_character($macroKeywords)\s*\(/) { + @pair = parse_keywords ($pair[1]) + } + return @pair +} + +# command line parser for macro +# returns undef if something went wrong, else the parsed command or "". +sub parse_command { + my ($self, $command) = @_; + return "" unless defined $command; + my ($keyword, $inside_brackets, $parsed, $result, $sub, $val); + + while (($keyword, $inside_brackets) = parse_keywords($command)) { + $result = "_%_"; + + #if $keyword is different of every key inside qw(), then we will substitue variables + unless (grep{$_ eq $keyword} qw(nick push unshift pop shift delete exists defined split keys values)) { + $parsed = $self->substitue_variables($inside_brackets); + } + my $only_replace_once = 0; + + if ($keyword eq 'npc') { + $result = getnpcID($parsed); + + } elsif ($keyword eq 'cart') { + $result = (getItemIDs($parsed, $char->cart))[0]; + + } elsif ($keyword eq 'Cart') { + $result = join ',', getItemIDs($parsed, $char->cart); + + } elsif ($keyword eq 'inventory') { + $result = (getInventoryIDs($parsed))[0]; + + } elsif ($keyword eq 'Inventory') { + $result = join ',', getInventoryIDs($parsed); + + } elsif ($keyword eq 'InventoryType') { + $result = join ',', getInventoryTypeIDs($parsed); + + } elsif ($keyword eq 'store') { + $result = (getItemIDs($parsed, $storeList))[0]; + + } elsif ($keyword eq 'storage') { + $result = (getStorageIDs($parsed))[0]; + + } elsif ($keyword eq 'Storage') { + $result = join ',', getStorageIDs($parsed); + + } elsif ($keyword eq 'player') { + $result = getPlayerID($parsed); + + } elsif ($keyword eq 'monster') { + $result = getMonsterID($parsed); + + } elsif ($keyword eq 'vender') { + $result = getVenderID($parsed); + + } elsif ($keyword eq 'venderitem') { + $result = (getItemIDs($parsed, $venderItemList))[0]; + + } elsif ($keyword eq 'venderItem') { + $result = join ',', getItemIDs($parsed, $venderItemList); + + } elsif ($keyword eq 'venderprice') { + $result = getItemPrice($parsed, $venderItemList->getItems); + + } elsif ($keyword eq 'venderamount') { + $result = getVendAmount(getItemIDs($parsed, $venderItemList), $venderItemList->getItems); + + } elsif ($keyword eq 'random') { + $result = getRandom($parsed); + $only_replace_once = 1; + + } elsif ($keyword eq 'rand') { + $result = getRandomRange($parsed); + $only_replace_once = 1; + + } elsif ($keyword eq 'invamount') { + $result = getInventoryAmount($parsed); + + } elsif ($keyword eq 'cartamount') { + $result = getCartAmount($parsed); + + } elsif ($keyword eq 'shopamount') { + $result = getShopAmount($parsed); + + } elsif ($keyword eq 'storamount') { + $result = getStorageAmount($parsed); + + } elsif ($keyword eq 'itemCard') { + $result = getEquipCards($parsed); + + } elsif ($keyword eq 'itemCardAmount') { + $result = getEquipCardAmount($parsed); + + } elsif ($keyword eq 'itemOption') { + $result = getEquipOptions($parsed); + + } elsif ($keyword eq 'itemOptAmount') { + $result = getEquipOptionsAmount($parsed); + + } elsif ($keyword eq 'config') { + $result = getConfig($parsed); + + } elsif ($keyword eq 'arg') { + $result = getWord($parsed); + + } elsif ($keyword eq 'eval') { + $result = eval($parsed) unless $Settings::lockdown; + + } elsif ($keyword eq 'listitem') { + $result = getArgFromList($parsed); + + } elsif ($keyword eq 'strip') { + $parsed =~ s/\(|\)//g; + $result = $parsed; + + } elsif ($keyword eq 'split') { + my ($pattern, $string) = get_pattern($inside_brackets); + my @values = split($pattern, $self->substitue_variables($string)); + $result = join (',', @values); + + } elsif ($keyword eq 'keys') { + $result = join ',', find_hash_and_get_keys($inside_brackets);; + + } elsif ($keyword eq 'values') { + $result = join ',', find_hash_and_get_values($inside_brackets); + + } elsif ($keyword eq 'nick') { + $parsed = $self->substitue_variables($inside_brackets); + $result = q4rx2($parsed); + + } elsif ($keyword eq 'push' || $keyword eq 'unshift' || $keyword eq 'pop' || $keyword eq 'shift') { + $result = $self->manage_array($keyword, $inside_brackets); + return if (defined $self->error); + $only_replace_once = 1; + + } elsif ($keyword eq 'exists' || $keyword eq 'delete') { + $result = $self->manage_hash($keyword, $inside_brackets); + return if (defined $self->error); + $only_replace_once = 1; + + } elsif ($keyword eq 'defined') { + $result = $self->parse_defined($inside_brackets); + return if (defined $self->error); + $only_replace_once = 1; + + } elsif ( $keyword eq 'questStatus' ) { + $result = getQuestStatus( $parsed )->{$parsed} || 'unknown'; + + } elsif ( $keyword eq 'questInactiveCount' ) { + $result = grep { $_ eq 'inactive' } values %{ getQuestStatus( split /\s*,\s*/, $parsed ) }; + + } elsif ( $keyword eq 'questIncompleteCount' ) { + $result = grep { $_ eq 'incomplete' } values %{ getQuestStatus( split /\s*,\s*/, $parsed ) }; + + } elsif ( $keyword eq 'questCompleteCount' ) { + $result = grep { $_ eq 'complete' } values %{ getQuestStatus( split /\s*,\s*/, $parsed ) }; + + } + + return unless defined $result; + return $command if ($result eq '_%_'); + + $inside_brackets = q4rx($inside_brackets); + + unless ($only_replace_once) { + $command =~ s/$macro_keywords_character$keyword\s*\(\s*$inside_brackets\s*\)/$result/g + } else { + $command =~ s/$macro_keywords_character$keyword\s*\(\s*$inside_brackets\s*\)/$result/ + } + } + + unless ($Settings::lockdown) { + # any round bracket(pair) found after parse_keywords sub-routine were treated as macro perl sub-routine + undef $result; undef $parsed; + while (($sub, $val) = parse_perl_subs($command)) { + my $sub_error = 1; + foreach my $e (@perl_name) { + if ($e eq $sub) { + $sub_error = 0; + } + } + if ($sub_error) { + $self->error("Unrecognized --> $sub <-- Sub-Routine"); + return ""; + } + $parsed = $self->substitue_variables($val, 1); + + #spliting $parsed to check if there is an array or hash + my (@array_holder, %hash_holder); + foreach (split /\s*,\s*/ , $parsed) { + #if don't have quotation marks and it is not a number or a variable, add the quotation marks + if ($_ !~ /"[^"]+"/ && $_ !~ /^\s*\d+\s*$/ && $_ !~ /$array_variable_qr|$hash_variable_qr/) { + $parsed =~ s/$_/"$_"/; + + #elsif it is a variable or a number but has quotation marks, remove it + } elsif ($_ =~ /"\s*$array_variable_qr\s*"|"\s*$hash_variable_qr\s*"/ || $_ =~ /"\s*\d+\s*"/) { + #first remove quotation from $_ + $_ =~ s/\"//g; + #after remove quotation from $parsed, using the $_ to find the correct place to remove + $parsed =~ s/\"$_\"/$_/; + #strange but it works + } + + if (my $var = find_variable($_)) { + #there is an array or a hash to pass to sub + if ($var->{type} eq 'array') { + #if array exists, gets the content and insert on array holder + #then insert array_holder on $parsed + if ($eventMacro->{Array_Variable_List_Hash}{ $var->{real_name}}) { + @array_holder = @{ $eventMacro->{Array_Variable_List_Hash}{$var->{real_name}} }; + $parsed =~ s/$var->{real_name}/array_holder/; + } else { + $self->error("Array '" . $var->{display_name} . "' does not exist"); + } + + } elsif ($var->{type} eq 'hash') { + #if hash exists, gets the content and insert on hash holder + #then insert hash_holder on $parsed + if ($eventMacro->{Hash_Variable_List_Hash}{$var->{real_name}}) { + %hash_holder = %{ $eventMacro->{Hash_Variable_List_Hash}{$var->{real_name}} }; + $parsed =~ s/$var->{real_name}/hash_holder/; + } else { + $self->error(("Hash '" . $var->{display_name} . "' does not exist")); + } + } else { + $self->error("Could not define variable type on calling sub"); + return ""; + } + } + } + my $sub1 = 'main::'.$sub.'('.$parsed.')'; + my @testArray = eval($sub1); + if ($@) { + warning "[eventMacro] Error in eval '".$@."'\n"; + } + return unless scalar(@testArray); + if (scalar(@testArray) > 1 ) { + #if this is true, user returned an array or hash that is not a reference + #but the code demands a reference + $result = \@testArray; + } else { + #can be a normal scalar value, or a reference to anything + $result = $testArray[0]; + } + if (ref($result) eq 'ARRAY' || ref($result) eq 'HASH' || ref($result) eq 'SCALAR') { + return $result; + } + $val = q4rx $val; + $command =~ s/$sub\s*\(\s*$val\s*\)/$result/g + } + } + + $command = $self->substitue_variables($command); + return $command; +} + +sub parse_defined { + my ($self, $inside_brackets) = @_; + + my $var; + if (my $var_hash = $self->find_and_define_key_index($inside_brackets)) { + return if (defined $self->error); + $var = $var_hash->{var}; + + } else { + return if (defined $self->error); + $var = find_variable($inside_brackets); + } + + if (!defined $var) { + $self->error("Could not define variable type"); + return; + + } elsif ($var->{type} ne 'accessed_hash' && $var->{type} ne 'accessed_array' && $var->{type} ne 'scalar') { + $self->error("defined function can only be used on scalars, hashes with keys or arrays with indexes, you trie to use it in a ".$var->{type}); + return; + } + + my $complement = (exists $var->{complement} ? $var->{complement} : undef); + + return ($eventMacro->defined_var($var->{type}, $var->{real_name}, $complement)); +} + +sub manage_hash { + my ($self, $keyword, $inside_brackets) = @_; + + if (my $var_hash = $self->find_and_define_key_index($inside_brackets)) { + return if (defined $self->error); + my $var = $var_hash->{var}; + if ($var->{type} ne 'accessed_hash') { + $self->error("Bad exists syntax, variable not a hash name/key pair"); + return; + } + + if ($keyword eq 'exists') { + return ($eventMacro->exists_hash($var->{real_name}, $var->{complement})); + + } elsif ($keyword eq 'delete') { + my $result = $eventMacro->delete_key($var->{real_name}, $var->{complement}); + $result = '' unless (defined $result); + return $result; + } + + } else { + return if (defined $self->error); + $self->error("Function '".$keyword."' must have a hash and a hash key as argument"); + return; + } +} + +sub manage_array { + my ($self, $keyword, $inside_brackets) = @_; + + my @args = split(/\s*,\s*/, $inside_brackets, 2); + my ($var); + + if ($args[0] =~ /^($array_variable_qr)/i) { + $var = find_variable($1); + if (defined $self->error) { + return; + } elsif (!defined $var) { + $self->error("Could not define variable type in array manage"); + return; + } + } else { + $self->error("'$args[0]' is not a valid array name"); + return; + } + + my $parsed = $self->parse_command($args[1]); + + my $result; + + if ($keyword eq 'push') { + if (@args != 2) { + $self->error("push sintax must be 'push(\@var_name, new_member)'"); + return; + } + $result = $eventMacro->push_array($var->{real_name}, $parsed); + + } elsif ($keyword eq 'unshift') { + if (@args != 2) { + $self->error("unshift sintax must be 'unshift(\@var_name, new_member)'"); + return; + } + $result = $eventMacro->unshift_array($var->{real_name}, $parsed); + + } elsif ($keyword eq 'pop') { + if (@args != 1) { + $self->error("pop sintax must be 'pop(\@var_name)'"); + return; + } + $result = $eventMacro->pop_array($var->{real_name}); + + } elsif ($keyword eq 'shift') { + if (@args != 1) { + $self->error("shift sintax must be 'shift(\@var_name)'"); + return; + } + $result = $eventMacro->shift_array($var->{real_name}); + } else { + $self->error("Unknown array keyword used '".$keyword."'"); + return; + } + + $result = '' unless (defined $result); + return $result; +} + +1; \ No newline at end of file diff --git a/openkore_llm_knowledge/plugins/eventMacro/eventMacro/Utilities.pm b/openkore_llm_knowledge/plugins/eventMacro/eventMacro/Utilities.pm new file mode 100644 index 0000000000..3d06974984 --- /dev/null +++ b/openkore_llm_knowledge/plugins/eventMacro/eventMacro/Utilities.pm @@ -0,0 +1,744 @@ +# $Id: Utilities.pm r6812 2009-07-29 14:00:00Z ezza $ +package eventMacro::Utilities; + +use strict; + +require Exporter; +our @ISA = qw(Exporter); +our @EXPORT_OK = qw(q4rx q4rx2 between cmpr match getArgs getnpcID getPlayerID + getMonsterID getVenderID getItemIDs getItemPrice getInventoryIDs getInventoryTypeIDs getStorageIDs getSoldOut getInventoryAmount + getCartAmount getShopAmount getStorageAmount getVendAmount getRandom getRandomRange getConfig + getWord call_macro getArgFromList sameParty processCmd find_variable get_key_or_index getInventoryAmountbyID + getStorageAmountbyID getCartAmountbyID getQuestStatus get_pattern find_hash_and_get_keys find_hash_and_get_values + getEquipProperty getEquipCards getEquipCardAmount getEquipOptions getEquipOptionsAmount getEquipRefinement); + +use Utils; +use Globals; +use Misc qw(cardName); +use AI; +use Log qw(message error warning debug); +use Utils qw(parseArgs); + +use eventMacro::Core; +use eventMacro::Data; +use eventMacro::Lists; +use eventMacro::Automacro; +use eventMacro::FileParser; + +sub between { + if ($_[0] <= $_[1] && $_[1] <= $_[2]) {return 1} + return 0 +} + +sub cmpr { + my ($first, $cond, $second) = @_; + + if (defined $first && !defined $cond & !defined $second) { + if ($first eq '' || $first == 0) { + return 0; + } else { + return 1; + } + + } elsif ($first =~ /^\s*(-?[\d.]+)\s*\.{2}\s*(-?[\d.]+)\s*$/) { + my ($first1, $first2) = ($1, $2); + if ($second =~ /^-?[\d.]+$/) { + if ($cond eq "!=") { + return ((between($first1, $second, $first2)) ? 0 : 1); + + } elsif ($cond eq "=" || $cond eq "==" || $cond eq "=~" || $cond eq "~") { + return between($first1, $second, $first2); + + } else { + error "cmpr: Range operations must be of equality or inequality\n", "eventMacro"; + return; + } + } + error "cmpr: wrong # of arguments ($first) ($cond) ($second)\n--> ($second) <-- maybe should be numeric?\n", "eventMacro"; + return; + + } elsif ($second =~ /^\s*(-?[\d.]+)\s*\.{2}\s*(-?[\d.]+)\s*$/) { + my ($second1, $second2) = ($1, $2); + if ($first =~ /^-?[\d.]+$/) { + if ($cond eq "!=") { + return ((between($second1, $first, $second2)) ? 0 : 1); + + } elsif ($cond eq "=" || $cond eq "==" || $cond eq "=~" || $cond eq "~") { + return between($second1, $first, $second2); + + } else { + error "cmpr: Range operations must be of equality or inequality\n", "eventMacro"; + return; + } + } + error "cmpr: wrong # of arguments ($first) ($cond) ($second)\n--> ($first) <-- maybe should be numeric?\n", "eventMacro"; + return; + + } elsif ($first =~ /^-?[\d.]+$/ && $second =~ /^-?[\d.]+$/) { + return ($first == $second ? 1 : 0) if (($cond eq "=" || $cond eq "==")); + return ($first >= $second ? 1 : 0) if ($cond eq ">="); + return ($first <= $second ? 1 : 0) if ($cond eq "<="); + return ($first > $second ? 1 : 0) if ($cond eq ">"); + return ($first < $second ? 1 : 0) if ($cond eq "<"); + return ($first != $second ? 1 : 0) if ($cond eq "!="); + + } elsif (($cond eq "=" || $cond eq "==")) { + return ($first eq $second ? 1 : 0); + + } elsif ($cond eq "!=") { + return ($first ne $second ? 1 : 0); + + } elsif ($cond eq "~") { + $first = lc($first); + foreach my $member (split(/\s*,\s*/, $second)) { + return 1 if ($first eq lc($member)); + } + + } elsif ($cond eq "=~" && $second =~ /^\/.*?\/\w?\s*$/) { + return match($first, $second); + } + + return 0; +} + +sub q4rx { + my $s = $_[0]; + $s =~ s/([\/*+(){}\[\]\\\$\^?])/\\$1/g; + return $s +} + +sub q4rx2 { + # We let alone the original q4rx sub routine... + # instead, we use this for our new @nick ;p + my $s = $_[0]; + $s =~ s/([\/*+(){}\[\]\\\$\^?"'\. ])/\\$1/g; + return $s +} + +sub match { + my ($text_to_be_compared, $regex) = @_; + + unless (defined $text_to_be_compared && defined $regex) { + # this produces a warning but that's what we want + error "match: wrong # of arguments ($text_to_be_compared) ($regex)\n", "eventMacro"; + return; + } + + if ($regex =~ /^\/(.*?)\/(\w?)$/) { + if ($text_to_be_compared =~ /$1/ || ($2 eq 'i' && $text_to_be_compared =~ /$1/i)) { + no strict; + foreach my $idx (1..$#-) {$eventMacro->set_scalar_var(".lastMatch$idx",${$idx})} + use strict; + return 1; + } else { + return 0; + } + } + + return; +} + +sub getArgs { + my $arg = $_[0]; + if ($arg =~ /".*"/) { + my @ret = $arg =~ /^"(.*?)"\s+(.*?)( .*)?$/; + $ret[2] =~ s/^\s+//g if defined $ret[2]; + return @ret + } else { + return split(/\s/, $arg, 3) + } +} + +# gets word from message +sub getWord { + my ($message, $wordno) = $_[0] =~ /^"(.*?)"\s*,\s?(\d+|\$[a-zA-Z][a-zA-Z\d]*)$/s; + my @words = split(/[ ,.:;\"\'!?\r\n]/, $message); + +# this code is never used +# if ($wordno =~ /^\$/) { +# my ($val) = $wordno =~ /^\$([a-zA-Z][a-zA-Z\d]*)\s*$/; +# return "" unless defined $val; +# if ($eventMacro->get_scalar_var($val) =~ /^[1-9][0-9]*$/) {$wordno = $eventMacro->get_scalar_var($val)} +# else {return ""} +# } + + my $no = 1; + foreach (@words) { + next if /^$/; + return $_ if $no == $wordno; + $no++ + } + warning "[eventMacro] the '$wordno' number item does not exist in &arg\n", "eventMacro"; + return ""; +} + +# gets openkore setting +sub getConfig { + my ($arg1) = $_[0] =~ /^\s*(\w*\.*\w+)\s*$/; + # Basic Support for "label" in blocks. Thanks to "piroJOKE" (from Commands.pm, sub cmdConf) + if ($arg1 =~ /\./) { + $arg1 =~ s/\.+/\./; # Filter Out Unnececary dot's + my ($label, $param) = split /\./, $arg1, 2; # Split the label form parameter + foreach (%::config) { + if ($_ =~ /_\d+_label/){ # we only need those blocks witch have labels + if ($::config{$_} eq $label) { + my ($real_key, undef) = split /_label/, $_, 2; + # "<label>.block" param support. Thanks to "vit" + if ($param ne "block") { + $real_key .= "_"; + $real_key .= $param; + } + $arg1 = $real_key; + last; + }; + }; + }; + }; + return (defined $::config{$arg1})?$::config{$arg1}:""; +} + +# get quest status +# returns a hash of { id => status }, where status is: +# 'inactive' - if the quest doesn't exist or is not active +# 'incomplete' - if the quest has a timer which hasn't timed out, or incomplete kill missions +# 'complete' - if the quest is active and both timer and missions (if any) are complete +sub getQuestStatus { + my @quest_ids = @_; + my $result = {}; + foreach my $quest_id ( @quest_ids ) { + my $quest = $questList->{$quest_id}; + if ( !$quest || !$quest->{active} ) { + $result->{$quest_id} = 'inactive'; + } elsif ( $quest->{time_expire} && $quest->{time_expire} > time ) { + $result->{$quest_id} = 'incomplete'; + } elsif ( grep { $_->{mob_goal} && $_->{mob_count} < $_->{mob_goal} } values %{ $quest->{missions} } ) { + $result->{$quest_id} = 'incomplete'; + } elsif ( grep { !$_->{mob_goal} && $_->{mob_count} == 0 } values %{ $quest->{missions} } ) { + $result->{$quest_id} = 'incomplete'; + } else { + $result->{$quest_id} = 'complete'; + } + } + $result; +} + +# get NPC array index +sub getnpcID { + my $arg = $_[0]; + my ($what, $a, $b); + + if (($a, $b) = $arg =~ /^\s*(\d+) (\d+)\s*$/) {$what = 1} + elsif (($a, $b) = $arg =~ /^\s*\/(.+?)\/(\w?)\s*$/) {$what = 2} + elsif (($a) = $arg =~ /^\s*"(.*?)"\s*$/) {$what = 3} + else {return -1} + + my @ids; + foreach my $npc (@{$npcsList->getItems()}) { + if ($what == 1) {return $npc->{binID} if ($npc->{pos}{x} == $a && $npc->{pos}{y} == $b)} + elsif ($what == 2) { + if ($npc->{name} =~ /$a/ || ($b eq "i" && $npc->{name} =~ /$a/i)) {push @ids, $npc->{binID}} + } + else {return $npc->{binID} if $npc->{name} eq $a} + } + if (@ids) {return join ',', @ids} + return -1 +} + +# get player array index +sub getPlayerID { + foreach my $pl (@{$playersList->getItems()}) { + return $pl->{binID} if $pl->name eq $_[0] + } + return -1 +} + +# get monster array index +sub getMonsterID { + foreach my $ml (@{$monstersList}) { + return $ml->{binID} if ($ml->name eq $_[0] || $ml->{binType} eq $_[0] || $ml->{name_given} eq $_[0]); + } + return -1 +} + +# get vender array index +sub getVenderID { + for (my $i = 0; $i < @::venderListsID; $i++) { + next if $::venderListsID[$i] eq ""; + my $player = Actor::get($::venderListsID[$i]); + return $i if $player->name eq $_[0] + } + return -1 +} + +# get inventory item ids +# checked and ok +sub getInventoryIDs { + return unless $char->inventory->isReady(); + my $find = lc($_[0]); + my @ids; + foreach my $item (@{$char->inventory->getItems}) { + if (lc($item->name) eq $find || $item->{nameID} == $find) {push @ids, $item->{binID}} + } + unless (@ids) {push @ids, -1} + return @ids +} + +# get inventory item type ids +# checked and ok +sub getInventoryTypeIDs { + return unless $char->inventory->isReady(); + my $find = lc($_[0]); + my @ids; + foreach my $item (@{$char->inventory->getItems}) { + if ( $item->usable() eq 1 && $find eq "usable") { push @ids, $item->{binID} } + if ( $item->equippable() eq 1 && $item->{equipped} eq 0 && $find eq "equip") { push @ids, $item->{binID} } + if ( $item->usable() eq 0 && $item->equippable() eq 0 && $find eq "etc" ) { push @ids, $item->{binID} } + if ( $item->{type} eq 6 && $find eq "card" ) { push @ids, $item->{binID} } + } + unless (@ids) {push @ids, -1} + return @ids +} + +# get item array index +sub getItemIDs { + my ($item, $pool) = (lc($_[0]), $_[1]); + return if !$pool->isReady; + my @ids = map { $_->{binID} } grep { $item eq lc $_->name || $item == $_->{nameID} } @$pool; + push @ids, -1 if !@ids; + @ids; +} + +# get item price from its index +# works with @venderprice +# returns -1 if no shop is being visited +sub getItemPrice { + my ($itemIndex, $pool) = ($_[0], $_[1]); + my $price = -1; + if ($$pool[$itemIndex]) {$price = $$pool[$itemIndex]{price}} + return $price +} + +# get storage array index +# returns -1 if no matching items in storage +sub getStorageIDs { + return unless $char->storage->wasOpenedThisSession(); + my $find = lc($_[0]); + my @ids; + foreach my $item (@{$char->storage->getItems}) { + if (lc($item->name) eq $find|| $item->{nameID} == $find) {push @ids, $item->{binID}} + } + unless (@ids) {push @ids, -1} + return @ids +} + +# get amount of sold out slots +sub getSoldOut { + return 0 unless $shopstarted; + my $soldout = 0; + foreach my $aitem (@::articles) { + next unless $aitem; + if ($aitem->{quantity} == 0) {$soldout++} + } + return $soldout +} + +# get amount of an item in inventory +sub getInventoryAmount { + my $arg = lc($_[0]); + return -1 unless ($char->inventory->isReady()); + my $amount = 0; + foreach my $item (@{$char->inventory->getItems}) { + if (lc($item->name) eq $arg || $item->{nameID} == $arg) {$amount += $item->{amount}} + } + return $amount +} + +# get amount of an item in inventory by its ID +sub getInventoryAmountbyID { + my $ID = $_[0]; + return -1 unless ($char->inventory->isReady); + my $amount = 0; + foreach my $item (@{$char->inventory->getItems}) { + if ($item->{nameID} == $ID) { + $amount += $item->{amount}; + } + } + return $amount +} + +# get amount of an item in cart +sub getCartAmount { + my $arg = lc($_[0]); + return -1 unless ($char->cart->isReady()); + my $amount = 0; + foreach my $item (@{$char->cart->getItems}) { + if (lc($item->name) eq $arg || $item->{nameID} == $arg) {$amount += $item->{amount}} + } + return $amount +} + +# get amount of an item in cart by its ID +sub getCartAmountbyID { + my $ID = $_[0]; + return -1 unless ($char->cart->isReady()); + my $amount = 0; + foreach my $item (@{$char->cart->getItems}) { + if ($item->{nameID} == $ID) { + $amount += $item->{amount}; + } + } + return $amount +} + +# get amount of an item in your shop +sub getShopAmount { + my $arg = lc($_[0]); + my $amount = 0; + foreach my $aitem (@::articles) { + next unless $aitem; + if (lc($aitem->{name}) eq $arg || $aitem->{nameID} == $arg) {$amount += $aitem->{quantity}} + } + return $amount +} + +# get amount of an item in storage +# returns -1 if the storage is closed +sub getStorageAmount { + my $arg = lc($_[0]); + return -1 unless ($char->storage->wasOpenedThisSession()); + my $amount = 0; + foreach my $item (@{$char->storage->getItems}) { + if (lc($item->name) eq $arg || $item->{nameID} == $arg ) {$amount += $item->{amount}} + } + return $amount +} + +# get amount of an item in storage by its ID +# returns -1 if the storage is closed +sub getStorageAmountbyID { + my $ID = $_[0]; + return -1 unless ($char->storage->wasOpenedThisSession()); + my $amount = 0; + foreach my $item (@{$char->storage->getItems}) { + if ($item->{nameID} == $ID) { + $amount += $item->{amount}; + } + } + return $amount +} + +sub get_equipped_item_by_bin_id { + my $identifier = $_[0]; + return unless defined $identifier; + return unless $char->inventory->isReady(); + + my $item; + my $is_numeric = $identifier =~ /^\s*(\d+)\s*$/; + my $id = $1 if $is_numeric; + + if ($is_numeric) { + $item = $char->inventory->getByNameID($id); + $item ||= $char->inventory->get($id); + } else { + $item = $char->inventory->getByName($identifier); + } + + my $matched_item = $item if $item && $item->{equipped}; + + foreach my $equipped_item (@{$char->inventory->getItems}) { + next unless $equipped_item->{equipped}; + if ($is_numeric && $equipped_item->{nameID} == $id) { + $matched_item = $equipped_item; + last; + } elsif (!$is_numeric && lc($equipped_item->{name}) eq lc($identifier)) { + $matched_item = $equipped_item; + last; + } + } + + # If nothing equipped was found, fall back to the first matching item (even if unequipped) + return $matched_item || $item; +} + +sub _split_cards_and_enchants { + my $item = shift; + return ([], []) unless $item; + + my @cards = grep { $_ } unpack 'v*', $item->{cards} || ''; + return ([], []) unless @cards; + + # Strip sentinel entries such as element markers or zeroed options that + # do not represent real cards. + if ($cards[0] == 255 && @cards >= 2) { + splice @cards, 0, 2; + } + @cards = grep { $_ != 65280 } @cards; + + my @card_names = map { cardName($_) } @cards; + + my @options_like = grep { $_ !~ /carta/i } @card_names; + my @real_cards = grep { $_ =~ /carta/i } @card_names; + + return (\@real_cards, \@options_like); +} + +sub getEquipProperty { + my $item = get_equipped_item_by_bin_id($_[0]); + return 0 unless $item; + + my $element_id; + + $element_id = $item->{attribute} if defined $item->{attribute}; + + if (!defined $element_id) { + my @cards = unpack 'v*', $item->{cards} || ''; + if (@cards && $cards[0] == 255) { + $element_id = $cards[1] % 10; + } + } + + return 0 unless defined $element_id; + + return $elements_lut{$element_id} || 0; +} + +sub getEquipCards { + my $item = get_equipped_item_by_bin_id($_[0]); + return 0 unless $item; + + my ($cards, undef) = _split_cards_and_enchants($item); + return @$cards ? join(',', @$cards) : 0; +} + +sub getEquipCardAmount { + my $item = get_equipped_item_by_bin_id($_[0]); + return 0 unless $item; + + my ($cards, undef) = _split_cards_and_enchants($item); + return scalar @$cards; +} + +sub getEquipOptions { + my $item = get_equipped_item_by_bin_id($_[0]); + return 0 unless $item; + + my @options = grep { $_->{type} } map { my @c = unpack 'vvC', $_; { type => $c[0], value => $c[1], param => $c[2] } } unpack '(a5)*', $item->{options} || ''; + my ($cards, $card_options) = _split_cards_and_enchants($item); + + my @option_names = @$card_options; + foreach my $option (@options) { + if ($itemOptionHandle{$option->{type}} && $itemOption_lut{$itemOptionHandle{$option->{type}}}) { + push @option_names, sprintf($itemOption_lut{$itemOptionHandle{$option->{type}}}, $option->{value}); + } else { + push @option_names, sprintf('Option (%d, %d, %d)', $option->{type}, $option->{value}, $option->{param}); + } + } + + return @option_names ? join(',', @option_names) : 0; +} + +sub getEquipOptionsAmount { + my $item = get_equipped_item_by_bin_id($_[0]); + return 0 unless $item; + + my @options = grep { $_->{type} } map { my @c = unpack 'vvC', $_; { type => $c[0], value => $c[1], param => $c[2] } } unpack '(a5)*', $item->{options} || ''; + my (undef, $card_options) = _split_cards_and_enchants($item); + + return scalar(@options) + scalar(@$card_options); +} + +sub getEquipRefinement { + my $item = get_equipped_item_by_bin_id($_[0]); + return 0 unless $item; + + return $item->{upgrade} || 0; +} + +# get amount of items for the specifical index in another venders shop +# returns -1 if no shop is being visited +sub getVendAmount { + my ($itemIndex, $pool); + my $amount = -1; + if ($#_ == 1 and $_[0] >= 0) { + ($itemIndex, $pool) = ($_[0], $_[1]); + $amount = $$pool[$itemIndex]{amount} if ($$pool[$itemIndex]); + } elsif ($#_ > 1) { + $amount = $#_; + } + return $amount +} + +# returns random item from argument list +sub getRandom { + my $arg = $_[0]; + my @items; + my $id = 0; + while (($items[$id++]) = $arg =~ /^[, ]*"(.*?)"/) { + $arg =~ s/^[, ]*".*?"//g; + } + pop @items; + unless (@items) { + warning "[eventMacro] wrong syntax in \@random\n", "eventMacro"; + return + } + return $items[rand $id-1] +} + +# returns given argument from a comma separated list +# returns -1 if no such listID exists or when the list is empty or wrong +sub getArgFromList { + my ($listID, $list) = split(/, \s*/, $_[0]); + my @items = split(/,\s*/, $list); + unless (@items) { + warning "[eventMacro] wrong syntax in \@listItem\n", "eventMacro"; + return -1 + } + if ($items[$listID]) { + return $items[$listID] + } else { + warning "[eventMacro] the $listID number item does not exist in the list\n", "eventMacro"; + return -1 + } +} + +# check if player is in party +sub sameParty { + my $player = shift; + for (my $i = 0; $i < @partyUsersID; $i++) { + next if $partyUsersID[$i] eq ""; + next if $partyUsersID[$i] eq $accountID; + return 1 if $char->{'party'}{'users'}{$partyUsersID[$i]}{'name'} eq $player + } + return 0 +} + +# returns random number within the given range ########### +sub getRandomRange { + my ($low, $high) = split(/,\s*/, $_[0]); + return int(rand($high-$low+1))+$low if (defined $high && defined $low) +} + +sub get_pattern { + my ($inside_brackets) = @_; + my ($pattern, $var_str) = parseArgs($inside_brackets, undef, ','); + $var_str =~ s/^\s+|\s+$//gos; + return $pattern, $var_str; +} + +sub find_variable { + my ($text) = @_; + + if (my $scalar = find_scalar_variable($text)) { + return ({ display_name => $scalar->{display_name}, type => 'scalar', real_name => $scalar->{real_name} }); + } + + if (my $array = find_array_variable($text)) { + return ({ display_name => $array->{display_name}, type => 'array', real_name => $array->{real_name} }); + } + + if (my $hash = find_hash_variable($text)) { + return ({ display_name => $hash->{display_name}, type => 'hash', real_name => $hash->{real_name} }); + } + + if (my $accessed_var = find_accessed_variable($text)) { + return ({ display_name => $accessed_var->{display_name}, type => $accessed_var->{type}, real_name => $accessed_var->{real_name}, complement => $accessed_var->{complement} }); + } + + return undef; +} + +sub find_scalar_variable { + my ($text) = @_; + if ($text =~ /^($scalar_variable_qr)$/) { + my $name = $1; + $name =~ s/^\$//; + return ({display_name => ('$'.$name), real_name => $name}); + } else { + return; + } +} + +sub find_array_variable { + my ($text) = @_; + if ($text =~ /^($array_variable_qr)$/) { + my $name = $1; + $name =~ s/^\@//; + return ({display_name => ('@'.$name), real_name => $name}); + } else { + return; + } +} + +sub find_hash_variable { + my ($text) = @_; + if ($text =~ /^($hash_variable_qr)$/) { + my $name = $1; + $name =~ s/^\%//; + return ({display_name => ('%'.$name), real_name => $name}); + } else { + return; + } +} + +my %open_to_close_bracket_pair = ('[' => ']', '{' => '}'); + +sub find_accessed_variable { + my ($text) = @_; + if ($text =~ /^\$($valid_var_characters)(\[|\{)(.+)(\]|\})/) { + my $name = $1; + my $open_bracket = $2; + my $complement = $3; + return if (!defined $complement || $complement eq ''); + my $close_bracket = $4; + + my $type = ($open_bracket eq '[' ? 'accessed_array' : 'accessed_hash'); + my $close_bracket = (($type eq 'accessed_hash') ? '}' : ']'); + + if ($open_to_close_bracket_pair{$open_bracket} ne $close_bracket) { + return; + } + + if ($type eq 'accessed_array') { + return if ($complement !~ /^\d+$/ && !find_variable($complement)); + + } elsif ($type eq 'accessed_hash') { + return if ($complement !~ /^[a-zA-Z\d_]+$/ && !find_variable($complement)); + } + + my $original_name = ('$'.$name.$open_bracket.$complement.$close_bracket); + + return {real_name => $name, type => $type, display_name => $original_name, complement => $complement}; + } +} + +sub get_key_or_index { + my ($open_char, $close_char, $code) = @_; + my $counter = 0; + my $key_index = ''; + my @characters = split('',$code); + foreach my $current (@characters) { + if ($current eq $open_char) { + $counter++; + } elsif ($current eq $close_char) { + if ($counter == 0) { + return $key_index; + } else { + $counter--; + } + } + $key_index .= $current; + } + return undef; +} + +sub find_hash_and_get_keys { + my ($var) = find_variable(@_); + return @{$eventMacro->get_hash_keys($var->{real_name})}; +} + +sub find_hash_and_get_values { + my ($var) = find_variable(@_); + return @{$eventMacro->get_hash_values($var->{real_name})}; +} + +1; \ No newline at end of file diff --git a/openkore_llm_knowledge/plugins/macro/Macro/Automacro.pm b/openkore_llm_knowledge/plugins/macro/Macro/Automacro.pm new file mode 100644 index 0000000000..f93dfea5b6 --- /dev/null +++ b/openkore_llm_knowledge/plugins/macro/Macro/Automacro.pm @@ -0,0 +1,825 @@ +# $Id: Automacro.pm r6760 2009-07-06 02:23:00Z ezza $ +package Macro::Automacro; + +use strict; + +require Exporter; +our @ISA = qw(Exporter); +our @EXPORT_OK = qw(releaseAM lockAM automacroCheck consoleCheckWrapper); +our @EXPORT = qw(checkLocalTime checkVar checkVarVar checkLoc checkPersonGuild checkLevel checkLevel checkClass + checkPercent checkStatus checkItem checkPerson checkCond checkCast checkGround checkSpellsID + checkEquip checkMsg checkMonster checkAggressives checkConsole checkMapChange); + +use Misc qw(whenGroundStatus getSpellName getActorName); +use Utils; +use Globals; +use Skill; +use AI; +use Log qw(message error warning); +use Macro::Data; +use Macro::Utilities qw(between cmpr match getArgs refreshGlobal + getPlayerID getSoldOut getInventoryAmount getCartAmount getShopAmount + getStorageAmount callMacro sameParty); + +our ($rev) = q$Revision: 6760 $ =~ /(\d+)/; + +# check for variable ####################################### +sub checkVar { + my ($var, $cond, $val) = getArgs($_[0]); + + $var = "#$var" if $_[1] eq 'varvar'; + + if ($cond eq "unset") {return exists $varStack{$var}?0:1} + + # TODO: seems like this is not needed, because automacroCheck does it + # and refreshGlobal ignores args + refreshGlobal($var); + + if (exists $varStack{$var}) {return cmpr($varStack{$var}, $cond, $val)} + else {return $cond eq "!="} +} + +# check for ground statuses +sub checkGround { + my $arg = $_[0]; + my $not = ($arg =~ s/^not +//)?1:0; + + if (whenGroundStatus(calcPosition($char), $arg)) {return $not?0:1} + return $not?1:0 +} + +# checks for location ###################################### +# parameter: map [x1 y1 [x2 y2]] +# note: when looking in the default direction (north) +# x1 < x2 and y1 > y2 where (x1|y1)=(upper left) and +# (x2|y2)=(lower right) +# uses: calcPosition (Utils?) +sub checkLoc { + my $arg = $_[0]; + if ($arg =~ /,/) { + my @locs = split(/\s*,\s*/, $arg); + foreach my $l (@locs) {return 1 if checkLoc($l)} + return 0 + } + my $not = ($arg =~ s/^not +//)?1:0; + my ($map, $x1, $y1, $x2, $y2) = split(/ /, $arg); + if ($map =~ /^\s*\$/) { + my ($var) = $map =~ /^\$([a-zA-Z][a-zA-Z\d]*)\s*$/; + return 0 unless defined $var; + return 0 unless exists $varStack{$var}; + $map = $varStack{$var} + } + if ($map eq $field->baseName || $map eq $field->name) { + if ($x1 && $y1) { + my $pos = calcPosition($char); + return 0 unless defined $pos->{x} && defined $pos->{y}; + if ($x2 && $y2) { + if (between($x1, $pos->{x}, $x2) && between($y2, $pos->{y}, $y1)) { + return $not?0:1 + } + return $not?1:0 + } + if ($x1 == $pos->{x} && $y1 == $pos->{y}) { + return $not?0:1 + } + return $not?1:0 + } + return $not?0:1 + } + return $not?1:0 +} + +# check for pc local time +sub checkLocalTime { + my ($cond, $val) = $_[0] =~ /^\s*([<>=!]+)\s+(\$[a-zA-Z][a-zA-Z\d]*|\d{2}:\d{2}(?::\d{2})?)\s*$/; + return 0 if !defined $cond || !defined $val; + my ($time, $hr, $min, $sec); + if ($val =~ /^\$/) { + my ($var) = $val =~ /^\$([a-zA-Z][a-zA-Z\d]*)\s*$/; + if (exists $varStack{$var}) {$val = $varStack{$var}} + else {return 0} + } + if ($val =~ /^\d{2}:\d{2}(?::\d{2})?\s*$/) { + ($hr, $min, $sec) = split(/:/, $val, 3); + $sec = 0 if !defined $sec; + } + else {message "Wrong time format: hh:mm:ss\n", "menu"; return 0} + $time = $hr * 3600 + $min * 60 + $sec; + my ($lc_sec, $lc_min, $lc_hr) = localtime; + my $lc_time = $lc_hr * 3600 + $lc_min * 60 + $lc_sec; + + return cmpr($lc_time, $cond, $time)?1:0; +} + +# check for ($playersList guild) vs (guild.txt or guild list) +sub checkPersonGuild { + my ($guild, $trigger, $arg) = @_; + return 0 if !defined $guild || !defined $trigger || !defined $arg; + + my $actor = $arg->{player}; + + return 0 unless defined $actor->{guild}; + my $guildName = $actor->{guild}{name}; + my $dist = $config{clientSight}; + + my $not = 0; + if ($guild =~ /^not\s+/) {$not = 1; $guild =~ s/^not +//g} + if ($guild =~ /(^.*),\s*(\d+)\s*$/) {$guild = $1; $dist = $2} + + return 0 unless (distance(calcPosition($char), calcPosition($actor)) <= $dist); + + $varStack{".lastPlayerID"} = undef; + $varStack{".lastGuildName"} = undef; + $varStack{".lastGuildNameBinID"} = undef; + $varStack{".lastGuildNameBinIDDist"} = undef; + $varStack{".lastGuildNameBinIDName"} = undef; + $varStack{".lastGuildNameBinIDJobName"} = undef; + + if ($guild eq 'guild.txt') { + my @gld; + if (open(FILE, "<:utf8", Settings::getControlFilename("guild.txt"))) { + while (<FILE>) { + $_ =~ s/\x{FEFF}//g; + chomp($_); + next if ($_ =~ /^[\n\r#]/); + $_ =~ s/ +$/ /; $_ =~ s/^ +/ /; + push @gld, $_; + } + close FILE; + } + if (@gld) {$guild = join(' , ', @gld)} + else {$guild = ''} + } + + if (defined $guild && existsInList($guild, $guildName)) { + return 0 if $not; + $varStack{".lastPlayerID"} = unpack("V1", $actor->{ID}); + $varStack{".lastGuildName"} = $guildName; + $varStack{".lastGuildNameBinID"} = $actor->{binID}; + $varStack{".lastGuildNameBinIDDist"} = sprintf("%.1f", distance(calcPosition($char), calcPosition($actor))); + $varStack{".lastGuildNameBinIDName"} = $actor->{name}; + $varStack{".lastGuildNameBinIDJobName"} = $jobs_lut{$actor->{jobID}}; + return 1 + } + elsif (defined $guild && $not) { + $varStack{".lastPlayerID"} = unpack("V1", $actor->{ID}); + $varStack{".lastGuildName"} = $guildName; + $varStack{".lastGuildNameBinID"} = $actor->{binID}; + $varStack{".lastGuildNameBinIDDist"} = sprintf("%.1f", distance(calcPosition($char), calcPosition($actor))); + $varStack{".lastGuildNameBinIDName"} = $actor->{name}; + $varStack{".lastGuildNameBinIDJobName"} = $jobs_lut{$actor->{jobID}}; + return 1; + } + + return 0 +} + +# checks for base/job level ################################ +# uses cmpr (Macro::Utils) +sub checkLevel { + my ($cond, $level) = $_[0] =~ /([<>=!]+)\s+(\$[a-zA-Z][a-zA-Z\d]*|\d+|\d+\s*\.{2}\s*\d+)\s*$/; + if ($level =~ /^\s*\$/) { + my ($var) = $level =~ /^\$([a-zA-Z][a-zA-Z\d]*)\s*$/; + return 0 unless defined $var; + if (exists $varStack{$var}) {$level = $varStack{$var}} + else {return 0} + } + return cmpr($char->{$_[1]}, $cond, $level) +} + +# checks for player's jobclass ############################# +sub checkClass { + return 0 unless defined $char; + my $class = $_[0]; + if ($class =~ /^\s*\$/) { + my ($var) = $class =~ /^\$([a-zA-Z][a-zA-Z\d]*)\s*$/; + return 0 unless defined $var; + if (exists $varStack{$var}) {$class = $varStack{$var}} + else {return 0} + } + return lc($class) eq lc($::jobs_lut{$char->{jobID}})?1:0 +} + +# checks for HP/SP/Weight ################################## +# uses cmpr (Macro::Utils) +sub checkPercent { + my ($arg, $what) = @_; + my ($cond, $amount) = $arg =~ /([<>=!]+)\s+(\$[a-zA-Z][a-zA-Z\d]*%?|\d+%?|\d+\s*\.{2}\s*\d+%?)\s*$/; + if ($what =~ /^(?:hp|sp|weight)$/) { + return 0 unless (defined $char && defined $char->{$what} && defined $char->{$what."_max"}); + if ($amount =~ /^\s*(?:\d+|\d+\s*\.{2}\s*\d+)%$/ && $char->{$what."_max"}) { + $amount =~ s/%$//; + return cmpr(($char->{$what} / $char->{$what."_max"} * 100), $cond, $amount) + } + elsif ($amount =~ /^\s*\$/) { + my ($var, $percent) = $amount =~ /^\$([a-zA-Z][a-zA-Z\d]*)(%)?\s*/; + return 0 unless defined $var; + if ((defined $percent || $percent eq "%") && defined $char->{$what."_max"}) { + if (exists $varStack{$var}) { + $amount = $varStack{$var}; + return cmpr(($char->{$what} / $char->{$what."_max"} * 100), $cond, $amount) + } + } else { + if (exists $varStack{$var}) { + $amount = $varStack{$var}; + return cmpr($char->{$what}, $cond, $amount) + } + } + } + else {return cmpr($char->{$what}, $cond, $amount)} + } + elsif ($what eq 'cweight') { + return 0 unless (defined $char->cart->{weight} && defined $char->cart->{weight_max}); + if ($amount =~ /^\s*(?:\d+|\d+\s*\.{2}\s*\d+)%$/ && $char->cart->{weight_max}) { + $amount =~ s/%$//; + return cmpr(($char->cart->{weight} / $char->cart->{weight_max} * 100), $cond, $amount) + } + elsif ($amount =~ /^\s*\$/) { + my ($var, $percent) = $amount =~ /^\$([a-zA-Z][a-zA-Z\d]*)(%)?\s*/; + return 0 unless defined $var; + if ((defined $percent || $percent eq "%") && defined $char->cart->{weight_max}) { + if (exists $varStack{$var}) { + $amount = $varStack{$var}; + return cmpr(($char->cart->{weight} / $char->cart->{weight_max} * 100), $cond, $amount) + } + } else { + if (exists $varStack{$var}) { + $amount = $varStack{$var}; + return cmpr($char->cart->{weight}, $cond, $amount) + } + } + } + else {return cmpr($char->cart->{weight}, $cond, $amount)} + } + return 0 +} + +# checks for status ####################################### +sub checkStatus { + if ($_[0] =~ /,/) { + my @statuses = split(/\s*,\s*/, $_[0]); + foreach my $s (@statuses) {return 1 if checkStatus($s)} + return 0 + } + + my $status = $_[0]; + return ($status =~ s/^not +//i xor $char->statusActive($status)) if defined $char; +} + +# checks for item conditions ############################## +# uses: getInventoryAmount, getCartAmount, getShopAmount, +# getStorageAmount (Macro::Utils?) +sub checkItem { + my ($where, $check) = @_; + if ($check =~ /,/) { + my @checks = split(/\s*,\s*/, $check); + foreach my $c (@checks) {return 1 if checkItem($where, $c)} + return 0 + } + my ($item, $cond, $amount) = getArgs($check); + if ($item =~ /^\$/) { + my ($var) = $item =~ /^\$([a-zA-Z][a-zA-Z\d]*)\s*/; + return 0 unless defined $var; + if (exists $varStack{$var}) {$item = $varStack{$var}} + else {return 0} + } + if ($amount =~ /^\$/) { + my ($var1) = $amount =~ /^\$([a-zA-Z][a-zA-Z\d]*)\s*/; + return 0 unless defined $var1; + if (exists $varStack{$var1}) {$amount = $varStack{$var1}} + else {return 0} + } + my $what; + if ($where eq 'inv') {return 0 unless ($char->inventory->isReady()); $what = getInventoryAmount($item);}; + if ($where eq 'cart') {return 0 unless ($char->cart->isReady()); $what = getCartAmount($item)}; + if ($where eq 'shop') {return 0 unless $shopstarted; $what = getShopAmount($item)} + if ($where eq 'stor') {return 0 unless ($char->storage->isReady()); $what = getStorageAmount($item)} + + return cmpr($what, $cond, $amount)?1:0 +} + +# checks for near person ################################## +sub checkPerson { + my ($who, $dist) = $_[0] =~ /^(["\/].*?["\/]\w*)\s*,?\s*(.*)/; + + foreach my $player (@{$playersList->getItems()}) { + next unless match($player->name, $who); + if ($dist > 0 && distance($char->{pos_to}, $player->{pos_to}) >= $dist) { + return 0; + } + my $val = sprintf("%d %d %s", $player->{pos_to}{x}, $player->{pos_to}{y}, $field->baseName); + $varStack{".lastPlayerName"} = $player->name; + $varStack{".lastPlayerPos"} = $val; + $varStack{".lastPlayerLevel"} = $player->{lv}; + $varStack{".lastPlayerJob"} = $player->job; + $varStack{".lastPlayerAccountId"} = $player->{nameID}; + $varStack{".lastPlayerBinId"} = $player->{binID}; + return 1 + } + return 0 +} + +# checks arg1 for condition in arg3 ####################### +# uses: cmpr (Macro::Utils) +sub checkCond { + my ($cond, $amount) = $_[1] =~ /([<>=!]+)\s+(\$[a-zA-Z][a-zA-Z\d]*|\d+|\d+\s*\.{2}\s*\d+)\s*$/; + if ($amount =~ /^\s*\$/) { + my ($var) = $amount =~ /^\$([a-zA-Z][a-zA-Z\d]*)\s*$/; + return 0 unless defined $var; + if (exists $varStack{$var}) {$amount = $varStack{$var}} + else {return 0} + } + return cmpr($_[0], $cond, $amount)?1:0 +} + +# checks for equipment #################################### +# equipped <item>, <item2>, ... # equipped item or item2 or .. +# equipped rightHand <item>, rightAccessory <item2>, ... # equipped <item> on righthand etc. +# equipped leftHand none, .. # equipped nothing on lefthand etc. +# see @Item::slots +sub checkEquip { + if ($_[0] =~ /,/) { + my @equip = split(/\s*,\s*/, $_[0]); + foreach my $e (@equip) {return 1 if checkEquip($e)} + return 0 + } + + my $arg = $_[0]; + + if ($arg =~ m/^((?:top|mid|low)Head|(?:left|right)Hand|robe|armor|shoes|(?:left|right)Accessory|arrow)\s+(.*)/i) { + if (my $item = $char->{equipment}{$1}) { + return lc($2) eq lc($item->name)?1:0 + } + return lc($2) eq 'none'?1:0 + } + + $arg = lc($arg); + foreach my $s (keys %{$char->{equipment}}) { + next unless lc($char->{equipment}{$s}->name) eq $arg; + return 1 + } + return 0 +} + +# checks for a spell casted on us/party members ######################### +# uses: distance, judgeSkillArea (Utils?) +sub checkCast { + my ($cast, $args) = @_; + return 0 if $args->{sourceID} eq $accountID; + + $cast = lc($cast); + my $party = ($cast =~ s/^party +//)?1:0; + my $pos = calcPosition($char); + my $target = (defined $args->{targetID})?$args->{targetID}:0; + my $source = $args->{sourceID}; + return 0 if $target eq $source; + + if (($target eq $accountID || ($pos->{x} == $args->{x} && $pos->{y} == $args->{y}) || distance($pos, $args) <= judgeSkillArea($args->{skillID})) && existsInList($cast, lc(Skill->new(idn => $args->{skillID})->getName()))) { + if (my $actor = $monstersList->getByID($source)) { + $varStack{".caster"} = "monster"; + $varStack{".casterName"} = $actor->{name}; + $varStack{".casterID"} = $actor->{binID}; + $varStack{".casterPos"} = $actor->{pos_to}{x} ." ". $actor->{pos_to}{y}; + $varStack{".casterDist"} = sprintf("%.1f", distance($pos, calcPosition($actor))) + + } + elsif (my $actor = $playersList->getByID($source)) { + $varStack{".caster"} = "player"; + $varStack{".casterName"} = (defined $actor->{name})?$actor->{name}:"Unknown"; + $varStack{".casterID"} = $actor->{binID}; + $varStack{".casterPos"} = $actor->{pos_to}{x} ." ". $actor->{pos_to}{y}; + $varStack{".casterDist"} = sprintf("%.1f", distance($pos, calcPosition($actor))); + + } + else {return 0} + $varStack{".casterSkill"} = Skill->new(idn => $args->{skillID})->getName(); + $varStack{".casterTarget"} = $args->{x} ." ". $args->{y}; + $varStack{".casterTargetName"} = $char->{name}; + return 1 + } + elsif ($party && existsInList($cast, lc(Skill->new(idn => $args->{skillID})->getName()))) { + return 0 if !$char->{'party'} || !%{$char->{'party'}}; + if (my $actor = $monstersList->getByID($source)) { + foreach my Actor::Player $player (@{$playersList->getItems()}) { + next unless sameParty($player->{name}); + if ($target eq $player->{ID} || ($player->{pos_to}{x} == $args->{x} && $player->{pos_to}{y} == $args->{y}) || distance($player->{pos}, $args) <= judgeSkillArea($args->{skillID})) { + $varStack{".caster"} = "monster"; + $varStack{".casterName"} = $actor->{name}; + $varStack{".casterID"} = $actor->{binID}; + $varStack{".casterPos"} = $actor->{pos_to}{x} ." ". $actor->{pos_to}{y}; + $varStack{".casterSkill"} = Skill->new(idn => $args->{skillID})->getName(); + $varStack{".casterTarget"} = $args->{x} ." ". $args->{y}; + $varStack{".casterTargetName"} = $player->{name}; + $varStack{".casterDist"} = sprintf("%.1f", distance($pos, calcPosition($actor))); + return 1 + } + } + + } + elsif (my $actor = $playersList->getByID($source)) { + return 0 if sameParty($actor->{name}); + foreach my Actor::Player $player (@{$playersList->getItems()}) { + next unless sameParty($player->{name}); + if ($target eq $player->{ID} || ($player->{pos_to}{x} == $args->{x} && $player->{pos_to}{y} == $args->{y}) || distance($player->{pos}, $args) <= judgeSkillArea($args->{skillID})) { + $varStack{".caster"} = "player"; + $varStack{".casterName"} = (defined $actor->{name})?$actor->{name}:"Unknown"; + $varStack{".casterID"} = $actor->{binID}; + $varStack{".casterPos"} = $actor->{pos_to}{x} ." ". $actor->{pos_to}{y}; + $varStack{".casterSkill"} = Skill->new(idn => $args->{skillID})->getName(); + $varStack{".casterTarget"} = $args->{x} ." ". $args->{y}; + $varStack{".casterTargetName"} = $player->{name}; + $varStack{".casterDist"} = sprintf("%.1f", distance($pos, calcPosition($actor))); + return 1 + } + } + } + else {return 0} + } + else {return 0} +} + +# checks for public, private, party or guild message ###### +# uses calcPosition, distance (Utils?) +sub checkMsg { + my ($var, $tmp, $arg) = @_; + my $msg; + if ($var eq '.lastpub') { + ($msg, my $distance) = $tmp =~ /^([\/"].*?[\/"]\w*)\s*,?\s*(\d*)/; + if ($distance ne '') { + my $mypos = calcPosition($char); + my $pos = calcPosition($::players{$arg->{pubID}}); + return 0 unless distance($mypos, $pos) <= $distance + } + } elsif ($var eq '.lastpm') { + ($msg, my $allowed) = $tmp =~ /^([\/"].*?[\/"]\w*)\s*,?\s*(.*)/; + my $auth; + if (!$allowed) { + $auth = 1 + } else { + my @tfld = split(/,/, $allowed); + for (my $i = 0; $i < @tfld; $i++) { + next unless defined $tfld[$i]; + $tfld[$i] =~ s/(?:^ +| +$)//g; + if ($arg->{privMsgUser} eq $tfld[$i]) {$auth = 1; last} + } + } + return 0 unless $auth + } elsif ($var eq '.lastsys') { + ($msg) = $tmp =~ /^([\/"].*?[\/"]\w*)\s*,?\s*(.*)/; + chomp($msg); + } else { + $msg = $tmp + } + $arg->{Msg} =~ s/[\r\n]*$//g; + if (match($arg->{Msg},$msg)){ + $varStack{$var} = $arg->{MsgUser}; + $varStack{$var."Msg"} = $arg->{Msg}; + return 1 + } + return 0 +} + +# checks for area spell +sub checkSpellsID { + my ($line, $args) = @_; + my $dist = $config{clientSight} || 20; + my ($list, $cond); + if ($line =~ /^\s*(.*),?\s+([<>=!~]+)\s+(\d+|\d+\s*.{2}\s*\d+)\s*$/) { + ($list, $cond, $dist) = ($1, $2, $3) + } + else {$list = $line; $cond = "<="} + + foreach (@spellsID) { + my $spell = $spells{$_}; + my $type = getSpellName($spell->{type}); + my $dist1 = sprintf("%.1f",distance(calcPosition($char), calcPosition($spell))); + my ($actor, $owner, $ID) = getActorName($spell->{sourceID}) =~ /^(\w+?)\s(.*?)\s\((\d+)\)\s*$/; + if (existsInList($list, $type) && + $args->{x} eq $spell->{'pos'}{'x'} && + $args->{y} eq $spell->{'pos'}{'y'} && + $args->{sourceID} eq $spell->{sourceID} + ) { + $varStack{".areaName"} = $type; + $varStack{".areaActor"} = $actor; + $varStack{".areaSourceName"} = $owner; + $varStack{".areaSourceID"} = $ID; + $varStack{".areaPos"} = sprintf("%d %d %s", $spell->{'pos'}{'x'}, $spell->{'pos'}{'y'}, $field->baseName); + $varStack{".areaDist"} = $dist1; + return cmpr($dist1, $cond, $dist) + } + } + return 0 +} + +# checks for monster ... +sub checkMonster { + my $line = $_[0]; + my $not = $_[1]; + my ($mercenary, $use, $monsterList, $cond); + my $mondist = $config{clientSight} || 20; + + if ($line =~ /^\s*(.*),?\s+([<>=!~]+)\s+(\d+|\d+\s*.{2}\s*\d+)\s*$/) { + ($monsterList, $cond, $mondist) = ($1, $2, $3) + } else { + $monsterList = $line; + $cond = "<=" + } + + if (!$not && $monsterList =~ /^(not|mercenary)\s+(.*)\s*$/) { + if ($1 eq "not") {$not = 1; $monsterList = $2} + else {$mercenary = 1; $use = 1; $monsterList = $2} + } + + foreach (@monstersID) { + next unless defined $_; + if ($mercenary) { + #Whose the mercenary's master, + #update later ;p + my $mypos = calcPosition($char); + my $pos = calcPosition($monsters{$_}); + my $dist = sprintf("%.1f",distance($pos, $mypos)); + if (existsInList($monsterList, $monsters{$_}->{name}) && $dist < 3) {$use = 0; last} + } + elsif ($not) { + next if existsInList($monsterList, $monsters{$_}->{name}); + my $mypos = calcPosition($char); + my $pos = calcPosition($monsters{$_}); + my $dist = sprintf("%.1f",distance($pos, $mypos)); + my $val = sprintf("%d %d %s", $pos->{x}, $pos->{y}, $field->baseName); + $varStack{".lastMonster"} = $monsters{$_}->{name}; + $varStack{".lastMonsterPos"} = $val; + $varStack{".lastMonsterDist"} = $dist; + $varStack{".lastMonsterID"} = $monsters{$_}->{binID}; + $varStack{".lastMonsterBinID"} = $monsters{$_}->{binType}; + return cmpr($dist, $cond, $mondist) + } else { + if (existsInList($monsterList, $monsters{$_}->{name})) { + my $counter; + my $mypos = calcPosition($char); + my $pos = calcPosition($monsters{$_}); + my $dist = sprintf("%.1f", distance($mypos, $pos)); + my $val = sprintf("%d %d %s", $pos->{x}, $pos->{y}, $field->baseName); + $varStack{".lastMonster"} = $monsters{$_}->{name}; + $varStack{".lastMonsterPos"} = $val; + $varStack{".lastMonsterDist"} = $dist; + $varStack{".lastMonsterID"} = $monsters{$_}->{binID}; + $varStack{".lastMonsterBinID"} = $monsters{$_}->{binType}; + for (my $i = 0; $i < @::monstersID; $i++) { + next if $::monstersID[$i] eq ""; + my $monster = Actor::get($::monstersID[$i]); + if ($monster->name eq $monsters{$_}->{name}) { + if ($monster->{binID} eq $monsters{$_}->{binID}) { + $counter++; + next + } else { + my $monsToMonDist = sprintf("%.1f",distance($pos, $monster->{pos_to})); + $counter++ if $monsToMonDist < 12; + next + } + } + next + } + $varStack{".lastMonsterCount"} = $counter; + return cmpr($dist, $cond, $mondist) + } + } + } + return 1 if ($use); + return 0 +} + +# checks for aggressives +sub checkAggressives { + my ($cond, $amount) = $_[0] =~ /([<>=!]+)\s+(\$[a-zA-Z][a-zA-Z\d]*|\d+|\d+\s*\.{2}\s*\d+)\s*$/; + if ($amount =~ /^\s*\$/) { + my ($var) = $amount =~ /^\$([a-zA-Z][a-zA-Z\d]*)\s*$/; + return 0 unless defined $var; + if (exists $varStack{$var}) {$amount = $varStack{$var}} + else {return 0} + } + return cmpr(scalar ai_getAggressives, $cond, $amount) +} +# checks for console message +sub checkConsole { + my ($msg, $arg) = @_; + $$arg[4] =~ s/[\r\n]*$//; + if (match($$arg[4],$msg)){ + $varStack{".lastLogMsg"} = $$arg[4]; + return 1 + } + return 0 +} + +sub consoleCheckWrapper { + return unless defined $conState; + # skip "macro" and "cvsdebug" domains to avoid loops + return if $_[1] =~ /^(?:macro|cvsdebug)$/; + # skip debug messages unless macro_allowDebug is set + return if ($_[0] eq 'debug' && !$::config{macro_allowDebug}); + my @args = @_; + automacroCheck("log", \@args) +} + +# checks for map change +sub checkMapChange { + return ($_[0] eq 'any' || $_[0] eq '*' || existsInList($_[0], $field->baseName))?1:0 +} + +# checks for eval +sub checkEval { + return if $Settings::lockdown; + + #if ($_[0] =~ /;/) { + # my @eval = split(/\s*;\s*/, $_[0]); + # foreach my $e (@eval) {return 1 if checkEval($e)} + # return 0 + #} + return eval $_[0]; +} + + +# releases a locked automacro ################## +sub releaseAM { + if ($_[0] eq 'all') { + foreach (keys %automacro) { + undef $automacro{$_}->{disabled} + } + return 1 + } + if (defined $automacro{$_[0]}) { + undef $automacro{$_[0]}->{disabled}; + return 1 + } + return 0 +} + +# locks an automacro ################## +sub lockAM { + if ($_[0] eq 'all') { + foreach (keys %automacro) { + $automacro{$_}->{disabled} = 1 + } + return 1 + } + if (defined $automacro{$_[0]}) { + $automacro{$_[0]}->{disabled} = 1; + return 1 + } + return 0 +} + +# parses automacros and checks conditions ################# +sub automacroCheck { + my ($trigger, $args) = @_; + return unless ($conState == 5 && $char) || $trigger =~ /^(?:charSelectScreen|Network|packet)/; + + # do not run an automacro if there's already a macro running and the running + # macro is non-interruptible + return if (defined $queue && !$queue->interruptible); + + refreshGlobal(); + + CHKAM: + foreach my $am (sort { + ($automacro{$a}->{priority} or 0) <=> ($automacro{$b}->{priority} or 0) + } keys %automacro) { + next CHKAM if $automacro{$am}->{disabled}; + + if (defined $automacro{$am}->{call} && !defined $macro{$automacro{$am}->{call}}) { + error "automacro $am: macro ".$automacro{$am}->{call}." not found.\n"; + $automacro{$am}->{disabled} = 1; return + } + + if (defined $automacro{$am}->{timeout}) { + $automacro{$am}->{time} = 0 unless $automacro{$am}->{time}; + my %tmptimer = (timeout => $automacro{$am}->{timeout}, time => $automacro{$am}->{time}); + next CHKAM unless timeOut(\%tmptimer) + } + + if (defined $automacro{$am}->{hook}) { + next CHKAM unless $trigger eq $automacro{$am}->{hook}; + # save arguments + my $s = 0; + foreach my $save (@{$automacro{$am}->{save}}) { + if (defined $args->{$save}) { + if (ref($args->{$save}) eq 'SCALAR') { + $varStack{".hooksave$s"} = ${$args->{$save}} + } else { + if (!$::config{macro_nowarn} && ref($args->{$save}) ne '') { + warning "[macro] \$.hooksave$s is of type ".ref($args->{$save}).". Take care!\n" + } + $varStack{".hooksave$s"} = $args->{$save} + } + } else { + error "[macro] \$args->{$save} does not exist\n" + } + $s++ + } + } elsif (defined $automacro{$am}->{console}) { + if ($trigger eq 'log') { + next CHKAM unless checkConsole($automacro{$am}->{console}, $args) + } else {next CHKAM} + } elsif (defined $automacro{$am}->{spell}) { + if ($trigger =~ /^(?:is_casting|packet_skilluse)$/) { + next CHKAM unless checkCast($automacro{$am}->{spell}, $args) + } else {next CHKAM} + } elsif (defined $automacro{$am}->{pm}) { + if ($trigger eq 'packet_privMsg') { + next CHKAM unless checkMsg(".lastpm", $automacro{$am}->{pm}, $args) + } else {next CHKAM} + } elsif (defined $automacro{$am}->{pubm}) { + if ($trigger eq 'packet_pubMsg') { + next CHKAM unless checkMsg(".lastpub", $automacro{$am}->{pubm}, $args) + } else {next CHKAM} + } elsif (defined $automacro{$am}->{system}) { + if ($trigger eq 'packet_sysMsg') { + next CHKAM unless checkMsg(".lastsys", $automacro{$am}->{system}, $args) + } else {next CHKAM} + } elsif (defined $automacro{$am}->{party}) { + if ($trigger eq 'packet_partyMsg') { + next CHKAM unless checkMsg(".lastparty", $automacro{$am}->{party}, $args) + } else {next CHKAM} + } elsif (defined $automacro{$am}->{guild}) { + if ($trigger eq 'packet_guildMsg') { + next CHKAM unless checkMsg(".lastguild", $automacro{$am}->{guild}, $args) + } else {next CHKAM} + } elsif (defined $automacro{$am}->{mapchange}) { + if ($trigger eq 'packet_mapChange') { + next CHKAM unless checkMapChange($automacro{$am}->{mapchange}) + } else {next CHKAM} + } elsif (defined $automacro{$am}->{playerguild}) { + if (($trigger eq 'player') || ($trigger eq 'charNameUpdate')) { + next CHKAM unless checkPersonGuild($automacro{$am}->{playerguild},$trigger,$args) + } else {next CHKAM} + } elsif (defined $automacro{$am}->{areaSpell}) { + if ($trigger eq 'packet_areaSpell') { + next CHKAM unless checkSpellsID($automacro{$am}->{areaSpell}, $args) + } else {next CHKAM} + } + + next CHKAM if (defined $automacro{$am}->{map} && $automacro{$am}->{map} ne $field->baseName); + next CHKAM if (defined $automacro{$am}->{class} && !checkClass($automacro{$am}->{class})); + next CHKAM if (defined $automacro{$am}->{eval} && !checkEval($automacro{$am}->{eval})); + next CHKAM if (defined $automacro{$am}->{whenGround} && !checkGround($automacro{$am}->{whenGround})); + next CHKAM if (defined $automacro{$am}->{notMonster} && !checkMonster($automacro{$am}->{notMonster}, 1)); + + foreach my $i (@{$automacro{$am}->{monster}}) {next CHKAM unless checkMonster($i)} + foreach my $i (@{$automacro{$am}->{aggressives}}){next CHKAM unless checkAggressives($i)} + foreach my $i (@{$automacro{$am}->{location}}) {next CHKAM unless checkLoc($i)} + foreach my $i (@{$automacro{$am}->{localtime}}) {next CHKAM unless checkLocalTime($i, "")} + foreach my $i (@{$automacro{$am}->{var}}) {next CHKAM unless checkVar($i, "")} + foreach my $i (@{$automacro{$am}->{varvar}}) {next CHKAM unless checkVar($i, "varvar")} + foreach my $i (@{$automacro{$am}->{base}}) {next CHKAM unless checkLevel($i, "lv")} + foreach my $i (@{$automacro{$am}->{job}}) {next CHKAM unless checkLevel($i, "lv_job")} + foreach my $i (@{$automacro{$am}->{hp}}) {next CHKAM unless checkPercent($i, "hp")} + foreach my $i (@{$automacro{$am}->{sp}}) {next CHKAM unless checkPercent($i, "sp")} + foreach my $i (@{$automacro{$am}->{spirit}}) {next CHKAM unless checkCond($char->{spirits} || 0, $i)} + foreach my $i (@{$automacro{$am}->{weight}}) {next CHKAM unless checkPercent($i, "weight")} + foreach my $i (@{$automacro{$am}->{cartweight}}) {next CHKAM unless checkPercent($i, "cweight")} + foreach my $i (@{$automacro{$am}->{soldout}}) {next CHKAM unless checkCond(getSoldOut(), $i)} + foreach my $i (@{$automacro{$am}->{zeny}}) {next CHKAM unless checkCond($char->{zeny}, $i)} + foreach my $i (@{$automacro{$am}->{cash}}) {next CHKAM unless checkCond($cashShop{points}->{cash}?$cashShop{points}->{cash}:0, $i)} + foreach my $i (@{$automacro{$am}->{player}}) {next CHKAM unless checkPerson($i)} + foreach my $i (@{$automacro{$am}->{equipped}}) {next CHKAM unless checkEquip($i)} + foreach my $i (@{$automacro{$am}->{status}}) {next CHKAM unless checkStatus($i)} + foreach my $i (@{$automacro{$am}->{inventory}}) {next CHKAM unless checkItem("inv", $i)} + foreach my $i (@{$automacro{$am}->{storage}}) {next CHKAM unless checkItem("stor", $i)} + foreach my $i (@{$automacro{$am}->{shop}}) {next CHKAM unless checkItem("shop", $i)} + foreach my $i (@{$automacro{$am}->{cart}}) {next CHKAM unless checkItem("cart", $i)} + + message "[macro] automacro $am triggered.\n", "macro"; + + unless (defined $automacro{$am}->{call} || $::config{macro_nowarn}) { + warning "[macro] automacro $am: call not defined.\n", "macro" + } + + $automacro{$am}->{time} = time if $automacro{$am}->{timeout}; + $automacro{$am}->{disabled} = 1 if $automacro{$am}->{'run-once'}; + + foreach my $i (@{$automacro{$am}->{set}}) { + my ($var, $val) = $i =~ /^(.*?)\s+(.*)/; + $varStack{$var} = $val + } + + if (defined $automacro{$am}->{call}) { + undef $queue if defined $queue; + $queue = new Macro::Script($automacro{$am}->{call}); + if (defined $queue) { + $queue->overrideAI(1) if $automacro{$am}->{overrideAI}; + $queue->interruptible(0) if $automacro{$am}->{exclusive}; + $queue->orphan($automacro{$am}->{orphan}) if defined $automacro{$am}->{orphan}; + $queue->timeout($automacro{$am}->{delay}) if $automacro{$am}->{delay}; + $queue->setMacro_delay($automacro{$am}->{macro_delay}) if $automacro{$am}->{macro_delay}; + $varStack{".caller"} = $am; + $onHold = 0; + callMacro + } else { + error "unable to create macro queue.\n" + } + } + + return # don't execute multiple macros at once + } +} + +1; diff --git a/openkore_llm_knowledge/plugins/macro/Macro/Data.pm b/openkore_llm_knowledge/plugins/macro/Macro/Data.pm new file mode 100644 index 0000000000..a2a4357eb0 --- /dev/null +++ b/openkore_llm_knowledge/plugins/macro/Macro/Data.pm @@ -0,0 +1,102 @@ +# $Id: Data.pm r6753 2009-07-02 00:43:00Z ezza $ +package Macro::Data; + +use strict; + +require Exporter; +our ($rev) = q$Revision: 6753 $ =~ /(\d+)/; +our @ISA = qw(Exporter); +our @EXPORT = qw(@perl_name %macro %automacro %varStack $queue $onHold %amSingle %amMulti $macroKeywords); + +our @perl_name; +our %macro; +our %automacro; +our %varStack; +our $queue; +our $onHold; +our $inBlock; + +our %amSingle = ( + 'map' => 1, # map check + 'mapchange' => 1, # map change check + 'class' => 1, # job class check + 'timeout' => 1, # setting: re-check timeout + 'delay' => 1, # option: delay before the macro starts + 'run-once' => 1, # option: run automacro only once + 'disabled' => 1, # option: automacro disabled + 'call' => 1, # setting: macro to be called + 'spell' => 1, # check: cast sensor + 'notMonster' => 1, # check: disallow monsters other than ~ + 'pm' => 1, # check: private message + 'pubm' => 1, # check: public chat + 'system' => 1, # check: system chat + 'guild' => 1, # check: guild chat + 'party' => 1, # check: party chat + 'console' => 1, # check: console message + 'overrideAI' => 1, # option: override AI + 'orphan' => 1, # option: orphan handling + 'macro_delay' => 1, # option: default macro delay + 'hook' => 1, # check: openkore hook + 'priority' => 1, # option: automacro priority + 'exclusive' => 1, # option: is macro interruptible + 'playerguild' => 1, # check: player guilds + 'eval' => 1, # check : eval + 'whenGround' => 1, # check : when ground statuses + 'areaSpell' => 1 # check : area spell +); + +our %amMulti = ( + 'set' => 1, # set: variable + 'save' => 1, # setting: save hook arguments + 'monster' => 1, # check: monster on screen + 'aggressives' => 1, # check: aggressives + 'location' => 1, # check: player's location + 'var' => 1, # check: variable / value + 'varvar' => 1, # check: nested variable / value + 'base' => 1, # check: base level + 'job' => 1, # check: job level + 'hp' => 1, # check: player's hp + 'sp' => 1, # check: player's sp + 'spirit' => 1, # check: spirit spheres + 'weight' => 1, # check: player's weight + 'cartweight' => 1, # check: cart weight + 'soldout' => 1, # check: sold out shop slots + 'zeny' => 1, # check: player's zeny + 'cash' => 1, # check: player's cash + 'player' => 1, # check: player name near + 'equipped' => 1, # check: equipment + 'status' => 1, # check: player's status + 'inventory' => 1, # check: item amount in inventory + 'storage' => 1, # check: item amount in storage + 'shop' => 1, # check: item amount in shop + 'cart' => 1, # check: item amount in cart + 'localtime' => 1 # check: localtime +); + +our $macroKeywords = + "npc" . "|" . + "store" . "|" . + "player" . "|" . + "monster" . "|" . + "venderitem" . "|" . + "venderprice" . "|" . + "venderamount" . "|" . + "random" . "|" . + "rand" . "|" . + "invamount" . "|" . + "cartamount" . "|" . + "shopamount" . "|" . + "storamount" . "|" . + "[Ii]nventory" . "|" . + "[Ss]torage" . "|" . + "[Cc]art" . "|" . + "vender" . "|" . + "config" . "|" . + "eval" . "|" . + "arg" . "|" . + "listitem" . "|" . + "nick" . "|" . + "listlenght" +; + +1; diff --git a/openkore_llm_knowledge/plugins/macro/Macro/Parser.pm b/openkore_llm_knowledge/plugins/macro/Macro/Parser.pm new file mode 100644 index 0000000000..6d7289286d --- /dev/null +++ b/openkore_llm_knowledge/plugins/macro/Macro/Parser.pm @@ -0,0 +1,375 @@ +# $Id: Parser.pm r6759 2009-07-05 04:00:00Z ezza $ +package Macro::Parser; + +use strict; + +require Exporter; +our @ISA = qw(Exporter); +our @EXPORT = qw(parseMacroFile parseCmd isNewCommandBlock); +our @EKSPORT_OK = qw(parseCmd isNewCommandBlock); + +use Globals; +use Utils qw/existsInList/; +use List::Util qw(max min sum); +use Log qw(message warning error); +use Text::Balanced qw/extract_bracketed/; +use Macro::Data; +use Macro::Utilities qw(refreshGlobal getnpcID getItemIDs getItemPrice getStorageIDs getInventoryIDs + getPlayerID getMonsterID getVenderID getRandom getRandomRange getInventoryAmount getCartAmount getShopAmount + getStorageAmount getVendAmount getConfig getWord q4rx q4rx2 getArgFromList getListLenght); + +our ($rev) = q$Revision: 6759 $ =~ /(\d+)/; + +my $tempmacro = 0; + +# adapted config file parser +sub parseMacroFile { + my ($file, $no_undef) = @_; + unless ($no_undef) { + undef %macro; + undef %automacro; + undef @perl_name + } + + my %block; + my $inBlock = 0; + my $macroCountOpenBlock = 0; + my ($macro_subs, @perl_lines); + open my $fp, "<:utf8", $file or return 0; + while (<$fp>) { + $. == 1 && s/^\x{FEFF}//; # utf bom + s/(.*)[\s\t]+#.*$/$1/; # remove last comments + s/^\s*#.*$//; # remove comments + s/^\s*//; # remove leading whitespaces + s/\s*[\r\n]?$//g; # remove trailing whitespaces and eol + s/ +/ /g; # trim down spaces - very cool for user's string data? + next unless ($_); + + if (!%block && /{$/) { + my ($key, $value) = $_ =~ /^(.*?)\s+(.*?)\s*{$/; + if ($key eq 'macro') { + %block = (name => $value, type => "macro"); + $macro{$value} = [] + } elsif ($key eq 'automacro') { + %block = (name => $value, type => "auto") + } elsif ($key eq 'sub') { + %block = (name => $value, type => "sub") + } else { + %block = (type => "bogus"); + warning "$file: ignoring line '$_' in line $. (munch, munch, strange block)\n" + } + next + } + + if (%block && $block{type} eq "macro") { + if ($_ eq "}") { + if ($macroCountOpenBlock) { # If the '}' is being used to terminate a block of commands from 'if' + push(@{$macro{$block{name}}}, '}'); + + if ($macroCountOpenBlock) { + $macroCountOpenBlock--; + } + } else { + undef %block + } + } else { + if (isNewCommandBlock($_)) { + $macroCountOpenBlock++ + } elsif (!$macroCountOpenBlock && ($_ =~ /^}\s*else\s*{$/ || $_ =~ /}\s*elsif.*{$/ || $_ =~ /^case.*{$/ || $_ =~ /^else*{$/)) { + warning "$file: ignoring '$_' in line $. (munch, munch, not found the open block command)\n"; + next + } + + push(@{$macro{$block{name}}}, $_); + } + + next + } + + if (%block && $block{type} eq "auto") { + if ($_ eq "}") { + if ($block{loadmacro}) { + if ($macroCountOpenBlock) { + push(@{$macro{$block{loadmacro_name}}}, '}'); + + if ($macroCountOpenBlock) { + $macroCountOpenBlock--; + } + } else { + undef $block{loadmacro} + } + } else { + undef %block + } + } elsif ($_ eq "call {") { + $block{loadmacro} = 1; + $block{loadmacro_name} = "tempMacro".$tempmacro++; + $automacro{$block{name}}->{call} = $block{loadmacro_name}; + $macro{$block{loadmacro_name}} = [] + } elsif ($block{loadmacro}) { + if (isNewCommandBlock($_)) { + $macroCountOpenBlock++ + } elsif (!$macroCountOpenBlock && ($_ =~ /^}\s*else\s*{$/ || $_ =~ /}\s*elsif.*{$/ || $_ =~ /^case.*{$/ || $_ =~ /^else*{$/)) { + warning "$file: ignoring '$_' in line $. (munch, munch, not found the open block command)\n"; + next + } + + push(@{$macro{$block{loadmacro_name}}}, $_); + } else { + my ($key, $value) = $_ =~ /^(.*?)\s+(.*)/; + unless (defined $key) { + warning "$file: ignoring '$_' in line $. (munch, munch, not a pair)\n"; + next + } + if ($amSingle{$key}) { + $automacro{$block{name}}->{$key} = $value + } elsif ($amMulti{$key}) { + push(@{$automacro{$block{name}}->{$key}}, $value) + } else { + warning "$file: ignoring '$_' in line $. (munch, munch, unknown automacro keyword)\n" + } + } + + next + } + + if (%block && $block{type} eq "sub") { + if ($_ eq "}") { + if ($inBlock > 0) { + push(@perl_lines, $_); + $inBlock--; + next + } + $macro_subs = join('', @perl_lines); + sub_execute($block{name}, $macro_subs); + push(@perl_name, $block{name}) unless existsInList(join(',', @perl_name), $block{name}); + undef %block; undef @perl_lines; undef $macro_subs; + $inBlock = 0 + } + elsif ($_ =~ /^}.*?{$/ && $inBlock > 0) {push(@perl_lines, $_)} + elsif ($_ =~ /{$/) {$inBlock++; push(@perl_lines, $_)} + else {push(@perl_lines, $_)} + next + } + + if (%block && $block{type} eq "bogus") { + if ($_ eq "}") {undef %block} + next + } + + my ($key, $value) = $_ =~ /(?:^(.*?)\s|})+(.*)/; + unless (defined $key) { + warning "$file: ignoring '$_' in line $. (munch, munch, strange food)\n"; + next + } + + if ($key eq "!include") { + my $f = $value; + if (!File::Spec->file_name_is_absolute($value) && $value !~ /^\//) { + if ($file =~ /[\/\\]/) { + $f = $file; + $f =~ s/(.*)[\/\\].*/$1/; + $f = File::Spec->catfile($f, $value) + } else { + $f = $value + } + } + if (-f $f) { + my $ret = parseMacroFile($f, 1); + return $ret unless $ret + } else { + error "$file: Include file not found: $f\n"; + return 0 + } + } + } + #close $fp; + return 0 if %block; + return 1 +} + +sub sub_execute { + return if $Settings::lockdown; + + my ($name, $arg) = @_; + my $run = "sub ".$name." {".$arg."}"; + eval($run); # cycle the macro sub between macros only + $run = "eval ".$run; + Commands::run($run); # exporting sub to the &main::sub, becarefull on your sub name + # dont name your new sub equal to any &main::sub, you should take + # the risk yourself. + message "[macro] registering sub $name ...\n", "menu"; +} + +# parses a text for keywords and returns keyword + argument as array +# should be an adequate workaround for the parser bug +#sub parseKw { +# my @pair = $_[0] =~ /\@($macroKeywords)\s*\(\s*(.*)\s*\)/i; +# return unless @pair; +# if ($pair[0] eq 'arg') { +# return $_[0] =~ /\@(arg)\s*\(\s*(".*?",\s*\d+)\s*\)/ +# } elsif ($pair[0] eq 'random') { +# return $_[0] =~ /\@(random)\s*\(\s*(".*?")\s*\)/ +# } +# while ($pair[1] =~ /\@($macroKeywords)\s*\(/) { +# @pair = $pair[1] =~ /\@($macroKeywords)\s*\((.*)/ +# } +# return @pair +#} + +sub parseKw { + my @full = $_[0] =~ /@($macroKeywords)s*((s*(.*?)s*).*)$/i; + my @pair = ($full[0]); + my ($bracketed) = extract_bracketed ($full[1], '()'); + return unless $bracketed; + push @pair, substr ($bracketed, 1, -1); + + return unless @pair; + if ($pair[0] eq 'arg') { + return $_[0] =~ /\@(arg)\s*\(\s*(".*?",\s*(\d+|\$[a-zA-Z][a-zA-Z\d]*))\s*\)/ + } elsif ($pair[0] eq 'random') { + return $_[0] =~ /\@(random)\s*\(\s*(".*?")\s*\)/ + } + while ($pair[1] =~ /\@($macroKeywords)\s*\(/) { + @pair = parseKw ($pair[1]) + } + return @pair +} + +# parses all macro perl sub-routine found in the macro script +sub parseSub { + #Taken from sub parseKw :D + my @full = $_[0] =~ /(?:^|\s+)(\w+)s*((s*(.*?)s*).*)$/i; + my @pair = ($full[0]); + my ($bracketed) = extract_bracketed ($full[1], '()'); + return unless $bracketed; + push @pair, substr ($bracketed, 1, -1); + + return unless @pair; + + while ($pair[1] =~ /(?:^|\s+)(\w+)\s*\(/) { + @pair = parseSub ($pair[1]) + } + + return @pair +} + +# substitute variables +sub subvars { +# should be working now + my ($pre, $nick) = @_; + my ($var, $tmp); + + # variables + $pre =~ s/(?:^|(?<=[^\\]))\$(\.?[a-z][a-z\d]*)/defined $varStack{$1} ? $varStack{$1} : ''/gei; +=pod + while (($var) = $pre =~ /(?:^|[^\\])\$(\.?[a-z][a-z\d]*)/i) { + $tmp = (defined $varStack{$var})?$varStack{$var}:""; + $var = q4rx $var; + $pre =~ s/(^|[^\\])\$$var([^a-zA-Z\d]|$)/$1$tmp$2/g; + last if defined $nick + } +=cut + + # doublevars + $pre =~ s/\$\{(.*?)\}/defined $varStack{"#$1"} ? $varStack{"#$1"} : ''/gei; +=pod + while (($var) = $pre =~ /\$\{(.*?)\}/i) { + $tmp = (defined $varStack{"#$var"})?$varStack{"#$var"}:""; + $var = q4rx $var; + $pre =~ s/\$\{$var\}/$tmp/g + } +=cut + + return $pre +} + +# command line parser for macro +# returns undef if something went wrong, else the parsed command or "". +sub parseCmd { + my ($cmd, $self) = @_; + return "" unless defined $cmd; + my ($kw, $arg, $targ, $ret, $sub, $val); + + # refresh global vars only once per command line + refreshGlobal(); + + while (($kw, $targ) = parseKw($cmd)) { + $ret = "_%_"; + # first parse _then_ substitute. slower but more safe + $arg = subvars($targ) unless $kw eq 'nick'; + my $randomized = 0; + + if ($kw eq 'npc') {$ret = getnpcID($arg)} + elsif ($kw eq 'cart') {($ret) = getItemIDs($arg, $char->cart->getItems)} + elsif ($kw eq 'Cart') {$ret = join ',', getItemIDs($arg, $char->cart->getItems)} + elsif ($kw eq 'inventory') {($ret) = getInventoryIDs($arg)} + elsif ($kw eq 'Inventory') {$ret = join ',', getInventoryIDs($arg)} + elsif ($kw eq 'store') {($ret) = getItemIDs($arg, $storeList->getItems)} + elsif ($kw eq 'storage') {($ret) = getStorageIDs($arg)} + elsif ($kw eq 'Storage') {$ret = join ',', getStorageIDs($arg)} + elsif ($kw eq 'player') {$ret = getPlayerID($arg)} + elsif ($kw eq 'monster') {$ret = getMonsterID($arg)} + elsif ($kw eq 'vender') {$ret = getVenderID($arg)} + elsif ($kw eq 'venderitem') {($ret) = getItemIDs($arg, $venderItemList->getItems)} + elsif ($kw eq 'venderItem') {$ret = join ',', getItemIDs($arg, $venderItemList->getItems)} + elsif ($kw eq 'venderprice'){$ret = getItemPrice($arg, $venderItemList->getItems)} + elsif ($kw eq 'venderamount'){$ret = getVendAmount($arg, $venderItemList->getItems)} + elsif ($kw eq 'random') {$ret = getRandom($arg); $randomized = 1} + elsif ($kw eq 'rand') {$ret = getRandomRange($arg); $randomized = 1} + elsif ($kw eq 'invamount') {$ret = getInventoryAmount($arg)} + elsif ($kw eq 'cartamount') {$ret = getCartAmount($arg)} + elsif ($kw eq 'shopamount') {$ret = getShopAmount($arg)} + elsif ($kw eq 'storamount') {$ret = getStorageAmount($arg)} + elsif ($kw eq 'config') {$ret = getConfig($arg)} + elsif ($kw eq 'arg') {$ret = getWord($arg)} + elsif ($kw eq 'eval') {$ret = eval($arg) unless $Settings::lockdown} + elsif ($kw eq 'listitem') {$ret = getArgFromList($arg)} + elsif ($kw eq 'listlenght') {$ret = getListLenght($arg)} + elsif ($kw eq 'nick') {$arg = subvars($targ, 1); $ret = q4rx2($arg)} + return unless defined $ret; + return $cmd if $ret eq '_%_'; + $targ = q4rx $targ; + unless ($randomized) { + $cmd =~ s/\@$kw\s*\(\s*$targ\s*\)/$ret/g + } else { + $cmd =~ s/\@$kw\s*\(\s*$targ\s*\)/$ret/ + } + } + + unless ($Settings::lockdown) { + # any round bracket(pair) found after parseKw sub-routine were treated as macro perl sub-routine + undef $ret; undef $arg; + while (($sub, $val) = parseSub($cmd)) { + my $sub_error = 1; + foreach my $e (@perl_name) { + if ($e eq $sub) { + $sub_error = 0; + } + } + if ($sub_error) {$self->{error} = "Unrecognized --> $sub <-- Sub-Routine"; return ""} + $arg = subvars($val); + my $sub1 = $sub."(".$arg.")"; + $ret = eval($sub1); + return unless defined $ret; + $val = q4rx $val; + $cmd =~ s/$sub\s*\(\s*$val\s*\)/$ret/g + } + } + + $cmd = subvars($cmd); + return $cmd +} + +# check if on the line there commands that open new command blocks +sub isNewCommandBlock { + my ($line) = @_; + + if ($line =~ /^if.*{$/ || $line =~ /^case.*{$/ || $line =~ /^switch.*{$/ || $line =~ /^else.*{$/) { + return 1; + } else { + return 0; + } +} + +1; diff --git a/openkore_llm_knowledge/plugins/macro/Macro/Script.pm b/openkore_llm_knowledge/plugins/macro/Macro/Script.pm new file mode 100644 index 0000000000..7c51eddf88 --- /dev/null +++ b/openkore_llm_knowledge/plugins/macro/Macro/Script.pm @@ -0,0 +1,1016 @@ +# $Id: Script.pm r6782 2009-07-24 16:36:00Z ezza $ +package Macro::Script; + +use strict; + +require Exporter; +our @ISA = qw(Exporter); + +use Utils; +use Globals; +use AI; +use Macro::Data; +use Macro::Parser qw(parseCmd isNewCommandBlock); +use Macro::Utilities qw(cmpr); +use Macro::Automacro qw(releaseAM lockAM); +use Log qw(message warning); + +our ($rev) = q$Revision: 6782 $ =~ /(\d+)/; + +# constructor +sub new { + my ($class, $name, $repeat, $lastname, $lastline, $interruptible) = @_; + + $repeat = 0 unless ($repeat && $repeat =~ /^\d+$/); + return unless defined $macro{$name}; + my $self = { + name => $name, + lastname => undef, + registered => 0, + submacro => 0, + macro_delay => $timeout{macro_delay}{timeout}, + timeout => 0, + mainline_delay => undef, + subline_delay => undef, + result => undef, + time => time, + finished => 0, + overrideAI => 0, + line => 0, + lastline => undef, + label => {scanLabels($macro{$name})}, + repeat => $repeat, + subcall => undef, + error => undef, + orphan => $::config{macro_orphans}, + interruptible => 1, + macro_block => 0 + + }; + if (defined $lastname && defined $lastline) { + $self->{lastname} = $lastname; + $self->{lastline} = $lastline + } + if (defined $interruptible) {$self->{interruptible} = $interruptible} + + bless ($self, $class); + return $self +} + + +# destructor +sub DESTROY { + AI::clear('macro') if (AI::inQueue('macro') && !$_[0]->{submacro}) +} + +# declares current macro to be a submacro +sub regSubmacro { + $_[0]->{submacro} = 1 +} + +# registers to AI queue +sub register { + unless ($_[0]->{overrideAI}) { + if (AI::is("NPC")) { + splice(@AI::ai_seq, 1, 0, 'macro'); + splice(@AI::ai_seq_args, 1, 0, {}); + } else { + AI::queue('macro'); + } + } + $_[0]->{registered} = 1 +} + +# checks register status +sub registered { + return $_[0]->{registered} +} + +# sets or gets method for orphaned macros +sub orphan { + if (defined $_[1]) {$_[0]->{orphan} = $_[1]} + return $_[0]->{orphan} +} + +# sets macro_delay timeout for this macro +sub setMacro_delay { + $_[0]->{macro_delay} = $_[1] +} + +# sets or gets timeout for next command +sub timeout { + if (defined $_[1]) {$_[0]->{timeout} = $_[1]} + return (time => $_[0]->{time}, timeout => $_[0]->{timeout}) +} + +# sets or gets override AI value +sub overrideAI { + if (defined $_[1]) {$_[0]->{overrideAI} = $_[1]} + return $_[0]->{overrideAI} +} + +# sets or get interruptible flag +sub interruptible { + if (defined $_[1]) {$_[0]->{interruptible} = $_[1]} + return $_[0]->{interruptible} +} + +# sets or gets macro block flag +sub macro_block { + my $script = $_[0]; + do { + if (defined $_[1]) { + $script->{macro_block} = $_[1]; + } else { + return $script->{macro_block} if $script->{macro_block}; + } + } while $script = $script->{subcall}; + + return $_[1]; +} +=pod +sub macro_block { + if (defined $_[1]) {$_[0]->{macro_block} = $_[1]} + return $_[0]->{macro_block} +} +=cut + +# returns whether or not the macro finished +sub finished { + return $_[0]->{finished} +} + +# returns the name of the current macro +sub name { + return $_[0]->{name} +} + +# returns the current line number +sub line { + return $_[0]->{line} +} + +# returns the error line +sub error { + return $_[0]->{error} +} + +# re-sets the timer +sub ok { + $_[0]->{time} = time +} + +# scans the script for labels +sub scanLabels { + my $script = $_[0]; + my %labels; + for (my $line = 0; $line < @{$script}; $line++) { + if (${$script}[$line] =~ /^:/) { + my ($label) = ${$script}[$line] =~ /^:(.*)/; + $labels{$label} = $line + } + if (${$script}[$line] =~ /^while\s+/) { + my ($label) = ${$script}[$line] =~ /\s+as\s+(.*)/; + $labels{$label} = $line + } + if (${$script}[$line] =~ /^end\s+/) { + my ($label) = ${$script}[$line] =~ /^end\s+(.*)/; + $labels{"end ".$label} = $line + } + } + return %labels +} + +# processes next line +sub next { + my $self = $_[0]; + + if (defined $self->{subcall}) { + my $command = $self->{subcall}->next; + if (defined $command) { + my %tmptime = $self->{subcall}->timeout; + $self->{timeout} = $tmptime{timeout}; + $self->{time} = $tmptime{time}; + if ($self->{subcall}->finished) { + if ($self->{subcall}->{repeat} == 0) {$self->{finished} = 1} + undef $self->{subcall}; + # $self->{line}++ + } + return $command + } + $self->{error} = $self->{subcall}->{error}; + return + } + + if (defined $self->{mainline_delay} && defined $self->{subline_delay}) {$self->{line} = $self->{mainline_delay}} + my $line = ${$macro{$self->{name}}}[$self->{line}]; + if (!defined $line) { + if (defined $self->{lastname} && defined $self->{lastline}) { + if ($self->{repeat} > 1) {$self->{repeat}--; $self->{line} = 0} + else { + $self->{line} = $self->{lastline} + 1; + $self->{name} = $self->{lastname}; + ($self->{lastline}, $self->{lastname}) = undef; + $self->{finished} = 1 + } + $line = ${$macro{$self->{name}}}[$self->{line}] + } + else { + if ($self->{repeat} > 1) {$self->{repeat}--; $self->{line} = 0} + else {$self->{finished} = 1} + return "" + } + } + + # TODO: separate line advancing and timeout setting + + my $errtpl = "error in ".$self->{line}; + + # "If" postfix control + if ($line =~ /.+\s+if\s*\(.*\)$/) { + my ($text) = $line =~ /.+\s+if\s*\(\s*(.*)\s*\)$/; + $text = parseCmd($text, $self); + if (defined $self->{error}) {$self->{error} = "$errtpl: $self->{error}"; return} + my $savetxt = particle($text, $self, $errtpl); + if (multi($savetxt, $self, $errtpl)) { + $line =~ s/\s+if\s*\(.*\)$//; + } else { + $self->{line}++; + $self->{timeout} = 0; + return ""; + } + } + + ########################################## + # jump to label: goto label + if ($line =~ /^goto\s/) { + my ($tmp) = $line =~ /^goto\s+([a-zA-Z][a-zA-Z\d]*)/; + if (exists $self->{label}->{$tmp}) {$self->{line} = $self->{label}->{$tmp}} + else {$self->{error} = "$errtpl: cannot find label $tmp"} + $self->{timeout} = 0 + ########################################## + # declare block ending: end label + } elsif ($line =~ /^end\s/) { + my ($tmp) = $line =~ /^end\s+(.*)/; + if (exists $self->{label}->{$tmp}) {$self->{line} = $self->{label}->{$tmp}} + else {$self->{error} = "$errtpl: cannot find block start for $tmp"} + $self->{timeout} = 0 + ########################################## + # macro block: begin + } elsif ($line eq '[') { + $self->{macro_block} = 1; + $self->{line}++ + ########################################## + # macro block: end + } elsif ($line eq ']') { + $self->{macro_block} = 0; + $self->{timeout} = 0; + $self->{line}++ + ########################################## + # if statement: if (foo = bar) goto label? + # Unlimited If Statement by: ezza @ http://forums.openkore.com/ + } elsif ($line =~ /^if\s/) { + my ($text, $then) = $line =~ /^if\s+\(\s*(.*)\s*\)\s+(goto\s+.*|call\s+.*|stop|{|)\s*/; + + # The main trick is parse all the @special keyword and vars 1st, + $text = parseCmd($text, $self); + if (defined $self->{error}) {$self->{error} = "$errtpl: $self->{error}"; return} + my $savetxt = particle($text, $self, $errtpl); + if (multi($savetxt, $self, $errtpl)) { + newThen($then, $self, $errtpl); + if (defined $self->{error}) {$self->{error} = "$errtpl: $self->{error}"; return} + } elsif ($then eq "{") { # If the condition is false because "if" this is not using the command block + my $countBlockIf = 1; + while ($countBlockIf) { + $self->{line}++; + my $searchEnd = ${$macro{$self->{name}}}[$self->{line}]; + + if ($searchEnd =~ /^if.*{$/) { + $countBlockIf++; + } elsif (($searchEnd eq '}') || ($searchEnd =~ /^}\s*else\s*{$/ && $countBlockIf == 1)) { + $countBlockIf--; + } elsif ($searchEnd =~ /^}\s*elsif\s+\(\s*(.*)\s*\).*{$/ && $countBlockIf == 1) { + # If the condition of 'elsif' is true, the commands of your block will be executed, + # if false, will not run. + $text = parseCmd($1, $self); + if (defined $self->{error}) {$self->{error} = "$errtpl: $self->{error}"; return} + $savetxt = particle($text, $self, $errtpl); + if (multi($savetxt, $self, $errtpl)) { + $countBlockIf--; + } + } + } + } + $self->{line}++; + $self->{timeout} = 0 + + ########################################## + # If arriving at a line 'else', 'elsif' or 'case', it should be skipped - + # it will never be activated if coming from a false 'if' or a previous 'case' has not been called + } elsif ($line =~ /^}\s*else\s*{/ || $line =~ /^}\s*elsif.*{$/ || $line =~ /^case/ || $line =~ /^else/) { + my $countCommandBlock = 1; + while ($countCommandBlock) { + $self->{line}++; + my $searchEnd = ${$macro{$self->{name}}}[$self->{line}]; + + if (isNewCommandBlock($searchEnd)) { + $countCommandBlock++; + } elsif ($searchEnd eq '}') { + $countCommandBlock--; + } + } + + $self->{timeout} = 0 + + ########################################## + # switch statement: + } elsif ($line =~ /^switch.*{$/) { + my ($firstPartCondition) = $line =~ /^switch\s*\(\s*(.*)\s*\)\s*{$/; + + my $countBlocks = 1; + while ($countBlocks) { + $self->{line}++; + my $searchNextCase = ${$macro{$self->{name}}}[$self->{line}]; + + if ($searchNextCase =~ /^else/) { + my ($then) = $searchNextCase =~ /^else\s*(.*)/; + newThen($then, $self, $errtpl); + if (defined $self->{error}) {$self->{error} = "$errtpl: $self->{error}"; return} + last; + } + + my ($secondPartCondition, $then) = $searchNextCase =~ /^case\s*\(\s*(.*)\s*\)\s*(.*)/; + next if (!$secondPartCondition); + + my $completCondition = $firstPartCondition . ' ' . $secondPartCondition; + my $text = parseCmd($completCondition, $self); + if (defined $self->{error}) {$self->{error} = "$errtpl: $self->{error}"; return} + my $savetxt = particle($text, $self, $errtpl); + if (multi($savetxt, $self, $errtpl)) { + newThen($then, $self, $errtpl); + if (defined $self->{error}) {$self->{error} = "$errtpl: $self->{error}"; return} + last; + } elsif ($searchNextCase =~ /^case.*{$/) { + my $countCommandBlock = 1; + while ($countCommandBlock) { + $self->{line}++; + my $searchEnd = ${$macro{$self->{name}}}[$self->{line}]; + + if (isNewCommandBlock($searchEnd)) { + $countCommandBlock++; + } elsif ($searchEnd eq '}') { + $countCommandBlock--; + } + } + } + } + + $self->{line}++; + $self->{timeout} = 0 + + ########################################## + # end block of "if" or "switch" + } elsif ($line eq '}') { + $self->{line}++; + $self->{timeout} = 0 + + ########################################## + # while statement: while (foo <= bar) as label + } elsif ($line =~ /^while\s/) { + my ($text, $label) = $line =~ /^while\s+\(\s*(.*)\s*\)\s+as\s+(.*)/; + my $text = parseCmd($text, $self); + if (defined $self->{error}) {$self->{error} = "$errtpl: $self->{error}"; return} + my $savetxt = particle($text, $self, $errtpl); + if (!multi($savetxt, $self, $errtpl)) { + $self->{line} = $self->{label}->{"end ".$label}; + } + $self->{line}++; + $self->{timeout} = 0 + ########################################## + # pop value from variable: $var = [$list] + } elsif ($line =~ /^\$[a-z][a-z\d]*\s+=\s+\[\s*\$[a-z][a-z\d]*\s*\]$/i) { + if ($line =~ /;/) {run_sublines($line, $self); if (defined $self->{error}) {$self->{error} = "$errtpl: $self->{error}"; return}} + else { + my ($var, $list) = $line =~ /^\$([a-z][a-z\d]*?)\s+=\s+\[\s*\$([a-z][a-z\d]*?)\s*\]$/i; + my $listitems = ($varStack{$list} or ""); + my $val; + if (($val) = $listitems =~ /^(.*?)(?:,|$)/) { + $listitems =~ s/^(?:.*?)(?:,|$)//; + $varStack{$list} = $listitems + } + else {$val = $listitems} + $varStack{$var} = $val; + } + $self->{line}++; + $self->{timeout} = 0 unless defined $self->{mainline_delay} && defined $self->{subline_delay}; + return $self->{result} if $self->{result} + ########################################## + # set variable: $variable = value + } elsif ($line =~ /^\$[a-z]/i) { + my ($var, $val); + if ($line =~ /;/) {run_sublines($line, $self); if (defined $self->{error}) {$self->{error} = "$errtpl: $self->{error}"; return}} + else { + if (($var, $val) = $line =~ /^\$([a-z][a-z\d]*?)\s+=\s+(.*)/i) { + my $pval = parseCmd($val, $self); + if (defined $self->{error}) {$self->{error} = "$errtpl: $self->{error}"; return} + if (defined $pval) { + if ($pval =~ /^\s*(?:undef|unset)\s*$/i && exists $varStack{$var}) {undef $varStack{$var}} + else {$varStack{$var} = $pval} + } + else {$self->{error} = "$errtpl: $val failed"} + } + elsif (($var, $val) = $line =~ /^\$([a-z][a-z\d]*?)([+-]{2})$/i) { + if ($val eq '++') {$varStack{$var} = ($varStack{$var} or 0)+1} + else {$varStack{$var} = ($varStack{$var} or 0)-1} + } + else {$self->{error} = "$errtpl: unrecognized assignment"} + } + $self->{line}++; + $self->{timeout} = 0 unless defined $self->{mainline_delay} && defined $self->{subline_delay}; + return $self->{result} if $self->{result} + ########################################## + # set doublevar: ${$variable} = value + } elsif ($line =~ /^\$\{\$[.a-z]/i) { + my ($dvar, $val); + if ($line =~ /;/) {run_sublines($line, $self); if (defined $self->{error}) {$self->{error} = "$errtpl: $self->{error}"; return}} + else { + if (($dvar, $val) = $line =~ /^\$\{\$([.a-z][a-z\d]*?)\}\s+=\s+(.*)/i) { + my $var = $varStack{$dvar}; + unless (defined $var) {$self->{error} = "$errtpl: $dvar not defined"} + else { + my $pval = parseCmd($val, $self); + if (defined $self->{error}) {$self->{error} = "$errtpl: $self->{error}"; return} + unless (defined $pval) {$self->{error} = "$errtpl: $val failed"} + else { + if ($pval =~ /^\s*(?:undef|unset)\s*$/i) {undef $varStack{"#$var"}} + else {$varStack{"#$var"} = $pval} + } + } + } + elsif (($dvar, $val) = $line =~ /^\$\{\$([.a-z][a-z\d]*?)\}([+-]{2})$/i) { + my $var = $varStack{$dvar}; + unless (defined $var) {$self->{error} = "$errtpl: $dvar undefined"} + else { + if ($val eq '++') {$varStack{"#$var"} = ($varStack{"#$var"} or 0)+1} + else {$varStack{"#$var"} = ($varStack{"#$var"} or 0)-1} + } + } + else {$self->{error} = "$errtpl: unrecognized assignment."} + } + $self->{line}++; + $self->{timeout} = 0 unless defined $self->{mainline_delay} && defined $self->{subline_delay}; + return $self->{result} if $self->{result} + ########################################## + # label definition: :label + } elsif ($line =~ /^:/) { + $self->{line}++; + $self->{timeout} = 0 + ########################################## + # returns command: do whatever + } elsif ($line =~ /^do\s/) { + if ($line =~ /;/ && $line =~ /^do eval/ eq "") { + run_sublines($line, $self); + if (defined $self->{error}) {$self->{error} = "$errtpl: $self->{error}"; return} + unless (defined $self->{mainline_delay} && defined $self->{subline_delay}) {$self->{timeout} = $self->{macro_delay}; $self->{line}++} + if ($self->{result}) {return $self->{result}} + } + else { + my ($tmp) = $line =~ /^do\s+(.*)/; + if ($tmp =~ /^macro\s+/) { + my ($arg) = $tmp =~ /^macro\s+(.*)/; + if ($arg =~ /^reset/) {$self->{error} = "$errtpl: use 'release' instead of 'macro reset'"} + elsif ($arg eq 'pause' || $arg eq 'resume') {$self->{error} = "$errtpl: do not use 'macro pause' or 'macro resume' within a macro"} + elsif ($arg =~ /^set\s/) {$self->{error} = "$errtpl: do not use 'macro set'. Use \$foo = bar"} + elsif ($arg eq 'stop') {$self->{error} = "$errtpl: use 'stop' instead"} + elsif ($arg !~ /^(?:list|status)$/) {$self->{error} = "$errtpl: use 'call $arg' instead of 'macro $arg'"} + } + elsif ($tmp =~ /^ai\s+clear$/) {$self->{error} = "$errtpl: do not mess around with ai in macros"} + my $result = parseCmd($tmp, $self); + if (defined $self->{error}) {$self->{error} = "$errtpl: $self->{error}"; return} + unless (defined $result) {$self->{error} = "$errtpl: command $tmp failed";return} + $self->{timeout} = $self->{macro_delay}; + $self->{line}++; + return $result + } + ########################################## + # log command + } elsif ($line =~ /^log\s+/) { + if ($line =~ /;/) {run_sublines($line, $self); if (defined $self->{error}) {$self->{error} = "$errtpl: $self->{error}"; return}} + else { + my ($tmp) = $line =~ /^log\s+(.*)/; + my $result = parseCmd($tmp, $self); + if (defined $self->{error}) {$self->{error} = "$errtpl: $self->{error}"; return} + unless (defined $result) {$self->{error} = "$errtpl: $tmp failed"} + else {message "[macro log] $result\n", "macro";} + } + $self->{line}++; + $self->{timeout} = $self->{macro_delay} unless defined $self->{mainline_delay} && defined $self->{subline_delay}; + return $self->{result} if $self->{result} + ########################################## + # pause command + } elsif ($line =~ /^pause/) { + if ($line =~ /;/) { + run_sublines($line, $self); + if (defined $self->{error}) {$self->{error} = "$errtpl: $self->{error}"; return} + $self->{timeout} = $self->{macro_delay} unless defined $self->{mainline_delay} && defined $self->{subline_delay} + } + else { + my ($tmp) = $line =~ /^pause\s*(.*)/; + if (defined $tmp) { + my $result = parseCmd($tmp, $self); + if (defined $self->{error}) {$self->{error} = "$errtpl: $self->{error}"; return} + unless (defined $result) {$self->{error} = "$errtpl: $tmp failed"} + else {$self->{timeout} = $result} + } + else {$self->{timeout} = $self->{macro_delay}} + } + $self->{line}++; + return $self->{result} if $self->{result} + ########################################## + # stop command + } elsif ($line eq "stop") { + $self->{finished} = 1 + ########################################## + # release command + } elsif ($line =~ /^release\s+/) { + if ($line =~ /;/) {run_sublines($line, $self); if (defined $self->{error}) {$self->{error} = "$errtpl: $self->{error}"; return}} + else { + my ($tmp) = $line =~ /^release\s+(.*)/; + if (!releaseAM(parseCmd($tmp, $self))) { + if (defined $self->{error}) {$self->{error} = "$errtpl: $self->{error}"; return} + $self->{error} = "$errtpl: releasing $tmp failed" + } + } + $self->{line}++; + $self->{timeout} = 0 unless defined $self->{mainline_delay} && defined $self->{subline_delay}; + return $self->{result} if $self->{result} + ########################################## + # lock command + } elsif ($line =~ /^lock\s+/) { + if ($line =~ /;/) {run_sublines($line, $self); if (defined $self->{error}) {$self->{error} = "$errtpl: $self->{error}"; return}} + else { + my ($tmp) = $line =~ /^lock\s+(.*)/; + if (!lockAM(parseCmd($tmp, $self))) { + if (defined $self->{error}) {$self->{error} = "$errtpl: $self->{error}"; return} + $self->{error} = "$errtpl: locking $tmp failed" + } + } + $self->{line}++; + $self->{timeout} = 0 unless defined $self->{mainline_delay} && defined $self->{subline_delay}; + return $self->{result} if $self->{result} + ########################################## + # call command + } elsif ($line =~ /^call\s+/) { + my ($tmp) = $line =~ /^call\s+(.*)/; + my $name = $tmp; + my $args; + my $cparms; + + my $calltimes = 1; + + if ($tmp =~ /\s/) { + ($name, $args) = $tmp =~ /^(\S+?)\s+(.+)/; + my ($times); + if ($args =~ /(\d+)\s+(--.*)/) { + ($times, $cparms) = $args =~ /(\d+)?\s+?(--.*)?/; + $times = parseCmd($args, $self); + $cparms = parseCmd($args, $self); + } elsif ($args =~ /^\d+/) { + $times = parseCmd($args, $self); + } elsif ($args =~ /^--.*/) { + $cparms = parseCmd($args, $self); + } + + if (defined $self->{error}) {$self->{error} = "$errtpl: $self->{error}"; return} + if (defined $times && $times =~ /\d+/) { $calltimes = $times; }; # do we have a valid repeat value? + } + + $self->{subcall} = new Macro::Script($name, $calltimes, undef, undef, $self->{interruptible}); + + unless (defined $self->{subcall}) { + $self->{error} = "$errtpl: failed to call script"; + } else { + my @new_params = substr($cparms, 2) =~ /"[^"]+"|\S+/g; + foreach my $p (1..@new_params) { + $varStack{".param".$p} = $new_params[$p-1]; + $varStack{".param".$p} = substr($varStack{".param".$p}, 1, -1) if ($varStack{".param".$p} =~ /^".*"$/); # remove quotes + } + + $self->{subcall}->regSubmacro; + $self->{line}++; # point to the next line to be executed in the caller + $self->{timeout} = $self->{macro_delay}; + } + ########################################## + # set command + } elsif ($line =~ /^set\s+/) { + if ($line =~ /;/) {run_sublines($line, $self); if (defined $self->{error}) {$self->{error} = "$errtpl: $self->{error}"; return}} + else { + my ($var, $val) = $line =~ /^set\s+(\w+)\s+(.*)$/; + if ($var eq 'macro_delay' && $val =~ /^[\d\.]*\d+$/) { + $self->{macro_delay} = $val + } elsif ($var eq 'repeat' && $val =~ /^\d+$/) { + $self->{repeat} = $val + } elsif ($var eq 'overrideAI' && $val =~ /^[01]$/) { + $self->{overrideAI} = $val + } elsif ($var eq 'exclusive' && $val =~ /^[01]$/) { + $self->{interruptible} = $val?0:1 + } elsif ($var eq 'orphan' && $val =~ /^(?:terminate|reregister(?:_safe)?)$/) { + $self->{orphan} = $val + } else { + $self->{error} = "$errtpl: unrecognized key or wrong value" + } + } + $self->{line}++; + $self->{timeout} = 0 unless defined $self->{mainline_delay} && defined $self->{subline_delay}; + return $self->{result} if $self->{result} + ########################################## + # sub-routine command, still figuring out how to include unclever/fail sub-routine into the error msg + } elsif ($line =~ /^(?:\w+)\s*\(.*?\)/) { + if ($line =~ /;/) {run_sublines($line, $self)} + else { + parseCmd($line, $self); + } + if (defined $self->{error}) {$self->{error} = "$errtpl: $self->{error}"; return} + $self->{line}++; + $self->{timeout} = 0 unless defined $self->{mainline_delay} && defined $self->{subline_delay}; + return $self->{result} if $self->{result} + ########################################## + # unrecognized line + } else { + $self->{error} = "$errtpl: syntax error" + } + + if (defined $self->{error}) {return} else {return ""} +} + + +sub run_sublines { + my ($real_line, $self) = @_; + my ($i, $real_num, @sub_line) = (0, $self->{line}, undef); + my @split = split(/\s*;\s*/, $real_line); + my ($dvar, $var, $val, $list); + + foreach my $e (@split) { + next if $e eq ""; + if (defined $self->{subline_delay} && $i < $self->{subline_delay}) {$i++; next} + if (defined $self->{subline_delay} && $i == $self->{subline_delay}) { + $self->{timeout} = 0; + ($self->{mainline_delay}, $self->{subline_delay}, $self->{result}) = undef; + $i++; next + } + + ########################################## + # pop value from variable: $var = [$list] + if ($e =~ /^\$[a-z][a-z\d]*\s+=\s+\[\s*\$[a-z][a-z\d]*\s*\]$/i) { + ($var, $list) = $e =~ /^\$([a-z][a-z\d]*?)\s+=\s+\[\s*\$([a-z][a-z\d]*?)\s*\]$/i; + my $listitems = ($varStack{$list} or ""); + if (($val) = $listitems =~ /^(.*?)(?:,|$)/) { + $listitems =~ s/^(?:.*?)(?:,|$)//; + $varStack{$list} = $listitems + } + else {$val = $listitems} + $varStack{$var} = $val; + $i++; next + + # set variable: $variable = value + } elsif ($e =~ /^\$[a-z]/i) { + if (($var, $val) = $e =~ /^\$([a-z][a-z\d]*?)\s+=\s+(.*)/i) { + my $pval = parseCmd($val, $self); + if (defined $self->{error}) {$self->{error} = "Error in line $real_num: $real_line\n[macro] $self->{name} error in sub-line $i: $self->{error}"; last} + if (defined $pval) { + if ($pval =~ /^\s*(?:undef|unset)\s*$/i && exists $varStack{$var}) {undef $varStack{$var}} + else {$varStack{$var} = $pval} + } + else {$self->{error} = "Error in line $real_num: $real_line\n[macro] $self->{name} error in sub-line $i: $e failed"; last} + } + elsif (($var, $val) = $e =~ /^\$([a-z][a-z\d]*?)([+-]{2})$/i) { + if ($val eq '++') {$varStack{$var} = ($varStack{$var} or 0)+1} else {$varStack{$var} = ($varStack{$var} or 0)-1} + } + else {$self->{error} = "Error in line $real_num: $real_line\n[macro] $self->{name} error in sub-line $i: unrecognized assignment in ($e)"; last} + $i++; next + + # set doublevar: ${$variable} = value + } elsif ($e =~ /^\$\{\$[.a-z]/i) { + if (($dvar, $val) = $e =~ /^\$\{\$([.a-z][a-z\d]*?)\}\s+=\s+(.*)/i) { + $var = $varStack{$dvar}; + unless (defined $var) {$self->{error} = "Error in line $real_num: $real_line\n[macro] $self->{name} error in sub-line $i: $dvar not defined in ($e)"; last} + else { + my $pval = parseCmd($val, $self); + if (defined $self->{error}) {$self->{error} = "Error in line $real_num: $real_line\n[macro] $self->{name} error in sub-line $i: $self->{error}"; last} + unless (defined $pval) {$self->{error} = "Error in line $real_num: $real_line\n[macro] $self->{name} error in sub-line $i: $e failed"; last} + else { + if ($pval =~ /^\s*(?:undef|unset)\s*$/i) {undef $varStack{"#$var"}} + else {$varStack{"#$var"} = $pval} + } + } + } + elsif (($dvar, $val) = $e =~ /^\$\{\$([.a-z][a-z\d]*?)\}([+-]{2})$/i) { + $var = $varStack{$dvar}; + unless (defined $var) {$self->{error} = "Error in line $real_num: $real_line\n[macro] $self->{name} error in sub-line $i: undefined $dvar in ($e)"; last} + else {if ($val eq '++') {$varStack{"#$var"} = ($varStack{"#$var"} or 0)+1} else {$varStack{"#$var"} = ($varStack{"#$var"} or 0)-1}} + $i++; next + } + else {$self->{error} = "Error in line $real_num: $real_line\n[macro] $self->{name} error in sub-line $i: unrecognized assignment in ($e)"; last} + $i++; next + + # stop command + } elsif ($e eq "stop") { + $self->{finished} = 1; last + + # set command + } elsif (($var, $val) = $e =~ /^set\s+(\w+)\s+(.*)$/) { + if ($var eq 'macro_delay' && $val =~ /^[\d\.]*\d+$/) {$self->{macro_delay} = $val} + elsif ($var eq 'repeat' && $val =~ /^\d+$/) {$self->{repeat} = $val} + elsif ($var eq 'overrideAI' && $val =~ /^[01]$/) {$self->{overrideAI} = $val} + elsif ($var eq 'exclusive' && $val =~ /^[01]$/) {$self->{interruptible} = $val?0:1} + elsif ($var eq 'orphan' && $val =~ /^(?:terminate|reregister(?:_safe)?)$/) {$self->{orphan} = $val} + else {$self->{error} = "Error in line $real_num: $real_line\n[macro] $self->{name} error in sub-line $i: unrecognized key or wrong value in ($e)"; last} + + # lock command + } elsif ($e =~ /^lock\s+/) { + my ($tmp) = $e =~ /^lock\s+(.*)/; + if (!lockAM(parseCmd($tmp, $self))) { + if (defined $self->{error}) {$self->{error} = "Error in line $real_num: $real_line\n[macro] $self->{name} error in sub-line $i: $self->{error}"; last} + $self->{error} = "Error in line $real_num: $real_line\n[macro] $self->{name} error in sub-line $i: locking $tmp failed in ($e)"; last + } + + # release command + } elsif ($e =~ /^release\s+/) { + my ($tmp) = $e =~ /^release\s+(.*)/; + if (!releaseAM(parseCmd($tmp, $self))) { + if (defined $self->{error}) {$self->{error} = "Error in line $real_num: $real_line\n[macro] $self->{name} error in sub-line $i: $self->{error}"; last} + $self->{error} = "Error in line $real_num: $real_line\n[macro] $self->{name} error in sub-line $i: releasing $tmp failed in ($e)"; last + } + + # pause command + } elsif ($e =~ /^pause/) { + my ($tmp) = $e =~ /^pause\s*(.*)/; + if (defined $tmp) { + my $result = parseCmd($tmp, $self); + if (defined $self->{error}) {$self->{error} = "Error in line $real_num: $real_line\n[macro] $self->{name} error in sub-line $i: $self->{error}"; last} + unless (defined $result) {$self->{error} = "Error in line $real_num: $real_line\n[macro] $self->{name} error in sub-line $i: $tmp failed in ($e)"; last} + else {$self->{timeout} = $result} + } + else {$self->{timeout} = $self->{macro_delay}} + $self->{mainline_delay} = $real_num; + $self->{subline_delay} = $i; + last + + # log command + } elsif ($e =~ /^log\s+/) { + my ($tmp) = $e =~ /^log\s+(.*)/; + my $result = parseCmd($tmp, $self); + if (defined $self->{error}) {$self->{error} = "Error in line $real_num: $real_line\n[macro] $self->{name} error in sub-line $i: $self->{error}"; last} + unless (defined $result) {$self->{error} = "Error in line $real_num: $real_line\n[macro] $self->{name} error in sub-line $i: $tmp failed in ($e)"; last} + else {message "[macro log] $result\n", "macro"} + $self->{timeout} = $self->{macro_delay}; + $self->{mainline_delay} = $real_num; + $self->{subline_delay} = $i; + last + } + + # do command + elsif ($e =~ /^do\s/) { + my ($tmp) = $e =~ /^do\s+(.*)/; + if ($tmp =~ /^macro\s+/) { + my ($arg) = $tmp =~ /^macro\s+(.*)/; + if ($arg =~ /^reset/) {$self->{error} = "Error in line $real_num: $real_line\n[macro] $self->{name} error in sub-line $i: use 'release' instead of 'macro reset'"} + elsif ($arg eq 'pause' || $arg eq 'resume') {$self->{error} = "Error in line $real_num: $real_line\n[macro] $self->{name} error in sub-line $i: do not use 'macro pause' or 'macro resume' within a macro"} + elsif ($arg =~ /^set\s/) {$self->{error} = "Error in line $real_num: $real_line\n[macro] $self->{name} error in sub-line $i: do not use 'macro set'. Use \$foo = bar"} + elsif ($arg eq 'stop') {$self->{error} = "Error in line $real_num: $real_line\n[macro] $self->{name} error in sub-line $i: use 'stop' instead"} + elsif ($arg !~ /^(?:list|status)$/) {$self->{error} = "Error in line $real_num: $real_line\n[macro] $self->{name} error in sub-line $i: use 'call $arg' instead of 'macro $arg'"} + } + elsif ($tmp =~ /^eval\s+/) {$self->{error} = "Error in line $real_num: $real_line\n[macro] $self->{name} error in sub-line $i: do not mix eval in the sub-line"} + elsif ($tmp =~ /^ai\s+clear$/) {$self->{error} = "Error in line $real_num: $real_line\n[macro] $self->{name} error in sub-line $i: do not mess around with ai in macros"} + my $result = parseCmd($tmp, $self); + if (defined $self->{error}) {$self->{error} = "Error in line $real_num: $real_line\n[macro] $self->{name} error in sub-line $i: $self->{error}"; last} + unless (defined $result) {$self->{error} = "Error in line $real_num: $real_line\n[macro] $self->{name} error in sub-line $i: command $tmp failed"; last} + $self->{timeout} = $self->{macro_delay}; + $self->{mainline_delay} = $real_num; + $self->{subline_delay} = $i; + $self->{result} = $result; + last + + # "call", "[", "]", ":", "if", "while", "end" and "goto" commands block + } elsif ($e =~ /^(?:call|\[|\]|:|if|end|goto|while)\s*/i) { + $self->{error} = "Line $real_num sub-line $i\n[Reason:] Use saperate line for HERE --> $e <-- HERE"; + last + # sub-routine + } elsif (my ($sub) = $e =~ /^(\w+)\s*\(.*?\)$/) { + parseCmd($e, $self); + $self->{error} = "Error in line $real_num: $real_line\n[macro] $self->{name} error in sub-line $i: $self->{error}" if defined $self->{error}; + last + + ##################### End ################## + } else { + #$self->{error} = "Error in line $real_num: $real_line\n[macro] $self->{name} error in sub-line $i: unrecognized assignment in ($e)" + message "Error in $self->{line}: $real_line\nWarning: Ignoring Unknown Command in sub-line $i: ($e)\n", "menu"; + } + $i++ + } +} + +sub newThen { + my ($then, $self, $errtpl) = @_; + + if ($then =~ /^goto\s/) { + my ($tmp) = $then =~ /^goto\s+([a-zA-Z][a-zA-Z\d]*)$/; + if (exists $self->{label}->{$tmp}) { + $self->{line} = $self->{label}->{$tmp} + } + else {$self->{error} = "$errtpl: cannot find label $tmp"} + } + elsif ($then =~ /^call\s+/) { + my ($tmp) = $then =~ /^call\s+(.*)/; + if ($tmp =~ /\s/) { + my ($name, $times) = $tmp =~ /(.*?)\s+(.*)/; + my $ptimes = parseCmd($times, $self); + if (defined $self->{error}) {$self->{error} = "$errtpl: $self->{error}"; return} + if (defined $ptimes && $ptimes =~ /^\d+$/) { + if ($ptimes > 0) { + $self->{subcall} = new Macro::Script($name, $ptimes, $self->{name}, $self->{line}, $self->{interruptible}) + } + else {$self->{subcall} = new Macro::Script($name, 0, undef, undef, $self->{interruptible})} + } + else {$self->{error} = "$errtpl: $ptimes must be numeric"} + } + else {$self->{subcall} = new Macro::Script($tmp, 1, undef, undef, $self->{interruptible})} + unless (defined $self->{subcall}) {$self->{error} = "$errtpl: failed to call script"} + else { + $self->{subcall}->regSubmacro; + $self->{timeout} = $self->{macro_delay} + } + } + elsif ($then eq "stop") {$self->{finished} = 1} +} + + +sub statement { + my ($temp_multi, $self, $errtpl) = @_; + my ($first, $cond, $last) = $temp_multi =~ /^\s*"?(.*?)"?\s+([<>=!~]+?)\s+"?(.*?)"?\s*$/; + if (!defined $first || !defined $cond || !defined $last) {$self->{error} = "$errtpl: syntax error in if statement"} + else { + my $pfirst = parseCmd(refined_macroKeywords($first), $self); my $plast = parseCmd(refined_macroKeywords($last), $self); + if (defined $self->{error}) {$self->{error} = "$errtpl: $self->{error}"; return} + unless (defined $pfirst && defined $plast) {$self->{error} = "$errtpl: either '$first' or '$last' has failed"} + elsif (cmpr($pfirst, $cond, $plast)) {return 1} + } + return 0 +} + +sub particle { + # I need to test this main code alot becoz it will be disastrous if something goes wrong + # in the if statement block below + + my ($text, $self, $errtpl) = @_; + my @brkt; + + if ($text =~ /\(/) { + @brkt = txtPosition($text, $self, $errtpl); + $brkt[0] = multi($brkt[0], $self, $errtpl) if !bracket($brkt[0]) && $brkt[0] =~ /[\(\)]/ eq ""; + $text = extracted($text, @brkt); + } + + unless ($text =~ /\(/) {return $text} + $text = particle($text, $self, $errtpl) +} + +sub multi { + my ($text, $self, $errtpl) = @_; + my ($i, $n, $ok, $ok2) = (0, 0, 1, 0); + my %save; + + while ($text =~ /(\|{2}|\&{2})/g) { + # Check if we put the wrong '&&' or '||' in the statement + # Logically, each clean statement(none-bracket statement), + # cant use the simbol '&&' or '||' together. Infact, it must be saperated + # by using round brackets '(' and ')' in the 1st place + + $save{$i} = $1; + if ($i > 0) { + $n = $i - 1; + if ($save{$i} ne $save{$n}) { + my $s = $text; + $ok = 0; + #$s =~ s/($save{$i})/\($1\) <-- HERE/g; # Later maybe? ;p + $self->{error} = "Wrong Conditions: $errtpl ($save{$n} vs $save{$i})" + } + } + $i++ + } + + if ($save{$n} eq "||" && $ok && $i > 0) { + my @split = split(/\s*\|{2}\s*/, $text); + foreach my $e (@split) { + next if $e eq "0"; + return 1 if $e eq "1"; + return 1 if statement($e, $self, $errtpl) + } + return 0 + } + elsif ($save{$n} eq "&&" && $ok && $i > 0) { + my @split = split(/\s*\&{2}\s*/, $text); + foreach my $e (@split) { + next if $e eq "1"; + return 0 if $e eq "0"; + next if statement($e, $self, $errtpl); + return 0 + } + return 1 + } + elsif ($i == 0) { + return $text if $text =~ /^[0-1]$/; + return statement($text, $self, $errtpl) + } +} + +sub txtPosition { + # This sub will deal which bracket is belongs to which statement, + # Using this, will capture the most correct statement to be checked 1st before the next statement, + # Ex: ((((1st statement)2nd statement)3rd statement)4th statement) + # will return: $new[0] = "1st statement", $new[1] = 4, $new[2] = 16 + + my ($text, $self, $errtpl) = @_; + my ($start, $i) = (0, 0); + my (@save, @new, $first, $last); + my @w = split(//, $text); + + foreach my $e (@w) { + if ($e eq ")" && $start) { + $last = $i; last + } + elsif ($e eq "(") { + if ($start) {undef @save; undef $first} + $start = 1; $first = $i; + } + else {if ($start) {push @save, $e}} + $i++ + } + + $self->{error} = "$errtpl: You probably missed 1 or more closing round-\nbracket ')' in the statement." if !defined $last; + + $new[0] = join('', @save); + $new[1] = $first; + $new[2] = $last; + return @new +} + +sub extracted { + # Normally we just do substract s/// or s///g or using while grouping for s{}{} to delete or replace... + # but for some cases, the perl substract is failed... atleast for this text + # ex: $text = "(1 || 0) && 1 && 0" (or I might missed some info for the substract function?) + # so, below code will simply ignore the (1 || 0) but will replace it with $brkt[0] which is either 1 or 0, + # in return, the new $text will be in this format: $text = "1 && 1 && 0" if the $brkt[0] happened to be 1. + + my ($text, @brkt) = @_; + my @save; + my @w = split(//, $text); + + my $txt_lenght = scalar(@w); + + for (my $i = 0; $i < $txt_lenght; $i++) { + if ($i == $brkt[1]) {push @save, $brkt[0]; next} + next if $i > $brkt[1] && $i <= $brkt[2]; + push @save, $w[$i]; + next + } + + $text = join('', @save); + return $text +} + +sub refined_macroKeywords { + # To make sure if there is really no more @special keywords + + my @pair = $_[0] =~ /\@($macroKeywords)\s*\(\s*(.*)\s*\)/i; + return $_[0] unless @pair; + + $pair[1] = parseCmd($pair[1]); + my $new = "@".$pair[0]."(".$pair[1].")"; #sorry! cheap code ;p + return $new +} + +sub bracket { + # Scans $text for @special keywords + + my ($text, $dbg) = @_; + my @brkt; my $i = 0; + + while ($text =~ /(\@)?($macroKeywords)?\s*\(\s*([^\)]+)\s*/g) { + my ($first, $second, $third) = ($1, $2, $3); + unless (defined $first && defined $second && !bracket($third, 1)) { + message "Bracket Detected: $text <-- HERE\n", "menu" if $dbg; + $brkt[$i] = 1 + } + else {$brkt[$i] = 0} + $i++ + } + + foreach my $e (@brkt) { + if ($e == 1) {return 1} + } + + return 0 +} + +1; \ No newline at end of file diff --git a/openkore_llm_knowledge/plugins/macro/Macro/Utilities.pm b/openkore_llm_knowledge/plugins/macro/Macro/Utilities.pm new file mode 100644 index 0000000000..2fbf7b416d --- /dev/null +++ b/openkore_llm_knowledge/plugins/macro/Macro/Utilities.pm @@ -0,0 +1,536 @@ +# $Id: Utilities.pm r6812 2009-07-29 14:00:00Z ezza $ +package Macro::Utilities; + +use strict; + +require Exporter; +our @ISA = qw(Exporter); +our @EXPORT_OK = qw(ai_isIdle q4rx q4rx2 between cmpr match getArgs refreshGlobal getnpcID getPlayerID + getMonsterID getVenderID getItemIDs getItemPrice getInventoryIDs getStorageIDs getSoldOut getInventoryAmount + getCartAmount getShopAmount getStorageAmount getVendAmount getRandom getRandomRange getConfig + getWord callMacro getArgFromList getListLenght sameParty); + +use Utils; +use Globals; +use AI; +use Log qw(warning error); +use Macro::Data; + +our ($rev) = q$Revision: 6812 $ =~ /(\d+)/; + +# own ai_Isidle check that excludes deal +sub ai_isIdle { + return 1 if $queue->overrideAI; + + # now check for orphaned script object + # may happen when messing around with "ai clear" and stuff. + if (defined $queue && !AI::inQueue('macro')) { + my $method = $queue->orphan; + + # 'terminate' undefs the macro object and returns "ai is not idle" + if ($method eq 'terminate') { + undef $queue; + return 0 + # 'reregister' re-inserts "macro" in ai_queue at the first position + } elsif ($method eq 'reregister') { + $queue->register; + return 1 + # 'reregister_safe' waits until AI is idle then re-inserts "macro" + } elsif ($method eq 'reregister_safe') { + if (AI::isIdle || AI::is('deal')) { + $queue->register; + return 1 + } + return 0 + } else { + error "unknown 'orphan' method. terminating macro\n", "macro"; + undef $queue; + return 0 + } + } + return 1 if (AI::is('macro', 'deal')); + return 1 if (AI::is('NPC') && $char->args->waitingForSteps); + return 0; +} + +sub between { + if ($_[0] <= $_[1] && $_[1] <= $_[2]) {return 1} + return 0 +} + +sub cmpr { + my ($a, $cond, $b) = @_; + unless (defined $a && defined $cond && defined $b) { + # this produces a warning but that's what we want + error "cmpr: wrong # of arguments ($a) ($cond) ($b)\n", "macro"; + return 0 + } + + if ($a =~ /^\s*(-?[\d.]+)\s*\.{2}\s*(-?[\d.]+)\s*$/) { + my ($a1, $a2) = ($1, $2); + if ($b =~ /^-?[\d.]+$/) { + if ($cond eq "!=") {return (between($a1, $b, $a2))?0:1} + if ($cond eq "=" || $cond eq "==" || $cond eq "=~" || $cond eq "~") { + return between($a1, $b, $a2) + } + } + error "cmpr: wrong # of arguments ($a) ($cond) ($b)\n--> ($b) <-- maybe should be numeric?\n", "macro"; + return 0 + } + + if ($b =~ /^\s*(-?[\d.]+)\s*\.{2}\s*(-?[\d.]+)\s*$/) { + my ($b1, $b2) = ($1, $2); + if ($a =~ /^-?[\d.]+$/) { + if ($cond eq "!=") {return (between($b1, $a, $b2))?0:1} + if ($cond eq "=" || $cond eq "==" || $cond eq "=~" || $cond eq "~") { + return between($b1, $a, $b2) + } + } + error "cmpr: wrong # of arguments ($a) ($cond) ($b)\n--> ($a) <-- maybe should be numeric?\n", "macro"; + return 0 + } + + if ($a =~ /^-?[\d.]+$/ && $b =~ /^-?[\d.]+$/) { + if (($cond eq "=" || $cond eq "==") && $a == $b) {return 1} + if ($cond eq ">=" && $a >= $b) {return 1} + if ($cond eq "<=" && $a <= $b) {return 1} + if ($cond eq ">" && $a > $b) {return 1} + if ($cond eq "<" && $a < $b) {return 1} + if ($cond eq "!=" && $a != $b) {return 1} + return 0 + } + + if (($cond eq "=" || $cond eq "==") && $a eq $b) {return 1} + if ($cond eq "!=" && $a ne $b) {return 1} + if ($cond eq "~") { + $a = lc($a); + foreach my $e (split(/,/, $b)) {return 1 if $a eq lc($e)} + } + if ($cond eq "=~" && $b =~ /^\/.*?\/\w?\s*$/) { + return match($a, $b, 1) + } + + return 0 +} + +sub q4rx { + my $s = $_[0]; + $s =~ s/([\/*+(){}\[\]\\\$\^?])/\\$1/g; + return $s +} + +sub q4rx2 { + # We let alone the original q4rx sub routine... + # instead, we use this for our new @nick ;p + my $s = $_[0]; + $s =~ s/([\/*+(){}\[\]\\\$\^?"'\. ])/\\$1/g; + return $s +} + +sub match { + my ($text, $kw, $cmpr) = @_; + + unless (defined $text && defined $kw) { + # this produces a warning but that's what we want + error "match: wrong # of arguments ($text) ($kw)\n", "macro"; + return 0 + } + + if ($kw =~ /^"(.*?)"$/) { + return $text eq $1 + } + + if ($kw =~ /^\/(.*?)\/(\w?)$/) { + if ($text =~ /$1/ || ($2 eq 'i' && $text =~ /$1/i)) { + if (!defined $cmpr) { + no strict; + foreach my $idx (1..$#+) {$varStack{".lastMatch$idx"} = ${$idx}} + use strict; + } + return 1 + } + } + + return 0 +} + +sub getArgs { + my $arg = $_[0]; + if ($arg =~ /".*"/) { + my @ret = $arg =~ /^"(.*?)"\s+(.*?)( .*)?$/; + $ret[2] =~ s/^\s+//g if defined $ret[2]; + return @ret + } else { + return split(/\s/, $arg, 3) + } +} + +# gets word from message +sub getWord { + my ($message, $wordno) = $_[0] =~ /^"(.*?)"\s*,\s?(\d+|\$[a-zA-Z][a-zA-Z\d]*)$/s; + my @words = split(/[ ,.:;\"\'!?\r\n]/, $message); + my $no = 1; + if ($wordno =~ /^\$/) { + my ($val) = $wordno =~ /^\$([a-zA-Z][a-zA-Z\d]*)\s*$/; + return "" unless defined $val; + if (exists $varStack{$val} && $varStack{$val} =~ /^[1-9][0-9]*$/) {$wordno = $varStack{$val}} + else {return ""} + + } + foreach (@words) { + next if /^$/; + return $_ if $no == $wordno; + $no++ + } + return "" +} + +# gets openkore setting +sub getConfig { + my ($arg1) = $_[0] =~ /^\s*(\w*\.*\w+)\s*$/; + # Basic Support for "label" in blocks. Thanks to "piroJOKE" (from Commands.pm, sub cmdConf) + if ($arg1 =~ /\./) { + $arg1 =~ s/\.+/\./; # Filter Out Unnececary dot's + my ($label, $param) = split /\./, $arg1, 2; # Split the label form parameter + foreach (%::config) { + if ($_ =~ /_\d+_label/){ # we only need those blocks witch have labels + if ($::config{$_} eq $label) { + my ($real_key, undef) = split /_label/, $_, 2; + # "<label>.block" param support. Thanks to "vit" + if ($param ne "block") { + $real_key .= "_"; + $real_key .= $param; + } + $arg1 = $real_key; + last; + }; + }; + }; + }; + return (defined $::config{$arg1})?$::config{$arg1}:""; +} + +# sets and/or refreshes global variables +sub refreshGlobal { + my $var = $_[0]; + + $varStack{".time"} = time; + $varStack{".datetime"} = scalar localtime; + ($varStack{".second"}, $varStack{".minute"}, $varStack{".hour"}) = localtime; + + return unless $net && $net->getState == Network::IN_GAME; + + $varStack{".map"} = (defined $field)?$field->baseName:"undef"; + my $pos = calcPosition($char); $varStack{".pos"} = sprintf("%d %d", $pos->{x}, $pos->{y}); + + $varStack{".hp"} = $char->{hp}; + $varStack{".sp"} = $char->{sp}; + $varStack{".lvl"} = $char->{lv}; + $varStack{".joblvl"} = $char->{lv_job}; + $varStack{".spirits"} = ($char->{spirits} or 0); + $varStack{".zeny"} = $char->{zeny}; + $varStack{".weight"} = $char->{weight}; + $varStack{".maxweight"} = $char->{weight_max}; + $varStack{'.status'} = (join ',', + ('muted')x!!$char->{muted}, + ('dead')x!!$char->{dead}, + map { $statusName{$_} || $_ } keys %{$char->{statuses}} + ) || 'none'; +} + +# get NPC array index +sub getnpcID { + my $arg = $_[0]; + my ($what, $a, $b); + + if (($a, $b) = $arg =~ /^\s*(\d+) (\d+)\s*$/) {$what = 1} + elsif (($a, $b) = $arg =~ /^\s*\/(.+?)\/(\w?)\s*$/) {$what = 2} + elsif (($a) = $arg =~ /^\s*"(.*?)"\s*$/) {$what = 3} + else {return -1} + + my @ids; + foreach my $npc (@{$npcsList->getItems()}) { + if ($what == 1) {return $npc->{binID} if ($npc->{pos}{x} == $a && $npc->{pos}{y} == $b)} + elsif ($what == 2) { + if ($npc->{name} =~ /$a/ || ($b eq "i" && $npc->{name} =~ /$a/i)) {push @ids, $npc->{binID}} + } + else {return $npc->{binID} if $npc->{name} eq $a} + } + if (@ids) {return join ',', @ids} + return -1 +} + +# get player array index +sub getPlayerID { + foreach my $pl (@{$playersList->getItems()}) { + return $pl->{binID} if $pl->name eq $_[0] + } + return -1 +} + +# get monster array index +sub getMonsterID { + foreach my $ml (@{$monstersList->getItems()}) { + return $ml->{binID} if ($ml->name eq $_[0] || $ml->{binType} eq $_[0]); + } + return -1 +} + +# get vender array index +sub getVenderID { + for (my $i = 0; $i < @::venderListsID; $i++) { + next if $::venderListsID[$i] eq ""; + my $player = Actor::get($::venderListsID[$i]); + return $i if $player->name eq $_[0] + } + return -1 +} + +# get inventory item ids +# checked and ok +sub getInventoryIDs { + return unless $char->inventory->isReady(); + my $find = lc($_[0]); + my @ids; + foreach my $item (@{$char->inventory->getItems}) { + if (lc($item->name) eq $find) {push @ids, $item->{binID}} + } + unless (@ids) {push @ids, -1} + return @ids +} + +# get item array index +sub getItemIDs { + my ($item, $pool) = (lc($_[0]), $_[1]); + my @ids; + for (my $id = 0; $id < @{$pool}; $id++) { + next unless $$pool[$id]; + if (lc($$pool[$id]{name}) eq $item) {push @ids, $id} + } + unless (@ids) {push @ids, -1} + return @ids +} + +# get item price from its index +# works with @venderprice +# returns -1 if no shop is being visited +sub getItemPrice { + my ($itemIndex, $pool) = ($_[0], $_[1]); + my $price = -1; + if ($$pool[$itemIndex]) {$price = $$pool[$itemIndex]{price}} + return $price +} + +# get storage array index +# returns -1 if no matching items in storage +sub getStorageIDs { + return unless $char->storage->wasOpenedThisSession(); + my $find = lc($_[0]); + my @ids; + foreach my $item (@{$char->storage->getItems}) { + if (lc($item->name) eq $find) {push @ids, $item->{binID}} + } + unless (@ids) {push @ids, -1} + return @ids +} + +# get amount of sold out slots +sub getSoldOut { + return 0 unless $shopstarted; + my $soldout = 0; + foreach my $aitem (@::articles) { + next unless $aitem; + if ($aitem->{quantity} == 0) {$soldout++} + } + return $soldout +} + +# get amount of an item in inventory +sub getInventoryAmount { + my $arg = lc($_[0]); + return -1 unless ($char->inventory->isReady()); + my $amount = 0; + foreach my $item (@{$char->inventory->getItems}) { + if (lc($item->name) eq $arg) {$amount += $item->{amount}} + } + return $amount +} + +# get amount of an item in cart +sub getCartAmount { + my $arg = lc($_[0]); + return -1 unless ($char->cart->isReady()); + my $amount = 0; + foreach my $item (@{$char->cart->getItems}) { + if (lc($item->name) eq $arg) {$amount += $item->{amount}} + } + return $amount +} + +# get amount of an item in your shop +sub getShopAmount { + my $arg = lc($_[0]); + my $amount = 0; + foreach my $aitem (@::articles) { + next unless $aitem; + if (lc($aitem->{name}) eq $arg) {$amount += $aitem->{quantity}} + } + return $amount +} + +# get amount of an item in storage +# returns -1 if the storage is closed +sub getStorageAmount { + my $arg = lc($_[0]); + return -1 unless ($char->storage->wasOpenedThisSession()); + my $amount = 0; + foreach my $item (@{$char->storage->getItems}) { + if (lc($item->name) eq $arg) {$amount += $item->{amount}} + } + return $amount +} + +# get amount of items for the specifical index in another venders shop +# returns -1 if no shop is being visited +sub getVendAmount { + my ($itemIndex, $pool) = ($_[0], $_[1]); + my $amount = -1; + if ($$pool[$itemIndex]) {$amount = $$pool[$itemIndex]{amount}} + return $amount +} + +# returns random item from argument list +sub getRandom { + my $arg = $_[0]; + my @items; + my $id = 0; + while (($items[$id++]) = $arg =~ /^[, ]*"(.*?)"/) { + $arg =~ s/^[, ]*".*?"//g; + } + pop @items; + unless (@items) { + warning "[macro] wrong syntax in \@random\n", "macro"; + return + } + return $items[rand $id-1] +} + +# returns given argument from a comma separated list +# returns -1 if no such listID exists or when the list is empty or wrong +sub getArgFromList { + my ($listID, $list) = split(/, \s*/, $_[0]); + my @items = split(/,\s*/, $list); + unless (@items) { + warning "[macro] wrong syntax in \@listItem\n", "macro"; + return -1 + } + if ($items[$listID]) { + return $items[$listID] + } else { + warning "[macro] the $listID number item does not exist in the list\n", "macro"; + return -1 + } +} + +# returns the lenght of a comma separated list +sub getListLenght { + my $list = $_[0]; + my @items = split(/,\s*/, $list); + return scalar(@items) +} + +# check if player is in party +sub sameParty { + my $player = shift; + for (my $i = 0; $i < @partyUsersID; $i++) { + next if $partyUsersID[$i] eq ""; + next if $partyUsersID[$i] eq $accountID; + return 1 if $char->{'party'}{'users'}{$partyUsersID[$i]}{'name'} eq $player + } + return 0 +} + +# returns random number within the given range ########### +sub getRandomRange { + my ($low, $high) = split(/,\s*/, $_[0]); + return int(rand($high-$low+1))+$low if (defined $high && defined $low) +} + +sub processCmd { + my $command = $_[0]; + if (defined $_[0]) { + if ($_[0] ne '') { + unless (Commands::run($command)) { + my $errorMsg = sprintf("[macro] %s failed with %s\n", $queue->name, $command); + + my $hookArgs = { + 'message' => $errorMsg, + 'name' => $queue->name, + 'error' => 'Commands::run failed', + }; + Plugins::callHook ('macro/error', $hookArgs); + return $hookArgs->{continue} if $hookArgs->{return}; + + error $errorMsg, "macro"; + undef $queue; + return + } + } + $queue->ok; + if (defined $queue && $queue->finished) {undef $queue} + } else { + my $name = (defined $queue->{subcall}) ? $queue->{subcall}->name : $queue->name; + my $error = $queue->error; + my $errorMsg = sprintf( + "[macro] %s error: %s\n", + $name =~ /^tempMacro\d+$/ && $varStack{'.caller'} ? $varStack{'.caller'}.'.call' : $name, + $error + ); + + my $hookArgs = { + 'message' => $errorMsg, + 'name' => $name, + 'error' => $error, + }; + Plugins::callHook ('macro/error', $hookArgs); + return $hookArgs->{continue} if $hookArgs->{return}; + + error $errorMsg, "macro"; + undef $queue; + return + } + + return 1; +} + +# macro/script +sub callMacro { + return unless defined $queue; + return if $onHold; + my %tmptime = $queue->timeout; + unless ($queue->registered || $queue->overrideAI) { + if (timeOut(\%tmptime)) {$queue->register} + else {return} + } + if (timeOut(\%tmptime) && ai_isIdle()) { + do { + last unless processCmd $queue->next; + Plugins::callHook ('macro/callMacro/process'); + } while !$onHold && $queue && $queue->macro_block; + +=pod + # crashes when error inside macro_block encountered and $queue becomes undefined + my $command = $queue->next; + if ($queue->macro_block) { + while ($queue->macro_block) { + $command = $queue->next; + processCmd($command) + } + } else { + processCmd($command) + } +=cut + } +} + +1; diff --git a/openkore_llm_knowledge/plugins/macro/macro.pl b/openkore_llm_knowledge/plugins/macro/macro.pl new file mode 100644 index 0000000000..5380b4dd3e --- /dev/null +++ b/openkore_llm_knowledge/plugins/macro/macro.pl @@ -0,0 +1,307 @@ +# macro by Arachno +# +# $Id: macro.pl r6744 2009-06-28 20:05:00Z ezza $ +# +# This source code is licensed under the +# GNU General Public License, Version 2. +# See http://www.gnu.org/licenses/gpl.html + +package macro; +my $Version = "2.0.3-svn"; +my ($rev) = q$Revision: 6744 $ =~ /(\d+)/; +our $plugin_folder = $Plugins::current_plugin_folder; + +use strict; +use Plugins; +use Settings; +use Globals; +use Utils; +use Misc; +use Log qw(message error warning); +use Translation qw/T TF/; +use lib $Plugins::current_plugin_folder; +use Macro::Data; +use Macro::Script; +use Macro::Parser qw(parseMacroFile); +use Macro::Automacro qw(automacroCheck consoleCheckWrapper releaseAM); +use Macro::Utilities qw(callMacro); + +######### +# startup +Plugins::register('macro', 'allows usage of macros', \&Unload, \&Reload); + +my $hooks = Plugins::addHooks( + ['configModify', \&onconfigModify, undef], + ['start3', \&onstart3, undef], + ['mainLoop_pre', \&callMacro, undef] +); +my $chooks = Commands::register( + ['macro', "Macro plugin", \&commandHandler] +); +my $autohooks; +my $loghook; +my $cfID; +my $macro_file; + +# onconfigModify +sub onconfigModify { + my (undef, $args) = @_; + if ($args->{key} eq 'macro_file') { + Settings::removeFile($cfID); + $cfID = Settings::addControlFile($args->{val}, loader => [ \&parseAndHook, \%macro]); + Settings::loadByHandle($cfID); + } +} + +# onstart3 +sub onstart3 { + &checkConfig; + $cfID = Settings::addControlFile($macro_file,loader => [\&parseAndHook,\%macro], mustExist => 0); + Settings::loadByHandle($cfID); + + if ( + $interface->isa ('Interface::Wx') + && $interface->{viewMenu} + && $interface->can ('addMenu') + && $interface->can ('openWindow') + ) { + $interface->addMenu ($interface->{viewMenu}, T('Macro debugger'), sub { + $interface->openWindow (T('Macro'), 'Macro::Wx::Debugger', 1); + }, T('Interactive debugger for macro plugin')); + } +} + +# onReload +sub Reload { + message "macro plugin reloading, ", 'success'; + &cleanup; + &onstart3 +} + +# onUnload +sub Unload { + message "macro plugin unloading, ", 'success'; + &cleanup; + Plugins::delHooks($hooks); + Commands::unregister($chooks); +} + +sub cleanup { + message "cleaning up\n", 'success'; + Settings::removeFile($cfID) if defined $cfID; + Log::delHook($loghook); + foreach (@{$autohooks}) {Plugins::delHook($_)} + undef $autohooks; + undef $queue; + undef %macro; + undef %automacro; + undef %varStack +} + +# onFile(Re)load +sub parseAndHook { + my $file = shift; + if (parseMacroFile($file, 0)) { + Plugins::callHook ('macro/parseAndHook'); + &hookOnDemand; return 1 + } + error "error loading $file.\n"; + return 0 +} + +# only adds hooks that are needed +sub hookOnDemand { + foreach (@{$autohooks}) {Plugins::delHook($_)} + undef $autohooks; + Log::delHook($loghook) if defined $loghook; + my %load = ('AI_pre' => 1); + my $hookToLog; + foreach my $a (keys %automacro) { + next if $automacro{$a}->{disabled}; + if (defined $automacro{$a}->{spell}) { + if (!defined $load{'is_casting'}) {$load{'is_casting'} = 1} + if (!defined $load{'packet_skilluse'}) {$load{'packet_skilluse'} = 1} + } + if (defined $automacro{$a}->{areaSpell} && !defined $load{'packet_areaSpell'}) {$load{'packet_areaSpell'} = 1} + if (defined $automacro{$a}->{pm} && !defined $load{'packet_privMsg'}) {$load{'packet_privMsg'} = 1} + if (defined $automacro{$a}->{pubm} && !defined $load{'packet_pubMsg'}) {$load{'packet_pubMsg'} = 1} + if (defined $automacro{$a}->{system} && !defined $load{'packet_sysMsg'}) {$load{'packet_sysMsg'} = 1;} + if (defined $automacro{$a}->{party} && !defined $load{'packet_partyMsg'}) {$load{'packet_partyMsg'} = 1} + if (defined $automacro{$a}->{guild} && !defined $load{'packet_guildMsg'}) {$load{'packet_guildMsg'} = 1} + if (defined $automacro{$a}->{mapchange} && !defined $load{'packet_mapChange'}) {$load{'packet_mapChange'} = 1} + if (defined $automacro{$a}->{hook} && !defined $load{$automacro{$a}->{hook}}) {$load{$automacro{$a}->{hook}} = 1} + if (defined $automacro{$a}->{console} && !defined $hookToLog) {$hookToLog = 1} + if (defined $automacro{$a}->{playerguild} && !defined $load{'player'}) {$load{'player'} = 1} + if (defined $automacro{$a}->{playerguild} && !defined $load{'charNameUpdate'}) {$load{'charNameUpdate'} = 1} + } + foreach my $l (keys %load) { + message "[macro] hooking to $l\n"; + push(@{$autohooks}, Plugins::addHook($l, \&automacroCheck)) + } + if ($hookToLog) { + message "[macro] hooking to log\n"; + $loghook = Log::addHook(\&consoleCheckWrapper) + } +} + +# checks macro configuration +sub checkConfig { + $timeout{macro_delay}{timeout} = 1 unless defined $timeout{macro_delay}; + $macro_file = (defined $::config{macro_file})?$::config{macro_file}:"macros.txt"; + + if (!defined $::config{macro_orphans} || $::config{macro_orphans} !~ /^(?:terminate|reregister(?:_safe)?)$/) { + warning "[macro] orphans: using method 'terminate'\n"; + configModify('macro_orphans', 'terminate') + } + + return 1 +} + +# macro command handler +sub commandHandler { + ### no parameter given + if (!defined $_[1]) { + message "usage: macro [MACRO|list|stop|set|version|reset] [automacro]\n", "list"; + message "macro MACRO: run macro MACRO\n". + "macro list: list available macros\n". + "macro status: shows current status\n". + "macro stop: stop current macro\n". + "macro pause: interrupt current macro\n". + "macro resume: resume interrupted macro\n". + "macro version: print macro plugin version\n". + "macro reset [automacro]: resets run-once status for all or given automacro(s)\n"; + return + } + my ($arg, @params) = split(/\s+/, $_[1]); + ### parameter: list + if ($arg eq 'list') { + message(sprintf("The following macros are available:\n%smacros%s\n","-"x10,"-"x9), "list"); + foreach my $m (keys %macro) {message "$m\n" unless $m =~ /^tempMacro/} + message(sprintf("%sautomacros%s\n", "-"x8, "-"x7), "list"); + foreach my $a (sort { + ($automacro{$a}->{priority} or 0) <=> ($automacro{$b}->{priority} or 0) + } keys %automacro) {message "$a\n"} + message(sprintf("%sPerl Sub%s\n", "-"x9, "-"x8), "list"); + foreach my $s (@perl_name) {message "$s\n"} + message(sprintf("%s\n","-"x25), "list"); + ### parameter: status + } elsif ($arg eq 'status') { + if (defined $queue) { + message(sprintf("macro %s\n", $queue->name), "list"); + message(sprintf("status: %s\n", $queue->registered?"running":"waiting")); + my %tmp = $queue->timeout; + message(sprintf("delay: %ds\n", $tmp{timeout})); + message(sprintf("line: %d\n", $queue->line)); + message(sprintf("override AI: %s\n", $queue->overrideAI?"yes":"no")); + message(sprintf("paused: %s\n", $onHold?"yes":"no")); + message(sprintf("finished: %s\n", $queue->finished?"yes":"no")); + } else { + message "There's no macro currently running.\n" + } + ### parameter: stop + } elsif ($arg eq 'stop') { + undef $queue; + message "macro queue cleared.\n" + ### parameter: pause + } elsif ($arg eq 'pause') { + if (defined $queue) { + $onHold = 1; + message "macro ".$queue->name." paused.\n" + } else { + warning "There's no macro currently running.\n" + } + ### parameter: resume + } elsif ($arg eq 'resume') { + if (defined $queue) { + $onHold = 0; + message "macro ".$queue->name." resumed.\n" + } else { + warning "There's no macro currently running.\n" + } + ### parameter: reset + } elsif ($arg eq 'reset') { + if (!defined $params[0]) { + foreach my $am (keys %automacro) {undef $automacro{$am}->{disabled}} + message "automacro runonce list cleared.\n"; + return + } + for my $reset (@params) { + my $ret = releaseAM($reset); + if ($ret == 1) {message "automacro $reset reenabled.\n"} + elsif ($ret == 0) {warning "automacro $reset wasn't disabled.\n"} + else {error "automacro $reset not found.\n"} + } + ### parameter: version + } elsif ($arg eq 'version') { + message "macro plugin version $Version\n", "list"; + message "macro.pl ". $rev."\n"; + message "Macro::Automacro ".$Macro::Automacro::rev."\n"; + message "Macro::Script ".$Macro::Script::rev."\n"; + message "Macro::Parser ".$Macro::Parser::rev."\n"; + message "Macro::Utilities ".$Macro::Utilities::rev."\n"; + message "Macro::Data ".$Macro::Data::rev."\n" + ### debug + } elsif ($arg eq 'varstack') { + message "Varstack List\n", "menu"; + foreach my $v (keys %varStack) { + message "\$varStack{$v} = [".$varStack{$v}."]\n", "menu" + } + ### parameter: probably a macro + } else { + if (defined $queue) { + warning "a macro is already running. Wait until the macro has finished or call 'macro stop'\n"; + return + } + my ($repeat, $oAI, $exclusive, $mdelay, $orphan) = (1, 0, 0, undef, undef); + my $cparms; + for (my $idx = 0; $idx <= @params; $idx++) { + if ($params[$idx] eq '-repeat') {$repeat += $params[++$idx]} + if ($params[$idx] eq '-overrideAI') {$oAI = 1} + if ($params[$idx] eq '-exclusive') {$exclusive = 1} + if ($params[$idx] eq '-macro_delay') {$mdelay = $params[++$idx]} + if ($params[$idx] eq '-orphan') {$orphan = $params[++$idx]} + if ($params[$idx] =~ /^--/) {$cparms = substr(join(' ', map { "$_" } @params[$idx..($#params)]), 2); last} + } + + delete $varStack{$_} for grep /^\.param\d+$/, keys %varStack; + if ($cparms) { + #parse macro parameters + my @new_params = $cparms =~ /"[^"]+"|\S+/g; + foreach my $p (1..@new_params) { + $varStack{".param".$p} = $new_params[$p-1]; + $varStack{".param".$p} = substr($varStack{".param".$p}, 1, -1) if ($varStack{".param".$p} =~ /^".*"$/); # remove quotes + } + } + + $queue = new Macro::Script($arg, $repeat); + if (!defined $queue) {error "macro $arg not found or error in queue\n"} + else { + $onHold = 0; + if ($oAI) {$queue->overrideAI(1)} + if ($exclusive) {$queue->interruptible(0)} + if (defined $mdelay) {$queue->setMacro_delay($mdelay)} + if (defined $orphan) {$queue->orphan($orphan)} + } + } +} + +1; + +__END__ + +=head1 NAME + +macro.pl - plugin for openkore 2.0.0 and later + +=head1 AVAILABILITY + +Get the latest release from L<https://openkore.com/wiki/Macro_plugin> +or from GIT: + +C<git co https://github.com/OpenKore/openkore/tree/master/plugins/macro> + +=head1 AUTHOR + +Arachno <arachnophobia at users dot sf dot net> + +=cut diff --git a/openkore_llm_knowledge/plugins/map/map.pl b/openkore_llm_knowledge/plugins/map/map.pl new file mode 100644 index 0000000000..7a5a744a83 --- /dev/null +++ b/openkore_llm_knowledge/plugins/map/map.pl @@ -0,0 +1,314 @@ +package OpenKore::Plugin::Map; +############################################################################### +# Plugin to display a map of the current location. +# +# control/config.txt +# map_showWater 1 - show water tiles with blue background +# map_showBlockedShopTiles 1 - show tiles within 3 tiles of an NPC with green background +# map_showGroundEffects 1 - show ground effects with red background + +use strict; + +use Plugins; +use Globals; +use Utils; +use Misc; +use Log qw(message warning error); +use AI; +use Time::HiRes; +use Task::UseSkill; +use Skill; + +Plugins::register( 'map', 'map', \&Unload, \&Unload ); + +our $mode = 'auto'; +our $fw; +our $fh; +our $x1; +our $x2; +our $y1; +our $y2; +our $grid; +our $color_grid; + +my $hooks = Plugins::addHooks( + [ 'curses/updateObjects' => \&onUpdateObjects ], +); + +my $chooks = Commands::register( + [ 'map', "Map plugin", \&onMapCommand ], +); + +sub Unload { + Plugins::delHooks($hooks); + message "map unloaded.\n"; +} + +sub onUpdateObjects { + onMapCommand( 'map', int( ( $interface->{winObjectsWidth} - 1 ) / 2 ) ); +} + +sub onMapCommand { + my ( @params ) = split /\s+/, $_[1]; + + if ( !@params ) { + Log::message("map: Usage\n"); + Log::message(" map [width] [height] - display map (default size: 30 15)\n"); + Log::message(" map set x y - set a map cell\n"); + return; + } + + if ( !$char || !$char->{pos} ) { + Log::message( "map: No character yet!\n" ); + return; + } + + my $gw = $params[0] || 30; + my $gh = $params[1] || 15; + + if ( $interface->{winObjects} ) { + my $map = drawcolormap( $gw, $gh ); + my $line = $interface->{winObjectsHeight} - @$map; + $interface->printw( $interface->{winObjects}, $line++, 0, $_->{pic}, @{ $_->{mapchars} } ) foreach @$map; + Curses::noutrefresh( $interface->{winObjects} ); + } else { + my $lines = drawmap( $gw, $gh ); + Log::message( "$_\n" ) foreach @$lines; + } +} + +sub drawmap { + return if !$field; + my $map = drawcolormap( @_ ); + return [ map { join '', @{ $_->{mapchars} } } @$map ]; +} + +sub drawcolormap { + my ( $gw, $gh ) = @_; + + my $pos = calcPosition( $char ); + + $fw = $field->width; + $fh = $field->height; + $x1 = $pos->{x} - $gw; + $x2 = $pos->{x} + $gw; + $y1 = $pos->{y} - $gh; + $y2 = $pos->{y} + $gh; + + our $base_key ||= ''; + our $base_grid; + our $base_color_grid; + + my $new_key = join ':', $pos->{x}, $pos->{y}, $gw, $gh; + + if ( $base_key ne $new_key ) { + + $grid = []; + $color_grid = []; + + # Add walkability. + foreach my $y ( reverse $y1 .. $y2 ) { + push @$grid, my $row = []; + push @$color_grid, []; + + if ( $y < 0 || $y >= $fh ) { + @$row = ( '~' ) x 2 * $gw + 1; + next; + } + + foreach my $x ( $x1 .. $x2 ) { + if ( $field->isWalkable( $x, $y ) ) { + push @$row, ' '; + add_pos( 'bg_blue', { x => $x, y => $y } ) if $config{map_showWater} && $field->getBlock( $x, $y ) == Field::TILE_WALK|Field::TILE_WATER; + } else { + push @$row, '#'; + } + } + } + + # Add tiles which are to close to npcs to be able to open a shop. + if ( $config{map_showBlockedShopTiles} ) { + foreach my $npc ( @{ $npcsList->getItems } ) { + foreach my $y ( -3 .. 3 ) { + foreach my $x ( -3 .. 3 ) { + add_pos( 'bg_green', { x => $npc->{pos}->{x} + $x, y => $npc->{pos}->{y} + $y } ); + } + } + } + } + + $base_key = $new_key; + $base_grid = $grid; + $base_color_grid = $color_grid; + } + + # Clone the grid. + $grid = [ map { [ @$_ ] } @$base_grid]; + $color_grid = [ map { [ @$_ ] } @$base_color_grid]; + + # Add ground effects. Background-only. + if ( $config{map_showGroundEffects} ) { + foreach ( @spellsID ) { + next if !$_ || !$spells{$_}; + add_pos( 'bg_red', $spells{$_}{pos} ); + } + } + + # Add follow preference. + if ( $config{follow} && $config{followTarget} ) { + my ( $master ) = grep { $_->name eq $config{followTarget} && !$_->{dead} } @{ $playersList->getItems }; + if ( $master ) { + my $pos = calcPosition( $master ); + my $poss = []; + my $min = ($config{followDistanceMin} - 0.5) ** 2; + my $max = ($config{followDistanceMax} + 0.5) ** 2; + foreach my $dy ( -$config{followDistanceMax} .. $config{followDistanceMax} ) { + foreach my $dx ( -$config{followDistanceMax} .. $config{followDistanceMax} ) { + next if $dx * $dx + $dy * $dy < $min; + next if $dx * $dx + $dy * $dy > $max; + my $p = { x => $pos->{x} + $dx, y => $pos->{y} + $dy }; + push @$poss, $p if $field->isWalkable( $p->{x}, $p->{y} ); + } + } + add_pos( '.', $_ ) foreach @$poss; + add_pos( 'T', $master->{pos_to} ); + + foreach my $p ( @$poss ) { + $p->{threat} = ( abs( $char->{pos_to}->{x} - $p->{x} ) + abs( $char->{pos_to}->{y} - $p->{y} ) ) / 10; + foreach my $monster ( @{ $monstersList->getItems } ) { + my $pm = $monster->{pos_to} || $monster->{pos}; + $p->{threat} += 20 - ( abs( $pm->{x} - $p->{x} ) + abs( $pm->{y} - $p->{y} ) ); + } + } + my ( $safest ) = sort { $a->{threat} <=> $b->{threat} } @$poss; + add_pos( '*', $safest ); + } + } + + # Add current route (if any). + my $route_task; + foreach ( 0 .. 10 ) { + if ( AI::action( $_ ) eq '' ) { + last; + } elsif ( AI::action( $_ ) eq 'route' ) { + $route_task = AI::args( $_ ); + last; + } + } + $route_task = $route_task->{ST_subtask} if $route_task && $route_task->isa( 'Task::MapRoute' ); + if ( $route_task && $route_task->{solution} ) { + add_pos( '.', $_ ) foreach @{ $route_task->{solution} }; + } + + # Add player. + add_pos( 't', $char->{pos_to} ); + add_pos( '@', $pos ); + + # Add portals. + for ( my $i = 0 ; $i < @portalsID ; $i++ ) { + next if $portalsID[$i] eq ""; + add_pos( 'o', $portals{ $portalsID[$i] }->{pos} ); + } + + # Add NPCs. + foreach (@{$npcsList->getItems}) { + add_pos( 'n', $_->{pos} ); + } + + # Add slaves. + foreach my $slave ( @{ $slavesList->getItems } ) { + add_pos( 's', calcPosition( $slave ) ); + } + + # Add players. + foreach my $player ( @{ $playersList->getItems } ) { + add_pos( 'p', calcPosition( $player ) ); + } + + # Add vendors. + foreach my $id ( @venderListsID ) { + next if !$id; + my $player = Actor::get( $id ); + next if !$player || !$player->{pos_to}; + + # Vendors don't move, so we don't need to calculate their position. + add_pos( '$', $player->{pos_to} ); + } + + # Add chats. + foreach my $id ( @chatRoomsID ) { + next if !$id; + my $room = $chatRooms{$id}; + next if !$room; + my $player = Actor::get( $room->{ownerID} ); + next if !$player || !$player->{pos_to}; + + # Chat rooms don't move, so we don't need to calculate their position. + add_pos( 'C', $player->{pos_to} ); + } + + # Add monsters. + foreach my $monster ( @{ $monstersList->getItems } ) { + if ( $config{monster_filter} ) { + next if $monster->{name_given} !~ /$config{monster_filter}/igs; + add_pos( 'M', calcPosition( $monster ), 'red' ); + } else { + add_pos( ':', $monster->{pos_to} ); + add_pos( 'm', calcPosition( $monster ) ); + } + } + + # Render map into a picture (containing color information) and mapchars (containing the ASCII representation). + my $lines = []; + foreach my $i ( 0 .. $#$grid ) { + my $row = $grid->[$i]; + my $crow = $color_grid->[$i]; + my $line = join '', @$row; + if ( !@$crow ) { + push @$lines, { pic => '{normal}@*', mapchars => [$line] }; + next; + } + my $color = $crow->[0] || 'normal'; + my $mapchars = []; + my $pic = "{$color}@*"; + my $i0 = 0; + my $i1 = 0; + foreach ( @$crow ) { + $_ ||= 'normal'; + if ( $color ne $_ ) { + push @$mapchars, substr $line, $i0, $i1 - $i0 if $i1; + + $pic .= "{$_}@*"; + $color = $_; + $i0 = $i1; + } + $i1++; + } + push @$mapchars, substr $line, $i0, $i1 - $i0; + if ( $i1 != length $line ) { + $pic .= '{normal}@*'; + push @$mapchars, substr $line, $i1, length( $line ) - $i1; + } + push @$lines, { pic => $pic, mapchars => $mapchars }; + } + + return $lines; +} + +sub add_pos { + my ( $ch, $pos, $color ) = @_; + return if $pos->{x} < $x1; + return if $pos->{x} > $x2; + return if $pos->{y} < $y1; + return if $pos->{y} > $y2; + + # Don't touch wall tiles once they're defined. + return if $grid->[ $y2 - $pos->{y} ]->[ $pos->{x} - $x1 ] eq '#'; + + ( $ch, $color ) = ( undef, $ch ) if $ch && $ch =~ /^bg_/o; + + $grid->[ $y2 - $pos->{y} ]->[ $pos->{x} - $x1 ] = $ch if defined $ch && length $ch < 2; + $color_grid->[ $y2 - $pos->{y} ]->[ $pos->{x} - $x1 ] = $color if $color; +} + +1; diff --git a/openkore_llm_knowledge/plugins/profiles/profiles.pl b/openkore_llm_knowledge/plugins/profiles/profiles.pl new file mode 100644 index 0000000000..bb1f2418ed --- /dev/null +++ b/openkore_llm_knowledge/plugins/profiles/profiles.pl @@ -0,0 +1,220 @@ +######################################################################### +# This software is open source, licensed under the GNU General Public +# License, version 2. +# Basically, this means that you're allowed to modify and distribute +# this software. However, if you distribute modified versions, you MUST +# also distribute the source code. +# See http://www.gnu.org/licenses/gpl.html for the full license. +######################################################################### +# profiles selector (full) +# d3fc0n 30/12/2007 +######################################################################### + +package profiles; + +use strict; +use File::Spec; +use Plugins; +use Globals qw($interface $quit); +use Log qw(debug message warning error); +use Getopt::Long; +use Settings qw( %sys ); + +our $profile_folder = "profiles"; +our $profile; + +return unless +Plugins::register('profiles', 'Profiles Selector', \&on_unload); + +my $hooks = Plugins::addHooks( + ['parse_command_line', \&onParseCommandLine], + ['usage', \&onUsage], + ['mainLoop::setTitle', \&setTitle, undef], + ['start', \&onStart] + ); + +my $chooks = Commands::register( + ['changeProfile', "Changes profile", \&commandHandler] + ); + +sub on_unload { + Plugins::delHook($hooks); + Commands::unregister($chooks); + undef $profile_folder; +} + +sub onUsage { + my ( undef, $params ) = @_; + push @{ $params->{options} }, { plugin => 'profiles', long => '--profile=PROFILE', description => 'profile to use (default: prompt)' }; +} + +sub setTitle { + my (undef, $args) = @_; + $args->{return} = ($profile) ? "[$profile] " . $args->{return} : $args->{return}; +} + +sub onParseCommandLine { + GetOptions( 'profile=s' => \$profile ); +} + +sub onStart { + opendir my $d, $profile_folder; + my @conlist = readdir($d); + closedir $d; + + my @profiles; + + foreach (@conlist) { + next unless -d File::Spec->catdir($profile_folder, $_); + next if ($_ =~ /^\.|^#/); + push @profiles, $_; + } + + if (!@profiles) { + message "No profiles found, using standard control folder\n"; + return; + } + + @profiles = sort { $a cmp $b } @profiles; + push @profiles, 'Use standard control folder'; + + if ( $profile && !grep { $_ eq $profile } @profiles ) { + printf "Unknown profile [%s] requested.\n", $profile; + $profile = undef; + } + + if ( !$profile && @profiles ) { + my $choice = $interface->showMenu( # + "Please choose a Profiles folder.", + \@profiles, + title => "Profiles Selector" + ); + + my $num = @profiles; + return 0 if $choice == $num - 1; + return $quit = 1 if $choice == -1; + + $profile = $profiles[$choice]; + } + + unshift @Settings::controlFolders, File::Spec->catdir( $profile_folder, $profile ) if $profile; +} + +sub commandHandler { + my (undef, $new_profile) = @_; + + if (!grep { $_ eq File::Spec->catdir($profile_folder, $profile) } @Settings::controlFolders) { + error "[profiles] Profile loaded not found in control folder list\n"; + return; + } + + opendir my $d, $profile_folder; + my @conlist = readdir($d); + closedir $d; + + if (!$new_profile) { + + my @profiles; + + foreach (@conlist) { + next unless -d File::Spec->catdir($profile_folder, $_); + next if ($_ =~ /^\.|^#/); + push @profiles, $_; + } + + @profiles = sort { $a cmp $b } @profiles; + push @profiles, 'Use standard control folder'; + + if (@profiles) { + my $choice = $interface->showMenu( # + "Please choose a Profiles folder.", + \@profiles, + title => "Profiles Selector" + ); + + my $num = @profiles; + return 0 if $choice == $num - 1; + return $quit = 1 if $choice == -1; + + $new_profile = $profiles[$choice]; + } else { + error "[profiles] There are no profiles in profiles folder\n"; + return; + } + } else { + my $found = 0; + foreach (@conlist) { + next unless -d File::Spec->catdir($profile_folder, $_); + next if ($_ =~ /^\.|^#/); + $found = 1 if ($new_profile eq $_); + } + if (!$found) { + error "[profiles] Provided profile not found in profiles folder\n"; + return; + } + } + + my $new_profile_folder = File::Spec->catdir($profile_folder, $new_profile); + my %reloadFiles; + + message "[profiles] Looking for loaded files in old profile '".$profile."' to unload \n", "system"; + foreach my $file (@{$Settings::files->getItems}) { + next if ($file->{'type'} != Settings::CONTROL_FILE_TYPE); + my $filepath; + if ($file->{'autoSearch'} == 1) { + $filepath = Settings::_findFileFromFolders($file->{'name'}, \@Settings::controlFolders); + } else { + $filepath = $file->{'name'}; + } + my (undef,$directories,$filename) = File::Spec->splitpath($filepath); + my @dirs = File::Spec->splitdir($directories); + + if ($dirs[-2] eq $profile) { + message "[profiles] Unloading file '".$filename."' from old profile '".$profile."'\n"; + $reloadFiles{$file->{'index'}} = $filename; + } + } + + opendir my $d, $new_profile_folder; + my @newProfileFiles = readdir($d); + closedir $d; + + message "[profiles] Looking for files in new profile '".$new_profile."'\n", "system"; + foreach my $filename (@newProfileFiles) { + next unless -f File::Spec->catdir($new_profile_folder, $filename); + next if ($filename =~ /^\./); + foreach my $file (@{$Settings::files->getItems}) { + next if ($file->{'type'} != Settings::CONTROL_FILE_TYPE); + next if (exists $reloadFiles{$file->{'index'}}); + my $name = $file->{'autoSearch'} == 1 ? $file->{'name'} : $file->{'internalName'}; + if ($name eq $filename) { + $reloadFiles{$file->{'index'}} = $filename; + message "[profiles] Found control file '".$filename."' in new profile '".$new_profile."'\n"; + } + } + } + + @Settings::controlFolders = map { $_ eq File::Spec->catdir($profile_folder, $profile) ? $new_profile_folder : $_ } @Settings::controlFolders; + + my $progressHandler = sub { + my ($filename) = @_; + message "[profiles] Loading ".$filename."...\n"; + }; + + message "[profiles] Loading files\n", "system"; + my @files; + foreach my $file_index (keys %reloadFiles) { + my $file = $Settings::files->get($file_index); + if ($file->{'autoSearch'} == 0) { + $file->{'name'} = Settings::_findFileFromFolders($file->{'internalName'}, \@Settings::controlFolders); + } + push (@files, $file); + } + + Settings::loadFiles(\@files, $progressHandler); + + message "[profiles] Loading finished, profile '".$new_profile."' loaded\n", "system"; + $profile = $new_profile; +} + +return 1; diff --git a/openkore_llm_knowledge/plugins/reconnect/reconnect.pl b/openkore_llm_knowledge/plugins/reconnect/reconnect.pl new file mode 100644 index 0000000000..b2b3a6c144 --- /dev/null +++ b/openkore_llm_knowledge/plugins/reconnect/reconnect.pl @@ -0,0 +1,61 @@ +# This package depends on two entries in control/timeouts.txt. +# * reconnect_backoff 30,60,180 +# * reconnect_random 20 +# reconnect_backoff is a comma-separated list of timeouts which will be used in order when we get repeatedly disconnected from the server. +# reconnect_random is the maximum random amount of time to be added to all reconnect times. This only applies if reconnect_backoff is defined. Default is zero. +# +package OpenKore::Plugins::reconnect; + +use strict; + +use Globals qw( %config %masterServers %timeout ); +use Log qw( &message ); +use Plugins; +use Utils qw( &min ); +use Translation qw( &TF ); + +our $default; +our $counter = 0; + +Plugins::register( 'reconnect', 'v1.0', \&unload ); + +my $hooks = Plugins::addHooks( # + [ 'Network::connectTo' => \&trying_to_connect ], + [ 'in_game' => \&connected ], +); + +sub unload { + Plugins::delHooks( $hooks ); +} + +sub trying_to_connect { + my ( undef, $params ) = @_; + return if($config{XKore} eq 1 || $config{XKore} eq 3); + # Only trigger if we're connecting to the login server. + next if $masterServers{ $config{master} }->{ip} ne $params->{host}; + next if $masterServers{ $config{master} }->{port} ne $params->{port}; + + my $timeout = timeout(); + + $timeout{reconnect} = { timeout => timeout() }; + $counter++; + + if ( $counter > 1 ) { + message TF( "[reconnect] Login retry number %d, setting reconnect timeout to %d seconds.\n", $counter, $timeout{reconnect}->{timeout} ), 'success'; + } +} + +sub connected { + return if($config{XKore} eq 1 || $config{XKore} eq 3); + $counter = 0; + $timeout{reconnect} = { timeout => timeout() }; +} + +# Return the current timeout if there is one. +sub timeout { + my @timeouts = split /\s*,\s*/, $timeout{reconnect_backoff}->{timeout} || ''; + return $timeout{reconnect}->{timeout} if !@timeouts; + $timeouts[ min( $counter, $#timeouts ) ] + int rand( $timeout{reconnect_random}->{timeout} || 0 ); +} + +1; From 18e5be61c274bab230260d5f9b4831806485e579 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Bosc=C3=A1?= <juan_bosca@live.com> Date: Tue, 10 Mar 2026 18:21:50 -0300 Subject: [PATCH 06/18] Curate gameplay tables and networking knowledge bundle --- .../knowledge/debugging_guide.md | 27 + .../knowledge/networking_packets.md | 27 + .../knowledge/table_reference.md | 24 + .../knowledge/xkore_modes.md | 21 + .../networking/ClientReceive.pm | 216 + .../networking/DirectConnection.pm | 662 + .../networking/MessageTokenizer.pm | 210 + openkore_llm_knowledge/networking/Network.pm | 53 + .../networking/Network/Receive/ServerType0.pm | 1613 ++ .../networking/Network/Receive/iRO.pm | 44 + .../networking/Network/Receive/kRO.pm | 23 + .../networking/Network/Send/ServerType0.pm | 512 + .../networking/Network/Send/iRO.pm | 50 + .../networking/Network/Send/kRO.pm | 23 + .../Network/XKore2/AccountServer.pm | 41 + .../networking/Network/XKore2/CharServer.pm | 50 + .../networking/Network/XKore2/MapServer.pm | 866 ++ .../networking/PacketParser.pm | 538 + openkore_llm_knowledge/networking/Receive.pm | 12561 ++++++++++++++++ openkore_llm_knowledge/networking/Send.pm | 3589 +++++ openkore_llm_knowledge/networking/XKore.pm | 642 + openkore_llm_knowledge/networking/XKore2.pm | 187 + .../networking/XKoreProxy.pm | 764 + .../tables/SKILL_id_handle.txt | 1707 +++ .../tables/STATUS_id_handle.txt | 1204 ++ openkore_llm_knowledge/tables/elements.txt | 9 + openkore_llm_knowledge/tables/iRO/items.txt | 11764 +++++++++++++++ openkore_llm_knowledge/tables/iRO/maps.txt | 1038 ++ .../tables/iRO/recvpackets.txt | 1580 ++ .../tables/iRO/skillnametable.txt | 993 ++ openkore_llm_knowledge/tables/itemtypes.txt | 21 + openkore_llm_knowledge/tables/kRO/items.txt | 8415 +++++++++++ openkore_llm_knowledge/tables/kRO/maps.txt | 723 + .../tables/kRO/recvpackets.txt | 1056 ++ .../tables/kRO/skillnametable.txt | 999 ++ .../tables/msgstringtable.txt | 3577 +++++ .../tables/packetdescriptions.txt | 1032 ++ openkore_llm_knowledge/tables/packetlist.txt | 3108 ++++ openkore_llm_knowledge/tables/portals.txt | 3576 +++++ openkore_llm_knowledge/tables/servers.txt | 354 + openkore_llm_knowledge/tables/skillsarea.txt | 104 + .../tables/statusnametable.txt | 1204 ++ 42 files changed, 65207 insertions(+) create mode 100644 openkore_llm_knowledge/knowledge/debugging_guide.md create mode 100644 openkore_llm_knowledge/knowledge/networking_packets.md create mode 100644 openkore_llm_knowledge/knowledge/table_reference.md create mode 100644 openkore_llm_knowledge/knowledge/xkore_modes.md create mode 100644 openkore_llm_knowledge/networking/ClientReceive.pm create mode 100644 openkore_llm_knowledge/networking/DirectConnection.pm create mode 100644 openkore_llm_knowledge/networking/MessageTokenizer.pm create mode 100644 openkore_llm_knowledge/networking/Network.pm create mode 100644 openkore_llm_knowledge/networking/Network/Receive/ServerType0.pm create mode 100644 openkore_llm_knowledge/networking/Network/Receive/iRO.pm create mode 100644 openkore_llm_knowledge/networking/Network/Receive/kRO.pm create mode 100644 openkore_llm_knowledge/networking/Network/Send/ServerType0.pm create mode 100644 openkore_llm_knowledge/networking/Network/Send/iRO.pm create mode 100644 openkore_llm_knowledge/networking/Network/Send/kRO.pm create mode 100644 openkore_llm_knowledge/networking/Network/XKore2/AccountServer.pm create mode 100644 openkore_llm_knowledge/networking/Network/XKore2/CharServer.pm create mode 100644 openkore_llm_knowledge/networking/Network/XKore2/MapServer.pm create mode 100644 openkore_llm_knowledge/networking/PacketParser.pm create mode 100644 openkore_llm_knowledge/networking/Receive.pm create mode 100644 openkore_llm_knowledge/networking/Send.pm create mode 100644 openkore_llm_knowledge/networking/XKore.pm create mode 100644 openkore_llm_knowledge/networking/XKore2.pm create mode 100644 openkore_llm_knowledge/networking/XKoreProxy.pm create mode 100644 openkore_llm_knowledge/tables/SKILL_id_handle.txt create mode 100644 openkore_llm_knowledge/tables/STATUS_id_handle.txt create mode 100644 openkore_llm_knowledge/tables/elements.txt create mode 100644 openkore_llm_knowledge/tables/iRO/items.txt create mode 100644 openkore_llm_knowledge/tables/iRO/maps.txt create mode 100644 openkore_llm_knowledge/tables/iRO/recvpackets.txt create mode 100644 openkore_llm_knowledge/tables/iRO/skillnametable.txt create mode 100644 openkore_llm_knowledge/tables/itemtypes.txt create mode 100755 openkore_llm_knowledge/tables/kRO/items.txt create mode 100755 openkore_llm_knowledge/tables/kRO/maps.txt create mode 100644 openkore_llm_knowledge/tables/kRO/recvpackets.txt create mode 100755 openkore_llm_knowledge/tables/kRO/skillnametable.txt create mode 100644 openkore_llm_knowledge/tables/msgstringtable.txt create mode 100644 openkore_llm_knowledge/tables/packetdescriptions.txt create mode 100644 openkore_llm_knowledge/tables/packetlist.txt create mode 100644 openkore_llm_knowledge/tables/portals.txt create mode 100644 openkore_llm_knowledge/tables/servers.txt create mode 100644 openkore_llm_knowledge/tables/skillsarea.txt create mode 100644 openkore_llm_knowledge/tables/statusnametable.txt diff --git a/openkore_llm_knowledge/knowledge/debugging_guide.md b/openkore_llm_knowledge/knowledge/debugging_guide.md new file mode 100644 index 0000000000..25bc17294c --- /dev/null +++ b/openkore_llm_knowledge/knowledge/debugging_guide.md @@ -0,0 +1,27 @@ +# Debugging Guide (Networking & Tables) + +## 1) Confirm server/table alignment +- Verify the active server profile in `tables/servers.txt`. +- Confirm matching `recvpackets.txt` set (e.g., kRO vs iRO). +- Packet mismatch symptoms: unknown switch warnings, broken map/actor parsing, failed actions. + +## 2) Trace receive path +- Start from `networking/Receive.pm` dispatch behavior. +- Compare overridden handlers in server-specific receive modules (`ServerType0`, `kRO`, `iRO`). +- Use packet references (`tables/packetlist.txt`, `tables/packetdescriptions.txt`) to identify suspect opcodes. + +## 3) Trace send path +- Inspect `networking/Send.pm` and server-specific send modules. +- Validate whether a command/action is encoded in the expected packet version/format. + +## 4) XKore-specific troubleshooting +- For bridge issues, inspect `networking/XKore.pm` lifecycle and channel forwarding. +- For proxy/session issues, inspect `networking/XKore2.pm` and `Network/XKore2/{AccountServer,CharServer,MapServer}.pm`. +- Ensure client/server stage transitions match expected state. + +## 5) Practical iterative workflow +1. Reproduce issue with minimal actions. +2. Isolate receive vs send failure. +3. Validate server profile + recvpackets table pairing. +4. Check server-specific module overrides. +5. Re-test and document exact packet/table combination that works. diff --git a/openkore_llm_knowledge/knowledge/networking_packets.md b/openkore_llm_knowledge/knowledge/networking_packets.md new file mode 100644 index 0000000000..03573d8adb --- /dev/null +++ b/openkore_llm_knowledge/knowledge/networking_packets.md @@ -0,0 +1,27 @@ +# Networking & Packets + +## Packet handling architecture +OpenKore networking is structured around base transport/state modules plus packet parser/dispatcher modules: +- `networking/Network.pm` and `networking/DirectConnection.pm` for connection state and transport. +- `networking/PacketParser.pm` and `networking/MessageTokenizer.pm` for framing/tokenization and packet reconstruction support. +- `networking/Receive.pm` for incoming packet dispatch. +- `networking/Send.pm` for outgoing packet construction. + +## Receive flow (high level) +1. Bytes arrive from the active network backend. +2. Tokenizer/parser identifies packet boundaries and switch IDs. +3. `Receive.pm` maps switches to handlers. +4. Server-type subclasses (for example `Network/Receive/ServerType0.pm`, `Network/Receive/kRO.pm`, `Network/Receive/iRO.pm`) override or extend behavior. +5. Handlers update runtime state (actors, inventory, map/chat/combat events). + +## Send flow (high level) +1. Runtime logic requests an action (move, skill, talk, item ops). +2. `Send.pm` builds base packet payloads. +3. Server-type send classes (for example `Network/Send/ServerType0.pm`, `kRO.pm`, `iRO.pm`) apply server-specific packet formats. +4. Transport layer sends encoded bytes to the game server/proxy endpoint. + +## Table coupling +Packet behavior is tightly coupled to table data: +- `tables/servers.txt` selects server profile/type. +- `tables/*/recvpackets.txt` maps opcode formats by server build/family. +- `tables/packetlist.txt` and `tables/packetdescriptions.txt` assist packet reference/debug workflows. diff --git a/openkore_llm_knowledge/knowledge/table_reference.md b/openkore_llm_knowledge/knowledge/table_reference.md new file mode 100644 index 0000000000..1b9b90f556 --- /dev/null +++ b/openkore_llm_knowledge/knowledge/table_reference.md @@ -0,0 +1,24 @@ +# Table Reference + +This curated table subset focuses on gameplay lookups and packet/network references. + +## Core network/profile tables +- `tables/servers.txt` — master server profile definitions and serverType anchors. +- `tables/packetlist.txt` — packet index/reference list. +- `tables/packetdescriptions.txt` — textual packet purpose descriptions. +- `tables/kRO/recvpackets.txt`, `tables/iRO/recvpackets.txt` — server-family packet structure maps. + +## Gameplay identity/reference tables +- `tables/statusnametable.txt` — status effect name mapping. +- `tables/SKILL_id_handle.txt` — skill ID ↔ handle mapping. +- `tables/STATUS_id_handle.txt` — status ID ↔ handle mapping. +- `tables/itemtypes.txt` — item type categories. +- `tables/elements.txt` — elemental mapping. + +## Gameplay world/content examples +- `tables/portals.txt` — portal transitions. +- `tables/skillsarea.txt` — area-skill metadata. +- `tables/msgstringtable.txt` — message string references. +- Profile snapshots included for two major families: + - `tables/kRO/{maps.txt,items.txt,skillnametable.txt}` + - `tables/iRO/{maps.txt,items.txt,skillnametable.txt}` diff --git a/openkore_llm_knowledge/knowledge/xkore_modes.md b/openkore_llm_knowledge/knowledge/xkore_modes.md new file mode 100644 index 0000000000..7665b00559 --- /dev/null +++ b/openkore_llm_knowledge/knowledge/xkore_modes.md @@ -0,0 +1,21 @@ +# XKore Modes + +## XKore mode 1 (`networking/XKore.pm`) +- Runs a local bridge server and communicates between OpenKore and an RO client. +- Maintains client/server packet channels and forwards transformed packets. +- Useful for hybrid play/automation workflows where a game client remains in the loop. + +## XKore mode 2 (`networking/XKore2.pm` + `networking/Network/XKore2/*`) +- Implements proxy-style account/char/map server components. +- Uses dedicated XKore2 server classes: + - `AccountServer.pm` + - `CharServer.pm` + - `MapServer.pm` +- Integrates with hooks to manage packet mangling and in-game synchronization. + +## XKore proxy support (`networking/XKoreProxy.pm`) +- Additional proxy integration layer used in XKore-style setups. + +## Practical difference summary +- **Mode 1**: direct local bridge style, simpler path for client relay. +- **Mode 2**: fuller proxy stack with separate protocol-stage servers and session handling. diff --git a/openkore_llm_knowledge/networking/ClientReceive.pm b/openkore_llm_knowledge/networking/ClientReceive.pm new file mode 100644 index 0000000000..7d2cc01b63 --- /dev/null +++ b/openkore_llm_knowledge/networking/ClientReceive.pm @@ -0,0 +1,216 @@ +## +# MODULE DESCRIPTION: Outgoing client messages handling +# +# This class contains only handler functions +# which are used to handle messages +# that are sent by the RO client, if it's present. + +package Network::ClientReceive; + +use strict; +use Modules 'register'; +use Time::HiRes qw(time); + +use Globals qw($packetParser $incomingMessages %config $char %ai_v %timeout $shopstarted $firstLoginMap $sentWelcomeMessage @lastpm %lastpm); +use Misc qw(configModify visualDump); +use Log qw(message debug warning); +use Translation; +use Utils qw(existsInList); + +use base qw(Network::PacketParser); + +sub new { + my ( $class ) = @_; + my $self = $class->SUPER::new( @_ ); + + $self->{hook_prefix} = 'Network::ClientReceive'; + + return $self; +} + +sub handleChat { + my ($self, $args, $chat) = @_; + + my $prefix = quotemeta $config{commandPrefix}; + if ($chat =~ /^$prefix/) { + $chat =~ s/^$prefix//; + $chat =~ s/^\s*//; + $chat =~ s/\s*$//; + main::parseInput($chat, 1); + $args->{mangle} = 2; + return 1; + } +} + +sub game_login { + $incomingMessages->nextMessageMightBeAccountID; +} + +sub char_login { + my ($self, $args) = @_; + + configModify('char', $args->{slot}); +} + +sub map_login { + my ($self, $args) = @_; + + $incomingMessages->nextMessageMightBeAccountID; + + if ($config{sex} ne '') { + $args->{sex} = $config{sex}; + $args->{mangle} = 1; + } +} + +sub map_loaded { + $packetParser->changeToInGameState; + AI::clear('clientSuspend'); + $timeout{ai}{time} = time; + if ($firstLoginMap) { + undef $sentWelcomeMessage; + undef $firstLoginMap; + } + $timeout{welcomeText}{time} = time; + $ai_v{portalTrace_mapChanged} = time; + message T("Map loaded\n"), 'connection'; + + Plugins::callHook('map_loaded'); +} + +sub actor_action { + my ($self, $args) = @_; + + unless ($config{tankMode} || AI::inQueue('attack')) { + AI::clear('clientSuspend'); + $char->clientSuspend($args->{switch}, 2, $args->{type}, $args->{targetID}); + } else { + $args->{mangle} = 2; + } +} + +sub public_chat { + my ($self, $args) = @_; + + $self->handleChat($args, $args->{message}); +} + +sub private_message { + my ($self, $args) = @_; + + unless ($self->handleChat($args, $args->{privMsg})) { + undef %lastpm; + @lastpm{qw(msg user)} = @{$args}{qw(privMsg privMsgUser)}; + push @lastpm, {%lastpm}; + } +} + +sub actor_look_at { + my ($self, $args) = @_; + + @{$char->{look}}{qw(head body)} = @{$args}{qw(head body)}; +} + +sub item_take { + my ($self, $args) = @_; + + AI::clear('clientSuspend'); + $char->clientSuspend($args->{switch}, 2, $args->{ID}); +} + +sub restart { + my ($self, $args) = @_; + + AI::clear('clientSuspend'); + $char->clientSuspend($args->{switch}, $timeout{'ai_clientSuspend'}{'timeout'}); +} + +sub party_chat { + my ($self, $args) = @_; + + $self->handleChat($args, $args->{message}); +} + +sub alignment { + my ($self, $args) = @_; + + # Chat/skill mute + $args->{mangle} = 2; +} + +sub guild_chat { + my ($self, $args) = @_; + + $self->handleChat($args, $args->{message}); +} + +sub quit_request { + my ($self, $args) = @_; + + AI::clear('clientSuspend'); + $char->clientSuspend($args->{switch}, 10); +} + +sub shop_open { + # client started a shop manually + $shopstarted = 1; +} + +sub shop_close { + # client stopped shop manually + $shopstarted = 0; +} + +=pod +# sendSync + if ($masterServer->{syncID} && $switch eq sprintf('%04X', hex($masterServer->{syncID}))) { + #syncSync support for XKore 1 mode + $syncSync = substr($msg, $masterServer->{syncTickOffset}, 4); + +# sendSync + } elsif ($switch eq "00A7") { + if($masterServer && $masterServer->{paddedPackets}) { + $syncSync = substr($msg, 8, 4); + } + +# sendSync + } elsif ($switch eq "007E") { + if ($masterServer && $masterServer->{paddedPackets}) { + $syncSync = substr($msg, 4, 4); + } + +# sendMapLoaded + } elsif ($switch eq "007D") { + # syncSync support for XKore 1 mode + if($masterServer->{serverType} == 11) { + $syncSync = substr($msg, 8, 4); + } else { + # formula: MapLoaded_len + Sync_len - 4 - Sync_packet_last_junk + $syncSync = substr($msg, $masterServer->{mapLoadedTickOffset}, 4); + } + +# sendMove + } elsif ($switch eq "0085") { + #if ($masterServer->{serverType} == 0 || $masterServer->{serverType} == 1 || $masterServer->{serverType} == 2) { + # #Move + # AI::clear("clientSuspend"); + # makeCoordsDir(\%coords, substr($msg, 2, 3)); + # ai_clientSuspend($switch, (distance($char->{'pos'}, \%coords) * $char->{walk_speed}) + 4); + #} +=cut + +sub unhandledMessage {} + +sub unknownMessage { + my ($self, $args) = @_; + + # Unknown message - ignore it + unless (existsInList($config{debugPacket_exclude}, $args->{switch})) { + debug TF("Packet Tokenizer: Unknown outgoing switch: %s\n", $args->{switch}), 'outgoing'; + visualDump($args->{RAW_MSG}, "<< Outgoing unknown packet") if $config{debugPacket_unparsed}; + } + + # Pass it along to the server, whatever it is +} + +1; diff --git a/openkore_llm_knowledge/networking/DirectConnection.pm b/openkore_llm_knowledge/networking/DirectConnection.pm new file mode 100644 index 0000000000..e298ff4d5d --- /dev/null +++ b/openkore_llm_knowledge/networking/DirectConnection.pm @@ -0,0 +1,662 @@ +######################################################################### +# OpenKore - Networking subsystem +# This module contains functions for sending packets to the server. +# +# This software is open source, licensed under the GNU General Public +# License, version 2. +# Basically, this means that you're allowed to modify and distribute +# this software. However, if you distribute modified versions, you MUST +# also distribute the source code. +# See http://www.gnu.org/licenses/gpl.html for the full license. +# +# $Revision: 7069 $ +# $Id: Network.pm 7069 2010-01-16 02:23:00Z klabmouse $ +# +######################################################################### +## +# MODULE DESCRIPTION: Connection handling +# +# The Network module handles connections to the Ragnarok Online server. +# This module only handles connection issues, and nothing else. It doesn't do +# anything with the actual data. Network data handling is performed by +# the @MODULE(Network::Receive) and Network::Receive::ServerTypeX classes. +# +# The submodule @MODULE(Network::Send) contains functions for sending all +# kinds of messages to the RO server. +# +# Please also read <a href="https://openkore.com/wiki/Network_subsystem">the +# network subsystem overview.</a> +# +# This implementation establishes a direct connection to the RO server. +# Note that there are alternative implementations for this interface: @MODULE(Network::XKore), +# @MODULE(Network::XKore2) and @MODULE(Network::XKoreProxy) + +package Network::DirectConnection; + +use strict; +use Modules 'register'; +use Exporter; +use base qw(Exporter); +use Time::HiRes qw(time); +use IO::Socket::INET; +use utf8; +use Scalar::Util; +use File::Spec; + +use Globals; +use Log qw(message warning error); +use Misc qw(chatLog); +use Network; +use Network::Send (); +use Plugins; +use Settings; +use Interface; +use Utils qw(dataWaiting timeOut); +use Utils::Exceptions; +use Translation; + +## +# Network::DirectConnection->new([wrapper]) +# wrapper: If this object is to be wrapped by another object which is interface-compatible +# with the Network::DirectConnection class, then specify the wrapper object here. The message +# sender will use this wrapper to send socket data. Internally, the reference to the wrapper +# will be stored as a weak reference. +# +# Create a new Network::DirectConnection object. The connection is not yet established. +sub new { + my ($class, $wrapper) = @_; + my %self; + + $self{remote_socket} = new IO::Socket::INET; + if ($wrapper) { + $self{wrapper} = $wrapper; + Scalar::Util::weaken($self{wrapper}); + } + + return bless \%self, $class; +} + +## +# int $net->version() +# +# Returns the implementation number this object. +sub version { + return 0; +} + +sub DESTROY { + my $self = shift; + + $self->serverDisconnect(); +} + + +###################### +## Server Functions ## +###################### + +## +# boolean $net->serverAliveServer() +# +# Check whether the connection to the server is alive. +sub serverAlive { + return $_[0]->{remote_socket} && $_[0]->{remote_socket}->connected(); +} + +## +# String $net->serverPeerHost() +# +# If the connection to the server is alive, returns the host name of the server. +# Otherwise, returns undef. +sub serverPeerHost { + return $_[0]->{remote_socket}->peerhost if ($_[0]->serverAlive); + return undef; +} + +## +# int $net->serverPeerPort() +# +# If the connection to the server is alive, returns the port number of the server. +# Otherwise, returns undef. +sub serverPeerPort { + return $_[0]->{remote_socket}->peerport if ($_[0]->serverAlive); + return undef; +} + +## +# $net->serverConnect(String host, int port) +# host: the host name/IP of the RO server to connect to. +# port: the port number of the RO server to connect to. +# +# Establish a connection to a Ragnarok Online server. +# +# This function is used internally by $net->checkConnection() and should not be used directly. +sub serverConnect { + my $self = shift; + my $host = shift; + my $port = shift; + my $return = 0; + + Plugins::callHook('Network::connectTo', { + socket => \$self->{remote_socket}, + return => \$return, + host => $host, + port => $port + }); + return if ($return); + + message TF("Connecting (%s:%s)... ", $host, $port), "connection"; + $self->{remote_socket} = new IO::Socket::INET( + LocalAddr => $config{bindIp} || undef, + PeerAddr => $host, + PeerPort => $port, + Proto => 'tcp', + Timeout => 4); + ($self->{remote_socket} && inet_aton($self->{remote_socket}->peerhost()) eq inet_aton($host)) ? + message T("connected\n"), "connection" : + error(TF("couldn't connect: %s (error code %d)\n", "$!", int($!)), "connection"); + if ($self->getState() != Network::NOT_CONNECTED) { + $incomingMessages->nextMessageMightBeAccountID(); + } +} + +## +# void $net->serverSend(Bytes data) +# +# If the connection to the server is alive, send data to the server. +# Otherwise, this method does nothing. +sub serverSend { + my $self = shift; + my $msg = shift; + if ($self->serverAlive && defined $self->serverPeerHost, $self->serverPeerPort) { + if (Plugins::hasHook('Network::serverSend/pre')) { + Plugins::callHook('Network::serverSend/pre', {msg => \$msg}); + } + if (defined $msg) { + $self->{remote_socket}->send($msg); + if (Plugins::hasHook('Network::serverSend')) { + Plugins::callHook('Network::serverSend', {msg => $msg}); + } + } + } +} + +## +# Bytes $net->serverRecv() +# +# Receive data from the RO server. +sub serverRecv { + my $self = shift; + my $msg; + + return undef unless (dataWaiting(\$self->{remote_socket})); + + $self->{remote_socket}->recv($msg, 1024 * 32); + if (Plugins::hasHook('Network::serverRecv')) { + Plugins::callHook('Network::serverRecv', {msg => \$msg}); + } + if (!defined($msg) || length($msg) == 0) { + # Connection from server closed. + close($self->{remote_socket}); + return undef; + } + return $msg; +} + +## +# Bytes $net->serverAddress() +# +# Return the server's raw address. +sub serverAddress { + my ($self) = @_; + return $self->{remote_socket}->sockaddr(); +} + +## +# $net->serverDisconnect() +# +# Disconnect from the current Ragnarok Online server. +# +# This function is used internally by $net->checkConnection() and should not be used directly. +sub serverDisconnect { + my $self = shift; + + if ($self->serverAlive) { + if ($incomingMessages && length(my $incoming = $incomingMessages->getBuffer)) { + warning TF("Incoming data left in the buffer:\n"); + Misc::visualDump($incoming); + + if (defined(my $rplen = $incomingMessages->{rpackets}{my $switch = Network::MessageTokenizer::getMessageID($incoming)})) { + my $inlen = do { no encoding 'utf8'; use bytes; length $incoming }; + if (($rplen->{length} > $inlen) || ($rplen->{minLength} > $inlen)) { # check for minLength too, if defined + warning TF("Only %d bytes in the buffer, when %s's packet length is supposed to be %d (wrong recvpackets?)\n", $inlen, $switch, $rplen); + } + } + } + + $messageSender->sendQuit() if ($self->getState() == Network::IN_GAME); + + message TF("Disconnecting (%s:%s)...", $self->{remote_socket}->peerhost(), + $self->{remote_socket}->peerport()), "connection"; + close($self->{remote_socket}); + + if ($self->serverAlive()) { + error T("couldn't disconnect\n"), "connection"; + Plugins::callHook('serverDisconnect/fail'); + } else { + message T("disconnected\n"), "connection"; + Plugins::callHook('serverDisconnect/success'); + } + } +} + +sub getState { + return $conState; +} + +sub setState { + my ($self, $state) = @_; + $conState = $state; + Plugins::callHook('Network::stateChanged'); +} + + +###################### +## Client Functions ## +###################### + +## +# boolean $net->clientAlive() +# +# Check whether there are one or more clients connected to Kore. +sub clientAlive { + my %args = (net => $_[0]); + Plugins::callHook('Network::clientAlive', \%args); + return $args{return}; +} + +## +# $net->clientSend(Bytes data) +# +# Make the RO client think that it has received $data. +sub clientSend { + my ($self) = @_; + if ($self->clientAlive && Plugins::hasHook('Network::clientSend')) { + my %args = (net => $self, data => $_[1]); + Plugins::callHook('Network::clientSend', \%args); + return $args{return}; + } else { + return undef; + } +} + +## +# Bytes $net->clientRecv() +# +# Receive data that the RO client wants to send to the RO server. +sub clientRecv { + my ($self) = @_; + if ($self->clientAlive) { + my %args = (net => $_[0]); + Plugins::callHook('Network::clientRecv', \%args); + return $args{return}; + } else { + return undef; + } +} + + +####################### +## Utility Functions ## +####################### + +####################################### +####################################### +# Check Connection +####################################### +####################################### + +## +# $net->checkConnection() +# +# Handles any connection issues. Based on the current situation, this function may +# re-connect to the RO server, disconnect, do nothing, etc. +# +# This function is meant to be run in the Kore main loop. +sub checkConnection { + my $self = shift; + + return if ($Settings::no_connect); + + my %plugin_args = ( return => 0 ); + Plugins::callHook('checkConnection' => \%plugin_args); + return if ($plugin_args{return}); + + if ($self->getState() == Network::NOT_CONNECTED && (!$self->{remote_socket} || !$self->{remote_socket}->connected) && timeOut($timeout_ex{'master'}) && !$conState_tries) { + my $master = $masterServer = $masterServers{$config{master}}; + + message T("Connecting to Account Server...\n"), "connection"; + $shopstarted = 1; + $conState_tries++; + $initSync = 1; + $incomingMessages->clear(); + + eval { + my $wrapper = ($self->{wrapper}) ? $self->{wrapper} : $self; + $packetParser = Network::Receive->create($wrapper, $masterServer->{serverType}); + $messageSender = Network::Send->create($wrapper, $masterServer->{serverType}); + }; + if ($@) { + $interface->errorDialog("$@"); + $quit = 1; + return; + } + $reconnectCount++; + if (defined $master->{OTP_ip} && defined $master->{OTP_port}) { + $self->serverConnect($master->{OTP_ip}, $master->{OTP_port}); + } else { + $self->serverConnect($master->{ip}, $master->{port}); + } + # call plugin's hook to determine if we can continue the work + if ($self->serverAlive) { + Plugins::callHook('Network::serverConnect/master'); + return if ($conState == 1.5); + } + + # GameGuard support + if ($self->serverAlive && $masterServer->{gameGuard} == 2) { + my $msg = pack("v", 0x0258); + $net->serverSend($msg); + message T("Requesting permission to logon on account server...\n"); + $conState = 1.2; + + # Saving Last Request Time (Logon) (GG/HS Query) + $timeout{poseidon_wait_reply}{time} = time; + + return; + } + + if ($self->serverAlive && $master->{secureLogin} >= 1) { + my $code; + + message T("Secure Login...\n"), "connection"; + undef $secureLoginKey; + + if ($master->{secureLogin_requestCode} ne '') { + $code = $master->{secureLogin_requestCode}; + } + + if ($code ne '') { + $messageSender->sendToServer($messageSender->reconstruct({ + switch => 'client_hash', + code => $code, + })); + } elsif ($master->{secureLogin_type}) { + $messageSender->sendToServer($messageSender->reconstruct({ + switch => 'client_hash', + type => $master->{secureLogin_type}, + })); + } + + $messageSender->sendToServer($messageSender->reconstruct({ + switch => 'secure_login_key_request', + })); + + } elsif ($self->serverAlive) { + $messageSender->sendPreLoginCode($master->{preLoginCode}) if ($master->{preLoginCode}); + $messageSender->sendMasterLogin($config{'username'}, $config{'password'}, + $master->{master_version}, $master->{version}); + } + + $timeout{'master'}{'time'} = time; + } elsif ($self->getState() == 1.2) { + # Checking if we succesful received the Game Guard Confirmation (Should Happen Sooner) + if ( time - $timeout{poseidon_wait_reply}{time} > ($timeout{poseidon_wait_reply}{timeout} || 15) ) + { + message T("The Game Guard Authorization Request\n"); + message T("has timed out, please check your poseidon server !!\n"); + message TF("Address poseidon server: %s\n", $config{'poseidonServer'}); + message TF("Port poseidon: %s\n", $config{'poseidonPort'}); + $self->serverDisconnect; + $self->setState(Network::NOT_CONNECTED); + } + # we skipped some required connection operations while waiting for the server to allow as to login, + # after we have successfully sent in the reply to the game guard challenge (using the poseidon server) + # this conState will allow us to continue from where we left off. + } elsif ($self->getState() == 1.3) { + $conState = 1; + my $master = $masterServer = $masterServers{$config{'master'}}; + + if ($master->{secureLogin} >= 1) { + my $code; + + message T("Secure Login...\n"), "connection"; + undef $secureLoginKey; + + if ($master->{secureLogin_requestCode} ne '') { + $code = $master->{secureLogin_requestCode}; + } + + if ($code ne '') { + $messageSender->sendToServer($messageSender->reconstruct({ + switch => 'client_hash', + code => $code, + })); + } elsif ($master->{secureLogin_type}) { + $messageSender->sendToServer($messageSender->reconstruct({ + switch => 'client_hash', + type => $master->{secureLogin_type}, + })); + } + + $messageSender->sendToServer($messageSender->reconstruct({ + switch => 'secure_login_key_request', + })); + + } else { + $messageSender->sendPreLoginCode($master->{preLoginCode}) if ($master->{preLoginCode}); + $messageSender->sendMasterLogin($config{'username'}, $config{'password'}, + $master->{master_version}, $master->{version}); + } + + $timeout{'master'}{'time'} = time; + + } elsif ($self->getState() == Network::NOT_CONNECTED) { + if($masterServer->{secureLogin} >= 1 && $secureLoginKey ne "" && !timeOut($timeout{'master'}) && $conState_tries) { + my $master = $masterServer; + message T("Sending encoded password...\n"), "connection"; + $messageSender->sendMasterSecureLogin($config{'username'}, $config{'password'}, $secureLoginKey, + $master->{version}, $master->{master_version}, + $master->{secureLogin}, $master->{secureLogin_account}); + undef $secureLoginKey; + + } elsif (timeOut($timeout{'master'}) && timeOut($timeout_ex{'master'})) { + if ($config{dcOnMaxReconnections} && $config{dcOnMaxReconnections} <= $reconnectCount) { + error T("Auto disconnecting on MaxReconnections!\n"); + chatLog("k", T("*** Exceeded the maximum number attempts to reconnect, auto disconnect! ***\n")); + $quit = 1; + return; + } + Plugins::callHook('timeout_accountserver'); + message TF("Timeout on Account Server, reconnecting. Wait %s seconds...\n", $timeout{'reconnect'}{'timeout'}), "connection"; + $timeout_ex{'master'}{'time'} = time; + $timeout_ex{'master'}{'timeout'} = $timeout{'reconnect'}{'timeout'}; + $self->serverDisconnect; + undef $conState_tries; + } + } elsif ($self->getState() == 1.5) { + if (!$self->serverAlive) { + $self->setState(Network::NOT_CONNECTED); + undef $conState_tries; + return; + } + + # on this special stage, the plugin will know what to do next. + Plugins::callHook('Network::serverConnect/special'); + + } elsif ($self->getState() == Network::CONNECTED_TO_MASTER_SERVER) { + if(!$self->serverAlive() && ($config{'server'} ne "" || $masterServer->{charServer_ip}) && !$conState_tries) { + if ($config{pauseCharServer}) { + message "Pausing for $config{pauseCharServer} second(s)...\n", "system"; + sleep $config{pauseCharServer}; + } + my $master = $masterServer; + message T("Connecting to Character Server...\n"), "connection"; + $conState_tries++; + $captcha_state = 0; + + if ($master->{charServer_ip}) { + $self->serverConnect($master->{charServer_ip}, $master->{charServer_port}); + } elsif ($servers[$config{'server'}]) { + message TF("Selected server: %s\n", $servers[$config{server}]->{name}), 'connection'; + $self->serverConnect($servers[$config{'server'}]{'ip'}, $servers[$config{'server'}]{'port'}); + } else { + error TF("Invalid server specified, server %s does not exist...\n", $config{server}), "connection"; + + my @serverList; + foreach my $server (@servers) { + push @serverList, $server->{name}; + } + my $ret = $interface->showMenu( + T("Please select your login server."), + \@serverList, + title => T("Select Login Server")); + if ($ret == -1) { + quit(); + } else { + main::configModify('server', $ret, 1); + undef $conState_tries; + } + return; + } + + # call plugin's hook to determine if we can continue the connection + if ($self->serverAlive) { + Plugins::callHook('Network::serverConnect/char'); + $reconnectCount = 0; + return if ($conState == 1.5); + } + # TODO: the connect code needs a major rewrite =/ + unless($masterServer->{captcha}) { + $messageSender->sendGameLogin($accountID, $sessionID, $sessionID2, $accountSex); + $timeout{'gamelogin'}{'time'} = time; + } + } elsif($self->serverAlive() && $masterServer->{captcha}) { + if ($captcha_state == 0) { # send initiate once, then wait for servers captcha_answer packet + $messageSender->sendCaptchaInitiate(); + $captcha_state = -1; + } elsif ($captcha_state == 1) { # captcha answer was correct, sent sendGameLogin once, then wait for servers + $messageSender->sendGameLogin($accountID, $sessionID, $sessionID2, $accountSex); + $timeout{'gamelogin'}{'time'} = time; + $captcha_state = -1; + } else { + return; + } + } elsif (timeOut($timeout{'gamelogin'}) && ($config{'server'} ne "" || $masterServer->{'charServer_ip'})) { + Plugins::callHook('timeout_characterserver'); + error TF("Timeout on Character Server, reconnecting. Wait %s seconds...\n", $timeout{'reconnect'}{'timeout'}), "connection"; + $timeout_ex{'master'}{'time'} = time; + $timeout_ex{'master'}{'timeout'} = $timeout{'reconnect'}{'timeout'}; + $self->serverDisconnect; + undef $conState_tries; + $self->setState(Network::NOT_CONNECTED); + } + } elsif ($self->getState() == Network::CONNECTED_TO_LOGIN_SERVER) { + if(!$self->serverAlive() && $config{'char'} ne "" && !$conState_tries) { + message T("Connecting to Character Select Server...\n"), "connection"; + $conState_tries++; + $self->serverConnect($servers[$config{'server'}]{'ip'}, $servers[$config{'server'}]{'port'}); + + # call plugin's hook to determine if we can continue the connection + if ($self->serverAlive) { + Plugins::callHook('Network::serverConnect/charselect'); + return if ($conState == 1.5); + } + $messageSender->sendCharLogin($config{'char'}); + $timeout{'charlogin'}{'time'} = time; + + } elsif (timeOut($timeout{'charlogin'}) && $config{'char'} ne "") { + Plugins::callHook('timeout_characterselectserver'); + error T("Timeout on Character Select Server, reconnecting...\n"), "connection"; + $timeout_ex{'master'}{'time'} = time; + $timeout_ex{'master'}{'timeout'} = $timeout{'reconnect'}{'timeout'}; + $self->serverDisconnect; + $self->setState(Network::NOT_CONNECTED); + undef $conState_tries; + } + } elsif ($self->getState() == Network::CONNECTED_TO_CHAR_SERVER) { + if(!$self->serverAlive() && !$conState_tries) { + if ($config{pauseMapServer}) { + return if($config{XKore} eq 1 || $config{XKore} eq 3); + message "Pausing for $config{pauseMapServer} second(s)...\n", "system"; + sleep($config{pauseMapServer}); + } + message T("Connecting to Map Server...\n"), "connection"; + $conState_tries++; + main::initConnectVars(); + my $master = $masterServer; + my ($ip, $port); + if ($master->{private}) { + $ip = $config{forceMapIP} || $master->{ip}; + $port = $map_port; + } else { + $ip = $master->{mapServer_ip} || $config{forceMapIP} || $map_ip; + $port = $master->{mapServer_port} || $map_port; + } + $self->serverConnect($ip, $port); + + # call plugin's hook to determine if we can continue the connection + if ($self->serverAlive) { + Plugins::callHook('Network::serverConnect/mapserver'); + return if ($conState == 1.5); + } + + $messageSender->sendPing() if (grep { $masterServer->{serverType} eq $_ } qw(ROla)); + $messageSender->sendMapLogin($accountID, $charID, $sessionID, $accountSex2); + $timeout_ex{master}{time} = time; + $timeout_ex{master}{timeout} = $timeout{reconnect}{timeout}; + $timeout{maplogin}{time} = time; + + } elsif (timeOut($timeout{maplogin})) { + message T("Timeout on Map Server, connecting to Account Server...\n"), "connection"; + Plugins::callHook('timeout_mapserver'); + $timeout_ex{master}{timeout} = $timeout{reconnect}{timeout}; + $self->serverDisconnect; + $self->setState(Network::NOT_CONNECTED); + undef $conState_tries; + } + } elsif ($self->getState() == Network::IN_GAME) { + if(!$self->serverAlive()) { + Plugins::callHook('disconnected'); + if ($config{dcOnDisconnect}) { + error T("Auto disconnecting on Disconnect!\n"); + chatLog("k", T("*** You disconnected, auto disconnect! ***\n")); + $quit = 1; + } else { + message TF("Disconnected from Map Server, connecting to Account Server in %s seconds...\n", $timeout{reconnect}{timeout}), "connection"; + $timeout_ex{master}{time} = time; + $timeout_ex{master}{timeout} = $timeout{reconnect}{timeout}; + $self->setState(Network::NOT_CONNECTED); + undef $conState_tries; + } + + } elsif (timeOut($timeout{play})) { + error T("Timeout on Map Server, "), "connection"; + Plugins::callHook('disconnected'); + if ($config{dcOnDisconnect}) { + error T("Auto disconnecting on Disconnect!\n"); + chatLog("k", T("*** You disconnected, auto disconnect! ***\n")); + $quit = 1; + } else { + error TF("connecting to Account Server in %s seconds...\n", $timeout{reconnect}{timeout}), "connection"; + $timeout_ex{master}{time} = time; + $timeout_ex{master}{timeout} = $timeout{reconnect}{timeout}; + $self->serverDisconnect; + $self->setState(Network::NOT_CONNECTED); + undef $conState_tries; + } + } + } +} + +return 1; diff --git a/openkore_llm_knowledge/networking/MessageTokenizer.pm b/openkore_llm_knowledge/networking/MessageTokenizer.pm new file mode 100644 index 0000000000..d0b512f6ce --- /dev/null +++ b/openkore_llm_knowledge/networking/MessageTokenizer.pm @@ -0,0 +1,210 @@ +######################################################################### +# OpenKore - Networking subsystem +# This module contains functions for sending packets to the server. +# +# This software is open source, licensed under the GNU General Public +# License, version 2. +# Basically, this means that you're allowed to modify and distribute +# this software. However, if you distribute modified versions, you MUST +# also distribute the source code. +# See http://www.gnu.org/licenses/gpl.html for the full license. +######################################################################### +## +# MODULE DESCRIPTION: Conversion of byte stream to descrete messages. +# +# As explained by the <a href="https://openkore.com/wiki/Network_subsystem"> +# network subsystem overview</a>, the Ragnarok Online protocol uses TCP, which means +# that all server messages are received as a byte stream. +# This class is specialized in extracting discrete RO server or client messages from a byte +# stream. +package Network::MessageTokenizer; + +use strict; +use Carp::Assert; +use Modules 'register'; +use bytes; +no encoding 'utf8'; +use enum qw(KNOWN_MESSAGE UNKNOWN_MESSAGE ACCOUNT_ID); + +## +# Network::MessageTokenizer->new(Hash* rpackets) +# rpackets: A reference to a hash containing the packet length database. +# Required: defined($rpackets) +# +# Create a new Network::MessageTokenizer object. +sub new { + my ($class, $rpackets) = @_; + assert(defined $rpackets, "Can't create new MessageTokenizer with undef packet length database (\$rpackets is undefined)") if DEBUG; + #Log::warning (Data::Dumper::Dumper($rpackets)."\n"); + my %self = ( + + rpackets => $rpackets, + buffer => '' + ); + return bless \%self, $class; +} + +## +# void $Network_MessageTokenizer->add(Bytes data) +# Requires: defined($data) +# +# Add raw data to this tokenizer's buffer. +sub add { + my ($self, $data) = @_; + assert(defined $data, "Can't add undefined data to MessageTokenizer buffer") if DEBUG; + $self->{buffer} .= $data; +} + +## +# void $Network_MessageTokenizer->clear([int size]) +# Requires: size >= 0 +# +# Clear the internal buffer. If $size is given, only the first $size +# bytes are removed. +sub clear { + my ($self, $size) = @_; + if (defined $size) { + substr($_[0]->{buffer}, 0, $size, ''); + } else { + $_[0]->{buffer} = ''; + } +} + +## +# void $Network_MessageTokenizer->nextMessageMightBeAccountID() +# +# Tell this tokenizer that the next message might be the account ID. +sub nextMessageMightBeAccountID { + my ($self) = @_; + $self->{nextMessageMightBeAccountID} = 1; +} + +## +# String Network::MessageTokenizer::getMessageID(Bytes message) +# Requires: length($message) >= 2 +# +# Extract the message ID (also known as the "packet switch") from the given message. +sub getMessageID { + uc join '', unpack '@1H2 @0H2', $_[0] +} + +## +# Bytes $Network->MessageTokenizer->getBuffer() +# Ensures: defined(result) +# +# Get the internal buffer. +sub getBuffer { + return $_[0]->{buffer}; +} + +## +# Bytes $Network_MessageTokenizer->readNext(int* type) +# +# Read the next full message from the buffer, if there is one. +# If not, undef will be returned. +# +# The message's type will be returned via the type parameter. +# It will be one of: +# `l +# - KNOWN_MESSAGE - This is a known message, i.e. we know its length. +# - UNKNOWN_MESSAGE - This is an unknown message, i.e. we don't know its length. +# - ACCOUNT_ID - This is an account ID. +# `l` +sub readNext { + my ($self, $type) = @_; + my $buffer = \$self->{buffer}; + + return undef if (length($$buffer) < 2); + + my $switch = getMessageID($$buffer); + my $rpackets = $self->{rpackets}; + my $size = $rpackets->{$switch}{length}; + + my $result; + + #Log::warning sprintf("Packet %s %d %d \n", $switch, $rpackets->{$switch}{length}, $size); + + my $nextMessageMightBeAccountID = $self->{nextMessageMightBeAccountID}; + $self->{nextMessageMightBeAccountID} = undef; + + if ($nextMessageMightBeAccountID) { + if (length($$buffer) >= 4) { + + $result = substr($$buffer, 0, 4); + if (unpack("V1",$result) == unpack("V1",$Globals::accountID)) { + substr($$buffer, 0, 4, ''); + $$type = ACCOUNT_ID; + } else { + # Account ID is "hidden" in a packet (0283 is one of them) + return $self->readNext($type); + } + + } else { + $self->{nextMessageMightBeAccountID} = $nextMessageMightBeAccountID; + } + + } elsif ($size > 1) { + # Static length message. + if (length($$buffer) >= $size) { + $result = substr($$buffer, 0, $size); + substr($$buffer, 0, $size, ''); + $$type = KNOWN_MESSAGE; + } + + } elsif ( + defined($size) + and $size == 0 # old Kore convention + || $size == -1 # packet extractor v3 + ) { + # Variable length message. + if (length($$buffer) >= 4) { + $size = unpack("v", substr($$buffer, 2, 2)); + if (length($$buffer) >= $size) { + $result = substr($$buffer, 0, $size, ''); + $$type = KNOWN_MESSAGE; + } + } + + } else { + $result = $$buffer; + $self->{buffer} = ''; + $$type = UNKNOWN_MESSAGE; + } + return $result; +} + +# ragnarok servers +sub slicePacket { + my ($self, $data, $additional_data) = @_; + # temporary hack for new recvpackets format + my $switch = getMessageID($data); + my $real_length = $self->{rpackets}{$switch}{length}; + my $packet; + + if (($real_length > 0) # packet size is not variable + && (length($data) >= $real_length)) { + if (length($data) > $real_length) { + $packet = substr($data, 0, $real_length); + $$additional_data = substr($data, $real_length); # sliced data + } else { + $packet = $data; + $$additional_data = undef; + } + } else { # packet is at correct size? + $packet = $data; + $$additional_data = undef; + if (length($data) > 4) { + my $packet_length = unpack("v", substr($data, 2, 2)); + if ($packet_length > 4 && length($data) > $packet_length) { + my $next_data = substr($data, $packet_length); + if (length($next_data) > 1) { + $packet = substr($data, 0, $packet_length); + $$additional_data = $next_data; + } + } + } + } + return $packet; # real packet +} + +1; diff --git a/openkore_llm_knowledge/networking/Network.pm b/openkore_llm_knowledge/networking/Network.pm new file mode 100644 index 0000000000..1d3c03e24c --- /dev/null +++ b/openkore_llm_knowledge/networking/Network.pm @@ -0,0 +1,53 @@ +######################################################################### +# OpenKore - Networking subsystem +# This module contains functions for sending packets to the server. +# +# This software is open source, licensed under the GNU General Public +# License, version 2. +# Basically, this means that you're allowed to modify and distribute +# this software. However, if you distribute modified versions, you MUST +# also distribute the source code. +# See http://www.gnu.org/licenses/gpl.html for the full license. +# +# $Revision$ +# $Id$ +# +######################################################################### +package Network; + +use strict; +use Modules 'register'; + +# $conState contains the connection state: +# 1: Not connected to anything (next step -> connect to master server). +# 2: Connected to master server (next step -> connect to login server) +# 3: Connected to login server (next step -> connect to character server) +# 4: Connected to character server (next step -> connect to map server) +# 5: Connected to map server; ready and functional. +# +# Special states: +# 1.2 (set by $masterServer->{gameGuard} == 2): Wait for the server response allowing us +# to continue login +# 1.3 (set by parseMsg()): The server allowed us to continue logging in, continue +# where we left off +# 1.5 (set by plugins): There is a special sequence for login servers and we must +# wait the plugins to finalize before continuing +# 2.5 (set by parseMsg()): Just passed character selection; next 4 bytes will be +# the account ID + +use constant { + NOT_CONNECTED => 1, + CONNECTED_TO_MASTER_SERVER => 2, + CONNECTED_TO_LOGIN_SERVER => 3, + CONNECTED_TO_CHAR_SERVER => 4, + IN_GAME => 5, + + # This can only happen if we're in XKore or XKoreProxy mode. + # It means that the RO client is already logged in the game + # before OpenKore did. Because of this, OpenKore does not have + # enough information (such as the character's name) to be able to + # work properly. + IN_GAME_BUT_UNINITIALIZED => -1 +}; + +1; diff --git a/openkore_llm_knowledge/networking/Network/Receive/ServerType0.pm b/openkore_llm_knowledge/networking/Network/Receive/ServerType0.pm new file mode 100644 index 0000000000..f4bf845c72 --- /dev/null +++ b/openkore_llm_knowledge/networking/Network/Receive/ServerType0.pm @@ -0,0 +1,1613 @@ +######################################################################### +# OpenKore - Network subsystem +# Copyright (c) 2006 OpenKore Team +# +# This software is open source, licensed under the GNU General Public +# License, version 2. +# Basically, this means that you're allowed to modify and distribute +# this software. However, if you distribute modified versions, you MUST +# also distribute the source code. +# See http://www.gnu.org/licenses/gpl.html for the full license. +######################################################################### +# Servertype overview: https://openkore.com/wiki/ServerType +package Network::Receive::ServerType0; + +use strict; +use Network::Receive qw(:actor_type :connection :stat_info :party_invite :party_leave :exp_origin); +use base qw(Network::Receive); +use Time::HiRes qw(time usleep); + +use AI; +use Log qw(message warning error debug); + +# from old receive.pm +use Task::Wait; +use Task::Function; +use Task::Chained; +use utf8; +use Carp::Assert; +use Scalar::Util; +use Exception::Class ('Network::Receive::InvalidServerType', 'Network::Receive::CreationError'); + +use Globals; +use Actor; +use Actor::You; +use Actor::Player; +use Actor::Monster; +use Actor::Party; +use Actor::Item; +use Actor::Unknown; +use Field; +use Settings; +use FileParsers; +use Interface; +use Misc; +use Network; +use Network::MessageTokenizer; +use Network::Send (); +use Plugins; +use Utils; +use Skill; +use Utils::Assert; +use Utils::Exceptions; +use Utils::Crypton; +use Translation; +use I18N qw(bytesToString stringToBytes); +# from old receive.pm + +sub new { + my ($class) = @_; + my $self = $class->SUPER::new(); + + $self->{packet_list} = { + '0069' => ['account_server_info', 'v a4 a4 a4 a4 a26 C a*', [qw(len sessionID accountID sessionID2 lastLoginIP lastLoginTime accountSex serverInfo)]], + '006A' => ['login_error', 'C Z20', [qw(type date)]], + # '006B' => ['received_characters_info', 'v x20 a*', [qw(len charInfo)]], # not used in official server + '006B' => ['received_characters_info', 'v C3 x20 a*', [qw(len total_slot premium_start_slot premium_end_slot charInfo)]], # last known struct + '006C' => ['login_error_game_login_server'], + '006D' => ['character_creation_successful', 'a*', [qw(charInfo)]], + '006E' => ['character_creation_failed', 'C' ,[qw(type)]], + '006F' => ['character_deletion_successful'], + '0070' => ['character_deletion_failed'], + '0071' => ['received_character_ID_and_Map', 'a4 Z16 a4 v', [qw(charID mapName mapIP mapPort)]], + '0072' => ['received_characters', 'v a*', [qw(len charInfo)]], # last known struct + '0073' => ['map_loaded', 'V a3 C2', [qw(syncMapSync coords xSize ySize)]], # 11 + '0074' => ['map_load_error', 'C', [qw(error)]], # 3 + '0075' => ['changeToInGameState'], + '0077' => ['changeToInGameState'], + # OLD '0078' => ['actor_exists', 'a4 v14 a4 x7 C a3 x2 C v', [qw(ID walk_speed opt1 opt2 option type hair_style weapon lowhead shield tophead midhead hair_color clothes_color head_dir guildID sex coords act lv)]], + '0078' => ['actor_exists', 'a4 v14 a4 a2 v2 C2 a3 C3 v', [qw(ID walk_speed opt1 opt2 option type hair_style weapon lowhead shield tophead midhead hair_color clothes_color head_dir guildID emblemID manner opt3 stance sex coords unknown1 unknown2 act lv)]], #standing + # OLD'0079' => ['actor_connected', 'a4 v14 a4 x7 C a3 x2 v', [qw(ID walk_speed opt1 opt2 option type hair_style weapon lowhead shield tophead midhead hair_color clothes_color head_dir guildID sex coords lv)]], + '0079' => ['actor_connected', 'a4 v14 a4 a2 v2 C2 a3 C2 v', [qw(ID walk_speed opt1 opt2 option type hair_style weapon lowhead shield tophead midhead hair_color clothes_color head_dir guildID emblemID manner opt3 stance sex coords unknown1 unknown2 lv)]], #spawning + '007A' => ['changeToInGameState'], + # OLD '007B' => ['actor_moved', 'a4 v8 x4 v6 a4 x7 C a5 x3 v', [qw(ID walk_speed opt1 opt2 option type hair_style weapon lowhead shield tophead midhead hair_color clothes_color head_dir guildID sex coords lv)]], #walking + '007B' => ['actor_moved', 'a4 v8 V v6 a4 a2 v2 C2 a6 C2 v', [qw(ID walk_speed opt1 opt2 option type hair_style weapon lowhead tick shield tophead midhead hair_color clothes_color head_dir guildID emblemID manner opt3 stance sex coords unknown1 unknown2 lv)]], #walking + #VERY OLD '007C' => ['actor_exists', 'a4 v1 v1 v1 v1 x6 v1 C1 x12 C1 a3', [qw(ID walk_speed opt1 opt2 option type pet sex coords)]], + #OLD '007C' => ($rpackets{'007C'}{length} == 41 # or 42 + #OLD ? ['actor_exists', 'x a4 v14 C2 a3 C', [qw(ID walk_speed opt1 opt2 option hair_style weapon lowhead type shield tophead midhead hair_color clothes_color head_dir stance sex coords unknown1)]] + #OLD : ['actor_exists', 'x a4 v14 C2 a3 C2', [qw(ID walk_speed opt1 opt2 option hair_style weapon lowhead type shield tophead midhead hair_color clothes_color head_dir stance sex coords unknown1 unknown2)]] + #OLD), + '007C' => ['actor_spawned', 'a4 v14 C2 a3 C2', [qw(ID walk_speed opt1 opt2 option hair_style weapon lowhead type shield tophead midhead hair_color clothes_color head_dir stance sex coords unknown1 unknown2)]], #spawning: eA does not send this for players + '007F' => ['received_sync', 'V', [qw(time)]], + '0080' => ['actor_died_or_disappeared', 'a4 C', [qw(ID type)]], + '0081' => ['errors', 'C', [qw(type)]], + '0086' => ['actor_display', 'a4 a6 V', [qw(ID coords tick)]], + '0087' => ['character_moves', 'a4 a6', [qw(move_start_time coords)]], # 12 + '0088' => ['actor_movement_interrupted', 'a4 v2', [qw(ID x y)]], + '008A' => ['actor_action', 'a4 a4 a4 V2 v2 C v', [qw(sourceID targetID tick src_speed dst_speed damage div type dual_wield_damage)]], + '008D' => ['public_chat', 'v a4 Z*', [qw(len ID message)]], + '008E' => ['self_chat', 'v Z*', [qw(len message)]], + '0091' => ['map_change', 'Z16 v2', [qw(map x y)]], + '0092' => ['map_changed', 'Z16 v2 a4 v', [qw(map x y IP port)]], # 28 + '0095' => ['actor_info', 'a4 Z24', [qw(ID name)]], + '0097' => ['private_message', 'v Z24 Z*', [qw(len privMsgUser privMsg)]], + '0098' => ['private_message_sent', 'C', [qw(type)]], + '009A' => ['system_chat', 'v a*', [qw(len message)]], + '009C' => ['actor_look_at', 'a4 v C', [qw(ID head body)]], + '009D' => ['item_exists', 'a4 v C v3 C2', [qw(ID nameID identified x y amount subx suby)]], + '009E' => ['item_appeared', 'a4 v C v2 C2 v', [qw(ID nameID identified x y subx suby amount)]], + '00A0' => ['inventory_item_added', 'a2 v2 C3 a8 v C2', [qw(ID amount nameID identified broken upgrade cards type_equip type fail)]], + '00A1' => ['item_disappeared', 'a4', [qw(ID)]], + '00A3' => ['inventory_items_stackable', 'v a*', [qw(len itemInfo)]], + '00A4' => ['inventory_items_nonstackable', 'v a*', [qw(len itemInfo)]], + '00A5' => ['storage_items_stackable', 'v a*', [qw(len itemInfo)]], + '00A6' => ['storage_items_nonstackable', 'v a*', [qw(len itemInfo)]], + '00A8' => ['use_item', 'a2 v C', [qw(ID amount success)]], # 7 + '00AA' => ($rpackets{'00AA'}{length} == 7) # or 9 + ? ['equip_item', 'a2 v C', [qw(ID type success)]] + : ['equip_item', 'a2 v2 C', [qw(ID type viewid success)]], + '00AC' => ['unequip_item', 'a2 v C', [qw(ID type success)]], + '00AF' => ['inventory_item_removed', 'a2 v', [qw(ID amount)]], + '00B0' => ['stat_info', 'v V', [qw(type val)]], + '00B1' => ['stat_info', 'v V', [qw(type val)]], # was "exp_zeny_info" + '00B3' => ['switch_character', 'C', [qw(result)]], # 3 + '00B4' => ['npc_talk', 'v a4 Z*', [qw(len ID msg)]], + '00B5' => ['npc_talk_continue', 'a4', [qw(ID)]], + '00B6' => ['npc_talk_close', 'a4', [qw(ID)]], + '00B7' => ['npc_talk_responses'], + '00BC' => ['stats_added', 'v x C', [qw(type val)]], # actually 'v C2', 'type result val' + '00BD' => ['stats_info', 'v C12 v14', [qw(points_free str points_str agi points_agi vit points_vit int points_int dex points_dex luk points_luk attack attack_bonus attack_magic_min attack_magic_max def def_bonus def_magic def_magic_bonus hit flee flee_bonus critical stance manner)]], # (stance manner) actually are (ASPD plusASPD) + '00BE' => ['stat_info', 'v C', [qw(type val)]], # was "stats_points_needed" + '00C0' => ['emoticon', 'a4 C', [qw(ID type)]], + '00C2' => ['users_online', 'V', [qw(users)]], + '00C3' => ['sprite_change', 'a4 C2', [qw(ID type value1)]], # 8 + '00C4' => ['npc_store_begin', 'a4', [qw(ID)]], + '00C6' => ['npc_store_info', 'v a*', [qw(len itemList)]],#-1 + '00C7' => ['npc_sell_list', 'v a*', [qw(len itemsdata)]], + '00CA' => ['buy_result', 'C', [qw(fail)]], + '00CB' => ['sell_result', 'C', [qw(fail)]], # 3 + '00D1' => ['ignore_player_result', 'C2', [qw(type error)]], + '00D2' => ['ignore_all_result', 'C2', [qw(type error)]], + '00D4' => ['whisper_list'], + '00D6' => ['chat_created', 'C', [qw(result)]], # 3 + '00D7' => ['chat_info', 'v a4 a4 v2 C a*', [qw(len ownerID ID limit num_users public title)]], + '00D8' => ['chat_removed', 'a4', [qw(ID)]], + '00DA' => ['chat_join_result', 'C', [qw(type)]], + '00DB' => ['chat_users'], + '00DC' => ['chat_user_join', 'v Z24', [qw(num_users user)]], + '00DD' => ['chat_user_leave', 'v Z24 C', [qw(num_users user flag)]], + '00DF' => ['chat_modified', 'v a4 a4 v2 C a*', [qw(len ownerID ID limit num_users public title)]], # -1 + '00E1' => ['chat_newowner', 'C x3 Z24', [qw(type user)]], + '00E5' => ['deal_request', 'Z24', [qw(user)]], + '00E7' => ['deal_begin', 'C', [qw(type)]], + '00E9' => ['deal_add_other', 'V v C3 a8', [qw(amount nameID identified broken upgrade cards)]], + '00EA' => ['deal_add_you', 'a2 C', [qw(ID fail)]], + '00EC' => ['deal_finalize', 'C', [qw(type)]], + '00EE' => ['deal_cancelled'], + '00F0' => ['deal_complete'], + '00F2' => ['storage_opened', 'v2', [qw(items items_max)]], + '00F4' => ['storage_item_added', 'a2 V v C3 a8', [qw(ID amount nameID identified broken upgrade cards)]], + '00F6' => ['storage_item_removed', 'a2 V', [qw(ID amount)]], + '00F8' => ['storage_closed'], + '00FA' => ['party_organize_result', 'C', [qw(fail)]], + '00FB' => ['party_users_info', 'v Z24 a*', [qw(len party_name playerInfo)]], + '00FD' => ['party_invite_result', 'Z24 C', [qw(name type)]], + '00FE' => ['party_invite', 'a4 Z24', [qw(ID name)]], + '0101' => ['party_exp', 'V', [qw(type)]], + '0104' => ['party_join', 'a4 V v2 C Z24 Z24 Z16', [qw(ID role x y type name user map)]], + '0105' => ['party_leave', 'a4 Z24 C', [qw(ID name result)]], + '0106' => ['party_hp_info', 'a4 v2', [qw(ID hp hp_max)]], + '0107' => ['party_location', 'a4 v2', [qw(ID x y)]], + '0108' => ['item_upgrade', 'v a2 v', [qw(type ID upgrade)]], + '0109' => ['party_chat', 'v a4 Z*', [qw(len ID message)]], + '010A' => ['mvp_item', 'v', [qw(itemID)]], + '010B' => ['mvp_you', 'V', [qw(expAmount)]], + '010C' => ['mvp_other', 'a4', [qw(ID)]], + '010E' => ['skill_update', 'v4 C', [qw(skillID lv sp range up)]], # range = skill range, up = this skill can be leveled up further + '010F' => ['skills_list'], + '0110' => ($rpackets{'0110'}{length} == 14) # or 10 + ? ['skill_use_failed', 'v V2 C2', [qw(skillID btype itemId flag cause)]] + : ['skill_use_failed', 'v v2 C2', [qw(skillID btype itemId flag cause)]] + , + '0111' => ['skill_add', 'v V v3 Z24 C', [qw(skillID target lv sp range name upgradable)]], + '0114' => ['skill_use', 'v a4 a4 V3 v3 C', [qw(skillID sourceID targetID tick src_speed dst_speed damage level option type)]], + '0117' => ['skill_use_location', 'v a4 v3 V', [qw(skillID sourceID lv x y tick)]], + '0119' => ['character_status', 'a4 v3 C', [qw(ID opt1 opt2 option stance)]], + '011A' => ['skill_used_no_damage', 'v2 a4 a4 C', [qw(skillID amount targetID sourceID success)]], + '011C' => ['warp_portal_list', 'v Z16 Z16 Z16 Z16', [qw(type memo1 memo2 memo3 memo4)]], + '011E' => ['memo_success', 'C', [qw(fail)]], + '011F' => ['area_spell', 'a4 a4 v2 C2', [qw(ID sourceID x y type isVisible)]], + '0120' => ['area_spell_disappears', 'a4', [qw(ID)]], + '0121' => ['cart_info', 'v2 V2', [qw(items items_max weight weight_max)]], + '0122' => ['cart_items_nonstackable', 'v a*', [qw(len itemInfo)]], + '0123' => ['cart_items_stackable', 'v a*', [qw(len itemInfo)]], + '0124' => ['cart_item_added', 'a2 V v C3 a8', [qw(ID amount nameID identified broken upgrade cards)]], + '0125' => ['cart_item_removed', 'a2 V', [qw(ID amount)]], + '012B' => ['cart_off'], + '012C' => ['cart_add_failed', 'C', [qw(fail)]], + '012D' => ['shop_skill', 'v', [qw(number)]], + '0131' => ['vender_found', 'a4 Z80', [qw(ID title)]], + '0132' => ['vender_lost', 'a4', [qw(ID)]], + '0133' => ['vender_items_list', 'v a4 a*', [qw(len venderID itemList)]], + '0135' => ['vender_buy_fail', 'v2 C', [qw(ID amount fail)]], + '0136' => ['vending_start', 'v a4 a*', [qw(len accountID itemList)]], # -1 + '0137' => ['shop_sold', 'v2', [qw(number amount)]], + '0139' => ['monster_ranged_attack', 'a4 v5', [qw(ID sourceX sourceY targetX targetY range)]], + '013A' => ['attack_range', 'v', [qw(type)]], + '013B' => ['arrow_none', 'v', [qw(type)]], + '013C' => ['arrow_equipped', 'a2', [qw(ID)]], + '013D' => ['hp_sp_changed', 'v2', [qw(type amount)]], + '013E' => ['skill_cast', 'a4 a4 v5 V', [qw(sourceID targetID x y skillID unknown type wait)]], + '0141' => ['stat_info2', 'V2 l', [qw(type val val2)]], + '0142' => ['npc_talk_number', 'a4', [qw(ID)]], + '0144' => ['minimap_indicator', 'a4 V3 C5', [qw(npcID type x y ID blue green red alpha)]], + '0145' => ['npc_image', 'Z16 C', [qw(npc_image type)]], + '0147' => ['item_skill', 'v6 A*', [qw(skillID targetType unknown skillLv sp unknown2 skillName)]], + '0148' => ['resurrection', 'a4 v', [qw(targetID type)]], + '014A' => ['manner_message', 'V', [qw(type)]], + '014B' => ['GM_silence', 'C Z24', [qw(type name)]], + '014C' => ['guild_allies_enemy_list'], + '014E' => ['guild_master_member', 'V', [qw(type)]], + '0150' => ['guild_info', 'a4 V9 a4 Z24 Z24 Z16 V', [qw(ID lv conMember maxMember average exp exp_next tax tendency_left_right tendency_down_up emblemID name master castles_string zeny)]], + '0152' => ['guild_emblem', 'v a4 a4 a*', [qw(len guildID emblemID emblem)]], + '0154' => ['guild_members_list', 'v a*', [qw(len member_list)]], + '0156' => ['guild_update_member_position', 'v a*', [qw(len member_list)]], + '015A' => ['guild_leave', 'Z24 Z40', [qw(name message)]], + '015C' => ['guild_expulsion', 'Z24 Z40 Z24', [qw(name message accountName)]], + '015E' => ['guild_broken', 'V', [qw(flag)]], # clif_guild_broken + '0160' => ['guild_member_setting_list'], + '0162' => ['guild_skills_list'], + '0163' => ['guild_expulsion_list', 'v a*', [qw(len expulsion_list)]], #-1 + '0166' => ['guild_members_title_list'], + '0167' => ['guild_create_result', 'C', [qw(type)]], + '0169' => ['guild_invite_result', 'C', [qw(type)]], + '016A' => ['guild_request', 'a4 Z24', [qw(ID name)]], + '016C' => ['guild_name', 'a4 a4 V C a4 Z24', [qw(guildID emblemID mode is_master interSID guildName)]], # 43 + '016D' => ['guild_member_online_status', 'a4 a4 V', [qw(ID charID online)]], + '016F' => ['guild_notice', 'Z60 Z120', [qw(subject notice)]], # 182 + '0171' => ['guild_ally_request', 'a4 Z24', [qw(ID guildName)]], + '0173' => ['guild_alliance', 'C', [qw(flag)]], + '0174' => ['guild_position_changed', 'v a4 a4 a4 V Z20', [qw(len ID mode sameID exp position_name)]], # -1 + '0177' => ['identify_list'], + '0179' => ['identify', 'a2 C', [qw(ID flag)]], + '017B' => ['card_merge_list'], + '017D' => ['card_merge_status', 'a2 a2 C', [qw(item_index card_index fail)]], + '017F' => ['guild_chat', 'v Z*', [qw(len message)]], # -1 + '0181' => ['guild_opposition_result', 'C', [qw(flag)]], # clif_guild_oppositionack + '0182' => ['guild_member_add', 'a4 a4 v5 V3 Z50 Z24', [qw(ID charID hair_style hair_color sex jobID lv contribution online position memo name)]], # 106 + '0184' => ['guild_unally', 'a4 V', [qw(guildID flag)]], # clif_guild_delalliance + '0185' => ['guild_alliance_added', 'a4 a4 Z24', [qw(opposition alliance_guildID name)]], # clif_guild_allianceadded + '0187' => ['sync_request', 'a4', [qw(ID)]], + '0188' => ['item_upgrade', 'v a2 v', [qw(type ID upgrade)]], + '0189' => ['no_teleport', 'v', [qw(fail)]], + '018B' => ['quit_response', 'v', [qw(fail)]], # 4 + '018C' => ['sense_result', 'v3 V v4 C9', [qw(nameID level size hp def race mdef element ice earth fire wind poison holy dark spirit undead)]], + '018D' => ['makable_item_list', 'v a*', [qw(len item_list)]], + '018F' => ['refine_result', 'v2', [qw(fail nameID)]], + '0191' => ['talkie_box', 'a4 Z80', [qw(ID message)]], # talkie box message + '0192' => ['map_change_cell', 'v3 Z16', [qw(x y type map_name)]], # ex. due to ice wall + '0194' => ['character_name', 'a4 Z24', [qw(ID name)]], + '0195' => ['actor_info', 'a4 Z24 Z24 Z24 Z24', [qw(ID name partyName guildName guildTitle)]], + '0196' => ['actor_status_active', 'v a4 C', [qw(type ID flag)]], + '0199' => ['map_property', 'v', [qw(type)]], + '019A' => ['pvp_rank', 'V3', [qw(ID rank num)]], + '019B' => ['unit_levelup', 'a4 V', [qw(ID type)]], + '019E' => ['pet_capture_process'], + '01A0' => ['pet_capture_result', 'C', [qw(success)]], + #'01A2' => ($rpackets{'01A2'}{length} == 35 # or 37 + # ? ['pet_info', 'Z24 C v4', [qw(name renameflag level hungry friendly accessory)]] + # : ['pet_info', 'Z24 C v5', [qw(name renameflag level hungry friendly accessory type)]] + #), + '01A2' => ['pet_info', 'Z24 C v5', [qw(name renameflag level hungry friendly accessory type)]], + '01A3' => ['pet_food', 'C v', [qw(success foodID)]], + '01A4' => ['pet_info2', 'C a4 V', [qw(type ID value)]], + '01A6' => ['egg_list'], + '01AA' => ['pet_emotion', 'a4 V', [qw(ID type)]], + '01AB' => ['stat_info', 'a4 v V', [qw(ID type val)]], # was "actor_muted"; is struct/handler correct at all? + '01AC' => ['actor_trapped', 'a4', [qw(ID)]], + '01AD' => ['arrowcraft_list'], + '01B0' => ['monster_typechange', 'a4 a V', [qw(ID unknown type)]], + '01B3' => ['npc_image', 'Z64 C', [qw(npc_image type)]], + '01B4' => ['guild_emblem_update', 'a4 a4 a2', [qw(ID guildID emblemID)]], + '01B5' => ['account_payment_info', 'V2', [qw(D_minute H_minute)]], + '01B6' => ['guild_info', 'a4 V9 a4 Z24 Z24 Z16 V', [qw(ID lv conMember maxMember average exp exp_next tax tendency_left_right tendency_down_up emblemID name master castles_string zeny)]], + '01B9' => ['cast_cancelled', 'a4', [qw(ID)]], + '01C1' => ['remain_time_info' , 'a4 a4 a4', [qw(result expiration_date remain_time)]], + '01C3' => ['local_broadcast', 'v V v4 Z*', [qw(len color font_type font_size font_align font_y message)]], + '01C4' => ['storage_item_added', 'a2 V v C4 a8', [qw(ID amount nameID type identified broken upgrade cards)]], + '01C5' => ['cart_item_added', 'a2 V v C4 a8', [qw(ID amount nameID type identified broken upgrade cards)]], + '01C8' => ['item_used', 'a2 v a4 v C', [qw(ID itemID actorID remaining success)]], + '01C9' => ['area_spell', 'a4 a4 v2 C2 C Z80', [qw(ID sourceID x y type isVisible scribbleLen scribbleMsg)]], + '01CD' => ['sage_autospell', 'a*', [qw(autospell_list)]], + '01CF' => ['devotion', 'a4 a20 v', [qw(sourceID targetIDs range)]], + '01D0' => ['revolving_entity', 'a4 v', [qw(sourceID entity)]], + '01D1' => ['blade_stop', 'a4 a4 V', [qw(sourceID targetID active)]], + '01D2' => ['combo_delay', 'a4 V', [qw(ID delay)]], + '01D3' => ['sound_effect', 'Z24 C V a4', [qw(name type term ID)]], + '01D4' => ['npc_talk_text', 'a4', [qw(ID)]], + '01D6' => ['map_property2', 'v', [qw(type)]], + '01D7' => ($rpackets{'01D7'}{length} == 15) # or 11 + ? ['sprite_change', 'a4 C V2', [qw(ID type value1 value2)]] # 15 + : ['sprite_change', 'a4 C v2', [qw(ID type value1 value2)]] # 11 + , + # OLD' 01D8' => ['actor_exists', 'a4 v14 a4 x4 v x C a3 x2 C v', [qw(ID walk_speed opt1 opt2 option type hair_style weapon shield lowhead tophead midhead hair_color clothes_color head_dir guildID skillstatus sex coords act lv)]], + '01D8' => ['actor_exists', 'a4 v14 a4 a2 v2 C2 a3 C3 v', [qw(ID walk_speed opt1 opt2 option type hair_style weapon shield lowhead tophead midhead hair_color clothes_color head_dir guildID emblemID manner opt3 stance sex coords unknown1 unknown2 act lv)]], # standing + # OLD '01D9' => ['actor_connected', 'a4 v14 a4 x4 v x C a3 x2 v', [qw(ID walk_speed opt1 opt2 option type hair_style weapon shield lowhead tophead midhead hair_color clothes_color head_dir guildID skillstatus sex coords lv)]], + '01D9' => ['actor_connected', 'a4 v14 a4 a2 v2 C2 a3 C2 v', [qw(ID walk_speed opt1 opt2 option type hair_style weapon shield lowhead tophead midhead hair_color clothes_color head_dir guildID emblemID manner opt3 stance sex coords unknown1 unknown2 lv)]], # spawning + # OLD '01DA' => ['actor_moved', 'a4 v5 C x v3 x4 v5 a4 x4 v x C a5 x3 v', [qw(ID walk_speed opt1 opt2 option type hair_style weapon shield lowhead tophead midhead hair_color clothes_color head_dir guildID skillstatus sex coords lv)]], + '01DA' => ['actor_moved', 'a4 v9 V v5 a4 a2 v2 C2 a6 C2 v', [qw(ID walk_speed opt1 opt2 option type hair_style weapon shield lowhead tick tophead midhead hair_color clothes_color head_dir guildID emblemID manner opt3 stance sex coords unknown1 unknown2 lv)]], # walking + '01DC' => ['secure_login_key', 'x2 a*', [qw(secure_key)]], + '01DE' => ['skill_use', 'v a4 a4 V4 v2 C', [qw(skillID sourceID targetID tick src_speed dst_speed damage level option type)]], + '01E0' => ['GM_req_acc_name', 'a4 Z24', [qw(targetID accountName)]], + '01E1' => ['revolving_entity', 'a4 v', [qw(sourceID entity)]], + #'01E2' => ['marriage_unknown'], clif_parse_ReqMarriage + #'01E4' => ['marriage_unknown'], clif_marriage_process + ## + '01E6' => ['marriage_partner_name', 'Z24', [qw(name)]], + '01E9' => ['party_join', 'a4 V v2 C Z24 Z24 Z16 v C2', [qw(ID role x y type name user map lv item_pickup item_share)]], + '01EA' => ['married', 'a4', [qw(ID)]], + '01EB' => ['guild_location', 'a4 v2', [qw(ID x y)]], + '01EC' => ['guild_member_map_change', 'a4 a4 Z16', [qw(charID ID mapName)]], # 26 + '01EE' => ['inventory_items_stackable', 'v a*', [qw(len itemInfo)]], + '01EF' => ['cart_items_stackable', 'v a*', [qw(len itemInfo)]], + '01F0' => ['storage_items_stackable', 'v a*', [qw(len itemInfo)]], + '01F2' => ['guild_member_online_status', 'a4 a4 V v3', [qw(ID charID online sex hair_style hair_color)]], + '01F3' => ['misc_effect', 'a4 V', [qw(ID effect)]], # weather/misceffect2 packet + '01F4' => ['deal_request', 'Z24 a4 v', [qw(user ID level)]], + '01F5' => ['deal_begin', 'C a4 v', [qw(type targetID level)]], + '01F6' => ['adopt_request', 'a4 a4 Z24', [qw(sourceID targetID name)]], + #'01F8' => ['adopt_unknown'], # clif_adopt_process + '01FC' => ['repair_list'], + '01FE' => ['repair_result', 'v C', [qw(index flag)]], # 5 + '01FF' => ['high_jump', 'a4 v2', [qw(ID x y)]], + '0201' => ['friend_list'], + '0205' => ['divorced', 'Z24', [qw(name)]], # clif_divorced + '0206' => ['friend_logon', 'a4 a4 C', [qw(friendAccountID friendCharID isNotOnline)]], + '0207' => ['friend_request', 'a4 a4 Z24', [qw(accountID charID name)]], + '0209' => ['friend_response', 'v a4 a4 Z24', [qw(type accountID charID name)]], + '020A' => ['friend_removed', 'a4 a4', [qw(friendAccountID friendCharID)]], + '020D' => ['character_ban_list', 'v a*', [qw(len charList)]], # -1 charList[charName size:24] + '020E' => ['taekwon_packets', 'Z24 a4 C2', [qw(name ID value flag)]], + '020F' => ['pvp_point', 'V2', [qw(AID GID)]], #TODO: PACKET_CZ_REQ_PVPPOINT + '0215' => ['gospel_buff_aligned', 'a4', [qw(ID)]], + '0216' => ['adopt_reply', 'V', [qw(type)]], + '0219' => ['top10_blacksmith_rank'], + '021A' => ['top10_alchemist_rank'], + '021B' => ['blacksmith_points', 'V2', [qw(points total)]], + '021C' => ['alchemist_point', 'V2', [qw(points total)]], + '0221' => ['upgrade_list', 'v a*', [qw(len item_list)]], + '0223' => ['upgrade_message', 'V v', [qw(type itemID)]], + '0224' => ['taekwon_rank', 'V2', [qw(type rank)]], + '0226' => ['top10_taekwon_rank'], + '0227' => ['gameguard_request'], + '0229' => ['character_status', 'a4 v2 V C', [qw(ID opt1 opt2 option stance)]], + # OLD '022A' => ['actor_exists', 'a4 v4 x2 v8 x2 v a4 a4 v x2 C2 a3 x2 C v', [qw(ID walk_speed opt1 opt2 option type hair_style weapon shield lowhead tophead midhead hair_color head_dir guildID emblemID visual_effects stance sex coords act lv)]], + '022A' => ['actor_exists', 'a4 v3 V v10 a4 a2 v V C2 a3 C3 v', [qw(ID walk_speed opt1 opt2 option type hair_style weapon shield lowhead tophead midhead hair_color clothes_color head_dir guildID emblemID manner opt3 stance sex coords unknown1 unknown2 act lv)]], # standing + # OLD '022B' => ['actor_connected', 'a4 v4 x2 v8 x2 v a4 a4 v x2 C2 a3 x2 v', [qw(ID walk_speed opt1 opt2 option type hair_style weapon shield lowhead tophead midhead hair_color head_dir guildID emblemID visual_effects stance sex coords lv)]], + '022B' => ['actor_connected', 'a4 v3 V v10 a4 a2 v V C2 a3 C2 v', [qw(ID walk_speed opt1 opt2 option type hair_style weapon shield lowhead tophead midhead hair_color clothes_color head_dir guildID emblemID manner opt3 stance sex coords unknown1 unknown2 lv)]], # spawning + # OLD '022C' => ['actor_moved', 'a4 v4 x2 v5 V v3 x4 a4 a4 v x2 C2 a5 x3 v', [qw(ID walk_speed opt1 opt2 option type hair_style weapon shield lowhead timestamp tophead midhead hair_color guildID emblemID visual_effects stance sex coords lv)]], + '022C' => ['actor_moved', 'a4 v3 V v5 V v5 a4 a2 v V C2 a6 C2 v', [qw(ID walk_speed opt1 opt2 option type hair_style weapon shield lowhead tick tophead midhead hair_color clothes_color head_dir guildID emblemID manner opt3 stance sex coords unknown1 unknown2 lv)]], # walking + '022E' => ($rpackets{'022E'}{length} == 71) # or 73 + ? ['homunculus_property', 'Z24 C v16 V2 v2', [qw(name state level hunger intimacy accessory atk matk hit critical def mdef flee aspd hp hp_max sp sp_max exp exp_max points_skill attack_range)]] + : ['homunculus_property', 'Z24 C v3 V v12 V2 v2', [qw(name state level hunger intimacy accessory atk matk hit critical def mdef flee aspd hp hp_max sp sp_max exp exp_max points_skill attack_range)]] + , + '022F' => ($rpackets{'022F'}{length} == 5) # or 7 + ? ['homunculus_food', 'C v', [qw(success foodID)]] + : ['homunculus_food', 'C V', [qw(success foodID)]] + , + '0230' => ['homunculus_info', 'C2 a4 V',[qw(type state ID val)]], + '0235' => ['skills_list'], # homunculus skills + '0238' => ['top10_pk_rank'], + # homunculus skill update + '0239' => ['skill_update', 'v4 C', [qw(skillID lv sp range up)]], # range = skill range, up = this skill can be leveled up further + '023A' => ['storage_password_request', 'v', [qw(flag)]], + '023C' => ['storage_password_result', 'v2', [qw(type val)]], + '023E' => ['storage_password_request', 'v', [qw(flag)]], + '0240' => ['mail_refreshinbox', 'v V', [qw(size count)]], + '0242' => ['mail_read', 'v V Z40 Z24 V3 v2 C3 a8 C Z*', [qw(len mailID title sender delete_time zeny amount nameID type identified broken upgrade cards msg_len message)]], + '0245' => ['mail_getattachment', 'C', [qw(fail)]], + '0249' => ['mail_send', 'C', [qw(fail)]], + '024A' => ['mail_new', 'V Z40 Z24', [qw(mailID title sender)]], + '0250' => ['auction_result', 'C', [qw(flag)]], + '0252' => ['auction_item_request_search', 'v V2', [qw(size pages count)]], + '0253' => ['starplace', 'C', [qw(which)]], # 3 + '0255' => ['mail_setattachment', 'a2 C', [qw(ID fail)]], + '0256' => ['auction_add_item', 'a2 C', [qw(ID fail)]], + '0257' => ['mail_delete', 'V v', [qw(mailID fail)]], + '0259' => ['gameguard_grant', 'C', [qw(server)]], + '025A' => ['cooking_list', 'v2 a*', [qw(len type item_list)]], + '025D' => ['auction_my_sell_stop', 'V', [qw(flag)]], + '025F' => ['auction_windows', 'V C4 v', [qw(flag unknown1 unknown2 unknown3 unknown4 unknown5)]], + '0260' => ['mail_window', 'v', [qw(flag)]], + '0274' => ['mail_return', 'V v', [qw(mailID fail)]], + # mail_return packet: '0274' => ['account_server_info', 'x2 a4 a4 a4 x30 C1 x4 a*', [qw(sessionID accountID sessionID2 accountSex serverInfo)]], + '0276' => ['account_server_info', 'v a4 a4 a4 a4 Z26 C V a*', [qw(len sessionID accountID sessionID2 lastLoginIP lastLoginTime accountSex iAccountSID serverInfo)]], + '027B' => ['premium_rates_info', 'V3', [qw(exp death drop)]], + # tRO new packets, need some work on them + '0283' => ['account_id', 'a4', [qw(accountID)]], + '0284' => ['GANSI_RANK', 'c24 c24 c24 c24 c24 c24 c24 c24 c24 c24 V10 v', [qw(name1 name2 name3 name4 name5 name6 name7 name8 name9 name10 pt1 pt2 pt3 pt4 pt5 pt6 pt7 pt8 pt9 pt10 switch)]], #TODO: PACKET_ZC_GANGSI_RANK + '0287' => ['cash_dealer', 'v V a*', [qw(len cash_points item_list)]], # -1 + '0289' => ['cash_buy_fail', 'V2 v', [qw(cash_points kafra_points fail)]], + '028A' => ['character_status', 'a4 V3', [qw(ID option lv opt3)]], + '0291' => ['message_string', 'v', [qw(index)]], + '0293' => ['boss_map_info', 'C V2 v2 x4 Z24', [qw(flag x y hours minutes name)]], + '0294' => ['book_read', 'a4 a4', [qw(bookID page)]], + '0295' => ['inventory_items_nonstackable', 'v a*', [qw(len itemInfo)]], + '0296' => ['storage_items_nonstackable', 'v a*', [qw(len itemInfo)]], + '0297' => ['cart_items_nonstackable', 'v a*', [qw(len itemInfo)]], + '0298' => ['rental_time', 'v V', [qw(nameID seconds)]], + '0299' => ['rental_expired', 'a2 v', [qw(ID nameID)]], + '029A' => ['inventory_item_added', 'a2 v2 C3 a8 v C2 a4', [qw(ID amount nameID identified broken upgrade cards type_equip type fail cards_ext)]], + '029B' => ($rpackets{'029B'}{length} == 72 # or 80 + ? ['mercenary_init', 'a4 v8 Z24 v5 V v2', [qw(ID atk matk hit critical def mdef flee aspd name level hp hp_max sp sp_max contract_end faith summons)]] + : ['mercenary_init', 'a4 v8 Z24 v V5 v V2 v', [qw(ID atk matk hit critical def mdef flee aspd name level hp hp_max sp sp_max contract_end faith summons kills attack_range)]]), + '029D' => ['skills_list'], # mercenary skills + '02A2' => ['stat_info', 'v V', [qw(type val)]], # was "mercenary_param_change" + # tRO HShield packet challenge. + # Borrow sub gameguard_request because it use the same mechanic. + '02A6' => ['gameguard_request'], + '02AA' => ['cash_password_request', 'v', [qw(info)]], #TODO: PACKET_ZC_REQ_CASH_PASSWORD + '02AC' => ['cash_password_result', 'v2', [qw(info count)]], #TODO: PACKET_ZC_RESULT_CASH_PASSWORD + '02AD' => ['login_pin_code_request', 'v V', [qw(flag key)]], # mRO PIN code Check + '02AE' => ['initialize_message_id_encryption', 'V2', [qw(param1 param2)]], # Packet Prefix encryption Support + # tRO new packets (2008-09-16Ragexe12_Th) + '02B1' => ['quest_all_list', 'v V a*', [qw(len quest_amount message)]], + '02B2' => ['quest_all_mission', 'v V a*', [qw(len mission_amount message)]], + '02B3' => ['quest_add', 'V C V2 v a*', [qw(questID active time_start time_expire mission_amount message)]], + '02B4' => ['quest_delete', 'V', [qw(questID)]], + '02B5' => ['quest_update_mission_hunt', 'v2 a*', [qw(len mission_amount message)]], + '02B7' => ['quest_active', 'V C', [qw(questID active)]], + '02B8' => ['party_show_picker', 'a4 v C3 a8 v C', [qw(sourceID nameID identified broken upgrade cards location type)]], + '02B9' => ['hotkeys', 'a*', [qw(hotkeys)]], + '02C1' => ['npc_chat', 'v a4 a4 Z*', [qw(len ID color message)]], + '02C5' => ['party_invite_result', 'Z24 V', [qw(name type)]], + '02C6' => ['party_invite', 'a4 Z24', [qw(ID name)]], + '02C9' => ['party_allow_invite', 'C', [qw(type)]], + '02CA' => ['login_error_game_login_server', 'C', [qw(type)]], + '02CB' => ['instance_window_start', 'Z61 v', [qw(name flag)]], + '02CC' => ['instance_window_queue', 'C', [qw(flag)]], + '02CD' => ['instance_window_join', 'Z61 V2', [qw(name time_remaining time_close)]], + '02CE' => ['instance_window_leave', 'C', [qw(flag)]], + '02D0' => ['inventory_items_nonstackable', 'v a*', [qw(len itemInfo)]], + '02D1' => ['storage_items_nonstackable', 'v a*', [qw(len itemInfo)]], + '02D2' => ['cart_items_nonstackable', 'v a*', [qw(len itemInfo)]], + '02D4' => ['inventory_item_added', 'a2 v2 C3 a8 v C2 a4 v', [qw(ID amount nameID identified broken upgrade cards type_equip type fail expire unknown)]], + '02D5' => ['isvr_disconnect'], #TODO: PACKET_ZC_ISVR_DISCONNECT + '02D7' => ['show_eq', 'v Z24 v7 C a*', [qw(len name type hair_style tophead midhead lowhead hair_color clothes_color sex equips_info)]], #type is job + '02D9' => ['misc_config_reply', 'V2', [qw(type flag)]], + '02DA' => ['show_eq_msg_self', 'C', [qw(type)]], + '02DC' => ['battleground_message', 'v a4 Z24 Z*', [qw(len ID name message)]], + '02DD' => ['battleground_emblem', 'a4 Z24 v', [qw(emblemID name ID)]], + '02DE' => ['battleground_score', 'v2', [qw(score_lion score_eagle)]], + '02DF' => ['battleground_position', 'a4 Z24 v3', [qw(ID name job x y)]], + '02E0' => ['battleground_hp', 'a4 Z24 v2', [qw(ID name hp max_hp)]], + # 02E1 packet unsure of dual_wield_damage needs more testing + # a4 a4 a4 V3 v C V ? + #'02E1' => ['actor_action', 'a4 a4 a4 V2 v x2 v x2 C v', [qw(sourceID targetID tick src_speed dst_speed damage div type dual_wield_damage)]], + '02E1' => ['actor_action', 'a4 a4 a4 V3 v C V', [qw(sourceID targetID tick src_speed dst_speed damage div type dual_wield_damage)]], + '02E7' => ['map_property', 'v2 a*', [qw(len type info_table)]], + '02E8' => ['inventory_items_stackable', 'v a*', [qw(len itemInfo)]], + '02E9' => ['cart_items_stackable', 'v a*', [qw(len itemInfo)]], + '02EA' => ['storage_items_stackable', 'v a*', [qw(len itemInfo)]], + '02EB' => ['map_loaded', 'V a3 a a v', [qw(syncMapSync coords xSize ySize font)]], # 13 + '02EC' => ['actor_exists', 'x a4 v3 V v5 V v5 a4 a4 V C2 a6 x2 v2',[qw(ID walk_speed opt1 opt2 option type hair_style weapon shield lowhead tick tophead midhead hair_color clothes_color head_dir guildID emblemID opt3 stance sex coords lv unknown)]], # Moving + '02ED' => ['actor_connected', 'a4 v3 V v10 a4 a4 V C2 a3 v3', [qw(ID walk_speed opt1 opt2 option type hair_style weapon shield lowhead tophead midhead hair_color clothes_color head_dir guildID emblemID opt3 stance sex coords act lv unknown)]], # Spawning + '02EE' => ['actor_moved', 'a4 v3 V v10 a4 a4 V C2 a3 x v3', [qw(ID walk_speed opt1 opt2 option type hair_style weapon shield lowhead tophead midhead hair_color clothes_color head_dir guildID emblemID opt3 stance sex coords act lv unknown)]], # Standing + '02EF' => ['font', 'a4 v', [qw(ID fontID)]], + '02F0' => ['progress_bar', 'V2', [qw(color time)]], + '02F2' => ['progress_bar_stop'], + '02F7' => ['guild_name', 'a4 a4 V C a4 Z24 a4', [qw(guildID emblemID mode is_master interSID guildName master_char_id)]], #47 + '040C' => ['local_broadcast', 'v a4 v4 Z*', [qw(len color font_type font_size font_align font_y message)]], #TODO: PACKET_ZC_BROADCAST3 + '043D' => ['skill_post_delay', 'v V', [qw(ID time)]], + '043E' => ['skill_post_delaylist', 'v a*', [qw(len skill_list)]], + '043F' => ['actor_status_active', 'v a4 C V4', [qw(type ID flag tick unknown1 unknown2 unknown3)]], + '0440' => ['millenium_shield', 'a4 v2', [qw(ID num state)]], + '0441' => ['skill_delete', 'v', [qw(skillID)]], + '0442' => ['sage_autospell', 'x2 V a*', [qw(why autoshadowspell_list)]], + '0444' => ['cash_item_list', 'v V3 c v', [qw(len cash_point price discount_price type item_id)]], #TODO: PACKET_ZC_SIMPLE_CASH_POINT_ITEMLIST + '0446' => ['minimap_indicator', 'a4 v4', [qw(npcID x y effect qtype)]], + '0449' => ['hack_shield_alarm'], + '07D8' => ['party_exp', 'V C2', [qw(type itemPickup itemDivision)]], + '07D9' => ['hotkeys', 'a*', [qw(hotkeys)]], # 268 # hotkeys:38 + '07DB' => ['stat_info', 'v V', [qw(type val)]], # 8 + '07E1' => ['skill_update', 'v V v3 C', [qw(skillID type lv sp range up)]], + '07E2' => ['message_string', 'v V', [qw(index param)]], + '07E3' => ['skill_exchange_item', 'V2', [qw(type val)]], # 8 + '07E6' => ['skill_msg', 'v V', [qw(id msgid)]], + # '07E6' => ['captcha_session_ID', 'v V', [qw(ID generation_time)]], # 8 is not used but add here to log + '07E8' => ['captcha_image', 'v a*', [qw(len image)]], # -1 + '07E9' => ['captcha_answer', 'v C', [qw(code flag)]], # 5 + '07F6' => ['exp', 'a4 V v2', [qw(ID val type flag)]], # 14 # type: 1 base, 2 job; flag: 0 normal, 1 quest # TODO: use. I think this replaces the exp gained message trough guildchat hack + '07F7' => ['actor_exists', 'v C a4 v3 V v5 a4 v5 a4 a2 v V C2 a6 C2 v2 Z*', [qw(len object_type ID walk_speed opt1 opt2 option type hair_style weapon shield lowhead tick tophead midhead hair_color clothes_color head_dir guildID emblemID manner opt3 stance sex coords xSize ySize lv font name)]], # -1 # walking + '07F8' => ['actor_connected', 'v C a4 v3 V v10 a4 a2 v V C2 a3 C2 v2 Z*', [qw(len object_type ID walk_speed opt1 opt2 option type hair_style weapon shield lowhead tophead midhead hair_color clothes_color head_dir guildID emblemID manner opt3 stance sex coords xSize ySize lv font name)]], # -1 # spawning + '07F9' => ['actor_moved', 'v C a4 v3 V v10 a4 a2 v V C2 a3 C3 v2 Z*', [qw(len object_type ID walk_speed opt1 opt2 option type hair_style weapon shield lowhead tophead midhead hair_color clothes_color head_dir guildID emblemID manner opt3 stance sex coords xSize ySize act lv font name)]], # -1 # standing + '07FA' => ['inventory_item_removed', 'v a2 v', [qw(reason ID amount)]], #//0x07fa,8 + '07FB' => ['skill_cast', 'a4 a4 v5 V C', [qw(sourceID targetID x y skillID unknown type wait dispose)]], + '07FC' => ['party_leader', 'V2', [qw(old new)]], + '07FD' => ['special_item_obtain', 'v C v c/Z a*', [qw(len type nameID holder etc)]], # record "c/Z" (holder) means: if the first byte ('c') = 24(dec), then Z24, if 'c' = 18(dec), then Z18, еtc. + '07FE' => ['sound_effect', 'Z24', [qw(name)]], + '07FF' => ['define_check', 'v V', [qw(len result)]], #TODO: PACKET_ZC_DEFINE_CHECK + '0800' => ['vender_items_list', 'v a4 a4 a*', [qw(len venderID venderCID itemList)]], # -1 + '0803' => ['booking_register_request', 'v', [qw(result)]], + '0805' => ['booking_search_request', 'x2 a a*', [qw(IsExistMoreResult innerData)]], + '0807' => ['booking_delete_request', 'v', [qw(result)]], + '0809' => ['booking_insert', 'V Z24 V v8', [qw(index name expire lvl map_id job1 job2 job3 job4 job5 job6)]], + '080A' => ['booking_update', 'V v6', [qw(index job1 job2 job3 job4 job5 job6)]], + '080B' => ['booking_delete', 'V', [qw(index)]], + '080E' => ['party_hp_info', 'a4 V2', [qw(ID hp hp_max)]], + '080F' => ['deal_add_other', 'v C V C3 a8', [qw(nameID type amount identified broken upgrade cards)]], # 0x080F,20 + '0810' => ['open_buying_store', 'c', [qw(amount)]], + '0812' => ['open_buying_store_fail', 'v', [qw(result)]], + '0813' => ['open_buying_store_item_list', 'v a4 V', [qw(len AID zeny)]], + '0814' => ['buying_store_found', 'a4 Z*', [qw(ID title)]], + '0816' => ['buying_store_lost', 'a4', [qw(ID)]], + '0818' => ['buying_store_items_list', 'v a4 a4 V a*', [qw(len buyerID buyingStoreID zeny itemList)]], + '081A' => ['buying_buy_fail', 'v', [qw(result)]], #4 + '081B' => ['buying_store_update', 'v2 V', [qw(itemID count zeny)]], + '081C' => ['buying_store_item_delete', 'a2 v V', [qw(ID amount zeny)]], + '081D' => ['elemental_info', 'a4 V4', [qw(ID hp hp_max sp sp_max)]], + '081E' => ['stat_info', 'v V', [qw(type val)]], # 8, Sorcerer's Spirit + '0824' => ['buying_store_fail', 'v2', [qw(result itemID)]], + '0828' => ['char_delete2_result', 'a4 V2', [qw(charID result deleteDate)]], # 14 + '082A' => ['char_delete2_accept_result', 'V V', [qw(charID result)]], # 10 + '082C' => ['char_delete2_cancel_result', 'a4 V', [qw(charID result)]], # 14 + '082D' => ['received_characters_info', 'v C5 x20', [qw(len normal_slot premium_slot billing_slot producible_slot valid_slot)]], + '0836' => ['search_store_result', 'v C3 a*', [qw(len first_page has_next remaining storeInfo)]], + '0837' => ['search_store_fail', 'C', [qw(reason)]], + '0839' => ['guild_expulsion', 'Z24 Z40', [qw(name message)]], + '083A' => ['search_store_open', 'v C', [qw(type amount)]], + '083D' => ['search_store_pos', 'v v', [qw(x y)]], + '083E' => ['login_error', 'V Z20', [qw(type date)]], + '0845' => ['cash_shop_open_result', 'V2', [qw(cash_points kafra_points)]], #10 + '0849' => ['cash_shop_buy_result', 'V s V', [qw(item_id result updated_points)]], + '084B' => ['item_appeared', 'a4 v2 C v2 C2 v', [qw(ID nameID type identified x y subx suby amount)]], + '0856' => ['actor_moved', 'v C a4 v3 V v5 a4 v6 a4 a2 v V C2 a6 C2 v2 Z*', [qw(len object_type ID walk_speed opt1 opt2 option type hair_style weapon shield lowhead tick tophead midhead hair_color clothes_color head_dir costume guildID emblemID manner opt3 stance sex coords xSize ySize lv font name)]], # -1 # walking provided by try71023 TODO: costume + '0857' => ['actor_exists', 'v C a4 v3 V v11 a4 a2 v V C2 a3 C3 v2 Z*', [qw(len object_type ID walk_speed opt1 opt2 option type hair_style weapon shield lowhead tophead midhead hair_color clothes_color head_dir costume guildID emblemID manner opt3 stance sex coords xSize ySize act lv font name)]], # -1 # spawning provided by try71023 + '0858' => ['actor_connected', 'v C a4 v3 V v11 a4 a2 v V C2 a3 C2 v2 Z*', [qw(len object_type ID walk_speed opt1 opt2 option type hair_style weapon shield lowhead tophead midhead hair_color clothes_color head_dir costume guildID emblemID manner opt3 stance sex coords xSize ySize lv font name)]], # -1 # standing provided by try71023 + '0859' => ['show_eq', 'v Z24 v7 v C a*', [qw(len name jobID hair_style tophead midhead lowhead robe hair_color clothes_color sex equips_info)]], + '08B3' => ['show_script', 'v a4 Z*', [qw(len ID message)]], + '08B4' => ['pet_capture_process'], + '08B6' => ['pet_capture_result', 'C', [qw(success)]], + #'08B9' => ['account_id', 'x4 V v', [qw(accountID unknown)]], # len: 12 Conflict with the struct (found in twRO 29032013) + '08B9' => ['login_pin_code_request', 'V a4 v', [qw(seed accountID flag)]], + '08BB' => ['login_pin_new_code_result', 'v V', [qw(flag seed)]], + '08C7' => ['area_spell', 'x2 a4 a4 v2 C3', [qw(ID sourceID x y type range isVisible)]], # -1 + '08C8' => ['actor_action', 'a4 a4 a4 V3 x v C V', [qw(sourceID targetID tick src_speed dst_speed damage div type dual_wield_damage)]], + '08CA' => ['cash_shop_list', 'v3 a*', [qw(len amount tabcode itemInfo)]],#-1 + '08CB' => ['rates_info', 's4 a*', [qw(len exp death drop detail)]], + '08CD' => ['actor_movement_interrupted', 'a4 v2', [qw(ID x y)]], + '08CF' => ['revolving_entity', 'a4 v v', [qw(sourceID type entity)]], + '08D2' => ['high_jump', 'a4 v2', [qw(ID x y)]], + '08E2' => ['navigate_to', 'C3 Z16 v3', [qw(type flag hide_window map x y mob_id)]], + '08FE' => ['quest_update_mission_hunt', 'v a*', [qw(len message)]], + '08FF' => ['actor_status_active', 'a4 v V4', [qw(ID type tick unknown1 unknown2 unknown3)]], + '0900' => ['inventory_items_stackable', 'v a*', [qw(len itemInfo)]], + '0901' => ['inventory_items_nonstackable', 'v a*', [qw(len itemInfo)]], + '0902' => ['cart_items_stackable', 'v a*', [qw(len itemInfo)]], + '0903' => ['cart_items_nonstackable', 'v a*', [qw(len itemInfo)]], + '0906' => ['show_eq', 'v Z24 x17 a*', [qw(len name equips_info)]], + '0908' => ['inventory_item_favorite', 'a2 C', [qw(ID flag)]],#5 + '090F' => ['actor_connected', 'v C a4 v3 V v11 a4 a2 v V C2 a3 C2 v2 V2 C v Z*', [qw(len object_type ID walk_speed opt1 opt2 option type hair_style weapon shield lowhead tophead midhead hair_color clothes_color head_dir costume guildID emblemID manner opt3 stance sex coords xSize ySize lv font maxHP HP isBoss body_style name)]], + '0914' => ['actor_moved', 'v C a4 v3 V v5 a4 v6 a4 a2 v V C2 a6 C2 v2 V2 C v Z*', [qw(len object_type ID walk_speed opt1 opt2 option type hair_style weapon shield lowhead tick tophead midhead hair_color clothes_color head_dir costume guildID emblemID manner opt3 stance sex coords xSize ySize lv font maxHP HP isBoss body_style name)]], + '0915' => ['actor_exists', 'v C a4 v3 V v11 a4 a2 v V C2 a3 C3 v2 V2 C v Z*', [qw(len object_type ID walk_speed opt1 opt2 option type hair_style weapon shield lowhead tophead midhead hair_color clothes_color head_dir costume guildID emblemID manner opt3 stance sex coords xSize ySize act lv font maxHP HP isBoss body_style name)]], + '096D' => ['merge_item_open', 'v a*', [qw(length itemList)]], #-1 + '096F' => ['merge_item_result', 'a2 v C', [qw(itemIndex total result)]], #5 + '0975' => ['storage_items_stackable', 'v Z24 a*', [qw(len title itemInfo)]], + '0976' => ['storage_items_nonstackable', 'v Z24 a*', [qw(len title itemInfo)]], + '0977' => ['monster_hp_info', 'a4 V V', [qw(ID hp hp_max)]], + '097A' => ['quest_all_list', 'v V a*', [qw(len quest_amount message)]], + '097B' => ['rates_info2', 's V3 a*', [qw(len exp death drop detail)]], + '097D' => ['top10', 'v a*', [qw(type message)]], + '097E' => ['rank_points', 'vV2', [qw(type points total)]], + '0983' => ['actor_status_active', 'v a4 C V5', [qw(type ID flag total tick unknown1 unknown2 unknown3)]], + '0984' => ['actor_status_active', 'a4 v V5', [qw(ID type total tick unknown1 unknown2 unknown3)]], + '0985' => ['skill_post_delaylist', 'v a*', [qw(len skill_list)]], + '0988' => ['clan_user', 'v2' ,[qw(onlineuser totalmembers)]], + '098A' => ['clan_info', 'v a4 Z24 Z24 Z16 C2 a*', [qw(len clan_ID clan_name clan_master clan_map alliance_count antagonist_count ally_antagonist_names)]], + '098D' => ['clan_leave'], + '098E' => ['clan_chat', 'v Z24 Z*', [qw(len charname message)]], + '0990' => ['inventory_item_added', 'a2 v2 C3 a8 V C2 a4 v', [qw(ID amount nameID identified broken upgrade cards type_equip type fail expire unknown)]], + '0991' => ['inventory_items_stackable', 'v a*', [qw(len itemInfo)]], + '0992' => ['inventory_items_nonstackable', 'v a*', [qw(len itemInfo)]], + '0993' => ['cart_items_stackable', 'v a*', [qw(len itemInfo)]], + '0994' => ['cart_items_nonstackable', 'v a*', [qw(len itemInfo)]], + '0995' => ['storage_items_stackable', 'v Z24 a*', [qw(len title itemInfo)]], + '0996' => ['storage_items_nonstackable', 'v Z24 a*', [qw(len title itemInfo)]], + '0997' => ['show_eq', 'v Z24 v7 v C a*', [qw(len name jobID hair_style tophead midhead lowhead robe hair_color clothes_color sex equips_info)]], + '0999' => ['equip_item', 'a2 V v C', [qw(ID type viewID success)]], #11 + '099A' => ['unequip_item', 'a2 V C', [qw(ID type success)]],#9 + '099B' => ['map_property3', 'v a4', [qw(type info_table)]], + '099D' => ['received_characters', 'v a*', [qw(len charInfo)]], + '099F' => ['area_spell_multiple2', 'v a*', [qw(len spellInfo)]], # -1 + '09A0' => ['sync_received_characters', 'V', [qw(sync_Count)]], + '09A6' => ['banking_check', 'V2 v',[qw(zeny zeny2 reason)]], + '09A8' => ['banking_deposit', 'v V2 V',[qw(reason zeny zeny2 balance)]], + '09AA' => ['banking_withdraw', 'v V2 V',[qw(reason zeny zeny2 balance)]], + '09BB' => ['storage_opened', 'v2', [qw(items items_max)]], + '09BF' => ['storage_closed'], + '09CA' => ['area_spell_multiple3', 'v a*', [qw(len spellInfo)]], # -1 + '09CB' => ['skill_used_no_damage', 'v V a4 a4 C', [qw(skillID amount targetID sourceID success)]], + '09CD' => ['message_string', 'v V', [qw(index param)]], + '09CF' => ['gameguard_request'], + '09D1' => ['progress_bar_unit', 'V3', [qw(GID color time)]], + '09D5' => ['npc_market_info', 'v a*', [qw(len itemList)]], + '09D7' => ['npc_market_purchase_result', 'v C a*', [qw(len result itemList)]], + '09DA' => ['guild_storage_log', 'v3 a*', [qw(len result count log)]], # -1 + '09DB' => ['actor_moved', 'v C a4 a4 v3 V v5 a4 v6 a4 a2 v V C2 a6 C2 v2 V2 C Z*', [qw(len object_type ID charID walk_speed opt1 opt2 option type hair_style weapon shield lowhead tick tophead midhead hair_color clothes_color head_dir costume guildID emblemID manner opt3 stance sex coords xSize ySize lv font maxHP HP isBoss name)]], + '09DC' => ['actor_connected', 'v C a4 a4 v3 V v11 a4 a2 v V C2 a3 C2 v2 V2 C Z*', [qw(len object_type ID charID walk_speed opt1 opt2 option type hair_style weapon shield lowhead tophead midhead hair_color clothes_color head_dir costume guildID emblemID manner opt3 stance sex coords xSize ySize lv font maxHP HP isBoss name)]], + '09DD' => ['actor_exists', 'v C a4 a4 v3 V v11 a4 a2 v V C2 a3 C3 v2 V2 C Z*', [qw(len object_type ID charID walk_speed opt1 opt2 option type hair_style weapon shield lowhead tophead midhead hair_color clothes_color head_dir costume guildID emblemID manner opt3 stance sex coords xSize ySize act lv font maxHP HP isBoss name)]], + '09DE' => ['private_message', 'v V Z24 C Z*', [qw(len charID privMsgUser isAdmin privMsg)]], + '09DF' => ['private_message_sent', 'C V', [qw(type charID)]], + '09E5' => ['shop_sold_long', 'v2 a4 V2', [qw(number amount charID time zeny)]], + '09E7' => ['unread_rodex', 'C', [qw(show)]], # 3 + '09EB' => ['rodex_read_mail', 'v C V2 v V2 C', [qw(len type mailID1 mailID2 text_len zeny1 zeny2 itemCount)]], # -1 + '09ED' => ['rodex_write_result', 'C', [qw(fail)]], # 3 + '09F0' => ['rodex_mail_list', 'v C3 a*', [qw(len type amount isEnd mailList)]], # -1 + '09F2' => ['rodex_get_zeny', 'V2 C2', [qw(mailID1 mailID2 type fail)]], # 12 + '09F4' => ['rodex_get_item', 'V2 C2', [qw(mailID1 mailID2 type fail)]], # 12 + '09F6' => ['rodex_delete', 'C V2', [qw(type mailID1 mailID2)]], # 11 + '09F7' => ($rpackets{'09F7'}{length} == 75) # or 77 + ? ['homunculus_property', 'Z24 C v12 V2 v2 V2 v2', [qw(name state level hunger intimacy accessory atk matk hit critical def mdef flee aspd hp hp_max sp sp_max exp exp_max points_skill attack_range)]] + : ['homunculus_property', 'Z24 C v3 V v8 V2 v2 V2 v2', [qw(name state level hunger intimacy accessory atk matk hit critical def mdef flee aspd hp hp_max sp sp_max exp exp_max points_skill attack_range)]] + , + '09F8' => ['quest_all_list', 'v V a*', [qw(len quest_amount message)]], + '09F9' => ['quest_add', 'V C V2 v a*', [qw(questID active time_start time_expire mission_amount message)]], + '09FA' => ['quest_update_mission_hunt', 'v2 a*', [qw(len mission_amount message)]], + '09FC' => ['pet_evolution_result', 'v V',[qw(len result)]], + '09FD' => ['actor_moved', 'v C a4 a4 v3 V v2 V2 v V v6 a4 a2 v V C2 a6 C2 v2 V2 C v Z*', [qw(len object_type ID charID walk_speed opt1 opt2 option type hair_style weapon shield lowhead tick tophead midhead hair_color clothes_color head_dir costume guildID emblemID manner opt3 stance sex coords xSize ySize lv font maxHP HP isBoss opt4 name)]], + '09FE' => ['actor_connected', 'v C a4 a4 v3 V v2 V2 v7 a4 a2 v V C2 a3 C2 v2 V2 C v Z*', [qw(len object_type ID charID walk_speed opt1 opt2 option type hair_style weapon shield lowhead tophead midhead hair_color clothes_color head_dir costume guildID emblemID manner opt3 stance sex coords xSize ySize lv font maxHP HP isBoss opt4 name)]], + '09FF' => ['actor_exists', 'v C a4 a4 v3 V v2 V2 v7 a4 a2 v V C2 a3 C3 v2 V2 C v Z*', [qw(len object_type ID charID walk_speed opt1 opt2 option type hair_style weapon shield lowhead tophead midhead hair_color clothes_color head_dir costume guildID emblemID manner opt3 stance sex coords xSize ySize act lv font maxHP HP isBoss opt4 name)]], + '0A00' => ['hotkeys', 'C a*', [qw(rotate hotkeys)]], # 269 # hotkeys:38 + '0A05' => ['rodex_add_item', 'C a2 v2 C4 a8 a25 v C V', [qw(fail ID amount nameID type identified broken upgrade cards options weight favorite type_equip)]], # 53 + '0A07' => ['rodex_remove_item', 'C a2 v2', [qw(result ID amount weight)]], # 9 + '0A09' => ['deal_add_other', 'v C V C3 a8 a25', [qw(nameID type amount identified broken upgrade cards options)]], + '0A0A' => ['storage_item_added', 'a2 V v C4 a8 a25', [qw(ID amount nameID type identified broken upgrade cards options)]], + '0A0B' => ['cart_item_added', 'a2 V v C4 a8 a25', [qw(ID amount nameID type identified broken upgrade cards options)]], + '0A0C' => ['inventory_item_added', 'a2 v2 C3 a8 V C2 a4 v a25', [qw(ID amount nameID identified broken upgrade cards type_equip type fail expire unknown options)]], + '0A0D' => ['inventory_items_nonstackable', 'v a*', [qw(len itemInfo)]], + '0A0F' => ['cart_items_nonstackable', 'v a*', [qw(len itemInfo)]], + '0A10' => ['storage_items_nonstackable', 'v Z24 a*', [qw(len title itemInfo)]], + '0A12' => ['rodex_open_write', 'Z24 C', [qw(name result)]], # 27 + '0A14' => ['rodex_check_player', 'V v2', [qw(char_id class base_level)]], + '0A15' => ['gold_pc_cafe_point', 'C2 V2', [qw(isActive mode point playedTime)]], # 12 + '0A17' => ['dynamicnpc_create_result', 'V', [qw(result)]], # 6 + '0A18' => ['map_loaded', 'V a3 C2 v C', [qw(syncMapSync coords xSize ySize font sex)]], # 14 + '0A1A' => ['roulette_window', 'C V C2 v V3', [qw(result serial stage price additional_item gold silver bronze)]], + '0A1C' => ['roulette_info', 'v V a*', [qw(len serial roulette_info)]], + '0A20' => ['roulette_window_update', 'C v3 V3', [qw(result stage price additional_item gold silver bronze)]], + '0A22' => ['roulette_recv_item', 'C v', [qw(type item_id)]], + '0A23' => ['achievement_list', 'v V V v V V', [qw(len ach_count total_points rank current_rank_points next_rank_points)]], # -1 + '0A24' => ['achievement_update', 'V v VVV C V10 V C', [qw(total_points rank current_rank_points next_rank_points achievementID completed objective1 objective2 objective3 objective4 objective5 objective6 objective7 objective8 objective9 objective10 completed_at reward)]], # 66 + '0A26' => ['achievement_reward_ack', 'C V', [qw(received achievementID)]], # 7 + '0A27' => ['hp_sp_changed', 'v V', [qw(type amount)]], + '0A28' => ['open_store_status', 'C', [qw(flag)]], + '0A2D' => ['show_eq', 'v Z24 v7 v C a*', [qw(len name jobID hair_style tophead midhead lowhead robe hair_color clothes_color sex equips_info)]], + '0A2F' => ['change_title', 'C V', [qw(result title_id)]], + '0A30' => ['actor_info', 'a4 Z24 Z24 Z24 Z24 V', [qw(ID name partyName guildName guildTitle titleID)]], + '0A34' => ['senbei_amount', 'V', [qw(amount)]], #new senbei system (new cash currency) + '0A36' => ['monster_hp_info_tiny', 'a4 C', [qw(ID hp)]], + '0A37' => ($rpackets{'0A37'}{length} == 57) # or 59 + ? ['inventory_item_added', 'a2 v2 C3 a8 V C2 a4 v a25 C', [qw(ID amount nameID identified broken upgrade cards type_equip type fail expire unknown options favorite)]] + : ['inventory_item_added', 'a2 v2 C3 a8 V C2 a4 v a25 C v', [qw(ID amount nameID identified broken upgrade cards type_equip type fail expire unknown options favorite viewID)]] + , + '0A38' => ['open_ui', 'C', [qw(type)]], + '0A3B' => ['hat_effect', 'v a4 C a*', [qw(len ID flag effect)]], # -1 + '0A43' => ['party_join', 'a4 V v4 C Z24 Z24 Z16 C2', [qw(ID role jobID lv x y type name user map item_pickup item_share)]], + '0A44' => ['party_users_info', 'v Z24 a*', [qw(len party_name playerInfo)]], + '0A47' => ['stylist_res', 'C', [qw(res)]], + '0A4A' => ['private_airship_type', 'V', [qw(type)]], + '0A4B' => ['map_change', 'Z16 v2', [qw(map x y)]], # ZC_AIRSHIP_MAPMOVE + '0A4C' => ['map_changed', 'Z16 v2 a4 v', [qw(map x y IP port)]], # ZC_AIRSHIP_SERVERMOVE + '0A51' => ['rodex_check_player', 'V v2 Z24', [qw(char_id class base_level name)]], # 34 + '0A53' => ['captcha_upload_request', 'Z4 V', [qw(captcha_key flag)]], + '0A55' => ['captcha_upload_request_status'], + '0A57' => ['macro_reporter_status', 'V', [qw(status)]], + '0A58' => ['macro_detector', 'v Z4', [qw(image_size captcha_key)]], + '0A59' => ['macro_detector_image', 'v Z4 a*', [qw(len captcha_key captcha_image)]], + '0A5B' => ['macro_detector_show', 'c V', [qw(remaining_chances remaining_time)]], + '0A5D' => ['macro_detector_status', 'V', [qw(status)]], + '0A6A' => ['captcha_preview', 'V v Z4', [qw(flag image_size captcha_key)]], + '0A6B' => ['captcha_preview_image', 'v Z4 a*', [qw(len captcha_key captcha_image)]], + '0A6D' => ['macro_reporter_select', 'v a*', [qw(len account_list)]], + '0A6F' => ['message_string', 'v2 V Z*', [qw(len index color param)]], # -1 + '0A7B' => ['EAC_key'], + '0A7D' => ['rodex_mail_list', 'v C3 a*', [qw(len type amount isEnd mailList)]], # -1 + '0A82' => ['guild_expulsion', 'Z40 a4', [qw(message charID)]], # 46 + '0A83' => ['guild_leave', 'a4 Z40', [qw(charID message)]], # 46 + '0A84' => ['guild_info', 'a4 V9 a4 Z24 Z16 V a4', [qw(ID lv conMember maxMember average exp exp_next tax tendency_left_right tendency_down_up emblemID name castles_string zeny master_char_id)]], + '0A89' => ['offline_clone_found', 'a4 v4 C v9 Z24', [qw(ID jobID unknown coord_x coord_y sex head_dir weapon shield lowhead tophead midhead hair_color clothes_color robe title)]], + '0A8A' => ['offline_clone_lost', 'a4', [qw(ID)]], + '0A8D' => ['vender_items_list', 'v a4 a4 C V a*', [qw(len venderID venderCID flag expireDate itemList)]], # -1 [offline vending store] + '0A91' => ['buying_store_items_list', 'v a4 a4 C V V x4 a*', [qw(len buyerID buyingStoreID flag expireDate zeny itemList)]], # -1 [offline buying store] + '0A95' => ['misc_config', 'C2', [qw(show_eq_flag call_flag)]], + '0A96' => ['deal_add_other', 'V C V C3 a16 a25 V v', [qw(nameID type amount identified broken upgrade cards options type_equip viewID)]],#61 + '0A98' => ($rpackets{'0A98'}{length} == 10) # or 12 + ? ['equip_item_switch', 'a2 V v', [qw(ID type success)]] + : ['equip_item_switch', 'a2 V2', [qw(ID type success)]] #kRO <= 20170502 + , + '0A9A' => ['unequip_item_switch', 'a2 V C', [qw(ID type success)]], + '0A9B' => ['equip_switch_log', 'v a*', [qw(len log)]], # -1 + '0A9D' => ['equip_switch_run_res', 'v', [qw(success)]], + '0AA0' => ['refineui_opened', '' ,[qw()]], + '0AA2' => ['refineui_info', 'v v C a*' ,[qw(len index bless materials)]], + '0AA5' => ['guild_members_list', 'v a*', [qw(len member_list)]], + '0AA8' => ['misc_config', 'C3', [qw(show_eq_flag call_flag pet_autofeed_flag)]], + '0AB2' => ['party_dead', 'a4 C', [qw(ID isDead)]], + '0AB8' => ['move_interrupt'], + '0AB9' => ['item_preview', 'a2 v a8 a25', [qw(index upgrade cards options)]], + '0ABD' => ['partylv_info', 'a4 v2', [qw(ID job lv)]], + '0ABE' => ['warp_portal_list', 'v2 Z16 Z16 Z16 Z16', [qw(len type memo1 memo2 memo3 memo4)]], #TODO : MapsCount || size is -1 + '0AC2' => ['rodex_mail_list', 'v C a*', [qw(len isEnd mailList)]], # -1 + '0AC4' => ['account_server_info', 'v a4 a4 a4 a4 a26 C x17 a*', [qw(len sessionID accountID sessionID2 lastLoginIP lastLoginTime accountSex serverInfo)]], + '0AC5' => ['received_character_ID_and_Map', 'a4 Z16 a4 v a128', [qw(charID mapName mapIP mapPort mapUrl)]], + '0AC7' => ['map_changed', 'Z16 v2 a4 v a128', [qw(map x y IP port url)]], # 156 + '0AC9' => ['account_server_info', 'v a4 a4 a4 a4 a26 C a6 a*', [qw(len sessionID accountID sessionID2 lastLoginIP lastLoginTime accountSex unknown serverInfo)]], + '0ACA' => ['errors', 'C', [qw(type)]], + '0ACB' => ['stat_info', 'v V2', [qw(type val val2)]], + '0ACC' => ['exp', 'a4 V2 v2', [qw(ID val val2 type flag)]], + '0ACD' => ['login_error', 'C Z20', [qw(type date)]], + '0ADA' => ['refine_status', 'Z24 V C C', [qw(name itemID refine_level status)]], + '0ADC' => ['misc_config', 'C4', [qw(show_eq_flag call_flag pet_autofeed_flag homunculus_autofeed_flag)]], + '0ADD' => ['item_appeared', 'a4 v2 C v2 C2 v C v', [qw(ID nameID type identified x y subx suby amount show_effect effect_type )]], + '0ADE' => ['overweight_percent', 'V', [qw(percent)]],# 6 TODO + '0ADF' => ['actor_info', 'a4 a4 Z24 Z24', [qw(ID charID name prefix_name)]], + '0AE0' => ['login_error', 'V V Z20', [qw(type error date)]], + '0AE2' => ['open_ui', 'C V', [qw(type data)]], + '0AE3' => ['received_login_token', 'v l Z20 Z*', [qw(len login_type flag login_token)]], + '0AE4' => ['party_join', 'a4 a4 V v4 C Z24 Z24 Z16 C2', [qw(ID charID role jobID lv x y type name user map item_pickup item_share)]], + '0AE5' => ['party_users_info', 'v Z24 a*', [qw(len party_name playerInfo)]], + '0AF0' => ['action_ui', 'C V', [qw(type data)]], + '0AF7' => ['character_name', 'v a4 Z24', [qw(flag ID name)]], + '0AFB' => ['sage_autospell', 'v a*', [qw(len autospell_list)]], #herc PR 2310 + '0AFD' => ['guild_position', 'v a4', [qw(len charID)]], #herc PR 2176 + '0AFE' => ['quest_update_mission_hunt', 'v2 a*', [qw(len mission_amount message)]], + '0AFF' => ['quest_all_list', 'v V a*', [qw(len quest_amount message)]], + '0B03' => ['show_eq', 'v Z24 v9 C a*', [qw(len name jobID hair_style tophead midhead lowhead robe hair_color clothes_color clothes_color2 sex equips_info)]], + '0B05' => ['offline_clone_found', 'a4 v4 C v9 V Z24 v', [qw(ID jobID unknown coord_x coord_y sex head_dir weapon shield lowhead tophead midhead hair_color clothes_color robe unknown2 name unknown3)]], + '0B08' => ['item_list_start', 'v C Z*', [qw(len type name)]], + '0B09' => ['item_list_stackable', 'v C a*', [qw(len type itemInfo)]], + '0B0A' => ['item_list_nonstackable', 'v C a*', [qw(len type itemInfo)]], + '0B0B' => ['item_list_end', 'C2', [qw(type flag)]], + '0B0C' => ['quest_add', 'V C V2 v a*', [qw(questID active time_start time_expire mission_amount message)]], + '0B13' => ['item_preview', 'a2 C v a16 a25', [qw(index broken upgrade cards options)]], + '0B18' => ['inventory_expansion_result', 'v', [qw(result)]], # + '0B1A' => ['skill_cast', 'a4 a4 v5 V C V', [qw(sourceID targetID x y skillID unknown type wait dispose unknow)]], # 29 + '0B1B' => ['load_confirm'], + '0B1D' => ['ping'], #2 + '0B20' => ['hotkeys', 'C v a*', [qw(rotate tab hotkeys)]],#herc PR 2468 + '0B2F' => ['homunculus_property', 'Z24 C v11 V2 v2 V2 v2', [qw(name state level hunger intimacy atk matk hit critical def mdef flee aspd hp hp_max sp sp_max exp exp_max points_skill attack_range)]], + '0B31' => ['skill_add', 'v V v3 C v', [qw(skillID target lv sp range upgradable lv2)]], #17 + '0B32' => ['skills_list'], + '0B33' => ['skill_update', 'v V v3 C v', [qw(skillID type lv sp range up lv2)]], #17 + '0B39' => ['item_list_nonstackable', 'v C a*', [qw(len type itemInfo)]], + '0B3D' => ['vender_items_list', 'v a4 a4 a*', [qw(len venderID venderCID itemList)]], # -1 + '0B41' => ['inventory_item_added', 'a2 v V C2 a16 V C2 a4 v a25 C v C2', [qw(ID amount nameID identified broken cards type_equip type fail expire unknown options favorite viewID upgrade grade)]], + '0B44' => ['storage_item_added', 'a2 V V C3 a16 a25 C2', [qw(ID amount nameID type identified broken cards options upgrade grade)]], + '0B45' => ['cart_item_added', 'a2 V V C3 a16 a25 C2', [qw(ID amount nameID type identified broken upgrade cards options upgrade grade)]], + '0B47' => ['char_emblem_update', 'a4 a4', [qw(guildID emblemID accountID)]], # 14 TODO + '0B5F' => ['rodex_mail_list', 'v C a*', [qw(len isEnd mailList)]], #-1 + '0B60' => ['account_server_info', 'v a4 a4 a4 a4 a26 C x17 a*', [qw(len sessionID accountID sessionID2 lastLoginIP lastLoginTime accountSex serverInfo)]], + '0B6F' => ['character_creation_successful', 'a*', [qw(charInfo)]], + '0B72' => ['received_characters', 'v a*', [qw(len charInfo)]], + '0B73' => ['revolving_entity', 'a4 v', [qw(sourceID entity)]], + '0B76' => ['homunculus_property', 'Z24 C v11 V6 v2', [qw(name state level hunger intimacy atk matk hit critical def mdef flee aspd hp hp_max sp sp_max exp exp_max points_skill attack_range)]], + '0B77' => ['npc_store_info', 'v a*', [qw(len itemList)]],#-1 + '0B7B' => ['guild_info', 'a4 V9 a4 Z24 Z16 V a4 Z24', [qw(ID lv conMember maxMember average exp exp_next tax tendency_left_right tendency_down_up emblemID name castles_string zeny master_char_id master)]], #118 + '0B7C' => ['guild_expulsion_list', 'v a*', [qw(len expulsion_list)]], # -1 + '0B7D' => ['guild_members_list', 'v a*', [qw(len member_list)]], # -1 + '0B7E' => ['guild_member_add', 'a4 a4 v5 V4 Z24', [qw(ID charID hair_style hair_color sex jobID lv contribution online position lastLoginTime name)]], # 60 TODO + '0B8D' => ['repute_info', 'v C a*', [qw(len sucess reputeInfo)]], # -1 + # 'C350' => ['senbei_vender_items_list'], #new senbei vender, need research + '0BA4' => ['homunculus_property', 'Z24 C v11 V4 V4 v2', [qw(name state level hunger intimacy atk matk hit critical def mdef flee aspd hp hp_max sp sp_max exp exp2 exp_max exp_max2 points_skill attack_range)]], + '0840' => ['notify_accessible_mapname', 'v a*', [qw(len mapList)]], # map_list + }; + + # Item RECORD Struct's + $self->{nested} = { + items_nonstackable => { # EQUIPMENTITEM_EXTRAINFO + type1 => { + len => 20, + types => 'a2 v C2 v2 C2 a8', + keys => [qw(ID nameID type identified type_equip equipped broken upgrade cards)], + }, + type2 => { + len => 24, + types => 'a2 v C2 v2 C2 a8 l', + keys => [qw(ID nameID type identified type_equip equipped broken upgrade cards expire)], + }, + type3 => { + len => 26, + types => 'a2 v C2 v2 C2 a8 l v', + keys => [qw(ID nameID type identified type_equip equipped broken upgrade cards expire bindOnEquipType)], + }, + type4 => { + len => 28, + types => 'a2 v C2 v2 C2 a8 l v2', + keys => [qw(ID nameID type identified type_equip equipped broken upgrade cards expire bindOnEquipType sprite_id)], + }, + type5 => { + len => 27, + types => 'a2 v C v2 C a8 l v2 C', + keys => [qw(ID nameID type type_equip equipped upgrade cards expire bindOnEquipType sprite_id identified)], + }, + type6 => { + len => 31, + types => 'a2 v C V2 C a8 l v2 C', + keys => [qw(ID nameID type type_equip equipped upgrade cards expire bindOnEquipType sprite_id identified)], + }, + type7 => { + len => 57, + types => 'a2 v C V2 C a8 l v2 C a25 C', + keys => [qw(ID nameID type type_equip equipped upgrade cards expire bindOnEquipType sprite_id num_options options identified)], + }, + type8 => { + len => 67, + types => 'a2 V C V2 C a16 l v2 C a25 C', + keys => [qw(ID nameID type type_equip equipped upgrade cards expire bindOnEquipType sprite_id num_options options identified)], + }, + type9 => { + len => 68, + types => 'a2 V C V2 a16 l v2 C a25 C3', + keys => [qw(ID nameID type type_equip equipped cards expire bindOnEquipType sprite_id num_options options upgrade grade identified)], + }, + }, + items_stackable => { + type1 => { + len => 10, + types => 'a2 v C2 v2', + keys => [qw(ID nameID type identified amount type_equip)], # type_equip or equipped? + }, + type2 => { + len => 18, + types => 'a2 v C2 v2 a8', + keys => [qw(ID nameID type identified amount type_equip cards)], + }, + type3 => { + len => 22, + types => 'a2 v C2 v2 a8 l', + keys => [qw(ID nameID type identified amount type_equip cards expire)], + }, + type5 => { + len => 22, + types => 'a2 v C v2 a8 l C', + keys => [qw(ID nameID type amount type_equip cards expire identified)], + }, + type6 => { + len => 24, + types => 'a2 v C v V a8 l C', + keys => [qw(ID nameID type amount type_equip cards expire identified)], + }, + type7 => { + len => 34, + types => 'a2 V C v V a16 l C', + keys => [qw(ID nameID type amount type_equip cards expire identified)], + }, + }, + }; + + my %sync_ex; + my $load_sync = Settings::addTableFile( 'sync.txt', loader => [ \&FileParsers::parseDataFile2, \%sync_ex ], mustExist => 0 ); + Settings::loadByHandle( $load_sync ); + Settings::removeFile( $load_sync ); + + foreach ( keys %sync_ex ) { + $self->{packet_list}{$_} = ['sync_request_ex']; + $self->{sync_ex_reply}{$_} = $sync_ex{$_}; + } + + return $self; +} + +use constant { + DEFINE__BROADCASTING_SPECIAL_ITEM_OBTAIN => 1 << 0, + DEFINE__RENEWAL_ADD_2 => 1 << 1, + DEFINE__CHANNELING_SERVICE => 1 << 2, +}; + +###################################### +#### Packet inner struct handlers #### +###################################### + +# Override this function if you need to. +sub items_nonstackable { + my ($self, $args) = @_; + + my $items = $self->{nested}->{items_nonstackable}; + + if($args->{switch} eq '00A4' || # inventory + $args->{switch} eq '00A6' || # storage + $args->{switch} eq '0122' # cart + ) { + return $items->{type1}; + + } elsif ($args->{switch} eq '0295' || # inventory + $args->{switch} eq '0296' || # storage + $args->{switch} eq '0297' # cart + ) { + return $items->{type2}; + + } elsif ($args->{switch} eq '02D0' || # inventory + $args->{switch} eq '02D1' || # storage + $args->{switch} eq '02D2' # cart + ) { + return $items->{$rpackets{'00AA'}{length} == 7 ? 'type3' : 'type4'}; + } elsif ($args->{switch} eq '0901' # inventory + || $args->{switch} eq '0976' # storage + || $args->{switch} eq '0903' # cart + || $args->{switch} eq '0906' # other player + ) { + return $items->{type5}; + } elsif ($args->{switch} eq '0992' # inventory + || $args->{switch} eq '0994' # cart + || $args->{switch} eq '0996' # storage + || $args->{switch} eq '0997' # other player + ) { + return $items->{type6}; + } elsif ($args->{switch} eq '0A0D' # inventory + || $args->{switch} eq '0A0F' # cart + || $args->{switch} eq '0A10' # storage + || $args->{switch} eq '0A2D' # other player + ) { + return $items->{type7}; + } elsif ($args->{switch} eq '0B0A') { # item_list + return $items->{type7} if ($masterServer->{itemListUseOldType}); + return $items->{type8}; + } elsif ($args->{switch} eq '0B39') { # item_list + return $items->{type9}; + } else { + warning "items_nonstackable: unsupported packet ($args->{switch})!\n"; + } +} + +# Override this function if you need to. +sub items_stackable { + my ($self, $args) = @_; + + my $items = $self->{nested}->{items_stackable}; + + if($args->{switch} eq '00A3' || # inventory + $args->{switch} eq '00A5' || # storage + $args->{switch} eq '0123' # cart + ) { + return $items->{type1}; + + } elsif ($args->{switch} eq '01EE' || # inventory + $args->{switch} eq '01F0' || # storage + $args->{switch} eq '01EF' # cart + ) { + return $items->{type2}; + + } elsif ($args->{switch} eq '02E8' || # inventory + $args->{switch} eq '02EA' || # storage + $args->{switch} eq '02E9' # cart + ) { + return $items->{type3}; + + } elsif ($args->{switch} eq '0900' # inventory + || $args->{switch} eq '0975' # storage + || $args->{switch} eq '0902' # cart + ) { + return $items->{type5}; + + } elsif ($args->{switch} eq '0991' # inventory + || $args->{switch} eq '0993' # cart + || $args->{switch} eq '0995' # storage + ) { + return $items->{type6}; + } elsif ($args->{switch} eq '0B09') { # item_list + return $items->{type6} if ($masterServer->{itemListUseOldType}); + return $items->{type7}; + } else { + warning "items_stackable: unsupported packet ($args->{switch})!\n"; + } +} + +sub parse_items { + my ($self, $args, $unpack, $process) = @_; + my @itemInfo; + + my $length = length $args->{itemInfo}; + for (my $i = 0; $i < $length; $i += $unpack->{len}) { + my $item; + @{$item}{@{$unpack->{keys}}} = unpack($unpack->{types}, substr($args->{itemInfo}, $i, $unpack->{len})); + + if ( $args->{switch} eq '0B09' && $masterServer->{serverType} ne 'iRO_Renewal' && existsInList("10, 16, 17, 19", $item->{type}) ) { # workaround arrow/ammunition byte bug + $item->{amount} = unpack("v", substr($args->{itemInfo}, $i+7, 2)); + } + + $process->($item); + + push @itemInfo, $item; + } + + @itemInfo +} + +=pod +parse_items_nonstackable + +Change in packet behavior: the amount is not specified, but this is a +non-stackable item (equipment), so the amount is obviously "1". + +=cut +sub parse_items_nonstackable { + my ($self, $args) = @_; + + $self->parse_items($args, $self->items_nonstackable($args), sub { + my ($item) = @_; + + #$item->{placeEtcTab} = $item->{identified} & (1 << 2); + + # Non stackable items now have no amount normally given in the + # packet, so we must assume one. We'll even play it safe, and + # not change the amount if it's already a non-zero value. + $item->{amount} = 1 unless ($item->{amount}); + $item->{broken} = $item->{identified} & (1 << 1) unless exists $item->{broken}; + $item->{identified} = $item->{identified} & (1 << 0); + }) +} + +sub parse_items_stackable { + my ($self, $args) = @_; + + $self->parse_items($args, $self->items_stackable($args), sub { + my ($item) = @_; + + #$item->{placeEtcTab} = $item->{identified} & (1 << 1); + $item->{identified} = $item->{identified} & (1 << 0); + }) +} + +sub _items_list { + my ($self, $args) = @_; + + for my $item (@{$args->{items}}) { + my ($local_item, $add); + + unless ($local_item = $args->{getter} && $args->{getter}($item)) { + $local_item = $args->{class}->new; + $add = 1; + } + + for ([keys %$item]) { + @{$local_item}{@$_} = @{$item}{@$_}; + } + $local_item->{name} = itemName($local_item); + $local_item->{serverID} = unpack('v', $local_item->{ID}); + + $args->{callback}($local_item) if $args->{callback}; + + $args->{adder}($local_item) if $add; + + my $index = ($local_item->{binID} >= 0) ? $local_item->{binID} : $local_item->{ID}; + debug "$args->{debug_str}: $local_item->{name} ($index) x $local_item->{amount} - $itemTypes_lut{$local_item->{type}}\n", 'parseMsg'; + Plugins::callHook($args->{hook}, { + index => $index, + item => $local_item + }); + } +} + +####################################### +###### Packet handling callbacks ###### +####################################### + +# Non kRO client still use old packet that without kafra_points (equals to kRO 2007-07-11) +# Confirmed on idRO_Renewal and iRO Chaos +sub parse_cash_dealer { + my ($self, $args) = @_; + $args->{kafra_points} = 0; +} + +sub guild_chat { + my ($self, $args) = @_; + my ($chatMsgUser, $chatMsg, $parsed_msg); # Type: String + my $chat; # Type: String + + return unless changeToInGameState(); + + $chat = bytesToString($args->{message}); + if (($chatMsgUser, $chatMsg) = $chat =~ /(.*?)\s?: (.*)/) { + $chatMsgUser =~ s/ $//; + stripLanguageCode(\$chatMsg); + $parsed_msg = solveMessage($chatMsg); + $chat = "$chatMsgUser : $parsed_msg"; + } else { + $parsed_msg = solveMessage($chat); + } + + chatLog("g", "$chat\n") if ($config{'logGuildChat'}); + # Translation Comment: Guild Chat + message TF("[Guild] %s\n", $chat), "guildchat"; + # Only queue this if it's a real chat message + ChatQueue::add('g', 0, $chatMsgUser, $parsed_msg) if ($chatMsgUser); + debug "guildchat: $chatMsg\n", "guildchat", 1; + + Plugins::callHook('packet_guildMsg', { + MsgUser => $chatMsgUser, + Msg => $parsed_msg, + RawMsg => $chatMsg, + }); +} + +sub inventory_items_nonstackable { + my ($self, $args) = @_; + return unless changeToInGameState(); + + $self->_items_list({ + class => 'Actor::Item', + hook => 'packet_inventory', + debug_str => 'Non-Stackable Inventory Item', + items => [$self->parse_items_nonstackable($args)], + getter => sub { $char->inventory->getByID($_[0]{ID}) }, + adder => sub { $char->inventory->add($_[0]) }, + callback => sub { + my ($local_item) = @_; + + if ($local_item->{equipped}) { + foreach (%equipSlot_rlut){ + if ($_ & $local_item->{equipped}){ + next if $_ == 10; #work around Arrow bug + next if $_ == 32768; + $char->{equipment}{$equipSlot_lut{$_}} = $local_item; + } + } + } + } + }); +} + +sub item_skill { + my ($self, $args) = @_; + + my $skillID = $args->{skillID}; + my $targetType = $args->{targetType}; # we don't use this yet + my $skillLv = $args->{skillLv}; + my $sp = $args->{sp}; # we don't use this yet + my $skillName = $args->{skillName}; + + my $skill = new Skill(idn => $skillID, level => $skillLv); + message TF("Permitted to use %s (%d), level %d\n", $skill->getName, $skill->getIDN, $skill->getLevel); + + unless ($config{noAutoSkill}) { + $messageSender->sendSkillUse($skillID, $skillLv, $accountID); + undef $char->{permitSkill}; + } else { + $char->{permitSkill} = $skill; + } + + Plugins::callHook('item_skill', { + ID => $skillID, + level => $skillLv, + name => $skillName + }); +} + +sub public_chat { + my ($self, $args) = @_; + # Type: String + my $message = bytesToString($args->{message}); + my ($chatMsgUser, $chatMsg, $parsed_msg); # Type: String + my ($actor, $dist); + + if ($message =~ / : /) { + ($chatMsgUser, $chatMsg) = split / : /, $message, 2; + $chatMsgUser =~ s/ $//; + $chatMsg =~ s/^ //; + stripLanguageCode(\$chatMsg); + + $actor = Actor::get($args->{ID}); + $dist = "unknown"; + if (!$actor->isa('Actor::Unknown')) { + $dist = distance($char->{pos_to}, $actor->{pos_to}); + $dist = sprintf("%.1f", $dist) if ($dist =~ /\./); + } + $parsed_msg = solveMessage($chatMsg); + $message = "$chatMsgUser ($actor->{binID}): $parsed_msg"; + + } else { + $chatMsg = $message; + $message = $parsed_msg = solveMessage($message); + } + + my $position = sprintf("[%s %d, %d]", + $field ? $field->baseName : T("Unknown field,"), + $char->{pos_to}{x}, $char->{pos_to}{y}); + my $distInfo; + if ($actor) { + $position .= sprintf(" [%d, %d] [dist=%s] (%d)", + $actor->{pos_to}{x}, $actor->{pos_to}{y}, + $dist, $actor->{nameID}); + $distInfo = "[dist=$dist] "; + } + + # this code autovivifies $actor->{pos_to} but it doesnt matter + chatLog("c", "$position $message\n") if ($config{logChat}); + message TF("%s%s\n", $distInfo, $message), "publicchat"; + debug "publicchat: $chatMsg\n", "publicchat", 1; + + ChatQueue::add('c', $args->{ID}, $chatMsgUser, $parsed_msg); + Plugins::callHook('packet_pubMsg', { + pubID => $args->{ID}, + pubMsgUser => $chatMsgUser, + pubMsg => $parsed_msg, + MsgUser => $chatMsgUser, + Msg => $parsed_msg, + RawMsg => $chatMsg, + }); +} + +sub map_property { + my ($self, $args) = @_; + + if($config{'status_mapProperty'}){ + $char->setStatus(@$_) for map {[$_->[1], $args->{type} == $_->[0]]} + grep { $args->{type} == $_->[0] || $char->{statuses}{$_->[1]} } + map {[$_, defined $mapPropertyTypeHandle{$_} ? $mapPropertyTypeHandle{$_} : "UNKNOWN_MAPPROPERTY_TYPE_$_"]} + 1 .. List::Util::max $args->{type}, keys %mapPropertyTypeHandle; + + if ($args->{info_table}) { + my $info_table = unpack('V1',$args->{info_table}); + for (my $i = 0; $i < 16; $i++) { + if ($info_table&(1<<$i)) { + $char->setStatus(defined $mapPropertyInfoHandle{$i} ? $mapPropertyInfoHandle{$i} : "UNKNOWN_MAPPROPERTY_INFO_$i",1); + } + } + } + } + $pvp = {1 => 1, 3 => 2}->{$args->{type}}; + if ($pvp) { + Plugins::callHook('pvp_mode', { + pvp => $pvp # 1 PvP, 2 GvG + }); + } +} + +sub map_property2 { + my ($self, $args) = @_; + + if($config{'status_mapType'}){ + $char->setStatus(@$_) for map {[$_->[1], $args->{type} == $_->[0]]} + grep { $args->{type} == $_->[0] || $char->{statuses}{$_->[1]} } + map {[$_, defined $mapTypeHandle{$_} ? $mapTypeHandle{$_} : "UNKNOWN_MAPTYPE_$_"]} + 0 .. List::Util::max $args->{type}, keys %mapTypeHandle; + } + $pvp = {6 => 1, 8 => 2, 19 => 3}->{$args->{type}}; + if ($pvp) { + Plugins::callHook('pvp_mode', { + pvp => $pvp # 1 PvP, 2 GvG, 3 Battleground + }); + } +} + +sub skill_use { + my ($self, $args) = @_; + return unless changeToInGameState(); + + if (my $spell = $spells{$args->{sourceID}}) { + # Resolve source of area attack skill + $args->{sourceID} = $spell->{sourceID}; + } + + my $source = Actor::get($args->{sourceID}); + my $target = Actor::get($args->{targetID}); + $args->{source} = $source; + $args->{target} = $target; + delete $source->{casting}; + + # Perform trigger actions + if ($args->{switch} eq "0114") { + $args->{damage} = intToSignedShort($args->{damage}); + } else { + $args->{damage} = intToSignedInt($args->{damage}); + } + updateDamageTables($args->{sourceID}, $args->{targetID}, $args->{damage}) if ($args->{damage} != -30000); + setSkillUseTimer($args->{skillID}, $args->{targetID}) if ( + $args->{sourceID} eq $accountID + or $char->{slaves} && $char->{slaves}{$args->{sourceID}} + ); + setPartySkillTimer($args->{skillID}, $args->{targetID}) if ( + $args->{sourceID} eq $accountID + or $char->{slaves} && $char->{slaves}{$args->{sourceID}} + or $args->{sourceID} eq $args->{targetID} # wtf? + ); + countCastOn($args->{sourceID}, $args->{targetID}, $args->{skillID}); + + # Resolve source and target names + my $skill = new Skill(idn => $args->{skillID}); + $args->{skill} = $skill; + my $disp = skillUse_string($source, $target, $skill->getName(), $args->{damage}, + $args->{level}, ($args->{src_speed})); + + if ($args->{damage} != -30000 && + $args->{sourceID} eq $accountID && + $args->{targetID} ne $accountID) { + calcStat($args->{damage}); + } + + my $domain = ($args->{sourceID} eq $accountID) ? "selfSkill" : "skill"; + + if ($args->{damage} == 0) { + $domain = "attackMonMiss" if (($args->{sourceID} eq $accountID && $args->{targetID} ne $accountID) || ($char->{homunculus} && $args->{sourceID} eq $char->{homunculus}{ID} && $args->{targetID} ne $char->{homunculus}{ID})); + $domain = "attackedMiss" if (($args->{sourceID} ne $accountID && $args->{targetID} eq $accountID) || ($char->{homunculus} && $args->{sourceID} ne $char->{homunculus}{ID} && $args->{targetID} eq $char->{homunculus}{ID})); + + } elsif ($args->{damage} != -30000) { + $domain = "attackMon" if (($args->{sourceID} eq $accountID && $args->{targetID} ne $accountID) || ($char->{homunculus} && $args->{sourceID} eq $char->{homunculus}{ID} && $args->{targetID} ne $char->{homunculus}{ID})); + $domain = "attacked" if (($args->{sourceID} ne $accountID && $args->{targetID} eq $accountID) || ($char->{homunculus} && $args->{sourceID} ne $char->{homunculus}{ID} && $args->{targetID} eq $char->{homunculus}{ID})); + } + + if ((($args->{sourceID} eq $accountID) && ($args->{targetID} ne $accountID)) || + (($args->{sourceID} ne $accountID) && ($args->{targetID} eq $accountID))) { + my $status = sprintf("[%3d/%3d] ", $char->hp_percent, $char->sp_percent); + $disp = $status.$disp; + } elsif ($char->{slaves} && $char->{slaves}{$args->{sourceID}} && !$char->{slaves}{$args->{targetID}}) { + my $status = sprintf("[%3d/%3d] ", $char->{slaves}{$args->{sourceID}}->hp_percent, $char->{slaves}{$args->{sourceID}}->sp_percent); + $disp = $status.$disp; + } elsif ($char->{slaves} && !$char->{slaves}{$args->{sourceID}} && $char->{slaves}{$args->{targetID}}) { + my $status = sprintf("[%3d/%3d] ", $char->{slaves}{$args->{targetID}}->hp_percent, $char->{slaves}{$args->{targetID}}->sp_percent); + $disp = $status.$disp; + } + $target->{sitting} = 0 unless $args->{type} == 4 || $args->{type} == 9 || $args->{damage} == 0; + + #EFST_MAGICPOWER OVERRIDE + if ($args->{sourceID} eq $accountID && $char->statusActive('EFST_MAGICPOWER') && $args->{skillID} != 366) { + $char->setStatus("EFST_MAGICPOWER", 0); + } + + Plugins::callHook('packet_skilluse', { + 'skillID' => $args->{skillID}, + 'sourceID' => $args->{sourceID}, + 'targetID' => $args->{targetID}, + 'damage' => $args->{damage}, + 'amount' => 0, + 'x' => 0, + 'y' => 0, + 'disp' => \$disp + }); + message $disp, $domain, 1; + + if ($args->{targetID} eq $accountID && $args->{damage} > 0) { + $damageTaken{$source->{name}}{$skill->getName()} += $args->{damage}; + } +} + +# Skill used on a set of map tile coordinates. +# Examples: Warp Portal/Teleport, Bard/Dancer skills, etc. +# +sub skill_use_location { + my ($self, $args) = @_; + + # Skill used on coordinates + my $skillID = $args->{skillID}; + my $sourceID = $args->{sourceID}; + my $lv = $args->{lv}; + my $x = $args->{x}; + my $y = $args->{y}; + + # Perform trigger actions + setSkillUseTimer($skillID) if $sourceID eq $accountID; + + # Resolve source name + my $source = Actor::get($sourceID); + my $skillName = Skill->new(idn => $skillID)->getName(); + my $disp = skillUseLocation_string($source, $skillName, $args); + + delete $source->{casting}; + + # Print skill use message + my $domain = ($sourceID eq $accountID) ? "selfSkill" : "skill"; + message $disp, $domain; + + #EFST_MAGICPOWER OVERRIDE + if ($args->{sourceID} eq $accountID && $char->statusActive('EFST_MAGICPOWER') && $args->{skillID} != 366) { + $char->setStatus("EFST_MAGICPOWER", 0); + } + + Plugins::callHook('packet_skilluse', { + 'skillID' => $skillID, + 'sourceID' => $sourceID, + 'targetID' => '', + 'damage' => 0, + 'amount' => $lv, + 'x' => $x, + 'y' => $y + }); +} +# TODO: a skill can fail, do something with $args->{success} == 0 (this means that the skill failed) +sub skill_used_no_damage { + my ($self, $args) = @_; + return unless changeToInGameState(); + + # Skill used on target, with no damage done + if (my $spell = $spells{$args->{sourceID}}) { + # Resolve source of area attack skill + $args->{sourceID} = $spell->{sourceID}; + } + + # Perform trigger actions + # FIXME: setSkillUseTimer does many different things, so which of them "screw up monk comboing"? + setSkillUseTimer($args->{skillID}, $args->{targetID}) if ($args->{sourceID} eq $accountID + && $skillsArea{$args->{skillHandle}} != 2); # ignore these skills because they screw up monk comboing + setPartySkillTimer($args->{skillID}, $args->{targetID}) if + $args->{sourceID} eq $accountID or $args->{sourceID} eq $args->{targetID}; + countCastOn($args->{sourceID}, $args->{targetID}, $args->{skillID}); + if ($args->{sourceID} eq $accountID) { + my $pos = calcPosFromPathfinding($field, $char); + %{$char->{pos}} = %{$pos}; + %{$char->{pos_to}} = %{$pos}; + $char->{time_move} = 0; + $char->{time_move_calc} = 0; + $char->{solution} = []; + push(@{$char->{solution}}, { x => $char->{pos}{x}, y => $char->{pos}{y} }); + } + + # Resolve source and target names + my ($source, $target); + $target = $args->{target} = Actor::get($args->{targetID}); + $source = $args->{source} = ( + $args->{sourceID} ne "\000\000\000\000" + ? Actor::get($args->{sourceID}) + : $target # for Heal generated by Potion Pitcher sourceID = 0 + ); + my $verb = $source->verb('use', 'uses'); + + delete $source->{casting}; + + # Print skill use message + my $extra = ""; + if ($args->{skillID} == 28) { + $extra = ": $args->{amount} hp gained"; + updateDamageTables($args->{sourceID}, $args->{targetID}, -$args->{amount}); + } elsif ($args->{amount} != 65535 && $args->{amount} != 4294967295) { + $extra = ": Lv $args->{amount}"; + } + + my $domain = ($args->{sourceID} eq $accountID) ? "selfSkill" : "skill"; + my $skill = $args->{skill} = new Skill(idn => $args->{skillID}); + my $disp = skillUseNoDamage_string($source, $target, $skill->getIDN(), $skill->getName(), $args->{amount}); + message $disp, $domain; + + # Set teleport time + if ($args->{sourceID} eq $accountID && $skill->getHandle() eq 'AL_TELEPORT') { + $timeout{ai_teleport_delay}{time} = time; + } + + if (AI::state == AI::AUTO && $config{'autoResponseOnHeal'}) { + # Handle auto-response on heal + my $player = $playersList->getByID($args->{sourceID}); + if ($player && ($args->{skillID} == 28 || $args->{skillID} == 29 || $args->{skillID} == 34)) { + if ($args->{targetID} eq $accountID) { + chatLog("k", "***$source ".$skill->getName()." on $target$extra***\n"); + sendMessage($messageSender, "pm", getResponse("skillgoodM"), $player->name); + } elsif ($monstersList->getByID($args->{targetID})) { + chatLog("k", "***$source ".$skill->getName()." on $target$extra***\n"); + sendMessage($messageSender, "pm", getResponse("skillbadM"), $player->name); + } + } + } + + #EFST_MAGICPOWER OVERRIDE + if ($args->{sourceID} eq $accountID && $char->statusActive('EFST_MAGICPOWER') && $args->{skillID} != 366) { + $char->setStatus("EFST_MAGICPOWER", 0); + } + + Plugins::callHook('packet_skilluse', { + skillID => $args->{skillID}, + sourceID => $args->{sourceID}, + targetID => $args->{targetID}, + damage => 0, + amount => $args->{amount}, + x => 0, + y => 0 + }); +} + +# 08CB +sub rates_info { + my ($self, $args) = @_; + my %rates = ( + exp => { total => $args->{exp} }, + death => { total => $args->{death} }, + drop => { total => $args->{drop} }, + ); + + # get details + for (my $offset = 0; $offset < length($args->{detail}); $offset += 7) { + my ($type, $exp, $death, $drop) = unpack("C s3", substr($args->{detail}, $offset, 7)); + $rates{exp}{$type} = $exp; $rates{death}{$type} = $death; $rates{drop}{$type} = $drop; + } + + # we have 4 kinds of detail: + # $rates{exp or drop or death}{DETAIL_KIND} + # 0 = base server exp (?) + # 1 = premium acc additional exp + # 2 = server additional exp + # 3 = not sure, maybe it's for "extra exp" events? never seen this using the official client (bRO) + message T("=========================== Server Infos ===========================\n"), "info"; + message TF("EXP Rates: %s%% (Base %s%% + Premium %s%% + Server %s%% + Plus %s%%) \n", $rates{exp}{total}, $rates{exp}{0}, $rates{exp}{1}, $rates{exp}{2}, $rates{exp}{3}), "info"; + message TF("Drop Rates: %s%% (Base %s%% + Premium %s%% + Server %s%% + Plus %s%%) \n", $rates{drop}{total}, $rates{drop}{0}, $rates{drop}{1}, $rates{drop}{2}, $rates{drop}{3}), "info"; + message TF("Death Penalty: %s%% (Base %s%% + Premium %s%% + Server %s%% + Plus %s%%) \n", $rates{death}{total}, $rates{death}{0}, $rates{death}{1}, $rates{death}{2}, $rates{death}{3}), "info"; + message "=====================================================================\n", "info"; +} + +sub auction_item_request_search { + my ($self, $args) = @_; + + #$pages = $args->{pages};$size = $args->{size}; + undef $auctionList; + my $count = $args->{count}; + + if (!$count) { + message T("No item in auction.\n"), "info"; + return; + } + + message TF("Found %s items in auction.\n", $count), "info"; + my $msg; + $msg .= center(" " . T("Auction") . " ", 79, '-') . "\n"; + $msg .= swrite("\@%s \@%s \@%s \@%s \@%s", ('>'x2), ('<'x37), ('>'x10), ('>'x10), ('<'x11), + ["#", T("Item"), T("High Bid"), T("Purchase"), T("End-Date")]); + $msg .= sprintf("%s\n", ('-'x79)); + + my $j = 0; + for (my $i = 12; $i < 12 + $count * 83; $i += 83) { + $auctionList->[$j]->{ID} = unpack("V1", substr($args->{RAW_MSG}, $i, 4)); + $auctionList->[$j]->{seller} = bytesToString(unpack("Z24", substr($args->{RAW_MSG}, $i+4, 24))); + $auctionList->[$j]->{nameID} = unpack("v1", substr($args->{RAW_MSG}, $i+28, 2)); + $auctionList->[$j]->{type} = unpack("v1", substr($args->{RAW_MSG}, $i+30, 2)); + $auctionList->[$j]->{unknown} = unpack("v1", substr($args->{RAW_MSG}, $i+32, 2)); + $auctionList->[$j]->{amount} = unpack("v1", substr($args->{RAW_MSG}, $i+34, 2)); + $auctionList->[$j]->{identified} = unpack("C1", substr($args->{RAW_MSG}, $i+36, 1)); + $auctionList->[$j]->{broken} = unpack("C1", substr($args->{RAW_MSG}, $i+37, 1)); + $auctionList->[$j]->{upgrade} = unpack("C1", substr($args->{RAW_MSG}, $i+38, 1)); + # TODO + #$auctionList->[$j]->{card}->[0] = unpack("v1", substr($args->{RAW_MSG}, $i+39, 2)); + #$auctionList->[$j]->{card}->[1] = unpack("v1", substr($args->{RAW_MSG}, $i+41, 2)); + #$auctionList->[$j]->{card}->[2] = unpack("v1", substr($args->{RAW_MSG}, $i+43, 2)); + #$auctionList->[$j]->{card}->[3] = unpack("v1", substr($args->{RAW_MSG}, $i+45, 2)); + $auctionList->[$j]->{cards} = unpack("a8", substr($args->{RAW_MSG}, $i+39, 8)); + $auctionList->[$j]->{price} = unpack("V1", substr($args->{RAW_MSG}, $i+47, 4)); + $auctionList->[$j]->{buynow} = unpack("V1", substr($args->{RAW_MSG}, $i+51, 4)); + $auctionList->[$j]->{buyer} = bytesToString(unpack("Z24", substr($args->{RAW_MSG}, $i+55, 24))); + $auctionList->[$j]->{timestamp} = unpack("V1", substr($args->{RAW_MSG}, $i+79, 4)); + + my $item = {}; + $item->{nameID} = $auctionList->[$j]->{nameID}; + $item->{upgrade} = $auctionList->[$j]->{upgrade}; + $item->{cards} = $auctionList->[$j]->{cards}; + $item->{broken} = $auctionList->[$j]->{broken}; + $item->{name} = itemName($item); + + $msg .= swrite("\@%s \@%s \@%s \@%s \@%s", ('>'x2),, ('<'x37), ('>'x10), ('>'x10), ('<'x11), + [$j, $item->{name}, formatNumber($auctionList->[$j]->{price}), + formatNumber($auctionList->[$j]->{buynow}), getFormattedDate(int($auctionList->[$j]->{timestamp}))]); + $j++; + } + + $msg .= sprintf("%s\n", ('-'x79)); + message($msg, "list"); +} + +# 0151 +# TODO +sub guild_emblem_img { + my ($self, $args) = @_; + debug $self->{packet_list}{$args->{switch}}->[0] . " " . join(', ', @{$args}{@{$self->{packet_list}{$args->{switch}}->[2]}}) . "\n"; +} + +sub battleground_score { + my ($self, $args) = @_; + message TF("Battleground score - Lions: '%d' VS Eagles: '%d'\n", $args->{score_lion}, $args->{score_eagle}), "info"; +} + +sub battleground_position { + my ($self, $args) = @_; +} + +sub battleground_hp { + my ($self, $args) = @_; +} + +sub define_check { + my ($self, $args) = @_; + #TODO +} + +sub battlefield_position { + my ($self, $args) = @_; + + my $ID = $args->{ID}; + my $name = $args->{name}; +} + +sub battlefield_hp { + my ($self, $args) = @_; + + my $ID = $args->{ID}; + my $name = $args->{name}; + +} + +sub millenium_shield { + my ($self, $args) = @_; +} + +sub senbei_amount { + my ($self, $args) = @_; + + $char->{senbei} = $args->{senbei}; +} + +*changeToInGameState = *Network::Receive::changeToInGameState; + +1; diff --git a/openkore_llm_knowledge/networking/Network/Receive/iRO.pm b/openkore_llm_knowledge/networking/Network/Receive/iRO.pm new file mode 100644 index 0000000000..ec115f4e3e --- /dev/null +++ b/openkore_llm_knowledge/networking/Network/Receive/iRO.pm @@ -0,0 +1,44 @@ +######################################################################### +# OpenKore - Network subsystem +# Copyright (c) 2006 OpenKore Team +# +# This software is open source, licensed under the GNU General Public +# License, version 2. +# Basically, this means that you're allowed to modify and distribute +# this software. However, if you distribute modified versions, you MUST +# also distribute the source code. +# See http://www.gnu.org/licenses/gpl.html for the full license. +######################################################################### +# iRO (International) +# Servertype overview: https://openkore.com/wiki/ServerType +package Network::Receive::iRO; + +use strict; +use base qw(Network::Receive::ServerType0); + +sub new { + my ($class) = @_; + my $self = $class->SUPER::new(@_); + my %packets = ( + '0097' => ['private_message', 'v Z24 V Z*', [qw(len privMsgUser flag privMsg)]], # -1 + ); + + foreach my $switch (keys %packets) { + $self->{packet_list}{$switch} = $packets{$switch}; + } + + my %handlers = qw( + received_characters 099D + received_characters_info 082D + sync_received_characters 09A0 + ); + + $self->{packet_lut}{$_} = $handlers{$_} for keys %handlers; + + # Version 2 of the 0800 vender items packet (with options). + $self->{vender_items_list_item_pack} = 'V v2 C v C3 a8 a25'; + + return $self; +} + +1; \ No newline at end of file diff --git a/openkore_llm_knowledge/networking/Network/Receive/kRO.pm b/openkore_llm_knowledge/networking/Network/Receive/kRO.pm new file mode 100644 index 0000000000..a14d58cdad --- /dev/null +++ b/openkore_llm_knowledge/networking/Network/Receive/kRO.pm @@ -0,0 +1,23 @@ +######################################################################### +# OpenKore - Packet Receiveing +# This module contains functions for Receiveing packets to the server. +# +# This software is open source, licensed under the GNU General Public +# License, version 2. +# Basically, this means that you're allowed to modify and distribute +# this software. However, if you distribute modified versions, you MUST +# also distribute the source code. +# See http://www.gnu.org/licenses/gpl.html for the full license. +# +# $Revision: 6687 $ +# $Id: kRO.pm 6687 2009-04-19 19:04:25Z technologyguild $ +######################################################################## +# Korea (kRO) +# The majority of private servers use eAthena, this is a clone of kRO +package Network::Receive::kRO; +use strict; +use Network::Receive qw(:actor_type :connection :stat_info :party_invite :party_leave :exp_origin); +use base 'Network::Receive'; + + +1; \ No newline at end of file diff --git a/openkore_llm_knowledge/networking/Network/Send/ServerType0.pm b/openkore_llm_knowledge/networking/Network/Send/ServerType0.pm new file mode 100644 index 0000000000..a24f055c63 --- /dev/null +++ b/openkore_llm_knowledge/networking/Network/Send/ServerType0.pm @@ -0,0 +1,512 @@ +######################################################################### +# OpenKore - Network subsystem +# This module contains functions for sending messages to the server. +# +# This software is open source, licensed under the GNU General Public +# License, version 2. +# Basically, this means that you're allowed to modify and distribute +# this software. However, if you distribute modified versions, you MUST +# also distribute the source code. +# See http://www.gnu.org/licenses/gpl.html for the full license. +######################################################################### +# June 21 2007, this is the server type for: +# pRO (Philippines), except Sakray and Thor +# And many other servers. +# Servertype overview: https://openkore.com/wiki/ServerType +package Network::Send::ServerType0; + +use strict; + +use Network::Send (); +use base qw(Network::Send); +use Plugins; +use Globals qw(%rpackets $char $masterServer); +use Log qw(debug); +use Translation qw(T TF); +use I18N qw(stringToBytes); +use Utils; +use Utils::Exceptions; +use Utils::Rijndael; + +# to test zealotus bug +#use Data::Dumper; + + +sub new { + my ($class) = @_; + my $self = $class->SUPER::new(@_); + + my %packets = ( + '0064' => ['master_login', 'V Z24 Z24 C', [qw(version username password master_version)]], + '0065' => ['game_login', 'a4 a4 a4 v C', [qw(accountID sessionID sessionID2 userLevel accountSex)]], + '0066' => ['char_login', 'C', [qw(slot)]], + '0067' => ['char_create', 'a24 C7 v2', [qw(name str agi vit int dex luk slot hair_color hair_style)]], + '0068' => ['char_delete', 'a4 a40', [qw(charID email)]], + '0072' => ['map_login', 'a4 a4 a4 V C', [qw(accountID charID sessionID tick sex)]], + '007D' => ['map_loaded'], # len 2 + '007E' => ['sync', 'V', [qw(time)]], + '0085' => ['character_move', 'a3', [qw(coords)]], + '0089' => ['actor_action', 'a4 C', [qw(targetID type)]], + '008C' => ['public_chat', 'v Z*', [qw(len message)]], + '0090' => ['npc_talk', 'a4 C', [qw(ID type)]], + '0094' => ['actor_info_request', 'a4', [qw(ID)]], + '0096' => ['private_message', 'x2 Z24 Z*', [qw(privMsgUser privMsg)]], + '0099' => ['gm_broadcast', 'v Z*', [qw(len message)]], + '009B' => ['actor_look_at', 'v C', [qw(head body)]], + '009F' => ['item_take', 'a4', [qw(ID)]], + '00A2' => ['item_drop', 'a2 v', [qw(ID amount)]], + '00A7' => ['item_use', 'a2 a4', [qw(ID targetID)]],#8 + '00A9' => ['send_equip', 'a2 v', [qw(ID type)]],#6 + '00AB' => ['send_unequip_item', 'a2', [qw(ID)]], + '00B2' => ['restart', 'C', [qw(type)]], + '00B8' => ['npc_talk_response', 'a4 C', [qw(ID response)]], + '00B9' => ['npc_talk_continue', 'a4', [qw(ID)]], + '00BB' => ['send_add_status_point', 'v2', [qw(statusID Amount)]], + '00BF' => ['send_emotion', 'C', [qw(ID)]], + '00C1' => ['request_user_count'], + '00C5' => ['request_buy_sell_list', 'a4 C', [qw(ID type)]], + '00C8' => ['buy_bulk', 'v a*', [qw(len buyInfo)]], + '00C9' => ['sell_bulk', 'v a*', [qw(len sellInfo)]], + '00CC' => ['gm_kick', 'a4', [qw(targetAccountID)]], + '00CE' => ['gm_kick_all'], + '00CF' => ['ignore_player', 'Z24 C', [qw(name flag)]], + '00D0' => ['ignore_all', 'C', [qw(flag)]], + '00D3' => ['get_ignore_list'], + '00D5' => ['chat_room_create', 'v2 C Z8 a*', [qw(len limit public password title)]], + '00D9' => ['chat_room_join', 'a4 Z8', [qw(ID password)]], + '00DE' => ['chat_room_change', 'v2 C Z8 a*', [qw(len limit public password title)]], + '00E0' => ['chat_room_bestow', 'V Z24', [qw(role name)]], + '00E2' => ['chat_room_kick', 'Z24', [qw(name)]], + '00E3' => ['chat_room_leave'], + '00E4' => ['deal_initiate', 'a4', [qw(ID)]], + '00E6' => ['deal_reply', 'C', [qw(action)]], + '00E8' => ['deal_item_add', 'a2 V', [qw(ID amount)]], + '00EB' => ['deal_finalize'], + '00ED' => ['deal_cancel'], + '00EF' => ['deal_trade'], + '00F3' => ['storage_item_add', 'a2 V', [qw(ID amount)]], + '00F5' => ['storage_item_remove', 'a2 V', [qw(ID amount)]], + '00F7' => ['storage_close'], + '00FC' => ['party_join_request', 'a4', [qw(ID)]], + '00FF' => ['party_join', 'a4 V', [qw(ID flag)]], + '0100' => ['party_leave'], + '0102' => ['party_setting', 'V', [qw(exp)]], + '0103' => ['party_kick', 'a4 Z24', [qw(ID name)]], + '0108' => ['party_chat', 'x2 Z*', [qw(message)]], + '0112' => ['send_add_skill_point', 'v', [qw(skillID)]], + '0113' => ['skill_use', 'v2 a4', [qw(lv skillID targetID)]], + '0116' => ['skill_use_location', 'v4', [qw(lv skillID x y)]], + '011B' => ['warp_select', 'v Z16', [qw(skillID mapName)]], + '011D' => ['memo_request'], + '0126' => ['cart_add', 'a2 V', [qw(ID amount)]], + '0127' => ['cart_get', 'a2 V', [qw(ID amount)]], + '0128' => ['storage_to_cart', 'a2 V', [qw(ID amount)]], + '0129' => ['cart_to_storage', 'a2 V', [qw(ID amount)]], + '012A' => ['companion_release'], + '012E' => ['shop_close'], # len 2 + '0130' => ['send_entering_vending', 'a4', [qw(accountID)]], + '0134' => ['buy_bulk_vender', 'x2 a4 a*', [qw(venderID itemInfo)]], + '013F' => ['gm_item_mob_create', 'a24', [qw(name)]], + '0140' => ['gm_move_to_map', 'Z16 v v', [qw(mapName x y)]], + '0143' => ['npc_talk_number', 'a4 V', [qw(ID value)]], + '0146' => ['npc_talk_cancel', 'a4', [qw(ID)]], + '0149' => ['alignment', 'a4 C v', [qw(targetID type point)]], + '014D' => ['guild_check'], # len 2 + '014F' => ['guild_info_request', 'V', [qw(type)]], + '0151' => ['guild_emblem_request', 'a4', [qw(guildID)]], + '0159' => ['guild_leave', 'a4 a4 a4 Z40', [qw(guildID accountID charID reason)]], + '015B' => ['guild_kick', 'a4 a4 a4 Z40', [qw(guildID accountID charID reason)]], + '015D' => ['guild_break', 'a4', [qw(guildName)]], + '0165' => ['guild_create', 'a4 Z24', [qw(charID guildName)]], + '0168' => ['guild_join_request', 'a4 a4 a4', [qw(ID accountID charID)]], + '016B' => ['guild_join', 'a4 V', [qw(ID flag)]], + '016E' => ['guild_notice', 'a4 Z60 Z120', [qw(guildID name notice)]], + '0170' => ['guild_alliance_request', 'a4 a4 a4', [qw(targetAccountID accountID charID)]], + '0172' => ['guild_alliance_reply', 'a4 V', [qw(ID flag)]], + '0178' => ['identify', 'a2', [qw(ID)]], + '017A' => ['card_merge_request', 'a2', [qw(cardID)]], + '017C' => ['card_merge', 'a2 a2', [qw(cardID itemID)]], + '017E' => ['guild_chat', 'x2 Z*', [qw(message)]], + '0187' => ['ban_check', 'a4', [qw(accountID)]], + '018A' => ['quit_request', 'v', [qw(type)]], + '018E' => ['make_item_request', 'v4', [qw(nameID material_nameID1 material_nameID2 material_nameID3)]], # Forge Item / Create Potion + '0190' => ['skill_use_location_text', 'v5 Z80', [qw(lvl ID x y info)]], + '0193' => ['actor_name_request', 'a4', [qw(ID)]], + '0197' => ['gm_reset_state_skill', 'v', [qw(type)]], + '0198' => ['gm_change_cell_type', 'v v v', [qw(x y type)]], + '019C' => ['gm_broadcast_local', 'v Z*', [qw(len message)]], + '019D' => ['gm_change_effect_state', 'V', [qw(effect_state)]], + '019F' => ['pet_capture', 'a4', [qw(ID)]], + '01A1' => ['pet_menu', 'C', [qw(action)]], + '01A5' => ['pet_name', 'a24', [qw(name)]], + '01A7' => ['pet_hatch', 'a2', [qw(ID)]], + '01A9' => ['pet_emotion', 'V', [qw(ID)]], + '01AE' => ['make_arrow', 'v', [qw(nameID)]], + '01AF' => ['change_cart', 'v', [qw(lvl)]], + '01B2' => ['shop_open', 'v a80 C a*', [qw(len title result vendingInfo)]], + '01BA' => ['gm_remove', 'a24', [qw(playerName)]], + '01BB' => ['gm_shift', 'a24', [qw(playerName)]], + '01BC' => ['gm_recall', 'a24', [qw(playerName)]], + '01BD' => ['gm_summon_player', 'a24', [qw(playerName)]], + '01C0' => ['request_remain_time'], + '01CE' => ['auto_spell', 'V', [qw(ID)]], + '01D5' => ['npc_talk_text', 'v a4 Z*', [qw(len ID text)]], + '01DB' => ['secure_login_key_request'], # len 2 + '01DD' => ['master_login', 'V Z24 a16 C', [qw(version username password_salted_md5 master_version)]], + '01DF' => ['gm_request_account_name', 'V', [qw(targetID)]], + '01E7' => ['novice_dori_dori'], + '01ED' => ['novice_explosion_spirits'], + '01F7' => ['adopt_reply_request', 'V3', [qw(parentID1 parentID2 result)]], + '01F9' => ['adopt_request', 'V', [qw(ID)]], + '01FA' => ['master_login', 'V Z24 a16 C C', [qw(version username password_salted_md5 master_version clientInfo)]], + '01FD' => ['repair_item', 'v2 C a8', [qw(index nameID upgrade cards)]], + '0202' => ['friend_request', 'a*', [qw(username)]],# len 26 + '0203' => ['friend_remove', 'a4 a4', [qw(accountID charID)]], + '0204' => ['client_hash', 'a16', [qw(hash)]], + '0208' => ['friend_response', 'a4 a4 V', [qw(friendAccountID friendCharID type)]], + '0212' => ['manner_by_name', 'Z24', [qw(playerName)]], + '0213' => ['gm_request_status', 'Z24', [qw(playerName)]], + '0217' => ['rank_blacksmith'], + '0218' => ['rank_alchemist'], + '021D' => ['less_effect'], # TODO + '0222' => ['refine_item', 'V', [qw(ID)]], + '0225' => ['rank_taekwon'], + '022D' => ['homunculus_command', 'v C', [qw(commandType commandID)]], + '0231' => ['homunculus_name', 'a24', [qw(name)]], + '0232' => ['actor_move', 'a4 a3', [qw(ID coords)]], # should be called slave_move... + '0233' => ['slave_attack', 'a4 a4 C', [qw(slaveID targetID flag)]], + '0234' => ['slave_move_to_master', 'a4', [qw(slaveID)]], + '0237' => ['rank_killer'], + '023B' => ['storage_password', 'v a*', [qw(type data)]], + '023F' => ['mailbox_open'], + '0241' => ['mail_read', 'V', [qw(mailID)]], + '0243' => ['mail_delete', 'V', [qw(mailID)]], + '0244' => ['mail_attachment_get', 'V', [qw(mailID)]], + '0246' => ['mail_remove', 'v', [qw(flag)]], + '0247' => ['mail_attachment_set', 'a2 V', [qw(ID amount)]], + '0248' => ['mail_send', 'v Z24 a40 C a*', [qw(len recipient title body_len body)]], + '024B' => ['auction_add_item_cancel', 'v', [qw(flag)]], + '024C' => ['auction_add_item', 'a2 V', [qw(ID amount)]], + '024D' => ['auction_create', 'V V v', [qw(now_price max_price delete_time)]], + '024E' => ['auction_cancel', 'V', [qw(ID)]], + '024F' => ['auction_buy', 'V V', [qw(ID price)]], + '0251' => ['auction_search', 'v V Z24 v', [qw(type price search_string page)]], + '0254' => ['starplace_agree', 'C', [qw(flag)]], + '025B' => ['cook_request', 'v2', [qw(type nameID)]], + '025C' => ['auction_info_self', 'v', [qw(type)]], + '025D' => ['auction_sell_stop', 'V', [qw(ID)]], + '0273' => ['mail_return', 'V Z24', [qw(mailID sender)]], + '0275' => ['game_login', 'a4 a4 a4 v C Z16 V', [qw(accountID sessionID sessionID2 userLevel accountSex mac iAccountSID)]], + '0288' => ['cash_dealer_buy', 'v2 V', [qw(itemid amount kafra_points)]], + '0292' => ['auto_revive'], + '029F' => ['mercenary_command', 'C', [qw(flag)]], + '02B0' => ['master_login', 'V Z24 a24 C Z16 Z14 C', [qw(version username password_rijndael master_version ip mac isGravityID)]], + '02B6' => ['send_quest_state', 'V C', [qw(questID state)]], + '02BA' => ['hotkey_change', 'v C V v', [qw(idx type id lvl)]], + '02C4' => ['party_join_request_by_name', 'Z24', [qw(partyName)]], + '02C7' => ['party_join_request_by_name_reply', 'a4 C', [qw(accountID flag)]], + '02CF' => ['memorial_dungeon_command', 'V', [qw(command)]], + '02D6' => ['view_player_equip_request', 'a4', [qw(ID)]], + '02D8' => ['misc_config_set', 'V2', [qw(type flag)]], + '02DB' => ['battleground_chat', 'v Z*', [qw(len message)]], + '02F1' => ['notify_progress_bar_complete'], + '035F' => ['character_move', 'a3', [qw(coords)]], + '0360' => ['sync', 'V', [qw(time)]], + '0361' => ['actor_look_at', 'v C', [qw(head body)]], + '0362' => ['item_take', 'a4', [qw(ID)]], + '0363' => ['item_drop', 'a2 v', [qw(ID amount)]], + '0364' => ['storage_item_add', 'a2 V', [qw(ID amount)]], + '0365' => ['storage_item_remove', 'a2 V', [qw(ID amount)]], + '0366' => ['skill_use_location', 'v4', [qw(lv skillID x y)]], + '0367' => ['skill_use_location_text', 'v5 Z80', [qw(lvl ID x y info)]], + '0368' => ['actor_info_request', 'a4', [qw(ID)]], + '0369' => ['actor_name_request', 'a4', [qw(ID)]], + '0436' => ['map_login', 'a4 a4 a4 V C', [qw(accountID charID sessionID tick sex)]], + '0437' => ['actor_action', 'a4 C', [qw(targetID type)]], + '0438' => ['skill_use', 'v2 a4', [qw(lv skillID targetID)]], + '0439' => ['item_use', 'a2 a4', [qw(ID targetID)]], + '0443' => ['skill_select', 'V v', [qw(why skillID)]], + '0447' => ['blocking_play_cancel'], + '044A' => ['client_version', 'V', [qw(clientVersion)]], + '07DA' => ['party_leader', 'a4', [qw(accountID)]], + '07D7' => ['party_setting', 'V C2', [qw(exp itemPickup itemDivision)]], + '07E4' => ['item_list_window_selected', 'v V V a*', [qw(len type act itemInfo)]], + '07E7' => ['captcha_answer', 'v a4 a24', [qw(len accountID answer)]], + '0801' => ['buy_bulk_vender', 'v a4 a4 a*', [qw(len venderID venderCID itemInfo)]], #Selling store + '0802' => ['booking_register', 'v8', [qw(level MapID job0 job1 job2 job3 job4 job5)]], + '0804' => ['booking_search', 'v3 V s', [qw(level MapID job LastIndex ResultCount)]], + '0806' => ['booking_delete'], + '0808' => ['booking_update', 'v6', [qw(job0 job1 job2 job3 job4 job5)]], + '0811' => ['buy_bulk_openShop', 'v V C Z80 a*', [qw(len limitZeny result storeName itemInfo)]], # Buying store + '0815' => ['buy_bulk_closeShop'], + '0817' => ['buy_bulk_request', 'a4', [qw(ID)]], #6 + '0819' => ['buy_bulk_buyer', 'v a4 a4 a*', [qw(len buyerID buyingStoreID itemInfo)]], #Buying store + '0825' => ['token_login', 'v v x v Z24 a27 Z17 Z15 a*', [qw(len version master_version username password_rijndael mac ip token)]], # kRO Zero 2017/2018 login + '0827' => ['char_delete2', 'a4', [qw(charID)]], # 6 + '0829' => ['char_delete2_accept', 'a4 a6', [qw(charID code)]], # 12 + '082B' => ['char_delete2_cancel', 'a4', [qw(charID)]], # 6 + '0835' => ['search_store_info', 'v C V2 C2 a*', [qw(len type max_price min_price item_count card_count item_card_list)]], + '0838' => ['search_store_request_next_page'], + '083B' => ['search_store_close'], + '083C' => ['search_store_select', 'a4 a4 v', [qw(accountID storeID nameID)]], + '0842' => ['recall_sso', 'V', [qw(ID)]], + '0843' => ['remove_aid_sso', 'V', [qw(ID)]], + '0844' => ['cash_shop_open'],#2 + '0846' => ['req_cash_tabcode', 'v', [qw(ID)]], + '0848' => ($rpackets{'0848'}{minLength} == 6) ? + ['cash_shop_buy', 'v v a*', [qw(len count buy_info)]] : + ['cash_shop_buy', 'v v V a*', [qw(len count kafra_points buy_info)]], + '084A' => ['cash_shop_close'],#2 + '08B5' => ['pet_capture', 'a4', [qw(ID)]], + '08B8' => ['send_pin_password','a4 Z*', [qw(accountID pin)]], + '08BA' => ['new_pin_password','a4 Z*', [qw(accountID pin)]], + '08C1' => ['macro_start'],#2 + '08C2' => ['macro_stop'],#2 + '08C9' => ['request_cashitems'],#2 + '096E' => ['merge_item_request', 'v a*', [qw(length itemList)]], #-1 + '0970' => ['char_create', 'a24 C v2', [qw(name slot hair_style hair_color)]], + '0974' => ['merge_item_cancel'], #2 + '097C' => ['rank_general', 'v', [qw(type)]], + '0987' => ['master_login', 'V Z24 a32 C', [qw(version username password_md5_hex master_version)]], + '098D' => ['clan_chat', 'v Z*', [qw(len message)]], + '098F' => ['char_delete2_accept', 'v a4 a*', [qw(len charID code)]], + '0998' => ['send_equip', 'a2 V', [qw(ID type)]],#8 + '09A1' => ['sync_received_characters'], + '09A7' => ['banking_deposit_request', 'a4 V', [qw(accountID zeny)]], + '09A9' => ['banking_withdraw_request', 'a4 V', [qw(accountID zeny)]], + '09AB' => ['banking_check_request', 'a4', [qw(accountID)]], + '09D0' => ['gameguard_reply'], + '09D4' => ['sell_buy_complete'], + '09D6' => ['buy_bulk_market', 'v a*', [qw(len buyInfo)]], + '09D8' => ['market_close'], + '09E1' => ['guild_storage_item_add', 'a2 V', [qw(ID amount)]], + '09E2' => ['guild_storage_item_remove', 'a2 V', [qw(ID amount)]], + '09E3' => ['cart_to_guild_storage', 'a2 V', [qw(ID amount)]], + '09E4' => ['guild_storage_to_cart', 'a2 V', [qw(ID amount)]], + #'08BE' => ['change_pin_password','a*', [qw(accountID oldPin newPin)]], # TODO: PIN change system/command? + '09E8' => ['rodex_open_mailbox', 'C V2', [qw(type mailID1 mailID2)]], # 11 -- RodexOpenMailbox + '09E9' => ['rodex_close_mailbox'], # 2 -- RodexCloseMailbox + '09EA' => ['rodex_read_mail', 'C V2', [qw(type mailID1 mailID2)]], # 11 -- RodexReadMail + '09EE' => ['rodex_next_maillist', 'C V2', [qw(type mailID1 mailID2)]], # 11 -- RodexNextMaillist + '09EF' => ['rodex_refresh_maillist', 'C V2', [qw(type mailID1 mailID2)]], # 11 -- RodexRefreshMaillist + '09F1' => ['rodex_request_zeny', 'V2 C', [qw(mailID1 mailID2 type)]], # 11 -- RodexRequestZeny + '09F3' => ['rodex_request_items', 'V2 C', [qw(mailID1 mailID2 type)]], # 11 -- RodexRequestItems + '09F5' => ['rodex_delete_mail', 'C V2', [qw(type mailID1 mailID2)]], # 11 -- RodexDeleteMail + '09FB' => ['pet_evolution', 'a4 a*', [qw(ID itemInfo)]], + '0A03' => ['rodex_cancel_write_mail'], # 2 -- RodexCancelWriteMail + '0A04' => ['rodex_add_item', 'a2 v', [qw(ID amount)]], # 6 -- RodexAddItem + '0A06' => ['rodex_remove_item', 'a2 v', [qw(ID amount)]], # 6 -- RodexRemoveItem + '0A08' => ['rodex_open_write_mail', 'Z24', [qw(name)]], # 26 -- RodexOpenWriteMail + '0A13' => ['rodex_checkname', 'Z24', [qw(name)]], # 26 -- RodexCheckName + '0A16' => ['dynamicnpc_create_request', 'Z24', [qw(name)]], # 26 + '0A19' => ['roulette_window_open'], + '0A1B' => ['roulette_info_request'], + '0A1D' => ['roulette_close'], + '0A1F' => ['roulette_start'], + '0A21' => ['roulette_claim_prize'], + '0A25' => ['achievement_get_reward', 'V', [qw(achievementID)]], + '0A2E' => ['send_change_title', 'V', [qw(ID)]], + '0A39' => ['char_create', 'a24 C v4 C', [qw(name slot hair_color hair_style job_id unknown sex)]], + '0A46' => ['stylist_change', 'v6' ,[qw(hair_color hair_style cloth_color head_top head_mid head_bottom)]], + '0A49' => ['private_airship_request', 'Z16 v' ,[qw(map_name nameID)]], + '0A52' => ['captcha_register', 'Z16 v', [qw(answer image_size)]], + '0A54' => ['captcha_upload_request_ack', 'v Z4 a*', [qw(len captcha_key captcha_image)]], + '0A56' => ['macro_reporter_ack', 'a4', [qw(ID)]], + '0A5A' => ['macro_detector_download'], + '0A5C' => ['macro_detector_answer', 'Z16', [qw(answer)]], + '0A69' => ['captcha_preview_request', 'V', [qw(captcha_key)]], + '0A6C' => ['macro_reporter_select', 'v2 C', [qw(x y range)]], + '0A68' => ['open_ui_request', 'C', [qw(UIType)]], + '0A6E' => ['rodex_send_mail', 'v Z24 Z24 V2 v v V a* a*', [qw(len receiver sender zeny1 zeny2 title_len body_len char_id title body)]], # -1 -- RodexSendMail + '0A76' => ['master_login', 'V Z40 a32 v', [qw(version username password_rijndael master_version)]], + '0A97' => ['equip_switch_add', 'a2 V', [qw(ID position)]], # Add item to equipswitch + '0A99' => ['equip_switch_remove', 'a2', [qw(ID)]], # remove item in equipswitch + '0A9C' => ['equip_switch_run'], # switch Item ! + '0AA1' => ['refineui_select', 'a2' ,[qw(index)]], + '0AA3' => ['refineui_refine', 'a2 v C' ,[qw(index catalyst bless)]], + '0AA4' => ['refineui_close', '' ,[qw()]], + '0AAC' => ['master_login', 'V Z30 a32 C', [qw(version username password_hex master_version)]], + '0AC0' => ['rodex_open_mailbox', 'C V6', [qw(type mailID1 mailID2 mailReturnID1 mailReturnID2 mailAccountID1 mailAccountID2)]], # 26 -- RodexOpenMailbox + '0AC1' => ['rodex_refresh_maillist', 'C V6', [qw(type mailID1 mailID2 mailReturnID1 mailReturnID2 mailAccountID1 mailAccountID2)]], # 26 -- RodexRefreshMaillist + '0ACE' => ['equip_switch_single', 'a2', [qw(ID)]], + '0ACF' => ['master_login', 'a4 Z25 a32 a5', [qw(game_code username password_rijndael flag)]], + '0AE8' => ['change_dress'], + '0AEF' => ['attendance_reward_request'], + '0AF4' => ['skill_use_location', 'v4 C', [qw(lv skillID x y unknown)]], #11 + '0B10' => ['start_skill_use', 'v2 a4', [qw(skillID lv targetID)]], + '0B11' => ['stop_skill_use', 'v', [qw(skillID)]], + '0B14' => ['inventory_expansion_request'], #2 + '0B19' => ['inventory_expansion_rejected'], #2 + '0B1C' => ['ping'], #2 + '0B21' => ['hotkey_change', 'v2 C V v', [qw(tab idx type id lvl)]], + '0C23' => ['send_otp_login', 'a6 C', [qw(otp padding)]], + '0841' => ['select_accessible_mapname', 'C C', [qw(char_slot map_slot)]], + '0BAF' => ['use_packageitem', 'v a4 V V', [qw(index accountID itemID boxIndex)]], + ); + $self->{packet_list}{$_} = $packets{$_} for keys %packets; + + # # it would automatically use the first available if not set + # my %handlers = qw( + # master_login 0064 + # game_login 0065 + # map_login 0072 + # character_move 0085 + # buy_bulk_vender 0134 + # ); + # $self->{packet_lut}{$_} = $handlers{$_} for keys %handlers; + + return $self; +} + +sub shuffle { + my ( $self ) = @_; + + my %shuffle; + my $load_shuffle = Settings::addTableFile( 'shuffle.txt', loader => [ \&FileParsers::parseDataFile2, \%shuffle ], mustExist => 0 ); + Settings::loadByHandle( $load_shuffle ); + Settings::removeFile( $load_shuffle ); + + # Build the list of changes. Be careful to handle swaps correctly. + my $new = {}; + foreach ( sort keys %shuffle ) { + # We can only patch packets we know about. + next if !$self->{packet_list}->{$_}; + # Ignore changes to packets which aren't used by this server. + my $handler = $self->{packet_list}->{$_}->[0]; + next if $self->{packet_lut}->{$handler} && $self->{packet_lut}->{$handler} ne $_; + $new->{ $shuffle{$_} } = $self->{packet_list}->{$_}; + } + + # Patch! + $self->{packet_list}->{$_} = $new->{$_} foreach keys %$new; + $self->{packet_lut}->{ $new->{$_}->[0] } = $_ foreach keys %$new; +} + +sub version { + return $masterServer->{version} || 1; +} + +# 0x0089,7,actionrequest,2:6 + +sub sendAttackStop { + my $self = shift; + #my $msg = pack("C*", 0x18, 0x01); + # Apparently this packet is wrong. The server disconnects us if we do this. + # Sending a move command to the current position seems to be able to emulate + # what this function is supposed to do. + + # Don't use this function, use Misc::stopAttack() instead! + #sendMove ($char->{'pos_to'}{'x'}, $char->{'pos_to'}{'y'}); + #debug "Sent stop attack\n", "sendPacket"; +} + +=pod +sub sendGuildMemberTitleSelect { + # set the title for a member + my ($self, $accountID, $charID, $index) = @_; + + my $msg = pack("C*", 0x55, 0x01).pack("v1",16).$accountID.$charID.pack("V1",$index); + $self->sendToServer($msg); + debug "Sent Change Guild title: ".getHex($charID)." $index\n", "sendPacket", 2; +} +=cut +# 0x0155,-1,guildchangememberposition,2 +sub sendGuildMemberPositions { + my ($self, $r_array) = @_; + my $msg = pack('v2', 0x0155, 4+12*@{$r_array}); + for (my $i = 0; $i < @{$r_array}; $i++) { + $msg .= pack('a4 a4 V', $r_array->[$i]{accountID}, $r_array->[$i]{charID}, $r_array->[$i]{index}); + debug "Sent GuildChangeMemberPositions: $r_array->[$i]{accountID} $r_array->[$i]{charID} $r_array->[$i]{index}\n", "d_sendPacket", 2; + } + $self->sendToServer($msg); +} + +=pod +sub sendGuildRankChange { + # change the title for a certain index + # i would guess 0 is the top rank, but i dont know + my ($self, $index, $permissions, $tax, $title) = @_; + + my $msg = pack("C*", 0x61, 0x01) . + pack("v1", 44) . # packet length, we can actually send multiple titles in the same packet if we wanted to + pack("V1", $index) . # index of this rank in the list + pack("V1", $permissions) . # this is their abilities, not sure what format + pack("V1", $index) . # isnt even used on emulators, but leave in case Aegis wants this + pack("V1", $tax) . # guild tax amount, not sure what format + pack("a24", $title); + $self->sendToServer($msg); + debug "Sent Set Guild title: $index $title\n", "sendPacket", 2; +} +=cut +# 0x0161,-1,guildchangepositioninfo,2 +sub sendGuildPositionInfo { + my ($self, $r_array) = @_; + my $msg = pack('v2', 0x0161, 4+44*@{$r_array}); + for (my $i = 0; $i < @{$r_array}; $i++) { + $msg .= pack('v2 V4 a24', $r_array->[$i]{index}, $r_array->[$i]{permissions}, $r_array->[$i]{index}, $r_array->[$i]{tax}, stringToBytes($r_array->[$i]{title})); + debug "Sent GuildPositionInfo: $r_array->[$i]{index}, $r_array->[$i]{permissions}, $r_array->[$i]{index}, $r_array->[$i]{tax}, ".stringToBytes($r_array->[$i]{title})."\n", "d_sendPacket", 2; + } + $self->sendToServer($msg); +} + +sub sendPartyOrganize { + my ($self, $name, $share1, $share2) = @_; + $share1 ||= 1; + $share2 ||= 1; + + # my $msg = pack("C*", 0xF9, 0x00) . pack("Z24", stringToBytes($name)); + # I think this is obsolete - which serverTypes still support this packet anyway? + # FIXME: what are shared with $share1 and $share2? experience? item? vice-versa? + + my $msg = pack("C*", 0xE8, 0x01) . pack("Z24", stringToBytes($name)) . pack("C*", $share1, $share2); + + $self->sendToServer($msg); + debug "Sent Organize Party: $name\n", "sendPacket", 2; +} + +sub sendPreLoginCode { + # no server actually needs this, but we might need it in the future? + my $self = shift; + my $type = shift; + my $msg; + if ($type == 1) { + $msg = pack("C*", 0x04, 0x02, 0x82, 0xD1, 0x2C, 0x91, 0x4F, 0x5A, 0xD4, 0x8F, 0xD9, 0x6F, 0xCF, 0x7E, 0xF4, 0xCC, 0x49, 0x2D); + } + $self->sendToServer($msg); + debug "Sent pre-login packet $type\n", "sendPacket", 2; +} + +sub sendRequestMakingHomunculus { + # WARNING: If you don't really know, what are you doing - don't touch this + my ($self, $make_homun) = @_; + + my $skill = new Skill (idn => 241); + + if ( + Actor::Item::get (997) && Actor::Item::get (998) && Actor::Item::get (999) + && ($char->getSkillLevel ($skill) > 0) + ) { + my $msg = pack ('v C', 0x01CA, $make_homun); + $self->sendToServer($msg); + debug "Sent RequestMakingHomunculus\n", "sendPacket", 2; + } +} + +# 0x0213 has no info on eA + +sub sendMessageIDEncryptionInitialized { + my $self = shift; + my $msg = pack("v", 0x02AF); + $self->sendToServer($msg); + debug "Sent Message ID Encryption Initialized\n", "sendPacket", 2; +} + +# this is different from kRO +sub sendCaptchaInitiate { + my ($self) = @_; + my $msg = pack('v2', 0x07E5, 0x0); + $self->sendToServer($msg); + debug "Sending Captcha Initiate\n"; +} + +1; diff --git a/openkore_llm_knowledge/networking/Network/Send/iRO.pm b/openkore_llm_knowledge/networking/Network/Send/iRO.pm new file mode 100644 index 0000000000..db4e9e96ca --- /dev/null +++ b/openkore_llm_knowledge/networking/Network/Send/iRO.pm @@ -0,0 +1,50 @@ +######################################################################### +# OpenKore - Network subsystem +# This module contains functions for sending messages to the server. +# +# This software is open source, licensed under the GNU General Public +# License, version 2. +# Basically, this means that you're allowed to modify and distribute +# this software. However, if you distribute modified versions, you MUST +# also distribute the source code. +# See http://www.gnu.org/licenses/gpl.html for the full license. +######################################################################### +# International (iRO) + +package Network::Send::iRO; + +use strict; +use base qw(Network::Send::ServerType0); + +sub new { + my ($class) = @_; + my $self = $class->SUPER::new(@_); + + my %handlers = qw( + actor_action 0437 + actor_info_request 0368 + actor_look_at 0361 + buy_bulk_vender 0801 + char_create 0A39 + char_delete2_accept 098F + character_move 035F + item_drop 0363 + item_take 0362 + map_login 0436 + party_setting 07D7 + send_equip 0998 + skill_use 0438 + skill_use_location 0366 + storage_item_add 0364 + storage_item_remove 0365 + sync 0360 + ); + $self->{packet_lut}{$_} = $handlers{$_} for keys %handlers; + + $self->{char_create_version} = 0x0A39; + $self->{send_sell_buy_complete} = 1; + + return $self; +} + +1; diff --git a/openkore_llm_knowledge/networking/Network/Send/kRO.pm b/openkore_llm_knowledge/networking/Network/Send/kRO.pm new file mode 100644 index 0000000000..8e85c0dbe8 --- /dev/null +++ b/openkore_llm_knowledge/networking/Network/Send/kRO.pm @@ -0,0 +1,23 @@ +######################################################################### +# OpenKore - Packet sending +# This module contains functions for sending packets to the server. +# +# This software is open source, licensed under the GNU General Public +# License, version 2. +# Basically, this means that you're allowed to modify and distribute +# this software. However, if you distribute modified versions, you MUST +# also distribute the source code. +# See http://www.gnu.org/licenses/gpl.html for the full license. +# +# $Revision: 6687 $ +# $Id: kRO.pm 6687 2009-04-19 19:04:25Z technologyguild $ +######################################################################## +# Korea (kRO) +# The majority of private servers use eAthena, this is a clone of kRO +package Network::Send::kRO; + +use strict; + +use base 'Network::Send'; + +1; \ No newline at end of file diff --git a/openkore_llm_knowledge/networking/Network/XKore2/AccountServer.pm b/openkore_llm_knowledge/networking/Network/XKore2/AccountServer.pm new file mode 100644 index 0000000000..eb75af4e41 --- /dev/null +++ b/openkore_llm_knowledge/networking/Network/XKore2/AccountServer.pm @@ -0,0 +1,41 @@ +######################################################################### +# OpenKore - X-Kore Mode 2 +# Copyright (c) 2007 OpenKore developers +# +# This software is open source, licensed under the GNU General Public +# License, version 2. +# Basically, this means that you're allowed to modify and distribute +# this software. However, if you distribute modified versions, you MUST +# also distribute the source code. +# See http://www.gnu.org/licenses/gpl.html for the full license. +######################################################################### +## +# MODULE DESCRIPTION: Account server implementation. + +package Network::XKore2::AccountServer; + +use strict; +use Globals; +use Base::Ragnarok::AccountServer; +use base qw(Base::Ragnarok::AccountServer); + +# Overrided method. +sub login { + my ($self, $session, $username, $password_check_callback) = @_; + if ($char) { + $session->{accountID} = $char->{ID}; + $session->{sex} = $char->{sex}; + } else { + $session->{accountID} = pack("V", 123456); + $session->{sex} = 0; + $session->{dummy} = 1; + } + + #return Base::Ragnarok::AccountServer::SERVER_REFUSED unless ($net->getState() == Network::IN_GAME); + return Base::Ragnarok::AccountServer::ACCOUNT_NOT_FOUND unless $config{username} eq $username; + return Base::Ragnarok::AccountServer::PASSWORD_INCORRECT unless $password_check_callback->($config{adminPassword}); + + return Base::Ragnarok::AccountServer::LOGIN_SUCCESS; +} + +1; diff --git a/openkore_llm_knowledge/networking/Network/XKore2/CharServer.pm b/openkore_llm_knowledge/networking/Network/XKore2/CharServer.pm new file mode 100644 index 0000000000..920802f93e --- /dev/null +++ b/openkore_llm_knowledge/networking/Network/XKore2/CharServer.pm @@ -0,0 +1,50 @@ +######################################################################### +# OpenKore - X-Kore Mode 2 +# Copyright (c) 2007 OpenKore developers +# +# This software is open source, licensed under the GNU General Public +# License, version 2. +# Basically, this means that you're allowed to modify and distribute +# this software. However, if you distribute modified versions, you MUST +# also distribute the source code. +# See http://www.gnu.org/licenses/gpl.html for the full license. +######################################################################### +## +# MODULE DESCRIPTION: Character server implementation. + +package Network::XKore2::CharServer; + +use strict; +use Globals qw($char $masterServer); +use Base::Ragnarok::CharServer; +use base qw(Base::Ragnarok::CharServer); + +# Overrided method. +sub getCharacters { + my ($self, $session) = @_; + my @chars; + if (!$session->{dummy} && $char) { + #for (my $i = 0; $i < 5; $i++) { + # push @chars, $char; + #} + push @chars, $char; + } else { + $session->{dummy} = 1; + for (my $i = 0; $i < 5; $i++) { + push @chars, Base::Ragnarok::CharServer::DUMMY_CHARACTER; + } + } + return @chars; +} + +sub charBlockSize { + my ($self) = @_; + if ($masterServer) { + return $masterServer->{charBlockSize} || $self->{charBlockSize} || 106; + } else { + return $self->SUPER::charBlockSize(); + } +} + +1; + diff --git a/openkore_llm_knowledge/networking/Network/XKore2/MapServer.pm b/openkore_llm_knowledge/networking/Network/XKore2/MapServer.pm new file mode 100644 index 0000000000..f93d8a0729 --- /dev/null +++ b/openkore_llm_knowledge/networking/Network/XKore2/MapServer.pm @@ -0,0 +1,866 @@ +######################################################################### +# OpenKore - X-Kore Mode 2 +# Copyright (c) 2007 OpenKore developers +# +# This software is open source, licensed under the GNU General Public +# License, version 2. +# Basically, this means that you're allowed to modify and distribute +# this software. However, if you distribute modified versions, you MUST +# also distribute the source code. +# See http://www.gnu.org/licenses/gpl.html for the full license. +######################################################################### +## +# MODULE DESCRIPTION: Map server implementation. + +package Network::XKore2::MapServer; + +use strict; +use Globals qw( + $char $field %statusHandle @skillsID @itemsID %items + $portalsList $npcsList $monstersList $playersList $petsList + @friendsID %friends %pet @partyUsersID %spells + @chatRoomsID %chatRooms @venderListsID %venderLists $hotkeyList + %config $questList $incomingMessages $masterServer $messageSender + %cashShop +); +use Base::Ragnarok::MapServer; +use base qw(Base::Ragnarok::MapServer); +use Network::MessageTokenizer; +use I18N qw(stringToBytes); +use Utils qw(shiftPack getTickCount getCoordString); + +use Log qw(debug warning error); + +my $RunOnce = 1; + +sub new { + my $class = shift; + my $self = $class->SUPER::new(@_); + debug "XKore 2 Map Server started \n"; + $self->{kore_map_loaded_hook} = Plugins::addHook('packet/map_loaded', \&kore_map_loaded, $self); + $self->{kore_disconnected} = Plugins::addHook('disconnected', \&kore_disconnected, $self); + return $self; +} + +# Overrided method. +sub onClientNew { + my ($self, $client, $index) = @_; + $self->SUPER::onClientNew($client, $index); + if ($messageSender->{encryption}) { # using encryption? + # enable MID encryption. + cryptKeys($client, $messageSender->{encryption}{crypt_key_1}, $messageSender->{encryption}{crypt_key_2}, $messageSender->{encryption}{crypt_key_3}); + } + + # In here we store messages that the RO client wants to + # send to the server. + $client->{outbox} = new Network::MessageTokenizer($self->getRecvPackets()); + push (@{$self->{clients}}, $client); # keep a list of clients + # TODO: remove disconnected clients +} +sub kore_map_loaded { + my (undef, $args, $self) = @_; + foreach my $client (@{$self->{clients}}) { + if ($client->{session}{dummy}) { + $client->send($self->{recvPacketParser}->reconstruct({ + switch => 'map_change', + map => $field->name() . ".gat", + x => $char->{pos_to}{x}, + y => $char->{pos_to}{y}, + })); + $client->{session}{dummy} = 0; + } + } +} + +sub kore_disconnected { + my (undef, $args, $self) = @_; + if (!$config{XKore_silent}) { + foreach my $client (@{$self->{clients}}) { + $client->send($self->{recvPacketParser}->reconstruct({ + switch => 'system_chat', + message => 'OpenKore disconnected, please wait...', + })); + } + } +} + +sub cryptKeys { + my $self = shift; + $self->{encryption} = { + 'crypt_key_1' => Math::BigInt->new(shift), + 'crypt_key_2' => Math::BigInt->new(shift), + 'crypt_key_3' => Math::BigInt->new(shift), + }; + # calculate first key + $self->{encryption}->{crypt_key} = $self->{encryption}->{crypt_key_1}; +} + +sub onClientData { + my ($self, $client, $data) = @_; + my $additional_data; + encryptMessageID($client, \$data); # encrypt MID + #debug sprintf("XKore2 Server: Received packet %s \n", Network::MessageTokenizer::getMessageID($data)), "xkore2_server"; + $data = $client->{tokenizer}->slicePacket($data, \$additional_data); # slice packet if needed + $client->{tokenizer}->add($data, 1); + $client->{outbox} && $client->{outbox}->add($_) for $self->{sendPacketParser}->process( + $client->{tokenizer}, $self, $client + ); + $self->onClientData($client, $additional_data) if $additional_data; +} + +sub gameguard_reply { + my ($self, $args, $client) = @_; + if ($masterServer->{gameGuard} == 0) { + debug("Replying XKore 2's gameguard query"); + } else { + # mangle, may be unsafe + $args->{mangle} = 2; + } +} + +sub npc_talk_continue { + my ($self, $args, $client) = @_; + # TODO: Mangle every npc_talk packet during talkNPC sequences + $args->{mangle} = 2 if ($config{autoTalkCont}); +} + +# Overrided method. +sub getCharInfo { + my ($self, $session) = @_; + if ($char && $field && !$session->{dummy}) { + return { + map => $field->name() . ".gat", + x => $char->{pos_to}{x}, + y => $char->{pos_to}{y} + }; + } else { + $session->{dummy} = 1; + return Base::Ragnarok::MapServer::DUMMY_POSITION; + } +} + +sub encryptMessageID { + my ($self, $r_message) = @_; + if ($self->{encryption}) { + my $messageID = unpack("v", $$r_message); + # by Fr3DBr + # Calculating the Encryption Key + $self->{encryption}->{crypt_key} = ($self->{encryption}->{crypt_key} * $self->{encryption}->{crypt_key_3} + $self->{encryption}->{crypt_key_2}) & 0xFFFFFFFF; + # Xoring the Message ID + $messageID = ($messageID ^ (($self->{encryption}->{crypt_key} >> 16) & 0x7FFF)) & 0xFFFF; + $$r_message = pack("v", $messageID) . substr($$r_message, 2); + } +} + +sub map_loaded { + # The RO client has finished loading the map. + # Send character information to the RO client. + my ($self, $args, $client) = @_; + no encoding 'utf8'; + use bytes; + + my $char; + # TODO: Character vending, character in chat, character in deal + # TODO: Cart Items, Guild Notice + # TODO: Fix walking speed? Might that be part of the map login packet? Or 00BD? + + if (!$client->{session}) { + $client->close(); + return; + } elsif ($client->{session}{dummy}) { + $char = Base::Ragnarok::CharServer::DUMMY_CHARACTER; + } elsif ($Globals::char) { + $char = $Globals::char; + } else { + $char = Base::Ragnarok::CharServer::DUMMY_CHARACTER; + $client->{session}{dummy} = 1; + } + # Do this just in case $client->{session}{dummy} was set after + # the user logs in. + $char->{ID} = $client->{session}{accountID}; + $self->send_player_info($client, $char); + $self->send_avoid_sprite_error_hack($client, $char); + $self->send_npc_info($client); + $self->send_inventory($client, $char); + $self->send_ground_items($client); + $self->send_portals($client); + $self->send_npcs($client); + $self->send_monsters($client); + $self->send_npcs($client); + $self->send_pets($client); + $self->send_vendors($client); + $self->send_chatrooms($client); + $self->send_ground_skills($client); + $self->send_friends_list($client); + $self->send_party_list($client, $char); + $self->send_pet($client); + $self->send_welcome($client); + + $args->{mangle} = 2; + $RunOnce = 0; +} + +sub send_quest_info { + my ($self, $client) = @_; + my $data = undef; + my $q_output = ''; + my $m_output = ''; + my $tmp = ''; + my $k = 0; + my $mi = 0; + foreach my $questID (keys %{$questList}) { + my $quest = $questList->{$questID}; + $q_output .= pack('V C', $questID, $quest->{active}); + + # misson info + $tmp = ''; + $mi = 0; + foreach my $mobID (keys %{$quest->{missions}}) { + my $mission = $quest->{missions}->{$mobID}; + $tmp = pack('V v Z24', $mission->{mob_id}, $mission->{mob_count}, $mission->{mob_name_original}); + $mi++; + } + $m_output .= pack('V3 v a90', $questID, $quest->{time_start}, $quest->{time_expire}, $mi, $tmp); + $k++; + } + if ($k > 0 && length($q_output) > 0) { + $data = pack('C2 v V', 0xB1, 0x02, length($q_output) + 8, $k) . $q_output; + $data .= pack('C2 v V', 0xB2, 0x02, length($m_output) + 8, $k) . $m_output; + $client->send($data); + } +} + +sub send_guild_info { + my ($self, $client) = @_; + my $data = undef; + if ($char->{guildID}) { + $data = pack('C2 V3 x5 Z24', 0x6C, 0x01, + $char->{guildID}, $char->{guild}{emblem}, $char->{guild}{mode}, + stringToBytes($char->{guild}{name})); + $client->send($data); + } +} + +sub send_pet { + my ($self, $client) = @_; + my $data = undef; + if (defined $pet{ID}) { + $data = pack('C2 C a4 V', 0xA4, 0x01, 0, $pet{ID}, 0); + $data .= pack('C2 C a4 V', 0xA4, 0x01, 5, $pet{ID}, 0x64); + $data .= pack('C2 Z24 C v4', 0xA2, 0x01, + stringToBytes($pet{name}), $pet{renameflag}, $pet{level}, + $pet{hungry}, $pet{friendly}, $pet{accessory}); + $client->send($data); + } +} +sub send_party_list { + my ($self, $client, $char) = @_; + my $data = undef; + if ($char->{party}{joined}) { + my $num = 0; + foreach my $ID (@partyUsersID) { + next if !defined($ID) || !$char->{party}{users}{$ID}; + if (!$char->{party}{users}{$ID}{admin}) { + $num++; + } + $data .= pack("a4 Z24 Z16 C2", + $ID, stringToBytes($char->{party}{users}{$ID}{name}), + $char->{party}{users}{$ID}{map}, + $char->{party}{users}{$ID}{admin} ? 0 : $num, + 1 - $char->{party}{users}{$ID}{online}); + } + $data = pack('C2 v Z24', 0xFB, 0x00, + length($data) + 28, + stringToBytes($char->{party}{name})) . + $data; + $client->send($data); + } +} + +sub send_vendors { + my ($self, $client) = @_; + my $data = undef; + foreach my $ID (@venderListsID) { + next if !defined($ID) || !$venderLists{$ID}; + $data .= pack('C2 a4 a30 x50', 0x31, 0x01, $ID, stringToBytes($venderLists{$ID}{title})); + } + $client->send($data) if (length($data) > 0); +} + +sub send_chatrooms { + my ($self, $client) = @_; + my $data = undef; + foreach my $ID (@chatRoomsID) { + next if !defined($ID) || !$chatRooms{$ID} || !$chatRooms{$ID}{ownerID}; + # '00D7' => ['chat_info', 'x2 a4 a4 v1 v1 C1 a*', [qw(ownerID ID limit num_users public title)]], + my $chatMsg = pack('a4 a4 v2 C1 a* x1', + $chatRooms{$ID}{ownerID}, $ID, $chatRooms{$ID}{limit}, + $chatRooms{$ID}{num_users}, $chatRooms{$ID}{public}, + stringToBytes($chatRooms{$ID}{title})); + $data .= pack('C2 v', 0xD7, 0x00, length($chatMsg) + 4) . $chatMsg; + } + $client->send($data) if (length($data) > 0); +} + +sub send_ground_skills { + my ($self, $client) = @_; + my $data = undef; + foreach my $ID (@skillsID) { + next if !defined($ID) || !$spells{$ID}; + $data .= pack('C2 a4 a4 v2 C2 x81', 0xC9, 0x01, + $ID, $spells{$ID}{sourceID}, + $spells{$ID}{pos}{x}, $spells{$ID}{pos}{y}, $spells{$ID}{type}, + $spells{$ID}{fail}); + } + $client->send($data) if (length($data) > 0); +} +sub send_friends_list { + my ($self, $client) = @_; + my $data = undef; + my ($friendMsg, $friendOnlineMsg); + foreach my $ID (@friendsID) { + next if !defined($ID) || !$friends{$ID}; + $friendMsg .= pack('a4 a4 Z24', + $friends{$ID}{accountID}, + $friends{$ID}{charID}, + stringToBytes($friends{$ID}{name})); + if ($friends{$ID}{online}) { + $friendOnlineMsg .= pack('C2 a4 a4 C', + 0x06, 0x02, + $friends{$ID}{accountID}, + $friends{$ID}{charID}, + 0); + }; + } + $data = pack('C2 v', 0x01, 0x02, length($friendMsg) + 4) . $friendMsg; + $client->send($data); + $client->send($friendOnlineMsg); + undef $friendMsg; + undef $friendOnlineMsg; +} +sub send_pets { + my ($self, $client) = @_; + my $data = undef; + foreach my $pet (@{$petsList->getItems()}) { + my $coords = ''; + shiftPack(\$coords, $pet->{pos_to}{x}, 10); + shiftPack(\$coords, $pet->{pos_to}{y}, 10); + shiftPack(\$coords, $pet->{look}{body}, 4); + $data .= $self->{recvPacketParser}->reconstruct({ + switch => 'actor_exists', + walk_speed => $pet->{walk_speed} * 1000, + coords => $coords, + map { $_ => $pet->{$_} } qw(object_type ID type hair_style lv) + }); + } + $client->send($data) if (length($data) > 0); +} + +sub send_monsters { + my ($self, $client) = @_; + my $data = undef; + foreach my $monster (@{$monstersList->getItems()}) { + my $coords = ''; + shiftPack(\$coords, $monster->{pos_to}{x}, 10); + shiftPack(\$coords, $monster->{pos_to}{y}, 10); + shiftPack(\$coords, $monster->{look}{body}, 4); + $data .= $self->{recvPacketParser}->reconstruct({ + switch => 'actor_exists', + walk_speed => $monster->{walk_speed} * 1000, + coords => $coords, + map { $_ => $monster->{$_} } qw(object_type ID opt1 opt2 option type lv) + }); + } + $client->send($data) if (length($data) > 0); +} + +sub send_npcs { + my ($self, $client) = @_; + my $data = undef; + foreach my $npc (@{$npcsList->getItems()}) { + my $coords = ''; + shiftPack(\$coords, $npc->{pos}{x}, 10); + shiftPack(\$coords, $npc->{pos}{y}, 10); + shiftPack(\$coords, $npc->{look}{body}, 4); + $data .= $self->{recvPacketParser}->reconstruct({ + switch => 'actor_exists', + coords => $coords, + map { $_ => $npc->{$_} } qw(object_type ID opt1 opt2 option type) + }); + } + $client->send($data) if (length($data) > 0); +} + +sub send_portals { + my ($self, $client) = @_; + # Send portals info + my $data = undef; + my $switch = ($masterServer->{serverType} eq 'bRO')?'0857':'actor_exists'; + foreach my $portal (@{$portalsList->getItems()}) { + my $coords = ''; + shiftPack(\$coords, $portal->{pos}{x}, 10); + shiftPack(\$coords, $portal->{pos}{y}, 10); + shiftPack(\$coords, 0, 4); + $data .= $self->{recvPacketParser}->reconstruct({ + switch => $switch, + coords => $coords, + map { $_ => $portal->{$_} } qw(object_type ID type) + }); + } + $client->send($data) if (length($data) > 0); +} + +sub send_ground_items { + my ($self, $client) = @_; + # Send info about items on the ground + my $data = undef; + foreach my $ID (@itemsID) { + next if !defined($ID) || !$items{$ID}; + $data .= pack('C2 a4 v x v3 x2', 0x9D, 0x00, + $ID, $items{$ID}{nameID}, + $items{$ID}{pos}{x}, $items{$ID}{pos}{y}, $items{$ID}{amount}); + } + $client->send($data) if (length($data) > 0); +} + +sub send_sit { + my ($self, $client) = @_; + # '08C8' => ['actor_action', 'a4 a4 a4 V3 x v C V', [qw(sourceID targetID tick src_speed dst_speed damage div type dual_wield_damage)]], + $client->send($self->{recvPacketParser}->reconstruct({ + switch => 'actor_action', + sourceID => $client->{session}{accountID}, + targetID => pack("V", 0), + tick => pack("V", 9999), + src_speed => 0, + dst_speed => 0, + damage => 0, + div => 0, + type => Network::PacketParser::ACTION_SIT, + dual_wield_damage => 0 + })); +} + +sub send_welcome { + my ($self, $client) = @_; + if (!$config{XKore_silent}) { + $client->send($self->{recvPacketParser}->reconstruct({ + switch => 'system_chat', + message => $Settings::welcomeText, + })); + } +} + +sub send_player_info { + my ($self, $client, $char) = @_; + my $data = undef; + + # Player stats. + $data = pack('C2 v1 C12 v12 x4', + 0xBD, 0x00, + $char->{points_free}, + + $char->{str}, $char->{points_str}, $char->{agi}, $char->{points_agi}, + $char->{vit}, $char->{points_vit}, $char->{int}, $char->{points_int}, $char->{dex}, + $char->{points_dex}, $char->{luk}, $char->{points_luk}, + + $char->{attack}, $char->{attack_bonus}, + $char->{attack_magic_min}, $char->{attack_magic_max}, + $char->{def}, $char->{def_bonus}, + $char->{def_magic}, $char->{def_magic_bonus}, + $char->{hit}, + $char->{flee}, + $char->{flee_bonus}, $char->{critical} + ); + $client->send($data); + + # More stats + $data = pack('C2 v V', 0xB0, 0x00, 0, $char->{walk_speed} * 1000); # Walk speed + $data .= pack('C2 v V', 0xB0, 0x00, 5, $char->{hp}); # Current HP + $data .= pack('C2 v V', 0xB0, 0x00, 6, $char->{hp_max}); # Max HP + $data .= pack('C2 v V', 0xB0, 0x00, 7, $char->{sp}); # Current SP + $data .= pack('C2 v V', 0xB0, 0x00, 8, $char->{sp_max}); # Max SP + $data .= pack('C2 v V', 0xB0, 0x00, 12, $char->{points_skill}); # Skill points left + $data .= pack('C2 v V', 0xB0, 0x00, 24, $char->{weight} * 10); # Current weight + $data .= pack('C2 v V', 0xB0, 0x00, 25, $char->{weight_max} * 10); # Max weight + $data .= pack('C2 v V', 0xB0, 0x00, 53, $char->{attack_delay}); # Attack speed + $client->send($data); + + # Base stat info (str, agi, vit, int, dex, luk) this time with bonus + $data = pack('C2 V3', 0x41, 0x01, 13, $char->{str}, $char->{str_bonus}); + $data .= pack('C2 V3', 0x41, 0x01, 14, $char->{agi}, $char->{agi_bonus}); + $data .= pack('C2 V3', 0x41, 0x01, 15, $char->{vit}, $char->{vit_bonus}); + $data .= pack('C2 V3', 0x41, 0x01, 16, $char->{int}, $char->{int_bonus}); + $data .= pack('C2 V3', 0x41, 0x01, 17, $char->{dex}, $char->{dex_bonus}); + $data .= pack('C2 V3', 0x41, 0x01, 18, $char->{luk}, $char->{luk_bonus}); + $client->send($data); + + # # Make the character face the correct direction + $client->send(pack('C2 a4 C1 x1 C1', 0x9C, 0x00, + $char->{ID}, $char->{look}{head}, $char->{look}{body}) + ); + + # Send attack range + $data = pack('C2 v', 0x3A, 0x01, $char->{attack_range}); + # Send weapon/shield appearance + $data .= pack('C2 a4 C v2', 0xD7, 0x01, $char->{ID}, 2, $char->{weapon}, $char->{shield}); + # Send status info + $data .= pack('v a4 v3 x', 0x119, $char->{ID}, $char->{opt1}, $char->{opt2}, $char->{option}); + $client->send($data); + + if ($RunOnce) { + foreach my $ID (keys %{$char->{statuses}}) { + while (my ($statusID, $statusName) = each %statusHandle) { + if ($ID eq $statusName) { +# $data .= pack('C2 v a4 C', 0x96, 0x01, $statusID, $char->{ID}, 1); + if ($statusID == 673) { + # for Cart active + $data .= pack('C2 v a4 C V4', 0x3F, 0x04, $statusID, $char->{ID}, 1, 9999, $char->cart->type, 0, 0); + } elsif ($statusID == 622) { + # sit + $data .= pack('C2 v a4 C V4', 0x3F, 0x04, $statusID, $char->{ID}, 1, 9999, 1, 0, 0); + $self->send_sit($client); + } else { + $data .= pack('C2 v a4 C', 0x96, 0x01, $statusID, $char->{ID}, 1); + } + } + } + } + } + + $client->send($data) if (length($data) > 0); + + # Send spirit sphere information + $data = pack('C2 a4 v', 0xD0, 0x01, $char->{ID}, $char->{spirits}) if ($char->{spirits}); + # Send exp-required-to-level-up info + $data .= pack('C2 v V', 0xB1, 0x00, 22, $char->{exp_max}); + $data .= pack('C2 v V', 0xB1, 0x00, 23, $char->{exp_job_max}); + $client->send($data); + + # Send skill information + $data = undef; + foreach my $ID (@skillsID) { + $data .= pack('v2 x2 v3 a24 C', + $char->{skills}{$ID}{ID}, $char->{skills}{$ID}{targetType}, + $char->{skills}{$ID}{lv}, $char->{skills}{$ID}{sp}, + $char->{skills}{$ID}{range}, $ID, $char->{skills}{$ID}{up}); + } + $data = pack('C2 v', 0x0F, 0x01, length($data) + 4) . $data; + $client->send($data); + + # Send Hotkeys + if ($hotkeyList) { + $data = undef; + if(@{$hotkeyList} <= 28) { # todo: there is also 07D9,254 + $data .= pack('v', 0x02B9); # old interface (28 hotkeys) + } else { + $data .= pack('v', 0x07D9); # renewal interface as of: RagexeRE_2009_06_10a (38 hotkeys) + } + for (my $i = 0; $i < @{$hotkeyList}; $i++) { + $data .= pack('C V v', $hotkeyList->[$i]->{type}, $hotkeyList->[$i]->{ID}, $hotkeyList->[$i]->{lv}); + } + $client->send($data) if (@{$hotkeyList}); + } + + # Send info about items on the ground + $data = undef; + foreach my $ID (@itemsID) { + next if !defined($ID) || !$items{$ID}; + $data .= pack('C2 a4 v x v3 x2', 0x9D, 0x00, + $ID, $items{$ID}{nameID}, + $items{$ID}{pos}{x}, $items{$ID}{pos}{y}, $items{$ID}{amount}); + } + $client->send($data) if (length($data) > 0); + + + # # Send info about surrounding players + foreach my $player (@{$playersList->getItems()}) { + my $coords = ''; + shiftPack(\$coords, $player->{pos_to}{x}, 10); + shiftPack(\$coords, $player->{pos_to}{y}, 10); + shiftPack(\$coords, $player->{look}{body}, 4); + $data .= pack('C2 a4 v4 x2 v8 x2 v a4 a4 v x2 C2 a3 x2 C v', + 0x2A, 0x02, $player->{ID}, $player->{walk_speed} * 1000, + $player->{opt1}, $player->{opt2}, $player->{option}, + $player->{jobID}, $player->{hair_style}, $player->{weapon}, $player->{shield}, + $player->{headgear}{low}, $player->{headgear}{top}, $player->{headgear}{mid}, + $player->{hair_color}, $player->{look}{head}, $player->{guildID}, $player->{emblemID}, + $player->{opt3}, $player->{stance}, $player->{sex}, $coords, + ($player->{dead}? 1 : ($player->{sitting}? 2 : 0)), $player->{lv}); + } + $client->send($data) if (length($data) > 0); +} + +sub send_inventory { + my ($self, $client, $char) = @_; + my $data = undef; + # Send cart information includeing the items + if (!$client->{session}{dummy} && $char->cartActive && $RunOnce) { + $data = pack('C2 v2 V2', 0x21, 0x01, $char->cart->items, $char->cart->items_max, ($char->cart->{weight} * 10), ($char->cart->{weight_max} * 10)); + $client->send($data); + + my @stackable; + my @nonstackable; + my $n = 0; + my $i = 0; + foreach my $item (@{$char->cart->getItems()}) { + $item->{ID} = $i++; + if ($item->{type} <= 3 || $item->{type} == 6 || $item->{type} == 10 || $item->{type} == 16 || $item->{type} == 17 || $item->{type} == 19) { + push @stackable, $item; + } else { + push @nonstackable, $item; + } + } + + # Send stackable item information + $data = undef; + $n = 0; + foreach my $item (@stackable) { + $data .= pack('a2 v C2 v2 a8 l', + $item->{ID}, + $item->{nameID}, + $item->{type}, + $item->{identified}, # identified + $item->{amount}, + $item->{type_equip}, + $item->{cards}, + $item->{expire}, + ); + $n++; + } + $data = pack('C2 v', 0xE9, 0x02, length($data) + 4) . $data; + $client->send($data) if ($n > 0); + + # Send non-stackable item information + $data = undef; + $n = 0; + foreach my $item (@nonstackable) { + $data .= pack('a2 v C2 v2 C2 a8 l v2', + $item->{ID}, + $item->{nameID}, + $item->{type}, + $item->{identified}, # identified + $item->{type_equip}, + $item->{equipped}, + $item->{broken}, + $item->{upgrade}, + $item->{cards}, + $item->{expire}, + $item->{bindOnEquipType}, + $item->{sprite_id}, + ); + $n++; + } + $data = pack('C2 v', 0xD2, 0x02, length($data) + 4) . $data; + $client->send($data) if ($n > 0); + } + + # Sort items into stackable and non-stackable + if (UNIVERSAL::isa($char, 'Actor::You')) { + my @stackable; + my @nonstackable; + foreach my $item (@{$char->inventory->getItems()}) { + if ($item->{type} <= 3 || $item->{type} == 6 || $item->{type} == 10 || $item->{type} == 16 || $item->{type} == 17 || $item->{type} == 19) { + push @stackable, $item; + } else { + push @nonstackable, $item; + } + } + + # Send stackable item information + $data = undef; + foreach my $item (@stackable) { + $data .= pack('a2 v C2 v1 x2', + $item->{ID}, + $item->{nameID}, + $item->{type}, + 1, # identified + $item->{amount} + ); + } + $data = pack('C2 v', 0xA3, 0x00, length($data) + 4) . $data; + $client->send($data); + + # Send non-stackable item (mostly equipment) information + $data = undef; + foreach my $item (@nonstackable) { + $data .= pack('a2 v C2 v2 C2 a8', + $item->{ID}, $item->{nameID}, $item->{type}, + $item->{identified}, $item->{type_equip}, $item->{equipped}, $item->{broken}, + $item->{upgrade}, $item->{cards}); + } + $data = pack('C2 v', 0xA4, 0x00, length($data) + 4) . $data; + $client->send($data); + } + + # Send equipped arrow information + $client->send(pack('C2 v', 0x3C, 0x01, $char->{arrow})) if ($char->{arrow}); +} + +sub send_npc_info { + my ($self, $client) = @_; + my $data = undef; + my $switch = ($masterServer->{serverType} eq 'bRO')?'0857':'actor_exists'; + + foreach my $npc (@{$npcsList->getItems()}) { + my $coords = ''; + shiftPack(\$coords, $npc->{pos}{x}, 10); + shiftPack(\$coords, $npc->{pos}{y}, 10); + shiftPack(\$coords, $npc->{look}{body}, 4); + $data .= $self->{recvPacketParser}->reconstruct({ + switch => $switch, + coords => $coords, + map { $_ => $npc->{$_} } qw(object_type ID opt1 opt2 option type) + }); + } + $client->send($data) if (length($data) > 0); +} + +sub send_avoid_sprite_error_hack { + my ($self, $client, $char) = @_; + my $data = pack('C15', 0x29, 0x02, 0xA7, 0x94, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40); + $client->send($data); + $data = $self->{recvPacketParser}->reconstruct({ + switch => '0229', + ID => $char->{ID}, + opt1 => $char->{opt1}, + opt2 => $char->{opt2}, + option => $char->{option}, + stance => $char->{stance} + }); + $client->send($data); +} + +sub map_login { + my ($self, $args, $client) = @_; + # maybe sessionstore should store sessionID as bytes? + my $session = $self->{sessionStore}->get(unpack('V', $args->{sessionID})); + + unless ( + $session && $session->{accountID} eq $args->{accountID} + # maybe sessionstore should store sessionID as bytes? + && pack('V', $session->{sessionID}) eq $args->{sessionID} + && $session->{sex} == $args->{sex} + && $session->{charID} eq $args->{charID} + && $session->{state} eq 'About to load map' + ) { + $client->close(); + + } else { + $self->{sessionStore}->remove($session); + $client->{session} = $session; + + if (exists $self->{recvPacketParser}{packet_lut}{define_check}) { + $client->send($self->{recvPacketParser}->reconstruct({ + switch => 'define_check', + result => Network::Receive::ServerType0::DEFINE__BROADCASTING_SPECIAL_ITEM_OBTAIN | Network::Receive::ServerType0::DEFINE__RENEWAL_ADD_2, + })); + } + + if (exists $self->{recvPacketParser}{packet_lut}{account_id}) { + $client->send($self->{recvPacketParser}->reconstruct({ + switch => 'account_id', + accountID => $args->{accountID}, + })); + } elsif ($masterServer->{serverType} eq 'iRO_Classic') { + $client->send($args->{accountID}); + } else { + # BUGGY $client->send($args->{accountID}); + } + + my $charInfo = $self->getCharInfo($session); + my $coords = ''; + shiftPack(\$coords, $charInfo->{x}, 10); + shiftPack(\$coords, $charInfo->{y}, 10); + shiftPack(\$coords, 0, 4); + $client->send($self->{recvPacketParser}->reconstruct({ + switch => 'map_loaded', + syncMapSync => int time, + coords => $coords, + })); + } + + $args->{mangle} = 2; +} + +sub restart { + my ($self, $args, $client) = @_; + # If they want to character select/respawn, kick them to the login screen + # immediately (GM kick) + $client->send(pack('C3', 0x81, 0, 15)); + + $args->{mangle} = 2; + $RunOnce = 1; +} + +sub quit_request { + my ($self, $args, $client) = @_; + # Client wants to quit + $client->send(pack('C*', 0x8B, 0x01, 0, 0)); + $args->{mangle} = 2; + $RunOnce = 1; +} + +sub sync { + my ($self, $args, $client) = @_; + # my $data = $self->{recvPacketParser}->reconstruct({ + # switch => 'received_sync', + # time => getTickCount + # }); + #$client->send($data); + $args->{mangle} = 2; +} + +sub request_cashitems { + my ($self, $args, $client) = @_; + $self->send_cash_list($client); + $args->{mangle} = 2; +} + +sub send_cash_list { + my ($self, $client) = @_; + # '08CA' => ['cashitem', 'v3 a*', [qw(len amount tabcode itemInfo)]],#-1 + return unless defined $cashShop{list}; + my $pack_string = "v V"; + for (my $tab = 0; $tab < @{$cashShop{list}}; $tab++) { + my $item_block; + foreach my $item (@{$cashShop{list}[$tab]}) { + $item_block .= pack($pack_string, $item->{item_id}, $item->{price}); + $self->send_cash_tab($client, $tab, \$item_block) if (length($item_block) >= (6*64)); + } + # send current tab + # max tab size: 392 total and 384 item_block + $self->send_cash_tab($client, $tab, \$item_block); + } +} + +sub send_cash_tab { + my ($self, $client, $tab_code, $item_block) = @_; + my $data = $self->{recvPacketParser}->reconstruct({ + switch => 'cash_shop_list', + len => 8+length($$item_block), + tabcode => $tab_code, + amount => length($$item_block)/6, + itemInfo => $$item_block + }); + $$item_block = undef; + $client->send($data) if (length($data) > 0); +} + +# Not sure what these are, but don't let it get to the RO server. +sub less_effect { + my ($self, $args, $client) = @_; + $args->{mangle} = 2; +} + +sub guild_check { + my ($self, $args, $client) = @_; + $args->{mangle} = 2; +} + +sub guild_info_request { + my ($self, $args, $client) = @_; + $args->{mangle} = 2; +} + +1; + diff --git a/openkore_llm_knowledge/networking/PacketParser.pm b/openkore_llm_knowledge/networking/PacketParser.pm new file mode 100644 index 0000000000..899e49ea6d --- /dev/null +++ b/openkore_llm_knowledge/networking/PacketParser.pm @@ -0,0 +1,538 @@ +######################################################################### +# OpenKore - Server message parsing +# +# This software is open source, licensed under the GNU General Public +# License, version 2. +# Basically, this means that you're allowed to modify and distribute +# this software. However, if you distribute modified versions, you MUST +# also distribute the source code. +# See http://www.gnu.org/licenses/gpl.html for the full license. +######################################################################### +## +# MODULE DESCRIPTION: Server message parsing +# +# This class is responsible for parsing messages that are sent by the RO +# server to Kore. Information in the messages are stored in global variables +# (in the module Globals). +# +# Please also read <a href="https://openkore.com/wiki/Network_subsystem">the +# network subsystem overview.</a> +package Network::PacketParser; + +use strict; +use utf8; +use base qw(Exporter); +use Carp::Assert; +use Scalar::Util; +use Time::HiRes qw(time); + +use Globals; +#use Settings; +use Log qw(message warning error debug); +#use FileParsers; +use I18N qw(bytesToString stringToBytes); +use Interface; +use Network; +use Network::MessageTokenizer; +use Misc; +use Plugins; +use Utils; +use Utils::Exceptions; +use Utils::Crypton; +use Translation; + +our @EXPORT = qw( + ACTION_ATTACK ACTION_ITEMPICKUP ACTION_SIT ACTION_STAND + ACTION_ATTACK_NOMOTION ACTION_SPLASH ACTION_SKILL ACTION_ATTACK_REPEAT + ACTION_ATTACK_MULTIPLE ACTION_ATTACK_MULTIPLE_NOMOTION + ACTION_ATTACK_CRITICAL ACTION_ATTACK_LUCKY ACTION_TOUCHSKILL + STATUS_STR STATUS_AGI STATUS_VIT STATUS_INT STATUS_DEX STATUS_LUK +); + +### CATEGORY: Ragnarok Online constants + +use constant { + ACTION_ATTACK => 0x0, + ACTION_ITEMPICKUP => 0x1, # pick up item + ACTION_SIT => 0x2, # sit down + ACTION_STAND => 0x3, # stand up + ACTION_ATTACK_NOMOTION => 0x4, # reflected/absorbed damage? + ACTION_SPLASH => 0x5, + ACTION_SKILL => 0x6, + ACTION_ATTACK_REPEAT => 0x7, + ACTION_ATTACK_MULTIPLE => 0x8, # double attack + ACTION_ATTACK_MULTIPLE_NOMOTION => 0x9, # don't display flinch animation (endure) + ACTION_ATTACK_CRITICAL => 0xa, # critical hit + ACTION_ATTACK_LUCKY => 0xb, # lucky dodge + ACTION_TOUCHSKILL => 0xc, + STATUS_STR => 0x0d, + STATUS_AGI => 0x0e, + STATUS_VIT => 0x0f, + STATUS_INT => 0x10, + STATUS_DEX => 0x11, + STATUS_LUK => 0x12, +}; + +### CATEGORY: Hash members + +## +# Hash* {packet_list} +# +# A list of packet handlers and decoding information. +# +# 'packet switch' => ['handler function', 'unpack string', [qw(argument names)]] + +## +# Hash* {packet_lut} +# +# Lookup table for currently used packet switches. +# Used for constructing packets by handler name. +# +# 'handler function' => 'packet switch' + +###################################### +### CATEGORY: Class methods +###################################### + +# Do not call this directly. Use create() instead. +sub new { + my ($class) = @_; + my $self; + + # If you are wondering about those funny strings like 'x2 v1' read http://perldoc.perl.org/functions/pack.html + # and http://perldoc.perl.org/perlpacktut.html + + $self->{packet_list} = {}; + $self->{packet_lut} = {}; + $self->{bytesProcessed} = 0; + + return bless $self, $class; +} + +## +# Network::PacketParser->create(Network net, String serverType) +# net: An object compatible with the '@MODULE(Network)' class. +# serverType: A server type. +# +# Create a new server message parsing object for the specified server type. +# +# Throws FileNotFoundException, ModuleLoadException. +sub create { + my ($base, $net, $serverType) = @_; + + my ($mode, $type, $param) = Settings::parseServerType ($serverType); + my $class = join '::', $base, $type, (($param) || ()); #param like Thor in bRO_Thor + + debug "[$base] $class ". " (mode: " . ($mode ? "new" : "old") .")\n"; + + undef $@; + eval("use $class;"); + if ($@ =~ /^Can't locate /s) { + FileNotFoundException->throw( + TF("Cannot load server message parser for server type '%s'.", $type) + ); + } elsif ($@) { + ModuleLoadException->throw( + TF("An error occured while loading the server message parser for server type '%s':\n%s", + $type, $@) + ); + } + + my $self = $class->new; + $self->shuffle if $self->can( 'shuffle' ); + + $self->{hook_prefix} = $base; + $self->{net} = $net; + $self->{serverType} = $type; # TODO: eliminate {serverType} from there + Modules::register($class); + + return $self; +} + +### CATEGORY: Methods + +## +# Bytes $packetParser->reconstruct(Hash* args) +# +# Reconstructs a raw packet from $args using {packet_list} and {packet_lut}. +# +# $args->{switch} may contain a packet switch or a handler name. +sub reconstruct { + my ($self, $args) = @_; + + my $switch = $args->{switch}; + unless ($switch =~ /^[0-9A-F]{4}$/) { + # lookup by handler name + unless (exists $self->{packet_lut}{$switch}) { + # alternative (if any) isn't set yet, pick the first available + for (sort {$a cmp $b} keys %{$self->{packet_list}}) { + if ($self->{packet_list}{$_} && $self->{packet_list}{$_}[0] eq $switch) { + $self->{packet_lut}{$switch} = $_; + last; + } + } + } + + $switch = $self->{packet_lut}{$switch} || $switch; + } + + unless ($self->{packet_list}{$switch}) { + die "Can't reconstruct unknown packet: $switch"; + } + + my $packet = $self->{packet_list}{$switch}; + my ($name, $packString, $varNames) = @{$packet}; + + if (my $custom_reconstruct = $self->can('reconstruct_'.$name)) { + $self->$custom_reconstruct($args); + } + + if (DEBUG && $config{debugAssertOnNetwork}) { + # check if all values we're going to pack are defined + for (@$varNames) { + assert(defined $args->{$_}, "Argument $_ should be defined for packet $name"); + } + } + + my $packet = pack("v $packString", hex $switch, $packString && @{$args}{@$varNames}); + + if (exists $rpackets{$switch}) { + if ($rpackets{$switch}{length} > 0) { + # fixed length packet, pad/truncate to the correct length + $packet = pack('a'.(0+$rpackets{$switch}{length}), $packet); + } else { + # variable length packet, store its length in the packet + substr($packet, 2, 2) = pack('v', length $packet); + } + } + + return $packet; +} + +## +# Hash* $packetParser->parse(Bytes msg) +# +# Parses a raw packet using {packet_list}. +# +# Result hashref would contain parsed arguments and the following information: +# `l +# - switch: packet switch +# - RAW_MSG: original message passed +# - RAW_MSG_SIZE: length of original message passed +# - KEYS: list of argument names from {packet_list} +# `l` +sub parse { + my ($self, $msg, $handleContainer, @handleArguments) = @_; + + $lastSwitch = Network::MessageTokenizer::getMessageID($msg); + my $handler = $self->{packet_list}{$lastSwitch}; + + unless ($handler) { + unless (existsInList($config{debugPacket_exclude}, $lastSwitch)) { + warning TF("Packet Parser: Unknown switch: %s\n", $lastSwitch); + Misc::visualDump($msg, "<< Received unknown switch") if $config{debugPacket_unparsed}; + } + return undef; + } + + # $handler->[0] may be (re)binded to $switch here for current serverType + # but all the distinct packets need a distinct names for that, even if they share the handler + # like actor_display = actor_exists + actor_connected + actor_moved + # if (DEBUG) { + # unless ($self->{packet_lut}{$handler->[0]} eq $switch) { + # $self->{packet_lut}{$handler->[0]} = $switch; + # if ((grep { $_ && $_->[0] eq $handler->[0] } values %{$self->{packet_list}}) > 1) { + # warning sprintf "Using %s to provide %s\n", $switch, $handler->[0]; + # } + # } + # } + + debug "Received packet: $lastSwitch Handler: $handler->[0]\n", "packetParser", 2; + + # RAW_MSG is the entire message, including packet switch + my %args = ( + switch => $lastSwitch, + RAW_MSG => $msg, + RAW_MSG_SIZE => length($msg), + KEYS => $handler->[2], + ); + if ($handler->[1]) { + @args{@{$handler->[2]}} = unpack("x2 $handler->[1]", $msg); + } + if (my $custom_parse = $self->can('parse_'.$handler->[0])) { + $self->$custom_parse(\%args); + } + + my $callback = $handleContainer->can($handler->[0]); + if ($callback) { + # Hook names can be made more uniform, + # but the ones for Receive must be kept for compatibility anyway. + # TODO: restrict to $Globals::packetParser and $Globals::messageSender? + if ($self->{hook_prefix} eq 'Network::Receive') { + Plugins::callHook("packet_pre/$handler->[0]", \%args); + } else { + Plugins::callHook("$self->{hook_prefix}/packet_pre/$handler->[0]", \%args); + } + Misc::checkValidity("Packet: " . $handler->[0] . " (pre)"); + + # If return is set in a packet_pre handler, the packet will be ignored. + unless($args{return}) { + $handleContainer->$callback(\%args, @handleArguments); + } + + Misc::checkValidity("Packet: " . $handler->[0]); + } else { + $handleContainer->unhandledMessage(\%args, @handleArguments); + } + + if ($self->{hook_prefix} eq 'Network::Receive') { + Plugins::callHook("packet/$handler->[0]", \%args); + } else { + Plugins::callHook("$self->{hook_prefix}/packet/$handler->[0]", \%args); + } + return \%args; +} + +sub unhandledMessage { + my ($self, $args) = @_; + + warning "Packet Parser: Unhandled Packet: $args->{switch} Handler: $self->{packet_list}{$args->{switch}}[0]\n"; + debug ("Unpacked: " . join(', ', @{$args}{@{$args->{KEYS}}}) . "\n"), "packetParser", 2 if $args->{KEYS}; +} + +## +# boolean $packetParser->willMangle(Bytes messageID) +# messageID: a message ID, such as "008A". +# +# Check whether the message with the specified message ID will be mangled. +# If the bot is running in X-Kore mode, then messages that will be mangled will not +# be sent to the RO client. +# +# By default, a message will never be mangled. Plugins can register mangling procedures +# though. This is done by using the following hooks: +# `l +# - "Network::Receive/willMangle" - This hook has arguments 'messageID' (Bytes) and 'name' (String). +# 'name' is a human-readable description of the message, and may be undef. Plugins +# should set the 'return' argument to 1 if they want willMangle() to return 1. +# - "Network::Receive/mangle" - This hook has arguments 'messageArgs' and 'messageName' (the latter may be undef). +# `l` +# The following example demonstrates how this is done: +# <pre class="example"> +# Plugins::addHook("Network::Receive/willMangle", \&willMangle); +# Plugins::addHook("Network::Receive/mangle", \&mangle); +# +# sub willMangle { +# my (undef, $args) = @_; +# if ($args->{messageID} eq '008A') { +# $args->{return} = 1; +# } +# } +# +# sub mangle { +# my (undef, $args) = @_; +# my $message_args = $args->{messageArgs}; +# if ($message_args->{switch} eq '008A') { +# ...Modify $message_args as necessary.... +# $args->{return} = 2; Modify {return} as necessary.... +# } +# } +# </pre> +# +# You can also mangle packets by defining $args->{mangle} in other plugin hooks. The options avalable are: +# `l +# - 0 = no mangle +# - 1 = mangle (change packet and reconstruct) +# - 2 = drop +# `l` +# The following example will drop all public chat messages: +# <pre class="example"> +# Plugins::addHook("packet_pre/public_chat", \&mangleChat); +# +# sub mangleChat +# { +# my(undef, $args) = @_; +# $args->{mangle} = 2; +# } +# </pre> + +sub willMangle { + my ($self, $messageID) = @_; + if (Plugins::hasHook("$self->{hook_prefix}/willMangle")) { + my $packet = $self->{packet_list}{$messageID}; + my $name; + $name = $packet->[0] if ($packet); + + my %args = ( + messageID => $messageID, + name => $name + ); + Plugins::callHook("$self->{hook_prefix}/willMangle", \%args); + return $args{return}; + } else { + return undef; + } +} + +## +# boolean $packetParser->mangle(Array* args) +# +# Calls the appropriate plugin function to mangle the packet, which +# destructively modifies $args. +# Returns false if the packet should be suppressed. +sub mangle { + my ($self, $args) = @_; + + my %hook_args = (messageArgs => $args); + my $entry = $self->{packet_list}{$args->{switch}}; + if ($entry) { + $hook_args{messageName} = $entry->[0]; + } + + Plugins::callHook("$self->{hook_prefix}/mangle", \%hook_args); + return $hook_args{return}; +} + +sub process { + my ($self, $tokenizer, $handleContainer, @handleArguments) = @_; + + my @result; + my $type; + while (my $message = $tokenizer->readNext(\$type)) { + $handleContainer->{bytesProcessed} += length($message); + $handleContainer->{lastPacketTime} = time; + + my $args; + + if ($type == Network::MessageTokenizer::KNOWN_MESSAGE) { + my $switch = Network::MessageTokenizer::getMessageID($message); + + # FIXME? + $self->parse_pre($handleContainer->{hook_prefix}, $switch, $message, \$message); + + my $willMangle = $handleContainer->can('willMangle') && $handleContainer->willMangle($switch); + + if ($args = $self->parse($message, $handleContainer, @handleArguments)) { + if ($willMangle) { + $args->{mangle} = $handleContainer->mangle($args); + } + } else { + $args = { + switch => $switch, + RAW_MSG => $message, + (mangle => 2) x!! $willMangle, + }; + } + + } elsif ($type == Network::MessageTokenizer::ACCOUNT_ID) { + $args = { + RAW_MSG => $message + }; + + } elsif ($type == Network::MessageTokenizer::UNKNOWN_MESSAGE) { + $args = { + switch => Network::MessageTokenizer::getMessageID($message), + RAW_MSG => $message, + # RAW_MSG_SIZE => length($message), + }; + $handleContainer->unknownMessage($args, @handleArguments); + + } else { + die "Packet Tokenizer: Unknown type: $type"; + } + + unless ($args->{mangle}) { + # Packet was not mangled + push @result, $args->{RAW_MSG}; + #$result .= $args->{RAW_MSG}; + } elsif ($args->{mangle} == 1) { + # Packet was mangled + push @result, $self->reconstruct($args); + #$result .= $self->reconstruct($args); + } else { + # Packet was suppressed + } + } + + # If we're running in X-Kore mode, pass messages back to the RO client. + + # It seems like messages can't be just concatenated safely + # (without "use bytes" pragma or messing with unicode stuff) + # http://perldoc.perl.org/perlunicode.html#The-%22Unicode-Bug%22 + return @result; +} + +sub parse_pre { + my ($self, $mode, $switch, $msg, $realMsg) = @_; + my $values = { + 'Network::Receive' => ['<< Received packet:', 'received', 'Recv', 'parseMsg/pre'], + 'Network::ClientReceive' => ['<< Sent by RO client:', 'ro_sent', 'ROSend', 'RO_sendMsg_pre'], + }->{$mode} or return; + my ($title, $config_suffix, $desc_key, $hook) = @$values; + + if ($config{'debugPacket_'.$config_suffix} && !existsInList($config{'debugPacket_exclude'}, $switch) || + $config{'debugPacket_include_dumpMethod'} && existsInList($config{'debugPacket_include'}, $switch)) + { + #my $label = $packetDescriptions{$desc_key}{$switch} ? " - $packetDescriptions{$desc_key}{$switch}" : ''; + my $label = $rpackets{$switch}{function}?" - ".$rpackets{$switch}{function}:($packetDescriptions{$desc_key}{$switch} ? " - $packetDescriptions{$desc_key}{$switch}" : ''); + if ($config{'debugPacket_'.$config_suffix} == 1) { + debug sprintf("%-24s %-4s%s [%2d bytes]%s\n", $title, $switch, $label, length($msg)), 'parseMsg', 0; + } elsif ($config{'debugPacket_'.$config_suffix} == 2) { + Misc::visualDump($msg, sprintf('%-24s %-4s%s', $title, $switch, $label)); + } + if ($config{debugPacket_include_dumpMethod} == 1) { + debug sprintf("%-24s %-4s%s\n", $title, $switch, $label), "parseMsg", 0; + } elsif ($config{debugPacket_include_dumpMethod} == 2) { + Misc::visualDump($msg, sprintf('%-24s %-4s%s', $title, $switch, $label)); + } elsif ($config{debugPacket_include_dumpMethod} == 3) { + Misc::dumpData($msg, 1); + } elsif ($config{debugPacket_include_dumpMethod} == 4) { + open my $dump, '>>', 'DUMP_LINE.txt'; + print $dump unpack('H*', $msg) . "\n"; + } elsif ($config{debugPacket_include_dumpMethod} == 5) { + open my $dump, '>>', 'DUMP_HEAD.txt'; + print $dump sprintf("%-4s %2d %s%s\n", $switch, length($msg), $desc_key, $label); + } + } + + Plugins::callHook($hook, { + switch => $switch, + msg => $msg, + msg_size => length($msg), + realMsg => $realMsg + }); +} + +sub unknownMessage { + my ($self, $args) = @_; + + # Unknown message - ignore it + unless (existsInList($config{debugPacket_exclude}, $args->{switch})) { + warning TF("Packet Tokenizer: Unknown switch: %s\n", $args->{switch}), 'connection'; + Misc::visualDump($args->{RAW_MSG}, "<< Received unknown packet") if $config{debugPacket_unparsed}; + } + + # Pass it along to the client, whatever it is +} + +# Utility methods used by both Receive and Send + +sub parseChat { + my ($self, $args) = @_; + $args->{message} = bytesToString($args->{message}); + if ($args->{message} =~ /^(.*?)\s{1,2}:\s{1,2}(.*)$/) { + $args->{name} = $1; + $args->{message} = $2; + Misc::stripLanguageCode(\$args->{message}); + } + if (exists $args->{ID}) { + $args->{actor} = Actor::get($args->{ID}); + } +} + +sub reconstructChat { + my ($self, $args) = @_; + $args->{message} = '|00' . $args->{message} if $masterServer->{chatLangCode}; + $args->{message} = stringToBytes($char->{name}) . ' : ' . stringToBytes($args->{message}); +} + +1; diff --git a/openkore_llm_knowledge/networking/Receive.pm b/openkore_llm_knowledge/networking/Receive.pm new file mode 100644 index 0000000000..a933913e86 --- /dev/null +++ b/openkore_llm_knowledge/networking/Receive.pm @@ -0,0 +1,12561 @@ +######################################################################### +# OpenKore - Server message parsing +# +# This software is open source, licensed under the GNU General Public +# License, version 2. +# Basically, this means that you're allowed to modify and distribute +# this software. However, if you distribute modified versions, you MUST +# also distribute the source code. +# See http://www.gnu.org/licenses/gpl.html for the full license. +######################################################################### +## +# MODULE DESCRIPTION: Server message parsing +# +# This class is responsible for parsing messages that are sent by the RO +# server to Kore. Information in the messages are stored in global variables +# (in the module Globals). +# +# Please also read <a href="https://openkore.com/wiki/Network_subsystem">the +# network subsystem overview.</a> +package Network::Receive; + +use strict; +use Time::HiRes qw(time); +use Exporter; +use Network::PacketParser; # import +use base qw(Network::PacketParser); +use utf8; +use Carp::Assert; +use Utils::Assert; +use Scalar::Util; +use Socket qw(inet_aton inet_ntoa); +use Compress::Zlib; + +use AI; +use Globals; +use Field; +#use Settings; +use Log qw(message warning error debug); +use FileParsers qw(updateMonsterLUT updateNPCLUT); +use I18N qw(bytesToString stringToBytes); +use Interface; +use Network; +use Network::MessageTokenizer; +use Misc; +use Plugins; +use Skill; +use Utils; +use Utils::Exceptions; +use Utils::Crypton; +use Translation; +use Actor::Slave::Homunculus; +use Actor::Slave::Mercenary; +use Actor::Slave::Unknown; + +our %EXPORT_TAGS = ( + actor_type => [qw(PC_TYPE NPC_TYPE ITEM_TYPE SKILL_TYPE UNKNOWN_TYPE NPC_MOB_TYPE NPC_EVT_TYPE NPC_PET_TYPE NPC_HO_TYPE NPC_MERSOL_TYPE + NPC_ELEMENTAL_TYPE NPC_TYPE2)], + connection => [qw(REFUSE_INVALID_ID REFUSE_INVALID_PASSWD REFUSE_ID_EXPIRED ACCEPT_ID_PASSWD REFUSE_NOT_CONFIRMED REFUSE_INVALID_VERSION + REFUSE_BLOCK_TEMPORARY REFUSE_BILLING_NOT_READY REFUSE_NONSAKRAY_ID_BLOCKED REFUSE_BAN_BY_DBA + REFUSE_EMAIL_NOT_CONFIRMED REFUSE_BAN_BY_GM REFUSE_TEMP_BAN_FOR_DBWORK REFUSE_SELF_LOCK REFUSE_NOT_PERMITTED_GROUP + REFUSE_WAIT_FOR_SAKRAY_ACTIVE REFUSE_NOT_CHANGED_PASSWD REFUSE_BLOCK_INVALID REFUSE_WARNING REFUSE_NOT_OTP_USER_INFO + REFUSE_OTP_AUTH_FAILED REFUSE_SSO_AUTH_FAILED REFUSE_NOT_ALLOWED_IP_ON_TESTING REFUSE_OVER_BANDWIDTH + REFUSE_OVER_USERLIMIT REFUSE_UNDER_RESTRICTION REFUSE_BY_OUTER_SERVER REFUSE_BY_UNIQUESERVER_CONNECTION + REFUSE_BY_AUTHSERVER_CONNECTION REFUSE_BY_BILLSERVER_CONNECTION REFUSE_BY_AUTH_WAITING REFUSE_DELETED_ACCOUNT + REFUSE_ALREADY_CONNECT REFUSE_TEMP_BAN_HACKING_INVESTIGATION REFUSE_TEMP_BAN_BUG_INVESTIGATION + REFUSE_TEMP_BAN_DELETING_CHAR REFUSE_TEMP_BAN_DELETING_SPOUSE_CHAR REFUSE_USER_PHONE_BLOCK + ACCEPT_LOGIN_USER_PHONE_BLOCK ACCEPT_LOGIN_CHILD REFUSE_IS_NOT_FREEUSER REFUSE_INVALID_ONETIMELIMIT + REFUSE_CHANGE_PASSWD_FORCE REFUSE_OUTOFDATE_PASSWORD REFUSE_NOT_CHANGE_ACCOUNTID REFUSE_NOT_CHANGE_CHARACTERID REFUSE_TOKEN_EXPIRED + REFUSE_SSO_AUTH_BLOCK_USER REFUSE_SSO_AUTH_GAME_APPLY REFUSE_SSO_AUTH_INVALID_GAMENUM REFUSE_SSO_AUTH_INVALID_USER + REFUSE_SSO_AUTH_OTHERS REFUSE_SSO_AUTH_INVALID_AGE REFUSE_SSO_AUTH_INVALID_MACADDRESS REFUSE_SSO_AUTH_BLOCK_ETERNAL + REFUSE_SSO_AUTH_BLOCK_ACCOUNT_STEAL REFUSE_SSO_AUTH_BLOCK_BUG_INVESTIGATION REFUSE_SSO_NOT_PAY_USER + REFUSE_SSO_ALREADY_LOGIN_USER REFUSE_SSO_CURRENT_USED_USER REFUSE_SSO_OTHER_1 REFUSE_SSO_DROP_USER + REFUSE_SSO_NOTHING_USER REFUSE_SSO_OTHER_2 REFUSE_SSO_WRONG_RATETYPE_1 REFUSE_SSO_EXTENSION_PCBANG_TIME + REFUSE_SSO_WRONG_RATETYPE_2 REFUSE_UNKNOWN REFUSE_INVALID_ID2 REFUSE_BLOCKED_ID REFUSE_BLOCKED_COUNTRY REFUSE_INVALID_PASSWD2 + REFUSE_EMAIL_NOT_CONFIRMED2 REFUSE_BILLING REFUSE_BILLING2 REFUSE_WEB REFUSE_CHANGE_PASSWD_FORCE2 REFUSE_SERVER_ERROR + REFUSE_SERVER_ERROR2 REFUSE_SERVER_ERROR3 REFUSE_ACCOUNT_NOT_PREMIUM REFUSE_BAN_ACCOUNT)], + stat_info => [qw(VAR_SPEED VAR_EXP VAR_JOBEXP VAR_VIRTUE VAR_HONOR VAR_HP VAR_MAXHP VAR_SP VAR_MAXSP VAR_POINT VAR_HAIRCOLOR VAR_CLEVEL VAR_SPPOINT + VAR_STR VAR_AGI VAR_VIT VAR_INT VAR_DEX VAR_LUK VAR_JOB VAR_MONEY VAR_SEX VAR_MAXEXP VAR_MAXJOBEXP VAR_WEIGHT VAR_MAXWEIGHT VAR_POISON + VAR_STONE VAR_CURSE VAR_FREEZING VAR_SILENCE VAR_CONFUSION VAR_STANDARD_STR VAR_STANDARD_AGI VAR_STANDARD_VIT VAR_STANDARD_INT + VAR_STANDARD_DEX VAR_STANDARD_LUK VAR_ATTACKMT VAR_ATTACKEDMT VAR_NV_BASIC VAR_ATTPOWER VAR_REFININGPOWER VAR_MAX_MATTPOWER + VAR_MIN_MATTPOWER VAR_ITEMDEFPOWER VAR_PLUSDEFPOWER VAR_MDEFPOWER VAR_PLUSMDEFPOWER VAR_HITSUCCESSVALUE VAR_AVOIDSUCCESSVALUE + VAR_PLUSAVOIDSUCCESSVALUE VAR_CRITICALSUCCESSVALUE VAR_ASPD VAR_PLUSASPD VAR_JOBLEVEL VAR_ACCESSORY2 VAR_ACCESSORY3 VAR_HEADPALETTE + VAR_BODYPALETTE VAR_PKHONOR VAR_CURXPOS VAR_CURYPOS VAR_CURDIR VAR_CHARACTERID VAR_ACCOUNTID VAR_MAPID VAR_MAPNAME VAR_ACCOUNTNAME + VAR_CHARACTERNAME VAR_ITEM_COUNT VAR_ITEM_ITID VAR_ITEM_SLOT1 VAR_ITEM_SLOT2 VAR_ITEM_SLOT3 VAR_ITEM_SLOT4 VAR_HEAD VAR_WEAPON + VAR_ACCESSORY VAR_STATE VAR_MOVEREQTIME VAR_GROUPID VAR_ATTPOWERPLUSTIME VAR_ATTPOWERPLUSPERCENT VAR_DEFPOWERPLUSTIME + VAR_DEFPOWERPLUSPERCENT VAR_DAMAGENOMOTIONTIME VAR_BODYSTATE VAR_HEALTHSTATE VAR_RESETHEALTHSTATE VAR_CURRENTSTATE VAR_RESETEFFECTIVE + VAR_GETEFFECTIVE VAR_EFFECTSTATE VAR_SIGHTABILITYEXPIREDTIME VAR_SIGHTRANGE VAR_SIGHTPLUSATTPOWER VAR_STREFFECTIVETIME + VAR_AGIEFFECTIVETIME VAR_VITEFFECTIVETIME VAR_INTEFFECTIVETIME VAR_DEXEFFECTIVETIME VAR_LUKEFFECTIVETIME VAR_STRAMOUNT VAR_AGIAMOUNT + VAR_VITAMOUNT VAR_INTAMOUNT VAR_DEXAMOUNT VAR_LUKAMOUNT VAR_MAXHPAMOUNT VAR_MAXSPAMOUNT VAR_MAXHPPERCENT VAR_MAXSPPERCENT + VAR_HPACCELERATION VAR_SPACCELERATION VAR_SPEEDAMOUNT VAR_SPEEDDELTA VAR_SPEEDDELTA2 VAR_PLUSATTRANGE VAR_DISCOUNTPERCENT + VAR_AVOIDABLESUCCESSPERCENT VAR_STATUSDEFPOWER VAR_PLUSDEFPOWERINACOLYTE VAR_MAGICITEMDEFPOWER VAR_MAGICSTATUSDEFPOWER VAR_CLASS + VAR_PLUSATTACKPOWEROFITEM VAR_PLUSDEFPOWEROFITEM VAR_PLUSMDEFPOWEROFITEM VAR_PLUSARROWPOWEROFITEM VAR_PLUSATTREFININGPOWEROFITEM + VAR_PLUSDEFREFININGPOWEROFITEM VAR_IDENTIFYNUMBER VAR_ISDAMAGED VAR_ISIDENTIFIED VAR_REFININGLEVEL VAR_WEARSTATE VAR_ISLUCKY + VAR_ATTACKPROPERTY VAR_STORMGUSTCNT VAR_MAGICATKPERCENT VAR_MYMOBCOUNT VAR_ISCARTON VAR_GDID VAR_NPCXSIZE VAR_NPCYSIZE VAR_RACE + VAR_SCALE VAR_PROPERTY VAR_PLUSATTACKPOWEROFITEM_RHAND VAR_PLUSATTACKPOWEROFITEM_LHAND VAR_PLUSATTREFININGPOWEROFITEM_RHAND + VAR_PLUSATTREFININGPOWEROFITEM_LHAND VAR_TOLERACE VAR_ARMORPROPERTY VAR_ISMAGICIMMUNE VAR_ISFALCON VAR_ISRIDING VAR_MODIFIED + VAR_FULLNESS VAR_RELATIONSHIP VAR_ACCESSARY VAR_SIZETYPE VAR_SHOES VAR_STATUSATTACKPOWER VAR_BASICAVOIDANCE VAR_BASICHIT + VAR_PLUSASPDPERCENT VAR_CPARTY VAR_ISMARRIED VAR_ISGUILD VAR_ISFALCONON VAR_ISPECOON VAR_ISPARTYMASTER VAR_ISGUILDMASTER + VAR_BODYSTATENORMAL VAR_HEALTHSTATENORMAL VAR_STUN VAR_SLEEP VAR_UNDEAD VAR_BLIND VAR_BLOODING VAR_BSPOINT VAR_ACPOINT VAR_BSRANK + VAR_ACRANK VAR_CHANGESPEED VAR_CHANGESPEEDTIME VAR_MAGICATKPOWER VAR_MER_KILLCOUNT VAR_MER_FAITH VAR_MDEFPERCENT VAR_CRITICAL_DEF + VAR_ITEMPOWER VAR_MAGICDAMAGEREDUCE VAR_STATUSMAGICPOWER VAR_PLUSMAGICPOWEROFITEM VAR_ITEMMAGICPOWER VAR_NAME VAR_FSMSTATE + VAR_ATTMPOWER VAR_CARTWEIGHT VAR_HP_SELF VAR_SP_SELF VAR_COSTUME_BODY VAR_RESET_COSTUMES + VAR_SP_POW VAR_SP_STA VAR_SP_WIS VAR_SP_SPL VAR_SP_CON VAR_SP_CRT + VAR_SP_PATK VAR_SP_SMATK VAR_SP_RES VAR_SP_MRES VAR_SP_HPLUS VAR_SP_CRATE VAR_SP_TRAITPOINT VAR_SP_AP VAR_SP_MAXAP + VAR_SP_UPOW VAR_SP_USTA VAR_SP_UWIS VAR_SP_USPL VAR_SP_UCON VAR_SP_UCRT)], + party_invite => [qw(ANSWER_ALREADY_OTHERGROUPM ANSWER_JOIN_REFUSE ANSWER_JOIN_ACCEPT ANSWER_MEMBER_OVERSIZE ANSWER_DUPLICATE + ANSWER_JOINMSG_REFUSE ANSWER_UNKNOWN_ERROR ANSWER_UNKNOWN_CHARACTER ANSWER_INVALID_MAPPROPERTY)], + party_leave => [qw(GROUPMEMBER_DELETE_LEAVE GROUPMEMBER_DELETE_EXPEL)], + exp_origin => [qw(EXP_FROM_BATTLE EXP_FROM_QUEST)], +); + +our @EXPORT = ( + @{$EXPORT_TAGS{actor_type}}, + @{$EXPORT_TAGS{connection}}, + @{$EXPORT_TAGS{stat_info}}, + @{$EXPORT_TAGS{party_invite}}, + @{$EXPORT_TAGS{party_leave}}, + @{$EXPORT_TAGS{exp_origin}}, +); + +# object_type constants for &actor_display +use constant { + PC_TYPE => 0x0, + NPC_TYPE => 0x1, + ITEM_TYPE => 0x2, + SKILL_TYPE => 0x3, + UNKNOWN_TYPE => 0x4, + NPC_MOB_TYPE => 0x5, + NPC_EVT_TYPE => 0x6, + NPC_PET_TYPE => 0x7, + NPC_HO_TYPE => 0x8, + NPC_MERSOL_TYPE => 0x9, + NPC_ELEMENTAL_TYPE => 0xa, + NPC_TYPE2 => 0xc, +}; + +# connection +use constant { + REFUSE_INVALID_ID => 0x0, + REFUSE_INVALID_PASSWD => 0x1, + REFUSE_ID_EXPIRED => 0x2, + ACCEPT_ID_PASSWD => 0x3, + REFUSE_NOT_CONFIRMED => 0x4, + REFUSE_INVALID_VERSION => 0x5, + REFUSE_BLOCK_TEMPORARY => 0x6, + REFUSE_BILLING_NOT_READY => 0x7, + REFUSE_NONSAKRAY_ID_BLOCKED => 0x8, + REFUSE_BAN_BY_DBA => 0x9, + REFUSE_EMAIL_NOT_CONFIRMED => 0xa, + REFUSE_BAN_BY_GM => 0xb, + REFUSE_TEMP_BAN_FOR_DBWORK => 0xc, + REFUSE_SELF_LOCK => 0xd, + REFUSE_NOT_PERMITTED_GROUP => 0xe, + REFUSE_WAIT_FOR_SAKRAY_ACTIVE => 0xf, + REFUSE_NOT_CHANGED_PASSWD => 0x10, + REFUSE_BLOCK_INVALID => 0x11, + REFUSE_WARNING => 0x12, + REFUSE_NOT_OTP_USER_INFO => 0x13, + REFUSE_OTP_AUTH_FAILED => 0x14, + REFUSE_SSO_AUTH_FAILED => 0x15, + REFUSE_NOT_ALLOWED_IP_ON_TESTING => 0x16, + REFUSE_OVER_BANDWIDTH => 0x17, + REFUSE_OVER_USERLIMIT => 0x18, + REFUSE_UNDER_RESTRICTION => 0x19, + REFUSE_BY_OUTER_SERVER => 0x1a, + REFUSE_BY_UNIQUESERVER_CONNECTION => 0x1b, + REFUSE_BY_AUTHSERVER_CONNECTION => 0x1c, + REFUSE_BY_BILLSERVER_CONNECTION => 0x1d, + REFUSE_BY_AUTH_WAITING => 0x1e, + REFUSE_DELETED_ACCOUNT => 0x63, + REFUSE_ALREADY_CONNECT => 0x64, + REFUSE_TEMP_BAN_HACKING_INVESTIGATION => 0x65, + REFUSE_TEMP_BAN_BUG_INVESTIGATION => 0x66, + REFUSE_TEMP_BAN_DELETING_CHAR => 0x67, + REFUSE_TEMP_BAN_DELETING_SPOUSE_CHAR => 0x68, + REFUSE_USER_PHONE_BLOCK => 0x69, + ACCEPT_LOGIN_USER_PHONE_BLOCK => 0x6a, + ACCEPT_LOGIN_CHILD => 0x6b, + REFUSE_IS_NOT_FREEUSER => 0x6c, + REFUSE_INVALID_ONETIMELIMIT => 0x6d, + REFUSE_CHANGE_PASSWD_FORCE => 0x6e, + REFUSE_OUTOFDATE_PASSWORD => 0x6f, + REFUSE_NOT_CHANGE_ACCOUNTID => 0xf0, + REFUSE_NOT_CHANGE_CHARACTERID => 0xf1, + REFUSE_TOKEN_EXPIRED => 0xf3, + REFUSE_SSO_AUTH_BLOCK_USER => 0x1394, + REFUSE_SSO_AUTH_GAME_APPLY => 0x1395, + REFUSE_SSO_AUTH_INVALID_GAMENUM => 0x1396, + REFUSE_SSO_AUTH_INVALID_USER => 0x1397, + REFUSE_SSO_AUTH_OTHERS => 0x1398, + REFUSE_SSO_AUTH_INVALID_AGE => 0x1399, + REFUSE_SSO_AUTH_INVALID_MACADDRESS => 0x139a, + REFUSE_SSO_AUTH_BLOCK_ETERNAL => 0x13c6, + REFUSE_SSO_AUTH_BLOCK_ACCOUNT_STEAL => 0x13c7, + REFUSE_SSO_AUTH_BLOCK_BUG_INVESTIGATION => 0x13c8, + REFUSE_SSO_NOT_PAY_USER => 0x13ba, + REFUSE_SSO_ALREADY_LOGIN_USER => 0x13bb, + REFUSE_SSO_CURRENT_USED_USER => 0x13bc, + REFUSE_SSO_OTHER_1 => 0x13bd, + REFUSE_SSO_DROP_USER => 0x13be, + REFUSE_SSO_NOTHING_USER => 0x13bf, + REFUSE_SSO_OTHER_2 => 0x13c0, + REFUSE_SSO_WRONG_RATETYPE_1 => 0x13c1, + REFUSE_SSO_EXTENSION_PCBANG_TIME => 0x13c2, + REFUSE_SSO_WRONG_RATETYPE_2 => 0x13c3, + REFUSE_BAN_ACCOUNT => 0x13c6, + + # 0x0AE0 + REFUSE_UNKNOWN => 0x1450, + REFUSE_INVALID_ID2 => 0x1451, + REFUSE_BLOCKED_ID => 0x1452, + REFUSE_BLOCKED_COUNTRY => 0x1453, + REFUSE_INVALID_PASSWD2 => 0x1454, + REFUSE_EMAIL_NOT_CONFIRMED2 => 0x1455, + REFUSE_BILLING => 0x1456, + REFUSE_WEB => 0x1457, + REFUSE_BILLING2 => 0x1458, + REFUSE_CHANGE_PASSWD_FORCE2 => 0x1459, + REFUSE_SERVER_ERROR => 0x145A, + REFUSE_SERVER_ERROR2 => 0x145B, + REFUSE_SERVER_ERROR3 => 0x145C, + REFUSE_ACCOUNT_NOT_PREMIUM => 0x14B5, +}; + +# stat_info +use constant { + VAR_SPEED => 0x0, + VAR_EXP => 0x1, + VAR_JOBEXP => 0x2, + VAR_VIRTUE => 0x3, + VAR_HONOR => 0x4, + VAR_HP => 0x5, + VAR_MAXHP => 0x6, + VAR_SP => 0x7, + VAR_MAXSP => 0x8, + VAR_POINT => 0x9, + VAR_HAIRCOLOR => 0xa, + VAR_CLEVEL => 0xb, + VAR_SPPOINT => 0xc, + VAR_STR => 0xd, + VAR_AGI => 0xe, + VAR_VIT => 0xf, + VAR_INT => 0x10, + VAR_DEX => 0x11, + VAR_LUK => 0x12, + VAR_JOB => 0x13, + VAR_MONEY => 0x14, + VAR_SEX => 0x15, + VAR_MAXEXP => 0x16, + VAR_MAXJOBEXP => 0x17, + VAR_WEIGHT => 0x18, + VAR_MAXWEIGHT => 0x19, + VAR_POISON => 0x1a, + VAR_STONE => 0x1b, + VAR_CURSE => 0x1c, + VAR_FREEZING => 0x1d, + VAR_SILENCE => 0x1e, + VAR_CONFUSION => 0x1f, + VAR_STANDARD_STR => 0x20, + VAR_STANDARD_AGI => 0x21, + VAR_STANDARD_VIT => 0x22, + VAR_STANDARD_INT => 0x23, + VAR_STANDARD_DEX => 0x24, + VAR_STANDARD_LUK => 0x25, + VAR_ATTACKMT => 0x26, + VAR_ATTACKEDMT => 0x27, + VAR_NV_BASIC => 0x28, + VAR_ATTPOWER => 0x29, + VAR_REFININGPOWER => 0x2a, + VAR_MAX_MATTPOWER => 0x2b, + VAR_MIN_MATTPOWER => 0x2c, + VAR_ITEMDEFPOWER => 0x2d, + VAR_PLUSDEFPOWER => 0x2e, + VAR_MDEFPOWER => 0x2f, + VAR_PLUSMDEFPOWER => 0x30, + VAR_HITSUCCESSVALUE => 0x31, + VAR_AVOIDSUCCESSVALUE => 0x32, + VAR_PLUSAVOIDSUCCESSVALUE => 0x33, + VAR_CRITICALSUCCESSVALUE => 0x34, + VAR_ASPD => 0x35, + VAR_PLUSASPD => 0x36, + VAR_JOBLEVEL => 0x37, + VAR_ACCESSORY2 => 0x38, + VAR_ACCESSORY3 => 0x39, + VAR_HEADPALETTE => 0x3a, + VAR_BODYPALETTE => 0x3b, + VAR_PKHONOR => 0x3c, + VAR_CURXPOS => 0x3d, + VAR_CURYPOS => 0x3e, + VAR_CURDIR => 0x3f, + VAR_CHARACTERID => 0x40, + VAR_ACCOUNTID => 0x41, + VAR_MAPID => 0x42, + VAR_MAPNAME => 0x43, + VAR_ACCOUNTNAME => 0x44, + VAR_CHARACTERNAME => 0x45, + VAR_ITEM_COUNT => 0x46, + VAR_ITEM_ITID => 0x47, + VAR_ITEM_SLOT1 => 0x48, + VAR_ITEM_SLOT2 => 0x49, + VAR_ITEM_SLOT3 => 0x4a, + VAR_ITEM_SLOT4 => 0x4b, + VAR_HEAD => 0x4c, + VAR_WEAPON => 0x4d, + VAR_ACCESSORY => 0x4e, + VAR_STATE => 0x4f, + VAR_MOVEREQTIME => 0x50, + VAR_GROUPID => 0x51, + VAR_ATTPOWERPLUSTIME => 0x52, + VAR_ATTPOWERPLUSPERCENT => 0x53, + VAR_DEFPOWERPLUSTIME => 0x54, + VAR_DEFPOWERPLUSPERCENT => 0x55, + VAR_DAMAGENOMOTIONTIME => 0x56, + VAR_BODYSTATE => 0x57, + VAR_HEALTHSTATE => 0x58, + VAR_RESETHEALTHSTATE => 0x59, + VAR_CURRENTSTATE => 0x5a, + VAR_RESETEFFECTIVE => 0x5b, + VAR_GETEFFECTIVE => 0x5c, + VAR_EFFECTSTATE => 0x5d, + VAR_SIGHTABILITYEXPIREDTIME => 0x5e, + VAR_SIGHTRANGE => 0x5f, + VAR_SIGHTPLUSATTPOWER => 0x60, + VAR_STREFFECTIVETIME => 0x61, + VAR_AGIEFFECTIVETIME => 0x62, + VAR_VITEFFECTIVETIME => 0x63, + VAR_INTEFFECTIVETIME => 0x64, + VAR_DEXEFFECTIVETIME => 0x65, + VAR_LUKEFFECTIVETIME => 0x66, + VAR_STRAMOUNT => 0x67, + VAR_AGIAMOUNT => 0x68, + VAR_VITAMOUNT => 0x69, + VAR_INTAMOUNT => 0x6a, + VAR_DEXAMOUNT => 0x6b, + VAR_LUKAMOUNT => 0x6c, + VAR_MAXHPAMOUNT => 0x6d, + VAR_MAXSPAMOUNT => 0x6e, + VAR_MAXHPPERCENT => 0x6f, + VAR_MAXSPPERCENT => 0x70, + VAR_HPACCELERATION => 0x71, + VAR_SPACCELERATION => 0x72, + VAR_SPEEDAMOUNT => 0x73, + VAR_SPEEDDELTA => 0x74, + VAR_SPEEDDELTA2 => 0x75, + VAR_PLUSATTRANGE => 0x76, + VAR_DISCOUNTPERCENT => 0x77, + VAR_AVOIDABLESUCCESSPERCENT => 0x78, + VAR_STATUSDEFPOWER => 0x79, + VAR_PLUSDEFPOWERINACOLYTE => 0x7a, + VAR_MAGICITEMDEFPOWER => 0x7b, + VAR_MAGICSTATUSDEFPOWER => 0x7c, + VAR_CLASS => 0x7d, + VAR_PLUSATTACKPOWEROFITEM => 0x7e, + VAR_PLUSDEFPOWEROFITEM => 0x7f, + VAR_PLUSMDEFPOWEROFITEM => 0x80, + VAR_PLUSARROWPOWEROFITEM => 0x81, + VAR_PLUSATTREFININGPOWEROFITEM => 0x82, + VAR_PLUSDEFREFININGPOWEROFITEM => 0x83, + VAR_IDENTIFYNUMBER => 0x84, + VAR_ISDAMAGED => 0x85, + VAR_ISIDENTIFIED => 0x86, + VAR_REFININGLEVEL => 0x87, + VAR_WEARSTATE => 0x88, + VAR_ISLUCKY => 0x89, + VAR_ATTACKPROPERTY => 0x8a, + VAR_STORMGUSTCNT => 0x8b, + VAR_MAGICATKPERCENT => 0x8c, + VAR_MYMOBCOUNT => 0x8d, + VAR_ISCARTON => 0x8e, + VAR_GDID => 0x8f, + VAR_NPCXSIZE => 0x90, + VAR_NPCYSIZE => 0x91, + VAR_RACE => 0x92, + VAR_SCALE => 0x93, + VAR_PROPERTY => 0x94, + VAR_PLUSATTACKPOWEROFITEM_RHAND => 0x95, + VAR_PLUSATTACKPOWEROFITEM_LHAND => 0x96, + VAR_PLUSATTREFININGPOWEROFITEM_RHAND => 0x97, + VAR_PLUSATTREFININGPOWEROFITEM_LHAND => 0x98, + VAR_TOLERACE => 0x99, + VAR_ARMORPROPERTY => 0x9a, + VAR_ISMAGICIMMUNE => 0x9b, + VAR_ISFALCON => 0x9c, + VAR_ISRIDING => 0x9d, + VAR_MODIFIED => 0x9e, + VAR_FULLNESS => 0x9f, + VAR_RELATIONSHIP => 0xa0, + VAR_ACCESSARY => 0xa1, + VAR_SIZETYPE => 0xa2, + VAR_SHOES => 0xa3, + VAR_STATUSATTACKPOWER => 0xa4, + VAR_BASICAVOIDANCE => 0xa5, + VAR_BASICHIT => 0xa6, + VAR_PLUSASPDPERCENT => 0xa7, + VAR_CPARTY => 0xa8, + VAR_ISMARRIED => 0xa9, + VAR_ISGUILD => 0xaa, + VAR_ISFALCONON => 0xab, + VAR_ISPECOON => 0xac, + VAR_ISPARTYMASTER => 0xad, + VAR_ISGUILDMASTER => 0xae, + VAR_BODYSTATENORMAL => 0xaf, + VAR_HEALTHSTATENORMAL => 0xb0, + VAR_STUN => 0xb1, + VAR_SLEEP => 0xb2, + VAR_UNDEAD => 0xb3, + VAR_BLIND => 0xb4, + VAR_BLOODING => 0xb5, + VAR_BSPOINT => 0xb6, + VAR_ACPOINT => 0xb7, + VAR_BSRANK => 0xb8, + VAR_ACRANK => 0xb9, + VAR_CHANGESPEED => 0xba, + VAR_CHANGESPEEDTIME => 0xbb, + VAR_MAGICATKPOWER => 0xbc, + VAR_MER_KILLCOUNT => 0xbd, + VAR_MER_FAITH => 0xbe, + VAR_MDEFPERCENT => 0xbf, + VAR_CRITICAL_DEF => 0xc0, + VAR_ITEMPOWER => 0xc1, + VAR_MAGICDAMAGEREDUCE => 0xc2, + VAR_STATUSMAGICPOWER => 0xc3, + VAR_PLUSMAGICPOWEROFITEM => 0xc4, + VAR_ITEMMAGICPOWER => 0xc5, + VAR_NAME => 0xc6, + VAR_FSMSTATE => 0xc7, + VAR_ATTMPOWER => 0xc8, + VAR_CARTWEIGHT => 0xc9, + VAR_HP_SELF => 0xca, + VAR_SP_SELF => 0xcb, + VAR_COSTUME_BODY => 0xcc, + VAR_RESET_COSTUMES => 0xcd, + VAR_SP_POW => 0xdb, + VAR_SP_STA => 0xdc, + VAR_SP_WIS => 0xdd, + VAR_SP_SPL => 0xde, + VAR_SP_CON => 0xdf, + VAR_SP_CRT => 0xe0, + VAR_SP_PATK => 0xe1, + VAR_SP_SMATK => 0xe2, + VAR_SP_RES => 0xe3, + VAR_SP_MRES => 0xe4, + VAR_SP_HPLUS => 0xe5, + VAR_SP_CRATE => 0xe6, + VAR_SP_TRAITPOINT => 0xe7, + VAR_SP_AP => 0xe8, + VAR_SP_MAXAP => 0xe9, + VAR_SP_UPOW => 0xf7, + VAR_SP_USTA => 0xf8, + VAR_SP_UWIS => 0xf9, + VAR_SP_USPL => 0xfa, + VAR_SP_UCON => 0xfb, + VAR_SP_UCRT => 0xfc, +}; + +# party invite result +use constant { + ANSWER_ALREADY_OTHERGROUPM => 0x0, + ANSWER_JOIN_REFUSE => 0x1, + ANSWER_JOIN_ACCEPT => 0x2, + ANSWER_MEMBER_OVERSIZE => 0x3, + ANSWER_DUPLICATE => 0x4, + ANSWER_JOINMSG_REFUSE => 0x5, + ANSWER_UNKNOWN_ERROR => 0x6, + ANSWER_UNKNOWN_CHARACTER => 0x7, + ANSWER_INVALID_MAPPROPERTY => 0x8, +}; + +# party leave result +use constant { + GROUPMEMBER_DELETE_LEAVE => 0x0, + GROUPMEMBER_DELETE_EXPEL => 0x1, +}; + +# item list type +use constant { + INVTYPE_INVENTORY => 0x0, + INVTYPE_CART => 0x1, + INVTYPE_STORAGE => 0x2, + INVTYPE_GUILD_STORAGE => 0x3, +}; + +# exp origin +use constant { + EXP_FROM_BATTLE => 0x0, + EXP_FROM_QUEST => 0x1, +}; + +# client UI types +use constant { + BANK_UI => 0x0, + STYLIST_UI => 0x1, + CAPTCHA_UI => 0x2, + MACRO_UI => 0x3, + UI_UNUSED => 0x4, + TIPBOX_UI => 0x5, + RENEWQUEST_UI => 0x6, + ATTENDANCE_UI => 0x7, +}; + +use constant { + LEVELUP_EFFECT => 0x0, + JOBLEVELUP_EFFECT => 0x1, + REFINING_FAIL_EFFECT => 0x2, + REFINING_SUCCESS_EFFECT => 0x3, + GAME_OVER_EFFECT => 0x4, + MAKEITEM_AM_SUCCESS_EFFECT => 0x5, + MAKEITEM_AM_FAIL_EFFECT => 0x6, + LEVELUP_EFFECT2 => 0x7, + JOBLEVELUP_EFFECT2 => 0x8, + LEVELUP_EFFECT3 => 0x9, +}; + +# market buy item result +use constant { + MARKET_BUY_RESULT_ERROR => 0xffff, # -1 + MARKET_BUY_RESULT_SUCCESS => 0, + MARKET_BUY_RESULT_NO_ZENY => 1, + MARKET_BUY_RESULT_OVER_WEIGHT => 2, + MARKET_BUY_RESULT_OUT_OF_SPACE => 3, + MARKET_BUY_RESULT_AMOUNT_TOO_BIG => 9, +}; + +# misc configurations +use constant { + CONFIG_OPEN_EQUIPMENT_WINDOW => 0, + CONFIG_CALL => 1, + CONFIG_PET_AUTOFEED => 2, + CONFIG_HOMUNCULUS_AUTOFEED => 3, +}; + +#expand_inventory_result +use constant { + EXPAND_INVENTORY_RESULT_SUCCESS => 0x0, + EXPAND_INVENTORY_RESULT_FAILED => 0x1, + EXPAND_INVENTORY_RESULT_OTHER_WORK => 0x2, + EXPAND_INVENTORY_RESULT_MISSING_ITEM => 0x3, + EXPAND_INVENTORY_RESULT_MAX_SIZE => 0x4, +}; + +# macro detector ui +use constant { + MCD_TIMEOUT => 0, + MCD_INCORRECT => 1, + MCD_GOOD => 2, +}; + +use constant { + MCR_MONITORING => 0, + MCR_NO_DATA => 1, + MCR_INPROGRESS => 2, +}; + +# dynamicnpc_create_result +use constant { + DYNAMICNPC_RESULT_SUCCESS => 0x0, + DYNAMICNPC_RESULT_UNKNOWN => 0x1, + DYNAMICNPC_RESULT_UNKNOWNNPC => 0x2, + DYNAMICNPC_RESULT_DUPLICATE => 0x3, + DYNAMICNPC_RESULT_OUTOFTIME => 0x4 +}; + +# Display gained exp. +# 07F6 <account id>.L <amount>.L <var id>.W <exp type>.W (ZC_NOTIFY_EXP) +# 0ACC <account id>.L <amount>.Q <var id>.W <exp type>.W (ZC_NOTIFY_EXP2) +# amount: INT32_MIN ~ INT32_MAX +# var id: +# SP_BASEEXP, SP_JOBEXP +# exp type: +# 0 = normal exp gained/lost +# 1 = quest exp gained/lost +# 07F6 (exp) doesn't change any exp information because 00B1 (exp_zeny_info) is always sent with it +sub exp { + my ($self, $args) = @_; + + my $max = {VAR_EXP, $char->{exp_max}, VAR_JOBEXP, $char->{exp_job_max}}->{$args->{type}}; + $args->{percent} = $max ? $args->{val} / $max * 100 : 0; + + if ($args->{flag} == EXP_FROM_BATTLE) { + if ($args->{type} == VAR_EXP) { + message TF("Base Exp gained: %d (%.2f%%)\n", @{$args}{qw(val percent)}), 'exp2', 2; + } elsif ($args->{type} == VAR_JOBEXP) { + message TF("Job Exp gained: %d (%.2f%%)\n", @{$args}{qw(val percent)}), 'exp2', 2; + } else { + message TF("Unknown (type=%d) Exp gained: %d\n", @{$args}{qw(type val)}), 'exp2', 2; + } + } elsif ($args->{flag} == EXP_FROM_QUEST) { + if ($args->{type} == VAR_EXP) { + message TF("Base Quest Exp gained: %d (%.2f%%)\n", @{$args}{qw(val percent)}), 'exp2', 2; + } elsif ($args->{type} == VAR_JOBEXP) { + message TF("Job Quest Exp gained: %d (%.2f%%)\n", @{$args}{qw(val percent)}), 'exp2', 2; + } else { + message TF("Unknown (type=%d) Quest Exp gained: %d\n", @{$args}{qw(type val)}), 'exp2', 2; + } + } else { + if ($args->{type} == VAR_EXP) { + message TF("Base Unknown (flag=%d) Exp gained: %d (%.2f%%)\n", @{$args}{qw(flag val percent)}), 'exp2', 2; + } elsif ($args->{type} == VAR_JOBEXP) { + message TF("Job Unknown (flag=%d) Exp gained: %d (%.2f%%)\n", @{$args}{qw(flag val percent)}), 'exp2', 2; + } else { + message TF("Unknown (type=%d) Unknown (flag=%d) Exp gained: %d\n", @{$args}{qw(type flag val)}), 'exp2', 2; + } + } +} + +###################################### +### CATEGORY: Class methods +###################################### + +# Just a wrapper for SUPER::parse. +sub parse { + my $self = shift; + my $args = $self->SUPER::parse(@_); + + if ($args && $config{debugPacket_received} == 3 && + existsInList($config{'debugPacket_include'}, $args->{switch})) { + my $packet = $self->{packet_list}{$args->{switch}}; + my ($name, $packString, $varNames) = @{$packet}; + + my @vars = (); + for my $varName (@{$varNames}) { + message "$varName = $args->{$varName}\n"; + } + } + + return $args; +} + +####################################### +### CATEGORY: Private class methods +####################################### + +## +# int Network::Receive::queryLoginPinCode([String message]) +# Returns: login PIN code, or undef if cancelled +# Ensures: length(result) in 4..8 +# +# Request login PIN code from user. +sub queryLoginPinCode { + my $message = $_[0] || T("You've never set a login PIN code before.\nPlease enter a new login PIN code:"); + do { + my $input = $interface->query($message, isPassword => 1,); + if (!defined($input)) { + quit(); + return; + } else { + if ($input !~ /^\d+$/) { + $interface->errorDialog(T("The PIN code may only contain digits.")); + } elsif ((length($input) <= 3) || (length($input) >= 9)) { + $interface->errorDialog(T("The PIN code must be between 4 and 9 characters.")); + } else { + return $input; + } + } + } while (1); +} + +## +# boolean Network::Receive->queryAndSaveLoginPinCode([String message]) +# Returns: true on success +# +# Request login PIN code from user and save it in config. +sub queryAndSaveLoginPinCode { + my ($self, $message) = @_; + my $pin = queryLoginPinCode($message); + if (defined $pin) { + configModify('loginPinCode', $pin, silent => 1); + return 1; + } else { + return 0; + } +} + +sub changeToInGameState { + if ($net->version() == 1) { + if ($accountID && UNIVERSAL::isa($char, 'Actor::You')) { + if ($net->getState() != Network::IN_GAME) { + $net->setState(Network::IN_GAME); + } + return 1; + } else { + if ($net->getState() != Network::IN_GAME_BUT_UNINITIALIZED) { + $net->setState(Network::IN_GAME_BUT_UNINITIALIZED); + if ($config{verbose} && $messageSender && !$sentWelcomeMessage) { + $messageSender->injectAdminMessage("Please relogin to enable X-${Settings::NAME}."); + $sentWelcomeMessage = 1; + } + } + return 0; + } + } else { + return 1; + } +} + +### Packet inner struct handlers + +# The block size in the received_characters packet varies from server to server. +# This method may be overrided in other ServerType handlers to return +# the correct block size. +sub received_characters_blockSize { + if ($masterServer && $masterServer->{charBlockSize}) { + return $masterServer->{charBlockSize}; + } else { + # last change: 2020-11-13 + # default in kRO, most of official servers and emulators (rAthena, Hercules) + return 155; + } +} + +# The length must exactly match charBlockSize, as it's used to construct packets. +sub received_characters_unpackString { + my $char_info; + for ($masterServer && $masterServer->{charBlockSize}) { + if ($_ == 175) { # PACKETVER >= 20201007 [hp, hp_max, sp and sp_max are now uint64] + $char_info = { + types => 'a4 V2 V V2 V6 v V2 V2 V2 V2 v2 V v9 Z24 C8 v Z16 V4 C', + keys => [qw(charID exp exp_2 zeny exp_job exp_job_2 lv_job body_state health_state effect_state stance manner status_point hp hp_2 hp_max hp_max_2 sp sp_2 sp_max sp_max_2 walkspeed jobID hair_style weapon lv skill_point head_bottom shield head_top head_mid hair_pallete clothes_color name str agi vit int dex luk slot hair_color is_renamed last_map delete_date robe slot_addon rename_addon sex)], + }; + } elsif ($_ == 155) { # PACKETVER >= 20170830 [base and job exp are now uint64] + $char_info = { + types => 'a4 V2 V V2 V6 v V2 v4 V v9 Z24 C8 v Z16 V4 C', + keys => [qw(charID exp exp_2 zeny exp_job exp_job_2 lv_job body_state health_state effect_state stance manner status_point hp hp_max sp sp_max walkspeed jobID hair_style weapon lv skill_point head_bottom shield head_top head_mid hair_pallete clothes_color name str agi vit int dex luk slot hair_color is_renamed last_map delete_date robe slot_addon rename_addon sex)], + }; + + } elsif ($_ == 147) { # PACKETVER >= 20141022 [iRO Doram Update, walk_speed is now long] + $char_info = { + types => 'a4 V9 v V2 v4 V v9 Z24 C8 v Z16 V4 C', + keys => [qw(charID exp zeny exp_job lv_job body_state health_state effect_state stance manner status_point hp hp_max sp sp_max walkspeed jobID hair_style weapon lv skill_point head_bottom shield head_top head_mid hair_pallete clothes_color name str agi vit int dex luk slot hair_color is_renamed last_map delete_date robe slot_addon rename_addon sex)], + }; + + } elsif ($_ == 146) { # equal to charblocksize 147, but not added sex. (Sep, 2019) + $char_info = { + types => 'a4 V9 v V2 v4 V v9 Z24 C8 v Z16 V4', + keys => [qw(charID exp zeny exp_job lv_job body_state health_state effect_state stance manner status_point hp hp_max sp sp_max walkspeed jobID hair_style weapon lv skill_point head_bottom shield head_top head_mid hair_pallete clothes_color name str agi vit int dex luk slot hair_color is_renamed last_map delete_date robe slot_addon rename_addon)], + }; + + } elsif ($_ == 145) { # PACKETVER >= 20141016 [support to double sex account] + $char_info = { + types => 'a4 V9 v V2 v14 Z24 C8 v Z16 V4 C', + keys => [qw(charID exp zeny exp_job lv_job body_state health_state effect_state stance manner status_point hp hp_max sp sp_max walkspeed jobID hair_style weapon lv skill_point head_bottom shield head_top head_mid hair_pallete clothes_color name str agi vit int dex luk slot hair_color is_renamed last_map delete_date robe slot_addon rename_addon sex)], + }; + + } elsif ($_ == 144) { # PACKETVER >= 20111025 [added rename char feature] + $char_info = { + types => 'a4 V9 v V2 v14 Z24 C8 v Z16 V4', + keys => [qw(charID exp zeny exp_job lv_job body_state health_state effect_state stance manner status_point hp hp_max sp sp_max walkspeed jobID hair_style weapon lv skill_point head_bottom shield head_top head_mid hair_pallete clothes_color name str agi vit int dex luk slot hair_color is_renamed last_map delete_date robe slot_addon rename_addon)], + }; + + } elsif ($_ == 140) { # PACKETVER >= 20110928 [added change slot feature] + $char_info = { + types => 'a4 V9 v V2 v14 Z24 C8 v Z16 V3', + keys => [qw(charID exp zeny exp_job lv_job body_state health_state effect_state stance manner status_point hp hp_max sp sp_max walkspeed jobID hair_style weapon lv skill_point head_bottom shield head_top head_mid hair_pallete clothes_color name str agi vit int dex luk slot hair_color is_renamed last_map delete_date robe slot_addon)], + }; + + } elsif ($_ == 136) { # PACKETVER >= 20110111 [added robe] + $char_info = { + types => 'a4 V9 v V2 v14 Z24 C8 v Z16 V2', + keys => [qw(charID exp zeny exp_job lv_job body_state health_state effect_state stance manner status_point hp hp_max sp sp_max walkspeed jobID hair_style weapon lv skill_point head_bottom shield head_top head_mid hair_pallete clothes_color name str agi vit int dex luk slot hair_color is_renamed last_map delete_date robe)], + }; + + } elsif ($_ == 132) { # PACKETVER >= 20100803 [added delete date] + $char_info = { + types => 'a4 V9 v V2 v14 Z24 C8 v Z16 V', + keys => [qw(charID exp zeny exp_job lv_job body_state health_state effect_state stance manner status_point hp hp_max sp sp_max walkspeed jobID hair_style weapon lv skill_point head_bottom shield head_top head_mid hair_pallete clothes_color name str agi vit int dex luk slot hair_color is_renamed last_map delete_date)], + }; + + } elsif ($_ == 128) { # [Update in last_map size] + $char_info = { + types => 'a4 V9 v V2 v14 Z24 C8 v Z16', + keys => [qw(charID exp zeny exp_job lv_job body_state health_state effect_state stance manner status_point hp hp_max sp sp_max walkspeed jobID hair_style weapon lv skill_point head_bottom shield head_top head_mid hair_pallete clothes_color name str agi vit int dex luk slot hair_color is_renamed last_map)], + }; + + } elsif ($_ == 124) { # PACKETVER >= 20100803 [added last_map, bRO (bitfrost update)] + $char_info = { + types => 'a4 V9 v V2 v14 Z24 C8 v Z12', + keys => [qw(charID exp zeny exp_job lv_job body_state health_state effect_state stance manner status_point hp hp_max sp sp_max walkspeed jobID hair_style weapon lv skill_point head_bottom shield head_top head_mid hair_pallete clothes_color name str agi vit int dex luk slot hair_color is_renamed last_map)], + }; + + } elsif ($_ == 116) { # Unknown change + $char_info = { + types => 'a4 V9 v V2 v14 Z24 C8 v x4', + keys => [qw(charID exp zeny exp_job lv_job body_state health_state effect_state stance manner status_point hp hp_max sp sp_max walkspeed jobID hair_style weapon lv skill_point head_bottom shield head_top head_mid hair_pallete clothes_color name str agi vit int dex luk slot hair_color is_renamed)], + }; + + } elsif ($_ == 112) { # [Added is_renamed] + $char_info = { + types => 'a4 V9 v V2 v14 Z24 C8 v', + keys => [qw(charID exp zeny exp_job lv_job body_state health_state effect_state stance manner status_point hp hp_max sp sp_max walkspeed jobID hair_style weapon lv skill_point head_bottom shield head_top head_mid hair_pallete clothes_color name str agi vit int dex luk slot hair_color is_renamed)], + }; + + } elsif ($_ == 108) { # [Added hair_color] + $char_info = { + types => 'a4 V9 v17 Z24 C6 v2', + keys => [qw(charID exp zeny exp_job lv_job body_state health_state effect_state stance manner status_point hp hp_max sp sp_max walkspeed jobID hair_style weapon lv skill_point head_bottom shield head_top head_mid hair_pallete clothes_color name str agi vit int dex luk slot hair_color)], + }; + + } elsif ($_ == 106) { # PACKETVER >= 2003+ [First known charBlockSize] + $char_info = { + types => 'a4 V9 v17 Z24 C6 v', + keys => [qw(charID exp zeny exp_job lv_job body_state health_state effect_state stance manner status_point hp hp_max sp sp_max walkspeed jobID hair_style weapon lv skill_point head_bottom shield head_top head_mid hair_pallete clothes_color name str agi vit int dex luk slot)], + }; + + } else { + die "Unknown charBlockSize: $_"; + } + return $char_info; + } + die "masterserver or charBlockSize is undefined"; +} + +sub received_characters_slots_info { + return if ($net->getState() == Network::IN_GAME); + my ($self, $args) = @_; + $net->setState(Network::CONNECTED_TO_LOGIN_SERVER); + $charSvrSet{total_slot} = $args->{total_slot} if (exists $args->{total_slot}); + $charSvrSet{premium_start_slot} = $args->{premium_start_slot} if (exists $args->{premium_start_slot}); + $charSvrSet{premium_end_slot} = $args->{premium_end_slot} if (exists $args->{premium_end_slot}); + + $charSvrSet{normal_slot} = $args->{normal_slot} if (exists $args->{normal_slot}); + $charSvrSet{premium_slot} = $args->{premium_slot} if (exists $args->{premium_slot}); + $charSvrSet{billing_slot} = $args->{billing_slot} if (exists $args->{billing_slot}); + + $charSvrSet{producible_slot} = $args->{producible_slot} if (exists $args->{producible_slot}); + $charSvrSet{valid_slot} = $args->{valid_slot} if (exists $args->{valid_slot}); + + undef $conState_tries; + + Plugins::callHook('parseMsg/recvChars', $args->{options}); + if ($args->{options} && exists $args->{options}{charServer}) { + $charServer = $args->{options}{charServer}; + } else { + $charServer = $net->serverPeerHost . ":" . $net->serverPeerPort; + } + + $self->received_characters($args) if ($args->{charInfo}); +} + +# Send to client Characters pages in Char Select Screen +# 099D <size>.W { CHARACTER_INFO_NEO_UNION3 } * (PACKET_HC_ACK_CHARINFO_PER_PAGE) +# CHARACTER_INFO_NEO_UNION3 is based in charblocksize, check sub received_characters_unpackString +sub received_characters { + my ($self, $args) = @_; + my $blockSize = $self->received_characters_blockSize(); + my $char_info = $self->received_characters_unpackString; + + # rAthena and Hercules send all pages + # Official Server send only pages with characters + 1 empty (tested bRO, iRO) Jul-2020 + if (length($args->{charInfo} == 0)) { + $charSvrSet{sync_received_characters} = $charSvrSet{sync_Count} if (exists $charSvrSet{sync_received_characters}); + } else { + $charSvrSet{sync_received_characters}++ if (exists $charSvrSet{sync_received_characters}); + } + + $net->setState(Network::CONNECTED_TO_LOGIN_SERVER) if $net->getState() != Network::CONNECTED_TO_LOGIN_SERVER; + + return unless exists $args->{charInfo}; + + for (my $i = 0; $i < length($args->{charInfo}); $i += $masterServer->{charBlockSize}) { + my $temporary_character; + @{$temporary_character}{@{$char_info->{keys}}} = unpack($char_info->{types}, substr($args->{charInfo}, $i, $masterServer->{charBlockSize})); + + my $character; + + # Re-use existing $char object instead of re-creating it. + # Required because existing AI sequences (eg, route) keep a reference to $char. + if ($char && $char->{ID} eq $accountID && $char->{charID} eq $temporary_character->{charID}) { + $character = $char; + } elsif (exists $chars[$temporary_character->{slot}] && $chars[$temporary_character->{slot}]->{charID} eq $temporary_character->{charID}) { # Re-use existing $char object from $chars if available. + $character = $chars[$temporary_character->{slot}]; + } else { # create new one + $character = new Actor::You; + } + + @{$character}{@{$char_info->{keys}}} = unpack($char_info->{types}, substr($args->{charInfo}, $i, $masterServer->{charBlockSize})); + $character->{ID} = $accountID; + + $character->{name} = bytesToString($character->{name}); + + $character->{lastJobLvl} = $character->{lv_job}; # This is for counting exp + $character->{lastBaseLvl} = $character->{lv}; # This is for counting exp + $character->{headgear}{low} = $character->{head_bottom}; + $character->{headgear}{top} = $character->{head_top}; + $character->{headgear}{mid} = $character->{head_mid}; + + $character->{nameID} = unpack("V", $character->{ID}); + $character->{last_map} =~ s/\.gat.*//g if ($character->{last_map}); + + if ((!exists($character->{sex})) || ($character->{sex} ne "0" && $character->{sex} ne "1")) { $character->{sex} = $accountSex2; } + + $chars[$character->{slot}] = $character; + setCharDeleteDate($character->{slot}, $character->{delete_date}) if $character->{delete_date}; + } + + message T("Received characters from Character Server\n"), "connection"; + + # gradeA says it's supposed to send this packet here, but + # it doesn't work... + # 30 Dec 2005: it didn't work before because it wasn't sending the accountiD -> fixed (kaliwanagan) + $messageSender->sendBanCheck($accountID) if (!$net->clientAlive && $masterServer->{serverType} == 2); + + if ($masterServer->{pinCode}) { + message T("Waiting for PIN code request\n"), "connection"; + $timeout{'charlogin'}{'time'} = time; + + } elsif ($config{pauseCharLogin}) { + return if ($config{XKore} eq 1 || $config{XKore} eq 3); + if (!defined $timeout{'char_login_pause'}{'timeout'}) { + $timeout{'char_login_pause'}{'timeout'} = $config{pauseCharLogin}; + } + $timeout{'char_login_pause'}{'time'} = time; + + } else { + CharacterLogin(); + } +} + +# Tell client how many pages have character selection screen +# 09A0 <total count>.W (PACKET_HC_CHARLIST_NOTIFY) +# total count: Server send from total pages until 1 page +sub sync_received_characters { + my ($self, $args) = @_; + + return unless (UNIVERSAL::isa($net, 'Network::DirectConnection')); + + $charSvrSet{sync_Count} = $args->{sync_Count} if (exists $args->{sync_Count}); + $charSvrSet{sync_received_characters} = 0 if (exists $args->{sync_Count}); + + unless ($net->clientAlive) { + for (1..$args->{sync_Count}) { + $messageSender->sendToServer($messageSender->reconstruct({switch => 'sync_received_characters'})); + } + } +} + +sub reconstruct_received_characters { + my ($self, $args) = @_; + my $char_info = $self->received_characters_unpackString; + + $args->{charInfo} = pack '(a'.$masterServer->{charBlockSize}.')*', map { pack $char_info->{types}, @{$_}{@{$char_info->{keys}}} } @{$args->{chars}}; +} + +sub reconstruct_received_characters_info { + my ($self, $args) = @_; + my $char_info = $self->received_characters_unpackString; + + $args->{charInfo} = pack '(a'.$masterServer->{charBlockSize}.')*', map { pack $char_info->{types}, @{$_}{@{$char_info->{keys}}} } @{$args->{chars}}; +} + +# Notifies client, that character was succesfull created +# 006E { CHARACTER_INFO_NEO_UNION } (PACKET_HC_ACCEPT_MAKECHAR_NEO_UNION) +# CHARACTER_INFO_NEO_UNION is based in charblocksize, check sub received_characters_unpackString +sub character_creation_successful { + my ($self, $args) = @_; + return unless exists $args->{charInfo}; + + my $char_info = $self->received_characters_unpackString; + + my $character = new Actor::You; + @{$character}{@{$char_info->{keys}}} = unpack($char_info->{types}, substr($args->{charInfo}, 0, $masterServer->{charBlockSize})); + $character->{ID} = $accountID; + + $character->{lastJobLvl} = $character->{lv_job}; # This is for counting exp + $character->{lastBaseLvl} = $character->{lv}; # This is for counting exp + $character->{headgear}{low} = $character->{head_bottom}; + $character->{headgear}{top} = $character->{head_top}; + $character->{headgear}{mid} = $character->{head_mid}; + + $character->{nameID} = unpack("V", $character->{ID}); + $character->{name} = bytesToString($character->{name}); + $character->{last_map} = substr($character->{last_map}, 0, length($character->{last_map}) - 4); + + $character->{exp} = 0; + $character->{exp_job} = 0; + + if ((!exists($character->{sex})) || ($character->{sex} ne "0" && $character->{sex} ne "1")) { $character->{sex} = $accountSex2; } + + $chars[$character->{slot}] = $character; + + $net->setState(3); + message TF("Character %s (%d) created.\n", $character->{name}, $character->{slot}), "info"; + + Plugins::callHook('char_created', {char => $character}); + + if ($net->getState() == 3 && charSelectScreen() == 1) { + $firstLoginMap = 1; + $startingzeny = $chars[$config{'char'}]{'zeny'} unless defined $startingzeny; + $sentWelcomeMessage = 1; + } +} + +# Notifies Client that the character was not created +# 006E <error code>.B (PACKET_HC_REFUSE_MAKECHAR) +# code: +# 0x00 = Charname already exists +# 0x01 = You are underaged +# 0x02 = Symbols in Character Names are forbidden +# 0x03 = You are not elegible to open the Character Slot +# 0xFF = Char creation denied +sub character_creation_failed { + my ($self, $args) = @_; + if ($args->{flag} == 0x00) { + message T("Charname already exists.\n"), "info"; + } elsif ($args->{flag} == 0xFF) { + message T("Char creation denied.\n"), "info"; + } elsif ($args->{flag} == 0x01) { + message T("You are underaged.\n"), "info"; + } elsif ($args->{flag} == 0x02) { + message T("Symbols in Character Names are forbidden .\n"), "info"; + } elsif ($args->{flag} == 0x03) { + message T("You are not elegible to open the Character Slot.\n"), "info"; + } else { + message T("Character creation failed. " . + "If you didn't make any mistake, then the name you chose already exists.\n"), "info"; + } + if (charSelectScreen() == 1) { + $net->setState(3); + $firstLoginMap = 1; + $startingzeny = $chars[$config{'char'}]{'zeny'} unless defined $startingzeny; + $sentWelcomeMessage = 1; + } +} + +# Notifies client about account slots info and chars +# 006B <size>.W <total slots>.B <premium start slot>.B <premium end slot>.B <dummy1_beginbilling>.B <code>.L <start time>.L <end time>.L { CHARACTER_INFO_NEO_UNION3 }* (PACKET_HC_ACCEPT_ENTER_NEO_UNION) +# 082D <size>.W <normal slot>.B <premium slot>.B <billing slot>.B <producible slot>.B <valid slot>.B <m_extension>.20B { CHARACTER_INFO_NEO_UNION2 }* (PACKET_HC_ACCEPT2) +# CHARACTER_INFO_NEO_UNION3 and CHARACTER_INFO_NEO_UNION2 are based in charblocksize, check sub received_characters_unpackString +sub received_characters_info { + my ($self, $args) = @_; + Scalar::Util::weaken(my $weak = $self); + my $timeout = {timeout => 6, time => time}; + + $self->{charSelectTimeoutHook} = Plugins::addHook('Network::serverConnect/special' => sub { + if ($weak && timeOut($timeout)) { + $weak->received_characters_slots_info({charInfo => '', RAW_MSG_SIZE => 4}); + } + }); + $self->{charSelectHook} = Plugins::addHook(charSelectScreen => sub { + if ($weak) { + Plugins::delHook(delete $weak->{charSelectTimeoutHook}) if $weak->{charSelectTimeoutHook}; + } + }); + $timeout{charlogin}{time} = time; + $self->received_characters_slots_info($args); +} + +### Parse/reconstruct callbacks and packet handlers + +sub parse_account_server_info { + my ($self, $args) = @_; + my $server_info; + + if ($args->{switch} eq '0B60') { # tRO 2020, twRO 2021 + $server_info = { + len => 164, + types => 'a4 v Z20 v3 a128 V', + keys => [qw(ip port name state users property ip_port unknown)], + }; + + } elsif ($args->{switch} eq '0AC4' || $args->{switch} eq '0B07') { # kRO Zero 2017, kRO ST 201703+, vRO 2021 + $server_info = { + len => 160, + types => 'a4 v Z20 v3 a128', + keys => [qw(ip port name users state property ip_port)], + }; + + } elsif ($args->{switch} eq '0AC9') { # cRO 2017 + $server_info = { + len => 154, + types => 'a20 V v a126', + keys => [qw(name users unknown ip_port)], + }; + } elsif ($args->{switch} eq '0276' && ($masterServer->{serverType} eq "tRO" or $masterServer->{serverType} eq "aRO")) { # tRO 2020 and aRO 2022. Keep this here to future uses + $server_info = { + len => 36, + types => 'a4 v Z20 v5', + keys => [qw(ip port name state users property sid unknown)], + }; + } elsif ($args->{switch} eq '0C32') { + $server_info = { + len => 165, + types => 'a4 v Z20 v3 a128 a5', + keys => [qw(ip port name users state property ip_port unknown)], + }; + } else { # 0069 [default] and 0276 [pRO] + $server_info = { + len => 32, + types => 'a4 v Z20 v3', + keys => [qw(ip port name users display unknown)], + }; + } + + @{$args->{servers}} = map { + my %server; + @server{@{$server_info->{keys}}} = unpack($server_info->{types}, $_); + if ($masterServer && $masterServer->{private}) { + $server{ip} = $masterServer->{ip}; + } elsif (exists $server{ip_port} && $server{ip_port} =~ /.*\:\d+/) { + @server{qw(ip port)} = split (/\:/, $server{ip_port}); + $server{ip} =~ s/^\s+|\s+$//g; + $server{port} =~ tr/0-9//cd; + } else { + $server{ip} = inet_ntoa($server{ip}); + } + $server{name} = bytesToString($server{name}); + \%server + } unpack '(a'.$server_info->{len}.')*', $args->{serverInfo}; + + if (length $args->{lastLoginIP} == 4 && $args->{lastLoginIP} ne "\0"x4) { + $args->{lastLoginIP} = inet_ntoa($args->{lastLoginIP}); + } else { + delete $args->{lastLoginIP}; + } +} + +sub reconstruct_account_server_info { + my ($self, $args) = @_; + $args->{lastLoginIP} = inet_aton($args->{lastLoginIP}); + + my $serverInfo; + + if ($args->{switch} eq '0B60') { # tRO 2020 + $serverInfo = { + len => 164, + types => 'a4 v Z20 v3 a128 V', + keys => [qw(ip port name state users property ip_port unknown)], + }; + + } elsif ($args->{switch} eq "0AC4" || $self->{packet_lut}{$args->{switch}} eq "0AC4" || $args->{switch} eq '0B07') { + $serverInfo = { + len => 160, + types => 'a4 v Z20 v3 a128', + keys => [qw(ip port name users state property ip_port)], + }; + } elsif ($args->{switch} eq "0AC9" || $self->{packet_lut}{$args->{switch}} eq "0AC9") { + $serverInfo = { + len => 154, + types => 'a20 V a2 a126', + keys => [qw(name users unknown ip_port)], + }; + } elsif ($masterServer->{serverType} eq "tRO" && ( $args->{switch} eq "0276" || $self->{packet_lut}{$args->{switch}} eq "0276" )) { + $serverInfo = { + len => 36, + types => 'a4 v Z20 v5', + keys => [qw(ip port name state users property sid unknown)], + }; + } elsif ($args->{switch} eq '0C32') { + $serverInfo = { + len => 165, + types => 'a4 v Z20 v3 a128 a5', + keys => [qw(ip port name users state property ip_port unknown)], + }; + } else { + $serverInfo = { + len => 32, + types => 'a4 v Z20 v2 x2', + keys => [qw(ip port name users display)], + }; + } + + foreach my $server (@{$args->{servers}}) { + $server->{ip} = inet_aton($server->{ip}); + $server->{name} = stringToBytes($server->{name}); + } + + $args->{serverInfo} = pack '(a' . $serverInfo->{len} .')*', map { pack($serverInfo->{types}, @{$_}{@{$serverInfo->{keys}}}) } @{$args->{servers}}; +} + +sub account_server_info { + my ($self, $args) = @_; + $net->setState(2); + undef $conState_tries; + $sessionID = $args->{sessionID}; + $accountID = $args->{accountID}; + $sessionID2 = $args->{sessionID2}; + # Account sex should only be 0 (female) or 1 (male) + # inRO gives female as 2 but expects 0 back + # do modulus of 2 here to fix? + # FIXME: we should check exactly what operation the client does to the number given + $accountSex = $args->{accountSex} % 2; + $accountSex2 = ($config{'sex'} ne "") ? $config{'sex'} : $accountSex; + + # any servers with lastLoginIP lastLoginTime? + # message TF("Last login: %s from %s\n", @{$args}{qw(lastLoginTime lastLoginIP)}) if ...; + + message + center(T(" Account Info "), 34, '-') ."\n" . + swrite( + T("Account ID: \@<<<<<<<<< \@<<<<<<<<<<\n" . + "Sex: \@<<<<<<<<<<<<<<<<<<<<<\n" . + "Session ID: \@<<<<<<<<< \@<<<<<<<<<<\n" . + "SessionID2: \@<<<<<<<<< \@<<<<<<<<<<\n"), + [unpack('V',$accountID), getHex($accountID), $sex_lut{$accountSex}, unpack('V',$sessionID), getHex($sessionID), + unpack('V',$sessionID2), getHex($sessionID2)]) . + ('-'x34) . "\n", 'connection'; + + @servers = @{$args->{servers}}; + my @state = ("Idle", "Normal", "Busy", "Full"); + + my $msg = center(T(" Servers "), 70, '-') ."\n" . + T("# Name Users IP Port SID State\n"); + for (my $num = 0; $num < @servers; $num++) { + $msg .= swrite( + "@<< @<<<<<<<<<<<<<<<<<<<< @<<<<< @<<<<<<<<<<<<<< @<<<<< @<<<<< @<<<<<<", + [$num, $servers[$num]{name}, $servers[$num]{users}, $servers[$num]{ip}, $servers[$num]{port}, ($servers[$num]{sid}) ? $servers[$num]{sid} : 0, defined($servers[$num]{state}) ? $state[$servers[$num]{state}] : 0]); + } + $msg .= ('-'x70) . "\n"; + message $msg, "connection"; + + if ($net->version != 1) { + message T("Closing connection to Account Server\n"), 'connection'; + $net->serverDisconnect(); + if (!$masterServer->{charServer_ip} && $config{server} eq "") { + my @serverList; + foreach my $server (@servers) { + push @serverList, $server->{name}; + } + my $ret = $interface->showMenu( + T("Please select your login server."), + \@serverList, + title => T("Select Login Server")); + if ($ret == -1) { + quit(); + } else { + main::configModify('server', $ret, 1); + } + + } elsif ($masterServer->{charServer_ip}) { + message TF("Forcing connect to char server %s: %s\n", $masterServer->{charServer_ip}, $masterServer->{charServer_port}), 'connection'; + } + } + + # FIXME better support for multiple received_characters packets + undef @chars; + if ($config{'XKore'} eq '1') { + $incomingMessages->nextMessageMightBeAccountID(); + } +} + +sub connection_refused { + my ($self, $args) = @_; + + error TF("The server has denied your connection (error: %d).\n", $args->{error}), 'connection'; +} + +# Notifies the client, that it's connection attempt was accepted. +# 0073 <start time>.L <position>.3B <x size>.B <y size>.B (ZC_ACCEPT_ENTER) +# 02EB <start time>.L <position>.3B <x size>.B <y size>.B <font>.W (ZC_ACCEPT_ENTER2) +# 0A18 <start time>.L <position>.3B <x size>.B <y size>.B <font>.W <sex>.B (ZC_ACCEPT_ENTER3) +sub map_loaded { + my ($self, $args) = @_; + $net->setState(Network::IN_GAME); + undef $conState_tries; + $char = $chars[$config{char}]; + return unless changeToInGameState(); + # assertClass($char, 'Actor::You'); + $syncMapSync = pack('V1',$args->{syncMapSync}); # unused, should we keep this for legacy compatibility? + main::initMapChangeVars(); + %minimap_indicator_seen = (); + + if ($net->version == 1) { + $net->setState(4); + message(T("Waiting for map to load...\n"), "connection"); + ai_clientSuspend(0, $timeout{'ai_clientSuspend'}{'timeout'}); + } else { + $messageSender->sendReqRemainTime() if (grep { $masterServer->{serverType} eq $_ } qw(Zero Sakray)); + + $messageSender->sendMapLoaded(); + + $messageSender->sendSync(1); + + # Request for Guild Information + $messageSender->sendGuildRequestInfo(0) unless (grep { $masterServer->{serverType} eq $_ } qw(twRO ROla)); # some servers does not send this packet + + $messageSender->sendRequestCashItemsList() if (grep { $masterServer->{serverType} eq $_ } qw(bRO idRO_Renewal twRO ROla)); # tested at bRO 2013.11.30, request for cashitemslist + $messageSender->sendCashShopOpen() if ($config{whenInGame_requestCashPoints}); + + # request to unfreeze char - alisonrag + $messageSender->sendBlockingPlayerCancel() if $masterServer->{blockingPlayerCancel} || $self->{blockingPlayerCancel}; + } + + message(T("You are now in the game\n"), "connection"); + Plugins::callHook('in_game'); + $timeout{'ai'}{'time'} = time; + our $quest_generation++; + + $char->{pos} = {}; + makeCoordsDir($char->{pos}, $args->{coords}, \$char->{look}{body}); + $char->{pos_to} = {%{$char->{pos}}}; + message(TF("Your Coordinates: %s, %s\n", $char->{pos}{x}, $char->{pos}{y}), undef, 1); + $char->{time_move} = 0; + $char->{time_move_calc} = 0; + $char->{solution} = []; + push(@{$char->{solution}}, { x => $char->{pos}{x}, y => $char->{pos}{y} }); + + # set initial status from data received from the char server (seems needed on eA, dunno about kRO)} + if ($masterServer->{private}){ setStatus($char, $char->{opt1}, $char->{opt2}, $char->{option}); } + + # ignoreAll + $ignored_all = 0; +} + +# Notifies the client, that it's connection attempt was refused (ZC_REFUSE_ENTER). +# 0074 <error code>.B +# error code: +# 0 = client type mismatch +# 1 = ID mismatch +# 2 = mobile - out of available time +# 3 = mobile - already logged in +# 4 = mobile - waiting state +sub map_load_error { + my ($self, $args) = @_; + + error T("Error while try to login in map-server: "); + if ($args->{error} == 0) { + error TF("Wrong Client Type (%s). \n", $args->{error}); + } elsif ($args->{error} == 1) { + error TF("Wrong ID (%s). \n", $args->{error}); + } elsif ($args->{error} == 2) { + error TF("Timeout (%s). \n", $args->{error}); + } elsif ($args->{error} == 3) { + error TF("Already Logged In (%s). \n", $args->{error}); + } elsif ($args->{error} == 4) { + error TF("Waiting State (%s). \n", $args->{error}); # ?? + } else { + error TF("Unknown Error (%s). \n", $args->{error}); + } + + Plugins::callHook('disconnected'); + if ($config{dcOnDisconnect}) { + error T("Auto disconnecting on Disconnect!\n"); + chatLog("k", T("*** You disconnected, auto disconnect! ***\n")); + $quit = 1; + } + + $net->setState(1); + undef $conState_tries; + + $timeout_ex{'master'}{'time'} = time; + $timeout_ex{'master'}{'timeout'} = $timeout{'reconnect'}{'timeout'}; + $net->serverDisconnect(); +} + +our %stat_info_handlers = ( + VAR_SPEED, sub { $_[0]{walk_speed} = $_[1] / 1000 }, + VAR_EXP, sub { + my ($actor, $value) = @_; + + $actor->{exp_last} = $actor->{exp}; + $actor->{exp} = $value; + + return unless $actor->isa('Actor::You'); +=pod + unless ($bExpSwitch) { + $bExpSwitch = 1; + } else { + if ($actor->{exp_last} > $actor->{exp}) { + $monsterBaseExp = 0; + } else { + $monsterBaseExp = $actor->{exp} - $actor->{exp_last}; + } + $totalBaseExp += $monsterBaseExp; + if ($bExpSwitch == 1) { + $totalBaseExp += $monsterBaseExp; + $bExpSwitch = 2; + } + } +=cut + + if ($actor->{lastBaseLvl} eq $actor->{lv}) { + $monsterBaseExp = $actor->{exp} - $actor->{exp_last}; + } else { + $monsterBaseExp = $actor->{exp_max_last2} - $actor->{exp_last} + $actor->{exp}; + $actor->{lastBaseLvl} = $actor->{lv}; + $actor->{exp_max_last2} = $actor->{exp_max}; + } + + if ($monsterBaseExp > 0) { + $totalBaseExp += $monsterBaseExp; + } + + # no VAR_JOBEXP next - no message? + }, + VAR_JOBEXP, sub { + my ($actor, $value) = @_; + + $actor->{exp_job_last} = $actor->{exp_job}; + $actor->{exp_job} = $value; + + # TODO: message for all actors + return unless $actor->isa('Actor::You'); + # TODO: exp report (statistics) - no globals, move to plugin +=pod + if ($jExpSwitch == 0) { + $jExpSwitch = 1; + } else { + if ($char->{exp_job_last} > $char->{exp_job}) { + $monsterJobExp = 0; + } else { + $monsterJobExp = $char->{exp_job} - $char->{exp_job_last}; + } + $totalJobExp += $monsterJobExp; + if ($jExpSwitch == 1) { + $totalJobExp += $monsterJobExp; + $jExpSwitch = 2; + } + } +=cut + + if ($actor->{lastJobLvl} eq $actor->{lv_job}) { + $monsterJobExp = $actor->{exp_job} - $actor->{exp_job_last}; + } else { + $monsterJobExp = $actor->{exp_job_max_last2} - $actor->{exp_job_last} + $actor->{exp_job}; + $actor->{lastJobLvl} = $actor->{lv_job}; + $actor->{exp_job_max_last2} = $actor->{exp_job_max}; + } + + if ($monsterJobExp > 0) { + $totalJobExp += $monsterJobExp; + } + + my $basePercent = $char->{exp_max} ? + ($monsterBaseExp / $char->{exp_max} * 100) : + 0; + my $jobPercent = $char->{exp_job_max} ? + ($monsterJobExp / $char->{exp_job_max} * 100) : + 0; + message TF("%s have gained %d/%d (%.2f%%/%.2f%%) Exp\n", $char, $monsterBaseExp, $monsterJobExp, $basePercent, $jobPercent), "exp"; + Plugins::callHook('exp_gained'); + }, + #VAR_VIRTUE + VAR_HONOR, sub { + my ($actor, $value) = @_; + + if ($value > 0) { + my $duration = 0xffffffff - $value + 1; + $actor->{mute_period} = $duration * 60; + $actor->{muted} = time; + message sprintf( + $actor->verb(T("%s have been muted for %d minutes\n"), T("%s has been muted for %d minutes\n")), + $actor, $duration + ), "parseMsg_statuslook", $actor->isa('Actor::You') ? 1 : 2; + $actor->setStatus('EFST_MUTED', 1, $actor->{mute_period} * 1000); + } else { + delete $actor->{muted}; + delete $actor->{mute_period}; + message sprintf( + $actor->verb(T("%s are no longer muted."), T("%s is no longer muted.")), $actor + ), "parseMsg_statuslook", $actor->isa('Actor::You') ? 1 : 2; + $actor->setStatus('EFST_MUTED', 0); + } + + return unless $actor->isa('Actor::You'); + + if ($config{dcOnMute} && $actor->{muted}) { + error TF("Auto disconnecting, %s have been muted for %s minutes!\n", $actor, $actor->{mute_period}/60); + chatLog("k", TF("*** %s have been muted for %d minutes, auto disconnect! ***\n", $actor, $actor->{mute_period}/60)); + $messageSender->sendQuit(); + quit(); + } + }, + VAR_HP, sub { + $_[0]{hp} = $_[1]; + }, + VAR_MAXHP, sub { + $_[0]{hp_max} = $_[1]; + }, + VAR_SP, sub { + $_[0]{sp} = $_[1]; + }, + VAR_MAXSP, sub { + $_[0]{sp_max} = $_[1]; + }, + VAR_POINT, sub { $_[0]{points_free} = $_[1] }, + #VAR_HAIRCOLOR + VAR_CLEVEL, sub { + my ($actor, $value) = @_; + + $actor->{lv} = $value; + + message sprintf($actor->verb(T("%s are now level %d\n"), T("%s is now level %d\n")), $actor, $value), "success", $actor->isa('Actor::You') ? 1 : 2; + + return unless $actor->isa('Actor::You'); + + Plugins::callHook('base_level_changed', {level => $actor->{lv}}); + + if ($config{dcOnLevel} && $actor->{lv} >= $config{dcOnLevel}) { + message TF("Disconnecting on level %s!\n", $config{dcOnLevel}); + chatLog("k", TF("Disconnecting on level %s!\n", $config{dcOnLevel})); + quit(); + } + }, + VAR_SPPOINT, sub { $_[0]{points_skill} = $_[1] }, + #VAR_STR + #VAR_AGI + #VAR_VIT + #VAR_INT + #VAR_DEX + #VAR_LUK + #VAR_JOB + VAR_MONEY, sub { + my ($actor, $value) = @_; + + my $change = $value - $actor->{zeny}; + $actor->{zeny} = $value; + + message sprintf( + $change > 0 + ? $actor->verb(T("%s gained %s zeny.\n"), T("%s gained %s zeny.\n")) + : $actor->verb(T("%s lost %s zeny.\n"), T("%s lost %s zeny.\n")), + $actor, formatNumber(abs $change) + ), 'info', $actor->isa('Actor::You') ? 1 : 2 if $change; + + return unless $actor->isa('Actor::You'); + + Plugins::callHook('zeny_change', { + zeny => $actor->{zeny}, + change => $change + }); + + if ($config{dcOnZeny} && $actor->{zeny} <= $config{dcOnZeny}) { + $messageSender->sendQuit(); + error (TF("Auto disconnecting due to zeny lower than %s!\n", $config{dcOnZeny})); + chatLog("k", T("*** You have no money, auto disconnect! ***\n")); + quit(); + } + }, + #VAR_SEX + VAR_MAXEXP, sub { + $_[0]{exp_max_last} = $_[0]{exp_max}; + $_[0]{exp_max_last2} = $_[0]{exp_max} if !$_[0]{exp_max_last2}; + $_[0]{exp_max} = $_[1]; + + if (!$net->clientAlive() && $initSync && $masterServer->{serverType} == 2) { + $messageSender->sendSync(1); + $initSync = 0; + } + }, + VAR_MAXJOBEXP, sub { + $_[0]{exp_job_max_last} = $_[0]{exp_job_max}; + $_[0]{exp_job_max_last2} = $_[0]{exp_job_max} if !$_[0]{exp_job_max_last2}; + $_[0]{exp_job_max} = $_[1]; + #message TF("BaseExp: %s | JobExp: %s\n", $monsterBaseExp, $monsterJobExp), "info", 2 if ($monsterBaseExp); + }, + VAR_WEIGHT, sub { $_[0]{weight} = $_[1] / 10 }, + VAR_MAXWEIGHT, sub { $_[0]{weight_max} = int($_[1] / 10) }, + #VAR_POISON + #VAR_STONE + #VAR_CURSE + #VAR_FREEZING + #VAR_SILENCE + #VAR_CONFUSION + VAR_STANDARD_STR, sub { $_[0]{points_str} = $_[1] }, + VAR_STANDARD_AGI, sub { $_[0]{points_agi} = $_[1] }, + VAR_STANDARD_VIT, sub { $_[0]{points_vit} = $_[1] }, + VAR_STANDARD_INT, sub { $_[0]{points_int} = $_[1] }, + VAR_STANDARD_DEX, sub { $_[0]{points_dex} = $_[1] }, + VAR_STANDARD_LUK, sub { $_[0]{points_luk} = $_[1] }, + #VAR_ATTACKMT + #VAR_ATTACKEDMT + #VAR_NV_BASIC + VAR_ATTPOWER, sub { $_[0]{attack} = $_[1] }, + VAR_REFININGPOWER, sub { $_[0]{attack_bonus} = $_[1] }, + VAR_MAX_MATTPOWER, sub { $_[0]{attack_magic_max} = $_[1] }, + VAR_MIN_MATTPOWER, sub { $_[0]{attack_magic_min} = $_[1] }, + VAR_ITEMDEFPOWER, sub { $_[0]{def} = $_[1] }, + VAR_PLUSDEFPOWER, sub { $_[0]{def_bonus} = $_[1] }, + VAR_MDEFPOWER, sub { $_[0]{def_magic} = $_[1] }, + VAR_PLUSMDEFPOWER, sub { $_[0]{def_magic_bonus} = $_[1] }, + VAR_HITSUCCESSVALUE, sub { $_[0]{hit} = $_[1] }, + VAR_AVOIDSUCCESSVALUE, sub { $_[0]{flee} = $_[1] }, + VAR_PLUSAVOIDSUCCESSVALUE, sub { $_[0]{flee_bonus} = $_[1] }, + VAR_CRITICALSUCCESSVALUE, sub { $_[0]{critical} = $_[1] }, + VAR_ASPD, sub { + $_[0]{attack_delay} = $_[1] >= 10 ? $_[1] : 10; # at least for mercenary + $_[0]{attack_speed} = 200 - $_[0]{attack_delay} / 10; + }, + #VAR_PLUSASPD + VAR_JOBLEVEL, sub { + my ($actor, $value) = @_; + + $actor->{lv_job} = $value; + message sprintf($actor->verb("%s are now job level %d\n", "%s is now job level %d\n"), $actor, $actor->{lv_job}), "success", $actor->isa('Actor::You') ? 1 : 2; + + return unless $actor->isa('Actor::You'); + + Plugins::callHook('job_level_changed', {level => $actor->{lv_job}}); + + if ($config{dcOnJobLevel} && $actor->{lv_job} >= $config{dcOnJobLevel}) { + message TF("Disconnecting on job level %d!\n", $config{dcOnJobLevel}); + chatLog("k", TF("Disconnecting on job level %d!\n", $config{dcOnJobLevel})); + quit(); + } + }, + #... + VAR_MER_KILLCOUNT, sub { $_[0]{kills} = $_[1] }, + VAR_MER_FAITH, sub { $_[0]{faith} = $_[1] }, + #... + VAR_SP_POW, sub { $_[0]{pow} = $_[1] }, + VAR_SP_STA, sub { $_[0]{sta} = $_[1] }, + VAR_SP_WIS, sub { $_[0]{wis} = $_[1] }, + VAR_SP_SPL, sub { $_[0]{spl} = $_[1] }, + VAR_SP_CON, sub { $_[0]{con} = $_[1] }, + VAR_SP_CRT, sub { $_[0]{crt} = $_[1] }, + VAR_SP_PATK, sub { $_[0]{patk} = $_[1] }, + VAR_SP_SMATK, sub { $_[0]{smatk} = $_[1] }, + VAR_SP_RES, sub { $_[0]{res} = $_[1] }, + VAR_SP_MRES, sub { $_[0]{mres} = $_[1] }, + VAR_SP_HPLUS, sub { $_[0]{hplus} = $_[1] }, + VAR_SP_CRATE, sub { $_[0]{crate} = $_[1] }, + VAR_SP_TRAITPOINT, sub { $_[0]{traitpoint} = $_[1] }, + VAR_SP_AP, sub { $_[0]{ap} = $_[1] }, + VAR_SP_MAXAP, sub { $_[0]{ap_max} = $_[1] }, + VAR_SP_UPOW, sub { $_[0]{need_pow} = $_[1] }, + VAR_SP_USTA, sub { $_[0]{need_sta} = $_[1] }, + VAR_SP_UWIS, sub { $_[0]{need_wis} = $_[1] }, + VAR_SP_USPL, sub { $_[0]{need_spl} = $_[1] }, + VAR_SP_UCON, sub { $_[0]{need_con} = $_[1] }, + VAR_SP_UCRT, sub { $_[0]{need_crt} = $_[1] }, +); + +# Notifies client of a character parameter change. +# 00B0 <var id>.W <value>.L (ZC_PAR_CHANGE) +# 00B1 <var id>.W <value>.L (ZC_LONGPAR_CHANGE) +# 00BE <status id>.W <value>.B (ZC_STATUS_CHANGE) +# 0141 <status id>.L <base status>.L <plus status>.L (ZC_COUPLESTATUS) +# 0ACB <var id>.W <value>.Q (ZC_LONGPAR_CHANGE2) +# +# Notifies client of a parameter change of an another player. +# 01AB <account id>.L <var id>.W <value>.L (ZC_PAR_CHANGE_USER) +# +# Notification about a mercenary status parameter change. +# 02A2 <var id>.W <value>.L (ZC_MER_PAR_CHANGE) +# +# Notification about a homunculus status parameter change. +# 07DB <var id>.W <value>.L +sub stat_info { + my ($self, $args) = @_; + + return unless changeToInGameState; + + my $actor = { + '00B0' => $char, + '00B1' => $char, + '00BE' => $char, + '0141' => $char, + '01AB' => exists $args->{ID} && Actor::get($args->{ID}), + '07DB' => $char->{homunculus}, + '0ACB' => $char, + }->{$args->{switch}}; + + if ($args->{switch} eq "081E") { + if (!$char->{elemental}) { + $char->{elemental} = new Actor::Elemental; + } + $actor = $char->{elemental}; # Sorcerer's Spirit + } + + if ($args->{switch} eq "02A2") { + if (!$char->{mercenary}) { + $char->{mercenary} = new Actor::Slave::Mercenary; + } + $actor = $char->{mercenary}; + } + + unless ($actor) { + warning sprintf "Actor is unknown or not ready for stat information (switch %s, type %d, val %d)\n", @{$args}{qw(switch type val)}; + return; + } + + if (exists $stat_info_handlers{$args->{type}}) { + # TODO: introduce Actor->something() to determine per-actor configurable verbosity level? (not only here) + debug "Stat: $args->{type} => $args->{val}\n", "parseMsg", $_[0]->isa('Actor::You') ? 1 : 2; + $stat_info_handlers{$args->{type}}($actor, $args->{val}); + } else { + warning sprintf "Unknown stat (%d => %d) received for %s\n", @{$args}{qw(type val)}, $actor; + } + + if (!$char->{walk_speed}) { + $char->{walk_speed} = 0.15; # This is the default speed, since xkore requires this and eA (And aegis?) do not send this if its default speed + } +} + +# TODO: merge with stat_info +# Notifies the client, about the result of an status change request (ZC_STATUS_CHANGE_ACK). +# 00BC <status id>.W <result>.B <value>.B +# result: +# 0 = failure +# 1 = success +sub stats_added { + my ($self, $args) = @_; + + if ($args->{val} == 207) { # client really checks this and not the result field? + error T("Not enough stat points to add\n"); + } else { + if ($args->{type} == VAR_STR) { + $char->{str} = $args->{val}; + debug "Strength: $args->{val}\n", "parseMsg"; + + } elsif ($args->{type} == VAR_AGI) { + $char->{agi} = $args->{val}; + debug "Agility: $args->{val}\n", "parseMsg"; + + } elsif ($args->{type} == VAR_VIT) { + $char->{vit} = $args->{val}; + debug "Vitality: $args->{val}\n", "parseMsg"; + + } elsif ($args->{type} == VAR_INT) { + $char->{int} = $args->{val}; + debug "Intelligence: $args->{val}\n", "parseMsg"; + + } elsif ($args->{type} == VAR_DEX) { + $char->{dex} = $args->{val}; + debug "Dexterity: $args->{val}\n", "parseMsg"; + + } elsif ($args->{type} == VAR_LUK) { + $char->{luk} = $args->{val}; + debug "Luck: $args->{val}\n", "parseMsg"; + + } elsif ($args->{type} == VAR_SP_POW) { + $char->{pow} = $args->{val}; + debug "Power: $args->{val}\n", "parseMsg"; + + } elsif ($args->{type} == VAR_SP_STA) { + $char->{sta} = $args->{val}; + debug "Stamina: $args->{val}\n", "parseMsg"; + + } elsif ($args->{type} == VAR_SP_WIS) { + $char->{wis} = $args->{val}; + debug "Wisdom: $args->{val}\n", "parseMsg"; + + } elsif ($args->{type} == VAR_SP_SPL) { + $char->{spl} = $args->{val}; + debug "Spell: $args->{val}\n", "parseMsg"; + + } elsif ($args->{type} == VAR_SP_CON) { + $char->{con} = $args->{val}; + debug "Concentration: $args->{val}\n", "parseMsg"; + + } elsif ($args->{type} == VAR_SP_CRT) { + $char->{crt} = $args->{val}; + debug "Creative: $args->{val}\n", "parseMsg"; + + } else { + error "$args->{type}: $args->{val}\n", "parseMsg"; + } + } + Plugins::callHook('packet_charStats', { + type => $args->{type}, + val => $args->{val} + }); +} + +# Character status (ZC_STATUS). +# 00BD <stpoint>.W <str>.B <need str>.B <agi>.B <need agi>.B <vit>.B <need vit>.B <int>.B <need int>.B <dex>.B <need dex>.B <luk>.B <need luk>.B +# <atk>.W <atk2>.W <matk min>.W <matk max>.W <def>.W <def2>.W <mdef>.W <mdef2>.W <hit>.W <flee>.W <flee2>.W <crit>.W <aspd>.W <aspd2>.W +sub stats_info { + my ($self, $args) = @_; + return unless changeToInGameState(); + $char->{points_free} = $args->{points_free}; + $char->{str} = $args->{str}; + $char->{points_str} = $args->{points_str}; + $char->{agi} = $args->{agi}; + $char->{points_agi} = $args->{points_agi}; + $char->{vit} = $args->{vit}; + $char->{points_vit} = $args->{points_vit}; + $char->{int} = $args->{int}; + $char->{points_int} = $args->{points_int}; + $char->{dex} = $args->{dex}; + $char->{points_dex} = $args->{points_dex}; + $char->{luk} = $args->{luk}; + $char->{points_luk} = $args->{points_luk}; + $char->{attack} = $args->{attack}; + $char->{attack_bonus} = $args->{attack_bonus}; + $char->{attack_magic_min} = $args->{attack_magic_min}; + $char->{attack_magic_max} = $args->{attack_magic_max}; + $char->{def} = $args->{def}; + $char->{def_bonus} = $args->{def_bonus}; + $char->{def_magic} = $args->{def_magic}; + $char->{def_magic_bonus} = $args->{def_magic_bonus}; + $char->{hit} = $args->{hit}; + $char->{flee} = $args->{flee}; + $char->{flee_bonus} = $args->{flee_bonus}; + $char->{critical} = $args->{critical}; + debug "Strength: $char->{str} #$char->{points_str}\n" + ."Agility: $char->{agi} #$char->{points_agi}\n" + ."Vitality: $char->{vit} #$char->{points_vit}\n" + ."Intelligence: $char->{int} #$char->{points_int}\n" + ."Dexterity: $char->{dex} #$char->{points_dex}\n" + ."Luck: $char->{luk} #$char->{points_luk}\n" + ."Attack: $char->{attack}\n" + ."Attack Bonus: $char->{attack_bonus}\n" + ."Magic Attack Min: $char->{attack_magic_min}\n" + ."Magic Attack Max: $char->{attack_magic_max}\n" + ."Defense: $char->{def}\n" + ."Defense Bonus: $char->{def_bonus}\n" + ."Magic Defense: $char->{def_magic}\n" + ."Magic Defense Bonus: $char->{def_magic_bonus}\n" + ."Hit: $char->{hit}\n" + ."Flee: $char->{flee}\n" + ."Flee Bonus: $char->{flee_bonus}\n" + ."Critical: $char->{critical}\n" + ."Status Points: $char->{points_free}\n", "parseMsg"; +} + +# Notifies client of a character parameter change. +# 0141 <status id>.L <base status>.L <plus status>.L (ZC_COUPLESTATUS) +sub stat_info2 { + my ($self, $args) = @_; + return unless changeToInGameState(); + my ($type, $val, $val2) = @{$args}{qw(type val val2)}; + if ($type == VAR_STR) { + $char->{str} = $val; + $char->{str_bonus} = $val2; + debug "Strength: $val + $val2\n", "parseMsg"; + } elsif ($type == VAR_AGI) { + $char->{agi} = $val; + $char->{agi_bonus} = $val2; + debug "Agility: $val + $val2\n", "parseMsg"; + } elsif ($type == VAR_VIT) { + $char->{vit} = $val; + $char->{vit_bonus} = $val2; + debug "Vitality: $val + $val2\n", "parseMsg"; + } elsif ($type == VAR_INT) { + $char->{int} = $val; + $char->{int_bonus} = $val2; + debug "Intelligence: $val + $val2\n", "parseMsg"; + } elsif ($type == VAR_DEX) { + $char->{dex} = $val; + $char->{dex_bonus} = $val2; + debug "Dexterity: $val + $val2\n", "parseMsg"; + } elsif ($type == VAR_LUK) { + $char->{luk} = $val; + $char->{luk_bonus} = $val2; + debug "Luck: $val + $val2\n", "parseMsg"; + } + $char->inventory->onStatInfo2() if (!$masterServer->{itemListType}); + +} +# Notifies clients in an area, that an other visible object is walking (ZC_NOTIFY_PLAYERMOVE). +# 0086 <id>.L <walk data>.6B <walk start time>.L +# Note: unit must not be self +*actor_exists = *actor_display_compatibility; +*actor_connected = *actor_display_compatibility; +*actor_moved = *actor_display_compatibility; +*actor_spawned = *actor_display_compatibility; +sub actor_display_compatibility { + my ($self, $args) = @_; + # compatibility; TODO do it in PacketParser->parse? + Plugins::callHook('packet_pre/actor_display', $args); + &actor_display unless $args->{return}; + Plugins::callHook('packet/actor_display', $args); +} + +# This function is a merge of actor_exists, actor_connected, actor_moved, etc... +sub actor_display { + my ($self, $args) = @_; + return unless changeToInGameState(); + my ($actor, $mustAdd); + + #### Initialize #### + + my $nameID = unpack("V", $args->{ID}); + my $name = bytesToString($args->{name}); + $name =~ s/^\s+|\s+$//g; + + if ($args->{switch} eq "0086") { + # Message 0086 contains less information about the actor than other similar + # messages. So we use the existing actor information. + my $coordsArg = $args->{coords}; + my $tickArg = $args->{tick}; + $args = Actor::get($args->{ID})->deepCopy(); + # Here we overwrite the $args data with the 0086 packet data. + $args->{switch} = "0086"; + $args->{coords} = $coordsArg; + $args->{tick} = $tickArg; # lol tickcount what do we do with that? debug "tick: " . $tickArg/1000/3600/24 . "\n"; + } + + my (%coordsFrom, %coordsTo); + if (length $args->{coords} == 6) { + # Actor Moved + makeCoordsFromTo(\%coordsFrom, \%coordsTo, $args->{coords}); # body dir will be calculated using the vector + } else { + # Actor Spawned/Exists + makeCoordsDir(\%coordsTo, $args->{coords}, \$args->{body_dir}); + %coordsFrom = %coordsTo; + } + +=pod + # Zealotus bug + if ($args->{type} == 1200) { + open DUMP, ">> test_Zealotus.txt"; + print DUMP "Zealotus: " . $nameID . "\n"; + print DUMP Dumper($args); + close DUMP; + } +=cut + + #### Step 0: determine object type #### + my $object_class; + if (defined $args->{object_type}) { + if ($args->{type} == 45) { # portals use the same object_type as NPCs + $object_class = 'Actor::Portal'; + } else { + $object_class = { + PC_TYPE, 'Actor::Player', + # NPC_TYPE? # not encountered, NPCs are NPC_EVT_TYPE + # SKILL_TYPE? # not encountered + # UNKNOWN_TYPE? # not encountered + NPC_MOB_TYPE, 'Actor::Monster', + NPC_EVT_TYPE, 'Actor::NPC', # both NPCs and portals + NPC_PET_TYPE, 'Actor::Pet', + NPC_HO_TYPE, 'Actor::Slave::Homunculus', + NPC_MERSOL_TYPE, 'Actor::Slave::Mercenary', + # NPC_ELEMENTAL_TYPE, 'Actor::Elemental', # Sorcerer's Spirit + NPC_TYPE2, 'Actor::NPC', + }->{$args->{object_type}}; + } + + } + + unless (defined $object_class) { + if ($jobs_lut{$args->{type}}) { + if ($args->{type} <= 6000) { + $object_class = 'Actor::Player'; + } elsif (($args->{type} >= 6001 && $args->{type} <= 6016) || ($args->{type} >= 6048 && $args->{type} <= 6052)) { + $object_class = 'Actor::Slave::Homunculus'; + } elsif ($$args->{type} >= 6017 && $$args->{type} <= 6046) { + $object_class = 'Actor::Slave::Mercenary'; + } else { + $object_class = 'Actor::Slave::Unknown'; + } + } elsif ($args->{type} == 45) { + $object_class = 'Actor::Portal'; + + } elsif ($args->{type} >= 1000) { + if ($args->{hair_style} == 0x64) { + $object_class = 'Actor::Pet'; + } else { + $object_class = 'Actor::Monster'; + } + } else { # ($args->{type} < 1000 && $args->{type} != 45 && !$jobs_lut{$args->{type}}) + $object_class = 'Actor::NPC'; + } + } + + #### Step 1: create the actor object #### + + if ($object_class eq 'Actor::Player') { + # Actor is a player + $actor = $playersList->getByID($args->{ID}); + if (!defined $actor) { + $actor = new Actor::Player(); + $actor->{appear_time} = time; + # New actor_display packets include the player's name + $actor->setName($name) if defined $name; + $mustAdd = 1; + } + $actor->{nameID} = $nameID; + } elsif ($object_class eq 'Actor::Slave') { + require ErrorHandler; + die "Unset Actor::Slave type, this shouldn't happen\n"; + } elsif ($object_class eq 'Actor::Slave::Homunculus' || $object_class eq 'Actor::Slave::Mercenary' || $object_class eq 'Actor::Slave::Unknown') { + # Actor is a homunculus or a mercenary + $actor = $slavesList->getByID($args->{ID}); + if (!defined $actor) { + if ($char->{slaves} && $char->{slaves}{$args->{ID}}) { + $actor = $char->{slaves}{$args->{ID}}; + } elsif ($char->{homunculus} && $char->{homunculus}{ID} && $char->{homunculus}{ID} eq $args->{ID}) { + $actor = $char->{homunculus}; + } elsif ($char->{mercenary} && ($char->{mercenary}{ID} && $char->{mercenary}{ID} eq $args->{ID})) { + $actor = $char->{mercenary}; + } else { + $actor = $object_class->new(); + } + + $actor->{appear_time} = time; + $actor->{name_given} = $name if defined $name; + $actor->{jobID} = $args->{type} if exists $args->{type}; + $mustAdd = 1; + } + $actor->{nameID} = $nameID; + } elsif ($object_class eq 'Actor::Portal') { + # Actor is a portal + $actor = $portalsList->getByID($args->{ID}); + if (!defined $actor) { + $actor = new Actor::Portal(); + $actor->{appear_time} = time; + my $exists = portalExists($field->baseName, \%coordsTo); + $actor->{source}{map} = $field->baseName; + if ($exists ne "") { + $actor->setName("$portals_lut{$exists}{source}{map} -> " . getPortalDestName($exists)); + } + $mustAdd = 1; + + # Strangely enough, portals (like all other actors) have names, too. + # We _could_ send a "actor_info_request" packet to find the names of each portal, + # however I see no gain from this. (And it might even provide another way of private + # servers to auto-ban bots.) + } + $actor->{nameID} = $nameID; + } elsif ($object_class eq 'Actor::Pet') { + # Actor is a pet + $actor = $petsList->getByID($args->{ID}); + if (!defined $actor) { + $actor = new Actor::Pet(); + $actor->{appear_time} = time; + $actor->setName($name) if defined $name; +# if ($monsters_lut{$args->{type}}) { +# $actor->setName($monsters_lut{$args->{type}}); +# } + $actor->{name_given} = defined $name ? $name : T("Unknown"); + $mustAdd = 1; + + # Previously identified monsters could suddenly be identified as pets. + if ($monstersList->getByID($args->{ID})) { + $monstersList->removeByID($args->{ID}); + } + + # Why do monsters and pets use nameID as type? + $actor->{nameID} = $args->{type}; + + } + } elsif ($object_class eq 'Actor::Monster') { + $actor = $monstersList->getByID($args->{ID}); + if (!defined $actor) { + $actor = new Actor::Monster(); + $actor->{appear_time} = time; + if ($monsters_lut{$args->{type}}) { + $actor->setName($monsters_lut{$args->{type}}); + } + # New actor_display packets include the Monster name + $actor->setName($name) if defined $name; + $actor->{name_given} = "Unknown"; + $actor->{binType} = $args->{type}; + $mustAdd = 1; + + # Why do monsters and pets use nameID as type? + $actor->{nameID} = $args->{type}; + } + } elsif ($object_class eq 'Actor::NPC') { + # Actor is an NPC + $actor = $npcsList->getByID($args->{ID}); + if (!defined $actor) { + $actor = new Actor::NPC(); + $actor->{appear_time} = time; + $actor->setName($name) if defined $name; + $mustAdd = 1; + } + $actor->{nameID} = $nameID; + } elsif ($object_class eq 'Actor::Elemental') { + # Actor is a Elemental + $actor = $elementalsList->getByID($args->{ID}); + if (!defined $actor) { + $actor = new Actor::Elemental(); + $actor->{appear_time} = time; + $mustAdd = 1; + } + + $actor->setName($jobs_lut{$args->{type}}); + } + + #### Step 2: update actor information #### + $actor->{ID} = $args->{ID}; + $actor->{charID} = $args->{charID} if $args->{charID} && $args->{charID} ne "\0\0\0\0"; + $actor->{jobID} = $args->{type}; + $actor->{type} = $args->{type}; + $actor->{lv} = $args->{lv}; + $actor->{walk_speed} = $args->{walk_speed} / 1000 if (exists $args->{walk_speed} && $args->{switch} ne "0086"); + $actor->{len} = $args->{len} if $args->{len}; + # 0086 would need that? + $actor->{object_type} = $args->{object_type} if (defined $args->{object_type}); + + # Remove actors that are located outside the map + # This may be caused by: + # - server sending us false actors + # - actor packets not being parsed correctly + if (defined $field && ($field->isOffMap($coordsFrom{x}, $coordsFrom{y}) || $field->isOffMap($coordsTo{x}, $coordsTo{y}))) { + warning TF("Ignoring actor with off map coordinates: (%d, %d)->(%d, %d), field max: (%d, %d)\n",$coordsFrom{x},$coordsFrom{y},$coordsTo{x},$coordsTo{y},$field->width(),$field->height()); + $actor->{avoid} = 1; + return; + } + + if ( ($coordsFrom{x} == 0 && $coordsFrom{y} == 0) || ($coordsTo{x} == 0 && $coordsTo{y} == 0) || + (blockDistance(\%coordsFrom, \%coordsTo) > $config{clientSight}) ) { + warning TF("Ignoring bugged actor moved packet (%s) (%d, %d)->(%d, %d)\n", $args->{switch}, $coordsFrom{x}, $coordsFrom{y}, $coordsTo{x}, $coordsTo{y}); + # seems this is just a position bug, lets just ignore the change in position + # $actor->{avoid} = 1; + return; + } + + $actor->{pos} = {%coordsFrom}; + $actor->{pos_to} = {%coordsTo}; + $actor->{time_move} = time; + $actor->{time_move_calc} = calcTime(\%coordsFrom, \%coordsTo, $actor->{walk_speed}); + + + if (UNIVERSAL::isa($actor, "Actor::Player")) { + # None of this stuff should matter if the actor isn't a player... => does matter for a guildflag npc! + + # Interesting note about emblemID. If it is 0 (or none), the Ragnarok + # client will display "Send (Player) a guild invitation" (assuming one has + # invitation priveledges), regardless of whether or not guildID is set. + # I bet that this is yet another brilliant "feature" by GRAVITY's good programmers. + $actor->{emblemID} = $args->{emblemID} if (exists $args->{emblemID}); + $actor->{guildID} = $args->{guildID} if (exists $args->{guildID}); + + if (exists $args->{lowhead}) { + $actor->{headgear}{low} = $args->{lowhead}; + $actor->{headgear}{mid} = $args->{midhead}; + $actor->{headgear}{top} = $args->{tophead}; + $actor->{weapon} = $args->{weapon}; + $actor->{shield} = $args->{shield}; + } + + $actor->{sex} = $args->{sex}; + + if ($args->{act} == 1) { + $actor->{dead} = 1; + } elsif ($args->{act} == 2) { + $actor->{sitting} = 1; + } + + # Monsters don't have hair colors or heads to look around... + $actor->{hair_color} = $args->{hair_color} if (exists $args->{hair_color}); + + } elsif (UNIVERSAL::isa($actor, "Actor::NPC") && $args->{type} == 722) { # guild flag has emblem + # odd fact: "this data can also be found in a strange place: + # (shield OR lowhead) + midhead = emblemID (either shield or lowhead depending on the packet) + # tophead = guildID + $actor->{emblemID} = $args->{emblemID}; + $actor->{guildID} = $args->{guildID}; + } + + # But hair_style is used for pets, and their bodies can look different ways... + $actor->{hair_style} = $args->{hair_style} if (exists $args->{hair_style}); + $actor->{look}{body} = $args->{body_dir} if (exists $args->{body_dir}); + $actor->{look}{head} = $args->{head_dir} if (exists $args->{head_dir}); + + # When stance is non-zero, character is bobbing as if they had just got hit, + # but the cursor also turns to a sword when they are mouse-overed. + #$actor->{stance} = $args->{stance} if (exists $args->{stance}); + + # Visual effects are a set of flags (some of the packets don't have this argument) + $actor->{opt3} = $args->{opt3} if (exists $args->{opt3}); # stackable + + # Known visual effects: + # 0x0001 = Yellow tint (eg, a quicken skill) + # 0x0002 = Red tint (eg, power-thrust) + # 0x0004 = Gray tint (eg, energy coat) + # 0x0008 = Slow lightning (eg, mental strength) + # 0x0010 = Fast lightning (eg, MVP fury) + # 0x0020 = Black non-moving statue (eg, stone curse) + # 0x0040 = Translucent weapon + # 0x0080 = Translucent red sprite (eg, marionette control?) + # 0x0100 = Spaztastic weapon image (eg, mystical amplification) + # 0x0200 = Gigantic glowy sphere-thing + # 0x0400 = Translucent pink sprite (eg, marionette control?) + # 0x0800 = Glowy sprite outline (eg, assumptio) + # 0x1000 = Bright red sprite, slowly moving red lightning (eg, MVP fury?) + # 0x2000 = Vortex-type effect + + # Note that these are flags, and you can mix and match them + # Example: 0x000C (0x0008 & 0x0004) = gray tint with slow lightning + +=pod +typedef enum <unnamed-tag> { + SHOW_EFST_NORMAL = 0x0, + SHOW_EFST_QUICKEN = 0x1, + SHOW_EFST_OVERTHRUST = 0x2, + SHOW_EFST_ENERGYCOAT = 0x4, + SHOW_EFST_EXPLOSIONSPIRITS = 0x8, + SHOW_EFST_STEELBODY = 0x10, + SHOW_EFST_BLADESTOP = 0x20, + SHOW_EFST_AURABLADE = 0x40, + SHOW_EFST_REDBODY = 0x80, + SHOW_EFST_LIGHTBLADE = 0x100, + SHOW_EFST_MOON = 0x200, + SHOW_EFST_PINKBODY = 0x400, + SHOW_EFST_ASSUMPTIO = 0x800, + SHOW_EFST_SUN_WARM = 0x1000, + SHOW_EFST_REFLECT = 0x2000, + SHOW_EFST_BUNSIN = 0x4000, + SHOW_EFST_SOULLINK = 0x8000, + SHOW_EFST_UNDEAD = 0x10000, + SHOW_EFST_CONTRACT = 0x20000, +} <unnamed-tag>; +=cut + + # Save these parameters ... + $actor->{opt1} = $args->{opt1}; # nonstackable + $actor->{opt2} = $args->{opt2}; # stackable + $actor->{option} = $args->{option}; # stackable + + # And use them to set status flags. + if (setStatus($actor, $args->{opt1}, $args->{opt2}, $args->{option})) { + $mustAdd = 0; + } + + #### Step 3: Add actor to actor list #### + if ($mustAdd) { + if (UNIVERSAL::isa($actor, "Actor::Player")) { + $playersList->add($actor); + Plugins::callHook('add_player_list', $actor); + + } elsif (UNIVERSAL::isa($actor, "Actor::Monster")) { + $monstersList->add($actor); + Plugins::callHook('add_monster_list', $actor); + + } elsif (UNIVERSAL::isa($actor, "Actor::Pet")) { + $petsList->add($actor); + Plugins::callHook('add_pet_list', $actor); + + } elsif (UNIVERSAL::isa($actor, "Actor::Portal")) { + $portalsList->add($actor); + Plugins::callHook('add_portal_list', $actor); + + } elsif (UNIVERSAL::isa($actor, "Actor::NPC")) { + my $ID = $args->{ID}; + my $location = $field->baseName . " $actor->{pos}{x} $actor->{pos}{y}"; + if ($npcs_lut{$location}) { + $actor->setName($npcs_lut{$location}); + } + $npcsList->add($actor); + Plugins::callHook('add_npc_list', $actor); + + } elsif (UNIVERSAL::isa($actor, "Actor::Slave")) { + $slavesList->add($actor); + Plugins::callHook('add_slave_list', $actor); + } elsif (UNIVERSAL::isa($actor, "Actor::Elemental")) { + $elementalsList->add($actor); + Plugins::callHook('add_elemental_list', $actor); + + } + } + + #### Packet specific #### + if ($args->{switch} eq "0078" || + $args->{switch} eq "01D8" || + $args->{switch} eq "022A" || + $args->{switch} eq "02EE" || + $args->{switch} eq "07F9" || + $args->{switch} eq "0915" || + $args->{switch} eq "09DD" || + $args->{switch} eq "09FF" || + $packetParser->{packet_list}->{$args->{switch}}[0] eq "actor_exists") { + # Actor Exists (standing) + + if ($actor->isa('Actor::Player')) { + my $domain = existsInList($config{friendlyAID}, unpack("V", $actor->{ID})) ? 'parseMsg_presence' : 'parseMsg_presence/player'; + debug "Player Exists: " . $actor->name . " ($actor->{binID}) Level $actor->{lv} $sex_lut{$actor->{sex}} $jobs_lut{$actor->{jobID}} ($coordsFrom{x}, $coordsFrom{y})\n", $domain; + + playerLog("player " .$actor->{name} ." is near (" .$field->{baseName} .", lvl=" .$actor->{lv} .", job=" .$jobs_lut{$actor->{jobID}} .")") if (!$field->isCity); + Plugins::callHook('player', {player => $actor}); #backwards compatibility + + Plugins::callHook('player_exist', {player => $actor}); + + } elsif ($actor->isa('Actor::NPC')) { + message TF("NPC Exists: %s (%d, %d) (ID %d) - (%d)\n", $actor->name, $actor->{pos_to}{x}, $actor->{pos_to}{y}, $actor->{nameID}, $actor->{binID}), ($config{showDomain_NPC}?$config{showDomain_NPC}:"parseMsg_presence"), 1; + Plugins::callHook('npc_exist', {npc => $actor}); + + } elsif ($actor->isa('Actor::Portal')) { + message TF("Portal Exists: %s (%s, %s) - (%s)\n", $actor->name, $actor->{pos_to}{x}, $actor->{pos_to}{y}, $actor->{binID}), "portals", 1; + Plugins::callHook('portal_exist', {portal => $actor}); + + } elsif ($actor->isa('Actor::Monster')) { + debug sprintf("Monster Exists: %s (%d)\n", $actor->name, $actor->{binID}), "parseMsg_presence", 1; + Plugins::callHook('monster_exist', {monster => $actor}); + + } elsif ($actor->isa('Actor::Pet')) { + debug sprintf("Pet Exists: %s (%d)\n", $actor->name, $actor->{binID}), "parseMsg_presence", 1; + Plugins::callHook('pet_exist', {pet => $actor}); + + } elsif ($actor->isa('Actor::Slave')) { + debug sprintf("Slave Exists: %s (%d)\n", $actor->name, $actor->{binID}), "parseMsg_presence", 1; + Plugins::callHook('slave_exist', {slave => $actor}); + + } elsif ($actor->isa('Actor::Elemental')) { + debug sprintf("Elemental Exists: %s (%d)\n", $actor->name, $actor->{binID}), "parseMsg_presence", 1; + Plugins::callHook('elemental_exist', {elemental => $actor}); + + } else { + debug sprintf("Unknown Actor Exists: %s (%d)\n", $actor->name, $actor->{binID}), "parseMsg_presence", 1; + Plugins::callHook('unknown_exist', {unknown => $actor}); + } + + } elsif ($args->{switch} eq "0079" || + $args->{switch} eq "01DB" || + $args->{switch} eq "022B" || + $args->{switch} eq "02ED" || + $args->{switch} eq "01D9" || + $args->{switch} eq "07F8" || + $args->{switch} eq "0858" || + $args->{switch} eq "090F" || + $args->{switch} eq "09DC" || + $args->{switch} eq "09FE" || + $packetParser->{packet_list}->{$args->{switch}}[0] eq "actor_connected") { + # Actor Connected (new) + + if ($actor->isa('Actor::Player')) { + my $domain = existsInList($config{friendlyAID}, unpack("V", $args->{ID})) ? 'parseMsg_presence' : 'parseMsg_presence/player'; + debug "Player Connected: ".$actor->name." ($actor->{binID}) Level $args->{lv} $sex_lut{$actor->{sex}} $jobs_lut{$actor->{jobID}} ($coordsTo{x}, $coordsTo{y})\n", $domain; + + playerLog("player " .$actor->{name} ." is near (" .$field->{baseName} .", lvl=" .$actor->{lv} .", job=" .$jobs_lut{$actor->{jobID}} .")") if (!$field->isCity); + Plugins::callHook('player', {player => $actor}); #backwards compatibailty + + Plugins::callHook('player_connected', {player => $actor}); + } else { + debug "Unknown Connected: $args->{type} - \n", "parseMsg"; + Plugins::callHook('unknown_connected', {unknown => $actor}); + } + + } elsif ($args->{switch} eq "007B" || + $args->{switch} eq "0086" || + $args->{switch} eq "01DA" || + $args->{switch} eq "022C" || + $args->{switch} eq "02EC" || + $args->{switch} eq "07F7" || + $args->{switch} eq "0856" || + $args->{switch} eq "0914" || + $args->{switch} eq "09DB" || + $args->{switch} eq "09FD" || + $packetParser->{packet_list}->{$args->{switch}}[0] eq "actor_moved") { + # Actor Moved + + # Correct the direction in which they're looking + my %vec; + getVector(\%vec, \%coordsTo, \%coordsFrom); + my $direction = int sprintf("%.0f", (360 - vectorToDegree(\%vec)) / 45); + + $actor->{look}{body} = $direction; + $actor->{look}{head} = 0; + + if ($actor->isa('Actor::Player')) { + debug "Player Moved: " . $actor->name . " ($actor->{binID}) Level $actor->{lv} $sex_lut{$actor->{sex}} $jobs_lut{$actor->{jobID}} - ($coordsFrom{x}, $coordsFrom{y}) -> ($coordsTo{x}, $coordsTo{y})\n", "parseMsg"; + Plugins::callHook('player_moved', $actor); + } elsif ($actor->isa('Actor::Monster')) { + debug "Monster Moved: " . $actor->nameIdx . " - ($coordsFrom{x}, $coordsFrom{y}) -> ($coordsTo{x}, $coordsTo{y})\n", "parseMsg"; + Plugins::callHook('monster_moved', $actor); + } elsif ($actor->isa('Actor::Pet')) { + debug "Pet Moved: " . $actor->nameIdx . " - ($coordsFrom{x}, $coordsFrom{y}) -> ($coordsTo{x}, $coordsTo{y})\n", "parseMsg"; + Plugins::callHook('pet_moved', $actor); + } elsif ($actor->isa('Actor::Slave')) { + debug "Slave Moved: " . $actor->nameIdx . " - ($coordsFrom{x}, $coordsFrom{y}) -> ($coordsTo{x}, $coordsTo{y})\n", "parseMsg"; + Plugins::callHook('slave_moved', $actor); + } elsif ($actor->isa('Actor::Portal')) { + # This can never happen of course. + debug "Portal Moved: " . $actor->nameIdx . " - ($coordsFrom{x}, $coordsFrom{y}) -> ($coordsTo{x}, $coordsTo{y})\n", "parseMsg"; + Plugins::callHook('portal_moved', $actor); + } elsif ($actor->isa('Actor::NPC')) { + # Neither can this. + debug "NPC Moved: " . $actor->nameIdx . " - ($coordsFrom{x}, $coordsFrom{y}) -> ($coordsTo{x}, $coordsTo{y})\n", "parseMsg"; + Plugins::callHook('npc_moved', $actor); + } elsif ($actor->isa('Actor::Elemental')) { + debug "Elemental Moved: " . $actor->nameIdx . " - ($coordsFrom{x}, $coordsFrom{y}) -> ($coordsTo{x}, $coordsTo{y})\n", "parseMsg"; + Plugins::callHook('pet_moved', $actor); + } else { + debug "Unknown Actor Moved: " . $actor->nameIdx . " - ($coordsFrom{x}, $coordsFrom{y}) -> ($coordsTo{x}, $coordsTo{y})\n", "parseMsg"; + Plugins::callHook('unknown_moved', $actor); + } + + } elsif ($args->{switch} eq "007C") { + # Actor Spawned + if ($actor->isa('Actor::Player')) { + debug "Player Spawned: " . $actor->nameIdx . " $sex_lut{$actor->{sex}} $jobs_lut{$actor->{jobID}}\n", "parseMsg"; + Plugins::callHook('player_spawned', {player => $actor}); + } elsif ($actor->isa('Actor::Monster')) { + debug "Monster Spawned: " . $actor->nameIdx . "\n", "parseMsg"; + Plugins::callHook('monster_spawned', {monster => $actor}); + } elsif ($actor->isa('Actor::Pet')) { + debug "Pet Spawned: " . $actor->nameIdx . "\n", "parseMsg"; + Plugins::callHook('pet_spawned', {pet => $actor}); + } elsif ($actor->isa('Actor::Slave')) { + debug "Slave Spawned: " . $actor->nameIdx . " $jobs_lut{$actor->{jobID}}\n", "parseMsg"; + Plugins::callHook('slave_spawned', {slave => $actor}); + } elsif ($actor->isa('Actor::Portal')) { + # Can this happen? + debug "Portal Spawned: " . $actor->nameIdx . "\n", "parseMsg"; + Plugins::callHook('portal_spawned', {portal => $actor}); + } elsif ($actor->isa('Actor::Elemental')) { + debug "Elemental Spawned: " . $actor->nameIdx . "\n", "parseMsg"; + Plugins::callHook('elemental_spawned', {elemental => $actor}); + } elsif ($actor->isa('NPC')) { + debug "NPC Spawned: " . $actor->nameIdx . "\n", "parseMsg"; + Plugins::callHook('npc_spawned', {npc => $actor}); + } else { + debug "Unknown Spawned: " . $actor->nameIdx . "\n", "parseMsg"; + Plugins::callHook('unknown_spawned', {unknown => $actor}); + } + } + + if ($char->{elemental}{ID} eq $actor->{ID}) { + $char->{elemental} = $actor; + } +} + +# Makes a unit (char, npc, mob, homun) disappear to all clients in area (ZC_NOTIFY_VANISH). +# 0080 <id>.L <type>.B +# type: +# 0 = out of sight +# 1 = died +# 2 = logged out +# 3 = teleport +# 4 = trickdead +sub actor_died_or_disappeared { + my ($self,$args) = @_; + return unless changeToInGameState(); + my $ID = $args->{ID}; + avoidList_ID($ID); + + if ($ID eq $accountID) { + message T("You have died\n") if (!$char->{dead}); + Plugins::callHook('self_died'); + closeShop() unless !$shopstarted || $config{'dcOnDeath'} == -1 || AI::state == AI::OFF; + $char->{deathCount}++; + $char->{dead} = 1; + $char->{dead_time} = time; + if ($char->{equipment}{arrow} && $char->{equipment}{arrow}{type} == 19) { + delete $char->{equipment}{arrow}; + } + + } elsif (defined $monstersList->getByID($ID)) { + my $monster = $monstersList->getByID($ID); + if ($args->{type} == 0) { + debug "Monster Disappeared: " . $monster->name . " ($monster->{binID})\n", "parseMsg_presence"; + $monster->{disappeared} = 1; + + } elsif ($args->{type} == 1) { + debug "Monster Died: " . $monster->name . " ($monster->{binID})\n", "parseMsg_damage"; + $monster->{dead} = 1; + + if ((AI::action ne "attack" || AI::args(0)->{ID} eq $ID) && + ($config{itemsTakeAuto_party} && + ($monster->{dmgFromParty} > 0 || + $monster->{dmgFromYou} > 0))) { + AI::clear("items_take"); + ai_items_take($monster->{pos}{x}, $monster->{pos}{y}, + $monster->{pos_to}{x}, $monster->{pos_to}{y}); + } + + } elsif ($args->{type} == 2) { # What's this? + debug "Monster Disappeared: " . $monster->name . " ($monster->{binID})\n", "parseMsg_presence"; + $monster->{disappeared} = 1; + + } elsif ($args->{type} == 3) { + debug "Monster Teleported: " . $monster->name . " ($monster->{binID})\n", "parseMsg_presence"; + $monster->{teleported} = 1; + } + + $monster->{gone_time} = time; + $monsters_old{$ID} = $monster->deepCopy(); + Plugins::callHook('monster_disappeared', {monster => $monster}); + $monstersList->remove($monster); + + } elsif (defined $playersList->getByID($ID)) { + my $player = $playersList->getByID($ID); + if ($args->{type} == 1) { + message TF("Player Died: %s (%d) %s %s\n", $player->name, $player->{binID}, $sex_lut{$player->{sex}}, $jobs_lut{$player->{jobID}}); + $player->{dead} = 1; + $player->{dead_time} = time; + } else { + if ($args->{type} == 0) { + debug "Player Disappeared: " . $player->name . " ($player->{binID}) $sex_lut{$player->{sex}} $jobs_lut{$player->{jobID}} ($player->{pos_to}{x}, $player->{pos_to}{y})\n", "parseMsg_presence"; + $player->{disappeared} = 1; + } elsif ($args->{type} == 2) { + debug "Player Disconnected: ".$player->name." ($player->{binID}) $sex_lut{$player->{sex}} $jobs_lut{$player->{jobID}} ($player->{pos_to}{x}, $player->{pos_to}{y})\n", "parseMsg_presence"; + $player->{disconnected} = 1; + } elsif ($args->{type} == 3) { + debug "Player Teleported: ".$player->name." ($player->{binID}) $sex_lut{$player->{sex}} $jobs_lut{$player->{jobID}} ($player->{pos_to}{x}, $player->{pos_to}{y})\n", "parseMsg_presence"; + $player->{teleported} = 1; + } else { + debug "Player Disappeared in an unknown way: ".$player->name." ($player->{binID}) $sex_lut{$player->{sex}} $jobs_lut{$player->{jobID}}\n", "parseMsg_presence"; + $player->{disappeared} = 1; + } + + if (grep { $ID eq $_ } @venderListsID) { + binRemove(\@venderListsID, $ID); + delete $venderLists{$ID}; + } + + if (grep { $ID eq $_ } @buyerListsID) { + binRemove(\@buyerListsID, $ID); + delete $buyerLists{$ID}; + } + + $player->{gone_time} = time; + $players_old{$ID} = $player->deepCopy(); + Plugins::callHook('player_disappeared', {player => $player}); + + $playersList->remove($player); + } + + } elsif ($players_old{$ID}) { + if ($args->{type} == 2) { + debug "Player Disconnected: " . $players_old{$ID}->name . "\n", "parseMsg_presence"; + $players_old{$ID}{disconnected} = 1; + } elsif ($args->{type} == 3) { + debug "Player Teleported: " . $players_old{$ID}->name . "\n", "parseMsg_presence"; + $players_old{$ID}{teleported} = 1; + } + + } elsif (defined $portalsList->getByID($ID)) { + my $portal = $portalsList->getByID($ID); + debug "Portal Disappeared: " . $portal->name . " ($portal->{binID})\n", "parseMsg"; + $portal->{disappeared} = 1; + $portal->{gone_time} = time; + $portals_old{$ID} = $portal->deepCopy(); + Plugins::callHook('portal_disappeared', {portal => $portal}); + $portalsList->remove($portal); + + } elsif (defined $npcsList->getByID($ID)) { + my $npc = $npcsList->getByID($ID); + debug "NPC Disappeared: " . $npc->name . " ($npc->{nameID})\n", "parseMsg"; + $npc->{disappeared} = 1; + $npc->{gone_time} = time; + $npcs_old{$ID} = $npc->deepCopy(); + Plugins::callHook('npc_disappeared', {npc => $npc}); + $npcsList->remove($npc); + + } elsif (defined $petsList->getByID($ID)) { + my $pet = $petsList->getByID($ID); + debug "Pet Disappeared: " . $pet->name . " ($pet->{binID})\n", "parseMsg"; + $pet->{disappeared} = 1; + $pet->{gone_time} = time; + Plugins::callHook('pet_disappeared', {pet => $pet}); + $petsList->remove($pet); + + } elsif (defined $slavesList->getByID($ID)) { + my $slave = $slavesList->getByID($ID); + if ($args->{type} == 1) { + message TF("Slave Died: %s (%d) %s\n", $slave->name, $slave->{binID}, $slave->{actorType}); + if (isMySlaveID($ID)) { + if ($slave->isa("AI::Slave::Homunculus") || $slave->isa("Actor::Slave::Homunculus")) { + $char->{homunculus_info}{dead} = 1; + AI::SlaveManager::removeSlave($slave) if ($char->has_homunculus); + + } elsif ($slave->isa("AI::Slave::Mercenary") || $slave->isa("Actor::Slave::Mercenary")) { + AI::SlaveManager::removeSlave($slave) if ($char->has_mercenary); + } + } + } else { + if ($args->{type} == 0) { + debug "Slave Disappeared: " . $slave->name . " ($slave->{binID}) $slave->{actorType} ($slave->{pos_to}{x}, $slave->{pos_to}{y})\n", "parseMsg_presence"; + $slave->{disappeared} = 1; + } elsif ($args->{type} == 2) { + debug "Slave Disconnected: ".$slave->name." ($slave->{binID}) $slave->{actorType} ($slave->{pos_to}{x}, $slave->{pos_to}{y})\n", "parseMsg_presence"; + $slave->{disconnected} = 1; + } elsif ($args->{type} == 3) { + debug "Slave Teleported: ".$slave->name." ($slave->{binID}) $slave->{actorType} ($slave->{pos_to}{x}, $slave->{pos_to}{y})\n", "parseMsg_presence"; + $slave->{teleported} = 1; + } else { + debug "Slave Disappeared in an unknown way: ".$slave->name." ($slave->{binID}) $slave->{actorType}\n", "parseMsg_presence"; + $slave->{disappeared} = 1; + } + + $slave->{gone_time} = time; + Plugins::callHook('slave_disappeared', {slave => $slave}); + } + + $slavesList->remove($slave); + + } elsif (defined $elementalsList->getByID($ID)) { + my $elemental = $elementalsList->getByID($ID); + if ($args->{type} == 0) { + message "Elemental Disappeared: " .$elemental->{name}. " ($elemental->{binID}) $elemental->{actorType} ($elemental->{pos_to}{x}, $elemental->{pos_to}{y})\n", "parseMsg_presence"; + $elemental->{disappeared} = 1; + } else { + debug "Elemental Disappeared in an unknown way: ".$elemental->{name}." ($elemental->{binID}) $elemental->{actorType}\n", "parseMsg_presence"; + $elemental->{disappeared} = 1; + } + + $elemental->{gone_time} = time; + Plugins::callHook('elemental_disappeared', {elemental => $elemental}); + + if ($char->{elemental}{ID} eq $ID) { + $char->{elemental} = undef; + } + + $elementalsList->remove($elemental); + + } else { + debug "Unknown Disappeared: ".getHex($ID)."\n", "parseMsg"; + } +} + +sub actor_action { + my ($self,$args) = @_; + return unless changeToInGameState(); + + $args->{damage} = intToSignedShort($args->{damage}); + if ($args->{type} == ACTION_ITEMPICKUP) { + # Take item + my $source = Actor::get($args->{sourceID}); + my $verb = $source->verb('pick up', 'picks up'); + my $target = getActorName($args->{targetID}); + debug "$source $verb $target\n", 'parseMsg_presence'; + + my $item = $itemsList->getByID($args->{targetID}); + $item->{takenBy} = $args->{sourceID} if ($item); + + } elsif ($args->{type} == ACTION_SIT) { + # Sit + my ($source, $verb) = getActorNames($args->{sourceID}, 0, 'are', 'is'); + if ($args->{sourceID} eq $accountID) { + message T("You are sitting.\n") if (!$char->{sitting}); + $char->{sitting} = 1; + if ($ai_v{sitAuto_pendingLook}) { + my $pendingLook = delete $ai_v{sitAuto_pendingLook}; + delete $ai_v{sitAuto_scheduledLook}; + my $delay = ($pendingLook->{delay} && $pendingLook->{delay} > 0) ? $pendingLook->{delay} : 0; + if ($delay > 0) { + $ai_v{sitAuto_scheduledLook} = { + due => time + $delay, + body => $pendingLook->{body}, + head => $pendingLook->{head}, + }; + } elsif (defined $pendingLook->{head}) { + Misc::look($pendingLook->{body}, $pendingLook->{head}); + } elsif (defined $pendingLook->{body}) { + Misc::look($pendingLook->{body}); + } + } + AI::queue("sitAuto") unless (AI::inQueue("sitAuto")) || $ai_v{sitAuto_forcedBySitCommand}; + } else { + message TF("%s is sitting.\n", getActorName($args->{sourceID})), 'parseMsg_statuslook', 2; + my $player = $playersList->getByID($args->{sourceID}); + $player->{sitting} = 1 if ($player); + } + Misc::checkValidity("actor_action (take item)"); + + } elsif ($args->{type} == ACTION_STAND) { + # Stand + my ($source, $verb) = getActorNames($args->{sourceID}, 0, 'are', 'is'); + if ($args->{sourceID} eq $accountID) { + message T("You are standing.\n") if ($char->{sitting}); + if ($config{sitAuto_idle}) { + $timeout{ai_sit_idle}{time} = time; + } + delete $ai_v{sitAuto_forcedBySitCommand} if $ai_v{sitAuto_forcedBySitCommand}; + $char->{sitting} = 0; + } else { + message TF("%s is standing.\n", getActorName($args->{sourceID})), 'parseMsg_statuslook', 2; + my $player = $playersList->getByID($args->{sourceID}); + $player->{sitting} = 0 if ($player); + } + Misc::checkValidity("actor_action (stand)"); + + } else { + # Attack + my $dmgdisplay; + my $totalDamage = $args->{damage} + $args->{dual_wield_damage}; + if ($totalDamage == 0) { + $dmgdisplay = T("Miss!"); + $dmgdisplay .= "!" if ($args->{type} == ACTION_ATTACK_LUCKY); # lucky dodge + } else { + $dmgdisplay = $args->{div} > 1 + ? sprintf '%d*%d', $args->{damage} / $args->{div}, $args->{div} + : $args->{damage} + ; + $dmgdisplay .= "!" if ($args->{type} == ACTION_ATTACK_CRITICAL); # critical hit + $dmgdisplay .= " + $args->{dual_wield_damage}" if $args->{dual_wield_damage}; + } + + Misc::checkValidity("actor_action (attack 1)"); + + updateDamageTables($args->{sourceID}, $args->{targetID}, $totalDamage); + + Misc::checkValidity("actor_action (attack 2)"); + + my $source = Actor::get($args->{sourceID}); + my $target = Actor::get($args->{targetID}); + my $verb = $source->verb('attack', 'attacks'); + + $target->{sitting} = 0 unless $args->{type} == ACTION_ATTACK_NOMOTION || $args->{type} == ACTION_ATTACK_MULTIPLE_NOMOTION || $totalDamage == 0; + + my $msg = attack_string($source, $target, $dmgdisplay, ($args->{src_speed})); + Plugins::callHook('packet_attack', { + sourceID => $args->{sourceID}, + targetID => $args->{targetID}, + msg => \$msg, + dmg => $totalDamage, + type => $args->{type} + }); + + my $status = sprintf("[%3d/%3d]", percent_hp($char), percent_sp($char)); + + Misc::checkValidity("actor_action (attack 3)"); + + if ($args->{sourceID} eq $accountID) { + message("$status $msg", $totalDamage > 0 ? "attackMon" : "attackMonMiss"); + if ($startedattack) { + $monstarttime = time(); + $monkilltime = time(); + $startedattack = 0; + } + Misc::checkValidity("actor_action (attack 4)"); + calcStat($args->{damage}); + Misc::checkValidity("actor_action (attack 5)"); + + } elsif ($args->{targetID} eq $accountID) { + message("$status $msg", $args->{damage} > 0 ? "attacked" : "attackedMiss"); + if ($args->{damage} > 0) { + $damageTaken{$source->{name}}{attack} += $args->{damage}; + } + + } elsif ($char->{slaves} && $char->{slaves}{$args->{sourceID}}) { + message(sprintf("[%3d/%3d]", $char->{slaves}{$args->{sourceID}}->hp_percent, $char->{slaves}{$args->{sourceID}}->sp_percent) . " $msg", $totalDamage > 0 ? "attackMon" : "attackMonMiss"); + + } elsif ($char->{slaves} && $char->{slaves}{$args->{targetID}}) { + message(sprintf("[%3d/%3d]", $char->{slaves}{$args->{targetID}}->hp_percent, $char->{slaves}{$args->{targetID}}->sp_percent) . " $msg", $args->{damage} > 0 ? "attacked" : "attackedMiss"); + + } elsif ($args->{sourceID} eq $args->{targetID}) { + message("$status $msg"); + + } elsif ($config{showAllDamage}) { + message("$status $msg"); + + } else { + debug("$msg", 'parseMsg_damage'); + } + + Misc::checkValidity("actor_action (attack 6)"); + } +} + +sub actor_info { + my ($self, $args) = @_; + return unless changeToInGameState(); + my $name = bytesToString($args->{name}); + $name =~ s/^\s+|\s+$//g; + debug "Received object info: $name\n", "parseMsg_presence/name", 2; + my $player = $playersList->getByID($args->{ID}); + if ($player) { + # 0095: This packet tells us the names of players who aren't in a guild. + # 0195: Receive names of players who are in a guild. + # FIXME: There is more to this packet than just party name and guild name. + # This packet is received when you leave a guild + # (with cryptic party and guild name fields, at least for now) + $player->setName($name); + $player->{info} = 1; + + $player->{party}{name} = bytesToString($args->{partyName}) if defined $args->{partyName}; + $player->{guild}{name} = bytesToString($args->{guildName}) if defined $args->{guildName}; + $player->{guild}{title} = bytesToString($args->{guildTitle}) if defined $args->{guildTitle}; + $player->{title}{ID} = $args->{titleID} if defined $args->{titleID}; + message "Player Info: " . $player->nameIdx . "\n", "parseMsg_presence", 2; + updatePlayerNameCache($player); + playerLog("player " .$player->{name} ." is near (" .$field->{baseName} .", lvl=" .$player->{lv} .", job=" .$jobs_lut{$player->{jobID}} .")") if (!$field->isCity); + Plugins::callHook('charNameUpdate', {player => $player}); + } + + my $monster = $monstersList->getByID($args->{ID}); + if ($monster) { + debug "Monster Info: $name ($monster->{binID})\n", "parseMsg", 2; + $monster->{name_given} = $name; + $monster->{info} = 1; + if ($monsters_lut{$monster->{nameID}} eq "") { + $monster->setName($name); + $monsters_lut{$monster->{nameID}} = $name; + updateMonsterLUT(Settings::getTableFilename("monsters.txt"), $monster->{nameID}, $name); + Plugins::callHook('mobNameUpdate', {monster => $monster}); + } + } + + my $npc = $npcs{$args->{ID}}; + if ($npc) { + $npc->setName($name); + $npc->{info} = 1; + if ($config{debug} >= 2) { + my $binID = binFind(\@npcsID, $args->{ID}); + debug "NPC Info: $npc->{name} ($binID)\n", "parseMsg", 2; + } + + my $location = $field->baseName . " $npc->{pos}{x} $npc->{pos}{y}"; + if (!$npcs_lut{$location}) { + $npcs_lut{$location} = $npc->{name}; + updateNPCLUT(Settings::getTableFilename("npcs.txt"), $location, $npc->{name}); + } + Plugins::callHook('npcNameUpdate', {npc => $npc}); + } + + my $pet = $pets{$args->{ID}}; + if ($pet) { + $pet->{name_given} = $name; + $pet->setName($name); + $pet->{info} = 1; + if ($config{debug} >= 2) { + my $binID = binFind(\@petsID, $args->{ID}); + debug "Pet Info: $pet->{name_given} ($binID)\n", "parseMsg", 2; + } + Plugins::callHook('petNameUpdate', {pet => $pet}); + } + + my $slave = $slavesList->getByID($args->{ID}); + if ($slave) { + $slave->{name_given} = $name; + $slave->setName($name); + $slave->{info} = 1; + my $binID = binFind(\@slavesID, $args->{ID}); + debug "Slave Info: $name ($binID)\n", "parseMsg_presence", 2; + updatePlayerNameCache($slave); + Plugins::callHook('slaveNameUpdate', {slave => $slave}); + } + + my $elemental = $elementals{$args->{ID}}; + if ($elemental) { + $elemental->{name_given} = $name; + $elemental->setName($name); + $elemental->{info} = 1; + if ($config{debug} >= 2) { + my $binID = binFind(\@elementalsID, $args->{ID}); + debug "elemental Info: $elemental->{name_given} ($binID)\n", "parseMsg", 2; + } + Plugins::callHook('elementalNameUpdate', {elemental => $elemental}); + } + + # TODO: $args->{ID} eq $accountID +} + +# Notifies clients in the area about an special/visual effect (ZC_NOTIFY_EFFECT). +# 019B <id>.L <effect id>.L +# effect id: +# 0 = base level up +# 1 = job level up +# 2 = refine failure +# 3 = refine success +# 4 = game over +# 5 = pharmacy success +# 6 = pharmacy failure +# 7 = base level up (super novice) +# 8 = job level up (super novice) +# 9 = base level up (taekwon) +sub unit_levelup { + my ($self, $args) = @_; + + my $ID = $args->{ID}; + my $type = $args->{type}; + my $actor = Actor::get($ID); + if ($type == LEVELUP_EFFECT || $type == LEVELUP_EFFECT2 || $type == LEVELUP_EFFECT3) { + message TF("%s gained a level!\n", $actor); + Plugins::callHook('base_level', {name => $actor}); + } elsif ($type == JOBLEVELUP_EFFECT || $type == JOBLEVELUP_EFFECT2) { + message TF("%s gained a job level!\n", $actor); + Plugins::callHook('job_level', {name => $actor}); + } elsif ($type == REFINING_FAIL_EFFECT) { + message TF("%s failed to refine a weapon!\n", $actor), "refine"; + } elsif ($type == REFINING_SUCCESS_EFFECT) { + message TF("%s successfully refined a weapon!\n", $actor), "refine"; + } elsif ($type == MAKEITEM_AM_SUCCESS_EFFECT) { + message TF("%s successfully created a potion!\n", $actor), "refine"; + } elsif ($type == MAKEITEM_AM_FAIL_EFFECT) { + message TF("%s failed to create a potion!\n", $actor), "refine"; + } elsif ($type == GAME_OVER_EFFECT) { + message TF("%s received GAME OVER!\n", $actor); + } else { + message TF("%s unknown unit_levelup effect (%d)\n", $actor, $type); + } +} + +use constant QTYPE => ( + 0x0 => [0xff, 0xff, 0, 0], + 0x1 => [0xff, 0x80, 0, 0], + 0x2 => [0, 0xff, 0, 0], + 0x3 => [0x80, 0, 0x80, 0], +); + +sub parse_minimap_indicator { + my ($self, $args) = @_; + + $args->{actor} = Actor::get($args->{npcID}); + $args->{show} = $args->{type} != 2; + + unless (defined $args->{red}) { + @{$args}{qw(red green blue alpha)} = @{{QTYPE}->{$args->{qtype}} || [0xff, 0xff, 0xff, 0]}; + } + + # FIXME: packet 0144: coordinates are missing now when clearing indicators; ID is used + # Wx depends on coordinates there +} + +sub account_payment_info { + my ($self, $args) = @_; + my $D_minute = $args->{D_minute}; + my $H_minute = $args->{H_minute}; + + my $D_d = int($D_minute / 1440); + my $D_h = int(($D_minute % 1440) / 60); + my $D_m = int(($D_minute % 1440) % 60); + + my $H_d = int($H_minute / 1440); + my $H_h = int(($H_minute % 1440) / 60); + my $H_m = int(($H_minute % 1440) % 60); + + my $msg = center(T(" Account payment information "), 56, '-') ."\n" . + TF("Pay per day : %s day(s) %s hour(s) and %s minute(s)\n", $D_d, $D_h, $D_m). + TF("Pay per hour : %s day(s) %s hour(s) and %s minute(s)\n", $H_d, $H_h, $H_m); + $msg .= ('-'x56) . "\n"; + message $msg, "info"; +} + +# TODO +sub reconstruct_minimap_indicator { +} + +# Sends information about owned homunculus to the client . [orn] +# 022e <name>.24B <modified>.B <level>.W <hunger>.W <intimacy>.W <equip id>.W <atk>.W <matk>.W <hit>.W <crit>.W <def>.W <mdef>.W <flee>.W <aspd>.W <hp>.W <max hp>.W <sp>.W <max sp>.W <exp>.L <max exp>.L <skill points>.W <atk range>.W (ZC_PROPERTY_HOMUN) +# 09f7 <name>.24B <modified>.B <level>.W <hunger>.W <intimacy>.W <equip id>.W <atk>.W <matk>.W <hit>.W <crit>.W <def>.W <mdef>.W <flee>.W <aspd>.W <hp>.L <max hp>.L <sp>.W <max sp>.W <exp>.L <max exp>.L <skill points>.W <atk range>.W (ZC_PROPERTY_HOMUN_2) +# 0b2f <name>.24B <modified>.B <level>.W <hunger>.W <intimacy>.W <atk>.W <matk>.W <hit>.W <crit>.W <def>.W <mdef>.W <flee>.W <aspd>.W <hp>.L <max hp>.L <sp>.W <max sp>.W <exp>.L <max exp>.L <skill points>.W <atk range>.W (ZC_PROPERTY_HOMUN3) type 1 +# 0b76 <name>.24B <modified>.B <level>.W <hunger>.W <intimacy>.W <atk>.W <matk>.W <hit>.W <crit>.W <def>.W <mdef>.W <flee>.W <aspd>.W <hp>.L <max hp>.L <sp>.L <max sp>.L <exp>.L <max exp>.L <skill points>.W <atk range>.W (ZC_PROPERTY_HOMUN3) type 2 +# 0ba4 <name>.24B <modified>.B <level>.W <hunger>.W <intimacy>.W <atk>.W <matk>.W <hit>.W <crit>.W <def>.W <mdef>.W <flee>.W <aspd>.W <hp>.L <max hp>.L <sp>.L <max sp>.L <exp>.eL <max exp>.eL <skill points>.W <atk range>.W (ZC_PROPERTY_HOMUN4) +sub homunculus_property { + my ($self, $args) = @_; + + return 0 unless enforce_homun_state(); + + my $slave = $char->{homunculus}; + $slave->setName(bytesToString($args->{name})); + + slave_calcproperty_handler($slave, $args); + homunculus_state_handler($slave, $args); + + foreach (@{$args->{KEYS}}) { + $slave->{$_} = $args->{$_}; + } + + # ST0's counterpart for ST kRO, since it attempts to support all servers + # TODO: we do this for homunculus, mercenary and our char... make 1 function and pass actor and attack_range? + # or make function in Actor class + if ($config{homunculus_attackDistanceAuto} && exists $slave->{attack_range}) { + if($config{homunculus_attackDistance} > $slave->{attack_range}) { # decrease attack range if necessary + configModify('homunculus_attackDistance', $slave->{attack_range}, 1); + message TF("Autodetected attackDistance for homunculus = %s\n", $config{homunculus_attackDistance}), "success"; + } + if ($config{homunculus_attackMaxDistance} != $slave->{attack_range}) { # set max distance using information coming from the server + configModify('homunculus_attackMaxDistance', $slave->{attack_range}, 1); + message TF("Autodetected homunculus_attackMaxDistance for homunculus = %s\n", $config{homunculus_attackMaxDistance}), "success"; + } + } +} + +sub enforce_homun_state { + return 0 unless ($char); + if (exists $char->{homunculus} && defined $char->{homunculus}) { + return 1; + } else { + debug "[Homunculus] Received homunculus property without the homunculus objetive existing, creating a temporary one.\n"; + $char->{homunculus} = Actor::new('Actor::Slave::Homunculus'); + return 1; + } +} + +sub homunculus_state_handler { + my ($slave, $args) = @_; + # Homunculus states bit: + # 0 - alive, unnamed and not vaporized + # 1 - named + # 2 - rest + # 4 - dead + + if (!defined $slave->{state}) { + if ($args->{state} & 1) { + $char->{homunculus_info}{renameflag} = 1; + message T("Your Homunculus has already been renamed\n"), 'homunculus'; + } else { + $char->{homunculus_info}{renameflag} = 0; + message T("Your Homunculus has not been renamed\n"), 'homunculus'; + } + + if ($args->{state} & 2) { + $char->{homunculus_info}{vaporized} = 1; + message T("Your Homunculus is vaporized\n"), 'homunculus'; + } else { + $char->{homunculus_info}{vaporized} = 0; + message T("Your Homunculus is not vaporized\n"), 'homunculus'; + } + + if ($args->{state} & 4) { + $char->{homunculus_info}{dead} = 0; + message T("Your Homunculus is not dead\n"), 'homunculus'; + } else { + $char->{homunculus_info}{dead} = 1; + message T("Your Homunculus is dead\n"), 'homunculus'; + } + + } elsif (defined $slave->{state} && $slave->{state} != $args->{state}) { + if (($args->{state} & 1) && !($slave->{state} & 1)) { + $char->{homunculus_info}{renameflag} = 1; + message T("Your Homunculus was renamed\n"), 'homunculus'; + } + + if (($args->{state} & 2) && !($slave->{state} & 2)) { + $char->{homunculus_info}{vaporized} = 1; + message T("Your Homunculus was vaporized!\n"), 'homunculus'; + } + + if (($args->{state} & 4) && !($slave->{state} & 4)) { + $char->{homunculus_info}{dead} = 0; + message T("Your Homunculus was resurrected!\n"), 'homunculus'; + } + + if (!($args->{state} & 1) && ($slave->{state} & 1)) { + $char->{homunculus_info}{renameflag} = 0; + message T("Your Homunculus was un-renamed? lol\n"), 'homunculus'; + } + + if (!($args->{state} & 2) && ($slave->{state} & 2)) { + $char->{homunculus_info}{vaporized} = 0; + message T("Your Homunculus was recalled!\n"), 'homunculus'; + } + + if (!($args->{state} & 4) && ($slave->{state} & 4)) { + $char->{homunculus_info}{dead} = 1; + message T("Your Homunculus died!\n"), 'homunculus'; + } + } +} + +use constant { + HO_PRE_INIT => 0x0, + HO_RELATIONSHIP_CHANGED => 0x1, + HO_FULLNESS_CHANGED => 0x2, + HO_ACCESSORY_CHANGED => 0x3, + HO_HEADTYPE_CHANGED => 0x4, +}; + +# Notification about a change in homunuculus' state (ZC_CHANGESTATE_MER). +# 0230 <type>.B <state>.B <id>.L <data>.L +# type: +# unused +# state: +# 0 = pre-init +# 1 = intimacy +# 2 = hunger +# 3 = accessory? +# ? = ignored +sub homunculus_info { + my ($self, $args) = @_; + debug "homunculus_info type: $args->{type}\n", "homunculus"; + if ($args->{state} == HO_PRE_INIT) { + + # Some servers won't send 'homunculus_property' after a teleport, so we don't delete $char->{homunculus} object + if ($char->{homunculus_info}{dead} == 1) { + debug "[Homunculus] We received a homunculus_info packet while our homunculus is dead, assume it was resurrected.\n"; + $char->{homunculus_info}{dead} = 0; + } + + $char->{homunculus} = Actor::get($args->{ID}) if ($char->{homunculus}{ID} ne $args->{ID}); + $char->{homunculus}{map} = $field->baseName; + unless ($char->{slaves}{$char->{homunculus}{ID}}) { + if ($char->{homunculus}->isa('AI::Slave::Homunculus')) { + # After a teleport the homunculus object is still AI::Slave::Homunculus, but AI::SlaveManager::addSlave requires it to be Actor::Slave::Homunculus, so we change it back + bless $char->{homunculus}, 'Actor::Slave::Homunculus'; + } + if (!$char->has_homunculus) { + debug "[Homunculus] Adding homunculus to SlaveManager after homunculus_info packet.\n"; + AI::SlaveManager::addSlave($char->{homunculus}); + } + $char->{homunculus}{appear_time} = time; + } + } elsif ($args->{state} == HO_RELATIONSHIP_CHANGED) { + $char->{homunculus}{intimacy} = $args->{val} if $char->{homunculus}; + } elsif ($args->{state} == HO_FULLNESS_CHANGED) { + $char->{homunculus}{hunger} = $args->{val} if $char->{homunculus}; + } elsif ($args->{state} == HO_ACCESSORY_CHANGED) { + $char->{homunculus}{accessory} = $args->{val} if $char->{homunculus}; + } elsif ($args->{state} == HO_HEADTYPE_CHANGED) { + # + } +} + +# Marks a position on client's minimap (ZC_COMPASS). +# 0144 <npc id>.L <type>.L <x>.L <y>.L <id>.B <color>.L +# +# Notification about an NPC's quest state (ZC_QUEST_NOTIFY_EFFECT). +# 0446 <npc id>.L <x>.W <y>.W <effect>.W <color>.W +## +# minimap_indicator({bool show, Actor actor, int x, int y, int red, int green, int blue, int alpha [, int effect]}) +# show: whether indicator is shown or cleared +# actor: @MODULE(Actor) who issued the indicator; or which Actor it's binded to +# x, y: indicator coordinates +# red, green, blue, alpha: indicator color +# effect: unknown, may be missing +# +# Minimap indicator. +sub minimap_indicator { + my ($self, $args) = @_; + + my $marker_type = defined $args->{type} + ? "type:$args->{type}" + : "effect:" . (defined $args->{effect} ? $args->{effect} : '-'); + my $marker_key = join(':', + $args->{show} ? 1 : 0, + $marker_type, + map { defined $_ ? $_ : '-' } @{$args}{qw(x y red green blue alpha)} + ); + + if ($minimap_indicator_seen{$marker_key}) { + return; + } + $minimap_indicator_seen{$marker_key} = 1; + + my $color_str = "[R:$args->{red}, G:$args->{green}, B:$args->{blue}, A:$args->{alpha}]"; + my $indicator = T("minimap indicator"); + if (defined $args->{type}) { + unless ($args->{type} == 1 || $args->{type} == 2) { + $indicator .= TF(" (unknown type %d)", $args->{type}); + } + } elsif (defined $args->{effect}) { + if ($args->{effect} == 1) { + $indicator = T("*Quest!*"); + } elsif ($args->{effect} == 9999) { + return; + } elsif ($args->{effect}) { # 0 is no effect + $indicator = TF("unknown effect %d", $args->{effect}); + } + } + + if ($args->{show}) { + message TF("%s shown %s at location %d, %d " . + "with the color %s\n", $args->{actor}, $indicator, @{$args}{qw(x y)}, $color_str), + 'minimap_indicator'; + } else { + message TF("%s cleared %s at location %d, %d " . + "with the color %s\n", $args->{actor}, $indicator, @{$args}{qw(x y)}, $color_str), + 'minimap_indicator'; + } +} + +# 0x01B3 +sub parse_npc_image { + my ($self, $args) = @_; + + $args->{npc_image} = bytesToString($args->{npc_image}); +} + +sub reconstruct_npc_image { + my ($self, $args) = @_; + + $args->{npc_image} = stringToBytes($args->{npc_image}); +} + +# Displays an illustration image. +# 0145 <image name>.16B <type>.B (ZC_SHOW_IMAGE) +# 01b3 <image name>.64B <type>.B (ZC_SHOW_IMAGE2) +sub npc_image { + my ($self, $args) = @_; + + if ($args->{type} == 2) { + message TF("NPC image: %s\n", $args->{npc_image}), 'npc'; + } elsif ($args->{type} == 255) { + debug "Hide NPC image: $args->{npc_image}\n", "parseMsg"; + } else { + message TF("NPC image: %s (unknown type %s)\n", $args->{npc_image}, $args->{type}), 'npc'; + } + + unless ($args->{type} == 255) { + $talk{image} = $args->{npc_image}; + } else { + delete $talk{image}; + } +} + +# Send broadcast message with font formatting (ZC_BROADCAST2). +# 01C3 <packet len>.W <fontColor>.L <fontType>.W <fontSize>.W <fontAlign>.W <fontY>.W <message>.?B +sub local_broadcast { + my ($self, $args) = @_; + my $message = bytesToString($args->{message}); + my $color = uc(sprintf("%06x", $args->{color})); # hex code + stripLanguageCode(\$message); + chatLog("lb", "$message\n") if ($config{logLocalBroadcast}); + message "$message\n", "schat"; + Plugins::callHook('packet_localBroadcast', { + Msg => $message, + color => $color + }); +} + +sub parse_sage_autospell { + my ($self, $args) = @_; + + $args->{skills} = [map { Skill->new(idn => $_) } sort { $a<=>$b } grep {$_} + exists $args->{autoshadowspell_list} + ? (unpack 'v*', $args->{autoshadowspell_list}) + : (unpack 'V*', $args->{autospell_list}) + ]; +} + +sub reconstruct_sage_autospell { + my ($self, $args) = @_; + + my @skillIDs = map { $_->getIDN } $args->{skills}; + $args->{autoshadowspell_list} = pack 'v*', @skillIDs; + $args->{autospell_list} = pack 'V*', @skillIDs; +} + +## +# sage_autospell({arrayref skills, int why}) +# skills: list of @MODULE(Skill) instances +# why: unknown +# +# Skill list for Sage's Hindsight and Shadow Chaser's Auto Shadow Spell. +sub sage_autospell { + my ($self, $args) = @_; + + return unless $self->changeToInGameState; + + my $msg = center(' ' . T('Auto Spell') . ' ', 40, '-') . "\n" + . T(" # Skill\n") + . (join '', map { swrite '@>>> @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<', [$_->getIDN, $_] } @{$args->{skills}}) + . ('-'x40) . "\n"; + + message $msg, 'list'; + + if ($config{autoSpell}) { + my @autoSpells = split /\s*,\s*/, $config{autoSpell}; + for my $autoSpell (@autoSpells) { + my $skill = new Skill(auto => $autoSpell); + message 'Testing autoSpell ' . $autoSpell . "\n"; + if (!$config{autoSpell_safe} || List::Util::first { $_->getIDN == $skill->getIDN } @{$args->{skills}}) { + if (defined $args->{why}) { + $messageSender->sendSkillSelect($skill->getIDN, $args->{why}); + return; + } else { + $messageSender->sendAutoSpell($skill->getIDN); + return; + } + } + } + error TF("Configured autoSpell (%s) not available.\n", $config{autoSpell}); + message T("Disable autoSpell_safe to use it anyway.\n"), 'hint'; + } else { + message T("Configure autoSpell to automatically select skill for Auto Spell.\n"), 'hint'; + } +} + +# Sends info about a player's equipped items. +# 02D7 <packet len>.W <name>.24B <class>.W <hairstyle>.W <up-viewid>.W <mid-viewid>.W <low-viewid>.W <haircolor>.W <cloth-dye>.W <gender>.B {equip item}.26B* (ZC_EQUIPWIN_MICROSCOPE) +# 02D7 <packet len>.W <name>.24B <class>.W <hairstyle>.W <bottom-viewid>.W <mid-viewid>.W <up-viewid>.W <haircolor>.W <cloth-dye>.W <gender>.B {equip item}.28B* (ZC_EQUIPWIN_MICROSCOPE, PACKETVER >= 20100629) +# 0859 <packet len>.W <name>.24B <class>.W <hairstyle>.W <bottom-viewid>.W <mid-viewid>.W <up-viewid>.W <haircolor>.W <cloth-dye>.W <gender>.B {equip item}.28B* (ZC_EQUIPWIN_MICROSCOPE2, PACKETVER >= 20101124) +# 0859 <packet len>.W <name>.24B <class>.W <hairstyle>.W <bottom-viewid>.W <mid-viewid>.W <up-viewid>.W <robe>.W <haircolor>.W <cloth-dye>.W <gender>.B {equip item}.28B* (ZC_EQUIPWIN_MICROSCOPE2, PACKETVER >= 20110111) +# 0997 <packet len>.W <name>.24B <class>.W <hairstyle>.W <bottom-viewid>.W <mid-viewid>.W <up-viewid>.W <robe>.W <haircolor>.W <cloth-dye>.W <gender>.B {equip item}.31B* (ZC_EQUIPWIN_MICROSCOPE_V5, PACKETVER >= 20120925) +# 0A2D <packet len>.W <name>.24B <class>.W <hairstyle>.W <bottom-viewid>.W <mid-viewid>.W <up-viewid>.W <robe>.W <haircolor>.W <cloth-dye>.W <gender>.B {equip item}.57B* (ZC_EQUIPWIN_MICROSCOPE_V6, PACKETVER >= 20150225) +# 0B03 <packet len>.W <name>.24B <class>.W <hairstyle>.W <bottom-viewid>.W <mid-viewid>.W <up-viewid>.W <robe>.W <haircolor>.W <cloth-dye>.W <gender>.B {equip item}.57B* (ZC_EQUIPWIN_MICROSCOPE_V7, PACKETVER >= 201200000) +sub show_eq { + my ($self, $args) = @_; + my $item_info; + my @item; + + if ($args->{switch} eq '02D7') { # PACKETVER DEFAULT + $item_info = { + len => 26, + types => 'a2 v C2 v2 C2 a8 l v', + keys => [qw(ID nameID type identified type_equip equipped broken upgrade cards expire bindOnEquipType)], + }; + + if (exists $args->{robe}) { # PACKETVER >= 20100629 + $item_info->{type} .= 'v'; + $item_info->{len} += 2; + } + + } elsif ($args->{switch} eq '0906') { # PACKETVER >= ?? NOT IMPLEMENTED ON EATHENA BASED EMULATOR + $item_info = { + len => 27, + types => 'v2 C v2 C a8 l v2 C', + keys => [qw(ID nameID type type_equip equipped upgrade cards expire bindOnEquipType sprite_id identified)], + }; + + } elsif ($args->{switch} eq '0859') { # PACKETVER >= 20101124 + $item_info = { + len => 28, + types => 'a2 v C2 v2 C2 a8 l v2', + keys => [qw(ID nameID type identified type_equip equipped broken upgrade cards expire bindOnEquipType sprite_id)], + }; + + } elsif ($args->{switch} eq '0997') { # PACKETVER >= 20120925 + $item_info = { + len => 31, + types => 'a2 v C V2 C a8 l v2 C', + keys => [qw(ID nameID type type_equip equipped upgrade cards expire bindOnEquipType sprite_id identified)], + }; + + } elsif ($args->{switch} eq '0A2D') { # PACKETVER >= 20150226 + $item_info = { + len => 57, + types => 'a2 v C V2 C a8 l v2 C a25 C', + keys => [qw(ID nameID type type_equip equipped upgrade cards expire bindOnEquipType sprite_id num_options options identified)], + }; + } elsif ($args->{switch} eq '0B03') { # PACKETVER >= 20150226 + $item_info = { + len => 67, + types => 'a2 V C V2 C a16 l v2 C a25 C', + keys => [qw(ID nameID type type_equip equipped upgrade cards expire bindOnEquipType sprite_id num_options options identified)], + }; + } else { # this can't happen + return; + } + + my $name = bytesToString($args->{name}); + my $msg = center(" $name " . T("Equip Info") . " ", 50, '-') . "\n"; + for (my $i = 0; $i < length($args->{equips_info}); $i += $item_info->{len}) { + my $item; + @{$item}{@{$item_info->{keys}}} = unpack($item_info->{types}, substr($args->{equips_info}, $i, $item_info->{len})); + $item->{broken} = 0; + $item->{identified} = 1; + $msg .= sprintf("%-20s: %s\n", $equipTypes_lut{$item->{equipped}}, itemName($item)); + } + $msg .= sprintf("%s\n", ('-'x50)); + message($msg, "list"); +} + +# The player's 'Configuration' state, sent during login. +# 0A95 <show_eq flag>.B <call flag>.B +# 0AA8 <show_eq flag>.B <call flag>.B <pet autofeeding flag>.B +# 0ADC <show_eq flag>.B <call flag>.B <pet autofeeding flag>.B <homunculus autofeeding flag>.B +sub misc_config { + my ($self, $args) = @_; + + if (defined ($args->{show_eq_flag})) { + if ($args->{show_eq_flag} == 1) { + message T("Your Equipment information is now open to the public.\n"); + } else { + message T("Your Equipment information is now not open to the public.\n"); + } + } + + if (defined ($args->{call_flag})) { + if ($args->{call_flag} == 1) { + message T("Allowed being summoned by skills: Urgent Call, Marriage Skills, etc.\n"); + } else { + message T("Not Allowed being summoned by skills: Urgent Call, Marriage Skills, etc.\n"); + } + } + + if (defined ($args->{pet_autofeed_flag})) { + if ($args->{pet_autofeed_flag} == 1) { + message T("Pet automatic feeding is ON. (Ragexe Client Feature)\n"); + } else { + message T("Pet automatic feeding is OFF. (Ragexe Client Feature)\n"); + } + } + + if (defined ($args->{homunculus_autofeed_flag})) { + if ($args->{homunculus_autofeed_flag} == 1) { + message T("Homunculus automatic feeding is ON. (Ragexe Client Feature)\n"); + } else { + message T("Homunculus automatic feeding is OFF. (Ragexe Client Feature)\n"); + } + } +} + +# Send configurations (ZC_CONFIG). +# 02D9 <type>.L <value>.L +# type: +# 0 = show equip windows to other players +# 1 = being summoned by skills: Urgent Call, Romantic Rendezvous, Come to me, honey~ & Let's Go, Family! +# 2 = pet autofeeding +# 3 = homunculus autofeeding +# value: +# 0 = disabled +# 1 = enabled +sub misc_config_reply { + my ($self, $args) = @_; + + if ( $args->{type} == CONFIG_OPEN_EQUIPMENT_WINDOW ) { + if ($args->{flag}) { + message T("Your Equipment information is now open to the public.\n"); + } else { + message T("Your Equipment information is now not open to the public.\n"); + } + } elsif ( $args->{type} == CONFIG_CALL ) { + if ($args->{flag}) { + message T("Allowed being summoned by skills: Urgent Call, Marriage Skills, etc.\n"); + } else { + message T("Not Allowed being summoned by skills: Urgent Call, Marriage Skills, etc.\n"); + } + } elsif ( $args->{type} == CONFIG_PET_AUTOFEED ) { + if ($args->{flag}) { + message T("Pet automatic feeding is ON. (Ragexe Client Feature)\n"); + } else { + message T("Pet automatic feeding is OFF. (Ragexe Client Feature)\n"); + } + } elsif ( $args->{type} == CONFIG_HOMUNCULUS_AUTOFEED ) { + if ($args->{flag}) { + message T("Homunculus automatic feeding is ON. (Ragexe Client Feature)\n"); + } else { + message T("Homunculus automatic feeding is OFF. (Ragexe Client Feature)\n"); + } + } else { + message TF("Unknown Config Type: %s, Flag: %s\n", $args->{type}, $args->{flag}); + } +} + +sub show_eq_msg_self { + my ($self, $args) = @_; + if ($args->{type}) { + message T("Your Equipment information is now open to the public.\n"); + } else { + message T("Your Equipment information is now not open to the public.\n"); + } +} + +#08B3 +sub show_script { + my ($self, $args) = @_; + my $ID = $args->{ID}; + my $message = bytesToString($args->{message}); + if (defined $npcsList->getByID($ID)) { + my $npc = $npcsList->getByID($ID); + debug $npc->name . " ($npc->{nameID}): $message\n", 'parseMsg'; + Plugins::callHook('show_script', { + ID => $ID, + message => $message + }); + } +} + +# Skill cooldown display icon (ZC_SKILL_POSTDELAY). +# 043D <skill ID>.W <tick>.L +sub skill_post_delay { + my ($self, $args) = @_; + + my $skillName = (new Skill(idn => $args->{ID}))->getName; + my $status = defined $statusName{'EFST_DELAY'} ? $statusName{'EFST_DELAY'} : 'Delay'; + + $char->setStatus($skillName." ".$status, 1, $args->{time}); +} + +# Skill cooldown display icon List. +# 043E <len>.w { <skill ID>.W <tick>.L }* +# 0985 <len>.w { <skill ID>.W <total time>.L <tick>.L }* +sub skill_post_delaylist { + my ($self, $args) = @_; + + my $skill_post_delay_info; + if ($args->{switch} eq "0985") { # 0985 + $skill_post_delay_info = { + len => 10, + types => 'v V2', + keys => [qw(ID total_time remain_time)], + }; + + } else { # 043E + $skill_post_delay_info = { + len => 6, + types => 'v V', + keys => [qw(ID remain_time)], + }; + } + + for (my $i = 0; $i < length($args->{skill_list}); $i += $skill_post_delay_info->{len}) { + my $skill; + @{$skill}{@{$skill_post_delay_info->{keys}}} = unpack($skill_post_delay_info->{types}, substr($args->{skill_list}, $i, $skill_post_delay_info->{len})); + $skill->{name} = (new Skill(idn => $skill->{ID}))->getName; + my $status = defined $statusName{'EFST_DELAY'} ? $statusName{'EFST_DELAY'} : 'Delay'; + + $char->setStatus($skill->{name}." ".$status, 1, $skill->{remain_time}); + } +} + +# Displays a skill message (thanks to Rayce) (ZC_SKILLMSG). +# 0215 <msg id>.L +# msg id: +# 0x15 = End all negative status (PA_GOSPEL) +# 0x16 = Immunity to all status (PA_GOSPEL) +# 0x17 = MaxHP +100% (PA_GOSPEL) +# 0x18 = MaxSP +100% (PA_GOSPEL) +# 0x19 = All stats +20 (PA_GOSPEL) +# 0x1c = Enchant weapon with Holy element (PA_GOSPEL) +# 0x1d = Enchant armor with Holy element (PA_GOSPEL) +# 0x1e = DEF +25% (PA_GOSPEL) +# 0x1f = ATK +100% (PA_GOSPEL) +# 0x20 = HIT/Flee +50 (PA_GOSPEL) +# 0x28 = Full strip failed because of coating (ST_FULLSTRIP) +# ? = nothing +sub gospel_buff_aligned { + my ($self, $args) = @_; + my $status = unpack("V1", $args->{ID}); + + if ($status == 21) { + message T("All abnormal status effects have been removed.\n"), "info"; + } elsif ($status == 22) { + message T("You will be immune to abnormal status effects for the next minute.\n"), "info"; + } elsif ($status == 23) { + message T("Your Max HP will stay increased for the next minute.\n"), "info"; + } elsif ($status == 24) { + message T("Your Max SP will stay increased for the next minute.\n"), "info"; + } elsif ($status == 25) { + message T("All of your Stats will stay increased for the next minute.\n"), "info"; + } elsif ($status == 28) { + message T("Your weapon will remain blessed with Holy power for the next minute.\n"), "info"; + } elsif ($status == 29) { + message T("Your armor will remain blessed with Holy power for the next minute.\n"), "info"; + } elsif ($status == 30) { + message T("Your Defense will stay increased for the next 10 seconds.\n"), "info"; + } elsif ($status == 31) { + message T("Your Attack strength will stay increased for the next minute.\n"), "info"; + } elsif ($status == 32) { + message T("Your Accuracy and Flee Rate will stay increased for the next minute.\n"), "info"; + } else { + #message T("Unknown buff from Gospel: " . $status . "\n"), "info"; + } +} + +# TODO: known prefixes (chat domains): micc | ssss | blue | tool +# micc = micc<24 characters, this is the sender name. seems like it's null padded><hex color code><message> +# micc = Player Broadcast The struct: micc<23bytes player name+some hex><\x00><colour code><full message> +# The first player name is used for detecting the player name only according to the disassembled client. +# The full message contains the player name at the first 22 bytes +# TODO micc.* is currently unstricted, however .{24} and .{23} do not detect chinese with some reasons, please improve this regex if necessary +sub system_chat { + my ($self, $args) = @_; + my $message = bytesToString($args->{message}); + my $prefix; + my $color; + if ($message =~ s/^ssss//g) { # forces color yellow, or WoE indicator? + $prefix = T('[WoE]'); + } elsif ($message =~ /^micc.*\0\0([0-9a-fA-F]{6})(.*)/s) { #appears in twRO ## [micc][name][\x00\x00][unknown][\x00\x00][color][name][blablabla][message] + ($color, $message) = ($1, $2); + $prefix = T('[S]'); + } elsif ($message =~ /^micc.{12,24}([0-9a-fA-F]{6})(.*)/s) { + ($color, $message) = ($1, $2); + $prefix = T('[S]'); + } elsif ($message =~ s/^blue//g) { # forces color blue + $prefix = T('[S]'); + } elsif ($message =~ /^tool([0-9a-fA-F]{6})(.*)/s) { + ($color, $message) = ($1, $2); + $prefix = T('[S]'); + } else { + $prefix = T('[S]'); + } + $message =~ s/\000//g; # remove null charachters + $message =~ s/^ +//g; $message =~ s/ +$//g; # remove whitespace in the beginning and the end of $message + stripLanguageCode(\$message); + my $parsed_msg = solveMessage($message); + chatLog("s", "$parsed_msg\n") if ($config{logSystemChat}); + # Translation Comment: System/GM chat + message "$prefix $parsed_msg\n", "schat"; + ChatQueue::add('gm', undef, undef, $parsed_msg) if ($config{callSignGM}); + debug "schat: $message\n", "schat", 1; + + Plugins::callHook('packet_sysMsg', { + Msg => $parsed_msg, + RawMsg => $message, + MsgColor => $color, + MsgUser => undef # TODO: implement this value, we can get this from "micc" messages by regex. + }); +} + +sub warp_portal_list { + my ($self, $args) = @_; + + # strip gat extension + ($args->{memo1}) = $args->{memo1} =~ /^(.*)\.gat/; + ($args->{memo2}) = $args->{memo2} =~ /^(.*)\.gat/; + ($args->{memo3}) = $args->{memo3} =~ /^(.*)\.gat/; + ($args->{memo4}) = $args->{memo4} =~ /^(.*)\.gat/; + # Auto-detect saveMap + if ($args->{type} == 26) { + configModify('saveMap', $args->{memo2}) if ($args->{memo2} && $config{'saveMap'} ne $args->{memo2}); + } elsif ($args->{type} == 27) { + configModify('saveMap', $args->{memo1}) if ($args->{memo1} && $config{'saveMap'} ne $args->{memo1}); + configModify( "memo$_", $args->{"memo$_"} ) foreach grep { $args->{"memo$_"} ne $config{"memo$_"} } 1 .. 4; + } + + $char->{warp}{type} = $args->{type}; + undef @{$char->{warp}{memo}}; + push @{$char->{warp}{memo}}, $args->{memo1} if $args->{memo1} ne ""; + push @{$char->{warp}{memo}}, $args->{memo2} if $args->{memo2} ne ""; + push @{$char->{warp}{memo}}, $args->{memo3} if $args->{memo3} ne ""; + push @{$char->{warp}{memo}}, $args->{memo4} if $args->{memo4} ne ""; + + my $msg = center(T(" Warp Portal "), 50, '-') ."\n". + T("# Place Map\n"); + for (my $i = 0; $i < @{$char->{warp}{memo}}; $i++) { + $msg .= swrite( + "@< @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< @<<<<<<<<<<<<<<<", + [$i, $maps_lut{$char->{warp}{memo}[$i].'.rsw'}, $char->{warp}{memo}[$i]]); + } + $msg .= ('-'x50) . "\n"; + message $msg, "list"; + + if ($args->{type} == 26 && AI::inQueue('teleport')) { + # We have already successfully used the Teleport skill. + $messageSender->sendWarpTele(26, AI::args->{lv} == 2 ? "$config{saveMap}.gat" : "Random"); + AI::dequeue; + } +} + +# 0828,14 +sub char_delete2_result { + my ($self, $args) = @_; + my $result = $args->{result}; + my $deleteDate = $args->{deleteDate}; + + if ($result && $deleteDate) { + setCharDeleteDate($messageSender->{char_delete_slot}, $deleteDate); + message TF("Your character will be delete, left %s\n", $chars[$messageSender->{char_delete_slot}]{deleteDate}), "connection"; + } elsif ($result == 0) { + error T("That character already planned to be erased!\n"); + } elsif ($result == 3) { + error T("Error in database of the server!\n"); + } elsif ($result == 4) { + error T("To delete a character you must withdraw from the guild!\n"); + } elsif ($result == 5) { + error T("To delete a character you must withdraw from the party!\n"); + } else { + error TF("Unknown error when trying to delete the character! (Error number: %s)\n", $result); + } + + charSelectScreen; +} + +# 082A,10 +sub char_delete2_accept_result { + my ($self, $args) = @_; + my $charID = $args->{charID}; + my $result = $args->{result}; + + if ($result == 1) { # Success + if (defined $AI::temp::delIndex) { + message TF("Character %s (%d) deleted.\n", $chars[$AI::temp::delIndex]{name}, $AI::temp::delIndex), "info"; + delete $chars[$AI::temp::delIndex]; + undef $AI::temp::delIndex; + for (my $i = 0; $i < @chars; $i++) { + delete $chars[$i] if ($chars[$i] && !scalar(keys %{$chars[$i]})) + } + } else { + message T("Character deleted.\n"), "info"; + } + + if (charSelectScreen() == 1) { + $net->setState(3); + $firstLoginMap = 1; + $startingzeny = $chars[$config{'char'}]{'zeny'} unless defined $startingzeny; + $sentWelcomeMessage = 1; + } + return; + } elsif ($result == 0) { + error T("Enter your 6-digit birthday (YYMMDD) (e.g: 801122).\n"); + } elsif ($result == 2) { + error T("Due to system settings, can not be deleted.\n"); + } elsif ($result == 3) { + error T("A database error has occurred.\n"); + } elsif ($result == 4) { + error T("You cannot delete this character at the moment.\n"); + } elsif ($result == 5) { + error T("Your entered birthday does not match.\n"); + } elsif ($result == 7) { + error T("Character Deletion has failed because you have entered an incorrect e-mail address.\n"); + } else { + error TF("An unknown error has occurred. Error number %d\n", $result); + } + + undef $AI::temp::delIndex; + if (charSelectScreen() == 1) { + $net->setState(3); + $firstLoginMap = 1; + $startingzeny = $chars[$config{'char'}]{'zeny'} unless defined $startingzeny; + $sentWelcomeMessage = 1; + } +} + +# 082C,14 +sub char_delete2_cancel_result { + my ($self, $args) = @_; + my $result = $args->{result}; + + if ($result) { + message T("Character is no longer scheduled to be deleted\n"), "connection"; + $chars[$messageSender->{char_delete_slot}]{deleteDate} = ''; + } elsif ($result == 2) { + error T("Error in database of the server!\n"); + } else { + error TF("Unknown error when trying to cancel the deletion of the character! (Error number: %s)\n", $result); + } + + charSelectScreen; +} + +# 013C +sub arrow_equipped { + my ($self, $args) = @_; + return unless changeToInGameState(); + return unless $args->{ID}; + $char->{arrow} = $args->{ID}; + + my $item = $char->inventory->getByID($args->{ID}); + if ($item && $char->{equipment}{arrow} != $item) { + $char->{equipment}{arrow} = $item; + $item->{equipped} = 32768; + $ai_v{temp}{waitForEquip}-- if $ai_v{temp}{waitForEquip}; + message TF("Arrow/Bullet equipped: %s (%d) x %s\n", $item->{name}, $item->{binID}, $item->{amount}); + Plugins::callHook('equipped_item', { + slot => 'arrow', + item => $item + }); + } +} + +# Notifies the client, about a received inventory item or the result of a pick-up request. +# 00A0 <index>.W <amount>.W <name id>.W <identified>.B <damaged>.B <refine>.B <card1>.W <card2>.W <card3>.W <card4>.W <equip location>.W <item type>.B <result>.B (ZC_ITEM_PICKUP_ACK) +# 029A <index>.W <amount>.W <name id>.W <identified>.B <damaged>.B <refine>.B <card1>.W <card2>.W <card3>.W <card4>.W <equip location>.W <item type>.B <result>.B <expire time>.L (ZC_ITEM_PICKUP_ACK2) +# 02D4 <index>.W <amount>.W <name id>.W <identified>.B <damaged>.B <refine>.B <card1>.W <card2>.W <card3>.W <card4>.W <equip location>.W <item type>.B <result>.B <expire time>.L <bindOnEquipType>.W (ZC_ITEM_PICKUP_ACK3) +# 0990 <index>.W <amount>.W <name id>.W <identified>.B <damaged>.B <refine>.B <card1>.W <card2>.W <card3>.W <card4>.W <equip location>.L <item type>.B <result>.B <expire time>.L <bindOnEquipType>.W (ZC_ITEM_PICKUP_ACK_V5) +# 0A0C <index>.W <amount>.W <name id>.W <identified>.B <damaged>.B <refine>.B <card1>.W <card2>.W <card3>.W <card4>.W <equip location>.L <item type>.B <result>.B <expire time>.L <bindOnEquipType>.W { <option id>.W <option value>.W <option param>.B }*5 (ZC_ITEM_PICKUP_ACK_V6) +# 0A37 <index>.W <amount>.W <name id>.W <identified>.B <damaged>.B <refine>.B <card1>.W <card2>.W <card3>.W <card4>.W <equip location>.L <item type>.B <result>.B <expire time>.L <bindOnEquipType>.W { <option id>.W <option value>.W <option param>.B }*5 <favorite>.B <view id>.W (ZC_ITEM_PICKUP_ACK_V7) +sub inventory_item_added { + my ($self, $args) = @_; + + return unless changeToInGameState(); + + my ($index, $amount, $fail) = ($args->{ID}, $args->{amount}, $args->{fail}); + + if (!$fail) { + my $item = $char->inventory->getByID($index); + if (!$item) { + # Add new item + $item = new Actor::Item(); + $item->{ID} = $index; + $item->{nameID} = $args->{nameID}; + $item->{type} = $args->{type}; + $item->{type_equip} = $args->{type_equip}; + $item->{amount} = $amount; + $item->{identified} = $args->{identified}; + $item->{broken} = $args->{broken}; + $item->{upgrade} = $args->{upgrade}; + $item->{cards} = ($args->{switch} eq '029A') ? $args->{cards} + $args->{cards_ext}: $args->{cards}; + if ($args->{switch} eq '029A') { + $args->{cards} .= $args->{cards_ext}; + } elsif ($args->{switch} eq '02D4') { + $item->{expire} = $args->{expire} if (exists $args->{expire}); #a4 or V1 unpacking? + } + $item->{options} = $args->{options}; + $item->{name} = itemName($item); + $char->inventory->add($item); + } else { + # Add stackable item + $item->{amount} += $amount; + } + + $itemChange{$item->{name}} += $amount; + my $disp = TF("Item added to inventory: %s (%d) x %d - %s", + $item->{name}, $item->{binID}, $amount, $itemTypes_lut{$item->{type}}); + message "$disp\n", "drop"; + $disp .= " (". $field->baseName . ")\n"; + itemLog($disp); + + Plugins::callHook('item_gathered', { + item => $item->{name}, + amount => $amount + }); + + $args->{item} = $item; + + if (AI::state == AI::AUTO) { + # Auto-drop item + if (pickupitems($item->{name}, $item->{nameID}) == -1 && !AI::inQueue('storageAuto', 'buyAuto')) { + $messageSender->sendDrop($item->{ID}, $amount); + message TF("Auto-dropping item: %s (%d) x %d\n", $item->{name}, $item->{binID}, $amount), "drop"; + } + } + + } elsif ($fail == 6) { + message T("Can't loot item...wait...\n"), "drop"; + } elsif ($fail == 2) { + message T("Cannot pickup item (inventory full)\n"), "drop"; + } elsif ($fail == 1) { + message T("Cannot pickup item (you're Frozen?)\n"), "drop"; + } else { + message TF("Cannot pickup item (failure code %d)\n", $fail), "drop"; + } +} + +# 00AF, 07FA +sub inventory_item_removed { + my ($self, $args) = @_; + return unless changeToInGameState(); + my $item = $char->inventory->getByID($args->{ID}); + my $reason = $args->{reason}; + + if ($reason) { + if ($reason == 1) { + debug TF("%s was used to cast the skill\n", $item->{name}), "inventory", 1; + } elsif ($reason == 2) { + debug TF("%s broke due to the refinement failed\n", $item->{name}), "inventory", 1; + } elsif ($reason == 3) { + debug TF("%s used in a chemical reaction\n", $item->{name}), "inventory", 1; + } elsif ($reason == 4) { + debug TF("%s was moved to the storage\n", $item->{name}), "inventory", 1; + } elsif ($reason == 5) { + debug TF("%s was moved to the cart\n", $item->{name}), "inventory", 1; + } elsif ($reason == 6) { + debug TF("%s was sold\n", $item->{name}), "inventory", 1; + } elsif ($reason == 7) { + debug TF("%s was consumed by Four Spirit Analysis skill\n", $item->{name}), "inventory", 1; + } else { + debug TF("%s was consumed by an unknown reason (reason number %s)\n", $item->{name}, $reason), "inventory", 1; + } + } + + if ($item) { + inventoryItemRemoved($item->{binID}, $args->{amount}); + Plugins::callHook('packet_item_removed', {index => $item->{binID}}); + } +} + +# 0299 +sub rental_expired { + my ($self, $args) = @_; + my $item = $char->inventory->getByID($args->{ID}); + message TF("Rental item '%s' has expired!\n", itemNameSimple($args->{nameID})), "info"; + + if ($item) { + inventoryItemRemoved($item->{binID}, 1); + Plugins::callHook('rental_expired', { + index => $item->{binID}, + nameID => $item->{nameID} + }); + } +} + +# 012B +sub cart_off { + $char->cart->close; + message T("Cart released.\n"), "success"; +} + +# 012D +sub shop_skill { + my ($self, $args) = @_; + + # Used the shop skill. + my $number = $args->{number}; + message TF("You can sell %s items!\n", $number); +} + +# Your shop has sold an item -- one packet sent per item sold. +# +sub shop_sold { + my ($self, $args) = @_; + + # sold something + my $number = $args->{number}; + my $amount = $args->{amount}; + + $articles[$number]{sold} += $amount; + my $earned = $amount * $articles[$number]{price}; + $shopEarned += $earned; + $articles[$number]{quantity} -= $amount; + my $msg = TF("Sold: %s x %s - %sz\n", $articles[$number]{name}, $amount, $earned); + shopLog($msg) if $config{logShop}; + message($msg, "sold"); + + # Call hook before we possibly remove $articles[$number] or + # $articles itself as a result of the sale. + Plugins::callHook('vending_item_sold', { + 'vendShopIndex' => $number, + 'amount' => $amount, + 'vendArticle' => $articles[$number], #This is a hash + 'zenyEarned' => $earned, + 'packetType' => "short" + }); + + # Adjust the shop's articles for sale, and notify if the sold + # item and/or the whole shop has been sold out. + if ($articles[$number]{quantity} < 1) { + message TF("Sold out: %s\n", $articles[$number]{name}), "sold"; + Plugins::callHook('vending_item_sold_out', { + 'vendShopIndex' => $number, + 'vendArticle' => $articles[$number] + }); + #$articles[$number] = ""; + if (!--$articles){ + message T("Items have been sold out.\n"), "sold"; + closeShop(); + } + } +} + +sub shop_sold_long { + my ($self, $args) = @_; + + # sold something + my $number = $args->{number}; + my $amount = $args->{amount}; + my $earned = $args->{zeny}; + my $charID = getHex($args->{charID}); + my $when = $args->{time}; + + $articles[$number]{sold} += $amount; + $shopEarned += $earned; + $articles[$number]{quantity} -= $amount; + + my $msg = TF("Sold: %s x %s - %sz (Buyer charID: %s)\n", $articles[$number]{name}, $amount, $earned, $charID); + shopLog($msg) if $config{logShop}; + message("[" . getFormattedDate($when) . "] " . $msg, "sold"); + + # Call hook before we possibly remove $articles[$number] or + # $articles itself as a result of the sale. + Plugins::callHook('vending_item_sold', { + 'vendShopIndex' => $number, + 'amount' => $amount, + 'vendArticle' => $articles[$number], #This is a hash + 'buyerCharID' => $args->{charID}, + 'zenyEarned' => $earned, + 'time' => $when, + 'packetType' => "long" + }); + + # Adjust the shop's articles for sale, and notify if the sold + # item and/or the whole shop has been sold out. + if ($articles[$number]{quantity} < 1) { + message TF("Sold out: %s\n", $articles[$number]{name}), "sold"; + Plugins::callHook('vending_item_sold_out', { + 'vendShopIndex' => $number, + 'vendArticle' => $articles[$number] + }); + #$articles[$number] = ""; + if (!--$articles){ + message T("Items have been sold out.\n"), "sold"; + closeShop(); + } + } +} + +# TODO +sub vending_start { + my ($self, $args) = @_; + + my $item_pack = $self->{vender_items_list_item_pack_self} || $self->{vender_items_list_item_pack} || 'V v2 C v C3 a8'; + my $item_len = length pack $item_pack; + my $item_list_len = length $args->{itemList}; + #started a shop. + message TF("Shop '%s' opened!\n", $shop{title}), "success"; + @articles = (); + # FIXME: why do we need a seperate variable to track how many items are left in the store? + $articles = 0; + + # FIXME: Read the packet the server sends us to determine + # the shop title instead of using $shop{title}. + my $msg = center(" $shop{title} ", 83, '-') . "\n" . + T("# Name Type Price Amount\n"); + for (my $i = 0; $i < $item_list_len; $i += $item_len) { + my $item = {}; + @$item{qw( price number quantity type nameID identified broken upgrade cards options location sprite_id)} = unpack $item_pack, substr $args->{itemList}, $i, $item_len; + $item->{name} = itemName($item); + $articles[delete $item->{number}] = $item; + $articles++; + + debug ("Item added to Vender Store: $item->{name} - $item->{price} z\n", "vending", 2); + + $msg .= swrite( + "@< @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< @<<<<<<<<<<<<<< @>>>>>>>>>>>>z @<<<<<", + [$articles, $item->{name}, $itemTypes_lut{$item->{type}}, formatNumber($item->{price}), formatNumber($item->{quantity})]); + } + $msg .= ('-'x83) . "\n"; + message $msg, "list"; + $shopEarned ||= 0; +} + +sub vender_items_list { + my ($self, $args) = @_; + + $venderID = $args->{venderID}; + $venderCID = $args->{venderCID}; + + my $expireDate = 0; + my $item_pack = $self->{vender_items_list_item_pack} || 'V v2 C v C3 a8'; + my $item_len = length pack $item_pack; + my $item_list_len = length $args->{itemList}; + + my $player = Actor::get($args->{venderID}); + + $venderItemList->clear; + + my $msg = TF("%s\n" . + "# Name Type Price Amount\n", + center(' Vender: ' . $player->nameIdx . ' ', 88, '-')); + for (my $i = 0; $i < $item_list_len; $i+=$item_len) { + my $item = Actor::Item->new; + + @$item{qw( price amount ID type nameID identified broken upgrade cards options location sprite_id )} = unpack $item_pack, substr $args->{itemList}, $i, $item_len; + + $item->{name} = itemName($item); + $venderItemList->add($item); + + debug("Item added to Vender Store: $item->{name} - $item->{price} z\n", "vending", 2); + + Plugins::callHook('packet_vender_store', { item => $item }); + + $msg .= swrite( + "@< @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< @<<<<<<<<<<<<<<<<<<<< @>>>>>>>>>>>>z @<<<<<", + [$item->{binID}, $item->{name}, $itemTypes_lut{$item->{type}}, formatNumber($item->{price}), formatNumber($item->{amount})]); + } + $msg .= ('-'x88) . "\n"; + message $msg, $config{showDomain_Shop} || 'list'; + + if ($args->{expireDate}) { + $expireDate = $args->{expireDate}; + my $date = int(time) + int($args->{expireDate}/1000); + message "Expire Date: ".getFormattedDate($date)."\n"; + } + + Plugins::callHook('packet_vender_store2', { + venderID => $args->{venderID}, + venderCID => $args->{venderCID}, + itemList => $venderItemList, + expireDate => $expireDate + }); +} + +# 01D0 (Monk spirits), 01E1 (Gunslingers coins), 08CF (Kagerou/Oboro amulet spirit) +# Notifies the client of an object's spirits. +# 01D0 <id>.L <amount>.W (ZC_SPIRITS) +# 01E1 <id>.L <amount>.W (ZC_SPIRITS2) +# 08CF <id>.L <type>.W <amount>.W (ZC_SPIRITS3) +# 0B73 <id>.L <amount>.W (ZC_SPIRITS3) +sub revolving_entity { + my ($self, $args) = @_; + + # Monk Spirits or Gunslingers' coins or senior ninja + my $sourceID = $args->{sourceID}; + my $entityNum = $args->{entity}; + my $entityElement = $elements_lut{$args->{type}} if ($args->{type} && $entityNum); + my $entityType; + + my $actor = Actor::get($sourceID); + if ($args->{switch} eq '01D0') { + # Translation Comment: Spirit sphere of the monks + $entityType = T('spirit'); + } elsif ($args->{switch} eq '01E1') { + # Translation Comment: Coin of the gunslinger + $entityType = T('coin'); + } elsif ($args->{switch} eq '08CF') { + # Translation Comment: Amulet of the warlock + $entityType = T('amulet'); + } elsif ($args->{switch} eq '0B73') { + # Translation Comment: Soul Energy or Soul Reaper + $entityType = T('soul energy'); + } else { + $entityType = T('entity unknown'); + } + + if ($sourceID eq $accountID && $entityNum != $char->{spirits}) { + $char->{spirits} = $entityNum; + $char->{amuletType} = $entityElement; + $char->{spiritsType} = $entityType; + $entityElement ? + # Translation Comment: Message displays following: quantity, the name of the entity and its element + message TF("You have %s %s(s) of %s now\n", $entityNum, $entityType, $entityElement), "parseMsg_statuslook", 1 : + # Translation Comment: Message displays following: quantity and the name of the entity + message TF("You have %s %s(s) now\n", $entityNum, $entityType), "parseMsg_statuslook", 1; + } elsif ($entityNum != $actor->{spirits}) { + $actor->{spirits} = $entityNum; + $actor->{amuletType} = $entityElement; + $actor->{spiritsType} = $entityType; + $entityElement ? + # Translation Comment: Message displays following: actor, quantity, the name of the entity and its element + message TF("%s has %s %s(s) of %s now\n", $actor, $entityNum, $entityType, $entityElement), "parseMsg_statuslook", 1 : + # Translation Comment: Message displays following: actor, quantity and the name of the entity + message TF("%s has %s %s(s) now\n", $actor, $entityNum, $entityType), "parseMsg_statuslook", 1; + } +} + +# Changes sprite of an NPC object (ZC_NPCSPRITE_CHANGE). +# 01B0 <id>.L <type>.B <value>.L +# type: +# unused +sub monster_typechange { + my ($self, $args) = @_; + + my $ID = $args->{ID}; + my $type = $args->{type}; + my $monster = $monstersList->getByID($ID); + if ($monster) { + my $oldName = $monster->name; + if ($monsters_lut{$type}) { + $monster->setName($monsters_lut{$type}); + } else { + $monster->setName(undef); + } + $monster->{nameID} = $type; + $monster->{dmgToParty} = 0; + $monster->{dmgFromParty} = 0; + $monster->{missedToParty} = 0; + message TF("Monster %s (%d) changed to %s\n", $oldName, $monster->{binID}, $monster->name); + } +} + +# Show monster HP +# 0977 <id>.L <HP>.L <maxHP>.L (ZC_HP_INFO). +sub monster_hp_info { + my ($self, $args) = @_; + my $monster = $monstersList->getByID($args->{ID}); + if ($monster) { + $monster->{hp} = $args->{hp}; + $monster->{hp_max} = $args->{hp_max}; + + debug TF("Monster %s has hp %s/%s (%s%)\n", $monster->name, $monster->{hp}, $monster->{hp_max}, $monster->{hp} * 100 / $monster->{hp_max}), "parseMsg_damage"; + } +} + +# Show Monster HP bar +# 0A36 <id>.L <HP>.B +sub monster_hp_info_tiny { + my ($self, $args) = @_; + my $monster = $monstersList->getByID($args->{ID}); + if ($monster) { + $monster->{hp_percent} = $args->{hp} * 5; + + debug TF("Monster %s has about %d%% hp left\n", $monster->name, $monster->{hp_percent}), "parseMsg_damage"; + } +} + +## +# account_id({accountID}) +# +# This is for what eA calls PacketVersion 9, they send the AID in a 'proper' packet +sub account_id { + my ($self, $args) = @_; + # the account ID is already unpacked into PLAIN TEXT when it gets to this function... + # So lets not fuckup the $accountID since we need that later... someone will prolly have to fix this later on + my $accountID = $args->{accountID}; + debug sprintf("Account ID: %s (%s)\n", unpack('V',$accountID), getHex($accountID)); +} + +## +# marriage_partner_name({String name}) +# +# Name of the partner character, sent to everyone around right before casting "I miss you". +sub marriage_partner_name { + my ($self, $args) = @_; + + message TF("Marriage partner name: %s\n", bytesToString($args->{name})); +} + +sub login_pin_code_request { + # This is ten second-level password login for 2013/3/29 upgrading of twRO + my ($self, $args) = @_; + + if ($args->{flag} ne 0 && ($config{XKore} eq "1" || $config{XKore} eq "3")) { + $timeout{master}{time} = time; + return; + } + + # tRO "workaround" + # receive pincode means that we already received all character pages + $charSvrSet{sync_received_characters} = $charSvrSet{sync_Count} if (exists $charSvrSet{sync_received_characters} && !$masterServer->{private}); + + # flags: + # 0 - correct + # 1 - requested (already defined) + # 2 - requested (not defined) + # 3 - expired + # 4 - requested (not defined) - private servers + # 5 - invalid (official servers?) + # 7 - disabled? + # 8 - incorrect + if ($args->{flag} == 0) { # removed check for seed 0, eA/rA/brA sends a normal seed. + $timeout{'char_login_pause'}{'time'} = time; + message T("PIN code is correct.\n"), "success"; + } elsif ($args->{flag} == 1) { + # PIN code query request. + $accountID = $args->{accountID}; + debug sprintf("Account ID: %s (%s)\n", unpack('V',$accountID), getHex($accountID)); + + message T("Server requested PIN password in order to select your character.\n"), "connection"; + return if ($config{loginPinCode} eq '' && !($self->queryAndSaveLoginPinCode())); + $messageSender->sendLoginPinCode($args->{seed}, 0); + } elsif ($args->{flag} == 2 or $args->{flag} == 4) { + # PIN code has never been set before, so set it. + warning T("PIN password is not set for this account.\n"), "connection"; + return if ($config{loginPinCode} eq '' && !($self->queryAndSaveLoginPinCode())); + + while ((($config{loginPinCode} =~ /[^0-9]/) || (length($config{loginPinCode}) != 4)) && + !($self->queryAndSaveLoginPinCode("Your PIN should never contain anything but exactly 4 numbers.\n"))) { + error T("Your PIN should never contain anything but exactly 4 numbers.\n"); + } + $messageSender->sendLoginPinCode($args->{seed}, 1); + } elsif ($args->{flag} == 3) { + # should we use the same one again? is it possible? + warning T("PIN password expired.\n"), "connection"; + return if ($config{loginPinCode} eq '' && !($self->queryAndSaveLoginPinCode())); + + while ((($config{loginPinCode} =~ /[^0-9]/) || (length($config{loginPinCode}) != 4)) && + !($self->queryAndSaveLoginPinCode("Your PIN should never contain anything but exactly 4 numbers.\n"))) { + error T("Your PIN should never contain anything but exactly 4 numbers.\n"); + } + $messageSender->sendLoginPinCode($args->{seed}, 1); + } elsif ($args->{flag} == 5) { + # PIN code invalid. + error T("PIN code is invalid, don't use sequences or repeated numbers.\n"); + # configModify('loginPinCode', '', 1); + return if (!($self->queryAndSaveLoginPinCode(T("The login PIN code that you entered is invalid. Please re-enter your login PIN code.")))); + $messageSender->sendLoginPinCode($args->{seed}, 0); + } elsif ($args->{flag} == 7) { + # PIN code disabled. + $accountID = $args->{accountID}; + debug sprintf("Account ID: %s (%s)\n", unpack('V',$accountID), getHex($accountID)); + + # call charSelectScreen + $self->{lockCharScreen} = 0; + $timeout{'char_login_pause'}{'time'} = time; + } elsif ($args->{flag} == 8) { + # PIN code incorrect. + error T("PIN code is incorrect.\n"); + #configModify('loginPinCode', '', 1); + return if (!($self->queryAndSaveLoginPinCode(T("The login PIN code that you entered is incorrect. Please re-enter your login PIN code.")))); + $messageSender->sendLoginPinCode($args->{seed}, 0); + } else { + debug("login_pin_code_request: unknown flag $args->{flag}\n"); + } + + $timeout{master}{time} = time; +} + +sub login_pin_new_code_result { + my ($self, $args) = @_; + + if ($args->{flag} == 2) { + # PIN code invalid. + error T("PIN code is invalid, don't use sequences or repeated numbers.\n"); + #configModify('loginPinCode', '', 1); + return if (!($self->queryAndSaveLoginPinCode(T("PIN code is invalid, don't use sequences or repeated numbers.\n")))); + + # there's a bug in bRO where you can use letters or symbols or even a string as your PIN code. + # as a result this will render you unable to login again (forever?) using the official client + # and this is detectable and can result in a permanent ban. we're using this code in order to + # prevent this. - revok 17.12.2012 + while ((($config{loginPinCode} =~ /[^0-9]/) || (length($config{loginPinCode}) != 4)) && + !($self->queryAndSaveLoginPinCode("Your PIN should never contain anything but exactly 4 numbers.\n"))) { + error T("Your PIN should never contain anything but exactly 4 numbers.\n"); + } + + $messageSender->sendLoginPinCode($args->{seed}, 0); + } +} + +sub actor_status_active { + my ($self, $args) = @_; + return unless changeToInGameState(); + my ($type, $ID, $tick, $unknown1, $unknown2, $unknown3, $unknown4) = @{$args}{qw(type ID tick unknown1 unknown2 unknown3 unknown4)}; + my $flag = (exists $args->{flag}) ? $args->{flag} : 1; + my $status = defined $statusHandle{$type} ? $statusHandle{$type} : "UNKNOWN_STATUS_$type"; + $char->cart->changeType($unknown1) if ($type == 673 && defined $unknown1 && ($ID eq $accountID)); # for Cart active + $args->{skillName} = defined $statusName{$status} ? $statusName{$status} : $status; +# ($args->{actor} = Actor::get($ID))->setStatus($status, 1, $tick == 9999 ? undef : $tick, $args->{unknown1}); # need test for '08FF' + ($args->{actor} = Actor::get($ID))->setStatus($status, $flag, $tick == 9999 ? undef : $tick); + # Rolling Cutter counters. + if ( $type == 0x153 && $char->{spirits} != $unknown1 ) { + $char->{spirits} = $unknown1 || 0; + if ( $ID eq $accountID ) { + message TF( "You have %s %s(s) now\n", $char->{spirits}, 'counters' ), "parseMsg_statuslook", 1; + } else { + message TF( "%s has %s %s(s) now\n", $args->{actor}, $char->{spirits}, 'counters' ), "parseMsg_statuslook", 1; + } + } +} + +#099B +sub map_property3 { + my ($self, $args) = @_; + + if ($config{'status_mapType'}){ + $char->setStatus(@$_) for map {[$_->[1], $args->{type} == $_->[0]]} + grep { $args->{type} == $_->[0] || $char->{statuses}{$_->[1]} } + map {[$_, defined $mapTypeHandle{$_} ? $mapTypeHandle{$_} : "UNKNOWN_MAPTYPE_$_"]} + 0 .. List::Util::max $args->{type}, keys %mapTypeHandle; + + if ($args->{info_table}) { + my $info_table = unpack('V1',$args->{info_table}); + for (my $i = 0; $i < 16; $i++) { + if ($info_table&(1<<$i)) { + $char->setStatus(defined $mapPropertyInfoHandle{$i} ? $mapPropertyInfoHandle{$i} : "UNKNOWN_MAPPROPERTY_INFO_$i",1); + } + } + } + } + + $pvp = {6 => 1, 8 => 2, 19 => 3}->{$args->{type}}; + if ($pvp) { + Plugins::callHook('pvp_mode', {pvp => $pvp});# 1 PvP, 2 GvG, 3 Battleground + } +} + +#011F, 01C9, 08C7 +sub area_spell { + my ($self, $args) = @_; + + # Area effect spell; including traps! + my $ID = $args->{ID}; + my $sourceID = $args->{sourceID}; + my $x = $args->{x}; + my $y = $args->{y}; + my $type = $args->{type}; + my $isVisible = $args->{isVisible}; + my $binID; + + if ($spells{$ID} && $spells{$ID}{'sourceID'} eq $sourceID) { + $binID = binFind(\@spellsID, $ID); + $binID = binAdd(\@spellsID, $ID) if ($binID eq ""); + } else { + $binID = binAdd(\@spellsID, $ID); + } + + $spells{$ID}{'ID'} = $ID; + $spells{$ID}{'sourceID'} = $sourceID; + $spells{$ID}{'pos'}{'x'} = $x; + $spells{$ID}{'pos'}{'y'} = $y; + $spells{$ID}{'pos_to'}{'x'} = $x; + $spells{$ID}{'pos_to'}{'y'} = $y; + $spells{$ID}{'binID'} = $binID; + $spells{$ID}{'type'} = $type; + $spells{$ID}{'isVisible'} = $isVisible; + if ($type == 0x81) { + message TF("%s opened Warp Portal on (%d, %d)\n", getActorName($sourceID), $x, $y), "skill"; + } + debug "Area effect ".getSpellName($type)." ($binID) from ".getActorName($sourceID)." appeared on ($x, $y), isVisible = $isVisible\n", "skill", 2; + + if ($args->{switch} eq "01C9") { + message TF("%s has scribbled: %s on (%d, %d)\n", getActorName($sourceID), $args->{scribbleMsg}, $x, $y); + } + + Plugins::callHook('packet_areaSpell', { + ID => $ID, + sourceID => $sourceID, + x => $x, + y => $y, + type => $type, + isVisible => $isVisible + }); +} + +#099F +sub area_spell_multiple2 { + my ($self, $args) = @_; + + # Area effect spells; including traps! + my $len = $args->{len} - 4; + my $spellInfo = $args->{spellInfo}; + my $msg = ""; + my $binID; + my ($ID, $sourceID, $x, $y, $type, $range, $isVisible); + for (my $i = 0; $i < $len; $i += 18) { + $msg = substr($spellInfo, $i, 18); + ($ID, $sourceID, $x, $y, $type, $range, $isVisible) = unpack('a4 a4 v2 V C2', $msg); + + if ($spells{$ID} && $spells{$ID}{'sourceID'} eq $sourceID) { + $binID = binFind(\@spellsID, $ID); + $binID = binAdd(\@spellsID, $ID) if ($binID eq ""); + } else { + $binID = binAdd(\@spellsID, $ID); + } + + $spells{$ID}{'ID'} = $ID; + $spells{$ID}{'sourceID'} = $sourceID; + $spells{$ID}{'pos'}{'x'} = $x; + $spells{$ID}{'pos'}{'y'} = $y; + $spells{$ID}{'pos_to'}{'x'} = $x; + $spells{$ID}{'pos_to'}{'y'} = $y; + $spells{$ID}{'binID'} = $binID; + $spells{$ID}{'type'} = $type; + $spells{$ID}{'range'} = $range; + $spells{$ID}{'isVisible'} = $isVisible; + if ($type == 0x81) { + message TF("%s opened Warp Portal on (%d, %d)\n", getActorName($sourceID), $x, $y), "skill"; + } + debug "Area effect ".getSpellName($type)." ($binID) from ".getActorName($sourceID)." appeared on ($x, $y), isVisible = $isVisible, range = $range\n", "skill", 2; + } + + Plugins::callHook('packet_areaSpell', { + ID => $ID, + sourceID => $sourceID, + x => $x, + y => $y, + type => $type, + isVisible => $isVisible, + range => $range + }); +} + +#09CA +sub area_spell_multiple3 { + my ($self, $args) = @_; + + # Area effect spells; including traps! + my $len = $args->{len} - 4; + my $spellInfo = $args->{spellInfo}; + my $msg = ""; + my $binID; + my ($ID, $sourceID, $x, $y, $type, $range, $isVisible, $lvl); + for (my $i = 0; $i < $len; $i += 19) { + $msg = substr($spellInfo, $i, 19); + ($ID, $sourceID, $x, $y, $type, $range, $isVisible, $lvl) = unpack('a4 a4 v2 V C3', $msg); + + if ($spells{$ID} && $spells{$ID}{'sourceID'} eq $sourceID) { + $binID = binFind(\@spellsID, $ID); + $binID = binAdd(\@spellsID, $ID) if ($binID eq ""); + } else { + $binID = binAdd(\@spellsID, $ID); + } + + $spells{$ID}{'ID'} = $ID; + $spells{$ID}{'sourceID'} = $sourceID; + $spells{$ID}{'pos'}{'x'} = $x; + $spells{$ID}{'pos'}{'y'} = $y; + $spells{$ID}{'pos_to'}{'x'} = $x; + $spells{$ID}{'pos_to'}{'y'} = $y; + $spells{$ID}{'binID'} = $binID; + $spells{$ID}{'type'} = $type; + $spells{$ID}{'range'} = $range; + $spells{$ID}{'isVisible'} = $isVisible; + $spells{$ID}{'lvl'} = $lvl; + if ($type == 0x81) { + message TF("%s opened Warp Portal on (%d, %d)\n", getActorName($sourceID), $x, $y), "skill"; + } + debug "Area effect ".getSpellName($type)." ($binID) from ".getActorName($sourceID)." appeared on ($x, $y), isVisible = $isVisible, range = $range, lvl = $lvl\n", "skill", 2; + } + + Plugins::callHook('packet_areaSpell', { + ID => $ID, + sourceID => $sourceID, + x => $x, + y => $y, + type => $type, + isVisible => $isVisible, + range => $range, + lvl => $lvl + }); +} + +sub sync_request_ex { + my ($self, $args) = @_; + + return if ($config{XKore} eq 1 || $config{XKore} eq 3); # let the clien hanle this + + # Debug Log + # message "Received Sync Ex : 0x" . $args->{switch} . "\n"; + + # Computing Sync Ex - By Fr3DBr + my $PacketID = $args->{switch}; + + # Getting Sync Ex Reply ID from Table + my $SyncID = $self->{sync_ex_reply}->{$PacketID}; + + # Cleaning Leading Zeros + $PacketID =~ s/^0+//; + + # Cleaning Leading Zeros + $SyncID =~ s/^0+//; + + # Debug Log + #error sprintf("Received Ex Packet ID : 0x%s => 0x%s\n", $PacketID, $SyncID); + + # Converting ID to Hex Number + $SyncID = hex($SyncID); + + # Dispatching Sync Ex Reply + $messageSender->sendReplySyncRequestEx($SyncID); +} + +sub cash_shop_list { + my ($self, $args) = @_; + my $tabcode = $args->{tabcode}; + my $item_pack = $self->{cash_shop_list_pack} || 'v V'; + my $item_len = length pack $item_pack; + my $item_list_len = length $args->{itemInfo}; + # CASHSHOP_TAB_NEW => 0x0, + # CASHSHOP_TAB_POPULAR => 0x1, + # CASHSHOP_TAB_LIMITED => 0x2, + # CASHSHOP_TAB_RENTAL => 0x3, + # CASHSHOP_TAB_PERPETUITY => 0x4, + # CASHSHOP_TAB_BUFF => 0x5, + # CASHSHOP_TAB_RECOVERY => 0x6, + # CASHSHOP_TAB_ETC => 0x7 + # CASHSHOP_TAB_MAX => 8 + my %cashitem_tab = ( + 0 => T('New'), + 1 => T('Popular'), + 2 => T('Limited'), + 3 => T('Rental'), + 4 => T('Perpetuity'), + 5 => T('Buff'), + 6 => T('Recovery'), + 7 => T('Etc'), + ); + debug TF("%s\n" . + "# Name Price\n", + center(' Tab: ' . $cashitem_tab{$tabcode} . ' ', 44, '-')), "list"; + for (my $i = 0; $i < $item_list_len; $i += $item_len) { + my ($ID, $price) = unpack($item_pack, substr($args->{itemInfo}, $i)); + my $name = itemNameSimple($ID); + push(@{$cashShop{list}[$tabcode]}, {item_id => $ID, price => $price}); # add to cashshop + debug(swrite( + "@<< @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< @>>>>>>C", + [$i, $name, formatNumber($price)]), + "list"); + + } +} + +sub cash_shop_open_result { + my ($self, $args) = @_; + #'0845' => ['cash_window_shop_open', 'v2', [qw(cash_points kafra_points)]], + message TF("Cash Points: %sC - Kafra Points: %sC\n", formatNumber ($args->{cash_points}), formatNumber ($args->{kafra_points})); + $cashShop{points} = { + cash => $args->{cash_points}, + kafra => $args->{kafra_points} + }; +} + +sub cash_shop_buy_result { + my ($self, $args) = @_; + # SUCCESS = 0x0, + # WRONG_TAB? = 0x1, // we should take care with this, as it's detectable by the server + # SHORTTAGE_CASH = 0x2, + # UNKONWN_ITEM = 0x3, + # INVENTORY_WEIGHT = 0x4, + # INVENTORY_ITEMCNT = 0x5, + # RUNE_OVERCOUNT = 0x9, + # EACHITEM_OVERCOUNT = 0xa, + # UNKNOWN = 0xb, + # BUSY = 0xc, + my %result = ( + 0 => T('Success'), + 1 => T('Wrong Tab'), + 2 => T('Shorttage cash'), + 3 => T('Unkonwn item'), + 4 => T('Inventory weight'), + 5 => T('Inventory item count'), + 9 => T('Rune overcount'), + 10 => T('Eachitem overcount'), + 11 => T('Unknown'), + 12 => T('Busy'), + ); + if ($args->{result} > 0) { + error TF("Error while buying %s from cash shop. Error code: %d (%s)\n", itemNameSimple($args->{item_id}), $args->{result}, $result{$args->{result}}); + } else { + message TF("Bought %s from cash shop. Current CASH: %d\n", itemNameSimple($args->{item_id}), formatNumber($args->{updated_points})), "success"; + $cashShop{points}->{cash} = $args->{updated_points}; + } + + debug sprintf("Got result ID [%d] while buying %s from CASH Shop. Current CASH: %d \n", $args->{result}, itemNameSimple($args->{item_id}), formatNumber($args->{updated_points})); + +} + +sub sprite_change { + my ($self, $args) = @_; + + my ($ID, $type, $value1, $value2) = @{$args}{qw(ID type value1 value2)}; + my $player = ($ID ne $accountID)? $playersList->getByID($ID) : $char; + return unless $player; + + if ($type == 0) { + $player->{jobID} = $value1; + message TF("%s changed Job to: %s\n", $player, $jobs_lut{$value1}), "parseMsg_statuslook"; + + } elsif ($type == 2) { + if ($value1 ne $player->{weapon}) { + message TF("%s changed Weapon to %s (%d)\n", $player, itemName({nameID => $value1}), $value1), "parseMsg_statuslook", 2; + $player->{weapon} = $value1; + } + if ($value2 ne $player->{shield}) { + message TF("%s changed Shield to %s (%d)\n", $player, itemName({nameID => $value2}), $value2), "parseMsg_statuslook", 2; + $player->{shield} = $value2; + } + } elsif ($type == 3) { + message TF("%s changed Lower headgear to %s (%d)\n", $player, headgearName($value1), $value1), "parseMsg_statuslook"; + $player->{headgear}{low} = $value1; + } elsif ($type == 4) { + message TF("%s changed Upper headgear to %s (%d)\n", $player, headgearName($value1), $value1), "parseMsg_statuslook"; + $player->{headgear}{top} = $value1; + } elsif ($type == 5) { + message TF("%s changed Middle headgear to %s (%d)\n", $player, headgearName($value1), $value1), "parseMsg_statuslook"; + $player->{headgear}{mid} = $value1; + } elsif ($type == 6) { + message TF("%s changed Hair color to: %s (%d)\n", $player, $haircolors{$value1}, $value1), "parseMsg_statuslook"; + $player->{hair_color} = $value1; + } elsif ($type == 9) { + if ($player->{shoes} && $value1 ne $player->{shoes}) { + message TF("%s changed Shoes to: %s\n", $player, itemName({nameID => $value1})), "parseMsg_statuslook", 2; + } + $player->{shoes} = $value1; + } elsif ($type == 12) { + message TF("%s changed Robe to: SPRITE_ROBE_ID=%d\n", $player, $value1, $value1), "parseMsg_statuslook", 2; + } elsif ($type== 7 || $type == 13) { + # Type 7 looks like body palette or body color. Type 13 looks like body2 + debug sprintf("%s changed type= %d. value1=%d, value2=%d\n", $player, $type, $value1, $value2); + } else { + error TF("%s changed unknown sprite type (%d), write about it to OpenKore developer\n", $player, $type), "parseMsg_statuslook"; + } + Plugins::callHook('sprite_job_change'); +} + +sub progress_bar { + my($self, $args) = @_; + message TF("Progress bar loading (time: %d).\n", $args->{time}), 'info'; + $char->{progress_bar} = 1; + $taskManager->add( + new Task::Chained(tasks => [new Task::Wait(seconds => $args->{time}), + new Task::Function(function => sub { + $messageSender->sendProgress(); + message TF("Progress bar finished.\n"), 'info'; + $char->{progress_bar} = 0; + $_[0]->setDone; + })])); +} + +sub progress_bar_stop { + my($self, $args) = @_; + message TF("Progress bar finished.\n", 'info'); +} + +# Sends list of all quest states +# 02b1 <packet len>.W <num>.L { <quest id>.L <active>.B }*num (ZC_ALL_QUEST_LIST) +# 097a <packet len>.W <num>.L { <quest id>.L <active>.B <remaining time>.L <time>.L <count>.W { <mob_id>.L <killed>.W <total>.W <mob name>.24B }*count }*num (ZC_ALL_QUEST_LIST2) +# 09f8 <packet len>.W <num>.L { <quest id>.L <active>.B <remaining time>.L <time>.L <count>.W { <hunt identification>.L <mob type>.L <mob_id>.L <min level>.W <max level>.W <killed>.W <total>.W <mob name>.24B }*count }*num (ZC_ALL_QUEST_LIST3) +sub quest_all_list { + my ( $self, $args ) = @_; + + my $offset = 0; + + my $quest_info; + + if ($args->{switch} eq '02B1') { # DEFAULT PACKET + $quest_info = { + quest_pack => 'V C', + quest_keys => [qw(quest_id active)], + quest_len => 5, + mission_pack => '', + mission_keys => [], + mission_len => 0, + }; + + } elsif ($args->{switch} eq '097A') { # SERVERTYPE >= 20141022 + $quest_info = { + quest_pack => 'V C V2 v', + quest_keys => [qw(quest_id active time_expire time_start mission_amount)], + quest_len => 15, + mission_pack => 'V v2 Z24', + mission_keys => [qw(mob_id mob_count mob_goal mob_name_original)], + mission_len => 32, + }; + + } elsif ($args->{switch} eq '09F8') { # SERVERTYPE >= 20150513 + $quest_info = { + quest_pack => 'V C V2 v', + quest_keys => [qw(quest_id active time_expire time_start mission_amount)], + quest_len => 15, + mission_pack => 'V3 v4 Z24', + mission_keys => [qw(hunt_id mob_type mob_id min_level max_level mob_count mob_goal mob_name_original)], + mission_len => 44, + }; + + } elsif ($args->{switch} eq '0AFF') { # SERVERTYPE >= 20181010 + $quest_info = { + quest_pack => 'V C V2 v', + quest_keys => [qw(quest_id active time_expire time_start mission_amount)], + quest_len => 15, + mission_pack => 'V4 v4 Z24', + mission_keys => [qw(hunt_id hunt_id_cont mob_type mob_id min_level max_level mob_count mob_goal mob_name_original)], + mission_len => 48, + }; + + } else { # this can't happen + return; + } + + # Long quest lists are split up over multiple packets. Only reset the quest list if we've switched maps. + our $quest_generation ||= 0; + our $last_quest_generation ||= 0; + if ( $last_quest_generation != $quest_generation ) { + $last_quest_generation = $quest_generation; + $questList = {}; + } + + for (my $i = 0 ; $i < $args->{quest_amount} ; $i++) { + my $quest; + + @{$quest}{@{$quest_info->{quest_keys}}} = unpack($quest_info->{quest_pack}, substr($args->{message}, $offset, $quest_info->{quest_len})); + + %{$questList->{$quest->{quest_id}}} = %$quest; + + debug "Quest ID: $quest->{quest_id} - active: $quest->{active}\n", "info"; + + $offset += $quest_info->{quest_len}; + + next if !exists $quest->{mission_amount}; + + debug "- Mission amount: $quest->{mission_amount}\n", "info"; + + for ( my $j = 0 ; $j < $quest->{mission_amount}; $j++ ) { + my $mission; + + @{$mission}{@{$quest_info->{mission_keys}}} = unpack($quest_info->{mission_pack}, substr($args->{message}, $offset, $quest_info->{mission_len})); + $mission->{mob_name} = bytesToString($mission->{mob_name_original}); + $mission->{mission_index} = $j; + + %{$questList->{$quest->{quest_id}}->{missions}->{$mission->{mob_id}}} = %$mission; + + debug "- MobID: $mission->{mob_id} - Name: $mission->{mob_name} - Count: $mission->{mob_count} - Goal: $mission->{mob_goal}\n", "info"; + + $offset += $quest_info->{mission_len}; + + Plugins::callHook('quest_mission_added', { + questID => $quest->{quest_id}, + mission_id => $mission->{mob_id} + }); + } + } + + Plugins::callHook('quest_all_list_end'); +} + +# 02b2 <packet len>.W <num>.L { <quest id>.L <start time>.L <expire time>.L <mobs>.W { <mob id>.L <mob count>.W <mob name>.24B }*3 }*num +# note: this packet shows all quests + their missions and has variable length +sub quest_all_mission { + my ($self, $args) = @_; + + my $offset = 0; + + my $quest_info = { + quest_pack => 'V3 v', + quest_keys => [qw(quest_id time_start time_expire mission_amount)], + quest_len => 14, + mission_pack => 'V v Z24', + mission_keys => [qw(mob_id mob_count mob_name_original)], + mission_len => 30, + }; + + for (my $i = 0 ; $i < $args->{mission_amount} ; $i++) { + my $quest; + + @{$quest}{@{$quest_info->{quest_keys}}} = unpack($quest_info->{quest_pack}, substr($args->{message}, $offset, $quest_info->{quest_len})); + + my $char_quest = \%{$questList->{$quest->{quest_id}}}; + + foreach my $key (keys %{$quest}) { + $char_quest->{$key} = $quest->{$key}; + } + + debug "Quest ID: $char_quest->{quest_id} - active: $char_quest->{active}\n", "info"; + + $offset += $quest_info->{quest_len}; + + for ( my $j = 0 ; $j < 3; $j++ ) { + + if ($j >= $char_quest->{mission_amount}) { + $offset += $quest_info->{mission_len}; + next; + } + + my $mission; + + @{$mission}{@{$quest_info->{mission_keys}}} = unpack($quest_info->{mission_pack}, substr($args->{message}, $offset, $quest_info->{mission_len})); + $mission->{mob_name} = bytesToString($mission->{mob_name_original}); + $mission->{mission_index} = $j; + + %{$questList->{$char_quest->{quest_id}}->{missions}->{$mission->{mob_id}}} = %$mission; + + debug "- MobID: $mission->{mob_id} - Name: $mission->{mob_name} - Count: $mission->{mob_count}\n", "info"; + + $offset += $quest_info->{mission_len}; + + Plugins::callHook('quest_mission_added', { + questID => $char_quest->{quest_id}, + mission_id => $mission->{mob_id} + }); + } + } + + Plugins::callHook('quest_all_mission_end'); +} + +# 02b3 <quest id>.L <active>.B <start time>.L <expire time>.L <mobs>.W { <mob id>.L <mob count>.W <mob name>.24B }*3 (ZC_ADD_QUEST) +# 09f9 <quest id>.L <active>.B <start time>.L <expire time>.L <mobs>.W { <hunt identification>.L <mob type>.L <mob id>.L <min level>.W <max level>.W <mob count>.W <mob name>.24B }*3 (ZC_ADD_QUEST_EX) +# note: this packet shows all missions for 1 quest and has fixed length +sub quest_add { + my ($self, $args) = @_; + + my $offset = 0; + + my $quest_info; + + if ($args->{switch} eq '09F9') { # SERVERTYPE >= 20150513 + $quest_info = { + mission_pack => 'V3 v3 Z24', + mission_keys => [qw(hunt_id mob_type mob_id min_level max_level mob_count mob_name_original)], + mission_len => 42, + }; + + } elsif ($args->{switch} eq '0B0C') { # SERVERTYPE >= 20150513 + $quest_info = { + mission_pack => 'V4 v3 Z24', + mission_keys => [qw(hunt_id hunt_id_cont mob_type mob_id min_level max_level mob_count mob_name_original)], + mission_len => 46, + }; + + } else { # DEFAULT PACKET - 02B3 + $quest_info = { + mission_pack => 'V v Z24', + mission_keys => [qw(mob_id mob_count mob_name_original)], + mission_len => 30, + }; + } + + my $quest = \%{$questList->{$args->{questID}}}; + $quest->{quest_id} = $args->{questID}; + $quest->{active} = $args->{active}; + $quest->{time_start} = $args->{time_start}; + $quest->{time_expire} = $args->{time_expire}; + $quest->{mission_amount} = $args->{mission_amount}; + + if ($args->{questID}) { + message TF("Quest: %s has been added.\n", $quests_lut{$args->{questID}} ? "$quests_lut{$args->{questID}}{title} ($args->{questID})" : $args->{questID}), "info"; + } + + for ( my $j = 0 ; $j < 3; $j++ ) { + if ($j >= $quest->{mission_amount}) { + $offset += $quest_info->{mission_len}; + next; + } + my $mission; + + @{$mission}{@{$quest_info->{mission_keys}}} = unpack($quest_info->{mission_pack}, substr($args->{message}, $offset, $quest_info->{mission_len})); + $mission->{mob_name} = bytesToString($mission->{mob_name_original}); + $mission->{mission_index} = $j; + + %{$questList->{$quest->{quest_id}}->{missions}->{$mission->{mob_id}}} = %$mission; + + debug "- MobID: $mission->{mob_id} - Name: $mission->{mob_name} - Count: $mission->{mob_count}\n", "info"; + + $offset += $quest_info->{mission_len}; + + Plugins::callHook('quest_mission_added', { + questID => $quest->{quest_id}, + mission_id => $mission->{mob_id} + }); + } + + Plugins::callHook('quest_added', {questID => $args->{questID}}); +} + +# 02b5 <packet len>.W <mobs>.W { <quest id>.L <mob id>.L <total count>.W <current count>.W }*3 (ZC_UPDATE_MISSION_HUNT) +# 09fa <packet len>.W <mobs>.W { <quest id>.L <hunt identification>.L <total count>.W <current count>.W }*3 (ZC_UPDATE_MISSION_HUNT_EX) (Sends hunt identification which is quest_id * 1000 + mission_id) +sub quest_update_mission_hunt { + my ($self, $args) = @_; + + my $offset = 0; + + my $quest_info; + + if ($args->{switch} eq '09FA') { + $quest_info = { + mission_pack => 'V2 v2', + mission_keys => [qw(questID hunt_id mob_goal mob_count)], + mission_len => 12, + }; + + } elsif ($args->{switch} eq '0AFE') { + $quest_info = { + mission_pack => 'V3 v2', + mission_keys => [qw(questID hunt_id hunt_id_cont mob_goal mob_count)], + mission_len => 16, + }; + } else { # 02B5 and 08FE + $quest_info = { + mission_pack => 'V2 v2', + mission_keys => [qw(questID mob_id mob_goal mob_count)], + mission_len => 12, + }; + } + + # workaround 08FE dont have mission_count + if ($args->{switch} eq '08FE') { + $args->{mission_amount} = (length $args->{message}) / ($quest_info->{mission_len}); + } + + for (my $i = 0; $i < $args->{mission_amount}; $i++) { + my $mission; + + @{$mission}{@{$quest_info->{mission_keys}}} = unpack($quest_info->{mission_pack}, substr($args->{message}, $offset, $quest_info->{mission_len})); + + my $quest = \%{$questList->{$mission->{questID}}}; + + my $mission_id; + + # Mission is saved as hunt_id and server sent hunt_id + if (exists $mission->{hunt_id} && exists $quest->{missions}->{$mission->{hunt_id}}) { + $mission_id = $mission->{hunt_id}; + + # Mission is saved as mob_id and server sent mob_id + } elsif (exists $mission->{mob_id} && exists $quest->{missions}->{$mission->{mob_id}}) { + $mission_id = $mission->{mob_id}; + + # Mission is saved as hunt_id and server sent mob_id + } elsif (exists $mission->{mob_id} && !exists $quest->{missions}->{$mission->{mob_id}}) { + # Search in the quest of a mission with this mob_id + foreach my $current_key (keys %{$quest->{missions}}) { + if (exists $quest->{missions}->{$current_key}{mob_id} && $quest->{missions}->{$current_key}{mob_id} == $mission->{mob_id}) { + $mission_id = $quest->{missions}->{$current_key}{hunt_id}; + last; + } + } + + # Mission is saved as mob_id and server sent hunt_id + } elsif (exists $mission->{hunt_id} && !exists $quest->{missions}->{$mission->{hunt_id}}) { + # Search in the quest of a mission with this hunt_id + foreach my $current_key (keys %{$quest->{missions}}) { + if (exists $quest->{missions}->{$current_key}{hunt_id} && $quest->{missions}->{$current_key}{hunt_id} == $mission->{hunt_id}) { + $mission_id = $quest->{missions}->{$current_key}{mob_id}; + last; + } + } + } + + my $quest_mission = \%{$quest->{missions}->{$mission_id}}; + + $quest_mission->{mob_count} = $mission->{mob_count}; + $quest_mission->{mob_goal} = $mission->{mob_goal}; + + debug "- MobID: $mission->{mob_id} - Name: $mission->{mob_name} - Count: $mission->{mob_count} - Goal: $mission->{mob_goal}\n", "info"; + + if ($config{questDisplayStyle}) { + if ($config{questDisplayStyle} >= 2) { + warning TF("[%s] Quest - defeated [%s] progress (%s/%s)\n", $quests_lut{$mission->{questID}} ? "$quests_lut{$mission->{questID}}{title} ($mission->{questID})" : $mission->{questID}, $quest_mission->{mob_name}, $quest_mission->{mob_count}, $quest_mission->{mob_goal}), "info"; + } else { + warning TF("%s [%s/%s]\n", $quest_mission->{mob_name}, $quest_mission->{mob_count}, $quest_mission->{mob_goal}), "info"; + } + } + + $offset += $quest_info->{mission_len}; + + Plugins::callHook('quest_mission_updated', { + questID => $quest_mission->{questID}, + mission_id => $mission_id, + mobID => $quest_mission->{mob_id}, + count => $quest_mission->{mob_count}, + goal => $quest_mission->{mob_goal} + }); + } + + Plugins::callHook('quest_update_mission_hunt_end'); +} + +# 02B4 +sub quest_delete { + my ($self, $args) = @_; + message TF("Quest: %s has been deleted.\n", $quests_lut{$args->{questID}} ? "$quests_lut{$args->{questID}}{title} ($args->{questID})" : $args->{questID}), "info"; + delete $questList->{$args->{questID}}; + + Plugins::callHook('quest_delete'); +} + +# 02B7 +sub quest_active { + my ($self, $args) = @_; + + message $args->{active} + ? TF("Quest %s is now active.\n", $quests_lut{$args->{questID}} ? "$quests_lut{$args->{questID}}{title} ($args->{questID})" : $args->{questID}) + : TF("Quest %s is now inactive.\n", $quests_lut{$args->{questID}} ? "$quests_lut{$args->{questID}}{title} ($args->{questID})" : $args->{questID}) + , "info"; + + $questList->{$args->{questID}}->{active} = $args->{active}; + + Plugins::callHook('quest_active'); +} + +# 02C1 +sub parse_npc_chat { + my ($self, $args) = @_; + + $args->{actor} = Actor::get($args->{ID}); +} + +sub npc_chat { + my ($self, $args) = @_; + + # like public_chat, but also has color + + my $actor = $args->{actor}; + my $message = $args->{message}; # needs bytesToString or not? + my $position = sprintf("[%s %d, %d]", + $field ? $field->baseName : T("Unknown field,"), + @{$char->{pos_to}}{qw(x y)}); + my $dist; + + if ($message =~ / : /) { + ((my $name), $message) = split / : /, $message, 2; + $dist = 'unknown'; + unless ($actor->isa('Actor::Unknown')) { + $dist = distance($char->{pos_to}, $actor->{pos_to}); + $dist = sprintf("%.1f", $dist) if ($dist =~ /\./); + } + if ($actor->{name} eq $name) { + $name = "$actor"; + } else { + $name = sprintf "%s (%s)", $name, $actor->{binID}; + } + $message = "$name: $message"; + + $position .= sprintf(" [%d, %d] [dist=%s] (%d)", + @{$actor->{pos_to}}{qw(x y)}, + $dist, $actor->{nameID}); + $dist = "[dist=$dist] "; + } + + chatLog("npc", "$position $message\n") if ($config{logChat}); + message TF("%s%s\n", $dist, $message), "npcchat"; + + Plugins::callHook('npc_chat', { + actor => $actor, + ID => $args->{ID}, + message => $message, + }); +} + +# 018d <packet len>.W { <name id>.W { <material id>.W }*3 }* +sub makable_item_list { + my ($self, $args) = @_; + undef $makableList; + my $unpack = $self->{makable_item_list_pack} || 'v4'; + my $len = length pack $unpack; + my $k = 0; + my $msg; + $msg .= center(" " . T("Create Item List") . " ", 79, '-') . "\n"; + for (my $i = 0; $i < length($args->{item_list}); $i += $len) { + my ($nameID, $material_1, $material_2, $material_3) = unpack($unpack, substr($args->{item_list}, $i, $len)); + $makableList->[$k] = $nameID; + $msg .= swrite(sprintf("\@%s \@%s (\@%s)", ('>'x2), ('<'x50), ('<'x6)), [$k, itemNameSimple($nameID), $nameID]); + $k++; + } + $msg .= sprintf("%s\n", ('-'x79)); + message($msg, "list"); + message T("You can now use the 'create' command.\n"), "info"; + + Plugins::callHook('makable_item_list', {item_list => $makableList}); +} + +sub storage_opened { + my ($self, $args) = @_; + $char->storage->open($args); +} + +sub storage_closed { + $char->storage->close(); + message T("Storage closed.\n"), "storage";; + + # Storage log + writeStorageLog(0); + + if ($char->{dcOnEmptyItems} ne "") { + message TF("Disconnecting on empty %s!\n", $char->{dcOnEmptyItems}); + chatLog("k", TF("Disconnecting on empty %s!\n", $char->{dcOnEmptyItems})); + quit(); + } +} + +sub storage_items_stackable { + my ($self, $args) = @_; + + $char->storage->clear; + + $self->_items_list({ + class => 'Actor::Item', + hook => 'packet_storage', + debug_str => 'Stackable Storage Item', + items => [$self->parse_items_stackable($args)], + getter => sub { $char->storage->getByID($_[0]{ID}) }, + adder => sub { $char->storage->add($_[0]) }, + callback => sub { + my ($local_item) = @_; + + $local_item->{amount} = $local_item->{amount} & ~0x80000000; + }, + }); + + $storageTitle = $args->{title} ? $args->{title} : undef; +} + +sub storage_items_nonstackable { + my ($self, $args) = @_; + + $self->_items_list({ + class => 'Actor::Item', + hook => 'packet_storage', + debug_str => 'Non-Stackable Storage Item', + items => [$self->parse_items_nonstackable($args)], + getter => sub { $char->storage->getByID($_[0]{ID}) }, + adder => sub { $char->storage->add($_[0]) }, + }); + + $storageTitle = $args->{title} ? $args->{title} : undef; +} + +sub storage_item_added { + my ($self, $args) = @_; + + my $index = $args->{ID}; + my $amount = $args->{amount}; + + my $item = $char->storage->getByID($index); + if (!$item) { + $item = new Actor::Item(); + $item->{nameID} = $args->{nameID}; + $item->{ID} = $index; + $item->{amount} = $amount; + $item->{type} = $args->{type}; + $item->{identified} = $args->{identified}; + $item->{broken} = $args->{broken}; + $item->{upgrade} = $args->{upgrade}; + $item->{cards} = $args->{cards}; + $item->{options} = $args->{options}; + $item->{name} = itemName($item); + $char->storage->add($item); + } else { + $item->{amount} += $amount; + } + my $disp = TF("Storage Item Added: %s (%d) x %d - %s", + $item->{name}, $item->{binID}, $amount, $itemTypes_lut{$item->{type}}); + message "$disp\n", "drop"; + + $itemChange{$item->{name}} += $amount; + $args->{item} = $item; +} + +sub storage_item_removed { + my ($self, $args) = @_; + + my ($index, $amount) = @{$args}{qw(ID amount)}; + + my $item = $char->storage->getByID($index); + + if ($item) { + Misc::storageItemRemoved($item->{binID}, $amount); + } +} + +sub cart_items_stackable { + my ($self, $args) = @_; + + $self->_items_list({ + class => 'Actor::Item', + hook => 'packet_cart', + debug_str => 'Stackable Cart Item', + items => [$self->parse_items_stackable($args)], + getter => sub { $char->cart->getByID($_[0]{ID}) }, + adder => sub { $char->cart->add($_[0]) }, + }); +} + +sub cart_items_nonstackable { + my ($self, $args) = @_; + + $self->_items_list({ + class => 'Actor::Item', + hook => 'packet_cart', + debug_str => 'Non-Stackable Cart Item', + items => [$self->parse_items_nonstackable($args)], + getter => sub { $char->cart->getByID($_[0]{ID}) }, + adder => sub { $char->cart->add($_[0]) }, + }); +} + +sub cart_item_added { + my ($self, $args) = @_; + + my $index = $args->{ID}; + my $amount = $args->{amount}; + + my $item = $char->cart->getByID($index); + if (!$item) { + $item = new Actor::Item(); + $item->{ID} = $args->{ID}; + $item->{nameID} = $args->{nameID}; + $item->{amount} = $args->{amount}; + $item->{identified} = $args->{identified}; + $item->{broken} = $args->{broken}; + $item->{upgrade} = $args->{upgrade}; + $item->{cards} = $args->{cards}; + $item->{options} = $args->{options}; + $item->{type} = $args->{type} if (exists $args->{type}); + $item->{name} = itemName($item); + $char->cart->add($item); + } else { + $item->{amount} += $args->{amount}; + } + my $disp = TF("Cart Item Added: %s (%d) x %d - %s", + $item->{name}, $item->{binID}, $amount, $itemTypes_lut{$item->{type}}); + message "$disp\n", "drop"; + $itemChange{$item->{name}} += $args->{amount}; + $args->{item} = $item; +} + +sub cart_item_removed { + my ($self, $args) = @_; + + my ($index, $amount) = @{$args}{qw(ID amount)}; + + my $item = $char->cart->getByID($index); + + if ($item) { + Misc::cartItemRemoved($item->{binID}, $amount); + } +} + +# Notifies client of a character parameter change. +# 0121 <current count>.W <max count>.W <current weight>.L <max weight>.L (ZC_NOTIFY_CARTITEM_COUNTINFO) +sub cart_info { + my ($self, $args) = @_; + $char->cart->info($args); + debug "[cart_info] received.\n", "parseMsg"; +} + +sub cart_add_failed { + my ($self, $args) = @_; + + my $reason; + if ($args->{fail} == 0) { + $reason = T('overweight'); + } elsif ($args->{fail} == 1) { + $reason = T('too many items'); + } else { + $reason = TF("Unknown code %s",$args->{fail}); + } + error TF("Can't Add Cart Item (%s)\n", $reason); +} + +sub inventory_items_stackable { + my ($self, $args) = @_; + return unless changeToInGameState(); + + $self->_items_list({ + class => 'Actor::Item', + hook => 'packet_inventory', + debug_str => 'Stackable Inventory Item', + items => [$self->parse_items_stackable($args)], + getter => sub { $char->inventory->getByID($_[0]{ID}) }, + adder => sub { $char->inventory->add($_[0]) }, + callback => sub { + my ($local_item) = @_; + + if (defined $char->{arrow} && $local_item->{ID} eq $char->{arrow}) { + $local_item->{equipped} = 32768; + $char->{equipment}{arrow} = $local_item; + } + } + }); +} + +sub item_list_start { + my ($self, $args) = @_; + $current_item_list = $args->{type}; + + debug "Starting Item List. ID: $args->{type}". ($args->{name} ? " ($args->{name})\n" : "\n"), "info"; + + if ( $args->{type} == INVTYPE_INVENTORY ) { + $char->inventory->onitemListStart(); + } elsif ( $args->{type} == INVTYPE_CART ) { + $char->cart->onitemListStart(); + } elsif ( $args->{type} == INVTYPE_STORAGE || $args->{type} == INVTYPE_GUILD_STORAGE ) { + $char->storage->onitemListStart(); + } else { + warning TF("Unsupported item_list_start type (%s)", $args->{type}), "info"; + } +} + +sub item_list_stackable { + my ($self, $args) = @_; + return unless changeToInGameState(); + + my $arguments = { + class => 'Actor::Item', + debug_str => 'Stackable Item List', + items => [$self->parse_items_stackable($args)], + callback => sub { + my ($local_item) = @_; + + if (defined $char->{arrow} && $local_item->{ID} eq $char->{arrow}) { + $local_item->{equipped} = 32768; + $char->{equipment}{arrow} = $local_item; + } + + } + }; + + if ( $args->{type} == INVTYPE_INVENTORY ) { + $arguments->{hook} = 'packet_inventory'; + $arguments->{getter} = sub { $char->inventory->getByID($_[0]{ID}) }; + $arguments->{adder} = sub { $char->inventory->add($_[0]) }; + } elsif ( $args->{type} == INVTYPE_CART ) { + $arguments->{hook} = 'packet_cart', + $arguments->{getter} = sub { $char->cart->getByID($_[0]{ID}) }, + $arguments->{adder} = sub { $char->cart->add($_[0]) }, + } elsif ( $args->{type} == INVTYPE_STORAGE ) { + $arguments->{hook} = 'packet_storage'; + $arguments->{getter} = sub { $char->storage->getByID($_[0]{ID}) }; + $arguments->{adder} = sub { $char->storage->add($_[0]) }; + } elsif ( $args->{type} == INVTYPE_GUILD_STORAGE ) { + $arguments->{hook} = 'packet_storage'; + $arguments->{getter} = sub { $char->storage->getByID($_[0]{ID}) }; + $arguments->{adder} = sub { $char->storage->add($_[0]) }; + } else { + warning TF("Unsupported item_list_stackable type (%s)", $args->{type}), "info"; + } + + $self->_items_list($arguments); +} + +sub item_list_nonstackable { + my ($self, $args) = @_; + return unless changeToInGameState(); + + my $arguments = { + class => 'Actor::Item', + debug_str => 'Non-Stackable Item List', + items => [$self->parse_items_nonstackable($args)], + callback => sub { + my ($local_item) = @_; + + if ($local_item->{equipped}) { + foreach (%equipSlot_rlut){ + if ($_ & $local_item->{equipped}){ + next if $_ == 10; #work around Arrow bug + next if $_ == 32768; + $char->{equipment}{$equipSlot_lut{$_}} = $local_item; + } + } + } + } + }; + + if ( $args->{type} == INVTYPE_INVENTORY ) { + $arguments->{hook} = 'packet_inventory'; + $arguments->{getter} = sub { $char->inventory->getByID($_[0]{ID}) }; + $arguments->{adder} = sub { $char->inventory->add($_[0]) }; + + } elsif ( $args->{type} == INVTYPE_CART ) { + $arguments->{hook} = 'packet_cart', + $arguments->{getter} = sub { $char->cart->getByID($_[0]{ID}) }, + $arguments->{adder} = sub { $char->cart->add($_[0]) }, + + } elsif ( $args->{type} == INVTYPE_STORAGE ) { + $arguments->{hook} = 'packet_storage'; + $arguments->{getter} = sub { $char->storage->getByID($_[0]{ID}) }; + $arguments->{adder} = sub { $char->storage->add($_[0]) }; + + } elsif ( $args->{type} == INVTYPE_GUILD_STORAGE ) { + $arguments->{hook} = 'packet_storage'; + $arguments->{getter} = sub { $char->storage->getByID($_[0]{ID}) }; + $arguments->{adder} = sub { $char->storage->add($_[0]) }; + + } else { + warning TF("Unsupported item_list_nonstackable type (%s)", $args->{type}), "info"; + } + + $self->_items_list($arguments); +} + +sub item_list_end { + my ($self, $args) = @_; + debug TF("Ending Item List. ID: %s\n", $args->{type}), "info"; + if ( $args->{type} == INVTYPE_INVENTORY ) { + $char->inventory->onitemListEnd(); + } elsif ( $args->{type} == INVTYPE_CART ) { + $char->cart->onitemListEnd(); + } elsif ( $args->{type} == INVTYPE_STORAGE || $args->{type} == INVTYPE_GUILD_STORAGE ) { + $char->storage->onitemListEnd(); + } else { + warning TF("Unsupported item_list_end type (%s)", $args->{type}), "info"; + } + undef $current_item_list; +} + +sub login_error { + my ($self, $args) = @_; + + $net->serverDisconnect(); + if ($args->{type} == REFUSE_INVALID_ID || $args->{type} == REFUSE_INVALID_ID2) { + error TF("Account name [%s] doesn't exist\n", $config{'username'}), "connection"; + if (!$net->clientAlive() && !$config{'ignoreInvalidLogin'} && !UNIVERSAL::isa($net, 'Network::XKoreProxy')) { + my $username = $interface->query(T("Enter your Ragnarok Online username again.")); + if (defined($username)) { + configModify('username', $username, 1); + $timeout_ex{master}{time} = 0; + $conState_tries = 0; + } else { + quit(); + return; + } + } + } elsif ($args->{type} == REFUSE_INVALID_PASSWD || $args->{type} == REFUSE_INVALID_PASSWD2) { + error TF("Password Error for account [%s]\n", $config{'username'}), "connection"; + Plugins::callHook('invalid_password'); + if (!$net->clientAlive() && !$config{'ignoreInvalidLogin'} && !UNIVERSAL::isa($net, 'Network::XKoreProxy')) { + my $password = $interface->query(T("Enter your Ragnarok Online password again."), isPassword => 1); + if (defined($password)) { + configModify('password', $password, 1); + $timeout_ex{master}{time} = 0; + $conState_tries = 0; + } else { + quit(); + return; + } + } + } elsif ($args->{type} == ACCEPT_ID_PASSWD) { + error T("The server has denied your connection.\n"), "connection"; + } elsif ($args->{type} == REFUSE_BAN_BY_GM || $args->{type} == REFUSE_NOT_CONFIRMED) { + $interface->errorDialog(T("Critical Error: Your account has been blocked.")); + $quit = 1 unless ($net->clientAlive()); + } elsif ($args->{type} == REFUSE_INVALID_VERSION) { + my $master = $masterServer; + error TF("Connect failed, something is wrong with the login settings:\n" . + "version: %s\n" . + "master_version: %s\n" . + "serverType: %s\n", $master->{version}, $master->{master_version}, $masterServer->{serverType}), "connection"; + relog(30); + } elsif ($args->{type} == REFUSE_BLOCK_TEMPORARY) { + error TF("The server is temporarily blocking your connection until %s\n", $args->{date}), "connection"; + } elsif ($args->{type} == REFUSE_USER_PHONE_BLOCK) { #Phone lock + error T("Please dial to activate the login procedure.\n"), "connection"; + Plugins::callHook('dial'); + relog(10); + } elsif ($args->{type} == ACCEPT_LOGIN_USER_PHONE_BLOCK) { + error T("Mobile Authentication: Max number of simultaneous IP addresses reached.\n"), "connection"; + } elsif ($args->{type} == REFUSE_EMAIL_NOT_CONFIRMED || $args->{type} == REFUSE_EMAIL_NOT_CONFIRMED2) { + error T("Account email address not confirmed.\n"), "connection"; + Misc::offlineMode() unless $config{ignoreInvalidLogin}; + } elsif ($args->{type} == REFUSE_BLOCKED_ID) { + error TF("The server is blocking connection from this user (%d).\n", $args->{error}), "connection"; + Misc::offlineMode() unless $config{ignoreInvalidLogin}; + } elsif ($args->{type} == REFUSE_BLOCKED_COUNTRY) { + error T("The server is blocking connections from your country.\n"); + Misc::offlineMode() unless $config{ignoreInvalidLogin}; + } elsif ($args->{type} == REFUSE_BILLING || $args->{type} == REFUSE_BILLING2) { + error TF("The server is blocking your connection due to billing issues (%d) (%d).\n", $args->{type}, $args->{error}); + Misc::offlineMode() unless $config{ignoreInvalidLogin}; + } elsif ($args->{type} == REFUSE_CHANGE_PASSWD_FORCE2) { + error T("The server demands a password change for this account.\n"); + error TF("Password Error for account [%s]\n", $config{'username'}), "connection"; + Plugins::callHook('invalid_password'); + if (!$net->clientAlive() && !$config{'ignoreInvalidLogin'} && !UNIVERSAL::isa($net, 'Network::XKoreProxy')) { + my $password = $interface->query(T("Enter your Ragnarok Online password again."), isPassword => 1); + if (defined($password)) { + configModify('password', $password, 1); + $timeout_ex{master}{time} = 0; + $conState_tries = 0; + } else { + quit(); + return; + } + } + } elsif ($args->{type} == REFUSE_ACCOUNT_NOT_PREMIUM) { + error TF("Account [%s] doesn't have access to Premium Server\n", $config{'username'}), "connection"; + quit(); + return; + } elsif ($args->{type} == REFUSE_NOT_ALLOWED_IP_ON_TESTING) { + # this can also mens server under maintenance + error TF("Your connection is currently delayed. You can connect again later.\n"), "connection"; + Misc::offlineMode(); + } elsif ($args->{type} == REFUSE_TOKEN_EXPIRED) { + error TF("Your connection was refused due to expired Token.\n"), "connection"; + } elsif ($args->{type} == REFUSE_BAN_ACCOUNT) { + error TF("Your account has been banned.\n"), "connection"; + Plugins::callHook('account_banned'); + } else { + error TF("The server has denied your connection for unknown reason (%d).\n", $args->{type}), 'connection'; + } + + if ($args->{type} != REFUSE_INVALID_VERSION && $versionSearch) { + $versionSearch = 0; + writeSectionedFileIntact(Settings::getTableFilename("servers.txt"), \%masterServers); + } +} + +sub login_error_game_login_server { + error T("Error logging into Character Server (invalid character specified)...\n"), 'connection'; + $net->setState(1); + undef $conState_tries; + $timeout_ex{master}{time} = time; + $timeout_ex{master}{timeout} = $timeout{'reconnect'}{'timeout'}; + $net->serverDisconnect(); +} + +sub character_deletion_successful { + if (defined $AI::temp::delIndex) { + message TF("Character %s (%d) deleted.\n", $chars[$AI::temp::delIndex]{name}, $AI::temp::delIndex), "info"; + delete $chars[$AI::temp::delIndex]; + undef $AI::temp::delIndex; + for (my $i = 0; $i < @chars; $i++) { + delete $chars[$i] if ($chars[$i] && !scalar(keys %{$chars[$i]})) + } + } else { + message T("Character deleted.\n"), "info"; + } + + if (charSelectScreen() == 1) { + $net->setState(3); + $firstLoginMap = 1; + $startingzeny = $chars[$config{'char'}]{'zeny'} unless defined $startingzeny; + $sentWelcomeMessage = 1; + } +} + +sub character_deletion_failed { + error T("Character cannot be deleted. Your e-mail address was probably wrong.\n"); + undef $AI::temp::delIndex; + if (charSelectScreen() == 1) { + $net->setState(3); + $firstLoginMap = 1; + $startingzeny = $chars[$config{'char'}]{'zeny'} unless defined $startingzeny; + $sentWelcomeMessage = 1; + } +} + +# Notifies the client, that it is walking (ZC_NOTIFY_PLAYERMOVE). +# 0087 <walk start time>.L <walk data>.6B +sub character_moves { + my ($self, $args) = @_; + + return unless changeToInGameState(); + makeCoordsFromTo($char->{pos}, $char->{pos_to}, $args->{coords}); + my $dist = blockDistance($char->{pos}, $char->{pos_to}); + debug "You're moving from ($char->{pos}{x}, $char->{pos}{y}) to ($char->{pos_to}{x}, $char->{pos_to}{y}) - distance $dist\n", "parseMsg_move"; + $char->{time_move} = time; + + my $speed = ($char->{walk_speed} || 0.12); + my $my_solution = get_solution($field, $char->{pos}, $char->{pos_to}); + my $time = calcTimeFromSolution($my_solution, $speed); + $char->{solution} = $my_solution; + $char->{time_move_calc} = $time; + + # Correct the direction in which we're looking + my (%vec, $degree); + getVector(\%vec, $char->{pos_to}, $char->{pos}); + $degree = vectorToDegree(\%vec); + if (defined $degree) { + my $direction = int sprintf("%.0f", (360 - $degree) / 45); + $char->{look}{body} = $direction & 0x07; + $char->{look}{head} = 0; + } + + # Ugly; AI code in network subsystem! This must be fixed. + if (AI::action eq "mapRoute" && $config{route_escape_reachedNoPortal} && $dist eq "0.0"){ + if (!$portalsID[0]) { + if ($config{route_escape_shout} ne "" && !defined($timeout{ai_route_escape}{time})){ + sendMessage("c", $config{route_escape_shout}); + } + $timeout{ai_route_escape}{time} = time; + AI::queue("escape"); + } + } +} + +sub character_name { + my ($self, $args) = @_; + my $name; # Type: String + + $name = bytesToString($args->{name}); + + if ($guild{member}) { + foreach my $guildMember (@{$guild{member}}) { + if ($guildMember->{charID} eq $args->{ID}) { + $guildMember->{name} = $name; + last; + } + } + } + + debug "Character name received: $name\n"; +} + +sub character_status { + my ($self, $args) = @_; + + my $actor = Actor::get($args->{ID}); + + if ($args->{switch} eq '028A') { + $actor->{lv} = $args->{lv}; # TODO: test if it is ok to use this piece of information + $actor->{opt3} = $args->{opt3}; + } elsif ($args->{switch} eq '0229' || $args->{switch} eq '0119') { + $actor->{opt1} = $args->{opt1}; + $actor->{opt2} = $args->{opt2}; + } + + $actor->{option} = $args->{option}; + + setStatus($actor, $args->{opt1}, $args->{opt2}, $args->{option}); +} + +# Whisper ignore list (ZC_WHISPER_LIST). +# 00D4 <packet len>.W { <char name>.24B }* +sub whisper_list { + my ($self, $args) = @_; + + my @whisperList = unpack 'x4' . (' Z24' x (($args->{RAW_MSG_SIZE}-4)/24)), $args->{RAW_MSG}; + + debug "whisper_list: @whisperList\n", "parseMsg"; +} + +# Inform client whether chatroom creation was successful or not (ZC_ACK_CREATE_CHATROOM). +# 00D6 <flag>.B +# flag: +# 0 = Room has been successfully created (opens chat room) +# 1 = Room limit exceeded +# 2 = Same room already exists +sub chat_created { + my ($self, $args) = @_; + + $currentChatRoom = $accountID; + $chatRooms{$accountID} = {%createdChatRoom}; + binAdd(\@chatRoomsID, $accountID); + binAdd(\@currentChatRoomUsers, $char->{name}); + message T("Chat Room Created\n"); + + Plugins::callHook('chat_created', {chat => $chatRooms{$accountID}}); +} + +# Display a chat above the owner (ZC_ROOM_NEWENTRY). +# 00D7 <packet len>.W <owner id>.L <char id>.L <limit>.W <users>.W <type>.B <title>.?B +# type: +# 0 = private (password protected) +# 1 = public +# 2 = arena (npc waiting room) +# 3 = PK zone (non-clickable) +sub chat_info { + my ($self, $args) = @_; + + my $title = bytesToString($args->{title}); + + my $chat = $chatRooms{$args->{ID}}; + if (!$chat || !%{$chat}) { + $chat = $chatRooms{$args->{ID}} = {}; + binAdd(\@chatRoomsID, $args->{ID}); + } + $chat->{len} = $args->{len}; + $chat->{title} = $title; + $chat->{ownerID} = $args->{ownerID}; + $chat->{limit} = $args->{limit}; + $chat->{public} = $args->{public}; + $chat->{num_users} = $args->{num_users}; + + Plugins::callHook('packet_chatinfo', { + chatID => $args->{ID}, + ownerID => $args->{ownerID}, + title => $title, + limit => $args->{limit}, + public => $args->{public}, + num_users => $args->{num_users} + }); +} + +# Notifies the client about entering a chatroom (ZC_ENTER_ROOM). +# 00DB <packet len>.W <chat id>.L { <role>.L <name>.24B }* +# role: +# 0 = owner (menu) +# 1 = normal +sub chat_users { + my ($self, $args) = @_; + + my $msg = $args->{RAW_MSG}; + + my $ID = substr($args->{RAW_MSG},4,4); + $currentChatRoom = $ID; + + my $chat = $chatRooms{$currentChatRoom} ||= {}; + + $chat->{num_users} = 0; + for (my $i = 8; $i < $args->{RAW_MSG_SIZE}; $i += 28) { + my ($type, $chatUser) = unpack('V Z24', substr($msg, $i, 28)); + + $chatUser = bytesToString($chatUser); + + if ($chat->{users}{$chatUser} eq "") { + binAdd(\@currentChatRoomUsers, $chatUser); + if ($type == 0) { + $chat->{users}{$chatUser} = 2; + } else { + $chat->{users}{$chatUser} = 1; + } + $chat->{num_users}++; + } + } + + message TF("You have joined the Chat Room %s\n", $chat->{title}); + + Plugins::callHook('chat_joined', {chat => $chat}); +} + +# Displays messages regarding join chat failures (ZC_REFUSE_ENTER_ROOM). +# 00DA <result>.B +# result: +# 0 = room full +# 1 = wrong password +# 2 = kicked +# 3 = success (no message) +# 4 = no enough zeny +# 5 = too low level +# 6 = too high level +# 7 = unsuitable job class +sub chat_join_result { + my ($self, $args) = @_; + + if ($args->{type} == 0) { + message T("Can't join Chat Room - Room is Full\n"); + } elsif ($args->{type} == 1) { + message T("Can't join Chat Room - Incorrect Password\n"); + } elsif ($args->{type} == 2) { + message T("Can't join Chat Room - You're Kicked\n"); + } elsif ($args->{type} == 2) { + message T("Joined Chat Room\n"); + } elsif ($args->{type} == 2) { + message T("Can't join Chat Room - No Enough Zeny\n"); # ?? + } elsif ($args->{type} == 2) { + message T("Can't join Chat Room - You're Low Level\n"); + } elsif ($args->{type} == 2) { + message T("Can't join Chat Room - You're High Level\n"); + } elsif ($args->{type} == 2) { + message T("Can't join Chat Room - You're Unsuitable Job Class\n"); + } else { + message TF("Can't join Chat Room - Unknown Reason (%s)\n", $args->{type}); + } +} + +# Chatroom properties adjustment (ZC_CHANGE_CHATROOM). +# 00DF <packet len>.W <owner id>.L <chat id>.L <limit>.W <users>.W <type>.B <title>.?B +# type: +# 0 = private (password protected) +# 1 = public +# 2 = arena (npc waiting room) +# 3 = PK zone (non-clickable) +sub chat_modified { + my ($self, $args) = @_; + + my $title = bytesToString($args->{title}); + + my ($ownerID, $chat_ID, $limit, $public, $num_users) = @{$args}{qw(ownerID ID limit public num_users)}; + my $ID; + if ($ownerID eq $accountID) { + $ID = $accountID; + } else { + $ID = $chat_ID; + } + + my %chat = (); + $chat{title} = $title; + $chat{ownerID} = $ownerID; + $chat{limit} = $limit; + $chat{public} = $public; + $chat{num_users} = $num_users; + + Plugins::callHook('chat_modified', { + ID => $ID, + old => $chatRooms{$ID}, + new => \%chat, + }); + + $chatRooms{$ID} = {%chat}; + + message T("Chat Room Properties Modified\n"); +} + +# Announce the new owner (ZC_ROLE_CHANGE). +# 00E1 <role>.L <nick>.24B +# role: +# 0 = owner (menu) +# 1 = normal +sub chat_newowner { + my ($self, $args) = @_; + + my $user = bytesToString($args->{user}); + if ($args->{type} == 0) { + if ($user eq $char->{name}) { + $chatRooms{$currentChatRoom}{ownerID} = $accountID; + } else { + my $player; + for my $p (@$playersList) { + if ($p->{name} eq $user) { + $player = $p; + last; + } + } + + if ($player) { + my $key = $player->{ID}; + $chatRooms{$currentChatRoom}{ownerID} = $key; + } + } + $chatRooms{$currentChatRoom}{users}{$user} = 2; + } else { + $chatRooms{$currentChatRoom}{users}{$user} = 1; + } +} + +# Notifies clients in a chat about a new member (ZC_MEMBER_NEWENTRY). +# 00DC <users>.W <name>.24B +sub chat_user_join { + my ($self, $args) = @_; + + my $user = bytesToString($args->{user}); + if ($currentChatRoom ne "") { + binAdd(\@currentChatRoomUsers, $user); + $chatRooms{$currentChatRoom}{users}{$user} = 1; + $chatRooms{$currentChatRoom}{num_users} = $args->{num_users}; + message TF("%s has joined the Chat Room\n", $user); + } +} + +# Notify about user leaving the chatroom (ZC_MEMBER_EXIT). +# 00DD <users>.W <nick>.24B <flag>.B +# flag: +# 0 = left +# 1 = kicked +sub chat_user_leave { + my ($self, $args) = @_; + + my $user = bytesToString($args->{user}); + delete $chatRooms{$currentChatRoom}{users}{$user}; + binRemove(\@currentChatRoomUsers, $user); + $chatRooms{$currentChatRoom}{num_users} = $args->{num_users}; + if ($user eq $char->{name}) { + binRemove(\@chatRoomsID, $currentChatRoom); + delete $chatRooms{$currentChatRoom}; + undef @currentChatRoomUsers; + $currentChatRoom = ""; + message T("You left the Chat Room\n"); + Plugins::callHook('chat_leave'); + } else { + message TF("%s has left the Chat Room\n", $user); + } +} + +# Removes the chatroom (ZC_DESTROY_ROOM). +# 00D8 <chat id>.L +sub chat_removed { + my ($self, $args) = @_; + + binRemove(\@chatRoomsID, $args->{ID}); + my $chat = delete $chatRooms{ $args->{ID} }; + + Plugins::callHook('chat_removed', { + ID => $args->{ID}, + chat => $chat, + }); +} + +sub deal_add_other { + my ($self, $args) = @_; + + if ($args->{nameID} > 0) { + my $item = $currentDeal{other}{ $args->{nameID} } ||= {}; + $item->{amount} += $args->{amount}; + $item->{nameID} = $args->{nameID}; + $item->{identified} = $args->{identified}; + $item->{broken} = $args->{broken}; + $item->{upgrade} = $args->{upgrade}; + $item->{cards} = $args->{cards}; + $item->{options} = $args->{options}; + $item->{name} = itemName($item); + message TF("%s added Item to Deal: %s x %s\n", $currentDeal{name}, $item->{name}, $args->{amount}), "deal"; + } elsif ($args->{amount} > 0) { + $currentDeal{other_zeny} += $args->{amount}; + my $amount = formatNumber($args->{amount}); + message TF("%s added %s z to Deal\n", $currentDeal{name}, $amount), "deal"; + } +} + +sub deal_begin { + my ($self, $args) = @_; + + if ($args->{type} == 0) { + error T("That person is too far from you to trade.\n"), "deal"; + Plugins::callHook('error_deal', {type => $args->{type}}); + undef %outgoingDeal; + + } elsif ($args->{type} == 2) { + error T("That person is in another deal.\n"), "deal"; + Plugins::callHook('error_deal', {type => $args->{type}}); + undef %outgoingDeal; + + } elsif ($args->{type} == 3) { + if (%incomingDeal) { + $currentDeal{name} = $incomingDeal{name}; + undef %incomingDeal; + } else { + my $ID = $outgoingDeal{ID}; + my $player; + $player = $playersList->getByID($ID) if (defined $ID); + $currentDeal{ID} = $ID; + if ($player) { + $currentDeal{name} = $player->{name}; + } else { + $currentDeal{name} = T('Unknown #') . unpack("V", $ID); + } + undef %outgoingDeal; + } + message TF("Engaged Deal with %s\n", $currentDeal{name}), "deal"; + Plugins::callHook('engaged_deal', {name => $currentDeal{name}}); + + } elsif ($args->{type} == 5) { + error T("That person is opening storage.\n"), "deal"; + Plugins::callHook('error_deal', {type =>$args->{type}}); + undef %outgoingDeal; + + } else { + error TF("Deal request failed (unknown error %s).\n", $args->{type}), "deal"; + Plugins::callHook('error_deal', {type =>$args->{type}}); + undef %outgoingDeal; + + } +} + +sub deal_cancelled { + undef %incomingDeal; + undef %outgoingDeal; + undef %currentDeal; + message T("Deal Cancelled\n"), "deal"; + Plugins::callHook('cancelled_deal'); +} + +sub deal_complete { + undef %outgoingDeal; + undef %incomingDeal; + undef %currentDeal; + message T("Deal Complete\n"), "deal"; + Plugins::callHook('complete_deal'); +} + +sub deal_finalize { + my ($self, $args) = @_; + if ($args->{type} == 1) { + $currentDeal{other_finalize} = 1; + message TF("%s finalized the Deal\n", $currentDeal{name}), "deal"; + Plugins::callHook('finalized_deal', {name => $currentDeal{name}}); + + } else { + $currentDeal{you_finalize} = 1; + # FIXME: shouldn't we do this when we actually complete the deal? + $char->{zeny} -= $currentDeal{you_zeny}; + message T("You finalized the Deal\n"), "deal"; + } +} + +sub deal_request { + my ($self, $args) = @_; + my $level = $args->{level} || 'Unknown'; # TODO: store this info + my $user = bytesToString($args->{user}); + + $incomingDeal{name} = $user; + $timeout{ai_dealAutoCancel}{time} = time; + message TF("%s (level %s) Requests a Deal\n", $user, $level), "deal"; + message T("Type 'deal' to start dealing, or 'deal no' to deny the deal.\n"), "deal"; + Plugins::callHook('incoming_deal', { + name => $user, + level => $level, + ID => $args->{ID} + }); +} + +sub devotion { + my ($self, $args) = @_; + my $msg = ''; + my $source = Actor::get($args->{sourceID}); + + undef $devotionList->{$args->{sourceID}}; + for (my $i = 0; $i < 5; $i++) { + my $ID = substr($args->{targetIDs}, $i*4, 4); + last if unpack("V", $ID) == 0; + $devotionList->{$args->{sourceID}}->{targetIDs}->{$ID} = $i; + my $actor = Actor::get($ID); + #FIXME: Need a better display + $msg .= skillUseNoDamage_string($source, $actor, 0, 'devotion'); + } + $devotionList->{$args->{sourceID}}->{range} = $args->{range}; + + message "$msg", "devotion"; +} + +sub egg_list { + my ($self, $args) = @_; + my $msg = center(T(" Egg Hatch Candidates "), 38, '-') ."\n"; + for (my $i = 4; $i < $args->{RAW_MSG_SIZE}; $i += 2) { + my $index = unpack("a2", substr($args->{RAW_MSG}, $i, 2)); + my $item = $char->inventory->getByID($index); + $msg .= "$item->{binID} $item->{name}\n"; + } + $msg .= ('-'x38) . "\n". + T("Ready to use command 'pet [hatch|h] #'\n"); + message $msg, "list"; +} + +sub emoticon { + my ($self, $args) = @_; + my $emotion = $emotions_lut{$args->{type}}{display} || "<emotion #$args->{type}>"; + + if ($args->{ID} eq $accountID) { + message "$char->{name}: $emotion\n", "emotion"; + chatLog("e", "$char->{name}: $emotion\n") if (existsInList($config{'logEmoticons'}, $args->{type}) || $config{'logEmoticons'} eq "all"); + + } elsif (my $player = $playersList->getByID($args->{ID})) { + my $name = $player->name; + + #my $dist = "unknown"; + my $dist = distance($char->{pos_to}, $player->{pos_to}); + $dist = sprintf("%.1f", $dist) if ($dist =~ /\./); + + # Translation Comment: "[dist=$dist] $name ($player->{binID}): $emotion\n" + message TF("[dist=%s] %s (%d): %s\n", $dist, $name, $player->{binID}, $emotion), "emotion"; + chatLog("e", "$name".": $emotion\n") if (existsInList($config{'logEmoticons'}, $args->{type}) || $config{'logEmoticons'} eq "all"); + + my $index = AI::findAction("follow"); + if ($index ne "") { + my $masterID = AI::args($index)->{ID}; + if ($config{'followEmotion'} && $masterID eq $args->{ID} && + blockDistance($char->{pos_to}, $player->{pos_to}) <= $config{'followEmotion_distance'}) + { + my %args = (); + $args{timeout} = time + rand (1) + 0.75; + + if ($args->{type} == 30) { + $args{emotion} = 31; + } elsif ($args->{type} == 31) { + $args{emotion} = 30; + } else { + $args{emotion} = $args->{type}; + } + + AI::queue("sendEmotion", \%args); + } + } + } elsif (my $monster = $monstersList->getByID($args->{ID}) || $slavesList->getByID($args->{ID})) { + my $dist = distance($char->{pos_to}, $monster->{pos_to}); + $dist = sprintf("%.1f", $dist) if ($dist =~ /\./); + + # Translation Comment: "[dist=$dist] $monster->name ($monster->{binID}): $emotion\n" + message TF("[dist=%s] %s %s (%d): %s\n", $dist, $monster->{actorType}, $monster->name, $monster->{binID}, $emotion), "emotion"; + + } else { + my $actor = Actor::get($args->{ID}); + my $name = $actor->name; + + my $dist = T("unknown"); + if (!$actor->isa('Actor::Unknown')) { + $dist = distance($char->{pos_to}, $actor->{pos_to}); + $dist = sprintf("%.1f", $dist) if ($dist =~ /\./); + } + + message TF("[dist=%s] %s: %s\n", $dist, $actor->nameIdx, $emotion), "emotion"; + chatLog("e", "$name".": $emotion\n") if (existsInList($config{'logEmoticons'}, $args->{type}) || $config{'logEmoticons'} eq "all"); + } + Plugins::callHook('packet_emotion', { + emotion => $emotion, + ID => $args->{ID} + }); +} + +# Notifies the client of a ban or forced disconnect (SC_NOTIFY_BAN). +# 0081 <error code>.B +# error code: +# 0 = BAN_UNFAIR -> "disconnected from server" -> MsgStringTable[3] +# 1 = server closed -> MsgStringTable[4] +# 2 = ID already logged in -> MsgStringTable[5] +# 3 = timeout/too much lag -> MsgStringTable[241] +# 4 = server full -> MsgStringTable[264] +# 5 = underaged -> MsgStringTable[305] +# 8 = Server sill recognizes last connection -> MsgStringTable[441] +# 9 = too many connections from this ip -> MsgStringTable[529] +# 10 = out of available time paid for -> MsgStringTable[530] +# 11 = BAN_PAY_SUSPEND +# 12 = BAN_PAY_CHANGE +# 13 = BAN_PAY_WRONGIP +# 14 = BAN_PAY_PNGAMEROOM +# 15 = disconnected by a GM -> if (servicetype == taiwan) MsgStringTable[579] +# 16 = BAN_JAPAN_REFUSE1 +# 17 = BAN_JAPAN_REFUSE2 +# 18 = BAN_INFORMATION_REMAINED_ANOTHER_ACCOUNT +# 100 = BAN_PC_IP_UNFAIR +# 101 = BAN_PC_IP_COUNT_ALL +# 102 = BAN_PC_IP_COUNT +# 103 = BAN_GRAVITY_MEM_AGREE +# 104 = BAN_GAME_MEM_AGREE +# 105 = BAN_HAN_VALID +# 106 = BAN_PC_IP_LIMIT_ACCESS +# 107 = BAN_OVER_CHARACTER_LIST +# 108 = BAN_IP_BLOCK +# 109 = BAN_INVALID_PWD_CNT +# 110 = BAN_NOT_ALLOWED_JOBCLASS +# 113 = access is restricted between the hours of midnight to 6:00am. +# 115 = You are in game connection ban period. +# ? = disconnected -> MsgStringTable[3] +sub errors { + my ($self, $args) = @_; + + Plugins::callHook('disconnected') if ($net->getState() == Network::IN_GAME); + if ($net->getState() == Network::IN_GAME && + ($config{dcOnDisconnect} > 1 || + ($config{dcOnDisconnect} && + $args->{type} != 3 && + $args->{type} != 10))) { + error T("Auto disconnecting on Disconnect!\n"); + chatLog("k", T("*** You disconnected, auto disconnect! ***\n")); + $quit = 1; + } + + $net->setState(1); + undef $conState_tries; + + $timeout_ex{'master'}{'time'} = time; + $timeout_ex{'master'}{'timeout'} = $timeout{'reconnect'}{'timeout'}; + if (($args->{type} != 0)) { + $net->serverDisconnect(); + } + if ($args->{type} == 0) { + # FIXME BAN_SERVER_SHUTDOWN is 0x1, 0x0 is BAN_UNFAIR + if ($config{'dcOnServerShutDown'} == 1) { + error T("Auto disconnecting on ServerShutDown!\n"); + chatLog("k", T("*** Server shutting down , auto disconnect! ***\n")); + $quit = 1; + } else { + error T("Server shutting down\n"), "connection"; + } + } elsif ($args->{type} == 1) { + if ($config{'dcOnServerClose'} == 1) { + error T("Auto disconnecting on ServerClose!\n"); + chatLog("k", T("*** Server is closed , auto disconnect! ***\n")); + $quit = 1; + } else { + error T("Error: Server is closed\n"), "connection"; + } + } elsif ($args->{type} == 2) { + if ($config{'dcOnDualLogin'} == 1) { + error (TF("Critical Error: Dual login prohibited - Someone trying to login!\n\n" . + "%s will now immediately disconnect.\n", $Settings::NAME)); + chatLog("k", T("*** DualLogin, auto disconnect! ***\n")); + quit(); + } elsif ($config{'dcOnDualLogin'} >= 2) { + error T("Critical Error: Dual login prohibited - Someone trying to login!\n"); + message TF("Reconnecting, wait %s seconds...\n", $config{'dcOnDualLogin'}), "connection"; + $timeout_ex{'master'}{'timeout'} = $config{'dcOnDualLogin'}; + } else { + error T("Critical Error: Dual login prohibited - Someone trying to login!\n"), "connection"; + } + + } elsif ($args->{type} == 3) { + error T("Error: Out of sync with server\n"), "connection"; + } elsif ($args->{type} == 4) { + # fRO: "Your account is not validated, please click on the validation link in your registration mail." + error T("Error: Server is jammed due to over-population.\n"), "connection"; + } elsif ($args->{type} == 5) { + error T("Error: You are underaged and cannot join this server.\n"), "connection"; + } elsif ($args->{type} == 6) { + $interface->errorDialog(T("Critical Error: You must pay to play this account!\n")); + $quit = 1 unless ($net->version == 1); + } elsif ($args->{type} == 8) { + error T("Error: The server still recognizes your last connection\n"), "connection"; + } elsif ($args->{type} == 9) { + error T("Error: IP capacity of this Internet Cafe is full. Would you like to pay the personal base?\n"), "connection"; + } elsif ($args->{type} == 10) { + error T("Error: You are out of available time paid for\n"), "connection"; + } elsif ($args->{type} == 15) { + error T("Error: You have been forced to disconnect by a GM\n"), "connection"; + } elsif ($args->{type} == 101) { + error T("Error: Your account has been suspended until the next maintenance period for possible use of 3rd party programs\n"), "connection"; + } elsif ($args->{type} == 102) { + error T("Error: For an hour, more than 10 connections having same IP address, have made. Please check this matter.\n"), "connection"; + } else { + error TF("Unknown error %s\n", $args->{type}), "connection"; + } +} + +# Sends the whole friends list (ZC_FRIENDS_LIST). +# 0201 <packet len>.W { <account id>.L <char id>.L <name>.24B }* +# 0201 <packet len>.W { <account id>.L <char id>.L }* +sub friend_list { + my ($self, $args) = @_; + + # Friend list + undef @friendsID; + undef %friends; + my $msg = $args->{RAW_MSG}; + my $msg_size = $args->{RAW_MSG_SIZE}; + + my $ID = 0; + for (my $i = 4; $i < $msg_size; $i += 32) { + binAdd(\@friendsID, $ID); + ($friends{$ID}{'accountID'}, + $friends{$ID}{'charID'}, + $friends{$ID}{'name'}) = unpack('a4 a4 Z24', substr($args->{RAW_MSG}, $i, 32)); + + $friends{$ID}{'name'} = bytesToString($friends{$ID}{'name'}); + $friends{$ID}{'online'} = 0; + $ID++; + } +} + +# Toggles a single friend online/offline (ZC_FRIENDS_STATE). +# 0206 <account id>.L <char id>.L <state>.B +# 0206 <account id>.L <char id>.L <state>.B <name>.24B +# state: +# 0 = online +# 1 = offline +sub friend_logon { + my ($self, $args) = @_; + + # Friend In/Out + my $friendAccountID = $args->{friendAccountID}; + my $friendCharID = $args->{friendCharID}; + my $isNotOnline = $args->{isNotOnline}; + + for (my $i = 0; $i < @friendsID; $i++) { + if ($friends{$i}{'accountID'} eq $friendAccountID && $friends{$i}{'charID'} eq $friendCharID) { + $friends{$i}{'online'} = 1 - $isNotOnline; + if ($isNotOnline) { + message TF("Friend %s has disconnected\n", $friends{$i}{name}), undef, 1; + } else { + message TF("Friend %s has connected\n", $friends{$i}{name}), undef, 1; + } + last; + } + } +} + +# Asks a player for permission to be added as friend (ZC_REQ_ADD_FRIENDS). +# 0207 <req account id>.L <req char id>.L <req char name>.24B +sub friend_request { + my ($self, $args) = @_; + + # Incoming friend request + $incomingFriend{'accountID'} = $args->{accountID}; + $incomingFriend{'charID'} = $args->{charID}; + $incomingFriend{'name'} = bytesToString($args->{name}); + message TF("%s wants to be your friend\n", $incomingFriend{'name'}); + message TF("Type 'friend accept' to be friend with %s, otherwise type 'friend reject'\n", $incomingFriend{'name'}); + Plugins::callHook('friend_request', { + accountID => $incomingFriend{'accountID'}, + charID => $incomingFriend{'charID'}, + name => $incomingFriend{'name'} + }); +} + +# Notification about a friend removed (PACKET_ZC_DELETE_FRIENDS). +# 020A <account id>.L <char id>.L +sub friend_removed { + my ($self, $args) = @_; + + # Friend removed + my $friendAccountID = $args->{friendAccountID}; + my $friendCharID = $args->{friendCharID}; + for (my $i = 0; $i < @friendsID; $i++) { + if ($friends{$i}{'accountID'} eq $friendAccountID && $friends{$i}{'charID'} eq $friendCharID) { + message TF("%s is no longer your friend\n", $friends{$i}{'name'}); + binRemove(\@friendsID, $i); + delete $friends{$i}; + last; + } + } +} + +# Notification about the result of a friend add request (ZC_ADD_FRIENDS_LIST). +# 0209 <result>.W <account id>.L <char id>.L <name>.24B +# result: +# 0 = MsgStringTable[821]="You have become friends with (%s)." +# 1 = MsgStringTable[822]="(%s) does not want to be friends with you." +# 2 = MsgStringTable[819]="Your Friend List is full." +# 3 = MsgStringTable[820]="(%s)'s Friend List is full." +sub friend_response { + my ($self, $args) = @_; + + # Response to friend request + my $type = $args->{type}; + my $name = bytesToString($args->{name}); + if ($type == 0) { + my $ID = @friendsID; + binAdd(\@friendsID, $ID); + $friends{$ID}{accountID} = substr($args->{RAW_MSG}, 4, 4); + $friends{$ID}{charID} = substr($args->{RAW_MSG}, 8, 4); + $friends{$ID}{name} = $name; + $friends{$ID}{online} = 1; + message TF("You have become friends with (%s)\n", $name); + } elsif ($type == 1) { + message TF("(%s) does not want to be friends with you\n", $name); + } elsif ($type == 2) { + message T("Your Friend List is full"); + } elsif ($type == 3) { + message TF("%s's Friend List is full\n", $name); + } else { + message TF("%s rejected to be your friend\n", $name); + } +} + +# Result of request to feed a homun/merc (ZC_FEED_MER). +# 022F <result>.B <name id>.W +# result: +# 0 = failure +# 1 = success +sub homunculus_food { + my ($self, $args) = @_; + if ($args->{success}) { + message TF("Fed homunculus with %s\n", itemNameSimple($args->{foodID})), "homunculus"; + } else { + error TF("Failed to feed homunculus with %s: no food in inventory.\n", itemNameSimple($args->{foodID})), "homunculus"; + # auto-vaporize + if ($char->{homunculus} && $char->{homunculus}{hunger} <= 11 && timeOut($char->{homunculus}{vaporize_time}, 5)) { + $messageSender->sendSkillUse(244, 1, $accountID); + $char->{homunculus}{vaporize_time} = time; + error "Critical hunger level reached. Homunculus is put to rest.\n", "homunculus"; + } + } +} + +# TODO: wouldn't it be better if we calculated these only at (first) request after a change in value, if requested at all? +sub slave_calcproperty_handler { + my ($slave, $args) = @_; + # so we don't devide by 0 + # wtf +=pod + $slave->{hp_max} = ($args->{hp_max} > 0) ? $args->{hp_max} : $args->{hp}; + $slave->{sp_max} = ($args->{sp_max} > 0) ? $args->{sp_max} : $args->{sp}; +=cut + + $slave->{attack_speed} = int (200 - (($args->{aspd} < 10) ? 10 : ($args->{aspd} / 10))); +} + +sub EAC_key { + return if ($masterServer->{'ignoreAntiCheatWarning'}); + chatLog("k", T("*** Easy Anti-Cheat Detected ***\n")); + error T("OpenKore don't have support to servers with Easy Anti-Cheat Shield, please read the FAQ (github).\n"); + quit(); +} + +sub gameguard_grant { + my ($self, $args) = @_; + + if ($args->{server} == 0) { + error T("The server Denied the login because GameGuard packets where not replied " . + "correctly or too many time has been spent to send the response.\n" . + "Please verify the version of your poseidon server and try again\n"), "poseidon"; + return; + } elsif ($args->{server} == 1) { + message T("Server granted login request to account server\n"), "poseidon"; + } else { + message T("Server granted login request to char/map server\n"), "poseidon"; + # FIXME + change_to_constate25() if ($masterServer->{'gameGuard'} eq "2"); + } + $net->setState(1.3) if ($net->getState() == 1.2); +} + +sub gameguard_request { + my ($self, $args) = @_; + + return if (($net->version == 1 && $masterServer->{gameGuard} ne '2') || ($masterServer->{gameGuard} == 0)); + Poseidon::Client::getInstance()->query( + substr($args->{RAW_MSG}, 0, $args->{RAW_MSG_SIZE}) + ); + debug "Querying Poseidon\n", "poseidon"; +} + +# Guild alliance and opposition list (ZC_MYGUILD_BASIC_INFO). +# 014C <packet len>.W { <relation>.L <guild id>.L <guild name>.24B }* +sub guild_allies_enemy_list { + my ($self, $args) = @_; + + # Guild Allies/Enemy List + # <len>.w (<type>.l <guildID>.l <guild name>.24B).* + # type=0 Ally + # type=1 Enemy + + # This is the length of the entire packet + my $msg = $args->{RAW_MSG}; + my $len = unpack("v", substr($msg, 2, 2)); + + # clear $guild{enemy} and $guild{ally} otherwise bot will misremember alliances -zdivpsa + $guild{enemy} = {}; $guild{ally} = {}; + + for (my $i = 4; $i < $len; $i += 32) { + my ($type, $guildID, $guildName) = unpack('V2 Z24', substr($msg, $i, 32)); + $guildName = bytesToString($guildName); + if ($type) { + # Enemy guild + $guild{enemy}{$guildID} = $guildName; + } else { + # Allied guild + $guild{ally}{$guildID} = $guildName; + } + debug "Your guild is ".($type ? 'enemy' : 'ally')." with guild $guildID ($guildName)\n", "guild"; + } +} + +# Request for guild alliance (ZC_REQ_ALLY_GUILD). +# 0171 <inviter account id>.L <guild name>.24B +sub guild_ally_request { + my ($self, $args) = @_; + + my $ID = $args->{ID}; # is this a guild ID or account ID? Freya calls it an account ID + my $name = bytesToString($args->{guildName}); # Type: String + + message TF("Incoming Request to Ally Guild '%s'\n", $name); + $incomingGuild{ID} = $ID; + $incomingGuild{Type} = 2; + $timeout{ai_guildAutoDeny}{time} = time; +} + +# Notifies the client about the result of a guild break (ZC_ACK_DISORGANIZE_GUILD_RESULT). +# 015E <reason>.L +# 0 = success +# 1 = invalid key (guild name, @see clif_parse_GuildBreak) +# 2 = there are still members in the guild +sub guild_broken { + my ($self, $args) = @_; + my $flag = $args->{flag}; + + if ($flag == 2) { + error T("Guild can not be undone: there are still members in the guild\n"); + } elsif ($flag == 1) { + error T("Guild can not be undone: invalid key\n"); + } elsif ($flag == 0) { + message T("Guild broken.\n"); + undef %{$char->{guild}}; + undef $char->{guildID}; + undef %guild; + } else { + error TF("Guild can not be undone: unknown reason (flag: %s)\n", $flag); + } +} + +# Guild creation result (ZC_RESULT_MAKE_GUILD). +# 0167 <result>.B +# result: +# 0 = "Guild has been created." +# 1 = "You are already in a Guild." +# 2 = "That Guild Name already exists." +# 3 = "You need the neccessary item to create a Guild." +sub guild_create_result { + my ($self, $args) = @_; + my $type = $args->{type}; + + my %types = ( + 0 => T("Guild create successful.\n"), + 2 => T("Guild create failed: Guild name already exists.\n"), + 3 => T("Guild create failed: Emperium is needed.\n") + ); + if ($types{$type}) { + message $types{$type}; + } else { + message TF("Guild create: Unknown error %s\n", $type); + } +} + +# Guild basic information +# 0150 <guild id>.L <level>.L <member num>.L <member max>.L <exp>.L <max exp>.L <points>.L <honor>.L <virtue>.L <emblem id>.L <name>.24B <master name>.24B <manage land>.16B (ZC_GUILD_INFO) +# 01B6 <guild id>.L <level>.L <member num>.L <member max>.L <exp>.L <max exp>.L <points>.L <honor>.L <virtue>.L <emblem id>.L <name>.24B <master name>.24B <manage land>.16B <zeny>.L (ZC_GUILD_INFO2) +# 0A84 <guild id>.L <level>.L <member num>.L <member max>.L <exp>.L <max exp>.L <points>.L <honor>.L <virtue>.L <emblem id>.L <name>.24B <manage land>.16B <zeny>.L <master char id>.L (ZC_GUILD_INFO3) +# 0B7B +sub guild_info { + my ($self, $args) = @_; + # Guild Info + foreach (@{$args->{KEYS}}) { + $guild{$_} = $args->{$_}; + } + $guild{name} = bytesToString($args->{name}); + $guild{master} = bytesToString($args->{master}) if ($args->{master}); + $guild{members}++; # count ourselves in the guild members count +} + +# Guild member manager information +# 0154 <packet len>.W { <account>.L <char id>.L <hair style>.W <hair color>.W <gender>.W <class>.W <level>.W <contrib exp>.L <state>.L <position>.L <memo>.50B <name>.24B }* (ZC_MEMBERMGR_INFO) +# 0AA5 <packet len>.W { <account>.L <char id>.L <hair style>.W <hair color>.W <gender>.W <class>.W <level>.W <contrib exp>.L <state>.L <position>.L <lastlogin>.L }* +# 0B7D +# state: +# 0 = offline +# 1 = online +sub guild_members_list { + my ($self, $args) = @_; + + my $guild_member_info; + if ($args->{switch} eq "0B7D") { + $guild_member_info = { + len => 58, + types => 'a4 a4 v5 V4 Z24', + keys => [qw(ID charID hair_style hair_color sex jobID lv contribution online position lastLoginTime name)], + }; + + } elsif ($args->{switch} eq "0AA5") { + $guild_member_info = { + len => 34, + types => 'a4 a4 v5 V4', + keys => [qw(ID charID hair_style hair_color sex jobID lv contribution online position lastLoginTime)], + }; + + } else { # 0154, others + $guild_member_info = { + len => 104, + types => 'a4 a4 v5 V3 Z50 Z24', + keys => [qw(ID charID hair_style hair_color sex jobID lv contribution online position memo name)], + }; + } + + delete $guild{member}; + my $index = 0; + + for (my $i = 0; $i < length($args->{member_list}); $i += $guild_member_info->{len}) { + @{$guild{member}[$index]}{@{$guild_member_info->{keys}}} = unpack($guild_member_info->{types}, substr($args->{member_list}, $i, $guild_member_info->{len})); + + $guild{member}[$index]{name} = bytesToString($guild{member}[$index]{name}) if ($guild{member}[$index]{name}); + $messageSender->sendGetCharacterName($guild{member}[$index]{charID}) if ($args->{switch} eq "0AA5"); + $index++; + } +} + +# Reply to invite request (ZC_ACK_REQ_JOIN_GUILD). +# 0169 <answer>.B +# answer: +# 0 = Already in guild. +# 1 = Offer rejected. +# 2 = Offer accepted. +# 3 = Guild full. +sub guild_invite_result { + my ($self, $args) = @_; + + my $type = $args->{type}; + + my %types = ( + 0 => T('Target is already in a guild.'), + 1 => T('Target has denied.'), + 2 => T('Target has accepted.'), + 3 => T('Your guild is full.') + ); + if ($types{$type}) { + message TF("Guild join request: %s\n", $types{$type}); + } else { + message TF("Guild join request: Unknown %s\n", $type); + } +} + +# Guild XY locators (ZC_NOTIFY_POSITION_TO_GUILDM) +# 01EB <account id>.L <x>.W <y>.W +sub guild_location { + my ($self, $args) = @_; + + foreach my $guildMember (@{$guild{member}}) { + # check if char is the online (we can have more then 1 char per account in our guild) + # why use accountID instead of charID? + if ($guildMember->{ID} eq $args->{ID} && $guildMember->{online}) { + last if ($args->{x} == 0 || $args->{y} == 0); + $guildMember->{pos}{x} = $args->{x}; + $guildMember->{pos}{y} = $args->{y}; + $guildMember->{pos_to}{x} = $args->{x}; + $guildMember->{pos_to}{y} = $args->{y}; + last; + } + } +} + +# Notifies clients of a guild of a leaving member (ZC_ACK_LEAVE_GUILD). +# 015A <char name>.24B <reason>.40B +# 0A83 +sub guild_leave { + my ($self, $args) = @_; + my ($name, $msg); + + if ($args->{name}) { + $name = bytesToString($args->{name}); + } elsif ($args->{charID}) { + foreach my $guildMember (@{$guild{member}}) { + if ($guildMember->{charID} eq $args->{charID}) { + $name = $guildMember->{name}; + binRemove(\@{$guild{member}}, $guildMember); + last; + } + } + } + + message TF("%s has left the guild.\n" . + "Reason: %s\n", $name, bytesToString($args->{message})), "guildchat"; +} + +# Notifies clients of a guild of an expelled member. +# 015C <char name>.24B <reason>.40B <account name>.24B (ZC_ACK_BAN_GUILD) +# 0839 <char name>.24B <reason>.40B (ZC_ACK_BAN_GUILD_SSO) +# 0A82 +sub guild_expulsion { + my ($self, $args) = @_; + my $name; + + if ($args->{name}) { + $name = bytesToString($args->{name}); + } elsif ($args->{charID}) { + foreach my $guildMember (@{$guild{member}}) { + if ($guildMember->{charID} eq $args->{charID}) { + $name = $guildMember->{name}; + binRemove(\@{$guild{member}}, $guildMember); + last; + } + } + } + + message TF("%s has been removed from the guild.\n" . + "Reason: %s\n", $name, bytesToString($args->{message})), "guildchat"; +} + +# Guild member login notice. +# 016D <account id>.L <char id>.L <status>.L (ZC_UPDATE_CHARSTAT) +# 01F2 <account id>.L <char id>.L <status>.L <gender>.W <hair style>.W <hair color>.W (ZC_UPDATE_CHARSTAT2) +# status: +# 0 = offline +# 1 = online +# TODO: we can update the following information from this package: sex, hair_style, hair_color +sub guild_member_online_status { + my ($self, $args) = @_; + + foreach my $guildMember (@{$guild{member}}) { + if ($guildMember->{charID} eq $args->{charID}) { + if ($guildMember->{online} = $args->{online}) { + message TF("Guild member %s logged in.\n", $guildMember->{name}), "guildchat"; + } else { + message TF("Guild member %s logged out.\n", $guildMember->{name}), "guildchat"; + } + last; + } + } +} + +# Notifies clients in a guild about updated member position assignments (ZC_ACK_REQ_CHANGE_MEMBERS). +# 0156 <packet len>.W { <account id>.L <char id>.L <position id>.L }* +sub guild_update_member_position { + my ($self, $args) = @_; + + my $guild_position_info = { + len => 12, + types => 'a4 a4 V', + keys => [qw(ID charID position)], + }; + + my $position_info; + for (my $i = 0; $i < length($args->{member_list}); $i += $guild_position_info->{len}) { + @{$position_info}{@{$guild_position_info->{keys}}} = unpack($guild_position_info->{types}, substr($args->{member_list}, $i, $guild_position_info->{len})); + foreach my $guildMember (@{$guild{member}}) { + if ($guildMember->{charID} eq $position_info->{charID}) { + message TF("Guild Member (%s) has the title changed from %s to %s\n",$guildMember->{name}, $guild{positions}[ $guildMember->{position} ]{title}, $guild{positions}[$position_info->{position}]{title}); + $guildMember->{position} = $position_info->{position}; + last; + } + } + } +} + +# Guild position name information (ZC_POSITION_ID_NAME_INFO). +# 0166 <packet len>.W { <position id>.L <position name>.24B }* +sub guild_members_title_list { + my ($self, $args) = @_; + + my $msg = $args->{RAW_MSG}; + my $msg_size = $args->{RAW_MSG_SIZE}; + + my $gtIndex; + for (my $i = 4; $i < $msg_size; $i+=28) { + $gtIndex = unpack('V', substr($msg, $i, 4)); + $guild{positions}[$gtIndex]{title} = bytesToString(unpack('Z24', substr($msg, $i + 4, 24))); + } +} + +# Notifies the client that it is belonging to a guild (ZC_UPDATE_GDID). +# 016C <guild id>.L <emblem id>.L <mode>.L <ismaster>.B <inter sid>.L <guild name>.24B +# mode: +# &0x01 = allow invite +# &0x10 = allow expel +sub guild_name { + my ($self, $args) = @_; + + my $guildID = $args->{guildID}; + my $emblemID = $args->{emblemID}; + my $mode = $args->{mode}; + my $guildName = bytesToString($args->{guildName}); + $char->{guild}{name} = $guildName; + $char->{guildID} = $guildID; + $char->{guild}{emblem} = $emblemID; + + debug "guild name: $guildName\n"; + + # emulate client behavior + if ($masterServer->{serverType} eq 'twRO') { + $messageSender->sendGuildRequestInfo(0); # Requests for Basic Information Guild, Hostile Alliance Information + $messageSender->sendGuildRequestInfo(3); + $messageSender->sendGuildRequestInfo(1); # Requests for Members list, list job title + } elsif ($masterServer->{serverType} eq 'jRO') { + $messageSender->sendGuildRequestInfo(1); # Requests for Members list, list job title + } else { + $messageSender->sendGuildMasterMemberCheck(); + $messageSender->sendGuildRequestInfo(4); # Requests for Expulsion list + $messageSender->sendGuildRequestInfo(0); # Requests for Basic Information Guild, Hostile Alliance Information + $messageSender->sendGuildRequestInfo(1); # Requests for Members list, list job title + $messageSender->sendGuildRequestEmblem($guildID); # Requests for Guild Emblem + # TODO: check if is necessary use PAGE 2 (title information list) + # $messageSender->sendGuildRequestInfo(2); # Requests for List job title, title information list [Guild Title System] + } +} + +# Guild invite (ZC_REQ_JOIN_GUILD). +# 016A <guild id>.L <guild name>.24B +sub guild_request { + my ($self, $args) = @_; + + # Guild request + my $ID = $args->{ID}; + my $name = bytesToString($args->{name}); + message TF("Incoming Request to join Guild '%s'\n", $name); + $incomingGuild{'ID'} = $ID; + $incomingGuild{'Type'} = 1; + $timeout{'ai_guildAutoDeny'}{'time'} = time; +} + +# Bitmask of enabled guild window tabs (ZC_ACK_GUILD_MENUINTERFACE). +# 014E <menu flag>.L +# menu flag: +# 0x00 = Basic Info (always on) +# &0x01 = Member manager +# &0x02 = Positions +# &0x04 = Skills +# &0x10 = Expulsion list +# &0x40 = Unknown (GMENUFLAG_ALLGUILDLIST) +# &0x80 = Notice +sub guild_master_member { + my ($self, $args) = @_; + if ($args->{type} == 0xd7) { + } elsif ($args->{type} == 0x57) { + message T("You are not a guildmaster.\n"), "info"; + return; + } else { + warning TF("Unknown results in %s (type: %s)\n", $self->{packet_list}{$args->{switch}}->[0], $args->{type}); + return; + } + message T("You are a guildmaster.\n"), "info"; +} + +# Notifies the client about the result of a alliance request (ZC_ACK_REQ_ALLY_GUILD). +# 0173 <answer>.B +# answer: +# 0 = Already allied. +# 1 = You rejected the offer. +# 2 = You accepted the offer. +# 3 = They have too any alliances. +# 4 = You have too many alliances. +# 5 = Alliances are disabled. +sub guild_alliance { + my ($self, $args) = @_; + if ($args->{flag} == 0) { + message T("Already allied.\n"), "info"; + } elsif ($args->{flag} == 1) { + message T("You rejected the offer.\n"), "info"; + } elsif ($args->{flag} == 2) { + message T("You accepted the offer.\n"), "info"; + } elsif ($args->{flag} == 3) { + message T("They have too any alliances\n"), "info"; + } elsif ($args->{flag} == 4) { + message T("You have too many alliances.\n"), "info"; + } else { + warning TF("Unknown results in %s (flag: %s)\n", $self->{packet_list}{$args->{switch}}->[0], $args->{flag}); + } +} + +# Guild position information (ZC_POSITION_INFO). +# 0160 <packet len>.W { <position id>.L <mode>.L <ranking>.L <pay rate>.L }* +# mode: +# &0x01 = allow invite +# &0x10 = allow expel +# ranking: +# TODO +sub guild_member_setting_list { + my ($self, $args) = @_; + my $msg = $args->{RAW_MSG}; + my $msg_size = $args->{RAW_MSG_SIZE}; + + for (my $i = 4; $i < $msg_size; $i += 16) { + my ($gtIndex, $invite_punish, $ranking, $freeEXP) = unpack('V4', substr($msg, $i, 16)); # TODO: use ranking + # TODO: isn't there a nyble unpack or something and is this even correct? + $guild{positions}[$gtIndex]{invite} = ($invite_punish & 0x01) ? 1 : ''; + $guild{positions}[$gtIndex]{punish} = ($invite_punish & 0x10) ? 1 : ''; + $guild{positions}[$gtIndex]{gstorage} = ($invite_punish & 0x100) ? 1 : ''; + $guild{positions}[$gtIndex]{feeEXP} = $freeEXP; + } +} + +# Sends guild skills (ZC_GUILD_SKILLINFO). +# 0162 <packet len>.W <skill points>.W { <skill id>.W <type>.L <level>.W <sp cost>.W <atk range>.W <skill name>.24B <upgradable>.B }* +# TODO: merge with skills_list? +sub guild_skills_list { + my ($self, $args) = @_; + my $msg = $args->{RAW_MSG}; + my $msg_size = $args->{RAW_MSG_SIZE}; + for (my $i = 6; $i < $args->{RAW_MSG_SIZE}; $i += 37) { + + my ($skillID, $targetType, $level, $sp, $range, $skillName, $up) = unpack('v V v3 Z24 C', substr($msg, $i, 37)); # TODO: use range + + $skillName = bytesToString($skillName); + $guild{skills}{$skillName}{ID} = $skillID; + $guild{skills}{$skillName}{sp} = $sp; + $guild{skills}{$skillName}{up} = $up; + $guild{skills}{$skillName}{targetType} = $targetType; + if (!$guild{skills}{$skillName}{lv}) { + $guild{skills}{$skillName}{lv} = $level; + } + } +} + +# Guild expulsion list (ZC_BAN_LIST). +# 0163 <packet len>.W { <char name>.24B <account name>.24B <reason>.40B }* +# 0163 <packet len>.W { <char name>.24B <reason>.40B }* (PACKETVER >= 20100803) +# Change 64 to 88 if needed +# 0B7C +sub guild_expulsion_list { + my ($self, $args) = @_; + + my $guild_expulsion_list; + if ($args->{switch} eq "0B7C") { + $guild_expulsion_list = { + len => 68, + types => 'a4 Z40 Z24', + keys => [qw(charID cause name)], + }; + } else { # 0163 + $guild_expulsion_list = { + len => 88, + types => 'Z24 Z24 Z40', + keys => [qw(name acc cause)], + }; + } + + delete $guild{expulsion}; + my $index = 0; + + for (my $i = 0; $i < length($args->{expulsion_list}); $i += $guild_expulsion_list->{len}) { + @{$guild{expulsion}[$index]}{@{$guild_expulsion_list->{keys}}} = unpack($guild_expulsion_list->{types}, substr($args->{expulsion_list}, $i, $guild_expulsion_list->{len})); + $guild{expulsion}[$index]{name} = bytesToString($guild{expulsion}[$index]{name}) if ($guild{expulsion}[$index]{name}); + $guild{expulsion}[$index]{cause} = bytesToString($guild{expulsion}[$index]{cause}) if ($guild{expulsion}[$index]{cause}); + $index++; + } +} + +# Notifies that a member changed the map +# 01EC <account id>.L <char id>.L <status>.L <map name>.16B +sub guild_member_map_change { + my ($self, $args) = @_; + + foreach my $guildMember (@{$guild{member}}) { + if ($guildMember->{charID} eq $args->{charID}) { + $guildMember->{pos} = {}; + $guildMember->{pos_to} = {}; + $guildMember->{map} = bytesToString($args->{mapName}); + debug sprintf("Guild Member: %s changed map to %s\n",$guildMember->{name}, $guildMember->{map}); + last; + } + } +} +# Notifies that a member was added in the guild +# 0182 <account>.L <char id>.L <hair style>.W <hair color>.W <gender>.W <class>.W <level>.W <contrib exp>.L <state>.L <position>.L <memo>.50B <name>.24B +# 0B7E +sub guild_member_add { + my ($self, $args) = @_; + + if ($guild{member}) { + my $index = scalar @{$guild{member}}; + foreach (@{$args->{KEYS}}) { + @{$guild{member}[$index]}{$_} = $args->{$_}; + } + $guild{member}[$index]{name} = bytesToString($guild{member}[$index]{name}) if ($guild{member}[$index]{name}); + } + + my $name = bytesToString($args->{name}); + message TF("Guild member added: %s\n",$name), "guildchat"; +} + +# Sends guild notice to client (ZC_GUILD_NOTICE). +# 016F <subject>.60B <notice>.120B +sub guild_notice { + my ($self, $args) = @_; + stripLanguageCode(\$args->{subject}); + stripLanguageCode(\$args->{notice}); + # don't show the huge guildmessage notice if there is none + # the client does something similar to this... + if ($args->{subject} || $args->{notice}) { + my $msg = TF("---Guild Notice---\n" . + "%s\n\n" . + "%s\n" . + "------------------\n", $args->{subject}, $args->{notice}); + message $msg, "guildnotice"; + } +} + +# Displays special effects (npcs, weather, etc) [Valaris] (ZC_NOTIFY_EFFECT2). +# 01F3 <id>.L <effect id>.L +sub misc_effect { + my ($self, $args) = @_; + + my $actor = Actor::get($args->{ID}); + message sprintf( + $actor->verb(T("%s use effect: %s\n"), T("%s uses effect: %s\n")), + $actor, defined $effectName{$args->{effect}} ? $effectName{$args->{effect}} : T("Unknown #")."$args->{effect}" + ), 'effect' +} + +# Plays/stops a wave sound (ZC_SOUND). +# 01d3 <file name>.24B <act>.B <term>.L <npc id>.L +# file name: +# relative to data\wav +# act: +# 0 = play (once) +# 1 = play (repeat, does not work) +# 2 = stops all sound instances of file name (does not work) +# term: +# unknown purpose, only relevant to act = 1 +# $args->{term} seems like duration or repeat count +sub sound_effect { + my ($self, $args) = @_; + + # continuous sound effects can be implemented as actor statuses + my $actor = exists $args->{ID} && Actor::get($args->{ID}); + message sprintf( + $actor + ? $args->{type} == 0 + ? $actor->verb(T("%2\$s play: %s\n"), T("%2\$s plays: %s\n")) + : $args->{type} == 1 + ? $actor->verb(T("%2\$s are now playing: %s\n"), T("%2\$s is now playing: %s\n")) + : $actor->verb(T("%2\$s stopped playing: %s\n"), T("%2\$s stopped playing: %s\n")) + : T("Now playing: %s\n"), + $args->{name}, $actor), 'effect' +} + +# Presents a list of items that can be identified (ZC_ITEMIDENTIFY_LIST). +# 0177 <packet len>.W { <name id>.W }* +sub identify_list { + my ($self, $args) = @_; + + my $msg = $args->{RAW_MSG}; + my $msg_size = $args->{RAW_MSG_SIZE}; + + undef @identifyID; + for (my $i = 4; $i < $msg_size; $i += 2) { + my $index = unpack("a2", substr($msg, $i, 2)); + my $item = $char->inventory->getByID($index); + binAdd(\@identifyID, $item->{binID}); + } + + my $num = @identifyID; + message TF("Received Possible Identify List (%s item(s)) - type 'identify'\n", $num), 'info'; +} + +# Notifies the client about the result of a item identify request (ZC_ACK_ITEMIDENTIFY). +# 0179 <index>.W <result>.B +sub identify { + my ($self, $args) = @_; + if ($args->{flag} == 0) { + my $item = $char->inventory->getByID($args->{ID}); + $item->{identified} = 1; + $item->{type_equip} = $itemSlots_lut{$item->{nameID}}; + message TF("Item Identified: %s (%d)\n", $item->{name}, $item->{binID}), "info"; + } else { + message T("Item Appraisal has failed.\n"); + } + undef @identifyID; +} + +# TODO: store this state +sub ignore_all_result { + my ($self, $args) = @_; + if ($args->{type} == 0) { + $ignored_all = 1; + message T("All Players ignored\n"); + } elsif ($args->{type} == 1) { + if ($args->{error} == 0) { + message T("All players unignored\n"); + } + } +} + +# TODO: store list of ignored players +sub ignore_player_result { + my ($self, $args) = @_; + if ($args->{type} == 0) { + message T("Player ignored\n"); + } elsif ($args->{type} == 1) { + if ($args->{error} == 0) { + message T("Player unignored\n"); + } + } +} + +sub item_used { + my ($self, $args) = @_; + + my ($index, $itemID, $ID, $remaining, $success) = + @{$args}{qw(ID itemID actorID remaining success)}; + my %hook_args = ( + serverIndex => $index, + itemID => $itemID, + userID => $ID, + remaining => $remaining, + success => $success + ); + + if ($ID eq $accountID) { + my $item = $char->inventory->getByID($index); + if ($item) { + if ($success == 1) { + my $amount = $item->{amount} - $remaining; + + message TF("You used Item: %s (%d) x %d - %d left\n", $item->{name}, $item->{binID}, + $amount, $remaining), "useItem", 1; + + inventoryItemRemoved($item->{binID}, $amount); + + $hook_args{item} = $item; + $hook_args{binID} = $item->{binID}; + $hook_args{name} => $item->{name}; + $hook_args{amount} = $amount; + + } else { + message TF("You failed to use item: %s (%d)\n", $item ? $item->{name} : "#$itemID", $remaining), "useItem", 1; + } + } else { + if ($success == 1) { + message TF("You used unknown item #%d - %d left\n", $itemID, $remaining), "useItem", 1; + } else { + message TF("You failed to use unknown item #%d - %d left\n", $itemID, $remaining), "useItem", 1; + } + } + } else { + my $actor = Actor::get($ID); + my $itemDisplay = itemNameSimple($itemID); + message TF("%s used Item: %s - %s left\n", $actor, $itemDisplay, $remaining), "useItem", 2; + } + Plugins::callHook('packet_useitem', \%hook_args); +} + +sub married { + my ($self, $args) = @_; + + my $actor = Actor::get($args->{ID}); + message TF("%s got married!\n", $actor); +} + +# Makes an item appear on the ground. +# 009E <id>.L <name id>.W <identified>.B <x>.W <y>.W <subX>.B <subY>.B <amount>.W (ZC_ITEM_FALL_ENTRY) +# 084B <id>.L <name id>.W <type>.W <identified>.B <x>.W <y>.W <subX>.B <subY>.B <amount>.W (ZC_ITEM_FALL_ENTRY4) +# 0ADD <id>.L <name id>.W <type>.W <identified>.B <x>.W <y>.W <subX>.B <subY>.B <amount>.W <show drop effect>.B <drop effect mode>.W (ZC_ITEM_FALL_ENTRY5) +sub item_appeared { + my ($self, $args) = @_; + return unless changeToInGameState(); + + my $item = $itemsList->getByID($args->{ID}); + my $mustAdd; + if (!$item) { + $item = new Actor::Item(); + $item->{appear_time} = time; + $item->{amount} = $args->{amount}; + $item->{nameID} = $args->{nameID}; + $item->{identified} = $args->{identified}; + $item->{name} = itemName($item); + $item->{ID} = $args->{ID}; + $mustAdd = 1; + } + $item->{pos}{x} = $args->{x}; + $item->{pos}{y} = $args->{y}; + $item->{pos_to}{x} = $args->{x}; + $item->{pos_to}{y} = $args->{y}; + $itemsList->add($item) if ($mustAdd); + + # Take item as fast as possible + if (AI::state == AI::AUTO && pickupitems($item->{name}, $item->{nameID}) == 2 + && ($config{'itemsTakeAuto'} || $config{'itemsGatherAuto'}) + && (!$config{itemsGatherAuto_notInTown} || !$field->isCity) + && (percent_weight($char) < $config{'itemsMaxWeight'}) + && distance($item->{pos}, $char->{pos_to}) <= 5) { + $messageSender->sendTake($args->{ID}); + } + + message TF("Item Appeared: %s (%d) x %d (%d, %d)\n", $item->{name}, $item->{binID}, $item->{amount}, $args->{x}, $args->{y}), "drop", 1; + + Plugins::callHook('item_appeared', { + item => $item, + type => $args->{type} + }); +} + +sub item_exists { + my ($self, $args) = @_; + return unless changeToInGameState(); + + my $item = $itemsList->getByID($args->{ID}); + my $mustAdd; + if (!$item) { + $item = new Actor::Item(); + $item->{appear_time} = time; + $item->{amount} = $args->{amount}; + $item->{nameID} = $args->{nameID}; + $item->{identified} = $args->{identified}; + $item->{name} = itemName($item); + $item->{ID} = $args->{ID}; + $mustAdd = 1; + } + $item->{pos}{x} = $args->{x}; + $item->{pos}{y} = $args->{y}; + $item->{pos_to}{x} = $args->{x}; + $item->{pos_to}{y} = $args->{y}; + $itemsList->add($item) if ($mustAdd); + + message TF("Item Exists: %s (%d) x %d\n", $item->{name}, $item->{binID}, $item->{amount}), "drop", 1; + + Plugins::callHook('item_exists', { + item => $item, + type => $args->{type}, + show_effect => $args->{show_effect}, + effect_type => $args->{effect_type} + }); +} + +# Makes an item disappear from the ground. +# 00A1 <id>.L (ZC_ITEM_DISAPPEAR) +sub item_disappeared { + my ( $self, $args ) = @_; + return unless changeToInGameState(); + + my $item = $itemsList->getByID( $args->{ID} ); + if ( $item ) { + if ( $config{attackLooters} && AI::action ne "sitAuto" && pickupitems( $item->{name}, $item->{nameID} ) > 0 ) { + for my Actor::Monster $monster ( @$monstersList ) { # attack looter code + if ( my $control = mon_control( $monster->name, $monster->{nameID} ) ) { + next + if ( ( $control->{attack_auto} ne "" && $control->{attack_auto} == -1 ) + || ( $control->{attack_lvl} ne "" && $control->{attack_lvl} > $char->{lv} ) + || ( $control->{attack_jlvl} ne "" && $control->{attack_jlvl} > $char->{lv_job} ) + || ( $control->{attack_hp} ne "" && $control->{attack_hp} > $char->{hp} ) + || ( $control->{attack_sp} ne "" && $control->{attack_sp} > $char->{sp} ) ); + } + if ( distance( $item->{pos}, $monster->{pos} ) <= ( $config{attackLooters_dist} || 0 ) ) { + my %plugin_args; + $plugin_args{monster} = $monster; + $plugin_args{item} = $item; + $plugin_args{return} = 0; + Plugins::callHook( 'check_attackLooter' => \%plugin_args ); + unless ( $plugin_args{return} ) { + message TF( "Looter: %s looted %s - Adding it to looters list to be attacked\n", + $monster->nameIdx, $item->{name} ), + "looter"; + $monster->{attackLooters} = 1; + last; + } + } + } + } + + debug "Item Disappeared: $item->{name} ($item->{binID})\n", "parseMsg_presence"; + my $ID = $args->{ID}; + $items_old{$ID} = $item->deepCopy(); + $items_old{$ID}{disappeared} = 1; + $items_old{$ID}{gone_time} = time; + $itemsList->removeByID( $ID ); + } +} + +sub item_upgrade { + my ($self, $args) = @_; + my ($type, $index, $upgrade) = @{$args}{qw(type ID upgrade)}; + + my $item = $char->inventory->getByID($index); + if ($item) { + $item->{upgrade} = $upgrade; + message TF("Item %s has been upgraded to +%s\n", $item->{name}, $upgrade), "parseMsg/upgrade"; + $item->setName(itemName($item)); + } +} + +# Leap, Snap, Back Slide... Various knockback +sub high_jump { + my ($self, $args) = @_; + return unless changeToInGameState(); + + my $actor = Actor::get ($args->{ID}); + if (!defined $actor) { + $actor = new Actor::Unknown; + $actor->{appear_time} = time; + $actor->{nameID} = unpack ('V', $args->{ID}); + } elsif ($actor->{pos_to}{x} == $args->{x} && $actor->{pos_to}{y} == $args->{y}) { + message TF("%s failed to instantly move\n", $actor->nameString), 'skill'; + return; + } + + $actor->{pos} = {x => $args->{x}, y => $args->{y}}; + $actor->{pos_to} = {x => $args->{x}, y => $args->{y}}; + + message TF("%s instantly moved to %d, %d\n", $actor->nameString, $actor->{pos_to}{x}, $actor->{pos_to}{y}), 'skill', 2; + + $actor->{time_move} = time; + $actor->{time_move_calc} = 0; +} + +sub hp_sp_changed { + my ($self, $args) = @_; + return unless changeToInGameState(); + + my $type = $args->{type}; + my $amount = $args->{amount}; + if ($type == 5) { + $char->{hp} += $amount; + $char->{hp} = $char->{hp_max} if ($char->{hp} > $char->{hp_max}); + } elsif ($type == 7) { + $char->{sp} += $amount; + $char->{sp} = $char->{sp_max} if ($char->{sp} > $char->{sp_max}); + } +} + +# Notifies the client of a position change to coordinates on given map (ZC_NPCACK_MAPMOVE). +# 0091 <map name>.16B <x>.W <y>.W +# Notifies the client of a position change (on air ship) to coordinates on given map (ZC_AIRSHIP_MAPMOVE). +# 0A4B <map name>.16B <x>.W <y>.W +# The difference between map_change and map_changed is that map_change +# represents a map change event on the current map server, while +# map_changed means that you've changed to a different map server. +# map_change also represents teleport events. +sub map_change { + my ($self, $args) = @_; + return unless changeToInGameState(); + + $messageSender->sendStopSkillUse($char->{last_continuous_skill_used}) if $char->{last_skill_used_is_continuous}; + + my $oldMap = $field ? $field->baseName : undef; # Get old Map name without InstanceID + my ($map) = $args->{map} =~ /([\s\S]*)\./; + my $map_noinstance; + ($map_noinstance, undef) = Field::nameToBaseName(undef, $map); # Hack to clean up InstanceID + + checkAllowedMap($map_noinstance); + if (!$field || $map ne $field->name()) { + eval { + $field = new Field(name => $map); + }; + if (my $e = caught('FileNotFoundException', 'IOException')) { + error TF("Cannot load field %s: %s\n", $map_noinstance, $e); + undef $field; + } elsif ($@) { + die $@; + } + } + + if ($ai_v{temp}{clear_aiQueue}) { + AI::clear; + AI::SlaveManager::clear(); + } + + main::initMapChangeVars(); + %minimap_indicator_seen = (); + for (my $i = 0; $i < @ai_seq; $i++) { + ai_setMapChanged($i); + } + AI::SlaveManager::setMapChanged (); + $ai_v{portalTrace_mapChanged} = time; + + my %coords = ( + x => $args->{x}, + y => $args->{y} + ); + $char->{pos} = {%coords}; + $char->{pos_to} = {%coords}; + $char->{time_move} = 0; + $char->{time_move_calc} = 0; + $char->{solution} = []; + push(@{$char->{solution}}, { x => $char->{pos}{x}, y => $char->{pos}{y} }); + message TF("Map Change: %s (%s, %s)\n", $args->{map}, $char->{pos}{x}, $char->{pos}{y}), "connection"; + if ($net->version == 1) { + ai_clientSuspend(0, $timeout{'ai_clientSuspend'}{'timeout'}); + } else { + $messageSender->sendMapLoaded(); + # $messageSender->sendSync(1); + + # request to unfreeze char alisonrag + $messageSender->sendBlockingPlayerCancel() if $masterServer->{blockingPlayerCancel} || $self->{blockingPlayerCancel}; + + $timeout{ai}{time} = time; + } + + Plugins::callHook('Network::Receive::map_changed', { + oldMap => $oldMap, + }); + $timeout{ai}{time} = time; +} + +# Notifies the client of a position change to coordinates on given map, which is on another map-server. +# 0092 <map name>.16B <x>.W <y>.W <ip>.L <port>.W (ZC_NPCACK_SERVERMOVE) +# 0AC7 <map name>.16B <x>.W <y>.W <ip>.L <port>.W <dns host>.128B (ZC_NPCACK_SERVERMOVE2) +sub map_changed { + my ($self, $args) = @_; + $net->setState(4); + + my $oldMap = $field ? $field->baseName : undef; # Get old Map name without InstanceID + my ($map) = $args->{map} =~ /([\s\S]*)\./; + my $map_noinstance; + ($map_noinstance, undef) = Field::nameToBaseName(undef, $map); # Hack to clean up InstanceID + + checkAllowedMap($map_noinstance); + if (!$field || $map ne $field->name()) { + eval { + $field = new Field(name => $map); + }; + if (my $e = caught('FileNotFoundException', 'IOException')) { + error TF("Cannot load field %s: %s\n", $map_noinstance, $e); + undef $field; + } elsif ($@) { + die $@; + } + } + + my %coords = ( + x => $args->{x}, + y => $args->{y} + ); + $char->{pos} = {%coords}; + $char->{pos_to} = {%coords}; + $char->{time_move} = 0; + $char->{time_move_calc} = 0; + $char->{solution} = []; + push(@{$char->{solution}}, { x => $char->{pos}{x}, y => $char->{pos}{y} }); + + undef $conState_tries; + main::initMapChangeVars(); + %minimap_indicator_seen = (); + for (my $i = 0; $i < @ai_seq; $i++) { + ai_setMapChanged($i); + } + AI::SlaveManager::setMapChanged (); + $ai_v{portalTrace_mapChanged} = time; + + if ($args->{'url'} =~ /.*\:\d+/) { + $map_ip = $args->{url}; + $map_ip =~ s/:[0-9\0]+//; + $map_port = $args->{port}; + } else { + $map_ip = makeIP($args->{IP}); + $map_port = $args->{port}; + } + + message(swrite( + "---------Map Info----------", [], + "MAP Name: @<<<<<<<<<<<<<<<<<<", + [$args->{map}], + "MAP IP: @<<<<<<<<<<<<<<<<<<", + [$map_ip], + "MAP Port: @<<<<<<<<<<<<<<<<<<", + [$map_port], + "-------------------------------", []), + "connection"); + + message T("Closing connection to Map Server\n"), "connection"; + $net->serverDisconnect unless ($net->version == 1); + + # Reset item and skill times. The effect of items (like aspd potions) + # and skills (like Twohand Quicken) disappears when we change map server. + # NOTE: with the newer servers, this isn't true anymore + my $i = 0; + while (exists $config{"useSelf_item_$i"}) { + if (!$config{"useSelf_item_$i"}) { + $i++; + next; + } + + $ai_v{"useSelf_item_$i"."_time"} = 0; + $i++; + } + $i = 0; + while (exists $config{"useSelf_skill_$i"}) { + if (!$config{"useSelf_skill_$i"}) { + $i++; + next; + } + + $ai_v{"useSelf_skill_$i"."_time"} = 0; + $i++; + } + $i = 0; + while (exists $config{"doCommand_$i"}) { + if (!$config{"doCommand_$i"}) { + $i++; + next; + } + + $ai_v{"doCommand_$i"."_time"} = 0; + $i++; + } + if ($char) { + delete $char->{statuses}; + $char->{spirits} = 0; + delete $char->{permitSkill}; + delete $char->{encoreSkill}; + } + undef %guild; + if ( $char->cartActive ) { + $char->cart->close; + $char->cart->clear; + } + + Plugins::callHook('Network::Receive::map_changed', { + oldMap => $oldMap, + }); + $timeout{ai}{time} = time; +} + +# Parse 0A3B with structure +# '0A3B' => ['hat_effect', 'v a4 C a*', [qw(len ID flag effect)]], +# Unpack effect info into HatEFID +# @author [Cydh] +sub parse_hat_effect { + my ($self, $args) = @_; + @{$args->{effects}} = map {{ HatEFID => unpack('v', $_) }} unpack '(a2)*', $args->{effect}; + debug "Hat Effect. Flag: ".$args->{flag}." HatEFIDs: ".(join ', ', map {$_->{HatEFID}} @{$args->{effects}})."\n"; +} + +# Display information for player's Hat Effects +# @author [Cydh] +sub hat_effect { + my ($self, $args) = @_; + + my $actor = Actor::get($args->{ID}); + my $hatName; + my $i = 0; + + #TODO: Stores the hat effect into actor for single player's information + for my $hat (@{$args->{effects}}) { + my $hatHandle; + $hatName .= ", " if ($i); + if (defined $hatEffectHandle{$hat->{HatEFID}}) { + $hatHandle = $hatEffectHandle{$hat->{HatEFID}}; + $hatName .= defined $hatEffectName{$hatHandle} ? $hatEffectName{$hatHandle} : $hatHandle; + } else { + $hatName .= T("Unknown #").$hat->{HatEFID}; + } + $i++; + } + + if ($args->{flag} == 1) { + message sprintf( + $actor->verb(T("%s use effect: %s\n"), T("%s uses effect: %s\n")), + $actor, $hatName + ), 'effect'; + } else { + message sprintf( + $actor->verb(T("%s are no longer: %s\n"), T("%s is no longer: %s\n")), + $actor, $hatName + ), 'effect'; + } +} + +# Displays an NPC dialog message (ZC_SAY_DIALOG). +# 00B4 <packet len>.W <npc id>.L <message>.?B +sub npc_talk { + my ($self, $args) = @_; + + my $ID = $args->{ID}; + my $nameID = unpack 'V', $ID; + autoNpcTalk($ID, $nameID); + + $talk{ID} = $args->{ID}; + $talk{nameID} = unpack 'V', $args->{ID}; + my $msg = bytesToString ($args->{msg}); + + # Remove RO color codes + $talk{msg} =~ s/\^[a-fA-F0-9]{6}//g; + $msg =~ s/\^[a-fA-F0-9]{6}//g; + + # Prepend existing conversation. + $talk{msg} .= "\n" if $talk{msg}; + $talk{msg} .= $msg; + + $ai_v{'npc_talk'}{'ID'} = $talk{ID}; + $ai_v{'npc_talk'}{talk} = 'initiated'; + $ai_v{'npc_talk'}{time} = time; + + my $name = getNPCName($talk{ID}); + Plugins::callHook('npc_talk', { + ID => $talk{ID}, + nameID => $talk{nameID}, + name => $name, + msg => $talk{msg}, + }); + message "$name: $msg\n", "npc"; +} + +# Adds a 'close' button to an NPC dialog (ZC_CLOSE_DIALOG). +# 00B6 <npc id>.L +sub npc_talk_close { + my ($self, $args) = @_; + # 00b6: long ID + + if (!exists $ai_v{'npc_talk'} || !defined $ai_v{'npc_talk'} || !exists $ai_v{'npc_talk'}{'ID'} || !defined $ai_v{'npc_talk'}{'ID'}) { + debug "We received an strange 'npc_talk_done', just ignoring it\n", "npc"; + return; + } + + return if (exists $ai_v{'npc_talk'}{'talk'} && $ai_v{'npc_talk'}{'talk'} eq 'buy_or_sell'); + + my $ID = $args->{ID}; + my $name = getNPCName($ID); + + $ai_v{'npc_talk'}{'ID'} = $talk{ID}; + $ai_v{'npc_talk'}{'talk'} = 'close'; + $ai_v{'npc_talk'}{'time'} = time; + undef %talk; + + Plugins::callHook('npc_talk_done', {ID => $ID}); +} + +# Adds a 'next' button to an NPC dialog (ZC_WAIT_DIALOG). +# 00B5 <npc id>.L +sub npc_talk_continue { + my ($self, $args) = @_; + my $ID = substr($args->{RAW_MSG}, 2, 4); + my $name = getNPCName($ID); + + $ai_v{'npc_talk'}{'ID'} = $ID; + $ai_v{'npc_talk'}{'talk'} = 'next'; + $ai_v{'npc_talk'}{'time'} = time; +} + +# Displays an NPC dialog input box for numbers (ZC_OPEN_EDITDLG). +# 0142 <npc id>.L +sub npc_talk_number { + my ($self, $args) = @_; + + my $ID = $args->{ID}; + + my $name = getNPCName($ID); + $ai_v{'npc_talk'}{'ID'} = $ID; + $ai_v{'npc_talk'}{'talk'} = 'number'; + $ai_v{'npc_talk'}{'time'} = time; +} + +# Displays an NPC dialog menu (ZC_MENU_LIST). +# 00B7 <packet len>.W <npc id>.L <menu items>.?B +sub npc_talk_responses { + my ($self, $args) = @_; + + # 00b7: word len, long ID, string str + # A list of selections appeared on the NPC message dialog. + # Each item is divided with ':' + my $msg = $args->{RAW_MSG}; + + my $ID = substr($msg, 4, 4); + my $nameID = unpack 'V', $ID; + autoNpcTalk($ID, $nameID); + + $talk{ID} = $ID; + $talk{nameID} = $nameID; + my $talk = unpack("Z*", substr($msg, 8)); + $talk = substr($msg, 8) if (!defined $talk); + $talk = bytesToString($talk); + + my @preTalkResponses = split /:/, $talk; + + Plugins::callHook('pre/npc_talk_responses', { + responses => \@preTalkResponses, + }); + + $talk{responses} = []; + foreach my $response (@preTalkResponses) { + # Remove RO color codes + $response =~ s/\^[a-fA-F0-9]{6}//g; + if ($response =~ /^\^nItemID\^(\d+)$/) { + $response = itemNameSimple($1); + } + + push @{$talk{responses}}, $response if ($response ne ""); + } + + $talk{responses}[@{$talk{responses}}] = T("Cancel Chat"); + + $ai_v{'npc_talk'}{'ID'} = $talk{ID}; + $ai_v{'npc_talk'}{'talk'} = 'select'; + $ai_v{'npc_talk'}{'time'} = time; + + Commands::run('talk resp'); + + my $name = getNPCName($ID); + Plugins::callHook('npc_talk_responses', { + ID => $ID, + name => $name, + responses => $talk{responses}, + }); +} + +# Displays an NPC dialog input box for numbers (ZC_OPEN_EDITDLGSTR). +# 01D4 <npc id>.L +sub npc_talk_text { + my ($self, $args) = @_; + + my $ID = $args->{ID}; + + my $name = getNPCName($ID); + $ai_v{'npc_talk'}{'ID'} = $ID; + $ai_v{'npc_talk'}{'talk'} = 'text'; + $ai_v{'npc_talk'}{'time'} = time; +} + +# Displays the buy/sell dialog of an NPC shop (ZC_SELECT_DEALTYPE). +# 00C4 <shop id>.L +sub npc_store_begin { + my ($self, $args) = @_; + undef %talk; + $talk{ID} = $args->{ID}; + $ai_v{'npc_talk'}{'ID'} = $talk{ID}; + $ai_v{'npc_talk'}{'talk'} = 'buy_or_sell'; + $ai_v{'npc_talk'}{'time'} = time; + + $storeList->{npcName} = getNPCName($args->{ID}) || T('Unknown'); +} + +# Presents list of items, that can be bought in an NPC shop (ZC_PC_PURCHASE_ITEMLIST). +# 00C6 <packet len>.W { <price>.L <discount price>.L <item type>.B <name id>.W }* +# 00C6 <packet len>.W { <price>.L <discount price>.L <item type>.B <name id>.L }* +# 0B77 <packet len>.W { <name id>.L <price>.L <discount price>.L <item type>.B <viewSprite>.W <location>.L}* +# 2 versions of same packet. $self->{npc_store_info_pack} (ZC_PC_PURCHASE_ITEMLIST_sub) should be changed in own serverType file if needed +sub npc_store_info { + my ($self, $args) = @_; + my $msg = $args->{RAW_MSG}; + my $pack; + my $keys; + + if ($args->{switch} eq '0B77') { + $pack = "V3 C v V"; + $keys = [qw( nameID price _ type sprite_id location )]; + } else { + $pack = $self->{npc_store_info_pack} || 'V V C v'; + $keys = [qw( price _ type nameID )]; + } + + my $len = length pack $pack; + $storeList->clear; + undef %talk; + for (my $i = 4; $i < $args->{RAW_MSG_SIZE}; $i += $len) { + my $item = Actor::Item->new; + @$item{@{$keys}} = unpack $pack, substr $msg, $i, $len; + + # Workaround some npcs that have items appearing more than once in their store list, + # for example the Trader at moc_ruins 90 149 sells only bananas, but 6 times + # + # Usually, $Actor::Item->{ID} is equal to $Actor::Item->{nameID} - that WILL crash + # kore in the event described above + # + # This workaround causes $Actor::Item->{ID} to be equal to $Actor::Item->{binID} and, + # therefore, never overlap + # - lututui & alisonrag - Sep, 2018 + $item->{ID} = $storeList->size; + + $item->{name} = itemName($item); + $storeList->add($item); + + debug "Item added to Store: $item->{name} - $item->{price}z\n", "parseMsg", 2; + } + + $ai_v{'npc_talk'}{talk} = 'store'; + # continue talk sequence now + $ai_v{'npc_talk'}{'time'} = time; + + if (AI::action ne 'buyAuto') { + Commands::run('store'); + } +} + +# Presents list of items, that can be sold to an NPC shop (ZC_PC_SELL_ITEMLIST). +# 00C7 <packet len>.W { <index>.W <price>.L <overcharge price>.L }* +sub npc_sell_list { + my ($self, $args) = @_; + #sell list, similar to buy list + if (length($args->{RAW_MSG}) > 4) { + my $msg = $args->{RAW_MSG}; + } + + debug T("You can sell:\n"), "info"; + for (my $i = 0; $i < length($args->{itemsdata}); $i += 10) { + my ($index, $price, $price_overcharge) = unpack("a2 L L", substr($args->{itemsdata},$i,($i + 10))); + my $item = $char->inventory->getByID($index); + $item->{sellable} = 1; # flag this item as sellable + debug TF("%s x %s for %sz each. \n", $item->{amount}, $item->{name}, $price_overcharge), "info"; + } + + foreach my $item (@{$char->inventory->getItems()}) { + next if ($item->{equipped} || $item->{sellable}); + $item->{unsellable} = 1; # flag this item as unsellable + } + + undef %talk; + message T("Ready to start selling items\n"); + + $ai_v{'npc_talk'}{talk} = 'sell'; + # continue talk sequence now + $ai_v{'npc_talk'}{'time'} = time; +} + +sub npc_clear_dialog { + my ($self, $args) = @_; + my $ID = $args->{ID}; + debug "The dialogue with the NPC " .getHex($ID) ." was closed.\n", "parseMsg"; +} + +# Notification about the result of a purchase attempt from an NPC shop (ZC_PC_PURCHASE_RESULT). +# 00CA <result>.B +# result: +# 0 = "The deal has successfully completed." +# 1 = "You do not have enough zeny." +# 2 = "You are over your Weight Limit." +# 3 = "Out of the maximum capacity, you have too many items." +# 4 = "Item does not exist in store" +# 5 = "Item cannot be exchanged" +# 6 = "Invalid store" +sub buy_result { + my ($self, $args) = @_; + if ($args->{fail} == 0) { + message T("Buy completed.\n"), "success"; + } elsif ($args->{fail} == 1) { + error T("Buy failed (insufficient zeny).\n"); + } elsif ($args->{fail} == 2) { + error T("Buy failed (insufficient weight capacity).\n"); + } elsif ($args->{fail} == 3) { + error T("Buy failed (too many different inventory items).\n"); + } elsif ($args->{fail} == 4) { + error T("Buy failed (item does not exist in store).\n"); + } elsif ($args->{fail} == 5) { + error T("Buy failed (item cannot be exchanged).\n"); + } elsif ($args->{fail} == 6) { + error T("Buy failed (invalid store).\n"); + } else { + error TF("Buy failed (failure code %s).\n", $args->{fail}); + } + if (AI::is("buyAuto")) { + AI::args->{recv_buy_packet} = 1; + } + Plugins::callHook('buy_result', {fail => $args->{fail}}); +} + +# Presents list of items, that can be bought in an NPC MARKET shop (PACKET_ZC_NPC_MARKET_OPEN). +# 09D5 <packet len>.W { <name id>.W <type>.B <price>.L <amount>.L <weight>.W }* +# 09D5 <packet len>.W { <name id>.L <type>.B <price>.L <amount>.L <weight>.W }* +# 2 versions of same packet. $self->{npc_market_info_pack} (PACKET_ZC_NPC_MARKET_OPEN_sub) should be changed in own serverType file if needed +sub npc_market_info { + my ($self, $args) = @_; + my $pack = $self->{npc_market_info_pack} || 'v C V2 v'; + my $len = length pack $pack; + + $storeList->clear; + undef %talk; + + for (my $i = 0; $i < length($args->{itemList}); $i += $len) { + my $item = Actor::Item->new; + @$item{qw( nameID type price amount weight )} = unpack $pack, substr $args->{itemList}, $i, $len; + next if (!$item->{amount}); # Client behavior (dont show the item in market window) + # Workaround some npcs that have items appearing more than once in their store list, + # for example the Trader at moc_ruins 90 149 sells only bananas, but 6 times + # + # Usually, $Actor::Item->{ID} is equal to $Actor::Item->{nameID} - that WILL crash + # kore in the event described above + # + # This workaround causes $Actor::Item->{ID} to be equal to $Actor::Item->{binID} and, + # therefore, never overlap + # - lututui & alisonrag - Sep, 2018 + $item->{ID} = $storeList->size; + + $item->{name} = itemName($item); + + $storeList->add($item); + + debug "Item added to Store: $item->{name} - $item->{price}z\n", "parseMsg", 2; + } + + return if !$storeList->size; + + if (AI::action ne 'buyAuto') { + Commands::run('store'); + } + + $in_market = 1; + + # continue talk sequence now + $ai_v{'npc_talk'}{'talk'} = 'store'; + $ai_v{'npc_talk'}{'time'} = time; +} + +# Show the purchase result update the list of items, that can be bought in an NPC MARKET shop (PACKET_ZC_NPC_MARKET_OPEN). +# 09D7 <packet len>.W <result>.B { <name id>.W <type>.B <price>.L <amount>.L <weight>.W }* +# 09D7 <packet len>.W <result>.B { <name id>.L <type>.B <price>.L <amount>.L <weight>.W }* +# result: +# -1 = error +# 0 = sucess +# 1 = no zeny +# 2 = you are overweight +# 3 = you dont have space in inventory +# 4 = amount too big +sub npc_market_purchase_result { + my ($self, $args) = @_; + + debug "Npc market purchase result: " .$args->{result}. "\n", "parseMsg", 2; + if ( $args->{result} == MARKET_BUY_RESULT_ERROR) { + error T("Error while trying to buy in a Market Store.\n"), "info"; + } elsif ( $args->{result} == MARKET_BUY_RESULT_SUCCESS) { + message T("Item buyed Successfully.\n"), "info"; + } elsif ( $args->{result} == MARKET_BUY_RESULT_NO_ZENY) { + error T("Error Market Store (You don't have the necessary zeny).\n"), "info"; + } elsif ( $args->{result} == MARKET_BUY_RESULT_OVER_WEIGHT) { + error T("Error Market Store (You are Overweight).\n"), "info"; + } elsif ( $args->{result} == MARKET_BUY_RESULT_OUT_OF_SPACE) { + error T("Error Market Store (You dont have space in inventory).\n"), "info"; + } elsif ( $args->{result} == MARKET_BUY_RESULT_AMOUNT_TOO_BIG) { + error T("Error Market Store (You tried to buy a amount higher then NPC is selling).\n"), "info"; + } else { + error TF("Error while trying to buy in a Market Store (Unknown). (%s)\n", $args->{result}), "info"; + } + + if (AI::is("buyAuto")) { + AI::args->{recv_buy_packet} = 1; + } + + my $pack = $self->{npc_market_info_pack} || 'v C V2 v'; + my $len = length pack $pack; + + $storeList->clear; + undef %talk; + + for (my $i = 0; $i < length($args->{itemList}); $i += $len) { + my $item = Actor::Item->new; + @$item{qw( nameID type price amount weight )} = unpack $pack, substr $args->{itemList}, $i, $len; + next if (!$item->{amount}); # Client behavior (dont show the item in market window) + # Workaround some npcs that have items appearing more than once in their store list, + # for example the Trader at moc_ruins 90 149 sells only bananas, but 6 times + # + # Usually, $Actor::Item->{ID} is equal to $Actor::Item->{nameID} - that WILL crash + # kore in the event described above + # + # This workaround causes $Actor::Item->{ID} to be equal to $Actor::Item->{binID} and, + # therefore, never overlap + # - lututui & alisonrag - Sep, 2018 + $item->{ID} = $storeList->size; + + $item->{name} = itemName($item); + + $storeList->add($item); + + debug "Item added to Store: $item->{name} - $item->{price}z\n", "parseMsg", 2; + } + + return if !$storeList->size; + + if (AI::action ne 'buyAuto') { + Commands::run('store'); + } + + $in_market = 1; + + # continue talk sequence now + $ai_v{'npc_talk'}{'talk'} = 'store'; + $ai_v{'npc_talk'}{'time'} = time; +} + +sub deal_add_you { + my ($self, $args) = @_; + + if ($args->{fail} == 1) { + error T("That person is overweight; you cannot trade.\n"), "deal"; + return; + } elsif ($args->{fail} == 2) { + error T("This item cannot be traded.\n"), "deal"; + return; + } elsif ($args->{fail} == 192) { + debug "Unknown status (success).\n", "deal"; + } elsif ($args->{fail}) { + error TF("You cannot trade (fail code %s).\n", $args->{fail}), "deal"; + return; + } + + my $id = unpack('v',$args->{ID}); + + if ($id == 0) { + message "You added Zeny to Deal (suposedly $currentDeal{'you_zeny'} z).\n"; + return; + } + + return unless ($id > 0); + + my $item = $char->inventory->getByID($args->{ID}); + $args->{item} = $item; + # FIXME: quickly add two items => lastItemAmount is lost => inventory corruption; see also Misc::dealAddItem + # FIXME: what will be in case of two items with the same nameID? + # TODO: no info about items is stored + $currentDeal{you_items}++; + $currentDeal{you}{$item->{nameID}}{amount} += $currentDeal{lastItemAmount}; + $currentDeal{you}{$item->{nameID}}{nameID} = $item->{nameID}; + message TF("You added Item to Deal: %s x %s\n", $item->{name}, $currentDeal{lastItemAmount}), "deal"; + inventoryItemRemoved($item->{binID}, $currentDeal{lastItemAmount}); + Plugins::callHook('deal_you_added', { + id => $id, + item => $item + }); +} + +sub skill_exchange_item { + my ($self, $args) = @_; + if ($args->{type} == 0) { + message T("Change Material is ready. Use command 'cm' to continue.\n"), "info"; + } else { + message T("Four Spirit Analysis is ready. Use command 'analysis' to continue.\n"), "info"; + } + ## + # $args->{type} : Type + # 0: Change Material -> 1 + # 1: Elemental Analysis Lv 1 -> 2 + # 2: Elemental Analysis Lv 2 -> 3 + # This value will be added +1 for simple check later + # $args->{val} : ???? + ## + $skillExchangeItem = $args->{type} + 1; +} + +# Allowed to RefineUI by server +# '0AA0' => ['refineui_opened', '' ,[qw()]], +# @author [Cydh] +sub refineui_opened { + my ($self, $args) = @_; + message TF("RefineUI is opened. Type 'i' to check equipment and its index. To continue: refineui select [ItemIdx]\n"), "info"; + $refineUI->{open} = 1; +} + +# Received refine info for selected item +# '0AA2' => ['refineui_info', 'v v C a*' ,[qw(index bless materials)]], +# @param args Packet data +# @author [Cydh] +sub refineui_info { + my ($self, $args) = @_; + + if ($args->{len} > 7) { + $refineUI->{itemIndex} = $args->{index}; + $refineUI->{bless} = $args->{bless}; + + my $item = $char->inventory->[$refineUI->{invIndex}]; + my $bless = $char->inventory->getByNameID($Blacksmith_Blessing); + + message T("========= RefineUI Info =========\n"), "info"; + message TF("Target Equip:\n". + "- Index: %d\n". + "- Name: %s\n", + $refineUI->{invIndex}, $item ? itemName($item) : "Unknown."), + "info"; + + message TF("%s:\n". + "- Needed: %d\n". + "- Owned: %d\n", + #itemNameSimple($Blacksmith_Blessing) + "Blacksmith Blessing", $refineUI->{bless}, $bless ? $bless->{amount} : 0), + "info"; + + @{$refineUI->{materials}} = map { my %r; @r{qw(nameid chance zeny)} = unpack 'v C V', $_; \%r} unpack '(a7)*', $args->{materials}; + + my $msg = center(T(" Possible Materials "), 53, '-') ."\n" . + T("Mat_ID % Zeny Material \n"); + foreach my $mat (@{$refineUI->{materials}}) { + my $myMat = $char->inventory->getByNameID($mat->{nameid}); + my $myMatCount = sprintf("%d ea %s", $myMat ? $myMat->{amount} : 0, itemNameSimple($mat->{nameid})); + $msg .= swrite( + "@>>>>>>>> @>>>>> @>>>>>>>>>>>> @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<", + [$mat->{nameid}, $mat->{chance}, $mat->{zeny}, $myMatCount]); + } + $msg .= ('-'x53) . "\n"; + message $msg, "info"; + message TF("Continue: refineui refine %d [Mat_ID] [catalyst_toggle] to continue.\n", $refineUI->{invIndex}), "info"; + } else { + error T("Equip cannot be refined, try different equipment. Type 'i' to check equipment and its index.\n"); + } +} + +sub refine_status { + my ($self, $args) = @_; + my $msgIndex = $args->{status} ? 3272 : 3273; + my $msg = sprintf($msgTable[$msgIndex], bytesToString($args->{name}), $args->{refine_level}, itemNameSimple($args->{itemID}))."\n"; + warning $msg, "info"; +} + +sub character_ban_list { + my ($self, $args) = @_; + # Header + Len + CharList[character_name(size:24)] +} + +sub flag { + my ($self, $args) = @_; +} + +sub offline_clone_found { + my ($self, $args) = @_; + + my $actor = $playersList->getByID($args->{ID}); + if (!defined $actor) { + $actor = new Actor::Player(); + $actor->{object_type} = 0x0; #player + $actor->{clone} = 1; + $actor->{ID} = $args->{ID}; + $actor->{nameID} = unpack("V", $args->{ID}); + $actor->{name} = bytesToString($args->{name}); + $actor->{appear_time} = time; + $actor->{jobID} = $args->{jobID}; + $actor->{type} = $args->{jobID}; + $actor->{pos}{x} = $args->{coord_x}; + $actor->{pos}{y} = $args->{coord_y}; + $actor->{pos_to}{x} = $args->{coord_x}; + $actor->{pos_to}{y} = $args->{coord_y}; + $actor->{time_move} = time; + $actor->{time_move_calc} = 0; + $actor->{walk_speed} = 1; #hack + $actor->{lv} = 1; + $actor->{robe} = $args->{robe}; + $actor->{clothes_color} = $args->{clothes_color}; + $actor->{headgear}{low} = $args->{lowhead}; + $actor->{headgear}{mid} = $args->{midhead}; + $actor->{headgear}{top} = $args->{tophead}; + $actor->{weapon} = $args->{weapon}; + $actor->{shield} = $args->{shield}; + $actor->{sex} = $args->{sex}; + $actor->{hair_color} = $args->{hair_color} if (exists $args->{hair_color}); + + $playersList->add($actor); + + Plugins::callHook('add_player_list', $actor); + Plugins::callHook('player', {player => $actor}); #backwards compatibility + Plugins::callHook('player_exist', {player => $actor}); + } +} + +sub offline_clone_lost { + my ($self, $args) = @_; + + # remove from player list + if (defined $playersList->getByID($args->{ID})) { + my $player = $playersList->getByID($args->{ID}); + + $player->{gone_time} = time; + $players_old{$args->{ID}} = $player->deepCopy(); + Plugins::callHook('player_disappeared', {player => $player}); + + $playersList->remove($player); + } + + # try to remove from vender list + binRemove(\@venderListsID, $args->{ID}); + delete $venderLists{$args->{ID}}; + + # try to remove from buyer list + binRemove(\@buyerListsID, $args->{ID}); + delete $buyerLists{$args->{ID}}; +} + +sub remain_time_info { + my ($self, $args) = @_; + debug TF("Remain Time - Result: %s - Expiration Date: %s - Time: %s\n", $args->{result}, $args->{expiration_date}, $args->{remain_time}), "console", 1; +} + +sub received_login_token { + my ($self, $args) = @_; + + # Skip in XKore mode 1 / 3 + return if $self->{net}->version == 1; + + my $master = $masterServers{$config{master}}; + my $login_type = $args->{login_type}; + + if ($login_type == 0) { + # rAthena uses 0064 not 0825 + $messageSender->sendTokenToServer( + $config{username}, + $config{password}, + $master->{master_version}, + $master->{version}, + $args->{login_token}, + $args->{len}, + $master->{ip}, + $master->{port} + ); + + } elsif ($login_type == 400 || $login_type == 1000) { + die 'ERROR: otpSeed is not set in config.txt' unless $config{otpSeed}; + + my $otp; + Plugins::callHook('request_otp_login', { otp => \$otp, seed => $config{otpSeed} }); + unless (defined $otp && length $otp) { + error "No Plugin returned a OTP code for account $config{username}\n", 'connection'; + $otp = $interface->query(T(', please enter your OTP: ')); + } + $messageSender->sendOtpToServer($otp); + + } elsif ($login_type == 300) { + error "OTP token malformed for account $config{username}\n", 'connection'; + my $otp = $interface->query(T('Please enter the OTP code: ')); + $messageSender->sendOtpToServer($otp); + + } elsif ($login_type == 500) { + error "Wrong OTP for account $config{username}\n", 'connection'; + my $otp = $interface->query(T('Please enter the OTP code: ')); + $messageSender->sendOtpToServer($otp); + + } elsif ($login_type == 600) { + error "Password Error for account $config{username}\n", 'connection'; + Misc::quit(); + + } elsif ($login_type == 900) { + error "You need to setup your OTP for account $config{username}\n", 'connection'; + Misc::quit(); + } else { + error "Unknown login_type $login_type\n", 'connection'; + Misc::quit(); + } +} + +# this info will be sent to xkore 2 clients +sub hotkeys { + my ($self, $args) = @_; + undef $hotkeyList; + my $msg; + + # TODO: implement this: $hotkeyList->{rotate} = $args->{rotate} if $args->{rotate}; + $msg .= center(" " . T("Hotkeys") . " ", 79, '-') . "\n"; + $msg .= swrite(sprintf("\@%s \@%s \@%s \@%s", ('>'x3), ('<'x30), ('<'x5), ('>'x3)), + ["#", T("Name"), T("Type"), T("Lv")]); + $msg .= sprintf("%s\n", ('-'x79)); + my $j = 0; + for (my $i = 0; $i < length($args->{hotkeys}); $i += 7) { + @{$hotkeyList->[$j]}{qw(type ID lv)} = unpack('C V v', substr($args->{hotkeys}, $i, 7)); + $msg .= swrite(sprintf("\@%s \@%s \@%s \@%s", ('>'x3), ('<'x30), ('<'x5), ('>'x3)), + [$j, $hotkeyList->[$j]->{type} ? Skill->new(idn => $hotkeyList->[$j]->{ID})->getName() : itemNameSimple($hotkeyList->[$j]->{ID}), + $hotkeyList->[$j]->{type} ? T("skill") : T("item"), + $hotkeyList->[$j]->{lv}]); + $j++; + } + $msg .= sprintf("%s\n", ('-'x79)); + debug($msg, "list"); +} + +sub received_character_ID_and_Map { + my ($self, $args) = @_; + message T("Received character ID and Map IP from Character Server\n"), "connection"; + $net->setState(4); + undef $conState_tries; + $charID = $args->{charID}; + + if ($net->version == 1) { + undef $masterServer; + $masterServer = $masterServers{$config{master}} if ($config{master} ne ""); + } + + my ($map) = $args->{mapName} =~ /([\s\S]*)\./; # cut off .gat + my $map_noinstance; + ($map_noinstance, undef) = Field::nameToBaseName(undef, $map); # Hack to clean up InstanceID + if (!$field || $map ne $field->name()) { + eval { + $field = new Field(name => $map); + }; + if (my $e = caught('FileNotFoundException', 'IOException')) { + error TF("Cannot load field %s: %s\n", $map_noinstance, $e); + undef $field; + } elsif ($@) { + die $@; + } + } + + if (exists $args->{mapUrl} && $args->{'mapUrl'} =~ /.*\:\d+/) { + $map_ip = $args->{mapUrl}; + $map_ip =~ s/:[0-9\0]+//; + $map_port = $args->{mapPort}; + } else { + $map_ip = makeIP($args->{mapIP}); + $map_ip = $masterServer->{ip} if ($masterServer && $masterServer->{private}); + $map_port = $args->{mapPort}; + } + + # Workaround. Current xKore 1 is not able to define the $char + if ($config{XKore} == 1) { + foreach my $character (@chars) { + if (getHex($charID) eq getHex($character->{charID})) { + configModify("char", $character->{slot}); + $char = $chars[$character->{slot}]; + } + } + } + + message TF("----------Game Info----------\n" . + "Char ID: %s (%s)\n" . + "MAP Name: %s\n" . + "MAP IP: %s\n" . + "MAP Port: %s\n" . + "-----------------------------\n", getHex($charID), unpack("V1", $charID), + $args->{mapName}, $map_ip, $map_port), "connection"; + checkAllowedMap($map_noinstance); + message(T("Closing connection to Character Server\n"), "connection") unless ($net->version == 1); + $net->serverDisconnect(1); + main::initStatVars(); +} + +sub received_sync { + return unless changeToInGameState(); + debug "Received Sync\n", 'parseMsg', 2; + $timeout{'play'}{'time'} = time; +} + +sub actor_look_at { + my ($self, $args) = @_; + return unless changeToInGameState(); + + my $actor = Actor::get($args->{ID}); + $actor->{look}{head} = $args->{head}; + $actor->{look}{body} = $args->{body}; + debug $actor->nameString . " looks at $args->{body}, $args->{head}\n", "parseMsg"; +} + +# Visually moves(slides) a character to x,y. If the target cell +# isn't walkable, the char doesn't move at all. If the char is +# sitting it will stand up (ZC_STOPMOVE). +# 0088 <id>.L <x>.W <y>.W +# 08CD <id>.L <x>.W <y>.W +sub actor_movement_interrupted { + my ($self, $args) = @_; + return unless changeToInGameState(); + my %coords; + $coords{x} = $args->{x}; + $coords{y} = $args->{y}; + + my $actor = Actor::get($args->{ID}); + $actor->{pos} = {%coords}; + $actor->{pos_to} = {%coords}; + $actor->{time_move} = time; + $actor->{time_move_calc} = 0; + if ($actor->isa('Actor::You') || $actor->isa('Actor::Player')) { + $actor->{sitting} = 0; + } + if ($actor->isa('Actor::You')) { + debug "Movement interrupted, your coordinates: $coords{x}, $coords{y}\n", "parseMsg_move"; + AI::clear("move"); + } + if ($char->{homunculus} && $char->{homunculus}{ID} eq $actor->{ID}) { + AI::clear("move"); + } +} + +sub actor_trapped { + my ($self, $args) = @_; + # original comment was that ID is not a valid ID + # but it seems to be, at least on eAthena/Freya + my $actor = Actor::get($args->{ID}); + debug "$actor->nameString() is trapped.\n"; +} + +sub party_join { + my ($self, $args) = @_; + return unless changeToInGameState(); + my $keys; + my $info; + if ($args->{switch} eq '0104') { # DEFAULT OLD PACKET + $keys = [qw(ID role x y type name user map)]; + } elsif ($args->{switch} eq '01E9') { # PACKETVER >= 2015 + $keys = [qw(ID role x y type name user map lv item_pickup item_share)]; + + } elsif ($args->{switch} eq '0A43') { # PACKETVER >= 2016 + $keys = [qw(ID role jobID lv x y type name user map item_pickup item_share)]; + + } elsif ($args->{switch} eq '0AE4') { # PACKETVER >= 2017 + $keys = [qw(ID charID role jobID lv x y type name user map item_pickup item_share)]; + + } else { # this can't happen + return; + } + + @{$info}{@{$keys}} = @{$args}{@{$keys}}; + + if (!$char->{party}{joined} || !$char->{party}{users}{$info->{ID}} || !%{$char->{party}{users}{$info->{ID}}}) { + binAdd(\@partyUsersID, $info->{ID}) if (binFind(\@partyUsersID, $info->{ID}) eq ""); + if ($info->{ID} eq $accountID) { + message TF("You joined party '%s'\n", bytesToString($info->{name})), undef, 1; + # Some servers receive party_users_info before party_join when logging in + # This is to prevent clearing info already in $char->{party} + $char->{party} = {} unless ref($char->{party}) eq "HASH"; + $char->{party}{joined} = 1; + Plugins::callHook('packet_partyJoin', { partyName => bytesToString($info->{name}) }); + } else { + message TF("%s joined your party '%s'\n", bytesToString($info->{user}), bytesToString($info->{name})), undef, 1; + } + } + + my $actor = $char->{party}{users}{$info->{ID}} && %{$char->{party}{users}{$info->{ID}}} ? $char->{party}{users}{$info->{ID}} : new Actor::Party; + + $actor->{admin} = !$info->{'role'}; + delete $actor->{statuses} unless $actor->{'online'} = !$info->{'type'}; + $actor->{pos}{x} = $info->{'x'}; + $actor->{pos}{y} = $info->{'y'}; + $actor->{map} = $info->{'map'}; + $actor->{name} = bytesToString($info->{'user'}); + $actor->{ID} = $info->{'ID'}; + $actor->{lv} = $info->{'lv'} if $info->{'lv'}; + $actor->{jobID} = $info->{'jobID'} if $info->{'jobID'}; + $actor->{charID} = $info->{'charID'} if $info->{'charID'}; # why now use charID? + $char->{party}{users}{$info->{'ID'}} = $actor; + $char->{party}{name} = bytesToString($info->{'name'}); + $char->{party}{itemPickup} = $info->{'item_pickup'}; + $char->{party}{itemDivision} = $info->{'item_share'}; +} + +# TODO: store this state +sub party_allow_invite { + my ($self, $args) = @_; + + if ($args->{type}) { + message T("Not allowed other player invite to Party\n"), "party", 1; + } else { + message T("Allowed other player invite to Party\n"), "party", 1; + } +} + +sub party_chat { + my ($self, $args) = @_; + my $msg = bytesToString($args->{message}); + + # Type: String + my ($chatMsgUser, $chatMsg) = $msg =~ /(.*?) : (.*)/; + $chatMsgUser =~ s/ $//; + + stripLanguageCode(\$chatMsg); + my $parsed_msg = solveMessage($chatMsg); + # Type: String + my $chat = "$chatMsgUser : $parsed_msg"; + message TF("[Party] %s\n", $chat), "partychat"; + + chatLog("p", "$chat\n") if ($config{'logPartyChat'}); + ChatQueue::add('p', $args->{ID}, $chatMsgUser, $parsed_msg); + debug "partychat: $chatMsg\n", "partychat", 1; + + Plugins::callHook('packet_partyMsg', { + MsgUser => $chatMsgUser, + Msg => $parsed_msg, + RawMsg => $chatMsg, + }); +} + +sub party_exp { + my ($self, $args) = @_; + $char->{party}{share} = $args->{type}; # Always will be there, in 0101 also in 07D8 + if ($args->{type} == 0) { + message T("Party EXP set to Individual Take\n"), "party", 1; + } elsif ($args->{type} == 1) { + message T("Party EXP set to Even Share\n"), "party", 1; + } else { + error T("Error setting party option\n"); + } + if (exists($args->{itemPickup}) || exists($args->{itemDivision})) { + $char->{party}{itemPickup} = $args->{itemPickup}; + $char->{party}{itemDivision} = $args->{itemDivision}; + if ($args->{itemPickup} == 0) { + message T("Party item set to Individual Take\n"), "party", 1; + } elsif ($args->{itemPickup} == 1) { + message T("Party item set to Even Share\n"), "party", 1; + } else { + error T("Error setting party option\n"); + } + if ($args->{itemDivision} == 0) { + message T("Party item division set to Individual Take\n"), "party", 1; + } elsif ($args->{itemDivision} == 1) { + message T("Party item division set to Even Share\n"), "party", 1; + } else { + error T("Error setting party option\n"); + } + } +} + +sub party_leader { + my ($self, $args) = @_; + for (my $i = 0; $i < @partyUsersID; $i++) { + if (unpack("V",$partyUsersID[$i]) eq $args->{old}) { + $char->{party}{users}{$partyUsersID[$i]}{admin} = ''; + } + if (unpack("V",$partyUsersID[$i]) eq $args->{new}) { + $char->{party}{users}{$partyUsersID[$i]}{admin} = 1; + message TF("New party leader: %s\n", $char->{party}{users}{$partyUsersID[$i]}{name}), "party", 1; + } + } +} + +sub party_hp_info { + my ($self, $args) = @_; + my $ID = $args->{ID}; + + if ($char->{party}{users}{$ID}) { + $char->{party}{users}{$ID}{hp} = $args->{hp}; + $char->{party}{users}{$ID}{hp_max} = $args->{hp_max}; + } +} + +sub party_invite { + my ($self, $args) = @_; + my $name = bytesToString($args->{name}); + message TF("Incoming Request to join party '%s'\n", $name); + $incomingParty{ID} = $args->{ID}; + $incomingParty{ACK} = $args->{switch} eq '02C6' ? '02C7' : '00FF'; + $timeout{ai_partyAutoDeny}{time} = time; + Plugins::callHook('party_invite', { + partyID => $args->{ID}, + partyName => $name + }); +} + +sub party_invite_result { + my ($self, $args) = @_; + my $name = bytesToString($args->{name}); + if ($args->{type} == ANSWER_ALREADY_OTHERGROUPM) { + warning TF("Join request failed: %s is already in a party\n", $name); + } elsif ($args->{type} == ANSWER_JOIN_REFUSE) { + warning TF("Join request failed: %s denied request\n", $name); + } elsif ($args->{type} == ANSWER_JOIN_ACCEPT) { + message TF("%s accepted your request\n", $name), "info"; + } elsif ($args->{type} == ANSWER_MEMBER_OVERSIZE) { + message T("Join request failed: Party is full.\n"), "info"; + } elsif ($args->{type} == ANSWER_DUPLICATE) { + message TF("Join request failed: same account of %s allready joined the party.\n", $name), "info"; + } elsif ($args->{type} == ANSWER_JOINMSG_REFUSE) { + message TF("Join request failed: ANSWER_JOINMSG_REFUSE.\n", $name), "info"; + } elsif ($args->{type} == ANSWER_UNKNOWN_ERROR) { + message TF("Join request failed: unknown error.\n", $name), "info"; + } elsif ($args->{type} == ANSWER_UNKNOWN_CHARACTER) { + message TF("Join request failed: the character is not currently online or does not exist.\n", $name), "info"; + } elsif ($args->{type} == ANSWER_INVALID_MAPPROPERTY) { + message TF("Join request failed: ANSWER_INVALID_MAPPROPERTY.\n", $name), "info"; + } +} + +sub party_leave { + my ($self, $args) = @_; + + my $ID = $args->{ID}; + my $actor = $char->{party}{users}{$ID}; # bytesToString($args->{name}) + delete $char->{party}{users}{$ID}; + binRemove(\@partyUsersID, $ID); + if ($ID eq $accountID) { + $actor = $char; + delete $char->{party}; + undef @partyUsersID; + $char->{party}{joined} = 0; + } + + if ($args->{result} == GROUPMEMBER_DELETE_LEAVE) { + message TF("%s left the party\n", $actor); + } elsif ($args->{result} == GROUPMEMBER_DELETE_EXPEL) { + message TF("%s left the party (kicked)\n", $actor); + } else { + message TF("%s left the party (unknown reason: %d)\n", $actor, $args->{result}); + } +} + +sub party_location { + my ($self, $args) = @_; + + my $ID = $args->{ID}; + + if ($char->{party}{users}{$ID}) { + $char->{party}{users}{$ID}{pos}{x} = $args->{x}; + $char->{party}{users}{$ID}{pos}{y} = $args->{y}; + $char->{party}{users}{$ID}{online} = 1; + debug "Party member location: $char->{party}{users}{$ID}{name} - $args->{x}, $args->{y}\n", "parseMsg"; + } +} +sub party_organize_result { + my ($self, $args) = @_; + + unless ($args->{fail}) { + $char->{party}{users}{$accountID}{admin} = 1 if $char->{party}{users}{$accountID}; + } elsif ($args->{fail} == 1) { + warning T("Can't organize party - party name exists\n"); + } elsif ($args->{fail} == 2) { + warning T("Can't organize party - you are already in a party\n"); + } elsif ($args->{fail} == 3) { + warning T("Can't organize party - not allowed in current map\n"); + } else { + warning TF("Can't organize party - unknown (%d)\n", $args->{fail}); + } +} + +sub party_show_picker { + my ($self, $args) = @_; + + # wtf the server sends this packet for your own character? (rRo) + return if $args->{sourceID} eq $accountID; + + my $string = ($char->{party}{users}{$args->{sourceID}} && %{$char->{party}{users}{$args->{sourceID}}}) ? $char->{party}{users}{$args->{sourceID}}->name() : $args->{sourceID}; + my $item = {}; + $item->{nameID} = $args->{nameID}; + $item->{identified} = $args->{identified}; + $item->{upgrade} = $args->{upgrade}; + $item->{cards} = $args->{cards}; + $item->{broken} = $args->{broken}; + message TF("Party member %s has picked up item %s.\n", $string, itemName($item)), "info"; +} + +sub party_users_info { + my ($self, $args) = @_; + return unless changeToInGameState(); + + my $player_info; + + if ($args->{switch} eq '0A44') { # PACKETVER >= 20151007 + $player_info = { + len => 50, + types => 'V Z24 Z16 C2 v2', + keys => [qw(ID name map admin online jobID lv)], + }; + + } elsif ($args->{switch} eq '0AE5') { # PACKETVER >= 20171207 + $player_info = { + len => 54, + types => 'V V Z24 Z16 C2 v2', + keys => [qw(ID GID name map admin online jobID lv)], + }; + + } else { # 00FB - DEFAULT [OLD] + $player_info = { + len => 46, + types => 'V Z24 Z16 C2', + keys => [qw(ID name map admin online)], + }; + } + + $char->{party}{name} = bytesToString($args->{party_name}); + + for (my $i = 0; $i < length($args->{playerInfo}); $i += $player_info->{len}) { + # in 0a43 lasts bytes: { <item pickup rule>.B <item share rule>.B <unknown>.L } + next if (length($args->{playerInfo}) - $i == 6); + + my $ID = substr($args->{playerInfo}, $i, 4); + + if (binFind(\@partyUsersID, $ID) eq "") { + binAdd(\@partyUsersID, $ID); + } + + $char->{party}{users}{$ID} = new Actor::Party(); + @{$char->{party}{users}{$ID}}{@{$player_info->{keys}}} = unpack($player_info->{types}, substr($args->{playerInfo}, $i, $player_info->{len})); + $char->{party}{users}{$ID}{name} = bytesToString($char->{party}{users}{$ID}{name}); + $char->{party}{users}{$ID}{admin} = !$char->{party}{users}{$ID}{admin}; + $char->{party}{users}{$ID}{online} = !$char->{party}{users}{$ID}{online}; + + # If party member return to saveMap out of our screen, the server will send to us party_users_info [iRO-RT 2020-jan] + undef $char->{party}{users}{$ID}{'dead'}; + undef $char->{party}{users}{$ID}{'dead_time'}; + + debug TF("Party Member: %s (%s)\n", $char->{party}{users}{$ID}{name}, $char->{party}{users}{$ID}{map}), "party", 1; + } + Plugins::callHook('party_users_info_ready'); +} + +# Notifies the party members of a character's death or revival. +# 0AB2 <GID>.L <dead>.B +sub party_dead { + my ($self, $args) = @_; + + my $string = ($char->{party}{users}{$args->{ID}} && %{$char->{party}{users}{$args->{ID}}}) ? $char->{party}{users}{$args->{ID}}->name() : $args->{ID}; + + # 0x0 = alive + # 0x1 = dead + if ($args->{isDead} == 1) { + message TF("Party member %s is dead.\n", $string), "info"; + $char->{party}{users}{$args->{ID}}{dead} = 1; + $char->{party}{users}{$args->{ID}}{dead_time} = time; + } else { + message TF("Party member %s is alive.\n", $string), "info"; + undef $char->{party}{users}{$args->{ID}}{'dead'}; + undef $char->{party}{users}{$args->{ID}}{'dead_time'}; + } +} + +sub rodex_mail_list { + my ( $self, $args ) = @_; + + my $mail_info; + + if ($args->{switch} eq '0B5F') { + $mail_info = { + len => 45, + types => 'C V2 C2 Z24 V v x4', + keys => [qw(openType mailID1 mailID2 isRead attach sender expireSecconds Titlelength)], + }; + + } elsif ($args->{switch} eq '0AC2') { + $mail_info = { + len => 41, + types => 'C V2 C2 Z24 V v', + keys => [qw(openType mailID1 mailID2 isRead attach sender expireSecconds Titlelength)], + }; + + } else { # 09F0, 0A7D + $mail_info = { + len => 44, + types => 'V2 C2 Z24 V2 v', + keys => [qw(mailID1 mailID2 isRead attach sender regDateTime expireSecconds Titlelength)], + }; + } + + if ($args->{switch} eq '09F0' || $args->{switch} eq '0A7D') { + $rodexCurrentType = $args->{attach}; + } + + if ($args->{switch} eq '0A7D' || $args->{switch} eq '0AC2' || $args->{switch} eq '0B5F') { + $rodexList->{current_page} = 0; + $rodexList = {}; + $rodexList->{mails} = {}; + } else { + $rodexList->{current_page}++; + } + + if ($args->{isEnd} == 1) { + $rodexList->{last_page} = $rodexList->{current_page}; + } else { + $rodexList->{mails_per_page} = $args->{amount}; + } + + my $mail_len; + my $msg = center(" ". TF("Rodex Mail Page %d", $rodexList->{current_page}) ." ", 119, '-') . "\n" . + T(" # ID From Att New Expire Title\n"); + + my $index = 0; + for (my $i = 0; $i < length($args->{mailList}); $i+=$mail_info->{len}) { + my $mail; + + @{$mail}{@{$mail_info->{keys}}} = unpack($mail_info->{types}, substr($args->{mailList}, $i, $mail_info->{len})); + + $mail->{title} = solveMSG(bytesToString(substr($args->{mailList}, ($i+$mail_info->{len}), $mail->{Titlelength}))); + $mail->{sender} = solveMSG(bytesToString($mail->{sender})); + $mail->{page} = $rodexList->{current_page}; + $mail->{page_index} = $index; + $mail->{expireDay} = int ($mail->{expireSecconds} / 60 / 60 / 24); + + $i+= $mail->{Titlelength}; + + $rodexList->{mails}{$mail->{mailID1}} = $mail; + $rodexList->{current_page_last_mailID} = $mail->{mailID1}; + + my %attach = ( + #0 => '-', # no attach + 2 => T('z'), # only zeny + 4 => T('i'), # only item + 6 => T('z+i'), # zeny + item + 12 => T('gift'),# a gift from the admin + ); + $mail->{attach} = $attach{$mail->{attach}}; + + $msg .= swrite("@> @<<<<<<< @<<<<<<<<<<<<<<<<<<<<<< @<<< @<< @>>>>>>> @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<", [$index, $mail->{mailID1}, $mail->{sender}, $mail->{attach} ? $mail->{attach} : "-", $mail->{isRead} ? T("No") : T("Yes"), $mail->{expireDay} ." ".T("Days"), $mail->{title}]); + + $index++; + } + $msg .= ('-'x119) . "\n"; + message $msg, "list"; + + Plugins::callHook('rodex_mail_list', { + 'mails' => $rodexList->{mails}, + 'current_page' => $rodexList->{current_page}, + 'last_mailID' => $rodexList->{current_page_last_mailID}, + 'isEnd' => $args->{isEnd}, + }); +} + +sub rodex_read_mail { + my ( $self, $args ) = @_; + + my $msg = $args->{RAW_MSG}; + my $msg_size = $args->{RAW_MSG_SIZE}; + my $header_pack = 'v C V2 v V2 C'; + my $header_len = ((length pack $header_pack) + 2); + + my $mail = {}; + + $mail->{body} = bytesToString( substr($msg, $header_len, $args->{text_len}) ); + chomp ($mail->{body}); + $mail->{body} = solveMSG($mail->{body}); + + $mail->{zeny1} = $args->{zeny1}; + $mail->{zeny2} = $args->{zeny2}; + + $mail->{type} = $args->{type}; + my %opentype = ( + 0 => T('Mail from players'), + 1 => T('Account mail'), + 2 => T('Return'), + 3 => T('Unset'), + ); + + my $item_pack = $self->{rodex_read_mail_item_pack} || 'v2 C3 a8 a4 C a4 a25'; + my $item_len = length pack $item_pack; + + my $mail_len; + + $mail->{items} = []; + + my $print_msg = center(" " .TF("Mail %d from %s", $args->{mailID1}, $rodexList->{mails}{$args->{mailID1}}{sender}) ." ", 119, '-') . "\n"; + $print_msg .= swrite("@<<<<<<<<<<< @<<<<<<<<<<<<<<<<", [T("Mail type:"), $opentype{$mail->{type}}]); + $print_msg .= swrite("@<<<<<<<<<<< @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<", [T("Title:"), $rodexList->{mails}{$args->{mailID1}}{title}]); + $print_msg .= T("Message:") ." " .$mail->{body} ."\n"; + message $print_msg, "list"; + + $print_msg = swrite("@<<<<<<<<<<< @<<<<<<", [T("Item count:"), $args->{itemCount}]); + $print_msg .= swrite("@<<<<<<<<<<< @<<<<<<<<<", [T("Zeny:"), $args->{zeny1}]); + + my $index = 0; + for (my $i = ($header_len + $args->{text_len}); $i < $args->{RAW_MSG_SIZE}; $i += $item_len) { + my $item; + ($item->{amount}, + $item->{nameID}, + $item->{identified}, + $item->{broken}, + $item->{upgrade}, + $item->{cards}, + $item->{unknow1}, + $item->{type}, + $item->{unknow2}, + $item->{options}) = unpack($item_pack, substr($msg, $i, $item_len)); + + $item->{name} = itemName($item); + + my $display = $item->{name}; + $display .= " x $item->{amount}"; + + $print_msg .= swrite("@<<< @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<", [$index, $display]); + + push(@{$mail->{items}}, $item); + $index++; + } + + $print_msg .= ('-'x119) . "\n"; + message $print_msg, "list"; + + @{$rodexList->{mails}{$args->{mailID1}}}{qw(body items zeny1 zeny2)} = @{$mail}{qw(body items zeny1 zeny2)}; + + $rodexList->{mails}{$args->{mailID1}}{isRead} = 1; + + $rodexList->{current_read} = $args->{mailID1}; + + Plugins::callHook('rodex_mail', { + 'mailID' => $args->{mailID1}, + 'from' => $rodexList->{mails}{$args->{mailID1}}->{sender}, + 'title' => $rodexList->{mails}{$args->{mailID1}}->{title}, + 'content' => $mail->{body}, + 'zeny' => $args->{zeny1}, + 'itemCount' => $args->{itemCount}, + 'items' => $mail->{items}, + }); +} + +sub unread_rodex { + my ( $self, $args ) = @_; + message T("You have new unread rodex mails.\n"); + Plugins::callHook('rodex_unread_mail'); +} + +sub rodex_remove_item { + my ( $self, $args ) = @_; + + if (!$args->{result}) { + error T("You failed to remove an item from rodex mail.\n"); + return; + } + + my $rodex_item = $rodexWrite->{items}->getByID($args->{ID}); + + my $msg = TF("Item removed from rodex mail message: %s (%d) x %d - %s", + $rodex_item->{name}, $rodex_item->{binID}, $args->{amount}, $itemTypes_lut{$rodex_item->{type}}); + message "$msg\n", "drop"; + + $rodex_item->{amount} -= $args->{amount}; + if ($rodex_item->{amount} <= 0) { + $rodexWrite->{items}->remove($rodex_item); + } +} + +sub rodex_add_item { + my ( $self, $args ) = @_; + + if ($args->{fail} == 1) { + error T("Item attachment has been failed.\n");#RODEX_ADD_ITEM_WEIGHT_ERROR + } elsif ($args->{fail} == 2) { + error T("Item attachment has been failed.\n");#MsgStringTable[2630] + } elsif ($args->{fail} == 3) { + error T("Maximum number of item attachments has been exceeded.\n");#MsgStringTable[2698] + } elsif ($args->{fail} == 4) { + error T("This item is banned to attach.\n");#MsgStringTable[2700] + } elsif ($args->{fail} != 0) { + error TF("Unknown error %s\n", $args->{fail}); + } + return if ($args->{fail}); + + my $rodex_item = $rodexWrite->{items}->getByID($args->{ID}); + + if ($rodex_item) { + $rodex_item->{amount} += $args->{amount}; + } else { + $rodex_item = new Actor::Item(); + $rodex_item->{ID} = $args->{ID}; + $rodex_item->{nameID} = $args->{nameID}; + $rodex_item->{type} = $args->{type}; + $rodex_item->{amount} = $args->{amount}; + $rodex_item->{identified} = $args->{identified}; + $rodex_item->{broken} = $args->{broken}; + $rodex_item->{upgrade} = $args->{upgrade}; + $rodex_item->{cards} = $args->{cards}; + $rodex_item->{options} = $args->{options}; + $rodex_item->{weight} = $args->{weight}; + $rodex_item->{name} = itemName($rodex_item); + + $rodexWrite->{items}->add($rodex_item); + } + + my $msg = TF("Item added to rodex mail message: %s (%d) x %d - %s", + $rodex_item->{name}, $rodex_item->{binID}, $args->{amount}, $itemTypes_lut{$rodex_item->{type}}); + message "$msg\n", "drop"; +} + +sub rodex_open_write { + my ( $self, $args ) = @_; + + $rodexWrite = {}; + + $rodexWrite->{items} = new InventoryList; + if ($args->{name}) { + $rodexWrite->{target}{name} = bytesToString($args->{name}); + $messageSender->rodex_checkname($rodexWrite->{target}{name}); + } + $rodexWrite->{title} = T("TITLE"); + debug "Rodex Mail Target: '$rodexWrite->{target}{name}', Title: '$rodexWrite->{title}'\n"; +} + +sub rodex_check_player { + my ( $self, $args ) = @_; + my $rodex_check_player_unpack; + my $name = bytesToString($args->{name}); + + if (!$args->{char_id}) { + error TF("Could not find player with name '%s'.\n", $name); + delete $rodexWrite->{target}; + return; + } + + if ($args->{switch} eq '0A14') { + $rodex_check_player_unpack = { + target => [qw(char_id class base_level)], + }; + } elsif ($args->{switch} eq '0A51') { + $rodexWrite->{target}{name} = $name; + $rodex_check_player_unpack = { + target => [qw(char_id class base_level name)], + }; + } + + my $print_msg = center( " " .T("Rodex Mail Target") ." ", 62, '-') . "\n"; + $print_msg .= swrite(" @>>>> @<<<<<<<<<<<<<<<<<<<<<<< @<<<<<<<<<< @<<<", [T("Name:"), $rodexWrite->{target}{name}, T("Base Level:"), $args->{base_level}]); + $print_msg .= swrite("@>>>>>>> @<<<<<<<<<<<<<<<<<<<<<<< @<<<<< @<<<<<<<<<<<<<<<<<<<<", [T("Char ID:"), $args->{char_id}, T("Class:"), $jobs_lut{$args->{class}}]); + $print_msg .= ('-'x62) . "\n"; + message $print_msg, "list"; + + @{$rodexWrite->{target}}{@{$rodex_check_player_unpack->{target}}} = @{$args}{@{$rodex_check_player_unpack->{target}}}; +} + +sub rodex_write_result { + my ( $self, $args ) = @_; + + if ($args->{fail}) { + error T("You failed to send the rodex mail.\n"); + return; + } + + message T("Your rodex mail was sent with success.\n"); + undef $rodexWrite; +} + +sub rodex_get_zeny { + my ( $self, $args ) = @_; + + if ($args->{fail}) { + error T("You failed to get the zeny of the rodex mail.\n"); + return; + } + + message T("The zeny of the rodex mail was requested with success.\n"); + + $rodexList->{mails}{ $args->{mailID1} }{zeny1} = 0; + $rodexList->{mails}{ $args->{mailID1} }{attach} = $rodexList->{mails}{ $args->{mailID1} }{attach} eq 'z' ? 0 : 'i'; +} + +sub rodex_get_item { + my ( $self, $args ) = @_; + + if ($args->{fail}) { + error T("You failed to get the items of the rodex mail.\n"); + return; + } + + message T("The items of the rodex mail were requested with success.\n"); + + $rodexList->{mails}{$args->{mailID1}}{items} = []; + $rodexList->{mails}{$args->{mailID1}}{attach} = $rodexList->{mails}{$args->{mailID1}}{attach} eq 'i' ? undef : 'z'; +} + +sub rodex_delete { + my ( $self, $args ) = @_; + + return unless (exists $rodexList->{mails}{$args->{mailID1}}); + + message TF("You have deleted the mail of ID %s.\n", $args->{mailID1}); + + Plugins::callHook('rodex_mail_deleted', { + 'mailID' => $args->{mailID1}, + }); + + delete $rodexList->{mails}{$args->{mailID1}}; +} + +# 0x803 +sub booking_register_request { + my ($self, $args) = @_; + my $result = $args->{result}; + + if ($result == 0) { + message T("Booking successfully created!\n"), "booking"; + } elsif ($result == 2) { + error T("You already got a reservation group active!\n"), "booking"; + } else { + error TF("Unknown error in creating the group booking (Error %s)\n", $result), "booking"; + } +} + +# 0x805 +sub booking_search_request { + my ($self, $args) = @_; + + if (length($args->{innerData}) == 0) { + error T("Without results!\n"), "booking"; + return; + } + + message T("-------------- Booking Search ---------------\n"); + for (my $offset = 0; $offset < length($args->{innerData}); $offset += 48) { + my ($index, $charName, $expireTime, $level, $mapID, @job) = unpack("V Z24 V s8", substr($args->{innerData}, $offset, 48)); + message swrite( + T("Name: \@<<<<<<<<<<<<<<<<<<<<<<<< Index: \@>>>>\n" . + "Created: \@<<<<<<<<<<<<<<<<<<<<< Level: \@>>>\n" . + "MapID: \@<<<<<\n". + "Job: \@<<<< \@<<<< \@<<<< \@<<<< \@<<<<\n" . + "---------------------------------------------"), + [bytesToString($charName), $index, getFormattedDate($expireTime), $level, $mapID, @job]), "booking"; + } +} + +# 0x807 +sub booking_delete_request { + my ($self, $args) = @_; + my $result = $args->{result}; + + if ($result == 0) { + message T("Reserve deleted successfully!\n"), "booking"; + } elsif ($result == 3) { + error T("You're not with a group booking active!\n"), "booking"; + } else { + error TF("Unknown error in deletion of group booking (Error %s)\n", $result), "booking"; + } +} + +# 0x809 +sub booking_insert { + my ($self, $args) = @_; + + message TF("%s has created a new group booking (index: %s)\n", bytesToString($args->{name}), $args->{ID}); +} + +# 0x80A +sub booking_update { + my ($self, $args) = @_; + + message TF("Reserve index of %s has changed its settings\n", $args->{ID}); +} + +# 0x80B +sub booking_delete { + my ($self, $args) = @_; + + message TF("Deleted reserve group index %s\n", $args->{ID}); +} + +sub clan_user { + my ($self, $args) = @_; + foreach (qw(onlineuser totalmembers)) { + $clan{$_} = $args->{$_}; + } + $clan{onlineuser} = $args->{onlineuser}; + $clan{totalmembers} = $args->{totalmembers}; +} + +sub clan_info { + my ($self, $args) = @_; + foreach (qw(clan_ID clan_name clan_master clan_map alliance_count antagonist_count)) { + $clan{$_} = $args->{$_}; + } + + $clan{clan_name} = bytesToString($args->{clan_name}); + $clan{clan_master} = bytesToString($args->{clan_master}); + $clan{clan_map} = bytesToString($args->{clan_map}); + + my $i = 0; + my $count = 0; + $clan{ally_names} = ""; + $clan{antagonist_names} = ""; + + if ($args->{alliance_count} > 0) { + for ($count; $count < $args->{alliance_count}; $count++) { + $clan{ally_names} .= bytesToString(unpack("Z24", substr($args->{ally_antagonist_names}, $i, 24))).", "; + $i += 24; + } + } + + $count = 0; + if ($args->{antagonist_count} > 0) { + for ($count; $count < $args->{antagonist_count}; $count++) { + $clan{antagonist_names} .= bytesToString(unpack("Z24", substr($args->{ally_antagonist_names}, $i, 24))).", "; + $i += 24; + } + } +} + +sub clan_chat { + my ($self, $args) = @_; + my ($chatMsgUser, $chatMsg, $parsed_msg); # Type: String + + return unless changeToInGameState(); + $chatMsgUser = bytesToString($args->{charname}); + $chatMsg = bytesToString($args->{message}); + $parsed_msg = solveMessage($chatMsg); + + chatLog("clan", "$chatMsgUser : $parsed_msg\n") if ($config{'logClanChat'}); + # Translation Comment: Guild Chat + message TF("[Clan]%s %s\n", $chatMsgUser, $parsed_msg), "clanchat"; + # Only queue this if it's a real chat message + ChatQueue::add('clan', 0, $chatMsgUser, $parsed_msg) if ($chatMsgUser); + debug "clanchat: $chatMsg\n", "clanchat", 1; + + Plugins::callHook('packet_clanMsg', { + MsgUser => $chatMsgUser, + Msg => $parsed_msg, + RawMsg => $chatMsg, + }); +} + +sub clan_leave { + my ($self, $args) = @_; + + if ($clan{clan_name}) { + message TF("[Clan] You left %s\n", $clan{clan_name}); + undef %clan; + } +} + +sub change_title { + my ($self, $args) = @_; + #TODO : <result>.B + message TF("You changed Title_ID : %s.\n", $args->{title_id}), "info"; +} + +# 019E +# TODO +# note: this is probably the trigger for the client's slotmachine effect or so. +sub pet_capture_process { + my ($self, $args) = @_; + message T("Attempting to capture pet (slot machine).\n"), "info"; +} + +sub pet_capture_result { + my ($self, $args) = @_; + if ($args->{success}) { + message T("Pet capture success\n"), "info"; + } else { + message T("Pet capture failed\n"), "info"; + } +} + +sub pet_emotion { + my ($self, $args) = @_; + my ($ID, $type) = ($args->{ID}, $args->{type}); + my $emote = $emotions_lut{$type}{display} || "/e$type"; + if ($pets{$ID}) { + message $pets{$ID}->name . " : $emote\n", "emotion"; + } +} + +sub pet_evolution_result { + my ($self, $args) = @_; + if ($args->{result} == 0x0) { + error TF("Pet evolution error.\n"); + #PET_EVOL_NO_CALLPET = 0x1, + #PET_EVOL_NO_PETEGG = 0x2, + } elsif ($args->{result} == 0x3) { + error TF("Unequip pet accessories first to start evolution.\n"); + } elsif ($args->{result} == 0x4) { + error TF("Insufficient materials for evolution.\n"); + } elsif ($args->{result} == 0x5) { + error TF("Loyal Intimacy is required to evolve.\n"); + } elsif ($args->{result} == 0x6) { + message TF("Pet evolution success.\n"), "success"; + } +} + +sub pet_food { + my ($self, $args) = @_; + if ($args->{success}) { + message TF("Fed pet with %s\n", itemNameSimple($args->{foodID})), "pet"; + } else { + error TF("Failed to feed pet with %s: no food in inventory.\n", itemNameSimple($args->{foodID})); + } +} + +sub pet_info { + my ($self, $args) = @_; + $pet{name} = bytesToString($args->{name}); + $pet{renameflag} = $args->{renameflag}; + $pet{level} = $args->{level}; + $pet{hungry} = $args->{hungry}; + $pet{friendly} = $args->{friendly}; + $pet{accessory} = $args->{accessory}; + $pet{type} = $args->{type} if (exists $args->{type}); + debug "Pet status: name=$pet{name} name_set=". ($pet{renameflag} ? 'yes' : 'no') ." level=$pet{level} hungry=$pet{hungry} intimacy=$pet{friendly} accessory=".itemNameSimple($pet{accessory})." type=".($pet{type}||"N/A")."\n", "pet"; +} + +sub pet_info2 { + my ($self, $args) = @_; + my ($type, $ID, $value) = @{$args}{qw(type ID value)}; + + # receive information about your pet + + # related freya functions: clif_pet_equip clif_pet_performance clif_send_petdata + + # these should never happen, pets should spawn like normal actors (at least on Freya) + # this isn't even very useful, do we want random pets with no location info? + #if (!$pets{$ID} || !%{$pets{$ID}}) { + # binAdd(\@petsID, $ID); + # $pets{$ID} = {}; + # %{$pets{$ID}} = %{$monsters{$ID}} if ($monsters{$ID} && %{$monsters{$ID}}); + # $pets{$ID}{'name_given'} = "Unknown"; + # $pets{$ID}{'binID'} = binFind(\@petsID, $ID); + # debug "Pet spawned (unusually): $pets{$ID}{'name'} ($pets{$ID}{'binID'})\n", "parseMsg"; + #} + #if ($monsters{$ID}) { + # if (%{$monsters{$ID}}) { + # objectRemoved('monster', $ID, $monsters{$ID}); + # } + # # always clear these in case + # binRemove(\@monstersID, $ID); + # delete $monsters{$ID}; + #} + + if ($type == 0) { + # You own no pet. + undef $pet{ID}; + + } elsif ($type == 1) { + $pet{friendly} = $value; + debug "Pet friendly: $value\n"; + + } elsif ($type == 2) { + $pet{hungry} = $value; + debug "Pet hungry: $value\n"; + + } elsif ($type == 3) { + # accessory info for any pet in range + $pet{accessory} = $value; + debug "Pet accessory info: $value\n"; + + } elsif ($type == 4) { + # performance info for any pet in range + #debug "Pet performance info: $value\n"; + + } elsif ($type == 5) { + # You own pet with this ID + $pet{ID} = $ID; + } +} + +sub elemental_info { + my ($self, $args) = @_; + + $char->{elemental} = Actor::get($args->{ID}) if ($char->{elemental}{ID} ne $args->{ID}); + if (!defined $char->{elemental}) { + $char->{elemental} = new Actor::Elemental; + } + + foreach (@{$args->{KEYS}}) { + $char->{elemental}{$_} = $args->{$_}; + } +} + +# 0221 +sub upgrade_list { + my ($self, $args) = @_; + undef $refineList; + my $k = 0; + my $msg; + + $msg .= center(" " . T("Upgrade List") . " ", 79, '-') . "\n"; + + for (my $i = 0; $i < length($args->{item_list}); $i += 13) { + my ($index, $nameID) = unpack('a2 x6 C', substr($args->{item_list}, $i, 13)); + my $item = $char->inventory->getByID($index); + $refineList->[$k] = unpack('v', $item->{ID}); + $msg .= swrite(sprintf("\@%s - \@%s (\@%s)", ('<'x2), ('<'x50), ('<'x3)), [$k, itemName($item), $item->{binID}]); + $k++; + } + + $msg .= sprintf("%s\n", ('-'x79)); + + message($msg, "list"); + message T("You can now use the 'refine' command.\n"), "info"; +} + +# 025A +sub cooking_list { + my ($self, $args) = @_; + undef $cookingList; + undef $currentCookingType; + my $k = 0; + my $msg; + $currentCookingType = $args->{type}; + $msg .= center(" " . T("Cooking List") . " ", 79, '-') . "\n"; + for (my $i = 0; $i < length($args->{item_list}); $i += 2) { + my $nameID = unpack('v', substr($args->{item_list}, $i, 2)); + $cookingList->[$k] = $nameID; + $msg .= swrite(sprintf("\@%s \@%s", ('>'x2), ('<'x50)), [$k, itemNameSimple($nameID)]); + $k++; + } + $msg .= sprintf("%s\n", ('-'x79)); + + message($msg, "list"); + message T("You can now use the 'cook' command.\n"), "info"; + + Plugins::callHook('cooking_list', { + cooking_list => $cookingList, + }); +} + +sub refine_result { + my ($self, $args) = @_; + if ($args->{fail} == 0) { + message TF("You successfully refined a weapon (ID %s)!\n", $args->{nameID}); + } elsif ($args->{fail} == 1) { + message TF("You failed to refine a weapon (ID %s)!\n", $args->{nameID}); + } elsif ($args->{fail} == 2) { + message TF("You successfully made a potion (ID %s)!\n", $args->{nameID}); + } elsif ($args->{fail} == 3) { + message TF("You failed to make a potion (ID %s)!\n", $args->{nameID}); + } elsif ($args->{fail} == 6) { + message TF("You successfully cook a item (ID %s)!\n", $args->{nameID}); + } else { + message TF("You tried to refine a weapon (ID %s); result: unknown %s\n", $args->{nameID}, $args->{fail}); + } +} + +# 0223 +sub upgrade_message { + my ($self, $args) = @_; + my $item = itemNameSimple($args->{itemID}); + if ($args->{type} == 0) { # Success + message TF("Weapon upgraded: %s\n", $item), "info"; + } elsif ($args->{type} == 1) { # Fail + message TF("Weapon not upgraded: %s\n", $item), "info"; + # message TF("Weapon upgraded: %s\n", $item), "info"; + } elsif ($args->{type} == 2) { # Fail Lvl + error TF("Cannot upgrade %s until you level up the upgrade weapon skill.\n", $item), "info"; + } elsif ($args->{type} == 3) { # Fail Item + message TF("You lack item %s to upgrade the weapon.\n", $item), "info"; + } +} + +sub open_buying_store_fail { #0x812 + my ($self, $args) = @_; + my $result = $args->{result}; + if ($result == 1){ + error T("Failed to open Purchasing Store.\n"),"info"; + } elsif ($result == 2){ + error T("The total weight of the item exceeds your weight limit. Please reconfigure.\n"), "info"; + } elsif ($result == 8){ + error T("Shop information is incorrect and cannot be opened.\n"), "info"; + } else { + error T("Failed opening your buying store.\n"), "info"; + } + $buyershopstarted = 0; +} + +sub search_store_open { + my ($self, $args) = @_; + + debug TF("Opened %s for searching open vendors in this map.\n", + $args->{type} ? T("Universal Catalog Gold") : T("Universal Catalog Silver")), + 2, "search_store"; + message TF("You can now search open vendors in this map. Searches remaining: %d\n", $args->{amount}); + + $universalCatalog{open} = 1; + $universalCatalog{type} = $args->{type}; +} + +sub search_store_fail { + my ($self, $args) = @_; + + error TF("Search store failed. Reason #%d\n", $args->{reason}); + + if ($args->{reason} == 0) { + error $msgTable[1804] . "\n"; + } elsif ($args->{reason} == 1) { + error $msgTable[1785] . "\n"; + } elsif ($args->{reason} == 2) { + error $msgTable[1799] . "\n"; + } elsif ($args->{reason} == 3) { + error $msgTable[1801] . "\n"; + } elsif ($args->{reason} == 4) { + error $msgTable[1798] . "\n"; + } else { + error "Unknown reason\n"; + } +} + +sub search_store_result { + my ($self, $args) = @_; + my $step = (length($args->{storeInfo}) % 114 == 0) ? 114 : 131; + my $unpackString = "a4 a4 a80 v C V v C a16" . (($step == 114) ? "" : " a17"); + + @{$universalCatalog{list}} = () if $args->{first_page}; + $universalCatalog{has_next} = $args->{has_next}; + + my @universalCatalogPage; + + for (my $i = 0; $i < length($args->{storeInfo}); $i += $step) { + my ($storeID, $accountID, $shopName, $nameID, $itemType, $price, $amount, $refine, $cards, $unknown) = unpack($unpackString, substr($args->{storeInfo}, $i, $step)); + + my @cards = unpack "v4", $cards; + + my $universalCatalogInfo = { + storeID => $storeID, + accountID => $accountID, + shopName => $shopName, + nameID => $nameID, + itemType => $itemType, + price => $price, + amount => $amount, + refine => $refine, + cards_nameID => $cards, + cards => \@cards, + unknown => $unknown + }; + + push(@universalCatalogPage, $universalCatalogInfo); + Plugins::callHook('search_store', $universalCatalogInfo); + } + + return unless scalar @universalCatalogPage; + + push(@{$universalCatalog{list}}, \@universalCatalogPage); + Misc::searchStoreInfo(scalar(@{$universalCatalog{list}}) - 1); +} + +sub search_store_pos { + my ($self, $args) = @_; + + message TF("Selected store is at (%d, %d)\n", $args->{x}, $args->{y}); +} + +sub skill_msg { + my ($self, $args) = @_; + if ($msgTable[++$args->{msgid}]) { # show message from msgstringtable.txt -> [<Skill_Name>] <Message> + my $skill = new Skill(idn => $args->{id}); + message "[".$skill->getName."] $msgTable[$args->{msgid}]\n", "info"; + } else { + warning TF("Unknown skill_msg msgid:%d skill:%d. Need to update the file msgstringtable.txt (from data.grf)\n", $args->{msgid}, $args->{id}); + } +} + +# Display msgstringtable.txt string and fill in a valid for %d format (ZC_MSG_VALUE). +# 07E2 <message>.W <value>.L +# Displays msgstringtable.txt string in a color. (ZC_MSG_COLOR). +# 09CD <msg id>.W <color>.L +# Displays a format string from msgstringtable.txt with a %s value and color (ZC_FORMATSTRING_MSG). +# 0A6F +sub message_string { + my ($self, $args) = @_; + + my $index = ++$args->{index}; + my $param = bytesToString($args->{param}) if $args->{param}; + + if ($msgTable[$index]) { # show message from msgstringtable.txt + if ($param && ($args->{switch} eq '07E2' || $args->{switch} eq '0A6F') ) { + warning sprintf($msgTable[$index], $param)."\n"; + } else { + warning "$msgTable[$index]\n"; + } + } else { + warning TF("Unknown message_string: %s param: %s. Need to update the file msgstringtable.txt (from data.grf)\n", $index, $param); + } + + $self->mercenary_off() if ($index >= 1267 && $index <= 1270); + + Plugins::callHook('packet_message_string', { + index => $index, + val => $param + }); +} + +# TODO: move @skillsID to Actor, per-actor {skills}, Skill::DynamicInfo +sub skills_list { + my ($self, $args) = @_; + + return unless changeToInGameState(); + + my $msg = $args->{RAW_MSG}; + + my $skill_info; + + if ($args->{switch} eq '0B32') { + $skill_info = { + len => 15, + types => 'v V v3 C v', + keys => [qw(ID targetType lv sp range up lv2)], + }; + } else { + $skill_info = { + len => 37, + types => 'v1 V1 v3 Z24 C1', + keys => [qw(ID targetType lv sp range handle up)], + }; + } + + # TODO: per-actor, if needed at all + # Skill::DynamicInfo::clear; + my ($ownerType, $hook, $actor) = @{{ + '010F' => [Skill::OWNER_CHAR, 'packet_charSkills', $char], + '0235' => [Skill::OWNER_HOMUN, 'packet_homunSkills', $char->{homunculus}], + '029D' => [Skill::OWNER_MERC, 'packet_mercSkills', $char->{mercenary}], + '0B32' => [Skill::OWNER_CHAR, 'packet_charSkills', $char], + }->{$args->{switch}}}; + + my $skillsIDref; + if ($ownerType == Skill::OWNER_CHAR) { + $skillsIDref = \@skillsID; + delete @{$char->{skills}}{@$skillsIDref}; + } elsif ($ownerType == Skill::OWNER_HOMUN) { + $skillsIDref = \@{$char->{homunculus}->{slave_skillsID}}; + delete @{$char->{homunculus}->{skills}}{@$skillsIDref}; + } elsif ($ownerType == Skill::OWNER_MERC) { + $skillsIDref = \@{$char->{mercenary}->{slave_skillsID}}; + delete @{$char->{mercenary}->{skills}}{@$skillsIDref}; + } + @$skillsIDref = (); + + for (my $i = 4; $i < $args->{RAW_MSG_SIZE}; $i += $skill_info->{len}) { + my $skill; + @{$skill}{@{$skill_info->{keys}}} = unpack($skill_info->{types}, substr($msg, $i, $skill_info->{len})); + + my $handle = Skill->new(idn => $skill->{ID})->getHandle; + + foreach(@{$skill_info->{keys}}) { + $actor->{skills}{$handle}{$_} = $skill->{$_}; + } + + binAdd($skillsIDref, $handle) unless defined binFind($skillsIDref, $handle); + Skill::DynamicInfo::add($skill->{ID}, $handle, $skill->{lv}, $skill->{sp}, $skill->{range}, $skill->{targetType}, $ownerType); + + Plugins::callHook($hook, { + ID => $skill->{ID}, + handle => $handle, + level => $skill->{lv}, + upgradable => $skill->{up}, + level2 => $skill->{lv2}, + }); + } +} + +# TODO: use $args->{type} if present +sub skill_update { + my ($self, $args) = @_; + + my ($ID, $lv, $sp, $range, $up) = ($args->{skillID}, $args->{lv}, $args->{sp}, $args->{range}, $args->{up}); + + my $skill = new Skill(idn => $ID); + my $handle = $skill->getHandle(); + my $name = $skill->getName(); + $char->{skills}{$handle}{lv} = $lv; + $char->{skills}{$handle}{sp} = $sp; + $char->{skills}{$handle}{range} = $range; + $char->{skills}{$handle}{up} = $up; + + Skill::DynamicInfo::add($ID, $handle, $lv, $sp, $range, $skill->getTargetType(), Skill::OWNER_CHAR); + + Plugins::callHook('packet_charSkills', { + ID => $ID, + handle => $handle, + level => $lv, + upgradable => $up, + level2 => $args->{lv2}, + }); + + debug "Skill $name: $lv\n", "parseMsg"; +} + +#TODO ! +sub overweight_percent { + my ($self, $args) = @_; + debug "Received overweight percent: $args->{percent}\n"; +} + +sub partylv_info { + my ($self, $args) = @_; + my $ID = $args->{ID}; + if ($char->{party}{users}{$ID}) { + $char->{party}{users}{$ID}{job} = $args->{job}; + $char->{party}{users}{$ID}{lv} = $args->{lv}; + } +} + +sub achievement_reward_ack { + my ($self, $args) = @_; + message TF("Received reward for achievement '%s' (%s).\n", ($achievements{$args->{achievementID}}) ? $achievements{$args->{achievementID}}->{title} : "", $args->{achievementID}), "info"; +} + +sub achievement_update { + my ($self, $args) = @_; + + my $achieve; + @{$achieve}{qw(achievementID completed objective1 objective2 objective3 objective4 objective5 objective6 objective7 objective8 objective9 objective10 completed_at reward)} = @{$args}{qw(achievementID completed objective1 objective2 objective3 objective4 objective5 objective6 objective7 objective8 objective9 objective10 completed_at reward)}; + + $achievementList->{$achieve->{achievementID}} = $achieve; + message TF("Achievement '%s' (%s) added or updated.\n", ($achievements{$achieve->{achievementID}}) ? $achievements{$achieve->{achievementID}}->{title} : "", $achieve->{achievementID}), "info"; +} + +sub achievement_list { + my ($self, $args) = @_; + + $achievementList = {}; + + my $msg = $args->{RAW_MSG}; + my $msg_size = $args->{RAW_MSG_SIZE}; + my $headerlen = 22; + my $achieve_pack = 'V C V10 V C'; + my $achieve_len = length pack $achieve_pack; + + for (my $i = $headerlen; $i < $args->{RAW_MSG_SIZE}; $i+=$achieve_len) { + my $achieve; + + ($achieve->{achievementID}, + $achieve->{completed}, + $achieve->{objective1}, + $achieve->{objective2}, + $achieve->{objective3}, + $achieve->{objective4}, + $achieve->{objective5}, + $achieve->{objective6}, + $achieve->{objective7}, + $achieve->{objective8}, + $achieve->{objective9}, + $achieve->{objective10}, + $achieve->{completed_at}, + $achieve->{reward}) = unpack($achieve_pack, substr($msg, $i, $achieve_len)); + + $achievementList->{$achieve->{achievementID}} = $achieve; + message TF("Achievement '%s' (%s) added.\n", ($achievements{$achieve->{achievementID}}) ? $achievements{$achieve->{achievementID}}->{title} : "",$achieve->{achievementID}), "info"; + } +} + +# Notification about the result of a disconnect request (ZC_ACK_REQ_DISCONNECT). +# 018B <result>.W +# result: +# 0 = disconnect (quit) +# 1 = cannot disconnect (wait 10 seconds) +# ? = ignored +sub quit_response { + my ($self, $args) = @_; + if ($args->{fail}) { # NOTDISCONNECTABLE_STATE = 0x1 + error T("Please wait 10 seconds before trying to log out.\n"); # MSI_CANT_EXIT_NOW = 0x1f6 + } else { # DISCONNECTABLE_STATE = 0x0 + message T("Logged out from the server succesfully.\n"), "success"; + } +} + +sub private_airship_type { + my ($self, $args) = @_; + my $result = $args->{type}; + if (!defined $result) { + warning T("Received Private Airship response without a result code.\n"); + return; + } + my $item_id = $char->{last_private_airship_item}; + my $item_name = defined $item_id ? itemNameSimple($item_id) : itemNameSimple(25464); + if ($result == 0) { + message T("Use Private Airship success.\n"), "info"; + } elsif ($result == 1) { + error T("Please try PivateAirship again.\n"); + } elsif ($result == 2) { + error TF("You do not have enough %s to use Private Airship.\n", $item_name); + } elsif ($result == 3) { + error T("Destination map is invalid.\n"); + } elsif ($result == 4) { + error T("Source map is invalid.\n"); + } elsif ($result == 5) { + my $required_item = itemNameSimple(25464); + error TF("%s cannot be used for Private Airship. Please use %s.\n", $item_name, $required_item); + } else { + warning TF("Unknown Private Airship response %d.\n", $result); + } +} + +# 00CB +sub sell_result { + my ($self, $args) = @_; + if ($args->{fail}) { + error T("Sell failed.\n"); + } else { + message TF("Sold %s items.\n", @sellList.""), "success"; + message T("Sell completed.\n"), "success"; + } + @sellList = (); + if (AI::is("sellAuto")) { + AI::args->{recv_sell_packet} = 1; + } +} + +sub GM_req_acc_name { + my ($self, $args) = @_; + message TF("The accountName for ID %s is %s.\n", $args->{targetID}, $args->{accountName}), "info"; +} + +# 0293 +sub boss_map_info { + my ($self, $args) = @_; + my $bossName = bytesToString($args->{name}); + + if ($args->{flag} == 0) { + message T("You cannot find any trace of a Boss Monster in this area.\n"), "info"; + } elsif ($args->{flag} == 1) { + message TF("MVP Boss %s is now on location: (%d, %d)\n", $bossName, $args->{x}, $args->{y}), "info"; + } elsif ($args->{flag} == 2) { + message TF("MVP Boss %s has been detected on this map!\n", $bossName), "info"; + } elsif ($args->{flag} == 3) { + message TF("MVP Boss %s is dead, but will spawn again in %d hour(s) and %d minutes(s).\n", $bossName, $args->{hours}, $args->{minutes}), "info"; + } else { + debug $self->{packet_list}{$args->{switch}}->[0] . " " . join(', ', @{$args}{@{$self->{packet_list}{$args->{switch}}->[2]}}) . "\n"; + warning TF("Unknown results in %s (flag: %s)\n", $self->{packet_list}{$args->{switch}}->[0], $args->{flag}); + } +} + +sub adopt_reply { + my ($self, $args) = @_; + if ($args->{type} == 0) { + message T("You cannot adopt more than 1 child.\n"), "info"; + } elsif ($args->{type} == 1) { + message T("You must be at least character level 70 in order to adopt someone.\n"), "info"; + } elsif ($args->{type} == 2) { + message T("You cannot adopt a married person.\n"), "info"; + } +} + +sub GM_silence { + my ($self, $args) = @_; + if ($args->{flag}) { + message TF("You have been: muted by %s.\n", bytesToString($args->{name})), "info"; + } else { + message TF("You have been: unmuted by %s.\n", bytesToString($args->{name})), "info"; + } +} + +sub guild_storage_log { + my ($self, $args) = @_; + + if ($args->{result} == 0 || $args->{result} == 1) { + my %action = ( + 0 => T('Get'), + 1 => T('Put'), + ); + + my $storage_info = { + len => 83, + types => 'a4 v V C V a8 C v a8 Z24 Z24 C', + keys => [qw(ID nameID amount action upgrade uniqueID identified type_equip cards charName time attribute)], + }; + + my $message = center(T("[ Guild Storage LOG ]"), 80, '-') ."\n". + T("# Name Item-Name Amount Action Time\n"); + + my $index = 0; + for (my $i = 0; $i < length($args->{log}); $i+= $storage_info->{len}) { + my $item; + @{$item}{@{$storage_info->{keys}}} = unpack($storage_info->{types}, substr($args->{log}, $i, $storage_info->{len})); + $item->{charName} = bytesToString($item->{charName}); + $item->{time} = bytesToString($item->{time}); + $message .= swrite(sprintf("\@%s \@%s \@%s \@%s \@%s \@%s", ('<'x2), ('<'x24), ('<'x48), ('<'x6), ('<'x7), ('<'x20)), [$index, $item->{charName}, itemName($item), $item->{amount}, $action{$item->{action}}, $item->{time}]); + $index++; + } + + $message .= sprintf("%s\n", ('-'x80)); + message($message, "list"); + + } elsif ($args->{result} == 2) { + message TF("Guild Storage empty.\n"), "info"; + } elsif ($args->{result} == 3) { + message TF("You are not currently using Guild Storage. Please try later.\n"), "info"; + } +} + +sub skill_delete { + my ( $self, $args ) = @_; + my $skill = new Skill( idn => $args->{skillID} ); + return if !$skill; + return if !$char->{skills}->{ $skill->getHandle }; + + message TF( "Lost skill: %s\n", $skill->getName ), 'skill'; + delete $char->{skills}->{ $skill->getHandle }; + binRemove( \@skillsID, $skill->getHandle ); +} + +# captcha packets from kRO::RagexeRE_2009_09_22a + +# 07E6? +sub captcha_session_ID { + my ($self, $args) = @_; + debug $self->{packet_list}{$args->{switch}}->[0] . " " . join(', ', @{$args}{@{$self->{packet_list}{$args->{switch}}->[2]}}) . "\n"; +} + +# 0x07e8,-1 +# todo: debug + remove debug message +sub captcha_image { + my ($self, $args) = @_; + debug $self->{packet_list}{$args->{switch}}->[0] . " " . join(', ', @{$args}{@{$self->{packet_list}{$args->{switch}}->[2]}}) . "\n"; + + my $hookArgs = {image => $args->{image}}; + Plugins::callHook ('captcha_image', $hookArgs); + return 1 if $hookArgs->{return}; + + my $file = $Settings::logs_folder . "/captcha.bmp"; + open my $DUMP, '>', $file; + print $DUMP $args->{image}; + close $DUMP; + + $hookArgs = {file => $file}; + Plugins::callHook ('captcha_file', $hookArgs); + return 1 if $hookArgs->{return}; + + warning "captcha.bmp has been saved to: " . $Settings::logs_folder . ", open it, solve it and use the command: captcha <text>\n"; +} + +# 0x07e9,5 +# todo: debug + remove debug message +sub captcha_answer { + my ($self, $args) = @_; + debug $self->{packet_list}{$args->{switch}}->[0] . " " . join(', ', @{$args}{@{$self->{packet_list}{$args->{switch}}->[2]}}) . "\n"; + debug ($args->{flag} ? "good" : "bad") . " answer\n"; + $captcha_state = $args->{flag}; + + Plugins::callHook ('captcha_answer', {flag => $args->{flag}}); +} + +sub open_buying_store { + my($self, $args) = @_; + my $amount = $args->{amount}; + message TF("Your buying store can buy %d items \n", $amount); +} + +# TODO +sub buyer_items +{ + my($self, $args) = @_; + + my $BinaryID = $args->{venderID}; + my $Player = Actor::get($BinaryID); + my $Name = $Player->name; + + my $headerlen = 12; + my $Total = unpack('V4', substr($args->{msg}, $headerlen, 4)); + $headerlen += 4; + + for (my $i = $headerlen; $i < $args->{msg_size}; $i+=9) + { + my $Item = {}; + + ($Item->{price}, + $Item->{amount}, + undef, + $Item->{nameID}) = unpack('V v C v', substr($args->{msg}, $i, 9)); + } +} + +sub open_buying_store_item_list { + my ($self, $args) = @_; + + my $msg = $args->{RAW_MSG}; + my $msg_size = $args->{RAW_MSG_SIZE}; + my $headerlen = 12; + my $unpack = $self->{open_buying_store_items_list_pack} || 'V v C v'; + my $len = length pack $unpack; + + undef @selfBuyerItemList; + + #started a shop. + message TF("Buying Shop opened!\n"), "BuyShop"; +# what is: +# @articles = (); +# $articles = 0; + my $index = 0; + + for (my $i = $headerlen; $i < $msg_size; $i += $len) { + my $item = {}; + + ($item->{price}, + $item->{amount}, + $item->{type}, + $item->{nameID}) = unpack($unpack, substr($msg, $i, $len)); + + $item->{name} = itemName($item); + $selfBuyerItemList[$index] = $item; + + Plugins::callHook('packet_open_buying_store', { + name => $item->{name}, + amount => $item->{amount}, + price => $item->{price}, + type => $item->{type} + }); + + $index++; + } + Commands::run('bs'); +} + +sub buying_store_found { + my ($self, $args) = @_; + my $ID = $args->{ID}; + + if (!$buyerLists{$ID} || !%{$buyerLists{$ID}}) { + binAdd(\@buyerListsID, $ID); + Plugins::callHook('packet_buying', {ID => $ID}); + } + $buyerLists{$ID}{title} = bytesToString($args->{title}); + $buyerLists{$ID}{id} = $ID; +} + +sub buying_store_lost { + my ($self, $args) = @_; + + my $ID = $args->{ID}; + binRemove(\@buyerListsID, $ID); + delete $buyerLists{$ID}; +} + +sub buying_store_items_list { + my($self, $args) = @_; + + undef $buyerPriceLimit; + undef $buyerID; + undef $buyingStoreID; + + $buyerItemList->clear; + + $buyerPriceLimit = $args->{zeny}; + $buyerID = $args->{buyerID}; + $buyingStoreID = $args->{buyingStoreID}; + + my $expireDate = 0; + my $player = Actor::get($buyerID); + my $index = 0; + my $pack = $self->{buying_store_items_list_pack} || 'V v C v'; + my $item_len = length pack $pack; + my $item_list_len = length $args->{itemList}; + + my $msg = center(T(" Buyer: ") . $player->nameIdx . ' ', 83, '-') ."\n". + T("# Name Type Price Amount\n"); + + for (my $i = 0; $i < $item_list_len; $i+=$item_len) { + my $item = Actor::Item->new; + + ($item->{price}, + $item->{amount}, + $item->{type}, + $item->{nameID}) = unpack($pack, substr($args->{itemList}, $i, $item_len)); + + $item->{name} = itemName($item); + $item->{ID} = $i; + + $buyerItemList->add($item); + + debug "Item added to Buying Store: $item->{name} - $item->{price} z\n", "buying_store", 2; + + Plugins::callHook('packet_buying_store', { + buyerID => $buyerID, + number => $index, + name => $item->{name}, + amount => $item->{amount}, + price => $item->{price}, + type => $item->{type} + }); + + $msg .= swrite( + "@< @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< @<<<<<<<<<<<<<< @>>>>>>>>>>>>z @<<<<<", + [$index, $item->{name}, $itemTypes_lut{$item->{type}}, formatNumber($item->{price}), formatNumber($item->{amount})]); + + $index++; + } + + $msg .= "\n" . TF("Price limit: %s Zeny\n", formatNumber($buyerPriceLimit)) . ('-'x83) . "\n"; + message $msg, "list"; + + if ($args->{expireDate}) { + $expireDate = $args->{expireDate}; + my $date = int(time) + int($args->{expireDate}/1000); + message "Expire Date: ".getFormattedDate($date)."\n"; + } + + Plugins::callHook('packet_buying_store2', { + buyerID => $buyerID, + buyingStoreID => $buyingStoreID, + itemList => $buyerItemList, + expireDate => $expireDate, + }); +} + +sub buying_store_item_delete { + my($self, $args) = @_; + return unless changeToInGameState(); + my $item = $char->inventory->getByID($args->{ID}); + my $zeny = $args->{amount} * $args->{zeny}; + if ($item) { + inventoryItemRemoved($item->{binID}, $args->{amount}); + } + message TF("You have sold %s. Amount: %s. Total zeny: %sz\n", $item, $args->{amount}, $zeny);# msgstring 1747 +} + +sub buying_store_fail { + my ($self, $args) = @_; + if ($args->{result} == 5) { + error T("The deal has failed.\n");# msgstring 58 + } elsif ($args->{result} == 6) { + error TF("%s item could not be sold because you do not have the wanted amount of items.\n", itemNameSimple($args->{itemID}));# msgstring 1748 + } elsif ($args->{result} == 7) { + error T("Failed to deal because you have not enough Zeny.\n");# msgstring 1746 + } else { + error TF("Unknown 'buying_store_fail' result: %s.\n", $args->{result}); + } +} + +sub buying_store_update { + my($self, $args) = @_; + if (@selfBuyerItemList) { + for(my $i = 0; $i < @selfBuyerItemList; $i++) { + my $item = $selfBuyerItemList[$i]; + if ($item->{nameID} == $args->{itemID}) { + message TF("You bought %s %s\n", $args->{count}, $item->{name}); + $selfBuyerItemList[$i]->{amount} = $item->{amount} - $args->{count}; + } + } + } +} + +sub buyer_found { + my($self, $args) = @_; + my $ID = $args->{ID}; + + if (!$buyerLists{$ID} || !%{$buyerLists{$ID}}) { + binAdd(\@buyerListsID, $ID); + Plugins::callHook('packet_buyer', {ID => $ID}); + } + $buyerLists{$ID}{title} = bytesToString($args->{title}); + $buyerLists{$ID}{id} = $ID; +} + +sub buyer_lost { + my ($self, $args) = @_; + + my $ID = $args->{ID}; + binRemove(\@buyerListsID, $ID); + delete $buyerLists{$ID}; +} + +sub buying_buy_fail { + my ($self, $args) = @_; + if ($args->{result} == 3) { + error T("Failed to buying (insufficient zeny).\n"); + } elsif ($args->{result} == 4) { + $buyershopstarted = 0; + Plugins::callHook('buyer_shop_closed'); + message T("Buying up complete.\n"); + } else { + error TF("Failed to buying (unknown error: %s).\n", $args->{result}); + } +} + +use constant { + TYPE_BOXITEM => 0x0, + TYPE_MONSTER_ITEM => 0x1, +}; + +sub special_item_obtain { + my ($self, $args) = @_; + + my $item_name = itemNameSimple($args->{nameID}); + my $holder = bytesToString($args->{holder}); + my ($source_item_id, $source_name, $msg); + + stripLanguageCode(\$holder); + if ($args->{type} == TYPE_BOXITEM) { + my $c = unpack 'c', $args->{etc}; + my $unpack = ($c == 2) ? 'c/v' : 'c/V'; + @{$args}{qw(box_nameID)} = unpack $unpack, $args->{etc}; + + my $box_item_name = itemNameSimple($args->{box_nameID}); + $source_name = $box_item_name; + $source_item_id = $args->{box_nameID}; + + if ($msgTable[1629]) { + $msg = sprintf($msgTable[1629], $holder, $box_item_name, $item_name)."\n"; + } else { + $msg = TF("%s has got %s from %s.\n", $holder, $item_name, $box_item_name); + } + + chatLog("GM", $msg) if ($config{logSystemChat}); + message $msg, 'schat'; + + } elsif ($args->{type} == TYPE_MONSTER_ITEM) { + @{$args}{qw(len monster_name)} = unpack 'c Z*', $args->{etc}; + my $monster_name = bytesToString($args->{monster_name}); + $source_name = $monster_name; + stripLanguageCode(\$monster_name); + chatLog("GM", "$holder has got $item_name from $monster_name\n") if ($config{logSystemChat}); + $msg = TF("%s has got %s from %s.\n", $holder, $item_name, $monster_name); + message $msg, 'schat'; + + } else { + $msg = TF("%s has got %s (from Unknown type %d).\n", $holder, $item_name, $args->{type}); + warning $msg, 'schat'; + } + + Plugins::callHook('packet_special_item_obtain', { + ObtainType => $args->{type}, + ItemName => $item_name, + ItemID => $args->{nameID}, + Holder => $holder, + SourceItemID => $source_item_id, # ItemID if type (0) TYPE_BOXITEM + SourceName => $source_name, # Monster if type (1) TYPE_MONSTER_ITEM + Msg => $msg, + }); +} + +sub inventory_item_favorite { + my ($self, $args) = @_; + my $item = $char->inventory->getByID($args->{ID}); + if ($args->{flag}) { + message TF("Inventory Item removed from favorite tab: %s\n", $item), "storage"; + } else { + message TF("Inventory Item move to favorite tab: %s\n", $item), "storage"; + } +} + +sub private_message_sent { + my ($self, $args) = @_; + if ($args->{type} == 0) { + message TF("(To %s) : %s\n", $lastpm[0]{'user'}, $lastpm[0]{'msg'}), "pm/sent"; + chatLog("pm", "(To: $lastpm[0]{user}) : $lastpm[0]{msg}\n") if ($config{'logPrivateChat'}); + + Plugins::callHook('packet_sentPM', { + to => $lastpm[0]{user}, + msg => $lastpm[0]{msg} + }); + + } elsif ($args->{type} == 1) { + warning TF("%s is not online\n", $lastpm[0]{user}); + } elsif ($args->{type} == 2) { + warning TF("Player %s ignored your message\n", $lastpm[0]{user}); + } else { + warning TF("Player %s doesn't want to receive messages\n", $lastpm[0]{user}); + } + shift @lastpm; +} + +sub vender_buy_fail { + my ($self, $args) = @_; + + if ($args->{fail} == 1) { + error TF("Failed to buy %s of item #%s from vender (insufficient zeny) (error code %s).\n", $args->{amount}, $args->{ID}, $args->{fail}); + } elsif ($args->{fail} == 2) { + error TF("Failed to buy %s of item #%s from vender (overweight) (error code %s).\n", $args->{amount}, $args->{ID}, $args->{fail}); + } elsif ($args->{fail} == 4) { + error TF("Failed to buy %s of item #%s from vender (requested to purchase more than vender had in stock) (error code %s).\n", $args->{amount}, $args->{ID}, $args->{fail}); + } elsif ($args->{fail} == 6) { + error TF("Failed to buy %s of item #%s from vender (vender refreshed shop before purchase request) (error code %s).\n", $args->{amount}, $args->{ID}, $args->{fail}); + } elsif ($args->{fail} == 8) { + error TF("Failed to buy %s of item #%s from vender (vender would go over max zeny with the purchase) (error code %s).\n", $args->{amount}, $args->{ID}, $args->{fail}); + } else { + error TF("Failed to buy %s of item #%s from vender (unknown error code %s).\n", $args->{amount}, $args->{ID}, $args->{fail}); + } +} + +# Receive list of items from cash shop NPC +# +# ['cash_dealer', 'v V a*', [qw(len cash_points item_list)]] +# ['cash_dealer', 'v V2 a*', [qw(len cash_points kafra_points item_list)]] +sub cash_dealer { + my ($self, $args) = @_; + + undef %talk; + $ai_v{'npc_talk'}{talk} = 'cash'; + # continue talk sequence now + $ai_v{'npc_talk'}{time} = time; + + # Parse item_list => ['V2 C v', [qw(price price_discount type nameid)]] + $cashList->clear; + @{$args->{items}} = map { my %item; @item{qw(price price_discount type nameid)} = unpack 'V2 C v', $_; \%item } unpack '(a11)*', $args->{item_list}; + + # Just keep cash_points and kafra_points locally not as $char->{cashpoint}, $cashShop{points}->{cash}, $cashShop{points}->{kafra} + # private servers can add custom currency that may overwrite the cash & points from cash shop + message TF("------------CashList (Cash Point: %-5d. Kafra Points: %-d)-------------\n" . + "# Name Type Price\n", $args->{cash_points}, $args->{kafra_points}), "list"; + + foreach my $curr_item (@{$args->{items}}) { + my $item = Actor::Item->new; + + @$item{qw(price type nameID)} = ($curr_item->{price}, $curr_item->{type}, $curr_item->{nameid}); + $item->{ID} = $cashList->size; + $item->{name} = itemName($item); + $cashList->add($item); + + debug "Item added to Store: $item->{name} - $item->{price}z\n", "parseMsg", 2; + message(swrite( + "@<<< @<<<<<<<<<<<<<<<<<<<<<< @<<<<<<<<<<<<< @>>>>>>>p", + [$item->{ID}, $item->{name}, $itemTypes_lut{$item->{type}}, $curr_item->{price_discount}]), + "list"); + } + message("-----------------------------------------------------\n", "list"); +} + +## +# 096D <size>.W { <index>.W }* +# @author [Cydh] +## +sub merge_item_open { + my ($self, $args) = @_; + $mergeItemList = {}; + debug "Enable to merge ".(scalar @{$args->{list}})." items\n"; + # Grouping items by ItemID, easier to merge by user later + foreach (@{$args->{list}}) { + my $item = $char->inventory->getByID($_->{ID}); + if (!defined $mergeItemList->{$item->{nameID}}) { + $mergeItemList->{$item->{nameID}}->{name} = $item->{name}; + @{$mergeItemList->{$item->{nameID}}->{list}} = (); + } + push @{$mergeItemList->{$item->{nameID}}->{list}},{ ID => $_->{ID}, info => $item }; + debug "- ".(unpack "v",$_->{ID}).": ".$item->{name}." (".$item->{binID}.") x ".$item->{amount}."\n"; + } + message TF("Received %d items that can be merged. Use 'merge' to continue\n", (scalar @{$args->{list}})), "info"; +} + +sub parse_merge_item_open { + my ($self, $args) = @_; + @{$args->{list}} = map { { ID => $_ } } unpack '(a2)*', $args->{itemList}; # received index from server is +2 +} + +## +# 096F <index>.W <total>.W <result>.B +# @author [Cydh] +## +sub merge_item_result { + my ($self, $args) = @_; + if ($args->{result} == 0) { + # now update inventory data + my $item = $char->inventory->getByID($args->{itemIndex}); + message T("Items were merged successfully!\n"), "info"; + if ($item) { + my $oldAmount = $item->{amount}; + $item->{amount} = $args->{total}; + message TF("Updated amount of item %s (%d): %d -> %d\n", $item->{name}, $item->{binID}, $oldAmount, $item->{amount}); + } else { + error TF("Item was moved during merging process. itemIndex: %d. New amount: %d\n", $args->{index}, $args->{total}); + } + } elsif ($args->{result} == 1) { + error T("Items cannot be merged.\n"); + } elsif ($args->{result} == 2) { + error T("The amount of merged item will be exceed stack limit.\n"); + } else { + error TF("An error occured to merge item. Error:%d\n", $args->{result}); + } + debug "Merge item result: itemIndex:$args->{index} total:$args->{total} result:$args->{result}\n"; +} + +sub parse_merge_item_result { + my ($self, $args) = @_; + $args->{index} = (unpack "(a2)", $args->{itemIndex})-2; +} + +sub memo_success { + my ($self, $args) = @_; + if ($args->{fail}) { + warning T("Memo Failed\n"); + Plugins::callHook('memo_fail', {field => $field->baseName}); + } else { + message T("Memo Succeeded\n"), "success"; + Plugins::callHook('memo_success', {field => $field->baseName}); + } +} + +sub change_to_constate25 { + $net->setState(2.5); + undef $accountID; +} + +# TODO do something with sourceID, targetID? -> tech: maybe your spouses adopt_request will also display this message for you. +sub adopt_request { + my ($self, $args) = @_; + message TF("%s wishes to adopt you. Do you accept?\n", $args->{name}), "info"; +} + +# Updates the fame rank points for the given ranking. +# 097E <RankingType>.W <point>.L <TotalPoint>.L (ZC_UPDATE_RANKING_POINT) +# RankingType: +# 0 = Blacksmith +# 1 = Alchemist +# 2 = Taekwon +sub rank_points { + my ( $self, $args ) = @_; + + $self->blacksmith_points( $args ) if $args->{type} == 0; + $self->alchemist_point( $args ) if $args->{type} == 1; + $self->taekwon_rank( { rank => $args->{total} } ) if $args->{type} == 2; + message "Unknown rank type %s.\n", $args->{type} if $args->{type} > 2; +} + +# Updates the fame rank points for the Blacksmith ranking. +# 021B <points>.L <total points>.L (ZC_BLACKSMITH_POINT) +sub blacksmith_points { + my ($self, $args) = @_; + message TF("[POINT] Blacksmith Ranking Point is increasing by %s. Now, The total is %s points.\n", $args->{points}, $args->{total}, "list"); +} + +# Updates the fame rank points for the Alchemist ranking. +# 021C <points>.L <total points>.L (ZC_ALCHEMIST_POINT) +sub alchemist_point { + my ($self, $args) = @_; + message TF("[POINT] Alchemist Ranking Point is increasing by %s. Now, The total is %s points.\n", $args->{points}, $args->{total}, "list"); +} + +sub area_spell_disappears { + my ($self, $args) = @_; + # The area effect spell with ID dissappears + my $ID = $args->{ID}; + my $spell = $spells{$ID}; + debug "Area effect ".getSpellName($spell->{type})." ($spell->{binID}) from ".getActorName($spell->{sourceID})." disappeared from ($spell->{pos}{x}, $spell->{pos}{y})\n", "skill", 2; + delete $spells{$ID}; + binRemove(\@spellsID, $ID); +} + +sub arrow_none { + my ($self, $args) = @_; + + my $type = $args->{type}; + if ($type == 0) { + delete $char->{'arrow'}; + if ($config{'dcOnEmptyArrow'}) { + error T("Auto disconnecting on EmptyArrow!\n"); + chatLog("k", T("*** Your Arrows is ended, auto disconnect! ***\n")); + $messageSender->sendQuit(); + quit(); + } else { + error T("Please equip arrow first.\n"); + } + } elsif ($type == 1) { + debug "You can't Attack or use Skills because your Weight Limit has been exceeded.\n"; + } elsif ($type == 2) { + debug "You can't use Skills because Weight Limit has been exceeded.\n"; + } elsif ($type == 3) { + debug "Arrow equipped\n"; + } +} + +sub arrowcraft_list { + my ($self, $args) = @_; + + my $msg = $args->{RAW_MSG}; + my $msg_size = $args->{RAW_MSG_SIZE}; + $char->{selected_craft} = 0; + + undef @arrowCraftID; + for (my $i = 4; $i < $msg_size; $i += 2) { + my $ID = unpack("v", substr($msg, $i, 2)); + my $item = $char->inventory->getByNameID($ID); + $char->{last_skill_used} = 2027 if ($config{autoPoison} && $item->{name} eq $config{autoPoison}); + binAdd(\@arrowCraftID, $item->{binID}); + } + + if ($char->{last_skill_used} == 2027) { # GC_POISONINGWEAPON + message T("Received Possible Poison List - type 'poison'\n"); + if ($config{autoPoison}) { + my $item = $char->inventory->getByName($config{autoPoison}); + if ($item) { + $messageSender->sendArrowCraft($item->{nameID}); + $char->{selected_craft} = 1; + } else { + error TF("Configured autoPoison (%s) not available.\n", $config{autoSpell}); + } + } else { + warning T("Configure autoPoison to automatically select skill for Auto Spell.\n"), 'hint'; + } + } else { + message T("Received Possible Item List - type 'arrowcraft' or 'poison'\n"); + } +} + +# Notifies client of a character parameter change. +# 013A <atk range>.W (ZC_ATTACK_RANGE) +sub attack_range { + my ($self, $args) = @_; + + my $type = $args->{type}; + debug "Your attack range is: $type\n"; + return unless changeToInGameState(); + + $char->{attack_range} = $type; + if ($config{attackDistanceAuto}) { + if($config{attackDistance} > $type) { # decrease attack range if necessary + configModify('attackDistance', $type, 1); + message TF("Autodetected attackDistance = %s\n", $config{attackDistance}), "success"; + } + if ($config{attackMaxDistance} != $type) { # set max distance using information coming from the server + configModify('attackMaxDistance', $type, 1) if ($config{attackMaxDistance} != $type); + message TF("Autodetected attackMaxDistance = %s\n", $config{attackMaxDistance}), "success"; + } + } +} + +sub auction_my_sell_stop { + my ($self, $args) = @_; + my $flag = $args->{flag}; + + if ($flag == 0) { + message T("You have ended the auction.\n"), "info"; + } elsif ($flag == 1) { + message T("You cannot end the auction.\n"), "info"; + } elsif ($flag == 2) { + message T("Bid number is incorrect.\n"), "info"; + } else { + warning TF("Unknown results in %s (flag: %s)\n", $self->{packet_list}{$args->{switch}}->[0], $args->{flag}); + } +} + +sub auction_windows { + my ($self, $args) = @_; + if ($args->{flag}) { + message T("Auction window is now closed.\n"), "info"; + } + else { + message T("Auction window is now opened.\n"), "info"; + } +} + +sub auction_add_item { + my ($self, $args) = @_; + if ($args->{fail}) { + message TF("Failed (note: usable items can't be auctioned) to add item with index: %s.\n", $args->{ID}), "info"; + } + else { + message TF("Succeeded to add item with index: %s.\n", $args->{ID}), "info"; + } +} + +sub premium_rates_info { + my ($self, $args) = @_; + message TF("Premium rates: exp %+i%%, death %+i%%, drop %+i%%.\n", $args->{exp}, $args->{death}, $args->{drop}), "info"; +} + +# Transmit personal information to player. (rates) +# 08CB <packet len>.W <exp>.W <death>.W <drop>.W <DETAIL_EXP_INFO>7B (ZC_PERSONAL_INFOMATION) +# <InfoType>.B <Exp>.W <Death>.W <Drop>.W (DETAIL_EXP_INFO 08CB) +# 097B <packet len>.W <exp>.L <death>.L <drop>.L <DETAIL_EXP_INFO>13B (ZC_PERSONAL_INFOMATION2) +# 0981 <packet len>.W <exp>.W <death>.W <drop>.W <activity rate>.W <DETAIL_EXP_INFO>13B (ZC_PERSONAL_INFOMATION_CHN) +# <InfoType>.B <Exp>.L <Death>.L <Drop>.L (DETAIL_EXP_INFO 097B|0981) +sub rates_info2 { + my ($self, $args) = @_; + + my $msg = $args->{RAW_MSG}; + my $msg_size = $args->{RAW_MSG_SIZE}; + my $header_pack = 'v V3'; + my $header_len = ((length pack $header_pack) + 2); + + my $detail_pack = 'C l3'; + my $detail_len = length pack $detail_pack; + + my %rates = ( + exp => { total => $args->{exp}/1000 }, # Value to Percentage => /100 + death => { total => $args->{death}/1000 }, # 1 d.p. => /10 + drop => { total => $args->{drop}/1000 }, + ); + + # get details + for (my $i = $header_len; $i < $args->{RAW_MSG_SIZE}; $i += $detail_len) { + + my ($type, $exp, $death, $drop) = unpack($detail_pack, substr($msg, $i, $detail_len)); + + $rates{exp}{$type} = $exp/1000; + $rates{death}{$type} = $death/1000; + $rates{drop}{$type} = $drop/1000; + } + + # we have 4 kinds of detail: + # $rates{exp or drop or death}{DETAIL_KIND} + # 0 = base server exp (?) + # 1 = premium acc additional exp + # 2 = server additional exp + # 3 = not sure, maybe it's for "extra exp" events? never seen this using the official client (bRO) + message T("=========================== Server Infos ===========================\n"), "info"; + message TF("EXP Rates: %s%% (Base %s%% + Premium %s%% + Server %s%% + Plus %s%%) \n", $rates{exp}{total}, $rates{exp}{0}+100, $rates{exp}{1}, $rates{exp}{2}, $rates{exp}{3}), "info"; + message TF("Drop Rates: %s%% (Base %s%% + Premium %s%% + Server %s%% + Plus %s%%) \n", $rates{drop}{total}, $rates{drop}{0}+100, $rates{drop}{1}, $rates{drop}{2}, $rates{drop}{3}), "info"; + message TF("Death Penalty: %s%% (Base %s%% + Premium %s%% + Server %s%% + Plus %s%%) \n", $rates{death}{total}, $rates{death}{0}+100, $rates{death}{1}, $rates{death}{2}, $rates{death}{3}), "info"; + message "=====================================================================\n", "info"; +} + +sub auction_result { + my ($self, $args) = @_; + my $flag = $args->{flag}; + + if ($flag == 0) { + message T("You have failed to bid into the auction.\n"), "info"; + } elsif ($flag == 1) { + message T("You have successfully bid in the auction.\n"), "info"; + } elsif ($flag == 2) { + message T("The auction has been canceled.\n"), "info"; + } elsif ($flag == 3) { + message T("An auction with at least one bidder cannot be canceled.\n"), "info"; + } elsif ($flag == 4) { + message T("You cannot register more than 5 items in an auction at a time.\n"), "info"; + } elsif ($flag == 5) { + message T("You do not have enough Zeny to pay the Auction Fee.\n"), "info"; + } elsif ($flag == 6) { + message T("You have won the auction.\n"), "info"; + } elsif ($flag == 7) { + message T("You have failed to win the auction.\n"), "info"; + } elsif ($flag == 8) { + message T("You do not have enough Zeny.\n"), "info"; + } elsif ($flag == 9) { + message T("You cannot place more than 5 bids at a time.\n"), "info"; + } else { + warning TF("Unknown results in %s (flag: %s)\n", $self->{packet_list}{$args->{switch}}->[0], $args->{flag}); + } +} + +# 02DC +# TODO +sub battleground_message { + my ($self, $args) = @_; + debug $self->{packet_list}{$args->{switch}}->[0] . " " . join(', ', @{$args}{@{$self->{packet_list}{$args->{switch}}->[2]}}) . "\n"; +} + +# 02DD +# TODO +sub battleground_emblem { + my ($self, $args) = @_; + debug $self->{packet_list}{$args->{switch}}->[0] . " " . join(', ', @{$args}{@{$self->{packet_list}{$args->{switch}}->[2]}}) . "\n"; +} + +# 0152 +# TODO +sub guild_emblem { + my ($self, $args) = @_; + debug $self->{packet_list}{$args->{switch}}->[0] . " " . join(', ', @{$args}{@{$self->{packet_list}{$args->{switch}}->[2]}}) . "\n"; +} + +# 01B4 +# TODO +sub guild_emblem_update { + my ($self, $args) = @_; + debug $self->{packet_list}{$args->{switch}}->[0] . " " . join(', ', @{$args}{@{$self->{packet_list}{$args->{switch}}->[2]}}) . "\n"; +} + +# 0B47 +# TODO +sub char_emblem_update { + my ($self, $args) = @_; + debug $self->{packet_list}{$args->{switch}}->[0] . " " . join(', ', @{$args}{@{$self->{packet_list}{$args->{switch}}->[2]}}) . "\n"; +} + +# 0174 +# TODO +sub guild_position_changed { + my ($self, $args) = @_; + debug $self->{packet_list}{$args->{switch}}->[0] . " " . join(', ', @{$args}{@{$self->{packet_list}{$args->{switch}}->[2]}}) . "\n"; +} + +# 0AFD +# TODO +sub guild_position { + my ($self, $args) = @_; + debug $self->{packet_list}{$args->{switch}}->[0] . " " . join(', ', @{$args}{@{$self->{packet_list}{$args->{switch}}->[2]}}) . "\n"; +} + +# 0184 +# TODO +sub guild_unally { + my ($self, $args) = @_; + debug $self->{packet_list}{$args->{switch}}->[0] . " " . join(', ', @{$args}{@{$self->{packet_list}{$args->{switch}}->[2]}}) . "\n"; +} + +# 0181 +# TODO +sub guild_opposition_result { + my ($self, $args) = @_; + debug $self->{packet_list}{$args->{switch}}->[0] . " " . join(', ', @{$args}{@{$self->{packet_list}{$args->{switch}}->[2]}}) . "\n"; +} + +# 0185 +# TODO: this packet doesn't exist in eA +sub guild_alliance_added { + my ($self, $args) = @_; + debug $self->{packet_list}{$args->{switch}}->[0] . " " . join(', ', @{$args}{@{$self->{packet_list}{$args->{switch}}->[2]}}) . "\n"; +} + +# 0192 +# TODO: add actual functionality, maybe alter field? +sub map_change_cell { + my ($self, $args) = @_; + debug "Cell on ($args->{x}, $args->{y}) has been changed to $args->{type} on $args->{map_name}\n", "info"; +} + +# 01D1 +# TODO: the actual status is sent to us in opt3 +sub blade_stop { + my ($self, $args) = @_; + if ($args->{active} == 0) { + message TF("Blade Stop by %s on %s is deactivated.\n", Actor::get($args->{sourceID})->nameString(), Actor::get($args->{targetID})->nameString()), "info"; + } elsif ($args->{active} == 1) { + message TF("Blade Stop by %s on %s is active.\n", Actor::get($args->{sourceID})->nameString(), Actor::get($args->{targetID})->nameString()), "info"; + } +} + +sub divorced { + my ($self, $args) = @_; + message TF("%s and %s have divorced from each other.\n", $char->{name}, $args->{name}), "info"; # is it $char->{name} or is this packet also used for other players? +} + +sub hack_shield_alarm { + error T("Error: You have been forced to disconnect by a Hack Shield.\n Please check Poseidon.\n"), "connection"; + Commands::run('relog 100000000'); +} + +sub talkie_box { + my ($self, $args) = @_; + message TF("%s's talkie box message: %s.\n", Actor::get($args->{ID})->nameString(), $args->{message}), "info"; +} + +sub manner_message { + my ($self, $args) = @_; + if ($args->{flag} == 0) { + message T("A manner point has been successfully aligned.\n"), "info"; + } elsif ($args->{flag} == 3) { + message T("Chat Block has been applied by GM due to your ill-mannerous action.\n"), "info"; + } elsif ($args->{flag} == 4) { + message T("Automated Chat Block has been applied due to Anti-Spam System.\n"), "info"; + } elsif ($args->{flag} == 5) { + message T("You got a good point.\n"), "info"; + } else { + warning TF("Unknown results in %s (flag: %s)\n", $self->{packet_list}{$args->{switch}}->[0], $args->{flag}); + } +} + +# 02CB +# TODO +# Required to start the instancing information window on Client +# This window re-appear each "refresh" of client automatically until 02CD is send to client. +sub instance_window_start { + my ($self, $args) = @_; + debug $self->{packet_list}{$args->{switch}}->[0] . " " . join(', ', @{$args}{@{$self->{packet_list}{$args->{switch}}->[2]}}) . "\n"; +} + +# 02CC +# TODO +# To announce Instancing queue creation if no maps available +sub instance_window_queue { + my ($self, $args) = @_; + debug $self->{packet_list}{$args->{switch}}->[0] . " " . join(', ', @{$args}{@{$self->{packet_list}{$args->{switch}}->[2]}}) . "\n"; +} + +# 02CD +# TODO +sub instance_window_join { + my ($self, $args) = @_; + debug $self->{packet_list}{$args->{switch}}->[0] . " " . join(', ', @{$args}{@{$self->{packet_list}{$args->{switch}}->[2]}}) . "\n"; + + Plugins::callHook('instance_ready'); +} + +# 02CE +#0 = "The Memorial Dungeon reservation has been canceled/updated." +# Re-innit Window, in some rare cases. +#1 = "The Memorial Dungeon expired; it has been destroyed." +#2 = "The Memorial Dungeon's entry time limit expired; it has been destroyed." +#3 = "The Memorial Dungeon has been removed." +#4 = "A system error has occurred in the Memorial Dungeon. Please relog in to the game to continue playing." +# Just remove the window, maybe party/guild leave. +# TODO: test if correct message displays, no type == 0 ? +sub instance_window_leave { + my ($self, $args) = @_; + + if ($args->{flag} == 0) { # TYPE_NOTIFY = 0x0; Ihis one will pop up Memory Dungeon Window + debug T("Received Memory Dungeon reservation update\n"); + } elsif ($args->{flag} == 1) { # TYPE_DESTROY_LIVE_TIMEOUT = 0x1 + message T("The Memorial Dungeon expired it has been destroyed.\n"), "info"; + } elsif ($args->{flag} == 2) { # TYPE_DESTROY_ENTER_TIMEOUT = 0x2 + message T("The Memorial Dungeon's entry time limit expired it has been destroyed.\n"), "info"; + } elsif ($args->{flag} == 3) { # TYPE_DESTROY_USER_REQUEST = 0x3 + message T("The Memorial Dungeon has been removed.\n"), "info"; + } elsif ($args->{flag} == 4) { # TYPE_CREATE_FAIL = 0x4 + message T("The instance windows has been removed, possibly due to party/guild leave.\n"), "info"; + } else { + warning TF("Unknown results in %s (flag: %s)\n", $self->{packet_list}{$args->{switch}}->[0], $args->{flag}); + } +} + +sub card_merge_list { + my ($self, $args) = @_; + + # You just requested a list of possible items to merge a card into + # The RO client does this when you double click a card + my $msg = $args->{RAW_MSG}; + my ($len) = unpack("x2 v", $msg); + + my $index; + for (my $i = 4; $i < $len; $i += 2) { + $index = unpack("a2", substr($msg, $i, 2)); + my $item = $char->inventory->getByID($index); + binAdd(\@cardMergeItemsID, $item->{binID}); + } + + Commands::run('card mergelist'); +} + +sub card_merge_status { + my ($self, $args) = @_; + + # something about successful compound? + my $item_index = $args->{item_index}; + my $card_index = $args->{card_index}; + my $fail = $args->{fail}; + + if ($fail) { + message T("Card merging failed\n"); + } else { + my $item = $char->inventory->getByID($item_index); + my $card = $char->inventory->getByID($card_index); + message TF("%s has been successfully merged into %s\n", + $card->{name}, $item->{name}), "success"; + + # Remove one of the card + inventoryItemRemoved($card->{binID}, 1); + + # Rename the slotted item now + # FIXME: this is unoptimized + use bytes; + no encoding 'utf8'; + my $newcards = ''; + my $addedcard; + for (my $i = 0; $i < 4; $i++) { + my $cardData = substr($item->{cards}, $i * 2, 2); + if (unpack("v", $cardData)) { + $newcards .= $cardData; + } elsif (!$addedcard) { + $newcards .= pack("v", $card->{nameID}); + $addedcard = 1; + } else { + $newcards .= pack("v", 0); + } + } + $item->{cards} = $newcards; + $item->setName(itemName($item)); + } + + undef @cardMergeItemsID; + undef $cardMergeIndex; +} + +sub combo_delay { + my ($self, $args) = @_; + + $char->{combo_packet} = ($args->{delay}); #* 15) / 100000; + # How was the above formula derived? I think it's better that the manipulation be + # done in functions.pl (or whatever sub that handles this) instead of here. + + $args->{actor} = Actor::get($args->{ID}); + my $verb = $args->{actor}->verb('have', 'has'); + debug "$args->{actor} $verb combo delay $args->{delay}\n", "parseMsg_comboDelay"; +} + +# 0294 +# TODO -> maybe add table file? +sub book_read { + my ($self, $args) = @_; + debug "Reading book: $args->{bookID} page: $args->{page}\n", "info"; +} + +# TODO can we use itemName($actor)? -> tech: don't think so because it seems that this packet is received before the inventory list +sub rental_time { + my ($self, $args) = @_; + message TF("The '%s' item will disappear in %d minutes.\n", itemNameSimple($args->{nameID}), $args->{seconds}/60), "info"; +} + +# 0289 +# TODO +sub cash_buy_fail { + my ($self, $args) = @_; + debug "cash_buy_fail $args->{cash_points} $args->{kafra_points} $args->{fail}\n"; +} + +# Notifies the client about the result of a request to equip an item. +# 00AA <index>.W <equip location>.W <result>.B (ZC_REQ_WEAR_EQUIP_ACK) +# 00AA <index>.W <equip location>.W <view id>.W <result>.B (PACKETVER >= 20100629) +# 08D0 <index>.W <equip location>.W <view id>.W <result>.B (ZC_REQ_WEAR_EQUIP_ACK2) +# 0999 <index>.W <equip location>.L <view id>.W <result>.B (ZC_ACK_WEAR_EQUIP_V5) +# @ok: //inversed forv2 v5 +# 0 = failure +# 1 = success +# 2 = failure due to low level +sub equip_item { + my ($self, $args) = @_; + my $item = $char->inventory->getByID($args->{ID}); + if ((!$args->{success} && $args->{switch} eq "00AA") || ($args->{success} && $args->{switch} eq "0999")) { + message TF("You can't put on %s (%d)\n", $item->{name}, $item->{binID}); + } else { + $item->{equipped} = $args->{type}; + + if ($args->{type} == 10 || $args->{type} == 32768) { + $char->{equipment}{arrow} = $item; + } else { + foreach (%equipSlot_rlut) { + if ($_ & $args->{type}) { + next if $_ == 10; # work around Arrow bug + next if $_ == 32768; + $char->{equipment}{$equipSlot_lut{$_}} = $item; + Plugins::callHook('equipped_item', {slot => $equipSlot_lut{$_}, item => $item}); + } + } + } + message TF("You equip %s (%d) - %s (type %s)\n", $item->{name}, $item->{binID}, $equipTypes_lut{$item->{type_equip}}, $args->{type}), 'inventory'; + } + $ai_v{temp}{waitForEquip}-- if $ai_v{temp}{waitForEquip}; +} + +# Acknowledgement for adding an equip to the equip switch window +# 0A98 <index>.W <position.>.L <flag>.L <= 20170502 +# 0A98 <index>.W <position.>.L <flag>.W +sub equip_item_switch { + my ($self, $args) = @_; + my $item = $char->inventory->getByID($args->{ID}); + if ( $args->{success} == 1 || $args->{success} == 2 ) { + message TF("[Equip Switch] You can't put on %s (%d)\n", $item->{name}, $item->{binID}); + } else { + $item->{eqswitch} = $args->{type}; + + if ($args->{type} == 10 || $args->{type} == 32768) { + $char->{equipment}{arrow} = $item; + } else { + foreach (%equipSlot_rlut) { + if ($_ & $args->{type}) { + next if $_ == 10; # work around Arrow bug + next if $_ == 32768; + $char->{eqswitch}{$equipSlot_lut{$_}} = $item; + Plugins::callHook('equipped_item_sw', {slot => $equipSlot_lut{$_}, item => $item}); + } + } + } + + message TF("[Equip Switch] You equip %s (%d) - %s (type %s)\n", $item->{name}, $item->{binID}, $equipTypes_lut{$item->{type_equip}}, $args->{type}), 'inventory'; + } + $ai_v{temp}{waitForEquip}-- if $ai_v{temp}{waitForEquip}; +} + + +# Acknowledgement packet for the full equip switch +# 0A9D <failed>.W +sub equip_switch_run_res { + my ($self, $args) = @_; + if ($args->{success}) { + message TF("[Equip Switch] Fail !\n"), "info"; + } else { + message TF("[Equip Switch] Success !\n"), "info"; + } +} + +# Set the full list of items in the equip switch window +# 0A9B <length>.W { <index>.W <position>.L }* +sub equip_switch_log { + my ($self, $args) = @_; + for (my $i = 0; $i < length($args->{log}); $i+= 6) { + my ($index, $position) = unpack('a2 V', substr($args->{log}, $i, 6)); + my $item = $char->inventory->getByID($index); + $char->{eqswitch}{$equipSlot_lut{$position}} = $item; + } +} + +# 02EF +# TODO +sub font { + my ($self, $args) = @_; + debug "Account: $args->{ID} is using fontID: $args->{fontID}\n", "info"; +} + +sub initialize_message_id_encryption { + my ($self, $args) = @_; + if ($masterServer->{messageIDEncryption} ne '0') { + $messageSender->sendMessageIDEncryptionInitialized(); + + my @c; + my $shtmp = $args->{param1}; + for (my $i = 8; $i > 0; $i--) { + $c[$i] = $shtmp & 0x0F; + $shtmp >>= 4; + } + my $w = ($c[6]<<12) + ($c[4]<<8) + ($c[7]<<4) + $c[1]; + $enc_val1 = ($c[2]<<12) + ($c[3]<<8) + ($c[5]<<4) + $c[8]; + $enc_val2 = (((($enc_val1 ^ 0x0000F3AC) + $w) << 16) | (($enc_val1 ^ 0x000049DF) + $w)) ^ $args->{param2}; + } +} + +sub mail_delete { + my ($self, $args) = @_; + if ($args->{fail}) { + message TF("Failed to delete mail with ID: %s.\n", $args->{mailID}), "info"; + } + else { + message TF("Succeeded to delete mail with ID: %s.\n", $args->{mailID}), "info"; + } +} + +sub mail_window { + my ($self, $args) = @_; + if ($args->{flag}) { + message T("Mail window is now closed.\n"), "info"; + } + else { + message T("Mail window is now opened.\n"), "info"; + } +} + +sub mail_return { + my ($self, $args) = @_; + ($args->{fail}) ? + error TF("The mail with ID: %s does not exist.\n", $args->{mailID}), "info" : + message TF("The mail with ID: %s is returned to the sender.\n", $args->{mailID}), "info"; +} + +sub mail_read { + my ($self, $args) = @_; + + my $item = {}; + $item->{nameID} = $args->{nameID}; + $item->{upgrade} = $args->{upgrade}; + $item->{cards} = $args->{cards}; + $item->{broken} = $args->{broken}; + $item->{name} = itemName($item); + + my $msg; + $msg .= center(" " . T("Mail") . " ", 119, '-') . "\n"; + $msg .= swrite(TF("Title: \@%s Sender: \@%s", ('<'x39), ('<'x24)), + [bytesToString($args->{title}), bytesToString($args->{sender})]); + $msg .= TF("Message: %s\n", bytesToString($args->{message})); + $msg .= sprintf("%s\n", ('-'x119)); + $msg .= TF( "Item: %s %s\n" . + "Zeny: %sz\n", + $item->{name}, ($args->{amount}) ? "x " . $args->{amount} : "", formatNumber($args->{zeny})); + $msg .= sprintf("%s\n", ('-'x119)); + + message($msg, "info"); +} + +sub mail_refreshinbox { + my ($self, $args) = @_; + + my $old_count = defined $mailList ? scalar(@$mailList) : 0; + undef $mailList; + my $count = $args->{count}; + + if (!$count) { + message T("There is no mail in your inbox.\n"), "info"; + return; + } + + return if ($old_count == $count); + + message TF("You've got %s mail in your Mailbox.\n", $count), "info"; + my $msg; + $msg .= center(" " . T("Inbox") . " ", 86, '-') . "\n"; + # truncating the title from 39 to 34, the user will be able to read the full title when reading the mail + # truncating the date with precision of minutes and leave year out + + $msg .= swrite(sprintf("\@> \@ \@%s \@%s \@%s", ('<'x34), ('<'x24), ('<'x19)), + ["#", T("R"), T("Title"), T("Sender"), T("Date")]); + $msg .= sprintf("%s\n", ('-'x86)); + + my $j = 0; + for (my $i = 8; $i < 8 + $count * 73; $i+=73) { + ($mailList->[$j]->{mailID}, + $mailList->[$j]->{title}, + $mailList->[$j]->{read}, + $mailList->[$j]->{sender}, + $mailList->[$j]->{timestamp}) = unpack('V Z40 C Z24 V', substr($args->{RAW_MSG}, $i, 73)); + + $mailList->[$j]->{title} = bytesToString($mailList->[$j]->{title}); + $mailList->[$j]->{sender} = bytesToString($mailList->[$j]->{sender}); + + $msg .= swrite(sprintf("\@> \@ \@%s \@%s \@%s", ('<'x34), ('<'x24), ('<'x19)), + [$j, $mailList->[$j]->{read}, $mailList->[$j]->{title}, $mailList->[$j]->{sender}, getFormattedDate(int($mailList->[$j]->{timestamp}))]); + $j++; + } + + $msg .= ("%s\n", ('-'x86)); + message($msg . "\n", "list"); +} + +sub mail_getattachment { + my ($self, $args) = @_; + if (!$args->{fail}) { + message T("Successfully added attachment to inventory.\n"), "info"; + } elsif ($args->{fail} == 2) { + error T("Failed to get the attachment to inventory due to your weight.\n"), "info"; + } else { + error T("Failed to get the attachment to inventory.\n"), "info"; + } +} + +sub mail_setattachment { + my ($self, $args) = @_; + + if ($args->{fail}) { + if (defined $AI::temp::mailAttachAmount) { + undef $AI::temp::mailAttachAmount; + } + message TF("Failed to attach %s.\n", ($args->{ID}) ? T("item: ").$char->inventory->getByID($args->{ID}) : T("zeny")), "info"; + } else { + my $item = $char->inventory->getByID($args->{ID}); + if ($item) { + message TF("Succeeded to attach %s.\n", T("item: ").$char->inventory->getByID($args->{ID})), "info"; + if (defined $AI::temp::mailAttachAmount) { + my $change = min($item->{amount},$AI::temp::mailAttachAmount); + inventoryItemRemoved($item->{binID}, $change); + Plugins::callHook('packet_item_removed', {index => $item->{binID}}); + undef $AI::temp::mailAttachAmount; + } + } else { + message TF("Succeeded to attach %s.\n", T("zeny")), "info"; + if (defined $AI::temp::mailAttachAmount) { + my $change = min($char->{zeny},$AI::temp::mailAttachAmount); + $char->{zeny} = $char->{zeny} - $change; + message TF("You lost %s zeny.\n", formatNumber($change)); + } + } + } +} + +sub mail_send { + my ($self, $args) = @_; + ($args->{fail}) ? + error T("Failed to send mail, the recipient does not exist.\n"), "info" : + message T("Mail sent succesfully.\n"), "info"; +} + +sub mail_new { + my ($self, $args) = @_; + message TF("New mail from sender: %s titled: %s.\n", bytesToString($args->{sender}), bytesToString($args->{title})), "info"; +} + +# Top 10 rank +# 097D <RankingType>.W {<CharName>.24B <point>L}*10 <mypoint>L (ZC_ACK_RANKING) +sub top10 { + my ( $self, $args ) = @_; + + if ( $args->{type} == 0 ) { + $self->top10_blacksmith_rank( { RAW_MSG => substr $args->{RAW_MSG}, 2 } ); + } elsif ( $args->{type} == 1 ) { + $self->top10_alchemist_rank( { RAW_MSG => substr $args->{RAW_MSG}, 2 } ); + } elsif ( $args->{type} == 2 ) { + $self->top10_taekwon_rank( { RAW_MSG => substr $args->{RAW_MSG}, 2 } ); + } elsif ( $args->{type} == 3 ) { + $self->top10_pk_rank( { RAW_MSG => substr $args->{RAW_MSG}, 2 } ); + } else { + message "Unknown top10 type %s.\n", $args->{type}; + } +} + +# Alchemist Top 10 rank +# 021A { <name>.24B }*10 { <point>.L }*10 (ZC_ALCHEMIST_RANK) +sub top10_alchemist_rank { + my ($self, $args) = @_; + + my $textList = bytesToString(top10Listing($args)); + message TF("============= ALCHEMIST RANK ================\n" . + "# Name Points\n". + "%s" . + "=============================================\n", $textList), "list"; +} + +# Blacksmith Top 10 rank +# 0219 { <name>.24B }*10 { <point>.L }*10 (ZC_BLACKSMITH_RANK) +sub top10_blacksmith_rank { + my ($self, $args) = @_; + + my $textList = bytesToString(top10Listing($args)); + message TF("============= BLACKSMITH RANK ===============\n" . + "# Name Points\n". + "%s" . + "=============================================\n", $textList), "list"; +} + +# PK Top 10 rank +# 0238 { <name>.24B }*10 { <point>.L }*10 (ZC_KILLER_RANK) +sub top10_pk_rank { + my ($self, $args) = @_; + + my $textList = bytesToString(top10Listing($args)); + message TF("================ PVP RANK ===================\n" . + "# Name Points\n". + "%s" . + "=============================================\n", $textList), "list"; +} + +# Taekwon Top 10 rank +# 0226 { <name>.24B }*10 { <point>.L }*10 (ZC_TAEKWON_RANK) +sub top10_taekwon_rank { + my ($self, $args) = @_; + + my $textList = bytesToString(top10Listing($args)); + message TF("=============== TAEKWON RANK ================\n" . + "# Name Points\n". + "%s" . + "=============================================\n", $textList), "list"; +} + +# TODO test if we must use ID to know if the packets are meant for us. +# ID is monsterID +sub taekwon_packets { + my ($self, $args) = @_; + my $string = ($args->{value} == 1) ? T("Sun") : ($args->{value} == 2) ? T("Moon") : ($args->{value} == 3) ? T("Stars") : TF("Unknown (%d)", $args->{value}); + if ($args->{flag} == 0) { # Info about Star Gladiator save map: Map registered + message TF("You have now marked: %s as Place of the %s.\n", bytesToString($args->{name}), $string), "info"; + } elsif ($args->{flag} == 1) { # Info about Star Gladiator save map: Information + message TF("%s is marked as Place of the %s.\n", bytesToString($args->{name}), $string), "info"; + } elsif ($args->{flag} == 10) { # Info about Star Gladiator hate mob: Register mob + message TF("You have now marked %s as Target of the %s.\n", bytesToString($args->{name}), $string), "info"; + } elsif ($args->{flag} == 11) { # Info about Star Gladiator hate mob: Information + message TF("%s is marked as Target of the %s.\n", bytesToString($args->{name}), $string); + } elsif ($args->{flag} == 20) { #Info about TaeKwon Do TK_MISSION mob + message TF("[TaeKwon Mission] Target Monster : %s (%d%)"."\n", bytesToString($args->{name}), $args->{value}), "info"; + } elsif ($args->{flag} == 30) { #Feel/Hate reset + message T("Your Hate and Feel targets have been resetted.\n"), "info"; + } else { + warning TF("Unknown results in %s (flag: %s)\n", $self->{packet_list}{$args->{switch}}->[0], $args->{flag}); + } +} + +# Updates the fame rank points for the Taekwon ranking. +# 0224 <points>.L <total points>.L (ZC_TAEKWON_POINT) +sub taekwon_rank { + my ($self, $args) = @_; + message T("TaeKwon Mission Rank : ".$args->{rank}."\n"), "info"; +} + +sub storage_password_request { + my ($self, $args) = @_; + + if ($args->{flag} == 0) { + if ($args->{switch} eq '023E') { + message T("Please enter a new character password:\n"); + } else { + if ($config{storageAuto_password} eq '') { + my $input = $interface->query(T("You've never set a storage password before.\nYou must set a storage password before you can use the storage.\nPlease enter a new storage password:"), isPassword => 1); + if (!defined($input)) { + return; + } + configModify('storageAuto_password', $input, 1); + } + } + + my @key = split /[, ]+/, $masterServer->{storageEncryptKey}; + if (!@key) { + error (($args->{switch} eq '023E') ? + T("Unable to send character password. You must set the 'storageEncryptKey' option in servers.txt.\n") : + T("Unable to send storage password. You must set the 'storageEncryptKey' option in servers.txt.\n")); + return; + } + my $crypton = new Utils::Crypton(pack("V*", @key), 32); + my $num = ($args->{switch} eq '023E') ? $config{charSelect_password} : $config{storageAuto_password}; + $num = sprintf("%d%08d", length($num), $num); + my $ciphertextBlock = $crypton->encrypt(pack("V*", $num, 0, 0, 0)); + message TF("Storage password set to: %s\n", $config{storageAuto_password}), "success"; + $messageSender->sendStoragePassword($ciphertextBlock, 2); + $messageSender->sendStoragePassword($ciphertextBlock, 3); + + } elsif ($args->{flag} == 1) { + if ($args->{switch} eq '023E') { + if ($config{charSelect_password} eq '') { + my $input = $interface->query(T("Please enter your character password."), isPassword => 1); + if (!defined($input)) { + return; + } + configModify('charSelect_password', $input, 1); + message TF("Character password set to: %s\n", $input), "success"; + } + } else { + if ($config{storageAuto_password} eq '') { + my $input = $interface->query(T("Please enter your storage password."), isPassword => 1); + if (!defined($input)) { + return; + } + configModify('storageAuto_password', $input, 1); + message TF("Storage password set to: %s\n", $input), "success"; + } + } + + my @key = split /[, ]+/, $masterServer->{storageEncryptKey}; + if (!@key) { + error (($args->{switch} eq '023E') ? + T("Unable to send character password. You must set the 'storageEncryptKey' option in servers.txt.\n") : + T("Unable to send storage password. You must set the 'storageEncryptKey' option in servers.txt.\n")); + return; + } + my $crypton = new Utils::Crypton(pack("V*", @key), 32); + my $num = ($args->{switch} eq '023E') ? $config{charSelect_password} : $config{storageAuto_password}; + $num = sprintf("%d%08d", length($num), $num); + my $ciphertextBlock = $crypton->encrypt(pack("V*", $num, 0, 0, 0)); + $messageSender->sendStoragePassword($ciphertextBlock, 3); + + } elsif ($args->{flag} == 8) { # apparently this flag means that you have entered the wrong password + # too many times, and now the server is blocking you from using storage + error T("You have entered the wrong password 5 times. Please try again later.\n"); + # temporarily disable storageAuto + $config{storageAuto} = 0; + my $index = AI::findAction('storageAuto'); + if (defined $index) { + AI::args($index)->{done} = 1; + while (AI::action ne 'storageAuto') { + AI::dequeue; + } + } + } else { + debug(($args->{switch} eq '023E') ? + "Character password: unknown flag $args->{flag}\n" : + "Storage password: unknown flag $args->{flag}\n"); + } +} + +# TODO +sub storage_password_result { + my ($self, $args) = @_; + + # TODO: + # STORE_PASSWORD_EMPTY = 0x0 + # STORE_PASSWORD_EXIST = 0x1 + # STORE_PASSWORD_CHANGE = 0x2 + # STORE_PASSWORD_CHECK = 0x3 + # STORE_PASSWORD_PANALTY = 0x8 + + if ($args->{type} == 4) { # STORE_PASSWORD_CHANGE_OK = 0x4 + message T("Successfully changed storage password.\n"), "success"; + } elsif ($args->{type} == 5) { # STORE_PASSWORD_CHANGE_NG = 0x5 + error T("Error: Incorrect storage password.\n"); + } elsif ($args->{type} == 6) { # STORE_PASSWORD_CHECK_OK = 0x6 + message T("Successfully entered storage password.\n"), "success"; + } elsif ($args->{type} == 7) { # STORE_PASSWORD_CHECK_NG = 0x7 + error T("Error: Incorrect storage password.\n"); + # disable storageAuto or the Kafra storage will be blocked + configModify("storageAuto", 0); + my $index = AI::findAction('storageAuto'); + if (defined $index) { + AI::args($index)->{done} = 1; + while (AI::action ne 'storageAuto') { + AI::dequeue; + } + } + } else { + #message "Storage password: unknown type $args->{type}\n"; + } + + # $args->{val} + # unknown, what is this for? +} + +# Mercenary base status data (ZC_MER_INIT). +# 029B <id>.L <atk>.W <matk>.W <hit>.W <crit>.W <def>.W <mdef>.W <flee>.W <aspd>.W <name>.24B <level>.W <hp>.L <maxhp>.L <sp>.L <maxsp>.L <expire time>.L <faith>.W <calls>.L <kills>.L <atk range>.W +sub mercenary_init { + my ($self, $args) = @_; + + $char->{mercenary} = Actor::get($args->{ID}) if ($char->{mercenary}{ID} ne $args->{ID}); + $char->{mercenary}{map} = $field->baseName; + + my $slave = $char->{mercenary}; + + foreach (@{$args->{KEYS}}) { + $slave->{$_} = $args->{$_}; + } + $slave->{name} = bytesToString($args->{name}); + + Network::Receive::slave_calcproperty_handler($slave, $args); + + unless ($char->{slaves}{$char->{mercenary}{ID}}) { + if ($char->{mercenary}->isa('AI::Slave::Mercenary')) { + # After a teleport the mercenary object is still AI::Slave::Mercenary, but AI::SlaveManager::addSlave requires it to be Actor::Slave::Mercenary, so we change it back + bless $char->{mercenary}, 'Actor::Slave::Mercenary'; + } + AI::SlaveManager::addSlave($char->{mercenary}) if (!$char->has_mercenary); + } + + # ST0's counterpart for ST kRO, since it attempts to support all servers + # TODO: we do this for homunculus, mercenary and our char... make 1 function and pass actor and attack_range? + if ($config{mercenary_attackDistanceAuto} && exists $slave->{attack_range}) { + if($config{mercenary_attackDistance} > $slave->{attack_range}) { # decrease attack range if necessary + configModify('mercenary_attackDistance', $slave->{attack_range}, 1); + message TF("Autodetected attackDistance for mercenary = %s\n", $config{mercenary_attackDistance}), "success"; + } + if ($config{mercenary_attackMaxDistance} != $slave->{attack_range}) { # set max distance using information coming from the server + configModify('mercenary_attackMaxDistance', $slave->{attack_range}, 1); + message TF("Autodetected mercenary_attackMaxDistance for mercenary = %s\n", $config{mercenary_attackMaxDistance}), "success"; + } + } +} + +# +message_string +sub mercenary_off { + #delete $char->{slaves}{$char->{mercenary}{ID}}; + AI::SlaveManager::removeSlave($char->{mercenary}) if ($char->has_mercenary); + + $slavesList->removeByID($char->{mercenary}{ID}); + delete $char->{mercenary}; +} +# -message_string + +sub monster_ranged_attack { + my ($self, $args) = @_; + + my $ID = $args->{ID}; + my $range = $args->{range}; + + my %coords1; + $coords1{x} = $args->{sourceX}; + $coords1{y} = $args->{sourceY}; + my %coords2; + $coords2{x} = $args->{targetX}; + $coords2{y} = $args->{targetY}; + + my $monster = $monstersList->getByID($ID); + if ($monster) { + $monster->{movetoattack_pos} = {%coords1}; + $monster->{movetoattack_time} = time; + } + + $char->{movetoattack_targetID} = $ID; + + $char->{movetoattack_pos} = {%coords2}; + $char->{movetoattack_time} = time; + debug "Received Failed to attack target - you: $coords2{x},$coords2{y} - monster: $coords1{x},$coords1{y} - range $range\n", "parseMsg_move"; + + Plugins::callHook('monster_ranged_attack', {ID => $ID}); +} + +sub mvp_item { + my ($self, $args) = @_; + my $display = itemNameSimple($args->{itemID}); + message TF("Get MVP item %s\n", $display); + chatLog("k", TF("Get MVP item %s\n", $display)); +} + +sub mvp_other { + my ($self, $args) = @_; + my $display = Actor::get($args->{ID}); + message TF("%s become MVP!\n", $display); + chatLog("k", TF("%s become MVP!\n", $display)); +} + +sub mvp_you { + my ($self, $args) = @_; + my $msg = TF("Congratulations, you are the MVP! Your reward is %s exp!\n", $args->{expAmount}); + message $msg; + chatLog("k", $msg); +} + +sub no_teleport { + my ($self, $args) = @_; + my $fail = $args->{fail}; + + if ($fail == 0) { + error T("Unavailable Area To Teleport\n"); + AI::clear(qw/teleport/); + } elsif ($fail == 1) { + error T("Unavailable Area To Memo\n"); + } else { + error TF("Unavailable Area To Teleport (fail code %s)\n", $fail); + } +} + +sub private_message { + my ($self, $args) = @_; + + return unless changeToInGameState(); + + # Type: String + my $privMsgUser = bytesToString($args->{privMsgUser}); + my $privMsg = bytesToString($args->{privMsg}); + stripLanguageCode(\$privMsg); + my $parsed_msg = solveMessage($privMsg); + + if ($privMsgUser ne "" && binFind(\@privMsgUsers, $privMsgUser) eq "") { + push @privMsgUsers, $privMsgUser; + Plugins::callHook('parseMsg/addPrivMsgUser', { + user => $privMsgUser, + msg => $parsed_msg, + rawMsg => $privMsg, + userList => \@privMsgUsers, + }); + } + + chatLog("pm", TF("(From: %s) : %s\n", $privMsgUser, $parsed_msg)) if ($config{'logPrivateChat'}); + message TF("(From: %s) : %s\n", $privMsgUser, $parsed_msg), "pm"; + + ChatQueue::add('pm', undef, $privMsgUser, $parsed_msg); + Plugins::callHook('packet_privMsg', { + privMsgUser => $privMsgUser, + privMsg => $parsed_msg, + MsgUser => $privMsgUser, + Msg => $parsed_msg, + RawMsg => $privMsg, + }); + + if ($config{dcOnPM} && AI::state == AI::AUTO) { + message T("Auto disconnecting on PM!\n"); + chatLog("k", T("*** You were PM'd, auto disconnect! ***\n")); + $messageSender->sendQuit(); + quit(); + } +} + +sub progress_bar_unit { + my($self, $args) = @_; + debug "Displays progress bar (GID: $args-{GID} time: $args-{time})\n"; +} + +sub pvp_rank { + my ($self, $args) = @_; + + # 9A 01 - 14 bytes long + my $ID = $args->{ID}; + my $rank = $args->{rank}; + my $num = $args->{num};; + if ($rank != $ai_v{temp}{pvp_rank} || + $num != $ai_v{temp}{pvp_num}) { + $ai_v{temp}{pvp_rank} = $rank; + $ai_v{temp}{pvp_num} = $num; + if ($ai_v{temp}{pvp}) { + message TF("Your PvP rank is: %s/%s\n", $rank, $num), "map_event"; + } + } +} + +# Presents a list of items that can be repaired (ZC_REPAIRITEMLIST). +# 01FC <packet len>.W { <index>.W <name id>.W <refine>.B <card1>.W <card2>.W <card3>.W <card4>.W }* +sub repair_list { + my ($self, $args) = @_; + undef $repairList; + my $myself = 1; + my $msg1 = center(T(" Repair List "), 80, '-') ."\n". + T(" # Short name Full name\n"); + my $msg2; + for (my $i = 4; $i < $args->{RAW_MSG_SIZE}; $i += 13) { + my $repairItem = {}; + ($repairItem->{index}, + $repairItem->{nameID}, + $repairItem->{upgrade}, + $repairItem->{cards}, + ) = unpack('v2 C a8', substr($args->{RAW_MSG}, $i, 13)); + my $ID = $repairItem->{index} + 2; + $ID = pack("v", $ID); + my $item = $char->inventory->getByID($ID); + $repairItem->{name} = $item->{name}; + + #dirty hack - if the item ID does not match, then we repair other people's items + if ($repairItem->{nameID} ne $item->{nameID}) { + debug "Received 'Repair list' belongs to another player\n", 1; + $myself = 0; + last; + } + + $repairList->[$item->{binID}] = $repairItem; + my $shortName = itemNameSimple($repairItem->{nameID}); + $msg2 .= sprintf("%4d %-30s %s\n", $item->{binID}, $shortName, $item->{name}); + } + + if (!$myself) { + # then we repair other people's items + # we need to rebuild the entire array + undef $repairList; + undef $msg2; + for (my $i = 4; $i < $args->{RAW_MSG_SIZE}; $i += 13) { + my $repairItem = {}; + ($repairItem->{index}, + $repairItem->{nameID}, + $repairItem->{upgrade}, + $repairItem->{cards}, + ) = unpack('v2 C a8', substr($args->{RAW_MSG}, $i, 13)); + my $shortName = itemNameSimple($repairItem->{nameID}); + my $fullName = itemName($repairItem); + $repairItem->{name} = $fullName; + + $repairList->[$repairItem->{index}] = $repairItem; + $msg2 .= sprintf("%4d %-30s %s\n", $repairItem->{index}, $shortName, $fullName); + } + } + $msg2 .= ('-'x80) . "\n"; + message $msg1.$msg2, "list"; +} + +# Notifies the client about the result of a item repair request (ZC_ACK_ITEMREPAIR). +# 01FE <index>.W <result>.B +# index: +# ignored (inventory index) +# result: +# 0 = Item repair success. +# 1 = Item repair failure. +sub repair_result { + my ($self, $args) = @_; + + my $index = $args->{index} - 2; + my $item = $char->inventory->getByID($index); + + if ($args->{flag}) { + message TF("Repair of %s failed.\n", $repairList->[$index]->{name}); + } else { + message TF("Successfully repaired '%s'.\n", $repairList->[$index]->{name}); + } + undef $repairList; +} + +sub resurrection { + my ($self, $args) = @_; + + my $targetID = $args->{targetID}; + my $player = $playersList->getByID($targetID); + my $type = $args->{type}; + + if ($targetID eq $accountID) { + message T("You have been resurrected\n"), "info"; + undef $char->{'dead'}; + undef $char->{'dead_time'}; + $char->{'resurrected'} = 1; + + } else { + if ($player) { + undef $player->{'dead'}; + $player->{deltaHp} = 0; + } + + if (isMySlaveID($targetID)) { + enforce_homun_state(); + my $slave = Actor::get($targetID); + if ($slave->isa("AI::Slave::Homunculus") || $slave->isa("Actor::Slave::Homunculus")) { + message TF("Slave Resurrected: %s\n", $slave); + $char->{homunculus_info}{dead} = 0; + if (!$char->has_homunculus) { + debug "[Homunculus] Adding homunculus to SlaveManager after homunculus_info packet.\n"; + bless $char->{homunculus}, 'Actor::Slave::Homunculus'; + AI::SlaveManager::addSlave($slave); + } + } + } + message TF("%s has been resurrected\n", getActorName($targetID)), "info"; + } +} + +sub secure_login_key { + my ($self, $args) = @_; + $secureLoginKey = $args->{secure_key}; + debug sprintf("Secure login key: %s\n", getHex($args->{secure_key})), 'connection'; +} + +sub self_chat { + my ($self, $args) = @_; + my ($message, $chatMsgUser, $chatMsg); # Type: String + + $message = bytesToString($args->{message}); + + ($chatMsgUser, $chatMsg) = $message =~ /([\s\S]*?) : ([\s\S]*)/; + # Note: $chatMsgUser/Msg may be undefined. This is the case on + # eAthena servers: it uses this packet for non-chat server messages. + + if (defined $chatMsgUser) { + stripLanguageCode(\$chatMsg); + my $parsed_msg = solveMessage($chatMsg); + $message = $chatMsgUser . " : " . $parsed_msg; + } + + chatLog("c", "$message\n") if ($config{'logChat'}); + message "$message\n", "selfchat"; + + Plugins::callHook('packet_selfChat', { + user => $chatMsgUser, + msg => $chatMsg + }); +} + +sub sync_request { + my ($self, $args) = @_; + + # 0187 - long ID + # I'm not sure what this is. In inRO this seems to have something + # to do with logging into the game server, while on + # oRO it has got something to do with the sync packet. + if ($masterServer->{serverType} == 1) { + my $ID = $args->{ID}; + if ($ID == $accountID) { + $timeout{ai_sync}{time} = time; + $messageSender->sendSync() unless ($net->clientAlive); + debug "Sync packet requested\n", "connection"; + } else { + warning T("Sync packet requested for wrong ID\n"); + } + } +} + +sub sense_result { + my ($self, $args) = @_; + # nameID level size hp def race mdef element ice earth fire wind poison holy dark spirit undead + my @race_lut = qw(Formless Undead Beast Plant Insect Fish Demon Demi-Human Angel Dragon Boss Non-Boss); + my @size_lut = qw(Small Medium Large); + message TF("=====================Sense========================\n" . + "Monster: %-16s Level: %-12s\n" . + "Size: %-16s Race: %-12s\n" . + "Def: %-16s MDef: %-12s\n" . + "Element: %-16s HP: %-12s\n" . + "=================Damage Modifiers=================\n" . + "Ice: %-3s Earth: %-3s Fire: %-3s Wind: %-3s\n" . + "Poison: %-3s Holy: %-3s Dark: %-3s Spirit: %-3s\n" . + "Undead: %-3s\n" . + "==================================================\n", + $monsters_lut{$args->{nameID}}, $args->{level}, $size_lut[$args->{size}], $race_lut[$args->{race}], + $args->{def}, $args->{mdef}, $elements_lut{$args->{element}}, $args->{hp}, + $args->{ice}, $args->{earth}, $args->{fire}, $args->{wind}, $args->{poison}, $args->{holy}, $args->{dark}, + $args->{spirit}, $args->{undead}), "list"; +} + +# TODO: +# Add 'dispose' support +sub skill_cast { + my ($self, $args) = @_; + + return unless changeToInGameState(); + my $sourceID = $args->{sourceID}; + my $targetID = $args->{targetID}; + my $x = $args->{x}; + my $y = $args->{y}; + my $skillID = $args->{skillID}; + my $type = $args->{type}; + my $wait = $args->{wait}; + my ($dist, %coords); + + # Resolve source and target + my $source = Actor::get($sourceID); + my $target = Actor::get($targetID); + my $verb = $source->verb('are casting', 'is casting'); + + Misc::checkValidity("skill_cast part 1"); + + my $skill = new Skill(idn => $skillID); + $source->{casting} = { + skill => $skill, + target => $target, + x => $x, + y => $y, + startTime => time, + castTime => $wait + }; + # Since we may have a circular reference, weaken this reference + # to prevent memory leaks. + Scalar::Util::weaken($source->{casting}{target}); + + my $targetString; + if ($x != 0 || $y != 0) { + # If $dist is positive we are in range of the attack? + $coords{x} = $x; + $coords{y} = $y; + $dist = judgeSkillArea($skillID) - blockDistance($char->{pos_to}, \%coords); + $targetString = "location ($x, $y)"; + undef $targetID; + } else { + $targetString = $target->nameString($source); + } + + # Perform trigger actions + if ($sourceID eq $accountID) { + $char->{time_cast} = time; + $char->{time_cast_wait} = $wait / 1000; + delete $char->{cast_cancelled}; + } + countCastOn($sourceID, $targetID, $skillID, $x, $y); + + Misc::checkValidity("skill_cast part 2"); + + my $domain = ($sourceID eq $accountID) ? "selfSkill" : "skill"; + my $disp = skillCast_string($source, $target, $x, $y, $skill->getName(), $wait); + message $disp, $domain, 1; + + Plugins::callHook('is_casting', { + sourceID => $sourceID, + targetID => $targetID, + source => $source, + target => $target, + skillID => $skillID, + skill => $skill, + time => $source->{casting}{time}, + castTime => $wait, + x => $x, + y => $y + }); + + Misc::checkValidity("skill_cast part 3"); + + # Skill Cancel + my $monster = $monstersList->getByID($sourceID); + my $control; + $control = mon_control($monster->name,$monster->{nameID}) if ($monster); + if (AI::state == AI::AUTO && $control->{skillcancel_auto}) { + if ($targetID eq $accountID || $dist > 0 || (AI::action eq "attack" && AI::args->{ID} ne $sourceID)) { + message TF( "Monster Skill - %s (%d) - Adding it to monsterSkillCancel list to be attacked\n", + $monster->name, $monster->{binID} ); + $monster->{monsterSkillCancel} = 1; + } + + # Skill area casting -> running to monster's back + my $ID; + if ($dist > 0 && AI::action eq "attack" && ($ID = AI::args->{ID}) && (my $monster2 = $monstersList->getByID($ID))) { + # Calculate X axis + if ($char->{pos_to}{x} - $monster2->{pos_to}{x} < 0) { + $coords{x} = $monster2->{pos_to}{x} + 3; + } else { + $coords{x} = $monster2->{pos_to}{x} - 3; + } + # Calculate Y axis + if ($char->{pos_to}{y} - $monster2->{pos_to}{y} < 0) { + $coords{y} = $monster2->{pos_to}{y} + 3; + } else { + $coords{y} = $monster2->{pos_to}{y} - 3; + } + + my (%vec, %pos); + getVector(\%vec, \%coords, $char->{pos_to}); + moveAlongVector(\%pos, $char->{pos_to}, \%vec, distance($char->{pos_to}, \%coords)); + ai_route($field->baseName, $pos{x}, $pos{y}, + maxRouteDistance => $config{attackRouteMaxPathDistance}, + maxRouteTime => $config{attackMaxRouteTime}, + noMapRoute => 1); + message TF("Avoid casting Skill - switch position to : %s,%s\n", $pos{x}, $pos{y}), 1; + } + + Misc::checkValidity("skill_cast part 4"); + } +} + +# Notifies clients in area, that an object canceled casting (ZC_DISPEL). +# 01B9 <id>.L +sub cast_cancelled { + my ($self, $args) = @_; + + # Cast is cancelled + my $ID = $args->{ID}; + + my $source = Actor::get($ID); + $source->{cast_cancelled} = time; + my $skill = $source->{casting}->{skill}; + my $skillName = $skill ? $skill->getName() : T('Unknown'); + my $domain = ($ID eq $accountID) ? "selfSkill" : "skill"; + message sprintf($source->verb(T("%s failed to cast %s\n"), T("%s failed to cast %s\n")), $source, $skillName), $domain; + Plugins::callHook('packet_castCancelled', { + sourceID => $ID + }); + delete $source->{casting}; +} + +# Notifies the client, whether it can disconnect and change servers (ZC_RESTART_ACK). +# 00B3 <type>.B +# type: +# 1 = disconnect, char-select +# ? = nothing +# TODO: add real client messages and logic? +# ClientLogic: LoginStartMode = 5; ShowLoginScreen; +sub switch_character { + my ($self, $args) = @_; + # User is switching characters in X-Kore + $net->setState(Network::CONNECTED_TO_MASTER_SERVER); + $net->serverDisconnect() if(UNIVERSAL::isa($net, 'Network::DirectConnection')); + + # FIXME better support for multiple received_characters packets + undef @chars; + + debug "result: $args->{result}\n"; +} + +# Notifies the client about the result of a request to take off an item. +# 00AC <index>.W <equip location>.W <result>.B (ZC_REQ_TAKEOFF_EQUIP_ACK) +# 08D1 <index>.W <equip location>.W <result>.B (ZC_REQ_TAKEOFF_EQUIP_ACK2) +# 099A <index>.W <equip location>.L <result>.B (ZC_ACK_TAKEOFF_EQUIP_V5) +# @ok : //inversed for v2 v5 +# 0 = failure +# 1 = success +sub unequip_item { + my ($self, $args) = @_; + + return unless changeToInGameState(); + my $item = $char->inventory->getByID($args->{ID}); + delete $item->{equipped}; + + if ($args->{type} == 10 || $args->{type} == 32768) { + delete $char->{equipment}{arrow}; + delete $char->{arrow}; + } else { + foreach (%equipSlot_rlut){ + if ($_ & $args->{type}){ + next if $_ == 10; #work around Arrow bug + next if $_ == 32768; + delete $char->{equipment}{$equipSlot_lut{$_}}; + Plugins::callHook('unequipped_item', { + slot => $equipSlot_lut{$_}, + item => $item + }); + } + } + } + + if ($item) { + message TF("You unequip %s (%d) - %s\n",$item->{name}, $item->{binID},$equipTypes_lut{$item->{type_equip}}), 'inventory'; + } +} + +# Acknowledgement for removing an equip to the equip switch window +# 0A9A <index>.W <position.>.L <failure>.W +sub unequip_item_switch { + my ($self, $args) = @_; + + return unless changeToInGameState(); + my $item = $char->inventory->getByID($args->{ID}); + delete $item->{eqswitch}; + + if ($args->{type} == 10 || $args->{type} == 32768) { + delete $char->{eqswitch}{arrow}; + } else { + foreach (%equipSlot_rlut){ + if ($_ & $args->{type}){ + next if $_ == 10; #work around Arrow bug + next if $_ == 32768; + + delete $char->{eqswitch}{$equipSlot_lut{$_}}; + Plugins::callHook('unequipped_item_sw', { + slot => $equipSlot_lut{$_}, + item => $item + }); + } + } + } + + if ($item) { + message TF("[Equip Switch] You unequip %s (%d) - %s\n",$item->{name}, $item->{binID},$equipTypes_lut{$item->{type_equip}}), 'inventory'; + } +} + +# TODO: only used to report failure? $args->{success} +sub use_item { + my ($self, $args) = @_; + return unless changeToInGameState(); + my $item = $char->inventory->getByID($args->{ID}); + if ($item) { + message TF("You used Item: %s (%d) x %s\n", $item->{name}, $item->{binID}, $args->{amount}), "useItem"; + inventoryItemRemoved($item->{binID}, $args->{amount}); + } +} + +sub users_online { + my ($self, $args) = @_; + message TF("There are currently %s users online\n", $args->{users}), "info"; +} + +# You see a vender! Add them to the visible venders list. +sub vender_found { + my ($self, $args) = @_; + my $ID = $args->{ID}; + + if (!$venderLists{$ID} || !%{$venderLists{$ID}}) { + binAdd(\@venderListsID, $ID); + Plugins::callHook('packet_vender', { + ID => $ID, + title => bytesToString($args->{title}) + }); + } + $venderLists{$ID}{title} = bytesToString($args->{title}); + $venderLists{$ID}{id} = $ID; +} + +sub vender_lost { + my ($self, $args) = @_; + + my $ID = $args->{ID}; + binRemove(\@venderListsID, $ID); + delete $venderLists{$ID}; +} + +sub skill_add { + my ($self, $args) = @_; + + return unless changeToInGameState(); + my $handle = ($args->{name}) ? $args->{name} : Skill->new(idn => $args->{skillID})->getHandle(); + + $char->{skills}{$handle}{ID} = $args->{skillID}; + $char->{skills}{$handle}{sp} = $args->{sp}; + $char->{skills}{$handle}{range} = $args->{range}; + $char->{skills}{$handle}{up} = $args->{upgradable}; + $char->{skills}{$handle}{targetType} = $args->{target}; + $char->{skills}{$handle}{lv} = $args->{lv}; + $char->{skills}{$handle}{new} = 1; + + #Fix bug , receive status "Night" 2 time + binAdd(\@skillsID, $handle) if (binFind(\@skillsID, $handle) eq ""); + + Skill::DynamicInfo::add($args->{skillID}, $handle, $args->{lv}, $args->{sp}, $args->{target}, $args->{target}, Skill::OWNER_CHAR); + + Plugins::callHook('packet_charSkills', { + ID => $args->{skillID}, + handle => $handle, + level => $args->{lv}, + upgradable => $args->{upgradable}, + level2 => $args->{lv2}, + }); +} + +sub isvr_disconnect { + debug "Received the package 'isvr_disconnect'\n"; +} + +sub skill_use_failed { + my ($self, $args) = @_; + + my %basefailtype = ( + 0 => $msgTable[160],#"skill failed" + 1 => $msgTable[161],#"no emotions" + 2 => $msgTable[162],#"no sit" + 3 => $msgTable[163],#"no chat" + 4 => $msgTable[164],#"no party" + 5 => $msgTable[165],#"no shout" + 6 => $msgTable[166],#"no PKing" + 7 => $msgTable[384],#"no aligning" + #? = ignored + ); + + my %failtype = ( + 0 => T( 'Basic' ), + 1 => T( 'Insufficient SP' ), + 2 => T( 'Insufficient HP' ), + 3 => T( 'No Memo' ), + 4 => T( 'Mid-Delay' ), + 5 => T( 'No Zeny' ), + 6 => T( 'Wrong Weapon Type' ), + 7 => T( 'Red Gem Needed' ), + 8 => T( 'Blue Gem Needed' ), + 9 => TF( '%s Overweight', '90%' ), + 10 => T( 'Requirement' ), + 11 => T( 'Failed to use in Target' ), + 12 => T( 'Maximum Ancilla exceed' ), + 13 => T( 'Need this within the Holy water' ), + 14 => T( 'Missing Ancilla' ), + 19 => T( 'Full Amulet' ), + 24 => T( '[Purchase Street Stall License] need 1' ), + 26 => T( 'Position error' ), + 29 => TF( 'Must have at least %s of base XP', '1%' ), + 30 => T( 'Insufficient SP' ), + 33 => T( 'Failed to use Madogear' ), + 34 => T( 'Kunai is Required' ), + 37 => T( 'Canon ball is Required' ), + 43 => T( 'Failed to use Guillotine Poison' ), + 50 => T( 'Failed to use Madogear' ), + 71 => T( 'Missing Required Item' ), # (item name) required x amount + 72 => T( 'Equipment is required' ), + 73 => T( 'Combo Skill Failed' ), + 76 => T( 'Too many HP' ), + 77 => T( 'Need Royal Guard Branding' ), + 78 => T( 'Required Equiped Weapon Class' ), + 83 => T( 'Location not allowed to create chatroom/market' ), + 84 => T( 'Need more bullet' ), + ); + + my $errorMessage; + if ($args->{skillID} == 1 && $args->{cause} == 0 && exists $basefailtype{$args->{btype}}) { + $errorMessage = $basefailtype{$args->{btype}}; + } elsif (exists $failtype{$args->{cause}}) { + $errorMessage = $failtype{$args->{cause}}; + if ($args->{cause} == 71) { + $errorMessage .= T(' - item ').$args->{itemId}; + } + } else { + $errorMessage = T('Unknown error'); + } + + delete $char->{casting}; + + my %hookArgs; + $hookArgs{skillID} = $args->{skillID}; + $hookArgs{btype} = $args->{btype}; + $hookArgs{itemId} = $args->{itemId}; + $hookArgs{flag} = $args->{flag}; + $hookArgs{cause} = $args->{cause}; + $hookArgs{failMessage} = $errorMessage; + $hookArgs{warn} = 1; + + Plugins::callHook('packet_skillfail', \%hookArgs); + + warning(TF("Skill %s failed: %s (error number %s)\n", Skill->new(idn => $args->{skillID})->getName(), $errorMessage, $args->{cause}), "skill") if ($hookArgs{warn}); + + # Ressurect Homunculus failed - which means we have no dead homunculus + if ($args->{skillID} == 247 && $args->{cause} == 0) { + debug "[Homunculus] Ressurect Homunculus failed - which means we have no dead homunculus.\n"; + $char->{homunculus_info}{dead} = 0; + } + + # Call Homunculus failed - which means we have no vaporized homunculus + if ($args->{skillID} == 243) { + if ($args->{cause} == 0) { + debug "[Homunculus] Call Homunculus failed - which means we have no vaporized homunculus.\n"; + $char->{homunculus_info}{vaporized} = 0; + } elsif ($args->{cause} == 71) { + debug "[Homunculus] Call Homunculus failed because of missing item - which means we have a vaporized homunculus.\n"; + $char->{homunculus_info}{vaporized} = 1; + } + } +} + +sub open_store_status { + my ($self, $args) = @_; + + if ($args->{flag} == 0) { + message T("Store set up succesfully\n"), 'success'; + + Plugins::callHook('open_store_success'); + } else { + error TF("Failed setting up shop with error code %d\n", $args->{flag}); + + Plugins::callHook('open_store_fail', { flag => $args->{flag} }); + } +} + +sub stylist_res { + my ($self, $args) = @_; + + if ($args->{res}) { + message T("[Stylist UI] Success.\n"), "info"; + } else { + error T("[Stylist UI] Fail.\n"); + } +} + +## +# User Interface (open system) +## + +# Opens an UI window of the given type and initializes it with the given data +# 0AE2 <type>.B <data>.L +# type: +# 0x0 = BANK_UI +# 0x1 = STYLIST_UI +# 0x2 = CAPTCHA_UI +# 0x3 = MACRO_UI +# 0x4 = UI_UNUSED +# 0x5 = TIPBOX_UI +# 0x6 = RENEWQUEST_UI +# 0x7 = ATTENDANCE_UI +sub open_ui { + my ($self, $args) = @_; + + debug TF("Received request from server to open UI: %s\n", $args->{type}); + + if ($args->{type} == BANK_UI) { # TODO: implement bank system and add Bank open Request + message T("Server requested to open Bank UI.\n"); + } elsif ($args->{type} == STYLIST_UI) { # TODO: implement Stylist system and add Stylist open Request + message T("Server requested to open Stylist UI.\n"); + } elsif ($args->{type} == CAPTCHA_UI) { + message T("Server requested to open Captcha UI.\n"); + } elsif ($args->{type} == MACRO_UI) { + message T("Server requested to open Macro Recorder UI.\n"); + } elsif ($args->{type} == UI_UNUSED) { + message T("Server requested to open Unused UI.\n"); # why? + } elsif ($args->{type} == TIPBOX_UI) { + message T("Server requested to open Tip Box UI.\n"); + } elsif ($args->{type} == RENEWQUEST_UI) { + message T("Server requested to open Quest UI.\n"); + } elsif ($args->{type} == ATTENDANCE_UI) { + message T("Server requested to open Attendance UI.\n"); + $self->attendance_ui($args); + } else { + error TF("Received request from server to open unknown UI: %s\n", $args->{type}); + } +} + +# Response for UI request +# 0AF0 <type>.L <data>.L (PACKET_ZC_UI_ACTION) +# type: +# 0x0 = close current UI +sub action_ui { + my ($self, $args) = @_; + + debug TF("Received request from server to close UI: %s\n", $args->{type}); +} + +## +# Attendance System +## + +# Opens an ATTENDANCE UI window and initializes it with the given data +# 0AE2 <type>.B <data>.L +# type = 0x7 +sub attendance_ui { + my ($self, $args) = @_; + + if (defined $attendance_rewards{period}) { + my $date = getFormattedDateShort(time, 3); + + if ($date >= $attendance_rewards{period}{start} && $date <= $attendance_rewards{period}{end}) { + my $already_requested = $args->{data}%10; + my $attendance_count = int($args->{data}/10) + 1 - $already_requested; + my $attendanceAuto; + my $msg = center(T(" Attendance "), 54, '-') ."\n"; + $msg .= TF("Start: %s End: %s Day: %s\n", $attendance_rewards{period}{start}, $attendance_rewards{period}{end}, $attendance_count); + + $msg .= T("Day Item Amount Requested\n"); + for (my $i = 1; $i <= 20; $i++) { + my $requested = ($attendance_count >= $i) ? T("yes") : T("no"); + if ($attendance_count == $i && !$already_requested) { + $requested = T("can"); + $attendanceAuto = 1 if $config{'attendanceAuto'}; + } + $msg .= swrite(sprintf("\@%s \@%s \@%s \@%s", ('<'x3), ('<'x30), ('<'x6), ('<'x9)), + [$i, itemNameSimple($attendance_rewards{items}{$i}{item_id}), $attendance_rewards{items}{$i}{amount}, $requested]); + } + + $msg .= ('-'x54) . "\n"; + message $msg, "info"; + + if ($attendanceAuto) { + Commands::run('attendance request'); + message T("Run command: 'attendance request'\n"); + } + } else { + warning T("attendance_rewards.txt is outdated\n"), "info"; + } + } else { + error T("attendance_rewards.txt not exist\n"); + } +} + +# Notifies a movement interrupted +# 0AB8 +sub move_interrupt { + my ($self, $args) = @_; + debug "Movement interrupted by casting a skill/fleeing a mob/etc\n"; +} + +## +# Banking System +## + +# Display how much we have in bank +# 09A6 <Bank_Vault>Q <Reason>W (PACKET_ZC_BANKING_CHECK) +# Reason: +# 1 = mark opening and closing +sub banking_check { + my ($self, $args) = @_; + + $bankingopened = 1; + $banking{zeny} = $args->{zeny}; + + message center(T("[Zeny Storage (Bank)]"), 40, '-') ."\n", "info"; + message TF("In Bank : %s z\n", $args->{zeny}), "info"; + message TF("On Hand : %s z\n", $char->{zeny}), "info"; + message ('-'x40) . "\n", "info"; + + Plugins::callHook('banking_opened'); +} + +# Acknowledge of deposit some money in bank +# 09A8 <Reason>W <Money>Q <balance>L (PACKET_ZC_ACK_BANKING_DEPOSIT) +# reason: +# BDA_SUCCESS = 0x0 +# BDA_ERROR = 0x1 +# BDA_NO_MONEY = 0x2 +# BDA_OVERFLOW = 0x3 +sub banking_deposit { + my ($self, $args) = @_; + + if ($args->{reason} == 0x0) { + message T("Bank: Deposit Success.\n"), "success"; + $char->{zeny} = $args->{balance}; # TODO: check if 'stat_info' is received (if yes, delete this line) + Plugins::callHook('banking_deposit_success'); + return; + } elsif ($args->{reason} == 0x1) { + error T("Bank: Deposit Error (Try it again).\n"); + } elsif ($args->{reason} == 0x2) { + error T("Bank: No Money For Deposit.\n"); + } elsif ($args->{reason} == 0x3) { + error T("Bank: Money in the bank overflow.\n"); + } + Plugins::callHook('banking_deposit_failed', {'reason' => $args->{reason}}); +} + +# Acknowledge of withdrawing some money from bank +# 09AA <Reason>W <Money>Q <balance>L (PACKET_ZC_ACK_BANKING_WITHDRAW) +# reason: +# BWA_SUCCESS = 0x0 +# BWA_NO_MONEY = 0x1 +# BWA_UNKNOWN_ERROR = 0x2 +sub banking_withdraw { + my ($self, $args) = @_; + + if ($args->{reason} == 0x0) { + message T("Bank: Withdraw Success \n"),"success"; + $char->{zeny} = $args->{balance}; # TODO: check if 'stat_info' is received (if yes, delete this line) + Plugins::callHook('banking_withdraw_success'); + return; + } elsif ($args->{reason} == 0x1) { + error T("Bank: No Money for Withdraw.\n"); + } elsif ($args->{reason} == 0x2) { + error T("Bank: Money in the bank overflow.\n"); + } + Plugins::callHook('banking_withdraw_failed', {'reason' => $args->{reason}}); +} + +## +# Navigation System +## + +# start a navigation to designed location/map +# 08E2 <type>.B <flag>.B <hide>.B <map>.16B <x pos>.W <y pos>.W <mob id>.W +# TODO: document type and flag +sub navigate_to { + my ($self, $args) = @_; + + if ($args->{mob_id}) { + message TF("Server asked us to navigate to %s map and look for monster with ID %s\n", $args->{map}, $args->{mob_id}), "info"; + } else { + message TF("Server asked us to navigate to %s (%s,%s)\n", $args->{map}, $args->{x}, $args->{y}), "info"; + } + + Plugins::callHook('navigate_to', $args); +} + +## +# Roulette System +## +# Opens the roulette window +# 0A1A <result>.B <serial>.L <stage>.B <price index>.B <additional item id>.W <gold>.L <silver>.L <bronze>.L (ZC_ACK_OPEN_ROULETTE) +sub roulette_window { + my ($self, $args) = @_; + my @result_lut = qw(Success Failed No_Enought_Point Losing); + + foreach (@{$args->{KEYS}}) { + $roulette{$_} = $args->{$_}; + } + + if ($args->{result} == 1) { + warning T("Roulette: Something went wrong\n"); + return; + } elsif ($args->{result} == 2) { + warning T("Roulette: No enough Point (coin) to roll\n"); + return; + } + + message center(T("[Roulette] - " . $args->{serial}), 60, '-') ."\n", "info"; + message TF("Result: %s Row: %s Column: %s Bonus Item: %s\n", $result_lut[$args->{result}], $args->{stage}, $args->{price}, itemNameSimple($args->{additional_item})), "info"; + message T("Coins:\n"), "info"; + message TF("Gold: %s Silver: %s Bronze: %s\n", $args->{gold}, $args->{silver}, $args->{bronze}, itemNameSimple($args->{additional_item})), "info"; + message center(T("-"), 60, '-') . "\n", "info"; + + if ($args->{stage} == 6) { + warning T("Please Claim Your Prize this was the last roll in this round. (you will lost the gold and the item)\n"); + } +} + +# Sends the info about the available roulette rewards to the client +# 0A1C <length>.W <serial>.L { { <level>.W <column>.W <item>.W <amount>.W } * MAX_ROULETTE_COLUMNS } * MAX_ROULETTE_LEVEL (ZC_ACK_ROULEITTE_INFO) +# 0A1C <length>.W <serial>.L { { <level>.W <column>.W <item>.L <amount>.L } * MAX_ROULETTE_COLUMNS } * MAX_ROULETTE_LEVEL (ZC_ACK_ROULEITTE_INFO) >= 20180516 +sub roulette_info { + my ($self, $args) = @_; + + my $item_info = { + len => 8, # or 12 + types => 'v4', # or v2 V2 + keys => [qw(level column item_id amount)], + }; + + for (my $i = 0; $i < length($args->{roulette_info}); $i += $item_info->{len}) { + my $item; + @{$item}{@{$item_info->{keys}}} = unpack($item_info->{types}, substr($args->{roulette_info}, $i, $item_info->{len})); + $item->{name} = itemNameSimple($item->{item_id}); + $roulette{items}{$item->{level}}{$item->{column}} = $item; + debug TF("Level: %s Column: %s Item: %s\n", $item->{level}, $item->{column}, $item->{name}); + } +} + +# Response to a item reward request +# 0A22 <type>.B <bonus item>.W (ZC_RECV_ROULETTE_ITEM) +sub roulette_recv_item { + my ($self, $args) = @_; + message TF("Roulette Bonus - Type: %s Bonus Item: %s\n", $args->{type}, itemNameSimple($args->{item_id})), "info"; + +} + +# Update Roulette window with current stats +# 0A20 <result>.B <stage>.W <price index>.W <bonus item>.W <gold>.L <silver>.L <bronze>.L (ZC_ACK_GENERATE_ROULETTE) +sub roulette_window_update { + my ($self, $args) = @_; + my @result_lut = qw(Success Failed No_Enought_Point Losing); + + foreach (@{$args->{KEYS}}) { + $roulette{$_} = $args->{$_}; + } + + if ($args->{result} == 1) { + warning T("Roulette: Something went wrong\n"); + return; + } elsif ($args->{result} == 2) { + warning T("Roulette: No enough Point (coin) to roll\n"); + return; + } + + message center(T("[Roulette] - " . $roulette{serial}), 60, '-') ."\n", "info"; + message TF("Result: %s Row: %s Column: %s Bonus Item: %s\n", $result_lut[$args->{result}], $args->{stage}, $args->{price}, itemNameSimple($args->{additional_item})), "info"; + message T("Coins:\n"), "info"; + message TF("Gold: %s Silver: %s Bronze: %s\n", $args->{gold}, $args->{silver}, $args->{bronze}, itemNameSimple($args->{additional_item})), "info"; + message T("Result:\n"), "info"; + message T(">> ".$roulette{items}{$args->{stage}}{$args->{price}}->{name}." << \n"), "info"; + message center(T("-"), 60, '-') . "\n", "info"; + + if ($args->{stage} == 6) { + warning T("Please Claim Your Prize this was the last roll in this round. (you will lost the gold and the item)\n"); + } +} + +# Allow Client Shortcut/Keys Input +# 0B01 +sub load_confirm { + my ($self, $args) = @_; + debug TF("You are allowed to use Keyboard\n"); # this only matter in ragexe client +} + +# Inventory Expansion Result +# 0B18 <Result>W +# result: +# EXPAND_INVENTORY_RESULT_SUCCESS = 0x0 +# EXPAND_INVENTORY_RESULT_FAILED = 0x1 +# EXPAND_INVENTORY_RESULT_OTHER_WORK = 0x2 +# EXPAND_INVENTORY_RESULT_MISSING_ITEM = 0x3 +# EXPAND_INVENTORY_RESULT_MAX_SIZE = 0x4 +sub inventory_expansion_result { + my($self, $args) = @_; + + #msgstringtable + if ($args->{result} == EXPAND_INVENTORY_RESULT_SUCCESS) { + message TF("You have successfully expanded the possession limit.\n"),"info"; + } elsif ($args->{result} == EXPAND_INVENTORY_RESULT_FAILED) { + message TF("Failed to expand the maximum possession limit.\n"),"info"; + } elsif ($args->{result} == EXPAND_INVENTORY_RESULT_OTHER_WORK) { + message TF("To expand the possession limit, please close other windows.\n"),"info"; + } elsif ($args->{result} == EXPAND_INVENTORY_RESULT_MISSING_ITEM) { + message TF("Failed to expand the maximum possession limit, insufficient required item.\n"),"info"; + } elsif ($args->{result} == EXPAND_INVENTORY_RESULT_MAX_SIZE) { + message TF("You can no longer expand the maximum possession limit.\n"),"info"; + } else { + message TF("Unknown result in inventory expansion (%s).\n", $args->{result}),"info"; + } +} + +sub item_preview { + my ($self, $args) = @_; + my $item = $char->inventory->getByID($args->{index}); + if ($item) { + $item->{broken} = $args->{broken} if (defined $args->{broken}); + $item->{upgrade} = $args->{upgrade}; + $item->{cards} = $args->{cards}; + $item->{options} = $args->{options}; + $item->setName(itemName($item)); + + } +} + +# 0B1D (PACKET_ZC_PING) +sub ping { + return if ($config{XKore} eq 1 || $config{XKore} eq 3); + $messageSender->sendPing(); +} + +# 0253 - ZC_STARPLACE +# Star Gladiator's Feeling map confirmation prompt +sub starplace { + my ($self, $args) = @_; + message TF("Wich: %s\n", $args->{which}); +} + +### +# +# Captcha System ( macro detector ) +# 4 parts: Macro Register UI ( /macro_register ), Macro Detector UI ( player ), Macro Reporter UI ( /macro_detector ) and Captcha Preview UI ( /macro_preview ) +# +### + +# 0A53 - PACKET_ZC_CAPTCHA_UPLOAD_REQUEST +# Captcha Upload Image UI +sub captcha_upload_request { + my ($self, $args) = @_; + if ($args->{status} == 0) { + message T("Captcha Register - Now you can upload the image\n"); + } elsif ($args->{status} == 1) { + message T("Captcha Register - Failed to upload the image\n"); + } else { + message TF("Captcha Register - Unknown status: %s\n", $args->{status}); + } + + return unless (UNIVERSAL::isa($net, 'Network::DirectConnection')); +} + +# 0A55 - PACKET_ZC_CAPTCHA_UPLOAD_REQUEST_STATUS +# Result of Captcha Upload +sub captcha_upload_request_status { + message T("Captcha Register - Image uploaded succesfully\n"); +} + +# 0A57 - PACKET_ZC_MACRO_REPORTER_STATUS +# Status of Macro Reporter +sub macro_reporter_status { + my ($self, $args) = @_; + my $status = T("Unknown"); + + if ($args->{status} == MCR_MONITORING) { + $status = T("Monitoring"); + } elsif ($args->{status} == MCR_NO_DATA) { + $status = T("No Data"); + } elsif ($args->{status} == MCR_INPROGRESS) { + $status = T("In Progress"); + } + + message TF("Macro Reporter - Status: %s \n", $status), "captcha"; +} + +# 0A58 - PACKET_ZC_MACRO_DETECTOR_REQUEST +# Macro Detector Image info +sub macro_detector { + my ($self, $args) = @_; + debug TF("Macro Detector - image_size: %s bytes - captcha_key: %s\n", $args->{image_size}, $args->{captcha_key}), "captcha"; + $captcha_size = $args->{image_size}; + $captcha_key = $args->{captcha_key}; +} + +# 0A59 - PACKET_ZC_MACRO_DETECTOR_REQUEST_DOWNLOAD +# Macro DDetector Captcha Image +# captcha_image is sended in chunks +sub macro_detector_image { + my ($self, $args) = @_; + + $captcha_image .= $args->{captcha_image}; + + if (length($captcha_image) >= $captcha_size) { + my $image = uncompress($captcha_image); + my $imageHex = unpack("H*", $image); + my $byte1; my $byte2; my $byte3; + for (my $i = 102; $i < 3564; $i += 6) { + $byte1 = hex(substr($imageHex, $i, 2)); + $byte2 = substr($imageHex, $i + 2, 2); + $byte3 = hex(substr($imageHex, $i + 4, 2)); + + if ($byte1 > 250 && $byte2 eq '00' && $byte3 > 250) { + substr($imageHex, $i + 2, 2) = 'FF'; + } + } + + my $file = $Settings::logs_folder . "/captcha_$captcha_key.bmp"; + my $final_image = pack("H*", $imageHex); + $captcha_image_content = $final_image; + open my $DUMP, '>:raw', $file; + print $DUMP $final_image; + close $DUMP; + + + my $hookArgs = {captcha_image => $final_image}; + Plugins::callHook ('captcha_image', $hookArgs); + Plugins::callHook ('captcha_file', {file => $file}); + return 1 if $hookArgs->{return}; + + warning TF("Macro Detector - captcha has been saved in: %s, open it, solve it and use the command: captcha <text>\n", $file), "captcha"; + + $captcha_image = ""; + $captcha_size = undef; + $captcha_key = undef; + $messageSender->sendMacroDetectorDownload() if (UNIVERSAL::isa($net, 'Network::DirectConnection')); + } +} + +# 0A5B - PACKET_ZC_MACRO_DETECTOR_SHOW +# Macro Detector UI +sub macro_detector_show { + my ($self, $args) = @_; + message T("Macro Detector\n"), "captcha"; + message TF("Remaining Chances: %s - Remaining Time: %s seconds\n", $args->{remaining_chances}, $args->{remaining_time} / 1000), "captcha"; + return unless (UNIVERSAL::isa($net, 'Network::DirectConnection')); + # TODO: check request image? +} + +# 0A5D - PACKET_ZC_MACRO_DETECTOR_STATUS +# Status of Macro Detector +sub macro_detector_status { + my ($self, $args) = @_; + my $status = T("Unknown"); + + if ($args->{status} == MCD_TIMEOUT) { + $status = T("Timeout"); + } elsif ($args->{status} == MCD_INCORRECT) { + $status = T("Incorrect"); + } elsif ($args->{status} == MCD_GOOD) { + $status = T("Correct"); + } + + message TF("Macro Detector Status: %s \n", $status), "captcha"; +} + +# 0A6A - PACKET_ZC_CAPTCHA_PREVIEW_REQUEST +# Status of Preview Captcha Image Request +sub captcha_preview { + my ($self, $args) = @_; + + $captcha_size = $args->{image_size}; + $captcha_key = $args->{captcha_key}; + + if ($args->{status} == 0) { + message T("Captcha Preview - Now you can download the image\n"); + } elsif ($args->{status} == 1) { + message T("Captcha Preview - Failed to Request Captcha (ID is out of range)\n"); + } else { + message TF("Captcha Preview - Unknown status: %s\n", $args->{status}); + } + debug TF("Captcha Preview - image_size: %s bytes - captcha_key: %s\n", $args->{image_size}, $args->{captcha_key}), "captcha"; +} + +# 0A6B - PACKET_ZC_CAPTCHA_PREVIEW_REQUEST_DOWNLOAD +# Preview a captcha image +sub captcha_preview_image { + my ($self, $args) = @_; + + $captcha_image .= $args->{captcha_image}; + + if (length($captcha_image) >= $captcha_size) { + my $image = uncompress($captcha_image); + my $imageHex = unpack("H*", $image); + + my $file = $Settings::logs_folder . "/captcha_preview_".$char->{name}."_".$captcha_key.".bmp"; + open my $DUMP, '>:raw', $file; + print $DUMP pack("H*", $imageHex); + close $DUMP; + + message TF("Captcha Preview - captcha has been saved in: %s\n", $file), "captcha"; + $captcha_image = ""; + $captcha_size = undef; + $captcha_key = undef; + } +} + +# 0A6D - PACKET_ZC_MACRO_REPORTER_SELECT +# Player List +sub macro_reporter_select { + my ($self, $args) = @_; + + message T("Macro Reporter - Account List:\n"); + for (my $i = 0; $i < length($args->{account_list}); $i += 4) { + my $accID = unpack("a4", substr($args->{account_list}, $i, 4)); + my $player = $playersList->getByID($accID); + message TF("%s\n", $player->{name}); + } +} + +# 0B8D - PACKET_ZC_REPUTE_INFO +sub repute_info { + my ($self, $args) = @_; + + @reputation_list = (); + + my $unpack = { + len => 16, + types => 'V4', + keys => [qw(type type2 points points2)], + }; + my $length = length $args->{reputeInfo}; + for (my $i = 0; $i < $length; $i += $unpack->{len}) { + my $repute; + @{$repute}{@{$unpack->{keys}}} = unpack($unpack->{types}, substr($args->{reputeInfo}, $i, $unpack->{len})); + + push @reputation_list, $repute; + } +} + +# 0A15 - PACKET_ZC_GOLDPCCAFE_POINT +# TODO: this package is not supported yet. +sub gold_pc_cafe_point { + my ($self, $args) = @_; + debug TF("[gold_pc_cafe_point] isActive=%d, mode=%d, point=%d, playedTime=%d\n", $args->{isActive}, $args->{mode}, $args->{point}, $args->{playedTime}); +} + +# 0A17 - PACKET_ZC_DYNAMICNPC_CREATE_RESULT +sub dynamicnpc_create_result { + my ($self, $args) = @_; + my $status; + + if ($args->{result} == DYNAMICNPC_RESULT_SUCCESS ) { + $status = T("Success"); + } elsif ($args->{result} == DYNAMICNPC_RESULT_UNKNOWN) { + $status = T("Unknown"); + } elsif ($args->{result} == DYNAMICNPC_RESULT_UNKNOWNNPC) { + $status = T("Unknown NPC"); + } elsif ($args->{result} == DYNAMICNPC_RESULT_DUPLICATE) { + $status = T("Duplicate"); + } elsif ($args->{result} == DYNAMICNPC_RESULT_OUTOFTIME) { + $status = T("Out of time"); + } + + message TF("Dynamic NPC create result - Status: %s\n", $status); +} + +# 0840 - PACKET_HC_NOTIFY_ACCESSIBLE_MAPNAME +sub parse_notify_accessible_mapname { + my ($self, $args) = @_; + + my $mapList = { + len => 20, + types => 'V Z16', + keys => [qw(unknown map_name)], + }; + + @{$args->{map_list}} = map { + my %map; + @map{@{$mapList->{keys}}} = unpack($mapList->{types}, $_); + \%map; + } unpack "(a$mapList->{len})*", $args->{mapList}; +} + +sub notify_accessible_mapname { + my ($self, $args) = @_; + my $map_index = 0; + + foreach my $i (0 .. $#{$args->{map_list}}) { + my $map = $args->{map_list}[$i]; + error("[notify_accessible_mapname] unknown = $map->{unknown}, name = $map->{map_name}\n"); + if (defined $config{saveMap} && $map->{map_name} =~ /$config{saveMap}/) { + $map_index = $i; + } + } + + $messageSender->sendSelectAccessibleMapname($map_index); +} + +1; diff --git a/openkore_llm_knowledge/networking/Send.pm b/openkore_llm_knowledge/networking/Send.pm new file mode 100644 index 0000000000..1af920bb73 --- /dev/null +++ b/openkore_llm_knowledge/networking/Send.pm @@ -0,0 +1,3589 @@ +######################################################################### +# OpenKore - Message sending +# This module contains functions for sending messages to the RO server. +# +# This software is open source, licensed under the GNU General Public +# License, version 2. +# Basically, this means that you're allowed to modify and distribute +# this software. However, if you distribute modified versions, you MUST +# also distribute the source code. +# See http://www.gnu.org/licenses/gpl.html for the full license. +# +# $Revision$ +# $Id$ +# +######################################################################### +## +# MODULE DESCRIPTION: Sending messages to RO server +# +# This class contains convenience methods for sending messages to the RO +# server. +# +# Please also read <a href="https://openkore.com/wiki/Network_subsystem">the +# network subsystem overview.</a> +package Network::Send; + +use strict; +use Time::HiRes qw(time); +use Network::PacketParser; # import +use base qw(Network::PacketParser); +use utf8; +use Carp::Assert; +use Digest::MD5; +use Math::BigInt; + +# TODO: remove 'use Globals' from here, instead pass vars on +use Globals qw(%ai_v %config $bytesSent %packetDescriptions $enc_val1 $enc_val2 $char $masterServer $syncSync $accountID %timeout %talk $skillExchangeItem $net $rodexList $rodexWrite %universalCatalog %rpackets $mergeItemList $repairList %cashShop); + +use I18N qw(bytesToString stringToBytes); +use Utils qw(existsInList getHex getTickCount getCoordString makeCoordsDir); +use Misc; +use Log qw(debug); + +sub new { + my ( $class ) = @_; + my $self = $class->SUPER::new( @_ ); + + my $cryptKeys = $masterServer->{sendCryptKeys}; + if ( $cryptKeys && $cryptKeys =~ /^(0x[0-9A-Fa-f]{8})\s*,\s*(0x[0-9A-Fa-f]{8})\s*,\s*(0x[0-9A-Fa-f]{8})$/ ) { + $self->cryptKeys( hex $1, hex $2, hex $3 ); + } + + return $self; +} + +sub import { + # This code is for backward compatibility reasons, so that you can still + # write: + # sendFoo(\$remote_socket, args); + + my ($package) = caller; + # This is necessary for some weird reason. + return if ($package =~ /^Network::Send/); + + package Network::Send::Compatibility; + require Exporter; + our @ISA = qw(Exporter); + require Network::Send::ServerType0; + no strict 'refs'; + + our @EXPORT_OK; + @EXPORT_OK = (); + + my $class = shift; + if (@_) { + @EXPORT_OK = @_; + } else { + @EXPORT_OK = grep {/^send/} keys(%{Network::Send::ServerType0::}); + } + + foreach my $symbol (@EXPORT_OK) { + *{$symbol} = sub { + my $remote_socket = shift; + my $func = $Globals::messageSender->can($symbol); + if (!$func) { + die "No such function: $symbol"; + } else { + return $func->($Globals::messageSender, @_); + } + }; + } + Network::Send::Compatibility->export_to_level(1, undef, @EXPORT_OK); +} + +### CATEGORY: Class methods + +### CATEGORY: Methods + +## +# void $messageSender->encryptMessageID(r_message) +sub encryptMessageID { + my ($self, $r_message) = @_; + my $messageID = unpack("v", $$r_message); + my $messageID2 = uc(unpack("H2", substr($$r_message, 1, 1))) . uc(unpack("H2", substr($$r_message, 0, 1))); + + if ($self->{encryption}->{crypt_key_3}) { + if (sprintf("%04X",$messageID) eq $self->{packet_lut}{map_login}) { + $self->{encryption}->{crypt_key} = $self->{encryption}->{crypt_key_1}; + } elsif ($self->{net}->getState() != Network::IN_GAME) { + # Turn off keys + $self->{encryption}->{crypt_key} = 0; return; + } + + # Checking if Encryption is Activated + if ($self->{encryption}->{crypt_key} > 0) { + # Saving Last Informations for Debug Log + my $oldMID = $messageID; + my $oldKey = ($self->{encryption}->{crypt_key} >> 16) & 0x7FFF; + + # Calculating the Encryption Key + $self->{encryption}->{crypt_key} = ($self->{encryption}->{crypt_key} * $self->{encryption}->{crypt_key_3} + $self->{encryption}->{crypt_key_2}) & 0xFFFFFFFF; + + # Xoring the Message ID + $messageID = ($messageID ^ (($self->{encryption}->{crypt_key} >> 16) & 0x7FFF)) & 0xFFFF; + $$r_message = pack("v", $messageID) . substr($$r_message, 2); + + # Debug Log + debug (sprintf("Encrypted MID : [%04X]->[%04X] / KEY : [0x%04X]->[0x%04X]\n", $oldMID, $messageID, $oldKey, ($self->{encryption}->{crypt_key} >> 16) & 0x7FFF), "sendPacket", 0) if ($config{debugPacket_sent} || ($config{'debugPacket_include_dumpMethod'} && !existsInList($config{debugPacket_exclude}, $messageID2) && existsInList($config{'debugPacket_include'}, $messageID2))); + } + } else { + use bytes; + if ($self->{net}->getState() != Network::IN_GAME) { + $enc_val1 = 0; + $enc_val2 = 0; + return; + } + + my $messageID = unpack("v", $$r_message); + if ($enc_val1 != 0 && $enc_val2 != 0) { + # Prepare encryption + $enc_val1 = ((0x000343FD * $enc_val1) + $enc_val2)& 0xFFFFFFFF; + debug (sprintf("enc_val1 = %x", $enc_val1) . "\n", "sendPacket", 2); + # Encrypt message ID + $messageID = ($messageID ^ (($enc_val1 >> 16) & 0x7FFF)) & 0xFFFF; + $$r_message = pack("v", $messageID) . substr($$r_message, 2); + } + } +} + +sub cryptKeys { + my $self = shift; + $self->{encryption} = { + 'crypt_key_1' => Math::BigInt->new(shift), + 'crypt_key_2' => Math::BigInt->new(shift), + 'crypt_key_3' => Math::BigInt->new(shift), + }; +} + +## +# void $messageSender->injectMessage(String message) +# +# Send text message to the connected client's party chat. +sub injectMessage { + my ($self, $message) = @_; + my $name = stringToBytes("|"); + my $msg .= $name . stringToBytes(" : $message") . chr(0); + + # Packet Prefix Encryption Support + #$self->encryptMessageID(\$msg); + + $msg = pack("C*", 0x09, 0x01) . pack("v*", length($name) + length($message) + 12) . pack("C*",0,0,0,0) . $msg; + $self->{net}->clientSend($msg); +} + +## +# void $messageSender->injectAdminMessage(String message) +# +# Send text message to the connected client's system chat. +sub injectAdminMessage { + my ($self, $message) = @_; + $message = stringToBytes($message); + $message = pack("C*",0x9A, 0x00) . pack("v*", length($message)+5) . $message .chr(0); + + # Packet Prefix Encryption Support + #$self->encryptMessageID(\$message); + $self->{net}->clientSend($message); +} + +## +# String pinEncode(int seed, int pin) +# pin: the PIN code +# key: the encryption seed/key +# +# Another version of the PIN Encode Function, used to hide the real PIN code, using seed/key. +sub pinEncode { + # randomizePin function/algorithm by Kurama, ever_boy_, kLabMouse and Iniro. cleanups by Revok + my ($seed, $pin) = @_; + + $seed = Math::BigInt->new($seed); + my $mulfactor = 0x3498; + my $addfactor = 0x881234; + my @keypad_keys_order = ('0'..'9'); + + # calculate keys order (they are randomized based on seed value) + if (@keypad_keys_order >= 1) { + my $k = 2; + for (my $pos = 1; $pos < @keypad_keys_order; $pos++) { + $seed = $addfactor + $seed * $mulfactor & 0xFFFFFFFF; # calculate next seed value + my $replace_pos = $seed % $k; + if ($pos != $replace_pos) { + my $old_value = $keypad_keys_order[$pos]; + $keypad_keys_order[$pos] = $keypad_keys_order[$replace_pos]; + $keypad_keys_order[$replace_pos] = $old_value; + } + $k++; + } + } + # associate keys values with their position using a hash + my %keypad; + for (my $pos = 0; $pos < @keypad_keys_order; $pos++) { $keypad{@keypad_keys_order[$pos]} = $pos; } + my $pin_reply = ''; + my @pin_numbers = split('',$pin); + foreach (@pin_numbers) { $pin_reply .= $keypad{$_}; } + return $pin_reply; +} + +## +# void $messageSender->sendToServer(Bytes msg) +# +# Send a raw data to the server. +sub sendToServer { + my ($self, $msg) = @_; + my $net = $self->{net}; + + shouldnt(length($msg), 0); + return unless ($net->serverAlive); + + my $messageID = uc(unpack("H2", substr($msg, 1, 1))) . uc(unpack("H2", substr($msg, 0, 1))); + + my $hookName = "packet_send/$messageID"; + if (Plugins::hasHook($hookName)) { + my %args = ( + switch => $messageID, + data => $msg + ); + Plugins::callHook($hookName, \%args); + return if ($args{return}); + } + + # Packet Prefix Encryption Support + $self->encryptMessageID(\$msg); + + $net->serverSend($msg); + $bytesSent += length($msg); + + if ($config{debugPacket_sent} && !existsInList($config{debugPacket_exclude}, $messageID) && $config{debugPacket_include_dumpMethod} < 3) { + my $label = $packetDescriptions{Send}{$messageID} ? + "[$packetDescriptions{Send}{$messageID}]" : ''; + if ($config{debugPacket_sent} == 1) { + debug(sprintf("Sent packet : %-4s [%2d bytes] %s\n", $messageID, length($msg), $label), "sendPacket", 0); + } else { + Misc::visualDump($msg, ">> Sent packet: $messageID $label"); + } + } + + if ($config{'debugPacket_include_dumpMethod'} && !existsInList($config{debugPacket_exclude}, $messageID) && existsInList($config{'debugPacket_include'}, $messageID)) { + my $label = $packetDescriptions{Send}{$messageID} ? + "[$packetDescriptions{Send}{$messageID}]" : ''; + if ($config{debugPacket_include_dumpMethod} == 2) { + Misc::visualDump($msg, ">> Sent packet: $messageID $label"); + } elsif ($config{debugPacket_include_dumpMethod} == 3 && existsInList($config{'debugPacket_include'}, $messageID)) { + #Security concern: Dump only when you included the header in config + Misc::dumpData($msg, 1, 1); + } elsif ($config{debugPacket_include_dumpMethod} == 4) { + open my $dump, '>>', 'DUMP_LINE.txt'; + print $dump unpack('H*', $msg) . "\n"; + } elsif ($config{debugPacket_include_dumpMethod} == 5 && existsInList($config{'debugPacket_include'}, $messageID)) { + #Security concern: Dump only when you included the header in config + open my $dump, '>>', 'DUMP_HEAD.txt'; + print $dump sprintf("%-4s %2d %s%s\n", $messageID, length($msg), 'Send', $label); + } + } +} + +## +# void $messageSender->sendRaw(String raw) +# raw: space-delimited list of hex byte values +# +# Send a raw data to the server. +sub sendRaw { + my ($self, $raw) = @_; + my $msg; + my @raw = split / /, $raw; + foreach (@raw) { + $msg .= pack("C", hex($_)); + } + $self->sendToServer($msg); + debug "Sent Raw Packet: @raw\n", "sendPacket", 2; +} + +# parse/reconstruct callbacks and send* subs left for compatibility + +sub parse_master_login { + my ($self, $args) = @_; + + if (exists $args->{password_md5_hex}) { + $args->{password_md5} = pack 'H*', $args->{password_md5_hex}; + } + + if (exists $args->{password_rijndael}) { + my $key = pack('C24', (6, 169, 33, 64, 54, 184, 161, 91, 81, 46, 3, 213, 52, 18, 0, 6, 61, 175, 186, 66, 157, 158, 180, 48)); + my $chain = pack('C24', (61, 175, 186, 66, 157, 158, 180, 48, 180, 34, 218, 128, 44, 159, 172, 65, 1, 2, 4, 8, 16, 32, 128)); + my $in = pack('a24', $args->{password_rijndael}); + my $rijndael = Utils::Rijndael->new; + $rijndael->MakeKey($key, $chain, 24, 24); + $args->{password} = unpack("Z24", $rijndael->Decrypt($in, undef, 24, 0)); + } +} + +sub reconstruct_master_login { + my ($self, $args) = @_; + + $args->{ip} = sprintf("192.168.%02d.%02d", (map { int(rand(255)) } 1..2)) unless exists $args->{ip}; + unless (exists $args->{mac}) { + $args->{mac} = $config{macAddress} || sprintf("E0311E%02X%02X%02X", (map { int(rand(256)) } 1..3)); + $args->{mac} = uc($args->{mac}); + $args->{mac_hyphen_separated} = join '-', $args->{mac} =~ /(..)/g; + } + $args->{isGravityID} = 0 unless exists $args->{isGravityID}; + + if (exists $args->{password}) { + for (Digest::MD5->new) { + $_->add($args->{password}); + $args->{password_md5} = $_->clone->digest; + $args->{password_md5_hex} = $_->hexdigest; + } + + my $key = pack('C24', (6, 169, 33, 64, 54, 184, 161, 91, 81, 46, 3, 213, 52, 18, 0, 6, 61, 175, 186, 66, 157, 158, 180, 48)); + my $chain = pack('C24', (61, 175, 186, 66, 157, 158, 180, 48, 180, 34, 218, 128, 44, 159, 172, 65, 1, 2, 4, 8, 16, 32, 128)); + my $in = pack('a24', $args->{password}); + my $rijndael = Utils::Rijndael->new; + $rijndael->MakeKey($key, $chain, 24, 24); + $args->{password_rijndael} = $rijndael->Encrypt($in, undef, 24, 0); + } +} + +sub sendMasterLogin { + my ($self, $username, $password, $master_version, $version) = @_; + my $msg; + + if ( + $masterServer->{masterLogin_packet} eq '' + # TODO a way to select any packet, handled globally, something like "packet_<handler> <switch>"? + or $self->{packet_list}{$masterServer->{masterLogin_packet}} + && $self->{packet_list}{$masterServer->{masterLogin_packet}}[0] eq 'master_login' + && ($self->{packet_lut}{master_login} = $masterServer->{masterLogin_packet}) + ) { + $self->sendClientMD5Hash() unless $masterServer->{clientHash} eq ''; # this is a hack, just for testing purposes, it should be moved to the login algo later on + + $msg = $self->reconstruct({ + switch => 'master_login', + version => $version || $self->version, + master_version => $master_version, + username => $username, + password => $password, + game_code => '0011', # kRO Ragnarok game code + flag => 'G000', # Maybe this say that we are connecting from client + }); + } else { + $msg = pack("v1 V", hex($masterServer->{masterLogin_packet}) || 0x64, $version || $self->version) . + pack("a24", $username) . + pack("a24", $password) . + pack("C*", $master_version); + } + + $self->sendToServer($msg); + debug "Sent sendMasterLogin\n", "sendPacket", 2; +} + +sub secureLoginHash { + my ($self, $password, $salt, $type) = @_; + my $md5 = Digest::MD5->new; + + $password = stringToBytes($password); + if ($type % 2) { + $salt = $salt . $password; + } else { + $salt = $password . $salt; + } + $md5->add($salt); + + $md5->digest +} + +sub sendMasterSecureLogin { + my ($self, $username, $password, $salt, $version, $master_version, $type, $account) = @_; + + $self->{packet_lut}{master_login} ||= $type < 3 ? '01DD' : '01FA'; + + $self->sendToServer($self->reconstruct({ + switch => 'master_login', + version => $version || $self->version, + master_version => $master_version, + username => $username, + password_salted_md5 => $self->secureLoginHash($password, $salt, $type), + clientInfo => $account > 0 ? $account - 1 : 0, + })); +} + +sub reconstruct_game_login { + my ($self, $args) = @_; + $args->{userLevel} = 0 unless exists $args->{userLevel}; + ($args->{iAccountSID}) = $masterServer->{ip} =~ /\d+\.\d+\.\d+\.(\d+)/ unless exists $args->{iAccountSID}; + + if (exists $args->{mac}) { + my $key = pack('C16', (0x06, 0xA9, 0x21, 0x40, 0x36, 0xB8, 0xA1, 0x5B, 0x51, 0x2E, 0x03, 0xD5, 0x34, 0x12, 0x00, 0x06)); + my $chain = pack('C16', (0x3D, 0xAF, 0xBA, 0x42, 0x9D, 0x9E, 0xB4, 0x30, 0xB4, 0x22, 0xDA, 0x80, 0x2C, 0x9F, 0xAC, 0x41)); + my $mac = $config{macAddress} || sprintf("E0311E%02X%02X%02X", (map { int(rand(256)) } 1..3)); + $mac = uc($mac); + my $in = pack('a16', $mac); + + my $rijndael = Utils::Rijndael->new; + $rijndael->MakeKey($key, $chain, 16, 16); + $args->{mac} = $rijndael->Encrypt($in, undef, 16, 0); + } +} + +# TODO: $masterServer->{gameLogin_packet}? +sub sendGameLogin { + my ($self, $accountID, $sessionID, $sessionID2, $sex) = @_; + $self->sendToServer($self->reconstruct({ + switch => 'game_login', + accountID => $accountID, + sessionID => $sessionID, + sessionID2 => $sessionID2, + mac => '111111111111', + accountSex => $sex, + })); + debug "Sent sendGameLogin\n", "sendPacket", 2; +} + +sub sendCharLogin { + my ($self, $char) = @_; + $self->sendToServer($self->reconstruct({switch => 'char_login', slot => $char})); + debug "Sent sendCharLogin\n", "sendPacket", 2; +} + +sub sendMapLogin { + my ($self, $accountID, $charID, $sessionID, $sex) = @_; + my $msg; + $sex = 0 if ($sex > 1 || $sex < 0); # Sex can only be 0 (female) or 1 (male) + + if ($self->{serverType} == 0 || $self->{serverType} == 17 || $self->{serverType} == 18 || $self->{serverType} == 19 || + $self->{serverType} == 20 || $self->{serverType} == 21 || $self->{serverType} == 22) { + + $msg = $self->reconstruct({ + switch => 'map_login', + accountID => $accountID, + charID => $charID, + sessionID => $sessionID, + tick => getTickCount, + sex => $sex, + }); + + } else { #oRO and pRO + my $key; + + $key = pack("C*", 0xFA, 0x12, 0, 0x50, 0x83); + $msg = pack("C*", 0x72, 0, 0, 0, 0) . + $accountID . + $key . + $charID . + pack("C*", 0xFF, 0xFF) . + $sessionID . + pack("V", getTickCount()) . + pack("C", $sex); + } + + $self->sendToServer($msg); + debug "Sent sendMapLogin\n", "sendPacket", 2; +} + +sub sendMapLoaded { + my $self = shift; + $syncSync = pack("V", getTickCount()); + debug "Sending Map Loaded\n", "sendPacket"; + $self->sendToServer($self->reconstruct({switch => 'map_loaded'})); + Plugins::callHook('packet/sendMapLoaded'); +} + +sub reconstruct_sync { + my ($self, $args) = @_; + $args->{time} = getTickCount; +} + +sub sendSync { + my ($self, $initialSync) = @_; + # XKore mode 1 lets the client take care of syncing. + return if ($self->{net}->version == 1); + + $self->sendToServer($self->reconstruct({switch => 'sync'})); + debug "Sent Sync\n", "sendPacket", 2; +} + +sub parse_character_move { + my ($self, $args) = @_; + makeCoordsDir($args, $args->{coords}); +} + +sub reconstruct_character_move { + my ($self, $args) = @_; + + $args->{no_padding} = exists $args->{no_padding} ? $args->{no_padding} : $masterServer->{serverType} == 0; + + $args->{coords} = getCoordString(@{$args}{qw(x y)}, $args->{no_padding}); +} + +sub sendMove { + my ($self, $x, $y) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'character_move', + x => $x, + y => $y + })); + + debug "Sent move to: $x, $y\n", "sendPacket", 2; +} + +sub sendAction { # flag: 0 attack (once), 7 attack (continuous), 2 sit, 3 stand + my ($self, $monID, $flag) = @_; + + my %args; + $args{monID} = $monID; + $args{flag} = $flag; + # eventually we'll trow this hooking out so... + Plugins::callHook('packet_pre/sendAttack', \%args) if $flag == ACTION_ATTACK || $flag == ACTION_ATTACK_REPEAT; + Plugins::callHook('packet_pre/sendSit', \%args) if $flag == ACTION_SIT || $flag == ACTION_STAND; + if ($args{return}) { + $self->sendToServer($args{msg}); + return; + } + + $self->sendToServer($self->reconstruct({switch => 'actor_action', targetID => $monID, type => $flag})); + debug "Sent Action: " .$flag. " on: " .getHex($monID)."\n", "sendPacket", 2; +} + +sub parse_public_chat { + my ($self, $args) = @_; + $self->parseChat($args); +} + +sub reconstruct_public_chat { + my ($self, $args) = @_; + $self->reconstructChat($args); +} + +sub sendChat { + my ($self, $message) = @_; + $self->sendToServer($self->reconstruct({switch => 'public_chat', message => $message})); +} + +sub sendGetPlayerInfo { + my ($self, $ID) = @_; + $self->sendToServer($self->reconstruct({switch => 'actor_info_request', ID => $ID})); + debug "Sent get player info: ID - ".getHex($ID)."\n", "sendPacket", 2; +} + +sub sendGetCharacterName { + my ($self, $ID) = @_; + $self->sendToServer($self->reconstruct({switch => 'actor_name_request', ID => $ID})); + debug "Sent get character name: ID - ".getHex($ID)."\n", "sendPacket", 2; +} + +sub sendTalk { + my ($self, $ID) = @_; + delete $talk{msg}; + delete $talk{image}; + $self->sendToServer($self->reconstruct({switch => 'npc_talk', ID => $ID, type => 1})); + debug "Sent talk: ".getHex($ID)."\n", "sendPacket", 2; +} + +sub sendTalkCancel { + my ($self, $ID) = @_; + undef %talk; + delete $ai_v{'npc_talk'} if (exists $ai_v{'npc_talk'}); + $self->sendToServer($self->reconstruct({switch => 'npc_talk_cancel', ID => $ID})); + debug "Sent talk cancel: ".getHex($ID)."\n", "sendPacket", 2; +} + +sub sendTalkContinue { + my ($self, $ID) = @_; + $self->sendToServer($self->reconstruct({switch => 'npc_talk_continue', ID => $ID})); + debug "Sent talk continue: ".getHex($ID)."\n", "sendPacket", 2; +} + +sub sendTalkResponse { + my ($self, $ID, $response) = @_; + delete $talk{msg}; + delete $talk{image}; + $self->sendToServer($self->reconstruct({switch => 'npc_talk_response', ID => $ID, response => $response})); + debug "Sent talk respond: ".getHex($ID).", $response\n", "sendPacket", 2; +} + +sub sendTalkNumber { + my ($self, $ID, $number) = @_; + delete $talk{msg}; + delete $talk{image}; + $self->sendToServer($self->reconstruct({switch => 'npc_talk_number', ID => $ID, value => $number})); + debug "Sent talk number: ".getHex($ID).", $number\n", "sendPacket", 2; +} + +sub sendTalkText { + my ($self, $ID, $input) = @_; + delete $talk{msg}; + delete $talk{image}; + $input = stringToBytes($input); + $self->sendToServer($self->reconstruct({ + switch => 'npc_talk_text', + len => length($input)+length($ID)+5, + ID => $ID, + text => $input + })); + debug "Sent talk text: ".getHex($ID).", $input\n", "sendPacket", 2; +} + +sub parse_private_message { + my ($self, $args) = @_; + $args->{privMsg} = bytesToString($args->{privMsg}); + Misc::stripLanguageCode(\$args->{privMsg}); + $args->{privMsgUser} = bytesToString($args->{privMsgUser}); +} + +sub reconstruct_private_message { + my ($self, $args) = @_; + $args->{privMsg} = '|00' . $args->{privMsg} if $masterServer->{chatLangCode}; + $args->{privMsg} = stringToBytes($args->{privMsg}); + $args->{privMsgUser} = stringToBytes($args->{privMsgUser}); +} + +sub sendPrivateMsg { + my ($self, $user, $message) = @_; + $self->sendToServer($self->reconstruct({ switch => 'private_message', privMsg => $message, privMsgUser => $user, })); +} + +sub sendLook { + my ($self, $body, $head) = @_; + $self->sendToServer($self->reconstruct({switch => 'actor_look_at', body => $body, head => $head})); + debug "Sent look: $body $head\n", "sendPacket", 2; + @{$char->{look}}{qw(body head)} = ($body, $head); +} + +sub sendTake { + my ($self, $itemID) = @_; + $self->sendToServer($self->reconstruct({switch => 'item_take', ID => $itemID})); + debug "Sent take\n", "sendPacket", 2; +} + +sub sendDrop { + my ($self, $ID, $amount) = @_; + $self->sendToServer($self->reconstruct({switch => 'item_drop', ID => $ID, amount => $amount})); + debug sprintf("Sent drop: %s x $amount\n", unpack('v', $ID)), "sendPacket", 2; +} + +sub sendItemUse { + my ($self, $ID, $targetID) = @_; + $self->sendToServer($self->reconstruct({switch => 'item_use', ID => $ID, targetID => $targetID})); + debug sprintf("Item Use: %s\n", unpack('v', $ID)), "sendPacket", 2; +} + +# for old plugin compatibility, use sendRestart instead! +sub sendRespawn { $_[0]->sendRestart(0) } + +# for old plugin compatibility, use sendRestart instead! +sub sendQuitToCharSelect { $_[0]->sendRestart(1) } + +# 0x00b2,3,restart,2 +# type: 0=respawn ; 1=return to char select +sub sendRestart { + my ($self, $type) = @_; + $self->sendToServer($self->reconstruct({switch => 'restart', type => $type})); + debug "Sent Restart: " . ($type ? 'Quit To Char Selection' : 'Respawn') . "\n", "sendPacket", 2; +} + +sub sendStorageAdd { + my ($self, $ID, $amount) = @_; + if ($config{storageAuto_type} == 1) { + $self->sendToServer($self->reconstruct({switch => 'guild_storage_item_add', ID => $ID, amount => $amount})); + } else { + $self->sendToServer($self->reconstruct({switch => 'storage_item_add', ID => $ID, amount => $amount})); + } + debug sprintf("Sent Storage Add: %s x $amount\n", unpack('v', $ID)), "sendPacket", 2; +} + +sub sendStorageGet { + my ($self, $ID, $amount) = @_; + if ($config{storageAuto_type} == 1) { + $self->sendToServer($self->reconstruct({switch => 'guild_storage_item_remove', ID => $ID, amount => $amount})); + } else { + $self->sendToServer($self->reconstruct({switch => 'storage_item_remove', ID => $ID, amount => $amount})); + } + debug sprintf("Sent Storage Get: %s x $amount\n", unpack('v', $ID)), "sendPacket", 2; +} + +sub sendStoragePassword { + my ($self, $pass, $type) = @_; + + # $pass -> 16 byte packed hex data + + $self->sendToServer($self->reconstruct({ + switch => 'storage_password', + type => $type, + pass => $pass, + })); +} + +sub reconstruct_storage_password { + my ($self, $args) = @_; + + my $aux = pack "H*", "EC62E539BB6BBC811A60C06FACCB7EC8"; + + # $type == 2 -> change password + # $type == 3 -> check password + + if ($args->{type} == 3) { + $args->{data} = pack '(a*)*', $args->{pass}, $aux; + } elsif ($args->{type} == 2) { + $args->{data} = pack '(a*)*', $aux, $args->{pass}; + } else { + ArgumentException->throw("The 'type' argument has invalid value ($args->{type})."); + } +} + +sub parse_party_chat { + my ($self, $args) = @_; + $self->parseChat($args); +} + +sub reconstruct_party_chat { + my ($self, $args) = @_; + $self->reconstructChat($args); +} + +sub sendPartyChat { + my ($self, $message) = @_; + $self->sendToServer($self->reconstruct({switch => 'party_chat', message => $message})); +} + + +sub parse_buy_bulk_vender { + my ($self, $args) = @_; + @{$args->{items}} = map {{ amount => unpack('v', $_), itemIndex => unpack('x2 v', $_) }} unpack '(a4)*', $args->{itemInfo}; +} + +sub reconstruct_buy_bulk_vender { + my ($self, $args) = @_; + # ITEM index. There were any other indexes expected to be in item buying packet? + $args->{itemInfo} = pack '(a4)*', map { pack 'v2', @{$_}{qw(amount itemIndex)} } @{$args->{items}}; +} + +# not "buy", it sells items! +sub sendBuyBulkVender { + my ($self, $venderID, $r_array, $venderCID) = @_; + $self->sendToServer($self->reconstruct({ + switch => 'buy_bulk_vender', + venderID => $venderID, + venderCID => $venderCID, + items => $r_array, + })); + debug "Sent bulk buy vender: ".(join ', ', map {"$_->{itemIndex} x $_->{amount}"} @$r_array)."\n", "sendPacket"; +} + +sub reconstruct_buy_bulk_buyer { + my ($self, $args) = @_; + my $packet_size = $self->{buy_bulk_buyer_size} || '(a6)*'; + my $packet_unpack = $self->{buy_bulk_buyer_size_unpack} || 'a2 v2'; + $args->{itemInfo} = pack($packet_size, map { pack $packet_unpack, @{$_}{qw(ID itemID amount)} } @{$args->{items}}); +} + +sub sendBuyBulkBuyer { + my ($self, $buyerID, $r_array, $buyingStoreID) = @_; + + my $len = 12 + (scalar @{$r_array} * 8); + + $self->sendToServer($self->reconstruct({ + switch => 'buy_bulk_buyer', + len => $len, + buyerID => $buyerID, + buyingStoreID => $buyingStoreID, + items => $r_array, + })); +} + +sub sendEnteringBuyer { + my ($self, $ID) = @_; + $self->sendToServer($self->reconstruct({switch => 'buy_bulk_request', ID => $ID})); + debug "Sent Entering Buyer: ID - ".getHex($ID)."\n", "sendPacket", 2; +} + +sub sendBuyBulkOpenShop { + my ($self, $limitZeny, $result, $storeName, @items) = @_; + + my $len = 89 + (($#items + 1) * 8); + + $self->sendToServer($self->reconstruct({ + switch => 'buy_bulk_openShop', + len => $len, + limitZeny => $limitZeny, + result => $result, + storeName => $storeName, + items => @items, + })); + + debug "Sent Buyer openShop Request\n", "sendPacket", 2; +} + +sub reconstruct_buy_bulk_openShop { + my ($self, $args) = @_; + my $packet_size = $self->{buy_bulk_openShop_size} || '(a8)*'; + my $packet_unpack = $self->{buy_bulk_openShop_size_unpack} || 'v2 V'; + $args->{itemInfo} = pack $packet_size, map { pack $packet_unpack, @{$_}{qw(nameID amount price)} } @{$args->{items}}; +} + +sub sendSkillUse { + my ($self, $ID, $lv, $targetID) = @_; +### need to check Hook### + my %args; + Plugins::callHook('packet_pre/sendSkillUse', \%args); + if ($args{return}) { + $self->sendToServer($args{msg}); + return; + } +########################## + $self->sendToServer($self->reconstruct({switch => 'skill_use', lv => $lv, skillID => $ID, targetID => $targetID})); + debug "Skill Use: $ID\n", "sendPacket", 2; +} + +sub sendSkillUseLoc { + my ($self, $ID, $lv, $x, $y) = @_; + $self->sendToServer($self->reconstruct({switch => 'skill_use_location', lv => $lv, skillID => $ID, x => $x, y => $y})); + debug "Skill Use on Location: $ID, ($x, $y)\n", "sendPacket", 2; +} + +sub sendGuildMasterMemberCheck { + my ($self, $ID) = @_; + $self->sendToServer($self->reconstruct({switch => 'guild_check'})); + debug "Sent Guild Master/Member Check.\n", "sendPacket"; +} + +sub sendGuildRequestInfo { + my ($self, $page) = @_; # page 0-4 + $self->sendToServer($self->reconstruct({ + switch => 'guild_info_request', + type => $page, + })); + debug "Sent Guild Request Page : ".$page."\n", "sendPacket"; +} + +sub parse_guild_chat { + my ($self, $args) = @_; + $self->parseChat($args); +} + +sub reconstruct_guild_chat { + my ($self, $args) = @_; + $self->reconstructChat($args); +} + +sub sendGuildChat { + my ($self, $message) = @_; + $self->sendToServer($self->reconstruct({switch => 'guild_chat', message => $message})); +} + +sub sendQuit { + my $self = shift; + $self->sendToServer($self->reconstruct({switch => 'quit_request', type => 0})); + debug "Sent Quit\n", "sendPacket", 2; +} + +sub sendCloseShop { + my $self = shift; + $self->sendToServer($self->reconstruct({switch => 'shop_close'})); + debug "Shop Closed\n", "sendPacket", 2; +} + +# 0x0102,6,partychangeoption,2:4 +# 0x07D7 +# note: item share changing seems disabled in newest clients +sub sendPartyOption { + my ($self, $exp, $itemPickup, $itemDivision) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'party_setting', + exp => $exp, + itemPickup => $itemPickup, + itemDivision => $itemDivision, + })); + debug "Sent Party Option\n", "sendPacket", 2; +} + +# 0x7DA +sub sendPartyLeader { + my ($self, $ID) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'party_leader', + accountID => $ID, + })); + + debug "Sent Change Party Leader ".getHex($ID)."\n", "sendPacket", 2; +} + +# 0x802 +sub sendPartyBookingRegister { + my ($self, $level, $MapID, @jobList) = @_; + + $self->sendToServer($self->reconstruct({switch => 'booking_register', level => $level, MapID => $MapID, + job0 => @jobList[0], job1 => @jobList[1], job2 => @jobList[2], + job3 => @jobList[3], job4 => @jobList[4], job5 => @jobList[5]})); + + debug "Sent Booking Register\n", "sendPacket", 2; +} + +# 0x804 +sub sendPartyBookingReqSearch { + my ($self, $level, $MapID, $job, $LastIndex, $ResultCount) = @_; + + $job = "65535" if ($job == 0); # job null = 65535 + $ResultCount = "10" if ($ResultCount == 0); # ResultCount defaut = 10 + + $self->sendToServer($self->reconstruct({switch => 'booking_search', level => $level, MapID => $MapID, job => $job, LastIndex => $LastIndex, ResultCount => $ResultCount})); + debug "Sent Booking Search\n", "sendPacket", 2; +} + +# 0x806 +sub sendPartyBookingDelete { + my $self = shift; + $self->sendToServer($self->reconstruct({switch => 'booking_delete'})); + debug "Booking Deleted\n", "sendPacket", 2; +} + +# 0x808 +sub sendPartyBookingUpdate { + my ($self, @jobList) = @_; + + $self->sendToServer($self->reconstruct({switch => 'booking_update', job0 => @jobList[0], + job1 => @jobList[1], job2 => @jobList[2], job3 => @jobList[3], + job4 => @jobList[4], job5 => @jobList[5]})); + + debug "Sent Booking Update\n", "sendPacket", 2; +} + +# 0x0827,6 +sub sendCharDelete2 { + my ($self, $charID) = @_; + + $self->sendToServer($self->reconstruct({switch => 'char_delete2', charID => $charID})); + debug "Sent sendCharDelete2\n", "sendPacket", 2; +} + +## +# switch: 0x0829,12: '0829' => ['char_delete2_accept', 'a4 a6', [qw(charID code)]], # 12 -> kRO +# switch: 0x098F,-1: '098f' => ['char_delete2_accept', 'v a4 a*', [qw(length charID code)]], -> idRO, iRO Renewal +sub sendCharDelete2Accept { + my ($self, $charID, $code) = @_; + + $self->sendToServer($self->reconstruct({switch => 'char_delete2_accept', charID => $charID, code => $code})); +} + +sub reconstruct_char_delete2_accept { + my ($self, $args) = @_; + debug "Sent sendCharDelete2Accept. CharID: $args->{charID}, Code: $args->{code}\n", "sendPacket", 2; +} + +# 0x082B,6 +sub sendCharDelete2Cancel { + my ($self, $charID) = @_; + + $self->sendToServer($self->reconstruct({switch => 'char_delete2_cancel', charID => $charID})); + debug "Sent sendCharDelete2Cancel\n", "sendPacket", 2; +} + +sub reconstruct_client_hash { + my ($self, $args) = @_; + + if (defined $args->{code}) { + # FIXME there's packet switch in that code. How to handle it correctly? + my $code = $args->{code}; + $code =~ s/^02 04 //; + + $args->{hash} = pack 'C*', map hex, split / /, $code; + + } elsif ($args->{type}) { + if ($args->{type} == 1) { + $args->{hash} = pack('C*', 0x7B, 0x8A, 0xA8, 0x90, 0x2F, 0xD8, 0xE8, 0x30, 0xF8, 0xA5, 0x25, 0x7A, 0x0D, 0x3B, 0xCE, 0x52); + } elsif ($args->{type} == 2) { + $args->{hash} = pack('C*', 0x27, 0x6A, 0x2C, 0xCE, 0xAF, 0x88, 0x01, 0x87, 0xCB, 0xB1, 0xFC, 0xD5, 0x90, 0xC4, 0xED, 0xD2); + } elsif ($args->{type} == 3) { + $args->{hash} = pack('C*', 0x42, 0x00, 0xB0, 0xCA, 0x10, 0x49, 0x3D, 0x89, 0x49, 0x42, 0x82, 0x57, 0xB1, 0x68, 0x5B, 0x85); + } elsif ($args->{type} == 4) { + $args->{hash} = pack('C*', 0x22, 0x37, 0xD7, 0xFC, 0x8E, 0x9B, 0x05, 0x79, 0x60, 0xAE, 0x02, 0x33, 0x6D, 0x0D, 0x82, 0xC6); + } elsif ($args->{type} == 5) { + $args->{hash} = pack('C*', 0xc7, 0x0A, 0x94, 0xC2, 0x7A, 0xCC, 0x38, 0x9A, 0x47, 0xF5, 0x54, 0x39, 0x7C, 0xA4, 0xD0, 0x39); + } + } +} + +# TODO: clientHash and secureLogin_requestCode is almost the same, merge +sub sendClientMD5Hash { + my ($self) = @_; + $self->sendToServer($self->reconstruct({ + switch => 'client_hash', + hash => pack('H32', $masterServer->{clientHash}), # ex 82d12c914f5ad48fd96fcf7ef4cc492d (kRO sakray != kRO main) + })); +} + +sub parse_actor_move { + my ($self, $args) = @_; + makeCoordsDir($args, $args->{coords}); +} + +sub reconstruct_actor_move { + my ($self, $args) = @_; + + $args->{no_padding} = exists $args->{no_padding} ? $args->{no_padding} : !($masterServer->{serverType} > 0); + + $args->{coords} = getCoordString(@{$args}{qw(x y)}, $args->{no_padding}); +} + +sub sendSlaveMove { + my ($self, $ID, $x, $y) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'actor_move', + ID => $ID, + x => $x, + y => $y, + })); + + debug sprintf("Sent %s move to: %d, %d\n", Actor::get($ID), $x, $y), "sendPacket", 2; +} + +sub sendFriendListReply { + my ($self, $accountID, $charID, $flag) = @_; + $self->sendToServer($self->reconstruct({ + switch => 'friend_response', + friendAccountID => $accountID, + friendCharID => $charID, + type => $flag, + })); + debug "Sent Reject friend request\n", "sendPacket"; +} + +sub sendFriendRequest { + my ($self, $name) = @_; + my $binName = stringToBytes($name); + $binName = substr($binName, 0, 24) if (length($binName) > 24); + $binName = $binName . chr(0) x (24 - length($binName)); + $self->sendToServer($self->reconstruct({ + switch => 'friend_request', + username => $binName, + + })); + debug "Sent Request to be a friend: $name\n", "sendPacket"; +} + +sub sendHomunculusCommand { + my ($self, $command, $type) = @_; # $type is ignored, $command can be 0:get stats, 1:feed or 2:fire + $self->sendToServer($self->reconstruct({ + switch => 'homunculus_command', + commandType => $type, + commandID => $command, + })); + debug "Sent Homunculus Command $command", "sendPacket", 2; +} + +sub sendPartyJoinRequestByName +{ + my ($self, $name) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'party_join_request_by_name', + partyName => stringToBytes ($name), + })); + + debug "Sent Request Join Party (by name): $name\n", "sendPacket", 2; +} + +sub sendSkillSelect { + my ($self, $skillID, $why) = @_; + $self->sendToServer($self->reconstruct({ + switch => 'skill_select', + skillID => $skillID, + why => $why, + })); + debug sprintf("Sent Skill Select (skillID: %d, why: %d)", $skillID, $why), 'sendPacket', 2; +} + +sub sendReplySyncRequestEx { + my ($self, $SyncID) = @_; + # Packing New Message and Dispatching + + $self->sendToServer(pack("v", $SyncID)); + # Debug Log + # print "Dispatching Sync Ex Reply : 0x" . $pid . "\n"; + # Debug Log + debug "Sent Reply Sync Request Ex\n", "sendPacket", 2; +} + +sub sendLoginPinCode { + my ($self, $seed, $type) = @_; + + my $pin = pinEncode($seed, $config{loginPinCode}); + my $msg; + if ($type == 0) { + $msg = $self->reconstruct({ + switch => 'send_pin_password', + accountID => $accountID, + pin => $pin, + }); + } elsif ($type == 1) { + $msg = $self->reconstruct({ + switch => 'new_pin_password', + accountID => $accountID, + pin => $pin, + }); + } + $self->sendToServer($msg); + $timeout{charlogin}{time} = time; + debug "Sent loginPinCode\n", "sendPacket", 2; +} + +sub sendCloseBuyShop { + my $self = shift; + $self->sendToServer($self->reconstruct({switch => 'buy_bulk_closeShop'})); + debug "Buying Shop Closed\n", "sendPacket", 2; +} + +sub sendRequestCashItemsList { + my $self = shift; + $self->sendToServer($self->reconstruct({switch => 'request_cashitems'})); + debug "Requesting cashItemsList\n", "sendPacket", 2; +} + +sub sendCashShopOpen { + my ($self) = @_; + $self->sendToServer($self->reconstruct({switch => 'cash_shop_open'})); + debug "Requesting sendCashShopOpen\n", "sendPacket", 2; +} + +sub sendCashShopClose { + my ($self) = @_; + $self->sendToServer($self->reconstruct({switch => 'cash_shop_close'})); + undef $cashShop{points}; + debug "Requesting sendCashShopClose\n", "sendPacket", 2; +} + +sub sendCashBuy { + my ($self, $kafra_points, $items) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'cash_shop_buy', + kafra_points => $kafra_points, + count => scalar @{$items}, + items => $items, + })); + + debug "Requesting cash shop buy\n", "sendPacket", 2; +} + +sub reconstruct_cash_shop_buy { + my ($self, $args) = @_; + + $args->{buy_info} = pack '(a*)*', map { pack 'V V v', $_->{nameID}, $_->{amount}, $_->{tab} } @{$args->{items}}; + # Some older clients (prior to 2013, I don't know the exact date) use 'v3' instead of 'V2 v' - lututui + # $args->{buy_info} = pack '(a*)*', map { pack 'v v v', $_->{nameID}, $_->{amount}, $_->{tab} } @{$args->{items}}; +} + +sub sendShowEquipPlayer { + my ($self, $ID) = @_; + $self->sendToServer($self->reconstruct({ + switch => 'view_player_equip_request', + ID => $ID + } + ) + ); + debug "Sent Show Equip Player.\n", "sendPacket", 2; +} + +# Send configurations (CZ_CONFIG). +# 02D8 <type>.L <value>.L +# type: +# 0 = show equip windows to other players +# 1 = being summoned by skills: Urgent Call, Romantic Rendezvous, Come to me, honey~ & Let's Go, Family! +# 2 = pet autofeeding +# 3 = homunculus autofeeding +# value: +# 0 = disabled +# 1 = enabled +sub sendMiscConfigSet { + my ($self, $type, $flag) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'misc_config_set', + type => $type, + flag => $flag + } + ) + ); + + debug sprintf("Sent Misc Config Type: %s Flag: %s.\n", $type, $flag), "sendPacket", 2; +} + +sub sendSlaveAttack { + my $self = shift; + my $slaveID = shift; + my $targetID = shift; + my $flag = shift; + $self->sendToServer($self->reconstruct({ + switch => 'slave_attack', + slaveID => $slaveID, + targetID => $targetID, + flag => $flag + } + ) + ); + debug "Sent Slave attack: ".getHex($targetID)."\n", "sendPacket", 2; +} + +sub sendSlaveStandBy { + my $self = shift; + my $slaveID = shift; + $self->sendToServer($self->reconstruct({ + switch => 'slave_move_to_master', + slaveID => $slaveID + } + ) + ); + debug "Sent Slave standby\n", "sendPacket", 2; +} + +# Request to equip an item +# 00A9 <index>.W <position>.W (CZ_REQ_WEAR_EQUIP). +# 0998 <index>.W <position>.L (CZ_REQ_WEAR_EQUIP_V5) +sub sendEquip { + my ($self, $ID, $type) = @_; + $self->sendToServer($self->reconstruct({ + switch => 'send_equip', + ID => $ID, + type => $type + } + ) + ); + debug sprintf("Sent Equip: %s Type: $type\n", unpack('v', $ID)), 2; +} + +# Request to add an equip to the equip switch window +# 0A97 <index>.W <position>.L +sub sendEquipSwitchAdd { + my ($self, $ID, $position) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'equip_switch_add', + ID => $ID, + position => $position, + })); + + debug sprintf("Sent Equip Switch Add Item: %s\n", unpack('v', $ID)), "sendPacket", 2; +} + +# Request to remove an equip from the equip switch window +# 0A99 <index>.W <position>.L <= 20170502 +# 0A99 <index>.W +sub sendEquipSwitchRemove { + my ($self, $ID, $position) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'equip_switch_remove', + ID => $ID, + position => $position, + })); + + debug sprintf("Sent Equip Switch Remove Item: %s\n", unpack('v', $ID)), "sendPacket", 2; +} + +# Request to do a full equip switch +# 0A9C +sub sendEquipSwitchRun { + my ($self) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'equip_switch_run' + })); + + debug "Sent Equip Switch All\n", "sendPacket", 2; +} + +# Request to do a single equip switch +# 0ACE <index>.W +sub sendEquipSwitchSingle { + my ($self, $ID) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'equip_switch_single', + ID => $ID + })); + + debug sprintf("Sent Equip Switch Single Item: %s\n", unpack('v', $ID)), "sendPacket", 2; +} + +sub sendProgress { + my ($self) = @_; + + $self->sendToServer($self->reconstruct({switch => 'notify_progress_bar_complete'})); + + debug "Sent Progress Bar Finish\n", "sendPacket", 2; +} + +sub sendDealAddItem { + my ($self, $ID, $amount) = @_; + $self->sendToServer($self->reconstruct({ + switch => 'deal_item_add', + ID => $ID, + amount => $amount + })); + debug sprintf("Sent Deal Add Item: %s, $amount\n", unpack('v', $ID)), "sendPacket", 2; +} + +## +# sendItemListWindowSelected +# @param num Number of items +# @param type 0: Change Material +# 1: Elemental Analysis (Level 1: Pure to Rough) +# 2: Elemental Analysis (Level 1: Rough to Pure) +# @param act 0: Cancel +# 1: Process +# @param items List of items [itemIndex,amount,itemName] +# @author [Cydh] +## +sub sendItemListWindowSelected { + my ($self, $num, $type, $act, $items) = @_; + my $len = ($num * 4) + 12; + $self->sendToServer($self->reconstruct({ + switch => 'item_list_window_selected', + len => $len, + type => $type, + act => $act, + items => $items, + })); + if ($act == 1) { + debug "Selected ".(scalar @{$items})." items: ".(join ', ', map {"".$_->{amount}." x ".$_->{itemName}." (binID:".$_->{itemIndex}.")"} @{$items})."\n", "sendPacket"; + } else { + debug "Selected items were canceled.\n", "sendPacket"; + } + undef $skillExchangeItem; +} + +sub reconstruct_item_list_window_selected { + my ($self, $args) = @_; + $args->{itemInfo} = pack '(a4)*', map { pack 'v2', @{$_}{qw(itemIndex amount)} } @{$args->{items}}; +} + +# Select equip for refining +# '0AA1' => ['refineui_select', 'a2' ,[qw(index)]], +# @param itemIndex OpenKore's Inventory Item Index +# @author [Cydh] +sub sendRefineUISelect { + my ($self, $itemIndex) = @_; + $self->sendToServer($self->reconstruct({ + switch => 'refineui_select', + index => $itemIndex, + })); + debug "Checking item for RefineUI\n", "sendPacket"; +} + +# Continue to refine equip +# '0AA3' => ['refineui_refine', 'a2 v C' ,[qw(index catalyst bless)]], +# @param itemIndex OpenKore's Inventory Item Index +# @param materialNameIDMaterial's NameID +# @param useCatalyst Catalyst (Blacksmith Blessing) toggle. 0 = Not using, 1 = Use catalyst +# @author [Cydh] +sub sendRefineUIRefine { + my ($self, $itemIndex, $materialNameID, $useCatalyst) = @_; + $self->sendToServer($self->reconstruct({ + switch => 'refineui_refine', + index => $itemIndex, + catalyst => $materialNameID, + bless => $useCatalyst, + })); + debug "Refining using RefineUI\n", "sendPacket"; +} + +# Cancel RefineUI usage +# '0AA4' => ['refineui_close', '' ,[qw()]], +# @author [Cydh] +sub sendRefineUIClose { + my $self = shift; + $self->sendToServer($self->reconstruct({switch => 'refineui_close'})); + debug "Closing RefineUI\n", "sendPacket"; +} + +sub sendTokenToServer { + my ($self, $username, $password, $master_version, $version, $token, $length, $ip, $port) = @_; + my $len = $length + 92; + + my $password_rijndael = $self->encrypt_password($password); + my $ip = '192.168.0.14'; + my $mac = '20CF3095572A'; + my $mac_hyphen_separated = join '-', $mac =~ /(..)/g; + + $net->serverDisconnect(); + $net->serverConnect($ip, $port);# OTP - One Time Password + + my $msg = $self->reconstruct({ + switch => 'token_login', + len => $len, # size of packet + version => $version || $self->version, + master_version => $master_version, + username => $username, + password => $password, + password_rijndael => $password_rijndael, + mac => $mac_hyphen_separated, + ip => $ip, + token => $token, + }); + + $self->sendToServer($msg); + + debug "Sent sendTokenLogin\n", "sendPacket", 2; +} + +# Send OTP code for authentication (packet 0C23) +# 0C23 <otp>.a6 <padding>.C +# otp: 6-digit One-Time Password (TOTP) +# padding: 0x00 (always 0) +sub sendOtpToServer { + my ($self, $otp) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'send_otp_login', + otp => $otp, + padding => 0x00, + })); + + debug "Sent OTP login packet: $otp\n", "sendPacket", 2; +} + +# encrypt password kRO/cRO version 2017-2018 +sub encrypt_password { + my ($self, $password) = @_; + my $password_rijndael; + if (defined $password) { + my $key = pack('C32', (0x06, 0xA9, 0x21, 0x40, 0x36, 0xB8, 0xA1, 0x5B, 0x51, 0x2E, 0x03, 0xD5, 0x34, 0x12, 0x00, 0x06, 0x06, 0xA9, 0x21, 0x40, 0x36, 0xB8, 0xA1, 0x5B, 0x51, 0x2E, 0x03, 0xD5, 0x34, 0x12, 0x00, 0x06)); + my $chain = pack('C32', (0x3D, 0xAF, 0xBA, 0x42, 0x9D, 0x9E, 0xB4, 0x30, 0xB4, 0x22, 0xDA, 0x80, 0x2C, 0x9F, 0xAC, 0x41, 0x3D, 0xAF, 0xBA, 0x42, 0x9D, 0x9E, 0xB4, 0x30, 0xB4, 0x22, 0xDA, 0x80, 0x2C, 0x9F, 0xAC, 0x41)); + my $in = pack('a32', $password); + my $rijndael = Utils::Rijndael->new; + $rijndael->MakeKey($key, $chain, 32, 32); + $password_rijndael = unpack("Z32", $rijndael->Encrypt($in, undef, 32, 0)); + return $password_rijndael; + } else { + error("Password is not configured"); + } +} + +sub sendReqRemainTime { + my ($self) = @_; + + my $msg = $self->reconstruct({ + switch => 'request_remain_time', + }); + + $self->sendToServer($msg); +} + +sub sendBlockingPlayerCancel { + my ($self) = @_; + # XKore mode 1 / 3. + return if ($self->{net}->version == 1); + my $msg = $self->reconstruct({ + switch => 'blocking_play_cancel', + }); + + $self->sendToServer($msg); +} + + +sub rodex_delete_mail { + my ($self, $type, $mailID1, $mailID2) = @_; + $self->sendToServer($self->reconstruct({ + switch => 'rodex_delete_mail', + type => $type, + mailID1 => $mailID1, + mailID2 => $mailID2, + })); +} + +sub rodex_request_zeny { + my ($self, $mailID1, $mailID2, $type) = @_; + $self->sendToServer($self->reconstruct({ + switch => 'rodex_request_zeny', + mailID1 => $mailID1, + mailID2 => $mailID2, + type => $type, + })); +} + +sub rodex_request_items { + my ($self, $mailID1, $mailID2, $type) = @_; + $self->sendToServer($self->reconstruct({ + switch => 'rodex_request_items', + mailID1 => $mailID1, + mailID2 => $mailID2, + type => $type, + })); +} + +sub rodex_cancel_write_mail { + my ($self) = @_; + $self->sendToServer($self->reconstruct({ + switch => 'rodex_cancel_write_mail', + })); + undef $rodexWrite; +} + +sub rodex_add_item { + my ($self, $ID, $amount) = @_; + $self->sendToServer($self->reconstruct({ + switch => 'rodex_add_item', + ID => $ID, + amount => $amount, + })); +} + +sub rodex_remove_item { + my ($self, $ID, $amount) = @_; + $self->sendToServer($self->reconstruct({ + switch => 'rodex_remove_item', + ID => $ID, + amount => $amount, + })); +} + +sub rodex_open_write_mail { + my ($self, $name) = @_; + $self->sendToServer($self->reconstruct({ + switch => 'rodex_open_write_mail', + name => stringToBytes($name), + })); +} + +sub rodex_checkname { + my ($self, $name) = @_; + $self->sendToServer($self->reconstruct({ + switch => 'rodex_checkname', + name => stringToBytes($name), + })); +} + +#note ! +#merge sub of 0A6E / 09EC ? [sctnightcore] +sub rodex_send_mail { + my ($self) = @_; + + my $title = stringToBytes($rodexWrite->{title}) . chr(0); + my $body = stringToBytes($rodexWrite->{body}) . chr(0); + my $pack = $self->reconstruct({ + switch => 'rodex_send_mail', + receiver => $rodexWrite->{target}{name}, + sender => stringToBytes($char->{name}), + zeny1 => $rodexWrite->{zeny}, + zeny2 => 0, + title_len => length $title, + body_len => length $body, + char_id => $rodexWrite->{target}{char_id}, + title => $title, + body => $body, + }); + + $self->sendToServer($pack); +} + +sub rodex_refresh_maillist { + my ($self, $type, $mailID1, $mailID2) = @_; + $self->sendToServer($self->reconstruct({ + switch => 'rodex_refresh_maillist', + type => $type, + mailID1 => $mailID1, + mailID2 => $mailID2, + # seems that is not current used by client/server 2019-09-16 + mailReturnID1 => 0, + mailReturnID2 => 0, + mailAccountID1 => 0, + mailAccountID2 => 0, + })); +} + +sub rodex_read_mail { + my ($self, $type, $mailID1, $mailID2) = @_; + $self->sendToServer($self->reconstruct({ + switch => 'rodex_read_mail', + type => $type, + mailID1 => $mailID1, + mailID2 => $mailID2, + })); +} + +sub rodex_next_maillist { + my ($self, $type, $mailID1, $mailID2) = @_; + $self->sendToServer($self->reconstruct({ + switch => 'rodex_next_maillist', + type => $type, + mailID1 => $mailID1, + mailID2 => $mailID2, + })); +} + +sub rodex_open_mailbox { + my ($self, $type, $mailID1, $mailID2) = @_; + $self->sendToServer($self->reconstruct({ + switch => 'rodex_open_mailbox', + type => $type, + mailID1 => $mailID1, + mailID2 => $mailID2, + # seems that is not current used by client/server 2019-09-16 + mailReturnID1 => 0, + mailReturnID2 => 0, + mailAccountID1 => 0, + mailAccountID2 => 0, + })); +} + +sub rodex_close_mailbox { + my ($self) = @_; + $self->sendToServer($self->reconstruct({ + switch => 'rodex_close_mailbox', + })); + undef $rodexList; +} + +sub sendEnteringVender { + my ($self, $accountID) = @_; + $self->sendToServer($self->reconstruct({ + switch => 'send_entering_vending', + accountID => $accountID, + })); +} + +sub sendUnequip { + my ($self, $ID) = @_; + $self->sendToServer($self->reconstruct({ + switch => 'send_unequip_item', + ID => $ID, + })); +} + +sub sendAddStatusPoint { + my ($self, $ID,$Amount) = @_; + $self->sendToServer($self->reconstruct({ + switch => 'send_add_status_point', + statusID => $ID, + Amount => '1', + })); +} + +sub sendAddSkillPoint { + my ($self, $skillID) = @_; + $self->sendToServer($self->reconstruct({ + switch => 'send_add_skill_point', + skillID => $skillID, + })); +} + +sub sendHotKeyChange { + my ($self, $args) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'hotkey_change', + idx => $args->{idx}, + type => $args->{type}, + id => $args->{id}, + lvl => $args->{lvl}, + })); +} + +sub sendQuestState { + my ($self, $questID,$state) = @_; + $self->sendToServer($self->reconstruct({ + switch => 'send_quest_state', + questID => $questID, + state => $state, #TODO:[active=0x00],[inactive=0x01] + })); + debug "Sent Quest State.\n", "sendPacket", 2; +} + +sub sendClanChat { + my ($self, $message) = @_; + $message = $char->{name}." : ".$message; + $self->sendToServer($self->reconstruct({switch => 'clan_chat', len => length($message) + 4,message => $message})); +} + +sub sendchangetitle { + my ($self, $title_id) = @_; + $self->sendToServer($self->reconstruct({ + switch => 'send_change_title', + ID => $title_id, + })); + debug "Sent Change Title.\n", "sendPacket", 2; +} + +sub sendRecallSso { + my ($self, $accountID) = @_; + $self->sendToServer($self->reconstruct({ + switch => 'recall_sso', + ID => $accountID, + })); +} + +sub sendRemoveAidSso { + my ($self, $accountID) = @_; + $self->sendToServer($self->reconstruct({ + switch => 'remove_aid_sso', + ID => $accountID, + })); +} + +sub sendMacroStart { + my ($self) = @_; + $self->sendToServer($self->reconstruct({ + switch => 'macro_start', + })); +} + +sub sendMacroStop { + my ($self) = @_; + $self->sendToServer($self->reconstruct({ + switch => 'macro_stop', + })); +} + +sub sendReqCashTabCode { + my ($self, $tabID) = @_; + $self->sendToServer($self->reconstruct({ + switch => 'req_cash_tabcode', + ID => $tabID, + })); +} + +sub parse_pet_evolution { + my ($self, $args) = @_; + @{$args->{items}} = map {{ itemIndex => unpack('v', $_), amount => unpack('x2 v', $_) }} unpack '(a4)*', $args->{itemInfo}; +} + +sub reconstruct_pet_evolution { + my ($self, $args) = @_; + $args->{itemInfo} = pack '(a4)*', map { pack 'v2', @{$_}{qw(itemIndex amount)} } @{$args->{items}}; +} + +sub sendPetEvolution { + my ($self, $peteggid, $r_array) = @_; + $self->sendToServer($self->reconstruct({ + switch => 'pet_evolution', + ID => $peteggid, + items => $r_array, + })); +} + +sub sendWeaponRefine { + my ($self, $ID) = @_; + + my $msg = $self->reconstruct({ + switch => 'refine_item', + ID => $ID, + }); + + $self->sendToServer($msg); + + debug "Sent Weapon Refine.\n", "sendPacket", 2; +} + +sub sendCooking { + my ($self, $type, $nameID) = @_; + $self->sendToServer($self->reconstruct({ + switch => 'cook_request', + nameID => $nameID, + type => $type, + })); + debug "Sent Cooking.\n", "sendPacket", 2; +} + +sub sendMakeItemRequest { + my ($self, $nameID, $material_nameID1, $material_nameID2, $material_nameID3) = @_; + $self->sendToServer($self->reconstruct({ + switch => 'make_item_request', + nameID => $nameID, + material_nameID1 => $material_nameID1, + material_nameID2 => $material_nameID2, + material_nameID3 => $material_nameID3, + })); + debug "Sent Make Item Request.\n", "sendPacket", 2; +} + +sub sendSearchStoreClose { + my ($self, $args) = @_; + + $self->sendToServer($self->reconstruct({switch => 'search_store_close'})); + + $universalCatalog{open} = 0; + + debug "Sent search store close\n", "sendPacket", 2; +} + +sub sendSearchStoreSearch { + my ($self, $args) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'search_store_info', + type => $args->{type}, + max_price => $args->{max_price}, + min_price => $args->{min_price}, + item_list => \@{$args->{item_list}}, + card_list => \@{$args->{card_list}}, + })); + + debug "Sent search store search\n", "sendPacket", 2; +} + +sub reconstruct_search_store_info { + my ($self, $args) = @_; + + $args->{item_count} = scalar(@{$args->{item_list}}); + $args->{card_count} = scalar(@{$args->{card_list}}); + + my @id_list = (@{$args->{item_list}}, @{$args->{card_list}}); + + $args->{item_card_list} = pack "(a*)*", map { pack "v", $_ } @id_list; +} + +sub sendSearchStoreRequestNextPage { + my ($self, $args) = @_; + + $self->sendToServer($self->reconstruct({switch => 'search_store_request_next_page'})); + + debug "Sent search store next page request\n", "sendPacket", 2; +} + +sub sendSearchStoreSelect { + my ($self, $args) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'search_store_select', + accountID => $args->{accountID}, + storeID => $args->{storeID}, + nameID => $args->{nameID}, + })); + + debug "Sent search store select request\n", "sendPacket", 2; +} + +sub sendNoviceDoriDori { + my ($self, $args) = @_; + + $self->sendToServer($self->reconstruct({switch => 'novice_dori_dori'})); + + debug "Sent Novice Dori Dori\n", "sendPacket", 2; +} + +sub sendChangeDress { + my ($self, $args) = @_; + + $self->sendToServer($self->reconstruct({switch => 'change_dress'})); + + debug "Sent Change Dress\n", "sendPacket", 2; +} + +sub sendFriendRemove { + my ($self, $accountID, $charID) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'friend_remove', + accountID => $accountID, + charID => $charID, + })); + + debug "Sent Remove a friend\n", "sendPacket"; +} + +sub sendRepairItem { + my ($self, $args) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'repair_item', + index => $args->{index}, + nameID => $args->{nameID}, + upgrade => $args->{upgrade}, + cards => $args->{cards}, + })); + debug ("Sent repair item index: ".$args->{index}."\n", "sendPacket", 2); +} + +sub sendAdoptRequest { + my ($self, $ID) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'adopt_request', + ID => $ID, + })); + + debug "Sent Adoption Request.\n", "sendPacket", 2; +} + +sub sendAdoptReply { + my ($self, $parentID1, $parentID2, $result) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'adopt_reply_request', + parentID1 => $parentID1, + parentID2 => $parentID2, + result => $result + })); + + debug "Sent Adoption Reply.\n", "sendPacket", 2; +} + +sub sendPrivateAirshipRequest { + my ($self, $map_name, $nameID) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'private_airship_request', + map_name => stringToBytes($map_name), + nameID => $nameID, + })); +} + +sub sendNoviceExplosionSpirits { + my ($self) = @_; + + $self->sendToServer($self->reconstruct({switch => 'novice_explosion_spirits'})); + + debug "Sent Novice Explosion Spirits\n", "sendPacket", 2; +} + +sub sendBanCheck { + my ($self, $ID) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'ban_check', + accountID => $ID, + })); + + debug "Sent Account Ban Check Request : " . getHex($ID) . "\n", "sendPacket", 2; +} + +sub sendChangeCart { + my ($self, $lvl) = @_; + + # lvl: 1..5 + $self->sendToServer($self->reconstruct({ + switch => 'change_cart', + lvl => $lvl, + })); + + debug "Sent Cart Change to : $lvl\n", "sendPacket", 2; +} + +sub sendArrowCraft { + my ($self, $nameID) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'make_arrow', + nameID => $nameID, + })); + + debug "Sent Arrowmake: $nameID\n", "sendPacket", 2; +} + +sub sendAutoSpell { + my ($self, $ID) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'auto_spell', + ID => $ID, + })); +} + +sub sendEmotion { + my ($self, $ID) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'send_emotion', + ID => $ID, + })); + + debug "Sent Emotion\n", "sendPacket", 2; +} + +sub sendWho { + my ($self) = @_; + + $self->sendToServer($self->reconstruct({switch => 'request_user_count'})); + debug "Sent Who (User Count)\n", "sendPacket", 2; +} + +sub sendNPCBuySellList { + my ($self, $ID, $type) = @_; + + # type: 0 get store list + # type: 1 get sell list + $self->sendToServer($self->reconstruct({ + switch => 'request_buy_sell_list', + ID => $ID, + type => $type, + })); + + debug "Sent get ".($type ? "buy" : "sell")." list to NPC: ".getHex($ID)."\n", "sendPacket", 2; +} + +sub sendIgnore { + my ($self, $name, $flag) = @_; + + my $nameToBytes = stringToBytes($name); + + $self->sendToServer($self->reconstruct({ + switch => 'ignore_player', + name => $nameToBytes, + flag => $flag, + })); + + debug "Sent Ignore: $name, $flag\n", "sendPacket", 2; +} + +sub sendIgnoreAll { + my ($self, $flag) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'ignore_all', + flag => $flag, + })); + + debug "Sent Ignore All: $flag\n", "sendPacket", 2; +} + +sub sendGetIgnoreList { + my ($self) = @_; + + $self->sendToServer($self->reconstruct({switch => 'get_ignore_list'})); + + debug "Sent get Ignore List.\n", "sendPacket", 2; +} + +sub sendChatRoomCreate { + my ($self, $title, $limit, $public, $password) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'chat_room_create', + limit => $limit, + public => $public, + password => stringToBytes($password), + title => stringToBytes($title), + })); + + debug "Sent Create Chat Room: $title, $limit, $public, $password\n", "sendPacket", 2; +} + +sub sendChatRoomJoin { + my ($self, $ID, $password) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'chat_room_join', + ID => $ID, + password => stringToBytes($password), + })); + + debug "Sent Join Chat Room: ".getHex($ID).", $password\n", "sendPacket", 2; +} + +sub sendChatRoomChange { + my ($self, $title, $limit, $public, $password) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'chat_room_change', + limit => $limit, + public => $public, + password => stringToBytes($password), + title => stringToBytes($title), + })); + + debug "Sent Change Chat Room: $title, $limit, $public, $password\n", "sendPacket", 2; +} + +sub sendChatRoomBestow { + my ($self, $name) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'chat_room_bestow', + name => stringToBytes($name), + + # There are two roles: + # 0 means 'admin' + # 1 means 'normal (not-admin)' + # + # Weirdly, you can only bestow the chat window if you are admin (role 0), + # and in the official client you cannot try to bestow the chat window UNLESS + # you're admin - so it always sends role 0 + # In rA and Hercules, this info is not used at all, instead it's checked whether + # you're actually the chat window admin or not. This might be exploitable in + # official servers (by lying that you're admin when you're not) but I never cared + # enough to test - lututui, Aug 2018 + role => 0, + })); + + debug "Sent Chat Room Bestow: $name\n", "sendPacket", 2; +} + +sub sendChatRoomKick { + my ($self, $name) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'chat_room_kick', + name => stringToBytes($name), + })); + + debug "Sent Chat Room Kick: $name\n", "sendPacket", 2; +} + +sub sendChatRoomLeave { + my ($self) = @_; + + $self->sendToServer($self->reconstruct({switch => 'chat_room_leave'})); + + debug "Sent Leave Chat Room\n", "sendPacket", 2; +} + +sub sendDeal { + my ($self, $ID) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'deal_initiate', + ID => $ID, + })); + + debug "Sent Initiate Deal: ".getHex($ID)."\n", "sendPacket", 2; +} + +sub sendDealReply { + my ($self, $action) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'deal_reply', + + # Action values: + # 0: Char is too far + # 1: Character does not exist + # 2: Trade failed + # 3: Accept + # 4: Cancel + # + # Weird enough, the client should only send 3/4 + # and the server is the one that can reply 0~2 - technologyguild, Dec 2009 + action => $action, + })); + + debug "Sent Deal Reply (Action: $action)\n", "sendPacket", 2; +} + +sub sendDealFinalize { + my ($self) = @_; + + $self->sendToServer($self->reconstruct({switch => 'deal_finalize'})); + + debug "Sent Deal Finalize\n", "sendPacket", 2; +} + +sub sendCurrentDealCancel { + my ($self) = @_; + + $self->sendToServer($self->reconstruct({switch => 'deal_cancel'})); + + debug "Sent Cancel Current Deal\n", "sendPacket", 2; +} + +sub sendDealTrade { + my ($self) = @_; + + $self->sendToServer($self->reconstruct({switch => 'deal_trade'})); + + debug "Sent Deal Trade\n", "sendPacket", 2; +} + +sub sendStorageClose { + my ($self) = @_; + + $self->sendToServer($self->reconstruct({switch => 'storage_close'})); + + debug "Sent Storage Close\n", "sendPacket", 2; +} + +sub sendPartyJoinRequest { + my ($self, $ID) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'party_join_request', + ID => $ID, + })); + + debug "Sent Party Request Join: ".getHex($ID)."\n", "sendPacket", 2; +} + +sub sendPartyJoin { + my ($self, $ID, $flag) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'party_join', + ID => $ID, + flag => $flag, + })); + + debug "Sent Party Join: ".getHex($ID).", $flag\n", "sendPacket", 2; +} + +sub sendPartyLeave { + my ($self) = @_; + + $self->sendToServer($self->reconstruct({switch => 'party_leave'})); + + debug "Sent Party Leave\n", "sendPacket", 2; +} + +sub sendPartyKick { + my ($self, $ID, $name) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'party_kick', + ID => $ID, + name => stringToBytes($name), + })); + + debug "Sent Party Kick: ".getHex($ID).", $name\n", "sendPacket", 2; +} + +sub sendMemo { + my ($self) = @_; + + $self->sendToServer($self->reconstruct({switch => 'memo_request'})); + + debug "Sent Memo\n", "sendPacket", 2; +} + +sub sendCompanionRelease { + my ($self) = @_; + + $self->sendToServer($self->reconstruct({switch => 'companion_release'})); + + debug "Sent Companion Release (Cart, Falcon or Pecopeco)\n", "sendPacket", 2; +} + +sub sendCartAdd { + my ($self, $ID, $amount) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'cart_add', + ID => $ID, + amount => $amount, + })); + + debug "Sent Cart Add: " . getHex($ID) . " x $amount\n", "sendPacket", 2; +} + +sub sendCartGet { + my ($self, $ID, $amount) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'cart_get', + ID => $ID, + amount => $amount, + })); + + debug "Sent Cart Get: " . getHex($ID) . " x $amount\n", "sendPacket", 2; +} + +sub sendIdentify { + my ($self, $ID) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'identify', + ID => $ID, + })); + + debug "Sent Identify: ".getHex($ID)."\n", "sendPacket", 2; +} + +sub sendCardMergeRequest { + my ($self, $cardID) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'card_merge_request', + cardID => $cardID, + })); + + debug "Sent Card Merge Request: " . getHex($cardID) . "\n", "sendPacket", 2; +} + +sub sendCardMerge { + my ($self, $cardID, $itemID) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'card_merge', + cardID => $cardID, + itemID => $itemID, + })); + + debug "Sent Card Merge: " . getHex($cardID) . ", " . getHex($itemID) . "\n", "sendPacket", 2; +} + +sub sendCharCreate { + my $self = shift; + my ($slot, $name, $str, $agi, $vit, $int, $dex, $luk, $hair_style, $hair_color, $job_id, $sex); + + if ($self->{packet_lut}{char_create} eq '0067') { + ($slot, $name, $str, $agi, $vit, $int, $dex, $luk, $hair_style, $hair_color) = @_; + } elsif ($self->{packet_lut}{char_create} eq '0970') { + ($slot, $name, $hair_style, $hair_color) = @_; + } elsif ($self->{packet_lut}{char_create} eq '0A39') { + ($slot, $name, $hair_style, $hair_color, $job_id, $sex) = @_; + $job_id ||= 0; # novice + $sex ||= 0; # female + } + $hair_color ||= 1; + $hair_style ||= 0; + + $self->sendToServer($self->reconstruct({ + switch => 'char_create', + name => stringToBytes($name), + str => $str, + agi => $agi, + vit => $vit, + int => $int, + dex => $dex, + luk => $luk, + slot => $slot, + hair_color => $hair_color, + hair_style => $hair_style, + job_id => $job_id, + sex => $sex + })); + + debug "Sent Char Create\n", "sendPacket", 2; +} + +sub sendCharDelete { + my ($self, $charID, $email) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'char_delete', + charID => $charID, + email => stringToBytes($email), + })); + + debug "Sent Char Delete\n", "sendPacket", 2; +} + +sub sendGuildAlly { + my ($self, $ID, $flag) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'guild_alliance_reply', + ID => $ID, + flag => $flag, + })); + + debug "Sent Ally Guild : ".getHex($ID).", $flag\n", "sendPacket", 2; +} + +sub sendGuildRequestEmblem { + my ($self, $guildID) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'guild_emblem_request', + guildID => $guildID, + })); + + debug "Sent Guild Request Emblem.\n", "sendPacket"; +} + +sub sendGuildBreak { + my ($self, $guildName) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'guild_break', + guildName => stringToBytes($guildName), + })); + + debug "Sent Guild Break: $guildName\n", "sendPacket", 2; +} + +sub sendWarpTele { + my ($self, $skillID, $map) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'warp_select', + # skillID: + # 26 => Teleport (Respawn/Random) + # 27 => Open Warp + skillID => $skillID, + mapName => stringToBytes($map), + })); + + debug "Sent ". ($skillID == 26 ? "Teleport" : "Open Warp") . "\n", "sendPacket", 2 +} + +sub sendStorageGetToCart { + my ($self, $ID, $amount) = @_; + if ($config{storageAuto_type} == 1) { + $self->sendToServer($self->reconstruct({ + switch => 'guild_storage_to_cart', + ID => $ID, + amount => $amount, + })); + } else { + $self->sendToServer($self->reconstruct({ + switch => 'storage_to_cart', + ID => $ID, + amount => $amount, + })); + } + + debug "Sent Storage Get From Cart: " . getHex($ID) . " x $amount\n", "sendPacket", 2; +} + +sub sendStorageAddFromCart { + my ($self, $ID, $amount) = @_; + if ($config{storageAuto_type} == 1) { + $self->sendToServer($self->reconstruct({ + switch => 'cart_to_guild_storage', + ID => $ID, + amount => $amount, + })); + } else { + $self->sendToServer($self->reconstruct({ + switch => 'cart_to_storage', + ID => $ID, + amount => $amount, + })); + } + + debug "Sent Storage Add From Cart: " . getHex($ID) . " x $amount\n", "sendPacket", 2; +} + +sub sendHomunculusName { + my ($self, $name) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'homunculus_name', + name => stringToBytes($name), + })); + + debug "Sent Homunculus Rename: $name\n", "sendPacket", 2; +} + +sub sendGuildLeave { + my ($self, $reason, $guildID, $charID) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'guild_leave', + guildID => $guildID, + accountID => $accountID, + charID => $charID, + reason => stringToBytes($reason), + })); + + debug "Sent Guild Leave: $reason\n", "sendPacket"; +} + +sub sendGuildMemberKick { + my ($self, $guildID, $accountID, $charID, $reason) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'guild_kick', + guildID => $guildID, + charID => $charID, + accountID => $accountID, + reason => stringToBytes($reason), + })); + + debug "Sent Guild Kick: ".getHex($charID)."\n", "sendPacket"; +} + +sub sendGuildCreate { + my ($self, $name, $charID) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'guild_create', + charID => $charID, + guildName => stringToBytes($name), + })); + + debug "Sent Guild Create: $name\n", "sendPacket", 2; +} + +sub sendGuildJoin { + my ($self, $ID, $flag) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'guild_join', + ID => $ID, + flag => $flag, + })); + + debug "Sent Join Guild : ".getHex($ID).", $flag\n", "sendPacket"; +} + +sub sendGuildJoinRequest { + my ($self, $ID, $charID) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'guild_join_request', + ID => $ID, + accountID => $accountID, + charID => $charID, + })); + + debug "Sent Request Join Guild: ".getHex($ID)."\n", "sendPacket"; +} + +sub sendGuildNotice { + my ($self, $guildID, $name, $notice) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'guild_notice', + guildID => $guildID, + name => stringToBytes($name), + notice => stringToBytes($notice), + })); + + debug "Sent Change Guild Notice: $notice\n", "sendPacket", 2; +} + +sub sendGuildSetAlly { + my ($self, $targetAID, $myAID, $charID) = @_; + + # this packet is for guildmaster asking to set alliance with another guildmaster + # the other sub for sendGuildAlly are responses to this sub + # kept the parameters open, but everything except $targetAID could be replaced with Global variables + # unless you plan to mess around with the alliance packet, no exploits though, I tried ;-) + # -zdivpsa + + $self->sendToServer($self->reconstruct({ + switch => 'guild_alliance_request', + targetAccountID => $targetAID, + accountID => $myAID, + charID => $charID, + })); + + debug "Sent Guild Alliance Request\n", "sendPacket", 2; +} + +sub sendPetCapture { + my ($self, $monID) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'pet_capture', + ID => $monID, + })); + debug "Sent pet capture: ".getHex($monID)."\n", "sendPacket", 2; +} + +sub sendPetMenu { + my ($self, $type) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'pet_menu', + + # Action + # 0 => info + # 1 => feed + # 2 => performance + # 3 => return to egg + # 4 => unequip accessory + action => $type, + })); + + debug "Sent Pet Menu\n", "sendPacket", 2; +} + +sub sendPetHatch { + my ($self, $ID) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'pet_hatch', + ID => $ID, + })); + + debug "Sent Incubator hatch: " . getHex($ID) . "\n", "sendPacket", 2; +} + +sub sendPetName { + my ($self, $name) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'pet_name', + name => stringToBytes($name), + })); + + debug "Sent Pet Rename: $name\n", "sendPacket", 2; +} + +sub sendPetEmotion { + my ($self, $ID) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'pet_emotion', + ID => $ID, + })); + + debug "Sent Pet Emotion: $ID\n", "sendPacket", 2; +} + + +sub sendBuyBulk { + my ($self, $r_array) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'buy_bulk', + items => \@{$r_array}, + })); + + debug("Sent bulk buy: $_->{itemID} x $_->{amount}\n", "sendPacket", 2) foreach (@{$r_array}); +} + +sub reconstruct_buy_bulk { + my ($self, $args) = @_; + my $pack = $self->{send_buy_bulk_pack} || "v2"; + + $args->{buyInfo} = pack "(a*)*", map { pack $pack, $_->{amount}, $_->{itemID} } @{$args->{items}}; +} + +sub sendSellBulk { + my ($self, $r_array) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'sell_bulk', + items => \@{$r_array}, + })); + + debug("Sent bulk sell: " . getHex($_->{ID}) . " x $_->{amount}\n", "sendPacket", 2) foreach (@{$r_array}); +} + +sub reconstruct_sell_bulk { + my ($self, $args) = @_; + + $args->{sellInfo} = pack "(a*)*", map { pack "a2 v", $_->{ID}, $_->{amount} } @{$args->{items}}; +} + +sub sendAchievementGetReward { + my ($self, $achievementID) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'achievement_get_reward', + achievementID => $achievementID, + })); +} + +sub sendTop10Alchemist { + my ($self) = @_; + + if (!$masterServer->{rankingSystemType}) { + $self->sendToServer($self->reconstruct({switch => 'rank_alchemist'})); + } else { + $self->sendTop10(1); + } + + debug "Sent Top 10 Alchemist request\n", "sendPacket", 2; +} + +sub sendTop10Blacksmith { + my ($self) = @_; + + if (!$masterServer->{rankingSystemType}) { + $self->sendToServer($self->reconstruct({switch => 'rank_blacksmith'})); + } else { + $self->sendTop10(0); + } + + debug "Sent Top 10 Blacksmith request\n", "sendPacket", 2; +} + +sub sendTop10PK { + my ($self) = @_; + + if (!$masterServer->{rankingSystemType}) { + $self->sendToServer($self->reconstruct({switch => 'rank_killer'})); + } else { + $self->sendTop10(3); + } + + debug "Sent Top 10 PK request\n", "sendPacket", 2; +} + +sub sendTop10Taekwon { + my ($self) = @_; + + if (!$masterServer->{rankingSystemType}) { + $self->sendToServer($self->reconstruct({switch => 'rank_taekwon'})); + } else { + $self->sendTop10(2); + } + + debug "Sent Top 10 Taekwon request\n", "sendPacket", 2; +} + +sub sendTop10 { + my ($self, $type) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'rank_general', + + # Type: + # 0 => Blacksmith + # 1 => Alchemist + # 2 => Taekwon + # 3 => PK + type => $type, + })); +} + +sub sendGMSummon { + my ($self, $playerName) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'gm_summon_player', + playerName => stringToBytes($playerName), + })); +} + +sub sendGMBroadcast { + my ($self, $message) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'gm_broadcast', + + # to colorize, add in front of message: micc | ssss | blue | tool ? + message => stringToBytes($message), + })); +} + +sub sendGMKick { + my ($self, $accountID) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'gm_kick', + targetAccountID => $accountID, + })); +} + +sub sendGMKickAll { + my ($self) = @_; + + $self->sendToServer($self->reconstruct({switch => 'gm_kick_all'})); +} + +sub sendGMMonsterItem { + my ($self, $name) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'gm_item_mob_create', + name => stringToBytes($name), + })); +} + +sub sendGMMapMove { + my ($self, $name, $x, $y) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'gm_move_to_map', + mapName => stringToBytes($name), + x => $x, + y => $y, + })); +} + +sub sendGMResetStateSkill { + my ($self, $type) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'gm_reset_state_skill', + + # type + # 0 => status + # 1 => skills + type => $type, + })); + + debug "Sent GM Reset State/Skill.\n", "sendPacket", 2; +} + +sub sendGMChangeMapType { + my ($self, $x, $y, $type) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'gm_change_cell_type', + x => $x, + y => $y, + + # type + # 0 => not walkable + # 1 => walkable + type => $type, + })); + + debug "Sent GM Change Map Type.\n", "sendPacket", 2; +} + +sub sendGMBroadcastLocal { + my ($self, $message) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'gm_broadcast_local', + message => stringToBytes($message), + })); +} + +sub sendGMChangeEffectState { + my ($self, $effect_state) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'gm_change_effect_state', + effect_state => $effect_state + })); + + debug "Sent GM Hide.\n", "sendPacket", 2; +} + +sub sendGMRemove { + my ($self, $playerName) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'gm_remove', + playerName => stringToBytes($playerName), + })); +} + +sub sendGMShift { + my ($self, $playerName) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'gm_shift', + playerName => stringToBytes($playerName), + })); +} + +sub sendGMRecall { + my ($self, $playerName) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'gm_recall', + playerName => stringToBytes($playerName), + })); +} + +sub sendAlignment { + my ($self, $ID, $alignment, $point) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'alignment', + targetID => $ID, + type => $alignment, + point => $point, + })); + + debug "Sent Alignment: ".getHex($ID).", $alignment\n", "sendPacket", 2; +} + +sub sendOpenShop { + my ($self, $title, $items) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'shop_open', + title => stringToBytes($title), + result => 1, + items => $items, + })); +} + +sub reconstruct_shop_open { + my ($self, $args) = @_; + + $args->{vendingInfo} = pack "(a*)*", map { pack "a2 v V", $_->{ID}, $_->{amount}, $_->{price} } @{$args->{items}}; +} + +sub sendMailboxOpen { + my ($self) = @_; + + $self->sendToServer($self->reconstruct({switch => 'mailbox_open'})); + + debug "Sent mailbox open.\n", "sendPacket", 2; +} + +sub sendMailRead { + my ($self, $mailID) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'mail_read', + mailID => $mailID, + })); + + debug "Sent read mail.\n", "sendPacket", 2; +} + +sub sendMailDelete { + my ($self, $mailID) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'mail_delete', + mailID => $mailID, + })); + + debug "Sent delete mail.\n", "sendPacket", 2; +} + +sub sendMailGetAttach { + my ($self, $mailID) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'mail_attachment_get', + mailID => $mailID, + })); + + debug "Sent mail get attachment.\n", "sendPacket", 2; +} + +sub sendMailOperateWindow { + my ($self, $flag) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'mail_remove', + flag => $flag, + })); + + debug "Sent mail remove item/zeny.\n", "sendPacket", 2; +} + +sub sendMailSetAttach { + my ($self, $amount, $ID) = @_; + + # Before setting an attachment, we must remove any zeny/item that was attached but the mail wasn't sent + # Otherwise the attachment will be lost + if ($ID) { + $self->sendMailOperateWindow(1); + } else { + $self->sendMailOperateWindow(2); + } + + $AI::temp::mailAttachAmount = $amount; + $self->sendToServer($self->reconstruct({ + switch => 'mail_attachment_set', + ID => $ID, + amount => $amount, + })); + + debug "Sent mail set attachment.\n", "sendPacket", 2; +} + +sub sendMailSend { + my ($self, $receiver, $title, $message) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'mail_send', + recipient => stringToBytes($receiver), + title => stringToBytes($title), + body_len => length $message > 255 ? 255 : length $message, + body => $message, + })); + + debug "Sent mail send.\n", "sendPacket", 2; +} + +sub reconstruct_mail_send { + my ($self, $args) = @_; + + $args->{body} = pack "Z" . $args->{body_len}, stringToBytes($args->{body}); +} + +sub sendMailReturn { + my ($self, $mailID, $sender) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'mail_return', + mailID => $mailID, + sender => $sender, + })); + + debug "Sent return mail.\n", "sendPacket", 2; +} + +sub sendAuctionAddItemCancel { + my ($self, $flag) = @_; + + $flag ||= 1; + + $self->sendToServer($self->reconstruct({ + switch => 'auction_add_item_cancel', + flag => $flag, + })); + + debug "Sent Auction Add Item Cancel.\n", "sendPacket", 2; +} + +sub sendAuctionAddItem { + my ($self, $ID, $amount) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'auction_add_item', + ID => $ID, + amount => $amount, + })); + + debug "Sent Auction Add Item.\n", "sendPacket", 2; +} + +sub sendAuctionCreate { + my ($self, $now_price, $max_price, $delete_time) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'auction_create', + now_price => $now_price, + max_price => $max_price, + delete_time => $delete_time, + })); + + debug "Sent Auction Create.\n", "sendPacket", 2; +} + +sub sendAuctionCancel { + my ($self, $ID) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'auction_cancel', + ID => $ID, + })); + + debug "Sent Auction Cancel.\n", "sendPacket", 2; +} + +sub sendAuctionBuy { + my ($self, $ID, $price) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'auction_buy', + ID => $ID, + price => $price, + })); + + debug "Sent Auction Buy.\n", "sendPacket", 2; +} + +sub sendAuctionItemSearch { + my ($self, $type, $price, $search_string, $page) = @_; + $page ||= 1; + + $self->sendToServer($self->reconstruct({ + switch => 'auction_search', + price => $price, + search_string => stringToBytes($search_string), + page => $page, + + # type + # 0 => armor + # 1 => weapon + # 2 => card + # 3 => misc + # 4 => name + # 5 => auction id + type => $type, + })); + + debug "Sent Auction Item Search.\n", "sendPacket", 2; +} + +sub sendAuctionReqMyInfo { + my ($self, $type) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'auction_info_self', + type => $type, + })); + + debug "Sent Auction Request My Info.\n", "sendPacket", 2; +} + +sub sendAuctionMySellStop { + my ($self, $ID) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'auction_sell_stop', + ID => $ID, + })); + + debug "Sent My Sell Stop.\n", "sendPacket", 2; +} + +sub sendPartyJoinRequestByNameReply { + my ($self, $accountID, $flag) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'party_join_request_by_name_reply', + accountID => $accountID, + flag => $flag, + })); + + debug "Sent reply Party Invite.\n", "sendPacket", 2; +} + +sub sendAutoRevive { + my ($self) = @_; + + $self->sendToServer($self->reconstruct({switch => 'auto_revive'})); + + debug "Sent Auto Revive.\n", "sendPacket", 2; +} + +sub sendBattlegroundChat { + my ($self, $message) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'battleground_chat', + message => ($masterServer->{chatLangCode}) ? stringToBytes("|00" . $message) : stringToBytes($message), + })); + + debug "Sent Battleground chat.\n", "sendPacket", 2; +} + +sub sendMercenaryCommand { + my ($self, $command) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'mercenary_command', + + # 0x0 => COMMAND_REQ_NONE + # 0x1 => COMMAND_REQ_PROPERTY + # 0x2 => COMMAND_REQ_DELETE + flag => $command + })); + + debug "Sent Mercenary Command $command", "sendPacket", 2; +} + +sub sendSkillUseLocInfo { + my ($self, $ID, $lvl, $x, $y, $moreinfo) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'skill_use_location_text', + lvl => $lvl, + ID => $ID, + x => $x, + y => $y, + info => $moreinfo + })); + + debug "Skill Use on Location: $ID, ($x, $y)\n", "sendPacket", 2; +} + +sub sendGMGiveMannerByName { + my ($self, $playerName) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'manner_by_name', + playerName => stringToBytes($playerName), + })); +} + +sub sendGMRequestStatus { + my ($self, $playerName) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'gm_request_status', + playerName => stringToBytes($playerName), + })); +} + +sub sendFeelSaveOk { + my ($self, $flag) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'starplace_agree', + flag => $flag, + })); + + debug "Sent FeelSaveOk.\n", "sendPacket", 2; +} + +sub sendGMReqAccName { + my ($self, $targetID) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'gm_request_account_name', + targetID => $targetID, + })); + + debug "Sent GM Request Account Name.\n", "sendPacket", 2; +} + +sub sendClientVersion { + my ($self, $version) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'client_version', + clientVersion => $version, + })); +} + +sub sendCaptchaAnswer { + my ($self, $answer) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'captcha_answer', + accountID => $accountID, + answer => $answer, + + # Strangely, this packet has fixed length (dec 32, or hex 0x20) but has it padded into it - lututui + len => (exists $rpackets{'07E7'}{length}) ? $rpackets{'07E7'}{length} : 32, + })); +} + +# kRO Client before 2010-08-03 only allow 1 item per transaction +# idRO_Renewal and iRO Chaos use this packet +# +# Since RagexeRE_2010_08_03a it's allowed for multiple items at once see Network/Send/kRO/RagexeRE_2010_08_03a.pm +sub sendCashShopBuy { + my ($self, $points, $items) = @_; + + if (scalar @{$items}) { + debug sprintf("Sent buying request from cashshop for %d items.\n", scalar @{$items}), "sendPacket", 2; + foreach my $item (@{$items}) { + $self->sendToServer($self->reconstruct({ + switch => 'cash_dealer_buy', + itemid => $item->{itemID}, + amount => $item->{amount}, + kafra_points => $points, + })); + } + } +} + +sub sendStartSkillUse { + my ($self, $ID, $lv, $targetID) = @_; + $char->{last_skill_used_is_continuous} = 1; + $char->{last_continuous_skill_used} = $ID; + $self->sendToServer($self->reconstruct({switch => 'start_skill_use', lv => $lv, skillID => $ID, targetID => $targetID})); + debug "Start Skill Use: $ID\n", "sendPacket", 2; +} + +sub sendStopSkillUse { + my ($self, $ID) = @_; + $char->{last_skill_used_is_continuous} = 0; + $char->{last_continuous_skill_used} = 0; + $self->sendToServer($self->reconstruct({switch => 'stop_skill_use',skillID => $ID})); + debug "Stop Skill Use: $ID\n", "sendPacket", 2; +} + +## +# Request to merge item +# 096E <size>.W { <index>.W }* +# @author [Cydh] +## +sub sendMergeItemRequest { + my ($self, $num, $items) = @_; + #my $len = ($num * 4) + 12; + $self->sendToServer($self->reconstruct({ + switch => 'merge_item_request', + #len => $len, + items => $items, + })); + debug "Sent merge item request: ".(join ', ', map { $_->{info}->{binID}." x ".$_->{info}->{amount} } @$items)."\n", "sendPacket"; +} + +sub reconstruct_merge_item_request { + my ($self, $args) = @_; + $args->{itemList} = pack '(a2)*', map { $_->{ID} } @{$args->{items}}; +} + +## +# Request to cancel merge item +# 0974 +# @author [Cydh] +## +sub sendMergeItemCancel { + my ($self, $args) = @_; + $self->sendToServer($self->reconstruct({ switch => 'merge_item_cancel' })); + debug "Cancel Merge item\n", "sendPacket"; + $mergeItemList = {}; +} + +#sub reconstruct_merge_item_cancel { +# my ($self, $args) = @_; +#} + +sub sendStylistChange { + my ($self, $hair_color, $hair_style, $cloth_color, $head_top, $head_mid, $head_bottom ) = @_; + $self->sendToServer($self->reconstruct({ + switch => 'stylist_change', + hair_color => $hair_color, + hair_style => $hair_style, + cloth_color => $cloth_color, + head_top => $head_top, + head_mid => $head_mid, + head_bottom => $head_bottom + })); +} + +## +# UI System +## + +# Request to open an UI window of the given type +# 0A68 <type>.B +sub sendOpenUIRequest { + my ($self, $UIType) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'open_ui_request', + UIType => $UIType, + })); + + debug "Sent Open UI Request (".$UIType.")\n", "sendPacket"; +} + +## +# Attendance System +## + +# Request from the client to retrieve today's attendance reward +# 0AEF + +sub sendAttendanceRewardRequest { + my ($self) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'attendance_reward_request', + })); + + debug "Sent Attendance Reward Request\n", "sendPacket"; +} + + +## +# Banking System +## + +# Requesting the data in bank +# 09AB <aid>L (PACKET_CZ_REQ_BANKING_CHECK) +sub sendBankingCheck { + my ($self, $accountID) = @_; + $self->sendToServer($self->reconstruct({ + switch => 'banking_check_request', + accountID => $accountID, + })); +} + +# Request Withdrawing some money from bank +# 09A9 <AID>L <Money>L (PACKET_CZ_REQ_BANKING_WITHDRAW) +sub sendBankingWithdraw { + my ($self, $accountID , $zeny) = @_; + $self->sendToServer($self->reconstruct({ + switch => 'banking_withdraw_request', + accountID => $accountID, + zeny => $zeny, + })); +} + +# Request saving some money in bank +# 09A7 <AID>L <Money>L (PACKET_CZ_REQ_BANKING_DEPOSIT) +sub sendBankingDeposit { + my ($self, $accountID , $zeny) = @_; + $self->sendToServer($self->reconstruct({ + switch => 'banking_deposit_request', + accountID => $accountID, + zeny => $zeny, + })); +} + +## +# Roulette System +## + +# Request to open the roulette window +# 0A19 (CZ_REQ_OPEN_ROULETTE) +sub sendRouletteWindowOpen { + my ($self) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'roulette_window_open', + })); + + debug "Sent Roulette Window Open\n", "sendPacket"; +} + +# Request the roulette reward data +# 0A1B (CZ_REQ_ROULETTE_INFO) +sub sendRouletteInfoRequest { + my ($self) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'roulette_info_request', + })); + + debug "Sent Roulette Info Request\n", "sendPacket"; +} + +# Notification of the client that the roulette window was closed +# 0A1D (CZ_REQ_CLOSE_ROULETTE) +sub sendRouletteClose { + my ($self) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'roulette_close', + })); + + debug "Sent Roulette Close\n", "sendPacket"; +} + +# Request to start the roulette +# 0A1F (CZ_REQ_GENERATE_ROULETTE) +sub sendRouletteStart { + my ($self) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'roulette_start', + })); + + debug "Sent Roulette Start\n", "sendPacket"; +} + +# Request to claim a prize +# 0A21 (CZ_RECV_ROULETTE_ITEM) +sub sendRouletteClaimPrize { + my ($self) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'roulette_claim_prize', + })); + + debug "Sent Roulette Claim Prize\n", "sendPacket"; +} + +## +# Market System +## + +# Send to Server confirmation that we already close NPC shop +# 09D4 +sub sendSellBuyComplete { + my ($self) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'sell_buy_complete', + })); + + debug "Sent Sell/Buy Complete\n", "sendPacket"; +} + +# Buy item from Market +# 09D6 +sub sendBuyBulkMarket { + my ($self, $r_array) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'buy_bulk_market', + items => \@{$r_array}, + })); + + debug("Sent bulk buy market: $_->{itemID} x $_->{amount}\n", "sendPacket", 2) foreach (@{$r_array}); +} + +sub reconstruct_buy_bulk_market { + my ($self, $args) = @_; + my $pack = $self->{send_buy_bulk_market_pack} || "v V"; + + $args->{buyInfo} = pack "(a*)*", map { pack $pack, $_->{itemID}, $_->{amount} } @{$args->{items}}; +} + +# Request to close current Market +# 09D8 +sub sendMarketClose { + my ($self) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'market_close', + })); + + debug "Sent Market Close\n", "sendPacket"; +} + +# Request Inventory Expansion +# 0B14 +sub sendInventoryExpansionRequest { + my ($self, $args) = @_; + $self->sendToServer($self->reconstruct({ switch => 'inventory_expansion_request' })); +} + +# Reject Inventory Expansion +# 0B19 +sub sendInventoryExpansionRejected { + my ($self, $args) = @_; + $self->sendToServer($self->reconstruct({ switch => 'inventory_expansion_rejected' })); +} + +# 0B1C (PACKET_CZ_PING) +sub sendPing { + my ($self, $args) = @_; + $self->sendToServer($self->reconstruct({ switch => 'ping' })); +} + +# 0A5A - PACKET_CZ_MACRO_DETECTOR_DOWNLOAD +# Let Server know that we already downloaded Captcha Image +sub sendMacroDetectorDownload { + my ($self) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'macro_detector_download', + })); +} + +# 0A5C - PACKET_CZ_MACRO_DETECTOR_ANSWER +# Send Captcha Answer +sub sendMacroDetectorAnswer { + my ($self, $answer) = @_; + + my $answer_bytes = stringToBytes($answer); + + $self->sendToServer($self->reconstruct({ + switch => 'macro_detector_answer', + answer => $answer_bytes, + })); +} + +# 0A69 - PACKET_CZ_CAPTCHA_PREVIEW_REQUEST +# Request to preview a captcha (privilege is required) +sub sendCaptchaPreviewRequest { + my ($self, $captcha_key) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'captcha_preview_request', + captcha_key => $captcha_key, + })); +} + +# 02CF CZ_MEMORIALDUNGEON_COMMAND +# Destroy an instance from the status window +sub sendMemorialDungeonCommand { + my ($self, $command) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'memorial_dungeon_command', + command => $command, + })); + + debug "Sent Memorial Dungeon Command\n", "sendPacket"; +} + +# 0A16 CZ_DYNAMICNPC_CREATE_REQUEST +sub sendNPCCreateRequest { + my ($self, $name) = @_; + $self->sendToServer($self->reconstruct({switch => 'dynamicnpc_create_request', ID => $name})); + debug "Sent request to create NPC by name: $name\n", "sendPacket", 2; +} + +# 0841 CH_SELECT_ACCESSIBLE_MAPNAME +sub sendSelectAccessibleMapname { + my ($self, $map_slot) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'select_accessible_mapname', + char_slot => $config{char}, + map_slot => $map_slot, + })); +} + +# 0BAF CZ_USE_PACKAGEITEM +sub sendUsePackageItem { + my ($self, $index, $itemID, $boxIndex) = @_; + + $self->sendToServer($self->reconstruct({ + switch => 'use_packageitem', + index => $index, + accountID => $accountID, + itemID => $itemID, + boxIndex => $boxIndex, + })); +} + +1; diff --git a/openkore_llm_knowledge/networking/XKore.pm b/openkore_llm_knowledge/networking/XKore.pm new file mode 100644 index 0000000000..caf845732c --- /dev/null +++ b/openkore_llm_knowledge/networking/XKore.pm @@ -0,0 +1,642 @@ +######################################################################### +# OpenKore - X-Kore +# +# This software is open source, licensed under the GNU General Public +# License, version 2. +# Basically, this means that you're allowed to modify and distribute +# this software. However, if you distribute modified versions, you MUST +# also distribute the source code. +# See http://www.gnu.org/licenses/gpl.html for the full license. +# +# $Revision$ +# $Id$ +# +######################################################################### +package Network::XKore; + +use strict; +use base qw(Exporter); +use Exporter; +use IO::Socket::INET; +use Time::HiRes qw(time usleep); +use Win32; +use Exception::Class ('Network::XKore::CannotStart'); + +use Modules 'register'; +use Globals; +use Log qw(message error debug); +use Utils::Win32; +use Network; +use Network::Send (); +use Utils qw(dataWaiting timeOut); +use Translation; +use Misc qw(chatLog); +use Network::MessageTokenizer; + +my $currentClientKey = 0; + +## +# Network::XKore->new() +# +# Initialize X-Kore mode. Throws Network::XKore::CannotStart on error. +sub new { + my $class = shift; + my $port = $config{XKore_port} || 2350; + my $self = bless {}, $class; + + undef $@; + $self->{server} = new IO::Socket::INET->new( + Listen => 5, + LocalAddr => 'localhost', + LocalPort => $port, + Proto => 'tcp'); + if (!$self->{server}) { + Network::XKore::CannotStart->throw(error => TF("Unable to start the X-Kore server.\n" . + "Make sure no other servers are running on port %s.\n", $port)); + } + + $self->{incomingPackets} = ""; + $self->{serverPackets} = ""; + $self->{clientPackets} = ""; + + $masterServer = $masterServers{$config{master}}; + $packetParser = Network::Receive->create($self, $masterServer->{serverType}); + $messageSender = Network::Send->create($self, $masterServer->{serverType}); + $clientPacketHandler = Network::ClientReceive->new; + + $self->{tokenizer} = new Network::MessageTokenizer($self->getRecvPackets()); + $self->{kore_map_changed_hook} = Plugins::addHook('packet/map_changed', \&kore_map_changed, $self); + + message T("X-Kore mode intialized.\n"), "startup"; + + return $self; +} + +sub version { + return 1; +} + +sub DESTROY { + my $self = shift; + + close($self->{client}); +} + +###################### +## Server Functions ## +###################### + +sub serverAlive { + return $_[0]->{client} && $_[0]->{client}->connected; +} + +sub serverConnect { + return undef; +} + +sub serverPeerHost { + return undef; +} + +sub serverPeerPort { + return undef; +} + +sub serverRecv { + my $self = shift; + $self->recv(); + + return undef unless length($self->{serverPackets}); + + my $packets = $self->{serverPackets}; + $self->{serverPackets} = ""; + + return $packets; +} + +sub serverSend { + my $self = shift; + my $msg = shift; + Plugins::callHook('Network::serverSend/pre', {msg => \$msg}); + $self->{client}->send("S".pack("v", length($msg)).$msg) if ($self->serverAlive); +} + +sub serverDisconnect { + return undef; +} + +sub serverAddress { + return undef; +} + +sub getState { + return $conState; +} + +sub setState { + my ($self, $state) = @_; + if ($conState != $state) { + $conState = $state; + Plugins::callHook('Network::stateChanged'); + } +} + + +###################### +## Client Functions ## +###################### + +## +# $net->clientAlive() +# Returns: a boolean. +# +# Check whether the connection with the client is still alive. +sub clientAlive { + return $_[0]->serverAlive(); +} + +## +# $net->clientConnect +# +# Not used with XKore mode 1 +sub clientConnect { + return undef; +} + +## +# $net->clientPeerHost +# +sub clientPeerHost { + return $_[0]->{client}->peerhost if ($_[0]->clientAlive); + return undef; +} + +## +# $net->clientPeerPort +# +sub clientPeerPort { + return $_[0]->{client}->peerport if ($_[0]->clientAlive); + return undef; +} + +## +# $net->clientRecv() +# Returns: the message sent from the client (towards the server), or undef if there are no pending messages. +sub clientRecv { + my $self = shift; + $self->recv(); + + return undef unless length($self->{clientPackets}); + + my $packets = $self->{clientPackets}; + $self->{clientPackets} = ""; + + return $packets; +} + +## +# $net->clientSend(msg) +# msg: A scalar to be sent to the RO client +# +sub clientSend { + my $self = shift; + my $msg = shift; + + my $switch = uc(unpack("H2", substr($msg, 1, 1))) . uc(unpack("H2", substr($msg, 0, 1))); + if ($switch eq "02AE") { #initialize_message_id_encryption + $msg = ""; + } + + $self->{client}->send("R".pack("v", length($msg)).$msg) if ($self->clientAlive); +} + +sub clientDisconnect { + return undef; +} + +####################### +## Utility Functions ## +####################### + +## +# $net->injectSync() +# +# Send a keep-alive packet to the injected DLL. +sub injectSync { + my $self = shift; + $self->{client}->send("K" . pack("v", 0)) if ($self->serverAlive); +} + +## +# $net->checkConnection() +# +# Handles any connection issues. Based on the current situation, this function may +# re-connect to the RO server, disconnect, do nothing, etc. +# +# This function is meant to be run in the Kore main loop. +sub checkConnection { + my $self = shift; + + if ($timeout{play}{time} && timeOut($timeout{play}) && $conState ==5) { + $self->setState(Network::NOT_CONNECTED); + error T("Timeout on Map Server, "), "connection"; + Plugins::callHook('disconnected'); + if ($config{dcOnDisconnect}) { + error T("Auto disconnecting on Disconnect!\n"); + chatLog("k", T("*** You disconnected, auto disconnect! ***\n")); + $quit = 1; + } else { + error "waiting actions for the Ragnarok Online client\n"; + } + } + + return if ($self->serverAlive); + + # (Re-)initialize X-Kore if necessary + $self->setState(Network::NOT_CONNECTED); + my $pid; + # Wait until the RO client has started + + my $loop = 1; + my @list; + + message TF("Please start the Ragnarok Online client (%s)\n", $config{XKore_exeName}), "startup"; + Plugins::callHook('XKore_start'); + while ($loop) { + undef @list; + my @z = Utils::Win32::listProcesses(); + + foreach (@z) { + if (lc($_->{'exe'}) eq lc($config{XKore_exeName})) { + push @list, {exe => $_->{'exe'}, pid => $_->{'pid'}}; + } + } + + if (@list == 0) { + # no process, wait for start + usleep 20000; + next; + } + + # automatically attach if one process found and config allows it + if (@list == 1 && $config{XKore_autoAttachIfOneExe}) { + $pid = $list[0]->{'pid'}; + message TF("Ragnarok Online client found, pid = %i\n", $pid), "startup"; + + $loop = 0; + last; + } + + # several exes, make choice + message T("Found Ragnarok Online client(s), select one: (enter to rescan, quit to quit)\n"), "startup"; + my $qr; + my $i = 0; + foreach (@list) { + $qr = $qr . TF("[%i] pid = %i (%s)\n", $i, $_->{'pid'}, $_->{'exe'}); + $i++; + } + my $input = $interface->query($qr, title => "Select Ragnarok Online client"); + if ($input eq "quit") { + $quit = 1; + $loop = 0; + last; + } elsif ($input eq "r" || !defined($input) || $input eq '' || $input !~ /^\d+$/) { + next; + } else { + if ($input < 0 || $input >= @list) { + error TF("Please enter a number between 0 and %i\n", @list - 1); + next; + } + $pid = $list[$input]->{'pid'}; + message TF("Selected pid = %i\n", $pid), "startup"; + $loop = 0; + last; + } + } + + return if $quit; + + sleep 1; + + # Inject DLL + if($config{XKore_injectDLL}) { + if (!$self->inject($pid)) { + # Failed to inject + $interface->errorDialog($@); + exit 1; + } + } + + # Patch client + $self->hackClient($pid) if ($config{XKore_bypassBotDetection}); + + # Wait until the RO client has connected to us + $self->waitForClient; + message T("You can login with the Ragnarok Online client now.\n"), "startup"; + $timeout{'injectSync'}{'time'} = time; +} + +## +# $net->inject(pid) +# pid: a process ID. +# Returns: 1 on success, 0 on failure. +# +# Inject NetRedirect.dll into an external process. On failure, $@ is set. +# +# This function is meant to be used internally only. +sub inject { + my ($self, $pid) = @_; + my $cwd = Win32::GetCwd(); + my $dllName = $config{XKore_dll} || 'NetRedirect.dll'; + my $dll; + undef $@; + foreach my $file ("$cwd\\src\\auto\\XSTools\\$dllName", "$cwd\\src\\auto\\XSTools\\win32\\$dllName", "$cwd\\$dllName") { + if (-f $file) { + $dll = $file; + last; + } + } + if (!$dll) { + $@ = TF("Cannot find %s. Please check your installation.", $dllName); + return 0; + } + if (Utils::Win32::InjectDLL($pid, $dll)) { + return 1; + } else { + $@ = TF("Unable to inject %s",$dll); + return undef; + } +} + +## +# $net->waitForClient() +# Returns: the socket which connects X-Kore to the client. +# +# Wait until the client has connected the X-Kore server. +# +# This function is meant to be used internally only. +sub waitForClient { + my $self = shift; + + message T("Waiting for the Ragnarok Online client to connect to X-Kore..."), "startup"; + $self->{client} = $self->{server}->accept; + # Translation Comment: Waiting for the Ragnarok Online client to connect to X-Kore... + message " " . T("ready\n"), "startup"; + return $self->{client}; +} + +## +# $net->recv() +# Returns: Nothing +# +# Receive packets from the client. Then sort them into server-bound or client-bound; +# +# This is meant to be used internally only. +sub recv { + my $self = shift; + my $msg; + + return undef unless dataWaiting(\$self->{client}); + undef $@; + eval { + $self->{client}->recv($msg, 32 * 1024); + }; + if (!defined $msg || length($msg) == 0 || $@) { + delete $self->{client}; + return undef; + } + + $self->{incomingPackets} .= $msg; + + while ($self->{incomingPackets} ne "") { + last if (!length($self->{incomingPackets})); + + my $type = substr($self->{incomingPackets}, 0, 1); + my $len = unpack("v",substr($self->{incomingPackets}, 1, 2)); + + last if ($len > length($self->{incomingPackets})); + + $msg = substr($self->{incomingPackets}, 3, $len); + $self->{incomingPackets} = (length($self->{incomingPackets}) - $len - 3)? + substr($self->{incomingPackets}, $len + 3, length($self->{incomingPackets}) - $len - 3) + : ""; + if ($type eq "R") { + # Client-bound (or "from server") packets + my $switch = uc(unpack("H2", substr($msg, 1, 1))) . uc(unpack("H2", substr($msg, 0, 1))); + if (($switch eq "0071" || $switch eq "0092") && $currentClientKey && $messageSender->{encryption}->{crypt_key}) { + $currentClientKey = $messageSender->{encryption}->{crypt_key_1}; + $messageSender->{encryption}->{crypt_key} = $messageSender->{encryption}->{crypt_key_1}; + } + $self->{serverPackets} .= $msg; + } elsif ($type eq "S") { + # Server-bound (or "to server") packets + if($self->getState() eq Network::IN_GAME || $self->getState() eq Network::CONNECTED_TO_CHAR_SERVER) { + $self->onClientData($msg); + return undef; + } + $self->{clientPackets} .= $msg; + } elsif ($type eq "K") { + # Keep-alive... useless. + } + } + + # Check if we need to send our sync + if (timeOut($timeout{'injectSync'})) { + $self->injectSync; + $timeout{'injectSync'}{'time'} = time; + } + + return 1; +} + +## +# $net->hackClient(pid) +# pid: Process ID of a running (and official) Ragnarok Online client +# +# Hacks the client (non-nProtect GameGuard version) to remove bot detection. +# If the code is in the RO Client, it should find it fairly quick and patch, but +# if not it will spend a bit of time scanning through Ragnarok's memory. Perhaps +# there should be a config option to disable/enable this? +# +# Code Note: $original is a regexp match, and since 0x7C is '|', its escaped. +sub hackClient { + my $self = shift; + my $pid = shift; + my $handle; + + my $pageSize = Utils::Win32::SystemInfo_PageSize(); + my $minAddr = Utils::Win32::SystemInfo_MinAppAddress(); + my $maxAddr = Utils::Win32::SystemInfo_MaxAppAddress(); + + my $patchFind = pack('C*', 0x66, 0xA3) . '....' # mov word ptr [xxxx], ax + . pack('C*', 0xA0) . '....' # mov al, byte ptr [xxxx] + . pack('C*', 0x3C, 0x0A, # cmp al, 0A + 0x66, 0x89, 0x0D) . '....'; # mov word ptr [xxxx], cx + + my $original = '\\' . pack('C*', 0x7C, 0x6D); # jl 6D + # (to be replaced by) + my $patched = pack('C*', 0xEB, 0x6D); # jmp 6D + + my $patchFind2 = pack('C*', 0xA1) . '....' # mov eax, dword ptr [xxxx] + . pack('C*', 0x8D, 0x4D, 0xF4, # lea ecx, dword ptr [ebp+var_0C] + 0x51); # push ecx + + + $original = $patchFind . $original . $patchFind2; + + message T("Patching client to remove bot detection:\n"), "startup"; + + # Open Ragnarok's process + my $hnd = Utils::Win32::OpenProcess(0x638, $pid); + + # Loop through Ragnarok's memory + my ($nextUpdate, $updateChar, $patchCount) = (0, '.', 0); + for (my $i = $minAddr; $i < $maxAddr; $i += $pageSize) { + # Status update... + my $percent = int($i / ($maxAddr - $minAddr) * 100); + if ($percent >= $nextUpdate) { + if ($nextUpdate % 25 == 0) { + if ($updateChar eq '.') { + message $percent . '%'; + } else { + message $updateChar . $percent . '%' . $updateChar; + } + } else { + message $updateChar; + } + + $updateChar = '.'; + $nextUpdate += 5; + } + + # Ensure we can read/write the memory + my $oldprot = Utils::Win32::VirtualProtectEx($hnd, $i, $pageSize, 0x40); + + if ($oldprot) { + # Read the page + my $data = Utils::Win32::ReadProcessMemory($hnd, $i, $pageSize); + + # Is the patched code in there? + if ($data =~ m/($original)/) { + # It is! + my $matched = $1; + + # Generate the new code, based on the old. + $patched = substr($matched, 0, length($patchFind)) . $patched; + $patched = $patched . substr($matched, length($patchFind) + 2, length($patchFind2)); + + # Patch the data + $data =~ s/$original/$patched/; + + # Write the new code + if (Utils::Win32::WriteProcessMemory($hnd, $i, $data)) { + $updateChar = '*'; + $patchCount++; + } else { + $updateChar = '!'; + } + } + + # Undo the protection change + Utils::Win32::VirtualProtectEx($hnd, $i, $pageSize, $oldprot); + } + } + message "\n"; + + # Close Ragnarok's process + Utils::Win32::CloseProcess($hnd); + + message TF("Client modified in %d places.\n", $patchCount), "startup"; +} + +# +# XKore::redirect([enabled]) +# enabled: Whether you want to redirect (some) console messages to the RO client. +# +# Enable or disable console message redirection. Or, if $enabled is not given, +# returns whether message redirection is currently enabled. +#sub redirect { +# my $arg = shift; +# if ($arg) { +# $redirect = $arg; +# } else { +# return $redirect; +# } +#} + +#sub redirectMessages { +# my ($type, $domain, $level, $globalVerbosity, $message, $user_data) = @_; +# +# return if ($type eq "debug" || $level > 0 || $conState != 5 || !$redirect); +# return if ($domain =~ /^(connection|startup|pm|publicchat|guildchat|selfchat|emotion|drop|inventory|deal)$/); +# return if ($domain =~ /^(attack|skill|list|info|partychat|npc)/); +# +# $message =~ s/\n*$//s; +# $message =~ s/\n/\\n/g; +# main::sendMessage($messageSender, "k", $message); +#} +sub onClientData { + my ($self, $msg) = @_; + my $additional_data; + my $type; + + while (my $message = $self->{tokenizer}->readNext(\$type)) { + $msg .= $message; + } + $self->decryptMessageID(\$msg); + + $msg = $self->{tokenizer}->slicePacket($msg, \$additional_data); # slice packet if needed + + $self->{tokenizer}->add($msg, 1); + + $messageSender->sendToServer($_) for $messageSender->process( + $self->{tokenizer}, $clientPacketHandler + ); + + $self->{tokenizer}->clear(); + + if($additional_data) { + $self->onClientData($additional_data); + } +} + +sub decryptMessageID { + my ($self, $r_message) = @_; + + if(!$messageSender->{encryption}->{crypt_key} && $messageSender->{encryption}->{crypt_key_3}) { + $currentClientKey = $messageSender->{encryption}->{crypt_key_1}; + } elsif(!$currentClientKey) { + return; + } + + my $messageID = unpack("v", $$r_message); + + # Saving Last Informations for Debug Log + my $oldMID = $messageID; + my $oldKey = ($currentClientKey >> 16) & 0x7FFF; + + # Calculating the Encryption Key + $currentClientKey = ($currentClientKey * $messageSender->{encryption}->{crypt_key_3} + $messageSender->{encryption}->{crypt_key_2}) & 0xFFFFFFFF; + + # Xoring the Message ID + $messageID = ($messageID ^ (($currentClientKey >> 16) & 0x7FFF)) & 0xFFFF; + $$r_message = pack("v", $messageID) . substr($$r_message, 2); + + # Debug Log + debug (sprintf("Decrypted MID : [%04X]->[%04X] / KEY : [0x%04X]->[0x%04X]\n", $oldMID, $messageID, $oldKey, ($currentClientKey >> 16) & 0x7FFF), "sendPacket", 0) if $config{debugPacket_sent}; +} + +sub getRecvPackets { + return \%rpackets; +} + +sub kore_map_changed { + # Reset CryptKey when mapserver change + if($currentClientKey && $messageSender->{encryption}->{crypt_key}) { + debug (sprintf("Reseting Key in Map-Change: [0x%04X]->[0x%04X]\n", $currentClientKey, $messageSender->{encryption}->{crypt_key_1})); + $currentClientKey = $messageSender->{encryption}->{crypt_key_1}; + $messageSender->{encryption}->{crypt_key} = $messageSender->{encryption}->{crypt_key_1}; + } +} + +return 1; diff --git a/openkore_llm_knowledge/networking/XKore2.pm b/openkore_llm_knowledge/networking/XKore2.pm new file mode 100644 index 0000000000..047f81c493 --- /dev/null +++ b/openkore_llm_knowledge/networking/XKore2.pm @@ -0,0 +1,187 @@ +######################################################################### +# OpenKore - X-Kore Mode 2 +# Copyright (c) 2007 OpenKore developers +# +# This software is open source, licensed under the GNU General Public +# License, version 2. +# Basically, this means that you're allowed to modify and distribute +# this software. However, if you distribute modified versions, you MUST +# also distribute the source code. +# See http://www.gnu.org/licenses/gpl.html for the full license. +# +# $Revision$ +# $Id$ +# +######################################################################### +## +# MODULE DESCRIPTION: X-Kore 2. +package Network::XKore2; + +use strict; +use Globals qw(%config %rpackets $masterServer $char); +use Utils::Exceptions; +use Plugins; +use Base::Ragnarok::SessionStore; +use Network; +use Network::XKore2::AccountServer; +use Network::XKore2::CharServer; +use Network::XKore2::MapServer; +use Modules 'register'; + +our ($hooks, $sessionStore, $accountServer, $charServer, $mapServer ,$mapServerChange); + +## +# void Network::XKore2::start() +# +# Start the X-Kore 2 subsystem. +sub start { + my $publicIP = $config{XKore_publicIp} || '127.0.0.1'; + my $port = $config{XKore_listenPort} || 6900; + $sessionStore = new Base::Ragnarok::SessionStore(); + $mapServer = new Network::XKore2::MapServer( + host => $publicIP, + port => $config{XKore_listenPort_map} || undef, + serverType => $masterServer->{serverType}, + rpackets => \%rpackets, + sessionStore => $sessionStore + ); + $charServer = new Network::XKore2::CharServer( + host => $publicIP, + port => $config{XKore_listenPort_char} || undef, + serverType => $masterServer->{serverType}, + rpackets => \%rpackets, + mapServer => $mapServer, + sessionStore => $sessionStore, + name => $config{XKore_ID} + ); + eval { + $accountServer = new Network::XKore2::AccountServer( + host => $publicIP, + port => $port, + serverType => $masterServer->{serverType}, + rpackets => \%rpackets, + charServer => $charServer, + sessionStore => $sessionStore + ); + }; + if ( my $e = caught( 'SocketException' ) ) { + die "Unable to start the X-Kore proxy ($publicIP:$port): $@\n" + . "Make sure no other servers are running on port $port."; + } else { + die $@ if $@; + } + $hooks = Plugins::addHooks( + ['packet_pre/sync_request_ex', \&sync_request_ex], + ['packet_pre/map_loaded', \&map_loaded], + ['packet_pre/map_changed', \&map_changed], + ['packet_pre/initialize_message_id_encryption', \&initialize_message_id_encryption], + ['Network::stateChanged', \&stateChanged], + ['Network::clientAlive', \&clientAlive], + ['Network::clientSend', \&clientSend], + ['Network::clientRecv', \&clientRecv], + ['mainLoop_pre', \&mainLoop], + ['disconnected', \&disconnectClients] + ); +} + +## +# void Network::XKore2::stop() +# +# Stop the X-Kore 2 subsystem. +sub stop { + Plugins::delHooks($hooks); +} + +# let kore handle this. +sub sync_request_ex { + my ($self, $args, $client) = @_; + $args->{mangle} = 2; +} + +sub map_loaded { + my (undef, $args) = @_; + + $args->{mangle} = 2; +} + +sub map_changed { + my (undef, $args) = @_; + + $mapServerChange = Storable::dclone($args); + + $args->{mangle} = 2; +} + +sub initialize_message_id_encryption { + my (undef, $args) = @_; + + $args->{mangle} = 2; +} + +sub stateChanged { + $accountServer->setServerType($masterServer->{serverType}); + $charServer->setServerType($masterServer->{serverType}); + $mapServer->setServerType($masterServer->{serverType}); + + if ($Globals::net->getState() == Network::IN_GAME && $mapServerChange ne '') { + $Globals::net->clientSend($mapServer->{recvPacketParser}->reconstruct({ + %$mapServerChange, + switch => 'map_change', + })); + + $mapServerChange = undef; + } +} + +sub clientAlive { + my (undef, $args) = @_; + $args->{return} = @{$mapServer->clients} > 0 && $args->{net}->getState() == Network::IN_GAME; +} + +sub clientSend { + my (undef, $args) = @_; + if ($args->{net}->getState() == Network::IN_GAME) { + foreach my $client (@{$mapServer->clients}) { + my $sendData = $args->{data}; + $client->send($sendData) if (length($sendData) > 0); + } + } + +} + +sub clientRecv { + no encoding 'utf8'; + use bytes; + my (undef, $args) = @_; + + if ($args->{net}->getState() != Network::IN_GAME) { + foreach my $client (@{$mapServer->clients}) { + $client->{outbox}->clear() if ($client->{outbox}); + } + } else { + my $result = ''; + foreach my $client (@{$mapServer->clients}) { + my $type; + while (my $message = $client->{outbox}->readNext(\$type)) { + $result .= $message; + } + } + $args->{return} = $result if (length($result) > 0); + } +} + +sub mainLoop { + if ($accountServer) { + $accountServer->iterate(); + $charServer->iterate(); + $mapServer->iterate(); + $sessionStore->removeTimedOutSessions(); + } +} + +sub disconnectClients { + # DC clients here +} + +1; + diff --git a/openkore_llm_knowledge/networking/XKoreProxy.pm b/openkore_llm_knowledge/networking/XKoreProxy.pm new file mode 100644 index 0000000000..ee1a7350ab --- /dev/null +++ b/openkore_llm_knowledge/networking/XKoreProxy.pm @@ -0,0 +1,764 @@ +######################################################################### +# OpenKore - X-Kore +# +# This software is open source, licensed under the GNU General Public +# License, version 2. +# Basically, this means that you're allowed to modify and distribute +# this software. However, if you distribute modified versions, you MUST +# also distribute the source code. +# See http://www.gnu.org/licenses/gpl.html for the full license. +# +# $Revision$ +# $Id$ +# +######################################################################### +# Note: the difference between XKore2 and XKoreProxy is that XKore2 can +# work headless (it handles all server messages by itself), while +# XKoreProxy lets the RO client handle many server messages. +package Network::XKoreProxy; + +# FIXME: $syncSync is not set correctly (required for ropp) + +use strict; +use base qw(Exporter); +use Exporter; +use IO::Socket::INET; +use Time::HiRes qw(time usleep); +use utf8; + +use Modules 'register'; +use Globals; +use Log qw(message warning error debug); +use Utils qw(dataWaiting timeOut makeIP encodeIP swrite existsInList); +use Misc qw(configModify visualDump); +use Translation qw(T TF); +use I18N qw(bytesToString); +use Interface; +use Network; +use Network::Send (); +use Utils::Exceptions; +use Network::MessageTokenizer; + +my $clientBuffer; +my $currentClientKey = 0; + +# Members: +# +# Socket proxy_listen +# A server socket which accepts new connections from the RO client. +# This is only defined when the RO client hasn't already connected +# to XKoreProxy. +# +# Socket proxy +# A client socket, which connects XKoreProxy with the RO client. +# This is only defined when the RO client has connected to XKoreProxy. + +## +# Network::XKoreProxy->new() +# +# Initialize X-Kore-Proxy mode. +sub new { + my $class = shift; + my $ip = $config{XKore_listenIp} || '0.0.0.0'; + my $port = $config{XKore_listenPort} || 6901; + my $self = bless {}, $class; + + # Reuse code from Network::DirectConnection to connect to the server + require Network::DirectConnection; + $self->{server} = new Network::DirectConnection($self); + + $self->{tokenizer} = new Network::MessageTokenizer($self->getRecvPackets()); + $self->{publicIP} = $config{XKore_publicIp} || undef; + $self->{client_state} = 0; + $self->{nextIp} = undef; + $self->{nextPort} = undef; + $self->{charServerIp} = undef; + $self->{charServerPort} = undef; + $self->{gotError} = 0; + $self->{waitingClient} = 1; + { + no encoding 'utf8'; + $self->{packetPending} = ''; + $clientBuffer = ''; + } + + eval { + $clientPacketHandler = Network::ClientReceive->new; + $packetParser = Network::Receive->create($self, $masterServer->{serverType}); + $messageSender = Network::Send->create($self, $masterServer->{serverType}); + }; + if (my $e = caught('Exception::Class::Base')) { + $interface->errorDialog($e->message()); + $quit = 1; + return; + } + + message T("X-Kore mode intialized.\n"), "startup"; + + return $self; +} + +sub version { + return 1; +} + +sub DESTROY { + my $self = shift; + + close($self->{proxy_listen}); + close($self->{proxy}); +} + + +###################### +## Server Functions ## +###################### + +sub serverAlive { + my $self = shift; + return $self->{server}->serverAlive; +} + +sub serverConnect { + my $self = shift; + my $host = shift; + my $port = shift; + + return $self->{server}->serverConnect($host, $port); +} + +sub serverPeerHost { + my $self = shift; + return $self->{server}->serverPeerHost if ($self->serverAlive); + return undef; +} + +sub serverPeerPort { + my $self = shift; + return $self->{server}->serverPeerPort if ($self->serverAlive); + return undef; +} + +sub serverRecv { + my $self = shift; + return $self->{server}->serverRecv(); +} + +sub serverSend { + my $self = shift; + my $msg = shift; + + if (!defined $msg || length($msg) >= 2) { + # Get packet switch + my $switch = uc(unpack("H2", substr($msg, 1, 1))) . uc(unpack("H2", substr($msg, 0, 1))); + # Handle 'master_login' + if ($switch eq "0C26" && $config{username} && $config{password}) { + # Log master login packet + warning "Modifying packet 'master_login'...\n", "xkoreProxy"; + # Parse master login packet + my ($game_code, $username, $password_rijndael, $flag) = unpack('a4 Z51 a32 a5', substr($msg, 2)); + # Rebuild master login packet + $msg = $messageSender->reconstruct({ + switch => 'master_login', + game_code => $game_code, + username => $config{username}, + password => $config{password}, + flag => $flag, + }); + } + # Handle 'token_login' + elsif ($switch eq "0825" && $config{username} && $config{password}) { + # Log token login packet + warning "Modifying packet 'token_login'...\n", "xkoreProxy"; + # Parse token login packet + my ($len, $version, $master_version, $username, $mac_hyphen_separated, $ip, $token) = unpack('v V C Z51 a17 a15 a*', substr($msg, 2)); + # Rebuild token login packet + $msg = $messageSender->reconstruct({ + switch => "token_login", + len => $len, + version => $version, + master_version => $master_version, + username => $config{username}, + mac_hyphen_separated => $mac_hyphen_separated, + ip => inet_aton($self->{publicIP} || $self->{proxy}->sockhost), + token => $token, + }); + } + } + + $self->{server}->serverSend($msg); +} + +sub serverDisconnect { + my $self = shift; + my $preserveClient = shift; + + return unless ($self->serverAlive); + + close($self->{proxy}) unless $preserveClient; + $self->{waitClientDC} = 1 if $preserveClient; + + # user has played with relog command. + if ($timeout_ex{'master'}{'time'}) { + undef $timeout_ex{'master'}{'time'}; + $self->{waitingClient} = 1; + } + return $self->{server}->serverDisconnect(); +} + +sub serverAddress { + my ($self) = @_; + return $self->{server}->serverAddress(); +} + +sub getState { + my ($self) = @_; + return $self->{server}->getState(); +} + +sub setState { + my ($self, $state) = @_; + $self->{server}->setState($state); +} + + +###################### +## Client Functions ## +###################### + +sub clientAlive { + my $self = shift; + return $self->proxyAlive(); +} + +sub proxyAlive { + my $self = shift; + return $self->{proxy} && $self->{proxy}->connected; +} + +sub clientPeerHost { + my $self = shift; + return $self->{proxy}->peerhost if ($self->proxyAlive); + return undef; +} + +sub clientPeerPort { + my $self = shift; + return $self->{proxy}->peerport if ($self->proxyAlive); + return undef; +} + +sub clientSend { + my $self = shift; + my $msg = shift; + my $dontMod = shift; + + return unless ($self->proxyAlive); + + my $switch = uc(unpack("H2", substr($msg, 1, 1))) . uc(unpack("H2", substr($msg, 0, 1))); + + if ($switch eq "08B9") { # login_pin_code_request + my $seed = unpack("V", substr($msg, 2, 4)); + my $accountID = unpack("a4", substr($msg, 6, 4)); + my $flag = unpack("v", substr($msg, 10, 2)); + + if ($flag == 1 and $config{loginPinCode}) { + $messageSender->sendLoginPinCode($seed, 0); + } + } + + $msg = $self->modifyPacketIn($msg, $switch) unless ($dontMod); + if ($config{debugPacket_ro_received}) { + debug "Modified packet sent to client\n", "xkoreProxy"; + visualDump($msg, 'sendToClient'); + } + + # queue message instead of sending directly + $clientBuffer .= $msg; +} + +sub clientFlush { + my $self = shift; + + return unless (length($clientBuffer)); + + $self->{proxy}->send($clientBuffer); + debug "Client network buffer flushed out\n", "xkoreProxy"; + $clientBuffer = ''; +} + +sub clientRecv { + my ($self, $msg) = @_; + + return undef unless ($self->proxyAlive && dataWaiting(\$self->{proxy})); + + $self->{proxy}->recv($msg, 1024 * 32); + if (length($msg) == 0) { + # Connection from client closed + close($self->{proxy}); + return undef; + } + + my $switch = uc(unpack("H2", substr($msg, 1, 1))) . uc(unpack("H2", substr($msg, 0, 1))); + $msg = $self->modifyPacketOut($msg, $switch); + + if($self->getState() eq Network::IN_GAME || $self->getState() eq Network::CONNECTED_TO_CHAR_SERVER) { + $self->onClientData($msg); + return undef; + } + + return $msg; +} + +sub onClientData { + my ($self, $msg) = @_; + my $additional_data; + my $type; + + while (my $message = $self->{tokenizer}->readNext(\$type)) { + $msg .= $message; + } + $self->decryptMessageID(\$msg); + + + $self->{tokenizer}->add($msg, 1); + + $messageSender->sendToServer($_) for $messageSender->process( + $self->{tokenizer}, $clientPacketHandler + ); + + $self->{tokenizer}->clear(); + + if($additional_data) { + $self->onClientData($additional_data); + } +} + +sub checkConnection { + my $self = shift; + + # Check connection to the client + $self->checkProxy(); + + # Check server connection + $self->checkServer(); +} + +sub checkProxy { + my $self = shift; + + if (defined $self->{proxy_listen}) { + # Listening for a client + if (dataWaiting($self->{proxy_listen})) { + # Client is connecting... + $self->{proxy} = $self->{proxy_listen}->accept; + + # Tell 'em about the new client + my $host = $self->clientPeerHost; + my $port = $self->clientPeerPort; + debug "XKore Proxy: RO Client connected ($host:$port).\n", "connection"; + + # Stop listening and clear errors. + close($self->{proxy_listen}); + undef $self->{proxy_listen}; + $self->{gotError} = 0; + } + #return; + + } elsif (!$self->proxyAlive) { + # Client disconnected... (or never existed) + if ($self->serverAlive()) { + message T("Client disconnected\n"), "connection"; + $self->setState(Network::NOT_CONNECTED) if ($self->getState() == Network::IN_GAME); + $self->{waitingClient} = 1; + $self->serverDisconnect(); + } + + close $self->{proxy} if $self->{proxy}; + $self->{waitClientDC} = undef; + debug "Removing pending packet from queue\n", "xkoreProxy" if (defined $self->{packetPending}); + $self->{packetPending} = ''; + + # FIXME: there's a racing condition here. If the RO client tries to connect + # to the listening port before we've set it up (this happens if sleepTime is + # sufficiently high), then the client will freeze. + + # (Re)start listening... + my $ip = $config{XKore_listenIp} || '127.0.0.1'; + my $port = $config{XKore_listenPort} || 6901; + $self->{proxy_listen} = new IO::Socket::INET( + LocalAddr => $ip, + LocalPort => $port, + Listen => 5, + Proto => 'tcp', + ReuseAddr => 1); + die "Unable to start the X-Kore proxy ($ip:$port): $@\n" . + "Make sure no other servers are running on port $port." unless $self->{proxy_listen}; + + # setup master server if necessary + getMainServer(); + + message TF("Waiting Ragnarok Client to connect on (%s:%s)\n", ($ip eq '127.0.0.1' ? 'localhost' : $ip), $port), "startup" if ($self->{waitingClient} == 1); + $self->{waitingClient} = 0; + return; + } + + if ($self->proxyAlive() && defined($self->{packetPending})) { + checkPacketReplay(); + } +} + +sub checkServer { + my $self = shift; + + # Do nothing until the client has (re)connected to us + return if (!$self->proxyAlive() || $self->{waitClientDC}); + + # Connect to the next server for proxying the packets + if (!$self->serverAlive()) { + # if no next server was defined by received packets, setup a primary server. + my $master = $masterServer = $masterServers{$config{'master'}}; + + # Setup the next server to connect. + if (!$self->{nextIp} || !$self->{nextPort}) { + if ($master->{OTP_ip} && $master->{OTP_port}) { + $self->{nextIp} = $master->{OTP_ip}; + $self->{nextPort} = $master->{OTP_port}; + } else { + $self->{nextIp} = $master->{ip}; + $self->{nextPort} = $master->{port}; + } + message TF("Proxying to [%s]\n", $config{master}), "connection" unless ($self->{gotError}); + } + + $self->serverConnect($self->{nextIp}, $self->{nextPort}) unless ($self->{gotError}); + if (!$self->serverAlive()) { + $self->{charServerIp} = undef; + $self->{charServerPort} = undef; + close($self->{proxy}); + error T("Invalid server specified or server does not exist...\n"), "connection" if (!$self->{gotError}); + $self->{gotError} = 1; + } + + # clean Next Server uppon connection + $self->{nextIp} = undef; + $self->{nextPort} = undef; + } +} + +## +# $Network_XKoreProxy->checkPacketReplay() +# +# Setup a timer to repeat the received logon/server change packet to the client +# in case it didn't responded in an appropriate time. +# +# This is an internal function. +sub checkPacketReplay { + my $self = shift; + + #message "Pending packet check\n"; + + if ($self->{replayTimeout}{time} && timeOut($self->{replayTimeout})) { + if ($self->{packetReplayTrial} < 3) { + warning TF("Client did not respond in time.\n" . + "Trying to replay the packet for %s of 3 times\n", $self->{packetReplayTrial}++), "connection"; + $self->clientSend($self->{packetPending}); + $self->{replayTimeout}{time} = time; + $self->{replayTimeout}{timeout} = 2.5; + } else { + error T("Client did not respond. Forcing disconnection\n"), "connection"; + close($self->{proxy}); + return; + } + + } elsif (!$self->{replayTimeout}{time}) { + $self->{replayTimeout}{time} = time; + $self->{replayTimeout}{timeout} = 2.5; + } +} + +sub modifyPacketIn { + my ($self, $msg, $switch) = @_; + + return undef if (length($msg) < 1); + + if ($switch eq "02AE") { + $msg = ""; + } + + # packet replay check: reset status for every different packet received + if ($self->{packetPending} && ($self->{packetPending} ne $msg)) { + debug "Removing pending packet from queue\n", "connection"; + use bytes; no encoding 'utf8'; + delete $self->{replayTimeout}; + $self->{packetPending} = ''; + $self->{packetReplayTrial} = 0; + } elsif ($self->{packetPending} && ($self->{packetPending} eq $msg)) { + # avoid doubled 0259 message: could mess the character selection and hang up the client + if ($switch eq "0259") { + debug T("Logon-grant packet received twice! Avoiding bug in client.\n"), "connection"; + $self->{packetPending} = undef; + return undef; + } + } + + # server list + if ($switch eq "0069" || $switch eq "0276" || $switch eq "0A4D" || $switch eq "0AC4" || $switch eq "0AC9" || $switch eq "0B07" || $switch eq "0B60" || $switch eq "0C32") { + use bytes; no encoding 'utf8'; + + # queue the packet as requiring client's response in time + $self->{packetPending} = $msg; + + # Show list of character servers. + unless ($config{server} =~/\d+/) { + my @menuServerList; + foreach my $server (@servers) { + push @menuServerList, $server->{name}; + } + my $ret = $interface->showMenu( + T("Please select your login server."), + \@menuServerList, + title => T("Select Login Server")); + if ($ret == -1) { + quit(); + } else { + main::configModify('server', $ret, 1); + undef $conState_tries; + } + } + + debug "Modifying Account Info packet...\n", "xkoreProxy"; + + my $xKoreCharServer = $servers[$config{server}]; + + $self->{nextIp} = $self->{charServerIp} = $xKoreCharServer->{ip}; + $self->{nextPort} = $self->{charServerPort} = $xKoreCharServer->{port}; + + $xKoreCharServer->{ip} = $self->{publicIP} || $self->{proxy}->sockhost; + $xKoreCharServer->{port} = $self->{proxy}->sockport; + $xKoreCharServer->{ip_port} = "$xKoreCharServer->{ip}:$xKoreCharServer->{port}"; + + my @serverList; + push @serverList, $xKoreCharServer; + + $msg = $packetParser->reconstruct({ + switch => $switch, + sessionID => $sessionID, + accountID => $accountID, + sessionID2 => $sessionID2, + accountSex => $accountSex, + servers => \@serverList, + }); + + message T("Closing connection to Account Server\n"), 'connection' if (!$self->{packetReplayTrial}); + $self->serverDisconnect(1); + + } elsif ($switch eq "0071" || $switch eq "0AC5") { # login in map-server + my ($mapInfo, $server_info); + + # queue the packet as requiring client's response in time + $self->{packetPending} = $msg; + + # Proxy the Logon to Map server + debug "Modifying Map Logon packet...\n", "xkoreProxy"; + + if ($switch eq '0AC5') { # cRO 2017 + $server_info = { + types => 'a4 Z16 a4 v a128', + keys => [qw(charID mapName mapIP mapPort mapUrl)], + }; + + } else { + $server_info = { + types => 'a4 Z16 a4 v', + keys => [qw(charID mapName mapIP mapPort)], + }; + } + + my $ip = $self->{publicIP} || $self->{proxy}->sockhost; + + @{$mapInfo}{@{$server_info->{keys}}} = unpack($server_info->{types}, substr($msg, 2)); + + if (exists $mapInfo->{mapUrl} && $mapInfo->{'mapUrl'} =~ /.*\:\d+/) { # in cRO we have server.alias.com:port + @{$mapInfo}{@{[qw(mapIP port)]}} = split (/\:/, $mapInfo->{'mapUrl'}); + $mapInfo->{mapIP} =~ s/^\s+|\s+$//g; + $mapInfo->{port} =~ tr/0-9//cd; + } else { + $mapInfo->{mapIP} = inet_ntoa($mapInfo->{mapIP}); + } + + if($masterServer->{'private'}) { + $mapInfo->{mapIP} = $masterServer->{ip}; + } + + $msg = $packetParser->reconstruct({ + switch => $switch, + charID => $mapInfo->{'charID'}, + mapName => $mapInfo->{'mapName'}, + mapIP => inet_aton($ip), + mapPort => $self->{proxy}->sockport, + mapUrl => $ip.':'.$self->{proxy}->sockport, + }); + + $self->{nextIp} = $mapInfo->{'mapIP'}; + $self->{nextPort} = $mapInfo->{'mapPort'}; + debug " next server to connect ($self->{nextIp}:$self->{nextPort})\n", "connection"; + + # reset key when change map-server + if ($currentClientKey && $messageSender->{encryption}->{crypt_key}) { + $currentClientKey = $messageSender->{encryption}->{crypt_key_1}; + $messageSender->{encryption}->{crypt_key} = $messageSender->{encryption}->{crypt_key_1}; + } + + if ($switch eq "0071" || $switch eq "0AC5") { + message T("Closing connection to Character Server\n"), 'connection' if (!$self->{packetReplayTrial}); + } else { + message T("Closing connection to Map Server\n"), "connection" if (!$self->{packetReplayTrial}); + } + $self->serverDisconnect(1); + + } elsif($switch eq "0092" || $switch eq "0AC7" || $switch eq "0A4C") { # In Game Map-server changed + my ($mapInfo, $server_info); + + if ($switch eq '0AC7') { # cRO 2017 + $server_info = { + types => 'Z16 v2 a4 v a128', + keys => [qw(map x y IP port url)], + }; + + } else { + $server_info = { + types => 'Z16 v2 a4 v', + keys => [qw(map x y IP port)], + }; + } + + my $ip = $self->{publicIP} || $self->{proxy}->sockhost; + my $port = $self->{proxy}->sockport; + + @{$mapInfo}{@{$server_info->{keys}}} = unpack($server_info->{types}, substr($msg, 2)); + + if (exists $mapInfo->{url} && $mapInfo->{'url'} =~ /.*\:\d+/) { # in cRO we have server.alias.com:port + @{$mapInfo}{@{[qw(ip port)]}} = split (/\:/, $mapInfo->{'url'}); + $mapInfo->{ip} =~ s/^\s+|\s+$//g; + $mapInfo->{port} =~ tr/0-9//cd; + } else { + $mapInfo->{ip} = inet_ntoa($mapInfo->{'IP'}); + } + + if($masterServer->{'private'}) { + $mapInfo->{ip} = $masterServer->{ip}; + } + + $msg = $packetParser->reconstruct({ + switch => $switch, + map => $mapInfo->{'map'}, + x => $mapInfo->{'x'}, + y => $mapInfo->{'y'}, + IP => inet_aton($ip), + port => $port, + url => $ip.':'.$port, + }); + + $self->{nextIp} = $mapInfo->{ip}; + $self->{nextPort} = $mapInfo->{'port'}; + debug " next server to connect ($self->{nextIp}:$self->{nextPort})\n", "connection"; + + # reset key when change map-server + if ($currentClientKey && $messageSender->{encryption}->{crypt_key}) { + $currentClientKey = $messageSender->{encryption}->{crypt_key_1}; + $messageSender->{encryption}->{crypt_key} = $messageSender->{encryption}->{crypt_key_1}; + } + + } elsif ($switch eq "006A" || $switch eq "006C" || $switch eq "0081" || $switch eq "02CA" || $switch eq "083E" || $switch eq "0ACD" || $switch eq "0AE0") { # error while login in server + # Show error message + error T("Server reported an error, disconnecting...\n"), "connection"; + # An error occurred. Restart proxying + $self->{gotError} = 1; + $self->{nextIp} = undef; + $self->{nextPort} = undef; + $self->{charServerIp} = undef; + $self->{charServerPort} = undef; + $self->serverDisconnect(); + + } elsif ($switch eq "00B3") { + $self->{nextIp} = $self->{charServerIp}; + $self->{nextPort} = $self->{charServerPort}; + $self->serverDisconnect(1); + + } elsif ($switch eq "0259") { + # queue the packet as requiring client's response in time + $self->{packetPending} = $msg; + } elsif ($switch eq "0AE3") { # If token was received, we need to connect to login server + # Parse token response + my ($len, $login_type, $flag, $login_token) = unpack('v l Z20 Z*', substr($msg, 2)); + # If token response contains a login token, we need to connect to the master server + if (length($login_token)) { + # Get master config + my $master = $masterServer = $masterServers{$config{'master'}}; + # Set next server to connect + $self->{nextIp} = $master->{ip}; + $self->{nextPort} = $master->{port}; + } else { + error T("Authentication failed, token not received: $flag.\n"), "connection"; + } + # Disconnect from token server + message T("Closing connection to Token Server\n"), 'connection' if (!$self->{packetReplayTrial}); + $self->serverDisconnect(1); + } + + return $msg; +} + +sub modifyPacketOut { + my ($self, $msg, $switch) = @_; + + return $msg; +} + +sub getMainServer { + if ($config{'master'} eq "" || $config{'master'} =~ /^\d+$/ || !exists $masterServers{$config{'master'}}) { + my @servers = sort { lc($a) cmp lc($b) } keys(%masterServers); + my $choice = $interface->showMenu( + T("Please choose a master server to connect to."), + \@servers, + title => T("Master servers")); + if ($choice == -1) { + exit; + } else { + configModify('master', $servers[$choice], 1); + } + } +} + +sub decryptMessageID { + my ($self, $r_message) = @_; + + if(!$messageSender->{encryption}->{crypt_key} && $messageSender->{encryption}->{crypt_key_3}) { + $currentClientKey = $messageSender->{encryption}->{crypt_key_1}; + } elsif(!$currentClientKey) { + return; + } + + my $messageID = unpack("v", $$r_message); + + # Saving Last Informations for Debug Log + my $oldMID = $messageID; + my $oldKey = ($currentClientKey >> 16) & 0x7FFF; + + # Calculating the Encryption Key + $currentClientKey = ($currentClientKey * $messageSender->{encryption}->{crypt_key_3} + $messageSender->{encryption}->{crypt_key_2}) & 0xFFFFFFFF; + + # Xoring the Message ID + $messageID = ($messageID ^ (($currentClientKey >> 16) & 0x7FFF)) & 0xFFFF; + $$r_message = pack("v", $messageID) . substr($$r_message, 2); + + # Debug Log + debug (sprintf("Decrypted MID : [%04X]->[%04X] / KEY : [0x%04X]->[0x%04X]\n", $oldMID, $messageID, $oldKey, ($currentClientKey >> 16) & 0x7FFF), "sendPacket", 0) if $config{debugPacket_sent}; +} + +sub getRecvPackets { + return \%rpackets; +} + +return 1; diff --git a/openkore_llm_knowledge/tables/SKILL_id_handle.txt b/openkore_llm_knowledge/tables/SKILL_id_handle.txt new file mode 100644 index 0000000000..1657a7aff5 --- /dev/null +++ b/openkore_llm_knowledge/tables/SKILL_id_handle.txt @@ -0,0 +1,1707 @@ +# see kRO client: luafiles514\lua files\skillinfoz\SkillID.lub +1 NV_BASIC +2 SM_SWORD +3 SM_TWOHAND +4 SM_RECOVERY +5 SM_BASH +6 SM_PROVOKE +7 SM_MAGNUM +8 SM_ENDURE +9 MG_SRECOVERY +10 MG_SIGHT +11 MG_NAPALMBEAT +12 MG_SAFETYWALL +13 MG_SOULSTRIKE +14 MG_COLDBOLT +15 MG_FROSTDIVER +16 MG_STONECURSE +17 MG_FIREBALL +18 MG_FIREWALL +19 MG_FIREBOLT +20 MG_LIGHTNINGBOLT +21 MG_THUNDERSTORM +22 AL_DP +23 AL_DEMONBANE +24 AL_RUWACH +25 AL_PNEUMA +26 AL_TELEPORT +27 AL_WARP +28 AL_HEAL +29 AL_INCAGI +30 AL_DECAGI +31 AL_HOLYWATER +32 AL_CRUCIS +33 AL_ANGELUS +34 AL_BLESSING +35 AL_CURE +36 MC_INCCARRY +37 MC_DISCOUNT +38 MC_OVERCHARGE +39 MC_PUSHCART +40 MC_IDENTIFY +41 MC_VENDING +42 MC_MAMMONITE +43 AC_OWL +44 AC_VULTURE +45 AC_CONCENTRATION +46 AC_DOUBLE +47 AC_SHOWER +48 TF_DOUBLE +49 TF_MISS +50 TF_STEAL +51 TF_HIDING +52 TF_POISON +53 TF_DETOXIFY +54 ALL_RESURRECTION +55 KN_SPEARMASTERY +56 KN_PIERCE +57 KN_BRANDISHSPEAR +58 KN_SPEARSTAB +59 KN_SPEARBOOMERANG +60 KN_TWOHANDQUICKEN +61 KN_AUTOCOUNTER +62 KN_BOWLINGBASH +63 KN_RIDING +64 KN_CAVALIERMASTERY +65 PR_MACEMASTERY +66 PR_IMPOSITIO +67 PR_SUFFRAGIUM +68 PR_ASPERSIO +69 PR_BENEDICTIO +70 PR_SANCTUARY +71 PR_SLOWPOISON +72 PR_STRECOVERY +73 PR_KYRIE +74 PR_MAGNIFICAT +75 PR_GLORIA +76 PR_LEXDIVINA +77 PR_TURNUNDEAD +78 PR_LEXAETERNA +79 PR_MAGNUS +80 WZ_FIREPILLAR +81 WZ_SIGHTRASHER +82 WZ_FIREIVY +83 WZ_METEOR +84 WZ_JUPITEL +85 WZ_VERMILION +86 WZ_WATERBALL +87 WZ_ICEWALL +88 WZ_FROSTNOVA +89 WZ_STORMGUST +90 WZ_EARTHSPIKE +91 WZ_HEAVENDRIVE +92 WZ_QUAGMIRE +93 WZ_ESTIMATION +94 BS_IRON +95 BS_STEEL +96 BS_ENCHANTEDSTONE +97 BS_ORIDEOCON +98 BS_DAGGER +99 BS_SWORD +100 BS_TWOHANDSWORD +101 BS_AXE +102 BS_MACE +103 BS_KNUCKLE +104 BS_SPEAR +105 BS_HILTBINDING +106 BS_FINDINGORE +107 BS_WEAPONRESEARCH +108 BS_REPAIRWEAPON +109 BS_SKINTEMPER +110 BS_HAMMERFALL +111 BS_ADRENALINE +112 BS_WEAPONPERFECT +113 BS_OVERTHRUST +114 BS_MAXIMIZE +115 HT_SKIDTRAP +116 HT_LANDMINE +117 HT_ANKLESNARE +118 HT_SHOCKWAVE +119 HT_SANDMAN +120 HT_FLASHER +121 HT_FREEZINGTRAP +122 HT_BLASTMINE +123 HT_CLAYMORETRAP +124 HT_REMOVETRAP +125 HT_TALKIEBOX +126 HT_BEASTBANE +127 HT_FALCON +128 HT_STEELCROW +129 HT_BLITZBEAT +130 HT_DETECTING +131 HT_SPRINGTRAP +132 AS_RIGHT +133 AS_LEFT +134 AS_KATAR +135 AS_CLOAKING +136 AS_SONICBLOW +137 AS_GRIMTOOTH +138 AS_ENCHANTPOISON +139 AS_POISONREACT +140 AS_VENOMDUST +141 AS_SPLASHER +142 NV_FIRSTAID +143 NV_TRICKDEAD +144 SM_MOVINGRECOVERY +145 SM_FATALBLOW +146 SM_AUTOBERSERK +147 AC_MAKINGARROW +148 AC_CHARGEARROW +149 TF_SPRINKLESAND +150 TF_BACKSLIDING +151 TF_PICKSTONE +152 TF_THROWSTONE +153 MC_CARTREVOLUTION +154 MC_CHANGECART +155 MC_LOUD +156 AL_HOLYLIGHT +157 MG_ENERGYCOAT +158 NPC_PIERCINGATT +159 NPC_MENTALBREAKER +160 NPC_RANGEATTACK +161 NPC_ATTRICHANGE +162 NPC_CHANGEWATER +163 NPC_CHANGEGROUND +164 NPC_CHANGEFIRE +165 NPC_CHANGEWIND +166 NPC_CHANGEPOISON +167 NPC_CHANGEHOLY +168 NPC_CHANGEDARKNESS +169 NPC_CHANGETELEKINESIS +170 NPC_CRITICALSLASH +171 NPC_COMBOATTACK +172 NPC_GUIDEDATTACK +173 NPC_SELFDESTRUCTION +174 NPC_SPLASHATTACK +175 NPC_SUICIDE +176 NPC_POISON +177 NPC_BLINDATTACK +178 NPC_SILENCEATTACK +179 NPC_STUNATTACK +180 NPC_PETRIFYATTACK +181 NPC_CURSEATTACK +182 NPC_SLEEPATTACK +183 NPC_RANDOMATTACK +184 NPC_WATERATTACK +185 NPC_GROUNDATTACK +186 NPC_FIREATTACK +187 NPC_WINDATTACK +188 NPC_POISONATTACK +189 NPC_HOLYATTACK +190 NPC_DARKNESSATTACK +191 NPC_TELEKINESISATTACK +192 NPC_MAGICALATTACK +193 NPC_METAMORPHOSIS +194 NPC_PROVOCATION +195 NPC_SMOKING +196 NPC_SUMMONSLAVE +197 NPC_EMOTION +198 NPC_TRANSFORMATION +199 NPC_BLOODDRAIN +200 NPC_ENERGYDRAIN +201 NPC_KEEPING +202 NPC_DARKBREATH +203 NPC_DARKBLESSING +204 NPC_BARRIER +205 NPC_DEFENDER +206 NPC_LICK +207 NPC_HALLUCINATION +208 NPC_REBIRTH +209 NPC_SUMMONMONSTER +210 RG_SNATCHER +211 RG_STEALCOIN +212 RG_BACKSTAP +213 RG_TUNNELDRIVE +214 RG_RAID +215 RG_STRIPWEAPON +216 RG_STRIPSHIELD +217 RG_STRIPARMOR +218 RG_STRIPHELM +219 RG_INTIMIDATE +220 RG_GRAFFITI +221 RG_FLAGGRAFFITI +222 RG_CLEANER +223 RG_GANGSTER +224 RG_COMPULSION +225 RG_PLAGIARISM +226 AM_AXEMASTERY +227 AM_LEARNINGPOTION +228 AM_PHARMACY +229 AM_DEMONSTRATION +230 AM_ACIDTERROR +231 AM_POTIONPITCHER +232 AM_CANNIBALIZE +233 AM_SPHEREMINE +234 AM_CP_WEAPON +235 AM_CP_SHIELD +236 AM_CP_ARMOR +237 AM_CP_HELM +238 AM_BIOETHICS +239 AM_BIOTECHNOLOGY +240 AM_CREATECREATURE +241 AM_CULTIVATION +242 AM_FLAMECONTROL +243 AM_CALLHOMUN +244 AM_REST +245 AM_DRILLMASTER +246 AM_HEALHOMUN +247 AM_RESURRECTHOMUN +248 CR_TRUST +249 CR_AUTOGUARD +250 CR_SHIELDCHARGE +251 CR_SHIELDBOOMERANG +252 CR_REFLECTSHIELD +253 CR_HOLYCROSS +254 CR_GRANDCROSS +255 CR_DEVOTION +256 CR_PROVIDENCE +257 CR_DEFENDER +258 CR_SPEARQUICKEN +259 MO_IRONHAND +260 MO_SPIRITSRECOVERY +261 MO_CALLSPIRITS +262 MO_ABSORBSPIRITS +263 MO_TRIPLEATTACK +264 MO_BODYRELOCATION +265 MO_DODGE +266 MO_INVESTIGATE +267 MO_FINGEROFFENSIVE +268 MO_STEELBODY +269 MO_BLADESTOP +270 MO_EXPLOSIONSPIRITS +271 MO_EXTREMITYFIST +272 MO_CHAINCOMBO +273 MO_COMBOFINISH +274 SA_ADVANCEDBOOK +275 SA_CASTCANCEL +276 SA_MAGICROD +277 SA_SPELLBREAKER +278 SA_FREECAST +279 SA_AUTOSPELL +280 SA_FLAMELAUNCHER +281 SA_FROSTWEAPON +282 SA_LIGHTNINGLOADER +283 SA_SEISMICWEAPON +284 SA_DRAGONOLOGY +285 SA_VOLCANO +286 SA_DELUGE +287 SA_VIOLENTGALE +288 SA_LANDPROTECTOR +289 SA_DISPELL +290 SA_ABRACADABRA +291 SA_MONOCELL +292 SA_CLASSCHANGE +293 SA_SUMMONMONSTER +294 SA_REVERSEORCISH +295 SA_DEATH +296 SA_FORTUNE +297 SA_TAMINGMONSTER +298 SA_QUESTION +299 SA_GRAVITY +300 SA_LEVELUP +301 SA_INSTANTDEATH +302 SA_FULLRECOVERY +303 SA_COMA +304 BD_ADAPTATION +305 BD_ENCORE +306 BD_LULLABY +307 BD_RICHMANKIM +308 BD_ETERNALCHAOS +309 BD_DRUMBATTLEFIELD +310 BD_RINGNIBELUNGEN +311 BD_ROKISWEIL +312 BD_INTOABYSS +313 BD_SIEGFRIED +314 BD_RAGNAROK +315 BA_MUSICALLESSON +316 BA_MUSICALSTRIKE +317 BA_DISSONANCE +318 BA_FROSTJOKE +319 BA_WHISTLE +320 BA_ASSASSINCROSS +321 BA_POEMBRAGI +322 BA_APPLEIDUN +323 DC_DANCINGLESSON +324 DC_THROWARROW +325 DC_UGLYDANCE +326 DC_SCREAM +327 DC_HUMMING +328 DC_DONTFORGETME +329 DC_FORTUNEKISS +330 DC_SERVICEFORYOU +331 NPC_RANDOMMOVE +332 NPC_SPEEDUP +333 NPC_REVENGE +334 WE_MALE +335 WE_FEMALE +336 WE_CALLPARTNER +337 ITM_TOMAHAWK +338 NPC_DARKCROSS +339 NPC_GRANDDARKNESS +340 NPC_DARKSTRIKE +341 NPC_DARKTHUNDER +342 NPC_STOP +343 NPC_WEAPONBRAKER +344 NPC_ARMORBRAKE +345 NPC_HELMBRAKE +346 NPC_SHIELDBRAKE +347 NPC_UNDEADATTACK +348 NPC_CHANGEUNDEAD +349 NPC_POWERUP +350 NPC_AGIUP +351 NPC_SIEGEMODE +352 NPC_CALLSLAVE +353 NPC_INVISIBLE +354 NPC_RUN +355 LK_AURABLADE +356 LK_PARRYING +357 LK_CONCENTRATION +358 LK_TENSIONRELAX +359 LK_BERSERK +360 LK_FURY +361 HP_ASSUMPTIO +362 HP_BASILICA +363 HP_MEDITATIO +364 HW_SOULDRAIN +365 HW_MAGICCRASHER +366 HW_MAGICPOWER +367 PA_PRESSURE +368 PA_SACRIFICE +369 PA_GOSPEL +370 CH_PALMSTRIKE +371 CH_TIGERFIST +372 CH_CHAINCRUSH +373 PF_HPCONVERSION +374 PF_SOULCHANGE +375 PF_SOULBURN +376 ASC_KATAR +377 ASC_HALLUCINATION +378 ASC_EDP +379 ASC_BREAKER +380 SN_SIGHT +381 SN_FALCONASSAULT +382 SN_SHARPSHOOTING +383 SN_WINDWALK +384 WS_MELTDOWN +385 WS_CREATECOIN +386 WS_CREATENUGGET +387 WS_CARTBOOST +388 WS_SYSTEMCREATE +389 ST_CHASEWALK +390 ST_REJECTSWORD +391 ST_STEALBACKPACK +392 CR_ALCHEMY +393 CR_SYNTHESISPOTION +394 CG_ARROWVULCAN +395 CG_MOONLIT +396 CG_MARIONETTE +397 LK_SPIRALPIERCE +398 LK_HEADCRUSH +399 LK_JOINTBEAT +400 HW_NAPALMVULCAN +401 CH_SOULCOLLECT +402 PF_MINDBREAKER +403 PF_MEMORIZE +404 PF_FOGWALL +405 PF_SPIDERWEB +406 ASC_METEORASSAULT +407 ASC_CDP +408 WE_BABY +409 WE_CALLPARENT +410 WE_CALLBABY +411 TK_RUN +412 TK_READYSTORM +413 TK_STORMKICK +414 TK_READYDOWN +415 TK_DOWNKICK +416 TK_READYTURN +417 TK_TURNKICK +418 TK_READYCOUNTER +419 TK_COUNTER +420 TK_DODGE +421 TK_JUMPKICK +422 TK_HPTIME +423 TK_SPTIME +424 TK_POWER +425 TK_SEVENWIND +426 TK_HIGHJUMP +427 SG_FEEL +428 SG_SUN_WARM +429 SG_MOON_WARM +430 SG_STAR_WARM +431 SG_SUN_COMFORT +432 SG_MOON_COMFORT +433 SG_STAR_COMFORT +434 SG_HATE +435 SG_SUN_ANGER +436 SG_MOON_ANGER +437 SG_STAR_ANGER +438 SG_SUN_BLESS +439 SG_MOON_BLESS +440 SG_STAR_BLESS +441 SG_DEVIL +442 SG_FRIEND +443 SG_KNOWLEDGE +444 SG_FUSION +445 SL_ALCHEMIST +446 AM_BERSERKPITCHER +447 SL_MONK +448 SL_STAR +449 SL_SAGE +450 SL_CRUSADER +451 SL_SUPERNOVICE +452 SL_KNIGHT +453 SL_WIZARD +454 SL_PRIEST +455 SL_BARDDANCER +456 SL_ROGUE +457 SL_ASSASIN +458 SL_BLACKSMITH +459 BS_ADRENALINE2 +460 SL_HUNTER +461 SL_SOULLINKER +462 SL_KAIZEL +463 SL_KAAHI +464 SL_KAUPE +465 SL_KAITE +466 SL_KAINA +467 SL_STIN +468 SL_STUN +469 SL_SMA +470 SL_SWOO +471 SL_SKE +472 SL_SKA +473 SM_SELFPROVOKE +474 NPC_EMOTION_ON +475 ST_PRESERVE +476 ST_FULLSTRIP +477 WS_WEAPONREFINE +478 CR_SLIMPITCHER +479 CR_FULLPROTECTION +480 PA_SHIELDCHAIN +481 HP_MANARECHARGE +482 PF_DOUBLECASTING +483 HW_GANBANTEIN +484 HW_GRAVITATION +485 WS_CARTTERMINATION +486 WS_OVERTHRUSTMAX +487 CG_LONGINGFREEDOM +488 CG_HERMODE +489 CG_TAROTCARD +490 CR_ACIDDEMONSTRATION +491 CR_CULTIVATION +492 ITEM_ENCHANTARMS +493 TK_MISSION +494 SL_HIGH +495 KN_ONEHAND +496 AM_TWILIGHT1 +497 AM_TWILIGHT2 +498 AM_TWILIGHT3 +499 HT_POWER +500 GS_GLITTERING +501 GS_FLING +502 GS_TRIPLEACTION +503 GS_BULLSEYE +504 GS_MADNESSCANCEL +505 GS_ADJUSTMENT +506 GS_INCREASING +507 GS_MAGICALBULLET +508 GS_CRACKER +509 GS_SINGLEACTION +510 GS_SNAKEEYE +511 GS_CHAINACTION +512 GS_TRACKING +513 GS_DISARM +514 GS_PIERCINGSHOT +515 GS_RAPIDSHOWER +516 GS_DESPERADO +517 GS_GATLINGFEVER +518 GS_DUST +519 GS_FULLBUSTER +520 GS_SPREADATTACK +521 GS_GROUNDDRIFT +522 NJ_TOBIDOUGU +523 NJ_SYURIKEN +524 NJ_KUNAI +525 NJ_HUUMA +526 NJ_ZENYNAGE +527 NJ_TATAMIGAESHI +528 NJ_KASUMIKIRI +529 NJ_SHADOWJUMP +530 NJ_KIRIKAGE +531 NJ_UTSUSEMI +532 NJ_BUNSINJYUTSU +533 NJ_NINPOU +534 NJ_KOUENKA +535 NJ_KAENSIN +536 NJ_BAKUENRYU +537 NJ_HYOUSENSOU +538 NJ_SUITON +539 NJ_HYOUSYOURAKU +540 NJ_HUUJIN +541 NJ_RAIGEKISAI +542 NJ_KAMAITACHI +543 NJ_NEN +544 NJ_ISSEN +545 MB_FIGHTING +546 MB_NEUTRAL +547 MB_TAIMING_PUTI +548 MB_WHITEPOTION +549 MB_MENTAL +550 MB_CARDPITCHER +551 MB_PETPITCHER +552 MB_BODYSTUDY +553 MB_BODYALTER +554 MB_PETMEMORY +555 MB_M_TELEPORT +556 MB_B_GAIN +557 MB_M_GAIN +558 MB_MISSION +559 MB_MUNAKKNOWLEDGE +560 MB_MUNAKBALL +561 MB_SCROLL +562 MB_B_GATHERING +563 MB_M_GATHERING +564 MB_B_EXCLUDE +565 MB_B_DRIFT +566 MB_B_WALLRUSH +567 MB_M_WALLRUSH +568 MB_B_WALLSHIFT +569 MB_M_WALLCRASH +570 MB_M_REINCARNATION +571 MB_B_EQUIP +572 SL_DEATHKNIGHT +573 SL_COLLECTOR +574 SL_NINJA +575 SL_GUNNER +576 AM_TWILIGHT4 +577 DA_RESET +578 DE_BERSERKAIZER +579 DA_DARKPOWER +580 DE_PASSIVE +581 DE_PATTACK +582 DE_PSPEED +583 DE_PDEFENSE +584 DE_PCRITICAL +585 DE_PHP +586 DE_PSP +587 DE_RESET +588 DE_RANKING +589 DE_PTRIPLE +590 DE_ENERGY +591 DE_NIGHTMARE +592 DE_SLASH +593 DE_COIL +594 DE_WAVE +595 DE_REBIRTH +596 DE_AURA +597 DE_FREEZER +598 DE_CHANGEATTACK +599 DE_PUNISH +600 DE_POISON +601 DE_INSTANT +602 DE_WARNING +603 DE_RANKEDKNIFE +604 DE_RANKEDGRADIUS +605 DE_GAUGE +606 DE_GTIME +607 DE_GPAIN +608 DE_GSKILL +609 DE_GKILL +610 DE_ACCEL +611 DE_BLOCKDOUBLE +612 DE_BLOCKMELEE +613 DE_BLOCKFAR +614 DE_FRONTATTACK +615 DE_DANGERATTACK +616 DE_TWINATTACK +617 DE_WINDATTACK +618 DE_WATERATTACK +619 DA_ENERGY +620 DA_CLOUD +621 DA_FIRSTSLOT +622 DA_HEADDEF +623 DA_SPACE +624 DA_TRANSFORM +625 DA_EXPLOSION +626 DA_REWARD +627 DA_CRUSH +628 DA_ITEMREBUILD +629 DA_ILLUSION +630 DA_NUETRALIZE +631 DA_RUNNER +632 DA_TRANSFER +633 DA_WALL +634 DA_ZENY +635 DA_REVENGE +636 DA_EARPLUG +637 DA_CONTRACT +638 DA_BLACK +639 DA_DREAM +640 DA_MAGICCART +641 DA_COPY +642 DA_CRYSTAL +643 DA_EXP +644 DA_CARTSWING +645 DA_REBUILD +646 DA_JOBCHANGE +647 DA_EDARKNESS +648 DA_EGUARDIAN +649 DA_TIMEOUT +650 ALL_TIMEIN +651 DA_ZENYRANK +652 DA_ACCESSORYMIX +653 NPC_EARTHQUAKE +654 NPC_FIREBREATH +655 NPC_ICEBREATH +656 NPC_THUNDERBREATH +657 NPC_ACIDBREATH +658 NPC_DARKNESSBREATH +659 NPC_DRAGONFEAR +660 NPC_BLEEDING +661 NPC_PULSESTRIKE +662 NPC_HELLJUDGEMENT +663 NPC_WIDESILENCE +664 NPC_WIDEFREEZE +665 NPC_WIDEBLEEDING +666 NPC_WIDESTONE +667 NPC_WIDECONFUSE +668 NPC_WIDESLEEP +669 NPC_WIDESIGHT +670 NPC_EVILLAND +671 NPC_MAGICMIRROR +672 NPC_SLOWCAST +673 NPC_CRITICALWOUND +674 NPC_EXPULSION +675 NPC_STONESKIN +676 NPC_ANTIMAGIC +677 NPC_WIDECURSE +678 NPC_WIDESTUN +679 NPC_VAMPIRE_GIFT +680 NPC_WIDESOULDRAIN +681 ALL_INCCARRY +682 NPC_TALK +683 NPC_HELLPOWER +684 NPC_WIDEHELLDIGNITY +685 NPC_INVINCIBLE +686 NPC_INVINCIBLEOFF +687 NPC_ALLHEAL +688 GM_SANDMAN +689 CASH_BLESSING +690 CASH_INCAGI +691 CASH_ASSUMPTIO +692 ALL_CATCRY +693 ALL_PARTYFLEE +694 ALL_ANGEL_PROTECT +695 ALL_DREAM_SUMMERNIGHT +696 NPC_CHANGEUNDEAD2 +697 ALL_REVERSEORCISH +698 ALL_WEWISH +699 ALL_SONKRAN +700 NPC_WIDEHEALTHFEAR +701 NPC_WIDEBODYBURNNING +702 NPC_WIDEFROSTMISTY +703 NPC_WIDECOLD +704 NPC_WIDE_DEEP_SLEEP +705 NPC_WIDESIREN +706 NPC_VENOMFOG +707 NPC_MILLENNIUMSHIELD +708 NPC_COMET +709 NPC_ICEMINE +710 NPC_ICEEXPLO +711 NPC_FLAMECROSS +712 NPC_PULSESTRIKE2 +713 NPC_DANCINGBLADE +714 NPC_DANCINGBLADE_ATK +715 NPC_DARKPIERCING +716 NPC_MAXPAIN +717 NPC_MAXPAIN_ATK +718 NPC_DEATHSUMMON +719 NPC_HELLBURNING +720 NPC_JACKFROST +721 NPC_WIDEWEB +722 NPC_WIDESUCK +723 NPC_STORMGUST2 +724 NPC_FIRESTORM +725 NPC_REVERBERATION +726 NPC_REVERBERATION_ATK +727 NPC_LEX_AETERNA +728 NPC_ARROWSTORM +729 NPC_CHEAL +730 NPC_SR_CURSEDCIRCLE +731 NPC_DRAGONBREATH +732 NPC_FATALMENACE +733 NPC_MAGMA_ERUPTION +734 NPC_MAGMA_ERUPTION_DOTDAMAGE +735 NPC_MANDRAGORA +736 NPC_PSYCHIC_WAVE +737 NPC_RAYOFGENESIS +738 NPC_VENOMIMPRESS +739 NPC_CLOUD_KILL +740 NPC_IGNITIONBREAK +741 NPC_PHANTOMTHRUST +742 NPC_POISON_BUSTER +743 NPC_HALLUCINATIONWALK +744 NPC_ELECTRICWALK +745 NPC_FIREWALK +746 NPC_WIDEDISPEL +747 NPC_LEASH +748 NPC_WIDELEASH +749 NPC_WIDECRITICALWOUND +750 NPC_EARTHQUAKE_K +751 NPC_ALL_STAT_DOWN +752 NPC_GRADUAL_GRAVITY +753 NPC_DAMAGE_HEAL +754 NPC_IMMUNE_PROPERTY +755 NPC_MOVE_COORDINATE +756 NPC_WIDEBLEEDING2 +757 NPC_WIDESILENCE2 +758 NPC_WIDESTUN2 +759 NPC_WIDESTONE2 +760 NPC_WIDESLEEP2 +761 NPC_WIDECURSE2 +762 NPC_WIDECONFUSE2 +763 NPC_WIDEFREEZE2 +764 NPC_BLEEDING2 +765 NPC_ICEBREATH2 +766 NPC_ACIDBREATH2 +767 NPC_EVILLAND2 +768 NPC_HELLJUDGEMENT2 +769 NPC_RAINOFMETEOR +770 NPC_GROUNDDRIVE +771 NPC_RELIEVE_ON +772 NPC_RELIEVE_OFF +773 NPC_LOCKON_LASER +774 NPC_LOCKON_LASER_ATK +775 NPC_SEEDTRAP +776 NPC_DEADLYCURSE +777 NPC_RANDOMBREAK +778 NPC_STRIP_SHADOW +779 NPC_DEADLYCURSE2 +780 NPC_CANE_OF_EVIL_EYE +781 NPC_CURSE_OF_RED_CUBE +782 NPC_CURSE_OF_BLUE_CUBE +783 NPC_KILLING_AURA +784 NPC_LAST +# TODO: 785-1000 +1001 KN_CHARGEATK +1002 CR_SHRINK +1003 AS_SONICACCEL +1004 AS_VENOMKNIFE +1005 RG_CLOSECONFINE +1006 WZ_SIGHTBLASTER +1007 SA_CREATECON +1008 SA_ELEMENTWATER +1009 HT_PHANTASMIC +1010 BA_PANGVOICE +1011 DC_WINKCHARM +1012 BS_UNFAIRLYTRICK +1013 BS_GREED +1014 PR_REDEMPTIO +1015 MO_KITRANSLATION +1016 MO_BALKYOUNG +1017 SA_ELEMENTGROUND +1018 SA_ELEMENTFIRE +1019 SA_ELEMENTWIND +# TODO: 1020-1999 +2000 THIRDJOB_BEGIN +2001 RK_ENCHANTBLADE +2002 RK_SONICWAVE +2003 RK_DEATHBOUND +2004 RK_HUNDREDSPEAR +2005 RK_WINDCUTTER +2006 RK_IGNITIONBREAK +2007 RK_DRAGONTRAINING +2008 RK_DRAGONBREATH +2009 RK_DRAGONHOWLING +2010 RK_RUNEMASTERY +2011 RK_MILLENNIUMSHIELD +2012 RK_CRUSHSTRIKE +2013 RK_REFRESH +2014 RK_GIANTGROWTH +2015 RK_STONEHARDSKIN +2016 RK_VITALITYACTIVATION +2017 RK_STORMBLAST +2018 RK_FIGHTINGSPIRIT +2019 RK_ABUNDANCE +2020 RK_PHANTOMTHRUST +2021 GC_VENOMIMPRESS +2022 GC_CROSSIMPACT +2023 GC_DARKILLUSION +2024 GC_RESEARCHNEWPOISON +2025 GC_CREATENEWPOISON +2026 GC_ANTIDOTE +2027 GC_POISONINGWEAPON +2028 GC_WEAPONBLOCKING +2029 GC_COUNTERSLASH +2030 GC_WEAPONCRUSH +2031 GC_VENOMPRESSURE +2032 GC_POISONSMOKE +2033 GC_CLOAKINGEXCEED +2034 GC_PHANTOMMENACE +2035 GC_HALLUCINATIONWALK +2036 GC_ROLLINGCUTTER +2037 GC_CROSSRIPPERSLASHER +2038 AB_JUDEX +2039 AB_ANCILLA +2040 AB_ADORAMUS +2041 AB_CLEMENTIA +2042 AB_CANTO +2043 AB_CHEAL +2044 AB_EPICLESIS +2045 AB_PRAEFATIO +2046 AB_ORATIO +2047 AB_LAUDAAGNUS +2048 AB_LAUDARAMUS +2049 AB_EUCHARISTICA +2050 AB_RENOVATIO +2051 AB_HIGHNESSHEAL +2052 AB_CLEARANCE +2053 AB_EXPIATIO +2054 AB_DUPLELIGHT +2055 AB_DUPLELIGHT_MELEE +2056 AB_DUPLELIGHT_MAGIC +2057 AB_SILENTIUM +# TODO: 2058-2199 +2200 WL_STARTMARK +2201 WL_WHITEIMPRISON +2202 WL_SOULEXPANSION +2203 WL_FROSTMISTY +2204 WL_JACKFROST +2205 WL_MARSHOFABYSS +2206 WL_RECOGNIZEDSPELL +2207 WL_SIENNAEXECRATE +2208 WL_RADIUS +2209 WL_STASIS +2210 WL_DRAINLIFE +2211 WL_CRIMSONROCK +2212 WL_HELLINFERNO +2213 WL_COMET +2214 WL_CHAINLIGHTNING +2215 WL_CHAINLIGHTNING_ATK +2216 WL_EARTHSTRAIN +2217 WL_TETRAVORTEX +2218 WL_TETRAVORTEX_FIRE +2219 WL_TETRAVORTEX_WATER +2220 WL_TETRAVORTEX_WIND +2221 WL_TETRAVORTEX_GROUND +2222 WL_SUMMONFB +2223 WL_SUMMONBL +2224 WL_SUMMONWB +2225 WL_SUMMON_ATK_FIRE +2226 WL_SUMMON_ATK_WIND +2227 WL_SUMMON_ATK_WATER +2228 WL_SUMMON_ATK_GROUND +2229 WL_SUMMONSTONE +2230 WL_RELEASE +2231 WL_READING_SB +2232 WL_ENDMARK +2233 RA_ARROWSTORM +2234 RA_FEARBREEZE +2235 RA_RANGERMAIN +2236 RA_AIMEDBOLT +2237 RA_DETONATOR +2238 RA_ELECTRICSHOCKER +2239 RA_CLUSTERBOMB +2240 RA_WUGMASTERY +2241 RA_WUGRIDER +2242 RA_WUGDASH +2243 RA_WUGSTRIKE +2244 RA_WUGBITE +2245 RA_TOOTHOFWUG +2246 RA_SENSITIVEKEEN +2247 RA_CAMOUFLAGE +2248 RA_RESEARCHTRAP +2249 RA_MAGENTATRAP +2250 RA_COBALTTRAP +2251 RA_MAIZETRAP +2252 RA_VERDURETRAP +2253 RA_FIRINGTRAP +2254 RA_ICEBOUNDTRAP +2255 NC_MADOLICENCE +2256 NC_BOOSTKNUCKLE +2257 NC_PILEBUNKER +2258 NC_VULCANARM +2259 NC_FLAMELAUNCHER +2260 NC_COLDSLOWER +2261 NC_ARMSCANNON +2262 NC_ACCELERATION +2263 NC_HOVERING +2264 NC_F_SIDESLIDE +2265 NC_B_SIDESLIDE +2266 NC_MAINFRAME +2267 NC_SELFDESTRUCTION +2268 NC_SHAPESHIFT +2269 NC_EMERGENCYCOOL +2270 NC_INFRAREDSCAN +2271 NC_ANALYZE +2272 NC_MAGNETICFIELD +2273 NC_NEUTRALBARRIER +2274 NC_STEALTHFIELD +2275 NC_REPAIR +2276 NC_TRAININGAXE +2277 NC_RESEARCHFE +2278 NC_AXEBOOMERANG +2279 NC_POWERSWING +2280 NC_AXETORNADO +2281 NC_SILVERSNIPER +2282 NC_MAGICDECOY +2283 NC_DISJOINT +2284 SC_FATALMENACE +2285 SC_REPRODUCE +2286 SC_AUTOSHADOWSPELL +2287 SC_SHADOWFORM +2288 SC_TRIANGLESHOT +2289 SC_BODYPAINT +2290 SC_INVISIBILITY +2291 SC_DEADLYINFECT +2292 SC_ENERVATION +2293 SC_GROOMY +2294 SC_IGNORANCE +2295 SC_LAZINESS +2296 SC_UNLUCKY +2297 SC_WEAKNESS +2298 SC_STRIPACCESSARY +2299 SC_MANHOLE +2300 SC_DIMENSIONDOOR +2301 SC_CHAOSPANIC +2302 SC_MAELSTROM +2303 SC_BLOODYLUST +2304 SC_FEINTBOMB +# TODO: 2305 +2306 SC_ENDMARK +2307 LG_CANNONSPEAR +2308 LG_BANISHINGPOINT +2309 LG_TRAMPLE +2310 LG_SHIELDPRESS +2311 LG_REFLECTDAMAGE +2312 LG_PINPOINTATTACK +2313 LG_FORCEOFVANGUARD +2314 LG_RAGEBURST +2315 LG_SHIELDSPELL +2316 LG_EXEEDBREAK +2317 LG_OVERBRAND +2318 LG_PRESTIGE +2319 LG_BANDING +2320 LG_MOONSLASHER +2321 LG_RAYOFGENESIS +2322 LG_PIETY +2323 LG_EARTHDRIVE +2324 LG_HESPERUSLIT +2325 LG_INSPIRATION +2326 SR_DRAGONCOMBO +2327 SR_SKYNETBLOW +2328 SR_EARTHSHAKER +2329 SR_FALLENEMPIRE +2330 SR_TIGERCANNON +2331 SR_HELLGATE +2332 SR_RAMPAGEBLASTER +2333 SR_CRESCENTELBOW +2334 SR_CURSEDCIRCLE +2335 SR_LIGHTNINGWALK +2336 SR_KNUCKLEARROW +2337 SR_WINDMILL +2338 SR_RAISINGDRAGON +2339 SR_GENTLETOUCH +2340 SR_ASSIMILATEPOWER +2341 SR_POWERVELOCITY +2342 SR_CRESCENTELBOW_AUTOSPELL +2343 SR_GATEOFHELL +2344 SR_GENTLETOUCH_QUIET +2345 SR_GENTLETOUCH_CURE +2346 SR_GENTLETOUCH_ENERGYGAIN +2347 SR_GENTLETOUCH_CHANGE +2348 SR_GENTLETOUCH_REVITALIZE +2349 WA_STARTMARK +2350 WA_SWING_DANCE +2351 WA_SYMPHONY_OF_LOVER +2352 WA_MOONLIT_SERENADE +# TODO: 2353-2378 +2379 WA_ENDMARK +2380 MI_STARTMARK +2381 MI_RUSH_WINDMILL +2382 MI_ECHOSONG +2383 MI_HARMONIZE +# TODO: 2384-2409 +2410 MI_ENDMARK +2411 WM_STARTMARK +2412 WM_LESSON +2413 WM_METALICSOUND +2414 WM_REVERBERATION +2415 WM_REVERBERATION_MELEE +2416 WM_REVERBERATION_MAGIC +2417 WM_DOMINION_IMPULSE +2418 WM_SEVERE_RAINSTORM +2419 WM_POEMOFNETHERWORLD +2420 WM_VOICEOFSIREN +2421 WM_DEADHILLHERE +2422 WM_LULLABY_DEEPSLEEP +2423 WM_SIRCLEOFNATURE +2424 WM_RANDOMIZESPELL +2425 WM_GLOOMYDAY +2426 WM_GREAT_ECHO +2427 WM_SONG_OF_MANA +2428 WM_DANCE_WITH_WUG +2429 WM_SOUND_OF_DESTRUCTION +2430 WM_SATURDAY_NIGHT_FEVER +2431 WM_LERADS_DEW +2432 WM_MELODYOFSINK +2433 WM_BEYOND_OF_WARCRY +2434 WM_UNLIMITED_HUMMING_VOICE +# TODO: 2435-2440 +2441 WM_ENDMARK +2442 SO_STARTMARK +2443 SO_FIREWALK +2444 SO_ELECTRICWALK +2445 SO_SPELLFIST +2446 SO_EARTHGRAVE +2447 SO_DIAMONDDUST +2448 SO_POISON_BUSTER +2449 SO_PSYCHIC_WAVE +2450 SO_CLOUD_KILL +2451 SO_STRIKING +2452 SO_WARMER +2453 SO_VACUUM_EXTREME +2454 SO_VARETYR_SPEAR +2455 SO_ARRULLO +2456 SO_EL_CONTROL +2457 SO_SUMMON_AGNI +2458 SO_SUMMON_AQUA +2459 SO_SUMMON_VENTUS +2460 SO_SUMMON_TERA +2461 SO_EL_ACTION +2462 SO_EL_ANALYSIS +2463 SO_EL_SYMPATHY +2464 SO_EL_CURE +2465 SO_FIRE_INSIGNIA +2466 SO_WATER_INSIGNIA +2467 SO_WIND_INSIGNIA +2468 SO_EARTH_INSIGNIA +# TODO: 2469-2471 +2472 SO_ENDMARK +2473 GN_START_MARK +2474 GN_TRAINING_SWORD +2475 GN_REMODELING_CART +2476 GN_CART_TORNADO +2477 GN_CARTCANNON +2478 GN_CARTBOOST +2479 GN_THORNS_TRAP +2480 GN_BLOOD_SUCKER +2481 GN_SPORE_EXPLOSION +2482 GN_WALLOFTHORN +2483 GN_CRAZYWEED +2484 GN_CRAZYWEED_ATK +2485 GN_DEMONIC_FIRE +2486 GN_FIRE_EXPANSION +2487 GN_FIRE_EXPANSION_SMOKE_POWDER +2488 GN_FIRE_EXPANSION_TEAR_GAS +2489 GN_FIRE_EXPANSION_ACID +2490 GN_HELLS_PLANT +2491 GN_HELLS_PLANT_ATK +2492 GN_MANDRAGORA +2493 GN_SLINGITEM +2494 GN_CHANGEMATERIAL +2495 GN_MIX_COOKING +2496 GN_MAKEBOMB +2497 GN_S_PHARMACY +2498 GN_SLINGITEM_RANGEMELEEATK +# TODO: 2499-2512 +2513 GN_ENDMARK +2514 ETC_THIRDJOB_SKILL_START +2515 AB_SECRAMENT +2516 WM_SEVERE_RAINSTORM_MELEE +2517 SR_HOWLINGOFLION +2518 SR_RIDEINLIGHTNING +2519 LG_OVERBRAND_BRANDISH +2520 LG_OVERBRAND_PLUSATK +# TODO: 2521-2430 +2531 ETC_THIRDJOB_SKILL_END +2532 THIRDJOB_END +2533 ALL_ODINS_RECALL +2534 RETURN_TO_ELDICASTES +2535 ALL_BUYING_STORE +2536 ALL_GUARDIAN_RECALL +2537 ALL_ODINS_POWER +2538 XX_BEER_BOTTLE_CAP +2539 NPC_ASSASSINCROSS +2540 NPC_DISSONANCE +2541 NPC_UGLYDANCE +2542 ALL_TETANY +2543 ALL_RAY_OF_PROTECTION +2544 MC_CARTDECORATE +2545 GM_ITEM_ATKMAX +2546 GM_ITEM_ATKMIN +2547 GM_ITEM_MATKMAX +2548 GM_ITEM_MATKMIN +2549 GM_AP_HEAL +2550 UPPER_EXTENDED_JOB_START +2551 RL_GLITTERING_GREED +2552 RL_RICHS_COIN +2553 RL_MASS_SPIRAL +2554 RL_BANISHING_BUSTER +2555 RL_B_TRAP +2556 RL_FLICKER +2557 RL_S_STORM +2558 RL_E_CHAIN +2559 RL_QD_SHOT +2560 RL_C_MARKER +2561 RL_FIREDANCE +2562 RL_H_MINE +2563 RL_P_ALTER +2564 RL_FALLEN_ANGEL +2565 RL_R_TRIP +2566 RL_D_TAIL +2567 RL_FIRE_RAIN +2568 RL_HEAT_BARREL +2569 RL_AM_BLAST +2570 RL_SLUGSHOT +2571 RL_HAMMER_OF_GOD +2572 RL_R_TRIP_PLUSATK +2573 RL_B_FLICKER_ATK +2574 SJ_LIGHTOFMOON +2575 SJ_LUNARSTANCE +2576 SJ_FULLMOONKICK +2577 SJ_LIGHTOFSTAR +2578 SJ_STARSTANCE +2579 SJ_NEWMOONKICK +2580 SJ_FLASHKICK +2581 SJ_STAREMPEROR +2582 SJ_NOVAEXPLOSING +2583 SJ_UNIVERSESTANCE +2584 SJ_FALLINGSTAR +2585 SJ_GRAVITYCONTROL +2586 SJ_BOOKOFDIMENSION +2587 SJ_BOOKOFCREATINGSTAR +2588 SJ_DOCUMENT +2589 SJ_PURIFY +2590 SJ_LIGHTOFSUN +2591 SJ_SUNSTANCE +2592 SJ_SOLARBURST +2593 SJ_PROMINENCEKICK +2594 SJ_FALLINGSTAR_ATK +2595 SJ_FALLINGSTAR_ATK2 +2596 SP_SOULGOLEM +2597 SP_SOULSHADOW +2598 SP_SOULFALCON +2599 SP_SOULFAIRY +2600 SP_CURSEEXPLOSION +2601 SP_SOULCURSE +2602 SP_SPA +2603 SP_SHA +2604 SP_SWHOO +2605 SP_SOULUNITY +2606 SP_SOULDIVISION +2607 SP_SOULREAPER +2608 SP_SOULREVOLVE +2609 SP_SOULCOLLECT +2610 SP_SOULEXPLOSION +2611 SP_SOULENERGY +2612 SP_KAUTE +# TODO: 2613-3000 +3001 KO_YAMIKUMO +3002 KO_RIGHT +3003 KO_LEFT +3004 KO_JYUMONJIKIRI +3005 KO_SETSUDAN +3006 KO_BAKURETSU +3007 KO_HAPPOKUNAI +3008 KO_MUCHANAGE +3009 KO_HUUMARANKA +3010 KO_MAKIBISHI +3011 KO_MEIKYOUSISUI +3012 KO_ZANZOU +3013 KO_KYOUGAKU +3014 KO_JYUSATSU +3015 KO_KAHU_ENTEN +3016 KO_HYOUHU_HUBUKI +3017 KO_KAZEHU_SEIRAN +3018 KO_DOHU_KOUKAI +3019 KO_KAIHOU +3020 KO_ZENKAI +3021 KO_GENWAKU +3022 KO_IZAYOI +3023 KG_KAGEHUMI +3024 KG_KYOMU +3025 KG_KAGEMUSYA +3026 OB_ZANGETSU +3027 OB_OBOROGENSOU +3028 OB_OBOROGENSOU_TRANSITION_ATK +3029 OB_AKAITSUKI +3030 UPPER_EXTENDED_JOB_END +3031 ECL_SNOWFLIP +3032 ECL_PEONYMAMY +3033 ECL_SADAGUI +3034 ECL_SEQUOIADUST +3035 ECLAGE_RECALL +3036 BA_POEMBRAGI2 +3037 DC_FORTUNEKISS2 +3038 ITEM_OPTION_SPLASH_ATTACK +3039 GM_FORCE_TRANSFER +3040 GM_WIDE_RESURRECTION +3041 ALL_NIFLHEIM_RECALL +3042 ALL_PRONTERA_RECALL +3043 ALL_GLASTHEIM_RECALL +3044 ALL_THANATOS_RECALL +# TODO: 3045-4999 +5000 LEVEL_EXPANSION_START +5001 GC_DARKCROW +5002 RA_UNLIMIT +5003 GN_ILLUSIONDOPING +5004 RK_DRAGONBREATH_WATER +5005 RK_LUXANIMA +5006 NC_MAGMA_ERUPTION +5007 WM_FRIGG_SONG +5008 SO_ELEMENTAL_SHIELD +5009 SR_FLASHCOMBO +5010 SC_ESCAPE +5011 AB_OFFERTORIUM +5012 WL_TELEKINESIS_INTENSE +5013 LG_KINGS_GRACE +5014 ALL_FULL_THROTTLE +5015 NC_MAGMA_ERUPTION_DOTDAMAGE +5016 LEVEL_EXPANSION_END +5017 DORAM_TRIBE_START +5018 SU_BASIC_SKILL +5019 SU_BITE +5020 SU_HIDE +5021 SU_SCRATCH +5022 SU_STOOP +5023 SU_LOPE +5024 SU_SPRITEMABLE +5025 SU_POWEROFLAND +5026 SU_SV_STEMSPEAR +5027 SU_CN_POWDERING +5028 SU_CN_METEOR +5029 SU_SV_ROOTTWIST +5030 SU_SV_ROOTTWIST_ATK +5031 SU_POWEROFLIFE +5032 SU_SCAROFTAROU +5033 SU_PICKYPECK +5034 SU_PICKYPECK_DOUBLE_ATK +5035 SU_ARCLOUSEDASH +5036 SU_LUNATICCARROTBEAT +5037 SU_POWEROFSEA +5038 SU_TUNABELLY +5039 SU_TUNAPARTY +5040 SU_BUNCHOFSHRIMP +5041 SU_FRESHSHRIMP +5042 SU_CN_METEOR2 +5043 SU_LUNATICCARROTBEAT2 +5044 SU_SOULATTACK +5045 SU_POWEROFFLOCK +5046 SU_SVG_SPIRIT +5047 SU_HISS +5048 SU_NYANGGRASS +5049 SU_GROOMING +5050 SU_PURRING +5051 SU_SHRIMPARTY +5052 SU_SPIRITOFLIFE +5053 SU_MEOWMEOW +5054 SU_SPIRITOFLAND +5055 SU_CHATTERING +5056 SU_SPIRITOFSEA +5057 DORAM_TRIBE_END +5058 LAST +# TODO: 5059-5062 +5063 WE_CALLALLFAMILY +5064 WE_ONEFOREVER +5065 WE_CHEERUP +# TODO: 5066-5067 +5068 CG_SPECIALSINGER +# TODO: 5069-5071 +5072 AB_VITUPERATUM +5073 AB_CONVENIO +# TODO: 5074 +5075 NV_BREAKTHROUGH +5076 NV_HELPANGEL +5077 NV_TRANSCENDENCE +5078 WL_READING_SB_READING +# TODO: 5079-5200 +5201 DK_SERVANTWEAPON +5202 DK_SERVANTWEAPON_ATK +5203 DK_SERVANT_W_SIGN +5204 DK_SERVANT_W_PHANTOM +5205 DK_SERVANT_W_DEMOL +5206 DK_CHARGINGPIERCE +5207 DK_TWOHANDDEF +5208 DK_HACKANDSLASHER +5209 DK_HACKANDSLASHER_ATK +5210 DK_DRAGONIC_AURA +5211 DK_MADNESS_CRUSHER +5212 DK_VIGOR +5213 DK_STORMSLASH +5214 AG_DEADLY_PROJECTION +5215 AG_DESTRUCTIVE_HURRICANE +5216 AG_RAIN_OF_CRYSTAL +5217 AG_MYSTERY_ILLUSION +5218 AG_VIOLENT_QUAKE +5219 AG_VIOLENT_QUAKE_ATK +5220 AG_SOUL_VC_STRIKE +5221 AG_STRANTUM_TREMOR +5222 AG_ALL_BLOOM +5223 AG_ALL_BLOOM_ATK +5224 AG_ALL_BLOOM_ATK2 +5225 AG_CRYSTAL_IMPACT +5226 AG_CRYSTAL_IMPACT_ATK +5227 AG_TORNADO_STORM +5228 AG_TWOHANDSTAFF +5229 AG_FLORAL_FLARE_ROAD +5230 AG_ASTRAL_STRIKE +5231 AG_ASTRAL_STRIKE_ATK +5232 AG_CLIMAX +5233 AG_ROCK_DOWN +5234 AG_STORM_CANNON +5235 AG_CRIMSON_ARROW +5236 AG_CRIMSON_ARROW_ATK +5237 AG_FROZEN_SLASH +5238 IQ_POWERFUL_FAITH +5239 IQ_FIRM_FAITH +5240 IQ_WILL_OF_FAITH +5241 IQ_OLEUM_SANCTUM +5242 IQ_SINCERE_FAITH +5243 IQ_MASSIVE_F_BLASTER +5244 IQ_EXPOSION_BLASTER +5245 IQ_FIRST_BRAND +5246 IQ_FIRST_FAITH_POWER +5247 IQ_JUDGE +5248 IQ_SECOND_FLAME +5249 IQ_SECOND_FAITH +5250 IQ_SECOND_JUDGEMENT +5251 IQ_THIRD_PUNISH +5252 IQ_THIRD_FLAME_BOMB +5253 IQ_THIRD_CONSECRATION +5254 IQ_THIRD_EXOR_FLAME +5255 IG_GUARD_STANCE +5256 IG_GUARDIAN_SHIELD +5257 IG_REBOUND_SHIELD +5258 IG_SHIELD_MASTERY +5259 IG_SPEAR_SWORD_M +5260 IG_ATTACK_STANCE +5261 IG_ULTIMATE_SACRIFICE +5262 IG_HOLY_SHIELD +5263 IG_GRAND_JUDGEMENT +5264 IG_JUDGEMENT_CROSS +5265 IG_SHIELD_SHOOTING +5266 IG_OVERSLASH +5267 IG_CROSS_RAIN +5268 CD_REPARATIO +5269 CD_MEDIALE_VOTUM +5270 CD_MACE_BOOK_M +5271 CD_ARGUTUS_VITA +5272 CD_ARGUTUS_TELUM +5273 CD_ARBITRIUM +5274 CD_ARBITRIUM_ATK +5275 CD_PRESENS_ACIES +5276 CD_FIDUS_ANIMUS +5277 CD_EFFLIGO +5278 CD_COMPETENTIA +5279 CD_PNEUMATICUS_PROCELLA +5280 CD_DILECTIO_HEAL +5281 CD_RELIGIO +5282 CD_BENEDICTUM +5283 CD_PETITIO +5284 CD_FRAMEN +5285 SHC_SHADOW_EXCEED +5286 SHC_DANCING_KNIFE +5287 SHC_SAVAGE_IMPACT +5288 SHC_SHADOW_SENSE +5289 SHC_ETERNAL_SLASH +5290 SHC_POTENT_VENOM +5291 SHC_SHADOW_STAB +5292 SHC_IMPACT_CRATER +5293 SHC_ENCHANTING_SHADOW +5294 SHC_FATAL_SHADOW_CROW +5295 MT_AXE_STOMP +5296 MT_RUSH_QUAKE +5297 MT_M_MACHINE +5298 MT_A_MACHINE +5299 MT_D_MACHINE +5300 MT_TWOAXEDEF +5301 MT_ABR_M +5302 MT_SUMMON_ABR_BATTLE_WARIOR +5303 MT_SUMMON_ABR_DUAL_CANNON +5304 MT_SUMMON_ABR_MOTHER_NET +5305 MT_SUMMON_ABR_INFINITY +5311 ABC_DAGGER_AND_BOW_M +5312 ABC_MAGIC_SWORD_M +5313 ABC_STRIP_SHADOW +5314 ABC_ABYSS_DAGGER +5315 ABC_UNLUCKY_RUSH +5316 ABC_CHAIN_REACTION_SHOT +5317 ABC_FROM_THE_ABYSS +5318 ABC_ABYSS_SLAYER +5319 ABC_ABYSS_STRIKE +5320 ABC_DEFT_STAB +5321 ABC_ABYSS_SQUARE +5322 ABC_FRENZY_SHOT +5323 WH_ADVANCED_TRAP +5324 WH_WIND_SIGN +5325 WH_NATUREFRIENDLY +5326 WH_HAWKRUSH +5327 WH_HAWK_M +5328 WH_CALAMITYGALE +5329 WH_HAWKBOOMERANG +5330 WH_GALESTORM +5331 WH_DEEPBLINDTRAP +5332 WH_SOLIDTRAP +5333 WH_SWIFTTRAP +5334 WH_CRESCIVE_BOLT +5335 WH_FLAMETRAP +5336 BO_BIONIC_PHARMACY +5337 BO_BIONICS_M +5338 BO_THE_WHOLE_PROTECTION +5339 BO_ADVANCE_PROTECTION +5340 BO_ACIDIFIED_ZONE_WATER +5341 BO_ACIDIFIED_ZONE_GROUND +5342 BO_ACIDIFIED_ZONE_WIND +5343 BO_ACIDIFIED_ZONE_FIRE +5344 BO_WOODENWARRIOR +5345 BO_WOODEN_FAIRY +5346 BO_CREEPER +5347 BO_RESEARCHREPORT +5348 BO_HELLTREE +5349 TR_STAGE_MANNER +5350 TR_RETROSPECTION +5351 TR_MYSTIC_SYMPHONY +5352 TR_KVASIR_SONATA +5353 TR_ROSEBLOSSOM +5354 TR_ROSEBLOSSOM_ATK +5355 TR_RHYTHMSHOOTING +5356 TR_METALIC_FURY +5357 TR_SOUNDBLEND +5358 TR_GEF_NOCTURN +5359 TR_ROKI_CAPRICCIO +5360 TR_AIN_RHAPSODY +5361 TR_MUSICAL_INTERLUDE +5362 TR_JAWAII_SERENADE +5363 TR_NIPELHEIM_REQUIEM +5364 TR_PRON_MARCH +5365 EM_MAGIC_BOOK_M +5366 EM_SPELL_ENCHANTING +5367 EM_ACTIVITY_BURN +5368 EM_INCREASING_ACTIVITY +5369 EM_DIAMOND_STORM +5370 EM_LIGHTNING_LAND +5371 EM_VENOM_SWAMP +5372 EM_CONFLAGRATION +5373 EM_TERRA_DRIVE +5374 EM_ELEMENTAL_SPIRIT_M +5375 EM_SUMMON_ELEMENTAL_ARDOR +5376 EM_SUMMON_ELEMENTAL_DILUVIO +5377 EM_SUMMON_ELEMENTAL_PROCELLA +5378 EM_SUMMON_ELEMENTAL_TERREMOTUS +5379 EM_SUMMON_ELEMENTAL_SERPENS +5380 EM_ELEMENTAL_BUSTER +5381 EM_ELEMENTAL_VEIL +5383 ABC_CHAIN_REACTION_SHOT_ATK +5384 ABC_FROM_THE_ABYSS_ATK +# TODO: 5385-7999 +8000 HOMUN_BEGIN +8001 HLIF_HEAL +8002 HLIF_AVOID +8003 HLIF_BRAIN +8004 HLIF_CHANGE +8005 HAMI_CASTLE +8006 HAMI_DEFENCE +8007 HAMI_SKIN +8008 HAMI_BLOODLUST +8009 HFLI_MOON +8010 HFLI_FLEET +8011 HFLI_SPEED +8012 HFLI_SBR44 +8013 HVAN_CAPRICE +8014 HVAN_CHAOTIC +8015 HVAN_INSTRUCT +8016 HVAN_EXPLOSION +8017 MUTATION_BASEJOB +8018 MH_SUMMON_LEGION +8019 MH_NEEDLE_OF_PARALYZE +8020 MH_POISON_MIST +8021 MH_PAIN_KILLER +8022 MH_LIGHT_OF_REGENE +8023 MH_OVERED_BOOST +8024 MH_ERASER_CUTTER +8025 MH_XENO_SLASHER +8026 MH_SILENT_BREEZE +8027 MH_STYLE_CHANGE +8028 MH_SONIC_CRAW +8029 MH_SILVERVEIN_RUSH +8030 MH_MIDNIGHT_FRENZY +8031 MH_STAHL_HORN +8032 MH_GOLDENE_FERSE +8033 MH_STEINWAND +8034 MH_HEILIGE_STANGE +8035 MH_ANGRIFFS_MODUS +8036 MH_TINDER_BREAKER +8037 MH_CBC +8038 MH_EQC +8039 MH_MAGMA_FLOW +8040 MH_GRANITIC_ARMOR +8041 MH_LAVA_SLIDE +8042 MH_PYROCLASTIC +8043 MH_VOLCANIC_ASH +8044 MH_BLAST_FORGE +8045 MH_TEMPERING +8046 MH_CLASSY_FLUTTER +8047 MH_TWISTER_CUTTER +8048 MH_ABSOLUTE_ZEPHYR +8049 MH_BRUSHUP_CLAW +8050 MH_BLAZING_AND_FURIOUS +8051 MH_THE_ONE_FIGHTER_RISES +8052 MH_POLISHING_NEEDLE +8053 MH_TOXIN_OF_MANDARA +8054 MH_NEEDLE_STINGER +8055 MH_LICHT_GEHORN +8056 MH_GLANZEN_SPIES +8057 MH_HEILIGE_PFERD +8058 MH_GOLDENE_TONE +8059 MH_BLAZING_LAVA +8060 MH_LAST +8061 HOMUN_LAST +# TODO: 8062-8199 +8200 MERCENARY_BEGIN +8201 MS_BASH +8202 MS_MAGNUM +8203 MS_BOWLINGBASH +8204 MS_PARRYING +8205 MS_REFLECTSHIELD +8206 MS_BERSERK +8207 MA_DOUBLE +8208 MA_SHOWER +8209 MA_SKIDTRAP +8210 MA_LANDMINE +8211 MA_SANDMAN +8212 MA_FREEZINGTRAP +8213 MA_REMOVETRAP +8214 MA_CHARGEARROW +8215 MA_SHARPSHOOTING +8216 ML_PIERCE +8217 ML_BRANDISH +8218 ML_SPIRALPIERCE +8219 ML_DEFENDER +8220 ML_AUTOGUARD +8221 ML_DEVOTION +8222 MER_MAGNIFICAT +8223 MER_QUICKEN +8224 MER_SIGHT +8225 MER_CRASH +8226 MER_REGAIN +8227 MER_TENDER +8228 MER_BENEDICTION +8229 MER_RECUPERATE +8230 MER_MENTALCURE +8231 MER_COMPRESS +8232 MER_PROVOKE +8233 MER_AUTOBERSERK +8234 MER_DECAGI +8235 MER_SCAPEGOAT +8236 MER_LEXDIVINA +8237 MER_ESTIMATION +8238 MER_KYRIE +8239 MER_BLESSING +8240 MER_INCAGI +8241 MER_INVINCIBLEOFF2 +8242 MERCENARY_LAST +# TODO: 8243-8399 +8400 ELEMENTAL_BEGIN +8401 EL_CIRCLE_OF_FIRE +8402 EL_FIRE_CLOAK +8403 EL_FIRE_MANTLE +8404 EL_WATER_SCREEN +8405 EL_WATER_DROP +8406 EL_WATER_BARRIER +8407 EL_WIND_STEP +8408 EL_WIND_CURTAIN +8409 EL_ZEPHYR +8410 EL_SOLID_SKIN +8411 EL_STONE_SHIELD +8412 EL_POWER_OF_GAIA +8413 EL_PYROTECHNIC +8414 EL_HEATER +8415 EL_TROPIC +8416 EL_AQUAPLAY +8417 EL_COOLER +8418 EL_CHILLY_AIR +8419 EL_GUST +8420 EL_BLAST +8421 EL_WILD_STORM +8422 EL_PETROLOGY +8423 EL_CURSED_SOIL +8424 EL_UPHEAVAL +8425 EL_FIRE_ARROW +8426 EL_FIRE_BOMB +8427 EL_FIRE_BOMB_ATK +8428 EL_FIRE_WAVE +8429 EL_FIRE_WAVE_ATK +8430 EL_ICE_NEEDLE +8431 EL_WATER_SCREW +8432 EL_WATER_SCREW_ATK +8433 EL_TIDAL_WEAPON +8434 EL_WIND_SLASH +8435 EL_HURRICANE +8436 EL_HURRICANE_ATK +8437 EL_TYPOON_MIS +8438 EL_TYPOON_MIS_ATK +8439 EL_STONE_HAMMER +8440 EL_ROCK_CRUSHER +8441 EL_ROCK_CRUSHER_ATK +8442 EL_STONE_RAIN +8443 EM_EL_FLAMETECHNIC +8444 EM_EL_FLAMEARMOR +8445 EM_EL_FLAMEROCK +8446 EM_EL_COLD_FORCE +8447 EM_EL_CRYSTAL_ARMOR +8448 EM_EL_AGE_OF_ICE +8449 EM_EL_GRACE_BREEZE +8450 EM_EL_EYES_OF_STORM +8451 EM_EL_STORM_WIND +8452 EM_EL_EARTH_CARE +8453 EM_EL_STRONG_PROTECTION +8454 EM_EL_AVALANCHE +8455 EM_EL_DEEP_POISONING +8456 EM_EL_POISON_SHIELD +8457 EM_EL_DEADLY_POISON +8601 ABR_BATTLE_BUSTER +8602 ABR_DUAL_CANNON_FIRE +8603 ABR_NET_REPAIR +8604 ABR_NET_SUPPORT +8605 ABR_INFINITY_BUSTER +# TODO: 8606-9998 +9999 FOLLOWER_NPC_RESET +10000 GD_APPROVAL +10001 GD_KAFRACONTRACT +10002 GD_GUARDRESEARCH +10003 GD_GUARDUP +10004 GD_EXTENSION +10005 GD_GLORYGUILD +10006 GD_LEADERSHIP +10007 GD_GLORYWOUNDS +10008 GD_SOULCOLD +10009 GD_HAWKEYES +10010 GD_BATTLEORDER +10011 GD_REGENERATION +10012 GD_RESTORE +10013 GD_EMERGENCYCALL +10014 GD_DEVELOPMENT +10015 GD_ITEMEMERGENCYCALL +10016 GD_GUILD_STORAGE +10017 GD_CHARGESHOUT_FLAG +10018 GD_CHARGESHOUT_BEATING +10019 GD_EMERGENCY_MOVE +10020 GD_LAST +# TODO: 10021-10099 +10100 SYS_FIRSTJOBLV +10101 SYS_SECONDJOBLV +# TODO: 10102-10999 +11000 SCRIPT_000 +11001 ITEM_COCKTAIL_WARG_BLOOD +11002 ITEM_MINOR_BBQ +11003 ITEM_SIROMA_ICE_TEA +11004 ITEM_DROCERA_HERB_STEAMED +11005 ITEM_PUTTI_TAILS_NOODLES +11006 ITEM_BANANA_BOMB +# TODO: 11007-11998 +11999 SCRIPT_999 +12000 EFST_DRESS_UP +# TODO: 12001-12998 +12999 EFST_999 diff --git a/openkore_llm_knowledge/tables/STATUS_id_handle.txt b/openkore_llm_knowledge/tables/STATUS_id_handle.txt new file mode 100644 index 0000000000..955fdb690b --- /dev/null +++ b/openkore_llm_knowledge/tables/STATUS_id_handle.txt @@ -0,0 +1,1204 @@ +# see kRO client: luafiles514\lua files\stateicon\EFSTIDs.lub +# example: https://github.com/llchrisll/ROenglishRE/blob/master/Renewal/data/luafiles514/lua%20files/stateicon/efstids.lub +0 EFST_PROVOKE +1 EFST_ENDURE +2 EFST_TWOHANDQUICKEN +3 EFST_CONCENTRATION +4 EFST_HIDING +5 EFST_CLOAKING +6 EFST_ENCHANTPOISON +7 EFST_POISONREACT +8 EFST_QUAGMIRE +9 EFST_ANGELUS +10 EFST_BLESSING +11 EFST_CRUCIS +12 EFST_INC_AGI +13 EFST_DEC_AGI +14 EFST_SLOWPOISON +15 EFST_IMPOSITIO +16 EFST_SUFFRAGIUM +17 EFST_ASPERSIO +18 EFST_BENEDICTIO +19 EFST_KYRIE +20 EFST_MAGNIFICAT +21 EFST_GLORIA +22 EFST_LEXAETERNA +23 EFST_ADRENALINE +24 EFST_WEAPONPERFECT +25 EFST_OVERTHRUST +26 EFST_MAXIMIZE +27 EFST_RIDING +28 EFST_FALCON +29 EFST_TRICKDEAD +30 EFST_SHOUT +31 EFST_ENERGYCOAT +32 EFST_BROKENARMOR +33 EFST_BROKENWEAPON +34 EFST_ILLUSION +35 EFST_WEIGHTOVER50 +36 EFST_WEIGHTOVER90 +37 EFST_ATTHASTE_POTION1 +38 EFST_ATTHASTE_POTION2 +39 EFST_ATTHASTE_POTION3 +40 EFST_ATTHASTE_INFINITY +41 EFST_MOVHASTE_POTION +42 EFST_MOVHASTE_INFINITY +43 EFST_AUTOCOUNTER +44 EFST_SPLASHER +45 EFST_ANKLESNARE +46 EFST_POSTDELAY +47 EFST_NOACTION +48 EFST_IMPOSSIBLEPICKUP +49 EFST_BARRIER +50 EFST_NOEQUIPWEAPON +51 EFST_NOEQUIPSHIELD +52 EFST_NOEQUIPARMOR +53 EFST_NOEQUIPHELM +54 EFST_PROTECTWEAPON +55 EFST_PROTECTSHIELD +56 EFST_PROTECTARMOR +57 EFST_PROTECTHELM +58 EFST_AUTOGUARD +59 EFST_REFLECTSHIELD +60 EFST_DEVOTION +61 EFST_PROVIDENCE +62 EFST_DEFENDER +63 EFST_MAGICROD +64 EFST_WEAPONPROPERTY +65 EFST_AUTOSPELL +66 EFST_SPECIALZONE +67 EFST_MASK +68 EFST_SPEARQUICKEN +69 EFST_BDPLAYING +70 EFST_WHISTLE +71 EFST_ASSASSINCROSS +72 EFST_POEMBRAGI +73 EFST_APPLEIDUN +74 EFST_HUMMING +75 EFST_DONTFORGETME +76 EFST_FORTUNEKISS +77 EFST_SERVICEFORYOU +78 EFST_RICHMANKIM +79 EFST_ETERNALCHAOS +80 EFST_DRUMBATTLEFIELD +81 EFST_RINGNIBELUNGEN +82 EFST_ROKISWEIL +83 EFST_INTOABYSS +84 EFST_SIEGFRIED +85 EFST_BLADESTOP +86 EFST_EXPLOSIONSPIRITS +87 EFST_STEELBODY +88 EFST_EXTREMITYFIST +89 EFST_COMBOATTACK +90 EFST_PROPERTYFIRE +91 EFST_PROPERTYWATER +92 EFST_PROPERTYWIND +93 EFST_PROPERTYGROUND +94 EFST_MAGICATTACK +95 EFST_STOP +96 EFST_WEAPONBRAKER +97 EFST_PROPERTYUNDEAD +98 EFST_POWERUP +99 EFST_AGIUP +100 EFST_SIEGEMODE +101 EFST_INVISIBLE +102 EFST_STATUSONE +103 EFST_AURABLADE +104 EFST_PARRYING +105 EFST_LKCONCENTRATION +106 EFST_TENSIONRELAX +107 EFST_BERSERK +108 EFST_SACRIFICE +109 EFST_GOSPEL +110 EFST_ASSUMPTIO +111 EFST_BASILICA +112 EFST_GROUNDMAGIC +113 EFST_MAGICPOWER +114 EFST_EDP +115 EFST_TRUESIGHT +116 EFST_WINDWALK +117 EFST_MELTDOWN +118 EFST_CARTBOOST +119 EFST_CHASEWALK +120 EFST_SWORDREJECT +121 EFST_MARIONETTE_MASTER +122 EFST_MARIONETTE +123 EFST_MOON +124 EFST_BLOODING +125 EFST_JOINTBEAT +126 EFST_MINDBREAKER +127 EFST_MEMORIZE +128 EFST_FOGWALL +129 EFST_SPIDERWEB +130 EFST_PROTECTEXP +131 EFST_SUB_WEAPONPROPERTY +132 EFST_AUTOBERSERK +133 EFST_RUN +134 EFST_TING +135 EFST_STORMKICK_ON +136 EFST_STORMKICK_READY +137 EFST_DOWNKICK_ON +138 EFST_DOWNKICK_READY +139 EFST_TURNKICK_ON +140 EFST_TURNKICK_READY +141 EFST_COUNTER_ON +142 EFST_COUNTER_READY +143 EFST_DODGE_ON +144 EFST_DODGE_READY +145 EFST_STRUP +146 EFST_PROPERTYDARK +147 EFST_ADRENALINE2 +148 EFST_PROPERTYTELEKINESIS +149 EFST_SOULLINK +150 EFST_PLUSATTACKPOWER +151 EFST_PLUSMAGICPOWER +152 EFST_DEVIL1 +153 EFST_KAITE +154 EFST_SWOO +155 EFST_STAR2 +156 EFST_KAIZEL +157 EFST_KAAHI +158 EFST_KAUPE +159 EFST_SMA_READY +160 EFST_SKE +161 EFST_ONEHANDQUICKEN +162 EFST_FRIEND +163 EFST_FRIENDUP +164 EFST_SG_WARM +165 EFST_SG_SUN_WARM +166 EFST_SG_MOON_WARM +167 EFST_SG_STAR_WARM +168 EFST_EMOTION +169 EFST_SUN_COMFORT +170 EFST_MOON_COMFORT +171 EFST_STAR_COMFORT +172 EFST_EXPUP +173 EFST_GDSKILL_BATTLEORDER +174 EFST_GDSKILL_REGENERATION +175 EFST_GDSKILL_POSTDELAY +176 EFST_RESISTHANDICAP +177 EFST_MAXHPPERCENT +178 EFST_MAXSPPERCENT +179 EFST_DEFENCE +180 EFST_SLOWDOWN +181 EFST_PRESERVE +182 EFST_CHASEWALK2 +183 EFST_NOT_EXTREMITYFIST +184 EFST_CLAIRVOYANCE +185 EFST_MOVESLOW_POTION +186 EFST_DOUBLECASTING +187 EFST_GRAVITATION +188 EFST_OVERTHRUSTMAX +189 EFST_LONGING +190 EFST_HERMODE +191 EFST_TAROTCARD +192 EFST_HLIF_AVOID +193 EFST_HFLI_FLEET +194 EFST_HFLI_SPEED +195 EFST_HLIF_CHANGE +196 EFST_HAMI_BLOODLUST +197 EFST_CR_SHRINK +198 EFST_WZ_SIGHTBLASTER +199 EFST_DC_WINKCHARM +200 EFST_RG_CCONFINE_M +201 EFST_RG_CCONFINE_S +202 EFST_DISABLEMOVE +203 EFST_GS_MADNESSCANCEL +204 EFST_GS_GATLINGFEVER +205 EFST_EARTHSCROLL +206 EFST_NJ_UTSUSEMI +207 EFST_NJ_BUNSINJYUTSU +208 EFST_NJ_NEN +209 EFST_GS_ADJUSTMENT +210 EFST_GS_ACCURACY +211 EFST_NJ_SUITON +212 EFST_PET +213 EFST_MENTAL +214 EFST_EXPMEMORY +215 EFST_PERFORMANCE +216 EFST_GAIN +217 EFST_GRIFFON +218 EFST_DRIFT +219 EFST_WALLSHIFT +220 EFST_REINCARNATION +221 EFST_PATTACK +222 EFST_PSPEED +223 EFST_PDEFENSE +224 EFST_PCRITICAL +225 EFST_RANKING +226 EFST_PTRIPLE +227 EFST_DENERGY +228 EFST_WAVE1 +229 EFST_WAVE2 +230 EFST_WAVE3 +231 EFST_WAVE4 +232 EFST_DAURA +233 EFST_DFREEZER +234 EFST_DPUNISH +235 EFST_DBARRIER +236 EFST_DWARNING +237 EFST_MOUSEWHEEL +238 EFST_DGAUGE +239 EFST_DACCEL +240 EFST_DBLOCK +241 EFST_FOOD_STR +242 EFST_FOOD_AGI +243 EFST_FOOD_VIT +244 EFST_FOOD_DEX +245 EFST_FOOD_INT +246 EFST_FOOD_LUK +247 EFST_FOOD_BASICAVOIDANCE +248 EFST_FOOD_BASICHIT +249 EFST_FOOD_CRITICALSUCCESSVALUE +250 EFST_CASH_PLUSEXP +251 EFST_CASH_DEATHPENALTY +252 EFST_CASH_RECEIVEITEM +253 EFST_CASH_BOSS_ALARM +254 EFST_DA_ENERGY +255 EFST_DA_FIRSTSLOT +256 EFST_DA_HEADDEF +257 EFST_DA_SPACE +258 EFST_DA_TRANSFORM +259 EFST_DA_ITEMREBUILD +260 EFST_DA_ILLUSION +261 EFST_DA_DARKPOWER +262 EFST_DA_EARPLUG +263 EFST_DA_CONTRACT +264 EFST_DA_BLACK +265 EFST_DA_MAGICCART +266 EFST_CRYSTAL +267 EFST_DA_REBUILD +268 EFST_DA_EDARKNESS +269 EFST_DA_EGUARDIAN +270 EFST_DA_TIMEOUT +271 EFST_FOOD_STR_CASH +272 EFST_FOOD_AGI_CASH +273 EFST_FOOD_VIT_CASH +274 EFST_FOOD_DEX_CASH +275 EFST_FOOD_INT_CASH +276 EFST_FOOD_LUK_CASH +277 EFST_MER_FLEE +278 EFST_MER_ATK +279 EFST_MER_HP +280 EFST_MER_SP +281 EFST_MER_HIT +282 EFST_SLOWCAST +283 EFST_MAGICMIRROR +284 EFST_STONESKIN +285 EFST_ANTIMAGIC +286 EFST_CRITICALWOUND +287 EFST_NPC_DEFENDER +288 EFST_NOACTION_WAIT +289 EFST_MOVHASTE_HORSE +290 EFST_PROTECT_DEF +291 EFST_PROTECT_MDEF +292 EFST_HEALPLUS +293 EFST_S_LIFEPOTION +294 EFST_L_LIFEPOTION +295 EFST_CRITICALPERCENT +296 EFST_PLUSAVOIDVALUE +297 EFST_ATKER_ASPD +298 EFST_TARGET_ASPD +299 EFST_ATKER_MOVESPEED +300 EFST_ATKER_BLOOD +301 EFST_TARGET_BLOOD +302 EFST_ARMOR_PROPERTY +303 EFST_REUSE_LIMIT_A +304 EFST_HELLPOWER +305 EFST_STEAMPACK +306 EFST_REUSE_LIMIT_B +307 EFST_REUSE_LIMIT_C +308 EFST_REUSE_LIMIT_D +309 EFST_REUSE_LIMIT_E +310 EFST_REUSE_LIMIT_F +311 EFST_INVINCIBLE +312 EFST_CASH_PLUSONLYJOBEXP +313 EFST_PARTYFLEE +314 EFST_ANGEL_PROTECT +315 EFST_ENDURE_MDEF +316 EFST_ENCHANTBLADE +317 EFST_DEATHBOUND +318 EFST_REFRESH +319 EFST_GIANTGROWTH +320 EFST_STONEHARDSKIN +321 EFST_VITALITYACTIVATION +322 EFST_FIGHTINGSPIRIT +323 EFST_ABUNDANCE +324 EFST_REUSE_MILLENNIUMSHIELD +325 EFST_REUSE_CRUSHSTRIKE +326 EFST_REUSE_REFRESH +327 EFST_REUSE_STORMBLAST +328 EFST_VENOMIMPRESS +329 EFST_EPICLESIS +330 EFST_ORATIO +331 EFST_LAUDAAGNUS +332 EFST_LAUDARAMUS +333 EFST_CLOAKINGEXCEED +334 EFST_HALLUCINATIONWALK +335 EFST_HALLUCINATIONWALK_POSTDELAY +336 EFST_RENOVATIO +337 EFST_WEAPONBLOCKING +338 EFST_WEAPONBLOCKING_POSTDELAY +339 EFST_ROLLINGCUTTER +340 EFST_EXPIATIO +341 EFST_POISONINGWEAPON +342 EFST_TOXIN +343 EFST_PARALYSE +344 EFST_VENOMBLEED +345 EFST_MAGICMUSHROOM +346 EFST_DEATHHURT +347 EFST_PYREXIA +348 EFST_OBLIVIONCURSE +349 EFST_LEECHESEND +350 EFST_DUPLELIGHT +351 EFST_FROSTMISTY +352 EFST_FEARBREEZE +353 EFST_ELECTRICSHOCKER +354 EFST_MARSHOFABYSS +355 EFST_RECOGNIZEDSPELL +356 EFST_STASIS +357 EFST_WUGRIDER +358 EFST_WUGDASH +359 EFST_WUGBITE +360 EFST_CAMOUFLAGE +361 EFST_ACCELERATION +362 EFST_HOVERING +363 EFST_SUMMON1 +364 EFST_SUMMON2 +365 EFST_SUMMON3 +366 EFST_SUMMON4 +367 EFST_SUMMON5 +368 EFST_MVPCARD_TAOGUNKA +369 EFST_MVPCARD_MISTRESS +370 EFST_MVPCARD_ORCHERO +371 EFST_MVPCARD_ORCLORD +372 EFST_OVERHEAT_LIMITPOINT +373 EFST_OVERHEAT +374 EFST_SHAPESHIFT +375 EFST_INFRAREDSCAN +376 EFST_MAGNETICFIELD +377 EFST_NEUTRALBARRIER +378 EFST_NEUTRALBARRIER_MASTER +379 EFST_STEALTHFIELD +380 EFST_STEALTHFIELD_MASTER +381 EFST_MANU_ATK +382 EFST_MANU_DEF +383 EFST_SPL_ATK +384 EFST_SPL_DEF +385 EFST_REPRODUCE +386 EFST_MANU_MATK +387 EFST_SPL_MATK +388 EFST_STR_SCROLL +389 EFST_INT_SCROLL +390 EFST_LG_REFLECTDAMAGE +391 EFST_FORCEOFVANGUARD +392 EFST_BUCHEDENOEL +393 EFST_AUTOSHADOWSPELL +394 EFST_SHADOWFORM +395 EFST_RAID +396 EFST_SHIELDSPELL_DEF +397 EFST_SHIELDSPELL_MDEF +398 EFST_SHIELDSPELL_REF +399 EFST_BODYPAINT +400 EFST_EXEEDBREAK +401 EFST_ADORAMUS +402 EFST_PRESTIGE +403 EFST_INVISIBILITY +404 EFST_DEADLYINFECT +405 EFST_BANDING +406 EFST_EARTHDRIVE +407 EFST_INSPIRATION +408 EFST_ENERVATION +409 EFST_GROOMY +410 EFST_RAISINGDRAGON +411 EFST_IGNORANCE +412 EFST_LAZINESS +413 EFST_LIGHTNINGWALK +414 EFST_ACARAJE +415 EFST_UNLUCKY +416 EFST_CURSEDCIRCLE_ATKER +417 EFST_CURSEDCIRCLE_TARGET +418 EFST_WEAKNESS +419 EFST_CRESCENTELBOW +420 EFST_NOEQUIPACCESSARY +421 EFST_STRIPACCESSARY +422 EFST_MANHOLE +423 EFST_POPECOOKIE +424 EFST_FALLENEMPIRE +425 EFST_GENTLETOUCH_ENERGYGAIN +426 EFST_GENTLETOUCH_CHANGE +427 EFST_GENTLETOUCH_REVITALIZE +428 EFST_BLOODYLUST +429 EFST_SWING +430 EFST_SYMPHONY_LOVE +431 EFST_PROPERTYWALK +432 EFST_SPELLFIST +433 EFST_NETHERWORLD +434 EFST_SIREN +435 EFST_DEEP_SLEEP +436 EFST_SIRCLEOFNATURE +437 EFST_COLD +438 EFST_GLOOMYDAY +439 EFST_SONG_OF_MANA +440 EFST_CLOUD_KILL +441 EFST_DANCE_WITH_WUG +442 EFST_RUSH_WINDMILL +443 EFST_ECHOSONG +444 EFST_HARMONIZE +445 EFST_STRIKING +446 EFST_WARMER +447 EFST_MOONLIT_SERENADE +448 EFST_SATURDAY_NIGHT_FEVER +449 EFST_SITDOWN_FORCE +450 EFST_ANALYZE +451 EFST_LERADS_DEW +452 EFST_MELODYOFSINK +453 EFST_BEYOND_OF_WARCRY +454 EFST_UNLIMITED_HUMMING_VOICE +455 EFST_SPELLBOOK1 +456 EFST_SPELLBOOK2 +457 EFST_SPELLBOOK3 +458 EFST_FREEZE_SP +459 EFST_GN_TRAINING_SWORD +460 EFST_GN_REMODELING_CART +461 EFST_GN_CARTBOOST +462 EFST_FIXEDCASTINGTM_REDUCE +463 EFST_THORNS_TRAP +464 EFST_BLOOD_SUCKER +465 EFST_SPORE_EXPLOSION +466 EFST_DEMONIC_FIRE +467 EFST_FIRE_EXPANSION_SMOKE_POWDER +468 EFST_FIRE_EXPANSION_TEAR_GAS +469 EFST_BLOCKING_PLAY +470 EFST_MANDRAGORA +471 EFST_ACTIVATE +472 EFST_AB_SECRAMENT +473 EFST_ASSUMPTIO2 +474 EFST_TK_SEVENWIND +475 EFST_LIMIT_ODINS_RECALL +476 EFST_STOMACHACHE +477 EFST_MYSTERIOUS_POWDER +478 EFST_MELON_BOMB +479 EFST_BANANA_BOMB_SITDOWN_POSTDELAY +480 EFST_PROMOTE_HEALTH_RESERCH +481 EFST_ENERGY_DRINK_RESERCH +482 EFST_EXTRACT_WHITE_POTION_Z +483 EFST_VITATA_500 +484 EFST_EXTRACT_SALAMINE_JUICE +485 EFST_BOOST500 +486 EFST_FULL_SWING_K +487 EFST_MANA_PLUS +488 EFST_MUSTLE_M +489 EFST_LIFE_FORCE_F +490 EFST_VACUUM_EXTREME +491 EFST_SAVAGE_STEAK +492 EFST_COCKTAIL_WARG_BLOOD +493 EFST_MINOR_BBQ +494 EFST_SIROMA_ICE_TEA +495 EFST_DROCERA_HERB_STEAMED +496 EFST_PUTTI_TAILS_NOODLES +497 EFST_BANANA_BOMB +498 EFST_SUMMON_AGNI +499 EFST_SPELLBOOK4 +500 EFST_SPELLBOOK5 +501 EFST_SPELLBOOK6 +502 EFST_SPELLBOOK7 +503 EFST_ELEMENTAL_AGGRESSIVE +504 EFST_RETURN_TO_ELDICASTES +505 EFST_BANDING_DEFENCE +506 EFST_SKELSCROLL +507 EFST_DISTRUCTIONSCROLL +508 EFST_ROYALSCROLL +509 EFST_IMMUNITYSCROLL +510 EFST_MYSTICSCROLL +511 EFST_BATTLESCROLL +512 EFST_ARMORSCROLL +513 EFST_FREYJASCROLL +514 EFST_SOULSCROLL +515 EFST_CIRCLE_OF_FIRE +516 EFST_CIRCLE_OF_FIRE_OPTION +517 EFST_FIRE_CLOAK +518 EFST_FIRE_CLOAK_OPTION +519 EFST_WATER_SCREEN +520 EFST_WATER_SCREEN_OPTION +521 EFST_WATER_DROP +522 EFST_WATER_DROP_OPTION +523 EFST_WIND_STEP +524 EFST_WIND_STEP_OPTION +525 EFST_WIND_CURTAIN +526 EFST_WIND_CURTAIN_OPTION +527 EFST_WATER_BARRIER +528 EFST_ZEPHYR +529 EFST_SOLID_SKIN +530 EFST_SOLID_SKIN_OPTION +531 EFST_STONE_SHIELD +532 EFST_STONE_SHIELD_OPTION +533 EFST_POWER_OF_GAIA +534 EFST_EL_WAIT +535 EFST_EL_PASSIVE +536 EFST_EL_DEFENSIVE +537 EFST_EL_OFFENSIVE +538 EFST_EL_COST +539 EFST_PYROTECHNIC +540 EFST_PYROTECHNIC_OPTION +541 EFST_HEATER +542 EFST_HEATER_OPTION +543 EFST_TROPIC +544 EFST_TROPIC_OPTION +545 EFST_AQUAPLAY +546 EFST_AQUAPLAY_OPTION +547 EFST_COOLER +548 EFST_COOLER_OPTION +549 EFST_CHILLY_AIR +550 EFST_CHILLY_AIR_OPTION +551 EFST_GUST +552 EFST_GUST_OPTION +553 EFST_BLAST +554 EFST_BLAST_OPTION +555 EFST_WILD_STORM +556 EFST_WILD_STORM_OPTION +557 EFST_PETROLOGY +558 EFST_PETROLOGY_OPTION +559 EFST_CURSED_SOIL +560 EFST_CURSED_SOIL_OPTION +561 EFST_UPHEAVAL +562 EFST_UPHEAVAL_OPTION +563 EFST_TIDAL_WEAPON +564 EFST_TIDAL_WEAPON_OPTION +565 EFST_ROCK_CRUSHER +566 EFST_ROCK_CRUSHER_ATK +567 EFST_FIRE_INSIGNIA +568 EFST_WATER_INSIGNIA +569 EFST_WIND_INSIGNIA +570 EFST_EARTH_INSIGNIA +571 EFST_EQUIPED_FLOOR +572 EFST_GUARDIAN_RECALL +573 EFST_MORA_BUFF +574 EFST_REUSE_LIMIT_G +575 EFST_REUSE_LIMIT_H +576 EFST_NEEDLE_OF_PARALYZE +577 EFST_PAIN_KILLER +578 EFST_G_LIFEPOTION +579 EFST_VITALIZE_POTION +580 EFST_LIGHT_OF_REGENE +581 EFST_OVERED_BOOST +582 EFST_SILENT_BREEZE +583 EFST_ODINS_POWER +584 EFST_STYLE_CHANGE +585 EFST_SONIC_CLAW_POSTDELAY +596 EFST_SILVERVEIN_RUSH_POSTDELAY +597 EFST_MIDNIGHT_FRENZY_POSTDELAY +598 EFST_GOLDENE_FERSE +599 EFST_ANGRIFFS_MODUS +600 EFST_TINDER_BREAKER +601 EFST_TINDER_BREAKER_POSTDELAY +602 EFST_CBC +603 EFST_CBC_POSTDELAY +604 EFST_EQC +605 EFST_MAGMA_FLOW +606 EFST_GRANITIC_ARMOR +607 EFST_PYROCLASTIC +608 EFST_VOLCANIC_ASH +609 EFST_SPIRITS_SAVEINFO1 +610 EFST_SPIRITS_SAVEINFO2 +611 EFST_MAGIC_CANDY +612 EFST_SEARCH_STORE_INFO +613 EFST_ALL_RIDING +614 EFST_ALL_RIDING_REUSE_LIMIT +615 EFST_MACRO +616 EFST_MACRO_POSTDELAY +617 EFST_BEER_BOTTLE_CAP +618 EFST_OVERLAPEXPUP +619 EFST_PC_IZ_DUN05 +620 EFST_CRUSHSTRIKE +621 EFST_MONSTER_TRANSFORM +622 EFST_SIT +623 EFST_ONAIR +624 EFST_MTF_ASPD +625 EFST_MTF_RANGEATK +626 EFST_MTF_MATK +627 EFST_MTF_MLEATKED +628 EFST_MTF_CRIDAMAGE +629 EFST_REUSE_LIMIT_MTF +630 EFST_MACRO_PERMIT +631 EFST_MACRO_PLAY +632 EFST_SKF_CAST +633 EFST_SKF_ASPD +634 EFST_SKF_ATK +635 EFST_SKF_MATK +636 EFST_REWARD_PLUSONLYJOBEXP +637 EFST_HANDICAPSTATE_NORECOVER +638 EFST_SET_NUM_DEF +639 EFST_SET_NUM_MDEF +640 EFST_SET_PER_DEF +641 EFST_SET_PER_MDEF +642 EFST_PARTYBOOKING_SEARCH_DELAY +643 EFST_PARTYBOOKING_REGISTER_DELAY +644 EFST_PERIOD_TIME_CHECK_DETECT_SKILL +645 EFST_KO_JYUMONJIKIRI +646 EFST_MEIKYOUSISUI +647 EFST_ATTHASTE_CASH +648 EFST_EQUIPPED_DIVINE_ARMOR +649 EFST_EQUIPPED_HOLY_ARMOR +650 EFST_2011RWC +651 EFST_KYOUGAKU +652 EFST_IZAYOI +653 EFST_ZENKAI +654 EFST_KG_KAGEHUMI +655 EFST_KYOMU +656 EFST_KAGEMUSYA +657 EFST_ZANGETSU +658 EFST_PHI_DEMON +659 EFST_GENSOU +660 EFST_AKAITSUKI +661 EFST_TETANY +662 EFST_GM_BATTLE +663 EFST_GM_BATTLE2 +664 EFST_2011RWC_SCROLL +665 EFST_ACTIVE_MONSTER_TRANSFORM +666 EFST_MYSTICPOWDER +667 EFST_ECLAGE_RECALL +668 EFST_ENTRY_QUEUE_APPLY_DELAY +669 EFST_REUSE_LIMIT_ECL +670 EFST_M_LIFEPOTION +671 EFST_ENTRY_QUEUE_NOTIFY_ADMISSION_TIME_OUT +672 EFST_UNKNOWN_NAME +673 EFST_ON_PUSH_CART +674 EFST_HAT_EFFECT +675 EFST_FLOWER_LEAF +676 EFST_RAY_OF_PROTECTION +677 EFST_GLASTHEIM_ATK +678 EFST_GLASTHEIM_DEF +679 EFST_GLASTHEIM_HEAL +680 EFST_GLASTHEIM_HIDDEN +681 EFST_GLASTHEIM_STATE +682 EFST_GLASTHEIM_ITEMDEF +683 EFST_GLASTHEIM_HPSP +684 EFST_FOLLOWER_NPC_SKILL_POSTDELAY +685 EFST_ALMIGHTY +686 EFST_GVG_GIANT +687 EFST_GVG_GOLEM +688 EFST_GVG_STUN +689 EFST_GVG_STONE +690 EFST_GVG_FREEZ +691 EFST_GVG_SLEEP +692 EFST_GVG_CURSE +693 EFST_GVG_SILENCE +694 EFST_GVG_BLIND +695 EFST_CLIENT_ONLY_EQUIP_ARROW +696 EFST_CLAN_INFO +697 EFST_JP_EVENT01 +698 EFST_JP_EVENT02 +699 EFST_JP_EVENT03 +700 EFST_JP_EVENT04 +701 EFST_TELEPORT_FIXEDCASTINGDELAY +702 EFST_GEFFEN_MAGIC1 +703 EFST_GEFFEN_MAGIC2 +704 EFST_GEFFEN_MAGIC3 +705 EFST_QUEST_BUFF1 +706 EFST_QUEST_BUFF2 +707 EFST_QUEST_BUFF3 +708 EFST_REUSE_LIMIT_RECALL +709 EFST_SAVEPOSITION +710 EFST_HANDICAPSTATE_ICEEXPLO +711 EFST_FENRIR_CARD +712 EFST_REUSE_LIMIT_ASPD_POTION +713 EFST_MAXPAIN +714 EFST_PC_STOP +715 EFST_FRIGG_SONG +716 EFST_OFFERTORIUM +717 EFST_TELEKINESIS_INTENSE +718 EFST_MOONSTAR +719 EFST_STRANGELIGHTS +720 EFST_FULL_THROTTLE +721 EFST_REBOUND +722 EFST_UNLIMIT +723 EFST_KINGS_GRACE +724 EFST_ITEM_ATKMAX +725 EFST_ITEM_ATKMIN +726 EFST_ITEM_MATKMAX +727 EFST_ITEM_MATKMIN +728 EFST_SUPER_STAR +729 EFST_HIGH_RANKER +730 EFST_DARKCROW +731 EFST_2013_VALENTINE1 +732 EFST_2013_VALENTINE2 +733 EFST_2013_VALENTINE3 +734 EFST_ILLUSIONDOPING +735 EFST_WIDEWEB +736 EFST_CHILL +737 EFST_BURNT +738 EFST_PCCAFE_PLAY_TIME +739 EFST_TWISTED_TIME +740 EFST_FLASHCOMBO +741 EFST_JITTER_BUFF1 +742 EFST_JITTER_BUFF2 +743 EFST_JITTER_BUFF3 +744 EFST_JITTER_BUFF4 +745 EFST_JITTER_BUFF5 +746 EFST_JITTER_BUFF6 +747 EFST_JITTER_BUFF7 +748 EFST_JITTER_BUFF8 +749 EFST_JITTER_BUFF9 +750 EFST_JITTER_BUFF10 +751 EFST_CUP_OF_BOZA +752 EFST_B_TRAP +753 EFST_E_CHAIN +754 EFST_E_QD_SHOT_READY +755 EFST_C_MARKER +756 EFST_H_MINE +757 EFST_H_MINE_SPLASH +758 EFST_P_ALTER +759 EFST_HEAT_BARREL +760 EFST_ANTI_M_BLAST +761 EFST_SLUGSHOT +762 EFST_SWORDCLAN +763 EFST_ARCWANDCLAN +764 EFST_GOLDENMACECLAN +765 EFST_CROSSBOWCLAN +766 EFST_PACKING_ENVELOPE1 +767 EFST_PACKING_ENVELOPE2 +768 EFST_PACKING_ENVELOPE3 +769 EFST_PACKING_ENVELOPE4 +770 EFST_PACKING_ENVELOPE5 +771 EFST_PACKING_ENVELOPE6 +772 EFST_PACKING_ENVELOPE7 +773 EFST_PACKING_ENVELOPE8 +774 EFST_PACKING_ENVELOPE9 +775 EFST_PACKING_ENVELOPE10 +776 EFST_GLASTHEIM_TRANS +777 EFST_ZONGZI_POUCH_TRANS +778 EFST_HEAT_BARREL_AFTER +779 EFST_DECORATION_OF_MUSIC +780 EFST_OVERSEAEXPUP +781 EFST_CLOWN_N_GYPSY_CARD +782 EFST_OPEN_NPC_MARKET +783 EFST_BEEF_RIB_STEW +784 EFST_PORK_RIB_STEW +785 EFST_CHUSEOK_MONDAY +786 EFST_CHUSEOK_TUESDAY +787 EFST_CHUSEOK_WEDNESDAY +788 EFST_CHUSEOK_THURSDAY +789 EFST_CHUSEOK_FRIDAY +790 EFST_CHUSEOK_WEEKEND +791 EFST_ALL_LIGHTGUARD +792 EFST_ALL_LIGHTGUARD_COOL_TIME +793 EFST_MTF_MHP +794 EFST_MTF_MSP +795 EFST_MTF_PUMPKIN +796 EFST_MTF_HITFLEE +797 EFST_MTF_CRIDAMAGE2 +798 EFST_MTF_SPDRAIN +799 EFST_ACUO_MINT_GUM +800 EFST_S_HEALPOTION +801 EFST_REUSE_LIMIT_S_HEAL_POTION +802 EFST_PLAYTIME_STATISTICS +803 EFST_GN_CHANGEMATERIAL_OPERATOR +804 EFST_GN_MIX_COOKING_OPERATOR +805 EFST_GN_MAKEBOMB_OPERATOR +806 EFST_GN_S_PHARMACY_OPERATOR +807 EFST_SO_EL_ANALYSIS_DISASSEMBLY_OPERATOR +808 EFST_SO_EL_ANALYSIS_COMBINATION_OPERATOR +809 EFST_NC_MAGICDECOY_OPERATOR +810 EFST_GUILD_STORAGE +811 EFST_GC_POISONINGWEAPON_OPERATOR +812 EFST_WS_WEAPONREFINE_OPERATOR +813 EFST_BS_REPAIRWEAPON_OPERATOR +814 EFST_UNREADMAIL_CHECK +815 EFST_JUMPINGCLAN +816 EFST_JP_OTP +817 EFST_HANDICAPTOLERANCE_LEVELGAP +818 EFST_MTF_RANGEATK2 +819 EFST_MTF_ASPD2 +820 EFST_MTF_MATK2 +821 EFST_SHOW_NPCHPBAR +822 EFST_FLOWERSMOKE +823 EFST_FSTONE +824 EFST_DAILYSENDMAILCNT +825 EFST_QSCARABA +826 EFST_LJOSALFAR +827 EFST_PAD_READER_KNIGHT +828 EFST_PAD_READER_CRUSADER +829 EFST_PAD_READER_BLACKSMITH +830 EFST_PAD_READER_ALCHEMIST +831 EFST_PAD_READER_ASSASSIN +832 EFST_PAD_READER_ROGUE +833 EFST_PAD_READER_WIZARD +834 EFST_PAD_READER_SAGE +835 EFST_PAD_READER_PRIEST +836 EFST_PAD_READER_MONK +837 EFST_PAD_READER_HUNTER +838 EFST_PAD_READER_BARD +839 EFST_PAD_READER_DANCER +840 EFST_PAD_READER_TAEKWON +841 EFST_PAD_READER_NINJA +842 EFST_PAD_READER_GUNSLINGER +843 EFST_PAD_READER_SUPERNOVICE +844 EFST_ESSENCE_OF_TIME +845 EFST_MINIGAME_ROULETTE +846 EFST_MINIGAME_GOLD_POINT +847 EFST_MINIGAME_SILVER_POINT +848 EFST_MINIGAME_BRONZE_POINT +849 EFST_HAPPINESS_STAR +850 EFST_SUMMEREVENT01 +851 EFST_SUMMEREVENT02 +852 EFST_SUMMEREVENT03 +853 EFST_SUMMEREVENT04 +854 EFST_SUMMEREVENT05 +855 EFST_MINIGAME_ROULETTE_BONUS_ITEM +856 EFST_DRESS_UP +857 EFST_MAPLE_FALLS +858 EFST_ALL_NIFLHEIM_RECALL +860 EFST_MTF_MARIONETTE +861 EFST_MTF_LUDE +862 EFST_MTF_CRUISER +863 EFST_MERMAID_LONGING +864 EFST_MAGICAL_FEATHER +865 EFST_DRACULA_CARD +867 EFST_LIMIT_POWER_BOOSTER +868 EFST_GIFT_OF_SNOW +872 EFST_TIME_ACCESSORY +873 EFST_EP16_DEF +874 EFST_NORMAL_ATKED_SP +875 EFST_BODYSTATE_STONECURSE +876 EFST_BODYSTATE_FREEZING +877 EFST_BODYSTATE_STUN +878 EFST_BODYSTATE_SLEEP +879 EFST_BODYSTATE_UNDEAD +880 EFST_BODYSTATE_STONECURSE_ING +881 EFST_BODYSTATE_BURNNING +882 EFST_BODYSTATE_IMPRISON +883 EFST_HEALTHSTATE_POISON +884 EFST_HEALTHSTATE_CURSE +885 EFST_HEALTHSTATE_SILENCE +886 EFST_HEALTHSTATE_CONFUSION +887 EFST_HEALTHSTATE_BLIND +888 EFST_HEALTHSTATE_ANGELUS +889 EFST_HEALTHSTATE_BLOODING +890 EFST_HEALTHSTATE_HEAVYPOISON +891 EFST_HEALTHSTATE_FEAR +892 EFST_CHERRY_BLOSSOM_CAKE +893 EFST_SU_STOOP +894 EFST_CATNIPPOWDER +895 EFST_HEAD_EQUIPMENT_EFFECT +896 EFST_SV_ROOTTWIST +897 EFST_ATTACK_PROPERTY_NOTHING +898 EFST_ATTACK_PROPERTY_WATER +899 EFST_ATTACK_PROPERTY_GROUND +900 EFST_ATTACK_PROPERTY_FIRE +901 EFST_ATTACK_PROPERTY_WIND +902 EFST_ATTACK_PROPERTY_POISON +903 EFST_ATTACK_PROPERTY_SAINT +904 EFST_ATTACK_PROPERTY_DARKNESS +905 EFST_ATTACK_PROPERTY_TELEKINESIS +906 EFST_ATTACK_PROPERTY_UNDEAD +907 EFST_RESIST_PROPERTY_NOTHING +908 EFST_RESIST_PROPERTY_WATER +909 EFST_RESIST_PROPERTY_GROUND +910 EFST_RESIST_PROPERTY_FIRE +911 EFST_RESIST_PROPERTY_WIND +912 EFST_RESIST_PROPERTY_POISON +913 EFST_RESIST_PROPERTY_SAINT +914 EFST_RESIST_PROPERTY_DARKNESS +915 EFST_RESIST_PROPERTY_TELEKINESIS +916 EFST_RESIST_PROPERTY_UNDEAD +917 EFST_BITESCAR +918 EFST_ARCLOUSEDASH +919 EFST_TUNAPARTY +920 EFST_SHRIMP +921 EFST_FRESHSHRIMP +922 EFST_PERIOD_RECEIVEITEM +923 EFST_PERIOD_PLUSEXP +924 EFST_PERIOD_PLUSJOBEXP +925 EFST_RUNEHELM +926 EFST_HELM_VERKANA +927 EFST_HELM_RHYDO +928 EFST_HELM_TURISUS +929 EFST_HELM_HAGALAS +930 EFST_HELM_ISIA +931 EFST_HELM_ASIR +932 EFST_HELM_URJ +933 EFST_SUHIDE +935 EFST_DORAM_BUF_01 +936 EFST_DORAM_BUF_02 +937 EFST_SPRITEMABLE +938 EFST_AID_PERIOD_RECEIVEITEM +939 EFST_AID_PERIOD_PLUSEXP +940 EFST_AID_PERIOD_PLUSJOBEXP +941 EFST_AID_PERIOD_DEADPENALTY +942 EFST_AID_PERIOD_ADDSTOREITEMCOUNT +948 EFST_MAGICSTONE_OF_GRACE_SET +950 EFST_HISS +952 EFST_NYANGGRASS +953 EFST_CHATTERING +961 EFST_GROOMING +962 EFST_PROTECTIONOFSHRIMP +963 EFST_EP16_2_BUFF_SS +964 EFST_EP16_2_BUFF_SC +965 EFST_EP16_2_BUFF_AC +966 EFST_GS_MAGICAL_BULLET +976 EFST_FALLEN_ANGEL +979 EFST_BLAZE_BEAD +980 EFST_FROZEN_BEAD +981 EFST_BREEZE_BEAD +983 EFST_AID_PERIOD_RECEIVEITEM_2ND +984 EFST_AID_PERIOD_PLUSEXP_2ND +985 EFST_AID_PERIOD_PLUSJOBEXP_2ND +986 EFST_PRONTERA_JP +988 EFST_GLOOM_CARD +989 EFST_PHARAOH_CARD +990 EFST_KIEL_CARD +992 EFST_CHEERUP +993 EFST_INVULNERABILITY +994 EFST_STOP +995 EFST_S_MANAPOTION +996 EFST_M_DEFSCROLL +1000 EFST_AS_RAGGED_GOLEM_CARD +1001 EFST_LHZ_DUN_N1 +1002 EFST_LHZ_DUN_N2 +1003 EFST_LHZ_DUN_N3 +1004 EFST_LHZ_DUN_N4 +1013 EFST_ALL_STAT_DOWN +1014 EFST_GRADUAL_GRAVITY +1015 EFST_DAMAGE_HEAL +1016 EFST_IMMUNE_PROPERTY_NOTHING +1017 EFST_IMMUNE_PROPERTY_WATER +1018 EFST_IMMUNE_PROPERTY_GROUND +1019 EFST_IMMUNE_PROPERTY_FIRE +1020 EFST_IMMUNE_PROPERTY_WIND +1021 EFST_IMMUNE_PROPERTY_POISON +1022 EFST_IMMUNE_PROPERTY_SAINT +1023 EFST_IMMUNE_PROPERTY_DARKNESS +1024 EFST_IMMUNE_PROPERTY_TELEKINESIS +1025 EFST_IMMUNE_PROPERTY_UNDEAD +1026 EFST_REUSE_LIMIT_NP +1027 EFST_SPECIALCOOKIE +1030 EFST_GLORY_OF_RETURN +1031 EFST_ATK_POPCORN +1032 EFST_MATK_POPCORN +1033 EFST_ASPD_POPCORN +1034 EFST_ULTIMATECOOK +1035 EFST_LIGHTOFMOON +1036 EFST_LIGHTOFSUN +1037 EFST_LIGHTOFSTAR +1038 EFST_LUNARSTANCE +1039 EFST_UNIVERSESTANCE +1040 EFST_SUNSTANCE +1041 EFST_FLASHKICK +1042 EFST_NEWMOON +1043 EFST_STARSTANCE +1044 EFST_DIMENSION +1045 EFST_DIMENSION1 +1046 EFST_DIMENSION2 +1047 EFST_CREATINGSTAR +1048 EFST_FALLINGSTAR +1049 EFST_NOVAEXPLOSING +1050 EFST_GRAVITYCONTROL +1053 EFST_SOULCOLLECT +1054 EFST_SOULREAPER +1055 EFST_SOULUNITY +1056 EFST_SOULSHADOW +1057 EFST_SOULFAIRY +1058 EFST_SOULFALCON +1059 EFST_SOULGOLEM +1060 EFST_SOULDIVISION +1061 EFST_SOULENERGY +1062 EFST_USE_SKILL_SP_SPA +1063 EFST_USE_SKILL_SP_SHA +1064 EFST_SP_SHA +1065 EFST_INFINITY_DRINK +1066 EFST_ABYSS_001 +1067 EFST_ABYSS_002 +1068 EFST_ABYSS_003 +1069 EFST_ABYSS_004 +1070 EFST_ABYSS_005 +1071 EFST_ABYSS_006 +1072 EFST_ABYSS_007 +1073 EFST_ABYSS_008 +1081 EFST_YGGDRASIL_BLESS +1083 EFST_HUNTING_EVENT +1084 EFST_PERIOD_RECEIVEITEM_2ND +1085 EFST_PERIOD_PLUSEXP_2ND +1086 EFST_EXPDROPUP +1087 EFST_TW_NEWYEAR_EVENT +1088 EFST_ENSEMBLEFATIGUE +1089 EFST_ADAPTATION +1095 EFST_ANCILLA +1104 EFST_FESTIVE_ENERGY +1107 EFST_WEAPONBLOCK_ON +1108 EFST_CRI_DAMAGE +1109 EFST_DEF_POWER +1110 EFST_DEF_IGNORE +1111 EFST_BOW_ATK_POWER +1112 EFST_RED_ORG_POTION +1113 EFST_CAST_TIME +1117 EFST_LEAPIMPAIRED +1119 EFST_EXCLUSIVE_RECEIVEITEM +1120 EFST_EXCLUSIVE_PLUSEXP +1121 EFST_ASSUMPTIO_BUFF +1122 EFST_BASILICA_BUFF +1123 EFST_OVERLAPEXPUP2 +1125 EFST_SOULCURSE +1126 EFST_SOUND_OF_DESTRUCTION +1129 EFST_NV_BREAKTHROUGH +1130 EFST_HELPANGEL +1131 EFST_NV_TRANSCENDENCE +1132 EFST_SWEETSFAIR_ATK +1133 EFST_SWEETSFAIR_MATK +1135 EFST_FLOWER_LEAF2 +1136 EFST_FLOWER_LEAF3 +1137 EFST_FLOWER_LEAF4 +1141 EFST_MISTY_FROST +1142 EFST_MAGIC_POISON +1150 EFST_DEADLY_DEFEASANCE +1151 EFST_CLIMAX_DES_HU +1152 EFST_CLIMAX +1154 EFST_LUXANIMA +1155 EFST_BATH_FOAM_A +1156 EFST_BATH_FOAM_B +1157 EFST_BATH_FOAM_C +1158 EFST_AROMA_OIL +1159 EFST_REUSE_LIMIT_LUXANIMA +1160 EFST_POWERFUL_FAITH +1161 EFST_SINCERE_FAITH +1162 EFST_FIRM_FAITH +1165 EFST_HELLS_PLANT_ARMOR +1166 EFST_RELIEVE_DAMAGE +1167 EFST_LOCKON_LASER +1169 EFST_REF_T_POTION +1170 EFST_ADD_ATK_DAMAGE +1171 EFST_ADD_MATK_DAMAGE +1172 EFST_SERVANTWEAPON +1173 EFST_SERVANT_SIGN +1174 EFST_CHARGINGPIERCE +1175 EFST_CHARGINGPIERCE_COUNT +1176 EFST_DRAGONIC_AURA +1177 EFST_BIG_SCAR +1178 EFST_VIGOR +1182 EFST_CLIMAX_EARTH +1183 EFST_CLIMAX_BLOOM +1184 EFST_CLIMAX_CRYIMP +1190 EFST_HOLY_OIL +1191 EFST_CRYSTAL_IMPACT +1192 EFST_SHADOW_EXCEED +1193 EFST_DANCING_KNIFE +1194 EFST_POTENT_VENOM +1195 EFST_SHADOW_SCAR +1196 EFST_E_SLASH_COUNT +1197 EFST_MEDIALE +1198 EFST_A_VITA +1199 EFST_A_TELUM +1200 EFST_PRE_ACIES +1201 EFST_COMPETENTIA +1202 EFST_GUARD_STANCE +1203 EFST_ATTACK_STANCE +1204 EFST_GUARDIAN_S +1205 EFST_HANDICAPSTATE_DEEPBLIND +1206 EFST_HANDICAPSTATE_DEEPSILENCE +1207 EFST_HANDICAPSTATE_LASSITUDE +1208 EFST_HANDICAPSTATE_FROSTBITE +1209 EFST_HANDICAPSTATE_SWOONING +1210 EFST_HANDICAPSTATE_LIGHTNINGSTRIKE +1211 EFST_HANDICAPSTATE_CRYSTALLIZATION +1212 EFST_HANDICAPSTATE_CONFLAGRATION +1213 EFST_HANDICAPSTATE_MISFORTUNE +1214 EFST_HANDICAPSTATE_DEADLYPOISON +1215 EFST_HANDICAPSTATE_DEPRESSION +1216 EFST_HANDICAPSTATE_HOLYFLAME +1217 EFST_REBOUND_S +1218 EFST_SHIELD_MASTERY +1219 EFST_SPEAR_SWORD_M +1220 EFST_HOLY_S +1221 EFST_ULTIMATE_S +1222 EFST_SPEAR_SCAR +1223 EFST_SHIELD_POWER +1226 EFST_SHADOW_WEAPON +1227 EFST_RELIGIO +1228 EFST_BENEDICTUM +1230 EFST_FIRST_BRAND +1231 EFST_SECOND_BRAND +1232 EFST_SECOND_JUDGE +1233 EFST_THIRD_EXOR_FLAME +1234 EFST_FIRST_FAITH_POWER +1235 EFST_AXE_STOMP +1236 EFST_A_MACHINE +1237 EFST_D_MACHINE +1238 EFST_MT_M_MACHINE_OPERATOR +1239 EFST_TWOAXEDEF +1240 EFST_DAGGER_AND_BOW_M +1241 EFST_MAGIC_SWORD_M +1242 EFST_SHADOW_STRIP +1243 EFST_ABYSS_DAGGER +1244 EFST_ABYSSFORCEWEAPON +1245 EFST_ABYSS_SLAYER +1247 EFST_PROTECTSHADOWEQUIP +1248 EFST_RESEARCHREPORT +1249 EFST_BO_HELL_DUSTY +1250 EFST_WINDSIGN +1251 EFST_CRESCIVEBOLT +1252 EFST_CALAMITYGALE +1254 EFST_STAGE_MANNER +1255 EFST_RETROSPECTION +1256 EFST_MYSTIC_SYMPHONY +1257 EFST_KVASIR_SONATA +1258 EFST_SOUNDBLEND +1259 EFST_GEF_NOCTURN +1260 EFST_AIN_RHAPSODY +1261 EFST_MUSICAL_INTERLUDE +1262 EFST_JAWAII_SERENADE +1263 EFST_PRON_MARCH +1264 EFST_ROSEBLOSSOM +1266 EFST_ACIDIFIED_ZONE_WATER +1267 EFST_ACIDIFIED_ZONE_GROUND +1268 EFST_ACIDIFIED_ZONE_WIND +1269 EFST_ACIDIFIED_ZONE_FIRE +1270 EFST_MAGIC_BOOK_M +1271 EFST_SPELL_ENCHANTING +1272 EFST_SUMMON_ELEMENTAL_ARDOR +1273 EFST_SUMMON_ELEMENTAL_DILUVIO +1274 EFST_SUMMON_ELEMENTAL_PROCELLA +1275 EFST_SUMMON_ELEMENTAL_TERREMOTUS +1276 EFST_SUMMON_ELEMENTAL_SERPENS +1277 EFST_FLAMETECHNIC +1278 EFST_FLAMETECHNIC_OPTION +1279 EFST_FLAMEARMOR +1280 EFST_FLAMEARMOR_OPTION +1281 EFST_COLD_FORCE +1282 EFST_COLD_FORCE_OPTION +1283 EFST_CRYSTAL_ARMOR +1284 EFST_CRYSTAL_ARMOR_OPTION +1285 EFST_GRACE_BREEZE +1286 EFST_GRACE_BREEZE_OPTION +1287 EFST_EYES_OF_STORM +1288 EFST_EYES_OF_STORM_OPTION +1289 EFST_EARTH_CARE +1290 EFST_EARTH_CARE_OPTION +1291 EFST_STRONG_PROTECTION +1292 EFST_STRONG_PROTECTION_OPTION +1293 EFST_DEEP_POISONING +1294 EFST_DEEP_POISONING_OPTION +1295 EFST_POISON_SHIELD +1296 EFST_POISON_SHIELD_OPTION +1297 EFST_ABR_BATTLE_WARIOR +1298 EFST_ABR_DUAL_CANNON +1299 EFST_ABR_MOTHER_NET +1300 EFST_ABR_INFINITY +1301 EFST_ELEMENTAL_VEIL +1303 EFST_HOMUN_TIME +1310 EFST_POISON_MIST +1313 EFST_STONE_WALL +1315 EFST_OVERBRANDREADY +1316 EFST_SHIELDSPELL +1318 EFST_CLOUD_POISON +1319 EFST_SPORE_EXPLOSION_DEBUFF +1326 EFST_MASSIVE_F_BLASTER +1330 EFST_NOEQUIPWEAPON2 +1331 EFST_NOEQUIPARMOR2 +1332 EFST_NOEQUIPSHIELD2 +1333 EFST_NOEQUIPSHOES2 +1334 EFST_NOEQUIPPENDANT2 +1335 EFST_NOEQUIPEARING2 +1336 EFST_NOEQUIPFULL2 +1337 EFST_CURSE_R_CUBE +1338 EFST_CURSE_B_CUBE +1339 EFST_KILLING_AURA diff --git a/openkore_llm_knowledge/tables/elements.txt b/openkore_llm_knowledge/tables/elements.txt new file mode 100644 index 0000000000..04b67354bf --- /dev/null +++ b/openkore_llm_knowledge/tables/elements.txt @@ -0,0 +1,9 @@ +1#Ice# +2#Earth# +3#Fire# +4#Wind# +5#Poison# +6#Holy# +7#Dark# +8#Spirit# +9#Undead# diff --git a/openkore_llm_knowledge/tables/iRO/items.txt b/openkore_llm_knowledge/tables/iRO/items.txt new file mode 100644 index 0000000000..e2992791da --- /dev/null +++ b/openkore_llm_knowledge/tables/iRO/items.txt @@ -0,0 +1,11764 @@ +1#Child Admin Beta Egg# +500#Warnut's Chocolate# +501#Red Potion# +502#Orange Potion# +503#Yellow Potion# +504#White Potion# +505#Blue Potion# +506#Green Potion# +507#Red Herb# +508#Yellow Herb# +509#White Herb# +510#Blue Herb# +511#Green Herb# +512#Apple# +513#Banana# +514#Grape# +515#Carrot# +516#Potato# +517#Meat# +518#Honey# +519#Milk# +520#Hinalle Leaflet# +521#Aloe Leaflet# +522#Mastela Fruit# +523#Holy Water# +525#Panacea# +526#Royal Jelly# +528#Monster's Feed# +529#Candy# +530#Candy Cane# +531#Apple Juice# +532#Banana Juice# +533#Grape Juice# +534#Carrot Juice# +535#Pumpkin# +536#Ice Cream# +537#Pet Food# +538#Well-Baked Cookie# +539#Piece of Cake# +540#Falcon Food# +541#PecoPeco Food# +544#Raw Fish# +545#Condensed Red Potion# +546#Condensed Yellow Potion# +547#Condensed White Potion# +548#Cheese# +549#Yam# +550#Rice Cake# +551#Sushi# +552#Ketupat# +553#Bao# +554#Mochi# +555#Traditional Rice Cake# +556#Rice Cake Stick# +557#Neatly Sliced Rice Cake# +558#Chocolate# +559#Handmade Chocolate# +560#Handmade White Chocolate# +561#White Chocolate# +562#Double Crust Swiss Fondue# +563#Double Crust Swiss Fondue# +564#Rice Ball# +565#Vita 500# +566#Tom Yum Goong# +567#Shrimp# +568#Lemon# +569#Novice Potion# +570#Lucky Candy# +571#Lucky Candy Cane# +572#Lucky Cookie# +573#Chocolate Drink# +574#Egg# +575#2nd Anniversary Cake# +576#Prickly Fruit# +577#Grains# +578#Strawberry# +579#Fresh Fish# +580#Bread# +581#Edible Mushroom# +582#Orange# +583#Unknown Item# +584#Fish Cake Soup# +587#Red Prickly Fruit# +588#Unknown Item# +589#Piece of Pizza# +591#Caviar Pancake# +592#Jam Pancake# +593#Honey Pancake# +594#Sour Cream Pancake# +595#Mushroom Pancake# +596#Cute Chocolate Strawberry# +597#Lovely Chocolate Tart# +598#Light Red Potion# +599#Light Orange Potion# +601#Fly Wing# +602#Butterfly Wing# +603#Old Blue Box# +604#Dead Branch# +605#Anodyne# +606#Aloe Vera# +607#Yggdrasil Berry# +608#Yggdrasil Seed# +609#Amulet# +610#Yggdrasil Leaf# +611#Magnifier# +612#Mini Furnace# +613#Iron Hammer# +614#Golden Hammer# +615#Oridecon Hammer# +616#Old Card Album# +617#Old Purple Box# +618#Worn Out Scroll# +619#Unripe Apple# +620#Orange Juice# +621#Bitter Herb# +622#Rainbow Carrot# +623#Earthworm the Dude# +624#Rotten Fish# +625#Rusty Iron# +626#Monster Juice# +627#Sweet Milk# +628#Well-Dried Bone# +629#Singing Flower# +630#Dew-Laden Moss# +631#Deadly Noxious Herb# +632#Fatty Chubby Earthworm# +633#Baked Sweet Potato# +634#Tropical Banana# +635#Orc Trophy# +636#No Recipient# +637#Old Broom# +638#Silver Knife of Chastity# +639#Armlet of Obedience# +640#Shining Stone# +641#Contract in Shadow# +642#Book of the Devil# +643#Pet Incubator# +644#Gift Box# +645#Concentration Potion# +656#Awakening Potion# +657#Berserk Potion# +658#Union of Tribe# +659#Her Heart# +660#Forbidden Red Candle# +661#Soft Apron# +662#Authoritative Badge# +663#Korean Rice Cake# +664#Red Gift Box# +665#Yellow Gift Box# +666#Blue Gift Box# +667#Green Gift Box# +668#Red Envelope# +669#Tempting Rice-Cake Soup# +670#Bag of Gold Coins# +671#Gold Coin # +672#Bag of Bronze Coins# +673#UGC Token# +674#Mithril Coin# +675#Silver Coin# +676#Bag of Silver Coins# +677#Platinum Coin# +678#Poison Bottle# +679#Gold Pill# +680#Magic Carnation# +681#Honeymoon Memories# +682#Distilled Fighting Spirit# +683#Herb of Incantation# +684#Durian# +685#Ramadan# +686#Level 3 Earth Spike# +687#Level 5 Earth Spike# +688#Level 3 Cold Bolt# +689#Level 5 Cold Bolt# +690#Level 3 Fire Bolt# +691#Level 5 Fire Bolt# +692#Level 3 Lightning Bolt# +693#Level 5 Lightning Bolt# +694#Level 3 Soul Strike# +695#Level 5 Soul Strike# +696#Level 1 Fire Ball# +697#Level 5 Fire Ball# +698#Level 1 Fire Wall# +699#Level 5 Fire Wall# +700#Level 1 Frost Diver# +701#Ora Ora# +702#Animal Gore# +703#Hinalle# +704#Aloe# +705#Clover# +706#Four-Leaf Clover# +707#Singing Plant# +708#Ment# +709#Izidor# +710#Illusion Flower# +711#Shoot# +712#Flower# +713#Empty Bottle# +714#Emperium# +715#Yellow Gemstone# +716#Red Gemstone# +717#Blue Gemstone# +718#Garnet# +719#Amethyst# +720#Aquamarine# +721#Emerald# +722#Pearl# +723#Ruby# +724#Cursed Ruby# +725#Sardonyx# +726#Sapphire# +727#Opal# +728#Topaz# +729#Zircon# +730#1carat Diamond# +731#2carat Diamond# +732#3carat Diamond# +733#Cracked Diamond# +734#Red Frame# +735#Chung Jah# +736#China# +737#Black Ladle# +738#Pencil Case# +739#Rouge# +740#Plush Doll# +741#Poring Doll# +742#Chonchon Doll# +743#Spore Doll# +744#Bouquet# +745#Wedding Bouquet# +746#Glass Bead# +747#Crystal Mirror# +748#Witherless Rose# +749#Frozen Rose# +750#Baphomet Doll# +751#Osiris Doll# +752#Rocker Doll# +753#Yoyo Doll# +754#Smokie Doll# +756#Rough Oridecon# +757#Rough Elunium# +766#A bundle of shiny hair.# +790#[Event]Flower Left by Phantom# +791#[Event]Phantom's Seal# +901#Daenggie# +902#Tree Root# +903#Reptile Tongue# +904#Scorpion Tail# +905#Stem# +906#Pointed Scale# +907#Resin# +908#Spawn# +909#Jellopy# +910#Garlet# +911#Scell# +912#Zargon# +913#Tooth of Bat# +914#Fluff# +915#Chrysalis# +916#Bird Feather# +917#Talon# +918#Sticky Webfoot# +919#Animal Skin# +920#Wolf Claw# +921#Mushroom Spore# +922#Orc's Fang# +923#Baphomet's Horn# +924#Butterfly Powder# +925#Bird's Bill# +926#Snake Scale# +928#Insect Feelers# +929#Immortal Heart# +930#Rotten Bandage# +931#Orcish Voucher# +932#Skeleton Bone# +934#Memento# +935#Shell# +936#Scale Shell# +937#Venomous Canine# +938#Sticky Mucus# +939#Bee Stinger# +940#Grasshopper's Leg# +941#Nose Ring# +942#Monkey Tail# +943#Solid Shell# +944#Horseshoe# +945#Raccoon Leaf# +946#Snail's Shell# +947#Horn# +948#Bear's Footskin# +949#Feather# +950#Mermaid's Heart# +951#Fin# +952#Cactus Needle# +953#Stone Heart# +954#Shining Scale# +955#Insect Peeling# +956#Gill# +957#Decayed Nail# +958#Horrendous Mouth# +959#Stinky Scale# +960#Nipper# +961#Conch# +962#Tentacle# +963#Sharp Scale# +964#Crab Shell# +965#Clam Shell# +966#Clam Flesh# +967#Turtle Shell# +968#Heroic Emblem# +969#Gold# +970#Alcohol# +971#Detrimindexta# +972#Karvodailnirol# +973#Counteragent# +974#Mixture# +975#Scarlet Dyestuffs# +976#Lemon Dyestuffs# +978#Cobalt Blue Dyestuffs# +979#Green Dyestuffs# +980#Orange Dyestuffs# +981#Violet Dyestuffs# +982#White Dyestuffs# +983#Black Dyestuffs# +984#Oridecon# +985#Elunium# +986#Anvil# +987#Oridecon Anvil# +988#Golden Anvil# +989#Emperium Anvil# +990#Red Blood# +991#Crystal Blue# +992#Wind of Verdure# +993#Green Live# +994#Flame Heart# +995#Mystic Frozen# +996#Rough Wind# +997#Great Nature# +998#Iron# +999#Steel# +1000#Star Crumb# +1001#Star Dust# +1002#Iron Ore# +1003#Coal# +1004#Chivalry Emblem# +1005#Hammer of Blacksmith# +1006#Old Magic Book# +1007#Necklace of Wisdom# +1008#Necklace of Oblivion# +1009#Hand of God# +1010#Phracon# +1011#Emveretarcon# +1012#Frill# +1013#Rainbow Shell# +1014#Ant Jaw# +1015#Tongue# +1016#Rat Tail# +1017#Mole Whiskers# +1018#Mole Claw# +1019#Trunk# +1020#Black Hair# +1021#Dokebi Horn# +1022#Nine Tails# +1023#Fish Tail# +1024#Squid Ink# +1025#Cobweb# +1026#Acorn# +1027#Porcupine Quill# +1028#Mane# +1029#Tiger Skin# +1030#Tiger's Footskin# +1031#Mantis Scythe# +1032#Maneater Blossom# +1033#Maneater Root# +1034#Blue Hair# +1035#Dragon Canine# +1036#Dragon Scale# +1037#Dragon Tail# +1038#Little Evil Horn# +1039#Little Evil Wing# +1040#Elder Pixie's Moustache# +1041#Lantern# +1042#Bug Leg# +1043#Orc Claw# +1044#Zenorc's Fang# +1045#Cultish Mask# +1046#Scorpion Nipper# +1047#Dead Medusa# +1048#Horrendous Hair# +1049#Skirt of Virgin# +1050#Tendon# +1051#Detonator# +1052#Single Cell# +1053#Ancient Tooth# +1054#Ancient Lips# +1055#Earthworm Peeling# +1056#Grit# +1057#Moth Dust# +1058#Moth Wings# +1059#Fabric# +1060#Golden Hair# +1061#Witch Starsand# +1062#Jack o' Pumpkin# +1063#Fang# +1064#Reins# +1065#Trap# +1066#Fine-grained Trunk# +1067#Solid Trunk# +1068#Barren Trunk# +1069#Orange Net Mushroom# +1070#Orange Gooey Mushroom # +1071#Unknown Test Tube# +1072#Delivery Message# +1073#Voucher# +1074#Voucher# +1075#Voucher# +1076#Voucher# +1077#Voucher# +1078#Voucher# +1079#Voucher# +1080#Voucher# +1081#Delivery Box# +1082#Delivery Box # +1083#Delivery Box # +1084#Kafra Pass# +1085#Unknown Test Tube# +1086#Unknown Test Tube# +1087#Unknown Test Tube# +1088#Morocc Solution# +1089#Payon Solution# +1090#Unknown Test Tube# +1091#Delivery Box# +1092#Empty Test Tube# +1093#Empty Potion Bottle# +1094#Short Daenggie# +1095#Clock Hands# +1096#Round Shell# +1097#Worn-out Page# +1098#Manacles# +1099#Worn-out Prison Uniform# +1101#Sword# +1102#Sword# +1103#Sword# +1104#Falchion# +1105#Falchion# +1106#Falchion# +1107#Blade# +1108#Blade# +1109#Blade# +1110#Rapier# +1111#Rapier# +1112#Rapier# +1113#Scimitar# +1114#Scimitar# +1115#Scimitar# +1116#Katana# +1117#Katana# +1118#Katana# +1119#Tsurugi# +1120#Tsurugi# +1121#Tsurugi# +1122#Ring Pommel Saber# +1123#Haedonggum# +1124#Orcish Sword# +1125#Ring Pommel Saber# +1126#Saber# +1127#Saber# +1128#Haedonggum# +1129#Flamberge# +1130#Nagan# +1131#Ice Falchion# +1132#Edge# +1133#Fireblend# +1134#Caesar's Sword# +1135#Cutlus# +1136#Solar Sword# +1137#Excalibur# +1138#Mysteltainn# +1139#Tirfing# +1140#Byeollungum# +1141#Immaterial Sword# +1142#Jeweled Sword# +1143#Gaia Sword# +1144#Sashimi# +1145#Holy Avenger# +1146#Town Sword# +1147#Town Sword# +1148#Star Dust Blade# +1149#Flamberge# +1151#Slayer# +1152#Slayer# +1153#Slayer# +1154#Bastard Sword# +1155#Bastard Sword# +1156#Bastard Sword# +1157#Two-Handed Sword# +1158#Two-Handed Sword# +1159#Two-Handed Sword# +1160#Broadsword# +1161#Balmung# +1162#Broadsword# +1163#Claymore# +1164#Muramasa# +1165#Masamune# +1166#Dragon Slayer# +1167#Schweizersabel# +1168#Zweihander# +1169#Executioner# +1170#Katzbalger# +1171#Zweihander# +1172#Claymore# +1173#Rental Muramasa# +1174#Rental Executioner# +1175#Atroce's Weapon# +1176#Muscle Cutter# +1178#Schweizersabel# +1179#Executioner# +1180#Dragon Slayer# +1181#Tae Goo Lyeon# +1182#Blood Eater# +1183#Brave Katzbalger# +1184#Valorous Katzbalger# +1185#Violet Fear# +1186#Death Guidance# +1187#Glorious Claymore# +1188#Veteran Sword# +1189#Krasnaya# +1191#Alca Bringer# +1192#Eden Slayer I# +1193#Eden Slayer II# +1194#Rental Executioner# +1196#Two-Handed Chrome Metal Sword# +1197#Eden Slayer III# +1198#Garfish# +1201#Knife# +1202#Knife# +1203#Knife# +1204#Cutter# +1205#Cutter# +1206#Cutter# +1207#Main Gauche# +1208#Main Gauche# +1209#Main Gauche# +1210#Dirk# +1211#Dirk# +1212#Dirk# +1213#Dagger# +1214#Dagger# +1215#Dagger# +1216#Stiletto# +1217#Stiletto# +1218#Stiletto# +1219#Gladius# +1220#Gladius# +1221#Gladius# +1222#Damascus# +1223#Fortune Sword# +1224#Swordbreaker# +1225#Mailbreaker# +1226#Damascus# +1227#Weeder Knife# +1228#Combat Knife# +1229#Kitchen Knife# +1230#Ice Pick# +1231#Bazerald# +1232#Assassin Dagger# +1233#Exorciser# +1234#Moonlight Dagger# +1235#Azoth# +1236#Sucsamad# +1237#Grimtooth# +1238#Zeny Knife# +1239#Poison Knife# +1240#Princess Knife# +1241#Cursed Dagger# +1242#Counter Dagger# +1243#Novice Main Gauche# +1244#Holy Dagger# +1245#Cinquedea# +1246#Cinquedea# +1247#Kindle Dagger# +1248#Obsidian Dagger# +1249#Fisherman's Dagger# +1250#Jur# +1251#Jur# +1252#Katar# +1253#Katar# +1254#Jamadhar# +1255#Jamadhar# +1256#Katar of Frozen Icicle# +1257#Katar of Dusty Thornbush# +1258#Katar of Raging Blaze# +1259#Katar of Piercing Wind# +1260#Sharpened Ghoul Leg Bone# +1261#Infiltrator# +1262#Loki's Nail# +1263#Unholy Touch# +1264#Specialty Jur# +1265#Bloody Roar# +1266#Infiltrator# +1267#Infiltrator# +1268#Beast Claw# +1269#Inverse Scale# +1270#Drill Katar# +1271#Hemorrhage# +1273#Bloody Roar# +1274#Unholy Touch# +1275#Katar of Frozen Icicle# +1276#Katar of Dusty Thornbush# +1277#Katar of Raging Blaze# +1278#Katar of Piercing Wind# +1279#Brave Blood Tears# +1280#Valorous Blood Tears# +1281#Glorious Bloody Roar# +1282#Glorious Jamadhar# +1284#Krishna# +1285#Chakram# +1287#Durga# +1289#Eden Group Katar I# +1290#Agent Katar# +1291#Guillotine Katar# +1293#Bellum Jamadhar# +1294#Bellum Scale# +1295#Bloody Tears# +1296#Metal Katar# +1297#Inverse Scale# +1299#WoE TE Katar# +1301#Axe# +1302#Axe# +1303#Axe# +1304#Orcish Axe# +1305#Cleaver# +1306#War Axe# +1307#Windhawk# +1308#Golden Axe# +1309#Orcish Axe# +1310#Glorious Cleaver# +1311#Vecer Axe# +1313#Traveler's Axe# +1314#Rental Tomahawk# +1315#Right Epsilon# +1319#WoE TE Axe# +1322#Ru Blue Axe# +1323#Ru Gold Axe# +1326#Illusion War Axe# +1338#Duggan Hacker# +1351#Battle Axe# +1352#Battle Axe# +1353#Battle Axe# +1354#Hammer# +1355#Hammer# +1356#Hammer# +1357#Buster# +1358#Buster# +1359#Buster# +1360#Two-Handed Axe# +1361#Two-Handed Axe# +1362#Two-Handed Axe# +1363#Bloody Axe# +1364#Great Axe# +1365#Sabbath# +1366#Light Epsilon# +1367#Slaughter# +1368#Tomahawk# +1369#Guillotine# +1370#Doom Slayer# +1371#Doom Slayer# +1372#Rental Light Epsilon# +1373#(null)# +1374#Tomahawk# +1375#Berdysz# +1376#Heart Breaker# +1377#Hurricane Fury# +1379#Valorous Battle Axe# +1380#Brave Battle Axe# +1381#Novice Battle Axe# +1382#Glorious Two-Handed Axe# +1383#Celestial Axe# +1384#Veteran Axe# +1385#Bradium Stonehammer# +1387#Giant Axe# +1391#Eden Group Two Handed Axe I# +1392#Ygnus Stale# +1393#End Sektura# +1395#Bellum Buster# +1396#Bellum Guillotine# +1398#Metal Two Handed Axe# +1399#TE WoE TwoHand Axe# +1400#Vicious Mind Spear# +1401#Javelin# +1402#Javelin# +1403#Javelin# +1404#Spear# +1405#Spear# +1406#Spear# +1407#Pike# +1408#Pike# +1409#Pike# +1410#Lance# +1411#Lance# +1412#Lance# +1413#Gungnir# +1414#Gelerdria# +1415#Brocca# +1416#Tjungkuletti# +1417#Pole Axe# +1418#Gungnir# +1419#Rental Pole Axe# +1420#Long Horn# +1421#Battle Hook# +1422#Hunting Spear# +1425#Brave Spear# +1426#Glorious Spear# +1431#Rental Pole Axe# +1433#Imperial Spear# +1434#Eden Group Spear I# +1435#Cannon Spear# +1436#Bellum Spear# +1437#WoE TE Pike# +1438#Thanatos Spear# +1441#Ru Blue Spear# +1442#Ru Gold Spear# +1443#Crimson Spear# +1447#Poison Forged Spear# +1449#Gelerdria# +1450#Vicious Mind Lance# +1451#Guisarme# +1452#Guisarme# +1453#Guisarme# +1454#Glaive# +1455#Glaive# +1456#Glaive# +1457#Partizan# +1458#Partizan# +1459#Partizan# +1460#Trident# +1461#Trident# +1462#Trident# +1463#Halberd# +1464#Halberd# +1465#Halberd# +1466#Crescent Scythe# +1467#Bill Guisarme# +1468#Zephyrus# +1469#Longinus's Spear# +1470#Brionac# +1471#Hellfire# +1472#Soul Staff# +1473#Wizardry Staff# +1474#Gae Bolg# +1475#Equestrian's Spear# +1476#Crescent Scythe# +1477#Spectral Spear# +1478#Ahlspiess# +1479#Spectral Spear# +1480#Gae Bolg# +1481#Zephyrus# +1482#Valorous Lance# +1483#Ivory Lance# +1484#Cardo# +1485#Battle Fork# +1486#Glorious Lance# +1489#Marlin# +1490#Giant Lance# +1492#Bellum Glaive# +1493#Metal Lance# +1495#TE WoE Lance# +1496#Thanatos Long Spear# +1498#Crimson Lance# +1501#Club# +1502#Club# +1503#Club# +1504#Mace# +1505#Mace# +1506#Mace# +1507#Smasher# +1508#Smasher# +1509#Smasher# +1510#Flail# +1511#Flail# +1512#Flail# +1513#Morning Star# +1514#Morning Star# +1515#Morning Star# +1516#Sword Mace# +1517#Sword Mace# +1518#Sword Mace# +1519#Chain# +1520#Chain# +1521#Chain# +1522#Stunner# +1523#Spike# +1524#Golden Mace# +1525#Long Mace# +1526#Slash# +1527#Quadrille# +1528#Grand Cross# +1529#Iron Driver# +1530#Mjolnir# +1531#Wrench# +1532#Stunner# +1533#(null)# +1534#Wrench# +1535#Hollgrehenn's Hammer# +1538#Spike# +1539#Golden Mace# +1540#Grand Cross# +1541#Nemesis# +1542#Valorous Morning Star# +1543#Brave Morning Star# +1544#Lunakaligo# +1545#Novice Mace# +1546#Glorious Morning Star# +1548#Veteran Hammer# +1549#Pile Bunker# +1550#Book# +1551#Bible# +1552#Tablet# +1553#Book of Billows# +1554#Book of Mother Earth# +1555#Book of the Blazing Sun# +1556#Book of Gust of Wind# +1557#Book of the Apocalypse# +1558#Girl's Diary# +1559#Legacy of Dragon# +1560#Sage's Diary# +1561#Hardcover Book# +1562#Battlefield Textbook# +1563#Rental Sage's Diary# +1564#Encyclopedia# +1565#Ledger of Death# +1568#Book of Billows# +1569#Book of Mother Earth# +1570#Book of Blazing Sun# +1571#Book of Gust of Wind# +1572#Principles of Magic# +1573#Ancient Magic# +1574#Brave Strategy Book# +1575#Valorous Strategy Book# +1576#Glorious Tablet# +1577#Glorious Apocalypse# +1581#Diary Of Great Sage# +1583#Eden Group Dictionary I# +1584#Chilly Spell Book# +1586#Bellum Bible# +1587#Bellum Encyclopedia# +1588#Metal Book# +1591#WoE TE Book# +1593#Book of Destiny# +1596#Earth Pedigree Book# +1599#Angra Manyu# +1600#Vicious Mind Rod# +1601#Rod# +1602#Rod# +1603#Rod# +1604#Wand# +1605#Wand# +1606#Wand# +1607#Staff# +1608#Staff# +1609#Staff# +1610#Arc Wand# +1611#Arc Wand# +1612#Arc Wand# +1613#Mighty Staff# +1614#Wand of Occult# +1615#Evil Bone Wand# +1616#Wing Staff# +1617#Survivor's Rod (Dex)# +1618#Survivor's Rod (Dex)# +1619#Survivor's Rod (Int)# +1620#Survivor's Rod (Int)# +1621#Hypnotist's Staff# +1622#Hypnotist's Staff# +1623#Rental Mighty Staff# +1624#Lich's Bone Wand# +1625#Healing Staff# +1626#Piercing Staff# +1628#Rental Survivor's Rod# +1629#Gentleman Staff# +1630#Release of Wish# +1631#Holy Stick# +1632#Warlock's Magic Wand# +1633#Warlock's Battle Wand# +1634#Brave Recovery Wand# +1635#Valorous Recovery Wand# +1636#Thorn Staff of Darkness# +1637#Eraser# +1639#Novice Rod# +1640#Glorious Arc Wand# +1641#Glorious Cure Wand# +1643#Dead Tree Cane Staff# +1646#La'cryma Stick# +1647#Croce Staff# +1648#Staff Of Bordeaux# +1649#Laphine Staff# +1650#Eden Staff I# +1651#Eden Staff II# +1652#Traveler's Rod# +1654#Mental Stick# +1657#Wand of Affection# +1658#Eden Group Staff III# +1659#Recovery Light# +1660#Empowered Wand Of Affection# +1661#Mental Destroyer# +1664#Thorn Staff of Darkness# +1665#Piercing Staff# +1667#TE WoE Staff# +1668#Sword Stick# +1669#Thanatos Staff# +1670#RWC Memory Staff# +1671#Evil Slayer Vanquisher Staff# +1672#Safety Rod# +1676#Baculum Daemonicum# +1677#Ru Blue Wand# +1678#Ru Gold Wand# +1680#Crimson Rod# +1681#Shoot Foxtail Staff# +1683#Enriched Foxtail Staff# +1684#Long Foxtail Staff# +1685#Dragonfly Sitting Foxtail Staff# +1686#Large Foxtail Staff# +1687#Beginner Foxtail Staff# +1690#Marvelous Foxtail Staff# +1691#Wondrous Foxtail Staff# +1692#Magic Foxtail Staff# +1693#Magic Yellow Foxtail Staff# +1694#Foxtail Replica# +1695#Fine Foxtail Replica# +1696#Elaborate Foxtail Replica# +1697#Elaborate Yellow Foxtail Replica# +1699#Eden Foxtail I# +1701#Bow# +1702#Bow# +1703#Bow# +1704#Composite Bow# +1705#Composite Bow# +1706#Composite Bow# +1707#Great Bow# +1708#Great Bow# +1709#Great Bow# +1710#Cross Bow# +1711#Cross Bow# +1712#Cross Bow# +1713#Arbalest Bow# +1714#Gakkung Bow# +1715#Arbalest Bow# +1716#Gakkung Bow# +1718#Hunter Bow# +1719#Roguemaster's Bow# +1720#Rudra Bow# +1721#Repeating Crossbow# +1722#Ballista# +1723#Luna Bow# +1724#Dragon Wing# +1725#Minstrel Bow# +1726#Hunter Bow# +1727#Ballista# +1728#Rental Ballista# +1729#Rental Rudra Bow# +1730#Burning Bow# +1731#Frozen Bow# +1732#Earth Bow# +1733#Gust Bow# +1734#Orc Archer Bow# +1736#Double Bound# +1737#Ixion Wing# +1738#Valorous Crossbow# +1739#Brave Crossbow# +1740#Nepenthes Bow# +1741#Cursed Lyre# +1742#Novice Composite Bow# +1743#Glorious Hunter Bow# +1745#Falken Blitz# +1746#Elven Bow# +1747#Eden Bow I# +1748#Eden Bow II# +1749#Traveler's Bow# +1750#Arrow# +1751#Silver Arrow# +1752#Fire Arrow# +1753#Steel Arrow# +1754#Crystal Arrow# +1755#Wind Arrow# +1756#Stone Arrow# +1757#Immaterial Arrow# +1758#Stun Arrow# +1759#Freezing Arrow# +1760#Flash Arrow# +1761#Curse Arrow# +1762#Rusty Arrow# +1763#Poison Arrow# +1764#Sharp Arrow# +1765#Oridecon Arrow# +1766#Arrow of Counter Evil# +1767#Shadow Arrow# +1768#Sleep Arrow# +1769#Silence Arrow# +1770#Iron Arrow# +1771#Venom Knife# +1772#Holy Arrow# +1773#Elven Arrow# +1774#Hunting Arrow# +1775#WoE Arrow S# +1776#WoE Arrow A# +1800#Vicious Mind Knuckle# +1801#Waghnak# +1802#Waghnak# +1803#Knuckle Dusters# +1804#Knuckle Dusters# +1805#Studded Knuckles# +1806#Studded Knuckles# +1807#Fist# +1808#Fist# +1809#Claw# +1810#Claw# +1811#Finger# +1812#Finger# +1813#Kaiser Knuckle# +1814#Berserk# +1815#Hatii Claw# +1816#Berserk# +1817#Kaiser Knuckle# +1818#Magma Fist# +1819#Icicle Fist# +1820#Electric Fist# +1821#Seismic Fist# +1822#Combo Battle Glove# +1823#Valorous Fist# +1824#Brave Fist# +1825#Horn of Hillslion# +1826#Glorious Claw# +1827#Glorious Fist# +1830#Sura's Rampage# +1831#Eden Group Knuckle I# +1832#Bellum Claw# +1834#WoE TE Fist# +1835#Spartacus# +1836#Thanatos Knuckle# +1837#Iron Nail# +1839#Crimson Knuckle# +1846#Illusion Combo Battle Glove# +1847#Iron Nail# +1862#Burning Knuckle-OS# +1867#Safety Knuckle# +1900#Vicious Mind Violin# +1901#Violin# +1902#Violin# +1903#Mandolin# +1904#Mandolin# +1905#Lute# +1906#Lute# +1907#Guitar# +1908#Guitar# +1909#Harp# +1910#Harp# +1911#Gumoongoh# +1912#Gumoongoh# +1913#Electric Guitar# +1914#Burning Passion Guitar# +1915#Loner's Guitar# +1916#Green Acre Guitar# +1917#Gentle Breeze Guitar# +1918#Oriental Lute# +1919#Base Guitar# +1920#Berserk Guitar# +1922#Oriental Lute# +1923#Valorous Guitar# +1924#Brave Guitar# +1925#Cello# +1926#Harp of Nepenthes# +1927#Glorious Guitar# +1930#Green Whistle# +1931#Eden Group Guitar I# +1932#WoE TE Guitar# +1933#Thanatos Violin# +1934#Contabass# +1935#Ukulele Of Newoz# +1936#Ru Blue Violin# +1937#Ru Gold Violin# +1939#Crimson Violin# +1944#Hippie Guitar# +1950#Rope# +1951#Rope# +1952#Line# +1953#Line# +1954#Wire Whip# +1955#Wire Whip# +1956#Rante Whip# +1957#Rante Whip# +1958#Tail Whip# +1959#Tail Whip# +1960#Whip# +1961#Whip# +1962#Lariat Whip# +1963#Rapture Rose# +1964#Chemeti Whip# +1965#Red Flame Whip# +1966#Icicle Whip# +1967#Gaia Whip# +1968#Skipping Rope# +1969#Blade Whip# +1970#Queen's Whip# +1971#Electric Wire# +1972#Electric Eel# +1973#Sea Witch's Foot# +1974#Carrot Whip# +1976#Queen's Whip# +1977#Valorous Lariat# +1978#Brave Lariat# +1979#Stem of Nepenthes# +1980#Whip of Balance# +1981#Glorious Lariat# +1984#Stem Whip# +1985#Rosevine# +1986#Eden Group Whip I# +1987#WoE TE Rope# +1988#Thanatos Whip# +1989#Gymnastics Ribbon# +1990#Floral Mic Of Aigu# +1991#Ru Blue Whip# +1992#Ru Gold Whip# +1995#Crimson Wire# +1996#Vicious Mind Wire# +2000#Staff of Destruction# +2001#Divine Cross# +2002#Glorious Destruction Staff# +2004#Kronos# +2005#Dea Staff# +2006#Sunshine Staff# +2007#Golden Rod Staff# +2008#Aqua Staff# +2009#Crimson Staff# +2010#Forest Staff# +2011#Empowered Golden Rod Staff# +2012#Empowered Aqua Staff# +2013#Empowered Crimson Staff# +2014#Empowered Forest Staff# +2016#Bellum Arc Wand# +2018#Metal Staff# +2019#WoE TE Two-Handed Staff# +2020#Jormungand# +2021#Ganbantein# +2022#Staff Of Geffen# +2023#Thanatos Two-handed Staff# +2025#Crimson Staff# +2026#Vicious Mind Staff# +2027#Sunflower Boy# +2039#Illusion Wizardry Staff# +2048#Iron Staff# +2049#Staff of Blue Flame# +2058#Detecting Staff# +2101#Guard# +2102#Guard# +2103#Buckler# +2104#Buckler# +2105#Shield# +2106#Shield# +2107#Mirror Shield# +2108#Mirror Shield# +2109#Memory Book# +2110#Holy Guard# +2111#Sacred Mission# +2112#Novice Guard# +2113#Novice Shield# +2114#Stone Buckler# +2115#Valkyrja's Shield# +2116#Angelic Guard# +2117#Arm Guard# +2118#Arm Guard# +2119#Advanced Arm Guard# +2120#Advanced Arm Guard# +2121#Memory Book# +2122#Platinum Shield# +2123#Orleans's Server# +2124#Thorny Buckler# +2125#Strong Shield# +2126#Guyak Shield# +2127#Secular Mission# +2128#Sacred Mission# +2129#Exorcism Bible# +2130#Cross Shield# +2131#Magic Bible Vol1# +2132#Shelter Resistance# +2133#Tournament Shield# +2134#Shield of Naga# +2135#Shadow Guard# +2136#Cracked Buckler# +2137#Neo Valkyrja's Shield# +2138#Bradium Shield# +2139#Flame Thrower# +2140#Energy Rune Guard# +2141#Freya's Spiritual Shield# +2142#Freya's Spiritual Shield# +2143#Freya's Spiritual Shield# +2144#Freya's Spiritual Shield# +2146#Silver Guard# +2147#Round Buckler# +2148#Rosa Shield# +2149#Upgrade Guard# +2150#Upgrade Buckler# +2151#Upgrade Shield# +2153#Imperial Guard# +2156#Bible of Promise (1st Vol.)# +2158#Lamor Shield# +2160#Giant Shield# +2161#Geffenia Water Book# +2162#Bible of Promise (2nd Vol.)# +2164#Sombre Shield# +2165#Sol Shield# +2168#Immune Shield# +2169#Kalasag# +2170#Kalasag (Bayani)# +2171#Fox Armguard# +2172#Wolf Armguard# +2173#Crescent Armguard# +2174#Lumiere Shield# +2176#Dark Book# +2177#Shield Of Death# +2178#WoE TE Buckler# +2179#WoE TE Shield# +2180#WoE TE Magic Guard# +2181#Hervor# +2182#Hervor Alvitr# +2183#Advanced Angelic Guard# +2184#Bunker Shield# +2185#Magic Reflector# +2186#Revised Encyclopedia# +2187#Shield Of Gray# +2188#Svalinn# +2189#Mad Bunny# +2190#Ancient Shield Of Aeon# +2191#Solomon's Key# +2192#Rose of Eden# +2193#Fallen Angel Shield# +2195#Lian Shield# +2196#White Gold Shield# +2197#Iron Shield# +2198#Laphine Shield# +2199#Ahura Mazdah# +2201#Sunglasses# +2202#Sunglasses# +2203#Glasses# +2204#Glasses# +2205#Diver Goggles# +2206#Wedding Veil# +2207#Fancy Flower# +2208#Ribbon# +2209#Ribbon# +2210#Hair Band# +2211#Bandana# +2212#Eye Patch# +2213#Kitty Band# +2214#Bunny Band# +2215#Flower Band# +2216#Biretta# +2217#Biretta# +2218#Flu Mask# +2219#Flu Mask# +2220#Hat# +2221#Hat# +2222#Turban# +2223#Turban# +2224#Goggles# +2225#Goggles# +2226#Cap# +2227#Cap# +2228#Helm# +2229#Helm# +2230#Gemmed Sallet# +2231#Gemmed Sallet# +2232#Circlet# +2233#Circlet# +2234#Tiara# +2235#Crown# +2236#Santa Hat# +2239#Monocle# +2241#Grampa Beard# +2242#Purple Glasses# +2243#Geek Glasses# +2244#Big Ribbon# +2245#Sweet Gent# +2246#Golden Gear# +2247#Romantic Gent# +2248#Western Grace# +2249#Coronet# +2250#Cute Ribbon# +2251#Monk Hat# +2252#Wizard Hat# +2253#Sunflower# +2254#Angel Wing# +2255#Evil Wing# +2256#Magestic Goat# +2257#Unicorn Horn# +2258#Spiky Band# +2259#Mini Propeller# +2260#Mini Glasses# +2261#Army Cap# +2262#Clown Nose# +2263#Zorro Masque# +2264#Munak Hat# +2265#Gangster Mask# +2266#Iron Cain# +2267#Cigarette# +2268#Pipe# +2269#Romantic Flower# +2270#Romantic Leaf# +2271#Jack be Dandy# +2272#Stop Post# +2273#Doctor Band# +2274#Ghost Bandana# +2275#Red Bandana# +2276#Angled Glasses# +2277#Nurse Cap# +2278#Mr. Smile# +2279#Bomb Wick# +2280#Sakkat# +2281#Opera Masque# +2282#Halo# +2283#Ear Muffs# +2284#Antlers# +2285#Apple of Archer# +2286#Elven Ears# +2287#Pirate Bandana# +2288#Mr. Scream# +2289#Poo Poo Hat# +2290#Funeral Hat# +2291#Masquerade# +2292#Welding Mask# +2293#Pretend Murdered# +2294#Stellar# +2295#Blinker# +2296#Binoculars# +2297#Goblin Mask# +2298#Green Feeler# +2299#Orc Helm# +2301#Cotton Shirt# +2302#Cotton Shirt# +2303#Jacket# +2304#Jacket# +2305#Adventurer's Suit# +2306#Adventurer's Suit# +2307#Mantle# +2308#Mantle# +2309#Coat# +2310#Coat# +2311#Mink Coat# +2312#Padded Armor# +2313#Padded Armor# +2314#Chain Mail# +2315#Chain Mail# +2316#Full Plate# +2317#Full Plate# +2318#Lord's Clothes# +2319#Glittering Jacket# +2320#Formal Suit# +2321#Silk Robe# +2322#Silk Robe# +2323#Scapulare# +2324#Scapulare# +2325#Saint's Robe# +2326#Saint's Robe# +2327#Holy Robe# +2328#Wooden Mail# +2329#Wooden Mail# +2330#Tights# +2331#Tights# +2332#Silver Robe# +2333#Silver Robe# +2334#Mage Coat# +2335#Thief Clothes# +2336#Thief Clothes# +2337#Ninja Suit# +2338#Wedding Dress# +2339#Pantie# +2340#Novice Breastplate# +2341#Legion Plate Armor# +2342#Legion Plate Armor# +2343#Robe of Cast# +2344#Lucius's Fierce Armor of Volcano# +2345#Lucius's Fierce Armor of Volcano# +2346#Saphien's Armor of Ocean# +2347#Saphien's Armor of Ocean# +2348#Aebecee's Raging Typhoon Armor# +2349#Aebecee's Raging Typhoon Armor# +2350#Claytos Cracking Earth Armor# +2351#Claytos Cracking Earth Armor# +2352#Tattered Novice Ninja Suit# +2353#Odin's Blessing# +2354#Goibne's Armor# +2355#Angelic Protection# +2356#Blessed Holy Robe# +2357#Valkyrian Armor# +2358#Angel's Dress# +2359#Ninja Suit# +2360#Robe of Cast# +2364#Meteo Plate Armor# +2365#Orleans's Gown# +2366#Divine Cloth# +2367#Sniping Suit# +2369#Freyja Overcoat# +2370#Used Mage Coat# +2371#Pantie# +2372#Mage Coat# +2373#Holy Robe# +2374#Diabolus Robe# +2375#Diabolus Armor# +2376#Assaulter Plate# +2377#Elite Engineer Armor# +2378#Assassin Robe# +2379#Warlock's Battle Robe# +2380#Medic's Robe# +2381#Elite Archer Suit# +2382#Elite Shooter Suit# +2383#Brynhild# +2384#Spritual Tunic# +2385#Recuperative Armor# +2386#Chameleon Armor# +2387#Sprint Mail# +2388#Kandura# +2389#Armor of Naga# +2390#Improved Tights# +2391#Life Link# +2393#Novice Adventurer's Suit# +2394#Glorious Suit# +2395#Glorious Popularized Suit# +2396#Glorious Mass-Production Suit# +2399#Dragon Vest# +2401#Sandals# +2402#Sandals# +2403#Shoes# +2404#Shoes# +2405#Boots# +2406#Boots# +2407#Crystal Pumps# +2408#Shackles# +2409#High Heels# +2410#Sleipnir# +2411#Greaves# +2412#Greaves# +2413#Safety Boots# +2414#Novice Slippers# +2415#Bunny Slipper# +2416#Novice Shoes# +2417#Fricco's Shoes# +2418#Vidar's Boots# +2419#Goibne's Greaves# +2420#Angel's Reincarnation# +2421#Valkyrian Shoes# +2422#High Fashion Sandals# +2423#Variant Shoes# +2424#Tidal Shoes# +2425#Black Leather Boots# +2426#Shadow Walk# +2428#Freyja Boots# +2429#Iron Boots# +2430#Iron Boots# +2431#Valley Shoes# +2432#Highheels# +2433#Diabolus Boots# +2434#Black Leather Boots# +2435#Battle Greave# +2436#Combat Boots# +2437#Battle Boots# +2438#Paw Of Cat# +2439#Refresh Shoes# +2440#Sprint Shoes# +2441#Beach Sandals# +2443#Fisher's Boots# +2444#Glorious Shoes# +2445#Glorious Popularized Shoes# +2446#Glorious Mass-Production Shoes# +2450#Vital Tree Shoes# +2451#Freya's Spiritual Sandals# +2452#Freya's Spiritual Sandals# +2453#Freya's Spiritual Sandals# +2454#Freya's Spiritual Sandals# +2456#Eden Group Boots I# +2457#Eden Group Boots II# +2458#Eden Group Boots III# +2459#Upgrade Shoes# +2460#Upgrade Boots# +2461#Upgrade Greave# +2462#Ephemeral Sleipnir# +2463#Feral Boots# +2464#NoFear Shoes# +2465#Dance Shoes# +2467#Golden Rod Shoes# +2468#Aqua Shoes# +2469#Crimson Shoes# +2470#Forest Shoes# +2471#Shoes Of Affection# +2472#Shoes of Judgement# +2473#Eden Group Boots IV# +2475#Ur's Greaves# +2476#Peuz's Greaves# +2477#Sapha Shoes# +2478#Nab Shoes# +2479#White Wing Boots# +2480#Black Wing Boots# +2481#Rune Boots# +2483#WoE Greave# +2484#WoE Boots# +2485#WoE Shoes# +2486#Shadow Walk# +2491#Bangungot Boots of Nightmare# +2492#Bangungot Boots of Nightmare (Bayani)# +2495#Egir Shoes# +2496#WoE TE Shoes# +2497#WoE TE Boots# +2498#WoE TE Magic Sandals# +2499#Temporal Boots# +2501#Hood# +2502#Hood# +2503#Muffler# +2504#Muffler# +2505#Manteau# +2506#Manteau# +2507#Ancient Cape# +2508#Ragamuffin Manteau# +2509#Survivor's Manteau# +2510#Somber Novice Hood# +2511#Skeleton Manteau# +2512#Novice Manteau# +2513#Heavenly Maiden Robe# +2514#Pauldron# +2515#Eagle Wing# +2516#Falcon Muffler# +2517#Vali's Manteau# +2518#Morpheus's Shawl# +2519#Morrigane's Manteau# +2520#Goibne's Spaulders# +2521#Angelic Cardigan# +2522#Undershirt# +2523#Undershirt# +2524#Valkyrian Manteau# +2525#Ancient Cape# +2527#Dragon Breath# +2528#Wool Scarf# +2529#Rider Insignia# +2530#Rider Insignia# +2531#Ulfhedinn# +2532#Mithril Magic Cape# +2533#Freyja Cape# +2534#Ruffler# +2535#Cloak Of Survival# +2536#Skin of Ventus# +2537#Diabolus Manteau# +2538#Captain's Manteau# +2539#Commander's Manteau# +2540#Sheriff's Manteau# +2541#Asprika# +2542#Flame Manteau of Naght Sieger# +2543#Sylphid Manteau# +2544#Leather of Tendrilion# +2545#Musika# +2546#Beach Manteau# +2548#Neo Muffler# +2549#Glorious Muffler# +2550#Fisher's Muffler# +2553#Dragon Manteau# +2554#Nidhoggur's Shadow Garb# +2555#Freya's Spiritual Scarf# +2556#Freya's Spiritual Scarf# +2557#Freya's Spiritual Scarf# +2558#Freya's Spiritual Scarf# +2560#Eden Group Manteau# +2561#Upgrade Hood# +2562#Upgrade Muffler# +2563#Upgrade Manteau# +2564#Feral Tail# +2565#Beach Towel# +2566#Half Asprika# +2568#Loki's Muffler# +2569#Shawl Of Affection# +2570#Shawl of Judgement# +2571#Eden Group Manteau II# +2573#Archangel Wings# +2574#Ur's Manteau# +2575#Peuz's Manteau# +2576#Heroic Backpack# +2577#Sapha Hood# +2578#Nab Hood# +2579#Magic Stole# +2580#White Wing Manteau# +2581#Black Wing Manteau# +2582#Salvage Cape# +2586#WoE Manteau# +2587#WoE Muffler# +2589#Fallen Angel Wings# +2590#Buwaya Sack Cloth# +2591#Buwaya Sack Cloth(Bayani)# +2593#Flower Garment# +2596#Sharel Manteau# +2598#Lamor Manteau# +2601#Ring# +2602#Earring# +2603#Necklace# +2604#Glove# +2605#Brooch# +2607#Clip# +2608#Rosary# +2609#Skull Ring# +2610#Gold Ring# +2611#Silver Ring# +2612#Flower Ring# +2613#Diamond Ring# +2614#Eye of Dullahan# +2615#Safety Ring# +2616#Critical Ring# +2617#Celebrant's Mitten# +2618#Matyr's Leash# +2619#Bow Thimble# +2620#Rogue's Treasure# +2621#Ring# +2622#Earring# +2623#Necklace# +2624#Glove# +2625#Brooch# +2626#Rosary# +2627#Belt# +2628#Novice Armlet# +2629#Megingjard# +2630#Brisingamen# +2631#Celebration Ring# +2634#Wedding Ring# +2635#Wedding Ring# +2636#Gold Christmas Ring# +2637#Silver Christmas Ring# +2638#Sacred Incense# +2639#Occult Incense# +2640#Kafra Ring# +2641#Fashion Hip Sack# +2642#Serin's Gold Ring# +2643#Serin's Gold Ring # +2644#The Sign# +2645#Moonlight Ring# +2646#Bunch of Carnation# +2647#Nile Rose# +2648#Morpheus's Ring# +2649#Morpheus's Bracelet# +2650#Morrigane's Belt# +2651#Morrigane's Pendant# +2652#Goddess of Fortune's Cursed Brooch# +2653#Sacrifice Ring# +2654#Shinobi Sash# +2655#Bloodied Shackle Ball# +2656#Armor Charm# +2657#Laboratory Permit# +2658#Nile Rose# +2659#Vesper Core 01# +2660#Vesper Core 02# +2661#Vesper Core 03# +2662#Vesper Core 04# +2663#Gauntlet of Accuracy# +2664#Scarf Belt# +2665#Exorcising Ring# +2666#Lantern of Hope# +2667#Renown Archer's Gloves# +2671#Bow Thimble# +2677#Spiritual Ring# +2678#Ring of Flame Lord# +2679#Ring of Resonance# +2680#Lesser Elemental Ring# +2682#Ring of Water# +2683#Ring of Fire# +2684#Ring of Wind# +2685#Ring of Earth# +2686#Rental Elven Ears# +2687#Rental Steel Flower# +2688#Rental Critical Ring# +2689#Rental Earring# +2690#Rental Ring# +2691#Rental Necklace# +2692#Rental Glove# +2693#Rental Brooch# +2694#Rental Rosary# +2695#Rental Safety Ring# +2696#Rental Vesper Core 01# +2697#Rental Vesper Core 02# +2698#Rental Vesper Core 03# +2699#Rental Vesper Core 04# +2700#Red Silk Seal# +2701#Orleans's Glove# +2702#Bison Horn# +2703#Expert Ring# +2707#GUSLI# +2708#Chinese Handicraft# +2709#5th Anniversary Coin# +2710#Bloody Iron Ball# +2711#Spiritual Ring# +2712#Ragnarok Limited Edition# +2713#Certificate# +2714#Marvelous Pendant# +2715#Skull Ring# +2716#Librarian Glove# +2717#Pocket Watch# +2718#Lunatic Brooch# +2719#Iron Wrist# +2720#Medal of Honor (Swordman)# +2721#Medal of Honor (Thief)# +2722#Medal of Honor (Acolyte)# +2723#Medal of Honor (Mage)# +2724#Medal of Honor (Archer)# +2725#Medal of Honor (Merchant)# +2726#Icarus Wings# +2727#Bowman Scarf# +2728#Cursed Hand# +2729#Diabolus Ring# +2730#Seal of Continental Guard# +2731#Rune Spellstone# +2732#Death Loop# +2733#Medal of Honor (Gunslinger)# +2734#Directive# +2735#Directive# +2736#Navel Ring# +2737#Foot Ring# +2738#Shiny Coin# +2739#Ordinary Coin# +2740#Rusty Coin# +2741#All In One Ring# +2742#Lucky Clip# +2743#Angelic Ring# +2744#Sprint Ring# +2745#Pinguicula Corsage# +2746#Cold Heart# +2747#Black Cat# +2748#Cursed Star# +2749#Linen Glove# +2751#Academy Badge# +2753#Rental Neutral Ring# +2754#Rental Hallow Ring# +2755#Rental Clamorous Ring# +2756#Rental Chemical Ring# +2757#Rental Insecticide Ring# +2758#Rental Fisher Ring# +2759#Rental Decussate Ring# +2760#Rental Bloody Ring# +2761#Rental Satanic Ring# +2762#Rental Dragon Ring# +2763#Rental Neo Skull Ring# +2764#Small Fishing Rod# +2765#Novice Figure# +2766#Swordman Figure# +2767#Acolyte Figure# +2768#Mage Figure# +2769#Archer Figure# +2770#Thief Figure# +2771#Merchant Figure# +2772#Glorious Ring# +2773#Glorious Popularized Ring# +2774#Glorious Mass-Production Ring# +2775#Lure# +2776#Adventurer's Trusty Towel# +2777#Shaman Ring# +2778#Shaman Earrings# +2779#Dark Knight Belt (Type A)# +2780#Dark Knight Glove (Type A)# +2781#Aumdura's Benefit# +2782#Ring of the Ancient Wise King# +2783#Eye Stone Ring# +2784#Christmas Musicbox# +2787#Waterdrop Brooch# +2788#Bradium Earring# +2789#Bradium Ring# +2790#Bradium Brooch# +2794#Magic Stone Ring# +2795#Green Apple Ring# +2796#Magical Stone (Splendide)# +2797#Magical Stone (Manuk)# +2798#Will of Exhausted Warrior# +2800#Accelerator# +2801#Hovering Booster# +2802#Suicidal Device# +2803#Shape Shifter# +2804#Cooling Device# +2805#Magnetic Field Generator# +2806#Barrier Builder# +2807#Repair Kit# +2808#Camouflage Generator# +2809#High Quality Cooler# +2810#Special Cooler# +2811#Freya's Spiritual Bracelet# +2812#Freya's Spiritual Bracelet# +2813#Freya's Spiritual Bracelet# +2814#Freya's Spiritual Bracelet# +2819#Swordman Manual# +2820#Thief Manual# +2821#Acolyte Manual# +2822#Archer Manual# +2823#Merchant Manual# +2824#Mage Manual# +2825#Shaman Earrings# +2826#Dark Knight Belt (Type B)# +2827#Dark Knight Glove (Type B)# +2828#Upgrade Clip# +2829#Greed Clip# +2830#Ephemeral Megingjard# +2831#Ephemeral Brisingamen# +2833#Odin's Recall# +2834#All In One Ring# +2835#Rental Critical Ring# +2836#Rental Glove# +2837#Rental Safety Ring# +2838#Rental Necklace# +2839#Rental Ring# +2840#Rental Rosary# +2843#Golden Bell# +2844#Light of El Dicastes# +2845#NoFear Belt# +2853#Telekinetic Orb# +2854#Alchemy Glove# +2855#Whikebain's Black Tail# +2856#Half Megin# +2857#Half Brysing# +2858#Pendant of Guardian# +2859#Golden Rod Orb# +2860#Aqua Orb# +2861#Crimson Orb# +2862#Forest Orb# +2864#Light of Cure# +2865#Seal of Cathedral# +2866#Ring of Archbishop# +2872#G Honor Certificate# +2873#Cat Hand Glove# +2874#Buffalo Horn# +2881#Orleans's Necklace# +2883#Ur's Seal# +2884#Peuz's Seal# +2886#Sapha Ring# +2887#Nab Ring# +2890#White Wing Brooch# +2891#Black Wing Brooch# +2892#Assassin Handcuffs# +2894#Glove of Shura# +2898#Black Rosary# +2899#Sound Amplifier# +2904#Naqsi# +2907#Buwaya Agimat Tattoo# +2910#Bakonawa Agimat Tattoo# +2911#Bangungot Agimat Tattoo# +2913#Thief Handcuffs# +2914#Left Eye Of An Wizard# +2915#Ettlang Keepsake# +2916#Fairy Wing# +2917#Str Glove# +2918#Int Glove# +2919#Agi Glove# +2920#Vit Glove# +2921#Dex Glove# +2922#Luk Glove# +2923#Str Glove # +2924#Int Glove # +2925#Agi Glove # +2926#Vit Glove # +2927#Dex Glove # +2928#Luk Glove # +2935#Sprint Glove# +2936#Recovery Ring# +2940#Ninja Manual# +2941#Gunslinger Manual# +2942#Taekwon Manual# +2944#TE Protection Ring# +2945#TE Rage Ring# +2946#TE Defiance Ring# +2948#Demons Wing# +2949#Silversmith Bracelet# +2950#Rune Ring# +2951#Kvasir Ring (Blue)# +2952#Kvasir Ring (Red)# +2953#Kvasir Ring (Green)# +2954#Kvasir Ring (Brown)# +2956#Advanced Safety Ring# +2957#Advanced Ring Of Flame Lord# +2958#Advanced Ring Of Resonance# +2959#Fidelity Necklace# +2963#Physical Enhancer Ring# +2964#Magic Intensifier Ring# +2966#RWC 2012 Ring# +2967#Chambered RWC 2012 Ring# +2968#RWC 2012 Pendant# +2969#Chambered RWC 2012 Pendant# +2971#Pocket Watch# +2976#Red Lantern# +2977#Hurt Mind# +2978#Kind Heart# +2979#Strawberry Decoration# +2980#Evil Spirit Glove# +2981#Hero Ring# +2984#Saehrimnir Gloves# +2985#Gyges's Ring# +2988#Hero Ring Of Newoz# +2989#Floral Bracelet Of Aigu# +2990#Pendant of Harmony# +2991#Pendant of Chaos# +2992#Pendant of Maelstorm# +2994#Water Crystal Stone# +2995#Supplement Part Dex# +2996#Upgrade Part - Gun Barrel# +3629#Brisingamen Token# +3630#Sleipnir Token# +3633#Mjolnir Token# +3634#Megingjard Token# +3737#Brownie Point# +4001#Poring Card# +4002#Fabre Card# +4003#Pupa Card# +4004#Drops Card# +4005#Santa Poring Card# +4006#Lunatic Card# +4007#Pecopeco Egg Card# +4008#Picky Card# +4009#Chonchon Card# +4010#Willow Card# +4011#Picky Egg Card# +4012#Thief Bug Egg Card# +4013#Andre Egg Card# +4014#Roda Frog Card# +4015#Condor Card# +4016#Thief Bug Card# +4017#Savage Bebe Card# +4018#Andre Larva Card# +4019#Hornet Card# +4020#Familiar Card# +4021#Rocker Card# +4022#Spore Card# +4023#Baby Desert Wolf Card# +4024#Plankton Card# +4025#Skeleton Card# +4026#Female Thief Bug Card# +4027#Kukre Card# +4028#Tarou Card# +4029#Wolf Card# +4030#Mandragora Card# +4031#Pecopeco Card# +4032#Ambernite Card# +4033#Poporing Card# +4034#Wormtail Card# +4035#Hydra Card# +4036#Muka Card# +4037#Boa Card# +4038#Zombie Card# +4039#Stainer Card# +4040#Creamy Card# +4041#Coco Card# +4042#Steel Chonchon Card# +4043#Andre Card# +4044#Smokie Card# +4045#Horn Card# +4046#Martin Card# +4047#Ghostring Card# +4048#Poison Spore Card# +4049#Vadon Card# +4050#Male Thief Bug Card# +4051#Yoyo Card# +4052#Elder Willow Card# +4053#Vitata Card# +4054#Angeling Card# +4055#Marina Card# +4056#Dustiness Card# +4057#Metaller Card# +4058#Thara Frog Card# +4059#Soldier Andre Card# +4060#Goblin Card# +4061#Cornutus Card# +4062#Anacondaq Card# +4063#Caramel Card# +4064#Zerom Card# +4065#Kaho Card# +4066#Orc Warrior Card# +4067#Megalodon Card# +4068#Scorpion Card# +4069#Drainliar Card# +4070#Eggyra Card# +4071#Orc Zombie Card# +4072#Golem Card# +4073#Pirate Skeleton Card# +4074#Bigfoot Card# +4075#Argos Card# +4076#Magnolia Card# +4077#Phen Card# +4078#Savage Card# +4079#Mantis Card# +4080#Flora Card# +4081#Hode Card# +4082#Desert Wolf Card# +4083#Rafflesia Card# +4084#Marine Sphere Card# +4085#Orc Skeleton Card# +4086#Soldier Skeleton Card# +4087#Giearth Card# +4088#Frilldora Card# +4089#Swordfish Card# +4090#Munak Card# +4091#Kobold Card# +4092#Skeleton Worker Card# +4093#Obeaune Card# +4094#Archer Skeleton Card# +4095#Marse Card# +4096#Zenorc Card# +4097#Matyr Card# +4098#Dokebi Card# +4099#Pasana Card# +4100#Sohee Card# +4101#Sandman Card# +4102#Whisper Card# +4103#Horong Card# +4104#Requiem Card# +4105#Marc Card# +4106#Mummy Card# +4107#Verit Card# +4108#Myst Card# +4109#Jakk Card# +4110#Ghoul Card# +4111#Strouf Card# +4112#Marduk Card# +4113#Marionette Card# +4114#Argiope Card# +4115#Hunter Fly Card# +4116#Isis Card# +4117#Sidewinder Card# +4118#Ground Petite Card# +4119#Bathory Card# +4120#Sky Petite Card# +4121#Phreeoni Card# +4122#Deviruchi Card# +4123#Eddga Card# +4124#Medusa Card# +4125#Deviace Card# +4126#Minorous Card# +4127#Nightmare Card# +4128#Golden Thief Bug Card# +4129#Baphomet Jr. Card# +4130#Scorpion King Card# +4131#Moonlight Flower Card# +4132#Mistress Card# +4133#Raydric Card# +4134#Dracula Card# +4135#Orc Lord Card# +4136#Khalitzburg Card# +4137#Drake Card# +4138#Anubis Card# +4139#Joker Card# +4140#Abysmal Knight Card# +4141#Evil Druid Card# +4142#Doppelganger Card# +4143#Orc Hero Card# +4144#Osiris Card# +4145#Beelzebub Card# +4146#Maya Card# +4147#Baphomet Card# +4148#Pharaoh Card# +4149#Gargoyle Card# +4150#Goat Card# +4151#Gajomart Card# +4152#Galapago Card# +4153#Crab Card# +4154#Dumpling Kid Card# +4155#Goblin Leader Card# +4156#Goblin Steamrider Card# +4157#Goblin Archer Card# +4158#Sky Deleter Card# +4159#Nine Tail Card# +4160#Firelock Soldier Card# +4161#Grand Peco Card# +4162#Grizzly Card# +4163#Gryphon Card# +4164#Gullinbursti Card# +4165#Gig Card# +4166#Nightmare Terror Card# +4167#Nereid Card# +4168#Dark Lord Card# +4169#Dark Illusion Card# +4170#Dark Frame Card# +4171#Dark Priest Card# +4172#The Paper Card# +4173#Demon Fungus Card# +4174#Deviling Card# +4175#Poisonous Toad Card# +4176#Dullahan Card# +4177#Dryad Card# +4178#Dragon Tail Card# +4179#Dragon Fly Card# +4180#Driller Card# +4181#Disguise Card# +4182#Diabolic Card# +4183#Vagabond Wolf Card# +4184#Lava Golem Card# +4185#Rideword Card# +4186#Raggler Card# +4187#Raydric Archer Card# +4188#Leib Olmai Card# +4189#Wraith Dead Card# +4190#Wraith Card# +4191#Loli Ruri Card# +4192#Rotar Zairo Card# +4193#Lude Card# +4194#Rybio Card# +4195#Leaf Cat Card# +4196#Marin Card# +4197#Mastering Card# +4198#Maya Purple Card# +4199#Merman Card# +4200#Megalith Card# +4201#Majoruros Card# +4202#Mao Guai Card# +4203#Mutant Dragonoid Card# +4204#Mini Demon Card# +4205#Mimic Card# +4206#Myst Case Card# +4207#Mysteltainn Card# +4208#Miyabi Doll Card# +4209#Violy Card# +4210#Wanderer Card# +4211#Vocal Card# +4212#Bongun Card# +4213#Brilight Card# +4214#Bloody Murderer Card# +4215#Blazzer Card# +4216#Sasquatch Card# +4217#Enchanted Peach Tree Card# +4218#Succubus Card# +4219#Sage Worm Card# +4220#Solider Card# +4221#Skeleton General Card# +4222#Skel Prisoner Card# +4223#Stalactic Golem Card# +4224#Stem Worm Card# +4225#Stone Shooter Card# +4226#Sting Card# +4227#Spring Rabbit Card# +4228#Sleeper Card# +4229#Tower Keeper Card# +4230#Shinobi Card# +4231#Mi Gao Card# +4232#Hermit Plant Card# +4233#Baby Leopard Card# +4234#Anolian Card# +4235#Christmas Cookie Card# +4236#Amon Ra Card# +4237#Owl Duke Card# +4238#Owl Baron Card# +4239#Iron Fist Card# +4240#Arclouze Card# +4241#Arc Angeling Card# +4242#Apocalipse Card# +4243#Antonio Card# +4244#Alarm Card# +4245#Am Mut Card# +4246#Assaulter Card# +4247#Aster Card# +4248#Ancient Mummy Card# +4249#Ancient Worm Card# +4250#Executioner Card# +4251#Elder Card# +4252#Alligator Card# +4253#Alice Card# +4254#Ogretooth Card# +4255#Orc Lady Card# +4256#Orc Archer Card# +4257#Wild Rose Card# +4258#Evil Nymph Card# +4259#Wooden Golem Card# +4260#Wootan Shooter Card# +4261#Wootan Fighter Card# +4262#Cloud Hermit Card# +4263#Samurai Specter Card# +4264#Wind Ghost Card# +4265#Jing Guai Card# +4266#Eclipse Card# +4267#Explosion Card# +4268#Injustice Card# +4269#Incubus Card# +4270#Giant Spider Card# +4271#Giant Hornet Card# +4272#Zhu Po Long Card# +4273#Shell Fish Card# +4274#Zombie Master Card# +4275#Zombie Prisoner Card# +4276#Lord of the Dead Card# +4277#Zealotus Card# +4278#Gibbet Card# +4279#Earth Deleter Card# +4280#Geographer Card# +4281#Zipper Bear Card# +4282#Tengu Card# +4283#Greatest General Card# +4284#Chepet Card# +4285#Choco Card# +4286#Karakasa Card# +4287#Kapha Card# +4288#Carat Card# +4289#Caterpillar Card# +4290#Cat O' Nine Tails Card# +4291#Kobold Leader Card# +4292#Kobold Archer Card# +4293#Cookie Card# +4294#Quve Card# +4295#Kraben Card# +4296#Cramp Card# +4297#Cruiser Card# +4298#Creamy Fear Card# +4299#Clock Card# +4300#Chimera Card# +4301#Killer Mantis Card# +4302#Tao Gunka Card# +4303#Giant Whisper Card# +4304#Tamruan Card# +4305#Turtle General Card# +4306#Toad Card# +4307#Beetle King Card# +4308#Tri Joint Card# +4309#Parasite Card# +4310#Panzer Goblin Card# +4311#Permeter Card# +4312#Fur Seal Card# +4313#Punk Card# +4314#Penomena Card# +4315#Pest Card# +4316#False Angel Card# +4317#Mobster Card# +4318#Stormy Knight Card# +4319#Freezer Card# +4320#Bloody Knight Card# +4321#Hylozoist Card# +4322#High Orc Card# +4323#Hatii Bebe Card# +4324#Hatii Card# +4325#Harpy Card# +4326#Sea-Otter Card# +4327#Bloody Butterfly Card# +4328#Yao Jun Card# +4329#Phendark Card# +4330#Evil Snake Lord Card# +4331#Heater Card# +4332#Waste Stove Card# +4333#Venomous Card# +4334#Noxious Card# +4335#Pitman Card# +4336#Ungoliant Card# +4337#Porcellio Card# +4338#Obsidian Card# +4339#Mineral Card# +4340#Teddy Bear Card# +4341#Metaling Card# +4342#RSX-0806 Card# +4343#Holden Card# +4344#Anopheles Card# +4345#Hill Wind Card# +4346#Egnigem Cenia Card# +4347#Armeyer Dinze Card# +4348#Wickebine Tres Card# +4349#Errende Ebecee Card# +4350#Laurell Weinder Card# +4351#Kavach Icarus Card# +4352#Boss Egnigem Card# +4353#Remover Card# +4354#Gemini-S58 Card# +4355#Gremlin Card# +4356#Beholder Card# +4357#Lord Knight Card# +4358#Seyren Windsor Card# +4359#Assassin Cross Card# +4360#Eremes Guile Card# +4361#Mastersmith Card# +4362#Howard Alt-Eisen Card# +4363#High Priest Card# +4364#Margaretha Sorin Card# +4365#High Wizard Card# +4366#Kathryne Keyron Card# +4367#Sniper Card# +4368#Cecil Damon Card# +4369#Venatu Card# +4370#Dimik Card# +4371#Archdam Card# +4372#White Lady Card# +4373#Green Maiden Card# +4374#Vesper Card# +4375#Orc Baby Card# +4376#Lady Tanee Card# +4377#Grove Card# +4378#Gold Acidus Card# +4379#Blue Acidus Card# +4380#Red Ferus Card# +4381#Green Ferus Card# +4382#Yellow Novus Card# +4383#Red Novus Card# +4384#Hydrolancer Card# +4385#Dragon Egg Card# +4386#Detardeurus Card# +4387#Ancient Mimic Card# +4388#Death Word Card# +4389#Plasma Card# +4390#Breeze Card# +4391#Baroness of Retribution Card# +4392#Dame of Sentinel Card# +4393#Mistress of Shelter Card# +4394#Lady Solace Card# +4395#Maero of Thanatos Card# +4396#Odium of Thanatos Card# +4397#Despero of Thanatos Card# +4398#Dolor of Thanatos Card# +4399#Memory of Thanatos Card# +4400#Aliza Card# +4401#Alicel Card# +4402#Aliot Card# +4403#Kiel-D-01 Card# +4404#Skogul Card# +4405#Frus Card# +4406#Skeggiold Card# +4407#Randgris Card# +4408#Gloom Under Night Card# +4409#Agav Card# +4410#Echio Card# +4411#Vanberk Card# +4412#Isilla Card# +4413#Hodremlin Card# +4414#Seeker Card# +4415#Snowier Card# +4416#Siroma Card# +4417#Ice Titan Card# +4418#Gazeti Card# +4419#Ktullanux Card# +4420#Muscipular Card# +4421#Drosera Card# +4422#Roween Card# +4423#Galion Card# +4424#Stapo Card# +4425#Atroce Card# +4426#Byorgue Card# +4427#Sword Guardian Card# +4428#Bow Guardian Card# +4429#Salamander Card# +4430#Ifrit Card# +4431#Kasa Card# +4432#Magmaring Card# +4433#Imp Card# +4434#Knocker Card# +4435#Zombie Slaughter Card# +4436#Ragged Zombie Card# +4437#Hell Poodle Card# +4438#Banshee Card# +4439#Flame Skull Card# +4440#Necromancer Card# +4441#Fallen Bishop Card# +4442#Tatacho Card# +4443#Aqua Elemental Card# +4444#Draco Card# +4445#Luciola Vespa Card# +4446#Powerful Skeleton Card# +4447#Centipede Card# +4448#Cornus Card# +4449#Dark Shadow Card# +4450#Banshee Master Card# +4451#Entweihen Crothen Card# +4452#Centipede Larva Card# +4453#Hillslion Card# +4456#Nidhoggur Shadow Card# +4457#Naght Seiger Card# +4458#Duneyrr Card# +4459#Rata Card# +4460#Rhyncho Card# +4461#Phylla Card# +4462#Hardrock Mammoth Card# +4463#Tendrillion Card# +4464#Aunoe Card# +4465#Fanat Card# +4466#Beholder Master Card# +4467#Heavy Metaling Card# +4468#Dark Pinguicula Card# +4469#Naga Card# +4470#Nepenthes Card# +4471#Draco Egg Card# +4472#Bradium Golem Card# +4473#Ancient Tree Card# +4474#Zakudam Card# +4475#Cobalt Mineral Card# +4476#Pinguicula Card# +4477#Hell Apocalypse Card# +4480#Sealed Kiel Card# +4481#Sealed Ktullanux Card# +4482#Sealed B Ygnizem Card# +4483#Sealed Dracula Card# +4484#Sealed Mistress Card# +4485#Sealed Gloom Card# +4486#Sealed Beelzebub Card# +4487#Sealed Ifrit Card# +4488#Sealed Dark Lord Card# +4489#Sealed Pharaoh Card # +4490#Sealed M Flower Card# +4491#Sealed Sniper Card# +4492#Sealed Orc Hero Card# +4493#Sealed Tao Gunka Card# +4494#Sealed Turtle General Card# +4495#Sealed Amon Ra Card# +4496#Sealed Drake Card# +4497#Sealed Stormy Knight Card# +4498#Sealed Lady Tanee Card# +4499#Sealed Samurai Card# +4500#Sealed Orc Load Card# +4501#Sealed High Priest Card# +4502#Sealed MasterSmith Card# +4503#Sealed Vesper Card# +4504#Sealed Eddga Card# +4505#Scaraba Card# +4506#Dolomedes Card# +4507#Queen Scaraba Card# +4508#Gold Scaraba Card# +4509#Gold Queen Scaraba Card# +4510#Miming Card# +4511#Little Fatum Card# +4512#Parus Card# +4513#Angra Mantis Card# +4514#Pom Spider Card# +4515#Alnold Card# +4516#Comodo Card# +4517#Cendrawasih Card# +4518#Banaspaty Card# +4519#Butoijo Card# +4520#Leak Card# +4521#Sedora Card# +4522#Sropho Card# +4523#Pot Dofle Card# +4524#King Dramoh Card# +4525#Kraken Card# +4526#Weird Coelacanth Card# +4527#Dark Coelacanth Card# +4528#Mutant Coelacanth Card# +4529#Cruel Coelacanth Card# +4530#Siorava Card# +4531#Red Eruma Card# +4532#Wild Rider Card# +4533#Mini Octopus Card# +4534#Giant Octopus Card# +4536#Sealed Atroce Card# +4537#Sealed Phreeoni Card# +4538#Sealed White Lady Card# +4539#Sealed Fallen Bishop Card# +4540#Sealed Lord Of The Death Card# +4541#Sealed High Wizard Card# +4542#SLD Detale Card# +4543#SLD Garm Card# +4544#Sealed Dark Snake Lord Card# +4545#Novice Poring Card# +4549#Upgrade Maya Puple Card# +4550#Upd Bow Guardian Card# +4556#Fenrir Card# +4557#Weakened Fenrir Card# +4559#Lord Morocc Card# +4560#Clown Card# +4561#Professor Card# +4562#Champion Card# +4563#Creator Card# +4564#Stalker Card# +4565#Paladin Card# +4566#Gypsy Card# +4567#Alphoccio Basil Card# +4568#Celia Alde Card# +4569#Chen Liu Card# +4570#Flamel Emure Card# +4571#Gertie Wie Card# +4572#Randel Lawrence Card# +4573#Trentini Card# +4574#Daehyon Card# +4575#Soheon Card# +4576#Gioia Card# +4577#Elvira Card# +4578#Pyuriel Card# +4579#Lora Card# +4580#Kades Card# +4581#Rudo Card# +4582#Bungisngis Card# +4583#Engkanto Card# +4584#Manananggal Card# +4585#Mangkukulam Card# +4586#Tikbalang Card# +4587#Tiyanak Card# +4588#Wakwak Card# +4589#Jejeling Card# +4590#Bangungot Card# +4591#Bakonawa Card# +4592#Buwaya Card# +4593#Menblatt Card# +4594#Petal Card# +4595#Cenere Card# +4596#Antique Book Card# +4597#Lichtern Blue Card# +4598#Lichtern Yellow Card# +4599#Lichtern Red Card# +4600#Lichtern Green Card# +4601#Amdarais Card# +4602#Amdarais H Card# +4603#Corruption Root Card# +4604#Corruption Root H Card# +4605#Undead Knight of Agony Card# +4606#Undead Knight of Grudge Card# +4607#Faithful Manager Card# +4608#White Knight Card# +4609#Khalitzburg Knight Card# +4610#Sarah Card# +4625#Time Holder Card# +4626#Big Ben Card# +4627#Big Bell Card# +4628#Neo Punk Card# +4629#Arc Elder Card# +4630#Timer Keeper Card# +4631#Owl Viscount Card# +4632#Owl Marquees Card# +4633#Powerful Archer Skeleton Card# +4634#Powerful Soldier Skeleton Card# +4635#Powerful Amdarais Card# +4636#Bijou Card# +4637#Immortal Legion Card# +4638#Watcher Card# +4639#Taffy Card# +4640#Frozen Wolf Card# +4641#Zombie Guard Card# +4652#Nightmare Amon Ra Card# +4653#Nightmare Arclouse Card# +4654#Nightmare Mimic Card# +4655#Nightmare Minorous Card# +4656#Nightmare Mummy Card# +4657#Nightmare Ancient Mummy Card# +4658#Nightmare Verit Card# +4659#Eggring Card# +4660#Scout Basilisk Card# +4661#Charge Basilisk Card# +4662#Big Eggring Card# +4663#Leaf Lunatic Card# +4664#Grass Fabre Card# +4665#Wild Hornet Card# +4666#Sweet Roda Frog Card# +4667#Hunter Wolf Card# +4668#Trans Spore Card# +4669#Jungle Mandragora Card# +4670#Fruit Pom Spider Card# +4671#Sorcerer Celia Card# +4672#Sura Chen Card# +4673#Maestro Alphoccio Card# +4674#Guillotine Cross Eremes Card# +4675#Arch Bishop Margaretha Card# +4676#Ranger Cecil Card# +4677#Mechanic Howard Card# +4678#Warlock Kathryne Card# +4679#Rune Knight Seyren Card# +4680#Royal Guard Randel Card# +4681#Geneticist Flamel Card# +4682#Shadow Chaser Gertie Card# +4683#Wanderer Trentini Card# +4684#True Eremes Guile Card# +4685#True Margaretha Sorin Card# +4686#True Kathryne Keyron Card# +4687#True Cecil Damon Card# +4688#True Howard Alt-Eisen Card# +4689#True Seyren Windsor Card# +4690#True Randel Lawrence Card# +4691#True Flamel Emure Card# +4692#True Celia Alde Card# +4693#True Chen Lio Card# +4694#True Gertie Wie Card# +4695#True Trentini Card# +4696#True Alphoccio Basil Card# +4697#Charleston Card# +4698#Step Card# +4699#Rock Step Card# +4700#STR + 1# +4701#STR + 2# +4702#STR + 3# +4703#STR + 4# +4704#STR + 5# +4705#STR + 6# +4706#STR + 7# +4707#STR + 8# +4708#STR + 9# +4709#STR + 10# +4710#INT + 1# +4711#INT + 2# +4712#INT + 3# +4713#INT + 4# +4714#INT + 5# +4715#INT + 6# +4716#INT + 7# +4717#INT + 8# +4718#INT + 9# +4719#INT + 10# +4720#DEX + 1# +4721#DEX + 2# +4722#DEX + 3# +4723#DEX + 4# +4724#DEX + 5# +4725#DEX + 6# +4726#DEX + 7# +4727#DEX + 8# +4728#DEX + 9# +4729#DEX + 10# +4730#AGI + 1# +4731#AGI + 2# +4732#AGI + 3# +4733#AGI + 4# +4734#AGI + 5# +4735#AGI + 6# +4736#AGI + 7# +4737#AGI + 8# +4738#AGI + 9# +4739#AGI + 10# +4740#VIT + 1# +4741#VIT + 2# +4742#VIT + 3# +4743#VIT + 4# +4744#VIT + 5# +4745#VIT + 6# +4746#VIT + 7# +4747#VIT + 8# +4748#VIT + 9# +4749#VIT + 10# +4750#LUK + 1# +4751#LUK + 2# +4752#LUK + 3# +4753#LUK + 4# +4754#LUK + 5# +4755#LUK + 6# +4756#LUK + 7# +4757#LUK + 8# +4758#LUK + 9# +4759#LUK + 10# +4760#MATK 1# +4761#MATK 2# +4762#FLEE + 6# +4763#FLEE + 12# +4764#CRIT + 5# +4765#CRIT + 7# +4766#ATK + 2%# +4767#ATK + 3%# +4786#MDEF + 2# +4787#MDEF + 4# +4788#MDEF + 6# +4789#MDEF + 8# +4790#MDEF + 10# +4791#DEF + 3# +4792#DEF + 6# +4793#DEF + 9# +4794#DEF + 12# +4795#HP + 100# +4796#HP + 200# +4797#HP + 300# +4798#HP + 400# +4799#HP + 500# +4800#SP + 50# +4801#SP + 100# +4802#SP + 150# +4803#Cure Lv1# +4804#Catholic Lv1# +4805#Archbishop Lv1# +4806#MATK 3# +4807#ASPD+1# +4808#Fighting Spirit 4# +4809#Fighting Spirit 3# +4810#Fighting Spirit 2# +4811#Fighting Spirit 1# +4812#Spell 4# +4813#Spell 3# +4814#Spell 2# +4815#Spell 1# +4816#Sharp 3# +4817#Sharp 2# +4818#Sharp 1# +4819#ATK + 1%# +4820#Fighting Spirit 5# +4821#Fighting Spirit 6# +4822#Fighting Spirit 7# +4823#Fighting Spirit 8# +4824#Fighting Spirit 9# +4825#Fighting Spirit 10# +4826#Spell 5# +4827#Spell 6# +4828#Spell 7# +4829#Spell 8# +4830#Spell 9# +4831#Spell 10# +4832#Expert Archer 1# +4833#Expert Archer 2# +4834#Expert Archer 3# +4835#Expert Archer 4# +4836#Expert Archer 5# +4837#Expert Archer 6# +4838#Expert Archer 7# +4839#Expert Archer 8# +4840#Expert Archer 9# +4841#Expert Archer 10# +4842#ASPD + 2# +4843#Sharp 4# +4844#Sharp 5# +4846#Fully Loved Stone# +4847#Spelled Stone# +4848#Immune Level 1# +4849#Cranial Lv1# +4850#Heal Lv3# +4851#Heal Lv4# +4852#Heal Lv5# +4853#Special Str# +4854#Special Agi# +4855#Special Vit# +4856#Special Int# +4857#Special Dex# +4858#Special Luk# +4859#FLEE + 1# +4860#FLEE + 3# +4861#Max HP + 1%# +4862#Max HP + 2%# +4863#Fatal Lv1# +4864#Fatal Lv2# +4865#Fatal Lv3# +4866#Fatal Lv4# +4867#Max HP + 3%# +4868#Max HP + 4%# +4869#ASPD 1# +4870#SP+25# +4871#SP + 75# +4872#ASPD 2# +4873#ASPD 3# +4875#Bear's Power# +4876#Runaway Magic# +4877#Speed of Light# +4878#Muscle Fool# +4879#Hawkeye# +4880#Lucky Day# +4881#ASPD 4# +4882#ATK + 1%# +4883#MATK + 1%# +4884#HIT + 1# +4885#Spell 1# +4886#Spell 2# +4887#Spell 3# +4888#Spell 4# +4889#Spell 5# +4890#MDEF+1# +4891#MDEF+3# +4892#MDEF+5# +4893#DEF+15# +4894#ATK + 4%# +4895#ATK + 5%# +4896#MATK + 2%# +4897#MATK + 3%# +4898#MATK + 4%# +4899#MATK + 5%# +4900#MHP+5%# +4901#MDEF + 7# +4902#DEF + 18# +4903#DEF + 21# +4904#ATK + 6%# +4905#ATK + 7%# +4906#MATK + 6%# +4907#MATK + 7%# +4908#Darklord Essence Force1# +4909#Darklord Essence Force2# +4910#Darklord Essence Force3# +4911#Darklord Essence Intelligence1# +4912#Darklord Essence Intelligence2# +4913#Darklord Essence Intelligence3# +4914#Darklord Essence Speed1# +4915#Darklord Essence Speed2# +4916#Darklord Essence Speed3# +4917#Darklord Essence Vitality1# +4918#Darklord Essence Vitality2# +4919#Darklord Essence Vitality3# +4920#Darklord Essence Concentration1# +4921#Darklord Essence Concentration2# +4922#Darklord Essence Concentration3# +4923#Darklord Essence Luck1# +4924#Darklord Essence Luck2# +4925#Darklord Essence Luck3# +4926#CRIT 1# +4927#MaxHP50# +4928#SP + 10# +4929#Max SP + 1%# +4930#Recovery UP# +4931#Heal 10# +4932#SP Recovery 1# +4933#Neutral Resistance Lv1# +4934#Neutral Resistance Lv2# +4935#Neutral Resistance Lv3# +4936#Attack Large 1# +4937#Attack Medium 1# +4938#Attack Small 1# +4939#CRIT 2# +4940#CRIT 3# +4941#CRIT 4# +4942#Parrying Lv1# +4943#Parrying Lv2# +4944#Parrying Lv3# +4945#Economy Lv1# +4946#Economy Lv2# +4947#Economy Lv3# +4948#After Skill Delay Lv1# +4949#After Skill Delay Lv2# +4950#After Skill Delay Lv3# +4991#Kafra Blossom Card# +4992#HP Absorption 1# +4993#SP Absorption 1# +4994#Rune of Strength Lv 1# +4995#Rune of Strength Lv 2# +4996#Rune of Strength Lv 3# +4997#Rune of Agility Lv 1# +4998#Rune of Agility Lv 2# +4999#Rune of Agility Lv 3# +5001#Headset# +5002#Jewel Crown# +5003#Joker Jester# +5004#Oxygen Mask# +5005#Gas Mask# +5006#Machoman's Glasses# +5007#Grand Circlet# +5008#Puppy Love# +5009#Safety Helmet# +5010#Indian Fillet# +5011#Aerial# +5012#Ph.D Hat# +5013#Lord Kaho's Horn# +5014#Fin Helm# +5015#Egg Shell# +5016#Boy's Cap# +5017#Bone Helm# +5018#Feather Bonnet# +5019#Corsair# +5020#Kafra Band# +5021#Grief for Greed# +5022#Hat of the Sun God# +5023#Parcel Hat# +5024#Cake Hat# +5025#Helm of Angel# +5026#Chef Hat# +5027#Mage Hat# +5028#Candle# +5029#Spore Hat# +5030#Panda Hat# +5031#Mine Hat# +5032#Sunday Hat# +5033#Raccoon Hat# +5034#Bulb Band# +5035#Poring Hat# +5036#Cross Hat# +5037#Nut Shell# +5038#Deviruchi Hat# +5039#Rainbow Eggshell# +5040#Blush# +5041#Heart Hairpin# +5042#Bao Bao# +5043#Opera Phantom Mask# +5044#Devil Wings# +5045#Magician Hat# +5046#Bongun Hat# +5047#Fashionable Glasses# +5048#Crescent Hairpin # +5049#Striped Hairband# +5050#Wonder Nutshell# +5051#Pussy Cat Bell# +5052#Blue Hairband# +5053#Sphinx Hat# +5054#Assassin Mask# +5055#Novice False Eggshell# +5056#Fruit of Love# +5057#Black Cat Ears# +5058#Drooping Cat# +5059#Teddybear Hat# +5060#Party Hat# +5061#Flower Hairpin# +5062#Straw Hat# +5063#Giant Band Aid# +5064#Smokie Leaf# +5065#Blue Fish# +5066#Succubus Horn# +5067#Sombrero# +5068#Evil Wing Ears# +5069#Kitsune Mask# +5070#Hot-blooded Headband# +5071#Indian Headband# +5072#Incubus Horn# +5073#Model Training Hat# +5074#Angel Wing Ears# +5075#Cowboy Hat# +5076#Beanie# +5077#Tulip Hairpin# +5078#Sea-Otter Hat# +5079#X Hairpin# +5080#Crown of Ancient Queen# +5081#Crown of Mistress# +5082#Decorative Mushroom# +5083#Red Ribbon# +5084#Lazy Smokie# +5085#Small Ribbons# +5086#Alarm Mask# +5087#Poker Face# +5088#Surprised Mask# +5089#Annoyed Mask# +5090#Goblin Leader Mask# +5091#Decorative Golden Bell# +5092#Coif# +5093#Coif# +5094#Helmet of Orc Hero# +5096#Assassin Mask# +5097#Holiday Hat# +5098#Tiger Mask# +5099#Neko Mimi# +5100#Sales Banner# +5101#Takius Blindfold# +5102#Blank Eyes# +5103#Sunflower Hairpin# +5104#Dark Blinder# +5105#Costume Cake Hat# +5106#2nd Anniversary Hat# +5107#Crunch Toast# +5108#Renown Detective's Cap# +5109#Red Bonnet# +5110#Baby Pacifier# +5111#Galapago Cap# +5112#Super Novice Hat# +5113#Angry Snarl# +5114#Bucket Hat# +5115#Winter Hat# +5116#Banana Hat# +5117#Mystic Rose# +5118#Puppy Headband# +5119#Super Novice Hat# +5120#Bucket Hat# +5121#Zealotus Mask# +5122#Magni's Cap# +5123#Ulle's Cap# +5124#Fricca's Circlet# +5125#Angel's Kiss# +5126#Morpheus's Hood# +5127#Morrigane's Helm# +5128#Goibne's Helm# +5129#Bird Nest# +5130#Lion Mask# +5131#Close Helmet# +5132#Angeling Hat# +5133#Sheep Hat# +5134#Pumpkin-Head# +5135#Cyclop's Eye# +5136#Antonio's Santa Hat# +5137#Alice Doll# +5138#Magic Eyes# +5139#Hibiscus# +5140#Charming Ribbon# +5141#Marionette Doll# +5142#Crescent Helm# +5143#Kabuki Mask# +5144#Gambler Hat# +5145#Carnival Joker Jester# +5146#Elephant Hat# +5147#Baseball Cap# +5148#Guildmaster's Cap# +5149#Tiara Of Champions# +5150#Joker Jester# +5151#Note Headphone# +5152#Chinese Crown# +5153#Angeling Hairpin# +5154#Papa Sunglasses# +5155#Papa Beard# +5156#Papa Mask# +5157#Orc Helm# +5158#Monk Hat# +5159#Golden Gear# +5160#Magestic Goat# +5161#Spiky Band# +5162#Bone Helm# +5163#Corsair# +5164#Tiara# +5165#Crown# +5166#Sphinx Hat# +5167#Munak Hat# +5168#Bongun Hat# +5169#Bride Mask# +5170#Feather Beret# +5171#Valkyrie Helm# +5172#Beret# +5173#Magistrate Hat# +5174#Ayam# +5175#Censor Bar# +5176#Hahoe Mask# +5177#Mythical Lion Mask# +5179#Gold Tiara# +5180#Holiday Hat# +5181#Helm Of Darkness# +5182#Puppy Hat# +5183#Bird Nest Hat# +5184#Captain's Hat# +5185#Laurel Wreath# +5186#Geographer Band# +5187#Twin Red Ribbon# +5188#Creative Convention Hat# +5189#Hat of Falling Leaf# +5191#Black Ribbon# +5192#Yellow Ribbon# +5193#Green Ribbon# +5194#Pink Ribbon# +5195#Red Ribbon# +5196#Orange Ribbon# +5197#White Ribbon# +5200#Coppola# +5202#Fantastic Pumpkin-Head# +5203#Smiling Mask# +5204#Rudolph's Nose# +5205#Emperor's Wreath# +5206#Romantic White Flower# +5207#Angel's Blessing# +5208#Rideword Hat# +5209#Yellow Baseball# +5210#Flapping Angel Wing# +5211#Dress Hat# +5212#Satellite Hairband# +5213#Black Bunny Band# +5214#Moonlight Flower Hat# +5215#Evolved Angel Wing# +5216#Evolved Evil Wing# +5217#Evolved Magestic Goat# +5218#Evolved Bunny Band# +5219#Evolved Drooping Cat# +5220#Evolved Pipe# +5221#Evolved Pair of Red Ribbon# +5222#Evolved Blue Fish# +5223#Evolved Big Golden Bell# +5224#Evolved Orc Hero Helm# +5225#Parade Hat# +5226#Mini Propeller# +5227#Red Deviruchi Hat# +5228#Gray Deviruchi Hat# +5229#Brown Deviruchi Hat# +5230#Gray Drooping Cat# +5231#Brown Drooping Cat# +5232#Pink Drooping Cat# +5233#Blue Drooping Cat# +5234#Yellow Drooping Cat# +5235#Brown Beanie# +5236#Blue Beanie# +5237#Pink Beanie# +5238#Red Mage Hat# +5239#Gray Mage Hat# +5240#Brown Mage Hat# +5241#Blue Mage Hat# +5242#Yellow Mage Hat# +5243#Shafka# +5244#Elven Blindfold# +5245#Elven Sunglasses# +5246#Angelic Helm# +5247#Satanic Helm# +5248#Robotic Blindfold# +5249#Human Blindfold# +5250#Robotic Ears# +5251#Round Ears# +5252#Drooping Nine Tail# +5253#Lif Doll Hat# +5254#Deviling Hat# +5255#Triple Poring Hat# +5256#Valkyrie Feather Band# +5257#Soulless Wing# +5258#Afro Wig# +5259#Elephant Hat# +5260#Cookie Hat# +5261#Silver Tiara# +5262#Gold Tiara# +5263#Pagdayaw# +5264#Australian Flag Hat# +5265#Rental Apple of Archer# +5266#Rental Bunny Band# +5267#Costume Sakkat# +5268#Grand Circlet# +5269#Flapping Angel Wing# +5270#Autumn Leaves# +5271#Chinese Crown# +5272#Tongue Mask# +5273#Happy Wig# +5274#Shiny Wig# +5275#Marvelous Wig# +5276#Fantastic Wig# +5277#Air Pirate's Bandana# +5278#Yellow Ribbon# +5280#Baphomet Horns# +5283#Chick Hat# +5284#Water Lily Crown# +5285#Vane Hairpin# +5286#Pecopeco Hairband# +5287#Vacation Hat# +5288#Red Glasses# +5289#Vanilmirth Hat# +5290#Drooping Bunny# +5291#Kettle Hat# +5292#Dragon Skull# +5293#Ramen Hat# +5294#Whisper Mask# +5300#Champion Helm# +5302#Water Lily Hat# +5303#Flower Wreath# +5304#Cap Of Blindness# +5305#Pirate Dagger# +5306#Freyja Crown# +5307#Carmen Miranda's Hat# +5308#Brazilian Flag Hat# +5310#Shining Electric Bulb Hairband# +5311#Large Hibiscus# +5312#Ayothaya King's Hat# +5313#Diadem# +5314#Hockey Mask# +5315#Observer# +5316#Umbrella Hat# +5317#Fisherman Hat# +5318#Poring Party Hat# +5319#Arc Angeling Hat# +5320#Champion Wreath# +5321#Indonesian Bandana# +5322#Scarf# +5323#Misstrance Crown# +5324#Little Angel Doll# +5325#Robo Eye# +5326#Masquerade C# +5328#Costume Evil Wing Ears# +5329#Costume: Dark Blindfold# +5330#kRO Drooping Kitty C# +5331#Corsair C# +5332#Loki Mask# +5333#Radio Antenna# +5334#Flapping Angeling# +5335#Jumping Poring# +5336#Guildsman Recruiter Hat# +5337#Party Recruiter Hat# +5338#Bf Recruiter Hat# +5339#Friend Recruiter Hat# +5340#Defolty Doll Hat# +5341#Glaris Doll Hat# +5342#Sorin Doll Hat# +5343#Telling Doll Hat# +5344#Bennit Doll Hat# +5345#W Doll Hat# +5346#Gf Recruiter Hat# +5347#Ph.D Hat# +5348#Big Ribbon# +5349#Boy's Cap# +5350#Pirate Bandana# +5351#Sunflower# +5352#Poporing Cap# +5353#Hat of the Sun God# +5354#Sons Of Kolbald Cap# +5355#Selendang# +5356#Festival Pumpkin Hat# +5357#Wings Of Victory# +5358#Peco Ears# +5359#Airship Captain's Hat# +5360#Wickebine's Black Cat Ears# +5361#Gangster Scarf# +5362#Ninja Scroll# +5363#Abysmal Knight Helm# +5364#Evil Snake Lord Hat# +5365#Magnolia Hat# +5366#Love Dad Bandana# +5367#Yao Jun Hat# +5368#White Wing# +5369#Dark Wing# +5370#Orchid Hairband# +5371#Judge Hat# +5372#Koneko Hat# +5373#Dark Randgris Helm# +5374#Gigantic Magestic Goat# +5375#Faux Orc Boss Hat# +5376#Flying Evil Wings# +5377#Gentleman's Pipe# +5378#Bunny Top Hat# +5379#Tam# +5380#Fish Head Hat# +5381#Santa Poring Hat# +5382#Bell Ribbon# +5383#Hunting Cap# +5384#Twin Pom Santa# +5385#Yoyo Hat# +5386#New Year's Hat# +5387#Neko Mimi Kafra# +5388#Snake Head# +5389#Angel Spirit# +5390#Costume Blue Christmas Cheer# +5391#Toast# +5392#Louyang NewYear Hat# +5393#Cupid's Target# +5394#Chewing Gum# +5395#Striped Hat# +5396#Jasper Crest# +5397#Scuba Mask# +5398#Bone Head# +5399#Mandragora Cap# +5401#Black Frame Glasses# +5402#Mischievous Fairy# +5403#Fish In Mouth# +5404#Blue Ribbon# +5405#Filir Hat# +5406#Clergyman Grad Hat# +5407#Magician Grad Hat# +5409#Purple Cowboy Hat# +5410#Brown Paperbag Hat# +5411#White Snake Hat# +5412#Lollipop# +5413#Popcorn Hat# +5414#Campfire Hat# +5415#Poring Cake Hat# +5416#Beer Hat# +5418#Soldier Hat# +5420#Ifrit Mask# +5421#Ifrit's Ears# +5422#Linguistic Book Hat# +5426#Creative Convention Cap# +5427#Creative Convention Chapeau# +5428#Dull Paper Bag# +5429#Hobgoblin's Hat# +5430#Promethean Crown# +5431#Chicken Hat# +5433#Champion's Wreath# +5434#Coca-Cola Bottle# +5435#Classic Creative Cap# +5436#Bride's Corolla# +5437#Fairy Flower# +5438#Cute Green Ribbon# +5439#Cute Red Ribbon# +5440#Cute Blue Ribbon# +5441#Cute White Ribbon# +5442#Necktie# +5443#Statue Of Baby Angel# +5444#Hair Brush# +5445#Candy Cane In Mouth# +5446#Catfoot Hairpin# +5447#Frog Hat# +5448#Indifferent Solo Hat# +5449#Angry Solo Hat# +5451#Costume Gold Dragonhelm# +5452#Silver Dragonhelm# +5453#Copper Dragonhelm# +5457#Moon Rabbit Hat# +5458#Pinwheel Hat# +5462#Spiked Scarf# +5463#Rainbow Scarf# +5464#Zaha Doll Hat# +5465#Hat Of Fortune# +5466#Wind's Guide# +5468#Parade Hat# +5469#Musketeer Hat# +5470#Darkness Eyes# +5471#Reginrev's Wings# +5472#Red White Cap# +5473#Crab Nipper Hat# +5474#AFK Hat# +5475#Gozarian Mask# +5476#Grand Peco Headdress# +5478#Classic Hat# +5479#Shaman's Hair Decoration# +5480#Bijofnil Wings# +5481#Hermode Cap# +5482#Dark Knight Mask (Type A)# +5483#Odin Mask# +5485#Tiger Face# +5486#Anniversary Hat# +5487#Poring Cake Hat# +5488#Cute Santa Hat# +5489#Love Daddy# +5490#Anubis Helm# +5491#Outlaw's Hat# +5495#Power Of Thor# +5496#Dice Hat# +5497#Edgga Doll# +5498#Costume Vagabond Wolf Hat# +5499#Pizza Pie of Plenty# +5500#Ice Cream Hat# +5501#Pirate's Pride# +5502#Necromancer's Hood# +5503#Rabbit Magic Hat# +5504#Chinese Wedding Veil# +5505#Asara Fairy Hat# +5508#Shark Hat# +5509#Sting Hat# +5510#Shower Cap# +5511#Samambaia# +5512#Aquarius Diadem# +5513#Aquarius Crown# +5514#Pisces Diadem# +5515#Pisces Crown# +5516#Hawk Eyes# +5517#Hawk Eyes# +5518#Large Baphomet Horns# +5519#Peacock Feather# +5520#Rabbit Earmuffs# +5521#Costume: Angry Snarl# +5524#Sakura Milk Tea Hat# +5525#First Leaf Tea Hat# +5526#Lady Tanee Doll# +5527#Lunatic Hat# +5528#Frog King Hat# +5529#Frost Giant's Skull# +5530#Raven Cap# +5531#Baby Dragon Hat# +5532#Pirate Dagger# +5533#Emperor Wreath# +5534#Fox Hat# +5535#Side Cap# +5536#Spare Card# +5537#Coati Hat# +5538#Tucan Hat# +5539#Jaguar Hat# +5540#Freya's Spiritual Circlet# +5541#Freya's Spiritual Circlet# +5542#Freya's Spiritual Circlet# +5543#Freya's Spiritual Circlet# +5544#Time Keeper Hat# +5545#Aries Diadem# +5546#Aries Crown# +5547#Red Flower Hairband# +5548#Scarlet Rose# +5549#Taurus Diadem# +5550#Taurus Crown# +5552#Festival Grand Circlet# +5553#Festival Bunny Band# +5554#Octopus Hat# +5555#Leaf Cat Hat# +5556#Fur Seal Hat# +5557#Wild Rose Hat# +5558#Saci's Hat# +5560#Champion Helm# +5561#Rabbit Magic Hat# +5562#Love of Truth# +5563#Dolor Hat# +5564#Crown of Deceit# +5566#Tiger Arhet Mask# +5567#Sinulog Hat# +5568#Rabbit Bonnet# +5569#Gemini Diadem# +5570#Gemini Crown# +5573#Dokkaebi Horn# +5574#Well-Chewed Pencil# +5575#Rice Ball Hat# +5577#Dark Knight Mask (Type B)# +5578#Voyage Hat# +5579#Wanderer's Sakkat# +5580#Red Beret# +5581#Cancer Diadem# +5582#Cancer Crown# +5583#Eden Group Hat# +5584#Majestic Devil Horns# +5585#Rune Hairband# +5588#Leo Crown# +5589#Leo Diadem# +5591#Desert Prince# +5592#Sigrun's Wings# +5593#Rabbit Bonnet# +5594#Donut In Mouth# +5595#Eye Of Juno# +5596#Four Leaf Clover in Mouth# +5597#Bubble Gum in Mouth# +5598#Virgo Crown# +5599#Virgo Diadem# +5600#Brazil Twin Ribbon# +5601#Banana Beret# +5602#Jaguar Face# +5603#pRO Blue Angel Crown# +5604#pRO Pink Angel Crown# +5605#pRO Violet Angel Crown# +5609#Chung Hairpin# +5610#Ice Ear Wing# +5611#Turtle Hat# +5612#Blue Drooping Cat# +5613#Flapping Angel Wing# +5614#Evolved Pipe# +5615#Evolved Pair of Red Ribbon# +5616#Evolved Blue Fish# +5617#Hibiscus# +5618#Neko Mimi# +5619#Evolved Bunny Band# +5620#Evolved Magestic Goat# +5621#Sheep Hat# +5622#Mini Propeller# +5623#Alice Doll# +5624#Red Glasses# +5625#Chick Hat# +5626#Gray Deviruchi Hat# +5627#Vane Hairpin# +5628#Pecopeco Hairband# +5629#Vacation Hat# +5630#Charming Ribbon# +5631#Water Lily Crown# +5632#Vanilmirth Hat# +5633#Drooping Bunny# +5634#Kettle Hat# +5635#Dragon Skull# +5636#Ramen Hat# +5637#Pink Beanie# +5638#Puppy Hat# +5639#Magic Eyes# +5640#Jumping Poring# +5641#Robo Eye# +5642#Yellow Mage Hat# +5643#Crescent Helm# +5644#Tiger Mask# +5645#Fantastic Wig# +5646#Whisper Mask# +5647#Bunny Band# +5648#Centimental Flower# +5649#Apple Of Archer# +5650#Elven Ears# +5651#Rental Brooch# +5652#Magestic Goat# +5653#Darkness Helm# +5654#Holy Marching Hat# +5655#Dark Snake Lord Hat# +5656#Scooter Hat# +5657#Peace Pipe# +5658#Imp Hat# +5659#Sleeper Hat# +5660#Gryphon Hat# +5661#Red Pirate Bandana# +5662#Libra Crown# +5663#Libra Diadem# +5664#Filir's Pinions# +5665#Norn Feather Hat# +5667#Skull Hood# +5668#Weird Pumpkin Hat# +5669#Poring Party Hat# +5670#Anniversary Thief Hat# +5671#Incarnation Of Morocc Doll# +5673#Nostalgic Sakura# +5674#Piggie Bank# +5675#Poring Letter# +5676#Scorpio Crown# +5677#Scorpio Diadem# +5680#Hawk Eye# +5681#Green Ribbon# +5683#Horn Of Arch Evil Model# +5684#Luxurious Crown# +5685#A Military Cap Of A Knight# +5686#A Hat With Nice Feather# +5687#Light Hornhelm# +5690#Red Wing Hat# +5691#Sailor's Bandana# +5692#Sea Cat Hat# +5693#NoFear Underwear# +5694#NoFear Headband# +5737#Potted Muka Hat# +5738#Snowman Hat# +5739#Sagittarius Crown# +5740#Sagittarius Diadem# +5742#Rudolf Santa Hat# +5744#Capricorn Crown# +5745#Capricorn Diadem# +5746#Rune Circlet# +5747#Mitra# +5748#Sniper Goggle# +5749#Driver Band# +5750#Shadow Handicraft# +5751#Maestro Song's Hat# +5752#Midas Whisper# +5753#Magic Stone Hat# +5754#Blazing Soul# +5755#Silent Executor# +5756#Wind Whisper# +5757#Dip Schmidt Helm# +5758#Dying Swan# +5759#Noa's Hat# +5760#Driver Band# +5761#Sloth Hat# +5762#LU Live2010 Duneyrr Hat# +5763#Red Bunny Band# +5764#Love Rabbit Hood# +5765#Pitch Black Ribbon# +5766#Amistr Hat# +5767#Samurai Mask# +5768#Cherry Blossom Crown# +5774#Scallywag's Hat# +5775#Chocolate Donut# +5777#Remover Hat# +5778#Turkey On Your Head# +5782#Legionaire Helm# +5788#3D Glasses# +5789#Thanatos Odium Mask# +5790#Mother's Kindness# +5792#Fish Pin# +5795#Bright Red Dress Hat# +5804#Pirate Eye Bandage# +5805#Victorious Coronet# +5806#Poem Natalia Hat# +5807#October Fest Cap# +5808#Dark Bacilium# +5809#Boom Boom Hat# +5810#Ph.D Hat V# +5811#Santa's Beard# +5812#Hat Of Expert# +5815#Classic Hat# +5816#New Cowboy Hat# +5817#Valentine's Emblem# +5827#Book Hat# +5848#Bandit Disguise# +5849#Doctor Hairband# +5852#Easter Egg Shell# +5855#Fish Rod# +5860#Siege Army Cap# +5861#Siege Kafra Band# +5862#Siege Sweet Gent# +5863#Siege Zoro Mask# +5866#Red Cherry Blossom# +5873#HeadGear Of Siegfried# +5874#Circlet Of Kriemhild# +5875#Diadem Of Bruenhild# +5876#Star-Spangled Bandana# +5877#'Merica Hat# +5878#Miracle Blue Rose# +5879#Poring Sunglasses J# +5880#Cheering Whistle J# +5897#Ascendant Crown# +5898#Autumn Headband# +5899#Black Ribbon# +5900#Divine Guard Hat# +5901#Focus Beret# +5902#Harvester Hat# +5903#Dead Man Bandana# +5904#Very Normal Hat# +5905#Lyrica Hat# +5906#Oni Horns# +5907#Sea Captain Hat# +5912#Costume Servant Deviling# +5916#Exorcist Glasses# +5917#Yellow Scarf# +5918#Gambler's Seal# +5921#Monster Fish Gills# +5924#Dragon Knight Eye Patch# +5931#Niflheim Bunny Hat# +5932#Heavens Cage# +5933#Crow Tengu Mask# +5937#Flying Helmet# +5938#Octopus Hat# +5940#Boitata Hat# +5945#Well-Chewed Pencil (Red)# +5948#Loki's Assassin Mask# +5949#Bra Hat# +5963#Wing Headphone# +5966#Kardui Ear# +5967#Flying Galapago# +5968#Clergy Nurse Cap# +5969#Queen Anz Revenge# +5970#Rune Helm# +5971#Moon Eyepatch# +5972#Chatty Parrot# +5973#Ancient Elven Ear# +5975#Zealotus Doll# +5978#Toy Syringe# +6000#Ashes of Darkness# +6001#Essence of Fire# +6002#Token of Apostle# +6003#Pendant of Spirit# +6004#Cursed Baphomet Doll# +6007#Freshly Boiled Octopus Rice Cake Soup# +6009#Big Fan Of Magic# +6010#Hoe# +6011#Blue Card B# +6012#Blue Card C# +6015#Blue Card M# +6017#Blue Card T# +6018#Blue Card V# +6020#Fur# +6021#Peaked Hat# +6022#Hard Skin# +6023#Mystic Horn# +6024#Shining Diamond# +6025#Towel of Memory# +6026#Written Oath Of Marriage# +6027#Crystal Of Feardom# +6028#Sealed Scroll# +6029#Morocc Tracing Log# +6030#Glittering Paper# +6031#Glittering Paper# +6032#Horn of Hillslion# +6033#Horn of Tendrilion# +6034#Weird Part# +6035#Decaying Stem# +6036#Meeting Invitation# +6037#Messy File# +6038#Neat Report# +6039#Piece of Fish# +6040#Part of a Report# +6041#Strong Vine# +6042#Ordinary Branch# +6043#Letter from Lugen# +6044#Letter from Otto# +6045#Supply Box# +6046#New Clothing Dye Coupon# +6047#Original Clothing Dye Coupon# +6048#Unidentified Mineral# +6049#Marlin# +6051#Gray Hollow# +6070#Shaman's Document# +6071#Broken Sword# +6072#Bijofnil Feather# +6073#Dragon's Mane# +6074#Bazett's Order# +6075#Crystalized Teardrop# +6076#Portable Toolbox# +6077#Rough Mineral# +6078#Stone Fragment# +6079#Flower Of Alfheim# +6080#Manuk Coin# +6081#Splendide Coin# +6082#Spirit Of Alfheim# +6083#Dolly Capsule# +6084#Bradium Fragments# +6085#Shaggy Muffler# +6086#Withered Flower# +6087#Spiritual Crystal# +6088#Spiritual Crystal# +6089#Dark Piece# +6090#Refined Bradium# +6091#Darkred Scale Piece# +6092#Piece Of Singing Crystal# +6093#Draco's Egg# +6094#Traditional Cookie# +6095#Flavored Alcohol# +6096#Fish With Blue Back# +6097#Pumpkin Pie # +6098#Small Snow Flower# +6099#Grilled Rice Cake# +6100#Damp Darkness# +6104#Big Scell# +6105#Morning Dew# +6106#Well-Ripened Berry# +6107#Sunset on the Rock# +6108#Apple Pudding# +6109#Plant Nutrient# +6110#Yellow Vital Flower# +6111#Mystic Stone# +6112#Fresh Salad# +6113#Blue Vital Flower# +6114#Flame Gemstone# +6115#Bun# +6120#Face Paint# +6121#Makeover Brush# +6122#Paint Brush# +6123#Surface Paint# +6124#Wolf Flute# +6125#Bunny Box# +6126#Summer Happy Box# +6128#Antidote# +6144#Regrettable Tears# +6145#Vulcan Bullet# +6146#Madogear Fuel# +6147#Liquid Condensed Bullet# +6150#Mansion Key# +6151#Giant Bradium Fragment# +6152#Glittering Crystal# +6153#Special Exchange Coupon# +6154#Broken Horn Pipe# +6156#A Report To Be Approved# +6157#Festa Gold Medal# +6158#Vote 2# +6186#Monkey Wrench# +6187#Blank Card# +6188#Slotting Advertisement# +6189#Spell Book(Fire Bolt)# +6190#Spell Book(Cold Bolt)# +6191#Spell Book(Lightning Bolt)# +6192#Spell Book(Storm Gust)# +6193#Spell Book(Lord of Vermilion)# +6194#Spell Book(Meteor Storm)# +6195#Spell Book(Comet)# +6196#Spell Book(Tetra Vortex)# +6197#Spell Book(Thunder Storm)# +6198#Spell Book(Jupitel Thunder)# +6199#Spell Book(Water Ball)# +6200#Spell Book(Heaven's Drive)# +6201#Spell Book(Earth Spike)# +6202#Spell Book(Earth Strain)# +6203#Spell Book(Chain Lightning)# +6204#Spell Book(Crimson Rock)# +6205#Spell Book(Drain Life)# +6210#Thorn Plant Seed# +6211#Blood Sucker Plant Seed# +6212#Bomb Mushroom Spore# +6213#Explosive Powder# +6214#Smoke Powder# +6215#Tear Gas# +6216#Oil Bottle# +6217#Mandragora Flowerpot# +6218#Dieshin's Delivery Box# +6219#Eden Group Mark# +6220#Mysterious Dyestuffs# +6221#Mystic Hydra Ball# +6222#Shining Beads# +6223#Carnium# +6224#Bradium# +6225#HD Carnium# +6226#HD Bradium# +6230#Safe to 7 Weapon Certificate# +6234#Safe to 7 Body Armor Certificate# +6235#Safe to 7 Headgear Certificate# +6237#Guarana Fruit# +6240#HD Oridecon# +6241#HD Elunium# +6243#WPS Point Token# +6244#Gun Powder# +6245#Black Powder# +6246#Yellow Powder# +6247#White Powder# +6248#Melange Pot# +6249#Savage Meat# +6250#Cooking Skewer# +6251#Black Charcoal# +6252#Blood of Wolf# +6253#Cold Ice# +6254#Beef Head# +6255#Large Cookpot# +6256#Ice Piece# +6257#Ice Crystal# +6258#Comodo Tropical Fruit# +6259#Drosera Tentacle# +6260#Petite Tail# +6261#Fine Noodle# +6262#Cool Gravy# +6263#Coconut Fruit# +6264#Melon# +6265#Pineapple# +6266#Key Of Deception# +6267#Key Of Illusion# +6268#Key Of Pleasure# +6269#A Master's Brush# +6270#A Picture Of Maestro Song# +6271#Receipt# +6272#Seed For Experiment# +6273#Seed For Experiemnt# +6274#Saint's Clothing Piece# +6275#King's Shield# +6276#Clear Reagent# +6277#Red Reagent# +6278#Black Reagent# +6279#How To Make An Apple Bomb# +6280#How To Make A Pineapple Bomb# +6281#How To Make A Coconut Fruit Bomb# +6282#How To Make A Melon Bomb# +6283#How To Make A Banana Bomb# +6284#How to Grow Plant Genes# +6285#How to Make High Quality Potions# +6286#Gym Pass# +6287#Omni Clothing Dye# +6288#Summer Happy Box# +6289#Mysterious Dyestuffs# +6290#Hairstyle Coupon# +6291#Enriched Elunium# +6292#Enriched Oridecon# +6293#Token Of Siegfried# +6294#Marriage Covenant# +6295#Original Clothing Dye Coupon# +6297#Bottle Throw# +6298#Crushed Pumpkin# +6299#Worn Fabric# +6302#GM Max Bond# +6304#Sapha Certification# +6305#Frozen Piece of Skin# +6306#Clotted Bloodstain# +6307#Strange Magic Stone# +6308#Unknown Relic# +6316#VIP Token of Siegfried# +6319#Small Bradium# +6320#Magical Stone# +6321#Rake Horn Helm# +6322#Antler Helm# +6323#Twin Horn Helm# +6324#Single Horn Helm# +6325#White Spider Limb# +6326#Piece of Queen's Wing# +6340#Faded Music(Green)# +6341#Faded Music(Red)# +6342#Faded Music(Purple)# +6343#Faded Music(Blue)# +6345#Love Lump# +6360#Scarlet Point# +6361#Indigo Point# +6362#Yellow Wish Point# +6363#Lime Green Point# +6376#KVM Badge# +6377#Bulk Buyer Shop License# +6380#Mora Coin# +6381#Field Shovel# +6382#Urn# +6383#Lope's Clue# +6384#Lope's Ring# +6385#Research Tool Bag# +6386#Bath Water Sample# +6387#Teeth Sample# +6388#Scale Sample# +6389#Sample of Puddle Research# +6390#Small Pocket# +6391#Splendid Supply Kit# +6392#Bradium Box# +6393#Round Feather# +6394#Golden Feather# +6395#Angel Magic Power# +6396#Spiritual Auger# +6401#Palm Oils# +6402#Palm Oil Fruit# +6403#Comodo Leather# +6404#Keris Hilt# +6405#Cendrawasih Feather# +6406#Shining Cendrawasih Feather# +6415#Strange Embryo# +6417#Silvervine Fruit# +6418#A Class Coin# +6419#B Class Coin# +6420#C Class Coin# +6421#D Class Coin# +6422#E Class Coin# +6423#Seagod's Anger# +6424#Spirit Piece# +6425#Halloween Certificate# +6426#Poor Can# +6427#Poor Can Sack# +6428#Adventure Card A# +6429#Adventure Card B# +6430#Picture Fragment# +6431#Bucket# +6432#Full Bucket# +6433#Cleaning Brush# +6434#Fixing Kit# +6435#Fresh Fruit# +6436#Seagod's Protection# +6437#Scaraba Perfume# +6440#General Lubricant# +6441#High Ranked Lubricant# +6442#Octopus Hunting Stick# +6443#Silit Pong Bottle# +6446#Green Paper# +6447#Red Paper# +6448#White Paper# +6449#Ordinary Kid's Diary# +6450#Honest Kid's Diary# +6451#Unidentified Fish# +6452#Etoille's Ring# +6453#Undelivered Gift# +6454#Santa Bag# +6457#Safe to 10 Certificate# +6464#Hate Crate# +6469#Will of Warrior# +6470#Blood Thirst# +6471#Ghost Chill# +6480#Poring coin# +6481#Sacred Rock Shard# +6482#Ancient City Key# +6483#Dream Scroll# +6484#Heroic Desocketing Book# +6488#Thanks Invest Ticket# +6489#Cats Invest Certificate# +6493#Makibishi# +6494#Kafra Coin# +6495#Eden Merit Badge# +6496#Tikbalang's Thick Spine# +6497#Lesser Agimat# +6498#Jejellopy# +6499#Ancient Grudge# +6500#Sharpened Bamboo# +6501#Salt Bag# +6502#Silver Cross# +6503#Spiritual Protection# +6504#Cast-Iron Caldron# +6505#Purified Spirit Bone# +6506#Offering Bouquet# +6507#Evil Spirit Bone# +6508#Silver Bracelet# +6509#Mysterious Flower# +6510#Elegant Flower# +6511#Beautiful Flower# +6512#Fire Charm# +6513#Ice Charm# +6514#Wind Charm# +6515#Earth Charm# +6516#Bakonawa Doll# +6517#Bangungot Doll# +6518#Buwaya Doll# +6519#Collected Sample# +6520#Lost Belongings# +6523#Piece of Bakonawa's Spirit# +6524#Piece of Bangungot's Spirit# +6525#Piece of Buwaya's Spirit# +6535#Piece Of Red Fabric# +6536#Star Shape Decoration# +6539#Old Left Lapine# +6540#Golden Leaf# +6541#Avant Research Data# +6542#Star Shape Mushroom# +6545#Firm Hair# +6546#Younger Brother Letter# +6547#Stained Research Book# +6548#Piece Of Lapine Wing# +6549#Courtesy Ticket# +6552#Mail Package# +6553#Leaf Made Wood# +6554#Seed Box# +6555#Birthday Candle# +6557#Fancy Fairy Wing# +6558#Pile Of Acorn# +6559#Eye Drops# +6560#Leaf Bookmark# +6561#Dustball# +6562#Tiny Mouse Tail# +6563#Weeds# +6564#Captive Hatchling# +6566#Fierce Cacao 99 Recipe# +6567#Chocolate Drink Recipe# +6581#Holy Amulet# +6592#Small Wooden box# +6593#Criatura Hair Coupon# +6594#Magic Bronze Bullion# +6595#Velund Hammer# +6596#Velund Anvil# +6597#Velund Bracelet# +6598#Jormungand rib# +6599#Spirit Of Hugin# +6600#Spirit Of Munin# +6601#Chisel Of Giant# +6602#Secret Of Rune# +6603#Skin Of Hraesvelg# +6604#Essence Of Rune# +6605#Muspellium# +6606#Cute Cart Remodel Coupon# +6607#Temporal Crystal# +6608#Coagulated Spell# +6609#Glast Decayed Nail# +6610#Glast Horrendous Mouth# +6611#Colorful Key# +6612#Gold Coin Basket# +6613#Colorful Brooch# +6614#Ancient Relic# +6615#WoE Guild Coin# +6623#Rough Energy Crystal# +6624#Purified Energy Crystal# +6625#High Energy Crystal# +6635#Blacksmith Blessing# +6636#STR Stone (Upper)# +6637#INT Stone (Upper)# +6638#AGI Stone (Upper)# +6639#DEX Stone (Upper)# +6640#VIT Stone (Upper)# +6641#LUK Stone (Upper)# +6642#ATK Stone (Middle)# +6643#MATK Stone (Middle)# +6644#HIT Stone (Lower)# +6645#FLEE Stone (Lower)# +6648#Old Crown# +6649#Broken Horn# +6650#Old Ring# +6651#Rusty Bracelet# +6652#Old Photo Album# +6653#Old Pill# +6658#Halloween Coin# +6665#RWC Enchant Reset Ticket# +6671#Geffen Magic Contest Coin# +6672#Gray Shard# +6681#XMAS Cookie# +6682#Bag Of Selling Goods# +6683#Thirsty Flower# +6684#Token Of Hero# +6685#Morocc Merit# +6686#Brick# +6687#Long Rope# +6688#Wood# +6689#Burning Bug Skin# +6690#Yummy Stem# +6691#Burning Feather# +6692#Patrol Log# +6693#Stone Of Blessing# +6694#Monster Blood# +6695#Golem's Fiery Stone Tooth# +6696#Frilldora's Fiery Nape# +6698#Hatchet# +6699#Faith Silence# +6700#White Snake's Scales# +6701#Dwarf's Treasure# +6702#Dwarf's Sweat# +6703#Warrior's Tears# +6704#Warrior's Anger# +6705#Warrior's Medal# +6706#Guardian's Flower# +6707#Cash Hair Coupon# +6708#Mana crystal# +6712#Love Wand# +6713#Soul Of Ahat# +6714#Soul Of Shnaim# +6715#Darklord Soulpiece# +6716#CRIT Stone (Upper)# +6717#Stamina Stone (Middle)# +6718#Magic Stone (Lower)# +6719#Tooth Of Jitterbug# +6740#Recovery Stone (Upper)# +6741#Recovery Skill Stone (Upper)# +6742#Recovery Stone (Middle)# +6743#HP Stone (Middle)# +6744#SP Stone (Middle)# +6745#Recovery Stone (Lower)# +6746#Steel Artifact# +6747#Steel Artifact# +6748#Excavator Report# +6749#Power Control Device# +6750#Broken Engine# +6751#Dented Iron Plate# +6752#Charleston Component# +6753#Doom Token# +6754#Gathered Herb# +6755#Contaminated Magic# +6756#Enriched Energy# +6757#Memory Record# +6767#Summer Fes Coin# +6770#Shark# +6771#Tuna# +6772#Octopus# +6773#Snapper# +6774#Piranha# +6775#Salmon# +6776#Eels# +6777#Carp# +6778#Squid# +6779#Mackerel# +6780#Crucian Carp# +6781#Living Earthworm# +6782#Fresh Lobster# +6790#Large Stone (Upper)# +6791#Medium Stone (Upper)# +6792#Small Stone (Upper)# +6797#10th Anniversary Coin# +6803#Shard of Gigantes# +6804#Organic Pumpkin# +6805#INORGANIC PUMPKIN# +6814#Soul of Swordman# +6815#Soul of Merchant# +6816#Soul of Thief# +6817#Soul of Mage# +6818#Soul of Archer# +6819#Soul of Acolyte# +6820#Energy Fragment# +6821#Single Union Badge# +6825#Air Purifier Box# +6826#Fresh Grape# +6834#Legendary Fur# +6835#Legendary Mane# +6839#Gold Chocolate Coin# +6840#Silver Chocolate Coin# +6841#Bronze Chocolate Coin# +6842#Huge Jewelry# +6843#Thin Ring# +6844#Mild Stone# +6845#Natural Fertilizer# +6867#Big Bugbox# +6868#Medium Bugbox# +6869#Poring Convention Badge# +6882#Jakk's Present# +6892#Invasion Book# +6893#To Serve Man# +6894#Champion Badge# +6895#Processed Ancient Rune# +6896#Processed Mystic Rune# +6897#Blue Whale# +6898#Santa Claws Token# +6899#Giant Octopus# +6900#Giant Squid# +6901#Sturgeon# +6902#King Shrimp# +6903#King Earthworm# +6908#ASPD Stone (Garment)# +6909#Nyangvine# +6910#Limited Highly Enriched Oridecon# +6911#Limited Highly Enriched Elunium# +6913#Sacred Rosary# +6914#Black Soul# +6915#Captured Soul# +6919#Honor Token# +6920#Rune Magic Powder# +6921#Dehumidifier# +6922#Sandpaper# +6923#Bright Light# +6924#Red Eye# +6925#Prisoner's Letter# +6926#Rune-Midgartz History Book# +6927#Sea Stone# +6928#Poring Loofah# +6929#Seal Stamped Letter# +6930#Sample of New Business Item# +6931#Top Secret Document# +6932#Rare Old Book# +6933#Banquet Invitation# +6934#Very Exquisite Food# +6935#High-Class Dish# +6936#Chilly Nucleus# +6937#Soft Skin# +6938#Hedgehog Spines# +6939#Worn-Out Belt# +6940#Moving Black Material# +6941#Valkyrie's Power Fragment# +6942#Master's Will# +6943#ATK Stone (Upper)# +6944#MATK Stone (Upper)# +6945#STR Stone (Middle)# +6946#INT Stone (Middle)# +6947#AGI Stone (Middle)# +6948#DEX Stone (Middle)# +6949#VIT Stone (Middle)# +6950#LUK Stone (Middle)# +6951#HP Stone (Lower)# +6956#Captured Sheep# +6957#Soft Wool# +6958#Lamb Horn# +6959#Second Costume Ticket# +6960#Sky Fortress Key# +6963#HP Absorption Stone (Garment)# +6964#SP Absorption Stone (Garment)# +6999#HP Absorption Stone(Upper)# +7001#Mould Powder# +7002#Ogre Tooth# +7003#Anolian Skin# +7004#Mud Lump# +7005#Skull# +7006#Wing of Red Bat# +7007#Claw of Rat# +7008#Stiff Horn# +7009#Glitter Shell# +7010#Tail of Steel Scorpion# +7011#Claw of Monkey# +7012#Tough Scalelike Stem# +7013#Coral Reef# +7014#Old Portrait# +7015#Bookclip in Memory# +7016#Spoon Stub# +7017#Executioner's Mitten# +7018#Young Twig# +7019#Loki's Whispers# +7020#Mother's Nightmare# +7021#Foolishness of the Blind# +7022#Old Hilt# +7023#Blade Lost in Darkness# +7024#Bloody Edge# +7025#Lucifer's Lament# +7026#Key of Clock Tower# +7027#Key of Underground# +7028#Invite for Duel# +7029#Admission for Duel# +7030#Claw of Desert Wolf# +7031#Old Frying Pan# +7032#Piece of Egg Shell# +7033#Poison Spore# +7034#Red Stocking# +7035#Matchstick# +7036#Fang of Hatii# +7037#Coupon# +7038#Yarn# +7039#Newbie Tag# +7040#Megaphone# +7041#Fine Grit# +7042#Leather Bag of Infinity# +7043#Fine Sand# +7044#Vigorgra# +7045#Magic Paint# +7046#Cart Parts# +7047#Alice's Apron# +7048#Talon of Griffon# +7049#Stone# +7050#Cotton Mat# +7051#Silk Mat# +7052#Old Papers# +7053#Cyfar# +7054#Brigan# +7055#Animal Excrement# +7056#Kafra Employee Paystub# +7057#Gjallar# +7058#Gleipnir# +7059#Free Ticket for Kafra Storage# +7060#Free Ticket for Kafra Transportation# +7061#Free Ticket for the Cart Service# +7062#Broken Turtle Shell# +7063#Soft Feather# +7064#Wing of Dragonfly# +7065#Sea-otter Fur# +7066#Ice Cubic# +7067#Stone Fragment# +7068#Burnt Tree# +7069#Destroyed Armor# +7070#Broken Shell# +7071#Tattered Clothes# +7072#Old Shuriken# +7073#Freya's Jewel# +7074#Thor's Gauntlets# +7075#Iron Maiden# +7076#Wheel of the Unknown# +7077#Silver Ornament# +7078#Wrath of Valkyrie# +7079#Feather of Angel Wing# +7080#Cat Tread# +7081#Woman's Moustache# +7082#Root of Stone# +7083#Spirit of Fish# +7084#Sputum of Bird# +7085#Sinew of Bear# +7086#Emblem of the Sun God# +7087#Breath of Spirit# +7088#Snow Crystal# +7089#Omen of Tempest# +7090#Ripple# +7091#Billow# +7092#Drifting Air# +7093#Cogwheel# +7094#Fragment# +7095#Metal Fragment# +7096#Lava# +7097#Burning Heart# +7098#Live Coal# +7099#Worn-out Magic Scroll# +7100#Sharp Leaf# +7101#PecoPeco Feather# +7102#Nightmare# +7103#Unknown Liquid Bottle# +7104#False Angel Wing# +7105#False Heaven Ring# +7106#Antelope Horn# +7107#Antelope Skin# +7108#Piece of Shield# +7109#Shining Spear Blade# +7110#Broken Sword# +7111#Slick Paper# +7112#Sharp Paper# +7113#Broken Pharaoh Emblem# +7114#Masque of Tutankhamen# +7115#Harpy Feather# +7116#Harpy Talon# +7117#Torn Magic Book# +7118#Torn Scroll# +7119#Bacillus# +7120#Burning Horseshoe# +7121#Honey Pot# +7122#Burning Hair# +7123#Dragon Skin# +7124#Sand Clump# +7125#Scorpion Claw# +7126#Large Jellopy# +7127#Alcohol Creation Guide# +7128#Bottle Grenade Creation Guide# +7129#Acid Bottle Creation Guide# +7130#Plant Bottle Creation Guide# +7131#Marine Sphere Creation Guide# +7132#Glistening Coat Creation Guide# +7133#Condensed Potion Creation Guide# +7134#Medicine Bowl# +7135#Bottle Grenade# +7136#Acid Bottle# +7137#Plant Bottle# +7138#Marine Sphere Bottle# +7139#Glistening Coat# +7140#Seed of Life# +7141#Morning Dew of Yggdrasil# +7142#Embryo# +7143#Glass Tube# +7144#Potion Creation Guide# +7145#Ragnarok T-shirt# +7146#Vacation Ticket# +7147#Jasmine# +7148#Mother's Letter# +7149#Yellow Plate# +7150#Piece of Bamboo# +7151#Oil Paper# +7152#Glossy Hair# +7153#Worn-out Kimono# +7154#Poisonous Powder# +7155#Poisonous Toad Skin# +7156#Broken Shuriken# +7157#Dark Mask# +7158#Broken Liquor Jar# +7159#Tengu Nose# +7160#Feudal Lord Permit# +7161#Black Bear Skin# +7162#Cloud Crumb# +7163#Hard Feeler# +7164#Solid Peach# +7165#Transparent Celestial Robe# +7166#Soft Silk# +7167#Strange Steel Piece# +7168#Giant Butterfly Wing# +7169#Ba Gua# +7170#Tuxedo# +7171#Leopard Skin# +7172#Leopard Claw# +7173#iROGM02's Backpack# +7174#Wrapping Lace# +7175#Wrapping Paper# +7176#Royal Certificate# +7177#Crumb of Sobbing Starlight# +7178#Sobbing Starlight# +7179#Proof of Donation# +7180#Hahn Sukbong's Recommendation# +7181#Receipt# +7182#Cacao# +7183#Letter from Sister# +7184#Piano Key# +7185#Quiz Entry# +7186#Thin Trunk# +7187#Festival Mask# +7188#Brown Root# +7189#Wooden Heart# +7190#Solid Husk# +7191#Lamp# +7192#Vane# +7193#Sprout# +7194#Soft Blade of Grass# +7195#Slingshot# +7196#Shoulder Protector# +7197#Tough Vines# +7198#Huge Leaf# +7199#12th Anniversary Coupon# +7200#Elastic Band# +7201#Log# +7202#Pincher of Beetle# +7203#Strong Branch# +7204#Gunpowder# +7205#Piece of Black Cloth# +7206#Black Cat Doll# +7207#Old Manteau# +7208#Rusty Kitchen Knife# +7209#Helm of Dullahan# +7210#Armor Piece of Dullahan# +7211#Fragment of Rossata Stone# +7212#Hung Doll# +7213#Needle Packet# +7214#Bat Cage# +7215#Broken Needle# +7216#Red Muffler# +7217#Spool# +7218#Decomposed Rope# +7219#Striped Sock# +7220#Ectoplasm# +7221#Tangled Chains# +7222#Wooden Gnarl# +7223#Contorted Self-Portrait# +7224#Red Stone# +7225#Pumpkin Lantern# +7226#Pellet# +7227#TCG Card# +7228#Gold Bullion# +7229#Silver Bullion# +7230#Platinum Bullion# +7231#Gold Ore# +7232#Silver Ore# +7233#Mithril Ore# +7234#Spirit of Guild# +7235#Spirit of Charge# +7236#Spirit of Protection# +7237#Spirit of Association# +7238#Spirit of Coordination# +7239#Spirit of Advance# +7240#Spirit of Trust# +7241#Spirit of Union# +7242#Spirit of Combination# +7243#Spirit of Cooperation# +7244#Spirit of Solidarity# +7245#Spirit of Friendship# +7246#Spirit of Peace# +7247#Spirit of Determination# +7248#Spirit of Honor# +7249#Spirit of Service# +7250#Spirit of Glory# +7251#Spirit of Victory# +7252#Herbal Medicine# +7253#Golden Korean Flag# +7254#Digital Picture Printing Coupon# +7255#Mystic Orb# +7256#Mystic Orb# +7257#Mystic Orb# +7258#Mystic Orb# +7259#Mystic Orb# +7260#Mystic Orb# +7261#Mystic Orb# +7262#Folding Fan of Cat Ghost# +7263#Cat's Eye# +7264#Dry Sand# +7265#Dragon Horn# +7266#Denture from Dragon Mask# +7267#Tiger Panty# +7268#Little Ghost Doll# +7269#Pinafore# +7270#Nursing Bottle# +7271#Novice Figure# +7272#Rice Ball Doll# +7273#RWC Necklace# +7274#Translated Ancient Language# +7275#Record of Ancient Language# +7276#Doodled Message# +7277#Munak Doll# +7278#Letter to Wife# +7279#Vita500 Lid# +7280#1st Quiz Entry# +7281#2nd Quiz Entry# +7282#3rd Quiz Entry# +7283#4th Quiz Entry# +7284#5th Quiz Entry# +7285#Holy Threads# +7286#Red Chile# +7287#Holier Threads# +7288#Engagement Ring# +7289#Peridot# +7290#Phlogopite# +7291#Agate# +7292#Muscovite# +7293#Rose Quartz# +7294#Turquoise# +7295#Citrin# +7296#Pyroxene# +7297#Biotite# +7298#Fig Leaf# +7299#Straw Basket# +7300#Gemstone# +7301#Tassel# +7302#Krathong# +7303#Straw Rice Bag# +7304#Witch's Spell Scroll# +7305#Symbol of the Nine Realms# +7306#Piece of Spirit# +7307#Spiritual Whispers# +7308#Witch's Tonic# +7309#Crow Wing# +7310#Free Ticket for Peco Ride# +7311#Free Ticket for Flyship# +7312#Jubilee# +7313#Witch's Medal# +7314#The Sign# +7315#Dark Crystal Fragment# +7316#Insect Leg# +7317#Rusty Screw# +7318#Old Pick# +7319#Used Iron Plate# +7320#Dust Pollutant# +7321#Crystal Fragment# +7322#Toxic Gas# +7323#Battered Kettle# +7325#Flexible Tube# +7326#Fluorescent Liquid# +7327#Flashlight# +7328#Legend of Songkran# +7329#Old Bronze Key# +7331#Heaven Flower# +7332#Complete Tablet# +7333#Prontera Tablet# +7334#Payon Tablet# +7335#Morroc Tablet# +7336#Geffen Tablet# +7337#Eye of Hellion# +7338#One-way Ticket# +7339#Commemorative Travel Card# +7340#Will of the Darkness# +7341#Old Pendant# +7342#File Folder# +7343#Sealed File Folder# +7344#Shinokas Case File# +7345#Handcuffs# +7346#Ymir's Heart Piece# +7347#Research Chart# +7348#Membership Card# +7349#Archive Permit# +7350#Pass# +7351#Friend's Diary# +7352#Transparent Plate (Green)# +7353#Transparent Plate (Red)# +7354#Transparent Plate (Orange)# +7355#Transparent Plate (Blue)# +7356#Crest Piece (Blue)# +7357#Crest Piece (Green)# +7358#Crest Piece (Orange)# +7359#Crest Piece (Purple)# +7360#RO Festival Invitation# +7361#Tattered Map 1# +7362#Tattered Map 2# +7363#Tattered Map 3# +7364#Tattered Map 4# +7365#Tattered Map 5# +7366#Tattered Map 6# +7367#Tattered Map 7# +7368#Tattered Map 8# +7369#Tattered Map 9# +7370#Tattered Map 10# +7371#Tattered Map 11# +7372#Tattered Map 12# +7373#Tattered Map 13# +7374#Tattered Map 14# +7375#Tattered Map 15# +7376#Tattered Map 16# +7377#Tattered Map 17# +7378#Tattered Map 18# +7379#Tattered Map 19# +7380#Tattered Map 20# +7381#Tattered Map 21# +7382#Tattered Map 22# +7383#Tattered Map 23# +7384#Tattered Map 24# +7385#Tattered Map 25# +7386#Tattered Map 26# +7387#Tattered Map 27# +7388#Tattered Map 28# +7389#Tattered Map 29# +7390#Tattered Map 30# +7391#Tattered Map 31# +7392#Tattered Map 32# +7393#Tattered Map 33# +7394#Tattered Map 34# +7395#Tattered Map 35# +7396#Tattered Map 36# +7397#Lotto Ball 37# +7398#Lotto Ball 38# +7416#Letter of Recommendation# +7417#Written Request(A)# +7418#Written Request(B)# +7419#Embryo Creation Guide# +7420#Skull# +7421#Red Key# +7422#Yellow Key# +7423#Blue Key# +7424#Green Key# +7425#Black Key# +7426#Red Charm Stone# +7427#Yellow Charm Stone# +7428#Blue Charm Stone# +7429#Green Charm Stone# +7430#Black Charm Stone# +7431#Pile of Books# +7432#Leather Pouch# +7433#Blank Scroll# +7434#Elemental Potion Creation Guide# +7435#Golden Ornament# +7436#Fragment of Agony# +7437#Fragment of Misery# +7438#Fragment of Hatred# +7439#Fragment of Despair# +7440#Red Feather# +7441#Blue Feather# +7442#Cursed Seal# +7443#Three-Headed Dragon's Head# +7444#Treasure Box# +7445#Green Bijou# +7446#Blue Bijou# +7447#Red Bijou# +7448#Yellow Bijou# +7449#Bloody Page# +7450#Skeletal Armor Piece# +7451#Fire Dragon Scale# +7452#Yellow Spice# +7453#Sweet Sauce# +7454#Savory Sauce# +7455#Spicy Sauce# +7456#Red Spice# +7457#Cooking Oil# +7460#Niflheim Express Ticket# +7461#Blue Card A# +7462#Blue Card E# +7463#Blue Card F# +7464#Blue Card H# +7465#Blue Card L# +7466#Blue Card N# +7467#Blue Card O# +7468#Blue P Card# +7469#Blue U Card# +7470#Blue Card W# +7471#Blue Y Card# +7472#Level 1 Cookbook# +7473#Level 2 Cookbook# +7474#Level 3 Cookbook# +7475#Level 4 Cookbook# +7476#Level 5 Cookbook# +7477#Level 6 Cookbook# +7478#Level 7 Cookbook# +7479#Level 8 Cookbook# +7480#Level 9 Cookbook# +7481#Level 10 Cookbook# +7482#Pot# +7483#Key of the Seal# +7484#GM Bond# +7487#Culinary Wine# +7488#Delivery Package# +7489#Cottage Key# +7490#Letter to Elly# +7491#Steel Box# +7492#Yellow Keycard# +7493#Golden Key# +7494#Luxurious Button# +7495#Blue Keycard# +7496#Red Keycard# +7497#Metal Fragment# +7498#Rosimier Mansion Keys# +7499#Family Portrait# +7500#Woman's Portrait# +7501#K.H's Letter# +7502#James's Note# +7503#Man's Portrait# +7504#Power Device# +7505#Toy Key# +7506#Black Keycard# +7507#Solid Iron Piece# +7508#Allysia's Ring# +7509#Luxurious Keycard# +7510#Valhala's Flower# +7511#Rune of Darkness# +7512#Burnt Part# +7513#Pocket Watch# +7514#Monster Ticket# +7515#Prize Medal# +7516#Green Keycard# +7517#Gold Coin# +7521#Flame Stone# +7522#Ice Stone# +7523#Wind Stone# +7524#Shadow Orb# +7526#Manuscript Paper# +7530#Travel Brochure (Amatsu)# +7531#Travel Brochure (Kunlun)# +7532#Travel Brochure (Louyang)# +7533#Travel Brochure (Ayothaya)# +7534#Amatsu Completed Photo Album# +7535#Kunlun Completed Photo Album# +7536#Louyang Completed Photo Album# +7537#Ayothaya Completed Photo Album# +7539#Poring Coin# +7540#Lotto Ball 39# +7541#Lotto Ball 40# +7542#Lotto Ball 41# +7543#Lotto Ball 42# +7544#Lotto Ball 43# +7545#Lotto Ball 44# +7546#Lotto Ball 45# +7548#Soccer Shoes# +7553#Water Lily# +7554#Striped Candle# +7555#Green Incense# +7561#Glacial Heart# +7562#Ice Scale# +7563#Bloody Rune# +7564#Rotten Meat# +7565#Sticky Poison# +7566#Will of Red Darkness# +7567#Suspicious Hat# +7568#White Mask# +7569#Wind Hammer# +7570#Temple Lottery Ticket# +7571#Bruspetti's Diary# +7572#Ashy Necklace# +7573#Sparkling Necklace# +7574#Freezing Snow Powder# +7575#Red Jewel# +7576#Blue Jewel# +7577#Yellow Jewel# +7594#Sonia's Letter# +7598#Blue Card I# +7599#Blue Card D# +7600#Blue K Card# +7601#Blue Card S# +7602#Blue Card R# +7604#Flour# +7605#Chicken Egg# +7606#Token of the Ox# +7609#Pumpkin Mojo# +7613#Small Rice Cake Dough# +7619#Old Enriched Elunium# +7620#Old Enriched Oridecon# +7621#Token Of Siegfried# +7622#Hairstyle Coupon# +7629#Pink Gift Box# +7630#Clean Beach Broom# +7631#Trash Debris# +7632#Terrible Report Card# +7633#Heavensent Report Card# +7634#Father Rose# +7635#Incense Bag# +7636#Mystic Vial# +7640#Butterfly Hairclip# +7641#Cure Box# +7642#Bloody Coin# +7643#Bloody Letter# +7644#Unsent Mail# +7663#Full Metal Jacket# +7664#Projectile Mine# +7665#Dragon Tail Missile# +7666#Time Travel Scroll# +7667#Abandoned Machine# +7668#Clean Bandage# +7669#Rubbing Alchohol# +7670#Sour Grass# +7671#Firstaid Kit# +7672#Relief Food# +7685#Sour Melon# +7704#Broken Thermometer# +7705#Note of Geologist# +7715#Hand-made Chocolate Recipe# +7716#Chocolate Strawberry Recipe# +7717#Chocolate Tart Recipe# +7718#Cacao Bean# +7719#Blue G Card# +7720#Gold Coin# +7721#Treasure Box# +7722#Debt Note# +7723#Diamond of Ruin# +7724#Forbidden Secret Art# +7725#Unlucky Emerald# +7726#Token of King# +7729#Rok Star Badge# +7730#Mission Ticket 1# +7731#Mission Ticket 2# +7732#Mission Ticket 3# +7733#Mission Ticket 4# +7734#Mission Ticket 5# +7735#Mission Ticket 6# +7736#Mission Ticket 7# +7737#Mission Ticket 8# +7738#Mission Ticket 9# +7739#Mission Ticket 10# +7740#Mission Ticket 11# +7741#Mission Ticket 12# +7742#Kaong# +7743#Gulaman# +7744#Leche Flan# +7745#Ube Jam# +7746#Sago# +7747#Langka# +7748#Sweet Beans# +7749#Sweet Bananas# +7750#Macapuno# +7751#Old White Cloth# +7752#Clattering Skull# +7753#Broken Farming Utensil# +7754#Broken Crown# +7755#Research Note# +7756#Sealed Book# +7760#Yaga's Magic Book# +7761#Magic Gourd Bottle# +7762#Yaga's Pestle# +7763#Sticky Herb# +7764#High Strength Adhesive# +7765#Baba Yaga's Secret Medicine# +7766#Bok Choy# +7768#Squid# +7769#Egg Yolk# +7770#Sweet Rice# +7772#String# +7773#War Badge# +7776#Gym Pass# +7782#Episode 13.1 Poporing Key# +7783#Episode 13.2 Poring Key# +7794#The Crow of Destiny# +7795#Mammi's Photo Album# +7796#Author's Autograph# +7797#Author's Memo# +7798#Fragment of Darkness# +7799#Crystal of Darkness# +7820#Piece of Morocc Skin# +7821#Green Apple# +7822#Whole Barbecue# +7823#Meat Veggie Skewer# +7824#Spirit Liquor# +7826#Continental Guard Paper# +7828#Bravery Badge# +7829#Valor Badge# +7830#Goddess Tear# +7831#Valkyrie's Token# +7832#Brynhild Armor Piece# +7833#Hero Remains# +7834#Andvari's Ring# +7835#Dusk Glow# +7836#Dawn Essence# +7837#Cold Moonlight# +7838#Hazy Starlight# +7839#Crystal Key# +7840#Valkyrie's Gift# +7850#Wooden Block# +7864#Stolen Cacao# +7869#Rice Pouch# +7875#Pirate Treasure# +7876#Golden Key# +7877#Red Ring# +7878#Lusalka's Hair# +7879#Golden Thread# +7880#Baba Yaga's Silver Spoon# +7881#Mystery Magic Book# +7882#Sharp Branch# +7883#Wooden Flute# +7891#Mug# +7906#Poppy Wreath# +7908#Louise's Beauty Coupon# +7909#Stolen Cookie# +7910#Stolen Candy# +7911#Yulia's Hat# +7912#Portable Snowman Machine# +7915#Copper Coin# +7916#Silver Coin# +7919#Festival Ticket# +7923#Krathong# +7925#Powerful Dimensional Essence# +7931#Poison Kit# +7932#Poison Herb Nerium# +7933#Poison Herb Rantana# +7934#Poison Herb Makulata# +7935#Poison Herb Seratum# +7936#Poison Herb Scopolia# +7937#Poison Herb Amoena# +7938#Light Granule# +7939#Elder Branch# +7940#Special Alloy Trap# +7941#Halloween Ticket# +7946#Gold Ring Of Valentine# +7947#Silver Ring Of Valentine# +7948#Box# +7959#UP Coin# +7960#Ancient Coin# +7974#Misty's Illusion Piece# +7975#Cupid's Chocolate# +9001#Poring Egg# +9002#Drops Egg# +9003#Poporing Egg# +9004#Lunatic Egg# +9005#Picky Egg# +9006#Chonchon Egg# +9007#Steel Chonchon Egg# +9008#Hunter Fly Egg# +9009#Savage Bebe Egg# +9010#Baby Desert Wolf Egg# +9011#Rocker Egg# +9012#Spore Egg# +9013#Poison Spore Egg# +9014#Peco Peco Egg# +9015#Smokie Egg# +9016#Yoyo Egg# +9017#Orc Warrior Egg# +9018#Munak Egg# +9019#Dokebi Egg# +9020#Sohee Egg# +9021#Isis Egg# +9022#Ground Petite Egg# +9023#Deviruchi Egg# +9024#Baphomet Jr. Egg# +9025#Bongun Egg# +9026#Zealotus Egg# +9027#Alice Egg# +9028#Hard Rice Cake Egg# +9029#Christmas Goblin Egg# +9030#Green Maiden Egg# +9031#Eclipse Egg# +9032#Dagger Goblin Egg# +9033#Flail Goblin Egg# +9034#Hammer Goblin Egg# +9035#Ground Deleter Egg# +9036#Diabolic Egg# +9037#Wanderer Egg# +9038#New Year Doll Egg# +9039#White Lady Egg# +9040#Mao Guai Egg# +9041#Leaf Cat Egg# +9042#Loli Ruri Egg# +9043#Marionette Egg# +9044#Shinobi Egg# +9045#Whisper Egg# +9046#Goblin Leader Egg# +9047#Evil Nymph Egg# +9048#Miyabi Doll Egg# +9049#Dullahan Egg# +9050#Medusa Egg# +9051#Stone Shooter Egg# +9052#Incubus Egg# +9053#Golem Egg# +9054#Nightmare Terror Egg# +9055#Succubus Egg# +9056#Imp Egg# +9057#Taini Egg# +9058#Christmas Snow Rabbit Egg# +9059#Tikbalang Egg# +9062#Little Poring Egg# +9069#Mastering Egg# +9070#Savage Egg# +9071#Grand Peco Peco Egg# +9087#High Orc Egg# +9088#Angeling Egg# +9089#Am Mut Egg# +9090#Little Isis Egg# +9091#Choco Egg# +9092#Eggring Egg# +9093#Yao Jun Egg# +9094#Leaf Lunatic Egg# +9095#Nine Tail Egg# +9096#Cat o' Nine Tails Egg# +9097#Evolved Diabolic Egg# +9098#Earth Deleter Egg# +9099#Teddy Bear Egg# +9100#Gremlin Egg# +9101#Skeleton Transfer Station# +9102#Mummy Egg# +9103#Willow Egg# +9104#Roween Egg# +9105#Hodremlin Egg# +9106#Metaller Egg# +9107#Ancient Mummy Egg# +9108#Abandoned Teddy Bear Egg# +9109#Sweets Drops Egg# +9111#Phreeoni Egg# +9112#Moonlight Egg# +9113#Roost of Skelion# +9115#BaekSoJin's Egg# +9123#Child Admin Beta Egg# +9124#Child Admin Alpha Egg# +9510#Costume Enchant Stone Box 17# +9514#Physical Modification Permit# +9515#Einbroch Weapon Box# +9517#Unidentified Ore# +9518#December Robotic Costume Box# +9523#Metal Refine Ticket# +9524#Overseas Care Package XXV# +9529#Magic Modification Permit# +9539#[Event] Blue Potion Box# +9543#January Costume Box# +9544#Overseas Care Package XXVI# +9550#Gemstone of Time# +9551#Unsealed Time Key# +9563#2019 February Costume Box# +9565#Overseas Care Package XXVII# +9587#[Event]Life Potion 20 Box# +9600#April 2019 Yggdrasil Ticket# +9610#[Event]Mysterious Water# +9611#Popcorn Festival Buff# +9618#2019 May Costume Box# +9620#May 2019 Yggdrasil Ticket# +9621## +9629#June 2019 Yggdrasil Ticket# +9630#June Costume Box 2019# +9636#2019 July Costume Box# +9645#July Animal Costume Box # +9654#July 2019 Yggdrasil Ticket# +9664#2019 August Costume Box# +9665#Yggdrasil Retro Ticket I# +9670#Yggdrasil Retro Ticket II# +9673#September 2019 Costume Box# +9702## +9736#Wing of Mistress# +9737#Transcendence Launch Box# +9923#Shiny Physical DEF Scroll# +9924#[Not for sale]Shiny Physical DEF Scroll# +10001#Skull Helmet# +10002#Monster Oxygen Mask# +10003#Transparent Head Protector# +10004#Pacifier# +10005#Wig# +10006#Queen's Hair Ornament# +10007#Silk Ribbon# +10008#Punisher# +10009#Wild Flower# +10010#Battered Pot# +10011#Stellar Hairpin# +10012#Tiny Egg Shell# +10013#Poring Pet Backpack# +10014#Rocker Glasses# +10015#Green Lace# +10016#Golden Bell# +10017#Bark Shorts# +10018#Monkey Circlet# +10019#Red Scarf# +10020#Grave Keeper's Sword# +10022#Golden Earring# +10023#Green Lucky Bag# +10024#Fashionable Glasses# +10025#Star Hairband# +10026#Exotic Tassel# +10027#Spirit Chain# +10028#Nice Badge# +10029#Jade Ornament# +10030#Summer Fan # +10031#Ring of Death# +10032#Queen's Coronet# +10033#Afro Hair# +10034#Ball Mask# +10035#Windup Spring# +10036#Hell Horn# +10037#Black Butterfly Mask# +10038#Horn Barrier# +10040#Red Bell Necklace# +10042#Dark Mane# +10043#Little Headdress Beta# +10044#Little Headdress Alpha# +10516#Overseas Care Package XXVIII# +10517#2019 March Costume Box# +10520#April Costume Box# +10523#April Hair Costume Box# +11000#History book of Prontera# +11001#Adventure Story Vol.1# +11002#Chef King Orleans Vol.1# +11003#Kafra Legend Vol.1# +11004#Old Book# +11005#Rune Royal Family Book# +11006#Blood Flower Vol.1# +11007#Blood Flower Vol.2# +11008#Biographical Dictionary Copy Edition# +11009#Adventure Story Vol.2# +11011#Varmunt's Note# +11012#Expedition Report# +11013#Expedition Report Vol1# +11014#Expedition Report Vol2# +11015#Expedition Report Vol3# +11016#Expedition Report Vol4# +11018#Splendide Selling Item# +11019#Manuk Selling Item# +11022#Mix Cooking Cookbook# +11023#How to Increase Stamina# +11024#How to Increase Vitality# +11056#Spiritualism Guide# +11057#February Sweets# +11058#Novice Combination Book# +11061#Honor Token Exchange List# +11500#Light Yellow Potion# +11501#Light White Potion# +11502#Light Blue Potion# +11503#WoE White Potion# +11504#WoE Blue Potion# +11505#Iris# +11513#Throat Lozenge# +11514#Enriched Slim Potion# +11515#Coconut# +11516#Asai Fruit# +11517#Purification Potion# +11518#Novice Blue Potion# +11519#Bifrost# +11520#Mora Mandarin# +11521#Pinguicula Berry Juice# +11522#Red Raffle Sap# +11523#Yellow Raffle Sap# +11524#White Raffle Sap# +11525#Mora Hip Tea# +11526#Rafflecino# +11527#Baklava# +11532#Nasi Goreng# +11533#Satay# +11534#Coconut Juice# +11536#Cat's Ship Biscuit# +11537#Weevil Bug Worm# +11538#Fresh Octopus Leg# +11547#WoE Violet Potion# +11548#WoE White Potion# +11549#WoE Blue Potion# +11550#Pumpkin Cake# +11551#Savory Herb Salad (Str+1)# +11552#Apple Carrot Salad (Agi+1)# +11553#Casual Stew (Vit+1)# +11554#Golden Roasted Apple (Dex+1)# +11557#TE White Potion# +11558#TE White Slim Potion# +11563#Hot Tea# +11564#Sweet Canape# +11565#[Event] White Potion# +11566#[Event] Yellow Potion# +11567#[Event] Novice Potion# +11568#[Event] Red Slim Potion# +11569#[Event] Orange Potion# +11570#[Event] Red Potion# +11571#[Event] Green Potion# +11572#[Event] Blue Potion# +11573#Jolly Condensed White Potion# +11574#[Event] Yellow Slim Potion# +11575#(Not for Sale) Lucky Cookie# +11578#(Not for Sale) Lucky Candy Cane# +11580#(Not for Sale) Lucky Candy# +11581#(Not for Sale) Piece of Cake# +11583#Jolly Chocolate Drink# +11584#[Not for Sale] White Chocolate# +11585#Hand-made Chocolate [Event]# +11586#Handmade White Chocolate [Event]# +11589#Iceflake# +11592#Trans Candy Red# +11593#Trans Candy Blue# +11594#Trans Candy Yellow# +11595#Trans Candy Green# +11596#Blood In Skull# +11597#Metalbug# +11598#Bitter Cacao Bean# +11600#Shining Holy Water# +11601#Delicious Anchovy# +11602#Nepeta Cataria# +11605#Cookie Bats# +11606#[Not for Sale] Light White Potion# +11607#Sweets Crepe# +11608#Chocolate Egg# +11609#Yummy Cookie Egg# +11610#Sweet Melon# +11611#Popcorn# +11612#Aromatic Popcorn# +11613#Harvest Biscuit# +11616#Yummy Meat# +11617#Silvervine Chocoball# +11618#Chocolate Egg# +11619#Yummy Coockie Egg# +11621#Red Syrup# +11622#Yellow Syrup# +11623#White Syrup# +11624#Blue Syrup# +11625#Gourmet Butter Popcorn# +11706#Steak# +11707#Roast Beef# +12000#Level 5 Frost Diver# +12001#Level 3 Heal# +12002#Level 5 Heal# +12003#Level 1 Teleport# +12004#Quiver# +12005#Iron Arrow Quiver# +12006#Steel Arrow Quiver# +12007#Oridecon Arrow Quiver# +12008#Fire Arrow Quiver# +12009#Silver Arrow Quiver# +12010#Wind Arrow Quiver# +12011#Stone Arrow Quiver# +12012#Crystal Arrow Quiver# +12013#Shadow Arrow Quiver# +12014#Immaterial Arrow Quiver# +12015#Rusty Arrow Quiver# +12016#Speed Potion# +12017#Slow Potion# +12018#Firecracker# +12019#Saint Egg# +12020#Cursed Water# +12021#Pork# +12022#Galbi# +12023#Wrapped Box# +12024#Red Pouch# +12025#Dano Festival Egg# +12026#Dano Festival Egg# +12027#Giggling Box# +12028#Box of Thunder# +12029#Box of Gloom# +12030#Box of Resentment# +12031#Box of Drowsiness# +12032#Box of Storms# +12033#Box of Sunlight# +12034#Box of Panting# +12035#Lotto Box 01# +12036#Lotto Box 02# +12037#Lotto Box 03# +12038#Lotto Box 04# +12039#Lotto Box 05# +12040#Stone of Sage# +12041#Fried Grasshopper Legs# +12042#Seasoned Sticky Webfoot# +12043#Bomber Steak# +12044#Herb Marinade Beef# +12045#Lutie Lady's Pancake# +12046#Grape Juice Herbal Tea# +12047#Autumn Red Tea# +12048#Honey Herbal Tea# +12049#Morroc Fruit Wine# +12050#Mastela Fruit Wine# +12051#Steamed Crab Nippers# +12052#Assorted Seafood# +12053#Clam Soup# +12054#Seasoned Jellyfish# +12055#Spicy Fried Bao# +12056#Frog Egg Squid Ink Soup# +12057#Smooth Noodle# +12058#Tentacle Cheese Gratin# +12059#Lutie Cold Noodle# +12060#Steamed Bat Wing in Pumpkin# +12061#Honey Grape Juice# +12062#Chocolate Mousse Cake# +12063#Fruit Mix# +12064#Cream Sandwich# +12065#Green Salad# +12066#Fried Monkey Tails# +12067#Mixed Juice# +12068#Fried Sweet Potato# +12069#Steamed Ancient Lips# +12070#Fried Scorpion Tails# +12071#Shiny Marinade Beef# +12072#Whole Roast# +12073#Bearfoot Special# +12074#Tendon Satay# +12075#Steamed Tongue# +12076#Red Mushroom Wine# +12077#Special Royal Jelly Herbal Tea# +12078#Royal Family Tea# +12079#Tristram 12# +12080#Dragon Breath Cocktail# +12081#Awfully Bitter Bracer# +12082#Sumptuous Feast# +12083#Giant Burito# +12084#Ascending Dragon Soup# +12085#Immortal Stew# +12086#Chile Shrimp Gratin# +12087#Steamed Alligator with Vegetable# +12088#Incredibly Spicy Curry# +12089#Special Meat Stew# +12090#Steamed Desert Scorpions# +12091#Peach Cake# +12092#Soul Haunted Bread# +12093#Special Toast# +12094#Heavenly Fruit Juice# +12095#Hwergelmir's Tonic# +12096#Lucky Soup# +12097#Assorted Shish Kebob# +12098#Strawberry Flavored Rice Ball# +12099#Blood Flavored Soda# +12100#Cooked Nine Tail's Tails# +12103#Bloody Branch# +12104#Random Quiver# +12105#Taming Gift Set# +12106#Jewelry Box# +12107#Wrapped Mask# +12108#Scroll Package# +12109#Poring Box# +12110#First Aid Kit# +12111#Bundle of Food# +12112#Tropical Sograt# +12113#Vermilion on the Beach# +12114#Elemental Converter (Fire)# +12115#Elemental Converter (Water)# +12116#Elemental Converter (Earth)# +12117#Elemental Converter (Wind)# +12118#Fireproof Potion# +12119#Coldproof Potion# +12120#Earthproof Potion# +12121#Thunderproof Potion# +12122#Sesame Pastry# +12123#Honey Pastry# +12124#Rainbow Cake# +12125#Outdoor Cooking Kit# +12126#Home Cooking Kit# +12127#Professional Cooking Kit# +12128#Royal Cooking Kit# +12129#Fantastic Cooking Kit# +12130#Cookie Bag# +12131#Lucky Potion# +12132#Santa's Bag# +12134#Red Envelope# +12135#Green Ale# +12144#Lightning Sphere Pack# +12145#Blind Sphere Pack# +12146#Poison Sphere Pack# +12147#Freezing Sphere Pack# +12148#Flare Sphere Pack# +12149#Bullet Case# +12150#Bloody Bullet Case# +12151#Silver Bullet Case# +12153#Bowman Scroll 1# +12154#Bowman Scroll 2# +12155#Bowman Scroll 3# +12156#Bowman Scroll 4# +12157#Bowman Scroll 5# +12158#Bowman Scroll 6# +12159#Bowman Scroll 7# +12160#Bowman Scroll 8# +12161#Bowman Scroll 9# +12162#Bowman Scroll 10# +12163#Fencer Scroll 1# +12164#Fencer Scroll 2# +12165#Fencer Scroll 3# +12166#Fencer Scroll 4# +12167#Fencer Scroll 5# +12168#Fencer Scroll 6# +12169#Fencer Scroll 7# +12170#Fencer Scroll 8# +12171#Fencer Scroll 9# +12172#Fencer Scroll 10# +12173#Spearman Scroll 1# +12174#Spearman Scroll 2# +12175#Spearman Scroll 3# +12176#Spearman Scroll 4# +12177#Spearman Scroll 5# +12178#Spearman Scroll 6# +12179#Spearman Scroll 7# +12180#Spearman Scroll 8# +12181#Spearman Scroll 9# +12182#Spearman Scroll 10# +12183#Holy Arrow Quiver# +12184#Mercenary Red Potion# +12185#Mercenary Blue Potion# +12192#Pumpkin Pie# +12194#Hometown Gift# +12195#Plain Rice Cake# +12196#Hearty Rice Cake# +12197#Salty Rice Cake# +12198#Lucky Rice Cake# +12200#Event Cake# +12202#Steamed Tongue# +12203#Steamed Scorpion# +12204#Dragon Breath Cocktail# +12205#Hwergelmir's Tonic# +12206#Cooked Nine Tail's Tails# +12207#Stew Of Immortality# +12208#Battle Manual# +12209#Life Insurance# +12210#Bubble Gum# +12211#Kafra Card# +12212#Giant Fly Wing# +12213#Neuralizer# +12214#Convex Mirror# +12215#LV10 Blessing Scroll# +12216#LV10 Agil Scroll# +12217#LV5 Aspersio Scroll# +12218#LV5 Assumptio Scroll# +12219#LV10 Wind Walker Scroll# +12220#LV5 Adrenaline Scroll# +12221#Megaphone# +12223#Sweets Macaron Cake# +12224#Sweets Strawberry Parfait# +12225#Sweet Candy Cane# +12234#Fierce Cacao 99%# +12235#Chocolate Strawberry# +12236#Chocolate Tart# +12237#Junky Chocolate# +12241#Mercenary Concentration Potion# +12242#Mercenary Awakening Potion# +12243#Mercenary Berserk Potion# +12245#Green Ale# +12246#Mystical Card Album# +12258#Bombring Capsule# +12259#Miracle Tonic# +12260#Cool Summer Outfit# +12262#Inspector Certificate# +12263#HE Battle Manual Plus# +12264#HE Bubble Gum Plus# +12269#Tasty Pink Ration# +12270#Tasty White Ration# +12271#Military Ration A# +12272#Military Ration B# +12273#Military Ration C# +12278#Alice Contract# +12290#Mysterious Can# +12291#Mysterious PET Bottle# +12292#Unripe Yggdrasil Berry# +12293#Dried Yggdrasil Berry# +12294#Costume: Blue Christmas Cheer Box# +12295#Groove Pack 1# +12296#Splendid Spring Hat Box# +12297#Expanded Party Buff Scroll Box# +12299#Mega Resist Potion# +12300#Wild Rose Contract# +12301#Doppelganger Contract# +12302#Egnigem Cenia Contract# +12310#Spray Of Flowers# +12311#Huge Spray Of Flowers# +12312#Thick Battle Manual# +12317#Snow Powder# +12319#Rune Strawberry Cake# +12320#Schwartzwald Pine Jubilee# +12321#Arunafeltz Desert Sandwich# +12322#Chocolate Pie# +12323#Novice Fly Wing# +12324#Novice Butterfly Wing# +12325#Novice Magnifier# +12326#Firecracker# +12329#Male Support Game Master# +12330#Female Support Game Master# +12333#Ancilla# +12340#Chewy Rice Powder# +12341#Special Alloy Trap Box# +12342#Manuk's Opportunity# +12343#Manuk's Courage# +12344#Pinguicula's Fruit Jam# +12345#Luciola's Honey Jam# +12346#Unripe Acorn# +12347#Acorn Jelly# +12348#Manuk's Faith# +12349#Cornus' Tears# +12350#Angeling Potion# +12353#Small Bottle# +12354#Buche De Noel# +12355#Xmas Gift# +12356#Louise Costume Box# +12357#Gingerbread Cookie# +12358#Fan of Wind# +12359#Very Soft Plant# +12360#Very Red Juice# +12361#Delicious Shaved Ice# +12362#Kuloren# +12363#Ghost Coffin# +12364#Staff of Leader# +12365#Charming Lotus# +12366#Girl's Doll# +12367#Luxury Whisky Bottle# +12368#Splendid Mirror# +12369#Oilpalm Coconut# +12370#Girl's Naivety# +12371#Magical Lithography# +12372#Hell Contract# +12373#Boy's Pure Heart# +12374#Ice Fireworks# +12376#Mysterious Can Mackerel Flavor# +12377#Mysterious PET Bottle Ocean Flavor# +12378#Boiled Octopus Rice Cake Soup# +12379#Pope Cookie# +12380#Job Change Flute# +12381#Ancient Language Scroll# +12382#Ancient Language Scroll# +12383#Vulcan Bullet Magazine# +12384#Rainbow Ruby# +12385#Rainbow Ruby# +12386#Rainbow Ruby# +12387#Rainbow Ruby# +12388#Rhydo Runestone For Apprentice# +12389#Pertz Runestone For Apprentice# +12390#Verkana Runestone For Apprentice# +12391#RWC Groove Pack# +12392#Repair A# +12393#Repair B# +12394#Repair C# +12395#Tantan Noodle# +12396#Gift Box?# +12397#Gift Box?# +12399#Castle Treasure Box# +12403#RWC Groove Pack 2# +12408#Hydra Ball# +12409#1st Class Pork Belly# +12410#Thick Pork Belly# +12411#HE Battle Manual# +12412#HE Bubble Gum# +12413#PC Cafe Coupon Box¥±# +12414#Guarana Candy# +12416#Thanksgiving Groove Pack# +12418#Full SwingK# +12419#Mana +# +12422#HP Increase Potion (Small)# +12423#HP Increase Potion (Medium)# +12424#HP Increase Potion (Large)# +12425#SP Increase Potion (Small)# +12426#SP Increase Potion (Medium)# +12427#SP Increase Potion (Large)# +12428#Enriched White PotionZ# +12429#Savage BBQ# +12430#Warg Blood Cocktail# +12431#Minor Brisket# +12432#Siroma Icetea# +12433#Drosera Herb Stew# +12434#Petite Tail Noodles# +12435#Black Thing# +12436#Vitata500# +12437#Enrich Celermine Juice# +12438#Giant Fly Wing# +12439#Old Battle Manual# +12440#Insurance# +12441#Old Bubble Gum# +12442#Kafra Card# +12443#Neuralizer# +12444#WoE Teleport Scroll# +12445#Steamed Tongue# +12446#Steamed Desert Scorpions# +12447#Dragon Breath Cocktail# +12448#Hwergelmir's Tonic# +12449#Nine Tail# +12450#Stew Of Immortality# +12451#Yellow Butterfly Wing# +12452#Green Butterfly Wing# +12453#Red Butterfly Wing# +12454#Blue Butterfly Wing# +12455#WoE Teleport Scroll# +12456#Greed Scroll# +12457#Glass Of Illusion# +12458#Abrasive# +12459#Medium Life Potion# +12460#Small Life Potion# +12461#Regeneration Potion# +12462#Large Magic Defense Potion# +12463#Small Magic Defense Potion# +12464#Big Defense Potion# +12465#Small Defense Potion# +12466#Level 10 Blessing Scroll# +12467#Level 10 Increase Agility Scroll# +12468#LV5 Aspersio Scroll# +12469#LV5 Assumptio Scroll# +12470#LV10 Wind Walker Scroll# +12471#LV5 Adrenaline Scroll# +12472#Convex Mirror# +12475#Cure Free# +12477#Prontera Costume Pack# +12488#Valkyrie Mercenary Scroll# +12490#Christmas Music Box# +12491#Curious Snowball# +12493#Groovy Dragon Box# +12494#[Event] Giant Fly Wing# +12495#[Event] Battle Manual# +12507#[Event]Yellow Butterfly Wing# +12508#Green Butterfly Wing# +12509#Red Butterfly Wing# +12510#Blue Butterfly Wing# +12512#[Event] Greed Scroll# +12513#[Event] Glass of Illusion# +12514#[Event] Abrasive# +12515#[Event]Medium Life Potion# +12516#[Event] Small Life Potion# +12517#[Event] Regeneration Potion# +12519#[Event] Small M. Defense Potion# +12521#[Event] Small Defense Potion# +12522#[Event] LV10 Blessing Scroll# +12523#[Event] LV10 Agil Scroll# +12524#[Event] LV5 Aspersio Scroll# +12525#[Event] LV5 Assumptio Scroll# +12530#Mastela Fruit Box# +12531#White Potion 100 Box# +12532#Royal Jelly 100 Box# +12533#Blue Herb 100 Box# +12534#Yggdrasil Seed 30 Box# +12535#Yggdrasilberry Box# +12536#Tako Rice Cake Soup# +12537#Gift Chocolate Basket# +12538#Firecracker Basket# +12539#Splendid Box# +12545#Fortune Egg¥´# +12548#Black Market Bulk Buyer Shop License# +12549#Condensed White Potion 200 Box# +12550#Poison Bottle 30 Box# +12553#Brisingamen Keep Box# +12554#Asprika Piece Box# +12555#Brynhild Piece Box# +12556#Sleipnir Keep Box# +12557#Mjolnir Keep Box# +12558#Megingjard Keep Box# +12561#Mysterious Seed# +12573#Fruit Basket# +12574#Mora Berry# +12575#Elven Arrow Quiver# +12576#Hunting Arrow Quiver# +12577#Rideword Hat Box# +12580#Window Shopper Catalogue# +12581#Bargain Hunter's Catalogue# +12582#WoE Supply Box# +12591#Black Market Catalogue# +12595#Incarnation Of Morocc Doll Box# +12596#Magic Candy# +12601#Cold Watermelon Juice# +12608#Splendid Box2# +12609#Old Ore Box# +12610#Mysterious Egg# +12612#Shabby Coin Bag# +12613#Premium Coin Bag# +12614#Normal Coin Bag# +12615#Poor Coin Bag# +12616#S Grade Coin Bag# +12617#Agrade Pocket# +12618#Bgrade Pocket# +12619#Cgrade Pocket# +12620#Dgrade Pocket# +12621#Egrade Pocket# +12622#Halter Lead# +12623#High Weapon Box# +12624#Delicious Jelly# +12633#Malang Cat Can# +12636#Malang Sp Can# +12637#Gong Bug Pocket# +12638#Dried Squid Box# +12639#Flying Fish Box# +12640#Starfish Box# +12641#New Recruit Pack# +12642#Fruit Of Mastela Box# +12643#E Coin Pack50# +12646#Poring Pretzel# +12647#Ink Ball# +12654#Recruit's Mount Gift# +12658#Transformation Scroll (Deviruchi)# +12659#Transformation Scroll (Raydric Archer)# +12660#Transformation Scroll (Mavka)# +12661#Transformation Scroll (Marduk)# +12662#Transformation Scroll (Banshee)# +12663#Transformation Scroll (Poring)# +12664#Transformation Scroll(Golem)# +12673#Safe to 7 Chance Box# +12674#God Material Box# +12675#WoE Weapon Supply Box# +12676#WoE Violet Potion 50 Box# +12677#WoE Arrow Quiver S# +12678#WoE Arrow Quiver A# +12679#WoE White Potion 30 Box# +12680#WoE Blue Potion 10 Box# +12683#WoE Violet Potion 200 Box# +12690#Headgear Card Album# +12691#Armor Card Album# +12692#Shield Card Album# +12693#Garment Card Album# +12694#Shoes Card Album# +12695#Accessory Card Album# +12696#RWC Celebration Firecracker# +12697#RWC Celebration Firecracker# +12698#Weapon Card Album# +12699#Tikbalang Harness# +12700#Inside-out Shirt# +12702#Old Navy Box# +12710#Guyak Pudding# +12715#Black Treasure Chest# +12717#Paralysis# +12718#Leech End# +12719#Oblivion Curse# +12720#Death Hurt# +12721#Toxin# +12722#Pyrexia# +12723#Magic Mushroom# +12724#Venom Bleed# +12725#Nosiege Runestone# +12726#Rhydo Runestone# +12727#Verkana Runestone# +12728#Isia Runestone# +12729#Asir Runestone# +12730#Urj Runestone# +12731#Turisus Runestone# +12732#Pertz Runestone# +12733#Hagalas Runestone# +12734#Quality Rough Runestone# +12735#Ancient Rough Runestone# +12736#Mystic Rough Runestone# +12737#General Rough Runestone# +12738#Rare Rough Runestone# +12739#Snow Flower# +12742#Valentine Gift Box# +12743#Valentine Gift Box# +12744#Chocolate Box# +12745#Vivid Notation# +12754#New Year Bun# +12775#Greater Agimat of Ancient Spirit# +12778#Baphomet Jr Ally Scroll# +12786#Character Position Change Coupon# +12790#Name Change Coupon# +12791#Combat Pill# +12806#Scaraba Scroll# +12812#Snow Flip# +12813#Peony Mommy# +12814#Slapping Herb# +12815#Yggdrasil Dust# +12817#Old Card Album# +12831#Potion Box# +12832#Mysterious Water# +12845#Amatsu Butterfly Wing# +12846#Little Unripe Apple# +12847#Old Equipment Box# +12848#Falcon Pipe# +12849#Combination Kit# +12853#Patron Scroll# +12854#Voluminous Spray of Flowers# +12858#RG Golden Potion# +12859#Spring Lucky Pack# +12860#Mommy Day Cake# +12873#TE Potion Box# +12874#Frost Giant Blood# +12875#Golem Stone# +12876#Elf Tear(Stun)# +12877#Elf Tear(Stone Curse)# +12878#Elf Tear(Freezing)# +12879#Elf Tear(Sleep)# +12880#Elf Tear(Curse)# +12881#Elf Tear(Silence)# +12882#Elf Tear(Darkness)# +12888#WoE Support Box# +12900#Battle Manual Box# +12901#Insurance Package# +12902#Bubble Gum Box# +12903#Steamed Tongue Box# +12904#Steamed Scorpion Box# +12905#Dragon Breath Cocktail Box# +12906#Hwergelmir's Tonic Box# +12907#Nine Tail Dish Box# +12908#Stew Of Immortality Box# +12909#Kafra Card Box# +12910#Giant Fly Wing Box# +12911#Neuralizer Box# +12912#Convex Mirror Box# +12913#Blessing Scroll Box# +12914#Increase AGI Scroll Box# +12915#Aspersio Scroll Box# +12916#Assumptio Scroll Box# +12917#Wind Walk Scroll Box# +12918#Adrenaline Scroll Box# +12919#Megaphone Box# +12920#Enriched Elunium 5 Box# +12921#Enriched Oridecon 5 Box# +12922#Token of Siegfried Box# +12923#December Lucky Box# +12924#Anniversary Destiny Crate# +12925#Kafra Item Mall Prize Package# +12926#Anniversary Destiny Box# +12929#Pet Egg Box 3# +12930#Pet Egg Box 4# +12931#Playspan 5x Easter Lucky Pack# +12932#Episode 13.2 Key Package# +12933#Summer Hat Pack# +12934#Easter Lucky Pack# +12935#Infiltrator Box# +12936#Muramasa Box# +12937#Excalibur Box# +12938#Combat Knife Box# +12939#Dagger of Counter Box# +12940#Kaiser Knuckle Box# +12941#Pole Axe Box# +12942#Mighty Staff Box# +12943#Light Epsilon Box# +12944#Ballista Box# +12945#Sage's Diary Box# +12946#Asura Box# +12947#Apple of Archer Box# +12948#Bunny Band Box# +12949#Sakkat Box# +12950#Grand Circlet Box# +12951#Elven Ears Box# +12952#Steel Flower Box# +12953#Critical Ring Box# +12954#Earring Box# +12955#Ring Box# +12956#Necklace Box# +12957#Glove Box# +12958#Brooch Box# +12959#Rosary Box# +12960#Safety Ring Box# +12961#Vesper Core 01 Box# +12962#Vesper Core 02 Box# +12963#Vesper Core 03 Box# +12964#Vesper Core 04 Box# +12983#Christmas Holiday Crate# +12984#Halloween Spooky Crate# +12985#Summer Lucky Crate# +12986#Adventurer Pack Box# +12987#Pet Egg Scroll Box 10# +12988#Pet Egg Scroll Box 11# +12989#Christmas Holiday Box# +12990#Halloween Spooky Pack# +12991#Party Hard Pack# +12992#Adventurer Pack# +12993#Party Buff Scroll Pack# +12994#HD Elunium 10 Box# +12995#White Herb Box# +12996#Blue Herb 15 Box# +12997#Elunium Box# +12998#Oridecon Box# +12999#Dead Branch Box# +13000#Jujube Dagger# +13001#Dragon Killer# +13002#Ginnungagap# +13003#Cowardice Blade# +13004#Cowardice Blade# +13005#Angelic Wing Dagger# +13006#Khukri# +13007#Jitte# +13008#Jitte# +13009#Kamaitachi# +13010#Asura# +13011#Asura# +13012#Murasame# +13013#Murasame# +13014#Hakujin# +13015#Hakujin# +13016#Poison Knife# +13017#Ice Pick# +13018#Sucsamad# +13019#Ginnungagap# +13021#Rental Combat Knife# +13022#Rental Dagger of Counter# +13023#Rental Asura# +13026#Moonlight Dagger# +13027#Scalpel# +13028#Tooth Blade# +13030#Dragon Killer# +13031#Swordbreaker# +13032#Mailbreaker# +13033#Assassin Dagger# +13034#Desert Twilight# +13035#Sandstorm# +13036#Brave Damascus# +13037#Valorous Damascus# +13038#Dagger of Hunter# +13039#Ivory Knife# +13040#Novice Cutter# +13041#Novice Main Gauche# +13042#Glorious Gladius# +13046#Krieg# +13047#Weihna# +13050#Eden Dagger I# +13051#Eden Dagger II# +13053#Rental Moonlight Sword# +13054#Combat Knife# +13055#Asura# +13056#Counter Dagger# +13061#Black Wing# +13062#Ancient Dagger# +13066#Eden Group Dagger III# +13067#Keris# +13068#Saurel# +13069#Aztoe Nail# +13070#Scarletto Nail# +13072#Bellum Damascus# +13074#Ninja Cutter# +13075#Kurenai# +13076#Raksasa Dagger# +13078#Mikatsuki# +13079#Metal Dagger# +13083#TE WoE Knife# +13085#Volcano Knife# +13086#Goldsmithing Dagger# +13088#Krishnagar# +13089#Faceworm Leg# +13090#Faceworm Queen Leg# +13092#RWC Memory Knife# +13093#Thanatos Dagger# +13094#Evil Slayer Stabber Dagger# +13097#Ru Blue Dagger# +13098#Ru Blue Ashura# +13099#Ru Blue Knife# +13100#Six Shooter# +13101#Six Shooter# +13102#Crimson Bolt# +13103#Crimson Bolt# +13104#Garrison# +13105#Garrison# +13106#Gold Lux# +13107#Wasteland's Outlaw# +13108#Brave Revolver# +13110#Glorious Pistol# +13112#Eden Revolver I# +13113#Eden Revolver II# +13114#Eden Group Revolver III# +13116#Novice Revolver# +13117#TE WoE Grenade# +13118#Fading Flame# +13119#Freedom Flame# +13120#Heaven's Feather & Hell's Fire# +13122#Altair & Ares# +13124#Altair & Ares# +13125#Metal Revolver# +13127#Crimson Revolver# +13128#Vicious Mind Revolver# +13136#Probation Revolver# +13138#Dark Rose# +13150#Branch# +13151#Cyclone# +13152#Cyclone# +13153#Dusk# +13154#Rolling Stone# +13155#Black Rose# +13156#Gate Keeper# +13157#Drifter# +13158#Butcher# +13159#Butcher# +13160#Destroyer# +13161#Destroyer# +13162#Inferno# +13163#Long Barrel# +13164#Long Barrel# +13165#Jungle Carbine# +13166#Jungle Carbine# +13167#Gate Keeper-DD# +13168#Thunder P# +13169#Thunder P# +13170#Lever Action Rifle# +13171#Valorous Rifle# +13172#Brave Gatling Gun# +13173#Valorous Shotgun# +13174#Valorous Grenade Launcher# +13176#Glorious Rifle# +13177#Glorious Gatling Gun# +13178#Glorious Shotgun# +13179#Glorious Grenade Launcher# +13180#Novice Rifle# +13181#Novice Shotgun# +13182#Novice Gatling# +13183#Novice Grenade Launcher# +13184#TE WoE Rifle# +13185#TE WoE Gatling# +13186#TE WoE Shotgun# +13187#TE WoE Grenade# +13189#Color Scope# +13190#R.A.G.203# +13192#Death Fire# +13193#Rolling Thunder# +13194#Peace Breaker# +13195#R.A.G.203# +13196#Peace Breaker# +13197#Minigun# +13198#Tempest# +13199#Tempest# +13200#Bullet# +13201#Old Silver Bullet# +13202#Old Bloody Shell# +13203#Old Flare Sphere# +13204#Old Lightning Sphere# +13205#Old Poison Sphere# +13206#Old Blind Sphere# +13207#Old Freezing Sphere# +13208#GongBug# +13209#Full Metal Jacket# +13210#Slug Shot L# +13211#Slug Shot M# +13212#Slug Shot H# +13213#Slug Shot SH# +13214#Slug Shot XH# +13215#Armor Piercing Bullet# +13216#Blazing Bullet# +13217#Freezing Bullet# +13218#Electric Bullet# +13219#Magic Stone Bullet# +13220#Purifying Bullet# +13221#Silver Bullet# +13222#Bloody Shell# +13223#Incendiary Grenade# +13224#Lightning Grenade# +13225#Poison Grenade# +13226#Flash Grenade# +13227#Cryo Grenade# +13228#Flare Bullet# +13229#Lightning Bullet# +13230#Ice Bullet# +13231#Poison Bullet# +13232#Blinding Bullet# +13250#Shuriken# +13251#Nimbus Shuriken# +13252#Flash Shuriken# +13253#Sharp Leaf Shuriken# +13254#Thorn Needle Shuriken# +13255#Icicle Kunai# +13256#Black Earth Kunai# +13257#High Wind Kunai# +13258#Heat Wave Kunai# +13259#Fell Poison Kunai# +13260#Apple Bomb# +13261#Coconut Bomb# +13262#Melon Bomb# +13263#Pineapple Bomb# +13264#Banana Bomb# +13265#Black Lump# +13266#Black Hard Lump# +13267#Very Hard Lump# +13268#Mysterious Powder# +13269#(null)# +13270#Full SwingK Throw# +13271#Mana + Throw# +13272#Cure Free Throw# +13275#HP Increase Potion(Small) Throw# +13276#HP Increase Potion(Mid) Throw# +13277#HP Increase Potion(Large) Throw# +13278#HP Increase Potion(Small) Throw# +13279#SP Increase Potion(Mid) Throw# +13280#SP Increase Potion(Large) Throw# +13281#Enriched White PotionZ Throw# +13282#Vitata50 Throw0# +13283#Enrich Celermine Juice Throw# +13284#Savage BBQ Throw# +13285#Warg Cocktail To Throw# +13286#M Brisket To Throw# +13287#Siroma Icetea To Throw# +13288#Drosera Stew To Throw# +13289#Petite Noodle To Throw# +13290#Black Thing To Throw# +13291#Starfish# +13292#Dried Squid# +13293#Flying Fish# +13294#Explosive Kunai# +13295#Light Shuriken# +13300#Huuma Wing Shuriken# +13301#Huuma Giant Wheel Shuriken# +13302#Huuma Giant Wheel Shuriken# +13303#Huuma Blaze Shuriken# +13304#Huuma Calm Mind# +13305#Brave Huuma Front Shuriken# +13306#Valorous Huuma Front Shuriken# +13307#Glorious Shuriken# +13310#Eden Group Huuma Shuriken I# +13311#Huuma Shadow# +13312#Huuma Job Test# +13313#Huuma Swirling Petal# +13314#Huuma Fluttering Snow# +13315#Huuma Thunderstorm# +13316#Upg Huuma Shuriken# +13317#TE WoE Huuma# +13322#Metal Huuma Shuriken# +13327#Crimson Huuma Shuriken# +13328#Vicious Mind Huuma Shuriken# +13332#Huuma Shuriken of Dancing Petals# +13337#Illusion Huuma Fluttering Snow# +13338#Illusion Wing Shuriken# +13345#Huuma Shuriken Clear# +13346#Grinder Huuma Shuriken# +13400#Cutlus# +13401#Excalibur# +13402#Cutlas# +13403#Solar Sword# +13404#Platinum Shotel# +13405#Curved Sword# +13410#Valorous Gladiator Blade# +13411#Brave Gladiator Blade# +13412#Twin Edge of Naght Sieger# +13413#Twin Edge of Naght Sieger# +13414#Elemental Sword# +13415#Novice Falchion# +13416#Glorious Flamberge# +13417#Glorious Rapier# +13418#Glorious Holy Avenger# +13421#Ruber# +13423#Eden Sabre I# +13424#Eden Sabre II# +13426#Rental Cutlas# +13427#Rental Solar Sword# +13431#Chrome Metal Sword# +13434#Eden Group Saber III# +13439#TE WoE Sword# +13440#Ceremonial Sword# +13441#Thanatos Sword# +13442#Old Parasol# +13444#Pala# +13451#Ru Blue Sword# +13452#Ru Gold Sword# +13454#Crimson Sabre# +13455#Vicious Mind Sabre# +13460#Sealed Magic Sword# +13461#Sealed Evil Sword# +13462#Sealed Maximum Sword# +13469#Illusion Immaterial Sword# +13485#Red Lotus Sword# +13493#Cannon Rapier-OS# +13503#Baphomet Horns Box# +13505#Executioner Box# +13507#Tomahawk Box# +13508#Rudra Bow Box# +13509#Cutlas Box# +13510#Solar Sword Box# +13513#Moonlight Dagger Box# +13514#Wrench Box# +13516#Royal Jelly 5 Box# +13517#Yggdrasil Berry 3 Box# +13526#HE Bubble Gum Box# +13527#HE Battle Manual Box# +13531#Light Red Potion Box# +13532#Light Orange Potion Box# +13533#Light Yellow Potion Box# +13534#Light White Potion Box# +13535#Light Center Potion Box# +13536#Light Awakening Potion Box# +13537#Light Berserk Potion Box# +13553#Dungeon Teleport Scroll 5 Box# +13572#Hwergelmir's Tonic 30 Box# +13573#Hwergelmir's Tonic 50 Box# +13574#Nine Tail Dish 30 Box# +13575#Nine Tail Dish 50 Box# +13578#Stew of Immortality 30 Box# +13579#Stew of Immortality 50 Box# +13580#Life Insurance 30 Package# +13581#Old pRO Box# +13584#Blessing Scroll 30 Box# +13592#Steamed Scorpion 30 Box# +13593#Steamed Scorpion 50 Box# +13596#Dragon Breath Cocktail 30 Box# +13597#Dragon Breath Cocktail 50 Box# +13601#Token Of Siegfried 20 Box# +13604#Steamed Tongue 30 Box# +13605#Steamed Tongue 50 Box# +13610#Enriched Elunium 10 Box# +13611#Enriched Oridecon 10 Box# +13621#Greed Scroll 30 Box# +13634#Zodiac Diadem Crown Package# +13642#Start your Journey Pack# +13680#Punksutawney Phil's Lucky Box# +13681#Serpent's Surprise# +13682#Tricky Halloween Treat Box# +13683#Lovely Groove Pack# +13685#Tricky Halloween Treat Crate# +13686#Yule Love This Lucky Box# +13687#Apocalypse Survivor Fire Sale Box# +13701#HD Oridecon 10 Box# +13702#Heavy Lifter Box# +13710#Gym Pass Box# +13711#Small Life Potion 10 Box# +13712#Small Life Potion 30 Box# +13713#Small Life Potion 50 Box# +13714#Medium Life Potion 10 Box# +13715#Medium Life Potion 30 Box# +13716#Medium Life Potion 50 Box# +13717#Abrasive 5 Box# +13718#Abrasive 10 Box# +13719#Regeneration Potion 5 Box# +13720#Regeneration 10 Box# +13721#Dungeon Teleport Scroll 10 Box# +13722#Pecopeco Hairband Box# +13723#Red Glasses Box# +13724#Whisper Mask Box# +13725#Ramen Hat Box# +13738#Glass of Illusion 5 Box# +13739#Glass of Illusion 10 Box# +13740#Shadow Armor Scroll 5 Box# +13741#Shadow Armor Scroll 10 Box# +13742#Shadow Armor Scroll 30 Box# +13743#Holy Armor Scroll 5 Box# +13744#Holy Armor Scroll 10 Box# +13745#Holy Armor Scroll 30 Box# +13746#(S) Defense Potion 10 Box# +13747#(S) Defense Potion 30 Box# +13748#(S) Defense Potion 50 Box# +13749#(L) Defense Potion 10 Box# +13750#(L) Defense Potion 30 Box# +13751#(L) Defense Potion 50 Box# +13752#(S) M. Defense Potion 10 Box# +13753#(S) M. Defense Potion 30 Box# +13754#(S) M. Defense Potion 50 Box# +13755#(L) M. Defense Potion 10 Box# +13756#(L) M. Defense Potion 30 Box# +13757#(L) M. Defense Potion 50 Box# +13790#Fall Into The Groove Pack# +13791#Turkey Day Groove Pack with Gravy# +13793#Black Friday Groove Pack# +13794#Cyber Monday Lucky Box# +13802#Pirate's Booty Box# +13804#I Love You Firecracker Box# +13805#Whiteday Firecracker Box# +13806#Valentine's Day Firecracker Box# +13807#Birthday Firecracker Box# +13808#Xmas Firecracker Box# +13809#Blue Gemstone Box# +13810#Light Blue Potion Box# +13811#+20 Basic Food Pack# +13812#Food Box Vol 2# +13813#Food Box Vol 3# +13826#Gary's Groovy Pack# +13827#Fantastic Village Costume Hat Box# +13849#Yellow Butterfly Wing Box5# +13850#Yellow Butterfly Wing Box# +13851#Green Butterfly Wing Box5# +13852#Green Butterfly Wing Box# +13853#Red Butterfly Wing Box5# +13854#Red Butterfly Wing Box# +13855#Blue Butterfly Wing Box5# +13856#Blue Butterfly Wing Box# +13859#Directive Envelope A# +13860#Directive Envelope B# +13862#Welcome to Midgard Pack# +13864#Hockey Mask Box# +13866#Flapping Angel Wing Box# +13869#Chick Hat Box# +13871#Mage Set Card Box# +13872#Acolyte Set Card Box# +13873#Archer Set Card Box# +13874#Swordman Set Card Box# +13875#Thief Set Card Box# +13876#Merchant Set Card Box# +13902#Fish Head Hat Box# +13903#Santa Poring Box# +13904#Bell Ribbon Box# +13908#Class Set Card Album# +13909#MVP Hunting Box# +13912#Party Blessing 10 Scroll Box# +13913#Party Increase Agi 10 Scroll Box# +13914#Party Assumptio 5 Scroll Box# +13918#Koneko Hat Box# +13919#Baphomet Horns Box# +13920#Flying Evil Wing Box# +13921#Gentleman's Pipe Box# +13922#Bunny Top Hat Box# +13923#Dark Randgris Helm Box# +13924#Orc Hero Headdress Box# +13937#Robo Eye Box# +13940#WoE Teleport Scroll 100 Box# +13945#Shiba Inu Set# +13953#All In One Ring Box# +13962#Defolty Doll Hat Box# +13963#Glaris Doll Hat Box# +13965#Telling Doll Hat Box# +13969#July Jubilee Groove Pack# +13973#Poison Bottle 30 Box# +13974#RWC Gear Set# +13990#Job Battle Manual 10 Box# +13991#Tiger Mask Box# +13992#Neko Mimi Box# +13993#Alice Hat Box# +13994#Speed Potion Box(5)# +13995#Speed Potion Box(10)# +13998#Giant Fly Wing Box 500# +14002#Advanced WoE Supply Box# +14004#Poison Bottle 10 Box# +14005#Poison Bottle 5 Box# +14017#All In One Ring Box# +14024#Robo Eye Box# +14039#Hockey Mask Box# +14042#Yellow Butterfly Wing Box# +14044#Green Butterfly Wing Box# +14046#Red Butterfly Wing Box# +14048#Blue Butterfly Wing Box# +14053#Little Angel Doll Box# +14075#Glass Of Illusion 10 Box# +14082#(S) Defense Potion 10 Box# +14085#(L) Defense Potion 10 Box# +14088#(S) M. Defense Potion 10 Box# +14091#(L) M. Defense Potion 10 Box# +14094#Flapping Angel Wing Box# +14095#Neko Mimi Box# +14096#Moonlight Flower Hat Box# +14097#Chick Hat Box# +14098#Pecopeco Hairband Box# +14099#Red Glasses Box# +14100#Whisper Mask Box# +14101#Ramen Hat Box# +14104#Small Life Potion 10 Box# +14107#Medium Life Potion 10 Box# +14111#Abrasive 10 Box# +14112#Regeneration Potion 5 Box# +14114#Dungeon Teleport Scroll 10 Box# +14118#Combat Knife Box# +14119#Counter Dagger Box# +14122#Light Epsilon Box# +14124#Sage's Diary Box# +14125#Asura Box# +14126#Apple of Archer Box# +14127#Bunny Band Box# +14130#Elven Ears Box# +14131#Steel Flower Box# +14132#Critical Ring Box# +14133#Earring Box# +14134#Ring Box# +14135#Necklace Box# +14136#Glove Box# +14137#Brooch Box# +14138#Rosary Box# +14139#Safety Ring Box# +14144#Vigorgra Box2# +14145#Summer 2013 Gear Box# +14146#Candy-Gram Lucky Box# +14147#Candy-Gram Lucky Crate# +14148#Vigorgra Box5# +14149#Vigorgra Box6# +14150#Vigorgra Box7# +14151#Vigorgra Box8# +14152#Start your Journey Pack# +14153#Siege Mode Pack# +14154#1 Hour Survival Pack# +14155#Weekend Hunting Pack# +14156#Battle Manual Box# +14157#Insurance Package# +14158#Bubble Gum Box# +14159#Steamed Tongue Box# +14160#Steamed Scorpion Box# +14161#Dragon Breath Cocktail Box# +14162#Hwergelmir's Tonic Box# +14163#Nine Tail Dish Box# +14164#Stew Of Immortality Box# +14165#Kafra Card Box# +14166#Giant Fly Wing Box# +14167#Neuralizer Box# +14168#Convex Mirror Box# +14169#Blessing 10 Scroll Box# +14170#Increase AGI 10 scroll Box# +14171#Aspersio 5 Scroll Box# +14172#Assumptio 5 Scroll Box# +14173#Wind Walk 10 Scroll Box# +14174#Adrenaline 5 Scroll Box# +14175#Megaphone 10 Box# +14176#Enriched Elunium 50 Box# +14177#Enriched Oridecon 50 Box# +14178#Token of Siegfried Box# +14181#Hwergelmir's Tonic 30 Box# +14182#Hwergelmir's Tonic 50 Box# +14183#Nine Tail Dish 30 Box# +14184#Nine Tail Dish 50 Box# +14187#Stew of Immortality 30 Box# +14188#Stew of Immortality 50 Box# +14189#Life Insurance 30 Package# +14194#Lv10 Blessing Scroll Box 50# +14198#Lv5 Assumptio Scroll Box 50# +14201#Steamed Scorpion 30 Box# +14202#Steamed Scorpion 50 Box# +14205#Dragon Breath Cocktail 30 Box# +14206#Dragon Breath Cocktail 50 Box# +14207#Battle Manual Box# +14208#Battle Manual 5 Box# +14210#Token Of Siegfried 20 Box# +14213#Steamed Tongue 30 Box# +14214#Steamed Tongue 50 Box# +14219#Enriched Elunium 5 Box# +14220#Enriched Oridecon 5 Box# +14223#Mystical Amplification 50 Box# +14232#Yggdrasil Berry 10 Box# +14283#Written Oath Of Marriage Box# +14289#New Clothing Dye Coupon Box# +14290#Original Clothing Dye Coupon Box# +14300#Mask Of Ifrit Box# +14301#Ear Of Ifrit Box# +14447#Scallywag's Hat Box# +14448#Necromancer's Hood Box# +14459#Community Created Headgears# +14466#Valentine's Emblem Box# +14495#Aquarius Diadem Box# +14496#Aquarius Crown Box# +14497#Pisces Diadem Box# +14498#Pisces Crown Box# +14501#Shadow Shield Box# +14505#Sky Fortress Ticket# +14506#Sky Fortress Ticket# +14509#Light Concentration Potion# +14510#Light Awakening Potion# +14511#Light Berserk Potion# +14512#Level 10 Meteor Storm# +14513#Level 10 Storm Gust# +14514#Level 10 Lord of Vermilion# +14517#Chemical Protection Helm Scroll# +14518#Chemical Protection Shield Scroll# +14519#Chemical Protection Armor Scroll# +14520#Chemical Protection Weapon Scroll# +14527#Dungeon Teleport Scroll# +14529#Greed Scroll# +14533#Advanced Battle Manual# +14534#Small Life Potion# +14535#Medium Life Potion# +14536#Abrasive# +14537#Regeneration Potion# +14538#Glass of Illusion# +14539#Shadow Armor Scroll# +14540#Holy Armor Scroll# +14541#Small Defense Potion# +14542#Large Defense Potion# +14543#Small Magic Defense Potion# +14544#Large Magic Defense Potion# +14545#Battle Manual X3# +14546#I Love You Firecracker# +14547#Whiteday Firecracker# +14548#Valentine's Day Firecracker# +14549#Birthday Firecracker# +14550#Xmas Firecracker# +14551#Fried Grasshopper Legs# +14552#Seasoned Sticky Webfoot# +14553#Bomber Steak# +14554#Grape Juice Herbal Tea# +14555#Autumn Red Tea# +14556#Honey Herbal Tea# +14557#Steamed Crab Nippers# +14558#Assorted Seafood# +14559#Clam Soup# +14560#Frog Egg Squid Ink Soup# +14561#Smooth Noodle# +14562#Tentacle Cheese Gratin# +14563#Honey Grape Juice# +14564#Chocolate Mousse Cake# +14565#Fruit Mix# +14566#Fried Monkey Tails# +14567#Mixed Juice# +14568#Fried Sweet Potato# +14569#Knife Goblin Ring# +14570#Flail Goblin Ring# +14571#Hammer Goblin Ring# +14572#Holy Marble# +14573#Red Burning Stone# +14574#Vagabond's Skull# +14575#Lutie Lady's Pancake# +14576#Mastela Fruit Wine# +14577#Spicy Fried Bao# +14578#Steamed Bat Wing in Pumpkin# +14579#Green Salad# +14580#Fried Scorpion Tails# +14582#Yellow Butterfly Wing# +14583#Green Butterfly Wing# +14584#Red Butterfly Wing# +14585#Blue Butterfly Wing# +14588#Party Blessing 10 Scroll# +14589#Party Increase Agi 10 Scroll# +14590#Party Assumptio 5 Scroll# +14591#WoE Teleport Scroll# +14592#Job Battle Manual# +14593#Mystical Amplification Scroll# +14595#Unsealed Magic Spell# +14596#Pierre's Treasurebox# +14600#Mental Potion# +14613#RWC Arms Box# +14645#Beelzebub Wing# +14646#Orleans Full Course# +14683#10th Anniversary Lucky Box# +14704#Gemstone Shadow Box# +14706#Pirate Eyepatch Box# +14735#Hop into the Costume Box# +14759#Shadow Armor Weapon Box# +14799#[7Day] Battle Manual & Bubble Gum# +14836#Xin Niar Scroll# +14856#Ancient Elven Scroll# +14884#Weather Scroll# +14921#Fancy February Costume Box# +14924#Overseas Care Package XVI# +14930#Overseas Care Package Remix I# +14940#Mystical Costume Box# +14944#May Costume Box# +14947#Anniversary 2018 Costume Box# +14948#Overseas Care Package XVIII# +14971#Overseas Care Package XX# +14977#Overseas Care Package XXI# +14979#MVP August Costume Box# +14991#Feisty September 2018 Costume Box# +14992#Overseas Care Package XXII# +15000#Bone Plate# +15002#Rune Plate# +15009#Eden Group Uniform I# +15010#Eden Group Uniform II# +15011#Eden Group Uniform III# +15012#Puente Robe# +15013#Claire Suits# +15014#Ebone Armor# +15021#Upgrade Formal Dress# +15024#Army Padding# +15025#Golden Rod Robe# +15026#Aqua Robe# +15027#Crimson Robe# +15028#Forest Robe# +15029#Robe Of Affection# +15030#Robe of Judgement# +15031#Eden Group Armor# +15032#Tidung# +15033#Tutorial Mantle# +15034#Tutorial Mantle# +15036#Ur's Plate# +15037#Peuz's Plate# +15038#Sapha's Cloth# +15039#Nab's Cloth# +15040#Prisoner's Uniform# +15041#Boitata Armor# +15042#White Wing Suit# +15043#Black Wing Suit# +15044#Green Operation Coat# +15046#WoE Plate# +15047#WoE Suits# +15048#WoE Robe# +15051#Bakonawa Scale Armor# +15052#Bakonawa Scale Armor(Bayani)# +15053#Special Ninja Suit# +15054#Ninja Scale Armor# +15055#Tenebris Latitantes# +15056#Special Ninja Suit# +15061#Egir Armor# +15062#TE WoE Coat# +15063#TE WoE Mail# +15064#TE WoE Magic Coat# +15066#Engraved Armor# +15067#Rune Suit# +15068#Advanced Angelic Protection# +15071#Valkyrian Robe# +15072#Nectar Suit# +15073#Anti-magic Suit# +15074#Geffen Magic Robe# +15087#Azure Dragon Armor# +15088#School Uniform# +15089#Lounge Suit# +15090#Armor Of Gray# +15091#Robe Of Gray# +15093#Hero Plate# +15094#Hero Magic Coat# +15095#Hero Judgement Shawl# +15096#Hero Trade Mail# +15097#Hero Hidden Cloth# +15098#Hero Target Suit# +15100#Frozen Breastplate# +15101#Harden Breastplate# +15102#Hunter Mail# +15103#Kirin Armor# +15104#Fisherman's Mail# +15105#Kaftan# +15106#Engineer Mail# +15107#Entomologist Mail# +15108#Venomous Insect Armor# +15110#Supplement Part Str# +15111#Upgrade Part - Plate# +15112#Military Mail# +15113#Exorcist Mail# +15116#Airship¡®s Armor# +15117#Felrock¡®s Armor# +15118#PRO 10th Armor# +15119#PRO 10th Robe# +15120#PRO 10th Shoes# +15121#Sarah's Battle Robe# +15122#Botany Mail# +15124#Holy Father Mail# +15125#Female Diver's Suit# +15126#Doram Suit# +15128#Excelion Suit# +15129#Luxurious Doram Suit# +15130#Dragon Mail# +15134#Vicious Cloth# +15135#Dragon Cloth# +15138#Egir Armour# +15139#Shepherd Clothes# +15140#Natural Clothes# +15142#Orca Clothes# +15143#Vermin Cloth# +15144#Agriculture Clothes# +15145#Evil Dragon Armor# +15146#Flattery Robe# +15147#Abusive Robe# +15150#White Shirt# +15151#White Eco Shirt# +15156#Elegant Doram Suit# +15163#Agenda Robe# +15164#Consultation Robe# +15165#Pure White Marching Hat# +15166#Rosary's Necklace# +15169#Kardui Robe# +15174#Surfer Swimsuit# +15175#Fire Dragon Armor# +15176#Vigilante Suit# +15177#Elemental Robe# +15178#Golden Ninja Suit# +15179#Mine Worker's Vest# +15180#Hippie Clothes# +15189#Fallen Warrior Armor# +15195#Illusion Puente Robe# +15196#Sea Dragon Armor# +15204#Abyss Dress# +15205#Medical Scrubs# +15209#Drakes Coat# +15210#[Rental] Surfer Swimsuit# +15212#YSF01 Plate# +15246#True Hunting Mail# +15247#Short-term Hunting Mail# +15249#Antonio's Coat# +15278#Overwelm Str Armor# +15279#Overwelm Int Armor# +15283#Mighty Black Threaded Armor# +15343#Supplement Part Str# +15344#Upgrade Part - Plate# +15346#Overwelm Luk Armor# +15347#Overwelm Vit Armor# +15352#Nature Dress# +15353#Overwelm Agi Armor# +15354#Overwelm Dex Armor# +15376#Illusion Armor Type A# +15377#Illusion Armor Type B# +15378#Lava Leather Armor# +15379#Lava Leather Suits# +15380#Lava Leather Robe# +15384#Ritual Robe# +15390#Regia Hunting Mail# +15391#Red Dragon Plate# +15392#Green Dragon Plate# +15393#Gold Dragon Plate# +15394#Purple Dragon Plate# +15395#Blue Dragon Plate# +15396#Silver Dragon Plate# +15397#Soutane of Strength# +15398#Soutane of Agility# +15399#Soutane of Vitality# +15400#Soutane of Dexterity# +15401#Soutane of intelligence# +15402#Soutane of Luck# +15405#Fafnir Scale# +15407#Whiteknight Armor# +15409#Bloody Dress# +15410#High Adventurer Suit# +15417#Armor of Purple Thread# +15419#Armor of White Thread# +15420#Icefall Dress# +15421#Okami Robe# +15825#Costume Black Musang Hat# +15838#Costume Vesper Headgear# +15839#Costume Drooping Gunslinger# +15840#Costume Bio Protector# +15841#Costume Goggles Hat# +15842#Costume Magic Time# +15851#Costume Freyja Crown# +15853#Costume Gold Ingot Poring# +15858#Costume Choco Banana# +15875#King of Spirit Circle# +15876#RTC Second Best# +15882#Rhythm Hairband# +15888#Costume Devil's Hand# +15889#Costume Boitata Cap# +15890#Costume Black Death King's Hat# +15891#Mountain Helmet# +15892#Themis Helm# +15904#Costume Love Guard# +15905#Costume Helmet of Siegfried# +15906#Northern Cross# +16000#Erde# +16001#Red Square Bag# +16003#Carga Mace# +16004#Eden Mace I# +16005#Eden Mace II# +16007#Ephemeral Mjolnir# +16008#Rental Wrench# +16010#Red Ether Bag# +16013#Mace of Judgement# +16014#Eden Group Mace3# +16015#Cat Club# +16016#Tuna# +16017#Bloody Cross# +16018#Empowered Mace of Judgement# +16020#Bellum Stunner# +16021#Bellum Flail# +16022#Nemesis# +16023#Metal Mace# +16024#Quadrille# +16025#TE WoE Mace# +16026#RWC Memory Mace# +16027#Evil Slayer Destroyer Hammer# +16028#Thanatos Hammer# +16029#Noble Cross# +16030#Pile Bunker S# +16031#Pile Bunker P# +16032#Pile Bunker T# +16033#Robot's Arm# +16036#Ru Blue Mace# +16037#Ru Gold Mace# +16039#Spoon# +16040#Crimson Mace# +16041#Vicious Mind Mace# +16051#Valkyrie Hammer# +16060#Liquor Bottle# +16063#Illusion Long Mace# +16065#Illusion Iron Driver# +16088#Saphir Hall-OS# +16089#Ultio-OS# +16097#Freezing Mace# +16099#Rubber Hammer# +16186#Baby Dragon Hat Box# +16192#Quati Hat Box# +16193#Tucan Hat Box# +16194#Jaguar Hat Box# +16226#Aries Diadem Box# +16227#Aries Crown Box# +16230#Taurus Diadem Box# +16231#Taurus Crown Box# +16246#Crown of Deceit Box# +16251#Gemini Diadem Box# +16252#Gemini Crown Box# +16258#HD Bradium 5 Box# +16259#HD Carnium 5 Box# +16260#HD Bradium 10 Box# +16261#HD Carnium 10 Box# +16262#HD Bradium 5 Box# +16263#HD Carnium 5 Box# +16264#HD Bradium 10 Box# +16265#HD Carnium 10 Box# +16267#HE Battle Manual Box# +16268#HE Bubble Gum Box# +16269#Cancer Diadem Box# +16270#Cancer Crown Box# +16325#Rune Hairband Box# +16343#Leo Crown Box# +16344#Leo Diadem Box# +16345#Leo Crown Box# +16346#Leo Diadem Box# +16368#Virgo Crown Box# +16372#Four Leaf Clover Box# +16381#WoE Teleport Scroll 100 Box# +16385#O'Riley's Green Box# +16388#Four Leaf Clover in Mouth Box III# +16390#Bubble Gum in Mouth Box III# +16397#Virgo Diadem Box# +16398#Virgo Crown Box# +16399#Virgo Diadem Box# +16414#Turtle Hat Box# +16415#Turtle Hat Box# +16418#Giant Fly Wing Box 500# +16419#Greed Scroll Box# +16420#Adventurer Pack# +16421#Written Oath Of Marriage Box# +16422#Magestic Goat Box# +16423#Episode 13.1 Key Package# +16424#Executioner Box# +16425#Cutlas Box# +16426#Moonlight Dagger Box# +16427#Wrench Box# +16428#Solar Sword Box# +16429#Tomahawk Box# +16430#Rudra Bow Box# +16431#Pole Axe Box# +16436#Libra Crown Box# +16437#Libra Crown Box# +16438#Libra Diadem Box# +16439#Libra Diadem Box# +16447#Scorpio Crown Box# +16448#Scorpio Diadem Box# +16449#Scorpio Crown Box# +16450#Scorpio Diadem Box# +16461#Red Wing Hat Box# +16462#Red Wing Hat Box# +16481#[Event] Small Life Potion Box(10)# +16483#Abrasive 10 Box# +16504#Bubble Gum Box# +16505#[Event] Steamed Tongue Box(10)# +16506#[Event] Steamed Desert Scorpions Box(10)# +16507#[Event] Dragon Breath Cocktail Box(10)# +16508#[Event] Hwergelmir's Tonic Box(10)# +16509#[Event] Cooked Nine Tail Box(10)# +16510#[Event] Immortal Stew Box(10)# +16514#[Event] Blessing Scroll Box(10)# +16515#[Event] Increase Agility Scroll Box(10)# +16543#Snowman Hat Box# +16544#Snowman Hat Box# +16548#Ephemeral Brisingamen Trade Box# +16549#Ephemeral Sleipnir Trade Box# +16550#Ephemeral Mjolnir Trade Box# +16551#Ephemeral Megingjard Trade Box# +16554#Elven Sunglasses Box# +16555#Playspan Magical Stone Box# +16556#Magical Stone Box# +16565#Capricorn Crown Box# +16566#Capricorn Crown Box# +16567#Capricorn Diadem Box# +16568#Capricorn Diadem Box# +16588#Thoughtful Hat Box# +16589#Thoughtful Hat Box# +16590#Thoughtful Hat Box# +16591#Overseas Care Package Remix XIII Mini# +16615#Charming Ribbon Box# +16616#Charming Ribbon Box# +16652#Flame Scroll# +16659#Zodiac Diadem Pack# +16676#Zodiac Crown Pack# +16677#Bargain Hunter's Catalog 10 Box# +16678#Bargain Hunter's Catalog 50 Box# +16682#Halter Lead 7 Day Box# +16683#Halter Lead 30 Day Box# +16689#Garuda Hat Box# +16692#Alice Hat Box# +16693#Crescent Helm Box# +16694#Crescent Helm Box# +16695#Dragon Skull Box# +16696#Dragon Skull Box# +16697#Drooping Bunny Box# +16698#Drooping Bunny Box# +16699#Evolved Blue Fish Box# +16700#Evolved Blue Fish Box# +16701#Evolved Pair of Red Ribbon Box# +16702#Evolved Pair of Red Ribbon Box# +16703#Evolved Pipe Box# +16704#Evolved Pipe Box# +16705#Hibiscus Box# +16706#Hibiscus Box# +16707#Jumping Poring Box# +16708#Jumping Poring Box# +16709#Kettle Hat Box# +16710#Kettle Hat Box# +16711#Magic Eyes Box# +16712#Magic Eyes Box# +16713#Mini Propeller Box# +16714#Mini Propeller Box# +16715#Puppy Hat Box# +16716#Puppy Hat Box# +16717#Sheep Hat Box# +16718#Sheep Hat Box# +16719#Tiger Mask Box# +16720#Vacation Hat Box# +16721#Vacation Hat Box# +16722#Vane Hairpin Box# +16723#Vane Hairpin Box# +16724#Vanilmirth Hat Box# +16725#Vanilmirth Hat Box# +16726#Water Lily Crown Box# +16727#Water Lily Crown Box# +16728#Pink Beanie Hat# +16729#Pink Beanie Hat# +16730#Green Ribbon Box# +16731#Green Ribbon Box# +16732#Gray Deviruchi Hat Box# +16733#Gray Deviruchi Hat Box# +16734#Blue Drooping Cat Box# +16735#Blue Drooping Cat Box# +16736#Fantastic Wig Box# +16737#Fantastic Wig Box# +16738#Yellow Mage Hat Box# +16739#Yellow Mage Hat Box# +16740#Seagod's Protection Box# +16741#Hairtail Box(1hr)# +16742#Hairtail Box(7day)# +16743#Marlin Box(1hr)# +16744#Marlin Box(7day)# +16745#Saurel Box(1hr)# +16746#Saurel Box(7day)# +16747#Tuna Box(1hr)# +16748#Tuna Box(7day)# +16749#Malangdo Crab Box(1hr)# +16750#Malangdo Crab Box(7day)# +16751#Spotty Eel Box1# +16752#Spotty Eel Box2# +16758#Umbala Spirit Box# +16760#Early Wrapped Christmas Gift# +16763#Seagod's Protection Box2# +16764#Seagod's Protection Box3# +16765#Octopus Hstick Box# +16766#Octopus Hstick Box2# +16767#Octopus Hstick Box3# +16768#Neuralizer Box# +16769#Neuralizer Box# +16770#Silvervine 10 Box# +16771#Silvervine 40 Box# +16794#Giant Flywing 500 Box# +16864#WoE Teleport Scroll 100 Box# +16867#WoE Teleport Scroll 500 Box# +16972#Weather Report Box# +16973#Costume: Yellow Hat Box# +16974#Costume: Old-Timey Box# +16975#Costume: Singing Bird Box# +16976#Costume: Chicken Box# +16977#Costume: Mini Crown Box# +16979#Silvervine 4 Box# +16999#Heroic Backpack Box# +17013#Malangdo Woe Encard Box# +17014#Costume: Butterfly Ears Box# +17015#Costume: Bolt Ears Box# +17030#St Patrick's Hat Box# +17047#Rebecca Friday Black Box# +17048#Cobra's Saturday Morning Box# +17049#Silly Sunday Sale Sack# +17053#Butterflywing Color Box# +17066#Poison Bottle 50 Box# +17067#Poison Bottle 100 Box# +17068#Acid Bomb 50 Pack# +17080#March and Groove Pack# +17081#Yggdrasil Crown Box# +17082#Alora's Costume Box# +17083#Hawt Groove Pack# +17104#HD Oridecon 50 Box# +17105#HD Elunium 50 Box# +17106#Heavy Lifter Box# +17107#Gemini Crown Scroll# +17108#Gemini Crown Scroll 10 Box# +17109#May You Groove On Pack# +17110#Aquarius Scroll# +17113#Marching Hat Box# +17114#Ancient Horns Box# +17115#Sprout Hat Box# +17116#Mercury Riser Box# +17117#Aries Scroll# +17119#Summer Lucky Pack# +17120#Taurus Scroll# +17121#Starry Scroll# +17122#Immune Shield Box# +17123#Black Devil's Mask Box# +17124#Cat Ear Beret Box# +17125#Red Pom Band Box# +17127#March Groove and Win Crate# +17138#Cancer Scroll# +17140#Leo Scroll# +17141#Virgo Scroll# +17142#Libra Scrol# +17143#Scorpius Scroll# +17155#Upg Huuma Shuriken Box# +17159#Name Change Box# +17162#[7Day] Boarding Halter Box# +17181#Getting Lucky Box# +17199#Spring Lucky Crate# +17221#Anniversary Extravaganza Box# +17222#Anniversary Extravaganza Crate# +17223#GoCash East West Costume Box# +17248#August Lucky Crate# +17252#RWC Rally Box# +17253#RWC Enchant Reset Ticket Box# +17254#RWC Enchant Reset 5 Ticket Box# +17289#Beelzebub Wing Box# +17290#Orleans Full Course Box# +17292#Shadow Box# +17293#Physical Shadow Package# +17294#Magical Shadow Package# +17296#Character Position Change Box# +17307#October Spooky Box# +17317#October Spooky Crate# +17333#Star-Spangled Bandana box# +17334#'Merica box# +17336#Cash Hair Coupon Box# +17340#Pumpkin Spiced Lucky Crate# +17341#Random Headgear Box# +17342#Groovember Pack# +17343#Black Friday Box# +17344#Cyber Monday Box# +17345#2014 Christmas Sock# +17346#Battle Assistance Box# +17347#Year of the Groove Pack# +17348#Bloody Valentine Lucky Box# +17349#The Summer Picnic Hat Box# +17350#Mid Year Madness Box# +17351#Stanley's Shadow Jewelry Box# +17352#Stanley's Shadow Footgear Box# +17353#Stanley's Shadow Shield Box# +17354#Stanley's Shadow Weapon Box# +17355#Stanley's Shadow Armor Box# +17356#Spooktober Lucky Box# +17357#2015 Black Friday Box# +17358#2015 Cybering Monday Box# +17359#Holly Joliday Lucky Crate# +17360#Holly Joliday Lucky Box# +17361#Shadow Resistance Box# +17362#End Of Year Madness Box# +17363#January Groove Pack# +17364#March Groove Pack# +17365#April Adventure Box# +17366#Safe to 7 Sale Box# +17367#May Groove Pack 2016# +17369#13th Anniversa-box# +17370#Costume: Man's Pride Box# +17371#Costume: Charleston's Antenna Box# +17372#Costume Show Me The Zeny Box# +17373#August Lucky Box 2016# +17374#August Lucky Crate 2016# +17375#Zaha Doll Hat Box# +17376#Winter Wonder Time Lucky Crate# +17377#Santa's Wardrobe Box# +17378#Paws N Claws Starter Pack# +17379#Anniversary Costume Box# +17381#Lucky Care Package Remix# +17382#Overseas Care Package IX# +17383#Blast from the Past Box# +17384#Amistr Bag Box# +17385#OCP Remix IV# +17386#OCP Remix XII# +17387#Overseas Care Package Remix XI# +17388#OCP Remix X# +17389#Overseas Care Package Remix IX# +17390#OCP Remix VII# +17391#Overseas Care Package Remix VI# +17392#Overseas Care Package Remix V# +17393#Poring Con Swag Bag# +17434#Pumpkin Spiced Lucky Box# +17450#Gary's Groove Box# +17452#Stocking +20 Basic Food Pack# +17453#Christmas Stocking# +17454#Christmas Stocking Crate# +17458#Steam Ring Box# +17459#Steam Starter Package 1# +17460#Steam Starter Package 2# +17461#Early Winter Costume Box# +17470#Silvervine 10 Sale Box# +17476#OCP Remix III# +17480#Saint Patrick Day Groove Box# +17481#Overseas Care Package Remix II# +17485#Easter Lucky box# +17486#Easter Lucky Set# +17487#May The Groove Be With You Box# +17488#Anniversary Lucky Box# +17489#Anniversary Lucky Crate# +17493#Fish In Mouth Box# +17497#Snowy Wears Costume Box# +17500#Dress Hat Box# +17503#Summer Cooler# +17504#Summer Cooler Crate# +17505#Circ de Ceptember Groove Pack# +17506#Paddys Costume Pack# +17507#Kit Costume Box# +17508#Kitcoin Purse# +17509#Nyangvine 40 Box# +17514#Rideword Hat Box# +17517#Overseas Care Boxception# +17523#100 Silvervine Box# +17528#Costama Frozen II# +17569#Sky Fortress Ticket 1 Hour Box# +17570#Sky Fortress Ticket 3 Hour Box# +17571#Costume: Hill Wind Mask Box# +17577#O'Riley's 2015 Groove Pack# +17578#April Lucky Basket# +17579#April Lucky Crate# +17580#Freshly May-d Groove Pack# +17581#Costume: Analyzing Eye Box# +17585#Costume: New Wave Sunglasses Box# +17594#12th Anniversary Celebraton Lucky Box# +17595#12th Anniversary Celebraton Lucky Crate# +17603#July Groove Pack# +17604#End Of Summer Lucky Box# +17605#End Of Summer Lucky Crate# +17606#Drake's Prize Pack# +17614#Early Fall Costume Box# +17629#Spellflow Shadow Pack# +17645#Thanksgiving Groove Pack# +17646#Halter Lead Box# +17667#Heart's Foundation Lucky Box# +17689#Premium Buff Box# +17709#Costume Amistr Bag Box# +17714#Akiba's Gashapon Pack# +17720#Valkyrie Scroll# +17728#HD Oridecon 100 Box# +17729#HD Elunium 100 Box# +17730#Enriched Oridecon 100 Box# +17731#Enriched Elunium 100 Box# +17732#HD Carnium 100 Box# +17733#HD Bradium 100 Box# +17739#Spooktober Costume Box# +17747#Creepy Costume Box# +17751#November Costume Pack# +17752#Winter Wonder Time Lucky Box# +17762#Fluffy Costume Egg# +17768#Overseas Care Package# +17772#Overseas Care Package II# +17778#Overseas Care Package III# +17779#Love Garden Costume Box# +17786#Gothic Costume Egg# +17787#2017 April Costume Box# +17788#Overseas Care Package IV# +17790#Overseas Care Package V# +17801#May Costume Box# +17802#Overseas Care Package VI# +17804#Overseas Care Package VII# +17805#Scrambled Anniversary Costume Box# +17806#Costume Eremes' Scarf (Black) Box# +17807#July Costume Box# +17837#Silvervine 1 Box# +17838#Silvervine 3 Box# +17839#Silvervine 10 Box# +17840#Box of Quivers# +17841#Box of Iron Quivers# +17842#Box of Steel Quivers# +17843#Box of Oridecon Quivers# +17844#Box of Fire Quivers# +17845#Box of Silver Quivers# +17846#Box of Wind Quivers# +17847#Box of Stone Quivers# +17848#Box of Crystal Quivers# +17849#Box of Shadow Quivers# +17850#Box of Immaterial Quivers# +17851#Box of Rusty Quivers# +17852#Costume Box I: Feline Fine# +17853#Costume Box II: Gothic Egg# +17854#Costume Box III: Helmets Galore# +17855#Costume Box IV: Jellopy Pink# +17856#Costume Box V: Cute Pets# +17857#Costume Box VI: Fall In# +17858#Costume Box VII: Fairy Dreams# +17859#Costume Box VIII: Spoopy Spectre# +17860#Costume Box IX: Spooptacular# +17861#Costume Box X: Dreamland# +17862#Costume Box XI: Creature Feature# +17863#Costume Box XII: Winter is Here# +17864#Costume Box I Feline Fine# +17865#Costume Box II: Gothic Egg# +17866#Costume Box III: Helmets Galore# +17867#Costume Box IV: Jellopy Pink# +17868#Costume Box V: Cute Pets# +17869#Costume Box VI: Fall In# +17870#Costume Box VII: Fairy Dreams# +17871#Costume Box VIII Spoopy Spectre# +17872#Costume Box IX Spooptacular# +17873#Costume Box X: Dreamland# +17874#Costume Box XI: Creature Feature# +17876#Costume Box: Garment Edition# +17877#RESTARTer Pack# +17878#RESTARTer Pack Plus# +17892#Overseas Care Package X# +17893#Overseas Care Package XI# +17900#Overseas Care Package XII# +17903#Overseas Care Package XIII# +17927#Overseas Care Package XIV# +17928#Costume Box XIV: Stay Frosty# +17938#Overseas Care Package XV# +17939#Spooky January Costume Box# +17946#Overseas Care Package XVII# +17971#Life Potion Sampler 10 Crate# +17972#Life Potion Sampler Crate# +17981#Infinity Drink 200 Box# +17982#Infinity Drink 20 Box# +18000#Cannon Ball# +18001#Holy Cannon Ball# +18002#Dark Cannon Ball# +18003#Soul Cannon Ball# +18004#Iron Cannon Ball# +18101#Rental Bow Of Rudra# +18103#Mystic Bow# +18106#Eden Group Bow3# +18107#Malang Snow Crab# +18108#Brindle Eel# +18109#Catapult# +18110#Big Crossbow# +18111#Creeper Bow# +18113#Bellum Arbalest# +18114#Bellum Crossbow# +18116#Metal Bow# +18118#TE WoE Bow# +18119#Thanatos Bow# +18120#Evil Slayer Piercer Bow# +18121#Vicious Mind Bow# +18122#Gigantic Bow# +18123#Bow Of Storm# +18126#Ru Blue Bow# +18127#Ru Gold Bow# +18129#Ixion Wing# +18130#Crimson Bow# +18145#Vigilante Bow# +18149#Illusion Ballista# +18164#Royal Bow# +18170#Narcissus Bow# +18178#Virtual Bow-OS# +18179#MH-P89-OS# +18180#AC-B44-OS# +18189#Bloody Bow# +18190#Bolt Shooter# +18199#The Big Bow# +18505#Umbala Spirit# +18507#Elven Ears# +18508#Garuda Hat# +18509#RWC 2010 Indonesia# +18514#Eden Group Hat II# +18518#Costume Angel Wing Ears# +18520#Jaty Crown# +18521#Lucky Clover# +18522#Evil Marching Hat# +18523#Super Scell# +18525#Watermelon Hat# +18526#Yummy Lollipop# +18527#Gloomy Pumpkin Hat# +18532#Heart Ribbon Hairband# +18536#Foxtail# +18537#Malangdo Hat# +18538#Spirit Whispers# +18539#Skull Cap# +18540#Demon Mask# +18541#Little Feather Hat# +18542#Benevolent Guardian# +18543#Witch Hat# +18551#Galaxy Circlet# +18553#Christmas Tree Hat# +18556#Angel's Symbol# +18557#Devil's Symbol# +18558#Sinsuncho Hat# +18563#Heart Wing Hairband# +18564#Love Piece# +18565#St Patrick's Hat# +18570#Ancient Gold Deco# +18571#Lucky Hat# +18574#Lord of the Dead Helm# +18575#Wunderkammer# +18576#Yin Yang Earrings# +18580#Yggdrasil Crown# +18581#Red Tiger Mask# +18582#Blue Tiger Mask# +18583#Navy Drooping Kitty# +18584#Brown Drooping Kitty# +18585#Orange Bunny Band# +18586#Violet Bunny Band# +18587#Blue Bunny Band# +18588#Silvah Bunny Band# +18589#Strawberry Hat# +18590#Gemma Hairband# +18591#Mini Glasses# +18593#Fancy Mini Crown# +18595#Ancient Horns# +18596#Sprout Hat# +18597#Mercury Riser# +18599#Black Devil's Mask# +18600#Cat Ear Beret# +18601#Red Pom Band# +18602#Watermelon Slice# +18603#Black Devil's Mask# +18604#Falcon Mask# +18605#Dark Age# +18606#Tear Drop# +18607#Blush# +18608#Small Ribbons# +18609#Dark Blinder# +18611#Black Frame Glasses# +18612#White Musang Hat# +18613#Black Musang Hat# +18614#Grim Reaper# +18615#Injured Eyepatch# +18616#Long Tongue# +18617#Onigiri# +18618#Rockabilly Hair# +18619#Thief Bandana# +18620#Heart Eyepatch# +18621#Mobster's Disguise# +18626#Chewy Gelato# +18627#Dried Leaf# +18628#Tare Brownie# +18629#B Desert Wolf Hat# +18630#Drooping Alicel# +18632#Yellow Poring Hairpin# +18633#Pink Poring Hairpin# +18634#Green Poring Hairpin# +18635#Blue Poring Hairpin# +18637#Ancient Admiral Helm# +18638#Citron Hat# +18639#Naval Officer Hat# +18640#Starfish Headband# +18641#Ribbon Magic Hat# +18642#Scissorhand Model# +18643#Rockhand Model# +18644#Paperhand Model# +18645#Sailor Hat# +18646#Cow Hat# +18647#Stunning Star Eyepatch# +18648#Tongue Charm# +18649#Lude Mask# +18650#RWC Shouting Mouth# +18651#Ignis Cap# +18652#Vanargand Helm# +18653#Deviruchi Headphone# +18655#Goedo Monocle# +18656#Witch's Pumpkin Hat# +18657#Pegasus Ear Wing# +18658#Holy Santa Beard# +18659#Boitata Hat# +18660#Indian Feather Headband# +18661#Trident Helmet# +18662#Antler Fedora# +18663#Sunglasses Baseball Hat# +18664#Stunner Shades# +18665#Orange In Mouth# +18666#CD in Mouth# +18667#Cat Lace Hairband# +18668#Droopy Turtle Hat# +18669#Cowhide Hat# +18670#Handkerchief In Mouth# +18671#Rudolf Hairband# +18673#Chibi Pope# +18674#Planewing Hat# +18675#Green Apple Hat# +18676#Hexagon Spectacles# +18677#Cherry Twig In Mouth # +18678#Leek In Mouth# +18679#Abacus In Mouth# +18680#Frog Hat# +18681#Puppy Ears Hat# +18682#Teardrop# +18683#Carrot In Mouth# +18684#Showy High Cap# +18685#Stardust Hairband# +18690#Sirt Evil Eye# +18691#Black Dragon's Ascension# +18692#Mike Hat# +18693#Sleeping Kitty Cat# +18694#Red Hood# +18695#Phoenix Crown# +18696#Orange Hat# +18697#Magda's Syringe# +18698#Cheesy Snack In Mouth# +18699#Starving Fish Hat# +18700#Rabbit Ribbon# +18701#Ancient Civil Man# +18702#Shaving Cream# +18704#Drosera Hairpin# +18706#Can Hat# +18707#Maneater Flower Hat# +18708#Candy Hat# +18709#Black Knitted Hat# +18711#Electric Sunglass# +18712#Fan In Mouth# +18713#Monkey On Fur Hat# +18714#Hippo Hat# +18716#Strawberry In Mouth# +18718#Rose Hairband# +18720#Magical Booster# +18727#Sedora Hat# +18728#Egir Helm# +18729#RWC2012 MVP Hat# +18730#Criatura Academy Hat# +18731#Valkyrie Randgris Helm# +18732#TE WoE Cap# +18733#TE WoE Bone Helm# +18734#TE WoE Magic eyes# +18736#Unknown request# +18737#Fortier Masque# +18738#Boys White Hat# +18739#Carnation Hairband# +18740#White Phoenix Hair# +18741#Costume Will O Wisp# +18742#Costume Moon and Stars# +18743#Costume Spirit Of Chung E# +18744#Twilight# +18745#Choco Stick In Mouth# +18746#Chilly Breath# +18747#Eyes Of Ifrit# +18748#Gold Ingot Poring Hat# +18749#Majoruros Horn# +18750#Poker Card In Mouth# +18752#Cursed Book# +18753#Rice Dumpling Hat# +18754#Blood Sucker# +18755#Feather Beret# +18756#Black Shiba Inu Hat# +18759#Wood Goblin's Nose# +18760#Improved Mage Hat# +18761#Improved Magician Hat# +18762#Improved Kitsune Mask# +18763#Improved Joker Jester# +18764#Improved Bunny Band# +18765#Enhanced Corsair# +18766#Enhanced Helm of Angel# +18767#Enhanced Hat of the Sun God# +18768#Enhanced Bone Helm# +18769#Improved Munak Hat# +18770#Improved Bongun Hat# +18771#Improved Opera Phantom Mask# +18772#Advanced Binoculars# +18773#Advanced Fin Helm# +18774#Advanced Assassin Mask# +18775#Advanced Welding Mask# +18776#Advanced Angel's Kiss# +18779#RWC Champ Crown First Place# +18780#RWC Champ Crown Second Place# +18781#RWC Champ Crown Third Place# +18782#Butterfly Wing Ear# +18785#King Poring Hat# +18786#Anemos Mask# +18787#Goal Tender Mask# +18791#Shrine Maiden Hat# +18793#Magician's Night Cap# +18794#Ordinary Black Magician Hat# +18795#Night Sparrow Hat# +18796#RWC Champ Crown Fourth Place# +18797#Blue Angel Hairband# +18798#Pink Angel Hairband# +18799#Nero Mask# +18802#Poring Fedora Hat# +18804#Thornbush Hairband# +18805#Eclipse Hat# +18806#Black Rabbit Hat# +18807#White Citron Hat# +18808#Wing Style Spectacle# +18810#Shadowmancer's Helm# +18813#New Wave Sunglasses# +18814#Angel School Cap# +18815#Devil School Cap# +18816#Evoked Angel School Cap# +18817#Evoked Devil School Cap# +18818#Red Pencil In Mouth# +18819#Blue Pencil In Mouth# +18820#Helmet Of Gray# +18821#RWC Commemorative Pin# +18822#Flame Wing Ear# +18823#Imperial Feather# +18824#Mask Of Bankrupt# +18825#Money Lost Spirit# +18827#Valkyrie Circlet# +18832#Rolf Von Gigue the 666th# +18833#Marin Crown# +18834#Sakura Hairband# +18837#Cheering Whistle# +18838#Angeling Woolly Hat# +18839#Poring Sunglasses# +18841#Small Poring Band# +18842#Hat Of Girl# +18843#Small Deviling Hat# +18844#Blue Poring Bubble# +18845#Banshee Master Kiss# +18848#Lush Rose# +18849#Celine's Ribbon# +18850#Polar Bear Cap# +18851#Valentine Heart Hairband# +18852#Tasty Strawberry Hat# +18853#Tasty Strawberry Hat# +18854#Golden Valentine Heart Hairband# +18855#Aviator Hat# +18856#White Drooping Eddga Hat# +18857#Curupira Hat# +18858#Odacious Angeling Balloon# +18859#Angeling Bubble# +18861#Zaha Doll Hat# +18862#Chiu Armor# +18863#Exorcist Robe# +18864#Earth Goddess Flower# +18866#Owlduke Silk Hat# +18867#Vajra# +18868#Assassin's Skull Mask# +18873#Sweet Valentine# +18874#Monocle# +18877#Baron's Evil Eye# +18878#Palace Guard Cap# +18880#Evil Snake Lord Hat# +18882#Kannam On Head# +18883#Black Witch Hat# +18885#Jejecap# +18886#Rainbow Long Octopus# +18887#Aqua Bunny Band# +18888#Maroon Bunny Band# +18889#Golden Bunny Band# +18890#Gray Bunny Band# +18891#Husky Hat# +18892#UFO Poring Hat# +18893#Dragon Claw Helm# +18894#Rainbow Star# +18895#Heavenly Dark Flame# +18896#Pterios Fins# +18897#Sky Crown# +18898#Yggdrasil Herald Crown# +18900#Weisswurst# +18901#Seppl Hat# +18903#Frozen Land Rose# +18904#Man's Pride# +18907#Clinging Panda# +18908#Isabella Red Ear# +18909#Isabella Brown Ear# +18910#Isabella Blue Ear# +18911#Red Flower Hat# +18912#Bell Pigeon# +18913#Gossip Raven# +18914#Baby Deviling# +18915#Blood Butterfly Ears# +18916#Bear Balloon# +18917#99 Love Balloons# +18918#Long Octopus Balloon# +18919#Jakk's Castle Bat# +18921#Costume Tarnished Lamp# +18924#Gentleman Fez# +18925#God of Winds Fan# +18929#Elephant Model Hat# +18930#Gorilla Model Hat# +18931#Lion Model Hat# +18932#Rhino Model Hat# +18933#Mechanical Plant Hat# +18935#There is Something# +18937#Memory of Lovers# +18938#Astro Circle# +18944#Deviruchi Balloon# +18945#Super Cute Doll Hat# +18946#Very Cute Doll Hat# +18953#PRO 10th Cap# +18955#Pirate's Folly# +18956#Chocolate Bomb# +18959#Old Pink Poo Hat# +18961#Lunatic Smelting Hat# +18962#Panda Balloon# +18963#Happy Parrot# +18964#Crown Of Saint# +18965#Shaving Foam# +18967#Jinn Poring Balloon# +18968#Jinn Marin Balloon# +18971#Old Rune Circlet# +18972#Old Mitra# +18973#Old Driver Band (Red)# +18974#Old Driver Band (Yellow)# +18975#Old Shadow Handicraft# +18976#Old Maestro Song's Hat# +18977#Old Midas Whisper# +18978#Old Magic Stone Hat# +18979#Old Blazing Soul# +18980#Old Wind Whisper# +18981#Old Dying Swan# +18982#Old Bone Circlet# +18983#Old Casket of Protection# +18984#Old Camouflage Bunny Hood# +18986#Happy Parrot# +18987#Midgard Serpent Hat# +18992#New Year Hairpin# +18993#Flower Hat# +18994#Dark Veil# +18996#Ribbon Of Lady# +18997#Runaway Chip# +19001#Classic Apple of Archer 2 Week Rental Box# +19002#Classic Asura 2 Week Rental Box# +19003#Classic Rudra Bow 2 Week Rental Box# +19004#Classic Brooch 2 Week Rental Box# +19005#Party Balloon# +19006#Classic Combat Knife 2 Week Rental Box# +19007#Classic Counter Dagger 2 Week Rental Box# +19008#Classic Critical Ring 2 Week Rental Box# +19009#Classic Cutlas 2 Week Rental Box# +19010#Baby Dragon Hat# +19011#Classic Earring 2 Week Rental Box# +19012#Classic Elven Ear 2 Week Rental Box# +19013#LuBu Helmet# +19014#Gravekeeper's Blinker# +19015#Classic Light Epsilon 2 Week Rental Box# +19016#Classic Moonlight Sword 2 Week Rental Box# +19017#Classic Necklace 2 Week Rental Box# +19018#Classic Ring 2 Week Rental Box# +19019#Classic Rosary 2 Week Rental Box# +19020#Survivor's Circlet# +19021#Classic Pole Axe 2 Week Rental Box# +19022#Classic Safety Ring 2 Week Rental Box# +19023#Classic Steel Flower 2 Week Rental Box# +19024#Feathers of Protection# +19025#Classic Spanner 2 Week Rental Box# +19026#Egir Helm# +19027#10 Type Glasses# +19028#Classic Silf Manteau 2 Week Rental Box# +19029#Classic Shell of Resistance 2 Week Rental Box# +19030#Red Beret# +19031#Classic Unholy Touch 2 Week Rental Box# +19032#Classic Whip of Balance 2 Week Rental Box# +19033#Classic Cello 2 Week Rental Box# +19034#Classic Dex Survival Rod 2 Week Rental Box# +19035#(null)# +19036#Brabery Hat# +19037#(null)# +19038#12th Anniversary Crown# +19039#12th Anniversary Elven Ears# +19042#Maneater Flower Hairpin# +19048#Elemental Tights# +19079#Celestial Woman's Flower# +19081#Faceworm Egg Shell# +19082#Bio Protector# +19083#Mask of Hero# +19084#Parfaille Vigilante Hat# +19095#Happy Balloon# +19101#Glast Heim Spectator# +19102#Pale Moon Hat# +19103#Classic New Hairstyle Coupon Box# +19104#Yellow Dragon Skywing# +19105#Classic Max Weight up Scroll 10 Box# +19106#Classic Marriage License Box# +19107#Classic Enriched Oridecon 10 Box# +19108#Wild Poring Rider# +19109#Classic Magical Stone Box# +19110#Classic Neuralizer Box# +19111#Classic Dungeon Teleport 1 10 Box# +19112#Tengu Scroll# +19113#Gemini-S58 Eyes# +19114#Sting Silk Ribbon# +19115#Republic Hat# +19116#Red Baby Dragon Hat# +19117#Poring Sunglasses# +19118#Poring Sunglasses+# +19125#Cylinder Hairband# +19135#Spirit of Chung E# +19136#Spirit of Chung E# +19137#Strawberry Mouth Guard# +19138#Seraphim Coronet# +19139#Survivor's Orb# +19141#Maero Mask# +19143#Poring Balloon# +19146#Marin Balloon# +19147#Drops Balloon# +19148#Santa Poring Balloon# +19149#Poporing Balloon# +19150#Metalring Balloon# +19151#Devilring Balloon# +19152#Angelring Balloon# +19153#Ghostring Balloon# +19154#Arch Angelring Balloon# +19156#Lunar Rainbow# +19162#Scuba Mask# +19163#Catherina Von Blood# +19170#Guardian Processor# +19171#Magician Knit Hat# +19172#Pope Osuwari Head# +19173#Floating Ice# +19181#New Wave Sunglasses# +19201#Classic Battle Manual 10 Box# +19202#Classic Bubble Gum 10 Box# +19203#Classic Rune Butterfly Wing 10 Box# +19204#Classic Giant Flywing 50 Box# +19205#Classic Kafra Card 10 Box# +19206#Classic Insurance 10 Box# +19207#Classic Token of Siegfried 10 Box# +19208#Classic Small Life Potion 10 Box# +19209#Illusion Nurse Cap# +19210#Illusion Apple of Archer# +19212#Malangdo Pirate Hat# +19221#Golden Angel Hairband# +19223#Illusion Cap# +19238#Poring Village Green Onion# +19239#Poring Village Carrot# +19241#Magical Booster# +19244#Rosary Necklace# +19245#Crimson Booster# +19246#Royal Guard Necklace# +19247#Illusion Fancy Flower# +19249#Spell Circuit# +19263#General Helm# +19264#Golden Fish Hat# +19268#Giant Snake Breath# +19269#Happy Flapping Angel Wings# +19274#Openair Headset# +19275#[Rental] Scuba Mask# +19277#Soda In Mouth# +19285#Siegfried's Helmet# +19288#Costume Giant's Helm# +19289#Costume Moon Eyepatch# +19290#Costume Elder Devil Horn# +19291#Costume Shiba Inu# +19292#Costume Smokie Knit Cap# +19293#Costume Under Rimmed Glasses# +19294#Costume Cyber Cat Ear Headphones (Red)# +19296#Fancy Feather Hat# +19299#Tree Sprout# +19300#Ruff Officer# +19306#Heart Card in Mouth# +19308#Amistr Beret# +19314#Royal Mantle# +19326#Book of Soyga# +19327#Seraphim Feather# +19329#Devil's Hand# +19335#Sweets BongBong# +19337#Safety Glasses# +19338#Clover Silk Hat# +19339#Egg Crispinette# +19340#Saint Egg Shell# +19341#Devil Egg Shell# +19342#Bull Hat# +19343#Circlet of Phoenix# +19355#Egg Minihat# +19364#Engineer's Cap# +19377#Mini Melon# +19378#Chile Shrimp Gratin# +19379#Striking Hat# +19380#Floating Ball# +19381#Cloth of Protection# +19382#Pop Popcorn Hat# +19383#Slurp Slurp Hat# +19384#Steamed Alligator with Vegetable# +19385#Special Royal Jelly Herbal Tea# +19386#Soul Haunted Bread# +19387#Experimental Goat Cap# +19388#Suptuous Feast# +19389#Bearfoot Special# +19390#Incredibly Spicy Curry# +19391#Eyes Of Illusion# +19392#Special Toast# +19393#Strawberry Flavored Rice Ball# +19394#Giant Burito# +19395#Tendon Satay# +19396#Special Meat Stew# +19397#Tristram 12# +19398#Heavenly Fruit Juice# +19399#Blood Flavored Soda# +19400#Ascending Dragon Soup# +19401#Classic Alice Doll Box# +19402#Classic Chick Hat Box# +19403#Memories of Summer Noodles# +19404#Classic Kettle Hat Box# +19405#Autumn Taste# +19406#Harvest Festa Hat# +19407#Work Cap# +19408#Classic Robo Eye Box# +19409#Black Feather# +19410#Classic Charming Ribbon Box# +19411#Classic Parade Hat Box# +19412#Classic Vacation Hat Box# +19413#Snow Flower# +19414#Classic Evolved Pipe Box# +19415#White Bird Rose# +19416#Midgarts Glory# +19424#Master's Head# +19425#Master's Head# +19426#King Of Spirit Circlet# +19429#Zarathustra# +19436#Vesper Headgear# +19437#Little Garden# +19444#Star Eyepatch# +19453#Jejecap Type J# +19457#Lovely Heart Hat# +19458#Lunatic on Shoulder# +19459#Lunatic on Shoulder# +19460#Black Veil# +19463#Melonbread Cap# +19464#Costume Melonbread Cap# +19469#Sacred Crown# +19495#Diabolus Wing# +19498#Citrus Ribbon# +19499#Fortunetelling Sealed# +19502#Costume Goggles# +19507#Costume Shining Sun# +19509#Costume Butterfly Ears# +19510#Costume Bolt Ears# +19511#Heart Eyepatch# +19513#Costume Chicken Beak# +19514#Costume Old Timey Mustache# +19515#Costume Yellow Hat# +19516#Costume Singing Bird# +19517#Costume Rooster's Comb# +19518#Costume Rainbow# +19519#Costume Lightning Cloud# +19520#Costume Rain Cloud# +19521#Costume Old Timey Derby# +19522#Costume Mini Crown# +19525#Costume Jack be Dandy# +19526#Costume Helm# +19527#Costume Spiky Band# +19528#Costume Iron Cain# +19529#Costume Angel Wing# +19533#Costume Spore Hat# +19535#Costume Sinsuncho Hat# +19536#Costume: Rose Corsage# +19537#Costume Gryphon Hat# +19538#Costume Full Moon# +19539#Costume: Reginleif Hairband# +19540#Costume: Rabbit Earmuffs# +19541#Costume: White Romantic Flower# +19542#Costume Devil Whisper# +19543#Costume Oliver Wolf Hood# +19544#Costume Drooping Cat Crew# +19545#Costume Boy's Cap# +19546#Costume Valkyrie Helm# +19547#Costume Deviruchi Cap# +19548#Costume Frog Cap# +19549#Costume Magestic Goat# +19550#Costume Blush# +19551#Costume Elven Ears# +19552#Costume Sentimental Flower# +19553#Costume Assassin Mask # +19554#Costume Hahoe Mask# +19555#Costume Crescent Helm# +19556#Costume Kabuki Mask# +19566#Costume Samurai Mask# +19569#Sprout Hat# +19573#Costume Heart Wing Hairband# +19574#Costume Lord of Death# +19577#Costume 10th Anniversary Poring Hat# +19580#Costume Sphinx Helm# +19581#Adventurer's Cap# +19582#Costume Cowboy Hat# +19583#Costume Zorro Mask# +19584#Costume Pirate Dagger# +19585#c Feather Beret# +19587#Costume King Poring Hat# +19589#Costume: Fallen Angel# +19597#Costume Magic Eyes# +19598#Costume: Wondering Wolf Helm# +19599#Costume Imp Hat# +19600#Costume Drooping Kiehl Doll# +19601#Costume Drooping Aliot Doll# +19602#Classic Hunting Cap Box# +19603#Classic Koneko Hat Box# +19604#Brawler's Supply Crate# +19605#Spellcaster's Supply Crate# +19606#Sharpshooter's Supply Crate# +19607#Costume Love Chick Hat# +19608#Costume Chick Hat# +19616#Costume Wickebine's Black Cat Ears# +19617#Costume Puppy Headband# +19618#Costume Kitsune Mask# +19619#Costume Corsair# +19620#Costume: Detective hat# +19621#Costume Evil Wing Ears# +19622#Costume Crescent Hairpin# +19623#Costume Bijofnil Wings# +19624#Costume Blank Eyes# +19625#Costume Bunny Band# +19627#Costume Satellite Hairband# +19629#Costume Tiara# +19630#Costume Crown# +19634#Costume Flu Mask# +19636#Costume Rudolph's Nose# +19643#Costume Wickebine Ears# +19649#Costume White Kitten Ears# +19650#Costume Rainbow Feather Decoration# +19651#C RWC Shouting Mouth# +19652#Costume: Rabbit Magic Hat# +19653#placeholder# +19654#Costume Captain Carocc's Hat# +19655#placeholder# +19656#Creative Convention Hat# +19657#placeholder# +19658#placeholder# +19659#Costume Brown Beanie# +19660#Costume Coppola's Hat# +19661#Costume Sweet Bonnet# +19667#Costume Helm of Dragoon# +19668#Costume Wind Mildstone# +19669#Costume Reginleif Wings# +19670#Costume Southern Cross# +19671#Costume Piggy Bank# +19672#Costume Poring Letter# +19673#Costume Love Guard# +19674#Costume Evil Mask# +19675#Costume Jumping Poring# +19676#Costume Rainbow Poring Hat# +19677#Costume Soulless Wing# +19678#Costume Bell Ribbon# +19679#Costume Blank Eyes# +19680#Costume Tongue Mask# +19681#Costume Silver Tiara# +19682#Costume Santa Poring Hat# +19683#Sweets Candy# +19684#Costume Happy Wig# +19685#Costume Santa Poring Hat# +19686#C Santa Hairband# +19687#Costume Lush Rose# +19688#Costume Katusa# +19689#Costume Ati Atihan# +19690#Costume Dark Snake Lord Hat# +19691#Costume Blue Ribbon# +19692#Costume Peace Pipe# +19693#Costume Triple Poring Hat# +19694#Costume Vane Hairpin# +19695#Costume Kettle Hat# +19696#Costume: Mochiring Hat# +19697#Costume Rudolf Santa Hat# +19701#Costume Red Bonnet# +19702#Costume Santa's Hat# +19707#Costume Polar Bear Cap# +19709#Costume Yellow Ribbon# +19710#Costume Wings of Victory# +19712#Costume Little Angel Doll# +19713#Costume Lucky Clover# +19714#Costume Lady Tanee Doll# +19715#Costume Scarf# +19716#Costume Alice Doll# +19717#Costume Pink Ribbon# +19718#Costume Gothic Headdress# +19721#Costume Darkness Helm# +19722#Costume Black Glasses# +19723#Costume Sacred Torch Coronet# +19724#Costume Pavianne Doll Hat# +19725#Costume: Bread Bag2# +19726#Costume Scarlet Rose# +19727#Costume Deviling Hat# +19728#Costume: Tare Zonda# +19729#Costume Neko Mimi Kafra# +19739#Costume Sleeping Kitty Hat# +19743#Costume Anubis Helm# +19744#Costume Black Tail Ribbon# +19745#Costume Holy Marching Hat# +19746#Costume: Cap Of Blindness# +19747#Costume Thanatos Despero Mask# +19748#Costume Diadem# +19749#Costume Angel's Blessing# +19750#Costume Saint Frill Ribbon# +19751#Costume Light Darkness Crown# +19752#Costume Shelter Wing Ears# +19753#Costume Hat of Fortune# +19754#Costume Good Wedding Veil# +19755#Costume YinYang Earring# +19756#Costume Holy Mother Love# +19757#Costume Water Lily Crown# +19758#Costume King Frog Hat# +19759#Costume Umbrella Hat# +19760#Costume Rainbow Veil# +19761#Costume White Lily# +19762#Costume Happy Peace Proof# +19767#Costume Home Cherry Blossom# +19768#Costume Sakura Coronet# +19769#Costume Mischievous Fairy# +19770#Costume: Japan Winecup# +19771#Costume Butterfly Hairpin# +19772#Costume Honeybee Hat# +19773#Costume Angeling Hairpin# +19774#Costume Emperor Wreath# +19775#Costume Marvelous Wig# +19776#Costume Tomboy Fairy# +19778#King Berry# +19780#Costume Lunatic Knit Hat# +19781#Costume: Angel Wing Ears# +19783#Costume Granpa Stache# +19785#Costume Crunch Toast# +19787#Costume Devoted Eyes# +19788#Costume Heart Eyepatch# +19789#Costume Sweet Gents# +19790#Costume Wedding Veil# +19793#Costume Goblin Mask# +19798#(null)# +19800#Costume Carnation Hairband# +19801#Costume Fox Hat# +19802#Costume Drooping Nine Tail# +19803#Costume Pinwheel Hat# +19804#Costume Red Vane Hairpin# +19805#Costume Taboo Curse Scroll# +19806#Costume Full Bloom Hairpin# +19807#Costume Majestic Helmet# +19808#Costume Blazing Sun# +19809#Costume Purple Cowboy Hat# +19810#Costume Ifrit's Ear# +19811#Costume Beer Cap# +19812#Costume Large Hibiscus# +19813#Costume Icecream Hat# +19814#Costume Shiny Wig# +19815#Costume Lolita Ten Gallon Hat# +19816#Pecopeco Cap Costume# +19817#Costume Ifrit's Breath# +19818#Costume: Droop Morocc Minion# +19819#Costume Necromancer Hood# +19821#Costume Hyegun Hat# +19822#Costume Yellow Bandana# +19823#Costume Kitsune Mask Hood# +19824#Costume Evil Druid Hat# +19825#Costume: Vicious Stop Bandage# +19826#Costume Ice Wing Ears# +19827#Costume Amistr Cap# +19828#Costume Fedora# +19829#Costume Straw Hat# +19830#Costume Sunglasses# +19831#Costume Filir Hat# +19832#Costume Poring Hat# +19833#Costume Fillet# +19834#Costume Baseball Cap# +19835#Costume Lif Doll Hat# +19836#Costume Gigantic Magestic Goat# +19837#Costume Asara Fairy Hat# +19838#Costume Fox Hat# +19839#Costume Vanilmirth Hat# +19840#Costume Hat of the Sun God# +19841#Costume Dragonhelm Copper# +19842#Costume Puppy Hat# +19848#Costume Angeling Hat# +19856#Costume Red Bean Shaved Ice Hat# +19860#Costume Criatura Academy Hat# +19871#Costume Musical Decoration# +19873#Costume Carnival Hat# +19874#Costume Carnival Circlet# +19875#Costume: Love Rabbit Hood# +19876#Costume Rabbit Ear Hat# +19877#Costume: Eyes Of Darkness# +19878#Costume Drooping Bunny# +19879#Costume Black Bunny Band# +19880#Costume Gold Tiara# +19881#Costume: Pretty Rabbit Hood# +19882#Costume Flowerpot Mask# +19883#Costume: Piamette Hood# +19884#Costume Vanargand Helm# +19886#Costume Luxury Sunglasses# +19892#Costume: Night Sparrow Hat# +19893#Costume Ordinary Black Magician Hat# +19894#Costume Shrine Maiden Hat# +19895#Costume: Magician's Night Cap# +19896#Costume pRO 10th Cap# +19900#Cool Pirate Eyepatch# +19903#Costume Witch's Pumpkin Hat# +19904#Costume Skull cap# +19905#Costume Secret Bandana# +19906#Costume 24 Bolt# +19907#Costume Beautiful Phantom Mask# +19908#Costume National flag hat# +19909#Costume White Baby demom hat# +19910#Costume Halloween hat# +19911#Costume Triangle roof hat# +19912#Costume Cat Eyes# +19914#Costume Ferlock's Hat# +19915#Costume Little Devil's Horn# +19917#Costume Gloomy Pumpkin Hat# +19918#Costume Lude Mask# +19919#Costume Quve Mask# +19920#Costume: Whisper Mask# +19922#Costume: Noah Hat# +19923#Costume Dark Age# +19924#Costume Odin Mask# +19925#Costume: One Eyed Glasses# +19926#Costume Ribbon Black# +19927#Costume Drooping Gray Kitty# +19928#Costume Gothic Heartwing Hairband# +19929#Costume Classic Ribbon# +19930#Costume Angel Mini Silk Hat# +19936#Costume Gigantic Water Cap# +19939#Costume Antler# +19951#placeholder# +19952#placeholder# +19953#Costume Parade Hat# +19954#Costume 3D Glasses# +19955#Costume Christmas Tree Hat# +19956#placeholder# +19957#placeholder# +19958#Costume Choir Hat# +19959#Costume Drooping Argiope# +19960#placeholder# +19961#Costume Rune Circlet# +19962#Costume Mitra# +19963#Costume Driver Band# +19964#Costume Driver Band (Yellow)# +19965#Costume Shadow Handicraft# +19966#Costume Maestro Song's Hat# +19967#Costume Midas Whisper# +19968#Costume Magic Stone Hat# +19969#Costume Blazing Soul# +19970#Costume Wind Whisper# +19971#Costume Dying Swan# +19972#Costume Casket of Protection# +19973#Costume Bone Circlet# +19974#Costume Camouflage Bunny Hood# +19976#Costume Cat Santa Hat# +19977#Costume Golden Exclamation# +19978#Costume Silver Exclamation# +19979#Costume Golden Question# +19980#Costume Silver Question# +19982#Costume Santa Hat# +19984#Costume Cold Protection Hat# +19985#Costume Aura Quartz Crown# +19986#Costume Lunatic Hat# +19987#Costume Blue Beanie# +19988#Costume Elder Crown# +19989#Costume Mouton Life# +19990#Costume Eclipse Knit Hat# +19991#Costume Galanthus Guard# +19992#Costume Chilly Breath# +19994#Costume saLUsalo Hat# +19995#Costume Flower Blossom# +19996#Costume Horse King# +19997#Costume Bomb Hat# +19998#Costume Dragon Turtle Hat# +20001#Hardened Coat# +20002#Whip of Balance# +20003#Cello# +20005#Costume Pegasus Wing Ears# +20006#Costume Dark Knight Mask# +20007#Costume Bullock Helm# +20008#Costume General Helmet# +20009#Costume Dragon Skull# +20010#Costume Rainbow Wing Ears# +20011#Costume Lightning Speed# +20012#Costume Double Horn Helm# +20014#Costume Lincoln Hat# +20015#Costume Lincoln Beard# +20016#Costume: Cool Dinner Hat# +20018#Costume Long Wolf Ears# +20019#Costume: Beret Of Artist# +20021#Greenish Shoes# +20022#Soul Shoes# +20031#Costume Bunny Headdress# +20032#Mocked Muffler# +20034#Costume Castle Bat# +20035#Costume Miracle Blue Rose# +20036#Costume Crown of the Sword Master# +20037#Costume Owlduke Silk Hat# +20038#Costume Haunted Armor Helm# +20039#Costume Butterfly Wing Ears# +20040#Costume Rose Crown# +20041#Costume Earth Goddess Flower# +20042#Costume Hermode Cap# +20043#Costume Red Cherry Blossom# +20044#Costume Carmen Miranda's Hat# +20045#Costume Samambaia# +20046#Costume Blue Rose Decoration# +20047#Costume Cherry Blossom Pray# +20048#Costume Wind of the Prairie# +20054#(null)# +20064#Giant Flywing 2500 Box# +20070#Costume Alpaca Hood# +20071#Costume Warg in Mouth# +20085#Costume Green Clover Hat# +20090#Costume Egg Shell# +20091#(null)# +20092#Sale Sign# +20094#Costume Green Ribbon Headband# +20095#Costume Red Ribbon Headband# +20096#Costume Blue Ribbon Headband# +20097#Costume White Ribbon Headband# +20099#Costume Ljosalfar# +20100#Costume Volume Feather Hat# +20106#Costume Classic Hat# +20113#Costume Star Reading Hat# +20121#Rental Underneath Saber# +20122#Rental Underneath Gladius# +20123#Rental Underneath Chain# +20124#Rental Underneath Book# +20125#Rental Underneath Hypnotist's Staff# +20126#Rental Underneath Knuckle Duster# +20127#Rental Underneath Lute# +20128#Rental Underneath Wire# +20129#Rental Underneath Bastard Sword# +20130#Costume Whisper Tall Hat# +20131#Rental Underneath HammerAxe# +20132#Costume Test Subject Aura# +20133#Costume Poring Mascot# +20134#Rental Underneath Crossbow# +20135#Costume: 12th Anniversary Crown# +20136#Costume: 12th Anniversary ElvenEars# +20137#Rental Underneath Drifter# +20138#Rental Underneath Destroyer# +20139#Rental Underneath Thunder P# +20145#Costume Robo Eye# +20146#Costume Nero Mask# +20147#Costume Pigeon on Shoulder# +20152#Costume Straw Rice Bag# +20153#Costume Monochrome Cap# +20154#Costume Falling Leaves# +20155#Costume Lady's Feather Hat# +20156#Costume Fan in Mouth# +20160#Costume Fried Egg Hat# +20161#Rental Saharic Saber# +20162#Rental Saharic Gladius# +20163#Rental Saharic Chain# +20164#Costume Duneyrr Helm# +20165#Rental Saharic Hypnotist's Staff# +20166#Rental Saharic Knuckle Duster# +20167#Rental Saharic Lute# +20168#Rental Saharic Wire# +20169#Rental Saharic Bastard Sword# +20170#Rental Saharic Jur# +20171#Costume Sepia Cap# +20172#Costume Pumpkin Head# +20173#Costume Lude Hood# +20174#Rental Saharic Crossbow# +20175#Costume Diabolic Headphones# +20176#Rental Saharic Cyclone# +20177#Rental Saharic Drifter# +20178#Rental Saharic Destroyer# +20179#Rental Saharic Thunder P# +20183#Costume Floating Ghost# +20190#Costume Chicken Hat# +20191#Costume Black Cat Ears Beret# +20194#Costume Lion Mask# +20195#Costume Scratching Cat# +20196#Costume Leopard Ear Hat# +20197#Costume:Amistr Beret# +20199#Costume Evil Marcher Hat# +20200#Costume Rabbit Head Dress# +20201#Costume Banshee Master Kiss# +20202#Costume Deviruchi Balloon# +20204#Costume Hunting Cap# +20207#Costume Striped Headband# +20217#Costume Veil# +20218#Costume Spell Circuit# +20219#(null)# +20222#Costume Blue Christmas Cheer# +20226#Hair Brush# +20227#Costume Husky Hat# +20230#Costume Bankruptcy Mask# +20231#Costume Snowman Hat# +20232#Costume Celine Ribbon# +20233#Costume Golden Angel# +20234#Costume Baphomet Hat# +20235#Costume Frozen Lands Rose# +20236#Costume Archangeling Hat# +20237#Costume Pink Wool Hat# +20238#Costume Blue Drooping Cat# +20239#Costume Large Ribbon Muffler (Orange)# +20240#Costume Present of Snow# +20241#Costume Eclipse Hat# +20242#Costume Snowysnow Hat# +20243#Costume Mint Chocolate Bonnet# +20246#Costume Time Decor# +20247#Costume Black Hand of Destiny# +20255#Costume Love Cheeks# +20266#Costume Secret Zipper# +20268#Costume Sleep Eclipse Family# +20273#Costume Soft Sheep Hat# +20278#Costume Man's Pride# +20279#Costume Whistle# +20280#Costume Chewed Pencil# +20281#Costume: Kindergartener's Hat# +20282#Costume White Boy's Cap# +20283#Costume Overprotector# +20284#Costume Cherry Blossom Hat# +20285#Costume Fluttering Cherry Blossom# +20286#Costume Under Rimmed Glasses (Red)# +20287#Costume Man's Golden Pride# +20288#Costume Bijou Hat# +20293#Costume Sleeper Hat# +20294#Costume: Maximilian Von Babe XXIX# +20295#Costume Poring Sunglasses# +20296#Costume Yoyo Hat# +20297#Costume Cactus Hat# +20298#Costume Happy Droopy Lunatic Ear# +20299#Costume Face Crasher# +20300#Costume Hill Wind Mask# +20301#Costume Golden Savage Hat# +20302#Costume Beelzebub's Crown# +20306#Costume Artist's Hat# +20311#Costume Magical Booster# +20312#Costume Baron's Evil Eye# +20313#Costume Disk In Mouth# +20314#Costume New Wave Sunglasses# +20315#Costume Analyzing Eye# +20316#Costume Cherub's Winged Helm# +20317#Costume Cyber Cat Ear Headphones# +20318#Costume Charleston's Antenna# +20319#Costume: Crimson Booster# +20321#Classic Underneath Saber 2 Week Rental Box# +20322#Classic Underneath Gladius 2 Week Rental Box# +20323#Classic Underneath Chain 2 Week Rental Box# +20324#Classic Underneath Book 2 Week Rental Box# +20325#Classic Underneath Hypnotist's Staff 2 Week Rental Box# +20326#Classic Underneath Knuckle Duster 2 Week Rental Box# +20327#Classic Underneath Lute 2 Week Rental Box# +20328#Classic Underneath Wire 2 Week Rental Box# +20329#Costume: Very Cute Doll Hat# +20330#Costume Sombrero# +20331#Classic Underneath HammerAxe 2 Week Rental Box# +20332#Classic Underneath Trident 2 Week Rental Box# +20333#Classic Underneath Huuma Giant Wheel 2 Week Rental Box# +20334#Classic Underneath Crossbow 2 Week Rental Box# +20335#Classic Underneath Garrison 2 Week Rental Box# +20336#Classic Underneath Cyclone 2 Week Rental Box# +20337#Classic Underneath Drifter 2 Week Rental Box# +20338#Classic Underneath Destroyer 2 Week Rental Box# +20339#Classic Underneath Thunder P 2 Week Rental Box# +20340#Costume Ponytail (Black)# +20341#Costume Cowlick (Black)# +20342#Costume Twin Ponytail (Black)# +20344#Costume Happy Balloon# +20349#Costume Flying Galapago# +20350#Costume Cowlick (Yellow)# +20351#Costume Cowlick (Green)# +20352#Costume Cowlick (Purple)# +20353#Costume Cowlick (Red)# +20354#Costume Cowlick (Orange)# +20355#Costume Cowlick (Blue)# +20356#Costume Cowlick (White)# +20357#Costume Ponytail (Yellow)# +20358#Costume Ponytail (Green)# +20359#Costume Ponytail (Purple)# +20360#Costume Ponytail (Red)# +20361#Costume Ponytail (Orange)# +20362#Costume Ponytail (Blue)# +20363#Costume Ponytail (White)# +20364#Costume Twin Ponytail (Yellow)# +20365#Costume Twin Ponytail (Green)# +20366#Costume Twin Ponytail (Purple)# +20367#Costume Twin Ponytail (Red)# +20368#Costume Twin Ponytail (Orange)# +20369#Costume Twin Ponytail (Blue)# +20370#Costume Twin Ponytail (White)# +20371#Costume Special Kafra Hat# +20372#(null)# +20373#(null)# +20374#(null)# +20375#(null)# +20376#(null)# +20377#Costume Flying Helmet# +20378#Costume Sky Helm# +20379#(null)# +20381#Costume Steampunk Hat# +20391#Costume Silent Executor# +20392#Costume Sniper Goggle# +20393#Costume Dip Schmidt Helm# +20397#Costume Jakk# +20398#Costume Niflheim Bunny Hat# +20399#Costume Crow Tengu Mask# +20405#Costume Eremes' Scarf# +20407#Costume Test Subject Aura (Red)# +20430#Costume Loyal Servant of Morroc# +20434#Costume Drooping Gunslinger# +20440#Tone of Gold# +20449#Costume Black and White Temptation# +20450#Costume Gram Peony# +20451#Costume Night Sky of Memory# +20453#Costume Wrapping Cloth# +20454#Costume Crown of Ancient Queen# +20455#Costume Republic Hat# +20456#Costume Combat Vestige# +20457#Costume Fluttering Feather# +20458#Costume Wild Poring Rider# +20459#Costume Valhalla Idol# +20468#Costume Harum Beret# +20470#Sweets Chocolate Hat# +20490#Costume Full Bloom Hairpin (Blue)# +20491#Costume Laser of Eagle# +20495#Costume Coati Hat# +20496#Costume Black Shiba Inu Hat# +20497#Costume Umbala Spirit# +20498#Costume Elephant Hat# +20499#Costume Cat Ear Hat# +20500#Costume Archangel Wing# +20507#Costume Poring Bag# +20512#Costume Heroic Backpack# +20514#Costume Sword of Thanatos# +20515#Costume Magic Circle# +20519#Costume Full Bloom Cherry Tree# +20521#Costume Black Cat Backpack# +20524#Costume: Shining Angel Wings# +20534#Costume Big White Cat# +20535#Costume Digital Space# +20537#Costume Falling Red Foliage# +20538#Costume Magic Circle Rainbow# +20541#Costume Angel's Ribboned Wings# +20543#Costume Halloween Poring Bag# +20547#Costume Whisper's Blessing# +20570#Costume Love Candy Pack# +20571#Costume Valkyrie Wings# +20572#Wing of Heart# +20574#Costume Backpack (Red)# +20575#Costume Backpack (Black)# +20577#Costume Balloon Wing# +20582#Costume Bear Backpack# +20584#Costume Fox Tail# +20585#Costume Angry Bear Bag# +20586#Costume Loli Ruri's Moon# +20589#Costume One Wing# +20590#Costume Evil Druid Cross# +20591#Costume Bow on Back# +20593#Costume Frozen Crystal Wings# +20594#Costume Big Ribbon Manteau# +20595#Costume Novice Red Backpack# +20599#Costume Sakura Wings# +20602#Costume Seraphim Wings# +20603# Costume Ulysses Wings# +20604#Costume Blessed Veil# +20605#Costume Leaf Umbrella# +20607#Costume Baldr Wing# +20700#Egir Manteau# +20701#Sol Manteau# +20702#TE WoE Muffler# +20703#TE WoE Cloak# +20704#TE WoE Magic Cloak# +20705#Lumiere Manteau# +20706#Amistr Bag# +20707#Kirin Wing# +20709#Mana Mantle# +20710#Advanced Angelic Cardigan# +20712#Valkyrie Cape# +20714#Assassin's Muffler# +20717#Giant Faceworm Snake Skin# +20718#Giant Faceworm Snake Skin# +20721#Cloak Of Gray# +20725#Ribbon Piamat# +20726#Fire Dragon's Coat# +20728#Water Dragon Coat# +20729#Wind Dragon Coat# +20730#Loyalists Hood# +20732#Supplement Part Con# +20733#Upgrade Part - Engine# +20734#Manteau Of Leafwind# +20735#Manteau Of Flame Heart# +20743#Airship's Cloak# +20744#Felrock's Cloak# +20745#Manteau Of Mistic Froz# +20746#Costume Rudra's Grace# +20747#Manteau Great Nature# +20748#Fallen Warrior Manteau# +20749#Fallen Warrior Manteau# +20752#Golden Wing# +20753#Lian Robe# +20756#Egir Manteau# +20757#placeholder# +20758#placeholder# +20761#Costume Happiness Wings# +20762#Costume GreatDevil Wing# +20763#Costume Amistr Bag# +20773#Excelion Wing# +20774#King of Void's Garment# +20778#Red Lotus Stole# +20783#Hero Manteau# +20788#Doram Manteau# +20789#Luxurious Doram Manteau# +20790#Elegant Doram Manteau# +20797#Etran's Undershirt# +20799#Elemental Towel# +20800#Enforcer Cape# +20802#Teleport Amistr Bag# +20803#Heal Amistr Bag# +20804#Greed Amistr Bag# +20805#Increase AGI Amistr Bag# +20806#Magnum Break Amistr Bag# +20807#Endure Amistr Bag# +20808#Sight Amistr Bag# +20809#Improve Concentration Amistr Bag# +20811#Hiding Amistr Bag# +20812#Kirin Wing# +20813#Survivor's Manteau# +20814#Wakwak Manteau# +20815#Seraphim Robe# +20816#Yoichi's Muffler# +20819#Oxygen Tank# +20820#Elemental Cape# +20821#Golden Scarf# +20822#Mine Worker's Backpack# +20826#Angel feather# +20831#Adventurer's Spirit# +20834#Drifter's Cape# +20836#Skin of Lindwyrm# +20837#Skin of Gwiber# +20838#Illusion Muffler# +20840#Illusion Ancient Cape# +20841#Golden Angel Wings# +20845#Skin of Malach# +20846#Temporal Manteau# +20847#Illusion Survivor's Manteau# +20854#Medical Cape# +20855#[Rental] Oxygen Tank# +20856#YSF01 Manteau# +20859#Phreeoni Wings# +20860#Battle Surcoat# +20861#Sword Wing J# +20863#Menblatt Wing Manteau# +20902#True Hunting Manteau# +20903#Short-term Hunting Manteau# +20922#Leviathan Muffler# +20925#Commander Manteau# +20931#Prism Rangers Scarf# +20932#Old Morroc Shawl# +20933#Illusion Engine Wing Type A# +20934#Illusion Engine Wing Type B# +20935#Lava Leather Manteau# +20936#Lava Leather Muffler# +20937#Lava Leather Hood# +20940#Violet Halo# +20941#Phoenix Muffler# +20942#Manteau Of Guardsman# +20943#Geffen Magic Muffler# +20944#Anti Magic Manteau# +20945#Regia Hunting Manteau# +20946#Dragon Scale Hood# +20947#Clergy's Manteau# +20949#Skin of Ladon# +20952#Mysterious Muffler# +20953#High Adventurer Hood# +20955#Christmas Guardian Tree# +20962#Skin of Typhon# +20963#Temporal Str Manteau# +20964#Temporal Agi Manteau# +20965#Temporal Vit Manteau# +20966#Temporal Int Manteau# +20967#Temporal Dex Manteau# +20968#Temporal Luk Manteau# +20969#Rainbow Muffler# +20973#Spiritual Cloth# +20985#Costume Gourd Sake Bottle# +20986#Scientists Mantle# +20988#Costume Mechanical Butterfly# +20989#Costume Wind-up Screw# +20990#Costume Rotating Gears# +20991#Fairy of Eden# +20993#Avoidance Cape# +21001#Bellum Claymore# +21002#Bellum Katzbalger# +21003#Muramasa# +21005#Metal Two Handed Sword# +21006#TE WoE TwoHand Sword# +21007#Heavy Sword# +21009#Thanatos Great Sword# +21010#Evil Slayer Sword# +21011#Gigant Blade# +21015#Crimson Twohand Sword# +21016#Vicious Mind Two-Handed Sword# +21018#Lindy Hop# +21038#Oriental Sword# +21039#Two handed Sword of Kingdom# +21047#Beam Claymore-OS# +21053#Full Force# +21054#Claw Sword# +21058#Awakened Dragonic Slayer# +21059#The Ultimate# +21202#Costume Eat Coin# +21205#Nut Cracker# +21206#Costume Nut Cracker# +21207#Costume Bull Head# +22000#Temporal Str Boots# +22001#Temporal Int Boots# +22002#Temporal Agi Boots# +22003#Temporal Vit Boots# +22004#Temporal Dex Boots# +22005#Temporal Luk Boots# +22006#Temporal Str Boots# +22007#Temporal Vit Boots# +22008#Temporal Dex Boots# +22009#Temporal Int Boots# +22010#Temporal Agi Boots# +22011#Temporal Luk Boots# +22012#Mana Boots# +22014#Enhanced Variant Shoes# +22015#Advanced Angel's Reincarnation# +22016#Assassin's Shoes# +22017#Fisherman's Shoes# +22032#Exorcist's Shoes# +22033#Boots of Gray# +22035#Hero Nependess Shoes# +22036#Hero Silverleather Boots# +22037#Hero Ungoliant Boots# +22042#Talaria Shoes# +22043#Supplement Part Agi# +22044#Upgrade Part - Booster# +22046#Airship's Boots# +22047#Felrock's Boots# +22049#Sol Shoes# +22050#Gardener Shoes# +22051#Lamor Shoes# +22052#Gravekeeper Shoes# +22055#Sharel Shoes# +22056#Fallen Angel Shoes# +22057#Flow Shoes# +22058#Dragon Trainer Shoes# +22059#Egir Shoes# +22060#Hunter Shoes# +22061#Lumiere Shoes# +22062#Mechanic's Shoes# +22064#Thorny Shoes# +22067#Witch Shoes# +22069#Lian Shoes# +22076#Wooden Slippers# +22077#Red Eco Boots# +22083#Doram Shoes# +22084#Luxurious Doram Shoes# +22085#Elegant Doram Shoes# +22091#Fisherman's Shoes# +22092## +22093#Exorcist's Shoes# +22094#Gardener Shoes# +22095#Gravekeeper Shoes# +22096#Fallen Angel Shoes# +22097#Dragon Trainer Shoes# +22099#Mechanic Shoes# +22101#Angel Poring Shoes# +22103#Excelion Leg# +22104#Pororoca Shoes# +22106#Giant Boots# +22107#Modified Strength Boots# +22108#Modified Intelligence Boots# +22109#Modified Agility Boots# +22110#Modified Vitality Boots# +22111#Modified Dexterity Boots# +22112#Modified Luck Boots# +22113#Modified Strength Boots# +22114#Modified Intelligence Boots# +22115#Modified Agility Boots# +22116#Modified Vitality Boots# +22117#Modified Dexterity Boots# +22118#Modified Luck Boots# +22120#Shoes Of Punishment# +22131#Spurred Boots# +22133#Illusion Shoes# +22134#Enforcer Shoes# +22138#Devil Worshipper Shoes# +22141#YSF01 Greaves# +22145#Tengu Shoes# +22168#True Hunting Boots# +22169#Short-term Hunting Boots# +22170#Survivor's Shoes# +22171#Ancient Hero Boots# +22172#Gray Wing Boots# +22174#Scaraba High Heels# +22189#Pilgrim Shoes# +22195#Booster Shoes# +22196#Illusion Leg Type A# +22197#Illusion Leg Type B# +22198#Traveler Shoes# +22199#Lava Leather Boots# +22200#Lava Leather Shoes# +22201#Lava Leather Sandles# +22206#Regia Hunting Boots# +22207#Imperial Boots# +22208#Dragon Scale Boots# +22209#Clergy's Boots# +22210#Fluffy Fish Shoes# +22215#High Adventurer's Sandals# +22234#Frontier Boots# +22238#Great Hero's Boots# +22507#Old Scroll# +22508#Eden Group Mark# +22511#Fenrir's Power Scroll# +22512#August Lucky Box# +22514#Candy Holder# +22515#Twisted Time Key# +22516#Dark Red Clot# +22517#Loki Summon Scroll# +22534#Closed Mind Box# +22535#Worker Scroll# +22536#Worker Scroll# +22537#Prize Of Hero# +22538#Hanbok bag# +22540#Lux Anima Runestone# +22542#[Event] Concentration Potion# +22543#[Event] Berserk Potion# +22544#[Event] Awakening Potion# +22545#[Event] Speed Potion# +22546#[Event] Speed Potion# +22547#[Event] Anti-Pain Med# +22548#[Event] Cursed Water# +22549#Poison Bottle# +22550#Cookie Bag# +22551#[Event] Teacake# +22552#[Event] Fried Pastry# +22553#[Event] Rainbow Bread# +22554#[Event] First Aid Box# +22564#Ides Of March Lucky Box# +22565#April Groove Pack# +22566#Frost Crystal# +22567#Squad Prize# +22568#Nydhogg Summon Scroll# +22594#Devoted Eyes Box# +22606#May Groove Pack# +22607#Isis Merc Scroll# +22611#Packing Envelope# +22612#Corrupt Reagents# +22613#Contaminated Reagents# +22621#Squid Bbq# +22650#Lucy's August Lucky Box# +22651#Lucy's August Lucky Box Crate# +22652#Brilliant Hat Box# +22658#Braised Short Ribs# +22659#Braised Spare Ribs# +22660#Sealed Envelope# +22669#October Spooky Trade Box# +22670#DARK INVITATION# +22679#Death's Chest# +22685#Single Union Christmas Gift# +22686#Single Cookie# +22687#Sentimental Fragment# +22699#Test Reagent# +22702#STR Soul Potion# +22703#AGI Soul Potion# +22704#VIT Soul Potion# +22705#INT Soul Potion# +22706#DEX Soul Potion# +22707#LUK Soul Potion# +22708#Thrilling Box# +22737#Bloody Bullet Case# +22738#Silver Bullet Case# +22739#Sphere Pack Lightning# +22740#Sphere Pack Blind# +22741#Sphere Pack Poison# +22742#Sphere Pack Ice# +22744#Armor Piercing Bullet Case# +22745#Blazing Bullet Case# +22746#Freezing Bullet Case# +22747#Electric Bullet Case# +22748#Magic Stone Bullet Case# +22749#Purifying Bullet Case# +22760#Argiope CaptureBox# +22761#LuciolaVespa CaptureBox# +22762#Centipede CaptureBox# +22770#Grilled Shark Skewer# +22771#Grilled Tuna Skewer# +22772#Grilled Snapper Skewer# +22773#Grilled Piranha Skewer# +22774#Grilled Salmon Skewer# +22775#Grilled Eel Skewer# +22776#Grilled Carp Skewer# +22815#July Groove Pack# +22819#Citizen Oda's Miracle Elixr# +22822#Summer Vacation Suit# +22826#Costume Enchant Stone Box# +22838#Something Candy Holder# +22841#Pumpkin Candy# +22847#Prontera Badge# +22848#Prison Key# +22849#Prontera Time Crystal# +22868#Costume Enchant Stone Box 5# +22870#Christmas Package# +22881#Hunting Rope# +22882#Octo Chocolate Rice Soup# +22899#City Map# +22901#Mysterious Blue Box# +22905#Costume Enchant Stone Box 6# +22911#Weapon +7 Refine Ticket# +22912#Weapon +8 Refine Ticket# +22913#Weapon +9 Refine Ticket# +22915#Weapon +11 Refine Ticket# +22916#Weapon +12 Refine Ticket# +22917#+13 Weapon Upgrade Certificate# +22926#Armor +7 Refine Ticket# +22927#Armor +8 Refine Ticket# +22928#Armor +9 Refine Ticket# +22930#Armor +11 Refine Ticket# +22931#Armor +12 Refine Ticket# +22932#+13 Armor Upgrade Certificate# +22943#Armor +10 Refine Ticket# +22944#Weapon +10 Refine Ticket# +22945#Cherry Blossom Rice Cake# +22947#Strength Soul Potion Box# +22948#Agility Soul Potion Box# +22949#Vitality Soul Potion Box# +22950#Intelligence Soul Potion Box# +22951#Dexterity Soul Potion Box# +22952#Lucky Soul Potion Box# +22971#Mad Bunny Scroll# +22972#Sealed Hat Box I# +22973#Shadow Cube (Weapon)# +22974#Shadow Cube (Armor)# +22979#[Not for Sale] Battle Manual and Bubble Gum# +22984#Kahluna Milk# +22985#Basil# +22988#Sealed Hat Box II# +22991#Soccer Cake# +22999#[Event] Transformation Pack# +23008#[24H] Bubble Gum# +23010#[24H] Battle Manual# +23011#[24H] Life Insurance# +23016#Cursed Fragment# +23025#Pope Mercenary Scroll# +23026#Casual Pope Mercenary Scroll# +23027#Dandelion Ring# +23036#Pope Mercenary Scroll# +23037#Casual Pope Mercenary Scroll# +23042#[Not for Sale] Yggdrasil Seed# +23045#Montblanc Cake# +23058#Costume Enchant Box 9# +23061#Sharp Arrow Quiver# +23063#Frozen Dream# +23073#Angel Poring Shoes Box# +23076#Build Up Potion SS# +23077#Build Up Potion SC# +23078#Build Up Potion AC# +23079#Strawberry Cream Cake# +23080#Cursed Crystal# +23081#Kunai Scroll of Flame# +23082#Kunai Scroll of Icicle# +23083#Kunai Scroll of Storm# +23084#Kunai Scroll of Sand# +23085#Kunai Scroll of Poison# +23086#Costume Enchant Stone Box 10# +23087#Small Leather Bag# +23115#Class Shadow Cube# +23123#Flare Bullet Case# +23124#Lightning Bullet Case# +23125#Ice Bullet Case# +23126#Poison Bullet Case# +23127#Blinding Bullet Case# +23135#Poring Capsule# +23136#Butterfly Wing Box# +23142#Minor Growth Potion# +23143#Minor Growth JPotion# +23144#Minor Growth Potion Box# +23145#Minor Growth JPotion Box# +23148#[2Day] Premium Subscription Box# +23149#[3Day] Premium Subscription Box# +23164#Granpa Speed Potion# +23165#Premium Service Personnel# +23169#Alchemist Box# +23174#Costume Enchant Stone Box 11# +23187#Sap Jelly# +23188#Airship Part# +23189#Small Doll Needle# +23191#Varetyr Spear (level 5) Scroll# +23192#Diamond Dust (level 5) Scroll# +23193#Crimson Rock (level 5) Scroll# +23194#Sienna Execrate (level 5) Scroll# +23198#Premium Wooden Box# +23199#Premium Golden Box# +23200#Premium Platinum Box# +23201#Premium Normal Box# +23202#Mulled Wine# +23207#[30Day] Premium Subscription Box# +23209#[1Day] Premium Subscription Box# +23210#[4Day] Premium Subscription Box# +23211#[5Day] Premium Subscription Box# +23212#[6Day] Premium Subscription Box# +23213#[7Day] Premium Subscription Box# +23214#[8Day] Premium Subscription Box# +23215#[9Day] Premium Subscription Box# +23216#[10Day] Premium Subscription# +23228#Hazy Mooncake# +23236#Class Shadow Box(Weapon)# +23237#Class Shadow Box(Armor)# +23238#Class Shadow Box(Shoes)# +23239#Class Shadow Box(Shield)# +23240#Class Shadow Box (Pendant)# +23241#Class Shadow Box (Earring)# +23247#Status Shadow Item Combiner# +23248#Gemstone Shadow Mix Recipe# +23251#Rose Bundle A# +23252#Orleans's Bundle A# +23253#Black Shiba Inu Bundle A# +23254#Valkyrie Bundle A# +23255#Kardui Bundle A# +23256#Spiritual Bandage# +23257#Old Tree's Dew# +23258#Stinky Rotten Meat# +23259#Strawberry Cream Cupcake# +23260#Fruit Parfait# +23261#Moist Macaron# +23265#Sprint Bundle A# +23266#Etran Bundle A# +23277#Mado Emergency Gear# +23280#Beginners Fly Wings# +23281#Race Shadow Mix Box# +23288#Zipped Fly Wings# +23293#Bison Bundle A# +23294#Pororoca Shoes Bundle A# +23295#Lian Bundle B# +23296#D.Burger# +23297#Lovely Egg Box# +23299#Enchant Stone Box 12# +23302#Poring Treasure Box# +23313#Grave Blessing 1# +23314#Grave Blessing 2# +23315#Grave Blessing 3# +23316#Grave Blessing 4# +23320#Melon Juice# +23321#Sweet Melon Juice# +23322#Melon Parfait# +23340#[Event] Megaphone# +23342#[1 Day] Consignment Merchant Envelope Lv1# +23343#[1 Day] Consignment Merchant Envelope Lv2# +23344#[1 Day] Consignment Merchant Envelope Lv3# +23345#[1 Day] Personal Shopper Envelope Lv1# +23346#[1 Day] Personal Shopper Envelope Lv2# +23347#[1 Day] Personal Shopper Envelope Lv3# +23348#Consignment Merchant Voucher Lv1# +23349#Consignment Merchant Voucher# +23350#Consignment Merchant Voucher Lv3# +23351#Personal Shopper Voucher Lv1# +23352#Personal Shopper Voucher# +23353#Personal Shopper Voucher Lv3# +23354#[3 Day] Consignment Merchant Envelope Lv1# +23355#Consignment Merchant Envelope# +23356#[3 Day] Consignment Merchant Envelope Lv3# +23357#[3 Day] Personal Shopper Envelope Lv1# +23358#Personal Shopper Envelope# +23359#[3 Day] Personal Shopper Envelope Lv3# +23361#Serum# +23362#Meatball# +23363#Watermelon Pudding# +23367#Spaghetti and Sauce# +23368#Special Shopper Catalog# +23414#Overseas Care Package VIII# +23415#Orleans Bundle C# +23436#Holgren Shadow Hammer# +23445#Home Food# +23446#Savage Trap# +23451#Devil Worshipper Box A# +23452#Kardui Bundle B# +23475#Infinity Drink# +23477#September Costume Box# +23504#[Event] Crate of 20 Orange Potions# +23505#[Event] Crate of 20 Yellow Potions# +23506#[Event] Box of 20 White Potion# +23507#October Costume Box# +23508#iRO 2017 Oct CosBox2# +23516#November Costume Box Ani Maul# +23524#Costume Enchant Stone Box 13# +23533#Rose of Choice# +23545#Silver Angel Idol# +23546#Cursed Blood# +23547#Gold Angel Idol# +23548#Snow Candy# +23549#Snow Cookie# +23550#Winter Cookie# +23551#Festi Cookie# +23552#Flora Cookie# +23553#Snow Flower Festival Giftbox# +23554#Snow Festa Card Pack# +23556#Dinner for Hunter# +23588#Winter Wonderland Costume Box# +23600#Prisoner Bundle A# +23601#Imperial Bundle A# +23602#Giant Bundle A# +23603#Sol Bundle B# +23625#Bio Protector Bundle A# +23629#Costume Enchant Stone Box 14# +23657#[Event] Life Insurance# +23658#Lamor Bundle B# +23660#Dogly Bottle Z# +23674#Sweets Festival Box# +23675#Geffen Magic Armor Scroll# +23676#Gray Abrasive (Physical)# +23677#Gray Abrasive (Magic)# +23678#Gray Abrasive (Ranged)# +23679#Geffen Magic Accessory Scroll# +23682#Costume Enchant Stone Box 15# +23683#Lucky Bag# +23684#Phantom's Spirit# +23685#Flower of Phantom# +23706#Charleston Upgrade Part (Melee)# +23707#Charleston Upgrade Part (Range)# +23711#Trap Box# +23720#Shadow Enhancement Box# +23723#Melon Bread# +23725#[Event] Haute Cuisine# +23726#[Event] Challenger Drink# +23727#[Event] Unlimited Drink# +23728#[Event] Power Drink# +23729#[Event] Ayer's Blessing# +23732#[Event] Mimir's Water# +23733#[Event] Valdur's Blessing# +23734#[Event] Abrasive Tool# +23735#[Event] Rapid Potion# +23736#[Event] Illusion Powder Bottle# +23743#Festa Commemorative Can# +23744#Cream Noodles# +23770#Costume Enchant Stone Box 16# +23772#OS Weapon Chest# +23773#Weapon Modification Device Chest (Physical)# +23774#Weapon Modification Device Chest (Magic)# +23775#Modification Module Chest# +23776#Weapon Modification Device (Physical)# +23777#High-grade Weapon Modification Device (Physical)# +23778#Finest Weapon Modification Device (Physical)# +23779#Weapon Modification Device (Magic)# +23780#High-grade Weapon Modification Device (Magic)# +23781#Finest Weapon Modification Device (Magic)# +23799#Overseas Care Package XIX# +23800#July Costume Box# +23806#Ancient Heros Weapon Box# +23815#Magma Essence# +23898#[Event] Power Booster# +23899#[Event] Miracle Elixir# +23919#Kachua's Secret Key# +23921#Event Reward Box# +23923#Poring Sunglasses Box# +23926#Shadow Smelting Hammer# +23962#Upgraded Malangdo Cat Can# +23981#Turbulent Dragon's Power# +23985#Dragon's Treasure# +23986#Odin's Relic# +23987#Overseas Care Package XXIII# +23988#October Costume Treat Box# +23996#Overseas Care Package XXIV# +23997#November Harvest Costume Box# +24012#Shadow Weapon# +24013#Shadow Armor# +24014#Shadow Shoes# +24015#Shadow Shield# +24016#Shadow Earring# +24017#Shadow Pendant# +24018#Physical Shadow Earring# +24019#Physical Shadow Weapon# +24020#Physical Shadow Pendant# +24021#Magical Shadow Earring# +24022#Magical Shadow Weapon# +24023#Magical Shadow Pendant# +24024#Breeze Shadow Armor# +24025#Champion Shadow Shoes# +24026#Athena Shadow Shield# +24027#Immune Shadow Armor# +24028#Hard Shadow Armor# +24029#Ancient Shadow Armor# +24030#Critical Shadow Armor# +24031#Kingbird's Shadow Weapon# +24032#Critical Shadow Weapon# +24033#Healing Shadow Weapon# +24034#Lucky Shadow Weapon# +24035#Power Shadow Earring# +24036#Intelligent Shadow Pendant# +24037#Dexterous Shadow Armor# +24038#Vital Shadow Shoes# +24039#Athletic Shadow Shield# +24040#Lucky Shadow Armor# +24041#Power Shadow Pendant# +24042#Intelligent Shadow Earring# +24043#Dexterous Shadow Weapon# +24044#Vital Shadow Shield# +24045#Athletic Shadow Shoes# +24046#Resist Spell Power Shadow Pendant# +24047#Rapid Shadow Pendant# +24048#Caster Shadow Pendant# +24049#Hard Shadow Earring# +24050#Wise Shadow Earring# +24051#Athena Shadow Earring# +24052#Cranial Shadow Shield# +24053#Safeguard Shadow Shield# +24054#Brutal Shadow Shield# +24055#Gargantua Shadow Shield# +24056#Homer's Shadow Shield# +24057#Dragoon Shadow Shield# +24058#Satanic Shadow Shield# +24059#Flameguard Shadow Shield# +24060#Requiem Shadow Shield# +24061#Cadi Shadow Shield# +24062#Bloody Shadow Shoes# +24063#Liberation Shadow Shoes# +24064#Chemical Shadow Shoes# +24065#Clamorous Shadow Shoes# +24066#Insecticide Shadow Shoes# +24067#Fisher Shadow Shoes# +24068#Seraphim Shadow Shoes# +24069#Beholder Shadow Shoes# +24070#Divine Shadow Shoes# +24071#Dragoon Shadow Shoes# +24072#Large Shadow Armor# +24073#Medium Shadow Armor# +24074#Small Shadow Armor# +24075#Large Shadow Weapon# +24076#Medium Shadow Weapon# +24077#Small Shadow Weapon# +24078#Shadow Spiritual Weapon # +24081#Malicious Shadow Armor# +24082#Malicious Shadow Shoes# +24083#Malicious Shadow Shield# +24084#Gemstone Shadow Armor# +24085#Gemstone Shadow Shoes# +24086#Gemstone Shadow Shield# +24087#Gemstone Shadow Weapon# +24088#Gemstone Shadow Earring# +24089#Gemstone Shadow Pendant# +24090#Stability Shadow Shield# +24091#Plasterer's Shadow Armor# +24093#Shadow Insomniac Armor # +24095#Peerless Shadow Armor# +24097#Shadow Adurate Armor # +24099#Unfreezing Shadow Weapon# +24100#Unfreezing Shadow Earring# +24101#Unfreezing Shadow Pendant# +24104#Neutral Shadow Weapon# +24110#Shadow Caster Weapon # +24111#Spellflow Shadow Shoes# +24112#Spellflow Shadow Armor# +24113#Spellflow Shadow Shield# +24114#Shadow Greed Armor # +24117#Shadow Greed Weapon # +24120#Shadow Heal Armor # +24123#Shadow Heal Weapon # +24126#Shadow Hiding Armor # +24129#Shadow Hiding Weapon # +24132#Cloaking Shadow Armor# +24135#Cloaking Shadow Weapon# +24138#Teleport Shadow Armor# +24141#Teleport Shadow Weapon# +24144#Steal Shadow Armor# +24147#Steal Shadow Weapon# +24152#Solid Shadow Weapon# +24154#Shadow Immortal Armor # +24156#Shadow Executioner Weapon # +24157#Shadow Exorcist Weapon # +24158#Shadow Hunting Weapon # +24159#Shadow Insect_Net Weapon # +24160#Shadow Fishing Weapon # +24161#Shadow Dragon_Killer Weapon # +24162#Shadow Corrupt Weapon # +24163#Vibration Shadow Weapon# +24164#Shadow Holy_Water Weapon # +24165#Scissors Shadow Weapon# +24166#Penetration Shadow Earring# +24167#Penetration Shadow Pendant# +24168#Tempest Shadow Earring# +24169#Tempest Shadow Pendant# +24170#Shadow Magic Executioner Weapon # +24171#Shadow Magic Exorcist Weapon # +24172#Shadow Magic Hunting Weapon # +24173#Shadow Magic Insect_Net Weapon # +24174#Shadow Magic Fishing Weapon # +24175#Shadow Magic Dragon_K Weapon # +24176#Shadow Magic Corrupt Weapon # +24177#Shadow Magic Vibration Weapon # +24178#Shadow Magic Holy_Water Weapon # +24179#Shadow Magic ScissorShadow Weapon # +24180#Bearer's Shadow Armor# +24181#Bearer's Shadow Shoes# +24182#Bearer's Shadow Shield# +24183#Bearer's Shadow Weapon# +24184#Bearer's Shadow Earring# +24185#Bearer's Shadow Pendant# +24186#Basis Shadow Armor# +24187#Hallowed Shadow Armor# +24188#Saharic Shadow Armor# +24189#Underneath Shadow Armor# +24190#Flame Shadow Armor# +24191#Windy Shadow Armor# +24192#Envenom Shadow Armor# +24193#Damned Shadow Armor# +24194#Geist Shadow Armor# +24195#Divine Shadow Armor# +24196#Hasty Shadow Shoes# +24197#Hasty Shadow Armor# +24198#Basis Shadow Shield# +24199#Hallowed Shadow Shield# +24200#Saharic Shadow Shield# +24201#Underneath Shadow Shield# +24202#Flame Shadow Shield# +24203#Windy Shadow Shield# +24204#Envenom Shadow Shield# +24205#Damned Shadow Shield# +24206#Geist Shadow Shield# +24207#Divine Shadow Shield# +24210#Beginner's Shadow Shoes# +24211#Beginner's Shadow Shield# +24212#Rookie's Shadow Shoes# +24213#Rookie's Shadow Shield# +24214#Advanced Shadow Shoes# +24215#Advanced Shadow Shield# +24216#Shadow Attack Armor # +24219#Shadow ColdBolt Armor # +24220#Shadow FireBolt Armor # +24221#Lightning Bolt Shadow Armor# +24222#Shadow EarthSpike Armor # +24223#Shadow Enhance_Force Weapon # +24224#Shadow Force Weapon # +24227#Shadow Enhance_Spirit Weapon # +24228#Spirit Shadow Weapon# +24231#Blitz Shadow Shoes# +24232#Blitz Shadow Shield# +24233#Shadow Exceed Weapon # +24242#Shadow Caster Armor # +24243#Reload Shoes Shadow# +24244#Reload Shield Shadow# +24245#Reload Shadow Armor# +24246#Swordman Shadow Earring# +24247#Merchant Shadow Earring# +24248#Acolyte Shadow Earring# +24249#Magician Shadow Earring# +24250#Swordman Shadow Pendant# +24251#Merchant Shadow Pendant# +24252#Acolyte Shadow Pendant# +24253#Thief Shadow Pendant# +24254#Magician Shadow Pendant# +24255#Archer Shadow Pendant# +24256#Knight Shadow Shoes# +24257#Crusader Shadow Shoes# +24258#Blacksmith Shadow Shoes# +24259#Alchemist Shadow Shoes# +24260#Priest Shadow Shoes# +24261#Monk Shadow Shoes# +24262#Assassin Shadow Shoes# +24263#Rogue Shadow Shoes# +24264#Wizard Shadow Shoes# +24265#Sage Shadow Shoes# +24266#Hunter Shadow Shoes# +24267#Bard Shadow Shoes# +24268#Dancer Shadow Shoes# +24269#Knight Shadow Armor# +24270#Crusader Shadow Armor# +24271#Blacksmith Shadow Armor# +24272#Alchemist Shadow Armor# +24273#Priest Shadow Armor# +24274#Monk Shadow Armor# +24275#Assassin Shadow Armor# +24276#Rogue Shadow Armor# +24277#Wizard Shadow Armor# +24278#Sage Shadow Armor# +24279#Hunter Shadow Armor# +24280#Bard Armor Shadow# +24281#Dancer Armor Shadow# +24282#Super Novice Shadow Weapon# +24283#Gunslinger Shadow Weapon# +24284#Taekwon Shadow Weapon# +24285#Ninja Weapon Shadow# +24286#Doram magical Weapon Shadow# +24287#Doram physical Weapon Shadow# +24288#Rune Knight Weapon Shadow# +24289#Royal Guard Weapon Shadow# +24290#Mechanic Weapon Shadow# +24291#Geneticist Weapon Shadow# +24292#Arch Bishop Weapon Shadow# +24293#Sura Weapon Shadow# +24294#Guillotine Cross Weapon Shadow# +24295#Shadow Chaser Weapon Shadow# +24296#Warlock Weapon Shadow# +24297#Sorcerer Weapon Shadow# +24298#Ranger Weapon Shadow# +24299#Maestro Weapon Shadow# +24300#Wanderer Weapon Shadow# +24301#Rune Knight Shield Shadow# +24302#Royal Guard Shield Shadow# +24303#Mechanic Shield Shadow# +24304#Geneticist Shield Shadow# +24305#Arch Bishop Shield Shadow# +24306#Sura Shield Shadow# +24307#Guillotine Cross Shield Shadow# +24308#Shadow Chaser Shield Shadow# +24309#Warlock Shield Shadow# +24310#Sorcerer Shield Shadow# +24311#Ranger Shield Shadow# +24312#Maestro Shield Shadow# +24313#Wanderer Shield Shadow# +24314#Ninja Shield Shadow# +24315#Taekwon Shield Shadow# +24316#Doram Physical Shield Shadow# +24317#Doram Magical Shadow Shield# +24318#Super Novice Shadow Shield# +24319#Gunslinger Shadow Shield# +24322#Gemstone Shadow Earring II# +24335#Gemstone Shadow Weapon II# +24336#Gemstone Shadow Shield II# +24339#Almighty Shadow Earring# +24340#Almighty Shadow Pendant# +24341#All Race Shadow Shoes# +24342#All Race Shadow Shield# +24360#Tension Shadow Weapon# +24361#Tension Shadow Earring# +24362#Tension Shadow Pendant# +24363#Elegant Shadow Weapon# +24364#Elegant Shadow Earring# +24365#Elegant Shadow Pendant# +24380#Sentimental Shadow Weapon# +24381#Sentimental Shadow Earring# +24382#Sentimental Shadow Pendant# +24383#Enchanting Shadow Weapon# +24384#Enchanting Shadow Earring# +24385#Enchanting Shadow Pendant# +24402#Rebellion Shadow Armor# +24403#Kagerou Shadow Armor# +24404#Oboro Shadow Armor# +24405#Rebellion Shadow Shoes# +24406#Kagerou Shadow Shoes# +24407#Oboro Shadow Shoes# +24408#Doram Physical Shadow Armor# +24409#Doram Physical Shadow Shoes# +24410#Doram Magical Shadow Armor# +24411#Doram Magical Shadow Shoes# +24416#Shadow Transcendental Time Weapon# +24417#Shadow Transcendental Time Armor# +24418#Shadow Transcendental Time Shield# +24419#Shadow Transcendental Time Shoes# +24420#Shadow Transcendental Time Earring# +24421#Shadow Transcendental Time Pendant# +24425#Perfect Size Shadow Weapon# +24426#Perfect Size Shadow Armor# +24427#Magic Exorcist Corrupt Shadow Weapon# +24428#Magic Vibration Dragon Killer Shadow Weapon# +24429#Magic Scissors Hunting Shadow Weapon# +24430#Magic Fishing Insect Shadow Weapon# +24431#Magic Executioner Holy Water Shadow Weapon# +24434#Executioner Holy Water Shadow Weapon# +24435#Fishing Insect Shadow Weapon# +24436#Scissors Hunting Shadow Weapon# +24437#Vibration Dragon Killer Shadow Weapon# +24438#Exorcist Corrupt Shadow Weapon# +24440#Sonic Armor Shadow# +24441#Sonic Shield Shadow# +24442#Sonic Shoes Shadow# +24443#Ignition Shadow Weapon# +24444#Ignition Shadow Pendant# +24445#Ignition Shadow Earring# +24449#Firebreath Weapon Shadow# +24450#Firebreath Pendant Shadow# +24451#Firebreath Earring Shadow# +24455#Aimed Shadow Weapon# +24456#Aimed Shadow Pendant# +24457#Aimed Shadow Earring# +24458#Arrow Shadow Armor# +24459#Arrow Shadow Shield# +24460#Arrow Shadow Shoes# +24461#Shooting Shadow Weapon# +24462#Shooting Shadow Pendant# +24463#Shooting Shadow Earrings# +24479#Skyblow Shadow Weapon# +24480#Skyblow Shadow Pendant# +24481#Skyblow Shadow Earring# +24488#Duple Shadow Armor# +24489#Duple Shadow Shield# +24490#Duple Shadow Shoes# +24497#Magnus Shadow Weapon# +24498#Magnus Shadow Pendant# +24499#Magnus Shadow Earrings# +24500#Rainstorm Shadow Armor# +24501#Rainstorm Shadow Shield# +24502#Rainstorm Shadow Shoes# +24503#Arrow Vulcan Shadow Weapon# +24504#Arrow Vulcan Shadow Pendant# +24505#Arrow Vulcan Shadow Earrings# +24509#Reverberation Shadow Weapon# +24510#Reverberation Shadow Pendant# +24511#Reverberation Shadow Earring# +24518#Crimson Shadow Armor# +24519#Crimson Shadow Shield# +24520#Crimson Shadow Shoes # +24521#Chain Shadow Weapon# +24522#Chain Shadow Pendant# +24523#Chain Shadow Earring# +24524#Triangle Shadow Armor# +24525#Triangle Shadow Shield# +24526#Triangle Shadow Shoes# +24527#Shadowspell Shadow Weapon# +24528#Shadowspell Shadow Pendant# +24529#Shadowspell Shadow Earring# +24533#Feint Shadow Weapon# +24534#Feint Shadow Pendant# +24535#Feint Shadow Earrings# +24536#Rolling Shadow Armor# +24537#Rolling Shadow Shield# +24538#Rolling Shadow Shoes# +24545#Ripperslasher Shadow Weapon# +24546#Ripperslasher Shadow Pendant# +24547#Ripperslasher Shadow Earring# +24548#Dust Shadow Armor# +24549#Dust Shadow Shield# +24550#Dust Shadow Shoes# +24554#Psychic Shadow Armor# +24555#Psychic Shadow Shield# +24556#Psychic Shadow Shoes# +24557#Varetyr Shadow Weapon# +24558#Varetyr Shadow Pendant# +24559#Varetyr Shadow Earring# +24560#Cart Tornado Shadow Armor# +24561#Cart Tornado Shadow Shield# +24562#Cart Tornado Shadow Shoes# +24563#Cart Cannon Shadow Weapon# +24564#Cart Cannon Shadow Pendant# +24565#Cart Cannon Shadow Earrings# +24566#Spore Bomb Shadow Armor# +24567#Spore Bomb Shadow Shield# +24568#Spore Bomb Shadow Shoes# +24578#Vanishing Cannon Shadow Armor# +24579#Vanishing Cannon Shadow Shield# +24580#Vanishing Cannon Shadow Shoes# +24581#Genesis Shadow Weapon# +24582#Genesis Shadow Pendant# +24583#Genesis Shadow Earrings# +25000#SP Absorption Stone (Upper)# +25001#Def Stone(Middle)# +25002#LUK Exchange Stone(Middle)# +25003#STR Exchange Stone(Middle)# +25004#AGI Exchange Stone(Middle)# +25005#INT Exchange Stone(Middle)# +25006#VIT Exchange Stone(Middle)# +25007#DEX Exchange Stone(Middle)# +25008#VIT Exchange Stone(Lower)# +25009#AGI Exchange Stone(Lower)# +25010#DEX Exchange Stone(Lower)# +25011#LUK Exchange Stone(Lower)# +25012#STR Exchange Stone(Lower)# +25013#INT Exchange Stone(Lower)# +25014#Mdef Stone(Lower)# +25015#EXP Stone(Lower)# +25016#ATK Stone(Lower)# +25017#MATK Stone(Lower)# +25041#Pump of Spirit# +25043#Thorny Vine Flute# +25044#Hard Thorny Vine# +25045#Luxurious Cloth# +25046#Boarding Pass# +25047#Kahlunac# +25048#Hearty Lunchbox# +25049#Basilac Clam# +25058#Twinkle Stone (Upper)# +25059#Ghost Stone (Middle)# +25060#Critical Stone# +25061#Range Stone (Middle)# +25062#Greed Stone (Lower)# +25063#Health Stone (Lower)# +25064#SP Stone (Lower)# +25065#Detoxify Stone (Lower)# +25066#Recovery Stone (Lower)# +25067#Casting Stone (Garment)# +25068#ASPDStone (Upper)# +25069#Reload Stone (Upper)# +25070#Reload Stone (Middle)# +25071#Reload Stone (Lower)# +25072#Kyrie Stone (Lower)# +25087#Old Doll# +25088#Dream Fragment# +25127#Silent Energy Particle# +25128#Weak Energy Particle# +25129#Unstable Energy Particle# +25130#Sinister Energy Particle# +25131#Fallen Energy Particle# +25132#Pumpkin Decorations# +25133#Dry White Stem# +25136#Electric Effect(middle)# +25137#Green Floor Effect(bottom)# +25138#Shrink Effect(middle)# +25139#Identify Stone(bottom)# +25141#Exp Stone(Middle)# +25142#Doram token# +25151#Rachel's Revolver# +25152#Cherished Bouquet# +25153#Broken Gun Wreck# +25154#Antique Gunpowder# +25155#Schwartz's Honor Token# +25156#Piece of Chimera# +25157#Fallen Leaves Branch# +25158#Core Jelly# +25159#Heart Hunter's Seal# +25160#Borrowed Book# +25161#Delicious Handmade Cookie# +25162#Crispy Anchovy# +25163#Arms Shop Ad# +25164#Fresh Tea Leaves# +25165#High Class Tea# +25166#Very Shiny Ring# +25167#Old Letter# +25170#Minor Casting Stone(Garment)# +25171#Exp Stone(Upper)# +25172#Variable Casting Stone(Upper)# +25173#Variable Casting Stone(Middle)# +25174#Variable Casting Stone(Lower)# +25175#Lex Aeterna Stone (Middle)# +25176#Blue Aura Effect(Middle)# +25177#Shadow Effect(Middle)# +25178#Pink Glow Effect(Middle)# +25179#Blessing Star# +25180#Old Rings# +25181#Wood Rosary# +25182#Assassin's Mark Dagger# +25183#Decorated Archer's Thimble# +25184#Portable Sewingbox# +25185#Locket Pendant# +25187#Slug Bullet# +25205#Shrink Effect(Lower)# +25224#Whitebody Effect(Middle)# +25225#Crimson Wave Effect(Middle)# +25226#Water Field Effect (Lower)# +25227#Heal Stone(Lower)# +25228#Teleportation Stone(Lower)# +25229#Steal Stone(Lower)# +25231#Suspicious Bottle# +25232#Cheap Lubricant# +25233#Cotton Tufts# +25235#Crafted Stone# +25240#Independence Certificate# +25245#Small pile of bones# +25246#Juice Mix Package# +25247#Purple Ore# +25248#Purple Ore Crate# +25249#Buffalo Bandit Mane# +25250#Rock Ridge Coin# +25251#Fermented Grape Juice# +25252#Sprig of Holly# +25253#A box containing sugar# +25254#Firewood# +25256#Hazy Dream Fragment# +25257#Bloody Love Letter# +25258#Broken Arrow# +25259#Recruitment Leaflet# +25260#Fragment of Purple Ore# +25261#Torn Paper# +25262#Well-dried Clover# +25263#Short Bat Hair# +25264#Cluster of Nightmares# +25265#Shining Spore# +25266#Dried Yggdrasil Leaf# +25267#Suspicious Pentacle# +25268#Sticky Blood# +25269#Mushroom Sap# +25270#Wavy Mane# +25271#Illusion Stone# +25272#Illusion Gemstone# +25276#Clean Bone# +25277#Deadly Poison Powder# +25278#Bandit's Scarf# +25279#Crude Ammo# +25280#Broken Shotgun# +25281#Crude Scimitar# +25282#Worn Revolver# +25283#Brown Muffler# +25284#Swamp Bug Shell# +25285#Brown Rat Tail# +25290#Sweets Coin# +25291#Chocolate Chunk# +25292#Stolen Cacao Beans# +25294#Clover Ticket# +25295#Happiness Clover# +25296#Something Fatal# +25297#Frozen Stone Fragment# +25298#Spirit Jewel# +25299#Snowball# +25300#Ktullanux Eye# +25302#Double Attack Stone(Garment)# +25303#Critical Stone(Garment)# +25304#Critical Stone(Upper)# +25305#Critical Stone(Lower)# +25306#Variable Casting Stone(Garment)# +25309#Dry Twig# +25311#Dark Soul Fragment# +25312#Old Doll# +25313#Old Turtle Shells# +25314#Logbooks# +25315#Shell Fragment# +25316#Old Metal Pieces# +25318#Dalcom Skin# +25319#Cold Box# +25320#Suspicious Melon# +25340#Golden Corn# +25341#Blazing Bead# +25342#Healthy Herbs# +25344#Flat Round Stone# +25358#Data Retrieval Card# +25359#Slightly Aged Antique Box# +25360#Receipt# +25365#Spaghetti# +25366#Noodle Festival Coin# +25367#Promotional Fan# +25375#Essence of Powerful Soul# +25376#Cupet Coin# +25377#Luxurious Pet Food# +25390#Captured Savage# +25391#High-quality branches# +25392#Ticket to anywhere# +25393#Tasty Corn# +25394#Fruit lunch box# +25395#Small Ember# +25401#Dark Fur# +25408#Memory of Gyol# +25409#Champion Stone (Upper)# +25410#Champion Stone (Middle)# +25411#Champion Stone (Lower)# +25412#Sura Stone (Garment)# +25413#Sniper Stone (Upper)# +25414#Sniper Stone (Middle)# +25415#Sniper Stone (Lower)# +25416#Ranger Stone (Garment)# +25417#Professor Stone (Upper)# +25418#Professor Stone (Middle)# +25419#Professor Stone (Lower)# +25420#Sorcerer Stone (Garment)# +25421#Pumpkin Decorations# +25422#Dry White Stem# +25425#Magical Snow Flower# +25426#Warm Cotton# +25445#Lord Knight Stone(Upper)# +25446#Lord Knight Stone(Middle)# +25447#Lord Knight Stone(Lower)# +25448#Rune Knight Stone(Garment)# +25449#Genetic Stone(Garment)# +25450#Biochemist Stone(Upper)# +25451#Biochemist Stone(Middle)# +25452#Biochemist Stone(Lower)# +25453#High Wizard Stone(Upper)# +25454#High Wizard Stone(Middle)# +25455#High Wizard Stone(Lower)# +25456#Warlock Stone(Garment)# +25462#[Event] Costume Coupon# +25464#World Moving Ticket# +25479#Dogly Bottle# +25490#Stalker Stone(Upper)# +25491#Stalker Stone(Middle)# +25492#Stalker Stone(Lower)# +25493#Shadowchaser Stone(Garment)# +25494#Mastersmith Stone(Upper)# +25495#Mastersmith Stone(Middle)# +25496#Mastersmith Stone(Lower)# +25497#Mechanic Stone(Garment)# +25498#Minstrel/Gypsy Stone(Upper)# +25499#Minstrel/Gypsy Stone(Middle)# +25500#Minstrel/Gypsy Stone(Lower)# +25501#Maestro/Wanderer Stone(Garment)# +25502#Rice Cake# +25503#Flower Left by Phantom# +25504#Phantom's Seal# +25511#Chocolate Plate# +25512#Almond# +25627#Gold Card# +25643#Signed Book# +25655#Dalcom Coin# +25656#Stamp Notebook# +25657#Tough Noodle Leather# +25658#Noodle Sap# +25659#Organic Wheat Flour# +25664#Ribbon Noodle# +25665#Dien's Envelope# +25666#Subject Identification Card# +25668#Damaged Weapon# +25669#Mysterious Component# +25670#Modification Module (DEF)# +25671#Modification Module (MDEF)# +25672#Modification Module (VIT)# +25673#Modification Module (LUK)# +25674#Modification Module (STR)# +25675#Modification Module (AGI)# +25676#Modification Module (INT)# +25677#Modification Module (DEX)# +25678#Modification Module (HP Recovery)# +25679#Modification Module (SP Recovery)# +25680#Modification Module (Mana)# +25681#Modification Module (ASPD)# +25682#Modification Module (Critical)# +25683#Modification Module (Ace Archer)# +25684#Modification Module (Health)# +25685#Modification Module (Spirit)# +25686#Modification Module (Heal)# +25687#Modification Module (ATK)# +25688#Modification Module (MATK)# +25689#Modification Module (Sharpshooter)# +25690#Modification Module (Speed)# +25691#Modification Module (Caster)# +25692#Modification Module (Critical)# +25693#Modification Module (Post-skill Delay)# +25694#Modification Module (Fixed Casting)# +25695#Modification Module (Above All)# +25696#Modification Module (Drain Life)# +25697#Modification Module (Drain Soul)# +25698#Modification Module (Magic Healing)# +25699#Modification Module (Magic Soul)# +25700#Modification Module (Unlimited Vitality)# +25701#Modification Module (Spell Buster)# +25702#Modification Module (Fierce Shooter)# +25703#Modification Module (Overpower)# +25704#Modification Module (Fatal Flash)# +25705#Modification Module (Lucky Strike)# +25706#High Priest Stone(Upper)# +25707#High Priest Stone(Middle)# +25708#High Priest Stone(Lower)# +25709#Arch Bishop Stone(Garment)# +25710#Paladin Stone(Upper)# +25711#Paladin Stone(Middle)# +25712#Paladin Stone(Lower)# +25713#Royal Guard Stone(Garment)# +25714#Assassin Cross Stone(Upper)# +25715#Assassin Cross Stone(Middle)# +25716#Assassin Cross Stone(Lower)# +25717#Guillotine Cross Stone(Garment)# +25723#Cor Core# +25728#Shadowdecon ore# +25729#Shadowdecon# +25730#Zelunium Ore# +25731#Zelunium# +25745#Piece of Five# +25753#Khalitzburg Map Piece# +25754#Scaled Map Piece # +25755#Undead Servant's Map Piece # +25756#Jellopy Map Piece # +25758#Drake's Map Piece # +25759#Dragon's Energy (Green)# +25760#Dragon's Energy (Blue)# +25761#Dragon's Energy (Red)# +25762#Dragon's Energy (Gold)# +25763#Dragon's Energy (Purple)# +25764#Dragon's Energy (Silver)# +25765#Dragon Bone (Large)# +25766#Dragon Bone (Small)# +25767#Angel's Dream# +25768#Valkyrie Ingrid's Armor Fragment# +25769#Valkyrie Reginleif's Armor Fragment# +25770#Valkyrie Reginleif's Armor# +25771#Valkyrie Ingrid's Armor# +25797#Sura Stone II(Garment)# +25798#Champion Stone II(Lower)# +25799#Champion Stone II(Middle)# +25800#Champion Stone II(Upper)# +25801#Sorcerer Stone II(Garment)# +25802#Scholar Stone II(Lower)# +25803#Scholar Stone II(Middle)# +25804#Scholar Stone II(Upper)# +25805#Shadow Chaser Stone II(Garment)# +25806#Stalker Stone II(Lower)# +25807#Stalker Stone II(Middle)# +25808#Stalker Stone II(Upper)# +25809#Redium# +25810#Rindium# +25811#Odium# +25812#Purdium# +25813#Whidium# +25814#Dynite# +25815#Solidified Dust# +25816#Dead Trap# +25817#Bizarre Stone Fragment# +25820#Metal Weapon Ticket# +25842#Soul Reaper Stone(Garment)# +25843#Soul Linker Stone(Upper)# +25844#Soul Linker Stone(Middle)# +25845#Soul Linker Stone(Lower)# +25846#Taekwon Master Stone(Upper)# +25847#Taekwon Master Stone(Middle)# +25848#Taekwon Master Stone(Lower)# +25849#Star Emperor Stone(Garment)# +25850#Ninja Stone(Upper)# +25851#Ninja Stone(Middle)# +25852#Ninja Stone(Lower)# +25853#Kagerou Stone(Garment)# +25854#Oboro Stone(Garment)# +25855#Gunslinger Stone(Upper)# +25856#Gunslinger Stone(Middle)# +25857#Gunslinger Stone(Lower)# +25858#Rebellion Stone(Garment)# +25859#Doram Stone(Upper)# +25860#Doram Stone(Middle)# +25861#Doram Stone(Lower)# +25862#Doram Stone(Garment)# +25876#Lottery ticket for harvest# +25891#Mark of an Outstanding Hunter# +26007#Illusion Spectral Spear# +26016#Two handed Lnace of Kingdom# +26023#Glacis Spear# +26100#Paradise Foxtail II# +26101#Paradise Foxtail III# +26107#Elder Staff# +26109#Illusion Staff of Bordeaux# +26110#Candy Cane Rod# +26111#Metal Foxtail# +26118#Shadow Staff# +26138#Wand of Flame# +26139#Wand of Ice# +26151#Rutilus Stick-OS # +26154#Spirit Pendulum# +26155#Meowmeow Foxtail# +26162#Welding Wand# +26164#Electric Fox-OS# +26173#The Rowdy Pipe# +26200#Hippie Rope# +26214#Soul Bell# +26215#Safety Whip# +26219#The Ol' Clothesline# +27012#Kick Step Card# +27013#Kick and Kick Card# +27014#Green Cenere Card# +27015#Repair Robot Turbo Card# +27016#Exploration Rover Turbo_Card# +27017#Scrap Robots Card# +27018#GC109 Card# +27019#DR815 Card# +27020#T W O Card# +27025#Dead King Card# +27026#Fire Condor Card# +27027#Fire Sandman Card# +27028#Fire Frilldora Card# +27029#Fire Golem Card# +27030#Fulbuk Card# +27081#Angry Moonlight Flower Card# +27082#Angry Nine Tail Card# +27083#Resentful Bongun Card# +27084#Resentful Sohee Card# +27085#Resentful Munak Card# +27086#Resentful Soldier Card# +27087#Wizard of Veritas Card# +27088#Fury Hero Card# +27101#Sweet Nightmare Card# +27102#Matte Drainliar Card# +27103#Restless Dead Card# +27104#Angry Dracula Card# +27105#Bomi Card# +27106#Awakened Ferre Card# +27107#Playing Ferre Card# +27108#Singing Ferre Card# +27109#Jitterbug Card# +27110#Angry Gazeti Card# +27111#Angry Snowier Card# +27112#Angry Ice Titan Card# +27113#Awakened Ktullanux Card# +27114#Ominous Solider Card# +27115#Ominous Permeter Card# +27116#Ominous Heater Card# +27117#Ominous Assaulter Card# +27118#Ominous Freezer Card# +27119#Ominous Turtle General Card# +27120#Iara Card# +27121#Piranha Card# +27122#Curupira Card# +27123#Toucan Card# +27124#Jaguar Card# +27125#Headless Mule Card# +27126#Boitata Card# +27147#Humanoid Chimera Card# +27148#Material Type Chimera Card# +27149#Heart Hunter Card# +27150#Toxic Enhancement Chimera Card# +27151#Evil Card# +27152#Cutie Card# +27157#Wood Goblin Card# +27158#Les Card# +27159#Uzhas Card# +27160#Baba Yaga Card# +27161#Mavka Card# +27162#Gopinich Card# +27163#Faceworm Card# +27164#Faceworm Queen Card# +27165#Faceworm Dark Card# +27166#Faceworm Egg Card# +27167#Faceworm Larva Card# +27168#Irene Elder Card# +27169#Payon Soldier Card# +27170#Cowraiders Class 1 Card# +27171#Cowraiders Class 2 Card# +27172#Cowraiders Class 3 Card# +27173#Mine Cowraiders Class 1 Card# +27174#Mine Cowraiders Class 2 Card# +27175#Mine Cowraiders Class 3 Card# +27176#Rockridge Cramp Card# +27177#Rockridge Arclouse Card# +27178#Gaster Card# +27179#Coyote Card# +27180#Mechaspider Card# +27181#Airship Raid Card# +27182#Captain Ferlock Card# +27183#Gigantes Card# +27184#Lord Sakray Card# +27196#Nihil M. Heine Card# +27197#Agnes Lugenburg Card# +27198#Jurgen Wigner Card# +27199#Spica Nerius Card# +27200#Nihil Card# +27201#Agnes Card# +27202#Jurgen Card# +27203#Spica Card# +27209#Sealed Q Scaraba Card# +27211#Sealed Baphomet Card# +27212#Sealed Maya Card# +27213#Sealed Clown Alphoccio Card# +27214#Sealed Professor Celia Card# +27215#Sealed Champion Chen Card# +27216#Sealed Creator Flamel Card# +27217#Sealed Stalker Gertie Card# +27218#Sealed Paladin Randel Card# +27219#Sealed Gypsy Trentini Card# +27220#Sealed Daehyon Card# +27221#Sealed Gioia Card# +27222#Sealed Pyuriel Card# +27224#Sealed Kades Card# +27225#Sealed Timeholder Card# +27249#Archi Card# +27250#Dio Anemos Card# +27251#Geffen Gang Card# +27252#Geffen Thug Card# +27253#Geffen Thief Card# +27254#Pa Monk Card# +27255#Ordre Card# +27256#Blut Hase Card# +27257#Kuro Akuma Card# +27258#Hyper Death Card# +27259#Rechenier Card# +27260#Odorico Card# +27261#Juu Card# +27262#Dy Card# +27263#Fei Kanabian Card# +27264#Evil Shadow Card# +27265#Evil Fanatics Card# +27304#E-EA2S Card# +27305#EL-A17T Card# +27306#Heart Hunter Bellare Card# +27307#Mutant Heart Hunter Bellare Card# +27308#Heart Hunter Sanare Card# +27309#Mutant Heart Hunter Sanare Card# +27310#Plaga Card# +27311#Mutant Plaga Card# +27312#Dolor Card# +27313#Mutant Dolor Card# +27314#Venenum Card# +27315#Mutant Venenum Card# +27316#Twin Caput Card# +27317#Mutant Twin Caput Card# +27318#Miguel Card# +27319#R48-85-BESTIA Card# +27320#E-EA1L Card# +27330#Kronecker Card# +27331#Kronecker G. Heine Card# +27332#Skia Card# +27333#Skia Nerius Card# +27346#Firm Muspell Card# +27347#Firm Kaho Card# +27348#Firm Lava Golem Card# +27349#Firm Explosion Card# +27350#Firm Ground Deleter Card# +27351#Firm Air Deleter Card# +27352#Firm Nightmare Terror Card# +27353#Firm Blazzer Card# +27354#Polluted Raydric Card# +27355#Polluted Raydrick Archer Card# +27356#Frozen Gargoyle Card# +27357#Polluted Sting Card# +27358#Prison Breaker Card# +27359#Ice Ghost Card# +27360#Flame Ghost Card# +27361#Polluted Wander Man Card# +27362#Polluted Queen Spider Card# +27363#Polluted Dark Lord Card# +27389#Wolfe Card# +27390#Wolfe Lugenburg Card# +27391#Poe Card# +27392#Poe Richard Card# +28000#Thanatos Katar# +28001#Evil Slayer Ripper Katar# +28005#Ru Blue Katar# +28006#Ru Gold Katar# +28007#Crimson Katar# +28008#Vicious Mind Katar# +28010#Juliette D Rachel# +28022#Illusion Infiltrator# +28023#Illusion Sharpened Legbone of Ghoul# +28038#Meuchler-OS# +28039#Katar of Shiver# +28043#Piercing Katar# +28045#Bolt Crusher# +28050#Frozen Creul Katar# +28100#Thanatos Axe# +28101#Tornado Axe# +28103#Ru Blue Axe M# +28104#Ru Gold Axe M# +28106#Crimson Twohand Axe# +28107#Vicious Mind Two-Handed Axe# +28116#Mine Worker's Pickaxe# +28130#Avenger# +28136#Blasti-OS# +28139#Grudge# +28140#Saw Axe# +28200#End of Horizon# +28201#Southern Cross# +28202#Southern Cross# +28215#Probation Rifle# +28216#Probation Gatling Gun# +28217#Probation Grenade Launcher# +28218#Probation Shotgun# +28223#Finisher# +28224#Dustfire# +28225#Burning Rose# +28226#Avenger# +28253#HR-S55-OS# +28255#Master Soul Rifle# +28256#Demon Slayer Shot Gun# +28257#Golden Rod Launcher# +28258#Avengers Gattling Gun# +28260#The Fastest Shooter# +28303#Vesper Core 02# +28305#Vesper Core 04# +28310#Sarah's Left Earring# +28311#Sarah's Right Earring# +28319#Cauda Daemonica# +28320#Assassin's Despair# +28324#Steam Starter Ring# +28326#Broken Chip 01# +28327#Broken Chip 02# +28332#Jewelry Ring# +28342#Critical Anklet# +28353#Vesper Gear 02# +28354#City Map# +28355#Shining Holy Water# +28356#Prontera Badge# +28358#Cursed Lucky Clover# +28359#Vesper Gear 04# +28372#Imperial Ring# +28374#Foxtail Ring# +28377#Magical Ring# +28379#Shadow Ring# +28380#Fresh Grass Necklace# +28381#Cute Grass Necklace# +28382#Charming Grass Necklace# +28386#Fallen Monk Rosary# +28387#Bishop Necklace# +28391#Thief Earring Shadow# +28392#Archer Earring Shadow# +28394#Spirit King's Ring# +28410#Sapphire Wrist# +28411#Emerald Earrings# +28413#Basic Saurel Charm# +28414#Fair Saurel Charm# +28415#Advanced Saurel Charm# +28416#Basic Leaf Charm# +28417#Fair Leaf Charm# +28418#Advanced Leaf Charm# +28419#Basic Bunny Charm# +28420#Fair Bunny Charm# +28421#Advanced Bunny Charm# +28422#Shining Branch Charm# +28423#Fresh Tuna Charm# +28424#Plump Earthworm Charm# +28425#Mercenary Ring Type A# +28426#Mercenary Ring Type B# +28429#Arquien's Necklace# +28430#Arch Bishop Ring# +28433#Luminous Blue Stone# +28434#Boxing Gloves# +28437#Hibram's Gloves# +28438#Fairy Leaf Powder# +28441#Vigilante Badge# +28442#Hippie Feather# +28483#Royal Guardian Ring# +28484#Rebellion's Scarf# +28485#Shinobi Sash H# +28491#Hunting Knife# +28492#Thieves' Guide Vol. 1# +28495#Sheriff's Left Badge# +28496#Sheriff's Right Badge# +28501#Kirin Horn# +28502#Mob Scarf# +28503#Keraunos# +28505#Demon God's Ring# +28506#Storm Stone# +28507#Magician's Gloves# +28508#Illusion Skull Ring# +28509#Illusion Ring# +28510#Vampire's Familiar# +28513#Celine's Brooch# +28520#Egir Ring# +28521#Giant's Protection# +28522#Ring_Of_Fallen# +28523#Ring_Of_Disaster# +28531#Blacksmith's Gloves# +28533#Chemical Glove# +28551#Imperial Glove# +28560#Yin Yang Talisman# +28561#Ring of Hero# +28562#True Hunting Ring# +28563#True Hunting Magical Ring# +28564#Valkyrie Drop# +28565#Perverse Demon Mask # +28573#Emerald Ring# +28575#Verus Core# +28594#Temporal Ring# +28596#Toy Ring # +28598#Supplemental Chip# +28600#Ru Blue Book# +28601#Ru Gold Book# +28604#Crimson Bible# +28605#Vicious Mind Book# +28608#Elemental Origin# +28612#Illusion Book of the Apocalypse# +28618#Book of the Sun God# +28619#Prisoner Diary# +28629#Circuit Board-OS # +28630#Exoricist's Bible# +28631#One Sky One Sun# +28634#On Bolt Magic# +28635#Safety Manual# +28640#Papa HooDoo Manual# +28700#Ru Gold Dagger# +28701#Ru Gold Knife# +28702#Ru Gold Ashura# +28705#Crimson Dagger# +28706#Vicious Mind Dagger# +28717#Valkyrie Knife# +28721#Monokage# +28725#Illusion Moonlight Dagger# +28744#Magic Sword# +28755#Kuroiro-OS# +28763#Sharp Wind Sword# +28764#Fog Dew Sword# +28769#Valkyrie Dagger# +28770#Wound Maker# +28771#Metal Detector Mk47# +28772#Jewel Detector Mk47# +28781#The Razor of Ramona# +28900#Royal Guard Shield# +28901#Mad Bunny Special# +28902#Mad Bunny Special# +28903#Scutum# +28910#Imuke Upper Shield# +28913#Ultralight Magic Shield# +28916#Gaia Shield# +28918#Shield Of Chaos# +28921#Anemos Shield# +28922#Illusion Sacred Mission# +28929#Happy Shield# +28930#Mad Bunny Guard# +28931#Mad Bunny Buckler# +28932#Mad Bunny Shield# +28941#Excelion Shield# +28942#Cursed Knight's Shield# +28945#Bloody Knight's Shield# +28946#Purified Knight's Shield# +28951#Nero Shield# +28955#Fotia Shield# +28956## +28959#Jericho Wall# +29000#Rune of Intellect Lv 1# +29001#Rune of Intellect Lv 2# +29002#Rune of Intellect Lv 3# +29003#Rune of Dexterity Lv 1# +29004#Rune of Dexterity Lv 2# +29005#Rune of Dexterity Lv 3# +29006#Rune of Luck Lv 1# +29007#Rune of Luck Lv 2# +29008#Rune of Luck Lv 3# +29009#Rune of Vitality Lv 1# +29010#Rune of Vitality Lv 2# +29011#Rune of Vitality Lv 3# +29013#HP Absorption 3# +29014#STR+3 INT-3# +29015#STR+3 DEX-3# +29016#INT+3 DEX-3# +29017#INT+3 VIT-3# +29018#DEX+3 VIT-3# +29019#DEX+3 AGI-3# +29020#VIT+3 AGI-3# +29021#VIT+3 LUK-3# +29022#AGI+3 LUK-3# +29023#AGI+3 STR-3# +29024#LUK+3 STR-3# +29025#LUK+3 INT-3# +29026#DEF+20# +29027#EXP+2%# +29028#ATK + 1%# +29029#ATK+1%(Lower)# +29030#MATK + 1%# +29031#MATK+1%(Lower)# +29032#SP Absorption 1# +29033#MDEF+4# +29040#Ghost Effect# +29041#Twinkle Effect# +29046#Greed# +29047#Fatal# +29049#HP+100# +29050#SP+50# +29051#Detoxify# +29052#Recovery# +29053#Skill Delay 1 (Upper)# +29054#Skill Delay 1 (Middle)# +29055#Skill Delay 1 (Lower)# +29056#Fixed Casting Decrease# +29057#Kyrie Eleison# +29061#Mettle Lv. 1# +29062#Mettle Lv. 2# +29063#Mettle Lv. 3# +29064#Mettle Lv. 4# +29065#Mettle Lv. 5# +29066#Mettle Lv. 6# +29067#Mettle Lv. 7# +29068#Mettle Lv. 8# +29069#Mettle Lv. 9# +29070#Mettle Lv. 10# +29071#Magic Essence Lv. 1# +29072#Magic Essence Lv. 2# +29073#Magic Essence Lv. 3# +29074#Magic Essence Lv. 4# +29075#Magic Essence Lv. 5# +29076#Magic Essence Lv. 6# +29077#Magic Essence Lv. 7# +29078#Magic Essence Lv. 8# +29079#Magic Essence Lv. 9# +29080#Magic Essence Lv. 10# +29081#Acute Lv. 1# +29082#Acute Lv. 2# +29083#Acute Lv. 3# +29084#Acute Lv. 4# +29085#Acute Lv. 5# +29086#Acute Lv. 6# +29087#Acute Lv. 7# +29088#Acute Lv. 8# +29089#Acute Lv. 9# +29090#Acute Lv. 10# +29091#Master Archer Lv. 1# +29092#Master Archer Lv. 2# +29093#Master Archer Lv. 3# +29094#Master Archer Lv. 4# +29095#Master Archer Lv. 5# +29096#Master Archer Lv. 6# +29097#Master Archer Lv. 7# +29098#Master Archer Lv. 8# +29099#Master Archer Lv. 9# +29100#Master Archer Lv. 10# +29101#Adamantine Lv. 1# +29102#Adamantine Lv. 2# +29103#Adamantine Lv. 3# +29104#Adamantine Lv. 4# +29105#Adamantine Lv. 5# +29106#Adamantine Lv. 6# +29107#Adamantine Lv. 7# +29108#Adamantine Lv. 8# +29109#Adamantine Lv. 9# +29110#Adamantine Lv. 10# +29111#Affection Lv. 1# +29112#Affection Lv. 2# +29113#Affection Lv. 3# +29114#Affection Lv. 4# +29115#Affection Lv. 5# +29116#Affection Lv. 6# +29117#Affection Lv. 7# +29118#Affection Lv. 8# +29119#Affection Lv. 9# +29120#Affection Lv. 10# +29121#Justice of Goddess A# +29122#Justice of Goddess S# +29123#Love of Goddess A# +29124#Love of Goddess S# +29125#Insight of Goddess A# +29126#Insight of Goddess S# +29142#Electric Effect# +29143#Green Floor Effect# +29144#Shrink Effect# +29145#Exp+2%# +29146#Item Appraisal# +29148#Leo Constellation Stone# +29149#Pisces Constellation Stone# +29150#Capricorn Constellation Stone# +29151#Aquarius Constellation Stone# +29152#Scorpio Constellation Stone# +29153#Taurus Constellation Stone# +29154#Reduce Minor Casting# +29155#Lex Aeterna# +29156#Reduce Variable Casting(Upper)# +29157#Reduce Variable Casting(Middle)# +29158#Reduce Variable Casting(Lower)# +29159#Exp+2%# +29160#Blue Aura Effect# +29161#Pink Glow Effect# +29162#Shadow Effect# +29213#HIT + 6# +29224#Whitebody Effect# +29225#Water Field Effect# +29226#Crimson Wave Effect# +29227#Heal# +29228#Steal# +29229#Teleportation# +29238#FLEE+10# +29239#HIT+5# +29241#Ancilla# +29358#Variable Casting Stone# +29359#Critical Stone(Upper)# +29360#Critical Stone(Lower)# +29361#Critical Stone(Garment)# +29362#Double Attack Stone# +29380#ATK + 5# +29381#MATK + 5# +29423#Champion Stone (Upper)# +29424#Champion Stone (Middle)# +29425#Champion Stone (Lower)# +29426#Sura Stone (Garment)# +29427#Sura Stone II(Garment)# +29428#Sniper Stone (Upper)# +29429#Sniper Stone (Middle)# +29430#Sniper Stone (Lower)# +29431#Ranger Stone (Garment)# +29432#Professor Stone (Upper)# +29433#Professor Stone (Middle)# +29434#Professor Stone (Lower)# +29435#Sorcerer Stone (Garment)# +29460#Lord Knight Stone (Upper)# +29461#Lord Knight Stone (Middle)# +29462#Lord Knight Stone (Lower)# +29463#Rune Knight Stone (Garment)# +29464#Biochemist Stone(Upper)# +29465#Biochemist Stone(Middle)# +29466#Biochemist Stone(Lower)# +29467#Genetic Stone(Garment)# +29468#High Wizard Stone(Upper)# +29469#High Wizard Stone(Middle)# +29470#High Wizard Stone(Lower)# +29471#Warlock Stone(Garment)# +29477#Stalker Stone(Upper)# +29478#Stalker Stone(Middle)# +29479#Stalker Stone(Lower)# +29480#Shadowchaser Stone(Garment)# +29481#Mastersmith Stone(Upper)# +29482#Mastersmith Stone(Middle)# +29483#Mastersmith Stone(Lower)# +29484#Mechanic Stone(Garment)# +29485#Minstrel/Gypsy Stone(Upper)# +29486#Minstrel/Gypsy Stone(Middle)# +29487#Minstrel/Gypsy Stone(Lower)# +29488#Maestro/Wanderer Stone(Garment)# +29513#High Priest Stone(Upper)# +29514#High Priest Stone(Middle)# +29515#High Priest Stone(Lower)# +29516#Arch Bishop Stone(Garment)# +29517#Paladin Stone(Upper)# +29518#Paladin Stone(Middle)# +29519#Paladin Stone(Lower)# +29520#Royal Guard Stone(Garment)# +29521#Assassin Cross Stone(Upper)# +29522#Assassin Cross Stone(Middle)# +29523#Assassin Cross Stone(Lower)# +29524#Guillotine Cross Stone(Garment)# +29527#Modification Orb (DEF)# +29528#Modification Orb (MDEF)# +29529#Modification Orb (HP Recovery)# +29530#Modification Orb (Spirit)# +29531#Modification Orb (Health)# +29532#Modification Orb (SP Recovery)# +29533#Modification Orb (Heal)# +29534#Modification Orb (ATK)# +29535#Modification Orb (MATK)# +29536#Modification Orb (Sharpshooter)# +29537#Modification Orb (Speed)# +29538#Modification Orb (Caster)# +29539#Modification Orb (Critical)# +29540#Modification Orb (Global Delay)# +29541#Modification Orb (Fixed Casting)# +29542#Modification Orb (Above All)# +29543#Modification Orb (Drain Life)# +29544#Modification Orb (Drain Soul)# +29545#Modification Orb (Magic Healing)# +29546#Modification Orb (Magic Soul)# +29547#Modification Orb (Unlimited Vitality)# +29548#Modification Orb (Spell Buster)# +29549#Modification Orb (Fierce Shooter)# +29550#Modification Orb (Overpower)# +29551#Modification Orb (Fatal Flash)# +29552#Modification Orb (Lucky Strike)# +29611#Champion Stone II(Lower)# +29612#Champion Stone II(Middle)# +29613#Champion Stone II(Upper)# +29614#Sorcerer Stone II(Garment)# +29615#Scholar Stone II(Lower)# +29616#Scholar Stone II(Middle)# +29617#Scholar Stone II(Upper)# +29618#Shadow Chaser Stone II(Garment)# +29619#Stalker Stone II(Lower)# +29620#Stalker Stone II(Middle)# +29621#Stalker Stone II(Upper)# +29651#Soul Linker Stone(Upper)# +29652#Soul Linker Stone(Middle)# +29653#Soul Linker Stone(Lower)# +29654#Soul Reaper Stone(Garment)# +29655#Taekwon Master Stone(Upper)# +29656#Taekwon Master Stone(Middle)# +29657#Taekwon Master Stone(Lower)# +29658#Star Emperor Stone(Garment)# +29659#Ninja Stone(Upper)# +29660#Ninja Stone(Middle)# +29661#Ninja Stone(Lower)# +29662#Kagerou Stone(Garment)# +29663#Oboro Stone(Garment)# +29664#Gunslinger Stone(Upper)# +29665#Gunslinger Stone(Middle)# +29666#Gunslinger Stone(Lower)# +29667#Rebellion Stone(Garment)# +29668#Doram Stone(Upper)# +29669#Doram Stone(Middle)# +29670#Doram Stone(Lower)# +29671#Doram Stone(Garment)# +29706#Tenacity Lv. 1# +29707#Tenacity Lv. 2# +29708#Tenacity Lv. 3# +29709#Tenacity Lv. 4# +29710#Tenacity Lv. 5# +29711#Tenacity Lv. 6# +29712#Tenacity Lv. 7# +29713#Tenacity Lv. 8# +29714#Tenacity Lv. 9# +29715#Tenacity Lv. 10# +31016#Decorated Evil Tree Card# +31017#Vicious Cookie Card# +31018#Evil Dwelling Box Card# +31019#Creepy Demon Card# +31020#Malicious Baby Ghost Card# +31021#Dancing Marionette Card# +31022#Abandoned Teddy Bear Card# +31023#Celine Kimi Card# +31024#Immortal Cursed Knight Card# +31025#Immortal Wind Ghost Card# +31026#Stephen Jack Ernest Wolf Card# +31027#Costume Pretty Bear# +31028#Costume Black Cat Hoodie# +31029#Costume Pig Nose# +31030#Costume Tiger Face# +31040#Costume Magical Feather# +31041#Costume Kitten Lace Hairband# +31042#Costume Survivor's Circlet# +31043#Costume White Ribbon# +31044#Costume Pink Drooping Kitty# +31045#Costume Blue Rear Ribbon# +31046#Costume White Rose Princess# +31047#Costume First Love Cheeks# +31048#Costume White Lily with Black Ribbon# +31052#Costume Alchemist Mask# +31053#Costume Drooping Eddga# +31054#Costume Sting Hat# +31055#Costume Poring Bubble Pipe# +31056#Costume Exploding Wave (Red)# +31057#Costume Eremes Scarf (Black)# +31058#Costume Chewy Gelato# +31059#Costume Seawater Hat# +31060#Costume Starfish Headband# +31061#Costume Claw Hairpin# +31063#Costume Hair Buns (Blue)# +31064#Costume Hair Buns (Red)# +31065#Costume Hair Buns (Yellow)# +31066#Costume Hair Buns (Green)# +31067#Costume Hair Buns (Black)# +31068#Costume Hair Buns (White)# +31069#Costume Hair Buns (Brown)# +31070#Costume Hair Buns (Purple)# +31087#Costume Dwarf Beard# +31089#Costume Exploding Wave# +31090#Costume Flapping Angeling# +31091#Costume Show Me The Zeny# +31092#Costume Rabbit Ribbon# +31117#Costume Hoplite Helmet# +31118#Costume Assassin's Skull Mask# +31119#Costume Blue Magician Hat# +31120#Costume Vampire Familiar# +31121#Costume Bat Stole# +31122#Costume Bloody Vicious Stop Bandage# +31123#Costume Ghostring Tall Hat# +31125#Costume Queen Anz Revenge# +31132#Costume Christmas Wreath# +31140#Costume Black Cowboy Hat# +31141#Costume Cactus Flower Corsage# +31143#Costume Water Lantern# +31144#Costume Native Feather Headband# +31155#Costume Spinning Eyes# +31158#Costume Squirrel Ear Hat# +31160#Costume Rune Helm# +31161#Costume Tiger Mask# +31162#Costume Shaving Foam# +31163#Costume Sheep Hat# +31164#Costume Brown Stole# +31165#Costume Piggyback# +31166#Costume Teddy Bear Hood# +31167#Costume Happy Droopy Lunatic Ears (Black)# +31168#Costume Mouton Life (Blue)# +31172#Costume Los Pollos Locos# +31176#Costume: Always Watching# +31177#Costume Tail Hat# +31178#Costume Fire Muffler# +31179#Costume Wolf Masquerade# +31180#Costume King Sura Headband# +31181#Costume Rosary's Necklace# +31182#Costume Side Cap# +31183#Costume Fallen Angel's Blessing# +31184#Costume Eye of the Hawk# +31185#Costume Engineer Cap# +31186#Costume Black Cat# +31187#Costume War Princess Ribbon# +31188#Costume Mono Gothic Bonnet# +31189#Costume Red Cat Ears Cape# +31190#Costume Angel's Black Mini Silk Hat# +31195#Chocolate Mini Hat# +31197#Costume Egg Crispinette# +31198#Costume Octopus Hat# +31199#Costume Weird Beard# +31200#Costume Wrapping Ribbon# +31201#Costume Royal Rabbit Crown# +31202#Costume Dog Officer# +31203#Costume Charcoal Stove# +31207#Costume Dokkebi Mask# +31208#Costume Straight Long (Yellow)# +31209#Costume Straight Long (White)# +31210#Costume Side Pigtail (Blue)# +31211#Costume Side Pigtail (Red)# +31212#Costume Side Pigtail (Yellow)# +31213#Costume Side Pigtail (Green)# +31214#Costume Side Pigtail (Black)# +31215#Costume Side Pigtail (White)# +31216#Costume Side Pigtail (Brown)# +31217#Costume Side Pigtail (Purple)# +31218#Costume Low Pony (Blue)# +31219#Costume Low Pony (Red)# +31220#Costume Low Pony (Yellow)# +31221#Costume Low Pony (Green)# +31222#Costume Low Pony (Black)# +31223#Costume Low Pony (White)# +31224#Costume Low Pony (Brown)# +31225#Costume Low Pony (Purple)# +31226#Costume Long Twin (Blue)# +31227#Costume Long Twin (Red)# +31228#Costume Long Twin (Yellow)# +31229#Costume Long Twin (Green)# +31230#Costume Long Twin (Black)# +31231#Costume Long Twin (White)# +31232#Costume Long Twin (Brown)# +31233#Costume Long Twin (Purple)# +31234#Costume Persica# +31245#Costume Bell Flower# +31246#Costume Humming Bird# +31247#Costume Hippo Hat# +31248#Costume Isabella Red Ears# +31249#Costume Hopping Rabbit# +31250#Costume Wonderful Beast Ears# +31251#Costume Cat's Mouth# +31252#Costume White Cat Ears Hat# +31255#Costume Sweet Melon Helmet# +31256#Costume Jaguar Mask# +31258#Costume Glowing White Blush# +31259#Costume Bubble Archangeling Hairband# +31261#Costume Soda In Mouth# +31262#Costume Disposable 3D Glasses# +31263#Costume Disposable Popcorn Hat# +31295#Costume Red Wing Hat# +31296#Costume Strawberry in Mouth# +31298#Costume Eden Group Hat II# +31299#Costume White Rabbit# +31300#Costume Warm Cat Muffler# +31301#Costume Blinking Eyes# +31302#Costume Black Magenta Ribbon# +31303#Costume Memory of the Pasta# +31304#Costume Midsummer Fan# +31318#Costume Gerhard Von Deviruchi 83 Years# +31319#[Rental] Costume Midsummer Fan# +31320#[Rental] Pinwheel Costume# +31324#Costume Kitty DJ# +31325#Costume Queen Scaraba Helm# +31326#Costume Rolf Von Gigue 666# +31327#Costume Wood Goblin Nose# +31328#Costume Faceworm Egg Shell# +31329#Costume Alice Wig# +31330#Costume Fallen Angel Valletta# +31331#Costume Chung E Shinyon Cap# +31332#Costume Black Khalitzburg Knight Helm# +31368#Costume Harvest Festa Hat# +31371#Costume Drooping Sorin Doll Hat# +31372#Costume Drooping Roxie# +31377#Costume Glast Heim Spectator# +31378#Costume Katarina von Blood 60# +31379#Costume Clock Winding Key# +31380#Costume Crow on Shoulder# +31381#Costume Diabolic Lapel# +31382#Costume Punkish Cat Ears# +31383#Costume Volume Low Twin# +31384#Costume Fake Ears# +31385#Costume Gothic Pumpkin Handicraft# +31386#Costume Survivor's Orb# +31387#Costume Jack# +31389#White Bird Rose# +31390#Costume Let It Snow# +31391#Costume Floating Sage Stone# +31392#Costume Radio Antenna# +31393#Costume Vajra# +31394#Costume White Wizard Hat# +31395#Costume Book of Magic# +31396#Costume Sorcerer Hood# +31397#Costume Sitting Pope# +31398#Costume Blinking Small Eyes# +31399#Costume Darkness Veil# +31408#Costume Ragnarok Rush Goat# +31413#Costume Taurus Crown# +31416#Costume Seraphim Coronet# +31419#Costume Angel of Happiness# +31420#Costume Shining Santa Poring# +31432#Costume Floating Ice# +31433#Costume Astro Circle# +31434#Costume Stormy Cloud# +31435#Costume Magician Knit Hat# +31436#Costume White Drooping Cat# +31437#Costume Baby Penguin# +31438#Costume Fluffy Angel Cape# +31439#Costume Fluffy Heart Earmuffs# +31440#Costume Snow Bear Hood# +31441#Costume Penguin Cap# +31445#Costume Feather Fedora# +31446#Costume Toy Syringe# +31447#Costume Pale Moon Hat# +31448#Costume Sting Silk Ribbon# +31449#Costume Blue Rose Eyepatch# +31450#Costume Lolita Two Side Up# +31451#Costume Blue Frill Ribbon# +31452#Costume White Cat# +31453#Costume Large Ribbon Muffler (Black)# +31455#Costume Super Cute Dog# +31459#Sweets BongBong# +31461#Costume Marin Earrings# +31462#Costume Cute Poring Earmuffs# +31463#Costume Flying Drone# +31465#Robins Egg Minihat# +31466#Saint Egg Shell# +31467#Devil Egg Shell# +31470#Costume Tengu Scroll# +31471#Costume Celestial Woman Flower# +31472#Costume Fairy Feathers# +31473#Costume Tipsy# +31474#Costume Straight Long (Black)# +31475#Costume Black Fox Ear Ribbon# +31476#Costume Yellow Cherry Blossom Hat# +31479#Costume Countless Stars# +31482#Costume Lunatic Muffler# +31486#Costume Diver's Goggles# +31487#Costume Eye Bandage# +31488#Costume MVP# +31489#Costume Bouquet Hat# +31490#Costume Poring Muffler# +31491#Costume Orange Tabby Cat# +31492#Costume Cat Ears Cape Brown# +31493#Costume Volume Low Twin (White)# +31504#Costume Large Hungry Fish# +31505#Costume Falcon Mask# +31506#Costume Cat Eared Bandana# +31507#Costume Drooping Yellow Cat# +31508#Costume Isabella's Blue Ear Hat# +31509#Costume Fawn Ears# +31510#Costume Short Haired Cat Ears# +31511#Costume Long Haired Cat Ears# +31512#Costume Tuxedo Rabbit# +31513#Costume Black Rabbit Ears# +31516#Costume Mini Melon# +31518#Costume Pop Popcorn Hat# +31519#Costume Slurp Slurp Hat# +31526#Costume Shaman Hat# +31527#Costume Radiant Rainbow Wings# +31528#Costume Stardust Hairband# +31529#Costume Happy Rabbit Ribbon# +31531#Lovely Heart Hat# +31533#Costume Warm Black Cat Muffler# +31534#Rainbow Star# +31538#Costume Lovely Feeling# +31540#Costume Red Gemini Eyes# +31541#Costume Drosera Hairpin# +31542#Costume Clay Poring Jar# +31543#Costume Siorava Hat# +31544#Costume Piamette's Curls# +31545#Costume Eremes Scarf (Blue)# +31546#Costume Clockwork Cap# +31547#Costume Poporing Mascot Head# +31559#Costume Royal Guard Necklace# +31560#Costume Clergy Nurse Cap# +31561#Costume Heavenly Dark Flame# +31562#Costume Zealotus Doll# +31563#Costume Blue Eye Shadow# +31564#Costume Variant Veil# +31565#Costume Crowned Princess Ribbon# +31566#Costume Stole of Dominion# +31567#Costume Sheep's Horns# +31569#Costume Open Air Headset# +31570#Costume Cyclops Visor# +31571#Costume Guardian Processor# +31572#Costume Mobile Pursuit System# +31573#Costume Mecha Cat Ears# +31574#Costume Cyber Income# +31575#Costume Electric Two Sides Up Hair# +31580#Costume Autumn Taste# +31586#Costume Poporing Muffler# +31588#Costume Snow Flower# +31591#Costume Saint's Crown# +31593#Costume Yellow Scarf# +31594#Costume Red Pencil in Mouth# +31595#Costume Book of Soyga# +31596#Costume Spider Seduction# +31597#Costume Golden Fish Hat# +31598#Costume Forest Guide# +31599#Costume Medium Wave# +31600#Costume Kishu Dog# +31601#Costume Under Rimmed Glasses (Blue)# +31602#Costume Rune-Midgarts Glory# +31608#Costume Magicstone of Grace# +31609#Costume Noble Mask# +31610#Costume Yellow Wizardry Hat# +31611#Costume Dark Snake Lord Stole# +31612#Costume Large Sorcerer Crown# +31615#Costume Geisha Make Up# +31616#Costume Sleep Sheep# +31617#Costume Lady Tanee# +31618#Costume Dancing Butterfly# +31620#Costume Snow Fox# +31625#Costume Protective Scarf# +31628#Costume Dokebi# +31635#Costume Flowing Long (Blonde)# +31636#Costume Flowing Long (Silver)# +31637#Costume Straight Long (Blue)# +31638#Costume Straight Long (Red)# +31639#Costume Straight Long (Blonde)# +31640#Costume Straight Long (Green)# +31641#Costume Straight Long (Jet Black)# +31642#Costume Straight Long (Light Purple)# +31643#Costume Straight Long (Brown)# +31644#Costume Straight Long (Purple)# +31645#Costume Side Pony (Blue)# +31646#Costume Side Pony (Red)# +31647#Costume Side Pony (Blonde)# +31648#Costume Side Pony (Green)# +31649#Costume Side Pony (Black)# +31650#Costume Side Pony (White)# +31651#Costume Side Pony (Brown)# +31652#Costume Side Pony (Purple)# +31653#Costume Chignon (Blue)# +31654#Costume Chignon (Red)# +31655#Costume Chignon (Blonde)# +31656#Costume Chignon (Green)# +31657#Costume Chignon (Black)# +31658#Costume Chignon (White)# +31659#Costume Chignon (Brown)# +31660#Costume Chignon (Purple)# +31664#Costume Drooping Ernst Von Wolf 11th# +31667#Costume Poring Beret# +31668#Costume Majorous Horns# +31669#Costume Evolved Whisper Mask# +31670#Costume Miyabi Doll Hair# +31674#Costume Jitterbug Cap# +31682#Costume Drooping Elven Ears# +31683#Costume Heart Wing Headband# +31684#Costume Kururinpa Tails# +31685#Costume Fluffy Semi Long# +31686#Costume Desert Wolf Baby# +31687#Costume Alchemist Bag# +31688#Costume Poring on Shoulder# +31694#Costume Poring Letter# +31695#Costume Tree Sprout# +31696#Costume Shark Hat# +31697#Costume Bird Nest# +31698#Costume Red Riding Hood# +31699#Costume Smiling Eyes# +31703#Costume Valentine Hat# +31710#Costume Lace Flower Crown# +31711#Costume Jewel Crown# +31712#Costume Oxygen Mask# +31713#Costume Mystical Fruit Hat# +31714#Costume Indian Headband# +31715#Costume Orange Rabbit# +31716#Costume Twinkling Red Eyes# +31717#Costume Medium Wave (Yellow)# +31718#Costume Sky Lantern# +31719#Costume Master of Fire# +31721#Costume Ancient Resonance# +31728#Costume Straw Cloche# +31731#Costume Sweets Party# +31733#Costume Phantom's Mask# +31734#Costume Citrus Colored Ribbon# +31735#Costume Yawata Seal# +31766#Costume Peony Headdress# +31784#Costume Experimental Goat Cap# +31785#Costume Monster Fish Gills# +31786#Costume Red Baby Dragon Hat# +31787#Costume Savage Shoulder Bebe # +31788#Costume Piamette Doll Hair (Silver)# +31789#Costume Turkey Hat# +31793#Costume Seraphim's Feather# +31794#Costume Dog Ear Cap# +31795#Costume Elephant Fairy# +31796#Costume Drooping Boto# +31797#Costume Shih Tzu Hair# +31798#Costume Baby Panda# +31799#Costume Pretty White Bear# +31800#Costume Brazil Twin Ribbon# +31801#Costume Creative Convention Cap# +31802#Costume Creative Convention Chapeau# +31811#Costume Thief's Hood# +31812#Costume Light Bulb Hairband# +31813#Costume Lunatic On Shoulder# +31814#Costume Lunatic Family Balloon# +31815#Costume Prince of Rabbit# +31816#Costume Three Rabbits Ribbon# +31817#Costume Rabbit Two Sides Up# +31819#Costume Dullahan Mask# +31821#Costume Jeje Cap# +31840#Costume DJ Headset# +31842#Costume Striking Hat# +31843#Costume Heavenly Order# +31844#Costume Mob Scarf# +31845#Costume Palace Guard Cap# +31846#Costume Hair Ribbon Wig# +31847#Costume Blinking Blue Eyes# +31848#Costume Gothic Rose Bonnet# +31850#Gift of Panagia# +31852#Costume Witch's Cloak# +31856#Costume Airy Twin Tails (Blonde)# +31882#Costume Choco Stick In Mouth# +31886#Costume Pierced Apple# +31887#Costume Pure Love Muffler# +31899#Costume Freyja's Crown# +31902#Costume Cool Shades# +31903#Costume White Snake# +31904#Costume Snow Parade Hat# +31905#Costume Ice Tails# +31906#Costume Cat's Paw Knit Hat# +31907#Costume Cat's Paw Knit Hat (Brown)# +31910#Costume Shaving Cream# +31911#Costume Little Garden# +31912#Costume Black Veil# +31913#Costume Feathered Black Cap# +31914#Costume Mini Beret Hairband# +31915#Costume Sweet Coronet# +31916#Costume Bow Tie# +31927#Costume Blue Well Chewed Pencil# +31928#Costume Cylinder Hairband# +31930#Costume Mic Stand# +31931#Costume Half Rimmed Glasses# +31932#Costume Suit Lapel# +31933#Costume Light Purple Miyabi Doll Hair# +31934#Costume Blinking Purple Eyes# +31935#Costume Baby Dragon Hat# +31936#Costume Honey Donuts# +31937#Costume Unicorn Headdress# +31938#Costume Long Twin Braids# +31940#Costume flag mask# +31941#Costume Storm Cell# +31943#Costume Tree Frog Hood# +31944#Costume Elegant Wave# +31946#Costume Demon King's Eye# +31947#Costume Dark Horn# +31948#Costume Headband of Illusion's Eyes# +31949#Costume Diablos Wings# +31950#Costume Replica Nut Cracker# +31951#Costume Happy Cat# +31953#Costume Core Headset# +31954#Costume Vassalage Necklace# +31965#Costume Wave Perm Hair (Red)# +31970#Ponytail Hair(brown)# +31973#Costume Blossom Glasses# +32005#Illusion Pole Axe# +32018#Aquatic Spear# +32019#Boost Lance-OS# +32026#Blocking Spear# +32032#The Berg Golden Spear# +32109#Trumpet Shell# +32110#Safety Lute# +32114#Accolade Lute# +32203#Accelerator Chip# +32204#Immortal Dog Tag# +32206#Prontera Militia Glove# +32207#Illusion Booster R# +32208#Illusion Booster L# +32209#Illusion Battle Chip R# +32210#Illusion Battle Chip L# +32222#Brooch of Hero# +32227#Fenrir Chain# +32234#Unrivaled Ring# +32235#Dragon Ring# +32236#Clergy's Ring# +32237#Celine's Brooch Type K# +32242#Old Detachments Ring# +32248#Safety Pendant R# +32249#Safety Pendant B# +32250#Safety Epaulet R# +32251#Safety Epaulet B# +32252#High Adventurer's Clip# +32258#Ring of Jupiter# +32262#Rasen Fuma's Orb# +32263#Shield Ring# +32265## +32302#Crimson Rose# +32303#Bolt Revolver# +32352#Safety Saber# +32356#The Twist of Fates# +32410#The Spinebuster# +99999## +100009#Joy of Victory# +100010#Perfect Size Lapine Box# +100019#Costume Enchant Stone Box 18# +100023#Ancient_Hero_Bravery# +100025#Ancient_Hero_Wisdom# +100029#ºÎ½ºÅÍ ÆÑ(1)# +100030#Booster Pack (15)# +100031#ºÎ½ºÅÍ ÆÑ(30)# +100032#ºÎ½ºÅÍ ÆÑ(45)# +100033#ºÎ½ºÅÍ ÆÑ(60)# +100034#ºÎ½ºÅÍ ÆÑ(75)# +100035#ºÎ½ºÅÍ ÆÑ(90)# +100036#ºÎ½ºÅÍ ÆÑ(100)# +100037#ºÎ½ºÅÍ ÆÑ(115)# +100038#ºÎ½ºÅÍ ÆÑ(130)# +100039#ºÎ½ºÅÍ ÆÑ(145)# +100040#ºÎ½ºÅÍ ÆÑ(160)# +100041#ºÎ½ºÅÍ ÆÑ(175)# +100043#Booster Armor Upgrade Package# +100044#Booster Weapon Upgrade Package# +100045#Starter Armor Crate# +100046#Attacker Booster Armor Crate# +100047#Ranged Booster Crate# +100048#Elemental Booster Crate# +100049#Defiant Booster Crate# +100051#ºÎ½ºÅÍ ÇÁ·Î¸ð¼Ç »çÀü¿¹¾à °¨»ç»óÀÚ# +100052#Costume Enchant Stone Box 19# +100054#Shadow Transcendental Time Crate# +100061#[Event] Red Booster# +100065#Spell Book(Storm Gust)# +100066#Spell Book(Lord of Vermilion)# +100067#Spell Book(Meteor Storm)# +100068#Spell Book(Drain Life)# +100069#Spell Book(Jack Frost)# +100070#Spell Book(Earth Strain)# +100071#Spell Book(Crimson Rock)# +100072#Spell Book(Chain Lightning)# +100073#Spell Book(Comet)# +100074#Spell Book(Tetra Vortex)# +100100#Temporal Manteau Box# +100128#Noblesse Upgrade Ticket# +100129#Imperial Upgrade Ticket# +100130#Grace Upgrade Ticket# +100131#Imperial Physical Modification Permit (Manteau)# +100132#Imperial Magic Modification Permit (Manteau)# +100133#Grace Physical Modification Permit (Manteau)# +100134#Grace Magic Modification Permit (Manteau)# +100135#Imperial Physical Modification Permit (Armor)# +100136#Imperial Magic Modification Permit (Armor)# +100137#Grace Physical Modification Permit (Armor)# +100138#Grace Magic Modification Permit (Armor)# +100142#Stable Dragon's Power# +100144#Burning Dragon's Power# +100145#Hot Dragon's Power# +100147#Bath Salt A# +100148#Bath Salt B# +100149#Bath Salt C# +100150#Aromatherapy Oil# +100160#Automatic Module Box# +100161#Epic Module Box# +100162#Physical Automatic Modification Device Box# +100163#Magic Automatic Modification Device Box# +100164#Medium Automatic Modification Device (Physical)# +100165#High Automatic Modification Device (Physical)# +100166#Finest Automatic Modification Device (Physical)# +100167#Medium Automatic Modification Device (Magic)# +100168#High Automatic Modification Device (Magic)# +100169#Finest Automatic Modification Device (Magic)# +100171#Cannon Ball Box# +100172#Iron Cannon Ball Box# +100173#Soul Cannon Ball Box# +100174#Poison Arrow Quiver# +100176#Dark Cannon Ball Box# +100177#Holy Cannon Ball Box# +100178#Liquid Condensed Bullet Box# +100179#Large Madogear Fuel Tank# +100180#Repair A Box# +100181#Repair B Box# +100182#Repair C Box# +100183#Flame Stone Bundle# +100184#Ice Stone Bundle# +100185#Wind Stone Bundle# +100186#Shadow Orb Bundle# +100187#Fire Charm Bundle# +100188#Ice Charm Bundle# +100189#Wind Charm Bundle# +100190#Earth Charm Bundle# +100191#Explosive Kunai Scroll# +100192#Blue Gemstone Pouch# +100193#Yellow Gemstone Pouch# +100194#Red Gemstone Pouch# +100195#Full Metal Jacket Case# +100196#Projectile Mine Case# +100197#Dragon Tail Missile Case# +100231#Golden X Potion# +100232#Red Herb Activator# +100233#Blue Herb Activator# +100251#Illusion Upgrade Cube# +100252#Automatic Upgrade Cube# +100271#Ancient Heros Weapon 9Up Box# +100273#Ancient Hero's Boots Modifying Cube# +100274#Oriental Sword Modification Cube# +100275#Dragonic Slayer Modification Cube# +100276#Shiver Katar Modification Cube# +100277#Blade Katar Modification Cube# +100327#Red Lotus Sword Modification Cube# +100328#Slate Sword Modification Cube# +100329#Narcis Bow Modification Cube# +100330#Trumpet Shell Modification Cube# +100331#Barbed Wire Whip Modification Cube# +100342#Growth Potion (Small)# +100343#Growth Potion (Medium)# +100346#Job Potion (Small)# +100360#Avenger Modification Cube# +100361#Meteor Striker Modification Cube# +100362#Madogum Modification Cube# +100363#Fatalist Modification Cube# +100371#Homunculus Tablet# +100376#Royal Bow Modification Cube# +100377#Scarlet Dragon Leather Bow Modification Cube# +100378#Shadow Staff Modification Cube# +100379#Chilling Cane Modification Cube# +100388#Iron Nail Modification Cube# +100390#Ray Knuckle Modification Cube# +100393#Aquatic Spear Modification Cube# +100394#¶óÀÌÆ® ºí·¹ÀÌµå °³Á¶ Å¥ºê# +100395#Iron Staff Modification Cube# +100396#Blue Crystal Staff Modification Cube# +100397#Exorcist's Bible Modification Cube# +100398#Saint Hall Modification Cube# +100399#Meowmeow Foxtail Modification Cube# +100400#Kiri no Tsuyu Modification Cube# +100401#Huuma Shuriken Clear Modification Cube# +100402#One Sky One Sun Modification Cube# +100403#Spirit Pendulum Modification Cube# +100404#Crimson Rose Modification Cube# +100405#Master Soul Rifle Modification Cube# +100406#Gold Road Launcher Modification Cube# +100407#The Black Modification Cube# +100408#Demon Slayer Shot Modification Cube# +100412#Great Heros Bravery# +100413#Great Heros Wisdom# +100437#Ancient Heros Weapon Modification Cube# +100461#Galaxy Costume Egg# +100481#Battle Processor Accelerator# +100536#Kachua's Secret Box# +100537#Class Weapon Shadow Cube# +100538#Class Armor Shadow Cube# +100539#Class Shoes Shadow Cube# +100540#Class Shield Shadow Cube# +100541#Class Pendant Shadow Cube# +100542#Class Earring Shadow Cube# +100710#EXP Advisor Modification# +100742#Awakened Ancient Heros Weapon Box# +100750#Advanced Refining Certificate Envelope# +100751#Refining Certificate Envelope# +100756#Steam Punk Costume Box# +100801#Spring Costama Egg# +100823#Lens Pore# +100855#Booster Goal Box# +100856#Booster Pack (1)# +100857#Booster Pack (175)# +100858#Booster Pack (30)# +100859#Booster Pack (45)# +100860#Booster Pack (60)# +100861#Booster Pack (75)# +100862#Booster Pack (90)# +100863#Booster Pack (100)# +100864#Booster Pack (115)# +100865#Booster Pack (130)# +100866#Booster Pack (145)# +100867#Booster Pack (160)# +100868#Moon Rabbit Costume Egg# +100927#Secret Shadow Box# +100941#Costume Animal Egg# +101052#Holy Costume Egg# +101083#Glacier-frozen Egg# +101102#Overseas Care XXX# +101104#Royal Egg# +101150#Overseas Care 31# +101198#Costume Fashion Show# +101206#Overseas Care 32# +101240#January Event's Reward Box# +101241#Hyul's Special Rice Cake Soup# +101283#Overseas Care 33# +101285#Diabolic Egg# +101301#Spring Blossom# +101325#Overseas Care 34# +101375#Dark Fantasy Costume Egg# +101400#Overseas Care Package 35# +101401#Oceanic Egg# +101467#Overseas Care Package 36# +101482#Freedom Stick Modification Cube# +101483#Blessed Knife Modification Cube# +101486#Inferno Egg# +101568#Overseas Care Package 37# +101593#Cutie Costume Egg# +101647#Overseas Care Package 38# +101651#Gourmet Egg# +101669#Music Festival Egg# +101698#Overseas Care Package 39# +101707#Chilly Costume Egg# +101790#Overseas Care Package 40# +101824#Charming Egg# +101833#Overseas Care Package 41# +101854#Eerie Egg# +101892#Overseas Care Package 44# +102000#Overseas Care Package 42# +102087#Flower Garden Egg# +102117#Overseas Care Package 43# +102212#Cute Character Egg# +102237#Overseas Care Package 46# +102263#Overseas Care Pacakage 45# +102293#Spring Celebration Egg# +200028#Frozen Costume Egg# +200030#[30D] Mistress Wing Box# +200048#Costume School Egg# +200052#Costume Thanksgiving Egg# +200055#Acid Bomb 50 Box# +200056#Acid Bomb 500 Box# +200064#Giant Flywing 2500 Box# +200117#Kachua's Enriched Oridecon Box# +200118#Kachua's Enriched Elunium Box# +200119#Kachua's HD Bradium Box# +200120#Kachua's HD Carnium Box# +200121#Kachua's HD Elunium Box# +200122#Kachua's HD Oridecon Box# +300001#Poisonous Card# +300002#Toxious Card# +300003#White Porcellio Card# +300004#Neo Mineral Card# +300005#Abyssman Card# +300006#Jewelry Ant Card# +300007#Jewel Ungoliant Card# +300008#Angelgiold Card# +300009#Spectrum Plasma Card# +300010#Arch Plasma Card# +300011#Holy Frus Card# +300012#Holy Skogul Card# +300013#Reginleif Card# +300014#Ingrid Card# +300015#Purple Ferus Card# +300016#Treasure Mimic Card# +300017#Black Acidus Card# +300018#Silver Acidus Card# +300019#Bone Ferus Card# +300020#Bone Acidus Card# +300021#Bone Detardeurus Card# +300076#Broken Security Beta Card# +300077#Broken Cleaner Robot Omega Card# +300078#Sweetie Card# +300079#Red Pepper Card# +300080#Great Red Pepper Card# +300081#Research Assistant Bot Card# +300082#Greater Research Assistant Bot Card# +300083#Dried Rafflesia Card# +300084#Greater Dried Rafflesia Card# +300085#Special Alnoldi Card# +300086#Greater Special Alnoldi Card# +300087#Broken Gardener Beta Card# +300088#Greater Broken Gardener Beta Card# +300089#Verporta Card# +300090#Verporte Card# +300091#Papila Card# +300092#Greater Papila Card# +300093#Papila Ruba Card# +300094#Greater Papila Ruba Card# +300095#Papila Cae Card# +300096#Greater Papila Cae Card# +300097#Aries Card# +300098#Greater Aries Card# +300099#Silva Papilia Card# +300100#Gran Papilia Card# +300101#Broken Cleaner Card# +300102#Bath Manager Card# +300103#Azure Princess Card# +300104#Bookworm Card# +300105#Roaming Spellbook Card# +300106#Red Pitaya Card# +300107#Meow Card# +300108#Sewage Venenum Card# +300109#Sewage Cramp Card# +300110#Sewage Waterfall Card# +300111#Elite Bellare Card# +300112#Magic-poisoned Dolor Card# +300113#Unleashed Magic Card# +300114#Magic-poisoned Plaga Card# +300115#Magic-poisoned Sanare Card# +300116#Powerful Magic Card# +300117#Sharp Magic Card# +300118#Boiling Phen Card# +300119#Boiling Swordfish Card# +300120#Boiling Piranha Card# +300121#Boiling Marc Card# +300122#Yellow Pitaya Card# +300123#Purple Pitaya Card# +300124#Blue Pitaya Card# +300125#Green Pitaya Card# +310000#Sniper Stone II(Upper)# +310001#Sniper Stone II(Middle)# +310002#Sniper Stone II(Lower)# +310003#Ranger Stone II(Garment)# +310004#Mastersmith Stone II(Upper)# +310005#Mastersmith Stone II(Middle)# +310006#Mastersmith Stone II(Lower)# +310007#Mechanic Stone II(Garment)# +310008#High Priest Stone II(Upper)# +310009#High Priest Stone II(Middle)# +310010#High Priest Stone II(Lower)# +310011#Arch Bishop Stone II(Garment)# +310076#Blessing of Strength# +310077#Blessing of Agility# +310078#Blessing of Vitality# +310079#Blessing of Dexterity# +310080#Blessing of intelligence# +310081#Blessing of Luck# +310082#Automatic Modification Orb (DEF)# +310083#Automatic Modification Orb (MDEF)# +310084#Automatic Modification Orb (VIT)# +310085#Automatic Modification Orb (LUK)# +310086#Automatic Modification Orb (STR)# +310087#Automatic Modification Orb (AGI)# +310088#Automatic Modification Orb (INT)# +310089#Automatic Modification Orb (DEX)# +310090#Automatic Modification Orb (HP Recovery)# +310091#Automatic Modification Orb (SP Recovery)# +310092#Automatic Modification Orb (Spell)# +310093#Automatic Modification Orb (ASPD)# +310094#Automatic Modification Orb (Fatal)# +310095#Automatic Modification Orb (Expert Archer)# +310096#Automatic Modification Orb (Health)# +310097#Automatic Modification Orb (SPR)# +310098#Automatic Modification Orb (Heal)# +310099#Automatic Modification Orb (ATK)# +310100#Automatic Modification Orb (MATK)# +310101#Automatic Modification Orb (Sharpshooter)# +310102#Automatic Modification Orb (Speed)# +310103#Automatic Modification Orb (Caster)# +310104#Automatic Modification Orb (Critical)# +310105#Automatic Modification Orb (Magical Force)# +310106#Automatic Modification Orb (Attacker Force)# +310107#Automatic Modification Orb (Ranged Force)# +310108#Automatic Modification Orb (Critical Force)# +310109#Automatic Modification Orb (Recovery Force)# +310110#Automatic Modification Orb (Post-skill Delay)# +310111#Automatic Modification Orb (Fixed Cast Time)# +310112#Automatic Modification Orb (Above All)# +310113#Automatic Modification Orb (Drain Life)# +310114#Automatic Modification Orb (Drain Soul)# +310115#Automatic Modification Orb (Magic Healing)# +310116#Automatic Modification Orb (Magic Soul)# +310117#Automatic Modification Orb (Power Force)# +310118#Automatic Modification Orb (Robust)# +310119#Automatic Modification Orb (Powerful)# +310120#Automatic Modification Orb (All-Force)# +310121#Automatic Orb (Unlimited Vitality)# +310122#Automatic Orb (Spell Buster)# +310123#Automatic Orb (Fierce Shooter)# +310124#Automatic Orb (Overpower)# +310125#Automatic Orb (Fatal Flash)# +310126#Automatic Orb (Lucky Strike)# +310127#Automatic Orb (Draconic Breath)# +310128#Automatic Orb (Wave Break)# +310129#Automatic Orb (Hundred Spiral)# +310130#Automatic Orb (Drive Press)# +310131#Automatic Orb (Vanishing Cannon)# +310132#Automatic Orb (Genesis Gloria)# +310133#Automatic Orb (Boost Cannon)# +310134#Automatic Orb (Ice Flame)# +310135#Automatic Orb (Tornado Swing)# +310136#Automatic Orb (Cannon Tornado)# +310137#Automatic Orb (Crazy Mandragora)# +310138#Automatic Orb (Acid Explosion)# +310139#Automatic Orb (Sonic Impact)# +310140#Automatic Orb (Cutter Slasher)# +310141#Automatic Orb (Berserk Slash)# +310142#Automatic Orb (Fatal Raid)# +310143#Automatic Orb (Shadow Spell)# +310144#Automatic Orb (Angle Shot)# +310145#Automatic Orb (Crimson Strain)# +310146#Automatic Orb (Jack Lightning)# +310147#Automatic Orb (Comet Vortex)# +310148#Automatic Orb (Double Bolt)# +310149#Automatic Orb (Warmer Wave)# +310150#Automatic Orb (Diamond Grave)# +310151#Automatic Orb (Magnusmus)# +310152#Automatic Orb (Holy Judex)# +310153#Automatic Orb (Duplelica)# +310154#Automatic Orb (Fallen Tiger)# +310155#Automatic Orb (Rampage Arrow)# +310156#Automatic Orb (Raging Crush)# +310157#Automatic Orb (Cluster)# +310158#Automatic Orb (Breeze Shooting)# +310159#Automatic Orb (Aimed Storm)# +310160#Automatic Orb (Metallic Echo)# +310161#Automatic Orb (Reverberation)# +310162#Automatic Orb (Vulcan Severe)# +310163#Automatic Orb (Blaze Explosion)# +310164#Automatic Orb (Moon Kick)# +310165#Automatic Orb (Falling Flash)# +310166#Automatic Orb (Eswoo)# +310167#Automatic Orb (Espa)# +310168#Automatic Orb (Curse Explosion)# +310169#Automatic Orb (Death Hammer Dance)# +310170#Automatic Orb (Fire Howling Tail)# +310171#Automatic Orb (Storm Buster Trip)# +310172#Automatic Orb (Petal Spear Blade)# +310173#Automatic Orb (Cross Slash)# +310174#Automatic Orb (Dragon Draft Wind)# +310175#Automatic Orb (Power of Sea)# +310176#Automatic Orb (Power of Land)# +310177#Automatic Orb (Power of Life)# +310178#Automatic Modification Orb (Mirror Counter)# +310179#Automatic Modification Orb (Reflection Reject)# +310330#Range Stone(Middle)# +400001#Victory Wing Ear# +400002#Victory Wing Ear# +400011#Officer's Hat# +400016#Turkey Hat# +400020#Costume Beachball# +400021#Red Clark Casquette# +400022#Ignis Cap Type K# +400023#The Agony of Thanatos# +400044#Phantom of Masquerade# +400046#Costume Pierced Apple# +400047#Runaway Accelerator# +400048#Gift of Panagia# +400049#Stripe Hat# +400052#Duel Dragon Hat# +400054#Great Magician's Ceremonial Crown# +400059#Scorpio Celestial Coronet# +400060#Shiny Kitty Crown# +400061#Sagittarius Diadem Type K# +400073#Costume Romantic Rose# +400077#Splash Cat Hat# +400078#Goral Crown# +400101#Gate of Netherworld# +400113#Victory Wing Ear# +400115#Costume Angeling Bread Hat# +400124#Costume Majestic Goat of Dawn# +400131#Costume Diadem of Brunhild# +400132#Costume Afro Wig# +400133#Costume Awakened Eden Group Hat# +400144#Costume Glory Symbol# +400157#Costume Astrologer's Cap# +400158#Costume Demon Crown# +400161#Costume Hat of King# +400162#Costume Red Navy Hat# +400163#Costume Whale Cap# +400172#Costume Atihan Hat# +400173#Costume Fancy Phantom Mask (Middle)# +400174#Costume Buyers Hat# +400175#Costume Camp Fire Cap# +400176#Costume Fishbowl Parfait Hat# +400177#Fafnir Helm# +400182#Costume Doram Head Hat# +400183#Costume Bunny Earmuffs(Upper)# +400184#Costume Wheelwind Mask# +400186#Costume Snake Hat# +400193#Costume Navy Drooping Kitty# +400194#Nostalgia Cherry Blossom# +400195#Costume Pepperoni Pizza# +400196#Costume Autumn Flavor(Upper)# +400197#Costume Sweet Chef Hat# +400203#Wolf Officer Hat# +400221#Costume Jasper Crest# +400223#Costume Fantastic Wig# +400224#Costume Navy Hat# +400225#Costume Fluffy Rabbit Cape# +400253#Costume Macaron Bunny Hat# +400254#Costume Gothic Heart Wing Band# +400258#Costume Wanderer Curl# +400259#Costume Music Note Hairband# +400269#Costume Nostalgia Cherry Blossom# +400270#Costume Kannam On Head# +400271#Costume Shovel Hat# +400272#Costume Flowers Prayer# +400277#Costume Ferlock's Hat# +400278#Costume Drooping Omega# +400290#Costume Pink Angel Hairband# +400292#Costume Angel Blessing# +400313#Costume Galaxy Circlet# +400314#Costume Northern Cross# +400315#Costume Blue Emotion# +410005#Costume Magic Heir# +410006#Costume Sudden Wealth# +410012#EXP Advisor# +410013#EXP Advisor# +410016#Battle Processor# +410017#Battle Processor# +410022#Costume Blue Mask# +410023#Brazil Twin Ribbon# +410027#Wonder Egg Basket# +410047#Costume Hero Mask# +410048#Costume Tiger# +410049#Costume Blinking Golden Eyes# +410051#Costume Falling Snow# +410053#Costume Exorcist Glasses# +410054#Long Ribbon# +410055#Floating Parasol# +410056#Costume Poulet# +410061#Costume Deviruchi Apron# +410062#Carefree Face# +410063#Cherry Ribbon# +410068#Costume Fish Marche# +410073#Costume Diadem# +410074#Costume Gravekeeper Blinker# +410079#Deep Blue Sunglasses# +410080#Deep Blue Sunglasses# +410081#Costume Released Ground# +410083#Costume Scuba Mask# +410095#Costume White Cat On Shoulder# +410096#Red Pope Ribbon# +410097#Red Pope Ribbon# +410098#Costume Nero Mask# +410099#Costume Avenger# +410107#Costume Rice Bale (Middle)# +410122#Costume Blinking Small Eyes# +410123#Costume Bear Head# +410126#Costume Professor Mini Glasses# +410127#Costume Spotlight# +410128#Costume Power Mic# +410129#Phantom Ears# +410138#Costume Flower Pot Mask (Middle)# +410141#Gorgon Coronet# +410143#Costume Charlie's Mustache# +410144#Costume Omega Golf Bag# +410156#Costume Bouquet Valletta# +410157#Costume Happening Clover# +410170#Costume Star Eyepatch# +410171#Costume Wishing Tree Hat# +420002#Durahan's Mask# +420003#CD in Mouth Type K# +420006#Vassalage Necklace# +420014#Costume Amatsu Wig# +420015#Costume Camellia Demon Ribbon# +420016#Costume Flying Katashiro# +420026#Costume Piamette Ribbon# +420027#Costume Fishbone Hair# +420028#Imperial Glory# +420030#Falconer Flute# +420031#Ninja's Blue Mask# +420033#Costume Airy Two Side Up (White)# +420044#Costume Sailor's White Collar# +420046#Costume Royal Mantle# +420047#Costume Honorable Knight Cloak# +420048#Costume White Coronet# +420050#Hair Bun Wave# +420052#Costume Chungee's Soul# +420053#Costume Shadow Devil# +420054#Costume Smokie Muffler# +420063#Desert Scarf# +420067#Costume Hopping Twin Pigtail# +420069#Costume Lord of Royals# +420070#Costume Cap of Blindness# +420071#Costume Feather Stola# +420072#Costume Knitting Low Twin(White)# +420077#Costume Cat Footprints# +420078#Costume Ghost Bat(Lower)# +420079#Costume Inner Color Long# +420081#Costume Carrot in Mouth# +420082#Costume Side Roll Pony# +420088#Costume Heart Angel# +420103#Costume Charm of Mystic Frozen# +420104#Costume Wild Long Hair# +420107#Costume Twinkle Twin# +420108#Costume Chocolate Bomb# +420109#Costume Charm of Flame Heart# +420111#Costume Rocking Short# +420112#Guardian Claus# +420132#Costume Charm of Great Nature# +420133#Costume Dreaming Rose# +420140#Costume Alpha Long Ponytail# +420156#Costume Flower Knitting# +420161#Costume Starry Long Wave# +420162#Costume Starry Ribbon Twin Tail# +436008#Big Tiger Hat# +440001#Costume Wunderkammer# +440003#Costume Novice Rabbit Hood# +450001#Attacker Booster Plate# +450002#Elemental Booster Robe# +450003#Defiant Booster Robe# +450004#Range Booster Suit# +450018#Noblesse Breath Armor# +450019#Noblesse Knight Armor# +450020#Noblesse Spear Armor# +450021#Noblesse Genesis Armor# +450022#Noblesse Sharp Suit# +450023#Noblesse Aim Suit# +450024#Noblesse Severe Suit# +450025#Noblesse Reverberation Suit# +450026#Noblesse Adora Robe# +450027#Noblesse Duple Robe# +450028#Noblesse Tornado Armor# +450029#Noblesse Vulcan Armor# +450030#Noblesse Cart Cannon Suit# +450031#Noblesse Cart Tornado Suit# +450032#Noblesse Rolling Suit# +450033#Noblesse Assassin Suit# +450034#Noblesse Fatal Suit# +450035#Noblesse Stalker Suit# +450036#Noblesse Picky Robe# +450037#Noblesse Catnip Robe# +450038#Noblesse Trip Suit# +450039#Noblesse Fire Rain Suit# +450040#Noblesse Crimson Robe# +450041#Noblesse Frost Robe# +450042#Noblesse Psychic Robe# +450043#Noblesse Dust Robe# +450044#Noblesse Sun Suit# +450045#Noblesse Full Moon Suit# +450046#Noblesse Ninja Suit# +450047#Noblesse Kunai Suit# +450048#Noblesse Eswoo Robe# +450049#Noblesse Curse Robe# +450050#Noblesse Knuckle Suit# +450051#Noblesse Tiger Cannon Suit# +450052#Imperial Breath Armor# +450053#Imperial Knight Armor# +450054#Imperial Spear Armor# +450055#Imperial Genesis Armor# +450056#Imperial Sharp Suit# +450057#Imperial Aim Suit# +450058#Imperial Severe Suit# +450059#Imperial Reverberation Suit# +450060#Imperial Adora Robe# +450061#Imperial Duple Robe# +450062#Imperial Knuckle Suit# +450063#Imperial Tiger Cannon Suit# +450064#Imperial Tornado Armor# +450065#Imperial Vulcan Armor# +450066#Imperial Cart Cannon Suit# +450067#Imperial Cart Tornado Suit# +450068#Imperial Rolling Suit# +450069#Imperial Assassin Suit# +450070#Imperial Fatal Suit# +450071#Imperial Stalker Suit# +450072#Imperial Picky Robe# +450073#Imperial Catnip Robe# +450074#Imperial Trip Suit# +450075#Imperial Fire Rain Suit# +450076#Imperial Crimson Robe# +450077#Imperial Frost Robe# +450078#Imperial Psychic Robe# +450079#Imperial Dust Robe# +450080#Imperial Sun Suit# +450081#Imperial Full Moon Suit# +450082#Imperial Ninja Suit# +450083#Imperial Kunai Suit# +450084#Imperial Eswoo Robe# +450085#Imperial Curse Robe# +450086#Grace Breath Armor# +450087#Grace Knight Armor# +450088#Grace Spear Armor# +450089#Grace Genesis Armor# +450090#Grace Sharp Suit# +450091#Grace Aim Suit# +450092#Grace Severe Suit# +450093#Grace Reverberation Suit# +450094#Grace Adora Robe# +450095#Grace Duple Robe# +450096#Grace Knuckle Suit# +450097#Grace Tiger Cannon Suit# +450098#Grace Tornado Armor# +450099#Grace Vulcan Armor# +450100#Grace Cart Cannon Suit# +450101#Grace Cart Tornado Suit# +450102#Grace Rolling Suit# +450103#Grace Assassin Suit# +450104#Grace Fatal Suit# +450105#Grace Stalker Suit# +450106#Grace Picky Robe# +450107#Grace Catnip Robe# +450108#Grace Trip Suit# +450109#Grace Fire Rain Suit# +450110#Grace Crimson Robe# +450111#Grace Frost Robe# +450112#Grace Psychic Robe# +450113#Grace Dust Robe# +450114#Grace Sun Suit# +450115#Grace Full Moon Suit# +450116#Grace Ninja Suit# +450117#Grace Kunai Suit# +450118#Grace Eswoo Robe# +450119#Grace Curse Robe# +450121#Noblesse Super Novice Suit# +450122#Noblesse Super Novice Robe# +450123#Imperial Super Novice Suit# +450124#Imperial Super Novice Robe# +450125#Grace Super Novice Suit# +450126#Grace Super Novice Robe# +450127#Automatic Armor Type A# +450128#Automatic Armor Type B# +450132#Magical Cloth# +450143#Samael Dress# +450165#Striking Mail# +450176#Powered Wing# +450179#Celine Dress# +450180#Cute Frog Raincoat# +450189#Oceanus Blessing# +450208#Paracelsus Coat# +450209#Imp Powered Suit# +450210#Fluffy Shark Pajama# +450214#Dark Triad# +460003#Feather Shield# +460004#Illusion Shield I# +460014#Illusion Shield II# +460023#Shield of Phoenix# +470000#Attacker Booster Greaves# +470001#Elemental Booster Shoes# +470002#Defiant Booster Shoes# +470003#Range Booster Boots# +470006#Sniping Shoes# +470008#Fluffy Dandelion Shoes# +470016#Noblesse Attack Boots# +470017#Noblesse Magic Boots# +470018#Imperial Attack Boots# +470019#Imperial Magic Boots# +470020#Grace Attack Boots# +470021#Grace Magic Boots# +470022#Automatic Leg Type A# +470023#Automatic Leg Type B# +470025#Cute Little Boar Shoes# +470027#Juggernaut# +470033#Elemental Boots# +470036#Illusion Military Boots# +470048#Striking Shoes# +470061#Cylinder Boots# +470075#Peep Toe Sandals# +470084#Rune Greave# +470112#Moaning of Evil Spirits# +470130#End of the World# +480000#Attacker Booster Manteau# +480001#Elemental Booster Muffler# +480002#Defiant Booster Muffler# +480003#Range Booster Manteau# +480012#Noblesse Attack Manteau# +480014#Noblesse Magic Manteau# +480016#Imperial Attack Manteau# +480017#Imperial Magic Manteau# +480018#Grace Attack Manteau# +480019#Grace Magic Manteau# +480020#Automatic Engine Wing Type A# +480021#Automatic Engine Wing Type B# +480023#Arabian Manteau# +480025#Owl Baron Mantle# +480045#Guardian Soul# +480052#Costume Sakura Ribbon# +480053#Behemoth Muffler# +480064#Storm Muffler# +480069#Costume Angel Wings# +480075#Baby Boar Bib# +480077#Magma Manteau# +480081#Glotoneria# +480084#Fafnir Breath# +480085#Devils Garment# +480093#Costume Scepter# +480094#Erymanthian Skin# +480095#Costume Giant White Rabbit# +480096#Costume Haori Coat# +480097#Costume Snow Powder# +480107#Costume Kings Twin Sword# +480108#Costume Poring Basket# +480110#Adventure Cat's Bag# +480111#Flower Wings# +480114#Striking Mikoshi# +480119#Costume Niflheim Key# +480122#Costume Samba Carnival Feather# +480123#Costume Nydhog's Rumor# +480126#Costume Giant Shark# +480127#Costume Hooked Straw Hat# +480129#Costume Triple Icecream# +480130#Costume Beer Server# +480131#Costume Swirling Flame# +480134#Avareco# +480137#Costume Filir Bag# +480152#Costume Fork Spoon Set# +480156#Aegis System# +480158#Costume Lunatic Bag# +480167#Costume Heart Stick Chocolate# +480168#Costume Melody Wings# +480169#Costume Diabolic Rock Guitar# +480176#Costume Blue Rose Garden# +480177#Costume Clutch Bouquet# +480181#Costume Dragon Backpack# +480187#Costume Wedding Ribbon# +480204#Costume Back Ribbon Bell (Blue)# +480205#Costume Astra Blessing# +490004#Attacker Booster Ring# +490005#Elemental Booster Earring# +490006#Defiant Booster Earring# +490007#Range Booster Brooch# +490013#Devil Ring# +490014#Noblesse Attack Ring# +490015#Noblesse Magic Ring# +490017#Imperial Attack Ring# +490018#Imperial Magic Ring# +490019#Grace Attack Ring# +490020#Grace Magic Ring# +490024#Automatic Booster R# +490025#Automatic Booster L# +490026#Automatic Battle Chip R# +490027#Automatic Battle Chip L# +490037#Dark Ring# +490038#Sixth Sense Ring# +490076#Ring of Nature# +490079#Soul Expansion Ring# +490083#Themis Balance# +490096#Ring of Hunter# +490098#Ring of Pazuzu# +490102#Skull Ring# +490104#Ring of Phoenix# +490110#Anulus Ring# +490113#Jasper Ring# +490114#Paradise Lost# +490118#Ring of Adoramus# +490130#Sky Blow Ring# +490141#Metal Pick# +490145#Ring of Ceyneian# +490146#Paracelsus Glove# +490149#Glittering Cat Choker# +490183#Military Glove# +490193#Traveler Ring# +490207#Memento Mori# +500001#Booster Sword# +500003#Light Blade# +500004#Slate Sword# +500007#Hypocrisy Machine# +500008#Invidia Bundle# +500013#Awakened Red Lotus Sword# +500014#Awakened Slate Sword# +500017#Awakened Light Blade# +510001#Booster Dagger# +510002#Booster Nindo# +510006#Fatalist# +510008#Wrath Rack# +510009#Gula Teeth# +510019#Awakened Madogum# +510020#Awakened Fatalist# +510022#Awakened Kiri no Tsuyu# +510031#Rotten Garden Knife# +510070#Blessed Knife# +510071#Awakened Blessed Knife# +520000#Booster Axe# +520002#Pride Steel# +520003#Scythe of Ice Flame# +530000#Booster Spear# +530002#Gluttony Stick# +530006#Awakened Aquatic Spear# +540000#Booster Spellbook# +540004#Sloth Text# +540005#Sloth Bible# +540010#Awakened One Sky One Sun# +540011#Awakened Exorcist's Bible# +540018#Military Law Book# +550002#Booster Foxtail# +550006#Safety Foxtail# +550007#Chilling Cane# +550008#Greed Wand# +550009#Addiction Plant# +550012#Awakened Shadow Staff# +550013#Awakened Chilling Cane# +550014#Awakened Meowmeow Foxtail# +550015#Awakened Spirit Pendulum# +550080#Freedom Stick# +550081#Awakened Freedom Stick# +560000#Booster Knuckle# +560002#Ray Knuckle# +560004#Ira Fist# +560008#Awakened Iron Nail# +560009#Awakened Ray Knuckle# +570000#Booster Guitar# +570002#Trumpet Shell# +570005#Pigritia Wave# +570009#Awakened Trumpet Shell# +580000#Booster Whip# +580002#Barbed Wire Whip# +580005#Pigritia Spark# +580009#Awakened Barbed Wire Whip# +590000#Booster Maul# +590002#Meteor Striker# +590003#Saint Mace# +590006#Envy Blunt# +590011#Awakened Meteor Striker# +590012#Awakened Saint Hall# +600001#Booster Two Handed Sword# +600004#Dragonic Slayer# +600008#Hypocrisy Edge# +600009#Awakened Onimaru# +610000#Booster Katar# +610003#Blade Katar# +610006#Avaritia Metal# +610008#Awakened Shiver Katar# +610009#Awakened Blade Katar# +620004#Awakened Avenger# +630003#Luxuria Pierce# +640000#Booster Staff# +640004#Blue Crystal Staff# +640005#Pride Stone# +640011#Awakened Iron Staff# +640012#Awakened Blue Crystal Staff# +650003#Wrath Wheel# +650004#Awakened Humma Clear# +700001#Booster Bow# +700003#Scarlet Dragon Leather Bow# +700007#Superbia String# +700008#Gula Gun# +700009#Pigritia Rhythm# +700013#Awakened Narcis Bow# +700018#Awakened Royal Bow# +700019#Awakened Scarlet Dragon Bow# +800002#Awakened Crimson Rose# +810000#Lust Pointer# +810001#Awakened Master Soul Rifle# +820000#Lust Shatter# +820001#Awakened Demon Slayer Shot# +830000#Booster Gatling# +830001#Lust Crusher# +830002#Awakened The Black# +830005#Antique Gatling Gun# +840000#Lust Boom# +840001#Awakened Golden Road Launcher# +1000001#First Job Change Ticket# +1000002#Second Job Change Ticket# +1000003#Transcendent Job Change Ticket# +1000004#Third Job Change Ticket# +1000005#Booster Weapon Voucher# +1000006#Booster Armor Voucher# +1000008#Sniper Stone II(Upper)# +1000009#Sniper Stone II(Middle)# +1000010#Sniper Stone II(Lower)# +1000011#Ranger Stone II(Garment)# +1000012#Mastersmith Stone II(Upper)# +1000013#Mastersmith Stone II(Middle)# +1000014#Mastersmith Stone II(Lower)# +1000015#Mechanic Stone II(Garment)# +1000016#High Priest Stone II(Upper)# +1000017#High Priest Stone II(Middle)# +1000018#High Priest Stone II(Lower)# +1000019#Arch Bishop Stone II(Garment)# +1000092#Eliumina Radar# +1000093#Red Pitaya Tail# +1000094#Yellow Pitaya Tail# +1000095#Blue Pitaya Tail# +1000096#Green Pitaya Tail# +1000097#Purple Pitaya Tail# +1000098#Boss Pitaya Tail# +1000099#Gardener's Cookie# +1000100#Freezing Trap# +1000101#Robot Part# +1000102#Emergency Key# +1000103#Barmeal Ticket# +1000104#Magical Soapstone# +1000105#Automatic Modification Module (DEF)# +1000106#Automatic Modification Module (MDEF)# +1000107#Automatic Modification Module (VIT)# +1000108#Automatic Modification Module (LUK)# +1000109#Automatic Modification Module (STR)# +1000110#Automatic Modification Module (AGI)# +1000111#Automatic Modification Module (INT)# +1000112#Automatic Modification Module (DEX)# +1000113#Automatic Modification Module (HP Recovery)# +1000114#Automatic Modification Module (SP Recovery)# +1000115#Automatic Modification Module (Spell)# +1000116#Automatic Modification Module (ASPD)# +1000117#Automatic Modification Module (Fatal)# +1000118#Automatic Modification Module (Expert Archer)# +1000119#Automatic Modification Module (Health)# +1000120#Automatic Modification Module (SPR)# +1000121#Automatic Modification Module (Heal)# +1000122#Automatic Modification Module (ATK)# +1000123#Automatic Modification Module (MATK)# +1000124#Automatic Modification Module (Sharpshooter)# +1000125#Automatic Modification Module (Speed)# +1000126#Automatic Modification Module (Caster)# +1000127#Automatic Modification Module (Critical)# +1000128#Automatic Modification Module (Magical Force)# +1000129#Automatic Modification Module (Attacker Force)# +1000130#Automatic Modification Module (Ranged Force)# +1000131#Automatic Modification Module (Critical Force)# +1000132#Automatic Modification Module (Recovery Force)# +1000133#Automatic Modification Module (Post-skill Delay)# +1000134#Automatic Modification Module (Fixed Cast Time)# +1000135#Automatic Modification Module (Above All)# +1000136#Automatic Modification Module (Drain Life)# +1000137#Automatic Modification Module (Drain Soul)# +1000138#Automatic Modification Module (Magic Healing)# +1000139#Automatic Modification Module (Magic Soul)# +1000140#Automatic Modification Module (Power Force)# +1000141#Automatic Modification Module (Robust)# +1000142#Automatic Modification Module (Powerful)# +1000143#Automatic Modification Module (All-Force)# +1000144#Automatic Module (Unlimited Vitality)# +1000145#Automatic Module (Spell Buster)# +1000146#Automatic Module (Fierce Shooter)# +1000147#Automatic Module (Overpower)# +1000148#Automatic Module (Fatal Flash)# +1000149#Automatic Module (Lucky Strike)# +1000152#Automatic Module (Draconic Breath)# +1000153#Automatic Module (Wave Break)# +1000154#Automatic Module (Hundred Spiral)# +1000155#Automatic Module (Drive Press)# +1000156#Automatic Module (Vanishing Cannon)# +1000157#Automatic Module (Genesis Gloria)# +1000158#Automatic Module (Boost Cannon)# +1000159#Automatic Module (Ice Flame)# +1000160#Automatic Module (Tornado Swing)# +1000161#Automatic Module (Cannon Tornado)# +1000162#Automatic Module (Crazy Mandragora)# +1000163#Automatic Module (Acid Explosion)# +1000164#Automatic Module (Sonic Impact)# +1000165#Automatic Module (Cutter Slasher)# +1000166#Automatic Module (Berserk Slash)# +1000167#Automatic Module (Fatal Raid)# +1000168#Automatic Module (Shadow Spell)# +1000169#Automatic Module (Angle Shot)# +1000170#Automatic Module (Crimson Strain)# +1000171#Automatic Module (Jack Lightning)# +1000172#Automatic Module (Comet Vortex)# +1000173#Automatic Module (Double Bolt)# +1000174#Automatic Module (Warmer Wave)# +1000175#Automatic Module (Diamond Grave)# +1000176#Automatic Module (Magnusmus)# +1000177#Automatic Module (Holy Judex)# +1000178#Automatic Module (Duplelica)# +1000179#Automatic Module (Fallen Tiger)# +1000180#Automatic Module (Rampage Arrow)# +1000181#Automatic Module (Raging Crush)# +1000182#Automatic Module (Cluster)# +1000183#Automatic Module (Breeze Shooting)# +1000184#Automatic Module (Aimed Storm)# +1000185#Automatic Module (Metallic Echo)# +1000186#Automatic Module (Reverberation)# +1000187#Automatic Module (Vulcan Severe)# +1000188#Automatic Module (Blaze Explosion)# +1000189#Automatic Module (Moon Kick)# +1000190#Automatic Module (Falling Flash)# +1000191#Automatic Module (Eswoo)# +1000192#Automatic Module (Espa)# +1000193#Automatic Module (Curse Explosion)# +1000194#Automatic Module (Death Hammer Dance)# +1000195#Automatic Module (Fire Howling Tail)# +1000196#Automatic Module (Storm Buster Trip)# +1000197#Automatic Module (Petal Spear Blade)# +1000198#Automatic Module (Cross Slash)# +1000199#Automatic Module (Dragon Draft Wind)# +1000200#Automatic Module (Power of Sea)# +1000201#Automatic Module (Power of Land)# +1000202#Automatic Module (Power of Life)# +1000207#Automatic Modification Module (Mirror Counter)# +1000208#Automatic Modification Module (Reflection Reject)# +1000225#Potato Chip# +1000226#Broken Robot Core# +1000227#Cloud Cotton# +1000231#Broken Robot Part# +1000232#Robot Communication Chip# +1000253#ºÎ½ºÅÍ ¿þÆù ±³È¯±Ç# +1000254#Booster Coin# +1000283#(À̺¥Æ®)¿¡ÇǼҵå16 Ŭ¸®¾î ƼÄÏ# +1000391#Phantom's Invitation# +1000392#Corridor of Phantom 1F Ticket# +1000393#Corridor of Phantom 2F Ticket# +1000394#Corridor of Phantom 3F Ticket# +1000851#Small Brush# +1000852#Soft Hanji# +1000853#Chulho's Portrait# +1001070#OCP Exchange Token# +1100003#Concentrated Red Syrup Potion# +1100004#Concentrated Blue Syrup Potion# +1100005#Concentrated Golden Syrup Potion# diff --git a/openkore_llm_knowledge/tables/iRO/maps.txt b/openkore_llm_knowledge/tables/iRO/maps.txt new file mode 100644 index 0000000000..6d4d1e1bc1 --- /dev/null +++ b/openkore_llm_knowledge/tables/iRO/maps.txt @@ -0,0 +1,1038 @@ +// Izlude tutorial map +prt_fild08d.rsw#Prontera field# +prt_fild08c.rsw#Prontera field# +prt_fild08b.rsw#Prontera field# +prt_fild08a.rsw#Prontera field# + +iz_ac01_d.rsw#Academy1st floor# +iz_ac02_d.rsw#Academy2nd floor# +iz_ac01_c.rsw#Academy1st floor# +iz_ac02_c.rsw#Academy2nd floor# +iz_ac01_b.rsw#Academy1st floor# +iz_ac02_b.rsw#Academy2nd floor# +iz_ac01_a.rsw#Academy1st floor# +iz_ac02_a.rsw#Academy2nd floor# + +izlude_d.rsw#Satellite city Izlude# +izlude_c.rsw#Satellite city Izlude# +izlude_b.rsw#Satellite city Izlude# +izlude_a.rsw#Satellite city Izlude# + +//Izlude renewal +iz_int.rsw#Intro# +iz_int01.rsw#Intro# +iz_int02.rsw#Intro# +iz_int03.rsw#Intro# +iz_int04.rsw#Intro# +iz_ac01.rsw#Academy1st floor# +iz_ac02.rsw#Academy2nd floor# +iz_ng01.rsw#Ninja Tutorial map# +treasure_n1.rsw#Izlude wreck 1st floor# +treasure_n2.rsw#Izlude wreck 2nd floor# + +moc_prydn1.rsw#Morocc Pyramid Underground 1st Floor - Challenge# +moc_prydn2.rsw#Morocc Pyramid Underground 2nd Floor - Challenge# + +eclage.rsw#Eclage# +ecl_fild01.rsw#Blooming Flower Land# +ecl_in01.rsw#Inside of Eclage# +ecl_in02.rsw#Inside of Eclage# +ecl_in03.rsw#Inside of Eclage# +ecl_in04.rsw#Inside of Eclage# +1@ecl.rsw#Eclage Interior# +ecl_tdun01.rsw#Bifrost Floor 1# +ecl_tdun02.rsw#Bifrost Floor 2# +ecl_tdun03.rsw#Bifrost Floor 3# +ecl_tdun04.rsw#Bifrost Floor 4# +ecl_hub01.rsw#Eclage Perimeter# +que_avan01.rsw#Avant's Laboratory# + + +malaya.rsw#Port Malaya# +ma_fild01.rsw#Baryo Mahiwaga# +ma_fild02.rsw#Forest# +ma_scene01.rsw#Bakonawa Lake# +ma_in01.rsw#Inside of Malaya# +ma_dun01.rsw#Bangungot Hospital 1F# +1@ma_h.rsw#Bangungot Hospital 2F# +1@ma_c.rsw#Buwaya Cave# +1@ma_b.rsw#Bakonawa Hideout# +ma_zif01.rsw#Inside of Jeepney# +ma_zif02.rsw#Inside of Jeepney# +ma_zif03.rsw#Inside of Jeepney# +ma_zif04.rsw#Inside of Jeepney# +ma_zif05.rsw#Inside of Jeepney# +ma_zif06.rsw#Inside of Jeepney# +ma_zif07.rsw#Inside of Jeepney# +ma_zif08.rsw#Inside of Jeepney# +ma_zif09.rsw#Inside of Jeepney# +job_ko.rsw#Hidden Place# + +gld_dun01_2.rsw#Guild basement dungeon 2nd floor# +gld_dun02_2.rsw#Guild basement dungeon 2nd floor# +gld_dun03_2.rsw#Guild basement dungeon 2nd floor# +gld_dun04_2.rsw#Guild basement dungeon 2nd floor# +gld2_ald.rsw#Hall of the Abyss : Tear of Hero# +gld2_gef.rsw#Hall of the Abyss : Hill of death# +gld2_pay.rsw#Hall of the Abyss : Wind of beginning# +gld2_prt.rsw#Hall of the Abyss : Warrior road# + +lhz_dun04.rsw#Human Experimentation Laboratory B4# +que_lhz.rsw#Human Experimentation Laboratory B4# +1@lhz.rsw#Wolfchev's Lab# +iz_dun05.rsw#Undersea Tunnel# +malangdo.rsw#Malangdo# +mal_in01.rsw#Malangdo Inside# +mal_in02.rsw#Inside the Ship# +mal_dun01.rsw#Starry Coral Area# +1@cash.rsw#Octopus Cave# +1@pump.rsw#Culvert# +2@pump.rsw#Culvert# +dic_dun03.rsw#Skarava hall# +que_house_s.rsw#Weird house# + +bif_fild01.rsw#Bifrost Bridge# +bif_fild02.rsw#Bifrost Bridge# +1@mist.rsw#The Hazy Maze Forest# +mora.rsw#Mora Village# +dicastes01.rsw#El Dicastes, the Sapha Capital# +dicastes02.rsw#Dicastes Diel# +dic_in01.rsw#Inside of El Dicastes# +dic_fild01.rsw#Outskirts of Kamidal Mountain# +dic_fild02.rsw#Outskirts of Kamidal Mountain# +dic_dun01.rsw#Kamidal Tunnel# +dic_dun02.rsw#Scaraba Hall# +job3_gen01.rsw#Geneticist Lab# +s_atelier.rsw#Shadow Workshop# +brasilis.rsw#Brasilis# +bra_in01.rsw#Inside Brasilis# +bra_fild01.rsw#Brasilis Field# +bra_dun01.rsw#Beyond the Waterfall# +bra_dun02.rsw#Beyond the Waterfall# +moc_para01.rsw#Inside of Paradise Group Building in Morroc# +job3_arch01.rsw#Waiting room for Arch Bishop Job Change# +job3_arch02.rsw#Odin Temple# +job3_arch03.rsw#Waiting room for Arch Bishop Job Change# +job3_guil01.rsw#Secret Tavern# +job3_guil02.rsw#Inside the Old Warehouse# +job3_guil03.rsw#An isolated mansion# +job3_rang01.rsw#Waiting room for Ranger Job Change# +job3_rang02.rsw#Test room for Ranger Job Change# +job3_rune01.rsw#Inside of Rune Knight Templar# +job3_rune02.rsw#Test room for Rune Knight Job Change# +job3_rune03.rsw#Test room for Rune Knight Job Change# +job3_war01.rsw#Test room for Warlock Job Change# +job3_war02.rsw#Test room for Warlock Job Change# +jupe_core2.rsw#The center of Juperos# +1@nyd.rsw#Nidhoggur's Nest# +2@nyd.rsw#Nidhoggur's Nest# +nyd_dun01.rsw#Yggdrasil Root# +nyd_dun02.rsw#Yggdrasil Root# +manuk.rsw#Manuk# +man_fild02.rsw#Manuk Field# +man_in01.rsw#Inside Manuk# +splendide.rsw#Splendide# +spl_fild01.rsw#Splendide Field# +spl_in01.rsw#Inside Splendide# +spl_in02.rsw#Inside Splendide# +bat_c01.rsw#Krieger von Midgards# +bat_c02.rsw#Krieger von Midgards# +bat_c03.rsw#Krieger von Midgards# +mid_camp.rsw#Rune Midgard Allied Forces Post# +mid_campin.rsw#Inside of the Rune Midgard Allied Forces Post# +man_fild01.rsw#Manuk Field# +man_fild03.rsw#Manuk Field# +spl_fild02.rsw#Splendide Field# +spl_fild03.rsw#Splendide Field# +moc_fild22b.rsw#Dimensional Gorge# +que_dan01.rsw#Hugel Field# +que_dan02.rsw#Inside of the Abandoned House in Juno# +schg_que01.rsw#Morestone Prairie# +schg_dun01.rsw#Subterranean Guild Dungeon# +arug_que01.rsw#Morestone Prairie# +arug_dun01.rsw#Subterranean Guild Dungeon# +1@orcs.rsw#Subterranean Orc Cave# +2@orcs.rsw#Subterranean Orc Cave# +1@cata.rsw#Catacomb# +2@cata.rsw#Sealed Shrine# +e_tower.rsw#Misty Island# +1@tower.rsw#Endless Tower# +2@tower.rsw#Endless Tower# +3@tower.rsw#Endless Tower# +4@tower.rsw#Endless Tower# +5@tower.rsw#Endless Tower# +6@tower.rsw#Endless Tower(Unknown Area)# +bat_room.rsw#Battle Field Waiting Room# +bat_a01.rsw#Tierra Canyon# +bat_a02.rsw#Tierra Canyon# +bat_b01.rsw#Flavian# +bat_b02.rsw#Flavian# +que_qsch01.rsw#Fallacious Okolnir# +que_qsch02.rsw#Fallacious Okolnir# +que_qsch03.rsw#Fallacious Okolnir# +que_qsch04.rsw#Fallacious Okolnir# +que_qsch05.rsw#Fallacious Okolnir# +que_qaru01.rsw#Fallacious Okolnir# +que_qaru02.rsw#Fallacious Okolnir# +que_qaru03.rsw#Fallacious Okolnir# +que_qaru04.rsw#Fallacious Okolnir# +que_qaru05.rsw#Fallacious Okolnir# +arug_cas01.rsw#Valfreyja Guild# +arug_cas02.rsw#Valfreyja Guild# +arug_cas03.rsw#Valfreyja Guild# +arug_cas04.rsw#Valfreyja Guild# +arug_cas05.rsw#Valfreyja Guild# +aru_gld.rsw#Valfreyja# +moscovia.rsw#Moscovia# +mosk_in.rsw#Inside Moscovia# +mosk_ship.rsw#Charabel# +mosk_fild01.rsw#somewhere in Moscovia# +mosk_fild02.rsw#Okrestnosti of Moscovia# +mosk_dun01.rsw#Les Forest# +mosk_dun02.rsw#Temny Forest# +mosk_dun03.rsw#Dremuci Forest# +mosk_que.rsw#Marozka's Cave# +schg_cas01.rsw#Nidhoggur Guild# +schg_cas02.rsw#Nidhoggur Guild# +schg_cas03.rsw#Nidhoggur Guild# +schg_cas04.rsw#Nidhoggur Guild# +schg_cas05.rsw#Nidhoggur Guild# +sch_gld.rsw#Nidhoggur# +cave.rsw#Cave village# +moc_fild20.rsw#Sograt Desert - Continental Guard Quarantine# +moc_fild21.rsw#Dimensional Gorge# +moc_fild22.rsw#Dimensional Gorge# +bossnia_01.rsw#Bossnia# +bossnia_02.rsw#Bossnia# +bossnia_03.rsw#Bossnia# +bossnia_04.rsw#Bossnia# +itemmall.rsw#Kafra Shop# +poring_w01.rsw#Poring War - Waiting Room# +poring_w02.rsw#Poring War# +nameless_i.rsw#Nameless Island# +nameless_n.rsw#Nameless Island# +nameless_in.rsw#Inside Nameless Island# +abbey01.rsw#Cursed Monastery# +abbey02.rsw#Cursed Monastery# +abbey03.rsw#Cursed Monastery# +que_temsky.rsw#Pope's Office - The Garden of Heaven# +z_agit.rsw#Z Gang's Hideout# +veins.rsw#Veins, the Canyon Village# +ve_in.rsw#Inside Veins# +ve_in02.rsw#Inside Veins# +ve_fild01.rsw#Veins Field# +mal_in02.rsw#Inside the Ship# +ve_fild02.rsw#Veins Field# +ve_fild03.rsw#Veins Field# +ve_fild04.rsw#Veins Field# +ve_fild05.rsw#Veins Field# +ve_fild06.rsw#Veins Field# +ve_fild07.rsw#Veins Field# +thor_camp.rsw#Thor Volcano Camp# +que_thor.rsw#Thor Volcano Dungeon# +thor_v01.rsw#Thor Volcano Dungeon# +thor_v02.rsw#Thor Volcano Dungeon# +thor_v03.rsw#Thor Volcano Dungeon# +rachel.rsw#Capital of Arunafeltz States, Rachel# +ra_in01.rsw#Inside Rachel# +ra_temple.rsw#Freya Grand Temple - Cheshrumnir# +ra_temin.rsw#Inside Temple# +que_rachel.rsw#Inside Temple# +ra_temsky.rsw#Pope's Office - The Garden of Heaven# +ra_fild01.rsw#Audhumbla Grassland# +ra_fild02.rsw#Od Canyon# +ra_fild03.rsw#The Plain of Ida# +ra_fild04.rsw#Audhumbla Grassland# +ra_fild05.rsw#Audhumbla Grassland# +ra_fild06.rsw#Portus Luna# +ra_fild07.rsw#Od Canyon# +ra_fild08.rsw#The Plain of Ida# +ra_fild09.rsw#Audhumbla Grassland# +ra_fild10.rsw#Od Canyon# +ra_fild11.rsw#The Plain of Ida# +ra_fild12.rsw#The Plain of Ida# +ra_fild13.rsw#Shore of Tears# +ra_san01.rsw#Holy Ground# +ra_san02.rsw#Holy Ground# +ra_san03.rsw#Holy Ground# +ra_san04.rsw#Holy Ground# +ra_san05.rsw#Holy Ground# +ice_dun01.rsw#Ice Cave# +ice_dun02.rsw#Ice Cave# +ice_dun03.rsw#Ice Cave# +ice_dun04.rsw#Ice Cave# +hugel.rsw#Hugel, the Quaint Garden Village# +hu_in01.rsw#Inside Hugel# +que_bingo.rsw#Bingo Game Room# +que_hugel.rsw#Odin Shrine's Underground# +p_track01.rsw#Monster Race Arena# +p_track02.rsw#Monster Race Arena# +odin_tem01.rsw#Odin Shirine# +odin_tem02.rsw#Odin Shirine# +odin_tem03.rsw#Odin Shirine# +odin_past.rsw#Ancient Odin's Temple# +hu_fild02.rsw#Hugel Field# +hu_fild03.rsw#Hugel Field# +hu_fild06.rsw#Hugel Field# +ein_fild01.rsw#Einbroch Field# +ein_fild02.rsw#Einbroch Field# +ein_fild05.rsw#Einbroch Field# +yuno_fild10.rsw#Juno Field# +kh_kiehl02.rsw#Kiehl's Room# +kh_kiehl01.rsw#Kiehl's Room# +kh_dun02.rsw#Robot Factory level 2# +kh_dun01.rsw#Robot Factory level 1# +kh_mansion.rsw#Kiel Hyre's Mansion# +kh_rossi.rsw#The Rosimier's Mansion# +kh_school.rsw#Kiel Hyre Academy# +kh_vila.rsw#Kiel Hyre's Cottage# +auction_01.rsw#Auction Hall# +auction_02.rsw#Auction Hall# +que_job01.rsw#Private Pub# +abyss_01.rsw#Abyss Lake Underground Cave# +abyss_02.rsw#Abyss Lake Underground Cave# +abyss_03.rsw#Abyss Lake Underground Cave# +abyss_04.rsw#Abyss Lake Underground Cave# +tha_t01.rsw#Thanatos Tower - Lower Level# +tha_t02.rsw#Thanatos Tower - Lower Level# +tha_t03.rsw#Thanatos Tower - Lower Level# +tha_t04.rsw#Thanatos Tower - Lower Level# +tha_t05.rsw#Thanatos Tower - Upper Level# +tha_t06.rsw#Thanatos Tower - Upper Level# +tha_t07.rsw#Thanatos Tower - Upper Level# +tha_t08.rsw#Thanatos Tower - Upper Level# +tha_t09.rsw#Thanatos Tower - Upper Level# +tha_t10.rsw#Thanatos Tower - Upper Level# +tha_t11.rsw#Thanatos Tower - Upper Level# +tha_t12.rsw#Thanatos Tower - Upper Level# +thana_step.rsw#Thanatos Tower - Upper Level# +thana_boss.rsw#Thanatos Tower - Unknown Area# +thana_scene01.rsw#Thanatos Tower Entrance# +job_soul.rsw#Your Heart# +job_star.rsw#The Sun, the Moon and the Stars# +hu_fild07.rsw#Hugel Field# +hu_fild05.rsw#The Abyss Lake# +hu_fild04.rsw#Hugel Field# +hu_fild01.rsw#Thanatos Tower# +yuno_fild06.rsw#El Mes Plateau# +quiz_02.rsw#Quiz Arena# +jupe_cave.rsw#Ruins of Juperos Entrance# +juperos_01.rsw#Ruins of Juperos# +juperos_02.rsw#Ruins of Juperos# +jupe_gate.rsw#Juperos, Restricted Zone# +jupe_area1.rsw#Juperos, Restricted Zone# +jupe_area2.rsw#Juperos, Restricted Zone# +jupe_ele.rsw#Juperos Elevator# +jupe_ele_r.rsw#Juperos Elevator# +jupe_core.rsw#Center of Juperos# +lighthalzen.rsw#Lightharlzen, the City-State of Prosperity# +lhz_in01.rsw#Rekenber Corporation Headquarters# +lhz_in02.rsw#Inside Lighthalzen# +lhz_in03.rsw#Inside Lighthalzen# +lhz_cube.rsw#Cube Room# +lhz_que01.rsw#Inside Lighthalzen# +lhz_airport.rsw#Lighthalzen Airport# +airplane_01.rsw#Airship# +lhz_dun01.rsw#Somatology Laboratory# +lhz_dun02.rsw#Somatology Laboratory# +lhz_dun03.rsw#Somatology Laboratory# +lhz_fild01.rsw#Lighthalzen Field# +yuno_pre.rsw#Schwaltzvalt Government Buildings# +y_airport.rsw#Juno Airport# +lhz_fild03.rsw#Lighthalzen Field# +lhz_fild02.rsw#Lighthalzen Field (Grim Reaper's Valley)# +ein_fild04.rsw#Einbroch Field# +ein_fild03.rsw#Einbroch Field# +ein_dun03.rsw#Mine Dungeon# +ein_dun02.rsw#Mine Dungeon# +ein_dun01.rsw#Mine Dungeon# +ein_fild10.rsw#Einbroch Field# +ein_fild09.rsw#Einbroch Field# +ein_fild08.rsw#Einbroch Field# +ein_fild07.rsw#Einbroch Field# +ein_fild06.rsw#Einbroch Field# +airplane.rsw#Airship# +airport.rsw#Airport# +ein_in01.rsw#Inside Einbroch# +einbech.rsw#Einbech, the mining village# +einbroch.rsw#Einbroch, the city of steel# +turbo_e_16.rsw#Turbo Track Stadium# +turbo_e_8.rsw#Turbo Track Stadium# +turbo_e_4.rsw#Turbo Track Stadium# +turbo_n_16.rsw#Turbo Track Stadium# +turbo_n_8.rsw#Turbo Track Stadium# +turbo_n_4.rsw#Turbo Track Stadium# +turbo_n_1.rsw#Turbo Track Stadium# +turbo_room.rsw#Waiting Room# +yuno_fild12.rsw#Border Checkpoint# +yuno_fild11.rsw#Juno Field# +yuno_fild09.rsw#Schwaltzwald Guards Camp# +yuno_fild08.rsw#Kiel Hyre's Academy# +yuno_fild07.rsw#El Mes Gorge (Valley of Abyss)# +yuno_fild05.rsw#El Mes Plateau# +ayo_in02.rsw#Inside Ayotaya# +ayo_in01.rsw#Inside Ayotaya# +ayo_dun02.rsw#Inside Ancient Shrine# +ayo_dun01.rsw#Ancient Shrine Maze# +ayo_fild02.rsw#Ayotaya Field# +ayo_fild01.rsw#Ayotaya Field# +ayothaya.rsw#Ayotaya# +que_god02.rsw#Quest Map# +que_god01.rsw#Quest Map# +quiz_test.rsw#Quiz Hall# +gefenia04.rsw#Geffenia# +gefenia03.rsw#Geffenia# +gefenia02.rsw#Geffenia# +gefenia01.rsw#Geffenia# +himinn.rsw#Valkyrie Hall (Himinn)# +jawaii_in.rsw#Inside Jawaii# +jawaii.rsw#Jawaii, the Honeymoon Island# +lou_in02.rsw#Inside Louyang# +lou_in01.rsw#Inside Louyang# +lou_dun03.rsw#Suei Long Gon# +lou_dun02.rsw#Inside the Royal Tomb# +lou_dun01.rsw#The Royal Tomb# +lou_fild01.rsw#Louyang Field# +louyang.rsw#Louyang, the highland# +valkyrie.rsw#Valkyrie Hall, the Hall of Honor# +nif_in.rsw#Inside Nifflheim# +yggdrasil01.rsw#Hvergelmir's Fountain (Trunk of Yggdrasil)# +nif_fild02.rsw#Vally of Gyoll# +nif_fild01.rsw#Skellington, a Solitary Village in Nifflheim# +niflheim.rsw#Nifflheim, Realm of the Dead# +um_dun01.rsw#Carpenter's Shop in the Tree# +um_dun02.rsw#Passage to a Foreign World# +um_in.rsw#Inside Umbala# +um_fild01.rsw#Luluka Forest# +um_fild02.rsw#Hoomga Forest# +um_fild03.rsw#Kalala Swamp# +um_fild04.rsw#Hoomga Jungle# +umbala.rsw#Wootan Tribe's Village, Umbala# +sec_in01.rsw#Inside Valhalla# +sec_in02.rsw#Inside Valhalla# +sec_pri.rsw#Room of Meditation (Valhalla Prison)# +gon_test.rsw#Arena# +gon_dun01.rsw#Shrine of Gonryun Queen# +gon_dun02.rsw#Hermit's Checkers# +gon_dun03.rsw#Arcadia# +gon_fild01.rsw#Gonryun Field# +gon_in.rsw#Inside Gonryun# +gonryun.rsw#Gonryun, the Hermit Land# +ama_test.rsw#Momotaro Experience Place# +ama_dun03.rsw#Amatsu Underground Shrine# +ama_dun02.rsw#Battle Field in the Underground Forest# +ama_dun01.rsw#Tatami Maze# +ama_fild01.rsw#Amatsu Field# +ama_in02.rsw#Inside Himezi Castle# +ama_in01.rsw#Inside Amatsu# +amatsu.rsw#Amatsu, the Land of Destiny# +alde_alche.rsw#Alchemist Realm# +yuno_in05.rsw#Power Plant of Ymir's Heart# +yuno_in04.rsw#Republic Library# +job_duncer.rsw#Comodo Theatre# +job_sage.rsw#Sage Realm# +job_cru.rsw#Crusader Realm# +job_monk.rsw#Saint Capitolina Abbey# +monk_test.rsw#Saint Capitolina Abbey# +in_rogue.rsw#Inside the Rogue Guild# +mag_dun03.rsw#Nogg Road# +mag_dun02.rsw#Nogg Road# +mag_dun01.rsw#Nogg Road# +yuno_fild04.rsw#El Mes Plateau# +yuno_fild03.rsw#El Mes Plateau# +yuno_fild02.rsw#Kiel Hyre's Cottage# +yuno_fild01.rsw#Border Posts# +yuno_in03.rsw#Inside Juno# +yuno_in02.rsw#Inside the Sage Castle# +yuno_in01.rsw#Inside Juno# +yuno.rsw#Juno, the capital of Schwarzwald Republic# +job_wiz.rsw#Wizard Realm# +job_prist.rsw#Priest Realm# +job_knt.rsw#Knight Realm# +job_hunte.rsw#Hunter Job Change Place# +gld_dun04.rsw#Guild Dungeon# +gld_dun03.rsw#Guild Dungeon# +gld_dun02.rsw#Guild Dungeon# +gld_dun01.rsw#Guild Dungeon# +payg_cas05.rsw#Greenwood Lake Guild# +payg_cas04.rsw#Greenwood Lake Guild# +payg_cas03.rsw#Greenwood Lake Guild# +payg_cas02.rsw#Greenwood Lake Guild# +payg_cas01.rsw#Greenwood Lake Guild# +pay_gld.rsw#Greenwood Lake# +aldeg_cas05.rsw#Luina Guild# +aldeg_cas04.rsw#Luina Guild# +aldeg_cas03.rsw#Luina Guild# +aldeg_cas02.rsw#Luina Guild# +aldeg_cas01.rsw#Luina Guild# +alde_gld.rsw#Luina the satellite of Al De Baran# +gefg_cas05.rsw#Britoniah Guild# +gefg_cas04.rsw#Britoniah Guild# +gefg_cas03.rsw#Britoniah Guild# +gefg_cas02.rsw#Britoniah Guild# +gefg_cas01.rsw#Britoniah Guild# +prtg_cas05.rsw#Valkyrie Realm Guild# +prtg_cas04.rsw#Valkyrie Realm Guild# +prtg_cas03.rsw#Valkyrie Realm Guild# +prtg_cas02.rsw#Valkyrie Realm Guild# +prtg_cas01.rsw#Valkyrie Realm Guild# +prt_gld.rsw#Valkyrie Realm# +tur_dun01.rsw#Turtle Island# +tur_dun02.rsw#Turtle Island Dungeon# +tur_dun03.rsw#Good Turtles Village# +tur_dun04.rsw#Turtle Palace# +tur_dun05.rsw#Underground swamp zone# +tur_dun06.rsw#Underground swamp zone# +guild_vs3.rsw#Guild Arena# +guild_vs2.rsw#Guild Arena# +guild_vs1.rsw#Guild Arena# +guild_room.rsw#Guild Arena Waiting Room# +quiz_00.rsw#Quiz Revolrution# +quiz_01.rsw#Quiz Revolrutiona# +gef_fild12.rsw#Kordt Forest# +gef_fild13.rsw#Britoniah# +gef_fild14.rsw#West Orc Village# +cmd_in02.rsw#Inside Comodo# +cmd_in01.rsw#Inside Comodo# +comodo.rsw#Beach town,Comodo# +beach_dun.rsw#Karu, the West cave# +beach_dun2.rsw#Ruande the northern cave# +beach_dun3.rsw#Mao, the East Cave# +cmd_fild01.rsw#Papuchicha Forest# +cmd_fild02.rsw#Kokomo beach# +cmd_fild03.rsw#Zenhai Marsh# +cmd_fild04.rsw#Kokomo beach# +cmd_fild05.rsw#Border of Papuchica forest# +cmd_fild06.rsw#Fortress Saint Darmain(West)# +cmd_fild07.rsw#Beacon Island, Pharos# +cmd_fild08.rsw#Fortress Saint Darmain (East)# +cmd_fild09.rsw#Fortress Saint Darmain (South)# +xmas_in.rsw#Inside Lutie# +xmas_dun02.rsw#Toy Monitoring Room# +xmas_dun01.rsw#Toy Factory Warehouse# +xmas_fild01.rsw#Lutie Field# +xmas.rsw#Lutie, Snow Village# +mjolnir_01.rsw#Mt.Mjolnir# +mjolnir_02.rsw#Mt.Mjolnir# +mjolnir_03.rsw#Mt.Mjolnir# +mjolnir_04.rsw#Mt.Mjolnir# +mjolnir_05.rsw#Mt.Mjolnir# +mjolnir_06.rsw#Mt.Mjolnir# +mjolnir_07.rsw#Mt.Mjolnir# +mjolnir_08.rsw#Mt.Mjolnir# +mjolnir_09.rsw#Mt.Mjolnir# +mjolnir_10.rsw#Mt.Mjolnir# +mjolnir_11.rsw#Mt.Mjolnir# +mjolnir_12.rsw#Mt.Mjolnir# +prt_fild00.rsw#Prontera Field# +prt_fild01.rsw#Prontera Field# +prt_fild02.rsw#Prontera Field# +prt_fild03.rsw#Prontera Field# +prt_fild04.rsw#Prontera Field# +prt_fild05.rsw#Prontera Field# +prt_fild06.rsw#Prontera Field# +prt_fild07.rsw#Prontera Field# +prt_fild08.rsw#Prontera Field# +prt_fild09.rsw#Prontera Field# +prt_fild10.rsw#Prontera Field# +prt_fild11.rsw#Prontera Field# +prt_monk.rsw#St. Capitolina Abbey# +gef_fild00.rsw#Geffen Field# +gef_fild01.rsw#Geffen Field# +gef_fild02.rsw#Geffen Field# +gef_fild03.rsw#Geffen Field# +gef_fild04.rsw#Geffen Field# +gef_fild05.rsw#Geffen Field# +gef_fild06.rsw#Geffen Field# +gef_fild07.rsw#Geffen Field# +gef_fild08.rsw#Geffen Field# +gef_fild09.rsw#Geffen Field# +gef_fild10.rsw#Geffen Field# +in_orcs01.rsw#Orc village# +gef_fild11.rsw#Geffen Field# +moc_fild01.rsw#Sograt Desert# +moc_fild02.rsw#Sograt Desert# +moc_fild03.rsw#Sograt Desert# +moc_fild04.rsw#Sograt Desert# +moc_fild05.rsw#Sograt Desert# +moc_fild06.rsw#Sograt Desert# +moc_fild07.rsw#Sograt Desert# +moc_fild08.rsw#Sograt Desert# +moc_fild09.rsw#Sograt Desert# +moc_fild10.rsw#Sograt Desert# +moc_fild11.rsw#Sograt Desert# +moc_fild12.rsw#Sograt Desert# +moc_fild13.rsw#Sograt Desert# +moc_fild14.rsw#Sograt Desert# +moc_fild15.rsw#Sograt Desert# +moc_fild16.rsw#Sograt Desert# +in_moc_16.rsw#Assassin Clan# +moc_fild17.rsw#Sograt Desert# +moc_fild18.rsw#Sograt Desert# +moc_fild19.rsw#Sograt Desert# +pay_fild01.rsw#Payon Forest# +pay_fild02.rsw#Payon Forest# +pay_fild03.rsw#Payon Forest# +pay_fild04.rsw#Sograt Desert# +pay_fild05.rsw#Payon Forest# +pay_fild06.rsw#Payon Forest# +pay_fild07.rsw#Payon Forest# +pay_fild08.rsw#Payon Forest# +pay_fild09.rsw#Payon Forest# +pay_fild10.rsw#Payon Forest# +pay_fild11.rsw#Payon Forest# +new_1-1.rsw#Training Ground# +new_2-1.rsw#Training Ground# +new_3-1.rsw#Training Ground# +new_4-1.rsw#Training Ground# +new_5-1.rsw#Training Ground# +new_1-2.rsw#Training Ground# +new_2-2.rsw#Training Ground# +new_3-2.rsw#Training Ground# +new_4-2.rsw#Training Ground# +new_5-2.rsw#Training Ground# +new_1-3.rsw#Training Ground# +new_2-3.rsw#Training Ground# +new_3-3.rsw#Training Ground# +new_4-3.rsw#Training Ground# +new_5-3.rsw#Training Ground# +new_1-4.rsw#Training Ground# +new_2-4.rsw#Training Ground# +new_3-4.rsw#Training Ground# +new_4-4.rsw#Training Ground# +new_5-4.rsw#Training Ground# +anthell01.rsw#Ant Hell# +anthell02.rsw#Ant Hell# +gef_dun00.rsw#Geffen Dungeon# +gef_dun01.rsw#Geffen Dungeon# +gef_dun02.rsw#Geffenia# +gef_dun03.rsw#Geffenia# +iz_dun00.rsw#Undersea Tunnel# +iz_dun01.rsw#Undersea Tunnel# +iz_dun02.rsw#Undersea Tunnel# +iz_dun03.rsw#Undersea Tunnel# +iz_dun04.rsw#Undersea Tunnel# + +in_sphinx1.rsw#Sphinx# +in_sphinx2.rsw#Sphinx# +in_sphinx3.rsw#Sphinx# +in_sphinx4.rsw#Sphinx# +in_sphinx5.rsw#Sphinx# +moc_pryd01.rsw#Inside Pyramid# +moc_pryd02.rsw#Inside Pyramid# +moc_pryd03.rsw#Inside Pyramid# +moc_pryd04.rsw#Inside Pyramid# +moc_pryd05.rsw#Inside Pyramid# +moc_pryd06.rsw#Inside Pyramid# +moc_prydb1.rsw#Thief Guild# +mjo_dun01.rsw#Mjolnir Dead Pit# +mjo_dun02.rsw#Mjolnir Dead Pit# +mjo_dun03.rsw#Mjolnir Dead Pit# +orcsdun01.rsw#Orc Dungeon# +orcsdun02.rsw#Orc Dungeon# +pay_dun00.rsw#Payon Cave# +pay_dun01.rsw#Payon Cave# +pay_dun02.rsw#Payon Cave# +pay_dun03.rsw#Payon Cave# +pay_dun04.rsw#Payon Cave# +prt_maze01.rsw#Labyrinth Forest# +prt_maze02.rsw#Labyrinth Forest# +prt_maze03.rsw#Labyrinth Forest# +prt_sewb1.rsw#Prontera Culvert# +prt_sewb2.rsw#Prontera Culvert# +prt_sewb3.rsw#Prontera Culvert# +prt_sewb4.rsw#Prontera Culvert# +treasure01.rsw#Sunken Ship# +treasure02.rsw#Sunken Ship# +hunter_1-1.rsw#Hunter Guild# +hunter_2-1.rsw#Hunter Guild# +hunter_3-1.rsw#Hunter Guild# +in_hunter.rsw#Hunter Guild# +knight_1-1.rsw#The Chivalry# +knight_2-1.rsw#The Chivalry# +knight_3-1.rsw#The Chivalry# +priest_1-1.rsw#The Sanctum# +priest_2-1.rsw#The Sanctum# +priest_3-1.rsw#The Sanctum# +sword_1-1.rsw#Swordman Test Hall# +sword_2-1.rsw#Swordman Test Hall# +sword_3-1.rsw#Swordman Test Hall# +job_thief1.rsw#Mushroom Farm# +wizard_1-1.rsw#Wizard Academy# +wizard_2-1.rsw#Wizard Academy# +wizard_3-1.rsw#Wizard Academy# +force_1-1.rsw#Time Limit Fight# +force_2-1.rsw#Time Limit Fight# +force_3-1.rsw#Time Limit Fight# +force_1-2.rsw#Time Limit Fight# +force_2-2.rsw#Time Limit Fight# +force_3-2.rsw#Time Limit Fight# +force_1-3.rsw#Time Limit Fight# +force_2-3.rsw#Time Limit Fight# +force_3-3.rsw#Time Limit Fight# +ordeal_1-1.rsw#Battle Ordeal Mode# +ordeal_2-1.rsw#Battle Ordeal Mode# +ordeal_3-1.rsw#Battle Ordeal Mode# +ordeal_1-2.rsw#Battle Ordeal Mode# +ordeal_2-2.rsw#Battle Ordeal Mode# +ordeal_3-2.rsw#Battle Ordeal Mode# +ordeal_1-3.rsw#Battle Ordeal Mode# +ordeal_2-3.rsw#Battle Ordeal Mode# +ordeal_3-3.rsw#Battle Ordeal Mode# +ordeal_1-4.rsw#Battle Ordeal Mode# +ordeal_2-4.rsw#Battle Ordeal Mode# +ordeal_3-4.rsw#Battle Ordeal Mode# +alb_ship.rsw#Alberta Ship# +alberta.rsw#Alberta# +alberta_in.rsw#Inside Alberta# +alb2trea.rsw# Alberta Island# +aldebaran.rsw# Al De Baran# +aldeba_in.rsw#Inside Al De Baran# +gef_tower.rsw#Geffen Tower# +geffen.rsw#Geffen# +geffen_in.rsw#Inside Geffen# +moc_castle.rsw#Morroc Castle# +moc_ruins.rsw#Morroc Ruins# +morocc.rsw#Morroc Town# +morocc_in.rsw#Inside Morroc# +pay_arche.rsw#Archer Village# +payon.rsw#Payon Town# +payon_in01.rsw#Inside Payon# +payon_in02.rsw#Inside Payon# +prontera.rsw#Prontera City, Capitol of Rune-Midgarts# +prt_in.rsw#Inside Prontera# +prt_castle.rsw#Prontera Castle# +prt_church.rsw#the Sanctuary# +izlude.rsw#Izlude Town# +izlude_in.rsw#Inside Izlude# +izlu2dun.rsw#Byalan Island# +monk_in.rsw#Inside St. Abbey# +prt_are_in.rsw#Waiting room# +arena_room.rsw#Waiting room# +prt_arena01.rsw#ARENA# +prt_are01.rsw#ARENA# +glast_01.rsw#Glast Heim# +gl_cas01_.rsw#Glast Heim Abyss# +alde_dun01.rsw#Clock Tower B1f# +alde_dun02.rsw#Clock Tower B2f# +alde_dun03.rsw#Clock Tower B3f# +alde_dun04.rsw#Clock Tower B4f# +c_tower1.rsw#Clock Tower 1f# +c_tower2.rsw#Clock Tower 2f# +c_tower3.rsw#Clock Tower 3f# +c_tower4.rsw#Clock Tower 4f# +gl_cas01.rsw#Glast Heim 1f# +gl_cas02.rsw#Glast Heim 2f# +gl_church.rsw#Glast Heim St. Abbey# +gl_chyard.rsw#Glast Heim Churchyard# +gl_dun01.rsw#The Lowest Cave in Glast Heim# +gl_dun02.rsw#The Lowest Cave in Glast Heim# +gl_in01.rsw#Inside Glast Heim# +gl_knt01.rsw#Inside Glast Heim Chivalry# +gl_knt02.rsw#Inside Glast Heim Chivalry# +gl_prison.rsw#Glast Heim Underprison# +gl_prison1.rsw#Glast Heim Underprison# +gl_sew01.rsw#Glast Heim Culvert# +gl_sew02.rsw#Glast Heim Culvert# +gl_sew03.rsw#Glast Heim Culvert# +gl_sew04.rsw#Glast Heim Culvert# +gl_step.rsw#Glast Heim Staircase Dungeon# +pvp_y_room.rsw#PvP : Waiting Room# +pvp_n_room.rsw#PvP : Waiting Room# +pvp_c_room.rsw#PvP : Waiting Room# +pvp_n_1-1.rsw#PvP : Room Sandwich# +pvp_n_2-1.rsw#PvP : Room Sandwich# +pvp_n_3-1.rsw#PvP : Room Sandwich# +pvp_n_4-1.rsw#PvP : Room Sandwich# +pvp_n_5-1.rsw#PvP : Room Sandwich# +pvp_n_6-1.rsw#PvP : Room Sandwich# +pvp_n_7-1.rsw#PvP : Room Sandwich# +pvp_n_8-1.rsw#PvP : Room Sandwich# +pvp_n_1-2.rsw#PvP : Room Rock On# +pvp_n_2-2.rsw#PvP : Room Rock On# +pvp_n_3-2.rsw#PvP : Room Rock On# +pvp_n_4-2.rsw#PvP : Room Rock On# +pvp_n_5-2.rsw#PvP : Room Rock On# +pvp_n_6-2.rsw#PvP : Room Rock On# +pvp_n_7-2.rsw#PvP : Room Rock On# +pvp_n_8-2.rsw#PvP : Room Rock On# +pvp_n_1-3.rsw#PvP : Four Room# +pvp_n_2-3.rsw#PvP : Four Room# +pvp_n_3-3.rsw#PvP : Four Room# +pvp_n_4-3.rsw#PvP : Four Room# +pvp_n_5-3.rsw#PvP : Four Room# +pvp_n_6-3.rsw#PvP : Four Room# +pvp_n_7-3.rsw#PvP : Four Room# +pvp_n_8-3.rsw#PvP : Four Room# +pvp_n_1-4.rsw#PvP : Room Undercross# +pvp_n_2-4.rsw#PvP : Room Undercross# +pvp_n_3-4.rsw#PvP : Room Undercross# +pvp_n_4-4.rsw#PvP : Room Undercross# +pvp_n_5-4.rsw#PvP : Room Undercross# +pvp_n_6-4.rsw#PvP : Room Undercross# +pvp_n_7-4.rsw#PvP : Room Undercross# +pvp_n_8-4.rsw#PvP : Room Undercross# +pvp_n_1-5.rsw#PvP : Room Copass# +pvp_n_2-5.rsw#PvP : Room Copass# +pvp_n_3-5.rsw#PvP : Room Copass# +pvp_n_4-5.rsw#PvP : Room Copass# +pvp_n_5-5.rsw#PvP : Room Copass# +pvp_n_6-5.rsw#PvP : Room Copass# +pvp_n_7-5.rsw#PvP : Room Copass# +pvp_n_8-5.rsw#PvP : Room Copass# +pvp_y_1-1.rsw#PvP : Room Prontera# +pvp_y_2-1.rsw#PvP : Room Prontera# +pvp_y_3-1.rsw#PvP : Room Prontera# +pvp_y_4-1.rsw#PvP : Room Prontera# +pvp_y_5-1.rsw#PvP : Room Prontera# +pvp_y_6-1.rsw#PvP : Room Prontera# +pvp_y_7-1.rsw#PvP : Room Prontera# +pvp_y_8-1.rsw#PvP : Room Prontera# +pvp_y_1-2.rsw#PvP : Room Izlude# +pvp_y_2-2.rsw#PvP : Room Izlude# +pvp_y_3-2.rsw#PvP : Room Izlude# +pvp_y_4-2.rsw#PvP : Room Izlude# +pvp_y_5-2.rsw#PvP : Room Izlude# +pvp_y_6-2.rsw#PvP : Room Izlude# +pvp_y_7-2.rsw#PvP : Room Izlude# +pvp_y_8-2.rsw#PvP : Room Izlude# +pvp_y_1-3.rsw#PvP : Room Payon# +pvp_y_2-3.rsw#PvP : Room Payon# +pvp_y_3-3.rsw#PvP : Room Payon# +pvp_y_4-3.rsw#PvP : Room Payon# +pvp_y_5-3.rsw#PvP : Room Payon# +pvp_y_6-3.rsw#PvP : Room Payon# +pvp_y_7-3.rsw#PvP : Room Payon# +pvp_y_8-3.rsw#PvP : Room Payon# +pvp_y_1-4.rsw#PvP : Room Alberta# +pvp_y_2-4.rsw#PvP : Room Alberta# +pvp_y_3-4.rsw#PvP : Room Alberta# +pvp_y_4-4.rsw#PvP : Room Alberta# +pvp_y_5-4.rsw#PvP : Room Alberta# +pvp_y_6-4.rsw#PvP : Room Alberta# +pvp_y_7-4.rsw#PvP : Room Alberta# +pvp_y_8-4.rsw#PvP : Room Alberta# +pvp_y_1-5.rsw#PvP : Room Morroc# +pvp_y_2-5.rsw#PvP : Room Morroc# +pvp_y_3-5.rsw#PvP : Room Morroc# +pvp_y_4-5.rsw#PvP : Room Morroc# +pvp_y_5-5.rsw#PvP : Room Morroc# +pvp_y_6-5.rsw#PvP : Room Morroc# +pvp_y_7-5.rsw#PvP : Room Morroc# +pvp_y_8-5.rsw#PvP : Room Morroc# +pvp_2vs2.rsw#PvP : Event Coliseum# + +eque_qsch01.rsw#Okolnir# +auction_03.rsw#Kafra Mail Annex# +2007rwc_01.rsw#2007 USRC Arena# +2007rwc_02.rsw#2007 USRC Arena# +2007rwc_03.rsw#2007 USRC Arena# +2007rwc_04.rsw#2007 USRC Arena# +2007rwc_05.rsw#2007 USRC Arena# +2007rwc_06.rsw#2007 USRC Arena# +2007rwc_07.rsw#2007 USRC Arena# +2007rwc_08.rsw#2007 USRC Arena# +06guild_r.rsw#Guild War Arena - Waiting Room# +06guild_01.rsw#Guild War Arena# +06guild_02.rsw#Guild War Arena# +06guild_03.rsw#Guild War Arena# +06guild_04.rsw#Guild War Arena# +06guild_05.rsw#Guild War Arena# +06guild_06.rsw#Guild War Arena# +06guild_07.rsw#Guild War Arena# +06guild_08.rsw#Guild War Arena# + +evt_hello.rsw#Dead Angel's landing# +evt_xmas.rsw#Toy Factory# + +//---------- Halloween 2008 ---- +evt_zombie.rsw#Run_From_Zombies# + +iz_dun00t.rsw#Overlook Water Dungeon# +iz_dun01t.rsw#Overlook Water Dungeon# +iz_dun02t.rsw#Overlook Water Dungeon# +iz_dun03t.rsw#Overlook Water Dungeon# +iz_dun04t.rsw#Overlook Water Dungeon# +que_god01t.rsw#Quest Map# + +ayothaya_e.rsw#Ayothaya Excavation Site# +louyang_e.rsw#Louyang Excavation Site# +morocc_mem.rsw#Memory of Morroc# +payon_mem.rsw#Memory of Payon# +moc_paraup.rsw#Inside of Paradise Group Building in Morroc 2nd floor# + +//공성전 TE 복사맵 +te_prt_gld.rsw#Gloria# +te_prtcas01.rsw#Gaebolg# +te_prtcas02.rsw#Richard# +te_prtcas03.rsw#Wigner# +te_prtcas04.rsw#Heine# +te_prtcas05.rsw#Nerious# +teg_dun01.rsw#Subterranean Guild Dungeon# +te_alde_gld.rsw#Kafragarten# +te_aldecas1.rsw#Leilah# +te_aldecas2.rsw#Pavianne# +te_aldecas3.rsw#Jasmine# +te_aldecas4.rsw#Roxie# +te_aldecas5.rsw#Curly Sue# +teg_dun02.rsw#Subterranean Guild Dungeon# + +//고성메모리얼던전 +gl_cas02_.rsw#2nd Hall# +gl_chyard_.rsw#Churchyard# +2@gl_k.rsw#1st floor of Old Glast Heim Chivalry# +1@gl_k.rsw#2nd floor of Old Glast Heim Chivalry# + +//Hero's trail +1@face.rsw#Faceworm's Nest# +1@sara.rsw#Sara's Memory# +dali.rsw#Dimensional Gap# + +//영웅의흔적 +dali02.rsw#Dimensional Gap# +1@tnm1.rsw#Masin Tower Top floor# +1@tnm2.rsw#Masin Tower Top floor# +1@tnm3.rsw#Morocc Castle's basement# +1@ge_st.rsw#Geffen Magic Competition# +1@gef.rsw#Geffen Magic Competition# +1@gef_in.rsw#Geffen Magic Competition# +1@spa.rsw#Palace of Ghost# + +1@def01.rsw#Wave Dungeon - forest# + +//시계탑나이트메어던전 +c_tower2_.rsw#Clock Town of twisted time 2nd floor# +c_tower3_.rsw#Clock Town of twisted time 3rd floor# + +//EP.14.3 +moro_vol.rsw#Flame Basin# +moro_cav.rsw#Flame cave# +1@dth1.rsw#Bios Island# +1@dth2.rsw#Bios Island# +1@dth3.rsw#Bios Island# +1@rev.rsw#Mors Cave# +1@eom.rsw#Masin war# +1@jtb.rsw#Jitterbug of Nightmare# + +// 영웅의 흔적3 +1@glast.rsw#Old Glastheim# +1@air1.rsw#Aircraft# +1@air2.rsw#Aircraft# + +// ep15.1 판타스마고리카 +ver_eju.rsw#Yuperos Eastern Ruin# +ver_tunn.rsw#Verus Outter tunnel# +verus04.rsw#Verus Findspot# +verus03.rsw#Verus Center Square# + +1@mcd.rsw#Charlston Factory# + +// ep15.2 판타스마고리카 +verus01.rsw#Lab-OPTATIO# +verus02.rsw#R&D-WISH# +un_bk_q.rsw#Under Bunker# +un_bunker.rsw#Under Bunker# +un_myst.rsw#Under Tunnel# +1@uns.rsw#The Last Room# +1@lab.rsw#Center Lab# +1@xm_d.rsw#Horror Toy Factory# + +lasagna.rsw#Port city of Lasagna# +lasa_fild01.rsw#Ravioli Plain Watch# +lasa_fild02.rsw#Ravioli Forest# +lasa_dun01.rsw#Dragon's Nest# +lasa_dun02.rsw#Dragon's Nest# +lasa_dun03.rsw#Dragon's Nest# +conch_in.rsw#Inside the Kon-Kilina# +lasa_in01.rsw#Inside Lasagna# +lasa_dun_q.rsw#Dragon's Nest# + +//-- 최종갱신 오후 3:58 2015-02-23 본서버 + +// ep16.1 영웅을 위한 연회 + 프론테라 리뉴얼 +1@mir.rsw#Ritual Room# +2@mir.rsw#Ritual Room# +1@sthb.rsw#Inside Air Portress# +1@sthc.rsw#Secret Room Air Portress# +1@sthd.rsw#Top Floor Air Portress# + +prt_cas.rsw#Prontera Center Palace# +prt_cas_q.rsw#Prontera Royal Villa# +prt_prison.rsw#Prontera Underground Prison# +prt_lib.rsw#Royal Memory# +prt_lib_q.rsw#Old Memoir of Royal Family# +prt_q.rsw#Prontera Invasion# +prt_pri00.rsw#Prontera Prison# + + +har_in01.rsw#Rock Ridge# +harboro1.rsw#Rock Ridge# +harboro2.rsw#Culvert# +rockmi1.rsw#Rock Ridge Mine# +rockmi2.rsw#Rock Ridge Mine# +rockrdg1.rsw#Kiwawa Desert# +rockrdg2.rsw#Kiwawa Desert# + +// 생체던전 신규맵 오전 2013-12-18 +lhz_dun_n.rsw#The Bed of Honor# + +// 16.2 (추카 컨텐츠 없음) +1@slw.rsw#Werner_Laboratory central_room# +1@swat.rsw#Heart_Hunter War_Base# +que_swat.rsw#Heart_Hunter War_Base# +slabw01.rsw#Werner Laboratory# +rebel_in.rsw#Clana Nemieri# + +// Illusion - Payon (Moonlight) +pay_d03_i.rsw#Nightmare_of_the_Moonlight_Flower# + +// Illusion - Geffen (vampire) +gef_d01_i.rsw#250 Page# + +1@jtb.rsw#The Shadowed Dream# + +//Episode 17.1 Quickening, Illusion +1@cor.rsw#Cor Memorial# +1@os_a.rsw#Os Occupation# +1@os_b.rsw#Blockaded Os# +1@rgsr.rsw#Regenschirm# +rgsr_in.rsw#Regenschirm Laboratory# +pub_cat.rsw#Cat on a Bullet# +sp_cor.rsw#Special Security Area, Cor# +sp_os.rsw#Special Security Area, Os# +sp_rudus.rsw#Laboratory Waste Disposal Plant Rudus Floor 1# +sp_rudus2.rsw#Laboratory Waste Disposal Plant Rudus Floor 2# +sp_rudus3.rsw#Laboratory Waste Disposal Plant Rudus Floor 3# +1@md_gef.rsw#Fri-Memorial# + +// 2018 할로윈 +1@halo.rsw#Halloween Festival# + +// 교황 메모리얼 낮에나온 반달 +1@pop1.rsw#Pope's Office# +1@pop2.rsw#The Way Home# +1@pop3.rsw#Sky Garden# + +star_frst.rsw#Meteor Forest# +star_in.rsw#Meteor Forest indoor# +//고성 상급 메모리얼던전 +1@soul.rsw#Passage Of Soul# + +//Episode 17.2 Sage's Legacy +ba_maison.rsw#Barmund Mansion Garden# +ba_in01.rsw#Inside Barmund Mansion# +ba_pw01.rsw#Magic Power Plant 1# +ba_pw03.rsw#Magic Power Plant 2# +ba_pw02.rsw#Sewage Treatment Plant# +ba_bath.rsw#Large Meditatio Bath# +ba_2whs01.rsw#Tartaros Storage Upper Floor# +ba_2whs02.rsw#Tartaros Storage Lower Floor# +ba_lost.rsw#Farm Lost Valley# +ba_lib.rsw#Library Hall of Memories# +ba_go.rsw#Palm Line Battlefield# +ba_chess.rsw#Checkmate# +1@lost.rsw#Farm Forgotten in Time# +1@herbs.rsw#Hidden Flower Garden# +1@ghg.rsw#Ortus Aqua# +1@bamn.rsw#Garden at Dusk# +1@bamq.rsw#Airship Depot# \ No newline at end of file diff --git a/openkore_llm_knowledge/tables/iRO/recvpackets.txt b/openkore_llm_knowledge/tables/iRO/recvpackets.txt new file mode 100644 index 0000000000..fed01fd28f --- /dev/null +++ b/openkore_llm_knowledge/tables/iRO/recvpackets.txt @@ -0,0 +1,1580 @@ +# 2021-04-11 12:36 PM +0437 7 +0438 10 +035F 5 +0360 6 +0361 5 +0362 6 +0363 6 +0364 8 +0365 8 +0366 10 +0367 90 +0368 6 +0369 6 +083C 14 +0838 2 +0835 -1 +0819 -1 +0817 6 +0815 2 +0811 -1 +0802 18 +07EC 8 +07E4 -1 +0436 19 +02C4 26 +0281 4 +0202 26 +022D 5 +023B 36 +085A 2 +085B 2 +085C 2 +085D 2 +085E 2 +085F 2 +0860 2 +0861 2 +0862 2 +0863 2 +0864 2 +0865 2 +0866 2 +0867 2 +0868 2 +0869 2 +086A 2 +086B 2 +086C 2 +086D 2 +086E 2 +086F 2 +0870 2 +0871 2 +0872 2 +0873 2 +0874 2 +0875 2 +0876 2 +0877 2 +0878 2 +0879 2 +087A 2 +087B 2 +087C 2 +087D 2 +087E 2 +087F 2 +0880 2 +0881 2 +0882 2 +0883 2 +0917 2 +0918 2 +0919 2 +091A 2 +091B 2 +091C 2 +091D 2 +091E 2 +091F 2 +0920 2 +0921 2 +0922 2 +0923 2 +0924 2 +0925 2 +0926 2 +0927 2 +0928 2 +0929 2 +092A 2 +092B 2 +092C 2 +092D 2 +092E 2 +092F 2 +0930 2 +0931 2 +0932 2 +0933 2 +0934 2 +0935 2 +0936 2 +0937 2 +0938 2 +0939 2 +093A 2 +093B 2 +093C 2 +093D 2 +093E 2 +093F 2 +0940 2 +0884 2 +0885 2 +0886 2 +0887 2 +0888 2 +0889 2 +088A 2 +088B 2 +088C 2 +088D 2 +088E 2 +088F 2 +0890 2 +0891 2 +0892 2 +0893 2 +0894 2 +0895 2 +0896 2 +0897 2 +0898 2 +0899 2 +089A 2 +089B 2 +089C 2 +089D 2 +089E 2 +089F 2 +08A0 2 +08A1 2 +08A2 2 +08A3 2 +08A4 2 +08A5 2 +08A6 2 +08A7 2 +08A8 2 +08A9 2 +08AA 2 +08AB 2 +08AC 2 +08AD 2 +0941 2 +0942 2 +0943 2 +0944 2 +0945 2 +0946 2 +0947 2 +0948 2 +0949 2 +094A 2 +094B 2 +094C 2 +094D 2 +094E 2 +094F 2 +0950 2 +0951 2 +0952 2 +0953 2 +0954 2 +0955 2 +0956 2 +0957 2 +0958 2 +0959 2 +095A 2 +095B 2 +095C 2 +095D 2 +095E 2 +095F 2 +0960 2 +0961 2 +0962 2 +0963 2 +0964 2 +0965 2 +0966 2 +0967 2 +0968 2 +0969 2 +096A 2 +0187 6 +0081 3 +01C6 4 +01C7 2 +0064 55 +0069 -1 +006A 23 +01DB 2 +01DC -1 +01DD 47 +01FA 48 +0204 18 +01F1 -1 +0200 26 +01BE 2 +01BF 3 +0065 17 +0066 3 +0067 37 +0970 31 +020D -1 +006B -1 +006C 3 +006D 157 +006E 3 +0071 28 +0068 46 +01FB 56 +006F 2 +0070 3 +02CA 3 +0072 19 +0082 2 +0085 5 +0089 7 +008C -1 +007E 6 +007D 2 +0090 7 +0099 -1 +019C -1 +009B 5 +00CC 6 +00CE 2 +009F 6 +00A2 6 +00A7 8 +00A9 6 +00AB 4 +00B8 7 +00B9 6 +00B2 3 +00BA 2 +00BB 5 +00BF 3 +00C1 2 +00C5 7 +00C8 -1 +00C9 -1 +0096 -1 +00CF 27 +00D0 3 +00D3 2 +00D5 -1 +00D9 14 +00DE -1 +00E0 30 +00E2 26 +00E3 2 +00E4 6 +00E6 3 +00E8 8 +00EB 2 +00ED 2 +00EF 2 +00F3 8 +00F5 8 +00F7 2 +00F9 26 +01E8 28 +00FC 6 +00FF 10 +0102 6 +0100 2 +0103 30 +0108 -1 +0112 4 +0113 10 +0116 10 +0118 2 +011B 20 +011D 2 +0126 8 +0127 8 +0128 8 +0129 8 +012A 2 +012E 2 +012F -1 +01B2 -1 +0134 -1 +0130 6 +0138 3 +013F 26 +0140 22 +0143 10 +0146 6 +0178 4 +017A 4 +017C 6 +01FD 25 +018A 4 +018E 18 +0190 90 +0197 4 +0198 8 +01AE 6 +025B 8 +01AF 4 +01B9 6 +01CD 30 +01CE 6 +01CF 28 +01D0 8 +01E1 8 +01D1 14 +01D2 10 +0094 6 +0095 30 +0195 102 +0193 6 +0175 6 +0176 106 +0079 53 +019D 6 +014C -1 +014D 2 +014E 6 +014F 6 +0150 110 +01B6 114 +0151 6 +0152 -1 +0153 -1 +0154 -1 +0166 -1 +0155 -1 +0156 -1 +0157 6 +0159 54 +015A 66 +015B 54 +015C 90 +015D 42 +015E 6 +015F 42 +0160 -1 +0161 -1 +0162 -1 +0163 -1 +0164 -1 +0165 30 +0167 3 +0168 14 +0169 3 +016A 30 +016B 10 +0149 9 +014A 6 +014B 27 +016E 186 +016F 182 +017E -1 +017F -1 +016C 43 +016D 14 +01F2 20 +0170 14 +0171 30 +0172 10 +0173 3 +0174 -1 +0180 6 +0181 3 +0182 106 +0183 10 +0184 10 +0185 34 +019E 2 +019F 6 +01A0 3 +01A1 3 +01A2 37 +01A3 7 +01A4 11 +01A5 26 +01A6 -1 +01A7 4 +01A8 4 +01A9 6 +01AA 10 +01CA 3 +01B0 11 +01B1 7 +01BA 26 +01BB 26 +01BC 26 +01BD 26 +01C0 2 +01C1 14 +01C2 10 +01D3 35 +01D5 -1 +01D4 6 +01DF 6 +01F3 10 +0284 14 +01FF 10 +01ED 2 +01E7 2 +01B7 6 +01F7 14 +01E3 14 +01CB 9 +01F9 6 +01E5 6 +0201 -1 +0203 10 +0205 26 +0206 11 +0207 34 +0208 14 +0209 36 +020A 10 +020E 32 +0212 26 +0213 26 +0214 42 +0215 6 +0216 6 +0217 2 +0218 2 +0225 2 +0219 282 +021A 282 +0226 282 +0282 284 +021B 10 +021C 10 +0224 10 +0280 12 +0285 6 +0286 4 +021D 6 +021E 6 +021F 66 +0222 6 +0221 -1 +0220 10 +0223 10 +0073 11 +0074 3 +0075 -1 +0076 9 +0077 5 +0078 55 +007A 58 +007B 60 +007C 44 +007F 6 +0080 7 +0083 2 +0084 2 +0086 16 +0087 12 +0088 10 +08CD 10 +008A 29 +008B 23 +008D -1 +008E -1 +0091 22 +0092 28 +0093 2 +0097 -1 +0098 3 +009A -1 +009C 9 +009D 19 +009E 19 +00A0 33 +00A1 6 +00A3 -1 +00A4 -1 +00A5 -1 +00A6 -1 +00A8 7 +00AA 9 +00AC 7 +00AE -1 +00AF 6 +00B0 8 +00B1 8 +00B3 3 +00B4 -1 +00B5 6 +00B6 6 +00B7 -1 +00BC 6 +00BD 44 +00BE 5 +00C0 7 +00C2 6 +00C3 8 +00C4 6 +00C6 -1 +00C7 -1 +00CA 3 +00CB 3 +00CD 3 +00D1 4 +00D2 4 +00D4 -1 +00D6 3 +00D7 -1 +00D8 6 +00DA 3 +00DB -1 +00DC 28 +00DD 29 +00DF -1 +00E1 30 +00E5 26 +00E7 3 +00E9 29 +00EA 5 +00EC 3 +00EE 2 +00F0 3 +00F1 2 +00F2 6 +00F4 31 +00F6 8 +00F8 2 +00FA 3 +00FB -1 +00FD 27 +00FE 30 +0101 6 +0104 79 +0105 31 +0106 10 +0107 10 +0109 -1 +010A 6 +010B 6 +010C 6 +010D 2 +010E 11 +010F -1 +02B1 -1 +02B2 -1 +02B5 -1 +0110 14 +0111 39 +0114 31 +0115 35 +0117 18 +0119 13 +0229 15 +011A 15 +011C 68 +011E 3 +011F 16 +0120 6 +0121 14 +0122 -1 +0123 -1 +0124 31 +0125 8 +012B 2 +012C 3 +012D 4 +0131 86 +0132 6 +0133 -1 +0135 7 +0136 -1 +0137 6 +0139 16 +013A 4 +013B 4 +013C 4 +013D 6 +013E 24 +0141 14 +0142 6 +0144 23 +0145 19 +0147 39 +0148 8 +0177 -1 +0179 5 +017B -1 +017D 7 +0188 8 +0189 4 +018B 4 +018C 29 +018D -1 +018F 8 +0191 86 +0192 24 +0194 30 +0196 9 +028A 18 +0199 4 +019A 14 +019B 10 +01AB 12 +01AC 6 +01AD -1 +025A -1 +01B3 67 +01B4 12 +01B5 18 +01B8 3 +01C3 -1 +01C4 32 +01C5 32 +01C8 15 +01C9 97 +01CC 9 +01D6 4 +01D7 15 +01D8 58 +022A 62 +01D9 57 +022B 61 +01DA 64 +022C 69 +01DE 33 +01E0 30 +01E2 34 +01E4 2 +01E6 26 +01E9 81 +01EA 6 +01EB 10 +01EC 26 +01EE -1 +01EF -1 +01F0 -1 +01F4 32 +01F5 9 +01F6 34 +0253 3 +0254 3 +01F8 2 +01FC -1 +01FE 5 +0227 18 +0228 18 +0232 9 +0233 11 +0234 6 +0230 12 +022E 73 +027D 62 +0235 -1 +0239 11 +022F 7 +0231 26 +0237 2 +0238 282 +0236 10 +023A 4 +023C 6 +023D 6 +023E 8 +023F 2 +0240 -1 +0241 6 +0242 -1 +0243 6 +0257 8 +0244 6 +0245 3 +0246 4 +0247 8 +0248 -1 +0249 3 +024A 70 +024B 4 +024C 8 +024D 12 +024E 6 +024F 10 +0250 3 +0251 34 +0252 -1 +0255 5 +0256 5 +0258 2 +0259 3 +025C 4 +025D 6 +025E 4 +025F 6 +0260 6 +0261 11 +0262 11 +0263 11 +0264 20 +0265 20 +0266 30 +0267 4 +0268 4 +0269 4 +026A 4 +026B 4 +026C 4 +026D 4 +026F 2 +0270 2 +0271 40 +0272 44 +0273 30 +0274 8 +0275 37 +0276 -1 +0277 84 +0278 2 +0279 2 +027A -1 +027B 14 +027C 60 +027E -1 +027F 8 +0283 6 +0287 -1 +0288 8 +0289 8 +0444 -1 +0445 8 +028B -1 +028C 46 +028D 34 +028E 4 +028F 6 +0290 4 +0291 4 +0292 2 +0293 70 +0294 10 +0295 -1 +0296 -1 +0297 -1 +0298 10 +0299 8 +029A 37 +029B 80 +029C 66 +029D -1 +029E 11 +029F 3 +02A2 8 +02A5 8 +02A6 -1 +02A7 -1 +02AA 4 +02AB 36 +02AC 6 +02AD 8 +02B0 85 +02B8 32 +02BB 8 +02B9 191 +02BA 11 +02BC 6 +02B3 107 +02B4 6 +02B6 7 +02B7 7 +02C1 -1 +02C2 -1 +02C5 30 +02C8 3 +02C9 3 +02C6 30 +02C7 7 +02CB 65 +02CC 4 +02CD 71 +02CE 10 +02CF 6 +02D5 2 +02D0 -1 +02D1 -1 +02D2 -1 +02D3 4 +02D4 39 +02D6 6 +02D7 -1 +02D8 10 +02D9 10 +02DA 3 +02DB -1 +02DC -1 +02DD 32 +02DE 6 +02DF 36 +02E0 34 +02E1 33 +02E2 8 +02E3 10 +02E4 6 +02E5 5 +02E6 6 +02E7 -1 +02E8 -1 +02E9 -1 +02EA -1 +02EB 13 +02EC 71 +02ED 63 +02EE 64 +02EF 8 +02F0 10 +02F1 2 +02F2 2 +035C 2 +035D -1 +035E 2 +03DD 18 +03DE 18 +0439 8 +043D 8 +043E -1 +043F 25 +0440 10 +0441 4 +0442 -1 +0443 8 +0446 14 +0448 -1 +0449 4 +044A 6 +044B 2 +0447 2 +07D7 8 +07D8 8 +07D9 268 +07DA 6 +07DB 8 +07DC 6 +07DD 54 +07DE 30 +07DF 54 +07E0 58 +07E1 15 +07E2 8 +07E3 6 +07E6 8 +07E5 4 +07E8 -1 +07E7 32 +07E9 5 +07EA 2 +07EB -1 +07ED 10 +07EE 6 +07EF 8 +07F0 6 +07F1 18 +07F2 8 +07F3 6 +07F4 3 +07F5 6 +07F6 14 +07F7 -1 +07F8 -1 +07F9 -1 +07FA 8 +07FB 25 +07FC 10 +07FD -1 +07FE 26 +0800 -1 +0801 -1 +0803 4 +0804 14 +0805 -1 +0806 2 +0807 4 +0808 14 +0809 50 +080A 18 +080B 6 +080C 2 +080D 3 +080E 14 +080F 30 +0810 3 +0812 8 +0813 -1 +0814 86 +0816 6 +0818 -1 +081A 4 +0824 8 +081B 12 +081C 10 +081D 22 +081E 8 +081F -1 +0820 11 +0821 2 +0822 9 +0823 -1 +0825 -1 +0826 4 +0836 -1 +0837 3 +0839 66 +083A 5 +083B 2 +083D 6 +083E 26 +0840 -1 +0841 4 +0827 6 +0828 14 +0829 12 +082A 10 +082B 6 +082C 10 +0842 6 +0843 6 +0844 2 +0845 6 +0846 4 +0847 -1 +0848 -1 +0849 12 +084A 2 +084B 21 +084C 10 +084D 10 +084E 5 +084F 6 +0850 7 +0855 6 +0851 -1 +0852 2 +0853 -1 +0854 -1 +0856 -1 +0857 -1 +0858 -1 +0859 -1 +08B1 -1 +082D -1 +08B2 -1 +08AF 10 +08B0 17 +08B3 -1 +08B4 2 +08B5 6 +08B6 3 +02F3 -1 +02F4 3 +02F5 7 +02F6 7 +08B8 10 +08B9 12 +08BA 10 +08BB 8 +08BC 10 +08BD 8 +08BE 14 +08BF 8 +08C3 10 +08C4 8 +08C5 6 +08C6 4 +08C0 -1 +08C1 2 +08C2 2 +08C7 -1 +08C8 34 +08C9 2 +08CA -1 +08CB -1 +097B -1 +08CC 109 +08CE 2 +08CF 10 +08D0 9 +08D1 7 +08D2 10 +08D3 10 +08D4 8 +08D5 -1 +08D6 6 +08D7 28 +08D8 27 +08D9 30 +08DA 26 +08DB 27 +08DC 26 +08DD 27 +08DE 27 +08DF 50 +08E0 51 +08E1 51 +08E2 27 +08E3 157 +08E4 6 +08FC 30 +08FD 6 +08FE -1 +08FF 24 +0900 -1 +0901 -1 +0902 -1 +0903 -1 +0904 -1 +0905 -1 +0906 -1 +0907 5 +0908 5 +090A 26 +090D -1 +090E 2 +0910 10 +0911 30 +0912 10 +0913 30 +0914 -1 +090F -1 +0915 -1 +0916 26 +096B 4 +096C 6 +096D -1 +096E -1 +096F 7 +0971 6 +0972 -1 +0973 7 +0974 2 +0975 -1 +0976 -1 +0977 14 +0978 6 +0979 50 +097A -1 +097C 4 +097D 288 +097E 12 +097F -1 +0980 7 +0981 -1 +0982 7 +0983 29 +0984 28 +0985 -1 +0986 10 +0987 -1 +0988 6 +0989 2 +098A -1 +098D -1 +098E -1 +098B 2 +098C 4 +098F -1 +0990 41 +0991 -1 +0992 -1 +0993 -1 +0994 -1 +0995 -1 +0996 -1 +0997 -1 +0998 8 +0999 11 +099A 9 +099B 8 +099C 6 +099D -1 +099E 12 +099F -1 +09A0 6 +09A1 2 +09A2 6 +09A3 -1 +09A4 18 +09A5 7 +09AB 6 +09A6 12 +09A7 10 +09A8 16 +09A9 10 +09AA 16 +09AC -1 +09AD 12 +09AE 19 +09AF 4 +09B0 10 +09B1 4 +09B2 10 +09B3 6 +09B4 6 +09B5 2 +09B6 6 +09B7 4 +09B8 6 +09B9 4 +09BA 2 +09BB 6 +09BC 6 +09BD 2 +09BE 2 +09BF 4 +09C1 10 +09C2 -1 +09C3 10 +09C4 10 +09C5 1042 +09C6 -1 +09C7 18 +09C8 -1 +09C9 -1 +09CA -1 +09CB 17 +09CC -1 +09CD 8 +09CE 102 +09CF -1 +09D0 -1 +09D1 14 +09D2 -1 +09D3 -1 +09D4 2 +09D5 -1 +09D6 -1 +09D8 2 +09D7 -1 +09D9 4 +09DA -1 +09DB -1 +09DC -1 +09DD -1 +09DE -1 +09DF 7 +09E0 -1 +09E1 8 +09E2 8 +09E3 8 +09E4 8 +09E5 18 +09E6 24 +09E7 3 +09E8 11 +09E9 2 +09EE 11 +09EF 11 +09F0 -1 +0A7D -1 +09F5 11 +09F6 11 +09EA 11 +09EB -1 +09F7 77 +09EC -1 +09ED 3 +09F3 11 +09F4 12 +09F1 11 +09F2 12 +0A04 6 +0A05 63 +0A06 6 +0A07 9 +0A03 2 +0A08 26 +0A12 27 +0A13 26 +0A14 10 +0A51 34 +0A32 2 +09F8 -1 +09F9 143 +09FA -1 +09FB -1 +09FC 6 +09FD -1 +09FE -1 +09FF -1 +0A00 269 +0A01 3 +0A02 4 +0A09 55 +0A0A 57 +0A0B 57 +0A0C 66 +0A0D -1 +0A0F -1 +0A10 -1 +0A11 -1 +0A0E 14 +0A15 12 +0A16 26 +0A17 6 +0A18 14 +0A19 2 +0A1A 25 +0A1B 2 +0A1C -1 +0A1D 2 +0A1E 3 +0A1F 2 +0A20 23 +0A21 3 +0A22 7 +0A23 -1 +0A24 66 +0A25 6 +0A26 7 +0A27 8 +0A28 3 +0A29 6 +0A2A 6 +0A2B 10 +0A2C 12 +0A2D -1 +0A2E 6 +0A2F 7 +0A30 106 +0A31 -1 +0A33 7 +0A34 6 +0A35 4 +0A36 7 +0A37 69 +0A38 3 +0A68 3 +0AE2 7 +0A39 36 +0A3A 12 +0A3B -1 +0A3C -1 +0A3D 20 +0A3E -1 +0A3F 11 +0A40 11 +0A41 18 +0A42 43 +0A43 85 +0A44 -1 +0A46 14 +0A47 3 +0A48 2 +0AFC 16 +0A49 22 +0A4A 6 +0A4B 22 +0A4C 28 +0A8F 2 +0A90 3 +0B23 6 +0A4D -1 +0A79 -1 +0B61 -1 +0B6A -1 +0A4E 6 +0A70 2 +0A4F -1 +0A50 4 +0A52 20 +0A53 10 +0A54 -1 +0A55 2 +0A56 6 +0A57 6 +0A58 8 +0A59 -1 +0A5A 2 +0A5B 7 +0A5C 18 +0A5D 6 +0A69 6 +0A6A 12 +0A6B -1 +0A6C 7 +0A6D -1 +0A6E -1 +0A6F -1 +0A71 -1 +0A72 61 +0A73 2 +0A74 8 +0A76 80 +0A77 15 +0A78 15 +0A7B -1 +0A7C -1 +0A7E -1 +0A8C 2 +0A80 6 +0A7F -1 +0A8D -1 +0A81 4 +0A92 -1 +0A91 -1 +0A93 3 +0A94 2 +0A89 61 +0B05 63 +0A8A 6 +0A82 46 +0A83 46 +0A84 94 +0A85 82 +0A86 -1 +0A87 -1 +0B7B 118 +0B7C -1 +0B7D -1 +0B7E 60 +0A88 2 +0A8B 2 +0A8E 2 +0A95 4 +0A96 61 +0A97 8 +0A98 10 +0A99 4 +0A9A 10 +0A9B -1 +0A9C 2 +0ACE 4 +0A9D 4 +0A9E 2 +0A9F 2 +0AA0 2 +0AA1 4 +0AA2 -1 +0AA3 9 +0AA4 2 +0AA5 -1 +0AA6 36 +0AA7 6 +0AA8 5 +0AA9 -1 +0AAA -1 +0AAB -1 +0AAC 69 +0AB1 14 +0AAD 51 +0AAE 2 +0AAF 6 +0AB0 6 +0ABA 2 +0ABB 2 +0AB2 7 +0AB3 19 +0AB4 6 +0AB5 2 +0AB6 8 +0AB7 4 +0AB8 2 +0AB9 47 +0ABC -1 +0ABD 10 +0ABE -1 +0ABF -1 +0AC0 26 +0AC1 26 +0AC2 -1 +0AC3 2 +0AC4 -1 +0AC5 156 +0AC6 156 +0AC7 156 +0AC8 2 +0AC9 -1 +0ACA 3 +0ACB 12 +0ACC 18 +0ACD 23 +0ACF 68 +0AD0 11 +0AD1 -1 +0AD2 30 +0AD3 -1 +0AD4 -1 +0AD5 2 +0AD6 2 +0AD7 8 +0AD8 8 +0AD9 -1 +0ADA 32 +0ADB -1 +0ADC 6 +0ADD 24 +0ADE 6 +0ADF 58 +0AE0 30 +0AE1 28 +0AE4 89 +0AE5 -1 +0AE3 -1 +0AE6 10 +0AE7 38 +0AE8 2 +0AEC 2 +0AED 2 +0AEE 2 +0AE9 13 +0AEF 2 +0AF0 10 +0AF1 102 +0AF2 40 +0AF3 -1 +0AF4 11 +0AF5 3 +0AF6 88 +0AF7 32 +0AF8 11 +0AF9 6 +0AFA 58 +0AFB -1 +0AFD -1 +0B0C 155 +0AFE -1 +0AFF -1 +0B00 8 +0B01 56 +0B02 26 +0B03 -1 +0B04 90 +0B07 -1 +0B08 -1 +0B09 -1 +0B0A -1 +0B0B 4 +0B0D 10 +0B0E -1 +0B0F -1 +0B10 10 +0B11 4 +0B12 2 +0B13 48 +0B14 2 +0B16 2 +0B19 2 +0B15 7 +0B17 3 +0B18 4 +0B1A 29 +0B1B 2 +0B1C 2 +0B1D 2 +0B1E 14 +0B1F 14 +0B20 271 +0B22 5 +0B21 13 +0B24 6 +0B25 6 +0B27 -1 +0B28 3 +0B2B 11 +0B2C 3 +0B2D 11 +0B2E 4 +0B2F 73 +0B30 -1 +0B31 17 +0B32 -1 +0B33 17 +0B34 50 +0B35 3 +0B36 -1 +0B37 -1 +0B39 -1 +0B3C 4 +0B3D -1 +0B3E -1 +0B3F 64 +0B40 -1 +0B41 70 +0B42 62 +0B43 48 +0B44 58 +0B45 58 +0B46 10 +0B47 14 +0B48 18 +0B49 4 +0B4A 6 +0B4B 4 +0B4C 2 +0B4D -1 +0B4F 2 +0B50 2 +0B51 2 +0B52 2 +0B53 52 +0B54 8 +0B4E -1 +0B55 -1 +0B56 -1 +0B57 -1 +0B58 2 +0B59 4 +0B5A -1 +0B5B 14 +0B5C 2 +0B5D 10 +0B5E 33 +0B5F -1 +0B60 -1 +0B62 -1 +0B63 -1 +0B64 -1 +0B65 -1 +0B66 26 +0B67 33 +0B68 12 +0B69 18 +0B6B 14 +0B6C 12 +0B6D 6 +0B6E 14 +0B6F 177 +0B70 -1 +0B71 177 +0B72 -1 +0B73 8 +0B74 1026 +0B75 1026 +0B76 77 +0B77 -1 +0B78 -1 +0B79 -1 +0B7A -1 +0B7F 10 +0B80 10 +0B8C -1 +0B8D -1 +0B8E 18 +0B8F 6 +0B90 2 +0B91 8 +0B92 5 +0B93 12 +0B94 14 +0B95 -1 +0B96 26 +0B97 27 +0B98 6 +0B99 10 +0B9A 11 +0B9B 12 +0B9C 16 +0B9D 14 +0B9E 12 +0B9F 10 +0BA0 2 +0BA1 3 +0BA2 10 +0BA3 10 +0BA4 85 +0BA5 12 +0BA6 -1 +0BA7 -1 +0BA8 7 +0BA9 -1 \ No newline at end of file diff --git a/openkore_llm_knowledge/tables/iRO/skillnametable.txt b/openkore_llm_knowledge/tables/iRO/skillnametable.txt new file mode 100644 index 0000000000..a85a1d9570 --- /dev/null +++ b/openkore_llm_knowledge/tables/iRO/skillnametable.txt @@ -0,0 +1,993 @@ +NV_BASIC#Basic Skill# +SM_SWORD#Sword Mastery# +SM_TWOHAND#Two Handed Sword Mastery# +SM_RECOVERY#Increase HP Recovery# +SM_BASH#Bash# +SM_PROVOKE#Provoke# +SM_MAGNUM#Magnum Break# +SM_ENDURE#Endure# +MG_SRECOVERY#Increase SP Recovery# +MG_SIGHT#Sight# +MG_NAPALMBEAT#Napalm beat# +MG_SAFETYWALL#Safety wall# +MG_SOULSTRIKE#Soul Strike# +MG_COLDBOLT#Cold Bolt# +MG_FROSTDIVER#Frost diver# +MG_STONECURSE#Stone Curse# +MG_FIREBALL#Fireball# +MG_FIREWALL#Firewall# +MG_FIREBOLT#Firebolt# +MG_LIGHTNINGBOLT#Lightening Bolt# +MG_THUNDERSTORM#Thunder storm# +AL_DP#Divine Protection# +AL_DEMONBANE#Demonbane# +AL_RUWACH#Ruwach# +AL_PNEUMA#Pneuma# +AL_TELEPORT#Teleport# +AL_WARP#Warp portal# +AL_HEAL#Heal# +AL_INCAGI#Increase agility# +AL_DECAGI#Decrease agility# +AL_HOLYWATER#Aqua Benedicta# +AL_CRUCIS#Signum Crucis# +AL_ANGELUS#Angelus# +AL_BLESSING#Blessing# +AL_CURE#Cure# +MC_INCCARRY#Enlarge Weight limit# +MC_DISCOUNT#Discount# +MC_OVERCHARGE#Overcharge# +MC_PUSHCART#Push Cart(Pushcart)# +MC_IDENTIFY#Item Appraisal# +MC_VENDING#Vending# +MC_MAMMONITE#Mammonite# +AC_OWL#Owl's Eye# +AC_VULTURE#Vulture's Eye# +AC_CONCENTRATION#Improve Concentration(Attention concentrate)# +AC_DOUBLE#Double Strafe(Double strafing)# +AC_SHOWER#Arrow Shower# +TF_DOUBLE#Double Attack# +TF_MISS# Improve Dodge# +TF_STEAL#Steal# +TF_HIDING#Hiding# +TF_POISON#Envenom# +TF_DETOXIFY#Detoxify# +ALL_RESURRECTION#Resurrection# +KN_SPEARMASTERY#Spear Mastery# +KN_PIERCE#Pierce# +KN_BRANDISHSPEAR#Brandish Spear# +KN_SPEARSTAB#Spear Stab# +KN_SPEARBOOMERANG#Spear Boomerang# +KN_TWOHANDQUICKEN#TwoHand Quicken# +KN_AUTOCOUNTER#Auto Counter# +KN_BOWLINGBASH#Bowling Bash# +KN_RIDING#Peco Peco Ride# +KN_CAVALIERMASTERY#Cavalier Mastery# +PR_MACEMASTERY#Mace Mastery# +PR_IMPOSITIO#Impositio Manus# +PR_SUFFRAGIUM#Suffragium# +PR_ASPERSIO#Aspersio# +PR_BENEDICTIO#Benedictio Sanctissimi Sacramenti# +PR_SANCTUARY#Sanctuary# +PR_SLOWPOISON#Slow Poison# +PR_STRECOVERY#Recovery# +PR_KYRIE#Kyrie Eleison# +PR_MAGNIFICAT#Magnificat# +PR_GLORIA#Gloria# +PR_LEXDIVINA#Lex Divina# +PR_TURNUNDEAD#Turn Undead# +PR_LEXAETERNA#Lex Aeterna# +PR_MAGNUS#Magnus Exorcismus# +WZ_FIREPILLAR#Fire Pillar# +WZ_SIGHTRASHER#Sightrasher# +WZ_FIREIVY#Fire Ivy# +WZ_METEOR#Meteor Storm# +WZ_JUPITEL#Jupitel Thunder# +WZ_VERMILION#Lord of Vermilion# +WZ_WATERBALL#Waterball# +WZ_ICEWALL#Icewall# +WZ_FROSTNOVA#Frost Nova# +WZ_STORMGUST#Storm Gust# +WZ_EARTHSPIKE#Earth Spike# +WZ_HEAVENDRIVE#Heaven's Drive# +WZ_QUAGMIRE#Quagmire# +WZ_ESTIMATION#Monster Property# +BS_IRON#Iron Tempering# +BS_STEEL#Steel Tempering# +BS_ENCHANTEDSTONE#Enchanted Stone Craft# +BS_ORIDEOCON#Oridecon Research# +BS_DAGGER#Smith Dagger# +BS_SWORD#Smith Sword# +BS_TWOHANDSWORD#Smith Two-handed Sword# +BS_AXE#Smith Axe# +BS_MACE#Smith Mace# +BS_KNUCKLE#Smith Brass Knuckle# +BS_SPEAR#Smith Spear# +BS_HILTBINDING#Hilt Binding# +BS_FINDINGORE#Finding Ore# +BS_WEAPONRESEARCH#Weaponry Research# +BS_REPAIRWEAPON#Repair Weapon# +BS_SKINTEMPER#Skin Tempering# +BS_HAMMERFALL#Hammerfall# +BS_ADRENALINE#Adrenaline Rush# +BS_WEAPONPERFECT#Weapon Perfection# +BS_OVERTHRUST#Power Thrust(Over Thrust)# +BS_MAXIMIZE#Maximize Power# +HT_SKIDTRAP#Skid Trap# +HT_LANDMINE#Land Mine# +HT_ANKLESNARE#Anklesnare# +HT_SHOCKWAVE#Shockwave Trap# +HT_SANDMAN#Sandman# +HT_FLASHER#Flasher# +HT_FREEZINGTRAP#Freezing Trap# +HT_BLASTMINE#Blast Mine# +HT_CLAYMORETRAP#Claymore Trap# +HT_REMOVETRAP#Remove Trap# +HT_TALKIEBOX#Talkie Box# +HT_BEASTBANE#Beastbane# +HT_FALCON#Falconry Mastery# +HT_STEELCROW#Steel Crow# +HT_BLITZBEAT#Blitz Beat# +HT_DETECTING#Detecting# +HT_SPRINGTRAP#Spring Trap# +AS_RIGHT#Right hand Mastery# +AS_LEFT#Left hand Mastery# +AS_KATAR#Katar Matery# +AS_CLOAKING#Cloaking# +AS_SONICBLOW#Sonic Blow# +AS_GRIMTOOTH#Grimtooth# +AS_ENCHANTPOISON#Enchant Poison# +AS_POISONREACT#Poison React# +AS_VENOMDUST#Venom Dust# +AS_SPLASHER#Venom Splasher# +NV_FIRSTAID#First Aid# +NV_TRICKDEAD#Play Dead# +SM_MOVINGRECOVERY#HP Recovery While Moving# +SM_FATALBLOW#Fatal Blow# +SM_AUTOBERSERK#Berserk# +AC_MAKINGARROW#Arrow Crafting# +AC_CHARGEARROW#Arrow Repel# +TF_SPRINKLESAND#Sand Attack# +TF_BACKSLIDING#Back Slide# +TF_PICKSTONE#Find Stone# +TF_THROWSTONE#Stone Fling# +MC_CARTREVOLUTION#Cart Revolution# +MC_CHANGECART#Change Cart# +MC_LOUD#Crazy Uproar# +AL_HOLYLIGHT#Holy Light# +MG_ENERGYCOAT#Energy Coat# +RG_SNATCHER#Snatcher# +RG_STEALCOIN#Steal Coin# +RG_BACKSTAP#Back Stab# +RG_TUNNELDRIVE#Stalk (Tunnel Drive)# +RG_RAID#Raid# +RG_STRIPWEAPON#Divest Weapon (Strip Weapon)# +RG_STRIPSHIELD#Strip Shield# +RG_STRIPARMOR#Strip Armor# +RG_STRIPHELM#Strip Helm# +RG_INTIMIDATE#Intimidate# +RG_GRAFFITI#Graffiti# +RG_FLAGGRAFFITI#Flag Graffiti# +RG_CLEANER#Remover# +RG_GANGSTER#GangSter's Paradise# +RG_COMPULSION#Compulsion Discount# +RG_PLAGIARISM#Intimidate (Plagiarism)# +AM_AXEMASTERY#Axe Mastery# +AM_LEARNINGPOTION#Learning Potion# +AM_PHARMACY#Pharmacy# +AM_DEMONSTRATION#Demonstration# +AM_ACIDTERROR#Acid Terror# +AM_POTIONPITCHER#Potion Pitcher# +AM_CANNIBALIZE#Bio Cannibalize# +AM_SPHEREMINE#Marine Sphere# +AM_CP_WEAPON#Chemical Protection(Weapon) (Chemical Protection Weapon)# +AM_CP_SHIELD#Chemical Protection(Shield) (Chemical Protection Shield)# +AM_CP_ARMOR#Chemical Protection(Armor) (Chemical Protection Armor)# +AM_CP_HELM#Chemical Protection(Helm) (Chemical Protection Helm)# +AM_BIOETHICS#Bioethics# +AM_BIOTECHNOLOGY#Biotechnology# +AM_CREATECREATURE#Create Creature# +AM_CULTIVATION#Cultivation# +AM_FLAMECONTROL#Flame Control# +AM_CALLHOMUN#Call Homunculus# +AM_REST#Rest# +AM_DRILLMASTER#Drillmaster# +AM_HEALHOMUN#Heal Homunculus# +AM_RESURRECTHOMUN#Resurrect Homunculus# +CR_TRUST#Faith (Faith)# +CR_AUTOGUARD#Auto Guard# +CR_SHIELDCHARGE#Smite (Shield Charge)# +CR_SHIELDBOOMERANG#Shield Boomerang# +CR_REFLECTSHIELD#Reflect Shield# +CR_HOLYCROSS#Holy Cross# +CR_GRANDCROSS#Grand Cross# +CR_DEVOTION#Sacrifice (Devotion)# +CR_PROVIDENCE#Providence# +CR_DEFENDER#Defender# +CR_SPEARQUICKEN#Spear Quicken# +MO_IRONHAND#Iron Fists# +MO_SPIRITSRECOVERY#Spiritual Cadence# +MO_CALLSPIRITS#Summon Spirit Sphere# +MO_ABSORBSPIRITS#Spiritual Sphere Absorption# +MO_TRIPLEATTACK#Raging Trifecta Blow# +MO_BODYRELOCATION#Snap# +MO_DODGE#Flee (Skill)# +MO_INVESTIGATE#Occult Impaction# +MO_FINGEROFFENSIVE#Throw Spirit Sphere# +MO_STEELBODY#Mental Strength# +MO_BLADESTOP#Root# +MO_EXPLOSIONSPIRITS#Fury# +MO_EXTREMITYFIST#Guillotine Fist# +MO_CHAINCOMBO#Raging Quadruple Blow# +MO_COMBOFINISH#Raging Thrust# +SA_ADVANCEDBOOK#Study (Advanced Book)# +SA_CASTCANCEL#Cast Cancel (Cast Cancel)# +SA_MAGICROD#Magic Rod (Magic Rod)# +SA_SPELLBREAKER#Spell Breaker (Spell Breaker)# +SA_FREECAST#Free Cast# +SA_AUTOSPELL#Hindsight (Auto Spell)# +SA_FLAMELAUNCHER#Flame Launcher# +SA_FROSTWEAPON#Frost Weapon# +SA_LIGHTNINGLOADER#Lightning Loader# +SA_SEISMICWEAPON#Seismic Weapon# +SA_DRAGONOLOGY#Dragonology# +SA_VOLCANO#Volcano# +SA_DELUGE#Deluge# +SA_VIOLENTGALE#Whirlwind (Violent Gale)# +SA_LANDPROTECTOR#Land Protector# +SA_DISPELL#Dispell# +SA_ABRACADABRA#Hocus Pocus# +SA_MONOCELL#Mono Cell# +SA_CLASSCHANGE#Class Change# +SA_SUMMONMONSTER#Summon Monster# +SA_REVERSEORCISH#Reverse Orcish# +SA_DEATH#Death# +SA_FORTUNE#Fortune# +SA_TAMINGMONSTER#Taming Monster# +SA_QUESTION#?# +SA_GRAVITY#Gravity# +SA_LEVELUP#Level Up# +SA_INSTANTDEATH#Instant Death# +SA_FULLRECOVERY#Full Recovery# +SA_COMA#Coma# +BD_ADAPTATION#Amp# +BD_ENCORE#Encore# +BD_LULLABY#Lullaby# +BD_RICHMANKIM#Mental Sensing# +BD_ETERNALCHAOS#Down Tempo# +BD_DRUMBATTLEFIELD#Battle Theme# +BD_RINGNIBELUNGEN#Harmonic Lick# +BD_ROKISWEIL#Classical Pluck# +BD_INTOABYSS#Power Cord# +BD_SIEGFRIED#Acoustic Rhythm# +BD_RAGNAROK#Ragnarok# +BA_MUSICALLESSON#Musical Lesson# +BA_MUSICALSTRIKE#Melody Strike (Musical Strike)# +BA_DISSONANCE#Unchained Serenade (Dissonance)# +BA_FROSTJOKE#Unbarring Octave# +BA_WHISTLE#Perfect Tablature# +BA_ASSASSINCROSS#Impressive Riff# +BA_POEMBRAGI#Magic Strings# +BA_APPLEIDUN#Song of Lutie# +DC_DANCINGLESSON#Dancing Lesson# +DC_THROWARROW#Slinging Arrow (Throw Arrow)# +DC_UGLYDANCE#Hip Shaker (Ugly Dance)# +DC_SCREAM#Dazzler# +DC_HUMMING#Focus Ballet# +DC_DONTFORGETME#Slow Grace# +DC_FORTUNEKISS#Lady Luck# +DC_SERVICEFORYOU#Gypsy's Kiss# +WE_MALE#I'll save you# +WE_FEMALE#I'll sacrifice myself for you# +WE_CALLPARTNER#I'm missing you# +ITM_TOMAHAWK#Tomahawk Throwing# +LK_AURABLADE#Aura Blade# +LK_PARRYING#Parrying# +LK_CONCENTRATION#Spear Dynamo# +LK_TENSIONRELAX#Tension Relax# +LK_BERSERK#Berserk# +HP_ASSUMPTIO#Assumptio# +HP_BASILICA#Basilica# +HP_MEDITATIO#Meditatio# +HW_SOULDRAIN#Soul Drain# +HW_MAGICCRASHER#Stave Crasher (Magic Crasher)# +HW_MAGICPOWER#Mystical Amplification# +PA_PRESSURE#Gloria Domini# +PA_SACRIFICE#Martyr's Reckoning# +PA_GOSPEL#Battle Chant# +CH_PALMSTRIKE#Raging Palm Strike# +CH_TIGERFIST#Glacier Fist# +CH_CHAINCRUSH#Chain Crush Combo# +PF_HPCONVERSION#Indulge# +PF_SOULCHANGE#Soul Exhale# +PF_SOULBURN#Soul Siphon# +ASC_KATAR#Advanced Katar Mastery# +ASC_EDP#Enchant Deadly Poison# +ASC_BREAKER#Soul Destroyer# +SN_SIGHT#Falcon Eyes# +SN_FALCONASSAULT#Falcon Assault# +SN_SHARPSHOOTING#Focused Arrow Strike# +SN_WINDWALK#Wind Walker# +WS_MELTDOWN#Shattering Strike# +WS_CARTBOOST#Cart Boost# +ST_CHASEWALK#Stealth# +ST_REJECTSWORD#Counter Instinct# +CG_ARROWVULCAN#Arrow Vulcan# +CG_MOONLIT#Sheltering Bliss# +CG_MARIONETTE#Marionette Control# +LK_SPIRALPIERCE#Clashing Spiral# +LK_HEADCRUSH#Traumatic Blow# +LK_JOINTBEAT#Vital Strike# +HW_NAPALMVULCAN#Napalm Vulcan# +CH_SOULCOLLECT#Zen# +PF_MINDBREAKER#Mind Breaker# +PF_MEMORIZE#Foresight# +PF_FOGWALL#Blinding Mist# +PF_SPIDERWEB#Fiber Lock# +ASC_METEORASSAULT#Meteor Assault# +ASC_CDP#Create Deadly Poison# +WE_BABY#Mom, Dad, I love you!# +WE_CALLPARENT#Mom, Dad, I miss you!# +WE_CALLBABY#Come to me, honey~# +TK_RUN#Sprint# +TK_READYSTORM#Tornado Stance# +TK_STORMKICK#Tornado Kick# +TK_READYDOWN#Heel Drop Stance# +TK_DOWNKICK#Heel Drop# +TK_READYTURN#Roundhouse Stance# +TK_TURNKICK#Roundhouse# +TK_READYCOUNTER#Counter Kick Stance# +TK_COUNTER#Counter Kick# +TK_DODGE#Tumbling# +TK_JUMPKICK#Flying Kick# +TK_HPTIME#Peaceful Break# +TK_SPTIME#Happy Break# +TK_POWER#Kihop# +TK_SEVENWIND#Mild Wind# +TK_HIGHJUMP#Leap# +SG_FEEL#Solar, Lunar and Stellar Perception# +SG_SUN_WARM#Solar Heat# +SG_MOON_WARM#Lunar Heat# +SG_STAR_WARM#Stellar Heat# +SG_SUN_COMFORT#Solar Protection# +SG_MOON_COMFORT#Lunar Protection# +SG_STAR_COMFORT#Stellar Protection# +SG_HATE#Solar, Lunar and Stellar Opposition# +SG_SUN_ANGER#Solar Wrath# +SG_MOON_ANGER#Lunar Wrath# +SG_STAR_ANGER#Stellar Wrath# +SG_SUN_BLESS#Solar Blessings# +SG_MOON_BLESS#Lunar Blessings# +SG_STAR_BLESS#Stellar Blessings# +SG_DEVIL#Solar, Lunar and Stellar Shadow # +SG_FRIEND#Solar, Lunar and Stellar Team-Up# +SG_KNOWLEDGE#Solar, Lunar and Stellar Courier # +SG_FUSION#Solar, Lunar and Stellar Union # +SL_ALCHEMIST#Alchemist Spirit# +AM_BERSERKPITCHER#Aid Berserk Potion# +SL_MONK#Monk Spirit# +SL_STAR#Taekwon Master Spirit# +SL_SAGE#Sage Spirit# +SL_CRUSADER#Crusader Spirit# +SL_SUPERNOVICE#Super Novice Spirit# +SL_KNIGHT#Knight Spirit# +SL_WIZARD#Wizard Spirit# +SL_PRIEST#Priest Spirit# +SL_BARDDANCER#Bard and Dancer Spirits# +SL_ROGUE#Rogue Spirit# +SL_ASSASIN#Assassin Spirit# +SL_BLACKSMITH#Blacksmith Spirit# +BS_ADRENALINE2#Advanced Adrenaline Rush# +SL_HUNTER#Hunter Spirit# +SL_SOULLINKER#Soul Linker Spirit# +SL_KAIZEL#Kaizel# +SL_KAAHI#Kaahi# +SL_KAUPE#Kaupe# +SL_KAITE#Kaite# +SL_KAINA#Kaina# +SL_STIN#Estin# +SL_STUN#Estun# +SL_SMA#Esma# +SL_SWOO#Eswoo# +SL_SKE#Eske# +SL_SKA#Eska# +ST_PRESERVE#Preserve# +ST_FULLSTRIP#Full Divestment# +WS_WEAPONREFINE#Upgrade Weapon# +CR_SLIMPITCHER#Aid Condensed Potion# +CR_FULLPROTECTION#Full Chemical Protection# +PA_SHIELDCHAIN#Rapid Smiting# +HP_MANARECHARGE#Spiritual Thrift# +PF_DOUBLECASTING#Double Bolt# +HW_GANBANTEIN#Ganbantein# +HW_GRAVITATION#Gravitational Field# +WS_CARTTERMINATION#High Speed Cart Ram# +WS_OVERTHRUSTMAX#Maximum Power-Thrust# +CG_HERMODE#Hermode's Rod# +CG_TAROTCARD#Tarot Card of Fate# +CR_ACIDDEMONSTRATION#Acid Bomb# +CR_CULTIVATION#Cultivate Plant# +TK_MISSION#Taekwon Mission# +SL_HIGH#1st Transcendent Spirit# +KN_ONEHAND#One Hand Quicken# +AM_TWILIGHT1#Spiritual Potion Creation# +AM_TWILIGHT2#Spiritual Potion Creation# +AM_TWILIGHT3#Spiritual Potion Creation# +HT_POWER#Beast Charge# +GS_GLITTERING#Coin Flip# +GS_FLING#Coin Fling# +GS_TRIPLEACTION#Triple Action# +GS_BULLSEYE#Bull's Eye# +GS_MADNESSCANCEL#Last Stand# +GS_ADJUSTMENT#Gunslinger's Panic# +GS_INCREASING#Increase Accuracy# +GS_MAGICALBULLET#Magicial Bullet# +GS_CRACKER#Cracker# +GS_SINGLEACTION#Single Action# +GS_SNAKEEYE#Snake Eyes# +GS_CHAINACTION#Chain Action# +GS_TRACKING#Tracking# +GS_DISARM#Disarm# +GS_PIERCINGSHOT#Wounding Shot# +GS_RAPIDSHOWER#Trigger Happy Shot# +GS_DESPERADO#Desperado# +GS_GATLINGFEVER#Gatling Fever# +GS_DUST#Crowd Control Shot# +GS_FULLBUSTER#Full Blast# +GS_SPREADATTACK#Spread Shot# +GS_GROUNDDRIFT#Gunslinger Mine# +NJ_TOBIDOUGU#Dagger Throwing Practice# +NJ_SYURIKEN#Throw Shuriken# +NJ_KUNAI#Throw Kunai# +NJ_HUUMA#Throw Huuma Shuriken# +NJ_ZENYNAGE#Throw Coins# +NJ_TATAMIGAESHI#Flip Tatami# +NJ_KASUMIKIRI#Haze Slasher# +NJ_SHADOWJUMP#Shadow Leap# +NJ_KIRIKAGE#Shadow Slash# +NJ_UTSUSEMI#Cicada Skin Shed# +NJ_BUNSINJYUTSU#Mirror Image# +NJ_NINPOU#Ninja Mastery# +NJ_KOUENKA#Flaming Petals# +NJ_KAENSIN#Blaze Shield# +NJ_BAKUENRYU#Exploding Dragon# +NJ_HYOUSENSOU#Freezing Spear# +NJ_SUITON#Watery Evasion# +NJ_HYOUSYOURAKU#Snow Flake Draft# +NJ_HUUJIN#Wind Blade# +NJ_RAIGEKISAI#Lightning Jolt# +NJ_KAMAITACHI#First Wind# +NJ_NEN#Ninja Aura# +NJ_ISSEN#Killing Strike# +ALL_INCCARRY#Enlarge Weight LimitR# +GM_SANDMAN#Goodnight, Sweety# +ALL_CATCRY#Monster's Cry# +ALL_ANGEL_PROTECT#Thank You So Much!!# +ALL_DREAM_SUMMERNIGHT#Summer Dream# +ALL_WEWISH#Sing along with the Singing Crystal's tune:# +KN_CHARGEATK#Charge Attack# +CR_SHRINK#Shrink# +AS_SONICACCEL#Sonic Acceleration# +AS_VENOMKNIFE#Venom Knife# +RG_CLOSECONFINE#Close Confine# +WZ_SIGHTBLASTER#Sight Blaster# +SA_CREATECON#Create Elemental Converter# +SA_ELEMENTWATER#Elemental Change - Water# +HT_PHANTASMIC#Phantasmic Arrow# +BA_PANGVOICE#Pang Voice# +DC_WINKCHARM#Charming Wink# +BS_UNFAIRLYTRICK#Dubious Salesmanship# +BS_GREED#Greed# +PR_REDEMPTIO#Redemptio# +MO_KITRANSLATION#Spiritual Bestowment# +MO_BALKYOUNG#Excruciating Palm# +SA_ELEMENTGROUND#Elemental Change - Ground# +SA_ELEMENTFIRE#Elemental Change - Fire# +SA_ELEMENTWIND#Elemental Change - Wind# +RK_ENCHANTBLADE#Enchant Blade# +RK_SONICWAVE#Sonic Wave# +RK_DEATHBOUND#Death Bound# +RK_HUNDREDSPEAR#Hundred Spears# +RK_WINDCUTTER#Wind Cutter# +RK_IGNITIONBREAK#Ignition Break# +RK_DRAGONTRAINING#Dragon Training# +RK_DRAGONBREATH#Dragon's Breath# +RK_DRAGONHOWLING#Dragon Howling# +RK_RUNEMASTERY#Rune Mastery# +RK_PHANTOMTHRUST#Phantom Thrust# +GC_VENOMIMPRESS#Venom Impression# +GC_CROSSIMPACT#Cross Impact# +GC_DARKILLUSION#Dark Illusion# +GC_RESEARCHNEWPOISON#New Poison Research# +GC_CREATENEWPOISON#New Poison Creation# +GC_ANTIDOTE#Antidote# +GC_POISONINGWEAPON#Poisonous Weapon# +GC_WEAPONBLOCKING#Weapon Blocking# +GC_COUNTERSLASH#Counter Slash# +GC_WEAPONCRUSH#Weapon Crush # +GC_VENOMPRESSURE#Venom Pressure# +GC_POISONSMOKE#Poisonous Smoke# +GC_CLOAKINGEXCEED#Cloaking Exceed# +GC_PHANTOMMENACE#Phantom Menace# +GC_HALLUCINATIONWALK#Hallucination Walk# +GC_ROLLINGCUTTER#Rolling Cutter# +GC_CROSSRIPPERSLASHER#Cross Ripper Slasher# +AB_JUDEX#Judex# +AB_ANCILLA#Ancilla# +AB_ADORAMUS#Adoramus# +AB_CLEMENTIA#Clementia# +AB_CANTO#Cantocandidus# +AB_CHEAL#Coluseo Heal# +AB_EPICLESIS#Epiclesis# +AB_PRAEFATIO#Praefatio# +AB_ORATIO#Oratio# +AB_LAUDAAGNUS#Lauda Agnus# +AB_LAUDARAMUS#Lauda Ramus# +AB_EUCHARISTICA#Eucharistica# +AB_RENOVATIO#Renovatio# +AB_HIGHNESSHEAL#HIGHNESSHEAL / High Priest's Healing# +AB_CLEARANCE#CLEARANCE / Removal# +AB_EXPIATIO#EXPIATIO / Expiation# +AB_DUPLELIGHT#Duple Light / Two Lights# +AB_SILENTIUM#Silentium# +WL_WHITEIMPRISON#White Imprison# +WL_SOULEXPANSION#Soul Expansion# +WL_FROSTMISTY#Frost Misty# +WL_JACKFROST#Jack Frost# +WL_MARSHOFABYSS#Marsh Of Abyss# +WL_RECOGNIZEDSPELL#Recognized Spell# +WL_SIENNAEXECRATE#Sienna Execrate# +WL_RADIUS#Radius# +WL_STASIS#Stasis# +WL_DRAINLIFE#Drain Life# +WL_CRIMSONROCK#Crimson Rock# +WL_HELLINFERNO#Hell Inferno# +WL_COMET#Comet# +WL_CHAINLIGHTNING#Chain Lightning# +WL_EARTHSTRAIN#Earth Strain# +WL_TETRAVORTEX#Tetra Vortex# +WL_SUMMONFB#Summon Fire Ball# +WL_SUMMONBL#Summon Lightning Ball# +WL_SUMMONWB#Summon Water Ball# +WL_SUMMONSTONE#Summon Stone# +WL_RELEASE#Release# +WL_READING_SB#Reading Spellbook# +WL_FREEZE_SP#Freezing Spell# +RA_ARROWSTORM#Arrow Storm# +RA_FEARBREEZE#Fear Breeze# +RA_RANGERMAIN#Main Ranger# +RA_AIMEDBOLT#Aimed Bolt# +RA_DETONATOR#Detonator# +RA_ELECTRICSHOCKER#Electric Shock# +RA_CLUSTERBOMB#Bomb Cluster# +RA_WUGMASTERY#Warg Mastery# +RA_WUGRIDER#Warg Ride# +RA_WUGDASH#Warg Dash# +RA_WUGSTRIKE#Warg Strike# +RA_WUGBITE#Warg Bite# +RA_TOOTHOFWUG#Warg Teeth# +RA_SENSITIVEKEEN#Keen Nose# +RA_CAMOUFLAGE#Camouflage# +RA_RESEARCHTRAP#Trap Research (RESEARCH TRAP / Trap Research)# +RA_MAGENTATRAP#Magenta Trap# +RA_COBALTTRAP#Cobalt Trap# +RA_MAIZETRAP#Maze Trap# +RA_VERDURETRAP#Verdure Trap# +RA_FIRINGTRAP#Fire Trap# +RA_ICEBOUNDTRAP#Ice Trap# +NC_MADOLICENCE#Madogear License# +NC_BOOSTKNUCKLE#Knuckle Boost# +NC_PILEBUNKER#Pile Bunker# +NC_VULCANARM#Vulcan Arm# +NC_FLAMELAUNCHER#Flame Launcher# +NC_COLDSLOWER#Ice Launcher# +NC_ARMSCANNON#Arm Cannon# +NC_ACCELERATION#Acceleration# +NC_HOVERING#Hover# +NC_F_SIDESLIDE#Front Slide# +NC_B_SIDESLIDE#Back Slide# +NC_MAINFRAME#Remodel Mainframe# +NC_SELFDESTRUCTION#Suicidal Destruction# +NC_SHAPESHIFT#Elemental Shift# +NC_EMERGENCYCOOL#Cooldown# +NC_INFRAREDSCAN#Infrared Scan# +NC_ANALYZE#Analyze# +NC_MAGNETICFIELD#Magnetic Field# +NC_NEUTRALBARRIER#Neutral Barrier# +NC_STEALTHFIELD#Stealth Field# +NC_REPAIR#Repair# +NC_TRAININGAXE#Axe Mastery # +NC_RESEARCHFE#Fire Earth Research # +NC_AXEBOOMERANG#Axe Boomerang# +NC_POWERSWING#Power Swing# +NC_AXETORNADO#Axe Tornado # +NC_SILVERSNIPER#FAW Silver Sniper# +NC_MAGICDECOY#FAW Magic Decoy# +NC_DISJOINT#Divest FAW # +SC_FATALMENACE#Fatal Manace# +SC_REPRODUCE#Reproduce# +SC_AUTOSHADOWSPELL#Shadow Spell# +SC_SHADOWFORM#Shadow Formation# +SC_TRIANGLESHOT#Triangle Shot# +SC_BODYPAINT#Body Painting# +SC_INVISIBILITY#Invisibility# +SC_DEADLYINFECT#Deadly Infection# +SC_ENERVATION#Masquerade-Enervation# +SC_GROOMY#Masquerade-Gloomy# +SC_IGNORANCE#Masquerade-Ignorance# +SC_LAZINESS#Masquerade-Laziness# +SC_UNLUCKY#Masquerade-Unlucky# +SC_WEAKNESS#Masquerade-Weakness# +SC_STRIPACCESSARY#Divest Accessory # +SC_MANHOLE#Manhole # +SC_DIMENSIONDOOR#Dimensional Door# +SC_CHAOSPANIC#Chaos Panic # +SC_MAELSTROM#Maelstrom# +SC_BLOODYLUST#Bloody Lust # +SC_FEINTBOMB#Feint Bomb# +LG_CANNONSPEAR#Cannon Spear# +LG_BANISHINGPOINT#Vanishing Point# +LG_TRAMPLE#Trample# +LG_SHIELDPRESS#Shield Press# +LG_REFLECTDAMAGE#Reflect Damage# +LG_PINPOINTATTACK#Pinpoint Attack# +LG_FORCEOFVANGUARD#Vanguard Force# +LG_RAGEBURST#Burst Attack# +LG_SHIELDSPELL#Shield Spell# +LG_EXEEDBREAK#Exceed Break# +LG_OVERBRAND#Overbrand# +LG_PRESTIGE#Prestige# +LG_BANDING#Banding# +LG_MOONSLASHER#Moonslasher# +LG_RAYOFGENESIS#Genesis Ray# +LG_PIETY#Piety# +LG_EARTHDRIVE#Earth Drive# +LG_HESPERUSLIT#Hesperus Lit# +LG_INSPIRATION#Inspiration# +SR_DRAGONCOMBO#Dragon Combo# +SR_SKYNETBLOW#Sky Blow# +SR_EARTHSHAKER#Earth Shaker# +SR_FALLENEMPIRE#Fallen Empire# +SR_TIGERCANNON#Tiger Cannon# +SR_RAMPAGEBLASTER#Rampage Blast# +SR_CRESCENTELBOW#Crescent Elbow# +SR_CURSEDCIRCLE#Cursed Circle# +SR_LIGHTNINGWALK#Lightning Walk# +SR_KNUCKLEARROW#Knuckle Arrow# +SR_WINDMILL#Windmill# +SR_RAISINGDRAGON#Rising Dragon# +SR_ASSIMILATEPOWER#Power Absorb# +SR_POWERVELOCITY#Power Implantation# +SR_GATEOFHELL#Gates of Hell# +SR_GENTLETOUCH_QUIET#Gentle Touch-Silence# +SR_GENTLETOUCH_CURE#Gentle Touch-Cure# +SR_GENTLETOUCH_ENERGYGAIN#Gentle Touch-Energy Gain# +SR_GENTLETOUCH_CHANGE#Gentle Touch-Convert# +SR_GENTLETOUCH_REVITALIZE#Gentle Touch-Revitalize# +WA_SWING_DANCE#Swing Dance# +WA_SYMPHONY_OF_LOVER#Lover Symphony# +WA_MOONLIT_SERENADE#Moonlight Serenade# +MI_RUSH_WINDMILL#Windmill Rush# +MI_ECHOSONG#Echo Song# +MI_HARMONIZE#Harmonize# +WM_LESSON#Voice Lessons# +WM_METALICSOUND#Metalic Sound# +WM_REVERBERATION#Reverberation# +WM_SEVERE_RAINSTORM#Severe Rainstorm# +WM_POEMOFNETHERWORLD#Song of Despair# +WM_VOICEOFSIREN#Siren's Voice# +WM_DEADHILLHERE#Death Valley# +WM_LULLABY_DEEPSLEEP#Deep Sleep Lullaby# +WM_SIRCLEOFNATURE#Circle of Nature# +WM_RANDOMIZESPELL#Improvised Song# +WM_GLOOMYDAY#Gloomy Shyness# +WM_GREAT_ECHO#Great Echo# +WM_SONG_OF_MANA#Song Of Mana# +WM_DANCE_WITH_WUG#Dances with Wargs# +WM_SOUND_OF_DESTRUCTION#Song of Destruction# +WM_SATURDAY_NIGHT_FEVER#Saturday Night Fever# +WM_LERADS_DEW#Lerad's Dew# +WM_MELODYOFSINK#Sinking Melody# +WM_BEYOND_OF_WARCRY#Warcry from Beyond# +WM_UNLIMITED_HUMMING_VOICE#Infinite Humming# +SO_FIREWALK#Fire Walk# +SO_ELECTRICWALK#Electric Walk# +SO_SPELLFIST#Spell Fist# +SO_EARTHGRAVE#Earth Grave# +SO_DIAMONDDUST#Diamond Dust# +SO_POISON_BUSTER#Poison Burst# +SO_PSYCHIC_WAVE#Psychic Wave# +SO_CLOUD_KILL#Killing Cloud# +SO_STRIKING#Striking# +SO_WARMER#Warmer# +SO_VACUUM_EXTREME#Extreme Vacuum# +SO_VARETYR_SPEAR#Varetyr Spear# +SO_ARRULLO#Arrullo# +SO_EL_CONTROL#Spirit Control # +SO_SUMMON_AGNI#Call Agni# +SO_SUMMON_AQUA#Call Aqua# +SO_SUMMON_VENTUS#Call Ventus# +SO_SUMMON_TERA#Call Tera# +SO_EL_ACTION#Elemental Action# +SO_EL_ANALYSIS#Analyze Element# +SO_EL_SYMPATHY#Spirit Sympathy# +SO_EL_CURE#Spirit Cure# +SO_FIRE_INSIGNIA#Fire Insignia# +SO_WATER_INSIGNIA#Water Insignia# +SO_WIND_INSIGNIA#Wind Insignia# +SO_EARTH_INSIGNIA#Earth Insignia# +GN_TRAINING_SWORD#Sword Mastery# +GN_REMODELING_CART#Cart Remodeling# +GN_CART_TORNADO#Cart Tornado# +GN_CARTCANNON#Cart Cannon# +GN_CARTBOOST#Geneticist Cart Boost# +GN_THORNS_TRAP#Thorn Trap# +GN_BLOOD_SUCKER#Blood Sucker# +GN_SPORE_EXPLOSION#Spore Explosion# +GN_WALLOFTHORN#Thorn Wall# +GN_CRAZYWEED#Crazy Vines# +GN_DEMONIC_FIRE#Demonic Fire # +GN_FIRE_EXPANSION#Fire Expansion# +GN_HELLS_PLANT#Hell Plant# +GN_MANDRAGORA#Mandragora Howl# +GN_SLINGITEM#Item Sling# +GN_CHANGEMATERIAL#Change Material# +GN_MIX_COOKING#Mixed Cooking# +GN_MAKEBOMB#Bomb Creation# +GN_S_PHARMACY#Special Pharmacy # +AB_SECRAMENT#Sacrament# +SR_HOWLINGOFLION#Lion's Howl# +SR_RIDEINLIGHTNING#Lightning Ride# +RETURN_TO_ELDICASTES#To El Dicastes# +ALL_BUYING_STORE#Open Buying Store# +ALL_GUARDIAN_RECALL#Call of Guardian# +ALL_ODINS_POWER#Odin's Power# +MC_CARTDECORATE#Cart Decoration# +GM_ITEM_ATKMAX#Max Physical item attack rate# +GM_ITEM_ATKMIN#Minimize Physical item attack rate# +GM_ITEM_MATKMAX#Max Magic item attack rate# +GM_ITEM_MATKMIN#Minimize Magic item attack rate# +RL_RICHS_COIN#Rich's Coin (Fortune of the Rich)# +RL_MASS_SPIRAL#Mass Spiral (Absolute Penetration)# +RL_BANISHING_BUSTER#Vanishing Buster (Exile)# +RL_B_TRAP#Binding Trap (Dark Pit)# +RL_FLICKER#Flicker (Flashing Signal)# +RL_S_STORM#Shattering Storm (Grinding Storm)# +RL_E_CHAIN#Eternal Chain (Infinite Chain)# +RL_QD_SHOT#Quick Draw Shot (Tailwind Shot)# +RL_C_MARKER#Crimson Marker (Blood Brand)# +RL_FIREDANCE#Fire Dance (Dance of Massacre)# +RL_H_MINE#Howling Mine (Destructive Cry)# +RL_P_ALTER#Platinum Altar (White Gold Altar)# +RL_FALLEN_ANGEL#Fallen Angel (Nephilim)# +RL_R_TRIP#Round Trip (Circle Dance)# +RL_D_TAIL#Dragon Tail (Magic Beast Tail)# +RL_FIRE_RAIN#Fire Rain (Fire Deluge)# +RL_HEAT_BARREL#Hit Barrel (Acceleration Bullet)# +RL_AM_BLAST#Anti Material Blast (Obliterator)# +RL_SLUGSHOT#Slug Shot (Fundamental Destruction)# +RL_HAMMER_OF_GOD#God's Hammer (God's Wrath)# +SJ_LIGHTOFMOON#Lunar Luminance# +SJ_LUNARSTANCE#Lunar Stance# +SJ_FULLMOONKICK#Full Moon Kick# +SJ_LIGHTOFSTAR#Stellar Luminance# +SJ_STARSTANCE#Stellar Stance# +SJ_NEWMOONKICK#New Moon Kick# +SJ_FLASHKICK#Flash Kick# +SJ_STAREMPEROR#Star Emperor's Descent# +SJ_NOVAEXPLOSING#Nova Explosion# +SJ_UNIVERSESTANCE#Universal Stance# +SJ_FALLINGSTAR#Falling Stars# +SJ_GRAVITYCONTROL#Gravity Control# +SJ_BOOKOFDIMENSION#Book of Dimensions# +SJ_BOOKOFCREATINGSTAR#Star Creator's Book# +SJ_DOCUMENT#Solar, Lunar, and Stellar Record# +SJ_PURIFY#Solar, Lunar, and Stellar Purification# +SJ_LIGHTOFSUN#Solar Luminance# +SJ_SUNSTANCE#Solar Stance# +SJ_SOLARBURST#Solar Explosion# +SJ_PROMINENCEKICK#Blaze Kick# +SP_SOULGOLEM#Golem Soul# +SP_SOULSHADOW#Shadow Soul# +SP_SOULFALCON#Falcon Soul# +SP_SOULFAIRY#Fairy Soul# +SP_CURSEEXPLOSION#Curse Explosion# +SP_SOULCURSE#Evil Soul Curse# +SP_SPA#Espa# +SP_SHA#Esha# +SP_SWHOO#Eswhoo# +SP_SOULUNITY#Soul Bind# +SP_SOULDIVISION#Soul Division# +SP_SOULREAPER#Soul Harvest# +SP_SOULREVOLVE#Soul Circulation# +SP_SOULCOLLECT#Soul Collection# +SP_SOULEXPLOSION#Soul Explosion# +SP_SOULENERGY#Soul Energy Research# +SP_KAUTE#Kaute# +KO_YAMIKUMO#Shadow Hiding# +KO_RIGHT#Righthand Mastery# +KO_LEFT#Lefthand Mastery# +KO_JYUMONJIKIRI#Cross Slash# +KO_SETSUDAN#Soul Cutter# +KO_BAKURETSU#Kunai Explosion# +KO_HAPPOKUNAI#Kunai Splash# +KO_MUCHANAGE#Rapid Throw# +KO_HUUMARANKA#Swirling Petal# +KO_MAKIBISHI#Makibishi (Caltrop Scatter)# +KO_MEIKYOUSISUI#Pure Soul # +KO_ZANZOU#Illusion - Shadow# +KO_KYOUGAKU#Illusion - Shock# +KO_JYUSATSU#Illusion - Death# +KO_KAHU_ENTEN#Fire Charm# +KO_HYOUHU_HUBUKI#Ice Charm# +KO_KAZEHU_SEIRAN#Wind Charm# +KO_DOHU_KOUKAI#Earth Charm# +KO_KAIHOU#Release Ninja Spell# +KO_ZENKAI#Cast Ninja Spell# +KO_GENWAKU#Illusion - Bewitch# +KO_IZAYOI#16th Night # +KG_KAGEHUMI#Shadow Trampling# +KG_KYOMU#Empty Shadow# +KG_KAGEMUSYA#Shadow Warrior# +OB_ZANGETSU#Distorted Crescent# +OB_OBOROGENSOU#Moonlight Fantasy# +OB_AKAITSUKI#Ominous Moonlight# +ECL_SNOWFLIP#Snow Flip# +ECL_PEONYMAMY#Peonymamy# +ECL_SADAGUI#Sadagui# +ECL_SEQUOIADUST#Sequoia Dust# +ECLAGE_RECALL#Move to Eclage# +ALL_NIFLHEIM_RECALL#The World of the Dead!# +ALL_PRONTERA_RECALL#Prontera Recall# +ALL_GLASTHEIM_RECALL#Glast Heim Return# +GC_DARKCROW#Dark Claw# +RA_UNLIMIT#No Limits# +GN_ILLUSIONDOPING#Hallucination Drug# +RK_DRAGONBREATH_WATER#Dragon Water Breath# +NC_MAGMA_ERUPTION#Lava Flow# +WM_FRIGG_SONG#Frigg's Song# +SO_ELEMENTAL_SHIELD#Elemental Shield# +SR_FLASHCOMBO#Flash Combo# +SC_ESCAPE#Urgent Escape# +AB_OFFERTORIUM#Offertorium# +WL_TELEKINESIS_INTENSE#Intensification# +LG_KINGS_GRACE#King's Grace# +ALL_FULL_THROTTLE#Full Throttle# +SU_BASIC_SKILL#New Basic Skill# +SU_BITE#Bite# +SU_HIDE#Hide# +SU_SCRATCH#Scratch# +SU_STOOP#Stoop# +SU_LOPE#Lope# +SU_SPRITEMABLE#Sprite Marble# +SU_POWEROFLAND#Power of Land# +SU_SV_STEMSPEAR#Silvervine Stem Spear# +SU_CN_POWDERING#Catnip Powdering# +SU_CN_METEOR#Catnip Meteor# +SU_SV_ROOTTWIST#Silvervine Root Twist# +SU_POWEROFLIFE#Power of Life# +SU_SCAROFTAROU#Scar of Tarou# +SU_PICKYPECK#Picky Peck# +SU_ARCLOUSEDASH#Arclouse Dash# +SU_LUNATICCARROTBEAT#Lunatic Carrot Beat# +SU_POWEROFSEA#Power of Sea# +SU_TUNABELLY#Tuna Belly# +SU_TUNAPARTY#Tuna Party# +SU_BUNCHOFSHRIMP#Bunch of Shrimp# +SU_FRESHSHRIMP#Fresh Shrimp# +SU_SOULATTACK#Soul Attack# +SU_POWEROFFLOCK#Power of Flock# +SU_SVG_SPIRIT#Spirit of Savage# +SU_HISS#Hiss# +SU_NYANGGRASS#Nyang Grass# +SU_GROOMING#Grooming# +SU_PURRING#Purring# +SU_SHRIMPARTY#Tasty Shrimp Party# +SU_SPIRITOFLIFE#Spirit of Life# +SU_MEOWMEOW#Meow Meow# +SU_SPIRITOFLAND#Spirit of Land# +SU_CHATTERING#Chattering# +SU_SPIRITOFSEA#Spirit of Sea# +WE_CALLALLFAMILY#Let's Go Family!.# +WE_ONEFOREVER#Love Conquers Death.# +WE_CHEERUP#Go Parents Go!# +CG_SPECIALSINGER#Skilled Special Singer# +AB_VITUPERATUM#VITUPERATUM / Criticism# +AB_CONVENIO#CONVENIO / Gather# +NV_BREAKTHROUGH#Breakthrough# +NV_HELPANGEL#Help, Angel!# +NV_TRANSCENDENCE#Transcendence# +HLIF_HEAL#Healing Hands# +HLIF_AVOID#Urgent Escape# +HLIF_BRAIN#Brain Surgery# +HLIF_CHANGE#Mental Charge# +HAMI_CASTLE#Castling# +HAMI_DEFENCE#Amistr Bulwark# +HAMI_SKIN#Adamantium Skin# +HAMI_BLOODLUST#Blood Lust# +HFLI_MOON#Moonlight# +HFLI_FLEET#Flitting# +HFLI_SPEED#Accelerated Flight# +HFLI_SBR44#S.B.R.44# +HVAN_CAPRICE#Caprice# +HVAN_CHAOTIC#Chaotic Blessings# +HVAN_INSTRUCT#Instruction Change# +HVAN_EXPLOSION#Self-Destruction# +MH_SUMMON_LEGION#Summon Legion# +MH_NEEDLE_OF_PARALYZE#Needle Of Paralyze# +MH_POISON_MIST#Poison Mist# +MH_PAIN_KILLER#Pain Killer# +MH_LIGHT_OF_REGENE#Light Of Regenerate# +MH_OVERED_BOOST#Over Boost# +MH_ERASER_CUTTER#Eraser Cutter# +MH_XENO_SLASHER#Xeno Slasher# +MH_SILENT_BREEZE#Silent Breeze# +MH_STYLE_CHANGE#Style Change# +MH_SONIC_CRAW#Sonic_Claw# +MH_SILVERVEIN_RUSH#Silvervein Rush# +MH_MIDNIGHT_FRENZY#Midnight Frenzy# +MH_STAHL_HORN#Stahl Horn# +MH_GOLDENE_FERSE#Goldene Ferse# +MH_STEINWAND#Stein Wand# +MH_HEILIGE_STANGE#Heilige Stange# +MH_ANGRIFFS_MODUS#Angriffs_Modus# +MH_TINDER_BREAKER#Tinder Breaker# +MH_CBC#C.B.C (Continual Break Combo)# +MH_EQC#E.Q.C (Eternal Quick Combo)# +MH_MAGMA_FLOW#Magma Flow# +MH_GRANITIC_ARMOR#Granitic Armor# +MH_LAVA_SLIDE#Lava Slide# +MH_PYROCLASTIC#Pyroclastic# +MH_VOLCANIC_ASH#Volcanic Ash# +MS_BASH#Bash# +MS_MAGNUM#Magnum Break# +MS_BOWLINGBASH#Bowling Bash# +MS_PARRYING# # +MS_REFLECTSHIELD#Shield Reflect# +MS_BERSERK#Frenzy# +MA_DOUBLE#Double Strafe# +MA_SHOWER#Arrow Shower# +MA_SKIDTRAP#Skid Trap# +MA_LANDMINE#Land Mine# +MA_SANDMAN#Sandman# +MA_FREEZINGTRAP#Freezing Trap# +MA_REMOVETRAP#Remove Trap# +MA_CHARGEARROW#Arrow Repel# +MA_SHARPSHOOTING#Focused Arrow Strike# +ML_PIERCE#Pierce# +ML_BRANDISH#Brandish Spear# +ML_SPIRALPIERCE#Clashing Spiral# +ML_DEFENDER#Defending Aura# +ML_AUTOGUARD#Guard# +ML_DEVOTION#Sacrifice# +MER_MAGNIFICAT#Magnificat# +MER_QUICKEN#Weapon Quicken# +MER_SIGHT#Sight# +MER_CRASH#Crash# +MER_REGAIN#Regain# +MER_TENDER#Tender# +MER_BENEDICTION#Benediction# +MER_RECUPERATE#Recuperate# +MER_MENTALCURE#Mental Cure# +MER_COMPRESS#Compress# +MER_PROVOKE#Provoke# +MER_AUTOBERSERK#Berserk# +MER_DECAGI#Decrease AGI# +MER_SCAPEGOAT#Scapegoat# +MER_LEXDIVINA#Lex Divina# +MER_ESTIMATION#Sense# +MER_INVINCIBLEOFF2#Mind Blaster# +GD_APPROVAL#Official Guild Approval# +GD_KAFRACONTRACT#Contract With Kafra# +GD_GUARDRESEARCH#Guardian Research# +GD_GUARDUP#Strengthen Guardians# +GD_EXTENSION#Guild Extension# +GD_LEADERSHIP#Skill Form: ^000099Passive^000000# +GD_GLORYWOUNDS#Glorious Wounds# +GD_SOULCOLD#Cold Heart# +GD_HAWKEYES#Sharp Gaze# +GD_BATTLEORDER#Battle Command# +GD_REGENERATION#Regeneration# +GD_RESTORE#Restoration# +GD_EMERGENCYCALL#Urgent Call# +GD_DEVELOPMENT#Permanent Development# +GD_GUILD_STORAGE#Guild Storage Extension# diff --git a/openkore_llm_knowledge/tables/itemtypes.txt b/openkore_llm_knowledge/tables/itemtypes.txt new file mode 100644 index 0000000000..6ba744f6ed --- /dev/null +++ b/openkore_llm_knowledge/tables/itemtypes.txt @@ -0,0 +1,21 @@ +0 Usable Heal +1 Usable Status +2 Usable Special +3 Event +4 Armor +5 Weapon +6 Card +7 Quest +8 Pet Armor +9 2-Hand Weapon +10 Arrow +11 Helmet +12 Unknown_Type_12 +13 Mask +14 Headgear +15 Gun +16 Ammo +17 Shuriken +18 Cash Item +19 Cannonball +20 Costume diff --git a/openkore_llm_knowledge/tables/kRO/items.txt b/openkore_llm_knowledge/tables/kRO/items.txt new file mode 100755 index 0000000000..8aa4115866 --- /dev/null +++ b/openkore_llm_knowledge/tables/kRO/items.txt @@ -0,0 +1,8415 @@ +2101#가드# +2102#가드# +2103#버클러# +2104#버클러# +2105#쉴드# +2106#쉴드# +2107#미러_쉴드# +2108#미러_쉴드# +2201#선글래스# +2202#선글래스# +2203#글래스# +2204#글래스# +2205#물안경# +2206#면사포# +2207#장식용_꽃# +2208#리본# +2209#리본# +2210#머리띠# +2211#두건# +2212#안대# +2213#고양이_머리띠# +2214#토끼_머리띠# +2215#꽃_머리띠# +2216#비레타# +2217#비레타# +2218#마스크# +2219#마스크# +2220#햇# +2221#햇# +2222#둥근_모자# +2223#둥근_모자# +2226#캡# +2227#캡# +2228#헬름# +2229#헬름# +2230#쥬얼_헬름# +2231#쥬얼_헬름# +2232#서클릿# +2233#서클릿# +2234#티아라# +2235#크라운# +2236#산타_모자# +2237#턱수염# +2238#콧수염# +2239#외눈_안경# +2240#수염# +2241#흰수염# +2242#고급_선글래스# +2243#스핀_글래스# +2244#왕_리본# +2245#스위트_젠틀# +2246#골든_헤드기어# +2247#올드스터_로맨스# +2248#웨스턴_그레이스# +2249#코로넷# +2250#머리끈# +2251#성직자의_모자# +2252#위저드햇# +2253#장식용_해바라기# +2254#천사의_머리띠# +2255#악마의_머리띠# +2256#마제스틱_고우트# +2257#새하얀_뿔# +2258#샤프_헤드기어# +2259#프로펠라# +2260#미니_글래스# +2261#프론테라_군모# +2262#광대코# +2263#의적_안대# +2265#시위용_마스크# +2266#아이언_케인# +2267#담배# +2268#파이프담배# +2269#꽃잎# +2270#풀잎# +2271#머리_안경# +2272#정지_표지판# +2273#의사_머리띠# +2274#망자의_머리띠# +2275#반다나# +2276#도끼눈# +2277#간호모# +2279#심지# +2280#삿갓# +2282#영혼고리# +2283#귀마개# +2284#사슴뿔# +2285#명사수의_사과# +2286#요정의_귀# +2287#해적_두건# +2289#거시기# +2290#장례건# +2291#나비_가면# +2293#연극_소도구# +2294#스타_더스트# +2295#눈가리개# +2296#망원경# +2298#초록_더듬이# +2299#오크족_헬름# +2301#코튼셔츠# +2302#코튼셔츠# +2303#가죽재킷# +2304#가죽재킷# +2305#어드밴쳐러_슈츠# +2306#어드밴쳐러_슈츠# +2307#맨틀# +2308#맨틀# +2309#롱_코트# +2310#롱_코트# +2311#밍크_코트# +2312#아머# +2313#아머# +2314#메일# +2315#메일# +2316#플레이트# +2317#플레이트# +2318#로드_클로스# +2319#글리터링_클로스# +2320#포멀_드레스# +2321#실크_로브# +2322#실크_로브# +2323#스캐퓰러# +2324#스캐퓰러# +2325#세인트_로브# +2326#세인트_로브# +2327#홀리_로브# +2328#우든_메일# +2329#우든_메일# +2330#타이즈# +2331#타이즈# +2332#실버_로브# +2333#실버_로브# +2334#매직코트# +2335#씨프_클로스# +2336#씨프_클로스# +2337#닌자_슈츠# +2338#웨딩드레스# +2339#삼각팬티# +2401#샌들# +2402#샌들# +2403#슈즈# +2404#슈즈# +2405#부츠# +2406#부츠# +2407#유리구두# +2408#족쇄# +2409#뾰족구두# +2501#후드# +2502#후드# +2503#머플러# +2504#머플러# +2505#망토# +2506#망토# +2507#옛영주의_망토# +2508#누더기_망토# +2601#링# +2602#이어링# +2603#네클리스# +2604#글러브# +2605#브로치# +2607#클립# +2608#로자리# +2609#해골반지# +2610#금반지# +2611#은반지# +2612#꽃반지# +2613#다이아반지# +2614#듀라한_아이# +2615#세이프티_링# +2616#크리티컬_링# +2617#신관의_장갑# +2618#마타의_목걸이# +2619#활골무# +2620#도적의_반지# +5001#헤드폰# +5002#쥬얼_크라운# +5003#광대모자# +5004#산소마스크# +5006#하트파운데이션# +5007#로드_서클릿# +5008#풋사랑# +5009#안전모# +5010#원주민_머리띠# +5011#안테나# +5012#학사모# +5013#로드카호의_뿔# +5014#아가미의_헬름# +5015#장식용_알껍질# +5016#학생모# +5017#본헬름# +5018#깃털모자# +5019#커세어# +2278#스마일# +2281#오페라_가면# +2288#스크래치_마스크# +2292#용접_마스크# +2297#고블린족_가면# +5005#가스_마스크# +2224#고글# +2225#고글# +2264#무낙모자# +1750#화살# +1751#은화살# +1752#불화살# +1116#카타나# +1117#카타나# +1118#카타나# +1151#슬레이어# +1152#슬레이어# +1153#슬레이어# +1154#바스타드소드# +1155#바스타드소드# +1156#바스타드소드# +1157#투핸드소드# +1158#투핸드소드# +1159#투핸드소드# +1160#브로드소드# +1162#브로드소드# +1161#발뭉# +1163#크레이모어# +1164#무라마사# +1165#마사무네# +1166#드래곤슬레이어# +1167#슈바이체르샤벨# +1168#즈바이핸더# +1169#액서큐셔너# +1170#카츠발게르# +1351#배틀액스# +1352#배틀액스# +1353#배틀액스# +1354#해머# +1355#해머# +1356#해머# +1357#버스터# +1358#버스터# +1359#버스터# +1360#투핸드액스# +1361#투핸드액스# +1362#투핸드액스# +1363#블러드액스# +1364#그레이트액스# +1365#새버스# +1366#라이트_엡실론# +1367#슬러터# +1368#토마호크# +1369#길로틴# +1451#귀삼# +1452#귀삼# +1453#귀삼# +1454#글레이브# +1455#글레이브# +1456#글레이브# +1457#파티잔# +1458#파티잔# +1459#파티잔# +1460#트라이던트# +1461#트라이던트# +1462#트라이던트# +1463#핼버드# +1464#핼버드# +1465#핼버드# +1466#크레센트_사이더# +1410#랜스# +1411#랜스# +1412#랜스# +1467#빌귀삼# +1468#제퓨로스# +1469#롱기누스의_창# +1470#브류나크# +1471#헬파이어# +1250#쥬르# +1251#쥬르# +1252#카타르# +1253#카타르# +1254#자마다르# +1255#자마다르# +1701#보우# +1702#보우# +1703#보우# +1715#알바레스트# +1704#컴포지트보우# +1705#컴포지트보우# +1706#컴포지트보우# +1716#각궁# +1707#그레이트보우# +1708#그레이트보우# +1709#그레이트보우# +1718#헌터보우# +1710#크로스보우# +1711#크로스보우# +1712#크로스보우# +1719#도둑의_활# +1713#알바레스트# +1714#각궁# +1720#루드라의_활# +701#오라오라# +702#가축의_피# +703#히나레# +704#알로에# +705#클로버# +706#네잎_클로버# +707#노래하는_풀# +708#멘트# +709#이지도르# +710#환상의_꽃# +711#새싹# +712#꽃# +713#빈병# +714#엠펠리움# +715#옐로우_젬스톤# +716#레드_젬스톤# +717#블루_젬스톤# +718#가넷# +719#아메디스트# +720#아쿠아마린# +721#에메랄드# +722#진주# +723#루비# +724#저주받은_루비# +725#사드오닉스# +726#사파이어# +727#오팔# +728#토파즈# +729#질콘# +730#다이아몬드_1캐럿# +731#다이아몬드_2캐럿# +732#다이아몬드_3캐럿# +733#손상된_다이아몬드# +734#붉은색_액자# +735#푸른색_도자기# +736#하얀색_접시# +737#검은색_국자# +738#연필꽂이# +739#화장품# +740#봉제인형# +741#포링인형# +742#촌촌인형# +743#스포아인형# +744#꽃다발# +745#부케# +746#유리구슬# +747#수정으로_만든_거울# +748#시들지_않는_장미# +749#얼음_장미# +750#바포메트인형# +751#오시리스인형# +752#메뚜기인형# +753#원숭이인형# +754#너구리인형# +756#오리데오콘_원석# +757#에르늄_원석# +901#댕기머리# +902#늙은나무의_뿌리# +903#파충류의_혓바닥# +904#전갈의_꼬리# +905#식물의_줄기# +906#비늘로된_줄기# +907#고목나무의_진# +908#개구리의_알# +909#젤로피# +910#가렛# +911#셀# +912#자르곤# +913#박쥐의_이빨# +914#솜털# +915#번데기_껍질# +916#새의_깃털# +917#새의_발톱# +918#끈적이는_물갈퀴# +919#동물의_가죽# +920#늑대의_발톱# +921#버섯_포자# +922#오크의_송곳니# +923#악마의_뿔# +924#나비의_날개가루# +925#새의_부리# +926#뱀의_비늘조각# +928#벌레의_더듬이# +929#멈추지_않는_심장# +930#썩은_붕대# +931#오크전사의_증표# +932#뼈조각# +934#죽은자의_유품# +935#단단한_껍질# +936#비늘로된_피부# +937#독_송곳니# +938#끈적끈적한_액체# +939#벌의_뒷침# +940#메뚜기의_뒷다리# +941#쇠코뚜레# +942#원숭이의_꼬리# +943#매우_단단한_껍질# +944#말발굽# +945#너구리의_나뭇잎# +946#달팽이의_껍질# +947#딱딱한_뿔# +948#곰의_발바닥# +949#부드러운_털# +950#인어의_심장# +951#등_지느러미# +952#선인장의_뾰족한_침# +953#돌심장# +954#빛나는_비늘# +955#벌레의_등껍질# +956#가시달린_아가미# +957#죽은자의_손톱# +958#죽은자의_이빨# +959#썩어버린_비늘# +960#집게발# +961#소라# +962#촉수# +963#날카로운_비늘# +964#게등껍질# +965#조개껍질# +966#조개살# +967#거북이_등껍질# +968#오크용자의_증표# +969#황금# +970#알코올# +971#디트리민# +972#카르보딜# +973#중화제# +974#혼합제# +975#빨간색_염료# +976#노란색_염료# +978#파란색_염료# +979#초록색_염료# +980#주홍색_염료# +981#보라색_염료# +982#하얀색_염료# +983#검은색_염료# +984#오리데오콘# +985#에르늄# +986#모루# +987#오리데오콘_모루# +988#황금의_모루# +989#엠펠리움_모루# +990#레드_블러드# +991#크리스탈_블루# +992#윈드_오브_버듀어# +993#그린_라이브# +994#프레임_하트# +995#미스틱_프로즌# +996#러프_윈드# +997#그레이트_네이쳐# +998#철# +999#강철# +1000#별의_조각# +1001#별의_가루# +1002#철광석# +1003#석탄# +1004#충절의_증거# +1005#대장장이의_망치# +1006#오래된_마법서# +1007#혜안# +1008#비정한_마음# +1009#성흔# +1010#프라콘# +1011#엠베르타콘# +1012#도마뱀의_목덜미# +1013#알록달록한_등껍질# +1014#개미의_턱뼈# +1015#길고_가느다란_혀# +1016#쥐의_꼬리# +1017#두더지의_수염# +1018#두더지의_손톱# +1019#나무_조각# +1020#긴_머리카락# +1021#도깨비의_뿔# +1022#여우의_꼬리# +1023#생선의_꼬리# +1024#먹물# +1025#거미줄# +1026#도토리# +1027#고슴도치의_가시# +1028#멧돼지의_갈기털# +1029#호랑이의_가죽# +1030#호랑이의_발바닥# +1031#사마귀의_팔# +1032#식인수의_꽃# +1033#식인수의_뿌리# +1034#코볼트의_털# +1035#드래곤의_이빨# +1036#드래곤의_비늘# +1037#드래곤의_꼬리# +1038#새끼악마의_뿔# +1039#새끼악마의_날개# +1040#늙은요정의_수염# +1041#랜턴# +1042#짧은_다리# +1043#오크의_손톱# +1044#제노크의_이빨# +1045#신관의_가면# +1046#전갈의_집게손# +1047#메두사의_머리# +1048#뱀모양의_머리카락# +1049#처녀의_옷자락# +1050#힘줄# +1051#뇌관# +1052#단세포# +1053#고대어의_이빨# +1054#고대어의_입술# +1055#지렁이의_가죽# +1056#모래_조각# +1057#나방의_날개가루# +1058#나방의_날개# +1059#투명한_천조각# +1060#금빛_머리카락# +1061#마녀의_별모래# +1062#호박_머리# +1063#날카로운_송곳니# +1064#말고삐# +1065#설치용_트랩# +1066#결이_고운_나무_조각# +1067#단단한_나무_조각# +1068#메마른_나무_조각# +1069#주홍털_그물버섯# +1070#주홍털_끈적버섯# +1071#검은액체_시험관# +1072#만수의_편지# +1073#배달_영수증# +1074#배달_영수증# +1075#배달_영수증# +1076#배달_영수증# +1077#배달_영수증# +1078#배달_영수증# +1079#배달_영수증# +1080#배달_영수증# +1081#배달용_나무상자# +1082#배달용_나무상자# +1083#배달용_나무상자# +1084#카프라_이용권# +1085#검은액체_시험관# +1086#검은액체_시험관# +1087#검은액체_시험관# +1088#모로크_수용액# +1089#페이욘_수용액# +1090#검은액체_시험관# +1091#배달용_나무상자# +1092#빈시험관# +1093#빈포션병# +501#빨간포션# +502#주홍포션# +503#노란포션# +504#하얀포션# +505#파란포션# +506#초록포션# +507#빨간허브# +508#노란허브# +509#하얀허브# +510#파란허브# +511#초록허브# +512#사과# +513#바나나# +514#포도# +515#당근# +516#고구마# +517#고기# +518#꿀# +519#우유# +520#히나레의_잎사귀# +521#알로에의_잎사귀# +522#마스테라의_열매# +523#성수# +525#만능약# +526#로얄제리# +528#괴물의_먹이# +529#사탕# +530#막대사탕# +531#사과_쥬스# +532#바나나_쥬스# +533#포도_쥬스# +534#당근_쥬스# +535#호박# +536#아이스_크림# +601#파리의_날개# +602#나비의_날개# +603#오래된_파란상자# +604#고목나무_가지# +605#안티_페인멘트# +606#알로에베라# +607#이그드라실의_열매# +608#이그드라실의_씨앗# +609#반혼부# +610#이그드라실의_나뭇잎# +611#돋보기# +612#휴대용_용광로# +613#강철의_망치# +614#황금의_망치# +615#오리데오콘_망치# +1101#소드# +1102#소드# +1103#소드# +1104#폴쳔# +1105#폴쳔# +1106#폴쳔# +1107#블레이드# +1108#블레이드# +1109#블레이드# +1110#레이피어# +1111#레이피어# +1112#레이피어# +1113#시미터# +1114#시미터# +1115#시미터# +1119#츠루기# +1120#츠루기# +1121#츠루기# +1122#환두태도# +1123#해동검# +1124#오키쉬_소드# +1125#환두태도# +1126#세이버# +1127#세이버# +1128#해동검# +1129#프람베르그# +1130#네이건# +1131#아이스_팔시온# +1132#엣지# +1133#화이어_브랜드# +1134#시저즈소드# +1135#커틀러스# +1136#태양검# +1137#엑스칼리버# +1138#미스틸테인# +1139#테일핑# +1140#별운검# +1141#무형검# +1201#나이프# +1202#나이프# +1203#나이프# +1204#커터# +1205#커터# +1206#커터# +1207#망고슈# +1208#망고슈# +1209#망고슈# +1210#더크# +1211#더크# +1212#더크# +1213#대거# +1214#대거# +1215#대거# +1216#스틸레토# +1217#스틸레토# +1218#스틸레토# +1219#그라디우스# +1220#그라디우스# +1221#그라디우스# +1222#다마스커스# +1223#포츈소드# +1224#소드_브레이커# +1225#메일_브레이커# +1226#다마스커스# +1227#위더나이프# +1228#컴뱃나이프# +1229#식칼# +1230#송곳# +1231#바제랄드# +1232#어새신대거# +1233#액서사이즈# +1234#월광검# +1235#아조스# +1236#수크사마드# +1237#그림투스# +1301#액스# +1302#액스# +1303#액스# +1304#오키쉬_액스# +1305#클리버# +1401#쟈벨린# +1402#쟈벨린# +1403#쟈벨린# +1404#스피어# +1405#스피어# +1406#스피어# +1407#파이크# +1408#파이크# +1409#파이크# +1413#궁그닐# +1414#겔러드리아# +1415#꼬챙이# +1416#튱클레티# +1501#클럽# +1502#클럽# +1503#클럽# +1504#메이스# +1505#메이스# +1506#메이스# +1507#스매셔# +1508#스매셔# +1509#스매셔# +1510#플레일# +1511#플레일# +1512#플레일# +1513#모닝스타# +1514#모닝스타# +1515#모닝스타# +1516#소드_메이스# +1517#소드_메이스# +1518#소드_메이스# +1519#체인# +1520#체인# +1521#체인# +1522#스터너# +1523#스파이크# +1524#골든_메이스# +1525#롱_메이스# +1526#슬래쉬# +1527#쿼드릴# +1528#그랜드_크로스# +1550#북# +1551#바이블# +1552#타블렛# +1601#롯드# +1602#롯드# +1603#롯드# +1604#완드# +1605#완드# +1606#완드# +1607#스태프# +1608#스태프# +1609#스태프# +1610#아크완드# +1611#아크완드# +1612#아크완드# +1613#마이트스태프# +1614#신관의_지팡이# +1615#해골_지팡이# +1801#바그낙# +1802#바그낙# +1803#너클더스터# +1804#너클더스터# +1805#호라# +1806#호라# +1807#피스트# +1808#피스트# +1809#크로# +1810#크로# +1811#핑거# +1812#핑거# +1813#카이저너클# +1814#베르세르크# +1901#바이올린# +1902#바이올린# +1903#만돌린# +1904#만돌린# +1905#류트# +1906#류트# +1907#기타# +1908#기타# +1909#하프# +1910#하프# +1911#거문고# +1912#거문고# +1950#로프# +1951#로프# +1952#라인# +1953#라인# +1954#와이어# +1955#와이어# +1956#란테# +1957#란테# +1958#테일# +1959#테일# +1960#휩# +1961#휩# +1962#라리어트# +1963#랩쳐로즈# +1964#슈메티# +4001#포링_카드# +4002#파브르_카드# +4003#푸파_카드# +4004#드롭프스_카드# +4005#산타포링_카드# +4006#루나틱_카드# +4007#페코페코_알_카드# +4008#픽키_카드# +4009#촌촌_카드# +4010#윌로우_카드# +4011#픽키_카드# +4012#도둑벌레_알_카드# +4013#앙드레_알_카드# +4014#로다프로그_카드# +4015#콘도르_카드# +4016#도둑벌레_카드# +4017#세비지_베베_카드# +4018#앙드레_유충_카드# +4019#호넷_카드# +4020#퍼밀리어_카드# +4021#로커_카드# +4022#스포아_카드# +4023#데저트울프_새끼_카드# +4024#플랑크톤_카드# +4025#스켈레톤_카드# +4026#암컷_도둑벌레_카드# +4027#쿠크레_카드# +4028#타로우_카드# +4029#울프_카드# +4030#만드라고라_카드# +4031#페코페코_카드# +4032#앰버나이트_카드# +4033#포포링_카드# +4034#웜테일_카드# +4035#히드라_카드# +4036#무카_카드# +4037#스네이크_카드# +4038#좀비_카드# +4039#스타이너_카드# +4040#크리미_카드# +4041#코코_카드# +4042#스틸촌촌_카드# +4043#앙드레_카드# +4044#스모키_카드# +4045#호른_카드# +4046#마르틴_카드# +4047#고스트링_카드# +4048#포이즌_스포아_카드# +4049#바돈_카드# +4050#수컷_도둑벌레_카드# +4051#요요_카드# +4052#엘더윌로우_카드# +4053#비타타_카드# +4054#엔젤링_카드# +4055#마리나_카드# +4056#더스티네스_카드# +4057#메틀러_카드# +4058#타라프로그_카드# +4059#병정앙드레_카드# +4060#고블린_카드# +4061#커너투스_카드# +4062#아나콘다크_카드# +4063#카라멜_카드# +4064#제롬_카드# +4065#카호_카드# +4066#오크워리어_카드# +4067#메갈로돈_카드# +4068#스콜피온_카드# +4069#드레인리어_카드# +4070#에기라_카드# +4071#오크좀비_카드# +4072#고렘_카드# +4073#파이어럿_스켈_카드# +4074#빅풋_카드# +4075#아르고스_카드# +4076#백련옥_카드# +4077#휀_카드# +4078#세비지_카드# +4079#맨티스_카드# +4080#플로라_카드# +4081#호드_카드# +4082#데저트울프_카드# +4083#라플레시아_카드# +4084#마린스피어_카드# +4085#오크_스켈레톤_카드# +4086#솔져_스켈레톤_카드# +4087#가이아스_카드# +4088#프릴도라_카드# +4089#소드피쉬_카드# +4090#무낙_카드# +4091#코볼트_카드# +4092#스켈워커_카드# +4093#오본느_카드# +4094#아쳐스켈레톤_카드# +4095#마르스_카드# +4096#제노크_카드# +4097#마타_카드# +4098#도깨비_카드# +4099#파사나_카드# +4100#소희_카드# +4101#샌드맨_카드# +4102#위스퍼_카드# +4103#호롱_카드# +4104#레퀴엠_카드# +4105#마르크_카드# +4106#미이라_카드# +4107#베릿트_카드# +4108#미스트_카드# +4109#잭_카드# +4110#구울_카드# +4111#스트라우프_카드# +4112#마르두크_카드# +4113#마리오네트_카드# +4114#아르지오프_카드# +4115#헌터플라이_카드# +4116#이시스_카드# +4117#사이드_와인더_카드# +4118#쁘띠_카드# +4119#바소리_카드# +4120#쁘띠_카드# +4121#프리오니_카드# +4122#데비루치_카드# +4123#에드가_카드# +4124#메두사_카드# +4125#데비어스_카드# +4126#마이너우로스_카드# +4127#나이트메어_카드# +4128#황금_도둑벌레_카드# +4129#바포메트_주니어_카드# +4130#스콜피언_킹_카드# +4131#월야화_카드# +4132#미스트레스_카드# +4133#레이드릭_카드# +4134#드라큐라_카드# +4135#오크로드_카드# +4136#칼리츠버그_카드# +4137#드레이크_카드# +4138#아누비스_카드# +4139#조커_카드# +4140#심연의기사_카드# +4141#드루이드_카드# +4142#도플갱어_카드# +4143#오크히어로_카드# +4144#오시리스_카드# +4145#베르제브브_카드# +4146#마야_카드# +4147#바포메트_카드# +4148#파라오_카드# + +//---- Episode 1.5 용 추가 아이템 ------ + + +1753#강철의_화살# +1754#수정_화살# +1755#바람의_화살# +1756#암석_화살# +1094#짧은_댕기머리# +1095#시계바늘# +1096#둥근_껍질# +1097#낡은_책장# +1098#수갑# +1099#낡은_죄수복# +7001#곰팡이_가루# +7002#오우거의_이빨# +7003#아놀리안의_피부# +7004#진흙_덩어리# +7005#해골_바가지# +7006#붉은_박쥐의_날개# +7007#큰쥐의_발톱# +7008#매우_딱딱한_뿔# +7009#화려한_껍질# +7010#강철같은_전갈의_꼬리# +7011#원숭이의_손톱# +7012#질긴_비늘_줄기# +7013#산호초# +7014#낡은_자화상# +7015#추억의_책갈피# +7016#낡은_숟가락# +7017#집행인의_장갑# +7018#어린_나뭇가지# +7019#로키의_속삭임# +7020#어머니의_악몽# +7021#장님의_어리석음# +7022#낡은_손잡이# +7023#어둠에_잠긴_칼날# +7024#피로_물든_칼날# +7025#마왕의_눈물# +7026#시계탑의_열쇠# +7027#시계탑지하의_열쇠# +616#낡은_카드첩# +617#오래된_보라색_상자# +618#낡은_두루마리# +1553#거친_파도의_서# +1554#갈라진_대지의_서# +1555#타오르는_태양의_서# +1556#메마른_바람의_서# +1557#묵시록# +1558#소녀의_일기장# + + +//---- 라게 PVP 대회용 추가 아이템 ------ + + +7028#대전의_증명# +7029#대전_관람권# + + +// ---- EP 2.0 내친구 몬스터 추가 아이템 ----------- + +2621#링# +2622#이어링# +2623#네클리스# +2624#글러브# +2625#브로치# +2626#로자리# +2627#벨트# +2628#노비스_암릿# +1256#시린_고드름의_카타르# +1257#먼지낀_가시_덤불_카타르# +1258#분출하는_불꽃의_카타르# +1259#살을_에는_바람의_카타르# +1260#날카롭게_깎은_구울의_다리뼈# +1261#등_뒤를_베는자# +7030#사막_늑대의_발톱# +7031#헌_후라이펜# +7032#달걀껍질_조각# +7033#독버섯_포자# +619#덜익은_사과# +620#오렌지_쥬스# +621#쓰디쓴_풀# +622#무지개색_당근# +623#멋쟁이_청년_지렁이# +624#썩은_생선# +625#녹슬어버린_철# +626#몬스터_쥬스# +627#달콤한_우유# +628#잘_말린_뼈다귀# +629#노래하는_꽃# +630#아침이슬을_머금은_이끼# +631#맹독초# +632#토실토실_살찐_지렁이# +633#구운_고구마# +634#열대의_바나나# +635#조직의_쓴맛# +636#수취인_불명# +637#헌_빗자루# +638#순결의_은장도# +639#복종의_팔찌# +640#반짝이는_돌# +641#어둠의_계약서# +642#악마소환서# +643#휴대용_알_부화기# +9001#포링의_알# +9002#드롭프스의_알# +9003#포포링의_알# +9004#루나틱의_알# +9005#픽키의_알# +9006#촌촌의_알# +9007#스틸_촌촌의_알# +9008#헌터_플라이의_알# +9009#세비지베베의_알# +9010#새끼_데저트_울프의_알# +9011#로커의_알# +9012#스포아의_알# +9013#포이즌_스포아의_알# +9014#페코페코의_알# +9015#스모키의_알# +9016#요요의_알# +9017#오크_워리어의_알# +9018#무낙의_알# +9019#도깨비의_알# +9020#소희의_알# +9021#이시스의_알# +9022#초록색_쁘띠의_알# +9023#데비루치의_알# +9024#바포메트_주니어의_알# +10001#동물의_머리뼈_투구# +10002#몬스터_산소_마스크# +10003#투명한_머리_보호대# +10004#꼭지# +10005#패션_가발# +10006#여왕의_머리장식# +10007#실크_리본# +10008#정신봉# +10009#야생화# +10010#찌그러진_냄비# +10011#별모양_머리핀# +10012#작은_알껍질# +10013#책가방# +10014#메뚜기_안경# +10015#초록색_레이스# +10016#금방울# +10017#나무팬츠# +10018#원숭이_머리띠# +10019#붉은색_머플러# +537#펫_푸드# + + +// ---- EP 2.5 크리스마스 ----------- + +7034#구멍난_빨간양말# +7035#성냥# +7036#하티의_이빨# +7037#거래_쿠폰# +644#선물상자# +538#잘_구운_쿠키# +539#조각_케이크# + + +// ---- Ep 3.0 코모도 ---------------- + + +7038#털실# +7039#초보자_명찰# +7040#확성기# +7041#고운_모래조각# +7042#무한_가죽_주머니# +7043#고운_모래가루# +7044#힘내그라# +7045#요술_페인트# +7046#카트_파츠# +7047#엘리스의_앞치마# +7048#그리폰의_발톱# +7049#돌# +7050#무명_돗자리# +7051#실크_돗자리# +7052#폐잡지# +7053#사이파# +7054#브리간# +7055#동물의_거시기# +540#팔콘의_먹이# +541#페코페코의_먹이# +1757#무형의_화살# +1758#스턴_애로우# +1759#프리징_애로우# +1760#플래쉬_애로우# +1761#커스_애로우# +1762#녹슨_화살# +1763#독화살# +1764#날카로운_화살# +1765#오리데오콘_화살# +1766#파마의_화살# +1767#그림자의_화살# +1768#슬립_애로우# +1769#사일런스_애로우# +1770#철_화살# +645#집중의_포션# +656#각성의_포션# +657#버서크_포션# + + +// ---- Ep 4.0 터틀아일랜드 ---------------- + + +544#회# +658#조직의단결력# +659#그녀의_마음# +1142#쥬얼_소드# +1143#가이아_소드# +1144#사시미# +1238#제니_나이프# +1239#포이즌_나이프# +1240#프린세스_나이프# +1306#워_액스# +1417#폴_액스# +1472#스태프_오브_소울# +1473#위자드리_스태프# +1529#아이언_드라이버# +1530#묘르닐# +1531#스패너# +1599#앙그라_마이뉴# +2109#메모라이즈_북# +2410#슬레이프니르# +2629#메긴기오르드# +2630#브리싱가멘# +5020#카프라의_머리띠# +5021#돈_잃은_자의_마음# +5022#태양신의_모자# +5023#보따리모# +5024#케이크_모자# +5025#천사의_투구# +5026#요리사의_모자# +7056#카프라의_월급_명세서# +7057#갤러르호른# +7058#글라이프니르# +7059#카프라_창고_무료_이용권# +7060#카프라_워프_무료_이용권# +7061#카프라_카트_무료_이용권# +7062#거북이_등껍질# +7063#부드러운_깃털# +7064#잠자리의_날개# +7065#해달의_가죽# +7066#얼음조각# +7067#돌조각# +7068#타다만_나무# +7069#깨진_갑옷조각# +7070#깨진_등껍질# +7071#너덜너덜한_옷# +7072#낡은_수리검# +7073#프레이야의_보석# +7074#토르의_쇠장갑# +7075#아이언_메이든# +7076#미지의_톱니바퀴# +7077#은장식# +7078#발키리의_분노# +7079#천사의_날개깃털# +7080#고양이의_발소리# +7081#여인의_수염# +7082#돌의_뿌리# +7083#물고기의_영혼# +7084#새의_침# +7085#곰의_힘줄# +7086#태양신의_상징# +7087#영혼의_숨소리# +7088#눈의_결정# +7089#폭풍우의_징조# +7090#조용한_물결# +7091#몰아치는_파도# +7092#흐르는_공기# +9025#본건알# +10020#영환도사의_검# +2199#아후라_마즈다# +5027#마법사의_모자# +5028#촛불# +5029#스포아_모자# +5030#팬더곰_모자# +5031#광산_헬멧# +5032#나들이_모자# +5033#너구리_모자# +5034#전구_머리띠# +5035#포링_모자# +5036#십자_머리띠# +5037#열매_껍질# +5038#새끼_악마_모자# +5039#알록달록한_알껍질# +5040#발그레# +5041#하트_머리핀# +5042#머리_포자기# +5043#오페라_유령_가면# +5044#악마의_날개# +5045#마술사의_모자# +5046#본건_모자# +5047#패션_선글래스# +5048#초승달_머리핀# +5049#줄무늬_머리띠# +5050#신비한_열매_껍질# +5051#고양이_방울# +5052#청색_머리띠# +5053#스핑크스_모자# + +// ---- Ep 5.0 유노 - 잃어버린 고대의 유산 ---------------- + +2110#홀리_가드# +2111#신의_사자# +2340#초보자용_흉갑# +2341#풀_플레이트_아머# +2342#풀_플레이트_아머# +2343#캐스팅의_로브# +2411#그리브# +2412#그리브# +2413#안전화# +2509#생존의_망토# +1721#리피팅_크로스_보우# +1722#발리스타# +1145#홀리_어벤져# +1241#저주받은_단검# +1242#카운터_단검# +7093#톱니바퀴# +7094#미지의_조각# +7095#깨진_금속_파편# +7096#굳어버린_용암# +7097#불타는_심장# +7098#불씨# +7099#낡은_마법진# +7100#날카로운_잎사귀# +7101#페코_날개_깃털# +7102#악몽# +7103#알_수_없는_액체병# +7104#가짜_천사의_날개# +7105#가짜_영혼의_고리# +7106#산양의_뿔# +7107#산양의_모피# +7108#깨진_방패_조각# +7109#빛나는_창날# +7110#깨진_검# +7111#매끈한_종이# +7112#섬뜩한_종이_칼날# +7113#깨진_파라오_상징# +7114#투탄카멘_마스크# +7115#하피의_깃털# +7116#하피의_발톱# +7117#찢어진_마법서# +7118#찢어진_스크롤# +7119#균사체# +7120#불타는_말발굽# +7121#꿀단지# +7122#뜨거운_털# +7123#드래곤의_껍질# +7124#모래_덩어리# +7125#전갈의_집게발# +7126#라지_젤로피# +7127#알콜_제조_메뉴얼# +7128#화염병_제조_메뉴얼# +7129#염산병_제조_메뉴얼# +7130#식물병_제조_메뉴얼# +7131#기뢰병_제조_메뉴얼# +7132#코팅약_제조_메뉴얼# +7133#슬림포션_제조_메뉴얼# +7134#약사발# +7135#화염병# +7136#염산병# +7137#식인식물병# +7138#기뢰병# +7139#코팅약# +7140#생명의_씨앗# +7141#이그드라실의_이슬# +7142#엠브리오# +7143#유리관# +7144#일반_포션_제조_메뉴얼# +545#레드_슬림_포션# +546#옐로우_슬림_포션# +547#화이트_슬림_포션# +548#치즈# +549#밤고구마# +550#뻥튀기# +660#금단의_붉은_양초# +661#하늘하늘한_앞치마# +5054#어세신_마스크# +2344#[루시우스]의_화산의_격렬함# +2345#[루시우스]의_화산의_격렬함# +2346#[사피엔]의_바다의_울부짖음# +2347#[사피엔]의_바다의_울부짖음# +2348#[에베시]의_태풍의_광폭함# +2349#[에베시]의_태풍의_광폭함# +2350#[클레이토스]의_대지의_갈라짐# +2351#[클레이토스]의_대지의_갈라짐# +7145#라그나로크_티셔츠# +1243#초보자용_망고슈# +2112#초보자용_가드# +5055#초보자용_가짜_알껍질# +2414#초보자용_해변_샌들# +2510#초보자용_음침한_후드# +2352#초보자용_닌자_누더기# + +// ---- 라그나로크 상용화 1주년 기념 아이템 ---------------- + +2631#돌반지# +7146#바캉스_티켓# +7147#자스민# +7148#어머니의_편지# + +// ---- 아마쯔 관련 추가 아이템 - 01 ---------------- + +663#송편# +7149#노란_접시# +7150#대나무_조각# +7151#기름종이# +7152#윤기있는_머리칼# +7153#낡은_기모노# +7154#독가루# +7155#독두꺼비의_껍질# +7156#깨진_슈리켄# +7157#검은_복면# +7158#깨진_술동이# +7159#천구의_코# +7160#영주의_통행증# +551#초밥# + +7131#기뢰병_제조_메뉴얼# + +7132#코팅약_제조_메뉴얼# + +7133#슬림포션_제조_메뉴얼# + +7134#약사발# + +7135#화염병# + +7136#염산병# + +7137#식인식물병# + +7138#기뢰병# + +7139#코팅약# + +7140#생명의_씨앗# + +7141#이그드라실의_이슬# + +7142#엠브리오# + +7143#유리관# + +7144#일반_포션_제조_메뉴얼# + +545#레드_슬림_포션# + +546#옐로우_슬림_포션# + +547#화이트_슬림_포션# + +548#치즈# + +549#밤고구마# + +550#뻥튀기# + +660#금단의_붉은_양초# + +661#하늘하늘한_앞치마# + +5054#어세신_마스크# + +2344#[루시우스]의_화산의_격렬함# + +2345#[루시우스]의_화산의_격렬함# + +2346#[사피엔]의_바다의_울부짖음# + +2347#[사피엔]의_바다의_울부짖음# + +2348#[에베시]의_태풍의_광폭함# + +2349#[에베시]의_태풍의_광폭함# + +2350#[클레이토스]의_대지의_갈라짐# + +2351#[클레이토스]의_대지의_갈라짐# + +7145#라그나로크_티셔츠# + +1243#초보자용_망고슈# + +2112#초보자용_가드# + +5055#초보자용_가짜_알껍질# + +2414#초보자용_해변_샌들# + +2510#초보자용_음침한_후드# + +2352#초보자용_닌자_누더기# + + + +// ---- 라그나로크 상용화 1주년 기념 아이템 ---------------- + + + +2631#돌반지# + +7146#바캉스_티켓# + +7147#자스민# + +7148#어머니의_편지# + + + +// ---- 아마쯔 관련 추가 아이템 - 01 ---------------- + + + +663#송편# + +7149#노란_접시# + +7150#대나무_조각# + +7151#기름종이# + +7152#윤기있는_머리칼# + +7153#낡은_기모노# + +7154#독가루# + +7155#독두꺼비의_껍질# + +7156#깨진_슈리켄# + +7157#검은_복면# + +7158#깨진_술동이# + +7159#천구의_코# + +7160#영주의_통행증# + +551#초밥# + +// ---- 쿤룬 관련 추가 아이템 - 01 ----------- + + +552#KETUPAT# +553#만두# +7161#검은_곰의_가죽# +7162#구름_조각# +7163#단단한_더듬이# +7164#매우_단단한_복숭아# +7165#투명한_날개옷# +7166#부드러운_비단천# +7167#기묘한_철조각# +7168#커다란_나비의_날개# +7169#태극패# +7170#턱시도# +7171#표범의_가죽# +7172#표범의_발톱# +2634#결혼반지# +2635#결혼반지# + +2636#크리스마스_금반지# +2637#크리스마스_은반지# + +7173#번버스터가방# + +// ---- 크리스마스 관련 아이템 추가 ----------- + +7174#포장끈# +7175#포장지# +664#선물상자# +665#선물상자# +666#선물상자# +667#선물상자# +7176#국왕의_증명서# + +// ----- 정월이벤트 ----- + +554#찹쌀떡# + + +// ----- 2003년 겨울방학 이벤트 ----- + +5056#사랑의_열매# +7177#별빛의_흐느낌_조각# +7178#별빛의_흐느낌# +555#떡# +7179#성금_기부증명서# +668#세뱃돈# + + +// ----- 설날 이벤트 ----- + +7180#한석봉의_소개장# +556#가래떡# +557#예쁘게_썰린_가래떡# +669#떡국# + +7181#물품_수령_영수증# + +// ----- 발렌타인 이벤트 ----- + +7182#카카오# +558#초콜렛# +559#핸드메이드_초콜렛# + +// ----- The sign ----- + +7183#여동생의_편지# +7184#피아노_건반# + +// ----- 움발라아이템 ----- + +7185#퀴즈참가권# +7186#가느다란_줄기# +7187#축제_가면# +7188#갈색뿌리# +7189#나무심장# +7190#단단한_등껍질# +7191#등불# +7192#바람개비의_칼날# +7193#발아새싹# +7194#부드러운_풀잎# +7195#새총# +7196#어깨보호대# +7197#질긴_나무덩쿨# +7198#커다란_잎사귀# +7199#쿠폰# +7200#탄력있는_줄# +7201#통나무# +7202#투구벌레의_집게# +7203#튼튼한_나뭇가지# +7204#화약# + +// ------ 니플헤임 ------ + +7205#검은_천조각# +7206#검은_고양이_인형# +7207#낡은_망토# +7208#녹슨_식칼# +7209#듀라한의_투구# +7210#듀라한의_갑옷조각# +7211#로제타스톤_조각# +7212#매달린_인형# +7213#바늘쌈지# +7214#박쥐우리# +7215#부러진_바늘# +7216#빨간_머플러# +7217#실패# +7218#썩은_밧줄# +7219#줄무늬_양말# +7220#액토플라즘# +7221#얽힌_사슬# +7222#나무_옹이# +7223#일그러진_자화상# +7224#현자의_돌# +7225#호박_초롱# +7226#환약# + +// ----- 화이트데이 이벤트 ----- + +560#핸드메이드_초콜렛# +561#화이트_초콜렛# + +7227#TCG카드# + +// ----- 금속화폐 ------ +7228#금괴# +7229#은괴# +7230#백금괴# +7231#금광석# +7232#은광석# +7233#미스릴광석# +670#금화_주머니# +671#금화# +672#동화_주머니# +673#동화# +674#미스릴화# +675#은화# +676#은화_주머니# +677#백금화# +678#독약병# + +// ----- 릴레이퀘스트 ----- +7234#길드의_마음# +7235#돌격의_마음# +7236#수호의_마음# +7237#협동의_마음# +7238#조화의_마음# +7239#전진의_마음# +7240#신뢰의_마음# +7241#결속의_마음# +7242#화합의_마음# +7243#협력의_마음# +7244#단결의_마음# +7245#우정의_마음# +7246#평화의_마음# +7247#근성의_마음# +7248#명예의_마음# +7249#봉사의_마음# +7250#영광의_마음# +7251#승리의_마음# +7252#한방약재# +679#소환단# +7253#금색의_태극기# +7254#디지털인화권# +11000#프론테라_역사서# +562#더블크러스트_스위스_퐁듀# +563#더블크러스트_스위스_퐁듀# + +// ----- 중국로컬라이징 ----- +7262#묘괴의_부채# +7263#묘안석# +7264#말라버린_모래# +7265#용의_뿔# +7266#용의_틀니# +7267#호랑이_팬티# +7268#소귀령# +7269#아기의_턱받침# +7270#아기의_우유병# + +// ----- 머리아이템 ----- +5057#검은_고양이귀# +5058#늘어진_고양이# +5059#곰돌이_모자# +5060#꼬깔모자# +5061#꽃_머리핀# +5062#밀짚모자# +5063#반창고# +5064#변신_나뭇잎# +5065#푸른_생선# +5066#서큐버스의_뿔# +5067#솜브레로# +5068#악마의_날개귀# +5069#여우가면# +5070#열혈머리띠# +5071#인디안의_머리띠# +5072#인큐버스의_뿔# +5073#자세교정모자# +5074#천사의_날개귀# +5075#카우보이모자# +5076#털모자# +5077#튤립머리핀# +5078#해달모자# +5079#X자머리핀# +5080#고대여왕의_왕관# +5081#미스트레스의_왕관# +5082#버섯머리띠# +5083#빨간테일의_리본# +5084#귀찮은_너구리# +5085#두개의_작은리본# +5086#알람가면# +5087#무표정_가면# +5088#놀란눈_가면# +5089#귀찮은_가면# +5090#고블린리더의_가면# +5091#커다란_금방울# +5092#코이프# +5093#코이프# +5094#오크히어로의_투구# + +// ----- 2004이벤트 ----- + +680#마력의_카네이션# +681#신혼의_추억# + +// ----- 2004중화권이벤트 ----- + +682#웅황주# +683#애초# +564#주먹밥# +7271#노비스_피규어# +7272#주먹밥_인형# +2638#피사향포# +2639#구마향포# + +// ----- 2004이벤트 ----- + +7273#RWC목걸이# + +// ----- 2004 the sign ----- + +7274#고대언어의_해석문# +7275#고대언어문서# +7276#그림편지# +7277#무낙인형# +7278#안부편지# + +// ----- 2004이벤트 ----- + +565#비타500# +7279#비타500_병뚜껑# +5096#어쌔신_마스크# +5097#5주년기념모자# +7280#1회_퀴즈참가권# +7281#2회_퀴즈참가권# +7282#3회_퀴즈참가권# +7283#4회_퀴즈참가권# +7284#5회_퀴즈참가권# +684#두리안# + +// ----- 2004모바일 ----- + +2415#토끼_슬리퍼# +2640#카프라반지# +1913#일렉트릭_기타# +5098#타이거마스크# + +// ----- 태국로컬라이징 ----- + +7285#성스러운_실타래# +7286#빨간_칠리# +566#똠양꿍# +567#새우# +568#레몬# +7287#성스러움2배_실타래# +7288#약혼반지# +5099#네코미미# +7298#나뭇잎옷# +7299#소쿠리# +7300#보석의_원석# +7301#칼_장식용술# +7302#KRATHONG# +685#RAMADAN# + +// ----- 4레벨무기퀘스트 ----- + +7289#감람석# +7290#금운모# +7291#마노# +7292#백운모# +7293#장미석영# +7294#터키석# +7295#황수정# +7296#휘석# +7297#흑운모# + +// ----- 신비스크롤 ----- + +9026#지르타스의_알# +9027#엘리스의_알# +569#초보자용_포션# +686#어스_스파이크_3레벨# +687#어스_스파이크_5레벨# +688#콜드_볼트_3레벨# +689#콜드_볼트_5레벨# +690#파이어_볼트_3레벨# +691#파이어_볼트_5레벨# +692#라이트닝_볼트_3레벨# +693#라이트닝_볼트_5레벨# +694#소울_스트라이크_3레벨# +695#소울_스트라이크_5레벨# +696#파이어_볼_1레벨# +697#파이어_볼_5레벨# +698#파이어_월_1레벨# +699#파이어_월_5레벨# +700#프로스트_다이버_1레벨# +12000#프로스트_다이버_5레벨# +12001#힐_3레벨# +12002#힐_5레벨# +12003#귀환서# + +// ----- 신비아이템 ----- + +662#마패# +12004#화살통# +12005#철_화살통# +12006#강철의_화살통# +12007#오리데오콘_화살통# +12008#불화살통# +12009#은화살통# +12010#바람_화살통# +12011#암석_화살통# +12012#수정_화살통# +12013#그림자화살통# +12014#무형의_화살통# +12015#녹슨_화살통# + +// ----- 2005 중화권이벤트 ----- + +570#행운의_사탕# +571#행운의_막대사탕# +572#행운의_쿠키# + +// ----- 알데바란터보트랙 ----- + +12016#이속변화포션# +12017#이속변화포션# + +// ----- 2005한국설날 ----- + +12018#폭죽# +7303#쌀가마니# + +// ----- 카드대량업데이트 ----- + +4149#가고일_카드# +4150#고우트_카드# +4151#가조마르트_카드# +4152#갈라파고_카드# +4153#게_카드# +4154#경단동자_카드# +4155#고블린리더_카드# +4156#고블린스팀라이더_카드# +4157#고블린아쳐_카드# +4158#공중딜리터_카드# +4159#구미호_카드# +4160#구식조총병_카드# +4161#그랜드페코_카드# +4162#그리즐리_카드# +4163#그리폰_카드# +4164#글린부르스티_카드# +4165#기그_카드# +4166#나이트메어테러_카드# +4167#네레이드_카드# +4168#다크로드_카드# +4169#다크일루젼_카드# +4170#다크프레임_카드# +4171#다크프리스트_카드# +4172#더페이퍼_카드# +4173#데몬펑거스_카드# +4174#데빌링_카드# +4175#독두꺼비_카드# +4176#듀라한_카드# +4177#드라이어드_카드# +4178#드래곤테일_카드# +4179#드래곤플라이_카드# +4180#드릴러_카드# +4181#디스가이즈_카드# +4182#디아볼릭_카드# +4183#떠돌이늑대_카드# +4184#라바골렘_카드# +4185#라이드워드_카드# +4186#레글러_카드# +4187#레이드릭아쳐_카드# +4188#레이브올마이_카드# +4189#레이스데드_카드# +4190#레이쓰_카드# +4191#로리루리_카드# +4192#로터자이로_카드# +4193#루드_카드# +4194#리비오_카드# +4195#리프캣_카드# +4196#마린_카드# +4197#마스터링_카드# +4198#마야퍼플_카드# +4199#머맨_카드# +4200#메갈리스_카드# +4201#메이저우로스_카드# +4202#묘괴_카드# +4203#뮤턴트드래고노이드_카드# +4204#미니데몬_카드# +4205#미믹_카드# +4206#미스트케이스_카드# +4207#미스틸테인_카드# +4208#미야비인형_카드# +4209#바이올리_카드# +4210#배회하는자_카드# +4211#보컬_카드# +4212#본건_카드# +4213#브릴라이트_카드# +4214#블러디머더러_카드# +4215#블레이저_카드# +4216#사스콰치_카드# +4217#생도목_카드# +4218#서큐버스_카드# +4219#세이지웜_카드# +4220#솔리더_카드# +4221#스켈레톤제네럴_카드# +4222#스켈프리즈너_카드# +4223#스터랙틱고렘_카드# +4224#스템웜_카드# +4225#스톤슈터_카드# +4226#스팅_카드# +4227#스프링래빗_카드# +4228#슬리퍼_카드# +4229#시계탑관리자_카드# +4230#시노비_카드# +4231#식양_카드# +4232#신선초_카드# +4233#아기표범_카드# +4234#아놀리안_카드# +4235#XMAS쿠키_카드# +4236#아몬라_카드# +4237#아울듀크_카드# +4238#아울바론_카드# +4239#아이언피스트_카드# +4240#아크라우스_카드# +4241#아크엔젤링_카드# +4242#아포칼립스_카드# +4243#안토니오_카드# +4244#알람_카드# +4245#암무트_카드# +4246#어절터_카드# +4247#에스터_카드# +4248#에인션트미이라_카드# +4249#에인션트웜_카드# +4250#엑서큐서너_카드# +4251#엘더_카드# +4252#엘리게이터_카드# +4253#엘리스_카드# +4254#오우거투스_카드# +4255#오크레이디_카드# +4256#오크아쳐_카드# +4257#와일드로즈_카드# +4258#요선녀_카드# +4259#우든골렘_카드# +4260#우탄슈터_카드# +4261#우탄파이터_카드# +4262#운악선_카드# +4263#원령무사_카드# +4264#윈드고스트_카드# +4265#이매망량_카드# +4266#이클립스_카드# +4267#익스플로젼_카드# +4268#인저스티스_카드# +4269#인큐버스_카드# +4270#자이언트스파이더_카드# +4271#자이언트호넷_카드# +4272#저파룡_카드# +4273#조개_카드# +4274#좀비마스터_카드# +4275#좀비프리즈너_카드# +4276#죽은자의주인_카드# +4277#지르타스_카드# +4278#지벳트_카드# +4279#지상딜리터_카드# +4280#지오그래퍼_카드# +4281#지퍼베어_카드# +4282#천구_카드# +4283#천하대장군_카드# +4284#체펫트_카드# +4285#쵸코_카드# +4286#카라카사_카드# +4287#캇파_카드# +4288#캐럿_카드# +4289#캐터필러_카드# +4290#캣오나인테일_카드# +4291#코볼트리더_카드# +4292#코볼트아쳐_카드# +4293#쿠키_카드# +4294#큐브_카드# +4295#크라벤_카드# +4296#크램프_카드# +4297#크루이져_카드# +4298#크리미피어_카드# +4299#클락_카드# +4300#키메라_카드# +4301#킬러맨티스_카드# +4302#타오군카_카드# +4303#거대위스퍼_카드# +4304#탐루안_카드# +4305#터틀제네럴_카드# +4306#토드_카드# +4307#투구벌레_카드# +4308#트라이조인트_카드# +4309#패러사이트_카드# +4310#팬저고블린_카드# +4311#퍼머터_카드# +4312#퍼씰_카드# +4313#펑크_카드# +4314#페노메나_카드# +4315#페스트_카드# +4316#페이크엔젤_카드# +4317#폭력배_카드# +4318#폭풍의기사_카드# +4319#프리저_카드# +4320#피빛의기사_카드# +4321#하이로조이스트_카드# +4322#하이오크_카드# +4323#하티베베_카드# +4324#하티_카드# +4325#하피_카드# +4326#해달_카드# +4327#혈호접_카드# +4328#혜군_카드# +4329#휀다르크_카드# +4330#흑사왕_카드# +4331#히터_카드# + +// ----- 2005 발렌타인이벤트 ----- + +573#초콜렛_드링크# + +// ----- 2005 모바일아이템 ----- + +2511#스켈레톤의_망토# +2641#패션힙색# +5100#세일_표지판# + +// ----- 2005 일본DVD한정 아이템 ----- + +5101#타키우스의_눈가리개# + +// --------- 부활절 이벤트 --------- + +574#알# +12019#성스러운_알# + +// ----- 2005 RBO눈동자 아이템 ----- + +5102#동그란_눈동자# + +// ------- 2005 The sign 3부 ----- +2642#세린의_금반지# +2643#세린의_금반지# +7304#마녀의_주문서# +7305#아홉세계의_권위# +7306#영혼의_조각# +7307#영혼의_속삭임# +7308#마녀의_물약# +7309#까마귀의_날개# +12020#암수# +7313#마녀의_증표# +7314#더_사인# +2644#더_사인# + + +// ------- 2005 강철도시 아인브로크 ----- + +7310#페코페코_무료_이용권# +7311#비공정_무료_승차권# +7312#쥬빌레# +7315#검은_수정의_조각# +7316#벌레의_긴_다리# +7317#녹슬어버린_나사# +7318#오래된_곡괭이# +7319#낡은_철판# +7320#분진# +7321#수정의_조각# +7322#유독가스# +7323#찌그러진_주전자# +7325#튜브# +7326#형광색의_액체# +7327#회중전등# +// ------- 2005 모바일아이템 도둑편 ----- +2645#달빛의_반지# +5103#해바라기핀# +1244#홀리대거# +// ------- 태국 2005 송끄란 이벤트 -------- +7328#송끄란의_전설# +7329#낡은_구리열쇠# +// ------- 대만 2005 어머니 이벤트 -------- +2646#카네이션_꽃다발# +// ------- 2005 강철도시 추가 아이템 ------- +5104#검은_눈가리개# +// 테스트아이템 +12021#삼겹살# +12022#갈비살# +12409#1등급_삼겹살# +12410#오겹살H# + +// ------- 중국1주년,2주년 이벤트 아이템 -------- + +7255#성화주# +7256#성화주# +7257#성화주# +7258#성화주# +7259#성화주# +7260#성화주# +7261#성화주# +7330#성화주# +5105#2주년_기념모자# +12023#주년경예물상# +7331#천당화# +12024#홍색작탄# + +// 인도네시아 2주년 기념이벤트 +575#2주년_기념케이크# + + +// -------- 미국 RO 2주년 기념이벤트 -------- +2647#나일로즈# +7332#석판# +7333#석판의_조각# +7334#석판의_조각# +7335#석판의_조각# +7336#석판의_조각# +7337#헬리온의_눈# +// ------- 중화권 단오절 2005 ------ +12025#단오절_달걀# +12026#단오절_달걀# + +// --------- 새로운 무기 패치 ---------- + +1262#로키의_손톱# +1263#언홀리터치# +1474#가에볼그# +1723#루나보우# +1724#드래곤_윙# +1146#타운소드# +1147#타운소드# +1148#스타더스트_블레이드# +1245#친퀘디아# +1246#친퀘디아# +1247#불쏘시개의_단검# +1248#흑요석_단검# +1249#어부의_단검# +13000#대추나무의_단검# +13001#드래곤킬러# +13002#간눙가가프# +13003#코워드# +13004#코워드# +1307#윈드호크# +1559#드래곤의_유산# +1560#현자의_일기# +1561#양장본# +1616#스태프_오브_윙# +1617#생존의_지팡이# +1618#생존의_지팡이# +1619#생존의_지팡이# +1620#생존의_지팡이# +1815#가름의_발톱# +1914#불타는_정열의_기타# +1915#고독한_솔로의_기타# +1916#광활한_대지의_기타# +1917#산들바람의_기타# +1918#비파# +1965#홍염의_채찍# +1966#빙검의_채찍# +1967#대지의_채찍# +1968#줄넘기# +1969#블레이드휩# +1970#여왕님의_채찍# + +// ---------- 추가무기와 방어구 ---------- +2113#초보자용_쉴드# +2114#스톤_버클러# +2115#발키리아의_쉴드# +2116#천사의_보호# +2353#오딘의_축복# +2354#게브네이의_갑옷# +2355#천사의_가호# +2416#초보자용_슈즈# +2417#프리코슈즈# +2418#비다르의_부츠# +2419#게브네이의_군화# +2420#천사의_재래# +2512#초보자용_망토# +2513#천상의_날개# +2514#폴드런# +2515#윙_오브_이글# +2516#매의_날개옷# +2517#발리의_망토# +2518#모르피셔스의_쇼올# +2519#모리아네의_망토# +2520#게브네이의_어깨장식# +2521#천사의_온기# +2522#런닝셔츠# +2523#런닝셔츠# +2648#모르피셔스의_반지# +2649#모르피셔스의_팔찌# +2650#모리아네의_벨트# +2651#모리아네의_팬던트# +2652#저주받은_행운의_여신_브로치# +2653#세크리파이스링# +2654#시노비의_허리띠# +2655#피묻은_철구# +2656#하이퍼_모드_체인저# +1264#바리어스_쥬르# +1265#블러디로어# +1621#최면술사의_지팡이# +1622#최면술사의_지팡이# + +// ---------- 새 투구추가 ----------- +5107#잘_구운_토스트# +5108#명탐정의_모자# +5109#붉은색의_보닛# +5110#아기_젖꼭지# +5111#갈라파고의_모자# +5112#슈퍼_노비스의_모자# +5113#분노한_입# +5114#벙거지_모자# +5115#패션_방한모자# +5116#바나나_모자# +5117#미스틱_로즈# +5118#강아지_머리띠# +5119#슈퍼_노비스의_모자# +5120#벙거지_모자# +5121#지르타스의_가면# + +// -------- 대만 이벤트 2005 ------- + +7338#편도_교통카드# +7339#기념_교통카드# + + +// ------- 3차 새로운 무기 방어구 -------- + +5122#메긴_캡# +5123#울_캡# +5124#프리카의_서클릿# +5125#천사의_입맞춤# +5126#모르피셔스의_두건# +5127#모리아네의_헬름# +5128#게브네이의_투구# +1475#마상창# +13005#천사의_날개_단검# +1308#금도끼# +1971#전기줄# + + +// -------- 기업도시 리히타르젠 ---------- + +576#가시열매# +7340#암흑의_의지# +7341#낡은_펜던트# +7342#얇은_서류철# +7343#봉인된_서류철# +7344#시노카스_사건_파일# +7345#죄수의_팔찌# +7346#이미르의_심장_더미# +7347#연구원_기록표# +7348#조직원의_증표# +7349#문서보관소_출입카드# +2657#연구소_출입카드# +7350#통행증# +7351#친구의_일기장# +2658#나일로즈# + +// -------- 고대의 유물 유페로스 ---------- + +7352#투명판# +7353#투명판# +7354#투명판# +7355#투명판# +7356#문장의_조각# +7357#문장의_조각# +7358#문장의_조각# +7359#문장의_조각# + +// ---- 상자 ---- +12027#낄낄_웃는_상자# +12028#벼락의_상자# +12029#우울한_상자# +12030#원한의_상자# +12031#졸린_상자# +12032#폭우의_상자# +12033#햇빛의_상자# +12034#헐떡이는_상자# + +7360#RO_페스티벌_초대권# + +// ---------- 유페로스 ---------- +2659#베스퍼_코어_01# +2660#베스퍼_코어_02# +2661#베스퍼_코어_03# +2662#베스퍼_코어_04# + +// ---------- 대만 이벤트 2005 ---------- + +7361#1번_공# +7362#2번_공# +7363#3번_공# +7364#4번_공# +7365#5번_공# +7366#6번_공# +7367#7번_공# +7368#8번_공# +7369#9번_공# +7370#10번_공# +7371#11번_공# +7372#12번_공# +7373#13번_공# +7374#14번_공# +7375#15번_공# +7376#16번_공# +7377#17번_공# +7378#18번_공# +7379#19번_공# +7380#20번_공# +7381#21번_공# +7382#22번_공# +7383#23번_공# +7384#24번_공# +7385#25번_공# +7386#26번_공# +7387#27번_공# +7388#28번_공# +7389#29번_공# +7390#30번_공# +7391#31번_공# +7392#32번_공# +7393#33번_공# +7394#34번_공# +7395#35번_공# +7396#36번_공# +7397#37번_공# +7398#38번_공# +12035#구슬상자# +12036#구슬상자# +12037#구슬상자# +12038#구슬상자# +12039#구슬상자# + + +// ---------- 인도네시아 독립이벤트 --------- + +7399#selamat# +7400#hari# +7401#Kemerdekaan# +7402#Republik# +7403#Indonesia# +7404#ke-60# + +// ---------- 모바일 검사편 택틱스 ---------- + +5129#새둥지# +5130#라이온_마스크# +5131#클로스_헬멧# +1562#전장의_서# +2663#명중의_건틀릿# +2664#벨카프# + +// ------------ 3분기 아이템 ----------- +12040#현자의_돌# +5132#엔젤링_모자# + +// ----------- 요리60가지 ------------ +12041#메뚜기_볶음# +12042#물갈퀴_무침# +12043#봄버_스테이크# +12044#허브_양념갈비# +12045#루티에_아줌마의_부침개# +12046#포도과즙_허브티# +12047#만엽의_홍차# +12048#허브_꿀차# +12049#모로크_과일주# +12050#마스테라의_술# +12051#게_집게발_찜# +12052#해산물_요리# +12053#조개탕# +12054#해파리_무침# +12055#매콤하게_구운_만두# +12056#개구리알_먹물_스프# +12057#미끌미끌_국수# +12058#촉수_치즈_그라탕# +12059#루티에_비빔냉면# +12060#박쥐날개_호박찜# +12061#꿀포도_쥬스# +12062#초콜렛무스_케이크# +12063#후르츠_믹스# +12064#크림_샌드위치# +12065#녹색의_샐러드# +12066#원숭이_꼬리볶음# +12067#혼합쥬스# +12068#맛탕# +12069#고대어_요리# +12070#전갈볶음# + +12071#빛나는_너비아니# +12072#통_바베큐# +12073#특제_곰발바닥요리# +12074#힘줄_소테# +12075#혓바닥요리# +12076#붉은버섯_와인# +12077#특제_로얄제리_허브_티# +12078#왕실전용_고급차# +12079#트리스탄_12년산# +12080#칵테일_용의_숨결# +12081#무지_쓴_보약# +12082#특제_산해진미# +12083#거대_쌈요리# +12084#승룡탕# +12085#불사의_찌개# +12086#칠리_새우_그라탕# +12087#악어구이_야채찜# +12088#초_매운맛_카레# +12089#특제_불도장# +12090#사막전갈_모래찜# +12091#복숭아케이크# +12092#소울_헌트_브레드# +12093#스페셜_토스트# +12094#천상의_과일쥬스# +12095#흐베르겔미르의_술# +12096#거운탕# +12097#꼬치구이# +12098#딸기맛_주먹밥# +12099#피맛나는_탄산소다# +12100#구미호_꼬리요리# + +// ---------- PPL립톤 ---------- +7405#찌그러진_캔# + +// ---------- 대만추석 --------- +7406#문자월병# +7407#문자월병# +7408#문자월병# +7409#문자월병# +7410#문자월병# +7411#문자월병# +7412#문자월병# +7413#문자월병# +7414#문자월병# +12101#유자# +12102#꼬치구이# + +// -------- 2005인도네시아 --------- +7415#소환석# + + +// ----------- 상품 아이템 --------------- + +12103#피묻은_나무가지# +12104#랜덤_화살통# +12105#테이밍_선물셋트# +12106#보석함# +12107#포장된_가면# +12108#주문서_다발# +12109#포링상자# +12110#응급처치상자# +12111#음식보자기# + +// -------------- 메인퀘스트 --------------- +7416#추천장# +7417#의뢰서A# +7418#의뢰서B# +12112#트로피칼_소그라트# +12113#버밀리온_더_비치# + + +// -------------- 추가스킬용 아이템 -------------- + +1771#베넘_나이프# +7419#엠브리오_제조책# +12114#엘리멘탈_컨버터# +12115#엘리멘탈_컨버터# +12116#엘리멘탈_컨버터# +12117#엘리멘탈_컨버터# +12118#레지스트_파이어포션# +12119#레지스트_콜드포션# +12120#레지스트_어스포션# +12121#레지스트_썬더포션# + +// ------------- PVP 아이템 ------------- + +7420#해골_바가지# + +// ------------- 3분기 퀘스트 아이템 ---------------- +7421#빨간_열쇠# +7422#노란_열쇠# +7423#파란_열쇠# +7424#초록_열쇠# +7425#검정_열쇠# +7426#빨강_마력석# +7427#노란_마력석# +7428#파란_마력석# +7429#초록_마력석# +7430#검정_마력석# +7431#여러권의_책# +7432#가죽_주머니# +7433#빈_스크롤# +7434#속성_포션_제조_메뉴얼# + +// ------------- 3분기 아이템 --------------- +7435#금색_장신구# +7436#고뇌의_파편# +7437#슬픔의_파편# +7438#증오의_파편# +7439#절망의_파편# +7440#붉은_깃털# +7441#푸른_깃털# +7442#저주받은_인# +7443#삼두룡의_목# +7444#보물상자# +7445#녹색의_보주# +7446#청색의_보주# +7447#홍색의_보주# +7448#황색의_보주# +7449#피묻은_책장# +7450#해골갑옷의_조각# +7451#화룡의_비늘조각# + +7452#노란_향신료# +7453#단맛_소스# +7454#담백한_소스# +7455#매운맛_소스# +7456#빨간_향신료# +7457#식용유# +577#곡식# +578#딸기# +579#맛좋은_물고기# +580#빵# +581#식용버섯# +582#오렌지# +12122#다식# +12123#유과# +12124#무지개떡# +12125#야외_조리기구# +12126#가정식_조리기구# +12127#고급_조리기구# +12128#궁중의_조리기구# +12129#환상의_조리기구# + +// ---------- 해외이벤트아이템 --------- +7458#파풍특적양각# +7459#RAMADAN# +583#KETUPAT_SAYUR# + +// ------------- 2005 모바일 복사편 ------------ +2356#은혜의_성의# +2665#퇴마의_반지# +5133#양모자# + +// ------------------- 해외 할로윈 아이템 ------------------ +5134#호박모자# +7460#니플헤임용_특급티켓# +7461#파란A카드# +7462#파란E카드# +7463#파란F카드# +7464#파란H카드# +7465#파란L카드# +7466#파란N카드# +7467#파란O카드# +7468#파란P카드# +7469#파란U카드# +7470#파란W카드# +7471#파란Y카드# + +2666#희망의_등잔# + +12130#과자주머니# + +// ------------ 요리제조 -------------- + +7472#1레벨_요리책# +7473#2레벨_요리책# +7474#3레벨_요리책# +7475#4레벨_요리책# +7476#5레벨_요리책# +7477#6레벨_요리책# +7478#7레벨_요리책# +7479#8레벨_요리책# +7480#9레벨_요리책# +7481#10레벨_요리책# + +7482#냄비# + +// ---------- 해외이벤트 아이템 ---------- + +7483#봉인의_열쇠# + + +// --------- 모바일 궁수편 --------------- + +5135#사이클롭스아이# +2667#명사수의장갑# +1725#음유시인의활# + +// ---------- 추가직업군 - 건너 ----------- +// ---------- 클라이언트 ------------- +// ------------ 수능이벤트 ----------------- +12131#행운의_포션# + +// ------------ 크리스마스이벤트 ---------------- +12132#빨간꾸러미# + +// ------------------- 중국 신정 ----------------------- +7484#용사의_상징# +7485#운장의_서# +7486#풍장의_서# + +// ------------------ 닌자 추가 ------------------------ +// ---------- 클라이언트 ------------- +// --------------- 킬하이르의 저택 퀘스트 ---------------- +7487#주점의_술# +7488#배달용_상자# +7489#별장의_보조열쇠# +7490#편지_엘리에게# +7491#철상자# +7492#노란_키카드# +7493#황금색_열쇠# +7494#고급단추# +7495#파란_키카드# +7496#빨간_키카드# +7497#금속파편# +7498#로시미에르가의_열쇠# +7499#어느_가족의_초상화# +7500#여인의_초상화# +7501#K.H의_편지# +7502#제임스의_메모조각# +7503#어떤_남자의_초상화# +7504#동력장치# +7505#장난감_열쇠# +7506#까만_키카드# +7507#단단한_철조각# +7508#엘리시아의_반지# +7509#화려한_키카드# +7516#초록_키카드# + + +// ------------- 4분기 아이템 -------------- +2357#발키리의_갑옷# +2421#발키리의_슈즈# +2524#발키리의_망토# + +7510#발할라의_꽃# +7511#어둠의_룬# +7512#타버린_부품조각# +7513#회중시계# + +// -------------- 해외이벤트아이템 ------------- +584#꼬치탕# +12133#맥도날드_아이스콘# + +// -------------- 몬스터레이싱 ----------------- +7514#몬스터_티켓# +7515#신기한_메달# +// --------------- 미국이벤--------------------- +12134#빨간봉투# +// --------------- 패키지아이템 ---------------- +5137#엘리스인형# +5138#매직아이즈# +5139#부용# +5140#예쁜_리본# +5141#마리오네트인형# +5142#초승달의_투구# +5143#가부키가면# +// --------------- 필리핀 ------------------- +5144#갬블러_햇# +// --------------- 중국 --------------------- +2668#여성의_영광# +12136#여성의_보따리# +7518#여성의_훈장# + +7519#세뱃돈_봉투# +7520#부자되세요# +// --------------- 미국 --------------------- +12135#초록에일# +7517#금동전# + +// ---------------- 브라질 ---------------- +7525#여름축제티켓# + +// --------------- 4분기 이벤트 --------------- +7526#원고용지# + +// --------------- 유럽 이벤트 -------------- +585#부르스티# + +// ---------------- 브라질 ---------------- +5145#카니발_광대모자# + +// ------------------ 인도 -------------------- +586#어머니의_조각케이크# + +// ------------------ 일본 이벤트 --------------------- +2669#RJC목걸이# + +// ------------------ 단계아이템 --------------------- +12137#일단계# +12138#이단계# +12139#삼단계# +12140#사단계# +12141#오단계# + + + +// ---------- 추가직업군 - 건너 ----------- +13100#식스_슈터# +13101#식스_슈터# +13102#크림즌_볼트# +13103#크림즌_볼트# +13104#더_개리슨# +13105#더_개리슨# +13106#골드룩스# +13150#브랜치# +13151#더_사이클론# +13152#더_사이클론# +13153#더스크# +13154#롤링스톤# +13155#블랙로즈# +13156#게이트_키퍼# +13157#드리프터# +13158#붓쳐# +13159#붓쳐# +13160#디스트로이어# +13161#디스트로이어# +13162#인페르노# + +13200#블릿# +13201#실버블릿# +13202#셀_오브_블러드# +13203#플레어_스피어# +13204#라이트닝_스피어# +13205#포이즌_스피어# +13206#블라인드_스피어# +13207#프리징_스피어# + + +// ------------------ 닌자 추가 ------------------------ +13250#수리검# +13251#비구름수리검# +13252#번뜩임의_수리검# +13253#날카로운잎_수리검# +13254#가시바늘의_수리검# +13255#고드름의_쿠나이# +13256#검은흙의_쿠나이# +13257#세찬바람의_쿠나이# +13258#폭염의_쿠나이# +13259#맹독의_쿠나이# + +7521#열화석# +7522#빙섬석# +7523#풍령석# +7524#환구# + +13006#고쿠린# +13007#짓테# +13008#짓테# +13009#카마이타치# +13010#아슈라# +13011#아슈라# +13012#무라사메# +13013#무라사메# +13014#하쿠진# +13015#하쿠진# + +13300#풍마수리검_편익# +13301#풍마수리검_대차륜# +13302#풍마수리검_대차륜# +13303#풍마수리검_열화# + +2117#팔목토시# +2118#팔목토시# +2119#개량형_팔목토시# +2120#개량형_팔목토시# + + +// ---------------인도네시아----------------- +7527#진실된_삶의_책# +12142#마법의_책# + +// ---------------- 태국 미국 필리핀 이벤트 ---------------- +5146#코끼리모자# +5147#야구모자# + +7528#복권_티켓# +7529#도둑맞은_샌들# + +7530#아마쯔_여행가이드# +7531#쿤룬_여행가이드# +7532#용지성_여행가이드# +7533#아요타야_여행가이드# +7534#아마쯔_앨범# +7535#쿤룬_앨범# +7536#용지성_앨범# +7537#아요타야_앨범# + +12143#빨간캔# + + +// ---------------- 포링코인 ----------------- +7538#작업용_모래# +7539#포링코인# + +// --------- 대만 이벤트 2006 ---------- +7540#39번_공# +7541#40번_공# +7542#41번_공# +7543#42번_공# +7544#43번_공# +7545#44번_공# +7546#45번_공# + +// ------------ 건너 탄약 ------------- + +12144#스피어팩_라이트닝# +12145#스피어팩_블라인드# +12146#스피어팩_포이즌# +12147#스피어팩_아이스# +12148#스피어팩_플레어# +12149#탄약통# +12150#탄약통_블러드# +12151#탄약통_실버# + +// -----------------유럽이벤트------------------- +5148#국경일모자# + + +// -----------------브라질이벤트------------------- + +5150#이벤트모자# +7547#축구공# +7548#축구화# +7549#브라질국기# +7550#6월13일_관람표# +7551#6월18일_관람표# +7552#6월22일_관람표# + +// ------------------유럽티아라이벤트-------------------- +5149#실버티아라# +5179#골드티아라# + +// ----------------- 대만 베사크의날 이벤트----------------- +7553#연꽃# +7554#줄무늬초# +7555#초록색향# + +// ------------------RO OST발매기념아이템-------------------- +5151#음표헤드폰# +// ------------------중국이벤트 봉관-------------------- +5152#봉관# + +// ----------------- 대만'Pink yearning' Event --------------- + +5153#엔젤링핀# +2670#그리움의_반지# +2358#천사의_예복# +7556#그리운마음# +7557#초대편지# +7558#초대티켓# +7559#비밀의_화원_열쇠# +12152#특별한_선물# +7560#그리운마음# + +// ---------------------- 인도 아버지의 날 이벤트 ----------------------- +5154#아버지의_선글래스# +5155#아버지의_흰수염# +5156#아버지의_마스크# + +// -------------------------- 소켓인챈트 신규무기추가 ---------------------------- + +1816#베르세르크# +1532#스터너# +1418#궁그닐# +13016#포이즌_나이프# +13017#송곳# +13018#수크사마드# +13019#간눙가가프# +1149#프람베르그# +13400#커틀러스# +1476#크레센트_사이더# +1266#등_뒤를_베는자# +1171#즈바이핸더# +1172#크레이모어# +1726#헌터보우# +1727#발리스타# +5166#스핑크스_모자# +5157#오크족_헬름# +5158#성직자의_모자# +5159#골든_헤드기어# +5160#마제스틱_고우트# +5161#샤프_헤드기어# +5162#본헬름# +5163#커세어# +5164#티아라# +5165#크라운# +2671#활골무# +2525#옛영주의_망토# +2359#닌자_슈츠# +2360#캐스팅의_로브# +2121#메모라이즈_북# +5167#무낙모자# +5168#본건_모자# + + +// ------------------- 2006 상반기 신규카드 추가.----------------------- + +4332#고철난로카드# +4333#베노머스카드# +4334#녹시어스카드# +4335#핏맨카드# +4336#운골리안트카드# +4337#폴셀리오카드# +4338#옵시디언카드# +4339#미네랄카드# +4340#곰인형카드# +4341#메탈링카드# +4342#RSX-0806카드# +4343#홀덴카드# +4344#위레스카드# +4345#힐윈드카드# +4346#이그니젬세니아카드# +4347#아르마이어딘제카드# +4348#휘케바인트리스카드# +4349#이렌드에베시카드# +4350#라우렐뷘더카드# +4351#카바크이카루스카드# +4352#보스이그니젬카드# +4353#리무버카드# +4354#제미니-S58카드# +4355#그렘린카드# +4356#비홀더카드# +4357#로드나이트카드# +4358#세이렌윈저카드# +4359#어쌔신크로스카드# +4360#에레메스가일카드# +4361#화이트스미스카드# +4362#하워드알트아이젠카드# +4363#하이프리스트카드# +4364#마가레타소린카드# +4365#하이위자드카드# +4366#카트린느케이론카드# +4367#스나이퍼카드# +4368#셰실디먼카드# +4369#베나투카드# +4370#디미크카드# +4371#아크담카드# +4372#백소진카드# +4373#청이카드# +4374#베스퍼카드# +4375#오크베이비카드# +4376#타니아가씨카드# +4377#그로브카드# +4378#골드어시더스카드# +4379#블루어시더스카드# +4380#레드페러스카드# +4381#그린페러스카드# +4382#옐로우노버스카드# +4383#레드노버스카드# +4384#하이드로랜서카드# +4385#드래곤의알카드# +4386#디타르데우르스카드# +4387#에인션트미믹카드# +4388#데스워드카드# +4389#플라즈마카드# +4390#산들바람카드# +4391#신벌의대행자카드# +4392#감시하는자카드# +4393#안식을주는자카드# +4394#위로하는자카드# +4395#타나토스의슬픔카드# +4396#타나토스의증오카드# +4397#타나토스의절망카드# +4398#타나토스의고뇌카드# +4399#타나토스의기억카드# +4400#엘리자카드# +4401#엘리셀카드# +4402#엘리엇카드# +4403#키엘-D-01카드# +4404#스코글카드# +4405#프루스카드# +4406#스케골트카드# +4407#란드그리스카드# + +// ------------- 아루나 상반기 ----------- + +7561#얼음_심장# +7562#얼음_비늘# +7563#핏빛의_룬# +7564#썩은_고기# +7565#끈적끈적한_독# +7566#붉은_암흑의_의지# +7567#수상한_모자# +7568#하얀가면# +7569#바람의_해머# +7570#신전추첨권# +7571#블루의_일기장# +7572#잿빛_목걸이# +7573#빛나는_목걸이# +7574#얼음_가루# +587#붉은_가시열매# +2422#고급샌들# + +// ------------------------인도네시아보석이벤트------------------------- +7575#붉은색_보석# +7576#파란색_보석# +7577#노란색_보석# + +// -----------------------2006 상반기 투구아이템-------------------------- + +5169#각시탈# +5170#깃털베레모# +5171#발키리투구# +5172#베레모# +5173#사또모자# +5174#아얌# +5175#익명요청# +5176#하회탈# +5177#사자탈# + +// ------------------ 2006 태국 이벤트 ---------------- + +5178#촛불# + +// -------------- 2006 프랑스이벤트 ----------------- +5180#국경일모자# + + +// ---------------------- 용병소환서 ------------------------------- + + +12153#활용병소환서1급# +12154#활용병소환서2급# +12155#활용병소환서3급# +12156#활용병소환서4급# +12157#활용병소환서5급# +12158#활용병소환서6급# +12159#활용병소환서7급# +12160#활용병소환서8급# +12161#활용병소환서9급# +12162#활용병소환서10급# +12163#검용병소환서1급# +12164#검용병소환서2급# +12165#검용병소환서3급# +12166#검용병소환서4급# +12167#검용병소환서5급# +12168#검용병소환서6급# +12169#검용병소환서7급# +12170#검용병소환서8급# +12171#검용병소환서9급# +12172#검용병소환서10급# +12173#창용병소환서1급# +12174#창용병소환서2급# +12175#창용병소환서3급# +12176#창용병소환서4급# +12177#창용병소환서5급# +12178#창용병소환서6급# +12179#창용병소환서7급# +12180#창용병소환서8급# +12181#창용병소환서9급# +12182#창용병소환서10급# + +//---------------2006중국이벤트------------- + +7578#파법구슬# +7579#직녀의_비단손수건# + +//----------- 2006 유럽이벤트 ------------ + +588#스파게티# +589#피자# + +//-------------- 2006 일본이벤트 ------------- + +5181#암흑의_투구# +5182#강아지모자# +5183#새둥지모자# +5185#월계수관# +5186#장식용_지오그래퍼# +5187#트윈리본# + +// -------------- 2006 추가아이템 ------------- + +5184#선장의_모자# +12183#성스러운_화살통# +12184#용병전용_빨간포션# +12185#용병전용_파란포션# +1772#성스러운_화살# + +// ------------------ 일본 이미지송 특전 ----------------- +5188#음유시인의_모자# + +// ------------------ 말레이시아 싱가폴 3주년 -------------------- +5189#낙엽# +2672#3주년_기념반지# +7581#기념문서# +5190#독립기념모자# + +// ----------------- 대만 4주년 기념---------------------- + +7582#파멸의_보석# +7583#사악한_마음# +7584#경비자의_증명_첫번째# +7585#경비자의_증명_두번째# +7586#경비자의_증명_세번째# +7587#경비자의_증명_네번째# +2673#용사의_빛나는_반지# +1533#용사의_발뭉# +13020#용사의_발뭉# + +// ------------------- 중국상자 ----------------------- +12186#오래된_빨간상자# +12187#오래된_초록상자# + +// ------------------베트남이벤트----------------------- +7588#IPOD티켓# + +2361#파란_아오자이# +2362#빨간_아오자이# +2363#하얀_아오자이# +//-----------------대만추석이벤트---------------------- +12188#신기한_월병# +7589#문자월병# +7590#문자월병# +7591#문자월병# +7592#문자월병# +7593#문자월병# +5198#늘어진_토끼# + +// ---------- 일본 리본 2006 -------------- +5191#까만_리본# +5192#노란_리본# +5193#초록_리본# +5194#분홍_리본# +5195#빨간_리본# +5196#주홍_리본# +5197#하얀_리본# + +12189#오래된_빨간상자# + +// --------- 유럽 복수 2006 -------- +7594#소니아의_편지# +7595#특별한_검# +7596#특별한_방패# +7597#마법의_돌# +5200#코폴라# + +// -------- 인도 2006 독립 이벤트 ------ +5199#야구모자# +7598#파란I카드# +7599#파란D카드# +7600#파란K카드# +7601#파란S카드# +7602#파란R카드# + +// ----------------- 2006 베트남 음력명절 ------------------- +12190#문_케이크# +12191#특별_문_케이크# + + +// ------------- 2006 브라질 2주년 ---------------- +7603#RO파티초대권# +5201#꼬깔모자# + +// --------------- 2006 베트남이벤트 ----------------- +7604#밀가루# +7605#달걀# + +// --------------- 2006 러시아이벤트 ----------------- +7606#파멸의_보석# +// --------------- 2006 중국마룡이벤트 --------------- +7607#마룡의_목# +7608#프리미엄_티켓# +2526#용비늘_외투# +7610#음식교환권# +7701#신룡의_혼백# + +// ---------------- 2006 할로윈이벤트 ----------------- +7609#호박의_정수# +12192#호박파이# +5202#무지_좋은_호박모자# + +// -------------- 2006 인도네시아 명예반지 ------------- +2674#명예의_반지# + +// -------------- 2006 독일 브리젤이벤트 --------------- +12193#브리젤# +590#브리젤# + +// ------------- 2006 브라질 어린이날이벤트 -------------- +5203#메롱가면# + +// ------------- 2006 유럽 여우여왕 이벤트 --------------- +7611#여우의_상징# +7612#여우여왕의_심장# + +// ------------- 2006 한국 추석이벤트 ------------------- +7613#작은_찹쌀반죽# +12199#마법의_스크롤# +12194#고향선물# +12195#담백한_떡# +12196#든든한_떡# +12197#짭짤한_떡# +12198#행운의_떡# +9028#딱딱한_떡# +// -------------------- 2006 일본 로드링 이벤트 ---------------------- + +2675#로드_링# + +//--------------------- 2006 한국크리스마스이벤트 --------------------- + +12200#크리스마스케이크# +5204#루돌프_사슴코# +12225#달콤한_막대사탕# +9029#크리스마스_고블린의_알# + +//--------------------- 필리핀 심방가비 이벤트 --------------------- +5207#천사의_축복# + +//--------------------- 로이크라통 이벤트 ----------------------------- +5206#하얀_꽃잎# + +//--------------------- 필리핀 월계관 이벤트 -------------------------- +5205#황제의_월계관# + +//-------------------- 2006 인도네시아이벤트 ------------------ +7614#특별한_포장지# +7618#몬스터_크리스탈# +2676#헌터_이어링# +// ------------------- 2006 호주이벤트 ------------------ +7615#MVP티켓# +7616#미니보스티켓# +7617#몬스터티켓# + +// ------------------ 2006 라그나로크 바이블 ----------------- +5208#라이드워드# + +// ------------------ 2006 중국빨간상자2 ------------------- +12201#평범한_빨간상자# + +// ----------------- 2006 태국 아버지의 날 -------------- +5209#노란_야구모자# + +// ----------------- 2006 일본 애니버서리 투구 ----------- +5210#비상하는_천사# +5211#드레스_햇# +5212#새틀라이트_헤어밴드# +5213#검은_토끼_머리띠# +5214#월야화_털모자# + +// ----------------- 2006 11월 대만 신규 투구 ------------ +5215#천사의_머리띠# +5216#악마의_머리띠# +5217#마제스틱_고우트# +5218#토끼_머리띠# +5219#늘어진_고양이# +5220#파이프담배# +5221#두개의_작은리본# +5222#푸른_생선# +5223#커다란_금방울# +5224#오크히어로의_투구# +5227#빨간색_새끼악마의_모자# +5228#흰색_새끼악마의_모자# +5229#회색_새끼악마의_모자# +5230#흰색_늘어진_고양이# +5231#회색_늘어진_고양이# +5232#분홍색_늘어진_고양이# +5233#파란색_늘어진_고양이# +5234#노란색_늘어진_고양이# +5235#회색_털모자# +5236#파란색_털모자# +5237#분홍색_털모자# +5238#빨간색_마법사의_모자# +5239#흰색_마법사의_모자# +5240#회색_마법사의_모자# +5241#파란색_마법사의_모자# +5242#노란색_마법사의_모자# + +// --------------- 인도네시아 알람 이벤트 -------------- +7702#특별한_톱니바퀴# +7703#톱니바퀴_조각# + +// ---------------- 책읽기 -------------------- +11001#청년견문록_시리즈_1권# +11002#요리왕_오를레앙_1권# +11003#세기말_카프라전설_1권# +11004#낡고_오래된_책# +11005#룬_왕가_전서# +11006#피를_머금어_피는..._1권# +11007#피를_머금어_피는..._2권# +11008#인명대사전의_카피본# +11009#청년견문록_시리즈_2권# + +// ------------------------ avex 리믹스 아이템 ----------------------- +5225#성스러운_마칭햇# + +// ------------------- 토르화산 퀘스트 아이템 --------------------- +7704#고장난_온도_측정기# +7705#지질학자의_메모# + +// ------------------- 토르화산 신규 장비 아이템 --------------------- +1370#둠_슬레이어# +1371#둠_슬레이어# +13163#롱_바렐# +13164#롱_바렐# +13165#정글_카빈# +13166#정글_카빈# +13167#게이트_키퍼-DD# +13168#THUNDER-P# +13169#THUNDER-P# +2677#스피리츄얼_링# +2678#링_오브_플레임로드# +2679#링_오브_레조넌스# +2680#하급정령의_반지# + +// ---------- 모자 디자인대회 수상작 및 대만 프로펠라 ------- +5252#늘어진_구미호# +5253#늘어진_리프_인형# +5254#데빌링_모자# +5255#삼단_포링모자# +5256#발키리_깃털_모자# +5257#소울리스_링# +5258#아프로_가발# +5226#프로펠라# + +// -------------------------- 퀘스트북 아이템 ------------------------------ +5259#코끼리_모자# +5260#쿠키_모자# +5261#실버_티아라# +5262#골드_티아라# + +// ----------------- 베인스 업데이트 기념 PPL 아이템 ------------------- +7711#이벤트_티켓# + +// ------------------- 노키아 PPL 아이템 ------------------------ +7712#노키아_5500# + +// ----------------- 파란카드 추가 ------------------------ +7713#파란A(2)카드# +7714#파란R(2)카드# +7719#파란G카드# + +// ------------------ 캐쉬 아이템 ------------------- +12202#혓바닥요리# +12203#사막전갈_모래찜# +12204#칵테일_용의_숨결# +12205#흐베르겔미르의_술# +12206#구미호_꼬리요리# +12207#불사의_찌개# +12208#전투_교범# +12209#생명보험_증서# +12210#풍선껌# +12211#카프라_명함# +12212#거대한_파리의_날개# +12213#섬광_막대# +12214#볼록_거울# +12215#블레싱_10레벨_스크롤# +12216#민첩성증가_10레벨_스크롤# +12217#아스페르시오_5레벨_스크롤# +12218#구_아숨프티오_5레벨_스크롤# +12219#윈드워크_10레벨_스크롤# +12220#아드레날린러쉬_5레벨_스크롤# +12221#확성기# +7619#농축_에르늄# +7620#농축_오리데오콘# +7621#지크프리드의_증표# +12900#전투교범_10개_상자# +12901#보험증서_10개_상자# +12902#풍선껌_10개_박스# +12903#혓바닥요리_10개_상자# +12904#사막전갈_모래찜_10개_상자# +12905#칵테일_용의_숨결_10개_상자# +12906#흐베르겔미르의_술_10개_상자# +12907#구미호_꼬리요리_10개_상자# +12908#불사의_찌개_10개_상자# +12909#카프라_명함_10개_상자# +12910#거대한_파리의_날개_10개_상자# +12911#섬광막대_상자# +12912#볼록거울_10개_상자# +12913#블레싱_스크롤_10개_상자# +12914#민첩성증가_스크롤_10개_상자# +12915#아스페르시오_스크롤_10개_상자# +12916#구_아숨프티오_스크롤_10개_상자# +12917#윈드워크_스크롤_10개_상자# +12918#아드레날린러쉬_스크롤_10개_상자# +12919#확성기_10개_상자# +12920#농축_에르늄_10개_상자# +12921#농축_오리데오콘_10개_상자# +12922#지크프리드의_증표_10개_상자# +12923#펫_알_스크롤_상자# +12924#펫_알_스크롤_상자# +12925#펫_알_스크롤# +12926#펫_알_스크롤# +12927#아스페르시오_스크롤상자# +12928#성스러운_스크롤# +12929#펫_알_스크롤_상자# +12930#펫_알_스크롤_상자# +12931#펫_알_스크롤_상자# +12932#펫_알_스크롤# +12933#펫_알_스크롤# +12934#펫_알_스크롤# +5269#비상하는_천사# +13500#생명보험_증서_상자# +13501#체험_스크롤_박스# +14500#훌륭한_생명보험_증서# + +// ----------------------- 러시아 MASLENITIA 아이템 ------------------ +7850#봄의_나무조각# + +// -------------------- 2007 인도 공화 기념일 반지 ----------------- +2681#공화국_기념일_반지# + +// -------------------- 필리핀 아티아티 모자와 반지 --------------------- +5263#파그다야# +2682#물의_반지# +2683#불의_반지# +2684#바람의_반지# +2685#대지의_반지# + +// ------------------ 호주 국기 모자 ------------------------ +5264#호주_국기모자# + +//-------------------- 2007 발렌타인 이벤트 ------------------- +7715#수제초콜렛_레시피# +7716#딸기초콜렛_레시피# +7717#초콜렛타르트_레시피# +7718#카카오콩# +596#깜찍한_딸기초콜렛# +597#사랑스런_초콜렛타르트# +12234#무서운_카카오99%# +12235#딸기초콜렛# +12236#초콜렛타르트# +12237#초콜렛덩어리# + +//------------- 러시아 FRO 아이템 --------------------------- +7851#Wii_응모권# +7852#Divx_플레이어_응모권# +7853#iPod_nano_응모권# +12701#오래된_파란상자# + +// ---------------------- 2007년 신년 이벤트 아이템 ------------------------------ +12238#상한_떡# +12239#상한_떡# + +// --------------------- 오래된 노란상자 --------------------------- +12240#오래된_노란상자# + +// --------------------- 러시아 상반기 이벤트 아이템 ----------------------- +5243#귀마개_모자# +12232#생강빵# +12233#크바스# +591#캐비어_팬케이크# +592#딸기잼_팬케이크# +593#벌꿀_팬케이크# +594#샤워크림_팬케이크# +595#머쉬룸_팬케이크# + +// -------------------------- 음원투구 -------------------- +12244#오래된_선물상자# +5270#낙엽# +5272#메롱가면# +5271#봉관# + +// -------------------- 2007 미국 이벤트 아이템 -------------------- +12245#그린_에일# +7720#골드_코인# +7721#보물상자# + +// ------------------ 브라질 가발 시리즈 ----------------------- +5273#Happy_Wig# +5274#Shinny_Wig# +5275#Marvelous_Wig# +5276#Fantastic_Wig# + +// -------------------- 태국 코모도 페스티발 ----------------- +7854#Comodo_Festival_Ticket# + +// -------------------- 대만 펫알 스크롤 3차 추가분 ---------------- +12983#펫_알_스크롤_상자# +12984#펫_알_스크롤_상자# +12985#펫_알_스크롤_상자# +12986#펫_알_스크롤_상자# +12987#펫_알_스크롤_상자# +12988#펫_알_스크롤_상자# +12989#펫_알_스크롤# +12990#펫_알_스크롤# +12991#펫_알_스크롤# +12992#펫_알_스크롤# +12993#펫_알_스크롤# +12994#펫_알_스크롤# + +// ------------------------ 2007 베트남 여성의 날 ----------------------- +7855#하트# +11701#소녀의_꽃다발# +7857#수제_고양이_인형# + +// ----------------------------2007 중국 여성의 날 ------------------------- +5800#새신랑의_수줍음# +5801#새색시의_리본# + +// ------------------------ 대만 상자 아이템 ----------------------- +12995#하얀허브_상자# +12996#파란허브_상자# +12997#에르늄_상자# +12998#오리데오콘_상자# +12999#고목나무가지_상자# + +// --------------- 대만 공간이동 스크롤 ------------------- +12971#공간이동_스크롤_상자# +12972#공간이동_스크롤_상자# +12973#공간이동_스크롤_상자# +12974#공간이동_스크롤_상자# +12975#공간이동_스크롤_상자# +12976#공간이동_스크롤_상자# +12977#공간이동_스크롤# +12978#공간이동_스크롤# +12979#공간이동_스크롤# +12980#공간이동_스크롤# +12981#공간이동_스크롤# +12982#공간이동_스크롤# + +// ----------------------- 대만 이머전시콜 아이템 ------------------------ +12965#이머전시_1레벨_스크롤_상자# +12966#이머전시_2레벨_스크롤_상자# +12967#이머전시_3레벨_스크롤_상자# +12968#이머전시_1레벨_스크롤# +12969#이머전시_2레벨_스크롤# +12970#이머전시_3레벨_스크롤# + +// -------------------- 필리핀 피플파워 이벤트 --------------- +5277#노란색_두건# +5278#노란색_리본# + +// ------------------- 마력이 깃든 카드첩 ------------------ +12246#마력이_깃든_카드첩# + +// ----------------- 중국 노동절 아이템 ---------------- +7858#꿈의_금속조각# + +// ------------------ 중국 어린이날 아이템 ------------- +7859#신비한_게임권# + +// ------------------ 용병용 포션 ------------------ +12241#용병용_집중_포션# +12242#용병용_각성_포션# +12243#용병용_버서크_포션# + +// ----------------- 대만 과일상자 --------------- +13515#포도_상자# +13516#로얄제리_상자# +13517#이그드라실_열매_상자# + +// ----------------------- 인도네시아 헬스데이 이벤트 --------------- +7727#HP_DOCTOR_TICKET# +7728#SP_DOCTOR_TICKET# + +// -------------------- 해외 만우절 이벤트 아이템 ---------------- +12702#오래된_파랑상자# + +// ------------------- 태국 송끄란 아이템 -------------------- +5802#개조_코끼리모자# + +// ---------------- 릴레이 퀘스트 아이템 ------------------ +7730#임무완수의_증표_1# +7731#임무완수의_증표_2# +7732#임무완수의_증표_3# +7733#임무완수의_증표_4# +7734#임무완수의_증표_5# +7735#임무완수의_증표_6# +7736#임무완수의_증표_7# +7737#임무완수의_증표_8# +7738#임무완수의_증표_9# +7739#임무완수의_증표_10# +7740#임무완수의_증표_11# +7741#임무완수의_증표_12# + +// --------------- 말레이시아 루카스 아이템 --------------------- +12703#성스러운_알# +7860#핍스# +7861#젤리빈# +7862#마쉬멜로우# + +// -------------- 11.3 잡템 ------------------- +7751#낡아빠진_흰_옷# +7752#덜그럭거리는_해골# +7753#부러진_농기구# +7754#부서진_왕관# + +// ------------ 퀘스트 아이템 ----------------- +7755#연구일지# +7756#봉인된_책# + +// -------------- 필리핀 할로할로 이벤트 ---------------- +12247#할로-할로# +7742#카옹# +7743#굴라만# +7744#레체_플란# +7745#우베_잼# +7746#싸고# +7747#랑카# +7748#스위트_빈# +7749#스위트_바나나# +7750#마카푸노# + +// --------------- 필리핀 락스타 뱃지 ------------- +7729#Rok_스타_뱃지# + +// ------------------- 11.3 신규 장비 아이템 -------------------------- +2122#플래티넘_쉴드# +2123#오를레앙의_서버# +2124#가시_방패# +2125#스트롱_쉴드# +2364#메테오_플레이트# +2365#오를레앙의_제복# +2366#디바인_클로스# +2367#스나이핑_슈츠# +2423#배리언트_슈즈# +2424#타이달_슈즈# +2425#검은_가죽부츠# +2426#쉐도우_워크# +2527#드래곤의_숨결# +2528#울_스카프# +2529#라이더_휘장# +2530#라이더_휘장# +2531#울프헤딘# +2532#미스릴_마법망토# +2700#봉인의_붉은비단# +2701#오를레앙의_장갑# +2702#들소의_뿔# +2703#익스퍼트_링# +1175#아트로스의_흉기# +1176#근육_절단기# +1268#야수의_발톱# +1269#인버스_스케어# +1270#드릴_카타르# +1271#혈루# +1375#버디슈# +1376#하트_브레이커# +1377#허리케인_퓨리# +1477#요괴의_창# +1478#알슈피스# +2000#스태프_오브_디스트럭션# +2001#디바인_크로스# +13304#풍마수리검_명정# +1730#불타는_활# +1731#냉각의_활# +1732#대지의_활# +1733#질풍의_활# +1734#오크아쳐의_활# +13404#백금의_쇼텔# +13405#연검# +13027#메스# +13028#투스_블레이드# +1420#롱혼# +1421#배틀훅# +1422#헌팅스피어# +1535#홀그랜의_제련망치# +1564#대백과_사전# +1565#사신의_명부# +1624#리치의_해골지팡이# +1625#치유의_지팡이# +1626#스태프_오브_피어싱# +1818#마그마_피스트# +1819#아이시클_피스트# +1820#일렉트릭_피스트# +1821#사이즈믹_피스트# +1822#연격의_투갑# +1919#베이스_기타# +1920#신들린_기타# +1972#전기_뱀장어# +1973#바다마녀의_발# +1974#당근채찍# +13107#황야의_무법자# +13170#레버액션_라이플# + +// --------------------- 대만 이벤트 아이템 -------------------- +13518#무기카드_펫_알_스크롤_상자# +13519#갑옷카드_펫_알_스크롤_상자# +13520#투구카드_펫_알_스크롤_상자# +13521#걸칠것카드_펫_알_스크롤_상자# +13522#방패카드_펫_알_스크롤_상자# +13523#신발카드_펫_알_스크롤_상자# +13524#악세사리카드_펫_알_스크롤_상자# +13525#제니_펫_알_스크롤_상자# +13554#무기카드_펫_알_스크롤_상자# +13555#무기카드_펫_알_스크롤_상자# +13556#갑옷카드_펫_알_스크롤_상자# +13557#악세사리카드_펫_알_스크롤_상자# +13558#무기카드_펫_알_스크롤# +13559#갑옷카드_펫_알_스크롤# +13560#투구카드_펫_알_스크롤# +13561#걸칠것카드_펫_알_스크롤# +13562#방패카드_펫_알_스크롤# +13563#신발카드_펫_알_스크롤# +13564#악세사리카드_펫_알_스크롤# +14508#제니_펫_알_스크롤# +13565#무기카드_펫_알_스크롤# +13566#무기카드_펫_알_스크롤# +13567#갑옷카드_펫_알_스크롤# +13568#악세사리카드_펫_알_스크롤# + +// --------------------- 말레이시아 캐쉬 아이템 -------------------- +13526#펫_알_스크롤_상자# +13527#펫_알_스크롤_상자# +13528#펫_알_스크롤_상자# +13529#펫_알_스크롤_상자# +13530#펫_알_스크롤_상자# + +// -------------- 대만 가벼운 물약 아이템 ----------- +13531#가벼운_빨간포션_상자# +13532#가벼운_주홍포션_상자# +13533#가벼운_노란포션_상자# +13534#가벼운_하얀포션_상자# +13535#가벼운_집중포션_상자# +13536#가벼운_각성포션_상자# +13537#가벼운_버서크포션_상자# +14509#가벼운_집중포션# +14510#가벼운_각성포션# +14511#가벼운_버서크포션# +598#가벼운_빨간포션# +599#가벼운_주홍포션# +11500#가벼운_노란포션# +11501#가벼운_하얀포션# + +// ------------ 11.3 퀘스트 아이템 -------------- +7722#차용증# +7723#파멸의_다이아몬드# +7724#금단의_신비술서# +7725#불운의_에메랄드# +7726#왕의_증명# + +// -------------- 태국 3월 캐쉬아이템 -------------- +13538#메테오스톰_스크롤_상자# +13539#스톰가스트_스크롤_상자# +13540#로드_오브_버밀리온_스크롤_상자# +13541#렉스_에테르나_스크롤_상자# +13542#마니피캇_스크롤_상자# +13543#케미칼_프로텍션_헬름_스크롤_상자# +13544#케미칼_프로텍션_쉴드_스크롤_상자# +13545#케미칼_프로텍션_아머_스크롤_상자# +13546#케미칼_프로텍션_웨폰_스크롤_상자# +13547#무기수리_스크롤_상자# +13548#왕만두_상자# +13549#환약_상자# +13550#환상의_회_상자# +13551#찰떡_상자# +13552#유밀과_상자# +13553#던전_순간이동_스크롤_5개_상자# +14512#메테오스톰_스크롤# +14513#스톰가스트_스크롤# +14514#로드_오브_버밀리온_스크롤# +14515#렉스_에테르나_스크롤# +14516#마니피캇_스크롤# +14517#케미칼_프로텍션_헬름_스크롤# +14518#케미칼_프로텍션_쉴드_스크롤# +14519#케미칼_프로텍션_아머_스크롤# +14520#케미칼_프로텍션_웨폰_스크롤# +14521#무기수리_스크롤# +14522#왕만두# +14523#환약# +14524#환상의_회# +14525#찰떡# +14526#유밀과# +14527#던전_순간이동_스크롤# + +// --------------------- 임대아이템 -------------------------- +1267#등뒤를_베는자# +1173#무라마사# +13401#엑스칼리버# +13021#컴뱃나이프# +13022#카운터단검# +1817#카이저너클# +1419#폴액스# +1623#마이트스태프# +1372#라이트엡실론# +1728#발리스타# +1563#현자의_일기# +13023#아슈라# +5265#명사수의_사과# +5266#토끼머리띠# +5267#삿갓# +5268#로드서클릿# +2686#요정의_귀# +2687#강철의_꽃잎# +2688#크리티컬링# +2689#이어링# +2690#링# +2691#네클리스# +2692#글러브# +2693#브로치# +2694#로자리# +2695#세이프티링# +2696#베스퍼코어01# +2697#베스퍼코어02# +2698#베스퍼코어03# +2699#베스퍼코어04# +12935#등뒤를_베는자_상자# +12936#무라마사_상자# +12937#엑스칼리버_상자# +12938#컴뱃나이프_상자# +12939#카운터단검_상자# +12940#카이저너클_상자# +12941#폴액스_상자# +12942#마이트스태프_상자# +12943#라이트엡실론_상자# +12944#발리스타_상자# +12945#현자의_일기_상자# +12946#아슈라_상자# +12947#명사수의_사과_상자# +12948#토끼머리띠_상자# +12949#삿갓_상자# +12950#로드서클릿_상자# +12951#요정의_귀_상자# +12952#강철의_꽃잎_상자# +12953#크리티컬링_상자# +12954#이어링_상자# +12955#링_상자# +12956#네클리스_상자# +12957#글러브_상자# +12958#브로치_상자# +12959#로자리_상자# +12960#세이프티링_상자# +12961#베스퍼코어01_상자# +12962#베스퍼코어02_상자# +12963#베스퍼코어03_상자# +12964#베스퍼코어04_상자# +13502#늘어진_고양이_상자# +13503#마제스틱_고우트_상자# +13504#새끼_악마모자_상자# +13505#액서큐셔너_상자# +13506#블러드_액스_상자# +13507#토마호크_상자# +13508#루드라의_활_상자# +13509#커틀러스_상자# +13510#태양검_상자# +13511#소드브레이커_상자# +13512#메일브레이커_상자# +13513#월광검_상자# +13514#스패너_상자# +5279#늘어진_고양이# +5280#마제스틱_고우트# +5281#새끼_악마모자# +1174#액서큐셔너# +1373#블러드_액스# +1374#토마호크# +1729#루드라의_활# +13402#커틀러스# +13403#태양검# +13024#소드_브레이커# +13025#메일_브레이커# +13026#월광검# +1534#스패너# + +// ------------------ 2007 유럽 생일 이벤트 -------------------- +5282#야구모자# + +// ------------- 말레이시아 PVP 이동스크롤 ------------ +13569#PVP_이동_스크롤_상자# +14528#PVP_이동_스크롤# + +// ------------- 인도네시아 4주년 이벤트 --------------- +7863#Special_Gold# +2368#Golden_Armor# +2427#Golden_Shoes# +2704#Golden_accesories# +2705#Golden_accesories# +5295#Golden_Bandana# + +// -------------- 일본 투구 12종 ----------------- +5294#위스퍼_마스크# +5283#병아리_모자# +5284#수련_왕관# +5285#풍차_비녀# +5286#페코페코_머리띠# +5287#바캉스_모자# +5288#빨간_안경# +5289#바닐미르스_모자# +5290#늘어진_토끼# +5291#주전자_모자# +5292#용의_해골# +5293#라면_모자# + +// ---------------- 캐쉬템 상자 들이 추가 및 모바일 아이템 ----------------- +13570#거대한_파리의_날개_50개_상자# +13571#거대한_파리의_날개_100개_상자# +13572#흐베르겔미르의_술_30개_상자# +13573#흐베르겔미르의_술_50개_상자# +13574#구미호_꼬리요리_30개_상자# +13575#구미호_꼬리요리_50개_상자# +13576#민첩성증가_스크롤_30개_상자# +13577#민첩성증가_스크롤_50개_상자# +13578#불사의_찌개_30개_상자# +13579#불사의_찌개_50개_상자# +13580#보험증서_30개_상자# +13581#보험증서_50개_상자# +13582#볼록거울_5개_상자# +13583#볼록거울_30개_상자# +13584#블레싱_스크롤_30개_상자# +13585#블레싱_스크롤_50개_상자# +13586#아드레날린러쉬_스크롤_30개_상자# +13587#아드레날린러쉬_스크롤_50개_상자# +13588#구_아숨프티오_스크롤_30개_상자# +13589#구_아숨프티오_스크롤_50개_상자# +13590#아스페르시오_스크롤_30개_상자# +13591#아스페르시오_스크롤_50개_상자# +13592#사막전갈_모래찜_30개_상자# +13593#사막전갈_모래찜_50개_상자# +13594#윈드워크_스크롤_30개_상자# +13595#윈드워크_스크롤_50개_상자# +13596#칵테일_용의_숨결_30개_상자# +13597#칵테일_용의_숨결_50개_상자# +13598#전투교범_상자# +13599#전투교범_5개_상자# +13600#지크프리드의_증표_5개_상자# +13601#지크프리드의_증표_20개_상자# +13602#카프라_명함_30개_상자# +13603#카프라_명함_50개_상자# +13604#혓바닥요리_30개_상자# +13605#혓바닥요리_50개_상자# +13606#풍선껌_박스# +13607#풍선껌_5개_박스# +13608#확성기_상자# +13609#확성기_5개_상자# +13610#농축_에르늄_5개_상자# +13611#농축_오리데오콘_5개_상자# +7757#미스릴# +7758#별의결정# + +// --------------- 가장무도회 상자 및 투구 --------------- +12248#가장무도회_상자# +5296#늘어진_구미호# +5297#소울리스_윙# +5298#아프로_가발# +5299#파그다야# + +// ---------------- 인도네시아 부모의 날 이벤트 ------------------ +5803#플라워러브햇# +7864#러브플라워# + +// -------------- 태국 환경의 날 아이템 --------------- +7865#금주머니# +7866#인증서# + +// --------------- 일본 구속 아이템 -------------------- +13612#구속수갑_상자# +2706#구속수갑# + +// ----------------- 태국 피치 몽콜 데이 이벤트 ------------------------ +7867#깨_주머니# +7868#물# +7869#쌀_주머니# +7870#옥수수# +7871#콩_주머니# +7872#풀# +5300#뿔투구_헬름# + +// -------------- 브라질 루키서머너 아이템 --------------- +7873#MVP_몬스터_스크롤# +7874#일반_몬스터_스크롤# + +// ------------- 대만 슈퍼 펫알 스크롤 ------------------ +13613#슈퍼_펫_알_상자# +13614#슈퍼_펫_알_상자# +13615#슈퍼_펫_알_상자# +13616#슈퍼_펫_알_상자# +13617#슈퍼_펫_알# +13618#슈퍼_펫_알# +13619#슈퍼_펫_알# +13620#슈퍼_펫_알# + +// ---------------- 탐욕 스크롤 ----------------- +13621#탐욕_스크롤_30개_상자# +13622#탐욕_스크롤_50개_상자# +13623#탐욕_스크롤_100개_상자# +14529#탐욕_스크롤# + +// --------------- 러시아 전승기념일 머리띠 -------------- +5301#전승기념일_머리띠# + +// --------------- 말레이시아 노동절 이벤트 -------------- +5302#연꽃_모자# + +// --------------- 러시아 나인허브 아이템 -------------- +5303#화관# + +// ---------------- 인도네시아 회피,명중 스크롤 -------------- +13624#날렵함의_스크롤_상자# +13625#집중력의_스크롤_상자# +14530#날렵함의_스크롤# +14531#집중력의_스크롤# + +// ------------- 대만 슈퍼 카드펫알 스크롤 ------------------ +13626#슈퍼_카드_펫_알_상자# +13627#슈퍼_카드_펫_알_상자# +13628#슈퍼_카드_펫_알_상자# +13629#슈퍼_카드_펫_알_상자# +13630#슈퍼_카드_펫_알# +13631#슈퍼_카드_펫_알# +13632#슈퍼_카드_펫_알# +13633#슈퍼_카드_펫_알# + +// --------------- 브라질 모자 컨테스트 당선작 -------------------- +5304#맹목의_후드# + +// ------------- 토르화산 퀘스트 아이템 --------------- +7759#지질학자의_보고서# + +// ---------------- 캐쉬템 패키지 추가 ----------------- +13634#1시간_패키지_Vol_1# +13635#1시간_패키지_Vol_2# +13636#1시간_패키지_Vol_3# +13637#1시간_패키지_Vol_4# +13638#1시간_패키지_Vol_5# +13639#1시간_패키지_Vol_6# +13640#2시간_패키지_Vol_1# +13641#2시간_패키지_Vol_2# +13642#2시간_패키지_Vol_3# +13643#2시간_패키지_Vol_4# +13644#2시간_패키지_Vol_5# +13645#2시간_패키지_Vol_6# + +// -------------- 일본 임대아이템, 1시간 => 한국 임대아이템 1일 --------------- +13646#등뒤를_베는자_상자# +13647#무라마사_상자# +13648#엑스칼리버_상자# +13649#컴뱃나이프_상자# +13650#카운터단검_상자# +13651#카이저너클_상자# +13652#폴액스_상자# +13653#마이트스태프_상자# +13654#라이트엡실론_상자# +13655#발리스타_상자# +13656#현자의_일기_상자# +13657#아슈라_상자# +13658#명사수의_사과_상자# +13659#토끼머리띠_상자# +13660#삿갓_상자# +13661#로드서클릿_상자# +13662#요정의_귀_상자# +13663#강철의_꽃잎_상자# +13664#크리티컬링_상자# +13665#이어링_상자# +13666#링_상자# +13667#네클리스_상자# +13668#글러브_상자# +13669#브로치_상자# +13670#로자리_상자# +13671#세이프티링_상자# +13672#베스퍼코어01_상자# +13673#베스퍼코어02_상자# +13674#베스퍼코어03_상자# +13675#베스퍼코어04_상자# +13676#늘어진_고양이_상자# +13677#마제스틱_고우트_상자# +13678#새끼_악마모자_상자# +13679#액서큐셔너_상자# +13680#블러드_액스_상자# +13681#토마호크_상자# +13682#루드라의_활_상자# +13683#커틀러스_상자# +13684#태양검_상자# +13685#소드브레이커_상자# +13686#메일브레이커_상자# +13687#월광검_상자# +13688#스패너_상자# +13734#페코페코_머리띠_상자# +13735#빨간안경_상자# +13736#위스퍼_마스크_상자# +13737#라면모자_상자# + +// ------------- 미국 해적의 날 이벤트 아이템 -------------- +7875#해적의_상자# +5305#해적의_대거# +5804#해적의_안대# + +// -------------- 일본 모바일 요리 ----------------- +12250#혓바닥요리# +12251#사막전갈_모래찜# +12252#칵테일_용의_숨결# +12253#흐베르겔미르의_술# +12254#구미호_꼬리요리# +12255#불사의_찌개# + +// ----------------- 중국 프레이야 셋트 ---------------- +13691#프레이야의_외투_상자# +13692#프레이야의_장화_상자# +13693#프레이야의_망토_상자# +13694#프레이야의_크라운_상자# +2369#프레이야의_외투# +2428#프레이야의_장화# +2533#프레이야의_망토# +5306#프레이야의_크라운# + +// ------------ 2007 대만단오절 이벤트 아이템 ------------------- +9030#청이의_알# +9031#옥토끼의_알# +13689#배추잎_상자# +13690#맛있는_조각케이크_상자# +7766#배추잎# +7767#맛있는_조각케이크# +7768#오징어# +7769#노른자# +7770#찹쌀# +7771#연잎# +7772#줄# + +// -------------- 일본 카프라명세서_ ----------------- +12249#카프라의_월급_명세서# + +// -------------- 일본 모바일 아이템 ----------------- +13695#기초_전투교범_상자# +13696#상급_전투교범_상자# +13697#블레싱_스크롤_10개_상자# +13698#민첩성증가_스크롤_10개_상자# +13699#윈드워크_스크롤_10개_상자# +13700#아드레날린러쉬_스크롤_10개_상자# +14532#기초_전투교범# +14533#상급_전투교범# + +// --------------- 브라질 캐쉬 아이템 20070605 ------------ +5307#Carmen_Miranda's_Hat# +5308#브라질_국기모자# +5309#모히칸_가면# + +// --------------- 필리핀 선물상자 ------------- +12256#PRO_선물박스# + +// ------------------------ 테스트 아이템 ------------------ +7706#상한_당근쥬스# +7707#상한_바나나쥬스# +7708#상한_사과쥬스# +7709#상한_포도쥬스# +7710#블랙_젬스톤# +5244#요정의_눈가리개# +5245#요정의_선글래스# +5246#천사의_헬름# +5247#악마의_헬름# +5248#로봇의_눈가리개# +5249#인간의_눈가리개# +5250#로봇귀# +5251#동그란귀# + +// --------------------- 말레이시아 캐쉬 아이템 -------------------- +13701#펫_알_스크롤# +13702#펫_알_스크롤# +13703#펫_알_스크롤# + +// ----------- 대만 슈퍼 펫알 20070614 -------- +13704#슈퍼_펫_알# +13705#슈퍼_펫_알# +13706#슈퍼_펫_알# +13707#슈퍼_펫_알# + +// --------- 말레이시아 20070614 펫알 스크롤 -------- +13708#펫_알_스크롤# +7774#청이알_교환권# +7775#옥토끼알_교환권# + +// ------------- 전투휘장 --------------- +7773#전투휘장# + +// -------------- 러시아 불사신 코쉐이 아이템 --------------- +7876#황금색의_열쇠# +7877#빨간_반지# +7878#루살까의_머리카락# +7879#금실# +7880#바바야가의_은_숟가락# +7881#마법_책# +7882#뾰족한_가지# +7883#뽀족한_나무_피리# +2429#쇠로_만든_신발# +2430#쇠로_만든_신발# + +// --------------- BRO 패키지 상자 ------------------- +13709#BRO패키지박스# + +// ---------------- 헬스장 회원권 ----------------- +13710#헬스장_회원권_상자# +7776#헬스장_회원권# + +// ---------------- 캐쉬아이템 20070702 ----------------- +13711#소형_생명수_10개_상자# +13712#소형_생명수_30개_상자# +13713#소형_생명수_50개_상자# +13714#중형_생명수_10개_상자# +13715#중형_생명수_30개_상자# +13716#중형_생명수_50개_상자# +13717#연마제_5개_상자# +13718#연마제_10개_상자# +13719#회복력_향상포션_5개_상자# +13720#회복력_향상포션_10개_상자# +13721#던전_순간이동_스크롤_10개_상자# +13722#페코페코_머리띠_상자# +13723#빨간안경_상자# +13724#위스퍼_마스크_상자# +13725#라면모자_상자# +14534#소형_생명수# +14535#중형_생명수# +14536#연마제# +14537#회복력_향상포션# + +// ------------------ 태국 패키지 투구 아이템 ------------------ +5310#반짝이는_전구_머리띠# +5311#커다란_하이비스커스# +5312#아요타야_왕의_모자# +5313#다이아뎀# +5314#하키_마스크# +5315#옵저버# + +// ---------------- 필리핀 우기 이벤트 20070607 ----------------- +12257#감기약# +5316#우산_모자# +5317#어부의_모자# + +// --------------- 태국 어머니의 날 이벤트 ------------ +5319#안녕엄마_모자# + +// ---------------- 인도네시아 반다나 월계관 ------------------ +5320#챔피언의_월계관# +5321#인도네시아_헤어밴드# + +// -------------- 말레이시아/싱가폴 이벤트 아이템 --------------- +7884#옥패# +7885#신성한_화살# +5805#승리자의_머리장식품# +2708#중국제수공품# +12704#불로장생약# +12705#귀족의_명패# + +// ------------- 대만 금상자 은상자 ------------------ +13726#황금_상자# +13727#은_상자# +13728#황금_열쇠_1개_상자# +13729#황금_열쇠_5개_상자# +13730#은_열쇠_1개_상자# +13731#은_열쇠_5개_상자# +7777#잠겨있는_황금_상자# +7778#잠겨있는_은_상자# +7779#황금_열쇠# +7780#은_열쇠# + +// -------------- 국내 추석 이벤트 아이템 --------------- +7886#팥소# +7887#말린_과일_상자# +7888#견과류_주머니# +7889#맛있는_닭_모이# +11702#달_과자# +12706#행운_과자# +12707#행운_과자# +12708#행운_과자# + +// ----------------- 중국 7월7석 이벤트 아이템 -------------- +7781#마음이_새겨진_보물상자# +7782#금의_열쇠# +7783#은의_열쇠# + +// ----------------- 폭탄링 상자 ------------- +12258#폭탄링_캡슐# + +// ------------- 보상아이템 쿠폰 --------------- +7784#무료쿠폰1# +7785#무료쿠폰2# +7786#무료쿠폰3# +7787#무료쿠폰4# +7788#무료쿠폰5# +7789#무료쿠폰6# +7790#무료쿠폰7# +7791#무료쿠폰8# + +// ---------------- 캐쉬아이템 20070801 ----------------- +13738#환영의_술잔_5개_상자# +13739#환영의_술잔_10개_상자# +13740#쉐도우_아머_스크롤_5개_상자# +13741#쉐도우_아머_스크롤_10개_상자# +13742#쉐도우_아머_스크롤_30개_상자# +13743#홀리_아머_스크롤_5개_상자# +13744#홀리_아머_스크롤_10개_상자# +13745#홀리_아머_스크롤_30개_상자# +13746#소형_물리_방어_포션_10개_상자# +13747#소형_물리_방어_포션_30개_상자# +13748#소형_물리_방어_포션_50개_상자# +13749#대형_물리_방어_포션_10개_상자# +13750#대형_물리_방어_포션_30개_상자# +13751#대형_물리_방어_포션_50개_상자# +13752#소형_마법_방어_포션_10개_상자# +13753#소형_마법_방어_포션_30개_상자# +13754#소형_마법_방어_포션_50개_상자# +13755#대형_마법_방어_포션_10개_상자# +13756#대형_마법_방어_포션_30개_상자# +13757#대형_마법_방어_포션_50개_상자# +14538#환영의_술잔# +14539#쉐도우_아머_스크롤# +14540#홀리_아머_스크롤# +14541#소형_물리_방어_포션# +14542#대형_물리_방어_포션# +14543#소형_마법_방어_포션# +14544#대형_마법_방어_포션# + +// ----------------- 운명의 까마귀 퀘스트 아이템 --------------- +7794#운명의_까마귀# +7795#마미의_사진집# +7796#작가의_서명# +7797#작가의_메모# + +// ------------------- 12.1 퀘스트 아이템 ---------------------- +7798#어둠의_파편# +7799#어둠의_결정체# + +// --------------- 대만 구약 이벤트 ----------- +2126#구약방패# +12709#구약사탕# +12710#구약푸딩# +7792#구약# + +// ----------------- 러시아 황금사과 이벤트 아이템 -------------- +7793#황금사과# +7800#마력의_황금사과# +5322#스카프# + +// ----------------- 20070808 신규카드 34종 ----------------- +4408#글룸언더나이트카드# +4409#아가브카드# +4410#에키오카드# +4411#판베르크카드# +4412#이실라카드# +4413#호드렘린카드# +4414#시커카드# +4415#스노이어카드# +4416#시로마카드# +4417#아이스티탄카드# +4418#게에즈티카드# +4419#크툴라낙스카드# +4420#머스키퓰라카드# +4421#드로세라카드# +4422#로우윈카드# +4423#갈리온카드# +4424#스태포카드# +4425#아트로스카드# +4426#뵤르그카드# +4427#소드가디언카드# +4428#보우가디언카드# +4429#살라만다카드# +4430#이프리트카드# +4431#카사카드# +4432#마그마링카드# +4433#임프카드# +4434#노커카드# +4435#좀비슬러터카드# +4436#누더기좀비카드# +4437#헬푸들카드# +4438#밴시카드# +4439#플레임스컬카드# +4440#네크로맨서카드# +4441#타락한대신관카드# + +// ------------- 20070806 인도 캐쉬아이템 ---------------- +13758#300%_전투교범_상자# +13759#파란허브_상자# +13760#꿀_상자# +13761#빈_병_상자# +13762#로얄젤리_상자# +14545#300%_전투교범# + +// --------- 대만 환상의 영약 -------------- +12259#환상의_영약# +12261#환상의_비약# + +// ------------- 여름이벤트 아이템 --------------- +12260#시원한_여름의상# +7801#소녀의_팬레터# +7802#싸인첩# + +// ------------- 사크라이 지급 아이템 --------------- +5323#미스트렌스_왕관# +2370#유성_매직코트# +2127#슬리퍼의_사자# +2534#옷플러# +2431#발위에_슈즈# +13029#프린센스_나이프# +13406#엣찌# +1536#굿모닝스타# +1423#폴X.O# +1921#거문곰# +1975#여왕님은_채찍# +1566#현지의_일기# +1627#스탬프# +1177#무랑맛살# +1272#등_뒤를_긁는_자# +1735#깍궁# + +// -------------- 태국 캐쉬아이템 투구 추가 20070809 -------------- +5324#리틀_엔젤_돌# +5325#로보_아이# + +// -------------- 대만 초보자 전투교범 ----------------- +13764#초보자_전투교범_5개_상자# +7803#초보자_전투교범# + +// ------------- 대만 출석체크 이벤트 아이템 --------------- +13765#동봉된_수행_증명서# +2713#수행_증명서# + +// ------------- 태국 모험가의 전설 이벤트 아이템 -------------- +7805#Brown_Jenoss’s_Family_Ring# +7806#God_Anvil# +7807#God_Mineral# +7808#God_Hammer# +7809#God_Furnace# +7810#Symbol_of_Richness# +7811#모루# +7812#Symbol_of_Bravery# +7813#Red_Jenoss’s_Family_Ring# +7814#Green_Jenoss’s_Family_Ring# +7815#Blue_Jenoss’s_Family_Ring# +7816#푸른묘안석# +7817#Symbol_of_Peace# +7818#Jessur's_Necklace# + +// ----------- 러시아 신비로운 피 이벤트 ------------------ +11703#신비로운_피# + +// ---------- 2007 대만 폭죽 ---------- +14546#사랑해_폭죽# +14547#화이트데이_폭죽# +14548#발렌타인데이_폭죽# +14549#생일_폭죽# +14550#크리스마스_폭죽# + +// ---------- 2007 대만 폭죽 상자---------- +13804#사랑해_폭죽_상자# +13805#화이트데이_폭죽_상자# +13806#발렌타인데이_폭죽_상자# +13807#생일_폭죽_상자# +13808#크리스마스_폭죽_상자# + +// ------------- 20070822 임대아이템 추가 -------------- +13407#네이건# +1424#꼬챙이# +1628#생존의_지팡이# +1537#쿼드릴# +1567#양장본# +13408#화이어_브랜드# +13409#무형검# +1378#그레이트액스# +1273#블러디로어# +1274#언홀리터치# +2535#생존의_망토# +5326#나비_가면# +5328#악마의_날개# +5329#검은_눈가리개# +5330#늘어진_고양이# +5331#커세어# +2710#피묻은_철구# +2711#스피리츄얼_링# +5327#오크히어로의_투구# +13766#네이건_상자# +13767#꼬챙이_상자# +13768#생존의_지팡이_상자# +13769#쿼드릴_상자# +13770#그레이트액스_상자# +13771#블러디로어_상자# +13772#양장본_상자# +13773#화이어_브랜드_상자# +13774#무형검_상자# +13775#언홀리터치_상자# +13776#생존의_망토_상자# +13777#나비_가면_상자# +13778#오크히어로의_투구_상자# +13779#악마의_날개귀_상자# +13780#검은_눈가리개_상자# +13781#늘어진_고양이_상자# +13782#커세어_상자# +13783#피묻은_철구_상자# +13784#스피리츄얼링_상자# +13785#네이건_상자# +13786#꼬챙이_상자# +13787#생존의_지팡이_상자# +13788#쿼드릴_상자# +13789#그레이트액스_상자# +13790#블러디로어_상자# +13791#양장본_상자# +13792#화이어_브랜드_상자# +13793#무형검_상자# +13794#언홀리터치_상자# +13795#생존의_망토_상자# +13796#나비_가면_상자# +13797#오크히어로의_투구_상자# +13798#악마의_날개귀_상자# +13799#검은_눈가리개_상자# +13800#늘어진_고양이_상자# +13801#커세어_상자# +13802#피묻은_철구_상자# +13803#스피리츄얼_링_상자# + +// ------------ 노키아 ppl 아이템 --------------- +7819#노키아5300# + +// ------------- 모로크의 피부조각 ------------- +7820#모로크의_피부조각# + +// ------------- 애장판 북 아이템 ---------------- +5332#로키_마스크# +2712#애장판북# + +// ----------- 베트남 나탈리아 모자 ------------------ +5806#시인_나탈리아의_모자# + +// ---------------- 인도네시아 가벼운 캐쉬 아이템 -------------- +13809#블루젬스톤_상자# +13810#파란포션_상자# +13811#음식상자_Vol_1# +13812#음식상자_Vol_2# +13813#음식상자_Vol_3# +11502#가벼운_파란포션# +14551#메뚜기_볶음# +14552#물갈퀴_무침# +14553#봄버_스테이크# +14554#포도과즙_허브티# +14555#만엽의_홍차# +14556#허브_꿀차# +14557#게_집게발_찜# +14558#해산물_요리# +14559#조개탕# +14560#개구리알_먹물_스프# +14561#미끌미끌_국수# +14562#촉수_치즈_그라탕# +14563#꿀포도_쥬스# +14564#초콜렛무스_케이크# +14565#후르츠_믹스# +14566#원숭이_꼬리볶음# +14567#혼합쥬스# +14568#맛탕# +13814#인도네시아_상자# + +// ----------- PC방 전용 마패 ---------- +12262#마패# + +// ----------- 대만 펫 추가 ------------------ +9032#단검고블린의_알# +9033#플레일고블린의_알# +9034#해머고블린의_알# +9035#딜리터의_알# +9036#디아볼릭의_알# +9037#배회하는_자의_알# +13815#단검고블린_테이밍_상자# +13816#플레일고블린_테이밍_상자# +13817#해머고블린_테이밍_상자# +13818#딜리터_테이밍_상자# +13819#디아볼릭_테이밍_상자# +13820#배회하는자_테이밍_상자# +14569#단검고블린의_반지# +14570#플레고블린의_반지# +14571#해머고블린의_반지# +14572#홀리_마블# +14573#붉게_타는_돌# +14574#낭인의_해골# +7821#초록색_사과# +7822#통_바베큐# +7823#고기야채꼬치# +7824#혼령주# +13821#초록색_사과_상자# +13822#통_바비큐_상자# +13823#고기야채꼬치_상자# +13824#혼령주_상자# + +// ---------- 김밥군 이벤트 ----------- +13825#오래된_초록상자# + +// ---------------- 인도네시아 RIC 2007 패키지 -------------- +13826#파워박스_1호# +13827#파워박스_2호# +13828#레지스트박스_ 1호# +13829#레지스트박스_2호# +13830#스텟부스트박스_1호# +13831#스텟부스트박스_2호# +13832#스텟부스트박스_3호# +13833#스텟부스트박스_4호# +14575#루티에_아줌마의_부침개# +14576#마스테라주# +14577#매콤하게_구운만두# +14578#박쥐날개_호박찜# +14579#녹색의_샐러드# +14580#전갈볶음# + +// ---------------- 던전 이동 스크롤 2 ----------------- +13834#던전_이동_스크롤_II_5개_상자# +13835#던전_이동_스크롤_II_10개_상자# +14581#던전_이동_스크롤_II# + +// -------------- 보상용 아이템 ----------------- +12263#전투_교범# +12264#풍선껌# +12265#생명보험_증서# + +// -------------- 2007년 9월 일본 모바일 투구 13종 ----------------- +5333#전파_안테나# +5334#날고싶은_엔젤링# +5335#폴짝이는_포링# +5336#길드원_모집_광고판# +5337#파티원_모집_광고판# +5338#남친_구함!_광고판# +5339#친구_모집_광고판# +5340#디포르테_인형모자# +5341#글라리스_인형모자# +5342#소린_인형모자# +5343#테일링_인형모자# +5344#비닛_인형모자# +5345#더블류_인형모자# +5346#여친_구함!_광고판# + +// ------------------ 하악 테스트 ---------------- +5353#태양신의_모자# +2434#검은_가죽부츠# +5351#장식용_해바라기# +5347#학사모# +5348#왕_리본# +5349#학생모# +5350#해적_두건# +2371#삼각팬티# +2372#매직코트# +2373#홀리_로브# +2128#신의_사자# +2432#뾰족구두# +2715#해골반지# +1568#거친_파도의_서# +1569#갈라진_대지의_서# +1570#타오르는_태양의_서# +1571#메마른_바람의_서# +1309#오키쉬_액스# +1538#스파이크# +1539#골든_메이스# +1540#그랜드_크로스# +13030#드래곤킬러# +13031#소드_브레이커# +13032#메일_브레이커# +13033#어새신대거# +1922#비파# +1976#여왕님의_채찍# +1275#시린_고드름의_카타르# +1276#먼지낀_가시_덤불_카타르# +1277#분출하는_불꽃의_카타르# +1278#살을_에는_바람의_카타르# +1479#요괴의_창# +1480#가에볼그# +1481#제퓨로스# +1178#슈바이체르샤벨# +1179#액서큐셔너# +1180#드래곤슬레이어# + +// ----------------- 필리핀_미드가드 -------------- +7891#머그컵# +5807#옥토버페스트_모자# +12711#프레첼# +12712#초록맥주# + +// -------------- 일본 모바일 아이템 ----------------- +13836#힘_요리_상자# +13837#어질_요리_상자# +13838#인트_요리_상자# +13839#덱스_요리_상자# +13840#럭_요리_상자# +13841#바이탈_요리_상자# +13842#카프라_월급_명세서_상자# +13843#전투교범_상자# + +// ---------------- 인도네시아 가벼운 캐쉬 아이템 -------------- +13844#영웅의_돌_상자# +7825#영웅의_돌# + +// ---------- 대만 이벤트 아이템 포포링 모자 ----------- +5352#포포링_모자# + +// ------------------- 모로크 컨티넨탈 가드 인증서 ------------------ +7826#컨티넨탈_가드_인증서# + +// --------------- 말레이시아 싱가폴 Fasting 이벤트 --------------- +11704#KETUPAT# + +// ----------- 추석 PC방 아이템 ----------- +12266#다식# +12267#유과# +12268#무지개떡# + +// ------------ 마왕 모로크 ---------------- +2536#스킨_오브_벤투스# +2537#디아볼루스_망토# +2728#저주받은_손# +2729#디아볼루스_링# +2730#컨티넨탈_가드의_인장# +2731#룬문자_주문석# +2732#죽음의_고리# +5808#다크_바실리움# +2374#디아볼루스_로브# +2375#디아볼루스_아머# +2433#디아볼루스_부츠# + +// ---------- 할짝 테스트 ----------- +2129#구마의_성서# +2130#크로스쉴드# +2131#마력의_서_제1권# +2716#사서의_장갑# +2717#회중시계# +2718#루나틱_브로치# +2719#아이언_리스트# +2720#무공훈장# +2721#무공훈장# +2722#무공훈장# +2723#무공훈장# +2724#무공훈장# +2725#무공훈장# +2726#이카루스의_날개# +2727#명궁의_스카프# +1629#신사의_지팡이# +1630#릴리즈_오브_위스# +1631#홀리스틱# +1541#네메시스# +13034#사막의_황혼# +13035#모래폭풍# +1572#마법의_정석# +1573#고대의_마법# +1736#더블바운드# +1737#익시온의_날개# +1181#태구련# +1182#블러디이터# + +// ------------- 대만 신비로운 여행가방 ------------------ +13845#신비로운_여행가방_A# +13846#신비로운_여행가방_B# +13847#신비로운_여행가방_C# +13848#신비로운_여행가방_D# + +// ----------- 20071004 캐시아이템 ----------- +13849#노랑나비의_날개_5개_상자# +13850#노랑나비의_날개_10개_상자# +13851#초록나비의_날개_5개_상자# +13852#초록나비의_날개_10개_상자# +13853#빨간나비의_날개_5개_상자# +13854#빨간나비의_날개_10개_상자# +13855#파란나비의_날개_5개_상자# +13856#파란나비의_날개_10개_상자# +13857#톡톡캔디_5개_상자# +13858#톡톡캔디_10개_상자# +14582#노랑나비의_날개# +14583#초록나비의_날개# +14584#빨간나비의_날개# +14585#파란나비의_날개# +14586#톡톡캔디# + +//-------------------- 해외 이벤트 아이템---인도------------------------------------------- +5809#붐붐모자# +7892#숯# +7893#유황# +7894#질산칼륨# + +// ----- 말레이시아 무슬림 모자 -------- +5354#SONGKOK# +5355#SELENDANG# + +// ----------- 일본 캐쉬템 체험판 --------------- +13861#전투교범의_작은_박스# +13862#트라이얼_박스# + +// ----------------- 러시아 이벤트 아이템 ------------------ +7760#야가_할머니의_마법서# +7761#마법의_호리병# +7762#야가_할머니의_절구공이# +7763#찰싹찰싹_풀# +7764#강력_접착제# +7765#바바야가의_비약# +2707#GUSLI# + +// ------------- 모로크 ppl 아이템 -------------- +7827#광물_감정서# + +// ------------- 전장 휘장 --------------- +7828#용맹휘장# +7829#특공휘장# + +// ------------- 할로윈 축제모자 --------------- +5356#축제용_호박모자# + +// -------------- 태국 라마 5세 이벤트 --------------- +7895#라마5세_기념책# +7896#로이크라통_기념책# +7897#제헌절_기념책# +7898#무지무지_센_발뭉# +7899#심령술사의_단도# + +// ---------------- 태국 모험가의 전설 추가 작업분 -------------- +7900#제노스가의_반지# +7901#제노스가의_반지# +7902#제노스가의_반지# +7903#제노스가의_반지# +7904#피아노의_열쇠# + +// ----------- 신급 퀘스트 아이템 -------------- +7839#크리스탈_키# + +// ------------- pc방 아이템 --------------- +12274#대환단# +12275#태청단# + +// ---------------- 20071024 캐쉬아이템 ----------------- +13863#무기_수리_주문서_10개_상자# +13864#하키마스크_상자# +13865#옵저버_상자# + +// -------------- 일본 몬스터 사이드 스토리 이벤트 ----------------- +2714#신기한_팬던트# + +// ------------ 신급 퀘스트 아이템 발키리의 선물 --------------- +7840#발키리의_선물# + +// ---------- 필리핀 승리의 날개 ----------------- +5357#승리의_날개# + +// ---------------- 20071029 영구투구 상자 ----------------- +13866#비상하는_천사_상자# +13867#네코미미_상자# +13868#월야화_모자_상자# +13869#병아리_모자_상자# + +// -------- 필리핀 락스타 뱃지 2 ----------- +7905#Rok_Star_뱃지# + +// ------------- 신규 신급 아이템 -------------- +2383#브륀힐트# +2541#아스프리카# +7830#여신의_눈물# +7831#발키리의_징표# +7832#브륀힐트의_갑옷조각# +7833#영웅의_유해# +7834#안드바리의_반지# +7835#황혼의_노을# +7836#여명의_정수# +7837#차가운_달빛# +7838#어스름한_별빛# + +// ------- 미국 휴전기념 화환 ----------- +7906#화환# + +// ---------- 인도어린이날 아이템 ---------------- +11705#어린이_물약# + +// ------------- 전장 보상 아이템 --------------- +13036#암살자의_다마스커스# +13037#암살자의_다마스커스# +13410#검투사의_블레이드# +13411#검투사의_블레이드# +1425#돌격대장의_스피어# +1632#워락의_마법지팡이# +1633#워락의_전투지팡이# +1634#강한_회복의_지팡이# +1635#빠른_회복의_지팡이# +1542#투사의_모닝스타# +1543#투사의_모닝스타# +1923#전장의_기타# +1924#전장의_기타# +1977#배틀_라리어트# +1978#배틀_라리어트# +1574#손자병법# +1575#손자병법# +1823#투사의_배틀_피스트# +1824#투사의_배틀_피스트# +1183#돌격대장의_카츠발게르# +1184#돌격대장의_카츠발게르# +1482#돌격대장의_랜스# +1379#광전사의_배틀엑스# +1380#광전사의_배틀엑스# +13305#전투_풍마수리검# +13306#전투_풍마수리검# +1279#학살의_카타르# +1280#학살의_카타르# +1738#배틀_크로스보우# +1739#배틀_크로스보우# +13108#솔져_리볼버# +13171#솔져_라이플# +13172#솔져_개틀링건# +13173#솔져_샷건# +13174#솔져_그레네이드_런쳐# + +2538#사령관의_망토# +2539#지휘관의_망토# +2540#보안관의_망토# +2435#전투_그리브# +2436#군화# +2437#전투_부츠# +2376#돌격대장의_플레이트# +2377#정예공병의_갑옷# +2378#암살자의_로브# +2379#워락의_전투로브# +2380#위생병의_로브# +2381#정예궁병의_슈츠# +2382#정예사수의_슈츠# +2733#보안관_뱃지# +2734#임무명령서# +2735#임무명령서# +12269#맛대령# +12270#맛소령# +12271#군용식량A# +12272#군용식량B# +12273#군용식량C# +13859#임무명령서_봉투# +13860#임무명령서_봉투# + +// ------------------- 몬스터 용병 앨리스 미믹 디스가이즈 ------------------ +12276#미믹_소환서# +12277#디스가이즈_소환서# +12278#엘리스_소환서# + +// ---------- 러시아 보물사냥꾼 이벤트 ------------- +7841#얼룩진_종이조각# +7842#찢어진_종이조각# +7843#낡은_종이조각# +7844#그을린_종이조각# +7845#얼룩진_종이조각의_사본# +7846#찢어진_종이조각의_사본# +7847#낡은_종이조각의_사본# +7848#그을린_종이조각의_사본# + +// ----------- 베트남 스승의 날 이벤트 아이템 ------------ +5810#수료의_모자# + +// -------------- 2007 하반기용 일본투구 ----------------- +5358#페코페코의_날개귀# +5361#갱_스카프# +5362#닌자의_두루마리# + +// ----------- 해외 아이템 ------------- +5366#왕의_모자# + +// --------- 인도네시아 다크윙 아이템 ---------- +5368#빛의_날개# +5369#어둠의_날개# +12279#언데드속성_스크롤# +12280#성속성_스크롤# + +// ---------- 필리핀 몬스터 에센스 --------------- +12713#몬스터에센스# + +// ---------- 러시아 여신의 운명 이벤트 --------------- +7907#여신의_실타래# + +// -------- 공성 1.5 업데이트 기념 이벤트 아이템 ---------- +12281#낡은_상자# +7849#영혼이_담긴_수정# + +// ---------------- 캐쉬 헤어스타일 교환권 ----------------- +13870#미용상품권_상자# +7622#미용상품권# + +// ------------------- 이벤트 아이템 ------------------ +6000#어둠의_재# + +// ------------- 필리핀 박스 패키지 아이템 ------------------ +13871#매지션_카드박스# +13872#어콜라이트_카드박스# +13873#아쳐_카드박스# +13874#소드맨_카드박스# +13875#씨프_카드박스# +13876#머천트_카드박스# +13877#시계탑_카드박스# +13878#게페니아_카드박스# +13879#아울_카드박스# +13880#유령_카드박스# +13881#악몽의_카드박스# +13882#저주의_카드박스# +13883#수면의_카드박스# +13884#동빙의_카드박스# +13885#기절의_카드박스# +13886#침묵의_카드박스# +13887#암흑의_카드박스# +13888#혼란의_카드박스# +13889#에르늄_광물상자# +13890#오리데오콘_광물상자# +13891#화속성_엘리멘탈_컨버터_상자# +13892#수속성_엘리멘탈_컨버터_상자# +13893#풍속성_엘리멘탈_컨버터_상자# +13894#지속성_엘리멘탈_컨버터_상자# + +// -------- 봉인의 신전 아이템 ------------ +6001#불의_정수# +6002#사도의_징표# +6003#영혼의_팬던트# + +// -------- 태국 로이크라통 난초머리띠 --------- +5370#난초머리띠# + +// ---------------- 장비수리주문서_ ----------------- +14587#무기_수리_주문서# + +// ---------------- 프랑스 패키지 추가 ----------------- +13895#스타터_팩# + +// ------------ 메모리얼 던전 보상템 ---------------- +13412#나흐트지거의_트윈엣지(B)# +13413#나흐트지거의_트윈엣지(R)# +1636#어둠의_가시나무_지팡이# +1185#바이올렛_피어# +2542#나흐트지거의_불꽃망토# + +// -------- 태국 제헌절 이벤트 아이템 ------------ +5371#판관모자# + +// ----- 크리스마스 이벤트 산타 수염 -------- +7908#루이제의_미용쿠폰# +7909#도난_당한_과자# +7910#도난_당한_사탕# +7911#율리의_모자# + +// ----------- 20071128 캐쉬 몬스터 용병 ----------- +13896#미믹_소환서_상자_5# +13897#디스가이즈_소환서_상자_5# +13898#엘리스_소환서_상자_5# +13899#미믹_소환서_상자_10# +13900#디스가이즈_소환서_상자_10# +13901#엘리스_소환서_상자_10# + +// ------- 일본 유저공모 모자 ----------- +5379#벌룬햇# + +// -------- 2007 일본 연말 캐쉬아이템 ---------- +5380#물고기_머리_모자# +5381#산타포링_모자# +5382#방울리본# +13902#물고기_머리_모자_상자# +13903#산타포링_모자_상자# +13904#방울리본_상자# + +// ------------------- 가장무도회상자 2 ------------------ +12286#가장_무도회_상자_2# + +// ---------- 모바일 아이템 ------------ +5383#헌팅캡# +2736#배꼽찌# +2737#발찌# + +// ---------------- 러시아 캐쉬템 패키지 추가 ----------------- +13905#XM_Hardcore_Set_Box# +13906#XM_Kitty_Set_Box# +13907#XM_Softcore_Set_Box# +13908#XM_Deviruchi_Set_Box# +13909#XM_Mvphunt_Set_Box# +13910#XM_Brewing_Set_Box# + +// ---------- 대만 크리스마스 펫 스크롤 ----------- +13911#크리스마스_펫_스크롤# + + +// --------------- 중국 캐쉬아이템 -------------- +13912#파티_블레싱_상자# +13913#파티_민첩성증가_상자# +13914#파티_아숨프티오_상자# +14588#파티_블레싱_스크롤# +14589#파티_민첩성증가_스크롤# +14590#파티_아숨프티오_스크롤# + +// ---------------- 테스트 아이템 ----------------- +13915#러브앤젤_마법가루_상자# +13916#스퀴럴_마법가루_상자# +13917#고고_마법가루_상자# +12287#러브앤젤_마법가루# +12288#스퀴럴_마법가루# +12289#고고_마법가루# +13942#러브앤젤_마법가루_상자_30일# +13943#스퀴럴_마법가루_상자_30일# +13944#고고_마법가루_상자_30일# + +// ----------- 2008 신년 이벤트 아이템 ------------ +2438#고양이의_발# +12290#신묘_통조림# +12291#신묘_패트병# +12292#덜익은_이그드라실열매# +12293#건조한_이그드라실열매# + +// --------------- 필리판 자체제작 투구 7종 ------------- +5372#늘어진_하얀_고양이# +5373#다크네스_헬름# +5374#대형_마제스틱_고우트# +5375#대형_오크히어로의_투구# +5376#사타닉_체인# +5377#앤티크_담뱃대# +5378#토끼귀_모자# +13918#늘어진_하얀_고양이_상자# +13919#대형_마제스틱_고우트_상자# +13920#사타닉_체인_상자# +13921#앤티크_담뱃대_상자# +13922#토끼귀_모자_상자# +13923#다크네스_헬름_상자# +13924#대형_오크히어로의_투구_상자# + + +// ----------------- 마력이 깃든 바포메트 인형 ----------------- +6004#마력이_깃든_바포메트_인형# + +// --------- 태국 요요 모자 ------------- +5385#요요모자# + +// ----------- 미국 이벤트 아이템 ---------- +7912#휴대용_눈사람_기계# + +// ------- 대만 2008 새해 스크롤 ----------- +13925#쥐띠해_행운_스크롤# + +// ----------- PC방 쿠폰 아이템 ------------ +2738#반짝이는_기념주화# +2739#일반_기념주화# +2740#녹슨_기념주화# +12294#PC방_코인상자_1# +12295#PC방_코인상자_2# +12296#PC방_코인상자_3# +12297#PC방_코인상자_4# + +// ----------- 필리핀 카드 박스 --------------- +13926#크루_카드박스# +13927#알케_카드박스# +13928#로그_카드박스# +13929#바댄_카드박스# +13930#세이지_카드박스# +13931#몽크_카드박스# +13932#실프_박스# +13933#운디네_박스# +13934#샐러맨더_박스# +13935#소울_박스# +13936#노움_박스# + +// ----------- 대만 펫 추가 ------------------ +9038#새해인형의_알# + +// ---------------- 20080116 영구투구 상자 ----------------- +13937#로보아이_상자# +13938#트윈리본_상자# +13939#다이아뎀_상자# + +// --------- 설날 이벤트 아이템 --------------- +5386#새해_복_모자# +6005#새해_떡# +6006#배달용_떡_상자# +6007#새해_떡국# + +// -------------- 베트남 Midgard army recruitment --------------- +7913#전투_테스트_증서# + +// --------- 태국 모자 아이템 ------------- +5387#네코미미_카프라밴드# +5388#뱀머리_모자# +5389#천사의_유령# + +// ------------- 프랑스 산타모자 -------------- +5390#파란_양갈래_산타모자# + + +// ---------- 대만발렌타인 스크롤 ------------- +13941#대만_발렌타인_스크롤# + +// ------------ 브라질 1차직업 패키지 상자 -------------- +13945#브라질_검사_패키지# +13946#브라질_마법사_패키지# +13947#브라질_복사_패키지# +13948#브라질_궁수_패키지# +13949#브라질_상인_패키지# +13950#브라질_도둑_패키지# + +// ----------- 피씨방 지급 아이템 ------------ +12298#SP_소모량_감소포션# +12299#상태이상_저항포션# + +// ------------- 브라질 이름변경 쿠폰 ---------------- +13960#이름변경_쿠폰_상자# +7623#이름변경_쿠폰# + +// --------- 인도네시아 모자아이템 ------------- +5392#뤄양의_설날모자# +5393#발렌타인_모자# + +// ------------ 대만 모찌상자 ----------- +13961#모찌상자# + +// ---------------- 2008년 2월 임대아이템 장비 ----------------- +13109#황야의_무법자# +13175#레버액션_라이플# +2741#올인원_반지# +2384#스피리츄얼_튜닉# +2385#리큐퍼레이티브_아머# +2132#쉘터_오브_레지스턴스# +2543#실피드_망토# +2439#리프레쉬_슈즈# +5391#토스트# +13951#황야의_무법자_상자# +13952#레버액션_라이플_상자# +13953#올인원_반지_상자# +13954#스피리츄얼_튜닉_상자# +13955#리큐퍼레이티브_아머_상자# +13956#쉘터_오브_레지스턴스_상자# +13957#실피드_망토_상자# +13958#리프레쉬_슈즈_상자# +13959#토스트_상자# + +//============2008 대만 부활절 이벤트 아이템 ============ +7914#고대언어의_문서# +12714#부활절_스크롤# + +// ---------------- 카프라 투구 상자 ----------------- +13962#디포르테_인형모자_상자# +13963#글라리스_인형모자_상자# +13964#소린_인형모자_상자# +13965#테일링_인형모자_상자# +13966#비닛_인형모자_상자# +13967#더블류_인형모자_상자# + +// --------- 목재 아이템 --------------- +6008#목재# + +// ================= 태국 '세계물의날 이벤트 ================ +5812#물_전문가의_모자# + +// ------------- 프랑스 프리미엄 서비스 아이템 -------------- +5394#맛있는_풍선껌# +2742#행운의_클립# +13968#맛있는_풍선껌_상자# +13969#행운의_클립_상자# + +// ------------- 브라질 필리핀 철,강철,석탄상자 ---------------- +13970#철_상자# +13971#강철_상자# +13972#석탄_상자# + +// ----------- 말레이시아 싱가폴 독약병 상자 --------------- +13973#독약병_상자# + +//============2008 미국 세인트패트릭 이벤트 아이템============ +7915#코퍼_코인# +7916#실버_코인# +12715#검은색_보물_상자# + +// --------- 2008 솔로 이벤트 아이템 --------------- +12300#와일드_로즈_소환서# +12301#도플갱어_소환서# +12302#이그니젬_세니아_소환서# + +// ------------- 유럽 4주년 기념모자 -------------- +5395#티라야_보넷# + +// ------------- 유럽 공모전 모자 -------------- +5396#재스퍼_크레스트# + +// -------- 태국 쏭크란 아이템 ---------- +12303#축복의_물# + +// --------- 태국 스쿠버 마스크 ------------- +5397#스쿠버_마스크# + +// ------------ 대만 어부스크롤 ----------- +13974#어부_스크롤# + +// ---------------- 20080326 폰트 6종 ----------------- +12304#그림일기_마법가루# +12305#미니하트_마법가루# +12306#새내기_마법가루# +12307#꼬꼬마_마법가루# +12308#마법의_성_마법가루# +12309#짱구_마법가루# +13975#그림일기_마법가루_상자# +13976#미니하트_마법가루_상자# +13977#새내기_마법가루_상자# +13978#꼬꼬마_마법가루_상자# +13979#마법의_성_마법가루_상자# +13980#짱구_마법가루_상자# +13981#그림일기_마법가루_30일_상자# +13982#미니하트_마법가루_30일_상자# +13983#새내기_마법가루_30일_상자# +13984#꼬꼬마_마법가루_30일_상자# +13985#마법의_성_마법가루_30일_상자# +13986#짱구_마법가루_30일_상자# + +// --------- 2008 봄맞이 이벤트 아이템 --------------- +12310#꽃가지# +12311#커다란_꽃가지# +6009#마력의_대형부채# + +// --------- 길드던전 이벤트 아이템 --------------- +6010#곡괭이# + +// ---------- 필리핀 3월 박스 패키지 ---------------- +13987#오리데오콘원석_5개_박스# +13988#오리데오콘원석_50개_박스# +13989#애시드폭탄_10개_박스# + +//-------------- 2008 태국 여우모자 --------------- +5400#여우모자# + +// ------------- 필리핀 클린업 이벤트 --------------- +2441#비치샌들# +2546#비치망토# + +//===============2008 인도 '펫 러버' 이벤트==================== +12716#인도_쌀떡# + +// -------------- 2008 일본 아이템 제 2탄 ----------------- +5401#검은_뿔테안경# +5402#장난꾸러기_요정# +5403#입에_문_물고기# +5404#블루리본# +5405#머리에_타고_있는_필리르# +13990#JOB_전투교범_상자# +14592#JOB_전투교범# +12312#두꺼운_전투교범# + +// ---------- 러시아 캐쉬모자 3종 ------------ +13991#타이거_마스크_상자# +13992#고양이_모자_상자# +13993#엘리스_모자_상자# + +// -------------- 2008 일본 아이템 투구 2탄 ----------------- +5363#심연의_투구# +// -----현재번호일본안씀---- +5364#흑사왕의_모자# +5367#혜군_모자# + +// --------- 2008년 5월 감사의 마음 이벤트 --------------- +12313#천사의_수호# + +// ---------- 필리핀 4월 박스 패키지 ---------------- +13994#이동속도_증가_포션_5개_상자# +13995#이동속도_증가_포션_10개_상자# +13996#왕만두_100개_상자# +13997#왕만두_500개_상자# +13998#거대한_파리의_날개_500개_상자# +13999#환약_100개_상자# +14000#환약_500개_상자# +14001#공성_신참_보급품_상자# +14002#공성_고참_보급품_상자# +14003#공성_정예_보급품_상자# +11503#공성용_화이트_포션# +11504#공성용_파란_포션# + +// ----------- 필리핀 독약병 상자 추가 ------------ +14004#독약병10상자# +14005#독약병5상자# + +// --------- 2008 꿈을 모으는 기계 이벤트 --------------- +6011#파란B카드# +6012#파란C카드# +6013#파란J카드# +6015#파란M카드# +6016#파란Q카드# +6017#파란T카드# +6018#파란V카드# +6019#파란Z카드# +2750#한_여름_밤의_꿈# + +// ---------------- 테스트용 캐쉬아이템 상자 ----------------- +14006#늘어진_하얀_고양이_상자# +14007#토끼귀_모자_상자# +14008#대형_오크히어로의_투구_상자# +14009#러브앤젤_마법가루_상자# +14010#스퀴럴_마법가루_상자# +14011#고고_마법가루_상자# +14012#러브앤젤_마법가루_상자_30일# +14013#스퀴럴_마법가루_상자_30일# +14014#고고_마법가루_상자_30일# +14015#황야의_무법자_상자# +14016#레버액션_라이플_상자# +14017#올인원_반지_상자# +14018#스피리츄얼_튜닉_상자# +14019#리큐퍼레이티브_아머_상자# +14020#쉘터_오브_레지스턴스_상자# +14021#실피드_망토_상자# +14022#리프레쉬_슈즈_상자# +14023#토스트_상자# +14024#로보아이_상자# +14025#트윈리본_상자# +14026#다이아뎀_상자# +14027#물고기_머리_모자_상자# +14028#산타포링_모자_상자# +14029#방울리본_상자# +14030#미믹_소환서_상자_5# +14031#디스가이즈_소환서_상자_5# +14032#엘리스_소환서_상자_5# +14033#미믹_소환서_상자_10# +14034#디스가이즈_소환서_상자_10# +14035#엘리스_소환서_상자_10# +14036#미용상품권_상자# +14037#무기수리_스크롤_상자# +14038#무기_수리_주문서_10개_상자# +14039#하키마스크_상자# +14040#옵저버_상자# +14041#노랑나비의_날개_5개_상자# +14042#노랑나비의_날개_10개_상자# +14043#초록나비의_날개_5개_상자# +14044#초록나비의_날개_10개_상자# +14045#빨간나비의_날개_5개_상자# +14046#빨간나비의_날개_10개_상자# +14047#파란나비의_날개_5개_상자# +14048#파란나비의_날개_10개_상자# +14049#톡톡캔디_5개_상자# +14050#톡톡캔디_10개_상자# +14051#던전_이동_스크롤_II_5개_상자# +14052#던전_이동_스크롤_II_10개_상자# +14053#리틀_엔젤_돌_상자# +14054#포링_삼단모자_상자# +14055#네이건_상자# +14056#꼬챙이_상자# +14057#생존의_지팡이_상자# +14058#쿼드릴_상자# +14059#그레이트액스_상자# +14060#블러디로어_상자# +14061#양장본_상자# +14062#화이어_브랜드_상자# +14063#무형검_상자# +14064#언홀리터치_상자# +14065#생존의_망토_상자# +14066#나비_가면_상자# +14067#오크히어로의_투구_상자# +14068#악마의_날개귀_상자# +14069#검은_눈가리개_상자# +14070#늘어진_고양이_상자# +14071#커세어_상자# +14072#피묻은_철구_상자# +14073#스피리츄얼링_상자# +14074#환영의_술잔_5개_상자# +14075#환영의_술잔_10개_상자# +14076#쉐도우_아머_스크롤_5개_상자# +14077#쉐도우_아머_스크롤_10개_상자# +14078#쉐도우_아머_스크롤_30개_상자# +14079#홀리_아머_스크롤_5개_상자# +14080#홀리_아머_스크롤_10개_상자# +14081#홀리_아머_스크롤_30개_상자# +14082#소형_물리_방어_포션_10개_상자# +14083#소형_물리_방어_포션_30개_상자# +14084#소형_물리_방어_포션_50개_상자# +14085#대형_물리_방어_포션_10개_상자# +14086#대형_물리_방어_포션_30개_상자# +14087#대형_물리_방어_포션_50개_상자# +14088#소형_마법_방어_포션_10개_상자# +14089#소형_마법_방어_포션_30개_상자# +14090#소형_마법_방어_포션_50개_상자# +14091#대형_마법_방어_포션_10개_상자# +14092#대형_마법_방어_포션_30개_상자# +14093#대형_마법_방어_포션_50개_상자# +14094#비상하는_천사_상자# +14095#네코미미_상자# +14096#월야화_모자_상자# +14097#병아리_모자_상자# +14098#페코페코_머리띠_상자# +14099#빨간안경_상자# +14100#위스퍼_마스크_상자# +14101#라면모자_상자# +14102#던전_순간이동_스크롤_5개_상자# +14103#헬스장_회원권_상자# +14104#소형_생명수_10개_상자# +14105#소형_생명수_30개_상자# +14106#소형_생명수_50개_상자# +14107#중형_생명수_10개_상자# +14108#중형_생명수_30개_상자# +14109#중형_생명수_50개_상자# +14110#연마제_5개_상자# +14111#연마제_10개_상자# +14112#회복력_향상포션_5개_상자# +14113#회복력_향상포션_10개_상자# +14114#던전_순간이동_스크롤_10개_상자# +14115#등뒤를_베는자_상자# +14116#무라마사_상자# +14117#엑스칼리버_상자# +14118#컴뱃나이프_상자# +14119#카운터단검_상자# +14120#카이저너클_상자# +14121#마이트스태프_상자# +14122#라이트엡실론_상자# +14123#발리스타_상자# +14124#현자의_일기_상자# +14125#아슈라_상자# +14126#명사수의_사과_상자# +14127#토끼머리띠_상자# +14128#삿갓_상자# +14129#로드서클릿_상자# +14130#요정의_귀_상자# +14131#강철의_꽃잎_상자# +14132#크리티컬링_상자# +14133#이어링_상자# +14134#링_상자# +14135#네클리스_상자# +14136#글러브_상자# +14137#브로치_상자# +14138#로자리_상자# +14139#세이프티링_상자# +14140#베스퍼코어01_상자# +14141#베스퍼코어02_상자# +14142#베스퍼코어03_상자# +14143#베스퍼코어04_상자# +14144#1시간_패키지_Vol_1# +14145#1시간_패키지_Vol_2# +14146#1시간_패키지_Vol_3# +14147#1시간_패키지_Vol_4# +14148#1시간_패키지_Vol_5# +14149#1시간_패키지_Vol_6# +14150#2시간_패키지_Vol_1# +14151#2시간_패키지_Vol_2# +14152#2시간_패키지_Vol_3# +14153#2시간_패키지_Vol_4# +14154#2시간_패키지_Vol_5# +14155#2시간_패키지_Vol_6# +14156#전투교범_10개_상자# +14157#보험증서_10개_상자# +14158#풍선껌_10개_박스# +14159#혓바닥요리_10개_상자# +14160#사막전갈_모래찜_10개_상자# +14161#칵테일_용의_숨결_10개_상자# +14162#흐베르겔미르의_술_10개_상자# +14163#구미호_꼬리요리_10개_상자# +14164#불사의_찌개_10개_상자# +14165#카프라_명함_10개_상자# +14166#거대한_파리의_날개_10개_상자# +14167#섬광막대_상자# +14168#볼록거울_10개_상자# +14169#블레싱_스크롤_10개_상자# +14170#민첩성증가_스크롤_10개_상자# +14171#아스페르시오_스크롤_10개_상자# +14172#구_아숨프티오_스크롤_10개_상자# +14173#윈드워크_스크롤_10개_상자# +14174#아드레날린러쉬_스크롤_10개_상자# +14175#확성기_10개_상자# +14176#농축_에르늄_10개_상자# +14177#농축_오리데오콘_10개_상자# +14178#지크프리드의_증표_10개_상자# +14179#거대한_파리의_날개_50개_상자# +14180#거대한_파리의_날개_100개_상자# +14181#흐베르겔미르의_술_30개_상자# +14182#흐베르겔미르의_술_50개_상자# +14183#구미호_꼬리요리_30개_상자# +14184#구미호_꼬리요리_50개_상자# +14185#민첩성증가_스크롤_30개_상자# +14186#민첩성증가_스크롤_50개_상자# +14187#불사의_찌개_30개_상자# +14188#불사의_찌개_50개_상자# +14189#보험증서_30개_상자# +14190#보험증서_50개_상자# +14191#볼록거울_5개_상자# +14192#볼록거울_30개_상자# +14193#블레싱_스크롤_30개_상자# +14194#블레싱_스크롤_50개_상자# +14195#아드레날린러쉬_스크롤_30개_상자# +14196#아드레날린러쉬_스크롤_50개_상자# +14197#구_아숨프티오_스크롤_30개_상자# +14198#구_아숨프티오_스크롤_50개_상자# +14199#아스페르시오_스크롤_30개_상자# +14200#아스페르시오_스크롤_50개_상자# +14201#사막전갈_모래찜_30개_상자# +14202#사막전갈_모래찜_50개_상자# +14203#윈드워크_스크롤_30개_상자# +14204#윈드워크_스크롤_50개_상자# +14205#칵테일_용의_숨결_30개_상자# +14206#칵테일_용의_숨결_50개_상자# +14207#전투교범_상자# +14208#전투교범_5개_상자# +14209#지크프리드의_증표_5개_상자# +14210#지크프리드의_증표_20개_상자# +14211#카프라_명함_30개_상자# +14212#카프라_명함_50개_상자# +14213#혓바닥요리_30개_상자# +14214#혓바닥요리_50개_상자# +14215#풍선껌_박스# +14216#풍선껌_5개_박스# +14217#확성기_상자# +14218#확성기_5개_상자# +14219#농축_에르늄_5개_상자# +14220#농축_오리데오콘_5개_상자# + +// ---------- 2008년 4월 캐쉬아이템 --------------- +14221#마법력_증폭_스크롤_10개_상자# +14222#마법력_증폭_스크롤_30개_상자# +14223#마법력_증폭_스크롤_50개_상자# +14224#콰그스크롤_10개_상자# +14225#콰그스크롤_30개_상자# +14226#콰그스크롤_50개_상자# +14227#치유의_지팡이_상자# +14228#프락시너스_상자# +14593#마법력_증폭_스크롤# +14594#콰그_마이어_스크롤# +1638#치유의_지팡이# +2752#프락시너스# + +// =========== 2008 인도 '프리스트데이 이벤트'================ +7917#매직포션# + +// ------------ 대만 벚꽃스크롤 ----------- +14229#벚꽃_스크롤# + +// ----------- 필리핀 포가튼 메모리 ------------- +7918#기억의_파편# + +// ----------- 에피소드 13 잡템 ------------ +6020#모피# +6021#고깔모자# +6022#딱딱한_피부# +6023#마력의_뿔# + +// ---------- 러시아 음표헤드폰 상자 ------------ +14230#음표헤드폰_상자# + +// ------------ 2008-04 대만 이그, 고목상자 ----------- +14232#이그드라실_열매_10개_상자# +14233#고목나무가지_10개_상자# +14234#고목나무가지_25개_상자# + +// ------- 대만 17캐럿 다이아몬드 ----------- +6024#17캐럿_다이아몬드# + +// ---------- 2008-05 일본 캐쉬상자 ---------------- +14235#전투교범_2개_상자# +14236#혓바닥요리_20개의_상자# +14237#사막전갈_모래찜_20개의_상자# +14238#불사의_찌개_20개의_상자# +14239#칵테일_용의_숨결_20개의_상자# +14240#흐베르겔미르의_술_20개의_상자# +14241#구미호_꼬리요리_20개의_상자# + +// ----------- 게임문화 아이템 ------------ +2136#흠집난_버클러# +2392#오래된_팬티# +2442#구멍난_부츠# +2547#싸구려_런닝셔츠# +5408#낡은_두건# +14231#초보자용_상자# + +// -------------- 일본 이펙트 아이템 ----------------- +12315#여신의_축복# +12316#천사의_축복# +12317#파우더_눈# +12318#작은_하트# + +// -------- 태국 경험치 증가 반지 ----------- +2753#비홀더_링# +2754#핼로우_링# +2755#클래머러스_링# +2756#케미컬_링# +2757#인섹티사이드_링# +2758#피셔_링# +2759#디커세이트_링# +2760#블러디_링# +2761#사타닉_링# +2762#드라군_링# +14242#비홀더_링_상자# +14243#핼로우_링_상자# +14244#클래머러스_링_상자# +14245#케미컬_링_상자# +14246#인섹티사이드_링_상자# +14247#피셔_링_상자# +14248#디커세이트_링_상자# +14249#블러디_링_상자# +14250#사타닉_링_상자# +14251#드라군_링_상자# +14252#비홀더_링_상자Ⅱ# +14253#핼로우_링_상자Ⅱ# +14254#클래머러스_링_상자Ⅱ# +14255#케미컬_링_상자Ⅱ# +14256#인섹티사이드_링_상자Ⅱ# +14257#피셔_링_상자Ⅱ# +14258#디커세이트_링_상자Ⅱ# +14259#블러디_링_상자Ⅱ# +14260#사타닉_링_상자Ⅱ# +14261#드라군_링_상자Ⅱ# + +// -------- 미국 이벤트 아이템 ---------------- +6025#추억의_수건# + +// ---------------- 바포메트 서버 폰트 6종 ----------------- +14262#그림일기_마법가루_상자# +14263#미니하트_마법가루_상자# +14264#새내기_마법가루_상자# +14265#꼬꼬마_마법가루_상자# +14266#마법의_성_마법가루_상자# +14267#짱구_마법가루_상자# +14268#그림일기_마법가루_30일_상자# +14269#미니하트_마법가루_30일_상자# +14270#새내기_마법가루_30일_상자# +14271#꼬꼬마_마법가루_30일_상자# +14272#마법의_성_마법가루_30일_상자# +14273#짱구_마법가루_30일_상자# + +// ---------- 바포섭 마증, 콰그, 치유, 프락시너스 --------------- +14274#마법력_증폭_스크롤_10개_상자# +14275#마법력_증폭_스크롤_30개_상자# +14276#마법력_증폭_스크롤_50개_상자# +14277#콰그스크롤_10개_상자# +14278#콰그스크롤_30개_상자# +14279#콰그스크롤_50개_상자# +14280#치유의_지팡이_상자# +14281#프락시너스_상자# + +// ---------- 엠펠리움 상자 --------------- +14282#엠펠리움_상자# + +// ---------- 혼인 상자 --------------- +14283#혼인_서약서_상자# +6026#혼인_서약서# + +// ---------- 필리핀 5월 박스 패키지 ---------------- +14284#머플러_상자# +14285#발키리아_쉴드_상자# +14286#해골반지_상자# +14287#저지선_수리킷트# +14288#수호석_수리킷트# +2548#머플러# +2137#발키리아_쉴드# +2763#해골반지# + +//================= 2008.06월 브라질 '히어로즈' 이벤트아이템 ==================== +7920#영웅들의_무기함# + +// -------- 미국 페스티벌 티켓 ---------- +7919#페스티벌_티켓# + +// -------- 미국 이벤트 아이템 ---------------- +5409#보라색_카우보이_모자# +5410#갈색_종이봉투_모자# + +// --------- EP13 신규 장비 아이템 --------------- +5398#본헤드# +5399#만드라고라_캡# +2386#카멜레온_아머# +2387#스프린트_메일# +2388#칸두라# +2389#나가의_비늘갑옷# +2390#개량형_타이즈# +2391#라이프링크# +2440#스프린트_슈즈# +2544#텐드릴리온의_가죽# +2545#무시카# +2133#토너먼트_쉴드# +2134#나가의_비늘방패# +2135#쉐도우_가드# +2743#앤젤릭_링# +2744#스프린트_반지# +2745#핀귀큘라의_코르사주# +2746#냉정한_마음# +2747#블랙캣# +2748#커즈드_스타# +2749#리넨_글러브# +1186#죽음의_인도자# +1483#아이보리_랜스# +1484#카르도# +1485#배틀포크# +1740#네펜데스_보우# +1741#커즈드_라이어# +1637#이레이저# +13414#엘레멘탈_소드# +1544#루나칼리고# +1825#혼_오브_힐스리온# +13038#추적자의_단검# +13039#아이보리_나이프# +1979#스템_오브_네펜데스# +1980#추_채찍# +1925#첼로# +1926#하프_오브_네펜데스# + +// ---------- 에피소드 13 퀘스트 아이템 ------------ +6027#피_묻은_어둠의_결정체# +6028#봉인된_마법주문서# +6029#모로크_추적일지# +11011#바르문트의_수첩# +14595#봉인이_풀린_마법주문서# +6030#번쩍이는_종이# +6031#번쩍이는_종이# +6032#힐스리온의_뿔# +6033#텐드릴리온의_뿔# +6034#기묘한_부품# +6035#썩어가는_식물_줄기# +14596#피에르의_보물상자# + +// ---------- 필리핀 드래곤의 정수 ----------- +7921#드래곤의_정수# + +// ----------- 20080521 에피소드 13 퀘스트 아이템 ----------- +6036#회의_참가_요청서# +6037#엉성한_서류철# +6038#깔끔한_보고서# +6039#생선_토막# +6040#흩어진_보고서의_일부# +6041#튼튼한_넝쿨# +6042#평범한_나뭇가지# +6043#루겐의_편지# +6044#오토의_편지# +6045#배급상자# +11012#미드가르드_원정대의_보고서# +11013#원정대_보고서_1권# +11014#원정대_보고서_2권# +11015#원정대_보고서_3권# +11016#원정대_보고서_4권# +12319#룬_미드가츠산_딸기케익# +12320#슈발츠발드산_파인애플_쥬빌레# +12321#아루나펠츠산_사막_샌드위치# +12322#초콜렛_파이# + +// -------- 초보자존 아이템 ----------- +12323#초보자용_파리의_날개# +12324#초보자용_나비의_날개# +12325#초보자용_돋보기# +2393#초보자용_어드밴쳐러_슈츠# +13415#초보자용_폴쳔# +1639#초보자용_롯드# +13040#초보자용_커터# +13041#초보자용_망고슈# +1545#초보자용_메이스# +1742#초보자용_컴포지트보우# +1381#초보자용_배틀액스# + +// ------------- 2008 대만 단오절 --------------- +11505#창포# +5411#백사_머리띠# + +// --------- 옷염색 아이템 ------------ +6046#옷염색_쿠폰# +14289#옷염색_쿠폰_상자# +6047#옷염색_쿠폰_Ⅱ# +14290#옷염색_쿠폰_상자_Ⅱ# +14291#옷염색_쿠폰_상자# +14292#옷염색_쿠폰_상자_Ⅱ# + +// -------- 태국 금연이벤트 아이템 ---------- +5412#달콤한_사탕# + +// ----------- 이벤트 아이템 ------------ +2443#낚시장화# +2764#작은_낚시대# +6048#미확인_광석# +6049#청새치# + +//----------- 브라질 2_4분기 신규모자 -------------- +5413#팝콘_모자# +5414#캠프파이어_모자# + +// ----------- 포링대전 보상품 아이템 ------------ +2765#노비스_피규어# +2766#검사_피규어# +2767#복사_피규어# +2768#마법사_피규어# +2769#궁수_피규어# +2770#도둑_피규어# +2771#상인_피규어# + +// ------------ 대만 용병계약서 ----------- +14293#용병_계약서_상자# +14294#용병_계약서_5장_상자# +14295#용병_계약서_10장_상자# +6050#용병_계약서# + +// ---------- 필리핀 포링케이크 맥주모자 ----------- +5415#포링_케이크_모자# +5416#맥주_모자# + +// ------------ 2008 대만 6월 스크롤 ----------- +14296#엔젤_스크롤# +14297#데빌_스크롤# +14298#깜짝_스크롤# + +// -------- 대만 rwc 추첨권 ---------- +7922#RWC_상품_교환권# + +// -------------- 일본 신규 투구 2008-07 ----------------- +5417#왕관앵무# +5418#병정모자# + +// ----------- 에피소드 13 ppl 아이템 ----------- +6051#잿빛의_공허# + +// ---------- 2008-06 러시아 제작아이템 ------------ +14299#진화한_잎새_상자# +5419#진화한_잎새# + +// ---------- 크리거 보상 아이템 ---------- +13416#글로리어스_프람베르그# +13417#글로리어스_레이피어# +13418#글로리어스_홀리어벤져# +13042#글로리어스_그라디우스# +1310#글로리어스_클리버# +1426#글로리어스_스피어# +1546#글로리어스_모닝스타# +1576#글로리어스_타블렛# +1577#글로리어스_아포칼립스# +1640#글로리어스_아크완드# +1641#글로리어스_치유지팡이# +1826#글로리어스_크로# +1827#글로리어스_피스트# +1927#글로리어스_기타# +1981#글로리어스_라리어트# +1187#글로리어스_크레이모어# +1281#글로리어스_블러디_로어# +1282#글로리어스_자마다르# +1382#글로리어스_투핸드_액스# +1486#글로리어스_랜스# +2002#글로리어스_파괴지팡이# +13307#글로리어스_풍마수리검# +1743#글로리어스_헌터보우# +13110#글로리어스_피스톨# +13176#글로리어스_라이플# +13177#글로리어스_개틀링건# +13178#글로리어스_샷건# +13179#글로리어스_그레네이드_런쳐# +2394#글로리어스_슈트# +2444#글로리어스_슈즈# +2549#글로리어스_머플러# +2772#글로리어스_링# + +// --------------- 필리판 자체제작 투구 이프리트 ------------- +5421#이프리트의_귀# +5420#이프리트_가면# +14300#이프리트_가면_상자# +14301#이프리트의_귀_상자# + +// ---------- 크리거 보상 아이템 추가 방어구 ---------- +2445#글로리어스_양산형_슈즈# +2446#글로리어스_보급용_슈즈# +2395#글로리어스_양산형_슈트# +2396#글로리어스_보급용_슈트# +2773#글로리어스_양산형_링# +2774#글로리어스_보급용_링# + +// --------------- 2008-07 필리판 투구 6종 ------------- +14302#머리에_타고_있는_필리르_상자# +14303#천사의_유령_상자# +14304#스쿠버_마스크_상자# +14305#네코미미_카프라밴드_상자# + +// -------------- 일본 이펙트 아이템 폭죽 추가 ----------------- +12326#화려한_폭죽# + +// ----------- 에피소드 13 낚시 ------------ +2775#낚시_미끼# +2550#낚시꾼의_머플러# + +// ---------- 미국 이벤트 아이템 -------------- +7923#KRATHONG# + +// ------------ 대만 7월 스크롤 ----------- +14306#RWC_특수_스크롤# +14307#RWC_한정_스크롤# +14308#열혈_스크롤# + +// -------- 태국 문자책 모자 ---------- +5422#문자책_모자# + +// ----------------- 중국 알럽차이나 아이템 -------------- +5423#I_LOVE_CHINA# + +// -------------- 일본 코카콜라 캠페인 ----------------- +5424#환타_오렌지_캔_모자# +5425#환타_그레이프_캔_모자# +11506#환타_오렌지_캔# +11507#환타_그레이프_캔# + +// ------------- 2008 대만 복운부적 --------------- +12328#행복의_부적# + +// -------------- 일본 코카콜라 캠페인 2차 ----------------- +5426#카라다메구리차_모자# +5427#홍차화전_모자# +11508#카라다_메구리차# +11509#홍차화전# + +// ---------------- 20080723 영구투구 상자 ----------------- +14309#갱_스카프_상자# +14310#닌자의_두루마리_상자# +14311#천사의_유령_상자# +14312#갱_스카프_상자# +14313#닌자의_두루마리_상자# + +// ----------- 2008 RWC 기념 모자 ------------ +5428#RWC_기념_빵봉투# + +// --------- 태국 5주년 기념 도깨비 모자 ------------- +5429#도깨비_모자# + +// ------------- 2008 대만 칠월칠석 아이템 --------------- +6052#비녀# + +// ------------- 2008 대만 중원절 --------------- +6053#원보# + +// ----------------- 중국 성화모 -------------- +5430#성화관# + +//----------- 브라질 4주년 기념모자 -------------- +5432#bRO 4주년 모자# + +//----------- 브라질 올림픽 이벤트 -------------- +5433#황금_월계수# + +// ----------- 필리핀 MPV 스크롤 상자 --------------- +14597#프리오니_스크롤# +14598#고스트링_스크롤# +14314#프리오니_스크롤_상자# +14315#고스트링_스크롤_상자# + +// ----------- 이벤트 GM 용병 소환서 ----------- +12329#멋진_운영자님_소환서# +12330#예쁜_운영자님_소환서# + +// ---------- 6주년 이벤트 아이템 ------------ +6054#숫자_6_카드# +6055#글자_주_카드# +6056#글자_년_카드# +6057#글자_이_카드# +6058#글자_벤_카드# +6059#글자_트_카드# +12331#인삼# +12332#과일쥬스# + +// ------------ 2008 대만 8월 스크롤 ----------- +14316#7월_7석_스크롤# + +// ---------- 브라질 이벤트 아이템 -------------- +7928#브라질_국기# +7929#금동전# + +// ------------- 2008 대만 태양모자 --------------- +5450#태양모자# + +// -------- 미국 이벤트 아이템 ---------------- +2776#시원한_수건# + +// ----------- 애장판 상자 ------------ +5454#강아지_모자# +5455#장식용_지오그래퍼# +5456#바캉스_모자# +12334#애장판_투구_상자# + +// ------------- 2008 대만 중추절 --------------- + +12335#맛있는_꼬치구이# +12336#버섯_구이# +12337#소시지_구이# +12338#옥수수_구이# +6060#달_관람_티켓# +6061#피초# +6062#문자월병# +6063#문자월병# +6064#문자월병# +6065#문자월병# +6066#문자월병# +6067#문자월병# +6068#토끼_가죽# + +// ---------------- 모바일 투구 상자 ----------------- +14318#전파_안테나_상자# +14319#티라야_보넷_상자# +14320#벌룬햇_상자# + +// ------------ 2008 대만 9월 스크롤 ----------- +14317#백소진_스크롤# + +// --------- 태국 바람개비 모자 ------------- +5458#바람개비_모자# + +// ------------- 원래 애장판 상자 --------------- +12339#애장판_상자# + +// --------- 2008 추석 이벤트 아이템 --------------- +5459#늘어진_토끼# +12340#신비한_찹쌀가루# + +// --------- 인도네시아 임대아이템 ------------- +13419#신성의_세이버# +1578#기도하는_자의_서# +1982#범상치_않은_자의_채찍# +1642#밝지_못한_자의_지팡이# +1828#수도승의_너클# +1547#광기의_둔기# +1427#훌륭한_장창# +1744#악마의_활# +1283#재빠른_카타르# +13111#명사수의_리볼버# +14321#신성의_세이버_상자# +14322#기도하는_자의_서_상자# +14323#범상치_않은_자의_채찍_상자# +14324#밝지_못한_자의_지팡이# +14325#수도승의_너클_상자# +14326#광기의_둔기_상자# +14327#훌륭한_장창_상자# +14328#악마의_활_상자# +14329#재빠른_카타르_상자# +14330#명사수의_리볼버_상자# + +// -------- 중국 국경절 아이템 ---------------- +7930#우마왕의_코뚜레# + +//============2008 대만 부활절 이벤트 아이템 2 ============ +5460#진화된_용의_해골# +5461#진화된_위스퍼_마스크# + +// ---------------- 9월 영구 및 임대 투구 상자 ----------------- +14331#길드원_모집_광고판_모자_상자# +14332#파티원_모집_광고판_모자_상자# +14333#남친_구함!_광고판_모자_상자# +14334#친구_모집_광고판_모자_상자# +14335#여친_구함!_광고판_모자_상자# +14336#마칭햇_상자# +14337#길드원_모집_광고판_모자_상자# +14338#파티원_모집_광고판_모자_상자# +14339#남친_구함!_광고판_모자_상자# +14340#친구_모집_광고판_모자_상자# +14341#여친_구함!_광고판_모자_상자# +14342#마칭햇_상자# + +// ---------- 러시아 캐쉬모자 2종 ------------ +5462#대못박힌_스카프# +5463#무지개_스카프# +14343#대못박힌_스카프_상자# +14344#무지개_스카프# + +// ----------------- 중국 국경절 자하인형모자 -------------- +5464#자하인형_모자# + +//----------- 브라질 어린이날 유니폼 -------------- +2397#인크레디블_이벤트사의_외투# + +// ------------ 2008 대만 10월 스크롤 ----------- +14345#애니멀_스크롤# + +//----------- 2008-09 브라질 요청 아이템 -------------- +5477#브라질_국기# +14346#브라질_국기_상자# + +// --------------- 미국 할로윈 이벤트 ------------- +7941#할로윈티켓# + +// --------- 태국 라마 5세 모자 ------------- +5478#클래식_햇# + +// ---------- 2008-10-22 캐쉬아이템 --------------- +14599#탐욕_스크롤# +14600#멘탈_포션# +14601#티르의_축복# +14347#탐욕_스크롤_20개_상자# +14348#탐욕_스크롤_50개_상자# +14349#멘탈_포션_20개_상자# +14350#멘탈_포션_50개_상자# +14351#티르의_축복_20개_상자# +14352#티르의_축복_50개_상자# +14353#탐욕_스크롤_20개_상자# +14354#탐욕_스크롤_50개_상자# +14355#멘탈_포션_20개_상자# +14356#멘탈_포션_50개_상자# +14357#티르의_축복_20개_상자# +14358#티르의_축복_50개_상자# + +// -------- 대만 크리스마스 기념 아이템 ---------------- +12739#눈의_꽃# + +// ----------- 필리핀 MPV 스크롤 상자 --------------- +14602#타오군_카_스크롤# +14603#미스트레스_스크롤# +14604#오크히어로_스크롤# +14605#오크로드_스크롤# +14359#타오군_카_스크롤_상자# +14360#미스트레스_스크롤_상자# +14361#오크히어로_스크롤_상자# +14362#오크로드_스크롤_상자# + +// ------------ 2008 대만 11월 스크롤 ----------- +14363#따뜻한_마음의_스크롤# + +// 2008년 4/4 분기 브라질 '국가의 날' 이벤트 관련 신규 아이템 +2447#군용_부츠# + +// ------------- 2008 대만 국경일 이벤트 --------------- +5484#국경일_모자# + +// 2008년 4/4 분기 태국 '교육의 날' 이벤트 아이템. +5813#붉은_학사모# + +//-------------- 2008 태국 호랑이얼굴 --------------- +5485#호랑이_얼굴# + +// ---------- 2008-11-19 잡교범 캐쉬아이템 --------------- +14606#JOB_전투교범# +14364#JOB_전투교범_상자# +14365#JOB_전투교범_5개_상자# +14366#JOB_전투교범_10개_상자# +14367#JOB_전투교범_상자# +14368#JOB_전투교범_5개_상자# +14369#JOB_전투교범_10개_상자# + +// ------------- 2008 에피소드13.2 보상 아이템 ------------------ +12342#마누크의_호기# +12343#마누크의_의지# +12344#핀귀큘라의_열매절임# +12345#루시올라의_꿀쨈# +12346#덜_익은_도토리# +12347#도토리_젤리# +6073#용의_갈기# +6074#바젯의_비밀노트# +6075#눈물_결정# +6076#휴대용_도구상자# +6077#정제되지_않은_광석# +6078#돌_부스러기# +6079#알프헤임의_꽃# +6080#마누크_주화# +6081#스플랑디드_주화# +6082#알프헤임의_영혼# +2782#지혜의_왕의_반지# + +11018#스플랑디드판매아이템# +11019#마누크판매아이템# + +6084#브라디움_파편# +6085#낡은_머플러# +12348#마누크의_신념# +12349#코르누스의_눈물# + +// ----------- 펫 ppl 아이템 ----------- +6083#인형_캡슐# + +// -------------- 애니버서리 투구 ------------------ +5486#애니버서리_햇# +5487#포링_케이크_모자# +5488#양갈래_산타모자# + +// ----------- 신규 조합 투구 아이템 ------------ +5436#신부의_화관# +5437#선녀의_꽃# +5438#녹색_머리끈# +5439#빨간_머리끈# +5440#파란_머리끈# +5441#하얀_머리끈# +5442#넥타이# +5443#아기천사상# +5444#머리빗# +5445#입에무는_막대사탕# +5446#고양이_발자국_머리핀# +5447#개구리_모자# +5448#혼자놀기_상자# +5449#혼자놀기_상자# + +// -------------- 일본 이펙트 아이템 상자 ----------------- +14370#여신의_축복_상자# +14371#천사의_축복_상자# +14372#파우더_눈_상자# +14373#작은_하트_상자# +14374#화려한_폭죽_상자# + +// --------- 2008 대만 유저공모 3종 ------------ +1383#성천도끼# +12350#엔젤링_포션# +12351#고함_메가폰# +14375#성천도끼_상자# +14376#엔젤링_포션_상자# +14377#고함_메가폰_상자# + +//----------------2008 필리핀 던전이동스크롤3 ------------------- +12352#던전_이동_스크롤3# +14378#던전_이동_스크롤3_상자# + +// 2008.11. 25 브라질 '바히아' 이벤트 아이템. +2448#애어_보스# + +// 2008년 4/4 분기 브라질 '바히아' 이벤트 +7942#치코_세자르의_편지# +7943#까스키냐# + +// ------------ 2008 태국 러브대디 ------------------- +5489#러브_대디# +14379#러브_대디_상자# + +// ------------ 2008 에피소드 13.2 추가아이템 -------------- +2783#눈구슬_반지# +12353#작은_물병# +6086#시든_꽃# +6087#정령의_결정# +6088#정령의_결정# +6089#어둠의_조각# +6090#정제한_브라디움# +6091#검붉은_비늘조각# +1643#고목나무_지팡이# + +// -------------- 2008 필리핀 아누비스모자 -------------- +5490#아누비스투구# +14380#아누비스_투구_상자# + +// --------------- 2008 크리스마스 ------------------- +2784#오_홀리_나이트# +12354#부쉬_드_노엘# +12355#크리스마스_선물# +12356#루이제의_옷상자# +6092#노래하는_수정조각# + +5811#산타수염# +5136#안토니오의_산타모자# + +// ----------- 2008-12-05 말싱 imall아이템 ------------ +1644#스태프_오브_피어싱# +1645#리치의_해골지팡이# +1428#롱혼# +1429#헌팅_스피어# +1579#사신의_명부# +2003#스태프_오브_디스트럭션# +2551#라이더_휘장# +2552#미스릴_마법망토# +2398#스나이핑_슈츠# +2785#오를레앙의_장갑# +2786#스피리츄얼_링# +2449#배리언트_슈즈# +14381#스태프_오브_피어싱_상자# +14382#리치의_해골지팡이_상자# +14383#롱혼_상자# +14384#헌팅_스피어_상자# +14385#사신의_명부_상자# +14386#스태프_오브_디스트럭션_상자# +14387#라이더_휘장_상자# +14388#미스릴_마법망토_상자# +14389#스나이핑_슈트_상자# +14390#오를레앙의_장갑_상자# +14391#스피리츄얼_링_상자# +14392#배리언트_슈즈_상자# + +// 2008.12.018 중국 '봉인의 상자' 이벤트 +7944#봉인된_상자# +7945#만능부적# +12740#힘_증폭_스크롤# +12741#지력_증폭_스크롤# +14393#만능부적_1개_상자# + +// ----------- 2008-12 한국캐시아이템 ------------ +5384#양갈래_산타모자# +5359#선장님의_모자# +5435#빨간_챙_모자# +14394#양갈래_산타모자_상자# +14395#선장님의_모자_상자# +14396#빨간_챙_모자_상자# + +14409#양갈래_산타모자_상자# +14410#선장님의_모자_상자# +14411#빨간_챙_모자_상자# + +// ------------ 2008 에피소드 13.2 추가아이템 -------------- +6093#드라코의_알# + +// ------------ 2008 연말 아이템 1+1 ----------------------- +14397#보너스상자_01# +14398#보너스상자_02# +14399#보너스상자_03# +14400#보너스상자_04# +14401#보너스상자_05# +14402#보너스상자_06# +14403#보너스상자_07# +14404#보너스상자_08# +14405#보너스상자_09# +14406#보너스상자_10# + +14412#보너스상자_01# +14413#보너스상자_02# +14414#보너스상자_03# +14415#보너스상자_04# +14416#보너스상자_05# +14417#보너스상자_06# +14418#보너스상자_07# +14419#보너스상자_08# +14420#보너스상자_09# +14421#보너스상자_10# + +// ------------ 2008 대만 12월 1월 스크롤 ----------- +14407#크리스마스_스크롤# +14408#신년_스크롤# + +// ----------- 2008-12-12 에피소드 ppl 아이템 ----------- +6101#출석표# +6102#스플랑디드_보고서# +6103#마누크_보고서# + + + +// ------------- 한국 이벤트 펫 관련 -------------- +9055#서큐버스_알# +9056#임프_알# +10037#검은_나비_가면# +10038#뿔_보호대# +12373#소년의_순정# +12374#불꽃_얼음# +12395#탄탄면# + +6113#생기있는_꽃# +6114#불꽃_젬스톤# +6115#어린이_만두# +6116#서큐버스_펫_교환쿠폰# +6117#임프_펫_교환쿠폰# +6118#청이_펫_교환쿠폰# + +// 2008 13.2 에피소드 신규 아이템 +1548#베테랑_햄머# +1188#베테랑_소드# +1384#베테랑_액스# +1385#브라디움_돌망치# +2138#브라디움_방패# +2450#생명나무_줄기_슈즈# +2554#대행자의_껍질_조각# + +//----------- 브라질 12월 모자 -------------- +5491#무법자의_모자# +12375#아까라제# +// ----------- 2009 신년이벤트 ----------- +2791#갓_잡은_생선# +6119#소의_천연가죽# +11706#스테이크# +11707#로스트_비프# +12376#신묘_통조림_고등어맛# +12377#신묘_패트병_바다맛# + +// ----------- 2009-01-08 인도네시아 임대아이템 ------------ +13043#포츈소드# +13044#송곳# +13045#카마이타치# +1928#신들린_기타# +1386#둠_슬레이어# +13308#풍마수리검_열화# +15001#오딘의_축복# +2792#링_오브_플레임로드# +2793#링_오브_레조넌스# +5492#학생모# +5493#울캡# +5494#스핑크스_모자# +14426#포츈소드_상자# +14427#송곳_상자# +14428#카마이타치_상자# +14429#신들린_기타_상자# +14430#둠_슬레이어_상자# +14431#풍마수리검_열화_상자# +14432#오딘의_축복_상자# +14433#플레임로드_링_상자# +14434#레조넌스_링_상자# +14435#학생모_상자# +14436#울캡_상자# +14437#스핑크스_모자_상자# + +// 2009-01-08 대만 유저공모 아이템 +13420#홍련의검# +5495#뇌신지력# +5496#주사위모자# +5497#호랑이왕_인형모자# +5498#떠돌이_늑대왕투구# +14438#홍련의검_상자# +14439#뇌신지력_상자# +14440#주사위모자_상자# +14441#호랑이왕_인형모자_상자# +14442#떠돌이_늑대왕투구_상자# + +// 2009-01-09 해외 요청 아이템 +5499#피자_모자# +5500#아이스크림_모자# +14443#피자_모자_상자# +14444#아이스크림_모자_상자# + +// ------------ 2009한국 구정이벤트 -------------- +12378#떡국# +12379#교황_쿠키# +// --------- 대만2009 만한전석 ---------- +11708#채끝등심# +14607#호화로운_양식# +14608#만한전석# +14609#실패한_요리# +// 2009.01.15 전 세계 발렌타인 데이 이벤트 +7946#발렌타인_금반지# +7947#발렌타인_은반지# +// 2009-01-15 미국 요청 아이템 +5501#해적의_자존심# +14447#해적의_자존심_상자# +14448#강령술사의_두건_상자# + +// 2009-01-15 보상 아이템, 2009년 12월 한국 겨울이벤트 +14449#후긴의_알_첫번째# +14450#후긴의_알_두번째# +14451#후긴의_알_세번째# +14452#후긴의_알_네번째# +14453#후긴의_알_다섯번째# +14454#후긴의_알_여섯번째# +14455#무닌의_알_첫번째# +14456#무닌의_알_두번째# +14457#무닌의_알_세번째# +14458#무닌의_알_네번째# + +// 2009-01-15 중국 +5504#RO_5주년_기념_면사포# + + +14459#토끼_마술_모자_상자# +14460#중국_5주년_면사포_상자# +14461#아사라_요정_모자_상자# + +// 2009-01-08 대만 유저공모 아이템 +13420#홍련의_검# +5496#주사위모자# +5497#호랑이왕_인형모자# +5498#떠돌이_늑대왕투구# +14438#홍련의검_상자# +14440#주사위모자_상자# +14441#호랑이왕_인형모자_상자# +14442#떠돌이_늑대왕투구_상자# + +// ------------- 일본 몬스터브리더 총 15종 2009 -------------- + +9040#묘괴_알# +9041#리프캣_알# +9042#로리루리_알# + +9044#시노비_알# + +9046#고블린_리더_알# +9047#요선녀_알# +9048#미야비인형_알# +9049#듀라한_알# +9050#메두사_알# +9051#스톤_슈터_알# + +9053#고렘_알# +9054#나이트메어_테러_알# + +10022#금빛_귀걸이# +10023#초록_복주머니# +10024#패션_안경# + +10026#두루마기용_술# + +10028#멋진_휘장# +10029#옥_노리개# +10030#여름_부채# +10031#죽음의_고리# +10032#여왕의_코로넷# +10033#아프로_헤어# + +10035#태엽# +10036#지옥의_뿔# + +12358#바람의_부채# +12359#매우_부드러운_풀# +12360#새빨간_쥬스# + +12362#쿠로렌# + +12364#지도자의_지팡이# +12365#탐스러운_연꽃# +12366#여자아이_인형# +12367#고급_양주병# +12368#화려한_거울# +12369#기름야자_열매# + +12371#마력의_석판# +12372#지옥의_계약서# + +6095#향주# +6096#등푸른_생선# +6097#호박_파이# + +6099#구운_떡# + +6104#커다란_셀# +6105#아침_이슬# +6106#잘익은_딸기# +6107#선셋_온_더_락# +6108#사과_푸딩# +6109#식물_영양제# + +6111#마력_담긴_돌# +6112#신선한_풀# + +// ------ 2009-01-21 다크네스,카우보이,캐시 -------- +14462#다크네스_헬름_상자# +14463#보라색_카우보이_모자_상자# +14464#다크네스_헬름_상자# +14465#보라색_카우보이_모자_상자# + +// -------- 전세계 발렌타인 이벤트 ---------- +12742#발렌타인_선물상자# +12743#발렌타인_선물상자# +12744#초콜렛_상자# +12745#발렌타인_증표상자# +7948#상자# +7946#발렌타인_금반지# +7947#발렌타인_은반지# +5817#발렌타인의_증표# + +// -------- 필리핀 엔들레스입장 --------- +6127#정화석# + +// -------------- 2009 일본 미적용 투구 ------------------ +5815#클래식_햇# +5816#보라색_카우보이_모자# + +// 2009.01.28 브라질 카니발 이벤트 +7949#솜털로_방직된_천# +14467#카니발모자상자# +14468#카니발서클릿상자# +5818#카니발_축제_모자# +5819#카니발_축제_서클릿# + +//해외 2009 대만 [발렌타인데이 이벤트] +5822#사랑의_병아리_모자# +5823#사랑의_화살# +7952#로미로스의_선물# +7953#줄리엣지의_선물# + +// 2009 아요타야이벤트 +7950#아요타야_페스티벌_티켓# +5820#검은_요정의_귀# + +// 2009.01.28 중국 [어머니의 날 이벤트] +7951#황금_튤립_꽃# +5821#황금_튤립머리핀# + +// 2009-01-29 대만소꼬리스크롤제작 +14469#대만_소꼬리_스크롤# + +// 2009-02-02 일본 펫브리더용 교환티켓 +6129#교환티켓_나메트메어_테러# +6130#교환티켓_로리루리# +6131#교환티켓_고블린리더# +6132#교환티켓_인큐버스# +6133#교환티켓_미야비인형# +6134#교환티켓_위스퍼# +6135#교환티켓_요선녀# +6136#교환티켓_메두사# +6137#교환티켓_스톤슈터# +6138#교환티켓_마리오네트# +6139#교환티켓_리프캣# +6140#교환티켓_듀라한# +6141#교환티켓_시노비# +6142#교환티켓_고렘# +6143#교환티켓_묘괴# + +14470#나메트메어_테러_교환티켓_상자# +14471#로리루리_교환티켓_상자# +14472#고블린리더_교환티켓_상자# +14473#인큐버스_교환티켓_상자# +14474#미야비인형_교환티켓_상자# +14475#위스퍼_교환티켓_상자# +14476#요선녀_교환티켓_상자# +14477#메두사_교환티켓_상자# +14478#스톤슈터_교환티켓_상자# +14479#마리오네트_교환티켓_상자# +14480#리프캣_교환티켓_상자# +14481#듀라한_교환티켓_상자# +14482#시노비_교환티켓_상자# +14483#고렘_교환티켓_상자# +14484#묘괴_교환티켓_상자# + +// 2009-02-04 브라질 아카데미뱃지 +2751#수습_마법사의_뱃지# +5406#성직자학교_졸업모# +5407#마법학교_졸업모# +14485#수습_마법사의_뱃지_상자# +14486#성직자학교_졸업모_상자# +14487#마법학교_졸업모_상자# + +// 2009-02-06 태국파자마모자 +5506#파란_파자마_모자# +5507#핑크_파자마_모자# +14488#파란_파자마_모자_상자# +14489#핑크_파자마_모자_상자# + +// ------- 2009 한국 발렌타인 ----------- +6148#영원의_초콜렛# +6149#소박한_초콜렛# + +// -------- 2009-02 한국 모바일 캐시 -------- +5508#상어모자# +5509#스팅모자# +14490#상어모자_상자# +14491#스팅모자_상자# +14492#상어모자_상자# +14493#스팅모자_상자# + +// 2009-02-12 브라질 샤워캡 +5510#샤워캡# + +// 2009-02-16 미국 보병궁 쌍어궁 추가 +5512#보병궁_보관# +5513#보병궁_왕관# +5514#쌍어궁_보관# +5515#쌍어궁_왕관# +14495#보병궁_보관_상자# +14496#보병궁_왕관_상자# +14497#쌍어궁_보관_상자# +14498#쌍어궁_왕관_상자# + +// 2009-02-16 브라질_사만바이아 + +14494#사만바이아_상자# + +// -------------- 13.2 장비 추가 업데이트 ------------ +1189#크라스나야# +1284#크리슈나# +1285#차크람# +1311#베체르_액스# +1387#기간트_액스# +1646#라크리마_스틱# +1647#크로체_스태프# +1648#스태프_오브_오르도# +1745#팔켄_블리츠# +2004#크로노스# +2005#스태프_오브_데아# +13046#크리그# +13047#바이나# +13421#루베르# +16000#에르데# +16001#빨간_스퀘어백# + +// 2009_02_19_브라질이벤트 +5518#대형_마제스틱_고우트# + +//2009_02_브라질_공작새 +5519#공작새_깃털# +14499#공작새_깃털_상자# + +// ------- 200902한국캐시 ------ +1190#크레이모어# +1286#자마다르# +1312#오키쉬_액스# +1388#투핸드액스# +1430#파이크# +1487#랜스# +1580#대백과_사전# +1829#피스트# +1929#기타# +1983#란테# +5520#토끼_귀마개# +5521#분노한_입# +13048#다마스커스# +13422#프람베르그# +13309#풍마수리검_대차륜# +16002#스터너# +12391#행운의_알# +16101#행운의_상자# +16102#크레이모어_상자# +16103#자마다르_상자# +16104#투핸드액스_상자# +16105#랜스_상자# +16106#풍마수리검_대차륜_상자# +16107#오키쉬_액스_상자# +16108#파이크_상자# +16109#대백과_사전_상자# +16110#피스트_상자# +16111#기타_상자# +16112#란테_상자# +16113#다마스커스_상자# +16114#프람베르그_상자# +16115#스터너_상자# +16116#행운의_상자# +16117#크레이모어_상자# +16118#자마다르_상자# +16119#투핸드액스_상자# +16120#랜스_상자# +16121#풍마수리검_대차륜_상자# +16122#오키쉬_액스_상자# +16123#파이크_상자# +16124#대백과_사전_상자# +16125#피스트_상자# +16126#기타_상자# +16127#란테_상자# +16128#다마스커스_상자# +16129#프람베르그_상자# +16130#스터너_상자# + + +// -------- 2009-02-20 ---------- +4700#STR+1# +4701#STR+2# +4702#STR+3# +4703#STR+4# +4704#STR+5# +4705#STR+6# +4706#STR+7# +4707#STR+8# +4708#STR+9# +4709#STR+10# +4710#INT+1# +4711#INT+2# +4712#INT+3# +4713#INT+4# +4714#INT+5# +4715#INT+6# +4716#INT+7# +4717#INT+8# +4718#INT+9# +4719#INT+10# +4720#DEX+1# +4721#DEX+2# +4722#DEX+3# +4723#DEX+4# +4724#DEX+5# +4725#DEX+6# +4726#DEX+7# +4727#DEX+8# +4728#DEX+9# +4729#DEX+10# +4730#AGI+1# +4731#AGI+2# +4732#AGI+3# +4733#AGI+4# +4734#AGI+5# +4735#AGI+6# +4736#AGI+7# +4737#AGI+8# +4738#AGI+9# +4739#AGI+10# +4740#VIT+1# +4741#VIT+2# +4742#VIT+3# +4743#VIT+4# +4744#VIT+5# +4745#VIT+6# +4746#VIT+7# +4747#VIT+8# +4748#VIT+9# +4749#VIT+10# +4750#LUK+1# +4751#LUK+2# +4752#LUK+3# +4753#LUK+4# +4754#LUK+5# +4755#LUK+6# +4756#LUK+7# +4757#LUK+8# +4758#LUK+9# +4759#LUK+10# + +// 2009-02-20 일본_코카콜라 +5522#환타제로_레몬_모자# +5523#사쿠라_미스트_모자# +5524#사쿠라_밀크티_모자# +5525#일차화_모자# +11709#환타_제로_레몬# +11710#사쿠라_미스트# +11711#사쿠라_밀크티# +11712#일차화# +6155#멤버스_카드# + +//2009-02-24_일본_아카데미시크릿 +2799#신비한_쿠이르펜_링# + +// 2009-02 대만 캐시아이템 +2006#빛의_수호_지팡이# +5526#타니_아가씨_인형# +16131#타니_아가씨_인형_상자# +16132#루나틱_모자_상자# +16133#빛의_수호_지팡이_상자# + +// 2009-02 중국2월캐시 +5528#개구리_왕자_모자# + +16134#개구리_왕자_모자_상자# + + +// 2009-02 일본 2월이펙트상자 +16136#여신의_축복_상자# +16137#천사의_축복_상자# +16138#파우더_눈_상자# +16139#작은_하트_상자# +16140#화려한_폭죽_상자# + +// 2009-02 일본 펫악세상자 +16141#금빛_귀걸이_상자# +16142#초록_복주머니_상자# +16143#패션_안경_상자# +16144#별모양_머리띠_상자# +16145#두루마기용_술_상자# +16146#펫_영혼고리_상자# +16147#멋진_휘장_상자# +16148#옥_노리개_상자# +16149#여름_부채_상자# +16150#죽음의_고리_상자# +16151#여왕의_코로넷_상자# +16152#아프로_헤어_상자# +16153#무도회_가면_상자# +16154#태엽_상자# +16155#지옥의_뿔_상자# + +// ------- 3월 브라질 만우절 ------ +5824#만우절모자# + +// ------ 3월 일본 기존펫티켓 ------ +6157#포링교환티켓# +6158#드롭프스_교환_티켓# +6159#포포링_교환_티켓# +6160#루나틱_교환_티켓# +6161#픽키_교환_티켓# +6162#페코페코_교환_티켓# +6163#세비지_베베_교환_티켓# +6164#스포아_교환_티켓# +6165#포이즌_스포아_교환_티켓# +6166#촌촌_교환_티켓# +6167#스틸_촌촌_교환_티켓# +6168#쁘띠_교환_티켓# +6169#데비루치_교환_티켓# +6170#이시스_교환_티켓# +6171#스모키_교환_티켓# +6172#도깨비_교환_티켓# +6173#새끼_데저트울프_교환_티켓# +6174#요요_교환_티켓# +6175#소희_교환_티켓# +6176#로커_교환_티켓# +6177#헌터플라이_교환_티켓# +6178#오크워리어_교환_티켓# +6179#바포메트_주니어_교환_티켓# +6180#무낙_교환_티켓# +6181#본건_교환_티켓# +6182#크리스마스_고블린_교환_티켓# +6183#딱딱한_떡_교환_티켓# +6184#지르타스_교환_티켓# +6185#앨리스_교환_티켓# +16156#포링_교환_티켓_상자# +16157#드롭프스_교환_티켓_상자# +16158#포포링_교환_티켓_상자# +16159#루나틱_교환_티켓_상자# +16160#픽키_교환_티켓_상자# +16161#페코페코_교환_티켓_상자# +16162#세비지_베베_교환_티켓_상자# +16163#스포아_교환_티켓_상자# +16164#포이즌_스포아_교환_티켓_상자# +16165#촌촌_교환_티켓_상자# +16166#스틸_촌촌_교환_티켓_상자# +16167#쁘띠_교환_티켓_상자# +16168#데비루치_교환_티켓_상자# +16169#이시스_교환_티켓_상자# +16170#스모키_교환_티켓_상자# +16171#도깨비_교환_티켓_상자# +16172#새끼_데저트울프_교환_티켓_상자# +16173#요요_교환_티켓_상자# +16174#소희_교환_티켓_상자# +16175#로커_교환_티켓_상자# +16176#헌터플라이_교환_티켓_상자# +16177#오크워리어_교환_티켓_상자# +16178#바포메트_주니어_교환_티켓_상자# +16179#무낙_교환_티켓_상자# +16180#본건_교환_티켓_상자# +16181#크리스마스_고블린_교환_티켓_상자# +16182#딱딱한_떡_교환_티켓_상자# +16183#지르타스_교환_티켓_상자# +16184#앨리스_교환_티켓_상자# + +// ---------- 3월 프랑스 캐시투구 ---------- +5530#까마귀_모자# +5531#아기_용_모자# +16185#까마귀_모자_상자# +16186#아기_용_모자_상자# + +// ----------- 일본 3월 기존아이템수정 --------- +5532#해적의_대거# +5533#황제의_월계관# +5534#여우모자# + +// ----------- 한국 3월 캐시 분노입 --------- +16100#분노한_입_상자# +16187#분노한_입_상자# + +// ------------- 3월 한국 모바일캐시 ----------- +5503#토끼_마술_모자# +5535#사이드_캡# +16188#토끼_마술_모자_상자# +16189#사이드_캡_상자# +16190#토끼_마술_모자_상자# +16191#사이드_캡_상자# + +// ------------ 4월 미국 스페어카드 ------------ +6187#백지_카드# + +// ------------ 4월 전세계만우절이벤트 ------------- +12396#선물상자?# +12397#선물상자?# + + +// ------------ 3월 브라질 캐시모자 ------------ +5537#콰티_모자# +5538#투칸_모자# +5539#재규어_모자# +16192#콰티_모자_상자# +16193#투칸_모자_상자# +16194#재규어_모자_상자# + +// --------- 3월 한국 PC방 이벤트 --------- +12398#PC방_선물상자# + +// ------------ 3월 일본공성보상 ----------- +12399#성의_보물상자# + +// ----------- 3월 태국 송크란2009 ----------- +2815#물의_반지# +12400#축복의_물# + +// ------------ 3월 대만스크롤2009 ---------- +16195#대만_3월_스크롤# + +// ------------ 3월 미국슬롯쿠폰 ---------- +6188#슬롯_쿠폰# + +// ------------ 3월 필리핀 프레야템 --------------- +2141#프레야_영혼쉴드# +2142#프레야_영혼쉴드# +2143#프레야_영혼쉴드# +2144#프레야_영혼쉴드# +2451#프레야_영혼샌달# +2452#프레야_영혼샌달# +2453#프레야_영혼샌달# +2454#프레야_영혼샌달# +2555#프레야_영혼스카프# +2556#프레야_영혼스카프# +2557#프레야_영혼스카프# +2558#프레야_영혼스카프# +2811#프레야_영혼팔찌# +2812#프레야_영혼팔찌# +2813#프레야_영혼팔찌# +2814#프레야_영혼팔찌# +5540#프레야_영혼서클릿# +5541#프레야_영혼서클릿# +5542#프레야_영혼서클릿# +5543#프레야_영혼서클릿# +15003#프레야_영혼로브# +15004#프레야_영혼로브# +15005#프레야_영혼로브# +15006#프레야_영혼로브# +16196#프레야_영혼쉴드_상자# +16197#프레야_영혼쉴드_상자# +16198#프레야_영혼쉴드_상자# +16199#프레야_영혼쉴드_상자# +16200#프레야_영혼로브_상자# +16201#프레야_영혼로브_상자# +16202#프레야_영혼로브_상자# +16203#프레야_영혼로브_상자# +16204#프레야_영혼스카프_상자# +16205#프레야_영혼스카프_상자# +16206#프레야_영혼스카프_상자# +16207#프레야_영혼스카프_상자# +16208#프레야_영혼샌달_상자# +16209#프레야_영혼샌달_상자# +16210#프레야_영혼샌달_상자# +16211#프레야_영혼샌달_상자# +16212#프레야_영혼서클릿_상자# +16213#프레야_영혼서클릿_상자# +16214#프레야_영혼서클릿_상자# +16215#프레야_영혼서클릿_상자# +16216#프레야_영혼팔찌_상자# +16217#프레야_영혼팔찌_상자# +16218#프레야_영혼팔찌_상자# +16219#프레야_영혼팔찌_상자# + +// ----------- 한국 3월 캐시 펫 -------------- +6098#작은_눈꽃# +6100#음습한_어둠# +6110#생기있는_꽃# +9043#마리오네트_알# +9045#위스퍼_알# +9052#인큐버스_알# +10025#별모양_머리띠# +10027#펫_영혼_고리# +10034#무도회_가면# +12361#맛있는_빙수# +12363#맞춤_관# +12370#소녀의_순정# +16220#마리오네트_악세사리_상자# +16221#위스퍼_악세사리_상자# +16222#인큐버스_악세사리_상자# +16223#마리오네트_악세사리_상자# +16224#위스퍼_악세사리_상자# +16225#인큐버스_악세사리_상자# + +// ------------ TNT 보상 ------------ +2145#시간의_수호자_쉴드# +2455#시간의_수호자_부츠# +2559#시간의_수호자_망토# +5544#시간의_수호자_모자# +15007#시간의_수호자_로브# + +// -----------3월 미국 황도12궁 ----------- + +5545#백양궁_하늘의_보관# +5546#백양궁_하늘의_왕관# +16226#백양궁_하늘의_보관_상자# +16227#백양궁_하늘의_왕관_상자# + +// ------------- 3월 일본 RJC 카츄사 ------------ + +5547#RJC_꽃의_카츄사# +5548#진홍의_장미# +16228#RJC_꽃의_카츄사_상자# +16229#진홍의_장미_상자# + +// -------- 미국 4월 황도12궁 ------ + +5549#금우궁_하늘의_보관# +5550#금우궁_하늘의_왕관# +16230#금우궁_하늘의_보관_상자# +16231#금우궁_하늘의_왕관_상자# + +// --------- 필리핀 4월 캐시상자 --------- + +16232#레긴레이브의_날개_상자# + +// --------- 인도네시아 4월 이벤트 ---------- +5551#성스러운_달걀_모자# + +// --------- 필리핀4월 섬머이벤트 --------- +5552#축제용_로드서클릿# +5553#축제용_토끼머리띠# +16233#축제용_로드서클릿_상자# +16234#축제용_토끼머리띠_상자# +7954#페스티벌_티켓# + +// ----------- 대만 4월 캐시모자 --------------- +5554#문어_모자# +5555#리프캣_모자# +5557#와일드_로즈_모자# +16235#문어_모자_상자# +16236#리프캣_모자_상자# +16237#퍼씰_모자_상자# +16238#와일드_로즈_모자# + +// ----------- 브라질 4월 캐시모자 -------------- +5558#사치의_모자# +16239#사치의_모자_상자# + +// ---------- 한국 5월 이벤트 아이템 ----------- +2816#레이더링# +2817#레이더링# +2818#레이더링# +5559#새하얀_천조각# +6206#사랑합니다# +6207#감사합니다# +6208#존경합니다# +12402#29열매# + +// ---------- 한국 초보자수련장 ----------- +2819#검사_입문서# +2820#도둑_입문서# +2821#복사_입문서# +2822#궁수_입문서# +2823#상인_입문서# +2824#마술사_입문서# + +// -------- 대만 4월 노동절 -------- + +6209#기사의_영예# +16249#기사단의_선물상자# +5825#기사단의_영예_뱃지# + +// ------ 2009 일본 4월 RJC아이템 ------ + +5560#뿔투구_헬름# +5561#토끼_마술_모자# +5562#화려한_면사포# + + +// --------- 2009 한국 4월 캐시모자 ---------- +5360#휘케바인의_검은_고양이귀# +5431#꼬꼬댁_모자# +16240#휘케바인의_검은_고양이귀_상자# +16241#꼬꼬댁모자상자# +16242#휘케바인의_검은_고양이귀_상자# +16243#꼬꼬댁모자상자# + + +// -------- 2009 대만 4월 캐시 ------- + +16245#대만_4월_스크롤# + + +// ------- 2009 미국 4월 캐시투구 ------- + +5564#사기의_왕관# +16246#사기의_왕관_상자# + +// ------ 2009 중국 4월 RWC ----- + +5565#항룡_나한_가면# +5566#복호_나한_가면# +16247#항룡_나한_가면_상자# +16248#복호_나한_가면_상자# + +// ----- 2009 4월 인도네시아 6주년 ----- + +16250#발키리에의_선물# +5826#발키리에의_투구# + +// --------- 미국 4월 황도12궁투구 -------- +5569#쌍자궁_하늘의_보관# +5570#쌍자궁_하늘의_왕관# +16251#쌍자궁_하늘의_보관_상자# +16252#쌍자궁_하늘의_왕관_상자# + +// ------ 대만 4월 토끼스크롤 ------ + +5568#토끼_보닛# +16253#토끼_스크롤# + +// ------ 브라질 4월 페스타주니나 ----- + + +11713#줄리아의_사탕# + +// ------- 태국 4월 쏨차이 ------- + +5571#라스타_가발# + + +// ------- 한국 5월 퀘스트아이템 ------ + +6218#다이신_택배_상자# + + +// -------- 한국 5월 캐시 -------- + +5396#재스퍼_크레스트# +5573#도깨비_뿔# +12403#행운의_알Ⅱ# +12404#활성화_포션# +12405#덜_익은_이그드라실_씨앗# +12406#사이킥_아머_스크롤# +15008#화염의_갑옷# +16254#활성화_포션_상자# +16255#활성화_포션_상자# + + +// -------- 라그 DS 아이템 --------- +2777#샤먼_링# +2778#샤먼_이어링# +2779#다크나이트_벨트_A# +2780#다크나이트_글로브_A# +2781#아우둠라의_혜택# +2825#샤먼_이어링_B# +2826#다크나이트_벨트_B# +2827#다크나이트_글로브_B# +5479#샤먼의_머리장식# +5480#비조프닐의_날개장식# +5481#헤르모즈_캡# +5482#다크나이트_마스크_A# +5483#오딘_마스크# +5577#다크나이트_마스크_B# +6070#샤먼의_고문서# +6071#부러진_검# +6072#비조프닐의_날개# +14422#샤먼의_고문서_상자# +14423#부러진_검의_상자# +14424#비조프닐의_날개_상자# +16256#라그DS_투구_상자# +7959#고대의_금화# +7960#고대의_은화# + + +// --------- 한국 5월 낙원단 ------- + +6219#낙원단의_증표# + +// --------- 프랑스 5월 공모전 -------- +5575#주먹밥_모자# + +// --------- 대만 6월 스크롤 -------- +16257#불심_스크롤# + +// --------- 필리핀 6월 배청소, 2009-10-브라질리스--------- +5578#항해모자# +6221#변한_리프캣_볼# +6222#반짝이는_구슬# +12408#리프캣_볼# + +// --------- 미국 6월 로또 --------- +6220#신비한_염색약# + +// --------- 미국 6월 유저공모 --------- +5579#배회하는_자의_삿갓# + +// --------- 한국 6월 오버제련 --------- +6223#카르늄# +6224#브라디움# +6225#고밀도_카르늄# +6226#고밀도_브라디움# +16258#고밀도_브라디움_5개_상자# +16259#고밀도_카르늄_5개_상자# +16260#고밀도_브라디움_10개_상자# +16261#고밀도_카르늄_10개_상자# +16262#고밀도_브라디움_5개_상자# +16263#고밀도_카르늄_5개_상자# +16264#고밀도_브라디움_10개_상자# +16265#고밀도_카르늄_10개_상자# + +// ------------- 미국 6월 황도 12궁 --------------- +5581#거해궁_보관# +5582#거해궁_왕관# +16269#거해궁_보관_상자# +16270#거해궁_왕관_상자# + + +// --------------- 미국 6월 로또 ----------------- +12411#요약된_전투교범# +12412#최강_끈적_풍선껌# +16267#요약된_전투교범_상자# +16268#최강_끈적_풍선껌_상자# + +// -------------- 인도네시아 6월 독립모 ------------- +5580#인도네시아_베레모# +7955#잃어버린_카드1# +7956#잃어버린_카드2# +7957#잃어버린_카드3# +7958#잃어버린_카드4# +16266#인도네시아_독립모_상자# + +// -------- 한국6월_3차 무기추가 -------- +1191#알카_브링거# +1287#두르가# +1649#라피니_스태프# +1746#엘븐보우# +16003#카르가_메이스# + +// -------- 한국6월_1일캐시 -------- +16271#하키마스크_상자# +16272#옵저버_상자# +16273#올인원_반지_상자# +16274#스피리츄얼_튜닉_상자# +16275#리큐퍼레이티브_아머_상자# +16276#쉘터_오브_레지스턴스_상자# +16277#실피드_망토_상자# +16278#리프레쉬_슈즈_상자# +16279#토스트_상자# +16280#황야의_무법자_상자# +16281#레버액션_라이플_상자# +16282#치유의_지팡이_상자# +16283#프락시너스_상자# +16284#길드원_모집_광고판_모자_상자# +16285#파티원_모집_광고판_모자_상자# +16286#남친_구함!_광고판_모자_상자# +16287#여친_구함!_광고판_모자_상자# +16288#친구_모집_광고판_모자_상자# +16289#크레이모어_상자# +16290#투핸드액스_상자# +16291#랜스_상자# +16292#자마다르_상자# +16293#풍마수리검_대차륜_상자# +16294#오키쉬_액스_상자# +16295#파이크_상자# +16296#대백과_사전_상자# +16297#피스트_상자# +16298#기타_상자# +16299#란테_상자# +16300#다마스커스_상자# +16301#프람베르그_상자# +16302#스터너_상자# +16303#분노한_입_상자# + +// -------- 한국6월 낙원단 ---------- +1192#P.슬레이어Ⅰ# +1193#P.슬레이어Ⅱ# +1650#P.스태프Ⅰ# +1651#P.스태프Ⅱ# +1747#P.보우Ⅰ# +1748#P.보우Ⅱ# +2456#낙원단_부츠Ⅰ# +2457#낙원단_부츠Ⅱ# +2458#낙원단_부츠Ⅲ# +2560#낙원단_망토# +5583#낙원단_모자# +13050#P.대거Ⅰ# +13051#P.대거Ⅱ# +13112#P.리볼버Ⅰ# +13113#P.리볼버Ⅱ# +13423#P.세이버Ⅰ# +13424#P.세이버Ⅱ# +15009#낙원단_제복Ⅰ# +15010#낙원단_제복Ⅱ# +15011#낙원단_제복Ⅲ# +16004#P.메이스Ⅰ# +16005#P.메이스Ⅱ# + +// -------- 한국 6월 PC방쿠폰 --------- + +11514#농축_슬림_포션# +12413#PC방_쿠폰_상자Ⅱ# + + +// -------- 한국 6월 추가방어구 --------- +2146#실버가드# +2147#라운드_버클러# +2148#로사쉴드# +15012#퓨엔테_로브# +15013#클레르_슈츠# +15014#칠흑의_갑옷# + +// ------- 대만7월 스크롤 -------- + +16304#악마의_현신# + +// ------- 한국 7월 캐시방어구 ------ +2149#강화_가드# +2150#강화_버클러# +2151#강화_쉴드# +2459#강화_슈즈# +2460#강화_부츠# +2461#강화_그리브# +2561#강화_후드# +2562#강화_머플러# +2563#강화_망토# +2828#강화_클립# +5585#룬_매듭_머리띠# +15015#강화_어드벤쳐_슈츠# +15016#강화_롱_코트# +15017#강화_세인트_로브# +15018#강화_타이즈# +15019#강화_씨프클로스# +15020#강화_메일# +15021#강화_포멀_드레스# +16305#강화_가드_상자# +16306#강화_가드_상자# +16307#강화_버클러_상자# +16308#강화_버클러_상자# +16309#강화_쉴드_상자# +16310#강화_쉴드_상자# +16311#강화_슈즈_상자# +16312#강화_슈즈_상자# +16313#강화_부츠_상자# +16314#강화_부츠_상자# +16315#강화_그리브_상자# +16316#강화_그리브_상자# +16317#강화_후드_상자# +16318#강화_후드_상자# +16319#강화_머플러_상자# +16320#강화_머플러_상자# +16321#강화_망토_상자# +16322#강화_망토_상자# +16323#강화_클립_상자# +16324#강화_클립_상자# +16325#룬_매듭_머리띠_상자# +16326#룬_매듭_머리띠_상자# +16327#강화_어드벤쳐_슈츠_상자# +16328#강화_어드벤쳐_슈츠_상자# +16329#강화_롱_코트_상자# +16330#강화_롱_코트_상자# +16331#강화_세인트_로브_상자# +16332#강화_세인트_로브_상자# +16333#강화_타이즈_상자# +16334#강화_타이즈_상자# +16335#강화_씨프클로스_상자# +16336#강화_씨프클로스_상자# +16337#강화_메일_상자# +16338#강화_메일_상자# +16339#강화_포멀_드레스_상자# +16340#강화_포멀_드레스_상자# + +// --------- 중동 7월 월드투어 ---------- +1313#여행자의_도끼# +1652#여행자의_지팡이# +1749#여행자의_활# +7961#무기_교환_티켓# +13052#여행자의_단검# +13425#여행자의_검# +16006#여행자의_메이스# + +// ------- 한국 7월 여름 이벤트 ------- + +5586#모기향# +5587#모기향_1회용# + +// ------- 한국 7월 신규카드 ------- +4442#따따초_카드# +4443#아쿠아_엘레멘탈_카드# +4444#드라코_카드# +4445#루시올라_베스파_카드# +4447#센티페데_카드# +4448#코르누스_카드# +4449#다크_쉐도우_카드# +4450#밴시_마스터_카드# +4451#엔트바이엔_크놋헨_카드# +4452#센티페데_유충_카드# +4453#힐스리온_카드# + +// ------ 미국 7월 캐시투구 ------ + +5588#사자궁_하늘의_왕관# +5589#사자궁_하늘의_보관# +16343#사자궁_왕관_상자# +16344#사자궁_보관_상자# +16345#사자궁_왕관_상자# +16346#사자궁_보관_상자# + +// ------ 한국 7월 캐시아이템 -------- +1288#블러디피어# +1488#알슈피스# +1653#힐링_스태프# +2152#안티_데몬_쉴드# +16347#슈팅스타_상자# +16348#블러디피어_상자# +16349#알슈피스_상자# +16350#힐링_스태프_상자# +16351#안티_데몬_쉴드_상자# +16352#슈팅스타_상자# +16353#블러디피어_상자# +16354#알슈피스_상자# +16355#힐링_스태프_상자# +16356#안티_데몬_쉴드_상자# +18100#슈팅스타# + +// ------- 일본 책 아이템 ------- +11020#일본책1# +11021#일본책2# + +//------- 브라질 7월 책모자 ------- +5827#책파일_모자# + +// -------- 한국 7월 모바일캐시 -------- + +5529#마왕의_뼈_투구# +16135#마왕의_뼈_투구_상자# +16357#마왕의_뼈_투구_상자# +16358#모바일_펫_랜덤상자# + +// ----------- 한국 7월 순위달성이벤트 -------- +6228#무기_9_제련_보장권# +6229#무기_8_제련_보장권# +6230#무기_7_제련_보장권# +6231#무기_6_제련_보장권# +6232#방어구_9_제련_보장권# +6233#방어구_8_제련_보장권# +6234#방어구_7_제련_보장권# +6235#방어구_6_제련_보장권# + +// --------- 7월 브라질 로컬라이징 ------ +6237#과라나_열매# +11515#코코넛# +11516#아사이_열매# +12414#과라나_사탕# +11517#정화의_물약# + +// --------- 한국 7주년 이벤트 ---------- + +5318#포링_파티모자# +5590#포링_케이크_모자# +6236#파란_7_카드# + +16359#7주년_랜덤_상자# + +// ------- 프랑스 7월 유저공모 ------- +5591#사막의_왕자# +16360#사막의_왕자_상자# +16361#사막의_왕자_상자# + +// ------- 미국 7월 유저공모, 2010년 1월 한국 3개월 결제 ------- +5592#사이그룬의_날개# +16362#사이그룬의_날개_상자# +16363#사이그룬의_날개_상자# + +// ------- 2009-08-06 -------- +2462#슬레이프니르# +2830#메긴기오르드# +2831#브리싱가멘# +16007#묘르닐# +16364#슬레이프니르_상자# +16365#메긴기오르드_상자# +16366#브리싱가멘_상자# +16367#묘르닐_상자# + +// -------- 한국 8월 캐시 -------- +2832#프레이야의_반지# +5593#토끼_보닛# +5594#입에_문_도넛# +12416#행운의_알Ⅲ# +16369#프레이야의_반지_상자# +16370#프레이야의_반지_상자# +16407#프레이야의_반지_상자# +16408#프레이야의_반지_상자# + +// ------- 8월 필리핀 프리미엄 ------ +2833#오딘의_리콜# +16376#오딘의_리콜_7일_상자# +16377#오딘의_리콜_30일_상자# + +// -------- 8월 수정 아이템 ------- +2399#드래곤_베스트# +2553#드래곤의_망토# +2787#물방울_브로치# +2788#브라디움_이어링# +2789#브라디움_링# +2790#브라디움_브로치# +15000#본_플레이트# + +// -------- 8월 공성맵 상자 -------- +12415#공성맵_텔레포트_스크롤Ⅱ# +13940#공성맵_텔레포트_10개_상자# +14591#공성맵_텔레포트_스크롤# +16378#공성맵_텔레포트_30개_상자# +16379#공성맵_텔레포트Ⅱ_10개_상자# +16380#공성맵_텔레포트Ⅱ_30개_상자# +16381#공성맵_텔레포트_10개_상자# +16382#공성맵_텔레포트_30개_상자# +16383#공성맵_텔레포트Ⅱ_10개_상자# +16384#공성맵_텔레포트Ⅱ_30개_상자# + +// ------- 8월 PC방 ------- +5596#입에_문_네잎클로버# +5597#입에_문_풍선껌# +16372#입에_문_네잎클로버_상자# +16373#입에_문_네잎클로버_상자# +16374#입에_문_풍선껌_상자# +16375#입에_문_풍선껌_상자# +16385#입에_문_네잎클로버_상자Ⅱ# +16386#입에_문_네잎클로버_상자Ⅲ# +16387#입에_문_네잎클로버_상자Ⅱ# +16388#입에_문_네잎클로버_상자Ⅲ# +16389#입에_문_풍선껌_상자Ⅱ# +16390#입에_문_풍선껌_상자Ⅲ# +16391#입에_문_풍선껌_상자Ⅱ# +16392#입에_문_풍선껌_상자Ⅲ# + +// --------- 한국 8월 캐시제련 ---------- +6240#고농축_오리데오콘# +6241#고농축_에르늄# +16393#고농축_오리데오콘_5개_상자# +16394#고농축_오리데오콘_10개_상자# +16395#고농축_에르늄_5개_상자# +16396#고농축_에르늄_10개_상자# +16400#고농축_오리데오콘_5개_상자# +16401#고농축_오리데오콘_10개_상자# +16402#고농축_에르늄_5개_상자# +16403#고농축_에르늄_10개_상자# + + +// ----------- 미국 8월 황도12궁 ---------- +5598#쌍녀궁_하늘의_왕관# +5599#쌍녀궁_하늘의_보관# +16368#쌍녀궁_하늘의_왕관_상자# +16397#쌍녀궁_하늘의_보관_상자# +16398#쌍녀궁_하늘의_왕관_상자# +16399#쌍녀궁_하늘의_보관_상자# + +// -------- 2009년 8월 브라질 이벤트 --------- +5600#브라질_트윈리본# +5601#브라질_베레모# + +// ---------한국 8월 11제련권 ---------- +6238#무기_11_제련_보장권# +6239#방어구_11_제련_보장권# + +// ----------- 중국 8월 캐시 ------------ +6242#미드가르드_주화# +16405#미드가르드_주화_상자# +16406#미드가르드_주화_상자# + + +// ---------- 8월 대만 RTC 우승자 ----------- +5603#RTC_우승자_전용# +5604#RTC_준우승자_전용# +5605#RTC_3등_전용# + +// ----------- 8월 중동 캠푸미 --------- +5606#캠푸미_투구# + +// -------- 9월 대만 스크롤 -------- +16409#국화_스크롤# + +// --------- 9월 한국 RWC 상자 --------- +12473#RWC_참가_접수_기념상자# +12474#RWC_결승전_참가_기념상자# + +// --------- 구2008대만-------- +5451#RWC_우승자_전용투구# +5452#RWC_준우승자_전용투구# +5453#RWC_3등_전용투구# + +// -------- 9월 한국 캐시 1+1 ------- +16432#전투교범_패키지_상자A# +16433#전투교범_패키지_상자A# +16434#전투교범_패키지_상자B# +16435#전투교범_패키지_상자B# + +// ------- 9월 중국 국경일 ------ +5661#붉은_해적_두건# + +7962#보물지도1# +7963#보물지도2# +7964#보물지도3# +7965#보물지도4# +7966#이상한_양피지_첫_번째_조각# +7967#이상한_양피지_두_번째_조각# +7968#이상한_양피지_세_번째_조각# +7969#이상한_양피지_네_번째_조각# + +// ---------- 9월 한국 추석 --------- + +5457#옥토끼_모자# + +// --------- 9월 운영팀 이벤트 -------- + +6296#예비군_통지서# + +// -------- 9월 미국 유저공모 --------- +5664#필리르의_날개# +16440#필리르의_날개_상자# +16441#필리르의_날개_상자# + +// -------- 9월 미국 황도12궁 천칭궁 -------- +5662#천칭궁_하늘의_왕관# +5663#천칭궁_하늘의_보관# +16436#천칭궁_하늘의_왕관_상자# +16437#천칭궁_하늘의_왕관_상자# +16438#천칭궁_하늘의_보관_상자# +16439#천칭궁_하늘의_보관_상자# + + +// -------- 10월 대만 스크롤 --------- +16446#페가수스_스크롤# + +// -------- 9월 프랑스 유저공모 -------- +5667#해골_후드# + +// ------- 9월 한국 추석한정캐시 -------- +5665#무속인의_모자# +5666#금관모자# +16442#무속인의_모자_상자# +16443#무속인의_모자_상자# +16444#금관모자_상자# +16445#금관모자_상자# + +// -------- 10월 PC방 상자 ------ +12476#PC방_쿠폰_상자Ⅲ# + +// -------- 2009년 10월 GM이벤트 상자 ---------- +6302#GM의_손_편지# +12477#선물보따리# + +// ------- 2009 전세계 할로윈 ------- +5668#묘한_호박모자# +6298#깨진_호박머리# +6299#너덜한_천조각# + +// ---------- 2009년 10월 미국 황도12궁 -------- + +5676#천헐궁_하늘의_왕관# +5677#천헐궁_하늘의_보관# +16447#천헐궁_하늘의_왕관_상자# +16448#천헐궁_하늘의_보관_상자# +16449#천헐궁_하늘의_왕관_상자# +16450#천헐궁_하늘의_보관_상자# + +// --------- 2009년 10월 국내 마케팅이벤트 --------- +12478#기회의_상자# + +// -------- 2009년 10월 브라질 까마귀모자 --------- + +16451#까마귀_모자_상자# + +// -------- 2009년 10월 제네릭소서러 전용보상 -------- +2841#카라카스의_반지# +12479#카라카스의_반지_상자# + + +// -------- 2009년 10월 인도네시아 서약의 날 -------- +5828#명예의_금반지# +7970#편지_조각_첫_번째_장# +7971#편지_조각_두_번째_장# +7972#서약의_날_편지# + +// --------- 2009년 10월 브라질 악보모자 --------- +5678#악보_머리띠# + +// ------- 2009년 10월 한국 모바일, 대만 5월 캐시(광휘) --------- + +5567#광휘의_격노# +5682#트라이앵글_룬_캡# +16452#광휘의_격노_상자# +16453#트라이앵글_룬_캡_상자# +16454#광휘의_격노_상자# +16455#트라이앵글_룬_캡_상자# + +// ------ 2009년 10월 미국캐시 ------- + +2842#이어링# +5681#초록_리본# + +// ------ 2009년 10월 말레샤싱가폴캐시 ----- + +16456#새색시의_리본_스크롤# + +// ------ 2009년 10월 투구추가 ------- + +5683#대악마의_뿔_모형# +5684#화려한_왕관# +5685#어느_기사단의_군모# +5686#깃털이_멋진_모자# +5687#가벼운_뿔투구# + +// --------- 2009년 10월 한국 PC방 출석 이벤트 ------- + +12480#출석_3일_상자# +12481#출석_7일_상자# +12482#출석_10일_상자# +12483#출석_15일_상자# +12484#출석_20일_상자# +12485#출석_25일_상자# +12486#골드_PC방_1등_상자# +12487#PC방_네잎클로버_상자# +5584#마제스틱_데빌혼# + +// ------------ 2009년 10월 미국 유저공모 ---------- + +5689#개미_여왕의_왕관# +16459#개미_여왕의_왕관_상자# +16460#개미_여왕의_왕관_상자# + +// ---------- 2009년 10월 태국러브대디 ---------- +5688#2009_러브대디# +16458#2009_러브대디_상자# + +// ---------- 2009년 11월 대만 캐시스크롤 ----------- +16457#랑신전설# + +// ---------- 2009년 11월 필리핀 배청소 보상 이벤트 ----------- + +5691#선장의_모자# +5692#바다고양이_모자# + +// ---------- 2009년 11월 필리핀 노피어이벤트 ---------- +2464#NoFear_신발# +2845#NoFear_벨트# +5693#NoFear_언더웨어# +5694#NoFear_파워_헤드기어# +16463#NoFear_벨트_상자# +16464#NoFear_신발_상자# +16465#NoFear_언더웨어_상자# + +// ----------- 2009년 11월 티켓선물상자 ------------ +12488#티켓_선물상자# +12489#티켓_선물상자Ⅱ# + +// ---------- 2009년 11월 4분기 ------------- +5365#백련옥_모자# +5502#강령술사의_두건# +5527#루나틱_모자# +5536#크리스마스_카드# +5556#퍼씰_모자# +5572#세비지_베베_모자# +12493#행운의_알Ⅳ# +16467#크리스마스_카드_상자# +16468#크리스마스_카드_상자# +16469#크리스마스_카드_상자# +16470#크리스마스_카드_상자# + +// ---------- 2009년 11월 말레샤싱가폴스크롤 -------- + +16466#빛의_계란_스크롤# + +// ---------- 2009년 12월 전세계 이벤트 캐시 -------- + +5738#눈사람_모자# +16543#눈사람_모자_상자# +16544#눈사람_모자_상자# + +// ----------- 2009년 12월 한국 게임문화, 대만2009년 1월 ---------------- +5495#바람의_날개# +5505#아사라_요정_모자# +5574#잘근잘근_씹어놓은_연필# +14439#가이드북_상자# + +// --------- 12월 중국원단절 ---------- +5741#영생알껍질모자# +7973#영생알# + +// --------- 12월 해외크리스마스 --------- +7974#환영_조각# +12490#생생한_악보# +12491#신기한_눈뭉치# + +// --------- 12월 프리미엄 리셋돌 --------- +6320#프리미엄_리셋_돌# +16555#프리미엄_리셋_돌_상자# +16556#프리미엄_리셋_돌_상자# + +// --------- 2010년 1월_임시내부상자 -------- + +12529#화이트_슬림_포션_상자# +12530#마스테라의_열매_상자# +12531#하얀_포션_상자# +12532#로얄제리_상자# +12533#파란허브_상자# +12534#이그드라실_씨앗_상자# +12535#이그드라실_열매_상자# + +// -------- 2010년 2월 브라질PPL ---------- + +5845#버지볼보드# +5846#버지볼껌# +7626#버블껌토큰# +9028#딱딱한_떡# +12760#초록버블껌# +12761#노란버블껌# +12762#주홍버블껌# +12763#빨간버블껌# + +// --------- 2010년 2월 중국여성의날 ----------- + +5769#황금천사_조각상# +7629#분홍색_선물상자# + +// ----------- 2010년 2월 대만만우절 ------------- + +5847#만우절투구# +7627#현자의_열쇠# +7628#우인의_열쇠# +12764#대지야우_상자# + +// ------------ 2010년 2월 브라질 캐시 ------------ + +5761#나무늘보_모자# +5762#두네이르_투구# +16584#나무늘보_모자_상자# +16585#나무늘보_모자_상자# +16586#두네이르_투구_상자# +16587#두네이르_투구_상자# + +// ---------- 2010년 3월 미국 사려깊은상자 ---------- + +16588#사려깊은_상자# +16589#사려깊은_상자# +16590#사려깊은_상자# + +// ------------ 2010년 3월 필리핀Q2썸머 ------------- + +5851#여름의_기사# +12765#여름의_기사_상자# + +// ---------- 2010년 3월 대만노동절 ---------- + +12766#JOB_전투교범# + +// --------- 2010년 3월 필리핀 해변 ---------- + +7630#청정해변_빗자루# +7631#쓰레기_잔여물# + +// --------- 2010년 3월 브라질방학 ---------- + +2565#비치_타올# +5770#찰랑_모자# +16591#여름_스크롤_2# + +// ---------- 2010년 필리핀 패밀리 ---------- + +5771#패밀리_모자# +16592#패밀리_모자_상자# +16593#패밀리_모자_상자# + +// ---------- 2010년 3월 브라질 골든세비지 ---------- + +5850#골든_세비지_모자# + +// ---------- 2010년 3월 필리핀 알베르타 일일퀘 ---------- + +5772#붉은_해군_모자# +5773#해군의_베레모# +5774#붉은_해적_모자# + +// --------- 2010년 3월 해외 부활절 ---------- + +5852#부활의_알껍질# + +// --------- 2010년 4월 브라질캐시 ---------- + +5778#파란_아라라_모자# +5779#늘어진_보토# +5780#텐드릴리온_모자# +16601#파란_아라라_모자_상자# +16602#파란_아라라_모자_상자# +16603#늘어진_보토_상자# +16604#늘어진_보토_상자# +16605#텐드릴리온_모자_상자# +16606#텐드릴리온_모자_상자# + +// ----------- 2010년 4월 필리핀 캐시 ---------- + +5783#노란_토끼_머리띠# +5784#핑크_토끼_머리띠# +5785#그린_토끼_머리띠# +16619#노란_토끼_머리띠_상자# +16620#노란_토끼_머리띠_상자# +16621#핑크_토끼_머리띠_상자# +16622#핑크_토끼_머리띠_상자# +16623#그린_토끼_머리띠_상자# +16624#그린_토끼_머리띠_상자# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +// -------------- 3차 룬나이트, 아크비숍 ----------------- +12717#패럴라이즈# +12718#리치_엔드# +12719#오블리비언_커즈# +12720#데스_허트# +12721#톡신# +12722#파이렉시아# +12723#매직_머쉬룸# +12724#베놈_블리드# + +12725#노씨즈_룬스톤# +12726#라이도_룬스톤# +12727#베르카나_룬스톤# +12728#아이샤_룬스톤# +12729#에이시르_룬스톤# +12730#우르즈_룬스톤# +12731#튜리서스_룬스톤# +12732#페르쓰_룬스톤# +12733#하갈라즈_룬스톤# +12734#고급_룬_원석# +12735#고대의_룬_원석# +12736#신비한_룬_원석# +12737#일반_룬_원석# +12738#희귀한_룬_원석# +12333#안실라# +7931#독제조키트# +7932#독초_네리움# +7933#독초_란타나# +7934#독초_마쿨라타# +7935#독초_세라툼# +7936#독초_스코폴리아# +7937#독초_아모에나# +7938#빛의_알갱이# +7939#엘더의_가지# + +// 3차직업군 아이템-새도우체이서, 레인저 +6120#페이스_페인트# +6121#분장용_붓# +6122#페인트_붓# +6123#서페이스_페인트# +6124#늑대_피리# +7940#특수합금_트랩# +12341#특수합금_트랩상자# + +6128#해독제# +6144#안타까운_눈물# +6145#발칸_블릿# +6146#마도_기어_연료# +6147#액체냉각탄# +1549#파일벙커# +12383#발칸_블릿_탄창# +2139#화염방사기# +2800#가속장치# +2801#호버링_부스터# +2802#자폭장치# +2803#셰이프_쉬프터# +2804#냉각장치# +2805#자기장_필드_생성기# +2806#배리어_생성기# +2807#리페어_키트# +12392#리페어_A# +12393#리페어_B# +12394#리페어_C# +18000#캐논볼# +18001#홀리_캐논_볼# +18002#다크_캐논_볼# +18003#소울_캐논_볼# +18004#아이언_캐논_볼# + +2808#광학_미채_발생기# +2809#고급냉각장치# +2810#특수냉각장치# +6186#몽키_스패너# + +// ---------3차 전직퀘스트--------- +12380#전직시험용_피리# +12381#고대언어_두루마리# +12382#고대언어_두루마리# +2140#에너지_룬_가드# +2794#마력석_반지# +2795#풋사과_반지# +2796#마력_깃든_돌# +2797#마력_깃든_돌# +5516#호크아이# +5517#호크아이# +6150#저택의_열쇠# +6151#거대한_브라디움_조각# +6152#빛나는_수정# +6153#특별한_교환티켓# +6154#조각난_뿔피리# +12384#무지갯빛_루비# +12385#무지갯빛_루비# +12386#무지갯빛_루비# +12387#무지갯빛_루비# +12388#수습용_라이도_룬_스톤# +12389#수습용_페르쓰_룬_스톤# +12390#수습용_베르카나_룬_스톤# +15002#룬_플레이트# +2798#지친_전사의_의지# +6156#결제를_기다리는_보고서# + + +// -------- 워록 추가스킬 아이템 -------- +6189#마법서(파이어_볼트)# +6190#마법서(콜드_볼트)# +6191#마법서(라이트닝_볼트)# +6192#마법서(스톰_가스트)# +6193#마법서(로드_오브_버밀리온)# +6194#마법서(메테오_스톰)# +6195#마법서(커미트)# +6196#마법서(테트라_볼텍스)# +6197#마법서(썬더_스톰)# +6198#마법서(유피텔_선더)# +6199#마법책(워터_볼)# +6200#마법서(헤븐즈_드라이브)# +6201#마법서(어스_스파이크)# +6202#마법서(어스_스트레인)# +6203#마법서(체인_라이트닝)# +6204#마법서(크림즌_록)# +6205#마법서(드레인_라이프)# + +// ------- 3차 제네릭 아이템 ------- +6210#가시나무_씨앗# +6211#흡혈_식물_씨앗# +6212#폭탄_버섯_포자# +6213#폭발_가루# +6214#연막_가루# +6215#최루_가스# +6216#기름병# +6217#만드라고라의_화분# + +// ------ 3차 원더러민스트렐 아이템 ------ +11513#목_보호_캔디# + +// --------- 9월 3-2차 전직용 아이템 --------- + +6266#기만의_열쇠# +6267#허상의_열쇠# +6268#환락의_열쇠# +6269#명장의_붓# +6270#민스트렐_송의_사진# +6271#영수증# +6272#실험용_씨앗# +6273#실험용_씨앗# +6274#성자의_옷조각# +6275#왕의_방패# +6276#투명_시약# +6277#붉은_시약# +6278#검은_시약# + + +// --------- 3-2차 제네릭추가아이템 ---------- + +6244#건_파우더# +6245#까만색_가루# +6246#노란색_가루# +6247#하얀색_가루# +6248#잡탕_항아리# +6249#세비지의_고기# +6250#요리용_쇠_꼬챙이# +6251#검정_숯# +6252#늑대의_피# +6253#차가운_얼음# +6254#소_머릿고기# +6255#커다란_냄비# +6256#얼음_파편# +6257#얼음_결정# +6258#코모도_열대과일# +6259#드로세라의_촉수# +6260#쁘띠의_꼬리# +6261#소면# +6262#시원한_육수# +6263#야자열매# +6264#멜론# +6265#파인애플# +6279#사과_폭탄_제조책# +6280#파인애플_폭탄_제조책# +6281#야자열매_폭탄_제조책# +6282#멜론_폭탄_제조책# +6283#바나나_폭탄_제조책# +6284#식물_유전자_재배법# +6285#상급_포션_제조_매뉴얼# +11022#믹스_쿠킹_요리책# +11023#폭발적인_체력_증진_연구서# +11024#활력_드링크_제조법# +12418#풀_스윙K# +12419#마나_플러스# +12422#HP_증가_포션(소)# +12423#HP_증가_포션(중)# +12424#HP_증가_포션(대)# +12425#SP_증가_포션(소)# +12426#SP_증가_포션(중)# +12427#SP_증가_포션(대)# +12428#농축_화이트_포션Z# +12429#세비지_통구이# +12430#칵테일_워그_블러드# +12431#마이너_양지머리# +12432#시로마_아이스티# +12433#드로세라_허브_찜# +12434#쁘띠_꼬리_국수# +12435#검은_물체# +12436#비타타500# +12437#농축_샐러마인즙# +12475#큐어_프리# +13260#사과_폭탄# +13261#야자열매_폭탄# +13262#멜론_폭탄# +13263#파인애플_폭탄# +13264#바나나_폭탄# +13265#검은_덩어리# +13266#검고_딱딱한_덩어리# +13267#무지_딱딱한_덩어리# +13268#알_수_없는_가루# +13269#투척용_부스트500# +13270#투척용_풀_스윙K# +13271#투척용_마나_플러스# +13272#투척용_큐어_프리# +13273#투척용_불끈불끈M# +13274#투척용_활면수F# +13275#투척용_HP_증가_포션(소)# +13276#투척용_HP_증가_포션(중)# +13277#투척용_HP_증가_포션(대)# +13278#투척용_SP_증가_포션(소)# +13279#투척용_SP_증가_포션(중)# +13280#투척용_SP_증가_포션(대)# +13281#투척용_농축_화이트_포션Z# +13282#투척용_비타타500# +13283#투척용_농축_샐러마인즙# +13284#투척용_세비지_통구이# +13285#투척용_칵테일_워그_블러드# +13286#투척용_마이너_양지머리# +13287#투척용_시로마_아이스티# +13288#투척용_드로세라_허브_찜# +13289#투척용_쁘띠_꼬리_국수# +13290#투척용_검은_물체# +6297#투척용_병# + +// ---------- 2009년 11월 Ep13.3 퀘스트아이템 ------------ +2463#야성의_부츠# +2564#야성의_꼬리# +2843#황금_방울# +2844#엘_디카스테스의_빛# +4760#MATK+1%# +4761#MATK+2%# +4762#FLEE+6# +4763#FLEE+12# +4764#CRI+5# +4765#CRI+7# +4766#ATK+2%# +4767#ATK+3%# +6304#사파_공훈증# +6305#얼어붙은_피부조각# +6306#단단하게_굳은_혈흔# +6307#수상한_마력의_돌# +6308#미확인_유물# +6319#조그만_브라디움# +6340#빛_바랜_악보(초록)# +6341#빛_바랜_악보(빨강)# +6342#빛_바랜_악보(보라)# +6343#빛_바랜_악보(파랑)# + +// ---------- EP13.3 신규 아이템 ------------ +6321#갈퀴_뿔_투구# +6322#사슴_뿔_투구# +6323#쌍뿔_투구# +6324#외뿔_투구# +6325#하얀_거미다리# +6326#여왕의_날개조각# +1196#크롬_메탈_투핸드_소드# +1433#임페리얼_스피어# +1654#멘탈_스틱# +1830#패황난무# +1930#그린_휘슬# +1984#스템_휩# +1985#로제바인# +2153#임페리얼_가드# +2465#댄스_구두# +2853#텔레키네틱_오브# +2854#연금술의_글러브# +13061#블랙_윙# +13062#에인션트_대거# +13431#크롬_메탈_소드# +16010#빨간_에테르_가방# +18103#미스틱_보우# + +// ------------ 12월 국내 크리스마스 ---------- + +5742#루돌프_산타모# +12492#구겨진_종이뭉치# +6328#1월_달력# +6329#2월_달력# +6330#3월_달력# +6331#4월_달력# +6332#5월_달력# +6333#6월_달력# +6334#7월_달력# +6335#8월_달력# +6336#9월_달력# +6337#10월_달력# +6338#11월_달력# +6339#12월_달력# + + +// ---------- 한국 2010년 1월 보스니아 ------------ + +12539#화려한_상자# + +// ---------- 한국 2010년 1월 3차 전용 투구 ---------- + +5746#룬_서클릿# +5747#미트라# +5748#스나이퍼_고글# +5749#드라이버_밴드# +5750#그림자_공작# +5751#민스트럴_송의_모자# +5752#마이더스의_속삭임# +5753#마력의_돌_모자# +5754#불타는_혼# +5755#침묵의_집행자# +5756#바람의_속삭임# +5757#복각_슈미츠의_투구# +5758#빈사의_백조# +5760#드라이버_밴드# + +// ------------ 2010년 한국 2월 설날 ------------ + +12327#행운의_부적# +12536#신년_떡국# + +// ------------ 2010년 2월 한국 발렌타인 ----------- +6345#사랑뭉치# +12537#솔로의_선물바구니# +12538#커플의_이벤트_바구니# + +// ------------- 2010년 2월 라크마 ------------- + +13049#라크마# +16580#라크마_상자# +16581#라크마_상자# + +// ----------- 2010년 2월 한국 덕담이벤트 ----------- + +6347#덕담_종이# +6348#덕담_종이# +6349#덕담_종이# +6350#덕담_종이# +6351#덕담_종이# +6352#덕담_종이# +6353#덕담_종이# +6354#덕담_종이# +6355#덕담_종이# +6356#덕담_종이# +6357#실패한_포춘쿠키# +6358#무료캐시_이용권# +6359#가이드북_교환권# +12540#운영자의_워프상자# +12541#포춘쿠키# +12542#포춘쿠키# +12543#포춘쿠키# +12544#묘한_나뭇가지# + +// ------------ 2010년 2월 속성석추가 ------------ + +6360#스칼렛_포인트# +6361#인디고_포인트# +6362#옐로우위시_포인트# +6363#라임그린_포인트# + +// ----------- 2010년 3월 한국 만우절 ------------ + +4454#밝히리_카드# +4455#밝히리_카드# +5775#입에_문_초코도넛# +12546#수상한_요리# + +// ---------- 2010년 3월 한국 행운의 알 -------- + +5511#사만바이아# +12545#행운의_알Ⅴ# +16598#활성화_포션_상자Ⅱ# +16599#활성화_포션_상자Ⅱ# + +// ---------- 2010년 4월 한국 그라비티10주년 ---------- +2709#G10주년_코인# +5106#G10주년_꼬깔모자# +13763#G10주년_코인_상자# + +// ---------- 2010년 4월 봄맞이 ---------- + +5781#페르시카# + +// ---------- 2010년 4월 소서러 추가 --------- +11056#정령술_안내서# + +// --------- 2010년 4월 상자 ---------- +12549#화이트_슬림_포션_상자# +12550#독약병_상자# +5786#고대요정의_귀# +5476#그랜드_페코의_머리띠# +16629#그랜드_페코의_머리띠_상자# +16630#그랜드_페코의_머리띠_상자# +2566#아스프리카(체험판)# +2856#메긴기오르드(체험판)# +2857#브리싱가멘(체험판)# +15023#브륀힐트(체험판)# +16625#아스프리카_상자# +16626#메긴기오르드_상자# +16627#브리싱가멘_상자# +16628#브륀힐트_상자# +5788#3D안경# +6377#구매노점_허가증# +12548#허술한_구매노점_허가증# +6378#대전_승리의_증표# diff --git a/openkore_llm_knowledge/tables/kRO/maps.txt b/openkore_llm_knowledge/tables/kRO/maps.txt new file mode 100755 index 0000000000..f4d8d7d598 --- /dev/null +++ b/openkore_llm_knowledge/tables/kRO/maps.txt @@ -0,0 +1,723 @@ +//사크라이서버 10.03.10 +dicastes01.rsw#사파 수도 엘 디카스테스# +dicastes02.rsw#브룸벨드 요르히 숲# +dic_in01.rsw#디카스테스 내부# +dic_fild01.rsw#카미달 산기슭# +dic_fild02.rsw#카미달 산기슭# +dic_dun01.rsw#카미달 터널# +dic_dun02.rsw#스카라바 홀# +job3_gen01.rsw#제네릭 연구소# +s_atelier.rsw#그림자 공방# +brasilis.rsw#브라질리스# +bra_in01.rsw#브라질리스 내부# +bra_fild01.rsw#브라질리스 필드# +bra_dun01.rsw#브라질리스 폭포 안의 동굴 입구# +bra_dun02.rsw#브라질리스 폭포 안의 동굴 내부# +moc_para01.rsw#모로크 낙원단 내부# +job3_arch01.rsw#아크비숍 전직 대기실# +job3_arch02.rsw#휘겔 오딘 신전# +job3_arch03.rsw#아크비숍 전직 대기실# +job3_guil01.rsw#비밀 주점# +job3_guil02.rsw#허름한 창고 내부# +job3_guil03.rsw#외딴 저택# +job3_rang01.rsw#레인저 전직 대기실# +job3_rang02.rsw#레인저 전직 시험장# +job3_rune01.rsw#룬나이트 기사단 내부# +job3_rune02.rsw#룬나이트 전직 시험장# +job3_rune03.rsw#룬나이트 전직 시험장# +job3_war01.rsw#워록 전직 시험장# +job3_war02.rsw#워록 전직 시험장# +jupe_core2.rsw#유페로스 중심부# +1@nyd.rsw#니드호그의 둥지# +2@nyd.rsw#니드호그의 둥지# +nyd_dun01.rsw#스플랑디드 난폭자의 상처 1층# +nyd_dun02.rsw#스플랑디드 난폭자의 상처 2층# +manuk.rsw#광산마을 마누크# +man_fild02.rsw#마누크 필드# +man_in01.rsw#마누크 내부# +splendide.rsw#라피네 전진기지 스플랑디드# +spl_fild01.rsw#스플랑디드 필드# +spl_in01.rsw#스플랑디드 야전사령부 내부# +spl_in02.rsw#스플랑디드 내부# +bat_c01.rsw#크리거 폰 미드가르드# +bat_c02.rsw#크리거 폰 미드가르드# +bat_c03.rsw#크리거 폰 미드가르드# +mid_camp.rsw#미드가르드 연합 주둔지# +mid_campin.rsw#미드가르드 연합 주둔지 내부# +man_fild01.rsw#마누크 필드# +man_fild03.rsw#마누크 필드# +spl_fild02.rsw#스플랑디드 필드# +spl_fild03.rsw#스플랑디드 필드# +moc_fild22b.rsw#소그라트 사막 차원의 균열# +que_dan01.rsw#휘겔 필드# +que_dan02.rsw#유노의 버려진 집 내부# +schg_que01.rsw#모어스톤의 풀밭# +schg_dun01.rsw#길드 지하 던전# +arug_que01.rsw#모어스톤의 풀밭# +arug_dun01.rsw#길드 지하 던전# +1@orcs.rsw#오크 지하 동굴# +2@orcs.rsw#오크 지하 동굴# +1@cata.rsw#카타콤# +2@cata.rsw#봉인된 신전# +e_tower.rsw#미스티 아일랜드# +1@tower.rsw#엔들레스 타워# +2@tower.rsw#엔들레스 타워# +3@tower.rsw#엔들레스 타워# +4@tower.rsw#엔들레스 타워# +5@tower.rsw#엔들레스 타워# +6@tower.rsw#엔들레스 타워(알 수 없는 곳)# +bat_room.rsw#전장 대기실# +bat_a01.rsw#티에라 협곡# +bat_a02.rsw#티에라 협곡# +bat_b01.rsw#플라비우스# +bat_b02.rsw#플라비우스# +que_qsch01.rsw#허상의 오콜니르# +que_qsch02.rsw#허상의 오콜니르# +que_qsch03.rsw#허상의 오콜니르# +que_qsch04.rsw#허상의 오콜니르# +que_qsch05.rsw#허상의 오콜니르# +que_qaru01.rsw#허상의 오콜니르# +que_qaru02.rsw#허상의 오콜니르# +que_qaru03.rsw#허상의 오콜니르# +que_qaru04.rsw#허상의 오콜니르# +que_qaru05.rsw#허상의 오콜니르# +arug_cas01.rsw#발프레이야 길드# +arug_cas02.rsw#발프레이야 길드# +arug_cas03.rsw#발프레이야 길드# +arug_cas04.rsw#발프레이야 길드# +arug_cas05.rsw#발프레이야 길드# +aru_gld.rsw#발프레이야# +moscovia.rsw#모스코비아# +mosk_in.rsw#모스코비아 내부# +mosk_ship.rsw#배# +mosk_fild01.rsw#고래 섬# +mosk_fild02.rsw#모스코비아 필드# +mosk_dun01.rsw#모스코비아 숲# +mosk_dun02.rsw#모스코비아 깊은 숲# +mosk_dun03.rsw#모스코비아 깊고 깊은 숲# +schg_cas01.rsw#니다뵐 길드# +schg_cas02.rsw#니다뵐 길드# +schg_cas03.rsw#니다뵐 길드# +schg_cas04.rsw#니다뵐 길드# +schg_cas05.rsw#니다뵐 길드# +sch_gld.rsw#니다뵐# +cave.rsw#동굴마을# +moc_fild20.rsw#소그라트 사막 차원의 균열# +moc_fild21.rsw#소그라트 사막 차원의 균열# +moc_fild22.rsw#소그라트 사막 차원의 균열# +bossnia_01.rsw#보스니아# +bossnia_02.rsw#보스니아# +bossnia_03.rsw#보스니아# +bossnia_04.rsw#보스니아# +itemmall.rsw#아이템 몰# +poring_w01.rsw#포링대전 대기실# +poring_w02.rsw#포링대전장# +nameless_i.rsw#이름없는 섬# +nameless_n.rsw#이름없는 섬# +nameless_in.rsw#이름없는 섬 내부# +abbey01.rsw#이름없는 섬 수도원 1층# +abbey02.rsw#이름없는 섬 수도원 지하 1층# +abbey03.rsw#이름없는 섬 수도원 지하 2층# +que_temsky.rsw#교황 집무실(하늘정원)# +z_agit.rsw#Z단의 아지트# +veins.rsw#협곡마을 베인스# +ve_in.rsw#베인스 내부# +ve_in02.rsw#베인스 내부# +ve_fild01.rsw#베인스 필드# +ve_fild02.rsw#베인스 필드# +ve_fild03.rsw#베인스 필드# +ve_fild04.rsw#베인스 필드# +//ve_fild05.rsw#베인스 필드# 2008년 리뉴얼 +ve_fild06.rsw#베인스 필드# +ve_fild07.rsw#베인스 필드# +thor_camp.rsw#베인스 토르 화산 병참기지# +que_thor.rsw#베인스 토르 화산 던전# +thor_v01.rsw#베인스 토르 화산 던전 1층# +thor_v02.rsw#베인스 토르 화산 던전 2층# +thor_v03.rsw#베인스 토르 화산 던전 3층# +rachel.rsw#아루나펠츠 교국 수도 라헬# +ra_in01.rsw#라헬 내부# +ra_temple.rsw#프레이야 대신전(세스룸니르)# +ra_temin.rsw#신전 내부# +que_rachel.rsw#신전 내부# +ra_temsky.rsw#교황 집무실(하늘 정원)# +ra_fild01.rsw#라헬 아우둠라 초원# +//ra_fild02.rsw#라헬 오즈 협곡# 2008년 리뉴얼 +ra_fild03.rsw#라헬 이다 평원# +ra_fild04.rsw#라헬 아우둠라 초원# +ra_fild05.rsw#라헬 아우둠라 초원# +ra_fild06.rsw#포르투 루나# +//ra_fild07.rsw#라헬 오즈 협곡# 2008년 리뉴얼 +ra_fild08.rsw#라헬 이다 평원# +//ra_fild09.rsw#라헬 아우둠라 초원# 2008년 리뉴얼 +//ra_fild10.rsw#라헬 오즈 협곡# 2008년 리뉴얼 +//ra_fild11.rsw#라헬 이다 평원# 2008년 리뉴얼 +ra_fild12.rsw#라헬 이다 평원# +//ra_fild13.rsw#라헬 눈물의 해안# 2008년 리뉴얼 +ra_san01.rsw#라헬 신전 성역 1층 북쪽 구역# +ra_san02.rsw#라헬 신전 성역 1층 서쪽 구역# +ra_san03.rsw#라헬 신전 성역 1층 동쪽 구역# +ra_san04.rsw#라헬 신전 성역 1층 남쪽 구역# +ra_san05.rsw#라헬 신전 성역 2층 중앙 구역# +ice_dun01.rsw#라헬 얼음 동굴 1층# +ice_dun02.rsw#라헬 얼음 동굴 2층# +ice_dun03.rsw#라헬 얼음 동굴 3층# +ice_dun04.rsw#라헬 얼음 동굴 봉인의 공간# +hugel.rsw#전원도시 휘겔# +hu_in01.rsw#휘겔 내부# +que_bingo.rsw#빙고 경기장# +que_hugel.rsw#오딘 신전 지하# +p_track01.rsw#몬스터 레이스 경기장# +p_track02.rsw#몬스터 레이스 경기장# +odin_tem01.rsw#휘겔 오딘 신전 서쪽 지역# +odin_tem02.rsw#휘겔 오딘 신전 남쪽 지역# +odin_tem03.rsw#휘겔 오딘 신전 북쪽 지역# +hu_fild02.rsw#휘겔 필드# +//hu_fild03.rsw#휘겔 필드# 2008년 리뉴얼 +hu_fild06.rsw#휘겔 필드# +ein_fild01.rsw#아인브로크 필드# +//ein_fild02.rsw#아인브로크 필드# 2008년 리뉴얼 +ein_fild05.rsw#아인브로크 필드# +//yuno_fild10.rsw#유노 필드# 2008년 리뉴얼 +kh_kiehl02.rsw#키엘의 마지막 방# +kh_kiehl01.rsw#키엘의 방# +kh_dun02.rsw#기계인형공장 2층# +kh_dun01.rsw#기계인형공장 1층# +kh_mansion.rsw#킬 하이르 저택# +kh_rossi.rsw#로시미에르가 저택# +kh_school.rsw#킬 하이르 학원# +kh_vila.rsw#킬 하이르 별장# +auction_01.rsw#경매장# +auction_02.rsw#경매장# +que_job01.rsw#비밀주점# +abyss_01.rsw#휘겔 어비스 호수 지하 동굴 1층# +abyss_02.rsw#휘겔 어비스 호수 지하 동굴 2층# +abyss_03.rsw#휘겔 어비스 호수 지하 동굴 3층# +tha_t01.rsw#타나토스 타워 하층부 박물관 입구# +tha_t02.rsw#타나토스 타워 하층부 박물관# +tha_t03.rsw#타나토스 타워 하층부 버려진 공간# +tha_t04.rsw#타나토스 타워 하층부 버려진 공간# +tha_t05.rsw#타나토스 타워 상층부# +tha_t06.rsw#타나토스 타워 상층부# +tha_t07.rsw#타나토스 타워 상층부 천사의 방# +tha_t08.rsw#타나토스 타워 상층부 천사의 방# +tha_t09.rsw#타나토스 타워 상층부 고뇌의 방# +tha_t10.rsw#타나토스 타워 상층부 슬픔의 방# +tha_t11.rsw#타나토스 타워 상층부 절망의 방# +tha_t12.rsw#타나토스 타워 상층부 증오의 방# +thana_step.rsw#타나토스 타워 상층부 계단# +thana_boss.rsw#타나토스 타워(알 수 없는 곳)# +thana_scene01.rsw#타나토스 타워 앞# +job_soul.rsw#당신의 마음# +job_star.rsw#태양과 달과 별# +//hu_fild07.rsw#휘겔 필드# 2008년 리뉴얼 +hu_fild05.rsw#휘겔 어비스 호수# +hu_fild04.rsw#휘겔 필드# +hu_fild01.rsw#타나토스 타워 앞# +yuno_fild06.rsw#엘메스 플레투# +quiz_02.rsw#퀴즈 경기장# +jupe_cave.rsw#유페로스 동굴 입구# +juperos_01.rsw#유페로스 폐허 외부# +juperos_02.rsw#유페로스 폐허 내부# +jupe_gate.rsw#유페로스 보안 구역# +jupe_area1.rsw#유페로스 보안 구역# +jupe_area2.rsw#유페로스 보안 구역# +jupe_ele.rsw#유페로스 엘리베이터# +jupe_ele_r.rsw#유페로스 엘리베이터실# +jupe_core.rsw#유페로스 중심부# +lighthalzen.rsw#기업도시 리히타르젠# +lhz_in01.rsw#레켄베르 본사# +lhz_in02.rsw#리히타르젠 내부# +lhz_in03.rsw#리히타르젠 내부# +lhz_cube.rsw#큐브 룸# +lhz_que01.rsw#리히타르젠 내부# +lhz_airport.rsw#리히타르젠 공항# +airplane_01.rsw#비공정# +lhz_dun01.rsw#생체실험 연구소 지하 1층# +lhz_dun02.rsw#생체실험 연구소 지하 2층# +lhz_dun03.rsw#생체실험 연구소 지하 3층# +lhz_fild01.rsw#리히타르젠 필드# +yuno_pre.rsw#슈발츠발드 정부 청사# +y_airport.rsw#유노 공항# +lhz_fild03.rsw#리히타르젠 필드# +lhz_fild02.rsw#리히타르젠 필드(사신의 계곡)# +ein_fild04.rsw#아인브로크 필드# +ein_fild03.rsw#아인브로크 필드# +ein_dun02.rsw#아인베흐 광산 2층# +ein_dun01.rsw#아인베흐 광산 1층# +//ein_fild10.rsw#아인브로크 필드# 2008년 리뉴얼 +ein_fild09.rsw#아인브로크 필드# +ein_fild08.rsw#아인브로크 필드# +ein_fild07.rsw#아인브로크 필드# +ein_fild06.rsw#아인브로크 필드# +airplane.rsw#비공정# +airport.rsw#아인브로크 공항# +ein_in01.rsw#실내# +einbech.rsw#채광마을 아인베흐# +einbroch.rsw#강철도시 아인브로크# +turbo_e_16.rsw#터보트랙 경기장# +turbo_e_8.rsw#터보트랙 경기장# +turbo_e_4.rsw#터보트랙 경기장# +turbo_n_16.rsw#터보트랙 경기장# +turbo_n_8.rsw#터보트랙 경기장# +turbo_n_4.rsw#터보트랙 경기장# +turbo_n_1.rsw#터보트랙 경기장# +turbo_room.rsw#터보트랙 대기실# +yuno_fild12.rsw#슈발츠발드 국경 검문소# +yuno_fild11.rsw#유노 필드# +yuno_fild09.rsw#슈발츠발드 경비대 야영지# +yuno_fild08.rsw#유노 킬 하이르 학원# +yuno_fild07.rsw#엘메스 협곡(심연의 골짜기)# +//yuno_fild05.rsw#엘메스 플레투# 2008년 리뉴얼 +ayo_in02.rsw#아요타야 내부# +ayo_in01.rsw#아요타야 내부# +ayo_dun02.rsw#아요타야 고대 신전 내부# +ayo_dun01.rsw#아요타야 고대 신전 미궁# +ayo_fild02.rsw#아요타야 필드# +ayo_fild01.rsw#아요타야 필드# +ayothaya.rsw#아요타야# +que_god02.rsw#퀘스트맵# +que_god01.rsw#퀘스트맵# +quiz_test.rsw#시험장# +gefenia04.rsw#게페니아# +gefenia03.rsw#게페니아# +gefenia02.rsw#게페니아# +gefenia01.rsw#게페니아# +himinn.rsw#발키리 신전(히민)# +jawaii_in.rsw#자와이 내부# +jawaii.rsw#신혼섬 자와이# +lou_in02.rsw#용지성 내부# +lou_in01.rsw#용지성 내부# +lou_dun03.rsw#용지성 센양궁# +lou_dun02.rsw#용지성 왕릉 내부# +lou_dun01.rsw#용지성 왕릉# +lou_fild01.rsw#용지성 필드# +louyang.rsw#고도 용지성# +valkyrie.rsw#발키리 신전(위대한 전사의 전당)# +nif_in.rsw#니플헤임 내부# +yggdrasil01.rsw#흐베르겔미르의 샘(이그드라실줄기)# +nif_fild02.rsw#니플헤임 굘의 골짜기# +nif_fild01.rsw#니플헤임 외딴마을 스켈링튼# +niflheim.rsw#죽은자의 나라 니플헤임# +um_dun01.rsw#움발라 나무 속의 목공소# +um_dun02.rsw#움발라 나무 속의 이계 통로# +um_in.rsw#움발라 내부# +um_fild01.rsw#움발라 루루카 숲# +um_fild02.rsw#움발라 훔가 숲# +um_fild03.rsw#움발라 카라라 늪# +um_fild04.rsw#움발라 훔가 정글# +umbala.rsw#우탄족마을 움발라# +sec_in01.rsw#발할라궁 내부# +sec_in02.rsw#발할라궁 내부# +sec_pri.rsw#발할라궁 반성의 방(감옥)# +gon_test.rsw#쿤룬 격투장# +gon_dun01.rsw#쿤룬 서왕모 신전# +gon_dun02.rsw#쿤룬 신선의 바둑판# +gon_dun03.rsw#쿤룬 무릉도원# +gon_fild01.rsw#쿤룬 필드# +gon_in.rsw#쿤룬 내부# +gonryun.rsw#신선의 섬 쿤룬(崑崙)# +ama_test.rsw#아마쯔 모모타로 체험소# +ama_dun03.rsw#아마쯔 지하 신사# +ama_dun02.rsw#아마쯔 지하 숲 전장# +ama_dun01.rsw#아마쯔 다다미 미궁# +ama_fild01.rsw#아마쯔 필드# +ama_in02.rsw#천수각 내부# +ama_in01.rsw#아마쯔 내부# +amatsu.rsw#천수의 나라 아마쯔# +alde_alche.rsw#알케미스트 전직소# +yuno_in05.rsw#이미르의 심장 동력실# +yuno_in04.rsw#공화국 도서관# +job_duncer.rsw#코모도 소극장# +job_sage.rsw#세이지 전직 시험장# +job_cru.rsw#크루세이더 전직 시험장# +job_monk.rsw#성 카피톨리나 수도원# +monk_test.rsw#성 카피톨리나 수도원# +in_rogue.rsw#로그 길드 내부# +mag_dun02.rsw#유노 노그로드 2층# +mag_dun01.rsw#유노 노그로드 1층# +yuno_fild04.rsw#엘메스 플레투# +yuno_fild03.rsw#엘메스 플레투# +yuno_fild02.rsw#킬 하이르의 별장# +yuno_fild01.rsw#슈발츠발드 국경 검문소# +yuno_in03.rsw#유노 내부# +yuno_in02.rsw#세이지 케슬내부# +yuno_in01.rsw#유노 내부# +yuno.rsw#슈발츠발드 공화국 수도 유노# +job_wiz.rsw#위져드 전직 시험장# +job_prist.rsw#프리스트 전직 시험장# +job_knt.rsw#나이트 전직 시험장# +job_hunte.rsw#헌터 전직 시험장# +gld_dun04.rsw#길드 지하 던전# +gld_dun03.rsw#길드 지하 던전# +gld_dun02.rsw#길드 지하 던전# +gld_dun01.rsw#길드 지하 던전# +payg_cas05.rsw#청림 호수 길드# +payg_cas04.rsw#청림 호수 길드# +payg_cas03.rsw#청림 호수 길드# +payg_cas02.rsw#청림 호수 길드# +payg_cas01.rsw#청림 호수 길드# +pay_gld.rsw#청림 호수# +aldeg_cas05.rsw#루이나 길드# +aldeg_cas04.rsw#루이나 길드# +aldeg_cas03.rsw#루이나 길드# +aldeg_cas02.rsw#루이나 길드# +aldeg_cas01.rsw#루이나 길드# +alde_gld.rsw#알데바란 위성도시 루이나# +gefg_cas05.rsw#브리토리아 길드# +gefg_cas04.rsw#브리토리아 길드# +gefg_cas03.rsw#브리토리아 길드# +gefg_cas02.rsw#브리토리아 길드# +gefg_cas01.rsw#브리토리아 길드# +prtg_cas05.rsw#발키리 렐름 길드# +prtg_cas04.rsw#발키리 렐름 길드# +prtg_cas03.rsw#발키리 렐름 길드# +prtg_cas02.rsw#발키리 렐름 길드# +prtg_cas01.rsw#발키리 렐름 길드# +prt_gld.rsw#발키리 렐름# +tur_dun01.rsw#거북이 섬# +tur_dun02.rsw#거북이 섬 던전# +tur_dun03.rsw#양거촌# +tur_dun04.rsw#구양궁# +tur_dun05.rsw#지하 늪 지대# +tur_dun06.rsw#지하 늪 지대# +guild_vs5.rsw#길드 대항 경기장# +guild_vs4.rsw#길드 대항 경기장# +guild_vs3.rsw#길드 대항 경기장# +guild_vs2.rsw#길드 대항 경기장# +guild_vs1.rsw#길드 대항 경기장# +guild_room.rsw#길드 대항 경기장 대기실# +quiz_00.rsw#퀴즈 경기장# +quiz_01.rsw#퀴즈 경기장# +//gef_fild12.rsw#코트 숲# 2008년 리뉴얼 +gef_fild13.rsw#브리토리아# +//gef_fild14.rsw#서쪽 오크 마을# 2008년 리뉴얼 +cmd_in02.rsw#코모도 내부# +cmd_in01.rsw#코모도 내부# +comodo.rsw#해변도시 코모도# +beach_dun.rsw#코모도 서쪽 동굴 카루# +beach_dun2.rsw#코모도 북쪽 동굴 루안다# +beach_dun3.rsw#코모도 동쪽 동굴 마오# +cmd_fild01.rsw#코모도 파푸치카 숲# +cmd_fild02.rsw#코모도 코코모 해변# +cmd_fild03.rsw#코모도 지나이 늪# +cmd_fild04.rsw#코모도 코코모 해변# +//cmd_fild05.rsw#코모도 파푸치카 숲 경계# 2008년 리뉴얼 +cmd_fild06.rsw#요새도시 산다르만 서쪽 지역# +cmd_fild07.rsw#파로스 등대섬# +cmd_fild08.rsw#요새도시 산다르만 동쪽 지역# +cmd_fild09.rsw#요새도시 산다르만 남쪽 지역# +xmas_in.rsw#루티에 내부# +xmas_dun02.rsw#루티에 장난감 분류소# +xmas_dun01.rsw#루티에 장난감 공장 창고# +xmas_fild01.rsw#루티에 필드# +xmas.rsw#눈의 마을 루티에(Lutie)# +mjolnir_01.rsw#묘르닐 산맥 북쪽 지역# +mjolnir_02.rsw#묘르닐 산맥 북쪽 지역# +mjolnir_03.rsw#묘르닐 산맥 북쪽 지역# +mjolnir_04.rsw#묘르닐 산맥 북쪽 지역# +mjolnir_05.rsw#묘르닐 산맥 북쪽 지역# +mjolnir_06.rsw#묘르닐 산맥 남쪽 지역# +mjolnir_07.rsw#묘르닐 산맥 남쪽 지역# +mjolnir_08.rsw#묘르닐 산맥 남쪽 지역# +mjolnir_09.rsw#묘르닐 산맥 남쪽 산기슭# +mjolnir_10.rsw#묘르닐 산맥 남쪽 지역# +mjolnir_11.rsw#묘르닐 산맥 남쪽 지역# +mjolnir_12.rsw#묘르닐 산맥 북쪽 산기슭# +prt_fild00.rsw#프론테라 필드# +prt_fild01.rsw#프론테라 필드# +prt_fild02.rsw#프론테라 필드# +prt_fild03.rsw#프론테라 필드# +prt_fild04.rsw#프론테라 필드# +prt_fild05.rsw#프론테라 필드# +prt_fild06.rsw#프론테라 필드# +prt_fild07.rsw#프론테라 필드# +prt_fild08.rsw#프론테라 필드# +prt_fild09.rsw#프론테라 필드# +prt_fild10.rsw#프론테라 필드# +prt_fild11.rsw#프론테라 필드# +prt_monk.rsw#성 카피톨리나 수도원# +gef_fild00.rsw#게펜 필드# +gef_fild01.rsw#게펜 필드# +gef_fild02.rsw#게펜 필드# +gef_fild03.rsw#게펜 필드# +gef_fild04.rsw#게펜 필드# +gef_fild05.rsw#집시 마을# +gef_fild06.rsw#게펜 필드# +gef_fild07.rsw#게펜 필드# +gef_fild08.rsw#게펜 필드# +gef_fild09.rsw#게펜 필드# +gef_fild10.rsw#오크 마을# +in_orcs01.rsw#오크 마을 실내# +gef_fild11.rsw#게펜 필드# +moc_fild01.rsw#소그라트 사막# +moc_fild02.rsw#소그라트 사막# +moc_fild03.rsw#소그라트 사막# +//moc_fild04.rsw#소그라트 사막# 2008 모로크 +//moc_fild05.rsw#소그라트 사막# 2008 모로크 +//moc_fild06.rsw#소그라트 사막# 2008 모로크 +moc_fild07.rsw#소그라트 사막# +//moc_fild08.rsw#소그라트 사막# 2008 모로크 +//moc_fild09.rsw#소그라트 사막# 2008 모로크 +//moc_fild10.rsw#소그라트 사막# 2008 모로크 +moc_fild11.rsw#소그라트 사막# +moc_fild12.rsw#소그라트 사막# +moc_fild13.rsw#소그라트 사막# +//moc_fild14.rsw#소그라트 사막# 2008 모로크 +//moc_fild15.rsw#소그라트 사막# 2008 모로크 +moc_fild16.rsw#소그라트 사막# +in_moc_16.rsw#어새신 길드# +moc_fild17.rsw#소그라트 사막# +moc_fild18.rsw#소그라트 사막# +moc_fild19.rsw#소그라트 사막# +pay_fild01.rsw#페이욘 숲 속# +pay_fild02.rsw#페이욘 숲 속# +pay_fild03.rsw#페이욘 숲 속# +pay_fild04.rsw#소그라트 사막# +//pay_fild05.rsw#페이욘 숲 속# 2008년 리뉴얼 +pay_fild06.rsw#페이욘 숲 속# +pay_fild07.rsw#페이욘 숲 속# +pay_fild08.rsw#페이욘 숲 속# +pay_fild09.rsw#페이욘 숲 속# +pay_fild10.rsw#페이욘 숲 속# +//pay_fild11.rsw#페이욘 숲 속# 2008년 리뉴얼 +new_1-1.rsw#초보자 수련장# +new_2-1.rsw#초보자 수련장# +new_3-1.rsw#초보자 수련장# +new_4-1.rsw#초보자 수련장# +new_5-1.rsw#초보자 수련장# +new_1-2.rsw#초보자 수련장# +new_2-2.rsw#초보자 수련장# +new_3-2.rsw#초보자 수련장# +new_4-2.rsw#초보자 수련장# +new_5-2.rsw#초보자 수련장# +new_1-3.rsw#초보자 수련장# +new_2-3.rsw#초보자 수련장# +new_3-3.rsw#초보자 수련장# +new_4-3.rsw#초보자 수련장# +new_5-3.rsw#초보자 수련장# +new_1-4.rsw#초보자 수련장# +new_2-4.rsw#초보자 수련장# +new_3-4.rsw#초보자 수련장# +new_4-4.rsw#초보자 수련장# +new_5-4.rsw#초보자 수련장# +anthell01.rsw#모로크 개미지옥 던전 1층# +anthell02.rsw#모로크 개미지옥 던전 2층# +gef_dun00.rsw#게펜 지하 던전 1층# +gef_dun01.rsw#게펜 지하 던전 2층# +gef_dun02.rsw#게펜 지하 던전 3층# +gef_dun03.rsw#게페니아 던전# +iz_dun00.rsw#이즈루드 해저동굴 던전 1층# +iz_dun01.rsw#이즈루드 해저동굴 던전 2층# +iz_dun02.rsw#이즈루드 해저동굴 던전 3층# +iz_dun03.rsw#이즈루드 해저동굴 던전 4층# +iz_dun04.rsw#이즈루드 해저동굴 던전 5층# +//iz_dun05.rsw#이즈루드 해저동굴 던전 6층# +in_sphinx1.rsw#모로크 스핑크스 지하 1층# +in_sphinx2.rsw#모로크 스핑크스 지하 2층# +in_sphinx3.rsw#모로크 스핑크스 지하 3층# +in_sphinx4.rsw#모로크 스핑크스 지하 4층# +in_sphinx5.rsw#모로크 스핑크스 지하 5층# +moc_pryd01.rsw#모로크 피라미드 1층# +moc_pryd02.rsw#모로크 피라미드 2층# +moc_pryd03.rsw#모로크 피라미드 3층# +moc_pryd04.rsw#모로크 피라미드 4층# +moc_pryd05.rsw#모로크 피라미드 지하 1층# +moc_pryd06.rsw#모로크 피라미드 지하 2층# +moc_prydb1.rsw#도둑 길드# +mjo_dun01.rsw#묘르닐 폐광 1층# +mjo_dun02.rsw#묘르닐 폐광 2층# +mjo_dun03.rsw#묘르닐 폐광 3층# +orcsdun01.rsw#게펜 오크 지하 동굴 1층# +orcsdun02.rsw#게펜 오크 지하 동굴 2층# +pay_dun00.rsw#페이욘 동굴 1층# +pay_dun01.rsw#페이욘 동굴 2층# +pay_dun02.rsw#페이욘 동굴 3층# +pay_dun03.rsw#페이욘 동굴 4층(폐가촌)# +pay_dun04.rsw#페이욘 동굴 5층(폐가촌)# +prt_maze01.rsw#프론테라 미궁 숲 1층# +prt_maze02.rsw#프론테라 미궁 숲 2층# +prt_maze03.rsw#프론테라 미궁 숲 3층# +prt_sewb1.rsw#프론테라 지하 수로 1층# +prt_sewb2.rsw#프론테라 지하 수로 2층# +prt_sewb3.rsw#프론테라 지하 수로 3층# +prt_sewb4.rsw#프론테라 지하 수로 4층# +treasure01.rsw#알베르타 침몰선 1층# +treasure02.rsw#알베르타 침몰선 2층# +hunter_1-1.rsw#헌터 전직소# +hunter_2-1.rsw#헌터 전직소# +hunter_3-1.rsw#헌터 전직소# +in_hunter.rsw#헌터 전직소# +knight_1-1.rsw#나이트 전직소# +knight_2-1.rsw#나이트 전직소# +knight_3-1.rsw#나이트 전직소# +priest_1-1.rsw#프리스트 전직소# +priest_2-1.rsw#프리스트 전직소# +priest_3-1.rsw#프리스트 전직소# +//sword_1-1.rsw#검사 전직소# +//sword_2-1.rsw#검사 전직소# +sword_3-1.rsw#검사 전직소# +job_thief1.rsw#도둑 전직소# +wizard_1-1.rsw#위자드 전직소# +wizard_2-1.rsw#위자드 전직소# +wizard_3-1.rsw#위자드 전직소# +force_1-1.rsw#타임어택모드# +force_2-1.rsw#타임어택모드# +force_3-1.rsw#타임어택모드# +force_1-2.rsw#타임어택모드# +force_2-2.rsw#타임어택모드# +force_3-2.rsw#타임어택모드# +force_1-3.rsw#타임어택모드# +force_2-3.rsw#타임어택모드# +force_3-3.rsw#타임어택모드# +ordeal_1-1.rsw#배틀오딜모드# +ordeal_2-1.rsw#배틀오딜모드# +ordeal_3-1.rsw#배틀오딜모드# +ordeal_1-2.rsw#배틀오딜모드# +ordeal_2-2.rsw#배틀오딜모드# +ordeal_3-2.rsw#배틀오딜모드# +ordeal_1-3.rsw#배틀오딜모드# +ordeal_2-3.rsw#배틀오딜모드# +ordeal_3-3.rsw#배틀오딜모드# +ordeal_1-4.rsw#배틀오딜모드# +ordeal_2-4.rsw#배틀오딜모드# +ordeal_3-4.rsw#배틀오딜모드# +alb_ship.rsw#알베르타 선박 내부# +alberta.rsw#항구도시 알베르타# +alberta_in.rsw#알베르타 내부# +alb2trea.rsw#침몰선 부근의 섬# +aldebaran.rsw#국경도시 알데바란# +aldeba_in.rsw#알데바란 내부# +gef_tower.rsw#게펜 중앙탑# +geffen.rsw#마법도시 게펜# +geffen_in.rsw#게펜 내부# +//moc_castle.rsw#모로크 성내# 2008 모로크 +moc_ruins.rsw#사막도시 모로크# +morocc.rsw#사막도시 모로크# +morocc_in.rsw#모로크 내부# +pay_arche.rsw#페이욘 궁수마을# +payon.rsw#산악도시 페이욘# +payon_in01.rsw#페이욘 내부# +payon_in02.rsw#페이욘 내부# +payon_in03.rsw#페이욘 내부# +prontera.rsw#룬미드가츠 왕국 수도 프론테라# +prt_in.rsw#프론테라 내부# +prt_castle.rsw#프론테라 성 내부# +prt_church.rsw#프론테라 성당 내부# +izlude.rsw#위성도시 이즈루드# +izlude_in.rsw#이즈루드 내부# +izlu2dun.rsw#바이아란 섬# +monk_in.rsw#성 카피톨리나 수도원 내부# +prt_are_in.rsw#아레나 대기실# +arena_room.rsw#아레나 대기실# +prt_arena01.rsw#아레나# +prt_are01.rsw#아레나# +glast_01.rsw#글레스트헤임# +alde_dun01.rsw#알데바란 시계탑 지하 1층# +alde_dun02.rsw#알데바란 시계탑 지하 2층# +alde_dun03.rsw#알데바란 시계탑 지하 3층# +alde_dun04.rsw#알데바란 시계탑 지하 4층# +c_tower1.rsw#알데바란 시계탑 지상 1층# +c_tower2.rsw#알데바란 시계탑 지상 2층# +c_tower3.rsw#알데바란 시계탑 지상 3층# +c_tower4.rsw#알데바란 시계탑 지상 4층# +gl_cas01.rsw#글레스트헤임 성 1층# +gl_cas02.rsw#글레스트헤임 성 2층# +gl_church.rsw#글레스트헤임 수도원# +gl_chyard.rsw#글레스트헤임 지하 묘지# +gl_dun01.rsw#글레스트헤임 지하 동굴 1층# +gl_dun02.rsw#글레스트헤임 지하 동굴 2층# +gl_in01.rsw#글레스트헤임 실내# +gl_knt01.rsw#글레스트헤임 기사단 1층# +gl_knt02.rsw#글레스트헤임 기사단 2층# +gl_prison.rsw#글레스트헤임 지하 감옥 1층# +gl_prison1.rsw#글레스트헤임 지하 감옥 2층# +gl_sew01.rsw#글레스트헤임 지하 수로 1층# +gl_sew02.rsw#글레스트헤임 지하 수로 2층# +gl_sew03.rsw#글레스트헤임 지하 수로 3층# +gl_sew04.rsw#글레스트헤임 지하 수로 4층# +gl_step.rsw#글레스트헤임 계단 던전# +pvp_y_room.rsw#PvP : 대기실# +pvp_n_room.rsw#PvP : 대기실# +pvp_c_room.rsw#PvP : 대기실# +pvp_n_1-1.rsw#PvP : 샌드위치 룸# +pvp_n_2-1.rsw#PvP : 샌드위치 룸# +pvp_n_3-1.rsw#PvP : 샌드위치 룸# +pvp_n_4-1.rsw#PvP : 샌드위치 룸# +pvp_n_5-1.rsw#PvP : 샌드위치 룸# +pvp_n_6-1.rsw#PvP : 샌드위치 룸# +pvp_n_7-1.rsw#PvP : 샌드위치 룸# +pvp_n_8-1.rsw#PvP : 샌드위치 룸# +pvp_n_1-2.rsw#PvP : 락 온 룸# +pvp_n_2-2.rsw#PvP : 락 온 룸# +pvp_n_3-2.rsw#PvP : 락 온 룸# +pvp_n_4-2.rsw#PvP : 락 온 룸# +pvp_n_5-2.rsw#PvP : 락 온 룸# +pvp_n_6-2.rsw#PvP : 락 온 룸# +pvp_n_7-2.rsw#PvP : 락 온 룸# +pvp_n_8-2.rsw#PvP : 락 온 룸# +pvp_n_1-3.rsw#PvP : 포 룸# +pvp_n_2-3.rsw#PvP : 포 룸# +pvp_n_3-3.rsw#PvP : 포 룸# +pvp_n_4-3.rsw#PvP : 포 룸# +pvp_n_5-3.rsw#PvP : 포 룸# +pvp_n_6-3.rsw#PvP : 포 룸# +pvp_n_7-3.rsw#PvP : 포 룸# +pvp_n_8-3.rsw#PvP : 포 룸# +pvp_n_1-4.rsw#PvP : 언더 크로스 룸# +pvp_n_2-4.rsw#PvP : 언더 크로스 룸# +pvp_n_3-4.rsw#PvP : 언더 크로스 룸# +pvp_n_4-4.rsw#PvP : 언더 크로스 룸# +pvp_n_5-4.rsw#PvP : 언더 크로스 룸# +pvp_n_6-4.rsw#PvP : 언더 크로스 룸# +pvp_n_7-4.rsw#PvP : 언더 크로스 룸# +pvp_n_8-4.rsw#PvP : 언더 크로스 룸# +pvp_n_1-5.rsw#PvP : 콤파스 룸# +pvp_n_2-5.rsw#PvP : 콤파스 룸# +pvp_n_3-5.rsw#PvP : 콤파스 룸# +pvp_n_4-5.rsw#PvP : 콤파스 룸# +pvp_n_5-5.rsw#PvP : 콤파스 룸# +pvp_n_6-5.rsw#PvP : 콤파스 룸# +pvp_n_7-5.rsw#PvP : 콤파스 룸# +pvp_n_8-5.rsw#PvP : 콤파스 룸# +pvp_y_1-1.rsw#PvP : 프론테라# +pvp_y_2-1.rsw#PvP : 프론테라# +pvp_y_3-1.rsw#PvP : 프론테라# +pvp_y_4-1.rsw#PvP : 프론테라# +pvp_y_5-1.rsw#PvP : 프론테라# +pvp_y_6-1.rsw#PvP : 프론테라# +pvp_y_7-1.rsw#PvP : 프론테라# +pvp_y_8-1.rsw#PvP : 프론테라# +pvp_y_1-2.rsw#PvP : 이즈루드# +pvp_y_2-2.rsw#PvP : 이즈루드# +pvp_y_3-2.rsw#PvP : 이즈루드# +pvp_y_4-2.rsw#PvP : 이즈루드# +pvp_y_5-2.rsw#PvP : 이즈루드# +pvp_y_6-2.rsw#PvP : 이즈루드# +pvp_y_7-2.rsw#PvP : 이즈루드# +pvp_y_8-2.rsw#PvP : 이즈루드# +pvp_y_1-3.rsw#PvP : 페이욘# +pvp_y_2-3.rsw#PvP : 페이욘# +pvp_y_3-3.rsw#PvP : 페이욘# +pvp_y_4-3.rsw#PvP : 페이욘# +pvp_y_5-3.rsw#PvP : 페이욘# +pvp_y_6-3.rsw#PvP : 페이욘# +pvp_y_7-3.rsw#PvP : 페이욘# +pvp_y_8-3.rsw#PvP : 페이욘# +pvp_y_1-4.rsw#PvP : 알베르타# +pvp_y_2-4.rsw#PvP : 알베르타# +pvp_y_3-4.rsw#PvP : 알베르타# +pvp_y_4-4.rsw#PvP : 알베르타# +pvp_y_5-4.rsw#PvP : 알베르타# +pvp_y_6-4.rsw#PvP : 알베르타# +pvp_y_7-4.rsw#PvP : 알베르타# +pvp_y_8-4.rsw#PvP : 알베르타# +pvp_y_1-5.rsw#PvP : 모로크# +pvp_y_2-5.rsw#PvP : 모로크# +pvp_y_3-5.rsw#PvP : 모로크# +pvp_y_4-5.rsw#PvP : 모로크# +pvp_y_5-5.rsw#PvP : 모로크# +pvp_y_6-5.rsw#PvP : 모로크# +pvp_y_7-5.rsw#PvP : 모로크# +pvp_y_8-5.rsw#PvP : 모로크# +pvp_2vs2.rsw#PvP : 이벤트 경기장# diff --git a/openkore_llm_knowledge/tables/kRO/recvpackets.txt b/openkore_llm_knowledge/tables/kRO/recvpackets.txt new file mode 100644 index 0000000000..6d9e63462b --- /dev/null +++ b/openkore_llm_knowledge/tables/kRO/recvpackets.txt @@ -0,0 +1,1056 @@ +0064 55 +0065 17 +0066 3 +0067 37 +0068 46 +0069 0 +006A 23 +006B 0 +006C 3 +006D 114 +006E 3 +006F 2 +0070 3 +0071 28 +0072 22 +0073 11 +0074 3 +0075 0 +0076 9 +0077 5 +0078 55 +0079 53 +007A 58 +007B 60 +007C 44 +007D 2 +007E 105 +007F 6 +0080 7 +0081 3 +0082 2 +0083 2 +0084 2 +0085 10 +0086 16 +0087 12 +0088 10 +0089 11 +008A 29 +008B 23 +008C 14 +008D 0 +008E 0 +0090 7 +0091 22 +0092 28 +0093 2 +0094 19 +0095 30 +0096 0 +0097 0 +0098 3 +0099 0 +009A 0 +009B 34 +009C 9 +009D 17 +009E 17 +009F 20 +00A0 23 +00A1 6 +00A2 14 +00A3 0 +00A4 0 +00A5 0 +00A6 0 +00A7 9 +00A8 7 +00A9 6 +00AA 7 +00AB 4 +00AC 7 +00AE 0 +00AF 6 +00B0 8 +00B1 8 +00B2 3 +00B3 3 +00B4 0 +00B5 6 +00B6 6 +00B7 0 +00B8 7 +00B9 6 +00BA 2 +00BB 5 +00BC 6 +00BD 44 +00BE 5 +00BF 3 +00C0 7 +00C1 2 +00C2 6 +00C3 8 +00C4 6 +00C5 7 +00C6 0 +00C7 0 +00C8 0 +00C9 0 +00CA 3 +00CB 3 +00CC 6 +00CD 3 +00CE 2 +00CF 27 +00D0 3 +00D1 4 +00D2 4 +00D3 2 +00D4 0 +00D5 0 +00D6 3 +00D7 0 +00D8 6 +00D9 14 +00DA 3 +00DB 0 +00DC 28 +00DD 29 +00DE 0 +00DF 0 +00E0 30 +00E1 30 +00E2 26 +00E3 2 +00E4 6 +00E5 26 +00E6 3 +00E7 3 +00E8 8 +00E9 19 +00EA 5 +00EB 2 +00EC 3 +00ED 2 +00EE 2 +00EF 2 +00F0 3 +00F1 2 +00F2 6 +00F3 0 +00F4 21 +00F5 11 +00F6 8 +00F7 17 +00F8 2 +00F9 26 +00FA 3 +00FB 0 +00FC 6 +00FD 27 +00FE 30 +00FF 10 +0100 2 +0101 6 +0102 6 +0103 30 +0104 79 +0105 31 +0106 10 +0107 10 +0108 0 +0109 0 +010A 4 +010B 6 +010C 6 +010D 2 +010E 11 +010F 0 +0110 10 +0111 39 +0112 4 +0113 25 +0114 31 +0115 35 +0116 17 +0117 18 +0118 2 +0119 13 +011A 15 +011B 20 +011C 68 +011D 2 +011E 3 +011F 16 +0120 6 +0121 14 +0122 0 +0123 0 +0124 21 +0125 8 +0126 8 +0127 8 +0128 8 +0129 8 +012A 2 +012B 2 +012C 3 +012D 4 +012E 2 +012F 0 +0130 6 +0131 86 +0132 6 +0133 0 +0134 0 +0135 7 +0136 0 +0137 6 +0138 3 +0139 16 +013A 4 +013B 4 +013C 4 +013D 6 +013E 24 +013F 26 +0140 22 +0141 14 +0142 6 +0143 10 +0144 23 +0145 19 +0146 6 +0147 39 +0148 8 +0149 9 +014A 6 +014B 27 +014C 0 +014D 2 +014E 6 +014F 6 +0150 110 +0151 6 +0152 0 +0153 0 +0154 0 +0155 0 +0156 0 +0157 6 +0158 0 +0159 54 +015A 66 +015B 54 +015C 90 +015D 42 +015E 6 +015F 42 +0160 0 +0161 0 +0162 0 +0163 0 +0164 0 +0165 30 +0166 0 +0167 3 +0168 14 +0169 3 +016A 30 +016B 10 +016C 43 +016D 14 +016E 186 +016F 182 +0170 14 +0171 30 +0172 10 +0173 3 +0174 0 +0175 6 +0176 106 +0177 0 +0178 4 +0179 5 +017A 4 +017B 0 +017C 6 +017D 7 +017E 0 +017F 0 +0180 6 +0181 3 +0182 106 +0183 10 +0184 10 +0185 34 +0187 6 +0188 8 +0189 4 +018A 4 +018B 4 +018C 29 +018D 0 +018E 10 +018F 6 +0190 23 +0191 86 +0192 24 +0193 2 +0194 30 +0195 102 +0196 9 +0197 4 +0198 8 +0199 4 +019A 14 +019B 10 +019C 0 +019D 6 +019E 2 +019F 6 +01A0 3 +01A1 3 +01A2 37 +01A3 5 +01A4 11 +01A5 26 +01A6 0 +01A7 4 +01A8 4 +01A9 6 +01AA 10 +01AB 12 +01AC 6 +01AD 0 +01AE 4 +01AF 4 +01B0 11 +01B1 7 +01B2 0 +01B3 67 +01B4 12 +01B5 18 +01B6 114 +01B7 6 +01B8 3 +01B9 6 +01BA 26 +01BB 26 +01BC 26 +01BD 26 +01BE 2 +01BF 3 +01C0 2 +01C1 14 +01C2 10 +01C3 0 +01C4 22 +01C5 22 +01C6 4 +01C7 2 +01C8 13 +01C9 97 +01CA 3 +01CB 9 +01CC 9 +01CD 30 +01CE 6 +01CF 28 +01D0 8 +01D1 14 +01D2 10 +01D3 35 +01D4 6 +01D5 0 +01D6 4 +01D7 11 +01D8 54 +01D9 53 +01DA 60 +01DB 2 +01DC 0 +01DD 47 +01DE 33 +01DF 6 +01E0 30 +01E1 8 +01E2 34 +01E3 14 +01E4 2 +01E5 6 +01E6 26 +01E7 2 +01E8 28 +01E9 81 +01EA 6 +01EB 10 +01EC 26 +01ED 2 +01EE 0 +01EF 0 +01F0 0 +01F1 0 +01F2 20 +01F3 10 +01F4 32 +01F5 9 +01F6 34 +01F7 14 +01F8 2 +01F9 6 +01FA 48 +01FB 56 +01FC 0 +01FD 15 +01FE 5 +01FF 10 +0200 26 +0201 0 +0202 26 +0203 10 +0204 18 +0205 26 +0206 11 +0207 34 +0208 14 +0209 36 +020A 10 +020D 0 +020E 32 +020F 10 +0210 22 +0212 26 +0213 26 +0214 42 +0215 6 +0216 6 +0217 2 +0218 2 +0219 282 +021A 282 +021B 10 +021C 10 +021D 6 +021E 6 +021F 66 +0220 10 +0221 0 +0222 6 +0223 8 +0224 10 +0225 2 +0226 282 +0227 18 +0228 18 +0229 15 +022A 58 +022B 57 +022C 65 +022D 5 +022E 71 +022F 5 +0230 12 +0231 26 +0232 9 +0233 11 +0234 6 +0235 0 +0236 10 +0237 2 +0238 282 +0239 11 +023A 4 +023B 36 +023C 6 +023D 6 +023E 8 +023F 2 +0240 0 +0241 6 +0242 0 +0243 6 +0244 6 +0245 3 +0246 4 +0247 8 +0248 0 +0249 3 +024A 70 +024B 4 +024C 8 +024D 12 +024E 6 +024F 10 +0250 3 +0251 34 +0252 0 +0253 3 +0254 3 +0255 5 +0256 5 +0257 8 +0258 2 +0259 3 +025A 0 +025B 6 +025C 4 +025D 6 +025E 4 +025F 6 +0260 6 +0261 11 +0262 11 +0263 11 +0264 20 +0265 20 +0266 30 +0267 4 +0268 4 +0269 4 +026A 4 +026B 4 +026C 4 +026D 4 +026F 2 +0270 2 +0271 40 +0272 44 +0273 30 +0274 8 +0275 37 +0276 0 +0277 84 +0278 2 +0279 2 +027A 0 +027B 14 +027C 60 +027D 62 +027E 0 +027F 8 +0280 12 +0281 4 +0282 284 +0283 6 +0284 14 +0285 6 +0286 4 +0287 0 +0288 10 +0289 12 +028A 18 +028B 0 +028C 46 +028D 34 +028E 4 +028F 6 +0290 4 +0291 4 +0292 2 +0293 70 +0294 10 +0295 0 +0296 0 +0297 0 +0298 8 +0299 6 +029A 27 +029B 80 +029C 66 +029D 0 +029E 11 +029F 3 +02A2 8 +02A5 8 +02AA 4 +02AB 36 +02AC 6 +02AD 8 +02B0 85 +02B1 0 +02B2 0 +02B3 107 +02B4 6 +02B5 0 +02B6 7 +02B7 7 +02B8 22 +02B9 191 +02BA 11 +02BB 8 +02BC 6 +02C1 0 +02C2 0 +02C4 26 +02C5 30 +02C6 30 +02C7 7 +02C8 3 +02C9 3 +02CA 3 +02CB 65 +02CC 4 +02CD 71 +02CE 10 +02CF 6 +02D0 0 +02D1 0 +02D2 0 +02D3 4 +02D4 29 +02D5 2 +02D6 6 +02D7 0 +02D8 10 +02D9 10 +02DA 3 +02DB 0 +02DC 0 +02DD 32 +02DE 6 +02DF 36 +02E0 34 +02E1 33 +02E2 20 +02E3 22 +02E4 11 +02E5 9 +02E6 6 +02E7 0 +02E8 0 +02E9 0 +02EA 0 +02EB 13 +02EC 67 +02ED 59 +02EE 60 +02EF 8 +02F0 10 +02F1 2 +02F2 2 +02F3 0 +02F4 0 +02F5 0 +02F6 0 +02F7 0 +02F8 0 +02F9 0 +02FA 0 +02FB 0 +02FC 0 +02FD 0 +02FE 0 +02FF 0 +0300 0 +0301 0 +0302 0 +0303 0 +0304 0 +0305 0 +0306 0 +0307 0 +0308 0 +0309 0 +030A 0 +030B 0 +030C 0 +030D 0 +030E 0 +030F 0 +0310 0 +0311 0 +0312 0 +0313 0 +0314 0 +0315 0 +0316 0 +0317 0 +0318 0 +0319 0 +031A 0 +031B 0 +031C 0 +031D 0 +031E 0 +031F 0 +0320 0 +0321 0 +0322 0 +0323 0 +0324 0 +0325 0 +0326 0 +0327 0 +0328 0 +0329 0 +032A 0 +032B 0 +032C 0 +032D 0 +032E 0 +032F 0 +0330 0 +0331 0 +0332 0 +0333 0 +0334 0 +0335 0 +0336 0 +0337 0 +0338 0 +0339 0 +033A 0 +033B 0 +033C 0 +033D 0 +033E 0 +033F 0 +0340 0 +0341 0 +0342 0 +0343 0 +0344 0 +0345 0 +0346 0 +0347 0 +0348 0 +0349 0 +034A 0 +034B 0 +034C 0 +034D 0 +034E 0 +034F 0 +0350 0 +0351 0 +0352 0 +0353 0 +0354 0 +0355 0 +0356 0 +0357 0 +0358 0 +0359 0 +035A 0 +035B 0 +035C 2 +035D 0 +035E 2 +035F 0 +0360 0 +0361 0 +0362 0 +0363 0 +0364 0 +0365 0 +0366 0 +0367 0 +0368 0 +0369 0 +036A 0 +036B 0 +036C 0 +036D 0 +036E 0 +036F 0 +0370 0 +0371 0 +0372 0 +0373 0 +0374 0 +0375 0 +0376 0 +0377 0 +0378 0 +0379 0 +037A 0 +037B 0 +037C 0 +037D 0 +037E 0 +037F 0 +0380 0 +0381 0 +0382 0 +0383 0 +0384 0 +0385 0 +0386 0 +0387 0 +0388 0 +0389 0 +038A 0 +038B 0 +038C 0 +038D 0 +038E 0 +038F 0 +0390 0 +0391 0 +0392 0 +0393 0 +0394 0 +0395 0 +0396 0 +0397 0 +0398 0 +0399 0 +039A 0 +039B 0 +039C 0 +039D 0 +039E 0 +039F 0 +03A0 0 +03A1 0 +03A2 0 +03A3 0 +03A4 0 +03A5 0 +03A6 0 +03A7 0 +03A8 0 +03A9 0 +03AA 0 +03AB 0 +03AC 0 +03AD 0 +03AE 0 +03AF 0 +03B0 0 +03B1 0 +03B2 0 +03B3 0 +03B4 0 +03B5 0 +03B6 0 +03B7 0 +03B8 0 +03B9 0 +03BA 0 +03BB 0 +03BC 0 +03BD 0 +03BE 0 +03BF 0 +03C0 0 +03C1 0 +03C2 0 +03C3 0 +03C4 0 +03C5 0 +03C6 0 +03C7 0 +03C8 0 +03C9 0 +03CA 0 +03CB 0 +03CC 0 +03CD 0 +03CE 0 +03CF 0 +03D0 0 +03D1 0 +03D2 0 +03D3 0 +03D4 0 +03D5 0 +03D6 0 +03D7 0 +03D8 0 +03D9 0 +03DA 0 +03DB 0 +03DC 0 +03DD 18 +03DE 18 +03E2 0 +03E3 0 +03E4 0 +03E5 0 +03E6 0 +03E7 0 +03E8 0 +03E9 0 +03EA 0 +03EB 0 +03EC 0 +03ED 0 +03EE 0 +03EF 0 +03F0 0 +03F1 0 +03F2 0 +03F3 0 +03F4 0 +03F5 0 +03F6 0 +03F7 0 +03F8 0 +03F9 0 +03FA 0 +03FB 0 +03FC 0 +03FD 0 +03FE 0 +03FF 0 +0400 0 +0401 0 +0402 0 +0403 0 +0404 0 +0405 0 +0406 0 +0407 0 +0408 0 +0409 0 +040A 0 +040B 0 +040C 0 +040D 0 +040E 0 +040F 0 +0410 0 +0411 0 +0412 0 +0413 0 +0414 0 +0415 0 +0416 0 +0417 0 +0418 0 +0419 0 +041A 0 +041B 0 +041C 0 +041D 0 +041E 0 +041F 0 +0420 0 +0421 0 +0422 0 +0423 0 +0424 0 +0425 0 +0426 0 +0427 0 +0428 0 +0429 0 +042A 0 +042B 0 +042C 0 +042D 0 +042E 0 +042F 0 +0430 0 +0431 0 +0432 0 +0433 0 +0434 0 +0435 0 +0436 19 +0437 7 +0438 10 +0439 8 +043D 8 +043E 0 +043F 25 +0440 10 +0441 4 +0442 0 +0443 8 +0444 0 +0445 10 +0446 14 +0447 2 +0448 0 +044A 6 +044B 2 +07D0 6 +07D1 2 +07D2 0 +07D3 4 +07D4 4 +07D5 4 +07D6 4 +07D7 8 +07D8 8 +07D9 268 +07DA 6 +07DB 8 +07DC 6 +07DD 54 +07DE 30 +07DF 54 +07E0 58 +07E1 15 +07E2 8 +07E3 6 +07E4 0 +07E5 8 +07E6 8 +07E7 32 +07E8 0 +07E9 5 +07EA 2 +07EB 0 +07EC 8 +07ED 10 +07EE 6 +07EF 8 +07F0 6 +07F1 15 +07F2 6 +07F3 4 +07F4 3 +07F5 6 +07F6 14 +07F7 0 +07F8 0 +07F9 0 +07FA 8 +07FB 25 +07FC 10 +07FD 0 +07FE 26 +07FF 0 +0800 0 +0801 0 +0802 18 +0803 4 +0804 14 +0805 0 +0806 2 +0807 4 +0808 14 +0809 50 +080A 18 +080B 6 +080C 2 +080D 3 +080E 14 +080F 20 +0810 3 +0811 0 +0812 8 +0813 0 +0814 86 +0815 2 +0816 6 +0817 6 +0818 0 +0819 0 +081A 4 +081B 10 +081C 10 +081D 22 +081E 8 +081F 0 +0820 11 +0821 2 +0822 9 +0823 0 +0824 6 diff --git a/openkore_llm_knowledge/tables/kRO/skillnametable.txt b/openkore_llm_knowledge/tables/kRO/skillnametable.txt new file mode 100755 index 0000000000..42cb3ef735 --- /dev/null +++ b/openkore_llm_knowledge/tables/kRO/skillnametable.txt @@ -0,0 +1,999 @@ +NV_BASIC#기본기능# +SM_SWORD#한손검_수련# +SM_TWOHAND#양손검_수련# +SM_RECOVERY#HP회복력_향상# +SM_BASH#배쉬# +SM_PROVOKE#프로보크# +SM_MAGNUM#매그넘_브레이크# +SM_ENDURE#인듀어# +MG_SRECOVERY#SP회복력_향상# +MG_SIGHT#사이트# +MG_NAPALMBEAT#네이팜_비트# +MG_SAFETYWALL#세이프티_월# +MG_SOULSTRIKE#소울_스트라이크# +MG_COLDBOLT#콜드볼트# +MG_FROSTDIVER#프로스트_다이버# +MG_STONECURSE#스톤_커스# +MG_FIREBOLT#화이어_볼트# +MG_FIREBALL#화이어_볼# +MG_FIREWALL#화이어_월# +MG_LIGHTNINGBOLT#라이트닝_볼트# +MG_THUNDERSTORM#선더스톰# +AL_DP#디바인_프로텍션# +AL_DEMONBANE#데몬베인# +AL_RUWACH#루아흐# +AL_PNEUMA#흐뉴마# +AL_TELEPORT#텔레포테이션# +AL_WARP#워프_포탈# +AL_HEAL#힐# +AL_INCAGI#민첩성_증가# +AL_DECAGI#민첩성_감소# +AL_HOLYWATER#아쿠아_베네딕타# +AL_CRUCIS#시그넘_크루시스# +AL_ANGELUS#안젤루스# +AL_BLESSING#블레싱# +AL_CURE#큐어# +MC_INCCARRY#소지한계량_증가# +MC_DISCOUNT#디스카운트# +MC_OVERCHARGE#오버챠지# +MC_PUSHCART#푸쉬카트# +MC_IDENTIFY#아이템_감정# +MC_VENDING#노점개설# +MC_MAMMONITE#매머나이트# +AC_OWL#올빼미의_눈# +AC_VULTURE#독수리의_눈# +AC_CONCENTRATION#집중력_향상# +AC_DOUBLE#더블_스트레이핑# +AC_SHOWER#애로우_샤워# +TF_DOUBLE#더블_어택# +TF_MISS#회피율_증가# +TF_STEAL#스틸# +TF_HIDING#하이딩# +TF_POISON#인베넘# +TF_DETOXIFY#해독# +KN_SPEARMASTERY#창_수련# +KN_PIERCE#피어스# +KN_BRANDISHSPEAR#브랜디쉬_스피어# +KN_SPEARSTAB#스피어_스탭# +KN_SPEARBOOMERANG#스피어_부메랑# +KN_TWOHANDQUICKEN#투핸드_퀴큰# +KN_AUTOCOUNTER#오토_카운터# +KN_BOWLINGBASH#볼링_배쉬# +KN_RIDING#라이딩# +KN_CAVALIERMASTERY#기병_수련# +PR_MACEMASTERY#메이스_수련# +PR_IMPOSITIO#임포시티오_마누스# +PR_SUFFRAGIUM#수프라기움# +PR_ASPERSIO#아스페르시오# +PR_BENEDICTIO#성체강복# +PR_SANCTUARY#생츄어리# +PR_STRECOVERY#리커버리_# +PR_SLOWPOISON#슬로우_포이즌# +ALL_RESURRECTION#리저렉션# +PR_KYRIE#기리에_엘레이손# +PR_MAGNIFICAT#마니피캇# +PR_GLORIA#글로리아# +PR_LEXDIVINA#렉스_디비나# +PR_TURNUNDEAD#턴_언데드# +PR_LEXAETERNA#렉스_에테르나# +PR_MAGNUS#마그누스_엑소르시스무스# +HT_ANKLESNARE#앵클스네어# +HT_BEASTBANE#비스트베인# +HT_BLASTMINE#블래스트_마인# +HT_BLITZBEAT#블릿츠_비트# +HT_CLAYMORETRAP#크레모어_트랩# +HT_DETECTING#디텍팅# +HT_FALCON#팔콘리_마스터리# +HT_FLASHER#플래셔# +HT_FREEZINGTRAP#프리징_트랩# +HT_LANDMINE#랜드_마인# +HT_REMOVETRAP#리무브_트랩# +HT_TALKIEBOX#토키_박스# +HT_SANDMAN#샌드맨# +HT_SHOCKWAVE#쇼크웨이브_트랩# +HT_SKIDTRAP#스키드_트랩# +HT_SPRINGTRAP#스프링_트랩# +HT_STEELCROW#스틸_크로# +WZ_FIREPILLAR#화이어_필라# +WZ_SIGHTRASHER#사이트래셔# +WZ_FIREIVY#화이어_아이비# +WZ_METEOR#메테오_스톰# +WZ_JUPITEL#유피텔_선더# +WZ_VERMILION#로드_오브_버밀리온# +WZ_WATERBALL#워터볼# +WZ_ICEWALL#아이스월# +WZ_FROSTNOVA#프로스트_노바# +WZ_STORMGUST#스톰_가스트# +WZ_EARTHSPIKE#어스_스파이크# +WZ_HEAVENDRIVE#헤븐즈_드라이브# +WZ_QUAGMIRE#콰그마이어# +WZ_ESTIMATION#몬스터_정보# +AS_RIGHT#오른손_수련# +AS_LEFT#왼손_수련# +AS_KATAR#카타르_수련# +AS_CLOAKING#클로킹# +AS_SONICBLOW#소닉_블로우# +AS_GRIMTOOTH#그림투스# +AS_ENCHANTPOISON#인찬트_포이즌# +AS_POISONREACT#포이즌_리액트# +AS_VENOMDUST#베넘_더스트# +AS_SPLASHER#베넘_스플래셔# +BS_IRON#철_제조# +BS_STEEL#강철_제조# +BS_ENCHANTEDSTONE#속성석_제조# +BS_ORIDEOCON#오리데오콘_연구# +BS_DAGGER#단검_제작# +BS_SWORD#검_제작# +BS_TWOHANDSWORD#양손검_제작# +BS_AXE#도끼_제작# +BS_MACE#메이스_제작# +BS_KNUCKLE#너클_제작# +BS_SPEAR#창_제작# +BS_HILTBINDING#힐트_바인딩# +BS_FINDINGORE#광석발견# +BS_WEAPONRESEARCH#무기연구# +BS_REPAIRWEAPON#무기_수리# +BS_SKINTEMPER#스킨_템퍼링# +BS_HAMMERFALL#해머폴# +BS_ADRENALINE#아드레날린_러쉬# +BS_WEAPONPERFECT#웨폰_퍼펙션# +BS_OVERTHRUST#오버_트러스트# +BS_MAXIMIZE#맥시마이즈_파워# +NV_FIRSTAID#응급_치료# +SM_MOVINGRECOVERY#이동시_HP_회복# +AC_MAKINGARROW#화살_만들기# +TF_SPRINKLESAND#모래_뿌리기# +MC_CARTREVOLUTION#카트_레볼루션# +AL_HOLYLIGHT#홀리_라이트# +MG_ENERGYCOAT#에너지_코트# +NV_TRICKDEAD#죽은척하기# +SM_FATALBLOW#급소치기# +AC_CHARGEARROW#챠지_애로우# +TF_BACKSLIDING#백_슬라이딩# +MC_CHANGECART#체인지_카트# +SM_AUTOBERSERK#오토_버서크# +TF_PICKSTONE#돌_줍기# +TF_THROWSTONE#돌_던지기# +MC_LOUD#고성방가# +GD_APPROVAL#정식_길드_승인# +GD_KAFRACONTRACT#카프라와의_계약# +GD_GUARDRESEARCH#가디언_리서치# +GD_CHARISMA#카리스마# +GD_EXTENSION#조합체계_확장# +RG_SNATCHER#스내쳐# +RG_STEALCOIN#스틸_코인# +RG_BACKSTAP#백_스탭# +RG_TUNNELDRIVE#터널_드라이브# +RG_RAID#기습# +RG_STRIPWEAPON#스트립_웨폰# +RG_STRIPSHIELD#스트립_쉴드# +RG_STRIPARMOR#스트립_아머# +RG_STRIPHELM#스트립_헬름# +RG_INTIMIDATE#인티머데이트# +RG_GRAFFITI#그래피티# +RG_FLAGGRAFFITI#플래그_그래피티# +RG_CLEANER#클리너# +RG_GANGSTER#갱스터_파라다이스# +RG_COMPULSION#컴펄션_디스카운트# +RG_PLAGIARISM#도작# +AM_AXEMASTERY#도끼_수련# +AM_LEARNINGPOTION#러닝_포션# +AM_PHARMACY#파머시# +AM_DEMONSTRATION#데몬스트레이션# +AM_ACIDTERROR#애시드_테러# +AM_POTIONPITCHER#포션_피쳐# +AM_CANNIBALIZE#바이오_캐니벌라이즈# +AM_SPHEREMINE#스피어_마인# +AM_CP_WEAPON#케미칼_프로텍션(웨폰)# +AM_CP_SHIELD#케미칼_프로텍션(쉴드)# +AM_CP_ARMOR#케미칼_프로텍션(아머)# +AM_CP_HELM#케미칼_프로텍션(헬름)# +AM_BIOETHICS#생명윤리# +AM_BIOTECHNOLOGY#생명공학_연구# +AM_CREATECREATURE#피조물_창조# +AM_CULTIVATION#컬터베이션# +AM_FLAMECONTROL#프레임_컨트롤# +AM_CALLHOMUN#콜_호문클루스# +AM_REST#안식# +AM_DRILLMASTER#드릴마스터# +AM_HEALHOMUN#힐_호문클루스# +AM_RESURRECTHOMUN#리저랙션_호문클루스# +CR_TRUST#믿음# +CR_AUTOGUARD#오토_가드# +CR_SHIELDCHARGE#쉴드_챠지# +CR_SHIELDBOOMERANG#쉴드_부메랑# +CR_REFLECTSHIELD#리플렉트_쉴드# +CR_HOLYCROSS#홀리_크로스# +CR_GRANDCROSS#그랜드_크로스# +CR_DEVOTION#헌신# +CR_PROVIDENCE#신의_뜻# +CR_DEFENDER#디펜더# +CR_SPEARQUICKEN#스피어_퀴큰# +MO_IRONHAND#철사장(鐵沙掌)# +MO_SPIRITSRECOVERY#운기조식(運氣調息)# +MO_CALLSPIRITS#축기(畜氣)# +MO_ABSORBSPIRITS#흡기(吸氣)# +MO_TRIPLEATTACK#육합권(六合拳)# +MO_BODYRELOCATION#궁신탄영(弓身彈影)# +MO_DODGE#이화접목(移花接木)# +MO_INVESTIGATE#침투경(浸透勁)# +MO_FINGEROFFENSIVE#탄지신통(彈指神通)# +MO_STEELBODY#금강불괴(金剛不壞)# +MO_BLADESTOP#진검백파도(眞劍百破道)# +MO_EXPLOSIONSPIRITS#폭기(爆氣)# +MO_EXTREMITYFIST#아수라_패황권(阿修羅覇凰拳)# +MO_CHAINCOMBO#연환전신장(連環全身掌)# +MO_COMBOFINISH#맹룡과강(猛龍誇强)# +SA_ADVANCEDBOOK#어드밴스드_북# +SA_CASTCANCEL#캐스트_캔슬# +SA_FREECAST#프리_캐스트# +SA_SPELLBREAKER#스펠_브레이커# +SA_AUTOSPELL#오토_스펠# +SA_MAGICROD#매직_로드# +SA_VOLCANO#볼케이노# +SA_DELUGE#델류즈# +SA_VIOLENTGALE#바이어런스_게일# +SA_BARRENZONE#더미# +SA_LANDPROTECTOR#랜드_프로텍터# +SA_FLAMELAUNCHER#프레임_런쳐# +SA_FROSTWEAPON#프로스트_웨폰# +SA_LIGHTNINGLOADER#라이트닝_로더# +SA_SEISMICWEAPON#사이즈믹_웨폰# +SA_DRAGONOLOGY#드래고놀로지# +SA_DISPELL#디스펠# +SA_ABRACADABRA#아브라카다브라# +SA_MONOCELL#모노_셀# +SA_CLASSCHANGE#클래스_체인지# +SA_SUMMONMONSTER#서먼_몬스터# +SA_REVERSEORCISH#리버스_오키쉬# +SA_DEATH#사신# +SA_FORTUNE#포츈# +SA_TAMINGMONSTER#테이밍_몬스터# +SA_QUESTION#?# +SA_GRAVITY#그라비티# +SA_LEVELUP#레벨_업# +SA_INSTANTDEATH#즉사# +SA_FULLRECOVERY#완전회복# +SA_COMA#코마# +BD_ADAPTATION#임기응변# +BD_ENCORE#앵콜# +BD_LULLABY#자장가# +BD_RICHMANKIM#김서방_돈벌었네# +BD_ETERNALCHAOS#영원의_혼돈# +BD_DRUMBATTLEFIELD#전장의_북소리# +BD_RINGNIBELUNGEN#니벨룽겐의_반지# +BD_ROKISWEIL#울부짖는_로키# +BD_INTOABYSS#심연속으로# +BD_SIEGFRIED#불사신_지크프리드# +BD_RAGNAROK#라그나로크# +BA_MUSICALLESSON#악기다루기# +BA_MUSICALSTRIKE#뮤지컬_스트라이크# +BA_DISSONANCE#불협화음# +BA_FROSTJOKE#썰렁한_농담# +BA_WHISTLE#휘파람# +BA_ASSASSINCROSS#석양의_어세신_크로스# +BA_POEMBRAGI#브라기의_시# +BA_APPLEIDUN#이둔의_사과# +DC_DANCINGLESSON#춤연습# +DC_THROWARROW#화살_감아_던지기# +DC_UGLYDANCE#막춤# +DC_SCREAM#비명지르기# +DC_HUMMING#흥얼거림# +DC_DONTFORGETME#나를_잊지_말아요# +DC_FORTUNEKISS#행운의_키스# +DC_SERVICEFORYOU#당신을_위한_서비스# +WE_MALE#당신만은_지킬께요# +WE_FEMALE#당신을_위해서_희생할께요# +WE_CALLPARTNER#당신이_보고싶어요# +LK_AURABLADE#오라_블레이드# +LK_PARRYING#패링# +LK_CONCENTRATION#컨센트레이션# +LK_TENSIONRELAX#텐션_릴렉스# +LK_BERSERK#버서크# +PA_PRESSURE#프레셔# +PA_SACRIFICE#세크리파이스# +PA_GOSPEL#가스펠# +HP_ASSUMPTIO#아숨프티오# +HP_BASILICA#바실리카# +HP_MEDITATIO#메디타티오# +HW_SOULDRAIN#소울_드레인# +HW_MAGICCRASHER#매직_크래셔# +HW_MAGICPOWER#마법력_증폭# +CH_PALMSTRIKE#맹호경파산_(猛虎硬派山)# +CH_TIGERFIST#복호권_(伏虎拳)# +CH_CHAINCRUSH#연주붕격_(聯柱崩擊)# +PF_HPCONVERSION#라이프_치환# +PF_SOULCHANGE#소울_체인지# +PF_SOULBURN#소울_번# +ASC_KATAR#어드밴스드_카타르_연구# +ASC_BREAKER#기공포# +SN_SIGHT#트루_사이트# +SN_FALCONASSAULT#팔콘_어절트# +SN_SHARPSHOOTING#샤프슈팅# +SN_WINDWALK#윈드_워크# +WS_MELTDOWN#멜트다운# +WS_CREATECOIN#동전제조# +WS_CREATENUGGET#괴(塊)_제조# +WS_CARTBOOST#카트_부스트# +WS_SYSTEMCREATE#자동공격장치_제작# +ST_CHASEWALK#체이스_워크# +ST_REJECTSWORD#소드_리젝트# +ST_STEALBACKPACK#배낭_털기# +CR_ALCHEMY#알케미# +CR_SYNTHESISPOTION#포션_시놉시스# +CG_ARROWVULCAN#애로우_발칸# +CG_MOONLIT#달빛_물레방앗간에_떨어지는_꽃잎# +CG_MARIONETTE#마리오네트_컨트롤# +LK_SPIRALPIERCE#스파이럴_피어스# +LK_HEADCRUSH#헤드_크러쉬# +LK_JOINTBEAT#조인트_비트# +HW_NAPALMVULCAN#네이팜_발칸# +CH_SOULCOLLECT#광축기(狂蓄氣)# +PF_MINDBREAKER#마인드_브레이커# +PF_MEMORIZE#메모라이즈# +PF_FOGWALL#월_오브_포그# +PF_SPIDERWEB#스파이더_웹# +ASC_METEORASSAULT#메테오_어절트# +ASC_CDP#치명적인_독_생산# +ASC_EDP#치명적인_독_부여# +WE_BABY#엄마_아빠_사랑해요# +WE_CALLPARENT#엄마_아빠_보고싶어요# +WE_CALLBABY#아가야_이리오렴# +TK_RUN#달리기# +TK_READYSTORM#회오리_준비# +TK_STORMKICK#회오리차기# +TK_READYDOWN#찍기_준비# +TK_DOWNKICK#내려찍기# +TK_READYTURN#차기_준비# +TK_TURNKICK#돌려차기# +TK_READYCOUNTER#카운터_준비# +TK_COUNTER#카운터차기# +TK_DODGE#낙법# +TK_JUMPKICK#날라차기# +TK_HPTIME#편안한_휴식# +TK_SPTIME#즐거운_휴식# +TK_POWER#화이팅# +TK_SEVENWIND#따듯한_바람# +TK_MISSION#태권_미션# +TK_HIGHJUMP#높이뛰기# +SG_FEEL#태양과_달과_별의_느낌# +SG_SUN_WARM#태양의_따스함# +SG_MOON_WARM#달의_따스함# +SG_STAR_WARM#별의_따스함# +SG_SUN_COMFORT#태양의_안락함# +SG_MOON_COMFORT#달의_안락함# +SG_STAR_COMFORT#별의_안락함# +SG_HATE#태양과_달과_별의_증오# +SG_SUN_ANGER#태양의_분노# +SG_MOON_ANGER#달의_분노# +SG_STAR_ANGER#별의_분노# +SG_SUN_BLESS#태양의_축복# +SG_MOON_BLESS#달의_축복# +SG_STAR_BLESS#별의_축복# +SG_DEVIL#태양과_달과_별의_악마# +SG_FRIEND#태양과_달과_별의_친구# +SG_KNOWLEDGE#태양과_달과_별의_지식# +SG_FUSION#태양과_달과_별의_융합# +SL_ALCHEMIST#알케미스트의_영혼# +AM_BERSERKPITCHER#버서크_피쳐# +AM_TWILIGHT1#트와이라이트_파머시# +AM_TWILIGHT2#트와이라이트_파머시# +AM_TWILIGHT3#트와이라이트_파머시# +SL_MONK#몽크의_영혼# +SL_STAR#권성의_영혼# +SL_SAGE#세이지의_영혼# +SL_CRUSADER#크루세이더의_영혼# +SL_SUPERNOVICE#슈퍼노비스의_영혼# +SL_KNIGHT#기사의_영혼# +SL_WIZARD#위저드의_영혼# +SL_PRIEST#프리스트의_영혼# +SL_BARDDANCER#바드와_댄서의_영혼# +SL_ROGUE#로그의_영혼# +SL_ASSASIN#어쌔신의_영혼# +SL_BLACKSMITH#블랙스미스의_영혼# +BS_ADRENALINE2#풀_아드레날린_러쉬# +SL_HUNTER#헌터의_영혼_# +SL_SOULLINKER#소울링커의_영혼# +SL_HIGH#일차상위직의_영혼# +BS_ADRENALINE2#풀_아드레날린_러쉬# +SL_KAIZEL#카이젤# +SL_KAAHI#카아히# +SL_KAUPE#카우프# +SL_KAITE#카이트# +SL_KAINA#카이나# +SL_STIN#에스틴# +SL_STUN#에스턴# +SL_SMA#에스마# +SL_SWOO#에스우# +SL_SKE#에스크# +SL_SKA#에스카# +GD_GLORYGUILD#길드의_영광# +GD_LEADERSHIP#위대한_지도력# +GD_GLORYWOUNDS#영광의_상처# +GD_SOULCOLD#냉정한_마음# +GD_HAWKEYES#날카로운_시선# +GD_BATTLEORDER#전투명령_하달# +GD_REGENERATION#리제네레이션# +GD_RESTORE#리스토어# +GD_EMERGENCYCALL#긴급호출# +GD_GUARDUP#가디언_강화# +GD_DEVELOPMENT#영구적인_발전# +ST_PRESERVE#프리저브# +ST_FULLSTRIP#풀_스트립# +WS_WEAPONREFINE#무기제련# +CR_SLIMPITCHER#슬림_피쳐# +CR_FULLPROTECTION#풀_케미칼_프로텍션# +ITM_TOMAHAWK#토마호크_던지기# +PA_SHIELDCHAIN#쉴드체인# +HP_MANARECHARGE#마나_리차지# +PF_DOUBLECASTING#더블_캐스팅# +HW_GANBANTEIN#간반테인# +HW_GRAVITATION#그라비테이션_필드# +WS_CARTTERMINATION#카트_터미네이션# +WS_OVERTHRUSTMAX#오버트러스트_맥스# +CG_LONGINGFREEDOM#나를_구속하지마# +CG_HERMODE#헤르모드의_지팡이# +CG_TAROTCARD#운명의_타로_카드# +CR_ACIDDEMONSTRATION#애시드_데몬스트레이션# +CR_CULTIVATION#식물_재배# +KN_ONEHAND#원핸드_퀴큰# +HT_POWER#비스트_스트레이핑# +HLIF_HEAL#치유의_손길(힐)# +HLIF_AVOID#긴급회피# +HLIF_BRAIN#뇌수술# +HLIF_CHANGE#멘탈 체인지# +HAMI_CASTLE#캐슬링# +HAMI_DEFENCE#디펜스# +HAMI_SKIN#아다만티움_스킨# +HAMI_BLOODLUST#블러드_러스트# +HFLI_MOON#문라이트# +HFLI_FLEET#플릿_무브# +HFLI_SPEED#오버드_스피드# +HFLI_SBR44#S.B.R.44# +HVAN_CAPRICE#카프리스# +HVAN_CHAOTIC#카오틱_베네딕션# +HVAN_INSTRUCT#체인지_인스트럭션# +HVAN_EXPLOSION#바이오_익스플로젼# + +KN_CHARGEATK#차지_어택# +CR_SHRINK#슈링크# +AS_SONICACCEL#소닉_액셀레이션# +AS_VENOMKNIFE#베넘_나이프# +RG_CLOSECONFINE#클로즈_컨파인# +WZ_SIGHTBLASTER#사이트_블래스터# +SA_CREATECON#엘레멘탈_컨버터_제조# +SA_ELEMENTWATER#엘레멘탈_체인지_(수)# +SA_ELEMENTGROUND#엘레멘탈_체인지_(지)# +SA_ELEMENTFIRE#엘레멘탈_체인지_(화)# +SA_ELEMENTWIND#엘레멘탈_체인지_(풍)# +HT_PHANTASMIC#판타즈믹_애로우# +BA_PANGVOICE#팽_보이스# +DC_WINKCHARM#매혹의_윙크# +BS_UNFAIRLYTRICK#수상쩍은_상술# +BS_GREED#탐욕# +PR_REDEMPTIO#희생# +MO_KITRANSLATION#진기주입(振氣注入)# +MO_BALKYOUNG#발경(發勁)# + +NJ_TOBIDOUGU#비도_수련# +NJ_SYURIKEN#수리검_던지기# +NJ_KUNAI#쿠나이_던지기# +NJ_HUUMA#풍마수리검_던지기# +NJ_ZENYNAGE#돈_던지기# +NJ_TATAMIGAESHI#다다미_뒤집기# +NJ_KASUMIKIRI#안개_베기# +NJ_SHADOWJUMP#그림자_도약# +NJ_KIRIKAGE#그림자_베기# +NJ_UTSUSEMI#매미허물_벗기# +NJ_BUNSINJYUTSU#환영분신# +NJ_NINPOU#인법_수련# +NJ_KOUENKA#홍염화(紅炎華)# +NJ_KAENSIN#화염진(火炎陣)# +NJ_BAKUENRYU#폭염룡(爆炎龍)# +NJ_HYOUSENSOU#빙섬창(氷閃槍)# +NJ_SUITON#수둔(水遁)# +NJ_HYOUSYOURAKU#빙정락(氷晶落)# +NJ_HUUJIN#풍인(風刃)# +NJ_RAIGEKISAI#뇌격쇄(雷擊碎)# +NJ_KAMAITACHI#삭풍(朔風)# +NJ_NEN#염(念)# +NJ_ISSEN#일섬# +GS_GLITTERING#플립_더_코인# +GS_FLING#플링# +GS_TRIPLEACTION#트리플_액션# +GS_BULLSEYE#불스_아이# +GS_MADNESSCANCEL#매드니스_캔슬러# +GS_ADJUSTMENT#애드저스트먼트# +GS_INCREASING#인크리징_어큐러시# +GS_MAGICALBULLET#매지컬_블릿# +GS_CRACKER#크래커# +GS_SINGLEACTION#싱글_액션# +GS_SNAKEEYE#스네이크_아이# +GS_CHAINACTION#체인_액션# +GS_TRACKING#트래킹# +GS_DISARM#디스암# +GS_PIERCINGSHOT#피어싱_샷# +GS_RAPIDSHOWER#래피드_샤워# +GS_DESPERADO#데스페라도# +GS_GATLINGFEVER#캐틀링_피버# +GS_DUST#더스트# +GS_FULLBUSTER#풀_버스터# +GS_SPREADATTACK#스프레드_어택# +GS_GROUNDDRIFT#그라운드_드리프트# + +MS_BASH#배쉬# +MS_MAGNUM#매그넘_브레이크# +MS_BOWLINGBASH#볼링_배쉬# +MS_PARRYING#패링# +MS_REFLECTSHIELD#리플렉트_쉴드# +MS_BERSERK#버서크# +MA_DOUBLE#더블_스트레이핑# +MA_SHOWER#애로우_샤워# +MA_SKIDTRAP#스키드_트랩# +MA_LANDMINE#랜드마인# +MA_SANDMAN#샌드맨# +MA_FREEZINGTRAP#프리징_트랩# +MA_REMOVETRAP#리무브_트랩# +MA_CHARGEARROW#차지_애로우# +MA_SHARPSHOOTING#샤프_슈팅# +ML_PIERCE#피어스# +ML_BRANDISH#브랜디쉬_스피어# +ML_SPIRALPIERCE#스파이럴_피어스# +ML_DEFENDER#디펜더# +ML_AUTOGUARD#오토_가드# +ML_DEVOTION#헌신# +MER_MAGNIFICAT#마나의_축복# +MER_QUICKEN#웨폰_퀴큰# +MER_SIGHT#사이트# +MER_CRASH#크래쉬# +MER_REGAIN#리게인# +MER_TENDER#텐더# +MER_BENEDICTION#베네딕션# +MER_RECUPERATE#리큐퍼레이트# +MER_MENTALCURE#멘탈_큐어# +MER_COMPRESS#컴프레스# +MER_PROVOKE#프로보크# +MER_AUTOBERSERK#오토_버서크# +MER_DECAGI#민첩성_감소# +MER_SCAPEGOAT#희생양# +MER_LEXDIVINA#렉스_디비나# +MER_ESTIMATION#몬스터_정보# + +GD_ITEMEMERGENCYCALL#긴급호출# + +MB_FIGHTING#무낙_화이팅# +MB_NEUTRAL#본건_뉴트럴# +MB_TAIMING_PUTI#펫_프렌드# +MB_WHITEPOTION#하얀포션_심부름# +MB_MENTAL#정신적인_심부름# +MB_CARDPITCHER#카드_피쳐# +MB_PETPITCHER#펫_피쳐# +MB_BODYSTUDY#보디_스터딩# +MB_BODYALTER#보디_얼터링# +MB_PETMEMORY#펫_메모리# +MB_M_TELEPORT#무낙_텔레포트# +MB_B_GAIN#본건_게인# +MB_M_GAIN#무낙_게인# +MB_MISSION#테이밍_미션# +MB_MUNAKKNOWLEDGE#테이밍_마스터# +MB_MUNAKBALL#무낙_볼# +MB_SCROLL#본건_스크롤# +MB_B_GATHERING#본건_게더링# +MB_M_GATHERING#무낙 게더링# +MB_B_EXCLUDE#본건_익스클루드# +MB_B_DRIFT#본건_드리프트# +MB_B_WALLRUSH#본건_월러쉬# +MB_M_WALLRUSH#무낙_월러쉬# +MB_B_WALLSHIFT#본건_월쉬프트# +MB_M_WALLCRASH#무낙_월크래쉬# +MB_M_REINCARNATION#무낙_리엔카네이션# +MB_B_EQUIP#본건_얼마이티# + +SL_DEATHKNIGHT#데스나이트의_영혼# +SL_COLLECTOR#다크콜렉터의_영혼# +SL_NINJA#닌자의_영혼# +SL_GUNNER#건슬링거의_영혼# +AM_TWILIGHT4#트와이라이트_파머시# +DE_BERSERKAIZER#버서카이저# +DA_DARKPOWER#다크_소울파워# + +DE_PASSIVE#Death_패시브# +DE_PATTACK#Death_어택_패시브# +DE_PSPEED#Death_스피드_패시브# +DE_PDEFENSE#Death_디펜스_패시브# +DE_PCRITICAL#Death_크리티칼_패시브# +DE_PHP#Death_회복_패시브# +DE_PSP#Death_마력_패시브# +DE_RESET#Death_옵티마이즈# +DE_RANKING#Death_랭킹_패시브# +DE_PTRIPLE#Death_트리플_패시브# +DE_ENERGY#데스_에너지# +DE_NIGHTMARE#데스_나이트메어# +DE_SLASH#데스_슬래쉬# +DE_COIL#데스_코일# +DE_WAVE#데스_웨이브# +DE_REBIRTH#데스_리버스_에너지# +DE_AURA#데스_오오라# +DE_FREEZER#데스_프리져# +DE_CHANGEATTACK#데스_체인지_어택# +DE_PUNISH#데스_퍼니쉬# +DE_POISON#데스_포이즌_슬래쉬# +DE_INSTANT#데스_인스턴트_배리어# +DE_WARNING#데스_워닝# +DE_RANKEDKNIFE#데스_나이프# +DE_RANKEDGRADIUS#데스_그라디우스# +DE_GAUGE#마이티_게이지# +DE_GTIME#마이티_타임_차지# +DE_GPAIN#마이티_페인_차지# +DE_GSKILL#마이티_스킬_차지# +DE_GKILL#마이티_킬_차지# +DE_ACCEL#데드_액셀# +DE_BLOCKDOUBLE#데드_더블_블러킹# +DE_BLOCKMELEE#데드_니어_블러킹# +DE_BLOCKFAR#데드_디스턴스_블러킹# +DE_FRONTATTACK#데드_러쉬_어택# +DE_DANGERATTACK#데드져러스_어택# +DE_TWINATTACK#데드_트윈_어택# +DE_WINDATTACK#데드_스톰_어택# +DE_WATERATTACK#데드_워터_어택# + +DA_ENERGY#다크_에너지# +DA_CLOUD#다크_클라우드# +DA_FIRSTSLOT#다크_퍼스트판타지# +DA_HEADDEF#다크_헤드디펜스# +DA_SPACE#다크_트와이라이트# +DA_TRANSFORM#다크_트랜스폼# +DA_EXPLOSION#다크_익스플로젼# +DA_REWARD#다크_리워드# +DA_CRUSH#다크_크러쉬# +DA_ITEMREBUILD#다크_아이템_리빌드# +DA_ILLUSION#다크_일루션# +DA_NUETRALIZE#다크_뉴트럴라이즈# +DA_RUNNER#다크_러너# +DA_TRANSFER#다크_트랜스퍼# +DA_WALL#다크_월# +DA_REVENGE#다크_리벤지# +DA_EARPLUG#다크_이어플러그# +DA_CONTRACT#블랙_젬스톤_콘트렉트# +DA_BLACK#블랙_젬스톤_매직# +DA_DREAM#블랙_드림_오브_젬스톤# +DA_MAGICCART#콜렉터_매직카트# +DA_COPY#콜렉터_카피# +DA_CRYSTAL#콜렉터_크리스탈# +DA_EXP#콜렉터_경험치# +DA_CARTSWING#콜렉터_매지컬_카트_스윙# +DA_REBUILD#콜렉터_휴먼_리빌드# +DA_JOBCHANGE#콜렉터_노비스_잡체인지# +DA_EDARKNESS#콜렉터_엠펠리움_다크니스# +DA_EGUARDIAN#콜렉터_엠펠리움_가디언# +DA_TIMEOUT#콜렉터_타임아웃# +ALL_TIMEIN#타임인# +DA_ZENYRANK#콜렉터_랭킹# +DA_ACCESSORYMIX#콜렉터_믹스# + +ALL_INCCARRY#소지량_증가R# +GM_SANDMAN#잘자라_우리아가# + +ALL_CATCRY#괴수의_울부짖음# +ALL_PARTYFLEE#불어라_꽃바람# +ALL_ANGEL_PROTECT#당신에게_감사해요# +ALL_DREAM_SUMMERNIGHT#한_여름_밤의_꿈# + +NPC_EARTHQUAKE#어스퀘이크# +NPC_DRAGONFEAR#드래곤_피어# +NPC_PULSESTRIKE#펄스_스트라이크# +NPC_HELLJUDGEMENT#헬_저지먼트# +NPC_WIDESILENCE#메스_사일런스# + +NPC_WIDEFREEZE#메스_프리징# +NPC_WIDEBLEEDING#메스_블리딩# +NPC_WIDESTONE#메스_스톤커스# +NPC_WIDECONFUSE#메스_컨퓨젼# +NPC_WIDESLEEP#메스_슬립# + +NPC_WIDESTUN#메스_스턴# +NPC_WIDECURSE#메스_커즈# +NPC_SLOWCAST#슬로우_캐스트# +NPC_MAGICMIRROR#매직_미러# +NPC_STONESKIN#스톤_스킨# + +NPC_ANTIMAGIC#안티_매직# +NPC_CRITICALWOUND#치명적인_상처# +NPC_EVILLAND#이블_랜드# +NPC_VAMPIRE_GIFT#뱀파이어릭_터치# +NPC_WIDESOULDRAIN#마나_번# + +NPC_ALLHEAL#라이프스트림# +NPC_HELLPOWER#헬의_권능# + +MER_KYRIE#기리에_엘레이손# +MER_INCAGI#민첩성_증가# +MER_BLESSING#블레싱# + +///////////////// + +RK_ENCHANTBLADE#엔첸트_블레이드# +RK_SONICWAVE#소닉_웨이브# +RK_DEATHBOUND#데스_바운드# +RK_HUNDREDSPEAR#헌드레드_스피어# +RK_WINDCUTTER#윈드_커터# +RK_IGNITIONBREAK#이그니션_브레이크# +RK_DRAGONTRAINING#드래곤_트레이닝# +RK_DRAGONBREATH#드래곤_브레스# +RK_DRAGONHOWLING#드래곤_하울링# +RK_RUNEMASTERY#룬_마스터리# +RK_MILLENNIUMSHIELD#밀레니엄_실드# +RK_CRUSHSTRIKE#크러쉬_스트라이크# +RK_REFRESH#리프레쉬# +RK_GIANTGROWTH#자이언트_그로쓰# +RK_STONEHARDSKIN#스톤하드_스킨# +RK_VITALITYACTIVATION#바이탈리티_액티베이션# +RK_STORMBLAST#스톰_블래스트# +RK_FIGHTINGSPIRIT#파이팅_스피릿# +RK_ABUNDANCE#어번던스# +RK_PHANTOMTHRUST#팬텀_스러스트# + +AB_JUDEX#쥬덱스# +AB_ANCILLA#안실라# +AB_ADORAMUS#아도라무스# +AB_CLEMENTIA#크레멘티아# +AB_CANTO#칸토캔디두스# +AB_CHEAL#콜루세오힐# +AB_EPICLESIS#에피클레시스# +AB_PRAEFATIO#프라에파티오# +AB_ORATIO#오라티오# +AB_LAUDAAGNUS#라우다아그누스# +AB_LAUDARAMUS#라우다라무스# +AB_EUCHARISTICA#에우카리스티카# +AB_RENOVATIO#레노바티오# +AB_HIGHNESSHEAL#하이네스_힐# +AB_CLEARANCE#클리어런스# +AB_EXPIATIO#엑스피아티오# +AB_DUPLELIGHT#듀플레_라이트# +AB_DUPLELIGHT_MELEE#듀플레_라이트# +AB_DUPLELIGHT_MAGIC#듀플레_라이트# +AB_SILENTIUM#시렌치움# +AB_SECRAMENT#세크라멘트# + +GC_VENOMIMPRESS#베놈_임프레스# +GC_CROSSIMPACT#크로스_임팩트# +GC_DARKILLUSION#다크_일루젼# +GC_RESEARCHNEWPOISON#새로운_독_연구# +GC_CREATENEWPOISON#새로운_독_제조# +GC_ANTIDOTE#안티_도트# +GC_POISONINGWEAPON#포이즈닝_웨폰# +GC_WEAPONBLOCKING#웨폰_블로킹# +GC_COUNTERSLASH#카운터_슬래쉬# +GC_WEAPONCRUSH#웨폰_크러쉬# +GC_VENOMPRESSURE#베놈_프렛셔# +GC_POISONSMOKE#포이즌_스모크# +GC_CLOAKINGEXCEED#클로킹_익시드# +GC_PHANTOMMENACE#팬텀_메나스# +GC_HALLUCINATIONWALK#할루시네이션_워크# +GC_ROLLINGCUTTER#롤링_커터# +GC_CROSSRIPPERSLASHER#크로스_리퍼_슬래셔# + +RA_AIMEDBOLT#에임드_볼트# +RA_RESEARCHTRAP#트랩연구# +RA_RANGERMAIN#레인저메인# +RA_ELECTRICSHOCKER#일렉트릭_쇼커# +RA_WUGMASTERY#워그마스터리# +RA_ARROWSTORM#애로우_스톰# +RA_CLUSTERBOMB#클러스터_봄# +RA_DETONATOR#디토네이터# +RA_CAMOUFLAGE#카무플라쥬# +RA_TOOTHOFWUG#투스_오브_워그# +RA_WUGRIDER#워그라이더# +RA_FEARBREEZE#피어브리즈# +RA_MAGENTATRAP#마젠타_트랩# +RA_FIRINGTRAP#파이어링_트랩# +RA_ICEBOUNDTRAP#아이스바운드_트랩# +RA_SENSITIVEKEEN#예민한_후각# +RA_WUGSTRIKE#워그_스트라이크# +RA_WUGDASH#워그_대쉬# +RA_COBALTTRAP#코발트_트랩# +RA_WUGBITE#워그_바이트# +RA_MAIZETRAP#메이즈_트랩# +RA_VERDURETRAP#버듀어_트랩# + +WL_MARSHOFABYSS#마쉬_오브_어비스# +WL_RADIUS#라디어스# +WL_SUMMONFB#서몬_파이어볼# +WL_SUMMONWB#서몬_워터볼# +WL_SUMMONBL#서몬_볼_라이트닝# +WL_SUMMONSTONE#서몬_스톤# +WL_DRAINLIFE#드레인_라이프# +WL_RELEASE#릴리즈# +WL_CRIMSONROCK#크림즌_록# +WL_FROSTMISTY#프로스트_미스티# +WL_CHAINLIGHTNING#체인_라이트닝# +WL_SIENNAEXECRATE#시에나_엑서크레이트# +WL_SOULEXPANSION#소울_익스펜션# +WL_STASIS#스테이시스# +WL_HELLINFERNO#헬_인페르노# +WL_JACKFROST#잭_프로스트# +WL_EARTHSTRAIN#어스_스트레인# +WL_WHITEIMPRISON#화이트_임프리즌# +WL_COMET#커미트# +WL_RECOGNIZEDSPELL#리코그나이즈드_스펠# +WL_TETRAVORTEX#테트라_볼텍스# + +WL_FREEZE_SP#프리징_스펠# +WL_READING_SB#리딩_스펠북# + +ALL_REVERSEORCISH#리버스_오키쉬# +ALL_WEWISH#홀리_나이트# + +SC_FATALMENACE#페이탈_메나스# +SC_REPRODUCE#리프로듀스# +SC_AUTOSHADOWSPELL#오토_섀도우_스펠# +SC_SHADOWFORM#섀도우_폼# +SC_TRIANGLESHOT#트라이앵글_샷# +SC_STRIPACCESSARY#스트립_액세서리# +SC_BODYPAINT#바디_페인팅# +SC_INVISIBILITY#인비지빌리티# +SC_DEADLYINFECT#데들리_인펙트# +SC_ENERVATION#마스커레이드-이너베이션# +SC_GROOMY#마스커레이드-그루미# +SC_IGNORANCE#마스커레이드-이그노어런스# +SC_LAZINESS#마스커레이드-레이지네스# +SC_UNLUCKY#마스커레이드-언럭키# +SC_WEAKNESS#마스커레이드-위크니스# +SC_MANHOLE#맨홀# +SC_DIMENSIONDOOR#디멘션_도어# +SC_CHAOSPANIC#카오스_패닉# +SC_MAELSTROM#마엘슈트롬# +SC_BLOODYLUST#블러디_러스트# +SC_FEINTBOMB#페인트_봄# + +NC_MADOLICENCE#마도_기어_라이센스# +NC_BOOSTKNUCKLE#부스트_너클# +NC_PILEBUNKER#파일_벙커# +NC_VULCANARM#발칸_암# +NC_FLAMELAUNCHER#프레임_런쳐# +NC_COLDSLOWER#콜드_슬로어# +NC_ARMSCANNON#암즈_캐논# +NC_ACCELERATION#액셀레이션# +NC_HOVERING#호버링# +NC_F_SIDESLIDE#프론트_사이트_슬라이드# +NC_B_SIDESLIDE#백_사이트_슬라이드# +NC_MAINFRAME#메인_프레임_개조# +NC_SELFDESTRUCTION#셀프_디스트럭션# +NC_SHAPESHIFT#셰이프_쉬프트# +NC_EMERGENCYCOOL#이머전시_쿨# +NC_INFRAREDSCAN#인프라레드_스캔# +NC_ANALYZE#애널라이즈# +NC_MAGNETICFIELD#마그네틱_필드# +NC_NEUTRALBARRIER#뉴트럴_배리어# +NC_STEALTHFIELD#스텔스_필드# +NC_REPAIR#리페어# +NC_TRAININGAXE#도끼_수련# +NC_RESEARCHFE#불과_대지의_연구# +NC_AXEBOOMERANG#액스_부메랑# +NC_POWERSWING#파워_스윙# +NC_AXETORNADO#액스_토네이도# +NC_SILVERSNIPER#FAW_실버_스나이퍼# +NC_MAGICDECOY#FAW_매직_디코이# +NC_DISJOINT#FAW_해제# + +WA_SWING_DANCE#스윙_댄스# +WA_SYMPHONY_OF_LOVER#연인들을_위한_심포니# +WA_MOONLIT_SERENADE#달빛의_세레나데# +MI_RUSH_WINDMILL#풍차를_향해_돌격# +MI_ECHOSONG#메아리의_노래# +MI_HARMONIZE#하모나이즈# +WM_LESSON#레슨# +WM_METALICSOUND#메탈릭_사운드# +WM_REVERBERATION#진동_잔향# +WM_DOMINION_IMPULSE#도미니온_임펄스# +WM_SEVERE_RAINSTORM#서비어_레인스톰# +WM_POEMOFNETHERWORLD#나락의_노래# +WM_VOICEOFSIREN#세이렌의_목소리# +WM_DEADHILLHERE#사망의_골짜기에서# +WM_LULLABY_DEEPSLEEP#안식의_자장가# +WM_SIRCLEOFNATURE#순환하는_자연의_소리# +WM_RANDOMIZESPELL#불확정요소의_언어# +WM_GLOOMYDAY#수줍은_하루의_우울# +WM_GREAT_ECHO#그레이트_에코# +WM_SONG_OF_MANA#마나의_노래# +WM_DANCE_WITH_WUG#워그와_함께_춤을# +WM_SOUND_OF_DESTRUCTION#사운드_오브_디스트럭션# +WM_SATURDAY_NIGHT_FEVER#새터데이_나이트_피버# +WM_LERADS_DEW#레라드의_이슬# +WM_MELODYOFSINK#멜로디_오브_싱크# +WM_BEYOND_OF_WARCRY#비욘드_오브_워_크라이# +WM_UNLIMITED_HUMMING_VOICE#언리미티드_허밍_보이스# + +SR_DRAGONCOMBO#쌍룡각(雙龍脚)# +SR_SKYNETBLOW#천라지망(天羅地網)# +SR_EARTHSHAKER#지뢰진(地雷震)# +SR_RAMPAGEBLASTER#폭기산탄(爆氣散彈)# +SR_KNUCKLEARROW#수라신탄(修羅身彈)# +SR_FALLENEMPIRE#대전붕추(大纏崩墜)# +SR_TIGERCANNON#호포(號砲)# +SR_GATEOFHELL#나찰파황격(羅刹破凰擊)# +SR_CRESCENTELBOW#파쇄주(破碎柱)# +SR_WINDMILL#선풍퇴(旋風腿)# +SR_CURSEDCIRCLE#주박진(呪縛陣)# +SR_LIGHTNINGWALK#섬전보(閃電步)# +SR_RAISINGDRAGON#잠룡승천(潛龍昇天)# +SR_GENTLETOUCH_QUIET#점혈-묵(點穴-默)# +SR_GENTLETOUCH_CURE#점혈-쾌(點穴-快)# +SR_GENTLETOUCH_ENERGYGAIN#점혈-구(點穴-救)# +SR_GENTLETOUCH_CHANGE#점혈-반(點穴-反)# +SR_GENTLETOUCH_REVITALIZE#점혈-활(點穴-活)# +SR_ASSIMILATEPOWER#흡기공(吸氣攻)# +SR_POWERVELOCITY#전기주입(全氣注入)# +SR_HOWLINGOFLION#사자후(獅子吼)# +SR_RIDEINLIGHTNING#뇌광탄(雷光彈)# + +LG_MOONSLASHER#문_슬래셔# +LG_BANISHINGPOINT#배니싱_포인트# +LG_OVERBRAND#오버_브랜드# +LG_PINPOINTATTACK#핀_포인트_어택# +LG_CANNONSPEAR#캐논_스피어# +LG_EXEEDBREAK#익시드_브레이크# +LG_BANDING#밴딩# +LG_FORCEOFVANGUARD#포스_오브_뱅가드# +LG_RAGEBURST#레이지_버스트_어택# +LG_TRAMPLE#트램플# +LG_PRESTIGE#프레스티지# +LG_HESPERUSLIT#헤스페루스_리트# +LG_RAYOFGENESIS#레이_오브_제네시스# +LG_INSPIRATION#인스피레이션# +LG_PIETY#파이어티# +LG_REFLECTDAMAGE#리플렉트_데미지# +LG_EARTHDRIVE#어스_드라이브# +LG_SHIELDPRESS#쉴드_프레스# +LG_SHIELDSPELL#쉴드_스펠# + +ALL_ODINS_RECALL#오딘의_리콜# +RETURN_TO_ELDICASTES#엘_디카스테스로의_귀환# + +GN_TRAINING_SWORD#검_수련# +GN_REMODELING_CART#카트개조# +GN_CART_TORNADO#카트_토네이도_어택# +GN_CARTCANNON#카트_캐논# +GN_CARTBOOST#카트_부스트# +GN_THORNS_TRAP#가시나무_덫# +GN_BLOOD_SUCKER#블러드_서커# +GN_SPORE_EXPLOSION#스포어_익스플로젼# +GN_WALLOFTHORN#가시나무_벽# +GN_CRAZYWEED#크레이지_위드# +GN_DEMONIC_FIRE#데모닉_파이어# +GN_FIRE_EXPANSION#파이어_익스펜션# +GN_HELLS_PLANT#헬즈_플랜트# +GN_MANDRAGORA#하울링_오브_만드라고라# +GN_SLINGITEM#슬링_아이템# +GN_CHANGEMATERIAL#체인지_매터리얼# +GN_MIX_COOKING#믹스_쿠킹# +GN_MAKEBOMB#폭탄_제조# +GN_S_PHARMACY#스페셜_파머시# + +SO_FIREWALK#파이어_워크# +SO_ELECTRICWALK#일렉트릭_워크# +SO_SPELLFIST#스펠_피스트# +SO_VACUUM_EXTREME#바큠_익스트림# +SO_PSYCHIC_WAVE#사이킥_웨이브# +SO_CLOUD_KILL#클라우드_킬# +SO_POISON_BUSTER#포이즌_버스터# +SO_STRIKING#스트라이킹# +SO_EARTHGRAVE#어스_그레이브# +SO_DIAMONDDUST#다이아몬드_더스트# +SO_WARMER#워머# +SO_VARETYR_SPEAR#바레티르_스피어# +SO_ARRULLO#아를로# + + + + + + + + + + + + + + diff --git a/openkore_llm_knowledge/tables/msgstringtable.txt b/openkore_llm_knowledge/tables/msgstringtable.txt new file mode 100644 index 0000000000..649ea5a553 --- /dev/null +++ b/openkore_llm_knowledge/tables/msgstringtable.txt @@ -0,0 +1,3577 @@ +Do you agree?# +Failed to Connect to Server.# +Disconnected from Server.# +Disconnected from Server!# +Server Closed.# +Someone has Logged in with this ID.# +Unregistered ID. Please make sure you have a registered account and you have correctly typed in the user ID.# +Incorrect User ID or Password. Please try again.# +This ID is expired.# +Rejected from Server.# +Character Name already exists.# +Character Creation is denied.# +Character Deletion is denied.# +Please Enter Room Title.# +Foul Language Detected.# +Please enter Password.# +Please enter Password. Passwords must be at least 4 characters long.# +Are you sure that you want to quit?# +Passwords are at least 4 characters long. Please try again.# +Are you sure that you want to delete this character?# +Foul Language Detected.# +Character Name must be at least 4 characters long.# +Command List: /h | /help# +Effects On# +Effects Off# +Sound Volume# +BGM Volume# +Sound Effects On# +Sound Effects Off# +Frame Skip On# +Frame Skip Off# +BGM On# +BGM Off# +/h or /help: Shows this Command Help List# +/w or /who or /player or /who : wiew current the number of player# +/music: Turns BGM On or Off# +/sound: Turns Sound Effects On or Off# +/effect: Effects On or Off# +/where: Shows your present location# +/skip: Turns Frame Skip On or Off# +/v (0~127): Controls the volume of the Sound Effects# +/bv (0~127): Controls the volume of the BGM# +/ex (Character Name): Blocks whispering from the Character# +/ex: View a list of Characters you have Blocked# +/in (Character Name): Allows whispering from the Character# +/inall: Allows whispers from anyone# +/exall: Blocks whispers from everyone# +Right click on a character and select [Register as a Friend] to add a person to your Friend List.# +F12 Brings up a Hotkey Window which allows you to drag and drop Recovery Items, Equipment and Skills into it for faster access.# +You can't type the same word/phrase more than 3 times.# +Chat Filter: Yeah, uh, I don't think so buddy...# +You cannot overlap items on a window.# +You cannot carry more items because you are overweight.# +You cannot get the item.# +The deal has successfully completed.# +You do not have enough zeny.# +You are over your Weight Limit.# +The deal has failed.# +You've blocked whispers from everyone.# +You've failed to block all whispers.# +You've allowed whispers from everyone.# +You've failed to allow all whispers.# +You have no Block List.# +[ Character Block List ]# +Room has been successfully created.# +Room Limit Exceeded.# +Same Room exists.# +The Room is full.# +You have been kicked out of this room.# +The deal has been rejected.# +You are too far away from the person to trade.# +The Character is not currently online or does not exist.# +The person is in another deal.# +You cannot trade because this character will exceed his weight limit.# +The deal has been canceled.# +The deal has successfully completed.# +The deal has failed.# +Party has successfully been organized.# +That Party Name already exists.# +The Character is already in a party.# +The Character already joined another party.# +Request for party rejected.# +Request for party accepted.# +Party Capacity exceeded.# +You left the party.# +Send to All# +Send to Party# +Send transaction request# +Send party invitation# +Pri:# +Pub:# +Click ''Restart'' to go back to your save point or click ''Exit'' to select another character.# +Please select a Deal Type.# + requests a deal.# + Party has sent you an invitation. Would you like to join?# +Invalid Command# +Leave party# +Expel from party# +Send Message# +1:1 Chat# +Information# +Party Setup# +Friend# +Party# +Equipped items# +Status# +Inventory# +/organize ''Party Name'' To organize a party. Type /leave To leave a Party.# +If you are the party master, you can invite someone into your party by right-clicking on a Character.# +Recover# +Attack# +Support# +Entire# +Weapon# +Defense# +Water# +Earth# +Fire# +Wind# +Please avoid buying 2 of the same items at one time.# +Please change your desktop Color Depth to 16-bit when running Ragnarok in windowed mode.# +Please wait...# +Please wait...# +Please wait...# +Please wait...# +Create Chat Room# +Room Setup# +Kick Character Out# +Give Master Authority# +View Information# +Chat Room# +Ppl# +/sit: Sit command. If you are sitting, you will stand instead.# +/stand: Stand command. If you are standing, you will sit instead.# +/chat: Creates a Chat Room# +/q: Leaves a Chat Room# +/deal ''Character Name'' Requests a deal with a character# +/organize ''Party Name'' Organizes a party# +/leave: Leaves a party# +/expel ''Character Name'' kicks a Character out of your party# +[Alt] + [End]: Turns HP/SP Bar On or Off# +[Alt] + [Home]: Turns Ground Cursor On or Off# +[Insert]: Makes you sit or stand. (Hotkey to toggle between /sit and /stand)# +Congratulations! You are the MVP! Your reward item is # +!!# +Congratulations! You are the MVP! Your reward EXP Points are # +!!# +You are the MVP, but you can't take the reward because you are over your weight limit.# +There is no such character name or the user is offline.# + doesn't want to receive your messages.# + is not in the mood to talk with anyone.# +Killed/Disconnected User.# +Kill has failed.# +You got %s (%d).# +[Alt] + [=]: Fix the interval error between letters.# +[F10]: To toggle Chat Window size; [Alt] + [F10]: Toggle Chat Window On or Off# +How to Whisper: Enter a Character's Name on the left side of chat window and type your message on the right side. The Tab key helps you move between these boxes.# +/!,/?,/ho,/lv,/lv2,/swt,/ic,/an,/ag,/$,/??,/thx,/wah,/sry,/heh,/swt2,/hmm,/no1,/??,/omg,/oh,/X,/hp,/go,/sob,/gg,/kis,/kis2,/pif,/ok: Emotion icons corresponding to Alt + (1~9) Ctrl + (-=\\)# +How to Speak to Party: Add '%' in front of every message.(Example: \%Hello\)# +You haven't learned enough Basic Skills to Trade.# +You haven't learned enough Basic Skills to use Emotion icons.# +You haven't learned enough Basic Skills to Sit.# +You haven't learned enough Basic Skills to create a chat room.# +You haven't learned enough Basic Skills to Party.# +You haven't learned enough skills to Shout.# +You haven't learned enough skills for Pking.# +Buying Items# +Item Shop# +Selling Items# +Storage# + is put on.# + is taken off.# +To add names on the Whispering List# +How to Take Screen Shots: Press [Print Screen] or [Scroll Lock]# +Tip of the Day# +^3850a0Did you know...?^709fed# +Display at startup# +/tip: Opens ''Tip of the Day''# +There are %d Players Currently Connected.# +(%s) has entered.# +(%s) has left.# +(%s) was kicked out.# + %d ea.# +%s: %d ea.# +%s %s: %d ea.# +Available Items to sell# +Shop Items# +Unknown Area# +Your Client language doesn't match the Server language.# +Please move your equipment to the inventory. And close the equipment window.# +This server provides English Text Characters Only.# +This is not implemented yet.# +No Whisper List.# +: Whispering Blocked.# +: Whispering Block has failed.# +: Whispering Block has failed. Block List is full.# +: Whispering accepted.# +: Command has failed.# +: Command has failed. Block List is full.# +You cannot put a space at the beginning or end of a name.# +Private# +Public# +Insufficient SP# +Insufficient HP# +Skill has failed.# +Steal has failed.# +Trade# +Envenom skill has failed.# +You cannot use this ID on this server.# +Speed has increased.# +Speed has decreased.# +/memo: To memorize a place as Warp Point (If you are an Acolyte Class character)# +Random Area# +Select an Area to Warp# +Skill Level is not high enough# +There are no memorized locations (Memo Points).# +You haven't learned Warp skill.# +Current location is saved as a Memo Point for Warp Skill.# +Cancel# +There is a Delay after using a Skill.# +You can't have this item because you will exceed the weight limit.# +Out of the maximum capacity# +Cart Items# +Take off Cart# +Opening a stall# +Please Name your Shop.# +My Shop# +Merchant Shop# +Buying Items# +%s Purchase Failed %s# +Out of Stock# +%s %d sold.# +Available Items for Vending# +Skill has failed because you do not have enough zeny.# +Select a Target.# +/pk on: Turns PK On. /pk off: Turns PK Off.# +Shop# +Cart Items [Alt+W]# +Basic Information# +The skill cannot be used with this weapon.# +Buying %s has been failed. Out of Stock. Current Stock %d.# +You've been disconnected due to a time gap between you and the server.# +Please equip the proper ammunition first.# +You can't attack or use skills because you've exceeded the Weight Limit.# +You can't use skills because you've exceeded the Weight Limit.# +Ammunition has been equipped.# +Red Gemstone required.# +Blue Gemstone required.# +Strength# +Agility# +Vitality# +Intelligence# +Dexterity# +Luck# +Physical Attack# +Physical Defense# +Accuracy Rate# +Critical Attack Rate# +Assigned Guild# +Status Points to allocate# +Magical Attack# +Magical Defense# +Flee Rate# +Attack Speed# +Server is jammed due to over population. Please try again shortly.# +Option# +Account ID blocked by the Game Master Team.# +Incorrect User ID or Password. Please try again.# +Choose Hairstyle# +ATK# +DEF# +Attack Snap On# +Attack Snap Off# +Skill Snap On# +Skill Snap Off# +/snap: Turns snap On | Off for fights, /skillsnap: Turns snap On | Off for skills. /itemsnap: Turns snap On | Off for items on the grounds.# +Item Snap On# +Item Snap Off# +Snap# +You cannot carry more than 30,000 of one kind of item.# +You cannot delete a Character with a level greater than 30. If you want to delete the character please contact a Game Master.# +You cannot use an NPC shop while in a trade.# +Name# +Skill Tree# +Skill Point: %d# +Skill has failed.# +Passive# +Each Take # +Even Share# +Each Take# +Party Share# +Party Setup# +How to share EXP# +How to share Items# +Only the Party Leader can change this setting.# +Toggle Item Amount.# +Character will be deleted after ^ff0000%d^000000 seconds. Press Cancel to quit.# +You cannot trade more than 10 types of items per trade.# +You are underaged.# +Please enter the deletion password.# +E-mail Address (Case Sensitive).# +Character Deletion has failed because you have entered an incorrect e-mail address.# +Enter Second Serial Cord of your Social Security number.# +Character Deletion has failed because you have entered an incorrect SSN.# +You can't sell more than 15 types of Items at one time.# +You are underaged and cannot join this server.# +HP and SP natural regeneration are disabled because your carried weight is over 50% of the Weight Limit.# +You can't use Skills or Attack while your carried weight is over 90% of your Weight Limit.# +Your HP/SP are now being restored naturally.# +Attack and Skills are now available.# +Your Game's Exe File is not the latest version.# +Items are sold out.# +Save Chat as Text File# +/savechat: Save a Chat Log# +Register# +Reject Whispering# +Allow Whispering# +Shows ''Miss'' On# +Shows ''Miss'' Off# +Camera Zooming On# +Camera Zooming Off# +/camera: Camera Zooming On or Off. /miss: Toggle ''Miss'' display# +View Skill Info# +Change Skill# +Sprite Resolution# +Texture Resolution# +Arrange Detail# +%s Zeny obtained# +Guild Name# +Guild Level# +Guild Master# +Members# +Avg.lvl of Members# +Territory# +Tendency# +EXP# +Emblem# +Tax Point# +Alliances# +Antagonists# +Guild Info# +Member# +Position# +Guild Skill# +Expel List# +Announcement# +Entire Guild List# +Whispering List# +Open Whispering Window# +How to Open Whispering List: Press [Alt] + [H]# +Open Whispering List Automatically# +Delete# +Close since next# +Last Log-in Time# +Last Log-in IP# +Friend Setup# +Are you sure that you want to delete?# +Are you sure that you want to leave?# +Register as a Friend# +Open 1:1 Chat between Friends# +Open 1:1 Chat# +Open 1:1 Chat between Strangers# +Alarm when receive a 1:1 Chat# +Are you sure that you want to expel?# +%s has withdrawn from the guild.# +Secession Reason: %s# +You have failed to disband the guild.# +Disband Reason: %s# +This ID has been removed.# +Price: # +%s has been expelled from our guild.# +Expulsion Reason: %s# +You can't put this item on.# +You can't modify Party Setup.# +Guild has been Created.# +You are already in a Guild.# +That Guild Name already exists.# + Guild has sent you an invitation. Would you like to join this Guild?# +He/She is already in a Guild.# +Offer Rejected# +Offer Accepted# +Your Guild is Full.# +Send (%s) a Guild invitation# +You haven't learned enough skills for aligning.# +Aligning completed.# +You already spent your point for today.# +Hasn't been a month yet since you aligned this person.# +Received positive manner point from %s.# +Received negative manner point from %s.# +Align with a Good Point# +Align with a Bad Point# +Request a deal with (%s)# +Ask (%s) to join your party# + Guild is asking you to agree to an Alliance with them. Do you accept?# +This Guild is already your Ally.# +You reject the offer# +You accept the offer# +They have too many Alliances.# +You have too many Alliances.# +Set this guild as an Alliance# +Guild was successfully disbanded.# +You have failed to disband the guild due to your incorrect SSN.# +You have failed to disband the guild because there are guildsmen still present.# +Set this guild as an Antagonist# +Choose Hair Color# +You need the necessary item to create a Guild.# +Monster Info# +Name# +Level# +HP# +Size# +Type# +MDEF# +Element# +Neutral# +Water# +Earth# +Fire# +Wind# +Poison# +Holy# +Shadow# +Ghost# +Undead# +You can't create items yet.# +Item List you can craft# + Create# +'s materials# + item creation failed.# + item created successfully.# +Failed to create %s.# +Successfully created %s.# +You are not the required lvl.# +Too high lvl for this job.# +Not the suitable job for this type of work.# +Record a message in the Talkie Box# +Please type a message for the Talkie Box# +Send to Guild# +You didn't pay for this ID. Would you like to pay for it now?# +Server is jammed due to overpopulation. Please try again after few minutes.# +Server still recognizes your last log-in. Please try again after a few minutes.# +Release Falcon# +Dismount# +Small# +Medium# +Large# +Double# +Triple# +Quadruple# +You are prohibited to log in until %s.# +'s # +'s Fire # +'s Ice # +'s Wind # +'s Earth # +211.239.161.246# +6900# +http://www.ragnarok.co.kr# +Kill %s# +Very Strong # +Very Very Strong # +Very Very Very Strong # +The Reason of Expulsion# + Attack speed increased.# + Attack speed decreased.# + Weapon damage is improved.# + Weapon damage is reduced.# + Cast delay is reduced.# + Cast delay has returned to normal.# + Weapon is temporarily enchanted with Poison.# + Weapon is temporarily enchanted with Holy element.# + Weapon has changed back to normal.# + Armor is temporarily enchanted with Holy element.# + Armor has changed back to normal.# +Barrier formed.# +Barrier canceled.# + Weapon Perfection Initiated.# + Weapon perfection Canceled.# + Power-Thrust Initiated.# + Power-Thrust Canceled.# + Maximize-Power Initiated.# + Maximize-Power Canceled.# +[New Server]# +(%d players)# +(On the maintenance)# +Guild member %s has connected.# +Guild member %s has disconnected.# +You got %d Base exp.# +You got %d Job exp.# +You left the guild.# +You have been expelled from the Guild.# +Item appraisal has completed successfully.# +Item appraisal has failed.# +Compounding has completed successfully.# +Compounding has failed.# +Antagonist has been set.# +Guild has too many Antagonists.# +Already set as an Antagonist# +Upgrade success!!# +Upgrade failed!!# +Unavailable Area to Teleport# +Unable to memorize this place as Warp Point# +Please wait 10 seconds before trying to log out.# +Position# +Job# +Note# +Devotion# +Tax Point# +Leave Guild# +Expel# +Rank# +Position Title# +Invitation# +Punish# +Tax %# +Title# +Contents# +Guild Name# +Guild Lvl# +Guildsmen# +Ranking# +Item Appraisal# +Insert Card# +Please enter the reason of Secession.# +Please enter the reason of Expulsion.# +Please close Shop.# +Skill # +Item Name# +https://pay.ragnarok.co.kr (Billing Web)# +IP capacity of this Internet Cafe is full. Would you like to pay the personal base?# +You are out of available paid playing time. Game will be shut down automatically.# +Name is too long. Please enter a name no greater than 23 english characters.# +deleted# +You paid with the personal regular base.# +You paid with the personal regular base. Available time is xx hrs xx mins xx secs.# +You are free!# +You are free for the test, your available time is xx hrs xx mins xx secs.# +You paid with the Internet Cafe regular base. Available time is xx hrs xx mins xx secs.# +You paid with the Time Limit for Internet Cafe. Available time is xx hrs xx mins xx secs.# +You are free for the test of Internet Cafe version .# +You are free for the Internet Cafe version.# +You paid on the Time Limit Website.# +Emotion icon List# +/emo# +/!# +/?# +/ho# +/lv# +/lv2# +/swt# +/ic# +/an# +/ag# +/$# +/...# +/thx# +/wah# +/sry# +/heh# +/swt2# +/hmm# +/no1# +/??# +/omg# +/oh# +/X# +/hlp# +/go# +/sob# +/gg# +/kis# +/kis2# +/pif# +/ok# +Shortcut List# +Your account is suspended.# +Your connection is terminated due to change in the billing policy. Please connect again.# +Your connection is terminated because your IP doesn't match the authorized IP from the account server.# +Your connection is terminated to prevent charging from your account's play time.# +You have been forced to disconnect by the Game Master Team.# +You can't use this Skill because you are over your Weight Limit.# +Nameless# +Congratulations! %s's rank has gone up to %d.# +What a pity! %s's rank has gone down to %d.# +Pet Info# +Hunger# +Intimacy# +Please avoid opening a chatroom while vending.# +ea# +You have knocked down %s.# +You have been knocked down by %s.# +Pet food '%s' is not available.# +Feed Pet# +Performance# +Return to Egg# +Unequip Accessory# +Check Pet Status# +Accessory# +Equipped# +Pet List# +Unequipped# +Are you sure that you want to feed your pet?# +Only the numbers (0~9) are available.# +You cannot sell unidentified items.# +Item at 0 Zeny exists. Do you wish to continue?# +[New Emotion List]# +N/A# +N/A# +Character in the same account already joined.# +(%d ppl) - over the age 18# + Provoke initiated.# + Provoke canceled.# + Endure initiated.# + Endure canceled.# + Improve Concentration initiated.# + Improve Concentration canceled.# + Hiding initiated.# + Hiding canceled.# + Cloaking initiated.# + Cloaking canceled.# + Poison React initiated.# + Poison React canceled.# + Movement speed reduced.# + Quagmire canceled.# + Defense increased.# + Angelus canceled.# + Str, Int and Dex increased.# + Blessing canceled.# + Signum Crusis initiated.# + Signum Crusis canceled.# + Slow Poison initiated.# + Slow Poison canceled.# +SP regeneration increased.# +Magnificat canceled.# +Luk increased.# +Gloria canceled.# + You will receive double damage from 1 attack.# + Lex Eterna canceled.# + Attack speed increased.# + Attack speed reduced.# + You just mounted a Pecopeco.# + You just dismounted a Pecopeco.# + You just carried a Falcon with.# + You just released a Falcon.# + Play Dead initiated.# + Play Dead canceled.# + Str increased.# + Str turned back to normal.# + Energy Coat initiated.# + Energy Coat canceled.# + Armor damaged.# + Armor repaired.# + Weapon damaged.# + Weapon repaired.# + Invisibility initiated.# + Invisibility canceled.# +Sorry. It is delayed due to the process of payment. Please re-connect in a minute.# +You must unequip ammunition first.# +Arrow list# +Cart list# +You must have a cart.# +You cannot open a Chat Window.# +Registering an account is the first step to accessing the game. Do you want to visit the registration page now?# +You cannot use this item while sitting.# +Your use of skills and chat will be blocked for the next %d minutes.# +Your use of skills and chat have been reinstated.# +- [Not equipped]# + Very Hungry# + Hungry# + Neutral# + Satisfied# + Stuffed# + Awkward# + Shy# + Cordial# + Loyal# +Unknown# +Your account has play time of %d day %d hour %d minute.# +Your account is already connected to account server.# +Your account has play time of %d hour %d minute.# +Your account is a free account.# +This account can't connect the Sakray server.# +Your pet name must be 23 characters or less.# +You may change your pet's name only once. Your pet's name will be changed to ^0000ff %s^000000. Do you wish to continue?# +/font# +Your guild lacks the funds to pay for this venture.# +Your guild zeny limit prevents you from performing this action.# +Simplified effects have been activated.# +Simplified effects have been deactivated.# +Required Fee# +If you wish to drop an item, you must first open your Item Window (alt+e).# +Internet Cafe Time Plan has been ended. Would you like to continue the game with your personal play time?# +# +# +You're lack of zeny or your zeny limit have prevented you from performing this action.# +Your character has fainted. Push the ESC key to restart.# + - %d obtained.# +Spell List# +/minimize# +This item has been damaged.# +/noshift: You may use your ''force heal'' ability without the Shift key. On | Off# +[no shift] option activated. [ON]# +[no shift] option deactivated. [OFF]# +MSI_REFUSE_BAN_BY_DBA# +MSI_REFUSE_EMAIL_NOT_CONFIRMED# +MSI_REFUSE_BAN_BY_GM# +MSI_REFUSE_TEMP_BAN_FOR_DBWORK# +MSI_REFUSE_SELF_LOCK# +MSI_REFUSE_NOT_PERMITTED_GROUP# +MSI_REFUSE_WAIT_FOR_SAKRAY_ACTIVE# +/aura: Simplify Aura effect On | Off# +Simplified Aura [OFF]# +Simplified Aura [ON]# +Chat block record %d times# +Chat block list# +/showname: Change the name font type.# +/noctrl | /nc: Auto attack without pressing ctrl key. On | Off# +Use auto attack without Ctrl. [Auto attack ON]# +Use auto attack with Ctrl. [Auto attack OFF]# +Mute this player.# +Unmute player & Erase mute time.# +Decrease Player Mute time.# +Normal Font Displayed. [showname type 1]# +Font will be thin and party name will be shown [showname type 2]# +/doridori: Shake head# +Internet cafe is paying now.# +Prepaid voucher validate until %d days %d hours %d minutes later.\nTime limit voucher validate untill %d hours %d minutes later.# +/bingbing: Rotates player counter clockwise.# +/bangbang: Rotates player clockwise.# +/skillfail: Display red font message when skill fails. On | Off# +Skill fail messages will be displayed. [Display On]# +Skill fail messages will not be displayed. [Display OFF]# +/notalkmsg: Chat will not be displayed in chat window. On | Off# +Chat content will be displayed in the chat window. [Display ON]# +Chat content will not be displayed in the chat window. [Display OFF]# +/set1: /noctrl + /showname + /skillfail# +/fog: Fog effect. On | Off# +You have received a marriage proposal. Do you accept?# +Item sharing type# +Individual# +Shared# +nProtect KeyCrypt# +Keyboard Driver has been detected. \n\nDo you want to install a program for keyboard security? \n\n(After installation, System Reboot is required)# +Installation has been completed. \n\nSystem will be rebooted.# +Installation has been failed.# +Keyboard Security will be skipped.# +Required file for Keyboard Security is not existing. \n\n(npkeyc.vxd, npkeyc.sys, npkeycs.sys)# +USB Keyboard has been detected. \n\nDo you want to install a program for keyboard security? \n\n(After installation, System Reboot is required)# +ftp://ragnarok.nefficient.co.kr/pub/ragnarok/ragnarok0526.exe# +FindHack is not installed correctly. Please download ragnarok0226.exe and install it in RagnarokOnline directory.(%d).# +Hacking tool is existing but it hasn't been cleaned. Rangarok Online will not be executed.# +Hacking tool scan program has not been downloaded correctly. Please download ragnarok0226.exe and install it in RagnarokOnline directory.# +NPX.DLL register error or there is no necessary file to run FindHack. Please download ragnarok0226.exe and install it in RagnarokOnline directory.# +Exceptional Error. Please contact the customer support. Return Value: (%d)# +Exit button has been clicked.# +Unable to connect Findhack Update Server. Please try again or contact the customer support.# +Beloved # +/report: Save a chat log file.# +Chat logs are not accepted as evidence for any ill-mannered violation on account of possible file modifications. However this feature is provided for players' personal reference.# +I love you.# +Please adjust your monitor/video brightness if effects appear too bright.# +If full screen mode fails to work, it is suggested you alt+tab [or ctrl+esc] to inactivate and reactivate the Ragnarok Client.# +(%d players) - Pay to Play Server# +(%d players) - Free Server# +Trial players can't connect Pay to Play Server.# +Right click menu skills for F9 are Enabled.[/q1 ON]# +Right click menu skills for F9 are Disabled.[/q1 OFF]# +/quickspell: Right-click menu enables you to use skills assigned to the F9 hotkey. On | Off# +Mouse wheel skills for F7 and F8 are Enabled.[/q2 ON]# +Mouse wheel skills for F7 and F8 are Disabled.[/q2 OFF]# +/quickspell2: By rolling the mouse wheel up and down, you are able to use skills registered on F7 and F8 hotkeys. On | Off# +/q3: /quickspell (/q1) + /quickspell2 (/q2)# +/bzz# +/rice# +/awsm# +/meh# +/shy# +/pat# +/mp# +/slur# +/com# +/yawn# +/grat# +/hp# +/emotion: views the emoticon list.# +Skills assigned to shortcut windows 1, 2, 3 are Enabled. [/bm ON]# +Skills assigned to shortcut windows 1, 2, 3 are Disabled. [/bm OFF]# +/battlemode: allows you to use skills assigned to Shortcut Window 2 by pressing Q ~ O keys.# +A ~ L keys allow you to use skills assigned to Shortcut Window 3.# +Please remember, programs running in the background while playing may affect the game's performance.# +Dear angel, can you hear my voice?# +I am# +Super Novice~# +Help me out~ Please~ T_T# + wishes to adopt you. Do you accept?# +Z ~ > keys allow you to use skills assigned on shortcut window 1. On | Off# +Press the space bar to Chat when in Battle mode [/battlemode | /bm].# +'Either there's no Game Guard installed on the program or Game Guard is cracked. Please, try to reinstall Game Guard from its setup file.'# +Some of Windows system files have been damaged. Please re-install your Internet Explorer.# +'Failed to run Game Guard. Please, try to reinstall Game Guard from its setup file.'# +'At least one hazardous program has been detected. Please, terminate all the unnecessary programs before executing Game Guard.'# +'Game Guard update is canceled. If the disconnection continues, please, check your internet or firewall settings.'# +'Failed to connect to Game Guard update server. Try to connect again later, or try to check the internet or firewall settings.'# +'Can't complete Game Guard update process. Please, try to execute a vaccine program to remove viruses. Or, please try to modify the settings of your PC managing tool if you are using any.'# +/notrade: Declines trade offers automatically. On | Off# +Auto decline trade offers has been Enabled. [/nt ON]# +Auto decline trade offers has been Disabled. [/nt OFF]# +You cannot buy more than 30,000ea items at once.# +You do not have enough ingredients.# +Login information remains at %s.# +Account has been locked for a hacking investigation. Please contact the GM Team for more information.# +This account has been temporarily prohibited from login due to a bug-related investigation.# +Repairable items# +Item has been successfully repaired.# +You have failed to repair this item. Please check the distance between you and opponent.# +System process enabled [GM mode] [/sc ON]# +System process disabled [GM mode] [/sc OFF]# +/systemcheck: Check the system process [GM mode] On | Off# +(%s) wishes to be friends with you. Would you like to accept?# +Your Friend List is full.# +(%s)'s Friend List is full.# +You have become friends with (%s).# +(%s) does not want to be friends with you.# +This character will be blocked to use until %s.# +Price will be fixed at 10,000,000 zeny, even if you enter higher price.# +(Very low)# +(Low)# +(Normal)# +(High)# +(Very high)# +You have been blocked from using chat and skills for %d minutes by the GM Team.# +%d minutes remain until release from the GM penalty.# +You have been released from the GM penalty.# +You have been blocked from using chat and skills for %d as an automatic penalty.# +%d minutes remain until release from auto penalty.# +You have been released from the auto penalty. Please refrain from spamming in-game.# +%s and %s have divorced from each other.# +%s has been designated as Gravity %s's Solar Space.# +%s has been designated as Gravity %s's Luna Space.# +%s has been designated as Gravity %s's Stellar Space.# +Gravity %s's Solar Space: %s# +Gravity %s's Luna Space: %s# +Gravity %s's Stellar Space: %s# +%s has been designated as Gravity %s's Solar Monster.# +%s has been designated as Gravity %s's Luna Monster.# +%s has been designated as Gravity %s's Stellar Monster.# +Gravity %s's Solar Monster: %s# +Gravity %s's Luna Monster: %s# +Gravity %s's Stellar Monster: %s# +/window: Display windows will snap/dock together. On | Off# +Display window docking enabled. [/wi ON]# +Display window docking disabled. [/wi OFF]# +/pvpinfo: shows your PVP result and PVP points.# +You have won %d times and have lost %d times in PVP. Current points %d.# +A manner point has been successfully aligned.# +You are in a PK area. Please beware of sudden attack.# +Game Guard update has been failed when either Virus or Spyware conflicted with. Please, Uninstall Spyware and Virus protection program before you log in.# +Program has encountered an error related to Windows compatibility. Please start the game again.# +You have been blocked from chatting, using skills and items.# +Login is temporarily unavailable while this character is being deleted.# +Login is temporarily unavailable while your spouse character is being deleted.# +Novice# +Swordman# +Mage# +Archer# +Acolyte# +Merchant# +Thief# +Knight# +Priest# +Wizard# +Blacksmith# +Hunter# +Assassin# +Novice# +Swordman# +Mage# +Archer# +Acolyte# +Merchant# +Thief# +Knight# +Priest# +Wizard# +Blacksmith# +Hunter# +Assassin# +Send an adoption request to %s# +When you become a child, you will be unable to become a Transcendent Class character, all stats will be limited to a maximum of 80, and Max HP/SP will be reduced. Are you sure that you want to be adopted?# +All abnormal status effects have been removed.# +You will be immune to abnormal status effects for the next minute.# +Your Max HP will stay increased for the next minute.# +Your Max SP will stay increased for the next minute.# +All of your Stats will stay increased for the next minute.# +Your weapon will remain blessed with Holy power for the next minute.# +Your armor will remain blessed with Holy power for the next minute.# +Your Defense will stay increased for the next 10 seconds.# +Your Attack strength will be increased for the next minute.# +Your Accuracy and Flee Rate will be increased for the next minute.# +You cannot adopt more than 1 child.# +You must be at least character level 70 in order to adopt someone.# +[Point] You have been rewarded with %d Blacksmith rank points. Your point total is %d.# +[Point] You have been rewarded with %d Alchemist rank points. Your point total is %d.# +Dear angel, can you hear my voice?# +I am# +Super Novice~# +Help me out~ Please~ T_T# +/notalkmsg2: Hides chat messages(including guild chat). On Off# +Show chat messages. [/nm2 ON]# +Hide chat messages(including guild chat) [/nm2 OFF]# +Upgradeable weapons# +Refined weapon: %s# +Refined weapon: %s# +You cannot upgrade %s until you level up your Upgrade Weapon skill.# +%s is required to upgrade this weapon.# +Full Divestment cannot pierce the target. The target is fully shielded.# +You cannot adopt a married person.# +This name is not registered in your Friend List. Please check the name again.# +/hi or /hi message: Send greetings to people who are online and registered on your Friend List.# +This character is not your guildsman. Please check the name again.# +Please be aware that the maximum selling price is fixed as 2 Billion. You cannot sell an item higher than that.# +Whispers from friends are displayed as [ Friend ], and ones from guildsmen are displayed as [ Member ].# +( From character name: ) is from an anonymous character who is neither your friend nor guildsman.# +/blacksmith: Shows top 10 Blacksmiths in the server.# +/alchemist: Shows top 10 Alchemists in the server.# +ALT+Y: Opens a window which allows you to use various commands with ease.# +[POINT] You have been rewarded with %d Tae-Kwon Mission rank points. Your point total is %d.# +[Taekwon Mission] Target Monster: %s (%d%%)# +Error - Failed to initialize GameGuard: %lu# +Speed Hack has been detected.# +The illegal program, (%s) has been detected.# +The Game or Gameguard has been cracked.# +GameGuard is currently running. Please wait for sometime and restart the game.# +The Game or GameGuard is already running. Please close the game and restart the game.# +Failed to intialize GameGuard. Please try again after rebooting the system or closing other programs.# +Failed to load the scan module of virus and hacking tool. It's caused by lack of memory or PC virus infection.# +Homunculus Info# +Homunculus Skill List# +Please give your Homunculus a name no longer than 23 letters.# +You can name a Homunculus only once. You have entered the name, ^0000ff%s^000000. Would you like to continue?# +(Away)# +[Automated Message]# +Send an automated message while you are away.# +Cancel automated away message.# +Please enter Away Message.# +/fsh# +/spin# +/sigh# +/dum# +/crwd# +/desp# +/dice# +/pk: Shows top 10 Slayers in the server.# +[POINT] You have been rewarded with %d Slayer rank points. Your point total is %d.# +Evolution Available# +You have decided to delete this Homunculus ^ff0000^ff0000. When deleted, the homunculus and its history will be deleted and they cannot be restored in the future. Would you like to continue?# +Save Homunculus status as a file.# +Do not save Homunculus status as a file.# +Crusader# +Monk# +Sage# +Rogue# +Alchemist# +Bard# +Crusader# +Monk# +Sage# +Rogue# +Alchemist# +Dancer# +High Novice# +High Swordman# +High Mage# +High Archer# +High Acolyte# +High Merchant# +High Thief# +High Novice# +High Swordman# +High Mage# +High Archer# +High Acolyte# +High Merchant# +High Thief# +Lord Knight# +High Priest# +High Wizard# +Whitesmith# +Sniper# +Assassin Cross# +Lord Knight# +High Priest# +High Wizard# +Whitesmith# +Sniper# +Assassin Cross# +Paladin# +Champion# +Professor# +Stalker# +Creator# +Clown# +Paladin# +Champion# +Professor# +Stalker# +Creator# +Gypsy# +You have not set a password yet. Would you like to create one now?# +You have incorrectly entered the password 3 times. Please try again later.# +Password creation has failed.# +Password must be 4~8 letters long.# +Password# +New Password# +Confirm Password# +Password has been changed.# +Password does not match.# +Enter Password# +Your Homunculus is starving. Please feed it, otherwise it will leave you.# +EXP# +[EVENT] You have won an event prize. Please claim your prize in game.# +Hate# +Hate with a Passion# +Homunculus has been customized.# +Homunculus has been activated with the basic AI.# +Mail List# +Write Mail# +Read Mail# +You cannot change a map's designation once it is designated. Are you sure that you want to designate this map?# +Item has been added in the Item Window.# +You have failed to add the item in the Item Window.# +You have successfully mailed a message.# +You have failed to mail a message. Recipient does not exist.# +[Solar, Lunar and Stellar Angel] Designated places and monsters have been reset.# +The minimum starting bid for auctions is 10,000,000 zeny.# +You have successfully started a new auction.# +The auction has been canceled.# +An auction with at least one bidder cannot be canceled.# +Mail has been successfully deleted.# +You have failed to delete the mail.# +You have equipped throwing daggers.# +%s has logged in.# +%s has logged out.# +/loginout: Shows guildsmen and friends online status. On Off# +Display online status of friends in Chat Window. [/li ON]# +Do not display online status of friends in Chat Window. [/li OFF]# +It is already running.# +Use of Macro program has been detected.# +Use of Speed hack has been detected.# +API Hooking has been detected.# +Message Hooking has been detected.# +Module has been modified or damaged or its version does not match.# +(Thailand) You have logged in game with Internet cafe payment.# +Prev# +Next# +Auction# +Product List# +Register# +Sale Status# +Purchase Status# +Item# +Name# +Current Bid / Max Bid# +Seller# +Buyer# +End Time# +%m %d %H# +Time (Hr)# +Fee# +No items found in auction search.# +Your Sale List is empty.# +Your Purchase List is empty.# +Auction Information is incorrect or incomplete.# +You must drag and drop an item from your Inventory into the Register Window to begin a new auction.# +The auction has already been registered.# +Starting Bid# +Current Bid# +Buy Now Price# +Your Current Zeny# +Highest Bid# +Previous Bid# +Next Bid# +Buy it now?# +Would you like to sell this item?# +Place Bid# +Buy Now# +End the Auction# +Place another Bid# +You have placed a bid.# +You have failed to place a bid.# +You do not have enough zeny.# +Armor# +Card# +Etc.# +Bid# +Search# +You have ended the auction.# +You cannot end the auction.# +Bid Number is incorrect.# +To# +Title# +You have received a message in the mail.# +Searching...# +You cannot register more than 5 items in an auction at a time.# +You cannot place more than 5 bids at a time.# +Please accept all items from your mail before deleting.# +Please enter a title.# +/shopping: Enables you to open a shop with a single left-click and close your shop with a single right-click. On Off# +You can now open a shop with a single left-click and close your shop with a single right-click. [sh ON].# +You can open a shop by double-clicking. [/sh OFF]# +Please enter zeny amount before sending mail.# +You do not have enough zeny to pay the Auction Fee.# +View Status# +Feed# +Stand By# +Super Novice# +Super Novice# +Taekwon Boy# +Taekwon Girl# +Star Gladiator# +Star Gladiator# +Soul Linker# +Soul Linker# +Please check the connection, more than 2 accounts are connected with Internet Cafe Time Plan.# +Your account is using monthly payment. (Remaining day: %d day)# +Your account is using time limited. (Remaining time: %d hour %d minute %d second)# +This item cannot be mailed.# +You cannot accept any more items. Please try again later.# +Male# +Female# +New User.# +E-mail address is required to delete a character.# +Please enter the correct information.# +Please use this key.# +Please enter the correct card password.# +PT Info# +PT_ID is %s# +NUM_ID is %s# +Please don't forget this information.# +1001# +1002# +1003# +1004# +1006# +1007# +1008# +1009# +1012# +1013# +1014# +1015# +1019# +1020# +1021# +1023# +1024# +1025# +1027# +1028# +10# +20# +40# +50# +60# +70# +80# +90# +100# +110# +Do you want to receive 30 points?# +30 points (5 hours) have been added.# +You cannot register Unidentified Items in auctions.# +You cannot register this Consumable Item in an auction.# +Please close the Cart Window to open the Mail Window.# +Please close the Mail Window to open the Cart Window.# +Bullets have been equipped.# +The mail has been returned to sender.# +The mail no longer exists.# +More than 30 players sharing the same IP have logged into the game for an hour. Please check this matter.# +More than 10 connections sharing the same IP have logged into the game for an hour. Please check this matter.# +Please restart the game.# +Mercenary: Archer# +Mercenary: Swordman# +Mercenary: Spearman# +Expiration# +Loyalty# +Summons# +Kill# +You can feel hatred from your pet for neglecting to feed it.# +[POINT] You earned %d Taming Mission Ranking Points, giving you a total of %d points.# +[Taming Mission] Target Monster: %s# +/hunting: You can check the your hunting list.# +[Angel's Question] Please tell me, how many %s skills do you have?# +[Angel's Question] Please tell me, how much zeny you'll have if you divide it by 100,000?# +[Angel's Question] Please tell me, what is today's date?# +[Angel's Question] Please tell me, how many %s do you have?# +[Angel's Question] If A is 1, B is 2, and so on, and if Z is 26, what number do you get if you add the letters in SiYeon's name?# +[Angel's Question] If A is 1, B is 2, and so on, and if Z is 26, what number do you get if you add the letters in Munak's name?# +[Angel's Question] If A is 1, B is 2, and so on, and if Z is 26, what number do you get if you add the letters in Bongun's name?# +[Angel's Question] If A is 1, B is 2, and so on, and if Z is 26, what number do you get if you add the letters in the word, Ragnarok?# +[Angel's Question] If A is 1, B is 2, and so on, and if Z is 26, what number do you get if you add the letters in the word, online?# +[Angel's Question] If A is 1, B is 2, and so on, and if Z is 26, what number do you get if you add the letters in the word, death?# +[Angel's Question] If A is 1, B is 2, and so on, and if Z is 26, what number do you get if you add the letters in the word, knight?# +[Angel's Question] If A is 1, B is 2, and so on, and if Z is 26, what number do you get if you add the letters in the word, gravity?# +[Angel's Question] If A is 1, B is 2, and so on, and if Z is 26, what number do you get if you add the letters in the word, dark?# +[Angel's Question] If A is 1, B is 2, and so on, and if Z is 26, what number do you get if you add the letters in the word, collecter?# +[Angel's Answer] Thank you for letting me know~# +[Angel's Answer] I'm very pleased with your answer. You are a splendid adventurer.# +[Angel's Answer] You've disappointed me...# +[Point] You earned %d Ranking Points, giving you a total of %d Ranking Points.# +[%s]'s Points: %d Points# +Unselected Characters will be deleted. Continue?# +You cannot select more than 8.# +Do you want to change your name to '%s'?# +Character Name has been changed successfully.# +You have failed to change this character's name.# +You can purchase only one kind of item at a time.# +No characters were selected. You must select at least one character.# +This character's name has already been changed. You cannot change a character's name more than once.# +User Information is not correct.# +Another user is using this character name, so please select another one.# +The party member was not summoned because you are not the party leader.# +There is no party member to summon in the current map.# +You cannot find any trace of a Boss Monster in this area.# +Boss Monster, '%s' will appear in %02d hour(s) and %02d minute(s).# +The location of Boss Monster, '%s', will be displayed on your Mini-Map.# +Do you want to open ^990099%s^000000? Once opened, the contents cannot be moved to other locations aside from the Kafra Storage. The item effect isn't doubled, even if the same items are used more than once.# +The Purchase has failed because the NPC does not exist.# +The Purchase has failed because the Kafra Shop System is not working correctly.# +You cannot purchase items while you are in a trade.# +The Purchase has failed because the Item Information was incorrect.# +STR has increased.# +STR has returned to normal.# +AGI has increased.# +AGI has returned to normal.# +VIT has increased.# +VIT has returned to normal.# +INT has increased.# +INT has returned to normal.# +DEX has increased.# +DEX has returned to normal.# +LUK has increased.# +LUK has returned to normal.# +Flee Rate (Flee) has increased.# +Flee Rate has returned to normal.# +Accuracy Rate (Hit) has increased.# +Accuracy Rate has returned to normal.# +Critical Attack (Critical) has increased.# +Critical Attack has returned to normal.# +You will receive 1.5 times more EXP from hunting monsters for the next 30 minutes.# +This character will not receive any EXP penalty if ko'ed within the next 30 minutes.# +Regular item drops from monsters will be doubled for the next 30 minutes.# +Boss Monster Map Information for the next 10 minutes.# +Do you really want to purchase this item? %d points will be deducted from your total Kafra Credit Points.# + You do not have enough Kafra Credit Points.# + ^ff0000Expiration Date: %s^000000# + The '%s' item will disappear in %d minutes.# + '%s' item will be deleted from the Inventory in 1 minute.# + '%s' item has been deleted from the Inventory.# +Input Number# +%m/%d %H:%M# +Boss Monster '%s' will appear within 1 minute.# +Mercenary Soldier Skill List# +Do you agree to cast the magic spell that consumes 1 Black Gemstone and 1,000,000 Zeny?# +[Point] You have gained %d Collector Rank Points; you now have a total of %d Collector Rank Points.# +[Collector Rank] Target Item: %s# +The mercenary contract has expired.# +The mercenary has died.# +You have released the mercenary.# +The mercenary has run away.# + The '%s' item will disappear in %d seconds.# +IP Bonus: EXP/JEXP %d%%, Death Penalty %d%%, Item Drop %d%%# +Symbols in Character Names are forbidden.# +Mercenary will follow custom AI.# +Mercenary will follow basic AI.# + %s's # +%s has acquired %s.# +Public Chat# +Whisper# +Party Chat# +Guild Chat# +Item get/drop# +Equipment on/off# +Abnormal status# +Party member's obtained item# +Party member's abnormal status# +Skill failure# +Party configuration# +Damaged equipment# +Battle Message Window# +[%s]'s Han Coin: %d Han Coin# +Public# +Battle# +Mobile Authentication# +Read# +Auto Read# +Bookmark# +Previous# +Next# +Close# +%s's equipment has been damaged.# +%s's %s was damaged.# +Weapon# +Armor# +Insufficient Skill Level for joining a Party# +[%s]'s Free Cash: %d Cash# +Use Free Cash: # +Cash# +http://payment.ro.hangame.com/index.asp# +You need to accept the Privacy Policy from Gravity in order to use the service.# +You need to accept the User Agreement in order to use the service.# +Incorrect or nonexistent ID.# +Do you really want to purchase these items? You will spend %d Regular Cash Points and %d Free Cash Points.# +%d hour(s) has passed.# +%d hour(s) %d minute(s) has passed.# +Please stop playing the game, and take a break. Exp and other features will be reduced to 50%.# +Please stop playing the game since you'll need to rest. Exp and other features will be fixed to 0%.# +Quest List# +RO Shop# +Memorial Dungeon, '%s' is booked.# +Failed to book Memorial Dungeon, '%s'.# +Memorial Dungeon, '%s' is already booked.# +Memorial Dungeon, '%s' is created.\n Please enter in 5 minutes.# +Failed to create Memorial Dungeon, '%s'.\n Please try again.# +The character blocked the party invitation.# +Block all party invitations.# +Allow all party invitations.# +This item will be permanently bound to this character once it is equipped. Do you really want to equip this item?# +%s is now permanently bound to this character.# +You do not have enough Kafra Credit Points. Please enter whether you have free credit points.# +Request to Join Party# +WOE Information# +Memorial Dungeon %s's reservation has been canceled.# +Failed to create Memorial Dungeon %s. Please try again.# +This skill cannot be used within this area.# +This item cannot be used within this area.# +Memorial Dungeon# +%s in Standby# +%s Available# +%s in Progress# +No one entered the Memorial Dungeon within its duration; the dungeon has disappeared.# +Please apply for dungeon entry again to play in this dungeon.# +Your Standby Priority: ^ff0000%d^000000# +The requested dungeon will be removed if you do not enter within ^ff0000%s^000000.# +Dungeon Mission Time Limit:# +The Memorial Dungeon reservation has been canceled.# +The Memorial Dungeon duration expired; it has been destroyed.# +The Memorial Dungeon's entry time limit expired; it has been destroyed.# +The Memorial Dungeon has been removed.# +A system error has occurred in the Memorial Dungeon. Please relog in to the game to continue playing.# +This slot is not usable.# +Your Base Level is over 15.# +Your Job Level is over 15.# +You cannot play the Merchant class character in this slot.# +Not Yet Implemented# +You are not eligible to open the Character Slot.# +This character cannot be deleted.# +This character's equipment information is not open to the public.# +Equipment information not open to the public.# +Equipment information open to the public.# +Check %s's Equipment Info# +'%s's Equipment# +Show Equip# +This service is only available for premium users.# +Free Trial users can only hold up to 50,000 zeny.# +Battlefield Chat has been activated.# +Battlefield Chat has been deactivated.# +Mercenary Info - Monster Type# +World Map# +The Memorial Dungeon is now closed.# +^ff0000Deleting a Mercenary Soldier^000000 will also delete his growth history. Do you really want to proceed with the deletion?# +The Memorial Dungeon is now open.# +This account has not been confirmed by connecting to the safe communication key. Please connect to the key first, and then log into the game.# +The number of accounts connected to this IP has exceeded the limit.# +You have received a new quest.# +^CC3399Requirement:# +View Skill Info# +Once used, skill points cannot be re-allocated. Would you like to use the skill points?# +1stJob# +2ndJob# +This account has been used for illegal program or hacking program. Block Time: %s# +The possibility of exposure to illegal program, PC virus infection or Hacking Tool has been detected. Please execute licensed client. Our team is trying to make a best environment for Ro players.# +You are currently playing in the best game environment. Please enjoy the Ragnarok.# +Job Exp points from hunting monsters are increased by 50% for 30 minutes.# +Exp points from hunting monsters are increased by 25% for 30 minutes.# +EXP points from hunting monsters are increased by 100% for 30 minutes.# +EXP points from hunting monsters are increased by 50% for 60 minutes.# +Unable to organize a party in this map.# +(%s) are currently in restricted map to join a party.# +Simple Item Shop# +Han Coin: %d Han Coin# +RoK Point: %d RoK Point# +Free Cash: %d Cash# +An user of this server cannot connect to free server# +Your password has expired. Please log in again# +3rdJob# +This skill can't be used on that target.# +You can't use skill because you have exceeded the number Ancilla possession limit# +[Holy Water] is required.# +[Ancilla] is required.# +Cannot be duplicated within a certain distance.# +This skill requires other skills to be used.# +Chat is not allowed in this map# +3 hours have passed.# +5 hours have passed.# +Game guard initialization error or previous version game guard file is installed. Please re-install the setup file and try again# +Either ini file is missing or altered. Install game guard setup file to fix the problem# +There is a program found that conflicts with game guard# +Incorrect client. Please run a normal client# +Please run a mobile verification# +Verification failed# +This skill can't be used alone# +This skill can be used to certain direction only# +Cannot summon spheres anymore.# +There is no summoned sphere or you do not have enough sphere.# +There is no imitation skills available.# +You can't reuse this skill# +Skill can't be used in this state# +You have exceeded the maximum amount of possession of another item.# +No administrative privileges. Must first run the program with administrator privileges.# +nProtect KeyCrypt not the same. Please restart the program and the computer first.# +Currently in Windows XP Compatibility Mode. The program now removes Compatibility Mode. Please restart the program.# +PS/2 keyloggers exist.# +USB Keylogging attempt was detected.# +HHD monitoring tool has been detected.# +[Paint Brush] is required.# +[Surface Paint] is required.# +Use the skills that are not at the specified location.# +Insufficient SP.# +Character %d is character selection window cannot connect to the game that exceeds the total. Please remove unwanted characters.# +[Neck Protection Candy] is required.# +[Regrettable Tears] is required.# +[Neck Protection Candy] is required.# +Can only be used for linked to Weapon Blocking.# +A weapon coated with Guillotine Cross's Poison is required.# +Item can only be used when Mado Gear is mounted.# +[Vulcan Bullet] is required.# +[Mado Gear Fuel] is required.# +[Liquid Condensed Bullet] is required.# +Please load a [Cannon Ball].# +Please equip [Accelerator].# +Please equip [Hovering Booster].# +[Toxin] Poison effect is applied to the weapon.# +[Paralysis] Poison effect is applied to the weapon.# +[Fatigue] Poison effect is applied to the weapon.# +[Laughing] Poison effect is applied to the weapon.# +[Disheart] Poison effect is applied to the weapon.# +[Pyrexia] Poison effect is applied to the weapon.# +[Oblivion] Poison effect is applied to the weapon.# +[Leech] Poison effect is applied to the weapon.# +Can only be used in Hovering state.# +Please equip [Suicidal Device].# +Please equip [Shape Shifter].# +Guillotine Cross Poison is required.# +Please equip [Cooling Device].# +Please equip [Magnetic Field Generator].# +Please equip [Barrier Builder].# +Please equip [Camouflage Generator].# +Please equip [Repair Kit].# +[Monkey Wrench] is required.# +Cannot use [%s] due to cooldown delay.# +Deletion is impossible for over level %d# +Can't be used while on Mado Gear.# +Dismount Dragon# +Dismount Mado Gear# +Use# +Cash# +Armors# +Weapons# +Ammo# +Card# +Etc# +Client response time has passed so connection is terminated# +Incorrect version of hack shield file. Please reinstall the client# +[Magic Book] is required.# +Feel sleepy since Magic Book is too difficult to understand.# +Not enough saved point.# +Can't read a Magic Book anymore.# +[Face Paint] is required.# +[Makeover Brush] is required.# +Waiting time has passed. Please log in again# +Watch out! Same account is already logged in. Stop mobile verification and log in again after changing your password# +Watch out! Same account is waiting for mobile verification. Stop mobile verification and log in again after changing your password# +Game option # +Graphic# +Sound# +Press a key to assign. Pressing 'ESC' will remove the assigned key.# +Unable to specify a single key.# +Unable to specify the key assigned.# +Duplicated with ['%s']. Do you still want to change?# +Initialization is stored in the shortcut key settings. Do you want to initialized?# +Skill Bar# +Interface# +Macros# +Shortcut settings# +BGM# +Effect# +Skin# +Chat room entry sound [ON]# +Chat room entry sound [OFF]# +/tingonly: you can hear only sound like a chat room entry.# +/rock# +/scissors# +/paper# +/love# +/mobile# +/mail# +/antenna0# +/antenna1# +/antenna2# +/antenna3# +/hum# +/abs# +/oops# +/spit# +/ene# +/panic# +/whisp# +Not Assigned# +Only available when cart is mounted.# +[Thorn Plant Seed] is required.# +[Blood Sucker Plant Seed] is required.# +Cannot be used anymore.# +[Bomb Mushroom Spore] is required.# +[Fire Bottle] is required.# +[Oil Bottle] is required.# +[Explosive Powder] is required.# +[Smokescreen Powder] is required.# +[Tear Gas] is required.# +[Acid Bottle] is required.# +[Plant Bottle] is required.# +[Mandragora Flowerpot] is required.# +Assign Party Leader# +Switch Party Leader?# +Can't re-assign party leader# +Can't be changed because of level gap# +[%s] required '%d' amount.# +Is now refining the value lowered.# +Please equip [%s].# +Battle field entrance setting# +Battlefield - [%s] you sign up?# +Admission application complete.# +It was unregistered and not be able to enter the state.# +Current admission application state.# +Do you want to cancel the admission application?# +%s has canceled Battle field admission.# +After %s go to the Battle field. Make sure you're in stable condition. (Trading and teleportation are disabled)# +Formal Name# +Do you really want to go back to your savepoint?# +Party member search# +Message option is off the search party members.# +10 seconds delay of party support is in effect# +Party leader is '%s'.# +Unable to enter due to system error.# +Cannot wait to enter the number of excess.# +Has already been applied.# +Registration has been cancelled because of the excessive waiting time.# +Unregistered because admission requirements are not matching.# +Was unregistered and error.# +The skill need [%s].# +The skill need a particular skill.# +Requires %d Spirit Sphere.# +Spirit Sphere is required.# +Cannot create rune stone more than the maximum amount.# +Not able to receive battle field list. Please check and try again# +Level is not high enough to enter# +You must consume all '%d' points in your 1st Tab.# +You must consume all '%d' remaining points in your 2nd Tab. 1st Tab is already done.# +Convertible item# +Items that can be converted# +Item to convert# +Combination of item is not possible in conversion.# +Is a very heavy weight of the Inventory.# +Please ensure an extra space in your inventory.# +Item does not exist.# +Successful.# +All material has disappeared and failed.# +Not very long to change the name of the specified tab.# +Cannot add more.# +Authentication failed.# +Bot checks# +Items cannot be used in materials cannot be emotional.# +It is impossible to connect using this IP in Ragnarok Online. Please contact the customer support center or home.# +You have entered a wrong password for more than six times, please check your personal information again.# +Consumption items are used in the synthesis. Are you sure?# +Please input the captcha code found at your left side.# +Describes the battlefield --# +Waiting for admission --# +Admission application help battlefield# +Sorry the character you are trying to use is banned for testing connection.# +Remove all equipment# +Mini Icon# +Camp A: Camp B# +Wait# +cancellation notice of Battlefield registration.# +Required field for staff# +Battlefield staff A is waiting.# +Battlefield staff B is waiting.# +Waiting for my situation: %d (Camp A)# +Waiting for my situation: %d (Camp B)# +Enable Battlefield icon.# +Disable Battlefield icon.# +Field notification was moved.# +Admission pending notification of the battlefield# +Someone# + [%s] inflicts '%d' damage points on you.# + [%s] attacked by [%s] with '%d' damage points.# + [%s] receives '%d' damage points.# + [%s] attacks [%s] with '%d' damage points.# +You dropped %s (%d).# +[%s] quest - defeating [%s] progress (%d/%d).# +[%s] quest was deleted.# +[%s] # + %d Base exp points# + %d Job exp points# + acquired.# + has lost.# +From [%s], '%d' coins were stolen.# +Battle message# +Party member's battle message# +Experience message# +Party member's experience message# +Quest information# +Battlefield message# +# +Casts [%s] skill.# +Activate lock function# +Deactivate lock function# +%s has opened [%s] and received [%s].# +Swordman# +Mage# +Archer# +Acolyte# +Merchant# +Thief# +Knight# +Priest# +Wizard# +Blacksmith# +Hunter# +Assassin# +Crusader# +Monk# +Sage# +Rogue# +Alchemist# +Bard# +Dancer# +Rune Knight# +Warlock# +Ranger# +Arc Bishop# +Mechanic# +Guillotine Cross# +Royal Guard# +Sorcerer# +Minstrel# +Wanderer# +Sura# +Geneticist# +Shadow Chaser# +High Swordman# +High Mage# +High Archer# +High Acolyte# +High Merchant# +High Thief# +Lord Knight# +High Priest# +High Wizard# +Whitesmith# +Sniper# +Assassin Cross# +Paladin# +Champion# +Professor# +Stalker# +Creator# +Clown# +Gypsy# +Wedding# +High Novice# +Super Novice# +Gunslinger# +Ninja# +Taekwon Boy/Girl# +Star Gladiator# +Soul Linker# +Party Recruitment# +Party Booking List# +Recruiting Party# +[Bow] must be equipped.# +[Musical Instrument/Whip] must be equipped.# +Only alphanumeric characters are allowed.# +Notice# +Item purchase failed due to incorrect shop information.# +Item cannot be discarded from the window.# +hour# +Map# +You can't use, equip or disarm items when you're trading.# +Unspecified value# +/stateinfo : Shows the description of status icons. On Off# +Status Information On: Status icon description is enabled.# +Status Information Off: Status icon description is disabled.# +It is not possible to purchase the same item more than %d pieces at a time# +It is not possible to purchase the same item more than %d pieces at a time# +Can purchase upto %d pieces of the same item at a time.# +User customized key is saved to [%s\%s]# +[%s] is currently on trade and cannot accept the request.# +RO_HELP# +Anvil does not exist.# +Novice below level 10 is not allowed to whisper.# +Attack# +Defense# +Recovery# +Support# +Party recruitment related command# +Guild alliance application is not possible..# +Guild hostility application is not possible.# +Adding friends is not possible in this map.# +Buying Store Window# +Price: # +Money: # +Purchase Zeny Limit# +Please register the item first that has to be purchased.# +Please enter the price for item %s.# +Please enter the price for item %s. It must be less than 9999 million Zeny.# +Please enter the quantity for %s.# +The total quantity of %s in possession is more than 9999. Please enter value less than 9999.# +You have duplicate items in your purchase list.# +Enter the limited price.# +You have entered a greater amount of zeny than you have. Please check your zeny.# +%s : %s Zeny => %s ea.# +Available items:# +Purchase list:# +Price limit: %s Zeny# +Buying %s for %s Zeny. Amount: %d.# +Wanted items# +Available items:# +The max. number of items you can sell is %d.# +Buyer has insufficient zeny, lower the amount of items you're selling.# +Failed to open purchase shop.# +You exceed the total amount of items.# +You have purchased all items within the limited price.# +You purchased all items.# +Failed to deal because you have insufficient zeny.# +You have sold %s. Amount: %d. Total Zeny: %dz# +%s item could not be sold because you do not have the wanted amount of items.# +You have not registered to sell the item.Please Register to sell item.# +You don't have any summoned spirits.# +This is a restricted server.# +OTP password is 6 digits long.# +OTP information is unavailable. Please contact your administrator.# +Party ad has been added.# +Recruit party members# +Roles# +1st Jobs# +2nd Jobs# +3-1 Classes# +3-2 Classes# +1st Jobs High# +2nd Jobs High# +Other Jobs# +Recruit# +Open party recruitment window.# +Searching - # +Select All# +Recruitment of at least one job must be running.# +You have to select at least 1 or more jobs.# +You have selected %d Jobs. You can only select up to 6 different jobs.# +Only numeric characters are allowed.# +Please enter levels between 1~150.# +Nothing found in the selected map.# +You cannot equip this item with your current level.# +You cannot use this item with your current level.# +Enable Battle mode# +Failed to add because you have reached the limit.# + message log settings# +Sell# +Purchase# +Search for Vends# +Shop Name# +Amount# +Price# +Too much results have been found. Please do a more precisely search.# +Do you want to open a street stall?# +Failed to recognize SSO.# +Cannot move to the applied area.# +searching item including the word# +User has been expelled.# +You have not accepted the user agreements yet.# +You will not be disconnect from the game.# +It is available only for 12 hours.# +Your account is blocked due to illegal use of the game account.# +Your account is blocked because there may exist a bug with your account.# +Increases base exp and job exp gained by killing monsters up to 75% for 30 minutes.# +Increases base exp and job exp gained by killing monsters up to 50% for 30 minutes.# +No sales information.# +Failed to search any further.# +The selected item does not exist.# +Cannot search yet.# +Enter the card name or prefix/suffix.# +Searches left: %d# +No result has been found.# +The item price is too high.# +General# +Costume# +minute# +second# +Please enter the name of the item.# +The item you have entered does not exist.# +The map is not available.# +The selected name or prefix/suffix does not exist.# +You can purchase up to 10 items.# +Some items could not be purchased.# +Enter your birthday (e.g: 20101126)# +Now Logging Out.# +A database error has occurred.# +Please leave your guild first in order to remove your character.# +Please leave your party first in order to remove your character.# +You cannot delete this character because the delete time has not expired yet.# +You cannot delete this character at the moment.# +Your entered birthday does not match.# +You lack of familiarity.# +This is only available on style change for fighting classes.# +This is only available on style change for novice.# +Registering party has failed.# +No list matching search criteria were found.# +Failed to remove result.# +No results have been found.# +No payment information has been found.# +Screenshot Trade# +[Trade_%s]# +Death due to the auto insurance young people are spending.# +Chat Dialog# +Redundant is not available.# +Use the limit that has been set.# +No user restrictions are set.# +Connection has failed. Please contact your administrator.# +Failed to authenticate.# +User is offline.# +The age limit from commandment tables cannot connect to this server.# +Buy# +Empty shopping cart# +First page# +Last page# +New Items# +Hot Items# +Limited Sell# +Rental Equipment# +Permanent Equipment# +Scrolls# +Consumables# +Other# +Cost# +Quantity# +Total# +Free Cash: %s Coin# +Cash Points: %s Coin# +You cannot summon a monster in this area.# +Exceeded total free cash# +Content has been saved in [SaveData_ExMacro%d]# +%d seconds left until you can use# +Must be equipped with [Spear].# +Available only on the dragon.# +Unable to proceed due to exceeding capacity.# +Real name has not been verified. Go to name verification site.# +Please select slot you are going to save.# +Congratulation %s, Acquired '%s' !# +Unable to use in gloomy state# +Purchased products has exceeded the total price.# +Cannot join a party in this map.# +Cannot leave a party in this map.# +Cannot withdraw/break the party in this map.# +Real Name# +ID Number# +E-mail# +Invalid input# +Failed to send the zeny # +This is not a relevant job# +This is not a relevant gender# +User information identification was successful.# +Name does not match. Please retry.# +ID number does not match. Please retry.# +Service is currently unavailable. Please try again later.# +Unable to attack while riding.# +Unable to cast the skill while riding.# +Pin number should be 4~6 characters.# +Secured authentication is successful.# +Succeeded in creating 2nd password.# +2nd password has been deleted.# +2nd password has been corrected.# +Password is incorrect.# +Failed to create 2nd password.# +Failed to delete 2nd password.# +Failed to correct 2nd password.# +Unable to use restricted number in 2nd password.# +Unable to use your KSSN number.# +~There is already a password.# +Security Code# +Account for the additional password security settings are recommended.# +Do not use secure password.# +Use the set security password failed.# +Use secure passwords. Will be applied to your next login.# +Use the set security password failed.# +Added to the security of your account password is set.# +Use the mouse to enter the 4-digit password below.# +Typing an incorrect password 3 times will shut down the client.# +ITEM# +SKILL# +TACTIC# +ETC# +COMBAT# +NON-COMBAT# +BUFF# +AUTO EQUIPED# +1st. ATTACK# +ATTACK# +Next attack time: # +When died# +When invited to a party# +Pickup Item# +Over 85% Weight# +Any work in progress (NPC dialog, manufacturing ...) quit and try again.# +Monster Job hunting experience that you can get through the doubling of %d is %.2f minutes.# +SaveData_ExMacro %d# +Settings for [%s] are stored in.# +Security level# +The current character is a party or join the guild cannot be deleted.# +Objects can be used only near the wall.# +%s : Level %d %s party to obtain level.# +While boarding reins is not available for items.# +This skill requires 1% experience.# +E X P rate : # +Drop rate : # +Death Penalty: # +%d%% (Internet cafe %d%% + TPLUS %d%% + Premium %d%% + %s Server %d%%)# +Amount of party members to cast the skill Chorus SP is low.# +Relative character that has possession of the items cannot trade because amount is exceeded..# +Relative character that has possession of the item amount which exceeds makes it impossible to trade.# +Amounts are exceeded the possession of the item is not available for purchase.# +Advertising is pending registration.# +With the following files and text content Ragnarok Official Website -> Support -> Contact Us to submit your comments by:# +Has caused an error in billing system(%d)# +Failed purchase of runes, items exceed the maximum number that can be held.# +Exceeded the number of individual items, purchase failed.# +Purchase failed due to an unknown error.# +Please try again later.# +Kunai must be equipped to use this skill..# +Please enter the value of the minimum level to be recruited.# +Jonda agency receipt of the item to the NPC is not possible. Gaining possession of the free space of the window.# +This skill is only available during WoE.# +This skill is available only to the player.# +Can't equip this item because you don't meet the requirements.# +Current location of the shop and chat room creation is disabled.# +Elapsed time: %d:%d:%d / %d:%d:%d# +Speed: X 1/4# +Speed: X 1/2# +Speed: X 1 # +Speed: X 2 # +Speed: X 4 # +Speed: X 8 # +Speed: X 16 # +Speed: Unknown# +Service Info: %s# +Character Name: %s# +Map Name: %s# +Record Time: %d-%01d-%01d %d: %02d: %02d# +Play Time: %02d: %02d: %02d# +No Replay File.# +Server No Matching# +Replay Option Setting# +Enter File Name# +Set Replay Save Data# +Set Rec Option# +%.1f %% Pos->:%d:%d:%d# +%.1f %% Pos->:cannot move# +Start# +Stop# +Input FileName -> Start# +Open Option# +Close Option# +End# +Time# +Party & Friends# +Chat# +Shortcuts# +Automatic filename generation# +Checking for duplicate files# +The same file exists already.# +Record Start# + is Saved.# +Weight: %3d / %3d# +Total: %s Coin# +[Shuriken] must be equipped.# +Base Lv. %d# +Job Lv. %d# +Zeny: %s# +Trilinear# +attack# +skill# +item# +NoCtrl# +Battleground# +(Character/Total Slot)# +Premium Service# +Premium # +Service# +Billing Service# +Billing # +Command List# +LEVEL# +MAP# +JOB# +Not Available# +[Protection of Guardian Angel] You can't use it when you reach the highest level.# +Do you really want to move?# +Failed to move Char slot.# +Character name is invalid.# +Show Quest# +Depending on the protection of youth, and 0:00 to 6:00 while under the age of 16 of your game use is limited.# +Depending on the protection of youth, 0:00 to 6:00 ^ff0000 under the age of 16 ^000000 limit your use of the game and the game ends.# +In order to change the character name, you must leave the guild.# +In order to change the character name, you must leave the party.# +Character name change failed, due an unknown error.# +Ready to change character slot in.(%d)# +Ready to change character name in.(%d)# +Length exceeds the maximum size of the character name you want to change.# +Name contains invalid characters. Character name change failed.# +The name change is prohibited. Character name change failed.# +Complete# +For %d minutes your Exp will increase by %d%%.# +%02d seconds left until summon.# +Your party leader summons you to %s (%s). Warp costs %d Zeny.# +Summon target# +Block List# +%d Zeny will be spent for making party ad.# +Insufficient Zeny for making party ad.# +) party: accept invitation# +) party: decline invitation# +) party: show equipment window# +Up to 36 english letters can be entered# +Enter# +1:1 Chat# +Block# +Insufficient Zeny for recall.# +Input your party ad.# +Only party leader can register party ad.# +You have already accepted this ad.# +For# +Eqp# +Fav# + Lock item drop# +Party Alarm# +Create Party# +Leave Party# +Party Invitation# +Party Name:# +Player Name:# + has received an invitation to join your party.# + rejected your party invitation.# + accepted your party invitation.# +Recruitment is already a party.# +Same conditions such as the previous search.# +Guild after withdrawl.# +Party after secession.# +The player cannot be summoned to this map.# +Party Leader is on a map that is restricted to summon players.# +Summon has been denied.# +Cannot be summoned.# +Only the leader can invite.# +Search item:# +You must enter a character name.# +You must enter the name of the party.# +Guild Companion# +Join a guild or start your own!# +Create Guild# +Guild Name# +Guild System# +What is the guild system# +You must enter the name of your guild.# +Supported at the party was rejected.# +Select Service:# +Possible escape area.# +Replay File List# +File info# +File List# +%s Item deal not possible.# +Disband the Guild# +Enter Guild Name# +The character is not online or does not exist.# +Failed to call Falcon.# +%d%%(default 100%%+ Premium%d%%+%s Server%d%%)# +This user is currently participating in WoE.# +It is only possible to change the party leader while on the same map.# +In the current region it is not possible to change the party.# +Dismount from Gryphon# +Delete: %d/%d - %d:%d:%d# +You can't invite characters in WoE maps.# +You are now in the battlefield queue.# +Queuing has finished.# +Invalid name of the battlefield.# +Invalid type of application.# +People count exceeded.# +Your level doesn't fit this battlefield rules.# +Duplicate application.# +After reconnecting, please re-apply.# +Your class can't participate in this battlefield.# +Only party leader / guild master can apply.# +You can't apply while your team member is already on a battlefield.# +You have left the battlefield queue.# +Wrong battlefield name.# +You are not in the battlefield queue list# +The selected arena is unavailable; your application has been cancelled# +You have left the queue# +Are you sure you want to join a battleground?# +[Battlefield application rules]# +Application and position into the battlefield cannot be applied under this circumtances# +1. Different types of battle cannot be applied simultaneously.# +2. Personal / party / guild battle cannot be applied simultaneously.# +3. Parties can only be applied by their party leaders.# +Offline party members won't proceed to the queue.# +4. You can add request to enter the arena from any map except for those who don't allow teleport/warp.# +When the battle is finished your character will be returned to the current spot or (if it's not possible) to the save point.# +5. You can view and choose rewards in the arena waiting room.# +Request help battle position# +%s battle begins.# +Do you want to enter the arena?# +[Note]# +When the battle is finished your character will# +be returned to the current spot or (if it's not# +possible) to the save point.# +Waiting for the opponents.# +Battlefield position request# +Accept standby time:%d seconds# +Standby position# +Battlefield name:%s# +Persons required:%d# +Your position:%d# +Name:# +Goal:# +Format:# +Level:# +Win:# +Draw:# +Loss:# +Do you want to participate in the individuals battle?# +Do you want to participate in the parties battle?# +Do you want to participate in the guilds battle?# +Battleground List# +%d VS %d# +LV %d and lower# +LV %d and higher# +LV %d ~ %d# +No restrictions# +[You can't apply on this map.]# +[You must wait about 1 minute to apply.]# +[You must be in a party.]# +[Only party leader can apply.]# +[Too many party members online.]# +[You must be in a guild.]# +[Only guild master can apply.]# +[Too many guild members online.]# +Moving Book# +Move# +Rename# +Create Character# +http://ro.game.gnjoy.com/# +(%s) Server# +Item Merge# +Two or more of the same type. Please select an item.# +Item merge is successful.# +Combining items will be only one kind at a time.# +You cannot have more than 30,000 stacked items.# +Rotate left# +Rotate right# +(%s) to view the old server information# +Existing server information# +^ff0000Existing server: ^0000ff# +^ff0000Existing character: ^0000ff# +Show monster HP bar when attacking.# +Hide monster HP bar when attacking.# +Merge does not exist as an item# +Merge items available does not exist.# +Act# +Pen# +Rec# +Epi# +Loc# +Evt# +New# +Target monsters# +Rewards# +Required Items# +Time Limit# +Deadline# +Search# +Navigation# +Back to Navigation# +View List# +Toggle Minimap# +Read external file path information# +Exit# +Change to basic UI# +Change to simple UI# +Help# +All# +Map# +Npc# +Mob# +Enter search string... (Ex: word word ...# +Scroll# +Use Scroll?# +Service# +Use Kafra Warp?# +Plane# +Use Airship?# +>> Failed to read the target information.# +>> Destination <<# +<< Goal... >># +-----------# +Navigation# += search result (%d) ==# +Npc)%s:%s# +Mob)%s:%s# +Map)%s# +======== Results ==========# +Dist %d Cell %d WarpMove# +Coords %s(%s)# +Goal:%s (%d,%d)# +Boss# +General# +Goal :# +Goal: (%d, %d)# +======= Guidance =======# +%2d) Item:%s => %s Use!# +%2d) %s(%d,%d)=>(%d,%d)# +E%2d) %s(%d,%d)=>(%d,%d)# +E%2d) %s# +Do you want to cancel navigation?# +How to Use Navigation# +------------------- Instruction --------------------# +1) /Navigation or /navi# +ex) /navi prontera 100 100 -> /navi 'MAPNAME', 100, 100# +Basic command is focussed on searching Zeny, Flight Machine.# +2) /Navigation2 or /navi2 # +ex) /navi2 prontera 100 111# +-> MAPNAME location (100 90), Scroll | Zeny | Plane (1: Enable or 0: Disable)# +3) $$all Output all the items (Can take a while...) # +4) $$lv30 monsters are placed in the output # +5) $$lv20~30 monsters in that level range are placed in the output # +------------------- Description --------------------# +1) One can search for monsters, npcs, maps, or all at once# +2) You can press the search button to get results. It will out put the results depending on what rule you choose# +ex) Drop down box -> Select 'Npc', then type in the box 'Kafra'. Results will now be displayed# +3) When you select an item from a list, information about it are displayed.# +-> When button is clicked, it will point you towards your destination if available# +4) Scroll | Zeny | Plane options can be checked to find a faster route# +5) Guide button is pressed, the result list window displays where routes can change direction# +6) Using the button below, search results can be found# +-> [Results List Window] <-> [View Modes can be switched]# +Level:%d (Boss)# +Level:%d (Mob)# +Water %d# +Earth %d# +Fire %d# +Wind %d# +Poison %d# +Holy %d# +Shadow %d# +Ghost %d# +Undead %d# +Neutral %d# +Medium# +Large# +Small# +Undead# +Brute# +Plant# +Insect# +Fish# +Demon# +Demihuman# +Angel# +Dragon# +Formless# +Click to move %s# +Move to the Kafra Service Npc# +Click the NPC# +Move %s# +Move to the Airship Service# +By Warp# +End Points: (%d %d)# +That does not support the navigation area# +The purpose is unclear# +Does not meet the map requirement# +Information Failure | Change settings# +Failed to set info for location!# +Failed to find a path# +Failed to find players # +No Information# +Map doesn't support directions# +Please specify target goals.# +Found# +Directions were started# +Is the map that you're looking for mob# +Map appears on the guide you are looking for# +Please navigate using the item# +To find the location, follow the arrows# +Arrived at the target map# +Arrived on the map that has the Npc you're looking for. Follow the arrows to go to that NPC.# +You have arrived at the mob you were looking for# +You have reached your goal# +Please go to indicated direction.# +The goal has been reached# +Navigation >: %s# +Navigation >: Talk to '%s'# +Navigation >: Please move to '%s' map# +Navigation >: Then select '%s'# +Navigation >: Please move to the Airship# +Navigation >: Use the Warp Portal to move to the next area.# +Item: # +$$# +$$lv# +~# +$$all# +Confirm Deal# +Below is the total cost:# + Zeny# +Press buy to confirm.# +%.1f%% (Internet cafe %.1f%% + TPLUS %.1f%% + Premium %.1f%% + %s Server %.1f%%)# +Card Book# +%d%% [ ( Basic 100%% + %s Server %d%% ) * Active %.1f ]# +%d%% [ Basic 100%% + %s Server %d%% ]# +This is PK region. Minors,Please leave immediately.# +Fatigue# +Health and gaming revenue is 100%.# +Fatigue because it is now a guest of the gaming revenue is down 50 percent.Hope for the proper health# +Now because it is a non-health to the health of the guests want to offline games. If you still are online gaming revenue because the damage to the health of the game falls to 0%% again after 5 hours will be restored offline.# +Online since %d minutes# +Online Time: %d# +Online since %d hours and %d minutes# +/monsterhp : Show the hp of attacked monster. On off# +Skill points : # +There is no response from the authentification server. Please try again# +Please change your password# +http://www.ragnarok.co.kr# +Guest access is prohibited# +Your System is been Shutdown, %1.2d-%1.2d-%1.2d %1.2d:%1.2d:%1.2d is the end time.# +Selected System Shutdown is activated in your account,Time Left: %1.2d hours %1.2d minutes.# +Replay# +Macro# +Webbrowser# +Navigation# +UAEURL# +Clan Info# +Clan Level# +Clan Name# +Clan Emblem# +Ally Clan# +Hostile Clan# +Send to Clan# +Clan Leader# +Number of Members# +Castles Owned# +Clan chat# +Go to Page Charged.# +https://gfb.gameflier.com/Billing/ingame/index_new.asp?# +Create Character# +Name does not match# +Enter the name of character# +Sex Selection Window# +Editing of the File Detected# +Items obtained by opening the item is character bounded (cannot move to storage). Do you want to open the box?# +Game Settings# +Game System# +Game Commands# +Game Command ON/OFF# +Macro# +Trading is prohibited in this Map# +Using cart is prohibited in this Map# +In this Map,Effect of Mirace of Sun and Moon is nullified.# +Ranking Board# +Rank# +Name# +Points# +BlackSmith# +Alchemist# +Taekwon# +Killer# +7 vs 7# +RuneKnight# +Warlock# +Ranger# +Mechanic# +GuillotineCross# +Archbishop# +RoyalGuard# +Sorcerer# +Minstrel# +Wanderer# +Geneticist# +ShadowChaser# +Sura# +Kagerou# +Oboro# +Select Ranking Type# +Ranking Type# +Currently,Server is full. ^0000ffPeople Currently Waiting : %d Expected Waiting Time : %dSeconds# +Currently,Server is full. ^0000ffPeople Currently Waiting : %d Expected Waiting Time : %dMinutes %d Seconds# +CBT is not an invited user# +------------------- Instruction --------------------# +1) /Navigation or /navi ex) /navi prontera 100 100 -> /navi 'MAPNAME', 100, 100# +2) /Navigation2 or /navi2 ex) /navi2 prontera 100 111 -> MAPNAME location (100 90), Scroll | Zeny | Plane (1: Enable or 0: Disable)# +-> /navi2 goes with the case with location coordinates. They must be no less than 3 characters # +3) $$all Output all the items (Can take a while...) # +4) $$lv30 monsters are placed in the output # +5) $$lv20~30 monsters in that level range are placed in the output # +1 vs 1# +Costume# +%d First character of the profession is more than information. Please contact the Customer Care Center. ErrorCode(%d)# +(%s) %d / %d# +%s-%s(%d/%d)# +Server Exceeded the maximum number of users,Cannot Connect anymore users.# +Server Connection Failed (%d)# +Login Timeout Permitted# +Login Authentication Failed from Authentication Server.# +Guild Cannot use Space in the name.# +Hey,Hello There# +Available Time will End on %d month %d hour %d:%d# +You've lot of time,Play in Peace.# +Your hours will be terminated within this week. Please Charge before termination.# +Your hours will be terminated within 24 hours.Please Charge Quickly.# +Current Time Left:%d hours.Charge the game for uninterrupted play.# +Current Time Left:%d minutes.Charge the game for uninterrupted play.# +Time Left: %d hours %d minutes# +%d%% ( Basic 100%% + Internet cafe %d%% + Premium %d%% + %s Server %d%% )# +After %d hours %d minutes, your game will be terminated.# +This Account is permanently Banned.# +This Account is banned.\nTermination Time:%04d-%02d-%02d %02d:%02d # +Mob(Tab)# +Map(Alt)# +Product Information# +Find Information# +Airship# +Kafra# +(Arrival)# +Mob)%s:%s(%s)# +Distribution:%s# +Very High# +High# +Average# +Low# +Very Low# +The bank is not available. Please try again in a few minutes.# +Bank balance is low.# +You don't have enough zeny# +Minimum Deposit Amount: 1 zeny# +Minimum Withdrawal Amount: 1 zeny# +You cannot hold more than 2,147,483,647 Zeny# +your account is lock by mobile otp# +MOTP auth fail# +For %d minutes, Job experience obtained from monster is increased by %d%%.# +Current Zeny: %s Zeny# +Zeny# +The Maximum amount is 2,147,483,647 Zeny# +Insufficient bullet# +You entered more than 1 Billion Zeny, the price will be set to 1 Billion Zeny.# +AuthTicket is Not Valid# +ErrorCategory : %d, ErrorCode : %d (%d,%d,%d,%d)# +%d%% ( Basic 100%% + Premium %d%% + Internet cafe %d%% + %s Server %d%% )# +For %d minutes, item drop rate from defeating monster is increased by %d%%.# +For %d minutes %.2d, item drop rate from defeating monster is increased by %d%%.# +The price of^0000FF %s^000000# +100000000# + is over ^FF0000%d00^0000FF million^000000 zeny and# +10000000# + more than^FF0000 %d0^0000FF million^000000 zeny# +. Is it correct?# +Safe check for over 10 mil zeny# +https://www.warpportal.com/account/login.aspx?ReturnUrl=%2faccount%2fpayment.aspx# +https://kepler.warpportal.com/ro1/purchase/?step=1&steamid=%lld&accountname=# +A giant crevice appeared in Bifrost, the bridge between Splendide, the end of the world and the floating continent of Alfheim, and you do not know the source of the labyrinth forest.# +This is a marker indicating the end of the trip, a new world is opening indicators! Guardian, such as the lyrics to the temptation was gradually losing the soul.# +For thousands of years, a mysterious melody has mesmerized the guardian. After a millennia of slumber, the guardian became confused about what he had been protecting all these years, and began to suspect that he might be the one who has been sealed and hidden away.# +When the melody reached its peak, a giant crevice appeared in Bifrost, the bridge between Splendide, the end of the world and the floating continent of Alfheim. As a result, the two worlds were cut off from each other, causing a big problem for the people.# +Now, the only way to get to Bifrost is through the Labyrinth Forest. Nobody knows how the forest came to exist, and nobody has ever come out of it alive...# +Swallowed countless adventurers to put a hell of confusion, wandering in the forest labyrinth of nowhere, like the heart of a woman was being extend deeper confusion.# +You can enter only numbers.# +Exchange or store window is active and cannot register the withdrawal.# +Go to# +Compare# +This Weapon is not suitable for the bullet you are equipping.# +This bullet is not suitable for the weapon you are equipping.# +This item you want to sell is not registered. Please register the item first.# +ITEM# +Guild storage is not available.# +You are not joining any guild. Please try again after joining a guild.# +Other guild member is using it. Please try againt later.# +Strg Permit# +Guild Storage# +You do not have permission to use guild storage.# +Limited Sale Registration Window# +Item DB Name# +Item DB Number# +Number of Sale# +Sale Start Time# +Time to sell# +Please enter amount you want to sell# +Enter start time of sale# +Start time does not match the scope of sales.# +Please enter the time# +Please enter the Item DB Name# +Item ID lookup failed. Please try again later# +>> ItemName : %s / Price : %dc / Quantity : %d / TimeOfSale : %dMonth:%dDay:%dHour:%dMinute ~ %dMonth:%dDay:%dHour:%dMinute# +Registration successful# +Registration failure. Please try again later.# +Item has already been registered. Please try again later.# +Failed to remove this item. Please try again later.# +%s item has been removed.# +Special# +Update quantity of limited sale items# +Sales closed# +item count should be renewed# +Item stock is low. You can only buy %d items.# +%s Items are on sale# +%s time-out or sale of the items sold has been shut down due to the limited sales # +/limitedsale# +http://www.ragnarokeurope.com/news/home-r70.html# +http://www.ragnarokeurope.com/index.php?rubrique=70&Steam# +Item purchase successful# +You do not have enough items.# +Character with that name is not possible to invite.# +Character with that name is not possbile to invite to the guild.# +Unable to purchase, this item is out of stock.# +Cannot save location as a Memo Point at current location.# +Cannot purchase item, You exceeded the weight limit.# +No.# +Item# +Number# +Name# +Time# +Input/Output# +Log does not exist.# +Entire# +Insert Symbol# +Remaining Time# +Guild Storage Logs# +100 entries are displayed.# +Guild Storage is open only for 2 minutes, and will close automatically.# +Incorrect GameSamba ID. Please Try Again.# +%s [%d Option] : %d ea.# +Requires a Coin.# +%d Coins are required.# +[Gatling Gun] weapon class must be equipped.# +[Shotgun] weapon class must be equipped.# +[Rifle] weapon class must be equipped.# +[Revolver] weapon class must be equipped.# +[Silver Bullet] must be equipped.# +Item Sell History# +Item Purchase History# +[Grenade Launcher] weapon class must be equipped.# +[Heat Barrel], [Madness Canceller] and [Platinum Alter] cannot be used while sitting.# +Cannot use skill in this map.# +If you are using a guild storage, all items inside it will disappear.# +http://ragnarok.gamesamba.com/paytest.html# +Please enter a password to login.# +Pet Evolution# +Evolution - %s# +Evolution requires the following ingredients:# +Do you want to evolve your pet?# +Unknown Error# +No pet can be summoned.# +It is not requested petal.# +Evolution material is low.# +Lack of required materials for evolution.# +Pet can only be evolved when intimacy is Loyal.# +Automatic feeding# +Your pet has been fed. %s (%d) remaining# +Automatic feeding is ON# +Automatic feeding is OFF# +Update# +%d minutes ago# +%d hours ago# +%d days ago# +%d%% ( Premium %d%% + %s Server )# +Mailed# +Your mail has been sent.# +This item has been moved to the inventory.# +Failed to get items.# +Please empty your inventory.# +Zeny obtained.# +Failed to get zeny.# +Exceeded the limit of zeny.# +Item successfully attached.# +The name of the recipient must be included.# +The length of the title must be 4 to 50 characters long.# +Mail delivery failed.# +Transmission has failed to inappropriate items.# +Information of the recipient does not exist.# +%s [%d option]# +Level, enter a number between 1 and %d.# +Please free the window possessing space.# +Sending mail count exceeded.# +You cannot open the mail.# +You currently belong to a clan.# +Internet cafe Gold mileage information# +Unequip pet accessory first to begin pet evolution# +in %d minutes# +in %d hours# +in %d days# +Press the OK button to confirm the C-CODE.# +Please claim all attachments before deleting your mail.# +You cannot send any more zeny.# +Fee: %s Zeny# +The recipient's name does not exist.# +E X P : %.1f%% ( basic %.1f%% premium %.1f%% + %s %.1f%%)# +DROP : %.1f%% ( basic %.1f%% premium %.1f%% + %s %.1f%%)# +DEATH : %.1f%% ( basic %.1f%% premium %.1f%% + %s %.1f%%)# +You can accumulate points of Internet cafe Gold up to %d points maximum.# +This function cannot be used in this server.# +Imposible to use in server.# +The auction is not available in server.# +The deal is imposible in server.# +Item discard is imposible in server.# +Store selling feature is not available.# +The SP's summoned enough.# +%s/%s# +The maximum length of the message content is 1K.# +Failed to attach an item.# +The weight of the items that can be attached to mail has been exceeded.# +Is already in service. Please try again in a few minutes.# +Unable to open the window of Lucky Roulette.# +Unable to close the window of the Lucky Roulette.# +You cannot start a roulette wheel of fortune.# +Points is required to play Lucky Roulette.# +You cannot receive a winning items.# +The number of items in the inventory has been exceeded. # +The weight has been exceeded. Please free up the possesion window.# +Failed to open stalls.# +The roulette wheel is spinning. Please try again after checking with prizes.# +Lucky Roulette# +Notify when item sells out# +Please check the fees.# +Verify user name# +Schedule deletion# +Achievement Challenges# +Achievement score# +Achievement rank# +[%5d] points to the next rank# +Progress# +Recent completed achievements# +Cracker Item# +Cracker : %d / %d # +Cracker is low.# +%s # +Summary # +General # +Character # +Action # +Other # +Adventure # +Rune Midgarts # +Schwartzvald # +Arunafeltz # +New World # +Localizing # +Dungeon # +Battle # +Duel # +Training # +Quest # +Episode # +General # +Memorial # +Midgard # +New World # +Other # +Feat # +Achieved challenges# +You cannot add anymore.# +< %s > achieved.# +only otp user login allow# +When this button pressed, you will receive a specified initialized roulette item.# +When this button pressed, the flashing arrows from the lines of the roulette wheel begins.# +Title# +Disable Title# +Can't create a Guild in this area.# +Can't disband a Guild in this area.# +Can't join a Guild in this area.# +Can't leave a Guild in this area.# +Can't expel Guild member in this area.# +Can't change a Guild title in this area# +Please empty at least 5 amount of possession in item window.# +E X P : %.1f%% ( basic 100.0%% Internet cafe %.1f%% + %s %.1f%% )# +DROP : %.1f%% ( basic 100.0%% Internet cafe %.1f%% + %s %.1f%% )# +DEATH : %.1f%% ( basic 100.0%% Internet cafe %.1f%% + %s %.1f%% )# +The number of items that can be attached is exceeded.# +Notify when item purchased# +Unable to attach the items.# +Because you cannot win a slam room and turn the roulette of the next higher step.# +Write reply# +Read mail# +Delete mail# +Crackers : %d# +%s %s crackers # +Crackers # +Total : %d Crackers # +%d -> %s Crackers # +%s Crackers # + Current Crackers : %s# + %s : %s Crackers# +%10s Crackers# +Total : %s Crackers# +* Commission 3% / Minimum 1 Crackers comission deducted# +The Crackers.# +^0000ff more than crackers^000000Did you enter the correct amount?# +Over than 10billions secure check# +If you fill over 1 billion, the price will be automatically set as 10 billion Crackers.# +If you fill over 9,999 Crackers, the price will automatically set as 9,999 Crackers.# +There are items priced 0 Crackers. You cannot open vending.# +The player have over the Crackers limit. Trade failed.# +Please fill out %s item price less than 1 billion Crackers.# +%s : %s Crackers => %s ea.# +Limited price : %s Crackers# +%s %s Crackers %d# +%s %d are sold. In total %d Crackers . 3%% charge will be imposed.# +RODEX# +SEND# +RECEIVE# +Please close the mail compose window.# +Server access denied(A)# +Server access denied(B)# +You need to unequip Arrow and Ammunition.# +Consuming item in inventory will close mail compose window.# +You don't have any Magnifier.# +Close email window if you want to discard item.# +The player has already requested you to be a friend.# +SCRIPT ERROR AID# +\nErrorfile : %s\nErrorLine : %d line\nErrorContent: \n*before line : %s \n*Errorline : %s \n*next line:\n %s# +^ff0000Do you want to purchase the item? after confirmation will consume %d point and %d crackers.# +Close# +Quest# +Show Quest icon# +Facility# +Show Facility icon# +Guild/Party# +Show Guild/Party# +Memorize location# +Boss Monster# +Me# +Do you want to delete it?# +Quest NPC# +Input location name# +Save location# +Memorize location# +%s [%d ea.] %d ea.# +%s [%d ea.] %s Zeny# +%s [%d ea.] %d %s %d -> %s %s# +%s [%d ea.] %d %s %s %s# +%s [%d ea.] %d -> %s %s# +%s [%d ea.] %s %s# +The minimum trading unit must be 100 crackers or more.# +Hair color# +Hair style# +Extra colors# +Extra costumes# +In this map You can't memorize and zoom the minimap.# +Enter zeny below 2,147,483,647.# +Please enter at least 1 zeny.# +You don't have any zeny.# +Zeny Storage# +in bank# +on hand# +Deposit# +Withdraw# +1 z UP# +1 z Down# +Max# +There is no Input value# +Insufficient zeny# +Input Required# +Numbers only# +Exceeded max input# +Below Minimum Input# +Insufficient zeny# +Insufficient funds# +Exceeded max zeny# +Can't be dropped# +Can't be put in Storage# +Can't be put in Cart# +Can't be attached in Mail# +Can't be traded with player# +Can't be auctioned# +Can't be put in Guild Storage# +Can't be sold to NPC# +Item move restrictions# +Click here to create a Character# +Please unequip ammunition first.# +Unable to enter, due to exceeding number of players.# +Access denied, due to exceeding number of players.# +%.1f%% ( %s Server: %.1f%% + Premium:%.1f%% )# +%.1f%% ( %s Server: %.1f%% + Premium:%.1f%% )# +Unable to use because there is duplicate item.# +Unable to use because there is similiar effect exist.# +Screen shake ON# +Screen shake OFF# +Illegal program detected.# +Send a mail# +E X P : %.1f%% ( basic 100.0%% VIP Bonus %.1f%% + %s %.1f%%)# +DROP : %.1f%% ( basic 100.0%% VIP Bonus %.1f%% + %s %.1f%%)# +DEATH : %.1f%% ( basic 100.0%% VIP Bonus %.1f%% + %s %.1f%%)# +Name with this tag cannot be used.# +Style Shop# +No serial number, Please visit the store to buy.# +Please open the item coupon box in the inventory.# +Bank# +Slot is full, please delete a character.# +Item cannot be restored, are you sure want to apply it?# +The current style is saved.# +There are plenty of zeny in the bank, Please go to the bank now.# +moving...# +You are not allowed to fly over the sovereign airspace of the country.# +Airship flight is temporarily unavailable due to atmospheric instability caused by magic.# +Private airship# +Please try again in a moment.# +Not enough Zeny to use the private airship.# +Not enough item to use the private airship.# +You don't meet the level to use the private airship.# +You cannot move to the selected point by the private airship.# +You cannot use the private airship from your current location.# +Accessory# +Second costume# +Wardrobe# +This hairstyle cannot be dyed.# +Press an arrow to choose the style you want.# +Come and see the new styles.\n(Purchased accessories will be sent via RODEX.)# +Basic style# +You can level up the skill from the base Lv. %d.# +Special symbol can't be used in name# +MaxHP is too low to use this skill# +MaxSP is too low to use this skill# +Compare equips# +Lock item drop# +Screenshot is not attached# +TWITTER# +General# +Notice# +Return# +Refresh# +%d minute# +%d hour# +%d day# +Returned# +Do you want to delete the message?# +Show information# +Zoom in# +Zoom out# +Maximize# +Show world map# +Total possession item type# +/minimap# +Show minimap buttons# +Hide minimap buttons# +Show guild member login status# +^0000CCIntimacy:^000000 %s# +Unrecognized# +Team name# +Leader# +Time limit exceeded. Closing the client# +Input error. Closing the client# +Incorrect input. A small buff has given to you# +Enter 4 english words and 2 chinese words# +Your entered answer is [%s]. Is it right?# +Icon is unchecked# +Incorrect input (Remaining chance: %d)# +Do you want to announce %d to player?# +Message has been sent to player# +Needed Data for Query illegal software are not signed.# +The player is being monitored.# +Already signed in investigation system.# +Failed to store icon.# +Failed to store replied answer.# +Hello, illegal software is being monitored.# +Please enter the text below within the specified time.# +According to game regulation, when you enter the wrong text three times, you will get banned.# +Remaining chance : %d# +Role# +Range# +You use the left mouse button to specify the role# +You use the left mouse button to specify the range# +Not a valid range, please press Enter# +E X P : %.1f%% ( basic %.1f%% %s %.1f%%)# +DROP : %.1f%% ( basic %.1f%% %s %.1f%%)# +DEATH : %.1f%% ( basic %.1f%% %s %.1f%%)# +The character name will be changed: %s # +Synthesis of the required materials# +Insufficient synthesis materials# +Required material %d ea.# +Refine value is to low for synthesis# +Do you want to sign in to the adventurers?# +Please leave a message# +Please enter the text on the graph# +%d second left# +Please enter your 6 identification number.# +Identification number.# +It's not a 6 identification number. Please try again# +The message has been deleted# +Please select the area where the private airship will move# +E X P : %d%% ( basic 100.0%% %s %d%%)# +DROP : %d%% ( basic 100.0%% %s %d%%)# +DEATH : %d%% ( basic 100.0%% %s %d%%)# +Move to the destination# +The [%s] is not present, the default AI will be used instead.# +%.1f%% ( Basic 100.0%% + Premium %.1f%% + %s %.1f%%)# +Would you like to open a shop at this location?# +Doram race can't wear this clothes# +Street vending sales will be traded on RODEX# +30,000 z# +Malangdo Special Can 100 pc# +Cannot carry anymore because weight limit is over 80%# +Unsold items are sent to RODEX# +Wear costume# +Assign Guild Leader# +Are sure want to assign %s as guild leader? After assigned your position will become %s# +Cancel# +Do you want to close the shop?# +%02d day %02d hour %02d min %02d sec# +Unable to sign in# +Unable to sign in because the maximum number of sign-in is exceeded# +Please select a location for your shop# +Pet name will be changed to ^0000ff^0000ff %s^000000^000000, do you want to continue?# +Trade date : # +Trade item : # +Trade quantity : # +Trade price : # +Total trade : # +Open vending sales agent# +Open vending purchase agent# +Close vending sales agent# +Close vending purchase agent# +Balance: # +Item list: # +Vending agent# +Returned item: # +Returned quantity: # +Returned date: # +Total trade will be sent to RODEX # +Name isn't available# +Item has been delivered# +Thank you for purchasing# +NPC sale lock applied# +NPC sale lock# +There is no callable location nearby# +Capture full screens# +Capture part of screen# +Send# +Family Affairs agent# +Acknowledgement of family member registrations.# +Dear whom it may concern. \r\n\r\n Today, \"%s\" and \"%s\" 's son/daughter \"%s\" has fully separated and independent from your member of family.\r\n\r\nHereat, we inform you in writing.\r\n\r\nPlease contact to Prontera Family Affairs if you have any enquiries.\r\n\r\nThank you.# +Equip# +No image# +[%s] is blocking Call Massage.# +Currently in WoE hours, unable to delegate Guild leader# +You have to wait for one day before delegating a new Guild leader# +When adopted, character will not able to transcend, maximum stats will be limited, MaxHP and MaxSP will be reduced.\nAre you sure you want to continue?# +Refine# +Use Blacksmith's Blessing# +Insufficient zeny# +Not enough Blacksmith's Blessing# +This equipment can not be refined# +Upgrade success!# +Upgrade failed!# +Back# +Success# +Return# +With Mr/Miss# +The Homunculus's name will be changed to^0000ff^0000ff %s^000000^000000, Are you sure?# +Call function is ON# +Call function is OFF# +Display Call messages# +This character is currently opening a shop and can't be deleted# +The same vend shop NPC has been set up# +Opening shop is not allowed on this location# +Opening shop is not allowed when there is other character# +Failed to feed pet, please close RODEX window# +Equipment swap# +Equip# +Equipment will dissapear when refine fails# +Equipment's refine level will be decreased when refine fails# +Equipment will dissapear or refine level will decrease when refine fails# +You cannot use RODEX while refining. RODEX has been closed.# +You cannot use RODEX while refining.# +Please close other windows to continue.# +Aura effect is ON# +Aura effect is OFF# +Aura effect is OFF. Please turn it ON and try again# +You need %d bullet(s) to use the skill.# +Do you want to swap the equipment?# +You can not do it while KO'ed.# +You can not do it while casting# +You can not do it while trading# +You can not do it while opening vendor# +You can not do it while talking with NPC# +You can not do it while opening chat room# +Party members are not connected.# +You need %d arrow(s) to use the skill.# +Align items# +To discard item, please close equipment swap window.# +There is no item to replace.# +Deleted character# +%y.%m.%d# +Access date: %s# +You can not use bank while refining. Bank has been closed.# +You can not use bank while refining.# +This item has been set for equipment swap.# +Human# +Swordman, Mage, Merchant, Acolyte, Thief, Archer, Expanded Classes# +Doram# +Summoner# +Dominant race of Rune Midgard. Unlimited potential. Excellent adaptability to solve problems.# +Race of Pasta continent with natural born curiosity and sparkie character.# +Possession limit is over 70%, or you have less than 10 free inventory space.# +C# +C# +Another work is in progress.# +Overheat limit : %d# +You can't invite or withdraw while in Memorial dungeon.# +Profanity detected.\nPlease check again.# +Please enter at least %d characters. If you don't have account, please click [Register] button to create account.# +Please enter at least %d characters.# +A work is in progress, please try again.# +%.1f%% (PCcafe %.1f%% + TPLUS %.1f%% + %s Server %.1f%%)# +Unable to register item# +/100# +/2000# +https://member.gnjoy.com.tw/mRO_SecPwd.aspx# +The selected emblem isn't available, try placing it in 'emblem' folder then try again# +New Ragnarok Online# +Please obtain permission first from other user that appears in screenshot or chat room# +Unable to swap equipment# +Loading the player's name# +The message contains unrecognizable character# +Purchase failed# +ID card is incorrect# +Close# +Rules# +Buy 1 ea.# +Buy 10 ea.# +Buy 100 ea.# +1st prize# +2nd prize# +3rd prize# +Currently unused# +Currently unused# +Zeny Lotto Winners# +Lottery purchase amount# +Participate in role# +1st prize# +2nd prize# +3rd prize# +Purchase reward items# +^1a1a1a1 ^b%d ^/b Lottery will spend \n^4435b2%d zeny^1a1a1a\nBuy it?# +Lottery purchase success# +Insufficient Zeny to purchase lottery ticket# +Maximum amount of purchase is 1000# +Yes# +No# +HP# +SP# +Lv# +Lv# +Exp# +all on# +Play Replay File# +P# +Total# +Basicinfo# +Equip# +Item# +Skill# +Guild# +Party# +Chatting# +Shortcut# +Status# +ALL# +User defined file name# +Repeated File Check# +on# +<Basic Skin># +Select Skin# +Unable to delete lottery ticket# +/achievement# +Zeny lottery# +%d Zeny lottery prize# +%d bonus prize: %s# +Total Zeny reward# +Total %d reward: %s# +Purchase Zeny# +Refund amount: %d Zeny\r\nRefund reason: Server failed to draw lottery# +Add small party window# +Remove small party window# +Lottery drawing is over, please verify if you've won the prize.# +%d month %d day# +Until next level# +Complete# +Incomplete# +(Complete!)# +(Incomplete)# +Swap Equipment# +CHANGE# +Strength: ^66cc33ATK^ffffff and ^66cc33Weight Capacity^ffffff# +Agility: ^66cc33ASPD, FLEE^ffffff and ^66cc33DEF^ffffff# +Vitality: ^66cc33MaxHP, DEF^ffffff and ^66cc33MDEF^ffffff# +Intelligence: ^66cc33MATK, Casting Time^ffffff and ^66cc33MDEF^ffffff# +Dexterity: ^66cc33Ranged ATK, HIT, MATK^ffffff and ^66cc33Casting Time^ffffff# +Luck: ^66cc33Critical, HIT, ATK, MATK^ffffff and ^66cc33Perfect Dodge^ffffff# +Physical Damage# +Physical Defense# +Hit Rate# +Critical Rate# +Affiliated Guild# +Status point# +Magical Damage# +Magical Defense# +Flee Rate# +Attack Speed# +Shortcut Description# +Effect# +Skill shortcut (F1 ~ F9)# +Screenshot# +Zoom Out# +Zoom In# +Guild# +Bank# +Mail# +Cash Shop# +Sit# +Move# +Rotate# +Party leader role must be appointed to other party member before leaving the party# +Stop watching the replay?# +Equipment have been placed on equipment swap window# +Equipment have been removed from equipment swap window# +In the last row# +%.1f%% (Basic 100%% + Bonus%.1f%%+ %s Server%.1f%% )# +Chinese# +Set shortcut# +Your monthly subscribsion will expire in %s# +Unable to find replay file# +Shoes# +Headgear# +Armor# +Garment# +Accessory# +Costume# +Sort order# +General# +Costume# +Title# +/quake : Screen shake effect ON/OFF# +/aura2 : Completly turn aura effect ON/OFF# +Basic settings# +Advanced settings# +Set basic settings# +Set basic settings?# +Set to these values?# +To apply these values, client restart is required, proceed?# +Fog# +Simplify aura# +Enable aura# +Effect# +Shadow# +No Shift# +Select hardware T&L acceleration# +Select game resolution# +Enable# +Enable# +Effect# +Control# +Graphic device# +Resolution# +Fullscreen# +Fixed mouse# +Fee : # +Total fee : # +Select receiver# +Select receiving group# +Change size(F10)# +Loading mailbox, Please don't delete the^c92114 message^000000~!!# +NOW LOADING..# +Title# +Sender# +Can't open two game windows, game installation might corrupt# +More commands# +This account does not exist.# +Password does not match.# +Failed to pass IP authentication# +No identification number, supplement your registration information# +Account block# +System error# +unknown error found.# +Navigation guide icon set# +navigation UI# +Navigation information# +Share current location# +Search result[0]# +Search & description# +Location error# +Search result[%d]# +Show navigation icon setting# +Path that uses zeny# +Open navigation window# +Account is not active# +Click on the location to share it in a chat room# +4. Select Service project, Contains use of zeny and airship# +5. [<-]ID : navigation mode<->swap search mode# +Share mode -> use it when searching# +%lld Base Experience obtained# +'%lld' Base Experience# +^b- Street vending fee description^/b: will be set according to item price, with certain percentage of fee.^b> Fee rate^/b The proportion of fee described as follows: 0 z ~ 10,000 z = Fee rate : ^1567fe0%^000000, 10,001 z ~ 100,000 z = Fee rate : ^1567fe2%^000000, 100,001 z ~ 1,000,000 z = Fee rate : ^1567fe4%^000000, 1,000,001 z ~ 10,000,000 z = Fee rate : ^1567fe6%^000000, 10,000,001 z ~ 100,000,000 z = Fee rate : ^1567fe8%^000000, more than 100,000,001 z = Fee rate : ^1567fe10%^000000# +Total amount : # +~ When selling items, total amount of zeny received # +~ When buying items, total amount of zeny reduced # +Open chat room# +Character with this level cannot join the party# +'%lld' Job Experience# +%lld Job Experience obtained# +Unable to open a shop at the current location# + seconds left before it become usable# +Cap# +SNS Failed to send(%d)# +SNS Failed to connect to server# +SNS will available after login.# +Address doesn't exist.# +Mini party windows can't be overlapped.# +My Shop# +BOX# +Tip box# +Tweet success.# +Refine# +Please return then select refine material again.# +Closed due insufficient refine material, please check again.# +Closed due insufficient zeny, please check again.# +Closed due insufficient Blacksmith's Blessing, please check again.# +This item has been destroyed.# +All stats have been reduced.# +All stats reducement have been disabled.# +OTP Password is 8 digits.# +Integrated account# +Ragnarok# +Currently service under maintenance.# +Please enter a search term# +MOTP# +Certification Number# +GNJOY MOTP downloaded to your phone, Please enter your verification number.# +There is no party member that can be delegated. Do you want to disband the party instead?# + - Age 18+# + - Paid server# + - Free server# +Smooth# +Normal# +Busy# +Crowded# +map# +The Server Storage can only store inventory items.# +You can't store Cute Pet Egg in Server Storage.# +You can't store Forged/Brewed item in Server Storage.# +%s can't be stored.# +Destroy instance# +[%s] successfully upgrades an equipment, [+%d %s] obtained.# +[%s] fails to upgrade [+%d %s].# +You can leave a party after delegation.# +Upgrade# +Downgrade# +Base# +Modified file is found. Please restart the game.# +%s can't be used in this map.# +Auto \nFeeding# +You can't feed Homunculus while the RODEX window is open.# +Homunculus has been fed. '%s' - %d remaining# +Homunculus automatic feeding is ON# +Homunculus automatic feeding is OFF# +Homunculus receives \n10% of master's experience.# +Rate : %d%%# +The target isn't in attack range vicinity. Please use 'Alt + Right Click' to command Homunculus attacks a target.# +There is no target in attack range vicinity. Please use 'Alt + Right Click' to command Homunculus attacks a target.# +Sell List# +Buy List# +New# +Popular# +Limited Sale# +Rental Equipment# +Permanent Equipment# +Scrolls# +Consumables# +Other# +Special# +Charging# +Purchase# +https://rathena.org/board/clients/donations/# +Item Search# +Free points# +Use Free points# +Owned points# +Update quantity# +Star Emperor(Girl)# +Soul Reaper(Girl)# +Star Emperor(Boy)# +Soul Ripper(Boy)# +Your weight limit is over %d, HP and SP will not recover naturally.# +The number of items that can be purchased in 8.# +https://rathena.org/board/crowdfunding/# +UNKNOWN ERROR:%d# +NOT USER# +THIS ACCOUNT ID IS BLOCKED# +COUNTRY REJECT (OR NOT AVALIABLE USER )# +NOT MATCH PASSWORD# +NOT EMAIL CERT# +PAYPAL BLOCK# +COUNTRY REJECT# +PAYPAL BLOCK# +WEB BLOCK# +AGE LIMIT USER# +PASSWORD HAS NOT BEEN CHANGED FOR MORE THAN 90DAYS# +INPUT DATA ERROR# +ERROR DATABASE# +ERROR SYSTEM# +Use %s# +Payment# +This map isn't available for restart.# +You can't teleport in this map# +Insufficient item.# +Please enter one line without line breaks.# +Names with bad words can not be registered.# +Go to the official website for membership.# +TokenAgency Server connection failed# +Billing information# +Delete reservation# +Cancel reservation# +Game start# +Delete# +Character List# +Notice# +Create# +Hair Style# +Hair Color# +Check availability# +%d hour %d min %d sec# +MOTP Input time exceeded. Please log in again from the beginning.# +Return# +You can not use items.# +Can not move to the same map.# +It is impossible to move when your character KO'ed# +Character Creation# +Adventurer agent registration# +Stop recruiting# +Adventurer Agent Settings# +All regions# +Directly# +Swordman classes# +Mage classes# +Archer classes# +Acolyte classes# +Merchant classes# +Thief classes# +Taekwon classes# +Ninja classes# +Gunslinger classes# +Doram race# +Location# +Location name search# +Request to join# +Report# +Party recruitment suspended# +Please enter a location name.# +Area# +All classes# +Login# +Account# +Exit# +Apply# +Connect# +Confirm# +GNJOY MOTP downloaded to mobile phone# +Please enter the verification code.# +NPC will move to random coondinate of the map.# +Taekwon# + is blocked.# + Failed to block.# + Failed to block.(Maximum number exceeded)# + is unblocked.# + Failed to unblock.# + Failed to unblock.(Maximum number exceeded)# +There is no block list.# +-Block list-# +Act# +Rec# +Pen# +Quest information# +Description# +Monster# +Reward# +Base Exp# +Job Exp# +Item# +Show/Hide# +Hide# +Display small quest window# +Input time# +Save ID# +Password# +ID# +Sign Up# +Novice classes# +Super Novice classes# +Failed to register adventurer's agent.# +Registered to adventurer's agent.# +Please select at least one job.# +Please enter at least two characters.# +%.1f%% (+ %s server %.1f%%)# +/ex (Character name) or /block (Character name) : Block all chats and whispers from a character# +/in (Character name) or /release (Character name) : Allow all chats and whispers from a character# +/ex or /block : Block all chats and whispers from a character# +/exall or /Blockall : Block all chats and whispers from all characters# +/inall or /Releaseall : Allow all chats and whispers from all characters# +Blocked all chats and whispers from all characters# +Failed to block all chats and whispers from all characters# +Allowed all chats and whispers from all characters# +Failed to allow all chats and whispers from all characters# +Opening# +Do you want to return to the login screen?# +STR and ATK increased.# +STR, INT, DEX and HIT increased.# +HP and DEF increased.# +Gelstar# +Account Buff# +Fixed service# +Your connection is currently delayed. You can connect again later.# +Your connection is currently delayed, Please reconnect again later.# +Name with bad words can not be searched.# +The party master cannot accept the request.# +No party can be found.# +Party request.# +http://rathena.org# +¡ã# +¡å# +You're already friends# +Can't find other party# +You're already a friend with another character.# +Sprite Marble effect is ON# +Sprite Marble effect is OFF# +This account has limited in-game access due to a secondary password mis-input. \n %02d hour %02d minute until released, To release it go to -> Change personal information -> and enter your secondary password.# +You've entered wrong password 3 times. Your account will be locked for 24 hours to secure your account.\n To release it go to -> Change personal information -> and enter your secondary password.# +Officer# +Day %d attendance reward.# +There was an error when loading the data account settings. (Please restart to retry.)# +This skill can only be used when in party.# +Party recruitment has been canceled.# +Guild experience can be accumulated up to %d%%.# +Guild level is at maximum, you can no longer contribute to guild exp.# +To keep your account secure, please login to MOTP homepage.\nPlease try to log in to the home page.# +Physical attack and magical attack have been improved.# +Physical attack and magical attack have been reduced.# +Server %s attendance reward# +Event period: month %02d day %02d - month %2d day %2d 24:00# +day %d# +Click the item to claim day %d reward# +You have claimed day %d reward# +D-day# +# +Attendance check failed. Please try again.# +Attendance check# +Currently there is no attendance check event.# +Guild experience reaches max, You can no longer accumulate Guild EXP# +Character experience reaches max, You can no longer accumulate Guild EXP# +message# +Failed to send message to twitter# +Must at least 4 characters in English, or 2 characters in Hangul.# +Monster taming is prohibited in this map.# +Fetching rank...# +This skill can only be used when in party.# +Partner's SP is insufficient or skill is disabled.# +Force close part time shop notification email# +Hello. Operation team.\r\nThose part-time job booths were confirmed to be inconsistent with the operation policy and were forcibly terminated.\r\nIf you would like more information, please contact us.\r\n thank you# +You can't enter value more than 50%.# +Sent a request to join the party.# +You can not register party at an adventurer's office# +Accept# +Refuse# +Healer# +The character could not be found.# +Close shop# +Unable to find applicable party.# +You can not join the party because your job level is low.# +The current requestor is in an area where you can not join the party.# +Party number exceeded.# +%s party leader has approved you to join the party.# +%s party leader has rejected you from joining the party.# +%s is already in the party.# +%s has accepted party invitation.# +%s refused party invitation.# +Since you can not receive party request, %s's party invitation has been rejected.# +Adventurer's Agency List# +Request to join the party# +If you are not a party leader, you can not register party at the adventurer's office.# +You can not stop the party recruitment.# +This is party does not exist.# +Zoom Out# +%s : Zoom Out can be turned On Off# +Zoom Out is turned (On)# +Zoom Out is turned (Off)# +/zoom# +Adventurer's Agency# +You can not request to join, if you are a party leader.# +Registering adventurer's agent. Please wait.# +You can no longer choose a job.# +The user can't receive invitation.# +After a few moments, please re-open.# +This function is not available for %d minutes during WoE.# +/viewclear# +Translucent building ON# +Translucent building OFF# +Building transparency# +Failed to revert, please close the window.# +Emblem Frame# +Emblem displayed with border# +Emblem displayed without border# +No equipped weapon.# +Insufficient energy sphere.# +Contains skill (%s) that can't be learned.# +/frame# +After Weapon Blocking is triggered, possible skills are available for use for limited duration.# +The username or password is incorrect.# +E X P : %.1f%% ( basic 100.0%% %s %.1f%%)# +DROP : %.1f%% ( basic 100.0%% %s %.1f%%)# +DEATH : %.1f%% ( basic 100.0%% %s %.1f%%)# +Available in English or Russian only.# +you must have an AccessTicket to login# +Loading Guild Storage.# +NOW LOADING..# +Delete# +Reply# +Send# +Validate name# +Notice# +General# +Return# +Search# +Failed to feed the pet. Pet in a state that can't be fed.# +Failed to feed the homunculus. Homunculus in a state that can't be fed.# +money# +%s %d ea# +Exchange failed.# +Exchange successfuly completed.# +Not enough items to exchange.# +The item has been sold and out of stock.# +KO'ed# +PvP# +Expand possession limit# +Do you want to expand the maximum possession limit by using ^0000ff%s^000000? \nIt will expand from (^0000ff%d^000000) to (^0000ff%d^000000).\n^ff0000 Expanded item possession limit can't be reversed.^000000# +Failed to expand the maximum possession limit.# +To expand the possession limit, please close other windows.# +Failed to expand the maximum possession limit, insufficient required item.# +You can no longer expand the maximum possession limit.# +You have successfully expanded the possession limit.# +You can't have more the same %d item at a time.# +It is not possible to purchase as it will exceeds the possession limit.# +You can only buy one item at a time.# +Star Emperor# +Soul Reaper# +The settings are stored on the server on normal exit.# +Capture Monster# +message# +TITLE# +Expand damage mark# diff --git a/openkore_llm_knowledge/tables/packetdescriptions.txt b/openkore_llm_knowledge/tables/packetdescriptions.txt new file mode 100644 index 0000000000..8f94a37790 --- /dev/null +++ b/openkore_llm_knowledge/tables/packetdescriptions.txt @@ -0,0 +1,1032 @@ +[Recv] +0000 Idle +0069 Account Info +006A Connection Failed With Error +006B Received characters from Game Login Server +006C Error logging into Game Login Server (invalid character specified)... +006D Char Creation Successful +006E Char Creation Failed +006F Char Deletion Successful +0070 Char Deletion Failed +0071 Received character ID and Map IP from Game Login Server +0072 Received characters Info from Game Login Server +0073 Enter Map +0074 Error logging into Map Server... +0075 Char Init +0076 Char Update +0077 Char Init +0078 actor_display (actor exists) +0079 Actor Connected +007A Char Entry +007B actor_display (actor moved) +007C Actor Spawned +007F Received Sync +0080 Actor Lost (Died, Disappeared, Disconnected) +0081 Disconnected from Server With Error +0083 Quit Accept +0084 Quit Reject +0086 actor_display (actor moved) +0087 You Move +0088 Actor Position +008A Actor Item take / attack damage / sit / stand +008D Public message +008E Public message by yourself +0091 Map Changed +0092 Map Server Changed +0093 NPC ack enable +0095 Actor Get Info (Name) +0097 Private Message +0098 Private Response +009A Special Message (broadcast) +009C Actor Look +009D Item Exists +009E Item Appeared +00A0 Item Pickup +00A1 Item Disappeared +00A3 Inventory Items List +00A4 Inventory Equipments List +00A5 Storage Items List +00A6 Storage Equipments List +00A8 Inventory Used +00AA Inventory Equip +00AC Inventory Un-Equip +00AF Inventory Removed +00B0 Your Status Info +00B1 Your Status Info (Exp, Job Exp, Zeny) +00B3 Character Select Result +00B4 NPC Talk +00B5 NPC Continue +00B6 NPC Cancel +00B7 NPC Response +00BC Your Status Added (Str, Agi, Vit, Int, Dex, Luk) +00BD Your Status Info (Calculated) +00BE Your Status Info (Points Need) +00C0 Emotion +00C2 Total Users +00C3 Change Equipment Display +00C4 NPC Buy-Sell +00C6 NPC Buy List +00C7 NPC Sell List +00CA Buy Result +00CB Sell Result +00CD Disconnected from Server +00D1 Player (Ignored / Unignored) +00D2 All Player (Ignored / Unignored) +00D4 Whisper List +00D6 Chat Room Created +00D7 Chat Room List +00D8 Chat Room Closed +00DA Join Chat Room Failed +00DB Chat Room Users List +00DC User Join Chat Room +00DD User Left Chat Room +00DF Chat Room Properties Modified +00E1 Chat Room Owner Changed +00E5 Deal Request +00E7 Deal Accept +00E9 Deal Item Added (Other) +00EA Deal Item Added (You) +00EC Deal Confirm +00EE Deal Cancelled +00F0 Deal Completed +00F1 Deal undo +00F2 Storage Capacity +00F4 Storage Item Added +00F6 Storage Item Removed +00F8 Storage Closed +00FA Party Creation Result +00FB Party Users List +00FD Party Join Request (From You) +00FE Party Join Request (From Other) +0101 Party Share +0104 Party User Info +0105 Party User Left +0106 Party HP +0107 Party Move +0108 Upgrade +0109 Party Message +010A MVP Item +010B MVP Exp +010C MVP Player +010D MVP Item Throw +010E Skill Level Changed +010F Skills List +0110 Skill Use Failed +0111 New Skill +0114 Skill Damage +0115 Skill Position +0117 Skill Location +0119 Actor Status (Freeze, Poison, ...) +011A Skill Restore +011C Warp Portal List +011E Memo +011F Skill Area +0120 Skill Disappeared +0121 Cart Capacity +0122 Cart Equipments List +0123 Cart Items List +0124 Cart Item Added +0125 Cart Item Removed +012B Cart Removed (Off) +012C Cart Add Failed +012D Vending +0131 Vender Founded +0132 Vender Lost +0133 Vender Sell List +0135 Vender Item Not Enough +0136 Sell List +0137 Sell +0139 Range Attack With New Position +013A Attack Range +013B Arrow +013C Arrow Equip +013D Your HP/SP Changed +013E Skill Cast +0141 Your Status Changed (Str, Agi, Vit, Int, Dex, Luk, Bonus) +0142 NPC Talk Number Dialog +0144 Mini Map Indicator +0145 NPC Image +0147 Skill Use +0148 Resurrection +014A Alignment Failed +014B Alignment +014C Guild Allies / Enemy List +014E Guild Master Flag +0150 Guild Info +0152 Guild Emblem Image +0154 Guild Members List +0156 Guild Member Titles Selected +0158 Guild Member Info +015A Guild Member Leave +015C Guild Member Delete +015E Guild Broken Result +015F Guild Disorganize +0160 Guild Member Setting List +0162 Guild Skills List +0163 Guild Member Leave +0164 Guild Info Other +0166 Guild Member Titles List +0167 Guild Create Result +0169 Guild Join Result +016A Guild Join Request (From Other) +016C Guild Name +016D Guild Member Online Status +016F Guild Notice +0171 Guild Ally Request +0173 Guild Ally Result +0174 Guild Member Titles Changed +0176 Guild Member Info +0177 Identify List +0179 Identify +017B Card Merge Items List +017D Card Merge Status +017F Guild Message +0181 Guild Request Status +0182 New Guild Member +0184 Guild Ally / Enemy Un-Set Status +0185 Guild Ally / Enemy Set Status +0187 Request Sync Packet +0188 Upgrade +0189 No Teleporting +018B Exit +018C Sense +018D Mixture List +018F Mixture Result +0191 Skill Talkbox +0192 Map Change Cell (Ice Wall, Other...) +0194 Character Name +0195 Player Guild Info +0196 Actor Effect (Skill, Weight, ...) +0199 PVP Off +019A PVP On +019B Actor Gained Level +019E Slot Machine +01A0 Pet Catch +01A2 Pet Name +01A3 Pet Request Food +01A4 Pet Event +01A6 Pet's Egg List +01AA Pet Talk / Emo +01AB Mute Counter +01AC Stop or Snare +01AD Arrow Craft +01B0 Monster Transform +01B1 Show Digit +01B3 NPC Image +01B4 Guild Emblem Updated +01B5 Account Remain +01B6 Guild Info : 0150 +01B8 Guild Zeny +01B9 Actor Skill Failed +01BE Ask pngameroom +01C1 Remain Time Info +01C2 Remain Time +01C3 Special Message (broadcast local) +01C4 Storage Added +01C5 Cart Added +01C7 Encryption +01C8 Inventory Used +01C9 Skill Area : 011F +01CC Monster Talk +01CD Available Autocast Spells +01CF Devotion List +01D0 Spirit or Coin Count (revolving entities) +01D1 Blade Stop +01D2 Combo Attack +01D3 Sound Effect +01D4 NPC Talk Text +01D6 Map Property +01D7 Weapon / Shield Display +01D8 actor_display (actor exists) +01D9 Actor Connected : 0079 +01DA actor_display (actor moved) +01DB actor_display (actor connected) +01DC Login Code +01DE Skill Damage : 0114 +01E0 GM Account Name Result +01E1 Spirit or Coin Count : 01D0 +01E2 Marriage Requested Couple +01E4 Marriage Start Couple +01E6 Marriage Partner Name. +01E9 Party Joined (Other Player) +01EA Married +01EB Guild Position +01EC Guild Member Map Changed +01EE Inventory Items List : 00A3 +01EF Cart Items List : 0123 +01F0 Storage Items List : 00A5 +01F1 Error +01F2 Guild member in/out +01F3 Misc Effect +01F4 Deal Request +01F5 Deal Accept +01F6 Adoption Request +01F8 Adoption Baby +01FC Repair Item List +01FE Repair Result +01FF High Jump +0201 Friend List +0205 Divorced +0206 Friend In/Out +0207 Friend Request Receive +0209 Friend Request Response +020A Friend Removed +020D Char Ban List +020E Taekwon Misson +020F PVP point +0210 PVP point result +0215 Skill Message (Gospel) +0216 Adoption Baby Reply +0219 Blacksmith Top 10 Ranking +021A Alchemist Top 10 Ranking +021B Blacksmith Points +021C Alchemist Points +021E Less Effect +021F PK info +0220 Crazy killer +0221 Refinable Item List +0223 Refine Message +0224 Taekwon Ranking +0226 Taekwon Top 10 Ranking +0227 GameGuard Request +0229 Actor Status (Freeze, Poison, ...) +022A actor_display (actor exists) +022B actor_display (actor connected) +022C actor_display (actor moved) +022E Homunculus Property +022F Homunculus Fed +0230 Homunculus Info +0235 Homunculus Skills List +0238 PK Top 10 Ranking +0239 Skill Update +023A Storage Password Request +023C Storage Password Result +023E Storage Password Request +0240 Mail Refresh Inbox +0242 Mail Read +0245 Mail Get Attachment +0249 Mail Send +024A Mail New +0250 Auction Result +0252 Auction Item Search Request +0253 Taekwon feel save +0255 Mail Set Attachment +0256 Auction Item Added +0257 Mail Delete +0259 GameGuard Grant +025A Cooking List +025D Auction Self Sell Stop +025F Auction Window +0260 Mail Window +0274 Mail Return +0276 Account Info With Server Info +027B Premium Rates Info +0283 Account ID +0284 GANSI Rank +0287 Cash Dealer +0289 Cash Dealer Buy Failed +028A Actor Status (Freeze, Poison, ...) +028E Charname is valid +0290 Charname change result +0291 Message String (stringtable) +0293 Boss Map Info +0294 Book Read +0295 Inventory Equipments List +0296 Storage Equipments List +0297 Cart Equipments List +0298 Item Rental Time +0299 Item Rental Time Expired +029A Inventory Item Added +029B Mercenary Init +029C Mercenary property +029D Skills List +02A2 Mercenary Status Info +02A3 GameGuard lingo key +02A6 GameGuard Request +02AA Cash Shop Password Request +02AC Cash Shop Password Result +02AD PinCode Request +02AE Initialize Message ID Encryption +02B1 Quest List +02B2 Quest List Mission +02B3 Quest Added +02B4 Quest Deleted +02B5 Quest Update Mission Hunt +02B7 Quest Active +02B8 Party Pickup Settings Info +02B9 Hotkey List +02BB Equipitem damaged +02C1 NPC Talk +02C5 Party Invite Result +02C6 Party Invite by Name +02C9 Party Allow Invite +02CA Error logging into Game Login Server (invalid character specified)... +02CB Instance Window Start +02CC Instance Window Queue +02CD Instance Window Join +02CE Instance Window Leave +02D0 Inventory Equipments List +02D1 Storage Equipments List +02D2 Cart Equipments List +02D3 Bind on equip +02D4 Inventory Item Added +02D5 Disconnected from Server (unknown) +02D7 Equipment Window (Microscope Other) +02D9 Show Equipment Window Flag (other) +02DA Show Equipment Window Flag (You) +02DC Battleground Message +02DD Battleground Emblem +02DE Battleground Score +02DF Battleground Player Position +02E0 Battleground Player HP +02E1 Actor Item take / attack damage / sit / stand +02E7 Map Property +02E8 Inventory Item List +02E9 Cart Item List +02EA Storage Item List +02EB Enter Map +02EC actor_display (actor exists) +02ED Actor Connected +02EE actor_display (actor moved) +02EF Font +02F0 Progress Bar +02F2 Progress Bar Stop +040C Special Message (broadcast local) +043D Skill Post Delay +043E Skill Post Delay List +043F Actor Effect (Skill, Weight, ...) +0440 Millenium Shield +0441 Skill Delete +0442 Sage AutoSpell +0444 Cash Shop Item List +0446 Mini Map Indicator +0449 HackShield Alarm +07D8 Party Exp Setting +07D9 Hotkey List +07DB Your Status Info +07E1 Skill Update +07E2 Message String (stringtable) +07E3 Skill Exchange Item +07E6 Skill Use Message +07E8 Captcha Image +07E9 Captcha Answer +07F6 Exp Gain +07F7 actor_display (actor exists) +07F8 Actor Connected +07F9 actor_display (actor moved) +07FA Inventory Item RemoveD +07FB Skill Cast +07FC Party Leader Changed +07FD Special Item Obtain +07FE Sound Effect +07FF Define Check +0800 Vender Sell List +0803 Booking Register Result +0805 Booking Search Result +0807 Booking Delete +0809 Booking insert +080A Booking Update +080B Booking Delete +080E Party User HP Info +080F Deal Item Added (Other) +0810 Buying Store Open +0812 Buying Store Result +0813 Buying Store Item List (You) +0814 Buying Store Found +0816 Buying Store Lost +0818 Buying Store Item List (Other) +081A Buying Store Buy Result +081B Buying Store Item Update +081C Buying Store Item Delete +081D Elemental Info (sage) +081E Elemental Status Info +0824 Buying Store Trade Failed +0828 Char Deletion Result +082A Char Deletion Accept Result +082C Char Deletion Cancel Result +082D Received characters from Game Login Server +0836 Search Store Result +0837 Search Store Fail +0839 Guild Expulsion +083A Search Store Open +083D Search Store Position +083E Connection Failed With Error +0845 Cash Shop Open Result +0849 Cash Shop Buy Result +084B Item Appeared +0856 actor_display (actor moved) +0857 actor_display (actor exists) +0858 Actor Connected +0859 Equipment Window (Microscope Other) +08B3 Show Script +08B4 Pet Capture Process +08B6 Pet Capture Result +08B9 PinCode Request +08BB PinCode New Code Result +08C7 Skill Area +08C8 Actor Item take / attack damage / sit / stand +08CA Cash Shop Item List +08CB Rates Info +08CD Actor Position +08CF Spirit or Coin Count (revolving entities) +08D0 Inventory Equip +08D1 Inventory Un-Equip +08D2 High Jump +08D6 NPC clear dialog +08E2 Navigation +08E3 Char Update Info +08FE Quest Update Mission Hunt +08FF Actor Effect (Skill, Weight, ...) +0900 Inventory Item List +0901 Inventory Equipments List +0902 Cart Item List +0903 Cart Equipments List +0906 Equipment Window (Microscope Other) +0908 Inventory Item Favorite +090F Actor Connected +0914 actor_display (actor moved) +0915 actor_display (actor exists) +096D Merge Item Open +096F Merge Item Result +0975 Storage Item List +0976 Storage Equipments List +0977 Monster HP Info +097A Quest List +097B Rates Info +097D Top 10 +097E Top 10 Rank Points +0983 Actor Effect (Skill, Weight, ...) +0984 Actor Effect (Skill, Weight, ...) +0985 Skill Post Delay List +0988 Clan Users +098A Clan Info +098D Clan Leave +098E Clan Chat Message +0990 Inventory Item Added +0991 Inventory Item List +0992 Inventory Equipments List +0993 Cart Item List +0994 Cart Equipments List +0995 Storage Item List +0996 Storage Equipments List +0997 Equipment Window (Microscope Other) +0999 Equip +099A Unequip +099B Map Property +099D Received characters Info from Game Login Server +099F Skill AOE Multiple +09A0 Received characters from Game Login Server +09A6 Banking check +09A8 Banking deposit +09AA Banking withdraw +09BB Guild Storage Opened +09BF Guild Storage Closed +09AD Bargain Sale Item Info +09B2 Bargain Sale Selling +09B3 Bargain Sale Close +09C4 Bargain Sale Count Item +09CA Skill AOE Multiple +09CB Skill Used No Damage +09CD Message String (stringtable) +09CF GameGuard Request +09D1 Progress Bar Unit +09D5 NPC market info +09D7 NPC market purchase result +09DA Guild Storage Log +09DB actor_display (actor moved) +09DC Actor Connected +09DD actor_display (actor exists) +09DE Private Message +09DF Private Message Result +09E5 Vender Shop Sold +09E6 Buying Store Item Update +09E7 Rodex Unread Mails +09EB Rodex Read Mail +09ED Rodex Write Result +09F0 Rodex Mail List +09F2 Rodex Zeny Get +09F4 Rodex Item Get +09F6 Rodex Delete +09F7 Homunculus Property +09F8 Quest List +09F9 Quest Added +09FA Quest Update Mision Hunt +09FC Pet Evolution Result +09FD actor_display (actor moved) +09FE Actor Connected +09FF actor_display (actor exists) +0A00 Hotkey List +0A02 Dressroom Open +0A05 Rodex Item Added +0A07 Rodex Item Removed +0A09 Deal Item Added (Other) +0A0A Storage Item Added +0A0B Cart Item Added +0A0C Inventory Item Added +0A0D Inventory Equipments List +0A0E Battleground HP Notify +0A0F Cart Equipments List +0A10 Storage Equipments List +0A11 Guild Storage Equipments List +0A12 Rodex Open to Write +0A14 Rodex Character Name Received +0A17 NPC Dynamic Create Result +0A18 Enter Map +0A1A Roullete Open +0A1C Roullete Info +0A1E Roullete Close +0A20 Roullete Generate +0A22 Roullete Item Received +0A23 Achievement List +0A24 Achievement Update +0A26 Achievement Reward +0A27 HP/SP Changed +0A28 Open Store Status +0A29 Bot AU Request +0A2B Cash Shop Open +0A2C Cash Shop Buy Item Result +0A2D Equipment Window (Microscope Other) +0A2F Guild Title Change +0A30 Actor Get Info (Name) +0A31 Package Item Result +0A32 Rodex Open Through NPC +0A33 Roullete Coin Update +0A34 Cash Shop Senbei Amount +0A36 Monster HP Info Tiny +0A37 Inventory Item Added +0A38 User Interface (open system) +0A3B Hat Effect Info +0A41 Skill AOE Effect +0A43 Party Join +0A44 Party Users List +0A47 Stylist UI Result +0A49 Private Airship Request +0A4A Private Airship Type +0A4B Map Changed +0A4C Map Server Changed +0A51 Rodex Character Name Received +0A53 Captcha Upload Window +0A55 Captcha Upload Status +0A57 Macro Reporter Status +0A58 Macro Detector Image Info +0A59 Macro Detector Image (captcha) +0A5B Macro Detector Image UI +0A5D Macro Detector Status +0A6A Captcha Preview Image Info +0A6B Captcha Preview Image Info +0A6D Macro Reporter Player List +0A6F Message String (stringtable) +0A7B EAC key +0A7D Rodex Mail List +0A7E Offline Store Items Ready +0A82 Guild Member Delete +0A83 Guild Member Leave +0A84 Guild Info +0A89 Actor Clone Vender Found +0A8A Actor Clone Vender Lost +0A8D Offline Store Itens +0A91 Buying Store Item List (Other) +0A95 Allow Other See Equipment +0A96 Deal Item Added (Other) +0A98 Switch Equip System Item Add Result +0A9A Switch Equip System Item Remove Result +0A9B Switch Equip System Log +0A9D Switch Equip System Result +0AA0 Refine UI Open +0AA2 Refine UI Info +0AA5 Guild Members List +0AA8 Allow Other See Equipment +0AB2 Party dead +0AB8 Move Interrupt +0AB9 Item preview +0ABD Party User Level Info +0ABE Warp Portal List +0AC2 Rodex Mail List +0AC4 Account Info With Server Info +0AC5 Received character ID and Map IP from Game Login Server +0AC7 Map Server Changed +0AC9 Account Info With Server Info +0ACA Disconnected from Server With Error +0ACB Your Status Info +0ACC Exp Gain +0ACD Connection Failed With Error +0ADA Refine status +0ADC Allow Other See Equipment +0ADD Item Exists +0ADE Overweight Percent +0ADF Actor Get Info (Name) +0AE0 Connection Failed With Error +0AE2 User Interface (open system) +0AE3 OTP Token Received +0AE4 Party Joined (Other Player) +0AE5 Party Users List +0AE8 Change Dress +0AE9 PinCode Request +0AF0 Action User Interface +0AF7 Character Name +0AFB Sage AutoSpell +0AFD Guild Position +0AFE Quest Update Mission Hunt +0AFF Quest List +0B02 Connection Failed With Error +0B03 Equipment Window (Microscope Other) +0B05 Actor Clone Vender Found +0B08 Item List Start +0B09 Item List Stackable +0B0A Item List Non-Stackable (Equips) +0B0B Item List End +0B0C Quest Added +0B13 Item preview +0B18 Inventory expansion result +0B1B Load confirm +0B1D Received Ping +0B20 Hotkey List +0B2F Homunculus Property +0B31 New Skill +0B32 Skills List +0B33 Skill Update +0B47 Char emblem update +0B5F Rodex Mail List +0B60 Account Info With Server Info +0B6F Char Creation Successful +0B72 Received characters from Game Login Server +0B73 Spirit or Coin Count (revolving entities) +0B7B Guild Info +0B7D Guild Members List +0B7E New Guild Member + +[Send] +0064 Account Server Login +0065 Character Server Login +0066 Char Login +0067 Char Create Request +0068 Char Delete Request +0072 Map Login +007D Map Loaded +007E Sync +0082 Quit Request +0085 Move +0089 Attack / Sit / Stand +008C Chat +0090 Talk +0094 Get Actor Info +0096 Private Msg +0099 Special Message (broadcast) +009B Look +009F Take +00A2 Drop +00A7 Item Use +00A9 Equip +00AB Unequip +00B2 Respawn +00B8 Talk Response +00B9 Talk Continue +00BA Request Status +00BB Add Status Point +00BF Emotion +00C1 Who +00C5 Get Store / Sell List +00C8 Buy +00C9 Sell +00CC GM Kick Char +00CE GM Kick All +00CF Ignore +00D0 Ignore All +00D3 Ignore List Request +00D5 Chat Room Create +00D9 Chat Room Join +00DE Chat Room Change +00E0 Chat Room Bestow +00E2 Chat Room Kick +00E3 Chat Room Leave +00E4 Deal +00E6 Deal Accept / Cancel +00E8 Deal Add Item +00EB Deal OK / Finalize +00ED Current Deal Cancel +00EF Deal Trade +00F3 Storage Add +00F5 Storage Get +00F7 Storage Close +00F9 Party Organize +00FC Party Join Request +00FF Party Join +0100 Party Leave +0102 Party Share EXP +0103 Party Kick +0108 Party Chat +0112 Add Skill Point +0113 Skill Use +0116 Skill Use Location +0118 Attack Stop +011B Teleport / Warp Portal +011D Memo +0126 Cart Add +0127 Cart Get +0128 Cart Get (Shop) +0129 Cart Storage Add +012A Companion Release +012E Shop Close +012F Shop Open +0130 Vender Sell List +0134 Vender Buy +0138 GM PK Mode Change +013F GM Item Mob Create +0140 GM Move Player To Map +0143 Talk Number +0146 Talk Cancel +0149 Alignment +014D Guild First Query +014F Guild Query Page +0151 Emblem Request +0153 Guild Emblem Register +0155 Guild Request Member Position +0157 Guild Open Member Info +0159 Guild Leave +015B Guild Member Delete +015D Guild Request Break +0161 Guild Request Guild Position Info +0165 Guild Request Create +0168 Guild Join Request +016B Guild Join +016E Guild Notice +0170 Guild Ally Request +0172 Guild Ally +0175 Guild Request Member Info +0178 Identify +017A Card Merge Request +017C Card Merge +017E Guild Chat +0180 Guild Enemy Request +0183 Guild Delete Request +0187 Char BAN Check +018A Exit +018E Make Item Request +0190 Skill Use Location With Talkbox +0193 Character Name Request +0197 GM Request Reset State Skill +0198 GM Request Change Cell Type +019C GM Special Message (broadcast local) +019D GM Request Perfect Hide +019F Pet Catch +01A1 Pet Feed +01A5 Pet Name +01A7 Pet Incubator +01A8 Pet Egg info +01A9 Pet Talk / Emo +01AE Make Arrow +01AF Request Change Cart +01B2 Shop Open +01B7 Guild Zeny +01BA GM Remove Account ID +01BB GM Shift +01BC GM Recall +01BD GM Recall Char +01C0 Request Remain Time +01C6 Encryption Request +01CA Homunculus Request Create +01CB Monster Talk +01CE Autocast Skill +01D5 Talk Text +01DB Login Code Request +01DD Login +01DF GM Request Account Name +01E3 Join Couple +01E5 Request Join Couple +01E7 Dori Dori +01E8 Party Create +01ED Novice Explosion Spirit +01F7 Adoption Reply Request +01F9 Char Adoption Request +01FA Login +01FB Char Delete Request +01FD Request Item Repair +0200 Connect Info Changed +0202 Friend Request +0203 Friend Remove +0204 Client hash +0208 Friend Request Accept/Reject +0212 GM Manner by Player Name +0213 GM Request Status +0217 Rank Blacksmith Request +0218 Rank Alchemist Request +021D Less Effect +0222 Refine Item +0225 Rank Taekwon Request +0228 gameguard_sync +022D Homunculus Command +0231 Homunculus Change Name Request +0232 Homunculus Move +0233 Homunculus Attack +0234 Homunculus Move to Master +0237 Rank Killer Request +023B Storage Password +023F Mail Open +0241 Mail Read +0243 Mail Delete +0244 Mail Attachment Get +0246 Mail Remove +0247 Mail Attachment Set +0248 Mail Send +024B Auction Item Added Cancel +024C Auction Item Add +024D Auction Create +024E Auction Cancel +024F Auction Buy +0251 Auction Search +0254 Star Place Agree +025B Cooking Request +025C Auction Info Self +025D Auction Sell Stop +0273 Mail Return +0275 Character Server Login +0281 Gangsi Rank +0288 Cash Dealer Buy Item +0292 Auto Revive (command, ziegfried token) +029F Mercenary Command +02AF Message ID Encryption Initialized +02B0 Account Server Login +02B6 Quest Send State +02BA Hotkey Change +02C4 Party Join (From You) +02C7 Party Join +02D6 View Player Equip Request +02D8 Allow View Sel Equip Windows +02DB Battleground Chat +02F1 Progress Bar Complete +035F Character Move +0360 Sync +0361 Look +0362 Actor Item take / attack damage / sit / stand +0363 Item Drop +0364 Storage Item Add +0365 Storage Item Remove +0366 Skill Use Location +0367 Skill Use Location With Talkbox +0368 Actor Get Info +0369 Actor Get Info (Name) +0436 Map Login +0437 Actor Action (Attack, Sit or Stand) +0438 Skill Use +0439 Item Use +0443 Skill Select +0445 Cash Shop Buy Item +0447 Request Blocking Player Cancel +044A Client Version +07DA Party Leader Change +07D7 Party Settings +07E4 Item List Window Selected +07E7 Captcha Answer +07EC Battleground Join +0801 Buy From Vender +0802 Booking Party Register +0804 Booking Register +0806 Booking Search +0808 Booking Request +0811 Buying Store Create +0815 Buying Store Close +0817 Buying Store Open +0819 Buying Store Info +0825 Character Server Login With OTP +0827 Char Delete +0829 Char Delete Accept +082B Char Delete Cancel +0835 Search Store Info +0838 Search Store Request Next Page +083B Search Store Close +083C SSI Item List on Click +0842 Recal SSO +0843 Remove AID SSO +0844 Cash Shop Open +0846 Cash Shop Tab Request +0848 Cash Shop Buy +084A Cash Shop Close +08B5 Pet Capture +08B8 PinCode Password +08BA Pincode New Password +08C1 Macro Start +08C2 Macro Stop +08C9 Cash Shop Request Items +096E Merge Item Request +0970 Char Create Request +0974 Merge Item Cancel +097C Rank Request +0987 Account Server Login +098D Clan Chat Message +098F Char Delete Accept +0998 Send Equip +09A1 Character Received Sync +09A7 Banking deposit request +09A9 Banking withdraw request +09AB Banking check request +09D0 GameGuard Reply +09D4 Sell or Buy Complete +09D6 Buy bulk market +09D8 Market close +09E1 Guild Storage Item Add +09E2 Guild Storage Item Remove +09E3 Cart to Guild Storage Item Add +09E4 Guild Storage Item Remove to Cart +09F1 Rodex Request Zeny +09F3 Rodex Request Item +09FB Pet Evolution +09E8 Rodex Open Mail Box +09E9 Rodex Close Mail Box +09EA Rodex Read Mail +09EE Rodex Next Page +09EF Rodex Refresh Mail Box +09F1 Rodex Zeny Request +09F3 Rodex Item Request +09F5 Rodex delete mail +09FB Pet Evolution +0A01 Hotkey Rotate +0A03 Rodex Cancel Write Mail +0A04 Rodex Add Item +0A06 Rodex Remove Item +0A08 Rodex Open Write Mail +0A13 Rodex Request Check Character Name +0A16 NPC Dynamic Create Request +0A19 Roullete Request Open +0A1B Roullete Request Info +0A1D Roullete Request Close +0A1F Roullete Request Generate +0A21 Roullete Send Item Received Confirmation +0A25 Achievement Get Reward +0A2A AU BOT +0A2E Guild Request Change Title +0A35 Identify +0A39 Char Create Request +0A46 Stylist UI Style Change +0A49 Private Airship Request +0A52 Captcha Register Image +0A54 Captcha Register Image +0A56 Macro Reporter Info +0A5A Macro Detector Download Done +0A5C Macro Detector captcha answer +0A69 Preview Captcha Image +0A6C Macro Reporter Select Area +0A68 Open User Interface Request User Interface (open system) +0A6E Rodex Mail Send +0A75 Account Server Login (encrypted) +0A76 Account Server Login +0A7F Offline Store Open +0A97 Switch Equip System Add Item +0A99 Switch Equip System Remove Item +0A9C Switch Equip System Switch +0AA1 Refine UI Select +0AA3 Refine UI Info Refine +0AA4 Refine UI Info close +0AAC Account Server Login +0AC0 Rodex Open Mail Box +0AC1 Rodex Refresh Mail Box +0ACE Switch Equip System +0ACF Account Server Login With OTP +0AE8 Stylist UI Change Dress +0AEF Attendance reward request +0AF4 Skill Use Location +0B10 Skill Start Use +0B11 Skill Stop Use +0B14 Inventory expansion request +0B19 Inventory expansion rejected +0B1C Ping +0B21 Hotkey Change diff --git a/openkore_llm_knowledge/tables/packetlist.txt b/openkore_llm_knowledge/tables/packetlist.txt new file mode 100644 index 0000000000..8d5b0ead94 --- /dev/null +++ b/openkore_llm_knowledge/tables/packetlist.txt @@ -0,0 +1,3108 @@ +PACKETS HISTORY SINCE 2004 UPDATED IN 2018-08-27 BY ALISONRAG + +ID = PACKET ID +LEN = PACKET LENGTH +TYPE = RECV: PACKETS FROM SERVER TO CLIENT. SEND = PACKETS FROM CLIENT TO SERVER +OPENKORE_SUB = SUB IN OPENKORE THAT PARSE THIS PACKET +PACKET_NAME = PACKET NAME FROM AEGIS ( GRAVITY EMULATOR ) +HERCULES_FUNCTION = FUNCTION IN HERCULES EMULATOR THAT PARSE THIS PACKET + +// default packets +ID LEN TYPE OPENKORE_SUB PACKET_NAME HERCULES_FUNCTION +0072 19 send received_characters CZ_ENTER clif->pWantToConnection +0064 55 send master_login CA_LOGIN lclif->p->parse_CA_LOGIN +0065 17 send game_login CH_ENTER chr->parse_char_connect +0066 6 send char_login CH_SELECT_CHAR chr->parse_char_select +0067 37 send char_create CH_MAKE_CHAR chr->parse_make_char_0067 +0068 46 send char_delete CH_DELETE_CHAR chr->parse_char_delete_char_0068 +0069 -1 recv account_server_info AC_ACCEPT_LOGIN +006A 23 recv login_error AC_REFUSE_LOGIN +006B -1 recv received_characters_info HC_ACCEPT_ENTER +006C 3 recv login_error_game_login_server HC_REFUSE_ENTER +006D 108 recv character_creation_successful HC_ACCEPT_MAKECHAR +006E 3 recv character_creation_failed HC_REFUSE_MAKECHAR +006F 2 recv character_deletion_successful HC_ACCEPT_DELETECHAR +0070 6 recv character_deletion_failed HC_REFUSE_DELETECHAR +0071 28 recv received_character_ID_and_Map HC_NOTIFY_ZONESVR +0072 19 send received_characters CZ_ENTER clif->pWantToConnection +0073 11 recv map_loaded ZC_ACCEPT_ENTER +0074 3 recv ZC_REFUSE_ENTER +0075 -1 recv changeToInGameState ZC_NOTIFY_INITCHAR +0076 9 recv ZC_NOTIFY_UPDATECHAR +0077 5 recv changeToInGameState ZC_NOTIFY_UPDATEPLAYER +0078 54 recv actor_exists ZC_NOTIFY_STANDENTRY +0079 53 recv actor_connected ZC_NOTIFY_NEWENTRY +007A 58 recv changeToInGameState ZC_NOTIFY_ACTENTRY +007B 60 recv actor_moved ZC_NOTIFY_MOVEENTRY +007C 41 recv actor_spawned ZC_NOTIFY_STANDENTRY_NPC +007D 2 send map_loaded CZ_NOTIFY_ACTORINIT clif->pLoadEndAck +007E 6 send sync CZ_REQUEST_TIME clif->pTickSend +007F 6 recv received_sync ZC_NOTIFY_TIME +0080 7 recv actor_died_or_disappeared ZC_NOTIFY_VANISH +0081 3 recv errors SC_NOTIFY_BAN +0082 2 send CZ_REQUEST_QUIT +0083 2 recv ZC_ACCEPT_QUIT +0084 2 recv ZC_REFUSE_QUIT +0085 5 send character_move CZ_REQUEST_MOVE clif->pWalkToXY +0086 16 recv actor_display ZC_NOTIFY_MOVE +0087 12 recv character_moves ZC_NOTIFY_PLAYERMOVE +0088 10 recv actor_movement_interrupted ZC_STOPMOVE +0089 7 send actor_action CZ_REQUEST_ACT clif->pActionRequest +008A 29 recv actor_action ZC_NOTIFY_ACT +008B 2 recv ZC_NOTIFY_ACT_POSITION +008C -1 send public_chat CZ_REQUEST_CHAT clif->pGlobalMessage +008D -1 recv public_chat ZC_NOTIFY_CHAT +008E -1 recv self_chat ZC_NOTIFY_PLAYERCHAT +008F -1 unkn +0090 7 send npc_talk CZ_CONTACTNPC clif->pNpcClicked +0091 22 recv map_change ZC_NPCACK_MAPMOVE +0092 28 recv map_changed ZC_NPCACK_SERVERMOVE +0093 2 recv ZC_NPCACK_ENABLE +0094 6 send actor_info_request CZ_REQNAME clif->pGetCharNameRequest +0095 30 recv actor_info ZC_ACK_REQNAME +0096 -1 send private_message CZ_WHISPER clif->pWisMessage +0097 -1 recv private_message ZC_WHISPER +0098 3 recv private_message_sent ZC_ACK_WHISPER +0099 -1 send gm_broadcast CZ_BROADCAST clif->pBroadcast +009A -1 recv system_chat ZC_BROADCAST +009B 5 send actor_look_at CZ_CHANGE_DIRECTION clif->pChangeDir +009C 9 recv actor_look_at ZC_CHANGE_DIRECTION +009D 17 recv item_exists ZC_ITEM_ENTRY +009E 17 recv item_appeared ZC_ITEM_FALL_ENTRY +009F 6 send item_take CZ_ITEM_PICKUP clif->pTakeItem +00A0 23 recv inventory_item_added ZC_ITEM_PICKUP_ACK +00A1 6 recv item_disappeared ZC_ITEM_DISAPPEAR +00A2 6 send item_drop CZ_ITEM_THROW clif->pDropItem +00A3 -1 recv inventory_items_stackable ZC_NORMAL_ITEMLIST +00A4 -1 recv inventory_items_nonstackable ZC_EQUIPMENT_ITEMLIST +00A5 -1 recv storage_items_stackable ZC_STORE_NORMAL_ITEMLIST +00A6 -1 recv storage_items_nonstackable ZC_STORE_EQUIPMENT_ITEMLIST +00A7 8 send item_use CZ_USE_ITEM clif->pUseItem +00A8 7 recv use_item ZC_USE_ITEM_ACK +00A9 6 send send_equip CZ_REQ_WEAR_EQUIP clif->pEquipItem +00AA 7 recv equip_item ZC_REQ_WEAR_EQUIP_ACK +00AB 4 send send_unequip_item CZ_REQ_TAKEOFF_EQUIP clif->pUnequipItem +00AC 7 recv unequip_item ZC_REQ_TAKEOFF_EQUIP_ACK +00AD -1 send CZ_REQ_ITEM_EXPLANATION_BYNAME +00AE -1 recv ZC_REQ_ITEM_EXPLANATION_ACK +00AF 6 recv inventory_item_removed ZC_ITEM_THROW_ACK +00B0 8 recv stat_info ZC_PAR_CHANGE +00B1 8 recv stat_info ZC_LONGPAR_CHANGE +00B2 3 send restart CZ_RESTART clif->pRestart +00B3 3 recv switch_character ZC_RESTART_ACK +00B4 -1 recv npc_talk ZC_SAY_DIALOG +00B5 6 recv npc_talk_continue ZC_WAIT_DIALOG +00B6 6 recv npc_talk_close ZC_CLOSE_DIALOG +00B7 -1 recv npc_talk_responses ZC_MENU_LIST +00B8 7 send npc_talk_response CZ_CHOOSE_MENU clif->pNpcSelectMenu +00B9 6 send npc_talk_continue CZ_REQ_NEXT_SCRIPT clif->pNpcNextClicked +00BA 2 send CZ_REQ_STATUS +00BB 5 send send_add_status_point CZ_STATUS_CHANGE clif->pStatusUp +00BC 6 recv stats_added ZC_STATUS_CHANGE_ACK +00BD 44 recv stats_info ZC_STATUS +00BE 5 recv stat_info ZC_STATUS_CHANGE +00BF 3 send send_emotion CZ_REQ_EMOTION clif->pEmotion +00C0 7 recv emoticon ZC_EMOTION +00C1 2 send request_user_count CZ_REQ_USER_COUNT clif->pHowManyConnections +00C2 6 recv users_online ZC_USER_COUNT +00C3 8 recv job_equipment_hair_change ZC_SPRITE_CHANGE +00C4 6 recv npc_store_begin ZC_SELECT_DEALTYPE +00C5 7 send request_buy_sell_list CZ_ACK_SELECT_DEALTYPE clif->pNpcBuySellSelected +00C6 -1 recv npc_store_info ZC_PC_PURCHASE_ITEMLIST +00C7 -1 recv npc_sell_list ZC_PC_SELL_ITEMLIST +00C8 -1 send buy_bulk CZ_PC_PURCHASE_ITEMLIST clif->pNpcBuyListSend +00C9 -1 send sell_bulk CZ_PC_SELL_ITEMLIST clif->pNpcSellListSend +00CA 3 recv buy_result ZC_PC_PURCHASE_RESULT +00CB 3 recv sell_result ZC_PC_SELL_RESULT +00CC 6 send gm_kick CZ_DISCONNECT_CHARACTER clif->pGMKick +00CD 3 recv ZC_ACK_DISCONNECT_CHARACTER +00CE 2 send gm_kick_all CZ_DISCONNECT_ALL_CHARACTER clif->pGMKickAll +00CF 27 send ignore_player CZ_SETTING_WHISPER_PC clif->pPMIgnore +00D0 3 send ignore_all CZ_SETTING_WHISPER_STATE clif->pPMIgnoreAll +00D1 4 recv ignore_player_result ZC_SETTING_WHISPER_PC +00D2 4 recv ignore_all_result ZC_SETTING_WHISPER_STATE +00D3 2 send get_ignore_list CZ_REQ_WHISPER_LIST clif->pPMIgnoreList +00D4 -1 recv whisper_list ZC_WHISPER_LIST +00D5 -1 send chat_room_create CZ_CREATE_CHATROOM clif->pCreateChatRoom +00D6 3 recv chat_created ZC_ACK_CREATE_CHATROOM +00D7 -1 recv chat_info ZC_ROOM_NEWENTRY +00D8 6 recv chat_removed ZC_DESTROY_ROOM +00D9 14 send chat_room_join CZ_REQ_ENTER_ROOM clif->pChatAddMember +00DA 3 recv chat_join_result ZC_REFUSE_ENTER_ROOM +00DB -1 recv chat_users ZC_ENTER_ROOM +00DC 28 recv chat_user_join ZC_MEMBER_NEWENTRY +00DD 29 recv chat_user_leave ZC_MEMBER_EXIT +00DE -1 send chat_room_change CZ_CHANGE_CHATROOM clif->pChatRoomStatusChange +00DF -1 recv chat_modified ZC_CHANGE_CHATROOM +00E0 30 send chat_room_bestow CZ_REQ_ROLE_CHANGE clif->pChangeChatOwner +00E1 30 recv chat_newowner ZC_ROLE_CHANGE +00E2 26 send chat_room_kick CZ_REQ_EXPEL_MEMBER clif->pKickFromChat +00E3 2 send chat_room_leave CZ_EXIT_ROOM clif->pChatLeave +00E4 6 send deal_initiate CZ_REQ_EXCHANGE_ITEM clif->pTradeRequest +00E5 26 recv deal_request ZC_REQ_EXCHANGE_ITEM +00E6 3 send deal_reply CZ_ACK_EXCHANGE_ITEM clif->pTradeAck +00E7 3 recv deal_begin ZC_ACK_EXCHANGE_ITEM +00E8 8 send deal_item_add CZ_ADD_EXCHANGE_ITEM clif->pTradeAddItem +00E9 19 recv deal_add_other ZC_ADD_EXCHANGE_ITEM +00EA 5 recv deal_add_you ZC_ACK_ADD_EXCHANGE_ITEM +00EB 2 send deal_finalize CZ_CONCLUDE_EXCHANGE_ITEM clif->pTradeOk +00EC 3 recv deal_finalize ZC_CONCLUDE_EXCHANGE_ITEM +00ED 2 send deal_cancel CZ_CANCEL_EXCHANGE_ITEM clif->pTradeCancel +00EE 2 recv deal_cancelled ZC_CANCEL_EXCHANGE_ITEM +00EF 2 send deal_trade CZ_EXEC_EXCHANGE_ITEM clif->pTradeCommit +00F0 3 recv deal_complete ZC_EXEC_EXCHANGE_ITEM +00F1 2 recv ZC_EXCHANGEITEM_UNDO +00F2 6 recv storage_opened ZC_NOTIFY_STOREITEM_COUNTINFO +00F3 8 send storage_item_add CZ_MOVE_ITEM_FROM_BODY_TO_STORE clif->pMoveToKafra +00F4 21 recv storage_item_added ZC_ADD_ITEM_TO_STORE +00F5 8 send storage_item_remove CZ_MOVE_ITEM_FROM_STORE_TO_BODY clif->pMoveFromKafra +00F6 8 recv storage_item_removed ZC_DELETE_ITEM_FROM_STORE +00F7 2 send storage_close CZ_REQUEST_TIME clif->pTickSend +00F8 2 recv storage_closed ZC_CLOSE_STORE +00F9 26 send CZ_MAKE_GROUP clif->pCreateParty +00FA 3 recv party_organize_result ZC_ACK_MAKE_GROUP +00FB -1 recv party_users_info ZC_GROUP_LIST +00FC 6 send party_join_request CZ_REQ_JOIN_GROUP clif->pPartyInvite +00FD 27 recv party_invite_result ZC_ACK_REQ_JOIN_GROUP +00FE 30 recv party_invite ZC_REQ_JOIN_GROUP +00FF 10 send party_join CZ_JOIN_GROUP clif->pReplyPartyInvite +0100 2 send party_leave CZ_REQ_LEAVE_GROUP clif->pLeaveParty +0101 6 recv party_exp ZC_GROUPINFO_CHANGE +0102 6 send party_setting CZ_CHANGE_GROUPEXPOPTION clif->pPartyChangeOption +0103 30 send party_kick CZ_REQ_EXPEL_GROUP_MEMBER clif->pRemovePartyMember +0104 79 recv party_join ZC_ADD_MEMBER_TO_GROUP +0105 31 recv party_leave ZC_DELETE_MEMBER_FROM_GROUP +0106 10 recv party_hp_info ZC_NOTIFY_HP_TO_GROUPM +0107 10 recv party_location ZC_NOTIFY_POSITION_TO_GROUPM +0108 -1 send item_upgrade CZ_REQUEST_CHAT_PARTY clif->pPartyMessage +0109 -1 recv party_chat ZC_NOTIFY_CHAT_PARTY +010A 4 recv mvp_item ZC_MVP_GETTING_ITEM +010B 6 recv mvp_you ZC_MVP_GETTING_SPECIAL_EXP +010C 6 recv mvp_other ZC_MVP +010D 2 recv ZC_THROW_MVPITEM +010E 11 recv skill_update ZC_SKILLINFO_UPDATE +010F -1 recv skills_list ZC_SKILLINFO_LIST +0110 10 recv skill_use_failed ZC_ACK_TOUSESKILL +0111 39 recv skill_add ZC_ADD_SKILL +0112 4 send send_add_skill_point CZ_UPGRADE_SKILLLEVEL clif->pSkillUp +0113 10 send skill_use CZ_USE_SKILL clif->pUseSkillToId +0114 31 recv skill_use ZC_NOTIFY_SKILL +0115 35 recv ZC_NOTIFY_SKILL_POSITION +0116 10 send skill_use_location CZ_USE_SKILL_TOGROUND clif->pUseSkillToPos +0117 18 recv skill_use_location ZC_NOTIFY_GROUNDSKILL +0118 2 send CZ_CANCEL_LOCKON clif->pStopAttack +0119 13 recv character_status ZC_STATE_CHANGE +011A 15 recv skill_used_no_damage ZC_USE_SKILL +011B 20 send warp_select CZ_SELECT_WARPPOINT clif->pUseSkillMap +011C 68 recv warp_portal_list ZC_WARPLIST +011D 2 send memo_request CZ_REMEMBER_WARPPOINT clif->pRequestMemo +011E 3 recv memo_success ZC_ACK_REMEMBER_WARPPOINT +011F 16 recv area_spell ZC_SKILL_ENTRY +0120 6 recv area_spell_disappears ZC_SKILL_DISAPPEAR +0121 14 recv cart_info ZC_NOTIFY_CARTITEM_COUNTINFO +0122 -1 recv cart_items_nonstackable ZC_CART_EQUIPMENT_ITEMLIST +0123 -1 recv cart_items_stackable ZC_CART_NORMAL_ITEMLIST +0124 21 recv cart_item_added ZC_ADD_ITEM_TO_CART +0125 8 recv cart_item_removed ZC_DELETE_ITEM_FROM_CART +0126 8 send cart_add CZ_MOVE_ITEM_FROM_BODY_TO_CART clif->pPutItemToCart +0127 8 send cart_get CZ_MOVE_ITEM_FROM_CART_TO_BODY clif->pGetItemFromCart +0128 8 send storage_to_cart CZ_MOVE_ITEM_FROM_STORE_TO_CART clif->pMoveFromKafraToCart +0129 8 send cart_to_storage CZ_MOVE_ITEM_FROM_CART_TO_STORE clif->pMoveToKafraFromCart +012A 2 send companion_release CZ_REQ_CARTOFF clif->pRemoveOption +012B 2 recv cart_off ZC_CARTOFF +012C 3 recv cart_add_failed ZC_ACK_ADDITEM_TO_CART +012D 4 recv shop_skill ZC_OPENSTORE +012E 2 send shop_close CZ_REQ_CLOSESTORE clif->pCloseVending +012F -1 send CZ_REQ_OPENSTORE +0130 6 send send_entering_vending CZ_REQ_BUY_FROMMC clif->pVendingListReq +0131 86 recv vender_found ZC_STORE_ENTRY +0132 6 recv vender_lost ZC_DISAPPEAR_ENTRY +0133 -1 recv vender_items_list ZC_PC_PURCHASE_ITEMLIST_FROMMC +0134 -1 send buy_bulk_vender CZ_PC_PURCHASE_ITEMLIST_FROMMC clif->pPurchaseReq +0135 7 recv vender_buy_fail ZC_PC_PURCHASE_RESULT_FROMMC +0136 -1 recv vending_start ZC_PC_PURCHASE_MYITEMLIST +0137 6 recv shop_sold ZC_DELETEITEM_FROM_MCSTORE +0138 3 send CZ_PKMODE_CHANGE +0139 16 recv monster_ranged_attack ZC_ATTACK_FAILURE_FOR_DISTANCE +013A 4 recv attack_range ZC_ATTACK_RANGE +013B 4 recv arrow_none ZC_ACTION_FAILURE +013C 4 recv arrow_equipped ZC_EQUIP_ARROW +013D 6 recv hp_sp_changed ZC_RECOVERY +013E 24 recv skill_cast ZC_USESKILL_ACK +013F 26 send gm_item_mob_create CZ_ITEM_CREATE clif->pGM_Monster_Item +0140 22 send gm_move_to_map CZ_MOVETO_MAP clif->pMapMove +0141 14 recv stat_info2 ZC_COUPLESTATUS +0142 6 recv npc_talk_number ZC_OPEN_EDITDLG +0143 10 send npc_talk_number CZ_INPUT_EDITDLG clif->pNpcAmountInput +0144 23 recv minimap_indicator ZC_COMPASS +0145 19 recv ZC_SHOW_IMAGE +0146 6 send npc_talk_cancel CZ_CLOSE_DIALOG clif->pNpcCloseClicked +0147 39 recv item_skill ZC_AUTORUN_SKILL +0148 8 recv resurrection ZC_RESURRECTION +0149 9 send alignment CZ_REQ_GIVE_MANNER_POINT clif->pGMReqNoChat +014A 6 recv manner_message ZC_ACK_GIVE_MANNER_POINT +014B 27 recv GM_silence ZC_NOTIFY_MANNER_POINT_GIVEN +014C -1 recv guild_allies_enemy_list ZC_MYGUILD_BASIC_INFO +014D 2 send guild_check CZ_REQ_GUILD_MENUINTERFACE clif->pGuildCheckMaster +014E 6 recv guild_master_member ZC_ACK_GUILD_MENUINTERFACE +014F 6 send guild_info_request CZ_REQ_GUILD_MENU clif->pGuildRequestInfo +0150 110 recv ZC_GUILD_INFO +0151 6 send guild_emblem_request CZ_REQ_GUILD_EMBLEM_IMG clif->pGuildRequestEmblem +0152 -1 recv guild_emblem ZC_GUILD_EMBLEM_IMG +0153 -1 send CZ_REGISTER_GUILD_EMBLEM_IMG clif->pGuildChangeEmblem +0154 -1 recv guild_members_list ZC_MEMBERMGR_INFO +0155 -1 send CZ_REQ_CHANGE_MEMBERPOS clif->pGuildChangeMemberPosition +0156 -1 recv guild_member_position_changed ZC_ACK_REQ_CHANGE_MEMBERS +0157 6 send CZ_REQ_OPEN_MEMBER_INFO +0158 -1 recv ZC_ACK_OPEN_MEMBER_INFO +0159 54 send guild_leave CZ_REQ_LEAVE_GUILD clif->pGuildLeave +015A 66 recv guild_leave ZC_ACK_LEAVE_GUILD +015B 54 send guild_kick CZ_REQ_BAN_GUILD clif->pGuildExpulsion +015C 90 recv guild_expulsion ZC_ACK_BAN_GUILD +015D 42 send guild_break CZ_REQ_DISORGANIZE_GUILD clif->pGuildBreak +015E 6 recv guild_broken ZC_ACK_DISORGANIZE_GUILD_RESULT +015F 42 recv ZC_ACK_DISORGANIZE_GUILD +0160 -1 recv guild_member_setting_list ZC_POSITION_INFO +0161 -1 send CZ_REG_CHANGE_GUILD_POSITIONINFO clif->pGuildChangePositionInfo +0162 -1 recv guild_skills_list ZC_GUILD_SKILLINFO +0163 -1 recv guild_expulsionlist ZC_BAN_LIST +0164 -1 recv ZC_OTHER_GUILD_LIST +0165 30 send guild_create CZ_REQ_MAKE_GUILD clif->pCreateGuild +0166 -1 recv guild_members_title_list ZC_POSITION_ID_NAME_INFO +0167 3 recv guild_create_result ZC_RESULT_MAKE_GUILD +0168 14 send guild_join_request CZ_REQ_JOIN_GUILD clif->pGuildInvite +0169 3 recv guild_invite_result ZC_ACK_REQ_JOIN_GUILD +016A 30 recv guild_request ZC_REQ_JOIN_GUILD +016B 10 send guild_join CZ_JOIN_GUILD clif->pGuildReplyInvite +016C 43 recv guild_name ZC_UPDATE_GDID +016D 14 recv guild_member_online_status ZC_UPDATE_CHARSTAT +016E 186 send guild_notice CZ_GUILD_NOTICE clif->pGuildChangeNotice +016F 182 recv guild_notice ZC_GUILD_NOTICE +0170 14 send guild_alliance_request CZ_REQ_ALLY_GUILD clif->pGuildRequestAlliance +0171 30 recv guild_ally_request ZC_REQ_ALLY_GUILD +0172 10 send guild_alliance_reply CZ_ALLY_GUILD clif->pGuildReplyAlliance +0173 3 recv guild_alliance ZC_ACK_REQ_ALLY_GUILD +0174 -1 recv guild_position_changed ZC_ACK_CHANGE_GUILD_POSITIONINFO +0175 6 send CZ_REQ_GUILD_MEMBER_INFO +0176 106 recv ZC_ACK_GUILD_MEMBER_INFO +0177 -1 recv identify_list ZC_ITEMIDENTIFY_LIST +0178 4 send identify CZ_REQ_ITEMIDENTIFY clif->pItemIdentify +0179 5 recv identify ZC_ACK_ITEMIDENTIFY +017A 4 send card_merge_request CZ_REQ_ITEMCOMPOSITION_LIST clif->pUseCard +017B -1 recv card_merge_list ZC_ITEMCOMPOSITION_LIST +017C 6 send card_merge CZ_REQ_ITEMCOMPOSITION clif->pInsertCard +017D 7 recv card_merge_status ZC_ACK_ITEMCOMPOSITION +017E -1 send guild_chat CZ_GUILD_CHAT clif->pGuildMessage +017F -1 recv guild_chat ZC_GUILD_CHAT +0180 6 send CZ_REQ_HOSTILE_GUILD clif->pGuildOpposition +0181 3 recv guild_opposition_result ZC_ACK_REQ_HOSTILE_GUILD +0182 106 recv guild_member_add ZC_MEMBER_ADD +0183 10 send CZ_REQ_DELETE_RELATED_GUILD clif->pGuildDelAlliance +0184 10 recv guild_unally ZC_DELETE_RELATED_GUILD +0185 34 recv guild_alliance_added ZC_ADD_RELATED_GUILD +0186 -1 unkn +0187 6 send sync_request CH_UNKNOWN_PING chr->parse_char_ping +0188 8 recv item_upgrade ZC_ACK_ITEMREFINING +0189 4 recv no_teleport ZC_NOTIFY_MAPINFO +018A 4 send quit_request CZ_REQ_DISCONNECT clif->pQuitGame +018B 4 recv quit_response ZC_ACK_REQ_DISCONNECT +018C 29 recv sense_result ZC_MONSTER_INFO +018D -1 recv makable_item_list ZC_MAKABLEITEMLIST +018E 10 send make_item_request CZ_REQMAKINGITEM clif->pProduceMix +018F 6 recv refine_result ZC_ACK_REQMAKINGITEM +0190 90 send CZ_USE_SKILL_TOGROUND_WITHTALKBOX clif->pUseSkillToPosMoreInfo +0191 86 recv talkie_box ZC_TALKBOX_CHATCONTENTS +0192 24 recv map_change_cell ZC_UPDATE_MAPINFO +0193 6 send actor_name_request CZ_REQNAME_BYGID clif->pSolveCharName +0194 30 recv character_name ZC_ACK_REQNAME_BYGID +0195 102 recv actor_info ZC_ACK_REQNAMEALL +0196 9 recv actor_status_active ZC_MSG_STATE_CHANGE +0197 4 send gm_reset_state_skill CZ_RESET clif->pResetChar +0198 8 send gm_change_cell_type CZ_CHANGE_MAPTYPE clif->pGMChangeMapType +0199 4 recv map_property ZC_NOTIFY_MAPPROPERTY +019A 14 recv pvp_rank ZC_NOTIFY_RANKING +019B 10 recv unit_levelup ZC_NOTIFY_EFFECT +019C -1 send gm_broadcast_local CZ_LOCALBROADCAST clif->pLocalBroadcast +019D 6 send gm_change_effect_state CZ_CHANGE_EFFECTSTATE clif->pGMHide +019E 2 recv pet_capture_process ZC_START_CAPTURE +019F 6 send pet_capture CZ_TRYCAPTURE_MONSTER clif->pCatchPet +01A0 3 recv pet_capture_result ZC_TRYCAPTURE_MONSTER +01A1 3 send pet_menu CZ_COMMAND_PET clif->pPetMenu +01A2 35 recv pet_info ZC_PROPERTY_PET +01A3 5 recv pet_food ZC_FEED_PET +01A4 11 recv pet_info2 ZC_CHANGESTATE_PET +01A5 26 send pet_name CZ_RENAME_PET clif->pChangePetName +01A6 -1 recv egg_list ZC_PETEGG_LIST +01A7 4 send pet_hatch CZ_SELECT_PETEGG clif->pSelectEgg +01A8 4 send CZ_PETEGG_INFO +01A9 6 send CZ_PET_ACT clif->pSendEmotion +01AA 10 recv pet_emotion ZC_PET_ACT +01AB 12 recv stat_info ZC_PAR_CHANGE_USER +01AC 6 recv actor_trapped ZC_SKILL_UPDATE +01AD -1 recv arrowcraft_list ZC_MAKINGARROW_LIST +01AE 4 send make_arrow CZ_REQ_MAKINGARROW clif->pSelectArrow +01AF 4 send change_cart CZ_REQ_CHANGECART clif->pChangeCart +01B0 11 recv monster_typechange ZC_NPCSPRITE_CHANGE +01B1 7 recv ZC_SHOWDIGIT +01B2 -1 send shop_open CZ_REQ_OPENSTORE2 clif->pOpenVending +01B3 67 recv npc_image ZC_SHOW_IMAGE2 +01B4 12 recv guild_emblem_update ZC_CHANGE_GUILD +01B5 18 recv account_payment_info SC_BILLING_INFO +01B6 114 recv guild_info ZC_GUILD_INFO2 +01B7 6 send CZ_GUILD_ZENY +01B8 3 recv ZC_GUILD_ZENY_ACK +01B9 6 recv cast_cancelled ZC_DISPEL +01BA 26 send gm_remove CZ_REMOVE_AID clif->pGMShift +01BB 26 send gm_shift CZ_SHIFT clif->pGMShift +01BC 26 send gm_recall CZ_RECALL clif->pGMRecall +01BD 26 send gm_summon_player CZ_RECALL_GID clif->pGMRecall +01BE 2 recv AC_ASK_PNGAMEROOM +01BF 3 send CA_REPLY_PNGAMEROOM +01C0 2 send request_remain_time CZ_REQ_REMAINTIME +01C1 14 recv remain_time_info ZC_REPLY_REMAINTIME +01C2 10 recv ZC_INFO_REMAINTIME +01C3 -1 recv local_broadcast ZC_BROADCAST2 +01C4 22 recv storage_item_added ZC_ADD_ITEM_TO_STORE2 +01C5 22 recv cart_item_added ZC_ADD_ITEM_TO_CART2 +01C6 4 send CS_REQ_ENCRYPTION +01C7 2 recv SC_ACK_ENCRYPTION +01C8 13 recv item_used ZC_USE_ITEM_ACK2 +01C9 97 recv area_spell ZC_SKILL_ENTRY2 +01CA -1 send CZ_REQMAKINGHOMUN +01CB 9 send CZ_MONSTER_TALK +01CC 9 recv ZC_MONSTER_TALK +01CD 30 recv sage_autospell ZC_AUTOSPELLLIST +01CE 6 send auto_spell CZ_SELECTAUTOSPELL clif->pAutoSpell +01CF 28 recv devotion ZC_DEVOTIONLIST +01D0 8 recv revolving_entity ZC_SPIRITS +01D1 14 recv blade_stop ZC_BLADESTOP +01D2 10 recv combo_delay ZC_COMBODELAY +01D3 35 recv sound_effect ZC_SOUND +01D4 6 recv npc_talk_text ZC_OPEN_EDITDLGSTR +01D5 -1 send npc_talk_text CZ_INPUT_EDITDLGSTR clif->pNpcStringInput +01D6 4 recv map_property2 ZC_NOTIFY_MAPPROPERTY2 +01D7 11 recv player_equipment ZC_SPRITE_CHANGE2 +01D8 54 recv actor_exists ZC_NOTIFY_STANDENTRY2 +01D9 53 recv actor_connected ZC_NOTIFY_NEWENTRY2 +01DA 60 recv actor_moved ZC_NOTIFY_MOVEENTRY2 +01DB 2 send secure_login_key_request CA_REQ_HASH lclif->p->parse_CA_REQ_HASH +01DC -1 recv secure_login_key AC_ACK_HASH +01DD 47 send master_login CA_LOGIN2 lclif->p->parse_CA_LOGIN2 +01DE 33 recv skill_use ZC_NOTIFY_SKILL2 +01DF 6 send CZ_REQ_ACCOUNTNAME clif->pGMReqAccountName +01E0 30 recv GM_req_acc_name ZC_ACK_ACCOUNTNAME +01E1 8 recv revolving_entity ZC_SPIRITS2 +01E2 34 recv marriage_unknown ZC_REQ_COUPLE +01E3 14 send CZ_JOIN_COUPLE +01E4 2 recv marriage_unknown ZC_START_COUPLE +01E5 6 send CZ_REQ_JOIN_COUPLE +01E6 26 recv marriage_partner_name ZC_COUPLENAME +01E7 2 send novice_dori_dori CZ_DORIDORI clif->pNoviceDoriDori +01E8 28 send CZ_MAKE_GROUP2 clif->pCreateParty2 +01E9 81 recv party_join ZC_ADD_MEMBER_TO_GROUP2 +01EA 6 recv married ZC_CONGRATULATION +01EB 10 recv guild_location ZC_NOTIFY_POSITION_TO_GUILDM +01EC 26 recv guild_member_map_change ZC_GUILD_MEMBER_MAP_CHANGE +01ED 2 send novice_explosion_spirits CZ_CHOPOKGI clif->pNoviceExplosionSpirits +01EE -1 recv inventory_items_stackable ZC_NORMAL_ITEMLIST2 +01EF -1 recv cart_items_stackable ZC_CART_NORMAL_ITEMLIST2 +01F0 -1 recv storage_items_stackable ZC_STORE_NORMAL_ITEMLIST2 +01F1 -1 recv AC_NOTIFY_ERROR +01F2 20 recv guild_member_online_status ZC_UPDATE_CHARSTAT2 +01F3 10 recv misc_effect ZC_NOTIFY_EFFECT2 +01F4 32 recv deal_request ZC_REQ_EXCHANGE_ITEM2 +01F5 9 recv deal_begin ZC_ACK_EXCHANGE_ITEM2 +01F6 34 recv adopt_request ZC_REQ_BABY +01F7 14 send adopt_reply_request CZ_JOIN_BABY clif->pAdopt_reply +01F8 2 recv adopt_unknown ZC_START_BABY +01F9 6 send adopt_request CZ_REQ_JOIN_BABY clif->pAdopt_request +01FA 48 send master_login CA_LOGIN3 lclif->p->parse_CA_LOGIN3 +01FB 56 send CH_DELETE_CHAR2 chr->parse_char_delete_char_01fb +01FC -1 recv repair_list ZC_REPAIRITEMLIST +01FD 4 send repair_item CZ_REQ_ITEMREPAIR clif->pRepairItem +01FE 5 recv repair_result ZC_ACK_ITEMREPAIR +01FF 10 recv high_jump ZC_HIGHJUMP +0200 26 send CA_CONNECT_INFO_CHANGED lclif->p->parse_CA_CONNECT_INFO_CHANGED +0201 -1 recv friend_list ZC_FRIENDS_LIST +0202 26 send friend_request CZ_ADD_FRIENDS clif->pFriendsListAdd +0203 10 send friend_remove CZ_DELETE_FRIENDS clif->pFriendsListRemove +0204 18 send client_hash CA_EXE_HASHCHECK lclif->p->parse_CA_EXE_HASHCHECK +0205 26 recv divorced ZC_DIVORCE +0206 11 recv friend_logon ZC_FRIENDS_STATE +0207 34 recv friend_request ZC_REQ_ADD_FRIENDS +0208 11 send friend_response CZ_ACK_REQ_ADD_FRIENDS clif->pFriendsListReply +0209 36 recv friend_response ZC_ADD_FRIENDS_LIST +020A 10 recv friend_removed ZC_DELETE_FRIENDS +020B -1 unkn +020C -1 unkn +020D -1 recv character_ban_list HC_BLOCK_CHARACTER + +//2004-07-05aSakexe +0072 22 send received_characters CZ_ENTER clif->pWantToConnection +0085 8 send character_move CZ_REQUEST_MOVE clif->pWalkToXY +00A7 13 send item_use CZ_USE_ITEM clif->pUseItem +0113 15 send skill_use CZ_USE_SKILL clif->pUseSkillToId +0116 15 send skill_use_location CZ_USE_SKILL_TOGROUND clif->pUseSkillToPos +0190 95 send CZ_USE_SKILL_TOGROUND_WITHTALKBOX clif->pUseSkillToPosMoreInfo +0208 14 send friend_response CZ_ACK_REQ_ADD_FRIENDS clif->pFriendsListReply +020E 24 recv taekwon_packets ZC_STARSKILL + +//2004-07-13aSakexe +0072 39 send received_characters CZ_ENTER clif->pWantToConnection +0085 9 send character_move CZ_REQUEST_MOVE clif->pWalkToXY +009B 13 send actor_look_at CZ_CHANGE_DIRECTION clif->pChangeDir +009F 10 send item_take CZ_ITEM_PICKUP clif->pTakeItem +00A7 17 send item_use CZ_USE_ITEM clif->pUseItem +0113 19 send skill_use CZ_USE_SKILL clif->pUseSkillToId +0116 19 send skill_use_location CZ_USE_SKILL_TOGROUND clif->pUseSkillToPos +0190 99 send CZ_USE_SKILL_TOGROUND_WITHTALKBOX clif->pUseSkillToPosMoreInfo + +//2004-07-26aSakexe +0072 14 send received_characters CZ_ENTER clif->pWantToConnection +007E 33 send sync CZ_REQUEST_TIME clif->pTickSend +0085 20 send character_move CZ_REQUEST_MOVE clif->pWalkToXY +0089 15 send actor_action CZ_REQUEST_ACT clif->pActionRequest +008C 23 send public_chat CZ_REQUEST_CHAT clif->pGlobalMessage +0094 10 send actor_info_request CZ_REQNAME clif->pGetCharNameRequest +009B 6 send actor_look_at CZ_CHANGE_DIRECTION clif->pChangeDir +009F 13 send item_take CZ_ITEM_PICKUP clif->pTakeItem +00A2 103 send item_drop CZ_ITEM_THROW clif->pDropItem +00A7 12 send item_use CZ_USE_ITEM clif->pUseItem +00F3 -1 send storage_item_add CZ_MOVE_ITEM_FROM_BODY_TO_STORE clif->pMoveToKafra +00F5 17 send storage_item_remove CZ_MOVE_ITEM_FROM_STORE_TO_BODY clif->pMoveFromKafra +00F7 10 send storage_close CZ_REQUEST_TIME clif->pTickSend +0113 16 send skill_use CZ_USE_SKILL clif->pUseSkillToId +0116 2 send skill_use_location CZ_USE_SKILL_TOGROUND clif->pUseSkillToPos +0190 26 send CZ_USE_SKILL_TOGROUND_WITHTALKBOX clif->pUseSkillToPosMoreInfo +0193 9 send actor_name_request CZ_REQNAME_BYGID clif->pSolveCharName + +//2004-08-09aSakexe +0072 17 send received_characters CZ_ENTER clif->pWantToConnection +007E 37 send sync CZ_REQUEST_TIME clif->pTickSend +0085 26 send character_move CZ_REQUEST_MOVE clif->pWalkToXY +0089 12 send actor_action CZ_REQUEST_ACT clif->pActionRequest +008C 40 send public_chat CZ_REQUEST_CHAT clif->pGlobalMessage +0094 13 send actor_info_request CZ_REQNAME clif->pGetCharNameRequest +009B 15 send actor_look_at CZ_CHANGE_DIRECTION clif->pChangeDir +009F 12 send item_take CZ_ITEM_PICKUP clif->pTakeItem +00A2 120 send item_drop CZ_ITEM_THROW clif->pDropItem +00A7 11 send item_use CZ_USE_ITEM clif->pUseItem +00F5 24 send storage_item_remove CZ_MOVE_ITEM_FROM_STORE_TO_BODY clif->pMoveFromKafra +00F7 13 send storage_close CZ_REQUEST_TIME clif->pTickSend +0113 23 send skill_use CZ_USE_SKILL clif->pUseSkillToId +0190 26 send CZ_USE_SKILL_TOGROUND_WITHTALKBOX clif->pUseSkillToPosMoreInfo +0193 18 send actor_name_request CZ_REQNAME_BYGID clif->pSolveCharName + +//2004-08-16aSakexe +0212 26 send CZ_REQ_GIVE_MANNER_BYNAME clif->pGMRc +0213 26 send CZ_REQ_STATUS_GM clif->pCheck +0214 42 recv ZC_ACK_STATUS_GM + +//2004-08-17aSakexe +020F 10 send pvp_point CZ_REQ_PVPPOINT clif->pPVPInfo +0210 22 recv ZC_ACK_PVPPOINT + +//2004-09-06aSakexe +0072 20 send received_characters CZ_ENTER clif->pWantToConnection +007E 19 send sync CZ_REQUEST_TIME clif->pTickSend +0085 23 send character_move CZ_REQUEST_MOVE clif->pWalkToXY +0089 9 send actor_action CZ_REQUEST_ACT clif->pActionRequest +008C 105 send public_chat CZ_REQUEST_CHAT clif->pGlobalMessage +0094 17 send actor_info_request CZ_REQNAME clif->pGetCharNameRequest +009B 14 send actor_look_at CZ_CHANGE_DIRECTION clif->pChangeDir +009F -1 send item_take CZ_ITEM_PICKUP clif->pTakeItem +00A2 14 send item_drop CZ_ITEM_THROW clif->pDropItem +00A7 25 send item_use CZ_USE_ITEM clif->pUseItem +00F3 10 send storage_item_add CZ_MOVE_ITEM_FROM_BODY_TO_STORE clif->pMoveToKafra +00F5 34 send storage_item_remove CZ_MOVE_ITEM_FROM_STORE_TO_BODY clif->pMoveFromKafra +00F7 2 send storage_close CZ_REQUEST_TIME clif->pTickSend +0113 11 send skill_use CZ_USE_SKILL clif->pUseSkillToId +0116 11 send skill_use_location CZ_USE_SKILL_TOGROUND clif->pUseSkillToPos +0190 22 send CZ_USE_SKILL_TOGROUND_WITHTALKBOX clif->pUseSkillToPosMoreInfo +0193 17 send actor_name_request CZ_REQNAME_BYGID clif->pSolveCharName + +//2004-09-20aSakexe +0072 18 send received_characters CZ_ENTER clif->pWantToConnection +007E 25 send sync CZ_REQUEST_TIME clif->pTickSend +0085 9 send character_move CZ_REQUEST_MOVE clif->pWalkToXY +0089 14 send actor_action CZ_REQUEST_ACT clif->pActionRequest +008C 109 send public_chat CZ_REQUEST_CHAT clif->pGlobalMessage +0094 19 send actor_info_request CZ_REQNAME clif->pGetCharNameRequest +009B 10 send actor_look_at CZ_CHANGE_DIRECTION clif->pChangeDir +00A2 10 send item_drop CZ_ITEM_THROW clif->pDropItem +00A7 29 send item_use CZ_USE_ITEM clif->pUseItem +00F3 18 send storage_item_add CZ_MOVE_ITEM_FROM_BODY_TO_STORE clif->pMoveToKafra +00F5 32 send storage_item_remove CZ_MOVE_ITEM_FROM_STORE_TO_BODY clif->pMoveFromKafra +0113 14 send skill_use CZ_USE_SKILL clif->pUseSkillToId +0116 14 send skill_use_location CZ_USE_SKILL_TOGROUND clif->pUseSkillToPos +0190 14 send CZ_USE_SKILL_TOGROUND_WITHTALKBOX clif->pUseSkillToPosMoreInfo +0193 12 send actor_name_request CZ_REQNAME_BYGID clif->pSolveCharName + +//2004-10-05aSakexe +0072 17 send received_characters CZ_ENTER clif->pWantToConnection +007E 16 send sync CZ_REQUEST_TIME clif->pTickSend +0089 6 send actor_action CZ_REQUEST_ACT clif->pActionRequest +008C 103 send public_chat CZ_REQUEST_CHAT clif->pGlobalMessage +0094 14 send actor_info_request CZ_REQNAME clif->pGetCharNameRequest +009B 15 send actor_look_at CZ_CHANGE_DIRECTION clif->pChangeDir +00A2 12 send item_drop CZ_ITEM_THROW clif->pDropItem +00A7 23 send item_use CZ_USE_ITEM clif->pUseItem +00F3 13 send storage_item_add CZ_MOVE_ITEM_FROM_BODY_TO_STORE clif->pMoveToKafra +00F5 33 send storage_item_remove CZ_MOVE_ITEM_FROM_STORE_TO_BODY clif->pMoveFromKafra +0113 10 send skill_use CZ_USE_SKILL clif->pUseSkillToId +0116 10 send skill_use_location CZ_USE_SKILL_TOGROUND clif->pUseSkillToPos +0190 20 send CZ_USE_SKILL_TOGROUND_WITHTALKBOX clif->pUseSkillToPosMoreInfo +0193 26 send actor_name_request CZ_REQNAME_BYGID clif->pSolveCharName + +//2004-10-25aSakexe +0072 13 send received_characters CZ_ENTER clif->pWantToConnection +007E 13 send sync CZ_REQUEST_TIME clif->pTickSend +0085 15 send character_move CZ_REQUEST_MOVE clif->pWalkToXY +008C 108 send public_chat CZ_REQUEST_CHAT clif->pGlobalMessage +0094 12 send actor_info_request CZ_REQNAME clif->pGetCharNameRequest +009B 10 send actor_look_at CZ_CHANGE_DIRECTION clif->pChangeDir +00A2 16 send item_drop CZ_ITEM_THROW clif->pDropItem +00A7 28 send item_use CZ_USE_ITEM clif->pUseItem +00F3 15 send storage_item_add CZ_MOVE_ITEM_FROM_BODY_TO_STORE clif->pMoveToKafra +00F5 29 send storage_item_remove CZ_MOVE_ITEM_FROM_STORE_TO_BODY clif->pMoveFromKafra +0113 9 send skill_use CZ_USE_SKILL clif->pUseSkillToId +0116 9 send skill_use_location CZ_USE_SKILL_TOGROUND clif->pUseSkillToPos +0190 26 send CZ_USE_SKILL_TOGROUND_WITHTALKBOX clif->pUseSkillToPosMoreInfo +0193 22 send actor_name_request CZ_REQNAME_BYGID clif->pSolveCharName + +//2004-11-01aSakexe +0084 -1 recv ZC_REFUSE_QUIT +0215 6 recv gospel_buff_aligned ZC_SKILLMSG + +//2004-11-08aSakexe +0084 2 recv ZC_REFUSE_QUIT +0216 6 recv adopt_reply ZC_BABYMSG +0217 2 send rank_blacksmith CZ_BLACKSMITH_RANK clif->pBlacksmith +0218 2 send rank_alchemist CZ_ALCHEMIST_RANK clif->pAlchemist +0219 282 recv top10_blacksmith_rank ZC_BLACKSMITH_RANK +021A 282 recv top10_alchemist_rank ZC_ALCHEMIST_RANK +021B 10 recv blacksmith_points ZC_BLACKSMITH_POINT +021C 10 recv alchemist_point ZC_ALCHEMIST_POINT + +//2004-11-15aSakexe +021D 6 send less_effect CZ_LESSEFFECT clif->pLessEffect + +//2004-11-29aSakexe +0072 22 send received_characters CZ_ENTER clif->pWantToConnection +007E 30 send sync CZ_REQUEST_TIME clif->pTickSend +0085 -1 send character_move CZ_REQUEST_MOVE clif->pWalkToXY +0089 7 send actor_action CZ_REQUEST_ACT clif->pActionRequest +008C 13 send public_chat CZ_REQUEST_CHAT clif->pGlobalMessage +0094 14 send actor_info_request CZ_REQNAME clif->pGetCharNameRequest +009B 2 send actor_look_at CZ_CHANGE_DIRECTION clif->pChangeDir +009F 18 send item_take CZ_ITEM_PICKUP clif->pTakeItem +00A2 7 send item_drop CZ_ITEM_THROW clif->pDropItem +00A7 7 send item_use CZ_USE_ITEM clif->pUseItem +00F3 8 send storage_item_add CZ_MOVE_ITEM_FROM_BODY_TO_STORE clif->pMoveToKafra +00F5 29 send storage_item_remove CZ_MOVE_ITEM_FROM_STORE_TO_BODY clif->pMoveFromKafra +00F7 14 send storage_close CZ_REQUEST_TIME clif->pTickSend +0113 110 send skill_use CZ_USE_SKILL clif->pUseSkillToId +0116 12 send skill_use_location CZ_USE_SKILL_TOGROUND clif->pUseSkillToPos +0190 15 send CZ_USE_SKILL_TOGROUND_WITHTALKBOX clif->pUseSkillToPosMoreInfo +0193 21 send actor_name_request CZ_REQNAME_BYGID clif->pSolveCharName +0221 -1 recv upgrade_list ZC_NOTIFY_WEAPONITEMLIST +0222 6 send refine_item CZ_REQ_WEAPONREFINE clif->pWeaponRefine +0223 8 recv upgrade_message ZC_ACK_WEAPONREFINE + +//2004-12-13aSakexe +0066 3 send char_login CH_SELECT_CHAR chr->parse_char_select +0070 3 recv character_deletion_failed HC_REFUSE_DELETECHAR +01CA 3 send CZ_REQMAKINGHOMUN +021E 6 recv ZC_LESSEFFECT +021F 66 recv ZC_NOTIFY_PKINFO +0220 10 recv ZC_NOTIFY_CRAZYKILLER + +//2005-01-10bSakexe +0072 26 send received_characters CZ_ENTER clif->pWantToConnection +007E 114 send sync CZ_REQUEST_TIME clif->pTickSend +0085 23 send character_move CZ_REQUEST_MOVE clif->pWalkToXY +0089 9 send actor_action CZ_REQUEST_ACT clif->pActionRequest +008C 8 send public_chat CZ_REQUEST_CHAT clif->pGlobalMessage +0094 20 send actor_info_request CZ_REQNAME clif->pGetCharNameRequest +009B 32 send actor_look_at CZ_CHANGE_DIRECTION clif->pChangeDir +009F 17 send item_take CZ_ITEM_PICKUP clif->pTakeItem +00A2 11 send item_drop CZ_ITEM_THROW clif->pDropItem +00A7 13 send item_use CZ_USE_ITEM clif->pUseItem +00F3 -1 send storage_item_add CZ_MOVE_ITEM_FROM_BODY_TO_STORE clif->pMoveToKafra +00F5 9 send storage_item_remove CZ_MOVE_ITEM_FROM_STORE_TO_BODY clif->pMoveFromKafra +00F7 21 send storage_close CZ_REQUEST_TIME clif->pTickSend +0113 34 send skill_use CZ_USE_SKILL clif->pUseSkillToId +0116 20 send skill_use_location CZ_USE_SKILL_TOGROUND clif->pUseSkillToPos +0190 20 send CZ_USE_SKILL_TOGROUND_WITHTALKBOX clif->pUseSkillToPosMoreInfo +0193 2 send actor_name_request CZ_REQNAME_BYGID clif->pSolveCharName + +//2005-03-28aSakexe +0224 10 recv taekwon_rank ZC_TAEKWON_POINT +0225 2 send rank_taekwon CZ_TAEKWON_RANK clif->pTaekwon +0226 282 recv top10_taekwon_rank ZC_TAEKWON_RANK + +//2005-04-04aSakexe +0227 18 recv gameguard_request ZC_GAME_GUARD +0228 18 send CZ_ACK_GAME_GUARD + +//2005-04-11aSakexe +0229 15 recv character_status ZC_STATE_CHANGE3 +022A 58 recv actor_exists ZC_NOTIFY_STANDENTRY3 +022B 57 recv actor_connected ZC_NOTIFY_NEWENTRY3 +022C 64 recv actor_moved ZC_NOTIFY_MOVEENTRY3 + +//2005-04-25aSakexe +022D 5 send homunculus_command CZ_COMMAND_MER clif->pHomMenu +0232 9 send actor_move CZ_REQUEST_MOVENPC clif->pHomMoveTo +0233 11 send slave_attack CZ_REQUEST_ACTNPC clif->pHomAttack +0234 6 send slave_move_to_master CZ_REQUEST_MOVETOOWNER clif->pHomMoveToMaster + +//2005-05-09aSakexe +0072 25 send received_characters CZ_ENTER clif->pWantToConnection +007E 102 send sync CZ_REQUEST_TIME clif->pTickSend +0085 11 send character_move CZ_REQUEST_MOVE clif->pWalkToXY +0089 8 send actor_action CZ_REQUEST_ACT clif->pActionRequest +008C 11 send public_chat CZ_REQUEST_CHAT clif->pGlobalMessage +0094 14 send actor_info_request CZ_REQNAME clif->pGetCharNameRequest +009B 26 send actor_look_at CZ_CHANGE_DIRECTION clif->pChangeDir +009F 14 send item_take CZ_ITEM_PICKUP clif->pTakeItem +00A2 15 send item_drop CZ_ITEM_THROW clif->pDropItem +00A7 8 send item_use CZ_USE_ITEM clif->pUseItem +00F5 8 send storage_item_remove CZ_MOVE_ITEM_FROM_STORE_TO_BODY clif->pMoveFromKafra +00F7 22 send storage_close CZ_REQUEST_TIME clif->pTickSend +0113 22 send skill_use CZ_USE_SKILL clif->pUseSkillToId +0116 10 send skill_use_location CZ_USE_SKILL_TOGROUND clif->pUseSkillToPos +0190 19 send CZ_USE_SKILL_TOGROUND_WITHTALKBOX clif->pUseSkillToPosMoreInfo + +//2005-05-23aSakexe +022E 69 recv homunculus_property ZC_PROPERTY_HOMUN +0230 12 recv homunculus_info ZC_CHANGESTATE_MER + +//2005-05-30aSakexe +022E 71 recv homunculus_property ZC_PROPERTY_HOMUN +0235 -1 recv skills_list ZC_HOSKILLINFO_LIST +0236 10 recv ZC_KILLER_POINT +0237 2 send rank_killer CZ_KILLER_RANK clif->pRankingPk +0238 282 recv top10_pk_rank ZC_KILLER_RANK + +//2005-05-31aSakexe +0216 2 recv adopt_reply ZC_BABYMSG +0239 11 recv skill_update ZC_HOSKILLINFO_UPDATE + +//2005-06-08aSakexe +0216 6 recv adopt_reply ZC_BABYMSG +0217 2 send rank_blacksmith CZ_BLACKSMITH_RANK clif->pBlacksmith +022F 5 recv homunculus_food ZC_FEED_MER +0231 26 send homunculus_name CZ_RENAME_MER clif->pChangeHomunculusName +023A 4 recv storage_password_request ZC_REQ_STORE_PASSWORD +023B 36 send storage_password CZ_ACK_STORE_PASSWORD clif->pStoragePassword +023C 6 recv storage_password_result ZC_RESULT_STORE_PASSWORD + +//2005-06-22aSakexe +022E 71 recv homunculus_property ZC_PROPERTY_HOMUN + +//2005-06-28aSakexe +0072 34 send received_characters CZ_ENTER clif->pWantToConnection +007E 113 send sync CZ_REQUEST_TIME clif->pTickSend +0085 17 send character_move CZ_REQUEST_MOVE clif->pWalkToXY +0089 13 send actor_action CZ_REQUEST_ACT clif->pActionRequest +008C 8 send public_chat CZ_REQUEST_CHAT clif->pGlobalMessage +0094 31 send actor_info_request CZ_REQNAME clif->pGetCharNameRequest +009B 32 send actor_look_at CZ_CHANGE_DIRECTION clif->pChangeDir +009F 19 send item_take CZ_ITEM_PICKUP clif->pTakeItem +00A2 9 send item_drop CZ_ITEM_THROW clif->pDropItem +00A7 11 send item_use CZ_USE_ITEM clif->pUseItem +00F5 13 send storage_item_remove CZ_MOVE_ITEM_FROM_STORE_TO_BODY clif->pMoveFromKafra +00F7 18 send storage_close CZ_REQUEST_TIME clif->pTickSend +0113 33 send skill_use CZ_USE_SKILL clif->pUseSkillToId +0116 12 send skill_use_location CZ_USE_SKILL_TOGROUND clif->pUseSkillToPos +0190 24 send CZ_USE_SKILL_TOGROUND_WITHTALKBOX clif->pUseSkillToPosMoreInfo +0216 -1 recv adopt_reply ZC_BABYMSG +023D -1 recv AC_EVENT_RESULT +023E 4 recv storage_password_request HC_REQUEST_CHARACTER_PASSWORD + +//2005-07-18aSakexe +0072 19 send received_characters CZ_ENTER clif->pWantToConnection +007E 110 send sync CZ_REQUEST_TIME clif->pTickSend +0085 11 send character_move CZ_REQUEST_MOVE clif->pWalkToXY +0089 7 send actor_action CZ_REQUEST_ACT clif->pActionRequest +008C 11 send public_chat CZ_REQUEST_CHAT clif->pGlobalMessage +0094 21 send actor_info_request CZ_REQNAME clif->pGetCharNameRequest +009B 31 send actor_look_at CZ_CHANGE_DIRECTION clif->pChangeDir +009F 12 send item_take CZ_ITEM_PICKUP clif->pTakeItem +00A2 18 send item_drop CZ_ITEM_THROW clif->pDropItem +00A7 15 send item_use CZ_USE_ITEM clif->pUseItem +00F5 7 send storage_item_remove CZ_MOVE_ITEM_FROM_STORE_TO_BODY clif->pMoveFromKafra +00F7 13 send storage_close CZ_REQUEST_TIME clif->pTickSend +0113 30 send skill_use CZ_USE_SKILL clif->pUseSkillToId +0116 12 send skill_use_location CZ_USE_SKILL_TOGROUND clif->pUseSkillToPos +0190 21 send CZ_USE_SKILL_TOGROUND_WITHTALKBOX clif->pUseSkillToPosMoreInfo +0216 6 recv adopt_reply ZC_BABYMSG +023F 2 send mailbox_open CZ_MAIL_GET_LIST clif->pMail_refreshinbox +0240 8 recv mail_refreshinbox ZC_MAIL_REQ_GET_LIST +0241 6 send mail_read CZ_MAIL_OPEN clif->pMail_read +0242 -1 recv mail_read ZC_MAIL_REQ_OPEN +0243 6 send mail_delete CZ_MAIL_DELETE clif->pMail_delete +0244 6 send mail_attachment_get CZ_MAIL_GET_ITEM clif->pMail_getattach +0245 7 recv mail_getattachment ZC_MAIL_REQ_GET_ITEM +0246 4 send mail_remove CZ_MAIL_RESET_ITEM clif->pMail_winopen +0247 8 send mail_attachment_set CZ_MAIL_ADD_ITEM clif->pMail_setattach +0248 68 send mail_send CZ_MAIL_SEND clif->pMail_send +0249 3 recv mail_send ZC_MAIL_REQ_SEND +024A 70 recv mail_new ZC_MAIL_RECEIVE +024B 4 send auction_add_item_cancel CZ_AUCTION_CREATE clif->pAuction_cancelreg +024C 8 send auction_add_item CZ_AUCTION_ADD_ITEM clif->pAuction_setitem +024D 14 send auction_create CZ_AUCTION_ADD clif->pAuction_register +024E 6 send auction_cancel CZ_AUCTION_ADD_CANCEL clif->pAuction_cancel +024F 10 send auction_buy CZ_AUCTION_BUY clif->pAuction_bid +0250 3 recv auction_result ZC_AUCTION_RESULT +0251 2 send auction_search CZ_AUCTION_ITEM_SEARCH clif->pAuction_search +0252 -1 recv auction_item_request_search ZC_AUCTION_ITEM_REQ_SEARCH + +//2005-07-19bSakexe +0072 34 send received_characters CZ_ENTER clif->pWantToConnection +007E 113 send sync CZ_REQUEST_TIME clif->pTickSend +0085 17 send character_move CZ_REQUEST_MOVE clif->pWalkToXY +0089 13 send actor_action CZ_REQUEST_ACT clif->pActionRequest +008C 8 send public_chat CZ_REQUEST_CHAT clif->pGlobalMessage +0094 31 send actor_info_request CZ_REQNAME clif->pGetCharNameRequest +009B 32 send actor_look_at CZ_CHANGE_DIRECTION clif->pChangeDir +009F 19 send item_take CZ_ITEM_PICKUP clif->pTakeItem +00A2 9 send item_drop CZ_ITEM_THROW clif->pDropItem +00A7 11 send item_use CZ_USE_ITEM clif->pUseItem +00F5 13 send storage_item_remove CZ_MOVE_ITEM_FROM_STORE_TO_BODY clif->pMoveFromKafra +00F7 18 send storage_close CZ_REQUEST_TIME clif->pTickSend +0113 33 send skill_use CZ_USE_SKILL clif->pUseSkillToId +0116 12 send skill_use_location CZ_USE_SKILL_TOGROUND clif->pUseSkillToPos +0190 24 send CZ_USE_SKILL_TOGROUND_WITHTALKBOX clif->pUseSkillToPosMoreInfo + +//2005-08-01aSakexe +0245 3 recv mail_getattachment ZC_MAIL_REQ_GET_ITEM +0251 4 send auction_search CZ_AUCTION_ITEM_SEARCH clif->pAuction_search + +//2005-08-08aSakexe +024D 12 send auction_create CZ_AUCTION_ADD clif->pAuction_register +024E 4 send auction_cancel CZ_AUCTION_ADD_CANCEL clif->pAuction_cancel + +//2005-08-17aSakexe +0253 3 recv ZC_STARPLACE +0254 3 send CZ_AGREE_STARPLACE clif->pFeelSaveOk + +//2005-08-29aSakexe +0240 -1 recv mail_refreshinbox ZC_MAIL_REQ_GET_LIST +0248 -1 send mail_send CZ_MAIL_SEND clif->pMail_send +0255 5 recv mail_setattachment ZC_ACK_MAIL_ADD_ITEM +0256 -1 recv auction_add_item ZC_ACK_AUCTION_ADD_ITEM +0257 8 recv mail_delete ZC_ACK_MAIL_DELETE + +//2005-09-12bSakexe +0256 5 recv auction_add_item ZC_ACK_AUCTION_ADD_ITEM +0258 2 send CA_REQ_GAME_GUARD_CHECK +0259 3 recv gameguard_grant AC_ACK_GAME_GUARD + +//2005-10-10aSakexe +020E 32 recv taekwon_packets ZC_STARSKILL +025A -1 recv cooking_list ZC_MAKINGITEM_LIST +025B 6 send cook_request CZ_REQ_MAKINGITEM clif->pCooking + +//2005-10-13aSakexe +007A 6 recv changeToInGameState ZC_NOTIFY_ACTENTRY +0251 32 send auction_search CZ_AUCTION_ITEM_SEARCH clif->pAuction_search +025C 4 send auction_info_self CZ_AUCTION_REQ_MY_INFO clif->pAuction_buysell + +//2005-10-17aSakexe +007A 58 recv changeToInGameState ZC_NOTIFY_ACTENTRY +025D 6 send auction_my_sell_stop CZ_AUCTION_REQ_MY_SELL_STOP clif->pAuction_close +025E 4 recv ZC_AUCTION_ACK_MY_SELL_STOP + +//2005-10-24aSakexe +025F 6 recv auction_windows ZC_AUCTION_WINDOWS +0260 6 recv mail_window ZC_MAIL_WINDOWS + +//2005-11-07aSakexe +024E 6 send auction_cancel CZ_AUCTION_ADD_CANCEL clif->pAuction_cancel +0251 34 send auction_search CZ_AUCTION_ITEM_SEARCH clif->pAuction_search + +//2006-01-09aSakexe +0261 11 recv AC_REQ_LOGIN_OLDEKEY +0262 11 recv AC_REQ_LOGIN_NEWEKEY +0263 11 recv AC_REQ_LOGIN_CARDPASS +0264 20 send CA_ACK_LOGIN_OLDEKEY +0265 20 send CA_ACK_LOGIN_NEWEKEY +0266 30 send CA_ACK_LOGIN_CARDPASS +0267 4 recv AC_ACK_EKEY_FAIL_NOTEXIST +0268 4 recv AC_ACK_EKEY_FAIL_NOTUSESEKEY +0269 4 recv AC_ACK_EKEY_FAIL_NOTUSEDEKEY +026A 4 recv AC_ACK_EKEY_FAIL_AUTHREFUSE +026B 4 recv AC_ACK_EKEY_FAIL_INPUTEKEY +026C 4 recv AC_ACK_EKEY_FAIL_NOTICE +026D 4 recv AC_ACK_EKEY_FAIL_NEEDCARDPASS +026F 2 recv AC_ACK_FIRST_LOGIN +0270 2 recv AC_REQ_LOGIN_ACCOUNT_INFO +0271 38 send CA_ACK_LOGIN_ACCOUNT_INFO +0272 44 recv AC_ACK_PT_ID_INFO + +//2006-01-26aSakexe +0271 40 send CA_ACK_LOGIN_ACCOUNT_INFO + +//2006-03-06aSakexe +0273 6 send mail_return CZ_REQ_MAIL_RETURN clif->pMail_return +0274 8 recv mail_return ZC_ACK_MAIL_RETURN + +//2006-03-13aSakexe +0273 30 send mail_return CZ_REQ_MAIL_RETURN clif->pMail_return + +//2006-03-27aSakexe +0072 26 send received_characters CZ_ENTER clif->pWantToConnection +007E 120 send sync CZ_REQUEST_TIME clif->pTickSend +0085 12 send character_move CZ_REQUEST_MOVE clif->pWalkToXY +0089 13 send actor_action CZ_REQUEST_ACT clif->pActionRequest +008C 12 send public_chat CZ_REQUEST_CHAT clif->pGlobalMessage +0094 23 send actor_info_request CZ_REQNAME clif->pGetCharNameRequest +009B 37 send actor_look_at CZ_CHANGE_DIRECTION clif->pChangeDir +009F 24 send item_take CZ_ITEM_PICKUP clif->pTakeItem +00A2 11 send item_drop CZ_ITEM_THROW clif->pDropItem +00A7 15 send item_use CZ_USE_ITEM clif->pUseItem +00F5 13 send storage_item_remove CZ_MOVE_ITEM_FROM_STORE_TO_BODY clif->pMoveFromKafra +00F7 26 send storage_close CZ_REQUEST_TIME clif->pTickSend +0113 40 send skill_use CZ_USE_SKILL clif->pUseSkillToId +0116 17 send skill_use_location CZ_USE_SKILL_TOGROUND clif->pUseSkillToPos +0190 18 send CZ_USE_SKILL_TOGROUND_WITHTALKBOX clif->pUseSkillToPosMoreInfo + +//2006-10-23aSakexe +006D 110 recv character_creation_successful HC_ACCEPT_MAKECHAR + +//2006-04-24aSakexe to 2007-01-02aSakexe +023E 8 recv storage_password_request HC_REQUEST_CHARACTER_PASSWORD +0277 84 send CA_LOGIN_PCBANG lclif->p->parse_CA_LOGIN_PCBANG +0278 2 recv ZC_NOTIFY_PCBANG +0279 2 send CZ_HUNTINGLIST +027A -1 recv ZC_HUNTINGLIST +027B 14 recv premium_rates_info ZC_PCBANG_EFFECT +027C 60 send CA_LOGIN4 lclif->p->parse_CA_LOGIN4 +027D 62 recv ZC_PROPERTY_MERCE +027E -1 recv ZC_SHANDA_PROTECT +027F 8 send CA_CLIENT_TYPE +0280 12 recv ZC_GANGSI_POINT +0281 4 send CZ_GANGSI_RANK +0282 284 recv ZC_GANGSI_RANK +0283 6 recv account_id ZC_AID +0284 14 recv GANSI_RANK ZC_NOTIFY_EFFECT3 +0285 6 recv ZC_DEATH_QUESTION +0286 4 send CZ_DEATH_QUESTION +0287 -1 recv cash_dealer ZC_PC_CASH_POINT_ITEMLIST +0288 6 send CZ_PC_BUY_CASH_POINT_ITEM clif->pcashshop_buy +0289 8 recv cash_buy_fail ZC_PC_CASH_POINT_UPDATE +028A 18 recv character_status ZC_NPC_SHOWEFST_UPDATE +028B -1 recv HC_CHARNOTBEENSELECTED +028C 46 send CH_SELECT_CHAR_GOINGTOBEUSED +028D 34 send CH_REQ_IS_VALID_CHARNAME chr->parse_char_rename_char2 +028E 4 recv HC_ACK_IS_VALID_CHARNAME +028F 6 send CH_REQ_CHANGE_CHARNAME chr->parse_char_rename_char_confirm +0290 4 recv HC_ACK_CHANGE_CHARNAME +0291 4 recv message_string ZC_MSG +0292 2 send CZ_STANDING_RESURRECTION clif->pAutoRevive +0293 70 recv boss_map_info ZC_BOSS_INFO +0294 10 recv book_read ZC_READ_BOOK +0295 -1 recv inventory_items_nonstackable ZC_EQUIPMENT_ITEMLIST2 +0296 -1 recv storage_items_nonstackable ZC_STORE_EQUIPMENT_ITEMLIST2 +0297 -1 recv cart_items_nonstackable ZC_CART_EQUIPMENT_ITEMLIST2 +0298 8 recv rental_time ZC_CASH_TIME_COUNTER +0299 6 recv rental_expired ZC_CASH_ITEM_DELETE +029A 27 recv inventory_item_added ZC_ITEM_PICKUP_ACK2 +029C 66 recv ZC_MER_PROPERTY +029D -1 recv skills_list ZC_MER_SKILLINFO_LIST +029E 11 recv ZC_MER_SKILLINFO_UPDATE +029F 3 send CZ_MER_COMMAND clif->pmercenary_action +02A0 -1 send CZ_UNUSED_MER_USE_SKILL +02A1 -1 send CZ_UNUSED_MER_UPGRADE_SKILLLEVEL +02A2 8 recv stat_info ZC_MER_PAR_CHANGE + +//2007-01-08aSakexe +0072 30 send received_characters CZ_ENTER clif->pWantToConnection +007E 120 send sync CZ_REQUEST_TIME clif->pTickSend +0085 14 send character_move CZ_REQUEST_MOVE clif->pWalkToXY +0089 11 send actor_action CZ_REQUEST_ACT clif->pActionRequest +008C 17 send public_chat CZ_REQUEST_CHAT clif->pGlobalMessage +0094 17 send actor_info_request CZ_REQNAME clif->pGetCharNameRequest +009B 35 send actor_look_at CZ_CHANGE_DIRECTION clif->pChangeDir +009F 21 send item_take CZ_ITEM_PICKUP clif->pTakeItem +00A2 10 send item_drop CZ_ITEM_THROW clif->pDropItem +00A7 8 send item_use CZ_USE_ITEM clif->pUseItem +00F5 11 send storage_item_remove CZ_MOVE_ITEM_FROM_STORE_TO_BODY clif->pMoveFromKafra +00F7 15 send storage_close CZ_REQUEST_TIME clif->pTickSend +0113 40 send skill_use CZ_USE_SKILL clif->pUseSkillToId +0116 19 send skill_use_location CZ_USE_SKILL_TOGROUND clif->pUseSkillToPos +0190 10 send CZ_USE_SKILL_TOGROUND_WITHTALKBOX clif->pUseSkillToPosMoreInfo + +//2007-01-22aSakexe +02A3 18 recv ZC_GAMEGUARD_LINGO_KEY +02A4 2 send CZ_GAMEGUARD_LINGO_READY + +//2007-01-29aSakexe +029B 72 recv mercenary_init ZC_MER_INIT +02A3 -1 recv ZC_GAMEGUARD_LINGO_KEY +02A4 -1 send CZ_GAMEGUARD_LINGO_READY +02A5 8 send CZ_KSY_EVENT + +//2007-02-05aSakexe +02AA 4 recv cash_password_request ZC_REQ_CASH_PASSWORD +02AB 36 send CZ_ACK_CASH_PASSWORD +02AC 6 recv cash_password_result ZC_RESULT_CASH_PASSWORD + +//2007-02-12aSakexe +0072 25 send received_characters CZ_ENTER clif->pWantToConnection +007E 102 send sync CZ_REQUEST_TIME clif->pTickSend +0085 11 send character_move CZ_REQUEST_MOVE clif->pWalkToXY +0089 8 send actor_action CZ_REQUEST_ACT clif->pActionRequest +008C 11 send public_chat CZ_REQUEST_CHAT clif->pGlobalMessage +0094 14 send actor_info_request CZ_REQNAME clif->pGetCharNameRequest +009B 26 send actor_look_at CZ_CHANGE_DIRECTION clif->pChangeDir +009F 14 send item_take CZ_ITEM_PICKUP clif->pTakeItem +00A2 15 send item_drop CZ_ITEM_THROW clif->pDropItem +00A7 8 send item_use CZ_USE_ITEM clif->pUseItem +00F5 8 send storage_item_remove CZ_MOVE_ITEM_FROM_STORE_TO_BODY clif->pMoveFromKafra +00F7 22 send storage_close CZ_REQUEST_TIME clif->pTickSend +0113 22 send skill_use CZ_USE_SKILL clif->pUseSkillToId +0116 10 send skill_use_location CZ_USE_SKILL_TOGROUND clif->pUseSkillToPos +0190 19 send CZ_USE_SKILL_TOGROUND_WITHTALKBOX clif->pUseSkillToPosMoreInfo + +//2007-05-07aSakexe +01FD 15 send repair_item CZ_REQ_ITEMREPAIR clif->pRepairItem + +//2007-02-27aSakexe to 2007-10-02aSakexe +0288 10 send CZ_PC_BUY_CASH_POINT_ITEM clif->pcashshop_buy +0289 12 recv cash_buy_fail ZC_PC_CASH_POINT_UPDATE +02A6 22 recv gameguard_request ZC_HACKSH_CPX_MSG +02A7 22 send CZ_HACKSH_CPX_MSG +02A8 162 recv ZC_HACKSHIELD_CRC_MSG +02A9 58 send CZ_HACKSHIELD_CRC_MSG +02AD 8 recv login_pin_code_request AC_REQUEST_SECOND_PASSWORD +02B0 85 send master_login CA_LOGIN_HAN lclif->p->parse_CA_LOGIN_HAN +02B1 -1 recv quest_all_list ZC_ALL_QUEST_LIST +02B2 -1 recv quest_all_mission ZC_ALL_QUEST_MISSION +02B3 107 recv quest_add ZC_ADD_QUEST +02B4 6 recv quest_delete ZC_DEL_QUEST +02B5 -1 recv quest_update_mission_hunt ZC_UPDATE_MISSION_HUNT +02B6 7 send send_quest_state CZ_ACTIVE_QUEST clif->pquestStateAck +02B7 7 recv quest_active ZC_ACTIVE_QUEST +02B8 22 recv party_show_picker ZC_ITEM_PICKUP_PARTY +02B9 191 recv hotkeys ZC_SHORTCUT_KEY_LIST +02BA 11 send hotkey_change CZ_SHORTCUT_KEY_CHANGE clif->pHotkey +02BB 8 recv ZC_EQUIPITEM_DAMAGED +02BC 6 recv ZC_NOTIFY_PCBANG_PLAYING_TIME +02BF 10 recv ZC_SRPACKETR2_INIT +02C0 2 send CZ_SRPACKETR2_START +02C1 -1 recv npc_chat ZC_NPC_CHAT +02C2 -1 recv ZC_FORMATSTRING_MSG +02C4 26 send party_join_request_by_name CZ_PARTY_JOIN_REQ clif->pPartyInvite2 +02C5 30 recv party_invite_result ZC_PARTY_JOIN_REQ_ACK +02C6 30 recv party_invite ZC_PARTY_JOIN_REQ +02C7 7 send CZ_PARTY_JOIN_REQ_ACK clif->pReplyPartyInvite2 +02C8 3 send CZ_PARTY_CONFIG clif->pPartyTick +02C9 3 recv party_allow_invite ZC_PARTY_CONFIG +02CA 3 recv login_error_game_login_server HC_REFUSE_SELECTCHAR +02CB 20 recv instance_window_start ZC_MEMORIALDUNGEON_SUBSCRIPTION_INFO +02CC 4 recv instance_window_queue ZC_MEMORIALDUNGEON_SUBSCRIPTION_NOTIFY +02CD 26 recv instance_window_join ZC_MEMORIALDUNGEON_INFO +02CE 10 recv instance_window_leave ZC_MEMORIALDUNGEON_NOTIFY +02CF 6 send CZ_MEMORIALDUNGEON_COMMAND +02D0 -1 recv inventory_items_nonstackable ZC_EQUIPMENT_ITEMLIST3 +02D1 -1 recv storage_items_nonstackable ZC_STORE_EQUIPMENT_ITEMLIST3 +02D2 -1 recv cart_items_nonstackable ZC_CART_EQUIPMENT_ITEMLIST3 +02D3 4 recv ZC_NOTIFY_BIND_ON_EQUIP +02D4 29 recv inventory_item_added ZC_ITEM_PICKUP_ACK3 +02D5 2 recv ISVR_DISCONNECT ZC_ISVR_DISCONNECT +02D6 6 send view_player_equip_request CZ_EQUIPWIN_MICROSCOPE clif->pViewPlayerEquip +02D7 -1 recv show_eq ZC_EQUIPWIN_MICROSCOPE +02D8 10 send equip_window_tick CZ_CONFIG clif->pEquipTick +02D9 10 recv show_eq_msg_other ZC_CONFIG +02DA 3 recv show_eq_msg_self ZC_CONFIG_NOTIFY +02DB -1 send CZ_BATTLEFIELD_CHAT clif->pBattleChat +02DC -1 recv battleground_message ZC_BATTLEFIELD_CHAT +02DD 32 recv battleground_emblem ZC_BATTLEFIELD_NOTIFY_CAMPINFO +02DE 6 recv battleground_score ZC_BATTLEFIELD_NOTIFY_POINT +02DF 36 recv battleground_position ZC_BATTLEFIELD_NOTIFY_POSITION +02E0 34 recv battleground_hp ZC_BATTLEFIELD_NOTIFY_HP + +//2007-10-23aSakexe +02CB 65 recv instance_window_start ZC_MEMORIALDUNGEON_SUBSCRIPTION_INFO +02CD 71 recv instance_window_join ZC_MEMORIALDUNGEON_INFO + +//2007-11-06aSakexe +0078 55 recv actor_exists ZC_NOTIFY_STANDENTRY +007C 42 recv actor_spawned ZC_NOTIFY_STANDENTRY_NPC +022C 65 recv actor_moved ZC_NOTIFY_MOVEENTRY3 +029B 80 recv mercenary_init ZC_MER_INIT + +//2007-11-13aSakexe +02E1 33 recv actor_action ZC_NOTIFY_ACT2 + +//2007-11-20aSakexe +01DF 10 send CZ_REQ_ACCOUNTNAME clif->pGMReqAccountName +02E2 14 send CZ_USE_ITEM_NEW_JAPEN +02E3 25 send CZ_USE_SKILL_NEW_JAPEN +02E4 8 send CZ_ITEM_PICKUP_NEW_JAPEN +02E5 8 send CZ_REQUEST_MOVE_NEW_JAPEN +02E6 6 send CZ_BOT_CHECK + +//2007-11-27aSakexe +02E7 -1 recv map_property ZC_MAPPROPERTY + +//2008-01-02aSakexe +01DF 6 send CZ_REQ_ACCOUNTNAME clif->pGMReqAccountName +02E8 -1 recv inventory_items_stackable ZC_NORMAL_ITEMLIST3 +02E9 -1 recv cart_items_stackable ZC_CART_NORMAL_ITEMLIST3 +02EA -1 recv storage_items_stackable ZC_STORE_NORMAL_ITEMLIST3 +02EB 13 recv map_loaded ZC_ACCEPT_ENTER2 +02EC 67 recv actor_exists ZC_NOTIFY_MOVEENTRY4 +02ED 59 recv actor_connected ZC_NOTIFY_NEWENTRY4 +02EE 60 recv actor_moved ZC_NOTIFY_STANDENTRY4 +02EF 8 recv font ZC_NOTIFY_FONT + +//2008-03-18aSakexe +02BF -1 recv ZC_SRPACKETR2_INIT +02C0 -1 send CZ_SRPACKETR2_START +02F0 10 recv progress_bar ZC_PROGRESS +02F1 2 send notify_progress_bar_complete CZ_PROGRESS clif->pProgressbar +02F2 2 recv progress_bar_stop ZC_PROGRESS_CANCEL + +//2008-03-25bSakexe +02F3 -1 send CZ_IRMAIL_SEND +02F4 -1 recv ZC_IRMAIL_SEND_RES +02F5 -1 recv ZC_IRMAIL_NOTIFY +02F6 -1 send CZ_IRMAIL_LIST +02F7 -1 unkn +02F8 -1 unkn +02F9 -1 unkn +02FA -1 unkn +02FB -1 unkn +02FC -1 unkn +02FD -1 unkn +02FE -1 unkn +02FF -1 unkn +0300 -1 unkn + +//2008-04-01aSakexe +0301 -1 unkn +0302 -1 unkn +0303 -1 unkn +0304 -1 unkn +0305 -1 unkn +0306 -1 unkn +0307 -1 unkn +0308 -1 unkn +0309 -1 unkn +030A -1 unkn +030B -1 unkn +030C -1 unkn +030D -1 unkn +030E -1 unkn +030F -1 unkn +0310 -1 unkn +0311 -1 unkn +0312 -1 unkn +0313 -1 unkn +0314 -1 unkn +0315 -1 unkn +0316 -1 unkn +0317 -1 unkn +0318 -1 unkn +0319 -1 unkn +031A -1 unkn +031B -1 unkn +031C -1 unkn +031D -1 unkn +031E -1 unkn +031F -1 unkn +0320 -1 unkn +0321 -1 unkn +0322 -1 unkn +0323 -1 unkn +0324 -1 unkn +0325 -1 unkn +0326 -1 unkn +0327 -1 unkn +0328 -1 unkn +0329 -1 unkn +032A -1 unkn +032B -1 unkn +032C -1 unkn +032D -1 unkn +032E -1 unkn +032F -1 unkn +0330 -1 unkn +0331 -1 unkn +0332 -1 unkn +0333 -1 unkn +0334 -1 unkn +0335 -1 unkn +0336 -1 unkn +0337 -1 unkn +0338 -1 unkn +0339 -1 unkn +033A -1 unkn +033B -1 unkn +033C -1 unkn +033D -1 unkn +033E -1 unkn +033F -1 unkn +0340 -1 unkn +0341 -1 unkn +0342 -1 unkn +0343 -1 unkn +0344 -1 unkn +0345 -1 unkn +0346 -1 unkn +0347 -1 unkn +0348 -1 unkn +0349 -1 unkn +034A -1 unkn +034B -1 unkn +034C -1 unkn +034D -1 unkn +034E -1 unkn +034F -1 unkn +0350 -1 unkn +0351 -1 unkn +0352 -1 unkn +0353 -1 unkn +0354 -1 unkn +0355 -1 unkn +0356 -1 unkn +0357 -1 unkn +0358 -1 unkn +0359 -1 unkn +035A -1 unkn + +//2008-05-27aSakexe +035B -1 unkn +035C 2 send CZ_OPEN_SIMPLE_CASHSHOP_ITEMLIST +035D -1 recv ZC_SIMPLE_CASHSHOP_POINT_ITEMLIST +035E 2 send CZ_CLOSE_WINDOW +035F -1 send character_move CZ_REQUEST_MOVE2 clif->pWalkToXY +0389 -1 unkn + +//2008-08-20aSakexe +040C -1 unkn local_broadcast +040D -1 unkn +040E -1 unkn +040F -1 unkn +0410 -1 unkn +0411 -1 unkn +0412 -1 unkn +0413 -1 unkn +0414 -1 unkn +0415 -1 unkn +0416 -1 unkn +0417 -1 unkn +0418 -1 unkn +0419 -1 unkn +041A -1 unkn +041B -1 unkn +041C -1 unkn +041D -1 unkn +041E -1 unkn +041F -1 unkn +0420 -1 unkn +0421 -1 unkn +0422 -1 unkn +0423 -1 unkn +0424 -1 unkn +0425 -1 unkn +0426 -1 unkn +0427 -1 unkn +0428 -1 unkn +0429 -1 unkn +042A -1 unkn +042B -1 unkn +042C -1 unkn +042D -1 unkn +042E -1 unkn +042F -1 unkn +0430 -1 unkn +0431 -1 unkn +0432 -1 unkn +0433 -1 unkn +0434 -1 unkn +0435 -1 unkn + +//2008-09-10aSakexe +0436 19 send map_login CZ_ENTER2 clif->pWantToConnection +0437 7 send character_move CZ_REQUEST_ACT2 clif->pActionRequest +0438 10 send CZ_USE_SKILL2 clif->pUseSkillToId +0439 8 send item_use CZ_USE_ITEM2 clif->pUseItem + +//2008-11-13aSakexe +043D 8 recv skill_post_delay ZC_SKILL_POSTDELAY +043E -1 recv skill_post_delaylist ZC_SKILL_POSTDELAY_LIST +043F 8 recv actor_status_active ZC_MSG_STATE_CHANGE2 + +//2008-11-26aSakexe +01A2 37 recv pet_info ZC_PROPERTY_PET +0440 10 recv millenium_shield ZC_MILLENNIUMSHIELD +0441 4 recv skill_delete ZC_SKILLINFO_DELETE + +//2008-12-10aSakexe +0442 -1 recv sage_autospell ZC_SKILL_SELECT_REQUEST +0443 8 send skill_select CZ_SKILL_SELECT_RESPONSE clif->pSkillSelectMenu + +//2009-01-14aSakexe +043F 25 recv actor_status_active ZC_MSG_STATE_CHANGE2 +0444 -1 recv cash_item_list ZC_SIMPLE_CASH_POINT_ITEMLIST +0445 10 send CZ_SIMPLE_BUY_CASH_POINT_ITEM + +//2009-02-18aSakexe +0446 14 recv minimap_indicator ZC_QUEST_NOTIFY_EFFECT + +//2009-02-25aSakexe +0448 -1 recv HC_CHARACTER_LIST + +//2009-03-30aSakexe +0449 4 recv hack_shield_alarm ZC_HACKSH_ERROR_MSG + +//2009-04-08aSakexe +02A6 -1 recv gameguard_request ZC_HACKSH_CPX_MSG +02A7 -1 send CZ_HACKSH_CPX_MSG +044A 6 send CZ_CLIENT_VERSION + +//2008-08-27aRagexeRE +0072 22 send received_characters CZ_ENTER clif->pWantToConnection +007C 44 recv actor_spawned ZC_NOTIFY_STANDENTRY_NPC +007E 105 send sync CZ_REQUEST_TIME clif->pTickSend +0085 10 send character_move CZ_REQUEST_MOVE clif->pWalkToXY +0089 11 send actor_action CZ_REQUEST_ACT clif->pActionRequest +008C 14 send public_chat CZ_REQUEST_CHAT clif->pGlobalMessage +0094 19 send actor_info_request CZ_REQNAME clif->pGetCharNameRequest +009B 34 send actor_look_at CZ_CHANGE_DIRECTION clif->pChangeDir +009F 20 send item_take CZ_ITEM_PICKUP clif->pTakeItem +00A2 14 send item_drop CZ_ITEM_THROW clif->pDropItem +00A7 9 send item_use CZ_USE_ITEM clif->pUseItem +00F5 11 send storage_item_remove CZ_MOVE_ITEM_FROM_STORE_TO_BODY clif->pMoveFromKafra +00F7 17 send storage_close CZ_REQUEST_TIME clif->pTickSend +0113 25 send skill_use CZ_USE_SKILL clif->pUseSkillToId +0116 17 send skill_use_location CZ_USE_SKILL_TOGROUND clif->pUseSkillToPos +0190 23 send CZ_USE_SKILL_TOGROUND_WITHTALKBOX clif->pUseSkillToPosMoreInfo +02E2 20 send CZ_USE_ITEM_NEW_JAPEN +02E3 22 send CZ_USE_SKILL_NEW_JAPEN +02E4 11 send CZ_ITEM_PICKUP_NEW_JAPEN +02E5 9 send CZ_REQUEST_MOVE_NEW_JAPEN + +//2008-09-10aRagexeRE +0436 19 send map_login CZ_ENTER2 clif->pWantToConnection +0437 7 send character_move CZ_REQUEST_ACT2 clif->pActionRequest +0438 10 send CZ_USE_SKILL2 clif->pUseSkillToId +0439 8 send item_use CZ_USE_ITEM2 clif->pUseItem + +//2008-11-12aRagexeRE +043D 8 recv skill_post_delay ZC_SKILL_POSTDELAY +043E -1 recv skill_post_delaylist ZC_SKILL_POSTDELAY_LIST +043F 8 recv actor_status_active ZC_MSG_STATE_CHANGE2 + +//2008-12-17aRagexeRE +01A2 37 recv pet_info ZC_PROPERTY_PET +0440 10 recv millenium_shield ZC_MILLENNIUMSHIELD +0441 4 recv skill_delete ZC_SKILLINFO_DELETE +0442 8 recv sage_autospell ZC_SKILL_SELECT_REQUEST +0443 8 send skill_select CZ_SKILL_SELECT_RESPONSE clif->pSkillSelectMenu + +//2008-12-17bRagexeRE +006D 114 recv character_creation_successful HC_ACCEPT_MAKECHAR + +//2009-01-21aRagexeRE +043F 25 recv actor_status_active ZC_MSG_STATE_CHANGE2 +0444 -1 recv cash_item_list ZC_SIMPLE_CASH_POINT_ITEMLIST +0445 10 send CZ_SIMPLE_BUY_CASH_POINT_ITEM + +//2009-02-18aRagexeRE +0446 14 recv minimap_indicator ZC_QUEST_NOTIFY_EFFECT + +//2009-02-26cRagexeRE +0448 -1 recv HC_CHARACTER_LIST + +//2009-04-01aRagexeRE +0449 4 recv hack_shield_alarm ZC_HACKSH_ERROR_MSG + +//2009-05-14aRagexeRE +044B 2 send CZ_CLOSE_SIMPLECASH_SHOP + +//2009-05-20aRagexeRE +07D0 6 recv ZC_ES_RESULT +07D1 2 send CZ_ES_GET_LIST +07D2 -1 recv ZC_ES_LIST +07D3 4 send CZ_ES_CHOOSE +07D4 4 send CZ_ES_CANCEL +07D5 4 recv ZC_ES_READY +07D6 4 recv ZC_ES_GOTO + +// 2009-05-20aRagexe, 2009-05-20aRagexeRE +0447 2 send blocking_play_cancel CZ_BLOCKING_PLAY_CANCEL + +//2009-06-03aRagexeRE +07D7 8 send party_setting CZ_GROUPINFO_CHANGE_V2 clif->pPartyChangeOption +07D8 8 recv party_exp ZC_REQ_GROUPINFO_CHANGE_V2 +07D9 254 recv hotkeys ZC_SHORTCUT_KEY_LIST_V2 +07DA 6 send party_leader CZ_CHANGE_GROUP_MASTER clif->pPartyChangeLeader + +//2009-06-10aRagexeRE +07DB 8 recv stat_info ZC_HO_PAR_CHANGE + +//2009-06-17aRagexeRE +07D9 268 recv hotkeys ZC_SHORTCUT_KEY_LIST_V2 +07DC 6 send CZ_SEEK_PARTY +07DD 54 recv ZC_SEEK_PARTY +07DE 30 send CZ_SEEK_PARTY_MEMBER +07DF 54 recv ZC_SEEK_PARTY_MEMBER + +//2009-07-01aRagexeRE +0275 37 send game_login CH_ENTER2 +0276 -1 recv AC_ACCEPT_LOGIN2 + +//2009-07-08aRagexeRE +07E0 58 recv ZC_ES_NOTI_MYINFO + +//2009-07-15aRagexeRE +07E1 15 recv skill_update ZC_SKILLINFO_UPDATE2 + +//2009-08-05aRagexeRE +07E2 8 recv msg_string ZC_MSG_VALUE + +//2009-08-18aRagexeRE +07E3 6 recv skill_exchange_item ZC_ITEMLISTWIN_OPEN +07E4 -1 send item_list_window_selected CZ_ITEMLISTWIN_RES clif->pItemListWindowSelected +07E6 8 recv skill_msg ZC_MSG_SKILL + +//2009-08-25aRagexeRE +07E6 28 recv skill_msg ZC_MSG_SKILL +07E7 5 send CH_CHECKBOT chr->parse_char_check_captcha + +//2009-09-22aRagexeRE +07E5 8 send CH_ENTER_CHECKBOT chr->parse_char_request_captcha +07E6 8 recv skill_msg ZC_MSG_SKILL +07E7 32 send CH_CHECKBOT chr->parse_char_check_captcha +07E8 -1 recv captcha_image HC_CHECKBOT +07E9 5 recv captcha_answer HC_CHECKBOT_RESULT + +//2009-09-29aRagexeRE +07EA 2 send CZ_BATTLE_FIELD_LIST +07EB -1 recv ZC_BATTLE_FIELD_LIST +07EC 6 send CZ_JOIN_BATTLE_FIELD +07ED 8 recv ZC_JOIN_BATTLE_FIELD +07EE 6 send CZ_CANCEL_BATTLE_FIELD +07EF 8 recv ZC_CANCEL_BATTLE_FIELD +07F0 4 send CZ_REQ_BATTLE_STATE_MONITOR +07F2 4 recv ZC_BATTLE_NOTI_START_STEP +07F3 3 recv ZC_BATTLE_JOIN_NOTI_DEFER + +//2009-10-06aRagexeRE +07EC 8 send CZ_JOIN_BATTLE_FIELD +07ED 10 recv ZC_JOIN_BATTLE_FIELD +07F0 8 send CZ_REQ_BATTLE_STATE_MONITOR +07F1 15 recv ZC_ACK_BATTLE_STATE_MONITOR +07F2 6 recv ZC_BATTLE_NOTI_START_STEP +07F3 4 recv ZC_BATTLE_JOIN_NOTI_DEFER +07F4 3 recv ZC_BATTLE_JOIN_DISABLE_STATE + +//2009-10-27aRagexeRE +07F5 6 send CZ_GM_FULLSTRIP clif->pGMFullStrip +07F6 14 recv exp ZC_NOTIFY_EXP + +//2009-11-03aRagexeRE +07F7 -1 recv actor_exists ZC_NOTIFY_MOVEENTRY7 +07F8 -1 recv actor_connected ZC_NOTIFY_NEWENTRY5 +07F9 -1 recv actor_moved ZC_NOTIFY_STANDENTRY5 + +//2009-11-17aRagexeRE +07FA 8 recv inventory_item_removed ZC_DELETE_ITEM_FROM_BODY + +//2009-11-24aRagexeRE +07FB 25 recv skill_cast ZC_USESKILL_ACK2 + +//2009-12-01aRagexeRE +07FC 10 recv party_leader ZC_CHANGE_GROUP_MASTER +07FD -1 recv special_item_obtain ZC_BROADCASTING_SPECIAL_ITEM_OBTAIN +07FE 26 recv sound_effect ZC_PLAY_NPC_BGM +07FF -1 recv define_check ZC_DEFINE_CHECK + +//2009-12-15aRagexeRE +0800 -1 recv vender_items_list ZC_PC_PURCHASE_ITEMLIST_FROMMC2 +0801 -1 send buy_bulk_vender CZ_PC_PURCHASE_ITEMLIST_FROMMC2 clif->pPurchaseReq2 + +//2009-12-22aRagexeRE +0802 18 send booking_register CZ_PARTY_BOOKING_REQ_REGISTER clif->pPartyBookingRegisterReq +0803 4 recv booking_register_request ZC_PARTY_BOOKING_ACK_REGISTER +0804 8 send booking_search CZ_PARTY_BOOKING_REQ_SEARCH clif->pPartyBookingSearchReq +0805 -1 recv booking_search_request ZC_PARTY_BOOKING_ACK_SEARCH +0806 4 send booking_delete CZ_PARTY_BOOKING_REQ_DELETE clif->pPartyBookingDeleteReq +0807 2 recv booking_delete_request ZC_PARTY_BOOKING_ACK_DELETE +0808 4 send booking_update CZ_PARTY_BOOKING_REQ_UPDATE clif->pPartyBookingUpdateReq +0809 14 recv booking_insert ZC_PARTY_BOOKING_NOTIFY_INSERT +080A 50 recv booking_update ZC_PARTY_BOOKING_NOTIFY_UPDATE +080B 18 recv booking_delete ZC_PARTY_BOOKING_NOTIFY_DELETE +080C 6 send CZ_SIMPLE_CASH_BTNSHOW +0804 14 send booking_search CZ_PARTY_BOOKING_REQ_SEARCH clif->pPartyBookingSearchReq +0806 2 send booking_delete CZ_PARTY_BOOKING_REQ_DELETE clif->pPartyBookingDeleteReq +0807 4 recv booking_delete_request ZC_PARTY_BOOKING_ACK_DELETE +0808 14 send booking_update CZ_PARTY_BOOKING_REQ_UPDATE clif->pPartyBookingUpdateReq +0809 50 recv booking_insert ZC_PARTY_BOOKING_NOTIFY_INSERT +080A 18 recv booking_update ZC_PARTY_BOOKING_NOTIFY_UPDATE +080B 6 recv booking_delete ZC_PARTY_BOOKING_NOTIFY_DELETE +0801 -1 send buy_bulk_vender CZ_PC_PURCHASE_ITEMLIST_FROMMC2 clif->pPurchaseReq2 +080C 2 send CZ_SIMPLE_CASH_BTNSHOW +080D 3 recv ZC_SIMPLE_CASH_BTNSHOW +080E 14 recv party_hp_info ZC_NOTIFY_HP_TO_GROUPM_R2 +07F0 6 send CZ_REQ_BATTLE_STATE_MONITOR +080F 20 recv deal_add_other ZC_ADD_EXCHANGE_ITEM2 +0810 3 recv open_buying_store ZC_OPEN_BUYING_STORE +0811 -1 send buy_bulk_openShop CZ_REQ_OPEN_BUYING_STORE clif->pReqOpenBuyingStore +0812 86 recv open_buying_store_fail ZC_FAILED_OPEN_BUYING_STORE_TO_BUYER +0813 6 recv open_buying_store_item_list ZC_MYITEMLIST_BUYING_STORE +0814 6 recv buying_store_found ZC_BUYING_STORE_ENTRY +0815 -1 send buy_bulk_closeShop CZ_REQ_CLOSE_BUYING_STORE clif->pReqCloseBuyingStore +0817 -1 send buy_bulk_request CZ_REQ_CLICK_TO_BUYING_STORE clif->pReqClickBuyingStore +0818 6 recv buying_store_items_list ZC_ACK_ITEMLIST_BUYING_STORE +0819 4 send buy_bulk_buyer CZ_REQ_TRADE_BUYING_STORE clif->pReqTradeBuyingStore +0813 -1 recv open_buying_store_item_list ZC_MYITEMLIST_BUYING_STORE +0814 2 recv buying_store_found ZC_BUYING_STORE_ENTRY +0815 6 send buy_bulk_closeShop CZ_REQ_CLOSE_BUYING_STORE clif->pReqCloseBuyingStore +0816 6 recv buying_store_lost ZC_DISAPPEAR_BUYING_STORE_ENTRY +0818 -1 recv buying_store_items_list ZC_ACK_ITEMLIST_BUYING_STORE +0819 10 send buy_bulk_buyer CZ_REQ_TRADE_BUYING_STORE clif->pReqTradeBuyingStore +081A 4 recv ZC_FAILED_TRADE_BUYING_STORE_TO_BUYER +081B 4 recv buying_store_update ZC_UPDATE_ITEM_FROM_BUYING_STORE +081C 6 recv buying_store_item_delete ZC_ITEM_DELETE_BUYING_STORE +081D 22 recv elemental_info ZC_EL_INIT +081E 8 recv stat_info ZC_EL_PAR_CHANGE +081F -1 recv ZC_BROADCAST4 +081A 6 recv ZC_FAILED_TRADE_BUYING_STORE_TO_BUYER +081A 10 recv ZC_FAILED_TRADE_BUYING_STORE_TO_BUYER +0820 11 recv ZC_COSTUME_SPRITE_CHANGE +0821 2 recv AC_OTP_USER +0822 9 send CA_OTP_AUTH_REQ +0823 -1 recv AC_OTP_AUTH_ACK +081B 8 recv buying_store_update ZC_UPDATE_ITEM_FROM_BUYING_STORE +0812 8 recv open_buying_store_fail ZC_FAILED_OPEN_BUYING_STORE_TO_BUYER +0814 86 recv buying_store_found ZC_BUYING_STORE_ENTRY +0815 2 send buy_bulk_closeShop CZ_REQ_CLOSE_BUYING_STORE clif->pReqCloseBuyingStore +0817 6 send buy_bulk_request CZ_REQ_CLICK_TO_BUYING_STORE clif->pReqClickBuyingStore +0819 -1 send buy_bulk_buyer CZ_REQ_TRADE_BUYING_STORE clif->pReqTradeBuyingStore +081A 4 recv ZC_FAILED_TRADE_BUYING_STORE_TO_BUYER +081B 10 recv buying_store_update ZC_UPDATE_ITEM_FROM_BUYING_STORE +081C 10 recv buying_store_item_delete ZC_ITEM_DELETE_BUYING_STORE +0824 6 recv buying_store_fail ZC_FAILED_TRADE_BUYING_STORE_TO_SELLER +0825 -1 send token_login CA_SSO_LOGIN_REQ +0826 4 recv AC_SSO_LOGIN_ACK +0835 -1 send search_store_info CZ_SEARCH_STORE_INFO clif->pSearchStoreInfo +0836 -1 recv search_store_result ZC_SEARCH_STORE_INFO_ACK +0837 3 recv search_store_fail ZC_SEARCH_STORE_INFO_FAILED +0838 3 send search_store_request_next_page CZ_SEARCH_STORE_INFO_NEXT_PAGE clif->pSearchStoreInfoNextPage +0838 2 send search_store_request_next_page CZ_SEARCH_STORE_INFO_NEXT_PAGE clif->pSearchStoreInfoNextPage +083A 4 recv search_store_open ZC_OPEN_SEARCH_STORE_INFO +083B 2 send search_store_close CZ_CLOSE_SEARCH_STORE_INFO clif->pCloseSearchStoreInfo +083C 12 send search_store_select CZ_SSILIST_ITEM_CLICK clif->pSearchStoreInfoListItemClick +083D 6 recv search_store_pos ZC_SSILIST_ITEM_CLICK_ACK + +//2010-06-15aRagexeRE +083E 26 recv login_error AC_REFUSE_LOGIN_R2 + +//2010-06-22aRagexeRE +083F 22 recv ZC_SEARCH_STORE_OPEN_INFO + +//2010-06-29aRagexeRE +00AA 9 recv equip_item ZC_REQ_WEAR_EQUIP_ACK +07F1 18 recv ZC_ACK_BATTLE_STATE_MONITOR +07F2 8 recv ZC_BATTLE_NOTI_START_STEP +07F3 6 recv ZC_BATTLE_JOIN_NOTI_DEFER + +//2010-07-01aRagexeRE +083A 5 recv search_store_open ZC_OPEN_SEARCH_STORE_INFO + +//2010-07-13aRagexeRE +0827 6 send char_delete2 CH_DELETE_CHAR3_RESERVED chr->parse_char_delete2_req +0828 14 recv char_delete2_result HC_DELETE_CHAR3_RESERVED +0829 6 send char_delete2_accept CH_DELETE_CHAR3 chr->parse_char_delete2_accept +082A 10 recv char_delete2_accept_result HC_DELETE_CHAR3 +082B 6 send char_delete2_cancel CH_DELETE_CHAR3_CANCEL chr->parse_char_delete2_cancel +082C 14 recv char_delete2_cancel_result HC_DELETE_CHAR3_CANCEL +0840 -1 recv HC_NOTIFY_ACCESSIBLE_MAPNAME +0841 19 send CH_SELECT_ACCESSIBLE_MAPNAME + +//2010-07-14aRagexeRE + +//2010-08-03aRagexeRE +0839 66 recv guild_expulsion ZC_ACK_BAN_GUILD_SSO +0842 6 send recall_sso CZ_RECALL_SSO clif->pGMRecall2 +0843 6 send remove_aid_sso CZ_REMOVE_AID_SSO clif->pGMRemove2 + +//2010-11-24aRagexeRE +0288 -1 send CZ_PC_BUY_CASH_POINT_ITEM clif->pcashshop_buy +0436 19 send map_login CZ_ENTER2 clif->pWantToConnection +035F 5 send character_move CZ_REQUEST_MOVE2 clif->pWalkToXY +0360 6 send sync CZ_REQUEST_TIME2 clif->pTickSend +0361 5 send actor_look_at CZ_CHANGE_DIRECTION2 clif->pChangeDir +0362 6 send item_take CZ_ITEM_PICKUP2 clif->pTakeItem +0363 6 send item_drop CZ_ITEM_THROW2 clif->pDropItem +0364 8 send storage_item_add CZ_MOVE_ITEM_FROM_BODY_TO_STORE2 clif->pMoveToKafra +0365 8 send storage_item_remove CZ_MOVE_ITEM_FROM_STORE_TO_BODY2 clif->pMoveFromKafra +0366 10 send skill_use_location CZ_USE_SKILL_TOGROUND2 clif->pUseSkillToPos +0367 90 send CZ_USE_SKILL_TOGROUND_WITHTALKBOX2 clif->pUseSkillToPosMoreInfo +0368 6 send actor_info_request CZ_REQNAME2 clif->pGetCharNameRequest +0369 6 send actor_name_request CZ_REQNAME_BYGID2 clif->pSolveCharName +0856 -1 recv actor_moved ZC_NOTIFY_MOVEENTRY8 +0857 -1 recv actor_exists ZC_NOTIFY_STANDENTRY7 +0858 -1 recv actor_connected ZC_NOTIFY_NEWENTRY6 +0859 -1 recv show_eq ZC_EQUIPWIN_MICROSCOPE2 + +// 2010-12-21aRagexe +08B1 -1 recv ZC_MCSTORE_NOTMOVEITEM_LIST + +// 2011-01-11aRagexe +08B3 -1 recv show_script ZC_SHOWSCRIPT + +// 2011-01-25aRagexe +08B4 2 recv pet_capture_process ZC_START_COLLECTION +08B5 6 send pet_capture CZ_TRYCOLLECTION +08B6 3 recv pet_capture_result ZC_TRYCOLLECTION + +// 2011-01-31aRagexe +02F3 -1 send CZ_IRMAIL_SEND +02F4 3 recv ZC_IRMAIL_SEND_RES +02F5 7 recv ZC_IRMAIL_NOTIFY +02F6 7 send CZ_IRMAIL_LIST + +// 2011-02-22aRagexe +08C0 -1 recv ZC_ACK_SE_CASH_ITEM_LIST2 +08C1 2 send macro_start CZ_MACRO_START +08C2 2 send macro_stop CZ_MACRO_STOP + +// 2011-04-19aRagexe +08C7 -1 recv area_spell ZC_SKILL_ENTRY3 + +// 2011-06-14aRagexe +08C8 34 recv actor_action ZC_NOTIFY_ACT3 +08C9 2 send request_cashitems CZ_REQ_SCHEDULER_CASHITEM clif->pCashShopSchedule +08CA -1 recv cash_shop_list ZC_ACK_SCHEDULER_CASHITEM + +// 2011-06-27aRagexe +08CB -1 recv rates_info ZC_PERSONAL_INFOMATION + +//2011-07-18aRagexe (Thanks to Yommy!) +0844 2 send cash_shop_open CZ_SE_CASHSHOP_OPEN clif->pCashShopOpen +084A 2 send cash_shop_close CZ_SE_CASHSHOP_CLOSE clif->pCashShopClose +0846 4 send req_cash_tabcode CZ_REQ_SE_CASH_TAB_CODE clif->pCashShopReqTab +0848 -1 send cash_shop_buy CZ_SE_PC_BUY_CASHITEM_LIST clif->pCashShopBuy + +// 2011-08-02aRagexe +09DC 2 recv actor_connected ZC_NOTIFY_NEWENTRY10 + +// 2011-08-09aRagexe +08CF 10 recv revolving_entity ZC_SPIRITS_ATTRIBUTE +08D0 9 recv ZC_REQ_WEAR_EQUIP_ACK2 +08D1 7 recv ZC_REQ_TAKEOFF_EQUIP_ACK2 +08D2 10 recv high_jump ZC_FASTMOVE + +// 2011-08-16aRagexe +08D3 10 recv ZC_SE_CASHSHOP_UPDATE + +// 2011-09-28aRagexe +08D6 6 recv ZC_CLEAR_DIALOG + +//2011-10-05aRagexeRE +0364 5 send storage_item_add CZ_MOVE_ITEM_FROM_BODY_TO_STORE2 clif->pMoveToKafra +0817 6 send buy_bulk_request CZ_REQ_CLICK_TO_BUYING_STORE clif->pReqClickBuyingStore +0366 5 send skill_use_location CZ_USE_SKILL_TOGROUND2 clif->pUseSkillToPos +0815 6 send buy_bulk_closeShop CZ_REQ_CLOSE_BUYING_STORE clif->pReqCloseBuyingStore +0885 6 send CZ_REASSEMBLY_AUTH02 +0893 8 send CZ_REASSEMBLY_AUTH16 +0897 8 send CZ_REASSEMBLY_AUTH20 +0369 10 send actor_name_request CZ_REQNAME_BYGID2 clif->pSolveCharName +08AD 90 send CZ_REASSEMBLY_AUTH42 +088A 6 send CZ_REASSEMBLY_AUTH07 +0838 6 send search_store_request_next_page CZ_SEARCH_STORE_INFO_NEXT_PAGE clif->pSearchStoreInfoNextPage +0439 8 send item_use CZ_USE_ITEM2 clif->pUseItem +08D7 28 send CZ_REQ_ENTRY_QUEUE_APPLY clif->pBGQueueRegister +090A 26 send CZ_REQ_ENTRY_QUEUE_RANKING clif->pBGQueueCheckState +08DA 26 send CZ_REQ_ENTRY_QUEUE_CANCEL clif->pBGQueueRevokeReq +08E0 51 send CZ_REPLY_LOBBY_ADMISSION clif->pBGQueueBattleBeginAck + +//2011-11-02aRagexe +0436 26 send map_login CZ_ENTER2 clif->pWantToConnection +0898 5 send CZ_REASSEMBLY_AUTH21 +0281 36 send CZ_GANGSI_RANK +088D 26 send CZ_REASSEMBLY_AUTH10 +083C 19 send search_store_select CZ_SSILIST_ITEM_CLICK clif->pSearchStoreInfoListItemClick +08AA 7 send CZ_REASSEMBLY_AUTH39 +02C4 10 send party_join_request_by_name CZ_PARTY_JOIN_REQ clif->pPartyInvite2 +0811 -1 send buy_bulk_openShop CZ_REQ_OPEN_BUYING_STORE clif->pReqOpenBuyingStore +08A5 18 send CZ_REASSEMBLY_AUTH34 +0835 -1 send search_store_info CZ_SEARCH_STORE_INFO clif->pSearchStoreInfo +089B 2 send CZ_REASSEMBLY_AUTH24 +08A1 6 send CZ_REASSEMBLY_AUTH30 +089E -1 send CZ_REASSEMBLY_AUTH27 +08AB -1 send CZ_REASSEMBLY_AUTH40 +088B 2 send CZ_REASSEMBLY_AUTH08 +08A2 12 send CZ_REASSEMBLY_AUTH31 +0835 19 send search_store_info CZ_SEARCH_STORE_INFO clif->pSearchStoreInfo +0892 5 send CZ_REASSEMBLY_AUTH15 +0899 6 send CZ_REASSEMBLY_AUTH22 + +//2012-03-07fRagexeRE +086A 19 recv ZC_REASSEMBLY_AUTH17 +0437 5 send character_move CZ_REQUEST_ACT2 clif->pActionRequest +0887 6 send CZ_REASSEMBLY_AUTH04 +0890 5 send CZ_REASSEMBLY_AUTH13 +0865 6 recv ZC_REASSEMBLY_AUTH12 +02C4 6 send party_join_request_by_name CZ_PARTY_JOIN_REQ clif->pPartyInvite2 +093B 8 recv ZC_REASSEMBLY_AUTH79 +0963 8 send CZ_REASSEMBLY_AUTH77 +0438 10 send CZ_USE_SKILL2 clif->pUseSkillToId +0366 90 send skill_use_location CZ_USE_SKILL_TOGROUND2 clif->pUseSkillToPos +096A 6 send CZ_REASSEMBLY_AUTH84 +0368 6 send actor_info_request CZ_REQNAME2 clif->pGetCharNameRequest +0369 26 send actor_name_request CZ_REQNAME_BYGID2 clif->pSolveCharName +0863 5 recv ZC_REASSEMBLY_AUTH10 +0861 36 recv ZC_REASSEMBLY_AUTH08 +0929 26 recv ZC_REASSEMBLY_AUTH61 +0885 7 send CZ_REASSEMBLY_AUTH02 +0889 10 send CZ_REASSEMBLY_AUTH06 +0870 -1 recv ZC_REASSEMBLY_AUTH23 +0365 18 send storage_item_remove CZ_MOVE_ITEM_FROM_STORE_TO_BODY2 clif->pMoveFromKafra +0815 -1 send buy_bulk_closeShop CZ_REQ_CLOSE_BUYING_STORE clif->pReqCloseBuyingStore +0817 2 send buy_bulk_request CZ_REQ_CLICK_TO_BUYING_STORE clif->pReqClickBuyingStore +0360 6 send sync CZ_REQUEST_TIME2 clif->pTickSend +0811 -1 send buy_bulk_openShop CZ_REQ_OPEN_BUYING_STORE clif->pReqOpenBuyingStore +0884 -1 send CZ_REASSEMBLY_AUTH01 +0835 2 send search_store_info CZ_SEARCH_STORE_INFO clif->pSearchStoreInfo +0838 12 send search_store_request_next_page CZ_SEARCH_STORE_INFO_NEXT_PAGE clif->pSearchStoreInfoNextPage +0439 8 send item_use CZ_USE_ITEM2 clif->pUseItem +08E2 27 recv ZC_NAVIGATION_ACTIVE + +//2012-04-10aRagexeRE +01FD 15 send repair_item CZ_REQ_ITEMREPAIR clif->pRepairItem +089C 26 send CZ_REASSEMBLY_AUTH25 +0885 5 send CZ_REASSEMBLY_AUTH02 +0961 36 send CZ_REASSEMBLY_AUTH75 +0288 -1 send CZ_PC_BUY_CASH_POINT_ITEM clif->pcashshop_buy +091C 26 recv ZC_PARTY_RECRUIT_CANCEL_VOLUNTEER_TO_PM +094B 19 send CZ_REASSEMBLY_AUTH53 +0369 7 send actor_name_request CZ_REQNAME_BYGID2 clif->pSolveCharName +083C 10 send search_store_select CZ_SSILIST_ITEM_CLICK clif->pSearchStoreInfoListItemClick +0439 8 send item_use CZ_USE_ITEM2 clif->pUseItem +0945 -1 send CZ_REASSEMBLY_AUTH47 +0815 -1 send buy_bulk_closeShop CZ_REQ_CLOSE_BUYING_STORE clif->pReqCloseBuyingStore +0817 2 send buy_bulk_request CZ_REQ_CLICK_TO_BUYING_STORE clif->pReqClickBuyingStore +0360 6 send sync CZ_REQUEST_TIME2 clif->pTickSend +0811 -1 send buy_bulk_openShop CZ_REQ_OPEN_BUYING_STORE clif->pReqOpenBuyingStore +0819 -1 send buy_bulk_buyer CZ_REQ_TRADE_BUYING_STORE clif->pReqTradeBuyingStore +0835 2 send search_store_info CZ_SEARCH_STORE_INFO clif->pSearchStoreInfo +0838 12 send search_store_request_next_page CZ_SEARCH_STORE_INFO_NEXT_PAGE clif->pSearchStoreInfoNextPage +0437 5 send character_move CZ_REQUEST_ACT2 clif->pActionRequest +0886 6 send CZ_REASSEMBLY_AUTH03 +0871 5 recv ZC_REASSEMBLY_AUTH24 +0938 6 recv ZC_REASSEMBLY_AUTH76 +0891 6 send CZ_REASSEMBLY_AUTH14 +086C 8 recv ZC_REASSEMBLY_AUTH19 +08A6 8 send CZ_REASSEMBLY_AUTH35 +0438 10 send CZ_USE_SKILL2 clif->pUseSkillToId +0366 90 send skill_use_location CZ_USE_SKILL_TOGROUND2 clif->pUseSkillToPos +0889 6 send CZ_REASSEMBLY_AUTH06 +0884 6 send CZ_REASSEMBLY_AUTH01 +091D 18 recv ZC_REASSEMBLY_AUTH49 +08E5 41 send CZ_PARTY_RECRUIT_REQ_REGISTER clif->pPartyRecruitRegisterReq +08E6 4 recv ZC_PARTY_RECRUIT_ACK_REGISTER +08E7 10 send CZ_PARTY_RECRUIT_REQ_SEARCH clif->pPartyRecruitSearchReq +08E8 -1 unkn +08E9 2 send CZ_PARTY_RECRUIT_REQ_DELETE clif->pPartyRecruitDeleteReq +08EA 4 recv ZC_PARTY_RECRUIT_ACK_DELETE +08EB 39 send CZ_PARTY_RECRUIT_REQ_UPDATE clif->pPartyRecruitUpdateReq +08EC 73 recv ZC_PARTY_RECRUIT_NOTIFY_INSERT +08ED 43 recv ZC_PARTY_RECRUIT_NOTIFY_UPDATE +08EE 6 recv ZC_PARTY_RECRUIT_NOTIFY_DELETE +08EF 6 send CZ_PARTY_RECRUIT_ADD_FILTERLINGLIST +08F0 6 send CZ_PARTY_RECRUIT_SUB_FILTERLINGLIST +08F1 6 send CZ_PARTY_RECRUIT_REQ_VOLUNTEER +08F2 36 recv ZC_PARTY_RECRUIT_VOLUNTEER_INFO +08F3 -1 unkn +08F4 6 send CZ_PARTY_RECRUIT_SHOW_EQUIPMENT +08F5 -1 send CZ_UNKNOWN_BOOKING_SOMMON_MEMBER_08f5 +08F6 22 recv ZC_PARTY_RECRUIT_RECALL_COST +08F7 3 send CZ_PARTY_RECRUIT_ACK_RECALL +08F8 7 recv ZC_PARTY_RECRUIT_FAILED_RECALL +08F9 6 send CZ_PARTY_RECRUIT_REFUSE_VOLUNTEER +08F9 6 send CZ_PARTY_RECRUIT_REFUSE_VOLUNTEER +08FA 6 recv ZC_PARTY_RECRUIT_REFUSE_VOLUNTEER +08FB 6 send CZ_PARTY_RECRUIT_CANCEL_VOLUNTEER +0907 5 send CZ_INVENTORY_TAB clif->pMoveItem +0908 5 recv ZC_INVENTORY_TAB +0977 14 recv monster_hp_info ZC_HP_INFO + +//2012-04-18aRagexeRE [Special Thanks to Judas!] +023B 26 send storage_password CZ_ACK_STORE_PASSWORD clif->pStoragePassword +0361 5 send actor_look_at CZ_CHANGE_DIRECTION2 clif->pChangeDir +08A8 36 send CZ_REASSEMBLY_AUTH37 +0802 26 send booking_register CZ_PARTY_BOOKING_REQ_REGISTER clif->pPartyBookingRegisterReq +022D 19 send homunculus_command CZ_COMMAND_MER clif->pHomMenu +0281 -1 send CZ_GANGSI_RANK +035F 6 send character_move CZ_REQUEST_MOVE2 clif->pWalkToXY +0202 5 send friend_request CZ_ADD_FRIENDS clif->pFriendsListAdd +07E4 6 send item_list_window_selected CZ_ITEMLISTWIN_RES clif->pItemListWindowSelected +0362 6 send item_take CZ_ITEM_PICKUP2 clif->pTakeItem +07EC 8 send CZ_JOIN_BATTLE_FIELD +0364 8 send storage_item_add CZ_MOVE_ITEM_FROM_BODY_TO_STORE2 clif->pMoveToKafra +096A 6 send CZ_REASSEMBLY_AUTH84 +0368 6 send actor_info_request CZ_REQNAME2 clif->pGetCharNameRequest +08E5 41 send CZ_PARTY_RECRUIT_REQ_REGISTER clif->pPartyRecruitRegisterReq +0916 26 send CZ_REQ_JOIN_GUILD2 clif->pGuildInvite2 + +// 2012-05-02aRagexeRE +097D 288 recv top10 ZC_ACK_RANKING +097E 12 recv rank_points ZC_UPDATE_RANKING_POINT +097F -1 recv ZC_SELECTCART +0980 7 send CZ_SELECTCART clif->pSelectCart +0861 18 recv ZC_REASSEMBLY_AUTH08 + +//2012-06-18aRagexeRE +0983 29 recv actor_status_active ZC_MSG_STATE_CHANGE3 +0363 19 send item_drop CZ_ITEM_THROW2 clif->pDropItem +0364 6 send storage_item_add CZ_MOVE_ITEM_FROM_BODY_TO_STORE2 clif->pMoveToKafra +085A 7 recv ZC_REASSEMBLY_AUTH01 +0861 8 recv ZC_REASSEMBLY_AUTH08 +0862 10 recv ZC_REASSEMBLY_AUTH09 +0863 10 recv ZC_REASSEMBLY_AUTH10 +0886 6 send CZ_REASSEMBLY_AUTH03 +0889 90 send CZ_REASSEMBLY_AUTH06 +089E 6 send CZ_REASSEMBLY_AUTH27 +089F 6 send CZ_REASSEMBLY_AUTH28 +08A0 8 send CZ_REASSEMBLY_AUTH29 +094A 6 send CZ_REASSEMBLY_AUTH52 +0953 5 send CZ_REASSEMBLY_AUTH61 +0960 5 send CZ_REASSEMBLY_AUTH74 + +//2012-07-02 + +//2012-07-10 +0886 2 send CZ_REASSEMBLY_AUTH03 + +//2012-07-16aRagExe (special thanks to Yommy/Frost!) +0879 18 recv ZC_REASSEMBLY_AUTH32 +023B 26 send storage_password CZ_ACK_STORE_PASSWORD clif->pStoragePassword +0361 5 send actor_look_at CZ_CHANGE_DIRECTION2 clif->pChangeDir +0819 36 send buy_bulk_buyer CZ_REQ_TRADE_BUYING_STORE clif->pReqTradeBuyingStore +0802 26 send booking_register CZ_PARTY_BOOKING_REQ_REGISTER clif->pPartyBookingRegisterReq +022D 19 send homunculus_command CZ_COMMAND_MER clif->pHomMenu +0369 7 send actor_name_request CZ_REQNAME_BYGID2 clif->pSolveCharName +083C 10 send search_store_select CZ_SSILIST_ITEM_CLICK clif->pSearchStoreInfoListItemClick +0439 8 send item_use CZ_USE_ITEM2 clif->pUseItem +0281 -1 send CZ_GANGSI_RANK +0815 -1 send buy_bulk_closeShop CZ_REQ_CLOSE_BUYING_STORE clif->pReqCloseBuyingStore +0817 2 send buy_bulk_request CZ_REQ_CLICK_TO_BUYING_STORE clif->pReqClickBuyingStore +0360 6 send sync CZ_REQUEST_TIME2 clif->pTickSend +0940 -1 recv ZC_REASSEMBLY_AUTH84 +0811 -1 send buy_bulk_openShop CZ_REQ_OPEN_BUYING_STORE clif->pReqOpenBuyingStore +0835 2 send search_store_info CZ_SEARCH_STORE_INFO clif->pSearchStoreInfo +0838 12 send search_store_request_next_page CZ_SEARCH_STORE_INFO_NEXT_PAGE clif->pSearchStoreInfoNextPage +0437 5 send character_move CZ_REQUEST_ACT2 clif->pActionRequest +035F 6 send character_move CZ_REQUEST_MOVE2 clif->pWalkToXY +0202 5 send friend_request CZ_ADD_FRIENDS clif->pFriendsListAdd +07E4 6 send item_list_window_selected CZ_ITEMLISTWIN_RES clif->pItemListWindowSelected +0362 6 send item_take CZ_ITEM_PICKUP2 clif->pTakeItem +07EC 8 send CZ_JOIN_BATTLE_FIELD +0364 8 send storage_item_add CZ_MOVE_ITEM_FROM_BODY_TO_STORE2 clif->pMoveToKafra +0438 10 send CZ_USE_SKILL2 clif->pUseSkillToId +0366 90 send skill_use_location CZ_USE_SKILL_TOGROUND2 clif->pUseSkillToPos +096A 6 send CZ_REASSEMBLY_AUTH84 +0368 6 send actor_info_request CZ_REQNAME2 clif->pGetCharNameRequest +0363 8 send item_drop CZ_ITEM_THROW2 clif->pDropItem +0436 4 send map_login CZ_ENTER2 clif->pWantToConnection + +//2012-07-16aRagExe + +// 2012-09-25aRagexe +0998 8 send send_equip CZ_REQ_WEAR_EQUIP_V5 clif->pEquipItem + +// 2013-02-06aRagexe +09A4 18 recv ZC_DISPATCH_TIMING_INFO_CHN + +// 2013-03-06aRagexe +09A6 12 recv ZC_BANKING_CHECK +09A7 14 send CZ_REQ_BANKING_DEPOSIT clif->pBankDeposit +09A8 4 recv ZC_ACK_BANKING_DEPOSIT +09A9 14 send CZ_REQ_BANKING_WITHDRAW clif->pBankWithdraw +09AA 4 recv ZC_ACK_BANKING_WITHDRAW + +// 2013-03-13aRagexe +09AB -1 send CZ_REQ_BANKING_CHECK clif->pBankCheck +09AC 20 send CZ_REQ_CASH_BARGAIN_SALE_ITEM_INFO +09AD 6 recv ZC_ACK_CASH_BARGAIN_SALE_ITEM_INFO +09AE -1 send CZ_REQ_APPLY_BARGAIN_SALE_ITEM +09AF -1 recv ZC_ACK_APPLY_BARGAIN_SALE_ITEM +09B0 8 send CZ_REQ_REMOVE_BARGAIN_SALE_ITEM +09B1 6 recv ZC_ACK_REMOVE_BARGAIN_SALE_ITEM +09B2 -1 recv ZC_NOTIFY_BARGAIN_SALE_SELLING + +//2013-03-20Ragexe (Judas + Yommy) +088E 7 send CZ_REASSEMBLY_AUTH11 +089B 10 send CZ_REASSEMBLY_AUTH24 +0881 5 recv ZC_REASSEMBLY_AUTH40 +0363 6 send item_drop CZ_ITEM_THROW2 clif->pDropItem +0897 5 send CZ_REASSEMBLY_AUTH20 +0933 6 recv ZC_REASSEMBLY_AUTH71 +0438 6 send CZ_USE_SKILL2 clif->pUseSkillToId +08AC 8 send CZ_REASSEMBLY_AUTH41 +0874 8 recv ZC_REASSEMBLY_AUTH27 +0959 10 send CZ_REASSEMBLY_AUTH67 +085A 90 recv ZC_REASSEMBLY_AUTH01 +0898 6 send CZ_REASSEMBLY_AUTH21 +094C 6 send CZ_REASSEMBLY_AUTH54 +0365 12 send storage_item_remove CZ_MOVE_ITEM_FROM_STORE_TO_BODY2 clif->pMoveFromKafra +092E 2 recv ZC_REASSEMBLY_AUTH66 +094E -1 send CZ_REASSEMBLY_AUTH56 +0922 -1 recv ZC_REASSEMBLY_AUTH54 +035F 6 send character_move CZ_REQUEST_MOVE2 clif->pWalkToXY +0886 2 send CZ_REASSEMBLY_AUTH03 +0938 -1 recv ZC_REASSEMBLY_AUTH76 +085D 41 recv ZC_REASSEMBLY_AUTH04 +085D 18 recv ZC_REASSEMBLY_AUTH04 +0868 -1 recv ZC_REASSEMBLY_AUTH15 +0888 19 send CZ_REASSEMBLY_AUTH05 +086D 26 recv ZC_REASSEMBLY_AUTH20 +086F 26 recv ZC_REASSEMBLY_AUTH22 +093F 5 recv ZC_REASSEMBLY_AUTH83 +0947 36 send CZ_REASSEMBLY_AUTH49 +0890 4 send CZ_REASSEMBLY_AUTH13 +095A 8 send CZ_REASSEMBLY_AUTH68 +099F 24 recv area_spell_multiple2 ZC_SKILL_ENTRY4 +09A7 10 send CZ_REQ_BANKING_DEPOSIT clif->pBankDeposit +09A8 12 recv ZC_ACK_BANKING_DEPOSIT +09A9 10 send CZ_REQ_BANKING_WITHDRAW clif->pBankWithdraw +09AA 12 recv ZC_ACK_BANKING_WITHDRAW +09AB 6 send CZ_REQ_BANKING_CHECK clif->pBankCheck + +// 2013-03-27bRagexe +09AC -1 send CZ_REQ_CASH_BARGAIN_SALE_ITEM_INFO +09AD 10 recv ZC_ACK_CASH_BARGAIN_SALE_ITEM_INFO +09AE 17 send CZ_REQ_APPLY_BARGAIN_SALE_ITEM +09AF 4 recv ZC_ACK_APPLY_BARGAIN_SALE_ITEM +09B0 8 send CZ_REQ_REMOVE_BARGAIN_SALE_ITEM +09B1 4 recv ZC_ACK_REMOVE_BARGAIN_SALE_ITEM +09B2 6 recv ZC_NOTIFY_BARGAIN_SALE_SELLING +09B3 6 recv ZC_NOTIFY_BARGAIN_SALE_CLOSE + +//2013-05-15aRagexe (Shakto) +0369 7 send actor_name_request CZ_REQNAME_BYGID2 clif->pSolveCharName +083C 10 send search_store_select CZ_SSILIST_ITEM_CLICK clif->pSearchStoreInfoListItemClick +0437 5 send character_move CZ_REQUEST_ACT2 clif->pActionRequest +035F 6 send character_move CZ_REQUEST_MOVE2 clif->pWalkToXY +0362 5 send item_take CZ_ITEM_PICKUP2 clif->pTakeItem +08A1 6 send CZ_REASSEMBLY_AUTH30 +0944 6 send CZ_REASSEMBLY_AUTH46 +0887 8 send CZ_REASSEMBLY_AUTH04 +08AC 8 send CZ_REASSEMBLY_AUTH41 +0438 10 send CZ_USE_SKILL2 clif->pUseSkillToId +0366 90 send skill_use_location CZ_USE_SKILL_TOGROUND2 clif->pUseSkillToPos +096A 6 send CZ_REASSEMBLY_AUTH84 +0368 6 send actor_info_request CZ_REQNAME2 clif->pGetCharNameRequest +0838 12 send search_store_request_next_page CZ_SEARCH_STORE_INFO_NEXT_PAGE clif->pSearchStoreInfoNextPage +0835 2 send search_store_info CZ_SEARCH_STORE_INFO clif->pSearchStoreInfo +0819 -1 send buy_bulk_buyer CZ_REQ_TRADE_BUYING_STORE clif->pReqTradeBuyingStore +0811 -1 send buy_bulk_openShop CZ_REQ_OPEN_BUYING_STORE clif->pReqOpenBuyingStore +0360 6 send sync CZ_REQUEST_TIME2 clif->pTickSend +0817 2 send buy_bulk_request CZ_REQ_CLICK_TO_BUYING_STORE clif->pReqClickBuyingStore +0815 -1 send buy_bulk_closeShop CZ_REQ_CLOSE_BUYING_STORE clif->pReqCloseBuyingStore +092D 41 recv ZC_REASSEMBLY_AUTH65 +092D 18 recv ZC_REASSEMBLY_AUTH65 +0963 -1 send CZ_REASSEMBLY_AUTH77 +0943 19 send CZ_REASSEMBLY_AUTH45 +0947 26 send CZ_REASSEMBLY_AUTH49 +0962 26 send CZ_REASSEMBLY_AUTH76 +0931 5 recv ZC_REASSEMBLY_AUTH69 +093E 36 recv ZC_REASSEMBLY_AUTH82 +0862 4 recv ZC_REASSEMBLY_AUTH09 +08AA 8 send CZ_REASSEMBLY_AUTH39 + +//2013-05-22Ragexe (Shakto) +08A2 7 send CZ_REASSEMBLY_AUTH31 +095C 10 send CZ_REASSEMBLY_AUTH70 +0360 5 send sync CZ_REQUEST_TIME2 clif->pTickSend +07EC 6 send CZ_JOIN_BATTLE_FIELD +0925 5 recv ZC_REASSEMBLY_AUTH57 +095E 6 send CZ_REASSEMBLY_AUTH72 +089C 6 send CZ_REASSEMBLY_AUTH25 +08A3 8 send CZ_REASSEMBLY_AUTH32 +087E 8 recv ZC_REASSEMBLY_AUTH37 +0811 10 send buy_bulk_openShop CZ_REQ_OPEN_BUYING_STORE clif->pReqOpenBuyingStore +0964 90 send CZ_REASSEMBLY_AUTH78 +08A6 6 send CZ_REASSEMBLY_AUTH35 +0369 6 send actor_name_request CZ_REQNAME_BYGID2 clif->pSolveCharName +093E 12 recv ZC_REASSEMBLY_AUTH82 +08AA 2 send CZ_REASSEMBLY_AUTH39 +095B -1 send CZ_REASSEMBLY_AUTH69 +0952 -1 send CZ_REASSEMBLY_AUTH60 +0368 6 send actor_info_request CZ_REQNAME2 clif->pGetCharNameRequest +086E 2 recv ZC_REASSEMBLY_AUTH21 +0874 -1 recv ZC_REASSEMBLY_AUTH27 +089B 41 send CZ_REASSEMBLY_AUTH24 +089B 18 send CZ_REASSEMBLY_AUTH24 +086A -1 recv ZC_REASSEMBLY_AUTH17 +08A9 19 send CZ_REASSEMBLY_AUTH38 +0950 26 send CZ_REASSEMBLY_AUTH58 +0362 26 send item_take CZ_ITEM_PICKUP2 clif->pTakeItem +0926 5 recv ZC_REASSEMBLY_AUTH58 +088E 36 send CZ_REASSEMBLY_AUTH11 +08AC 4 send CZ_REASSEMBLY_AUTH41 +0965 8 send CZ_REASSEMBLY_AUTH79 + +//2013-05-29Ragexe (Shakto) +0890 7 send CZ_REASSEMBLY_AUTH13 +0438 10 send CZ_USE_SKILL2 clif->pUseSkillToId +0876 5 recv ZC_REASSEMBLY_AUTH29 +0897 6 send CZ_REASSEMBLY_AUTH20 +0951 5 send CZ_REASSEMBLY_AUTH59 +0895 6 send CZ_REASSEMBLY_AUTH18 +08A7 6 send CZ_REASSEMBLY_AUTH36 +0938 8 recv ZC_REASSEMBLY_AUTH76 +0957 8 send CZ_REASSEMBLY_AUTH65 +0917 10 recv ZC_REASSEMBLY_AUTH43 +085E 90 recv ZC_REASSEMBLY_AUTH05 +0863 6 recv ZC_REASSEMBLY_AUTH10 +0937 6 recv ZC_REASSEMBLY_AUTH75 +085A 12 recv ZC_REASSEMBLY_AUTH01 +0941 2 send CZ_REASSEMBLY_AUTH43 +0918 -1 recv ZC_REASSEMBLY_AUTH44 +0936 -1 recv ZC_REASSEMBLY_AUTH74 +0892 6 send CZ_REASSEMBLY_AUTH15 +0964 2 send CZ_REASSEMBLY_AUTH78 +0869 -1 recv ZC_REASSEMBLY_AUTH16 +0874 41 recv ZC_REASSEMBLY_AUTH27 +0874 18 recv ZC_REASSEMBLY_AUTH27 +0958 -1 send CZ_REASSEMBLY_AUTH66 +0919 19 recv ZC_REASSEMBLY_AUTH45 +08A8 26 send CZ_REASSEMBLY_AUTH37 +0877 26 recv ZC_REASSEMBLY_AUTH30 +023B 5 send storage_password CZ_ACK_STORE_PASSWORD clif->pStoragePassword +0956 36 send CZ_REASSEMBLY_AUTH64 +0888 4 send CZ_REASSEMBLY_AUTH05 +088E 8 send CZ_REASSEMBLY_AUTH11 + +//2013-06-05Ragexe (Shakto) +0369 7 send actor_name_request CZ_REQNAME_BYGID2 clif->pSolveCharName +083C 10 send search_store_select CZ_SSILIST_ITEM_CLICK clif->pSearchStoreInfoListItemClick +0437 5 send character_move CZ_REQUEST_ACT2 clif->pActionRequest +035F 6 send character_move CZ_REQUEST_MOVE2 clif->pWalkToXY +0202 5 send friend_request CZ_ADD_FRIENDS clif->pFriendsListAdd +07E4 6 send item_list_window_selected CZ_ITEMLISTWIN_RES clif->pItemListWindowSelected +0362 6 send item_take CZ_ITEM_PICKUP2 clif->pTakeItem +07EC 8 send CZ_JOIN_BATTLE_FIELD +0364 8 send storage_item_add CZ_MOVE_ITEM_FROM_BODY_TO_STORE2 clif->pMoveToKafra +0438 10 send CZ_USE_SKILL2 clif->pUseSkillToId +0366 90 send skill_use_location CZ_USE_SKILL_TOGROUND2 clif->pUseSkillToPos +096A 6 send CZ_REASSEMBLY_AUTH84 +0368 6 send actor_info_request CZ_REQNAME2 clif->pGetCharNameRequest +0838 12 send search_store_request_next_page CZ_SEARCH_STORE_INFO_NEXT_PAGE clif->pSearchStoreInfoNextPage +0835 2 send search_store_info CZ_SEARCH_STORE_INFO clif->pSearchStoreInfo +0819 -1 send buy_bulk_buyer CZ_REQ_TRADE_BUYING_STORE clif->pReqTradeBuyingStore +0811 -1 send buy_bulk_openShop CZ_REQ_OPEN_BUYING_STORE clif->pReqOpenBuyingStore +0360 6 send sync CZ_REQUEST_TIME2 clif->pTickSend +0817 2 send buy_bulk_request CZ_REQ_CLICK_TO_BUYING_STORE clif->pReqClickBuyingStore +0815 -1 send buy_bulk_closeShop CZ_REQ_CLOSE_BUYING_STORE clif->pReqCloseBuyingStore +0365 41 send storage_item_remove CZ_MOVE_ITEM_FROM_STORE_TO_BODY2 clif->pMoveFromKafra +0365 18 send storage_item_remove CZ_MOVE_ITEM_FROM_STORE_TO_BODY2 clif->pMoveFromKafra +0281 -1 send CZ_GANGSI_RANK +022D 19 send homunculus_command CZ_COMMAND_MER clif->pHomMenu +0802 26 send booking_register CZ_PARTY_BOOKING_REQ_REGISTER clif->pPartyBookingRegisterReq +023B 26 send storage_password CZ_ACK_STORE_PASSWORD clif->pStoragePassword +0361 5 send actor_look_at CZ_CHANGE_DIRECTION2 clif->pChangeDir +0883 36 recv ZC_REASSEMBLY_AUTH42 +097C 4 send rank_general CZ_REQ_RANKING clif->pRanklist +0363 8 send item_drop CZ_ITEM_THROW2 clif->pDropItem +0436 4 send map_login CZ_ENTER2 clif->pWantToConnection + +//2013-06-12Ragexe (Shakto) +087E 5 recv ZC_REASSEMBLY_AUTH37 +0919 19 recv ZC_REASSEMBLY_AUTH45 +0940 26 recv ZC_REASSEMBLY_AUTH84 +093A 5 recv ZC_REASSEMBLY_AUTH78 +0964 36 send CZ_REASSEMBLY_AUTH78 + +//2013-06-18Ragexe (Shakto) +0889 7 send CZ_REASSEMBLY_AUTH06 +0951 10 send CZ_REASSEMBLY_AUTH59 +088E 5 send CZ_REASSEMBLY_AUTH11 +0930 6 recv ZC_REASSEMBLY_AUTH68 +08A6 5 send CZ_REASSEMBLY_AUTH35 +0962 6 send CZ_REASSEMBLY_AUTH76 +0917 6 recv ZC_REASSEMBLY_AUTH43 +0885 8 send CZ_REASSEMBLY_AUTH02 +0936 8 recv ZC_REASSEMBLY_AUTH74 +096A 10 send CZ_REASSEMBLY_AUTH84 +094F 90 send CZ_REASSEMBLY_AUTH57 +0944 6 send CZ_REASSEMBLY_AUTH46 +0945 6 send CZ_REASSEMBLY_AUTH47 +0890 12 send CZ_REASSEMBLY_AUTH13 +0363 2 send item_drop CZ_ITEM_THROW2 clif->pDropItem +0281 -1 send CZ_GANGSI_RANK +0891 -1 send CZ_REASSEMBLY_AUTH14 +0862 6 recv ZC_REASSEMBLY_AUTH09 +085A 2 recv ZC_REASSEMBLY_AUTH01 +0932 -1 recv ZC_REASSEMBLY_AUTH70 +08A7 41 send CZ_REASSEMBLY_AUTH36 +08A7 18 send CZ_REASSEMBLY_AUTH36 +0942 -1 send CZ_REASSEMBLY_AUTH44 +095B 19 send CZ_REASSEMBLY_AUTH69 +0887 26 send CZ_REASSEMBLY_AUTH04 +0953 26 send CZ_REASSEMBLY_AUTH61 +02C4 5 send party_join_request_by_name CZ_PARTY_JOIN_REQ clif->pPartyInvite2 +0864 36 recv ZC_REASSEMBLY_AUTH11 +0878 4 recv ZC_REASSEMBLY_AUTH31 +087A 8 recv ZC_REASSEMBLY_AUTH33 + +//2013-06-26Ragexe (Shakto) +0369 7 send actor_name_request CZ_REQNAME_BYGID2 clif->pSolveCharName +083C 10 send search_store_select CZ_SSILIST_ITEM_CLICK clif->pSearchStoreInfoListItemClick +0437 5 send character_move CZ_REQUEST_ACT2 clif->pActionRequest +035F 6 send character_move CZ_REQUEST_MOVE2 clif->pWalkToXY +094D 5 send CZ_REASSEMBLY_AUTH55 +088B 6 send CZ_REASSEMBLY_AUTH08 +0952 6 send CZ_REASSEMBLY_AUTH60 +0921 8 recv ZC_REASSEMBLY_AUTH53 +0817 8 send buy_bulk_request CZ_REQ_CLICK_TO_BUYING_STORE clif->pReqClickBuyingStore +0438 10 send CZ_USE_SKILL2 clif->pUseSkillToId +0366 90 send skill_use_location CZ_USE_SKILL_TOGROUND2 clif->pUseSkillToPos +096A 6 send CZ_REASSEMBLY_AUTH84 +0368 6 send actor_info_request CZ_REQNAME2 clif->pGetCharNameRequest +0838 12 send search_store_request_next_page CZ_SEARCH_STORE_INFO_NEXT_PAGE clif->pSearchStoreInfoNextPage +0835 2 send search_store_info CZ_SEARCH_STORE_INFO clif->pSearchStoreInfo +0819 -1 send buy_bulk_buyer CZ_REQ_TRADE_BUYING_STORE clif->pReqTradeBuyingStore +0811 -1 send buy_bulk_openShop CZ_REQ_OPEN_BUYING_STORE clif->pReqOpenBuyingStore +0360 6 send sync CZ_REQUEST_TIME2 clif->pTickSend +0365 2 send storage_item_remove CZ_MOVE_ITEM_FROM_STORE_TO_BODY2 clif->pMoveFromKafra +0815 -1 send buy_bulk_closeShop CZ_REQ_CLOSE_BUYING_STORE clif->pReqCloseBuyingStore +0894 41 send CZ_REASSEMBLY_AUTH17 +0894 18 send CZ_REASSEMBLY_AUTH17 +08A5 -1 send CZ_REASSEMBLY_AUTH34 +088C 19 send CZ_REASSEMBLY_AUTH09 +0895 26 send CZ_REASSEMBLY_AUTH18 +08AB 26 send CZ_REASSEMBLY_AUTH40 +0960 5 send CZ_REASSEMBLY_AUTH74 +0930 36 recv ZC_REASSEMBLY_AUTH68 +0860 8 recv ZC_REASSEMBLY_AUTH07 +088F 4 send CZ_REASSEMBLY_AUTH12 + +//2013-07-03Ragexe (Shakto) +0930 5 recv ZC_REASSEMBLY_AUTH68 +07E4 6 send item_list_window_selected CZ_ITEMLISTWIN_RES clif->pItemListWindowSelected +0362 6 send item_take CZ_ITEM_PICKUP2 clif->pTakeItem +07EC 8 send CZ_JOIN_BATTLE_FIELD +0364 8 send storage_item_add CZ_MOVE_ITEM_FROM_BODY_TO_STORE2 clif->pMoveToKafra +0202 6 send friend_request CZ_ADD_FRIENDS clif->pFriendsListAdd +0817 2 send buy_bulk_request CZ_REQ_CLICK_TO_BUYING_STORE clif->pReqClickBuyingStore +0815 -1 send buy_bulk_closeShop CZ_REQ_CLOSE_BUYING_STORE clif->pReqCloseBuyingStore +0365 41 send storage_item_remove CZ_MOVE_ITEM_FROM_STORE_TO_BODY2 clif->pMoveFromKafra +0365 18 send storage_item_remove CZ_MOVE_ITEM_FROM_STORE_TO_BODY2 clif->pMoveFromKafra +0281 -1 send CZ_GANGSI_RANK +022D 19 send homunculus_command CZ_COMMAND_MER clif->pHomMenu +0802 26 send booking_register CZ_PARTY_BOOKING_REQ_REGISTER clif->pPartyBookingRegisterReq +0360 26 send sync CZ_REQUEST_TIME2 clif->pTickSend +094A 5 send CZ_REASSEMBLY_AUTH52 +0873 36 recv ZC_REASSEMBLY_AUTH26 +0363 8 send item_drop CZ_ITEM_THROW2 clif->pDropItem +0436 4 send map_login CZ_ENTER2 clif->pWantToConnection + +// 2013-04-17aRagexe +09B4 6 send CZ_OPEN_BARGAIN_SALE_TOOL +09B5 2 recv ZC_OPEN_BARGAIN_SALE_TOOL +09B6 6 send CZ_REQ_OPEN_BANKING clif->pBankOpen +09B7 4 recv ZC_ACK_OPEN_BANKING +09B8 6 send CZ_REQ_CLOSE_BANKING clif->pBankClose +09B9 4 recv ZC_ACK_CLOSE_BANKING + +// 2013-04-24aRagexe +09BA 6 send CZ_REQ_OPEN_GUILD_STORAGE +09BB 4 recv ZC_ACK_OPEN_GUILD_STORAGE +09BC 6 send CZ_CLOSE_BARGAIN_SALE_TOOL +09BD 2 recv ZC_CLOSE_BARGAIN_SALE_TOOL + +// 2013-05-02aRagexe +09BE 6 send CZ_REQ_CLOSE_GUILD_STORAGE +09BF 4 recv ZC_ACK_CLOSE_GUILD_STORAGE +09BB 6 recv ZC_ACK_OPEN_GUILD_STORAGE + +// 2013-05-15aRagexe +09C0 11 recv ZC_ACTION_MOVE +09C1 11 recv ZC_C_MARKERINFO +09A8 16 recv ZC_ACK_BANKING_DEPOSIT +09AA 16 recv ZC_ACK_BANKING_WITHDRAW + +// 2013-05-29Ragexe +09C3 8 send CZ_REQ_COUNT_BARGAIN_SALE_ITEM + +// 2013-06-05Ragexe +09C4 8 recv ZC_ACK_COUNT_BARGAIN_SALE_ITEM + +// 2013-06-18aRagexe +09CA 23 recv area_spell_multiple3 ZC_SKILL_ENTRY5 + +// 2013-07-17cRagexe +09CB 17 recv skill_used_no_damage ZC_USE_SKILL2 +09CC -1 recv ZC_SECRETSCAN_DATA +09C1 10 recv ZC_C_MARKERINFO + +//2013-08-07Ragexe (Shakto) +0369 7 send actor_name_request CZ_REQNAME_BYGID2 clif->pSolveCharName +083C 10 send search_store_select CZ_SSILIST_ITEM_CLICK clif->pSearchStoreInfoListItemClick +0437 5 send character_move CZ_REQUEST_ACT2 clif->pActionRequest +035F 6 send character_move CZ_REQUEST_MOVE2 clif->pWalkToXY +0202 5 send friend_request CZ_ADD_FRIENDS clif->pFriendsListAdd +07E4 6 send item_list_window_selected CZ_ITEMLISTWIN_RES clif->pItemListWindowSelected +0362 6 send item_take CZ_ITEM_PICKUP2 clif->pTakeItem +07EC 8 send CZ_JOIN_BATTLE_FIELD +0364 8 send storage_item_add CZ_MOVE_ITEM_FROM_BODY_TO_STORE2 clif->pMoveToKafra +0438 10 send CZ_USE_SKILL2 clif->pUseSkillToId +0366 90 send skill_use_location CZ_USE_SKILL_TOGROUND2 clif->pUseSkillToPos +096A 6 send CZ_REASSEMBLY_AUTH84 +0368 6 send actor_info_request CZ_REQNAME2 clif->pGetCharNameRequest +0838 12 send search_store_request_next_page CZ_SEARCH_STORE_INFO_NEXT_PAGE clif->pSearchStoreInfoNextPage +0835 2 send search_store_info CZ_SEARCH_STORE_INFO clif->pSearchStoreInfo +0819 -1 send buy_bulk_buyer CZ_REQ_TRADE_BUYING_STORE clif->pReqTradeBuyingStore +0811 -1 send buy_bulk_openShop CZ_REQ_OPEN_BUYING_STORE clif->pReqOpenBuyingStore +0360 6 send sync CZ_REQUEST_TIME2 clif->pTickSend +0817 2 send buy_bulk_request CZ_REQ_CLICK_TO_BUYING_STORE clif->pReqClickBuyingStore +0815 -1 send buy_bulk_closeShop CZ_REQ_CLOSE_BUYING_STORE clif->pReqCloseBuyingStore +0365 41 send storage_item_remove CZ_MOVE_ITEM_FROM_STORE_TO_BODY2 clif->pMoveFromKafra +0365 18 send storage_item_remove CZ_MOVE_ITEM_FROM_STORE_TO_BODY2 clif->pMoveFromKafra +0281 -1 send CZ_GANGSI_RANK +022D 19 send homunculus_command CZ_COMMAND_MER clif->pHomMenu +0802 26 send booking_register CZ_PARTY_BOOKING_REQ_REGISTER clif->pPartyBookingRegisterReq +023B 26 send storage_password CZ_ACK_STORE_PASSWORD clif->pStoragePassword +0361 5 send actor_look_at CZ_CHANGE_DIRECTION2 clif->pChangeDir +0887 36 send CZ_REASSEMBLY_AUTH04 +0363 8 send item_drop CZ_ITEM_THROW2 clif->pDropItem +0436 4 send map_login CZ_ENTER2 clif->pWantToConnection + +// 2013-08-07aRagexe +09CD 8 recv message_string ZC_MSG_COLOR + +//2013-08-14aRagexe - Themon +0874 7 recv ZC_REASSEMBLY_AUTH27 +0947 10 send CZ_REASSEMBLY_AUTH49 +093A 5 recv ZC_REASSEMBLY_AUTH78 +088A 6 send CZ_REASSEMBLY_AUTH07 +088C 5 send CZ_REASSEMBLY_AUTH09 +0926 6 recv ZC_REASSEMBLY_AUTH58 +095F 6 send CZ_REASSEMBLY_AUTH73 +0202 8 send friend_request CZ_ADD_FRIENDS clif->pFriendsListAdd +0873 8 recv ZC_REASSEMBLY_AUTH26 +0887 10 send CZ_REASSEMBLY_AUTH04 +0962 90 send CZ_REASSEMBLY_AUTH76 +0937 6 recv ZC_REASSEMBLY_AUTH75 +0923 6 recv ZC_REASSEMBLY_AUTH55 +0868 12 recv ZC_REASSEMBLY_AUTH15 +0941 2 send CZ_REASSEMBLY_AUTH43 +0889 -1 send CZ_REASSEMBLY_AUTH06 +0835 -1 send search_store_info CZ_SEARCH_STORE_INFO clif->pSearchStoreInfo +0895 6 send CZ_REASSEMBLY_AUTH18 +094E 2 send CZ_REASSEMBLY_AUTH56 +0936 -1 recv ZC_REASSEMBLY_AUTH74 +0365 41 send storage_item_remove CZ_MOVE_ITEM_FROM_STORE_TO_BODY2 clif->pMoveFromKafra +0959 18 send CZ_REASSEMBLY_AUTH67 +08A4 -1 send CZ_REASSEMBLY_AUTH33 +0368 19 send actor_info_request CZ_REQNAME2 clif->pGetCharNameRequest +0927 26 recv ZC_REASSEMBLY_AUTH59 +0281 26 send CZ_GANGSI_RANK +0958 5 send CZ_REASSEMBLY_AUTH66 +0885 36 send CZ_REASSEMBLY_AUTH02 +0815 4 send buy_bulk_closeShop CZ_REQ_CLOSE_BUYING_STORE clif->pReqCloseBuyingStore +0896 8 send CZ_REASSEMBLY_AUTH19 + +// 2013-08-14aRagexe +09CE 102 send CZ_ITEM_CREATE_EX clif->pGM_Monster_Item +09CF -1 recv gameguard_request ZC_NPROTECTGAMEGUARDCSAUTH +09D0 -1 send gameguard_reply CZ_NPROTECTGAMEGUARDCSAUTH + +// 2013-08-21bRagexe +09D1 14 recv progress_bar_unit ZC_PROGRESS_ACTOR + +// 2013-08-28bRagexe +09D2 -1 recv ZC_GUILDSTORAGE_ITEMLIST_NORMAL_V5 +09D3 -1 recv ZC_GUILDSTORAGE_ITEMLIST_EQUIP_V5 +09BA 2 send CZ_REQ_OPEN_GUILD_STORAGE +09BE 2 send CZ_REQ_CLOSE_GUILD_STORAGE + +// 2013-09-04aRagexe +09CA -1 recv area_spell_multiple3 ZC_SKILL_ENTRY5 + +// 2013-09-11aRagexe +09D4 2 send sell_buy_complete CZ_NPC_TRADE_QUIT clif->pNPCShopClosed +09D5 -1 recv ZC_NPC_MARKET_OPEN +09D6 -1 send CZ_NPC_MARKET_PURCHASE clif->pNPCMarketPurchase +09D7 -1 recv ZC_NPC_MARKET_PURCHASE_RESULT +09D8 2 send CZ_NPC_MARKET_CLOSE clif->pNPCMarketClosed +09D9 2 send CZ_REQ_GUILDSTORAGE_LOG +09DA 2 recv guild_storage_log ZC_ACK_GUILDSTORAGE_LOG + +// 2013-09-25aRagexe +09DA 10 recv guild_storage_log ZC_ACK_GUILDSTORAGE_LOG + +// 2013-10-02aRagexe +09D9 4 send CZ_REQ_GUILDSTORAGE_LOG +09DA -1 recv guild_storage_log ZC_ACK_GUILDSTORAGE_LOG + +// 2013-10-16aRagexe +09D9 6 send CZ_REQ_GUILDSTORAGE_LOG + +// 2013-10-23aRagexe +09DB -1 recv actor_moved ZC_NOTIFY_MOVEENTRY10 +09DC -1 recv actor_connected ZC_NOTIFY_NEWENTRY10 +09DD -1 recv actor_exists ZC_NOTIFY_STANDENTRY10 +09D9 4 send CZ_REQ_GUILDSTORAGE_LOG + +// 2013-10-30aRagexe +09DE -1 recv private_message ZC_WHISPER02 +09DF 7 recv private_message_sent ZC_ACK_WHISPER02 +09E0 -1 recv SC_LOGIN_ANSWER_WITH_ID + +// 2013-11-06aRagexe +09E1 8 send CZ_MOVE_ITEM_FROM_BODY_TO_GUILDSTORAGE +09E2 8 send CZ_MOVE_ITEM_FROM_GUILDSTORAGE_TO_BODY +09E3 8 send CZ_MOVE_ITEM_FROM_CART_TO_GUILDSTORAGE +09E4 8 send CZ_MOVE_ITEM_FROM_GUILDSTORAGE_TO_CART + +// 2013-11-20dRagexe +09E5 14 recv shop_sold_long ZC_DELETEITEM_FROM_MCSTORE2 +09E6 18 recv ZC_UPDATE_ITEM_FROM_BUYING_STORE2 + +// 2013-11-27bRagexe +09E5 18 recv shop_sold_long ZC_DELETEITEM_FROM_MCSTORE2 +09E6 22 recv ZC_UPDATE_ITEM_FROM_BUYING_STORE2 + +// 2013-12-11dRagexe +09E7 2 recv unread_rodex ZC_NOTIFY_UNREAD_RODEX +09E8 18 send rodex_open_mailbox CZ_OPEN_RODEXBOX clif->pRodexOpenMailbox +09E9 2 send rodex_close_mailbox CZ_CLOSE_RODEXBOX +09ED -1 recv rodex_write_result ZC_ACK_SEND_RODEX +09EE -1 send rodex_next_maillist CZ_REQ_NEXT_RODEX + +// 2013-12-18bRagexe - Yommy +0369 7 send actor_name_request CZ_REQNAME_BYGID2 clif->pSolveCharName +083C 10 send search_store_select CZ_SSILIST_ITEM_CLICK clif->pSearchStoreInfoListItemClick +0437 5 send character_move CZ_REQUEST_ACT2 clif->pActionRequest +035F 6 send character_move CZ_REQUEST_MOVE2 clif->pWalkToXY +0947 5 send CZ_REASSEMBLY_AUTH49 +07E4 6 send item_list_window_selected CZ_ITEMLISTWIN_RES clif->pItemListWindowSelected +0362 6 send item_take CZ_ITEM_PICKUP2 clif->pTakeItem +07EC 8 send CZ_JOIN_BATTLE_FIELD +0364 8 send storage_item_add CZ_MOVE_ITEM_FROM_BODY_TO_STORE2 clif->pMoveToKafra +0438 10 send CZ_USE_SKILL2 clif->pUseSkillToId +0366 90 send skill_use_location CZ_USE_SKILL_TOGROUND2 clif->pUseSkillToPos +096A 6 send CZ_REASSEMBLY_AUTH84 +0368 6 send actor_info_request CZ_REQNAME2 clif->pGetCharNameRequest +0838 12 send search_store_request_next_page CZ_SEARCH_STORE_INFO_NEXT_PAGE clif->pSearchStoreInfoNextPage +0835 2 send search_store_info CZ_SEARCH_STORE_INFO clif->pSearchStoreInfo +0819 -1 send buy_bulk_buyer CZ_REQ_TRADE_BUYING_STORE clif->pReqTradeBuyingStore +022D -1 send homunculus_command CZ_COMMAND_MER clif->pHomMenu +0360 6 send sync CZ_REQUEST_TIME2 clif->pTickSend +0817 2 send buy_bulk_request CZ_REQ_CLICK_TO_BUYING_STORE clif->pReqClickBuyingStore +0815 -1 send buy_bulk_closeShop CZ_REQ_CLOSE_BUYING_STORE clif->pReqCloseBuyingStore +0365 18 send storage_item_remove CZ_MOVE_ITEM_FROM_STORE_TO_BODY2 clif->pMoveFromKafra +0281 -1 send CZ_GANGSI_RANK +092F 19 recv ZC_REASSEMBLY_AUTH67 +0802 26 send booking_register CZ_PARTY_BOOKING_REQ_REGISTER clif->pPartyBookingRegisterReq +08AB 26 send CZ_REASSEMBLY_AUTH40 +0811 5 send buy_bulk_openShop CZ_REQ_OPEN_BUYING_STORE clif->pReqOpenBuyingStore +085C 36 recv ZC_REASSEMBLY_AUTH03 +0363 8 send item_drop CZ_ITEM_THROW2 clif->pDropItem +087B 4 recv ZC_REASSEMBLY_AUTH34 + +// 2013-12-18bRagexe +09EA 10 send rodex_read_mail CZ_REQ_READ_RODEX +09EB 14 recv rodex_read_mail ZC_ACK_READ_RODEX +09EF 11 send rodex_refresh_maillist CZ_REQ_REFRESH_RODEX clif->pRodexRefreshMaillist +09F0 -1 recv rodex_mail_list ZC_ACK_RODEX_LIST +09F5 11 send rodex_delete_mail CZ_REQ_DELETE_RODEX +09F6 11 recv rodex_delete ZC_ACK_DELETE_RODEX +09E8 10 send rodex_open_mailbox CZ_OPEN_RODEXBOX clif->pRodexOpenMailbox +09EE 11 send rodex_next_maillist CZ_REQ_NEXT_RODEX + +// 2013-12-23cRagexe - Yommy +0369 7 send actor_name_request CZ_REQNAME_BYGID2 clif->pSolveCharName +083C 10 send search_store_select CZ_SSILIST_ITEM_CLICK clif->pSearchStoreInfoListItemClick +0437 5 send character_move CZ_REQUEST_ACT2 clif->pActionRequest +035F 6 send character_move CZ_REQUEST_MOVE2 clif->pWalkToXY +0202 5 send friend_request CZ_ADD_FRIENDS clif->pFriendsListAdd +07E4 6 send item_list_window_selected CZ_ITEMLISTWIN_RES clif->pItemListWindowSelected +0362 6 send item_take CZ_ITEM_PICKUP2 clif->pTakeItem +07EC 8 send CZ_JOIN_BATTLE_FIELD +0364 8 send storage_item_add CZ_MOVE_ITEM_FROM_BODY_TO_STORE2 clif->pMoveToKafra +0438 10 send CZ_USE_SKILL2 clif->pUseSkillToId +0366 90 send skill_use_location CZ_USE_SKILL_TOGROUND2 clif->pUseSkillToPos +096A 6 send CZ_REASSEMBLY_AUTH84 +0368 6 send actor_info_request CZ_REQNAME2 clif->pGetCharNameRequest +0838 12 send search_store_request_next_page CZ_SEARCH_STORE_INFO_NEXT_PAGE clif->pSearchStoreInfoNextPage +0835 2 send search_store_info CZ_SEARCH_STORE_INFO clif->pSearchStoreInfo +0819 -1 send buy_bulk_buyer CZ_REQ_TRADE_BUYING_STORE clif->pReqTradeBuyingStore +0811 -1 send buy_bulk_openShop CZ_REQ_OPEN_BUYING_STORE clif->pReqOpenBuyingStore +0360 6 send sync CZ_REQUEST_TIME2 clif->pTickSend +0817 2 send buy_bulk_request CZ_REQ_CLICK_TO_BUYING_STORE clif->pReqClickBuyingStore +0815 -1 send buy_bulk_closeShop CZ_REQ_CLOSE_BUYING_STORE clif->pReqCloseBuyingStore +0365 18 send storage_item_remove CZ_MOVE_ITEM_FROM_STORE_TO_BODY2 clif->pMoveFromKafra +0281 -1 send CZ_GANGSI_RANK +022D 19 send homunculus_command CZ_COMMAND_MER clif->pHomMenu +0802 26 send booking_register CZ_PARTY_BOOKING_REQ_REGISTER clif->pPartyBookingRegisterReq +023B 26 send storage_password CZ_ACK_STORE_PASSWORD clif->pStoragePassword +0361 5 send actor_look_at CZ_CHANGE_DIRECTION2 clif->pChangeDir +08A4 36 send CZ_REASSEMBLY_AUTH33 +0363 8 send item_drop CZ_ITEM_THROW2 clif->pDropItem +0436 4 send map_login CZ_ENTER2 clif->pWantToConnection + +// 2013-12-23bRagexe +09EA 11 send rodex_read_mail CZ_REQ_READ_RODEX +09EB 24 recv rodex_read_mail ZC_ACK_READ_RODEX + +// 2013-12-30aRagexe - Yommy +0871 7 recv ZC_REASSEMBLY_AUTH24 +02C4 10 send party_join_request_by_name CZ_PARTY_JOIN_REQ clif->pPartyInvite2 +035F 5 send character_move CZ_REQUEST_MOVE2 clif->pWalkToXY +0438 6 send CZ_USE_SKILL2 clif->pUseSkillToId +094A 5 send CZ_REASSEMBLY_AUTH52 +092A 6 recv ZC_REASSEMBLY_AUTH62 +0860 6 recv ZC_REASSEMBLY_AUTH07 +0968 8 send CZ_REASSEMBLY_AUTH82 +0895 8 send CZ_REASSEMBLY_AUTH18 +091E 10 recv ZC_REASSEMBLY_AUTH50 +096A 90 send CZ_REASSEMBLY_AUTH84 +0926 6 recv ZC_REASSEMBLY_AUTH58 +0898 6 send CZ_REASSEMBLY_AUTH21 +087B 12 recv ZC_REASSEMBLY_AUTH34 +0369 2 send actor_name_request CZ_REQNAME_BYGID2 clif->pSolveCharName +093D -1 recv ZC_REASSEMBLY_AUTH81 +087F -1 recv ZC_REASSEMBLY_AUTH38 +0969 6 send CZ_REASSEMBLY_AUTH83 +094C 2 send CZ_REASSEMBLY_AUTH54 +0365 -1 send storage_item_remove CZ_MOVE_ITEM_FROM_STORE_TO_BODY2 clif->pMoveFromKafra +091F 18 recv ZC_REASSEMBLY_AUTH51 +022D -1 send homunculus_command CZ_COMMAND_MER clif->pHomMenu +089C 19 send CZ_REASSEMBLY_AUTH25 +08A9 26 send CZ_REASSEMBLY_AUTH38 +0943 26 send CZ_REASSEMBLY_AUTH45 +0949 5 send CZ_REASSEMBLY_AUTH51 +091D 36 recv ZC_REASSEMBLY_AUTH49 +087E 4 recv ZC_REASSEMBLY_AUTH37 +093E 8 recv ZC_REASSEMBLY_AUTH82 + +// 2013-12-30aRagexe +09EC -1 send CZ_REQ_SEND_RODEX +09ED 3 recv rodex_write_result ZC_ACK_SEND_RODEX +09F7 75 recv homunculus_property ZC_PROPERTY_HOMUN_2 +09EB 23 recv rodex_read_mail ZC_ACK_READ_RODEX + +// 2014 Packet Data + +// 2014-01-15cRagexeRE +09F1 10 send rodex_request_zeny CZ_REQ_ZENY_FROM_RODEX +09F2 3 recv rodex_get_zeny ZC_ACK_ZENY_FROM_RODEX +09F3 15 send rodex_request_items CZ_REQ_ITEM_FROM_RODEX +09F4 12 recv rodex_get_item ZC_ACK_ITEM_FROM_RODEX +09F8 -1 recv quest_all_list3 ZC_ALL_QUEST_LIST3 +09F9 131 recv quest_add ZC_ADD_QUEST_EX +09FA -1 recv quest_update_mission_hunt ZC_UPDATE_MISSION_HUNT_EX +09EB -1 recv rodex_read_mail ZC_ACK_READ_RODEX + +// 2014-01-22aRagexeRE +09FB -1 send pet_evolution CZ_PET_EVOLUTION +09FC 6 recv pet_evolution_result ZC_PET_EVOLUTION_RESULT +09FD -1 recv actor_moved ZC_NOTIFY_MOVEENTRY11 +09FE -1 recv actor_connected ZC_NOTIFY_NEWENTRY11 +09FF -1 recv actor_exists ZC_NOTIFY_STANDENTRY11 +09F9 143 recv quest_add ZC_ADD_QUEST_EX + +// 2014-01-29bRagexeRE +0A00 269 recv hotkeys ZC_SHORTCUT_KEY_LIST_V3 +0A01 3 send CZ_SHORTCUTKEYBAR_ROTATE clif->pHotkeyRowShift +01C4 43 recv storage_item_added ZC_ADD_ITEM_TO_STORE2 +01C5 43 recv cart_item_added ZC_ADD_ITEM_TO_CART2 +080F 41 recv deal_add_other ZC_ADD_EXCHANGE_ITEM2 +0990 52 recv inventory_item_added ZC_ITEM_PICKUP_ACK_V5 + +// 2014-02-12aRagexeRE +0A02 4 recv ZC_DRESSROOM_OPEN +09E8 11 send rodex_open_mailbox CZ_OPEN_RODEXBOX clif->pRodexOpenMailbox + +// 2014-02-19aRagexeRE +01C4 53 recv storage_item_added ZC_ADD_ITEM_TO_STORE2 +01C5 53 recv cart_item_added ZC_ADD_ITEM_TO_CART2 +080F 51 recv deal_add_other ZC_ADD_EXCHANGE_ITEM2 +0990 62 recv inventory_item_added ZC_ITEM_PICKUP_ACK_V5 + +// 2014-02-26aRagexeRE +0A03 14 send rodex_cancel_write_mail CZ_REQ_CANCEL_WRITE_RODEX +0A04 11 send rodex_add_item CZ_REQ_ADD_ITEM_RODEX +0A05 6 recv rodex_add_item ZC_ACK_ADD_ITEM_RODEX +0A06 5 send rodex_remove_item CZ_REQ_REMOVE_RODEX_ITEM + +// 2014-03-05aRagexeRE +0A07 4 recv rodex_remove_item ZC_ACK_REMOVE_RODEX_ITEM +0A08 5 send rodex_open_write_mail CZ_REQ_OPEN_WRITE_RODEX +0A09 50 recv deal_add_other ZC_ADD_EXCHANGE_ITEM3 +0A0A 52 recv storage_item_added ZC_ADD_ITEM_TO_STORE3 +0A0B 52 recv cart_item_added ZC_ADD_ITEM_TO_CART3 +0A0C 61 recv inventory_item_added ZC_ITEM_PICKUP_ACK_V6 +0A0D 4 recv inventory_items_nonstackable ZC_INVENTORY_ITEMLIST_EQUIP_V6 +01C4 22 recv storage_item_added ZC_ADD_ITEM_TO_STORE2 +01C5 22 recv cart_item_added ZC_ADD_ITEM_TO_CART2 +080F 20 recv deal_add_other ZC_ADD_EXCHANGE_ITEM2 +0990 31 recv inventory_item_added ZC_ITEM_PICKUP_ACK_V5 +09F3 10 send rodex_request_items CZ_REQ_ITEM_FROM_RODEX +09F4 3 recv rodex_get_item ZC_ACK_ITEM_FROM_RODEX + +// 2014-03-12bRagexeRE +0A0E 14 recv ZC_BATTLEFIELD_NOTIFY_HP2 +0A09 45 recv deal_add_other ZC_ADD_EXCHANGE_ITEM3 +0A0A 47 recv storage_item_added ZC_ADD_ITEM_TO_STORE3 +0A0B 47 recv cart_item_added ZC_ADD_ITEM_TO_CART3 +0A0C 56 recv inventory_item_added ZC_ITEM_PICKUP_ACK_V6 +0A0D -1 recv inventory_items_nonstackable ZC_INVENTORY_ITEMLIST_EQUIP_V6 + +// 2014-03-26cRagexeRE +09F1 11 send rodex_request_zeny CZ_REQ_ZENY_FROM_RODEX +09F2 4 recv rodex_get_zeny ZC_ACK_ZENY_FROM_RODEX +09F3 11 send rodex_request_items CZ_REQ_ITEM_FROM_RODEX +09F4 4 recv rodex_get_item ZC_ACK_ITEM_FROM_RODEX +0A03 2 send rodex_cancel_write_mail CZ_REQ_CANCEL_WRITE_RODEX +0A07 6 recv rodex_remove_item ZC_ACK_REMOVE_RODEX_ITEM +0A08 7 send rodex_open_write_mail CZ_REQ_OPEN_WRITE_RODEX + +// 2014-04-02eRagexeRE +0A0F -1 recv cart_items_nonstackable ZC_CART_ITEMLIST_EQUIP_V6 +0A10 -1 recv storage_items_nonstackable ZC_STORE_ITEMLIST_EQUIP_V6 +0A11 -1 recv ZC_GUILDSTORAGE_ITEMLIST_EQUIP_V6 + +// 2014-04-09aRagexeRE +09F2 12 recv rodex_get_zeny ZC_ACK_ZENY_FROM_RODEX +09F4 12 recv rodex_get_item ZC_ACK_ITEM_FROM_RODEX + +// 2014-04-16aRagexeRE +0A04 6 send rodex_add_item CZ_REQ_ADD_ITEM_RODEX +0A12 27 recv rodex_open_write ZC_ACK_OPEN_WRITE_RODEX +0A13 2 send rodex_checkname CZ_CHECK_RECEIVE_CHARACTER_NAME +0A05 48 recv rodex_add_item ZC_ACK_ADD_ITEM_RODEX +0A06 6 send rodex_remove_item CZ_REQ_REMOVE_RODEX_ITEM +0A07 7 recv rodex_remove_item ZC_ACK_REMOVE_RODEX_ITEM +0A08 26 send rodex_open_write_mail CZ_REQ_OPEN_WRITE_RODEX + +// 2014-04-23aRagexeRE +0A14 6 recv rodex_check_player ZC_CHECK_RECEIVE_CHARACTER_NAME +0A13 26 send rodex_checkname CZ_CHECK_RECEIVE_CHARACTER_NAME + +// 2014-04-30aRagexeRE +0A15 11 recv ZC_GOLDPCCAFE_POINT +0A16 26 send CZ_DYNAMICNPC_CREATE_REQUEST +0A17 6 recv ZC_DYNAMICNPC_CREATE_RESULT + +// 2014-05-08bRagexeRE +0A15 12 recv ZC_GOLDPCCAFE_POINT + +// 2014-05-21aRagexeRE +0A07 9 recv rodex_remove_item ZC_ACK_REMOVE_RODEX_ITEM +0A14 10 recv rodex_check_player ZC_CHECK_RECEIVE_CHARACTER_NAME + +// 2014-06-05aRagexe +0A18 2 recv ZC_ACCEPT_ENTER3 +0A19 -1 send CZ_REQ_OPEN_ROULETTE clif->pRouletteOpen +0A1A 10 recv ZC_ACK_OPEN_ROULETTE +0A1B 2 send CZ_REQ_ROULETTE_INFO clif->pRouletteInfo +0A1C 6 recv ZC_ACK_ROULEITTE_INFO +0A1D 14 send CZ_REQ_CLOSE_ROULETTE clif->pRouletteClose + +// 2014-06-11bRagexe / RE. moved by 4144 +0A1E 3 recv ZC_ACK_CLOSE_ROULETTE +0A1F 2 send CZ_REQ_GENERATE_ROULETTE clif->pRouletteGenerate +0A20 21 recv ZC_ACK_GENERATE_ROULETTE +0A21 6 send CZ_RECV_ROULETTE_ITEM clif->pRouletteRecvItem +0A22 3 recv ZC_RECV_ROULETTE_ITEM +0A23 -1 recv achievement_list ZC_ALL_ACH_LIST +0A24 35 recv achievement_update ZC_ACH_UPDATE +0A25 6 send achievement_get_reward CZ_REQ_ACH_REWARD +0A26 7 recv achievement_reward_ack ZC_REQ_ACH_REWARD_ACK +0A18 14 recv ZC_ACCEPT_ENTER3 +0A19 2 send CZ_REQ_OPEN_ROULETTE clif->pRouletteOpen +0A1A 23 recv ZC_ACK_OPEN_ROULETTE +0A1C -1 recv ZC_ACK_ROULEITTE_INFO +0A1D 2 send CZ_REQ_CLOSE_ROULETTE clif->pRouletteClose + +// 2014-06-18cRagexeRE +0A21 3 send CZ_RECV_ROULETTE_ITEM clif->pRouletteRecvItem +0A22 5 recv ZC_RECV_ROULETTE_ITEM + +// 2014-06-25aRagexeRE +0A27 8 recv hp_sp_changed ZC_RECOVERY2 +0A28 3 recv ZC_ACK_OPENSTORE2 +0A24 36 recv achievement_update ZC_ACH_UPDATE + +// 2014-07-02aRagexeRE +0A29 6 recv ZC_REQ_AU_BOT +0A2A 6 send CZ_ACK_AU_BOT + +// 2014-07-16aRagexeRE +09E7 3 recv unread_rodex ZC_NOTIFY_UNREAD_RODEX + +// 2014-07-23aRagexeRE +0A2B 14 recv ZC_SE_CASHSHOP_OPEN2 +0A2C 12 recv ZC_SE_PC_BUY_TAIWANCASHITEM_RESULT +0A24 56 recv achievement_update ZC_ACH_UPDATE + +// 2014-08-20aRagexeRE +0A2D -1 recv show_eq ZC_EQUIPWIN_MICROSCOPE_V6 + +// 2014-09-03aRagexeRE +0A2E 6 send send_change_title CZ_REQ_CHANGE_TITLE +0A2F 7 recv change_title ZC_ACK_CHANGE_TITLE + +// 2014-09-24bRagexeRE +0A30 106 recv actor_info ZC_ACK_REQNAMEALL2 +0A31 -1 recv ZC_RESULT_PACKAGE_ITEM_TEST +0A32 2 recv ZC_OPEN_RODEX_THROUGH_NPC_ONLY +0A33 7 recv ZC_UPDATE_ROULETTE_COIN +0A34 6 recv senbei_amount ZC_UPDATE_TAIWANCASH + +// 2014-10-01bRagexeRE +0A24 66 recv achievement_update ZC_ACH_UPDATE + +// 2014-10-08bRagexeRE +0A05 49 recv rodex_add_item ZC_ACK_ADD_ITEM_RODEX + +// 2014-11-19bRagexeRE +0A35 4 send CZ_REQ_ONECLICK_ITEMIDENTIFY clif->pOneClick_ItemIdentify +0A05 53 recv rodex_add_item ZC_ACK_ADD_ITEM_RODEX + +// 2014-11-26aRagexeRE +0A36 7 recv monster_hp_info_tiny ZC_HP_INFO_TINY +0A37 57 recv inventory_item_added ZC_ITEM_PICKUP_ACK_V7 + +// 2015-01-28aRagexeRE +0A38 3 unkn + +// 2015-03-11aRagexeRE +0A3A 12 unkn + +// 2015-04-15aRagexeRE +0A39 36 send char_create CH_MAKE_CHAR chr->parse_char_create_new_char + +// 2015-04-22aRagexeRE +0A3B -1 recv hat_effect ZC_CUSTOM_HAT_EFFECT + +// 2015-05-20aRagexeRE +0A3C -1 unkn +0A3D 18 unkn + +// 2015-06-03bRagexeRE +0A3E -1 unkn + +// 2015-06-24aRagexeRE +0A3F 9 unkn + +// 2015-08-12aRagexeRE +0A40 11 unkn + +// 2015-09-09aRagexeRE +0A41 18 recv ZC_AOE_EFFECT_SKILL + +// 2015-09-16aRagexeRE +0A42 43 unkn + +// 2015-10-07aRagexeRE +0A43 85 recv party_join ZC_ADD_MEMBER_TO_GROUP +0A44 -1 recv party_users_info ZC_GROUP_LIST + +// 2015-10-28cRagexeRE +0A45 -1 unkn + +// 2015-11-04aRagexeRE +0A46 14 unkn +0A47 3 unkn +0A48 2 unkn + +// 2015-11-18aRagexeRE +0A49 22 unkn private_airship_request +0A4A 6 unkn private_airship_type +0A4B 22 unkn map_change +0A4C 28 unkn map_changed + +// 2016-01-27aRagexeRE +0A4D -1 unkn + +// 2016-03-02bRagexeRE +0A4E 4 unkn +0A4F -1 unkn +0A50 6 unkn +0A51 34 recv rodex_check_player ZC_CHECK_RECEIVE_CHARACTER_NAME + +// 2016-03-16aRagexeRE +0A52 20 unkn +0A53 10 unkn +0A54 -1 unkn +0A55 2 unkn +0A56 6 unkn +0A57 6 unkn +0A58 8 unkn +0A59 -1 unkn +0A5A 2 unkn +0A5B 7 unkn +0A5C 18 unkn +0A5D 6 unkn + +// 2016-03-23aRagexeRE +0A68 3 unkn +0A69 6 unkn +0A6A 12 unkn +0A6B -1 unkn + +// 2016-03-30aRagexeRE +0A6C 7 unkn +0A6D -1 unkn +0A6E -1 send rodex_send_mail CZ_REQ_SEND_RODEX clif->pRodexSendMail +0A6F -1 unkn + +// 2016-04-27aRagexeRE +0A50 4 unkn + +// 2016-05-04aRagexeRE +0A70 2 unkn +0A71 -1 unkn +0A72 61 unkn + +// 2016-05-11aRagexeRE +0A73 6 unkn +0A74 8 unkn + +// 2016-05-18aRagexeRE +0A76 80 unkn +0A73 2 unkn + +// 2016-05-25aRagexeRE +0A77 15 unkn +0A78 15 unkn + +// 2016-06-01aRagexeRE +0A79 -1 unkn +0A7B -1 unkn +0A7C -1 unkn +0A7D -1 recv rodex_mail_list ZC_ACK_RODEX_LIST + +// 2016-06-15aRagexeRE +0A7E 4 recv ZC_OFFLINE_STORE_OWNER_ITEMS_READY +0A7F -1 send CZ_OFFLINE_STORE_CREATE +0A80 2 unkn +0A81 3 unkn + +// 2016-06-22aRagexeRE +0A82 46 unkn +0A83 46 unkn +0A84 94 recv ZC_GUILD_INFO +0A85 82 unkn +0A86 -1 unkn +0A87 4 unkn +0A88 2 unkn + +// 2016-06-29aRagexeRE +0A89 32 recv clone_vender_found ZC_NOTIFY_OFFLINE_STORE +0A8A 6 recv clone_vender_lost ZC_OFFLINE_STORE_VANISH +0A8B 2 unkn +0A8C 2 unkn +0A8D -1 recv ZC_OFFLINE_STORE_OWNER_ITEMS +0A80 6 unkn + +// 2016-07-06cRagexeRE +0A81 3 unkn +0A7E -1 recv ZC_OFFLINE_STORE_OWNER_ITEMS_READY +0A89 57 recv clone_vender_found ZC_NOTIFY_OFFLINE_STORE + +// 2016-07-13aRagexeRE +0A87 -1 unkn + +// 2016-07-20aRagexeRE +0A8E 2 unkn +0A8F 2 unkn +0A90 3 unkn + +// 2016-07-27aRagexeRE +0A91 -1 unkn +0A92 -1 unkn +0A93 3 unkn + +// 2016-08-03bRagexeRE +0A94 2 unkn +0A81 4 unkn + +// 2016-09-07aRagexeRE +0A95 4 unkn + +// 2016-09-21bRagexeRE +0A96 51 unkn +0A37 59 recv inventory_item_added ZC_ITEM_PICKUP_ACK_V7 + +// 2016-09-28cRagexeRE +0A97 8 unkn +0A98 12 unkn +0A99 8 unkn +0A9A 10 unkn +0A9B -1 unkn +0A9C 2 unkn +0A9D 4 unkn +0A9E 2 unkn +0A9F 2 unkn + +// 2016-10-05aRagexeRE +0AA0 2 unkn refineui_opened +0AA1 4 unkn refineui_select +0AA2 -1 unkn refineui_info +0AA3 7 unkn refineui_refine +0AA4 2 unkn refineui_close + +// 2016-10-26bRagexeRE +0AA5 -1 recv ZC_MEMBERMGR_INFO +0AA6 36 unkn + +// 2016-11-30aRagexeRE +0AA7 6 unkn +0AA8 5 unkn +0AA9 -1 unkn +0AAA -1 unkn +0AAB -1 unkn + +// 2016-12-07cRagexeRE +0AAC 67 unkn master_login + +// 2016-12-21aRagexeRE +0AAD 47 unkn +0AAE 2 unkn +0AAF 6 unkn +0AB0 6 unkn +0AB1 10 unkn + +// 2016-12-28aRagexeRE +0AB1 14 unkn + +// 2017-01-04bRagexeRE +0AB2 7 unkn +0AB3 15 unkn + +// 2017-01-11aRagexeRE +0AB4 4 unkn +0AB5 2 unkn +0AB6 6 unkn +0AB7 4 unkn +0AB8 2 unkn move_interrupt +0AB9 39 unkn + +// 2017-01-18aRagexeRE +0ABA 2 unkn +0ABB 2 unkn +0AAD 51 unkn +0AB3 19 unkn + +// 2017-02-01aRagexeRE +0ABC -1 unkn + +// 2017-02-15aRagexeRE +0ABD 10 unkn partylv_info + +// 2017-02-22aRagexeRE +0ABE 116 unkn warp_portal_list +0ABF 114 unkn + +// 2017-02-28aRagexeRE +0AC0 26 send CZ_OPEN_RODEXBOX clif->pRodexOpenMailbox +0AC1 26 send CZ_REQ_REFRESH_RODEX clif->pRodexRefreshMaillist +0AC2 -1 recv ZC_ACK_RODEX_LIST +0AC3 2 unkn +0AC4 -1 recv account_server_info AC_ACCEPT_LOGIN +0AC5 156 recv received_character_ID_and_Map HC_NOTIFY_ZONESVR +0AC6 156 unkn +0AC7 156 recv map_changed ZC_NPCACK_SERVERMOVE +0ABE -1 unkn warp_portal_list +0ABF -1 unkn + +// 2017-03-08bRagexeRE +0AC8 2 unkn +0AC9 -1 unkn account_server_info + +// 2017-03-22aRagexeRE +0ACA 3 unkn errors + +// 2017-03-29cRagexeRE +0AAC 69 unkn master_login + +// 2017-04-05bRagexeRE +0ACB 12 recv stat_info ZC_PAR_CHANGE +0ACC 18 recv exp ZC_NOTIFY_EXP + +// 2017-04-19bRagexeRE +0ACD 23 unkn login_error +0A99 4 unkn + +// 2017-04-26dRagexeRE +0A98 10 unkn + +// 2017-05-02dRagexeRE +0ACE 4 unkn + +// 2017-06-14bRagexeRE +0ACF 52 send master_login CA_LOGIN_OTP lclif->p->parse_CA_LOGIN_OTP +0AD0 11 unkn +0AD1 -1 unkn + +// 2017-06-21aRagexeRE +0ACF 57 send master_login CA_LOGIN_OTP lclif->p->parse_CA_LOGIN_OTP + +// 2017-07-05aRagexeRE +0ACF 64 send master_login CA_LOGIN_OTP lclif->p->parse_CA_LOGIN_OTP + +// 2017-07-19aRagexeRE +0AD2 30 unkn +0AD3 -1 unkn +0AD4 -1 unkn +0AD5 2 unkn +0AD6 2 unkn +0AD7 8 unkn +0AD8 8 unkn +0AD9 -1 unkn + +// 2017-07-26cRagexeRE +0ADA 30 unkn + +// 2017-08-30aRagexeRE +0ADB -1 unkn +006D 157 recv character_creation_successful HC_ACCEPT_MAKECHAR +08E3 157 recv HC_UPDATE_CHARINFO + +// 2017-09-06cRagexeRE +0ADC 6 recv flag ZC_EQUIPWIN_OTHER + +// 2017-09-13bRagexeRE +0ADD 22 unkn item_exists +0ADE 6 unkn overweight_percent +0ADF 58 recv actor_info ZC_ACK_REQNAME +0AE0 30 unkn + +// 2017-10-25bRagexe_zero +0AE1 28 unkn + +// 2017-11-01bRagexeRE +0AE1 28 unkn + +// 2017-11-09aRagexe +0AE2 7 unkn + +// 2017-11-13aRagexe +0ACF 68 send master_login CA_LOGIN_OTP lclif->p->parse_CA_LOGIN_OTP + +// 2017-11-15aRagexeRE +0AE2 7 unkn +0ACF 68 send master_login CA_LOGIN_OTP lclif->p->parse_CA_LOGIN_OTP + +// 2017-11-23dRagexe +0AE3 -1 recv received_login_token AC_LOGIN_OTP + +// 2017-11-30bRagexe +0AE4 89 recv party_join ZC_ADD_MEMBER_TO_GROUP +0AE5 -1 recv party_users_info ZC_GROUP_LIST + +// 2017-12-06aRagexeRE +0AE4 89 recv party_join ZC_ADD_MEMBER_TO_GROUP +0AE5 -1 recv party_users_info ZC_GROUP_LIST +0AE3 -1 recv received_login_token AC_LOGIN_OTP +0AE6 30 unkn +0AE7 30 unkn +0AE6 30 unkn +0AE7 30 unkn +0AE8 2 unkn change_dress + +// 2017-12-13bRagexe +0AE3 -1 recv received_login_token AC_LOGIN_OTP +0AE6 30 unkn +0AE7 30 unkn + +// 2017-12-20aRagexe +0AE8 2 unkn change_dress +0AE9 64 unkn +0AEA 11 unkn + +// 2017-12-27aRagexe +0AEB 11 unkn +0AEC 2 unkn +0AEA 2 unkn + +// 2018-01-03aRagexe +0AED 2 unkn +0AEE 2 unkn +09A0 6 recv sync_received_characters HC_CHARLIST_NOTIFY +0AEF 2 unkn +0AF0 10 unkn +0AE9 13 unkn + +// 2018-01-24bRagexeRE +0AF2 40 unkn +0AF3 -1 unkn + +// 2018-01-31dRagexe_zero +0AF2 40 unkn +0AF3 -1 unkn +0AF4 11 unkn +0AE6 10 unkn + +// 2018-02-07bRagexe_zero +0AF5 3 unkn +0AF6 88 unkn +0AF7 32 unkn + +// 2018-02-07bRagexeRE, 2018-02-07bRagexe +0AF4 11 unkn +0AF5 3 unkn +0AF6 88 unkn +0AF7 32 unkn +0AE6 10 unkn + +// 2018-02-21aRagexeRE +0206 35 recv friend_logon ZC_FRIENDS_STATE + +// 2018-03-07bRagexe +0206 35 recv friend_logon ZC_FRIENDS_STATE + +// 2018-03-21aRagexe, 2018-03-21aRagexeRE +0AF8 11 unkn +0AE7 34 unkn + +// 2018-03-28_1aRagexe_zero +0AF8 11 unkn +0AF9 6 unkn +0AFA 54 unkn +0206 35 recv friend_logon ZC_FRIENDS_STATE +0AE7 38 unkn + +// 2018-04-04bRagexe, 2018-04-04cRagexeRE +0AF9 6 unkn +0AFA 54 unkn +0AE7 38 unkn + +// 2018-04-18aRagexe, 2018-04-18bRagexeRE +0AFB -1 unkn + +// 2018-04-25_3aRagexe_zero +0AFB -1 unkn + +// 2018-05-16cRagexe, 2018-05-16cRagexeRE +0AFC 16 unkn + +// 2018-05-23aRagexe_zero +0AFC 16 unkn + +// 2018-06-05bRagexe, 2018-06-05bRagexeRE, 2018-06-05bRagexe_zero +0AFD -1 unkn +0AFE -1 unkn +0AFF -1 unkn + +// 2018-06-20cRagexe, 2018-06-20eRagexe, 2018-06-20dRagexeRE, 2018-06-20eRagexeRE +0B00 8 unkn +0B01 40 unkn + +// 2018-07-04aRagexe, 2018-07-04aRagexeRE +0B02 26 unkn +0B01 56 unkn + +// 2018-06-27aRagexe_zero +0B00 8 unkn +0B01 56 unkn +0B02 26 unkn + +// 2018-07-04aRagexeRE +009D 19 recv item_exists ZC_ITEM_ENTRY +009E 19 recv item_appeared ZC_ITEM_FALL_ENTRY +00A0 33 recv inventory_item_added ZC_ITEM_PICKUP_ACK +00E9 29 recv deal_add_other ZC_ADD_EXCHANGE_ITEM +00F4 31 recv storage_item_added ZC_ADD_ITEM_TO_STORE +010A 6 recv mvp_item ZC_MVP_GETTING_ITEM +0110 14 recv skill_use_failed ZC_ACK_TOUSESKILL +0124 31 recv cart_item_added ZC_ADD_ITEM_TO_CART +018E 18 send make_item_request CZ_REQMAKINGITEM clif->pProduceMix +018F 8 recv refine_result ZC_ACK_REQMAKINGITEM +01A3 7 recv pet_food ZC_FEED_PET +01AE 6 send make_arrow CZ_REQ_MAKINGARROW clif->pSelectArrow +01C4 32 recv storage_item_added ZC_ADD_ITEM_TO_STORE2 +01C5 32 recv cart_item_added ZC_ADD_ITEM_TO_CART2 +01C8 15 recv item_used ZC_USE_ITEM_ACK2 +01D7 15 recv player_equipment ZC_SPRITE_CHANGE2 +01D8 58 recv actor_exists ZC_NOTIFY_STANDENTRY2 +01D9 57 recv actor_connected ZC_NOTIFY_NEWENTRY2 +01DA 64 recv actor_moved ZC_NOTIFY_MOVEENTRY2 +01FD 25 send repair_item CZ_REQ_ITEMREPAIR clif->pRepairItem +0223 10 recv upgrade_message ZC_ACK_WEAPONREFINE +022A 62 recv actor_exists ZC_NOTIFY_STANDENTRY3 +022B 61 recv actor_connected ZC_NOTIFY_NEWENTRY3 +022C 69 recv actor_moved ZC_NOTIFY_MOVEENTRY3 +022E 73 recv homunculus_property ZC_PROPERTY_HOMUN +022F 7 recv homunculus_food ZC_FEED_MER +025B 8 send cook_request CZ_REQ_MAKINGITEM clif->pCooking +0298 10 recv rental_time ZC_CASH_TIME_COUNTER +0299 8 recv rental_expired ZC_CASH_ITEM_DELETE +029A 37 recv inventory_item_added ZC_ITEM_PICKUP_ACK2 +02B8 32 recv party_show_picker ZC_ITEM_PICKUP_PARTY +02D4 39 recv inventory_item_added ZC_ITEM_PICKUP_ACK3 +02EC 71 recv actor_exists ZC_NOTIFY_MOVEENTRY4 +02ED 63 recv actor_connected ZC_NOTIFY_NEWENTRY4 +02EE 64 recv actor_moved ZC_NOTIFY_STANDENTRY4 +0445 12 send CZ_SIMPLE_BUY_CASH_POINT_ITEM +080F 30 recv deal_add_other ZC_ADD_EXCHANGE_ITEM2 +081B 12 recv buying_store_update ZC_UPDATE_ITEM_FROM_BUYING_STORE +0824 8 recv buying_store_fail ZC_FAILED_TRADE_BUYING_STORE_TO_SELLER +084B 21 recv item_appeared ZC_ITEM_FALL_ENTRY4 +0990 41 recv inventory_item_added ZC_ITEM_PICKUP_ACK_V5 +09AD 12 recv ZC_ACK_CASH_BARGAIN_SALE_ITEM_INFO +09AE 19 send CZ_REQ_APPLY_BARGAIN_SALE_ITEM +09B0 10 send CZ_REQ_REMOVE_BARGAIN_SALE_ITEM +09B2 10 recv ZC_NOTIFY_BARGAIN_SALE_SELLING +09B3 6 recv ZC_NOTIFY_BARGAIN_SALE_CLOSE +09C3 10 send CZ_REQ_COUNT_BARGAIN_SALE_ITEM +09C4 10 recv ZC_ACK_COUNT_BARGAIN_SALE_ITEM +09E6 24 recv ZC_UPDATE_ITEM_FROM_BUYING_STORE2 +09F7 77 recv homunculus_property ZC_PROPERTY_HOMUN_2 +0A05 63 recv rodex_add_item ZC_ACK_ADD_ITEM_RODEX +0A09 55 recv deal_add_other ZC_ADD_EXCHANGE_ITEM3 +0A0A 57 recv storage_item_added ZC_ADD_ITEM_TO_STORE3 +0A0B 57 recv cart_item_added ZC_ADD_ITEM_TO_CART3 +0A0C 66 recv inventory_item_added ZC_ITEM_PICKUP_ACK_V6 +0A1A 25 recv ZC_ACK_OPEN_ROULETTE +0A20 23 recv ZC_ACK_GENERATE_ROULETTE +0A22 7 recv ZC_RECV_ROULETTE_ITEM +0A37 69 recv inventory_item_added ZC_ITEM_PICKUP_ACK_V7 +0A3D 20 unkn +0A3F 11 unkn +0A49 22 unkn private_airship_request +0A4E 6 unkn +0A89 61 recv clone_vender_found ZC_NOTIFY_OFFLINE_STORE +0A96 61 unkn +0AA3 9 unkn refineui_refine +0AB4 6 unkn +0AB6 8 unkn +0AB9 47 unkn +0ADA 32 unkn +0ADD 24 unkn item_exists + +// 2018-07-25_2aRagexe_zero +0B03 -1 unkn + +// 2018-08-01cRagexe, 2018-08-01cRagexeRE +0B03 -1 unkn +0B04 80 unkn + +// 2018-08-08_2aRagexe_zero +0B04 80 unkn + +// 2018-08-22aRagexe_zero +0B05 59 unkn +0B06 53 unkn +0B07 -1 unkn +0B08 26 unkn +0B09 -1 unkn +0B0A -1 unkn +0B0B 3 unkn +0B0C 155 unkn diff --git a/openkore_llm_knowledge/tables/portals.txt b/openkore_llm_knowledge/tables/portals.txt new file mode 100644 index 0000000000..bb30a735d7 --- /dev/null +++ b/openkore_llm_knowledge/tables/portals.txt @@ -0,0 +1,3576 @@ +#format: <source coordinate><destination coordinate>[zenny cost][allow ticket][conversation sequence] +alberta 195 151 alb2trea 62 69 0 c c r0 +alb2trea 39 50 alberta 192 169 0 r0 +alberta 247 122 tur_dun01 157 39 10000 c r1 c r0 c c c +alberta 245 45 lou_fild01 190 101 10000 c r1 c c r0 +alberta 247 42 ayothaya 149 71 10000 c r1 c c r0 n +lou_fild01 190 100 alberta 235 45 0 c r0 +tur_dun01 165 29 alberta 241 115 0 c r0 c c +alberta 245 93 amatsu 197 83 10000 c r1 c c c r0 n +amatsu 194 79 alberta 243 91 0 c r0 n +alberta 245 62 gon_fild01 258 82 0 c r1 c c r0 n +gon_fild01 255 79 alberta 243 67 0 c r0 n +izlude 201 181 izlu2dun 107 50 150 c r0 +izlu2dun 108 27 izlude 176 182 0 c r0 +prt_fild05 270 212 prt_sewb1 135 248 0 c r0 +aldebaran 223 222 xmas_fild01 78 68 0 c r1 c c n +moc_fild12 35 303 cmd_fild08 331 319 0 c r0 n +ayothaya 152 68 alberta 247 42 0 c r0 n +ama_in02 115 177 ama_dun01 229 10 0 c r1 c r0 n +ama_dun01 229 7 ama_in02 119 181 0 c r0 n + +# VIP Warps +#yuno 142 183 prontera 116 72 1800 c c c c c r0 +#yuno 142 183 izlude 94 103 1800 c c c c c r1 +#yuno 142 183 geffen 120 39 1800 c c c c c r2 +#yuno 142 183 morocc 156 47 1800 c c c c c r3 +#yuno 142 183 payon 161 58 1800 c c c c c r4 +#yuno 142 183 alberta 117 56 1800 c c c c c r5 +#yuno 142 183 comodo 209 143 1800 c c c c c r6 +#yuno 152 187 aldebaran 168 112 1200 c r2 c r0 +#aldebaran 143 119 geffen 120 39 2300 c r2 c r0 +#aldebaran 143 119 yuno 158 125 1200 c r2 c r1 +#aldebaran 143 119 izude 94 103 1800 c r2 c r2 +#aldebaran 143 119 mjolnir_02 99 351 1700 c r2 c r3 +#morocc 156 97 prontera 116 72 1200 c r2 c r0 +#morocc 156 97 payon 161 58 1200 c r2 c r1 +#morocc 156 97 alberta 117 56 1800 c r2 c r2 +#morocc 156 97 comodo 209 143 1800 c r2 c r3 +#morocc 156 97 cmd_fild07 127 134 1200 c r2 c r4 +#morocc 160 258 prontera 116 72 1200 c r2 c r0 +#morocc 160 258 payon 161 58 1200 c r2 c r1 +#morocc 160 258 alberta 117 56 1800 c r2 c r2 +#morocc 160 258 comodo 209 143 1800 c r2 c r3 +#morocc 160 258 cmd_fild07 127 134 1200 c r2 c r4 +#cmd_fild07 136 134 morocc 156 47 1200 c c r2 c r0 +#cmd_fild07 136 134 comodo 209 143 1200 c c r2 c r1 +#comodo 195 150 morocc 156 47 1800 c c r2 c r0 +#comodo 195 150 cmd_fild07 127 134 1200 c c r2 c r1 +#comodo 195 150 umbala 100 154 1800 c c r2 c r2 +#umbala 87 160 comodo 209 143 1800 c r2 c r0 +#prontera 146 89 izlude 94 103 600 c r2 c r0 +#prontera 146 89 geffen 120 39 1200 c r2 c r1 +#prontera 146 89 payon 161 58 1200 c r2 c r2 +#prontera 146 89 morocc 156 47 1200 c r2 c r3 +#prontera 146 89 gef_fild10 52 326 1700 c r2 c r4 +#prontera 146 89 alberta 117 56 1800 c r2 c r5 +#prontera 151 29 izlude 94 103 600 c r2 c r0 +#prontera 151 29 geffen 120 39 1200 c r2 c r1 +#prontera 151 29 payon 161 58 1200 c r2 c r2 +#prontera 151 29 morocc 156 47 1200 c r2 c r3 +#prontera 151 29 gef_fild10 52 326 1700 c r2 c r4 +#prontera 151 29 alberta 117 56 1800 c r2 c r5 +#prontera 282 200 izlude 94 103 600 c r2 c r0 +#prontera 282 200 geffen 120 39 1200 c r2 c r1 +#prontera 282 200 payon 161 58 1200 c r2 c r2 +#prontera 282 200 morocc 156 47 1200 c r2 c r3 +#prontera 282 200 gef_fild10 52 326 1700 c r2 c r4 +#prontera 282 200 alberta 117 56 1800 c r2 c r5 +#prontera 35 208 izlude 94 103 2000 1 c r2 c r0 +#prontera 35 208 geffen 120 39 2000 1 c r2 c r1 +#prontera 35 208 payon 161 58 2000 1 c r2 c r2 +#prontera 35 208 morocc 156 47 2000 1 c r2 c r3 +#prontera 35 208 alberta 117 56 2000 1 c r2 c r4 +#prontera 35 208 aldebaran 168 112 2000 1 c r2 c r5 +#prontera 35 208 comodo 209 143 3000 1 c r2 c r6 +#prontera 35 208 umbala 100 154 3000 1 c r2 c r7 +#payon 175 226 prontera 116 72 1200 c r2 c r0 +#payon 175 226 alberta 117 56 1200 c r2 c r1 +#payon 175 226 morocc 156 47 1200 c r2 c r2 +#payon 181 104 prontera 116 72 1200 c r2 c r0 +#payon 181 104 alberta 117 56 1200 c r2 c r1 +#payon 181 104 morocc 156 47 1200 c r2 c r2 +#alberta 113 60 payon 161 58 1200 c r2 c r0 +#alberta 113 60 morocc 156 47 1800 c r2 c r1 +#alberta 113 60 prontera 116 72 1800 c r2 c r2 +#alberta 28 229 payon 161 58 1200 c r2 c r0 +#alberta 28 229 morocc 156 47 1800 c r2 c r1 +#alberta 28 229 prontera 116 72 1800 c r2 c r2 +#izlude 134 88 geffen 120 39 1200 c r2 c r0 +#izlude 134 88 payon 161 58 1200 c r2 c r1 +#izlude 134 88 morocc 156 47 1200 c r2 c r2 +#izlude 134 88 aldebaran 168 112 1800 c r2 c r3 +# to add: Eden warper, Prontera warper + +# Non-VIP Warps +prontera 146 89 izlude 94 103 2000 1 c r2 c r0 +prontera 146 89 geffen 120 39 2000 1 c r2 c r1 +prontera 146 89 payon 161 58 2000 1 c r2 c r2 +prontera 146 89 morocc 156 47 2000 1 c r2 c r3 +prontera 146 89 alberta 117 56 2000 1 c r2 c r4 +prontera 146 89 aldebaran 168 112 2000 1 c r2 c r5 +prontera 146 89 comodo 209 143 3000 1 c r2 c r6 +prontera 146 89 umbala 100 154 3000 1 c r2 c r7 +prontera 151 29 izlude 94 103 2000 1 c r2 c r0 +prontera 151 29 geffen 120 39 2000 1 c r2 c r1 +prontera 151 29 payon 161 58 2000 1 c r2 c r2 +prontera 151 29 morocc 156 47 2000 1 c r2 c r3 +prontera 151 29 alberta 117 56 2000 1 c r2 c r4 +prontera 151 29 aldebaran 168 112 2000 1 c r2 c r5 +prontera 151 29 comodo 209 143 3000 1 c r2 c r6 +prontera 151 29 umbala 100 154 3000 1 c r2 c r7 +prontera 282 200 izlude 94 103 2000 1 c r2 c r0 +prontera 282 200 geffen 120 39 2000 1 c r2 c r1 +prontera 282 200 payon 161 58 2000 1 c r2 c r2 +prontera 282 200 morocc 156 47 2000 1 c r2 c r3 +prontera 282 200 alberta 117 56 2000 1 c r2 c r4 +prontera 282 200 aldebaran 168 112 2000 1 c r2 c r5 +prontera 282 200 comodo 209 143 3000 1 c r2 c r6 +prontera 282 200 umbala 100 154 3000 1 c r2 c r7 +prontera 35 208 izlude 94 103 2000 1 c r2 c r0 +prontera 35 208 geffen 120 39 2000 1 c r2 c r1 +prontera 35 208 payon 161 58 2000 1 c r2 c r2 +prontera 35 208 morocc 156 47 2000 1 c r2 c r3 +prontera 35 208 alberta 117 56 2000 1 c r2 c r4 +prontera 35 208 aldebaran 168 112 2000 1 c r2 c r5 +prontera 35 208 comodo 209 143 3000 1 c r2 c r6 +prontera 35 208 umbala 100 154 3000 1 c r2 c r7 +prontera 98 120 yuno 157 167 1800 1 c c r0 c +geffen 120 62 prontera 116 72 1200 1 c r2 c r0 +geffen 120 62 aldebaran 168 112 1200 1 c r2 c r1 +geffen 120 62 gef_fild10 52 326 1200 1 c r2 c r2 +geffen 120 62 mjolnir_02 99 351 1700 1 c r2 c r3 +geffen 203 123 prontera 116 72 1200 1 c r2 c r0 +geffen 203 123 aldebaran 168 112 1200 1 c r2 c r1 +geffen 203 123 gef_fild10 52 326 1200 1 c r2 c r2 +geffen 203 123 mjolnir_02 99 351 1700 1 c r2 c r3 +aldebaran 143 119 geffen 120 39 2000 1 c r2 c r0 +izlude 134 88 prontera 116 72 2000 1 c r2 c r0 +yuno 152 187 einbroch 67 195 2000 1 c r2 c r0 +yuno 152 187 lighthalzen 159 90 2000 1 c r2 c r1 +yuno 152 187 hugel 98 150 2000 1 c r2 c r2 +yuno 152 187 rachel 119 135 2000 1 c r2 c r3 +yuno 327 108 aldebaran 168 112 2000 1 c r2 c r0 +yuno 142 183 prontera 116 72 1800 1 c c r0 c n +morocc 156 97 prontera 116 72 2000 1 c r2 c r0 +comodo 195 150 morocc 156 47 2000 1 c c r2 c r0 +payon 181 104 prontera 116 72 2000 1 c r2 c r0 +payon 175 226 prontera 116 72 1200 1 c r2 c r0 +alberta 113 60 payon 161 58 2000 1 c r2 c r0 +rachel 109 138 veins 205 101 2200 1 c r2 c r0 +rachel 109 138 yuno 158 125 3000 1 c r2 c r1 +veins 208 128 rachel 115 125 2200 1 c r2 c r0 +veins 208 128 yuno 158 125 3000 1 c r2 c r1 +moc_para01 14 32 prontera 116 72 1800 1 c c r0 c n + +# Training Grounds +new_1-1 148 112 new_1-2 100 9 +new_2-1 148 112 new_2-2 100 9 +new_3-1 148 112 new_3-2 100 9 +new_4-1 148 112 new_4-2 100 9 +new_5-1 148 112 new_5-2 100 9 +new_1-2 100 6 new_1-1 144 112 +new_2-2 100 6 new_2-1 144 112 +new_3-2 100 6 new_3-1 144 112 +new_4-2 100 6 new_4-1 144 112 +new_5-2 100 6 new_5-1 144 112 + +# Training Grounds entrance +new_1-2 100 29 new_1-2 100 70 0 c r0 c c +new_2-2 100 29 new_2-2 100 70 0 c r0 c c +new_3-2 100 29 new_3-2 100 70 0 c r0 c c +new_4-2 100 29 new_4-2 100 70 0 c r0 c c +new_5-2 100 29 new_5-2 100 70 0 c r0 c c + +# Training Grounds Kafra +new_1-2 118 108 prontera 150 50 0 c c c r0 c c c r0 c +new_2-2 118 108 prontera 150 50 0 c c c r0 c c c r0 c +new_3-2 118 108 prontera 150 50 0 c c c r0 c c c r0 c +new_4-2 118 108 prontera 150 50 0 c c c r0 c c c r0 c +new_5-2 118 108 prontera 150 50 0 c c c r0 c c c r0 c +new_1-2 118 108 morocc 155 110 0 c c c r0 c c c r1 c +new_2-2 118 108 morocc 155 110 0 c c c r0 c c c r1 c +new_3-2 118 108 morocc 155 110 0 c c c r0 c c c r1 c +new_4-2 118 108 morocc 155 110 0 c c c r0 c c c r1 c +new_5-2 118 108 morocc 155 110 0 c c c r0 c c c r1 c +new_1-2 118 108 payon 166 67 0 c c c r0 c c c r2 c +new_2-2 118 108 payon 166 67 0 c c c r0 c c c r2 c +new_3-2 118 108 payon 166 67 0 c c c r0 c c c r2 c +new_4-2 118 108 payon 166 67 0 c c c r0 c c c r2 c +new_5-2 118 108 payon 166 67 0 c c c r0 c c c r2 c +new_1-2 118 108 alberta 114 58 0 c c c r0 c c c r3 c +new_2-2 118 108 alberta 114 58 0 c c c r0 c c c r3 c +new_3-2 118 108 alberta 114 58 0 c c c r0 c c c r3 c +new_4-2 118 108 alberta 114 58 0 c c c r0 c c c r3 c +new_5-2 118 108 alberta 114 58 0 c c c r0 c c c r3 c +new_1-2 118 108 geffen 122 65 0 c c c r0 c c c r4 c +new_2-2 118 108 geffen 122 65 0 c c c r0 c c c r4 c +new_3-2 118 108 geffen 122 65 0 c c c r0 c c c r4 c +new_4-2 118 108 geffen 122 65 0 c c c r0 c c c r4 c +new_5-2 118 108 geffen 122 65 0 c c c r0 c c c r4 c + +new_1-2 126 106 new_1-2 160 171 +new_2-2 126 106 new_2-2 160 171 +new_3-2 126 106 new_3-2 160 171 +new_4-2 126 106 new_4-2 160 171 +new_5-2 126 106 new_5-2 160 171 +new_1-2 156 171 new_1-2 123 106 +new_2-2 156 171 new_2-2 123 106 +new_3-2 156 171 new_3-2 123 106 +new_4-2 156 171 new_4-2 123 106 +new_5-2 156 171 new_5-2 123 106 +new_1-2 73 106 new_1-2 41 172 +new_2-2 73 106 new_2-2 41 172 +new_3-2 73 106 new_3-2 41 172 +new_4-2 73 106 new_4-2 41 172 +new_5-2 73 106 new_5-2 41 172 +new_1-2 46 172 new_1-2 78 106 +new_2-2 46 172 new_2-2 78 106 +new_3-2 46 172 new_3-2 78 106 +new_4-2 46 172 new_4-2 78 106 +new_5-2 46 172 new_5-2 78 106 + +new_1-3 96 174 new_1-4 99 10 0 c r0 c +new_2-3 96 174 new_2-4 99 10 0 c r0 c +new_3-3 96 174 new_3-4 99 10 0 c r0 c +new_4-3 96 174 new_4-4 99 10 0 c r0 c +new_5-3 96 174 new_5-4 99 10 0 c r0 c + +# Training Grounds exit +new_1-4 100 29 moc_ruins 155 44 0 c c c c c r0 c c c c r1 r0 r0 r0 r1 r1 c c r0 c r0 c r0 c r1 c c r0 c r1 c r1 c r1 c c r2 c r2 c r2 c r0 c r2 c r2 c r0 c r1 c c c c c c c c c c r0 c c c c c c c c +new_2-4 100 29 moc_ruins 155 44 0 c c c c c r0 c c c c r1 r0 r0 r0 r1 r1 c c r0 c r0 c r0 c r1 c c r0 c r1 c r1 c r1 c c r2 c r2 c r2 c r0 c r2 c r2 c r0 c r1 c c c c c c c c c c r0 c c c c c c c c +new_3-4 100 29 moc_ruins 155 44 0 c c c c c r0 c c c c r1 r0 r0 r0 r1 r1 c c r0 c r0 c r0 c r1 c c r0 c r1 c r1 c r1 c c r2 c r2 c r2 c r0 c r2 c r2 c r0 c r1 c c c c c c c c c c r0 c c c c c c c c +new_4-4 100 29 moc_ruins 155 44 0 c c c c c r0 c c c c r1 r0 r0 r0 r1 r1 c c r0 c r0 c r0 c r1 c c r0 c r1 c r1 c r1 c c r2 c r2 c r2 c r0 c r2 c r2 c r0 c r1 c c c c c c c c c c r0 c c c c c c c c +new_5-4 100 29 moc_ruins 155 44 0 c c c c c r0 c c c c r1 r0 r0 r0 r1 r1 c c r0 c r0 c r0 c r1 c c r0 c r1 c r1 c r1 c c r2 c r2 c r2 c r0 c r2 c r2 c r0 c r1 c c c c c c c c c c r0 c c c c c c c c + +# Izlude Arena +arena_room 105 93 prt_are_in 60 14 0 c r0 +prt_are_in 54 13 arena_room 100 77 + +# Anthell +#anthell01 35 267 moc_fild20 161 144 +#anthell02 171 170 moc_fild20 333 315 +#moc_fild20 156 143 anthell01 35 263 +#moc_fild20 337 315 anthell02 168 170 +anthell01 35 267 cmd_fild08 335 355 +anthell02 171 170 cmd_fild08 344 82 +cmd_fild08 335 355 anthell01 35 263 +cmd_fild08 348 82 anthell02 171 170 + +# Morroc area (Episode 12) +moc_fild01 84 19 moc_fild20 209 333 +moc_fild07 380 202 moc_fild20 36 177 +moc_fild11 189 360 moc_fild20 197 24 +moc_fild11 377 197 moc_fild20 36 177 +moc_fild13 32 171 moc_fild20 349 179 +moc_fild16 124 381 moc_fild20 197 24 +moc_fild16 333 380 moc_fild20 197 24 +moc_fild20 38 174 morocc 160 61 0 c r1 c c c c r1 c c r1 +moc_fild20 38 183 morocc 160 61 0 c r1 c c c c r1 c c r1 +moc_fild20 189 21 morocc 160 61 0 c r1 c c c c r1 c c r1 +moc_fild20 200 21 morocc 160 61 0 c r1 c c c c r1 c c r1 +moc_fild20 354 183 morocc 160 61 0 c r1 c c c c r1 c c r1 +moc_fild20 354 174 morocc 160 61 0 c r1 c c c c r1 c c r1 +moc_fild20 203 336 moc_fild01 86 32 0 c r1 c c c c r2 +moc_fild20 215 336 moc_fild01 86 32 0 c r1 c c c c r2 +moc_fild20 203 336 morocc 160 61 0 c r1 c c c c r1 c c r1 +moc_fild20 215 336 morocc 160 61 0 c r1 c c c c r1 c c r1 +morocc 299 207 moc_fild20 36 177 +prt_fild09 95 19 moc_fild20 209 333 +prt_fild09 246 17 moc_fild20 209 333 +prt_fild10 263 23 moc_fild20 209 333 + +# Dimensional Gorge (Episode 12) +# moc_fild20 -> moc_fild21 npcs are only available with the quest and a party with at least two members online +moc_fild20 38 174 moc_fild21 38 193 0 c r1 c c c +moc_fild20 38 183 moc_fild21 38 193 0 c r1 c c c +moc_fild20 189 21 moc_fild21 38 193 0 c r1 c c c +moc_fild20 200 21 moc_fild21 38 193 0 c r1 c c c +moc_fild20 354 183 moc_fild21 38 193 0 c r1 c c c +moc_fild20 354 174 moc_fild21 38 193 0 c r1 c c c +moc_fild20 203 336 moc_fild21 38 193 0 c r1 c c c +moc_fild20 215 336 moc_fild21 38 193 0 c r1 c c c +moc_fild21 26 196 moc_fild20 349 179 +moc_fild22 32 196 moc_fild20 349 179 + +# Battlegrounds (Episode 12) +aldebaran 146 109 bat_room 154 150 0 c c r0 c +geffen 109 66 bat_room 154 150 0 c c r0 c +lighthalzen 153 86 bat_room 154 150 0 c c r0 c +moc_ruins 75 162 bat_room 154 150 0 c c r0 c +payon 189 105 bat_room 154 150 0 c c r0 c +prontera 123 83 bat_room 154 150 0 c c r0 c +rachel 149 138 bat_room 154 150 0 c c r0 c + +# following is a single warp, depends of entrance npc used +# currently unsupported, do not enable that +#bat_room 148 150 aldebaran 168 112 0 c r0 c +#bat_room 148 150 geffen 120 39 0 c r0 c +#bat_room 148 150 lighthalzen 159 93 0 c r0 c +#bat_room 148 150 moc_ruins 152 48 0 c r0 c +#bat_room 148 150 payon 161 58 0 c r0 c +#bat_room 148 150 prontera 116 72 0 c r0 c +#bat_room 148 150 rachel 115 124 0 c r0 c + +# following bat_room npcs are only available with base lvl 80 +# Tierra 1 Blue +bat_room 124 178 bat_room 57 223 0 c r0 c +bat_room 57 220 bat_room 154 150 +# Tierra 1 Red +bat_room 125 121 bat_room 57 207 0 c r0 c +bat_room 57 211 bat_room 154 150 +# Flavius 1 Blue +bat_room 133 178 bat_room 85 223 0 c r0 c +bat_room 85 220 bat_room 154 150 +# Flavius 1 Red +bat_room 133 121 bat_room 85 207 0 c r0 c +bat_room 85 211 bat_room 154 150 +# Tierra 2 Blue +bat_room 140 178 bat_room 114 223 0 c r0 c +bat_room 113 220 bat_room 154 150 +# Tierra 2 Red +bat_room 140 121 bat_room 114 207 0 c r0 c +bat_room 113 211 bat_room 154 150 +# Flavius 2 Blue +bat_room 148 178 bat_room 141 224 0 c r0 c +bat_room 141 220 bat_room 154 150 +bat_b02 10 293 bat_room 154 150 0 c +# Flavius 2 Red +bat_room 148 121 bat_room +bat_room bat_room 154 150 +bat_b02 189 14 bat_room 154 150 0 c + +# The New World (Episode 13) +mid_camp 213 286 moc_fild20 342 179 0 c c r0 + +#Midgard Camp +mid_camp 214 249 mid_campin 89 102 +mid_campin 89 99 mid_camp 215 247 +mid_campin 111 120 mid_campin 162 126 c n +mid_campin 162 126 mid_campin 110 120 +mid_campin 111 128 mid_campin 165 170 +mid_campin 162 170 mid_campin 108 128 +mid_campin 113 114 mid_campin 165 82 +mid_campin 162 82 mid_campin 110 114 +mid_campin 117 178 mid_campin 94 138 +mid_campin 68 128 mid_campin 22 168 +mid_campin 66 120 mid_campin 22 124 +mid_campin 66 114 mid_campin 22 80 +mid_campin 86 141 mid_campin 66 181 +mid_campin 65 178 mid_campin 86 138 +mid_campin 25 168 mid_campin 71 128 +mid_campin 25 124 mid_campin 70 120 +mid_campin 25 80 mid_campin 70 114 +mid_camp 266 260 mid_campin 286 124 +mid_campin 283 124 mid_camp 263 260 +mid_camp 205 211 mid_campin 229 123 +mid_campin 232 123 mid_camp 208 211 +mid_campin 223 109 mid_camp 198 205 +mid_camp 196 204 mid_campin 220 109 +mid_campin 376 136 mid_camp 163 234 +mid_camp 163 231 mid_campin 376 133 + +#Dimensional Gap +moc_fild22b 222 200 dali 119 56 c r0 n +dali 122 48 moc_fild22b 227 200 +dali 115 94 moc_para01 31 14 c r0 c n +mid_camp 210 292 dali 141 82 c r0 n +dali 149 82 mid_camp 210 289 +bif_fild01 318 159 dali 43 92 c r0 n +dali 39 87 bif_fild01 318 155 +dic_fild02 241 31 dali 41 134 c r0 n +dali 35 139 dic_fild02 237 32 +dali 93 68 moro_vol 136 135 c r0 n +moro_vol 137 dali 91 63 136 c r0 n +dali 64 129 dali02 66 101 +dali02 66 97 dali 64 125 +dali02 45 96 moc_fild22 36 196 c r0 c n + +#Cat Hand Agent Warper +#moc_para01 17 33 moc_fild20 342 198 c r0 n +#moc_fild20 368 197 moc_fild22b c r1 n +moc_fild22b 182 179 mid_camp 210 291 c c c c r0 c c n +mid_camp 207 234 spl_fild02 51 240 10000 c c r2 +mid_camp 207 234 man_fild02 133 47 10000 c c r4 +spl_fild02 53 242 mid_camp 180 247 10000 c c r4 +spl_fild02 53 242 bif_fild02 291 232 55000 c c r2 +bif_fild02 293 325 spl_fild02 51 240 55000 c c r4 +bif_fild02 293 325 ecl_fild01 116 309 15000 c c r2 +ecl_fild01 117 311 bif_fild02 291 323 15000 c c r2 +man_fild02 135 49 mid_camp 180 247 10000 c c r2 +man_fild02 135 49 dic_fild01 159 264 20000 c c r4 +dic_fild01 161 266 man_fild02 135 49 20000 c c r2 + +#Splendide +mid_camp 13 138 spl_fild02 379 143 c r0 n +spl_fild02 382 143 mid_camp 16 143 +mid_camp 9 213 spl_fild02 380 217 c r0 n +spl_fild02 383 216 mid_camp 12 215 +spl_fild02 310 10 spl_fild03 306 376 +spl_fild03 306 379 spl_fild02 311 12 +spl_fild02 103 30 spl_fild03 99 370 +spl_fild03 99 373 spl_fild02 102 32 +spl_fild02 289 374 spl_fild01 291 36 +spl_fild01 291 28 spl_fild02 286 369 +spl_fild02 6 243 splendide 383 251 +splendide 388 251 spl_fild02 11 243 +splendide 170 168 spl_in01 190 301 +spl_in01 190 299 splendide 170 166 +spl_in01 191 320 spl_in01 276 334 +spl_in01 276 336 spl_in01 190 322 +splendide 228 164 spl_in01 30 301 +spl_in01 30 299 splendide 228 162 +splendide 197 193 spl_in01 110 19 +spl_in01 110 17 splendide 197 191 +spl_in01 95 38 spl_in01 199 36 +spl_in01 197 36 spl_in01 93 38 +spl_in01 238 36 spl_in01 126 38 +spl_in01 124 38 spl_in01 236 36 +spl_in01 218 22 spl_in01 310 19 +spl_in01 310 17 spl_in01 218 20 +splendide 156 210 spl_in01 171 189 +spl_in01 171 187 splendide 156 208 +splendide 198 240 spl_in01 110 301 +spl_in01 110 299 splendide 198 238 +splendide 238 212 spl_in01 30 215 +spl_in01 30 213 splendide 237 210 +splendide 299 251 spl_in02 140 93 +spl_in02 139 91 splendide 297 250 +splendide 286 229 spl_in02 137 66 +spl_in02 135 66 splendide 284 228 +splendide 259 112 spl_in02 189 41 +spl_in02 187 42 splendide 259 114 +splendide 286 128 spl_in02 236 63 +spl_in02 234 64 splendide 284 129 +splendide 230 299 spl_in02 207 195 +spl_in02 207 193 splendide 228 299 +splendide 214 310 spl_in02 180 223 +spl_in02 180 221 splendide 214 308 +splendide 240 317 spl_in02 224 232 +spl_in02 222 232 splendide 238 317 +splendide 275 390 bif_fild01 316 50 +bif_fild01 318 48 splendide 271 386 +splendide 157 316 spl_in02 108 213 +spl_in02 108 211 splendide 157 314 +splendide 119 324 spl_in02 61 229 +spl_in02 62 227 splendide 119 322 +splendide 132 68 spl_in02 45 52 +spl_in02 44 54 splendide 133 70 + +#Manuk +mid_camp 336 171 man_fild01 36 235 c r0 n +man_fild01 35 232 mid_camp 341 176 +man_fild01 103 55 man_fild03 84 366 +man_fild03 84 369 man_fild01 103 58 +man_fild01 372 230 man_fild02 34 261 +man_fild02 32 261 man_fild01 369 230 +man_fild02 139 41 manuk 112 356 +manuk 114 357 man_fild02 138 43 +man_in01 61 173 man_in01 20 173 +man_in01 20 175 man_in01 61 175 +man_in01 61 190 man_in01 63 238 +man_in01 61 238 man_in01 61 188 +man_in01 11 19 man_in01 182 32 +man_in01 182 34 man_in01 11 21 +man_in01 318 17 man_in01 388 16 +man_in01 390 16 man_in01 316 17 +man_in01 275 41 manuk 309 142 +manuk 311 142 man_in01 277 41 +manuk 255 110 man_in01 7 61 +man_in01 5 61 manuk 253 110 +manuk 235 124 man_in01 70 171 +man_in01 70 169 manuk 235 122 +manuk 253 195 man_in01 24 284 +man_in01 26 284 manuk 255 195 +man_in01 22 275 man_in01 76 279 +man_in01 74 279 man_in01 20 275 +man_in01 5 275 man_in01 7 221 +man_in01 7 219 man_in01 7 276 +manuk 311 234 man_in01 380 267 +man_in01 380 265 manuk 309 232 +manuk 288 249 man_in01 280 267 +man_in01 280 265 manuk 289 247 +manuk 276 247 man_in01 230 267 +man_in01 230 265 manuk 276 245 +manuk 265 237 man_in01 180 267 +man_in01 180 265 manuk 267 235 +manuk 257 228 man_in01 130 267 +man_in01 130 265 manuk 258 226 +manuk 310 201 man_in01 358 121 +man_in01 328 141 man_in01 324 220 +man_in01 324 222 man_in01 328 139 +man_in01 358 119 manuk 310 199 +manuk 279 115 man_in01 123 224 +man_in01 123 226 manuk 278 117 +manuk 321 182 dic_dun01 33 212 c r0 n + +#El-Dicastes +dicastes01 199 34 dic_fild01 149 279 +dic_fild01 146 281 dicastes01 199 41 c r0 n +dic_fild01 153 281 dicastes01 199 41 c r0 n +dic_fild01 24 79 dic_dun01 366 45 +dic_fild02 71 375 dic_fild01 69 25 +dic_fild01 69 23 dic_fild02 70 373 +dic_dun01 30 216 manuk 326 180 c r0 n +dic_dun01 286 104 dic_dun02 101 142 c r0 n +dic_dun02 102 148 dic_dun01 290 99 +dic_dun01 87 212 dic_dun01 169 227 +dic_dun01 371 228 dic_dun01 32 156 +dic_dun01 28 156 dic_dun01 367 228 +dic_dun01 165 227 dic_dun01 83 212 +dic_dun01 371 172 dic_dun01 32 100 +dic_dun01 28 100 dic_dun01 367 172 +dic_dun01 371 100 dic_dun01 33 43 +dic_dun01 29 43 dic_dun01 367 100 +dic_dun01 371 44 dic_fild01 28 79 +dic_dun01 286 104 dic_dun02 101 142 c r0 n +dic_dun02 102 148 dic_dun01 290 99 +dicastes01 255 175 dic_in01 344 272 +dic_in01 342 271 dicastes01 255 173 +dicastes01 136 103 dic_in01 26 98 +dic_in01 44 116 dic_in01 42 115 r0 +dic_in01 44 116 dic_in01 110 108 r1 +dic_in01 44 116 dic_in01 178 108 r2 +dic_in01 44 116 dic_in01 260 115 r3 +dic_in01 180 108 dic_in01 42 115 r0 +dic_in01 180 108 dic_in01 110 108 r1 +dic_in01 180 108 dic_in01 178 108 r2 +dic_in01 180 108 dic_in01 260 115 r3 +dic_in01 112 108 dic_in01 42 115 r0 +dic_in01 112 108 dic_in01 110 108 r1 +dic_in01 112 108 dic_in01 178 108 r2 +dic_in01 112 108 dic_in01 260 115 r3 +dic_in01 261 115 dic_in01 42 115 r0 +dic_in01 261 115 dic_in01 110 108 r1 +dic_in01 261 115 dic_in01 178 108 r2 +dic_in01 261 115 dic_in01 260 115 r3 +dic_in01 26 96 dicastes01 137 106 +dicastes01 94 259 dic_in01 390 53 +dic_in01 390 55 dicastes01 95 257 +dicastes01 163 297 dic_in01 371 101 +dic_in01 372 99 dicastes01 163 295 +dicastes01 283 285 dic_in01 45 30 +dic_in01 45 28 dicastes01 281 284 +dicastes01 198 353 dicastes02 120 80 +dicastes02 120 77 dicastes01 197 351 +dicastes02 120 237 dic_in01 45 246 +dic_in01 45 244 dicastes02 119 235 +dic_in01 46 288 dic_in01 45 286 r0 +dic_in01 46 288 dic_in01 36 212 r1 +dic_in01 46 288 dic_in01 122 282 r2 +dic_in01 46 288 dic_in01 121 201 r3 +dic_in01 35 214 dic_in01 45 286 r0 +dic_in01 35 214 dic_in01 36 212 r1 +dic_in01 35 214 dic_in01 122 282 r2 +dic_in01 35 214 dic_in01 121 201 r3 +dic_in01 122 284 dic_in01 45 286 r0 +dic_in01 122 284 dic_in01 36 212 r1 +dic_in01 122 284 dic_in01 122 282 r2 +dic_in01 122 284 dic_in01 121 201 r3 +dic_in01 121 203 dic_in01 45 286 r0 +dic_in01 121 203 dic_in01 36 212 r1 +dic_in01 121 203 dic_in01 122 282 r2 +dic_in01 121 203 dic_in01 121 201 r3 + +# Battlegrounds (Episode 13) +# KVM Blue +bat_room 164 178 bat_room 169 223 0 c r0 c +bat_room 169 220 bat_room 154 150 +#(chatroom) bat_room 169 226 bat_c01 53 131 +bat_c01 51 130 bat_room 154 150 0 c +# KVM Red +bat_room 164 121 bat_room 169 207 0 c r0 c +bat_room 169 211 bat_room 154 150 +#(chatroom) bat_room 169 205 bat_c01 147 56 +bat_c01 148 54 bat_room 154 150 0 c + +#################### Criatura Academy #################### +izlude 125 257 iz_ac01 99 29 +izlude 130 257 iz_ac01 99 29 +iz_ac01 100 24 izlude 127 253 +# Eden Teleport Officer +iz_ac01 106 36 moc_para01 31 14 0 c r0 c +# Garden +iz_ac01 45 80 new_1-3 95 171 0 c r0 c +new_1-3 96 176 iz_ac01 49 73 +# Second floor +iz_ac01 78 25 iz_ac02 104 27 +iz_ac01 122 25 iz_ac02 104 27 +iz_ac02 94 27 iz_ac01 78 28 +iz_ac02 113 27 iz_ac01 122 28 +# Free Warps (Kafra) +iz_ac01 95 46 iz_ac01 97 86 0 c c c r2 c c c r1 r0 +iz_ac01 95 46 iz_ac02 104 179 0 c c c r2 c c c r1 r1 +iz_ac01 95 46 izlude 128 98 0 c c c r2 c c c r1 r2 c r1 +iz_ac02 101 176 iz_ac01 97 86 0 c c c r2 c c c r1 r0 +iz_ac02 101 176 izlude 128 98 0 c c c r2 c c c r1 r2 c r1 + +alb_ship 26 166 alberta 170 170 +alb_ship 37 174 alb_ship 68 99 +alb_ship 41 163 alberta 178 165 +alb_ship 68 99 alb_ship 37 174 +alb_ship 160 171 alberta 209 173 +alb2trea 88 111 treasure01 69 22 +alberta 15 234 pay_fild03 392 63 +alberta 33 42 alberta_in 78 44 +alberta 65 233 alberta_in 14 141 +alberta 93 205 alberta_in 114 130 +alberta 98 153 alberta_in 189 89 +alberta 99 221 alberta_in 125 160 +alberta 117 38 alberta_in 180 34 +alberta 170 170 alb_ship 26 166 +alberta 178 165 alb_ship 41 163 +alberta 209 173 alb_ship 160 171 +alberta_in 14 141 alberta 65 233 +alberta_in 22 113 alberta_in 22 130 +alberta_in 22 130 alberta_in 22 113 +alberta_in 22 153 alberta_in 22 170 +alberta_in 22 170 alberta_in 22 153 +alberta_in 24 33 alberta_in 64 31 +alberta_in 24 54 alberta_in 64 57 +alberta_in 64 31 alberta_in 24 33 +alberta_in 64 57 alberta_in 24 54 +alberta_in 78 44 alberta 33 42 +alberta_in 114 49 alberta_in 159 153 +alberta_in 114 97 alberta_in 159 175 +alberta_in 114 130 alberta 93 205 +alberta_in 114 183 alberta_in 152 186 +alberta_in 125 160 alberta 99 221 +alberta_in 152 186 alberta_in 114 183 +alberta_in 159 153 alberta_in 114 49 +alberta_in 159 175 alberta_in 114 97 +alberta_in 180 34 alberta 117 38 +alberta_in 189 89 alberta 98 153 +alde_dun01 167 158 c_tower2 142 283 +alde_dun01 292 306 alde_dun02 43 20 +alde_dun01 302 25 c_tower1 123 22 +alde_dun02 43 20 alde_dun01 292 306 +alde_dun02 122 169 c_tower3 42 41 +alde_dun02 187 234 alde_dun04 32 74 +alde_dun02 279 250 alde_dun03 12 267 +alde_dun03 12 267 alde_dun02 279 250 +alde_dun03 191 31 c_tower3 212 159 +alde_dun03 276 48 c_tower1 235 226 +alde_dun03 277 183 c_tower2 24 24 +alde_dun03 264 16 alde_dun04 78 268 0 c r2 c +alde_dun04 32 74 alde_dun02 187 239 +alde_dun04 208 58 alde_dun04 268 74 +alde_dun04 272 74 alde_dun04 208 58 +aldeba_in 24 224 aldebaran 51 218 +aldeba_in 27 34 aldebaran 72 197 +aldeba_in 27 71 aldeba_in 27 102 +aldeba_in 27 102 aldeba_in 27 71 +aldeba_in 37 238 aldeba_in 64 157 +aldeba_in 64 157 aldeba_in 37 238 +aldeba_in 83 191 aldeba_in 83 224 +aldeba_in 83 224 aldeba_in 83 191 +aldeba_in 94 38 aldebaran 197 70 +aldeba_in 97 102 aldeba_in 103 61 +aldeba_in 103 61 aldeba_in 97 102 +aldeba_in 103 157 aldeba_in 134 237 +aldeba_in 134 237 aldeba_in 103 157 +aldeba_in 144 92 aldeba_in 149 55 +aldeba_in 148 224 aldebaran 61 229 +aldeba_in 149 55 aldeba_in 144 92 +aldeba_in 149 123 aldebaran 225 54 +aldeba_in 157 193 aldebaran 233 105 +aldeba_in 208 117 aldebaran 118 63 +aldeba_in 217 71 aldeba_in 218 104 +aldeba_in 217 131 aldeba_in 217 160 +aldeba_in 217 160 aldeba_in 217 131 +aldeba_in 218 104 aldeba_in 217 71 +aldeba_in 245 237 aldebaran 89 234 +aldebaran 35 140 alde_gld 284 160 +aldebaran 51 218 aldeba_in 24 224 +aldebaran 61 229 aldeba_in 148 224 +aldebaran 72 197 aldeba_in 27 34 +aldebaran 89 234 aldeba_in 245 237 +aldebaran 118 63 aldeba_in 208 117 +aldebaran 138 34 mjolnir_12 199 378 +aldebaran 140 135 c_tower1 199 159 +aldebaran 140 243 yuno_fild01 208 18 +aldebaran 197 70 aldeba_in 94 38 +aldebaran 225 54 aldeba_in 149 123 +aldebaran 233 105 aldeba_in 157 193 +alde_gld 48 79 aldeg_cas01 34 252 +alde_gld 95 253 aldeg_cas02 88 159 +alde_gld 142 81 aldeg_cas03 114 290 +alde_gld 243 242 aldeg_cas04 145 17 +alde_gld 259 90 aldeg_cas05 216 107 +alde_gld 284 160 aldebaran 35 140 +aldeg_cas01 34 252 alde_gld 48 83 +aldeg_cas01 50 222 aldeg_cas01 104 108 +aldeg_cas01 104 112 aldeg_cas01 45 224 +aldeg_cas01 66 191 aldeg_cas01 122 61 +aldeg_cas01 126 61 aldeg_cas01 62 191 +aldeg_cas01 54 27 aldeg_cas01 62 191 +aldeg_cas01 26 188 aldeg_cas01 50 70 +aldeg_cas01 46 70 aldeg_cas01 24 188 +aldeg_cas01 70 112 aldeg_cas01 42 225 +aldeg_cas01 39 222 aldeg_cas01 70 108 +aldeg_cas01 89 23 aldeg_cas01 207 132 +aldeg_cas01 207 128 aldeg_cas01 89 27 +aldeg_cas01 206 188 aldeg_cas01 216 50 +aldeg_cas01 216 54 aldeg_cas01 206 184 +aldeg_cas01 232 186 aldeg_cas01 42 197 +aldeg_cas01 46 197 aldeg_cas01 232 182 +aldeg_cas01 171 175 aldeg_cas01 35 197 +aldeg_cas01 31 197 aldeg_cas01 175 175 +aldeg_cas02 88 159 alde_gld 95 249 +aldeg_cas02 84 208 aldeg_cas02 105 84 +aldeg_cas02 105 88 aldeg_cas02 79 208 +aldeg_cas02 45 39 aldeg_cas02 79 208 +aldeg_cas02 50 185 aldeg_cas02 192 192 +aldeg_cas02 192 196 aldeg_cas02 50 180 +aldeg_cas02 33 174 aldeg_cas02 126 61 +aldeg_cas02 130 61 aldeg_cas02 33 179 +aldeg_cas02 22 194 aldeg_cas02 88 13 +aldeg_cas02 88 9 aldeg_cas02 22 190 +aldeg_cas02 121 88 aldeg_cas02 177 135 +aldeg_cas02 177 131 aldeg_cas02 121 84 +aldeg_cas02 206 196 aldeg_cas02 197 13 +aldeg_cas02 197 9 aldeg_cas02 206 192 +aldeg_cas03 114 290 alde_gld 142 85 +aldeg_cas03 92 217 aldeg_cas03 127 90 +aldeg_cas03 130 90 aldeg_cas03 96 215 +aldeg_cas03 87 247 aldeg_cas03 54 90 +aldeg_cas03 51 90 aldeg_cas03 87 251 +aldeg_cas03 93 124 aldeg_cas03 87 251 +aldeg_cas03 44 222 aldeg_cas03 213 182 +aldeg_cas03 214 186 aldeg_cas03 49 222 +aldeg_cas03 91 57 aldeg_cas03 60 236 +aldeg_cas03 60 241 aldeg_cas03 91 61 +aldeg_cas03 79 130 aldeg_cas03 201 149 +aldeg_cas03 201 145 aldeg_cas03 79 126 +aldeg_cas03 199 190 aldeg_cas03 195 51 +aldeg_cas03 195 54 aldeg_cas03 199 186 +aldeg_cas04 145 17 alde_gld 239 242 +aldeg_cas04 197 40 aldeg_cas04 26 88 +aldeg_cas04 22 88 aldeg_cas04 192 41 +aldeg_cas04 175 54 aldeg_cas04 74 88 +aldeg_cas04 50 132 aldeg_cas04 74 88 +aldeg_cas04 78 88 aldeg_cas04 174 58 +aldeg_cas04 185 87 aldeg_cas04 111 210 +aldeg_cas04 108 210 aldeg_cas04 186 92 +aldeg_cas04 171 100 aldeg_cas04 152 210 +aldeg_cas04 156 210 aldeg_cas04 169 97 +aldeg_cas04 196 86 aldeg_cas04 49 57 +aldeg_cas04 49 53 aldeg_cas04 196 82 +aldeg_cas04 21 123 aldeg_cas04 125 168 +aldeg_cas04 121 168 aldeg_cas04 25 123 +aldeg_cas04 132 209 aldeg_cas04 14 196 +aldeg_cas04 17 196 aldeg_cas04 132 228 +aldeg_cas05 216 107 alde_gld 264 90 +aldeg_cas05 194 71 aldeg_cas05 129 194 +aldeg_cas05 125 194 aldeg_cas05 199 70 +aldeg_cas05 164 86 aldeg_cas05 66 189 +aldeg_cas05 70 189 aldeg_cas05 166 81 +aldeg_cas05 150 67 aldeg_cas05 9 187 +aldeg_cas05 5 187 aldeg_cas05 151 62 +aldeg_cas05 165 232 aldeg_cas05 193 49 +aldeg_cas05 188 49 aldeg_cas05 165 228 +aldeg_cas05 195 42 aldeg_cas05 19 227 +aldeg_cas05 15 227 aldeg_cas05 195 46 +aldeg_cas05 13 175 aldeg_cas05 162 194 +aldeg_cas05 166 194 aldeg_cas05 13 179 +aldeg_cas05 156 231 aldeg_cas05 18 88 +aldeg_cas05 14 88 aldeg_cas05 156 227 +ama_dun01 235 144 ama_dun02 30 40 +ama_dun02 30 40 ama_dun01 235 144 +ama_dun02 196 123 ama_dun03 120 9 +ama_dun03 120 9 ama_dun02 196 123 +ama_in01 31 176 amatsu 177 138 +ama_in01 33 24 amatsu 94 117 +ama_in01 34 96 amatsu 168 182 +ama_in01 76 178 amatsu 248 160 +ama_in01 86 23 amatsu 132 117 +ama_in01 88 94 amatsu 52 148 +ama_in01 157 25 amatsu 217 116 +ama_in01 162 34 ama_in01 166 73 +ama_in01 166 73 ama_in01 162 34 +ama_in01 174 121 ama_fild01 330 141 +ama_in01 175 171 ama_fild01 174 331 +ama_in02 56 44 ama_in02 59 141 +ama_in02 59 141 ama_in02 56 44 +ama_in02 59 160 ama_in02 215 149 +ama_in02 65 37 ama_in02 195 44 +ama_in02 126 164 ama_in02 222 161 +ama_in02 195 44 ama_in02 65 37 +ama_in02 215 149 ama_in02 59 160 +ama_in02 222 161 ama_in02 126 164 +ama_in02 226 45 amatsu 85 235 +ama_fild01 75 30 amatsu 248 292 +ama_fild01 174 331 ama_in01 175 171 +ama_fild01 330 141 ama_in01 174 121 +amatsu 52 148 ama_in01 88 94 +amatsu 85 235 ama_in02 226 45 +amatsu 94 117 ama_in01 33 24 +amatsu 132 117 ama_in01 86 23 +amatsu 168 182 ama_in01 34 96 +amatsu 177 138 ama_in01 31 176 +amatsu 217 116 ama_in01 157 25 +amatsu 248 160 ama_in01 76 178 +amatsu 248 292 ama_fild01 75 30 +anthell01 253 32 anthell02 32 267 +anthell02 32 267 anthell01 253 32 +ayothaya 62 104 ayo_in01 191 183 +ayothaya 111 109 ayo_in01 180 129 +ayothaya 129 86 ayo_in01 21 178 +ayothaya 130 75 ayo_in01 17 153 +ayothaya 130 97 ayo_in01 177 129 +ayothaya 165 90 ayo_in01 77 174 +ayothaya 173 77 ayo_in01 85 149 +ayothaya 177 90 ayo_in01 95 175 +ayothaya 192 150 ayo_in01 10 94 +ayothaya 202 140 ayo_in01 31 78 +ayothaya 202 161 ayo_in01 31 108 +ayothaya 208 283 ayo_in02 100 152 +ayothaya 211 150 ayo_in01 50 94 +ayothaya 229 69 ayo_in01 137 192 +ayothaya 276 176 ayo_fild01 32 240 +ayo_in01 10 94 ayothaya 189 150 +ayo_in01 13 82 ayo_in01 18 14 +ayo_in01 13 107 ayo_in01 132 27 +ayo_in01 17 153 ayothaya 132 73 +ayo_in01 21 14 ayo_in01 13 82 +ayo_in01 23 176 ayothaya 129 86 +ayo_in01 31 78 ayothaya 202 137 +ayo_in01 31 111 ayothaya 202 161 +ayo_in01 46 183 ayothaya 130 97 +ayo_in01 48 82 ayo_in01 63 14 +ayo_in01 48 107 ayo_in01 179 27 +ayo_in01 50 94 ayothaya 214 150 +ayo_in01 60 14 ayo_in01 48 82 +ayo_in01 73 174 ayo_in01 184 194 +ayo_in01 85 149 ayothaya 173 74 +ayo_in01 98 175 ayothaya 177 90 +ayo_in01 135 27 ayo_in01 13 107 +ayo_in01 137 195 ayothaya 229 69 +ayo_in01 176 27 ayo_in01 48 107 +ayo_in01 177 129 ayothaya 111 109 +ayo_in01 194 183 ayothaya 62 104 +ayo_in02 100 148 ayothaya 208 283 +ayo_fild01 32 240 ayothaya 273 176 +ayo_fild01 129 197 ayo_fild02 100 100 0 c c c c c r0 c c c +ayo_fild01 129 197 ayo_fild02 30 135 c w1 c w1 c1 w1 c1 w1 c1 r0 c w1 c w1 c n +ayo_fild02 285 149 ayo_dun01 275 18 +ayo_fild02 25 154 ayo_fild01 115 200 c r0 c n +ayo_dun01 274 14 ayo_fild02 279 150 +ayo_dun01 24 283 ayo_dun02 23 26 c n +ayo_dun02 24 22 ayo_dun01 24 279 +beach_dun 276 67 comodo 24 214 +beach_dun2 154 13 comodo 176 358 +beach_dun2 258 244 um_fild01 31 274 +beach_dun3 17 265 comodo 333 175 +beach_dun3 286 57 cmd_fild01 26 318 +c_tower1 123 22 alde_dun01 302 25 +c_tower1 200 157 aldebaran 139 135 +c_tower1 235 226 c_tower2 273 26 +c_tower2 24 24 alde_dun03 277 183 +c_tower2 142 283 c_tower3 60 147 +c_tower2 273 26 c_tower1 235 226 +c_tower3 42 41 alde_dun02 122 169 +c_tower3 60 147 c_tower2 142 283 +c_tower3 212 159 alde_dun03 276 48 +cmd_fild01 26 318 beach_dun3 286 57 +cmd_fild01 222 24 cmd_fild02 222 376 +cmd_fild01 362 73 cmd_fild03 23 68 +cmd_fild01 362 263 cmd_fild03 23 269 +cmd_fild01 77 366 um_fild03 114 50 +cmd_fild01 178 370 um_fild03 243 26 +cmd_fild02 222 376 cmd_fild01 222 24 +cmd_fild02 358 95 cmd_fild04 31 92 +cmd_fild02 382 269 cmd_fild04 21 275 +cmd_fild03 23 68 cmd_fild01 362 73 +cmd_fild03 23 269 cmd_fild01 362 263 +cmd_fild03 181 17 cmd_fild04 180 372 +cmd_fild03 384 165 cmd_fild05 21 163 +cmd_fild04 21 275 cmd_fild02 382 269 +cmd_fild04 31 92 cmd_fild02 358 95 +cmd_fild04 180 372 cmd_fild03 181 17 +cmd_fild04 376 287 cmd_fild06 21 285 +cmd_fild05 21 163 cmd_fild03 384 165 +cmd_fild05 148 19 cmd_fild06 150 380 +cmd_fild06 21 285 cmd_fild04 376 287 +cmd_fild06 150 380 cmd_fild05 148 19 +cmd_fild06 151 27 cmd_fild07 149 379 +cmd_fild06 368 96 cmd_fild08 15 102 +cmd_fild06 372 359 cmd_fild08 25 355 +cmd_fild06 379 174 cmd_fild08 25 170 +cmd_fild07 149 379 cmd_fild06 151 27 +cmd_fild07 389 186 cmd_fild09 12 170 +cmd_fild08 15 102 cmd_fild06 368 96 +cmd_fild08 25 170 cmd_fild06 379 174 +cmd_fild08 25 355 cmd_fild06 372 359 +cmd_fild08 76 31 cmd_fild09 75 382 +cmd_fild08 309 48 cmd_fild09 309 381 +cmd_fild08 354 324 moc_fild12 26 305 +cmd_fild09 12 170 cmd_fild07 389 186 +cmd_fild09 75 382 cmd_fild08 76 31 +cmd_fild09 309 381 cmd_fild08 309 48 +cmd_fild09 369 164 moc_fild18 51 170 +cmd_in01 90 174 comodo 92 128 +cmd_in01 109 125 comodo 271 271 +cmd_in01 120 67 comodo 145 328 +cmd_in01 123 186 comodo 265 74 +cmd_in01 183 81 comodo 236 298 +cmd_in01 183 123 comodo 192 294 +cmd_in02 58 74 cmd_in02 168 113 +cmd_in02 69 129 comodo 115 291 +cmd_in02 74 21 comodo 140 90 +cmd_in02 90 37 cmd_in02 187 78 +cmd_in02 98 216 cmd_in02 208 206 +cmd_in02 139 97 comodo 126 98 +cmd_in02 139 169 comodo 130 195 +cmd_in02 168 113 cmd_in02 58 74 +cmd_in02 178 136 comodo 140 111 +cmd_in02 187 78 cmd_in02 90 37 +cmd_in02 208 206 cmd_in02 98 216 +cmd_in02 216 97 comodo 153 97 +comodo 24 214 beach_dun 276 67 +comodo 92 128 cmd_in01 90 174 +comodo 115 291 cmd_in02 69 129 +comodo 126 98 cmd_in02 139 97 +comodo 130 195 cmd_in02 139 169 +comodo 140 90 cmd_in02 74 21 +comodo 140 111 cmd_in02 178 136 +comodo 145 328 cmd_in01 120 67 +comodo 153 97 cmd_in02 216 97 +comodo 176 358 beach_dun2 154 13 +comodo 192 294 cmd_in01 183 123 +comodo 236 298 cmd_in01 183 81 +comodo 265 74 cmd_in01 123 186 +comodo 271 271 cmd_in01 109 125 +comodo 333 175 beach_dun3 17 265 +einbroch 150 25 ein_fild08 164 377 +einbroch 157 331 ein_fild04 184 31 +einbroch 232 272 einbech 43 215 200 c c r0 n +einbech 39 215 einbroch 226 276 200 c r0 n +einbech 145 112 ein_in01 268 104 +einbech 177 136 ein_in01 190 35 +einbech 138 252 ein_dun01 22 14 +einbech 62 29 ein_fild09 74 350 +ein_in01 288 89 einbech 157 106 +ein_in01 190 38 einbech 172 134 +ein_dun01 261 255 ein_dun02 291 292 +ein_dun01 22 11 einbech 138 249 +ein_dun02 286 292 ein_dun01 261 258 +ein_fild04 184 26 einbroch 157 327 +ein_fild06 355 95 yuno_fild07 66 79 +ein_fild06 135 36 ein_fild07 147 359 +ein_fild06 251 365 yuno_fild04 249 25 +ein_fild06 335 176 yuno_fild07 76 220 +ein_fild07 144 362 ein_fild06 135 39 +ein_fild07 183 43 ein_fild10 196 368 +ein_fild08 164 380 einbroch 150 28 +ein_fild08 361 129 ein_fild09 37 135 +ein_fild09 71 352 einbech 62 32 +ein_fild09 34 135 ein_fild08 358 130 +ein_fild09 328 344 ein_fild10 28 330 +ein_fild10 25 330 ein_fild09 325 344 +ein_fild10 196 371 ein_fild07 179 46 +gef_dun00 104 103 gef_tower 153 28 +gef_dun00 107 168 gef_dun01 115 240 +gef_dun01 115 240 gef_dun00 107 169 +gef_dun01 197 38 gef_dun02 106 134 +gef_dun02 106 134 gef_dun01 197 38 +gef_fild00 40 199 geffen 217 119 +gef_fild00 267 382 mjolnir_06 265 29 +gef_fild00 381 137 prt_fild00 18 129 +gef_fild01 16 102 gef_fild09 368 95 +gef_fild01 69 17 gef_fild03 66 382 +gef_fild01 382 111 prt_fild04 17 114 +gef_fild02 14 78 gef_fild03 382 77 +gef_fild02 16 275 gef_fild03 382 277 +gef_fild02 266 18 prt_fild10 227 299 +gef_fild02 380 68 prt_fild07 13 64 +gef_fild02 380 156 prt_fild07 17 145 +gef_fild02 380 289 prt_fild07 14 289 +gef_fild03 18 52 gef_fild10 370 56 +gef_fild03 66 382 gef_fild01 69 17 +gef_fild03 312 16 prt_fild11 302 301 +gef_fild03 382 77 gef_fild02 14 78 +gef_fild03 382 277 gef_fild02 16 275 +gef_fild04 16 309 gef_fild05 364 309 +gef_fild04 187 39 geffen 119 213 +gef_fild04 261 362 mjolnir_01 284 18 +gef_fild04 362 322 mjolnir_06 18 331 +gef_fild05 15 201 gef_fild06 382 211 +gef_fild05 64 15 gef_fild07 64 363 +gef_fild05 364 309 gef_fild04 16 309 +gef_fild06 18 304 glast_01 380 304 +gef_fild06 218 17 gef_fild08 200 355 +gef_fild06 382 211 gef_fild05 15 201 +gef_fild07 18 191 gef_fild08 360 187 +gef_fild07 40 19 gef_fild13 41 373 +gef_fild07 64 363 gef_fild05 64 15 +gef_fild07 339 187 geffen 26 119 +gef_fild08 200 355 gef_fild06 218 17 +gef_fild08 215 18 gef_fild12 221 374 +gef_fild08 360 187 gef_fild07 18 191 +gef_fild09 23 56 gef_fild13 380 56 +gef_fild09 225 25 gef_fild10 251 371 +gef_fild09 368 95 gef_fild01 16 102 +gef_fild10 27 219 gef_fild14 371 219 +gef_fild10 63 337 in_orcs01 30 154 +gef_fild10 104 21 gef_fild11 111 364 +gef_fild10 136 331 in_orcs01 124 171 +gef_fild10 138 284 in_orcs01 29 116 +gef_fild10 215 51 in_orcs01 162 55 +gef_fild10 223 205 in_orcs01 108 86 +gef_fild10 251 371 gef_fild09 225 25 +gef_fild10 370 56 gef_fild03 18 52 +gef_fild11 111 364 gef_fild10 104 21 +gef_fild11 377 293 prt_fild11 17 281 +gef_fild12 221 374 gef_fild08 215 18 +gef_fild12 368 180 gef_fild13 25 202 +gef_fild12 373 50 gef_fild13 25 59 +gef_fild13 25 59 gef_fild12 373 50 +gef_fild13 25 202 gef_fild12 368 180 +gef_fild13 41 373 gef_fild07 40 19 +gef_fild13 77 284 gefg_cas03 68 290 +gef_fild13 83 185 gefg_cas03 93 159 +gef_fild13 112 269 gefg_cas03 103 283 +gef_fild13 139 240 gefg_cas03 130 250 +gef_fild13 150 54 gefg_cas01 34 136 +gef_fild13 196 281 gefg_cas04 24 145 +gef_fild13 200 25 gef_fild14 180 360 +gef_fild13 210 75 gefg_cas01 99 178 +gef_fild13 256 57 gefg_cas05 7 134 +gef_fild13 305 83 gefg_cas05 99 204 +gef_fild13 308 244 gefg_cas02 70 147 +gef_fild13 380 56 gef_fild09 23 56 +gef_fild14 180 360 gef_fild13 200 25 +gef_fild14 371 219 gef_fild10 27 219 +gef_tower 38 160 gef_tower 42 86 +gef_tower 42 86 gef_tower 38 160 +gef_tower 42 86 gef_tower 66 156 +gef_tower 44 36 gef_tower 106 158 +gef_tower 52 135 gef_tower 158 174 +gef_tower 52 181 geffen 120 114 +gef_tower 60 34 gef_tower 62 87 +gef_tower 62 87 gef_tower 60 34 +gef_tower 66 156 gef_tower 42 86 +gef_tower 106 69 gef_tower 106 115 +gef_tower 106 115 gef_tower 106 69 +gef_tower 106 158 gef_tower 44 36 +gef_tower 116 31 gef_tower 118 68 +gef_tower 118 68 gef_tower 116 31 +gef_tower 118 114 gef_tower 120 158 +gef_tower 120 158 gef_tower 118 114 +gef_tower 153 28 gef_dun00 104 103 +gef_tower 156 93 gef_tower 158 104 +gef_tower 158 104 gef_tower 156 93 +gef_tower 158 128 gef_tower 158 150 +gef_tower 158 150 gef_tower 158 128 +gef_tower 158 174 gef_tower 52 135 +geffen 26 119 gef_fild07 339 187 +geffen 43 85 geffen_in 70 132 +geffen 61 180 geffen_in 163 94 +geffen 98 141 geffen_in 28 156 +geffen 119 213 gef_fild04 187 39 +geffen 120 114 gef_tower 52 181 +geffen 138 138 geffen_in 28 106 +geffen 172 174 geffen_in 70 48 +geffen 182 59 geffen_in 106 181 +geffen 217 119 gef_fild00 40 199 +geffen_in 26 37 geffen_in 26 60 +geffen_in 26 60 geffen_in 26 37 +geffen_in 28 106 geffen 138 138 +geffen_in 28 156 geffen 98 141 +geffen_in 41 67 geffen_in 52 65 +geffen_in 52 65 geffen_in 41 67 +geffen_in 70 48 geffen 172 174 +geffen_in 70 83 geffen_in 72 98 +geffen_in 70 132 geffen 43 85 +geffen_in 70 149 geffen_in 70 158 +geffen_in 70 158 geffen_in 70 149 +geffen_in 72 98 geffen_in 70 83 +geffen_in 79 107 geffen_in 104 109 +geffen_in 87 65 geffen_in 100 67 +geffen_in 100 67 geffen_in 87 65 +geffen_in 104 109 geffen_in 79 107 +geffen_in 106 181 geffen 182 59 +geffen_in 113 163 geffen_in 136 169 +geffen_in 114 37 geffen_in 114 60 +geffen_in 114 60 geffen_in 114 37 +geffen_in 136 169 geffen_in 113 163 +geffen_in 138 149 geffen_in 138 162 +geffen_in 138 162 geffen_in 138 149 +geffen_in 163 94 geffen 61 180 +gefg_cas01 170 14 gefg_cas01 50 84 +gefg_cas01 170 34 gefg_cas01 30 167 +gefg_cas01 181 52 gefg_cas01 198 160 +gefg_cas01 202 160 gefg_cas01 185 52 +gefg_cas01 209 34 gefg_cas01 56 170 +gefg_cas01 31 185 gefg_cas01 33 47 +gefg_cas01 33 51 gefg_cas01 35 185 +gefg_cas01 34 136 gef_fild13 150 50 +gefg_cas01 34 167 gefg_cas01 174 34 +gefg_cas01 39 196 gefg_cas01 62 13 +gefg_cas01 54 84 gefg_cas01 174 14 +gefg_cas01 58 185 gefg_cas01 90 47 +gefg_cas01 59 170 gefg_cas01 205 34 +gefg_cas01 62 9 gefg_cas01 39 192 +gefg_cas01 90 51 gefg_cas01 54 185 +gefg_cas01 99 178 gef_fild13 214 75 +gefg_cas02 148 18 gefg_cas02 35 150 +gefg_cas02 150 36 gefg_cas02 152 186 +gefg_cas02 152 190 gefg_cas02 150 41 +gefg_cas02 174 11 gefg_cas02 21 13 +gefg_cas02 184 36 gefg_cas02 48 155 +gefg_cas02 185 18 gefg_cas02 53 136 +gefg_cas02 22 160 gefg_cas02 34 17 +gefg_cas02 25 13 gefg_cas02 170 11 +gefg_cas02 34 13 gefg_cas02 22 156 +gefg_cas02 34 152 gefg_cas02 153 18 +gefg_cas02 34 68 gefg_cas02 50 175 +gefg_cas02 35 173 gefg_cas02 76 42 +gefg_cas02 46 175 gefg_cas02 34 64 +gefg_cas02 48 159 gefg_cas02 184 41 +gefg_cas02 57 136 gefg_cas02 180 18 +gefg_cas02 70 147 gef_fild13 308 240 +gefg_cas02 80 42 gefg_cas02 39 173 +gefg_cas03 103 283 gef_fild13 117 273 +gefg_cas03 106 217 gefg_cas03 131 15 +gefg_cas03 115 210 gefg_cas03 92 215 +gefg_cas03 130 250 gef_fild13 143 240 +gefg_cas03 135 15 gefg_cas03 110 217 +gefg_cas03 135 92 gefg_cas03 34 282 +gefg_cas03 152 92 gefg_cas03 59 255 +gefg_cas03 154 16 gefg_cas03 252 11 +gefg_cas03 17 206 gefg_cas03 29 219 +gefg_cas03 212 46 gefg_cas03 225 158 +gefg_cas03 225 154 gefg_cas03 212 42 +gefg_cas03 237 74 gefg_cas03 62 213 +gefg_cas03 256 11 gefg_cas03 159 16 +gefg_cas03 266 47 gefg_cas03 45 175 +gefg_cas03 27 215 gefg_cas03 17 202 +gefg_cas03 34 286 gefg_cas03 131 92 +gefg_cas03 38 243 gefg_cas03 29 219 +gefg_cas03 38 259 gefg_cas03 43 271 +gefg_cas03 42 175 gefg_cas03 266 43 +gefg_cas03 43 191 gefg_cas03 70 185 +gefg_cas03 47 271 gefg_cas03 38 255 +gefg_cas03 50 248 gefg_cas03 54 229 +gefg_cas03 58 232 gefg_cas03 62 213 +gefg_cas03 63 255 gefg_cas03 156 92 +gefg_cas03 65 215 gefg_cas03 233 74 +gefg_cas03 66 223 gefg_cas03 96 53 +gefg_cas03 68 290 gef_fild13 74 287 +gefg_cas03 70 182 gefg_cas03 39 191 +gefg_cas03 79 244 gefg_cas03 91 250 +gefg_cas03 88 248 gefg_cas03 76 242 +gefg_cas03 90 218 gefg_cas03 111 210 +gefg_cas03 92 53 gefg_cas03 62 223 +gefg_cas03 93 159 gef_fild13 83 181 +gefg_cas03 93 209 gefg_cas03 92 250 +gefg_cas03 95 251 gefg_cas03 91 209 +gefg_cas04 140 168 gefg_cas04 178 61 +gefg_cas04 142 33 gefg_cas04 52 21 +gefg_cas04 142 55 gefg_cas04 32 180 +gefg_cas04 174 36 gefg_cas04 53 192 +gefg_cas04 178 57 gefg_cas04 143 166 +gefg_cas04 18 82 gefg_cas04 34 215 +gefg_cas04 18 9 gefg_cas04 57 220 +gefg_cas04 24 145 gef_fild13 193 278 +gefg_cas04 27 180 gefg_cas04 142 59 +gefg_cas04 34 211 gefg_cas04 18 78 +gefg_cas04 42 81 gefg_cas04 42 13 +gefg_cas04 42 9 gefg_cas04 42 77 +gefg_cas04 52 25 gefg_cas04 142 37 +gefg_cas04 53 196 gefg_cas04 170 36 +gefg_cas04 57 224 gefg_cas04 18 13 +gefg_cas05 149 44 gefg_cas05 37 20 +gefg_cas05 178 72 gefg_cas05 46 165 +gefg_cas05 190 20 gefg_cas05 194 151 +gefg_cas05 194 147 gefg_cas05 190 16 +gefg_cas05 206 44 gefg_cas05 93 20 +gefg_cas05 37 16 gefg_cas05 153 44 +gefg_cas05 43 62 gefg_cas05 78 138 +gefg_cas05 44 143 gefg_cas05 70 155 +gefg_cas05 50 165 gefg_cas05 178 68 +gefg_cas05 66 76 gefg_cas05 80 155 +gefg_cas05 68 150 gefg_cas05 44 147 +gefg_cas05 7 134 gef_fild13 252 57 +gefg_cas05 74 138 gefg_cas05 47 62 +gefg_cas05 83 152 gefg_cas05 66 72 +gefg_cas05 87 165 gefg_cas05 84 62 +gefg_cas05 88 62 gefg_cas05 83 165 +gefg_cas05 93 16 gefg_cas05 202 44 +gefg_cas05 99 204 gef_fild13 305 87 +gl_cas01 185 236 gl_cas01 167 191 +gl_cas01 200 18 glast_01 200 297 +gl_cas01 200 165 gl_cas02 104 15 +gl_cas01 215 236 gl_cas01 234 192 +gl_cas01 149 314 gl_prison 11 70 +gl_cas01 167 191 gl_prison 11 70 +gl_cas01 234 192 gl_prison 11 70 +gl_cas01 371 301 gl_prison 11 70 +gl_cas01 215 236 gl_cas01 135 40 +gl_cas02 104 15 gl_cas01 200 165 +gl_cas02 104 193 glast_01 199 322 +gl_cas02 104 193 glast_01 199 325 +gl_church 16 299 gl_chyard 147 287 +gl_church 156 4 glast_01 200 137 +gl_church 301 46 gl_chyard 147 12 +gl_chyard 12 149 gl_sew02 30 273 +gl_chyard 147 12 gl_church 301 46 +gl_chyard 147 287 gl_church 16 299 +gl_dun01 133 277 gl_sew04 104 78 +gl_dun01 225 18 gl_dun02 224 277 +gl_dun02 224 277 gl_dun01 225 18 +gl_in01 81 68 glast_01 162 330 +gl_in01 83 174 glast_01 179 360 +gl_in01 106 125 glast_01 220 360 +gl_in01 118 59 glast_01 237 330 +gl_knt01 12 148 gl_knt02 10 138 +gl_knt01 104 204 gl_knt01 123 292 +gl_knt01 128 292 gl_knt01 104 199 +gl_knt01 150 6 glast_01 77 193 +gl_knt01 150 291 gl_knt02 157 292 +gl_knt01 231 197 gl_sew02 296 18 +gl_knt01 287 144 gl_knt02 289 138 +gl_knt02 10 138 gl_knt01 12 148 +gl_knt02 157 292 gl_knt01 150 291 +gl_knt02 289 138 gl_knt01 287 144 +gl_prison 11 70 gl_cas01 234 192 +gl_prison 149 183 gl_prison1 150 10 +gl_prison1 62 187 gl_sew01 258 258 +gl_prison1 150 10 gl_prison 149 183 +gl_sew01 19 21 gl_sew02 109 294 +gl_sew01 258 258 gl_prison1 62 187 +gl_sew02 30 273 gl_chyard 12 149 +gl_sew02 109 294 gl_sew01 19 21 +gl_sew02 290 156 gl_step 120 124 +gl_sew02 296 18 gl_knt01 231 197 +gl_sew02 299 294 gl_sew03 171 286 +gl_sew03 64 10 gl_sew04 68 280 +gl_sew03 171 286 gl_sew02 299 294 +gl_sew04 68 280 gl_sew03 64 10 +gl_sew04 104 78 gl_dun01 133 277 +gl_step 8 7 glast_01 51 108 +gl_step 120 124 gl_sew02 290 156 +glast_01 51 108 gl_step 8 7 +glast_01 77 193 gl_knt01 150 6 +glast_01 162 330 gl_in01 81 68 +glast_01 179 360 gl_in01 83 174 +glast_01 200 137 gl_church 156 4 +glast_01 200 297 gl_cas01 200 18 +glast_01 199 322 gl_cas02 104 189 +glast_01 220 360 gl_in01 106 125 +glast_01 237 330 gl_in01 118 59 +glast_01 380 304 gef_fild06 18 304 +gon_dun01 153 45 gonryun 159 201 +gon_dun01 162 273 gon_dun02 17 113 +gon_dun02 14 113 gon_dun01 162 270 +gon_dun02 44 166 gon_dun02 97 121 +gon_dun02 44 213 gon_dun02 51 119 +gon_dun02 56 119 gon_dun02 47 210 +gon_dun02 63 66 gon_dun02 203 94 +gon_dun02 76 100 gon_dun02 145 62 +gon_dun02 92 190 gon_dun02 199 20 +gon_dun02 94 118 gon_dun02 177 189 +gon_dun02 145 66 gon_dun02 199 20 +gon_dun02 148 236 gon_dun02 234 194 +gon_dun02 165 189 gon_dun02 235 135 +gon_dun02 168 92 gon_dun02 273 76 +gon_dun02 171 258 gon_dun02 76 96 +gon_dun02 180 189 gon_dun02 170 164 +gon_dun02 170 161 gon_dun02 89 41 +gon_dun02 86 44 gon_dun02 145 233 +gon_dun02 196 20 gon_dun02 95 190 +gon_dun02 199 94 gon_dun02 60 70 +gon_dun02 234 191 gon_dun02 145 233 +gon_dun02 235 138 gon_dun02 168 189 +gon_dun02 251 268 gon_dun03 68 9 +gon_dun02 276 76 gon_dun02 163 87 +gon_dun03 68 6 gon_dun02 251 265 +gon_in 31 97 gon_in 42 35 +gon_in 42 35 gon_in 31 97 +gon_in 47 24 gonryun 109 131 +gon_in 72 67 gonryun 107 184 +gon_in 95 26 gonryun 195 93 +gon_in 109 98 gonryun 221 162 +gon_in 149 24 gonryun 215 114 +gon_in 184 11 gon_in 186 85 +gon_in 184 36 gon_in 186 107 +gon_in 186 85 gon_in 184 11 +gon_in 186 107 gon_in 184 36 +gon_fild01 192 265 gonryun 161 8 +gonryun 107 184 gon_in 72 67 +gonryun 109 131 gon_in 47 24 +gonryun 159 201 gon_dun01 153 45 +gonryun 161 8 gon_fild01 192 265 +gonryun 195 93 gon_in 95 26 +gonryun 215 114 gon_in 149 24 +gonryun 221 162 gon_in 109 98 +hu_fild04 122 27 yuno_fild02 117 375 +in_moc_16 18 8 moc_fild16 205 296 +in_orcs01 29 116 gef_fild10 138 284 +in_orcs01 30 154 gef_fild10 63 337 +in_orcs01 30 182 orcsdun01 32 172 +in_orcs01 108 86 gef_fild10 223 205 +in_orcs01 108 114 orcsdun02 180 15 +in_orcs01 124 171 gef_fild10 136 331 +in_orcs01 162 55 gef_fild10 215 51 +in_sphinx1 80 191 in_sphinx2 149 77 +in_sphinx1 288 6 moc_fild19 98 99 +in_sphinx2 149 77 in_sphinx1 80 191 +in_sphinx2 276 272 in_sphinx3 210 57 +in_sphinx3 12 69 in_sphinx4 10 224 +in_sphinx3 35 227 in_sphinx3 60 227 +in_sphinx3 60 227 in_sphinx3 35 227 +in_sphinx3 70 83 in_sphinx3 70 111 +in_sphinx3 70 111 in_sphinx3 70 83 +in_sphinx3 210 57 in_sphinx2 276 272 +in_sphinx4 10 224 in_sphinx3 12 69 +in_sphinx4 120 113 in_sphinx5 100 96 +in_sphinx5 100 96 in_sphinx4 120 113 +iz_dun00 39 41 iz_dun01 41 32 +iz_dun00 168 173 izlu2dun 108 83 +iz_dun00 352 342 iz_dun01 253 258 +iz_dun01 41 32 iz_dun00 39 41 +iz_dun01 118 170 iz_dun02 236 198 +iz_dun01 253 258 iz_dun00 352 342 +iz_dun02 236 198 iz_dun01 118 170 +iz_dun02 339 331 iz_dun03 29 63 +iz_dun03 29 63 iz_dun02 339 331 +iz_dun03 264 245 iz_dun04 26 24 +iz_dun04 26 24 iz_dun03 264 245 +izlu2dun 108 83 iz_dun00 168 173 +izlude 30 78 prt_fild08 371 212 +izlude 52 140 izlude_in 74 158 +izlude 109 151 izlude_in 65 84 +izlude 148 148 izlude_in 116 46 +izlude 216 129 izlude_in 148 127 +izlude_in 65 84 izlude 109 151 +izlude_in 74 158 izlude 52 140 +izlude_in 87 169 izlude_in 108 169 +izlude_in 108 169 izlude_in 87 169 +izlude_in 116 46 izlude 148 148 +izlude_in 148 127 izlude 216 129 +izlude_in 171 97 izlude_in 172 116 +izlude_in 172 116 izlude_in 171 97 +izlude_in 172 139 izlude_in 172 158 +izlude_in 172 158 izlude_in 172 139 +juperos_01 51 249 jupe_cave 29 52 +jupe_cave 147 52 yuno_fild07 202 175 +jupe_cave 29 52 juperos_01 54 246 +lhz_cube 231 12 lighthalzen 310 302 +lhz_cube 231 96 lhz_dun02 220 6 +lhz_dun01 149 9 lhz_dun02 153 19 +lhz_dun01 18 145 lhz_dun02 17 150 +lhz_dun01 281 150 lhz_dun02 282 153 +lhz_dun01 149 291 lhz_in01 33 224 +lhz_dun02 224 6 lhz_cube 231 90 +lhz_dun02 149 149 lhz_dun03 140 133 +lhz_dun03 139 137 lhz_dun02 149 142 +lhz_dun02 283 153 lhz_dun01 281 154 +lhz_dun02 17 153 lhz_dun01 18 150 +lhz_dun02 146 19 lhz_dun02 17 156 +lhz_dun02 17 156 lhz_dun02 159 33 +lhz_dun02 146 19 lhz_dun01 144 9 +lhz_dun02 17 156 lhz_dun01 18 150 +lhz_dun02 282 161 lhz_dun01 281 154 +lhz_in01 88 215 lhz_in01 106 18 +lhz_in01 106 15 lhz_in01 88 211 +lhz_in01 251 226 lhz_in01 225 226 +lhz_in01 231 227 lhz_in01 257 226 +lhz_in01 188 253 lhz_in01 191 52 +lhz_in01 194 55 lhz_in01 188 250 +lhz_in01 76 254 lhz_in01 100 52 +lhz_in01 98 55 lhz_in01 76 250 +lhz_in01 149 55 lhz_in01 188 250 +lhz_in01 158 15 lhz_in01 140 211 +lhz_in01 140 215 lhz_in01 158 18 +lhz_in02 21 191 lighthalzen 202 160 +lhz_in02 39 204 lhz_in02 41 136 +lhz_in02 36 136 lhz_in02 34 204 +lhz_in02 13 155 lhz_in02 84 223 +lhz_in02 79 223 lhz_in02 17 155 +lhz_in02 94 212 lhz_in02 94 144 +lhz_in02 89 144 lhz_in02 89 212 +lhz_in02 103 155 lhz_in02 44 154 +lhz_in02 49 154 lhz_in02 98 155 +lighthalzen 267 200 lighthalzen 303 229 0 c n +lighthalzen 294 223 lighthalzen 260 199 0 c n +lighthalzen 313 301 lhz_cube 231 12 0 c c r0 n +lighthalzen 198 163 lhz_in02 21 195 +louyang 218 19 lou_fild01 233 356 +louyang 130 63 lou_in02 72 31 +louyang 37 271 lou_dun01 218 195 +louyang 136 97 lou_in02 247 171 +louyang 145 174 lou_in02 126 168 +louyang 125 121 lou_in02 198 161 +louyang 218 255 lou_in01 101 122 +lou_fild01 233 356 louyang 218 22 +lou_in01 101 119 louyang 218 255 +lou_in02 72 28 louyang 130 63 +lou_in02 249 173 louyang 136 97 +lou_in02 126 165 louyang 145 174 +lou_in02 198 159 louyang 124 118 +lou_in02 203 159 louyang 129 118 +lou_dun01 221 195 louyang 37 271 +lou_dun01 40 201 lou_dun02 288 18 +lou_dun02 288 18 lou_dun01 40 201 +lou_dun02 165 269 lou_dun03 165 35 +lou_dun03 165 35 lou_dun02 165 269 +mag_dun01 125 67 yuno_fild03 33 140 +mag_dun01 243 240 mag_dun02 48 33 +mag_dun02 48 33 mag_dun01 243 240 +mjo_dun01 14 283 mjo_dun02 384 343 +mjo_dun01 52 14 mjolnir_02 79 365 +mjo_dun02 31 21 mjo_dun03 302 264 +mjo_dun02 39 21 mjo_dun03 308 264 +mjo_dun02 384 343 mjo_dun01 14 283 +mjo_dun03 302 264 mjo_dun02 31 21 +mjo_dun03 308 264 mjo_dun02 39 21 +mjolnir_01 284 18 gef_fild04 261 362 +mjolnir_01 381 256 mjolnir_02 28 258 +mjolnir_02 28 258 mjolnir_01 381 256 +mjolnir_02 79 365 mjo_dun01 52 14 +mjolnir_02 326 289 mjolnir_03 21 258 +mjolnir_02 361 18 mjolnir_06 366 383 +mjolnir_03 21 258 mjolnir_02 326 289 +mjolnir_03 212 17 mjolnir_07 214 383 +mjolnir_03 242 204 mjolnir_04 122 208 +mjolnir_04 122 208 mjolnir_03 242 204 +mjolnir_04 160 46 mjolnir_08 159 373 +mjolnir_04 387 174 mjolnir_05 16 171 +mjolnir_05 16 171 mjolnir_04 387 174 +mjolnir_05 220 382 mjolnir_12 220 26 +mjolnir_05 235 16 mjolnir_10 235 381 +mjolnir_06 18 331 gef_fild04 362 322 +mjolnir_06 265 29 gef_fild00 267 382 +mjolnir_06 366 383 mjolnir_02 361 18 +mjolnir_06 382 377 mjolnir_07 16 377 +mjolnir_06 383 74 mjolnir_07 17 77 +mjolnir_07 16 377 mjolnir_06 382 377 +mjolnir_07 17 77 mjolnir_06 383 74 +mjolnir_07 156 16 prt_fild00 159 383 +mjolnir_07 214 383 mjolnir_03 212 17 +mjolnir_07 383 233 mjolnir_08 30 234 +mjolnir_07 383 362 mjolnir_08 29 346 +mjolnir_08 29 346 mjolnir_07 383 362 +mjolnir_08 30 234 mjolnir_07 383 233 +mjolnir_08 159 373 mjolnir_04 160 46 +mjolnir_08 185 28 mjolnir_09 194 367 +mjolnir_08 369 257 mjolnir_10 15 258 +mjolnir_09 30 249 prt_fild00 383 249 +mjolnir_09 106 28 prt_fild05 105 381 +mjolnir_09 194 367 mjolnir_08 185 28 +mjolnir_09 300 28 prt_fild05 292 385 +mjolnir_09 373 288 prt_fild01 20 292 +mjolnir_10 15 258 mjolnir_08 369 257 +mjolnir_10 66 15 prt_fild01 66 373 +mjolnir_10 235 381 mjolnir_05 235 16 +mjolnir_10 265 13 prt_fild01 261 373 +mjolnir_10 384 220 mjolnir_11 20 220 +mjolnir_11 20 220 mjolnir_10 384 220 +mjolnir_11 174 20 prt_fild02 173 382 +mjolnir_12 44 17 prt_maze01 17 115 +mjolnir_12 199 378 aldebaran 138 34 +mjolnir_12 220 26 mjolnir_05 220 382 +#moc_castle 94 183 morocc 160 183 +#moc_castle 107 163 moc_castle 124 163 +#moc_castle 120 163 moc_castle 103 163 +#moc_castle 134 156 moc_castle 134 136 +#moc_castle 134 139 moc_castle 134 150 +#moc_castle 149 163 moc_castle 162 163 +#moc_castle 158 163 moc_castle 145 163 +#moc_castle 170 160 moc_castle 170 128 +#moc_castle 170 131 moc_castle 170 163 +#moc_castle 134 124 moc_castle 134 101 +#moc_castle 134 101 moc_castle 134 128 +#moc_castle 120 75 moc_castle 56 34 +#moc_castle 59 34 moc_castle 124 75 +#moc_castle 59 69 moc_castle 52 117 +#moc_castle 51 114 moc_castle 54 65 +#moc_castle 54 139 moc_castle 54 160 +#moc_castle 54 156 moc_castle 54 134 +#moc_castle 40 163 moc_castle 25 163 +#moc_castle 29 163 moc_castle 44 163 +#moc_castle 16 160 moc_castle 16 125 +#moc_castle 16 131 moc_castle 16 164 +#moc_castle 69 163 moc_castle 86 163 +#moc_castle 82 163 moc_castle 66 163 +#moc_castle 94 156 moc_castle 94 140 +#moc_castle 94 143 moc_castle 94 160 +#moc_castle 94 116 moc_castle 88 90 +#moc_castle 88 93 moc_castle 94 119 +#moc_castle 80 89 moc_castle 60 89 +#moc_castle 63 89 moc_castle 83 89 +#moc_castle 92 82 moc_castle 92 63 +#moc_castle 92 67 moc_castle 92 85 +moc_fild01 22 242 prt_fild09 383 223 +moc_fild01 56 384 prt_fild08 55 21 +moc_fild01 239 382 prt_fild08 233 16 +moc_fild01 321 16 moc_fild02 67 342 +moc_fild01 379 162 pay_fild04 17 165 +moc_fild02 67 342 moc_fild01 321 16 +moc_fild02 71 18 moc_fild13 146 368 +moc_fild02 228 29 moc_fild13 298 370 +moc_fild02 332 19 moc_fild03 70 341 +moc_fild02 350 339 pay_fild04 194 17 +moc_fild02 378 272 pay_gld 16 276 +moc_fild03 17 37 moc_fild13 308 49 +moc_fild03 70 341 moc_fild02 332 19 +moc_fild03 179 16 pay_fild11 38 330 +moc_fild03 303 170 pay_fild01 13 152 +moc_fild07 198 21 morocc 160 297 +moc_fild11 26 161 moc_fild12 289 168 +moc_fild11 212 29 moc_fild17 218 369 +moc_fild12 118 30 moc_fild18 158 382 +moc_fild12 159 381 morocc 160 17 +moc_fild12 289 168 moc_fild11 26 161 +moc_fild13 146 368 moc_fild02 71 18 +moc_fild13 298 370 moc_fild02 228 29 +moc_fild13 308 49 moc_fild03 17 37 +moc_fild16 16 179 moc_fild17 369 272 +moc_fild16 205 296 in_moc_16 18 8 +moc_fild17 30 300 moc_fild18 382 305 +moc_fild17 218 369 moc_fild11 212 29 +moc_fild17 369 272 moc_fild16 16 179 +moc_fild18 51 170 cmd_fild09 369 164 +moc_fild18 158 382 moc_fild12 118 30 +moc_fild18 382 305 moc_fild17 30 300 +moc_fild19 71 170 moc_ruins 71 16 +moc_fild19 98 99 in_sphinx1 288 6 +moc_fild19 169 107 morocc 24 164 +moc_pryd01 10 195 moc_pryd02 10 192 +moc_pryd01 10 195 moc_pryd02 10 195 +moc_pryd01 90 109 moc_prydb1 100 191 +moc_pryd01 195 9 moc_ruins 54 161 +moc_pryd02 10 195 moc_pryd01 10 192 +moc_pryd02 10 195 moc_pryd01 10 195 +moc_pryd02 100 99 moc_pryd03 100 97 +moc_pryd03 12 15 moc_pryd04 12 15 +moc_pryd03 15 187 moc_pryd04 15 187 +moc_pryd03 100 97 moc_pryd02 100 99 +moc_pryd03 184 11 moc_pryd04 184 11 +moc_pryd03 188 184 moc_pryd04 188 184 +moc_pryd04 12 15 moc_pryd03 12 15 +moc_pryd04 15 187 moc_pryd03 15 187 +moc_pryd04 184 11 moc_pryd03 184 11 +moc_pryd04 188 184 moc_pryd03 188 184 +moc_pryd05 94 98 moc_prydb1 100 55 +moc_pryd05 223 9 moc_pryd06 195 8 +moc_pryd06 195 8 moc_pryd05 223 9 +moc_prydb1 59 115 moc_prydb1 87 115 +moc_prydb1 87 115 moc_prydb1 59 115 +moc_prydb1 100 55 moc_pryd05 94 98 +moc_prydb1 100 77 moc_prydb1 100 104 +moc_prydb1 100 104 moc_prydb1 100 77 +moc_prydb1 100 191 moc_pryd01 90 109 +moc_prydb1 111 115 moc_prydb1 142 115 +moc_prydb1 142 115 moc_prydb1 111 115 +moc_ruins 54 161 moc_pryd01 195 9 +moc_ruins 71 16 moc_fild19 71 170 +moc_ruins 159 37 morocc 25 294 +monk_test 329 61 job_monk 226 175 0 c c r0 c +morocc 24 164 moc_fild19 169 107 +morocc 25 294 moc_ruins 159 37 +morocc 46 46 morocc_in 68 75 +morocc 52 259 morocc_in 183 65 +morocc 85 55 morocc_in 44 146 +morocc 98 68 morocc_in 44 178 +morocc 160 17 moc_fild12 159 381 +morocc 160 183 moc_castle 94 183 +morocc 160 297 moc_fild07 198 21 +morocc 197 66 morocc_in 83 90 +morocc 253 56 morocc_in 134 77 +morocc 274 269 morocc_in 136 136 +morocc 284 170 morocc_in 108 179 +morocc_in 23 161 morocc_in 34 161 +morocc_in 34 161 morocc_in 23 161 +morocc_in 44 146 morocc 85 55 +morocc_in 44 178 morocc 98 68 +morocc_in 55 95 morocc_in 68 95 +morocc_in 55 123 morocc_in 68 123 +morocc_in 57 161 morocc_in 70 161 +morocc_in 68 42 morocc_in 68 62 +morocc_in 68 62 morocc_in 68 42 +morocc_in 68 75 morocc 46 46 +morocc_in 68 95 morocc_in 55 95 +morocc_in 68 123 morocc_in 55 123 +morocc_in 70 161 morocc_in 57 161 +morocc_in 83 90 morocc 197 66 +morocc_in 86 101 morocc_in 86 117 +morocc_in 86 117 morocc_in 86 101 +morocc_in 93 95 morocc_in 105 95 +morocc_in 93 123 morocc_in 106 123 +morocc_in 105 95 morocc_in 93 95 +morocc_in 106 123 morocc_in 93 123 +morocc_in 108 179 morocc 284 170 +morocc_in 134 77 morocc 253 56 +morocc_in 136 136 morocc 274 269 +morocc_in 144 109 morocc_in 144 122 +morocc_in 144 122 morocc_in 144 109 +morocc_in 144 151 morocc_in 144 166 +morocc_in 144 166 morocc_in 144 151 +morocc_in 149 129 morocc_in 166 130 +morocc_in 166 130 morocc_in 149 129 +morocc_in 171 37 morocc_in 171 50 +morocc_in 171 50 morocc_in 171 37 +morocc_in 174 109 morocc_in 174 122 +morocc_in 174 122 morocc_in 174 109 +morocc_in 174 151 morocc_in 174 166 +morocc_in 174 166 morocc_in 174 151 +morocc_in 183 65 morocc 52 259 +nif_in 88 29 nif_in 34 34 +nif_in 34 34 nif_in 88 29 +nif_in 23 11 niflheim 189 210 +nif_fild01 344 322 nif_fild02 22 311 +nif_fild02 373 235 niflheim 19 154 +nif_fild02 22 311 nif_fild01 344 322 +niflheim 189 210 nif_in 23 11 +niflheim 19 154 nif_fild02 378 235 +orcsdun01 32 172 in_orcs01 30 182 +orcsdun01 183 8 orcsdun02 21 188 +orcsdun02 21 188 orcsdun01 183 8 +orcsdun02 180 15 in_orcs01 108 114 +payon 267 89 pay_fild08 17 75 +payon 228 329 pay_arche 81 22 +payon 122 27 pay_fild01 333 356 +payon 16 143 pay_gld 370 149 +payon 141 85 payon_in01 14 51 +payon 151 127 payon_in01 56 53 +payon 136 158 payon_in01 20 129 +payon 130 169 payon_in01 13 139 +payon 189 233 payon_in03 149 39 +payon 187 233 payon_in03 149 39 +payon 270 152 payon_in01 90 9 +pay_arche 36 131 pay_dun00 21 186 +pay_arche 81 17 payon 228 329 +pay_arche 145 165 payon_in02 64 56 +pay_arche 71 156 payon_in02 82 45 +pay_arche 92 170 payon_in02 50 4 +pay_dun00 21 186 pay_arche 36 131 +pay_dun00 184 33 pay_dun01 15 33 +pay_dun01 15 33 pay_dun00 184 33 +pay_dun01 286 25 pay_dun02 16 63 +pay_dun02 16 63 pay_dun01 286 25 +pay_dun02 137 128 pay_dun03 155 161 +pay_dun03 127 62 pay_dun04 202 206 +pay_dun03 127 62 pay_dun04 40 37 +pay_dun03 155 161 pay_dun02 137 128 +pay_dun04 40 37 pay_dun03 127 62 +pay_dun04 202 206 pay_dun03 127 62 +pay_fild01 13 152 moc_fild03 303 170 +pay_fild01 278 14 pay_fild02 83 386 +pay_fild01 353 14 pay_fild02 167 390 +pay_fild01 379 201 pay_fild07 16 200 +pay_fild01 333 361 payon 122 27 +#pay_fild02 16 175 pay_fild11 297 135 +pay_fild02 83 386 pay_fild01 278 14 +pay_fild02 134 16 pay_fild05 127 378 +pay_fild02 167 390 pay_fild01 353 14 +pay_fild02 284 108 pay_fild03 15 110 +pay_fild03 15 110 pay_fild02 284 108 +pay_fild03 172 281 pay_fild07 164 17 +pay_fild03 313 16 pay_fild06 305 375 +pay_fild03 392 63 alberta 15 234 +pay_fild04 17 165 moc_fild01 379 162 +pay_fild04 194 17 moc_fild02 350 339 +pay_fild05 127 378 pay_fild02 134 16 +pay_fild05 271 284 pay_fild06 28 288 +pay_fild06 28 288 pay_fild05 271 284 +pay_fild06 305 375 pay_fild03 313 16 +pay_fild07 16 200 pay_fild01 379 201 +pay_fild07 164 17 pay_fild03 172 281 +pay_fild07 280 382 pay_fild08 160 16 +pay_fild07 382 290 pay_fild10 16 290 +pay_fild08 17 75 payon 265 92 +pay_fild08 160 16 pay_fild07 280 382 +pay_fild08 262 91 pay_fild09 16 91 +pay_fild09 16 91 pay_fild08 262 91 +pay_fild09 112 16 pay_fild10 112 382 +pay_fild10 16 290 pay_fild07 382 290 +pay_fild10 112 382 pay_fild09 112 16 +pay_fild11 38 330 moc_fild03 179 16 +#pay_fild11 297 135 pay_fild02 16 175 +pay_gld 16 276 moc_fild02 378 272 +pay_gld 121 238 payg_cas01 214 44 +pay_gld 140 156 payg_cas04 252 275 +pay_gld 204 270 payg_cas05 62 223 +pay_gld 291 116 payg_cas02 276 61 +pay_gld 321 293 payg_cas03 226 22 +pay_gld 374 149 payon 16 143 +payg_cas01 214 44 pay_gld 121 233 +payg_cas01 201 126 payg_cas01 102 21 +payg_cas01 102 17 payg_cas01 201 121 +payg_cas01 222 130 payg_cas01 130 43 +payg_cas01 134 43 payg_cas01 226 130 +payg_cas01 218 112 payg_cas01 230 94 +payg_cas01 230 98 payg_cas01 222 112 +payg_cas01 213 76 payg_cas01 201 118 +payg_cas01 201 114 payg_cas01 213 72 +payg_cas01 84 15 payg_cas01 15 115 +payg_cas01 11 115 payg_cas01 81 15 +payg_cas01 53 111 payg_cas01 115 147 +payg_cas01 115 151 payg_cas01 53 115 +payg_cas02 276 61 pay_gld 295 116 +payg_cas02 232 72 payg_cas02 28 289 +payg_cas02 28 293 payg_cas02 229 72 +payg_cas02 236 59 payg_cas02 229 72 +payg_cas02 222 26 payg_cas02 80 240 +payg_cas02 84 240 payg_cas02 224 30 +payg_cas02 215 31 payg_cas02 65 288 +payg_cas02 65 292 payg_cas02 215 35 +payg_cas02 47 223 payg_cas02 280 287 +payg_cas02 280 291 payg_cas02 47 227 +payg_cas02 254 239 payg_cas02 13 38 +payg_cas02 13 42 payg_cas02 254 243 +payg_cas03 226 22 pay_gld 317 293 +payg_cas03 255 76 payg_cas03 24 19 +payg_cas03 20 19 payg_cas03 255 72 +payg_cas03 269 79 payg_cas03 53 19 +payg_cas03 57 19 payg_cas03 269 75 +payg_cas03 255 64 payg_cas03 245 37 +payg_cas03 245 41 payg_cas03 255 68 +payg_cas03 262 71 payg_cas03 39 9 +payg_cas03 39 5 payg_cas03 263 66 +payg_cas03 272 68 payg_cas03 261 38 +payg_cas03 261 34 payg_cas03 270 66 +payg_cas03 39 84 payg_cas03 29 249 +payg_cas03 29 245 payg_cas03 39 80 +payg_cas03 29 269 payg_cas03 269 287 +payg_cas03 269 290 payg_cas03 29 273 +payg_cas04 252 275 pay_gld 140 160 +payg_cas04 260 212 payg_cas04 70 240 +payg_cas04 74 240 payg_cas04 256 212 +payg_cas04 232 189 payg_cas04 74 261 +payg_cas04 78 261 payg_cas04 236 189 +payg_cas04 229 208 payg_cas04 70 282 +payg_cas04 74 282 payg_cas04 225 208 +payg_cas04 7 261 payg_cas04 55 30 +payg_cas04 59 30 payg_cas04 11 261 +payg_cas04 28 31 payg_cas04 251 42 +payg_cas04 254 45 payg_cas04 24 31 +payg_cas05 23 283 payg_cas05 237 282 +payg_cas05 237 286 payg_cas05 19 282 +payg_cas05 56 255 payg_cas05 223 256 +payg_cas05 219 256 payg_cas05 52 255 +payg_cas05 39 264 payg_cas05 237 231 +payg_cas05 237 227 payg_cas05 40 260 +payg_cas05 283 256 payg_cas05 286 43 +payg_cas05 290 43 payg_cas05 287 256 +payg_cas05 242 41 payg_cas05 18 18 +payg_cas05 14 14 payg_cas05 246 41 +payg_cas05 62 223 pay_gld 198 264 +payon_in01 17 51 payon 141 85 +payon_in01 10 80 payon_in01 10 59 +payon_in01 10 59 payon_in01 10 83 +payon_in01 10 38 payon_in01 10 80 +payon_in01 10 17 payon_in01 10 38 +payon_in01 56 50 payon 151 127 +payon_in01 23 129 payon 136 158 +payon_in01 13 139 payon 130 172 +payon_in01 86 9 payon 270 152 +payon_in01 22 26 payon_in01 30 81 +payon_in01 26 115 payon_in01 30 126 +payon_in01 26 164 payon_in01 30 151 +payon_in01 30 59 payon_in01 52 35 +payon_in01 30 81 payon_in01 22 26 +payon_in01 30 126 payon_in01 26 115 +payon_in01 30 151 payon_in01 26 164 +payon_in01 52 35 payon_in01 30 59 +payon_in01 60 99 payon_in01 113 77 +payon_in01 60 107 payon_in01 113 107 +payon_in01 60 145 payon_in01 113 143 +payon_in01 60 170 payon_in01 72 159 +payon_in01 70 52 payon_in01 86 37 +payon_in01 70 75 payon_in01 130 36 +payon_in01 72 121 payon_in01 72 134 +payon_in01 72 134 payon_in01 72 121 +payon_in01 72 159 payon_in01 60 170 +payon_in01 83 99 payon_in01 122 77 +payon_in01 83 107 payon_in01 122 107 +payon_in01 83 145 payon_in01 124 143 +payon_in01 86 37 payon_in01 70 52 +payon_in01 111 177 payon_in01 126 177 +payon_in01 113 77 payon_in01 60 99 +payon_in01 113 107 payon_in01 60 107 +payon_in01 113 143 payon_in01 60 145 +payon_in01 122 77 payon_in01 83 99 +payon_in01 122 107 payon_in01 83 107 +payon_in01 124 143 payon_in01 83 145 +payon_in01 126 177 payon_in01 111 177 +payon_in01 130 36 payon_in01 70 75 +payon_in01 143 179 payon_in01 164 179 +payon_in01 164 179 payon_in01 143 179 +payon_in01 167 74 payon_in01 168 53 +payon_in01 168 53 payon_in01 167 74 +payon_in01 170 87 payon_in01 170 100 +payon_in01 170 100 payon_in01 170 87 +payon_in01 170 119 payon_in01 170 132 +payon_in01 170 132 payon_in01 170 119 +payon_in02 10 25 payon_in02 75 67 +payon_in02 35 67 payon_in02 52 67 +payon_in02 50 4 pay_arche 92 170 +payon_in02 52 67 payon_in02 35 67 +payon_in02 61 33 payon_in02 70 33 +payon_in02 64 56 pay_arche 145 165 +payon_in02 70 33 payon_in02 61 33 +payon_in02 75 67 payon_in02 10 25 +payon_in02 82 45 pay_arche 71 156 +payon_in03 172 33 payon_in03 160 14 +payon_in03 160 15 payon_in03 172 35 +payon_in03 147 39 payon 186 233 +payon_in03 158 32 payon 189 233 +payon_in03 130 17 payon_in03 158 32 +payon_in03 172 32 payon_in03 130 17 +payon_in03 160 17 payon_in03 172 32 +payon_in03 186 32 payon_in03 160 17 +payon_in03 190 17 payon_in03 186 32 +payon_in03 146 39 payon 186 233 +prontera 22 203 prt_fild05 373 205 +prontera 42 67 prt_in 47 29 +prontera 45 346 prt_in 80 113 +prontera 73 100 prt_in 208 179 +prontera 74 90 prt_in 248 173 +prontera 84 89 prt_in 282 179 +prontera 107 215 prt_in 240 141 +prontera 120 267 prt_in 183 97 +prontera 132 222 prt_in 135 71 +prontera 133 183 prt_in 53 105 +prontera 156 22 prt_fild08 170 378 +prontera 156 360 prt_castle 102 16 +prontera 177 221 prt_in 168 124 +prontera 179 184 prt_in 60 77 +prontera 192 267 prt_in 181 55 +prontera 204 192 prt_in 68 130 +prontera 208 154 prt_in 172 33 +prontera 237 317 prt_church 100 56 +prontera 289 203 prt_fild06 23 193 +prontera 121 72 prt_cas 18 29 c r0 +prt_cas 27 27 dali 109 86 c r3 c c r0 +prt_castle 28 121 prt_castle 40 138 +prt_castle 31 113 prt_castle 54 113 +prt_castle 40 138 prt_castle 28 121 +prt_castle 45 145 prt_castle 68 153 +prt_castle 54 113 prt_castle 31 113 +prt_castle 59 29 prt_castle 82 29 +prt_castle 68 153 prt_castle 45 145 +prt_castle 75 107 prt_castle 92 107 +prt_castle 82 29 prt_castle 59 29 +prt_castle 92 107 prt_castle 75 107 +prt_castle 102 16 prontera 156 360 +prt_castle 102 73 prt_castle 102 88 +prt_castle 102 88 prt_castle 102 73 +prt_castle 102 129 prt_castle 102 140 +prt_castle 102 140 prt_castle 102 129 +prt_castle 102 181 prt_gld 159 25 +prt_castle 113 107 prt_castle 130 107 +prt_castle 121 29 prt_castle 144 29 +prt_castle 130 107 prt_castle 113 107 +prt_castle 135 153 prt_castle 164 145 +prt_castle 144 29 prt_castle 121 29 +prt_castle 149 113 prt_castle 172 113 +prt_castle 164 145 prt_castle 135 153 +prt_castle 170 138 prt_castle 176 121 +prt_castle 172 113 prt_castle 149 113 +prt_castle 176 121 prt_castle 170 138 +prt_church 31 19 prt_church 90 81 +prt_church 90 81 prt_church 31 19 +prt_church 100 56 prontera 237 317 +prt_church 109 81 prt_church 168 19 +prt_church 168 19 prt_church 109 81 +prt_fild00 18 129 gef_fild00 381 137 +prt_fild00 159 383 mjolnir_07 156 16 +prt_fild00 165 18 prt_fild04 160 387 +prt_fild00 317 18 prt_fild04 323 387 +prt_fild00 383 249 mjolnir_09 30 249 +prt_fild01 20 292 mjolnir_09 373 288 +prt_fild01 66 373 mjolnir_10 66 15 +prt_fild01 136 373 prt_maze01 176 4 +prt_fild01 199 24 prt_gld 159 298 +prt_fild01 261 373 mjolnir_10 265 13 +prt_fild01 380 243 prt_fild02 17 242 +prt_fild01 382 304 prt_fild02 17 305 +prt_fild01 382 351 prt_fild02 17 350 +prt_fild02 17 242 prt_fild01 380 243 +prt_fild02 17 305 prt_fild01 382 304 +prt_fild02 17 350 prt_fild01 382 351 +prt_fild02 173 382 mjolnir_11 174 20 +prt_fild02 305 17 prt_fild06 277 320 +prt_fild02 380 347 prt_fild03 16 249 +prt_fild03 16 249 prt_fild02 380 347 +prt_fild03 371 255 prt_monk 22 248 +prt_fild04 17 114 gef_fild01 382 111 +prt_fild04 160 387 prt_fild00 165 18 +prt_fild04 323 387 prt_fild00 317 18 +prt_fild04 378 72 prt_fild05 13 63 +prt_fild04 384 155 prt_fild05 14 141 +prt_fild04 384 334 prt_fild05 15 333 +prt_fild05 13 63 prt_fild04 378 72 +prt_fild05 14 141 prt_fild04 384 155 +prt_fild05 15 333 prt_fild04 384 334 +prt_fild05 105 381 mjolnir_09 106 28 +prt_fild05 134 14 prt_fild07 132 381 +prt_fild05 255 14 prt_fild07 248 376 +prt_fild05 292 385 mjolnir_09 300 28 +prt_fild05 373 205 prontera 22 203 +prt_fild06 23 193 prontera 289 203 +prt_fild06 277 320 prt_fild02 305 17 +prt_fild07 13 64 gef_fild02 380 68 +prt_fild07 14 289 gef_fild02 380 289 +prt_fild07 17 145 gef_fild02 380 156 +prt_fild07 84 13 prt_fild09 87 380 +prt_fild07 132 381 prt_fild05 134 14 +prt_fild07 206 12 prt_fild09 224 380 +prt_fild07 248 376 prt_fild05 255 14 +prt_fild07 383 239 prt_fild08 16 239 +prt_fild07 385 186 prt_fild08 16 187 +prt_fild08 16 187 prt_fild07 385 186 +prt_fild08 16 239 prt_fild07 383 239 +prt_fild08 55 21 moc_fild01 56 384 +prt_fild08 170 378 prontera 156 22 +prt_fild08 233 16 moc_fild01 239 382 +prt_fild08 371 212 izlude 30 78 +prt_fild09 14 139 prt_fild10 339 126 +prt_fild09 87 380 prt_fild07 84 13 +prt_fild09 224 380 prt_fild07 206 12 +prt_fild09 383 223 moc_fild01 22 242 +prt_fild10 20 122 prt_fild11 362 111 +prt_fild10 20 196 prt_fild11 361 184 +prt_fild10 227 299 gef_fild02 266 18 +prt_fild10 339 126 prt_fild09 14 139 +prt_fild11 17 281 gef_fild11 377 293 +prt_fild11 302 301 gef_fild03 312 16 +prt_fild11 361 184 prt_fild10 20 196 +prt_fild11 362 111 prt_fild10 20 122 +prt_gld 107 240 prtg_cas04 86 9 +prt_gld 129 65 prtg_cas01 103 32 +prt_gld 153 141 prtg_cas03 168 8 +prt_gld 159 25 prt_castle 102 181 +prt_gld 159 298 prt_fild01 199 24 +prt_gld 212 240 prtg_cas05 17 231 +prt_gld 240 124 prtg_cas02 43 233 +prt_in 37 65 prt_in 48 65 +prt_in 47 29 prontera 42 67 +prt_in 48 65 prt_in 37 65 +prt_in 53 105 prontera 133 183 +prt_in 60 77 prontera 179 184 +prt_in 68 130 prontera 204 192 +prt_in 69 65 prt_in 82 65 +prt_in 70 143 prt_in 70 162 +prt_in 70 162 prt_in 70 143 +prt_in 80 113 prontera 45 346 +prt_in 82 65 prt_in 69 65 +prt_in 135 71 prontera 132 222 +prt_in 168 124 prontera 177 221 +prt_in 172 33 prontera 208 154 +prt_in 181 55 prontera 192 267 +prt_in 183 97 prontera 120 267 +prt_in 208 179 prontera 73 100 +prt_in 217 163 prt_in 234 163 +prt_in 234 163 prt_in 217 163 +prt_in 240 141 prontera 107 215 +prt_in 248 173 prontera 74 90 +prt_in 254 113 prt_in 256 131 +prt_in 256 131 prt_in 254 113 +prt_in 263 163 prt_in 274 163 +prt_in 274 163 prt_in 263 163 +prt_in 282 179 prontera 84 89 +prt_maze01 100 35 prt_maze01 139 47 +prt_maze01 102 165 prt_maze01 98 151 +prt_maze01 105 115 prt_maze01 175 168 +prt_maze01 105 75 prt_maze01 54 8 +prt_maze01 115 145 prt_maze01 8 186 +prt_maze01 115 21 prt_maze01 167 22 +prt_maze01 115 56 prt_maze01 7 57 +prt_maze01 115 96 prt_maze01 128 105 +prt_maze01 124 105 prt_maze01 111 96 +prt_maze01 124 169 prt_maze01 191 139 +prt_maze01 138 124 prt_maze01 142 111 +prt_maze01 139 44 prt_maze01 100 32 +prt_maze01 14 75 prt_maze01 63 128 +prt_maze01 140 75 prt_maze01 96 47 +prt_maze01 142 115 prt_maze01 138 128 +prt_maze01 155 133 prt_maze01 8 140 +prt_maze01 155 181 prt_maze01 88 145 +prt_maze01 155 21 prt_maze01 87 13 +prt_maze01 164 140 prt_maze01 70 68 +prt_maze01 164 22 prt_maze01 112 21 +prt_maze01 164 93 prt_maze01 72 11 +prt_maze01 17 115 prt_maze01 50 48 +prt_maze01 17 34 prt_maze01 23 128 +prt_maze01 175 164 prt_maze01 105 111 +prt_maze01 176 35 prt_maze01 182 88 +prt_maze01 176 4 prt_fild01 136 368 +prt_maze01 176 44 prt_maze01 18 152 +prt_maze01 18 155 prt_maze01 176 47 +prt_maze01 182 84 prt_maze01 177 31 +prt_maze01 19 195 prt_maze02 94 19 +prt_maze01 195 139 prt_maze01 129 174 +prt_maze01 195 15 prt_maze01 47 105 +prt_maze01 195 174 prt_maze01 47 23 +prt_maze01 195 55 prt_maze01 87 97 +prt_maze01 195 93 prt_maze01 88 55 +prt_maze01 22 84 prt_maze01 55 151 +prt_maze01 23 124 prt_maze01 17 30 +prt_maze01 25 5 prt_maze01 65 113 +prt_maze01 4 140 prt_maze01 151 133 +prt_maze01 4 186 prt_maze01 111 145 +prt_maze01 4 57 prt_maze01 112 56 +prt_maze01 44 105 prt_maze01 192 15 +prt_maze01 44 23 prt_maze01 192 174 +prt_maze01 50 44 prt_maze01 17 111 +prt_maze01 51 195 prt_maze01 63 88 +prt_maze01 54 4 prt_maze01 105 71 +prt_maze01 55 155 prt_maze01 22 88 +prt_maze01 63 124 prt_maze01 14 71 +prt_maze01 63 195 prt_maze01 63 88 +prt_maze01 63 84 prt_maze01 58 192 +prt_maze01 65 116 prt_maze01 23 9 +prt_maze01 70 75 prt_maze01 169 140 +prt_maze01 75 11 prt_maze01 167 93 +prt_maze01 75 66 prt_maze01 169 140 +prt_maze01 75 95 prt_maze01 88 173 +prt_maze01 84 10 prt_maze01 152 25 +prt_maze01 84 145 prt_maze01 151 181 +prt_maze01 84 173 prt_maze01 71 95 +prt_maze01 84 55 prt_maze01 191 93 +prt_maze01 84 97 prt_maze01 192 55 +prt_maze01 96 44 prt_maze01 140 72 +prt_maze01 98 155 prt_maze01 102 169 +prt_maze02 103 15 prt_maze01 22 191 +prt_maze02 108 182 prt_maze03 23 8 +prt_maze02 80 182 prt_maze03 23 8 +prt_maze02 84 15 prt_maze01 22 191 +prt_maze03 100 35 prt_maze03 63 88 +prt_maze03 102 164 prt_maze03 143 111 +prt_maze03 104 115 prt_maze03 22 88 +prt_maze03 105 75 prt_maze03 140 47 +prt_maze03 115 145 prt_maze03 88 173 +prt_maze03 115 21 prt_maze03 128 105 +prt_maze03 115 56 prt_maze03 88 145 +prt_maze03 115 96 prt_maze03 167 22 +prt_maze03 124 105 prt_maze03 111 21 +prt_maze03 124 168 prt_maze03 69 69 +prt_maze03 137 124 prt_maze03 177 32 +prt_maze03 14 75 prt_maze03 54 8 +prt_maze03 140 44 prt_maze03 105 72 +prt_maze03 140 75 prt_maze03 97 48 +prt_maze03 143 115 prt_maze03 102 168 +prt_maze03 155 133 prt_maze03 8 186 +prt_maze03 155 181 prt_maze03 88 56 +prt_maze03 155 21 prt_maze03 48 22 +prt_maze03 164 139 prt_maze03 191 16 +prt_maze03 164 22 prt_maze03 111 95 +prt_maze03 17 115 prt_maze03 49 47 +prt_maze03 175 164 prt_maze03 18 30 +prt_maze03 176 35 prt_maze03 137 127 +prt_maze03 176 44 prt_maze03 55 151 +prt_maze03 178 75 prt_maze03 182 88 +prt_maze03 18 155 prt_maze03 63 128 +prt_maze03 18 34 prt_maze03 175 168 +prt_maze03 182 84 prt_maze03 178 71 +prt_maze03 195 139 prt_maze03 167 22 +prt_maze03 195 16 prt_maze03 168 139 +prt_maze03 195 174 prt_maze03 48 105 +prt_maze03 195 54 prt_maze03 88 96 +prt_maze03 22 195 prt_maze03 65 111 +prt_maze03 22 84 prt_maze03 104 111 +prt_maze03 23 124 prt_maze03 98 151 +prt_maze03 25 4 prt_maze02 95 177 +prt_maze03 4 140 prt_maze03 60 192 +prt_maze03 4 186 prt_maze03 151 133 +prt_maze03 4 57 prt_maze03 71 95 +prt_maze03 44 105 prt_maze03 191 174 +prt_maze03 44 22 prt_maze03 151 25 +prt_maze03 49 44 prt_maze03 17 112 +prt_maze03 53 195 prt_maze03 7 140 +prt_maze03 54 4 prt_maze03 14 71 +prt_maze03 55 155 prt_maze03 176 48 +prt_maze03 63 124 prt_maze03 18 151 +prt_maze03 63 195 prt_maze03 7 140 +prt_maze03 63 84 prt_maze03 100 31 +prt_maze03 65 115 prt_maze03 22 191 +prt_maze03 71 75 prt_maze03 127 173 +prt_maze03 75 12 prt_maze03 88 13 +prt_maze03 75 64 prt_maze03 127 173 +prt_maze03 75 95 prt_maze03 8 57 +prt_maze03 84 10 prt_maze03 71 12 +prt_maze03 84 145 prt_maze03 111 56 +prt_maze03 84 173 prt_maze03 111 145 +prt_maze03 84 56 prt_maze03 151 181 +prt_maze03 84 96 prt_maze03 191 54 +prt_maze03 97 44 prt_maze03 140 71 +prt_maze03 98 155 prt_maze03 23 128 +prt_monk 22 248 prt_fild03 371 255 +prt_monk 192 172 monk_test 329 50 +prt_sewb1 135 248 prt_fild05 270 212 +prt_sewb1 188 247 prt_sewb2 19 12 +prt_sewb2 19 12 prt_sewb1 188 247 +prt_sewb2 19 175 prt_sewb2 60 24 +prt_sewb2 60 24 prt_sewb2 19 175 +prt_sewb2 100 176 prt_sewb2 140 24 +prt_sewb2 140 24 prt_sewb2 100 176 +prt_sewb2 180 24 prt_sewb3 180 173 +prt_sewb3 20 185 prt_sewb4 100 96 +prt_sewb3 180 173 prt_sewb2 180 24 +prt_sewb4 100 96 prt_sewb3 20 185 +prtg_cas01 103 32 prt_gld 134 65 +prtg_cas01 109 163 prtg_cas01 202 183 +prtg_cas01 147 120 prtg_cas01 75 187 +prtg_cas01 196 119 prtg_cas01 40 54 +prtg_cas01 196 65 prtg_cas01 75 54 +prtg_cas01 206 183 prtg_cas01 113 163 +prtg_cas01 206 92 prtg_cas01 55 70 +prtg_cas01 37 47 prtg_cas01 45 34 +prtg_cas01 37 54 prtg_cas01 192 119 +prtg_cas01 41 34 prtg_cas01 40 47 +prtg_cas01 51 70 prtg_cas01 202 92 +prtg_cas01 57 19 prtg_cas01 80 49 +prtg_cas01 62 34 prtg_cas01 192 119 +prtg_cas01 71 54 prtg_cas01 192 65 +prtg_cas01 75 183 prtg_cas01 147 116 +prtg_cas01 84 19 prtg_cas01 192 65 +prtg_cas01 84 49 prtg_cas01 61 19 +prtg_cas02 157 135 prtg_cas02 184 40 +prtg_cas02 161 41 prtg_cas02 57 202 +prtg_cas02 184 44 prtg_cas02 157 140 +prtg_cas02 203 21 prtg_cas02 45 25 +prtg_cas02 210 41 prtg_cas02 84 215 +prtg_cas02 35 183 prtg_cas02 71 82 +prtg_cas02 43 233 prt_gld 240 128 +prtg_cas02 45 21 prtg_cas02 203 25 +prtg_cas02 53 202 prtg_cas02 165 41 +prtg_cas02 64 164 prtg_cas02 98 25 +prtg_cas02 71 86 prtg_cas02 35 187 +prtg_cas02 88 215 prtg_cas02 206 41 +prtg_cas02 98 21 prtg_cas02 64 168 +prtg_cas03 164 173 prtg_cas03 45 117 +prtg_cas03 165 59 prtg_cas03 45 47 +prtg_cas03 168 8 prt_gld 153 137 +prtg_cas03 169 235 prtg_cas03 11 200 +prtg_cas03 172 44 prtg_cas03 10 78 +prtg_cas03 178 85 prtg_cas03 82 73 +prtg_cas03 191 55 prtg_cas03 190 233 +prtg_cas03 194 233 prtg_cas03 191 59 +prtg_cas03 45 120 prtg_cas03 164 177 +prtg_cas03 45 43 prtg_cas03 165 54 +prtg_cas03 6 78 prtg_cas03 176 44 +prtg_cas03 7 200 prtg_cas03 169 231 +prtg_cas03 86 73 prtg_cas03 178 81 +prtg_cas04 10 229 prtg_cas04 48 44 +prtg_cas04 238 257 prtg_cas04 34 286 +prtg_cas04 247 258 prtg_cas04 255 14 +prtg_cas04 251 14 prtg_cas04 247 254 +prtg_cas04 32 28 prtg_cas04 11 254 +prtg_cas04 34 225 prtg_cas04 63 26 +prtg_cas04 34 290 prtg_cas04 238 261 +prtg_cas04 42 13 prtg_cas04 56 254 +prtg_cas04 48 48 prtg_cas04 10 233 +prtg_cas04 54 25 prtg_cas04 56 233 +prtg_cas04 56 229 prtg_cas04 54 29 +prtg_cas04 60 254 prtg_cas04 42 17 +prtg_cas04 63 30 prtg_cas04 34 229 +prtg_cas04 7 254 prtg_cas04 32 32 +prtg_cas04 86 9 prt_gld 111 240 +prtg_cas05 17 231 prt_gld 208 240 +prtg_cas05 195 13 prtg_cas05 55 248 +prtg_cas05 228 96 prtg_cas05 26 7 +prtg_cas05 244 3 prtg_cas05 35 247 +prtg_cas05 253 294 prtg_cas05 58 11 +prtg_cas05 26 3 prtg_cas05 228 92 +prtg_cas05 260 96 prtg_cas05 66 229 +prtg_cas05 292 13 prtg_cas05 76 246 +prtg_cas05 38 250 prtg_cas05 244 7 +prtg_cas05 53 246 prtg_cas05 199 13 +prtg_cas05 58 7 prtg_cas05 253 290 +prtg_cas05 66 225 prtg_cas05 260 92 +prtg_cas05 76 242 prtg_cas05 288 13 +treasure01 27 164 treasure01 38 164 +treasure01 38 164 treasure01 27 164 +treasure01 41 37 treasure01 58 37 +treasure01 41 111 treasure01 61 111 +treasure01 58 37 treasure01 41 37 +treasure01 61 111 treasure01 41 111 +treasure01 69 22 alb2trea 88 111 +treasure01 69 75 treasure01 69 102 +treasure01 69 102 treasure01 69 75 +treasure01 69 125 treasure01 69 140 +treasure01 69 140 treasure01 69 125 +treasure01 69 177 treasure02 102 24 +treasure01 76 111 treasure01 98 111 +treasure01 79 37 treasure01 96 37 +treasure01 96 37 treasure01 79 37 +treasure01 98 111 treasure01 76 111 +treasure01 99 164 treasure01 112 164 +treasure01 112 164 treasure01 99 164 +treasure01 125 161 treasure01 142 161 +treasure01 142 161 treasure01 125 161 +treasure01 164 91 treasure01 164 114 +treasure01 164 114 treasure01 164 91 +treasure02 49 99 treasure02 49 128 +treasure02 49 128 treasure02 49 99 +treasure02 65 72 treasure02 80 72 +treasure02 80 72 treasure02 65 72 +treasure02 102 24 treasure01 69 177 +treasure02 102 103 treasure02 102 118 +treasure02 102 118 treasure02 102 103 +treasure02 123 72 treasure02 138 72 +treasure02 138 72 treasure02 123 72 +treasure02 155 99 treasure02 155 128 +treasure02 155 128 treasure02 155 99 +tur_dun01 154 241 tur_dun02 148 268 +tur_dun03 132 193 tur_dun02 167 19 +tur_dun02 148 268 tur_dun01 154 241 +tur_dun02 167 19 tur_dun03 132 193 +tur_dun03 217 71 tur_dun04 100 196 +tur_dun04 100 196 tur_dun03 217 71 +umbala 130 79 um_fild04 215 339 +umbala 129 78 um_fild04 215 340 +umbala 106 285 um_dun01 42 27 +umbala 107 130 um_in 99 63 +umbala 138 129 um_in 99 114 +umbala 125 157 um_in 155 111 +umbala 94 186 um_in 141 39 +umbala 100 203 um_in 163 69 +umbala 107 118 um_in 15 20 +# bungee jump +#umbala 140 197 nif_in 68 14 +um_dun01 42 27 umbala 106 285 +um_dun01 149 198 um_dun02 52 22 +um_dun02 52 22 um_dun01 149 198 +um_dun02 126 162 yggdrasil01 38 63 +um_fild01 31 374 beach_dun2 258 244 +um_fild01 369 277 um_fild02 22 272 +um_fild01 31 274 beach_dun2 258 244 +um_fild02 188 374 um_fild04 182 13 +um_fild02 373 148 um_fild03 32 145 +um_fild02 373 329 um_fild03 19 334 +um_fild02 22 272 um_fild01 369 277 +um_fild02 373 148 um_fild03 32 145 +um_fild03 19 334 um_fild02 373 329 +um_fild03 32 145 um_fild02 373 148 +um_fild03 114 50 cmd_fild01 77 366 +um_fild03 243 26 cmd_fild01 178 370 +um_fild04 182 13 um_fild02 188 374 +um_fild04 215 339 umbala 130 79 +um_fild04 215 340 umbala 129 78 +um_in 155 111 umbala 125 157 +um_in 99 111 umbala 136 127 +um_in 99 63 umbala 107 130 +um_in 141 39 umbala 94 183 +um_in 11 18 umbala 107 118 +um_in 166 69 umbala 100 203 +yggdrasil01 27 195 yggdrasil01 270 52 +yggdrasil01 270 52 yggdrasil01 27 195 +yggdrasil01 248 259 nif_fild01 316 67 +yuno 48 105 yuno_in01 40 176 +yuno 48 151 yuno_in01 34 100 +yuno 87 321 yuno_in02 172 61 +yuno 117 135 yuno_in01 116 40 +yuno 158 13 yuno_fild04 231 288 +yuno 196 138 yuno_in01 32 36 +yuno 245 147 yuno_in01 101 85 +yuno 264 87 yuno_in01 168 104 +yuno 278 293 yuno_in03 25 11 +yuno 284 366 yuno_in03 224 19 +yuno 323 284 yuno_in03 167 19 +yuno 342 203 yuno_in04 29 58 +yuno_fild01 208 18 aldebaran 140 243 +yuno_fild01 27 247 yuno_fild12 366 244 +yuno_fild01 70 377 yuno_fild09 70 21 +yuno_fild01 287 367 yuno_fild09 280 32 +yuno_fild02 19 340 yuno_fild03 382 331 +yuno_fild02 117 375 hu_fild04 122 35 +yuno_fild02 69 23 yuno_fild08 75 375 +yuno_fild02 287 16 yuno_fild08 285 385 +yuno_fild03 20 79 yuno_fild04 374 82 +yuno_fild03 20 162 yuno_fild04 374 149 +yuno_fild03 33 140 mag_dun01 125 67 +yuno_fild03 382 331 yuno_fild02 19 340 +yuno_fild03 179 16 yuno_fild07 179 352 +yuno_fild03 178 15 yuno_fild07 179 351 +yuno_fild03 21 162 yuno_fild04 371 147 +yuno_fild04 231 288 yuno 158 13 +yuno_fild04 374 82 yuno_fild03 20 79 +yuno_fild04 374 149 yuno_fild03 21 162 +yuno_fild04 42 370 yuno_fild05 61 33 +yuno_fild04 249 22 ein_fild06 255 358 +yuno_fild05 61 29 yuno_fild04 42 366 +yuno_fild07 73 220 ein_fild06 332 176 +yuno_fild07 179 353 yuno_fild03 179 17 +yuno_fild07 352 292 yuno_fild08 27 275 +yuno_fild07 360 72 yuno_fild08 35 61 +yuno_fild07 57 79 ein_fild06 352 95 +yuno_fild07 92 28 yuno_fild11 91 369 +yuno_fild07 208 175 jupe_cave 143 52 +yuno_fild07 179 354 yuno_fild03 178 15 +yuno_fild08 31 58 yuno_fild07 357 75 +yuno_fild08 24 275 yuno_fild07 348 288 +yuno_fild08 134 29 yuno_fild12 197 371 +yuno_fild08 75 375 yuno_fild02 72 28 +yuno_fild08 285 385 yuno_fild02 290 20 +yuno_fild08 375 194 yuno_fild09 23 193 +yuno_fild09 280 29 yuno_fild01 287 364 +yuno_fild09 70 18 yuno_fild01 70 374 +yuno_fild09 20 193 yuno_fild08 372 194 +yuno_fild11 368 361 yuno_fild12 27 336 +yuno_fild11 28 266 ein_fild07 378 264 +yuno_fild11 91 372 yuno_fild07 92 31 +yuno_fild11 368 361 yuno_fild12 25 338 +yuno_fild12 193 372 yuno_fild08 134 33 +yuno_fild12 23 339 yuno_fild11 364 356 +yuno_fild12 369 244 yuno_fild01 27 247 +yuno_fild12 23 338 yuno_fild11 366 361 +yuno_in01 32 36 yuno 196 138 +yuno_in01 32 182 yuno_in01 82 164 +yuno_in01 34 100 yuno 48 151 +yuno_in01 40 176 yuno 48 105 +yuno_in01 82 164 yuno_in01 32 182 +yuno_in01 88 36 yuno_in01 176 34 +yuno_in01 101 85 yuno 245 147 +yuno_in01 116 40 yuno 117 135 +yuno_in01 168 104 yuno 264 87 +yuno_in01 176 34 yuno_in01 88 36 +yuno_in02 82 14 yuno_in05 196 194 +yuno_in02 172 61 yuno 87 321 +yuno_in03 25 11 yuno 278 293 +yuno_in03 25 56 yuno_in03 32 89 +yuno_in03 32 89 yuno_in03 25 56 +yuno_in03 66 186 yuno_in03 153 134 +yuno_in03 96 62 yuno_in03 223 167 +yuno_in03 104 115 yuno_in03 219 50 +yuno_in03 111 192 yuno_in03 162 129 +yuno_in03 124 178 yuno_in03 173 118 +yuno_in03 153 134 yuno_in03 66 186 +yuno_in03 161 174 yuno_in03 186 119 +yuno_in03 161 187 yuno_in03 186 131 +yuno_in03 162 129 yuno_in03 111 192 +yuno_in03 167 19 yuno 323 284 +yuno_in03 167 72 yuno_in03 179 109 +yuno_in03 173 118 yuno_in03 124 178 +yuno_in03 179 109 yuno_in03 167 72 +yuno_in03 186 119 yuno_in03 161 174 +yuno_in03 186 131 yuno_in03 161 187 +yuno_in03 219 50 yuno_in03 104 115 +yuno_in03 223 167 yuno_in03 96 62 +yuno_in03 224 19 yuno 284 366 +yuno_in03 231 62 yuno_in03 239 141 +yuno_in03 235 91 yuno_in03 244 52 +yuno_in03 239 141 yuno_in03 231 62 +yuno_in03 244 52 yuno_in03 235 91 +yuno_in04 29 58 yuno 342 203 +yuno_in04 95 58 yuno_in04 52 58 +yuno_in04 52 58 yuno_in04 95 58 +yuno_in04 103 22 yuno_in04 115 51 +yuno_in04 103 51 yuno_in04 115 22 +yuno_in04 103 64 yuno_in04 103 93 +yuno_in04 103 93 yuno_in04 103 64 +yuno_in04 115 22 yuno_in04 115 51 +yuno_in04 115 51 yuno_in04 115 22 +yuno_in04 115 64 yuno_in04 103 93 +yuno_in04 115 93 yuno_in04 103 64 +yuno_in04 122 57 yuno_in04 161 110 +yuno_in04 161 110 yuno_in04 122 57 +yuno_in05 16 188 yuno_in05 137 71 +yuno_in05 31 167 yuno_in05 50 88 +yuno_in05 50 88 yuno_in02 82 14 +yuno_in05 137 71 yuno_in05 16 188 +yuno_in05 148 83 yuno_in05 165 103 +yuno_in05 153 142 yuno_in05 196 103 +yuno_in05 165 103 yuno_in05 148 83 +yuno_in05 177 9 yuno_in05 181 116 +yuno_in05 177 52 yuno_in05 181 91 +yuno_in05 181 91 yuno_in05 177 52 +yuno_in05 181 116 yuno_in05 177 9 +yuno_in05 196 103 yuno_in05 153 142 +yuno_in05 196 194 yuno_in02 82 14 +xmas 104 289 xmas_in 30 161 +xmas 119 162 xmas_in 38 89 +xmas 120 131 xmas_in 46 33 +xmas 142 239 xmas_in 94 83 +xmas 142 258 xmas_in 94 117 +xmas 143 314 xmas_dun01 205 13 +xmas 149 41 xmas_fild01 69 252 +xmas 149 239 xmas_in 104 83 +xmas 149 258 xmas_in 104 117 +xmas 173 160 xmas_in 163 93 +xmas 174 131 xmas_in 153 32 +xmas 181 169 xmas_in 179 109 +xmas 189 278 xmas_in 168 160 +xmas_dun01 129 130 xmas_dun02 129 130 +xmas_dun01 205 13 xmas 143 314 +xmas_dun02 129 130 xmas_dun01 129 130 +xmas_fild01 69 252 xmas 149 44 +xmas_fild01 90 252 xmas 149 44 +xmas_fild01 84 48 aldebaran 140 234 +xmas_in 30 161 xmas 104 289 +xmas_in 38 89 xmas 119 162 +xmas_in 46 33 xmas 120 131 +xmas_in 94 83 xmas 142 239 +xmas_in 94 117 xmas 142 258 +xmas_in 104 83 xmas 149 239 +xmas_in 104 117 xmas 149 258 +xmas_in 153 32 xmas 174 131 +xmas_in 163 93 xmas 173 160 +xmas_in 168 160 xmas 189 278 +xmas_in 179 109 xmas 181 169 +rachel 150 249 ra_temple 120 30 +rachel 81 33 ra_in01 237 198 +rachel 61 33 ra_in01 181 198 +rachel 217 55 ra_in01 250 34 +rachel 179 220 ra_in01 295 352 +rachel 103 240 ra_in01 343 193 +rachel 216 160 ra_in01 135 282 +rachel 42 87 ra_in01 171 359 +rachel 42 109 ra_in01 171 388 +rachel 83 78 ra_in01 249 266 +rachel 115 149 ra_in01 386 43 +rachel 90 189 ra_in01 192 148 +rachel 108 200 ra_in01 213 83 +ra_in01 188 189 ra_in01 263 144 +ra_in01 296 144 ra_in01 235 189 +ra_in01 175 189 ra_in01 132 189 +ra_in01 244 189 ra_in01 291 189 +ra_in01 235 21 ra_in01 187 31 +ra_in01 312 374 ra_in01 359 374 +ra_in01 291 371 ra_in01 225 380 +ra_in01 381 200 ra_in01 377 268 +ra_in01 367 255 ra_in01 326 296 +ra_in01 319 303 ra_in01 381 320 +ra_in01 178 370 ra_in01 113 385 +ra_in01 165 369 ra_in01 122 334 +ra_in01 241 299 ra_in01 193 312 +ra_in01 185 303 ra_in01 194 249 +ra_in01 357 61 ra_in01 310 62 +ra_in01 297 59 ra_in01 339 116 +ra_temin 28 319 ra_san01 139 139 +ra_temin 130 93 ra_temin 239 258 +ra_temin 206 93 ra_temin 312 258 +ra_temin 275 332 ra_temin 27 305 +ra_temin 275 226 ra_temsky 99 11 +ra_temsky 99 118 ra_temsky 99 140 +ra_temsky 112 143 ra_temsky 141 139 +ra_temsky 87 143 ra_temsky 58 139 +ra_temple 120 22 rachel 150 243 +ra_temple 119 180 ra_temin 169 23 +ra_in01 237 202 rachel 81 38 +ra_in01 181 202 rachel 61 38 +ra_in01 250 38 rachel 217 60 +ra_in01 295 348 rachel 179 215 +ra_in01 339 193 rachel 98 238 +ra_in01 135 286 rachel 216 164 +ra_in01 171 355 rachel 42 82 +ra_in01 172 392 rachel 42 114 +ra_in01 249 262 rachel 83 73 +ra_in01 386 39 rachel 115 144 +ra_in01 192 152 rachel 90 193 +ra_in01 213 79 rachel 108 195 +ra_in01 259 144 ra_in01 184 189 +ra_in01 231 189 ra_in01 292 144 +ra_in01 136 189 ra_in01 179 189 +ra_in01 287 189 ra_in01 240 189 +ra_in01 183 31 ra_in01 239 21 +ra_in01 355 374 ra_in01 308 374 +ra_in01 221 380 ra_in01 287 371 +ra_in01 381 268 ra_in01 387 200 +ra_in01 330 296 ra_in01 371 255 +ra_in01 381 324 ra_in01 319 299 +ra_in01 109 385 ra_in01 174 370 +ra_in01 126 334 ra_in01 169 369 +ra_in01 197 312 ra_in01 245 299 +ra_in01 194 245 ra_in01 190 302 +ra_in01 314 62 ra_in01 361 61 +ra_in01 339 120 ra_in01 297 63 +ra_temin 169 18 ra_temple 119 175 +ra_temin 239 253 ra_temin 130 88 +ra_temin 312 253 ra_temin 206 88 +ra_temin 27 299 ra_temin 275 328 +ra_temsky 99 8 ra_temin 275 231 +ra_temsky 99 135 ra_temsky 99 113 +ra_temsky 137 139 ra_temsky 109 143 +ra_temsky 62 139 ra_temsky 91 143 +rachel 25 125 ra_fild11 353 226 +rachel 275 125 ra_fild12 40 226 +rachel 130 21 ve_fild02 195 377 +que_rachel 130 93 que_rachel 239 258 +que_rachel 206 93 que_rachel 312 258 +que_rachel 275 332 que_rachel 27 305 +que_rachel 239 253 que_rachel 130 88 +que_rachel 312 253 que_rachel 206 88 +que_rachel 27 299 que_rachel 275 328 +ra_in01 309 70 rachel 108 175 +rachel 105 171 ra_in01 309 65 +ra_in01 375 109 rachel 116 154 +rachel 116 158 ra_in01 375 114 +ra_temin 275 243 ra_temin 131 131 +ra_fild01 233 333 ice_dun01 157 15 +ra_fild01 306 38 ra_fild04 322 371 +ra_fild04 322 378 ra_fild01 306 43 +ra_fild04 362 351 ra_fild05 39 353 +ra_fild05 33 353 ra_fild04 356 351 +ra_fild05 27 13 ra_fild09 30 337 +ra_fild09 29 343 ra_fild05 31 17 +ra_fild04 263 54 ra_fild08 287 365 +ra_fild08 287 370 ra_fild04 263 60 +ra_fild09 27 238 ra_fild08 360 234 +ra_fild08 368 234 ra_fild09 35 238 +ra_fild04 23 176 ra_fild03 370 172 +ra_fild03 374 168 ra_fild04 29 176 +ra_fild05 348 274 ra_fild06 24 277 +ra_fild06 19 277 ra_fild05 341 273 +ra_fild06 294 21 lhz_fild01 296 376 +lhz_fild01 296 382 ra_fild06 298 25 +ra_fild12 36 225 rachel 270 125 +ra_fild08 165 29 ra_fild12 149 369 +ra_fild12 149 374 ra_fild08 165 36 +ra_fild12 303 27 ra_fild13 295 341 +ra_fild13 295 346 ra_fild12 303 33 +ra_fild11 360 226 rachel 30 125 +ra_fild11 21 290 ra_fild10 379 283 +ra_fild10 384 287 ra_fild11 28 290 +ra_fild11 202 335 ra_fild07 215 32 +ra_fild07 215 27 ra_fild11 201 329 +ra_fild07 168 353 ra_fild02 171 45 +ra_fild02 168 36 ra_fild07 168 349 +ra_fild02 373 275 ra_fild03 28 294 +ra_fild03 23 294 ra_fild02 367 270 +ra_san02 213 280 ra_san01 140 19 +ra_san03 123 283 ra_san01 140 19 +ra_san04 119 104 ra_san01 140 19 +ra_san03 279 21 ra_san05 282 149 +ra_san03 85 15 ra_san04 203 216 +ra_san04 203 221 ra_san03 85 20 +ra_san04 120 48 ra_san05 150 282 +ra_san05 150 7 ra_temple 119 153 +ra_san02 289 149 ra_san03 10 149 +ra_san03 5 149 ra_san02 284 149 +ra_san02 213 4 ra_san04 35 216 +ra_san04 35 221 ra_san02 213 9 +ra_san02 30 21 ra_san05 14 149 +ra_san01 139 139 ra_temin 27 314 +tha_scene01 144 198 tha_t01 149 38 +tha_t01 149 33 hu_fild01 140 158 +tha_t01 150 150 tha_t02 149 130 +tha_t02 149 125 tha_t01 150 145 +tha_t03 217 165 tha_t02 227 157 +tha_t03 59 140 tha_t04 60 142 +tha_t04 60 137 tha_t03 59 135 +tha_t04 81 36 tha_t05 62 8 +tha_t05 62 157 tha_t05 213 97 +tha_t05 208 97 tha_t05 62 162 +tha_t05 185 235 tha_t06 206 11 +thana_step 174 288 tha_t08 28 43 +thana_step 173 372 tha_t07 30 166 +tha_t07 114 166 thana_step 69 287 +tha_t08 114 43 thana_step 30 223 +thana_step 32 166 tha_t09 20 96 +tha_t09 92 146 thana_step 180 223 +thana_step 182 166 tha_t10 155 100 +tha_t10 170 138 thana_step 14 73 +thana_step 15 15 tha_t11 50 17 +tha_t11 92 36 thana_step 180 73 +thana_step 181 15 tha_t12 115 16 +ve_fild03 168 240 thor_v01 21 229 +thor_v01 21 224 ve_fild03 168 235 +thor_v01 201 37 thor_v02 78 203 +thor_v02 192 60 thor_v03 35 262 +thor_v02 73 203 thor_v01 196 37 +thor_v03 30 262 thor_v02 187 58 +veins 230 166 ve_in 251 287 +ve_in 251 283 veins 230 161 +ve_in 261 315 ve_in 177 297 +ve_in 177 293 ve_in 261 311 +veins 150 179 ve_in 349 223 +ve_in 349 219 veins 150 175 +ve_in 347 250 ve_in 282 223 +ve_in 286 223 ve_in 352 250 +veins 114 278 ve_in 183 207 +ve_in 183 203 veins 110 278 +veins 128 270 ve_in 201 211 +ve_in 206 211 veins 128 266 +veins 130 238 ve_in 99 219 +ve_in 99 215 veins 127 235 +ve_in 76 229 ve_in 75 160 +ve_in 71 160 ve_in 72 229 +veins 148 220 ve_in 252 127 +ve_in 256 127 veins 150 215 +ve_in 224 115 ve_in 313 114 +ve_in 309 114 ve_in 220 115 +veins 197 260 ve_in 351 321 +ve_in 351 317 veins 197 255 +veins 88 173 ve_in 77 295 +ve_in 77 291 veins 88 168 +ve_in 98 299 ve_in 124 372 +ve_in 128 372 ve_in 102 299 +veins 337 231 ve_in 194 374 +ve_in 194 369 veins 331 231 +veins 267 230 ve_in02 17 19 +ve_in02 13 19 veins 269 225 +veins 143 25 ve_fild07 147 366 +ve_fild07 147 371 veins 146 28 +veins 218 361 ve_fild06 148 225 +ve_fild06 153 220 veins 218 355 +ve_fild01 366 267 ve_fild02 36 263 +ve_fild01 184 20 ve_fild04 174 334 +ra_fild11 233 27 ve_fild01 243 363 +ve_fild01 243 368 ra_fild11 232 32 +ve_fild01 350 92 ve_fild02 78 133 +ra_fild13 29 308 ve_fild02 380 308 +ve_fild02 385 308 ra_fild13 34 308 +ve_fild02 195 382 rachel 130 25 +ve_fild02 31 263 ve_fild01 361 267 +ve_fild02 73 133 ve_fild01 345 92 +ve_fild03 355 223 ve_fild04 49 249 +ve_fild03 222 43 ve_fild05 200 325 +ve_fild04 174 339 ve_fild01 184 25 +ve_fild04 44 249 ve_fild03 350 220 +ve_fild04 115 50 ve_fild06 80 183 +ve_fild05 200 330 ve_fild03 222 48 +ve_fild05 359 192 ve_fild06 80 183 +ve_fild06 153 220 veins 218 355 +ve_fild06 81 177 ve_fild04 115 55 +ve_fild06 81 177 ve_fild05 354 191 +ve_fild07 147 371 veins 146 28 +yuno_fild06 369 136 hu_fild04 28 126 +hu_fild04 26 126 yuno_fild06 367 136 +yuno_fild06 149 369 hu_fild01 139 37 +hu_fild01 139 35 yuno_fild06 151 369 +hu_fild04 381 187 hu_fild05 34 202 +hu_fild05 32 202 hu_fild04 379 187 +hu_fild05 91 42 hu_fild07 80 369 +hu_fild07 80 371 hu_fild05 91 44 +hu_fild07 35 351 yuno_fild02 384 338 +yuno_fild02 384 340 hu_fild07 37 351 +hu_fild07 56 36 yuno_fild09 47 375 +yuno_fild09 47 377 hu_fild07 56 38 +hu_fild07 226 36 yuno_fild09 220 372 +yuno_fild09 220 374 hu_fild07 226 38 +abbey01 51 9 nameless_n 160 184 +abbey01 87 122 abbey01 13 201 +abbey01 13 204 abbey01 88 118 +abbey01 321 102 abbey02 149 11 +abbey02 149 8 abbey01 321 99 +abbey02 149 290 abbey03 119 8 +abbey03 119 5 abbey02 149 287 +abyss_01 267 274 hu_fild05 192 207 +abyss_01 26 23 abyss_02 275 270 +abyss_02 278 270 abyss_01 26 26 +abyss_02 145 281 abyss_03 116 27 +abyss_03 116 24 abyss_02 145 278 +einbroch 92 280 airplane 224 64 +einbroch 64 234 airport 138 51 +airport 142 61 einbroch 62 246 +airport 124 13 airport 19 21 +airport 19 18 airport 122 16 +airport 161 13 airport 47 21 +airport 47 18 airport 163 16 +airport 143 14 einbroch 64 204 +einbroch 64 207 airport 143 17 +lhz_airport 125 14 lhz_airport 19 20 +lhz_airport 19 18 lhz_airport 123 14 +lhz_airport 160 14 lhz_airport 48 20 +lhz_airport 48 18 lhz_airport 162 14 +lighthalzen 267 76 lhz_airport 143 15 +lhz_airport 143 13 lighthalzen 265 76 +lhz_airport 143 63 lighthalzen 296 76 +lighthalzen 294 76 lhz_airport 143 60 +lighthalzen 308 76 airplane 224 64 +y_airport 125 14 y_airport 19 20 +y_airport 19 18 y_airport 123 14 +y_airport 160 14 y_airport 48 20 +y_airport 48 18 y_airport 162 14 +yuno 53 214 y_airport 143 23 +y_airport 143 16 yuno 52 207 +yuno 47 240 y_airport 143 54 +yuno 59 240 y_airport 143 54 +airplane 208 53 airplane 239 160 +airplane 245 160 airplane 214 54 +airplane_01 254 54 airplane_01 91 67 +airplane_01 87 67 airplane_01 250 54 +airplane_01 208 53 airplane_01 239 160 +airplane_01 245 160 airplane_01 214 54 +airplane_01 104 199 airplane_01 105 72 +airplane_01 103 72 airplane_01 102 199 +airplane_01 104 176 airplane_01 105 52 +airplane_01 103 52 airplane_01 102 176 +ein_in01 265 104 einbech 141 112 +einbech 256 109 ein_in01 148 153 +ein_in01 144 153 einbech 253 109 +einbroch 132 79 ein_in01 17 213 +ein_in01 13 213 einbroch 129 79 +ein_in01 81 198 einbroch 179 70 +einbroch 179 73 ein_in01 81 203 +einbroch 214 263 einbroch 233 315 +einbroch 233 312 einbroch 214 260 +einbroch 250 263 einbroch 269 315 +einbroch 269 312 einbroch 250 260 +einbroch 216 214 ein_in01 108 17 +ein_in01 108 13 einbroch 216 211 +einbroch 257 199 ein_in01 198 224 +ein_in01 195 224 einbroch 254 199 +ein_in01 211 216 ein_in01 274 218 +ein_in01 271 218 ein_in01 208 216 +ein_in01 211 232 ein_in01 274 232 +ein_in01 271 232 ein_in01 208 232 +ein_in01 274 246 ein_in01 273 276 +ein_in01 273 273 ein_in01 274 243 +ein_in01 264 246 ein_in01 231 276 +ein_in01 231 273 ein_in01 264 243 +ein_in01 274 203 ein_in01 274 173 +ein_in01 274 176 ein_in01 274 206 +ein_in01 264 203 ein_in01 232 173 +ein_in01 232 176 ein_in01 264 206 +ein_in01 284 224 ein_in01 177 277 +ein_in01 180 277 ein_in01 280 224 +einbroch 278 233 ein_in01 107 95 +ein_in01 103 95 einbroch 275 233 +ein_in01 126 88 ein_in01 100 139 +ein_in01 100 142 ein_in01 123 85 +einbroch 290 222 ein_in01 121 80 +ein_in01 121 77 einbroch 290 219 +einbroch 129 229 ein_in01 14 147 +ein_in01 11 147 einbroch 126 229 +ein_in01 286 25 einbroch 54 52 +einbroch 255 107 ein_in01 14 17 +ein_in01 14 14 einbroch 255 110 +ein_in01 39 36 ein_in01 35 83 +ein_in01 39 85 ein_in01 36 36 +ein_fild07 382 263 yuno_fild11 30 266 +ein_fild04 20 251 ein_fild03 361 257 +ein_fild03 363 257 ein_fild04 22 251 +ein_fild04 14 215 ein_fild03 359 219 +ein_fild03 361 219 ein_fild04 16 215 +ein_fild04 14 205 ein_fild03 359 204 +ein_fild03 361 204 ein_fild04 16 205 +ein_fild03 142 32 lhz_fild02 166 365 +lhz_fild02 166 367 ein_fild03 142 34 +lhz_fild02 164 37 lhz_fild03 158 347 +lhz_fild03 158 349 lhz_fild02 164 39 +lhz_fild03 363 283 ein_fild08 25 275 +ein_fild08 23 275 lhz_fild03 361 283 +yuno 23 127 sch_gld 260 92 +sch_gld 355 89 yuno 26 127 +sch_gld 293 91 schg_cas01 276 243 +schg_cas01 276 241 sch_gld 293 95 +sch_gld 288 256 schg_cas02 340 73 +schg_cas02 340 69 sch_gld 288 253 +sch_gld 98 182 schg_cas03 338 332 +schg_cas03 338 335 sch_gld 98 187 +sch_gld 138 96 schg_cas04 276 243 +schg_cas04 276 241 sch_gld 138 93 +sch_gld 63 315 schg_cas05 276 243 +schg_cas05 276 241 sch_gld 69 315 +hugel 95 33 hu_fild06 200 367 +hugel 155 114 hu_in01 211 177 +hu_in01 211 173 hugel 159 114 +hu_in01 162 228 hu_in01 199 219 +hu_in01 195 219 hu_in01 159 228 +hu_in01 162 168 hu_in01 200 192 +hu_in01 195 192 hu_in01 158 168 +hugel 153 153 hu_in01 287 231 +hu_in01 287 227 hugel 153 149 +hu_in01 263 252 hu_in01 360 253 +hu_in01 365 253 hu_in01 268 252 +hugel 92 167 hu_in01 246 363 +hu_in01 246 359 hugel 92 163 +hu_in01 244 315 hu_in01 243 388 +hu_in01 248 388 hu_in01 250 315 +hu_in01 229 313 hu_in01 164 315 +hu_in01 168 315 hu_in01 234 313 +hu_in01 168 307 hu_in01 167 376 +hu_in01 168 379 hu_in01 168 311 +hugel 85 181 hu_in01 231 303 +hu_in01 231 301 hugel 83 181 +hugel 70 157 hu_in01 110 373 +hu_in01 110 369 hugel 73 155 +hu_in01 108 390 hu_in01 107 322 +hu_in01 114 322 hu_in01 113 389 +hugel 91 104 hu_in01 30 304 +hu_in01 30 299 hugel 95 103 +hu_in01 34 318 hu_in01 34 251 +hu_in01 34 246 hu_in01 34 313 +hugel 104 79 hu_in01 235 107 +hu_in01 231 107 hugel 101 77 +hu_in01 268 123 hu_in01 265 36 +hu_in01 265 42 hu_in01 262 123 +hu_in01 37 90 hugel 52 95 +hu_in01 15 70 hu_in01 15 25 +hu_in01 15 29 hu_in01 15 74 +hu_in01 15 151 hu_in01 15 104 +hugel 206 228 hu_in01 381 368 +hu_in01 381 363 hugel 209 224 +hu_in01 380 319 hu_in01 382 388 +hu_in01 388 388 hu_in01 385 319 +hu_in01 177 90 hugel 52 95 +hu_in01 107 90 hugel 52 95 +hu_in01 85 108 hu_in01 85 155 +hu_in01 85 151 hu_in01 85 104 +hu_in01 85 70 hu_in01 85 25 +hu_in01 85 29 hu_in01 85 74 +hugel 129 66 hu_in01 350 107 +hu_in01 345 107 hugel 126 64 +hu_in01 381 118 hu_in01 379 173 +hu_in01 379 178 hu_in01 376 118 +hugel 153 216 hu_in01 305 379 +hu_in01 301 379 hugel 147 216 +hu_in01 314 386 hu_in01 312 322 +hu_in01 307 322 hu_in01 310 386 +hugel 80 230 hu_in01 24 363 +hu_in01 24 361 hugel 82 230 +hugel 55 208 que_bingo 49 9 +que_bingo 49 7 hugel 57 207 +que_bingo 39 34 que_bingo 34 109 +que_bingo 34 105 que_bingo 38 28 +que_bingo 53 34 que_bingo 49 71 +que_bingo 48 66 que_bingo 53 29 +hugel 209 109 odin_tem01 100 146 800 c r1 n +odin_temp01 93 146 hugel 206 109 c r1 n +ein_fild04 343 293 ein_fild05 80 294 +ein_fild05 76 294 ein_fild04 336 292 +ein_fild05 376 183 ein_fild06 47 166 +ein_fild06 42 171 ein_fild05 371 183 +ein_fild02 357 251 yuno_fild04 38 280 +yuno_fild04 33 279 ein_fild02 350 250 +ein_fild01 349 369 yuno_fild05 43 346 +yuno_fild05 41 350 ein_fild01 345 367 +ein_fild01 106 34 ein_fild02 109 358 +ein_fild02 108 364 ein_fild01 106 40 +ein_fild01 234 34 ein_fild02 258 343 +ein_fild02 257 350 ein_fild01 231 40 +ein_fild02 170 29 ein_fild05 172 366 +ein_fild05 172 371 ein_fild02 170 37 +hu_fild01 366 185 hu_fild02 22 168 +hu_fild02 17 168 hu_fild01 361 189 +hu_fild04 47 372 hu_fild02 40 27 +hu_fild02 40 22 hu_fild04 48 368 +hu_fild04 294 379 hu_fild02 280 31 +hu_fild02 283 28 hu_fild04 289 376 +hu_fild05 284 356 hu_fild03 288 26 +hu_fild03 288 19 hu_fild05 276 346 +hu_fild02 374 339 hu_fild03 24 337 +hu_fild03 20 338 hu_fild02 370 339 +hu_fild02 378 162 hu_fild03 30 163 +hu_fild03 25 163 hu_fild02 373 160 +yuno_fild09 377 184 yuno_fild10 44 183 +yuno_fild10 39 183 yuno_fild09 371 184 +ice_dun01 157 10 ra_fild01 233 327 +ice_dun01 146 161 ice_dun02 151 139 +ice_dun02 151 145 ice_dun01 146 157 +ice_dun02 150 285 ice_dun03 149 24 +ice_dun03 149 19 ice_dun02 150 280 +ice_dun04 33 140 ice_dun03 149 130 +kh_dun01 3 234 yuno_fild08 74 174 +kh_dun01 232 226 kh_dun01 13 12 +kh_dun01 63 7 kh_dun01 227 176 +kh_dun01 232 176 kh_dun01 63 12 +kh_school 76 156 yuno_fild08 155 189 +kh_school 30 125 kh_school 67 14 +kh_school 71 14 kh_school 36 125 +kh_school 35 133 kh_school 148 16 +kh_school 153 16 kh_school 39 136 +kh_school 30 155 kh_school 182 116 +kh_school 186 117 kh_school 35 155 +kh_school 35 176 kh_school 148 56 +kh_school 153 56 kh_school 40 176 +kh_school 30 185 kh_school 67 74 +kh_school 71 74 kh_school 35 184 +kh_vila 79 11 yuno_fild02 92 208 +kh_vila 71 38 kh_vila 42 38 +kh_vila 46 38 kh_vila 75 38 +kh_vila 71 54 kh_vila 42 54 +kh_vila 46 54 kh_vila 75 54 +kh_vila 34 66 kh_vila 20 108 +kh_vila 20 103 kh_vila 34 61 +kh_vila 84 66 kh_vila 44 107 +kh_vila 44 103 kh_vila 84 62 +kh_vila 32 128 kh_vila 23 171 +kh_vila 22 167 kh_vila 32 123 +kh_vila 90 47 kh_vila 119 47 +kh_vila 115 47 kh_vila 85 47 +kh_vila 126 75 kh_vila 180 176 +kh_vila 180 171 kh_vila 126 70 +kh_vila 175 71 kh_vila 136 64 +kh_vila 191 19 yuno_fild02 74 215 +lighthalzen 188 204 kh_mansion 84 49 +kh_mansion 88 50 lighthalzen 188 199 +kh_mansion 21 11 kh_mansion 72 49 +kh_rossi 15 92 yuno 270 139 +kh_rossi 35 87 kh_rossi 27 37 +kh_rossi 27 42 kh_rossi 35 91 +kh_rossi 63 87 kh_rossi 99 38 +kh_rossi 99 42 kh_rossi 63 91 +kh_rossi 90 87 kh_rossi 168 30 +kh_rossi 168 34 kh_rossi 90 90 +kh_rossi 35 98 kh_rossi 27 147 +kh_rossi 27 143 kh_rossi 35 94 +kh_rossi 90 101 kh_rossi 282 64 +kh_rossi 42 26 kh_rossi 92 25 +kh_rossi 87 25 kh_rossi 36 26 +kh_rossi 282 60 kh_rossi 90 96 +kh_rossi 222 68 kh_rossi 22 277 +kh_rossi 22 273 kh_rossi 222 64 +kh_rossi 248 68 kh_rossi 88 277 +kh_rossi 88 273 kh_rossi 248 64 +kh_rossi 222 25 kh_rossi 22 219 +kh_rossi 22 222 kh_rossi 222 29 +kh_rossi 248 25 kh_rossi 88 217 +kh_rossi 88 222 kh_rossi 248 29 +kh_rossi 259 46 kh_rossi 224 234 +kh_rossi 228 234 kh_rossi 263 46 +kh_rossi 220 231 kh_rossi 260 186 +kh_rossi 260 190 kh_rossi 220 234 +kh_rossi 204 231 kh_rossi 204 186 +kh_rossi 204 190 kh_rossi 204 234 +kh_rossi 188 231 kh_rossi 148 186 +kh_rossi 148 190 kh_rossi 188 234 +kh_rossi 188 238 kh_rossi 148 283 +kh_rossi 148 279 kh_rossi 188 235 +kh_rossi 204 238 kh_rossi 204 283 +kh_rossi 204 279 kh_rossi 204 235 +kh_rossi 220 238 kh_rossi 261 283 +kh_rossi 260 279 kh_rossi 220 235 +lhz_fild01 210 18 lighthalzen 214 324 +lighthalzen 214 329 lhz_fild01 210 23 +lhz_fild01 367 222 lhz_fild02 36 221 +lhz_fild02 29 219 lhz_fild01 361 222 +mosk_fild02 190 256 mosk_dun01 190 46 +mosk_dun01 190 43 mosk_fild02 190 253 +mosk_dun01 200 272 mosk_dun02 163 31 +mosk_dun02 165 28 mosk_dun01 200 269 +mosk_dun02 266 117 mosk_dun03 33 136 +mosk_dun03 30 133 mosk_dun02 263 118 +nameless_n 157 184 abbey01 51 12 +nameless_i 168 257 nameless_in 12 43 +nameless_in 12 40 nameless_i 168 254 +nameless_i 252 130 nameless_in 26 128 +nameless_in 23 128 nameless_i 249 130 +nameless_i 79 256 nameless_in 12 172 +nameless_in 12 169 nameless_i 82 256 +nameless_i 78 94 nameless_in 96 180 +nameless_in 96 177 nameless_i 81 96 +odin_tem01 378 182 odin_tem02 28 180 +odin_tem02 21 180 odin_tem01 373 182 +odin_tem01 383 334 odin_tem02 27 334 +odin_tem02 21 334 odin_tem01 379 334 +odin_tem02 153 349 odin_tem03 120 54 +odin_tem03 121 49 odin_tem02 154 345 +odin_tem02 261 377 odin_tem03 247 40 +odin_tem03 247 34 odin_tem02 263 372 +prontera 263 279 prt_in 227 18 +prt_in 227 15 prontera 263 275 +prt_church 115 122 prt_church 168 106 +prt_church 166 106 prt_church 112 122 +alberta 245 66 moscovia 162 56 10000 c r1 c r0 n +moscovia 166 53 alberta 243 67 0 c r0 n +alberta 189 151 izlude 176 182 500 c r1 +izlude 201 181 alberta 188 169 500 c r1 +hu_fild06 200 372 hugel 95 37 +hugel 95 33 hu_fild06 200 367 +hu_fild06 28 119 hu_fild05 347 130 +hu_fild05 353 126 hu_fild06 34 119 +airplane 254 54 airplane 91 67 +airplane 87 67 airplane 250 54 +yuno 96 261 airplane 244 58 +yuno 6 261 airplane_01 244 58 +izlude 149 39 izlude 182 56 +izlude 176 56 izlude 145 40 +moscovia 205 98 mosk_in 214 255 +moscovia 203 171 mosk_in 86 180 +mosk_in 222 128 mosk_in 288 118 +moscovia 223 174 mosk_in 14 246 +mosk_in 11 246 moscovia 222 176 +moscovia 229 208 mosk_in 142 178 +mosk_in 142 175 moscovia 227 207 +mosk_in 150 192 mosk_in 204 188 +mosk_in 202 188 mosk_in 148 192 +mosk_in 220 273 mosk_in 152 280 +mosk_in 21 113 mosk_in 99 123 +izlude 128 226 arena_room 100 30 +arena_room 99 24 izlude 128 220 +mosk_in 93 61 moscovia 255 140 +mosk_in 93 91 moscovia 224 232 +mosk_in 13 179 moscovia 188 193 +mosk_in 215 37 moscovia 194 78 +mosk_in 211 111 moscovia 253 179 +mosk_in 288 116 mosk_in 222 126 +mosk_in 214 252 moscovia 203 96 +mosk_in 152 272 mosk_in 220 270 +mosk_in 99 126 mosk_in 21 116 +moscovia 256 138 mosk_in 96 61 +moscovia 256 179 mosk_in 211 114 +mosk_in 89 180 moscovia 204 169 + +# Atelier +prontera 272 108 s_atelier 13 119 +s_atelier 10 119 prontera 268 109 +s_atelier 76 128 s_atelier 31 128 +s_atelier 31 128 s_atelier 76 125 +s_atelier 166 72 s_atelier 117 71 +s_atelier 117 71 s_atelier 171 72 +rachel 180 115 s_atelier 130 70 +s_atelier 131 75 rachel 180 118 +s_atelier 159 130 s_atelier 108 129 +s_atelier 108 129 s_atelier 159 125 +yuno 278 67 s_atelier 109 123 +s_atelier 105 123 yuno 275 67 +s_atelier 80 66 s_atelier 32 64 +s_atelier 32 64 s_atelier 80 63 +lighthalzen 41 52 s_atelier 18 75 +s_atelier 18 79 lighthalzen 41 56 + +# Einbroch tower +einbroch 218 198 einbroch 181 196 10 c c c c r0 +einbroch 173 229 einbroch 179 204 10 c c c c r0 +einbroch 176 172 einbroch 181 196 10 c c c c r0 +# 175 196 have several destinations, all in einbroch +einbroch 175 196 einbroch 216 188 0 c c r0 + +einbroch 137 199 que_ng 138 167 +que_ng 130 166 einbroch 130 197 +que_ng 178 162 que_ng 176 86 +que_ng 170 85 que_ng 182 161 +que_ng 165 137 que_ng 177 41 +que_ng 172 42 que_ng 160 139 + +# Yuno airport +y_airport 143 43 y_airport 148 51 1200 c r0 c r0 +y_airport 143 49 y_airport 142 40 0 c r0 c r0 +y_airport 140 63 yuno 47 244 0 c r0 +y_airport 145 63 yuno 59 244 0 c r0 + +# Lighthalzen airport +lhz_airport 143 43 lhz_airport 148 51 1200 c r0 c r0 +lhz_airport 143 49 lhz_airport 142 40 0 c r0 c r0 + +# Izlude airship +izlude 206 55 airplane_01 244 58 1200 c r0 c r0 + +# Rachel airship +#(portal with dialog) ra_fild12 295 208 airplane_01 245 60 1200 c r0 + +prt_fild03 371 256 prt_monk 25 248 +prt_monk 245 137 monk_in 98 183 +monk_in 98 186 prt_monk 245 139 +monk_in 99 141 monk_in 99 100 +monk_in 99 102 monk_in 99 143 +monk_in 128 84 monk_in 161 90 +monk_in 159 90 monk_in 126 84 +monk_in 69 84 monk_in 38 92 +monk_in 40 92 monk_in 71 84 +monk_in 128 46 monk_in 161 38 +monk_in 159 38 monk_in 126 46 +monk_in 69 46 monk_in 38 38 +monk_in 40 38 monk_in 71 46 +monk_in 98 27 prt_monk 245 104 +prt_monk 245 106 monk_in 98 30 +job_monk 225 180 prt_monk 194 168 0 c +monk_test 329 76 monk_test 259 118 +monk_test 259 115 monk_test 329 71 +monk_test 272 125 monk_test 301 127 +monk_test 298 127 monk_test 268 125 +monk_test 166 278 prt_monk 196 168 +monk_test 329 47 prt_monk 193 166 + +# Aldebaran - alchemist guild +aldebaran 65 53 alde_alche 20 175 +alde_alche 13 184 alde_alche 88 113 +alde_alche 88 117 alde_alche 13 181 +alde_alche 46 104 alde_alche 157 17 +alde_alche 160 17 alde_alche 50 103 +alde_alche 133 103 alde_alche 164 164 +alde_alche 158 163 alde_alche 129 103 +alde_alche 46 77 alde_alche 88 17 +alde_alche 93 17 alde_alche 50 77 +alde_alche 133 77 alde_alche 162 107 +alde_alche 158 107 alde_alche 129 77 +alde_alche 89 62 alde_alche 17 23 +alde_alche 17 29 alde_alche 89 67 +aldebaran 53 65 alde_alche 42 175 +alde_alche 42 171 aldebaran 56 68 +alde_alche 41 186 alde_alche 113 178 +alde_alche 114 183 alde_alche 42 182 + +lhz_airport 142 13 lighthalzen 262 75 + +# Lighthalzen Rekenber HQ - entrance +lighthalzen 106 248 lhz_in01 116 126 +lhz_in01 116 121 lighthalzen 106 243 + +# Lighthalzen Rekenber HQ - alchemist area entrance +lhz_in01 35 226 lhz_in01 37 225 0 n +lhz_in01 40 224 lhz_in01 19 129 +lhz_in01 15 129 lhz_in01 34 224 + +lhz_in01 191 127 lhz_in01 53 129 +lhz_in01 43 114 lhz_in01 277 130 +lhz_in01 278 132 lhz_in01 43 120 +lhz_in01 43 144 lhz_in01 278 161 +lhz_in01 278 157 lhz_in01 43 139 +lighthalzen 159 133 lhz_in02 232 265 +lhz_in02 232 261 lighthalzen 158 129 +lhz_in02 250 278 lhz_in02 265 278 +lhz_in02 261 278 lhz_in02 246 278 +lhz_in01 58 129 lhz_in01 196 127 +lighthalzen 236 276 lhz_in02 133 199 +lhz_in02 129 199 lighthalzen 232 275 +lighthalzen 198 257 lhz_in02 39 28 +lhz_in02 44 28 lighthalzen 203 257 +lighthalzen 251 299 lhz_in03 97 21 +lhz_in03 93 21 lighthalzen 247 299 + +# Rogue Guild + +cmd_fild07 193 117 in_rogue 379 46 +in_rogue 375 46 cmd_fild07 196 117 +in_rogue 375 33 in_rogue 380 125 +in_rogue 375 125 in_rogue 379 33 + +#(portal with dialog) cmd_fild09 335 143 in_rogue 169 34 0 c r0 r2 r4 r0 c +in_rogue 172 34 cmd_fild09 341 143 +# maze entrance +#in_rogue 160 34 in_rogue x y 0 c c c c c c r0 + +# maze exit +in_rogue 370 320 in_rogue 378 113 + +# Louyang Tower +louyang 133 245 lou_in01 25 19 +lou_in01 28 19 louyang 136 245 +# bug: Kore can't use this portal properly lou_in01 25 23 lou_in01 17 19 500 c c r1 c + +# Mushroom Farm +moc_ruins 141 125 job_thief1 133 333 0 c # random endpoint +job_thief1 180 15 moc_ruins 145 117 + +# Hugel - Lighthalzen Airship (needs additional configuration) +#airplane 243 73 einbroch 92 278 +#airplane 243 73 hugel 181 146 +#airplane 243 73 lighthalzen 302 75 +#airplane 243 73 yuno 92 260 +new_zone01 148 112 new_zone02 100 9 +new_zone02 126 106 new_zone02 160 171 +new_zone02 73 106 new_zone02 41 172 +pay_fild07 163 17 pay_fild03 177 275 +alde_alche 19 171 aldebaran 68 56 +airplane 243 73 lighthalzen 302 75 +airport 142 13 lighthalzen 262 75 +lighthalzen 196 46 lhz_in02 282 83 +lhz_in02 274 82 lhz_in02 270 14 +lhz_in02 288 30 lighthalzen 194 35 +lighthalzen 197 36 lhz_in02 284 30 +lhz_in02 265 14 lhz_in02 269 82 +lhz_in02 282 79 lighthalzen 194 48 + +# Eden HQ Entrace +prontera 124 76 moc_para01 31 14 c c r0 +prontera 38 212 moc_para01 31 14 c c r0 +prontera 162 326 moc_para01 31 14 c c r0 +prontera 271 213 moc_para01 31 14 c c r0 +morocc 161 97 moc_para01 31 14 c c r0 +moc_ruins 68 164 moc_para01 31 14 c c r0 +moc_prydb1 53 126 moc_para01 31 14 c c r0 +amatsu 102 152 moc_para01 31 14 c c r0 +brasilis 194 221 moc_para01 31 14 c c r0 +geffen 132 66 moc_para01 31 14 c c r0 +geffen 196 147 moc_para01 31 14 c c r0 +payon 177 111 moc_para01 31 14 c c r0 +aldebaran 133 119 moc_para01 31 14 c c r0 +einbroch 244 200 moc_para01 31 14 c c r0 +moscovia 220 192 moc_para01 31 14 c c r0 +einbroch 55 202 moc_para01 31 14 c c r0 +yuno 144 189 moc_para01 31 14 c c r0 +lighthalzen 167 102 moc_para01 31 14 c c r0 +alberta 124 67 moc_para01 31 14 c c r0 +dewata 194 168 moc_para01 31 14 c c r0 +comodo 191 153 moc_para01 31 14 c c r0 +cmd_fild07 139 134 moc_para01 31 14 c c r0 +gonryun 162 122 moc_para01 31 14 c c r0 +umbala 96 160 moc_para01 31 14 c c r0 +ayothaya 209 169 moc_para01 31 14 c c r0 +hugel 99 167 moc_para01 31 14 c c r0 +rachel 125 144 moc_para01 31 14 c c r0 +veins 214 122 moc_para01 31 14 c c r0 +alberta_in 75 39 moc_para01 31 14 c c r0 +alberta 27 238 moc_para01 31 14 c c r0 +geffen_in 160 104 moc_para01 31 14 c c r0 +payon_in02 58 58 moc_para01 31 14 c c r0 +izlude 134 97 moc_para01 31 14 c c r0 +izlude_in 68 162 moc_para01 31 14 c c r0 +dicastes01 186 206 moc_para01 31 14 c r0 n +mora 41 133 moc_para01 31 14 c r0 n +mora 108 117 moc_para01 31 14 c r0 n +eclage 116 40 moc_para01 31 14 c r0 n +harboro1 90 221 moc_para01 31 14 c r0 n +verus04 116 243 moc_para01 31 14 c r0 n + +# Eden HQ +moc_para01 47 39 moc_para01 106 14 c r0 +moc_para01 107 12 moc_para01 47 37 +moc_para01 100 27 moc_para01 47 85 +moc_para01 49 86 moc_para01 103 27 +moc_para01 113 32 moc_para01 105 92 +moc_para01 102 92 moc_para01 111 33 +moc_para01 57 27 moc_para01 162 26 +moc_para01 158 26 moc_para01 55 27 +#moc_para01 48 16 moc_para01 48 164 +moc_para01 48 16 moc_paraup 48 164 +#moc_para01 47 161 moc_para01 47 18 +moc_paraup 47 161 moc_para01 47 18 +#moc_para01 41 187 moc_para01 179 93 +moc_paraup 41 187 moc_paraup 179 93 +#moc_para01 179 90 moc_para01 41 185 +moc_paraup 179 90 moc_paraup 41 185 +moc_paraup 29 187 moc_para01 180 159 +moc_para01 180 156 moc_paraup 29 185 +moc_paraup 17 187 moc_para01 132 159 +moc_para01 132 156 moc_paraup 17 185 + +# Port Malaya +alberta 237 71 malaya 271 55 10000 c r1 n +ma_fild01 37 272 malaya 370 277 +ma_in01 105 92 malaya 309 70 +ma_in01 126 17 malaya 261 240 +ma_in01 24 77 malaya 178 211 +ma_in01 33 152 malaya 300 211 +ma_in01 86 16 malaya 112 212 +ma_in01 9 24 malaya 299 167 +malaya 112 212 ma_in01 83 16 +malaya 178 211 ma_in01 24 80 +malaya 238 206 moc_para01 31 14 0 c r0 +malaya 261 240 ma_in01 126 20 +malaya 276 55 alberta 239 68 0 c r0 n +malaya 299 167 ma_in01 12 24 +malaya 300 211 ma_in01 36 152 +malaya 309 70 ma_in01 108 92 +malaya 370 277 ma_fild01 40 272 +ma_fild02 248 33 ma_fild01 266 359 +ma_fild01 266 359 ma_fild02 248 36 +ma_scene01 140 80 ma_fild01 288 52 +ma_fild01 288 52 ma_scene01 142 78 +#Need to finish nurse quest to get access to the hospital +malaya 48 76 ma_dun01 33 110 0 r0 n +ma_dun01 35 108 malaya 58 76 0 c r0 n + +# Malangdo +mal_in01 18 161 malangdo 112 167 +malangdo 112 167 mal_in01 17 163 +mal_in01 75 174 mal_in01 18 173 +mal_in01 18 173 mal_in01 72 173 +mal_in01 124 175 mal_in01 73 170 +mal_in01 73 170 mal_in01 124 172 +mal_in01 30 119 malangdo 113 151 +malangdo 113 151 mal_in01 28 118 +mal_in01 67 118 mal_in01 20 119 +mal_in01 20 119 mal_in01 65 117 +mal_in01 38 217 malangdo 119 137 +malangdo 119 137 mal_in01 36 216 +mal_in01 75 222 mal_in01 11 218 +mal_in01 11 218 mal_in01 76 220 +mal_in01 140 213 mal_in01 79 219 +mal_in01 79 219 mal_in01 140 216 +mal_in01 18 78 malangdo 135 109 +malangdo 135 109 mal_in01 19 76 +mal_in01 67 66 mal_in01 18 67 +mal_in01 18 67 mal_in01 65 65 +mal_dun01 30 230 malangdo 73 239 +malangdo 73 239 mal_dun01 33 230 +mal_in01 163 13 malangdo 135 276 +malangdo 135 276 mal_in01 164 15 +mal_in01 9 16 malangdo 240 150 +malangdo 240 150 mal_in01 11 17 +mal_in01 67 12 mal_in01 20 15 +mal_in01 20 15 mal_in01 65 11 +mal_in02 60 61 malangdo 162 163 +malangdo 162 163 mal_in02 58 61 +mal_in02 34 60 mal_in02 51 60 +mal_in02 51 60 mal_in02 31 60 +mal_in02 64 87 mal_in02 63 66 +mal_in02 63 66 mal_in02 63 89 +mal_in02 63 35 mal_in02 63 53 +mal_in02 63 53 mal_in02 63 33 +mal_in02 102 87 mal_in02 101 66 +mal_in02 101 66 mal_in02 101 89 +mal_in02 101 35 mal_in02 101 53 +mal_in02 101 53 mal_in02 101 33 +mal_in02 140 87 mal_in02 137 66 +mal_in02 137 66 mal_in02 139 89 +mal_in02 139 35 mal_in02 137 53 +mal_in02 137 53 mal_in02 139 33 +mal_in02 159 61 mal_in02 142 60 +mal_in02 142 60 mal_in02 162 61 + +# following is a single warp, depends of entrance npc used +# currently unsupported, do not enable that +#moc_para01 30 10 prontera 116 72 + +#------------------------------ +xmas 139 307 evt_xmas 129 129 0 c r1 + +#EP-14.2 +# mora <-> bif_fild02 +mora 182 74 bif_fild02 286 327 +mora 56 25 bif_fild02 176 162 +mora 20 158 bif_fild02 99 308 +bif_fild02 285 332 mora 179 74 0 c r0 n +bif_fild02 173 162 mora 58 27 0 c r0 n +bif_fild02 95 310 mora 25 158 0 c r0 n + +# bif_fild02 <-> ecl_fild01 +bif_fild02 292 351 ecl_fild01 205 76 +ecl_fild01 207 72 bif_fild02 294 350 + +#Eclage +ecl_in01 8 67 ecl_hub01 38 94 +ecl_hub01 40 95 ecl_in01 11 67 +ecl_in01 84 68 ecl_hub01 107 107 +ecl_hub01 107 110 ecl_in01 82 68 +ecl_hub01 127 95 ecl_hub01 18 32 +ecl_hub01 18 34 ecl_hub01 125 94 +ecl_hub01 22 109 ecl_in02 99 7 +ecl_in02 98 4 ecl_hub01 23 107 +ecl_hub01 40 14 ecl_in03 144 17 +ecl_in03 144 14 ecl_hub01 40 11 +ecl_in02 80 18 ecl_in02 157 66 +ecl_in02 157 68 ecl_in02 83 18 + +# eclage <-> ecl_fild01 +eclage 98 26 ecl_fild01 99 317 +ecl_fild01 97 322 eclage 100 28 + +# ecl_fild01 <-> ecl_tdun0.* +ecl_fild01 182 82 ecl_tdun01 60 13 +ecl_tdun01 61 11 ecl_fild01 182 85 +ecl_tdun01 67 106 ecl_tdun02 60 88 +ecl_tdun02 60 90 ecl_tdun01 70 105 +ecl_tdun02 52 9 ecl_tdun03 47 13 +ecl_tdun03 49 11 ecl_tdun02 50 11 +ecl_tdun03 50 46 ecl_tdun04 26 24 +ecl_tdun04 26 26 ecl_tdun03 50 44 +ecl_tdun04 34 17 ecl_fild01 183 73 +ecl_fild01 183 70 ecl_tdun04 33 19 + +# eclage <-> ecl_in01 +eclage 299 309 ecl_in01 47 11 +ecl_in01 47 8 eclage 297 307 + +#Flame Basin +moro_vol 91 105 ecl_in01 38 96 c c r0 n +ecl_in01 38 98 moro_vol 91 102 c r0 n + +aethra 71 132 aethra_in 127 126 +aethra 90 168 aethra_in 199 176 +aethra_in 127 122 aethra 71 132 +aethra_in 199 172 aethra 90 168 + +ashroom 24 78 pallet 41 68 +ashroom 37 27 ashroom 90 63 +ashroom 81 56 pallet 24 44 +ashroom 90 66 ashroom 37 27 + +pallet 24 44 ashroom 81 58 +pallet 41 68 ashroom 22 94 +pallet 51 28 ashroom 24 78 +pallet 53 49 plt_in 35 55 + +plt_in 19 62 plt_in 114 61 +plt_in 35 52 pallet 53 49 +plt_in 111 61 plt_in 19 62 +plt_in 131 69 saladuelo 44 17 + +saladuelo 44 17 plt_in 127 68 + +caspen 96 205 ayo_in02 100 151 +caspen 104 265 casp_in01 174 190 +caspen 117 232 cmd_in02 178 132 +caspen 136 198 casp_auct02 40 36 +caspen 137 237 casp_in01 178 269 +caspen 151 198 casp_auct02 40 32 +caspen 170 236 ayo_in01 13 94 +caspen 181 228 casp_in01 58 182 +caspen 267 277 casp_dun01 28 43 +casp_in01 54 240 caspen 136 198 +casp_in01 58 179 caspen 181 228 +casp_in01 77 187 caspen 181 228 +casp_in01 82 187 casp_in01 73 188 +casp_in01 110 240 caspen 136 201 +casp_in01 159 205 caspen 104 265 +casp_in01 174 180 casp_in01 262 203 +casp_in01 178 264 caspen 137 237 +casp_in01 191 205 casp_in01 262 203 +casp_in01 231 203 casp_in01 159 205 +casp_in01 262 203 casp_in01 191 201 +casp_auct02 40 36 caspen 151 198 +casp_dun01 23 38 caspen 267 277 + +#Museum +brasilis 146 162 bra_in01 17 137 +bra_in01 17 134 brasilis 148 161 +bra_in01 50 145 bra_in01 81 143 +bra_in01 79 142 bra_in01 47 144 +bra_in01 50 171 bra_in01 82 168 +bra_in01 79 169 bra_in01 46 170 +bra_in01 138 174 bra_in01 12 183 +bra_in01 206 98 bra_in01 138 176 + +#Inn +brasilis 274 153 bra_in01 38 9 +bra_in01 39 7 brasilis 273 151 +bra_in01 52 27 bra_in01 85 26 +bra_in01 83 27 bra_in01 50 27 +bra_in01 9 67 bra_in01 154 22 +bra_in01 156 23 bra_in01 11 65 +bra_in01 52 67 bra_in01 87 66 +bra_in01 85 67 bra_in01 50 66 +bra_in01 31 29 bra_in01 23 70 +bra_in01 25 70 bra_in01 32 26 +bra_in01 31 74 bra_in01 148 59 + +#Dungeon +bra_dun01 199 35 bra_dun02 261 263 +bra_dun02 261 265 bra_dun01 199 37 + +#Dungeon Access +bra_in01 144 187 bra_in01 206 102 c r0 n +bra_in01 206 188 bra_dun01 87 47 0 n +bra_dun01 87 43 bra_in01 206 185 0 n + +# dewata +dewata 44 252 dew_fild01 373 212 +dew_fild01 375 212 dewata 46 256 +dew_fild01 57 273 dew_in01 15 33 +dew_fild01 48 65 dew_dun02 302 30 +dew_dun02 305 30 dew_fild01 50 65 + +# Lasagna +izlude 195 213 lasagna 206 297 200 c c r0 n +lasagna 205 325 izlude 193 209 200 c c r1 n +lasagna 205 325 malangdo 193 209 200 c c r2 n +lasa_fild02 348 244 lasa_dun01 24 143 +lasa_dun01 18 143 lasa_fild02 344 243 +lasa_dun_q 190 14 lasa_dun01 152 98 +lasa_dun01 157 98 lasa_dun02 22 171 +lasa_dun02 18 171 lasa_dun01 153 98 +lasa_dun02 146 58 lasa_dun03 190 18 +lasa_dun03 190 15 lasa_dun02 146 54 +lasa_fild01 134 381 lasagna 153 58 +lasagna 150 54 lasa_fild01 131 378 +lasa_fild01 341 375 lasagna 327 56 +lasagna 327 51 lasa_fild01 344 371 +lasagna 358 91 lasa_fild02 20 98 +lasa_fild02 16 98 lasagna 355 92 +lasa_in01 159 61 lasagna 68 202 +conch_in 60 61 lasagna 206 323 +conch_in 51 60 conch_in 31 60 +conch_in 34 60 conch_in 53 59 +conch_in 63 53 conch_in 63 33 +conch_in 63 35 conch_in 64 55 +conch_in 64 66 conch_in 63 89 +conch_in 63 87 conch_in 63 64 +conch_in 101 53 conch_in 101 33 +conch_in 101 35 conch_in 102 55 +conch_in 101 66 conch_in 101 89 +conch_in 102 87 conch_in 101 64 +conch_in 137 53 conch_in 139 33 +conch_in 139 35 conch_in 138 55 +conch_in 137 66 conch_in 139 89 +conch_in 140 87 conch_in 137 64 +conch_in 142 60 conch_in 162 61 +conch_in 159 61 conch_in 140 59 + +# Rock Ridge +alberta 240 103 harboro1 70 215 10000 c r0 n +harboro1 60 215 alberta 172 131 c r0 n +harboro1 241 218 har_in01 18 18 +harboro1 362 206 rockrdg1 37 246 +rockmi1 247 16 rockrdg2 304 350 +rockrdg2 304 350 rockmi1 247 19 +harboro1 312 193 har_in01 26 87 +har_in01 26 90 harboro1 312 193 +har_in01 18 15 harboro1 241 218 +har_in01 34 28 harboro1 241 218 +har_in01 99 30 har_in01 34 31 +har_in01 34 28 har_in01 99 27 +rockrdg1 33 246 harboro1 362 206 +rockrdg1 371 206 rockrdg2 31 207 +rockrdg2 27 207 rockrdg1 371 206 + +#Verus +yuno_fild07 216 157 verus04 122 217 c r0 c c r1 c c n +verus04 122 218 yuno_fild07 230 156 c r0 c r1 n +verus04 204 163 ver_tunn 13 35 +ver_tunn 10 36 verus04 202 165 +ver_eju 107 36 ver_tunn 84 82 +ver_tunn 84 86 ver_eju 113 38 +ver_eju 10 148 juperos_01 242 84 +juperos_01 245 87 ver_eju 13 148 +verus04 121 267 verus03 121 20 +verus03 122 17 verus04 121 264 +verus04 44 267 verus03 44 20 +verus03 44 17 verus04 44 264 +verus02 72 16 verus03 169 255 +verus03 169 259 verus02 72 19 +verus03 52 254 verus01 243 62 +verus01 247 58 verus03 55 251 +verus01 123 181 un_bunker 98 91 c r0 c n +un_bunker 98 85 verus01 115 190 +un_bunker 97 124 un_bunker 100 144 +un_bunker 97 141 un_bunker 98 121 +un_bunker 75 128 un_bunker 276 196 +un_bunker 275 191 un_bunker 76 121 +un_bunker 69 118 un_bunker 53 117 +un_bunker 57 118 un_bunker 72 117 +un_bunker 47 135 un_bunker 37 134 +un_bunker 40 135 un_bunker 50 134 +un_bunker 60 169 un_bunker 72 168 +un_bunker 69 167 un_bunker 57 168 +un_bunker 60 183 un_bunker 75 235 +un_bunker 71 236 un_bunker 57 182 +un_bunker 51 196 un_bunker 68 378 +un_bunker 67 375 un_bunker 52 193 +un_bunker 31 196 un_bunker 22 378 +un_bunker 21 375 un_bunker 32 193 +un_bunker 23 190 un_bunker 21 256 +un_bunker 24 256 un_bunker 26 190 +un_bunker 24 276 un_bunker 45 275 +un_bunker 41 275 un_bunker 21 276 +un_bunker 124 236 un_bunker 140 184 +un_bunker 137 185 un_bunker 120 235 +un_bunker 147 196 un_bunker 106 378 +un_bunker 105 375 un_bunker 148 193 +un_bunker 167 196 un_bunker 152 378 +un_bunker 151 375 un_bunker 168 193 +un_bunker 174 190 un_bunker 172 229 +un_bunker 169 230 un_bunker 171 189 +un_bunker 137 168 un_bunker 122 167 +un_bunker 126 168 un_bunker 140 167 +un_bunker 140 118 un_bunker 125 117 +un_bunker 128 118 un_bunker 144 117 +un_bunker 119 128 un_bunker 319 196 +un_bunker 320 191 un_bunker 120 121 +un_bunker 324 200 un_bunker 344 199 +un_bunker 339 200 un_bunker 321 199 +un_bunker 350 214 un_bunker 368 213 +un_bunker 365 214 un_bunker 347 213 +un_bunker 337 236 un_bunker 323 235 +un_bunker 326 236 un_bunker 340 235 +un_bunker 337 262 un_bunker 321 289 +un_bunker 324 290 un_bunker 340 261 +un_bunker 343 270 un_bunker 382 328 +un_bunker 381 325 un_bunker 344 267 +un_bunker 370 264 un_bunker 388 263 +un_bunker 385 264 un_bunker 367 263 +un_bunker 271 290 un_bunker 255 261 +un_bunker 258 262 un_bunker 274 289 +un_bunker 281 279 un_bunker 282 263 +un_bunker 281 266 un_bunker 282 282 +un_bunker 313 266 un_bunker 314 282 +un_bunker 313 279 un_bunker 314 262 +un_bunker 297 206 un_bunker 298 224 +un_bunker 297 221 un_bunker 298 203 +un_bunker 271 200 un_bunker 258 199 +un_bunker 245 214 un_bunker 226 213 +un_bunker 230 214 un_bunker 248 213 +un_bunker 258 236 un_bunker 272 235 +un_bunker 269 236 un_bunker 255 235 +un_bunker 249 268 un_bunker 390 380 +un_bunker 389 377 un_bunker 250 265 +un_bunker 217 262 un_bunker 213 279 +un_bunker 216 300 un_bunker 216 345 +un_bunker 213 346 un_bunker 213 299 +un_bunker 216 280 un_bunker 220 261 +un_bunker 262 200 un_bunker 274 199 +un_bunker 297 192 un_bunker 298 181 +un_bunker 297 186 un_bunker 298 196 +un_bunker 229 163 un_bunker 159 52 +un_bunker 164 51 un_bunker 233 164 +un_bunker 366 164 un_bunker 31 51 +un_bunker 27 52 un_bunker 361 163 diff --git a/openkore_llm_knowledge/tables/servers.txt b/openkore_llm_knowledge/tables/servers.txt new file mode 100644 index 0000000000..7a8eaaf60b --- /dev/null +++ b/openkore_llm_knowledge/tables/servers.txt @@ -0,0 +1,354 @@ +# This file contains information about master servers. + +# To change displayed string in server list, use "title" option. + +# Do not change server identifiers (in []), +# except for when subservers were changed or reordered +# in such a way it requires user confirmation to correctly select a subserver + +##################### +# Official RO Servers + +# https://www.gnjoy.asia +[Asia (MSP) - aRO: Baphomet] +ip 18.136.20.146 +port 6900 +master_version 15 +version 15 +serverType aRO +serverEncoding Western +addTableFolders aRO +charBlockSize 175 +charDeleteDateType 1 +blockingPlayerCancel 1 +rankingSystemType 1 +itemListType 1 +pinCode 1 + +# http://ragnarok.uol.com.br +[Brazil - bRO: Valhalla] +ip 200.229.50.36 +port 6900 +master_version 22 +version 1 +serverType bRO +secureLogin 0 +secureLogin_type 0 +secureLogin_requestCode +secureLogin_account 0 +serverEncoding Western +storageEncryptKey 0x050B6F79, 0x0202C179, 0x0E20120, 0x04FA43E3, 0x0179B6C8, 0x05973DF2, 0x07D8D6B, 0x08CB9ED9 +addTableFolders bRO +charBlockSize 147 +pinCode 1 +charDeleteDateType 1 +blockingPlayerCancel 1 +rankingSystemType 1 +itemListType 1 +ignoreAntiCheatWarning 0 + +# http://ragnarok.uol.com.br +[Brazil - bRO: Thor] +ip 200.229.50.3 +port 6900 +master_version 22 +version 1 +serverType bRO +secureLogin 0 +secureLogin_type 0 +secureLogin_requestCode +secureLogin_account 0 +serverEncoding Western +storageEncryptKey 0x050B6F79, 0x0202C179, 0x0E20120, 0x04FA43E3, 0x0179B6C8, 0x05973DF2, 0x07D8D6B, 0x08CB9ED9 +addTableFolders bRO +charBlockSize 147 +pinCode 1 +charDeleteDateType 1 +blockingPlayerCancel 1 +rankingSystemType 1 +itemListType 1 +ignoreAntiCheatWarning 0 + +# http://ro.zhaouc.com/ +[China - cRO: Prontera] +ip acc.ro.zhaouc.com +port 6900 +master_version 4 +version 1 +serverType cRO +serverEncoding GBK +charBlockSize 155 +addTableFolders cRO +gameGuard 1 +storageEncryptKey 0x050B6F79, 0x0202C179, 0x0E20120, 0x04FA43E3, 0x0179B6C8, 0x05973DF2, 0x07D8D6B, 0x08CB9ED9 +pinCode 1 +charDeleteDateType 1 +blockingPlayerCancel 1 +rankingSystemType 1 +itemListType 1 + +# https://eu.4game.com/ro/ +[European - euRO: Revo Classic] +ip 185.47.158.72 +port 6800 +version 1 +master_version 24 +serverType euRO +serverEncoding Western +charBlockSize 155 +addTableFolders euRO + +# https://ro.gnjoy.id/ +[Indonesia - idRO: Yggdrasil] +ip 202.93.26.198 +port 6900 +master_version 12 +version 2 +patchserver wpatch.ragnarok.co.id +patchpath /patch02 +serverType idRO_Renewal +secureLogin 1 +secureLogin_type 0 +secureLogin_requestCode +secureLogin_account 0 +serverEncoding Western +storageEncryptKey 0x050B6F79, 0x0202C179, 0x00E20120, 0x04FA43E3, 0x0179B6C8, 0x05973DF2, 0x007D8D6B, 0x08CB9ED9 +addTableFolders idRO +charBlockSize 155 +charDeleteDateType 1 +blockingPlayerCancel 1 +rankingSystemType 1 +ignoreAntiCheatWarning 0 + +# Renewal +# http://renewal.playragnarok.com/ +[International - iRO: Chaos/Thor/Freya] +title International - iRO: Chaos|Thor|Freya (Renewal-compatible) +ip 128.241.92.36 +port 6800 +master_version 1 +version 18 +serverType iRO_Renewal +serverEncoding Western +patchserver ropatch1.gravityus.com +patchpath /patch02 +addTableFolders iRO/Renewal;iRO +charBlockSize 155 +charDeleteDateType 1 +blockingPlayerCancel 1 +rankingSystemType 1 +itemListType 1 +itemListUseOldType 1 +ignoreAntiCheatWarning 0 + +# https://ragnarokonline.gungho.jp/ +# TODO: Breidablik, World group 1, World group 2, World group 3, World for stalls, Yggdrasill +[Japan - jRO: Urdr] +ip 18.182.57.240 +port 6900 +master_version 3 +version 10 +serverType jRO +serverEncoding Japanese +addTableFolders jRO +charBlockSize 155 +storageEncryptKey 0x050B6F79, 0x0202C179, 0x0E20120, 0x04FA43E3, 0x0179B6C8, 0x05973DF2, 0x07D8D6B, 0x08CB9ED9 + +# http://roz.gnjoy.com/ +[Korea - kRO: Zero] +ip 112.175.128.137 +port 6950 +OTP_ip 112.175.128.135 +OTP_port 6900 +master_version 0 +version 19 +secureLogin 0 +secureLogin_type 0 +secureLogin_requestCode +secureLogin_account 0 +serverType Zero +serverEncoding Korean +charBlockSize 155 +gameGuard 1 +addTableFolders kRO/Zero;kRO +pinCode 1 +storageEncryptKey 0x050B6F79, 0x0202C179, 0x0E20120, 0x04FA43E3, 0x0179B6C8, 0x05973DF2, 0x07D8D6B, 0x08CB9ED9 +sendCryptKeys 0x2FC330DD, 0x4D914DE2, 0x01C04E1F +blockingPlayerCancel 1 + +# http://ro.gnjoy.com/ +[Korea - kRO: Sara/Rangidis/Thanatos] +ip 112.175.128.137 +port 6900 +master_version 0 +version 19 +serverType kRO_RagexeRE_0 +serverEncoding Korean +charBlockSize 112 +storageEncryptKey 0x050B6F79,0x0202C179,0x00E20120,0x04FA43E3,0x0179B6C8,0x05973DF2,0x007D8D6B,0x08CB9ED9 +gameGuard 1 +addTableFolders kRO + +# http://ro.gnjoy.com/ +[Korea - kRO: Sakray] +ip 112.175.128.138 +port 6900 +master_version 2 +version 29 +secureLogin 0 +secureLogin_type 0 +secureLogin_requestCode +secureLogin_account 0 +serverType Sakray +serverEncoding Korean +charBlockSize 155 +addTableFolders kRO/Sakray;kRO +storageEncryptKey 0x050B6F79, 0x0202C179, 0x0E20120, 0x04FA43E3, 0x0179B6C8, 0x05973DF2, 0x07D8D6B, 0x08CB9ED9 +pinCode 1 +gameGuard 0 + +# https://ro.gnjoylatam.com/ +[Latam - ROla: Freya/Nidhogg/Yggdrasil] +OTP_ip lt-account-01.gnjoylatam.com +OTP_port 6951 +ip lt-account-01.gnjoylatam.com +port 6900 +master_version 1 +version 22 +serverType ROla +serverEncoding Western +storageEncryptKey 0x050B6F79, 0x0202C179, 0x0E20120, 0x04FA43E3, 0x0179B6C8, 0x05973DF2, 0x07D8D6B, 0x08CB9ED9 +addTableFolders ROla +fields_folder fields/ROla +charBlockSize 155 +gameGuard 1 +pinCode 1 +charDeleteDateType 1 +blockingPlayerCancel 1 +rankingSystemType 1 +itemListType 1 + +# https://ru.4game.com/ro/play/ +[Russia - rRO: Renewal] +ip 109.105.140.145 +port 6800 +master_version 25 +version 1 +serverType rRO +serverEncoding Russian +addTableFolders rRO +charBlockSize 145 +storageEncryptKey 0x050B6F79, 0x0202C179, 0x0E20120, 0x04FA43E3, 0x0179B6C8, 0x05973DF2, 0x07D8D6B, 0x08CB9ED9 +blockingPlayerCancel 1 + +# https://ro.gnjoy.com.tw/ +[Taiwan - twRO: Sarah, Alice, Bakili, Charles, Poli] +ip twro-acc.gnjoy.com.tw +port 6900 +master_version 5 +version 1 +serverType twRO +serverEncoding Big5 +addTableFolders twRO +charBlockSize 175 +storageEncryptKey 0x050B6F79, 0x0202C179, 0x00E20120, 0x04FA43E3, 0x0179B6C8, 0x05973DF2, 0x07D8D6B, 0x08CB9ED9 +charDeleteDateType 1 +blockingPlayerCancel 1 +rankingSystemType 1 +itemListType 1 +pinCode 1 +field_morocc morocc-old + +# http://ro.gnjoy.in.th/ +[Thailand - tRO: Chaos, Thor, Iris, Odin] +ip thro-acc1.gnjoy.in.th +port 50001 +master_version 7 +version 24 +serverType tRO +serverEncoding Thai +addTableFolders tRO +charBlockSize 175 +charDeleteDateType 1 +blockingPlayerCancel 1 +rankingSystemType 1 +itemListType 1 +pinCode 1 +ignoreAntiCheatWarning 0 + +[Localhost] +ip localhost +port 6900 +private 1 +master_version 1 +version 20 +serverType kRO_RagexeRE_2009_11_04a +serverEncoding Western +addTableFolders translated/kRO_english;kRO + +#################### +# Dead + +# http://euro-ro.net/ +[Europe - euRO] +dead 1 +dead_message euRO was closed on September 30, 2010. There used to be transfers to fRO, check out their website. + +# Re:Start +# http://playragnarokrestart.com/ +[International - iRO: Re:Start] +dead 1 +dead_message iRO Re:Start was merged into iRO Renewal on 11th July 2019. Visit official website for more details. + +# Pre-renewal +# http://playragnarok.com +[International - iRO: Loki Classic] +title International - iRO: Classic.Loki +dead 1 +dead_message iRO Classic was closed on July 11, 2019. Visit official website for more details. + +# http://www.ragnarokeurope.com/ +[France - fRO] +title France - fRO (ragnarokeurope.com): Chaos +dead 1 +dead_message fRO was closed on 12 of August, 2019. Visit official website for more details. + +# https://roextreme.com/mysg/main +[Malaysia - mRO] +dead 1 +dead_message mRO was merged into phRO on July, 2019. Visit official website for more details. + +# https://ggamerz-ro.com/ +[GGamerzRO] +dead 1 +dead_message Server is officially closed on 15/2/2021 due to no more Sponsor. + +# https://roextreme.com/msp/main +[Philippines - pRO: Valhalla] +dead 1 +dead_message pRO was closed on 07 of Octuber, 2021. Visit official website for more details. + +# https://roextreme.com/msp/main +[Philippines - pRO: Yggdrasil] +dead 1 +dead_message pRO was closed on 07 of Octuber, 2021. Visit official website for more details. + +# http://freero.online +[FreeRO] +dead 1 +dead_message FreeRO was closed on 2022. + +# http://play.ratemyserver.net +[RMS Renewal Test Server] +dead 1 +dead_message RMS Test Server was closed on 2022. + +# http://play.ratemyserver.net +[RMS Pre-Re Test Server] +dead 1 +dead_message RMS Test Server was closed on 2022. + +# http://playragnarok.com +[International - iRO: Poring] +dead 1 +dead_message iRO Transcendence was closed on 31 of March, 2022. Visit official website for more details. diff --git a/openkore_llm_knowledge/tables/skillsarea.txt b/openkore_llm_knowledge/tables/skillsarea.txt new file mode 100644 index 0000000000..e796965cbf --- /dev/null +++ b/openkore_llm_knowledge/tables/skillsarea.txt @@ -0,0 +1,104 @@ +WZ_FIREPILLAR 1 +WZ_METEOR 1 +WZ_VERMILION 1 +WZ_STORMGUST 1 +WZ_HEAVENDRIVE 1 +WZ_QUAGMIRE 1 +WZ_ICEWALL 1 +MG_SAFETYWALL 1 +MG_FIREWALL 1 +MG_THUNDERSTORM 1 +AL_PNEUMA 1 +AL_WARP 1 +PR_SANCTUARY 1 +PR_MAGNUS 1 +BS_HAMMERFALL 1 +HT_SKIDTRAP 1 +HT_LANDMINE 1 +HT_ANKLESNARE 1 +HT_SHOCKWAVE 1 +HT_SANDMAN 1 +HT_FLASHER 1 +HT_FREEZINGTRAP 1 +HT_BLASTMINE 1 +HT_CLAYMORETRAP 1 +AS_VENOMDUST 1 +SA_LANDPROTECTOR 1 +SA_VOLCANO 1 +SA_DELUGE 1 +SA_VIOLENTGALE 1 +MO_BODYRELOCATION 1 +AC_SHOWER 1 +HT_DETECTING 1 +AM_CULTIVATION 1 +CR_SLIMPITCHER 1 +CR_CULTIVATION 1 +HT_DETECTING 1 +NJ_SHADOWJUMP 1 +NJ_SUITON 1 +NJ_KAENSIN 1 +NJ_RAIGEKISAI 1 +SA_GRAVITY 1 + +MO_CHAINCOMBO 2 +MO_COMBOFINISH 2 +KN_TWOHANDQUICKEN 2 +CR_SPEARQUICKEN 2 +TK_STORMKICK 2 +TK_DOWNKICK 2 +TK_TURNKICK 2 +TK_COUNTER 2 +TK_READYSTORM 2 +TK_READYDOWN 2 +TK_READYTURN 2 +TK_READYCOUNTER 2 + +CH_PALMSTRIKE 1 +CH_TIGERFIST 2 +CH_CHAINCRUSH 2 + +PF_FOGWALL 1 +PF_SPIDERWEB 1 + +RK_SONICWAVE 1 +RK_IGNITIONBREAK 1 +RK_DRAGONBREATH 1 +RK_STORMBLAST 1 + +RA_MAGENTATRAP 1 +RA_FIRINGTRAP 1 +RA_ICEBOUNDTRAP 1 +RA_COBALTTRAP 1 +RA_MAIZETRAP 1 +RA_VERDURETRAP 1 + +WL_FROSTMISTY 1 +WL_EARTHSTRAIN 1 +WL_COMET 1 + + +SC_BODYPAINT 1 +SC_MANHOLE 1 +SC_DIMENSIONDOOR 1 +SC_CHAOSPANIC 1 +SC_MAELSTROM 1 +SC_BLOODYLUST 1 +SC_FEINTBOMB 1 + +NC_SILVERSNIPER 1 +NC_MAGICDECOY 1 + +LG_OVERBRAND 1 + +GN_THORNS_TRAP 1 +GN_BLOOD_SUCKER 1 +GN_CRAZYWEED 1 +GN_DEMONIC_FIRE 1 +GN_FIRE_EXPANSION 1 +GN_HELLS_PLANT 1 + +SO_VACUUM_EXTREME 1 +SO_PSYCHIC_WAVE 1 +SO_CLOUD_KILL 1 +SO_EARTHGRAVE 1 +SO_DIAMONDDUST 1 \ No newline at end of file diff --git a/openkore_llm_knowledge/tables/statusnametable.txt b/openkore_llm_knowledge/tables/statusnametable.txt new file mode 100644 index 0000000000..2c7e7084c3 --- /dev/null +++ b/openkore_llm_knowledge/tables/statusnametable.txt @@ -0,0 +1,1204 @@ +EFST_PROVOKE Provoke +EFST_ENDURE Endure +EFST_TWOHANDQUICKEN Two-Hand Quicken +EFST_CONCENTRATION Improve Concentration +EFST_HIDING Hiding +EFST_CLOAKING Cloaking +EFST_ENCHANTPOISON Enchant Poison +EFST_POISONREACT Poison React +EFST_QUAGMIRE Quagmire +EFST_ANGELUS Angelus +EFST_BLESSING Blessing +EFST_CRUCIS Signum Crucis +EFST_INC_AGI Increase Agility +EFST_DEC_AGI Decrease AGI +EFST_SLOWPOISON Slow Poison +EFST_IMPOSITIO Impositio Manus +EFST_SUFFRAGIUM Suffragium +EFST_ASPERSIO Aspersio +EFST_BENEDICTIO Grants armor with Holy element +EFST_KYRIE Kyrie Eleison +EFST_MAGNIFICAT Magnificat +EFST_GLORIA Gloria +EFST_LEXAETERNA Lex Aeterna +EFST_ADRENALINE Adrenaline Rush +EFST_WEAPONPERFECT Weapon Perfection +EFST_OVERTHRUST Power Thrust +EFST_MAXIMIZE Maximize Power +EFST_RIDING Riding +EFST_FALCON Falconry Mastery +EFST_TRICKDEAD Play Dead +EFST_SHOUT Crazy Uproar +EFST_ENERGYCOAT Energy Coat +EFST_BROKENARMOR Broken Armor +EFST_BROKENWEAPON Broken Weapon +EFST_ILLUSION Hallucination +EFST_WEIGHTOVER50 Weight over 70% +EFST_WEIGHTOVER90 Weight Over 90% +EFST_ATTHASTE_POTION1 Concentration Potion +EFST_ATTHASTE_POTION2 Awakening Potion +EFST_ATTHASTE_POTION3 Berserk Potion +EFST_ATTHASTE_INFINITY ASPDPOTIONINFINITY +EFST_MOVHASTE_POTION Walking Speed Up +EFST_MOVHASTE_INFINITY Speed Up +EFST_AUTOCOUNTER Counter Attack +EFST_SPLASHER Venom Splasher +EFST_ANKLESNARE Ankle Snare +EFST_POSTDELAY Action Delay +EFST_NOACTION NOACTION +EFST_IMPOSSIBLEPICKUP IMPOSSIBLEPICKUP +EFST_BARRIER Barrier +EFST_MOVHASTE_POTION Increases Movement Speed +EFST_MOVHASTE_INFINITY Increases Movement Speed +EFST_NOEQUIPWEAPON Strip Weapon +EFST_NOEQUIPSHIELD Strip Shield +EFST_NOEQUIPARMOR Strip Armor +EFST_NOEQUIPHELM Strip Helm +EFST_PROTECTWEAPON Chemical Protection Weapon +EFST_PROTECTSHIELD Chemical Protection Shield +EFST_PROTECTARMOR Chemical Protection Armor +EFST_PROTECTHELM Chemical Protection Helm +EFST_AUTOGUARD Auto Guard +EFST_REFLECTSHIELD Reflect Shield +EFST_DEVOTION Devotion +EFST_PROVIDENCE Providence +EFST_DEFENDER Defending Aura +EFST_MAGICROD Magic Rod +EFST_WEAPONPROPERTY Enchant weapon with element +EFST_AUTOSPELL Auto Spell +EFST_SPECIALZONE SPECIALZONE +EFST_MASK Orc Head +EFST_SPEARQUICKEN Spear Quicken +EFST_BDPLAYING BDPLAYING +EFST_WHISTLE Whistle +EFST_ASSASSINCROSS Assassin Cross of Sunset +EFST_POEMBRAGI Poem of Bragi +EFST_APPLEIDUN Apple of Idun +EFST_HUMMING Humming +EFST_DONTFORGETME Forget Me Not +EFST_FORTUNEKISS Fortune's Kiss +EFST_SERVICEFORYOU Service For You +EFST_RICHMANKIM Mr. Kim A Rich Man +EFST_ETERNALCHAOS Eternal Chaos +EFST_DRUMBATTLEFIELD Drum of Battlefield +EFST_RINGNIBELUNGEN The Ring of Nibelungen +EFST_ROKISWEIL Loki's Wail +EFST_INTOABYSS Into the Abyss +EFST_SIEGFRIED Invulnerable Siegfried +EFST_BLADESTOP Blade Stop +EFST_EXPLOSIONSPIRITS Critical Explosion +EFST_STEELBODY Steel Body +EFST_EXTREMITYFIST Asura Strike +EFST_COMBOATTACK Combo Attack +EFST_PROPERTYFIRE Flame Launcher +EFST_PROPERTYWATER Frost Weapon +EFST_PROPERTYWIND Lightning Loader +EFST_PROPERTYGROUND Seismic Weapon +EFST_MAGICATTACK MAGICATTACK +EFST_STOP Stop +EFST_WEAPONBRAKER Break Weapon +EFST_PROPERTYUNDEAD Undead elemental +EFST_POWERUP POWERUP +EFST_AGIUP AGIUP +EFST_SIEGEMODE Siege Mode +EFST_INVISIBLE Turn Invisible +EFST_STATUSONE STATUSONE +EFST_AURABLADE Aura Blade +EFST_PARRYING Parry +EFST_LKCONCENTRATION Concentration +EFST_TENSIONRELAX Tension Relax +EFST_BERSERK Berserk +EFST_SACRIFICE Sacrifice +EFST_GOSPEL Gospel +EFST_ASSUMPTIO Assumptio +EFST_BASILICA Basilica +EFST_GROUNDMAGIC Apply the ground magic effects +EFST_MAGICPOWER Mystical Amplification +EFST_EDP Enchant Deadly Poison +EFST_TRUESIGHT True Sight +EFST_WINDWALK Wind Walk +EFST_MELTDOWN Meltdown +EFST_CARTBOOST Cart Boost +EFST_CHASEWALK Chase Walk +EFST_SWORDREJECT Reject Sword +EFST_MARIONETTE_MASTER Marionette Control +EFST_MARIONETTE Marionette Control +EFST_MOON Moonlit Water Mill +EFST_BLOODING Bleeding +EFST_JOINTBEAT Joint Beat +EFST_MINDBREAKER Mind Breaker +EFST_MEMORIZE Memorize +EFST_FOGWALL Wall of Fog +EFST_SPIDERWEB Spider Web +EFST_PROTECTEXP Mom, Dad, I love you! +EFST_SUB_WEAPONPROPERTY Magnum Break +EFST_AUTOBERSERK Berserk +EFST_RUN Running +EFST_TING Ready Spurt +EFST_STORMKICK_ON Tornado Kick +EFST_STORMKICK_READY Whirlwind Kick +EFST_DOWNKICK_ON Heel Drop Stance +EFST_DOWNKICK_READY Axe Kick +EFST_TURNKICK_ON Roundhouse Stance +EFST_TURNKICK_READY Round Kick +EFST_COUNTER_ON Counter Kick Stance +EFST_COUNTER_READY Counter Kick +EFST_DODGE_ON Tumbling +EFST_DODGE_READY Flying Side Kick +EFST_STRUP Sprint +EFST_PROPERTYDARK Grants Shadow element +EFST_ADRENALINE2 Full Adrenaline Rush +EFST_PROPERTYTELEKINESIS Inflicts Ghost elemental damage to target +EFST_SOULLINK Soul Link Status +EFST_PLUSATTACKPOWER Increases ATK +EFST_PLUSMAGICPOWER Increases MATK +EFST_DEVIL1 Devil +EFST_KAITE Kaite +EFST_SWOO Eswoo +EFST_STAR2 STAR2 +EFST_KAIZEL Kaizel +EFST_KAAHI Kaahi +EFST_KAUPE Kaupe +EFST_SMA_READY Esma +EFST_SKE Night +EFST_ONEHANDQUICKEN One Hand Quicken +EFST_FRIEND Friend +EFST_FRIENDUP Increase Friend +EFST_SG_WARM Warmth +EFST_SG_SUN_WARM Warmth of Sun +EFST_SG_MOON_WARM Warmth of Moon +EFST_SG_STAR_WARM Warmth of Star +EFST_EMOTION Show Emotion +EFST_SUN_COMFORT Solar Protection +EFST_MOON_COMFORT Lunar Protection +EFST_STAR_COMFORT Star Comfort +EFST_EXPUP EXPUP +EFST_GDSKILL_BATTLEORDER Battle Orders +EFST_GDSKILL_REGENERATION Regeneration +EFST_GDSKILL_POSTDELAY POSTDELAY +EFST_RESISTHANDICAP RESISTHANDICAP +EFST_MAXHPPERCENT MAXHPPERCENT +EFST_MAXSPPERCENT MAXSPPERCENT +EFST_DEFENCE Defense +EFST_SLOWDOWN SLOWDOWN +EFST_PRESERVE Preserve +EFST_CHASEWALK2 Increases STR +EFST_NOT_EXTREMITYFIST Not Asura Strike +EFST_CLAIRVOYANCE Invisible Detect +EFST_MOVESLOW_POTION Walking Speed Down +EFST_DOUBLECASTING Double Casting +EFST_GRAVITATION Gravitation Field +EFST_OVERTHRUSTMAX Maximum Power Thrust +EFST_LONGING Longing for Freedom +EFST_HERMODE Wand of Hermode +EFST_TAROTCARD Tarot Card of Fate +EFST_HLIF_AVOID Emergency Avoid +EFST_HFLI_FLEET Fleet Move +EFST_HFLI_SPEED Over Speed +EFST_HLIF_CHANGE Mental Change +EFST_HAMI_BLOODLUST Blood Lust +EFST_CR_SHRINK Shrink +EFST_WZ_SIGHTBLASTER Sight Blaster +EFST_DC_WINKCHARM Wink of Charm +EFST_RG_CCONFINE_M Close Confine +EFST_RG_CCONFINE_S Close Confine +EFST_DISABLEMOVE DISABLEMOVE +EFST_GS_MADNESSCANCEL Madness Canceller +EFST_GS_GATLINGFEVER Gatling Fever +EFST_EARTHSCROLL Happy Break +EFST_NJ_UTSUSEMI Cicada Skin Shed +EFST_NJ_BUNSINJYUTSU Mirror Image +EFST_NJ_NEN Ninja Aura +EFST_GS_ADJUSTMENT Adjustment +EFST_GS_ACCURACY Increase Accuracy +EFST_NJ_SUITON Water Escape Technique +EFST_PET PET +EFST_MENTAL Ordering Mental +EFST_EXPMEMORY EXPMEMORY +EFST_PERFORMANCE PERFORMANCE +EFST_GAIN Bongun Gain or Munak Gain +EFST_GRIFFON Riding Gryphon +EFST_DRIFT Bongun Drift +EFST_WALLSHIFT Bongun Wall Shift +EFST_REINCARNATION Munak Reincarnation +EFST_PATTACK Death Passive Attack +EFST_PSPEED Death Passive Speed +EFST_PDEFENSE Death Passive Defense +EFST_PCRITICAL Death Passive Critical +EFST_RANKING Death Passive Ranking +EFST_PTRIPLE Death Passive Triple +EFST_DENERGY Death Energy +EFST_WAVE1 WAVE1 +EFST_WAVE2 WAVE2 +EFST_WAVE3 WAVE3 +EFST_WAVE4 WAVE4 +EFST_DAURA Death Aura +EFST_DFREEZER Death Freezer +EFST_DPUNISH Death Punish +EFST_DBARRIER Death Instant Barrier +EFST_DWARNING Death Warning +EFST_MOUSEWHEEL MOUSEWHEEL +EFST_DGAUGE Mighty Gauge +EFST_DACCEL Dead Acceleration +EFST_DBLOCK DBLOCK +EFST_FOOD_STR Increases STR +EFST_FOOD_AGI Increases AGI +EFST_FOOD_VIT Increases VIT +EFST_FOOD_DEX Increases DEX +EFST_FOOD_INT Increases INT +EFST_FOOD_LUK Increases LUK +EFST_FOOD_BASICAVOIDANCE Increases Flee +EFST_FOOD_BASICHIT Increases HIT +EFST_FOOD_CRITICALSUCCESSVALUE Increases Critical rate +EFST_CASH_PLUSEXP Increases amount of EXP acquired +EFST_CASH_DEATHPENALTY Disable EXP lose when character is dead +EFST_CASH_RECEIVEITEM Increases item drop rate by 100% +EFST_CASH_BOSS_ALARM Convex Mirror +EFST_DA_ENERGY Dark Energy +EFST_DA_FIRSTSLOT Dark First Fantasy +EFST_DA_HEADDEF Dark Head Defense +EFST_DA_SPACE Dark Twilight +EFST_DA_TRANSFORM Dark Transform +EFST_DA_ITEMREBUILD Dark Item Re - Build +EFST_DA_ILLUSION Dark Illusion +EFST_DA_DARKPOWER Dark Soul Power +EFST_DA_EARPLUG Dark Ear Plug +EFST_DA_CONTRACT Black Gemstone Contract +EFST_DA_BLACK Black Gemstone Magic +EFST_DA_MAGICCART Collector Magic Cart +EFST_CRYSTAL Collector Crystal +EFST_DA_REBUILD Collector Human Re - Build +EFST_DA_EDARKNESS Collector Emperium Darkness +EFST_DA_EGUARDIAN Collector Emperium Guardian +EFST_DA_TIMEOUT Collector Time Out +EFST_FOOD_STR_CASH Increases STR +EFST_FOOD_AGI_CASH Increases AGI +EFST_FOOD_VIT_CASH Increases VIT +EFST_FOOD_DEX_CASH Increases DEX +EFST_FOOD_INT_CASH Increases INT +EFST_FOOD_LUK_CASH Increases LUK +EFST_MER_FLEE Mercenary Increase Flee +EFST_MER_ATK Mercenary Increase Atk +EFST_MER_HP Mercenary Increase Hp +EFST_MER_SP Mercenary Increase Sp +EFST_MER_HIT Mercenary Increase Hit +EFST_SLOWCAST Slow Cast +EFST_MAGICMIRROR Magic Mirror +EFST_STONESKIN Stone Skin +EFST_ANTIMAGIC Anti-Magic +EFST_CRITICALWOUND Critical Wound +EFST_NPC_DEFENDER Defender +EFST_NOACTION_WAIT NOACTION WAIT +EFST_MOVHASTE_HORSE Increases movement speed +EFST_PROTECT_DEF Physical Defense Potion +EFST_PROTECT_MDEF MDEF Potion +EFST_HEALPLUS Regeneration Potion +EFST_S_LIFEPOTION Small Life Potion +EFST_L_LIFEPOTION Medium Life Potion +EFST_CRITICALPERCENT Abrasive +EFST_PLUSAVOIDVALUE Cup of Welcome +EFST_ATKER_ASPD Super Medicine +EFST_TARGET_ASPD Increase MaxSP +EFST_ATKER_MOVESPEED Revive Medicine +EFST_ATKER_BLOOD SP Consumption Decrease Potion +EFST_TARGET_BLOOD Abnormal State Resistance Potion +EFST_ARMOR_PROPERTY Change Element Scroll +EFST_REUSE_LIMIT_A REUSE LIMIT A +EFST_HELLPOWER Hell Power +EFST_STEAMPACK STEAMPACK +EFST_REUSE_LIMIT_B REUSE LIMIT B +EFST_REUSE_LIMIT_C REUSE LIMIT C +EFST_REUSE_LIMIT_D REUSE LIMIT D +EFST_REUSE_LIMIT_E REUSE LIMIT E +EFST_REUSE_LIMIT_F REUSE LIMIT F +EFST_INVINCIBLE Invincible +EFST_CASH_PLUSONLYJOBEXP Get 1.5x Job EXP when defeating monsters +EFST_PARTYFLEE Increases Flee +EFST_ANGEL_PROTECT Thank You So Much +EFST_ENDURE_MDEF Endure Mdef +EFST_ENCHANTBLADE Enchant Blade +EFST_DEATHBOUND Death Bound +EFST_REFRESH Refresh +EFST_GIANTGROWTH Giant Growth +EFST_STONEHARDSKIN Stonehard Skin +EFST_VITALITYACTIVATION Vitality Activation +EFST_FIGHTINGSPIRIT Fighting Spirit +EFST_ABUNDANCE Abundance +EFST_REUSE_MILLENNIUMSHIELD Reuse Berkana Rune +EFST_REUSE_CRUSHSTRIKE Reuse Raido Rune +EFST_REUSE_REFRESH Reuse Nauthiz Rune +EFST_REUSE_STORMBLAST Reuse Wyrd Rune +EFST_VENOMIMPRESS Venom Impress +EFST_EPICLESIS Epiclesis +EFST_ORATIO Oratio +EFST_LAUDAAGNUS Lauda Agnus +EFST_LAUDARAMUS Lauda Ramus +EFST_CLOAKINGEXCEED Cloaking Exceed +EFST_HALLUCINATIONWALK Hallucination Walk +EFST_HALLUCINATIONWALK_POSTDELAY Hallucination Walk Postdelay +EFST_RENOVATIO Renovatio +EFST_WEAPONBLOCKING Weapon Blocking +EFST_WEAPONBLOCKING_POSTDELAY Weapon Blocking Postdelay +EFST_ROLLINGCUTTER Rolling Cutter +EFST_EXPIATIO Expiatio +EFST_POISONINGWEAPON Poisoning Weapon +EFST_TOXIN Toxin +EFST_PARALYSE Paralyze +EFST_VENOMBLEED Venom Bleed +EFST_MAGICMUSHROOM Magic Mushroom +EFST_DEATHHURT Death Hurt +EFST_PYREXIA Pyrexia +EFST_OBLIVIONCURSE Oblivion Curse +EFST_LEECHESEND Leech End +EFST_DUPLELIGHT Duple Light +EFST_FROSTMISTY Freezing +EFST_FEARBREEZE Fear Breeze +EFST_ELECTRICSHOCKER Electric Shocker +EFST_MARSHOFABYSS Marsh Of Abyss +EFST_RECOGNIZEDSPELL Recognized Spell +EFST_STASIS Stasis +EFST_WUGRIDER Warg Rider +EFST_WUGDASH Warg Dash +EFST_WUGBITE Warg Bite +EFST_CAMOUFLAGE Camouflage +EFST_ACCELERATION Acceleration +EFST_HOVERING Hover +EFST_SUMMON1 SPHERE 1 +EFST_SUMMON2 SPHERE 2 +EFST_SUMMON3 SPHERE 3 +EFST_SUMMON4 SPHERE 4 +EFST_SUMMON5 SPHERE 5 +EFST_MVPCARD_TAOGUNKA Tao Gunka Scroll +EFST_MVPCARD_MISTRESS Mistress Scroll +EFST_MVPCARD_ORCHERO Orc Hero Scroll +EFST_MVPCARD_ORCLORD Orc Lord Scroll +EFST_OVERHEAT_LIMITPOINT Overheat Limitpoint +EFST_OVERHEAT Over Heat +EFST_SHAPESHIFT Shift Shape +EFST_INFRAREDSCAN Infra Red Scan +EFST_MAGNETICFIELD Magnetic Field +EFST_NEUTRALBARRIER Neutral Barrier +EFST_NEUTRALBARRIER_MASTER Neutral Barrier Master +EFST_STEALTHFIELD Stealth Field +EFST_STEALTHFIELD_MASTER Stealth Field Master +EFST_MANU_ATK Manuk's Golden Chance +EFST_MANU_DEF Manuk's Will +EFST_SPL_ATK Pinguicula's Pickled Fruit +EFST_SPL_DEF Honey Jam +EFST_REPRODUCE Reproduce +EFST_MANU_MATK Manuk's Faith +EFST_SPL_MATK Cornus's Tear +EFST_STR_SCROLL Increases STR +EFST_INT_SCROLL Increases INT +EFST_LG_REFLECTDAMAGE Reflect Damage +EFST_FORCEOFVANGUARD Vanguard Force +EFST_BUCHEDENOEL BUCHEDENOEL +EFST_AUTOSHADOWSPELL Auto Shadow Spell +EFST_SHADOWFORM Shadow Form +EFST_RAID Raid +EFST_SHIELDSPELL_DEF Shield Spell +EFST_SHIELDSPELL_MDEF Shield Spell +EFST_SHIELDSPELL_REF Shield Spell +EFST_BODYPAINT Body Painting +EFST_EXEEDBREAK Exceed Break +EFST_ADORAMUS Adoramus +EFST_PRESTIGE Prestige +EFST_INVISIBILITY Invisibility +EFST_DEADLYINFECT Deadly Infection +EFST_EARTHDRIVE Earth Drive +EFST_BANDING Banding +EFST_INSPIRATION Inspiration +EFST_ENERVATION Masquerade Enervation +EFST_GROOMY Masquerade Gloomy +EFST_RAISINGDRAGON Raising Dragon +EFST_IGNORANCE Masquerade Ignorance +EFST_LAZINESS Masquerade Laziness +EFST_LIGHTNINGWALK Lightning Walk +EFST_ACARAJE Akaraje +EFST_UNLUCKY Masquerade Unlucky +EFST_CURSEDCIRCLE_ATKER Cursed Circle +EFST_CURSEDCIRCLE_TARGET Cursed Circle Target +EFST_WEAKNESS Masquerade Weakness +EFST_CRESCENTELBOW Crescent Elbow +EFST_NOEQUIPACCESSARY Noequip Accessory +EFST_STRIPACCESSARY Strip Accessory +EFST_MANHOLE Man Hole +EFST_POPECOOKIE Pope Cookie +EFST_FALLENEMPIRE Fallen Empire +EFST_GENTLETOUCH_ENERGYGAIN Gentle Touch-Energy Gain +EFST_GENTLETOUCH_CHANGE Gentle Touch-Change +EFST_GENTLETOUCH_REVITALIZE Gentle Touch-Revitalize +EFST_BLOODYLUST Bloody Lust +EFST_SWING Swing Dance +EFST_SYMPHONY_LOVE Symphony of Lover +EFST_PROPERTYWALK Swing Dance +EFST_SPELLFIST Spell Fist +EFST_NETHERWORLD Netherworld +EFST_SIREN Voice of Siren +EFST_DEEP_SLEEP Deep Sleep +EFST_SIRCLEOFNATURE Circle of Nature +EFST_COLD Crystallization +EFST_GLOOMYDAY Gloomy Shyness +EFST_SONG_OF_MANA Song Of Mana +EFST_CLOUD_KILL Cloud Kill +EFST_DANCE_WITH_WUG Dance With Warg +EFST_RUSH_WINDMILL Rush To Windmill +EFST_ECHOSONG Echo Song +EFST_HARMONIZE Harmonize +EFST_STRIKING Striking +EFST_WARMER Warmer +EFST_MOONLIT_SERENADE Moonlight Serenade +EFST_SATURDAY_NIGHT_FEVER Madness +EFST_SITDOWN_FORCE Sit Down Force +EFST_ANALYZE Analyze +EFST_LERADS_DEW Lerad's Dew +EFST_MELODYOFSINK Sinking Melody +EFST_BEYOND_OF_WARCRY Beyond of Warcry +EFST_UNLIMITED_HUMMING_VOICE Unlimited Humming Voice +EFST_SPELLBOOK1 Spellbook 1 +EFST_SPELLBOOK2 Spellbook 2 +EFST_SPELLBOOK3 Spellbook 3 +EFST_FREEZE_SP Freeze SP +EFST_GN_TRAINING_SWORD Sword Training +EFST_GN_REMODELING_CART Cart Remodeling +EFST_GN_CARTBOOST Cart Boost +EFST_FIXEDCASTINGTM_REDUCE Fixed CastingTM Reduce +EFST_THORNS_TRAP Thorns Trap +EFST_BLOOD_SUCKER Blood Sucker +EFST_SPORE_EXPLOSION Spore Explosion +EFST_DEMONIC_FIRE Demonic Fire +EFST_FIRE_EXPANSION_SMOKE_POWDER Fire Expansion Smoke Powder +EFST_FIRE_EXPANSION_TEAR_GAS Fire Expansion Tear Gas +EFST_BLOCKING_PLAY Blocking Play +EFST_MANDRAGORA Howling of Mandragora +EFST_ACTIVATE Activate +EFST_AB_SECRAMENT Sacrament +EFST_ASSUMPTIO2 Assumptio +EFST_TK_SEVENWIND Warm Wind +EFST_LIMIT_ODINS_RECALL Odins Recall Delay +EFST_STOMACHACHE Stomach Ache +EFST_MYSTERIOUS_POWDER Mysterious Powder +EFST_MELON_BOMB Melon Bomb +EFST_BANANA_BOMB_SITDOWN_POSTDELAY Banana Bomb Sitdown Postdelay +EFST_PROMOTE_HEALTH_RESERCH HP Increase Potion +EFST_ENERGY_DRINK_RESERCH SP Increase Potion +EFST_EXTRACT_WHITE_POTION_Z Enriched White PotionZ +EFST_VITATA_500 Vitata500 +EFST_EXTRACT_SALAMINE_JUICE Enrich Celermine Juice +EFST_BOOST500 Boost 500 +EFST_FULL_SWING_K Full Swing K +EFST_MANA_PLUS Mana Plus +EFST_MUSTLE_M Mustle M +EFST_LIFE_FORCE_F Life Force F +EFST_VACUUM_EXTREME Vacuum Extreme +EFST_SAVAGE_STEAK Savage BBQ +EFST_COCKTAIL_WARG_BLOOD Warg Blood Cocktail +EFST_MINOR_BBQ Minor Brisket +EFST_SIROMA_ICE_TEA Siroma Icetea +EFST_DROCERA_HERB_STEAMED Drocera Herb Stew +EFST_PUTTI_TAILS_NOODLES Petit Tail Noodle +EFST_BANANA_BOMB Banana Bomb +EFST_SM_ELEMENTAL Sm Elemental +EFST_SPELLBOOK4 Spellbook 4 +EFST_SPELLBOOK5 Spellbook 5 +EFST_SPELLBOOK6 Spellbook 6 +EFST_SPELLBOOK7 Spellbook 7 +EFST_ELEMENTAL_AGGRESSIVE Elemental Aggressive +EFST_RETURN_TO_ELDICASTES Return To Eldicastes +EFST_BANDING_DEFENCE Banding Defence +EFST_SKELSCROLL Skel Scroll +EFST_DISTRUCTIONSCROLL Distruction Scroll +EFST_ROYALSCROLL Royal Scroll +EFST_IMMUNITYSCROLL Immunity Scroll +EFST_MYSTICSCROLL Mystic Scroll +EFST_BATTLESCROLL Battle Scroll +EFST_ARMORSCROLL Armor Scroll +EFST_FREYJASCROLL Freyja Scroll +EFST_SOULSCROLL Soul Scroll +EFST_CIRCLE_OF_FIRE Circle Of Fire +EFST_CIRCLE_OF_FIRE_OPTION Circle Of Fire Option +EFST_FIRE_CLOAK Fire Cloak +EFST_FIRE_CLOAK_OPTION Fire Cloak Option +EFST_WATER_SCREEN Water Screen +EFST_WATER_SCREEN_OPTION Water Screen Option +EFST_WATER_DROP Water Drop +EFST_WATER_DROP_OPTION Water Drop Option +EFST_WIND_STEP Wind Step +EFST_WIND_STEP_OPTION Wind Step Option +EFST_WIND_CURTAIN Wind Curtain +EFST_WIND_CURTAIN_OPTION Wind Curtain Option +EFST_WATER_BARRIER Water Barrier +EFST_ZEPHYR Zephyr +EFST_SOLID_SKIN Solid Skin +EFST_SOLID_SKIN_OPTION Solid Skin Option +EFST_STONE_SHIELD Stone Shield +EFST_STONE_SHIELD_OPTION Stone Shield Option +EFST_POWER_OF_GAIA Power Of Gaia +EFST_EL_WAIT El Wait +EFST_EL_PASSIVE El Passive +EFST_EL_DEFENSIVE El Defensive +EFST_EL_OFFENSIVE El Offensive +EFST_EL_COST El Cost +EFST_PYROTECHNIC Pyrotechnic +EFST_PYROTECHNIC_OPTION Pyrotechnic Option +EFST_HEATER Heater +EFST_HEATER_OPTION Heater Option +EFST_TROPIC Tropic +EFST_TROPIC_OPTION Tropic Option +EFST_AQUAPLAY Aquaplay +EFST_AQUAPLAY_OPTION Aquaplay Option +EFST_COOLER Cooler +EFST_COOLER_OPTION Cooler Option +EFST_CHILLY_AIR Chilly Air +EFST_CHILLY_AIR_OPTION Chilly Air Option +EFST_GUST Gust +EFST_GUST_OPTION Gust Option +EFST_BLAST Blast +EFST_BLAST_OPTION Blast Option +EFST_WILD_STORM Wild Storm +EFST_WILD_STORM_OPTION Wild Storm Option +EFST_PETROLOGY Petrology +EFST_PETROLOGY_OPTION Petrology Option +EFST_CURSED_SOIL Cursed Soil +EFST_CURSED_SOIL_OPTION Cursed Soil Option +EFST_UPHEAVAL Upheaval +EFST_UPHEAVAL_OPTION Upheaval Option +EFST_TIDAL_WEAPON Tidal Weapon +EFST_TIDAL_WEAPON_OPTION Tidal Weapon Option +EFST_ROCK_CRUSHER Rock Crusher +EFST_ROCK_CRUSHER_ATK Rock Crusher Atk +EFST_FIRE_INSIGNIA Fire Insignia +EFST_WATER_INSIGNIA Water Insignia +EFST_WIND_INSIGNIA Wind Insignia +EFST_EARTH_INSIGNIA Earth Insignia +EFST_EQUIPED_FLOOR Equiped Floor +EFST_GUARDIAN_RECALL Guardian Recall +EFST_MORA_BUFF Mora Berry +EFST_REUSE_LIMIT_G Reuse Limit G +EFST_REUSE_LIMIT_H Reuse Limit H +EFST_NEEDLE_OF_PARALYZE Needle Of Paralyze +EFST_PAIN_KILLER Pain Killer +EFST_G_LIFEPOTION Life Potion +EFST_VITALIZE_POTION Vitalize Potion +EFST_LIGHT_OF_REGENE Light of Regen +EFST_OVERED_BOOST Overdose Boost +EFST_SILENT_BREEZE Silent Breeze +EFST_ODINS_POWER Odin's Power +EFST_STYLE_CHANGE Fighter Mode +EFST_SONIC_CLAW_POSTDELAY Sonic Claw Postdelay +EFST_SILVERVEIN_RUSH_POSTDELAY Silvervein Rush Postdelay +EFST_MIDNIGHT_FRENZY_POSTDELAY Midnight Frenzy Postdelay +EFST_GOLDENE_FERSE Goldene Ferse +EFST_ANGRIFFS_MODUS Angriffs Modus +EFST_TINDER_BREAKER Tinder Breaker +EFST_TINDER_BREAKER_POSTDELAY Tinder Breaker Postdelay +EFST_CBC Cbc +EFST_CBC_POSTDELAY Cbc Postdelay +EFST_EQC Eqc +EFST_MAGMA_FLOW Magma Flow +EFST_GRANITIC_ARMOR Granatic Armor +EFST_PYROCLASTIC Pyroclastic +EFST_VOLCANIC_ASH Volcanic Ash +EFST_SPIRITS_SAVEINFO1 Spirits Save Info 1 +EFST_SPIRITS_SAVEINFO2 Spirits Save Info 2 +EFST_MAGIC_CANDY Magic Candy +EFST_SEARCH_STORE_INFO Search Store Info +EFST_ALL_RIDING Riding +EFST_ALL_RIDING_REUSE_LIMIT All Riding Reuse Limit +EFST_MACRO Macro +EFST_MACRO_POSTDELAY After Macro Delay +EFST_BEER_BOTTLE_CAP Beer Bottle Cap +EFST_OVERLAPEXPUP Malangdo Cat Can +EFST_PC_IZ_DUN05 Pc Iz Dun05 +EFST_CRUSHSTRIKE Crush Strike +EFST_MONSTER_TRANSFORM Monster Transformation +EFST_SIT Sitting + +EFST_ONAIR Onair +EFST_MTF_ASPD Mtf Aspd +EFST_MTF_RANGEATK Mtf Rangeatk +EFST_MTF_MATK Mtf Matk +EFST_MTF_MLEATKED Mtf Mleatked +EFST_MTF_CRIDAMAGE Mtf Cridamage +EFST_REUSE_LIMIT_MTF Reuse Limit Mtf +EFST_MACRO_PERMIT Using Macros +EFST_MACRO_PLAY Macro Play +EFST_SKF_CAST Decreases Variable Cast Time +EFST_SKF_ASPD Increases ASPD +EFST_SKF_ATK Increases ATK +EFST_SKF_MATK Increases MATK +EFST_REWARD_PLUSONLYJOBEXP Gains extra JOB Exp +EFST_HANDICAPSTATE_NORECOVER No Recover State +EFST_SET_NUM_DEF DEF is adjusted to a specific value +EFST_SET_NUM_MDEF MDEF is adjusted to a specific value +EFST_SET_PER_DEF DEF is adjusted to a specific percentage +EFST_SET_PER_MDEF MDEF is adjusted to a specific percentage +EFST_PARTYBOOKING_SEARCH_DELAY Party Booking Search Delay +EFST_PARTYBOOKING_REGISTER_DELAY Party Booking Register Delay +EFST_PERIOD_TIME_CHECK_DETECT_SKILL Period Time Check Detect Skill +EFST_KO_JYUMONJIKIRI Ko Jyumonjikiri +EFST_MEIKYOUSISUI Sealed Mirror +EFST_ATTHASTE_CASH ASPD Reinforce Potion +EFST_EQUIPPED_DIVINE_ARMOR Equipped Divine Armor +EFST_EQUIPPED_HOLY_ARMOR Equipped Holy Armor +EFST_2011RWC Firecracker +EFST_KYOUGAKU Kyougaku +EFST_IZAYOI The 16th Night +EFST_ZENKAI Zenkai +EFST_KG_KAGEHUMI Shadow Trampling +EFST_KYOMU Empty Shadow +EFST_KAGEMUSYA Shadow Warrior +EFST_ZANGETSU Distorted Crescent +EFST_PHI_DEMON Ancient Spirit of Daebujeok +EFST_GENSOU Moonlight Fantasy +EFST_AKAITSUKI Omnius Moonlight +EFST_TETANY Tetany +EFST_GM_BATTLE Combat drug +EFST_GM_BATTLE2 Advanced Combat Drug +EFST_2011RWC_SCROLL Red Booster +EFST_ACTIVE_MONSTER_TRANSFORM Active Monster Transform +EFST_MYSTICPOWDER Mystic Powder +EFST_ECLAGE_RECALL Eclage Recall +EFST_ENTRY_QUEUE_APPLY_DELAY Entry Queue Apply Delay +EFST_REUSE_LIMIT_ECL Reuse Limit Eclage +EFST_M_LIFEPOTION Mysterious water of life +EFST_ENTRY_QUEUE_NOTIFY_ADMISSION_TIME_OUT Entry Queue Notify Admission Time Out +EFST_UNKNOWN_NAME Unknown Name +EFST_ON_PUSH_CART On Push Cart +EFST_HAT_EFFECT Hat Effect +EFST_FLOWER_LEAF Rich Flower Leaf +EFST_RAY_OF_PROTECTION Ray of Protection +EFST_GLASTHEIM_ATK You feels powerful force within your body. +EFST_GLASTHEIM_DEF You feels protected by magic shield +EFST_GLASTHEIM_HEAL You feels an increased efficiency power of healing +EFST_GLASTHEIM_HIDDEN You feels countless magical defense energy. +EFST_GLASTHEIM_STATE You feels a sudden increases of all stats +EFST_GLASTHEIM_ITEMDEF You feels a sudden increases of defense +EFST_GLASTHEIM_HPSP You feels a sudden increases of MaxHP and MaxSP +EFST_FOLLOWER_NPC_SKILL_POSTDELAY Follower Npc Skill Postdelay +EFST_ALMIGHTY Almighty + +EFST_GVG_GIANT The blood of Frozen Giant +EFST_GVG_GOLEM Core of Golem +EFST_GVG_STUN Elven tears(Stun) +EFST_GVG_STONE Elven tears(Stone Curse) +EFST_GVG_FREEZ Elven tears(Frozen) +EFST_GVG_SLEEP Elven tears(Sleep) +EFST_GVG_CURSE Elven tears(Curse) +EFST_GVG_SILENCE Elven tears(Silence) +EFST_GVG_BLIND Elven tears(Blind) +EFST_CLIENT_ONLY_EQUIP_ARROW Client Only Equip Arrow +EFST_CLAN_INFO Clan Info +EFST_JP_EVENT01 Increases physical damage against Fish monster +EFST_JP_EVENT02 Increases magical damage against Fish monster +EFST_JP_EVENT03 Reduces damage taken from Fish monster +EFST_JP_EVENT04 Increases exp gained +EFST_TELEPORT_FIXEDCASTINGDELAY Teleport Fixed Casting Delay +EFST_GEFFEN_MAGIC1 Increases physical damage +EFST_GEFFEN_MAGIC2 Increases magical damage +EFST_GEFFEN_MAGIC3 Reduces damage taken +EFST_QUEST_BUFF1 Energy storage +EFST_QUEST_BUFF2 Energy Storage +EFST_QUEST_BUFF3 Energy Storage +EFST_HANDICAPSTATE_ICEEXPLO Ice explotion +EFST_REUSE_LIMIT_RECALL Reuse Limit Recall +EFST_SAVEPOSITION Save Position +EFST_NPC_ICEEXPLO NPC Ice Explosion +EFST_FENRIR_CARD Power of Fenrir +EFST_REUSE_LIMIT_ASPD_POTION Reuse Limit Aspd Potion +EFST_MAXPAIN Maxpain +EFST_PC_STOP Pc Stop +EFST_FRIGG_SONG Frigg's Song +EFST_OFFERTORIUM Offertorium +EFST_TELEKINESIS_INTENSE Intense Telekinesis +EFST_MOONSTAR Moonstar +EFST_STRANGELIGHTS Strangelights +EFST_FULL_THROTTLE Full Throttle +EFST_REBOUND Rebound +EFST_UNLIMIT Unlimit +EFST_KINGS_GRACE King's Grace +EFST_ITEM_ATKMAX MAX ATK +EFST_ITEM_ATKMIN MIN ATK +EFST_ITEM_MATKMAX MAX MATK +EFST_ITEM_MATKMIN MIN MATK +EFST_SUPER_STAR Super Star +EFST_HIGH_RANKER High Ranker +EFST_DARKCROW Dark Claw +EFST_2013_VALENTINE1 Increases LUK +EFST_2013_VALENTINE2 Increases ATK, MATK +EFST_2013_VALENTINE3 Improve HP and SP recovery +EFST_ILLUSIONDOPING Illusiondoping +EFST_WIDEWEB Wideweb +EFST_CHILL Chill +EFST_BURNT Burn +EFST_PCCAFE_PLAY_TIME Pccafe Play Time +EFST_TWISTED_TIME Twisted Time +EFST_FLASHCOMBO Flashcombo +EFST_JITTER_BUFF1 New Oz's Finesse +EFST_JITTER_BUFF2 Aigu's Willpower +EFST_JITTER_BUFF3 Rohtert's Bluff +EFST_JITTER_BUFF4 Gelkah's Shyness +EFST_JITTER_BUFF5 Arang's Naivety +EFST_JITTER_BUFF6 Mingmin's Detailedness +EFST_JITTER_BUFF7 Ro.L's Seriousness +EFST_JITTER_BUFF8 Lunain's Attentiveness +EFST_JITTER_BUFF9 Ragi's Cheesiness +EFST_JITTER_BUFF10 Melody Jack's Requireness +EFST_CUP_OF_BOZA Let's Cup +EFST_B_TRAP Bound Trap +EFST_E_CHAIN Eternal Chain +EFST_E_QD_SHOT_READY E Qd Shot Ready +EFST_C_MARKER Crimson Marker +EFST_H_MINE Howling Mine +EFST_H_MINE_SPLASH H Mine Splash +EFST_P_ALTER Platinum Alter +EFST_HEAT_BARREL Heat Barrel +EFST_ANTI_M_BLAST Anti-Material Blast +EFST_SLUGSHOT Slugshot +EFST_SWORDCLAN Sword Clan +EFST_ARCWANDCLAN Arc Wand Clan +EFST_GOLDENMACECLAN Golden Mace Clan +EFST_CROSSBOWCLAN Crossbow Clan +EFST_PACKING_ENVELOPE1 Powerful 30 seconds +EFST_PACKING_ENVELOPE2 Smart 30 seconds +EFST_PACKING_ENVELOPE3 Stamina 30 Seconds +EFST_PACKING_ENVELOPE4 Horse Power 30 Seconds +EFST_PACKING_ENVELOPE5 Light 30 Seconds +EFST_PACKING_ENVELOPE6 Faster 30 Seconds +EFST_PACKING_ENVELOPE7 Strong 30 Seconds +EFST_PACKING_ENVELOPE8 Safe 30 Seconds +EFST_PACKING_ENVELOPE9 Good Luck 30 Seconds +EFST_PACKING_ENVELOPE10 Suits 30 Seconds +EFST_GLASTHEIM_TRANS Border of the living and the dead! +EFST_ZONGZI_POUCH_TRANS Duan Festival +EFST_HEAT_BARREL_AFTER Aftermath +EFST_DECORATION_OF_MUSIC Decoration Of Music +EFST_OVERSEAEXPUP Increases experience acquired +EFST_CLOWN_N_GYPSY_CARD Clown N Gypsy Card +EFST_OPEN_NPC_MARKET Open Npc Market +EFST_BEEF_RIB_STEW Beef Rib Stew +EFST_PORK_RIB_STEW Pork Rib Stew +EFST_CHUSEOK_MONDAY Enhanced Element +EFST_CHUSEOK_TUESDAY Enhanced Element +EFST_CHUSEOK_WEDNESDAY Enhanced Element +EFST_CHUSEOK_THURSDAY Enhanced Element +EFST_CHUSEOK_FRIDAY Enhanced Element +EFST_CHUSEOK_WEEKEND Enhanced Element +EFST_ALL_LIGHTGUARD All Lightguard +EFST_ALL_LIGHTGUARD_COOL_TIME All Lightguard Cool Time +EFST_MTF_MHP Mtf Mhp +EFST_MTF_MSP Mtf Msp +EFST_MTF_PUMPKIN Mtf Pumpkin +EFST_MTF_HITFLEE Mtf Hit Flee +EFST_MTF_CRIDAMAGE2 Mtf Critical Damage 2 +EFST_MTF_SPDRAIN Mtf Spdrain +EFST_ACUO_MINT_GUM ACUO Gum +EFST_S_HEALPOTION Small Heal Potion +EFST_REUSE_LIMIT_S_HEAL_POTION Reuse Limit S Heal Potion +EFST_PLAYTIME_STATISTICS Playtime Statistics +EFST_GN_CHANGEMATERIAL_OPERATOR Gn Change Material Operator +EFST_GN_MIX_COOKING_OPERATOR Gn Mix Cooking Operator +EFST_GN_MAKEBOMB_OPERATOR Gn Makebomb Operator +EFST_GN_S_PHARMACY_OPERATOR Gn S Pharmacy Operator +EFST_SO_EL_ANALYSIS_DISASSEMBLY_OPERATOR So El Analysis Disassembly Operator +EFST_SO_EL_ANALYSIS_COMBINATION_OPERATOR So El Analysis Combination Operator +EFST_NC_MAGICDECOY_OPERATOR Nc Magicdecoy Operator +EFST_GUILD_STORAGE Guild Storage +EFST_GC_POISONINGWEAPON_OPERATOR Gc Poisoning Weapon Operator +EFST_WS_WEAPONREFINE_OPERATOR Whitesmith Weapon Refine Operator +EFST_BS_REPAIRWEAPON_OPERATOR Blacksmith Repair Weapon Operator +EFST_UNREADMAIL_CHECK Unreadmail Check +EFST_JUMPINGCLAN Jumping Clan +EFST_JP_OTP OTP Subscriber Benefit +EFST_HANDICAPTOLERANCE_LEVELGAP Reduce state resistance level penalty +EFST_MTF_RANGEATK2 Mtf Rangeatk2 +EFST_MTF_ASPD2 Mtf Aspd2 +EFST_MTF_MATK2 Mtf Matk2 +EFST_SHOW_NPCHPBAR Show NPC HP Bar +EFST_FLOWERSMOKE Flowersmoke +EFST_FSTONE Fstone +EFST_DAILYSENDMAILCNT Daily Send Mail +EFST_QSCARABA Qscaraba +EFST_LJOSALFAR Ljosalfar +EFST_PAD_READER_KNIGHT Knight +EFST_PAD_READER_CRUSADER Crusader +EFST_PAD_READER_BLACKSMITH Blacksmith +EFST_PAD_READER_ALCHEMIST Alchemist +EFST_PAD_READER_ASSASSIN Assassin +EFST_PAD_READER_ROGUE Rogue +EFST_PAD_READER_WIZARD Wizard +EFST_PAD_READER_SAGE Sage +EFST_PAD_READER_PRIEST Priest +EFST_PAD_READER_MONK Monk +EFST_PAD_READER_HUNTER Hunter +EFST_PAD_READER_BARD Bard +EFST_PAD_READER_DANCER Dancer +EFST_PAD_READER_TAEKWON Taekwon +EFST_PAD_READER_NINJA Ninja +EFST_PAD_READER_GUNSLINGER Gunsliger +EFST_PAD_READER_SUPERNOVICE Super Novice +EFST_ESSENCE_OF_TIME Essence of Time +EFST_MINIGAME_ROULETTE Minigame Roulette +EFST_MINIGAME_GOLD_POINT Minigame Gold Point +EFST_MINIGAME_SILVER_POINT Minigame Silver Point +EFST_MINIGAME_BRONZE_POINT Minigame Bronze Point +EFST_HAPPINESS_STAR Happiness Star +EFST_SUMMEREVENT01 Summer Event 01 +EFST_SUMMEREVENT02 Summer Event 02 +EFST_SUMMEREVENT03 Summer Event 03 +EFST_SUMMEREVENT04 Summer Event 04 +EFST_SUMMEREVENT05 Summer Event 05 +EFST_MINIGAME_ROULETTE_BONUS_ITEM Minigame Roulette Bonus Item +EFST_DRESS_UP Dress Up +EFST_MAPLE_FALLS Maple Falls +EFST_ALL_NIFLHEIM_RECALL All Niflheim Recall +EFST_MTF_MARIONETTE Marionette transformation +EFST_MTF_LUDE Lude transformation +EFST_MTF_CRUISER Cruiser transformation +EFST_MERMAID_LONGING Mermaid Longing +EFST_MAGICAL_FEATHER Magical Feather +EFST_DRACULA_CARD Sealed Berzebub +EFST_LIMIT_POWER_BOOSTER Limited Power Booster +EFST_GIFT_OF_SNOW Gift Of Snow +EFST_TIME_ACCESSORY Time Accessory +EFST_EP16_DEF Shining Holy Water +EFST_NORMAL_ATKED_SP Normal Atked Sp +EFST_BODYSTATE_STONECURSE Stone Curse +EFST_BODYSTATE_FREEZING Frozen +EFST_BODYSTATE_STUN Stun +EFST_BODYSTATE_SLEEP Sleep +EFST_BODYSTATE_UNDEAD Undead +EFST_BODYSTATE_STONECURSE_ING Stone Curse progress +EFST_BODYSTATE_BURNNING Burning +EFST_BODYSTATE_IMPRISON Imprison +EFST_HEALTHSTATE_POISON Poison +EFST_HEALTHSTATE_CURSE Curse +EFST_HEALTHSTATE_SILENCE Silence +EFST_HEALTHSTATE_CONFUSION Chaos +EFST_HEALTHSTATE_BLIND Blind +EFST_HEALTHSTATE_ANGELUS Angelus +EFST_HEALTHSTATE_BLOODING Healthstate Blooding +EFST_HEALTHSTATE_HEAVYPOISON Deadly Poison +EFST_HEALTHSTATE_FEAR Fear +EFST_CHERRY_BLOSSOM_CAKE Cherry Blossom Cake +EFST_SU_STOOP Stoop +EFST_CATNIPPOWDER Catnip Powder +EFST_HEAD_EQUIPMENT_EFFECT Head Equipment Effect +EFST_SV_ROOTTWIST Root Twist +EFST_ATTACK_PROPERTY_NOTHING Grants weapon with Neutral element +EFST_ATTACK_PROPERTY_WATER Grants weapon with Water element +EFST_ATTACK_PROPERTY_GROUND Grants weapon with Earth element +EFST_ATTACK_PROPERTY_FIRE Grants weapon with Fire element +EFST_ATTACK_PROPERTY_WIND Grants weapon with Wind element +EFST_ATTACK_PROPERTY_POISON Grants weapon with Poison element +EFST_ATTACK_PROPERTY_SAINT Grants weapon with Holy element +EFST_ATTACK_PROPERTY_DARKNESS Grants weapon with Shadow element +EFST_ATTACK_PROPERTY_TELEKINESIS Grants weapon with Ghost element +EFST_ATTACK_PROPERTY_UNDEAD Grants weapon with Undead element +EFST_RESIST_PROPERTY_NOTHING Increases Neutral resistance +EFST_RESIST_PROPERTY_WATER Increases Water resistance +EFST_RESIST_PROPERTY_GROUND Increases Earth resistance +EFST_RESIST_PROPERTY_FIRE Increases Fire resistance +EFST_RESIST_PROPERTY_WIND Increases Wind resistance +EFST_RESIST_PROPERTY_POISON Increases Poison resistance +EFST_RESIST_PROPERTY_SAINT Increases Holy resistance +EFST_RESIST_PROPERTY_DARKNESS Increases Shadow resistance +EFST_RESIST_PROPERTY_TELEKINESIS Increases Ghost resistance +EFST_RESIST_PROPERTY_UNDEAD Increases Undead resistance +EFST_BITESCAR Bite +EFST_ARCLOUSEDASH Arclouse Dash +EFST_TUNAPARTY Tuna Party +EFST_SHRIMP Shrimp +EFST_FRESHSHRIMP Fresh Shrimp +EFST_PERIOD_RECEIVEITEM Increase drop rate of items by 2x +EFST_PERIOD_PLUSEXP Obtain more Base Exp when killing a monster +EFST_PERIOD_PLUSJOBEXP Obtain more Job Exp when killing a monster +EFST_RUNEHELM Rune Helm +EFST_HELM_VERKANA Verkana Runestone +EFST_HELM_RHYDO Rhydo Runestone +EFST_HELM_TURISUS Turisus Runestone +EFST_HELM_HAGALAS Hagalas Runestone +EFST_HELM_ISIA Isia Runestone +EFST_HELM_ASIR Asir Runestone +EFST_HELM_URJ Ur's Runestone +EFST_SUHIDE Hide +EFST_DORAM_BUF_01 Kalugna Milk +EFST_DORAM_BUF_02 Basil +EFST_SPRITEMABLE Spirit Marble +EFST_AID_PERIOD_RECEIVEITEM Improved drop rate +EFST_AID_PERIOD_PLUSEXP Improved experience +EFST_AID_PERIOD_PLUSJOBEXP Improved job experience +EFST_AID_PERIOD_DEADPENALTY Disable death penalty +EFST_AID_PERIOD_ADDSTOREITEMCOUNT Storage expansion +EFST_MAGICSTONE_OF_GRACE_SET Magicstone Of Grace Set +EFST_HISS Hiss +EFST_NYANGGRASS Nyan Grass +EFST_CHATTERING Chattering +EFST_GROOMING Grooming +EFST_PROTECTIONOFSHRIMP Protection Of Shrimp +EFST_EP16_2_BUFF_SS Enhanced Potion SS +EFST_EP16_2_BUFF_SC Enhanced Potion SC +EFST_EP16_2_BUFF_AC Enhanced Potion AC +EFST_GS_MAGICAL_BULLET Magical Bullet +EFST_FALLEN_ANGEL Fallen Angel +EFST_BLAZE_BEAD EFST_BLAZE_BEAD +EFST_FROZEN_BEAD EFST_FROZEN_BEAD +EFST_BREEZE_BEAD EFST_BREEZE_BEAD +EFST_AID_PERIOD_RECEIVEITEM_2ND Improved drop rate +EFST_AID_PERIOD_PLUSEXP_2ND Improved experience +EFST_AID_PERIOD_PLUSJOBEXP_2ND Improved job experience +EFST_PRONTERA_JP Prontera military pill +EFST_GLOOM_CARD Increase attack power +EFST_PHARAOH_CARD Reduced SP Cost +EFST_KIEL_CARD Reduced after skill delay +EFST_CHEERUP Mom and Dad cheer up. +EFST_S_MANAPOTION Small mana potion +EFST_M_DEFSCROLL Brilliant Defense Scroll +EFST_AS_RAGGED_GOLEM_CARD Increased evasion +EFST_LHZ_DUN_N1 Grave Blessing +EFST_LHZ_DUN_N2 Grave Blessing +EFST_LHZ_DUN_N3 Grave Blessing +EFST_LHZ_DUN_N4 Grave Blessing +EFST_ALL_STAT_DOWN All Stats Down +EFST_GRADUAL_GRAVITY Gravity Raise +EFST_DAMAGE_HEAL Damage Heal +EFST_IMMUNE_PROPERTY_NOTHING Immune Property Neutral +EFST_IMMUNE_PROPERTY_WATER Immune Property Water +EFST_IMMUNE_PROPERTY_GROUND Immune Property Ground +EFST_IMMUNE_PROPERTY_FIRE Immune Property Fire +EFST_IMMUNE_PROPERTY_WIND Immune Property Wind +EFST_IMMUNE_PROPERTY_POISON Immune Property Poison +EFST_IMMUNE_PROPERTY_SAINT Immune Property Saint +EFST_IMMUNE_PROPERTY_DARKNESS Immune Property Darkness +EFST_IMMUNE_PROPERTY_TELEKINESIS Immune Property Telekinesis +EFST_IMMUNE_PROPERTY_UNDEAD Immune Property Undead +EFST_REUSE_LIMIT_NP Reuse Limit Np +EFST_SPECIALCOOKIE Special Cookie +EFST_GLORY_OF_RETURN Glory Of Return +EFST_ATK_POPCORN Increase damage +EFST_MATK_POPCORN Increase magic damage +EFST_ASPD_POPCORN Increase attack speed +EFST_ULTIMATECOOK Ultimate Cook +EFST_LIGHTOFMOON Light of Moon +EFST_LIGHTOFSUN Light of Sun +EFST_LIGHTOFSTAR Light of Star +EFST_LUNARSTANCE Lunar Stance +EFST_UNIVERSESTANCE Universe Stance +EFST_SUNSTANCE Sun Stance +EFST_FLASHKICK Mark of the Stars +EFST_NEWMOON New Moon +EFST_STARSTANCE Star Stance +EFST_DIMENSION Book of Dimension +EFST_DIMENSION1 Dimension 1 +EFST_DIMENSION2 Dimension 2 +EFST_CREATINGSTAR Book of Creating Star +EFST_FALLINGSTAR Falling Star +EFST_NOVAEXPLOSING Nova EXP Losing +EFST_GRAVITYCONTROL Gravity Control +EFST_SOULCOLLECT Soul Collect +EFST_SOULREAPER Soul Harvest +EFST_SOULUNITY Soul Bound +EFST_SOULSHADOW Shadow Soul +EFST_SOULFAIRY Fairy Soul +EFST_SOULFALCON Falcon Soul +EFST_SOULGOLEM Golem Soul +EFST_SOULDIVISION Soul Division +EFST_SOULENERGY Soul Energy +EFST_USE_SKILL_SP_SPA Use Skill Sp Spa +EFST_USE_SKILL_SP_SHA Use Skill Sp Sha +EFST_SP_SHA SP Sha +EFST_INFINITY_DRINK Infinity Drink +EFST_ABYSS_001 Curse of Death +EFST_ABYSS_002 Crown of Darkness +EFST_ABYSS_003 Crown of Light +EFST_ABYSS_004 Crown of Mist +EFST_ABYSS_005 Magic Space +EFST_ABYSS_006 Devotion to The Emperor +EFST_ABYSS_007 Dragon's Honor +EFST_ABYSS_008 Curse of The Evil Eye +EFST_YGGDRASIL_BLESS Yggdrasil Blessing +EFST_HUNTING_EVENT Hunter's Supper +EFST_PERIOD_RECEIVEITEM_2ND Period Receiveitem 2Nd +EFST_PERIOD_PLUSEXP_2ND Period Plusexp 2Nd +EFST_EXPDROPUP Expdropup +EFST_TW_NEWYEAR_EVENT Taiwan New Year Event +EFST_ENSEMBLEFATIGUE Ensemble Fatigue +EFST_ADAPTATION Adaptation to Circumstances +EFST_ANCILLA Ancilla +EFST_FESTIVE_ENERGY Festive Energy +EFST_WEAPONBLOCK_ON Weapon Blocking +EFST_CRI_DAMAGE Cutlass +EFST_DEF_POWER Malang Snow Crab +EFST_DEF_IGNORE Marlin +EFST_BOW_ATK_POWER Striped Eel +EFST_RED_ORG_POTION Horse Mackerel +EFST_CAST_TIME Bluefin Tuna +EFST_LEAPIMPAIRED Leap Impaired +EFST_EXCLUSIVE_RECEIVEITEM Exclusive Receive Item +EFST_EXCLUSIVE_PLUSEXP Exclusive Plus EXP +EFST_ASSUMPTIO_BUFF Assumptio +EFST_BASILICA_BUFF Basilica +EFST_OVERLAPEXPUP2 Malang Cat Can2 +EFST_SOULCURSE Curse of Evil Spirit +EFST_SOUND_OF_DESTRUCTION Song of Destruction +EFST_NV_BREAKTHROUGH Nv Breakthrough +EFST_HELPANGEL Angel, Help me! +EFST_NV_TRANSCENDENCE Nv Transcendence +EFST_SWEETSFAIR_ATK Increase ATK +EFST_SWEETSFAIR_MATK Increase MATK +EFST_FLOWER_LEAF2 Increase Dodge and Attack Speed, decreases Variable Casting Time +EFST_FLOWER_LEAF3 Small Flowers +EFST_FLOWER_LEAF4 Cherry Blossom + +BODYSTATE_STONECURSE Petrified +BODYSTATE_FREEZING Frozen +BODYSTATE_STUN Stunned +BODYSTATE_SLEEP Sleeping +BODYSTATE_UNDEAD Undead +BODYSTATE_STONECURSE_ING Petrifying +BODYSTATE_BURNNING Burning + +EFFECTSTATE_SIGHT Sight +EFFECTSTATE_BURROW Hiding +EFFECTSTATE_HIDING Cloaking +EFFECTSTATE_PUSHCART Level 1 Cart +EFFECTSTATE_BIRD Falcon +EFFECTSTATE_CHICKEN Pecopeco +EFFECTSTATE_SPECIALHIDING GM Perfect Hide +EFFECTSTATE_PUSHCART2 Level 2 Cart +EFFECTSTATE_PUSHCART3 Level 3 Cart +EFFECTSTATE_PUSHCART4 Level 4 Cart +EFFECTSTATE_PUSHCART5 Level 5 Cart +EFFECTSTATE_ORCFACE Orc Head +EFFECTSTATE_MARRIED Wedding Sprites +EFFECTSTATE_RUWACH Ruwach +EFFECTSTATE_FOOTPRINT Chase Walk +EFFECTSTATE_STAR2 Flying +EFFECTSTATE_SANTA Xmas +EFFECTSTATE_TRANSFORM Transform +EFFECTSTATE_SUMMER Summer +EFFECTSTATE_DRAGON Riding Dragon (Default) +EFFECTSTATE_WUG Support Warg +EFFECTSTATE_WUGRIDER Riding Warg +EFFECTSTATE_MADOGEAR Madogear +EFFECTSTATE_DRAGON2 Riding Dragon (Black) +EFFECTSTATE_DRAGON3 Riding Dragon (White) +EFFECTSTATE_DRAGON4 Riding Dragon (Blue) +EFFECTSTATE_DRAGON5 Riding Dragon (Red) + +HEALTHSTATE_POISON Poisoned +HEALTHSTATE_CURSE Cursed +HEALTHSTATE_SILENCE Silenced +HEALTHSTATE_CONFUSION Signum Crisis +HEALTHSTATE_BLIND Blinded +HEALTHSTATE_ANGELUS Angelus +HEALTHSTATE_BLOODING Bleeding +HEALTHSTATE_HEAVYPOISON Deadly Poison + +EFST_DELAY Delay + +EFST_MISTY_FROST Freezing +EFST_MAGIC_POISON Magical Addiction +EFST_DEADLY_DEFEASANCE Deadly Projection +EFST_CLIMAX_DES_HU Destructive Hurricane +EFST_CLIMAX Climax +EFST_LUXANIMA Lux Anima Rune Stone: Lux Anima +EFST_BATH_FOAM_A Bath A +EFST_BATH_FOAM_B Bath B +EFST_BATH_FOAM_C Bath C +EFST_AROMA_OIL Aroma Oil +EFST_POWERFUL_FAITH Powerful Faith +EFST_SINCERE_FAITH Sincere Faith +EFST_FIRM_FAITH Firm Faith +EFST_HELLS_PLANT_ARMOR Hell Plant +EFST_LOCKON_LASER Set Firepoints +EFST_REF_T_POTION Golden Yesterday +EFST_ADD_ATK_DAMAGE Red Herb Activator +EFST_ADD_MATK_DAMAGE Blue Herb Activator +EFST_SERVANTWEAPON Servant Weapon +EFST_SERVANT_SIGN Servant Weapon Mark +EFST_CHARGINGPIERCE Charging Pierce +EFST_DRAGONIC_AURA Dragonic Aura +EFST_VIGOR Vigor +EFST_CLIMAX_EARTH Violant Quake +EFST_CLIMAX_BLOOM All Bloom +EFST_CLIMAX_CRYIMP Crystal Impact +EFST_HOLY_OIL Oleum Sanctum +EFST_SHADOW_EXCEED Shadow Exceed +EFST_DANCING_KNIFE Dancing Knife +EFST_POTENT_VENOM Potent Venom +EFST_SHADOW_SCAR Shadow Wound +EFST_MEDIALE Mediale Votum +EFST_A_VITA Argutus Vita +EFST_A_TELUM Argutus Telum +EFST_PRE_ACIES Presens Acies +EFST_COMPETENTIA Competentia +EFST_GUARD_STANCE Guard Stance +EFST_ATTACK_STANCE Attack Stance +EFST_GUARDIAN_S Guardian Shield +EFST_HANDICAPSTATE_DEEPBLIND Deep Blind +EFST_HANDICAPSTATE_DEEPSILENCE Deep Silence +EFST_HANDICAPSTATE_LASSITUDE Lethargy +EFST_HANDICAPSTATE_FROSTBITE Rapid Cooling +EFST_HANDICAPSTATE_SWOONING Swooning +EFST_HANDICAPSTATE_LIGHTNINGSTRIKE Torrent +EFST_HANDICAPSTATE_CRYSTALLIZATION Concretion +EFST_HANDICAPSTATE_CONFLAGRATION Blaze +EFST_HANDICAPSTATE_MISFORTUNE Unlucky +EFST_HANDICAPSTATE_DEADLYPOISON Severe Poison +EFST_HANDICAPSTATE_DEPRESSION Gloomy +EFST_HANDICAPSTATE_HOLYFLAME Holy Flame +EFST_REBOUND_S Rebound Shield +EFST_HOLY_S Holy Shield +EFST_ULTIMATE_S Ultimate Sacrifice +EFST_SPEAR_SCAR Grand Judgement +EFST_SHIELD_POWER Shield Shooting +EFST_SHADOW_WEAPON Enchanting Shadow +EFST_RELIGIO Religio +EFST_BENEDICTUM Benedictum +EFST_FIRST_BRAND First Brand +EFST_SECOND_BRAND Judgement Brand +EFST_SECOND_JUDGE Judge +EFST_THIRD_EXOR_FLAME Third Exorcism Flame +EFST_FIRST_FAITH_POWER First Faith Power +EFST_AXE_STOMP Axe Stomp +EFST_A_MACHINE Activation Attack Machine +EFST_D_MACHINE Activation Defense Machine +EFST_SHADOW_STRIP Strip Shadow +EFST_ABYSS_DAGGER Abyss Dagger +EFST_ABYSSFORCEWEAPON From the Abyss +EFST_ABYSS_SLAYER Abyss Slayer +EFST_PROTECTSHADOWEQUIP Full Shadow Protection +EFST_RESEARCHREPORT Research Report +EFST_BO_HELL_DUSTY Hell Tree Powder +EFST_WINDSIGN Wind Sign +EFST_CALAMITYGALE Calamity Gale +EFST_MYSTIC_SYMPHONY Mystic Symphony +EFST_KVASIR_SONATA Sonata of Kvashir +EFST_SOUNDBLEND Sound Blend +EFST_GEF_NOCTURN Geffenia Nocturne +EFST_AIN_RHAPSODY Raphsody of Mineworker +EFST_MUSICAL_INTERLUDE Musical Interlude +EFST_JAWAII_SERENADE Serenade Of Jawaii +EFST_PRON_MARCH March of Prontera +EFST_SPELL_ENCHANTING Spell Enchanting +EFST_HOMUN_TIME Call Homunculus +EFST_POISON_MIST Poison Mist +EFST_STONE_WALL Stone Wall +EFST_OVERBRANDREADY Overbrand Ready +EFST_SHIELDSPELL Shield Spell +EFST_CLOUD_POISON Poison Cloud +EFST_SPORE_EXPLOSION_DEBUFF Spore Explosion +EFST_MASSIVE_F_BLASTER Massive Flame Blaster +EFST_NOEQUIPWEAPON2 Strip Shadow Weapon +EFST_NOEQUIPARMOR2 Strip Shadow Armor +EFST_NOEQUIPSHIELD2 Strip Shadow Shield +EFST_NOEQUIPSHOES2 Strip Shadow Shoes +EFST_NOEQUIPPENDANT2 Strip Shadow Pendant +EFST_NOEQUIPEARING2 Strip Shadow Earring +EFST_NOEQUIPFULL2 Strip Shadow Equipment +EFST_CURSE_R_CUBE Curse of Red Cube +EFST_CURSE_B_CUBE Curse of Blue Cube + +EFST_INVULNERABILITY Invulnerability +EFST_STOP Stop \ No newline at end of file From 8cf0985d07426109de7460a03a6ff9ad600fbce4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Bosc=C3=A1?= <juan_bosca@live.com> Date: Tue, 10 Mar 2026 18:41:29 -0300 Subject: [PATCH 07/18] Add deep system architecture map for knowledge base --- .../knowledge/system_architecture_map.md | 249 ++++++++++++++++++ 1 file changed, 249 insertions(+) create mode 100644 openkore_llm_knowledge/knowledge/system_architecture_map.md diff --git a/openkore_llm_knowledge/knowledge/system_architecture_map.md b/openkore_llm_knowledge/knowledge/system_architecture_map.md new file mode 100644 index 0000000000..714ea4554f --- /dev/null +++ b/openkore_llm_knowledge/knowledge/system_architecture_map.md @@ -0,0 +1,249 @@ +# OpenKore System Architecture Map + +This map describes how the curated OpenKore knowledge bundle components fit together at runtime. + +## 1) AI Subsystem + +### Purpose +Coordinate autonomous behavior: combat decisions, state transitions, and action triggering. + +### Main modules +- `core/AI.pm` +- `core/AI/CoreLogic.pm` +- (supporting interaction surfaces: `core/Commands.pm`, `core/TaskManager.pm`) + +### Interactions +- Consumes actor/world state from the Actor subsystem. +- Schedules and updates executable work through the Task subsystem. +- Reacts to network-derived state updates (packet handlers). +- Can be influenced by Plugins/Macros through hook and command surfaces. + +### Important entry points +- AI cycle invoked from the main loop (`core/functions.pl`). +- Decision logic concentrated in `core/AI/CoreLogic.pm`. + +--- + +## 2) Actor System + +### Purpose +Represent dynamic world entities (player, monsters, NPCs, other actors) and maintain indexed state. + +### Main modules +- `core/Actor.pm` +- `core/ActorList.pm` + +### Interactions +- Updated primarily by packet receive handlers. +- Queried by AI and Tasks for targeting, proximity, and world context. +- Exposed indirectly to plugins/macros through runtime globals and events. + +### Important entry points +- Actor creation/update from packet decode paths. +- Actor lookup/list operations through `core/ActorList.pm`. + +--- + +## 3) Task System + +### Purpose +Provide executable units for actions and multi-step workflows. + +### Main modules +- `core/Task.pm` +- `core/TaskManager.pm` +- `core/Task/Route.pm` +- `core/Task/Move.pm` +- `core/Task/TalkNPC.pm` + +### Interactions +- Receives intent from AI, commands, and automation systems. +- Uses Network subsystem for action-side packet emission. +- Depends on Actor/Map state updates from receive flow to advance steps. + +### Important entry points +- Task queueing/scheduling in `core/TaskManager.pm`. +- Specialized flow handlers in route/move/talk task modules. + +--- + +## 4) Network Subsystem + +### Purpose +Manage connection state, transport lifecycle, and bridging/proxy modes. + +### Main modules +- `networking/Network.pm` +- `networking/DirectConnection.pm` +- `networking/XKore.pm` +- `networking/XKore2.pm` +- `networking/XKoreProxy.pm` +- `networking/Network/XKore2/{AccountServer,CharServer,MapServer}.pm` + +### Interactions +- Feeds decoded data into Packet Handling. +- Receives outgoing payload requests from Tasks, Commands, and Plugins. +- Selects mode-dependent behavior (direct, bridge, proxy/XKore2). + +### Important entry points +- Runtime connection state transitions. +- XKore mode startup/iteration paths. + +--- + +## 5) Packet Handling Subsystem + +### Purpose +Decode, map, dispatch, and construct protocol packets across server variants. + +### Main modules +- `networking/PacketParser.pm` +- `networking/MessageTokenizer.pm` +- `networking/Receive.pm` +- `networking/Send.pm` +- Server-specific variants under: + - `networking/Network/Receive/*` + - `networking/Network/Send/*` + +### Interactions +- Input side updates actor/runtime state and triggers higher-level events. +- Output side encodes requested actions for transport. +- Coupled to table definitions (servers + recvpackets mapping). + +### Important entry points +- Receive dispatch in `networking/Receive.pm`. +- Send construction in `networking/Send.pm`. +- Server-type overrides in `ServerType0`, `kRO`, `iRO`, etc. + +--- + +## 6) Routing and Movement Subsystem + +### Purpose +Convert navigation intent into route computation and movement execution. + +### Main modules +- `core/Task/Route.pm` +- `core/Task/Move.pm` +- (coordination via `core/TaskManager.pm` and AI core) + +### Interactions +- Triggered by AI decisions or direct command/plugin logic. +- Uses actor/map state from packet updates. +- Emits move-related packets through send pipeline. + +### Important entry points +- Route planning task instantiation. +- Movement step processing and re-evaluation. + +--- + +## 7) Plugin System + +### Purpose +Extend runtime behavior without changing core source. + +### Main modules +- Representative plugin entries in curated bundle: + - `plugins/macro/macro.pl` + - `plugins/eventMacro/eventMacro.pl` + - `plugins/reconnect/reconnect.pl` + - `plugins/profiles/profiles.pl` + - `plugins/map/map.pl` + +### Interactions +- Registers hooks into startup/main loop/config/packet related events. +- Registers commands to expose operational controls. +- Can influence AI/task execution by issuing commands or scheduling logic. + +### Important entry points +- `Plugins::register(...)` +- `Plugins::addHooks(...)` +- `Commands::register(...)` +- Plugin unload/reload callbacks. + +--- + +## 8) Config System + +### Purpose +Provide operator-controlled behavior policies without code edits. + +### Main modules / files +- `config/config.txt` (global behavior) +- `config/items_control.txt` (item policy) +- `config/mon_control.txt` (monster behavior policy) +- Additional control files in `config/` for specialized behavior domains. + +### Interactions +- Loaded/parsed by settings + file parser paths. +- Consumed by AI, task logic, plugins, and packet behavior decisions. +- Changes can trigger plugin/config hooks in runtime. + +### Important entry points +- Initial load during startup sequence. +- Config modification events watched by plugins (e.g., macro/eventMacro). + +--- + +## 9) Macro System + +### Purpose +Offer text-configured automation layers for reactive behavior. + +### Main modules +- Macro stack: + - `plugins/macro/macro.pl` + - `plugins/macro/Macro/*` +- EventMacro stack: + - `plugins/eventMacro/eventMacro.pl` + - `plugins/eventMacro/eventMacro/*` + +### Interactions +- Subscribes to hooks/events and loop phases. +- Evaluates automacro conditions against runtime state and events. +- Triggers commands/actions that flow into AI/tasks/network send paths. + +### Important entry points +- Macro file load keys (`macro_file`, `eventMacro_file`). +- Commands: `macro`, `eventMacro`, `emacro`. + +--- + +## System Interaction Overview + +At runtime, the main loop initializes modules, loads config/table data, and enters iterative execution. Incoming network data is tokenized and parsed, then dispatched by receive handlers that refresh actor/world state. AI logic evaluates this state and schedules tasks. Tasks execute concrete workflows (move, route, NPC interactions), producing outgoing actions encoded by send logic and delivered through the active network mode. + +Plugins hook into this lifecycle at well-defined points (startup, loop, config updates, packet-related events), extending behavior and command surfaces. Macro/eventMacro layers run on top of plugin hooks, evaluating declarative automacro rules and injecting actions back into command/task pipelines. + +### Example runtime flows + +#### A) AI loop +1. Main loop tick runs. +2. AI evaluates current actor/state context. +3. AI enqueues/updates tasks. +4. Tasks emit action requests -> send packets. + +#### B) NPC interaction +1. AI/command/plugin requests NPC action. +2. `Task/TalkNPC` drives dialogue steps. +3. Receive handlers process NPC responses. +4. Task advances/completes based on packet-updated state. + +#### C) Packet receive flow +1. Raw bytes arrive via network backend. +2. Tokenizer/parser resolves packet boundaries/opcodes. +3. Receive dispatch maps to handler. +4. Handler mutates globals/actors/tasks and may trigger hooks. + +#### D) Plugin hook execution +1. Plugin registers hooks and commands at startup. +2. Runtime events invoke plugin callbacks. +3. Plugin callback performs logic (state check, action trigger, config reload). +4. Optional commands expose runtime control to operator. + +#### E) Routing decision flow +1. AI decides to navigate to target/map/coordinate. +2. Route task computes route segments. +3. Move task executes stepwise movement. +4. Receive updates position/context; route logic re-evaluates until arrival/failure. From 37e3b05e134917391a4f1db5486fd6cfe0afb7ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Bosc=C3=A1?= <juan_bosca@live.com> Date: Tue, 10 Mar 2026 18:41:34 -0300 Subject: [PATCH 08/18] Add core module dependency map for knowledge bundle --- .../knowledge/module_dependency_map.md | 122 ++++++++++++++++++ 1 file changed, 122 insertions(+) create mode 100644 openkore_llm_knowledge/knowledge/module_dependency_map.md diff --git a/openkore_llm_knowledge/knowledge/module_dependency_map.md b/openkore_llm_knowledge/knowledge/module_dependency_map.md new file mode 100644 index 0000000000..d5d9e118fa --- /dev/null +++ b/openkore_llm_knowledge/knowledge/module_dependency_map.md @@ -0,0 +1,122 @@ +# Module Dependency Map (Core-Curated View) + +This dependency map is derived from `openkore_llm_knowledge/core/` and focuses on AI, Actor, Network, Commands, Task, Routing, and File Parsing relationships. + +## Major Module Relationships + +### 1) `functions.pl` +- **Purpose:** Runtime bootstrap loop and subsystem orchestration. +- **Key dependencies:** `AI`, `Commands`, `Network::Receive`, `TaskManager`, `Settings`. +- **Depended on by:** Runtime entry flow (top-level bootstrap path). + +### 2) `AI` +- **Purpose:** AI mode/state management and behavior control surface. +- **Key dependencies:** `Globals`, `Utils`, `Log`, `Field`. +- **Depended on by:** `AI::CoreLogic`, `Commands`, `Network::Receive`, task modules. + +### 3) `AI::CoreLogic` +- **Purpose:** Main decision engine for combat/behavior execution. +- **Key dependencies:** `AI`, `Globals`, `Misc`, `Network::Send`, `Commands`, `FileParsers`, `Task::TalkNPC`. +- **Depended on by:** `functions.pl` runtime loop and AI-driven task flow. + +### 4) `Actor` +- **Purpose:** Base actor model (player, mob, NPC entity behavior/state object). +- **Key dependencies:** `Globals`, `Utils`, `Task`, `Misc`, `Translation`. +- **Depended on by:** `ActorList`, `Network::Receive`, `AI`, routing/task logic. + +### 5) `ActorList` +- **Purpose:** Indexed actor container and type-aware lookup. +- **Key dependencies:** `Actor` + actor subclasses. +- **Depended on by:** `functions.pl`, `Task::TalkNPC` and other actor-consumers. + +### 6) `Commands` +- **Purpose:** Operator and automation command dispatch. +- **Key dependencies:** `Globals`, `Network`, `Network::Send`, `Settings`, `AI`, `Task`. +- **Depended on by:** `AI::CoreLogic`, `Task::TalkNPC`, packet/automation callbacks. + +### 7) `FileParsers` +- **Purpose:** Parse control/table data into runtime structures. +- **Key dependencies:** `Settings`, `Plugins`, `Utils`, `Log`. +- **Depended on by:** `Settings`, `AI::CoreLogic`, `Network::Receive`, `Commands`. + +### 8) `Settings` +- **Purpose:** Configuration path resolution, file registration, and argument parsing. +- **Key dependencies:** `Globals`, `Utils::ObjectList`, `Translation`, `Modules`. +- **Depended on by:** `functions.pl`, `Commands`, `FileParsers`, AI/network modules. + +### 9) `Globals` +- **Purpose:** Shared runtime state and exported global structures. +- **Key dependencies:** `Modules`. +- **Depended on by:** Nearly all core subsystems (`AI`, `Commands`, `Network::*`, `Task::*`). + +### 10) `Network` +- **Purpose:** Connection state abstraction and shared network constants. +- **Key dependencies:** `Modules`. +- **Depended on by:** `Commands`, `Network::PacketParser`, `Network::Receive`, `Task::*`. + +### 11) `Network::PacketParser` +- **Purpose:** Packet data parsing/reconstruction and packet utility constants. +- **Key dependencies:** `Globals`, `Network`, `Network::MessageTokenizer`, `Plugins`, `Utils`. +- **Depended on by:** `Network::Receive`, `Commands`. + +### 12) `Network::Receive` +- **Purpose:** Incoming packet dispatch and handler execution. +- **Key dependencies:** `Network::PacketParser`, `AI`, `Globals`, `FileParsers`, `Plugins`, `Skill`, `Actor::Slave::*`. +- **Depended on by:** Core runtime/loop flows and state update paths. + +### 13) `Task` +- **Purpose:** Base task abstraction (lifecycle/priority/state). +- **Key dependencies:** `Modules`, `Utils::CallbackList`, `Utils::Set`. +- **Depended on by:** `TaskManager`, task subclasses, actor/AI command flows. + +### 14) `TaskManager` +- **Purpose:** Task scheduling/execution container. +- **Key dependencies:** `Task`, `Utils::Set`, `Utils::CallbackList`. +- **Depended on by:** `functions.pl` orchestration loop and AI/task producers. + +### 15) `Task::Route` +- **Purpose:** Route planning and navigation workflow control. +- **Key dependencies:** `Task::Move`, `AI`, `Network`, `Field`, `Utils::PathFinding`. +- **Depended on by:** `AI::CoreLogic` and movement decision flow. + +### 16) `Task::Move` +- **Purpose:** Movement execution subtask and movement-state progression. +- **Key dependencies:** `Task::WithSubtask`, `Task::SitStand`, `Network`, `Plugins`, `Globals`. +- **Depended on by:** `Task::Route` and route execution chains. + +### 17) `Task::TalkNPC` +- **Purpose:** NPC conversation/state-machine interactions. +- **Key dependencies:** `Task`, `AI`, `Commands`, `Network`, `Misc`, `Plugins`. +- **Depended on by:** `AI::CoreLogic` and command-triggered NPC workflows. + +### 18) `Modules` +- **Purpose:** Core module registration/reload helper used across subsystems. +- **Key dependencies:** Perl runtime utilities (`Config`, `FindBin`, `File::Spec`). +- **Depended on by:** `Globals`, `Settings`, `Network`, `Task`, `Commands` and others. + +--- + +## Top 20 Most Important Modules (Architecture Perspective) + +1. `functions.pl` +2. `AI` +3. `AI::CoreLogic` +4. `Actor` +5. `ActorList` +6. `Task` +7. `TaskManager` +8. `Task::Route` +9. `Task::Move` +10. `Task::TalkNPC` +11. `Commands` +12. `Network` +13. `Network::PacketParser` +14. `Network::Receive` +15. `Settings` +16. `FileParsers` +17. `Globals` +18. `Modules` +19. `Network::Send` (critical dependency referenced by core decision/command paths) +20. `Misc` (high-touch utility dependency in AI/task/command/network logic) + +> Note: Items 1–18 are present in the curated `core/` set; 19–20 are explicitly identified from core dependency references to preserve architectural completeness. From 81087b91bc3309d08f04128df028ce7acb868013 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Bosc=C3=A1?= <juan_bosca@live.com> Date: Tue, 10 Mar 2026 18:41:38 -0300 Subject: [PATCH 09/18] Add OpenKore debugging playbook knowledge guide --- .../knowledge/debugging_playbook.md | 244 ++++++++++++++++++ 1 file changed, 244 insertions(+) create mode 100644 openkore_llm_knowledge/knowledge/debugging_playbook.md diff --git a/openkore_llm_knowledge/knowledge/debugging_playbook.md b/openkore_llm_knowledge/knowledge/debugging_playbook.md new file mode 100644 index 0000000000..9eadc082ea --- /dev/null +++ b/openkore_llm_knowledge/knowledge/debugging_playbook.md @@ -0,0 +1,244 @@ +# OpenKore Debugging Playbook + +This playbook provides structured troubleshooting for common OpenKore runtime issues. + +--- + +## 1) Bot Not Moving + +### Possible causes +- Movement/task pipeline blocked (AI state, mutex, sit/stand state, map lock logic). +- Invalid route target or unreachable coordinates. +- Network-side movement packets not being sent/accepted. +- Configuration constraints preventing movement. + +### Files to inspect +- Core runtime/task modules: + - `core/AI/CoreLogic.pm` + - `core/TaskManager.pm` + - `core/Task/Route.pm` + - `core/Task/Move.pm` +- Movement/network path: + - `networking/Send.pm` + - `networking/Network/Send/ServerType0.pm` (or active server-specific send module) +- Config: + - `config/config.txt` + - `config/timeouts.txt` + +### Configuration checks +- Verify movement/route-related settings in `config.txt` (lock map, teleport fallback, attack-route behavior). +- Confirm no conflicting delay/timeouts in `timeouts.txt`. +- Confirm map/server profile alignment (server selection and table set). + +### Debugging steps +1. Confirm AI is active and not paused/stuck in an unexpected state. +2. Inspect active tasks (route/move) and whether they are advancing or looping. +3. Check if movement packets are being emitted by send logic. +4. Validate map data/path constraints from route calculations. +5. Re-test with a simple direct move target; compare behavior. + +--- + +## 2) NPC Interaction Issues + +### Possible causes +- NPC not found in actor list or wrong target coordinates. +- Dialogue state machine mismatch (unexpected prompt/response sequence). +- Packet handler mismatch for NPC-related responses. +- Route-to-NPC task never reaches interaction range. + +### Files to inspect +- Task and command flow: + - `core/Task/TalkNPC.pm` + - `core/Commands.pm` + - `core/AI/CoreLogic.pm` +- Receive handlers: + - `networking/Receive.pm` + - server-specific receive module (`networking/Network/Receive/*.pm`) +- Tables/config: + - `tables/portals.txt` (if interaction depends on map pathing) + - `config/config.txt` + +### Configuration checks +- Ensure NPC scripts/macros (if used) match actual in-game dialogue sequence. +- Confirm route/lock-map settings still allow navigation to NPC. +- Validate server packet profile (`recvpackets`) is current. + +### Debugging steps +1. Verify NPC is visible/tracked in actor context. +2. Step through `TalkNPC` task progression (init, talk, select, complete). +3. Check receive handlers for expected NPC dialogue packets. +4. Validate command/script parameters (NPC name/id, sequence values). +5. Retry with a minimal NPC interaction command path. + +--- + +## 3) Plugin Not Loading + +### Possible causes +- Plugin disabled by load policy. +- Plugin name not listed when selective loading is enabled. +- Missing dependencies/imports in plugin code. +- Startup callback failure throws during register/load. + +### Files to inspect +- Plugin architecture docs/code: + - `knowledge/plugin_system.md` + - `plugins/<plugin>/<plugin>.pl` +- Config controls: + - `config/sys.txt` +- (Core plugin loader reference in source repo: `src/Plugins.pm`, `src/functions.pl`) + +### Configuration checks +- In `sys.txt`, verify: + - `loadPlugins` mode + - `loadPlugins_list` / `skipPlugins_list` +- Confirm plugin filename matches expected plugin name. + +### Debugging steps +1. Check startup logs for plugin load exception details. +2. Validate plugin register/unload function signatures. +3. Verify required modules are available and import paths are correct. +4. If selective loading, explicitly include plugin in list and restart. +5. Test plugin command availability after successful load. + +--- + +## 4) Macro Not Triggering + +### Possible causes +- Macro/eventMacro plugin not loaded. +- Macro file path key incorrect (`macro_file` / `eventMacro_file`). +- Condition never becomes true (state/event mismatch). +- Automacro disabled, timed out, or blocked by check mode. + +### Files to inspect +- Macro system docs/code: + - `knowledge/macro_system.md` + - `plugins/macro/macro.pl` + - `plugins/eventMacro/eventMacro.pl` + - `plugins/eventMacro/eventMacro/*` +- Config: + - `config/config.txt` + - `config/sys.txt` + +### Configuration checks +- Confirm macro plugin is in plugin load list. +- Validate macro file names/paths (`macros.txt` / `eventMacros.txt` equivalents). +- Check macro/eventMacro options (orphans/check-on-AI/enable states). + +### Debugging steps +1. Confirm plugin loaded and command is available (`macro`, `eventMacro`, `emacro`). +2. Reparse/reload macro file and verify no parse errors. +3. Use status/list commands to inspect macro/automacro states. +4. Verify trigger condition using simple known event first. +5. Incrementally add conditions until failing condition is isolated. + +--- + +## 5) Routing Problems + +### Possible causes +- Pathfinding cannot compute a valid route. +- Map data mismatch or stale map/portal table information. +- Dynamic obstacles or combat interruptions constantly reset route. +- Route task repeatedly interrupted by higher-priority tasks. + +### Files to inspect +- Routing logic: + - `core/Task/Route.pm` + - `core/Task/Move.pm` + - `core/TaskManager.pm` +- Table references: + - `tables/portals.txt` + - profile map tables (`tables/kRO/maps.txt`, `tables/iRO/maps.txt`) +- Config: + - `config/config.txt` + +### Configuration checks +- Confirm map/portal table set matches target server profile. +- Check route-related config constraints in `config.txt`. +- Verify no over-restrictive lockMap/avoid settings. + +### Debugging steps +1. Test short route on same map first. +2. Inspect route task generated path length/waypoints. +3. Check for repeated interruptions/timeouts. +4. Validate map transition assumptions (portal routes). +5. Re-test after narrowing to minimal route scenario. + +--- + +## 6) Packet Desync + +### Possible causes +- Wrong `recvpackets` profile for active server/client build. +- Server-type receive/send module mismatch. +- Packet format changed on server side. +- Corrupted/incomplete packet framing in tokenizer path. + +### Files to inspect +- Packet docs/code: + - `knowledge/networking_packets.md` + - `networking/PacketParser.pm` + - `networking/MessageTokenizer.pm` + - `networking/Receive.pm` + - `networking/Send.pm` +- Tables: + - `tables/servers.txt` + - `tables/*/recvpackets.txt` + - `tables/packetlist.txt` + - `tables/packetdescriptions.txt` + +### Configuration checks +- Verify selected server profile points to correct packet map. +- Ensure regional family tables (kRO/iRO/etc.) are aligned with server. + +### Debugging steps +1. Identify first unknown/broken packet switch in logs. +2. Match switch against packet list/description tables. +3. Confirm recvpackets version/profile used at runtime. +4. Compare affected switch handling in server-specific receive/send modules. +5. Re-validate behavior after profile/module adjustment. + +--- + +## 7) XKore Issues + +### Possible causes +- Port/listener conflicts. +- Wrong XKore mode assumptions (mode 1 bridge vs mode 2 proxy stack). +- Session/state transition mismatch between account/char/map stages. +- Client forwarding path blocked or malformed. + +### Files to inspect +- XKore docs/code: + - `knowledge/xkore_modes.md` + - `networking/XKore.pm` + - `networking/XKore2.pm` + - `networking/XKoreProxy.pm` + - `networking/Network/XKore2/{AccountServer,CharServer,MapServer}.pm` +- Config: + - `config/config.txt` + - `config/poseidon.txt` (if related environment integration is used) + +### Configuration checks +- Confirm XKore mode and listen/public IP/port values. +- Validate no duplicate process is already binding required ports. +- Ensure server-type settings are propagated correctly in XKore2 mode. + +### Debugging steps +1. Start with a single XKore mode and minimal config. +2. Verify listener startup and handshake progression. +3. Check state transitions (account -> char -> map). +4. Confirm packet forwarding in both directions. +5. If mode 2 fails, isolate failing stage server (Account/Char/Map). + +--- + +## General Triage Order (Recommended) +1. Confirm plugin/config/table profile alignment. +2. Isolate whether issue is logic-layer (AI/Task) or protocol-layer (packet/network). +3. Reproduce with minimal commands/actions. +4. Inspect module-specific path for first divergence. +5. Apply smallest fix/reconfiguration and re-test. From f34ff98e69a0ba2660f8fc500e8d1524803985c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Bosc=C3=A1?= <juan_bosca@live.com> Date: Tue, 10 Mar 2026 18:41:43 -0300 Subject: [PATCH 10/18] Add practical OpenKore development recipes --- .../knowledge/code_recipes.md | 196 ++++++++++++++++++ 1 file changed, 196 insertions(+) create mode 100644 openkore_llm_knowledge/knowledge/code_recipes.md diff --git a/openkore_llm_knowledge/knowledge/code_recipes.md b/openkore_llm_knowledge/knowledge/code_recipes.md new file mode 100644 index 0000000000..ac76a58024 --- /dev/null +++ b/openkore_llm_knowledge/knowledge/code_recipes.md @@ -0,0 +1,196 @@ +# OpenKore Code Recipes + +Practical snippets for common OpenKore customization tasks. + +--- + +## 1) Plugin Development + +### A) Minimal plugin skeleton +```perl +package MyPlugin; + +use strict; +use Plugins; +use Log qw(message); + +my $hooks; +my $cmds; + +Plugins::register('MyPlugin', 'Minimal example plugin', \&on_unload); + +sub on_unload { + Plugins::delHooks($hooks) if $hooks; + Commands::unregister($cmds) if $cmds; + message "[MyPlugin] unloaded\n", 'success'; +} + +1; +``` + +### B) Registering hooks +```perl +use Plugins; +use Log qw(message); + +$hooks = Plugins::addHooks( + ['start3', sub { message "[MyPlugin] start3 fired\n", 'info'; }], + ['mainLoop_pre', sub { + # periodic logic + # keep this lightweight + }], + ['configModify', sub { + my (undef, $args) = @_; + message "[MyPlugin] config changed: $args->{key}\n", 'info'; + }], +); +``` + +### C) Handling packets (hook-based) +```perl +# Example: observe a packet-level hook exposed by core/plugins +$hooks = Plugins::addHooks( + ['packet_pre/map_changed', sub { + my (undef, $args) = @_; + # inspect packet arguments; avoid mutating unless needed + message "[MyPlugin] map changed detected\n", 'info'; + }], +); +``` + +### D) Adding console commands +```perl +use Commands; +use Log qw(message); + +$cmds = Commands::register([ + 'mycmd', + 'My custom command', + sub { + my (undef, $params) = @_; + $params //= ''; + message "[MyPlugin] mycmd called with: $params\n", 'list'; + } +]); +``` + +--- + +## 2) Macros (`macros.txt` style) + +### A) Teleport on HP threshold +```txt +automacro emergency_tp { + hp < 35% + timeout 1 + run-once 0 + call { + do conf teleportAuto_useSkill 1 + do conf teleportAuto_useChatCommand 0 + do sp 26 1 # Teleport level 1 (example) + } +} +``` + +### B) Simple farming loop +```txt +macro farm_loop { + do move prontera 150 180 + pause 1 + do ai auto + pause 10 + do move prontera 120 160 + pause 1 + do ai auto + pause 10 + do macro farm_loop +} + +automacro start_farming { + map prontera + timeout 3 + run-once 1 + call farm_loop +} +``` + +### C) NPC interaction macro +```txt +macro use_healer { + do talknpc 156 186 c r0 c c +} + +automacro low_hp_heal { + hp < 60% + map prontera + timeout 15 + run-once 0 + call use_healer +} +``` + +--- + +## 3) EventMacros (`eventMacros.txt` style) + +### A) Reacting to monster presence +```txt +automacro danger_near { + MonsterNear 1 + MonsterNearDist < 6 + call { + do conf attackAuto 0 + do sp 26 1 + } +} +``` + +### B) Map entry trigger +```txt +automacro entered_payon { + InMap payon + mapLoaded + call { + do conf attackAuto 2 + do conf route_randomWalk 1 + do ai auto + } +} +``` + +--- + +## 4) Configuration Recipes + +### A) Item pickup logic (`items_control.txt`) +```txt +# item name keep storage sell +"Red Potion" 30 0 1 +"Blue Potion" 20 0 1 +"Jellopy" 0 0 1 +"Empty Bottle" 0 0 0 +``` + +### B) Monster avoidance (`mon_control.txt`) +```txt +# monster attack teleport search +"Baphomet" -1 1 0 +"Hydra" 1 0 0 +"Pupa" 0 0 0 +``` + +### C) Route locking (`config.txt`) +```txt +lockMap prontera +lockMap_x 156 +lockMap_y 186 +attackAuto_inLockOnly 1 +route_randomWalk 0 +``` + +--- + +## Notes +- Packet IDs, skill IDs, and command behaviors may vary by server profile. +- Keep plugin `mainLoop_pre` handlers lightweight to avoid lag. +- Test macros/eventMacros in a safe map before production farming routes. From 6863615c4200855b0b441c482c666d9c49c7750b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Bosc=C3=A1?= <juan_bosca@live.com> Date: Tue, 10 Mar 2026 18:41:48 -0300 Subject: [PATCH 11/18] Add OpenKore developer FAQ dataset --- .../knowledge/openkore_faq.md | 208 ++++++++++++++++++ 1 file changed, 208 insertions(+) create mode 100644 openkore_llm_knowledge/knowledge/openkore_faq.md diff --git a/openkore_llm_knowledge/knowledge/openkore_faq.md b/openkore_llm_knowledge/knowledge/openkore_faq.md new file mode 100644 index 0000000000..f7ed51a53b --- /dev/null +++ b/openkore_llm_knowledge/knowledge/openkore_faq.md @@ -0,0 +1,208 @@ +# OpenKore Developer FAQ + +This FAQ is based on the curated OpenKore knowledge bundle and references core, plugin, networking, config, and table modules. + +## 1) How do I write a plugin? +Start with a Perl file that calls `Plugins::register`, adds hooks via `Plugins::addHooks`, and optionally registers commands via `Commands::register`. +Reference: `knowledge/code_recipes.md`, `plugins/macro/macro.pl`, `plugins/eventMacro/eventMacro.pl`. + +## 2) What is the smallest plugin skeleton? +A minimal plugin includes `package`, `use Plugins`, `Plugins::register(...)`, optional hook/command setup, and unload cleanup. +Reference: `knowledge/code_recipes.md`. + +## 3) How do I register hooks in a plugin? +Use `Plugins::addHooks` with event/callback pairs such as `start3`, `mainLoop_pre`, and `configModify`. +Reference: `knowledge/plugin_system.md`, `plugins/macro/macro.pl`. + +## 4) How do I add a custom console command? +Use `Commands::register` and provide a callback receiving command parameters. +Reference: `knowledge/code_recipes.md`, `plugins/eventMacro/eventMacro.pl`. + +## 5) How do plugins clean up on unload? +Implement unload callback to remove hooks (`Plugins::delHooks`) and unregister commands. +Reference: `plugins/macro/macro.pl`, `plugins/eventMacro/eventMacro.pl`. + +## 6) How does the AI system work at a high level? +The main loop invokes AI logic, which inspects world state and schedules tasks. +Reference: `core/functions.pl`, `core/AI.pm`, `core/AI/CoreLogic.pm`, `knowledge/system_architecture_map.md`. + +## 7) Where is combat/behavior decision logic concentrated? +In `core/AI/CoreLogic.pm`. +Reference: `knowledge/core_subsystems.md`, `knowledge/module_dependency_map.md`. + +## 8) How are actors represented? +Actor entities use a base model plus indexed actor collections. +Reference: `core/Actor.pm`, `core/ActorList.pm`, `knowledge/core_subsystems.md`. + +## 9) Where is NPC interaction implemented? +Primary flow is in `core/Task/TalkNPC.pm` with related command/receive participation. +Reference: `knowledge/execution_flows.md`, `knowledge/debugging_playbook.md`. + +## 10) How does task scheduling work? +Tasks derive from `Task` and are queued/executed by `TaskManager`. +Reference: `core/Task.pm`, `core/TaskManager.pm`, `knowledge/module_dependency_map.md`. + +## 11) Where is routing implemented? +Routing is in `core/Task/Route.pm`, and movement execution in `core/Task/Move.pm`. +Reference: `knowledge/execution_flows.md`, `knowledge/core_subsystems.md`. + +## 12) How does packet receive flow work? +Data is tokenized/parsing handled, then dispatched by receive handlers to update runtime state. +Reference: `networking/MessageTokenizer.pm`, `networking/PacketParser.pm`, `networking/Receive.pm`, `knowledge/networking_packets.md`. + +## 13) Where are outgoing packets built? +Primarily in `networking/Send.pm` and server-specific send modules. +Reference: `knowledge/networking_packets.md`, `networking/Network/Send/ServerType0.pm`. + +## 14) How do server-specific packet differences get handled? +Through receive/send server-type modules and matching `recvpackets.txt` table data. +Reference: `networking/Network/Receive/*`, `networking/Network/Send/*`, `tables/*/recvpackets.txt`. + +## 15) What causes packet desync most often? +Profile mismatch between server configuration and selected `recvpackets` mapping. +Reference: `knowledge/debugging_playbook.md`, `knowledge/table_reference.md`. + +## 16) What are XKore modes? +Mode 1 is bridge-style; mode 2 is a fuller proxy stack with account/char/map components. +Reference: `knowledge/xkore_modes.md`, `networking/XKore.pm`, `networking/XKore2.pm`. + +## 17) Where are XKore2 stage servers implemented? +In `networking/Network/XKore2/AccountServer.pm`, `CharServer.pm`, and `MapServer.pm`. +Reference: `knowledge/xkore_modes.md`. + +## 18) Where is global runtime state stored? +In `core/Globals.pm`, consumed broadly by AI/network/task/command modules. +Reference: `knowledge/module_dependency_map.md`. + +## 19) Where is configuration path/file loading controlled? +In `core/Settings.pm` and parser routines in `core/FileParsers.pm`. +Reference: `knowledge/core_subsystems.md`, `knowledge/module_dependency_map.md`. + +## 20) Which config files matter most for behavior tuning? +`config.txt`, `items_control.txt`, and `mon_control.txt`. +Reference: `knowledge/config_system.md`. + +## 21) How does `lockMap` work conceptually? +It constrains activity/navigation to a target map (optionally coordinate-focused with lock map coords). +Reference: `knowledge/code_recipes.md` route locking example, `knowledge/config_system.md`. + +## 22) Where should I tune item pickup behavior? +In `config/items_control.txt`. +Reference: `knowledge/config_system.md`, `knowledge/code_recipes.md`. + +## 23) Where should I tune monster engagement/avoidance? +In `config/mon_control.txt`. +Reference: `knowledge/config_system.md`, `knowledge/code_recipes.md`. + +## 24) Where do plugin load policies live? +In `config/sys.txt` (`loadPlugins`, include/skip lists). +Reference: `knowledge/debugging_playbook.md`, `knowledge/plugin_system.md`. + +## 25) How do I tell macro and eventMacro apart? +Both are automation systems, but eventMacro emphasizes event/condition-driven structures with distinct parser/runner modules. +Reference: `knowledge/macro_system.md`, `plugins/macro/*`, `plugins/eventMacro/*`. + +## 26) Where are macro commands implemented? +In `plugins/macro/macro.pl` (`macro` command) and `plugins/eventMacro/eventMacro.pl` (`eventMacro`, `emacro`). +Reference: `knowledge/plugin_system.md`. + +## 27) Why would macros not trigger? +Common causes: plugin not loaded, wrong macro file path, false conditions, or disabled automacro. +Reference: `knowledge/debugging_playbook.md`. + +## 28) How do I set emergency teleport automation? +Use macro/eventMacro conditions on HP threshold and teleport action commands. +Reference: `knowledge/code_recipes.md`. + +## 29) How can I create a farming loop quickly? +Define a recursive macro with movement and `ai auto` pauses, gated by an automacro trigger. +Reference: `knowledge/code_recipes.md`. + +## 30) How do I debug “bot not moving”? +Inspect AI/task state and movement packet emission paths, then validate route/map constraints. +Reference: `knowledge/debugging_playbook.md`. + +## 31) How do I debug NPC interaction failures? +Trace `Task/TalkNPC` progression and corresponding receive handler updates. +Reference: `knowledge/debugging_playbook.md`, `knowledge/execution_flows.md`. + +## 32) How do I debug plugin load failures? +Check `sys.txt` plugin load mode/lists and plugin startup exceptions. +Reference: `knowledge/debugging_playbook.md`. + +## 33) How do I debug routing failures? +Validate route generation, movement progression, and map/portal tables. +Reference: `knowledge/debugging_playbook.md`, `tables/portals.txt`. + +## 34) What are the key packet reference tables? +`tables/packetlist.txt`, `tables/packetdescriptions.txt`, and server profile `recvpackets.txt` files. +Reference: `knowledge/table_reference.md`. + +## 35) What does `tables/servers.txt` do? +Defines server profiles and serverType anchors used by network/packet handling. +Reference: `knowledge/table_reference.md`, `knowledge/networking_packets.md`. + +## 36) Why include both kRO and iRO tables in curation? +They provide representative regional profiles for packet/content differences. +Reference: `knowledge/table_reference.md`. + +## 37) What is the role of `core/functions.pl`? +It drives startup stages and main loop transitions. +Reference: `knowledge/core_module_index.md`, `knowledge/module_dependency_map.md`. + +## 38) Why is `Commands.pm` so central? +It is a common control surface for operators, plugins, and automation. +Reference: `knowledge/module_dependency_map.md`. + +## 39) Why is `Network::Receive` so important? +Incoming packets drive most runtime state changes (actors, events, dialogs, combat updates). +Reference: `knowledge/networking_packets.md`, `knowledge/module_dependency_map.md`. + +## 40) How do plugins interact with runtime without core edits? +By hooking lifecycle/events and registering commands. +Reference: `knowledge/plugin_system.md`. + +## 41) Where should I start when extending behavior safely? +Start with a small plugin command + one lightweight hook; expand incrementally. +Reference: `knowledge/code_recipes.md`. + +## 42) How do I avoid plugin performance issues? +Keep `mainLoop_pre` callbacks lightweight and avoid heavy repeated processing. +Reference: `knowledge/code_recipes.md` notes. + +## 43) How do I map a runtime problem to the right subsystem quickly? +Use the system map: AI/Task for decisions/actions, Network/Packet for protocol/state updates, Config/Plugin for policy/extension. +Reference: `knowledge/system_architecture_map.md`. + +## 44) Where is the best overview of subsystem interactions? +`knowledge/system_architecture_map.md` and `knowledge/execution_flows.md`. + +## 45) What is the recommended debugging sequence? +Confirm profile alignment, isolate logic vs protocol layer, reproduce minimally, trace first divergence, re-test. +Reference: `knowledge/debugging_playbook.md`. + +## 46) How do I inspect module dependencies quickly? +Use `knowledge/module_dependency_map.md` for purpose + dependency + reverse-dependency summaries. + +## 47) Which modules are “top priority” for understanding architecture? +Start with `functions.pl`, AI, Actor, Task, Commands, Network, Receive/PacketParser, Settings/FileParsers, and Globals. +Reference: `knowledge/module_dependency_map.md` Top 20 section. + +## 48) How do I create map-entry automation? +Use eventMacro conditions like `InMap` + `mapLoaded` and issue config/AI commands. +Reference: `knowledge/code_recipes.md`. + +## 49) How do I create monster-proximity reactions? +Use eventMacro presence/distance conditions and trigger defensive actions (e.g., teleport). +Reference: `knowledge/code_recipes.md`. + +## 50) Where can I find practical examples across plugin/macro/config work? +`knowledge/code_recipes.md` contains concise snippets for all three domains. + +## 51) What if a change seems correct but behavior is still wrong? +Re-check server profile + packet table alignment first, then verify module override family (ServerType/kRO/iRO). +Reference: `knowledge/debugging_playbook.md`, `knowledge/networking_packets.md`. + +## 52) How should I keep this knowledge bundle maintainable? +Update high-volatility areas first: networking modules, packet tables, and macro/plugin docs tied to runtime hooks. +Reference: prioritize updates to `knowledge/networking_packets.md`, `knowledge/table_reference.md`, and `knowledge/plugin_system.md` when runtime behavior changes. From ffb619eb99cbd7db486d0456619f167bf8548a82 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Bosc=C3=A1?= <juan_bosca@live.com> Date: Tue, 10 Mar 2026 19:10:50 -0300 Subject: [PATCH 12/18] Add numbered architecture knowledge layer docs --- .../knowledge/01_llm_context.md | 21 ++++++++ .../knowledge/02_architecture_overview.md | 23 +++++++++ .../knowledge/03_system_architecture_map.md | 43 ++++++++++++++++ .../knowledge/04_core_subsystems.md | 49 +++++++++++++++++++ .../knowledge/05_module_dependency_map.md | 29 +++++++++++ .../knowledge/20_code_index.md | 46 +++++++++++++++++ 6 files changed, 211 insertions(+) create mode 100644 openkore_llm_knowledge/knowledge/01_llm_context.md create mode 100644 openkore_llm_knowledge/knowledge/02_architecture_overview.md create mode 100644 openkore_llm_knowledge/knowledge/03_system_architecture_map.md create mode 100644 openkore_llm_knowledge/knowledge/04_core_subsystems.md create mode 100644 openkore_llm_knowledge/knowledge/05_module_dependency_map.md create mode 100644 openkore_llm_knowledge/knowledge/20_code_index.md diff --git a/openkore_llm_knowledge/knowledge/01_llm_context.md b/openkore_llm_knowledge/knowledge/01_llm_context.md new file mode 100644 index 0000000000..cf85d00779 --- /dev/null +++ b/openkore_llm_knowledge/knowledge/01_llm_context.md @@ -0,0 +1,21 @@ +# OpenKore LLM Context + +## Objective +This layer gives an LLM enough structural context to reason about OpenKore runtime behavior, module boundaries, and extension points. + +## Runtime model (high level) +1. `src/functions.pl` boots runtime services, loads config/tables, initializes network mode, and drives the main loop. +2. Network receive handlers (`src/Network/Receive*.pm`) transform packets into world-state updates. +3. Actor state (`src/Actor.pm`, `src/ActorList.pm`) becomes the shared world model for decision logic. +4. AI (`src/AI.pm`, `src/AI/CoreLogic.pm`) evaluates state and schedules actions through tasks. +5. Task execution (`src/TaskManager.pm`, `src/Task/*.pm`) performs multi-step actions and emits packets through send modules (`src/Network/Send*.pm`). +6. Plugins (`src/Plugins.pm`, `plugins/*`) and macro layers (`plugins/macro`, `plugins/eventMacro`) inject automation and custom behavior. + +## Key architecture anchors +- **Entry/control loop**: `src/functions.pl` +- **Global state/config loading**: `src/Globals.pm`, `src/Settings.pm`, `src/FileParsers.pm`, `control/` +- **AI + behavior**: `src/AI.pm`, `src/AI/CoreLogic.pm` +- **Actor model**: `src/Actor.pm`, `src/ActorList.pm`, `src/Actor/*` +- **Task orchestration**: `src/Task.pm`, `src/TaskManager.pm`, `src/Task/*` +- **Networking and packets**: `src/Network.pm`, `src/Network/*` +- **Plugin/macro automation**: `src/Plugins.pm`, `plugins/macro/*`, `plugins/eventMacro/*` diff --git a/openkore_llm_knowledge/knowledge/02_architecture_overview.md b/openkore_llm_knowledge/knowledge/02_architecture_overview.md new file mode 100644 index 0000000000..6bfe238247 --- /dev/null +++ b/openkore_llm_knowledge/knowledge/02_architecture_overview.md @@ -0,0 +1,23 @@ +# OpenKore Architecture Overview + +## Top-level structure +- `src/`: core runtime modules (AI, actor model, networking, tasks, plugins API, command layer). +- `control/`: operator configuration and behavior policies (`config.txt`, control lists, route/macro inputs). +- `tables/`: protocol/game data mappings (packet maps, item/skill/map metadata, server-specific variants). +- `plugins/`: optional extensions, including macro/eventMacro automation stacks. +- `fields/`: map field data used by routing/navigation. + +## Core runtime layers +1. **Bootstrap + loop**: `src/functions.pl` and `src/Modules.pm` +2. **State + config**: `src/Globals.pm`, `src/Settings.pm`, `src/FileParsers.pm` +3. **Network transport + packet translation**: `src/Network.pm`, `src/Network/{Receive,Send,PacketParser}.pm` +4. **World model**: `src/Actor.pm`, `src/ActorList.pm`, `src/Field.pm` +5. **Decision engine**: `src/AI.pm`, `src/AI/CoreLogic.pm` +6. **Execution engine**: `src/TaskManager.pm`, `src/Task/*.pm` +7. **Extension surface**: `src/Plugins.pm`, `src/Commands.pm`, `plugins/*` + +## Architectural characteristics +- **Event-driven and loop-based**: runtime state is advanced each tick by receive updates + AI/task progression. +- **Protocol-adapter design**: packet send/receive classes are split by server type under `src/Network/Receive/*` and `src/Network/Send/*`. +- **Policy outside code**: behavior is heavily configured via `control/` and `tables/` files. +- **Extension-first automation**: macro/eventMacro run as plugins and reuse command/hook/task pathways. diff --git a/openkore_llm_knowledge/knowledge/03_system_architecture_map.md b/openkore_llm_knowledge/knowledge/03_system_architecture_map.md new file mode 100644 index 0000000000..3181b0647f --- /dev/null +++ b/openkore_llm_knowledge/knowledge/03_system_architecture_map.md @@ -0,0 +1,43 @@ +# System Architecture Map (Textual) + +## Primary flow +`functions.pl main loop` -> `Network receive` -> `Actor/Globals update` -> `AI decision` -> `Task execution` -> `Network send` + +## Subsystem map + +### 1) Networking +- Connection/mode handling: `src/Network.pm`, `src/Network/DirectConnection.pm`, `src/Network/XKore*.pm` +- Packet ingestion and dispatch: `src/Network/MessageTokenizer.pm`, `src/Network/PacketParser.pm`, `src/Network/Receive.pm`, `src/Network/Receive/*` +- Packet construction: `src/Network/Send.pm`, `src/Network/Send/*` + +### 2) Actor system +- Base entity model: `src/Actor.pm`, `src/Actor/*` +- Actor collections and lookup: `src/ActorList.pm` +- World/map coupling: `src/Field.pm`, map and position state in globals + +### 3) AI subsystem +- AI stack state and sequencing: `src/AI.pm` +- Core behavior logic: `src/AI/CoreLogic.pm` +- Uses actor/world/config state; issues actions via task manager and commands + +### 4) Task subsystem +- Task abstraction: `src/Task.pm` +- Scheduler and lifecycle: `src/TaskManager.pm` +- High-impact tasks: `src/Task/Route.pm`, `src/Task/Move.pm`, `src/Task/TalkNPC.pm`, `src/Task/UseSkill.pm` + +### 5) Configuration subsystem +- Config discovery/loading: `src/Settings.pm`, `src/FileParsers.pm` +- Runtime shared state: `src/Globals.pm` +- Policy sources: `control/*`, `tables/*` + +### 6) Plugin + macro subsystem +- Hook and plugin lifecycle: `src/Plugins.pm` +- Command bridge: `src/Commands.pm` +- Macro engines: `plugins/macro/*`, `plugins/eventMacro/*` + +## Key entry points +- Startup/main loop: `src/functions.pl` +- Command dispatch: `src/Commands.pm` +- Hook registration: `src/Plugins.pm` +- AI tick path: `src/AI.pm` -> `src/AI/CoreLogic.pm` +- Packet receive path: `src/Network/Receive.pm` + server-specific receivers diff --git a/openkore_llm_knowledge/knowledge/04_core_subsystems.md b/openkore_llm_knowledge/knowledge/04_core_subsystems.md new file mode 100644 index 0000000000..9621268ac6 --- /dev/null +++ b/openkore_llm_knowledge/knowledge/04_core_subsystems.md @@ -0,0 +1,49 @@ +# Core Subsystems + +## AI +- Main modules: `src/AI.pm`, `src/AI/CoreLogic.pm` +- Role: maintain AI queues/state machines, evaluate combat/loot/movement priorities, enqueue tasks. +- Inputs: actor state, config policies, packet-driven updates. +- Outputs: task requests, command invocations, action intents. + +## Actor system +- Main modules: `src/Actor.pm`, `src/ActorList.pm`, `src/Actor/*` +- Role: represent player/NPC/monster/portal entities, with indexed lookup for targeting and proximity logic. +- Inputs: receive handlers and map updates. +- Outputs: query surface for AI, tasks, commands, and plugins. + +## Networking +- Main modules: `src/Network.pm`, `src/Network/{Receive,Send,PacketParser,MessageTokenizer}.pm`, `src/Network/XKore*.pm` +- Role: manage transport mode, parse inbound packets, encode outbound actions, support server-specific packet families. +- Inputs: socket data + outgoing action intents. +- Outputs: state mutations via receive handlers and serialized packets to servers/clients. + +## Task system +- Main modules: `src/Task.pm`, `src/TaskManager.pm`, `src/Task/*` +- Role: run asynchronous multi-step actions (route, move, NPC dialogs, skill/item use). +- Inputs: AI/command/plugin action requests. +- Outputs: packet sends, follow-up tasks, completion/failure states consumed by AI. + +## Plugin system +- Main modules: `src/Plugins.pm`, `plugins/*` +- Role: dynamic extension lifecycle (`register`, hooks, unload), runtime behavior injection. +- Inputs: startup/load events, packet-related hooks, periodic loop hooks. +- Outputs: custom commands, state transitions, automation triggers. + +## Configuration system +- Main modules: `src/Settings.pm`, `src/FileParsers.pm`, `control/*`, `tables/*` +- Role: load policy and data files that parameterize AI, networking, and gameplay handling. +- Inputs: text configs and table files. +- Outputs: normalized runtime config/state in globals and subsystem-specific structures. + +## Macro system +- Main modules: `plugins/macro/macro.pl`, `plugins/macro/Macro/*`, `plugins/eventMacro/eventMacro.pl`, `plugins/eventMacro/eventMacro/*` +- Role: declarative automation over hooks and game events, with conditional triggers and scripted actions. +- Inputs: macro definitions and live runtime events. +- Outputs: command execution and indirect task/network activity. + +## Routing +- Main modules: `src/Task/Route.pm`, `src/Task/Move.pm`, `src/Field.pm`, `fields/*` +- Role: compute and execute navigation paths across map cells/portals. +- Inputs: destination intents, field data, actor position/state. +- Outputs: stepwise movement actions and route completion/fallback states. diff --git a/openkore_llm_knowledge/knowledge/05_module_dependency_map.md b/openkore_llm_knowledge/knowledge/05_module_dependency_map.md new file mode 100644 index 0000000000..35f8eb23cf --- /dev/null +++ b/openkore_llm_knowledge/knowledge/05_module_dependency_map.md @@ -0,0 +1,29 @@ +# Module Dependency Map + +## Dependency chains (high impact) +1. `src/functions.pl` -> `src/Settings.pm` / `src/FileParsers.pm` / `src/Modules.pm` (bootstrap) +2. `src/functions.pl` -> `src/Network.pm` + `src/Network/*` (connection and packet loop) +3. `src/Network/Receive*.pm` -> `src/Globals.pm` + `src/Actor*.pm` (state mutation) +4. `src/AI.pm` -> `src/AI/CoreLogic.pm` -> `src/TaskManager.pm` + `src/Task/*` (decision to execution) +5. `src/Task/*` -> `src/Network/Send*.pm` (action serialization) +6. `src/Plugins.pm` + `plugins/*` -> `src/Commands.pm` / AI / TaskManager (extension control paths) + +## Subsystem dependency view +- **AI depends on**: Actor state, config policies, task scheduler, command surface. +- **Actor system depends on**: Network receive updates and global runtime registries. +- **Task system depends on**: AI/commands/plugins for intents; network send + actor/map updates for progression. +- **Plugin/macro depends on**: hook lifecycle (`src/Plugins.pm`), command execution (`src/Commands.pm`), global state. +- **Routing depends on**: task framework, field data (`fields/*`), movement packet sends. + +## Coupling hotspots +- `src/Globals.pm`: shared mutable state touched by receive, AI, and plugins. +- `src/Commands.pm`: cross-cutting control surface used by user input and automation. +- `src/TaskManager.pm`: convergence point for AI and scripted automation. +- `src/Network/Receive.pm`: ingress bridge from protocol events to internal state transitions. + +## Practical navigation order for analysis +1. Start at `src/functions.pl`. +2. Follow receive path (`src/Network/Receive.pm`, subtype receivers). +3. Inspect AI loop (`src/AI.pm`, `src/AI/CoreLogic.pm`). +4. Inspect task execution (`src/TaskManager.pm`, route/move/NPC tasks). +5. Inspect extension points (`src/Plugins.pm`, macro/eventMacro plugins). diff --git a/openkore_llm_knowledge/knowledge/20_code_index.md b/openkore_llm_knowledge/knowledge/20_code_index.md new file mode 100644 index 0000000000..d0b179e3f4 --- /dev/null +++ b/openkore_llm_knowledge/knowledge/20_code_index.md @@ -0,0 +1,46 @@ +# Code Index (Architecture-Oriented) + +## Runtime entry and orchestration +- `src/functions.pl` — startup sequence and main loop +- `src/Modules.pm` — module loading registry +- `src/Globals.pm` — shared runtime state container + +## AI +- `src/AI.pm` — AI framework and queue/state management +- `src/AI/CoreLogic.pm` — core decision routines + +## Actor and world model +- `src/Actor.pm` — actor base model +- `src/ActorList.pm` — actor collections/indexing +- `src/Actor/*` — actor specializations +- `src/Field.pm` — map/field representation + +## Task execution +- `src/Task.pm` — task abstraction +- `src/TaskManager.pm` — scheduler/executor +- `src/Task/Route.pm` — route planning/execution wrapper +- `src/Task/Move.pm` — movement task +- `src/Task/TalkNPC.pm` — NPC dialogue workflow +- `src/Task/UseSkill.pm` — skill-use workflows + +## Networking and packets +- `src/Network.pm` — network facade and state +- `src/Network/DirectConnection.pm` — direct server mode +- `src/Network/XKore.pm`, `src/Network/XKore2.pm`, `src/Network/XKoreProxy.pm` — proxy/bridge modes +- `src/Network/MessageTokenizer.pm` — packet frame/token extraction +- `src/Network/PacketParser.pm` — packet definition parsing +- `src/Network/Receive.pm` + `src/Network/Receive/*` — inbound packet dispatch/handlers +- `src/Network/Send.pm` + `src/Network/Send/*` — outbound packet builders + +## Commands, plugins, and automation +- `src/Commands.pm` — command parser/dispatcher +- `src/Plugins.pm` — plugin lifecycle and hook API +- `plugins/macro/macro.pl` + `plugins/macro/Macro/*` — legacy macro automation +- `plugins/eventMacro/eventMacro.pl` + `plugins/eventMacro/eventMacro/*` — event-driven macro automation + +## Configuration and data sources +- `src/Settings.pm` — config path and file registration +- `src/FileParsers.pm` — config/table parsing +- `control/*` — user behavior policies and runtime options +- `tables/*` — server/data tables +- `fields/*` — map grids for routing/navigation From b6282b0d17624395d51f5d73570f18bf6345ae96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Bosc=C3=A1?= <juan_bosca@live.com> Date: Tue, 10 Mar 2026 19:14:37 -0300 Subject: [PATCH 13/18] Add runtime flow docs with Mermaid architecture diagrams --- .../knowledge/06_execution_flows.md | 29 +++++++++++++++ .../knowledge/07_npc_interaction_flow.md | 26 ++++++++++++++ .../knowledge/08_routing_and_movement.md | 20 +++++++++++ .../knowledge/09_networking_packets.md | 22 ++++++++++++ .../knowledge/10_xkore_modes.md | 20 +++++++++++ .../knowledge/26_architecture_diagrams.md | 17 +++++++++ .../knowledge/27_system_flows.md | 35 +++++++++++++++++++ 7 files changed, 169 insertions(+) create mode 100644 openkore_llm_knowledge/knowledge/06_execution_flows.md create mode 100644 openkore_llm_knowledge/knowledge/07_npc_interaction_flow.md create mode 100644 openkore_llm_knowledge/knowledge/08_routing_and_movement.md create mode 100644 openkore_llm_knowledge/knowledge/09_networking_packets.md create mode 100644 openkore_llm_knowledge/knowledge/10_xkore_modes.md create mode 100644 openkore_llm_knowledge/knowledge/26_architecture_diagrams.md create mode 100644 openkore_llm_knowledge/knowledge/27_system_flows.md diff --git a/openkore_llm_knowledge/knowledge/06_execution_flows.md b/openkore_llm_knowledge/knowledge/06_execution_flows.md new file mode 100644 index 0000000000..cac52ed2b0 --- /dev/null +++ b/openkore_llm_knowledge/knowledge/06_execution_flows.md @@ -0,0 +1,29 @@ +# Execution Flows + +## Runtime execution loop +OpenKore runs a tick-oriented loop rooted in `src/functions.pl`, where network ingestion, state updates, AI decisions, and task execution are iterated continuously. + +Operational sequence: +1. Main loop tick in `src/functions.pl` advances network, AI, and task phases. +2. Receive handlers (`src/Network/Receive.pm`, `src/Network/Receive/*`) apply packet-driven state updates. +3. Shared state (`src/Globals.pm`, `src/ActorList.pm`) becomes input for AI logic. +4. AI (`src/AI.pm`, `src/AI/CoreLogic.pm`) chooses actions and updates task queues. +5. Tasks (`src/TaskManager.pm`, `src/Task/*`) execute stepwise and emit outgoing packets through send modules. + +## AI execution loop + +```mermaid +flowchart TD + A[AI tick\nsrc/AI.pm] --> B[Gather context\nGlobals + ActorList + config] + B --> C[Core logic\nsrc/AI/CoreLogic.pm] + C --> D{Action required?} + D -- No --> E[Idle/monitor state] + D -- Yes --> F[Create/adjust tasks\nsrc/TaskManager.pm] + F --> G[Run task step\nsrc/Task/*.pm] + G --> H[Emit commands/packets\nCommands + Network::Send] + H --> I[Wait for receive updates] + E --> I + I --> A +``` + +AI is decision-oriented; tasks hold execution state between ticks while receive handlers provide feedback. diff --git a/openkore_llm_knowledge/knowledge/07_npc_interaction_flow.md b/openkore_llm_knowledge/knowledge/07_npc_interaction_flow.md new file mode 100644 index 0000000000..48d5f80007 --- /dev/null +++ b/openkore_llm_knowledge/knowledge/07_npc_interaction_flow.md @@ -0,0 +1,26 @@ +# NPC Interaction Flow + +NPC interactions are executed as task-driven conversations, typically using `src/Task/TalkNPC.pm` and packet handlers under `src/Network/Receive/*.pm`. + +```mermaid +sequenceDiagram + participant U as AI/Command/Plugin + participant TM as TaskManager + participant TN as Task::TalkNPC + participant NS as Network::Send + participant S as Ragnarok Server + participant NR as Network::Receive + participant G as Globals/Actor state + + U->>TM: enqueue NPC interaction + TM->>TN: start dialogue task + TN->>NS: send talk/response packet + NS->>S: outbound packet + S->>NR: NPC response packet + NR->>G: update dialogue/state flags + NR->>TM: task-relevant updates + TM->>TN: advance next dialogue step + TN-->>TM: complete/fail +``` + +`Task::TalkNPC` keeps dialogue progression explicit, while receive handlers synchronize server-side conversation state back into runtime state. diff --git a/openkore_llm_knowledge/knowledge/08_routing_and_movement.md b/openkore_llm_knowledge/knowledge/08_routing_and_movement.md new file mode 100644 index 0000000000..a1e0b0cf5f --- /dev/null +++ b/openkore_llm_knowledge/knowledge/08_routing_and_movement.md @@ -0,0 +1,20 @@ +# Routing and Movement + +Routing converts destination intent into path segments and movement packets using `src/Task/Route.pm`, `src/Task/Move.pm`, `src/Field.pm`, and `fields/*` data. + +```mermaid +flowchart TD + A[AI/Command target\nmap + coordinates] --> B[Task::Route init] + B --> C[Load field graph\nsrc/Field.pm + fields/*] + C --> D[Compute path segments] + D --> E[Task::Move executes step] + E --> F[Send move packet\nsrc/Network/Send.pm] + F --> G[Receive position update\nsrc/Network/Receive.pm] + G --> H{Reached waypoint?} + H -- No --> E + H -- Yes --> I{Reached destination?} + I -- No --> D + I -- Yes --> J[Route complete] +``` + +Routing is iterative and feedback-driven: each movement step is validated by receive updates before continuing. diff --git a/openkore_llm_knowledge/knowledge/09_networking_packets.md b/openkore_llm_knowledge/knowledge/09_networking_packets.md new file mode 100644 index 0000000000..a72e17de20 --- /dev/null +++ b/openkore_llm_knowledge/knowledge/09_networking_packets.md @@ -0,0 +1,22 @@ +# Networking and Packet Flow + +OpenKore packet handling combines framing/tokenization, opcode parsing, and server-type receive/send classes under `src/Network/*`. + +## Packet receive flow + +```mermaid +flowchart TD + A[Socket bytes\nNetwork mode] --> B[MessageTokenizer\nsrc/Network/MessageTokenizer.pm] + B --> C[PacketParser\nsrc/Network/PacketParser.pm] + C --> D[Receive dispatcher\nsrc/Network/Receive.pm] + D --> E[Server-type handler\nsrc/Network/Receive/*] + E --> F[Runtime mutation\nGlobals + Actor + Task signals] + F --> G[Hooks/plugins notified\nsrc/Plugins.pm] +``` + +Receive processing turns protocol frames into domain-level state transitions used by AI and tasks. + +## Packet send path (supporting context) +- Action request sources: AI, commands, task modules, plugins/macros. +- Encoding path: `src/Network/Send.pm` -> `src/Network/Send/*` (server-type specific structures). +- Transport path: active mode (`DirectConnection`, `XKore`, `XKore2`, or `XKoreProxy`) in `src/Network/*.pm`. diff --git a/openkore_llm_knowledge/knowledge/10_xkore_modes.md b/openkore_llm_knowledge/knowledge/10_xkore_modes.md new file mode 100644 index 0000000000..4f0d773936 --- /dev/null +++ b/openkore_llm_knowledge/knowledge/10_xkore_modes.md @@ -0,0 +1,20 @@ +# XKore Modes + +XKore modes define how OpenKore connects to the game client/server pipeline. + +## Core modules +- `src/Network/XKore.pm` +- `src/Network/XKore2.pm` +- `src/Network/XKoreProxy.pm` +- `src/Network/DirectConnection.pm` (baseline comparison) + +## Mode summary +- **DirectConnection**: bot connects directly to game servers. +- **XKore**: client-assisted mode using forwarding/bridging logic. +- **XKore2**: OpenKore hosts local account/char/map proxy endpoints (`src/Network/XKore2/*`) and relays traffic. +- **XKoreProxy**: proxy-style mediation path for packet bridging. + +## Architectural impact +- All modes reuse packet parsing/dispatch and send modules (`src/Network/{Receive,Send}.pm`). +- Mode choice primarily changes transport/session orchestration and how packets are sourced/sinked. +- AI, tasks, actor state, plugins, and macro systems remain mode-agnostic above the network abstraction. diff --git a/openkore_llm_knowledge/knowledge/26_architecture_diagrams.md b/openkore_llm_knowledge/knowledge/26_architecture_diagrams.md new file mode 100644 index 0000000000..54654be8e2 --- /dev/null +++ b/openkore_llm_knowledge/knowledge/26_architecture_diagrams.md @@ -0,0 +1,17 @@ +# Architecture Diagrams (Mermaid) + +This file indexes the runtime diagrams for OpenKore subsystem interactions. + +## Included diagrams +1. **AI execution loop** — `06_execution_flows.md` +2. **Packet receive flow** — `09_networking_packets.md` +3. **NPC interaction flow** — `07_npc_interaction_flow.md` +4. **Plugin hook execution** — `27_system_flows.md` +5. **Routing and movement flow** — `08_routing_and_movement.md` +6. **Command execution flow** — `27_system_flows.md` + +## Reading guidance +- Start with `06_execution_flows.md` for the main runtime cycle. +- Use `09_networking_packets.md` and `10_xkore_modes.md` for transport/protocol context. +- Use `07_npc_interaction_flow.md` and `08_routing_and_movement.md` for task-level behavioral flows. +- Use `27_system_flows.md` for extension and control-plane flows. diff --git a/openkore_llm_knowledge/knowledge/27_system_flows.md b/openkore_llm_knowledge/knowledge/27_system_flows.md new file mode 100644 index 0000000000..61357e961e --- /dev/null +++ b/openkore_llm_knowledge/knowledge/27_system_flows.md @@ -0,0 +1,35 @@ +# System Flows + +## Plugin hook execution flow +Plugins extend runtime behavior through hook callbacks registered in `src/Plugins.pm` and commonly invoke `src/Commands.pm` or task APIs. + +```mermaid +flowchart TD + A[Plugin load\nplugins/*.pl] --> B[Plugins::register] + B --> C[Plugins::addHooks] + C --> D[Runtime event\nloop/packet/config] + D --> E[Plugin callback] + E --> F{Action needed?} + F -- No --> G[Return] + F -- Yes --> H[Execute command\nsrc/Commands.pm] + H --> I[Task/Network side effects] + I --> G +``` + +Hook callbacks are event-driven and should remain lightweight; heavy operations are usually delegated to commands/tasks. + +## Command execution flow + +```mermaid +flowchart TD + A[Input source\nconsole/plugin/macro] --> B[Commands::run] + B --> C[Command parser\nsrc/Commands.pm] + C --> D{Built-in or plugin cmd?} + D -- Built-in --> E[Built-in handler] + D -- Plugin --> F[Plugin command callback] + E --> G[Update state / queue task / send packet] + F --> G + G --> H[Feedback to interface/log] +``` + +Commands are a shared control surface across manual operations, plugins, and macro automation. From e8b1f4b0c9df383c027578dd805a81e5105f9374 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Bosc=C3=A1?= <juan_bosca@live.com> Date: Tue, 10 Mar 2026 19:22:06 -0300 Subject: [PATCH 14/18] Add debugging playbook, decision trees, and pitfalls docs --- .../knowledge/15_debugging_playbook.md | 185 ++++++++++++++++++ .../knowledge/16_debug_decision_trees.md | 90 +++++++++ .../knowledge/22_common_pitfalls.md | 49 +++++ 3 files changed, 324 insertions(+) create mode 100644 openkore_llm_knowledge/knowledge/15_debugging_playbook.md create mode 100644 openkore_llm_knowledge/knowledge/16_debug_decision_trees.md create mode 100644 openkore_llm_knowledge/knowledge/22_common_pitfalls.md diff --git a/openkore_llm_knowledge/knowledge/15_debugging_playbook.md b/openkore_llm_knowledge/knowledge/15_debugging_playbook.md new file mode 100644 index 0000000000..c826ebf7c4 --- /dev/null +++ b/openkore_llm_knowledge/knowledge/15_debugging_playbook.md @@ -0,0 +1,185 @@ +# Debugging Playbook + +This playbook maps common OpenKore runtime failures to likely causes, inspection points, and step-by-step troubleshooting. + +--- + +## 1) Bot not moving +- **Likely subsystem**: AI + Task + Routing + Network Send +- **Possible causes**: + - AI state blocked (attack/dialog/task lock) + - Route task never created or immediately failing + - Movement packets not being sent or rejected + - Movement-related config prevents walking +- **Files to inspect**: + - `src/AI.pm`, `src/AI/CoreLogic.pm` + - `src/TaskManager.pm`, `src/Task/Move.pm`, `src/Task/Route.pm` + - `src/Network/Send.pm`, `src/Network/Receive.pm` +- **Config to inspect**: + - `control/config.txt` (movement/lockMap/route-related options) + - `control/mon_control.txt` (behavior that can pin combat state) +- **Debugging steps**: + 1. Confirm AI is ticking and not paused/stuck in a higher-priority state. + 2. Check whether `Task::Route`/`Task::Move` is queued in task manager. + 3. Validate that move packets are emitted by send path. + 4. Confirm receive updates are acknowledging position changes. + 5. Reduce constraints in config (lock-only behavior, avoid lists) and retest. + +## 2) Routing failure +- **Likely subsystem**: Routing + Field data + Task +- **Possible causes**: + - Missing/incorrect field data + - Blocked cells/portal transitions not resolved + - Route recomputation loops without progress +- **Files to inspect**: + - `src/Task/Route.pm`, `src/Task/Move.pm`, `src/Field.pm` +- **Config to inspect**: + - `control/config.txt` (route/move flags) + - `fields/*` (map walkability data) +- **Debugging steps**: + 1. Verify map field exists and corresponds to current map. + 2. Check route generation result (empty path or oscillating path). + 3. Confirm position updates are fresh (no stale coordinates). + 4. Test shorter route segments to isolate failing region. + 5. Re-check portal/map transition assumptions. + +## 3) NPC interaction failure +- **Likely subsystem**: Task + Network Receive/Send + Actor/NPC state +- **Possible causes**: + - Wrong NPC coordinates or stale actor reference + - Dialogue step mismatch with server response + - Talk packets not sent or incorrect sequence +- **Files to inspect**: + - `src/Task/TalkNPC.pm` + - `src/Network/Send.pm`, `src/Network/Receive.pm`, `src/Network/Receive/*` + - `src/ActorList.pm` +- **Config to inspect**: + - `control/config.txt` (npc interaction directives/scripts) +- **Debugging steps**: + 1. Confirm NPC exists in actor list at expected coordinates. + 2. Trace talk packet send sequence against expected dialogue steps. + 3. Verify receive handlers parse response packet IDs correctly for server type. + 4. Reproduce with minimal NPC script/task to reduce branching. + +## 4) Plugin not loading +- **Likely subsystem**: Plugin system + startup/module loading +- **Possible causes**: + - Syntax/runtime error in plugin file + - Incorrect plugin path/name + - Missing dependency module used by plugin +- **Files to inspect**: + - `src/Plugins.pm`, `src/Modules.pm` + - `plugins/<plugin_name>/*.pl` +- **Config to inspect**: + - `control/config.txt` (plugin enable/load entries) +- **Debugging steps**: + 1. Check startup logs for plugin load exceptions. + 2. Validate plugin registers with `Plugins::register`. + 3. Confirm plugin path and filename match expected loader behavior. + 4. Remove optional dependency imports to isolate failing require/use. + +## 5) Plugin hook not firing +- **Likely subsystem**: Plugin hooks + event dispatch +- **Possible causes**: + - Hook registered with wrong event name + - Hook callback blocked by guard condition + - Expected event never emitted in current runtime path +- **Files to inspect**: + - `src/Plugins.pm`, `src/functions.pl` + - Plugin file defining `Plugins::addHooks` +- **Config to inspect**: + - `control/config.txt` (conditions controlling event generation) +- **Debugging steps**: + 1. List hooks registered by plugin after load. + 2. Add lightweight logging at callback entry. + 3. Verify event name spelling and payload assumptions. + 4. Trigger event manually through a minimal reproduction path. + +## 6) Macro not triggering +- **Likely subsystem**: Macro plugin + command/hook bridge +- **Possible causes**: + - Macro file not loaded + - Automacro condition never true + - Macro command blocked by runtime state +- **Files to inspect**: + - `plugins/macro/macro.pl`, `plugins/macro/Macro/*` + - `src/Commands.pm`, `src/Plugins.pm` +- **Config to inspect**: + - `control/config.txt` (`macro_file` and related keys) + - macro definition file referenced by config +- **Debugging steps**: + 1. Confirm macro plugin loaded and macro file parsed. + 2. Validate automacro conditions against live state. + 3. Execute macro manually to separate trigger vs action issues. + 4. Inspect command dispatch path for blocked/invalid command. + +## 7) eventMacro issues +- **Likely subsystem**: eventMacro plugin + event parser/runner +- **Possible causes**: + - Invalid eventMacro syntax + - Trigger conditions not matching event payload + - Runner stalled by previous action/wait state +- **Files to inspect**: + - `plugins/eventMacro/eventMacro.pl` + - `plugins/eventMacro/eventMacro/{Core,Runner,Automacro,FileParser}.pm` +- **Config to inspect**: + - `control/config.txt` (`eventMacro_file`) + - eventMacro script file +- **Debugging steps**: + 1. Validate eventMacro file parsing with minimal script. + 2. Log trigger evaluation for target automacro. + 3. Check runner queue and pending wait/time gates. + 4. Reduce script to one trigger + one action and retest. + +## 8) Packet desync +- **Likely subsystem**: Networking receive/parser + server-type mappings +- **Possible causes**: + - Wrong server type packet tables + - Opcode map mismatch after server update + - Packet boundary/tokenization errors +- **Files to inspect**: + - `src/Network/MessageTokenizer.pm`, `src/Network/PacketParser.pm` + - `src/Network/Receive.pm`, `src/Network/Receive/*` + - `src/Network/Send.pm`, `src/Network/Send/*` +- **Config to inspect**: + - `control/config.txt` (serverType/recvpackets settings) + - `tables/*` packet definition files for the target server +- **Debugging steps**: + 1. Confirm selected serverType and recvpackets data. + 2. Compare failing opcode decode against current server packet definitions. + 3. Capture raw packet sequence around first desync point. + 4. Verify both receive and send side use compatible packet maps. + +## 9) XKore sync issues +- **Likely subsystem**: XKore transport/session bridging +- **Possible causes**: + - Client-proxy handshake mismatch + - Account/char/map relay state divergence + - Timing/forwarding issues in XKore mode +- **Files to inspect**: + - `src/Network/XKore.pm`, `src/Network/XKore2.pm`, `src/Network/XKoreProxy.pm` + - `src/Network/XKore2/{AccountServer,CharServer,MapServer}.pm` +- **Config to inspect**: + - `control/config.txt` (XKore mode and connection settings) +- **Debugging steps**: + 1. Confirm active mode (Direct vs XKore/XKore2/XKoreProxy). + 2. Trace handshake/login flow across account->char->map stages. + 3. Check for asymmetric forwarding (client sees state that bot does not, or inverse). + 4. Validate mode-specific ports/bind settings and restart cleanly. + +## 10) Visual client state not updating +- **Likely subsystem**: XKore bridge + packet forwarding + actor sync +- **Possible causes**: + - Forwarded packets not reaching client + - Actor state updated in bot but not mirrored to client channel + - Client session stuck after map transition +- **Files to inspect**: + - `src/Network/XKore*.pm` + - `src/Network/Receive.pm`, `src/ActorList.pm` +- **Config to inspect**: + - `control/config.txt` (XKore forwarding/session config) +- **Debugging steps**: + 1. Verify bot-side state is changing (position/actors) first. + 2. Confirm corresponding packets are forwarded to client path. + 3. Re-check session phase (account/char/map) for stuck transition. + 4. Reconnect client through same mode to re-establish synchronization. diff --git a/openkore_llm_knowledge/knowledge/16_debug_decision_trees.md b/openkore_llm_knowledge/knowledge/16_debug_decision_trees.md new file mode 100644 index 0000000000..8869941f85 --- /dev/null +++ b/openkore_llm_knowledge/knowledge/16_debug_decision_trees.md @@ -0,0 +1,90 @@ +# Debug Decision Trees + +These trees provide fast triage paths for recurring OpenKore failures. + +## A) Bot not moving / Routing failure + +```mermaid +flowchart TD + A[Bot not moving] --> B{AI ticking?} + B -- No --> B1[Inspect src/AI.pm and main loop in src/functions.pl] + B -- Yes --> C{Task::Route or Task::Move queued?} + C -- No --> C1[Inspect AI decisions in src/AI/CoreLogic.pm and config gates] + C -- Yes --> D{Move packets sent?} + D -- No --> D1[Inspect src/Network/Send.pm and command/task path] + D -- Yes --> E{Position updates received?} + E -- No --> E1[Inspect src/Network/Receive.pm and packet mapping] + E -- Yes --> F{Path valid in fields data?} + F -- No --> F1[Inspect src/Field.pm + fields/*] + F -- Yes --> G[Check blockers/lock settings in control/config.txt] +``` + +Use this when movement appears frozen or route tasks fail repeatedly. + +## B) NPC interaction failure + +```mermaid +flowchart TD + A[NPC interaction fails] --> B{NPC present in ActorList?} + B -- No --> B1[Inspect src/ActorList.pm updates from receive handlers] + B -- Yes --> C{TalkNPC task created?} + C -- No --> C1[Inspect trigger path in AI/commands/plugins] + C -- Yes --> D{Talk packets emitted?} + D -- No --> D1[Inspect src/Task/TalkNPC.pm + src/Network/Send.pm] + D -- Yes --> E{Expected response packet decoded?} + E -- No --> E1[Inspect src/Network/Receive/* serverType handlers] + E -- Yes --> F[Validate dialogue sequence assumptions in task script] +``` + +Use this when NPC talks stop, loop, or complete with wrong branch. + +## C) Plugin not loading / Hook not firing + +```mermaid +flowchart TD + A[Plugin issue] --> B{Plugin loads successfully?} + B -- No --> B1[Check syntax/dependencies + src/Plugins.pm loader logs] + B -- Yes --> C{Hook registered via Plugins::addHooks?} + C -- No --> C1[Fix registration block in plugin file] + C -- Yes --> D{Event emitted at runtime?} + D -- No --> D1[Inspect event source in src/functions.pl / network paths] + D -- Yes --> E{Callback guard conditions pass?} + E -- No --> E1[Log callback inputs and config-dependent guards] + E -- Yes --> F[Inspect side effects via Commands/TaskManager] +``` + +Use this for both plugin boot failures and silent hooks. + +## D) Macro/eventMacro not triggering + +```mermaid +flowchart TD + A[Macro/eventMacro not triggering] --> B{Plugin loaded?} + B -- No --> B1[Inspect plugins/macro or plugins/eventMacro load path] + B -- Yes --> C{Script file parsed?} + C -- No --> C1[Validate macro_file/eventMacro_file in control/config.txt] + C -- Yes --> D{Trigger condition true in live state?} + D -- No --> D1[Log variables/events used by automacro] + D -- Yes --> E{Action executes manually?} + E -- No --> E1[Inspect command path in src/Commands.pm] + E -- Yes --> F[Investigate scheduler/wait constraints in runner] +``` + +Use this to isolate parser issues from trigger logic and action execution. + +## E) Packet desync / XKore sync / visual client desync + +```mermaid +flowchart TD + A[Desync observed] --> B{Wrong serverType/packet table?} + B -- Yes --> B1[Fix control/config.txt + tables/* packet mappings] + B -- No --> C{Receive decode errors?} + C -- Yes --> C1[Inspect MessageTokenizer/PacketParser/Receive handlers] + C -- No --> D{Only in XKore mode?} + D -- No --> D1[Inspect send/receive parity and opcode updates] + D -- Yes --> E{Client and bot state diverge?} + E -- Yes --> E1[Inspect src/Network/XKore*.pm forwarding/session state] + E -- No --> F[Inspect map-transition/session phase sync] +``` + +Use this for protocol mismatches and client-bridge synchronization drift. diff --git a/openkore_llm_knowledge/knowledge/22_common_pitfalls.md b/openkore_llm_knowledge/knowledge/22_common_pitfalls.md new file mode 100644 index 0000000000..66dbbe6238 --- /dev/null +++ b/openkore_llm_knowledge/knowledge/22_common_pitfalls.md @@ -0,0 +1,49 @@ +# Common Pitfalls + +Concise checklist of high-frequency debugging traps in OpenKore architecture and operations. + +## 1) Assuming movement issues are only routing bugs +- AI state locks, command overrides, or task queue starvation can mimic routing failures. +- Check AI/task state before editing route logic. + +## 2) Ignoring serverType/packet table drift +- Packet desync often starts after server updates when recvpackets/opcodes change. +- Keep `control/config.txt` server settings aligned with `tables/*` packet definitions. + +## 3) Debugging plugin hooks without proving event emission +- A hook can be correct but never run if the event is not emitted in the current flow. +- Verify both registration and actual event source path. + +## 4) Treating macro trigger failures as parser failures +- Many cases are valid parse + false trigger conditions. +- Separate: plugin load -> file parse -> trigger true -> action execution. + +## 5) Overlooking config-side constraints +- `control/config.txt`, `mon_control.txt`, and related files can disable or redirect behavior. +- Always test with minimal, known-good config for reproduction. + +## 6) Mixing XKore and DirectConnection assumptions +- Transport/session behavior differs; debugging steps are mode-specific. +- Confirm active mode first before tracing packet/session paths. + +## 7) Trusting visual client state over bot internal state +- In XKore scenarios, client view may lag/diverge from bot state. +- Compare bot-side actor/position updates with forwarded client packets. + +## 8) Investigating deep modules before reproducing minimally +- Large automation stacks (AI + plugin + macro + eventMacro) hide root causes. +- Reproduce with minimal script/task/command to localize fault domain. + +## Fast isolation matrix +| Symptom | First subsystem to verify | Primary files | +|---|---|---| +| Bot not moving | AI/Task | `src/AI.pm`, `src/TaskManager.pm` | +| Route fails | Routing/Field | `src/Task/Route.pm`, `src/Field.pm` | +| NPC fails | Task + Receive | `src/Task/TalkNPC.pm`, `src/Network/Receive.pm` | +| Plugin not loading | Plugin loader | `src/Plugins.pm`, plugin file | +| Hook silent | Event dispatch | `src/Plugins.pm`, event source module | +| Macro not triggering | Macro pipeline | `plugins/macro/*`, `src/Commands.pm` | +| eventMacro issues | eventMacro runner | `plugins/eventMacro/eventMacro/*` | +| Packet desync | Packet mapping | `src/Network/PacketParser.pm`, `tables/*` | +| XKore sync issues | XKore bridge | `src/Network/XKore*.pm` | +| Client view stale | XKore forwarding | `src/Network/XKore*.pm`, `src/ActorList.pm` | From 641c5d1f1d8865ccf69bea2a187b702fb6f9260b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Bosc=C3=A1?= <juan_bosca@live.com> Date: Tue, 10 Mar 2026 19:22:11 -0300 Subject: [PATCH 15/18] Add practical development, FAQ, and onboarding knowledge docs --- .../knowledge/17_code_recipes.md | 147 ++++++++++++++++++ .../knowledge/18_openkore_faq.md | 147 ++++++++++++++++++ .../knowledge/19_glossary.md | 27 ++++ .../knowledge/24_learning_path.md | 49 ++++++ .../knowledge/29_prompt_examples_for_users.md | 49 ++++++ .../knowledge/30_dataset_summary.md | 37 +++++ 6 files changed, 456 insertions(+) create mode 100644 openkore_llm_knowledge/knowledge/17_code_recipes.md create mode 100644 openkore_llm_knowledge/knowledge/18_openkore_faq.md create mode 100644 openkore_llm_knowledge/knowledge/19_glossary.md create mode 100644 openkore_llm_knowledge/knowledge/24_learning_path.md create mode 100644 openkore_llm_knowledge/knowledge/29_prompt_examples_for_users.md create mode 100644 openkore_llm_knowledge/knowledge/30_dataset_summary.md diff --git a/openkore_llm_knowledge/knowledge/17_code_recipes.md b/openkore_llm_knowledge/knowledge/17_code_recipes.md new file mode 100644 index 0000000000..ba7d8fa816 --- /dev/null +++ b/openkore_llm_knowledge/knowledge/17_code_recipes.md @@ -0,0 +1,147 @@ +# Code Recipes + +Practical snippets for common OpenKore development tasks. + +## 1) Minimal plugin skeleton +```perl +package myPlugin; + +use strict; +use Plugins; +use Log qw(message warning error); + +my $hooks = []; + +Plugins::register('myPlugin', 'Minimal plugin skeleton', \&on_unload); +$hooks = Plugins::addHooks( + ['start3', \&on_start], + ['AI_pre', \&on_ai_pre], +); + +sub on_start { + message "[myPlugin] started\n", 'info'; +} + +sub on_ai_pre { + my ($hookName, $args) = @_; + # lightweight periodic logic +} + +sub on_unload { + Plugins::delHooks($hooks); + message "[myPlugin] unloaded\n", 'info'; +} + +1; +``` + +## 2) Hook registration pattern +```perl +my $hooks = Plugins::addHooks( + ['packet_pre/actor_moved', \&on_actor_moved], + ['packet/map_changed', \&on_map_changed], + ['configModify', \&on_config_modify], +); + +sub on_actor_moved { + my ($hook, $args) = @_; + # inspect $args->{ID}, $args->{coords} +} +``` + +## 3) Command registration +```perl +Commands::register( + ['mycmd', 'run custom action', \&cmd_mycmd] +); + +sub cmd_mycmd { + my (undef, $args) = @_; + message "[myPlugin] mycmd args: $args\n", 'info'; +} + +# on unload: +Commands::unregister('mycmd'); +``` + +## 4) Packet hook example +```perl +my $hooks = Plugins::addHooks( + ['packet_pre/login_error', \&on_login_error], + ['packet/map_changed', \&on_map_changed], +); + +sub on_login_error { + my ($hook, $args) = @_; + warning "Login error packet received: $args->{type}\n"; +} +``` + +## 5) Macro example (`macros.txt`) +```txt +automacro autoHeal { + hp < 40% + timeout 1 + call { + do ss 28 + } +} + +macro goTown { + do move prontera +} +``` + +## 6) eventMacro example +```txt +automacro checkWeight { + InLockMap 1 + weight > 85% + call { + do conf itemsTakeAuto 0 + do ai clear + } +} + +automacro greetOnMap { + map prontera + run-once 1 + call { + do c Hello from OpenKore + } +} +``` + +## 7) Configuration examples (`control/config.txt`) +```txt +lockMap prontera +saveMap prontera +route_randomWalk 1 +teleportAuto_hp 25 +itemsTakeAuto 2 +attackAuto 2 +macro_file macros.txt +eventMacro_file eventMacros.txt +``` + +## 8) Debugging snippets +### 8.1 Print current map and coordinates +```perl +use Globals qw($field %char); +message sprintf("Map=%s x=%d y=%d\n", $field->baseName, $char{pos_to}{x}, $char{pos_to}{y}), 'debug'; +``` + +### 8.2 Trace command callback entry +```perl +sub cmd_mycmd { + my (undef, $args) = @_; + message "[TRACE] cmd_mycmd called with '$args'\n", 'debug'; +} +``` + +### 8.3 Check task queue state quickly +```perl +use TaskManager; +my $task = TaskManager::getTaskManager()->activeTask; +message "Active task: " . ($task ? ref($task) : 'none') . "\n", 'debug'; +``` diff --git a/openkore_llm_knowledge/knowledge/18_openkore_faq.md b/openkore_llm_knowledge/knowledge/18_openkore_faq.md new file mode 100644 index 0000000000..c3a6760545 --- /dev/null +++ b/openkore_llm_knowledge/knowledge/18_openkore_faq.md @@ -0,0 +1,147 @@ +# OpenKore Technical FAQ + +## Architecture +1. **Q:** What is OpenKore's main runtime entry point? + **A:** `src/functions.pl` initializes modules and runs the main loop. +2. **Q:** Where is global runtime state stored? + **A:** Mostly in `src/Globals.pm` and shared structures. +3. **Q:** Which module runs AI logic? + **A:** `src/AI.pm` coordinates AI and calls `src/AI/CoreLogic.pm`. +4. **Q:** How are game entities represented? + **A:** Through `src/Actor.pm` and `src/ActorList.pm`. +5. **Q:** What handles tasks? + **A:** `src/Task.pm` defines tasks and `src/TaskManager.pm` schedules them. +6. **Q:** Where does command parsing happen? + **A:** In `src/Commands.pm`. +7. **Q:** How are plugins managed? + **A:** Via `src/Plugins.pm` lifecycle and hooks API. +8. **Q:** Why is architecture loop-based? + **A:** To repeatedly process receive->state->AI->tasks->send cycles. + +## Debugging +9. **Q:** First check when bot freezes? + **A:** Confirm AI tick and active task state. +10. **Q:** How to distinguish route vs movement failure? + **A:** Check if a route exists, then confirm move packets and position updates. +11. **Q:** Why log packet IDs during failures? + **A:** To detect serverType/recvpackets mismatch quickly. +12. **Q:** Best way to debug plugin hooks? + **A:** Log both hook registration and callback entry. +13. **Q:** Why test with minimal config? + **A:** To remove config side effects masking root causes. +14. **Q:** What indicates desync risk? + **A:** Decode errors, unknown packets, or stale actor updates. +15. **Q:** How to verify task deadlock? + **A:** Inspect active task and queued tasks in task manager. +16. **Q:** Should I debug with all plugins enabled? + **A:** No, isolate with minimal plugin set first. + +## Plugins +17. **Q:** Minimum plugin requirements? + **A:** `Plugins::register`, optional hooks, and clean unload. +18. **Q:** How to add a custom command? + **A:** Use `Commands::register` and unregister on unload. +19. **Q:** Why might plugin load fail silently? + **A:** Early syntax/runtime errors or missing dependencies. +20. **Q:** Can plugins alter AI behavior? + **A:** Yes, via commands, hooks, and task interactions. +21. **Q:** Where are community plugins stored? + **A:** Under `plugins/`. +22. **Q:** How to avoid hook performance issues? + **A:** Keep callbacks lightweight and defer heavy work. +23. **Q:** Can multiple plugins share hook names? + **A:** Yes, each callback runs if registered correctly. +24. **Q:** Why unregister hooks on unload? + **A:** To prevent stale callbacks and inconsistent state. + +## Macros / eventMacro +25. **Q:** Difference between macro and eventMacro? + **A:** Macro is classic command script flow; eventMacro is event/condition-driven automation. +26. **Q:** Where to set macro file path? + **A:** `control/config.txt` via `macro_file`. +27. **Q:** Where to set eventMacro file path? + **A:** `control/config.txt` via `eventMacro_file`. +28. **Q:** Why automacro never triggers? + **A:** Condition false, plugin not loaded, or parse issue. +29. **Q:** How to test macro trigger quickly? + **A:** Manually run equivalent command and compare behavior. +30. **Q:** Why use run-once in eventMacro? + **A:** To avoid repeated firing for one-time actions. +31. **Q:** Can macros execute OpenKore commands? + **A:** Yes, via `do <command>`. +32. **Q:** How to debug eventMacro parser issues? + **A:** Reduce to one trigger + one action and rebuild incrementally. + +## Routing / movement +33. **Q:** Which files control routing logic? + **A:** `src/Task/Route.pm`, `src/Task/Move.pm`, and `src/Field.pm`. +34. **Q:** What is walkability? + **A:** Whether a map cell is traversable based on field data. +35. **Q:** Why does route loop occur? + **A:** Stale position updates, blocked transitions, or invalid field assumptions. +36. **Q:** What is lockMap effect? + **A:** Restricts behavior to a preferred map scope. +37. **Q:** How to validate map data? + **A:** Ensure map exists in `fields/*` and coordinates are valid. +38. **Q:** Why movement works manually but not AI? + **A:** AI priorities/config may override movement intents. +39. **Q:** Can aggressive combat stop movement? + **A:** Yes, combat states can continuously preempt route tasks. +40. **Q:** Best first movement test? + **A:** Short route on same map with minimal automation enabled. + +## Networking +41. **Q:** Which module tokenizes incoming packets? + **A:** `src/Network/MessageTokenizer.pm`. +42. **Q:** Which module decodes packet structures? + **A:** `src/Network/PacketParser.pm` and receive handlers. +43. **Q:** Where is receive dispatch done? + **A:** `src/Network/Receive.pm`. +44. **Q:** Where are server-specific packet handlers? + **A:** `src/Network/Receive/*` and `src/Network/Send/*`. +45. **Q:** Why packet errors after server patch? + **A:** Opcode/table changes requiring updated mappings. +46. **Q:** Can wrong serverType break everything? + **A:** Yes, it causes systematic decode/encode mismatch. +47. **Q:** What causes partial desync? + **A:** Some packets map correctly while others fail for changed opcodes. +48. **Q:** How to isolate receive vs send bug? + **A:** Compare inbound decode logs and outbound packet construction separately. + +## NPC interaction +49. **Q:** Which task handles NPC dialogues? + **A:** `src/Task/TalkNPC.pm`. +50. **Q:** Why NPC script stalls mid-dialogue? + **A:** Response packet mismatch or wrong expected step sequence. +51. **Q:** Must NPC be in actor list first? + **A:** Yes, reliable interaction depends on valid actor presence and position. +52. **Q:** Why wrong NPC gets targeted? + **A:** Incorrect coordinates or stale target selection. +53. **Q:** What to inspect first in NPC failure? + **A:** Talk packet sequence and corresponding receive responses. +54. **Q:** Can plugins interfere with NPC flow? + **A:** Yes, if they alter commands/tasks concurrently. + +## Config behavior +55. **Q:** Where are core behavior settings? + **A:** `control/config.txt`. +56. **Q:** What file controls monster behavior rules? + **A:** `control/mon_control.txt`. +57. **Q:** Why config changes seem ignored? + **A:** Reload needed, wrong key, or overridden by plugin automation. +58. **Q:** How to keep config deterministic? + **A:** Use minimal explicit settings and disable conflicting automation. +59. **Q:** What is saveMap used for? + **A:** Preferred return/safety map behavior. +60. **Q:** Why document config with code changes? + **A:** Behavior is policy-driven; docs prevent invisible config regressions. + +## XKore / client sync +61. **Q:** What is XKore2 role? + **A:** Local proxy/relay flow for account/char/map sessions. +62. **Q:** Why client view stale but bot state updates? + **A:** Forwarding/session sync issue in XKore bridge path. +63. **Q:** Is debugging Direct and XKore identical? + **A:** No, transport/session layers differ significantly. +64. **Q:** First step for XKore sync bugs? + **A:** Confirm active mode and trace handshake phase transitions. diff --git a/openkore_llm_knowledge/knowledge/19_glossary.md b/openkore_llm_knowledge/knowledge/19_glossary.md new file mode 100644 index 0000000000..abf1c04be5 --- /dev/null +++ b/openkore_llm_knowledge/knowledge/19_glossary.md @@ -0,0 +1,27 @@ +# Glossary + +- **Actor**: Runtime entity abstraction (player, monster, NPC, portal) managed via `Actor` modules. +- **AI queue**: Ordered AI intention/state processing that decides which action/task should run next. +- **Task**: Executable unit with lifecycle (start/process/finish), e.g., move, route, NPC interaction. +- **Plugin hook**: Callback registration point triggered by runtime events (`Plugins::addHooks`). +- **Command handler**: Function bound to a command name through `Commands::register`. +- **Packet handler**: Receive/send routine that decodes or encodes specific protocol packets. +- **Route**: Planned path between coordinates/maps, later executed stepwise by movement tasks. +- **Automacro**: Conditional automation rule that triggers scripted actions when conditions match. +- **eventMacro**: Event/condition-driven macro framework with parser, runner, and trigger model. +- **lockMap**: Configuration policy limiting movement/behavior to a designated map. +- **saveMap**: Preferred map used for return/recovery behavior. +- **Walkability**: Whether map cells are traversable according to field data and routing constraints. +- **Client sync**: Consistency between bot internal state and visual game client state (important in XKore). +- **Server packet**: Protocol message exchanged with the game server; parsed/encoded by network modules. +- **ServerType**: Packet profile selection controlling opcode and packet layout mapping. +- **Receive dispatch**: Mechanism mapping inbound opcode frames to concrete handler methods. +- **Send pipeline**: Path from action intent to serialized outbound packet. +- **XKore mode**: Bridged/proxy connection model variants (`XKore`, `XKore2`, `XKoreProxy`). +- **Main loop tick**: One full iteration of runtime processing in `functions.pl`. +- **Field data**: Map walkability/cell data used by route calculations. +- **Desync**: Divergence between expected protocol/state and observed runtime behavior. +- **Hook event**: Named runtime signal emitted for plugin subscribers. +- **Task manager**: Scheduler coordinating active/pending tasks and transitions. +- **Packet desync**: Failure mode where packet mappings no longer align with server behavior. +- **Control files**: Operator-facing config files under `control/` that drive runtime policy. diff --git a/openkore_llm_knowledge/knowledge/24_learning_path.md b/openkore_llm_knowledge/knowledge/24_learning_path.md new file mode 100644 index 0000000000..f9c988b0e1 --- /dev/null +++ b/openkore_llm_knowledge/knowledge/24_learning_path.md @@ -0,0 +1,49 @@ +# Learning Path for New OpenKore Developers + +## Stage 0: Orientation (Day 1) +- Read `01_llm_context.md`, `02_architecture_overview.md`, `03_system_architecture_map.md`. +- Goal: understand receive->state->AI->task->send loop and key directories. + +## Stage 1: Runtime core comprehension (Days 2-3) +- Study `src/functions.pl`, `src/Globals.pm`, `src/Modules.pm`. +- Track one full startup + one main-loop tick. +- Exercise: write a short note describing what runs each tick. + +## Stage 2: Network + actor model (Days 4-5) +- Study `src/Network.pm`, `src/Network/Receive.pm`, `src/Network/Send.pm`, `src/ActorList.pm`. +- Exercise: trace one incoming packet to a world-state mutation. +- Exercise: trace one outgoing action from command/task to send packet. + +## Stage 3: AI and task flow (Days 6-7) +- Study `src/AI.pm`, `src/AI/CoreLogic.pm`, `src/TaskManager.pm`, `src/Task/Route.pm`, `src/Task/TalkNPC.pm`. +- Exercise: map how AI chooses a task and how task completion feeds back. + +## Stage 4: Plugins and automation (Week 2) +- Study `src/Plugins.pm`, `src/Commands.pm`, `plugins/macro/*`, `plugins/eventMacro/*`. +- Build a minimal plugin with one hook and one command. +- Add one macro and one eventMacro automation rule. + +## Stage 5: Config-driven behavior (Week 2) +- Study `control/config.txt`, `control/mon_control.txt`, relevant table files. +- Exercise: change one behavior via config only and document impact. + +## Stage 6: Debugging proficiency (Week 3) +- Use `15_debugging_playbook.md`, `16_debug_decision_trees.md`, `22_common_pitfalls.md`. +- Reproduce and fix one issue in each category: + - movement/routing + - plugin/hook + - macro/eventMacro + - packet/XKore sync + +## Stage 7: Advanced protocol + XKore (Week 3+) +- Study `09_networking_packets.md`, `10_xkore_modes.md`. +- Compare DirectConnection vs XKore2 flow and identify mode-specific risks. + +## Suggested competency checklist +- [ ] Explain OpenKore runtime architecture from memory. +- [ ] Trace packet receive and send paths. +- [ ] Implement and unload a safe plugin. +- [ ] Diagnose macro and eventMacro trigger failures. +- [ ] Diagnose route and movement failures. +- [ ] Diagnose basic packet/serverType desync. +- [ ] Explain XKore sync failure patterns. diff --git a/openkore_llm_knowledge/knowledge/29_prompt_examples_for_users.md b/openkore_llm_knowledge/knowledge/29_prompt_examples_for_users.md new file mode 100644 index 0000000000..ba46ebe919 --- /dev/null +++ b/openkore_llm_knowledge/knowledge/29_prompt_examples_for_users.md @@ -0,0 +1,49 @@ +# Prompt Examples for Users + +Realistic prompts users can ask an OpenKore-focused GPT assistant. + +## Architecture and code navigation +1. "Explain how `src/functions.pl` orchestrates the OpenKore main loop." +2. "Map the dependency chain between AI, TaskManager, and Network::Send." +3. "Which modules should I read first to understand actor updates from packets?" +4. "Summarize how plugin hooks interact with command execution." + +## Debugging +5. "My bot stopped moving. Give me a step-by-step diagnosis plan with files to inspect." +6. "How do I distinguish packet desync from route calculation failure?" +7. "Generate a troubleshooting checklist for NPC dialogue failures." +8. "Why are my plugin hooks not firing even though plugin loads fine?" +9. "Help me debug eventMacro that parses but never triggers." +10. "Create a minimal reproduction plan for XKore client sync issues." + +## Plugins +11. "Create a minimal plugin skeleton with register, hooks, and unload cleanup." +12. "Add a custom command `farmstatus` that prints current map and task." +13. "Show best practices for lightweight hook callbacks in OpenKore plugins." +14. "How do I instrument a plugin with debug logs without spamming output?" + +## Macro / eventMacro +15. "Write an automacro that pauses looting above 85% weight." +16. "Create eventMacro rules to send a chat message only once when entering Prontera." +17. "Compare macro and eventMacro for reactive combat behavior." +18. "How can I test whether a macro trigger condition is actually true at runtime?" + +## Routing and movement +19. "Explain why route tasks can oscillate and how to debug it." +20. "Show a script strategy to test short movement segments before full routes." +21. "What configs can block movement even if route exists?" + +## Networking and packets +22. "How do I verify that serverType and recvpackets are correctly configured?" +23. "Trace one incoming movement packet from socket bytes to Actor update." +24. "What logs should I capture when packet decode errors start after a patch?" +25. "Explain where to add temporary logging for packet send construction." + +## Configuration +26. "Review my config goals and suggest a minimal stable `config.txt` baseline." +27. "Which `mon_control.txt` settings commonly interfere with movement tasks?" +28. "How do lockMap and saveMap influence AI behavior?" + +## Learning and onboarding +29. "Build me a 2-week learning plan to become productive in OpenKore development." +30. "Give me a study order for networking, AI, tasks, plugins, and macros with exercises." diff --git a/openkore_llm_knowledge/knowledge/30_dataset_summary.md b/openkore_llm_knowledge/knowledge/30_dataset_summary.md new file mode 100644 index 0000000000..91cb53f8a1 --- /dev/null +++ b/openkore_llm_knowledge/knowledge/30_dataset_summary.md @@ -0,0 +1,37 @@ +# Dataset Summary + +## Purpose +This dataset provides an LLM-oriented knowledge layer for OpenKore architecture, runtime flow, debugging, and practical usage. + +## Coverage areas +- **Architecture core**: context, subsystem boundaries, dependency map, code index. +- **Runtime flows**: AI loop, packet receive flow, NPC interaction, routing/movement, command and plugin hook flows. +- **Networking**: packet pipeline and XKore mode behavior. +- **Debugging**: playbook, decision trees, and common pitfalls. +- **Practical usage**: code recipes, FAQ, glossary, learning path, and user prompt examples. + +## Main artifact groups +1. **Foundational architecture docs** (`01`-`05`, `20`) +2. **Flow/diagram docs** (`06`-`10`, `26`, `27`) +3. **Troubleshooting docs** (`15`, `16`, `22`) +4. **Developer enablement docs** (`17`, `18`, `19`, `24`, `29`) + +## Intended users +- Developers onboarding to OpenKore internals. +- Maintainers debugging runtime and packet issues. +- LLM assistants that need stable structural context for responses. + +## Strengths +- Grounded in likely module and directory references from the repository. +- Organized by numbered, task-oriented documents. +- Includes actionable diagnostics and reusable snippet patterns. + +## Known limits +- Documentation is descriptive and may lag upstream implementation changes. +- Server-specific packet behavior can change rapidly after game updates. +- Mermaid diagrams prioritize clarity over exhaustive edge-case detail. + +## Maintenance recommendations +- Re-validate packet/XKore docs after serverType or recvpackets updates. +- Update recipe snippets when plugin/hook APIs evolve. +- Keep FAQ and pitfalls synced with recurring issue reports. From 6e28be90a0af96d4fa8bdd8a8de4fe7fa7fddd93 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Bosc=C3=A1?= <juan_bosca@live.com> Date: Tue, 10 Mar 2026 19:35:03 -0300 Subject: [PATCH 16/18] Normalize knowledge docs to numbered-only set --- .../knowledge/01_llm_context.md | 21 ++ .../knowledge/02_architecture_overview.md | 23 ++ .../knowledge/03_system_architecture_map.md | 43 +++ .../knowledge/04_core_subsystems.md | 49 ++++ .../knowledge/05_module_dependency_map.md | 29 ++ .../knowledge/06_execution_flows.md | 29 ++ .../knowledge/07_npc_interaction_flow.md | 26 ++ .../knowledge/08_routing_and_movement.md | 20 ++ .../knowledge/09_networking_packets.md | 22 ++ .../knowledge/10_xkore_modes.md | 20 ++ .../knowledge/11_plugin_system.md | 28 ++ .../knowledge/12_config_system.md | 19 ++ .../knowledge/13_macro_system.md | 19 ++ .../knowledge/14_table_reference.md | 25 ++ .../knowledge/15_debugging_playbook.md | 185 +++++++++++++ .../knowledge/16_debug_decision_trees.md | 90 +++++++ .../knowledge/17_code_recipes.md | 147 +++++++++++ .../knowledge/18_openkore_faq.md | 147 +++++++++++ .../knowledge/19_glossary.md | 27 ++ .../knowledge/20_code_index.md | 46 ++++ .../knowledge/21_core_module_index.md | 13 + .../knowledge/22_common_pitfalls.md | 49 ++++ .../knowledge/23_debugging_guide.md | 23 ++ .../knowledge/24_learning_path.md | 49 ++++ .../knowledge/26_architecture_diagrams.md | 17 ++ .../knowledge/27_system_flows.md | 35 +++ .../knowledge/29_prompt_examples_for_users.md | 49 ++++ .../knowledge/30_dataset_summary.md | 37 +++ .../knowledge/architecture_overview.md | 15 -- .../knowledge/code_recipes.md | 196 -------------- .../knowledge/config_system.md | 27 -- .../knowledge/core_module_index.md | 20 -- .../knowledge/core_subsystems.md | 27 -- .../knowledge/debugging_guide.md | 27 -- .../knowledge/debugging_playbook.md | 244 ----------------- .../knowledge/execution_flows.md | 26 -- .../knowledge/macro_system.md | 31 --- .../knowledge/module_dependency_map.md | 122 --------- .../knowledge/networking_packets.md | 27 -- .../knowledge/openkore_faq.md | 208 --------------- .../knowledge/plugin_system.md | 41 --- .../knowledge/system_architecture_map.md | 249 ------------------ .../knowledge/table_reference.md | 24 -- .../knowledge/xkore_modes.md | 21 -- 44 files changed, 1287 insertions(+), 1305 deletions(-) create mode 100644 openkore_llm_knowledge/knowledge/01_llm_context.md create mode 100644 openkore_llm_knowledge/knowledge/02_architecture_overview.md create mode 100644 openkore_llm_knowledge/knowledge/03_system_architecture_map.md create mode 100644 openkore_llm_knowledge/knowledge/04_core_subsystems.md create mode 100644 openkore_llm_knowledge/knowledge/05_module_dependency_map.md create mode 100644 openkore_llm_knowledge/knowledge/06_execution_flows.md create mode 100644 openkore_llm_knowledge/knowledge/07_npc_interaction_flow.md create mode 100644 openkore_llm_knowledge/knowledge/08_routing_and_movement.md create mode 100644 openkore_llm_knowledge/knowledge/09_networking_packets.md create mode 100644 openkore_llm_knowledge/knowledge/10_xkore_modes.md create mode 100644 openkore_llm_knowledge/knowledge/11_plugin_system.md create mode 100644 openkore_llm_knowledge/knowledge/12_config_system.md create mode 100644 openkore_llm_knowledge/knowledge/13_macro_system.md create mode 100644 openkore_llm_knowledge/knowledge/14_table_reference.md create mode 100644 openkore_llm_knowledge/knowledge/15_debugging_playbook.md create mode 100644 openkore_llm_knowledge/knowledge/16_debug_decision_trees.md create mode 100644 openkore_llm_knowledge/knowledge/17_code_recipes.md create mode 100644 openkore_llm_knowledge/knowledge/18_openkore_faq.md create mode 100644 openkore_llm_knowledge/knowledge/19_glossary.md create mode 100644 openkore_llm_knowledge/knowledge/20_code_index.md create mode 100644 openkore_llm_knowledge/knowledge/21_core_module_index.md create mode 100644 openkore_llm_knowledge/knowledge/22_common_pitfalls.md create mode 100644 openkore_llm_knowledge/knowledge/23_debugging_guide.md create mode 100644 openkore_llm_knowledge/knowledge/24_learning_path.md create mode 100644 openkore_llm_knowledge/knowledge/26_architecture_diagrams.md create mode 100644 openkore_llm_knowledge/knowledge/27_system_flows.md create mode 100644 openkore_llm_knowledge/knowledge/29_prompt_examples_for_users.md create mode 100644 openkore_llm_knowledge/knowledge/30_dataset_summary.md delete mode 100644 openkore_llm_knowledge/knowledge/architecture_overview.md delete mode 100644 openkore_llm_knowledge/knowledge/code_recipes.md delete mode 100644 openkore_llm_knowledge/knowledge/config_system.md delete mode 100644 openkore_llm_knowledge/knowledge/core_module_index.md delete mode 100644 openkore_llm_knowledge/knowledge/core_subsystems.md delete mode 100644 openkore_llm_knowledge/knowledge/debugging_guide.md delete mode 100644 openkore_llm_knowledge/knowledge/debugging_playbook.md delete mode 100644 openkore_llm_knowledge/knowledge/execution_flows.md delete mode 100644 openkore_llm_knowledge/knowledge/macro_system.md delete mode 100644 openkore_llm_knowledge/knowledge/module_dependency_map.md delete mode 100644 openkore_llm_knowledge/knowledge/networking_packets.md delete mode 100644 openkore_llm_knowledge/knowledge/openkore_faq.md delete mode 100644 openkore_llm_knowledge/knowledge/plugin_system.md delete mode 100644 openkore_llm_knowledge/knowledge/system_architecture_map.md delete mode 100644 openkore_llm_knowledge/knowledge/table_reference.md delete mode 100644 openkore_llm_knowledge/knowledge/xkore_modes.md diff --git a/openkore_llm_knowledge/knowledge/01_llm_context.md b/openkore_llm_knowledge/knowledge/01_llm_context.md new file mode 100644 index 0000000000..cf85d00779 --- /dev/null +++ b/openkore_llm_knowledge/knowledge/01_llm_context.md @@ -0,0 +1,21 @@ +# OpenKore LLM Context + +## Objective +This layer gives an LLM enough structural context to reason about OpenKore runtime behavior, module boundaries, and extension points. + +## Runtime model (high level) +1. `src/functions.pl` boots runtime services, loads config/tables, initializes network mode, and drives the main loop. +2. Network receive handlers (`src/Network/Receive*.pm`) transform packets into world-state updates. +3. Actor state (`src/Actor.pm`, `src/ActorList.pm`) becomes the shared world model for decision logic. +4. AI (`src/AI.pm`, `src/AI/CoreLogic.pm`) evaluates state and schedules actions through tasks. +5. Task execution (`src/TaskManager.pm`, `src/Task/*.pm`) performs multi-step actions and emits packets through send modules (`src/Network/Send*.pm`). +6. Plugins (`src/Plugins.pm`, `plugins/*`) and macro layers (`plugins/macro`, `plugins/eventMacro`) inject automation and custom behavior. + +## Key architecture anchors +- **Entry/control loop**: `src/functions.pl` +- **Global state/config loading**: `src/Globals.pm`, `src/Settings.pm`, `src/FileParsers.pm`, `control/` +- **AI + behavior**: `src/AI.pm`, `src/AI/CoreLogic.pm` +- **Actor model**: `src/Actor.pm`, `src/ActorList.pm`, `src/Actor/*` +- **Task orchestration**: `src/Task.pm`, `src/TaskManager.pm`, `src/Task/*` +- **Networking and packets**: `src/Network.pm`, `src/Network/*` +- **Plugin/macro automation**: `src/Plugins.pm`, `plugins/macro/*`, `plugins/eventMacro/*` diff --git a/openkore_llm_knowledge/knowledge/02_architecture_overview.md b/openkore_llm_knowledge/knowledge/02_architecture_overview.md new file mode 100644 index 0000000000..6bfe238247 --- /dev/null +++ b/openkore_llm_knowledge/knowledge/02_architecture_overview.md @@ -0,0 +1,23 @@ +# OpenKore Architecture Overview + +## Top-level structure +- `src/`: core runtime modules (AI, actor model, networking, tasks, plugins API, command layer). +- `control/`: operator configuration and behavior policies (`config.txt`, control lists, route/macro inputs). +- `tables/`: protocol/game data mappings (packet maps, item/skill/map metadata, server-specific variants). +- `plugins/`: optional extensions, including macro/eventMacro automation stacks. +- `fields/`: map field data used by routing/navigation. + +## Core runtime layers +1. **Bootstrap + loop**: `src/functions.pl` and `src/Modules.pm` +2. **State + config**: `src/Globals.pm`, `src/Settings.pm`, `src/FileParsers.pm` +3. **Network transport + packet translation**: `src/Network.pm`, `src/Network/{Receive,Send,PacketParser}.pm` +4. **World model**: `src/Actor.pm`, `src/ActorList.pm`, `src/Field.pm` +5. **Decision engine**: `src/AI.pm`, `src/AI/CoreLogic.pm` +6. **Execution engine**: `src/TaskManager.pm`, `src/Task/*.pm` +7. **Extension surface**: `src/Plugins.pm`, `src/Commands.pm`, `plugins/*` + +## Architectural characteristics +- **Event-driven and loop-based**: runtime state is advanced each tick by receive updates + AI/task progression. +- **Protocol-adapter design**: packet send/receive classes are split by server type under `src/Network/Receive/*` and `src/Network/Send/*`. +- **Policy outside code**: behavior is heavily configured via `control/` and `tables/` files. +- **Extension-first automation**: macro/eventMacro run as plugins and reuse command/hook/task pathways. diff --git a/openkore_llm_knowledge/knowledge/03_system_architecture_map.md b/openkore_llm_knowledge/knowledge/03_system_architecture_map.md new file mode 100644 index 0000000000..3181b0647f --- /dev/null +++ b/openkore_llm_knowledge/knowledge/03_system_architecture_map.md @@ -0,0 +1,43 @@ +# System Architecture Map (Textual) + +## Primary flow +`functions.pl main loop` -> `Network receive` -> `Actor/Globals update` -> `AI decision` -> `Task execution` -> `Network send` + +## Subsystem map + +### 1) Networking +- Connection/mode handling: `src/Network.pm`, `src/Network/DirectConnection.pm`, `src/Network/XKore*.pm` +- Packet ingestion and dispatch: `src/Network/MessageTokenizer.pm`, `src/Network/PacketParser.pm`, `src/Network/Receive.pm`, `src/Network/Receive/*` +- Packet construction: `src/Network/Send.pm`, `src/Network/Send/*` + +### 2) Actor system +- Base entity model: `src/Actor.pm`, `src/Actor/*` +- Actor collections and lookup: `src/ActorList.pm` +- World/map coupling: `src/Field.pm`, map and position state in globals + +### 3) AI subsystem +- AI stack state and sequencing: `src/AI.pm` +- Core behavior logic: `src/AI/CoreLogic.pm` +- Uses actor/world/config state; issues actions via task manager and commands + +### 4) Task subsystem +- Task abstraction: `src/Task.pm` +- Scheduler and lifecycle: `src/TaskManager.pm` +- High-impact tasks: `src/Task/Route.pm`, `src/Task/Move.pm`, `src/Task/TalkNPC.pm`, `src/Task/UseSkill.pm` + +### 5) Configuration subsystem +- Config discovery/loading: `src/Settings.pm`, `src/FileParsers.pm` +- Runtime shared state: `src/Globals.pm` +- Policy sources: `control/*`, `tables/*` + +### 6) Plugin + macro subsystem +- Hook and plugin lifecycle: `src/Plugins.pm` +- Command bridge: `src/Commands.pm` +- Macro engines: `plugins/macro/*`, `plugins/eventMacro/*` + +## Key entry points +- Startup/main loop: `src/functions.pl` +- Command dispatch: `src/Commands.pm` +- Hook registration: `src/Plugins.pm` +- AI tick path: `src/AI.pm` -> `src/AI/CoreLogic.pm` +- Packet receive path: `src/Network/Receive.pm` + server-specific receivers diff --git a/openkore_llm_knowledge/knowledge/04_core_subsystems.md b/openkore_llm_knowledge/knowledge/04_core_subsystems.md new file mode 100644 index 0000000000..9621268ac6 --- /dev/null +++ b/openkore_llm_knowledge/knowledge/04_core_subsystems.md @@ -0,0 +1,49 @@ +# Core Subsystems + +## AI +- Main modules: `src/AI.pm`, `src/AI/CoreLogic.pm` +- Role: maintain AI queues/state machines, evaluate combat/loot/movement priorities, enqueue tasks. +- Inputs: actor state, config policies, packet-driven updates. +- Outputs: task requests, command invocations, action intents. + +## Actor system +- Main modules: `src/Actor.pm`, `src/ActorList.pm`, `src/Actor/*` +- Role: represent player/NPC/monster/portal entities, with indexed lookup for targeting and proximity logic. +- Inputs: receive handlers and map updates. +- Outputs: query surface for AI, tasks, commands, and plugins. + +## Networking +- Main modules: `src/Network.pm`, `src/Network/{Receive,Send,PacketParser,MessageTokenizer}.pm`, `src/Network/XKore*.pm` +- Role: manage transport mode, parse inbound packets, encode outbound actions, support server-specific packet families. +- Inputs: socket data + outgoing action intents. +- Outputs: state mutations via receive handlers and serialized packets to servers/clients. + +## Task system +- Main modules: `src/Task.pm`, `src/TaskManager.pm`, `src/Task/*` +- Role: run asynchronous multi-step actions (route, move, NPC dialogs, skill/item use). +- Inputs: AI/command/plugin action requests. +- Outputs: packet sends, follow-up tasks, completion/failure states consumed by AI. + +## Plugin system +- Main modules: `src/Plugins.pm`, `plugins/*` +- Role: dynamic extension lifecycle (`register`, hooks, unload), runtime behavior injection. +- Inputs: startup/load events, packet-related hooks, periodic loop hooks. +- Outputs: custom commands, state transitions, automation triggers. + +## Configuration system +- Main modules: `src/Settings.pm`, `src/FileParsers.pm`, `control/*`, `tables/*` +- Role: load policy and data files that parameterize AI, networking, and gameplay handling. +- Inputs: text configs and table files. +- Outputs: normalized runtime config/state in globals and subsystem-specific structures. + +## Macro system +- Main modules: `plugins/macro/macro.pl`, `plugins/macro/Macro/*`, `plugins/eventMacro/eventMacro.pl`, `plugins/eventMacro/eventMacro/*` +- Role: declarative automation over hooks and game events, with conditional triggers and scripted actions. +- Inputs: macro definitions and live runtime events. +- Outputs: command execution and indirect task/network activity. + +## Routing +- Main modules: `src/Task/Route.pm`, `src/Task/Move.pm`, `src/Field.pm`, `fields/*` +- Role: compute and execute navigation paths across map cells/portals. +- Inputs: destination intents, field data, actor position/state. +- Outputs: stepwise movement actions and route completion/fallback states. diff --git a/openkore_llm_knowledge/knowledge/05_module_dependency_map.md b/openkore_llm_knowledge/knowledge/05_module_dependency_map.md new file mode 100644 index 0000000000..35f8eb23cf --- /dev/null +++ b/openkore_llm_knowledge/knowledge/05_module_dependency_map.md @@ -0,0 +1,29 @@ +# Module Dependency Map + +## Dependency chains (high impact) +1. `src/functions.pl` -> `src/Settings.pm` / `src/FileParsers.pm` / `src/Modules.pm` (bootstrap) +2. `src/functions.pl` -> `src/Network.pm` + `src/Network/*` (connection and packet loop) +3. `src/Network/Receive*.pm` -> `src/Globals.pm` + `src/Actor*.pm` (state mutation) +4. `src/AI.pm` -> `src/AI/CoreLogic.pm` -> `src/TaskManager.pm` + `src/Task/*` (decision to execution) +5. `src/Task/*` -> `src/Network/Send*.pm` (action serialization) +6. `src/Plugins.pm` + `plugins/*` -> `src/Commands.pm` / AI / TaskManager (extension control paths) + +## Subsystem dependency view +- **AI depends on**: Actor state, config policies, task scheduler, command surface. +- **Actor system depends on**: Network receive updates and global runtime registries. +- **Task system depends on**: AI/commands/plugins for intents; network send + actor/map updates for progression. +- **Plugin/macro depends on**: hook lifecycle (`src/Plugins.pm`), command execution (`src/Commands.pm`), global state. +- **Routing depends on**: task framework, field data (`fields/*`), movement packet sends. + +## Coupling hotspots +- `src/Globals.pm`: shared mutable state touched by receive, AI, and plugins. +- `src/Commands.pm`: cross-cutting control surface used by user input and automation. +- `src/TaskManager.pm`: convergence point for AI and scripted automation. +- `src/Network/Receive.pm`: ingress bridge from protocol events to internal state transitions. + +## Practical navigation order for analysis +1. Start at `src/functions.pl`. +2. Follow receive path (`src/Network/Receive.pm`, subtype receivers). +3. Inspect AI loop (`src/AI.pm`, `src/AI/CoreLogic.pm`). +4. Inspect task execution (`src/TaskManager.pm`, route/move/NPC tasks). +5. Inspect extension points (`src/Plugins.pm`, macro/eventMacro plugins). diff --git a/openkore_llm_knowledge/knowledge/06_execution_flows.md b/openkore_llm_knowledge/knowledge/06_execution_flows.md new file mode 100644 index 0000000000..cac52ed2b0 --- /dev/null +++ b/openkore_llm_knowledge/knowledge/06_execution_flows.md @@ -0,0 +1,29 @@ +# Execution Flows + +## Runtime execution loop +OpenKore runs a tick-oriented loop rooted in `src/functions.pl`, where network ingestion, state updates, AI decisions, and task execution are iterated continuously. + +Operational sequence: +1. Main loop tick in `src/functions.pl` advances network, AI, and task phases. +2. Receive handlers (`src/Network/Receive.pm`, `src/Network/Receive/*`) apply packet-driven state updates. +3. Shared state (`src/Globals.pm`, `src/ActorList.pm`) becomes input for AI logic. +4. AI (`src/AI.pm`, `src/AI/CoreLogic.pm`) chooses actions and updates task queues. +5. Tasks (`src/TaskManager.pm`, `src/Task/*`) execute stepwise and emit outgoing packets through send modules. + +## AI execution loop + +```mermaid +flowchart TD + A[AI tick\nsrc/AI.pm] --> B[Gather context\nGlobals + ActorList + config] + B --> C[Core logic\nsrc/AI/CoreLogic.pm] + C --> D{Action required?} + D -- No --> E[Idle/monitor state] + D -- Yes --> F[Create/adjust tasks\nsrc/TaskManager.pm] + F --> G[Run task step\nsrc/Task/*.pm] + G --> H[Emit commands/packets\nCommands + Network::Send] + H --> I[Wait for receive updates] + E --> I + I --> A +``` + +AI is decision-oriented; tasks hold execution state between ticks while receive handlers provide feedback. diff --git a/openkore_llm_knowledge/knowledge/07_npc_interaction_flow.md b/openkore_llm_knowledge/knowledge/07_npc_interaction_flow.md new file mode 100644 index 0000000000..48d5f80007 --- /dev/null +++ b/openkore_llm_knowledge/knowledge/07_npc_interaction_flow.md @@ -0,0 +1,26 @@ +# NPC Interaction Flow + +NPC interactions are executed as task-driven conversations, typically using `src/Task/TalkNPC.pm` and packet handlers under `src/Network/Receive/*.pm`. + +```mermaid +sequenceDiagram + participant U as AI/Command/Plugin + participant TM as TaskManager + participant TN as Task::TalkNPC + participant NS as Network::Send + participant S as Ragnarok Server + participant NR as Network::Receive + participant G as Globals/Actor state + + U->>TM: enqueue NPC interaction + TM->>TN: start dialogue task + TN->>NS: send talk/response packet + NS->>S: outbound packet + S->>NR: NPC response packet + NR->>G: update dialogue/state flags + NR->>TM: task-relevant updates + TM->>TN: advance next dialogue step + TN-->>TM: complete/fail +``` + +`Task::TalkNPC` keeps dialogue progression explicit, while receive handlers synchronize server-side conversation state back into runtime state. diff --git a/openkore_llm_knowledge/knowledge/08_routing_and_movement.md b/openkore_llm_knowledge/knowledge/08_routing_and_movement.md new file mode 100644 index 0000000000..a1e0b0cf5f --- /dev/null +++ b/openkore_llm_knowledge/knowledge/08_routing_and_movement.md @@ -0,0 +1,20 @@ +# Routing and Movement + +Routing converts destination intent into path segments and movement packets using `src/Task/Route.pm`, `src/Task/Move.pm`, `src/Field.pm`, and `fields/*` data. + +```mermaid +flowchart TD + A[AI/Command target\nmap + coordinates] --> B[Task::Route init] + B --> C[Load field graph\nsrc/Field.pm + fields/*] + C --> D[Compute path segments] + D --> E[Task::Move executes step] + E --> F[Send move packet\nsrc/Network/Send.pm] + F --> G[Receive position update\nsrc/Network/Receive.pm] + G --> H{Reached waypoint?} + H -- No --> E + H -- Yes --> I{Reached destination?} + I -- No --> D + I -- Yes --> J[Route complete] +``` + +Routing is iterative and feedback-driven: each movement step is validated by receive updates before continuing. diff --git a/openkore_llm_knowledge/knowledge/09_networking_packets.md b/openkore_llm_knowledge/knowledge/09_networking_packets.md new file mode 100644 index 0000000000..a72e17de20 --- /dev/null +++ b/openkore_llm_knowledge/knowledge/09_networking_packets.md @@ -0,0 +1,22 @@ +# Networking and Packet Flow + +OpenKore packet handling combines framing/tokenization, opcode parsing, and server-type receive/send classes under `src/Network/*`. + +## Packet receive flow + +```mermaid +flowchart TD + A[Socket bytes\nNetwork mode] --> B[MessageTokenizer\nsrc/Network/MessageTokenizer.pm] + B --> C[PacketParser\nsrc/Network/PacketParser.pm] + C --> D[Receive dispatcher\nsrc/Network/Receive.pm] + D --> E[Server-type handler\nsrc/Network/Receive/*] + E --> F[Runtime mutation\nGlobals + Actor + Task signals] + F --> G[Hooks/plugins notified\nsrc/Plugins.pm] +``` + +Receive processing turns protocol frames into domain-level state transitions used by AI and tasks. + +## Packet send path (supporting context) +- Action request sources: AI, commands, task modules, plugins/macros. +- Encoding path: `src/Network/Send.pm` -> `src/Network/Send/*` (server-type specific structures). +- Transport path: active mode (`DirectConnection`, `XKore`, `XKore2`, or `XKoreProxy`) in `src/Network/*.pm`. diff --git a/openkore_llm_knowledge/knowledge/10_xkore_modes.md b/openkore_llm_knowledge/knowledge/10_xkore_modes.md new file mode 100644 index 0000000000..4f0d773936 --- /dev/null +++ b/openkore_llm_knowledge/knowledge/10_xkore_modes.md @@ -0,0 +1,20 @@ +# XKore Modes + +XKore modes define how OpenKore connects to the game client/server pipeline. + +## Core modules +- `src/Network/XKore.pm` +- `src/Network/XKore2.pm` +- `src/Network/XKoreProxy.pm` +- `src/Network/DirectConnection.pm` (baseline comparison) + +## Mode summary +- **DirectConnection**: bot connects directly to game servers. +- **XKore**: client-assisted mode using forwarding/bridging logic. +- **XKore2**: OpenKore hosts local account/char/map proxy endpoints (`src/Network/XKore2/*`) and relays traffic. +- **XKoreProxy**: proxy-style mediation path for packet bridging. + +## Architectural impact +- All modes reuse packet parsing/dispatch and send modules (`src/Network/{Receive,Send}.pm`). +- Mode choice primarily changes transport/session orchestration and how packets are sourced/sinked. +- AI, tasks, actor state, plugins, and macro systems remain mode-agnostic above the network abstraction. diff --git a/openkore_llm_knowledge/knowledge/11_plugin_system.md b/openkore_llm_knowledge/knowledge/11_plugin_system.md new file mode 100644 index 0000000000..8e93284d60 --- /dev/null +++ b/openkore_llm_knowledge/knowledge/11_plugin_system.md @@ -0,0 +1,28 @@ +# Plugin System + +## Purpose +OpenKore plugins extend runtime behavior without editing core modules. + +## Core interfaces +- `src/Plugins.pm`: plugin lifecycle and hook dispatch. +- `src/Commands.pm`: command registration/execution surface used by plugins. +- `plugins/*`: plugin implementations (`*.pl` entrypoints + helper modules). + +## Lifecycle pattern +1. Register plugin with `Plugins::register(...)`. +2. Register hooks with `Plugins::addHooks(...)`. +3. Optionally register commands via `Commands::register(...)`. +4. On unload, remove hooks/commands and clear plugin state. + +## Common hook usage +- Startup hooks (e.g., startup/init phases) +- Loop hooks (periodic behavior) +- Packet hooks (`packet/*`, `packet_pre/*`) +- Config-change hooks (`configModify`) + +## Representative plugins +- `plugins/macro/macro.pl` +- `plugins/eventMacro/eventMacro.pl` +- `plugins/reconnect/reconnect.pl` +- `plugins/profiles/profiles.pl` +- `plugins/map/map.pl` diff --git a/openkore_llm_knowledge/knowledge/12_config_system.md b/openkore_llm_knowledge/knowledge/12_config_system.md new file mode 100644 index 0000000000..35dc6108d2 --- /dev/null +++ b/openkore_llm_knowledge/knowledge/12_config_system.md @@ -0,0 +1,19 @@ +# Configuration System + +## Purpose +Behavior policy is primarily configuration-driven via `control/` and table data under `tables/`. + +## Key files +- `control/config.txt`: global runtime/AI/combat/movement/network options. +- `control/items_control.txt`: item pickup/storage/sell policy. +- `control/mon_control.txt`: per-monster behavior policy. + +## Runtime integration +- `src/Settings.pm`: config path/file registration. +- `src/FileParsers.pm`: parser logic for control/table formats. +- `src/Globals.pm`: shared runtime state consumed by AI/tasks/plugins. + +## Debugging guidance +- Validate key names and value format first. +- Re-test with minimal config to isolate policy conflicts. +- Check plugin/macro automation that may override config expectations. diff --git a/openkore_llm_knowledge/knowledge/13_macro_system.md b/openkore_llm_knowledge/knowledge/13_macro_system.md new file mode 100644 index 0000000000..cba32b5d0c --- /dev/null +++ b/openkore_llm_knowledge/knowledge/13_macro_system.md @@ -0,0 +1,19 @@ +# Macro System + +## Components +- Macro plugin: `plugins/macro/macro.pl`, `plugins/macro/Macro/*` +- eventMacro plugin: `plugins/eventMacro/eventMacro.pl`, `plugins/eventMacro/eventMacro/*` + +## Configuration keys +- `macro_file` -> macro script path (commonly `macros.txt`). +- `eventMacro_file` -> eventMacro script path (commonly `eventMacros.txt`). + +## Execution model +1. Plugin loads and parses macro file. +2. Hooks/events trigger condition evaluation. +3. Matching automacro rules enqueue/execute actions. +4. Actions flow through command/task/network paths. + +## Operational notes +- Macro and eventMacro are complementary automation layers. +- Troubleshooting should separate: load -> parse -> trigger -> action execution. diff --git a/openkore_llm_knowledge/knowledge/14_table_reference.md b/openkore_llm_knowledge/knowledge/14_table_reference.md new file mode 100644 index 0000000000..a67d5688f1 --- /dev/null +++ b/openkore_llm_knowledge/knowledge/14_table_reference.md @@ -0,0 +1,25 @@ +# Table Reference + +## Purpose +`tables/` contains protocol and gameplay data used by network parsing, IDs, maps, and runtime lookups. + +## Core packet/profile tables +- `tables/servers.txt` +- `tables/packetlist.txt` +- `tables/packetdescriptions.txt` +- server-family `recvpackets.txt` files (e.g., `tables/kRO/recvpackets.txt`, `tables/iRO/recvpackets.txt`) + +## Gameplay identity tables +- `tables/SKILL_id_handle.txt` +- `tables/STATUS_id_handle.txt` +- `tables/statusnametable.txt` +- `tables/itemtypes.txt` +- `tables/elements.txt` + +## World/content tables +- `tables/portals.txt` +- `tables/skillsarea.txt` +- regional `maps.txt`, `items.txt`, `skillnametable.txt` + +## Notes +Packet-related incidents should always be validated against serverType + recvpackets alignment. diff --git a/openkore_llm_knowledge/knowledge/15_debugging_playbook.md b/openkore_llm_knowledge/knowledge/15_debugging_playbook.md new file mode 100644 index 0000000000..c826ebf7c4 --- /dev/null +++ b/openkore_llm_knowledge/knowledge/15_debugging_playbook.md @@ -0,0 +1,185 @@ +# Debugging Playbook + +This playbook maps common OpenKore runtime failures to likely causes, inspection points, and step-by-step troubleshooting. + +--- + +## 1) Bot not moving +- **Likely subsystem**: AI + Task + Routing + Network Send +- **Possible causes**: + - AI state blocked (attack/dialog/task lock) + - Route task never created or immediately failing + - Movement packets not being sent or rejected + - Movement-related config prevents walking +- **Files to inspect**: + - `src/AI.pm`, `src/AI/CoreLogic.pm` + - `src/TaskManager.pm`, `src/Task/Move.pm`, `src/Task/Route.pm` + - `src/Network/Send.pm`, `src/Network/Receive.pm` +- **Config to inspect**: + - `control/config.txt` (movement/lockMap/route-related options) + - `control/mon_control.txt` (behavior that can pin combat state) +- **Debugging steps**: + 1. Confirm AI is ticking and not paused/stuck in a higher-priority state. + 2. Check whether `Task::Route`/`Task::Move` is queued in task manager. + 3. Validate that move packets are emitted by send path. + 4. Confirm receive updates are acknowledging position changes. + 5. Reduce constraints in config (lock-only behavior, avoid lists) and retest. + +## 2) Routing failure +- **Likely subsystem**: Routing + Field data + Task +- **Possible causes**: + - Missing/incorrect field data + - Blocked cells/portal transitions not resolved + - Route recomputation loops without progress +- **Files to inspect**: + - `src/Task/Route.pm`, `src/Task/Move.pm`, `src/Field.pm` +- **Config to inspect**: + - `control/config.txt` (route/move flags) + - `fields/*` (map walkability data) +- **Debugging steps**: + 1. Verify map field exists and corresponds to current map. + 2. Check route generation result (empty path or oscillating path). + 3. Confirm position updates are fresh (no stale coordinates). + 4. Test shorter route segments to isolate failing region. + 5. Re-check portal/map transition assumptions. + +## 3) NPC interaction failure +- **Likely subsystem**: Task + Network Receive/Send + Actor/NPC state +- **Possible causes**: + - Wrong NPC coordinates or stale actor reference + - Dialogue step mismatch with server response + - Talk packets not sent or incorrect sequence +- **Files to inspect**: + - `src/Task/TalkNPC.pm` + - `src/Network/Send.pm`, `src/Network/Receive.pm`, `src/Network/Receive/*` + - `src/ActorList.pm` +- **Config to inspect**: + - `control/config.txt` (npc interaction directives/scripts) +- **Debugging steps**: + 1. Confirm NPC exists in actor list at expected coordinates. + 2. Trace talk packet send sequence against expected dialogue steps. + 3. Verify receive handlers parse response packet IDs correctly for server type. + 4. Reproduce with minimal NPC script/task to reduce branching. + +## 4) Plugin not loading +- **Likely subsystem**: Plugin system + startup/module loading +- **Possible causes**: + - Syntax/runtime error in plugin file + - Incorrect plugin path/name + - Missing dependency module used by plugin +- **Files to inspect**: + - `src/Plugins.pm`, `src/Modules.pm` + - `plugins/<plugin_name>/*.pl` +- **Config to inspect**: + - `control/config.txt` (plugin enable/load entries) +- **Debugging steps**: + 1. Check startup logs for plugin load exceptions. + 2. Validate plugin registers with `Plugins::register`. + 3. Confirm plugin path and filename match expected loader behavior. + 4. Remove optional dependency imports to isolate failing require/use. + +## 5) Plugin hook not firing +- **Likely subsystem**: Plugin hooks + event dispatch +- **Possible causes**: + - Hook registered with wrong event name + - Hook callback blocked by guard condition + - Expected event never emitted in current runtime path +- **Files to inspect**: + - `src/Plugins.pm`, `src/functions.pl` + - Plugin file defining `Plugins::addHooks` +- **Config to inspect**: + - `control/config.txt` (conditions controlling event generation) +- **Debugging steps**: + 1. List hooks registered by plugin after load. + 2. Add lightweight logging at callback entry. + 3. Verify event name spelling and payload assumptions. + 4. Trigger event manually through a minimal reproduction path. + +## 6) Macro not triggering +- **Likely subsystem**: Macro plugin + command/hook bridge +- **Possible causes**: + - Macro file not loaded + - Automacro condition never true + - Macro command blocked by runtime state +- **Files to inspect**: + - `plugins/macro/macro.pl`, `plugins/macro/Macro/*` + - `src/Commands.pm`, `src/Plugins.pm` +- **Config to inspect**: + - `control/config.txt` (`macro_file` and related keys) + - macro definition file referenced by config +- **Debugging steps**: + 1. Confirm macro plugin loaded and macro file parsed. + 2. Validate automacro conditions against live state. + 3. Execute macro manually to separate trigger vs action issues. + 4. Inspect command dispatch path for blocked/invalid command. + +## 7) eventMacro issues +- **Likely subsystem**: eventMacro plugin + event parser/runner +- **Possible causes**: + - Invalid eventMacro syntax + - Trigger conditions not matching event payload + - Runner stalled by previous action/wait state +- **Files to inspect**: + - `plugins/eventMacro/eventMacro.pl` + - `plugins/eventMacro/eventMacro/{Core,Runner,Automacro,FileParser}.pm` +- **Config to inspect**: + - `control/config.txt` (`eventMacro_file`) + - eventMacro script file +- **Debugging steps**: + 1. Validate eventMacro file parsing with minimal script. + 2. Log trigger evaluation for target automacro. + 3. Check runner queue and pending wait/time gates. + 4. Reduce script to one trigger + one action and retest. + +## 8) Packet desync +- **Likely subsystem**: Networking receive/parser + server-type mappings +- **Possible causes**: + - Wrong server type packet tables + - Opcode map mismatch after server update + - Packet boundary/tokenization errors +- **Files to inspect**: + - `src/Network/MessageTokenizer.pm`, `src/Network/PacketParser.pm` + - `src/Network/Receive.pm`, `src/Network/Receive/*` + - `src/Network/Send.pm`, `src/Network/Send/*` +- **Config to inspect**: + - `control/config.txt` (serverType/recvpackets settings) + - `tables/*` packet definition files for the target server +- **Debugging steps**: + 1. Confirm selected serverType and recvpackets data. + 2. Compare failing opcode decode against current server packet definitions. + 3. Capture raw packet sequence around first desync point. + 4. Verify both receive and send side use compatible packet maps. + +## 9) XKore sync issues +- **Likely subsystem**: XKore transport/session bridging +- **Possible causes**: + - Client-proxy handshake mismatch + - Account/char/map relay state divergence + - Timing/forwarding issues in XKore mode +- **Files to inspect**: + - `src/Network/XKore.pm`, `src/Network/XKore2.pm`, `src/Network/XKoreProxy.pm` + - `src/Network/XKore2/{AccountServer,CharServer,MapServer}.pm` +- **Config to inspect**: + - `control/config.txt` (XKore mode and connection settings) +- **Debugging steps**: + 1. Confirm active mode (Direct vs XKore/XKore2/XKoreProxy). + 2. Trace handshake/login flow across account->char->map stages. + 3. Check for asymmetric forwarding (client sees state that bot does not, or inverse). + 4. Validate mode-specific ports/bind settings and restart cleanly. + +## 10) Visual client state not updating +- **Likely subsystem**: XKore bridge + packet forwarding + actor sync +- **Possible causes**: + - Forwarded packets not reaching client + - Actor state updated in bot but not mirrored to client channel + - Client session stuck after map transition +- **Files to inspect**: + - `src/Network/XKore*.pm` + - `src/Network/Receive.pm`, `src/ActorList.pm` +- **Config to inspect**: + - `control/config.txt` (XKore forwarding/session config) +- **Debugging steps**: + 1. Verify bot-side state is changing (position/actors) first. + 2. Confirm corresponding packets are forwarded to client path. + 3. Re-check session phase (account/char/map) for stuck transition. + 4. Reconnect client through same mode to re-establish synchronization. diff --git a/openkore_llm_knowledge/knowledge/16_debug_decision_trees.md b/openkore_llm_knowledge/knowledge/16_debug_decision_trees.md new file mode 100644 index 0000000000..8869941f85 --- /dev/null +++ b/openkore_llm_knowledge/knowledge/16_debug_decision_trees.md @@ -0,0 +1,90 @@ +# Debug Decision Trees + +These trees provide fast triage paths for recurring OpenKore failures. + +## A) Bot not moving / Routing failure + +```mermaid +flowchart TD + A[Bot not moving] --> B{AI ticking?} + B -- No --> B1[Inspect src/AI.pm and main loop in src/functions.pl] + B -- Yes --> C{Task::Route or Task::Move queued?} + C -- No --> C1[Inspect AI decisions in src/AI/CoreLogic.pm and config gates] + C -- Yes --> D{Move packets sent?} + D -- No --> D1[Inspect src/Network/Send.pm and command/task path] + D -- Yes --> E{Position updates received?} + E -- No --> E1[Inspect src/Network/Receive.pm and packet mapping] + E -- Yes --> F{Path valid in fields data?} + F -- No --> F1[Inspect src/Field.pm + fields/*] + F -- Yes --> G[Check blockers/lock settings in control/config.txt] +``` + +Use this when movement appears frozen or route tasks fail repeatedly. + +## B) NPC interaction failure + +```mermaid +flowchart TD + A[NPC interaction fails] --> B{NPC present in ActorList?} + B -- No --> B1[Inspect src/ActorList.pm updates from receive handlers] + B -- Yes --> C{TalkNPC task created?} + C -- No --> C1[Inspect trigger path in AI/commands/plugins] + C -- Yes --> D{Talk packets emitted?} + D -- No --> D1[Inspect src/Task/TalkNPC.pm + src/Network/Send.pm] + D -- Yes --> E{Expected response packet decoded?} + E -- No --> E1[Inspect src/Network/Receive/* serverType handlers] + E -- Yes --> F[Validate dialogue sequence assumptions in task script] +``` + +Use this when NPC talks stop, loop, or complete with wrong branch. + +## C) Plugin not loading / Hook not firing + +```mermaid +flowchart TD + A[Plugin issue] --> B{Plugin loads successfully?} + B -- No --> B1[Check syntax/dependencies + src/Plugins.pm loader logs] + B -- Yes --> C{Hook registered via Plugins::addHooks?} + C -- No --> C1[Fix registration block in plugin file] + C -- Yes --> D{Event emitted at runtime?} + D -- No --> D1[Inspect event source in src/functions.pl / network paths] + D -- Yes --> E{Callback guard conditions pass?} + E -- No --> E1[Log callback inputs and config-dependent guards] + E -- Yes --> F[Inspect side effects via Commands/TaskManager] +``` + +Use this for both plugin boot failures and silent hooks. + +## D) Macro/eventMacro not triggering + +```mermaid +flowchart TD + A[Macro/eventMacro not triggering] --> B{Plugin loaded?} + B -- No --> B1[Inspect plugins/macro or plugins/eventMacro load path] + B -- Yes --> C{Script file parsed?} + C -- No --> C1[Validate macro_file/eventMacro_file in control/config.txt] + C -- Yes --> D{Trigger condition true in live state?} + D -- No --> D1[Log variables/events used by automacro] + D -- Yes --> E{Action executes manually?} + E -- No --> E1[Inspect command path in src/Commands.pm] + E -- Yes --> F[Investigate scheduler/wait constraints in runner] +``` + +Use this to isolate parser issues from trigger logic and action execution. + +## E) Packet desync / XKore sync / visual client desync + +```mermaid +flowchart TD + A[Desync observed] --> B{Wrong serverType/packet table?} + B -- Yes --> B1[Fix control/config.txt + tables/* packet mappings] + B -- No --> C{Receive decode errors?} + C -- Yes --> C1[Inspect MessageTokenizer/PacketParser/Receive handlers] + C -- No --> D{Only in XKore mode?} + D -- No --> D1[Inspect send/receive parity and opcode updates] + D -- Yes --> E{Client and bot state diverge?} + E -- Yes --> E1[Inspect src/Network/XKore*.pm forwarding/session state] + E -- No --> F[Inspect map-transition/session phase sync] +``` + +Use this for protocol mismatches and client-bridge synchronization drift. diff --git a/openkore_llm_knowledge/knowledge/17_code_recipes.md b/openkore_llm_knowledge/knowledge/17_code_recipes.md new file mode 100644 index 0000000000..ba7d8fa816 --- /dev/null +++ b/openkore_llm_knowledge/knowledge/17_code_recipes.md @@ -0,0 +1,147 @@ +# Code Recipes + +Practical snippets for common OpenKore development tasks. + +## 1) Minimal plugin skeleton +```perl +package myPlugin; + +use strict; +use Plugins; +use Log qw(message warning error); + +my $hooks = []; + +Plugins::register('myPlugin', 'Minimal plugin skeleton', \&on_unload); +$hooks = Plugins::addHooks( + ['start3', \&on_start], + ['AI_pre', \&on_ai_pre], +); + +sub on_start { + message "[myPlugin] started\n", 'info'; +} + +sub on_ai_pre { + my ($hookName, $args) = @_; + # lightweight periodic logic +} + +sub on_unload { + Plugins::delHooks($hooks); + message "[myPlugin] unloaded\n", 'info'; +} + +1; +``` + +## 2) Hook registration pattern +```perl +my $hooks = Plugins::addHooks( + ['packet_pre/actor_moved', \&on_actor_moved], + ['packet/map_changed', \&on_map_changed], + ['configModify', \&on_config_modify], +); + +sub on_actor_moved { + my ($hook, $args) = @_; + # inspect $args->{ID}, $args->{coords} +} +``` + +## 3) Command registration +```perl +Commands::register( + ['mycmd', 'run custom action', \&cmd_mycmd] +); + +sub cmd_mycmd { + my (undef, $args) = @_; + message "[myPlugin] mycmd args: $args\n", 'info'; +} + +# on unload: +Commands::unregister('mycmd'); +``` + +## 4) Packet hook example +```perl +my $hooks = Plugins::addHooks( + ['packet_pre/login_error', \&on_login_error], + ['packet/map_changed', \&on_map_changed], +); + +sub on_login_error { + my ($hook, $args) = @_; + warning "Login error packet received: $args->{type}\n"; +} +``` + +## 5) Macro example (`macros.txt`) +```txt +automacro autoHeal { + hp < 40% + timeout 1 + call { + do ss 28 + } +} + +macro goTown { + do move prontera +} +``` + +## 6) eventMacro example +```txt +automacro checkWeight { + InLockMap 1 + weight > 85% + call { + do conf itemsTakeAuto 0 + do ai clear + } +} + +automacro greetOnMap { + map prontera + run-once 1 + call { + do c Hello from OpenKore + } +} +``` + +## 7) Configuration examples (`control/config.txt`) +```txt +lockMap prontera +saveMap prontera +route_randomWalk 1 +teleportAuto_hp 25 +itemsTakeAuto 2 +attackAuto 2 +macro_file macros.txt +eventMacro_file eventMacros.txt +``` + +## 8) Debugging snippets +### 8.1 Print current map and coordinates +```perl +use Globals qw($field %char); +message sprintf("Map=%s x=%d y=%d\n", $field->baseName, $char{pos_to}{x}, $char{pos_to}{y}), 'debug'; +``` + +### 8.2 Trace command callback entry +```perl +sub cmd_mycmd { + my (undef, $args) = @_; + message "[TRACE] cmd_mycmd called with '$args'\n", 'debug'; +} +``` + +### 8.3 Check task queue state quickly +```perl +use TaskManager; +my $task = TaskManager::getTaskManager()->activeTask; +message "Active task: " . ($task ? ref($task) : 'none') . "\n", 'debug'; +``` diff --git a/openkore_llm_knowledge/knowledge/18_openkore_faq.md b/openkore_llm_knowledge/knowledge/18_openkore_faq.md new file mode 100644 index 0000000000..c3a6760545 --- /dev/null +++ b/openkore_llm_knowledge/knowledge/18_openkore_faq.md @@ -0,0 +1,147 @@ +# OpenKore Technical FAQ + +## Architecture +1. **Q:** What is OpenKore's main runtime entry point? + **A:** `src/functions.pl` initializes modules and runs the main loop. +2. **Q:** Where is global runtime state stored? + **A:** Mostly in `src/Globals.pm` and shared structures. +3. **Q:** Which module runs AI logic? + **A:** `src/AI.pm` coordinates AI and calls `src/AI/CoreLogic.pm`. +4. **Q:** How are game entities represented? + **A:** Through `src/Actor.pm` and `src/ActorList.pm`. +5. **Q:** What handles tasks? + **A:** `src/Task.pm` defines tasks and `src/TaskManager.pm` schedules them. +6. **Q:** Where does command parsing happen? + **A:** In `src/Commands.pm`. +7. **Q:** How are plugins managed? + **A:** Via `src/Plugins.pm` lifecycle and hooks API. +8. **Q:** Why is architecture loop-based? + **A:** To repeatedly process receive->state->AI->tasks->send cycles. + +## Debugging +9. **Q:** First check when bot freezes? + **A:** Confirm AI tick and active task state. +10. **Q:** How to distinguish route vs movement failure? + **A:** Check if a route exists, then confirm move packets and position updates. +11. **Q:** Why log packet IDs during failures? + **A:** To detect serverType/recvpackets mismatch quickly. +12. **Q:** Best way to debug plugin hooks? + **A:** Log both hook registration and callback entry. +13. **Q:** Why test with minimal config? + **A:** To remove config side effects masking root causes. +14. **Q:** What indicates desync risk? + **A:** Decode errors, unknown packets, or stale actor updates. +15. **Q:** How to verify task deadlock? + **A:** Inspect active task and queued tasks in task manager. +16. **Q:** Should I debug with all plugins enabled? + **A:** No, isolate with minimal plugin set first. + +## Plugins +17. **Q:** Minimum plugin requirements? + **A:** `Plugins::register`, optional hooks, and clean unload. +18. **Q:** How to add a custom command? + **A:** Use `Commands::register` and unregister on unload. +19. **Q:** Why might plugin load fail silently? + **A:** Early syntax/runtime errors or missing dependencies. +20. **Q:** Can plugins alter AI behavior? + **A:** Yes, via commands, hooks, and task interactions. +21. **Q:** Where are community plugins stored? + **A:** Under `plugins/`. +22. **Q:** How to avoid hook performance issues? + **A:** Keep callbacks lightweight and defer heavy work. +23. **Q:** Can multiple plugins share hook names? + **A:** Yes, each callback runs if registered correctly. +24. **Q:** Why unregister hooks on unload? + **A:** To prevent stale callbacks and inconsistent state. + +## Macros / eventMacro +25. **Q:** Difference between macro and eventMacro? + **A:** Macro is classic command script flow; eventMacro is event/condition-driven automation. +26. **Q:** Where to set macro file path? + **A:** `control/config.txt` via `macro_file`. +27. **Q:** Where to set eventMacro file path? + **A:** `control/config.txt` via `eventMacro_file`. +28. **Q:** Why automacro never triggers? + **A:** Condition false, plugin not loaded, or parse issue. +29. **Q:** How to test macro trigger quickly? + **A:** Manually run equivalent command and compare behavior. +30. **Q:** Why use run-once in eventMacro? + **A:** To avoid repeated firing for one-time actions. +31. **Q:** Can macros execute OpenKore commands? + **A:** Yes, via `do <command>`. +32. **Q:** How to debug eventMacro parser issues? + **A:** Reduce to one trigger + one action and rebuild incrementally. + +## Routing / movement +33. **Q:** Which files control routing logic? + **A:** `src/Task/Route.pm`, `src/Task/Move.pm`, and `src/Field.pm`. +34. **Q:** What is walkability? + **A:** Whether a map cell is traversable based on field data. +35. **Q:** Why does route loop occur? + **A:** Stale position updates, blocked transitions, or invalid field assumptions. +36. **Q:** What is lockMap effect? + **A:** Restricts behavior to a preferred map scope. +37. **Q:** How to validate map data? + **A:** Ensure map exists in `fields/*` and coordinates are valid. +38. **Q:** Why movement works manually but not AI? + **A:** AI priorities/config may override movement intents. +39. **Q:** Can aggressive combat stop movement? + **A:** Yes, combat states can continuously preempt route tasks. +40. **Q:** Best first movement test? + **A:** Short route on same map with minimal automation enabled. + +## Networking +41. **Q:** Which module tokenizes incoming packets? + **A:** `src/Network/MessageTokenizer.pm`. +42. **Q:** Which module decodes packet structures? + **A:** `src/Network/PacketParser.pm` and receive handlers. +43. **Q:** Where is receive dispatch done? + **A:** `src/Network/Receive.pm`. +44. **Q:** Where are server-specific packet handlers? + **A:** `src/Network/Receive/*` and `src/Network/Send/*`. +45. **Q:** Why packet errors after server patch? + **A:** Opcode/table changes requiring updated mappings. +46. **Q:** Can wrong serverType break everything? + **A:** Yes, it causes systematic decode/encode mismatch. +47. **Q:** What causes partial desync? + **A:** Some packets map correctly while others fail for changed opcodes. +48. **Q:** How to isolate receive vs send bug? + **A:** Compare inbound decode logs and outbound packet construction separately. + +## NPC interaction +49. **Q:** Which task handles NPC dialogues? + **A:** `src/Task/TalkNPC.pm`. +50. **Q:** Why NPC script stalls mid-dialogue? + **A:** Response packet mismatch or wrong expected step sequence. +51. **Q:** Must NPC be in actor list first? + **A:** Yes, reliable interaction depends on valid actor presence and position. +52. **Q:** Why wrong NPC gets targeted? + **A:** Incorrect coordinates or stale target selection. +53. **Q:** What to inspect first in NPC failure? + **A:** Talk packet sequence and corresponding receive responses. +54. **Q:** Can plugins interfere with NPC flow? + **A:** Yes, if they alter commands/tasks concurrently. + +## Config behavior +55. **Q:** Where are core behavior settings? + **A:** `control/config.txt`. +56. **Q:** What file controls monster behavior rules? + **A:** `control/mon_control.txt`. +57. **Q:** Why config changes seem ignored? + **A:** Reload needed, wrong key, or overridden by plugin automation. +58. **Q:** How to keep config deterministic? + **A:** Use minimal explicit settings and disable conflicting automation. +59. **Q:** What is saveMap used for? + **A:** Preferred return/safety map behavior. +60. **Q:** Why document config with code changes? + **A:** Behavior is policy-driven; docs prevent invisible config regressions. + +## XKore / client sync +61. **Q:** What is XKore2 role? + **A:** Local proxy/relay flow for account/char/map sessions. +62. **Q:** Why client view stale but bot state updates? + **A:** Forwarding/session sync issue in XKore bridge path. +63. **Q:** Is debugging Direct and XKore identical? + **A:** No, transport/session layers differ significantly. +64. **Q:** First step for XKore sync bugs? + **A:** Confirm active mode and trace handshake phase transitions. diff --git a/openkore_llm_knowledge/knowledge/19_glossary.md b/openkore_llm_knowledge/knowledge/19_glossary.md new file mode 100644 index 0000000000..abf1c04be5 --- /dev/null +++ b/openkore_llm_knowledge/knowledge/19_glossary.md @@ -0,0 +1,27 @@ +# Glossary + +- **Actor**: Runtime entity abstraction (player, monster, NPC, portal) managed via `Actor` modules. +- **AI queue**: Ordered AI intention/state processing that decides which action/task should run next. +- **Task**: Executable unit with lifecycle (start/process/finish), e.g., move, route, NPC interaction. +- **Plugin hook**: Callback registration point triggered by runtime events (`Plugins::addHooks`). +- **Command handler**: Function bound to a command name through `Commands::register`. +- **Packet handler**: Receive/send routine that decodes or encodes specific protocol packets. +- **Route**: Planned path between coordinates/maps, later executed stepwise by movement tasks. +- **Automacro**: Conditional automation rule that triggers scripted actions when conditions match. +- **eventMacro**: Event/condition-driven macro framework with parser, runner, and trigger model. +- **lockMap**: Configuration policy limiting movement/behavior to a designated map. +- **saveMap**: Preferred map used for return/recovery behavior. +- **Walkability**: Whether map cells are traversable according to field data and routing constraints. +- **Client sync**: Consistency between bot internal state and visual game client state (important in XKore). +- **Server packet**: Protocol message exchanged with the game server; parsed/encoded by network modules. +- **ServerType**: Packet profile selection controlling opcode and packet layout mapping. +- **Receive dispatch**: Mechanism mapping inbound opcode frames to concrete handler methods. +- **Send pipeline**: Path from action intent to serialized outbound packet. +- **XKore mode**: Bridged/proxy connection model variants (`XKore`, `XKore2`, `XKoreProxy`). +- **Main loop tick**: One full iteration of runtime processing in `functions.pl`. +- **Field data**: Map walkability/cell data used by route calculations. +- **Desync**: Divergence between expected protocol/state and observed runtime behavior. +- **Hook event**: Named runtime signal emitted for plugin subscribers. +- **Task manager**: Scheduler coordinating active/pending tasks and transitions. +- **Packet desync**: Failure mode where packet mappings no longer align with server behavior. +- **Control files**: Operator-facing config files under `control/` that drive runtime policy. diff --git a/openkore_llm_knowledge/knowledge/20_code_index.md b/openkore_llm_knowledge/knowledge/20_code_index.md new file mode 100644 index 0000000000..d0b179e3f4 --- /dev/null +++ b/openkore_llm_knowledge/knowledge/20_code_index.md @@ -0,0 +1,46 @@ +# Code Index (Architecture-Oriented) + +## Runtime entry and orchestration +- `src/functions.pl` — startup sequence and main loop +- `src/Modules.pm` — module loading registry +- `src/Globals.pm` — shared runtime state container + +## AI +- `src/AI.pm` — AI framework and queue/state management +- `src/AI/CoreLogic.pm` — core decision routines + +## Actor and world model +- `src/Actor.pm` — actor base model +- `src/ActorList.pm` — actor collections/indexing +- `src/Actor/*` — actor specializations +- `src/Field.pm` — map/field representation + +## Task execution +- `src/Task.pm` — task abstraction +- `src/TaskManager.pm` — scheduler/executor +- `src/Task/Route.pm` — route planning/execution wrapper +- `src/Task/Move.pm` — movement task +- `src/Task/TalkNPC.pm` — NPC dialogue workflow +- `src/Task/UseSkill.pm` — skill-use workflows + +## Networking and packets +- `src/Network.pm` — network facade and state +- `src/Network/DirectConnection.pm` — direct server mode +- `src/Network/XKore.pm`, `src/Network/XKore2.pm`, `src/Network/XKoreProxy.pm` — proxy/bridge modes +- `src/Network/MessageTokenizer.pm` — packet frame/token extraction +- `src/Network/PacketParser.pm` — packet definition parsing +- `src/Network/Receive.pm` + `src/Network/Receive/*` — inbound packet dispatch/handlers +- `src/Network/Send.pm` + `src/Network/Send/*` — outbound packet builders + +## Commands, plugins, and automation +- `src/Commands.pm` — command parser/dispatcher +- `src/Plugins.pm` — plugin lifecycle and hook API +- `plugins/macro/macro.pl` + `plugins/macro/Macro/*` — legacy macro automation +- `plugins/eventMacro/eventMacro.pl` + `plugins/eventMacro/eventMacro/*` — event-driven macro automation + +## Configuration and data sources +- `src/Settings.pm` — config path and file registration +- `src/FileParsers.pm` — config/table parsing +- `control/*` — user behavior policies and runtime options +- `tables/*` — server/data tables +- `fields/*` — map grids for routing/navigation diff --git a/openkore_llm_knowledge/knowledge/21_core_module_index.md b/openkore_llm_knowledge/knowledge/21_core_module_index.md new file mode 100644 index 0000000000..be33611294 --- /dev/null +++ b/openkore_llm_knowledge/knowledge/21_core_module_index.md @@ -0,0 +1,13 @@ +# Core Module Index + +- `src/functions.pl` — bootstrap and main loop. +- `src/Modules.pm` — module registration/loading. +- `src/Globals.pm` — shared runtime state. +- `src/Settings.pm` — config path and file registration. +- `src/FileParsers.pm` — control/table parsing. +- `src/Commands.pm` — command parser and handlers. +- `src/AI.pm` and `src/AI/CoreLogic.pm` — AI orchestration and decisions. +- `src/Actor.pm` and `src/ActorList.pm` — entity model and indexing. +- `src/Task.pm` and `src/TaskManager.pm` — task abstraction and scheduling. +- `src/Task/Route.pm`, `src/Task/Move.pm`, `src/Task/TalkNPC.pm` — routing, movement, NPC flows. +- `src/Network.pm`, `src/Network/PacketParser.pm`, `src/Network/Receive.pm`, `src/Network/Send.pm` — network core. diff --git a/openkore_llm_knowledge/knowledge/22_common_pitfalls.md b/openkore_llm_knowledge/knowledge/22_common_pitfalls.md new file mode 100644 index 0000000000..66dbbe6238 --- /dev/null +++ b/openkore_llm_knowledge/knowledge/22_common_pitfalls.md @@ -0,0 +1,49 @@ +# Common Pitfalls + +Concise checklist of high-frequency debugging traps in OpenKore architecture and operations. + +## 1) Assuming movement issues are only routing bugs +- AI state locks, command overrides, or task queue starvation can mimic routing failures. +- Check AI/task state before editing route logic. + +## 2) Ignoring serverType/packet table drift +- Packet desync often starts after server updates when recvpackets/opcodes change. +- Keep `control/config.txt` server settings aligned with `tables/*` packet definitions. + +## 3) Debugging plugin hooks without proving event emission +- A hook can be correct but never run if the event is not emitted in the current flow. +- Verify both registration and actual event source path. + +## 4) Treating macro trigger failures as parser failures +- Many cases are valid parse + false trigger conditions. +- Separate: plugin load -> file parse -> trigger true -> action execution. + +## 5) Overlooking config-side constraints +- `control/config.txt`, `mon_control.txt`, and related files can disable or redirect behavior. +- Always test with minimal, known-good config for reproduction. + +## 6) Mixing XKore and DirectConnection assumptions +- Transport/session behavior differs; debugging steps are mode-specific. +- Confirm active mode first before tracing packet/session paths. + +## 7) Trusting visual client state over bot internal state +- In XKore scenarios, client view may lag/diverge from bot state. +- Compare bot-side actor/position updates with forwarded client packets. + +## 8) Investigating deep modules before reproducing minimally +- Large automation stacks (AI + plugin + macro + eventMacro) hide root causes. +- Reproduce with minimal script/task/command to localize fault domain. + +## Fast isolation matrix +| Symptom | First subsystem to verify | Primary files | +|---|---|---| +| Bot not moving | AI/Task | `src/AI.pm`, `src/TaskManager.pm` | +| Route fails | Routing/Field | `src/Task/Route.pm`, `src/Field.pm` | +| NPC fails | Task + Receive | `src/Task/TalkNPC.pm`, `src/Network/Receive.pm` | +| Plugin not loading | Plugin loader | `src/Plugins.pm`, plugin file | +| Hook silent | Event dispatch | `src/Plugins.pm`, event source module | +| Macro not triggering | Macro pipeline | `plugins/macro/*`, `src/Commands.pm` | +| eventMacro issues | eventMacro runner | `plugins/eventMacro/eventMacro/*` | +| Packet desync | Packet mapping | `src/Network/PacketParser.pm`, `tables/*` | +| XKore sync issues | XKore bridge | `src/Network/XKore*.pm` | +| Client view stale | XKore forwarding | `src/Network/XKore*.pm`, `src/ActorList.pm` | diff --git a/openkore_llm_knowledge/knowledge/23_debugging_guide.md b/openkore_llm_knowledge/knowledge/23_debugging_guide.md new file mode 100644 index 0000000000..ea888e238c --- /dev/null +++ b/openkore_llm_knowledge/knowledge/23_debugging_guide.md @@ -0,0 +1,23 @@ +# Debugging Guide (Networking and Tables) + +## 1) Confirm server/profile alignment +- Validate `serverType` and packet profile selection in `control/config.txt`. +- Verify matching packet tables in `tables/*` for the selected server family. + +## 2) Trace receive path +- `src/Network/MessageTokenizer.pm` -> `src/Network/PacketParser.pm` -> `src/Network/Receive.pm` -> `src/Network/Receive/*` +- Look for first packet decode mismatch point. + +## 3) Trace send path +- Inspect command/task origin, then `src/Network/Send.pm` + `src/Network/Send/*` packet construction. +- Confirm opcode/structure expected by current server profile. + +## 4) XKore-specific checks +- Inspect `src/Network/XKore.pm`, `src/Network/XKore2.pm`, `src/Network/XKoreProxy.pm`. +- Validate session phase transitions and packet forwarding symmetry. + +## 5) Iterative workflow +1. Reproduce with minimal actions. +2. Isolate receive vs send failure. +3. Validate serverType/packet table pairing. +4. Re-test after one controlled change. diff --git a/openkore_llm_knowledge/knowledge/24_learning_path.md b/openkore_llm_knowledge/knowledge/24_learning_path.md new file mode 100644 index 0000000000..f9c988b0e1 --- /dev/null +++ b/openkore_llm_knowledge/knowledge/24_learning_path.md @@ -0,0 +1,49 @@ +# Learning Path for New OpenKore Developers + +## Stage 0: Orientation (Day 1) +- Read `01_llm_context.md`, `02_architecture_overview.md`, `03_system_architecture_map.md`. +- Goal: understand receive->state->AI->task->send loop and key directories. + +## Stage 1: Runtime core comprehension (Days 2-3) +- Study `src/functions.pl`, `src/Globals.pm`, `src/Modules.pm`. +- Track one full startup + one main-loop tick. +- Exercise: write a short note describing what runs each tick. + +## Stage 2: Network + actor model (Days 4-5) +- Study `src/Network.pm`, `src/Network/Receive.pm`, `src/Network/Send.pm`, `src/ActorList.pm`. +- Exercise: trace one incoming packet to a world-state mutation. +- Exercise: trace one outgoing action from command/task to send packet. + +## Stage 3: AI and task flow (Days 6-7) +- Study `src/AI.pm`, `src/AI/CoreLogic.pm`, `src/TaskManager.pm`, `src/Task/Route.pm`, `src/Task/TalkNPC.pm`. +- Exercise: map how AI chooses a task and how task completion feeds back. + +## Stage 4: Plugins and automation (Week 2) +- Study `src/Plugins.pm`, `src/Commands.pm`, `plugins/macro/*`, `plugins/eventMacro/*`. +- Build a minimal plugin with one hook and one command. +- Add one macro and one eventMacro automation rule. + +## Stage 5: Config-driven behavior (Week 2) +- Study `control/config.txt`, `control/mon_control.txt`, relevant table files. +- Exercise: change one behavior via config only and document impact. + +## Stage 6: Debugging proficiency (Week 3) +- Use `15_debugging_playbook.md`, `16_debug_decision_trees.md`, `22_common_pitfalls.md`. +- Reproduce and fix one issue in each category: + - movement/routing + - plugin/hook + - macro/eventMacro + - packet/XKore sync + +## Stage 7: Advanced protocol + XKore (Week 3+) +- Study `09_networking_packets.md`, `10_xkore_modes.md`. +- Compare DirectConnection vs XKore2 flow and identify mode-specific risks. + +## Suggested competency checklist +- [ ] Explain OpenKore runtime architecture from memory. +- [ ] Trace packet receive and send paths. +- [ ] Implement and unload a safe plugin. +- [ ] Diagnose macro and eventMacro trigger failures. +- [ ] Diagnose route and movement failures. +- [ ] Diagnose basic packet/serverType desync. +- [ ] Explain XKore sync failure patterns. diff --git a/openkore_llm_knowledge/knowledge/26_architecture_diagrams.md b/openkore_llm_knowledge/knowledge/26_architecture_diagrams.md new file mode 100644 index 0000000000..54654be8e2 --- /dev/null +++ b/openkore_llm_knowledge/knowledge/26_architecture_diagrams.md @@ -0,0 +1,17 @@ +# Architecture Diagrams (Mermaid) + +This file indexes the runtime diagrams for OpenKore subsystem interactions. + +## Included diagrams +1. **AI execution loop** — `06_execution_flows.md` +2. **Packet receive flow** — `09_networking_packets.md` +3. **NPC interaction flow** — `07_npc_interaction_flow.md` +4. **Plugin hook execution** — `27_system_flows.md` +5. **Routing and movement flow** — `08_routing_and_movement.md` +6. **Command execution flow** — `27_system_flows.md` + +## Reading guidance +- Start with `06_execution_flows.md` for the main runtime cycle. +- Use `09_networking_packets.md` and `10_xkore_modes.md` for transport/protocol context. +- Use `07_npc_interaction_flow.md` and `08_routing_and_movement.md` for task-level behavioral flows. +- Use `27_system_flows.md` for extension and control-plane flows. diff --git a/openkore_llm_knowledge/knowledge/27_system_flows.md b/openkore_llm_knowledge/knowledge/27_system_flows.md new file mode 100644 index 0000000000..61357e961e --- /dev/null +++ b/openkore_llm_knowledge/knowledge/27_system_flows.md @@ -0,0 +1,35 @@ +# System Flows + +## Plugin hook execution flow +Plugins extend runtime behavior through hook callbacks registered in `src/Plugins.pm` and commonly invoke `src/Commands.pm` or task APIs. + +```mermaid +flowchart TD + A[Plugin load\nplugins/*.pl] --> B[Plugins::register] + B --> C[Plugins::addHooks] + C --> D[Runtime event\nloop/packet/config] + D --> E[Plugin callback] + E --> F{Action needed?} + F -- No --> G[Return] + F -- Yes --> H[Execute command\nsrc/Commands.pm] + H --> I[Task/Network side effects] + I --> G +``` + +Hook callbacks are event-driven and should remain lightweight; heavy operations are usually delegated to commands/tasks. + +## Command execution flow + +```mermaid +flowchart TD + A[Input source\nconsole/plugin/macro] --> B[Commands::run] + B --> C[Command parser\nsrc/Commands.pm] + C --> D{Built-in or plugin cmd?} + D -- Built-in --> E[Built-in handler] + D -- Plugin --> F[Plugin command callback] + E --> G[Update state / queue task / send packet] + F --> G + G --> H[Feedback to interface/log] +``` + +Commands are a shared control surface across manual operations, plugins, and macro automation. diff --git a/openkore_llm_knowledge/knowledge/29_prompt_examples_for_users.md b/openkore_llm_knowledge/knowledge/29_prompt_examples_for_users.md new file mode 100644 index 0000000000..ba46ebe919 --- /dev/null +++ b/openkore_llm_knowledge/knowledge/29_prompt_examples_for_users.md @@ -0,0 +1,49 @@ +# Prompt Examples for Users + +Realistic prompts users can ask an OpenKore-focused GPT assistant. + +## Architecture and code navigation +1. "Explain how `src/functions.pl` orchestrates the OpenKore main loop." +2. "Map the dependency chain between AI, TaskManager, and Network::Send." +3. "Which modules should I read first to understand actor updates from packets?" +4. "Summarize how plugin hooks interact with command execution." + +## Debugging +5. "My bot stopped moving. Give me a step-by-step diagnosis plan with files to inspect." +6. "How do I distinguish packet desync from route calculation failure?" +7. "Generate a troubleshooting checklist for NPC dialogue failures." +8. "Why are my plugin hooks not firing even though plugin loads fine?" +9. "Help me debug eventMacro that parses but never triggers." +10. "Create a minimal reproduction plan for XKore client sync issues." + +## Plugins +11. "Create a minimal plugin skeleton with register, hooks, and unload cleanup." +12. "Add a custom command `farmstatus` that prints current map and task." +13. "Show best practices for lightweight hook callbacks in OpenKore plugins." +14. "How do I instrument a plugin with debug logs without spamming output?" + +## Macro / eventMacro +15. "Write an automacro that pauses looting above 85% weight." +16. "Create eventMacro rules to send a chat message only once when entering Prontera." +17. "Compare macro and eventMacro for reactive combat behavior." +18. "How can I test whether a macro trigger condition is actually true at runtime?" + +## Routing and movement +19. "Explain why route tasks can oscillate and how to debug it." +20. "Show a script strategy to test short movement segments before full routes." +21. "What configs can block movement even if route exists?" + +## Networking and packets +22. "How do I verify that serverType and recvpackets are correctly configured?" +23. "Trace one incoming movement packet from socket bytes to Actor update." +24. "What logs should I capture when packet decode errors start after a patch?" +25. "Explain where to add temporary logging for packet send construction." + +## Configuration +26. "Review my config goals and suggest a minimal stable `config.txt` baseline." +27. "Which `mon_control.txt` settings commonly interfere with movement tasks?" +28. "How do lockMap and saveMap influence AI behavior?" + +## Learning and onboarding +29. "Build me a 2-week learning plan to become productive in OpenKore development." +30. "Give me a study order for networking, AI, tasks, plugins, and macros with exercises." diff --git a/openkore_llm_knowledge/knowledge/30_dataset_summary.md b/openkore_llm_knowledge/knowledge/30_dataset_summary.md new file mode 100644 index 0000000000..15acba93bd --- /dev/null +++ b/openkore_llm_knowledge/knowledge/30_dataset_summary.md @@ -0,0 +1,37 @@ +# Dataset Summary + +## Purpose +This dataset provides an LLM-oriented knowledge layer for OpenKore architecture, runtime flow, debugging, and practical usage. + +## Coverage areas +- **Architecture core**: context, subsystem boundaries, dependency map, code index. +- **Runtime flows**: AI loop, packet receive flow, NPC interaction, routing/movement, command and plugin hook flows. +- **Networking**: packet pipeline and XKore mode behavior. +- **Debugging**: playbook, decision trees, and common pitfalls. +- **Practical usage**: code recipes, FAQ, glossary, learning path, and user prompt examples. + +## Main artifact groups +1. **Foundational architecture docs** (`01`-`05`, `11`-`14`, `20`, `21`) +2. **Flow/diagram docs** (`06`-`10`, `26`, `27`) +3. **Troubleshooting docs** (`15`, `16`, `22`, `23`) +4. **Developer enablement docs** (`17`, `18`, `19`, `24`, `29`, `30`) + +## Intended users +- Developers onboarding to OpenKore internals. +- Maintainers debugging runtime and packet issues. +- LLM assistants that need stable structural context for responses. + +## Strengths +- Grounded in likely module and directory references from the repository. +- Organized by numbered, task-oriented documents. +- Includes actionable diagnostics and reusable snippet patterns. + +## Known limits +- Documentation is descriptive and may lag upstream implementation changes. +- Server-specific packet behavior can change rapidly after game updates. +- Mermaid diagrams prioritize clarity over exhaustive edge-case detail. + +## Maintenance recommendations +- Re-validate packet/XKore docs after serverType or recvpackets updates. +- Update recipe snippets when plugin/hook APIs evolve. +- Keep FAQ and pitfalls synced with recurring issue reports. diff --git a/openkore_llm_knowledge/knowledge/architecture_overview.md b/openkore_llm_knowledge/knowledge/architecture_overview.md deleted file mode 100644 index 4114739c4e..0000000000 --- a/openkore_llm_knowledge/knowledge/architecture_overview.md +++ /dev/null @@ -1,15 +0,0 @@ -# OpenKore Core Architecture Overview - -This bundle captures only the **core architecture** from `src/` for public AI-assistant knowledge use. - -## Structural layers -- **Runtime orchestration**: `core/functions.pl` controls initialization stages and the recurring main loop. -- **State and configuration**: `core/Globals.pm`, `core/Settings.pm`, `core/FileParsers.pm`. -- **Behavior engine**: `core/AI.pm`, `core/AI/CoreLogic.pm`. -- **World model**: `core/Actor.pm`, `core/ActorList.pm`. -- **Task execution**: `core/Task.pm`, `core/TaskManager.pm`, `core/Task/{Route,Move,TalkNPC}.pm`. -- **Packet/network core**: `core/Network.pm`, `core/Network/{PacketParser,Receive}.pm`. -- **Command surface**: `core/Commands.pm`. - -## Why this is “core” -The selected modules define startup, state, decision logic, movement/NPC flows, and packet ingestion—the minimum architecture needed to explain how OpenKore runs end-to-end without pulling in plugin- or UI-heavy areas. diff --git a/openkore_llm_knowledge/knowledge/code_recipes.md b/openkore_llm_knowledge/knowledge/code_recipes.md deleted file mode 100644 index ac76a58024..0000000000 --- a/openkore_llm_knowledge/knowledge/code_recipes.md +++ /dev/null @@ -1,196 +0,0 @@ -# OpenKore Code Recipes - -Practical snippets for common OpenKore customization tasks. - ---- - -## 1) Plugin Development - -### A) Minimal plugin skeleton -```perl -package MyPlugin; - -use strict; -use Plugins; -use Log qw(message); - -my $hooks; -my $cmds; - -Plugins::register('MyPlugin', 'Minimal example plugin', \&on_unload); - -sub on_unload { - Plugins::delHooks($hooks) if $hooks; - Commands::unregister($cmds) if $cmds; - message "[MyPlugin] unloaded\n", 'success'; -} - -1; -``` - -### B) Registering hooks -```perl -use Plugins; -use Log qw(message); - -$hooks = Plugins::addHooks( - ['start3', sub { message "[MyPlugin] start3 fired\n", 'info'; }], - ['mainLoop_pre', sub { - # periodic logic - # keep this lightweight - }], - ['configModify', sub { - my (undef, $args) = @_; - message "[MyPlugin] config changed: $args->{key}\n", 'info'; - }], -); -``` - -### C) Handling packets (hook-based) -```perl -# Example: observe a packet-level hook exposed by core/plugins -$hooks = Plugins::addHooks( - ['packet_pre/map_changed', sub { - my (undef, $args) = @_; - # inspect packet arguments; avoid mutating unless needed - message "[MyPlugin] map changed detected\n", 'info'; - }], -); -``` - -### D) Adding console commands -```perl -use Commands; -use Log qw(message); - -$cmds = Commands::register([ - 'mycmd', - 'My custom command', - sub { - my (undef, $params) = @_; - $params //= ''; - message "[MyPlugin] mycmd called with: $params\n", 'list'; - } -]); -``` - ---- - -## 2) Macros (`macros.txt` style) - -### A) Teleport on HP threshold -```txt -automacro emergency_tp { - hp < 35% - timeout 1 - run-once 0 - call { - do conf teleportAuto_useSkill 1 - do conf teleportAuto_useChatCommand 0 - do sp 26 1 # Teleport level 1 (example) - } -} -``` - -### B) Simple farming loop -```txt -macro farm_loop { - do move prontera 150 180 - pause 1 - do ai auto - pause 10 - do move prontera 120 160 - pause 1 - do ai auto - pause 10 - do macro farm_loop -} - -automacro start_farming { - map prontera - timeout 3 - run-once 1 - call farm_loop -} -``` - -### C) NPC interaction macro -```txt -macro use_healer { - do talknpc 156 186 c r0 c c -} - -automacro low_hp_heal { - hp < 60% - map prontera - timeout 15 - run-once 0 - call use_healer -} -``` - ---- - -## 3) EventMacros (`eventMacros.txt` style) - -### A) Reacting to monster presence -```txt -automacro danger_near { - MonsterNear 1 - MonsterNearDist < 6 - call { - do conf attackAuto 0 - do sp 26 1 - } -} -``` - -### B) Map entry trigger -```txt -automacro entered_payon { - InMap payon - mapLoaded - call { - do conf attackAuto 2 - do conf route_randomWalk 1 - do ai auto - } -} -``` - ---- - -## 4) Configuration Recipes - -### A) Item pickup logic (`items_control.txt`) -```txt -# item name keep storage sell -"Red Potion" 30 0 1 -"Blue Potion" 20 0 1 -"Jellopy" 0 0 1 -"Empty Bottle" 0 0 0 -``` - -### B) Monster avoidance (`mon_control.txt`) -```txt -# monster attack teleport search -"Baphomet" -1 1 0 -"Hydra" 1 0 0 -"Pupa" 0 0 0 -``` - -### C) Route locking (`config.txt`) -```txt -lockMap prontera -lockMap_x 156 -lockMap_y 186 -attackAuto_inLockOnly 1 -route_randomWalk 0 -``` - ---- - -## Notes -- Packet IDs, skill IDs, and command behaviors may vary by server profile. -- Keep plugin `mainLoop_pre` handlers lightweight to avoid lag. -- Test macros/eventMacros in a safe map before production farming routes. diff --git a/openkore_llm_knowledge/knowledge/config_system.md b/openkore_llm_knowledge/knowledge/config_system.md deleted file mode 100644 index c81528729b..0000000000 --- a/openkore_llm_knowledge/knowledge/config_system.md +++ /dev/null @@ -1,27 +0,0 @@ -# Configuration System (Curated from `control/`) - -Configuration files in `control/` define runtime behavior and policies. This bundle copies the full `control/*.txt` set into `openkore_llm_knowledge/config/`. - -## `config.txt` -Primary behavior configuration file. It contains the main bot options (AI behavior, combat options, looting, movement, network/runtime preferences, plugin-related keys, etc.). - -Use `config.txt` to set high-level behavior and feature toggles. - -## `items_control.txt` -Item-level policy file controlling item handling rules. -Typical usage includes deciding whether items should be picked up, stored, sold, or ignored based on names/IDs and rule flags. - -Use `items_control.txt` for inventory and looting policy tuning. - -## `mon_control.txt` -Monster-level AI policy file. -It defines per-monster behavior such as attack mode, teleport/disconnect response, and conditional thresholds. - -Use `mon_control.txt` to tune combat risk handling and target strategy. - -## Practical model -- `config.txt` = global behavior defaults -- `items_control.txt` = item policy overrides -- `mon_control.txt` = monster policy overrides - -Together, these provide most day-to-day operational control without changing source modules. diff --git a/openkore_llm_knowledge/knowledge/core_module_index.md b/openkore_llm_knowledge/knowledge/core_module_index.md deleted file mode 100644 index 0c04d08687..0000000000 --- a/openkore_llm_knowledge/knowledge/core_module_index.md +++ /dev/null @@ -1,20 +0,0 @@ -# Core Module Index - -- `core/functions.pl` — startup states, initialization sequence, and main loop transitions. -- `core/Modules.pm` — module registration and reload mechanics. -- `core/Globals.pm` — shared globals and runtime containers. -- `core/Settings.pm` — path/file registration and load policies. -- `core/FileParsers.pm` — parsers for core config/table content. -- `core/Commands.pm` — runtime command entrypoints. -- `core/AI.pm` — AI framework control. -- `core/AI/CoreLogic.pm` — primary decision-cycle logic. -- `core/Actor.pm` — actor base model. -- `core/ActorList.pm` — actor indexing/lookup list. -- `core/Task.pm` — task primitive. -- `core/TaskManager.pm` — task scheduler/runner. -- `core/Task/Route.pm` — route planning task. -- `core/Task/Move.pm` — movement task execution. -- `core/Task/TalkNPC.pm` — NPC conversation task. -- `core/Network.pm` — network state machine abstraction. -- `core/Network/PacketParser.pm` — packet decode/shape logic. -- `core/Network/Receive.pm` — incoming packet dispatch and handlers. diff --git a/openkore_llm_knowledge/knowledge/core_subsystems.md b/openkore_llm_knowledge/knowledge/core_subsystems.md deleted file mode 100644 index 2be26062db..0000000000 --- a/openkore_llm_knowledge/knowledge/core_subsystems.md +++ /dev/null @@ -1,27 +0,0 @@ -# Core Subsystems - -## AI -- `core/AI.pm` hosts AI stack control and high-level bot behavior gates. -- `core/AI/CoreLogic.pm` contains central decision logic used every loop tick. - -## Actor -- `core/Actor.pm` defines the base actor abstraction. -- `core/ActorList.pm` manages indexed actor collections and lookups. - -## Network -- `core/Network.pm` defines connection state abstraction. -- `core/Network/PacketParser.pm` performs packet-level extraction/reconstruction logic. -- `core/Network/Receive.pm` maps and dispatches incoming packet handlers. - -## Tasks -- `core/Task.pm` is the task base abstraction. -- `core/TaskManager.pm` queues/runs tasks. -- `core/Task/Route.pm` and `core/Task/Move.pm` handle routing and movement execution. -- `core/Task/TalkNPC.pm` encapsulates NPC interaction task flow. - -## Commands -- `core/Commands.pm` provides command parsing/dispatch used by console and automation. - -## File parsing -- `core/Settings.pm` registers and resolves data/control files. -- `core/FileParsers.pm` parses key config/table formats into runtime structures. diff --git a/openkore_llm_knowledge/knowledge/debugging_guide.md b/openkore_llm_knowledge/knowledge/debugging_guide.md deleted file mode 100644 index 25bc17294c..0000000000 --- a/openkore_llm_knowledge/knowledge/debugging_guide.md +++ /dev/null @@ -1,27 +0,0 @@ -# Debugging Guide (Networking & Tables) - -## 1) Confirm server/table alignment -- Verify the active server profile in `tables/servers.txt`. -- Confirm matching `recvpackets.txt` set (e.g., kRO vs iRO). -- Packet mismatch symptoms: unknown switch warnings, broken map/actor parsing, failed actions. - -## 2) Trace receive path -- Start from `networking/Receive.pm` dispatch behavior. -- Compare overridden handlers in server-specific receive modules (`ServerType0`, `kRO`, `iRO`). -- Use packet references (`tables/packetlist.txt`, `tables/packetdescriptions.txt`) to identify suspect opcodes. - -## 3) Trace send path -- Inspect `networking/Send.pm` and server-specific send modules. -- Validate whether a command/action is encoded in the expected packet version/format. - -## 4) XKore-specific troubleshooting -- For bridge issues, inspect `networking/XKore.pm` lifecycle and channel forwarding. -- For proxy/session issues, inspect `networking/XKore2.pm` and `Network/XKore2/{AccountServer,CharServer,MapServer}.pm`. -- Ensure client/server stage transitions match expected state. - -## 5) Practical iterative workflow -1. Reproduce issue with minimal actions. -2. Isolate receive vs send failure. -3. Validate server profile + recvpackets table pairing. -4. Check server-specific module overrides. -5. Re-test and document exact packet/table combination that works. diff --git a/openkore_llm_knowledge/knowledge/debugging_playbook.md b/openkore_llm_knowledge/knowledge/debugging_playbook.md deleted file mode 100644 index 9eadc082ea..0000000000 --- a/openkore_llm_knowledge/knowledge/debugging_playbook.md +++ /dev/null @@ -1,244 +0,0 @@ -# OpenKore Debugging Playbook - -This playbook provides structured troubleshooting for common OpenKore runtime issues. - ---- - -## 1) Bot Not Moving - -### Possible causes -- Movement/task pipeline blocked (AI state, mutex, sit/stand state, map lock logic). -- Invalid route target or unreachable coordinates. -- Network-side movement packets not being sent/accepted. -- Configuration constraints preventing movement. - -### Files to inspect -- Core runtime/task modules: - - `core/AI/CoreLogic.pm` - - `core/TaskManager.pm` - - `core/Task/Route.pm` - - `core/Task/Move.pm` -- Movement/network path: - - `networking/Send.pm` - - `networking/Network/Send/ServerType0.pm` (or active server-specific send module) -- Config: - - `config/config.txt` - - `config/timeouts.txt` - -### Configuration checks -- Verify movement/route-related settings in `config.txt` (lock map, teleport fallback, attack-route behavior). -- Confirm no conflicting delay/timeouts in `timeouts.txt`. -- Confirm map/server profile alignment (server selection and table set). - -### Debugging steps -1. Confirm AI is active and not paused/stuck in an unexpected state. -2. Inspect active tasks (route/move) and whether they are advancing or looping. -3. Check if movement packets are being emitted by send logic. -4. Validate map data/path constraints from route calculations. -5. Re-test with a simple direct move target; compare behavior. - ---- - -## 2) NPC Interaction Issues - -### Possible causes -- NPC not found in actor list or wrong target coordinates. -- Dialogue state machine mismatch (unexpected prompt/response sequence). -- Packet handler mismatch for NPC-related responses. -- Route-to-NPC task never reaches interaction range. - -### Files to inspect -- Task and command flow: - - `core/Task/TalkNPC.pm` - - `core/Commands.pm` - - `core/AI/CoreLogic.pm` -- Receive handlers: - - `networking/Receive.pm` - - server-specific receive module (`networking/Network/Receive/*.pm`) -- Tables/config: - - `tables/portals.txt` (if interaction depends on map pathing) - - `config/config.txt` - -### Configuration checks -- Ensure NPC scripts/macros (if used) match actual in-game dialogue sequence. -- Confirm route/lock-map settings still allow navigation to NPC. -- Validate server packet profile (`recvpackets`) is current. - -### Debugging steps -1. Verify NPC is visible/tracked in actor context. -2. Step through `TalkNPC` task progression (init, talk, select, complete). -3. Check receive handlers for expected NPC dialogue packets. -4. Validate command/script parameters (NPC name/id, sequence values). -5. Retry with a minimal NPC interaction command path. - ---- - -## 3) Plugin Not Loading - -### Possible causes -- Plugin disabled by load policy. -- Plugin name not listed when selective loading is enabled. -- Missing dependencies/imports in plugin code. -- Startup callback failure throws during register/load. - -### Files to inspect -- Plugin architecture docs/code: - - `knowledge/plugin_system.md` - - `plugins/<plugin>/<plugin>.pl` -- Config controls: - - `config/sys.txt` -- (Core plugin loader reference in source repo: `src/Plugins.pm`, `src/functions.pl`) - -### Configuration checks -- In `sys.txt`, verify: - - `loadPlugins` mode - - `loadPlugins_list` / `skipPlugins_list` -- Confirm plugin filename matches expected plugin name. - -### Debugging steps -1. Check startup logs for plugin load exception details. -2. Validate plugin register/unload function signatures. -3. Verify required modules are available and import paths are correct. -4. If selective loading, explicitly include plugin in list and restart. -5. Test plugin command availability after successful load. - ---- - -## 4) Macro Not Triggering - -### Possible causes -- Macro/eventMacro plugin not loaded. -- Macro file path key incorrect (`macro_file` / `eventMacro_file`). -- Condition never becomes true (state/event mismatch). -- Automacro disabled, timed out, or blocked by check mode. - -### Files to inspect -- Macro system docs/code: - - `knowledge/macro_system.md` - - `plugins/macro/macro.pl` - - `plugins/eventMacro/eventMacro.pl` - - `plugins/eventMacro/eventMacro/*` -- Config: - - `config/config.txt` - - `config/sys.txt` - -### Configuration checks -- Confirm macro plugin is in plugin load list. -- Validate macro file names/paths (`macros.txt` / `eventMacros.txt` equivalents). -- Check macro/eventMacro options (orphans/check-on-AI/enable states). - -### Debugging steps -1. Confirm plugin loaded and command is available (`macro`, `eventMacro`, `emacro`). -2. Reparse/reload macro file and verify no parse errors. -3. Use status/list commands to inspect macro/automacro states. -4. Verify trigger condition using simple known event first. -5. Incrementally add conditions until failing condition is isolated. - ---- - -## 5) Routing Problems - -### Possible causes -- Pathfinding cannot compute a valid route. -- Map data mismatch or stale map/portal table information. -- Dynamic obstacles or combat interruptions constantly reset route. -- Route task repeatedly interrupted by higher-priority tasks. - -### Files to inspect -- Routing logic: - - `core/Task/Route.pm` - - `core/Task/Move.pm` - - `core/TaskManager.pm` -- Table references: - - `tables/portals.txt` - - profile map tables (`tables/kRO/maps.txt`, `tables/iRO/maps.txt`) -- Config: - - `config/config.txt` - -### Configuration checks -- Confirm map/portal table set matches target server profile. -- Check route-related config constraints in `config.txt`. -- Verify no over-restrictive lockMap/avoid settings. - -### Debugging steps -1. Test short route on same map first. -2. Inspect route task generated path length/waypoints. -3. Check for repeated interruptions/timeouts. -4. Validate map transition assumptions (portal routes). -5. Re-test after narrowing to minimal route scenario. - ---- - -## 6) Packet Desync - -### Possible causes -- Wrong `recvpackets` profile for active server/client build. -- Server-type receive/send module mismatch. -- Packet format changed on server side. -- Corrupted/incomplete packet framing in tokenizer path. - -### Files to inspect -- Packet docs/code: - - `knowledge/networking_packets.md` - - `networking/PacketParser.pm` - - `networking/MessageTokenizer.pm` - - `networking/Receive.pm` - - `networking/Send.pm` -- Tables: - - `tables/servers.txt` - - `tables/*/recvpackets.txt` - - `tables/packetlist.txt` - - `tables/packetdescriptions.txt` - -### Configuration checks -- Verify selected server profile points to correct packet map. -- Ensure regional family tables (kRO/iRO/etc.) are aligned with server. - -### Debugging steps -1. Identify first unknown/broken packet switch in logs. -2. Match switch against packet list/description tables. -3. Confirm recvpackets version/profile used at runtime. -4. Compare affected switch handling in server-specific receive/send modules. -5. Re-validate behavior after profile/module adjustment. - ---- - -## 7) XKore Issues - -### Possible causes -- Port/listener conflicts. -- Wrong XKore mode assumptions (mode 1 bridge vs mode 2 proxy stack). -- Session/state transition mismatch between account/char/map stages. -- Client forwarding path blocked or malformed. - -### Files to inspect -- XKore docs/code: - - `knowledge/xkore_modes.md` - - `networking/XKore.pm` - - `networking/XKore2.pm` - - `networking/XKoreProxy.pm` - - `networking/Network/XKore2/{AccountServer,CharServer,MapServer}.pm` -- Config: - - `config/config.txt` - - `config/poseidon.txt` (if related environment integration is used) - -### Configuration checks -- Confirm XKore mode and listen/public IP/port values. -- Validate no duplicate process is already binding required ports. -- Ensure server-type settings are propagated correctly in XKore2 mode. - -### Debugging steps -1. Start with a single XKore mode and minimal config. -2. Verify listener startup and handshake progression. -3. Check state transitions (account -> char -> map). -4. Confirm packet forwarding in both directions. -5. If mode 2 fails, isolate failing stage server (Account/Char/Map). - ---- - -## General Triage Order (Recommended) -1. Confirm plugin/config/table profile alignment. -2. Isolate whether issue is logic-layer (AI/Task) or protocol-layer (packet/network). -3. Reproduce with minimal commands/actions. -4. Inspect module-specific path for first divergence. -5. Apply smallest fix/reconfiguration and re-test. diff --git a/openkore_llm_knowledge/knowledge/execution_flows.md b/openkore_llm_knowledge/knowledge/execution_flows.md deleted file mode 100644 index 95982978b1..0000000000 --- a/openkore_llm_knowledge/knowledge/execution_flows.md +++ /dev/null @@ -1,26 +0,0 @@ -# Execution Flows - -## AI decision loop -1. `core/functions.pl` runs the main loop and enters initialized state. -2. AI stack calls into `core/AI.pm` and `core/AI/CoreLogic.pm`. -3. Decisions enqueue/update tasks via `core/TaskManager.pm`. - -## NPC interaction flow -1. AI/commands request NPC interaction. -2. `core/Task/TalkNPC.pm` drives conversation steps. -3. Packet responses are consumed via `core/Network/Receive.pm` handlers and reflected back into task state. - -## Packet receive flow -1. Raw network data is managed through `core/Network.pm` runtime state. -2. `core/Network/PacketParser.pm` resolves packet framing and decode shape. -3. `core/Network/Receive.pm` dispatches switch-specific logic updating globals/actors/tasks. - -## Routing and movement flow -1. AI or commands schedule movement-related tasks. -2. `core/Task/Route.pm` computes route-level progression. -3. `core/Task/Move.pm` executes movement steps and reacts to packet/position updates. - -## Actor state updates -1. Incoming packets are decoded and mapped in `core/Network/Receive.pm`. -2. Actor entities from `core/Actor.pm` are created/updated. -3. Collections in `core/ActorList.pm` are synchronized and used by AI/task decisions. diff --git a/openkore_llm_knowledge/knowledge/macro_system.md b/openkore_llm_knowledge/knowledge/macro_system.md deleted file mode 100644 index e1bc339631..0000000000 --- a/openkore_llm_knowledge/knowledge/macro_system.md +++ /dev/null @@ -1,31 +0,0 @@ -# Macro System - -## `macros.txt` -The macro plugin (`plugins/macro/macro.pl`) loads a macro file from config key `macro_file`, defaulting to `macros.txt`. - -What it provides: -- macro definitions (named scripts/blocks) -- automacro triggers -- runtime command control via `macro` command - -The plugin reparses the configured macro file when `macro_file` changes and hooks runtime events according to defined automacro conditions. - -## `eventMacros.txt` -The eventMacro plugin (`plugins/eventMacro/eventMacro.pl`) loads from config key `eventMacro_file`, defaulting to `eventMacros.txt`. - -What it provides: -- event-driven macro model -- condition-based automacro activation -- runtime control via `eventMacro` / `emacro` commands - -If no event macro file is present, the plugin disables itself in startup logic. - -## Automacro behavior (conceptual) -Across both macro systems, automacros generally follow this pattern: -1. Parse macro file and build macro/automacro structures. -2. Subscribe to relevant hooks/events. -3. Evaluate conditions (state/event/regex/numeric checks). -4. Queue or execute macro actions when conditions are satisfied. -5. Respect timeout/run-once/enable-disable states and user commands. - -This makes macro automation configurable from text files rather than source edits. diff --git a/openkore_llm_knowledge/knowledge/module_dependency_map.md b/openkore_llm_knowledge/knowledge/module_dependency_map.md deleted file mode 100644 index d5d9e118fa..0000000000 --- a/openkore_llm_knowledge/knowledge/module_dependency_map.md +++ /dev/null @@ -1,122 +0,0 @@ -# Module Dependency Map (Core-Curated View) - -This dependency map is derived from `openkore_llm_knowledge/core/` and focuses on AI, Actor, Network, Commands, Task, Routing, and File Parsing relationships. - -## Major Module Relationships - -### 1) `functions.pl` -- **Purpose:** Runtime bootstrap loop and subsystem orchestration. -- **Key dependencies:** `AI`, `Commands`, `Network::Receive`, `TaskManager`, `Settings`. -- **Depended on by:** Runtime entry flow (top-level bootstrap path). - -### 2) `AI` -- **Purpose:** AI mode/state management and behavior control surface. -- **Key dependencies:** `Globals`, `Utils`, `Log`, `Field`. -- **Depended on by:** `AI::CoreLogic`, `Commands`, `Network::Receive`, task modules. - -### 3) `AI::CoreLogic` -- **Purpose:** Main decision engine for combat/behavior execution. -- **Key dependencies:** `AI`, `Globals`, `Misc`, `Network::Send`, `Commands`, `FileParsers`, `Task::TalkNPC`. -- **Depended on by:** `functions.pl` runtime loop and AI-driven task flow. - -### 4) `Actor` -- **Purpose:** Base actor model (player, mob, NPC entity behavior/state object). -- **Key dependencies:** `Globals`, `Utils`, `Task`, `Misc`, `Translation`. -- **Depended on by:** `ActorList`, `Network::Receive`, `AI`, routing/task logic. - -### 5) `ActorList` -- **Purpose:** Indexed actor container and type-aware lookup. -- **Key dependencies:** `Actor` + actor subclasses. -- **Depended on by:** `functions.pl`, `Task::TalkNPC` and other actor-consumers. - -### 6) `Commands` -- **Purpose:** Operator and automation command dispatch. -- **Key dependencies:** `Globals`, `Network`, `Network::Send`, `Settings`, `AI`, `Task`. -- **Depended on by:** `AI::CoreLogic`, `Task::TalkNPC`, packet/automation callbacks. - -### 7) `FileParsers` -- **Purpose:** Parse control/table data into runtime structures. -- **Key dependencies:** `Settings`, `Plugins`, `Utils`, `Log`. -- **Depended on by:** `Settings`, `AI::CoreLogic`, `Network::Receive`, `Commands`. - -### 8) `Settings` -- **Purpose:** Configuration path resolution, file registration, and argument parsing. -- **Key dependencies:** `Globals`, `Utils::ObjectList`, `Translation`, `Modules`. -- **Depended on by:** `functions.pl`, `Commands`, `FileParsers`, AI/network modules. - -### 9) `Globals` -- **Purpose:** Shared runtime state and exported global structures. -- **Key dependencies:** `Modules`. -- **Depended on by:** Nearly all core subsystems (`AI`, `Commands`, `Network::*`, `Task::*`). - -### 10) `Network` -- **Purpose:** Connection state abstraction and shared network constants. -- **Key dependencies:** `Modules`. -- **Depended on by:** `Commands`, `Network::PacketParser`, `Network::Receive`, `Task::*`. - -### 11) `Network::PacketParser` -- **Purpose:** Packet data parsing/reconstruction and packet utility constants. -- **Key dependencies:** `Globals`, `Network`, `Network::MessageTokenizer`, `Plugins`, `Utils`. -- **Depended on by:** `Network::Receive`, `Commands`. - -### 12) `Network::Receive` -- **Purpose:** Incoming packet dispatch and handler execution. -- **Key dependencies:** `Network::PacketParser`, `AI`, `Globals`, `FileParsers`, `Plugins`, `Skill`, `Actor::Slave::*`. -- **Depended on by:** Core runtime/loop flows and state update paths. - -### 13) `Task` -- **Purpose:** Base task abstraction (lifecycle/priority/state). -- **Key dependencies:** `Modules`, `Utils::CallbackList`, `Utils::Set`. -- **Depended on by:** `TaskManager`, task subclasses, actor/AI command flows. - -### 14) `TaskManager` -- **Purpose:** Task scheduling/execution container. -- **Key dependencies:** `Task`, `Utils::Set`, `Utils::CallbackList`. -- **Depended on by:** `functions.pl` orchestration loop and AI/task producers. - -### 15) `Task::Route` -- **Purpose:** Route planning and navigation workflow control. -- **Key dependencies:** `Task::Move`, `AI`, `Network`, `Field`, `Utils::PathFinding`. -- **Depended on by:** `AI::CoreLogic` and movement decision flow. - -### 16) `Task::Move` -- **Purpose:** Movement execution subtask and movement-state progression. -- **Key dependencies:** `Task::WithSubtask`, `Task::SitStand`, `Network`, `Plugins`, `Globals`. -- **Depended on by:** `Task::Route` and route execution chains. - -### 17) `Task::TalkNPC` -- **Purpose:** NPC conversation/state-machine interactions. -- **Key dependencies:** `Task`, `AI`, `Commands`, `Network`, `Misc`, `Plugins`. -- **Depended on by:** `AI::CoreLogic` and command-triggered NPC workflows. - -### 18) `Modules` -- **Purpose:** Core module registration/reload helper used across subsystems. -- **Key dependencies:** Perl runtime utilities (`Config`, `FindBin`, `File::Spec`). -- **Depended on by:** `Globals`, `Settings`, `Network`, `Task`, `Commands` and others. - ---- - -## Top 20 Most Important Modules (Architecture Perspective) - -1. `functions.pl` -2. `AI` -3. `AI::CoreLogic` -4. `Actor` -5. `ActorList` -6. `Task` -7. `TaskManager` -8. `Task::Route` -9. `Task::Move` -10. `Task::TalkNPC` -11. `Commands` -12. `Network` -13. `Network::PacketParser` -14. `Network::Receive` -15. `Settings` -16. `FileParsers` -17. `Globals` -18. `Modules` -19. `Network::Send` (critical dependency referenced by core decision/command paths) -20. `Misc` (high-touch utility dependency in AI/task/command/network logic) - -> Note: Items 1–18 are present in the curated `core/` set; 19–20 are explicitly identified from core dependency references to preserve architectural completeness. diff --git a/openkore_llm_knowledge/knowledge/networking_packets.md b/openkore_llm_knowledge/knowledge/networking_packets.md deleted file mode 100644 index 03573d8adb..0000000000 --- a/openkore_llm_knowledge/knowledge/networking_packets.md +++ /dev/null @@ -1,27 +0,0 @@ -# Networking & Packets - -## Packet handling architecture -OpenKore networking is structured around base transport/state modules plus packet parser/dispatcher modules: -- `networking/Network.pm` and `networking/DirectConnection.pm` for connection state and transport. -- `networking/PacketParser.pm` and `networking/MessageTokenizer.pm` for framing/tokenization and packet reconstruction support. -- `networking/Receive.pm` for incoming packet dispatch. -- `networking/Send.pm` for outgoing packet construction. - -## Receive flow (high level) -1. Bytes arrive from the active network backend. -2. Tokenizer/parser identifies packet boundaries and switch IDs. -3. `Receive.pm` maps switches to handlers. -4. Server-type subclasses (for example `Network/Receive/ServerType0.pm`, `Network/Receive/kRO.pm`, `Network/Receive/iRO.pm`) override or extend behavior. -5. Handlers update runtime state (actors, inventory, map/chat/combat events). - -## Send flow (high level) -1. Runtime logic requests an action (move, skill, talk, item ops). -2. `Send.pm` builds base packet payloads. -3. Server-type send classes (for example `Network/Send/ServerType0.pm`, `kRO.pm`, `iRO.pm`) apply server-specific packet formats. -4. Transport layer sends encoded bytes to the game server/proxy endpoint. - -## Table coupling -Packet behavior is tightly coupled to table data: -- `tables/servers.txt` selects server profile/type. -- `tables/*/recvpackets.txt` maps opcode formats by server build/family. -- `tables/packetlist.txt` and `tables/packetdescriptions.txt` assist packet reference/debug workflows. diff --git a/openkore_llm_knowledge/knowledge/openkore_faq.md b/openkore_llm_knowledge/knowledge/openkore_faq.md deleted file mode 100644 index f7ed51a53b..0000000000 --- a/openkore_llm_knowledge/knowledge/openkore_faq.md +++ /dev/null @@ -1,208 +0,0 @@ -# OpenKore Developer FAQ - -This FAQ is based on the curated OpenKore knowledge bundle and references core, plugin, networking, config, and table modules. - -## 1) How do I write a plugin? -Start with a Perl file that calls `Plugins::register`, adds hooks via `Plugins::addHooks`, and optionally registers commands via `Commands::register`. -Reference: `knowledge/code_recipes.md`, `plugins/macro/macro.pl`, `plugins/eventMacro/eventMacro.pl`. - -## 2) What is the smallest plugin skeleton? -A minimal plugin includes `package`, `use Plugins`, `Plugins::register(...)`, optional hook/command setup, and unload cleanup. -Reference: `knowledge/code_recipes.md`. - -## 3) How do I register hooks in a plugin? -Use `Plugins::addHooks` with event/callback pairs such as `start3`, `mainLoop_pre`, and `configModify`. -Reference: `knowledge/plugin_system.md`, `plugins/macro/macro.pl`. - -## 4) How do I add a custom console command? -Use `Commands::register` and provide a callback receiving command parameters. -Reference: `knowledge/code_recipes.md`, `plugins/eventMacro/eventMacro.pl`. - -## 5) How do plugins clean up on unload? -Implement unload callback to remove hooks (`Plugins::delHooks`) and unregister commands. -Reference: `plugins/macro/macro.pl`, `plugins/eventMacro/eventMacro.pl`. - -## 6) How does the AI system work at a high level? -The main loop invokes AI logic, which inspects world state and schedules tasks. -Reference: `core/functions.pl`, `core/AI.pm`, `core/AI/CoreLogic.pm`, `knowledge/system_architecture_map.md`. - -## 7) Where is combat/behavior decision logic concentrated? -In `core/AI/CoreLogic.pm`. -Reference: `knowledge/core_subsystems.md`, `knowledge/module_dependency_map.md`. - -## 8) How are actors represented? -Actor entities use a base model plus indexed actor collections. -Reference: `core/Actor.pm`, `core/ActorList.pm`, `knowledge/core_subsystems.md`. - -## 9) Where is NPC interaction implemented? -Primary flow is in `core/Task/TalkNPC.pm` with related command/receive participation. -Reference: `knowledge/execution_flows.md`, `knowledge/debugging_playbook.md`. - -## 10) How does task scheduling work? -Tasks derive from `Task` and are queued/executed by `TaskManager`. -Reference: `core/Task.pm`, `core/TaskManager.pm`, `knowledge/module_dependency_map.md`. - -## 11) Where is routing implemented? -Routing is in `core/Task/Route.pm`, and movement execution in `core/Task/Move.pm`. -Reference: `knowledge/execution_flows.md`, `knowledge/core_subsystems.md`. - -## 12) How does packet receive flow work? -Data is tokenized/parsing handled, then dispatched by receive handlers to update runtime state. -Reference: `networking/MessageTokenizer.pm`, `networking/PacketParser.pm`, `networking/Receive.pm`, `knowledge/networking_packets.md`. - -## 13) Where are outgoing packets built? -Primarily in `networking/Send.pm` and server-specific send modules. -Reference: `knowledge/networking_packets.md`, `networking/Network/Send/ServerType0.pm`. - -## 14) How do server-specific packet differences get handled? -Through receive/send server-type modules and matching `recvpackets.txt` table data. -Reference: `networking/Network/Receive/*`, `networking/Network/Send/*`, `tables/*/recvpackets.txt`. - -## 15) What causes packet desync most often? -Profile mismatch between server configuration and selected `recvpackets` mapping. -Reference: `knowledge/debugging_playbook.md`, `knowledge/table_reference.md`. - -## 16) What are XKore modes? -Mode 1 is bridge-style; mode 2 is a fuller proxy stack with account/char/map components. -Reference: `knowledge/xkore_modes.md`, `networking/XKore.pm`, `networking/XKore2.pm`. - -## 17) Where are XKore2 stage servers implemented? -In `networking/Network/XKore2/AccountServer.pm`, `CharServer.pm`, and `MapServer.pm`. -Reference: `knowledge/xkore_modes.md`. - -## 18) Where is global runtime state stored? -In `core/Globals.pm`, consumed broadly by AI/network/task/command modules. -Reference: `knowledge/module_dependency_map.md`. - -## 19) Where is configuration path/file loading controlled? -In `core/Settings.pm` and parser routines in `core/FileParsers.pm`. -Reference: `knowledge/core_subsystems.md`, `knowledge/module_dependency_map.md`. - -## 20) Which config files matter most for behavior tuning? -`config.txt`, `items_control.txt`, and `mon_control.txt`. -Reference: `knowledge/config_system.md`. - -## 21) How does `lockMap` work conceptually? -It constrains activity/navigation to a target map (optionally coordinate-focused with lock map coords). -Reference: `knowledge/code_recipes.md` route locking example, `knowledge/config_system.md`. - -## 22) Where should I tune item pickup behavior? -In `config/items_control.txt`. -Reference: `knowledge/config_system.md`, `knowledge/code_recipes.md`. - -## 23) Where should I tune monster engagement/avoidance? -In `config/mon_control.txt`. -Reference: `knowledge/config_system.md`, `knowledge/code_recipes.md`. - -## 24) Where do plugin load policies live? -In `config/sys.txt` (`loadPlugins`, include/skip lists). -Reference: `knowledge/debugging_playbook.md`, `knowledge/plugin_system.md`. - -## 25) How do I tell macro and eventMacro apart? -Both are automation systems, but eventMacro emphasizes event/condition-driven structures with distinct parser/runner modules. -Reference: `knowledge/macro_system.md`, `plugins/macro/*`, `plugins/eventMacro/*`. - -## 26) Where are macro commands implemented? -In `plugins/macro/macro.pl` (`macro` command) and `plugins/eventMacro/eventMacro.pl` (`eventMacro`, `emacro`). -Reference: `knowledge/plugin_system.md`. - -## 27) Why would macros not trigger? -Common causes: plugin not loaded, wrong macro file path, false conditions, or disabled automacro. -Reference: `knowledge/debugging_playbook.md`. - -## 28) How do I set emergency teleport automation? -Use macro/eventMacro conditions on HP threshold and teleport action commands. -Reference: `knowledge/code_recipes.md`. - -## 29) How can I create a farming loop quickly? -Define a recursive macro with movement and `ai auto` pauses, gated by an automacro trigger. -Reference: `knowledge/code_recipes.md`. - -## 30) How do I debug “bot not moving”? -Inspect AI/task state and movement packet emission paths, then validate route/map constraints. -Reference: `knowledge/debugging_playbook.md`. - -## 31) How do I debug NPC interaction failures? -Trace `Task/TalkNPC` progression and corresponding receive handler updates. -Reference: `knowledge/debugging_playbook.md`, `knowledge/execution_flows.md`. - -## 32) How do I debug plugin load failures? -Check `sys.txt` plugin load mode/lists and plugin startup exceptions. -Reference: `knowledge/debugging_playbook.md`. - -## 33) How do I debug routing failures? -Validate route generation, movement progression, and map/portal tables. -Reference: `knowledge/debugging_playbook.md`, `tables/portals.txt`. - -## 34) What are the key packet reference tables? -`tables/packetlist.txt`, `tables/packetdescriptions.txt`, and server profile `recvpackets.txt` files. -Reference: `knowledge/table_reference.md`. - -## 35) What does `tables/servers.txt` do? -Defines server profiles and serverType anchors used by network/packet handling. -Reference: `knowledge/table_reference.md`, `knowledge/networking_packets.md`. - -## 36) Why include both kRO and iRO tables in curation? -They provide representative regional profiles for packet/content differences. -Reference: `knowledge/table_reference.md`. - -## 37) What is the role of `core/functions.pl`? -It drives startup stages and main loop transitions. -Reference: `knowledge/core_module_index.md`, `knowledge/module_dependency_map.md`. - -## 38) Why is `Commands.pm` so central? -It is a common control surface for operators, plugins, and automation. -Reference: `knowledge/module_dependency_map.md`. - -## 39) Why is `Network::Receive` so important? -Incoming packets drive most runtime state changes (actors, events, dialogs, combat updates). -Reference: `knowledge/networking_packets.md`, `knowledge/module_dependency_map.md`. - -## 40) How do plugins interact with runtime without core edits? -By hooking lifecycle/events and registering commands. -Reference: `knowledge/plugin_system.md`. - -## 41) Where should I start when extending behavior safely? -Start with a small plugin command + one lightweight hook; expand incrementally. -Reference: `knowledge/code_recipes.md`. - -## 42) How do I avoid plugin performance issues? -Keep `mainLoop_pre` callbacks lightweight and avoid heavy repeated processing. -Reference: `knowledge/code_recipes.md` notes. - -## 43) How do I map a runtime problem to the right subsystem quickly? -Use the system map: AI/Task for decisions/actions, Network/Packet for protocol/state updates, Config/Plugin for policy/extension. -Reference: `knowledge/system_architecture_map.md`. - -## 44) Where is the best overview of subsystem interactions? -`knowledge/system_architecture_map.md` and `knowledge/execution_flows.md`. - -## 45) What is the recommended debugging sequence? -Confirm profile alignment, isolate logic vs protocol layer, reproduce minimally, trace first divergence, re-test. -Reference: `knowledge/debugging_playbook.md`. - -## 46) How do I inspect module dependencies quickly? -Use `knowledge/module_dependency_map.md` for purpose + dependency + reverse-dependency summaries. - -## 47) Which modules are “top priority” for understanding architecture? -Start with `functions.pl`, AI, Actor, Task, Commands, Network, Receive/PacketParser, Settings/FileParsers, and Globals. -Reference: `knowledge/module_dependency_map.md` Top 20 section. - -## 48) How do I create map-entry automation? -Use eventMacro conditions like `InMap` + `mapLoaded` and issue config/AI commands. -Reference: `knowledge/code_recipes.md`. - -## 49) How do I create monster-proximity reactions? -Use eventMacro presence/distance conditions and trigger defensive actions (e.g., teleport). -Reference: `knowledge/code_recipes.md`. - -## 50) Where can I find practical examples across plugin/macro/config work? -`knowledge/code_recipes.md` contains concise snippets for all three domains. - -## 51) What if a change seems correct but behavior is still wrong? -Re-check server profile + packet table alignment first, then verify module override family (ServerType/kRO/iRO). -Reference: `knowledge/debugging_playbook.md`, `knowledge/networking_packets.md`. - -## 52) How should I keep this knowledge bundle maintainable? -Update high-volatility areas first: networking modules, packet tables, and macro/plugin docs tied to runtime hooks. -Reference: prioritize updates to `knowledge/networking_packets.md`, `knowledge/table_reference.md`, and `knowledge/plugin_system.md` when runtime behavior changes. diff --git a/openkore_llm_knowledge/knowledge/plugin_system.md b/openkore_llm_knowledge/knowledge/plugin_system.md deleted file mode 100644 index 14d0fdda87..0000000000 --- a/openkore_llm_knowledge/knowledge/plugin_system.md +++ /dev/null @@ -1,41 +0,0 @@ -# Plugin System - -## Architecture (as seen in representative plugins) -OpenKore plugins are Perl packages under `plugins/` with an entrypoint `.pl` file. A plugin typically: -1. imports core APIs (`Plugins`, `Commands`, `Settings`, `Globals`), -2. registers itself, -3. subscribes to runtime hooks, -4. optionally registers console commands, -5. cleans up on unload. - -Representative curated files: -- `plugins/macro/macro.pl` -- `plugins/eventMacro/eventMacro.pl` -- `plugins/reconnect/reconnect.pl` -- `plugins/profiles/profiles.pl` -- `plugins/map/map.pl` - -## Hook system -Representative plugins use hook APIs such as: -- `Plugins::addHooks(...)` for multiple hook subscriptions. -- Event points like `start3`, `mainLoop_pre`, and `configModify`. -- Hook callbacks to react to runtime state or packets. - -In the macro/eventMacro plugins, hooks are used to load macro configs at startup, react to config changes, and run checks/actions each loop cycle. - -## Plugin lifecycle -Common lifecycle pattern: -- `Plugins::register(name, description, unload_cb[, reload_cb])` -- startup callback(s) load plugin state and files -- runtime callbacks handle logic -- unload callback removes hooks/commands and clears state - -The macro plugin includes both unload and reload behavior; eventMacro includes explicit unload cleanup and file reparse logic. - -## Command registration -Plugins can expose user commands via `Commands::register`. -Examples in this bundle: -- macro plugin: `macro ...` -- eventMacro plugin: `eventMacro ...` and `emacro ...` - -These command handlers provide runtime control (status, list, enable/disable, variable ops, etc.) without modifying core code. diff --git a/openkore_llm_knowledge/knowledge/system_architecture_map.md b/openkore_llm_knowledge/knowledge/system_architecture_map.md deleted file mode 100644 index 714ea4554f..0000000000 --- a/openkore_llm_knowledge/knowledge/system_architecture_map.md +++ /dev/null @@ -1,249 +0,0 @@ -# OpenKore System Architecture Map - -This map describes how the curated OpenKore knowledge bundle components fit together at runtime. - -## 1) AI Subsystem - -### Purpose -Coordinate autonomous behavior: combat decisions, state transitions, and action triggering. - -### Main modules -- `core/AI.pm` -- `core/AI/CoreLogic.pm` -- (supporting interaction surfaces: `core/Commands.pm`, `core/TaskManager.pm`) - -### Interactions -- Consumes actor/world state from the Actor subsystem. -- Schedules and updates executable work through the Task subsystem. -- Reacts to network-derived state updates (packet handlers). -- Can be influenced by Plugins/Macros through hook and command surfaces. - -### Important entry points -- AI cycle invoked from the main loop (`core/functions.pl`). -- Decision logic concentrated in `core/AI/CoreLogic.pm`. - ---- - -## 2) Actor System - -### Purpose -Represent dynamic world entities (player, monsters, NPCs, other actors) and maintain indexed state. - -### Main modules -- `core/Actor.pm` -- `core/ActorList.pm` - -### Interactions -- Updated primarily by packet receive handlers. -- Queried by AI and Tasks for targeting, proximity, and world context. -- Exposed indirectly to plugins/macros through runtime globals and events. - -### Important entry points -- Actor creation/update from packet decode paths. -- Actor lookup/list operations through `core/ActorList.pm`. - ---- - -## 3) Task System - -### Purpose -Provide executable units for actions and multi-step workflows. - -### Main modules -- `core/Task.pm` -- `core/TaskManager.pm` -- `core/Task/Route.pm` -- `core/Task/Move.pm` -- `core/Task/TalkNPC.pm` - -### Interactions -- Receives intent from AI, commands, and automation systems. -- Uses Network subsystem for action-side packet emission. -- Depends on Actor/Map state updates from receive flow to advance steps. - -### Important entry points -- Task queueing/scheduling in `core/TaskManager.pm`. -- Specialized flow handlers in route/move/talk task modules. - ---- - -## 4) Network Subsystem - -### Purpose -Manage connection state, transport lifecycle, and bridging/proxy modes. - -### Main modules -- `networking/Network.pm` -- `networking/DirectConnection.pm` -- `networking/XKore.pm` -- `networking/XKore2.pm` -- `networking/XKoreProxy.pm` -- `networking/Network/XKore2/{AccountServer,CharServer,MapServer}.pm` - -### Interactions -- Feeds decoded data into Packet Handling. -- Receives outgoing payload requests from Tasks, Commands, and Plugins. -- Selects mode-dependent behavior (direct, bridge, proxy/XKore2). - -### Important entry points -- Runtime connection state transitions. -- XKore mode startup/iteration paths. - ---- - -## 5) Packet Handling Subsystem - -### Purpose -Decode, map, dispatch, and construct protocol packets across server variants. - -### Main modules -- `networking/PacketParser.pm` -- `networking/MessageTokenizer.pm` -- `networking/Receive.pm` -- `networking/Send.pm` -- Server-specific variants under: - - `networking/Network/Receive/*` - - `networking/Network/Send/*` - -### Interactions -- Input side updates actor/runtime state and triggers higher-level events. -- Output side encodes requested actions for transport. -- Coupled to table definitions (servers + recvpackets mapping). - -### Important entry points -- Receive dispatch in `networking/Receive.pm`. -- Send construction in `networking/Send.pm`. -- Server-type overrides in `ServerType0`, `kRO`, `iRO`, etc. - ---- - -## 6) Routing and Movement Subsystem - -### Purpose -Convert navigation intent into route computation and movement execution. - -### Main modules -- `core/Task/Route.pm` -- `core/Task/Move.pm` -- (coordination via `core/TaskManager.pm` and AI core) - -### Interactions -- Triggered by AI decisions or direct command/plugin logic. -- Uses actor/map state from packet updates. -- Emits move-related packets through send pipeline. - -### Important entry points -- Route planning task instantiation. -- Movement step processing and re-evaluation. - ---- - -## 7) Plugin System - -### Purpose -Extend runtime behavior without changing core source. - -### Main modules -- Representative plugin entries in curated bundle: - - `plugins/macro/macro.pl` - - `plugins/eventMacro/eventMacro.pl` - - `plugins/reconnect/reconnect.pl` - - `plugins/profiles/profiles.pl` - - `plugins/map/map.pl` - -### Interactions -- Registers hooks into startup/main loop/config/packet related events. -- Registers commands to expose operational controls. -- Can influence AI/task execution by issuing commands or scheduling logic. - -### Important entry points -- `Plugins::register(...)` -- `Plugins::addHooks(...)` -- `Commands::register(...)` -- Plugin unload/reload callbacks. - ---- - -## 8) Config System - -### Purpose -Provide operator-controlled behavior policies without code edits. - -### Main modules / files -- `config/config.txt` (global behavior) -- `config/items_control.txt` (item policy) -- `config/mon_control.txt` (monster behavior policy) -- Additional control files in `config/` for specialized behavior domains. - -### Interactions -- Loaded/parsed by settings + file parser paths. -- Consumed by AI, task logic, plugins, and packet behavior decisions. -- Changes can trigger plugin/config hooks in runtime. - -### Important entry points -- Initial load during startup sequence. -- Config modification events watched by plugins (e.g., macro/eventMacro). - ---- - -## 9) Macro System - -### Purpose -Offer text-configured automation layers for reactive behavior. - -### Main modules -- Macro stack: - - `plugins/macro/macro.pl` - - `plugins/macro/Macro/*` -- EventMacro stack: - - `plugins/eventMacro/eventMacro.pl` - - `plugins/eventMacro/eventMacro/*` - -### Interactions -- Subscribes to hooks/events and loop phases. -- Evaluates automacro conditions against runtime state and events. -- Triggers commands/actions that flow into AI/tasks/network send paths. - -### Important entry points -- Macro file load keys (`macro_file`, `eventMacro_file`). -- Commands: `macro`, `eventMacro`, `emacro`. - ---- - -## System Interaction Overview - -At runtime, the main loop initializes modules, loads config/table data, and enters iterative execution. Incoming network data is tokenized and parsed, then dispatched by receive handlers that refresh actor/world state. AI logic evaluates this state and schedules tasks. Tasks execute concrete workflows (move, route, NPC interactions), producing outgoing actions encoded by send logic and delivered through the active network mode. - -Plugins hook into this lifecycle at well-defined points (startup, loop, config updates, packet-related events), extending behavior and command surfaces. Macro/eventMacro layers run on top of plugin hooks, evaluating declarative automacro rules and injecting actions back into command/task pipelines. - -### Example runtime flows - -#### A) AI loop -1. Main loop tick runs. -2. AI evaluates current actor/state context. -3. AI enqueues/updates tasks. -4. Tasks emit action requests -> send packets. - -#### B) NPC interaction -1. AI/command/plugin requests NPC action. -2. `Task/TalkNPC` drives dialogue steps. -3. Receive handlers process NPC responses. -4. Task advances/completes based on packet-updated state. - -#### C) Packet receive flow -1. Raw bytes arrive via network backend. -2. Tokenizer/parser resolves packet boundaries/opcodes. -3. Receive dispatch maps to handler. -4. Handler mutates globals/actors/tasks and may trigger hooks. - -#### D) Plugin hook execution -1. Plugin registers hooks and commands at startup. -2. Runtime events invoke plugin callbacks. -3. Plugin callback performs logic (state check, action trigger, config reload). -4. Optional commands expose runtime control to operator. - -#### E) Routing decision flow -1. AI decides to navigate to target/map/coordinate. -2. Route task computes route segments. -3. Move task executes stepwise movement. -4. Receive updates position/context; route logic re-evaluates until arrival/failure. diff --git a/openkore_llm_knowledge/knowledge/table_reference.md b/openkore_llm_knowledge/knowledge/table_reference.md deleted file mode 100644 index 1b9b90f556..0000000000 --- a/openkore_llm_knowledge/knowledge/table_reference.md +++ /dev/null @@ -1,24 +0,0 @@ -# Table Reference - -This curated table subset focuses on gameplay lookups and packet/network references. - -## Core network/profile tables -- `tables/servers.txt` — master server profile definitions and serverType anchors. -- `tables/packetlist.txt` — packet index/reference list. -- `tables/packetdescriptions.txt` — textual packet purpose descriptions. -- `tables/kRO/recvpackets.txt`, `tables/iRO/recvpackets.txt` — server-family packet structure maps. - -## Gameplay identity/reference tables -- `tables/statusnametable.txt` — status effect name mapping. -- `tables/SKILL_id_handle.txt` — skill ID ↔ handle mapping. -- `tables/STATUS_id_handle.txt` — status ID ↔ handle mapping. -- `tables/itemtypes.txt` — item type categories. -- `tables/elements.txt` — elemental mapping. - -## Gameplay world/content examples -- `tables/portals.txt` — portal transitions. -- `tables/skillsarea.txt` — area-skill metadata. -- `tables/msgstringtable.txt` — message string references. -- Profile snapshots included for two major families: - - `tables/kRO/{maps.txt,items.txt,skillnametable.txt}` - - `tables/iRO/{maps.txt,items.txt,skillnametable.txt}` diff --git a/openkore_llm_knowledge/knowledge/xkore_modes.md b/openkore_llm_knowledge/knowledge/xkore_modes.md deleted file mode 100644 index 7665b00559..0000000000 --- a/openkore_llm_knowledge/knowledge/xkore_modes.md +++ /dev/null @@ -1,21 +0,0 @@ -# XKore Modes - -## XKore mode 1 (`networking/XKore.pm`) -- Runs a local bridge server and communicates between OpenKore and an RO client. -- Maintains client/server packet channels and forwards transformed packets. -- Useful for hybrid play/automation workflows where a game client remains in the loop. - -## XKore mode 2 (`networking/XKore2.pm` + `networking/Network/XKore2/*`) -- Implements proxy-style account/char/map server components. -- Uses dedicated XKore2 server classes: - - `AccountServer.pm` - - `CharServer.pm` - - `MapServer.pm` -- Integrates with hooks to manage packet mangling and in-game synchronization. - -## XKore proxy support (`networking/XKoreProxy.pm`) -- Additional proxy integration layer used in XKore-style setups. - -## Practical difference summary -- **Mode 1**: direct local bridge style, simpler path for client relay. -- **Mode 2**: fuller proxy stack with separate protocol-stage servers and session handling. From 5929b6fa0a1f095d851bc90172fc02b81698f060 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Bosc=C3=A1?= <juan_bosca@live.com> Date: Tue, 10 Mar 2026 20:28:13 -0300 Subject: [PATCH 17/18] Create GPT upload final merged knowledge bundle --- gpt_upload_final/architecture.md | 237 ++++++++++++ gpt_upload_final/dataset_summary.md | 38 ++ gpt_upload_final/debugging.md | 354 ++++++++++++++++++ gpt_upload_final/development.md | 250 +++++++++++++ gpt_upload_final/faq.md | 148 ++++++++ gpt_upload_final/glossary.md | 28 ++ gpt_upload_final/plugin_config_system.md | 98 +++++ gpt_upload_final/system_flows.md | 182 +++++++++ .../knowledge/01_llm_context.md | 21 ++ .../knowledge/02_architecture_overview.md | 23 ++ .../knowledge/03_system_architecture_map.md | 43 +++ .../knowledge/04_core_subsystems.md | 49 +++ .../knowledge/05_module_dependency_map.md | 29 ++ .../knowledge/06_execution_flows.md | 29 ++ .../knowledge/07_npc_interaction_flow.md | 26 ++ .../knowledge/08_routing_and_movement.md | 20 + .../knowledge/09_networking_packets.md | 22 ++ .../knowledge/10_xkore_modes.md | 20 + .../knowledge/11_plugin_system.md | 28 ++ .../knowledge/12_config_system.md | 19 + .../knowledge/13_macro_system.md | 19 + .../knowledge/14_table_reference.md | 25 ++ .../knowledge/15_debugging_playbook.md | 185 +++++++++ .../knowledge/16_debug_decision_trees.md | 90 +++++ .../knowledge/17_code_recipes.md | 147 ++++++++ .../knowledge/18_openkore_faq.md | 147 ++++++++ .../knowledge/19_glossary.md | 27 ++ .../knowledge/20_code_index.md | 46 +++ .../knowledge/21_core_module_index.md | 13 + .../knowledge/22_common_pitfalls.md | 49 +++ .../knowledge/23_debugging_guide.md | 23 ++ .../knowledge/24_learning_path.md | 49 +++ .../knowledge/26_architecture_diagrams.md | 17 + .../knowledge/27_system_flows.md | 35 ++ .../knowledge/29_prompt_examples_for_users.md | 49 +++ .../knowledge/30_dataset_summary.md | 37 ++ .../knowledge/architecture_overview.md | 15 - .../knowledge/code_recipes.md | 196 ---------- .../knowledge/config_system.md | 27 -- .../knowledge/core_module_index.md | 20 - .../knowledge/core_subsystems.md | 27 -- .../knowledge/debugging_guide.md | 27 -- .../knowledge/debugging_playbook.md | 244 ------------ .../knowledge/execution_flows.md | 26 -- .../knowledge/macro_system.md | 31 -- .../knowledge/module_dependency_map.md | 122 ------ .../knowledge/networking_packets.md | 27 -- .../knowledge/openkore_faq.md | 208 ---------- .../knowledge/plugin_system.md | 41 -- .../knowledge/system_architecture_map.md | 249 ------------ .../knowledge/table_reference.md | 24 -- .../knowledge/xkore_modes.md | 21 -- 52 files changed, 2622 insertions(+), 1305 deletions(-) create mode 100644 gpt_upload_final/architecture.md create mode 100644 gpt_upload_final/dataset_summary.md create mode 100644 gpt_upload_final/debugging.md create mode 100644 gpt_upload_final/development.md create mode 100644 gpt_upload_final/faq.md create mode 100644 gpt_upload_final/glossary.md create mode 100644 gpt_upload_final/plugin_config_system.md create mode 100644 gpt_upload_final/system_flows.md create mode 100644 openkore_llm_knowledge/knowledge/01_llm_context.md create mode 100644 openkore_llm_knowledge/knowledge/02_architecture_overview.md create mode 100644 openkore_llm_knowledge/knowledge/03_system_architecture_map.md create mode 100644 openkore_llm_knowledge/knowledge/04_core_subsystems.md create mode 100644 openkore_llm_knowledge/knowledge/05_module_dependency_map.md create mode 100644 openkore_llm_knowledge/knowledge/06_execution_flows.md create mode 100644 openkore_llm_knowledge/knowledge/07_npc_interaction_flow.md create mode 100644 openkore_llm_knowledge/knowledge/08_routing_and_movement.md create mode 100644 openkore_llm_knowledge/knowledge/09_networking_packets.md create mode 100644 openkore_llm_knowledge/knowledge/10_xkore_modes.md create mode 100644 openkore_llm_knowledge/knowledge/11_plugin_system.md create mode 100644 openkore_llm_knowledge/knowledge/12_config_system.md create mode 100644 openkore_llm_knowledge/knowledge/13_macro_system.md create mode 100644 openkore_llm_knowledge/knowledge/14_table_reference.md create mode 100644 openkore_llm_knowledge/knowledge/15_debugging_playbook.md create mode 100644 openkore_llm_knowledge/knowledge/16_debug_decision_trees.md create mode 100644 openkore_llm_knowledge/knowledge/17_code_recipes.md create mode 100644 openkore_llm_knowledge/knowledge/18_openkore_faq.md create mode 100644 openkore_llm_knowledge/knowledge/19_glossary.md create mode 100644 openkore_llm_knowledge/knowledge/20_code_index.md create mode 100644 openkore_llm_knowledge/knowledge/21_core_module_index.md create mode 100644 openkore_llm_knowledge/knowledge/22_common_pitfalls.md create mode 100644 openkore_llm_knowledge/knowledge/23_debugging_guide.md create mode 100644 openkore_llm_knowledge/knowledge/24_learning_path.md create mode 100644 openkore_llm_knowledge/knowledge/26_architecture_diagrams.md create mode 100644 openkore_llm_knowledge/knowledge/27_system_flows.md create mode 100644 openkore_llm_knowledge/knowledge/29_prompt_examples_for_users.md create mode 100644 openkore_llm_knowledge/knowledge/30_dataset_summary.md delete mode 100644 openkore_llm_knowledge/knowledge/architecture_overview.md delete mode 100644 openkore_llm_knowledge/knowledge/code_recipes.md delete mode 100644 openkore_llm_knowledge/knowledge/config_system.md delete mode 100644 openkore_llm_knowledge/knowledge/core_module_index.md delete mode 100644 openkore_llm_knowledge/knowledge/core_subsystems.md delete mode 100644 openkore_llm_knowledge/knowledge/debugging_guide.md delete mode 100644 openkore_llm_knowledge/knowledge/debugging_playbook.md delete mode 100644 openkore_llm_knowledge/knowledge/execution_flows.md delete mode 100644 openkore_llm_knowledge/knowledge/macro_system.md delete mode 100644 openkore_llm_knowledge/knowledge/module_dependency_map.md delete mode 100644 openkore_llm_knowledge/knowledge/networking_packets.md delete mode 100644 openkore_llm_knowledge/knowledge/openkore_faq.md delete mode 100644 openkore_llm_knowledge/knowledge/plugin_system.md delete mode 100644 openkore_llm_knowledge/knowledge/system_architecture_map.md delete mode 100644 openkore_llm_knowledge/knowledge/table_reference.md delete mode 100644 openkore_llm_knowledge/knowledge/xkore_modes.md diff --git a/gpt_upload_final/architecture.md b/gpt_upload_final/architecture.md new file mode 100644 index 0000000000..af7f91267c --- /dev/null +++ b/gpt_upload_final/architecture.md @@ -0,0 +1,237 @@ +# OpenKore LLM Context +--- + +## Objective +This layer gives an LLM enough structural context to reason about OpenKore runtime behavior, module boundaries, and extension points. + +## Runtime model (high level) +1. `src/functions.pl` boots runtime services, loads config/tables, initializes network mode, and drives the main loop. +2. Network receive handlers (`src/Network/Receive*.pm`) transform packets into world-state updates. +3. Actor state (`src/Actor.pm`, `src/ActorList.pm`) becomes the shared world model for decision logic. +4. AI (`src/AI.pm`, `src/AI/CoreLogic.pm`) evaluates state and schedules actions through tasks. +5. Task execution (`src/TaskManager.pm`, `src/Task/*.pm`) performs multi-step actions and emits packets through send modules (`src/Network/Send*.pm`). +6. Plugins (`src/Plugins.pm`, `plugins/*`) and macro layers (`plugins/macro`, `plugins/eventMacro`) inject automation and custom behavior. + +## Key architecture anchors +- **Entry/control loop**: `src/functions.pl` +- **Global state/config loading**: `src/Globals.pm`, `src/Settings.pm`, `src/FileParsers.pm`, `control/` +- **AI + behavior**: `src/AI.pm`, `src/AI/CoreLogic.pm` +- **Actor model**: `src/Actor.pm`, `src/ActorList.pm`, `src/Actor/*` +- **Task orchestration**: `src/Task.pm`, `src/TaskManager.pm`, `src/Task/*` +- **Networking and packets**: `src/Network.pm`, `src/Network/*` +- **Plugin/macro automation**: `src/Plugins.pm`, `plugins/macro/*`, `plugins/eventMacro/*` + +# OpenKore Architecture Overview +--- + +## Top-level structure +- `src/`: core runtime modules (AI, actor model, networking, tasks, plugins API, command layer). +- `control/`: operator configuration and behavior policies (`config.txt`, control lists, route/macro inputs). +- `tables/`: protocol/game data mappings (packet maps, item/skill/map metadata, server-specific variants). +- `plugins/`: optional extensions, including macro/eventMacro automation stacks. +- `fields/`: map field data used by routing/navigation. + +## Core runtime layers +1. **Bootstrap + loop**: `src/functions.pl` and `src/Modules.pm` +2. **State + config**: `src/Globals.pm`, `src/Settings.pm`, `src/FileParsers.pm` +3. **Network transport + packet translation**: `src/Network.pm`, `src/Network/{Receive,Send,PacketParser}.pm` +4. **World model**: `src/Actor.pm`, `src/ActorList.pm`, `src/Field.pm` +5. **Decision engine**: `src/AI.pm`, `src/AI/CoreLogic.pm` +6. **Execution engine**: `src/TaskManager.pm`, `src/Task/*.pm` +7. **Extension surface**: `src/Plugins.pm`, `src/Commands.pm`, `plugins/*` + +## Architectural characteristics +- **Event-driven and loop-based**: runtime state is advanced each tick by receive updates + AI/task progression. +- **Protocol-adapter design**: packet send/receive classes are split by server type under `src/Network/Receive/*` and `src/Network/Send/*`. +- **Policy outside code**: behavior is heavily configured via `control/` and `tables/` files. +- **Extension-first automation**: macro/eventMacro run as plugins and reuse command/hook/task pathways. + +# System Architecture Map (Textual) +--- + +## Primary flow +`functions.pl main loop` -> `Network receive` -> `Actor/Globals update` -> `AI decision` -> `Task execution` -> `Network send` + +## Subsystem map + +### 1) Networking +- Connection/mode handling: `src/Network.pm`, `src/Network/DirectConnection.pm`, `src/Network/XKore*.pm` +- Packet ingestion and dispatch: `src/Network/MessageTokenizer.pm`, `src/Network/PacketParser.pm`, `src/Network/Receive.pm`, `src/Network/Receive/*` +- Packet construction: `src/Network/Send.pm`, `src/Network/Send/*` + +### 2) Actor system +- Base entity model: `src/Actor.pm`, `src/Actor/*` +- Actor collections and lookup: `src/ActorList.pm` +- World/map coupling: `src/Field.pm`, map and position state in globals + +### 3) AI subsystem +- AI stack state and sequencing: `src/AI.pm` +- Core behavior logic: `src/AI/CoreLogic.pm` +- Uses actor/world/config state; issues actions via task manager and commands + +### 4) Task subsystem +- Task abstraction: `src/Task.pm` +- Scheduler and lifecycle: `src/TaskManager.pm` +- High-impact tasks: `src/Task/Route.pm`, `src/Task/Move.pm`, `src/Task/TalkNPC.pm`, `src/Task/UseSkill.pm` + +### 5) Configuration subsystem +- Config discovery/loading: `src/Settings.pm`, `src/FileParsers.pm` +- Runtime shared state: `src/Globals.pm` +- Policy sources: `control/*`, `tables/*` + +### 6) Plugin + macro subsystem +- Hook and plugin lifecycle: `src/Plugins.pm` +- Command bridge: `src/Commands.pm` +- Macro engines: `plugins/macro/*`, `plugins/eventMacro/*` + +## Key entry points +- Startup/main loop: `src/functions.pl` +- Command dispatch: `src/Commands.pm` +- Hook registration: `src/Plugins.pm` +- AI tick path: `src/AI.pm` -> `src/AI/CoreLogic.pm` +- Packet receive path: `src/Network/Receive.pm` + server-specific receivers + +# Core Subsystems +--- + +## AI +- Main modules: `src/AI.pm`, `src/AI/CoreLogic.pm` +- Role: maintain AI queues/state machines, evaluate combat/loot/movement priorities, enqueue tasks. +- Inputs: actor state, config policies, packet-driven updates. +- Outputs: task requests, command invocations, action intents. + +## Actor system +- Main modules: `src/Actor.pm`, `src/ActorList.pm`, `src/Actor/*` +- Role: represent player/NPC/monster/portal entities, with indexed lookup for targeting and proximity logic. +- Inputs: receive handlers and map updates. +- Outputs: query surface for AI, tasks, commands, and plugins. + +## Networking +- Main modules: `src/Network.pm`, `src/Network/{Receive,Send,PacketParser,MessageTokenizer}.pm`, `src/Network/XKore*.pm` +- Role: manage transport mode, parse inbound packets, encode outbound actions, support server-specific packet families. +- Inputs: socket data + outgoing action intents. +- Outputs: state mutations via receive handlers and serialized packets to servers/clients. + +## Task system +- Main modules: `src/Task.pm`, `src/TaskManager.pm`, `src/Task/*` +- Role: run asynchronous multi-step actions (route, move, NPC dialogs, skill/item use). +- Inputs: AI/command/plugin action requests. +- Outputs: packet sends, follow-up tasks, completion/failure states consumed by AI. + +## Plugin system +- Main modules: `src/Plugins.pm`, `plugins/*` +- Role: dynamic extension lifecycle (`register`, hooks, unload), runtime behavior injection. +- Inputs: startup/load events, packet-related hooks, periodic loop hooks. +- Outputs: custom commands, state transitions, automation triggers. + +## Configuration system +- Main modules: `src/Settings.pm`, `src/FileParsers.pm`, `control/*`, `tables/*` +- Role: load policy and data files that parameterize AI, networking, and gameplay handling. +- Inputs: text configs and table files. +- Outputs: normalized runtime config/state in globals and subsystem-specific structures. + +## Macro system +- Main modules: `plugins/macro/macro.pl`, `plugins/macro/Macro/*`, `plugins/eventMacro/eventMacro.pl`, `plugins/eventMacro/eventMacro/*` +- Role: declarative automation over hooks and game events, with conditional triggers and scripted actions. +- Inputs: macro definitions and live runtime events. +- Outputs: command execution and indirect task/network activity. + +## Routing +- Main modules: `src/Task/Route.pm`, `src/Task/Move.pm`, `src/Field.pm`, `fields/*` +- Role: compute and execute navigation paths across map cells/portals. +- Inputs: destination intents, field data, actor position/state. +- Outputs: stepwise movement actions and route completion/fallback states. + +# Module Dependency Map +--- + +## Dependency chains (high impact) +1. `src/functions.pl` -> `src/Settings.pm` / `src/FileParsers.pm` / `src/Modules.pm` (bootstrap) +2. `src/functions.pl` -> `src/Network.pm` + `src/Network/*` (connection and packet loop) +3. `src/Network/Receive*.pm` -> `src/Globals.pm` + `src/Actor*.pm` (state mutation) +4. `src/AI.pm` -> `src/AI/CoreLogic.pm` -> `src/TaskManager.pm` + `src/Task/*` (decision to execution) +5. `src/Task/*` -> `src/Network/Send*.pm` (action serialization) +6. `src/Plugins.pm` + `plugins/*` -> `src/Commands.pm` / AI / TaskManager (extension control paths) + +## Subsystem dependency view +- **AI depends on**: Actor state, config policies, task scheduler, command surface. +- **Actor system depends on**: Network receive updates and global runtime registries. +- **Task system depends on**: AI/commands/plugins for intents; network send + actor/map updates for progression. +- **Plugin/macro depends on**: hook lifecycle (`src/Plugins.pm`), command execution (`src/Commands.pm`), global state. +- **Routing depends on**: task framework, field data (`fields/*`), movement packet sends. + +## Coupling hotspots +- `src/Globals.pm`: shared mutable state touched by receive, AI, and plugins. +- `src/Commands.pm`: cross-cutting control surface used by user input and automation. +- `src/TaskManager.pm`: convergence point for AI and scripted automation. +- `src/Network/Receive.pm`: ingress bridge from protocol events to internal state transitions. + +## Practical navigation order for analysis +1. Start at `src/functions.pl`. +2. Follow receive path (`src/Network/Receive.pm`, subtype receivers). +3. Inspect AI loop (`src/AI.pm`, `src/AI/CoreLogic.pm`). +4. Inspect task execution (`src/TaskManager.pm`, route/move/NPC tasks). +5. Inspect extension points (`src/Plugins.pm`, macro/eventMacro plugins). + +# Code Index (Architecture-Oriented) +--- + +## Runtime entry and orchestration +- `src/functions.pl` — startup sequence and main loop +- `src/Modules.pm` — module loading registry +- `src/Globals.pm` — shared runtime state container + +## AI +- `src/AI.pm` — AI framework and queue/state management +- `src/AI/CoreLogic.pm` — core decision routines + +## Actor and world model +- `src/Actor.pm` — actor base model +- `src/ActorList.pm` — actor collections/indexing +- `src/Actor/*` — actor specializations +- `src/Field.pm` — map/field representation + +## Task execution +- `src/Task.pm` — task abstraction +- `src/TaskManager.pm` — scheduler/executor +- `src/Task/Route.pm` — route planning/execution wrapper +- `src/Task/Move.pm` — movement task +- `src/Task/TalkNPC.pm` — NPC dialogue workflow +- `src/Task/UseSkill.pm` — skill-use workflows + +## Networking and packets +- `src/Network.pm` — network facade and state +- `src/Network/DirectConnection.pm` — direct server mode +- `src/Network/XKore.pm`, `src/Network/XKore2.pm`, `src/Network/XKoreProxy.pm` — proxy/bridge modes +- `src/Network/MessageTokenizer.pm` — packet frame/token extraction +- `src/Network/PacketParser.pm` — packet definition parsing +- `src/Network/Receive.pm` + `src/Network/Receive/*` — inbound packet dispatch/handlers +- `src/Network/Send.pm` + `src/Network/Send/*` — outbound packet builders + +## Commands, plugins, and automation +- `src/Commands.pm` — command parser/dispatcher +- `src/Plugins.pm` — plugin lifecycle and hook API +- `plugins/macro/macro.pl` + `plugins/macro/Macro/*` — legacy macro automation +- `plugins/eventMacro/eventMacro.pl` + `plugins/eventMacro/eventMacro/*` — event-driven macro automation + +## Configuration and data sources +- `src/Settings.pm` — config path and file registration +- `src/FileParsers.pm` — config/table parsing +- `control/*` — user behavior policies and runtime options +- `tables/*` — server/data tables +- `fields/*` — map grids for routing/navigation + +# Core Module Index +--- + +- `src/functions.pl` — bootstrap and main loop. +- `src/Modules.pm` — module registration/loading. +- `src/Globals.pm` — shared runtime state. +- `src/Settings.pm` — config path and file registration. +- `src/FileParsers.pm` — control/table parsing. +- `src/Commands.pm` — command parser and handlers. +- `src/AI.pm` and `src/AI/CoreLogic.pm` — AI orchestration and decisions. +- `src/Actor.pm` and `src/ActorList.pm` — entity model and indexing. +- `src/Task.pm` and `src/TaskManager.pm` — task abstraction and scheduling. +- `src/Task/Route.pm`, `src/Task/Move.pm`, `src/Task/TalkNPC.pm` — routing, movement, NPC flows. +- `src/Network.pm`, `src/Network/PacketParser.pm`, `src/Network/Receive.pm`, `src/Network/Send.pm` — network core. diff --git a/gpt_upload_final/dataset_summary.md b/gpt_upload_final/dataset_summary.md new file mode 100644 index 0000000000..b413264536 --- /dev/null +++ b/gpt_upload_final/dataset_summary.md @@ -0,0 +1,38 @@ +# Dataset Summary +--- + +## Purpose +This dataset provides an LLM-oriented knowledge layer for OpenKore architecture, runtime flow, debugging, and practical usage. + +## Coverage areas +- **Architecture core**: context, subsystem boundaries, dependency map, code index. +- **Runtime flows**: AI loop, packet receive flow, NPC interaction, routing/movement, command and plugin hook flows. +- **Networking**: packet pipeline and XKore mode behavior. +- **Debugging**: playbook, decision trees, and common pitfalls. +- **Practical usage**: code recipes, FAQ, glossary, learning path, and user prompt examples. + +## Main artifact groups +1. **Foundational architecture docs** (`01`-`05`, `11`-`14`, `20`, `21`) +2. **Flow/diagram docs** (`06`-`10`, `26`, `27`) +3. **Troubleshooting docs** (`15`, `16`, `22`, `23`) +4. **Developer enablement docs** (`17`, `18`, `19`, `24`, `29`, `30`) + +## Intended users +- Developers onboarding to OpenKore internals. +- Maintainers debugging runtime and packet issues. +- LLM assistants that need stable structural context for responses. + +## Strengths +- Grounded in likely module and directory references from the repository. +- Organized by numbered, task-oriented documents. +- Includes actionable diagnostics and reusable snippet patterns. + +## Known limits +- Documentation is descriptive and may lag upstream implementation changes. +- Server-specific packet behavior can change rapidly after game updates. +- Mermaid diagrams prioritize clarity over exhaustive edge-case detail. + +## Maintenance recommendations +- Re-validate packet/XKore docs after serverType or recvpackets updates. +- Update recipe snippets when plugin/hook APIs evolve. +- Keep FAQ and pitfalls synced with recurring issue reports. diff --git a/gpt_upload_final/debugging.md b/gpt_upload_final/debugging.md new file mode 100644 index 0000000000..60c36cf5a8 --- /dev/null +++ b/gpt_upload_final/debugging.md @@ -0,0 +1,354 @@ +# Debugging Playbook +--- + +This playbook maps common OpenKore runtime failures to likely causes, inspection points, and step-by-step troubleshooting. + +--- + +## 1) Bot not moving +- **Likely subsystem**: AI + Task + Routing + Network Send +- **Possible causes**: + - AI state blocked (attack/dialog/task lock) + - Route task never created or immediately failing + - Movement packets not being sent or rejected + - Movement-related config prevents walking +- **Files to inspect**: + - `src/AI.pm`, `src/AI/CoreLogic.pm` + - `src/TaskManager.pm`, `src/Task/Move.pm`, `src/Task/Route.pm` + - `src/Network/Send.pm`, `src/Network/Receive.pm` +- **Config to inspect**: + - `control/config.txt` (movement/lockMap/route-related options) + - `control/mon_control.txt` (behavior that can pin combat state) +- **Debugging steps**: + 1. Confirm AI is ticking and not paused/stuck in a higher-priority state. + 2. Check whether `Task::Route`/`Task::Move` is queued in task manager. + 3. Validate that move packets are emitted by send path. + 4. Confirm receive updates are acknowledging position changes. + 5. Reduce constraints in config (lock-only behavior, avoid lists) and retest. + +## 2) Routing failure +- **Likely subsystem**: Routing + Field data + Task +- **Possible causes**: + - Missing/incorrect field data + - Blocked cells/portal transitions not resolved + - Route recomputation loops without progress +- **Files to inspect**: + - `src/Task/Route.pm`, `src/Task/Move.pm`, `src/Field.pm` +- **Config to inspect**: + - `control/config.txt` (route/move flags) + - `fields/*` (map walkability data) +- **Debugging steps**: + 1. Verify map field exists and corresponds to current map. + 2. Check route generation result (empty path or oscillating path). + 3. Confirm position updates are fresh (no stale coordinates). + 4. Test shorter route segments to isolate failing region. + 5. Re-check portal/map transition assumptions. + +## 3) NPC interaction failure +- **Likely subsystem**: Task + Network Receive/Send + Actor/NPC state +- **Possible causes**: + - Wrong NPC coordinates or stale actor reference + - Dialogue step mismatch with server response + - Talk packets not sent or incorrect sequence +- **Files to inspect**: + - `src/Task/TalkNPC.pm` + - `src/Network/Send.pm`, `src/Network/Receive.pm`, `src/Network/Receive/*` + - `src/ActorList.pm` +- **Config to inspect**: + - `control/config.txt` (npc interaction directives/scripts) +- **Debugging steps**: + 1. Confirm NPC exists in actor list at expected coordinates. + 2. Trace talk packet send sequence against expected dialogue steps. + 3. Verify receive handlers parse response packet IDs correctly for server type. + 4. Reproduce with minimal NPC script/task to reduce branching. + +## 4) Plugin not loading +- **Likely subsystem**: Plugin system + startup/module loading +- **Possible causes**: + - Syntax/runtime error in plugin file + - Incorrect plugin path/name + - Missing dependency module used by plugin +- **Files to inspect**: + - `src/Plugins.pm`, `src/Modules.pm` + - `plugins/<plugin_name>/*.pl` +- **Config to inspect**: + - `control/config.txt` (plugin enable/load entries) +- **Debugging steps**: + 1. Check startup logs for plugin load exceptions. + 2. Validate plugin registers with `Plugins::register`. + 3. Confirm plugin path and filename match expected loader behavior. + 4. Remove optional dependency imports to isolate failing require/use. + +## 5) Plugin hook not firing +- **Likely subsystem**: Plugin hooks + event dispatch +- **Possible causes**: + - Hook registered with wrong event name + - Hook callback blocked by guard condition + - Expected event never emitted in current runtime path +- **Files to inspect**: + - `src/Plugins.pm`, `src/functions.pl` + - Plugin file defining `Plugins::addHooks` +- **Config to inspect**: + - `control/config.txt` (conditions controlling event generation) +- **Debugging steps**: + 1. List hooks registered by plugin after load. + 2. Add lightweight logging at callback entry. + 3. Verify event name spelling and payload assumptions. + 4. Trigger event manually through a minimal reproduction path. + +## 6) Macro not triggering +- **Likely subsystem**: Macro plugin + command/hook bridge +- **Possible causes**: + - Macro file not loaded + - Automacro condition never true + - Macro command blocked by runtime state +- **Files to inspect**: + - `plugins/macro/macro.pl`, `plugins/macro/Macro/*` + - `src/Commands.pm`, `src/Plugins.pm` +- **Config to inspect**: + - `control/config.txt` (`macro_file` and related keys) + - macro definition file referenced by config +- **Debugging steps**: + 1. Confirm macro plugin loaded and macro file parsed. + 2. Validate automacro conditions against live state. + 3. Execute macro manually to separate trigger vs action issues. + 4. Inspect command dispatch path for blocked/invalid command. + +## 7) eventMacro issues +- **Likely subsystem**: eventMacro plugin + event parser/runner +- **Possible causes**: + - Invalid eventMacro syntax + - Trigger conditions not matching event payload + - Runner stalled by previous action/wait state +- **Files to inspect**: + - `plugins/eventMacro/eventMacro.pl` + - `plugins/eventMacro/eventMacro/{Core,Runner,Automacro,FileParser}.pm` +- **Config to inspect**: + - `control/config.txt` (`eventMacro_file`) + - eventMacro script file +- **Debugging steps**: + 1. Validate eventMacro file parsing with minimal script. + 2. Log trigger evaluation for target automacro. + 3. Check runner queue and pending wait/time gates. + 4. Reduce script to one trigger + one action and retest. + +## 8) Packet desync +- **Likely subsystem**: Networking receive/parser + server-type mappings +- **Possible causes**: + - Wrong server type packet tables + - Opcode map mismatch after server update + - Packet boundary/tokenization errors +- **Files to inspect**: + - `src/Network/MessageTokenizer.pm`, `src/Network/PacketParser.pm` + - `src/Network/Receive.pm`, `src/Network/Receive/*` + - `src/Network/Send.pm`, `src/Network/Send/*` +- **Config to inspect**: + - `control/config.txt` (serverType/recvpackets settings) + - `tables/*` packet definition files for the target server +- **Debugging steps**: + 1. Confirm selected serverType and recvpackets data. + 2. Compare failing opcode decode against current server packet definitions. + 3. Capture raw packet sequence around first desync point. + 4. Verify both receive and send side use compatible packet maps. + +## 9) XKore sync issues +- **Likely subsystem**: XKore transport/session bridging +- **Possible causes**: + - Client-proxy handshake mismatch + - Account/char/map relay state divergence + - Timing/forwarding issues in XKore mode +- **Files to inspect**: + - `src/Network/XKore.pm`, `src/Network/XKore2.pm`, `src/Network/XKoreProxy.pm` + - `src/Network/XKore2/{AccountServer,CharServer,MapServer}.pm` +- **Config to inspect**: + - `control/config.txt` (XKore mode and connection settings) +- **Debugging steps**: + 1. Confirm active mode (Direct vs XKore/XKore2/XKoreProxy). + 2. Trace handshake/login flow across account->char->map stages. + 3. Check for asymmetric forwarding (client sees state that bot does not, or inverse). + 4. Validate mode-specific ports/bind settings and restart cleanly. + +## 10) Visual client state not updating +- **Likely subsystem**: XKore bridge + packet forwarding + actor sync +- **Possible causes**: + - Forwarded packets not reaching client + - Actor state updated in bot but not mirrored to client channel + - Client session stuck after map transition +- **Files to inspect**: + - `src/Network/XKore*.pm` + - `src/Network/Receive.pm`, `src/ActorList.pm` +- **Config to inspect**: + - `control/config.txt` (XKore forwarding/session config) +- **Debugging steps**: + 1. Verify bot-side state is changing (position/actors) first. + 2. Confirm corresponding packets are forwarded to client path. + 3. Re-check session phase (account/char/map) for stuck transition. + 4. Reconnect client through same mode to re-establish synchronization. + +# Debug Decision Trees +--- + +These trees provide fast triage paths for recurring OpenKore failures. + +## A) Bot not moving / Routing failure + +```mermaid +flowchart TD + A[Bot not moving] --> B{AI ticking?} + B -- No --> B1[Inspect src/AI.pm and main loop in src/functions.pl] + B -- Yes --> C{Task::Route or Task::Move queued?} + C -- No --> C1[Inspect AI decisions in src/AI/CoreLogic.pm and config gates] + C -- Yes --> D{Move packets sent?} + D -- No --> D1[Inspect src/Network/Send.pm and command/task path] + D -- Yes --> E{Position updates received?} + E -- No --> E1[Inspect src/Network/Receive.pm and packet mapping] + E -- Yes --> F{Path valid in fields data?} + F -- No --> F1[Inspect src/Field.pm + fields/*] + F -- Yes --> G[Check blockers/lock settings in control/config.txt] +``` + +Use this when movement appears frozen or route tasks fail repeatedly. + +## B) NPC interaction failure + +```mermaid +flowchart TD + A[NPC interaction fails] --> B{NPC present in ActorList?} + B -- No --> B1[Inspect src/ActorList.pm updates from receive handlers] + B -- Yes --> C{TalkNPC task created?} + C -- No --> C1[Inspect trigger path in AI/commands/plugins] + C -- Yes --> D{Talk packets emitted?} + D -- No --> D1[Inspect src/Task/TalkNPC.pm + src/Network/Send.pm] + D -- Yes --> E{Expected response packet decoded?} + E -- No --> E1[Inspect src/Network/Receive/* serverType handlers] + E -- Yes --> F[Validate dialogue sequence assumptions in task script] +``` + +Use this when NPC talks stop, loop, or complete with wrong branch. + +## C) Plugin not loading / Hook not firing + +```mermaid +flowchart TD + A[Plugin issue] --> B{Plugin loads successfully?} + B -- No --> B1[Check syntax/dependencies + src/Plugins.pm loader logs] + B -- Yes --> C{Hook registered via Plugins::addHooks?} + C -- No --> C1[Fix registration block in plugin file] + C -- Yes --> D{Event emitted at runtime?} + D -- No --> D1[Inspect event source in src/functions.pl / network paths] + D -- Yes --> E{Callback guard conditions pass?} + E -- No --> E1[Log callback inputs and config-dependent guards] + E -- Yes --> F[Inspect side effects via Commands/TaskManager] +``` + +Use this for both plugin boot failures and silent hooks. + +## D) Macro/eventMacro not triggering + +```mermaid +flowchart TD + A[Macro/eventMacro not triggering] --> B{Plugin loaded?} + B -- No --> B1[Inspect plugins/macro or plugins/eventMacro load path] + B -- Yes --> C{Script file parsed?} + C -- No --> C1[Validate macro_file/eventMacro_file in control/config.txt] + C -- Yes --> D{Trigger condition true in live state?} + D -- No --> D1[Log variables/events used by automacro] + D -- Yes --> E{Action executes manually?} + E -- No --> E1[Inspect command path in src/Commands.pm] + E -- Yes --> F[Investigate scheduler/wait constraints in runner] +``` + +Use this to isolate parser issues from trigger logic and action execution. + +## E) Packet desync / XKore sync / visual client desync + +```mermaid +flowchart TD + A[Desync observed] --> B{Wrong serverType/packet table?} + B -- Yes --> B1[Fix control/config.txt + tables/* packet mappings] + B -- No --> C{Receive decode errors?} + C -- Yes --> C1[Inspect MessageTokenizer/PacketParser/Receive handlers] + C -- No --> D{Only in XKore mode?} + D -- No --> D1[Inspect send/receive parity and opcode updates] + D -- Yes --> E{Client and bot state diverge?} + E -- Yes --> E1[Inspect src/Network/XKore*.pm forwarding/session state] + E -- No --> F[Inspect map-transition/session phase sync] +``` + +Use this for protocol mismatches and client-bridge synchronization drift. + +# Common Pitfalls +--- + +Concise checklist of high-frequency debugging traps in OpenKore architecture and operations. + +## 1) Assuming movement issues are only routing bugs +- AI state locks, command overrides, or task queue starvation can mimic routing failures. +- Check AI/task state before editing route logic. + +## 2) Ignoring serverType/packet table drift +- Packet desync often starts after server updates when recvpackets/opcodes change. +- Keep `control/config.txt` server settings aligned with `tables/*` packet definitions. + +## 3) Debugging plugin hooks without proving event emission +- A hook can be correct but never run if the event is not emitted in the current flow. +- Verify both registration and actual event source path. + +## 4) Treating macro trigger failures as parser failures +- Many cases are valid parse + false trigger conditions. +- Separate: plugin load -> file parse -> trigger true -> action execution. + +## 5) Overlooking config-side constraints +- `control/config.txt`, `mon_control.txt`, and related files can disable or redirect behavior. +- Always test with minimal, known-good config for reproduction. + +## 6) Mixing XKore and DirectConnection assumptions +- Transport/session behavior differs; debugging steps are mode-specific. +- Confirm active mode first before tracing packet/session paths. + +## 7) Trusting visual client state over bot internal state +- In XKore scenarios, client view may lag/diverge from bot state. +- Compare bot-side actor/position updates with forwarded client packets. + +## 8) Investigating deep modules before reproducing minimally +- Large automation stacks (AI + plugin + macro + eventMacro) hide root causes. +- Reproduce with minimal script/task/command to localize fault domain. + +## Fast isolation matrix +| Symptom | First subsystem to verify | Primary files | +|---|---|---| +| Bot not moving | AI/Task | `src/AI.pm`, `src/TaskManager.pm` | +| Route fails | Routing/Field | `src/Task/Route.pm`, `src/Field.pm` | +| NPC fails | Task + Receive | `src/Task/TalkNPC.pm`, `src/Network/Receive.pm` | +| Plugin not loading | Plugin loader | `src/Plugins.pm`, plugin file | +| Hook silent | Event dispatch | `src/Plugins.pm`, event source module | +| Macro not triggering | Macro pipeline | `plugins/macro/*`, `src/Commands.pm` | +| eventMacro issues | eventMacro runner | `plugins/eventMacro/eventMacro/*` | +| Packet desync | Packet mapping | `src/Network/PacketParser.pm`, `tables/*` | +| XKore sync issues | XKore bridge | `src/Network/XKore*.pm` | +| Client view stale | XKore forwarding | `src/Network/XKore*.pm`, `src/ActorList.pm` | + +# Debugging Guide (Networking and Tables) +--- + +## 1) Confirm server/profile alignment +- Validate `serverType` and packet profile selection in `control/config.txt`. +- Verify matching packet tables in `tables/*` for the selected server family. + +## 2) Trace receive path +- `src/Network/MessageTokenizer.pm` -> `src/Network/PacketParser.pm` -> `src/Network/Receive.pm` -> `src/Network/Receive/*` +- Look for first packet decode mismatch point. + +## 3) Trace send path +- Inspect command/task origin, then `src/Network/Send.pm` + `src/Network/Send/*` packet construction. +- Confirm opcode/structure expected by current server profile. + +## 4) XKore-specific checks +- Inspect `src/Network/XKore.pm`, `src/Network/XKore2.pm`, `src/Network/XKoreProxy.pm`. +- Validate session phase transitions and packet forwarding symmetry. + +## 5) Iterative workflow +1. Reproduce with minimal actions. +2. Isolate receive vs send failure. +3. Validate serverType/packet table pairing. +4. Re-test after one controlled change. diff --git a/gpt_upload_final/development.md b/gpt_upload_final/development.md new file mode 100644 index 0000000000..a4c3be2eee --- /dev/null +++ b/gpt_upload_final/development.md @@ -0,0 +1,250 @@ +# Code Recipes +--- + +Practical snippets for common OpenKore development tasks. + +## 1) Minimal plugin skeleton +```perl +package myPlugin; + +use strict; +use Plugins; +use Log qw(message warning error); + +my $hooks = []; + +Plugins::register('myPlugin', 'Minimal plugin skeleton', \&on_unload); +$hooks = Plugins::addHooks( + ['start3', \&on_start], + ['AI_pre', \&on_ai_pre], +); + +sub on_start { + message "[myPlugin] started\n", 'info'; +} + +sub on_ai_pre { + my ($hookName, $args) = @_; + # lightweight periodic logic +} + +sub on_unload { + Plugins::delHooks($hooks); + message "[myPlugin] unloaded\n", 'info'; +} + +1; +``` + +## 2) Hook registration pattern +```perl +my $hooks = Plugins::addHooks( + ['packet_pre/actor_moved', \&on_actor_moved], + ['packet/map_changed', \&on_map_changed], + ['configModify', \&on_config_modify], +); + +sub on_actor_moved { + my ($hook, $args) = @_; + # inspect $args->{ID}, $args->{coords} +} +``` + +## 3) Command registration +```perl +Commands::register( + ['mycmd', 'run custom action', \&cmd_mycmd] +); + +sub cmd_mycmd { + my (undef, $args) = @_; + message "[myPlugin] mycmd args: $args\n", 'info'; +} + +# on unload: +Commands::unregister('mycmd'); +``` + +## 4) Packet hook example +```perl +my $hooks = Plugins::addHooks( + ['packet_pre/login_error', \&on_login_error], + ['packet/map_changed', \&on_map_changed], +); + +sub on_login_error { + my ($hook, $args) = @_; + warning "Login error packet received: $args->{type}\n"; +} +``` + +## 5) Macro example (`macros.txt`) +```txt +automacro autoHeal { + hp < 40% + timeout 1 + call { + do ss 28 + } +} + +macro goTown { + do move prontera +} +``` + +## 6) eventMacro example +```txt +automacro checkWeight { + InLockMap 1 + weight > 85% + call { + do conf itemsTakeAuto 0 + do ai clear + } +} + +automacro greetOnMap { + map prontera + run-once 1 + call { + do c Hello from OpenKore + } +} +``` + +## 7) Configuration examples (`control/config.txt`) +```txt +lockMap prontera +saveMap prontera +route_randomWalk 1 +teleportAuto_hp 25 +itemsTakeAuto 2 +attackAuto 2 +macro_file macros.txt +eventMacro_file eventMacros.txt +``` + +## 8) Debugging snippets +### 8.1 Print current map and coordinates +```perl +use Globals qw($field %char); +message sprintf("Map=%s x=%d y=%d\n", $field->baseName, $char{pos_to}{x}, $char{pos_to}{y}), 'debug'; +``` + +### 8.2 Trace command callback entry +```perl +sub cmd_mycmd { + my (undef, $args) = @_; + message "[TRACE] cmd_mycmd called with '$args'\n", 'debug'; +} +``` + +### 8.3 Check task queue state quickly +```perl +use TaskManager; +my $task = TaskManager::getTaskManager()->activeTask; +message "Active task: " . ($task ? ref($task) : 'none') . "\n", 'debug'; +``` + +# Learning Path for New OpenKore Developers +--- + +## Stage 0: Orientation (Day 1) +- Read `01_llm_context.md`, `02_architecture_overview.md`, `03_system_architecture_map.md`. +- Goal: understand receive->state->AI->task->send loop and key directories. + +## Stage 1: Runtime core comprehension (Days 2-3) +- Study `src/functions.pl`, `src/Globals.pm`, `src/Modules.pm`. +- Track one full startup + one main-loop tick. +- Exercise: write a short note describing what runs each tick. + +## Stage 2: Network + actor model (Days 4-5) +- Study `src/Network.pm`, `src/Network/Receive.pm`, `src/Network/Send.pm`, `src/ActorList.pm`. +- Exercise: trace one incoming packet to a world-state mutation. +- Exercise: trace one outgoing action from command/task to send packet. + +## Stage 3: AI and task flow (Days 6-7) +- Study `src/AI.pm`, `src/AI/CoreLogic.pm`, `src/TaskManager.pm`, `src/Task/Route.pm`, `src/Task/TalkNPC.pm`. +- Exercise: map how AI chooses a task and how task completion feeds back. + +## Stage 4: Plugins and automation (Week 2) +- Study `src/Plugins.pm`, `src/Commands.pm`, `plugins/macro/*`, `plugins/eventMacro/*`. +- Build a minimal plugin with one hook and one command. +- Add one macro and one eventMacro automation rule. + +## Stage 5: Config-driven behavior (Week 2) +- Study `control/config.txt`, `control/mon_control.txt`, relevant table files. +- Exercise: change one behavior via config only and document impact. + +## Stage 6: Debugging proficiency (Week 3) +- Use `15_debugging_playbook.md`, `16_debug_decision_trees.md`, `22_common_pitfalls.md`. +- Reproduce and fix one issue in each category: + - movement/routing + - plugin/hook + - macro/eventMacro + - packet/XKore sync + +## Stage 7: Advanced protocol + XKore (Week 3+) +- Study `09_networking_packets.md`, `10_xkore_modes.md`. +- Compare DirectConnection vs XKore2 flow and identify mode-specific risks. + +## Suggested competency checklist +- [ ] Explain OpenKore runtime architecture from memory. +- [ ] Trace packet receive and send paths. +- [ ] Implement and unload a safe plugin. +- [ ] Diagnose macro and eventMacro trigger failures. +- [ ] Diagnose route and movement failures. +- [ ] Diagnose basic packet/serverType desync. +- [ ] Explain XKore sync failure patterns. + +# Prompt Examples for Users +--- + +Realistic prompts users can ask an OpenKore-focused GPT assistant. + +## Architecture and code navigation +1. "Explain how `src/functions.pl` orchestrates the OpenKore main loop." +2. "Map the dependency chain between AI, TaskManager, and Network::Send." +3. "Which modules should I read first to understand actor updates from packets?" +4. "Summarize how plugin hooks interact with command execution." + +## Debugging +5. "My bot stopped moving. Give me a step-by-step diagnosis plan with files to inspect." +6. "How do I distinguish packet desync from route calculation failure?" +7. "Generate a troubleshooting checklist for NPC dialogue failures." +8. "Why are my plugin hooks not firing even though plugin loads fine?" +9. "Help me debug eventMacro that parses but never triggers." +10. "Create a minimal reproduction plan for XKore client sync issues." + +## Plugins +11. "Create a minimal plugin skeleton with register, hooks, and unload cleanup." +12. "Add a custom command `farmstatus` that prints current map and task." +13. "Show best practices for lightweight hook callbacks in OpenKore plugins." +14. "How do I instrument a plugin with debug logs without spamming output?" + +## Macro / eventMacro +15. "Write an automacro that pauses looting above 85% weight." +16. "Create eventMacro rules to send a chat message only once when entering Prontera." +17. "Compare macro and eventMacro for reactive combat behavior." +18. "How can I test whether a macro trigger condition is actually true at runtime?" + +## Routing and movement +19. "Explain why route tasks can oscillate and how to debug it." +20. "Show a script strategy to test short movement segments before full routes." +21. "What configs can block movement even if route exists?" + +## Networking and packets +22. "How do I verify that serverType and recvpackets are correctly configured?" +23. "Trace one incoming movement packet from socket bytes to Actor update." +24. "What logs should I capture when packet decode errors start after a patch?" +25. "Explain where to add temporary logging for packet send construction." + +## Configuration +26. "Review my config goals and suggest a minimal stable `config.txt` baseline." +27. "Which `mon_control.txt` settings commonly interfere with movement tasks?" +28. "How do lockMap and saveMap influence AI behavior?" + +## Learning and onboarding +29. "Build me a 2-week learning plan to become productive in OpenKore development." +30. "Give me a study order for networking, AI, tasks, plugins, and macros with exercises." diff --git a/gpt_upload_final/faq.md b/gpt_upload_final/faq.md new file mode 100644 index 0000000000..81e3210d22 --- /dev/null +++ b/gpt_upload_final/faq.md @@ -0,0 +1,148 @@ +# OpenKore Technical FAQ +--- + +## Architecture +1. **Q:** What is OpenKore's main runtime entry point? + **A:** `src/functions.pl` initializes modules and runs the main loop. +2. **Q:** Where is global runtime state stored? + **A:** Mostly in `src/Globals.pm` and shared structures. +3. **Q:** Which module runs AI logic? + **A:** `src/AI.pm` coordinates AI and calls `src/AI/CoreLogic.pm`. +4. **Q:** How are game entities represented? + **A:** Through `src/Actor.pm` and `src/ActorList.pm`. +5. **Q:** What handles tasks? + **A:** `src/Task.pm` defines tasks and `src/TaskManager.pm` schedules them. +6. **Q:** Where does command parsing happen? + **A:** In `src/Commands.pm`. +7. **Q:** How are plugins managed? + **A:** Via `src/Plugins.pm` lifecycle and hooks API. +8. **Q:** Why is architecture loop-based? + **A:** To repeatedly process receive->state->AI->tasks->send cycles. + +## Debugging +9. **Q:** First check when bot freezes? + **A:** Confirm AI tick and active task state. +10. **Q:** How to distinguish route vs movement failure? + **A:** Check if a route exists, then confirm move packets and position updates. +11. **Q:** Why log packet IDs during failures? + **A:** To detect serverType/recvpackets mismatch quickly. +12. **Q:** Best way to debug plugin hooks? + **A:** Log both hook registration and callback entry. +13. **Q:** Why test with minimal config? + **A:** To remove config side effects masking root causes. +14. **Q:** What indicates desync risk? + **A:** Decode errors, unknown packets, or stale actor updates. +15. **Q:** How to verify task deadlock? + **A:** Inspect active task and queued tasks in task manager. +16. **Q:** Should I debug with all plugins enabled? + **A:** No, isolate with minimal plugin set first. + +## Plugins +17. **Q:** Minimum plugin requirements? + **A:** `Plugins::register`, optional hooks, and clean unload. +18. **Q:** How to add a custom command? + **A:** Use `Commands::register` and unregister on unload. +19. **Q:** Why might plugin load fail silently? + **A:** Early syntax/runtime errors or missing dependencies. +20. **Q:** Can plugins alter AI behavior? + **A:** Yes, via commands, hooks, and task interactions. +21. **Q:** Where are community plugins stored? + **A:** Under `plugins/`. +22. **Q:** How to avoid hook performance issues? + **A:** Keep callbacks lightweight and defer heavy work. +23. **Q:** Can multiple plugins share hook names? + **A:** Yes, each callback runs if registered correctly. +24. **Q:** Why unregister hooks on unload? + **A:** To prevent stale callbacks and inconsistent state. + +## Macros / eventMacro +25. **Q:** Difference between macro and eventMacro? + **A:** Macro is classic command script flow; eventMacro is event/condition-driven automation. +26. **Q:** Where to set macro file path? + **A:** `control/config.txt` via `macro_file`. +27. **Q:** Where to set eventMacro file path? + **A:** `control/config.txt` via `eventMacro_file`. +28. **Q:** Why automacro never triggers? + **A:** Condition false, plugin not loaded, or parse issue. +29. **Q:** How to test macro trigger quickly? + **A:** Manually run equivalent command and compare behavior. +30. **Q:** Why use run-once in eventMacro? + **A:** To avoid repeated firing for one-time actions. +31. **Q:** Can macros execute OpenKore commands? + **A:** Yes, via `do <command>`. +32. **Q:** How to debug eventMacro parser issues? + **A:** Reduce to one trigger + one action and rebuild incrementally. + +## Routing / movement +33. **Q:** Which files control routing logic? + **A:** `src/Task/Route.pm`, `src/Task/Move.pm`, and `src/Field.pm`. +34. **Q:** What is walkability? + **A:** Whether a map cell is traversable based on field data. +35. **Q:** Why does route loop occur? + **A:** Stale position updates, blocked transitions, or invalid field assumptions. +36. **Q:** What is lockMap effect? + **A:** Restricts behavior to a preferred map scope. +37. **Q:** How to validate map data? + **A:** Ensure map exists in `fields/*` and coordinates are valid. +38. **Q:** Why movement works manually but not AI? + **A:** AI priorities/config may override movement intents. +39. **Q:** Can aggressive combat stop movement? + **A:** Yes, combat states can continuously preempt route tasks. +40. **Q:** Best first movement test? + **A:** Short route on same map with minimal automation enabled. + +## Networking +41. **Q:** Which module tokenizes incoming packets? + **A:** `src/Network/MessageTokenizer.pm`. +42. **Q:** Which module decodes packet structures? + **A:** `src/Network/PacketParser.pm` and receive handlers. +43. **Q:** Where is receive dispatch done? + **A:** `src/Network/Receive.pm`. +44. **Q:** Where are server-specific packet handlers? + **A:** `src/Network/Receive/*` and `src/Network/Send/*`. +45. **Q:** Why packet errors after server patch? + **A:** Opcode/table changes requiring updated mappings. +46. **Q:** Can wrong serverType break everything? + **A:** Yes, it causes systematic decode/encode mismatch. +47. **Q:** What causes partial desync? + **A:** Some packets map correctly while others fail for changed opcodes. +48. **Q:** How to isolate receive vs send bug? + **A:** Compare inbound decode logs and outbound packet construction separately. + +## NPC interaction +49. **Q:** Which task handles NPC dialogues? + **A:** `src/Task/TalkNPC.pm`. +50. **Q:** Why NPC script stalls mid-dialogue? + **A:** Response packet mismatch or wrong expected step sequence. +51. **Q:** Must NPC be in actor list first? + **A:** Yes, reliable interaction depends on valid actor presence and position. +52. **Q:** Why wrong NPC gets targeted? + **A:** Incorrect coordinates or stale target selection. +53. **Q:** What to inspect first in NPC failure? + **A:** Talk packet sequence and corresponding receive responses. +54. **Q:** Can plugins interfere with NPC flow? + **A:** Yes, if they alter commands/tasks concurrently. + +## Config behavior +55. **Q:** Where are core behavior settings? + **A:** `control/config.txt`. +56. **Q:** What file controls monster behavior rules? + **A:** `control/mon_control.txt`. +57. **Q:** Why config changes seem ignored? + **A:** Reload needed, wrong key, or overridden by plugin automation. +58. **Q:** How to keep config deterministic? + **A:** Use minimal explicit settings and disable conflicting automation. +59. **Q:** What is saveMap used for? + **A:** Preferred return/safety map behavior. +60. **Q:** Why document config with code changes? + **A:** Behavior is policy-driven; docs prevent invisible config regressions. + +## XKore / client sync +61. **Q:** What is XKore2 role? + **A:** Local proxy/relay flow for account/char/map sessions. +62. **Q:** Why client view stale but bot state updates? + **A:** Forwarding/session sync issue in XKore bridge path. +63. **Q:** Is debugging Direct and XKore identical? + **A:** No, transport/session layers differ significantly. +64. **Q:** First step for XKore sync bugs? + **A:** Confirm active mode and trace handshake phase transitions. diff --git a/gpt_upload_final/glossary.md b/gpt_upload_final/glossary.md new file mode 100644 index 0000000000..2e8438c7ad --- /dev/null +++ b/gpt_upload_final/glossary.md @@ -0,0 +1,28 @@ +# Glossary +--- + +- **Actor**: Runtime entity abstraction (player, monster, NPC, portal) managed via `Actor` modules. +- **AI queue**: Ordered AI intention/state processing that decides which action/task should run next. +- **Task**: Executable unit with lifecycle (start/process/finish), e.g., move, route, NPC interaction. +- **Plugin hook**: Callback registration point triggered by runtime events (`Plugins::addHooks`). +- **Command handler**: Function bound to a command name through `Commands::register`. +- **Packet handler**: Receive/send routine that decodes or encodes specific protocol packets. +- **Route**: Planned path between coordinates/maps, later executed stepwise by movement tasks. +- **Automacro**: Conditional automation rule that triggers scripted actions when conditions match. +- **eventMacro**: Event/condition-driven macro framework with parser, runner, and trigger model. +- **lockMap**: Configuration policy limiting movement/behavior to a designated map. +- **saveMap**: Preferred map used for return/recovery behavior. +- **Walkability**: Whether map cells are traversable according to field data and routing constraints. +- **Client sync**: Consistency between bot internal state and visual game client state (important in XKore). +- **Server packet**: Protocol message exchanged with the game server; parsed/encoded by network modules. +- **ServerType**: Packet profile selection controlling opcode and packet layout mapping. +- **Receive dispatch**: Mechanism mapping inbound opcode frames to concrete handler methods. +- **Send pipeline**: Path from action intent to serialized outbound packet. +- **XKore mode**: Bridged/proxy connection model variants (`XKore`, `XKore2`, `XKoreProxy`). +- **Main loop tick**: One full iteration of runtime processing in `functions.pl`. +- **Field data**: Map walkability/cell data used by route calculations. +- **Desync**: Divergence between expected protocol/state and observed runtime behavior. +- **Hook event**: Named runtime signal emitted for plugin subscribers. +- **Task manager**: Scheduler coordinating active/pending tasks and transitions. +- **Packet desync**: Failure mode where packet mappings no longer align with server behavior. +- **Control files**: Operator-facing config files under `control/` that drive runtime policy. diff --git a/gpt_upload_final/plugin_config_system.md b/gpt_upload_final/plugin_config_system.md new file mode 100644 index 0000000000..369c3221b4 --- /dev/null +++ b/gpt_upload_final/plugin_config_system.md @@ -0,0 +1,98 @@ +# Plugin System +--- + +## Purpose +OpenKore plugins extend runtime behavior without editing core modules. + +## Core interfaces +- `src/Plugins.pm`: plugin lifecycle and hook dispatch. +- `src/Commands.pm`: command registration/execution surface used by plugins. +- `plugins/*`: plugin implementations (`*.pl` entrypoints + helper modules). + +## Lifecycle pattern +1. Register plugin with `Plugins::register(...)`. +2. Register hooks with `Plugins::addHooks(...)`. +3. Optionally register commands via `Commands::register(...)`. +4. On unload, remove hooks/commands and clear plugin state. + +## Common hook usage +- Startup hooks (e.g., startup/init phases) +- Loop hooks (periodic behavior) +- Packet hooks (`packet/*`, `packet_pre/*`) +- Config-change hooks (`configModify`) + +## Representative plugins +- `plugins/macro/macro.pl` +- `plugins/eventMacro/eventMacro.pl` +- `plugins/reconnect/reconnect.pl` +- `plugins/profiles/profiles.pl` +- `plugins/map/map.pl` + +# Configuration System +--- + +## Purpose +Behavior policy is primarily configuration-driven via `control/` and table data under `tables/`. + +## Key files +- `control/config.txt`: global runtime/AI/combat/movement/network options. +- `control/items_control.txt`: item pickup/storage/sell policy. +- `control/mon_control.txt`: per-monster behavior policy. + +## Runtime integration +- `src/Settings.pm`: config path/file registration. +- `src/FileParsers.pm`: parser logic for control/table formats. +- `src/Globals.pm`: shared runtime state consumed by AI/tasks/plugins. + +## Debugging guidance +- Validate key names and value format first. +- Re-test with minimal config to isolate policy conflicts. +- Check plugin/macro automation that may override config expectations. + +# Macro System +--- + +## Components +- Macro plugin: `plugins/macro/macro.pl`, `plugins/macro/Macro/*` +- eventMacro plugin: `plugins/eventMacro/eventMacro.pl`, `plugins/eventMacro/eventMacro/*` + +## Configuration keys +- `macro_file` -> macro script path (commonly `macros.txt`). +- `eventMacro_file` -> eventMacro script path (commonly `eventMacros.txt`). + +## Execution model +1. Plugin loads and parses macro file. +2. Hooks/events trigger condition evaluation. +3. Matching automacro rules enqueue/execute actions. +4. Actions flow through command/task/network paths. + +## Operational notes +- Macro and eventMacro are complementary automation layers. +- Troubleshooting should separate: load -> parse -> trigger -> action execution. + +# Table Reference +--- + +## Purpose +`tables/` contains protocol and gameplay data used by network parsing, IDs, maps, and runtime lookups. + +## Core packet/profile tables +- `tables/servers.txt` +- `tables/packetlist.txt` +- `tables/packetdescriptions.txt` +- server-family `recvpackets.txt` files (e.g., `tables/kRO/recvpackets.txt`, `tables/iRO/recvpackets.txt`) + +## Gameplay identity tables +- `tables/SKILL_id_handle.txt` +- `tables/STATUS_id_handle.txt` +- `tables/statusnametable.txt` +- `tables/itemtypes.txt` +- `tables/elements.txt` + +## World/content tables +- `tables/portals.txt` +- `tables/skillsarea.txt` +- regional `maps.txt`, `items.txt`, `skillnametable.txt` + +## Notes +Packet-related incidents should always be validated against serverType + recvpackets alignment. diff --git a/gpt_upload_final/system_flows.md b/gpt_upload_final/system_flows.md new file mode 100644 index 0000000000..115d8e8116 --- /dev/null +++ b/gpt_upload_final/system_flows.md @@ -0,0 +1,182 @@ +# Execution Flows +--- + +## Runtime execution loop +OpenKore runs a tick-oriented loop rooted in `src/functions.pl`, where network ingestion, state updates, AI decisions, and task execution are iterated continuously. + +Operational sequence: +1. Main loop tick in `src/functions.pl` advances network, AI, and task phases. +2. Receive handlers (`src/Network/Receive.pm`, `src/Network/Receive/*`) apply packet-driven state updates. +3. Shared state (`src/Globals.pm`, `src/ActorList.pm`) becomes input for AI logic. +4. AI (`src/AI.pm`, `src/AI/CoreLogic.pm`) chooses actions and updates task queues. +5. Tasks (`src/TaskManager.pm`, `src/Task/*`) execute stepwise and emit outgoing packets through send modules. + +## AI execution loop + +```mermaid +flowchart TD + A[AI tick\nsrc/AI.pm] --> B[Gather context\nGlobals + ActorList + config] + B --> C[Core logic\nsrc/AI/CoreLogic.pm] + C --> D{Action required?} + D -- No --> E[Idle/monitor state] + D -- Yes --> F[Create/adjust tasks\nsrc/TaskManager.pm] + F --> G[Run task step\nsrc/Task/*.pm] + G --> H[Emit commands/packets\nCommands + Network::Send] + H --> I[Wait for receive updates] + E --> I + I --> A +``` + +AI is decision-oriented; tasks hold execution state between ticks while receive handlers provide feedback. + +# NPC Interaction Flow +--- + +NPC interactions are executed as task-driven conversations, typically using `src/Task/TalkNPC.pm` and packet handlers under `src/Network/Receive/*.pm`. + +```mermaid +sequenceDiagram + participant U as AI/Command/Plugin + participant TM as TaskManager + participant TN as Task::TalkNPC + participant NS as Network::Send + participant S as Ragnarok Server + participant NR as Network::Receive + participant G as Globals/Actor state + + U->>TM: enqueue NPC interaction + TM->>TN: start dialogue task + TN->>NS: send talk/response packet + NS->>S: outbound packet + S->>NR: NPC response packet + NR->>G: update dialogue/state flags + NR->>TM: task-relevant updates + TM->>TN: advance next dialogue step + TN-->>TM: complete/fail +``` + +`Task::TalkNPC` keeps dialogue progression explicit, while receive handlers synchronize server-side conversation state back into runtime state. + +# Routing and Movement +--- + +Routing converts destination intent into path segments and movement packets using `src/Task/Route.pm`, `src/Task/Move.pm`, `src/Field.pm`, and `fields/*` data. + +```mermaid +flowchart TD + A[AI/Command target\nmap + coordinates] --> B[Task::Route init] + B --> C[Load field graph\nsrc/Field.pm + fields/*] + C --> D[Compute path segments] + D --> E[Task::Move executes step] + E --> F[Send move packet\nsrc/Network/Send.pm] + F --> G[Receive position update\nsrc/Network/Receive.pm] + G --> H{Reached waypoint?} + H -- No --> E + H -- Yes --> I{Reached destination?} + I -- No --> D + I -- Yes --> J[Route complete] +``` + +Routing is iterative and feedback-driven: each movement step is validated by receive updates before continuing. + +# Networking and Packet Flow +--- + +OpenKore packet handling combines framing/tokenization, opcode parsing, and server-type receive/send classes under `src/Network/*`. + +## Packet receive flow + +```mermaid +flowchart TD + A[Socket bytes\nNetwork mode] --> B[MessageTokenizer\nsrc/Network/MessageTokenizer.pm] + B --> C[PacketParser\nsrc/Network/PacketParser.pm] + C --> D[Receive dispatcher\nsrc/Network/Receive.pm] + D --> E[Server-type handler\nsrc/Network/Receive/*] + E --> F[Runtime mutation\nGlobals + Actor + Task signals] + F --> G[Hooks/plugins notified\nsrc/Plugins.pm] +``` + +Receive processing turns protocol frames into domain-level state transitions used by AI and tasks. + +## Packet send path (supporting context) +- Action request sources: AI, commands, task modules, plugins/macros. +- Encoding path: `src/Network/Send.pm` -> `src/Network/Send/*` (server-type specific structures). +- Transport path: active mode (`DirectConnection`, `XKore`, `XKore2`, or `XKoreProxy`) in `src/Network/*.pm`. + +# XKore Modes +--- + +XKore modes define how OpenKore connects to the game client/server pipeline. + +## Core modules +- `src/Network/XKore.pm` +- `src/Network/XKore2.pm` +- `src/Network/XKoreProxy.pm` +- `src/Network/DirectConnection.pm` (baseline comparison) + +## Mode summary +- **DirectConnection**: bot connects directly to game servers. +- **XKore**: client-assisted mode using forwarding/bridging logic. +- **XKore2**: OpenKore hosts local account/char/map proxy endpoints (`src/Network/XKore2/*`) and relays traffic. +- **XKoreProxy**: proxy-style mediation path for packet bridging. + +## Architectural impact +- All modes reuse packet parsing/dispatch and send modules (`src/Network/{Receive,Send}.pm`). +- Mode choice primarily changes transport/session orchestration and how packets are sourced/sinked. +- AI, tasks, actor state, plugins, and macro systems remain mode-agnostic above the network abstraction. + +# Architecture Diagrams (Mermaid) +--- + +This file indexes the runtime diagrams for OpenKore subsystem interactions. + +## Included diagrams +1. **AI execution loop** — `06_execution_flows.md` +2. **Packet receive flow** — `09_networking_packets.md` +3. **NPC interaction flow** — `07_npc_interaction_flow.md` +4. **Plugin hook execution** — `27_system_flows.md` +5. **Routing and movement flow** — `08_routing_and_movement.md` +6. **Command execution flow** — `27_system_flows.md` + +## Reading guidance +- Start with `06_execution_flows.md` for the main runtime cycle. +- Use `09_networking_packets.md` and `10_xkore_modes.md` for transport/protocol context. +- Use `07_npc_interaction_flow.md` and `08_routing_and_movement.md` for task-level behavioral flows. +- Use `27_system_flows.md` for extension and control-plane flows. + +# System Flows +--- + +## Plugin hook execution flow +Plugins extend runtime behavior through hook callbacks registered in `src/Plugins.pm` and commonly invoke `src/Commands.pm` or task APIs. + +```mermaid +flowchart TD + A[Plugin load\nplugins/*.pl] --> B[Plugins::register] + B --> C[Plugins::addHooks] + C --> D[Runtime event\nloop/packet/config] + D --> E[Plugin callback] + E --> F{Action needed?} + F -- No --> G[Return] + F -- Yes --> H[Execute command\nsrc/Commands.pm] + H --> I[Task/Network side effects] + I --> G +``` + +Hook callbacks are event-driven and should remain lightweight; heavy operations are usually delegated to commands/tasks. + +## Command execution flow + +```mermaid +flowchart TD + A[Input source\nconsole/plugin/macro] --> B[Commands::run] + B --> C[Command parser\nsrc/Commands.pm] + C --> D{Built-in or plugin cmd?} + D -- Built-in --> E[Built-in handler] + D -- Plugin --> F[Plugin command callback] + E --> G[Update state / queue task / send packet] + F --> G + G --> H[Feedback to interface/log] +``` + +Commands are a shared control surface across manual operations, plugins, and macro automation. diff --git a/openkore_llm_knowledge/knowledge/01_llm_context.md b/openkore_llm_knowledge/knowledge/01_llm_context.md new file mode 100644 index 0000000000..cf85d00779 --- /dev/null +++ b/openkore_llm_knowledge/knowledge/01_llm_context.md @@ -0,0 +1,21 @@ +# OpenKore LLM Context + +## Objective +This layer gives an LLM enough structural context to reason about OpenKore runtime behavior, module boundaries, and extension points. + +## Runtime model (high level) +1. `src/functions.pl` boots runtime services, loads config/tables, initializes network mode, and drives the main loop. +2. Network receive handlers (`src/Network/Receive*.pm`) transform packets into world-state updates. +3. Actor state (`src/Actor.pm`, `src/ActorList.pm`) becomes the shared world model for decision logic. +4. AI (`src/AI.pm`, `src/AI/CoreLogic.pm`) evaluates state and schedules actions through tasks. +5. Task execution (`src/TaskManager.pm`, `src/Task/*.pm`) performs multi-step actions and emits packets through send modules (`src/Network/Send*.pm`). +6. Plugins (`src/Plugins.pm`, `plugins/*`) and macro layers (`plugins/macro`, `plugins/eventMacro`) inject automation and custom behavior. + +## Key architecture anchors +- **Entry/control loop**: `src/functions.pl` +- **Global state/config loading**: `src/Globals.pm`, `src/Settings.pm`, `src/FileParsers.pm`, `control/` +- **AI + behavior**: `src/AI.pm`, `src/AI/CoreLogic.pm` +- **Actor model**: `src/Actor.pm`, `src/ActorList.pm`, `src/Actor/*` +- **Task orchestration**: `src/Task.pm`, `src/TaskManager.pm`, `src/Task/*` +- **Networking and packets**: `src/Network.pm`, `src/Network/*` +- **Plugin/macro automation**: `src/Plugins.pm`, `plugins/macro/*`, `plugins/eventMacro/*` diff --git a/openkore_llm_knowledge/knowledge/02_architecture_overview.md b/openkore_llm_knowledge/knowledge/02_architecture_overview.md new file mode 100644 index 0000000000..6bfe238247 --- /dev/null +++ b/openkore_llm_knowledge/knowledge/02_architecture_overview.md @@ -0,0 +1,23 @@ +# OpenKore Architecture Overview + +## Top-level structure +- `src/`: core runtime modules (AI, actor model, networking, tasks, plugins API, command layer). +- `control/`: operator configuration and behavior policies (`config.txt`, control lists, route/macro inputs). +- `tables/`: protocol/game data mappings (packet maps, item/skill/map metadata, server-specific variants). +- `plugins/`: optional extensions, including macro/eventMacro automation stacks. +- `fields/`: map field data used by routing/navigation. + +## Core runtime layers +1. **Bootstrap + loop**: `src/functions.pl` and `src/Modules.pm` +2. **State + config**: `src/Globals.pm`, `src/Settings.pm`, `src/FileParsers.pm` +3. **Network transport + packet translation**: `src/Network.pm`, `src/Network/{Receive,Send,PacketParser}.pm` +4. **World model**: `src/Actor.pm`, `src/ActorList.pm`, `src/Field.pm` +5. **Decision engine**: `src/AI.pm`, `src/AI/CoreLogic.pm` +6. **Execution engine**: `src/TaskManager.pm`, `src/Task/*.pm` +7. **Extension surface**: `src/Plugins.pm`, `src/Commands.pm`, `plugins/*` + +## Architectural characteristics +- **Event-driven and loop-based**: runtime state is advanced each tick by receive updates + AI/task progression. +- **Protocol-adapter design**: packet send/receive classes are split by server type under `src/Network/Receive/*` and `src/Network/Send/*`. +- **Policy outside code**: behavior is heavily configured via `control/` and `tables/` files. +- **Extension-first automation**: macro/eventMacro run as plugins and reuse command/hook/task pathways. diff --git a/openkore_llm_knowledge/knowledge/03_system_architecture_map.md b/openkore_llm_knowledge/knowledge/03_system_architecture_map.md new file mode 100644 index 0000000000..3181b0647f --- /dev/null +++ b/openkore_llm_knowledge/knowledge/03_system_architecture_map.md @@ -0,0 +1,43 @@ +# System Architecture Map (Textual) + +## Primary flow +`functions.pl main loop` -> `Network receive` -> `Actor/Globals update` -> `AI decision` -> `Task execution` -> `Network send` + +## Subsystem map + +### 1) Networking +- Connection/mode handling: `src/Network.pm`, `src/Network/DirectConnection.pm`, `src/Network/XKore*.pm` +- Packet ingestion and dispatch: `src/Network/MessageTokenizer.pm`, `src/Network/PacketParser.pm`, `src/Network/Receive.pm`, `src/Network/Receive/*` +- Packet construction: `src/Network/Send.pm`, `src/Network/Send/*` + +### 2) Actor system +- Base entity model: `src/Actor.pm`, `src/Actor/*` +- Actor collections and lookup: `src/ActorList.pm` +- World/map coupling: `src/Field.pm`, map and position state in globals + +### 3) AI subsystem +- AI stack state and sequencing: `src/AI.pm` +- Core behavior logic: `src/AI/CoreLogic.pm` +- Uses actor/world/config state; issues actions via task manager and commands + +### 4) Task subsystem +- Task abstraction: `src/Task.pm` +- Scheduler and lifecycle: `src/TaskManager.pm` +- High-impact tasks: `src/Task/Route.pm`, `src/Task/Move.pm`, `src/Task/TalkNPC.pm`, `src/Task/UseSkill.pm` + +### 5) Configuration subsystem +- Config discovery/loading: `src/Settings.pm`, `src/FileParsers.pm` +- Runtime shared state: `src/Globals.pm` +- Policy sources: `control/*`, `tables/*` + +### 6) Plugin + macro subsystem +- Hook and plugin lifecycle: `src/Plugins.pm` +- Command bridge: `src/Commands.pm` +- Macro engines: `plugins/macro/*`, `plugins/eventMacro/*` + +## Key entry points +- Startup/main loop: `src/functions.pl` +- Command dispatch: `src/Commands.pm` +- Hook registration: `src/Plugins.pm` +- AI tick path: `src/AI.pm` -> `src/AI/CoreLogic.pm` +- Packet receive path: `src/Network/Receive.pm` + server-specific receivers diff --git a/openkore_llm_knowledge/knowledge/04_core_subsystems.md b/openkore_llm_knowledge/knowledge/04_core_subsystems.md new file mode 100644 index 0000000000..9621268ac6 --- /dev/null +++ b/openkore_llm_knowledge/knowledge/04_core_subsystems.md @@ -0,0 +1,49 @@ +# Core Subsystems + +## AI +- Main modules: `src/AI.pm`, `src/AI/CoreLogic.pm` +- Role: maintain AI queues/state machines, evaluate combat/loot/movement priorities, enqueue tasks. +- Inputs: actor state, config policies, packet-driven updates. +- Outputs: task requests, command invocations, action intents. + +## Actor system +- Main modules: `src/Actor.pm`, `src/ActorList.pm`, `src/Actor/*` +- Role: represent player/NPC/monster/portal entities, with indexed lookup for targeting and proximity logic. +- Inputs: receive handlers and map updates. +- Outputs: query surface for AI, tasks, commands, and plugins. + +## Networking +- Main modules: `src/Network.pm`, `src/Network/{Receive,Send,PacketParser,MessageTokenizer}.pm`, `src/Network/XKore*.pm` +- Role: manage transport mode, parse inbound packets, encode outbound actions, support server-specific packet families. +- Inputs: socket data + outgoing action intents. +- Outputs: state mutations via receive handlers and serialized packets to servers/clients. + +## Task system +- Main modules: `src/Task.pm`, `src/TaskManager.pm`, `src/Task/*` +- Role: run asynchronous multi-step actions (route, move, NPC dialogs, skill/item use). +- Inputs: AI/command/plugin action requests. +- Outputs: packet sends, follow-up tasks, completion/failure states consumed by AI. + +## Plugin system +- Main modules: `src/Plugins.pm`, `plugins/*` +- Role: dynamic extension lifecycle (`register`, hooks, unload), runtime behavior injection. +- Inputs: startup/load events, packet-related hooks, periodic loop hooks. +- Outputs: custom commands, state transitions, automation triggers. + +## Configuration system +- Main modules: `src/Settings.pm`, `src/FileParsers.pm`, `control/*`, `tables/*` +- Role: load policy and data files that parameterize AI, networking, and gameplay handling. +- Inputs: text configs and table files. +- Outputs: normalized runtime config/state in globals and subsystem-specific structures. + +## Macro system +- Main modules: `plugins/macro/macro.pl`, `plugins/macro/Macro/*`, `plugins/eventMacro/eventMacro.pl`, `plugins/eventMacro/eventMacro/*` +- Role: declarative automation over hooks and game events, with conditional triggers and scripted actions. +- Inputs: macro definitions and live runtime events. +- Outputs: command execution and indirect task/network activity. + +## Routing +- Main modules: `src/Task/Route.pm`, `src/Task/Move.pm`, `src/Field.pm`, `fields/*` +- Role: compute and execute navigation paths across map cells/portals. +- Inputs: destination intents, field data, actor position/state. +- Outputs: stepwise movement actions and route completion/fallback states. diff --git a/openkore_llm_knowledge/knowledge/05_module_dependency_map.md b/openkore_llm_knowledge/knowledge/05_module_dependency_map.md new file mode 100644 index 0000000000..35f8eb23cf --- /dev/null +++ b/openkore_llm_knowledge/knowledge/05_module_dependency_map.md @@ -0,0 +1,29 @@ +# Module Dependency Map + +## Dependency chains (high impact) +1. `src/functions.pl` -> `src/Settings.pm` / `src/FileParsers.pm` / `src/Modules.pm` (bootstrap) +2. `src/functions.pl` -> `src/Network.pm` + `src/Network/*` (connection and packet loop) +3. `src/Network/Receive*.pm` -> `src/Globals.pm` + `src/Actor*.pm` (state mutation) +4. `src/AI.pm` -> `src/AI/CoreLogic.pm` -> `src/TaskManager.pm` + `src/Task/*` (decision to execution) +5. `src/Task/*` -> `src/Network/Send*.pm` (action serialization) +6. `src/Plugins.pm` + `plugins/*` -> `src/Commands.pm` / AI / TaskManager (extension control paths) + +## Subsystem dependency view +- **AI depends on**: Actor state, config policies, task scheduler, command surface. +- **Actor system depends on**: Network receive updates and global runtime registries. +- **Task system depends on**: AI/commands/plugins for intents; network send + actor/map updates for progression. +- **Plugin/macro depends on**: hook lifecycle (`src/Plugins.pm`), command execution (`src/Commands.pm`), global state. +- **Routing depends on**: task framework, field data (`fields/*`), movement packet sends. + +## Coupling hotspots +- `src/Globals.pm`: shared mutable state touched by receive, AI, and plugins. +- `src/Commands.pm`: cross-cutting control surface used by user input and automation. +- `src/TaskManager.pm`: convergence point for AI and scripted automation. +- `src/Network/Receive.pm`: ingress bridge from protocol events to internal state transitions. + +## Practical navigation order for analysis +1. Start at `src/functions.pl`. +2. Follow receive path (`src/Network/Receive.pm`, subtype receivers). +3. Inspect AI loop (`src/AI.pm`, `src/AI/CoreLogic.pm`). +4. Inspect task execution (`src/TaskManager.pm`, route/move/NPC tasks). +5. Inspect extension points (`src/Plugins.pm`, macro/eventMacro plugins). diff --git a/openkore_llm_knowledge/knowledge/06_execution_flows.md b/openkore_llm_knowledge/knowledge/06_execution_flows.md new file mode 100644 index 0000000000..cac52ed2b0 --- /dev/null +++ b/openkore_llm_knowledge/knowledge/06_execution_flows.md @@ -0,0 +1,29 @@ +# Execution Flows + +## Runtime execution loop +OpenKore runs a tick-oriented loop rooted in `src/functions.pl`, where network ingestion, state updates, AI decisions, and task execution are iterated continuously. + +Operational sequence: +1. Main loop tick in `src/functions.pl` advances network, AI, and task phases. +2. Receive handlers (`src/Network/Receive.pm`, `src/Network/Receive/*`) apply packet-driven state updates. +3. Shared state (`src/Globals.pm`, `src/ActorList.pm`) becomes input for AI logic. +4. AI (`src/AI.pm`, `src/AI/CoreLogic.pm`) chooses actions and updates task queues. +5. Tasks (`src/TaskManager.pm`, `src/Task/*`) execute stepwise and emit outgoing packets through send modules. + +## AI execution loop + +```mermaid +flowchart TD + A[AI tick\nsrc/AI.pm] --> B[Gather context\nGlobals + ActorList + config] + B --> C[Core logic\nsrc/AI/CoreLogic.pm] + C --> D{Action required?} + D -- No --> E[Idle/monitor state] + D -- Yes --> F[Create/adjust tasks\nsrc/TaskManager.pm] + F --> G[Run task step\nsrc/Task/*.pm] + G --> H[Emit commands/packets\nCommands + Network::Send] + H --> I[Wait for receive updates] + E --> I + I --> A +``` + +AI is decision-oriented; tasks hold execution state between ticks while receive handlers provide feedback. diff --git a/openkore_llm_knowledge/knowledge/07_npc_interaction_flow.md b/openkore_llm_knowledge/knowledge/07_npc_interaction_flow.md new file mode 100644 index 0000000000..48d5f80007 --- /dev/null +++ b/openkore_llm_knowledge/knowledge/07_npc_interaction_flow.md @@ -0,0 +1,26 @@ +# NPC Interaction Flow + +NPC interactions are executed as task-driven conversations, typically using `src/Task/TalkNPC.pm` and packet handlers under `src/Network/Receive/*.pm`. + +```mermaid +sequenceDiagram + participant U as AI/Command/Plugin + participant TM as TaskManager + participant TN as Task::TalkNPC + participant NS as Network::Send + participant S as Ragnarok Server + participant NR as Network::Receive + participant G as Globals/Actor state + + U->>TM: enqueue NPC interaction + TM->>TN: start dialogue task + TN->>NS: send talk/response packet + NS->>S: outbound packet + S->>NR: NPC response packet + NR->>G: update dialogue/state flags + NR->>TM: task-relevant updates + TM->>TN: advance next dialogue step + TN-->>TM: complete/fail +``` + +`Task::TalkNPC` keeps dialogue progression explicit, while receive handlers synchronize server-side conversation state back into runtime state. diff --git a/openkore_llm_knowledge/knowledge/08_routing_and_movement.md b/openkore_llm_knowledge/knowledge/08_routing_and_movement.md new file mode 100644 index 0000000000..a1e0b0cf5f --- /dev/null +++ b/openkore_llm_knowledge/knowledge/08_routing_and_movement.md @@ -0,0 +1,20 @@ +# Routing and Movement + +Routing converts destination intent into path segments and movement packets using `src/Task/Route.pm`, `src/Task/Move.pm`, `src/Field.pm`, and `fields/*` data. + +```mermaid +flowchart TD + A[AI/Command target\nmap + coordinates] --> B[Task::Route init] + B --> C[Load field graph\nsrc/Field.pm + fields/*] + C --> D[Compute path segments] + D --> E[Task::Move executes step] + E --> F[Send move packet\nsrc/Network/Send.pm] + F --> G[Receive position update\nsrc/Network/Receive.pm] + G --> H{Reached waypoint?} + H -- No --> E + H -- Yes --> I{Reached destination?} + I -- No --> D + I -- Yes --> J[Route complete] +``` + +Routing is iterative and feedback-driven: each movement step is validated by receive updates before continuing. diff --git a/openkore_llm_knowledge/knowledge/09_networking_packets.md b/openkore_llm_knowledge/knowledge/09_networking_packets.md new file mode 100644 index 0000000000..a72e17de20 --- /dev/null +++ b/openkore_llm_knowledge/knowledge/09_networking_packets.md @@ -0,0 +1,22 @@ +# Networking and Packet Flow + +OpenKore packet handling combines framing/tokenization, opcode parsing, and server-type receive/send classes under `src/Network/*`. + +## Packet receive flow + +```mermaid +flowchart TD + A[Socket bytes\nNetwork mode] --> B[MessageTokenizer\nsrc/Network/MessageTokenizer.pm] + B --> C[PacketParser\nsrc/Network/PacketParser.pm] + C --> D[Receive dispatcher\nsrc/Network/Receive.pm] + D --> E[Server-type handler\nsrc/Network/Receive/*] + E --> F[Runtime mutation\nGlobals + Actor + Task signals] + F --> G[Hooks/plugins notified\nsrc/Plugins.pm] +``` + +Receive processing turns protocol frames into domain-level state transitions used by AI and tasks. + +## Packet send path (supporting context) +- Action request sources: AI, commands, task modules, plugins/macros. +- Encoding path: `src/Network/Send.pm` -> `src/Network/Send/*` (server-type specific structures). +- Transport path: active mode (`DirectConnection`, `XKore`, `XKore2`, or `XKoreProxy`) in `src/Network/*.pm`. diff --git a/openkore_llm_knowledge/knowledge/10_xkore_modes.md b/openkore_llm_knowledge/knowledge/10_xkore_modes.md new file mode 100644 index 0000000000..4f0d773936 --- /dev/null +++ b/openkore_llm_knowledge/knowledge/10_xkore_modes.md @@ -0,0 +1,20 @@ +# XKore Modes + +XKore modes define how OpenKore connects to the game client/server pipeline. + +## Core modules +- `src/Network/XKore.pm` +- `src/Network/XKore2.pm` +- `src/Network/XKoreProxy.pm` +- `src/Network/DirectConnection.pm` (baseline comparison) + +## Mode summary +- **DirectConnection**: bot connects directly to game servers. +- **XKore**: client-assisted mode using forwarding/bridging logic. +- **XKore2**: OpenKore hosts local account/char/map proxy endpoints (`src/Network/XKore2/*`) and relays traffic. +- **XKoreProxy**: proxy-style mediation path for packet bridging. + +## Architectural impact +- All modes reuse packet parsing/dispatch and send modules (`src/Network/{Receive,Send}.pm`). +- Mode choice primarily changes transport/session orchestration and how packets are sourced/sinked. +- AI, tasks, actor state, plugins, and macro systems remain mode-agnostic above the network abstraction. diff --git a/openkore_llm_knowledge/knowledge/11_plugin_system.md b/openkore_llm_knowledge/knowledge/11_plugin_system.md new file mode 100644 index 0000000000..8e93284d60 --- /dev/null +++ b/openkore_llm_knowledge/knowledge/11_plugin_system.md @@ -0,0 +1,28 @@ +# Plugin System + +## Purpose +OpenKore plugins extend runtime behavior without editing core modules. + +## Core interfaces +- `src/Plugins.pm`: plugin lifecycle and hook dispatch. +- `src/Commands.pm`: command registration/execution surface used by plugins. +- `plugins/*`: plugin implementations (`*.pl` entrypoints + helper modules). + +## Lifecycle pattern +1. Register plugin with `Plugins::register(...)`. +2. Register hooks with `Plugins::addHooks(...)`. +3. Optionally register commands via `Commands::register(...)`. +4. On unload, remove hooks/commands and clear plugin state. + +## Common hook usage +- Startup hooks (e.g., startup/init phases) +- Loop hooks (periodic behavior) +- Packet hooks (`packet/*`, `packet_pre/*`) +- Config-change hooks (`configModify`) + +## Representative plugins +- `plugins/macro/macro.pl` +- `plugins/eventMacro/eventMacro.pl` +- `plugins/reconnect/reconnect.pl` +- `plugins/profiles/profiles.pl` +- `plugins/map/map.pl` diff --git a/openkore_llm_knowledge/knowledge/12_config_system.md b/openkore_llm_knowledge/knowledge/12_config_system.md new file mode 100644 index 0000000000..35dc6108d2 --- /dev/null +++ b/openkore_llm_knowledge/knowledge/12_config_system.md @@ -0,0 +1,19 @@ +# Configuration System + +## Purpose +Behavior policy is primarily configuration-driven via `control/` and table data under `tables/`. + +## Key files +- `control/config.txt`: global runtime/AI/combat/movement/network options. +- `control/items_control.txt`: item pickup/storage/sell policy. +- `control/mon_control.txt`: per-monster behavior policy. + +## Runtime integration +- `src/Settings.pm`: config path/file registration. +- `src/FileParsers.pm`: parser logic for control/table formats. +- `src/Globals.pm`: shared runtime state consumed by AI/tasks/plugins. + +## Debugging guidance +- Validate key names and value format first. +- Re-test with minimal config to isolate policy conflicts. +- Check plugin/macro automation that may override config expectations. diff --git a/openkore_llm_knowledge/knowledge/13_macro_system.md b/openkore_llm_knowledge/knowledge/13_macro_system.md new file mode 100644 index 0000000000..cba32b5d0c --- /dev/null +++ b/openkore_llm_knowledge/knowledge/13_macro_system.md @@ -0,0 +1,19 @@ +# Macro System + +## Components +- Macro plugin: `plugins/macro/macro.pl`, `plugins/macro/Macro/*` +- eventMacro plugin: `plugins/eventMacro/eventMacro.pl`, `plugins/eventMacro/eventMacro/*` + +## Configuration keys +- `macro_file` -> macro script path (commonly `macros.txt`). +- `eventMacro_file` -> eventMacro script path (commonly `eventMacros.txt`). + +## Execution model +1. Plugin loads and parses macro file. +2. Hooks/events trigger condition evaluation. +3. Matching automacro rules enqueue/execute actions. +4. Actions flow through command/task/network paths. + +## Operational notes +- Macro and eventMacro are complementary automation layers. +- Troubleshooting should separate: load -> parse -> trigger -> action execution. diff --git a/openkore_llm_knowledge/knowledge/14_table_reference.md b/openkore_llm_knowledge/knowledge/14_table_reference.md new file mode 100644 index 0000000000..a67d5688f1 --- /dev/null +++ b/openkore_llm_knowledge/knowledge/14_table_reference.md @@ -0,0 +1,25 @@ +# Table Reference + +## Purpose +`tables/` contains protocol and gameplay data used by network parsing, IDs, maps, and runtime lookups. + +## Core packet/profile tables +- `tables/servers.txt` +- `tables/packetlist.txt` +- `tables/packetdescriptions.txt` +- server-family `recvpackets.txt` files (e.g., `tables/kRO/recvpackets.txt`, `tables/iRO/recvpackets.txt`) + +## Gameplay identity tables +- `tables/SKILL_id_handle.txt` +- `tables/STATUS_id_handle.txt` +- `tables/statusnametable.txt` +- `tables/itemtypes.txt` +- `tables/elements.txt` + +## World/content tables +- `tables/portals.txt` +- `tables/skillsarea.txt` +- regional `maps.txt`, `items.txt`, `skillnametable.txt` + +## Notes +Packet-related incidents should always be validated against serverType + recvpackets alignment. diff --git a/openkore_llm_knowledge/knowledge/15_debugging_playbook.md b/openkore_llm_knowledge/knowledge/15_debugging_playbook.md new file mode 100644 index 0000000000..c826ebf7c4 --- /dev/null +++ b/openkore_llm_knowledge/knowledge/15_debugging_playbook.md @@ -0,0 +1,185 @@ +# Debugging Playbook + +This playbook maps common OpenKore runtime failures to likely causes, inspection points, and step-by-step troubleshooting. + +--- + +## 1) Bot not moving +- **Likely subsystem**: AI + Task + Routing + Network Send +- **Possible causes**: + - AI state blocked (attack/dialog/task lock) + - Route task never created or immediately failing + - Movement packets not being sent or rejected + - Movement-related config prevents walking +- **Files to inspect**: + - `src/AI.pm`, `src/AI/CoreLogic.pm` + - `src/TaskManager.pm`, `src/Task/Move.pm`, `src/Task/Route.pm` + - `src/Network/Send.pm`, `src/Network/Receive.pm` +- **Config to inspect**: + - `control/config.txt` (movement/lockMap/route-related options) + - `control/mon_control.txt` (behavior that can pin combat state) +- **Debugging steps**: + 1. Confirm AI is ticking and not paused/stuck in a higher-priority state. + 2. Check whether `Task::Route`/`Task::Move` is queued in task manager. + 3. Validate that move packets are emitted by send path. + 4. Confirm receive updates are acknowledging position changes. + 5. Reduce constraints in config (lock-only behavior, avoid lists) and retest. + +## 2) Routing failure +- **Likely subsystem**: Routing + Field data + Task +- **Possible causes**: + - Missing/incorrect field data + - Blocked cells/portal transitions not resolved + - Route recomputation loops without progress +- **Files to inspect**: + - `src/Task/Route.pm`, `src/Task/Move.pm`, `src/Field.pm` +- **Config to inspect**: + - `control/config.txt` (route/move flags) + - `fields/*` (map walkability data) +- **Debugging steps**: + 1. Verify map field exists and corresponds to current map. + 2. Check route generation result (empty path or oscillating path). + 3. Confirm position updates are fresh (no stale coordinates). + 4. Test shorter route segments to isolate failing region. + 5. Re-check portal/map transition assumptions. + +## 3) NPC interaction failure +- **Likely subsystem**: Task + Network Receive/Send + Actor/NPC state +- **Possible causes**: + - Wrong NPC coordinates or stale actor reference + - Dialogue step mismatch with server response + - Talk packets not sent or incorrect sequence +- **Files to inspect**: + - `src/Task/TalkNPC.pm` + - `src/Network/Send.pm`, `src/Network/Receive.pm`, `src/Network/Receive/*` + - `src/ActorList.pm` +- **Config to inspect**: + - `control/config.txt` (npc interaction directives/scripts) +- **Debugging steps**: + 1. Confirm NPC exists in actor list at expected coordinates. + 2. Trace talk packet send sequence against expected dialogue steps. + 3. Verify receive handlers parse response packet IDs correctly for server type. + 4. Reproduce with minimal NPC script/task to reduce branching. + +## 4) Plugin not loading +- **Likely subsystem**: Plugin system + startup/module loading +- **Possible causes**: + - Syntax/runtime error in plugin file + - Incorrect plugin path/name + - Missing dependency module used by plugin +- **Files to inspect**: + - `src/Plugins.pm`, `src/Modules.pm` + - `plugins/<plugin_name>/*.pl` +- **Config to inspect**: + - `control/config.txt` (plugin enable/load entries) +- **Debugging steps**: + 1. Check startup logs for plugin load exceptions. + 2. Validate plugin registers with `Plugins::register`. + 3. Confirm plugin path and filename match expected loader behavior. + 4. Remove optional dependency imports to isolate failing require/use. + +## 5) Plugin hook not firing +- **Likely subsystem**: Plugin hooks + event dispatch +- **Possible causes**: + - Hook registered with wrong event name + - Hook callback blocked by guard condition + - Expected event never emitted in current runtime path +- **Files to inspect**: + - `src/Plugins.pm`, `src/functions.pl` + - Plugin file defining `Plugins::addHooks` +- **Config to inspect**: + - `control/config.txt` (conditions controlling event generation) +- **Debugging steps**: + 1. List hooks registered by plugin after load. + 2. Add lightweight logging at callback entry. + 3. Verify event name spelling and payload assumptions. + 4. Trigger event manually through a minimal reproduction path. + +## 6) Macro not triggering +- **Likely subsystem**: Macro plugin + command/hook bridge +- **Possible causes**: + - Macro file not loaded + - Automacro condition never true + - Macro command blocked by runtime state +- **Files to inspect**: + - `plugins/macro/macro.pl`, `plugins/macro/Macro/*` + - `src/Commands.pm`, `src/Plugins.pm` +- **Config to inspect**: + - `control/config.txt` (`macro_file` and related keys) + - macro definition file referenced by config +- **Debugging steps**: + 1. Confirm macro plugin loaded and macro file parsed. + 2. Validate automacro conditions against live state. + 3. Execute macro manually to separate trigger vs action issues. + 4. Inspect command dispatch path for blocked/invalid command. + +## 7) eventMacro issues +- **Likely subsystem**: eventMacro plugin + event parser/runner +- **Possible causes**: + - Invalid eventMacro syntax + - Trigger conditions not matching event payload + - Runner stalled by previous action/wait state +- **Files to inspect**: + - `plugins/eventMacro/eventMacro.pl` + - `plugins/eventMacro/eventMacro/{Core,Runner,Automacro,FileParser}.pm` +- **Config to inspect**: + - `control/config.txt` (`eventMacro_file`) + - eventMacro script file +- **Debugging steps**: + 1. Validate eventMacro file parsing with minimal script. + 2. Log trigger evaluation for target automacro. + 3. Check runner queue and pending wait/time gates. + 4. Reduce script to one trigger + one action and retest. + +## 8) Packet desync +- **Likely subsystem**: Networking receive/parser + server-type mappings +- **Possible causes**: + - Wrong server type packet tables + - Opcode map mismatch after server update + - Packet boundary/tokenization errors +- **Files to inspect**: + - `src/Network/MessageTokenizer.pm`, `src/Network/PacketParser.pm` + - `src/Network/Receive.pm`, `src/Network/Receive/*` + - `src/Network/Send.pm`, `src/Network/Send/*` +- **Config to inspect**: + - `control/config.txt` (serverType/recvpackets settings) + - `tables/*` packet definition files for the target server +- **Debugging steps**: + 1. Confirm selected serverType and recvpackets data. + 2. Compare failing opcode decode against current server packet definitions. + 3. Capture raw packet sequence around first desync point. + 4. Verify both receive and send side use compatible packet maps. + +## 9) XKore sync issues +- **Likely subsystem**: XKore transport/session bridging +- **Possible causes**: + - Client-proxy handshake mismatch + - Account/char/map relay state divergence + - Timing/forwarding issues in XKore mode +- **Files to inspect**: + - `src/Network/XKore.pm`, `src/Network/XKore2.pm`, `src/Network/XKoreProxy.pm` + - `src/Network/XKore2/{AccountServer,CharServer,MapServer}.pm` +- **Config to inspect**: + - `control/config.txt` (XKore mode and connection settings) +- **Debugging steps**: + 1. Confirm active mode (Direct vs XKore/XKore2/XKoreProxy). + 2. Trace handshake/login flow across account->char->map stages. + 3. Check for asymmetric forwarding (client sees state that bot does not, or inverse). + 4. Validate mode-specific ports/bind settings and restart cleanly. + +## 10) Visual client state not updating +- **Likely subsystem**: XKore bridge + packet forwarding + actor sync +- **Possible causes**: + - Forwarded packets not reaching client + - Actor state updated in bot but not mirrored to client channel + - Client session stuck after map transition +- **Files to inspect**: + - `src/Network/XKore*.pm` + - `src/Network/Receive.pm`, `src/ActorList.pm` +- **Config to inspect**: + - `control/config.txt` (XKore forwarding/session config) +- **Debugging steps**: + 1. Verify bot-side state is changing (position/actors) first. + 2. Confirm corresponding packets are forwarded to client path. + 3. Re-check session phase (account/char/map) for stuck transition. + 4. Reconnect client through same mode to re-establish synchronization. diff --git a/openkore_llm_knowledge/knowledge/16_debug_decision_trees.md b/openkore_llm_knowledge/knowledge/16_debug_decision_trees.md new file mode 100644 index 0000000000..8869941f85 --- /dev/null +++ b/openkore_llm_knowledge/knowledge/16_debug_decision_trees.md @@ -0,0 +1,90 @@ +# Debug Decision Trees + +These trees provide fast triage paths for recurring OpenKore failures. + +## A) Bot not moving / Routing failure + +```mermaid +flowchart TD + A[Bot not moving] --> B{AI ticking?} + B -- No --> B1[Inspect src/AI.pm and main loop in src/functions.pl] + B -- Yes --> C{Task::Route or Task::Move queued?} + C -- No --> C1[Inspect AI decisions in src/AI/CoreLogic.pm and config gates] + C -- Yes --> D{Move packets sent?} + D -- No --> D1[Inspect src/Network/Send.pm and command/task path] + D -- Yes --> E{Position updates received?} + E -- No --> E1[Inspect src/Network/Receive.pm and packet mapping] + E -- Yes --> F{Path valid in fields data?} + F -- No --> F1[Inspect src/Field.pm + fields/*] + F -- Yes --> G[Check blockers/lock settings in control/config.txt] +``` + +Use this when movement appears frozen or route tasks fail repeatedly. + +## B) NPC interaction failure + +```mermaid +flowchart TD + A[NPC interaction fails] --> B{NPC present in ActorList?} + B -- No --> B1[Inspect src/ActorList.pm updates from receive handlers] + B -- Yes --> C{TalkNPC task created?} + C -- No --> C1[Inspect trigger path in AI/commands/plugins] + C -- Yes --> D{Talk packets emitted?} + D -- No --> D1[Inspect src/Task/TalkNPC.pm + src/Network/Send.pm] + D -- Yes --> E{Expected response packet decoded?} + E -- No --> E1[Inspect src/Network/Receive/* serverType handlers] + E -- Yes --> F[Validate dialogue sequence assumptions in task script] +``` + +Use this when NPC talks stop, loop, or complete with wrong branch. + +## C) Plugin not loading / Hook not firing + +```mermaid +flowchart TD + A[Plugin issue] --> B{Plugin loads successfully?} + B -- No --> B1[Check syntax/dependencies + src/Plugins.pm loader logs] + B -- Yes --> C{Hook registered via Plugins::addHooks?} + C -- No --> C1[Fix registration block in plugin file] + C -- Yes --> D{Event emitted at runtime?} + D -- No --> D1[Inspect event source in src/functions.pl / network paths] + D -- Yes --> E{Callback guard conditions pass?} + E -- No --> E1[Log callback inputs and config-dependent guards] + E -- Yes --> F[Inspect side effects via Commands/TaskManager] +``` + +Use this for both plugin boot failures and silent hooks. + +## D) Macro/eventMacro not triggering + +```mermaid +flowchart TD + A[Macro/eventMacro not triggering] --> B{Plugin loaded?} + B -- No --> B1[Inspect plugins/macro or plugins/eventMacro load path] + B -- Yes --> C{Script file parsed?} + C -- No --> C1[Validate macro_file/eventMacro_file in control/config.txt] + C -- Yes --> D{Trigger condition true in live state?} + D -- No --> D1[Log variables/events used by automacro] + D -- Yes --> E{Action executes manually?} + E -- No --> E1[Inspect command path in src/Commands.pm] + E -- Yes --> F[Investigate scheduler/wait constraints in runner] +``` + +Use this to isolate parser issues from trigger logic and action execution. + +## E) Packet desync / XKore sync / visual client desync + +```mermaid +flowchart TD + A[Desync observed] --> B{Wrong serverType/packet table?} + B -- Yes --> B1[Fix control/config.txt + tables/* packet mappings] + B -- No --> C{Receive decode errors?} + C -- Yes --> C1[Inspect MessageTokenizer/PacketParser/Receive handlers] + C -- No --> D{Only in XKore mode?} + D -- No --> D1[Inspect send/receive parity and opcode updates] + D -- Yes --> E{Client and bot state diverge?} + E -- Yes --> E1[Inspect src/Network/XKore*.pm forwarding/session state] + E -- No --> F[Inspect map-transition/session phase sync] +``` + +Use this for protocol mismatches and client-bridge synchronization drift. diff --git a/openkore_llm_knowledge/knowledge/17_code_recipes.md b/openkore_llm_knowledge/knowledge/17_code_recipes.md new file mode 100644 index 0000000000..ba7d8fa816 --- /dev/null +++ b/openkore_llm_knowledge/knowledge/17_code_recipes.md @@ -0,0 +1,147 @@ +# Code Recipes + +Practical snippets for common OpenKore development tasks. + +## 1) Minimal plugin skeleton +```perl +package myPlugin; + +use strict; +use Plugins; +use Log qw(message warning error); + +my $hooks = []; + +Plugins::register('myPlugin', 'Minimal plugin skeleton', \&on_unload); +$hooks = Plugins::addHooks( + ['start3', \&on_start], + ['AI_pre', \&on_ai_pre], +); + +sub on_start { + message "[myPlugin] started\n", 'info'; +} + +sub on_ai_pre { + my ($hookName, $args) = @_; + # lightweight periodic logic +} + +sub on_unload { + Plugins::delHooks($hooks); + message "[myPlugin] unloaded\n", 'info'; +} + +1; +``` + +## 2) Hook registration pattern +```perl +my $hooks = Plugins::addHooks( + ['packet_pre/actor_moved', \&on_actor_moved], + ['packet/map_changed', \&on_map_changed], + ['configModify', \&on_config_modify], +); + +sub on_actor_moved { + my ($hook, $args) = @_; + # inspect $args->{ID}, $args->{coords} +} +``` + +## 3) Command registration +```perl +Commands::register( + ['mycmd', 'run custom action', \&cmd_mycmd] +); + +sub cmd_mycmd { + my (undef, $args) = @_; + message "[myPlugin] mycmd args: $args\n", 'info'; +} + +# on unload: +Commands::unregister('mycmd'); +``` + +## 4) Packet hook example +```perl +my $hooks = Plugins::addHooks( + ['packet_pre/login_error', \&on_login_error], + ['packet/map_changed', \&on_map_changed], +); + +sub on_login_error { + my ($hook, $args) = @_; + warning "Login error packet received: $args->{type}\n"; +} +``` + +## 5) Macro example (`macros.txt`) +```txt +automacro autoHeal { + hp < 40% + timeout 1 + call { + do ss 28 + } +} + +macro goTown { + do move prontera +} +``` + +## 6) eventMacro example +```txt +automacro checkWeight { + InLockMap 1 + weight > 85% + call { + do conf itemsTakeAuto 0 + do ai clear + } +} + +automacro greetOnMap { + map prontera + run-once 1 + call { + do c Hello from OpenKore + } +} +``` + +## 7) Configuration examples (`control/config.txt`) +```txt +lockMap prontera +saveMap prontera +route_randomWalk 1 +teleportAuto_hp 25 +itemsTakeAuto 2 +attackAuto 2 +macro_file macros.txt +eventMacro_file eventMacros.txt +``` + +## 8) Debugging snippets +### 8.1 Print current map and coordinates +```perl +use Globals qw($field %char); +message sprintf("Map=%s x=%d y=%d\n", $field->baseName, $char{pos_to}{x}, $char{pos_to}{y}), 'debug'; +``` + +### 8.2 Trace command callback entry +```perl +sub cmd_mycmd { + my (undef, $args) = @_; + message "[TRACE] cmd_mycmd called with '$args'\n", 'debug'; +} +``` + +### 8.3 Check task queue state quickly +```perl +use TaskManager; +my $task = TaskManager::getTaskManager()->activeTask; +message "Active task: " . ($task ? ref($task) : 'none') . "\n", 'debug'; +``` diff --git a/openkore_llm_knowledge/knowledge/18_openkore_faq.md b/openkore_llm_knowledge/knowledge/18_openkore_faq.md new file mode 100644 index 0000000000..c3a6760545 --- /dev/null +++ b/openkore_llm_knowledge/knowledge/18_openkore_faq.md @@ -0,0 +1,147 @@ +# OpenKore Technical FAQ + +## Architecture +1. **Q:** What is OpenKore's main runtime entry point? + **A:** `src/functions.pl` initializes modules and runs the main loop. +2. **Q:** Where is global runtime state stored? + **A:** Mostly in `src/Globals.pm` and shared structures. +3. **Q:** Which module runs AI logic? + **A:** `src/AI.pm` coordinates AI and calls `src/AI/CoreLogic.pm`. +4. **Q:** How are game entities represented? + **A:** Through `src/Actor.pm` and `src/ActorList.pm`. +5. **Q:** What handles tasks? + **A:** `src/Task.pm` defines tasks and `src/TaskManager.pm` schedules them. +6. **Q:** Where does command parsing happen? + **A:** In `src/Commands.pm`. +7. **Q:** How are plugins managed? + **A:** Via `src/Plugins.pm` lifecycle and hooks API. +8. **Q:** Why is architecture loop-based? + **A:** To repeatedly process receive->state->AI->tasks->send cycles. + +## Debugging +9. **Q:** First check when bot freezes? + **A:** Confirm AI tick and active task state. +10. **Q:** How to distinguish route vs movement failure? + **A:** Check if a route exists, then confirm move packets and position updates. +11. **Q:** Why log packet IDs during failures? + **A:** To detect serverType/recvpackets mismatch quickly. +12. **Q:** Best way to debug plugin hooks? + **A:** Log both hook registration and callback entry. +13. **Q:** Why test with minimal config? + **A:** To remove config side effects masking root causes. +14. **Q:** What indicates desync risk? + **A:** Decode errors, unknown packets, or stale actor updates. +15. **Q:** How to verify task deadlock? + **A:** Inspect active task and queued tasks in task manager. +16. **Q:** Should I debug with all plugins enabled? + **A:** No, isolate with minimal plugin set first. + +## Plugins +17. **Q:** Minimum plugin requirements? + **A:** `Plugins::register`, optional hooks, and clean unload. +18. **Q:** How to add a custom command? + **A:** Use `Commands::register` and unregister on unload. +19. **Q:** Why might plugin load fail silently? + **A:** Early syntax/runtime errors or missing dependencies. +20. **Q:** Can plugins alter AI behavior? + **A:** Yes, via commands, hooks, and task interactions. +21. **Q:** Where are community plugins stored? + **A:** Under `plugins/`. +22. **Q:** How to avoid hook performance issues? + **A:** Keep callbacks lightweight and defer heavy work. +23. **Q:** Can multiple plugins share hook names? + **A:** Yes, each callback runs if registered correctly. +24. **Q:** Why unregister hooks on unload? + **A:** To prevent stale callbacks and inconsistent state. + +## Macros / eventMacro +25. **Q:** Difference between macro and eventMacro? + **A:** Macro is classic command script flow; eventMacro is event/condition-driven automation. +26. **Q:** Where to set macro file path? + **A:** `control/config.txt` via `macro_file`. +27. **Q:** Where to set eventMacro file path? + **A:** `control/config.txt` via `eventMacro_file`. +28. **Q:** Why automacro never triggers? + **A:** Condition false, plugin not loaded, or parse issue. +29. **Q:** How to test macro trigger quickly? + **A:** Manually run equivalent command and compare behavior. +30. **Q:** Why use run-once in eventMacro? + **A:** To avoid repeated firing for one-time actions. +31. **Q:** Can macros execute OpenKore commands? + **A:** Yes, via `do <command>`. +32. **Q:** How to debug eventMacro parser issues? + **A:** Reduce to one trigger + one action and rebuild incrementally. + +## Routing / movement +33. **Q:** Which files control routing logic? + **A:** `src/Task/Route.pm`, `src/Task/Move.pm`, and `src/Field.pm`. +34. **Q:** What is walkability? + **A:** Whether a map cell is traversable based on field data. +35. **Q:** Why does route loop occur? + **A:** Stale position updates, blocked transitions, or invalid field assumptions. +36. **Q:** What is lockMap effect? + **A:** Restricts behavior to a preferred map scope. +37. **Q:** How to validate map data? + **A:** Ensure map exists in `fields/*` and coordinates are valid. +38. **Q:** Why movement works manually but not AI? + **A:** AI priorities/config may override movement intents. +39. **Q:** Can aggressive combat stop movement? + **A:** Yes, combat states can continuously preempt route tasks. +40. **Q:** Best first movement test? + **A:** Short route on same map with minimal automation enabled. + +## Networking +41. **Q:** Which module tokenizes incoming packets? + **A:** `src/Network/MessageTokenizer.pm`. +42. **Q:** Which module decodes packet structures? + **A:** `src/Network/PacketParser.pm` and receive handlers. +43. **Q:** Where is receive dispatch done? + **A:** `src/Network/Receive.pm`. +44. **Q:** Where are server-specific packet handlers? + **A:** `src/Network/Receive/*` and `src/Network/Send/*`. +45. **Q:** Why packet errors after server patch? + **A:** Opcode/table changes requiring updated mappings. +46. **Q:** Can wrong serverType break everything? + **A:** Yes, it causes systematic decode/encode mismatch. +47. **Q:** What causes partial desync? + **A:** Some packets map correctly while others fail for changed opcodes. +48. **Q:** How to isolate receive vs send bug? + **A:** Compare inbound decode logs and outbound packet construction separately. + +## NPC interaction +49. **Q:** Which task handles NPC dialogues? + **A:** `src/Task/TalkNPC.pm`. +50. **Q:** Why NPC script stalls mid-dialogue? + **A:** Response packet mismatch or wrong expected step sequence. +51. **Q:** Must NPC be in actor list first? + **A:** Yes, reliable interaction depends on valid actor presence and position. +52. **Q:** Why wrong NPC gets targeted? + **A:** Incorrect coordinates or stale target selection. +53. **Q:** What to inspect first in NPC failure? + **A:** Talk packet sequence and corresponding receive responses. +54. **Q:** Can plugins interfere with NPC flow? + **A:** Yes, if they alter commands/tasks concurrently. + +## Config behavior +55. **Q:** Where are core behavior settings? + **A:** `control/config.txt`. +56. **Q:** What file controls monster behavior rules? + **A:** `control/mon_control.txt`. +57. **Q:** Why config changes seem ignored? + **A:** Reload needed, wrong key, or overridden by plugin automation. +58. **Q:** How to keep config deterministic? + **A:** Use minimal explicit settings and disable conflicting automation. +59. **Q:** What is saveMap used for? + **A:** Preferred return/safety map behavior. +60. **Q:** Why document config with code changes? + **A:** Behavior is policy-driven; docs prevent invisible config regressions. + +## XKore / client sync +61. **Q:** What is XKore2 role? + **A:** Local proxy/relay flow for account/char/map sessions. +62. **Q:** Why client view stale but bot state updates? + **A:** Forwarding/session sync issue in XKore bridge path. +63. **Q:** Is debugging Direct and XKore identical? + **A:** No, transport/session layers differ significantly. +64. **Q:** First step for XKore sync bugs? + **A:** Confirm active mode and trace handshake phase transitions. diff --git a/openkore_llm_knowledge/knowledge/19_glossary.md b/openkore_llm_knowledge/knowledge/19_glossary.md new file mode 100644 index 0000000000..abf1c04be5 --- /dev/null +++ b/openkore_llm_knowledge/knowledge/19_glossary.md @@ -0,0 +1,27 @@ +# Glossary + +- **Actor**: Runtime entity abstraction (player, monster, NPC, portal) managed via `Actor` modules. +- **AI queue**: Ordered AI intention/state processing that decides which action/task should run next. +- **Task**: Executable unit with lifecycle (start/process/finish), e.g., move, route, NPC interaction. +- **Plugin hook**: Callback registration point triggered by runtime events (`Plugins::addHooks`). +- **Command handler**: Function bound to a command name through `Commands::register`. +- **Packet handler**: Receive/send routine that decodes or encodes specific protocol packets. +- **Route**: Planned path between coordinates/maps, later executed stepwise by movement tasks. +- **Automacro**: Conditional automation rule that triggers scripted actions when conditions match. +- **eventMacro**: Event/condition-driven macro framework with parser, runner, and trigger model. +- **lockMap**: Configuration policy limiting movement/behavior to a designated map. +- **saveMap**: Preferred map used for return/recovery behavior. +- **Walkability**: Whether map cells are traversable according to field data and routing constraints. +- **Client sync**: Consistency between bot internal state and visual game client state (important in XKore). +- **Server packet**: Protocol message exchanged with the game server; parsed/encoded by network modules. +- **ServerType**: Packet profile selection controlling opcode and packet layout mapping. +- **Receive dispatch**: Mechanism mapping inbound opcode frames to concrete handler methods. +- **Send pipeline**: Path from action intent to serialized outbound packet. +- **XKore mode**: Bridged/proxy connection model variants (`XKore`, `XKore2`, `XKoreProxy`). +- **Main loop tick**: One full iteration of runtime processing in `functions.pl`. +- **Field data**: Map walkability/cell data used by route calculations. +- **Desync**: Divergence between expected protocol/state and observed runtime behavior. +- **Hook event**: Named runtime signal emitted for plugin subscribers. +- **Task manager**: Scheduler coordinating active/pending tasks and transitions. +- **Packet desync**: Failure mode where packet mappings no longer align with server behavior. +- **Control files**: Operator-facing config files under `control/` that drive runtime policy. diff --git a/openkore_llm_knowledge/knowledge/20_code_index.md b/openkore_llm_knowledge/knowledge/20_code_index.md new file mode 100644 index 0000000000..d0b179e3f4 --- /dev/null +++ b/openkore_llm_knowledge/knowledge/20_code_index.md @@ -0,0 +1,46 @@ +# Code Index (Architecture-Oriented) + +## Runtime entry and orchestration +- `src/functions.pl` — startup sequence and main loop +- `src/Modules.pm` — module loading registry +- `src/Globals.pm` — shared runtime state container + +## AI +- `src/AI.pm` — AI framework and queue/state management +- `src/AI/CoreLogic.pm` — core decision routines + +## Actor and world model +- `src/Actor.pm` — actor base model +- `src/ActorList.pm` — actor collections/indexing +- `src/Actor/*` — actor specializations +- `src/Field.pm` — map/field representation + +## Task execution +- `src/Task.pm` — task abstraction +- `src/TaskManager.pm` — scheduler/executor +- `src/Task/Route.pm` — route planning/execution wrapper +- `src/Task/Move.pm` — movement task +- `src/Task/TalkNPC.pm` — NPC dialogue workflow +- `src/Task/UseSkill.pm` — skill-use workflows + +## Networking and packets +- `src/Network.pm` — network facade and state +- `src/Network/DirectConnection.pm` — direct server mode +- `src/Network/XKore.pm`, `src/Network/XKore2.pm`, `src/Network/XKoreProxy.pm` — proxy/bridge modes +- `src/Network/MessageTokenizer.pm` — packet frame/token extraction +- `src/Network/PacketParser.pm` — packet definition parsing +- `src/Network/Receive.pm` + `src/Network/Receive/*` — inbound packet dispatch/handlers +- `src/Network/Send.pm` + `src/Network/Send/*` — outbound packet builders + +## Commands, plugins, and automation +- `src/Commands.pm` — command parser/dispatcher +- `src/Plugins.pm` — plugin lifecycle and hook API +- `plugins/macro/macro.pl` + `plugins/macro/Macro/*` — legacy macro automation +- `plugins/eventMacro/eventMacro.pl` + `plugins/eventMacro/eventMacro/*` — event-driven macro automation + +## Configuration and data sources +- `src/Settings.pm` — config path and file registration +- `src/FileParsers.pm` — config/table parsing +- `control/*` — user behavior policies and runtime options +- `tables/*` — server/data tables +- `fields/*` — map grids for routing/navigation diff --git a/openkore_llm_knowledge/knowledge/21_core_module_index.md b/openkore_llm_knowledge/knowledge/21_core_module_index.md new file mode 100644 index 0000000000..be33611294 --- /dev/null +++ b/openkore_llm_knowledge/knowledge/21_core_module_index.md @@ -0,0 +1,13 @@ +# Core Module Index + +- `src/functions.pl` — bootstrap and main loop. +- `src/Modules.pm` — module registration/loading. +- `src/Globals.pm` — shared runtime state. +- `src/Settings.pm` — config path and file registration. +- `src/FileParsers.pm` — control/table parsing. +- `src/Commands.pm` — command parser and handlers. +- `src/AI.pm` and `src/AI/CoreLogic.pm` — AI orchestration and decisions. +- `src/Actor.pm` and `src/ActorList.pm` — entity model and indexing. +- `src/Task.pm` and `src/TaskManager.pm` — task abstraction and scheduling. +- `src/Task/Route.pm`, `src/Task/Move.pm`, `src/Task/TalkNPC.pm` — routing, movement, NPC flows. +- `src/Network.pm`, `src/Network/PacketParser.pm`, `src/Network/Receive.pm`, `src/Network/Send.pm` — network core. diff --git a/openkore_llm_knowledge/knowledge/22_common_pitfalls.md b/openkore_llm_knowledge/knowledge/22_common_pitfalls.md new file mode 100644 index 0000000000..66dbbe6238 --- /dev/null +++ b/openkore_llm_knowledge/knowledge/22_common_pitfalls.md @@ -0,0 +1,49 @@ +# Common Pitfalls + +Concise checklist of high-frequency debugging traps in OpenKore architecture and operations. + +## 1) Assuming movement issues are only routing bugs +- AI state locks, command overrides, or task queue starvation can mimic routing failures. +- Check AI/task state before editing route logic. + +## 2) Ignoring serverType/packet table drift +- Packet desync often starts after server updates when recvpackets/opcodes change. +- Keep `control/config.txt` server settings aligned with `tables/*` packet definitions. + +## 3) Debugging plugin hooks without proving event emission +- A hook can be correct but never run if the event is not emitted in the current flow. +- Verify both registration and actual event source path. + +## 4) Treating macro trigger failures as parser failures +- Many cases are valid parse + false trigger conditions. +- Separate: plugin load -> file parse -> trigger true -> action execution. + +## 5) Overlooking config-side constraints +- `control/config.txt`, `mon_control.txt`, and related files can disable or redirect behavior. +- Always test with minimal, known-good config for reproduction. + +## 6) Mixing XKore and DirectConnection assumptions +- Transport/session behavior differs; debugging steps are mode-specific. +- Confirm active mode first before tracing packet/session paths. + +## 7) Trusting visual client state over bot internal state +- In XKore scenarios, client view may lag/diverge from bot state. +- Compare bot-side actor/position updates with forwarded client packets. + +## 8) Investigating deep modules before reproducing minimally +- Large automation stacks (AI + plugin + macro + eventMacro) hide root causes. +- Reproduce with minimal script/task/command to localize fault domain. + +## Fast isolation matrix +| Symptom | First subsystem to verify | Primary files | +|---|---|---| +| Bot not moving | AI/Task | `src/AI.pm`, `src/TaskManager.pm` | +| Route fails | Routing/Field | `src/Task/Route.pm`, `src/Field.pm` | +| NPC fails | Task + Receive | `src/Task/TalkNPC.pm`, `src/Network/Receive.pm` | +| Plugin not loading | Plugin loader | `src/Plugins.pm`, plugin file | +| Hook silent | Event dispatch | `src/Plugins.pm`, event source module | +| Macro not triggering | Macro pipeline | `plugins/macro/*`, `src/Commands.pm` | +| eventMacro issues | eventMacro runner | `plugins/eventMacro/eventMacro/*` | +| Packet desync | Packet mapping | `src/Network/PacketParser.pm`, `tables/*` | +| XKore sync issues | XKore bridge | `src/Network/XKore*.pm` | +| Client view stale | XKore forwarding | `src/Network/XKore*.pm`, `src/ActorList.pm` | diff --git a/openkore_llm_knowledge/knowledge/23_debugging_guide.md b/openkore_llm_knowledge/knowledge/23_debugging_guide.md new file mode 100644 index 0000000000..ea888e238c --- /dev/null +++ b/openkore_llm_knowledge/knowledge/23_debugging_guide.md @@ -0,0 +1,23 @@ +# Debugging Guide (Networking and Tables) + +## 1) Confirm server/profile alignment +- Validate `serverType` and packet profile selection in `control/config.txt`. +- Verify matching packet tables in `tables/*` for the selected server family. + +## 2) Trace receive path +- `src/Network/MessageTokenizer.pm` -> `src/Network/PacketParser.pm` -> `src/Network/Receive.pm` -> `src/Network/Receive/*` +- Look for first packet decode mismatch point. + +## 3) Trace send path +- Inspect command/task origin, then `src/Network/Send.pm` + `src/Network/Send/*` packet construction. +- Confirm opcode/structure expected by current server profile. + +## 4) XKore-specific checks +- Inspect `src/Network/XKore.pm`, `src/Network/XKore2.pm`, `src/Network/XKoreProxy.pm`. +- Validate session phase transitions and packet forwarding symmetry. + +## 5) Iterative workflow +1. Reproduce with minimal actions. +2. Isolate receive vs send failure. +3. Validate serverType/packet table pairing. +4. Re-test after one controlled change. diff --git a/openkore_llm_knowledge/knowledge/24_learning_path.md b/openkore_llm_knowledge/knowledge/24_learning_path.md new file mode 100644 index 0000000000..f9c988b0e1 --- /dev/null +++ b/openkore_llm_knowledge/knowledge/24_learning_path.md @@ -0,0 +1,49 @@ +# Learning Path for New OpenKore Developers + +## Stage 0: Orientation (Day 1) +- Read `01_llm_context.md`, `02_architecture_overview.md`, `03_system_architecture_map.md`. +- Goal: understand receive->state->AI->task->send loop and key directories. + +## Stage 1: Runtime core comprehension (Days 2-3) +- Study `src/functions.pl`, `src/Globals.pm`, `src/Modules.pm`. +- Track one full startup + one main-loop tick. +- Exercise: write a short note describing what runs each tick. + +## Stage 2: Network + actor model (Days 4-5) +- Study `src/Network.pm`, `src/Network/Receive.pm`, `src/Network/Send.pm`, `src/ActorList.pm`. +- Exercise: trace one incoming packet to a world-state mutation. +- Exercise: trace one outgoing action from command/task to send packet. + +## Stage 3: AI and task flow (Days 6-7) +- Study `src/AI.pm`, `src/AI/CoreLogic.pm`, `src/TaskManager.pm`, `src/Task/Route.pm`, `src/Task/TalkNPC.pm`. +- Exercise: map how AI chooses a task and how task completion feeds back. + +## Stage 4: Plugins and automation (Week 2) +- Study `src/Plugins.pm`, `src/Commands.pm`, `plugins/macro/*`, `plugins/eventMacro/*`. +- Build a minimal plugin with one hook and one command. +- Add one macro and one eventMacro automation rule. + +## Stage 5: Config-driven behavior (Week 2) +- Study `control/config.txt`, `control/mon_control.txt`, relevant table files. +- Exercise: change one behavior via config only and document impact. + +## Stage 6: Debugging proficiency (Week 3) +- Use `15_debugging_playbook.md`, `16_debug_decision_trees.md`, `22_common_pitfalls.md`. +- Reproduce and fix one issue in each category: + - movement/routing + - plugin/hook + - macro/eventMacro + - packet/XKore sync + +## Stage 7: Advanced protocol + XKore (Week 3+) +- Study `09_networking_packets.md`, `10_xkore_modes.md`. +- Compare DirectConnection vs XKore2 flow and identify mode-specific risks. + +## Suggested competency checklist +- [ ] Explain OpenKore runtime architecture from memory. +- [ ] Trace packet receive and send paths. +- [ ] Implement and unload a safe plugin. +- [ ] Diagnose macro and eventMacro trigger failures. +- [ ] Diagnose route and movement failures. +- [ ] Diagnose basic packet/serverType desync. +- [ ] Explain XKore sync failure patterns. diff --git a/openkore_llm_knowledge/knowledge/26_architecture_diagrams.md b/openkore_llm_knowledge/knowledge/26_architecture_diagrams.md new file mode 100644 index 0000000000..54654be8e2 --- /dev/null +++ b/openkore_llm_knowledge/knowledge/26_architecture_diagrams.md @@ -0,0 +1,17 @@ +# Architecture Diagrams (Mermaid) + +This file indexes the runtime diagrams for OpenKore subsystem interactions. + +## Included diagrams +1. **AI execution loop** — `06_execution_flows.md` +2. **Packet receive flow** — `09_networking_packets.md` +3. **NPC interaction flow** — `07_npc_interaction_flow.md` +4. **Plugin hook execution** — `27_system_flows.md` +5. **Routing and movement flow** — `08_routing_and_movement.md` +6. **Command execution flow** — `27_system_flows.md` + +## Reading guidance +- Start with `06_execution_flows.md` for the main runtime cycle. +- Use `09_networking_packets.md` and `10_xkore_modes.md` for transport/protocol context. +- Use `07_npc_interaction_flow.md` and `08_routing_and_movement.md` for task-level behavioral flows. +- Use `27_system_flows.md` for extension and control-plane flows. diff --git a/openkore_llm_knowledge/knowledge/27_system_flows.md b/openkore_llm_knowledge/knowledge/27_system_flows.md new file mode 100644 index 0000000000..61357e961e --- /dev/null +++ b/openkore_llm_knowledge/knowledge/27_system_flows.md @@ -0,0 +1,35 @@ +# System Flows + +## Plugin hook execution flow +Plugins extend runtime behavior through hook callbacks registered in `src/Plugins.pm` and commonly invoke `src/Commands.pm` or task APIs. + +```mermaid +flowchart TD + A[Plugin load\nplugins/*.pl] --> B[Plugins::register] + B --> C[Plugins::addHooks] + C --> D[Runtime event\nloop/packet/config] + D --> E[Plugin callback] + E --> F{Action needed?} + F -- No --> G[Return] + F -- Yes --> H[Execute command\nsrc/Commands.pm] + H --> I[Task/Network side effects] + I --> G +``` + +Hook callbacks are event-driven and should remain lightweight; heavy operations are usually delegated to commands/tasks. + +## Command execution flow + +```mermaid +flowchart TD + A[Input source\nconsole/plugin/macro] --> B[Commands::run] + B --> C[Command parser\nsrc/Commands.pm] + C --> D{Built-in or plugin cmd?} + D -- Built-in --> E[Built-in handler] + D -- Plugin --> F[Plugin command callback] + E --> G[Update state / queue task / send packet] + F --> G + G --> H[Feedback to interface/log] +``` + +Commands are a shared control surface across manual operations, plugins, and macro automation. diff --git a/openkore_llm_knowledge/knowledge/29_prompt_examples_for_users.md b/openkore_llm_knowledge/knowledge/29_prompt_examples_for_users.md new file mode 100644 index 0000000000..ba46ebe919 --- /dev/null +++ b/openkore_llm_knowledge/knowledge/29_prompt_examples_for_users.md @@ -0,0 +1,49 @@ +# Prompt Examples for Users + +Realistic prompts users can ask an OpenKore-focused GPT assistant. + +## Architecture and code navigation +1. "Explain how `src/functions.pl` orchestrates the OpenKore main loop." +2. "Map the dependency chain between AI, TaskManager, and Network::Send." +3. "Which modules should I read first to understand actor updates from packets?" +4. "Summarize how plugin hooks interact with command execution." + +## Debugging +5. "My bot stopped moving. Give me a step-by-step diagnosis plan with files to inspect." +6. "How do I distinguish packet desync from route calculation failure?" +7. "Generate a troubleshooting checklist for NPC dialogue failures." +8. "Why are my plugin hooks not firing even though plugin loads fine?" +9. "Help me debug eventMacro that parses but never triggers." +10. "Create a minimal reproduction plan for XKore client sync issues." + +## Plugins +11. "Create a minimal plugin skeleton with register, hooks, and unload cleanup." +12. "Add a custom command `farmstatus` that prints current map and task." +13. "Show best practices for lightweight hook callbacks in OpenKore plugins." +14. "How do I instrument a plugin with debug logs without spamming output?" + +## Macro / eventMacro +15. "Write an automacro that pauses looting above 85% weight." +16. "Create eventMacro rules to send a chat message only once when entering Prontera." +17. "Compare macro and eventMacro for reactive combat behavior." +18. "How can I test whether a macro trigger condition is actually true at runtime?" + +## Routing and movement +19. "Explain why route tasks can oscillate and how to debug it." +20. "Show a script strategy to test short movement segments before full routes." +21. "What configs can block movement even if route exists?" + +## Networking and packets +22. "How do I verify that serverType and recvpackets are correctly configured?" +23. "Trace one incoming movement packet from socket bytes to Actor update." +24. "What logs should I capture when packet decode errors start after a patch?" +25. "Explain where to add temporary logging for packet send construction." + +## Configuration +26. "Review my config goals and suggest a minimal stable `config.txt` baseline." +27. "Which `mon_control.txt` settings commonly interfere with movement tasks?" +28. "How do lockMap and saveMap influence AI behavior?" + +## Learning and onboarding +29. "Build me a 2-week learning plan to become productive in OpenKore development." +30. "Give me a study order for networking, AI, tasks, plugins, and macros with exercises." diff --git a/openkore_llm_knowledge/knowledge/30_dataset_summary.md b/openkore_llm_knowledge/knowledge/30_dataset_summary.md new file mode 100644 index 0000000000..15acba93bd --- /dev/null +++ b/openkore_llm_knowledge/knowledge/30_dataset_summary.md @@ -0,0 +1,37 @@ +# Dataset Summary + +## Purpose +This dataset provides an LLM-oriented knowledge layer for OpenKore architecture, runtime flow, debugging, and practical usage. + +## Coverage areas +- **Architecture core**: context, subsystem boundaries, dependency map, code index. +- **Runtime flows**: AI loop, packet receive flow, NPC interaction, routing/movement, command and plugin hook flows. +- **Networking**: packet pipeline and XKore mode behavior. +- **Debugging**: playbook, decision trees, and common pitfalls. +- **Practical usage**: code recipes, FAQ, glossary, learning path, and user prompt examples. + +## Main artifact groups +1. **Foundational architecture docs** (`01`-`05`, `11`-`14`, `20`, `21`) +2. **Flow/diagram docs** (`06`-`10`, `26`, `27`) +3. **Troubleshooting docs** (`15`, `16`, `22`, `23`) +4. **Developer enablement docs** (`17`, `18`, `19`, `24`, `29`, `30`) + +## Intended users +- Developers onboarding to OpenKore internals. +- Maintainers debugging runtime and packet issues. +- LLM assistants that need stable structural context for responses. + +## Strengths +- Grounded in likely module and directory references from the repository. +- Organized by numbered, task-oriented documents. +- Includes actionable diagnostics and reusable snippet patterns. + +## Known limits +- Documentation is descriptive and may lag upstream implementation changes. +- Server-specific packet behavior can change rapidly after game updates. +- Mermaid diagrams prioritize clarity over exhaustive edge-case detail. + +## Maintenance recommendations +- Re-validate packet/XKore docs after serverType or recvpackets updates. +- Update recipe snippets when plugin/hook APIs evolve. +- Keep FAQ and pitfalls synced with recurring issue reports. diff --git a/openkore_llm_knowledge/knowledge/architecture_overview.md b/openkore_llm_knowledge/knowledge/architecture_overview.md deleted file mode 100644 index 4114739c4e..0000000000 --- a/openkore_llm_knowledge/knowledge/architecture_overview.md +++ /dev/null @@ -1,15 +0,0 @@ -# OpenKore Core Architecture Overview - -This bundle captures only the **core architecture** from `src/` for public AI-assistant knowledge use. - -## Structural layers -- **Runtime orchestration**: `core/functions.pl` controls initialization stages and the recurring main loop. -- **State and configuration**: `core/Globals.pm`, `core/Settings.pm`, `core/FileParsers.pm`. -- **Behavior engine**: `core/AI.pm`, `core/AI/CoreLogic.pm`. -- **World model**: `core/Actor.pm`, `core/ActorList.pm`. -- **Task execution**: `core/Task.pm`, `core/TaskManager.pm`, `core/Task/{Route,Move,TalkNPC}.pm`. -- **Packet/network core**: `core/Network.pm`, `core/Network/{PacketParser,Receive}.pm`. -- **Command surface**: `core/Commands.pm`. - -## Why this is “core” -The selected modules define startup, state, decision logic, movement/NPC flows, and packet ingestion—the minimum architecture needed to explain how OpenKore runs end-to-end without pulling in plugin- or UI-heavy areas. diff --git a/openkore_llm_knowledge/knowledge/code_recipes.md b/openkore_llm_knowledge/knowledge/code_recipes.md deleted file mode 100644 index ac76a58024..0000000000 --- a/openkore_llm_knowledge/knowledge/code_recipes.md +++ /dev/null @@ -1,196 +0,0 @@ -# OpenKore Code Recipes - -Practical snippets for common OpenKore customization tasks. - ---- - -## 1) Plugin Development - -### A) Minimal plugin skeleton -```perl -package MyPlugin; - -use strict; -use Plugins; -use Log qw(message); - -my $hooks; -my $cmds; - -Plugins::register('MyPlugin', 'Minimal example plugin', \&on_unload); - -sub on_unload { - Plugins::delHooks($hooks) if $hooks; - Commands::unregister($cmds) if $cmds; - message "[MyPlugin] unloaded\n", 'success'; -} - -1; -``` - -### B) Registering hooks -```perl -use Plugins; -use Log qw(message); - -$hooks = Plugins::addHooks( - ['start3', sub { message "[MyPlugin] start3 fired\n", 'info'; }], - ['mainLoop_pre', sub { - # periodic logic - # keep this lightweight - }], - ['configModify', sub { - my (undef, $args) = @_; - message "[MyPlugin] config changed: $args->{key}\n", 'info'; - }], -); -``` - -### C) Handling packets (hook-based) -```perl -# Example: observe a packet-level hook exposed by core/plugins -$hooks = Plugins::addHooks( - ['packet_pre/map_changed', sub { - my (undef, $args) = @_; - # inspect packet arguments; avoid mutating unless needed - message "[MyPlugin] map changed detected\n", 'info'; - }], -); -``` - -### D) Adding console commands -```perl -use Commands; -use Log qw(message); - -$cmds = Commands::register([ - 'mycmd', - 'My custom command', - sub { - my (undef, $params) = @_; - $params //= ''; - message "[MyPlugin] mycmd called with: $params\n", 'list'; - } -]); -``` - ---- - -## 2) Macros (`macros.txt` style) - -### A) Teleport on HP threshold -```txt -automacro emergency_tp { - hp < 35% - timeout 1 - run-once 0 - call { - do conf teleportAuto_useSkill 1 - do conf teleportAuto_useChatCommand 0 - do sp 26 1 # Teleport level 1 (example) - } -} -``` - -### B) Simple farming loop -```txt -macro farm_loop { - do move prontera 150 180 - pause 1 - do ai auto - pause 10 - do move prontera 120 160 - pause 1 - do ai auto - pause 10 - do macro farm_loop -} - -automacro start_farming { - map prontera - timeout 3 - run-once 1 - call farm_loop -} -``` - -### C) NPC interaction macro -```txt -macro use_healer { - do talknpc 156 186 c r0 c c -} - -automacro low_hp_heal { - hp < 60% - map prontera - timeout 15 - run-once 0 - call use_healer -} -``` - ---- - -## 3) EventMacros (`eventMacros.txt` style) - -### A) Reacting to monster presence -```txt -automacro danger_near { - MonsterNear 1 - MonsterNearDist < 6 - call { - do conf attackAuto 0 - do sp 26 1 - } -} -``` - -### B) Map entry trigger -```txt -automacro entered_payon { - InMap payon - mapLoaded - call { - do conf attackAuto 2 - do conf route_randomWalk 1 - do ai auto - } -} -``` - ---- - -## 4) Configuration Recipes - -### A) Item pickup logic (`items_control.txt`) -```txt -# item name keep storage sell -"Red Potion" 30 0 1 -"Blue Potion" 20 0 1 -"Jellopy" 0 0 1 -"Empty Bottle" 0 0 0 -``` - -### B) Monster avoidance (`mon_control.txt`) -```txt -# monster attack teleport search -"Baphomet" -1 1 0 -"Hydra" 1 0 0 -"Pupa" 0 0 0 -``` - -### C) Route locking (`config.txt`) -```txt -lockMap prontera -lockMap_x 156 -lockMap_y 186 -attackAuto_inLockOnly 1 -route_randomWalk 0 -``` - ---- - -## Notes -- Packet IDs, skill IDs, and command behaviors may vary by server profile. -- Keep plugin `mainLoop_pre` handlers lightweight to avoid lag. -- Test macros/eventMacros in a safe map before production farming routes. diff --git a/openkore_llm_knowledge/knowledge/config_system.md b/openkore_llm_knowledge/knowledge/config_system.md deleted file mode 100644 index c81528729b..0000000000 --- a/openkore_llm_knowledge/knowledge/config_system.md +++ /dev/null @@ -1,27 +0,0 @@ -# Configuration System (Curated from `control/`) - -Configuration files in `control/` define runtime behavior and policies. This bundle copies the full `control/*.txt` set into `openkore_llm_knowledge/config/`. - -## `config.txt` -Primary behavior configuration file. It contains the main bot options (AI behavior, combat options, looting, movement, network/runtime preferences, plugin-related keys, etc.). - -Use `config.txt` to set high-level behavior and feature toggles. - -## `items_control.txt` -Item-level policy file controlling item handling rules. -Typical usage includes deciding whether items should be picked up, stored, sold, or ignored based on names/IDs and rule flags. - -Use `items_control.txt` for inventory and looting policy tuning. - -## `mon_control.txt` -Monster-level AI policy file. -It defines per-monster behavior such as attack mode, teleport/disconnect response, and conditional thresholds. - -Use `mon_control.txt` to tune combat risk handling and target strategy. - -## Practical model -- `config.txt` = global behavior defaults -- `items_control.txt` = item policy overrides -- `mon_control.txt` = monster policy overrides - -Together, these provide most day-to-day operational control without changing source modules. diff --git a/openkore_llm_knowledge/knowledge/core_module_index.md b/openkore_llm_knowledge/knowledge/core_module_index.md deleted file mode 100644 index 0c04d08687..0000000000 --- a/openkore_llm_knowledge/knowledge/core_module_index.md +++ /dev/null @@ -1,20 +0,0 @@ -# Core Module Index - -- `core/functions.pl` — startup states, initialization sequence, and main loop transitions. -- `core/Modules.pm` — module registration and reload mechanics. -- `core/Globals.pm` — shared globals and runtime containers. -- `core/Settings.pm` — path/file registration and load policies. -- `core/FileParsers.pm` — parsers for core config/table content. -- `core/Commands.pm` — runtime command entrypoints. -- `core/AI.pm` — AI framework control. -- `core/AI/CoreLogic.pm` — primary decision-cycle logic. -- `core/Actor.pm` — actor base model. -- `core/ActorList.pm` — actor indexing/lookup list. -- `core/Task.pm` — task primitive. -- `core/TaskManager.pm` — task scheduler/runner. -- `core/Task/Route.pm` — route planning task. -- `core/Task/Move.pm` — movement task execution. -- `core/Task/TalkNPC.pm` — NPC conversation task. -- `core/Network.pm` — network state machine abstraction. -- `core/Network/PacketParser.pm` — packet decode/shape logic. -- `core/Network/Receive.pm` — incoming packet dispatch and handlers. diff --git a/openkore_llm_knowledge/knowledge/core_subsystems.md b/openkore_llm_knowledge/knowledge/core_subsystems.md deleted file mode 100644 index 2be26062db..0000000000 --- a/openkore_llm_knowledge/knowledge/core_subsystems.md +++ /dev/null @@ -1,27 +0,0 @@ -# Core Subsystems - -## AI -- `core/AI.pm` hosts AI stack control and high-level bot behavior gates. -- `core/AI/CoreLogic.pm` contains central decision logic used every loop tick. - -## Actor -- `core/Actor.pm` defines the base actor abstraction. -- `core/ActorList.pm` manages indexed actor collections and lookups. - -## Network -- `core/Network.pm` defines connection state abstraction. -- `core/Network/PacketParser.pm` performs packet-level extraction/reconstruction logic. -- `core/Network/Receive.pm` maps and dispatches incoming packet handlers. - -## Tasks -- `core/Task.pm` is the task base abstraction. -- `core/TaskManager.pm` queues/runs tasks. -- `core/Task/Route.pm` and `core/Task/Move.pm` handle routing and movement execution. -- `core/Task/TalkNPC.pm` encapsulates NPC interaction task flow. - -## Commands -- `core/Commands.pm` provides command parsing/dispatch used by console and automation. - -## File parsing -- `core/Settings.pm` registers and resolves data/control files. -- `core/FileParsers.pm` parses key config/table formats into runtime structures. diff --git a/openkore_llm_knowledge/knowledge/debugging_guide.md b/openkore_llm_knowledge/knowledge/debugging_guide.md deleted file mode 100644 index 25bc17294c..0000000000 --- a/openkore_llm_knowledge/knowledge/debugging_guide.md +++ /dev/null @@ -1,27 +0,0 @@ -# Debugging Guide (Networking & Tables) - -## 1) Confirm server/table alignment -- Verify the active server profile in `tables/servers.txt`. -- Confirm matching `recvpackets.txt` set (e.g., kRO vs iRO). -- Packet mismatch symptoms: unknown switch warnings, broken map/actor parsing, failed actions. - -## 2) Trace receive path -- Start from `networking/Receive.pm` dispatch behavior. -- Compare overridden handlers in server-specific receive modules (`ServerType0`, `kRO`, `iRO`). -- Use packet references (`tables/packetlist.txt`, `tables/packetdescriptions.txt`) to identify suspect opcodes. - -## 3) Trace send path -- Inspect `networking/Send.pm` and server-specific send modules. -- Validate whether a command/action is encoded in the expected packet version/format. - -## 4) XKore-specific troubleshooting -- For bridge issues, inspect `networking/XKore.pm` lifecycle and channel forwarding. -- For proxy/session issues, inspect `networking/XKore2.pm` and `Network/XKore2/{AccountServer,CharServer,MapServer}.pm`. -- Ensure client/server stage transitions match expected state. - -## 5) Practical iterative workflow -1. Reproduce issue with minimal actions. -2. Isolate receive vs send failure. -3. Validate server profile + recvpackets table pairing. -4. Check server-specific module overrides. -5. Re-test and document exact packet/table combination that works. diff --git a/openkore_llm_knowledge/knowledge/debugging_playbook.md b/openkore_llm_knowledge/knowledge/debugging_playbook.md deleted file mode 100644 index 9eadc082ea..0000000000 --- a/openkore_llm_knowledge/knowledge/debugging_playbook.md +++ /dev/null @@ -1,244 +0,0 @@ -# OpenKore Debugging Playbook - -This playbook provides structured troubleshooting for common OpenKore runtime issues. - ---- - -## 1) Bot Not Moving - -### Possible causes -- Movement/task pipeline blocked (AI state, mutex, sit/stand state, map lock logic). -- Invalid route target or unreachable coordinates. -- Network-side movement packets not being sent/accepted. -- Configuration constraints preventing movement. - -### Files to inspect -- Core runtime/task modules: - - `core/AI/CoreLogic.pm` - - `core/TaskManager.pm` - - `core/Task/Route.pm` - - `core/Task/Move.pm` -- Movement/network path: - - `networking/Send.pm` - - `networking/Network/Send/ServerType0.pm` (or active server-specific send module) -- Config: - - `config/config.txt` - - `config/timeouts.txt` - -### Configuration checks -- Verify movement/route-related settings in `config.txt` (lock map, teleport fallback, attack-route behavior). -- Confirm no conflicting delay/timeouts in `timeouts.txt`. -- Confirm map/server profile alignment (server selection and table set). - -### Debugging steps -1. Confirm AI is active and not paused/stuck in an unexpected state. -2. Inspect active tasks (route/move) and whether they are advancing or looping. -3. Check if movement packets are being emitted by send logic. -4. Validate map data/path constraints from route calculations. -5. Re-test with a simple direct move target; compare behavior. - ---- - -## 2) NPC Interaction Issues - -### Possible causes -- NPC not found in actor list or wrong target coordinates. -- Dialogue state machine mismatch (unexpected prompt/response sequence). -- Packet handler mismatch for NPC-related responses. -- Route-to-NPC task never reaches interaction range. - -### Files to inspect -- Task and command flow: - - `core/Task/TalkNPC.pm` - - `core/Commands.pm` - - `core/AI/CoreLogic.pm` -- Receive handlers: - - `networking/Receive.pm` - - server-specific receive module (`networking/Network/Receive/*.pm`) -- Tables/config: - - `tables/portals.txt` (if interaction depends on map pathing) - - `config/config.txt` - -### Configuration checks -- Ensure NPC scripts/macros (if used) match actual in-game dialogue sequence. -- Confirm route/lock-map settings still allow navigation to NPC. -- Validate server packet profile (`recvpackets`) is current. - -### Debugging steps -1. Verify NPC is visible/tracked in actor context. -2. Step through `TalkNPC` task progression (init, talk, select, complete). -3. Check receive handlers for expected NPC dialogue packets. -4. Validate command/script parameters (NPC name/id, sequence values). -5. Retry with a minimal NPC interaction command path. - ---- - -## 3) Plugin Not Loading - -### Possible causes -- Plugin disabled by load policy. -- Plugin name not listed when selective loading is enabled. -- Missing dependencies/imports in plugin code. -- Startup callback failure throws during register/load. - -### Files to inspect -- Plugin architecture docs/code: - - `knowledge/plugin_system.md` - - `plugins/<plugin>/<plugin>.pl` -- Config controls: - - `config/sys.txt` -- (Core plugin loader reference in source repo: `src/Plugins.pm`, `src/functions.pl`) - -### Configuration checks -- In `sys.txt`, verify: - - `loadPlugins` mode - - `loadPlugins_list` / `skipPlugins_list` -- Confirm plugin filename matches expected plugin name. - -### Debugging steps -1. Check startup logs for plugin load exception details. -2. Validate plugin register/unload function signatures. -3. Verify required modules are available and import paths are correct. -4. If selective loading, explicitly include plugin in list and restart. -5. Test plugin command availability after successful load. - ---- - -## 4) Macro Not Triggering - -### Possible causes -- Macro/eventMacro plugin not loaded. -- Macro file path key incorrect (`macro_file` / `eventMacro_file`). -- Condition never becomes true (state/event mismatch). -- Automacro disabled, timed out, or blocked by check mode. - -### Files to inspect -- Macro system docs/code: - - `knowledge/macro_system.md` - - `plugins/macro/macro.pl` - - `plugins/eventMacro/eventMacro.pl` - - `plugins/eventMacro/eventMacro/*` -- Config: - - `config/config.txt` - - `config/sys.txt` - -### Configuration checks -- Confirm macro plugin is in plugin load list. -- Validate macro file names/paths (`macros.txt` / `eventMacros.txt` equivalents). -- Check macro/eventMacro options (orphans/check-on-AI/enable states). - -### Debugging steps -1. Confirm plugin loaded and command is available (`macro`, `eventMacro`, `emacro`). -2. Reparse/reload macro file and verify no parse errors. -3. Use status/list commands to inspect macro/automacro states. -4. Verify trigger condition using simple known event first. -5. Incrementally add conditions until failing condition is isolated. - ---- - -## 5) Routing Problems - -### Possible causes -- Pathfinding cannot compute a valid route. -- Map data mismatch or stale map/portal table information. -- Dynamic obstacles or combat interruptions constantly reset route. -- Route task repeatedly interrupted by higher-priority tasks. - -### Files to inspect -- Routing logic: - - `core/Task/Route.pm` - - `core/Task/Move.pm` - - `core/TaskManager.pm` -- Table references: - - `tables/portals.txt` - - profile map tables (`tables/kRO/maps.txt`, `tables/iRO/maps.txt`) -- Config: - - `config/config.txt` - -### Configuration checks -- Confirm map/portal table set matches target server profile. -- Check route-related config constraints in `config.txt`. -- Verify no over-restrictive lockMap/avoid settings. - -### Debugging steps -1. Test short route on same map first. -2. Inspect route task generated path length/waypoints. -3. Check for repeated interruptions/timeouts. -4. Validate map transition assumptions (portal routes). -5. Re-test after narrowing to minimal route scenario. - ---- - -## 6) Packet Desync - -### Possible causes -- Wrong `recvpackets` profile for active server/client build. -- Server-type receive/send module mismatch. -- Packet format changed on server side. -- Corrupted/incomplete packet framing in tokenizer path. - -### Files to inspect -- Packet docs/code: - - `knowledge/networking_packets.md` - - `networking/PacketParser.pm` - - `networking/MessageTokenizer.pm` - - `networking/Receive.pm` - - `networking/Send.pm` -- Tables: - - `tables/servers.txt` - - `tables/*/recvpackets.txt` - - `tables/packetlist.txt` - - `tables/packetdescriptions.txt` - -### Configuration checks -- Verify selected server profile points to correct packet map. -- Ensure regional family tables (kRO/iRO/etc.) are aligned with server. - -### Debugging steps -1. Identify first unknown/broken packet switch in logs. -2. Match switch against packet list/description tables. -3. Confirm recvpackets version/profile used at runtime. -4. Compare affected switch handling in server-specific receive/send modules. -5. Re-validate behavior after profile/module adjustment. - ---- - -## 7) XKore Issues - -### Possible causes -- Port/listener conflicts. -- Wrong XKore mode assumptions (mode 1 bridge vs mode 2 proxy stack). -- Session/state transition mismatch between account/char/map stages. -- Client forwarding path blocked or malformed. - -### Files to inspect -- XKore docs/code: - - `knowledge/xkore_modes.md` - - `networking/XKore.pm` - - `networking/XKore2.pm` - - `networking/XKoreProxy.pm` - - `networking/Network/XKore2/{AccountServer,CharServer,MapServer}.pm` -- Config: - - `config/config.txt` - - `config/poseidon.txt` (if related environment integration is used) - -### Configuration checks -- Confirm XKore mode and listen/public IP/port values. -- Validate no duplicate process is already binding required ports. -- Ensure server-type settings are propagated correctly in XKore2 mode. - -### Debugging steps -1. Start with a single XKore mode and minimal config. -2. Verify listener startup and handshake progression. -3. Check state transitions (account -> char -> map). -4. Confirm packet forwarding in both directions. -5. If mode 2 fails, isolate failing stage server (Account/Char/Map). - ---- - -## General Triage Order (Recommended) -1. Confirm plugin/config/table profile alignment. -2. Isolate whether issue is logic-layer (AI/Task) or protocol-layer (packet/network). -3. Reproduce with minimal commands/actions. -4. Inspect module-specific path for first divergence. -5. Apply smallest fix/reconfiguration and re-test. diff --git a/openkore_llm_knowledge/knowledge/execution_flows.md b/openkore_llm_knowledge/knowledge/execution_flows.md deleted file mode 100644 index 95982978b1..0000000000 --- a/openkore_llm_knowledge/knowledge/execution_flows.md +++ /dev/null @@ -1,26 +0,0 @@ -# Execution Flows - -## AI decision loop -1. `core/functions.pl` runs the main loop and enters initialized state. -2. AI stack calls into `core/AI.pm` and `core/AI/CoreLogic.pm`. -3. Decisions enqueue/update tasks via `core/TaskManager.pm`. - -## NPC interaction flow -1. AI/commands request NPC interaction. -2. `core/Task/TalkNPC.pm` drives conversation steps. -3. Packet responses are consumed via `core/Network/Receive.pm` handlers and reflected back into task state. - -## Packet receive flow -1. Raw network data is managed through `core/Network.pm` runtime state. -2. `core/Network/PacketParser.pm` resolves packet framing and decode shape. -3. `core/Network/Receive.pm` dispatches switch-specific logic updating globals/actors/tasks. - -## Routing and movement flow -1. AI or commands schedule movement-related tasks. -2. `core/Task/Route.pm` computes route-level progression. -3. `core/Task/Move.pm` executes movement steps and reacts to packet/position updates. - -## Actor state updates -1. Incoming packets are decoded and mapped in `core/Network/Receive.pm`. -2. Actor entities from `core/Actor.pm` are created/updated. -3. Collections in `core/ActorList.pm` are synchronized and used by AI/task decisions. diff --git a/openkore_llm_knowledge/knowledge/macro_system.md b/openkore_llm_knowledge/knowledge/macro_system.md deleted file mode 100644 index e1bc339631..0000000000 --- a/openkore_llm_knowledge/knowledge/macro_system.md +++ /dev/null @@ -1,31 +0,0 @@ -# Macro System - -## `macros.txt` -The macro plugin (`plugins/macro/macro.pl`) loads a macro file from config key `macro_file`, defaulting to `macros.txt`. - -What it provides: -- macro definitions (named scripts/blocks) -- automacro triggers -- runtime command control via `macro` command - -The plugin reparses the configured macro file when `macro_file` changes and hooks runtime events according to defined automacro conditions. - -## `eventMacros.txt` -The eventMacro plugin (`plugins/eventMacro/eventMacro.pl`) loads from config key `eventMacro_file`, defaulting to `eventMacros.txt`. - -What it provides: -- event-driven macro model -- condition-based automacro activation -- runtime control via `eventMacro` / `emacro` commands - -If no event macro file is present, the plugin disables itself in startup logic. - -## Automacro behavior (conceptual) -Across both macro systems, automacros generally follow this pattern: -1. Parse macro file and build macro/automacro structures. -2. Subscribe to relevant hooks/events. -3. Evaluate conditions (state/event/regex/numeric checks). -4. Queue or execute macro actions when conditions are satisfied. -5. Respect timeout/run-once/enable-disable states and user commands. - -This makes macro automation configurable from text files rather than source edits. diff --git a/openkore_llm_knowledge/knowledge/module_dependency_map.md b/openkore_llm_knowledge/knowledge/module_dependency_map.md deleted file mode 100644 index d5d9e118fa..0000000000 --- a/openkore_llm_knowledge/knowledge/module_dependency_map.md +++ /dev/null @@ -1,122 +0,0 @@ -# Module Dependency Map (Core-Curated View) - -This dependency map is derived from `openkore_llm_knowledge/core/` and focuses on AI, Actor, Network, Commands, Task, Routing, and File Parsing relationships. - -## Major Module Relationships - -### 1) `functions.pl` -- **Purpose:** Runtime bootstrap loop and subsystem orchestration. -- **Key dependencies:** `AI`, `Commands`, `Network::Receive`, `TaskManager`, `Settings`. -- **Depended on by:** Runtime entry flow (top-level bootstrap path). - -### 2) `AI` -- **Purpose:** AI mode/state management and behavior control surface. -- **Key dependencies:** `Globals`, `Utils`, `Log`, `Field`. -- **Depended on by:** `AI::CoreLogic`, `Commands`, `Network::Receive`, task modules. - -### 3) `AI::CoreLogic` -- **Purpose:** Main decision engine for combat/behavior execution. -- **Key dependencies:** `AI`, `Globals`, `Misc`, `Network::Send`, `Commands`, `FileParsers`, `Task::TalkNPC`. -- **Depended on by:** `functions.pl` runtime loop and AI-driven task flow. - -### 4) `Actor` -- **Purpose:** Base actor model (player, mob, NPC entity behavior/state object). -- **Key dependencies:** `Globals`, `Utils`, `Task`, `Misc`, `Translation`. -- **Depended on by:** `ActorList`, `Network::Receive`, `AI`, routing/task logic. - -### 5) `ActorList` -- **Purpose:** Indexed actor container and type-aware lookup. -- **Key dependencies:** `Actor` + actor subclasses. -- **Depended on by:** `functions.pl`, `Task::TalkNPC` and other actor-consumers. - -### 6) `Commands` -- **Purpose:** Operator and automation command dispatch. -- **Key dependencies:** `Globals`, `Network`, `Network::Send`, `Settings`, `AI`, `Task`. -- **Depended on by:** `AI::CoreLogic`, `Task::TalkNPC`, packet/automation callbacks. - -### 7) `FileParsers` -- **Purpose:** Parse control/table data into runtime structures. -- **Key dependencies:** `Settings`, `Plugins`, `Utils`, `Log`. -- **Depended on by:** `Settings`, `AI::CoreLogic`, `Network::Receive`, `Commands`. - -### 8) `Settings` -- **Purpose:** Configuration path resolution, file registration, and argument parsing. -- **Key dependencies:** `Globals`, `Utils::ObjectList`, `Translation`, `Modules`. -- **Depended on by:** `functions.pl`, `Commands`, `FileParsers`, AI/network modules. - -### 9) `Globals` -- **Purpose:** Shared runtime state and exported global structures. -- **Key dependencies:** `Modules`. -- **Depended on by:** Nearly all core subsystems (`AI`, `Commands`, `Network::*`, `Task::*`). - -### 10) `Network` -- **Purpose:** Connection state abstraction and shared network constants. -- **Key dependencies:** `Modules`. -- **Depended on by:** `Commands`, `Network::PacketParser`, `Network::Receive`, `Task::*`. - -### 11) `Network::PacketParser` -- **Purpose:** Packet data parsing/reconstruction and packet utility constants. -- **Key dependencies:** `Globals`, `Network`, `Network::MessageTokenizer`, `Plugins`, `Utils`. -- **Depended on by:** `Network::Receive`, `Commands`. - -### 12) `Network::Receive` -- **Purpose:** Incoming packet dispatch and handler execution. -- **Key dependencies:** `Network::PacketParser`, `AI`, `Globals`, `FileParsers`, `Plugins`, `Skill`, `Actor::Slave::*`. -- **Depended on by:** Core runtime/loop flows and state update paths. - -### 13) `Task` -- **Purpose:** Base task abstraction (lifecycle/priority/state). -- **Key dependencies:** `Modules`, `Utils::CallbackList`, `Utils::Set`. -- **Depended on by:** `TaskManager`, task subclasses, actor/AI command flows. - -### 14) `TaskManager` -- **Purpose:** Task scheduling/execution container. -- **Key dependencies:** `Task`, `Utils::Set`, `Utils::CallbackList`. -- **Depended on by:** `functions.pl` orchestration loop and AI/task producers. - -### 15) `Task::Route` -- **Purpose:** Route planning and navigation workflow control. -- **Key dependencies:** `Task::Move`, `AI`, `Network`, `Field`, `Utils::PathFinding`. -- **Depended on by:** `AI::CoreLogic` and movement decision flow. - -### 16) `Task::Move` -- **Purpose:** Movement execution subtask and movement-state progression. -- **Key dependencies:** `Task::WithSubtask`, `Task::SitStand`, `Network`, `Plugins`, `Globals`. -- **Depended on by:** `Task::Route` and route execution chains. - -### 17) `Task::TalkNPC` -- **Purpose:** NPC conversation/state-machine interactions. -- **Key dependencies:** `Task`, `AI`, `Commands`, `Network`, `Misc`, `Plugins`. -- **Depended on by:** `AI::CoreLogic` and command-triggered NPC workflows. - -### 18) `Modules` -- **Purpose:** Core module registration/reload helper used across subsystems. -- **Key dependencies:** Perl runtime utilities (`Config`, `FindBin`, `File::Spec`). -- **Depended on by:** `Globals`, `Settings`, `Network`, `Task`, `Commands` and others. - ---- - -## Top 20 Most Important Modules (Architecture Perspective) - -1. `functions.pl` -2. `AI` -3. `AI::CoreLogic` -4. `Actor` -5. `ActorList` -6. `Task` -7. `TaskManager` -8. `Task::Route` -9. `Task::Move` -10. `Task::TalkNPC` -11. `Commands` -12. `Network` -13. `Network::PacketParser` -14. `Network::Receive` -15. `Settings` -16. `FileParsers` -17. `Globals` -18. `Modules` -19. `Network::Send` (critical dependency referenced by core decision/command paths) -20. `Misc` (high-touch utility dependency in AI/task/command/network logic) - -> Note: Items 1–18 are present in the curated `core/` set; 19–20 are explicitly identified from core dependency references to preserve architectural completeness. diff --git a/openkore_llm_knowledge/knowledge/networking_packets.md b/openkore_llm_knowledge/knowledge/networking_packets.md deleted file mode 100644 index 03573d8adb..0000000000 --- a/openkore_llm_knowledge/knowledge/networking_packets.md +++ /dev/null @@ -1,27 +0,0 @@ -# Networking & Packets - -## Packet handling architecture -OpenKore networking is structured around base transport/state modules plus packet parser/dispatcher modules: -- `networking/Network.pm` and `networking/DirectConnection.pm` for connection state and transport. -- `networking/PacketParser.pm` and `networking/MessageTokenizer.pm` for framing/tokenization and packet reconstruction support. -- `networking/Receive.pm` for incoming packet dispatch. -- `networking/Send.pm` for outgoing packet construction. - -## Receive flow (high level) -1. Bytes arrive from the active network backend. -2. Tokenizer/parser identifies packet boundaries and switch IDs. -3. `Receive.pm` maps switches to handlers. -4. Server-type subclasses (for example `Network/Receive/ServerType0.pm`, `Network/Receive/kRO.pm`, `Network/Receive/iRO.pm`) override or extend behavior. -5. Handlers update runtime state (actors, inventory, map/chat/combat events). - -## Send flow (high level) -1. Runtime logic requests an action (move, skill, talk, item ops). -2. `Send.pm` builds base packet payloads. -3. Server-type send classes (for example `Network/Send/ServerType0.pm`, `kRO.pm`, `iRO.pm`) apply server-specific packet formats. -4. Transport layer sends encoded bytes to the game server/proxy endpoint. - -## Table coupling -Packet behavior is tightly coupled to table data: -- `tables/servers.txt` selects server profile/type. -- `tables/*/recvpackets.txt` maps opcode formats by server build/family. -- `tables/packetlist.txt` and `tables/packetdescriptions.txt` assist packet reference/debug workflows. diff --git a/openkore_llm_knowledge/knowledge/openkore_faq.md b/openkore_llm_knowledge/knowledge/openkore_faq.md deleted file mode 100644 index f7ed51a53b..0000000000 --- a/openkore_llm_knowledge/knowledge/openkore_faq.md +++ /dev/null @@ -1,208 +0,0 @@ -# OpenKore Developer FAQ - -This FAQ is based on the curated OpenKore knowledge bundle and references core, plugin, networking, config, and table modules. - -## 1) How do I write a plugin? -Start with a Perl file that calls `Plugins::register`, adds hooks via `Plugins::addHooks`, and optionally registers commands via `Commands::register`. -Reference: `knowledge/code_recipes.md`, `plugins/macro/macro.pl`, `plugins/eventMacro/eventMacro.pl`. - -## 2) What is the smallest plugin skeleton? -A minimal plugin includes `package`, `use Plugins`, `Plugins::register(...)`, optional hook/command setup, and unload cleanup. -Reference: `knowledge/code_recipes.md`. - -## 3) How do I register hooks in a plugin? -Use `Plugins::addHooks` with event/callback pairs such as `start3`, `mainLoop_pre`, and `configModify`. -Reference: `knowledge/plugin_system.md`, `plugins/macro/macro.pl`. - -## 4) How do I add a custom console command? -Use `Commands::register` and provide a callback receiving command parameters. -Reference: `knowledge/code_recipes.md`, `plugins/eventMacro/eventMacro.pl`. - -## 5) How do plugins clean up on unload? -Implement unload callback to remove hooks (`Plugins::delHooks`) and unregister commands. -Reference: `plugins/macro/macro.pl`, `plugins/eventMacro/eventMacro.pl`. - -## 6) How does the AI system work at a high level? -The main loop invokes AI logic, which inspects world state and schedules tasks. -Reference: `core/functions.pl`, `core/AI.pm`, `core/AI/CoreLogic.pm`, `knowledge/system_architecture_map.md`. - -## 7) Where is combat/behavior decision logic concentrated? -In `core/AI/CoreLogic.pm`. -Reference: `knowledge/core_subsystems.md`, `knowledge/module_dependency_map.md`. - -## 8) How are actors represented? -Actor entities use a base model plus indexed actor collections. -Reference: `core/Actor.pm`, `core/ActorList.pm`, `knowledge/core_subsystems.md`. - -## 9) Where is NPC interaction implemented? -Primary flow is in `core/Task/TalkNPC.pm` with related command/receive participation. -Reference: `knowledge/execution_flows.md`, `knowledge/debugging_playbook.md`. - -## 10) How does task scheduling work? -Tasks derive from `Task` and are queued/executed by `TaskManager`. -Reference: `core/Task.pm`, `core/TaskManager.pm`, `knowledge/module_dependency_map.md`. - -## 11) Where is routing implemented? -Routing is in `core/Task/Route.pm`, and movement execution in `core/Task/Move.pm`. -Reference: `knowledge/execution_flows.md`, `knowledge/core_subsystems.md`. - -## 12) How does packet receive flow work? -Data is tokenized/parsing handled, then dispatched by receive handlers to update runtime state. -Reference: `networking/MessageTokenizer.pm`, `networking/PacketParser.pm`, `networking/Receive.pm`, `knowledge/networking_packets.md`. - -## 13) Where are outgoing packets built? -Primarily in `networking/Send.pm` and server-specific send modules. -Reference: `knowledge/networking_packets.md`, `networking/Network/Send/ServerType0.pm`. - -## 14) How do server-specific packet differences get handled? -Through receive/send server-type modules and matching `recvpackets.txt` table data. -Reference: `networking/Network/Receive/*`, `networking/Network/Send/*`, `tables/*/recvpackets.txt`. - -## 15) What causes packet desync most often? -Profile mismatch between server configuration and selected `recvpackets` mapping. -Reference: `knowledge/debugging_playbook.md`, `knowledge/table_reference.md`. - -## 16) What are XKore modes? -Mode 1 is bridge-style; mode 2 is a fuller proxy stack with account/char/map components. -Reference: `knowledge/xkore_modes.md`, `networking/XKore.pm`, `networking/XKore2.pm`. - -## 17) Where are XKore2 stage servers implemented? -In `networking/Network/XKore2/AccountServer.pm`, `CharServer.pm`, and `MapServer.pm`. -Reference: `knowledge/xkore_modes.md`. - -## 18) Where is global runtime state stored? -In `core/Globals.pm`, consumed broadly by AI/network/task/command modules. -Reference: `knowledge/module_dependency_map.md`. - -## 19) Where is configuration path/file loading controlled? -In `core/Settings.pm` and parser routines in `core/FileParsers.pm`. -Reference: `knowledge/core_subsystems.md`, `knowledge/module_dependency_map.md`. - -## 20) Which config files matter most for behavior tuning? -`config.txt`, `items_control.txt`, and `mon_control.txt`. -Reference: `knowledge/config_system.md`. - -## 21) How does `lockMap` work conceptually? -It constrains activity/navigation to a target map (optionally coordinate-focused with lock map coords). -Reference: `knowledge/code_recipes.md` route locking example, `knowledge/config_system.md`. - -## 22) Where should I tune item pickup behavior? -In `config/items_control.txt`. -Reference: `knowledge/config_system.md`, `knowledge/code_recipes.md`. - -## 23) Where should I tune monster engagement/avoidance? -In `config/mon_control.txt`. -Reference: `knowledge/config_system.md`, `knowledge/code_recipes.md`. - -## 24) Where do plugin load policies live? -In `config/sys.txt` (`loadPlugins`, include/skip lists). -Reference: `knowledge/debugging_playbook.md`, `knowledge/plugin_system.md`. - -## 25) How do I tell macro and eventMacro apart? -Both are automation systems, but eventMacro emphasizes event/condition-driven structures with distinct parser/runner modules. -Reference: `knowledge/macro_system.md`, `plugins/macro/*`, `plugins/eventMacro/*`. - -## 26) Where are macro commands implemented? -In `plugins/macro/macro.pl` (`macro` command) and `plugins/eventMacro/eventMacro.pl` (`eventMacro`, `emacro`). -Reference: `knowledge/plugin_system.md`. - -## 27) Why would macros not trigger? -Common causes: plugin not loaded, wrong macro file path, false conditions, or disabled automacro. -Reference: `knowledge/debugging_playbook.md`. - -## 28) How do I set emergency teleport automation? -Use macro/eventMacro conditions on HP threshold and teleport action commands. -Reference: `knowledge/code_recipes.md`. - -## 29) How can I create a farming loop quickly? -Define a recursive macro with movement and `ai auto` pauses, gated by an automacro trigger. -Reference: `knowledge/code_recipes.md`. - -## 30) How do I debug “bot not moving”? -Inspect AI/task state and movement packet emission paths, then validate route/map constraints. -Reference: `knowledge/debugging_playbook.md`. - -## 31) How do I debug NPC interaction failures? -Trace `Task/TalkNPC` progression and corresponding receive handler updates. -Reference: `knowledge/debugging_playbook.md`, `knowledge/execution_flows.md`. - -## 32) How do I debug plugin load failures? -Check `sys.txt` plugin load mode/lists and plugin startup exceptions. -Reference: `knowledge/debugging_playbook.md`. - -## 33) How do I debug routing failures? -Validate route generation, movement progression, and map/portal tables. -Reference: `knowledge/debugging_playbook.md`, `tables/portals.txt`. - -## 34) What are the key packet reference tables? -`tables/packetlist.txt`, `tables/packetdescriptions.txt`, and server profile `recvpackets.txt` files. -Reference: `knowledge/table_reference.md`. - -## 35) What does `tables/servers.txt` do? -Defines server profiles and serverType anchors used by network/packet handling. -Reference: `knowledge/table_reference.md`, `knowledge/networking_packets.md`. - -## 36) Why include both kRO and iRO tables in curation? -They provide representative regional profiles for packet/content differences. -Reference: `knowledge/table_reference.md`. - -## 37) What is the role of `core/functions.pl`? -It drives startup stages and main loop transitions. -Reference: `knowledge/core_module_index.md`, `knowledge/module_dependency_map.md`. - -## 38) Why is `Commands.pm` so central? -It is a common control surface for operators, plugins, and automation. -Reference: `knowledge/module_dependency_map.md`. - -## 39) Why is `Network::Receive` so important? -Incoming packets drive most runtime state changes (actors, events, dialogs, combat updates). -Reference: `knowledge/networking_packets.md`, `knowledge/module_dependency_map.md`. - -## 40) How do plugins interact with runtime without core edits? -By hooking lifecycle/events and registering commands. -Reference: `knowledge/plugin_system.md`. - -## 41) Where should I start when extending behavior safely? -Start with a small plugin command + one lightweight hook; expand incrementally. -Reference: `knowledge/code_recipes.md`. - -## 42) How do I avoid plugin performance issues? -Keep `mainLoop_pre` callbacks lightweight and avoid heavy repeated processing. -Reference: `knowledge/code_recipes.md` notes. - -## 43) How do I map a runtime problem to the right subsystem quickly? -Use the system map: AI/Task for decisions/actions, Network/Packet for protocol/state updates, Config/Plugin for policy/extension. -Reference: `knowledge/system_architecture_map.md`. - -## 44) Where is the best overview of subsystem interactions? -`knowledge/system_architecture_map.md` and `knowledge/execution_flows.md`. - -## 45) What is the recommended debugging sequence? -Confirm profile alignment, isolate logic vs protocol layer, reproduce minimally, trace first divergence, re-test. -Reference: `knowledge/debugging_playbook.md`. - -## 46) How do I inspect module dependencies quickly? -Use `knowledge/module_dependency_map.md` for purpose + dependency + reverse-dependency summaries. - -## 47) Which modules are “top priority” for understanding architecture? -Start with `functions.pl`, AI, Actor, Task, Commands, Network, Receive/PacketParser, Settings/FileParsers, and Globals. -Reference: `knowledge/module_dependency_map.md` Top 20 section. - -## 48) How do I create map-entry automation? -Use eventMacro conditions like `InMap` + `mapLoaded` and issue config/AI commands. -Reference: `knowledge/code_recipes.md`. - -## 49) How do I create monster-proximity reactions? -Use eventMacro presence/distance conditions and trigger defensive actions (e.g., teleport). -Reference: `knowledge/code_recipes.md`. - -## 50) Where can I find practical examples across plugin/macro/config work? -`knowledge/code_recipes.md` contains concise snippets for all three domains. - -## 51) What if a change seems correct but behavior is still wrong? -Re-check server profile + packet table alignment first, then verify module override family (ServerType/kRO/iRO). -Reference: `knowledge/debugging_playbook.md`, `knowledge/networking_packets.md`. - -## 52) How should I keep this knowledge bundle maintainable? -Update high-volatility areas first: networking modules, packet tables, and macro/plugin docs tied to runtime hooks. -Reference: prioritize updates to `knowledge/networking_packets.md`, `knowledge/table_reference.md`, and `knowledge/plugin_system.md` when runtime behavior changes. diff --git a/openkore_llm_knowledge/knowledge/plugin_system.md b/openkore_llm_knowledge/knowledge/plugin_system.md deleted file mode 100644 index 14d0fdda87..0000000000 --- a/openkore_llm_knowledge/knowledge/plugin_system.md +++ /dev/null @@ -1,41 +0,0 @@ -# Plugin System - -## Architecture (as seen in representative plugins) -OpenKore plugins are Perl packages under `plugins/` with an entrypoint `.pl` file. A plugin typically: -1. imports core APIs (`Plugins`, `Commands`, `Settings`, `Globals`), -2. registers itself, -3. subscribes to runtime hooks, -4. optionally registers console commands, -5. cleans up on unload. - -Representative curated files: -- `plugins/macro/macro.pl` -- `plugins/eventMacro/eventMacro.pl` -- `plugins/reconnect/reconnect.pl` -- `plugins/profiles/profiles.pl` -- `plugins/map/map.pl` - -## Hook system -Representative plugins use hook APIs such as: -- `Plugins::addHooks(...)` for multiple hook subscriptions. -- Event points like `start3`, `mainLoop_pre`, and `configModify`. -- Hook callbacks to react to runtime state or packets. - -In the macro/eventMacro plugins, hooks are used to load macro configs at startup, react to config changes, and run checks/actions each loop cycle. - -## Plugin lifecycle -Common lifecycle pattern: -- `Plugins::register(name, description, unload_cb[, reload_cb])` -- startup callback(s) load plugin state and files -- runtime callbacks handle logic -- unload callback removes hooks/commands and clears state - -The macro plugin includes both unload and reload behavior; eventMacro includes explicit unload cleanup and file reparse logic. - -## Command registration -Plugins can expose user commands via `Commands::register`. -Examples in this bundle: -- macro plugin: `macro ...` -- eventMacro plugin: `eventMacro ...` and `emacro ...` - -These command handlers provide runtime control (status, list, enable/disable, variable ops, etc.) without modifying core code. diff --git a/openkore_llm_knowledge/knowledge/system_architecture_map.md b/openkore_llm_knowledge/knowledge/system_architecture_map.md deleted file mode 100644 index 714ea4554f..0000000000 --- a/openkore_llm_knowledge/knowledge/system_architecture_map.md +++ /dev/null @@ -1,249 +0,0 @@ -# OpenKore System Architecture Map - -This map describes how the curated OpenKore knowledge bundle components fit together at runtime. - -## 1) AI Subsystem - -### Purpose -Coordinate autonomous behavior: combat decisions, state transitions, and action triggering. - -### Main modules -- `core/AI.pm` -- `core/AI/CoreLogic.pm` -- (supporting interaction surfaces: `core/Commands.pm`, `core/TaskManager.pm`) - -### Interactions -- Consumes actor/world state from the Actor subsystem. -- Schedules and updates executable work through the Task subsystem. -- Reacts to network-derived state updates (packet handlers). -- Can be influenced by Plugins/Macros through hook and command surfaces. - -### Important entry points -- AI cycle invoked from the main loop (`core/functions.pl`). -- Decision logic concentrated in `core/AI/CoreLogic.pm`. - ---- - -## 2) Actor System - -### Purpose -Represent dynamic world entities (player, monsters, NPCs, other actors) and maintain indexed state. - -### Main modules -- `core/Actor.pm` -- `core/ActorList.pm` - -### Interactions -- Updated primarily by packet receive handlers. -- Queried by AI and Tasks for targeting, proximity, and world context. -- Exposed indirectly to plugins/macros through runtime globals and events. - -### Important entry points -- Actor creation/update from packet decode paths. -- Actor lookup/list operations through `core/ActorList.pm`. - ---- - -## 3) Task System - -### Purpose -Provide executable units for actions and multi-step workflows. - -### Main modules -- `core/Task.pm` -- `core/TaskManager.pm` -- `core/Task/Route.pm` -- `core/Task/Move.pm` -- `core/Task/TalkNPC.pm` - -### Interactions -- Receives intent from AI, commands, and automation systems. -- Uses Network subsystem for action-side packet emission. -- Depends on Actor/Map state updates from receive flow to advance steps. - -### Important entry points -- Task queueing/scheduling in `core/TaskManager.pm`. -- Specialized flow handlers in route/move/talk task modules. - ---- - -## 4) Network Subsystem - -### Purpose -Manage connection state, transport lifecycle, and bridging/proxy modes. - -### Main modules -- `networking/Network.pm` -- `networking/DirectConnection.pm` -- `networking/XKore.pm` -- `networking/XKore2.pm` -- `networking/XKoreProxy.pm` -- `networking/Network/XKore2/{AccountServer,CharServer,MapServer}.pm` - -### Interactions -- Feeds decoded data into Packet Handling. -- Receives outgoing payload requests from Tasks, Commands, and Plugins. -- Selects mode-dependent behavior (direct, bridge, proxy/XKore2). - -### Important entry points -- Runtime connection state transitions. -- XKore mode startup/iteration paths. - ---- - -## 5) Packet Handling Subsystem - -### Purpose -Decode, map, dispatch, and construct protocol packets across server variants. - -### Main modules -- `networking/PacketParser.pm` -- `networking/MessageTokenizer.pm` -- `networking/Receive.pm` -- `networking/Send.pm` -- Server-specific variants under: - - `networking/Network/Receive/*` - - `networking/Network/Send/*` - -### Interactions -- Input side updates actor/runtime state and triggers higher-level events. -- Output side encodes requested actions for transport. -- Coupled to table definitions (servers + recvpackets mapping). - -### Important entry points -- Receive dispatch in `networking/Receive.pm`. -- Send construction in `networking/Send.pm`. -- Server-type overrides in `ServerType0`, `kRO`, `iRO`, etc. - ---- - -## 6) Routing and Movement Subsystem - -### Purpose -Convert navigation intent into route computation and movement execution. - -### Main modules -- `core/Task/Route.pm` -- `core/Task/Move.pm` -- (coordination via `core/TaskManager.pm` and AI core) - -### Interactions -- Triggered by AI decisions or direct command/plugin logic. -- Uses actor/map state from packet updates. -- Emits move-related packets through send pipeline. - -### Important entry points -- Route planning task instantiation. -- Movement step processing and re-evaluation. - ---- - -## 7) Plugin System - -### Purpose -Extend runtime behavior without changing core source. - -### Main modules -- Representative plugin entries in curated bundle: - - `plugins/macro/macro.pl` - - `plugins/eventMacro/eventMacro.pl` - - `plugins/reconnect/reconnect.pl` - - `plugins/profiles/profiles.pl` - - `plugins/map/map.pl` - -### Interactions -- Registers hooks into startup/main loop/config/packet related events. -- Registers commands to expose operational controls. -- Can influence AI/task execution by issuing commands or scheduling logic. - -### Important entry points -- `Plugins::register(...)` -- `Plugins::addHooks(...)` -- `Commands::register(...)` -- Plugin unload/reload callbacks. - ---- - -## 8) Config System - -### Purpose -Provide operator-controlled behavior policies without code edits. - -### Main modules / files -- `config/config.txt` (global behavior) -- `config/items_control.txt` (item policy) -- `config/mon_control.txt` (monster behavior policy) -- Additional control files in `config/` for specialized behavior domains. - -### Interactions -- Loaded/parsed by settings + file parser paths. -- Consumed by AI, task logic, plugins, and packet behavior decisions. -- Changes can trigger plugin/config hooks in runtime. - -### Important entry points -- Initial load during startup sequence. -- Config modification events watched by plugins (e.g., macro/eventMacro). - ---- - -## 9) Macro System - -### Purpose -Offer text-configured automation layers for reactive behavior. - -### Main modules -- Macro stack: - - `plugins/macro/macro.pl` - - `plugins/macro/Macro/*` -- EventMacro stack: - - `plugins/eventMacro/eventMacro.pl` - - `plugins/eventMacro/eventMacro/*` - -### Interactions -- Subscribes to hooks/events and loop phases. -- Evaluates automacro conditions against runtime state and events. -- Triggers commands/actions that flow into AI/tasks/network send paths. - -### Important entry points -- Macro file load keys (`macro_file`, `eventMacro_file`). -- Commands: `macro`, `eventMacro`, `emacro`. - ---- - -## System Interaction Overview - -At runtime, the main loop initializes modules, loads config/table data, and enters iterative execution. Incoming network data is tokenized and parsed, then dispatched by receive handlers that refresh actor/world state. AI logic evaluates this state and schedules tasks. Tasks execute concrete workflows (move, route, NPC interactions), producing outgoing actions encoded by send logic and delivered through the active network mode. - -Plugins hook into this lifecycle at well-defined points (startup, loop, config updates, packet-related events), extending behavior and command surfaces. Macro/eventMacro layers run on top of plugin hooks, evaluating declarative automacro rules and injecting actions back into command/task pipelines. - -### Example runtime flows - -#### A) AI loop -1. Main loop tick runs. -2. AI evaluates current actor/state context. -3. AI enqueues/updates tasks. -4. Tasks emit action requests -> send packets. - -#### B) NPC interaction -1. AI/command/plugin requests NPC action. -2. `Task/TalkNPC` drives dialogue steps. -3. Receive handlers process NPC responses. -4. Task advances/completes based on packet-updated state. - -#### C) Packet receive flow -1. Raw bytes arrive via network backend. -2. Tokenizer/parser resolves packet boundaries/opcodes. -3. Receive dispatch maps to handler. -4. Handler mutates globals/actors/tasks and may trigger hooks. - -#### D) Plugin hook execution -1. Plugin registers hooks and commands at startup. -2. Runtime events invoke plugin callbacks. -3. Plugin callback performs logic (state check, action trigger, config reload). -4. Optional commands expose runtime control to operator. - -#### E) Routing decision flow -1. AI decides to navigate to target/map/coordinate. -2. Route task computes route segments. -3. Move task executes stepwise movement. -4. Receive updates position/context; route logic re-evaluates until arrival/failure. diff --git a/openkore_llm_knowledge/knowledge/table_reference.md b/openkore_llm_knowledge/knowledge/table_reference.md deleted file mode 100644 index 1b9b90f556..0000000000 --- a/openkore_llm_knowledge/knowledge/table_reference.md +++ /dev/null @@ -1,24 +0,0 @@ -# Table Reference - -This curated table subset focuses on gameplay lookups and packet/network references. - -## Core network/profile tables -- `tables/servers.txt` — master server profile definitions and serverType anchors. -- `tables/packetlist.txt` — packet index/reference list. -- `tables/packetdescriptions.txt` — textual packet purpose descriptions. -- `tables/kRO/recvpackets.txt`, `tables/iRO/recvpackets.txt` — server-family packet structure maps. - -## Gameplay identity/reference tables -- `tables/statusnametable.txt` — status effect name mapping. -- `tables/SKILL_id_handle.txt` — skill ID ↔ handle mapping. -- `tables/STATUS_id_handle.txt` — status ID ↔ handle mapping. -- `tables/itemtypes.txt` — item type categories. -- `tables/elements.txt` — elemental mapping. - -## Gameplay world/content examples -- `tables/portals.txt` — portal transitions. -- `tables/skillsarea.txt` — area-skill metadata. -- `tables/msgstringtable.txt` — message string references. -- Profile snapshots included for two major families: - - `tables/kRO/{maps.txt,items.txt,skillnametable.txt}` - - `tables/iRO/{maps.txt,items.txt,skillnametable.txt}` diff --git a/openkore_llm_knowledge/knowledge/xkore_modes.md b/openkore_llm_knowledge/knowledge/xkore_modes.md deleted file mode 100644 index 7665b00559..0000000000 --- a/openkore_llm_knowledge/knowledge/xkore_modes.md +++ /dev/null @@ -1,21 +0,0 @@ -# XKore Modes - -## XKore mode 1 (`networking/XKore.pm`) -- Runs a local bridge server and communicates between OpenKore and an RO client. -- Maintains client/server packet channels and forwards transformed packets. -- Useful for hybrid play/automation workflows where a game client remains in the loop. - -## XKore mode 2 (`networking/XKore2.pm` + `networking/Network/XKore2/*`) -- Implements proxy-style account/char/map server components. -- Uses dedicated XKore2 server classes: - - `AccountServer.pm` - - `CharServer.pm` - - `MapServer.pm` -- Integrates with hooks to manage packet mangling and in-game synchronization. - -## XKore proxy support (`networking/XKoreProxy.pm`) -- Additional proxy integration layer used in XKore-style setups. - -## Practical difference summary -- **Mode 1**: direct local bridge style, simpler path for client relay. -- **Mode 2**: fuller proxy stack with separate protocol-stage servers and session handling. From e18dff0d21c1bcc0ddc83fa94e492a55676bb044 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Bosc=C3=A1?= <juan_bosca@live.com> Date: Tue, 10 Mar 2026 21:14:57 -0300 Subject: [PATCH 18/18] update 1 --- gpt_upload_final/architecture_atlas.md | 362 ++++++++++++++++++ gpt_upload_final/assistant_rules.md | 55 +++ gpt_upload_final/index.md | 25 ++ gpt_upload_final/knowledge_index.md | 102 +++++ gpt_upload_final/plugin_examples_index.md | 58 +++ gpt_upload_final/runtime_map.md | 73 ++++ gpt_upload_final/symptom_index.md | 95 +++++ .../gpt_upload/01_llm_context.md | 21 + .../gpt_upload/02_architecture_overview.md | 23 ++ .../gpt_upload/03_system_architecture_map.md | 43 +++ .../gpt_upload/04_core_subsystems.md | 49 +++ .../gpt_upload/05_module_dependency_map.md | 29 ++ .../gpt_upload/06_execution_flows.md | 29 ++ .../gpt_upload/07_npc_interaction_flow.md | 26 ++ .../gpt_upload/08_routing_and_movement.md | 20 + .../gpt_upload/09_networking_packets.md | 22 ++ .../gpt_upload/10_xkore_modes.md | 20 + .../gpt_upload/11_plugin_system.md | 28 ++ .../gpt_upload/12_config_system.md | 19 + .../gpt_upload/13_macro_system.md | 19 + .../gpt_upload/14_table_reference.md | 25 ++ .../gpt_upload/15_debugging_playbook.md | 185 +++++++++ .../gpt_upload/16_debug_decision_trees.md | 90 +++++ .../gpt_upload/17_code_recipes.md | 147 +++++++ .../gpt_upload/18_openkore_faq.md | 147 +++++++ .../gpt_upload/19_glossary.md | 27 ++ .../gpt_upload/20_code_index.md | 46 +++ .../gpt_upload/21_core_module_index.md | 13 + .../gpt_upload/22_common_pitfalls.md | 49 +++ .../gpt_upload/23_debugging_guide.md | 23 ++ .../gpt_upload/24_learning_path.md | 49 +++ .../gpt_upload/26_architecture_diagrams.md | 17 + .../gpt_upload/27_system_flows.md | 35 ++ .../29_prompt_examples_for_users.md | 49 +++ .../gpt_upload/30_dataset_summary.md | 37 ++ openkore_llm_knowledge/gpt_upload/config.zip | Bin 0 -> 33807 bytes openkore_llm_knowledge/gpt_upload/core.zip | Bin 0 -> 288868 bytes .../gpt_upload/networking.zip | Bin 0 -> 196116 bytes openkore_llm_knowledge/gpt_upload/plugins.zip | Bin 0 -> 83077 bytes openkore_llm_knowledge/gpt_upload/tables.zip | Bin 0 -> 368590 bytes 40 files changed, 2057 insertions(+) create mode 100644 gpt_upload_final/architecture_atlas.md create mode 100644 gpt_upload_final/assistant_rules.md create mode 100644 gpt_upload_final/index.md create mode 100644 gpt_upload_final/knowledge_index.md create mode 100644 gpt_upload_final/plugin_examples_index.md create mode 100644 gpt_upload_final/runtime_map.md create mode 100644 gpt_upload_final/symptom_index.md create mode 100644 openkore_llm_knowledge/gpt_upload/01_llm_context.md create mode 100644 openkore_llm_knowledge/gpt_upload/02_architecture_overview.md create mode 100644 openkore_llm_knowledge/gpt_upload/03_system_architecture_map.md create mode 100644 openkore_llm_knowledge/gpt_upload/04_core_subsystems.md create mode 100644 openkore_llm_knowledge/gpt_upload/05_module_dependency_map.md create mode 100644 openkore_llm_knowledge/gpt_upload/06_execution_flows.md create mode 100644 openkore_llm_knowledge/gpt_upload/07_npc_interaction_flow.md create mode 100644 openkore_llm_knowledge/gpt_upload/08_routing_and_movement.md create mode 100644 openkore_llm_knowledge/gpt_upload/09_networking_packets.md create mode 100644 openkore_llm_knowledge/gpt_upload/10_xkore_modes.md create mode 100644 openkore_llm_knowledge/gpt_upload/11_plugin_system.md create mode 100644 openkore_llm_knowledge/gpt_upload/12_config_system.md create mode 100644 openkore_llm_knowledge/gpt_upload/13_macro_system.md create mode 100644 openkore_llm_knowledge/gpt_upload/14_table_reference.md create mode 100644 openkore_llm_knowledge/gpt_upload/15_debugging_playbook.md create mode 100644 openkore_llm_knowledge/gpt_upload/16_debug_decision_trees.md create mode 100644 openkore_llm_knowledge/gpt_upload/17_code_recipes.md create mode 100644 openkore_llm_knowledge/gpt_upload/18_openkore_faq.md create mode 100644 openkore_llm_knowledge/gpt_upload/19_glossary.md create mode 100644 openkore_llm_knowledge/gpt_upload/20_code_index.md create mode 100644 openkore_llm_knowledge/gpt_upload/21_core_module_index.md create mode 100644 openkore_llm_knowledge/gpt_upload/22_common_pitfalls.md create mode 100644 openkore_llm_knowledge/gpt_upload/23_debugging_guide.md create mode 100644 openkore_llm_knowledge/gpt_upload/24_learning_path.md create mode 100644 openkore_llm_knowledge/gpt_upload/26_architecture_diagrams.md create mode 100644 openkore_llm_knowledge/gpt_upload/27_system_flows.md create mode 100644 openkore_llm_knowledge/gpt_upload/29_prompt_examples_for_users.md create mode 100644 openkore_llm_knowledge/gpt_upload/30_dataset_summary.md create mode 100644 openkore_llm_knowledge/gpt_upload/config.zip create mode 100644 openkore_llm_knowledge/gpt_upload/core.zip create mode 100644 openkore_llm_knowledge/gpt_upload/networking.zip create mode 100644 openkore_llm_knowledge/gpt_upload/plugins.zip create mode 100644 openkore_llm_knowledge/gpt_upload/tables.zip diff --git a/gpt_upload_final/architecture_atlas.md b/gpt_upload_final/architecture_atlas.md new file mode 100644 index 0000000000..beefc215a5 --- /dev/null +++ b/gpt_upload_final/architecture_atlas.md @@ -0,0 +1,362 @@ +# OpenKore Architecture Atlas + +This document provides a visual architecture map of OpenKore. + +Each section contains Mermaid diagrams representing important subsystems and execution flows. +The goal is to help the assistant reason about the internal structure and runtime behavior of OpenKore. + +--- + +# System Overview + +```mermaid +flowchart TD + +Config[Configuration Files] +Plugins[Plugins] +Macros[Macros] + +Core[Core Engine] + +AI[AI System] +Routing[Routing System] +Network[Networking System] +Actors[Actor System] + +Server[Game Server] + +Config --> Core +Plugins --> Core +Macros --> Core + +Core --> AI +Core --> Routing +Core --> Network +Core --> Actors + +Network --> Server +Server --> Network +``` + +Explanation: + +OpenKore is structured around a core engine that connects multiple subsystems: + +- AI system +- networking system +- routing system +- actor system + +Plugins, macros, and configuration files extend or modify behavior. + +--- + +# AI Execution Loop + +```mermaid +flowchart TD + +Loop[Main Loop] +Loop --> Tick[AI Tick] + +Tick --> Queue[Check Task Queue] +Queue --> Task[Execute Task] + +Task --> Action[Perform Action] +Action --> Packet[Send Packet] + +Packet --> Server[Game Server] +Server --> Receive[Receive Packet] + +Receive --> Handler[Packet Handler] +Handler --> Update[Update Actor State] + +Update --> Tick +``` + +Explanation: + +The AI loop continuously: + +1. processes AI tasks +2. performs actions +3. communicates with the server +4. updates world state. + +--- + +# Networking Pipeline + +```mermaid +flowchart TD + +Server[Game Server] + +Server --> Socket[Network Socket] +Socket --> ReceiveModule[Receive Module] + +ReceiveModule --> PacketParser[Packet Parser] + +PacketParser --> ActorUpdate[Actor State Update] + +ActorUpdate --> AI +``` + +Explanation: + +The networking subsystem receives packets from the server and converts them into game state updates used by the AI. + +--- + +# NPC Interaction Flow + +```mermaid +flowchart TD + +AI --> DetectNPC[Detect NPC] +DetectNPC --> RouteNPC[Route To NPC] + +RouteNPC --> TalkPacket[Send Talk Packet] + +TalkPacket --> Server[Game Server] + +Server --> DialoguePacket[Dialogue Packet] + +DialoguePacket --> DialogueHandler[Dialogue Handler] + +DialogueHandler --> AI +``` + +Explanation: + +NPC interaction involves: + +1. detecting NPC location +2. routing to the NPC +3. sending interaction packets +4. processing dialogue responses. + +--- + +# Plugin System + +```mermaid +flowchart TD + +Core --> PluginLoader +PluginLoader --> PluginInstance + +PluginInstance --> HookRegistration + +HookRegistration --> EventHooks + +EventHooks --> AI +EventHooks --> Network +EventHooks --> Commands +``` + +Explanation: + +Plugins extend OpenKore by registering hooks that listen to events generated by different subsystems. + +--- + +# Macro System + +```mermaid +flowchart TD + +ConfigFiles[Configuration Files] --> MacroSystem + +MacroSystem --> TriggerCheck + +TriggerCheck --> ConditionMet + +ConditionMet --> MacroAction + +MacroAction --> AI +``` + +Explanation: + +Macros allow users to define automated behaviors triggered by conditions. + +--- + +# Routing and Movement + +```mermaid +flowchart TD + +AI --> RequestMove + +RequestMove --> Pathfinding + +Pathfinding --> Route + +Route --> MoveCommand + +MoveCommand --> PacketSend + +PacketSend --> Server +``` + +Explanation: + +Routing calculates paths between coordinates and sends movement packets to the server. + +--- + +# Actor State Updates + +```mermaid +flowchart TD + +PacketReceive --> PacketHandler + +PacketHandler --> ActorManager + +ActorManager --> UpdateActor + +UpdateActor --> AI +``` + +Explanation: + +Actors represent entities in the game world such as players, monsters, and NPCs. Their state is updated based on received packets. + +--- + +# Command Processing + +```mermaid +flowchart TD + +UserInput --> CommandParser + +CommandParser --> CommandHandler + +CommandHandler --> AI + +CommandHandler --> Plugins +``` + +Explanation: + +Commands can be entered through the console or triggered by macros and plugins. + +--- + +# Configuration Loading + +```mermaid +flowchart TD + +ConfigFiles --> ConfigParser + +ConfigParser --> RuntimeSettings + +RuntimeSettings --> AI +RuntimeSettings --> Plugins +RuntimeSettings --> Routing +``` + +Explanation: + +Configuration files define runtime behavior and influence multiple subsystems. + +--- + +# Task System + +```mermaid +flowchart TD + +AI --> TaskQueue + +TaskQueue --> TaskScheduler + +TaskScheduler --> ActiveTask + +ActiveTask --> AI +``` + +Explanation: + +The task system manages AI behaviors such as combat, routing, item collection, and interaction. + +--- + +# Packet Parsing + +```mermaid +flowchart TD + +NetworkSocket --> PacketReceive + +PacketReceive --> PacketDecoder + +PacketDecoder --> EventDispatch + +EventDispatch --> ActorSystem +EventDispatch --> AI +``` + +Explanation: + +Packets received from the server are decoded and dispatched to the appropriate subsystems. + +--- + +# Plugin Interaction With AI + +```mermaid +flowchart TD + +Plugin --> HookEvent + +HookEvent --> AI + +AI --> PluginCallback +``` + +Explanation: + +Plugins can intercept AI events and modify behavior dynamically. + +--- + +# Debugging Flow + +```mermaid +flowchart TD + +IssueDetected --> IdentifySubsystem + +IdentifySubsystem --> CheckConfig +IdentifySubsystem --> InspectPlugin +IdentifySubsystem --> InspectNetwork + +CheckConfig --> Resolve +InspectPlugin --> Resolve +InspectNetwork --> Resolve +``` + +Explanation: + +Debugging typically involves identifying which subsystem is responsible for the issue. + +--- + +# Summary + +The OpenKore architecture is built around several interacting subsystems: + +- AI engine +- networking pipeline +- routing system +- plugin system +- macro system +- actor management + +Understanding how these subsystems interact is essential for debugging and extending OpenKore. \ No newline at end of file diff --git a/gpt_upload_final/assistant_rules.md b/gpt_upload_final/assistant_rules.md new file mode 100644 index 0000000000..b846626cc6 --- /dev/null +++ b/gpt_upload_final/assistant_rules.md @@ -0,0 +1,55 @@ +# Assistant Behavior Rules + +This GPT specializes in OpenKore. + +Its purpose is to help developers and advanced users understand and work with the OpenKore codebase. + +--- + +## Core Principles + +The assistant should prioritize: + +1. technical accuracy +2. architecture-aware explanations +3. practical debugging advice +4. minimal working examples + +--- + +## When Explaining Features + +The assistant should: + +- identify the subsystem first +- reference relevant modules or files +- explain execution flow when relevant + +--- + +## When Debugging Problems + +The assistant should: + +1. identify likely subsystem +2. list possible causes +3. suggest files or configuration to inspect +4. propose debugging steps + +--- + +## When Generating Code + +The assistant should: + +- provide minimal working examples +- follow common OpenKore conventions +- explain assumptions if behavior depends on server configuration + +--- + +## Avoid + +- guessing module names +- inventing APIs +- giving generic advice without subsystem context \ No newline at end of file diff --git a/gpt_upload_final/index.md b/gpt_upload_final/index.md new file mode 100644 index 0000000000..f454c073ba --- /dev/null +++ b/gpt_upload_final/index.md @@ -0,0 +1,25 @@ +# OpenKore Knowledge Index + +This knowledge base contains curated documentation and code references for OpenKore. + +Documents are organized as follows: + +Architecture +- architecture.md +- system_flows.md + +Development +- development.md +- plugin_config_system.md + +Debugging +- debugging.md + +Reference +- glossary.md +- faq.md + +Dataset description +- dataset_summary.md + +Code archives contain curated source code for reference. \ No newline at end of file diff --git a/gpt_upload_final/knowledge_index.md b/gpt_upload_final/knowledge_index.md new file mode 100644 index 0000000000..8c8ceb4bdd --- /dev/null +++ b/gpt_upload_final/knowledge_index.md @@ -0,0 +1,102 @@ +# OpenKore Knowledge Base Index + +This knowledge base contains curated documentation and code references for the OpenKore automation framework. + +The documents are organized by technical domain to help the assistant locate relevant information quickly. + +--- + +## Architecture + +These documents describe the overall structure of the system. + +- architecture.md +- system_flows.md + +Topics covered: +- subsystem organization +- module relationships +- execution flow +- networking architecture +- AI behavior + +--- + +## Plugin and Configuration System + +These documents describe extensibility and configuration behavior. + +- plugin_config_system.md + +Topics covered: +- plugin lifecycle +- hook system +- command registration +- configuration files +- macro and eventMacro systems +- gameplay tables + +--- + +## Debugging + +These documents help diagnose and troubleshoot OpenKore behavior. + +- debugging.md + +Topics covered: +- routing failures +- plugin loading issues +- macro problems +- packet desynchronization +- NPC interaction problems + +--- + +## Development + +Practical development guidance and examples. + +- development.md + +Topics covered: +- plugin development +- hook examples +- macro recipes +- eventMacro examples +- configuration patterns + +--- + +## Reference + +Technical reference material. + +- glossary.md +- faq.md + +Topics covered: +- OpenKore terminology +- frequently asked questions +- architectural explanations + +--- + +## Dataset Description + +- dataset_summary.md + +This document explains the structure and purpose of the curated knowledge dataset. + +--- + +## Code Archives + +The following archives contain curated portions of the OpenKore source code. + +- openkore_core.zip +- openkore_plugins.zip +- openkore_config.zip +- openkore_tables.zip + +These archives allow the assistant to inspect real code when needed. \ No newline at end of file diff --git a/gpt_upload_final/plugin_examples_index.md b/gpt_upload_final/plugin_examples_index.md new file mode 100644 index 0000000000..8bc89a5b1a --- /dev/null +++ b/gpt_upload_final/plugin_examples_index.md @@ -0,0 +1,58 @@ +# Plugin Examples Index + +This document describes common OpenKore plugin patterns. + +It helps the assistant generate plugin code examples. + +--- + +## Minimal Plugin Skeleton + +Typical structure: + +- package declaration +- plugin registration +- hook registration +- unload handler + +Used for creating simple extensions. + +--- + +## Hook Registration + +Plugins often register hooks to react to events such as: + +- packet receive +- AI tick +- command execution + +--- + +## Command Registration + +Plugins can register custom console commands. + +These commands allow users to trigger custom logic during runtime. + +--- + +## Packet Hooks + +Some plugins inspect or modify packets to implement advanced behavior. + +This requires interaction with the networking subsystem. + +--- + +## Configuration Integration + +Plugins can read values from configuration files to modify behavior dynamically. + +--- + +## Best Practices + +- keep plugins modular +- avoid modifying core state directly +- prefer hook-based extensions \ No newline at end of file diff --git a/gpt_upload_final/runtime_map.md b/gpt_upload_final/runtime_map.md new file mode 100644 index 0000000000..7e1162d687 --- /dev/null +++ b/gpt_upload_final/runtime_map.md @@ -0,0 +1,73 @@ +# OpenKore Runtime Execution Map + +This document summarizes how OpenKore operates during runtime. + +It provides a simplified overview of the main execution loop. + +--- + +## Main Runtime Flow + +Main Loop +↓ +AI Tick +↓ +Check Task Queue +↓ +Execute Task +↓ +Action Performed +(movement, combat, interaction) +↓ +Packet Sent To Server +↓ +Server Response Received +↓ +Packet Handler Processes Data +↓ +Actor State Updated +↓ +AI Loop Continues + +--- + +## Important Runtime Components + +### AI System + +Responsible for decision-making and task scheduling. + +### Actor System + +Represents entities in the game world: + +- player +- monsters +- NPCs +- other players + +### Networking System + +Handles packet communication between OpenKore and the game server. + +### Task System + +Manages AI tasks such as: + +- routing +- combat +- interaction + +--- + +## Runtime Behavior Sources + +OpenKore behavior can originate from several sources: + +- core AI logic +- configuration files +- macros and eventMacros +- plugins +- server packet responses + +Understanding which subsystem is responsible is critical when debugging issues. \ No newline at end of file diff --git a/gpt_upload_final/symptom_index.md b/gpt_upload_final/symptom_index.md new file mode 100644 index 0000000000..b2895d4878 --- /dev/null +++ b/gpt_upload_final/symptom_index.md @@ -0,0 +1,95 @@ +# OpenKore Symptom Index + +This document maps common user problems to relevant documentation. + +It helps the assistant quickly identify which subsystem may be responsible. + +--- + +## Bot Not Moving + +Possible causes: + +- routing failure +- pathfinding issues +- configuration preventing movement +- plugin overriding behavior + +See: + +- debugging.md +- system_flows.md + +--- + +## NPC Interaction Not Working + +Possible causes: + +- packet desynchronization +- incorrect map coordinates +- interaction sequence failure + +See: + +- system_flows.md +- debugging.md + +--- + +## Plugin Not Loading + +Possible causes: + +- plugin registration failure +- syntax errors +- missing hooks + +See: + +- plugin_config_system.md +- debugging.md + +--- + +## Macro Not Triggering + +Possible causes: + +- incorrect macro syntax +- event condition not met +- configuration conflict + +See: + +- plugin_config_system.md +- development.md + +--- + +## Packet Desynchronization + +Possible causes: + +- networking issues +- incorrect packet interpretation +- server-specific behavior + +See: + +- system_flows.md +- debugging.md + +--- + +## Movement Visible In Bot But Not In Client + +Possible causes: + +- client synchronization issue +- packet handling problem + +See: + +- networking_packets.md +- debugging.md \ No newline at end of file diff --git a/openkore_llm_knowledge/gpt_upload/01_llm_context.md b/openkore_llm_knowledge/gpt_upload/01_llm_context.md new file mode 100644 index 0000000000..cf85d00779 --- /dev/null +++ b/openkore_llm_knowledge/gpt_upload/01_llm_context.md @@ -0,0 +1,21 @@ +# OpenKore LLM Context + +## Objective +This layer gives an LLM enough structural context to reason about OpenKore runtime behavior, module boundaries, and extension points. + +## Runtime model (high level) +1. `src/functions.pl` boots runtime services, loads config/tables, initializes network mode, and drives the main loop. +2. Network receive handlers (`src/Network/Receive*.pm`) transform packets into world-state updates. +3. Actor state (`src/Actor.pm`, `src/ActorList.pm`) becomes the shared world model for decision logic. +4. AI (`src/AI.pm`, `src/AI/CoreLogic.pm`) evaluates state and schedules actions through tasks. +5. Task execution (`src/TaskManager.pm`, `src/Task/*.pm`) performs multi-step actions and emits packets through send modules (`src/Network/Send*.pm`). +6. Plugins (`src/Plugins.pm`, `plugins/*`) and macro layers (`plugins/macro`, `plugins/eventMacro`) inject automation and custom behavior. + +## Key architecture anchors +- **Entry/control loop**: `src/functions.pl` +- **Global state/config loading**: `src/Globals.pm`, `src/Settings.pm`, `src/FileParsers.pm`, `control/` +- **AI + behavior**: `src/AI.pm`, `src/AI/CoreLogic.pm` +- **Actor model**: `src/Actor.pm`, `src/ActorList.pm`, `src/Actor/*` +- **Task orchestration**: `src/Task.pm`, `src/TaskManager.pm`, `src/Task/*` +- **Networking and packets**: `src/Network.pm`, `src/Network/*` +- **Plugin/macro automation**: `src/Plugins.pm`, `plugins/macro/*`, `plugins/eventMacro/*` diff --git a/openkore_llm_knowledge/gpt_upload/02_architecture_overview.md b/openkore_llm_knowledge/gpt_upload/02_architecture_overview.md new file mode 100644 index 0000000000..6bfe238247 --- /dev/null +++ b/openkore_llm_knowledge/gpt_upload/02_architecture_overview.md @@ -0,0 +1,23 @@ +# OpenKore Architecture Overview + +## Top-level structure +- `src/`: core runtime modules (AI, actor model, networking, tasks, plugins API, command layer). +- `control/`: operator configuration and behavior policies (`config.txt`, control lists, route/macro inputs). +- `tables/`: protocol/game data mappings (packet maps, item/skill/map metadata, server-specific variants). +- `plugins/`: optional extensions, including macro/eventMacro automation stacks. +- `fields/`: map field data used by routing/navigation. + +## Core runtime layers +1. **Bootstrap + loop**: `src/functions.pl` and `src/Modules.pm` +2. **State + config**: `src/Globals.pm`, `src/Settings.pm`, `src/FileParsers.pm` +3. **Network transport + packet translation**: `src/Network.pm`, `src/Network/{Receive,Send,PacketParser}.pm` +4. **World model**: `src/Actor.pm`, `src/ActorList.pm`, `src/Field.pm` +5. **Decision engine**: `src/AI.pm`, `src/AI/CoreLogic.pm` +6. **Execution engine**: `src/TaskManager.pm`, `src/Task/*.pm` +7. **Extension surface**: `src/Plugins.pm`, `src/Commands.pm`, `plugins/*` + +## Architectural characteristics +- **Event-driven and loop-based**: runtime state is advanced each tick by receive updates + AI/task progression. +- **Protocol-adapter design**: packet send/receive classes are split by server type under `src/Network/Receive/*` and `src/Network/Send/*`. +- **Policy outside code**: behavior is heavily configured via `control/` and `tables/` files. +- **Extension-first automation**: macro/eventMacro run as plugins and reuse command/hook/task pathways. diff --git a/openkore_llm_knowledge/gpt_upload/03_system_architecture_map.md b/openkore_llm_knowledge/gpt_upload/03_system_architecture_map.md new file mode 100644 index 0000000000..3181b0647f --- /dev/null +++ b/openkore_llm_knowledge/gpt_upload/03_system_architecture_map.md @@ -0,0 +1,43 @@ +# System Architecture Map (Textual) + +## Primary flow +`functions.pl main loop` -> `Network receive` -> `Actor/Globals update` -> `AI decision` -> `Task execution` -> `Network send` + +## Subsystem map + +### 1) Networking +- Connection/mode handling: `src/Network.pm`, `src/Network/DirectConnection.pm`, `src/Network/XKore*.pm` +- Packet ingestion and dispatch: `src/Network/MessageTokenizer.pm`, `src/Network/PacketParser.pm`, `src/Network/Receive.pm`, `src/Network/Receive/*` +- Packet construction: `src/Network/Send.pm`, `src/Network/Send/*` + +### 2) Actor system +- Base entity model: `src/Actor.pm`, `src/Actor/*` +- Actor collections and lookup: `src/ActorList.pm` +- World/map coupling: `src/Field.pm`, map and position state in globals + +### 3) AI subsystem +- AI stack state and sequencing: `src/AI.pm` +- Core behavior logic: `src/AI/CoreLogic.pm` +- Uses actor/world/config state; issues actions via task manager and commands + +### 4) Task subsystem +- Task abstraction: `src/Task.pm` +- Scheduler and lifecycle: `src/TaskManager.pm` +- High-impact tasks: `src/Task/Route.pm`, `src/Task/Move.pm`, `src/Task/TalkNPC.pm`, `src/Task/UseSkill.pm` + +### 5) Configuration subsystem +- Config discovery/loading: `src/Settings.pm`, `src/FileParsers.pm` +- Runtime shared state: `src/Globals.pm` +- Policy sources: `control/*`, `tables/*` + +### 6) Plugin + macro subsystem +- Hook and plugin lifecycle: `src/Plugins.pm` +- Command bridge: `src/Commands.pm` +- Macro engines: `plugins/macro/*`, `plugins/eventMacro/*` + +## Key entry points +- Startup/main loop: `src/functions.pl` +- Command dispatch: `src/Commands.pm` +- Hook registration: `src/Plugins.pm` +- AI tick path: `src/AI.pm` -> `src/AI/CoreLogic.pm` +- Packet receive path: `src/Network/Receive.pm` + server-specific receivers diff --git a/openkore_llm_knowledge/gpt_upload/04_core_subsystems.md b/openkore_llm_knowledge/gpt_upload/04_core_subsystems.md new file mode 100644 index 0000000000..9621268ac6 --- /dev/null +++ b/openkore_llm_knowledge/gpt_upload/04_core_subsystems.md @@ -0,0 +1,49 @@ +# Core Subsystems + +## AI +- Main modules: `src/AI.pm`, `src/AI/CoreLogic.pm` +- Role: maintain AI queues/state machines, evaluate combat/loot/movement priorities, enqueue tasks. +- Inputs: actor state, config policies, packet-driven updates. +- Outputs: task requests, command invocations, action intents. + +## Actor system +- Main modules: `src/Actor.pm`, `src/ActorList.pm`, `src/Actor/*` +- Role: represent player/NPC/monster/portal entities, with indexed lookup for targeting and proximity logic. +- Inputs: receive handlers and map updates. +- Outputs: query surface for AI, tasks, commands, and plugins. + +## Networking +- Main modules: `src/Network.pm`, `src/Network/{Receive,Send,PacketParser,MessageTokenizer}.pm`, `src/Network/XKore*.pm` +- Role: manage transport mode, parse inbound packets, encode outbound actions, support server-specific packet families. +- Inputs: socket data + outgoing action intents. +- Outputs: state mutations via receive handlers and serialized packets to servers/clients. + +## Task system +- Main modules: `src/Task.pm`, `src/TaskManager.pm`, `src/Task/*` +- Role: run asynchronous multi-step actions (route, move, NPC dialogs, skill/item use). +- Inputs: AI/command/plugin action requests. +- Outputs: packet sends, follow-up tasks, completion/failure states consumed by AI. + +## Plugin system +- Main modules: `src/Plugins.pm`, `plugins/*` +- Role: dynamic extension lifecycle (`register`, hooks, unload), runtime behavior injection. +- Inputs: startup/load events, packet-related hooks, periodic loop hooks. +- Outputs: custom commands, state transitions, automation triggers. + +## Configuration system +- Main modules: `src/Settings.pm`, `src/FileParsers.pm`, `control/*`, `tables/*` +- Role: load policy and data files that parameterize AI, networking, and gameplay handling. +- Inputs: text configs and table files. +- Outputs: normalized runtime config/state in globals and subsystem-specific structures. + +## Macro system +- Main modules: `plugins/macro/macro.pl`, `plugins/macro/Macro/*`, `plugins/eventMacro/eventMacro.pl`, `plugins/eventMacro/eventMacro/*` +- Role: declarative automation over hooks and game events, with conditional triggers and scripted actions. +- Inputs: macro definitions and live runtime events. +- Outputs: command execution and indirect task/network activity. + +## Routing +- Main modules: `src/Task/Route.pm`, `src/Task/Move.pm`, `src/Field.pm`, `fields/*` +- Role: compute and execute navigation paths across map cells/portals. +- Inputs: destination intents, field data, actor position/state. +- Outputs: stepwise movement actions and route completion/fallback states. diff --git a/openkore_llm_knowledge/gpt_upload/05_module_dependency_map.md b/openkore_llm_knowledge/gpt_upload/05_module_dependency_map.md new file mode 100644 index 0000000000..35f8eb23cf --- /dev/null +++ b/openkore_llm_knowledge/gpt_upload/05_module_dependency_map.md @@ -0,0 +1,29 @@ +# Module Dependency Map + +## Dependency chains (high impact) +1. `src/functions.pl` -> `src/Settings.pm` / `src/FileParsers.pm` / `src/Modules.pm` (bootstrap) +2. `src/functions.pl` -> `src/Network.pm` + `src/Network/*` (connection and packet loop) +3. `src/Network/Receive*.pm` -> `src/Globals.pm` + `src/Actor*.pm` (state mutation) +4. `src/AI.pm` -> `src/AI/CoreLogic.pm` -> `src/TaskManager.pm` + `src/Task/*` (decision to execution) +5. `src/Task/*` -> `src/Network/Send*.pm` (action serialization) +6. `src/Plugins.pm` + `plugins/*` -> `src/Commands.pm` / AI / TaskManager (extension control paths) + +## Subsystem dependency view +- **AI depends on**: Actor state, config policies, task scheduler, command surface. +- **Actor system depends on**: Network receive updates and global runtime registries. +- **Task system depends on**: AI/commands/plugins for intents; network send + actor/map updates for progression. +- **Plugin/macro depends on**: hook lifecycle (`src/Plugins.pm`), command execution (`src/Commands.pm`), global state. +- **Routing depends on**: task framework, field data (`fields/*`), movement packet sends. + +## Coupling hotspots +- `src/Globals.pm`: shared mutable state touched by receive, AI, and plugins. +- `src/Commands.pm`: cross-cutting control surface used by user input and automation. +- `src/TaskManager.pm`: convergence point for AI and scripted automation. +- `src/Network/Receive.pm`: ingress bridge from protocol events to internal state transitions. + +## Practical navigation order for analysis +1. Start at `src/functions.pl`. +2. Follow receive path (`src/Network/Receive.pm`, subtype receivers). +3. Inspect AI loop (`src/AI.pm`, `src/AI/CoreLogic.pm`). +4. Inspect task execution (`src/TaskManager.pm`, route/move/NPC tasks). +5. Inspect extension points (`src/Plugins.pm`, macro/eventMacro plugins). diff --git a/openkore_llm_knowledge/gpt_upload/06_execution_flows.md b/openkore_llm_knowledge/gpt_upload/06_execution_flows.md new file mode 100644 index 0000000000..cac52ed2b0 --- /dev/null +++ b/openkore_llm_knowledge/gpt_upload/06_execution_flows.md @@ -0,0 +1,29 @@ +# Execution Flows + +## Runtime execution loop +OpenKore runs a tick-oriented loop rooted in `src/functions.pl`, where network ingestion, state updates, AI decisions, and task execution are iterated continuously. + +Operational sequence: +1. Main loop tick in `src/functions.pl` advances network, AI, and task phases. +2. Receive handlers (`src/Network/Receive.pm`, `src/Network/Receive/*`) apply packet-driven state updates. +3. Shared state (`src/Globals.pm`, `src/ActorList.pm`) becomes input for AI logic. +4. AI (`src/AI.pm`, `src/AI/CoreLogic.pm`) chooses actions and updates task queues. +5. Tasks (`src/TaskManager.pm`, `src/Task/*`) execute stepwise and emit outgoing packets through send modules. + +## AI execution loop + +```mermaid +flowchart TD + A[AI tick\nsrc/AI.pm] --> B[Gather context\nGlobals + ActorList + config] + B --> C[Core logic\nsrc/AI/CoreLogic.pm] + C --> D{Action required?} + D -- No --> E[Idle/monitor state] + D -- Yes --> F[Create/adjust tasks\nsrc/TaskManager.pm] + F --> G[Run task step\nsrc/Task/*.pm] + G --> H[Emit commands/packets\nCommands + Network::Send] + H --> I[Wait for receive updates] + E --> I + I --> A +``` + +AI is decision-oriented; tasks hold execution state between ticks while receive handlers provide feedback. diff --git a/openkore_llm_knowledge/gpt_upload/07_npc_interaction_flow.md b/openkore_llm_knowledge/gpt_upload/07_npc_interaction_flow.md new file mode 100644 index 0000000000..48d5f80007 --- /dev/null +++ b/openkore_llm_knowledge/gpt_upload/07_npc_interaction_flow.md @@ -0,0 +1,26 @@ +# NPC Interaction Flow + +NPC interactions are executed as task-driven conversations, typically using `src/Task/TalkNPC.pm` and packet handlers under `src/Network/Receive/*.pm`. + +```mermaid +sequenceDiagram + participant U as AI/Command/Plugin + participant TM as TaskManager + participant TN as Task::TalkNPC + participant NS as Network::Send + participant S as Ragnarok Server + participant NR as Network::Receive + participant G as Globals/Actor state + + U->>TM: enqueue NPC interaction + TM->>TN: start dialogue task + TN->>NS: send talk/response packet + NS->>S: outbound packet + S->>NR: NPC response packet + NR->>G: update dialogue/state flags + NR->>TM: task-relevant updates + TM->>TN: advance next dialogue step + TN-->>TM: complete/fail +``` + +`Task::TalkNPC` keeps dialogue progression explicit, while receive handlers synchronize server-side conversation state back into runtime state. diff --git a/openkore_llm_knowledge/gpt_upload/08_routing_and_movement.md b/openkore_llm_knowledge/gpt_upload/08_routing_and_movement.md new file mode 100644 index 0000000000..a1e0b0cf5f --- /dev/null +++ b/openkore_llm_knowledge/gpt_upload/08_routing_and_movement.md @@ -0,0 +1,20 @@ +# Routing and Movement + +Routing converts destination intent into path segments and movement packets using `src/Task/Route.pm`, `src/Task/Move.pm`, `src/Field.pm`, and `fields/*` data. + +```mermaid +flowchart TD + A[AI/Command target\nmap + coordinates] --> B[Task::Route init] + B --> C[Load field graph\nsrc/Field.pm + fields/*] + C --> D[Compute path segments] + D --> E[Task::Move executes step] + E --> F[Send move packet\nsrc/Network/Send.pm] + F --> G[Receive position update\nsrc/Network/Receive.pm] + G --> H{Reached waypoint?} + H -- No --> E + H -- Yes --> I{Reached destination?} + I -- No --> D + I -- Yes --> J[Route complete] +``` + +Routing is iterative and feedback-driven: each movement step is validated by receive updates before continuing. diff --git a/openkore_llm_knowledge/gpt_upload/09_networking_packets.md b/openkore_llm_knowledge/gpt_upload/09_networking_packets.md new file mode 100644 index 0000000000..a72e17de20 --- /dev/null +++ b/openkore_llm_knowledge/gpt_upload/09_networking_packets.md @@ -0,0 +1,22 @@ +# Networking and Packet Flow + +OpenKore packet handling combines framing/tokenization, opcode parsing, and server-type receive/send classes under `src/Network/*`. + +## Packet receive flow + +```mermaid +flowchart TD + A[Socket bytes\nNetwork mode] --> B[MessageTokenizer\nsrc/Network/MessageTokenizer.pm] + B --> C[PacketParser\nsrc/Network/PacketParser.pm] + C --> D[Receive dispatcher\nsrc/Network/Receive.pm] + D --> E[Server-type handler\nsrc/Network/Receive/*] + E --> F[Runtime mutation\nGlobals + Actor + Task signals] + F --> G[Hooks/plugins notified\nsrc/Plugins.pm] +``` + +Receive processing turns protocol frames into domain-level state transitions used by AI and tasks. + +## Packet send path (supporting context) +- Action request sources: AI, commands, task modules, plugins/macros. +- Encoding path: `src/Network/Send.pm` -> `src/Network/Send/*` (server-type specific structures). +- Transport path: active mode (`DirectConnection`, `XKore`, `XKore2`, or `XKoreProxy`) in `src/Network/*.pm`. diff --git a/openkore_llm_knowledge/gpt_upload/10_xkore_modes.md b/openkore_llm_knowledge/gpt_upload/10_xkore_modes.md new file mode 100644 index 0000000000..4f0d773936 --- /dev/null +++ b/openkore_llm_knowledge/gpt_upload/10_xkore_modes.md @@ -0,0 +1,20 @@ +# XKore Modes + +XKore modes define how OpenKore connects to the game client/server pipeline. + +## Core modules +- `src/Network/XKore.pm` +- `src/Network/XKore2.pm` +- `src/Network/XKoreProxy.pm` +- `src/Network/DirectConnection.pm` (baseline comparison) + +## Mode summary +- **DirectConnection**: bot connects directly to game servers. +- **XKore**: client-assisted mode using forwarding/bridging logic. +- **XKore2**: OpenKore hosts local account/char/map proxy endpoints (`src/Network/XKore2/*`) and relays traffic. +- **XKoreProxy**: proxy-style mediation path for packet bridging. + +## Architectural impact +- All modes reuse packet parsing/dispatch and send modules (`src/Network/{Receive,Send}.pm`). +- Mode choice primarily changes transport/session orchestration and how packets are sourced/sinked. +- AI, tasks, actor state, plugins, and macro systems remain mode-agnostic above the network abstraction. diff --git a/openkore_llm_knowledge/gpt_upload/11_plugin_system.md b/openkore_llm_knowledge/gpt_upload/11_plugin_system.md new file mode 100644 index 0000000000..8e93284d60 --- /dev/null +++ b/openkore_llm_knowledge/gpt_upload/11_plugin_system.md @@ -0,0 +1,28 @@ +# Plugin System + +## Purpose +OpenKore plugins extend runtime behavior without editing core modules. + +## Core interfaces +- `src/Plugins.pm`: plugin lifecycle and hook dispatch. +- `src/Commands.pm`: command registration/execution surface used by plugins. +- `plugins/*`: plugin implementations (`*.pl` entrypoints + helper modules). + +## Lifecycle pattern +1. Register plugin with `Plugins::register(...)`. +2. Register hooks with `Plugins::addHooks(...)`. +3. Optionally register commands via `Commands::register(...)`. +4. On unload, remove hooks/commands and clear plugin state. + +## Common hook usage +- Startup hooks (e.g., startup/init phases) +- Loop hooks (periodic behavior) +- Packet hooks (`packet/*`, `packet_pre/*`) +- Config-change hooks (`configModify`) + +## Representative plugins +- `plugins/macro/macro.pl` +- `plugins/eventMacro/eventMacro.pl` +- `plugins/reconnect/reconnect.pl` +- `plugins/profiles/profiles.pl` +- `plugins/map/map.pl` diff --git a/openkore_llm_knowledge/gpt_upload/12_config_system.md b/openkore_llm_knowledge/gpt_upload/12_config_system.md new file mode 100644 index 0000000000..35dc6108d2 --- /dev/null +++ b/openkore_llm_knowledge/gpt_upload/12_config_system.md @@ -0,0 +1,19 @@ +# Configuration System + +## Purpose +Behavior policy is primarily configuration-driven via `control/` and table data under `tables/`. + +## Key files +- `control/config.txt`: global runtime/AI/combat/movement/network options. +- `control/items_control.txt`: item pickup/storage/sell policy. +- `control/mon_control.txt`: per-monster behavior policy. + +## Runtime integration +- `src/Settings.pm`: config path/file registration. +- `src/FileParsers.pm`: parser logic for control/table formats. +- `src/Globals.pm`: shared runtime state consumed by AI/tasks/plugins. + +## Debugging guidance +- Validate key names and value format first. +- Re-test with minimal config to isolate policy conflicts. +- Check plugin/macro automation that may override config expectations. diff --git a/openkore_llm_knowledge/gpt_upload/13_macro_system.md b/openkore_llm_knowledge/gpt_upload/13_macro_system.md new file mode 100644 index 0000000000..cba32b5d0c --- /dev/null +++ b/openkore_llm_knowledge/gpt_upload/13_macro_system.md @@ -0,0 +1,19 @@ +# Macro System + +## Components +- Macro plugin: `plugins/macro/macro.pl`, `plugins/macro/Macro/*` +- eventMacro plugin: `plugins/eventMacro/eventMacro.pl`, `plugins/eventMacro/eventMacro/*` + +## Configuration keys +- `macro_file` -> macro script path (commonly `macros.txt`). +- `eventMacro_file` -> eventMacro script path (commonly `eventMacros.txt`). + +## Execution model +1. Plugin loads and parses macro file. +2. Hooks/events trigger condition evaluation. +3. Matching automacro rules enqueue/execute actions. +4. Actions flow through command/task/network paths. + +## Operational notes +- Macro and eventMacro are complementary automation layers. +- Troubleshooting should separate: load -> parse -> trigger -> action execution. diff --git a/openkore_llm_knowledge/gpt_upload/14_table_reference.md b/openkore_llm_knowledge/gpt_upload/14_table_reference.md new file mode 100644 index 0000000000..a67d5688f1 --- /dev/null +++ b/openkore_llm_knowledge/gpt_upload/14_table_reference.md @@ -0,0 +1,25 @@ +# Table Reference + +## Purpose +`tables/` contains protocol and gameplay data used by network parsing, IDs, maps, and runtime lookups. + +## Core packet/profile tables +- `tables/servers.txt` +- `tables/packetlist.txt` +- `tables/packetdescriptions.txt` +- server-family `recvpackets.txt` files (e.g., `tables/kRO/recvpackets.txt`, `tables/iRO/recvpackets.txt`) + +## Gameplay identity tables +- `tables/SKILL_id_handle.txt` +- `tables/STATUS_id_handle.txt` +- `tables/statusnametable.txt` +- `tables/itemtypes.txt` +- `tables/elements.txt` + +## World/content tables +- `tables/portals.txt` +- `tables/skillsarea.txt` +- regional `maps.txt`, `items.txt`, `skillnametable.txt` + +## Notes +Packet-related incidents should always be validated against serverType + recvpackets alignment. diff --git a/openkore_llm_knowledge/gpt_upload/15_debugging_playbook.md b/openkore_llm_knowledge/gpt_upload/15_debugging_playbook.md new file mode 100644 index 0000000000..c826ebf7c4 --- /dev/null +++ b/openkore_llm_knowledge/gpt_upload/15_debugging_playbook.md @@ -0,0 +1,185 @@ +# Debugging Playbook + +This playbook maps common OpenKore runtime failures to likely causes, inspection points, and step-by-step troubleshooting. + +--- + +## 1) Bot not moving +- **Likely subsystem**: AI + Task + Routing + Network Send +- **Possible causes**: + - AI state blocked (attack/dialog/task lock) + - Route task never created or immediately failing + - Movement packets not being sent or rejected + - Movement-related config prevents walking +- **Files to inspect**: + - `src/AI.pm`, `src/AI/CoreLogic.pm` + - `src/TaskManager.pm`, `src/Task/Move.pm`, `src/Task/Route.pm` + - `src/Network/Send.pm`, `src/Network/Receive.pm` +- **Config to inspect**: + - `control/config.txt` (movement/lockMap/route-related options) + - `control/mon_control.txt` (behavior that can pin combat state) +- **Debugging steps**: + 1. Confirm AI is ticking and not paused/stuck in a higher-priority state. + 2. Check whether `Task::Route`/`Task::Move` is queued in task manager. + 3. Validate that move packets are emitted by send path. + 4. Confirm receive updates are acknowledging position changes. + 5. Reduce constraints in config (lock-only behavior, avoid lists) and retest. + +## 2) Routing failure +- **Likely subsystem**: Routing + Field data + Task +- **Possible causes**: + - Missing/incorrect field data + - Blocked cells/portal transitions not resolved + - Route recomputation loops without progress +- **Files to inspect**: + - `src/Task/Route.pm`, `src/Task/Move.pm`, `src/Field.pm` +- **Config to inspect**: + - `control/config.txt` (route/move flags) + - `fields/*` (map walkability data) +- **Debugging steps**: + 1. Verify map field exists and corresponds to current map. + 2. Check route generation result (empty path or oscillating path). + 3. Confirm position updates are fresh (no stale coordinates). + 4. Test shorter route segments to isolate failing region. + 5. Re-check portal/map transition assumptions. + +## 3) NPC interaction failure +- **Likely subsystem**: Task + Network Receive/Send + Actor/NPC state +- **Possible causes**: + - Wrong NPC coordinates or stale actor reference + - Dialogue step mismatch with server response + - Talk packets not sent or incorrect sequence +- **Files to inspect**: + - `src/Task/TalkNPC.pm` + - `src/Network/Send.pm`, `src/Network/Receive.pm`, `src/Network/Receive/*` + - `src/ActorList.pm` +- **Config to inspect**: + - `control/config.txt` (npc interaction directives/scripts) +- **Debugging steps**: + 1. Confirm NPC exists in actor list at expected coordinates. + 2. Trace talk packet send sequence against expected dialogue steps. + 3. Verify receive handlers parse response packet IDs correctly for server type. + 4. Reproduce with minimal NPC script/task to reduce branching. + +## 4) Plugin not loading +- **Likely subsystem**: Plugin system + startup/module loading +- **Possible causes**: + - Syntax/runtime error in plugin file + - Incorrect plugin path/name + - Missing dependency module used by plugin +- **Files to inspect**: + - `src/Plugins.pm`, `src/Modules.pm` + - `plugins/<plugin_name>/*.pl` +- **Config to inspect**: + - `control/config.txt` (plugin enable/load entries) +- **Debugging steps**: + 1. Check startup logs for plugin load exceptions. + 2. Validate plugin registers with `Plugins::register`. + 3. Confirm plugin path and filename match expected loader behavior. + 4. Remove optional dependency imports to isolate failing require/use. + +## 5) Plugin hook not firing +- **Likely subsystem**: Plugin hooks + event dispatch +- **Possible causes**: + - Hook registered with wrong event name + - Hook callback blocked by guard condition + - Expected event never emitted in current runtime path +- **Files to inspect**: + - `src/Plugins.pm`, `src/functions.pl` + - Plugin file defining `Plugins::addHooks` +- **Config to inspect**: + - `control/config.txt` (conditions controlling event generation) +- **Debugging steps**: + 1. List hooks registered by plugin after load. + 2. Add lightweight logging at callback entry. + 3. Verify event name spelling and payload assumptions. + 4. Trigger event manually through a minimal reproduction path. + +## 6) Macro not triggering +- **Likely subsystem**: Macro plugin + command/hook bridge +- **Possible causes**: + - Macro file not loaded + - Automacro condition never true + - Macro command blocked by runtime state +- **Files to inspect**: + - `plugins/macro/macro.pl`, `plugins/macro/Macro/*` + - `src/Commands.pm`, `src/Plugins.pm` +- **Config to inspect**: + - `control/config.txt` (`macro_file` and related keys) + - macro definition file referenced by config +- **Debugging steps**: + 1. Confirm macro plugin loaded and macro file parsed. + 2. Validate automacro conditions against live state. + 3. Execute macro manually to separate trigger vs action issues. + 4. Inspect command dispatch path for blocked/invalid command. + +## 7) eventMacro issues +- **Likely subsystem**: eventMacro plugin + event parser/runner +- **Possible causes**: + - Invalid eventMacro syntax + - Trigger conditions not matching event payload + - Runner stalled by previous action/wait state +- **Files to inspect**: + - `plugins/eventMacro/eventMacro.pl` + - `plugins/eventMacro/eventMacro/{Core,Runner,Automacro,FileParser}.pm` +- **Config to inspect**: + - `control/config.txt` (`eventMacro_file`) + - eventMacro script file +- **Debugging steps**: + 1. Validate eventMacro file parsing with minimal script. + 2. Log trigger evaluation for target automacro. + 3. Check runner queue and pending wait/time gates. + 4. Reduce script to one trigger + one action and retest. + +## 8) Packet desync +- **Likely subsystem**: Networking receive/parser + server-type mappings +- **Possible causes**: + - Wrong server type packet tables + - Opcode map mismatch after server update + - Packet boundary/tokenization errors +- **Files to inspect**: + - `src/Network/MessageTokenizer.pm`, `src/Network/PacketParser.pm` + - `src/Network/Receive.pm`, `src/Network/Receive/*` + - `src/Network/Send.pm`, `src/Network/Send/*` +- **Config to inspect**: + - `control/config.txt` (serverType/recvpackets settings) + - `tables/*` packet definition files for the target server +- **Debugging steps**: + 1. Confirm selected serverType and recvpackets data. + 2. Compare failing opcode decode against current server packet definitions. + 3. Capture raw packet sequence around first desync point. + 4. Verify both receive and send side use compatible packet maps. + +## 9) XKore sync issues +- **Likely subsystem**: XKore transport/session bridging +- **Possible causes**: + - Client-proxy handshake mismatch + - Account/char/map relay state divergence + - Timing/forwarding issues in XKore mode +- **Files to inspect**: + - `src/Network/XKore.pm`, `src/Network/XKore2.pm`, `src/Network/XKoreProxy.pm` + - `src/Network/XKore2/{AccountServer,CharServer,MapServer}.pm` +- **Config to inspect**: + - `control/config.txt` (XKore mode and connection settings) +- **Debugging steps**: + 1. Confirm active mode (Direct vs XKore/XKore2/XKoreProxy). + 2. Trace handshake/login flow across account->char->map stages. + 3. Check for asymmetric forwarding (client sees state that bot does not, or inverse). + 4. Validate mode-specific ports/bind settings and restart cleanly. + +## 10) Visual client state not updating +- **Likely subsystem**: XKore bridge + packet forwarding + actor sync +- **Possible causes**: + - Forwarded packets not reaching client + - Actor state updated in bot but not mirrored to client channel + - Client session stuck after map transition +- **Files to inspect**: + - `src/Network/XKore*.pm` + - `src/Network/Receive.pm`, `src/ActorList.pm` +- **Config to inspect**: + - `control/config.txt` (XKore forwarding/session config) +- **Debugging steps**: + 1. Verify bot-side state is changing (position/actors) first. + 2. Confirm corresponding packets are forwarded to client path. + 3. Re-check session phase (account/char/map) for stuck transition. + 4. Reconnect client through same mode to re-establish synchronization. diff --git a/openkore_llm_knowledge/gpt_upload/16_debug_decision_trees.md b/openkore_llm_knowledge/gpt_upload/16_debug_decision_trees.md new file mode 100644 index 0000000000..8869941f85 --- /dev/null +++ b/openkore_llm_knowledge/gpt_upload/16_debug_decision_trees.md @@ -0,0 +1,90 @@ +# Debug Decision Trees + +These trees provide fast triage paths for recurring OpenKore failures. + +## A) Bot not moving / Routing failure + +```mermaid +flowchart TD + A[Bot not moving] --> B{AI ticking?} + B -- No --> B1[Inspect src/AI.pm and main loop in src/functions.pl] + B -- Yes --> C{Task::Route or Task::Move queued?} + C -- No --> C1[Inspect AI decisions in src/AI/CoreLogic.pm and config gates] + C -- Yes --> D{Move packets sent?} + D -- No --> D1[Inspect src/Network/Send.pm and command/task path] + D -- Yes --> E{Position updates received?} + E -- No --> E1[Inspect src/Network/Receive.pm and packet mapping] + E -- Yes --> F{Path valid in fields data?} + F -- No --> F1[Inspect src/Field.pm + fields/*] + F -- Yes --> G[Check blockers/lock settings in control/config.txt] +``` + +Use this when movement appears frozen or route tasks fail repeatedly. + +## B) NPC interaction failure + +```mermaid +flowchart TD + A[NPC interaction fails] --> B{NPC present in ActorList?} + B -- No --> B1[Inspect src/ActorList.pm updates from receive handlers] + B -- Yes --> C{TalkNPC task created?} + C -- No --> C1[Inspect trigger path in AI/commands/plugins] + C -- Yes --> D{Talk packets emitted?} + D -- No --> D1[Inspect src/Task/TalkNPC.pm + src/Network/Send.pm] + D -- Yes --> E{Expected response packet decoded?} + E -- No --> E1[Inspect src/Network/Receive/* serverType handlers] + E -- Yes --> F[Validate dialogue sequence assumptions in task script] +``` + +Use this when NPC talks stop, loop, or complete with wrong branch. + +## C) Plugin not loading / Hook not firing + +```mermaid +flowchart TD + A[Plugin issue] --> B{Plugin loads successfully?} + B -- No --> B1[Check syntax/dependencies + src/Plugins.pm loader logs] + B -- Yes --> C{Hook registered via Plugins::addHooks?} + C -- No --> C1[Fix registration block in plugin file] + C -- Yes --> D{Event emitted at runtime?} + D -- No --> D1[Inspect event source in src/functions.pl / network paths] + D -- Yes --> E{Callback guard conditions pass?} + E -- No --> E1[Log callback inputs and config-dependent guards] + E -- Yes --> F[Inspect side effects via Commands/TaskManager] +``` + +Use this for both plugin boot failures and silent hooks. + +## D) Macro/eventMacro not triggering + +```mermaid +flowchart TD + A[Macro/eventMacro not triggering] --> B{Plugin loaded?} + B -- No --> B1[Inspect plugins/macro or plugins/eventMacro load path] + B -- Yes --> C{Script file parsed?} + C -- No --> C1[Validate macro_file/eventMacro_file in control/config.txt] + C -- Yes --> D{Trigger condition true in live state?} + D -- No --> D1[Log variables/events used by automacro] + D -- Yes --> E{Action executes manually?} + E -- No --> E1[Inspect command path in src/Commands.pm] + E -- Yes --> F[Investigate scheduler/wait constraints in runner] +``` + +Use this to isolate parser issues from trigger logic and action execution. + +## E) Packet desync / XKore sync / visual client desync + +```mermaid +flowchart TD + A[Desync observed] --> B{Wrong serverType/packet table?} + B -- Yes --> B1[Fix control/config.txt + tables/* packet mappings] + B -- No --> C{Receive decode errors?} + C -- Yes --> C1[Inspect MessageTokenizer/PacketParser/Receive handlers] + C -- No --> D{Only in XKore mode?} + D -- No --> D1[Inspect send/receive parity and opcode updates] + D -- Yes --> E{Client and bot state diverge?} + E -- Yes --> E1[Inspect src/Network/XKore*.pm forwarding/session state] + E -- No --> F[Inspect map-transition/session phase sync] +``` + +Use this for protocol mismatches and client-bridge synchronization drift. diff --git a/openkore_llm_knowledge/gpt_upload/17_code_recipes.md b/openkore_llm_knowledge/gpt_upload/17_code_recipes.md new file mode 100644 index 0000000000..ba7d8fa816 --- /dev/null +++ b/openkore_llm_knowledge/gpt_upload/17_code_recipes.md @@ -0,0 +1,147 @@ +# Code Recipes + +Practical snippets for common OpenKore development tasks. + +## 1) Minimal plugin skeleton +```perl +package myPlugin; + +use strict; +use Plugins; +use Log qw(message warning error); + +my $hooks = []; + +Plugins::register('myPlugin', 'Minimal plugin skeleton', \&on_unload); +$hooks = Plugins::addHooks( + ['start3', \&on_start], + ['AI_pre', \&on_ai_pre], +); + +sub on_start { + message "[myPlugin] started\n", 'info'; +} + +sub on_ai_pre { + my ($hookName, $args) = @_; + # lightweight periodic logic +} + +sub on_unload { + Plugins::delHooks($hooks); + message "[myPlugin] unloaded\n", 'info'; +} + +1; +``` + +## 2) Hook registration pattern +```perl +my $hooks = Plugins::addHooks( + ['packet_pre/actor_moved', \&on_actor_moved], + ['packet/map_changed', \&on_map_changed], + ['configModify', \&on_config_modify], +); + +sub on_actor_moved { + my ($hook, $args) = @_; + # inspect $args->{ID}, $args->{coords} +} +``` + +## 3) Command registration +```perl +Commands::register( + ['mycmd', 'run custom action', \&cmd_mycmd] +); + +sub cmd_mycmd { + my (undef, $args) = @_; + message "[myPlugin] mycmd args: $args\n", 'info'; +} + +# on unload: +Commands::unregister('mycmd'); +``` + +## 4) Packet hook example +```perl +my $hooks = Plugins::addHooks( + ['packet_pre/login_error', \&on_login_error], + ['packet/map_changed', \&on_map_changed], +); + +sub on_login_error { + my ($hook, $args) = @_; + warning "Login error packet received: $args->{type}\n"; +} +``` + +## 5) Macro example (`macros.txt`) +```txt +automacro autoHeal { + hp < 40% + timeout 1 + call { + do ss 28 + } +} + +macro goTown { + do move prontera +} +``` + +## 6) eventMacro example +```txt +automacro checkWeight { + InLockMap 1 + weight > 85% + call { + do conf itemsTakeAuto 0 + do ai clear + } +} + +automacro greetOnMap { + map prontera + run-once 1 + call { + do c Hello from OpenKore + } +} +``` + +## 7) Configuration examples (`control/config.txt`) +```txt +lockMap prontera +saveMap prontera +route_randomWalk 1 +teleportAuto_hp 25 +itemsTakeAuto 2 +attackAuto 2 +macro_file macros.txt +eventMacro_file eventMacros.txt +``` + +## 8) Debugging snippets +### 8.1 Print current map and coordinates +```perl +use Globals qw($field %char); +message sprintf("Map=%s x=%d y=%d\n", $field->baseName, $char{pos_to}{x}, $char{pos_to}{y}), 'debug'; +``` + +### 8.2 Trace command callback entry +```perl +sub cmd_mycmd { + my (undef, $args) = @_; + message "[TRACE] cmd_mycmd called with '$args'\n", 'debug'; +} +``` + +### 8.3 Check task queue state quickly +```perl +use TaskManager; +my $task = TaskManager::getTaskManager()->activeTask; +message "Active task: " . ($task ? ref($task) : 'none') . "\n", 'debug'; +``` diff --git a/openkore_llm_knowledge/gpt_upload/18_openkore_faq.md b/openkore_llm_knowledge/gpt_upload/18_openkore_faq.md new file mode 100644 index 0000000000..c3a6760545 --- /dev/null +++ b/openkore_llm_knowledge/gpt_upload/18_openkore_faq.md @@ -0,0 +1,147 @@ +# OpenKore Technical FAQ + +## Architecture +1. **Q:** What is OpenKore's main runtime entry point? + **A:** `src/functions.pl` initializes modules and runs the main loop. +2. **Q:** Where is global runtime state stored? + **A:** Mostly in `src/Globals.pm` and shared structures. +3. **Q:** Which module runs AI logic? + **A:** `src/AI.pm` coordinates AI and calls `src/AI/CoreLogic.pm`. +4. **Q:** How are game entities represented? + **A:** Through `src/Actor.pm` and `src/ActorList.pm`. +5. **Q:** What handles tasks? + **A:** `src/Task.pm` defines tasks and `src/TaskManager.pm` schedules them. +6. **Q:** Where does command parsing happen? + **A:** In `src/Commands.pm`. +7. **Q:** How are plugins managed? + **A:** Via `src/Plugins.pm` lifecycle and hooks API. +8. **Q:** Why is architecture loop-based? + **A:** To repeatedly process receive->state->AI->tasks->send cycles. + +## Debugging +9. **Q:** First check when bot freezes? + **A:** Confirm AI tick and active task state. +10. **Q:** How to distinguish route vs movement failure? + **A:** Check if a route exists, then confirm move packets and position updates. +11. **Q:** Why log packet IDs during failures? + **A:** To detect serverType/recvpackets mismatch quickly. +12. **Q:** Best way to debug plugin hooks? + **A:** Log both hook registration and callback entry. +13. **Q:** Why test with minimal config? + **A:** To remove config side effects masking root causes. +14. **Q:** What indicates desync risk? + **A:** Decode errors, unknown packets, or stale actor updates. +15. **Q:** How to verify task deadlock? + **A:** Inspect active task and queued tasks in task manager. +16. **Q:** Should I debug with all plugins enabled? + **A:** No, isolate with minimal plugin set first. + +## Plugins +17. **Q:** Minimum plugin requirements? + **A:** `Plugins::register`, optional hooks, and clean unload. +18. **Q:** How to add a custom command? + **A:** Use `Commands::register` and unregister on unload. +19. **Q:** Why might plugin load fail silently? + **A:** Early syntax/runtime errors or missing dependencies. +20. **Q:** Can plugins alter AI behavior? + **A:** Yes, via commands, hooks, and task interactions. +21. **Q:** Where are community plugins stored? + **A:** Under `plugins/`. +22. **Q:** How to avoid hook performance issues? + **A:** Keep callbacks lightweight and defer heavy work. +23. **Q:** Can multiple plugins share hook names? + **A:** Yes, each callback runs if registered correctly. +24. **Q:** Why unregister hooks on unload? + **A:** To prevent stale callbacks and inconsistent state. + +## Macros / eventMacro +25. **Q:** Difference between macro and eventMacro? + **A:** Macro is classic command script flow; eventMacro is event/condition-driven automation. +26. **Q:** Where to set macro file path? + **A:** `control/config.txt` via `macro_file`. +27. **Q:** Where to set eventMacro file path? + **A:** `control/config.txt` via `eventMacro_file`. +28. **Q:** Why automacro never triggers? + **A:** Condition false, plugin not loaded, or parse issue. +29. **Q:** How to test macro trigger quickly? + **A:** Manually run equivalent command and compare behavior. +30. **Q:** Why use run-once in eventMacro? + **A:** To avoid repeated firing for one-time actions. +31. **Q:** Can macros execute OpenKore commands? + **A:** Yes, via `do <command>`. +32. **Q:** How to debug eventMacro parser issues? + **A:** Reduce to one trigger + one action and rebuild incrementally. + +## Routing / movement +33. **Q:** Which files control routing logic? + **A:** `src/Task/Route.pm`, `src/Task/Move.pm`, and `src/Field.pm`. +34. **Q:** What is walkability? + **A:** Whether a map cell is traversable based on field data. +35. **Q:** Why does route loop occur? + **A:** Stale position updates, blocked transitions, or invalid field assumptions. +36. **Q:** What is lockMap effect? + **A:** Restricts behavior to a preferred map scope. +37. **Q:** How to validate map data? + **A:** Ensure map exists in `fields/*` and coordinates are valid. +38. **Q:** Why movement works manually but not AI? + **A:** AI priorities/config may override movement intents. +39. **Q:** Can aggressive combat stop movement? + **A:** Yes, combat states can continuously preempt route tasks. +40. **Q:** Best first movement test? + **A:** Short route on same map with minimal automation enabled. + +## Networking +41. **Q:** Which module tokenizes incoming packets? + **A:** `src/Network/MessageTokenizer.pm`. +42. **Q:** Which module decodes packet structures? + **A:** `src/Network/PacketParser.pm` and receive handlers. +43. **Q:** Where is receive dispatch done? + **A:** `src/Network/Receive.pm`. +44. **Q:** Where are server-specific packet handlers? + **A:** `src/Network/Receive/*` and `src/Network/Send/*`. +45. **Q:** Why packet errors after server patch? + **A:** Opcode/table changes requiring updated mappings. +46. **Q:** Can wrong serverType break everything? + **A:** Yes, it causes systematic decode/encode mismatch. +47. **Q:** What causes partial desync? + **A:** Some packets map correctly while others fail for changed opcodes. +48. **Q:** How to isolate receive vs send bug? + **A:** Compare inbound decode logs and outbound packet construction separately. + +## NPC interaction +49. **Q:** Which task handles NPC dialogues? + **A:** `src/Task/TalkNPC.pm`. +50. **Q:** Why NPC script stalls mid-dialogue? + **A:** Response packet mismatch or wrong expected step sequence. +51. **Q:** Must NPC be in actor list first? + **A:** Yes, reliable interaction depends on valid actor presence and position. +52. **Q:** Why wrong NPC gets targeted? + **A:** Incorrect coordinates or stale target selection. +53. **Q:** What to inspect first in NPC failure? + **A:** Talk packet sequence and corresponding receive responses. +54. **Q:** Can plugins interfere with NPC flow? + **A:** Yes, if they alter commands/tasks concurrently. + +## Config behavior +55. **Q:** Where are core behavior settings? + **A:** `control/config.txt`. +56. **Q:** What file controls monster behavior rules? + **A:** `control/mon_control.txt`. +57. **Q:** Why config changes seem ignored? + **A:** Reload needed, wrong key, or overridden by plugin automation. +58. **Q:** How to keep config deterministic? + **A:** Use minimal explicit settings and disable conflicting automation. +59. **Q:** What is saveMap used for? + **A:** Preferred return/safety map behavior. +60. **Q:** Why document config with code changes? + **A:** Behavior is policy-driven; docs prevent invisible config regressions. + +## XKore / client sync +61. **Q:** What is XKore2 role? + **A:** Local proxy/relay flow for account/char/map sessions. +62. **Q:** Why client view stale but bot state updates? + **A:** Forwarding/session sync issue in XKore bridge path. +63. **Q:** Is debugging Direct and XKore identical? + **A:** No, transport/session layers differ significantly. +64. **Q:** First step for XKore sync bugs? + **A:** Confirm active mode and trace handshake phase transitions. diff --git a/openkore_llm_knowledge/gpt_upload/19_glossary.md b/openkore_llm_knowledge/gpt_upload/19_glossary.md new file mode 100644 index 0000000000..abf1c04be5 --- /dev/null +++ b/openkore_llm_knowledge/gpt_upload/19_glossary.md @@ -0,0 +1,27 @@ +# Glossary + +- **Actor**: Runtime entity abstraction (player, monster, NPC, portal) managed via `Actor` modules. +- **AI queue**: Ordered AI intention/state processing that decides which action/task should run next. +- **Task**: Executable unit with lifecycle (start/process/finish), e.g., move, route, NPC interaction. +- **Plugin hook**: Callback registration point triggered by runtime events (`Plugins::addHooks`). +- **Command handler**: Function bound to a command name through `Commands::register`. +- **Packet handler**: Receive/send routine that decodes or encodes specific protocol packets. +- **Route**: Planned path between coordinates/maps, later executed stepwise by movement tasks. +- **Automacro**: Conditional automation rule that triggers scripted actions when conditions match. +- **eventMacro**: Event/condition-driven macro framework with parser, runner, and trigger model. +- **lockMap**: Configuration policy limiting movement/behavior to a designated map. +- **saveMap**: Preferred map used for return/recovery behavior. +- **Walkability**: Whether map cells are traversable according to field data and routing constraints. +- **Client sync**: Consistency between bot internal state and visual game client state (important in XKore). +- **Server packet**: Protocol message exchanged with the game server; parsed/encoded by network modules. +- **ServerType**: Packet profile selection controlling opcode and packet layout mapping. +- **Receive dispatch**: Mechanism mapping inbound opcode frames to concrete handler methods. +- **Send pipeline**: Path from action intent to serialized outbound packet. +- **XKore mode**: Bridged/proxy connection model variants (`XKore`, `XKore2`, `XKoreProxy`). +- **Main loop tick**: One full iteration of runtime processing in `functions.pl`. +- **Field data**: Map walkability/cell data used by route calculations. +- **Desync**: Divergence between expected protocol/state and observed runtime behavior. +- **Hook event**: Named runtime signal emitted for plugin subscribers. +- **Task manager**: Scheduler coordinating active/pending tasks and transitions. +- **Packet desync**: Failure mode where packet mappings no longer align with server behavior. +- **Control files**: Operator-facing config files under `control/` that drive runtime policy. diff --git a/openkore_llm_knowledge/gpt_upload/20_code_index.md b/openkore_llm_knowledge/gpt_upload/20_code_index.md new file mode 100644 index 0000000000..d0b179e3f4 --- /dev/null +++ b/openkore_llm_knowledge/gpt_upload/20_code_index.md @@ -0,0 +1,46 @@ +# Code Index (Architecture-Oriented) + +## Runtime entry and orchestration +- `src/functions.pl` — startup sequence and main loop +- `src/Modules.pm` — module loading registry +- `src/Globals.pm` — shared runtime state container + +## AI +- `src/AI.pm` — AI framework and queue/state management +- `src/AI/CoreLogic.pm` — core decision routines + +## Actor and world model +- `src/Actor.pm` — actor base model +- `src/ActorList.pm` — actor collections/indexing +- `src/Actor/*` — actor specializations +- `src/Field.pm` — map/field representation + +## Task execution +- `src/Task.pm` — task abstraction +- `src/TaskManager.pm` — scheduler/executor +- `src/Task/Route.pm` — route planning/execution wrapper +- `src/Task/Move.pm` — movement task +- `src/Task/TalkNPC.pm` — NPC dialogue workflow +- `src/Task/UseSkill.pm` — skill-use workflows + +## Networking and packets +- `src/Network.pm` — network facade and state +- `src/Network/DirectConnection.pm` — direct server mode +- `src/Network/XKore.pm`, `src/Network/XKore2.pm`, `src/Network/XKoreProxy.pm` — proxy/bridge modes +- `src/Network/MessageTokenizer.pm` — packet frame/token extraction +- `src/Network/PacketParser.pm` — packet definition parsing +- `src/Network/Receive.pm` + `src/Network/Receive/*` — inbound packet dispatch/handlers +- `src/Network/Send.pm` + `src/Network/Send/*` — outbound packet builders + +## Commands, plugins, and automation +- `src/Commands.pm` — command parser/dispatcher +- `src/Plugins.pm` — plugin lifecycle and hook API +- `plugins/macro/macro.pl` + `plugins/macro/Macro/*` — legacy macro automation +- `plugins/eventMacro/eventMacro.pl` + `plugins/eventMacro/eventMacro/*` — event-driven macro automation + +## Configuration and data sources +- `src/Settings.pm` — config path and file registration +- `src/FileParsers.pm` — config/table parsing +- `control/*` — user behavior policies and runtime options +- `tables/*` — server/data tables +- `fields/*` — map grids for routing/navigation diff --git a/openkore_llm_knowledge/gpt_upload/21_core_module_index.md b/openkore_llm_knowledge/gpt_upload/21_core_module_index.md new file mode 100644 index 0000000000..be33611294 --- /dev/null +++ b/openkore_llm_knowledge/gpt_upload/21_core_module_index.md @@ -0,0 +1,13 @@ +# Core Module Index + +- `src/functions.pl` — bootstrap and main loop. +- `src/Modules.pm` — module registration/loading. +- `src/Globals.pm` — shared runtime state. +- `src/Settings.pm` — config path and file registration. +- `src/FileParsers.pm` — control/table parsing. +- `src/Commands.pm` — command parser and handlers. +- `src/AI.pm` and `src/AI/CoreLogic.pm` — AI orchestration and decisions. +- `src/Actor.pm` and `src/ActorList.pm` — entity model and indexing. +- `src/Task.pm` and `src/TaskManager.pm` — task abstraction and scheduling. +- `src/Task/Route.pm`, `src/Task/Move.pm`, `src/Task/TalkNPC.pm` — routing, movement, NPC flows. +- `src/Network.pm`, `src/Network/PacketParser.pm`, `src/Network/Receive.pm`, `src/Network/Send.pm` — network core. diff --git a/openkore_llm_knowledge/gpt_upload/22_common_pitfalls.md b/openkore_llm_knowledge/gpt_upload/22_common_pitfalls.md new file mode 100644 index 0000000000..66dbbe6238 --- /dev/null +++ b/openkore_llm_knowledge/gpt_upload/22_common_pitfalls.md @@ -0,0 +1,49 @@ +# Common Pitfalls + +Concise checklist of high-frequency debugging traps in OpenKore architecture and operations. + +## 1) Assuming movement issues are only routing bugs +- AI state locks, command overrides, or task queue starvation can mimic routing failures. +- Check AI/task state before editing route logic. + +## 2) Ignoring serverType/packet table drift +- Packet desync often starts after server updates when recvpackets/opcodes change. +- Keep `control/config.txt` server settings aligned with `tables/*` packet definitions. + +## 3) Debugging plugin hooks without proving event emission +- A hook can be correct but never run if the event is not emitted in the current flow. +- Verify both registration and actual event source path. + +## 4) Treating macro trigger failures as parser failures +- Many cases are valid parse + false trigger conditions. +- Separate: plugin load -> file parse -> trigger true -> action execution. + +## 5) Overlooking config-side constraints +- `control/config.txt`, `mon_control.txt`, and related files can disable or redirect behavior. +- Always test with minimal, known-good config for reproduction. + +## 6) Mixing XKore and DirectConnection assumptions +- Transport/session behavior differs; debugging steps are mode-specific. +- Confirm active mode first before tracing packet/session paths. + +## 7) Trusting visual client state over bot internal state +- In XKore scenarios, client view may lag/diverge from bot state. +- Compare bot-side actor/position updates with forwarded client packets. + +## 8) Investigating deep modules before reproducing minimally +- Large automation stacks (AI + plugin + macro + eventMacro) hide root causes. +- Reproduce with minimal script/task/command to localize fault domain. + +## Fast isolation matrix +| Symptom | First subsystem to verify | Primary files | +|---|---|---| +| Bot not moving | AI/Task | `src/AI.pm`, `src/TaskManager.pm` | +| Route fails | Routing/Field | `src/Task/Route.pm`, `src/Field.pm` | +| NPC fails | Task + Receive | `src/Task/TalkNPC.pm`, `src/Network/Receive.pm` | +| Plugin not loading | Plugin loader | `src/Plugins.pm`, plugin file | +| Hook silent | Event dispatch | `src/Plugins.pm`, event source module | +| Macro not triggering | Macro pipeline | `plugins/macro/*`, `src/Commands.pm` | +| eventMacro issues | eventMacro runner | `plugins/eventMacro/eventMacro/*` | +| Packet desync | Packet mapping | `src/Network/PacketParser.pm`, `tables/*` | +| XKore sync issues | XKore bridge | `src/Network/XKore*.pm` | +| Client view stale | XKore forwarding | `src/Network/XKore*.pm`, `src/ActorList.pm` | diff --git a/openkore_llm_knowledge/gpt_upload/23_debugging_guide.md b/openkore_llm_knowledge/gpt_upload/23_debugging_guide.md new file mode 100644 index 0000000000..ea888e238c --- /dev/null +++ b/openkore_llm_knowledge/gpt_upload/23_debugging_guide.md @@ -0,0 +1,23 @@ +# Debugging Guide (Networking and Tables) + +## 1) Confirm server/profile alignment +- Validate `serverType` and packet profile selection in `control/config.txt`. +- Verify matching packet tables in `tables/*` for the selected server family. + +## 2) Trace receive path +- `src/Network/MessageTokenizer.pm` -> `src/Network/PacketParser.pm` -> `src/Network/Receive.pm` -> `src/Network/Receive/*` +- Look for first packet decode mismatch point. + +## 3) Trace send path +- Inspect command/task origin, then `src/Network/Send.pm` + `src/Network/Send/*` packet construction. +- Confirm opcode/structure expected by current server profile. + +## 4) XKore-specific checks +- Inspect `src/Network/XKore.pm`, `src/Network/XKore2.pm`, `src/Network/XKoreProxy.pm`. +- Validate session phase transitions and packet forwarding symmetry. + +## 5) Iterative workflow +1. Reproduce with minimal actions. +2. Isolate receive vs send failure. +3. Validate serverType/packet table pairing. +4. Re-test after one controlled change. diff --git a/openkore_llm_knowledge/gpt_upload/24_learning_path.md b/openkore_llm_knowledge/gpt_upload/24_learning_path.md new file mode 100644 index 0000000000..f9c988b0e1 --- /dev/null +++ b/openkore_llm_knowledge/gpt_upload/24_learning_path.md @@ -0,0 +1,49 @@ +# Learning Path for New OpenKore Developers + +## Stage 0: Orientation (Day 1) +- Read `01_llm_context.md`, `02_architecture_overview.md`, `03_system_architecture_map.md`. +- Goal: understand receive->state->AI->task->send loop and key directories. + +## Stage 1: Runtime core comprehension (Days 2-3) +- Study `src/functions.pl`, `src/Globals.pm`, `src/Modules.pm`. +- Track one full startup + one main-loop tick. +- Exercise: write a short note describing what runs each tick. + +## Stage 2: Network + actor model (Days 4-5) +- Study `src/Network.pm`, `src/Network/Receive.pm`, `src/Network/Send.pm`, `src/ActorList.pm`. +- Exercise: trace one incoming packet to a world-state mutation. +- Exercise: trace one outgoing action from command/task to send packet. + +## Stage 3: AI and task flow (Days 6-7) +- Study `src/AI.pm`, `src/AI/CoreLogic.pm`, `src/TaskManager.pm`, `src/Task/Route.pm`, `src/Task/TalkNPC.pm`. +- Exercise: map how AI chooses a task and how task completion feeds back. + +## Stage 4: Plugins and automation (Week 2) +- Study `src/Plugins.pm`, `src/Commands.pm`, `plugins/macro/*`, `plugins/eventMacro/*`. +- Build a minimal plugin with one hook and one command. +- Add one macro and one eventMacro automation rule. + +## Stage 5: Config-driven behavior (Week 2) +- Study `control/config.txt`, `control/mon_control.txt`, relevant table files. +- Exercise: change one behavior via config only and document impact. + +## Stage 6: Debugging proficiency (Week 3) +- Use `15_debugging_playbook.md`, `16_debug_decision_trees.md`, `22_common_pitfalls.md`. +- Reproduce and fix one issue in each category: + - movement/routing + - plugin/hook + - macro/eventMacro + - packet/XKore sync + +## Stage 7: Advanced protocol + XKore (Week 3+) +- Study `09_networking_packets.md`, `10_xkore_modes.md`. +- Compare DirectConnection vs XKore2 flow and identify mode-specific risks. + +## Suggested competency checklist +- [ ] Explain OpenKore runtime architecture from memory. +- [ ] Trace packet receive and send paths. +- [ ] Implement and unload a safe plugin. +- [ ] Diagnose macro and eventMacro trigger failures. +- [ ] Diagnose route and movement failures. +- [ ] Diagnose basic packet/serverType desync. +- [ ] Explain XKore sync failure patterns. diff --git a/openkore_llm_knowledge/gpt_upload/26_architecture_diagrams.md b/openkore_llm_knowledge/gpt_upload/26_architecture_diagrams.md new file mode 100644 index 0000000000..54654be8e2 --- /dev/null +++ b/openkore_llm_knowledge/gpt_upload/26_architecture_diagrams.md @@ -0,0 +1,17 @@ +# Architecture Diagrams (Mermaid) + +This file indexes the runtime diagrams for OpenKore subsystem interactions. + +## Included diagrams +1. **AI execution loop** — `06_execution_flows.md` +2. **Packet receive flow** — `09_networking_packets.md` +3. **NPC interaction flow** — `07_npc_interaction_flow.md` +4. **Plugin hook execution** — `27_system_flows.md` +5. **Routing and movement flow** — `08_routing_and_movement.md` +6. **Command execution flow** — `27_system_flows.md` + +## Reading guidance +- Start with `06_execution_flows.md` for the main runtime cycle. +- Use `09_networking_packets.md` and `10_xkore_modes.md` for transport/protocol context. +- Use `07_npc_interaction_flow.md` and `08_routing_and_movement.md` for task-level behavioral flows. +- Use `27_system_flows.md` for extension and control-plane flows. diff --git a/openkore_llm_knowledge/gpt_upload/27_system_flows.md b/openkore_llm_knowledge/gpt_upload/27_system_flows.md new file mode 100644 index 0000000000..61357e961e --- /dev/null +++ b/openkore_llm_knowledge/gpt_upload/27_system_flows.md @@ -0,0 +1,35 @@ +# System Flows + +## Plugin hook execution flow +Plugins extend runtime behavior through hook callbacks registered in `src/Plugins.pm` and commonly invoke `src/Commands.pm` or task APIs. + +```mermaid +flowchart TD + A[Plugin load\nplugins/*.pl] --> B[Plugins::register] + B --> C[Plugins::addHooks] + C --> D[Runtime event\nloop/packet/config] + D --> E[Plugin callback] + E --> F{Action needed?} + F -- No --> G[Return] + F -- Yes --> H[Execute command\nsrc/Commands.pm] + H --> I[Task/Network side effects] + I --> G +``` + +Hook callbacks are event-driven and should remain lightweight; heavy operations are usually delegated to commands/tasks. + +## Command execution flow + +```mermaid +flowchart TD + A[Input source\nconsole/plugin/macro] --> B[Commands::run] + B --> C[Command parser\nsrc/Commands.pm] + C --> D{Built-in or plugin cmd?} + D -- Built-in --> E[Built-in handler] + D -- Plugin --> F[Plugin command callback] + E --> G[Update state / queue task / send packet] + F --> G + G --> H[Feedback to interface/log] +``` + +Commands are a shared control surface across manual operations, plugins, and macro automation. diff --git a/openkore_llm_knowledge/gpt_upload/29_prompt_examples_for_users.md b/openkore_llm_knowledge/gpt_upload/29_prompt_examples_for_users.md new file mode 100644 index 0000000000..ba46ebe919 --- /dev/null +++ b/openkore_llm_knowledge/gpt_upload/29_prompt_examples_for_users.md @@ -0,0 +1,49 @@ +# Prompt Examples for Users + +Realistic prompts users can ask an OpenKore-focused GPT assistant. + +## Architecture and code navigation +1. "Explain how `src/functions.pl` orchestrates the OpenKore main loop." +2. "Map the dependency chain between AI, TaskManager, and Network::Send." +3. "Which modules should I read first to understand actor updates from packets?" +4. "Summarize how plugin hooks interact with command execution." + +## Debugging +5. "My bot stopped moving. Give me a step-by-step diagnosis plan with files to inspect." +6. "How do I distinguish packet desync from route calculation failure?" +7. "Generate a troubleshooting checklist for NPC dialogue failures." +8. "Why are my plugin hooks not firing even though plugin loads fine?" +9. "Help me debug eventMacro that parses but never triggers." +10. "Create a minimal reproduction plan for XKore client sync issues." + +## Plugins +11. "Create a minimal plugin skeleton with register, hooks, and unload cleanup." +12. "Add a custom command `farmstatus` that prints current map and task." +13. "Show best practices for lightweight hook callbacks in OpenKore plugins." +14. "How do I instrument a plugin with debug logs without spamming output?" + +## Macro / eventMacro +15. "Write an automacro that pauses looting above 85% weight." +16. "Create eventMacro rules to send a chat message only once when entering Prontera." +17. "Compare macro and eventMacro for reactive combat behavior." +18. "How can I test whether a macro trigger condition is actually true at runtime?" + +## Routing and movement +19. "Explain why route tasks can oscillate and how to debug it." +20. "Show a script strategy to test short movement segments before full routes." +21. "What configs can block movement even if route exists?" + +## Networking and packets +22. "How do I verify that serverType and recvpackets are correctly configured?" +23. "Trace one incoming movement packet from socket bytes to Actor update." +24. "What logs should I capture when packet decode errors start after a patch?" +25. "Explain where to add temporary logging for packet send construction." + +## Configuration +26. "Review my config goals and suggest a minimal stable `config.txt` baseline." +27. "Which `mon_control.txt` settings commonly interfere with movement tasks?" +28. "How do lockMap and saveMap influence AI behavior?" + +## Learning and onboarding +29. "Build me a 2-week learning plan to become productive in OpenKore development." +30. "Give me a study order for networking, AI, tasks, plugins, and macros with exercises." diff --git a/openkore_llm_knowledge/gpt_upload/30_dataset_summary.md b/openkore_llm_knowledge/gpt_upload/30_dataset_summary.md new file mode 100644 index 0000000000..15acba93bd --- /dev/null +++ b/openkore_llm_knowledge/gpt_upload/30_dataset_summary.md @@ -0,0 +1,37 @@ +# Dataset Summary + +## Purpose +This dataset provides an LLM-oriented knowledge layer for OpenKore architecture, runtime flow, debugging, and practical usage. + +## Coverage areas +- **Architecture core**: context, subsystem boundaries, dependency map, code index. +- **Runtime flows**: AI loop, packet receive flow, NPC interaction, routing/movement, command and plugin hook flows. +- **Networking**: packet pipeline and XKore mode behavior. +- **Debugging**: playbook, decision trees, and common pitfalls. +- **Practical usage**: code recipes, FAQ, glossary, learning path, and user prompt examples. + +## Main artifact groups +1. **Foundational architecture docs** (`01`-`05`, `11`-`14`, `20`, `21`) +2. **Flow/diagram docs** (`06`-`10`, `26`, `27`) +3. **Troubleshooting docs** (`15`, `16`, `22`, `23`) +4. **Developer enablement docs** (`17`, `18`, `19`, `24`, `29`, `30`) + +## Intended users +- Developers onboarding to OpenKore internals. +- Maintainers debugging runtime and packet issues. +- LLM assistants that need stable structural context for responses. + +## Strengths +- Grounded in likely module and directory references from the repository. +- Organized by numbered, task-oriented documents. +- Includes actionable diagnostics and reusable snippet patterns. + +## Known limits +- Documentation is descriptive and may lag upstream implementation changes. +- Server-specific packet behavior can change rapidly after game updates. +- Mermaid diagrams prioritize clarity over exhaustive edge-case detail. + +## Maintenance recommendations +- Re-validate packet/XKore docs after serverType or recvpackets updates. +- Update recipe snippets when plugin/hook APIs evolve. +- Keep FAQ and pitfalls synced with recurring issue reports. diff --git a/openkore_llm_knowledge/gpt_upload/config.zip b/openkore_llm_knowledge/gpt_upload/config.zip new file mode 100644 index 0000000000000000000000000000000000000000..635c12ce5bab5fd264f4a81feab9052d3f25a3c7 GIT binary patch literal 33807 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%0AQ9@l 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 literal 0 HcmV?d00001 diff --git a/openkore_llm_knowledge/gpt_upload/core.zip b/openkore_llm_knowledge/gpt_upload/core.zip new file mode 100644 index 0000000000000000000000000000000000000000..67d31bbcb3b307306e194cf757cd92a4410dd583 GIT binary patch literal 288868 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ⅈ;`v`B~;g?h$<eCI?lET&zuxolODdJD4@SV^` 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%0AiYAA3u4}#OF$|dUo8Y+*$`y`1z>hd^{ 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 YnG3LRcN+zObN4a3D}gNon7gz80g@0p+5i9m literal 0 HcmV?d00001 diff --git a/openkore_llm_knowledge/gpt_upload/networking.zip b/openkore_llm_knowledge/gpt_upload/networking.zip new file mode 100644 index 0000000000000000000000000000000000000000..5c7da6196c5c80defdf4bcd644ee0efe1fcc21d4 GIT binary patch literal 196116 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�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%0Da%eQ$h^7cDQ0HW}7&+ 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�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%0DIx3&N!XEK<a_>`cce~ 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 YfxXS`erA+l06x9VQ)(~YUSPog0a%0e<p2Nx literal 0 HcmV?d00001 diff --git a/openkore_llm_knowledge/gpt_upload/plugins.zip b/openkore_llm_knowledge/gpt_upload/plugins.zip new file mode 100644 index 0000000000000000000000000000000000000000..68f56f22b3b338215f6a5df8f625b5bcdcd9f11e GIT binary patch literal 83077 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 (CEyn>*Pb+8aVA)5Ck!otk8(Is6;;}L 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 literal 0 HcmV?d00001 diff --git a/openkore_llm_knowledge/gpt_upload/tables.zip b/openkore_llm_knowledge/gpt_upload/tables.zip new file mode 100644 index 0000000000000000000000000000000000000000..7790bf3114f19b64d2a4b89d370df248594b24fe GIT binary patch literal 368590 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%0Ac}3obMGmqXqg& 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 GjQ$5+BWuk7 literal 0 HcmV?d00001