浏览代码

Merge pull request #2 from rathena/master

rathena -> fork
anubisK 10 年之前
父节点
当前提交
78f9f797ae
共有 95 个文件被更改,包括 7345 次插入5938 次删除
  1. 16 2
      conf/battle/feature.conf
  2. 3 0
      conf/battle/misc.conf
  3. 16 0
      conf/battle/player.conf
  4. 1 0
      conf/groups.conf
  5. 6 0
      conf/login_athena.conf
  6. 3 3
      conf/msg_conf/map_msg.conf
  7. 39 21
      conf/msg_conf/map_msg_idn.conf
  8. 2 1
      conf/subnet_athena.conf
  9. 1 0
      db/const.txt
  10. 1 0
      db/import-tmpl/item_avail.txt
  11. 1 0
      db/item_avail.txt
  12. 4 4
      db/pre-re/attr_fix.txt
  13. 10 10
      db/pre-re/skill_db.txt
  14. 2 2
      db/pre-re/skill_unit_db.txt
  15. 4 4
      db/re/attr_fix.txt
  16. 20 6
      db/re/item_db.txt
  17. 1 1
      db/re/skill_cast_db.txt
  18. 10 10
      db/re/skill_db.txt
  19. 2 2
      db/re/skill_unit_db.txt
  20. 15 0
      doc/atcommands.txt
  21. 200 0
      doc/mob_db.txt
  22. 25 23
      doc/mob_db_mode_list.txt
  23. 145 0
      doc/skill_require_db.txt
  24. 8 7
      npc/custom/events/cluckers.txt
  25. 270 277
      npc/other/comodo_gambling.txt
  26. 117 137
      npc/quests/first_class/tu_acolyte.txt
  27. 136 171
      npc/quests/first_class/tu_archer.txt
  28. 677 1217
      npc/quests/first_class/tu_ma_th01.txt
  29. 79 112
      npc/quests/first_class/tu_magician01.txt
  30. 347 632
      npc/quests/first_class/tu_merchant.txt
  31. 184 183
      npc/quests/first_class/tu_thief01.txt
  32. 521 393
      npc/quests/quests_airship.txt
  33. 272 242
      npc/quests/quests_moscovia.txt
  34. 10 0
      npc/re/other/stone_change.txt
  35. 3 3
      npc/re/quests/quests_brasilis.txt
  36. 26 91
      npc/warps/dungeons/mosk_dun.txt
  37. 20 6
      sql-files/item_db_re.sql
  38. 1 1
      sql-files/logs.sql
  39. 6 0
      sql-files/main.sql
  40. 1 0
      sql-files/upgrades/upgrade_20140713.sql
  41. 7 0
      sql-files/upgrades/upgrade_20140723.sql
  42. 3 35
      src/char/char.c
  43. 9 0
      src/char/char_clif.c
  44. 2 2
      src/char/char_mapif.c
  45. 1 1
      src/common/mmo.h
  46. 7 1
      src/common/utils.h
  47. 3 0
      src/config/const.h
  48. 115 53
      src/login/account.c
  49. 11 6
      src/login/account.h
  50. 135 103
      src/login/ipban.c
  51. 36 11
      src/login/ipban.h
  52. 116 934
      src/login/login.c
  53. 135 15
      src/login/login.h
  54. 972 0
      src/login/loginchrif.c
  55. 56 0
      src/login/loginchrif.h
  56. 564 0
      src/login/loginclif.c
  57. 42 0
      src/login/loginclif.h
  58. 176 0
      src/login/logincnslif.c
  59. 58 0
      src/login/logincnslif.h
  60. 95 65
      src/login/loginlog.c
  61. 49 3
      src/login/loginlog.h
  62. 9 3
      src/login/sql/CMakeLists.txt
  63. 29 14
      src/map/atcommand.c
  64. 197 254
      src/map/battle.c
  65. 6 2
      src/map/battle.h
  66. 167 76
      src/map/buyingstore.c
  67. 5 2
      src/map/buyingstore.h
  68. 4 2
      src/map/cashshop.c
  69. 9 12
      src/map/chrif.c
  70. 106 78
      src/map/clif.c
  71. 18 2
      src/map/clif.h
  72. 4 4
      src/map/elemental.c
  73. 10 2
      src/map/map.c
  74. 9 5
      src/map/map.h
  75. 4 4
      src/map/mercenary.c
  76. 26 16
      src/map/mob.c
  77. 75 71
      src/map/npc.c
  78. 140 59
      src/map/pc.c
  79. 21 3
      src/map/pc.h
  80. 37 29
      src/map/script.c
  81. 1 2
      src/map/searchstore.c
  82. 265 226
      src/map/skill.c
  83. 4 3
      src/map/skill.h
  84. 140 164
      src/map/status.c
  85. 3 3
      src/map/status.h
  86. 57 36
      src/map/unit.c
  87. 77 47
      src/map/vending.c
  88. 1 1
      src/map/vending.h
  89. 9 3
      vcproj-10/login-server_sql.vcxproj
  90. 23 5
      vcproj-10/login-server_sql.vcxproj.filters
  91. 9 3
      vcproj-12/login-server_sql.vcxproj
  92. 23 5
      vcproj-12/login-server_sql.vcxproj.filters
  93. 9 3
      vcproj-13/login-server_sql.vcxproj
  94. 24 6
      vcproj-13/login-server_sql.vcxproj.filters
  95. 27 3
      vcproj-9/login-server_sql.vcproj

+ 16 - 2
conf/battle/feature.conf

@@ -39,7 +39,21 @@ feature.autotrade: on
 // In which direction should respawned autotraders look?
 // Possible values are from 0-7
 // Default: 4(South)
+// -1 = Last player's direction
 feature.autotrade_direction: 4
 
-// Do you want your autotraders to sit? (Note 1)
-feature.autotrade_sit: yes
+// Change player's head direction?
+// -1 = Last condition
+//  0 = Forward
+//  1 = Right
+//  2 = Left
+feature.autotrade_head_direction: 0
+
+// Do you want your autotraders to sit?
+// -1 = Last player's condition, sitting or standing
+//  0 = Standing
+//  1 = Sitting
+feature.autotrade_sit: 1
+
+// Delay in miliseconds to open vending/buyingsotre after player logged in.
+feature.autotrade_open_delay: 5000

+ 3 - 0
conf/battle/misc.conf

@@ -101,6 +101,9 @@ at_mapflag: no
 // Set this to the amount of minutes autotrade chars will be kicked from the server.
 at_timeout: 0
 
+// Makes player cannot be attacked when autotrade? (turns player's state.monster_ignore) (Note 1)
+at_monsterignore: no
+
 // Auction system, fee per hour. Default is 12000
 auction_feeperhour: 12000
 

+ 16 - 0
conf/battle/player.conf

@@ -229,3 +229,19 @@ fame_pharmacy_3: 1
 fame_pharmacy_5: 3
 fame_pharmacy_7: 10
 fame_pharmacy_10: 50
+
+// How the server should measure the character's idle time? (Note 3)
+// 0x001 - Walk Request
+// 0x002 - UseSkillToID Request (Targetted skill use attempt)
+// 0x004 - UseSkillToPos Request (AoE skill use attempt)
+// 0x008 - UseItem Request (Including equip/unequip)
+// 0x010 - Attack Request
+// 0x020 - Chat Request (Whisper, Party, Guild, Battlegrounds, etc)
+// 0x040 - Sit/Standup Request
+// 0x080 - Emotion Request
+// 0x100 - DropItem Request
+// 0x200 - @/#Command Request
+// Please note that at least 1 option has to be enabled.
+// Be mindful that the more options used, the easier it becomes to cheat features that rely on idletime (e.g. checkidle()).
+// Default: walk (0x1) + useskilltoid (0x2) + useskilltopos (0x4) + useitem (0x8) + attack (0x10) = 0x1F
+idletime_option: 0x1F

+ 1 - 0
conf/groups.conf

@@ -292,6 +292,7 @@ groups: (
 		can_trade_bounded: true
 		item_unconditional: false
 		bypass_stat_onclone: true
+		bypass_max_stat: true
 		/* all_permission: true */
 	}
 }

+ 6 - 0
conf/login_athena.conf

@@ -180,5 +180,11 @@ client_hash_check: off
 //client_hash: 10, cb1ea78023d337c38e8ba5124e2338ae
 //client_hash: 99, disabled
 
+//New registration flood protection [Kevin]
+//Number of new registration allowed
+allowed_regs: 1
+//Time in second before the counter for the number of registration is reset
+time_allowed: 10
+
 import: conf/inter_athena.conf
 import: conf/import/login_conf.txt

+ 3 - 3
conf/msg_conf/map_msg.conf

@@ -392,8 +392,8 @@
 377:  -- Player %s has rejected the duel --
 //etc
 378: Eleanor is now in %s mode.
-379: Able to use %.1f min later.
-380: Able to use %d sec later.
+379: Item Failed. [%s] is cooling down. Wait %.1f minutes.
+380: Item Failed. [%s] is cooling down. Wait %d seconds.
 381: Skill Failed. [%s] requires %dx %s.
 382: You're too close to a stone or emperium to use this skill.
 383: You cannot create a savepoint in an instance.
@@ -404,7 +404,7 @@
 387: The chosen emblem was detected invalid as it contain too much transparency (limit=%d)
 //etc
 388: You cannot use this item while storage is open.
-//389 free
+389: Speed returned to normal.
 //NoAsk
 390: Autorejecting is activated.
 391: Autorejecting is deactivated.

+ 39 - 21
conf/msg_conf/map_msg_idn.conf

@@ -392,9 +392,9 @@
 377:  -- Pemain %s telah menolak permintaan duel --
 //lain-lain
 378: Eleanor sekarang dalam mode %s.
-379: Dapat digunakan setelah %.1f menit.
-380: Dapat digunakan setelah %d detik.
-381: Penggunaan skill gagal. [%s] membutuhkan %dx %s.
+379: Penggunaan item [%s] gagal. Dapat digunakan setelah %1.f menit.
+380: Penggunaan item [%s] gagal. Dapat digunakan setelah %d detik.
+381: Penggunaan skill [%s] gagal. Membutuhkan %dx %s.
 382: Kamu terlalu dekat dengan batu atau emperium untuk menggunakan skill ini.
 383: Kamu tidak dapat membuat save point di instance
 384: Kamu tidak dapat membuat memo di instance
@@ -519,7 +519,25 @@
 // @auction
 517: Sistem Auction tidak tersedia.
 
-//518~534: kosong
+// @itemlist -- continued
+518: Lower Costume Head, 
+519: Top Costume Head, 
+520: Top/Lower Costume Head, 
+521: Mid Costume Head, 
+522: Mid/Lower Costume Head, 
+523: Top/Mid/Lower Costume Head, 
+524: Costume Robe, 
+525: Costume Floor, 
+526: Ammo, 
+527: Shadow Body, 
+528: Shadow Right Hand, 
+529: Shadow Left Hand, 
+530: Shadow Both Hands, 
+531: Shadow Shoes, 
+532: Shadow Right Accessory, 
+533: Shadow Left Accessory, 
+
+//534: kosong
 
 // Pesan yang berhubungan dengan deteksi Bot (Saat ini belum diimplementasikan)
 535: Kemungkinan BOT (99%%) atau client yang digunakan sudah dimodifikasi '%s' (akun: %d, char_id: %d). Pemain ini bisa melihat nama kamu saat kamu sedang tidak terlihat.
@@ -1373,25 +1391,25 @@
 
 // @itemlist
 1332: ------ %s daftar item dari '%s' ------
-1333:  | dipakai: 
-1334: jubah, 
-1335: aksesori kiri, 
-1336: badan/armor, 
-1337: tangan kanan, 
-1338: tangan kiri, 
-1339: kedua tangan, 
-1340: kaki, 
-1341: aksesori kanan, 
-1342: kepala bagian bawah, 
-1343: kepala bagian atas, 
-1344: kepala bagian bawah/atas, 
-1345: kepala bagian tengah, 
-1346: kepala bagian bawah/tengah, 
-1347: kepala bagian bawah/tengah/atas, 
+1333:  | Dipakai: 
+1334: Jubah, 
+1335: Aksesori kiri, 
+1336: Badan/armor, 
+1337: Tangan kanan, 
+1338: Tangan kiri, 
+1339: Kedua tangan, 
+1340: Kaki, 
+1341: Aksesori kanan, 
+1342: Kepala bagian bawah, 
+1343: Kepala bagian atas, 
+1344: Kepala bagian bawah/atas, 
+1345: Kepala bagian tengah, 
+1346: Kepala bagian bawah/tengah, 
+1347: Kepala bagian bawah/tengah/atas, 
 1348:  -> (telur peliharaan, id peliharaan: %u, sudah diberi nama)
 1349:  -> (telur peliharaan, id peliharaan: %u, belum diberi nama)
-1350:  -> (item dibuat, id pembuat: %u, %d star crumb, elemen %d)
-1351:  -> (memproduksi item, id pembuat: %u)
+1350:  -> (item buatan, id pembuat: %u, %d star crumb, elemen %d)
+1351:  -> (item produksi, id pembuat: %u)
 1352:  -> (kartu: 
 1353: Tidak ada item yang ditemukan di %s pemain.
 1354: %d item ditemukan di slot %d %s.

+ 2 - 1
conf/subnet_athena.conf

@@ -1,6 +1,7 @@
 // Subnet support file
 // Format is:
 // subnet: net-submask:char_ip:map_ip
-// you can add more than one subnet 
+// you can add more than one subnet (max 16)
+// check is if((net-submask & char_ip ) == (net-submask & servip)) => ok
 
 subnet: 255.0.0.0:127.0.0.1:127.0.0.1

+ 1 - 0
db/const.txt

@@ -4618,6 +4618,7 @@ IT_AMMO	10
 IT_DELAYCONSUME	11
 IT_CASH	18
 
+Bound_None	0
 Bound_Account	1
 Bound_Guild	2
 Bound_Party	3

+ 1 - 0
db/import-tmpl/item_avail.txt

@@ -9,3 +9,4 @@
 //
 // NOTE: Replaces an item client-side while keeping them separate server-side.
 // Think of it as a way to disguise items.
+// Don't sell the item in same shop with the source. Example, don't put 2240 & 2241 in same place!

+ 1 - 0
db/item_avail.txt

@@ -9,6 +9,7 @@
 //
 // NOTE: Replaces an item client-side while keeping them separate server-side.
 // Think of it as a way to disguise items.
+// Don't sell the item in same shop with the source. Example, don't put 2240 & 2241 in same place!
 
 2240,2241 //Beard - Grampa Beard
 

+ 4 - 4
db/pre-re/attr_fix.txt

@@ -4,7 +4,7 @@
 // Columns - attacker's weapon element
 // Rows    - target's defense element
 
-1,10	// lv1 Attribute table
+1	// lv1 Attribute table
 //Neut Watr Erth Fire Wind Pois Holy Shdw Gho  Und
   100, 100, 100, 100, 100, 100, 100, 100,  25, 100,  // Neutral
   100,  25, 100, 150,  50, 100,  75, 100, 100, 100,  // Water
@@ -17,7 +17,7 @@
    25, 100, 100, 100, 100, 100,  75,  75, 125, 100,  // Ghost
   100, 100, 100, 100, 100,  50, 100,   0, 100,   0,  // Undead
 
-2,10	// lv2 Attribute table
+2	// lv2 Attribute table
 //Neut Watr Erth Fire Wind Pois Holy Shdw Gho  Und
   100, 100, 100, 100, 100, 100, 100, 100,  25, 100,  // Neutral
   100,   0, 100, 175,  25, 100,  50,  75, 100, 100,  // Water
@@ -30,7 +30,7 @@
     0,  75,  75,  75,  75,  75,  50,  50, 150, 125,  // Ghost
   100,  75,  75,  75,  75,  25, 125,   0, 100,   0,  // Undead
 
-3,10	// lv3 Attribute table
+3	// lv3 Attribute table
 //Neut Watr Erth Fire Wind Pois Holy Shdw Gho  Und
   100, 100, 100, 100, 100, 100, 100, 100,   0, 100,  // Neutral
   100, -25, 100, 200,   0, 100,  25,  50, 100, 125,  // Water
@@ -43,7 +43,7 @@
     0,  50,  50,  50,  50,  50,  25,  25, 175, 150,  // Ghost
   100,  50,  50,  50,  50,   0, 150,   0, 100,   0,  // Undead
 
-4,10	// lv4 Attribute table
+4	// lv4 Attribute table
 //Neut Watr Erth Fire Wind Pois Holy Shdw Gho  Und
   100, 100, 100, 100, 100, 100, 100, 100,   0, 100,  // Neutral
   100, -50, 100, 200,   0,  75,   0,  25, 100, 150,  // Water

+ 10 - 10
db/pre-re/skill_db.txt

@@ -228,7 +228,7 @@
 128,0,0,0,0,0,0,10,0,no,0,0,0,misc,0,0x0,		HT_STEELCROW,Steel Crow
 129,5,8,1,0,0x42,1,5,1:2:3:4:5,yes,0,0,0,misc,0,0x80,	HT_BLITZBEAT,Blitz Beat
 130,3:5:7:9,6,2,0,0x3,3,4,1,no,0,0,0,misc,0,0x0,	HT_DETECTING,Detect
-131,4:5:6:7:8,6,32,0,0x1,0,5,1,no,0,0,0,misc,0,0x2000,	HT_SPRINGTRAP,Spring Trap
+131,4:5:6:7:8,6,32,0,0x1,0,5,1,no,0,0,0,misc,0,0x0,	HT_SPRINGTRAP,Spring Trap
 
 //****
 // Assassin
@@ -870,7 +870,7 @@
 1010,9,6,1,0,0x1,0,1,0,no,0,0x1,0,misc,0,0x0,	BA_PANGVOICE,Pang Voice
 1011,9,6,1,0,0x1,0,1,0,no,0,0x1,0,misc,0,0x0,	DC_WINKCHARM,Wink of Charm
 1012,0,0,0,0,0,0,1,0,no,0,0x1,0,weapon,0,0x4000,	BS_UNFAIRLYTRICK,Unfair Trick
-1013,0,6,4,0,0x3,2,1,0,no,0,0x1,0,weapon,0,0x0,	BS_GREED,Greed
+1013,0,6,4,0,0x3,2,1,0,no,0,0x1,0,weapon,0,0x2000,	BS_GREED,Greed
 1014,0,6,4,6,0x3,14,1,0,yes,0,0x1,0,magic,0,0x0,	PR_REDEMPTIO,Redemptio
 1015,9,6,16,0,0x1,0,1,1,no,0,0x401,0,weapon,0,0x0,	MO_KITRANSLATION,Ki Translation
 1016,-1,6,1,-1,0x2,1,1,1,no,0,0x1,0,weapon,5,0x0,	MO_BALKYOUNG,Ki Explosion
@@ -989,7 +989,7 @@
 2235,0,0,0,0,0,0,10,0,no,0,0,0,none,0,0x0,		RA_RANGERMAIN,Ranger Main
 2236,9,8,1,-1,0,0,10,1,yes,0,0,0,weapon,0,0x80,	RA_AIMEDBOLT,Aimed Bolt
 2237,9,6,2,0,0x3,3,1,1,no,0,0,0,none,0,0x2000,		RA_DETONATOR,Detonator
-2238,3,6,2,0,0x3,2,5,1,no,0,0x80,3,misc,0,0x0,	RA_ELECTRICSHOCKER,Electric Shocker
+2238,3,6,2,0,0x3,2,5,1,no,0,0x80,3,misc,0,0x2000,	RA_ELECTRICSHOCKER,Electric Shocker
 2239,3,6,2,0,0x42,3,5,1,no,0,0x80,3,misc,0,0x2800,	RA_CLUSTERBOMB,Cluster Bomb
 2240,0,6,4,0,0,0,1,1,no,0,0,0,none,0,0x0,		RA_WUGMASTERY,Warg Mastery
 2241,0,6,4,0,0,0,3,1,no,0,0,0,none,0,0x2000,		RA_WUGRIDER,Warg Rider
@@ -1000,10 +1000,10 @@
 2246,0,6,4,0,0x2,3:4:5:6:7,5,1,no,0,0,0,weapon,0,0x0,	RA_SENSITIVEKEEN,Sensitive Keen
 2247,0,6,4,0,0x1,0,5,1,no,0,0,0,none,0,0x42,		RA_CAMOUFLAGE,Camouflage
 2248,0,0,0,0,0,0,5,0,no,0,0,0,none,0,0x0,		RA_RESEARCHTRAP,Research Trap
-2249,3,6,2,3,0x43,2,1,1,no,0,0x80,1,misc,0,0x0,	RA_MAGENTATRAP,Magenta Trap
-2250,3,6,2,1,0x43,2,1,1,no,0,0x80,1,misc,0,0x0,	RA_COBALTTRAP,Cobalt Trap
-2251,3,6,2,2,0x43,2,1,1,no,0,0x80,1,misc,0,0x0,	RA_MAIZETRAP,Maize Trap
-2252,3,6,2,4,0x43,2,1,1,no,0,0x80,1,misc,0,0x0,	RA_VERDURETRAP,Verdure Trap
+2249,3,6,2,3,0x43,2,1,1,no,0,0x80,1,misc,0,0x2000,	RA_MAGENTATRAP,Magenta Trap
+2250,3,6,2,1,0x43,2,1,1,no,0,0x80,1,misc,0,0x2000,	RA_COBALTTRAP,Cobalt Trap
+2251,3,6,2,2,0x43,2,1,1,no,0,0x80,1,misc,0,0x2000,	RA_MAIZETRAP,Maize Trap
+2252,3,6,2,4,0x43,2,1,1,no,0,0x80,1,misc,0,0x2000,	RA_VERDURETRAP,Verdure Trap
 2253,3,6,2,3,0x42,2,5,1,no,0,0x80,2,misc,0,0x2800,	RA_FIRINGTRAP,Firing Trap
 2254,3,6,2,1,0x42,2,5,1,no,0,0x80,2,misc,0,0x2800,	RA_ICEBOUNDTRAP,Icebound Trap
 
@@ -1194,7 +1194,7 @@
 2479,9,6,2,0,0,0,5,1,yes,0,0x80,3,misc,0,0x0,	GN_THORNS_TRAP,Thorn Trap
 2480,11,6,1,0,0x1,0,5,1,yes,0,0,3,misc,0,0x0,		GN_BLOOD_SUCKER,Blood Sucker //CHECK Data says its a magic attack. Hmmmm....
 2481,11,6,1,-1,0x2,1:2:3:4:5,5,1,yes,0,0,0,weapon,0,0x0,	GN_SPORE_EXPLOSION,Spore Explosion //CHECK Data says its element is set to neutral. Need to confirm.
-2482,11,6,16,0,0,0,5,1,yes,0,0,1,weapon,2,0x0,	GN_WALLOFTHORN,Wall of Thorns
+2482,11,6,2,0,0x8,0,5,1,yes,0,0,1,weapon,2,0x0,	GN_WALLOFTHORN,Wall of Thorns
 2483,11,6,2,0,0x3,4,10,1,yes,0,0x0,0,weapon,0,0x1,	GN_CRAZYWEED,Crazy Weed
 2484,0,6,2,2,0x2,2,10,1,no,0,0x0,0,weapon,0,0x1,	GN_CRAZYWEED_ATK,Crazy Weed Attack
 2485,9,6,2,3,0,0,5,1,yes,0,0,0,magic,0,0x0,		GN_DEMONIC_FIRE,Demonic Fire
@@ -1341,8 +1341,8 @@
 8019,5,6,1,5,0,0,5,1,no,0,0,0,weapon,0,0x0,		MH_NEEDLE_OF_PARALYZE,Needle of Paralyze
 8020,5,6,2,5,0,0,5,1,no,0,0,1,weapon,0,0x0,		MH_POISON_MIST,Poison Mist
 8021,1,6,16,0,0x1,0,5,1,no,0,0,0,none,0,0x0,		MH_PAIN_KILLER,Pain Killer
-8022,0,6,4,0,0,0x1,5,1,no,0,0,0,none,0,0x0,		MH_LIGHT_OF_REGENE,Light of Regene
-8023,0,6,4,0,0,0x1,5,1,no,0,0,0,none,0,0x0,		MH_OVERED_BOOST,Overed Boost
+8022,0,6,4,0,0x1,0,5,1,no,0,0,0,none,0,0x0,		MH_LIGHT_OF_REGENE,Light of Regene
+8023,0,6,4,0,0x1,0,5,1,no,0,0,0,none,0,0x0,		MH_OVERED_BOOST,Overed Boost
 8024,7,8,1,4:0:4:0:4,0,0,5,-6,yes,0,0,0,magic,0,0x0,	MH_ERASER_CUTTER,Eraser Cutter
 8025,7,8,2,4:0:4:0:4,0x2,2:2:3:3:4,5,-6,no,0,0,0,magic,0,0x0,	MH_XENO_SLASHER,Xeno Slasher
 8026,5:5:7:7:9,6,6,0,0x1,0,5,1,no,0,0,0,magic,0,0x0,	MH_SILENT_BREEZE,Silent Breeze

+ 2 - 2
db/pre-re/skill_unit_db.txt

@@ -151,8 +151,8 @@
 2482,0xe6,0x7f,  0, 1, 100,all,   0x000	//GN_WALLOFTHORN
 2484,0x86,    ,  0, 1, 100,enemy, 0x080	//GN_CRAZYWEED_ATK
 2485,0xe7,    ,  0, 2,2000,enemy, 0x098	//GN_DEMONIC_FIRE
-2487,0xe8,    ,  2, 0,  -1,all,   0x2000	//GN_FIRE_EXPANSION_SMOKE_POWDER
-2488,0xe9,    ,  2, 0,  -1,all,   0x2000	//GN_FIRE_EXPANSION_TEAR_GAS
+2487,0xe8,    ,  2, 0,  -1,enemy, 0x2000	//GN_FIRE_EXPANSION_SMOKE_POWDER
+2488,0xe9,    ,  2, 0,  -1,enemy, 0x2000	//GN_FIRE_EXPANSION_TEAR_GAS
 2490,0xea,    ,  0, 1,1000,enemy, 0x002	//GN_HELLS_PLANT
 
 2555,0x104,   ,  0, 1:2:2:3:3,500,enemy,0x6	//RL_B_TRAP

+ 4 - 4
db/re/attr_fix.txt

@@ -4,7 +4,7 @@
 // Columns - attacker's weapon element
 // Rows    - target's defense element
 
-1,10	// lv1 Attribute table
+1	// lv1 Attribute table
 //Neut Watr Erth Fire Wind Pois Holy Shdw Gho  Und
   100, 100, 100, 100, 100, 100, 100, 100,  70, 100,  // Neutral
   100,  25, 100, 150,  90, 100,  75, 100, 100, 100,  // Water
@@ -17,7 +17,7 @@
    70, 100, 100, 100, 100, 100,  75,  75, 125, 100,  // Ghost
   100, 100, 100, 100, 100,  50, 100,   0, 100,   0,  // Undead
 
-2,10	// lv2 Attribute table
+2	// lv2 Attribute table
 //Neut Watr Erth Fire Wind Pois Holy Shdw Gho  Und
   100, 100, 100, 100, 100, 100, 100, 100,  50, 100,  // Neutral
   100,   0, 100, 175,  80, 100,  50,  75, 100, 100,  // Water
@@ -30,7 +30,7 @@
    50,  75,  75,  75,  75,  75,  50,  50, 150, 125,  // Ghost
   100,  75,  75,  75,  75,  25, 125,   0, 100,   0,  // Undead
 
-3,10	// lv3 Attribute table
+3	// lv3 Attribute table
 //Neut Watr Erth Fire Wind Pois Holy Shdw Gho  Und
   100, 100, 100, 100, 100, 100, 100, 100,   0, 100,  // Neutral
   100, -25, 100, 200,  70, 100,  25,  50, 100, 125,  // Water
@@ -43,7 +43,7 @@
     0,  50,  50,  50,  50,  50,  25,  25, 175, 150,  // Ghost
   100,  50,  50,  50,  50,   0, 150,   0, 100,   0,  // Undead
 
-4,10	// lv4 Attribute table
+4	// lv4 Attribute table
 //Neut Watr Erth Fire Wind Pois Holy Shdw Gho  Und
   100, 100, 100, 100, 100, 100, 100, 100,   0, 100,  // Neutral
   100, -50, 100, 200,  60,  75,   0,  25, 100, 150,  // Water

+ 20 - 6
db/re/item_db.txt

@@ -2457,7 +2457,7 @@
 4412,Isilla_Card,Isilla Card,6,20,,10,,,,,,,,769,,,,,{ bonus bInt,2; autobonus "{ bonus bVariableCastrate,-50; bonus bFlee,30; }",50,5000,BF_MAGIC,"{ specialeffect2 EF_SUFFRAGIUM; }"; },{},{}
 4413,Hodremlin_Card,Hodremlin Card,6,20,,10,,,,,,,,32,,,,,{ bonus2 bSubSize,Size_All,15; autobonus2 "{ bonus bFlee2,30; }",3,10000,BF_WEAPON|BF_MAGIC,"{ specialeffect2 EF_WIND; }"; },{},{}
 4414,Seeker_Card,Seeker Card,6,20,,10,,,,,,,,32,,,,,{ skill "MG_STONECURSE",1; bonus2 bResEff,Eff_Stone,3000; bonus bMdef,10; },{},{}
-4415,Snowier_Card,Snowier Card,6,20,,10,,,,,,,,136,,,,,{ bonus2 bAddMonsterDropItem,536,2000; bonus2 bAddItemHealRate,536,500; },{},{}
+4415,Snowier_Card,Snowier Card,6,20,,10,,,,,,,,136,,,,,{ bonus2 bAddMonsterDropItem,536,500; bonus2 bAddItemHealRate,536,100; },{},{}
 4416,Siroma_Card,Siroma Card,6,20,,10,,,,,,,,136,,,,,{ bonus2 bSkillAtk,"MG_COLDBOLT",25; bonus2 bVariableCastrate,"MG_COLDBOLT",-25; },{},{}
 4417,Ice_Titan_Card,Ice Titan Card,6,20,,10,,,,,,,,64,,,,,{ bonus bVit,2; autobonus2 "{ bonus bDef,10; }",3,10000,BF_WEAPON|BF_MAGIC,"{ specialeffect2 EF_FREEZED; }"; },{},{}
 4418,Gazeti_Card,Gazeti Card,6,20,,10,,,,,,,,136,,,,,{ bonus3 bAutoSpell,"MG_COLDBOLT",2,100; },{},{}
@@ -2500,9 +2500,9 @@
 4456,Nidhogg_Shadow_Card,Nidhoggur Shadow Card,6,20,,10,,,,,,,,16,,,,,{ bonus bInt,5; if (Class == Job_High_Wizard || Class == Job_Baby_Warlock || Class == Job_Warlock || Class == Job_Warlock_T) bonus bFixedCastrate,-50; },{},{}
 4457,Nahtzigger_Card,Naght Sieger Card,6,20,,10,,,,,,,,16,,,,,{ bonus2 bMagicAtkEle,Ele_Ghost,30; },{},{}
 4458,Duneirre_Card,Duneyrr Card,6,20,,10,,,,,,,,769,,,,,{ bonus bBaseAtk,10; autobonus "{ bonus bAspdRate,5; }",10,10000,0,"{ specialeffect2 EF_HASTEUP; }"; },{},{}
-4459,Lata_Card,Rata Card,6,20,,10,,,,,,,,769,,,,,{ bonus bMatk,10; autobonus "{ bonus bFixedCastrate,-50; }",10,4000,BF_MAGIC,"{ specialeffect2 EF_SUFFRAGIUM; }"; },{},{}
-4460,Ringco_Card,Rhyncho Card,6,20,,10,,,,,,,,769,,,,,{ bonus bHealPower,4; bonus bUseSPrate,5; },{},{}
-4461,Pillar_Card,Phylla Card,6,20,,10,,,,,,,,769,,,,,{ bonus bDex,1; bonus bAgi,1; autobonus "{ bonus bCritical,20; }",4,4000,0,"{ specialeffect2 EF_ENHANCE; }"; },{},{}
+4459,Lata_Card,Rata Card,6,20,,10,,,,,,,,769,,,,,{ bonus bMatk,10; autobonus "{ bonus bFixedCastrate,-50; }",5,4000,BF_MAGIC,"{ specialeffect2 EF_SUFFRAGIUM; }"; },{},{}
+4460,Ringco_Card,Rhyncho Card,6,20,,10,,,,,,,,769,,,,,{ bonus bHealPower,4; bonus2 bSkillUseSP,"AL_HEAL",-15; },{},{}
+4461,Pillar_Card,Phylla Card,6,20,,10,,,,,,,,769,,,,,{ bonus bDex,1; bonus bAgi,1; autobonus "{ bonus bCritical,20; }",15,4000,0,"{ specialeffect2 EF_ENHANCE; }"; },{},{}
 4462,Hardrock_Mammos_Card,Hardrock Mammoth Card,6,20,,10,,,,,,,,16,,,,,{ bonus bDef,5; if(getrefine()>=12) { bonus bDef,20; bonus bMaxHPrate,10; } if(getrefine()>=14) { bonus bMaxHPrate,3; } },{},{}
 4463,Tendrilrion_Card,Tendrilrion Card,6,20,,10,,,,,,,,2,,,,,{ bonus bCritical,5; if(getrefine()>=12) { bonus bBaseAtk,35; } if(getrefine()>=14) { bonus bCritical,10; } },{},{}
 4464,Aunoe_Card,Aunoe Card,6,20,,10,,,,,,,,2,,,,,{ bonus bCritAtkRate,20; },{},{}
@@ -4512,6 +4512,7 @@
 6863,Strong_Piece_Of_Soul_Tiger,Strong Piece Of Soul Tiger,3,10,,0,,,,,,,,,,,,,{},{},{}
 6864,Safe_to_19_Weapon_Certificate,Safe to 19 Weapon Certificate,3,10,,0,,,,,,,,,,,,,{},{},{}
 6865,Safe_to_19_Armor_Certificate,Safe to 19 Armor Certificate,3,10,,0,,,,,,,,,,,,,{},{},{}
+6866,Piece_Of_Soul_Rabbit,Piece Of Soul Rabbit,3,0,,0,,,,,,,,,,,,,{},{},{}
 6885,Piece_Of_Soul_Dragon,Piece Of Soul Dragon,3,10,,0,,,,,,,,,,,,,{},{},{}
 6870,Safe_to_13_Weapon_Certificate,Safe to 13 Weapon Certificate,3,10,,0,,,,,,,,,,,,,{},{},{}
 6871,Safe_to_14_Weapon_Certificate,Safe to 14 Weapon Certificate,3,10,,0,,,,,,,,,,,,,{},{},{}
@@ -6189,7 +6190,7 @@
 12421,Falmons_F,Falmons F,3,10,,10,,,,,0xFFFFFFFF,63,2,,,,,,{ sc_start SC_LIFE_FORCE_F,500000,5; },{},{}
 12422,HP_Increase_Potion_(Small),HP Increase Potion (Small),0,10,,10,,,,,0xFFFFFFFF,63,2,,,,,,{ sc_start SC_INCMHP,500000,(500+(BaseLevel*10/3)); percentheal 1,0; },{},{}
 12423,HP_Increase_Potion_(Medium),HP Increase Potion (Medium),0,10,,10,,,,,0xFFFFFFFF,63,2,,,,,,{ sc_start SC_INCMHP,500000,(1500+(BaseLevel*10/3)); percentheal 2,0; },{},{}
-12424,HP_Increase_Potion_(Large),HP Increase Potion (Large),0,10,,10,,,,,0xFFFFFFFF,63,2,,,,,,{ sc_start SC_INCMHP,500000,(2500+(BaseLevel*10/3)); percentheal 5,0; },{},{}
+12424,HP_Increase_Potion_(Large),HP Increase Potion (Large),0,10,,80,,,,,0xFFFFFFFF,63,2,,,,,,{ sc_start SC_INCMHP,500000,(2500+(BaseLevel*10/3)); percentheal 5,0; },{},{}
 12425,SP_Increase_Potion_(Small),SP Increase Potion (Small),0,10,,10,,,,,0xFFFFFFFF,63,2,,,,,,{ sc_start SC_INCMSPRATE,500000,((BaseLevel/10)-5); percentheal 0,2; },{},{}
 12426,SP_Increase_Potion_(Medium),SP Increase Potion (Medium),0,10,,10,,,,,0xFFFFFFFF,63,2,,,,,,{ sc_start SC_INCMSPRATE,500000,(BaseLevel/10); percentheal 0,4; },{},{}
 12427,SP_Increase_Potion_(Large),SP Increase Potion (Large),0,10,,10,,,,,0xFFFFFFFF,63,2,,,,,,{ sc_start SC_INCMSPRATE,500000,((BaseLevel/10)+5); percentheal 0,8; },{},{}
@@ -8188,7 +8189,7 @@
 15121,Sarah_Combat_Robe,Sarah Combat Robe,4,10,,800,,35,,1,0xFFFFFFFF,63,2,16,,145,1,,{ /*TODO: Confirm the real rate and additional MAtk*/ autobonus "{ bonus bMatk,20; }",100,(10+((getrefine()) ? getrefine()*8 : 0))*1000,BF_MAGIC;},{},{}
 
 // More Maces
-16000,Erde,Erde,5,20,,500,130,,1,2,0x0004C5B2,18,2,2,4,50,1,8,{ bonus2 bSkillAtk,"AM_ACIDTERROR",20; bonus2 bSkillAtk,"AM_DEMONSTRATION",20; bonus bMaxSP,50; bonus bHealPower,10; },{},{}
+16000,Erde,Erde,5,20,,500,130,,1,2,0x0004C5B2,18,2,2,3,50,1,8,{ bonus2 bSkillAtk,"AM_ACIDTERROR",20; bonus2 bSkillAtk,"AM_DEMONSTRATION",20; bonus bMaxSP,50; bonus bHealPower,10; },{},{}
 16001,Red_Square_Bag,Red Square Bag,5,20,,500,130,,1,2,0x0004C5B2,18,2,2,3,50,1,8,{ bonus bMaxHP,200; bonus2 bSkillAtk,"AM_ACIDTERROR",20; bonus2 bSkillAtk,"AM_DEMONSTRATION",20; bonus2 bAddMonsterDropItem,501,50; bonus2 bAddMonsterDropItem,502,20; bonus2 bAddMonsterDropItem,503,20; bonus2 bAddMonsterDropItem,504,20; bonus2 bAddMonsterDropItem,505,10; if(readparam(bStr)>=95) bonus2 bAddEff,Eff_Stun,500; },{},{}
 16002,Stunner_C,Stunner,5,0,,0,175,,1,0,0x00008110,63,2,2,3,1,0,8,{ bonus2 bAddEff,Eff_Stun,1000; bonus2 bAddSize,Size_All,40; },{},{}
 16003,Carga_Mace,Carga Mace,5,20,,1500,175,,1,2,0x0004C5B2,63,2,2,3,100,1,8,{},{},{}
@@ -9311,7 +9312,10 @@
 19590,C_Twin_Maiden_Ribbon_J,Maiden's Twin Ribbon,4,20,,0,,0,,0,0xFFFFFFFF,63,2,1024,,0,0,239,{},{},{}
 19598,C_Wandering_Wolf_Hat,Costume Wandering Wolf Hat,4,20,,600,,5,,0,0xFFFFFFFE,63,2,768,,20,0,490,{ bonus bVit,5; bonus bFlee,10; },{},{}
 19599,C_Imp_Hat,Costume Imp Hat,4,20,,400,,1,,0,0xFFFFFFFF,63,2,256,,1,1,589,{ bonus3 bAutoSpell,"SA_FLAMELAUNCHER",1,5; },{},{}
+19600,C_Drooping_Kiehl,Costume Drooping Kiehl,12,0,,40,,,,,0xFFFFFFFF,63,2,1024,,30,,909,{},{},{}
+19601,C_Drooping_Aliot,Costume Drooping Aliot,12,0,,10,,,,,0xFFFFFFFF,63,2,1024,,1,,910,{},{},{}
 19603,C_Invisible_Sunglasses,Costume Invisible Sunglasses,4,0,,0,,0,,0,0xFFFFFFFF,63,2,2048,,1,0,0,{},{},{}
+19604,Brawler's_Supply_Crate,Brawler's Supply Crate,18,0,,0,,,,,0xFFFFFFFF,63,2,,,,,,{ getitem(12208,4); getitem(12072,6); getitem(12082,6); getitem(12087,6); getitem(11502,30); },{},{}
 19607,C_Love_Chick_Hat,Costume Love Chick Hat,4,0,,0,,0,,0,0xFFFFFFFF,63,2,1024,,1,0,500,{},{},{}
 19613,C_Valkyrie_Feather_Hat,Costume Valkyrie Feather Hat,4,10,,0,,,,0,0xFFFFFFFF,63,2,1024,,1,1,300,{ bonus bAllStats,1; skill "PR_MAGNUS",5; skill "SA_FLAMELAUNCHER",1; skill "SA_FROSTWEAPON",1; skill "SA_LIGHTNINGLOADER",1; skill "SA_SEISMICWEAPON",1; },{},{}
 19617,C_Puppy_Headband,Costume Puppy Headband,4,0,,0,,,,,0xFFFFFFFF,63,2,1024,,,,199,{},{},{}
@@ -9411,6 +9415,8 @@
 19800,C_Carnation_Hairband,Costume Carnation Headband,4,10,,0,,,,0,0xFFFFFFFF,63,2,1024,,1,1,878,{},{},{}
 //
 19821,C_Hyegun_hat,C Yao Jun,4,20,,10,,0,,0,0xFFFFFFFF,63,2,1024,,1,0,375,{},{},{}
+19824,C_Evil_Druid_Hat,Costume Evil Druid Hat,12,0,,0,,,,,0xFFFFFFFF,63,2,1024,,1,,1053,{},{},{}
+19825,C_Vicious_Stop_Bandage,Costume Vicious Stop Bandage,12,0,,0,,,,,0xFFFFFFFF,63,2,1024,,1,,1054,{},{},{}
 19826,C_Ice_Wing_Ear,C Ice Wing Ear,4,20,,10,,0,,0,0xFFFFFFFF,63,2,2048,,1,0,584,{},{},{}
 19829,C_Straw_Hat,C Straw Hat,4,20,,0,,0,,0,0xFFFFFFFF,63,2,1024,,0,0,146,{},{},{}
 19830,C_Sunglasses,Costume Sunglasses,4,10,,0,,,,,0xFFFFFFFF,63,2,2048,,1,,12,{},{},{}
@@ -9445,6 +9451,8 @@
 19864,C_Dokebi's_Wig,Dokebi's Wig,4,20,,0,,0,,0,0xFFFFFFFF,63,2,3072,,0,0,302,{},{},{}
 19865,C_Joker_Jester,Joker Jester,4,20,,0,,0,,0,0xFFFFFFFF,63,2,1024,,0,0,89,{},{},{}
 19871,C_DecorationOfMusic_Accessory,Decoration of Music,4,20,,0,,0,,0,0xFFFFFFFF,63,2,2048,,0,0,1074,{ sc_start SC_DECORATION_OF_MUSIC,-1,0; },{},{ sc_end SC_DECORATION_OF_MUSIC; }
+19878,C_Evolved_Drooping_Bunny,Costume Evolved Drooping Bunny,12,0,,0,,,,,0xFFFFFFFF,63,2,1024,,1,,249,{},{},{}
+19883,C_Cyclops_Glasses,Costume Cyclops Glasses,12,0,,0,,,,,0xFFFFFFFF,63,2,2048,,1,,1087,{},{},{}
 19884,C_Vanargand_Helm,Costume Vanargand Helm,4,10,,0,,,,,0xFFFFFFFF,63,2,1024,,1,,804,{},{},{}
 19885,C_Blinker,Costume Blinker,4,10,,0,,,,,0xFFFFFFFF,63,2,2048,,1,,82,{},{},{}
 19886,C_Purple_Glasses,Costume Purple Glasses,4,10,,0,,,,,0xFFFFFFFF,63,2,2048,,1,,26,{},{},{}
@@ -9461,6 +9469,7 @@
 19922,Costume_Noahs_Hat,Costume Noahs Hat,4,0,,0,,0,,0,0xFFFFFFFF,63,2,7168,,1,0,636,{ /* TODO */ },{},{}
 19929,C_Classical_Ribbon,Costume Classical Ribbon,4,10,,0,,,,0,0xFFFFFFFF,63,2,1024,,,1,,{ /*TODO: View ID*/ },{},{}
 19934,C_Lolita_Ten_Gallon_Hat,Costume Alive Ten Gallon Hat Of Flame,4,10,,0,,,,0,0xFFFFFFFF,63,2,1024,,1,1,1048,{},{},{}
+19935,C_Hunting_Cap_Of_Gust,Costume Hunting Cap Of Gust,12,0,,0,,,,,0xFFFFFFFF,63,2,1024,,1,,1076,{},{},{}
 19938,Costume_Love_Rabbit_Hood,Costume Love Rabbit Hood,4,0,,0,,0,,0,0xFFFFFFFF,63,2,7168,,1,0,549,{},{},{}
 19961,C_Rune_Circlet,Costume Rune Circlet,4,10,,0,,,,0,0xFFFFFFFF,63,2,1024,,1,1,623,{},{},{}
 19962,C_Mitra,Costume Mitra,4,10,,0,,,,0,0xFFFFFFFF,63,2,1024,,1,1,624,{},{},{}
@@ -9499,9 +9508,14 @@
 20028,C_Cow_Hat4,Costume Cow Hat 4,4,10,,0,,,,0,0xFFFFFFFF,63,2,1024,,1,1,1214,{ bonus bDex,2; bonus2 bAddMonsterDropItem,519,100; },{},{}
 20034,C_Jack_Castle_Bat,Costume Jack Castle Bat,4,10,,0,,,,0,0xFFFFFFFF,63,2,1024,,1,1,1041,{},{},{}
 20038,C_Alphonse_Helmet,Costume Alphonse Helmet,4,10,,0,,,,0,0xFFFFFFFF,63,2,1024,,,1,,{ /*TODO: View ID*/ },{},{}
+20049,C_Giant_Band_Aid,Costume Giant Band Aid,12,0,,0,,,,,0xFFFFFFFF,63,2,1024,,1,,147,{},{},{}
 20053,C_W_King_Tiger_Doll_Hat,Costume White King Tiger Doll Hat,4,10,,0,,,,0,0xFFFFFFFF,63,2,1024,,1,1,973,{},{},{}
 20063,C_Yellow_Brain_Hat,Costume Yellow Brain Hat,4,10,,0,,,,0,0xFFFFFFFF,63,2,1024,,1,1,1228,{},{},{}
 20064,C_Blue_Brain_Hat,Costume Blue Brain Hat,4,10,,0,,,,0,0xFFFFFFFF,63,2,1024,,1,1,1228,{},{},{}
+20067,C_White_Rabbit_Headband,Costume White Rabbit Headband,12,0,,0,,,,,0xFFFFFFFF,63,2,1024,,1,,719,{},{},{}
+20068,C_Black_Rabbit_Headband,Costume Black Rabbit Headband,12,0,,0,,,,,0xFFFFFFFF,63,2,1024,,1,,718,{},{},{}
+20073,C_Hair_Band,Costume Hair Band,12,0,,0,,,,,0xFFFFFFFF,63,2,1024,,1,,9,{},{},{}
+20074,C_Biretta,Costume_Biretta,12,0,,0,,,,,0xFFFFFFFF,63,2,1024,,1,,11,{},{},{}
 20086,C_Dragon_Cintamani_Hat1,Costume Dragon Cintamani Hat,4,0,,0,,,,0,0xFFFFFFFF,63,2,1024,,1,1,1245,{},{},{}
 20087,C_Dragon_Cintamani_Hat2,Costume Dragon Cintamani Hat,4,0,,0,,,,0,0xFFFFFFFF,63,2,1024,,1,1,1246,{},{},{}
 20088,C_Dragon_Cintamani_Hat3,Costume Dragon Cintamani Hat,4,0,,0,,,,0,0xFFFFFFFF,63,2,1024,,1,1,1247,{},{},{}

+ 1 - 1
db/re/skill_cast_db.txt

@@ -1575,7 +1575,7 @@
 //-- SO_EARTH_INSIGNIA
 2468,1000,0,0,60000,0,60000,1000
 //-- SO_ELEMENTAL_SHIELD
-5008,1000,0,0,0,0,10000,-1
+5008,1000,0,0,10000,0,10000,-1
 //==========================================
 
 //==== Genetic skills ======================

+ 10 - 10
db/re/skill_db.txt

@@ -228,7 +228,7 @@
 128,0,0,0,0,0,0,10,0,no,0,0,0,misc,0,0x0,		HT_STEELCROW,Steel Crow
 129,5,8,1,0,0x42,1,5,1:2:3:4:5,yes,0,0,0,misc,0,0x80,	HT_BLITZBEAT,Blitz Beat
 130,3:5:7:9,6,2,0,0x3,3,4,1,no,0,0,0,misc,0,0x0,	HT_DETECTING,Detect
-131,4:5:6:7:8,6,32,0,0x1,0,5,1,no,0,0,0,misc,0,0x2000,	HT_SPRINGTRAP,Spring Trap
+131,4:5:6:7:8,6,32,0,0x1,0,5,1,no,0,0,0,misc,0,0x0,	HT_SPRINGTRAP,Spring Trap
 
 //****
 // Assassin
@@ -870,7 +870,7 @@
 1010,9,6,1,0,0x1,0,1,0,no,0,0x1,0,misc,0,0x0,	BA_PANGVOICE,Pang Voice
 1011,9,6,1,0,0x1,0,1,0,no,0,0x1,0,misc,0,0x0,	DC_WINKCHARM,Wink of Charm
 1012,0,0,0,0,0,0,1,0,no,0,0x1,0,weapon,0,0x4000,	BS_UNFAIRLYTRICK,Unfair Trick
-1013,0,6,4,0,0x3,2,1,0,no,0,0x1,0,weapon,0,0x0,	BS_GREED,Greed
+1013,0,6,4,0,0x3,2,1,0,no,0,0x1,0,weapon,0,0x2000,	BS_GREED,Greed
 1014,0,6,4,6,0x3,14,1,0,yes,0,0x1,0,magic,0,0x0,	PR_REDEMPTIO,Redemptio
 1015,9,6,16,0,0x1,0,1,1,no,0,0x401,0,weapon,0,0x0,	MO_KITRANSLATION,Ki Translation
 1016,-1,6,1,-1,0x2,1,1,1,no,0,0x1,0,weapon,5,0x0,	MO_BALKYOUNG,Ki Explosion
@@ -989,7 +989,7 @@
 2235,0,0,0,0,0,0,10,0,no,0,0,0,none,0,0x0,		RA_RANGERMAIN,Ranger Main
 2236,9,8,1,-1,0,0,10,1,yes,0,0,0,weapon,0,0x80,	RA_AIMEDBOLT,Aimed Bolt
 2237,9,6,2,0,0x3,3,1,1,no,0,0,0,none,0,0x2000,		RA_DETONATOR,Detonator
-2238,3,6,2,0,0x3,2,5,1,no,0,0x80,3,misc,0,0x0,	RA_ELECTRICSHOCKER,Electric Shocker
+2238,3,6,2,0,0x3,2,5,1,no,0,0x80,3,misc,0,0x2000,	RA_ELECTRICSHOCKER,Electric Shocker
 2239,3,6,2,0,0x42,3,5,1,no,0,0x80,3,misc,0,0x2800,	RA_CLUSTERBOMB,Cluster Bomb
 2240,0,6,4,0,0,0,1,1,no,0,0,0,none,0,0x0,		RA_WUGMASTERY,Warg Mastery
 2241,0,6,4,0,0,0,3,1,no,0,0,0,none,0,0x2000,		RA_WUGRIDER,Warg Rider
@@ -1000,10 +1000,10 @@
 2246,0,6,4,0,0x2,3:4:5:6:7,5,1,no,0,0,0,weapon,0,0x0,	RA_SENSITIVEKEEN,Sensitive Keen
 2247,0,6,4,0,0x1,0,5,1,no,0,0,0,none,0,0x42,		RA_CAMOUFLAGE,Camouflage
 2248,0,0,0,0,0,0,5,0,no,0,0,0,none,0,0x0,		RA_RESEARCHTRAP,Research Trap
-2249,3,6,2,3,0x43,2,1,1,no,0,0x80,1,misc,0,0x0,	RA_MAGENTATRAP,Magenta Trap
-2250,3,6,2,1,0x43,2,1,1,no,0,0x80,1,misc,0,0x0,	RA_COBALTTRAP,Cobalt Trap
-2251,3,6,2,2,0x43,2,1,1,no,0,0x80,1,misc,0,0x0,	RA_MAIZETRAP,Maize Trap
-2252,3,6,2,4,0x43,2,1,1,no,0,0x80,1,misc,0,0x0,	RA_VERDURETRAP,Verdure Trap
+2249,3,6,2,3,0x43,2,1,1,no,0,0x80,1,misc,0,0x2000,	RA_MAGENTATRAP,Magenta Trap
+2250,3,6,2,1,0x43,2,1,1,no,0,0x80,1,misc,0,0x2000,	RA_COBALTTRAP,Cobalt Trap
+2251,3,6,2,2,0x43,2,1,1,no,0,0x80,1,misc,0,0x2000,	RA_MAIZETRAP,Maize Trap
+2252,3,6,2,4,0x43,2,1,1,no,0,0x80,1,misc,0,0x2000,	RA_VERDURETRAP,Verdure Trap
 2253,3,6,2,3,0x42,2,5,1,no,0,0x80,2,misc,0,0x2800,	RA_FIRINGTRAP,Firing Trap
 2254,3,6,2,1,0x42,2,5,1,no,0,0x80,2,misc,0,0x2800,	RA_ICEBOUNDTRAP,Icebound Trap
 
@@ -1194,7 +1194,7 @@
 2479,9,6,2,0,0,0,5,1,yes,0,0x80,3,misc,0,0x0,	GN_THORNS_TRAP,Thorn Trap
 2480,11,6,1,0,0x1,0,5,1,yes,0,0,3,misc,0,0x0,		GN_BLOOD_SUCKER,Blood Sucker //CHECK Data says its a magic attack. Hmmmm....
 2481,11,6,1,-1,0x2,1:2:3:4:5,5,1,yes,0,0,0,weapon,0,0x0,	GN_SPORE_EXPLOSION,Spore Explosion //CHECK Data says its element is set to neutral. Need to confirm.
-2482,11,6,16,0,0,0,5,1,yes,0,0,1,weapon,2,0x0,	GN_WALLOFTHORN,Wall of Thorns
+2482,11,6,2,0,0x8,0,5,1,yes,0,0,1,weapon,2,0x0,	GN_WALLOFTHORN,Wall of Thorns
 2483,11,6,2,0,0x3,4,10,1,yes,0,0x0,0,weapon,0,0x1,	GN_CRAZYWEED,Crazy Weed
 2484,0,6,2,2,0x2,2,10,1,no,0,0x0,0,weapon,0,0x1,	GN_CRAZYWEED_ATK,Crazy Weed Attack
 2485,9,6,2,3,0,0,5,1,yes,0,0,0,magic,0,0x0,		GN_DEMONIC_FIRE,Demonic Fire
@@ -1344,8 +1344,8 @@
 8019,5,6,1,5,0,0,5,1,no,0,0,0,weapon,0,0x0,		MH_NEEDLE_OF_PARALYZE,Needle of Paralyze
 8020,5,6,2,5,0,0,5,1,no,0,0,1,weapon,0,0x0,		MH_POISON_MIST,Poison Mist
 8021,1,6,16,0,0x1,0,5,1,no,0,0,0,none,0,0x0,		MH_PAIN_KILLER,Pain Killer
-8022,0,6,4,0,0,0x1,5,1,no,0,0,0,none,0,0x0,		MH_LIGHT_OF_REGENE,Light of Regene
-8023,0,6,4,0,0,0x1,5,1,no,0,0,0,none,0,0x0,		MH_OVERED_BOOST,Overed Boost
+8022,0,6,4,0,0x1,0,5,1,no,0,0,0,none,0,0x0,		MH_LIGHT_OF_REGENE,Light of Regene
+8023,0,6,4,0,0x1,0,5,1,no,0,0,0,none,0,0x0,		MH_OVERED_BOOST,Overed Boost
 8024,7,8,1,4:0:4:0:4,0,0,5,-6,yes,0,0,0,magic,0,0x0,	MH_ERASER_CUTTER,Eraser Cutter
 8025,7,8,2,4:0:4:0:4,0x2,2:2:3:3:4,5,-6,no,0,0,0,magic,0,0x0,	MH_XENO_SLASHER,Xeno Slasher
 8026,5:5:7:7:9,6,6,0,0x1,0,5,1,no,0,0,0,magic,0,0x0,	MH_SILENT_BREEZE,Silent Breeze

+ 2 - 2
db/re/skill_unit_db.txt

@@ -153,8 +153,8 @@
 2482,0xe6,0x7f,  0, 1, 100,all,   0x000	//GN_WALLOFTHORN
 2484,0x86,    ,  0, 1, 100,enemy, 0x080	//GN_CRAZYWEED_ATK
 2485,0xe7,    ,  0, 2,2000,enemy, 0x098	//GN_DEMONIC_FIRE
-2487,0xe8,    ,  2, 0,  -1,all,   0x2000	//GN_FIRE_EXPANSION_SMOKE_POWDER
-2488,0xe9,    ,  2, 0,  -1,all,   0x2000	//GN_FIRE_EXPANSION_TEAR_GAS
+2487,0xe8,    ,  2, 0,  -1,enemy, 0x2000	//GN_FIRE_EXPANSION_SMOKE_POWDER
+2488,0xe9,    ,  2, 0,  -1,enemy, 0x2000	//GN_FIRE_EXPANSION_TEAR_GAS
 2490,0xea,    ,  0, 1,1000,enemy, 0x002	//GN_HELLS_PLANT
 
 2555,0x104,   ,  0, 1:2:2:3:3,500,enemy,0x6	//RL_B_TRAP

+ 15 - 0
doc/atcommands.txt

@@ -1304,6 +1304,21 @@ Configuration files:
 -- pcdb: Player Settings
 -- statusdb: Status Settings
 
+Affected files:
+-- atcommand: atcommand_athena.conf, groups.conf
+-- battleconf: battle_athena.conf, battle_conf.txt
+-- instancedb: instance_db.txt
+-- itemdb: item_db.txt, item_group_db.txt, item_trade.txt, item_noequip.txt, item_nouse.txt, item_combo_db.txt, item_avail.txt, item_stack.txt, item_delay.txt, item_buyingstore.txt, item_flag.txt
+-- mobdb: mob_db.txt, mob_item_ratio.txt, mob_chat_db.txt, mob_avail.txt, mob_race2_db.txt, mob_branch.txt, mob_poring.txt, mob_boss.txt, mob_pouch.txt, mob_classchange.txt, pet_db.txt, homunculus_db.txt, homun_skill_tree.txt, exp_homun.txt, mercenary_db.txt, mercenary_skill_db.txt, elemental_db.txt, elemental_skill_db.txt
+-- motd: motd.txt
+-- msgconf: atcommand_athena.conf
+-- packetdb: packet_db.txt
+-- pcdb: statpoint.txt, job_exp.txt, skill_tree.txt, attr_fix.txt, job_db1.txt, job_db2.txt, job_basehpsp_db.txt, job_maxhpsp_db.txt, job_param_db.txt, level_penalty.txt
+-- questdb: quest_db.txt
+-- script: /npc/*.txt, /npc/*.conf
+-- skilldb: skill_db.txt, const.txt, skill_require_db.txt, skill_cast_db.txt, skill_castnodex_db.txt, skill_nocast_db.txt, skill_copyable_db.txt, skill_improvise_db.txt, skill_changematerial_db.txt, skill_nonearnpc_db.txt, skill_damage_db.txt, skill_unit_db.txt, abra_db.txt, create_arrow_db.txt, produce_db.txt, spellbook_db.txt, magicmushroom_db.txt
+ -- statusdb: attr_fix.txt, size_fix.txt, refine_db.tx
+
 ---------------------------------------
 
 @set <variable> {<value>}

+ 200 - 0
doc/mob_db.txt

@@ -0,0 +1,200 @@
+//===== rAthena Documentation ================================
+//= rAthena Monster Database Reference
+//===== By: ==================================================
+//= rAthena Dev Team
+//===== Last Updated: ========================================
+//= 20140719
+//===== Description: =========================================
+//= Explanation of the mob_db.txt file and structure.
+//============================================================
+
+ID,Sprite_Name,kROName,iROName,LV,HP,SP,EXP,JEXP,Range1,ATK1,ATK2,DEF,MDEF,STR,AGI,VIT,INT,DEX,LUK,Range2,Range3,Scale,Race,Element,Mode,Speed,aDelay,aMotion,dMotion,MEXP,MVP1id,MVP1per,MVP2id,MVP2per,MVP3id,MVP3per,Drop1id,Drop1per,Drop2id,Drop2per,Drop3id,Drop3per,Drop4id,Drop4per,Drop5id,Drop5per,Drop6id,Drop6per,Drop7id,Drop7per,Drop8id,Drop8per,Drop9id,Drop9per,DropCardid,DropCardper
+
+---------------------------------------
+
+ID: The ID of the monster.
+
+---------------------------------------
+
+SpriteName: The sprite name of the monster (.act & .spr)
+
+---------------------------------------
+
+kROName: The name of the monster, this will be used when you use "--ja--".
+
+---------------------------------------
+
+iROName: The name of the monster, this will be used when you use "--en--".
+
+---------------------------------------
+
+LV: The level of the monster.
+
+---------------------------------------
+
+HP: The HP of the monster.
+
+---------------------------------------
+
+SP: The SP of the monster.
+
+---------------------------------------
+
+EXP: Base experience point of the monster.
+
+---------------------------------------
+
+JEXP: Job experience point of the monster.
+
+---------------------------------------
+
+Range1: Range of the monster attack. If set to 1 or 2, it will be set to melee attack. If set to 3 or more, it will be set to ranged attack.
+
+---------------------------------------
+
+ATK1: The minimum attack of the monster.
+
+---------------------------------------
+
+ATK2: The maximum attack of the monster. If undefined, the value of ATK1 will be used as the absolute attack.
+
+---------------------------------------
+
+DEF: Physical defense of the monster, reduce melee & ranged physical attack/skill.
+
+---------------------------------------
+
+MDEF: Magic defense of the monster, reduce magical skill.
+
+---------------------------------------
+
+STR: Strength of the monster. Affects ATK.
+
+---------------------------------------
+
+AGI: Agility of the monster. Affects FLEE.
+
+---------------------------------------
+
+VIT: Vitality of the monster. Adds additional DEF.
+
+---------------------------------------
+
+INT: Intelligence of the monster. Adds additional MATK.
+
+---------------------------------------
+
+DEX: Dexterity of the monster. Affects HIT rate.
+
+---------------------------------------
+
+LUK: Luck of the monster. Affects Perfect dodge/Lucky flee/Perfect flee/Lucky dodge rate.
+
+---------------------------------------
+
+Range2: Maximum Skill Range.
+
+---------------------------------------
+
+Range3: Sight limit of the monster. If set to 1000 or beyond, the monster will follow you all over the map.
+
+---------------------------------------
+
+Scale: Size of the monster
+
+	0 = Small
+	1 = Medium
+	2 = Large
+
+---------------------------------------
+
+Race: Race of the monster
+
+	0  = Formless
+	1  = Undead
+	2  = Brute
+	3  = Plant
+	4  = Insect
+	5  = Fish
+	6  = Demon
+	7  = Demi-Human
+	8  = Angel
+	9  = Dragon.
+	10 = Player (default race for player)
+
+Demi-Human is not same nor includes Player.
+
+---------------------------------------
+
+Element: Element of the monster, also for Element of db/elemental_db.txt, db/mercenary_db.txt, and db/[pre-]re/homunculus_db.txt
+
+	+-----------+-------+-------+-------+-------+
+	| Type		|  Lv1	|  Lv2	|  Lv3	|  Lv4	|
+	+-----------+-------+-------+-------+-------+
+	| Neutral	|  20	|  40	|  60	|  80	|
+	| Water		|  21	|  41	|  61	|  81	|
+	| Earth		|  22	|  42	|  62	|  82	|
+	| Fire		|  23	|  43	|  63	|  83	|
+	| Wind		|  24	|  44	|  64	|  84	|
+	| Poison	|  25	|  45	|  65	|  85	|
+	| Holy		|  26	|  46	|  66	|  86	|
+	| Shadow	|  27	|  47	|  67	|  87	|
+	| Ghost		|  28	|  48	|  68	|  88	|
+	| Undead	|  29	|  49	|  69	|  89	|
+	+-----------+-------+-------+-------+-------+
+	| For custom start from 30, 50, 70, and 90	|
+	+-----------+-------+-------+-------+-------+
+
+---------------------------------------
+
+Mode: Behaviour of the monster. Full explanation can be found on 'doc/mob_db_mode_list.txt'
+
+---------------------------------------
+
+Speed: Walk speed of the monster
+
+	1		= Fastest
+	100		= Normal
+	1000	= Slowest
+
+---------------------------------------
+
+aDelay: Attack Delay of the monster, also known as ASPD. Low value means faster attack speed, but don't make it too low or it will lag when a player got mobbed by several of these mobs.
+
+---------------------------------------
+
+aMotion: Attack animation motion. Low value means monster's attack will be displayed in higher fps (making it shorter, too) (Thanks to Wallex for this)
+
+---------------------------------------
+
+dMotion: Damage animation motion, same as aMotion but used to display the "I am hit" animation. Coincidentally, this same value is used to determine how long it is before the monster/player can move again. Endure is dMotion = 0, obviously.
+
+---------------------------------------
+
+MEXP: The MVP Experience point the monster gives when it is defeated (to the player who got the MVP reward) (This exp is a percentage of the exp the monster gives.)
+
+---------------------------------------
+
+MVP1-3id: The Item ID of the MVP drop goes here. Maximum of 3 items.
+
+---------------------------------------
+
+MVP1-3per: The rate of the MVP item being dropped, n/10000.
+
+---------------------------------------
+
+Drop1-9id: The Item ID of the drop goes here. Maximum of 9 items.
+
+---------------------------------------
+
+Drop1-9per: The rate of the item being dropped, n/10000.
+
+---------------------------------------
+
+DropCardid: The Item ID of the monster's card (if any).
+
+---------------------------------------
+
+DropCardper: The rate of the card being dropped, n/10000.
+
+---------------------------------------

+ 25 - 23
doc/mob_db_mode_list.txt

@@ -8,33 +8,34 @@
 //= A reference description of rAthena's mob_db 'mode' field.
 //============================================================
 
-Bit Legend:
+Bit Legend
 -------------------------------------------------------------------------------
 
-MD_CANMOVE            | 0x000001 |      1
-MD_LOOTER             | 0x000002 |      2
-MD_AGGRESSIVE         | 0x000004 |      4
-MD_ASSIST             | 0x000008 |      8
-MD_CASTSENSOR_IDLE    | 0x000010 |     16
-MD_BOSS               | 0x000020 |     32
-MD_PLANT              | 0x000040 |     64
-MD_CANATTACK          | 0x000080 |    128
-MD_DETECTOR           | 0x000100 |    256
-MD_CASTSENSOR_CHASE   | 0x000200 |    512
-MD_CHANGECHASE        | 0x000400 |   1024
-MD_ANGRY              | 0x000800 |   2048
-MD_CHANGETARGET_MELEE | 0x001000 |   4096
-MD_CHANGETARGET_CHASE | 0x002000 |   8192
-MD_TARGETWEAK         | 0x004000 |  16384
-MD_RANDOMTARGET       | 0x008000 |  32768
-MD_IGNOREMELEE        | 0x010000 |  65536
-MD_IGNOREMAGIC        | 0x020000 |  131072
-MD_IGNORERANGED       | 0x040000 |  262144
-MD_MVP                | 0x080000 |  524288
+MD_CANMOVE            | 0x000001 |        1
+MD_LOOTER             | 0x000002 |        2
+MD_AGGRESSIVE         | 0x000004 |        4
+MD_ASSIST             | 0x000008 |        8
+MD_CASTSENSOR_IDLE    | 0x000010 |       16
+MD_BOSS               | 0x000020 |       32
+MD_PLANT              | 0x000040 |       64
+MD_CANATTACK          | 0x000080 |      128
+MD_DETECTOR           | 0x000100 |      256
+MD_CASTSENSOR_CHASE   | 0x000200 |      512
+MD_CHANGECHASE        | 0x000400 |     1024
+MD_ANGRY              | 0x000800 |     2048
+MD_CHANGETARGET_MELEE | 0x001000 |     4096
+MD_CHANGETARGET_CHASE | 0x002000 |     8192
+MD_TARGETWEAK         | 0x004000 |    16384
+MD_RANDOMTARGET       | 0x008000 |    32768
+MD_IGNOREMELEE        | 0x010000 |    65536
+MD_IGNOREMAGIC        | 0x020000 |   131072
+MD_IGNORERANGED       | 0x040000 |   262144
+MD_MVP                | 0x080000 |   524288
 MD_IGNOREMISC         | 0x100000 |  1048576
 MD_KNOCKBACK_IMMUNE   | 0x200000 |  2097152
 
-Explanation for modes:
+
+Explanation for modes
 -------------------------------------------------------------------------------
 
 CanMove: Enables the mob to move/chase characters.
@@ -94,7 +95,8 @@ Ignore Misc: The mob will take 1 HP damage from "none" attack type.
 
 Knockback Immune: The mob will be unable to be knocked back.
 
-Aegis Mob Types:
+
+Aegis Mob Types
 -------------------------------------------------------------------------------
 
 What Aegis has are mob-types, where each type represents an AI behavior that

+ 145 - 0
doc/skill_require_db.txt

@@ -0,0 +1,145 @@
+//===== rAthena Documentation ================================
+//= rAthena Skill Requirement Reference
+//===== By: ==================================================
+//= rAthena Dev Team
+//===== Last Updated: ========================================
+//= 20140719
+//===== Description: =========================================
+//= Explanation of the skill_require_db.txt file and structure.
+//============================================================
+
+Structure:
+SkillID,HPCost,MaxHPTrigger,SPCost,HPRateCost,SPRateCost,ZenyCost,RequiredWeapons,RequiredAmmoTypes,RequiredAmmoAmount,RequiredState,RequiredStatuses,SpiritSphereCost,RequiredItemID1,RequiredItemAmount1,RequiredItemID2,RequiredItemAmount2,RequiredItemID3,RequiredItemAmount3,RequiredItemID4,RequiredItemAmount4,RequiredItemID5,RequiredItemAmount5,RequiredItemID6,RequiredItemAmount6,RequiredItemID7,RequiredItemAmount7,RequiredItemID8,RequiredItemAmount8,RequiredItemID9,RequiredItemAmount9,RequiredItemID10,RequiredItemAmount10,RequiredEquipment
+
+---------------------------------------
+
+SkillID: The ID of the skill. (See \db\[re|pre-re]\skill_db.txt for more details)
+
+---------------------------------------
+
+HPCost:	Amount of HP needed to use the skill.
+
+---------------------------------------
+
+MaxHPTrigger: Player's HP has to be below this % of Max HP, in order to use the skill.
+
+---------------------------------------
+
+SPCost: Amount of SP needed to use the skill.
+
+---------------------------------------
+
+HPRateCost: If more than 0, it's a percentage of the player's current HP. If less than 0, it's a percentage of the player's Max HP.
+
+---------------------------------------
+
+SPRateCost: If more than 0, it's a percentage of the player's current SP. If less than 0, it's a percentage of the player's Max SP.
+
+---------------------------------------
+
+ZenyCost: Amount of Zeny needed to use the skill.
+
+---------------------------------------
+
+RequiredWeapons: Player needs to equip this type of weapon, in order to use the skill.
+	0: bare fist
+	1: Daggers
+	2: One-handed swords
+	3: Two-handed swords
+	4: One-handed spears
+	5: Two-handed spears
+	6: One-handed axes
+	7: Two-handed axes
+	8: Maces
+	9: Unused
+	10: Staves
+	11: Bows
+	12: Knuckles
+	13: Musical Instruments
+	14: Whips
+	15: Books
+	16: Katars
+	17: Revolvers
+	18: Rifles
+	19: Gatling guns
+	20: Shotguns
+	21: Grenade launchers
+	22: Fuuma Shurikens
+	23: Two-handed staves
+	24: Max Type
+	25: Dual-wield Daggers
+	26: Dual-wield Swords
+	27: Dual-wield Axes
+	28: Dagger + Sword
+	29: Dagger + Axe
+	30: Sword + Axe
+	
+Example usage of multiple values = type1:type2:type3
+Maximum multiple value is 30.
+
+---------------------------------------
+
+RequiredAmmoTypes: Player needs to equip this type of ammo to use the skill.
+	1: Arrows
+	2: Throwable daggers
+	3: Bullets
+	4: Shells
+	5: Grenades
+	6: Shuriken
+	7: Kunai
+	8: Cannonballs
+	9: Throwable Items (Sling Item)
+
+Example usage of multiple values = type1:type2:type3
+Maximum multiple value is 9.
+---------------------------------------
+
+RequiredAmmoAmount: Amount of ammo needed to use the skill.
+
+---------------------------------------
+
+RequiredState: Specify the 'State' required to be active, in order to use the skill.
+none = Nothing special.
+hidden = Requires hidden status by using Hiding, Cloaking, or Chasewalk.
+riding = Requires the player to ride either a Peco or a Dragon.
+falcon = Requires a Falcon.
+cart = Requires a Pushcart (For renewal, this state can be replaced by SC_PUSH_CART in 'RequiredStatuses' field).
+shield = Requires a shield equipped.
+recover_weight_rate = Requires to be less than 50% weight.
+move_enable = Requires to be able to move.
+water = Requires to be standing on a water cell.
+dragon = Requires to ride a Dragon.
+warg = Requires a Warg.
+ridingwarg = Requires to ride a Warg.
+mado = Requires to have an active Mado.
+elementalspirit = Requires to have an Elemental Spirit summoned.
+peco = Requires riding a Peco.
+
+---------------------------------------
+
+RequiredStatuses: List of status needed to be active, in order to use the skill.
+Example usage of values = SC_STATUS1:SC_STATUS2:SC_STATUS3 (see db/const.txt for more details).
+Maximum multiple value is 3. (Refer to skill.h: MAX_SKILL_STATUS_REQUIRE)
+Use any number or SC_ALL to disable status requirements.
+
+---------------------------------------
+
+SpiritSphereCost: Amount of Spirit Sphere needed to use the skill.
+
+---------------------------------------
+
+RequiredItemID1-10: These items will be used when players use the skill. Maximum of 10 items.
+
+---------------------------------------
+
+RequiredItemAmount1-10: Amount of item used when player uses the skill.
+0 means the item is required to be in inventory but won't be consumed.
+---------------------------------------
+
+RequiredEquipment: Player needs to equip these equipments to use the skill. (Use Item ID).
+Example usage of multiple values = item1:item2:item3
+Maximum multiple value is 10. (Refer to skill.h: MAX_SKILL_EQUIP_REQUIRE)
+
+---------------------------------------
+
+Note: On some fields, ":" delimiter means for each skill level, but there are some level dependent check. (Refer to https://github.com/rathena/rathena/blob/master/src/map/skill.c#L14963)

+ 8 - 7
npc/custom/events/cluckers.txt

@@ -16,6 +16,7 @@
 //= 1.1 Using 'switch rand' instead.
 //= 1.2 Cleaned and standardized. [Euphy]
 //= 1.2a Switched 'atcommand' to 'unitskilluseid'.
+//= 1.3 Switched 'unitskilluseid' to 'unitkill'.
 //============================================================
 
 prontera,156,219,4	script	Cluckers	800,{
@@ -25,20 +26,20 @@ prontera,156,219,4	script	Cluckers	800,{
 	if (.startcluck) {
 		specialeffect2 EF_HIT3;
 		switch(rand(15)) {
-			case 0: npctalk "CLUUUUUUCK!!!"; unitskilluseid getcharid(3),"NPC_SELFDESTRUCTION",1; emotion e_omg; break;
+			case 0: npctalk "CLUUUUUUCK!!!"; unitkill getcharid(3); skilleffect "NPC_SELFDESTRUCTION",1; emotion e_omg; break;
 			case 1: npctalk "Cluuuuuck!~"; break;
-			case 2: unitskilluseid getcharid(3),"NPC_SELFDESTRUCTION",1; break;
+			case 2: unitkill getcharid(3); skilleffect "NPC_SELFDESTRUCTION",1; break;
 			case 3: sc_start SC_Freeze,10000,0; break;
-			case 4: npctalk "CLUUUUUUUUUCK!!!"; unitskilluseid getcharid(3),"NPC_SELFDESTRUCTION",1; emotion e_omg; break;
+			case 4: npctalk "CLUUUUUUUUUCK!!!"; unitkill getcharid(3); skilleffect "NPC_SELFDESTRUCTION",1; emotion e_omg; break;
 			case 5: sc_start SC_Sleep,10000,0; break;
 			case 6: sc_start SC_Stone,10000,0; emotion e_gg; break;
-			case 7: npctalk "CLUUUUUUCK!!!"; unitskilluseid getcharid(3),"NPC_SELFDESTRUCTION",1; emotion e_omg; break;
-			case 8: npctalk "Cluck! CLUUUCK!!"; unitskilluseid getcharid(3),"NPC_SELFDESTRUCTION",1; emotion e_omg; break;
+			case 7: npctalk "CLUUUUUUCK!!!"; unitkill getcharid(3); skilleffect "NPC_SELFDESTRUCTION",1; emotion e_omg; break;
+			case 8: npctalk "Cluck! CLUUUCK!!"; unitkill getcharid(3); skilleffect "NPC_SELFDESTRUCTION",1; emotion e_omg; break;
 			case 9: sc_start SC_Stun,10000,0; break;
 			case 10: sc_start SC_Sleep,10000,0; emotion e_gg; break;
 			case 11: npctalk "Cluck! Cluck!"; break;
 			case 12: sc_start SC_Stun,10000,0; break;
-			case 13: unitskilluseid getcharid(3),"NPC_SELFDESTRUCTION",1; break;
+			case 13: unitkill getcharid(3); skilleffect "NPC_SELFDESTRUCTION",1; break;
 			default:
 				if (rand(50) < 4) {
 					npctalk "WOOF!...........";
@@ -48,7 +49,7 @@ prontera,156,219,4	script	Cluckers	800,{
 					set .startcluck,0;
 				} else {
 					npctalk "Cluck! CLUUUCK!!";
-					unitskilluseid getcharid(3),"NPC_SELFDESTRUCTION",1;
+					unitkill getcharid(3); skilleffect "NPC_SELFDESTRUCTION",1;
 				}
 				break;
 		}

+ 270 - 277
npc/other/comodo_gambling.txt

@@ -3,7 +3,7 @@
 //===== By: ==================================================
 //= Reddozen, Cypress, Zefris
 //===== Current Version: =====================================
-//= 1.4a
+//= 1.4c
 //===== Compatible With: =====================================
 //= rAthena Project
 //===== Description: =========================================
@@ -25,6 +25,7 @@
 //= 1.4 Updated Script to match AEGIS file. [Masao]
 //= 1.4a Minor script updates. [Euphy]
 //= 1.4b Minor fix about Weight and miscs updates. [Capuche]
+//= 1.4c Another clean-up. [Capuche]
 //============================================================ 
 
 comodo,219,158,6	script	Kachua	4_F_02,{
@@ -41,7 +42,6 @@ comodo,219,158,6	script	Kachua	4_F_02,{
 	mes "showed me that diamond,";
 	mes "it's been all I think about!";
 	next;
-
 	if (select("Would you like to have mine?", "Ah, what a shame...") == 2) {
 		mes "[Kachua]";
 		mes "Yes, I know...";
@@ -52,7 +52,6 @@ comodo,219,158,6	script	Kachua	4_F_02,{
 		cutin "katsua01.bmp",255;
 		end;
 	}
-
 	if (countitem(732) == 0) {
 		cutin "katsua01.bmp",255;
 		cutin "katsua03.bmp",2;
@@ -64,7 +63,6 @@ comodo,219,158,6	script	Kachua	4_F_02,{
 		cutin "katsua03.bmp",255;
 		end;
 	}
-
 	mes "[Kachua]";
 	mes "Are you sure you don't mind";
 	mes "giving this to me? Thank you";
@@ -73,7 +71,6 @@ comodo,219,158,6	script	Kachua	4_F_02,{
 	mes "you something from one of my";
 	mes "collections~";
 	next;
-	
 	if (MaxWeight - Weight < 5500) {
 		cutin "katsua01.bmp",255;
 		cutin "katsua03.bmp",2;
@@ -86,300 +83,296 @@ comodo,219,158,6	script	Kachua	4_F_02,{
 		cutin "katsua03.bmp",255;
 		end;
 	}
-
 	if (countitem(732) == 0) { // anti-hack
 		logmes "Hack: Tried to bypass item check.";
 		cutin "katsua01.bmp",255;
 		close;
 	}
 	delitem 732,1;
-
 	mes "[Kachua]";
 	mes "So what would";
 	mes "you like to have?";
 	next;
-
 	switch (select("Weapon", "Armor", "Garment", "Helmet", "Shoes", "Shield")) {
-
 	case 1: // Weapons
-			.@gamble1 = rand(1,1000);
-			if ((.@gamble1 > 920) && (.@gamble1 < 931)) {
-				.@gamble2 = rand(1,85);
-				if      (.@gamble2 ==  1) .@item = 1128; // Haedonggum[2]
-				else if (.@gamble2 ==  2) .@item = 1120; // Tsurugi[2]
-				else if (.@gamble2 ==  3) .@item = 1127; // Saber[3]
-				else if (.@gamble2 ==  4) .@item = 1158; // Two-Handed Sword[2]
-				else if (.@gamble2 ==  5) .@item = 1155; // Bastard Sword[3]
-				else if (.@gamble2 ==  6) .@item = 1220; // Gladius[3]
-				else if (.@gamble2 ==  7) .@item = 1222; // Damascus[1]
-				else if (.@gamble2 ==  8) .@item = 1253; // Katar[2]
-				else if (.@gamble2 ==  9) .@item = 1529; // Iron Driver
-				else if (.@gamble2 == 10) .@item = 1251; // Jur[3]
-				else if (.@gamble2 == 11) .@item = 1361; // Two-Handed Axe[2]
-				else if (.@gamble2 == 12) .@item = 1258; // Katar of Raging Blaze
-				else if (.@gamble2 == 13) .@item = 1257; // Katar of Quaking
-				else if (.@gamble2 == 14) .@item = 1256; // Katar of Frozen Icicle
-				else if (.@gamble2 == 15) .@item = 1259; // Katar of Piercing Wind
-				else if (.@gamble2 == 16) .@item = 1260; // Sharpened Legbone of Ghoul
-				else if (.@gamble2 == 17) .@item = 1716; // Gakkung Bow[2]
-				else if (.@gamble2 == 18) .@item = 1715; // Arbalest[2]
-				else if (.@gamble2 == 19) .@item = 1711; // Crossbow[3]
-				else if (.@gamble2 == 20) .@item = 1702; // Bow[4]
-				else if (.@gamble2 == 21) .@item = 1520; // Chain[3]
-				else if (.@gamble2 == 22) .@item = 1610; // Arc Wand[1]
-				else if (.@gamble2 == 23) .@item = 1615; // Evil Bone Wand
-				else if (.@gamble2 == 24) .@item = 1602; // Rod[4]
-				else if (.@gamble2 == 25) .@item = 1461; // Trident[3]
-				else if (.@gamble2 == 26) .@item = 1402; // Javelin[4]
-				else if (.@gamble2 == 27) .@item = 1961; // Whip[2]
-				else if (.@gamble2 == 28) .@item = 1957; // Rante Whip[1]
-				else if (.@gamble2 == 29) .@item = 1552; // Tablet[1]
-				else if (.@gamble2 == 30) .@item = 1551; // Bible[2]
-				else if (.@gamble2 == 31) .@item = 1553; // Book of Billows
-				else if (.@gamble2 == 32) .@item = 1554; // Book of Mother Earth
-				else if (.@gamble2 == 33) .@item = 1555; // Book of the Blazing Sun
-				else if (.@gamble2 == 34) .@item = 1556; // Book of Gust of Wind
-				else if (.@gamble2 == 35) .@item = 1951; // Rope[4]
-				else if (.@gamble2 == 36) .@item = 1959; // Tail Whip[2]
-				else if (.@gamble2 == 37) .@item = 1953; // Whip[3]
-				else if (.@gamble2 == 38) .@item = 1955; // Wire Whip[3]
-				else if (.@gamble2 == 39) .@item = 1810; // Claw[2]
-				else if (.@gamble2 == 40) .@item = 1910; // Harp[2]
-				else if (.@gamble2 == 41) .@item = 1906; // Lute[3]
-				else if (.@gamble2 == 42) .@item = 1902; // Violin[4]
-				else if (.@gamble2 == 43) .@item = 1904; // Mandolin[3]
-				else if (.@gamble2 == 44) .@item = 1912; // Gumoongoh[2]
-				else if (.@gamble2 == 45) .@item = 1908; // Guitar[1]
-				else if (.@gamble2 == 46) .@item = 1808; // Fist[1]
-				else if (.@gamble2 == 47) .@item = 1802; // Waghnak[4]
-				else if (.@gamble2 == 48) .@item = 1812; // Finger[2]
-				else if (.@gamble2 == 49) .@item = 1806; // Studded Knuckles[3]
-				else if (.@gamble2 == 50) .@item = 1804; // Knuckle Duster[3]
-				else if (.@gamble2 == 51) .@item = 1550; // Book[3]
-				else if (.@gamble2 == 52) .@item = 1246; // Cinquedea[2]
-				else if (.@gamble2 == 53) .@item = 1147; // Town Sword
-				else if (.@gamble2 > 53 && .@gamble2 < 56) .@item = 1264; // Specialty Jur[4]
-				else if (.@gamble2 > 55 && .@gamble2 < 58) .@item = 1262; // Loki's Nail
-				else if (.@gamble2 > 57 && .@gamble2 < 60) .@item = 1622; // Hypnotist's Staff[2]
-				else if (.@gamble2 == 60) .@item = 1723; // Luna Bow[2]
-				else if (.@gamble2 > 60 && .@gamble2 < 63) .@item = 1965; // Red Flame Whip
-				else if (.@gamble2 > 62 && .@gamble2 < 65) .@item = 1966; // Icicle Whip
-				else if (.@gamble2 > 64 && .@gamble2 < 67) .@item = 1967; // Gaia Whip
-				else if (.@gamble2 > 66 && .@gamble2 < 69) .@item = 1968; // Skipping Rope
-				else if (.@gamble2 > 68 && .@gamble2 < 71) .@item = 1914; // Burning Passion Guitar
-				else if (.@gamble2 > 70 && .@gamble2 < 73) .@item = 1915; // Loner's Guitar
-				else if (.@gamble2 > 72 && .@gamble2 < 75) .@item = 1916; // Green Acre Guitar
-				else if (.@gamble2 > 74 && .@gamble2 < 77) .@item = 1917; // Gentle Breeze Guitar
-				else if (.@gamble2 > 76 && .@gamble2 < 79) .@item = 13004; // Cowardice Blade[2]
-				else if (.@gamble2 > 78 && .@gamble2 < 81) .@item = 1307; // Windhawk
-				else if (.@gamble2 == 81) .@item = 1560; // Sage's Diary[2]
-				else if (.@gamble2 == 82) .@item = 1618; // Survivor's Rod[1]
-				else if (.@gamble2 == 83) .@item = 1620; // Survivor's Rod[1]
-				else if (.@gamble2 > 83 && .@gamble2 < 86) .@item = 1971; // Electric Wire
-			}
-			else if ((.@gamble1 >   0) && (.@gamble1 <  201)) .@item = 1201; // Knife[3]
-			else if ((.@gamble1 > 200) && (.@gamble1 <  301)) .@item = 1101; // Sword[3]
-			else if ((.@gamble1 > 300) && (.@gamble1 <  401)) .@item = 1601; // Rod[3]
-			else if ((.@gamble1 > 400) && (.@gamble1 <  501)) .@item = 1116; // Katana[3]
-			else if ((.@gamble1 > 500) && (.@gamble1 <  601)) .@item = 1250; // Jur[2]
-			else if ((.@gamble1 > 600) && (.@gamble1 <  701)) .@item = 1301; // Axe[3]
-			else if ((.@gamble1 > 700) && (.@gamble1 <  801)) .@item = 1701; // Bow[3]
-			else if ((.@gamble1 > 800) && (.@gamble1 <  851)) .@item = 1504; // Mace[3]
-			else if ((.@gamble1 > 850) && (.@gamble1 <  901)) .@item = 1604; // Wand[2]
-			else if ((.@gamble1 > 900) && (.@gamble1 <  911)) .@item = 1108; // Blade[4]
-			else if ((.@gamble1 > 910) && (.@gamble1 <  921)) .@item = 1163; // Claymore
-			else if ((.@gamble1 > 930) && (.@gamble1 <  961)) .@item = 1522; // Stunner
-			else if ((.@gamble1 > 960) && (.@gamble1 <  971)) .@item = 1608; // Staff[3]
-			else if ((.@gamble1 > 970) && (.@gamble1 <  981)) .@item = 1408; // Pike[4]
-			else if ((.@gamble1 > 980) && (.@gamble1 <  991)) .@item = 1452; // Guisarme[3]
-			else if ((.@gamble1 > 990) && (.@gamble1 < 1001)) .@item = 1208; // Main Gauche[4]
-	break;
+		.@gamble1 = rand(1,1000);
+		if (.@gamble1 > 920 && .@gamble1 < 931) {
+			.@gamble2 = rand(1,85);
+			if      (.@gamble2 ==  1) .@item = 1128; // Haedonggum[2]
+			else if (.@gamble2 ==  2) .@item = 1120; // Tsurugi[2]
+			else if (.@gamble2 ==  3) .@item = 1127; // Saber[3]
+			else if (.@gamble2 ==  4) .@item = 1158; // Two-Handed Sword[2]
+			else if (.@gamble2 ==  5) .@item = 1155; // Bastard Sword[3]
+			else if (.@gamble2 ==  6) .@item = 1220; // Gladius[3]
+			else if (.@gamble2 ==  7) .@item = 1222; // Damascus[1]
+			else if (.@gamble2 ==  8) .@item = 1253; // Katar[2]
+			else if (.@gamble2 ==  9) .@item = 1529; // Iron Driver
+			else if (.@gamble2 == 10) .@item = 1251; // Jur[3]
+			else if (.@gamble2 == 11) .@item = 1361; // Two-Handed Axe[2]
+			else if (.@gamble2 == 12) .@item = 1258; // Katar of Raging Blaze
+			else if (.@gamble2 == 13) .@item = 1257; // Katar of Quaking
+			else if (.@gamble2 == 14) .@item = 1256; // Katar of Frozen Icicle
+			else if (.@gamble2 == 15) .@item = 1259; // Katar of Piercing Wind
+			else if (.@gamble2 == 16) .@item = 1260; // Sharpened Legbone of Ghoul
+			else if (.@gamble2 == 17) .@item = 1716; // Gakkung Bow[2]
+			else if (.@gamble2 == 18) .@item = 1715; // Arbalest[2]
+			else if (.@gamble2 == 19) .@item = 1711; // Crossbow[3]
+			else if (.@gamble2 == 20) .@item = 1702; // Bow[4]
+			else if (.@gamble2 == 21) .@item = 1520; // Chain[3]
+			else if (.@gamble2 == 22) .@item = 1610; // Arc Wand[1]
+			else if (.@gamble2 == 23) .@item = 1615; // Evil Bone Wand
+			else if (.@gamble2 == 24) .@item = 1602; // Rod[4]
+			else if (.@gamble2 == 25) .@item = 1461; // Trident[3]
+			else if (.@gamble2 == 26) .@item = 1402; // Javelin[4]
+			else if (.@gamble2 == 27) .@item = 1961; // Whip[2]
+			else if (.@gamble2 == 28) .@item = 1957; // Rante Whip[1]
+			else if (.@gamble2 == 29) .@item = 1552; // Tablet[1]
+			else if (.@gamble2 == 30) .@item = 1551; // Bible[2]
+			else if (.@gamble2 == 31) .@item = 1553; // Book of Billows
+			else if (.@gamble2 == 32) .@item = 1554; // Book of Mother Earth
+			else if (.@gamble2 == 33) .@item = 1555; // Book of the Blazing Sun
+			else if (.@gamble2 == 34) .@item = 1556; // Book of Gust of Wind
+			else if (.@gamble2 == 35) .@item = 1951; // Rope[4]
+			else if (.@gamble2 == 36) .@item = 1959; // Tail Whip[2]
+			else if (.@gamble2 == 37) .@item = 1953; // Whip[3]
+			else if (.@gamble2 == 38) .@item = 1955; // Wire Whip[3]
+			else if (.@gamble2 == 39) .@item = 1810; // Claw[2]
+			else if (.@gamble2 == 40) .@item = 1910; // Harp[2]
+			else if (.@gamble2 == 41) .@item = 1906; // Lute[3]
+			else if (.@gamble2 == 42) .@item = 1902; // Violin[4]
+			else if (.@gamble2 == 43) .@item = 1904; // Mandolin[3]
+			else if (.@gamble2 == 44) .@item = 1912; // Gumoongoh[2]
+			else if (.@gamble2 == 45) .@item = 1908; // Guitar[1]
+			else if (.@gamble2 == 46) .@item = 1808; // Fist[1]
+			else if (.@gamble2 == 47) .@item = 1802; // Waghnak[4]
+			else if (.@gamble2 == 48) .@item = 1812; // Finger[2]
+			else if (.@gamble2 == 49) .@item = 1806; // Studded Knuckles[3]
+			else if (.@gamble2 == 50) .@item = 1804; // Knuckle Duster[3]
+			else if (.@gamble2 == 51) .@item = 1550; // Book[3]
+			else if (.@gamble2 == 52) .@item = 1246; // Cinquedea[2]
+			else if (.@gamble2 == 53) .@item = 1147; // Town Sword
+			else if (.@gamble2 < 56) .@item = 1264;	// Specialty Jur[4]
+			else if (.@gamble2 < 58) .@item = 1262; // Loki's Nail
+			else if (.@gamble2 < 60) .@item = 1622; // Hypnotist's Staff[2]
+			else if (.@gamble2 == 60) .@item = 1723;// Luna Bow[2]
+			else if (.@gamble2 < 63) .@item = 1965; // Red Flame Whip
+			else if (.@gamble2 < 65) .@item = 1966; // Icicle Whip
+			else if (.@gamble2 < 67) .@item = 1967; // Gaia Whip
+			else if (.@gamble2 < 69) .@item = 1968; // Skipping Rope
+			else if (.@gamble2 < 71) .@item = 1914; // Burning Passion Guitar
+			else if (.@gamble2 < 73) .@item = 1915; // Loner's Guitar
+			else if (.@gamble2 < 75) .@item = 1916; // Green Acre Guitar
+			else if (.@gamble2 < 77) .@item = 1917; // Gentle Breeze Guitar
+			else if (.@gamble2 < 79) .@item = 13004; // Cowardice Blade[2]
+			else if (.@gamble2 < 81) .@item = 1307; // Windhawk
+			else if (.@gamble2 == 81) .@item = 1560; // Sage's Diary[2]
+			else if (.@gamble2 == 82) .@item = 1618; // Survivor's Rod[1]
+			else if (.@gamble2 == 83) .@item = 1620; // Survivor's Rod[1]
+			else if (.@gamble2 < 86) .@item = 1971; // Electric Wire
+		}
+		else if (.@gamble1 <  201) .@item = 1201; // Knife[3]
+		else if (.@gamble1 <  301) .@item = 1101; // Sword[3]
+		else if (.@gamble1 <  401) .@item = 1601; // Rod[3]
+		else if (.@gamble1 <  501) .@item = 1116; // Katana[3]
+		else if (.@gamble1 <  601) .@item = 1250; // Jur[2]
+		else if (.@gamble1 <  701) .@item = 1301; // Axe[3]
+		else if (.@gamble1 <  801) .@item = 1701; // Bow[3]
+		else if (.@gamble1 <  851) .@item = 1504; // Mace[3]
+		else if (.@gamble1 <  901) .@item = 1604; // Wand[2]
+		else if (.@gamble1 <  911) .@item = 1108; // Blade[4]
+		else if (.@gamble1 <  921) .@item = 1163; // Claymore
+		else if (.@gamble1 <  961) .@item = 1522; // Stunner
+		else if (.@gamble1 <  971) .@item = 1608; // Staff[3]
+		else if (.@gamble1 <  981) .@item = 1408; // Pike[4]
+		else if (.@gamble1 <  991) .@item = 1452; // Guisarme[3]
+		else if (.@gamble1 < 1001) .@item = 1208; // Main Gauche[4]
+		break;
 
 	case 2: // Armors
-			.@gamble1 = rand(1,500);
-			if ((.@gamble1 > 299) && (.@gamble1 < 303)) {
-				.@gamble2 = rand(1,30);
-				if      ((.@gamble2 >  0) && (.@gamble2 <  3)) .@item = 2315; // Chain Mail[1]
-				else if ((.@gamble2 >  2) && (.@gamble2 <  5)) .@item = 2336; // Thief Clothes[1]
-				else if ((.@gamble2 >  4) && (.@gamble2 <  7)) .@item = 2318; // Lord's Clothes[1]
-				else if ((.@gamble2 >  6) && (.@gamble2 <  9)) .@item = 2326; // Saint's Robe[1]
-				else if ((.@gamble2 >  8) && (.@gamble2 < 11)) .@item = 2327; // Holy Robe
-				else if ((.@gamble2 > 10) && (.@gamble2 < 13)) .@item = 2342; // Legion Plate Armor[1]
-				else if ((.@gamble2 > 12) && (.@gamble2 < 15)) .@item = 2331; // Tights[1]
-				else if ((.@gamble2 > 14) && (.@gamble2 < 17)) .@item = 2342; // Legion Plate Armor[1]
-				else if ((.@gamble2 > 16) && (.@gamble2 < 19)) .@item = 2311; // Mink Coat[1]
-				else if ((.@gamble2 > 18) && (.@gamble2 < 21)) .@item = 2320; // Formal Suit[1]
-				else if ((.@gamble2 > 20) && (.@gamble2 < 23)) .@item = 2319; // Glittering Jacket[1]
-				else if ((.@gamble2 > 22) && (.@gamble2 < 25)) .@item = 2344; // Lucius's Fierce Armor of Volcano
-				else if ((.@gamble2 > 24) && (.@gamble2 < 27)) .@item = 2346; // Saphien's Armor of Ocean
-				else if ((.@gamble2 > 26) && (.@gamble2 < 29)) .@item = 2348; // Aebeccee's Raging Typhoon Armor
-				else if ((.@gamble2 > 28) && (.@gamble2 < 31)) .@item = 2350; // Claytos Cracking Earth Armor
-			}
-			else if ((.@gamble1 >   0) && (.@gamble1 <  51)) .@item = 2301; // Cotton Shirt
-			else if ((.@gamble1 >  50) && (.@gamble1 < 101)) .@item = 2302; // Cotton Shirt[1]
-			else if ((.@gamble1 > 100) && (.@gamble1 < 151)) .@item = 2303; // Jacket
-			else if ((.@gamble1 > 150) && (.@gamble1 < 201)) .@item = 2304; // Jacket[1]
-			else if ((.@gamble1 > 200) && (.@gamble1 < 251)) .@item = 2305; // Adventurer's Suit
-			else if ((.@gamble1 > 250) && (.@gamble1 < 300)) .@item = 2301; // Cotton Shirt
-			else if ((.@gamble1 > 301) && (.@gamble1 < 351)) .@item = 2307; // Mantle
-			else if ((.@gamble1 > 350) && (.@gamble1 < 401)) .@item = 2309; // Coat
-			else if ((.@gamble1 > 400) && (.@gamble1 < 402)) .@item = 2322; // Silk Robe[1]
-			else if ((.@gamble1 > 401) && (.@gamble1 < 403)) .@item = 2310; // Coat[1]
-			else if ((.@gamble1 > 402) && (.@gamble1 < 411)) .@item = 2306; // Adventurer's Suit[1]
-			else if ((.@gamble1 > 410) && (.@gamble1 < 416)) .@item = 2308; // Mantle[1]
-			else if ((.@gamble1 > 415) && (.@gamble1 < 421)) .@item = 2313; // Padded Armor[1]
-			else if ((.@gamble1 > 420) && (.@gamble1 < 426)) .@item = 2337; // Ninja Suit
-			else if ((.@gamble1 > 425) && (.@gamble1 < 431)) .@item = 2341; // Legion Plate Armor
-			else if ((.@gamble1 > 430) && (.@gamble1 < 436)) .@item = 2325; // Saint's Robe
-			else if ((.@gamble1 > 435) && (.@gamble1 < 441)) .@item = 2317; // Full Plate
-			else if ((.@gamble1 > 440) && (.@gamble1 < 446)) .@item = 2330; // Tights
-			else if ((.@gamble1 > 445) && (.@gamble1 < 451)) .@item = 2314; // Chain Mail
-			else if ((.@gamble1 > 450) && (.@gamble1 < 456)) .@item = 2335; // Thief Clothes
-			else if ((.@gamble1 > 455) && (.@gamble1 < 461)) .@item = 2324; // Scapulare[1]
-			else if ((.@gamble1 > 460) && (.@gamble1 < 466)) .@item = 2329; // Wooden Mail[1]
-			else if ((.@gamble1 > 465) && (.@gamble1 < 471)) .@item = 2340; // Novice Breastplate[1]
-			else if ((.@gamble1 > 470) && (.@gamble1 < 476)) .@item = 2312; // Padded Armor
-			else if ((.@gamble1 > 475) && (.@gamble1 < 481)) .@item = 2339; // Pantie
-			else if ((.@gamble1 > 480) && (.@gamble1 < 486)) .@item = 2328; // Wooden Mail
-			else if ((.@gamble1 > 485) && (.@gamble1 < 491)) .@item = 2321; // Silk Robe
-			else if ((.@gamble1 > 490) && (.@gamble1 < 501)) .@item = 2323; // Scapulare
-	break;
+		.@gamble1 = rand(1,500);
+		if (.@gamble1 > 299 && .@gamble1 < 303) {
+			.@gamble2 = rand(1,30);
+			if      (.@gamble2 <  3) .@item = 2315; // Chain Mail[1]
+			else if (.@gamble2 <  5) .@item = 2336; // Thief Clothes[1]
+			else if (.@gamble2 <  7) .@item = 2318; // Lord's Clothes[1]
+			else if (.@gamble2 <  9) .@item = 2326; // Saint's Robe[1]
+			else if (.@gamble2 < 11) .@item = 2327; // Holy Robe
+			else if (.@gamble2 < 13) .@item = 2342; // Legion Plate Armor[1]
+			else if (.@gamble2 < 15) .@item = 2331; // Tights[1]
+			else if (.@gamble2 < 17) .@item = 2342; // Legion Plate Armor[1]
+			else if (.@gamble2 < 19) .@item = 2311; // Mink Coat[1]
+			else if (.@gamble2 < 21) .@item = 2320; // Formal Suit[1]
+			else if (.@gamble2 < 23) .@item = 2319; // Glittering Jacket[1]
+			else if (.@gamble2 < 25) .@item = 2344; // Lucius's Fierce Armor of Volcano
+			else if (.@gamble2 < 27) .@item = 2346; // Saphien's Armor of Ocean
+			else if (.@gamble2 < 29) .@item = 2348; // Aebeccee's Raging Typhoon Armor
+			else if (.@gamble2 < 31) .@item = 2350; // Claytos Cracking Earth Armor
+		}
+		else if (.@gamble1 <  51) .@item = 2301; // Cotton Shirt
+		else if (.@gamble1 < 101) .@item = 2302; // Cotton Shirt[1]
+		else if (.@gamble1 < 151) .@item = 2303; // Jacket
+		else if (.@gamble1 < 201) .@item = 2304; // Jacket[1]
+		else if (.@gamble1 < 251) .@item = 2305; // Adventurer's Suit
+		else if (.@gamble1 < 300) .@item = 2301; // Cotton Shirt
+		else if (.@gamble1 < 351) .@item = 2307; // Mantle
+		else if (.@gamble1 < 401) .@item = 2309; // Coat
+		else if (.@gamble1 < 402) .@item = 2322; // Silk Robe[1]
+		else if (.@gamble1 < 403) .@item = 2310; // Coat[1]
+		else if (.@gamble1 < 411) .@item = 2306; // Adventurer's Suit[1]
+		else if (.@gamble1 < 416) .@item = 2308; // Mantle[1]
+		else if (.@gamble1 < 421) .@item = 2313; // Padded Armor[1]
+		else if (.@gamble1 < 426) .@item = 2337; // Ninja Suit
+		else if (.@gamble1 < 431) .@item = 2341; // Legion Plate Armor
+		else if (.@gamble1 < 436) .@item = 2325; // Saint's Robe
+		else if (.@gamble1 < 441) .@item = 2317; // Full Plate
+		else if (.@gamble1 < 446) .@item = 2330; // Tights
+		else if (.@gamble1 < 451) .@item = 2314; // Chain Mail
+		else if (.@gamble1 < 456) .@item = 2335; // Thief Clothes
+		else if (.@gamble1 < 461) .@item = 2324; // Scapulare[1]
+		else if (.@gamble1 < 466) .@item = 2329; // Wooden Mail[1]
+		else if (.@gamble1 < 471) .@item = 2340; // Novice Breastplate[1]
+		else if (.@gamble1 < 476) .@item = 2312; // Padded Armor
+		else if (.@gamble1 < 481) .@item = 2339; // Pantie
+		else if (.@gamble1 < 486) .@item = 2328; // Wooden Mail
+		else if (.@gamble1 < 491) .@item = 2321; // Silk Robe
+		else if (.@gamble1 < 501) .@item = 2323; // Scapulare
+		break;
 
 	case 3:  // Garments
-			.@gamble1 = rand(1,500);
-			if ((.@gamble1 > 200) && (.@gamble1 < 204)) {
-				.@gamble2 = rand(1,16);
-				if      ((.@gamble2 >  0) && (.@gamble2 <  3)) .@item = 2506; // Manteau[1]
-				else if ((.@gamble2 >  2) && (.@gamble2 <  5)) .@item = 2504; // Muffler[1]
-				else if ((.@gamble2 >  4) && (.@gamble2 <  8)) .@item = 2508; // Ragamuffin Manteau
-				else if ((.@gamble2 >  7) && (.@gamble2 < 11)) .@item = 2507; // Ancient Cape
-				else if (.@gamble2 == 11) .@item = 2513; // Heavenly Maiden Robe[1]
-				else if (.@gamble2 == 12) .@item = 2514; // Pauldron[1]
-				else if (.@gamble2 == 13) .@item = 2523; // Undershirt[1]
-				else if (.@gamble2 == 14) .@item = 2530; // Rider Insigna[1]
-				else if (.@gamble2 == 15) .@item = 2509; // Survivor's Manteau
-				else if (.@gamble2 == 16) .@item = 2515; // Eagle Wing[1]
-			}
-			else if ((.@gamble1 >   0) && (.@gamble1 < 101)) .@item = 2503; // Muffler
-			else if ((.@gamble1 > 100) && (.@gamble1 < 201)) .@item = 2505; // Manteau
-			else if ((.@gamble1 > 203) && (.@gamble1 < 451)) .@item = 2501; // Hood
-			else if ((.@gamble1 > 450) && (.@gamble1 < 501)) .@item = 2502; // Hood[1]
-	break;
+		.@gamble1 = rand(1,500);
+		if (.@gamble1 > 200 && .@gamble1 < 204) {
+			.@gamble2 = rand(1,16);
+			if      (.@gamble2 <  3) .@item = 2506; // Manteau[1]
+			else if (.@gamble2 <  5) .@item = 2504; // Muffler[1]
+			else if (.@gamble2 <  8) .@item = 2508; // Ragamuffin Manteau
+			else if (.@gamble2 < 11) .@item = 2507; // Ancient Cape
+			else if (.@gamble2 == 11) .@item = 2513; // Heavenly Maiden Robe[1]
+			else if (.@gamble2 == 12) .@item = 2514; // Pauldron[1]
+			else if (.@gamble2 == 13) .@item = 2523; // Undershirt[1]
+			else if (.@gamble2 == 14) .@item = 2530; // Rider Insigna[1]
+			else if (.@gamble2 == 15) .@item = 2509; // Survivor's Manteau
+			else if (.@gamble2 == 16) .@item = 2515; // Eagle Wing[1]
+		}
+		else if (.@gamble1 < 101) .@item = 2503; // Muffler
+		else if (.@gamble1 < 201) .@item = 2505; // Manteau
+		else if (.@gamble1 < 451) .@item = 2501; // Hood
+		else if (.@gamble1 < 501) .@item = 2502; // Hood[1]
+		break;
 
 	case 4: // Headgears
-			.@gamble1 = rand(1,1000);
-			if ((.@gamble1 > 299) && (.@gamble1 < 304)) {
-				.@gamble2 = rand(1,93);
-				if      ((.@gamble2 >  0) && (.@gamble2 <  3)) .@item = 2251; // Monk Hat
-				else if ((.@gamble2 >  2) && (.@gamble2 <  5)) .@item = 2285; // Apple of Archer
-				else if ((.@gamble2 >  4) && (.@gamble2 <  7)) .@item = 2255; // Evil Wing
-				else if ((.@gamble2 >  6) && (.@gamble2 <  9)) .@item = 5045; // Magician Hat
-				else if ((.@gamble2 >  8) && (.@gamble2 < 11)) .@item = 2233; // Circlet[1]
-				else if ((.@gamble2 > 10) && (.@gamble2 < 13)) .@item = 2231; // Gemmed Sallet[1]
-				else if ((.@gamble2 > 12) && (.@gamble2 < 15)) .@item = 2217; // Biretta[1]
-				else if ((.@gamble2 > 14) && (.@gamble2 < 17)) .@item = 2206; // Wedding Veil
-				else if ((.@gamble2 > 16) && (.@gamble2 < 19)) .@item = 2246; // Golden Gear
-				else if ((.@gamble2 > 18) && (.@gamble2 < 21)) .@item = 2261; // Army Cap
-				else if ((.@gamble2 > 20) && (.@gamble2 < 23)) .@item = 2287; // Pirate Bandana
-				else if ((.@gamble2 > 22) && (.@gamble2 < 25)) .@item = 5012; // Ph.D Hat
-				else if ((.@gamble2 > 24) && (.@gamble2 < 27)) .@item = 2244; // Big Ribbon
-				else if ((.@gamble2 > 26) && (.@gamble2 < 29)) .@item = 2213; // Kitty Band
-				else if ((.@gamble2 > 28) && (.@gamble2 < 31)) .@item = 2248; // Western Grace
-				else if ((.@gamble2 > 30) && (.@gamble2 < 33)) .@item = 2223; // Turban[1]
-				else if ((.@gamble2 > 32) && (.@gamble2 < 35)) .@item = 2247; // Romantic Gent
-				else if ((.@gamble2 > 34) && (.@gamble2 < 37)) .@item = 2245; // Sweet Gent
-				else if ((.@gamble2 > 36) && (.@gamble2 < 39)) .@item = 5003; // Joker Jester
-				else if ((.@gamble2 > 38) && (.@gamble2 < 41)) .@item = 2225; // Goggles[1]
-				else if ((.@gamble2 > 40) && (.@gamble2 < 43)) .@item = 5017; // Bone Helm
-				else if ((.@gamble2 > 42) && (.@gamble2 < 45)) .@item = 5030; // Panda Hat
-				else if ((.@gamble2 > 44) && (.@gamble2 < 47)) .@item = 5035; // Poring Hat
-				else if ((.@gamble2 > 46) && (.@gamble2 < 49)) .@item = 2250; // Cute Ribbon
-				else if ((.@gamble2 > 48) && (.@gamble2 < 51)) .@item = 2277; // Nurse Cap
-				else if ((.@gamble2 > 50) && (.@gamble2 < 53)) .@item = 5011; // Aerial
-				else if ((.@gamble2 > 52) && (.@gamble2 < 55)) .@item = 2290; // Funeral Hat
-				else if ((.@gamble2 > 54) && (.@gamble2 < 57)) .@item = 5010; // Indian Fillet
-				else if ((.@gamble2 > 56) && (.@gamble2 < 60)) .@item = 2259; // Mini Propeller
-				else if ((.@gamble2 > 59) && (.@gamble2 < 62)) .@item = 5008; // Puppy Love
-				else if ((.@gamble2 > 61) && (.@gamble2 < 63)) .@item = 2249; // Coronet
-				else if ((.@gamble2 > 62) && (.@gamble2 < 65)) .@item = 2229; // Helm[1]
-				else if (.@gamble2 == 65) .@item = 2258; // Spiky Band
-				else if (.@gamble2 == 66) .@item = 2274; // Ghost Bandana
-				else if (.@gamble2 == 67) .@item = 5019; // Corsair
-				else if (.@gamble2 == 68) .@item = 2254; // Angel Wing
-				else if (.@gamble2 == 69) .@item = 5007; // Grand Circlet
-				else if (.@gamble2 == 70) .@item = 5066; // Succubus Horn
-				else if (.@gamble2 == 71) .@item = 2235; // Crown
-				else if (.@gamble2 == 72) .@item = 2234; // Tiara
-				else if (.@gamble2 == 73) .@item = 2256; // Majestic Goat
-				else if (.@gamble2 == 74) .@item = 5093; // Coif[1]
-				else if (.@gamble2 == 75) .@item = 5072; // Incubus Horn
-				else if (.@gamble2 == 76) .@item = 5002; // Jewel Crown
-				else if (.@gamble2 > 76 && .@gamble2 < 80) .@item = 5118; // Puppy Headband
-				else if (.@gamble2 > 79 && .@gamble2 < 83) .@item = 5120; // Bucket Hat[1]
-				else if (.@gamble2 > 82 && .@gamble2 < 86) .@item = 5111; // Galapago Cap
-				else if (.@gamble2 > 85 && .@gamble2 < 89) .@item = 5116; // Banana Hat
-				else if (.@gamble2 > 88 && .@gamble2 < 92) .@item = 5119; // Super Novice Hat[1]
-				else if (.@gamble2 > 91 && .@gamble2 < 94) .@item = 5141; // Marionetta Doll[1]
-			}
-			else if ((.@gamble1 >   0) && (.@gamble1 <  101)) .@item = 2226; // Cap
-			else if ((.@gamble1 > 100) && (.@gamble1 <  201)) .@item = 2211; // Bandana
-			else if ((.@gamble1 > 200) && (.@gamble1 <  300)) .@item = 2209; // Ribbon[1]
-			else if ((.@gamble1 > 303) && (.@gamble1 <  401)) .@item = 2220; // Hat
-			else if ((.@gamble1 > 400) && (.@gamble1 <  501)) .@item = 2232; // Circlet
-			else if ((.@gamble1 > 500) && (.@gamble1 <  601)) .@item = 2216; // Biretta
-			else if ((.@gamble1 > 600) && (.@gamble1 <  701)) .@item = 2230; // Gemmed Sallet
-			else if ((.@gamble1 > 700) && (.@gamble1 <  801)) .@item = 2224; // Goggles
-			else if ((.@gamble1 > 800) && (.@gamble1 <  901)) .@item = 2222; // Turban
-			else if ((.@gamble1 > 900) && (.@gamble1 <  906)) .@item = 2228; // Helm
-			else if ((.@gamble1 > 905) && (.@gamble1 <  911)) .@item = 2252; // Wizard Hat
-			else if ((.@gamble1 > 910) && (.@gamble1 <  916)) .@item = 2227; // Cap[1]
-			else if ((.@gamble1 > 915) && (.@gamble1 <  921)) .@item = 2221; // Hat[1]
-			else if ((.@gamble1 > 920) && (.@gamble1 <  926)) .@item = 2299; // Orc Helm
-			else if ((.@gamble1 > 925) && (.@gamble1 <  931)) .@item = 2236; // Santa Hat
-			else if ((.@gamble1 > 930) && (.@gamble1 <  936)) .@item = 2275; // Red Bandana
-			else if ((.@gamble1 > 935) && (.@gamble1 <  941)) .@item = 5015; // Egg Shell
-			else if ((.@gamble1 > 940) && (.@gamble1 <  946)) .@item = 2215; // Flower Band
-			else if ((.@gamble1 > 945) && (.@gamble1 <  951)) .@item = 5092; // Coif
-			else if ((.@gamble1 > 950) && (.@gamble1 < 1001)) .@item = 2226; // Cap
-	break;
+		.@gamble1 = rand(1,1000);
+		if (.@gamble1 > 299 && .@gamble1 < 304) {
+			.@gamble2 = rand(1,93);
+			if      (.@gamble2 <  3) .@item = 2251; // Monk Hat
+			else if (.@gamble2 <  5) .@item = 2285; // Apple of Archer
+			else if (.@gamble2 <  7) .@item = 2255; // Evil Wing
+			else if (.@gamble2 <  9) .@item = 5045; // Magician Hat
+			else if (.@gamble2 < 11) .@item = 2233; // Circlet[1]
+			else if (.@gamble2 < 13) .@item = 2231; // Gemmed Sallet[1]
+			else if (.@gamble2 < 15) .@item = 2217; // Biretta[1]
+			else if (.@gamble2 < 17) .@item = 2206; // Wedding Veil
+			else if (.@gamble2 < 19) .@item = 2246; // Golden Gear
+			else if (.@gamble2 < 21) .@item = 2261; // Army Cap
+			else if (.@gamble2 < 23) .@item = 2287; // Pirate Bandana
+			else if (.@gamble2 < 25) .@item = 5012; // Ph.D Hat
+			else if (.@gamble2 < 27) .@item = 2244; // Big Ribbon
+			else if (.@gamble2 < 29) .@item = 2213; // Kitty Band
+			else if (.@gamble2 < 31) .@item = 2248; // Western Grace
+			else if (.@gamble2 < 33) .@item = 2223; // Turban[1]
+			else if (.@gamble2 < 35) .@item = 2247; // Romantic Gent
+			else if (.@gamble2 < 37) .@item = 2245; // Sweet Gent
+			else if (.@gamble2 < 39) .@item = 5003; // Joker Jester
+			else if (.@gamble2 < 41) .@item = 2225; // Goggles[1]
+			else if (.@gamble2 < 43) .@item = 5017; // Bone Helm
+			else if (.@gamble2 < 45) .@item = 5030; // Panda Hat
+			else if (.@gamble2 < 47) .@item = 5035; // Poring Hat
+			else if (.@gamble2 < 49) .@item = 2250; // Cute Ribbon
+			else if (.@gamble2 < 51) .@item = 2277; // Nurse Cap
+			else if (.@gamble2 < 53) .@item = 5011; // Aerial
+			else if (.@gamble2 < 55) .@item = 2290; // Funeral Hat
+			else if (.@gamble2 < 57) .@item = 5010; // Indian Fillet
+			else if (.@gamble2 < 60) .@item = 2259; // Mini Propeller
+			else if (.@gamble2 < 62) .@item = 5008; // Puppy Love
+			else if (.@gamble2 < 63) .@item = 2249; // Coronet
+			else if (.@gamble2 < 65) .@item = 2229; // Helm[1]
+			else if (.@gamble2 == 65) .@item = 2258; // Spiky Band
+			else if (.@gamble2 == 66) .@item = 2274; // Ghost Bandana
+			else if (.@gamble2 == 67) .@item = 5019; // Corsair
+			else if (.@gamble2 == 68) .@item = 2254; // Angel Wing
+			else if (.@gamble2 == 69) .@item = 5007; // Grand Circlet
+			else if (.@gamble2 == 70) .@item = 5066; // Succubus Horn
+			else if (.@gamble2 == 71) .@item = 2235; // Crown
+			else if (.@gamble2 == 72) .@item = 2234; // Tiara
+			else if (.@gamble2 == 73) .@item = 2256; // Majestic Goat
+			else if (.@gamble2 == 74) .@item = 5093; // Coif[1]
+			else if (.@gamble2 == 75) .@item = 5072; // Incubus Horn
+			else if (.@gamble2 == 76) .@item = 5002; // Jewel Crown
+			else if (.@gamble2 < 80) .@item = 5118; // Puppy Headband
+			else if (.@gamble2 < 83) .@item = 5120; // Bucket Hat[1]
+			else if (.@gamble2 < 86) .@item = 5111; // Galapago Cap
+			else if (.@gamble2 < 89) .@item = 5116; // Banana Hat
+			else if (.@gamble2 < 92) .@item = 5119; // Super Novice Hat[1]
+			else if (.@gamble2 < 94) .@item = 5141; // Marionetta Doll[1]
+		}
+		else if (.@gamble1 <  101) .@item = 2226; // Cap
+		else if (.@gamble1 <  201) .@item = 2211; // Bandana
+		else if (.@gamble1 <  300) .@item = 2209; // Ribbon[1]
+		else if (.@gamble1 <  401) .@item = 2220; // Hat
+		else if (.@gamble1 <  501) .@item = 2232; // Circlet
+		else if (.@gamble1 <  601) .@item = 2216; // Biretta
+		else if (.@gamble1 <  701) .@item = 2230; // Gemmed Sallet
+		else if (.@gamble1 <  801) .@item = 2224; // Goggles
+		else if (.@gamble1 <  901) .@item = 2222; // Turban
+		else if (.@gamble1 <  906) .@item = 2228; // Helm
+		else if (.@gamble1 <  911) .@item = 2252; // Wizard Hat
+		else if (.@gamble1 <  916) .@item = 2227; // Cap[1]
+		else if (.@gamble1 <  921) .@item = 2221; // Hat[1]
+		else if (.@gamble1 <  926) .@item = 2299; // Orc Helm
+		else if (.@gamble1 <  931) .@item = 2236; // Santa Hat
+		else if (.@gamble1 <  936) .@item = 2275; // Red Bandana
+		else if (.@gamble1 <  941) .@item = 5015; // Egg Shell
+		else if (.@gamble1 <  946) .@item = 2215; // Flower Band
+		else if (.@gamble1 <  951) .@item = 5092; // Coif
+		else if (.@gamble1 < 1001) .@item = 2226; // Cap
+		break;
 	case 5: // Footgear
-			.@gamble1 = rand(1,500);
-			if ((.@gamble1 > 299) && (.@gamble1 < 303)) {
-				.@gamble2 = rand(1,10);
-				if      ((.@gamble2 > 0) && (.@gamble2 <  3)) .@item = 2406; // Boots[1]
-				else if ((.@gamble2 > 2) && (.@gamble2 <  5)) .@item = 2412; // Greaves[1]
-				else if ((.@gamble2 > 4) && (.@gamble2 <  8)) .@item = 2404; // Shoes[1]
-				else if ((.@gamble2 > 7) && (.@gamble2 < 11)) .@item = 2407; // Crystal Pumps
-			}
-			else if ((.@gamble1 >   0) && (.@gamble1 < 201)) .@item = 2401; // Sandals
-			else if ((.@gamble1 > 200) && (.@gamble1 < 300)) .@item = 2408; // Shackles
-			else if ((.@gamble1 > 302) && (.@gamble1 < 351)) .@item = 2411; // Greaves
-			else if ((.@gamble1 > 350) && (.@gamble1 < 401)) .@item = 2403; // Shoes
-			else if ((.@gamble1 > 400) && (.@gamble1 < 451)) .@item = 2405; // Boots
-			else if ((.@gamble1 > 450) && (.@gamble1 < 476)) .@item = 2409; // High Heels
-			else if ((.@gamble1 > 475) && (.@gamble1 < 501)) .@item = 2402; // Sandals[1]
-	break;
+		.@gamble1 = rand(1,500);
+		if (.@gamble1 > 299 && .@gamble1 < 303) {
+			.@gamble2 = rand(1,10);
+			if      (.@gamble2 <  3) .@item = 2406; // Boots[1]
+			else if (.@gamble2 <  5) .@item = 2412; // Greaves[1]
+			else if (.@gamble2 <  8) .@item = 2404; // Shoes[1]
+			else if (.@gamble2 < 11) .@item = 2407; // Crystal Pumps
+		}
+		else if (.@gamble1 < 201) .@item = 2401; // Sandals
+		else if (.@gamble1 < 300) .@item = 2408; // Shackles
+		else if (.@gamble1 < 351) .@item = 2411; // Greaves
+		else if (.@gamble1 < 401) .@item = 2403; // Shoes
+		else if (.@gamble1 < 451) .@item = 2405; // Boots
+		else if (.@gamble1 < 476) .@item = 2409; // High Heels
+		else if (.@gamble1 < 501) .@item = 2402; // Sandals[1]
+		break;
 
 	case 6: // Shields
-			.@gamble1 = rand(1,500);
-			if ((.@gamble1 > 200) && (.@gamble1 < 205)) {
-				.@gamble2 = rand(1,10);
-				if      ((.@gamble2 > 0) && (.@gamble2 <  3)) .@item = 2104; // Buckler[1]
-				else if ((.@gamble2 > 2) && (.@gamble2 <  5)) .@item = 2106; // Shield[1]
-				else if ((.@gamble2 > 4) && (.@gamble2 <  7)) .@item = 2102; // Guard[1]
-				else if ((.@gamble2 > 6) && (.@gamble2 <  9)) .@item = 2111; // Sacred Mission
-				else if ((.@gamble2 > 8) && (.@gamble2 < 11)) .@item = 2109; // Memory Book
-			}
-			else if ((.@gamble1 >   0) && (.@gamble1 < 201)) .@item = 2101; // Guard
-			else if ((.@gamble1 > 204) && (.@gamble1 < 301)) .@item = 2103; // Buckler
-			else if ((.@gamble1 > 300) && (.@gamble1 < 401)) .@item = 2107; // Mirror Shield
-			else if ((.@gamble1 > 401) && (.@gamble1 < 481)) .@item = 2105; // Shield
-			else if ((.@gamble1 > 480) && (.@gamble1 < 501)) .@item = 2108; // Mirror Shield[1]
-	break;
+		.@gamble1 = rand(1,500);
+		if (.@gamble1 > 200 && .@gamble1 < 205) {
+			.@gamble2 = rand(1,10);
+			if      (.@gamble2 <  3) .@item = 2104; // Buckler[1]
+			else if (.@gamble2 <  5) .@item = 2106; // Shield[1]
+			else if (.@gamble2 <  7) .@item = 2102; // Guard[1]
+			else if (.@gamble2 <  9) .@item = 2111; // Sacred Mission
+			else if (.@gamble2 < 11) .@item = 2109; // Memory Book
+		}
+		else if (.@gamble1 < 201) .@item = 2101; // Guard
+		else if (.@gamble1 < 301) .@item = 2103; // Buckler
+		else if (.@gamble1 < 401) .@item = 2107; // Mirror Shield
+		else if (.@gamble1 < 481) .@item = 2105; // Shield
+		else if (.@gamble1 < 501) .@item = 2108; // Mirror Shield[1]
+		break;
 	default: // Cancel button / hack
-			cutin "katsua01.bmp",255;
-			close;
-	break;
+		cutin "katsua01.bmp",255;
+		close;
+		break;
 	}
 	getitem .@item,1;
 	cutin "katsua01.bmp",255;

+ 117 - 137
npc/quests/first_class/tu_acolyte.txt

@@ -1,15 +1,15 @@
-//===== rAthena Script ======================================= 
+//===== rAthena Script =======================================
 //= Acolyte Class Tutorial and Job Specific Quest
-//===== By: ================================================== 
+//===== By: ==================================================
 //= Fix up by Jukka
-//===== Current Version: ===================================== 
-//= 1.9
-//===== Compatible With: ===================================== 
+//===== Current Version: =====================================
+//= 2.0
+//===== Compatible With: =====================================
 //= rAthena Project
-//===== Description: ========================================= 
+//===== Description: =========================================
 //= [Official Conversion]
 //= Acolyte training quest.
-//===== Additional Comments: ================================= 
+//===== Additional Comments: =================================
 //= 1.0 Fully working
 //= 1.1 optimized [Lupus]
 //= 1.2 Fixed experience gains to match upcoming rate adjustments. [SinSloth]
@@ -21,11 +21,12 @@
 //= 1.7 Fixed exp, texts, and requirements in pre-renewal [Daegaladh]
 //= 1.8 Updated to match the official script. [Euphy]
 //= 1.9 Added GM management function. [Euphy]
-//============================================================ 
+//= 2.0 Clean-up and little fix in Cleope Verce. [Capuche]
+//============================================================
 
 // Priest Praupin
 //============================================================
-prt_church,179,15,1	script	Priest Praupin	110,{
+prt_church,179,15,1	script	Priest Praupin	4_M_MINISTER,{
 	mes "[Priest Praupin]";
 	if(BaseJob != Job_Acolyte){
 		if(tu_acolyte01 == 25){
@@ -65,7 +66,7 @@ prt_church,179,15,1	script	Priest Praupin	110,{
 			mes "[Priest Praupin]";
 			mes "I look forward to seeing you";
 			mes "become a better Acolyte after learning from Sister Asthe's instructions. If you like, I can send you to the convent.";
-			set tu_acolyte01, 1;
+			tu_acolyte01 = 1;
 			next;
 			if(select("Go to the convent.:Do not go.")==1){
 				mes "[Priest Praupin]";
@@ -94,7 +95,7 @@ prt_church,179,15,1	script	Priest Praupin	110,{
 		mes "Ah, he's away at the convent right now. If you want to meet Bishop Maugins in the Saint Capitolina Convent, travel ^3131FFnorth^000000, ^3131FFeast^000000, ^3131FFeast^000000 and then ^3131FFeast^000000 from Prontera.";
 		close;
 	case 22:
-		if(countitem(7181) > 0){	// Receipt_01
+		if(countitem(7181) > 0){// Receipt_01
 			emotion e_gasp;
 			mes "Oh my, you seem";
 			mes "to be growing quickly";
@@ -119,25 +120,18 @@ prt_church,179,15,1	script	Priest Praupin	110,{
 			mes "I know I'm not offering much,";
 			mes "but they were quite helpful to me when I was a young Acolyte like yourself.";
 			next;
-			switch(select("Wand:Flail")) {
-			case 1:
-				delitem 7181,1; //Receipt_01
-				set tu_acolyte01,23;
+			if (select("Wand:Flail") == 1)
 				getitem 1604,1; //Wand
-				break;
-			case 2:
-				delitem 7181,1; //Receipt_01
-				set tu_acolyte01,23;
+			else
 				getitem 1510,1; //Flail
-				break;
-			}
+			delitem 7181,1; //Receipt_01
+			tu_acolyte01 = 23;
 			close;
 		} else {
 			mes "You are such a good person,";
 			mes "a true child of the light. I hope that many others will benefit from the purity of your heart and mind.";
 			close;
 		}
-		break;
 	case 25:
 		mes "You must now find";
 		mes "your own path and become";
@@ -176,7 +170,7 @@ prt_church,179,15,1	script	Priest Praupin	110,{
 
 // Asthe
 //============================================================
-prt_monk,230,106,3	script	Asthe#tu	79,{
+prt_monk,230,106,3	script	Asthe#tu	1_F_PRIEST,{
 	mes "[Asthe]";
 	switch(tu_acolyte01){
 	case 1:
@@ -189,8 +183,8 @@ prt_monk,230,106,3	script	Asthe#tu	79,{
 		if(select("I came for fun.:Priest Praupin suggested I come here.")==1){
 			mes "[Asthe]";
 			mes "This convent is always quiet and peaceful. Sitting on the benches and feeling the almighty presense all around you is such a relaxing, meditative experience.";
-			close;
-		} else {
+		}
+		else {
 			mes "[Asthe]";
 			mes "Priest Praupin?";
 			mes "He's a very kind man";
@@ -203,10 +197,9 @@ prt_monk,230,106,3	script	Asthe#tu	79,{
 			mes "why don't you rest";
 			mes "for a little bit? You must be tired after traveling all the way here. When you're ready, just come";
 			mes "see me, okay?";
-			set tu_acolyte01, 2;
-			close;
+			tu_acolyte01 = 2;
 		}
-		break;
+		close;
 	case 2:
 		mes "This castle, Saint Capitolina Convent, is a beautiful and peaceful place that brings";
 		mes "calm to any heart.";
@@ -249,13 +242,12 @@ prt_monk,230,106,3	script	Asthe#tu	79,{
 		mes "is the most necessary";
 		mes "skill for an Acolyte?";
 		next;
-		switch(select("Heal:Aqua Benedicta:Teleport")){ 
+		switch(select("Heal:Aqua Benedicta:Teleport")){
 		case 1:
 			mes "[Asthe]";
 			mes "Ah, yes.";
 			mes "^3131FFHeal ^000000 is a basic, yet important skill for people like us. It can recover your own health as";
 			mes "well as that of others.";
-			next;
 			break;
 		case 2:
 			mes "[Asthe]";
@@ -268,7 +260,6 @@ prt_monk,230,106,3	script	Asthe#tu	79,{
 			mes "^3131FFHeal^000000 is the most important";
 			mes "skill that an Acolyte can learn. It's a special ability that can save others and defeat the";
 			mes "monsters borne of darkness.";
-			next;
 			break;
 		case 3:
 			mes "[Asthe]";
@@ -278,10 +269,9 @@ prt_monk,230,106,3	script	Asthe#tu	79,{
 			mes "In my opinion,";
 			mes "^3131FFHeal^000000 is the most important skill that an Acolyte can learn. It's a special ability that can save others and defeat the";
 			mes "monsters borne of darkness.";
-			next;
 			break;
 		}
-
+		next;
 		mes "[Asthe]";
 		mes "You can use the holy power";
 		mes "in the Heal skill to attack Undead monsters (^3131FFShift + Heal^000000). The light of truth protects the righteous and destroys the wicked.";
@@ -294,7 +284,7 @@ prt_monk,230,106,3	script	Asthe#tu	79,{
 		mes "[Asthe]";
 		mes "HEAL !!";
 		npcskill "AL_HEAL",11,99,60;
-		set tu_acolyte01, 3;
+		tu_acolyte01 = 3;
 		if(getskilllv("AL_HEAL") == 0){
 			getexp 0,100;
 			specialeffect2 EF_CONE;
@@ -332,12 +322,12 @@ prt_monk,230,106,3	script	Asthe#tu	79,{
 			mes "Well then,";
 			mes "I hope you";
 			mes "have a safe trip.";
-			set tu_acolyte01,4;
+			tu_acolyte01 = 4;
 			getitem 1504,1; //Mace
 			getitem 602,1; //Wing_Of_Butterfly
 			savepoint "prt_monk",30,250;
-			close;
-		} else {
+		}
+		else {
 			mes "[Asthe]";
 			mes "Oh my!";
 			mes "You've already";
@@ -350,13 +340,12 @@ prt_monk,230,106,3	script	Asthe#tu	79,{
 			mes "ready for me to discuss";
 			mes "the next subject. Now,";
 			mes "let's see... Hmm...";
-			set tu_acolyte01, 5;
-			close;
+			tu_acolyte01 = 5;
 		}
-		break;
+		close;
 	case 4:
 		if(getskilllv("AL_HEAL") > 2){
-			if(countitem(1504) > 0){
+			if(countitem(1504) > 0){// Mace
 				mes "Oh my!";
 				mes "Welcome back~";
 				mes "I see that you've";
@@ -382,18 +371,15 @@ prt_monk,230,106,3	script	Asthe#tu	79,{
 				mes "Thank you so much.";
 				mes "You have returned the Mace.";
 				emotion e_heh;
-				set tu_acolyte01, 5;
-				delitem 1504, 1; //Mace
-				if(Class == Job_Acolyte_High) {
+				tu_acolyte01 = 5;
+				delitem 1504,1; //Mace
+				if(Class == Job_Acolyte_High)
 					getexp 2000,1000;
-					specialeffect2 EF_CONE;
-					close;
-				} else {
+				else
 					getexp 1000,500;
-					specialeffect2 EF_CONE;
-					close;
-				}
-			} else {
+				specialeffect2 EF_CONE;
+			}
+			else {
 				mes "Oh my!";
 				mes "Welcome back~";
 				mes "I see that you've";
@@ -401,8 +387,8 @@ prt_monk,230,106,3	script	Asthe#tu	79,{
 				next;
 				mes "[Asthe]";
 				mes "But you must have forgotten the Mace I've lent you. Would you bring it back so that I can return it to the church?";
-				close;  
 			}
+			close;
 		}
 		mes "Are you having";
 		mes "a hard time training";
@@ -435,7 +421,7 @@ prt_monk,230,106,3	script	Asthe#tu	79,{
 		mes "[Asthe]";
 		mes "Did you undertand all that?";
 		mes "Ho ho ho~ No need to try to remember everything right now. You'll get the hang of it.";
-		set tu_acolyte01, 6;
+		tu_acolyte01 = 6;
 		close;
 	case 6:
 		mes ""+strcharinfo(0)+",";
@@ -446,7 +432,7 @@ prt_monk,230,106,3	script	Asthe#tu	79,{
 		mes "[Asthe]";
 		mes "However, I'm a little busy with";
 		mes "my work right now, so would you deliver this to Priest Gardron in the next building for me?";
-		set tu_acolyte01,7;
+		tu_acolyte01 = 7;
 		getitem 7148,1; //Mother_Letter
 		close;
 	case 7:
@@ -494,7 +480,7 @@ prt_monk,230,106,3	script	Asthe#tu	79,{
 		next;
 		mes "^3355FFSister Asthe gives";
 		mes "you some dog food.^000000";
-		set tu_acolyte01, 10;
+		tu_acolyte01 = 10;
 		close;
 	case 11:
 	case 12:
@@ -515,7 +501,7 @@ prt_monk,230,106,3	script	Asthe#tu	79,{
 		mes "know the answer.";
 		mes "Think carefully";
 		mes "and it will come.";
-		set tu_acolyte01, 12;
+		tu_acolyte01 = 12;
 		close;
 	case 13:
 		mes "You remember that";
@@ -544,7 +530,7 @@ prt_monk,230,106,3	script	Asthe#tu	79,{
 		mes "Let's take a break before";
 		mes "we proceed to the next lesson,";
 		mes "alright? I'll be right here.";
-		set tu_acolyte01, 14;
+		tu_acolyte01 = 14;
 		close;
 	case 14:
 		mes "Now it's time";
@@ -566,7 +552,7 @@ prt_monk,230,106,3	script	Asthe#tu	79,{
 		mes "[Asthe]";
 		mes "Try it for";
 		mes "yourself, okay?";
-		set tu_acolyte01, 15;
+		tu_acolyte01 = 15;
 		npcskill "AL_INCAGI",10,0,0;
 		close;
 	case 15:
@@ -577,7 +563,7 @@ prt_monk,230,106,3	script	Asthe#tu	79,{
 		next;
 		mes "[Asthe]";
 		mes "Like you'd expect, Decrease AGI slows down enemies, reducing their agility, Movement Speed, Dodge Rate and Attack Speed. Its effect is the exact reverse of Increase AGI.";
-		set tu_acolyte01, 15;
+		tu_acolyte01 = 15;
 		next;
 		mes "[Asthe]";
 		mes "The last skill in the Heal skill tree is ^3131FFCure^000000. You can learn Cure after learning Level 2 Heal.";
@@ -593,7 +579,7 @@ prt_monk,230,106,3	script	Asthe#tu	79,{
 		mes "For now, don't forget that the Increase AGI, Decrease AGI and";
 		mes "Cure skills can only be learned";
 		mes "by learning the Heal skill.";
-		set tu_acolyte01, 16;
+		tu_acolyte01 = 16;
 		close;
 	case 16:
 		mes "You've been learning";
@@ -660,7 +646,7 @@ prt_monk,230,106,3	script	Asthe#tu	79,{
 			mes "[Asthe]";
 			mes "Ho ho!";
 			mes "What a smart Acolyte~";
-			set tu_acolyte01, 17;
+			tu_acolyte01 = 17;
 		}
 		close;
 	case 17:
@@ -693,7 +679,7 @@ prt_monk,230,106,3	script	Asthe#tu	79,{
 			mes "to carry everything.";
 			mes "Why don't you put some of your things in Kafra Storage first?";
 		} else {
-			set tu_acolyte01, 18;
+			tu_acolyte01 = 18;
 			getitem 7183,1; //Sister_Letter
 			getitem 7181,1; //Receipt_01
 			getitem 1081,2; //Merchant_Box_1
@@ -743,7 +729,7 @@ prt_monk,230,106,3	script	Asthe#tu	79,{
 		mes "the next time we";
 		mes "meet, I will give";
 		mes "you my final lesson.";
-		set tu_acolyte01, 24;
+		tu_acolyte01 = 24;
 		close;
 	case 24:
 		mes "The last skill";
@@ -777,7 +763,7 @@ prt_monk,230,106,3	script	Asthe#tu	79,{
 		next;
 		mes "[Asthe]";
 		mes "Well, please don't forget what I've taught you. I hope you look back with fondness at our time together amongst these beautiful flowers. May God bless you~";
-		set tu_acolyte01, 25;
+		tu_acolyte01 = 25;
 		if (checkre(3))
 			getexp 1000,1000;
 		else
@@ -802,10 +788,10 @@ prt_monk,230,106,3	script	Asthe#tu	79,{
 
 // Priest Gardron
 //============================================================
-monk_in,18,38,6	script	Priest Gardron#tu	110,{
+monk_in,18,38,6	script	Priest Gardron#tu	4_M_MINISTER,{
 	mes "[Priest Gardron]";
 	if(tu_acolyte01 == 7){
-		if(countitem(7148) >= 1){
+		if(countitem(7148) >= 1){// Mother_Letter
 			mes "Hmm...?";
 			mes "What's that?";
 			mes "You have something for me?";
@@ -820,8 +806,7 @@ monk_in,18,38,6	script	Priest Gardron#tu	110,{
 			mes "she is getting old now, but I'm relieved to hear that her health";
 			mes "is good. We are truly blessed...";
 			delitem 7148,1; //Mother_Letter
-			set tu_acolyte01,8;
-			close;
+			tu_acolyte01 = 8;
 		} else {
 			mes "Hmm...?";
 			mes "What's that?";
@@ -832,10 +817,10 @@ monk_in,18,38,6	script	Priest Gardron#tu	110,{
 			mes "Well, it seems that";
 			mes "you must have forgotten";
 			mes "it or misplaced it somewhere.";
-			close;
 		}
+		close;
 	} else if(tu_acolyte01 == 8){
-		if(countitem((checkre(0)?939:957)) > 4){
+		if(countitem((checkre(0)?939:957)) > 4){// Bee_Sting
 			mes "Oh...!";
 			mes "You've returned!";
 			mes "Let's see... One...";
@@ -855,7 +840,7 @@ monk_in,18,38,6	script	Priest Gardron#tu	110,{
 			mes "should return to Sister";
 			mes "Asthe to continue your";
 			mes "Acolyte training.";
-			set tu_acolyte01, 9;
+			tu_acolyte01 = 9;
 			percentheal 100,100;
 			if (checkre(3))
 				getexp 1000,1000;
@@ -954,7 +939,7 @@ monk_in,18,38,6	script	Priest Gardron#tu	110,{
 
 // Dog
 //============================================================
-prt_monk,235,245,5	script	Dog#tu	81,{
+prt_monk,235,245,5	script	Dog#tu	4_DOG01,{
 	mes "[Dog]";
 	mes "^CDB79EBark bark!";
 	mes "Woof woof!^000000";
@@ -963,7 +948,7 @@ prt_monk,235,245,5	script	Dog#tu	81,{
 		while(1) {
 			switch(select("Give a treat.:Pet the dog.:End actions.")){
 			case 1:
-				set .@dog_food, .@dog_food + 1;
+				.@dog_food++;
 				if(.@dog_food > 4){
 					mes "[Dog]";
 					mes "^CDB79EGrrrrrr...!^000000";
@@ -972,26 +957,23 @@ prt_monk,235,245,5	script	Dog#tu	81,{
 					mes "^3355FFThe dog grinds";
 					mes "its teeth and glares";
 					mes "at you menacingly.^000000";
-					next;
 				} else {
 					mes "[Dog]";
 					mes "^CDB79ERoof roof!^000000";
 					emotion e_lv;
-					next;
 				}
 				break;
 			case 2:
 				mes "[Dog]";
 				mes "^CDB79ERoof roof~^000000";
 				emotion e_lv;
-				next;
 				break;
 			case 3:
 				mes "[Dog]";
 				mes "^CDB79ERoof roof!^000000";
-				next;
 				break;
 			}
+			next;
 		}
 	}
 	mes "["+strcharinfo(0)+"]";
@@ -1001,9 +983,9 @@ prt_monk,235,245,5	script	Dog#tu	81,{
 
 // Boy
 //============================================================
-prt_monk,243,238,5	script	Boy#boy_voi	139,5,5,{
+prt_monk,243,238,5	script	Boy#boy_voi	HIDDEN_WARP_NPC,5,5,{
 OnTouch_:
-	if(tu_acolyte01 != 10)end;
+	if(tu_acolyte01 != 10) end;
 	mes "[??]";
 	mes "^333333*Sniff*^000000";
 	mes "Sister...";
@@ -1012,7 +994,7 @@ OnTouch_:
 
 // Ill Girl
 //============================================================
-prt_monk,226,257,6	script	Ill Girl#tu	93,{
+prt_monk,226,257,6	script	Ill Girl#tu	4_F_04,{
 	mes "[Angelic]";
 	if(tu_acolyte01 > 12){
 		mes "Thanks for helping me.";
@@ -1087,15 +1069,15 @@ prt_monk,226,257,6	script	Ill Girl#tu	93,{
 		mes "["+strcharinfo(0)+"]";
 		mes "I'll cure you...";
 		mes "For sure.";
-		set tu_acolyte01, 11;
-		close;
-	} else if(tu_acolyte01 == 11){
+		tu_acolyte01 = 11;
+	}
+	else if(tu_acolyte01 == 11){
 		next;
 		mes "[Angelic]";
 		mes "It hurts...!";
 		mes "I... I can't...!";
-		close;
-	} else if(tu_acolyte01 == 12){
+	}
+	else if(tu_acolyte01 == 12){
 		next;
 		mes "^3355FFYou go towards";
 		mes "the girl, place your hands";
@@ -1112,8 +1094,7 @@ prt_monk,226,257,6	script	Ill Girl#tu	93,{
 			mes "^3355FFNothing happened.";
 			mes "It doesn't look like";
 			mes "that skill will work.^000000";
-			close; 
-			break;
+			close;
 		case 2:
 			if(getskilllv("AL_BLESSING") > 0){
 				mes "["+strcharinfo(0)+"]";
@@ -1131,7 +1112,7 @@ prt_monk,226,257,6	script	Ill Girl#tu	93,{
 					mes "practice your abilities!";
 					mes "It seemed that this good";
 					mes "deed has improved your skills.^000000";
-					set tu_acolyte01, 13;
+					tu_acolyte01 = 13;
 					if (checkre(3))
 						getexp 0,500;
 					else
@@ -1153,7 +1134,6 @@ prt_monk,226,257,6	script	Ill Girl#tu	93,{
 				mes "not able to use it for now.^000000";
 				close;
 			}
-			break;
 		case 3:
 			if(getskilllv("AL_CURE") > 0){
 				mes "["+strcharinfo(0)+"]";
@@ -1163,13 +1143,13 @@ prt_monk,226,257,6	script	Ill Girl#tu	93,{
 				mes "^3355FFNothing happened.";
 				mes "It doesn't look like";
 				mes "that skill will work.^000000";
-				close;
-			} else {
+			}
+			else {
 				mes "^3355FFYou didn't learn";
 				mes "this skill yet, so you're";
 				mes "not able to use it for now.^000000";
-				close;
 			}
+			close;
 		case 4:
 			mes "^3355FFThis isn't a skill";
 			mes "that Acolytes can use...^000000";
@@ -1184,19 +1164,18 @@ prt_monk,226,257,6	script	Ill Girl#tu	93,{
 				mes "^3355FFNothing happened.";
 				mes "It doesn't look like";
 				mes "that skill will work.^000000";
-				close;
-			} else {
+			}
+			else {
 				mes "^3355FFYou didn't learn";
 				mes "this skill yet, so you're";
 				mes "not able to use it for now.^000000";
-				close;
 			}
+			close;
 		case 6:
 			mes "^3355FFThis isn't a skill";
 			mes "that Acolytes can use...^000000";
 			close;
 		}
-
 		mes "[Angelic]";
 		mes "Ah...";
 		mes "I feel...";
@@ -1214,9 +1193,9 @@ prt_monk,226,257,6	script	Ill Girl#tu	93,{
 
 // Veiner
 //============================================================
-prt_monk,197,228,3	script	Veiner	89,{
+prt_monk,197,228,3	script	Veiner	4_M_ORIENT02,{
 	mes "[Veiner]";
-	if(countitem(1081) && tu_acolyte01 == 18){
+	if(countitem(1081) && tu_acolyte01 == 18){// Merchant_Box_1
 		mes "So very exhausted...";
 		mes "Should I just go back home?";
 		next;
@@ -1238,34 +1217,35 @@ prt_monk,197,228,3	script	Veiner	89,{
 		mes "Oh right!";
 		mes "Thank you very much for delivering this to me. Sorry, but I'm always excited to hear from my cutie pie~";
 		emotion e_thx;
-		set tu_acolyte01,19;
+		tu_acolyte01 = 19;
 		delitem 1081,1; //Merchant_Box_1
-		close;
-	} else if(tu_acolyte01 > 18){
+	}
+	else if(tu_acolyte01 > 18){
 		mes "I can't wait";
 		mes "to see her again!";
 		mes "Bibi...! I loooove you!";
 		emotion e_kis2;
-		close;
 	}
-	mes "So very";
-	mes "exhausted...";
-	mes "Would you help";
-	mes "me carry my stuff?";
-	mes "P-please...";
+	else {
+		mes "So very";
+		mes "exhausted...";
+		mes "Would you help";
+		mes "me carry my stuff?";
+		mes "P-please...";
+	}
 	close;
 }
 
 // Hedrick
 //============================================================
-prt_monk,28,260,5	script	Hedrick	50,{
+prt_monk,28,260,5	script	Hedrick	1_M_04,{
 	mes "[Hedrick]";
 	mes "^333333*Whew!*^000000";
 	mes "Man I worked hard today!";
 	mes "And so efficiently too!";
 	mes "Awesome job as usual,";
 	mes "Hedrick. You're number one!";
-	if(countitem(7148)==0 || tu_acolyte01 != 19) close;
+	if(countitem(7148)==0 || tu_acolyte01 != 19) close;// Mother_Letter
 	next;
 	mes "[Hedrick]";
 	mes "Oh...?";
@@ -1287,16 +1267,16 @@ prt_monk,28,260,5	script	Hedrick	50,{
 	mes "you have one of the";
 	mes "cookies she sent me?";
 	delitem 7148,1; //Mother_Letter
-	set tu_acolyte01,20;
+	tu_acolyte01 = 20;
 	getitem 538,1; //Well_Baked_Cookie
 	close;
 }
 
 // Weapon Merchant
 //============================================================
-prt_monk,136,261,0	script	Weapon Merchant#tu	139,9,9,{
+prt_monk,136,261,0	script	Weapon Merchant#tu	HIDDEN_WARP_NPC,9,9,{
 OnTouch:
-	if(tu_acolyte01 != 20 || countitem(1081)==0) end;
+	if(tu_acolyte01 != 20 || countitem(1081) == 0) end;// Merchant_Box_1
 	mes "[Weapon Merchant]";
 	mes "The stuff I requested from the guild is finally here!";
 	mes "It's pretty tough to keep everything in stock since this is such a remote place.";
@@ -1306,18 +1286,18 @@ OnTouch:
 	mes "Oh, and come here if";
 	mes "you need anything, okay?";
 	delitem 1081,1; //Merchant_Box_1
-	set tu_acolyte01,21;
+	tu_acolyte01 = 21;
 	close;
 }
 
 // Karven
 //============================================================
-monk_in,103,176,7	script	Karven	60,{
+monk_in,103,176,7	script	Karven	1_M_PASTOR,{
 	mes "[Karven]";
 	mes "May you rest";
 	if(tu_acolyte01 == 21 && countitem(7183)){
-		delitem 7183, 1;
-		set tu_acolyte01, 22;
+		delitem 7183, 1;// Sister_Letter
+		tu_acolyte01 = 22;
 		mes "in peace forever...";
 		next;
 		mes "[Karven]";
@@ -1346,7 +1326,7 @@ monk_in,103,176,7	script	Karven	60,{
 
 // Gloria
 //============================================================
-prt_monk,219,164,3	script	Gloria#tu	95,{
+prt_monk,219,164,3	script	Gloria#tu	4_F_SISTER,{
 	mes "[Gloria]";
 	if(BaseJob != Job_Acolyte){
 		mes "Hi, hi~";
@@ -1419,12 +1399,12 @@ prt_monk,219,164,3	script	Gloria#tu	95,{
 
 // Cleope Verce
 //============================================================
-prt_monk,153,210,3	script	Cleope Verce	95,{
+prt_monk,153,210,3	script	Cleope Verce	4_F_SISTER,{
 	mes "[Cleope Verce]";
-	if(BaseJob != Job_Acolyte){
+	if(BaseJob == Job_Acolyte){
 		if (JobLevel < 40) {
 			if (BaseLevel < 26) {
-				mes "Oh my.. "; 
+				mes "Oh my.. ";
 				mes "A newbie Acolyte?";
 				mes "It doesn't even seem";
 				mes "like you've learned";
@@ -1466,7 +1446,7 @@ prt_monk,153,210,3	script	Cleope Verce	95,{
 				mes "Rookie.";
 				close;
 			} else if(BaseLevel < 36){
-				mes "Oh my.. "; 
+				mes "Oh my.. ";
 				mes "A young Acolyte?";
 				mes "You seem to have just";
 				mes "started learning your skills?";
@@ -1517,7 +1497,7 @@ prt_monk,153,210,3	script	Cleope Verce	95,{
 				mes "young friend.";
 				close;
 			} else if(BaseLevel < 46){
-				mes "Well, well, well~"; 
+				mes "Well, well, well~";
 				mes "Hello, young Acolyte.";
 				mes "You're still kind of";
 				mes "green, but I guess you're";
@@ -1593,7 +1573,7 @@ prt_monk,153,210,3	script	Cleope Verce	95,{
 }
 // Range NPC
 //============================================================
-prt_monk,217,123,0	script	#tu_monk	139,10,10,{
+prt_monk,217,123,0	script	#tu_monk	HIDDEN_WARP_NPC,10,10,{
 OnTouch_:
 	if(BaseJob == Job_Acolyte){
 		switch(tu_acolyte01){
@@ -1614,7 +1594,7 @@ OnTouch_:
 
 // Eavesdrop
 //============================================================
-prt_monk,223,123,3	script	Eavesdrop#tu	111,{
+prt_monk,223,123,3	script	Eavesdrop#tu	HIDDEN_NPC,{
 	if(BaseJob != Job_Acolyte){
 		mes "^3355FFThere's nothing here.^000000";
 		close;
@@ -1643,8 +1623,8 @@ prt_monk,223,123,3	script	Eavesdrop#tu	111,{
 		mes "^A68064 Alright, sir.";
 		mes "Still, I can't help";
 		mes "but feel uneasy...^000000";
-		close;
-	} else if(tu_acolyte01 > 9 && tu_acolyte01 < 25){
+	}
+	else if(tu_acolyte01 > 9 && tu_acolyte01 < 25){
 		mes "[Voices from Window]";
 		mes "^A68064I heard from an";
 		mes "official at the palace";
@@ -1674,8 +1654,8 @@ prt_monk,223,123,3	script	Eavesdrop#tu	111,{
 		next;
 		mes "[Voices from Window]";
 		mes "^CD6600Thank you...^000000";
-		close;
-	} else if(tu_acolyte01 > 24){
+	}
+	else if(tu_acolyte01 > 24){
 		mes "[Voices from Window]";
 		mes "^A68064...Sir, do you know anything about the ^855E42curse of the Gaebolg family^A68064?^000000";
 		next;
@@ -1776,34 +1756,34 @@ prt_monk,223,123,3	script	Eavesdrop#tu	111,{
 		mes "[Voice from Window]";
 		mes "^CD6600Oh dear God.";
 		mes "Please expel the darkness that approaches the Rune-Midgarts Kingdom. Please protect us with your light and compassion.^000000";
-		close;
 	}
+	close;
 }
 
-sec_in02,17,156,3	script	1st Job Quest Reset	726,{
+sec_in02,17,156,3	script	1st Job Quest Reset	4_F_JOB_BLACKSMITH,{
 	callfunc "F_GM_NPC";
 	mes "[1st Job Quest]";
 	mes "Which would you like to reset?";
 	next;
 	switch(select("Swordsman:Merchant:Archer:Acolyte:Thief:Mage")) {
 	case 1:
-		set tu_swordman,0;
+		tu_swordman = 0;
 		break;
 	case 2:
-		set tu_merchant,0;
+		tu_merchant = 0;
 		break;
 	case 3:
-		set tu_archer01,0;
-		set tu_archer02,0;
+		tu_archer01 = 0;
+		tu_archer02 = 0;
 		break;
 	case 4:
-		set tu_acolyte01,0;
+		tu_acolyte01 = 0;
 		break;
 	case 5:
-		set tu_thief01,0;
+		tu_thief01 = 0;
 		break;
 	case 6:
-		set tu_magician01,0;
+		tu_magician01 = 0;
 		break;
 	}
 	mes "Completed.";

+ 136 - 171
npc/quests/first_class/tu_archer.txt

@@ -1,15 +1,15 @@
-//===== rAthena Script ======================================= 
+//===== rAthena Script =======================================
 //= Archer Class Tutorial and Job Specific Quest
-//===== By: ================================================== 
+//===== By: ==================================================
 //= Fix up by Jukka
-//===== Current Version: ===================================== 
-//= 2.0
-//===== Compatible With: ===================================== 
+//===== Current Version: =====================================
+//= 3.0
+//===== Compatible With: =====================================
 //= rAthena Project
-//===== Description: ========================================= 
+//===== Description: =========================================
 //= [Official Conversion]
 //= Archer training quest.
-//===== Additional Comments: ================================= 
+//===== Additional Comments: =================================
 //= 1.0 Fully working
 //= 1.1 optimized [Lupus]
 //= 1.2 fixed Weight check
@@ -24,11 +24,12 @@
 //= 1.9a Added 'npcskill' command. [Euphy]
 //= 2.0 Updated to match the official script. [Euphy]
 //=     Added Pre-Renewal support.
-//============================================================ 
+//= 3.0 Clean-up. [Capuche]
+//============================================================
 
 // Bard Jet
 //============================================================
-payon_in02,67,65,3	script	Bard Jet#tu	51,{
+payon_in02,67,65,3	script	Bard Jet#tu	1_M_BARD,{
 	mes "[Jet]";
 	mes "Every god never grows old";
 	mes "Because of beautiful";
@@ -37,7 +38,7 @@ payon_in02,67,65,3	script	Bard Jet#tu	51,{
 	mes "Goddess of immortality.";
 	next;
 	mes "[Jet]";
-	if(Class == Job_Archer || Class == Job_Baby_Archer || Class == Job_Archer_High){
+	if (BaseJob == Job_Archer){
 		mes "Ooh, you're an Archer?";
 		mes "There was a time when I too";
 		mes "was an Archer. But I've changed jobs, so now I entertain the masses with my songs and humor.";
@@ -103,8 +104,7 @@ payon_in02,67,65,3	script	Bard Jet#tu	51,{
 		mes "everything and change";
 		mes "jobs so that you can";
 		mes "come play with me!";
-		close;
-	} else if(Class == Job_Bard || Class == Job_Clown || Class == Job_Baby_Bard){	//CHECK
+	} else if (BaseJob == Job_Bard){
 		mes "Ooh...!";
 		mes "You're...!";
 		next;
@@ -130,15 +130,13 @@ payon_in02,67,65,3	script	Bard Jet#tu	51,{
 		mes "and the word Mammonite";
 		mes "sounds-- wait. It's not funny";
 		mes "if I have to explain it, huh?";
-		close;
-	} else if(Class == Job_Dancer || Class == Job_Gypsy || Class == Job_Baby_Dancer){
+	} else if (BaseJob == Job_Dancer){
 		mes "Ooh...!";
 		mes "Hello, hello!";
 		mes "Wouldn't it be";
 		mes "nice if we could";
 		mes "perform together";
 		mes "one of these days?";
-		close;
 	} else {
 		mes "You look bored...";
 		mes "Is life really that";
@@ -164,7 +162,7 @@ payon_in02,67,65,3	script	Bard Jet#tu	51,{
 
 // Sign
 //============================================================
-pay_arche,127,154,4	script	Sign#arc	835,{
+pay_arche,127,154,4	script	Sign#arc	2_BOARD1,{
 	mes " ";
 	mes " Archer Job Change Office ---> ";
 	mes " <--- Icarus ";
@@ -174,7 +172,7 @@ pay_arche,127,154,4	script	Sign#arc	835,{
 
 // Master Kavaruk
 //============================================================
-payon_in02,54,13,3	script	Master Kavaruk	55,{
+payon_in02,54,13,3	script	Master Kavaruk	1_M_JOBTESTER,{
 	mes "[Master Kavaruk]";
 	mes "Hello, young one.";
 	mes "I am Master Kavaruk";
@@ -200,25 +198,22 @@ payon_in02,54,13,3	script	Master Kavaruk	55,{
 		mes "The members of our guild are those who not only wish to be brave, but to reach out for our dreams like Icarus did. Our members are aspiring Archers, former Archers and current Archers.";
 		next;
 		mes "[Master Kavaruk]";
-		if(Class == Job_Novice || Class == Job_Baby){
+		if (Class == Job_Novice){
 			mes "If you'd like to become an Archer, why don't you go around and learn";
 			mes "a few things about the profession? I'm sure you'll be able to hear plenty of stories around here.";
-			close;
-
-		} else if(Class == Job_Archer){
+		}
+		else if(Class == Job_Archer){
 			mes "Since you're an Archer, you might";
 			mes "benefit from going around and listening to everyone else's battle experiences. Becoming familiar with different fight scenarios will help you in the future.";
-			close;
-
-		} else if(Class == Job_Hunter || Class == Job_Sniper || Class == Job_Baby_Hunter){	//CHECK
+		}
+		else if (BaseJob == Job_Hunter){
 			mes "[Master Kavaruk]";
 			mes "Arpesto is waiting";
 			mes "outside. Since he's a";
 			mes "veteran hunter, he can";
 			mes "be of great help to you.";
-			close;
-
-		} else if(Class == Job_Dancer || Class == Job_Gypsy || Class == Job_Baby_Dancer){	//CHECK
+		}
+		else if (BaseJob == Job_Dancer){
 			mes "In fact, a few of our";
 			mes "members specialize in";
 			mes "capturing the fascination";
@@ -230,24 +225,23 @@ payon_in02,54,13,3	script	Master Kavaruk	55,{
 			mes "any of them around here right now. They never seem to remain in one";
 			mes "place for very long. Still, I'm sure of them will turn up here";
 			mes "one of these days.";
-			close;
-
-		} else if(Class == Job_Bard || Class == Job_Clown || Class == Job_Baby_Bard){	//CHECK
+		}
+		else if (BaseJob == Job_Bard){
 			mes "In fact, a few of our members";
 			mes "are highly skilled in singing and entertaining their allies, much in the same way you do.";
 			next;
 			mes "[Master Kavaruk]";
 			mes "...But I don't seem to see any of them around here right now. They always wander wherever the wind takes them, but I'm sure one of them will wander back here one";
 			mes "of these days.";
-			close;
 		}
-		mes "They happen to know a lot of information about jobs related to Archers. Why don't you speak to them and learn more about";
-		mes "these other jobs?";
+		else {
+			mes "They happen to know a lot of information about jobs related to Archers. Why don't you speak to them and learn more about";
+			mes "these other jobs?";
+		}
 		close;
-		break;
 	case 2:
 		mes "[Master Kavaruk]";
-		if(Class == Job_Archer || Class == Job_Baby_Archer || Class == Job_Archer_High){
+		if (BaseJob == Job_Archer) {
 			if(tu_archer02 == 0){
 				mes "Oh, it's a good thing";
 				mes "you're here, "+strcharinfo(0)+".";
@@ -267,9 +261,7 @@ payon_in02,54,13,3	script	Master Kavaruk	55,{
 				next;
 				mes "[Master Kavaruk]";
 				mes "If you're interested, it would probably be best to speak to the Alchemist Guild member who is waiting to hear from us at the ^3131FFshop next to the road south of Icarus^000000.";
-				set tu_archer02,1;
-				close;
-
+				tu_archer02 = 1;
 			} else if(tu_archer02 == 1){
 				mes "Hmmm...";
 				mes "..........";
@@ -293,27 +285,23 @@ payon_in02,54,13,3	script	Master Kavaruk	55,{
 				mes "[Master Kavaruk]";
 				mes "Please find out if he is all right and help him with whatever he";
 				mes "may need. In the meantime, I will be waiting to hear from you. Thank you very much.";
-				set tu_archer02, 2;
-				close;
-
+				tu_archer02 = 2;
 			} else if(tu_archer02 == 2){
 				mes "Find Arthail";
 				mes "of the Wind for me.";
 				mes "He must be somewhere";
 				mes "in Prontera...";
-				close;
-
 			} else if(tu_archer02 == 9){
 				mes "Hmmm, I see. Thank you";
 				mes "for bringing me the news. As Arthail has said, I shall wait until he has more news for me.";
 				mes "You should also train and prepare for the future as well.";
-				set tu_archer02, 10;
+				tu_archer02 = 10;
 				if (checkre(3))
 					getexp 1000,1000;
 				else
 					getexp 2000,1000;
-				close;
 			}
+			close;
 		}
 		mes "I don't know...";
 		mes "Recently, I haven't heard any noteworthy news. For now, the warmth of the sun seems to be protecting the peace here.";
@@ -323,8 +311,6 @@ payon_in02,54,13,3	script	Master Kavaruk	55,{
 		mes "any interesting rumors,";
 		mes "I will let you know.";
 		close;
-		break;
-
 	case 3:
 		mes "[Master Kavaruk]";
 		if(tu_archer01 < 1){
@@ -333,21 +319,18 @@ payon_in02,54,13,3	script	Master Kavaruk	55,{
 				mes "So are you";
 				mes "interested in";
 				mes "becoming an Archer?";
-
-			} else if(BaseClass == Job_Archer){
+			} else if (Class == Job_Archer_High || BaseJob == Job_Dancer || BaseJob == Job_Bard){
 				mes "Although I can't deny that you're an expert, I suppose it is a good idea to review the basics of archery from time to time. Would you like to try some refresher training?";
-			} else if(Class == Job_Hunter || Class == Job_Sniper){	//CHECK
+			} else if (BaseJob == Job_Hunter){
 				mes "[Master Kavaruk]";
 				mes "Arpesto is waiting";
 				mes "outside. Since he's a";
 				mes "veteran hunter, he can";
 				mes "be of great help to you.";
 				close;
-
 			} else if(Class == Job_Archer || Class == Job_Baby_Archer){
 				mes "As an Archer, I can see that you'd want to make sure that you have a strong grasp on the fundamentals";
 				mes "of our job. Would you like some specialized instruction?";
-
 			} else {
 				mes "Although you may not be able";
 				mes "to directly apply archery related knowledge, it's a good idea to understand the capabilities of";
@@ -360,7 +343,7 @@ payon_in02,54,13,3	script	Master Kavaruk	55,{
 			next;
 			switch(select("Yes:No")){
 			case 1:
-				set tu_archer01, 1;
+				tu_archer01 = 1;
 				mes "[Master Kavaruk]";
 				mes "Ah yes. Well then, please";
 				mes "take this message over to ^3131FFSeisner^000000 who is in the Training Grounds west of Icarus.";
@@ -391,25 +374,22 @@ payon_in02,54,13,3	script	Master Kavaruk	55,{
 			mes "hills west of Icarus and";
 			mes "talk to ^3131FFSeisner^000000 to begin your special training.";
 			close;
-
 		} else if(tu_archer01 == 3){
 			mes "So "+strcharinfo(0)+",";
 			mes "Did you have a good experience? Learning theory alone is never too enjoyable, but it is necessary.";
 			next;
 			mes "[Master Kavaruk]";
-			if(Class == Job_Archer || Class == Job_Archer_High || Class == Job_Baby_Archer){
+			if (BaseJob == Job_Archer){
 				mes "Now it is time to experience the principles you've just learned firsthand. Go speak to ^3131FFReidin Corse^000000, who is just outside of this building, and he'll tell you about the different skills.";
-				set tu_archer01, 4;
+				tu_archer01 = 4;
 			} else {
 				mes "Still, there's no need for you to do any field training since only Archers can actually participate";
 				mes "in that.";
 			}
 			close;
-
-		} else if(tu_archer01 == 4){
+		} else if(tu_archer01 == 4 && BaseJob == Job_Archer) {
 			mes "Reidin Corse is just outside of this building. Didn't you see him on your way in?";
 			close;
-
 		}
 	}
 	mes "You're great";
@@ -420,8 +400,14 @@ payon_in02,54,13,3	script	Master Kavaruk	55,{
 
 // Reidin Corse
 //============================================================
-pay_arche,103,165,5	script	Reidin Corse#tu	832,{
+pay_arche,103,165,5	script	Reidin Corse#tu	4_M_REIDIN_KURS,{
 	mes "[Reidin Corse]";
+	if (checkweight(1201,1) == 0) {
+		mes "[Reidin Corse]";
+		mes "Why are you carrying";
+		mes "so much stuff? You better put everything you don't need into Kafra Storage.";
+		close;
+	}
 	if(MaxWeight - Weight < 2000){
 		mes "Hey, you're carrying an";
 		mes "awful lot of stuff. You ought";
@@ -429,7 +415,7 @@ pay_arche,103,165,5	script	Reidin Corse#tu	832,{
 		mes "into Kafra Storage...";
 		close;
 	}
-	if(tu_archer01 > 0){
+	if(tu_archer01 > 0 && (BaseJob != Job_Hunter && BaseJob != Job_Bard && BaseJob != Job_Dancer || Upper == 2)){
 		if(tu_archer01 == 4){
 			mes "Ah, what is it?";
 			mes "Can I help you";
@@ -493,27 +479,25 @@ pay_arche,103,165,5	script	Reidin Corse#tu	832,{
 			mes "^3355FFReidin Corse narrows his experienced eyes and sizes you up.^000000";
 			next;
 			mes "[Reidin Corse]";
-			if(JobLevel > 39){
+			if(JobLevel > 39)
 				mes "Hey! You're ready to change jobs, aren't you? Don't waste time here and just get a new job already! Eh, or you can ask Master Kavaruk for something to do.";
-				close;
-			} else if(JobLevel < 5){
+			else if(JobLevel < 5){
 				mes "Alright! We got a fresh Archer here! You're lucky you came to me, I'm the best teacher you can find! But let me warn you, I teach at a really fast pace, so try to keep";
 				mes "up. Okay? Good.";
 				next;
-				set tu_archer01, 5;
+				tu_archer01 = 5;
 				mes "[Reidin Corse]";
 				mes "Okay, I'm ready to begin the lessons! Come back over here once you've got your bow and arrows and everything else ready, got it?";
-				close;
 			} else {
 				mes "Okay. It looks like you know";
 				mes "some stuff. But even if you're";
 				mes "a little experienced, there's still room for you to learn. So try and keep up, got it?";
 				next;
-				set tu_archer01, 5;
+				tu_archer01 = 5;
 				mes "[Reidin Corse]";
 				mes "Well, I'm ready to teach whenever you're ready to learn. Come back once your bow and arrows and everything else is ready, alright?";
-				close;
 			}
+			close;
 		} else if(tu_archer01 == 5){
 			mes "You ready? I think it's fair to remind you that I won't tolerate any complaining! Just do what";
 			mes "I say and you'll be the second best Archer in the world! After me, of course~";
@@ -544,9 +528,9 @@ pay_arche,103,165,5	script	Reidin Corse#tu	832,{
 				mes "if you can't aim at your targets, you'd be horrible at archery, right? You gotta have Accuracy!";
 				next;
 				mes "[Reidin Corse]";
-				set .@skill_owl, getskilllv("AC_OWL");
+				.@skill_owl = getskilllv("AC_OWL");
 				if(.@skill_owl < 3){
-					set tu_archer01, 6;
+					tu_archer01 = 6;
 					mes "Now, your first assignment";
 					mes "is to learn ^3131FFLevel 3 Owl's Eye^000000!";
 					if (checkre(0))
@@ -624,7 +608,7 @@ pay_arche,103,165,5	script	Reidin Corse#tu	832,{
 				} else {
 					emotion e_ag;
 					mes "Huh. So you already know a little about Owl's Eye already, huh? Well then, I guess I oughta talk about something you don't know about!";
-					set tu_archer01, 7;
+					tu_archer01 = 7;
 					getexp 500,0;
 					close;
 				}
@@ -639,7 +623,7 @@ pay_arche,103,165,5	script	Reidin Corse#tu	832,{
 				close;
 			}
 		} else if(tu_archer01 == 6){
-			set .@owl, getskilllv(43);
+			.@owl = getskilllv(43);
 			if(.@owl > 2){
 				mes "Ah, you're";
 				mes "finally back.";
@@ -651,7 +635,7 @@ pay_arche,103,165,5	script	Reidin Corse#tu	832,{
 				mes "Ah, but your eyes look a lot sharper than they used to be.";
 				mes "Here, this is a small, special reward for you.";
 				specialeffect2 EF_WIND;
-				set tu_archer01, 7;
+				tu_archer01 = 7;
 				getexp 500,0;
 				next;
 				emotion e_pif;
@@ -730,8 +714,7 @@ pay_arche,103,165,5	script	Reidin Corse#tu	832,{
 			mes "so they're perfect for target";
 			mes "practice from a distance!";
 			next;
-
-			set .@eagle, getskilllv("AC_VULTURE");
+			.@eagle = getskilllv("AC_VULTURE");
 			if(.@eagle < 3){
 				if (checkre(0))
 					mes "Alright, Mandagora usually live around Geffen. I can send you to Geffen, so just travel north from there to find";
@@ -745,16 +728,15 @@ pay_arche,103,165,5	script	Reidin Corse#tu	832,{
 				if(MaxWeight - Weight < 1000){
 					mes "You've got plenty of arrows";
 					mes "for that, so go for it!";
-					set tu_archer01, 8;
-					next;
+					tu_archer01 = 8;
 				} else {
 					next;
 					mes "[Reidin Corse]";
 					mes "Here's a little something to encourage you. Since Mandagora are Earth property monsters, these Fire Arrows will work really well.";
-					set tu_archer01,8;
+					tu_archer01 = 8;
 					getitem 1752,300; //Fire_Arrow
-					next;
 				}
+				next;
 				mes "[Reidin Corse]";
 				mes "Alright~";
 				mes "Ready to go?";
@@ -786,12 +768,12 @@ pay_arche,103,165,5	script	Reidin Corse#tu	832,{
 				mes "You already";
 				mes "learned Vulture's Eye!";
 				mes "Why did I spend so much time explaining about it? Well, I guess we're ready to move on to the next lesson...";
-				set tu_archer01,9;
+				tu_archer01 = 9;
 				getitem 1752,500; //Fire_Arrow
 				close;
 			}
 		} else if(tu_archer01 == 8){
-			set .@eagle, getskilllv("AC_VULTURE");
+			.@eagle = getskilllv("AC_VULTURE");
 			if(.@eagle < 3){
 				if (checkre(0))
 					mes "Mandagora lives in the area north of Geffen. Learn up to Level 3 Vulture's Eye and get acquainted with your attack range.";
@@ -824,7 +806,7 @@ pay_arche,103,165,5	script	Reidin Corse#tu	832,{
 			} else {
 				mes "Ah, so you've learned";
 				mes "a little something about Vulture's Eye! What do you think about it now? Ah, and here's a little reward for you before I start the next lesson~";
-				set tu_archer01,9;
+				tu_archer01 = 9;
 				getitem 1752,500; //Fire_Arrow
 				close;
 			}
@@ -846,7 +828,7 @@ pay_arche,103,165,5	script	Reidin Corse#tu	832,{
 			mes "Whaddya think?";
 			mes "Exciting, yes?";
 			next;
-			set .@eagle, getskilllv("AC_DOUBLE");
+			.@eagle = getskilllv("AC_DOUBLE");
 			if(.@eagle > 2){
 				mes "["+strcharinfo(0)+"]";
 				mes "I...";
@@ -860,7 +842,7 @@ pay_arche,103,165,5	script	Reidin Corse#tu	832,{
 				mes "[Reidin Corse]";
 				mes "^333333*Sigh*^000000";
 				mes "Next lesson...";
-				set tu_archer01, 10;
+				tu_archer01 = 10;
 				close;
 			} else {
 				mes "[Reidin Corse]";
@@ -878,7 +860,7 @@ pay_arche,103,165,5	script	Reidin Corse#tu	832,{
 				next;
 				if(.@eagle == 0){
 					specialeffect2 EF_WIND;
-					set tu_archer01, 10;
+					// tu_archer01 = 10;
 					getexp 0,500;
 				}
 				emotion e_no1;
@@ -928,9 +910,9 @@ pay_arche,103,165,5	script	Reidin Corse#tu	832,{
 				mes "[Reidin Corse]";
 				mes "It's easier to find Izlude if you pass by Prontera, so head towards the fields near Prontera. Alright, off you go, " + strcharinfo(0) + "~";
 			}
-			set tu_archer01,11;
+			tu_archer01 = 11;
 			close;
-		} else if(tu_archer01 == 11){
+		} else if(tu_archer01 == 11){// Grasshopper's_Leg
 			if (checkre(0) && countitem(940) < 10) {
 				mes "Hey...";
 				mes "You gotta bring";
@@ -956,7 +938,7 @@ pay_arche,103,165,5	script	Reidin Corse#tu	832,{
 				mes "Good! You pass.";
 				mes "Don't forget that you gotta keep training and improve your skills. Before you know it, you'll be an expert almost as good as me!";
 				specialeffect2 EF_WIND;
-				set tu_archer01, 12;
+				tu_archer01 = 12;
 				if (checkre(0))
 					delitem 940,10; //Grasshopper's_Leg
 				getexp 1000,1000;
@@ -984,7 +966,7 @@ pay_arche,103,165,5	script	Reidin Corse#tu	832,{
 			mes "[Reidin Corse]";
 			mes "You might not be able";
 			mes "to see it now, but as you become more skilled, you'll eventually recognize all the benefits of this skill.";
-			set tu_archer01, 13;
+			tu_archer01 = 13;
 			close;
 		} else if(tu_archer01 == 13){
 			mes "^333333*Sigh*^000000";
@@ -1029,14 +1011,14 @@ pay_arche,103,165,5	script	Reidin Corse#tu	832,{
 			} else {
 				mes "This stuff is just a little something to encourage you, so don't take it the wrong way! Now hurry up, get what I asked for,";
 				mes "and come back!";
-				set tu_archer01,14;
+				tu_archer01 = 14;
 				getitem 601,10; //Wing_Of_Fly
 				getitem 501,10; //Red_Potion
 				getitem 602,1; //Wing_Of_Butterfly
 				close;
 			}
 		} else if((tu_archer01 == 14) || (tu_archer01 == 15)){
-			if (checkre(0) && (countitem(906) < 10 || countitem(921) < 10)) {
+			if (checkre(0) && (countitem(906) < 10 || countitem(921) < 10)) {// Pointed_Scale, Mushroom_Spore
 				mes "Remember, you";
 				mes "need to head into";
 				mes "the forest east of Payon and get me";
@@ -1089,7 +1071,7 @@ pay_arche,103,165,5	script	Reidin Corse#tu	832,{
 			mes "my final exam! Here, take";
 			mes "this little reward!";
 			specialeffect2 EF_WIND;
-			set tu_archer01,16;
+			tu_archer01 = 16;
 			if (checkre(3))
 				getexp 1000,1000;
 			else
@@ -1189,7 +1171,7 @@ pay_arche,103,165,5	script	Reidin Corse#tu	832,{
 
 // Seisner
 //============================================================
-pay_arche,84,139,3	script	Seisner	727,{
+pay_arche,84,139,3	script	Seisner	4_F_JOB_HUNTER,{
 	specialeffect EF_HIT2,"#Target";
 	mes "[Seisner]";
 	mes "Aaaah!";
@@ -1216,8 +1198,8 @@ pay_arche,84,139,3	script	Seisner	727,{
 		mes "So Master Kavaruk";
 		mes "wants me to teach you";
 		mes "about Archers and Archery? Alright, what would you like to know more about?";
-		next;
 		while(1){
+			next;
 			switch(select("About Archers.:Stats for Archers:End Conversation.")){
 			case 1:
 				mes "[Seisner]";
@@ -1243,8 +1225,7 @@ pay_arche,84,139,3	script	Seisner	727,{
 				next;
 				mes "[Seisner]";
 				mes "Hunters can ultimately transcend into ^3131FFSnipers^000000, Bards into ^3131FFMinstrels^000000, and Dancers into ^3131FFGypsies^000000. I know that's pretty complex.";
-				if(tu_archer01 == 1) set tu_archer01, 2;
-				next;  
+				if(tu_archer01 == 1) tu_archer01 = 2;
 				break;
 			case 2:
 				mes "[Seisner]";
@@ -1270,8 +1251,8 @@ pay_arche,84,139,3	script	Seisner	727,{
 						mes "increase the amount of weight";
 						mes "that any character can carry. So if you have more STR, you can carry around more arrows.";
 						next;
-						if(tu_archer01 == 1) set tu_archer01, 2;
-						break;
+						if(tu_archer01 == 1) tu_archer01 = 2;
+						continue;
 					case 2:
 						mes "[Seisner]";
 						mes "^FF3131AGI^000000 as you know, ";
@@ -1281,8 +1262,8 @@ pay_arche,84,139,3	script	Seisner	727,{
 						mes "[Seisner]";
 						mes "One of the drawbacks of being an Archer is having weaker strength and lower Dodge Rate. However, the Dodge Rate disadvantage is lessened by increasing your ^FF3131AGI^000000.";
 						next;
-						if(tu_archer01 == 1) set tu_archer01, 2;
-						break;
+						if(tu_archer01 == 1) tu_archer01 = 2;
+						continue;
 					case 3:
 						mes "[Seisner]";
 						mes "^3131FFVIT^000000 will increase";
@@ -1297,8 +1278,8 @@ pay_arche,84,139,3	script	Seisner	727,{
 						mes "VIT might be helpful";
 						mes "depending on how you use it. Still, I wouldn't recommend focusing too much on increasing your VIT if you're an Archer.";
 						next;
-						if(tu_archer01 == 1) set tu_archer01, 2;
-						break;
+						if(tu_archer01 == 1) tu_archer01 = 2;
+						continue;
 					case 4:
 						mes "[Seisner]";
 						mes "^FF0000INT^000000 is more";
@@ -1313,8 +1294,8 @@ pay_arche,84,139,3	script	Seisner	727,{
 						mes "[Seisner]";
 						mes "For Hunters, one of the Second Job Classes for Archers, having higher INT will increase the damage inflicted by ^3131FFFalcons^000000.";
 						next;
-						if(tu_archer01 == 1) set tu_archer01, 2;
-						break;
+						if(tu_archer01 == 1) tu_archer01 = 2;
+						continue;
 					case 5:
 						mes "[Seisner]";
 						mes "Now DEX is almost";
@@ -1332,8 +1313,8 @@ pay_arche,84,139,3	script	Seisner	727,{
 						mes "[Seisner]";
 						mes "Just like any other class, ^FF3131DEX^000000 increases ^3131FFAccuracy (HIT)^000000 and slightly enhances Attack Speed (ASPD).";
 						next;
-						if(tu_archer01 == 1) set tu_archer01, 2;
-						break;
+						if(tu_archer01 == 1) tu_archer01 = 2;
+						continue;
 					case 6:
 						mes "[Seisner]";
 						mes "^FF0000LUK^000000 affects your";
@@ -1343,18 +1324,16 @@ pay_arche,84,139,3	script	Seisner	727,{
 						mes "[Seisner]";
 						mes "For Hunters, one of the Second Classes for the Archer, LUK increases the chance of ^3131FFBlitz Beat^000000 occurring automatically. So Hunters who rely on their Falcons might want to invest in LUK.";
 						next;
-						if(tu_archer01 == 1) set tu_archer01, 2;
-						break;
+						if(tu_archer01 == 1) tu_archer01 = 2;
+						continue;
 					case 7:
 						mes "[Seisner]";
 						mes "So is there";
 						mes "anything else that";
 						mes "you want to ask me?";
-						next;
-						set .@exitwhile,1;
 						break;
 					}
-					if (.@exitwhile) break;
+					break;
 				}
 				break;
 			case 3:
@@ -1378,7 +1357,7 @@ pay_arche,84,139,3	script	Seisner	727,{
 		mes "If you feel comfortable enough with the knowledge I've taught you, you should go back to Master Kavaruk. But if you still have questions, you can always come and ask me.";
 		close2;
 		specialeffect2 EF_WIND;
-		set tu_archer01, 3;
+		tu_archer01 = 3;
 		if(JobLevel == 1){
 			getexp 0,30;
 		} else if((JobLevel > 1) && (JobLevel < 11)){
@@ -1395,7 +1374,7 @@ pay_arche,84,139,3	script	Seisner	727,{
 
 // Target
 //============================================================
-pay_arche,76,135,3	script	#Target	111,{ end; }
+pay_arche,76,135,3	script	#Target	HIDDEN_NPC,{ end; }
 
 // Acolyte
 //============================================================
@@ -1431,7 +1410,7 @@ pay_arche,76,135,3	script	#Target	111,{ end; }
 				mes "Wow!";
 				mes "Thank you, thank you!";
 				mes "I''ll try my very best!";
-				set tu_archer01, 15;
+				tu_archer01 = 15;
 				close;
 			case 2:
 				mes "[Acolyte]";
@@ -1456,8 +1435,6 @@ pay_arche,76,135,3	script	#Target	111,{ end; }
 					mes "Y-you're a man!";
 					mes "D-d-d-don't tease me";
 					mes "like that! I'm serious!";
-					set tu_archer01, 15;
-					close;
 				} else {
 					mes "And he's so brave";
 					mes "and funny and smart.";
@@ -1468,12 +1445,12 @@ pay_arche,76,135,3	script	#Target	111,{ end; }
 					mes "^333333*Sniffle*^000000";
 					mes "I'll d-do my best to help you!";
 					mes "I wish you both happiness! (Waaaaaah~!)";
-					set tu_archer01, 15;
-					close;
 				}
+				tu_archer01 = 15;
+				close;
 			}
-			end;
-		} else {
+		}
+		else {
 			mes "^666666Zzzzz...^000000";
 			mes "Wh-wha...?";
 			mes "Who are you?";
@@ -1487,7 +1464,6 @@ pay_arche,76,135,3	script	#Target	111,{ end; }
 				case 0: npcskill "AL_HEAL",3,90,62; break;
 				case 1: npcskill "AL_HEAL",9,90,62; break;
 				case 2: npcskill "AL_HEAL",8,90,62; break;
-				case 3: break;
 			}
 			switch(rand(3)) {
 				case 0: npcskill "AL_INCAGI",1,0,0; break;
@@ -1501,7 +1477,8 @@ pay_arche,76,135,3	script	#Target	111,{ end; }
 			}
 			close;
 		}
-	} else if(tu_archer01 == 15){
+	}
+	else if(tu_archer01 == 15){
 		if((gettime(3) >= 18) && (gettime(3) < 22)){
 			mes "^666666Zzzzz...^000000";
 			mes "Wh-wha...?";
@@ -1528,7 +1505,6 @@ pay_arche,76,135,3	script	#Target	111,{ end; }
 			case 0: npcskill "AL_HEAL",3,90,62; break;
 			case 1: npcskill "AL_HEAL",10,90,62; break;
 			case 2: npcskill "AL_HEAL",8,90,62; break;
-			case 3: break;
 		}
 		switch(rand(3)) {
 			case 0: npcskill "AL_INCAGI",1,0,0; break;
@@ -1553,7 +1529,7 @@ pay_arche,76,135,3	script	#Target	111,{ end; }
 
 // Alchemist Guildmember
 //============================================================
-pay_arche,130,113,3	script	Alchemist Guildmember#tu	740,{
+pay_arche,130,113,3	script	Alchemist Guildmember#tu	2_M_ALCHE,{
 	mes "[Alchemist Guildmember]";
 	if(tu_archer02 > 0){
 		if((countitem(1032) > 0) || (countitem(905) > 0 )){	// Blossom_Of_Maneater & Stem
@@ -1580,20 +1556,14 @@ pay_arche,130,113,3	script	Alchemist Guildmember#tu	740,{
 				mes "Flora field! I really";
 				mes "appreciate your help~";
 
-				set .@alche_f,countitem(1032); // Blossom_Of_Maneater
-				set .@alche_s,countitem(905); // Stem
-				set .@f_zeny, .@alche_f * 130;
-				set .@s_zeny, .@alche_s * 30;
-				set .@total_zeny, .@f_zeny + .@s_zeny;
-				if(countitem(1032) == 0){
-					delitem 905, .@alche_s; //Stem
-				} else if(countitem(905) == 0){
-					delitem 1032, .@alche_f; //Blossom_Of_Maneater
-				} else {
+				.@alche_f = countitem(1032); // Blossom_Of_Maneater
+				.@alche_s = countitem(905); // Stem
+				.@total_zeny = .@alche_f * 130 + .@alche_s * 30;
+				if (countitem(1032))
 					delitem 1032, .@alche_f; //Blossom_Of_Maneater
+				if (countitem(905))
 					delitem 905, .@alche_s; //Stem
-				}
-				set Zeny,Zeny+.@total_zeny;
+				Zeny = Zeny+.@total_zeny;
 				close;
 			case 2:
 				mes "[Alchemist Guildmember]";
@@ -1627,7 +1597,7 @@ pay_arche,130,113,3	script	Alchemist Guildmember#tu	740,{
 
 // Arthail
 //============================================================
-prontera,126,335,5	script	Arthail	51,{
+prontera,126,335,5	script	Arthail	1_M_BARD,{
 	mes "[Arthail]";
 	if(tu_archer02 < 3){
 		mes "I am the Bard";
@@ -1679,16 +1649,15 @@ prontera,126,335,5	script	Arthail	51,{
 			next;
 			mes "[Arthail]";
 			mes "Would you please go and see if there's any news being spread around in that area for me?";
-			set tu_archer02, 3;
+			tu_archer02 = 3;
 		}
-		close;
-	} else if(tu_archer02 == 3){
+	}
+	else if(tu_archer02 == 3){
 		mes "I'm ashamed to ask,";
 		mes "but would you go the fountain in Prontera's central plaza and see";
 		mes "if there's any news?";
-		close;
-
-	} else if(tu_archer02 == 4){
+	}
+	else if(tu_archer02 == 4){
 		mes "I see...";
 		mes "Nothing much.";
 		mes "Another person";
@@ -1767,14 +1736,11 @@ prontera,126,335,5	script	Arthail	51,{
 		mes "I can't help it then.";
 		mes "I'd better go check";
 		mes "this out on my own.";
-		set tu_archer02, 5;
-		close;
-
-	} else if(tu_archer02 > 4 && tu_archer02 < 7){
+		tu_archer02 = 5;
+	}
+	else if(tu_archer02 > 4 && tu_archer02 < 7)
 		mes "^333333Zzzzz...^000000";
-		close;
-
-	} else if(tu_archer02 == 8){
+	else if(tu_archer02 == 8){
 		mes "Where did you go?";
 		mes "I thought you might have been kidnapped since you weren't";
 		mes "here when I woke up.";
@@ -1798,17 +1764,18 @@ prontera,126,335,5	script	Arthail	51,{
 		mes "I guess I'll finally";
 		mes "have a song that";
 		mes "I can share with you.";
-		set tu_archer02, 9;
-		close;
+		tu_archer02 = 9;
+	}
+	else {
+		mes "...";
+		mes "......";
 	}
-	mes "...";
-	mes "......";
 	close;
 }
 
 // New Guild Master
 //============================================================
-prontera,167,281,3	script	New Guild Master#tu	753,{
+prontera,167,281,3	script	New Guild Master#tu	4_M_MONK,{
 	mes "[New Guild Master]";
 	mes "Hearken, all";
 	mes "of you who seek";
@@ -1825,13 +1792,13 @@ prontera,167,281,3	script	New Guild Master#tu	753,{
 	mes "those who call themselves";
 	mes "my comrades will never";
 	mes "know the taste of defeat!";
-	if(tu_archer02 == 3) set tu_archer02, 4;
+	if(tu_archer02 == 3) tu_archer02 = 4;
 	close;
 }
 
 // Mage
 //============================================================
-prontera,144,301,5	script	Mage#tu	123,{
+prontera,144,301,5	script	Mage#tu	2_F_MAGICMASTER,{
 	mes "[Mage]";
 	mes "Did you see that";
 	mes "new guild master?";
@@ -1859,7 +1826,7 @@ prontera,144,301,5	script	Mage#tu	123,{
 
 // Minister
 //============================================================
-prt_castle,76,165,6	script	Minister#tu	55,{
+prt_castle,76,165,6	script	Minister#tu	1_M_JOBTESTER,{
 	mes "[Minister]";
 	mes "The royal family";
 	mes "is extremely busy";
@@ -1878,15 +1845,15 @@ prt_castle,76,165,6	script	Minister#tu	55,{
 		next;
 		mes "[Minister]";
 		mes "Ah, there are a few stray cats running around, those Wild Roses, so those noises you heard must have been them. Everything is fine here in the palace.";
-		set tu_archer02, 6;
-		close;
-	} else if(tu_archer02 == 7){
+		tu_archer02 = 6;
+	}
+	else if(tu_archer02 == 7){
 		next;
 		mes "^3355FFThe urgency that the Minister expressed earlier has been";
 		mes "replaced with a stone cold feeling of sternness. He probably won't tell you much more information,";
 		mes "no matter how much you ask him.";
-		close;
-	} else if(tu_acolyte01 == 25){
+	}
+	else if(tu_acolyte01 == 25){
 		next;
 		mes "["+strcharinfo(0)+"]";
 		mes "Um...";
@@ -1913,20 +1880,20 @@ prt_castle,76,165,6	script	Minister#tu	55,{
 		next;
 		mes "[Minister]";
 		mes "As a holy servant, I'm sure that you'll find plenty of other people who have need of your help and abilities outside of the palace.";
-	} 
+	}
 	close;
 }
 
 // Range NPC
 //============================================================
-prt_castle,94,150,4	script	#sound_tu	139,6,6,{
+prt_castle,94,150,4	script	#sound_tu	HIDDEN_WARP_NPC,6,6,{
 OnTouch_:
 	if(tu_archer02 == 6) {
 		mes "[Minister]";
 		mes "Contact the";
 		mes "Prontera Church.";
 		mes "Hurry, this is urgent!";
-		set tu_archer02, 7;
+		tu_archer02 = 7;
 		close;
 	}
 	end;
@@ -1934,7 +1901,7 @@ OnTouch_:
 
 // Bishop Maugins
 //============================================================
-monk_in,19,43,1	script	Bishop Maugins	60,{
+monk_in,19,43,1	script	Bishop Maugins	1_M_PASTOR,{
 	mes "[Bishop Maugins]";
 	if(tu_archer02 == 7){
 		mes "Greetings.";
@@ -1947,8 +1914,7 @@ monk_in,19,43,1	script	Bishop Maugins	60,{
 		mes "charge of that area. So";
 		mes "how may I help you?";
 		next;
-		switch(select("Has something happened to the Kingdom?:Nothing.")) {
-		case 1:
+		if (select("Has something happened to the Kingdom?:Nothing.") == 1) {
 			mes "[Bishop Maugins]";
 			mes "...!!";
 			next;
@@ -1961,8 +1927,7 @@ monk_in,19,43,1	script	Bishop Maugins	60,{
 			mes "Of course not!";
 			mes "The king and I regularly write to each other, but I haven't heard of anything in particular. Please don't worry yourself.";
 			next;
-			set tu_archer02, 8;
-			break;
+			tu_archer02 = 8;
 		}
 		mes "[Bishop Maugins]";
 		mes "Good luck on";

+ 677 - 1217
npc/quests/first_class/tu_ma_th01.txt

@@ -1,1336 +1,796 @@
-//===== rAthena Script ======================================= 
+//===== rAthena Script =======================================
 //= Thief Mage Quest Story Mode
-//===== By: ================================================== 
+//===== By: ==================================================
 //= Fix up by Jukka
-//===== Current Version: ===================================== 
-//= 1.3
-//===== Compatible With: ===================================== 
+//===== Current Version: =====================================
+//= 2.0
+//===== Compatible With: =====================================
 //= rAthena Project
-//===== Description: ========================================= 
+//===== Description: =========================================
 //= [Official Conversion]
 //= Battle Traces from Thief and Magician training quests.
-//===== Additional Comments: ================================= 
+//===== Additional Comments: =================================
 //= 1.0 Fully working
 //= 1.1 optimized [Lupus]
 //= 1.2 Changed "battle_trace" to "Trace of Battle". [L0ne_W0lf]
 //= 1.3 Misc. updates. [L0ne_W0lf]
-//============================================================ 
+//= 2.0 Clean-up. [Capuche]
+//============================================================
 
 // Battle Traces 1
 //============================================================
-moc_fild12,166,369,0	script	Trace of Battle#1	844,{
-	if(BaseClass == Job_Thief){
-		if(tu_thief01 < 5){
-			mes "^3355FFYou find traces of poison";
-			mes "used in a battle. You sense";
-			mes "its potency and decide not";
-			mes "to get too close to it.^000000";
-			close;
-		} else if(tu_thief01 == 5){
-			mes "^3355FFYou find traces of poison";
-			mes "used in a battle. A feeling";
-			mes "of acute dizziness overcomes";
-			mes "you after examining the scene.^000000";
-			set tu_thief01, 6;
-			sc_start SC_Poison,60000,0;
-			specialeffect EF_VENOMDUST2;
-			close;
-		} else if(tu_thief01 == 6){
-			mes "^3355FFYou find traces of poison";
-			mes "used in a battle. A feeling";
-			mes "of acute dizziness overcomes";
-			mes "you after examining the scene.^000000";
-			sc_start SC_Poison,60000,0;
-			specialeffect EF_VENOMDUST2;
-			close;
-		} else if(tu_thief01 == 7){
-			mes "^3355FFYou find traces of poison";
-			mes "used in a battle. You sense";
-			mes "its potency and decide not";
-			mes "to get too close to it.^000000";
-			close;
-		} else if(tu_thief01 == 8){
-			mes "^3355FFThere are traces of a battle";
-			mes "that seem to be leading in";
-			mes "different directions. From the";
-			mes "peculiar smell that permeates";
-			mes "the area, it seems that some";
-			mes "kind of lethal poison was used.^000000";
-			next;
-			mes "^3355FFStill...";
-			mes "The trail of this";
-			mes "battle decidedly";
-			mes "heads southward.^000000";
-			set tu_thief01, 9;
-			close;
-		} else {
-			mes "^3355FFYou find signs of";
-			mes "a heated pursuit";
-			mes "that head south.";
-			close;
-		}
-	}
-	if(BaseClass == Job_Mage){
-		if(tu_magician01 < 8){
-			mes "^3355FFYou find traces of poison";
-			mes "used in a battle. You sense";
-			mes "its potency and decide not";
-			mes "to get too close to it.^000000";
+moc_fild12,166,369,0	script	Trace of Battle#1	CLEAR_NPC,{
+	.@class_thief = ( BaseClass == Job_Thief );
+	.@class_mage = ( BaseClass == Job_Mage );
+	if (.@class_thief == 0 && .@class_mage == 0 || eaclass()&EAJL_BABY) {
+		mes "^3355FFThere are signs that show";
+		mes "that some violent scuffle";
+		mes "might have occurred here,";
+		mes "but you can't tell what had";
+		mes "happened exactly...^000000";
+	} else if (tu_thief01 < 5 && .@class_thief || .@class_mage && tu_magician01 < 8) {
+		mes "^3355FFYou find traces of poison";
+		mes "used in a battle. You sense";
+		mes "its potency and decide not";
+		mes "to get too close to it.^000000";
+		if (.@class_mage)
 			specialeffect EF_VENOMDUST2;
-			close;
-		} else if(tu_magician01 == 8){
-			mes "^3355FFThere are traces of a battle";
-			mes "that seem to be leading in";
-			mes "different directions. From the";
-			mes "peculiar smell that permeates";
-			mes "the area, it seems that some";
-			mes "kind of lethal poison was used.^000000";
-			next;
-			mes "^3355FFStill...";
-			mes "The trail of this";
-			mes "battle decidedly";
-			mes "heads southward.^000000";
-			set tu_magician01, 9;
-			close;
-		} else {
-			mes "^3355FFYou find signs of";
-			mes "a heated pursuit";
-			mes "that head south.";
-			close;
-		}
+	} else if (tu_thief01 == 5 && .@class_thief) {
+		mes "^3355FFYou find traces of poison";
+		mes "used in a battle. A feeling";
+		mes "of acute dizziness overcomes";
+		mes "you after examining the scene.^000000";
+		tu_thief01 = 6;
+		sc_start SC_Poison,60000,0;
+		specialeffect EF_VENOMDUST2;
+	} else if (tu_thief01 == 6 && .@class_thief) {
+		mes "^3355FFYou find traces of poison";
+		mes "used in a battle. A feeling";
+		mes "of acute dizziness overcomes";
+		mes "you after examining the scene.^000000";
+		sc_start SC_Poison,60000,0;
+		specialeffect EF_VENOMDUST2;
+	} else if (tu_thief01 == 7 && .@class_thief) {
+		mes "^3355FFYou find traces of poison";
+		mes "used in a battle. You sense";
+		mes "its potency and decide not";
+		mes "to get too close to it.^000000";
+	} else if (.@class_thief && tu_thief01 == 8 || .@class_mage && tu_magician01 == 8) {
+		mes "^3355FFThere are traces of a battle";
+		mes "that seem to be leading in";
+		mes "different directions. From the";
+		mes "peculiar smell that permeates";
+		mes "the area, it seems that some";
+		mes "kind of lethal poison was used.^000000";
+		next;
+		mes "^3355FFStill...";
+		mes "The trail of this";
+		mes "battle decidedly";
+		mes "heads southward.^000000";
+		if (tu_magician01 == 8)
+			tu_magician01 = 9;
+		else
+			tu_thief01 = 9;
+	} else {
+		mes "^3355FFYou find signs of";
+		mes "a heated pursuit";
+		mes "that head south.";
 	}
-	mes "^3355FFThere are signs that show";
-	mes "that some violent scuffle";
-	mes "might have occurred here,";
-	mes "but you can't tell what had";
-	mes "happened exactly...^000000";
 	close;
 }
 
 // Battle Traces 2
 //============================================================
-moc_fild12,173,215,0	script	Trace of Battle#2	844,{
-	if(BaseClass == Job_Thief){
-		if(tu_thief01 < 9){
-			mes "^3355FFThere are signs";
-			mes "that many people";
-			mes "have traveled through";
-			mes "this particular area.^000000";
-			close;
-		} else if(tu_thief01 == 9){
-			mes "^3355FFYou've found traces";
-			mes "of the pursuit which";
-			mes "continue eastward.^000000";
-			set tu_thief01, 10;
-			close;
-		} else {
-			mes "^3355FFThese traces of";
-			mes "the pursuit lead";
-			mes "towards the east.^000000";
-			close;
-		}
-	} else if(BaseClass == Job_Mage){
-		if(tu_magician01 < 9){
-			mes "^3355FFThere are signs";
-			mes "that many people";
-			mes "have traveled through";
-			mes "this particular area.^000000";
-			close;
-		} else if(tu_magician01 == 9){
-			mes "^3355FFYou've found traces";
-			mes "of the pursuit which";
-			mes "continue eastward.^000000";
-			set tu_magician01, 10;
-			close;
-		} else {
-			mes "^3355FFThese traces of";
-			mes "the pursuit lead";
-			mes "towards the east.^000000";
-			close;
-		}
+moc_fild12,173,215,0	script	Trace of Battle#2	CLEAR_NPC,{
+	.@class_thief = ( BaseClass == Job_Thief );
+	.@class_mage = ( BaseClass == Job_Mage );
+	if (.@class_thief == 0 && .@class_mage == 0 || eaclass()&EAJL_BABY) {
+		mes "^3355FFThere are signs that show";
+		mes "that some violent scuffle";
+		mes "might have occurred here,";
+		mes "but you can't tell what had";
+		mes "happened exactly...^000000";
+	} else if (tu_thief01 < 9 && .@class_thief || tu_magician01 < 9 && .@class_mage) {
+		mes "^3355FFThere are signs";
+		mes "that many people";
+		mes "have traveled through";
+		mes "this particular area.^000000";
+	} else if (tu_thief01 == 9 && .@class_thief || tu_magician01 == 9 && .@class_mage) {
+		mes "^3355FFYou've found traces";
+		mes "of the pursuit which";
+		mes "continue eastward.^000000";
+		if (tu_magician01 == 9)
+			tu_magician01 = 10;
+		else
+			tu_thief01 = 10;
+	} else {
+		mes "^3355FFThese traces of";
+		mes "the pursuit lead";
+		mes "towards the east.^000000";
 	}
-	mes "^3355FFThere are signs that show";
-	mes "that some violent scuffle";
-	mes "might have occurred here,";
-	mes "but you can't tell what had";
-	mes "happened exactly...^000000";
 	close;
 }
 
 // Battle Traces 3
 //============================================================
-moc_fild12,276,165,0	script	Trace of Battle#3	844,{
-	if(BaseClass == Job_Thief){
-		if(tu_thief01 < 10){
-			mes "^3355FFThere are signs";
-			mes "that many people";
-			mes "have traveled through";
-			mes "this particular area.^000000";
-			close;
-		} else if(tu_thief01 == 10){
-			mes "^3355FFYou examine these traces";
-			mes "and notice that one set of";
-			mes "footprints looks almost too";
-			mes "pronounced, as if it had been";
-			mes "made for somebody to find.^000000";
-			set tu_thief01, 11;
-			close;
-		} else {
-			mes "^3355FFIt's a very";
-			mes "strange looking";
-			mes "set of footprints.^000000";
-			close;
-		}
-	} else if(BaseClass == Job_Mage){
-		if(tu_magician01 < 10){
-			mes "^3355FFThere are signs";
-			mes "that many people";
-			mes "have traveled through";
-			mes "this particular area.^000000";
-			close;
-		} else if(tu_magician01 == 10){
-			mes "^3355FFYou examine these traces";
-			mes "and notice that one set of";
-			mes "footprints looks almost too";
-			mes "pronounced, as if it had been";
-			mes "made for somebody to find.^000000";
-			set tu_magician01, 11;
-			close;
-		} else {
-			mes "^3355FFIt's a very";
-			mes "strange looking";
-			mes "set of footprints.^000000";
-			close;
-		}
+moc_fild12,276,165,0	script	Trace of Battle#3	CLEAR_NPC,{
+	.@class_thief = ( BaseClass == Job_Thief );
+	.@class_mage = ( BaseClass == Job_Mage );
+	if (.@class_thief == 0 && .@class_mage == 0 || eaclass()&EAJL_BABY) {
+		mes "^3355FFThese look like";
+		mes "traces of some kind";
+		mes "of pursuit or battle, but";
+		mes "you can't really tell for sure.^000000";
+	} else if (tu_thief01 < 10 && .@class_thief || tu_magician01 < 10 && .@class_mage) {
+		mes "^3355FFThere are signs";
+		mes "that many people";
+		mes "have traveled through";
+		mes "this particular area.^000000";
+	} else if (tu_thief01 == 10 && .@class_thief || tu_magician01 == 10 && .@class_mage) {
+		mes "^3355FFYou examine these traces";
+		mes "and notice that one set of";
+		mes "footprints looks almost too";
+		mes "pronounced, as if it had been";
+		mes "made for somebody to find.^000000";
+		if (tu_magician01 == 10)
+			tu_magician01 = 11;
+		else
+			tu_thief01 = 11;
+	} else {
+		mes "^3355FFIt's a very";
+		mes "strange looking";
+		mes "set of footprints.^000000";
 	}
-	mes "^3355FFThese look like";
-	mes "traces of some kind";
-	mes "of pursuit or battle, but";
-	mes "you can't really tell for sure.^000000";
 	close;
 }
 
 // Battle Traces 4
 //============================================================
-moc_fild11,39,163,0	script	Trace of Battle#4	844,{
-	if(BaseClass == Job_Thief){
-		if(tu_thief01 < 11){
-			mes "^3355FFThere are signs";
-			mes "that many people";
-			mes "have traveled through";
-			mes "this particular area.^000000";
-			close;
-		} else if(tu_thief01 == 11){
-			mes "^3355FFFrom these traces,";
-			mes "you see that another";
-			mes "set of footprints has";
-			mes "been added. It looks like";
-			mes "someone } else got involved.";
-			mes "These prints are distinctly";
-			mes "clearer and much smaller.^000000";
-			set tu_thief01, 12;
-			close;
-		} else {
-			mes "^3355FFFrom these traces,";
-			mes "you see that another";
-			mes "person has gotten involved";
-			mes "in this heated scuffle.^000000";
-			close;
-		}
-	} else if(BaseClass == Job_Mage){
-		if(tu_magician01 < 11){
-			mes "^3355FFThere are signs";
-			mes "that many people";
-			mes "have traveled through";
-			mes "this particular area.^000000";
-			close;
-		} else if(tu_magician01 == 11){
-			mes "^3355FFFrom these traces,";
-			mes "you see that another";
-			mes "set of footprints has";
-			mes "been added. It looks like";
-			mes "someone } else got involved.";
-			mes "These prints are distinctly";
-			mes "clearer and much smaller.^000000";
-			set tu_magician01, 12;
-			close;
-		} else {
-			mes "^3355FFFrom these traces,";
-			mes "you see that another";
-			mes "person has gotten involved";
-			mes "in this heated scuffle.^000000";
-			close;
-		}
+moc_fild11,39,163,0	script	Trace of Battle#4	CLEAR_NPC,{
+	.@class_thief = ( BaseClass == Job_Thief );
+	.@class_mage = ( BaseClass == Job_Mage );
+	if (.@class_thief == 0 && .@class_mage == 0 || eaclass()&EAJL_BABY) {
+		mes "^3355FFThere are signs that show";
+		mes "that some violent scuffle";
+		mes "might have occurred here,";
+		mes "but you can't tell what had";
+		mes "happened exactly...^000000";
+	} else if (tu_thief01 < 11 && .@class_thief || tu_magician01 < 11 && .@class_mage) {
+		mes "^3355FFThere are signs";
+		mes "that many people";
+		mes "have traveled through";
+		mes "this particular area.^000000";
+	} else if (tu_thief01 == 11 && .@class_thief || tu_magician01 == 11 && .@class_mage) {
+		mes "^3355FFFrom these traces,";
+		mes "you see that another";
+		mes "set of footprints has";
+		mes "been added. It looks like";
+		mes "someone else got involved.";
+		mes "These prints are distinctly";
+		mes "clearer and much smaller.^000000";
+		if (tu_magician01 == 11)
+			tu_magician01 = 12;
+		else
+			tu_thief01 = 12;
+	} else {
+		mes "^3355FFFrom these traces,";
+		mes "you see that another";
+		mes "person has gotten involved";
+		mes "in this heated scuffle.^000000";
 	}
-	mes "^3355FFThere are signs that show";
-	mes "that some violent scuffle";
-	mes "might have occurred here,";
-	mes "but you can't tell what had";
-	mes "happened exactly...^000000";
 	close;
 }
 
 // Battle Traces 5
 //============================================================
-moc_fild11,205,52,0	script	Trace of Battle#5	844,{
-	if(BaseClass == Job_Thief){
-		if(tu_thief01 < 12){
-			mes "^3355FFThere are signs";
-			mes "that many people";
-			mes "have traveled through";
-			mes "this particular area.^000000";
-			close;
-		} else if(tu_thief01 == 12){
-			mes "^3355FFThe traces of the battle";
-			mes "now split and head towards";
-			mes "the north and south. However,^000000";
-			if(countitem(506) == 0){
-				mes "^3355FFthere is a puddle of strong poison that you must neutralize before you can investigate this scene.^000000";
+moc_fild11,205,52,0	script	Trace of Battle#5	CLEAR_NPC,{
+	.@class_thief = ( BaseClass == Job_Thief );
+	.@class_mage = ( BaseClass == Job_Mage );
+	if (.@class_thief == 0 && .@class_mage == 0 || eaclass()&EAJL_BABY) {
+		mes "^3355FFThese look like";
+		mes "traces of some kind";
+		mes "of pursuit or battle, but";
+		mes "you can't really tell for sure.^000000";
+	} else if (tu_thief01 < 12 && .@class_thief || tu_magician01 < 12 && .@class_mage) {
+		mes "^3355FFThere are signs";
+		mes "that many people";
+		mes "have traveled through";
+		mes "this particular area.^000000";
+	} else if (tu_thief01 == 12 && .@class_thief || tu_magician01 == 12 && .@class_mage) {
+		mes "^3355FFThe traces of the battle";
+		mes "now split and head towards";
+		mes "the north and south. However,^000000";
+		if (countitem(506) == 0) {// Green_Potion
+			mes "^3355FFthere is a puddle of strong poison that you must neutralize before you can investigate this scene.^000000";
+			close;
+		}
+		mes "^3355FFthere is a puddle of strong poison that you must neutralize before you can investigate this scene.^000000";
+		next;
+		mes "^3355FFFortunately, you can";
+		mes "temporarily nullify the";
+		mes "poison by using one";
+		mes "of your Green Potions.^000000";
+		next;
+		switch(select("Use Green Potion.:Don't use it.")) {
+		case 1:
+			mes "^3355FFThe poison weakens";
+			mes "and some of it evaporates,";
+			mes "revealing a piece of cloth that";
+			mes "was hidden in that puddle.^000000";
+			next;
+			switch(select("Don't investigate.:Investigate.")) {
+			case 1:
+				mes "["+strcharinfo(0)+"]";
+				mes "A piece of cloth";
+				mes "is nothing to be";
+				mes "concerned about.";
 				close;
-			} else {
-				mes "^3355FFthere is a puddle of strong poison that you must neutralize before you can investigate this scene.^000000";
-				next;
-				mes "^3355FFFortunately, you can";
-				mes "temporarily nullify the";
-				mes "poison by using one";
-				mes "of your Green Potions.^000000";
+			case 2:
+				mes "["+strcharinfo(0)+"]";
+				mes "Hey... There's";
+				mes "blood on this cloth";
+				mes "and some writing on";
+				mes "it that I can't recognize.";
+				mes "Hopefully, this'll provide";
+				mes "some sort of clue to all this?";
+				if (tu_thief01 == 12)
+					tu_thief01 = 13;
+				else
+					tu_magician01 = 13;
 				next;
-				switch(select("Use Green Potion.:Don't use it.")){
-				case 1:
-					mes "^3355FFThe poison weakens";
-					mes "and some of it evaporates,";
-					mes "revealing a piece of cloth that";
-					mes "was hidden in that puddle.^000000";
-					next;
-					switch(select("Don't investigate.:Investigate.")){
-					case 1:
-						mes "["+strcharinfo(0)+"]";
-						mes "A piece of cloth";
-						mes "is nothing to be";
-						mes "concerned about.";
-						close;
-					case 2:
-						mes "["+strcharinfo(0)+"]";
-						mes "Hey... There's";
-						mes "blood on this cloth";
-						mes "and some writing on";
-						mes "it that I can't recognize.";
-						mes "Hopefully, this'll provide";
-						mes "some sort of clue to all this?";
-						set tu_thief01, 13;
-						next;
-						mes "^3355FFYou take the piece";
-						mes "of cloth from the puddle";
-						mes "of poison and keep it with you.^000000";
-						close;
-					}
-				case 2:
-					mes "["+strcharinfo(0)+"]";
-					mes "Hmm...";
-					mes "It'll probably be";
-					mes "faster if I follow this";
-					mes "trail, rather than stop to";
-					mes "investigate this scene.";
-					close;
-				}
-			}
-		} else {
-			mes "^3355FFYou can't find";
-			mes "anything } else here,";
-			mes "aside from the traces";
-			mes "that split and lead both";
-			mes "northward and southward.^000000";
-			close;
-		}
-	} else if(BaseClass == Job_Mage){
-		if(tu_magician01 < 12){
-			mes "^3355FFThere are signs";
-			mes "that many people";
-			mes "have traveled through";
-			mes "this particular area.^000000";
-			close;
-		} else if(tu_magician01 == 12){
-			mes "^3355FFThe traces of the battle";
-			mes "now split and head towards";
-			mes "the north and south. However,^000000";
-			if(countitem(506) == 0){
-				mes "^3355FFthere is a puddle of strong poison that you must neutralize before you can investigate this scene.^000000";
+				mes "^3355FFYou take the piece";
+				mes "of cloth from the puddle";
+				mes "of poison and keep it with you.^000000";
 				close;
-			} else {
-				mes "^3355FFthere is a puddle of strong poison that you must neutralize before you can investigate this scene.^000000";
-				next;
-				mes "^3355FFFortunately, you can";
-				mes "temporarily nullify the";
-				mes "poison by using one";
-				mes "of your Green Potions.^000000";
-				next;
-				switch(select("Use Green Potion.:Don't use it.")){
-				case 1:
-					mes "^3355FFThe poison weakens";
-					mes "and some of it evaporates,";
-					mes "revealing a piece of cloth that";
-					mes "was hidden in that puddle.^000000";
-					next;
-					switch(select("Don't investigate.:Investigate.")){
-					case 1:
-						mes "["+strcharinfo(0)+"]";
-						mes "A piece of cloth";
-						mes "is nothing to be";
-						mes "concerned about.";
-						close;
-					case 2:
-						mes "["+strcharinfo(0)+"]";
-						mes "Hey... There's";
-						mes "blood on this cloth";
-						mes "and some writing on";
-						mes "it that I can't recognize.";
-						mes "Hopefully, this'll provide";
-						mes "some sort of clue to all this?";
-						set tu_magician01,13;
-						next;
-						mes "^3355FFYou take the piece";
-						mes "of cloth from the puddle";
-						mes "of poison and keep it with you.^000000";
-						close;
-					}
-				case 2:
-					mes "["+strcharinfo(0)+"]";
-					mes "Hmm...";
-					mes "It'll probably be";
-					mes "faster if I follow this";
-					mes "trail, rather than stop to";
-					mes "investigate this scene.";
-					close;
-				}
 			}
-		} else {
-			mes "^3355FFYou can't find";
-			mes "anything } else here,";
-			mes "aside from the traces";
-			mes "that split and lead both";
-			mes "northward and southward.^000000";
-			close;
-		}
+		case 2:
+			mes "["+strcharinfo(0)+"]";
+			mes "Hmm...";
+			mes "It'll probably be";
+			mes "faster if I follow this";
+			mes "trail, rather than stop to";
+			mes "investigate this scene.";
+			close;
+		}
+	} else {
+		mes "^3355FFYou can't find";
+		mes "anything else here,";
+		mes "aside from the traces";
+		mes "that split and lead both";
+		mes "northward and southward.^000000";
 	}
-	mes "^3355FFThese look like";
-	mes "traces of some kind";
-	mes "of pursuit or battle, but";
-	mes "you can't really tell for sure.^000000";
 	close;
 }
 
 // Battle Traces 6
 //============================================================
-moc_fild11,226,235,0	script	Trace of Battle#6	844,{
-	if(BaseClass == Job_Thief){
-		if(tu_thief01 < 13){
-			mes "^3355FFThere are signs";
-			mes "that many people";
-			mes "have traveled through";
-			mes "this particular area.^000000";
-			close;
-		} else {
-			mes "^3355FFThese footprints";
-			mes "look like they're";
-			mes "heading towards the";
-			mes "north from the south.";
-			mes "But you can't really";
-			mes "be sure just yet.^000000";
-			close;
-		}
-	} else if(BaseClass == Job_Mage){
-		if(tu_magician01 < 13){
-			mes "^3355FFThere are signs";
-			mes "that many people";
-			mes "have traveled through";
-			mes "this particular area.^000000";
-			close;
-		} else {
-			mes "^3355FFThese footprints";
-			mes "look like they're";
-			mes "heading towards the";
-			mes "north from the south.";
-			mes "But you can't really";
-			mes "be sure just yet.^000000";
-			close;
-		}
+moc_fild11,226,235,0	script	Trace of Battle#6	CLEAR_NPC,{
+	.@class_thief = ( BaseClass == Job_Thief );
+	.@class_mage = ( BaseClass == Job_Mage );
+	if (.@class_thief == 0 && .@class_mage == 0 || eaclass()&EAJL_BABY) {
+		mes "^3355FFThese look like";
+		mes "traces of some kind";
+		mes "of pursuit or battle, but";
+		mes "you can't really tell for sure.^000000";
+	} else if (tu_thief01 < 13 && .@class_thief || tu_magician01 < 13 && .@class_mage) {
+		mes "^3355FFThere are signs";
+		mes "that many people";
+		mes "have traveled through";
+		mes "this particular area.^000000";
+	} else {
+		mes "^3355FFThese footprints";
+		mes "look like they're";
+		mes "heading towards the";
+		mes "north from the south.";
+		mes "But you can't really";
+		mes "be sure just yet.^000000";
 	}
-	mes "^3355FFThese look like";
-	mes "traces of some kind";
-	mes "of pursuit or battle, but";
-	mes "you can't really tell for sure.^000000";
 	close;
 }
 
 // Battle Traces 7
 //============================================================
-moc_fild11,184,342,0	script	Trace of Battle#7	844,{
-	if(BaseClass == Job_Thief){
-		if(tu_thief01 < 13){
-			mes "^3355FFThere are signs";
-			mes "that many people";
-			mes "have traveled through";
-			mes "this particular area.^000000";
-			close;
-		} else {
-			mes "^3355FFThe trail here looks pretty";
-			mes "muddled, since it looks like";
-			mes "they battled here for quite a";
-			mes "while. But the footprints are";
-			mes "definitely heading south.^000000";
-			close;
-		}
-	} else if(BaseClass == Job_Mage){
-		if(tu_magician01 < 13){
-			mes "^3355FFThere are signs";
-			mes "that many people";
-			mes "have traveled through";
-			mes "this particular area.^000000";
-			close;
-		} else {
-			mes "^3355FFThe trail here looks pretty";
-			mes "muddled, since it looks like";
-			mes "they battled here for quite a";
-			mes "while. But the footprints are";
-			mes "definitely heading south.^000000";
-			close;
-		}
+moc_fild11,184,342,0	script	Trace of Battle#7	CLEAR_NPC,{
+	.@class_thief = ( BaseClass == Job_Thief );
+	.@class_mage = ( BaseClass == Job_Mage );
+	if (.@class_thief == 0 && .@class_mage == 0 || eaclass()&EAJL_BABY) {
+		mes "^3355FFThese look like";
+		mes "traces of some kind";
+		mes "of pursuit or battle, but";
+		mes "you can't really tell for sure.^000000";
+	} else if (tu_thief01 < 13 && .@class_thief || tu_magician01 < 13 && .@class_mage) {
+		mes "^3355FFThere are signs";
+		mes "that many people";
+		mes "have traveled through";
+		mes "this particular area.^000000";
+	} else {
+		mes "^3355FFThe trail here looks pretty";
+		mes "muddled, since it looks like";
+		mes "they battled here for quite a";
+		mes "while. But the footprints are";
+		mes "definitely heading south.^000000";
 	}
-	mes "^3355FFThese look like";
-	mes "traces of some kind";
-	mes "of pursuit or battle, but";
-	mes "you can't really tell for sure.^000000";
 	close;
 }
 
 // Battle Traces 8
 //============================================================
-moc_fild17,213,358,0	script	Trace of Battle#8	844,{
-	if(BaseClass == Job_Thief){
-		if(tu_thief01 < 13){
-			mes "^3355FFThere are two";
-			mes "distinct sets of";
-			mes "footprints in this";
-			mes "area, but they don't";
-			mes "hold any significance";
-			mes "right about now.^000000";
-			close;
-		} else if(tu_thief01 == 13){
-			mes "^3355FFAround here, it";
-			mes "looks like there are";
-			mes "only two sets of footprints.";
-			mes "What happened to the other";
-			mes "set that you found earlier?^000000";
-			set tu_thief01, 14;
-			close;
-		} else {
-			mes "^3355FFFrom the evidence";
-			mes "that you've found here,";
-			mes "it looks like the battle";
-			mes "involves only two people";
-			mes "from this point onward.^000000";
-			close;
-		}
-	} else if(BaseClass == Job_Mage){
-		if(tu_magician01 < 13){
-			mes "^3355FFThere are two";
-			mes "distinct sets of";
-			mes "footprints in this";
-			mes "area, but they don't";
-			mes "hold any significance";
-			mes "right about now.^000000";
-			close;
-		} else if(tu_magician01 == 13){
-			mes "^3355FFAround here, it";
-			mes "looks like there are";
-			mes "only two sets of footprints.";
-			mes "What happened to the other";
-			mes "set that you found earlier?^000000";
-			set tu_magician01, 14;
-			close;
-		} else {
-			mes "^3355FFFrom the evidence";
-			mes "that you've found here,";
-			mes "it looks like the battle";
-			mes "involves only two people";
-			mes "from this point onward.^000000";
-			close;
-		}
+moc_fild17,213,358,0	script	Trace of Battle#8	CLEAR_NPC,{
+	.@class_thief = ( BaseClass == Job_Thief );
+	.@class_mage = ( BaseClass == Job_Mage );
+	if (.@class_thief == 0 && .@class_mage == 0 || eaclass()&EAJL_BABY) {
+		mes "^3355FFThese look like";
+		mes "traces of some kind";
+		mes "of pursuit or battle, but";
+		mes "you can't really tell for sure.^000000";
+	} else if (tu_thief01 < 13 && .@class_thief || tu_magician01 < 13 && .@class_mage) {
+		mes "^3355FFThere are two";
+		mes "distinct sets of";
+		mes "footprints in this";
+		mes "area, but they don't";
+		mes "hold any significance";
+		mes "right about now.^000000";
+	} else if (tu_thief01 == 13 && .@class_thief || tu_magician01 == 13 && .@class_mage) {
+		mes "^3355FFAround here, it";
+		mes "looks like there are";
+		mes "only two sets of footprints.";
+		mes "What happened to the other";
+		mes "set that you found earlier?^000000";
+		if (tu_magician01 == 13)
+			tu_magician01 = 14;
+		else
+			tu_thief01 = 14;
+	} else {
+		mes "^3355FFFrom the evidence";
+		mes "that you've found here,";
+		mes "it looks like the battle";
+		mes "involves only two people";
+		mes "from this point onward.^000000";
 	}
-	mes "^3355FFThese look like";
-	mes "traces of some kind";
-	mes "of pursuit or battle, but";
-	mes "you can't really tell for sure.^000000";
 	close;
 }
 
 // Battle Traces 9
 //============================================================
-moc_fild17,228,274,0	script	Trace of Battle#9	844,{
-	if(BaseClass == Job_Thief){
-		if(tu_thief01 < 14){
-			mes "^3355FFTraces of some";
-			mes "sort of battle are";
-			mes "scattered all over";
-			mes "this particular area.^000000";
-			close;
-		} else if(tu_thief01 == 14){
-			mes "^3355FFYou follow the trail";
-			mes "and although traces from";
-			mes "the north and southeast mix";
-			mes "together, it looks like the battle continues towards the east.";
-			mes "But you should check this";
-			mes "spot a little bit more...^000000";
-			set tu_thief01, 15;
-			close;
-		} else if(tu_thief01 == 15){
-			set .@seek_tm, rand(1,10);
-			if(.@seek_tm == 7){
-				mes "^3355FFAfter investigating this";
-				mes "area more thoroughly,";
-				mes "you find another piece of";
-				mes "cloth stained with blood.";
-				mes "You decide to keep it with";
-				mes "you, hoping that it will";
-				mes "provide more clues.^000000";
-				set tu_thief01, 16;
-				close;
-			} else {
-				mes "^3355FFYou don't find anything,";
-				mes "but you still can't shake";
-				mes "the feeling that there is";
-				mes "some important clue that";
-				mes "you have to find here. It won't hurt to keep investigating here.^000000";
-				close;
-			}
-		} else {
-			mes "^3355FFYou better continue";
-			mes "following this trail";
-			mes "which leads westward.^000000";
-			close;
-		}
-	} else if(BaseClass == Job_Mage){
-		if(tu_magician01 < 14){
-			mes "^3355FFTraces of some";
-			mes "sort of battle are";
-			mes "scattered all over";
-			mes "this particular area.^000000";
-			close;
-		} else if(tu_magician01 == 14){
-			mes "^3355FFYou follow the trail";
-			mes "and although traces from";
-			mes "the north and southeast mix";
-			mes "together, it looks like the battle continues towards the east.";
-			mes "But you should check this";
-			mes "spot a little bit more...^000000";
-			set tu_magician01, 15;
-			close;
-		} else if(tu_magician01 == 15){
-			set .@seek_tm1, rand(1,10);
-			if(.@seek_tm1 == 7){
-				mes "^3355FFAfter investigating this";
-				mes "area more thoroughly,";
-				mes "you find another piece of";
-				mes "cloth stained with blood.";
-				mes "You decide to keep it with";
-				mes "you, hoping that it will";
-				mes "provide more clues.^000000";
-				set tu_magician01, 16;
-				close;
-			} else {
-				mes "^3355FFYou don't find anything,";
-				mes "but you still can't shake";
-				mes "the feeling that there is";
-				mes "some important clue that";
-				mes "you have to find here. It won't hurt to keep investigating here.^000000";
-				close;
-			}
-		} else {
-			mes "^3355FFYou better continue";
-			mes "following this trail";
-			mes "which leads westward.^000000";
-			close;
-		}
+moc_fild17,228,274,0	script	Trace of Battle#9	CLEAR_NPC,{
+	.@class_thief = ( BaseClass == Job_Thief );
+	.@class_mage = ( BaseClass == Job_Mage );
+	if (.@class_thief == 0 && .@class_mage == 0 || eaclass()&EAJL_BABY) {
+		mes "^3355FFThese look like";
+		mes "traces of some kind";
+		mes "of pursuit or battle, but";
+		mes "you can't really tell for sure.^000000";
+	} else if (tu_thief01 < 14 && .@class_thief || tu_magician01 < 14 && .@class_mage) {
+		mes "^3355FFTraces of some";
+		mes "sort of battle are";
+		mes "scattered all over";
+		mes "this particular area.^000000";
+	} else if (tu_thief01 == 14 && .@class_thief || tu_magician01 == 14 && .@class_mage) {
+		mes "^3355FFYou follow the trail";
+		mes "and although traces from";
+		mes "the north and southeast mix";
+		mes "together, it looks like the battle continues towards the east.";
+		mes "But you should check this";
+		mes "spot a little bit more...^000000";
+		if (tu_magician01 == 14)
+			tu_magician01 = 15;
+		else
+			tu_thief01 = 15;
+	} else if (tu_thief01 == 15 && .@class_thief || tu_magician01 == 15 && .@class_mage) {
+		if (rand(1,10) == 7) {
+			mes "^3355FFAfter investigating this";
+			mes "area more thoroughly,";
+			mes "you find another piece of";
+			mes "cloth stained with blood.";
+			mes "You decide to keep it with";
+			mes "you, hoping that it will";
+			mes "provide more clues.^000000";
+			if (tu_magician01 == 15)
+				tu_magician01 = 16;
+			else
+				tu_thief01 = 16;
+		} else {
+			mes "^3355FFYou don't find anything,";
+			mes "but you still can't shake";
+			mes "the feeling that there is";
+			mes "some important clue that";
+			mes "you have to find here. It won't hurt to keep investigating here.^000000";
+		}
+	} else {
+		mes "^3355FFYou better continue";
+		mes "following this trail";
+		mes "which leads westward.^000000";
 	}
-	mes "^3355FFThese look like";
-	mes "traces of some kind";
-	mes "of pursuit or battle, but";
-	mes "you can't really tell for sure.^000000";
 	close;
 }
 
 // Battle Traces 10
 //============================================================
-moc_fild17,34,292,0	script	Trace of Battle#10	844,{
-	if(BaseClass == Job_Thief){
-		if(tu_thief01 < 16){
-			mes "^3355FFThere are signs";
-			mes "showing that a lot";
-			mes "of people were in";
-			mes "this area earlier.^000000";
-			close;
-		} else if(tu_thief01 == 16){
-			mes "^3355FFThe lead set of footprints,";
-			mes "probably belonging to the one";
-			mes "who was being pursued, look";
-			mes "more erratic, as if exhaustion";
-			mes "and desperation were setting in. These traces lead to the west.^000000";
-			set tu_thief01, 17;
-			close;
-		} else {
-			mes "^3355FFThe trail from";
-			mes "this point heads";
-			mes "towards the west.^000000";
-			close;
-		}
-	} else if(BaseClass == Job_Mage){
-		if(tu_magician01 < 16){
-			mes "^3355FFThere are signs";
-			mes "showing that a lot";
-			mes "of people were in";
-			mes "this area earlier.^000000";
-			close;
-		} else if(tu_magician01 == 16){
-			mes "^3355FFThe lead set of footprints,";
-			mes "probably belonging to the one";
-			mes "who was being pursued, look";
-			mes "more erratic, as if exhaustion";
-			mes "and desperation were setting in. These traces lead to the west.^000000";
-			set tu_magician01, 17;
-			close;
-		} else {
-			mes "^3355FFThe trail from";
-			mes "this point heads";
-			mes "towards the west.^000000";
-			close;
-		}
+moc_fild17,34,292,0	script	Trace of Battle#10	CLEAR_NPC,{
+	.@class_thief = ( BaseClass == Job_Thief );
+	.@class_mage = ( BaseClass == Job_Mage );
+	if (.@class_thief == 0 && .@class_mage == 0 || eaclass()&EAJL_BABY) {
+		mes "^3355FFThese look like";
+		mes "traces of some kind";
+		mes "of pursuit or battle, but";
+		mes "you can't really tell for sure.^000000";
+	} else if (tu_thief01 < 16 && .@class_thief || tu_magician01 < 16 && .@class_mage) {
+		mes "^3355FFThere are signs";
+		mes "showing that a lot";
+		mes "of people were in";
+		mes "this area earlier.^000000";
+	} else if (tu_thief01 == 16 && .@class_thief || tu_magician01 == 16 && .@class_mage) {
+		mes "^3355FFThe lead set of footprints,";
+		mes "probably belonging to the one";
+		mes "who was being pursued, look";
+		mes "more erratic, as if exhaustion";
+		mes "and desperation were setting in. These traces lead to the west.^000000";
+		if (tu_magician01 == 16)
+			tu_magician01 = 17;
+		else
+			tu_thief01 = 17;
+	} else {
+		mes "^3355FFThe trail from";
+		mes "this point heads";
+		mes "towards the west.^000000";
 	}
-	mes "^3355FFThese look like";
-	mes "traces of some kind";
-	mes "of pursuit or battle, but";
-	mes "you can't really tell for sure.^000000";
 	close;
 }
 
 // Battle Traces 11
 //============================================================
-moc_fild18,346,296,0	script	Trace of Battle#11	844,{
-	if(BaseClass == Job_Thief){
-		if(tu_thief01 < 17){
-			mes "^3355FFThere are signs";
-			mes "showing that a lot";
-			mes "of people were in";
-			mes "this area earlier.^000000";
-			close;
-		} else if(tu_thief01 == 17){
-			mes "^3355FFFollowing the";
-			mes "trail, you see signs";
-			mes "that blood was spilled";
-			mes "in this area. It looks like";
-			mes "someone was injured";
-			mes "pretty badly around here.^000000";
-			set tu_thief01, 18;
-			close;
-		} else {
-			mes "^3355FFSince someone involved";
-			mes "in this conflict was bleeding,";
-			mes "further traces of this pursuit";
-			mes "might be easier to find now.^000000";
-			close;
-		}
-	} else if(BaseClass == Job_Mage){
-		if(tu_magician01 < 17){
-			mes "^3355FFThere are signs";
-			mes "showing that a lot";
-			mes "of people were in";
-			mes "this area earlier.^000000";
-			close;
-		} else if(tu_magician01 == 17){
-			mes "^3355FFFollowing the";
-			mes "trail, you see signs";
-			mes "that blood was spilled";
-			mes "in this area. It looks like";
-			mes "someone was injured";
-			mes "pretty badly around here.^000000";
-			set tu_magician01, 18;
-			close;
-		} else {
-			mes "^3355FFSince someone involved";
-			mes "in this conflict was bleeding,";
-			mes "further traces of this pursuit";
-			mes "might be easier to find now.^000000";
-			close;
-		}
+moc_fild18,346,296,0	script	Trace of Battle#11	CLEAR_NPC,{
+	.@class_thief = ( BaseClass == Job_Thief );
+	.@class_mage = ( BaseClass == Job_Mage );
+	if (.@class_thief == 0 && .@class_mage == 0 || eaclass()&EAJL_BABY) {
+		mes "^3355FFThese look like";
+		mes "traces of some kind";
+		mes "of pursuit or battle, but";
+		mes "you can't really tell for sure.^000000";
+	} else if (tu_thief01 < 17 && .@class_thief || tu_magician01 < 17 && .@class_mage) {
+		mes "^3355FFThere are signs";
+		mes "showing that a lot";
+		mes "of people were in";
+		mes "this area earlier.^000000";
+	} else if (tu_thief01 == 17 && .@class_thief || tu_magician01 == 17 && .@class_mage) {
+		mes "^3355FFFollowing the";
+		mes "trail, you see signs";
+		mes "that blood was spilled";
+		mes "in this area. It looks like";
+		mes "someone was injured";
+		mes "pretty badly around here.^000000";
+		if (tu_magician01 == 17)
+			tu_magician01 = 18;
+		else
+			tu_thief01 = 18;
+	} else {
+		mes "^3355FFSince someone involved";
+		mes "in this conflict was bleeding,";
+		mes "further traces of this pursuit";
+		mes "might be easier to find now.^000000";
 	}
-	mes "^3355FFThese look like";
-	mes "traces of some kind";
-	mes "of pursuit or battle, but";
-	mes "you can't really tell for sure.^000000";
 	close;
 }
 
 // Battle Traces 12
 //============================================================
-moc_fild18,309,257,0	script	Trace of Battle#12	844,{
-	if(BaseClass == Job_Thief){
-		if(tu_thief01 < 18){
-			mes "^3355FFThere are signs";
-			mes "showing that a lot";
-			mes "of people were in";
-			mes "this area earlier.^000000";
-			close;
-		} else if(tu_thief01 == 18){
-			mes "^3355FFThese traces lead";
-			mes "northward and it looks";
-			mes "like whoever is doing";
-			mes "the chasing is getting";
-			mes "much closer to his prey.^000000";
-			set tu_thief01, 19;
-			close;
-		} else {
-			mes "^3355FFThe trail of this";
-			mes "pursuit now leads";
-			mes "towards the north.^000000";
-			close;
-		}
-	} else if(BaseClass == Job_Mage){
-		if(tu_magician01 < 18){
-			mes "^3355FFThere are signs";
-			mes "showing that a lot";
-			mes "of people were in";
-			mes "this area earlier.^000000";
-			close;
-		} else if(tu_magician01 == 18){
-			mes "^3355FFThese traces lead";
-			mes "northward and it looks";
-			mes "like whoever is doing";
-			mes "the chasing is getting";
-			mes "much closer to his prey.^000000";
-			set tu_magician01, 19;
-			close;
-		} else {
-			mes "^3355FFThe trail of this";
-			mes "pursuit now leads";
-			mes "towards the north.^000000";
-			close;
-		}
+moc_fild18,309,257,0	script	Trace of Battle#12	CLEAR_NPC,{
+	.@class_thief = ( BaseClass == Job_Thief );
+	.@class_mage = ( BaseClass == Job_Mage );
+	if (.@class_thief == 0 && .@class_mage == 0 || eaclass()&EAJL_BABY) {
+		mes "^3355FFThese look like";
+		mes "traces of some kind";
+		mes "of pursuit or battle, but";
+		mes "you can't really tell for sure.^000000";
+	} else if (tu_thief01 < 18 && .@class_thief || tu_magician01 < 18 && .@class_mage) {
+		mes "^3355FFThere are signs";
+		mes "showing that a lot";
+		mes "of people were in";
+		mes "this area earlier.^000000";
+	} else if (tu_thief01 == 18 && .@class_thief || tu_magician01 == 18 && .@class_mage) {
+		mes "^3355FFThese traces lead";
+		mes "northward and it looks";
+		mes "like whoever is doing";
+		mes "the chasing is getting";
+		mes "much closer to his prey.^000000";
+		if (tu_magician01 == 18)
+			tu_magician01 = 19;
+		else
+			tu_thief01 = 19;
+	} else {
+		mes "^3355FFThe trail of this";
+		mes "pursuit now leads";
+		mes "towards the north.^000000";
 	}
-	mes "^3355FFThese look like";
-	mes "traces of some kind";
-	mes "of pursuit or battle, but";
-	mes "you can't really tell for sure.^000000";
 	close;
 }
 
 // Battle Traces 13
 //============================================================
-moc_fild18,177,333,0	script	Trace of Battle#13	844,{
-	if(BaseClass == Job_Thief){
-		if(tu_thief01 < 19){
-			mes "^3355FFThere are signs";
-			mes "showing that a lot";
-			mes "of people were in";
-			mes "this area earlier.^000000";
-			close;
-		} else if(tu_thief01 == 19){
-			mes "^3355FFJudging from these";
-			mes "traces, it looks like";
-			mes "even more people have";
-			mes "joined the battle which";
-			mes "now seems to be leading";
-			mes "in the southwest direction.^000000";
-			set tu_thief01, 20;
-			close;
-		} else {
-			mes "^3355FFIt looks like";
-			mes "the battle heads";
-			mes "towards the southwest";
-			mes "from this particular point.^000000";
-			close;
-		}
-	} else if(BaseClass == Job_Mage){
-		if(tu_magician01 < 19){
-			mes "A large group of people seem to have gone by.";
-			close;
-		} else if(tu_magician01 == 19){
-			mes "^3355FFJudging from these";
-			mes "traces, it looks like";
-			mes "even more people have";
-			mes "joined the battle which";
-			mes "now seems to be leading";
-			mes "in the southwest direction.^000000";
-			set tu_magician01, 20;
-			close;
-		} else {
-			mes "^3355FFIt looks like";
-			mes "the battle heads";
-			mes "towards the southwest";
-			mes "from this particular point.^000000";
-			close;
-		}
+moc_fild18,177,333,0	script	Trace of Battle#13	CLEAR_NPC,{
+	.@class_thief = ( BaseClass == Job_Thief );
+	.@class_mage = ( BaseClass == Job_Mage );
+	if (.@class_thief == 0 && .@class_mage == 0 || eaclass()&EAJL_BABY) {
+		mes "^3355FFThese look like";
+		mes "traces of some kind";
+		mes "of pursuit or battle, but";
+		mes "you can't really tell for sure.^000000";
+	} else if (tu_thief01 < 19 && .@class_thief) {
+		mes "^3355FFThere are signs";
+		mes "showing that a lot";
+		mes "of people were in";
+		mes "this area earlier.^000000";
+	} else if (tu_magician01 < 19 && .@class_mage) {
+		mes "A large group of people seem to have gone by.";
+	} else if (tu_thief01 == 19 && .@class_thief || tu_magician01 == 19 && .@class_mage) {
+		mes "^3355FFJudging from these";
+		mes "traces, it looks like";
+		mes "even more people have";
+		mes "joined the battle which";
+		mes "now seems to be leading";
+		mes "in the southwest direction.^000000";
+		if (tu_magician01 == 19)
+			tu_magician01 = 20;
+		else
+			tu_thief01 = 20;
+	} else {
+		mes "^3355FFIt looks like";
+		mes "the battle heads";
+		mes "towards the southwest";
+		mes "from this particular point.^000000";
 	}
-	mes "^3355FFThese look like";
-	mes "traces of some kind";
-	mes "of pursuit or battle, but";
-	mes "you can't really tell for sure.^000000";
 	close;
 }
 
 // Battle Traces 14
 //============================================================
-moc_fild18,111,303,0	script	Trace of Battle#14	844,{
-	if(BaseClass == Job_Thief){
-		if(tu_thief01 < 20){
-			mes "^3355FFThere are signs";
-			mes "showing that a lot";
-			mes "of people were in";
-			mes "this area earlier.^000000";
-			close;
-		} else if(tu_thief01 == 20){
-			mes "^3355FFIn this area, it looks";
-			mes "like even more people";
-			mes "joined in this battle and";
-			mes "the pursuit clearly heads";
-			mes "towards the south.^000000";
-			set tu_thief01, 21;
-			close;
-		} else {
-			mes "^3355FFThe trail of";
-			mes "this battle heads";
-			mes "towards the south.^000000";
-			close;
-		}
-	} else if(BaseClass == Job_Mage){
-		if(tu_magician01 < 20){
-			mes "^3355FFThere are signs";
-			mes "showing that a lot";
-			mes "of people were in";
-			mes "this area earlier.^000000";
-			close;
-		} else if(tu_magician01 == 20){
-			mes "^3355FFIn this area, it looks";
-			mes "like even more people";
-			mes "joined in this battle and";
-			mes "the pursuit clearly heads";
-			mes "towards the south.^000000";
-			set tu_magician01, 21;
-			close;
-		} else {
-			mes "^3355FFThe trail of";
-			mes "this battle heads";
-			mes "towards the south.^000000";
-			close;
-		}
+moc_fild18,111,303,0	script	Trace of Battle#14	CLEAR_NPC,{
+	.@class_thief = ( BaseClass == Job_Thief );
+	.@class_mage = ( BaseClass == Job_Mage );
+	if (.@class_thief == 0 && .@class_mage == 0 || eaclass()&EAJL_BABY) {
+		mes "^3355FFThese look like";
+		mes "traces of some kind";
+		mes "of pursuit or battle, but";
+		mes "you can't really tell for sure.^000000";
+	} else if (tu_thief01 < 20 && .@class_thief || tu_magician01 < 20 && .@class_mage) {
+		mes "^3355FFThere are signs";
+		mes "showing that a lot";
+		mes "of people were in";
+		mes "this area earlier.^000000";
+	} else if (tu_thief01 == 20 && .@class_thief || tu_magician01 == 20 && .@class_mage) {
+		mes "^3355FFIn this area, it looks";
+		mes "like even more people";
+		mes "joined in this battle and";
+		mes "the pursuit clearly heads";
+		mes "towards the south.^000000";
+		if (tu_magician01 == 20)
+			tu_magician01 = 21;
+		else
+			tu_thief01 = 21;
+	} else {
+		mes "^3355FFThe trail of";
+		mes "this battle heads";
+		mes "towards the south.^000000";
 	}
-	mes "^3355FFThese look like";
-	mes "traces of some kind";
-	mes "of pursuit or battle, but";
-	mes "you can't really tell for sure.^000000";
 	close;
 }
 
 // Battle Traces 15
 //============================================================
-moc_fild18,109,197,0	script	Trace of Battle#15	844,{
-	if(BaseClass == Job_Thief){
-		if(tu_thief01 < 21){
-			mes "^3355FFThere are signs";
-			mes "showing that a lot";
-			mes "of people were in";
-			mes "this area earlier.^000000";
-			close;
-		} else if(tu_thief01 == 21){
-			mes "^3355FFThis area is clearly";
-			mes "marked with signs";
-			mes "of a violent battle, with";
-			mes "traces of poison strewn";
-			mes "all over the ground.^000000";
+moc_fild18,109,197,0	script	Trace of Battle#15	CLEAR_NPC,{
+	.@class_thief = ( BaseClass == Job_Thief );
+	.@class_mage = ( BaseClass == Job_Mage );
+	if (.@class_thief == 0 && .@class_mage == 0 || eaclass()&EAJL_BABY) {
+		mes "^3355FFThese look like";
+		mes "traces of some kind";
+		mes "of pursuit or battle, but";
+		mes "you can't really tell for sure.^000000";
+	} else if (tu_thief01 < 21 && .@class_thief) {
+		mes "^3355FFThere are signs";
+		mes "showing that a lot";
+		mes "of people were in";
+		mes "this area earlier.^000000";
+	} else if (tu_magician01 < 21 && .@class_mage)
+		mes "A large group of people seem to have gone by.";
+	else if (tu_thief01 == 21 && .@class_thief || tu_magician01 == 21 && .@class_mage) {
+		mes "^3355FFThis area is clearly";
+		mes "marked with signs";
+		mes "of a violent battle, with";
+		mes "traces of poison strewn";
+		mes "all over the ground.^000000";
+		next;
+		switch(select("Continue following the traces.:Further investigate the area.")) {
+		case 1:
+			mes "^3355FFYou examine the";
+			mes "trail, but can't really";
+			mes "discern the direction";
+			mes "in which the battle";
+			mes "continues...^000000";
+			close;
+		case 2:
+			mes "^3355FFYou find a bunch of";
+			mes "traps that use different";
+			mes "kinds of poison. The ones";
+			mes "that have been set off are";
+			mes "mixed with the ones which";
+			mes "haven't been triggered.^000000";
+			if (tu_thief01 == 21)
+				tu_thief01 = 22;
+			else
+				tu_magician01 = 22;
+			close;
+		}
+	} else if (tu_thief01 == 22 && .@class_thief || tu_magician01 == 22 && .@class_mage) {
+		if (rand(1,3) == 3) {
+			mes "^3355FFOne trap in particular";
+			mes "stands out to you more";
+			mes "than the rest. Perhaps";
+			mes "you should pour some";
+			mes "Green Potion on it to";
+			mes "neutralize it first.^000000";
 			next;
-			switch(select("Continue following the traces.:Further investigate the area.")){
+			switch(select("Pour Green Potion.:Don't use Green Potion.")) {
 			case 1:
-				mes "^3355FFYou examine the";
-				mes "trail, but can't really";
-				mes "discern the direction";
-				mes "in which the battle";
-				mes "continues...^000000";
-				close;
-			case 2:
-				mes "^3355FFYou find a bunch of";
-				mes "traps that use different";
-				mes "kinds of poison. The ones";
-				mes "that have been set off are";
-				mes "mixed with the ones which";
-				mes "haven't been triggered.^000000";
-				set tu_thief01, 22;
-				close;
-			}
-		} else if(tu_thief01 == 22){
-			set .@trap_tm, rand(1,3);
-			if(.@trap_tm == 3){
-				mes "^3355FFOne trap in particular";
-				mes "stands out to you more";
-				mes "than the rest. Perhaps";
-				mes "you should pour some";
-				mes "Green Potion on it to";
-				mes "neutralize it first.^000000";
-				next;
-				switch(select("Pour Green Potion.:Don't use Green Potion.")){
-				case 1:
-					if(countitem(506) < 1){
-						mes "^3355FFUnfortunately, you";
-						mes "don't have a Green";
-						mes "Potion that you can";
-						mes "use to pour on this trap...^000000";
-						close;
-					} else {
-						mes "^3355FFPouring that";
-						mes "Green Potion didn't";
-						mes "really make anything";
-						mes "happen. Perhaps you";
-						mes "should try something else.^000000";
-						delitem 506,1; //Green_Potion
-						set tu_thief01, 23;
-						close;
-					}
-				case 2:
-					mes "["+strcharinfo(0)+"]";
-					mes "I guess...";
-					mes "I'll try investigating";
-					mes "this area a little more?";
-					close;
-				}
-			} else {
-				mes "^3355FFBy sheer accident,";
-				mes "you set off one of";
-				mes "the traps in the area.^000000";
-				sc_start SC_Poison,60000,0;
-				specialeffect EF_VENOMDUST2;
-				percentheal -30,0;
-				close;
-			}
-		} else if(tu_thief01 == 23){
-			if(countitem(511) > 0 || countitem(716) > 0){
-				if(countitem(511) > 0 && countitem(716) > 0){
-					mes "^3355FFYou try grinding";
-					mes "a Green Herb and";
-					mes "sprinkling it on the";
-					mes "trap and then place";
-					mes "a Red Gemstone on it.";
-					mes "The gem glows and";
-					mes "slowly melts away...^000000";
-					next;
-					mes "^3355FFAlthough you don't";
-					mes "fully understand the";
-					mes "science of using poisons";
-					mes "or antidotes, you managed";
-					mes "to successfully dismantle";
-					mes "the trap. Now you can safely";
-					mes "check what might be inside.^000000";
-					delitem 511,1; //Green_Herb
-					delitem 716,1; //Red_Gemstone
-					set tu_thief01, 24;
-					close;
-				} else if(countitem(511) > 0){
-					mes "^3355FFYou should try to";
-					mes "dismantle this trap";
-					mes "by using other catalysts";
-					mes "related to the curing or";
-					mes "use of poison. You do have";
-					mes "a Green Herb on you, so you";
-					mes "try sprinkling it on the trap.^000000";
-					next;
-					mes "^3355FFHowever, nothing";
-					mes "happens. It seems that";
-					mes "you need another catalyst";
-					mes "in addition to the Green Herb";
-					mes "that you have in order to";
-					mes "dismantle this trap.^000000";
+				if (countitem(506) < 1) {// Green_Potion
+					mes "^3355FFUnfortunately, you";
+					mes "don't have a Green";
+					mes "Potion that you can";
+					mes "use to pour on this trap...^000000";
 					close;
 				} else {
-					mes "^3355FFYou should try to";
-					mes "dismantle this trap";
-					mes "by using other catalysts";
-					mes "related to the curing or use";
-					mes "of poison. You do have a";
-					mes "Red Gemstone, so you grind";
-					mes "it and sprinkle it on the trap.^000000";
-					next;
-					mes "^3355FFHowever, nothing";
-					mes "happens. It seems that";
-					mes "you need another catalyst to";
-					mes "use with the Red Gemstone";
-					mes "that you have in order to";
-					mes "dismantle this trap.^000000";
+					mes "^3355FFPouring that";
+					mes "Green Potion didn't";
+					mes "really make anything";
+					mes "happen. Perhaps you";
+					mes "should try something else.^000000";
+					delitem 506,1; //Green_Potion
+					if (tu_thief01 == 22)
+						tu_thief01 = 23;
+					else
+						tu_magician01 = 23;
 					close;
 				}
-			} else {
-				mes "^3355FFYou should try to";
-				mes "dismantle this trap";
-				mes "by using other catalysts";
-				mes "related to the curing or";
-				mes "use of poison. But what";
-				mes "items should you bring?^000000";
-				close;
-			}
-		} else if(tu_thief01 == 24){
-			mes "^3355FFInside the dismantled";
-			mes "trap, you find another";
-			mes "piece of strange cloth";
-			mes "that's stained with blood.";
-			mes "You take it with you in";
-			mes "hopes that it provides";
-			mes "some kind of evidence.^000000";
-			set tu_thief01, 25;
-			close;
-		} else {
-			mes "^3355FFYou examine the area";
-			mes "a little further and guess";
-			mes "that the battle might head";
-			mes "towards the south.^000000";
-			close;
-		}
-	} else if(BaseClass == Job_Mage){
-		if(tu_magician01 < 21){
-			mes "A large group of people seem to have gone by.";
-			close;
-		} else if(tu_magician01 == 21){
-			mes "^3355FFThis area is clearly";
-			mes "marked with signs";
-			mes "of a violent battle, with";
-			mes "traces of poison strewn";
-			mes "all over the ground.^000000";
-			next;
-			switch(select("Continue following the traces.:Further investigate the area.")){
-			case 1:
-				mes "^3355FFYou examine the";
-				mes "trail, but can't really";
-				mes "discern the direction";
-				mes "in which the battle";
-				mes "continues...^000000";
-				close;
 			case 2:
-				mes "^3355FFYou find a bunch of";
-				mes "traps that use different";
-				mes "kinds of poison. The ones";
-				mes "that have been set off are";
-				mes "mixed with the ones which";
-				mes "haven't been triggered.^000000";
-				set tu_magician01, 22;
-				close;
-			}
-		} else if(tu_magician01 == 22){
-			set .@trap_tm, rand(1,3);
-			if(.@trap_tm == 3){
-				mes "^3355FFOne trap in particular";
-				mes "stands out to you more";
-				mes "than the rest. Perhaps";
-				mes "you should pour some";
-				mes "Green Potion on it to";
-				mes "neutralize it first.^000000";
-				next;
-				switch(select("Pour Green Potion.:Don't use Green Potion.")){
-				case 1:
-					if(countitem(506) < 1){
-						mes "^3355FFUnfortunately, you";
-						mes "don't have a Green";
-						mes "Potion that you can";
-						mes "use to pour on this trap...^000000";
-						close;
-					} else {
-						mes "^3355FFPouring that";
-						mes "Green Potion didn't";
-						mes "really make anything";
-						mes "happen. Perhaps you";
-						mes "should try something else.^000000";
-						delitem 506,1; //Green_Potion
-						set tu_magician01, 23;
-						close;
-					}
-				case 2:
-					mes "["+strcharinfo(0)+"]";
-					mes "I guess...";
-					mes "I'll try investigating";
-					mes "this area a little more?";
-					close;
-				}
-			} else {
-				mes "^3355FFBy sheer accident,";
-				mes "you set off one of";
-				mes "the traps in the area.^000000";
-				sc_start SC_Poison,60000,0;
-				specialeffect EF_VENOMDUST2;
-				percentheal -30,0;
-				close;
-			}
-		} else if(tu_magician01 == 23){
-			if(countitem(511) > 0 || countitem(716) > 0){
-				if(countitem(511) > 0 && countitem(716) > 0){
-					mes "^3355FFYou try grinding";
-					mes "a Green Herb and";
-					mes "sprinkling it on the";
-					mes "trap and then place";
-					mes "a Red Gemstone on it.";
-					mes "The gem glows and";
-					mes "slowly melts away...^000000";
-					next;
-					mes "^3355FFAlthough you don't";
-					mes "fully understand the";
-					mes "science of using poisons";
-					mes "or antidotes, you managed";
-					mes "to successfully dismantle";
-					mes "the trap. Now you can safely";
-					mes "check what might be inside.^000000";
-					delitem 511,1; //Green_Herb
-					delitem 716,1; //Red_Gemstone
-					set tu_magician01, 24;
-					close;
-				} else if(countitem(511) > 0){
-					mes "^3355FFYou should try to";
-					mes "dismantle this trap";
-					mes "by using other catalysts";
-					mes "related to the curing or";
-					mes "use of poison. You do have";
-					mes "a Green Herb on you, so you";
-					mes "try sprinkling it on the trap.^000000";
-					next;
-					mes "^3355FFHowever, nothing";
-					mes "happens. It seems that";
-					mes "you need another catalyst";
-					mes "in addition to the Green Herb";
-					mes "that you have in order to";
-					mes "dismantle this trap.^000000";
-					close;
-				} else {
-					mes "^3355FFYou should try to";
-					mes "dismantle this trap";
-					mes "by using other catalysts";
-					mes "related to the curing or use";
-					mes "of poison. You do have a";
-					mes "Red Gemstone, so you grind";
-					mes "it and sprinkle it on the trap.^000000";
-					next;
-					mes "^3355FFHowever, nothing";
-					mes "happens. It seems that";
-					mes "you need another catalyst to";
-					mes "use with the Red Gemstone";
-					mes "that you have in order to";
-					mes "dismantle this trap.^000000";
-					close;
-				}
-			} else {
-				mes "^3355FFYou should try to";
-				mes "dismantle this trap";
-				mes "by using other catalysts";
-				mes "related to the curing or";
-				mes "use of poison. But what";
-				mes "items should you bring?^000000";
+				mes "["+strcharinfo(0)+"]";
+				mes "I guess...";
+				mes "I'll try investigating";
+				mes "this area a little more?";
 				close;
 			}
-		} else if(tu_magician01 == 24){
-			mes "^3355FFInside the dismantled";
-			mes "trap, you find another";
-			mes "piece of strange cloth";
-			mes "that's stained with blood.";
-			mes "You take it with you in";
-			mes "hopes that it provides";
-			mes "some kind of evidence.^000000";
-			set tu_magician01, 25;
-			close;
 		} else {
-			mes "^3355FFYou examine the area";
-			mes "a little further and guess";
-			mes "that the battle might head";
-			mes "towards the south.^000000";
-			close;
-		}
-
+			mes "^3355FFBy sheer accident,";
+			mes "you set off one of";
+			mes "the traps in the area.^000000";
+			sc_start SC_Poison,60000,0;
+			specialeffect EF_VENOMDUST2;
+			percentheal -30,0;
+		}
+	} else if (tu_thief01 == 23 && .@class_thief || tu_magician01 == 23 && .@class_mage) {
+		if (countitem(511) > 0 && countitem(716) > 0) {
+			mes "^3355FFYou try grinding";
+			mes "a Green Herb and";
+			mes "sprinkling it on the";
+			mes "trap and then place";
+			mes "a Red Gemstone on it.";
+			mes "The gem glows and";
+			mes "slowly melts away...^000000";
+			next;
+			mes "^3355FFAlthough you don't";
+			mes "fully understand the";
+			mes "science of using poisons";
+			mes "or antidotes, you managed";
+			mes "to successfully dismantle";
+			mes "the trap. Now you can safely";
+			mes "check what might be inside.^000000";
+			delitem 511,1; //Green_Herb
+			delitem 716,1; //Red_Gemstone
+			if (tu_thief01 == 23)
+				tu_thief01 = 24;
+			else
+				tu_magician01 = 24;
+		} else if (countitem(511) > 0) {
+			mes "^3355FFYou should try to";
+			mes "dismantle this trap";
+			mes "by using other catalysts";
+			mes "related to the curing or";
+			mes "use of poison. You do have";
+			mes "a Green Herb on you, so you";
+			mes "try sprinkling it on the trap.^000000";
+			next;
+			mes "^3355FFHowever, nothing";
+			mes "happens. It seems that";
+			mes "you need another catalyst";
+			mes "in addition to the Green Herb";
+			mes "that you have in order to";
+			mes "dismantle this trap.^000000";
+		} else if (countitem(716) > 0) {
+			mes "^3355FFYou should try to";
+			mes "dismantle this trap";
+			mes "by using other catalysts";
+			mes "related to the curing or use";
+			mes "of poison. You do have a";
+			mes "Red Gemstone, so you grind";
+			mes "it and sprinkle it on the trap.^000000";
+			next;
+			mes "^3355FFHowever, nothing";
+			mes "happens. It seems that";
+			mes "you need another catalyst to";
+			mes "use with the Red Gemstone";
+			mes "that you have in order to";
+			mes "dismantle this trap.^000000";
+		} else {
+			mes "^3355FFYou should try to";
+			mes "dismantle this trap";
+			mes "by using other catalysts";
+			mes "related to the curing or";
+			mes "use of poison. But what";
+			mes "items should you bring?^000000";
+		}
+	} else if (tu_thief01 == 24 && .@class_thief || tu_magician01 == 24 && .@class_mage) {
+		mes "^3355FFInside the dismantled";
+		mes "trap, you find another";
+		mes "piece of strange cloth";
+		mes "that's stained with blood.";
+		mes "You take it with you in";
+		mes "hopes that it provides";
+		mes "some kind of evidence.^000000";
+		if (tu_thief01 == 24)
+			tu_thief01 = 25;
+		else
+			tu_magician01 = 25;
+	} else {
+		mes "^3355FFYou examine the area";
+		mes "a little further and guess";
+		mes "that the battle might head";
+		mes "towards the south.^000000";
 	}
-	mes "^3355FFThese look like";
-	mes "traces of some kind";
-	mes "of pursuit or battle, but";
-	mes "you can't really tell for sure.^000000";
 	close;
 }
 
 // Battle Traces 16
 //============================================================
-moc_fild18,156,96,0	script	Trace of Battle#16	844,{
-	if(BaseClass == Job_Thief){
-		if(tu_thief01 < 25){
-			mes "^3355FFThere are signs";
-			mes "that a large group";
-			mes "of people have been in";
-			mes "this area for some reason.^000000";
-			close;
-		} else if(tu_thief01 == 25){
-			mes "^3355FFThese traces lead";
-			mes "to the edge of the cliff.";
-			mes "marking the end of the trail.";
-			mes "Apparently, the one who was";
-			mes "being chased met his fate here.^000000";
-			next;
-			mes "^3355FFThere are many footprints";
-			mes "leading to the edge of the";
-			mes "cliff and all of them leave";
-			mes "this scene, save for the set";
-			mes "of footprints that distinctly";
-			mes "belong to the person who";
-			mes "was pursued all this time...^3355FF";
-			next;
-			mes "^3355FFIt looks like you've";
-			mes "learned all that you can";
-			mes "from this investigation. You";
-			mes "better return and report your";
-			mes "findings to Yierhan soon.^000000";
-			set tu_thief01, 26;
-			close;
-		} else {
-			mes "^3355FFThis is the end";
-			mes "of the trail. There";
-			mes "aren't any more traces";
-			mes "of the battle left to find.^000000";
-			close;
-		}
-	} else if(BaseClass == Job_Mage){
-		if(tu_magician01 < 25){
-			mes "^3355FFThere are signs";
-			mes "that a large group";
-			mes "of people have been in";
-			mes "this area for some reason.^000000";
-			close;
-		} else if(tu_magician01 == 25){
-			mes "^3355FFThese traces lead";
-			mes "to the edge of the cliff.";
-			mes "marking the end of the trail.";
-			mes "Apparently, the one who was";
-			mes "being chased met his fate here.^000000";
-			next;
-			mes "^3355FFThere are many footprints";
-			mes "leading to the edge of the";
-			mes "cliff and all of them leave";
-			mes "this scene, save for the set";
-			mes "of footprints that distinctly";
-			mes "belong to the person who";
-			mes "was pursued all this time...^3355FF";
-			next;
-			mes "^3355FFIt looks like you've";
-			mes "learned all that you can";
-			mes "from this investigation. You";
-			mes "better return and report your";
-			mes "findings to Yierhan soon.^000000";
-			set tu_magician01, 26;
-			close;
-		} else {
-			mes "^3355FFThis is the end";
-			mes "of the trail. There";
-			mes "aren't any more traces";
-			mes "of the battle left to find.^000000";
-			close;
-		}
+moc_fild18,156,96,0	script	Trace of Battle#16	CLEAR_NPC,{
+	.@class_thief = ( BaseClass == Job_Thief );
+	.@class_mage = ( BaseClass == Job_Mage );
+	if (.@class_thief == 0 && .@class_mage == 0 || eaclass()&EAJL_BABY) {
+		mes "^3355FFThese look like";
+		mes "traces of some kind";
+		mes "of pursuit or battle, but";
+		mes "you can't really tell for sure.^000000";
+	} else if (tu_thief01 < 25 && .@class_thief || tu_magician01 < 25 && .@class_mage) {
+		mes "^3355FFThere are signs";
+		mes "that a large group";
+		mes "of people have been in";
+		mes "this area for some reason.^000000";
+	} else if (tu_thief01 == 25 && .@class_thief || tu_magician01 == 25 && .@class_mage) {
+		mes "^3355FFThese traces lead";
+		mes "to the edge of the cliff.";
+		mes "marking the end of the trail.";
+		mes "Apparently, the one who was";
+		mes "being chased met his fate here.^000000";
+		next;
+		mes "^3355FFThere are many footprints";
+		mes "leading to the edge of the";
+		mes "cliff and all of them leave";
+		mes "this scene, save for the set";
+		mes "of footprints that distinctly";
+		mes "belong to the person who";
+		mes "was pursued all this time...^3355FF";
+		next;
+		mes "^3355FFIt looks like you've";
+		mes "learned all that you can";
+		mes "from this investigation. You";
+		mes "better return and report your";
+		mes "findings to Yierhan soon.^000000";
+		if (tu_magician01 == 25)
+			tu_magician01 = 26;
+		else
+			tu_thief01 = 26;
+	} else {
+		mes "^3355FFThis is the end";
+		mes "of the trail. There";
+		mes "aren't any more traces";
+		mes "of the battle left to find.^000000";
 	}
-	mes "^3355FFThese look like";
-	mes "traces of some kind";
-	mes "of pursuit or battle, but";
-	mes "you can't really tell for sure.^000000";
 	close;
-}
+}

+ 79 - 112
npc/quests/first_class/tu_magician01.txt

@@ -1,35 +1,35 @@
-//===== rAthena Script ======================================= 
+//===== rAthena Script =======================================
 //= Magician Class Tutorial and Job Specific Quest
-//===== By: ================================================== 
+//===== By: ==================================================
 //= Fix up by Jukka
-//===== Current Version: ===================================== 
-//= 1.4
-//===== Compatible With: ===================================== 
+//===== Current Version: =====================================
+//= 2.0
+//===== Compatible With: =====================================
 //= rAthena Project
-//===== Description: ========================================= 
+//===== Description: =========================================
 //= [Official Conversion]
 //= Magician training quest.
-//===== Additional Comments: ================================= 
+//===== Additional Comments: =================================
 //= 1.0 Fully working
 //= 1.1 Optimized [Lupus]
 //= 1.2 Fixed experience gains to match upcoming rate adjustments. [SinSloth]
 //= 1.3 Misc. updates. [L0ne_W0lf]
 //= 1.4 Updated to match the official script. [Euphy]
-//============================================================ 
+//= 2.0 Replaced occurrence of PcName and clean-up. [Capuche]
+//============================================================
 
 // Sign
 //============================================================
-geffen,61,174,4	script	Sign#M	111,{
+geffen,61,174,4	script	Sign#M	HIDDEN_NPC,{
 	mes "- Mage Job Change -";
 	close;
 }
 
 // New Mage Manager
 //============================================================
-geffen,67,180,4	script	New Mage Manager#M	102,{
+geffen,67,180,4	script	New Mage Manager#M	8_F,{
 	mes "[Mana]";
 	if(Class == Job_Novice){
-		mes "[Mana]";
 		mes "You're not ready for";
 		mes "any of the training I have";
 		mes "to offer. You'll have to train";
@@ -37,7 +37,7 @@ geffen,67,180,4	script	New Mage Manager#M	102,{
 		mes "a Mage first. Alright, dear?";
 		close;
 	}
-	if(BaseClass != Job_Mage){
+	if(BaseClass != Job_Mage || eaclass()&EAJL_BABY){
 		mes "You know, sometimes";
 		mes "other jobs might look a";
 		mes "little better in some battle";
@@ -46,8 +46,7 @@ geffen,67,180,4	script	New Mage Manager#M	102,{
 		mes "anyone can do what you do~";
 		close;
 	}
-	if(eaclass()&(EAJL_UPPER|EAJL_2|EAJL_THIRD) && tu_magician01 < 7){
-		mes "[Mana]";
+	if(Class != Job_Mage && tu_magician01 < 7){
 		mes "Although I offer magic";
 		mes "training, it's not very helpful";
 		mes "if you're already experienced";
@@ -133,7 +132,7 @@ geffen,67,180,4	script	New Mage Manager#M	102,{
 		mes "sitting! I'm bushed, so it's";
 		mes "time for a break. But we'll";
 		mes "continue this later, okay?";
-		set tu_magician01, 1;
+		tu_magician01 = 1;
 		if (checkre(3))
 			getexp BaseLevel*3,BaseLevel*2;
 		else
@@ -165,8 +164,8 @@ geffen,67,180,4	script	New Mage Manager#M	102,{
 		mes "In other words, even the same properties";
 		mes "have different features that must be considered.";
 		mes "I will explain the properties now.";
-		next;
 		while((1)){
+			next;
 			switch(select("^ff0000Fire^000000", "^5C4033Earth^000000", "^93DB70Wind^000000", "^0000FFWater^000000", "^CFB53BGhost^000000", "Quit.")){
 			case 1:
 				mes "[Mana]";
@@ -211,8 +210,7 @@ geffen,67,180,4	script	New Mage Manager#M	102,{
 				mes "only slightly injured and the";
 				mes "Fire Wall won't necessarily";
 				mes "block its approach. So beware!";
-				set .@eread_1,1;
-				next;
+				.@eread_1 = 1;
 				break;
 			case 2:
 				mes "[Mana]";
@@ -246,8 +244,7 @@ geffen,67,180,4	script	New Mage Manager#M	102,{
 				mes "afterwards, like Fire Bolt and";
 				mes "Fire Ball, to take advantage of";
 				mes "the monster's Earth property.";
-				set .@eread_2,1;
-				next;
+				.@eread_2 = 1;
 				break;
 			case 3:
 				mes "[Mana]";
@@ -284,8 +281,7 @@ geffen,67,180,4	script	New Mage Manager#M	102,{
 				mes "the ^93DB70Wind^000000 property skills";
 				mes "that we Mages can use are";
 				mes "Lighning Bolt and Thunder Storm.";
-				set .@eread_3,1;
-				next;
+				.@eread_3 = 1;
 				break;
 			case 4:
 				mes "[Mana]";
@@ -321,8 +317,7 @@ geffen,67,180,4	script	New Mage Manager#M	102,{
 				mes "actually freeze monsters.";
 				mes "While monsters are frozen by";
 				mes "Frost Diver, they're considered Water monsters. Interesting, huh?";
-				set .@eread_4,1;
-				next;
+				.@eread_4 = 1;
 				break;
 			case 5:
 				mes "[Mana]";
@@ -354,8 +349,7 @@ geffen,67,180,4	script	New Mage Manager#M	102,{
 				mes "shield against long range";
 				mes "attacks or strong property";
 				mes "attacks, so be really careful!";
-				set .@eread_5,1;
-				next;
+				.@eread_5 = 1;
 				break;
 			case 6:
 				mes "[Mana]";
@@ -371,18 +365,15 @@ geffen,67,180,4	script	New Mage Manager#M	102,{
 					mes "Alrighty, we'll";
 					mes "move on to the next";
 					mes "subject then. Ho ho ho~!";
-					set tu_magician01,2;
+					tu_magician01 = 2;
 					specialeffect2 EF_HIT5;
-					if(.@eread_1) getexp 150,70;
-					if(.@eread_2) getexp 150,70;
-					if(.@eread_3) getexp 150,70;
-					if(.@eread_4) getexp 150,70;
-					if(.@eread_5) getexp 150,70;
+					.@total = .@eread_1 + .@eread_2 + .@eread_3 + .@eread_4 + .@eread_5;
+					if (.@total) getexp (.@total*150),(.@total*70);
+					close;
 				}
-				close;
+				break;
 			}
 		}
-		break;
 	case 2:
 		mes "I know that the concept";
 		mes "of properties might be";
@@ -458,75 +449,66 @@ geffen,67,180,4	script	New Mage Manager#M	102,{
 		mes "until you've collected";
 		mes "10 Chrysalis from them.";
 		mes "Then bring them to me, okay?";
-		set tu_magician01, 3;
+		tu_magician01 = 3;
 		getexp 400,200;
 		specialeffect2 EF_HIT5;
 		close;
 	case 3:
-		mes "Ah, you're back?";
-		mes "Let's see if you gathered";
-		mes "all 10 Chrysalis from hunting";
-		mes "all of those Pupa. Hmmmm...";
-		next;
-		if (countitem(915) < 10) {
-			mes "[Mana]";
-			mes "Aw, you don't";
-			mes "have enough of";
-			mes "them yet. Head west";
-			mes "from here and hunt Pupa";
-			mes "to get more Chrysalis, okay?";
-			mes "For now, I'll just heal you up.";
-			set tu_magician01, 4;
-			percentheal 100,100;
-			close;
-		} else {
-			mes "[Mana]";
-			mes "...Nine, ten.";
-			mes "Alright, you pass!";
-			mes "By now, you must be more";
-			mes "used to using your skills, huh?";
-			mes "Anyway, let me know when you're ready for the next lesson, okay?";
-			set tu_magician01, 5;
-			getexp 400,200;
-			specialeffect2 EF_HIT5;
-			close;
-		}
 	case 4:
-		mes "So...";
-		mes "Did you get";
-		mes "10 Chrysalis";
-		mes "from hunting Pupa";
-		mes "like I asked? I hope so...";
-		mes "Because I'm going to check~";
+		if (tu_magician01 == 3) {
+			mes "Ah, you're back?";
+			mes "Let's see if you gathered";
+			mes "all 10 Chrysalis from hunting";
+			mes "all of those Pupa. Hmmmm...";
+		}
+		else {
+			mes "So...";
+			mes "Did you get";
+			mes "10 Chrysalis";
+			mes "from hunting Pupa";
+			mes "like I asked? I hope so...";
+			mes "Because I'm going to check~";
+		}
 		next;
-		if(countitem(915) < 10){
-			mes "[Mana]";
-			mes "Whoa, you need";
-			mes "more than just this.";
-			mes "I know you can do it...";
-			mes "Pupas are completely";
-			mes "defenseless monsters!";
-			close;
-		} else {
-			mes "[Mana]";
+		mes "[Mana]";
+		if (countitem(915) < 10){// Chrysalis
+			if (tu_magician01 == 3) {
+				mes "Aw, you don't";
+				mes "have enough of";
+				mes "them yet. Head west";
+				mes "from here and hunt Pupa";
+				mes "to get more Chrysalis, okay?";
+				mes "For now, I'll just heal you up.";
+				tu_magician01 = 4;
+				percentheal 100,100;
+			}
+			else {
+				mes "Whoa, you need";
+				mes "more than just this.";
+				mes "I know you can do it...";
+				mes "Pupas are completely";
+				mes "defenseless monsters!";
+			}
+		}
+		else {
 			mes "...Nine, ten.";
 			mes "Alright, you pass!";
 			mes "By now, you must be more";
 			mes "used to using your skills, huh?";
 			mes "Anyway, let me know when you're ready for the next lesson, okay?";
-			set tu_magician01, 5;
+			tu_magician01 = 5;
 			getexp 400,200;
 			specialeffect2 EF_HIT5;
-			close;
 		}
+		close;
 	case 5:
 		mes "Now I'll tell you";
 		mes "what I know about the";
 		mes "more advanced Mage";
 		mes "skills. Which one would";
 		mes "you like me to explain?";
-		next;
 		while(1){
+			next;
 			switch(select("Soul Strike:Safety Wall:Fire Ball:Frost Diver:Thunderstorm:Fire Wall:Energy Coat:Quit.")) {
 			case 1:
 				mes "[Mana]";
@@ -544,8 +526,7 @@ geffen,67,180,4	script	New Mage Manager#M	102,{
 				mes "of Soul Strike and increase";
 				mes "that skill's level, it will grow in strength and you'll be able";
 				mes "to cast it much more quickly.";
-				set .@skill_e1,1;
-				next;
+				.@skill_e1 = 1;
 				break;
 			case 2:
 				mes "[Mana]";
@@ -564,8 +545,7 @@ geffen,67,180,4	script	New Mage Manager#M	102,{
 				mes "certain number of attacks,";
 				mes "depending on the level of the";
 				mes "Safety Wall skill that you use.";
-				set .@skill_e2,1;
-				next;
+				.@skill_e2 = 1;
 				break;
 			case 3:
 				mes "[Mana]";
@@ -583,8 +563,7 @@ geffen,67,180,4	script	New Mage Manager#M	102,{
 				mes "the range of the skill. Make";
 				mes "good use of the destructive";
 				mes "power of the Fire Ball!";
-				next;
-				set .@skill_e3,1;
+				.@skill_e3 = 1;
 				break;
 			case 4:
 				mes "[Mana]";
@@ -610,8 +589,7 @@ geffen,67,180,4	script	New Mage Manager#M	102,{
 				mes "skill, its damage and chance";
 				mes "of freezing monsters will also";
 				mes "increase. Pretty dangerous, eh? ^FFFFFFkamenriderblack^000000";
-				set .@skill_e4,1;
-				next;
+				.@skill_e4 = 1;
 				break;
 			case 5:
 				mes "[Mana]";
@@ -629,8 +607,7 @@ geffen,67,180,4	script	New Mage Manager#M	102,{
 				mes "within a certain range.";
 				mes "It's an incredibly potent";
 				mes "ground targeting attack.";
-				set .@skill_e5,1;
-				next;
+				.@skill_e5 = 1;
 				break;
 			case 6:
 				mes "[Mana]";
@@ -655,8 +632,7 @@ geffen,67,180,4	script	New Mage Manager#M	102,{
 				mes "Fire Well strategically";
 				mes "and effectively, it can";
 				mes "be a very powerful skill~";
-				set .@skill_e6,1;
-				next;
+				.@skill_e6 = 1;
 				break;
 			case 7:
 				mes "[Mana]";
@@ -674,8 +650,7 @@ geffen,67,180,4	script	New Mage Manager#M	102,{
 				mes "learn Energy Coat, I'm";
 				mes "sure Blizardis will tell";
 				mes "you all you need to know.";
-				set .@skill_e7,1;
-				next;
+				.@skill_e7 = 1;
 				break;
 			case 8:
 				mes "[Mana]";
@@ -692,19 +667,12 @@ geffen,67,180,4	script	New Mage Manager#M	102,{
 				mes "you to help you in your";
 				mes "training. I hope it comes";
 				mes "in handy when fighting...";
-				set tu_magician01, 6;
+				tu_magician01 = 6;
 				getitem 1604,1; //Wand
 				specialeffect2 EF_HIT5;
-				if(.@skill_e1) getexp 300,100;
-				if(.@skill_e2) getexp 300,100;
-				if(.@skill_e3) getexp 300,100;
-				if(.@skill_e4) getexp 300,100;
-				if(.@skill_e5) getexp 300,100;
-				if(.@skill_e6) getexp 300,100;
-				if(.@skill_e7) getexp 300,100;
+				.@total = .@skill_e1 + .@skill_e2 + .@skill_e3 + .@skill_e4 + .@skill_e5;
+				if (.@total) getexp (.@total*300),(.@total*100);
 				close;
-			default: 
-				break;
 			}
 		}
 		close;
@@ -739,7 +707,7 @@ geffen,67,180,4	script	New Mage Manager#M	102,{
 			mes "done with what Yierhan";
 			mes "has you do, come back";
 			mes "and report to me, okay?";
-			set tu_magician01, 7;
+			tu_magician01 = 7;
 			emotion e_heh;
 			warp "morocc",182,286;
 			close;
@@ -764,7 +732,7 @@ geffen,67,180,4	script	New Mage Manager#M	102,{
 		mes "a message, saying that";
 		mes "you've done a great job in";
 		mes "completing your mission.";
-		mes "Very nice work, "+PcName+"~";
+		mes "Very nice work, "+strcharinfo(0)+"~";
 		next;
 		mes "[Mana]";
 		mes "It seems like there are";
@@ -786,9 +754,9 @@ geffen,67,180,4	script	New Mage Manager#M	102,{
 		mes "a little reward for helping";
 		mes "out both of our guilds. Once";
 		mes "again, thanks very much~";
-		set tu_magician01, 28;
+		tu_magician01 = 28;
 		getitem 2321,1; //Silk_Robe
-		set Zeny, Zeny + 3000;
+		Zeny = Zeny + 3000;
 		getexp 3000,1000;
 		close;
 	default:
@@ -804,7 +772,6 @@ geffen,67,180,4	script	New Mage Manager#M	102,{
 			mes "keep up the good";
 			mes "work. And, um, is";
 			mes "Yierhan doing well?";
-			close;
 		} else {
 			mes "You seem to be well";
 			mes "experienced now and there";
@@ -812,7 +779,7 @@ geffen,67,180,4	script	New Mage Manager#M	102,{
 			mes "really teach you. Now it's up";
 			mes "to you to find ways to master";
 			mes "the ways of magic on your own.";
-			close;
 		}
+		close;
 	}
 }

文件差异内容过多而无法显示
+ 347 - 632
npc/quests/first_class/tu_merchant.txt


+ 184 - 183
npc/quests/first_class/tu_thief01.txt

@@ -1,26 +1,27 @@
-//===== rAthena Script ======================================= 
+//===== rAthena Script =======================================
 //= Thief Class Tutorial and Job Specific Quest
-//===== By: ================================================== 
+//===== By: ==================================================
 //= Fix up by Jukka
-//===== Current Version: ===================================== 
-//= 1.5
-//===== Compatible With: ===================================== 
+//===== Current Version: =====================================
+//= 2.0
+//===== Compatible With: =====================================
 //= rAthena Project
-//===== Description: ========================================= 
+//===== Description: =========================================
 //= [Official Conversion]
 //= Thief training quest.
-//===== Additional Comments: ================================= 
+//===== Additional Comments: =================================
 //= 1.0 Fully working
 //= 1.1 optimized [Lupus]
 //= 1.2 Fixed experience gains to match upcoming rate adjustments. [SinSloth]
 //= 1.3 Fixed a few minor mistakes [Playtester]
 //= 1.4 Misc. updates. [L0ne_W0lf]
 //= 1.5 Added Pre-Renewal support. [Euphy]
-//============================================================ 
+//= 2.0 Fixed a wrong exp value & clean-up. [Capuche]
+//============================================================
 
 // Thief Trainer
 //============================================================
-moc_ruins,66,164,4	script	Thief Trainer#T	84,{
+moc_ruins,66,164,4	script	Thief Trainer#T	4_M_02,{
 	mes "[Yierhan]";
 	if(Class == Job_Novice){
 		mes "Eh...?";
@@ -34,22 +35,20 @@ moc_ruins,66,164,4	script	Thief Trainer#T	84,{
 		mes "Listen, if you ever decide to become a Thief--a smart choice";
 		mes "I might add--come and talk to me. I'll show you the ropes!";
 		close;
-
-	} else if(Class == Job_Thief || Class == Job_Thief_High || Class == Job_Assassin || Class ==Job_Assassin_Cross || Class == Job_Rogue || Class == Job_Stalker){
-		if(Class == Job_Thief_High || Class == Job_Assassin || Class == Job_Assassin_Cross || Class == Job_Rogue || Class == Job_Stalker){
-			if(tu_thief01 < 8){
-				mes "Whaaaat are you";
-				mes "doin' here? There's";
-				mes "nothing I can teach you!";
-				mes "You're waaay beyond me!";
-				next;
-				mes "[Yierhan]";
-				mes "In fact, I think";
-				mes "you're qualified";
-				mes "to teach me some stuff!";
-				mes "Come on! I need new moves!";
-				close;
-			}
+	}
+	else if (BaseClass == Job_Thief && Upper != 2) {
+		if (Class != Job_Thief && tu_thief01 < 8) {
+			mes "Whaaaat are you";
+			mes "doin' here? There's";
+			mes "nothing I can teach you!";
+			mes "You're waaay beyond me!";
+			next;
+			mes "[Yierhan]";
+			mes "In fact, I think";
+			mes "you're qualified";
+			mes "to teach me some stuff!";
+			mes "Come on! I need new moves!";
+			close;
 		}
 		if(tu_thief01 == 0){
 			mes "Heya pal.";
@@ -88,10 +87,8 @@ moc_ruins,66,164,4	script	Thief Trainer#T	84,{
 				next;
 				mes "[Yierhan]";
 				mes "So if you're feeling like the damage you're making is pretty weak, you might want more stat points in ^2F4F2FSTR^000000. How high your raise your own STR is really up to you.";
-				set tu_thief01, 1;
 				getexp 200,100;
-				specialeffect2 EF_HIT5;
-				close;
+				break;
 			case 2:
 				mes "[Yierhan]";
 				mes "Yeah, that's right! If you wanna increase your damage, you need";
@@ -104,10 +101,8 @@ moc_ruins,66,164,4	script	Thief Trainer#T	84,{
 				mes "[Yierhan]";
 				mes "Sure, ^23238EDEX^000000 and LUK can";
 				mes "also increase your attack damage, but they're insignificant compared to STR. I repeat: ^660000insignificant^000000.";
-				set tu_thief01, 1;
 				getexp 400,200;
-				specialeffect2 EF_HIT5;
-				close;
+				break;
 			case 3:
 				mes "[Yierhan]";
 				mes "Say whaaat? ^23238EDEX^000000 affects";
@@ -119,129 +114,131 @@ moc_ruins,66,164,4	script	Thief Trainer#T	84,{
 				next;
 				mes "[Yierhan]";
 				mes "So if you're feeling like the damage you're making is pretty weak, you might want more stat points in ^2F4F2FSTR^000000. How high your raise your own STR is really up to you.";
-				set tu_thief01, 1;
 				getexp 200,100;
-				specialeffect2 EF_HIT5;
-				close;
 			}
-		} else if(tu_thief01 == 1){
+			tu_thief01 = 1;
+			specialeffect2 EF_HIT5;
+			close;
+		}
+		else if(tu_thief01 == 1){
 			mes "Alright, enough about stats.";
 			mes "You know what? I think I'll just talk to you about the skills that we Thieves use.";
 			next;
 			mes "[Yierhan]";
 			mes "So level up your skills, learn a few new ones if you want, and";
 			mes "then come back over here.";
-			set tu_thief01, 2;
+			tu_thief01 = 2;
 			close;
-		} else if(tu_thief01 == 2){
+		}
+		else if(tu_thief01 == 2){
 			mes "Okay, let me see your skills. You know you gotta change your battle strategy depending on what skills you have, right? Skills are just as important as stats!";
 			next;
-			set .@chk_th_skill1, getskilllv("TF_DOUBLE");
-			set .@chk_th_skill2, getskilllv("TF_MISS");
-			set .@chk_th_skill3, getskilllv("TF_STEAL");
-			set .@chk_th_skill4, getskilllv("TF_HIDING");
-			set .@chk_th_skill5, getskilllv("TF_POISON");
-			set .@chk_th_skill6, getskilllv("TF_DETOXIFY");
+			.@chk_th_skill1 = getskilllv("TF_DOUBLE");
+			.@chk_th_skill2 = getskilllv("TF_MISS");
+			.@chk_th_skill3 = getskilllv("TF_STEAL");
+			.@chk_th_skill4 = getskilllv("TF_HIDING");
+			.@chk_th_skill5 = getskilllv("TF_POISON");
+			.@chk_th_skill6 = getskilllv("TF_DETOXIFY");
 
 			if(.@chk_th_skill1 == 0 && .@chk_th_skill2 == 0 && .@chk_th_skill3 == 0 && .@chk_th_skill4 == 0 && .@chk_th_skill5 == 0 && .@chk_th_skill6 == 0){
 				mes "[Yierhan]";
 				mes "You haven't learned any skills yet? Come on, it's your skills that'll set you apart from Novices and everyone else!";
 				close;
-			} else {
-				if(.@chk_th_skill1 > 0){
-					mes "[Yierhan]";
-					mes "Ah, so you've learned";
-					mes "Level "+.@chk_th_skill1+" Double Attack.";
-					mes "Nice! This skill gives you the chance to attack twice in one";
-					mes "attack. Wicked!";
-					next;
-					mes "[Yierhan]";
-					mes "It's a Passive skill, so it's always in effect and won't have to use any SP to use it. The higher your Double Attack skill level, the more double attacks you'll do.";
-					next;
-				}
-				if(.@chk_th_skill2 > 0){
-					mes "[Yierhan]";
-					mes "Let's see...";
-					mes "Level "+.@chk_th_skill2+" Increase Dodge?";
-					mes "That increases your Flee Rate, meaning you've got a better chance of dodging attacks from your enemies.";
-					next;
-					mes "[Yierhan]";
-					mes "Just like the Double Attack skill, Increase Dodge is a Passive skill. It won't use SP and it's always in";
-					mes "effect. If you don't like to bruise, this is your skill.";
-					next;
-				}
-				if(.@chk_th_skill3 > 0){
-					mes "[Yierhan]";
-					mes "Whoa, so you've";
-					mes "got Level "+.@chk_th_skill3+" Steal~";
-					mes "Now that's the skill which gives our job its name! You can't use";
-					mes "it against people, though...";
-					next;
-					mes "[Yierhan]";
-					mes "But you can use Steal to take items from monsters. If you're lucky, you can get some good items that way. Oh, and Steal doesn't affect monster drop rates.";
-					next;
-				}
-				if(.@chk_th_skill4 > 0){
-					mes "[Yierhan]";
-					mes "You've learned";
-					mes "Level "+.@chk_th_skill4+" Hiding?";
-					mes "Let's see, you can only learn";
-					mes "that after learning the Steal skill up to a certain level.";
-					next;
-					mes "[Yierhan]";
-					mes "Of course, you use ";
-					mes "the Hiding skill to hide underground in an emergency, like when you're surrounded by tough enemies. Be careful though...";
-					next;
-					mes "[Yierhan]";
-					mes "Certain monsters will still be able to find you, no matter how well you hide. There are even a few monsters that can flush you out of hiding!";
-					next;
-				}
-				if(.@chk_th_skill5 > 0){
-					mes "[Yierhan]";
-					mes "Alright, I see that you";
-					mes "know Level "+.@chk_th_skill5+" Envenom.";
-					mes "You like being dangerous,";
-					mes "don't you?";
-					next;
-					mes "[Yierhan]";
-					mes "This attack skill has the chance";
-					mes "to poison your enemy for a set amount of time. While poisoned, an enemy will constantly lose its HP and will have decreased defense.";
-					next;
-					mes "[Yierhan]";
-					mes "Eh, but remember.";
-					mes "If the monster's too strong for you, you might not be able to poison it. So don't go crazy.";
-					next;
-				}
-				if(.@chk_th_skill6 > 0){
-					mes "[Yierhan]";
-					mes "Level "+.@chk_th_skill6+" Detoxify.";
-					mes "If you took the trouble to learn that, you must be the cautious";
-					mes "type or something.";
-					next;
-					mes "[Yierhan]";
-					mes "You can only learn Detoxify";
-					mes "after you learn the Envenom skill. Detoxify allows you to counteract the effects of poison on a target.";
-					next;
-				}
+			}
+			if(.@chk_th_skill1 > 0){
+				mes "[Yierhan]";
+				mes "Ah, so you've learned";
+				mes "Level "+.@chk_th_skill1+" Double Attack.";
+				mes "Nice! This skill gives you the chance to attack twice in one";
+				mes "attack. Wicked!";
+				next;
+				mes "[Yierhan]";
+				mes "It's a Passive skill, so it's always in effect and won't have to use any SP to use it. The higher your Double Attack skill level, the more double attacks you'll do.";
+				next;
+			}
+			if(.@chk_th_skill2 > 0){
+				mes "[Yierhan]";
+				mes "Let's see...";
+				mes "Level "+.@chk_th_skill2+" Increase Dodge?";
+				mes "That increases your Flee Rate, meaning you've got a better chance of dodging attacks from your enemies.";
+				next;
+				mes "[Yierhan]";
+				mes "Just like the Double Attack skill, Increase Dodge is a Passive skill. It won't use SP and it's always in";
+				mes "effect. If you don't like to bruise, this is your skill.";
+				next;
+			}
+			if(.@chk_th_skill3 > 0){
+				mes "[Yierhan]";
+				mes "Whoa, so you've";
+				mes "got Level "+.@chk_th_skill3+" Steal~";
+				mes "Now that's the skill which gives our job its name! You can't use";
+				mes "it against people, though...";
+				next;
+				mes "[Yierhan]";
+				mes "But you can use Steal to take items from monsters. If you're lucky, you can get some good items that way. Oh, and Steal doesn't affect monster drop rates.";
+				next;
+			}
+			if(.@chk_th_skill4 > 0){
+				mes "[Yierhan]";
+				mes "You've learned";
+				mes "Level "+.@chk_th_skill4+" Hiding?";
+				mes "Let's see, you can only learn";
+				mes "that after learning the Steal skill up to a certain level.";
+				next;
+				mes "[Yierhan]";
+				mes "Of course, you use ";
+				mes "the Hiding skill to hide underground in an emergency, like when you're surrounded by tough enemies. Be careful though...";
+				next;
+				mes "[Yierhan]";
+				mes "Certain monsters will still be able to find you, no matter how well you hide. There are even a few monsters that can flush you out of hiding!";
+				next;
+			}
+			if(.@chk_th_skill5 > 0){
+				mes "[Yierhan]";
+				mes "Alright, I see that you";
+				mes "know Level "+.@chk_th_skill5+" Envenom.";
+				mes "You like being dangerous,";
+				mes "don't you?";
+				next;
+				mes "[Yierhan]";
+				mes "This attack skill has the chance";
+				mes "to poison your enemy for a set amount of time. While poisoned, an enemy will constantly lose its HP and will have decreased defense.";
+				next;
+				mes "[Yierhan]";
+				mes "Eh, but remember.";
+				mes "If the monster's too strong for you, you might not be able to poison it. So don't go crazy.";
+				next;
+			}
+			if(.@chk_th_skill6 > 0){
+				mes "[Yierhan]";
+				mes "Level "+.@chk_th_skill6+" Detoxify.";
+				mes "If you took the trouble to learn that, you must be the cautious";
+				mes "type or something.";
+				next;
+				mes "[Yierhan]";
+				mes "You can only learn Detoxify";
+				mes "after you learn the Envenom skill. Detoxify allows you to counteract the effects of poison on a target.";
+				next;
 			}
 			mes "[Yierhan]";
 			mes "Alright, I guess";
 			mes "if you want to know";
 			mes "about any other skills,";
 			mes "I can explain real quick.";
-			set tu_thief01, 3;
+			tu_thief01 = 3;
 			getexp BaseLevel*30,BaseLevel*15;
 			specialeffect2 EF_HIT5;
 			close;
-		} else if(tu_thief01 == 3){
+		}
+		else if(tu_thief01 == 3){
 			mes "So...";
 			mes "Are there any";
 			mes "skills you want";
 			mes "explained or is this";
 			mes "pretty much stuff you";
 			mes "already know?";
-			next;
 			while(1){
+				next;
 				switch(select("Double Attack:Increase Dodge:Steal:Hiding:Envenom:Detoxify:I know enough.")) {
 				case 1:
 					mes "[Yierhan]";
@@ -250,8 +247,7 @@ moc_ruins,66,164,4	script	Thief Trainer#T	84,{
 					next;
 					mes "[Yierhan]";
 					mes "It's a Passive skill, so it's always in effect and won't have to use any SP to use it. The higher your Double Attack skill level, the more double attacks you'll do.";
-					set .@read_d, 1;
-					next;
+					.@read_d = 1;
 					break;
 				case 2:
 					mes "[Yierhan]";
@@ -265,8 +261,7 @@ moc_ruins,66,164,4	script	Thief Trainer#T	84,{
 					mes "[Yierhan]";
 					mes "Just like the Double Attack skill, Increase Dodge is a Passive skill. It won't use SP and it's always in";
 					mes "effect. If you don't like to bruise, this is your skill.";
-					set .@read_f, 1;
-					next;
+					.@read_f = 1;
 					break;
 				case 3:
 					mes "[Yierhan]";
@@ -276,8 +271,7 @@ moc_ruins,66,164,4	script	Thief Trainer#T	84,{
 					specialeffect2 EF_STEAL;
 					mes "[Yierhan]";
 					mes "But you can use Steal to take items from monsters! If you're lucky, you can get some good items that way. Oh, and Steal doesn't affect monster drop rates.";
-					set .@read_s, 1;
-					next;
+					.@read_s = 1;
 					break;
 				case 4:
 					mes "[Yierhan]";
@@ -289,8 +283,7 @@ moc_ruins,66,164,4	script	Thief Trainer#T	84,{
 					next;
 					mes "[Yierhan]";
 					mes "Certain monsters will still be able to find you, no matter how well you hide. There are even a few monsters that can flush you out of hiding!";
-					set .@read_p, 1;
-					next;
+					.@read_p = 1;
 					break;
 				case 5:
 					mes "[Yierhan]";
@@ -300,8 +293,7 @@ moc_ruins,66,164,4	script	Thief Trainer#T	84,{
 					mes "This attack skill has the chance";
 					mes "to poison your enemy for a set amount of time. While poisoned, an enemy will constantly lose its HP and will have decreased defense.";
 					mes "Remember that.";
-					set .@read_h, 1;
-					next;
+					.@read_h = 1;
 					break;
 				case 6:
 					mes "[Yierhan]";
@@ -314,10 +306,9 @@ moc_ruins,66,164,4	script	Thief Trainer#T	84,{
 					mes "[Yierhan]";
 					mes "You can learn Detoxify";
 					mes "after you learn the Envenom skill. Detoxify allows you to counteract the effects of poison on a target.";
-					set .@read_r, 1;
+					.@read_r = 1;
 					next;
 					specialeffect2 EF_DETOXICATION;
-					next;
 					break;
 				case 7:
 					mes "[Yierhan]";
@@ -327,18 +318,16 @@ moc_ruins,66,164,4	script	Thief Trainer#T	84,{
 					next;
 					mes "[Yierhan]";
 					mes "Now that you're such an expert on skills, I want you to level up your skills and come back, got it?";
-					set tu_thief01, 4;
+					tu_thief01 = 4;
 					specialeffect2 EF_HIT5;
-					if(.@read_d) getexp 300,100;
-					if(.@read_f) getexp 300,100;
-					if(.@read_s) getexp 300,100;
-					if(.@read_p) getexp 300,100;
-					if(.@read_h) getexp 300,100;
-					if(.@read_r) getexp 300,100;
+					.@total = .@read_d + .@read_f + .@read_s + .@read_p + .@read_h + .@read_r;
+					if (.@total)
+						getexp (.@total*300),(.@total*100);
 					close;
 				}
 			}
-		} else if(tu_thief01 == 4){
+		}
+		else if(tu_thief01 == 4){
 			mes "Alright, we studied the skills and you've been practicing a little, right? You better have...";
 			next;
 			mes "[Yierhan]";
@@ -348,7 +337,7 @@ moc_ruins,66,164,4	script	Thief Trainer#T	84,{
 			mes "Go and get me";
 			mes "10 ^ff0000Feather of Birds^000000.";
 			mes "You can go ahead and kill Pickies to get those. It really shouldn't be that hard. Oh, and use this Wing thingee to come back.";
-			set tu_thief01, 5;
+			tu_thief01 = 5;
 			savepoint "moc_ruins",80,164;
 			getitem 602,1; //Wing_Of_Butterfly
 			getexp 100,50;
@@ -356,14 +345,16 @@ moc_ruins,66,164,4	script	Thief Trainer#T	84,{
 			close2;
 			warp "moc_fild12",158,373;
 			end;
-		} else if(tu_thief01 == 5 || tu_thief01 == 6){
+		}
+		else if(tu_thief01 == 5 || tu_thief01 == 6){
 			if(countitem(916) < 10){
 				mes "'Ey, you don't have the 10 ^ff0000Feather of Birds^000000 I asked you for! You gotta apply what you know, you know.";
 				mes "Now hurry up and do it!";
 				close2;
 				warp "moc_fild07",203,38;
 				end;
-			} else {
+			}
+			else {
 				mes "Alright...!";
 				mes "Nice work, pal.";
 				mes "Seeing as you got these feathers, you must be really gung-ho about becoming a good Thief.";
@@ -407,7 +398,7 @@ moc_ruins,66,164,4	script	Thief Trainer#T	84,{
 						mes "not to get too close";
 						mes "to the poison! That stuff";
 						mes "is pretty strong!";
-						set tu_thief01, 8;
+						tu_thief01 = 8;
 						getitem 1207,1; //Main_Gauche
 						getexp 1000,500;
 						specialeffect2 EF_HIT5;
@@ -417,13 +408,14 @@ moc_ruins,66,164,4	script	Thief Trainer#T	84,{
 						mes "Good!";
 						mes "Less work for me!";
 						mes "Alright, you better get stronger the next time I see you. Oh, and you can have this stuff. You know, since you're so gangster and all.";
-						set tu_thief01, 7;
+						tu_thief01 = 7;
 						getitem 1207,1; //Main_Gauche
 						getexp 500,200;
 						specialeffect2 EF_HIT5;
 						close;
 					}
-				} else {
+				}
+				else {
 					switch(select("It was nice to meet you.:Nope.")) {
 					case 1:
 						mes "[Yierhan]";
@@ -436,22 +428,24 @@ moc_ruins,66,164,4	script	Thief Trainer#T	84,{
 						mes "you so much, kid,";
 						mes "you can have this.";
 						mes "Take it, it's yours!";
+						getexp 500,200;
 						break;
 					case 2:
 						mes "[Yierhan]";
 						mes "Good!";
 						mes "Less work for me!";
 						mes "Alright, you better get stronger the next time I see you. Oh, and you can have this stuff. You know, since you're so gangster and all.";
+						getexp 50,20;
 						break;
 					}
-					set tu_thief01, 7;
+					tu_thief01 = 7;
 					getitem 1207,1; //Main_Gauche
-					getexp 500,200;
 					specialeffect2 EF_HIT5;
 					close;
 				}
 			}
-		} else if(tu_thief01 == 7){
+		}
+		else if(tu_thief01 == 7){
 			mes "[Yierhan]";
 			mes "You know...";
 			mes "There was this";
@@ -488,11 +482,12 @@ moc_ruins,66,164,4	script	Thief Trainer#T	84,{
 			mes "decide to check it";
 			mes "out, be real careful";
 			mes "other there, okay?";
-			set tu_thief01, 8;
+			tu_thief01 = 8;
 			getexp 200,100;
 			specialeffect2 EF_HIT5;
 			close;
-		} else if(tu_thief01 == 8){
+		}
+		else if(tu_thief01 == 8){
 			mes "Heya pal.";
 			mes "You doin' alright?";
 			next;
@@ -504,7 +499,8 @@ moc_ruins,66,164,4	script	Thief Trainer#T	84,{
 			mes "It's a good policy to just be really careful on your adventures. Look out for monsters and look";
 			mes "out for people! Got it?";
 			close;
-		} else if(tu_thief01 < 26){
+		}
+		else if(tu_thief01 < 26){
 			mes "I heard there was";
 			mes "this one Assassin";
 			mes "that went on a mission";
@@ -520,7 +516,8 @@ moc_ruins,66,164,4	script	Thief Trainer#T	84,{
 			mes "Anyway, take";
 			mes "care of yourself.";
 			close;
-		} else if(tu_thief01 == 26){
+		}
+		else if(tu_thief01 == 26){
 			mes "'Ey, did you";
 			mes "complete your mission?";
 			mes "I know, I know, the thing you've gotta do is pretty rough.";
@@ -570,13 +567,14 @@ moc_ruins,66,164,4	script	Thief Trainer#T	84,{
 				next;
 				mes "[Yierhan]";
 				mes "Hey, this stuff is yours. Think of it as a reward for helping us out. Take care of yourself now~";
-				set tu_thief01, 27;
+				tu_thief01 = 27;
 				getitem 2307,1; //Mantle
-				set Zeny, Zeny + 5000;
+				Zeny = Zeny + 5000;
 				getexp 8000,3000;
 				close;
 			}
-		} else {
+		}
+		else {
 			mes "[Yierhan]";
 			mes "Hey...";
 			mes "You got dreams,";
@@ -589,7 +587,8 @@ moc_ruins,66,164,4	script	Thief Trainer#T	84,{
 			mes "I just like telling people to follow their dreams. So do it. Life without anything to look forward to is pretty boring, doncha think?";
 			close;
 		}
-	} else if(Class == Job_Mage || Class == Job_Mage_High || Class == Job_Wizard || Class == Job_High_Wizard || Class == Job_Sage || Class == Job_Professor){
+	}
+	else if (BaseClass == Job_Mage && Upper != 2){
 		if(tu_magician01 < 7){
 			mes "[Yierhan]";
 			mes "Heya.";
@@ -599,31 +598,31 @@ moc_ruins,66,164,4	script	Thief Trainer#T	84,{
 			mes "I actually got a few magic using friends here and there. That mystic stuff is waaay over my head, but";
 			mes "I got a lotta respect for it.";
 			close;
-		} else if(tu_magician01 == 7){
+		}
+		else if(tu_magician01 == 7){
 			mes "Hm...?";
 			mes "That's weird, usually only";
 			mes "Thieves hang around this joint. What's someone like you doing";
 			mes "here? Unless...";
 			next;
-			switch(select("I'm here on behalf of 'Mana.'")) {
-			case 1:
-				mes "[Yierhan]";
-				mes "Right, you must be the help that Mana sent! You came just at the right time. You see, we found something weird in South Morroc.";
-				next;
-				mes "[Yierhan]";
-				mes "Basically, we found traces of poison that were used in a fight. We were going to investigate it, but we've been swamped with all this other work.";
-				next;
-				mes "[Yierhan]";
-				mes "That's why we've been asking";
-				mes "for help from the Mage Guild. Fortunately, I'm pals with Mana, so...";
-				next;
-				mes "[Yierhan]";
-				mes "Anyway, head over to";
-				mes "South Morroc since it seems to be a good place to start investigating. You'll see what we found right outside the South Morroc gate.";
-				set tu_magician01, 8;
-				break;
-			}
-		} else if(tu_magician01 < 26){
+			select("I'm here on behalf of 'Mana.'");
+			mes "[Yierhan]";
+			mes "Right, you must be the help that Mana sent! You came just at the right time. You see, we found something weird in South Morroc.";
+			next;
+			mes "[Yierhan]";
+			mes "Basically, we found traces of poison that were used in a fight. We were going to investigate it, but we've been swamped with all this other work.";
+			next;
+			mes "[Yierhan]";
+			mes "That's why we've been asking";
+			mes "for help from the Mage Guild. Fortunately, I'm pals with Mana, so...";
+			next;
+			mes "[Yierhan]";
+			mes "Anyway, head over to";
+			mes "South Morroc since it seems to be a good place to start investigating. You'll see what we found right outside the South Morroc gate.";
+			tu_magician01 = 8;
+			close;
+		}
+		else if(tu_magician01 < 26){
 			mes "I heard there was";
 			mes "this one Assassin";
 			mes "that went on a mission";
@@ -641,7 +640,8 @@ moc_ruins,66,164,4	script	Thief Trainer#T	84,{
 			mes "might be related to";
 			mes "that mysterious Assassin...";
 			close;
-		} else if(tu_magician01 == 26){
+		}
+		else if(tu_magician01 == 26){
 			mes "So how's the";
 			mes "investigation";
 			mes "coming along?";
@@ -694,12 +694,13 @@ moc_ruins,66,164,4	script	Thief Trainer#T	84,{
 				next;
 				mes "[Yierhan]";
 				mes "Hey, this stuff is yours. Think of it as a reward for helping us out. Right, and I'll let Mana know you did a great job. Take care of yourself now~";
-				set tu_magician01, 27;
-				set Zeny, Zeny + 5000;
+				tu_magician01 = 27;
+				Zeny = Zeny + 5000;
 				getexp 5000,2000;
 				close;
 			}
-		} else {
+		}
+		else {
 			mes "Just as I thought,";
 			mes "more than one group";
 			mes "was involved in all this.";

文件差异内容过多而无法显示
+ 521 - 393
npc/quests/quests_airship.txt


文件差异内容过多而无法显示
+ 272 - 242
npc/quests/quests_moscovia.txt


+ 10 - 0
npc/re/other/stone_change.txt

@@ -43,6 +43,16 @@ geffen_in,99,174,5	script	Ore Tester#sorty	8_F_GIRL,{
 			mes "You don't have any "+getitemname(.@item)+"...";
 			close;
 		}
+		// while(1) {
+			// if (countitem(.@item)) {
+				// set .@count, countitem(.@item);
+				// set .@total, .@total + .@count;
+				// delitem .@item, .@count;
+			// } else {
+				// getitem .@item, .@total;
+				// break;
+			// }
+		// }
 		.@count = countitem(.@item);
 		delitem .@item, .@count;
 		getitem .@item, .@count;

+ 3 - 3
npc/re/quests/quests_brasilis.txt

@@ -190,7 +190,7 @@ brasilis,192,133,6	script	Lucia#brasilis	4_F_BRZ_WOMAN,{
 			mes "See you then.";
 			close;
 		case 3:
-		L_whathappen:
+		L_WhatHappen:
 			mes "[Lucia]";
 			mes "One day ^FF0000Strange Hydra^000000s";
 			mes "came here and surrounded the town.";
@@ -261,7 +261,7 @@ brasilis,192,133,6	script	Lucia#brasilis	4_F_BRZ_WOMAN,{
 					mes "Ok, please do me a favor.";
 					close;
 				case 2:
-					goto L_whathappen;
+					goto L_WhatHappen;
 				}
 			}
 		}
@@ -297,7 +297,7 @@ brasilis,192,133,6	script	Lucia#brasilis	4_F_BRZ_WOMAN,{
 				mes "See you then.";
 				close;
 			case 3:
-				goto L_whathappen;
+				goto L_WhatHappen;
 			}
 		}
 	}

+ 26 - 91
npc/warps/dungeons/mosk_dun.txt

@@ -3,7 +3,7 @@
 //===== By: ==================================================
 //= Kisuka, $ephiroth
 //===== Current Version: =====================================
-//= 1.4
+//= 2.0
 //===== Compatible With: =====================================
 //= rAthena Project
 //===== Description: =========================================
@@ -13,13 +13,14 @@
 //= 1.1, 1.2 Updated from jA [$ephiroth]
 //= 1.3 Added warp npcs. Removed place holder warps. [Kisuka]
 //= 1.4 Fixed cords on milestone 1-3. [Kisuka]
+//= 2.0 Clean-up. [Capuche]
 //============================================================
 
 mosk_fild02,190,257,0	warp	moscoviawarp0401	1,1,mosk_dun01,189,45
 mosk_dun01,189,42,0	warp	moscoviawarp0501	1,1,mosk_fild02,190,254
 mosk_in,215,36,0	warp	babayagaout		1,1,mosk_dun02,53,217
 
-mosk_dun02,53,220,4	script	#babayagain	45,1,1,{
+mosk_dun02,53,220,4	script	#babayagain	WARPNPC,1,1,{
 	end;
 OnTouch:
 	if (mos_nowinter == 11) {
@@ -47,7 +48,7 @@ OnTouch:
 		warp "mosk_in",125,92;
 		end;
 	}
-	else if (mos_nowinter == 12) {
+	else if (mos_nowinter == 12 || mos_nowinter == 13) {
 		donpcevent "Soldier1#mos::OnEnable";
 		donpcevent "Soldier2#mos::OnEnable";
 		donpcevent "Soldier3#mos::OnEnable";
@@ -61,51 +62,17 @@ OnTouch:
 			mes "I should get out of here...";
 			close2;
 			warp "mosk_dun02",100,100;
-			donpcevent "Soldier1#mos::OnInit";
-			donpcevent "Soldier2#mos::OnInit";
-			donpcevent "Soldier3#mos::OnInit";
-			end;
 		}
-		mes "-I explain what happend until now.-";
-		next;
-		mes "[Soldier]";
-		mes "If you have something to say, speak";
-		mes "to the Csar.";
-		mes "If you leave to this place,";
-		mes "you will be considered as our enemy.";
-		close2;
-		donpcevent "Soldier1#mos::OnInit";
-		donpcevent "Soldier2#mos::OnInit";
-		donpcevent "Soldier3#mos::OnInit";
-		end;
-	}
-	else if (mos_nowinter == 13) {
-		donpcevent "Soldier1#mos::OnEnable";
-		donpcevent "Soldier2#mos::OnEnable";
-		donpcevent "Soldier3#mos::OnEnable";
-		mes "[Soldier]";
-		mes "We expected";
-		mes "that you would appear again.";
-		mes "What is your business with the Csar?";
-		next;
-		if(select("Run away.:Explain the situation.") == 1) {
-			mes "["+strcharinfo(0)+"]";
-			mes "I should get out of here...";
+		else {
+			mes "-I explain what happend until now.-";
+			next;
+			mes "[Soldier]";
+			mes "If you have something to say, speak";
+			mes "to the Csar.";
+			mes "If you leave to this place,";
+			mes "you will be considered as our enemy.";
 			close2;
-			warp "mosk_dun02",100,100;
-			donpcevent "Soldier1#mos::OnInit";
-			donpcevent "Soldier2#mos::OnInit";
-			donpcevent "Soldier3#mos::OnInit";
-			end;
 		}
-		mes "-I explain what happend until now.-";
-		next;
-		mes "[Soldier]";
-		mes "If you have something to say, speak";
-		mes "to the Csar.";
-		mes "If you leave to this place,";
-		mes "you will be considered as our enemy.";
-		close2;
 		donpcevent "Soldier1#mos::OnInit";
 		donpcevent "Soldier2#mos::OnInit";
 		donpcevent "Soldier3#mos::OnInit";
@@ -117,7 +84,7 @@ OnTouch:
 	}
 }
 
-mosk_dun02,53,215,3	script	Soldier1#mos	966,{
+mosk_dun02,53,215,3	script	Soldier1#mos	4_M_RUSKNIGHT,{
 	mes "[Soldier]";
 	mes "We're the Csar's soldiers.";
 	mes "We're called out for emergencies,";
@@ -125,55 +92,23 @@ mosk_dun02,53,215,3	script	Soldier1#mos	966,{
 	close;
 	
 OnInit:
-	hideonnpc "Soldier1#mos";
-	end;
-
-OnEnable:
-	hideoffnpc "Soldier1#mos";
+	hideonnpc strnpcinfo(0);
 	end;
-}
-
-mosk_dun02,50,217,3	script	Soldier2#mos	966,{
-	mes "[Soldier]";
-	mes "We're the Csar's soldiers.";
-	mes "We're called out for emergencies,";
-	mes "don't disturb us anymore.";
-	close;
-	
-OnInit:
-	hideonnpc "Soldier2#mos";
-	end;
-
-OnEnable:
-	hideoffnpc "Soldier2#mos";
-	end;
-}
-
-
-mosk_dun02,56,217,5	script	Soldier3#mos	966,{
-	mes "[Soldier]";
-	mes "We're the Csar's soldiers.";
-	mes "We're called out for emergencies,";
-	mes "don't disturb us anymore.";
-	close;
-	
-OnInit:
-	hideonnpc "Soldier3#mos";
-	end;
-
 OnEnable:
-	hideoffnpc "Soldier3#mos";
+	hideoffnpc strnpcinfo(0);
 	end;
 }
+mosk_dun02,50,217,3	duplicate(Soldier1#mos)	Soldier2#mos	4_M_RUSKNIGHT
+mosk_dun02,56,217,5	duplicate(Soldier1#mos)	Soldier3#mos	4_M_RUSKNIGHT
 
-mosk_dun01,170,164,3	script	Milestone#1	111,{
+mosk_dun01,170,164,3	script	Milestone#1	HIDDEN_NPC,{
 	mes "- There is writing on a big rock. -";
 	next;
 	mes "^3131FF- Those who serve the Sun will die, Go to the North. -^000000";
 	close;
 }
 
-mosk_dun01,207,276,3	script	Milestone#1-2	111,{
+mosk_dun01,207,276,3	script	Milestone#1-2	HIDDEN_NPC,{
 	mes "- There is writing on a big rock. -";
 	next;
 	mes "^3131FF- Those who advance may get hurt but are courageous of heart and those who move back will remain safe.";
@@ -189,7 +124,7 @@ mosk_dun01,207,276,3	script	Milestone#1-2	111,{
 	close;
 }
 
-mosk_dun01,264,191,3	script	Milestone#1-3	111,{
+mosk_dun01,264,191,3	script	Milestone#1-3	HIDDEN_NPC,{
 	mes "- There is writing on a big rock. -";
 	next;
 	mes "^3131FF- Those who advance may get hurt but are brave of heart and those who move back will remain safe.";
@@ -205,7 +140,7 @@ mosk_dun01,264,191,3	script	Milestone#1-3	111,{
 	close;
 }
 
-mosk_dun02,168,28,3	script	Milestone#2-1	111,{
+mosk_dun02,168,28,3	script	Milestone#2-1	HIDDEN_NPC,{
 	mes "- There is writing on a big rock. -";
 	next;
 	mes "^3131FF- Those who advance may get hurt but are valorous of heart and those who move back will remain safe.";
@@ -221,7 +156,7 @@ mosk_dun02,168,28,3	script	Milestone#2-1	111,{
 	close;
 }
 
-mosk_dun02,268,117,3	script	Milestone#2-3	111,{
+mosk_dun02,268,117,3	script	Milestone#2-3	HIDDEN_NPC,{
 	mes "- There is writing on a big rock. -";
 	next;
 	mes "^3131FF- Those who step forward will gain honor but may die, those who step backward will have courage but may still get hurt.";
@@ -237,7 +172,7 @@ mosk_dun02,268,117,3	script	Milestone#2-3	111,{
 	close;
 }
 
-mosk_dun03,27,131,3	script	Milestone#3-2	111,{
+mosk_dun03,27,131,3	script	Milestone#3-2	HIDDEN_NPC,{
 	mes "- There is writing on a big rock. -";
 	next;
 	mes "^3131FF- Those who stay may die but will gain honor, those who go back may get hurt but will gain courage.";
@@ -253,7 +188,7 @@ mosk_dun03,27,131,3	script	Milestone#3-2	111,{
 	close;
 }
 
-mosk_dun03,119,46,3	script	Milestone#3-1	111,{
+mosk_dun03,119,46,3	script	Milestone#3-1	HIDDEN_NPC,{
 	mes "There is writing on a big rock.";
 	next;
 	mes "^3131FF- Those who stay may die but will gain honor, those who go back may get hurt but will gain courage.";
@@ -269,14 +204,14 @@ mosk_dun03,119,46,3	script	Milestone#3-1	111,{
 	close;
 }
 
-mosk_dun01,194,43,3	script	Milestone#1-1	111,{
+mosk_dun01,194,43,3	script	Milestone#1-1	HIDDEN_NPC,{
 	mes "- There is writing on a big rock. -";
 	next;
 	mes "- If you want to protect your life, go back -";
 	close;
 }
 
-mosk_fild02,198,53,3	script	Ship#mos_dun	111,{
+mosk_fild02,198,53,3	script	Ship#mos_dun	HIDDEN_NPC,{
 	mes "- You find a ship at anchor. -";
 	next;
 	if(select("Take the ship and go back to the village.:Explore more.") == 1) {

+ 20 - 6
sql-files/item_db_re.sql

@@ -2488,7 +2488,7 @@ REPLACE INTO `item_db_re` VALUES (4411,'Vanberk_Card','Vanberk Card',6,20,NULL,1
 REPLACE INTO `item_db_re` VALUES (4412,'Isilla_Card','Isilla Card',6,20,NULL,10,NULL,NULL,NULL,NULL,NULL,NULL,NULL,769,NULL,NULL,NULL,NULL,'bonus bInt,2; autobonus "{ bonus bVariableCastrate,-50; bonus bFlee,30; }",50,5000,BF_MAGIC,"{ specialeffect2 EF_SUFFRAGIUM; }";',NULL,NULL);
 REPLACE INTO `item_db_re` VALUES (4413,'Hodremlin_Card','Hodremlin Card',6,20,NULL,10,NULL,NULL,NULL,NULL,NULL,NULL,NULL,32,NULL,NULL,NULL,NULL,'bonus2 bSubSize,Size_All,15; autobonus2 "{ bonus bFlee2,30; }",3,10000,BF_WEAPON|BF_MAGIC,"{ specialeffect2 EF_WIND; }";',NULL,NULL);
 REPLACE INTO `item_db_re` VALUES (4414,'Seeker_Card','Seeker Card',6,20,NULL,10,NULL,NULL,NULL,NULL,NULL,NULL,NULL,32,NULL,NULL,NULL,NULL,'skill "MG_STONECURSE",1; bonus2 bResEff,Eff_Stone,3000; bonus bMdef,10;',NULL,NULL);
-REPLACE INTO `item_db_re` VALUES (4415,'Snowier_Card','Snowier Card',6,20,NULL,10,NULL,NULL,NULL,NULL,NULL,NULL,NULL,136,NULL,NULL,NULL,NULL,'bonus2 bAddMonsterDropItem,536,2000; bonus2 bAddItemHealRate,536,500;',NULL,NULL);
+REPLACE INTO `item_db_re` VALUES (4415,'Snowier_Card','Snowier Card',6,20,NULL,10,NULL,NULL,NULL,NULL,NULL,NULL,NULL,136,NULL,NULL,NULL,NULL,'bonus2 bAddMonsterDropItem,536,500; bonus2 bAddItemHealRate,536,100;',NULL,NULL);
 REPLACE INTO `item_db_re` VALUES (4416,'Siroma_Card','Siroma Card',6,20,NULL,10,NULL,NULL,NULL,NULL,NULL,NULL,NULL,136,NULL,NULL,NULL,NULL,'bonus2 bSkillAtk,"MG_COLDBOLT",25; bonus2 bVariableCastrate,"MG_COLDBOLT",-25;',NULL,NULL);
 REPLACE INTO `item_db_re` VALUES (4417,'Ice_Titan_Card','Ice Titan Card',6,20,NULL,10,NULL,NULL,NULL,NULL,NULL,NULL,NULL,64,NULL,NULL,NULL,NULL,'bonus bVit,2; autobonus2 "{ bonus bDef,10; }",3,10000,BF_WEAPON|BF_MAGIC,"{ specialeffect2 EF_FREEZED; }";',NULL,NULL);
 REPLACE INTO `item_db_re` VALUES (4418,'Gazeti_Card','Gazeti Card',6,20,NULL,10,NULL,NULL,NULL,NULL,NULL,NULL,NULL,136,NULL,NULL,NULL,NULL,'bonus3 bAutoSpell,"MG_COLDBOLT",2,100;',NULL,NULL);
@@ -2531,9 +2531,9 @@ REPLACE INTO `item_db_re` VALUES (4455,'Light_Up_Card2','Light Up Card',6,20,NUL
 REPLACE INTO `item_db_re` VALUES (4456,'Nidhogg_Shadow_Card','Nidhoggur Shadow Card',6,20,NULL,10,NULL,NULL,NULL,NULL,NULL,NULL,NULL,16,NULL,NULL,NULL,NULL,'bonus bInt,5; if (Class == Job_High_Wizard || Class == Job_Baby_Warlock || Class == Job_Warlock || Class == Job_Warlock_T) bonus bFixedCastrate,-50;',NULL,NULL);
 REPLACE INTO `item_db_re` VALUES (4457,'Nahtzigger_Card','Naght Sieger Card',6,20,NULL,10,NULL,NULL,NULL,NULL,NULL,NULL,NULL,16,NULL,NULL,NULL,NULL,'bonus2 bMagicAtkEle,Ele_Ghost,30;',NULL,NULL);
 REPLACE INTO `item_db_re` VALUES (4458,'Duneirre_Card','Duneyrr Card',6,20,NULL,10,NULL,NULL,NULL,NULL,NULL,NULL,NULL,769,NULL,NULL,NULL,NULL,'bonus bBaseAtk,10; autobonus "{ bonus bAspdRate,5; }",10,10000,0,"{ specialeffect2 EF_HASTEUP; }";',NULL,NULL);
-REPLACE INTO `item_db_re` VALUES (4459,'Lata_Card','Rata Card',6,20,NULL,10,NULL,NULL,NULL,NULL,NULL,NULL,NULL,769,NULL,NULL,NULL,NULL,'bonus bMatk,10; autobonus "{ bonus bFixedCastrate,-50; }",10,4000,BF_MAGIC,"{ specialeffect2 EF_SUFFRAGIUM; }";',NULL,NULL);
-REPLACE INTO `item_db_re` VALUES (4460,'Ringco_Card','Rhyncho Card',6,20,NULL,10,NULL,NULL,NULL,NULL,NULL,NULL,NULL,769,NULL,NULL,NULL,NULL,'bonus bHealPower,4; bonus bUseSPrate,5;',NULL,NULL);
-REPLACE INTO `item_db_re` VALUES (4461,'Pillar_Card','Phylla Card',6,20,NULL,10,NULL,NULL,NULL,NULL,NULL,NULL,NULL,769,NULL,NULL,NULL,NULL,'bonus bDex,1; bonus bAgi,1; autobonus "{ bonus bCritical,20; }",4,4000,0,"{ specialeffect2 EF_ENHANCE; }";',NULL,NULL);
+REPLACE INTO `item_db_re` VALUES (4459,'Lata_Card','Rata Card',6,20,NULL,10,NULL,NULL,NULL,NULL,NULL,NULL,NULL,769,NULL,NULL,NULL,NULL,'bonus bMatk,10; autobonus "{ bonus bFixedCastrate,-50; }",5,4000,BF_MAGIC,"{ specialeffect2 EF_SUFFRAGIUM; }";',NULL,NULL);
+REPLACE INTO `item_db_re` VALUES (4460,'Ringco_Card','Rhyncho Card',6,20,NULL,10,NULL,NULL,NULL,NULL,NULL,NULL,NULL,769,NULL,NULL,NULL,NULL,'bonus bHealPower,4; bonus2 bSkillUseSP,"AL_HEAL",-15;',NULL,NULL);
+REPLACE INTO `item_db_re` VALUES (4461,'Pillar_Card','Phylla Card',6,20,NULL,10,NULL,NULL,NULL,NULL,NULL,NULL,NULL,769,NULL,NULL,NULL,NULL,'bonus bDex,1; bonus bAgi,1; autobonus "{ bonus bCritical,20; }",15,4000,0,"{ specialeffect2 EF_ENHANCE; }";',NULL,NULL);
 REPLACE INTO `item_db_re` VALUES (4462,'Hardrock_Mammos_Card','Hardrock Mammoth Card',6,20,NULL,10,NULL,NULL,NULL,NULL,NULL,NULL,NULL,16,NULL,NULL,NULL,NULL,'bonus bDef,5; if(getrefine()>=12) { bonus bDef,20; bonus bMaxHPrate,10; } if(getrefine()>=14) { bonus bMaxHPrate,3; }',NULL,NULL);
 REPLACE INTO `item_db_re` VALUES (4463,'Tendrilrion_Card','Tendrilrion Card',6,20,NULL,10,NULL,NULL,NULL,NULL,NULL,NULL,NULL,2,NULL,NULL,NULL,NULL,'bonus bCritical,5; if(getrefine()>=12) { bonus bBaseAtk,35; } if(getrefine()>=14) { bonus bCritical,10; }',NULL,NULL);
 REPLACE INTO `item_db_re` VALUES (4464,'Aunoe_Card','Aunoe Card',6,20,NULL,10,NULL,NULL,NULL,NULL,NULL,NULL,NULL,2,NULL,NULL,NULL,NULL,'bonus bCritAtkRate,20;',NULL,NULL);
@@ -4543,6 +4543,7 @@ REPLACE INTO `item_db_re` VALUES (6862,'Piece_Of_Soul_Tiger','Piece Of Soul Tige
 REPLACE INTO `item_db_re` VALUES (6863,'Strong_Piece_Of_Soul_Tiger','Strong Piece Of Soul Tiger',3,10,NULL,0,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);
 REPLACE INTO `item_db_re` VALUES (6864,'Safe_to_19_Weapon_Certificate','Safe to 19 Weapon Certificate',3,10,NULL,0,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);
 REPLACE INTO `item_db_re` VALUES (6865,'Safe_to_19_Armor_Certificate','Safe to 19 Armor Certificate',3,10,NULL,0,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);
+REPLACE INTO `item_db_re` VALUES (6866,'Piece_Of_Soul_Rabbit','Piece Of Soul Rabbit',3,0,NULL,0,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);
 REPLACE INTO `item_db_re` VALUES (6885,'Piece_Of_Soul_Dragon','Piece Of Soul Dragon',3,10,NULL,0,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);
 REPLACE INTO `item_db_re` VALUES (6870,'Safe_to_13_Weapon_Certificate','Safe to 13 Weapon Certificate',3,10,NULL,0,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);
 REPLACE INTO `item_db_re` VALUES (6871,'Safe_to_14_Weapon_Certificate','Safe to 14 Weapon Certificate',3,10,NULL,0,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);
@@ -6220,7 +6221,7 @@ REPLACE INTO `item_db_re` VALUES (12420,'Stamina_Up_M','Stamina Up M',2,100,NULL
 REPLACE INTO `item_db_re` VALUES (12421,'Falmons_F','Falmons F',3,10,NULL,10,NULL,NULL,NULL,NULL,0xFFFFFFFF,63,2,NULL,NULL,NULL,NULL,NULL,'sc_start SC_LIFE_FORCE_F,500000,5;',NULL,NULL);
 REPLACE INTO `item_db_re` VALUES (12422,'HP_Increase_Potion_(Small)','HP Increase Potion (Small)',0,10,NULL,10,NULL,NULL,NULL,NULL,0xFFFFFFFF,63,2,NULL,NULL,NULL,NULL,NULL,'sc_start SC_INCMHP,500000,(500+(BaseLevel*10/3)); percentheal 1,0;',NULL,NULL);
 REPLACE INTO `item_db_re` VALUES (12423,'HP_Increase_Potion_(Medium)','HP Increase Potion (Medium)',0,10,NULL,10,NULL,NULL,NULL,NULL,0xFFFFFFFF,63,2,NULL,NULL,NULL,NULL,NULL,'sc_start SC_INCMHP,500000,(1500+(BaseLevel*10/3)); percentheal 2,0;',NULL,NULL);
-REPLACE INTO `item_db_re` VALUES (12424,'HP_Increase_Potion_(Large)','HP Increase Potion (Large)',0,10,NULL,10,NULL,NULL,NULL,NULL,0xFFFFFFFF,63,2,NULL,NULL,NULL,NULL,NULL,'sc_start SC_INCMHP,500000,(2500+(BaseLevel*10/3)); percentheal 5,0;',NULL,NULL);
+REPLACE INTO `item_db_re` VALUES (12424,'HP_Increase_Potion_(Large)','HP Increase Potion (Large)',0,10,NULL,80,NULL,NULL,NULL,NULL,0xFFFFFFFF,63,2,NULL,NULL,NULL,NULL,NULL,'sc_start SC_INCMHP,500000,(2500+(BaseLevel*10/3)); percentheal 5,0;',NULL,NULL);
 REPLACE INTO `item_db_re` VALUES (12425,'SP_Increase_Potion_(Small)','SP Increase Potion (Small)',0,10,NULL,10,NULL,NULL,NULL,NULL,0xFFFFFFFF,63,2,NULL,NULL,NULL,NULL,NULL,'sc_start SC_INCMSPRATE,500000,((BaseLevel/10)-5); percentheal 0,2;',NULL,NULL);
 REPLACE INTO `item_db_re` VALUES (12426,'SP_Increase_Potion_(Medium)','SP Increase Potion (Medium)',0,10,NULL,10,NULL,NULL,NULL,NULL,0xFFFFFFFF,63,2,NULL,NULL,NULL,NULL,NULL,'sc_start SC_INCMSPRATE,500000,(BaseLevel/10); percentheal 0,4;',NULL,NULL);
 REPLACE INTO `item_db_re` VALUES (12427,'SP_Increase_Potion_(Large)','SP Increase Potion (Large)',0,10,NULL,10,NULL,NULL,NULL,NULL,0xFFFFFFFF,63,2,NULL,NULL,NULL,NULL,NULL,'sc_start SC_INCMSPRATE,500000,((BaseLevel/10)+5); percentheal 0,8;',NULL,NULL);
@@ -8219,7 +8220,7 @@ REPLACE INTO `item_db_re` VALUES (15117,'Tarlock\'s_Armor','Tarlock\'s Armor',4,
 REPLACE INTO `item_db_re` VALUES (15121,'Sarah_Combat_Robe','Sarah Combat Robe',4,10,NULL,800,NULL,35,NULL,1,0xFFFFFFFF,63,2,16,NULL,'145',1,NULL,'/*TODO: Confirm the real rate and additional MAtk*/ autobonus "{ bonus bMatk,20; }",100,(10+((getrefine()) ? getrefine()*8 : 0))*1000,BF_MAGIC;',NULL,NULL);
 
 # More Maces
-REPLACE INTO `item_db_re` VALUES (16000,'Erde','Erde',5,20,NULL,500,'130',NULL,1,2,0x0004C5B2,18,2,2,4,'50',1,8,'bonus2 bSkillAtk,"AM_ACIDTERROR",20; bonus2 bSkillAtk,"AM_DEMONSTRATION",20; bonus bMaxSP,50; bonus bHealPower,10;',NULL,NULL);
+REPLACE INTO `item_db_re` VALUES (16000,'Erde','Erde',5,20,NULL,500,'130',NULL,1,2,0x0004C5B2,18,2,2,3,'50',1,8,'bonus2 bSkillAtk,"AM_ACIDTERROR",20; bonus2 bSkillAtk,"AM_DEMONSTRATION",20; bonus bMaxSP,50; bonus bHealPower,10;',NULL,NULL);
 REPLACE INTO `item_db_re` VALUES (16001,'Red_Square_Bag','Red Square Bag',5,20,NULL,500,'130',NULL,1,2,0x0004C5B2,18,2,2,3,'50',1,8,'bonus bMaxHP,200; bonus2 bSkillAtk,"AM_ACIDTERROR",20; bonus2 bSkillAtk,"AM_DEMONSTRATION",20; bonus2 bAddMonsterDropItem,501,50; bonus2 bAddMonsterDropItem,502,20; bonus2 bAddMonsterDropItem,503,20; bonus2 bAddMonsterDropItem,504,20; bonus2 bAddMonsterDropItem,505,10; if(readparam(bStr)>=95) bonus2 bAddEff,Eff_Stun,500;',NULL,NULL);
 REPLACE INTO `item_db_re` VALUES (16002,'Stunner_C','Stunner',5,0,NULL,0,'175',NULL,1,0,0x00008110,63,2,2,3,'1',0,8,'bonus2 bAddEff,Eff_Stun,1000; bonus2 bAddSize,Size_All,40;',NULL,NULL);
 REPLACE INTO `item_db_re` VALUES (16003,'Carga_Mace','Carga Mace',5,20,NULL,1500,'175',NULL,1,2,0x0004C5B2,63,2,2,3,'100',1,8,NULL,NULL,NULL);
@@ -9342,7 +9343,10 @@ REPLACE INTO `item_db_re` VALUES (19589,'C_Fallen_Angel_Lost_J','Costume Fallen
 REPLACE INTO `item_db_re` VALUES (19590,'C_Twin_Maiden_Ribbon_J','Maiden\'s Twin Ribbon',4,20,NULL,0,NULL,0,NULL,0,0xFFFFFFFF,63,2,1024,NULL,'0',0,239,NULL,NULL,NULL);
 REPLACE INTO `item_db_re` VALUES (19598,'C_Wandering_Wolf_Hat','Costume Wandering Wolf Hat',4,20,NULL,600,NULL,5,NULL,0,0xFFFFFFFE,63,2,768,NULL,'20',0,490,'bonus bVit,5; bonus bFlee,10;',NULL,NULL);
 REPLACE INTO `item_db_re` VALUES (19599,'C_Imp_Hat','Costume Imp Hat',4,20,NULL,400,NULL,1,NULL,0,0xFFFFFFFF,63,2,256,NULL,'1',1,589,'bonus3 bAutoSpell,"SA_FLAMELAUNCHER",1,5;',NULL,NULL);
+REPLACE INTO `item_db_re` VALUES (19600,'C_Drooping_Kiehl','Costume Drooping Kiehl',12,0,NULL,40,NULL,NULL,NULL,NULL,0xFFFFFFFF,63,2,1024,NULL,'30',NULL,909,NULL,NULL,NULL);
+REPLACE INTO `item_db_re` VALUES (19601,'C_Drooping_Aliot','Costume Drooping Aliot',12,0,NULL,10,NULL,NULL,NULL,NULL,0xFFFFFFFF,63,2,1024,NULL,'1',NULL,910,NULL,NULL,NULL);
 REPLACE INTO `item_db_re` VALUES (19603,'C_Invisible_Sunglasses','Costume Invisible Sunglasses',4,0,NULL,0,NULL,0,NULL,0,0xFFFFFFFF,63,2,2048,NULL,'1',0,0,NULL,NULL,NULL);
+REPLACE INTO `item_db_re` VALUES (19604,'Brawler\'s_Supply_Crate','Brawler\'s Supply Crate',18,0,NULL,0,NULL,NULL,NULL,NULL,0xFFFFFFFF,63,2,NULL,NULL,NULL,NULL,NULL,'getitem(12208,4); getitem(12072,6); getitem(12082,6); getitem(12087,6); getitem(11502,30);',NULL,NULL);
 REPLACE INTO `item_db_re` VALUES (19607,'C_Love_Chick_Hat','Costume Love Chick Hat',4,0,NULL,0,NULL,0,NULL,0,0xFFFFFFFF,63,2,1024,NULL,'1',0,500,NULL,NULL,NULL);
 REPLACE INTO `item_db_re` VALUES (19613,'C_Valkyrie_Feather_Hat','Costume Valkyrie Feather Hat',4,10,NULL,0,NULL,NULL,NULL,0,0xFFFFFFFF,63,2,1024,NULL,'1',1,300,'bonus bAllStats,1; skill "PR_MAGNUS",5; skill "SA_FLAMELAUNCHER",1; skill "SA_FROSTWEAPON",1; skill "SA_LIGHTNINGLOADER",1; skill "SA_SEISMICWEAPON",1;',NULL,NULL);
 REPLACE INTO `item_db_re` VALUES (19617,'C_Puppy_Headband','Costume Puppy Headband',4,0,NULL,0,NULL,NULL,NULL,NULL,0xFFFFFFFF,63,2,1024,NULL,NULL,NULL,199,NULL,NULL,NULL);
@@ -9442,6 +9446,8 @@ REPLACE INTO `item_db_re` VALUES (19799,'C_Golden_Gear','Costume Golden Gear',4,
 REPLACE INTO `item_db_re` VALUES (19800,'C_Carnation_Hairband','Costume Carnation Headband',4,10,NULL,0,NULL,NULL,NULL,0,0xFFFFFFFF,63,2,1024,NULL,'1',1,878,NULL,NULL,NULL);
 #
 REPLACE INTO `item_db_re` VALUES (19821,'C_Hyegun_hat','C Yao Jun',4,20,NULL,10,NULL,0,NULL,0,0xFFFFFFFF,63,2,1024,NULL,'1',0,375,NULL,NULL,NULL);
+REPLACE INTO `item_db_re` VALUES (19824,'C_Evil_Druid_Hat','Costume Evil Druid Hat',12,0,NULL,0,NULL,NULL,NULL,NULL,0xFFFFFFFF,63,2,1024,NULL,'1',NULL,1053,NULL,NULL,NULL);
+REPLACE INTO `item_db_re` VALUES (19825,'C_Vicious_Stop_Bandage','Costume Vicious Stop Bandage',12,0,NULL,0,NULL,NULL,NULL,NULL,0xFFFFFFFF,63,2,1024,NULL,'1',NULL,1054,NULL,NULL,NULL);
 REPLACE INTO `item_db_re` VALUES (19826,'C_Ice_Wing_Ear','C Ice Wing Ear',4,20,NULL,10,NULL,0,NULL,0,0xFFFFFFFF,63,2,2048,NULL,'1',0,584,NULL,NULL,NULL);
 REPLACE INTO `item_db_re` VALUES (19829,'C_Straw_Hat','C Straw Hat',4,20,NULL,0,NULL,0,NULL,0,0xFFFFFFFF,63,2,1024,NULL,'0',0,146,NULL,NULL,NULL);
 REPLACE INTO `item_db_re` VALUES (19830,'C_Sunglasses','Costume Sunglasses',4,10,NULL,0,NULL,NULL,NULL,NULL,0xFFFFFFFF,63,2,2048,NULL,'1',NULL,12,NULL,NULL,NULL);
@@ -9476,6 +9482,8 @@ REPLACE INTO `item_db_re` VALUES (19863,'C_Incubus_Horn','Incubus Horn',4,20,NUL
 REPLACE INTO `item_db_re` VALUES (19864,'C_Dokebi\'s_Wig','Dokebi\'s Wig',4,20,NULL,0,NULL,0,NULL,0,0xFFFFFFFF,63,2,3072,NULL,'0',0,302,NULL,NULL,NULL);
 REPLACE INTO `item_db_re` VALUES (19865,'C_Joker_Jester','Joker Jester',4,20,NULL,0,NULL,0,NULL,0,0xFFFFFFFF,63,2,1024,NULL,'0',0,89,NULL,NULL,NULL);
 REPLACE INTO `item_db_re` VALUES (19871,'C_DecorationOfMusic_Accessory','Decoration of Music',4,20,NULL,0,NULL,0,NULL,0,0xFFFFFFFF,63,2,2048,NULL,'0',0,1074,'sc_start SC_DECORATION_OF_MUSIC,-1,0;',NULL,'sc_end SC_DECORATION_OF_MUSIC;');
+REPLACE INTO `item_db_re` VALUES (19878,'C_Evolved_Drooping_Bunny','Costume Evolved Drooping Bunny',12,0,NULL,0,NULL,NULL,NULL,NULL,0xFFFFFFFF,63,2,1024,NULL,'1',NULL,249,NULL,NULL,NULL);
+REPLACE INTO `item_db_re` VALUES (19883,'C_Cyclops_Glasses','Costume Cyclops Glasses',12,0,NULL,0,NULL,NULL,NULL,NULL,0xFFFFFFFF,63,2,2048,NULL,'1',NULL,1087,NULL,NULL,NULL);
 REPLACE INTO `item_db_re` VALUES (19884,'C_Vanargand_Helm','Costume Vanargand Helm',4,10,NULL,0,NULL,NULL,NULL,NULL,0xFFFFFFFF,63,2,1024,NULL,'1',NULL,804,NULL,NULL,NULL);
 REPLACE INTO `item_db_re` VALUES (19885,'C_Blinker','Costume Blinker',4,10,NULL,0,NULL,NULL,NULL,NULL,0xFFFFFFFF,63,2,2048,NULL,'1',NULL,82,NULL,NULL,NULL);
 REPLACE INTO `item_db_re` VALUES (19886,'C_Purple_Glasses','Costume Purple Glasses',4,10,NULL,0,NULL,NULL,NULL,NULL,0xFFFFFFFF,63,2,2048,NULL,'1',NULL,26,NULL,NULL,NULL);
@@ -9492,6 +9500,7 @@ REPLACE INTO `item_db_re` VALUES (19920,'C_Adv_Whisper_Mask','Costume Evolved Wh
 REPLACE INTO `item_db_re` VALUES (19922,'Costume_Noahs_Hat','Costume Noahs Hat',4,0,NULL,0,NULL,0,NULL,0,0xFFFFFFFF,63,2,7168,NULL,'1',0,636,'/* TODO */',NULL,NULL);
 REPLACE INTO `item_db_re` VALUES (19929,'C_Classical_Ribbon','Costume Classical Ribbon',4,10,NULL,0,NULL,NULL,NULL,0,0xFFFFFFFF,63,2,1024,NULL,NULL,1,NULL,'/*TODO: View ID*/',NULL,NULL);
 REPLACE INTO `item_db_re` VALUES (19934,'C_Lolita_Ten_Gallon_Hat','Costume Alive Ten Gallon Hat Of Flame',4,10,NULL,0,NULL,NULL,NULL,0,0xFFFFFFFF,63,2,1024,NULL,'1',1,1048,NULL,NULL,NULL);
+REPLACE INTO `item_db_re` VALUES (19935,'C_Hunting_Cap_Of_Gust','Costume Hunting Cap Of Gust',12,0,NULL,0,NULL,NULL,NULL,NULL,0xFFFFFFFF,63,2,1024,NULL,'1',NULL,1076,NULL,NULL,NULL);
 REPLACE INTO `item_db_re` VALUES (19938,'Costume_Love_Rabbit_Hood','Costume Love Rabbit Hood',4,0,NULL,0,NULL,0,NULL,0,0xFFFFFFFF,63,2,7168,NULL,'1',0,549,NULL,NULL,NULL);
 REPLACE INTO `item_db_re` VALUES (19961,'C_Rune_Circlet','Costume Rune Circlet',4,10,NULL,0,NULL,NULL,NULL,0,0xFFFFFFFF,63,2,1024,NULL,'1',1,623,NULL,NULL,NULL);
 REPLACE INTO `item_db_re` VALUES (19962,'C_Mitra','Costume Mitra',4,10,NULL,0,NULL,NULL,NULL,0,0xFFFFFFFF,63,2,1024,NULL,'1',1,624,NULL,NULL,NULL);
@@ -9530,9 +9539,14 @@ REPLACE INTO `item_db_re` VALUES (20027,'C_Cow_Hat3','Costume Cow Hat 3',4,10,NU
 REPLACE INTO `item_db_re` VALUES (20028,'C_Cow_Hat4','Costume Cow Hat 4',4,10,NULL,0,NULL,NULL,NULL,0,0xFFFFFFFF,63,2,1024,NULL,'1',1,1214,'bonus bDex,2; bonus2 bAddMonsterDropItem,519,100;',NULL,NULL);
 REPLACE INTO `item_db_re` VALUES (20034,'C_Jack_Castle_Bat','Costume Jack Castle Bat',4,10,NULL,0,NULL,NULL,NULL,0,0xFFFFFFFF,63,2,1024,NULL,'1',1,1041,NULL,NULL,NULL);
 REPLACE INTO `item_db_re` VALUES (20038,'C_Alphonse_Helmet','Costume Alphonse Helmet',4,10,NULL,0,NULL,NULL,NULL,0,0xFFFFFFFF,63,2,1024,NULL,NULL,1,NULL,'/*TODO: View ID*/',NULL,NULL);
+REPLACE INTO `item_db_re` VALUES (20049,'C_Giant_Band_Aid','Costume Giant Band Aid',12,0,NULL,0,NULL,NULL,NULL,NULL,0xFFFFFFFF,63,2,1024,NULL,'1',NULL,147,NULL,NULL,NULL);
 REPLACE INTO `item_db_re` VALUES (20053,'C_W_King_Tiger_Doll_Hat','Costume White King Tiger Doll Hat',4,10,NULL,0,NULL,NULL,NULL,0,0xFFFFFFFF,63,2,1024,NULL,'1',1,973,NULL,NULL,NULL);
 REPLACE INTO `item_db_re` VALUES (20063,'C_Yellow_Brain_Hat','Costume Yellow Brain Hat',4,10,NULL,0,NULL,NULL,NULL,0,0xFFFFFFFF,63,2,1024,NULL,'1',1,1228,NULL,NULL,NULL);
 REPLACE INTO `item_db_re` VALUES (20064,'C_Blue_Brain_Hat','Costume Blue Brain Hat',4,10,NULL,0,NULL,NULL,NULL,0,0xFFFFFFFF,63,2,1024,NULL,'1',1,1228,NULL,NULL,NULL);
+REPLACE INTO `item_db_re` VALUES (20067,'C_White_Rabbit_Headband','Costume White Rabbit Headband',12,0,NULL,0,NULL,NULL,NULL,NULL,0xFFFFFFFF,63,2,1024,NULL,'1',NULL,719,NULL,NULL,NULL);
+REPLACE INTO `item_db_re` VALUES (20068,'C_Black_Rabbit_Headband','Costume Black Rabbit Headband',12,0,NULL,0,NULL,NULL,NULL,NULL,0xFFFFFFFF,63,2,1024,NULL,'1',NULL,718,NULL,NULL,NULL);
+REPLACE INTO `item_db_re` VALUES (20073,'C_Hair_Band','Costume Hair Band',12,0,NULL,0,NULL,NULL,NULL,NULL,0xFFFFFFFF,63,2,1024,NULL,'1',NULL,9,NULL,NULL,NULL);
+REPLACE INTO `item_db_re` VALUES (20074,'C_Biretta','Costume_Biretta',12,0,NULL,0,NULL,NULL,NULL,NULL,0xFFFFFFFF,63,2,1024,NULL,'1',NULL,11,NULL,NULL,NULL);
 REPLACE INTO `item_db_re` VALUES (20086,'C_Dragon_Cintamani_Hat1','Costume Dragon Cintamani Hat',4,0,NULL,0,NULL,NULL,NULL,0,0xFFFFFFFF,63,2,1024,NULL,'1',1,1245,NULL,NULL,NULL);
 REPLACE INTO `item_db_re` VALUES (20087,'C_Dragon_Cintamani_Hat2','Costume Dragon Cintamani Hat',4,0,NULL,0,NULL,NULL,NULL,0,0xFFFFFFFF,63,2,1024,NULL,'1',1,1246,NULL,NULL,NULL);
 REPLACE INTO `item_db_re` VALUES (20088,'C_Dragon_Cintamani_Hat3','Costume Dragon Cintamani Hat',4,0,NULL,0,NULL,NULL,NULL,0,0xFFFFFFFF,63,2,1024,NULL,'1',1,1247,NULL,NULL,NULL);

+ 1 - 1
sql-files/logs.sql

@@ -32,7 +32,7 @@ CREATE TABLE IF NOT EXISTS `zenylog` (
   `time` datetime NOT NULL default '0000-00-00 00:00:00',
   `char_id` int(11) NOT NULL default '0',
   `src_id` int(11) NOT NULL default '0',
-  `type` enum('T','V','P','M','S','N','D','C','A','E','I','B') NOT NULL default 'S',
+  `type` enum('T','V','P','M','S','N','D','C','A','E','I','B','K') NOT NULL default 'S',
   `amount` int(11) NOT NULL default '0',
   `map` varchar(11) NOT NULL default '',
   PRIMARY KEY  (`id`),

+ 6 - 0
sql-files/main.sql

@@ -731,6 +731,9 @@ CREATE TABLE IF NOT EXISTS `vendings` (
   `x` smallint(5) unsigned NOT NULL,
   `y` smallint(5) unsigned NOT NULL,
   `title` varchar(80) NOT NULL,
+  `body_direction` CHAR( 1 ) NOT NULL DEFAULT '4',
+  `head_direction` CHAR( 1 ) NOT NULL DEFAULT '0',
+  `sit` CHAR( 1 ) NOT NULL DEFAULT '1',
   `autotrade` tinyint(4) NOT NULL,
   PRIMARY KEY (`id`)
 ) ENGINE=MyISAM;
@@ -753,6 +756,9 @@ CREATE TABLE IF NOT EXISTS `buyingstores` (
   `y` smallint(5) unsigned NOT NULL,
   `title` varchar(80) NOT NULL,
   `limit` int(10) unsigned NOT NULL,
+  `body_direction` CHAR( 1 ) NOT NULL DEFAULT '4',
+  `head_direction` CHAR( 1 ) NOT NULL DEFAULT '0',
+  `sit` CHAR( 1 ) NOT NULL DEFAULT '1',
   `autotrade` tinyint(4) NOT NULL,
   PRIMARY KEY (`id`)
 ) ENGINE=MyISAM;

+ 1 - 0
sql-files/upgrades/upgrade_20140713.sql

@@ -0,0 +1 @@
+ALTER TABLE `zenylog` CHANGE `type` `type` ENUM('T','V','P','M','S','N','D','C','A','E','I','B','K') NOT NULL DEFAULT 'S';

+ 7 - 0
sql-files/upgrades/upgrade_20140723.sql

@@ -0,0 +1,7 @@
+ALTER TABLE `vendings` ADD `body_direction` CHAR( 1 ) NOT NULL DEFAULT '4',
+ADD `head_direction` CHAR( 1 ) NOT NULL DEFAULT '0',
+ADD `sit` CHAR( 1 ) NOT NULL DEFAULT '1';
+
+ALTER TABLE `buyingstores` ADD `body_direction` CHAR( 1 ) NOT NULL DEFAULT '4',
+ADD `head_direction` CHAR( 1 ) NOT NULL DEFAULT '0',
+ADD `sit` CHAR( 1 ) NOT NULL DEFAULT '1';

+ 3 - 35
src/char/char.c

@@ -1968,43 +1968,11 @@ int char_lan_subnetcheck(uint32 ip){
 }
 
 // Console Command Parser [Wizputer]
-int parse_console(const char* buf)
-{
-	char type[64];
-	char command[64];
-	int n=0;
-
-	if( ( n = sscanf(buf, "%63[^:]:%63[^\n]", type, command) ) < 2 ){
-		if((n = sscanf(buf, "%63[^\n]", type))<1) return -1; //nothing to do no arg
-	}
-	if( n != 2 ){ //end string
-		ShowNotice("Type: '%s'\n",type);
-		command[0] = '\0';
-	}
-	else
-		ShowNotice("Type of command: '%s' || Command: '%s'\n",type,command);
-
-	if( n == 2 && strcmpi("server", type) == 0 ){
-		if( strcmpi("shutdown", command) == 0 || strcmpi("exit", command) == 0 || strcmpi("quit", command) == 0 ){
-			runflag = CHARSERVER_ST_SHUTDOWN;
-		}
-		else if( strcmpi("alive", command) == 0 || strcmpi("status", command) == 0 )
-			ShowInfo(CL_CYAN"Console: "CL_BOLD"I'm Alive."CL_RESET"\n");
-	}
-	else if( strcmpi("ers_report", type) == 0 ){
-		ers_report();
-	}
-	else if( strcmpi("help", type) == 0 ){
-		ShowInfo("Available commands:\n");
-		ShowInfo("\t server:shutdown => Stops the server.\n");
-		ShowInfo("\t server:alive => Checks if the server is running.\n");
-		ShowInfo("\t ers_report => Displays database usage.\n");
-	}
-
-	return 0;
+//FIXME to be remove (moved to cnslif / will be done once map/char/login, all have their cnslif interface ready)
+int parse_console(const char* buf){
+	return cnslif_parse(buf);
 }
 
-
 //------------------------------------------------
 //Pincode system
 //------------------------------------------------

+ 9 - 0
src/char/char_clif.c

@@ -337,6 +337,15 @@ void chclif_char_delete2_ack(int fd, int char_id, uint32 result, time_t delete_d
 /// Any (0x718): An unknown error has occurred.
 /// HC: <082a>.W <char id>.L <Msg:0-5>.L
 void chclif_char_delete2_accept_ack(int fd, int char_id, uint32 result) {
+	if(result == 1 ){
+		struct char_session_data* sd;
+		sd = (struct char_session_data*)session[fd]->session_data;
+
+		if( sd->version >= date2version(20130000) ){
+			chclif_mmo_char_send(fd, sd);
+		}
+	}
+
 	WFIFOHEAD(fd,10);
 	WFIFOW(fd,0) = 0x82a;
 	WFIFOL(fd,2) = char_id;

+ 2 - 2
src/char/char_mapif.c

@@ -348,7 +348,7 @@ int chmapif_parse_regmapuser(int fd, int id){
  */
 int chmapif_parse_reqsavechar(int fd, int id){
 	if (RFIFOREST(fd) < 4 || RFIFOREST(fd) < RFIFOW(fd,2))
-            return 0;
+		return 0;
 	{
 		int aid = RFIFOL(fd,4), cid = RFIFOL(fd,8), size = RFIFOW(fd,2);
 		struct online_char_data* character;
@@ -449,7 +449,7 @@ int chmapif_parse_authok(int fd){
 //Request to save skill cooldown data
 int chmapif_parse_req_saveskillcooldown(int fd){
 	if( RFIFOREST(fd) < 4 || RFIFOREST(fd) < RFIFOW(fd,2) )
-            return 0;
+		return 0;
 	else {
 		int count, aid, cid;
 		aid = RFIFOL(fd,4);

+ 1 - 1
src/common/mmo.h

@@ -60,7 +60,7 @@
 #define ACCOUNT_REG2_NUM 16 ///Max permanent global account variables per account
 #define MAX_REG_NUM 256 ///Should hold the max of GLOBAL/ACCOUNT/ACCOUNT2 (needed for some arrays that hold all three)
 #define DEFAULT_WALK_SPEED 150 ///Default walk speed
-#define MIN_WALK_SPEED 0 ///Min walk speed
+#define MIN_WALK_SPEED 20 ///Min walk speed
 #define MAX_WALK_SPEED 1000 ///Max walk speed
 #define MAX_STORAGE 600 ///Max number of storage slots a player can have, (up to ~850 tested)
 #define MAX_GUILD_STORAGE 600 ///Max number of storage slots a guild

+ 7 - 1
src/common/utils.h

@@ -14,9 +14,15 @@ void ShowDump(const void* buffer, size_t length);
 void findfile(const char *p, const char *pat, void (func)(const char*));
 bool exists(const char* filename);
 
-//Caps values to min/max
+/// Caps values to min/max
 #define cap_value(a, min, max) ((a >= max) ? max : (a <= min) ? min : a)
 
+/// Apply rate for val, divided by 100)
+#define apply_rate(val, rate) (((rate) == 100) ? (val) : ((val) > 100000) ? (val) / 100 * (rate) : (val) * (rate) / 100)
+
+/// Apply rate for val, divided by per
+#define apply_rate2(val, rate, per) (((rate) == (per)) ? (val) : ((val) > 100000) ? (val) / (per) * (rate) : (val) * (rate) / (per))
+
 /// calculates the value of A / B, in percent (rounded down)
 unsigned int get_percentage(const unsigned int A, const unsigned int B);
 

+ 3 - 0
src/config/const.h

@@ -89,6 +89,9 @@
 /* Feb 1st 2012 */
 #if PACKETVER >= 20120201
 	#define NEW_CARTS
+	#ifndef ENABLE_SC_SAVING
+	 #warning "Cart won't be able to be saved for relog"
+	#endif
 	#define MAX_CARTS 9
 #else
 	#define MAX_CARTS 5

+ 115 - 53
src/login/account_sql.c → src/login/account.c

@@ -1,5 +1,11 @@
-// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
-// For more information, see LICENCE in the main folder
+/**
+ * @file account.c
+ * Module purpose is to save, load, and update changes into the account table or file.
+ * Licensed under GNU GPL.
+ *  For more information, see LICENCE in the main folder.
+ * @author Athena Dev Teams < r15k
+ * @author rAthena Dev Team
+ */
 
 #include "../common/malloc.h"
 #include "../common/mmo.h"
@@ -16,8 +22,7 @@
 #define ACCOUNT_SQL_DB_VERSION 20110114
 
 /// internal structure
-typedef struct AccountDB_SQL
-{
+typedef struct AccountDB_SQL {
 	AccountDB vtable;    // public interface
 
 	Sql* accounts;       // SQL accounts storage
@@ -44,10 +49,8 @@ typedef struct AccountDB_SQL
 } AccountDB_SQL;
 
 /// internal structure
-typedef struct AccountDBIterator_SQL
-{
+typedef struct AccountDBIterator_SQL {
 	AccountDBIterator vtable;    // public interface
-
 	AccountDB_SQL* db;
 	int last_account_id;
 } AccountDBIterator_SQL;
@@ -70,8 +73,7 @@ static bool mmo_auth_fromsql(AccountDB_SQL* db, struct mmo_account* acc, int acc
 static bool mmo_auth_tosql(AccountDB_SQL* db, const struct mmo_account* acc, bool is_new);
 
 /// public constructor
-AccountDB* account_db_sql(void)
-{
+AccountDB* account_db_sql(void) {
 	AccountDB_SQL* db = (AccountDB_SQL*)aCalloc(1, sizeof(AccountDB_SQL));
 
 	// set up the vtable
@@ -114,9 +116,11 @@ AccountDB* account_db_sql(void)
 /* ------------------------------------------------------------------------- */
 
 
-/// establishes database connection
-static bool account_db_sql_init(AccountDB* self)
-{
+/**
+ * Establish the database connection.
+ * @param self: pointer to db
+ */
+static bool account_db_sql_init(AccountDB* self) {
 	AccountDB_SQL* db = (AccountDB_SQL*)self;
 	Sql* sql_handle;
 	const char* username;
@@ -162,9 +166,11 @@ static bool account_db_sql_init(AccountDB* self)
 	return true;
 }
 
-/// disconnects from database
-static void account_db_sql_destroy(AccountDB* self)
-{
+/**
+ * Destroy the database and close the connection to it.
+ * @param self: pointer to db
+ */
+static void account_db_sql_destroy(AccountDB* self){
 	AccountDB_SQL* db = (AccountDB_SQL*)self;
 
 	Sql_Free(db->accounts);
@@ -172,7 +178,15 @@ static void account_db_sql_destroy(AccountDB* self)
 	aFree(db);
 }
 
-/// Gets a property from this database.
+/**
+ * Get configuration information into buf.
+ *  If the option is supported, adjust the internal state.
+ * @param self: pointer to db
+ * @param key: config keyword
+ * @param buf: value set of the keyword
+ * @param buflen: size of buffer to avoid out of bound
+ * @return true if successful, false if something has failed
+ */
 static bool account_db_sql_get_property(AccountDB* self, const char* key, char* buf, size_t buflen)
 {
 	AccountDB_SQL* db = (AccountDB_SQL*)self;
@@ -259,9 +273,15 @@ static bool account_db_sql_get_property(AccountDB* self, const char* key, char*
 	return false;// not found
 }
 
-/// if the option is supported, adjusts the internal state
-static bool account_db_sql_set_property(AccountDB* self, const char* key, const char* value)
-{
+/**
+ * Read and set configuration.
+ *  If the option is supported, adjust the internal state.
+ * @param self: pointer to db
+ * @param key: config keyword
+ * @param value: config value for keyword
+ * @return true if successful, false if something has failed
+ */
+static bool account_db_sql_set_property(AccountDB* self, const char* key, const char* value) {
 	AccountDB_SQL* db = (AccountDB_SQL*)self;
 	const char* signature;
 
@@ -330,11 +350,15 @@ static bool account_db_sql_set_property(AccountDB* self, const char* key, const
 	return false;// not found
 }
 
-/// create a new account entry
-/// If acc->account_id is -1, the account id will be auto-generated,
-/// and its value will be written to acc->account_id if everything succeeds.
-static bool account_db_sql_create(AccountDB* self, struct mmo_account* acc)
-{
+/**
+ * Create a new account entry.
+ *  If acc->account_id is -1, the account id will be auto-generated,
+ *  and its value will be written to acc->account_id if everything succeeds.
+ * @param self: pointer to db
+ * @param acc: pointer of mmo_account to save
+ * @return true if successful, false if something has failed
+ */
+static bool account_db_sql_create(AccountDB* self, struct mmo_account* acc) {
 	AccountDB_SQL* db = (AccountDB_SQL*)self;
 	Sql* sql_handle = db->accounts;
 
@@ -383,9 +407,13 @@ static bool account_db_sql_create(AccountDB* self, struct mmo_account* acc)
 	return mmo_auth_tosql(db, acc, true);
 }
 
-/// delete an existing account entry + its regs
-static bool account_db_sql_remove(AccountDB* self, const int account_id)
-{
+/**
+ * Delete an existing account entry and its regs.
+ * @param self: pointer to db
+ * @param account_id: id of user account
+ * @return true if successful, false if something has failed
+ */
+static bool account_db_sql_remove(AccountDB* self, const int account_id) {
 	AccountDB_SQL* db = (AccountDB_SQL*)self;
 	Sql* sql_handle = db->accounts;
 	bool result = false;
@@ -402,23 +430,40 @@ static bool account_db_sql_remove(AccountDB* self, const int account_id)
 	return result;
 }
 
-/// update an existing account with the provided new data (both account and regs)
-static bool account_db_sql_save(AccountDB* self, const struct mmo_account* acc)
-{
+/**
+ * Update an existing account with the new data provided (both account and regs).
+ * @param self: pointer to db
+ * @param acc: pointer of mmo_account to save
+ * @return true if successful, false if something has failed
+ */
+static bool account_db_sql_save(AccountDB* self, const struct mmo_account* acc) {
 	AccountDB_SQL* db = (AccountDB_SQL*)self;
 	return mmo_auth_tosql(db, acc, false);
 }
 
-/// retrieve data from db and store it in the provided data structure
-static bool account_db_sql_load_num(AccountDB* self, struct mmo_account* acc, const int account_id)
-{
+/**
+ * Retrieve data from db and store it in the provided data structure.
+ *  Filled data structure is done by delegation to mmo_auth_fromsql.
+ * @param self: pointer to db
+ * @param acc: pointer of mmo_account to fill
+ * @param account_id: id of user account
+ * @return true if successful, false if something has failed
+ */
+static bool account_db_sql_load_num(AccountDB* self, struct mmo_account* acc, const int account_id) {
 	AccountDB_SQL* db = (AccountDB_SQL*)self;
 	return mmo_auth_fromsql(db, acc, account_id);
 }
 
-/// retrieve data from db and store it in the provided data structure
-static bool account_db_sql_load_str(AccountDB* self, struct mmo_account* acc, const char* userid)
-{
+/**
+ * Retrieve data from db and store it in the provided data structure.
+ *  Doesn't actually retrieve data yet: escapes and checks userid, then transforms it to accid for fetching.
+ *  Filled data structure is done by delegation to account_db_sql_load_num.
+ * @param self: pointer to db
+ * @param acc: pointer of mmo_account to fill
+ * @param userid: name of user account
+ * @return true if successful, false if something has failed
+ */
+static bool account_db_sql_load_str(AccountDB* self, struct mmo_account* acc, const char* userid) {
 	AccountDB_SQL* db = (AccountDB_SQL*)self;
 	Sql* sql_handle = db->accounts;
 	char esc_userid[2*NAME_LENGTH+1];
@@ -454,10 +499,12 @@ static bool account_db_sql_load_str(AccountDB* self, struct mmo_account* acc, co
 	return account_db_sql_load_num(self, acc, account_id);
 }
 
-
-/// Returns a new forward iterator.
-static AccountDBIterator* account_db_sql_iterator(AccountDB* self)
-{
+/**
+ * Create a new forward iterator.
+ * @param self: pointer to db iterator
+ * @return a new db iterator
+ */
+static AccountDBIterator* account_db_sql_iterator(AccountDB* self) {
 	AccountDB_SQL* db = (AccountDB_SQL*)self;
 	AccountDBIterator_SQL* iter = (AccountDBIterator_SQL*)aCalloc(1, sizeof(AccountDBIterator_SQL));
 
@@ -472,18 +519,22 @@ static AccountDBIterator* account_db_sql_iterator(AccountDB* self)
 	return &iter->vtable;
 }
 
-
-/// Destroys this iterator, releasing all allocated memory (including itself).
-static void account_db_sql_iter_destroy(AccountDBIterator* self)
-{
+/**
+ * Destroys this iterator, releasing all allocated memory (including itself).
+ * @param self: pointer to db iterator
+ */
+static void account_db_sql_iter_destroy(AccountDBIterator* self) {
 	AccountDBIterator_SQL* iter = (AccountDBIterator_SQL*)self;
 	aFree(iter);
 }
 
-
-/// Fetches the next account in the database.
-static bool account_db_sql_iter_next(AccountDBIterator* self, struct mmo_account* acc)
-{
+/**
+ * Fetches the next account in the database.
+ * @param self: pointer to db iterator
+ * @param acc: pointer of mmo_account to fill
+ * @return true if next account found and filled, false if something has failed
+ */
+static bool account_db_sql_iter_next(AccountDBIterator* self, struct mmo_account* acc) {
 	AccountDBIterator_SQL* iter = (AccountDBIterator_SQL*)self;
 	AccountDB_SQL* db = (AccountDB_SQL*)iter->db;
 	Sql* sql_handle = db->accounts;
@@ -514,9 +565,14 @@ static bool account_db_sql_iter_next(AccountDBIterator* self, struct mmo_account
 	return false;
 }
 
-
-static bool mmo_auth_fromsql(AccountDB_SQL* db, struct mmo_account* acc, int account_id)
-{
+/**
+ * Fetch a struct mmo_account from sql.
+ * @param db: pointer to db
+ * @param acc: pointer of mmo_account to fill
+ * @param account_id: id of user account to take data from
+ * @return true if successful, false if something has failed
+ */
+static bool mmo_auth_fromsql(AccountDB_SQL* db, struct mmo_account* acc, int account_id) {
 	Sql* sql_handle = db->accounts;
 	char* data;
 	int i = 0;
@@ -587,8 +643,14 @@ static bool mmo_auth_fromsql(AccountDB_SQL* db, struct mmo_account* acc, int acc
 	return true;
 }
 
-static bool mmo_auth_tosql(AccountDB_SQL* db, const struct mmo_account* acc, bool is_new)
-{
+/**
+ * Save a struct mmo_account in sql.
+ * @param db: pointer to db
+ * @param acc: pointer of mmo_account to save
+ * @param is_new: if it's a new entry or should we update
+ * @return true if successful, false if something has failed
+ */
+static bool mmo_auth_tosql(AccountDB_SQL* db, const struct mmo_account* acc, bool is_new) {
 	Sql* sql_handle = db->accounts;
 	SqlStmt* stmt = SqlStmt_Malloc(sql_handle);
 	bool result = false;

+ 11 - 6
src/login/account.h

@@ -1,11 +1,18 @@
-// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
-// For more information, see LICENCE in the main folder
+/**
+ * @file account.h
+ * Module purpose is to save, load, and update changes into the account table or file.
+ * Licensed under GNU GPL.
+ *  For more information, see LICENCE in the main folder.
+ * @author Athena Dev Teams < r15k
+ * @author rAthena Dev Team
+ */
 
 #ifndef __ACCOUNT_H_INCLUDED__
 #define __ACCOUNT_H_INCLUDED__
 
 #include "../common/cbasetypes.h"
 #include "../common/mmo.h" // ACCOUNT_REG2_NUM
+#include "../config/core.h"
 
 typedef struct AccountDB AccountDB;
 typedef struct AccountDBIterator AccountDBIterator;
@@ -61,8 +68,7 @@ struct mmo_account {
 };
 
 
-struct AccountDBIterator
-{
+struct AccountDBIterator {
 	/// Destroys this iterator, releasing all allocated memory (including itself).
 	///
 	/// @param self Iterator
@@ -77,8 +83,7 @@ struct AccountDBIterator
 };
 
 
-struct AccountDB
-{
+struct AccountDB {
 	/// Initializes this database, making it ready for use.
 	/// Call this after setting the properties.
 	///

+ 135 - 103
src/login/ipban_sql.c → src/login/ipban.c

@@ -1,5 +1,12 @@
-// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
-// For more information, see LICENCE in the main folder
+/**
+ * @file ipban.c
+ * Module purpose is to read configuration for login-server and handle accounts,
+ *  and also to synchronize all login interfaces: loginchrif, loginclif, logincnslif.
+ * Licensed under GNU GPL.
+ *  For more information, see LICENCE in the main folder.
+ * @author Athena Dev Teams < r15k
+ * @author rAthena Dev Team
+ */
 
 #include "../common/cbasetypes.h"
 #include "../common/db.h"
@@ -35,82 +42,90 @@ static Sql* sql_handle = NULL;
 static int cleanup_timer_id = INVALID_TIMER;
 static bool ipban_inited = false;
 
+//early declaration
 int ipban_cleanup(int tid, unsigned int tick, int id, intptr_t data);
 
+/**
+ * Check if ip is in the active bans list.
+ * @param ip: ipv4 ip to check if ban
+ * @return true if found or error, false if not in list
+ */
+bool ipban_check(uint32 ip) {
+	uint8* p = (uint8*)&ip;
+	char* data = NULL;
+	int matches;
 
-// initialize
-void ipban_init(void)
-{
-	const char* username;
-	const char* password;
-	const char* hostname;
-	uint16      port;
-	const char* database;
-	const char* codepage;
+	if( !login_config.ipban )
+		return false;// ipban disabled
 
-	ipban_inited = true;
+	if( SQL_ERROR == Sql_Query(sql_handle, "SELECT count(*) FROM `%s` WHERE `rtime` > NOW() AND (`list` = '%u.*.*.*' OR `list` = '%u.%u.*.*' OR `list` = '%u.%u.%u.*' OR `list` = '%u.%u.%u.%u')",
+		ipban_table, p[3], p[3], p[2], p[3], p[2], p[1], p[3], p[2], p[1], p[0]) )
+	{
+		Sql_ShowDebug(sql_handle);
+		// close connection because we can't verify their connectivity.
+		return true;
+	}
+
+	if( SQL_ERROR == Sql_NextRow(sql_handle) )
+		return true;// Shouldn't happen, but just in case...
+
+	Sql_GetData(sql_handle, 0, &data, NULL);
+	matches = atoi(data);
+	Sql_FreeResult(sql_handle);
+
+	return( matches > 0 );
+}
+
+/**
+ * Log a failed attempt.
+ *  Also bans the user if too many failed attempts are made.
+ * @param ip: ipv4 ip to record the failure
+ */
+void ipban_log(uint32 ip) {
+	unsigned long failures;
 
 	if( !login_config.ipban )
 		return;// ipban disabled
 
-	if( ipban_db_hostname[0] != '\0' )
-	{// local settings
-		username = ipban_db_username;
-		password = ipban_db_password;
-		hostname = ipban_db_hostname;
-		port     = ipban_db_port;
-		database = ipban_db_database;
-		codepage = ipban_codepage;
-	}
-	else
-	{// global settings
-		username = global_db_username;
-		password = global_db_password;
-		hostname = global_db_hostname;
-		port     = global_db_port;
-		database = global_db_database;
-		codepage = global_codepage;
-	}
+	failures = loginlog_failedattempts(ip, login_config.dynamic_pass_failure_ban_interval);// how many times failed account? in one ip.
 
-	// establish connections
-	sql_handle = Sql_Malloc();
-	if( SQL_ERROR == Sql_Connect(sql_handle, username, password, hostname, port, database) )
+	// if over the limit, add a temporary ban entry
+	if( failures >= login_config.dynamic_pass_failure_ban_limit )
 	{
-		Sql_ShowDebug(sql_handle);
-		Sql_Free(sql_handle);
-		exit(EXIT_FAILURE);
+		uint8* p = (uint8*)&ip;
+		if( SQL_ERROR == Sql_Query(sql_handle, "INSERT INTO `%s`(`list`,`btime`,`rtime`,`reason`) VALUES ('%u.%u.%u.*', NOW() , NOW() +  INTERVAL %d MINUTE ,'Password error ban')",
+			ipban_table, p[3], p[2], p[1], login_config.dynamic_pass_failure_ban_duration) )
+			Sql_ShowDebug(sql_handle);
 	}
-	if( codepage[0] != '\0' && SQL_ERROR == Sql_SetEncoding(sql_handle, codepage) )
-		Sql_ShowDebug(sql_handle);
-
-	if( login_config.ipban_cleanup_interval > 0 )
-	{ // set up periodic cleanup of connection history and active bans
-		add_timer_func_list(ipban_cleanup, "ipban_cleanup");
-		cleanup_timer_id = add_timer_interval(gettick()+10, ipban_cleanup, 0, 0, login_config.ipban_cleanup_interval*1000);
-	} else // make sure it gets cleaned up on login-server start regardless of interval-based cleanups
-		ipban_cleanup(0,0,0,0);
 }
 
-// finalize
-void ipban_final(void)
-{
+/**
+ * Timered function to remove expired bans.
+ *  Request all characters to update their registered ip and transmit their new ip.
+ *  Performed each ip_sync_interval.
+ * @param tid: timer id
+ * @param tick: tick of execution
+ * @param id: unused
+ * @param data: unused
+ * @return 0
+ */
+int ipban_cleanup(int tid, unsigned int tick, int id, intptr_t data) {
 	if( !login_config.ipban )
-		return;// ipban disabled
+		return 0;// ipban disabled
 
-	if( login_config.ipban_cleanup_interval > 0 )
-		// release data
-		delete_timer(cleanup_timer_id, ipban_cleanup);
-	
-	ipban_cleanup(0,0,0,0); // always clean up on login-server stop
+	if( SQL_ERROR == Sql_Query(sql_handle, "DELETE FROM `ipbanlist` WHERE `rtime` <= NOW()") )
+		Sql_ShowDebug(sql_handle);
 
-	// close connections
-	Sql_Free(sql_handle);
-	sql_handle = NULL;
+	return 0;
 }
 
-// load configuration options
-bool ipban_config_read(const char* key, const char* value)
-{
+/**
+ * Read configuration options.
+ * @param key: config keyword
+ * @param value: config value for keyword
+ * @return true if successful, false if config not complete or server already running
+ */
+bool ipban_config_read(const char* key, const char* value) {
 	const char* signature;
 
 	if( ipban_inited )
@@ -197,62 +212,79 @@ bool ipban_config_read(const char* key, const char* value)
 	return false;// not found
 }
 
-// check ip against active bans list
-bool ipban_check(uint32 ip)
-{
-	uint8* p = (uint8*)&ip;
-	char* data = NULL;
-	int matches;
 
-	if( !login_config.ipban )
-		return false;// ipban disabled
+/// Constructor destructor
 
-	if( SQL_ERROR == Sql_Query(sql_handle, "SELECT count(*) FROM `%s` WHERE `rtime` > NOW() AND (`list` = '%u.*.*.*' OR `list` = '%u.%u.*.*' OR `list` = '%u.%u.%u.*' OR `list` = '%u.%u.%u.%u')",
-		ipban_table, p[3], p[3], p[2], p[3], p[2], p[1], p[3], p[2], p[1], p[0]) )
-	{
-		Sql_ShowDebug(sql_handle);
-		// close connection because we can't verify their connectivity.
-		return true;
-	}
-
-	if( SQL_ERROR == Sql_NextRow(sql_handle) )
-		return true;// Shouldn't happen, but just in case...
-
-	Sql_GetData(sql_handle, 0, &data, NULL);
-	matches = atoi(data);
-	Sql_FreeResult(sql_handle);
-
-	return( matches > 0 );
-}
+/**
+ * Initialize the module.
+ * Launched at login-serv start, create db or other long scope variable here.
+ */
+void ipban_init(void) {
+	const char* username;
+	const char* password;
+	const char* hostname;
+	uint16      port;
+	const char* database;
+	const char* codepage;
 
-// log failed attempt
-void ipban_log(uint32 ip)
-{
-	unsigned long failures;
+	ipban_inited = true;
 
 	if( !login_config.ipban )
 		return;// ipban disabled
 
-	failures = loginlog_failedattempts(ip, login_config.dynamic_pass_failure_ban_interval);// how many times failed account? in one ip.
+	if( ipban_db_hostname[0] != '\0' )
+	{// local settings
+		username = ipban_db_username;
+		password = ipban_db_password;
+		hostname = ipban_db_hostname;
+		port     = ipban_db_port;
+		database = ipban_db_database;
+		codepage = ipban_codepage;
+	}
+	else
+	{// global settings
+		username = global_db_username;
+		password = global_db_password;
+		hostname = global_db_hostname;
+		port     = global_db_port;
+		database = global_db_database;
+		codepage = global_codepage;
+	}
 
-	// if over the limit, add a temporary ban entry
-	if( failures >= login_config.dynamic_pass_failure_ban_limit )
+	// establish connections
+	sql_handle = Sql_Malloc();
+	if( SQL_ERROR == Sql_Connect(sql_handle, username, password, hostname, port, database) )
 	{
-		uint8* p = (uint8*)&ip;
-		if( SQL_ERROR == Sql_Query(sql_handle, "INSERT INTO `%s`(`list`,`btime`,`rtime`,`reason`) VALUES ('%u.%u.%u.*', NOW() , NOW() +  INTERVAL %d MINUTE ,'Password error ban')",
-			ipban_table, p[3], p[2], p[1], login_config.dynamic_pass_failure_ban_duration) )
-			Sql_ShowDebug(sql_handle);
+		Sql_ShowDebug(sql_handle);
+		Sql_Free(sql_handle);
+		exit(EXIT_FAILURE);
 	}
+	if( codepage[0] != '\0' && SQL_ERROR == Sql_SetEncoding(sql_handle, codepage) )
+		Sql_ShowDebug(sql_handle);
+
+	if( login_config.ipban_cleanup_interval > 0 )
+	{ // set up periodic cleanup of connection history and active bans
+		add_timer_func_list(ipban_cleanup, "ipban_cleanup");
+		cleanup_timer_id = add_timer_interval(gettick()+10, ipban_cleanup, 0, 0, login_config.ipban_cleanup_interval*1000);
+	} else // make sure it gets cleaned up on login-server start regardless of interval-based cleanups
+		ipban_cleanup(0,0,0,0);
 }
 
-// remove expired bans
-int ipban_cleanup(int tid, unsigned int tick, int id, intptr_t data)
-{
+/**
+ * Destroy the module.
+ * Launched at login-serv end, cleanup db connection or other thing here.
+ */
+void ipban_final(void) {
 	if( !login_config.ipban )
-		return 0;// ipban disabled
+		return;// ipban disabled
 
-	if( SQL_ERROR == Sql_Query(sql_handle, "DELETE FROM `ipbanlist` WHERE `rtime` <= NOW()") )
-		Sql_ShowDebug(sql_handle);
+	if( login_config.ipban_cleanup_interval > 0 )
+		// release data
+		delete_timer(cleanup_timer_id, ipban_cleanup);
 
-	return 0;
+	ipban_cleanup(0,0,0,0); // always clean up on login-server stop
+
+	// close connections
+	Sql_Free(sql_handle);
+	sql_handle = NULL;
 }

+ 36 - 11
src/login/ipban.h

@@ -1,25 +1,50 @@
-// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
-// For more information, see LICENCE in the main folder
+/**
+ * @file ipban.h
+ * Module purpose is to read configuration for login-server and handle accounts,
+ *  and also to synchronize all login interfaces: loginchrif, loginclif, logincnslif.
+ * Licensed under GNU GPL.
+ *  For more information, see LICENCE in the main folder.
+ * @author Athena Dev Teams < r15k
+ * @author rAthena Dev Team
+ */
 
 #ifndef __IPBAN_H_INCLUDED__
 #define __IPBAN_H_INCLUDED__
 
 #include "../common/cbasetypes.h"
 
-// initialize
-void ipban_init(void);
-
-// finalize
-void ipban_final(void);
-
-// check ip against ban list
+/**
+ * Check if ip is in the active bans list.
+ * @param ip: ipv4 ip to check if ban
+ * @return true if found or error, false if not in list
+ */
 bool ipban_check(uint32 ip);
 
-// increases failure count for the specified IP
+/**
+ * Log a failed attempt.
+ *  Also bans the user if too many failed attempts are made.
+ * @param ip: ipv4 ip to record the failure
+ */
 void ipban_log(uint32 ip);
 
-// parses configuration option
+/**
+ * Read configuration options.
+ * @param key: config keyword
+ * @param value: config value for keyword
+ * @return true if successful, false if config not complete or server already running
+ */
 bool ipban_config_read(const char* key, const char* value);
 
+/**
+ * Initialize the module.
+ * Launched at login-serv start, create db or other long scope variable here.
+ */
+void ipban_init(void);
+
+/**
+ * Destroy the module.
+ * Launched at login-serv end, cleanup db connection or other thing here.
+ */
+void ipban_final(void);
 
 #endif // __IPBAN_H_INCLUDED__

文件差异内容过多而无法显示
+ 116 - 934
src/login/login.c


+ 135 - 15
src/login/login.h

@@ -1,15 +1,22 @@
-// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
-// For more information, see LICENCE in the main folder
+/**
+ * @file login.h
+ * Module purpose is to read configuration for login-server and handle accounts,
+ *  and also to synchronise all login interfaces: loginchrif, loginclif, logincnslif.
+ * Licensed under GNU GPL.
+ *  For more information, see LICENCE in the main folder.
+ * @author Athena Dev Teams < r15k
+ * @author rAthena Dev Team
+ */
 
 #ifndef _LOGIN_H_
 #define _LOGIN_H_
 
 #include "../common/mmo.h" // NAME_LENGTH,SEX_*
 #include "../common/core.h" // CORE_ST_LAST
+#include "account.h"
 #include "../config/core.h"
 
-enum E_LOGINSERVER_ST
-{
+enum E_LOGINSERVER_ST {
 	LOGINSERVER_ST_RUNNING = CORE_ST_LAST,
 	LOGINSERVER_ST_STARTING,
 	LOGINSERVER_ST_SHUTDOWN,
@@ -27,7 +34,7 @@ struct login_session_data {
 	char sex;			/// 'F','M','S'
 
 	char userid[NAME_LENGTH];	/// account name
-	char passwd[32+1];		/// 23+1 for plaintext, 32+1 for md5-ed passwords
+	char passwd[PASSWD_LENGTH]; // 23+1 for plaintext, 32+1 for md5-ed passwords
 	int passwdenc;			/// was the passwd transmited encrypted or clear ?
 	char md5key[20];		/// md5 key of session (each connection could be encrypted with a md5 key)
 	uint16 md5keylen;		/// len of the md5 key
@@ -43,6 +50,7 @@ struct login_session_data {
 	int fd;				///socket of client
 };
 
+#define MAX_SERVERS 30 //max number of mapserv that could be attach
 ///Struct describing 1 char-serv attach to us
 struct mmo_char_server {
 	char name[20];	///char-serv name
@@ -53,9 +61,10 @@ struct mmo_char_server {
 	uint16 type;	/// 0=normal, 1=maintenance, 2=over 18, 3=paying, 4=P2P
 	uint16 new_;	/// should display as 'new'?
 };
+extern struct mmo_char_server ch_server[MAX_SERVERS];
 
 struct client_hash_node {
-	unsigned int group_id;			///group
+	unsigned int group_id;			//inferior or egal group to apply restriction
 	uint8 hash[16];					///hash required for that groupid or below
 	struct client_hash_node *next;	///next entry
 };
@@ -84,10 +93,16 @@ struct Login_Config {
 	bool use_dnsbl;                                 /// dns blacklist blocking ?
 	char dnsbl_servs[1024];                         /// comma-separated list of dnsbl servers
 
-	char account_engine[256];                       /// name of the engine to use (defaults to auto, for the first available engine)
+	char account_engine[256];				// name of the engine to use (defaults to auto, for the first available engine)
+	int allowed_regs;					//max number of registration
+	int time_allowed;					//registration intervall in seconds
 
-	int client_hash_check;                          /// flags for checking client md5
-	struct client_hash_node *client_hash_nodes;     /// linked list containg md5 hash for each gm group
+	int client_hash_check;					// flags for checking client md5
+	struct client_hash_node *client_hash_nodes;		// linked list containg md5 hash for each gm group
+	char loginconf_name[256];				//name of main config file
+	char msgconf_name[256];					//name of msg_conf config file
+	char lanconf_name[256];					//name of lan config file
+	
 	int char_per_account;                           /// number of characters an account can have
 #ifdef VIP_ENABLE
 	struct {
@@ -96,6 +111,7 @@ struct Login_Config {
 	} vip_sys;
 #endif
 };
+extern struct Login_Config login_config;
 
 #define sex_num2str(num) ( (num ==  SEX_FEMALE  ) ? 'F' : (num ==  SEX_MALE  ) ? 'M' : 'S' )
 #define sex_str2num(str) ( (str == 'F' ) ?  SEX_FEMALE  : (str == 'M' ) ?  SEX_MALE  :  SEX_SERVER  )
@@ -103,15 +119,119 @@ struct Login_Config {
 #define msg_config_read(cfgName) login_msg_config_read(cfgName)
 #define msg_txt(msg_number) login_msg_txt(msg_number)
 #define do_final_msg() login_do_final_msg()
-
 int login_msg_config_read(char *cfgName);
 const char* login_msg_txt(int msg_number);
 void login_do_final_msg(void);
 
-
-#define MAX_SERVERS 30					///number of charserv loginserv can handle
-extern struct mmo_char_server ch_server[MAX_SERVERS];	///array of char-servs data
-extern struct Login_Config login_config;		///config of login serv
-
+/// Online User Database [Wizputer]
+struct online_login_data {
+	int account_id;
+	int waiting_disconnect;
+	int char_server;
+};
+extern DBMap* online_db; // int account_id -> struct online_login_data*
+
+/// Auth database
+#define AUTH_TIMEOUT 30000
+struct auth_node {
+	int account_id;
+	uint32 login_id1;
+	uint32 login_id2;
+	uint32 ip;
+	char sex;
+	uint32 version;
+	uint8 clienttype;
+};
+extern DBMap* auth_db; // int account_id -> struct auth_node*
+
+///Accessors
+AccountDB* login_get_accounts_db(void);
+
+/**
+ * Sub function to create an online_login_data and save it to db.
+ * @param key: Key of the database entry
+ * @param ap: args
+ * @return : Data identified by the key to be put in the database
+ * @see DBCreateData
+ */
+DBData login_create_online_user(DBKey key, va_list args);
+
+/**
+ * Function to add a user in online_db.
+ *  Checking if the user is already registered in the db.
+ *  Stop disconnection timer if set.
+ * @param char_server: id of char-serv on wich the player is
+ * @param account_id: the account identifier
+ * @return the new|registered online data
+ */
+struct online_login_data* login_add_online_user(int char_server, int account_id);
+
+/**
+ * Function to remove a user from online_db.
+ *  Checking if user was already scheduled for deletion, and remove that timer if found.
+ * @param account_id: the account identifier
+ */
+void login_remove_online_user(int account_id);
+
+/**
+ * Timered function to disconnect a user from login.
+ *  This is done either after auth_ok or kicked by char-server.
+ *  Removing user from auth_db and online_db.
+ *  Delay is AUTH_TIMEOUT by default.
+ * @param tid: timer id
+ * @param tick: tick of execution
+ * @param id: user account id
+ * @param data: unused
+ * @return :0
+ */
+int login_waiting_disconnect_timer(int tid, unsigned int tick, int id, intptr_t data);
+
+/**
+ * Sub function to apply on online_db.
+ * Mark a character as offline.
+ * @param data: 1 entry in the db
+ * @param ap: args
+ * @return : Value to be added up by the function that is applying this
+ * @see DBApply
+ */
+int login_online_db_setoffline(DBKey key, DBData *data, va_list ap);
+
+/**
+ * Test to determine if an IP come from LAN or WAN.
+ * @param ip: ip to check if in auth network
+ * @return 0 if from wan, or subnet_char_ip if lan
+ */
+int lan_subnetcheck(uint32 ip);
+
+
+/**
+ * Create a new account and save it in db/sql.
+ * @param userid: string for user login
+ * @param pass: string for user pass
+ * @param sex: should be M|F|S (todo make an enum ?)
+ * @param last_ip:
+ * @return :
+ *	-1: success
+ *	0: unregistered id (wrong sex fail to create in db);
+ *	1: incorrect pass or userid (userid|pass too short or already exist);
+ *	3: registration limit exceeded;
+ */
+int login_mmo_auth_new(const char* userid, const char* pass, const char sex, const char* last_ip);
+
+/**
+ * Check/authentication of a connection.
+ * @param sd: string (atm:md5key or dbpass)
+ * @param isServer: string (atm:md5key or dbpass)
+ * @return :
+ *	-1: success
+ *	0: unregistered id;
+ *	1: incorrect pass;
+ *	2: expired id
+ *	3: blacklisted (or registration limit exceeded if new acc);
+ *	5: invalid client_version|hash;
+ *	6: banned
+ *	x: acc state (TODO document me deeper)
+ */
+int login_mmo_auth(struct login_session_data* sd, bool isServer);
 
 #endif /* _LOGIN_H_ */

+ 972 - 0
src/login/loginchrif.c

@@ -0,0 +1,972 @@
+/**
+ * @file loginchrif.c
+ * Module purpose is to handle incoming and outgoing requests with char-server.
+ * Licensed under GNU GPL.
+ *  For more information, see LICENCE in the main folder.
+ * @author Athena Dev Teams originally in login.c
+ * @author rAthena Dev Team
+ */
+
+#include "../common/timer.h" //difftick
+#include "../common/strlib.h" //safeprint
+#include "../common/showmsg.h" //show notice
+#include "../common/socket.h" //wfifo session
+#include "../common/malloc.h"
+#include "account.h"
+#include "ipban.h" //ipban_check
+#include "login.h"
+#include "loginlog.h"
+#include "loginclif.h"
+#include "loginchrif.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+//early declaration
+void logchrif_on_disconnect(int id);
+
+/**
+ * Packet send to all char-servers, except one. (wos: without our self)
+ * @param sfd: fd to discard sending to
+ * @param buf: packet to send in form of an array buffer
+ * @param len: size of packet
+ * @return : the number of char-serv the packet was sent to
+ */
+int logchrif_sendallwos(int sfd, uint8* buf, size_t len) {
+	int i, c;
+	for( i = 0, c = 0; i < ARRAYLENGTH(ch_server); ++i ) {
+		int fd = ch_server[i].fd;
+		if( session_isValid(fd) && fd != sfd ){
+			WFIFOHEAD(fd,len);
+			memcpy(WFIFOP(fd,0), buf, len);
+			WFIFOSET(fd,len);
+			++c;
+		}
+	}
+	return c;
+}
+
+/**
+ * Timered function to synchronize ip addresses.
+ *  Requesting all char to update their registered ip and transmit their new ip.
+ *  Performed each ip_sync_interval.
+ * @param tid: timer id
+ * @param tick: tick of execution
+ * @param id: unused
+ * @param data: unused
+ * @return 0
+ */
+static int logchrif_sync_ip_addresses(int tid, unsigned int tick, int id, intptr_t data) {
+	uint8 buf[2];
+	ShowInfo("IP Sync in progress...\n");
+	WBUFW(buf,0) = 0x2735;
+	logchrif_sendallwos(-1, buf, 2);
+	return 0;
+}
+
+
+
+
+/// Parsing handlers
+
+/**
+ * Request from char-server to authenticate an account.
+ * @param fd: fd to parse from (char-serv)
+ * @param id: id of char-serv
+ * @param ip: char-serv ip (used for info)
+ * @return 0 not enough info transmitted, 1 success
+ */
+int logchrif_parse_reqauth(int fd, int id,char* ip){
+	if( RFIFOREST(fd) < 23 )
+		return 0;
+	else{
+		struct auth_node* node;
+		int account_id = RFIFOL(fd,2);
+		uint32 login_id1 = RFIFOL(fd,6);
+		uint32 login_id2 = RFIFOL(fd,10);
+		uint8 sex = RFIFOB(fd,14);
+		//uint32 ip_ = ntohl(RFIFOL(fd,15));
+		int request_id = RFIFOL(fd,19);
+		RFIFOSKIP(fd,23);
+
+		node = (struct auth_node*)idb_get(auth_db, account_id);
+		if( runflag == LOGINSERVER_ST_RUNNING &&
+			node != NULL &&
+			node->account_id == account_id &&
+			node->login_id1  == login_id1 &&
+			node->login_id2  == login_id2 &&
+			node->sex        == sex_num2str(sex) /*&&
+			node->ip         == ip_*/ ){// found
+			//ShowStatus("Char-server '%s': authentication of the account %d accepted (ip: %s).\n", server[id].name, account_id, ip);
+
+			// send ack
+			WFIFOHEAD(fd,25);
+			WFIFOW(fd,0) = 0x2713;
+			WFIFOL(fd,2) = account_id;
+			WFIFOL(fd,6) = login_id1;
+			WFIFOL(fd,10) = login_id2;
+			WFIFOB(fd,14) = sex;
+			WFIFOB(fd,15) = 0;// ok
+			WFIFOL(fd,16) = request_id;
+			WFIFOL(fd,20) = node->version;
+			WFIFOB(fd,24) = node->clienttype;
+			WFIFOSET(fd,25);
+
+			// each auth entry can only be used once
+			idb_remove(auth_db, account_id);
+		}else{// authentication not found
+			ShowStatus("Char-server '%s': authentication of the account %d REFUSED (ip: %s).\n", ch_server[id].name, account_id, ip);
+			WFIFOHEAD(fd,25);
+			WFIFOW(fd,0) = 0x2713;
+			WFIFOL(fd,2) = account_id;
+			WFIFOL(fd,6) = login_id1;
+			WFIFOL(fd,10) = login_id2;
+			WFIFOB(fd,14) = sex;
+			WFIFOB(fd,15) = 1;// auth failed
+			WFIFOL(fd,16) = request_id;
+			WFIFOL(fd,20) = 0;
+			WFIFOB(fd,24) = 0;
+			WFIFOSET(fd,25);
+		}
+	}
+	return 1;
+}
+
+/**
+ * Receive a request to update user count for char-server identified by id.
+ * @param fd: fd to parse from (char-serv)
+ * @param id: id of char-serv
+ * @return 0 not enough info transmitted, 1 success
+ */
+int logchrif_parse_ackusercount(int fd, int id){
+	if( RFIFOREST(fd) < 6 )
+		return 0;
+	else{
+		int users = RFIFOL(fd,2);
+		RFIFOSKIP(fd,6);
+		// how many users on world? (update)
+		if( ch_server[id].users != users ){
+			ShowStatus("set users %s : %d\n", ch_server[id].name, users);
+			ch_server[id].users = users;
+		}
+	}
+	return 1;
+}
+
+/**
+ * Receive a request from char-server to change e-mail from default "a@a.com".
+ * @param fd: fd to parse from (char-serv)
+ * @param id: id of char-serv
+ * @param ip: char-serv ip (used for info)
+ * @return 0 not enough info transmitted, 1 success
+ */
+int logchrif_parse_updmail(int fd, int id, char* ip){
+	if (RFIFOREST(fd) < 46)
+		return 0;
+	else{
+		AccountDB* accounts = login_get_accounts_db();
+		struct mmo_account acc;
+		char email[40];
+
+		int account_id = RFIFOL(fd,2);
+		safestrncpy(email, (char*)RFIFOP(fd,6), 40); remove_control_chars(email);
+		RFIFOSKIP(fd,46);
+
+		if( e_mail_check(email) == 0 )
+			ShowNotice("Char-server '%s': Attempt to create an e-mail on an account with a default e-mail REFUSED - e-mail is invalid (account: %d, ip: %s)\n", ch_server[id].name, account_id, ip);
+		else if( !accounts->load_num(accounts, &acc, account_id) || strcmp(acc.email, "a@a.com") == 0 || acc.email[0] == '\0' )
+			ShowNotice("Char-server '%s': Attempt to create an e-mail on an account with a default e-mail REFUSED - account doesn't exist or e-mail of account isn't default e-mail (account: %d, ip: %s).\n", ch_server[id].name, account_id, ip);
+		else{
+			memcpy(acc.email, email, 40);
+			ShowNotice("Char-server '%s': Create an e-mail on an account with a default e-mail (account: %d, new e-mail: %s, ip: %s).\n", ch_server[id].name, account_id, email, ip);
+			// Save
+			accounts->save(accounts, &acc);
+		}
+	}
+	return 1;
+}
+
+/**
+ * Transmit account data to char_server
+ * S 2717 aid.W email.40B exp_time.L group_id.B char_slot.B birthdate.11B pincode.5B pincode_change.L bank_vault.L
+ *  isvip.1B char_vip.1B max_billing.1B (tot 79)  
+ * @return -1 : account not found, 1:sucess
+ */
+int logchrif_send_accdata(int fd, uint32 aid) {
+	struct mmo_account acc;
+	time_t expiration_time = 0;
+	char email[40] = "";
+	int group_id = 0;
+	char birthdate[10+1] = "";
+	char pincode[PINCODE_LENGTH+1];
+	int bank_vault = 0;
+	char isvip = false;
+	uint8 char_slots = MIN_CHARS, char_vip = 0;
+	AccountDB* accounts = login_get_accounts_db();
+
+	memset(pincode,0,PINCODE_LENGTH+1);
+	if( !accounts->load_num(accounts, &acc, aid) )
+		return -1;
+	else {
+		safestrncpy(email, acc.email, sizeof(email));
+		expiration_time = acc.expiration_time;
+		group_id = acc.group_id;
+
+		safestrncpy(birthdate, acc.birthdate, sizeof(birthdate));
+		safestrncpy(pincode, acc.pincode, sizeof(pincode));
+		bank_vault = acc.bank_vault;
+#ifdef VIP_ENABLE
+		char_vip = login_config.vip_sys.char_increase;
+		if( acc.vip_time > time(NULL) ) {
+			isvip = true;
+			char_slots = login_config.char_per_account + char_vip;
+		} else
+			char_slots = login_config.char_per_account;
+#endif
+	}
+
+	WFIFOHEAD(fd,79);
+	WFIFOW(fd,0) = 0x2717;
+	WFIFOL(fd,2) = aid;
+	safestrncpy((char*)WFIFOP(fd,6), email, 40);
+	WFIFOL(fd,46) = (uint32)expiration_time;
+	WFIFOB(fd,50) = (unsigned char)group_id;
+	WFIFOB(fd,51) = char_slots;
+	safestrncpy((char*)WFIFOP(fd,52), birthdate, 10+1);
+	safestrncpy((char*)WFIFOP(fd,63), pincode, 4+1 );
+	WFIFOL(fd,68) = (uint32)acc.pincode_change;
+	WFIFOL(fd,72) = bank_vault;
+	WFIFOB(fd,76) = isvip;
+	WFIFOB(fd,77) = char_vip;
+	WFIFOB(fd,78) = MAX_CHAR_BILLING; //TODO create a config for this
+	WFIFOSET(fd,79);
+	return 1;
+}
+
+/**
+ * Transmit vip specific data to char-serv (will be transfered to mapserv)
+ */
+int logchrif_sendvipdata(int fd, struct mmo_account acc, char isvip, char isgm, int mapfd) {
+#ifdef VIP_ENABLE
+	WFIFOHEAD(fd,19);
+	WFIFOW(fd,0) = 0x2743;
+	WFIFOL(fd,2) = acc.account_id;
+	WFIFOL(fd,6) = (int)acc.vip_time;
+	WFIFOB(fd,10) = isvip;
+	WFIFOL(fd,11) = acc.group_id; //new group id
+	WFIFOL(fd,15) = isgm;
+	WFIFOL(fd,16) = mapfd; //link to mapserv
+	WFIFOSET(fd,20);
+	logchrif_send_accdata(fd,acc.account_id); //refresh char with new setting
+#endif
+	return 1;
+}
+
+/**
+ * Receive a request for account data reply by sending all mmo_account information.
+ * @param fd: fd to parse from (char-serv)
+ * @param id: id of char-serv
+ * @param ip: char-serv ip (used for info)
+ * @return 0 not enough info transmitted, 1 success
+ */
+int logchrif_parse_reqaccdata(int fd, int id, char *ip){
+	if( RFIFOREST(fd) < 6 )
+		return 0;
+	else {
+		uint32 aid = RFIFOL(fd,2);
+		RFIFOSKIP(fd,6);
+		if( logchrif_send_accdata(fd,aid) < 0 )
+			ShowNotice("Char-server '%s': account %d NOT found (ip: %s).\n", ch_server[id].name, aid, ip);
+	}
+	return 1;
+}
+
+/**
+ * Ping request from char-server to send a reply.
+ * @param fd: fd to parse from (char-serv)
+ * @return 1 success
+ */
+int logchrif_parse_keepalive(int fd){
+	RFIFOSKIP(fd,2);
+	WFIFOHEAD(fd,2);
+	WFIFOW(fd,0) = 0x2718;
+	WFIFOSET(fd,2);
+	return 1;
+}
+
+/**
+ * Map server send information to change an email of an account via char-server.
+ * 0x2722 <account_id>.L <actual_e-mail>.40B <new_e-mail>.40B
+ * @param fd: fd to parse from (char-serv)
+ * @param id: id of char-serv
+ * @param ip: char-serv ip (used for info)
+ * @return 0 not enough info transmitted, 1 success
+ */
+int logchrif_parse_reqchangemail(int fd, int id, char* ip){
+	if (RFIFOREST(fd) < 86)
+		return 0;
+	else{
+		struct mmo_account acc;
+		AccountDB* accounts = login_get_accounts_db();
+		char actual_email[40];
+		char new_email[40];
+
+		int account_id = RFIFOL(fd,2);
+		safestrncpy(actual_email, (char*)RFIFOP(fd,6), 40);
+		safestrncpy(new_email, (char*)RFIFOP(fd,46), 40);
+		RFIFOSKIP(fd, 86);
+
+		if( e_mail_check(actual_email) == 0 )
+			ShowNotice("Char-server '%s': Attempt to modify an e-mail on an account (@email GM command), but actual email is invalid (account: %d, ip: %s)\n", ch_server[id].name, account_id, ip);
+		else if( e_mail_check(new_email) == 0 )
+			ShowNotice("Char-server '%s': Attempt to modify an e-mail on an account (@email GM command) with a invalid new e-mail (account: %d, ip: %s)\n", ch_server[id].name, account_id, ip);
+		else if( strcmpi(new_email, "a@a.com") == 0 )
+			ShowNotice("Char-server '%s': Attempt to modify an e-mail on an account (@email GM command) with a default e-mail (account: %d, ip: %s)\n", ch_server[id].name, account_id, ip);
+		else if( !accounts->load_num(accounts, &acc, account_id) )
+			ShowNotice("Char-server '%s': Attempt to modify an e-mail on an account (@email GM command), but account doesn't exist (account: %d, ip: %s).\n", ch_server[id].name, account_id, ip);
+		else if( strcmpi(acc.email, actual_email) != 0 )
+			ShowNotice("Char-server '%s': Attempt to modify an e-mail on an account (@email GM command), but actual e-mail is incorrect (account: %d (%s), actual e-mail: %s, proposed e-mail: %s, ip: %s).\n", ch_server[id].name, account_id, acc.userid, acc.email, actual_email, ip);
+		else{
+			safestrncpy(acc.email, new_email, 40);
+			ShowNotice("Char-server '%s': Modify an e-mail on an account (@email GM command) (account: %d (%s), new e-mail: %s, ip: %s).\n", ch_server[id].name, account_id, acc.userid, new_email, ip);
+			// Save
+			accounts->save(accounts, &acc);
+		}
+	}
+	return 1;
+}
+
+/**
+ * Receiving an account state update request from a map-server (relayed via char-server).
+ * @param fd: fd to parse from (char-serv)
+ * @param id: id of char-serv
+ * @param ip: char-serv ip (used for info)
+ * @return 0 not enough info transmitted, 1 success
+ * TODO seems pretty damn close to logchrif_parse_reqbanacc
+ */
+int logchrif_parse_requpdaccstate(int fd, int id, char* ip){
+	if (RFIFOREST(fd) < 10)
+		return 0;
+	else{
+		struct mmo_account acc;
+
+		int account_id = RFIFOL(fd,2);
+		unsigned int state = RFIFOL(fd,6);
+		AccountDB* accounts = login_get_accounts_db();
+
+		RFIFOSKIP(fd,10);
+
+		if( !accounts->load_num(accounts, &acc, account_id) )
+			ShowNotice("Char-server '%s': Error of Status change (account: %d not found, suggested status %d, ip: %s).\n", ch_server[id].name, account_id, state, ip);
+		else if( acc.state == state )
+			ShowNotice("Char-server '%s':  Error of Status change - actual status is already the good status (account: %d, status %d, ip: %s).\n", ch_server[id].name, account_id, state, ip);
+		else{
+			ShowNotice("Char-server '%s': Status change (account: %d, new status %d, ip: %s).\n", ch_server[id].name, account_id, state, ip);
+
+			acc.state = state;
+			// Save
+			accounts->save(accounts, &acc);
+
+			// notify other servers
+			if (state != 0){
+				uint8 buf[11];
+				WBUFW(buf,0) = 0x2731;
+				WBUFL(buf,2) = account_id;
+				WBUFB(buf,6) = 0; // 0: change of state, 1: ban
+				WBUFL(buf,7) = state; // status or final date of a banishment
+				logchrif_sendallwos(-1, buf, 11);
+			}
+		}
+	}
+	return 1;
+}
+
+/**
+ * Receiving a ban request from map-server via char-server.
+ * @param fd: fd to parse from (char-serv)
+ * @param id: id of char-serv
+ * @param ip: char-serv ip (used for info)
+ * @return 0 not enough info transmitted, 1 success
+ * TODO check logchrif_parse_requpdaccstate for possible merge
+ */
+int logchrif_parse_reqbanacc(int fd, int id, char* ip){
+	if (RFIFOREST(fd) < 10)
+		return 0;
+	else{
+		struct mmo_account acc;
+		AccountDB* accounts = login_get_accounts_db();
+
+		int account_id = RFIFOL(fd,2);
+		int timediff = RFIFOL(fd,6);
+		RFIFOSKIP(fd,10);
+
+		if( !accounts->load_num(accounts, &acc, account_id) )
+			ShowNotice("Char-server '%s': Error of ban request (account: %d not found, ip: %s).\n", ch_server[id].name, account_id, ip);
+		else{
+			time_t timestamp;
+			if (acc.unban_time == 0 || acc.unban_time < time(NULL))
+				timestamp = time(NULL); // new ban
+			else
+				timestamp = acc.unban_time; // add to existing ban
+			timestamp += timediff;
+			if (timestamp == -1)
+				ShowNotice("Char-server '%s': Error of ban request (account: %d, invalid date, ip: %s).\n", ch_server[id].name, account_id, ip);
+			else if( timestamp <= time(NULL) || timestamp == 0 )
+				ShowNotice("Char-server '%s': Error of ban request (account: %d, new date unbans the account, ip: %s).\n", ch_server[id].name, account_id, ip);
+			else{
+				uint8 buf[11];
+				char tmpstr[24];
+				timestamp2string(tmpstr, sizeof(tmpstr), timestamp, login_config.date_format);
+				ShowNotice("Char-server '%s': Ban request (account: %d, new final date of banishment: %d (%s), ip: %s).\n", ch_server[id].name, account_id, timestamp, tmpstr, ip);
+
+				acc.unban_time = timestamp;
+
+				// Save
+				accounts->save(accounts, &acc);
+
+				WBUFW(buf,0) = 0x2731;
+				WBUFL(buf,2) = account_id;
+				WBUFB(buf,6) = 1; // 0: change of status, 1: ban
+				WBUFL(buf,7) = (uint32)timestamp; // status or final date of a banishment
+				logchrif_sendallwos(-1, buf, 11);
+			}
+		}
+	}
+	return 1;
+}
+
+/**
+ * Receiving a sex change request (sex is reversed).
+ * @param fd: fd to parse from (char-serv)
+ * @param id: id of char-serv
+ * @param ip: char-serv ip (used for info)
+ * @return 0 not enough info transmitted, 1 success
+ */
+int logchrif_parse_reqchgsex(int fd, int id, char* ip){
+	if( RFIFOREST(fd) < 6 )
+		return 0;
+	else{
+		struct mmo_account acc;
+		AccountDB* accounts = login_get_accounts_db();
+
+		int account_id = RFIFOL(fd,2);
+		RFIFOSKIP(fd,6);
+
+		if( !accounts->load_num(accounts, &acc, account_id) )
+			ShowNotice("Char-server '%s': Error of sex change (account: %d not found, ip: %s).\n", ch_server[id].name, account_id, ip);
+		else if( acc.sex == 'S' )
+			ShowNotice("Char-server '%s': Error of sex change - account to change is a Server account (account: %d, ip: %s).\n", ch_server[id].name, account_id, ip);
+		else{
+			unsigned char buf[7];
+			char sex = ( acc.sex == 'M' ) ? 'F' : 'M'; //Change gender
+
+			ShowNotice("Char-server '%s': Sex change (account: %d, new sex %c, ip: %s).\n", ch_server[id].name, account_id, sex, ip);
+
+			acc.sex = sex;
+			// Save
+			accounts->save(accounts, &acc);
+
+			// announce to other servers
+			WBUFW(buf,0) = 0x2723;
+			WBUFL(buf,2) = account_id;
+			WBUFB(buf,6) = sex_str2num(sex);
+			logchrif_sendallwos(-1, buf, 7);
+		}
+	}
+	return 1;
+}
+
+/**
+ * We receive account_reg2 from a char-server, and we send them to other char-servers.
+ * @param fd: fd to parse from (char-serv)
+ * @param id: id of char-serv
+ * @param ip: char-serv ip (used for info)
+ * @return 0 not enough info transmitted, 1 success
+ */
+int logchrif_parse_updreg2(int fd, int id, char* ip){
+	int j;
+	if( RFIFOREST(fd) < 4 || RFIFOREST(fd) < RFIFOW(fd,2) )
+		return 0;
+	else{
+		struct mmo_account acc;
+		AccountDB* accounts = login_get_accounts_db();
+		int account_id = RFIFOL(fd,4);
+
+		if( !accounts->load_num(accounts, &acc, account_id) )
+			ShowStatus("Char-server '%s': receiving (from the char-server) of account_reg2 (account: %d not found, ip: %s).\n", ch_server[id].name, account_id, ip);
+		else{
+			int len;
+			int p;
+			ShowNotice("char-server '%s': receiving (from the char-server) of account_reg2 (account: %d, ip: %s).\n", ch_server[id].name, account_id, ip);
+			for( j = 0, p = 13; j < ACCOUNT_REG2_NUM && p < RFIFOW(fd,2); ++j ){
+				sscanf((char*)RFIFOP(fd,p), "%31c%n", acc.account_reg2[j].str, &len);
+				acc.account_reg2[j].str[len]='\0';
+				p +=len+1; //+1 to skip the '\0' between strings.
+				sscanf((char*)RFIFOP(fd,p), "%255c%n", acc.account_reg2[j].value, &len);
+				acc.account_reg2[j].value[len]='\0';
+				p +=len+1;
+				remove_control_chars(acc.account_reg2[j].str);
+				remove_control_chars(acc.account_reg2[j].value);
+			}
+			acc.account_reg2_num = j;
+			// Save
+			accounts->save(accounts, &acc);
+			// Sending information towards the other char-servers.
+			RFIFOW(fd,0) = 0x2729;// reusing read buffer
+			logchrif_sendallwos(fd, RFIFOP(fd,0), RFIFOW(fd,2));
+		}
+		RFIFOSKIP(fd,RFIFOW(fd,2));
+	}
+	return 1;
+}
+
+/**
+ * Receiving an unban request from map-server via char-server.
+ * @param fd: fd to parse from (char-serv)
+ * @param id: id of char-serv
+ * @param ip: char-serv ip (used for info)
+ * @return 0 not enough info transmitted, 1 success
+ */
+int logchrif_parse_requnbanacc(int fd, int id, char* ip){
+	if( RFIFOREST(fd) < 6 )
+		return 0;
+	else{
+		struct mmo_account acc;
+		AccountDB* accounts = login_get_accounts_db();
+
+		int account_id = RFIFOL(fd,2);
+		RFIFOSKIP(fd,6);
+
+		if( !accounts->load_num(accounts, &acc, account_id) )
+			ShowNotice("Char-server '%s': Error of UnBan request (account: %d not found, ip: %s).\n", ch_server[id].name, account_id, ip);
+		else if( acc.unban_time == 0 )
+			ShowNotice("Char-server '%s': Error of UnBan request (account: %d, no change for unban date, ip: %s).\n", ch_server[id].name, account_id, ip);
+		else{
+			ShowNotice("Char-server '%s': UnBan request (account: %d, ip: %s).\n", ch_server[id].name, account_id, ip);
+			acc.unban_time = 0;
+			accounts->save(accounts, &acc);
+		}
+	}
+	return 1;
+}
+
+/**
+ * Set account_id to online.
+ * @author [Wizputer]
+ * @param fd: fd to parse from (char-serv)
+ * @param id: id of char-serv
+ * @return 0 not enough info transmitted, 1 success
+ */
+int logchrif_parse_setacconline(int fd, int id){
+	if( RFIFOREST(fd) < 6 )
+		return 0;
+	login_add_online_user(id, RFIFOL(fd,2));
+	RFIFOSKIP(fd,6);
+	return 1;
+}
+
+/**
+ * Set account_id to offline.
+ * @author  [Wizputer]
+ * @param fd: fd to parse from (char-serv)
+ * @return 0 not enough info transmitted, 1 success
+ */
+int logchrif_parse_setaccoffline(int fd){
+	if( RFIFOREST(fd) < 6 )
+		return 0;
+	login_remove_online_user(RFIFOL(fd,2));
+	RFIFOSKIP(fd,6);
+	return 1;
+}
+
+/**
+ * Receive list of all online accounts.
+ * @author  [Skotlex]
+ * @param fd: fd to parse from (char-serv)
+ * @param id: id of char-serv
+ * @return 0 not enough info transmitted, 1 success
+ */
+int logchrif_parse_updonlinedb(int fd, int id){
+	if (RFIFOREST(fd) < 4 || RFIFOREST(fd) < RFIFOW(fd,2))
+		return 0;
+	else{
+		uint32 i, users;
+		online_db->foreach(online_db, login_online_db_setoffline, id); //Set all chars from this char-server offline first
+		users = RFIFOW(fd,4);
+		for (i = 0; i < users; i++) {
+			int aid = RFIFOL(fd,6+i*4);
+			struct online_login_data *p = idb_ensure(online_db, aid, login_create_online_user);
+			p->char_server = id;
+			if (p->waiting_disconnect != INVALID_TIMER){
+				delete_timer(p->waiting_disconnect, login_waiting_disconnect_timer);
+				p->waiting_disconnect = INVALID_TIMER;
+			}
+		}
+		RFIFOSKIP(fd,RFIFOW(fd,2));
+	}
+	return 1;
+}
+
+/**
+ * Request account_reg2 for a character.
+ * @param fd: fd to parse from (char-serv)
+ * @return 0 not enough info transmitted, 1 success
+ */
+int logchrif_parse_reqacc2reg(int fd){
+	int j;
+	if (RFIFOREST(fd) < 10)
+		return 0;
+	else{
+		struct mmo_account acc;
+		AccountDB* accounts = login_get_accounts_db();
+		size_t off;
+
+		int account_id = RFIFOL(fd,2);
+		int char_id = RFIFOL(fd,6);
+		RFIFOSKIP(fd,10);
+
+		WFIFOHEAD(fd,ACCOUNT_REG2_NUM*sizeof(struct global_reg));
+		WFIFOW(fd,0) = 0x2729;
+		WFIFOL(fd,4) = account_id;
+		WFIFOL(fd,8) = char_id;
+		WFIFOB(fd,12) = 1; //Type 1 for Account2 registry
+
+		off = 13;
+		if( accounts->load_num(accounts, &acc, account_id) ){
+			for( j = 0; j < acc.account_reg2_num; j++ ){
+				if( acc.account_reg2[j].str[0] != '\0' ){
+					off += sprintf((char*)WFIFOP(fd,off), "%s", acc.account_reg2[j].str)+1; //We add 1 to consider the '\0' in place.
+					off += sprintf((char*)WFIFOP(fd,off), "%s", acc.account_reg2[j].value)+1;
+				}
+			}
+		}
+
+		WFIFOW(fd,2) = (uint16)off;
+		WFIFOSET(fd,WFIFOW(fd,2));
+	}
+	return 1;
+}
+
+/**
+ * Received new charip from char-serv, update information.
+ * @param fd: char-serv file descriptor
+ * @param id: char-serv id
+ * @return 0 not enough info transmitted, 1 success
+ */
+int logchrif_parse_updcharip(int fd, int id){
+	if( RFIFOREST(fd) < 6 )
+		return 0;
+	ch_server[id].ip = ntohl(RFIFOL(fd,2));
+	ShowInfo("Updated IP of Server #%d to %d.%d.%d.%d.\n",id, CONVIP(ch_server[id].ip));
+	RFIFOSKIP(fd,6);
+	return 1;
+}
+
+/**
+ * Request to set all accounts offline.
+ * @param fd: fd to parse from (char-serv)
+ * @param id: id of char-serv (char-serv)
+ * @return 1 success
+ */
+int logchrif_parse_setalloffline(int fd, int id){
+	ShowInfo("Setting accounts from char-server %d offline.\n", id);
+	online_db->foreach(online_db, login_online_db_setoffline, id);
+	RFIFOSKIP(fd,2);
+	return 1;
+}
+
+/**
+ * Request to change PIN Code for an account.
+ * @param fd: fd to parse from (char-serv)
+ * @return 0 fail (packet does not have enough data), 1 success
+ */
+int logchrif_parse_updpincode(int fd){
+	if( RFIFOREST(fd) < 11 )
+		return 0;
+	else{
+		struct mmo_account acc;
+		AccountDB* accounts = login_get_accounts_db();
+
+		if( accounts->load_num(accounts, &acc, RFIFOL(fd,2) ) ){
+			strncpy( acc.pincode, (char*)RFIFOP(fd,6), 5 );
+			acc.pincode_change = time( NULL );
+			accounts->save(accounts, &acc);
+		}
+		RFIFOSKIP(fd,11);
+	}
+	return 1;
+}
+
+/**
+ * PIN Code was incorrectly entered too many times.
+ * @param fd: fd to parse from (char-serv)
+ * @return 0 fail (packet does not have enough data), 1 success (continue parsing)
+ */
+int logchrif_parse_pincode_authfail(int fd){
+	if( RFIFOREST(fd) < 6 )
+		return 0;
+	else{
+		struct mmo_account acc;
+		AccountDB* accounts = login_get_accounts_db();
+		if( accounts->load_num(accounts, &acc, RFIFOL(fd,2) ) ){
+			struct online_login_data* ld;
+
+			ld = (struct online_login_data*)idb_get(online_db,acc.account_id);
+
+			if( ld == NULL )
+				return 0;
+
+			login_log( host2ip(acc.last_ip), acc.userid, 100, "PIN Code check failed" );
+		}
+		login_remove_online_user(acc.account_id);
+		RFIFOSKIP(fd,6);
+	}
+	return 1;
+}
+
+/**
+ * Request the bank info of login
+ * @param fd: fd to parse from (char-serv)
+ * @param id: char serv id
+ * @param ip: char-serv ip (used for info)
+ * @return 0 fail (packet does not have enough data), 1 success (continue parsing)
+ */
+int logchrif_parse_bankvault(int fd, int id, char* ip){
+	if( RFIFOREST(fd) < 11 )
+		return 0;
+	else {
+		struct mmo_account acc;
+
+
+		int account_id = RFIFOL(fd,2);
+		char type = RFIFOB(fd,6);
+		int32 data = RFIFOL(fd,7);
+		AccountDB* accounts = login_get_accounts_db();
+
+		RFIFOSKIP(fd,11);
+
+		if( !accounts->load_num(accounts, &acc, account_id) )
+			ShowNotice("Char-server '%s': Error on banking  (account: %d not found, ip: %s).\n", ch_server[id].name, account_id, ip);
+		else{
+			unsigned char buf[12];
+			if(type==2){ // upd and Save
+				acc.bank_vault = data;
+				accounts->save(accounts, &acc);
+				WBUFB(buf,10) = 1;
+			} else {
+				WBUFB(buf,10) = 0;
+			}
+			// announce to other servers
+			WBUFW(buf,0) = 0x2741;
+			WBUFL(buf,2) = account_id;
+			WBUFL(buf,6) = acc.bank_vault;
+			logchrif_sendallwos(-1, buf, 11);
+		}
+	}
+	return 1;
+}
+
+/**
+ * Received a vip data reqest from char
+ * type is the query to perform
+ *  &1 : Select info and update old_groupid
+ *  &2 : Update vip time
+ * @param fd link to charserv
+ * @return 0 missing data, 1 succeeded
+ */
+int logchrif_parse_reqvipdata(int fd) {
+#ifdef VIP_ENABLE
+	if( RFIFOREST(fd) < 15 )
+		return 0;
+	else { //request vip info
+		struct mmo_account acc;
+		AccountDB* accounts = login_get_accounts_db();
+		int aid = RFIFOL(fd,2);
+		int8 type = RFIFOB(fd,6);
+		int32 timediff = RFIFOL(fd,7);
+		int mapfd = RFIFOL(fd,11);
+		RFIFOSKIP(fd,15);
+		
+		if( accounts->load_num(accounts, &acc, aid ) ) {
+			time_t now = time(NULL);
+			time_t vip_time = acc.vip_time;
+			bool isvip = false;
+
+			if( acc.group_id > login_config.vip_sys.group ) { //Don't change group if it's higher.
+				logchrif_sendvipdata(fd,acc,false,true,mapfd);
+				return 1;
+			}
+			if( type&2 ) {
+				if(!vip_time)
+					vip_time = now; //new entry
+				vip_time += timediff; // set new duration
+			}
+			if( now < vip_time ) { //isvip
+				if(acc.group_id != login_config.vip_sys.group) //only upd this if we're not vip already
+					acc.old_group = acc.group_id;
+				acc.group_id = login_config.vip_sys.group;
+				acc.char_slots = login_config.char_per_account + login_config.vip_sys.char_increase;
+				isvip = true;
+			} else { //expired or @vip -xx
+				vip_time = 0;
+				if(acc.group_id == login_config.vip_sys.group) //prevent alteration in case account wasn't registered as vip yet
+					acc.group_id = acc.old_group;
+				acc.old_group = 0;
+				acc.char_slots = login_config.char_per_account;
+			}
+			acc.vip_time = vip_time;
+			accounts->save(accounts,&acc);
+			if( type&1 )
+				logchrif_sendvipdata(fd,acc,isvip,false,mapfd);
+		}
+	}
+#endif
+	return 1;
+}
+
+/**
+ * Entry point from char-server to log-server.
+ * Function that checks incoming command, then splits it to the correct handler.
+ * @param fd: file descriptor to parse, (link to char-serv)
+ * @return 0=invalid server,marked for disconnection,unknow packet; 1=success
+ */
+int logchrif_parse(int fd){
+	int cid; //char-serv id
+	uint32 ipl;
+	char ip[16];
+
+	ARR_FIND( 0, ARRAYLENGTH(ch_server), cid, ch_server[cid].fd == fd );
+	if( cid == ARRAYLENGTH(ch_server) ){// not a char server
+		ShowDebug("logchrif_parse: Disconnecting invalid session #%d (is not a char-server)\n", fd);
+		set_eof(fd);
+		do_close(fd);
+		return 0;
+	}
+
+	if( session[fd]->flag.eof ){
+		do_close(fd);
+		ch_server[cid].fd = -1;
+		logchrif_on_disconnect(cid);
+		return 0;
+	}
+
+	ipl = ch_server[cid].ip;
+	ip2str(ipl, ip);
+
+	while( RFIFOREST(fd) >= 2 ){
+		int next = 1;
+		uint16 command = RFIFOW(fd,0);
+		switch( command ){
+		case 0x2712: next = logchrif_parse_reqauth(fd, cid, ip); break;
+		case 0x2714: next = logchrif_parse_ackusercount(fd, cid); break;
+		case 0x2715: next = logchrif_parse_updmail(fd, cid, ip); break;
+		case 0x2716: next = logchrif_parse_reqaccdata(fd, cid, ip); break;
+		case 0x2719: next = logchrif_parse_keepalive(fd); break;
+		case 0x2722: next = logchrif_parse_reqchangemail(fd,cid,ip); break;
+		case 0x2724: next = logchrif_parse_requpdaccstate(fd,cid,ip); break;
+		case 0x2725: next = logchrif_parse_reqbanacc(fd,cid,ip); break;
+		case 0x2727: next = logchrif_parse_reqchgsex(fd,cid,ip); break;
+		case 0x2728: next = logchrif_parse_updreg2(fd,cid,ip); break;
+		case 0x272a: next = logchrif_parse_requnbanacc(fd,cid,ip); break;
+		case 0x272b: next = logchrif_parse_setacconline(fd,cid); break;
+		case 0x272c: next = logchrif_parse_setaccoffline(fd); break;
+		case 0x272d: next = logchrif_parse_updonlinedb(fd,cid); break;
+		case 0x272e: next = logchrif_parse_reqacc2reg(fd); break;
+		case 0x2736: next = logchrif_parse_updcharip(fd,cid); break;
+		case 0x2737: next = logchrif_parse_setalloffline(fd,cid); break;
+		case 0x2738: next = logchrif_parse_updpincode(fd); break;
+		case 0x2739: next = logchrif_parse_pincode_authfail(fd); break;
+		case 0x2740: next = logchrif_parse_bankvault(fd,cid,ip); break;
+		case 0x2742: next = logchrif_parse_reqvipdata(fd); break; //Vip sys
+		default:
+			ShowError("logchrif_parse: Unknown packet 0x%x from a char-server! Disconnecting!\n", command);
+			set_eof(fd);
+			return 0;
+		} // switch
+		if(next==0) return 0; // avoid processing of followup packets (prev was probably incomplete)
+	} // while
+	return 1; //or 0
+}
+
+
+
+
+/// Constructor destructor and signal handlers
+
+/**
+ * Initializes a server structure.
+ * @param id: id of char-serv (should be >0, FIXME)
+ */
+void logchrif_server_init(int id) {
+	memset(&ch_server[id], 0, sizeof(ch_server[id]));
+	ch_server[id].fd = -1;
+}
+
+/**
+ * Destroys a server structure.
+ * @param id: id of char-serv (should be >0, FIXME)
+ */
+void logchrif_server_destroy(int id){
+	if( ch_server[id].fd != -1 ) {
+		do_close(ch_server[id].fd);
+		ch_server[id].fd = -1;
+	}
+}
+
+/**
+ * Resets all the data related to a server.
+ *  Actually destroys then recreates the struct.
+ * @param id: id of char-serv (should be >0, FIXME)
+ */
+void logchrif_server_reset(int id) {
+	online_db->foreach(online_db, login_online_db_setoffline, id); //Set all chars from this char server to offline.
+	logchrif_server_destroy(id);
+	logchrif_server_init(id);
+}
+
+/**
+ * Called when the connection to Char Server is disconnected.
+ * @param id: id of char-serv (should be >0, FIXME)
+ */
+void logchrif_on_disconnect(int id) {
+	ShowStatus("Char-server '%s' has disconnected.\n", ch_server[id].name);
+	logchrif_server_reset(id);
+}
+
+/**
+ * loginchrif constructor
+ *  Initialisation, function called at start of the login-serv.
+ */
+void do_init_loginchrif(void){
+	int i;
+	for( i = 0; i < ARRAYLENGTH(ch_server); ++i )
+		logchrif_server_init(i);
+
+	// add timer to detect ip address change and perform update
+	if (login_config.ip_sync_interval) {
+		add_timer_func_list(logchrif_sync_ip_addresses, "sync_ip_addresses");
+		add_timer_interval(gettick() + login_config.ip_sync_interval, logchrif_sync_ip_addresses, 0, 0, login_config.ip_sync_interval);
+	}
+}
+
+/**
+ * Signal handler
+ *  This function attempts to properly close the server when an interrupt signal is received.
+ *  current signal catch : SIGTERM, SIGINT
+ */
+void do_shutdown_loginchrif(void){
+	int id;
+	for( id = 0; id < ARRAYLENGTH(ch_server); ++id )
+		logchrif_server_reset(id);
+}
+
+/**
+ * loginchrif destructor
+ *  dealloc..., function called at exit of the login-serv
+ */
+void do_final_loginchrif(void){
+	int i;
+	for( i = 0; i < ARRAYLENGTH(ch_server); ++i )
+		logchrif_server_destroy(i);
+}

+ 56 - 0
src/login/loginchrif.h

@@ -0,0 +1,56 @@
+/**
+ * @file loginchrif.h
+ * Module purpose is to handle incoming and outgoing requests with char-server.
+ * Licensed under GNU GPL.
+ *  For more information, see LICENCE in the main folder.
+ * @author Athena Dev Teams originally in login.c
+ * @author rAthena Dev Team
+ */
+
+#ifndef LOGINCHRIF_H
+#define	LOGINCHRIF_H
+
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
+/**
+ * Entry point from char-server to log-server.
+ * Function that checks incoming command, then splits it to the correct handler.
+ * @param fd: file descriptor to parse, (link to char-serv)
+ * @return 0=invalid server,marked for disconnection,unknow packet; 1=success
+ */
+int logchrif_parse(int fd);
+
+/**
+ * Packet send to all char-servers, except one. (wos: without our self)
+ * @param sfd: fd to discard sending to
+ * @param buf: packet to send in form of an array buffer
+ * @param len: size of packet
+ * @return : the number of char-serv the packet was sent to
+ */
+int logchrif_sendallwos(int sfd, uint8* buf, size_t len);
+
+/**
+ * loginchrif constructor
+ *  Initialisation, function called at start of the login-serv.
+ */
+void do_init_loginchrif(void);
+/**
+ * Signal handler
+ *  This function attempts to properly close the server when an interrupt signal is received.
+ *  current signal catch : SIGTERM, SIGINT
+ */
+void do_shutdown_loginchrif(void);
+/**
+ * loginchrif destructor
+ *  dealloc..., function called at exit of the login-serv
+ */
+void do_final_loginchrif(void);
+
+#ifdef	__cplusplus
+}
+#endif
+
+#endif	/* LOGINCHRIF_H */
+

+ 564 - 0
src/login/loginclif.c

@@ -0,0 +1,564 @@
+/**
+ * @file loginclif.c
+ * Module purpose is to handle incoming and outgoing requests with client.
+ * Licensed under GNU GPL.
+ *  For more information, see LICENCE in the main folder.
+ * @author Athena Dev Teams originally in login.c
+ * @author rAthena Dev Team
+ */
+
+#include "../common/timer.h" //difftick
+#include "../common/strlib.h" //safeprint
+#include "../common/showmsg.h" //show notice
+#include "../common/socket.h" //wfifo session
+#include "../common/malloc.h"
+#include "../common/utils.h"
+#include "../common/md5calc.h"
+#include "../common/random.h"
+#include "account.h"
+#include "ipban.h" //ipban_check
+#include "login.h"
+#include "loginlog.h"
+#include "loginclif.h"
+#include "loginchrif.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+/**
+ * Transmit auth result to client.
+ * @param fd: client file desciptor link
+ * @param result: result to transmit to client, see below
+ *  1 : Server closed
+ *  2 : Someone has already logged in with this id
+ *  8 : already online
+ * <result>.B (SC_NOTIFY_BAN)
+ */
+static void logclif_sent_auth_result(int fd,char result){
+	WFIFOHEAD(fd,3);
+	WFIFOW(fd,0) = 0x81;
+	WFIFOB(fd,2) = result;
+	WFIFOSET(fd,3);
+}
+
+/**
+ * Auth successful, inform client and create a temp auth_node.
+ * @param sd: player session
+ */
+static void logclif_auth_ok(struct login_session_data* sd) {
+	int fd = sd->fd;
+	uint32 ip = session[fd]->client_addr;
+
+	uint8 server_num, n;
+	uint32 subnet_char_ip;
+	struct auth_node* node;
+	int i;
+
+	if( runflag != LOGINSERVER_ST_RUNNING ){
+		// players can only login while running
+		logclif_sent_auth_result(fd,1); // server closed
+		return;
+	}
+
+	if( login_config.group_id_to_connect >= 0 && sd->group_id != login_config.group_id_to_connect ) {
+		ShowStatus("Connection refused: the required group id for connection is %d (account: %s, group: %d).\n", login_config.group_id_to_connect, sd->userid, sd->group_id);
+		logclif_sent_auth_result(fd,1); // server closed
+		return;
+	} else if( login_config.min_group_id_to_connect >= 0 && login_config.group_id_to_connect == -1 && sd->group_id < login_config.min_group_id_to_connect ) {
+		ShowStatus("Connection refused: the minimum group id required for connection is %d (account: %s, group: %d).\n", login_config.min_group_id_to_connect, sd->userid, sd->group_id);
+		logclif_sent_auth_result(fd,1); // server closed
+		return;
+	}
+
+	server_num = 0;
+	for( i = 0; i < ARRAYLENGTH(ch_server); ++i )
+		if( session_isActive(ch_server[i].fd) )
+			server_num++;
+
+	if( server_num == 0 )
+	{// if no char-server, don't send void list of servers, just disconnect the player with proper message
+		ShowStatus("Connection refused: there is no char-server online (account: %s).\n", sd->userid);
+		logclif_sent_auth_result(fd,1); // server closed
+		return;
+	}
+
+	{
+		struct online_login_data* data = (struct online_login_data*)idb_get(online_db, sd->account_id);
+		if( data )
+		{// account is already marked as online!
+			if( data->char_server > -1 )
+			{// Request char servers to kick this account out. [Skotlex]
+				uint8 buf[6];
+				ShowNotice("User '%s' is already online - Rejected.\n", sd->userid);
+				WBUFW(buf,0) = 0x2734;
+				WBUFL(buf,2) = sd->account_id;
+				logchrif_sendallwos(-1, buf, 6);
+				if( data->waiting_disconnect == INVALID_TIMER )
+					data->waiting_disconnect = add_timer(gettick()+AUTH_TIMEOUT, login_waiting_disconnect_timer, sd->account_id, 0);
+				logclif_sent_auth_result(fd,8); // 08 = Server still recognizes your last login
+				return;
+			}
+			else
+			if( data->char_server == -1 )
+			{// client has authed but did not access char-server yet
+				// wipe previous session
+				idb_remove(auth_db, sd->account_id);
+				login_remove_online_user(sd->account_id);
+				data = NULL;
+			}
+		}
+	}
+
+	login_log(ip, sd->userid, 100, "login ok");
+	ShowStatus("Connection of the account '%s' accepted.\n", sd->userid);
+
+	WFIFOHEAD(fd,47+32*server_num);
+	WFIFOW(fd,0) = 0x69;
+	WFIFOW(fd,2) = 47+32*server_num;
+	WFIFOL(fd,4) = sd->login_id1;
+	WFIFOL(fd,8) = sd->account_id;
+	WFIFOL(fd,12) = sd->login_id2;
+	WFIFOL(fd,16) = 0; // in old version, that was for ip (not more used)
+	//memcpy(WFIFOP(fd,20), sd->lastlogin, 24); // in old version, that was for name (not more used)
+	memset(WFIFOP(fd,20), 0, 24);
+	WFIFOW(fd,44) = 0; // unknown
+	WFIFOB(fd,46) = sex_str2num(sd->sex);
+	for( i = 0, n = 0; i < ARRAYLENGTH(ch_server); ++i ) {
+		if( !session_isValid(ch_server[i].fd) )
+			continue;
+		subnet_char_ip = lan_subnetcheck(ip); // Advanced subnet check [LuzZza]
+		WFIFOL(fd,47+n*32) = htonl((subnet_char_ip) ? subnet_char_ip : ch_server[i].ip);
+		WFIFOW(fd,47+n*32+4) = ntows(htons(ch_server[i].port)); // [!] LE byte order here [!]
+		memcpy(WFIFOP(fd,47+n*32+6), ch_server[i].name, 20);
+		WFIFOW(fd,47+n*32+26) = ch_server[i].users;
+		WFIFOW(fd,47+n*32+28) = ch_server[i].type;
+		WFIFOW(fd,47+n*32+30) = ch_server[i].new_;
+		n++;
+	}
+	WFIFOSET(fd,47+32*server_num);
+
+	// create temporary auth entry
+	CREATE(node, struct auth_node, 1);
+	node->account_id = sd->account_id;
+	node->login_id1 = sd->login_id1;
+	node->login_id2 = sd->login_id2;
+	node->sex = sd->sex;
+	node->ip = ip;
+	node->version = sd->version;
+	node->clienttype = sd->clienttype;
+	idb_put(auth_db, sd->account_id, node);
+	{
+		struct online_login_data* data;
+		// mark client as 'online'
+		data = login_add_online_user(-1, sd->account_id);
+		// schedule deletion of this node
+		data->waiting_disconnect = add_timer(gettick()+AUTH_TIMEOUT, login_waiting_disconnect_timer, sd->account_id, 0);
+	}
+}
+
+/**
+ * Inform client that auth has failed.
+ * @param sd: player session
+ * @param result: nb (msg define in conf)
+    0 = Unregistered ID
+    1 = Incorrect Password
+    2 = This ID is expired
+    3 = Rejected from Server
+    4 = You have been blocked by the GM Team
+    5 = Your Game's EXE file is not the latest version
+    6 = Your are Prohibited to log in until %s
+    7 = Server is jammed due to over populated
+    8 = No more accounts may be connected from this company
+    9 = MSI_REFUSE_BAN_BY_DBA
+    10 = MSI_REFUSE_EMAIL_NOT_CONFIRMED
+    11 = MSI_REFUSE_BAN_BY_GM
+    12 = MSI_REFUSE_TEMP_BAN_FOR_DBWORK
+    13 = MSI_REFUSE_SELF_LOCK
+    14 = MSI_REFUSE_NOT_PERMITTED_GROUP
+    15 = MSI_REFUSE_NOT_PERMITTED_GROUP
+    99 = This ID has been totally erased
+    100 = Login information remains at %s
+    101 = Account has been locked for a hacking investigation. Please contact the GM Team for more information
+    102 = This account has been temporarily prohibited from login due to a bug-related investigation
+    103 = This character is being deleted. Login is temporarily unavailable for the time being
+    104 = This character is being deleted. Login is temporarily unavailable for the time being
+     default = Unknown Error.
+ */
+static void logclif_auth_failed(struct login_session_data* sd, int result) {
+	int fd = sd->fd;
+	uint32 ip = session[fd]->client_addr;
+
+	if (login_config.log_login)
+	{
+		if(result >= 0 && result <= 15)
+		    login_log(ip, sd->userid, result, msg_txt(result));
+		else if(result >= 99 && result <= 104)
+		    login_log(ip, sd->userid, result, msg_txt(result-83)); //-83 offset
+		else
+		    login_log(ip, sd->userid, result, msg_txt(22)); //unknow error
+	}
+
+	if( result == 1 && login_config.dynamic_pass_failure_ban )
+		ipban_log(ip); // log failed password attempt
+
+//#if PACKETVER >= 20120000 /* not sure when this started */
+	if( sd->version >= date2version(20120000) ){ /* not sure when this started */
+		WFIFOHEAD(fd,26);
+		WFIFOW(fd,0) = 0x83e;
+		WFIFOL(fd,2) = result;
+		if( result != 6 )
+			memset(WFIFOP(fd,6), '\0', 20);
+		else { // 6 = Your are Prohibited to log in until %s
+			struct mmo_account acc;
+			AccountDB* accounts = login_get_accounts_db();
+			time_t unban_time = ( accounts->load_str(accounts, &acc, sd->userid) ) ? acc.unban_time : 0;
+			timestamp2string((char*)WFIFOP(fd,6), 20, unban_time, login_config.date_format);
+		}
+		WFIFOSET(fd,26);
+	}
+//#else	
+	else {
+		WFIFOHEAD(fd,23);
+		WFIFOW(fd,0) = 0x6a;
+		WFIFOB(fd,2) = (uint8)result;
+		if( result != 6 )
+			memset(WFIFOP(fd,3), '\0', 20);
+		else { // 6 = Your are Prohibited to log in until %s
+			struct mmo_account acc;
+			AccountDB* accounts = login_get_accounts_db();
+			time_t unban_time = ( accounts->load_str(accounts, &acc, sd->userid) ) ? acc.unban_time : 0;
+			timestamp2string((char*)WFIFOP(fd,3), 20, unban_time, login_config.date_format);
+		}
+		WFIFOSET(fd,23);
+	}
+//#endif	
+}
+
+/**
+ * Received a keepalive packet to maintain connection.
+ * 0x200 <account.userid>.24B.
+ * @param fd: fd to parse from (client fd)
+ * @return 0 not enough info transmitted, 1 success
+ */
+static int logclif_parse_keepalive(int fd){
+	if (RFIFOREST(fd) < 26)
+		return 0;
+	RFIFOSKIP(fd,26);
+	return 1;
+}
+
+/**
+ * Received a keepalive packet to maintain connection.
+ * S 0204 <md5 hash>.16B (kRO 2004-05-31aSakexe langtype 0 and 6)
+ * @param fd: fd to parse from (client fd)
+ * @return 0 not enough info transmitted, 1 success
+ */
+static int logclif_parse_updclhash(int fd, struct login_session_data *sd){
+	if (RFIFOREST(fd) < 18)
+		return 0;
+	sd->has_client_hash = 1;
+	memcpy(sd->client_hash, RFIFOP(fd, 2), 16);
+	RFIFOSKIP(fd,18);
+	return 1;
+}
+
+/**
+ * Received a connection request.
+ * @param fd: file descriptor to parse from (client)
+ * @param sd: client session
+ * @param command: packet type sent
+ * @param ip: ipv4 address (client)
+ *  S 0064 <version>.L <username>.24B <password>.24B <clienttype>.B
+ *  S 0277 <version>.L <username>.24B <password>.24B <clienttype>.B <ip address>.16B <adapter address>.13B
+ *  S 02b0 <version>.L <username>.24B <password>.24B <clienttype>.B <ip address>.16B <adapter address>.13B <g_isGravityID>.B
+ *  S 01dd <version>.L <username>.24B <password hash>.16B <clienttype>.B
+ *  S 01fa <version>.L <username>.24B <password hash>.16B <clienttype>.B <?>.B(index of the connection in the clientinfo file (+10 if the command-line contains "pc"))
+ *  S 027c <version>.L <username>.24B <password hash>.16B <clienttype>.B <?>.13B(junk)
+ *  S 0825 <packetsize>.W <version>.L <clienttype>.B <userid>.24B <password>.27B <mac>.17B <ip>.15B <token>.(packetsize - 0x5C)B
+ * @param fd: fd to parse from (client fd)
+ * @return 0 failure, 1 success
+ */
+static int logclif_parse_reqauth(int fd, struct login_session_data *sd, int command, char* ip){
+	size_t packet_len = RFIFOREST(fd);
+
+	if( (command == 0x0064 && packet_len < 55)
+	||  (command == 0x0277 && packet_len < 84)
+	||  (command == 0x02b0 && packet_len < 85)
+	||  (command == 0x01dd && packet_len < 47)
+	||  (command == 0x01fa && packet_len < 48)
+	||  (command == 0x027c && packet_len < 60)
+	||  (command == 0x0825 && (packet_len < 4 || packet_len < RFIFOW(fd, 2))) )
+		return 0;
+	else {
+		int result;
+		uint32 version;
+		char username[NAME_LENGTH];
+		char password[PASSWD_LENGTH];
+		unsigned char passhash[16];
+		uint8 clienttype;
+		bool israwpass = (command==0x0064 || command==0x0277 || command==0x02b0 || command == 0x0825);
+
+		// Shinryo: For the time being, just use token as password.
+		if(command == 0x0825) {
+			char *accname = (char *)RFIFOP(fd, 9);
+			char *token = (char *)RFIFOP(fd, 0x5C);
+			size_t uAccLen = strlen(accname);
+			size_t uTokenLen = RFIFOREST(fd) - 0x5C;
+
+			version = RFIFOL(fd,4);
+
+			if(uAccLen > NAME_LENGTH - 1 || uAccLen <= 0 || uTokenLen > NAME_LENGTH - 1  || uTokenLen <= 0)
+			{
+				logclif_auth_failed(sd, 3);
+				return 0;
+			}
+
+			safestrncpy(username, accname, uAccLen + 1);
+			safestrncpy(password, token, uTokenLen + 1);
+			clienttype = RFIFOB(fd, 8);
+		}
+		else
+		{
+			version = RFIFOL(fd,2);
+			safestrncpy(username, (const char*)RFIFOP(fd,6), NAME_LENGTH);
+			if( israwpass )
+			{
+				safestrncpy(password, (const char*)RFIFOP(fd,30), PASSWD_LENGTH);
+				clienttype = RFIFOB(fd,54);
+			}
+			else
+			{
+				memcpy(passhash, RFIFOP(fd,30), 16);
+				clienttype = RFIFOB(fd,46);
+			}
+		}
+		RFIFOSKIP(fd,RFIFOREST(fd)); // assume no other packet was sent
+
+		sd->clienttype = clienttype;
+		sd->version = version;
+		safestrncpy(sd->userid, username, NAME_LENGTH);
+		if( israwpass )
+		{
+			ShowStatus("Request for connection of %s (ip: %s) version=%d\n", sd->userid, ip,sd->version);
+			safestrncpy(sd->passwd, password, NAME_LENGTH);
+			if( login_config.use_md5_passwds )
+				MD5_String(sd->passwd, sd->passwd);
+			sd->passwdenc = 0;
+		}
+		else
+		{
+			ShowStatus("Request for connection (passwdenc mode) of %s (ip: %s) version=%d\n", sd->userid, ip,sd->version);
+			bin2hex(sd->passwd, passhash, 16); // raw binary data here!
+			sd->passwdenc = PASSWORDENC;
+		}
+
+		if( sd->passwdenc != 0 && login_config.use_md5_passwds )
+		{
+			logclif_auth_failed(sd, 3); // send "rejected from server"
+			return 0;
+		}
+
+		result = login_mmo_auth(sd, false);
+
+		if( result == -1 )
+			logclif_auth_ok(sd);
+		else
+			logclif_auth_failed(sd, result);
+	}
+	return 1;
+}
+
+/**
+ * Client requests an md5key for his session: keys will be generated and sent back.
+ * @param fd: file descriptor to parse from (client)
+ * @param sd: client session
+ * @return 1 success
+ */
+static int logclif_parse_reqkey(int fd, struct login_session_data *sd){
+	RFIFOSKIP(fd,2);
+	{
+		memset(sd->md5key, '\0', sizeof(sd->md5key));
+		sd->md5keylen = (uint16)(12 + rnd() % 4);
+		MD5_Salt(sd->md5keylen, sd->md5key);
+
+		WFIFOHEAD(fd,4 + sd->md5keylen);
+		WFIFOW(fd,0) = 0x01dc;
+		WFIFOW(fd,2) = 4 + sd->md5keylen;
+		memcpy(WFIFOP(fd,4), sd->md5key, sd->md5keylen);
+		WFIFOSET(fd,WFIFOW(fd,2));
+	}
+	return 1;
+}
+
+/**
+ * Char-server request to connect to the login-server.
+ * This is needed to exchange packets.
+ * @param fd: file descriptor to parse from (client)
+ * @param sd: client session
+ * @param ip: ipv4 address (client)
+ * @return 0 packet received too shirt, 1 success
+ */
+static int logclif_parse_reqcharconnec(int fd, struct login_session_data *sd, char* ip){
+	if (RFIFOREST(fd) < 86)
+		return 0;
+	{
+		int result;
+		char server_name[20];
+		char message[256];
+		uint32 server_ip;
+		uint16 server_port;
+		uint16 type;
+		uint16 new_;
+
+		safestrncpy(sd->userid, (char*)RFIFOP(fd,2), NAME_LENGTH);
+		safestrncpy(sd->passwd, (char*)RFIFOP(fd,26), NAME_LENGTH);
+		if( login_config.use_md5_passwds )
+			MD5_String(sd->passwd, sd->passwd);
+		sd->passwdenc = 0;
+		sd->version = login_config.client_version_to_connect; // hack to skip version check
+		server_ip = ntohl(RFIFOL(fd,54));
+		server_port = ntohs(RFIFOW(fd,58));
+		safestrncpy(server_name, (char*)RFIFOP(fd,60), 20);
+		type = RFIFOW(fd,82);
+		new_ = RFIFOW(fd,84);
+		RFIFOSKIP(fd,86);
+
+		ShowInfo("Connection request of the char-server '%s' @ %u.%u.%u.%u:%u (account: '%s', pass: '%s', ip: '%s')\n", server_name, CONVIP(server_ip), server_port, sd->userid, sd->passwd, ip);
+		sprintf(message, "charserver - %s@%u.%u.%u.%u:%u", server_name, CONVIP(server_ip), server_port);
+		login_log(session[fd]->client_addr, sd->userid, 100, message);
+
+		result = login_mmo_auth(sd, true);
+		if( runflag == LOGINSERVER_ST_RUNNING &&
+			result == -1 &&
+			sd->sex == 'S' &&
+			sd->account_id >= 0 && sd->account_id < ARRAYLENGTH(ch_server) &&
+			!session_isValid(ch_server[sd->account_id].fd) )
+		{
+			ShowStatus("Connection of the char-server '%s' accepted.\n", server_name);
+			safestrncpy(ch_server[sd->account_id].name, server_name, sizeof(ch_server[sd->account_id].name));
+			ch_server[sd->account_id].fd = fd;
+			ch_server[sd->account_id].ip = server_ip;
+			ch_server[sd->account_id].port = server_port;
+			ch_server[sd->account_id].users = 0;
+			ch_server[sd->account_id].type = type;
+			ch_server[sd->account_id].new_ = new_;
+
+			session[fd]->func_parse = logchrif_parse;
+			session[fd]->flag.server = 1;
+			realloc_fifo(fd, FIFOSIZE_SERVERLINK, FIFOSIZE_SERVERLINK);
+
+			// send connection success
+			WFIFOHEAD(fd,3);
+			WFIFOW(fd,0) = 0x2711;
+			WFIFOB(fd,2) = 0;
+			WFIFOSET(fd,3);
+		}
+		else
+		{
+			ShowNotice("Connection of the char-server '%s' REFUSED.\n", server_name);
+			WFIFOHEAD(fd,3);
+			WFIFOW(fd,0) = 0x2711;
+			WFIFOB(fd,2) = 3;
+			WFIFOSET(fd,3);
+		}
+	}
+	return 1;
+}
+
+/**
+ * Entry point from client to log-server.
+ * Function that checks incoming command, then splits it to the correct handler.
+ * @param fd: file descriptor to parse, (link to client)
+ * @return 0=invalid session,marked for disconnection,unknow packet, banned..; 1=success
+ */
+int logclif_parse(int fd) {
+	struct login_session_data* sd = (struct login_session_data*)session[fd]->session_data;
+
+	char ip[16];
+	uint32 ipl = session[fd]->client_addr;
+	ip2str(ipl, ip);
+
+	if( session[fd]->flag.eof )
+	{
+		ShowInfo("Closed connection from '"CL_WHITE"%s"CL_RESET"'.\n", ip);
+		do_close(fd);
+		return 0;
+	}
+
+	if( sd == NULL )
+	{
+		// Perform ip-ban check
+		if( login_config.ipban && ipban_check(ipl) )
+		{
+			ShowStatus("Connection refused: IP isn't authorised (deny/allow, ip: %s).\n", ip);
+			login_log(ipl, "unknown", -3, "ip banned");
+			WFIFOHEAD(fd,23);
+			WFIFOW(fd,0) = 0x6a;
+			WFIFOB(fd,2) = 3; // 3 = Rejected from Server
+			WFIFOSET(fd,23);
+			set_eof(fd);
+			return 0;
+		}
+		// create a session for this new connection
+		CREATE(session[fd]->session_data, struct login_session_data, 1);
+		sd = (struct login_session_data*)session[fd]->session_data;
+		sd->fd = fd;
+	}
+
+	while( RFIFOREST(fd) >= 2 )
+	{
+		uint16 command = RFIFOW(fd,0);
+		int next=1;
+
+		switch( command )
+		{
+		// New alive packet: used to verify if client is always alive.
+		case 0x0200: next = logclif_parse_keepalive(fd); break;
+		// client md5 hash (binary)
+		case 0x0204: next = logclif_parse_updclhash(fd,sd); break;
+		// request client login (raw password)
+		case 0x0064: // S 0064 <version>.L <username>.24B <password>.24B <clienttype>.B
+		case 0x0277: // S 0277 <version>.L <username>.24B <password>.24B <clienttype>.B <ip address>.16B <adapter address>.13B
+		case 0x02b0: // S 02b0 <version>.L <username>.24B <password>.24B <clienttype>.B <ip address>.16B <adapter address>.13B <g_isGravityID>.B
+		// request client login (md5-hashed password)
+		case 0x01dd: // S 01dd <version>.L <username>.24B <password hash>.16B <clienttype>.B
+		case 0x01fa: // S 01fa <version>.L <username>.24B <password hash>.16B <clienttype>.B <?>.B(index of the connection in the clientinfo file (+10 if the command-line contains "pc"))
+		case 0x027c: // S 027c <version>.L <username>.24B <password hash>.16B <clienttype>.B <?>.13B(junk)
+		case 0x0825: // S 0825 <packetsize>.W <version>.L <clienttype>.B <userid>.24B <password>.27B <mac>.17B <ip>.15B <token>.(packetsize - 0x5C)B
+			next = logclif_parse_reqauth(fd,  sd, command, ip); 
+			break;
+		// Sending request of the coding key
+		case 0x01db: next = logclif_parse_reqkey(fd, sd); break;
+		// Connection request of a char-server
+		case 0x2710: logclif_parse_reqcharconnec(fd,sd, ip); return 0; // processing will continue elsewhere
+		default:
+			ShowNotice("Abnormal end of connection (ip: %s): Unknown packet 0x%x\n", ip, command);
+			set_eof(fd);
+			return 0;
+		}
+		if(next==0) return 0; // avoid processing of followup packets (prev was probably incomplete)
+	}
+
+	return 0;
+}
+
+
+
+/// Constructor destructor
+
+/**
+ * Initialize the module.
+ * Launched at login-serv start, create db or other long scope variable here.
+ */
+void do_init_loginclif(void){
+	return;
+}
+
+/**
+ * loginclif destructor
+ *  dealloc..., function called at exit of the login-serv
+ */
+void do_final_loginclif(void){
+	return;
+}

+ 42 - 0
src/login/loginclif.h

@@ -0,0 +1,42 @@
+/**
+ * @file loginclif.h
+ * Module purpose is to handle incoming and outgoing requests with client.
+ * Licensed under GNU GPL.
+ *  For more information, see LICENCE in the main folder.
+ * @author Athena Dev Teams originally in login.c
+ * @author rAthena Dev Team
+ */
+
+#ifndef _LOGINCLIF_H
+#define	_LOGINCLIF_H
+
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
+/**
+ * Entry point from client to log-server.
+ * Function that checks incoming command, then splits it to the correct handler.
+ * @param fd: file descriptor to parse, (link to client)
+ * @return 0=invalid session,marked for disconnection,unknow packet, banned..; 1=success
+ */
+int logclif_parse(int fd);
+
+/**
+ * Initialize the module.
+ * Launched at login-serv start, create db or other long scope variable here.
+ */
+void do_init_loginclif(void);
+
+/**
+ * loginclif destructor
+ *  dealloc..., function called at exit of the login-serv
+ */
+void do_final_loginclif(void);
+
+#ifdef	__cplusplus
+}
+#endif
+
+#endif	/* _LOGINCLIF_H */
+

+ 176 - 0
src/login/logincnslif.c

@@ -0,0 +1,176 @@
+/**
+ * @file logincnslif.c
+ * Module purpose is to handle incoming and outgoing requests with console.
+ * Licensed under GNU GPL.
+ *  For more information, see LICENCE in the main folder.
+ * @author Athena Dev Teams originally in login.c
+ * @author rAthena Dev Team
+ */
+
+#include "../common/mmo.h" //cbasetype + NAME_LENGTH
+#include "../common/showmsg.h" //show notice
+#include "../common/md5calc.h"
+#include "../common/ers.h"
+#include "../common/cli.h"
+#include "../common/timer.h"
+#include "../common/strlib.h"
+#include "login.h"
+#include "logincnslif.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+/**
+ * Login-server console help: starting option info.
+ *  Do not rename function used as extern.
+ * @param do_exit: terminate program execution ?
+ */
+void display_helpscreen(bool do_exit) {
+	ShowInfo("Usage: %s [options]\n", SERVER_NAME);
+	ShowInfo("\n");
+	ShowInfo("Options:\n");
+	ShowInfo("  -?, -h [--help]\t\tDisplays this help screen.\n");
+	ShowInfo("  -v [--version]\t\tDisplays the server's version.\n");
+	ShowInfo("  --run-once\t\t\tCloses server after loading (testing).\n");
+	ShowInfo("  --login-config <file>\t\tAlternative login-server configuration.\n");
+	ShowInfo("  --lan-config <file>\t\tAlternative lag configuration.\n");
+	ShowInfo("  --msg-config <file>\t\tAlternative message configuration.\n");
+	if( do_exit )
+		exit(EXIT_SUCCESS);
+}
+
+/**
+ * Read the option specified in command line
+ *  and assign the confs used by the different server.
+ * @param argc:
+ * @param argv:
+ * @return true or Exit on failure.
+ */
+int logcnslif_get_options(int argc, char ** argv) {
+	int i = 0;
+	for (i = 1; i < argc; i++) {
+		const char* arg = argv[i];
+
+		if (arg[0] != '-' && (arg[0] != '/' || arg[1] == '-')) {// -, -- and /
+			ShowError("Unknown option '%s'.\n", argv[i]);
+			exit(EXIT_FAILURE);
+		} else if ((++arg)[0] == '-') {// long option
+			arg++;
+
+			if (strcmp(arg, "help") == 0) {
+				display_helpscreen(true);
+			} else if (strcmp(arg, "version") == 0) {
+				display_versionscreen(true);
+			} else if (strcmp(arg, "run-once") == 0){ // close the map-server as soon as its done.. for testing [Celest]
+				runflag = CORE_ST_STOP;
+			} else if (SERVER_TYPE & (ATHENA_SERVER_LOGIN)) { //login
+				if (strcmp(arg, "lan-config") == 0) {
+					if (opt_has_next_value(arg, i, argc)) safestrncpy(login_config.lanconf_name, argv[++i], sizeof(login_config.lanconf_name));
+				}
+				if (strcmp(arg, "login-config") == 0) {
+					if (opt_has_next_value(arg, i, argc)) safestrncpy(login_config.loginconf_name, argv[++i], sizeof(login_config.loginconf_name));
+				}
+				if (strcmp(arg, "msg-config") == 0) {
+					if (opt_has_next_value(arg, i, argc)) safestrncpy(login_config.msgconf_name, argv[++i], sizeof(login_config.msgconf_name));
+				} else {
+					ShowError("Unknown option '%s'.\n", argv[i]);
+					exit(EXIT_FAILURE);
+				}
+			}
+		} else switch (arg[0]) {// short option
+			case '?':
+			case 'h':
+				display_helpscreen(true);
+				break;
+			case 'v':
+				display_versionscreen(true);
+				break;
+			default:
+				ShowError("Unknown option '%s'.\n", argv[i]);
+				exit(EXIT_FAILURE);
+		}
+	}
+	return 1;
+}
+
+/**
+ * Console Command Parser
+ * Transmited from command cli.c
+ * note common name for all serv do not rename (extern in cli)
+ * @author [Wizputer]
+ * @param buf: buffer to parse, (from console)
+ * @return 1=success
+ */
+int cnslif_parse(const char* buf){
+	char type[64];
+	char command[64];
+	int n=0;
+
+	if( ( n = sscanf(buf, "%127[^:]:%255[^\n\r]", type, command) ) < 2 ){
+		if((n = sscanf(buf, "%63[^\n]", type))<1) return -1; //nothing to do no arg
+	}
+	if( n != 2 ){ //end string
+		ShowNotice("Type: '%s'\n",type);
+		command[0] = '\0';
+	}
+	else
+		ShowNotice("Type of command: '%s' || Command: '%s'\n",type,command);
+
+	if( n == 2 ){
+		if(strcmpi("server", type) == 0 ){
+			if( strcmpi("shutdown", command) == 0 || strcmpi("exit", command) == 0 || strcmpi("quit", command) == 0 ){
+				runflag = 0;
+			}
+			else if( strcmpi("alive", command) == 0 || strcmpi("status", command) == 0 )
+				ShowInfo(CL_CYAN"Console: "CL_BOLD"I'm Alive."CL_RESET"\n");
+		}
+		if( strcmpi("create",type) == 0 )
+		{
+			char username[NAME_LENGTH], password[NAME_LENGTH], md5password[32+1], sex; //23+1 plaintext 32+1 md5
+			bool md5 = 0;
+			if( sscanf(command, "%23s %23s %c", username, password, &sex) < 3 || strnlen(username, sizeof(username)) < 4 || strnlen(password, sizeof(password)) < 1 ){
+				ShowWarning("Console: Invalid parameters for '%s'. Usage: %s <username> <password> <sex:F/M>\n", type, type);
+				return 0;
+			}
+			if( login_config.use_md5_passwds ){
+				MD5_String(password,md5password);
+				md5 = 1;
+			}
+			if( login_mmo_auth_new(username,(md5?md5password:password), TOUPPER(sex), "0.0.0.0") != -1 ){
+				ShowError("Console: Account creation failed.\n");
+				return 0;
+			}
+			ShowStatus("Console: Account '%s' created successfully.\n", username);
+		}
+	}
+	else if( strcmpi("ers_report", type) == 0 ){
+		ers_report();
+	}
+	else if( strcmpi("help", type) == 0 ){
+		ShowInfo("Available commands:\n");
+		ShowInfo("\t server:shutdown => Stops the server.\n");
+		ShowInfo("\t server:alive => Checks if the server is running.\n");
+		ShowInfo("\t ers_report => Displays database usage.\n");
+		ShowInfo("\t create:<username> <password> <sex:M|F> => Creates a new account.\n");
+	}
+	return 1;
+}
+
+/**
+ * Initialize the module.
+ * Launched at login-serv start, create db or other long scope variable here.
+ */
+void do_init_logincnslif(void){
+	if( login_config.console ) {
+		add_timer_func_list(parse_console_timer, "parse_console_timer");
+		add_timer_interval(gettick()+1000, parse_console_timer, 0, 0, 1000); //start in 1s each 1sec
+	}
+}
+
+/**
+ * Handler to cleanup module, called when login-server stops.
+ */
+void do_final_logincnslif(void){
+	return;
+}

+ 58 - 0
src/login/logincnslif.h

@@ -0,0 +1,58 @@
+/**
+ * @file logincnslif.h
+ * Module purpose is to handle incoming and outgoing requests with console.
+ * Licensed under GNU GPL.
+ *  For more information, see LICENCE in the main folder.
+ * @author Athena Dev Teams originally in login.c
+ * @author rAthena Dev Team
+ */
+
+#ifndef CONSOLEIF_H
+#define	CONSOLEIF_H
+
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
+/**
+ * Console Command Parser
+ * Transmited from command cli.c
+ * note common name for all serv do not rename (extern in cli)
+ * @author [Wizputer]
+ * @param buf: buffer to parse, (from console)
+ * @return 1=success
+ */
+int cnslif_parse(const char* buf);
+
+/**
+ * Read the option specified in command line
+ *  and assign the confs used by the different server.
+ * @param argc:
+ * @param argv:
+ * @return true or Exit on failure.
+ */
+int logcnslif_get_options(int argc, char ** argv);
+
+/**
+ * Login-server console help: starting option info.
+ *  Do not rename function used as extern.
+ * @param do_exit: terminate program execution ?
+ */
+void display_helpscreen(bool do_exit);
+
+/**
+ * Initialize the module.
+ * Launched at login-serv start, create db or other long scope variable here.
+ */
+void do_init_logincnslif(void);
+/**
+ * Handler to cleanup module, called when login-server stops.
+ */
+void do_final_logincnslif(void);
+
+#ifdef	__cplusplus
+}
+#endif
+
+#endif	/* CONSOLEIF_H */
+

+ 95 - 65
src/login/loginlog_sql.c → src/login/loginlog.c

@@ -1,5 +1,11 @@
-// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
-// For more information, see LICENCE in the main folder
+/**
+ * @file loginlog.c
+ * Module purpose is to register (log) events into a file or sql database.
+ * Licensed under GNU GPL.
+ *  For more information, see LICENCE in the main folder.
+ * @author Athena Dev Teams rev < 15k
+ * @author rAthena Dev Team
+ */
 
 #include "../common/cbasetypes.h"
 #include "../common/mmo.h"
@@ -13,7 +19,7 @@
 static char   global_db_hostname[32] = "127.0.0.1";
 static uint16 global_db_port = 3306;
 static char   global_db_username[32] = "ragnarok";
-static char   global_db_password[32] = "";
+static char   global_db_password[32] = ""; //empty by default since mysql is empty by default as well
 static char   global_db_database[32] = "ragnarok";
 static char   global_codepage[32] = "";
 // local sql settings
@@ -29,9 +35,13 @@ static Sql* sql_handle = NULL;
 static bool enabled = false;
 
 
-// Returns the number of failed login attemps by the ip in the last minutes.
-unsigned long loginlog_failedattempts(uint32 ip, unsigned int minutes)
-{
+/**
+ * Get the number of failed login attempts by the ip in the last minutes.
+ * @param ip: ip to search attempt from
+ * @param minutes: intervall to search
+ * @return number of failed attempts
+ */
+unsigned long loginlog_failedattempts(uint32 ip, unsigned int minutes) {
 	unsigned long failures = 0;
 
 	if( !enabled )
@@ -52,11 +62,14 @@ unsigned long loginlog_failedattempts(uint32 ip, unsigned int minutes)
 }
 
 
-/*=============================================
- * Records an event in the login log
- *---------------------------------------------*/
-void login_log(uint32 ip, const char* username, int rcode, const char* message)
-{
+/**
+ * Records an event in the login log.
+ * @param ip:
+ * @param username:
+ * @param rcode:
+ * @param message:
+ */
+void login_log(uint32 ip, const char* username, int rcode, const char* message) {
 	char esc_username[NAME_LENGTH*2+1];
 	char esc_message[255*2+1];
 	int retcode;
@@ -75,60 +88,13 @@ void login_log(uint32 ip, const char* username, int rcode, const char* message)
 		Sql_ShowDebug(sql_handle);
 }
 
-bool loginlog_init(void)
-{
-	const char* username;
-	const char* password;
-	const char* hostname;
-	uint16      port;
-	const char* database;
-	const char* codepage;
-
-	if( log_db_hostname[0] != '\0' )
-	{// local settings
-		username = log_db_username;
-		password = log_db_password;
-		hostname = log_db_hostname;
-		port     = log_db_port;
-		database = log_db_database;
-		codepage = log_codepage;
-	}
-	else
-	{// global settings
-		username = global_db_username;
-		password = global_db_password;
-		hostname = global_db_hostname;
-		port     = global_db_port;
-		database = global_db_database;
-		codepage = global_codepage;
-	}
-
-	sql_handle = Sql_Malloc();
-
-	if( SQL_ERROR == Sql_Connect(sql_handle, username, password, hostname, port, database) )
-	{
-		Sql_ShowDebug(sql_handle);
-		Sql_Free(sql_handle);
-		exit(EXIT_FAILURE);
-	}
-
-	if( codepage[0] != '\0' && SQL_ERROR == Sql_SetEncoding(sql_handle, codepage) )
-		Sql_ShowDebug(sql_handle);
-
-	enabled = true;
-
-	return true;
-}
-
-bool loginlog_final(void)
-{
-	Sql_Free(sql_handle);
-	sql_handle = NULL;
-	return true;
-}
-
-bool loginlog_config_read(const char* key, const char* value)
-{
+/**
+ * Read configuration options.
+ * @param key: config keyword
+ * @param value: config value for keyword
+ * @return true if successful, false if config not complete or server already running
+ */
+bool loginlog_config_read(const char* key, const char* value) {
 	const char* signature;
 
 	signature = "sql.";
@@ -182,3 +148,67 @@ bool loginlog_config_read(const char* key, const char* value)
 
 	return true;
 }
+
+
+/// Constructor destructor
+
+/**
+ * Initialize the module.
+ * Launched at login-serv start, create db or other long scope variable here.
+ * @return true if success else exit execution
+ */
+bool loginlog_init(void) {
+	const char* username;
+	const char* password;
+	const char* hostname;
+	uint16      port;
+	const char* database;
+	const char* codepage;
+
+	if( log_db_hostname[0] != '\0' )
+	{// local settings
+		username = log_db_username;
+		password = log_db_password;
+		hostname = log_db_hostname;
+		port     = log_db_port;
+		database = log_db_database;
+		codepage = log_codepage;
+	}
+	else
+	{// global settings
+		username = global_db_username;
+		password = global_db_password;
+		hostname = global_db_hostname;
+		port     = global_db_port;
+		database = global_db_database;
+		codepage = global_codepage;
+	}
+
+	sql_handle = Sql_Malloc();
+
+	if( SQL_ERROR == Sql_Connect(sql_handle, username, password, hostname, port, database) )
+	{
+		Sql_ShowDebug(sql_handle);
+		Sql_Free(sql_handle);
+		exit(EXIT_FAILURE);
+	}
+
+	if( codepage[0] != '\0' && SQL_ERROR == Sql_SetEncoding(sql_handle, codepage) )
+		Sql_ShowDebug(sql_handle);
+
+	enabled = true;
+
+	return true;
+}
+
+
+/**
+ * Handler to cleanup module, called when login-server stops.
+ * Currently closing sql connection to log schema.
+ * @return true success
+ */
+bool loginlog_final(void) {
+	Sql_Free(sql_handle);
+	sql_handle = NULL;
+	return true;
+}

+ 49 - 3
src/login/loginlog.h

@@ -1,15 +1,61 @@
-// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
-// For more information, see LICENCE in the main folder
+/**
+ * @file loginlog.h
+ * Module purpose is to register (log) events into a file or sql database.
+ * Licensed under GNU GPL.
+ *  For more information, see LICENCE in the main folder.
+ * @author Athena Dev Teams rev < 15k
+ * @author rAthena Dev Team
+ */
 
 #ifndef __LOGINLOG_H_INCLUDED__
 #define __LOGINLOG_H_INCLUDED__
 
+#ifdef	__cplusplus
+extern "C" {
+#endif
 
+/**
+ * Get the number of failed login attempts by the ip in the last minutes.
+ * @param ip: ip to search attempt from
+ * @param minutes: intervall to search
+ * @return number of failed attempts
+ */
 unsigned long loginlog_failedattempts(uint32 ip, unsigned int minutes);
+
+/**
+ * Records an event in the login log.
+ * @param ip:
+ * @param username:
+ * @param rcode:
+ * @param message:
+ */
 void login_log(uint32 ip, const char* username, int rcode, const char* message);
+
+/**
+ * Read configuration options.
+ * @param key: config keyword
+ * @param value: config value for keyword
+ * @return true if successful, false if config not complete or server already running
+ */
+bool loginlog_config_read(const char* w1, const char* w2);
+
+
+/**
+ * Initialize the module.
+ * Launched at login-serv start, create db or other long scope variable here.
+ * @return true if success else exit execution
+ */
 bool loginlog_init(void);
+
+/**
+ * Handler to cleanup module, called when login-server stops.
+ * atm closing sql connection to log schema
+ * @return true success
+ */
 bool loginlog_final(void);
-bool loginlog_config_read(const char* w1, const char* w2);
 
+#ifdef	__cplusplus
+}
+#endif
 
 #endif // __LOGINLOG_H_INCLUDED__

+ 9 - 3
src/login/sql/CMakeLists.txt

@@ -8,13 +8,19 @@ set( SQL_LOGIN_HEADERS
 	"${SQL_LOGIN_SOURCE_DIR}/account.h"
 	"${SQL_LOGIN_SOURCE_DIR}/ipban.h"
 	"${SQL_LOGIN_SOURCE_DIR}/login.h"
+	"${SQL_LOGIN_SOURCE_DIR}/loginclif.h"
+	"${SQL_LOGIN_SOURCE_DIR}/loginchrif.h"
+	"${SQL_LOGIN_SOURCE_DIR}/logincnslif.h"
 	"${SQL_LOGIN_SOURCE_DIR}/loginlog.h"
 	)
 set( SQL_LOGIN_SOURCES
-	"${SQL_LOGIN_SOURCE_DIR}/account_sql.c"
-	"${SQL_LOGIN_SOURCE_DIR}/ipban_sql.c"
+	"${SQL_LOGIN_SOURCE_DIR}/account.c"
+	"${SQL_LOGIN_SOURCE_DIR}/ipban.c"
 	"${SQL_LOGIN_SOURCE_DIR}/login.c"
-	"${SQL_LOGIN_SOURCE_DIR}/loginlog_sql.c"
+	"${SQL_LOGIN_SOURCE_DIR}/loginclif.c"
+	"${SQL_LOGIN_SOURCE_DIR}/loginchrif.c"
+	"${SQL_LOGIN_SOURCE_DIR}/logincnslif.c"
+	"${SQL_LOGIN_SOURCE_DIR}/loginlog.c"
 	)
 set( DEPENDENCIES common_sql )
 set( LIBRARIES ${GLOBAL_LIBRARIES} )

+ 29 - 14
src/map/atcommand.c

@@ -862,15 +862,21 @@ ACMD_FUNC(speed)
 		return -1;
 	}
 
-	if (speed < 0) {
+	sd->state.permanent_speed = 0; // Remove lock when set back to default speed.
+
+	if (speed < 0)
 		sd->base_status.speed = DEFAULT_WALK_SPEED;
-		sd->state.permanent_speed = 0; // Remove lock when set back to default speed.
-	} else {
+	else
 		sd->base_status.speed = cap_value(speed, MIN_WALK_SPEED, MAX_WALK_SPEED);
+
+	if (sd->base_status.speed != DEFAULT_WALK_SPEED) {
 		sd->state.permanent_speed = 1; // Set lock when set to non-default speed.
-	}
+		clif_displaymessage(fd, msg_txt(sd,8)); // Speed changed.
+	} else
+		clif_displaymessage(fd, msg_txt(sd,389)); // Speed returned to normal.
+
 	status_calc_bl(&sd->bl, SCB_SPEED);
-	clif_displaymessage(fd, msg_txt(sd,8)); // Speed changed.
+
 	return 0;
 }
 
@@ -1165,7 +1171,7 @@ ACMD_FUNC(heal)
 
 	if ( hp < 0 && sp <= 0 ) {
 		status_damage(NULL, &sd->bl, -hp, -sp, 0, 0);
-		clif_damage(&sd->bl,&sd->bl, gettick(), 0, 0, -hp, 0, 4, 0);
+		clif_damage(&sd->bl,&sd->bl, gettick(), 0, 0, -hp, 0, DMG_ENDURE, 0);
 		clif_displaymessage(fd, msg_txt(sd,156)); // HP or/and SP modified.
 		return 0;
 	}
@@ -1176,7 +1182,7 @@ ACMD_FUNC(heal)
 			status_heal(&sd->bl, hp, 0, 0);
 		else {
 			status_damage(NULL, &sd->bl, -hp, 0, 0, 0);
-			clif_damage(&sd->bl,&sd->bl, gettick(), 0, 0, -hp, 0, 4, 0);
+			clif_damage(&sd->bl,&sd->bl, gettick(), 0, 0, -hp, 0, DMG_ENDURE, 0);
 		}
 	}
 
@@ -3539,7 +3545,7 @@ ACMD_FUNC(recallall)
 				count++;
 			else {
 				if (pc_isdead(pl_sd)) { //Wake them up
-					pc_setstand(pl_sd);
+					pc_setstand(pl_sd, true);
 					pc_setrestartvalue(pl_sd,1);
 				}
 				pc_setpos(pl_sd, sd->mapindex, sd->bl.x, sd->bl.y, CLR_RESPAWN);
@@ -5752,13 +5758,15 @@ ACMD_FUNC(autotrade) {
 	}
 
 	sd->state.autotrade = 1;
+	if (battle_config.autotrade_monsterignore)
+		sd->state.monster_ignore = 1;
 
 	if( sd->state.vending ){
 		if( Sql_Query( mmysql_handle, "UPDATE `%s` SET `autotrade` = 1 WHERE `id` = %d;", vendings_db, sd->vender_id ) != SQL_SUCCESS ){
 			Sql_ShowDebug( mmysql_handle );
 		}
 	}else if( sd->state.buyingstore ){
-		if( Sql_Query( mmysql_handle, "UPDATE `%s` SET `autotrade` = 1 WHERE `id` = %d;", buyingstore_db, sd->buyer_id ) != SQL_SUCCESS ){
+		if( Sql_Query( mmysql_handle, "UPDATE `%s` SET `autotrade` = 1 WHERE `id` = %d;", buyingstores_db, sd->buyer_id ) != SQL_SUCCESS ){
 			Sql_ShowDebug( mmysql_handle );
 		}
 	}
@@ -5770,7 +5778,7 @@ ACMD_FUNC(autotrade) {
 
 	channel_pcquit(sd,0xF); //leave all chan
 	clif_authfail_fd(sd->fd, 15);
-	
+
 	chrif_save(sd,3);
 
 	return 0;
@@ -6928,9 +6936,9 @@ ACMD_FUNC(mail)
  *------------------------------------------*/
 ACMD_FUNC(mobinfo)
 {
-	unsigned char msize[3][7] = {"Small", "Medium", "Large"};
-	unsigned char mrace[12][11] = {"Formless", "Undead", "Beast", "Plant", "Insect", "Fish", "Demon", "Demi-Human", "Angel", "Dragon", "Boss", "Non-Boss"};
-	unsigned char melement[10][8] = {"Neutral", "Water", "Earth", "Fire", "Wind", "Poison", "Holy", "Dark", "Ghost", "Undead"};
+	unsigned char msize[SZ_ALL][7] = { "Small", "Medium", "Large" };
+	unsigned char mrace[RC_ALL][11] = { "Formless", "Undead", "Beast", "Plant", "Insect", "Fish", "Demon", "Demi-Human", "Angel", "Dragon", "Player" };
+	unsigned char melement[ELE_ALL][8] = { "Neutral", "Water", "Earth", "Fire", "Wind", "Poison", "Holy", "Dark", "Ghost", "Undead" };
 	char atcmd_output2[CHAT_SIZE_MAX];
 	struct item_data *item_data;
 	struct mob_db *mob, *mob_array[MAX_SEARCH];
@@ -7847,6 +7855,8 @@ ACMD_FUNC(fakename)
 		{
 			sd->fakename[0] = '\0';
 			clif_charnameack(0, &sd->bl);
+			if (sd->disguise)
+				clif_charnameack(sd->fd, &sd->bl);
 			clif_displaymessage(sd->fd, msg_txt(sd,1307)); // Returned to real name.
 			return 0;
 		}
@@ -7863,6 +7873,8 @@ ACMD_FUNC(fakename)
 
 	safestrncpy(sd->fakename, message, sizeof(sd->fakename));
 	clif_charnameack(0, &sd->bl);
+	if (sd->disguise) // Another packet should be sent so the client updates the name for sd
+		clif_charnameack(sd->fd, &sd->bl);
 	clif_displaymessage(sd->fd, msg_txt(sd,1310)); // Fake name enabled.
 
 	return 0;
@@ -9520,7 +9532,7 @@ ACMD_FUNC(cloneequip) {
 	else {
 		int8 i;
 		for (i = 0; i < EQI_MAX; i++) {
-			int8 idx;
+			short idx;
 			char flag = 0;
 			struct item tmp_item;
 			if ((idx = pl_sd->equip_index[i]) < 0)
@@ -10148,6 +10160,9 @@ bool is_atcommand(const int fd, struct map_session_data* sd, const char* message
 		sprintf(atcmd_msg, "%s", message);
 	}
 
+	if (battle_config.idletime_option&IDLE_ATCOMMAND)
+		sd->idletime = last_tick;
+
 	//Clearing these to be used once more.
 	memset(command, '\0', sizeof(command));
 	memset(params, '\0', sizeof(params));

+ 197 - 254
src/map/battle.c

@@ -309,10 +309,14 @@ int battle_delay_damage(unsigned int tick, int amotion, struct block_list *src,
 	return 0;
 }
 
+/**
+* Get attribute ratio
+* @param atk_elem Attack element enum e_element
+* @param def_type Defense element enum e_element
+* @param def_lv Element level 1 ~ MAX_ELE_LEVEL
+*/
 int battle_attr_ratio(int atk_elem,int def_type, int def_lv) {
-	if (!CHK_ELEMENT(atk_elem))
-		return 100;
-	if (!CHK_ELEMENT(def_type) || def_lv < 1 || def_lv > 4)
+	if (!CHK_ELEMENT(atk_elem) || !CHK_ELEMENT(def_type) || !CHK_ELEMENT_LEVEL(def_lv))
 		return 100;
 
 	return attr_fix_table[def_lv-1][atk_elem][def_type];
@@ -334,8 +338,7 @@ int64 battle_attr_fix(struct block_list *src, struct block_list *target, int64 d
 	if (!CHK_ELEMENT(atk_elem))
 		atk_elem = rnd()%ELE_ALL;
 
-	if (!CHK_ELEMENT(def_type) ||
-		def_lv < 1 || def_lv > 4) {
+	if (!CHK_ELEMENT(def_type) || !CHK_ELEMENT_LEVEL(def_lv)) {
 		ShowError("battle_attr_fix: unknown attr type: atk=%d def_type=%d def_lv=%d\n",atk_elem,def_type,def_lv);
 		return damage;
 	}
@@ -343,18 +346,18 @@ int64 battle_attr_fix(struct block_list *src, struct block_list *target, int64 d
 	ratio = attr_fix_table[def_lv-1][atk_elem][def_type];
 	if (sc && sc->count) { //increase dmg by src status
 		switch(atk_elem){
-		case ELE_FIRE:
-			if(sc->data[SC_VOLCANO]) ratio += enchant_eff[sc->data[SC_VOLCANO]->val1-1];
-			break;
-		case ELE_WIND:
-			if(sc->data[SC_VIOLENTGALE]) ratio += enchant_eff[sc->data[SC_VIOLENTGALE]->val1-1];
-			break;
-		case ELE_WATER:
-			if(sc->data[SC_DELUGE]) ratio += enchant_eff[sc->data[SC_DELUGE]->val1-1];
-			break;
-		case ELE_GHOST:
-			if(sc->data[SC_TELEKINESIS_INTENSE]) ratio += (sc->data[SC_TELEKINESIS_INTENSE]->val3);
-			break;
+			case ELE_FIRE:
+				if(sc->data[SC_VOLCANO]) ratio += enchant_eff[sc->data[SC_VOLCANO]->val1-1];
+				break;
+			case ELE_WIND:
+				if(sc->data[SC_VIOLENTGALE]) ratio += enchant_eff[sc->data[SC_VIOLENTGALE]->val1-1];
+				break;
+			case ELE_WATER:
+				if(sc->data[SC_DELUGE]) ratio += enchant_eff[sc->data[SC_DELUGE]->val1-1];
+				break;
+			case ELE_GHOST:
+				if(sc->data[SC_TELEKINESIS_INTENSE]) ratio += (sc->data[SC_TELEKINESIS_INTENSE]->val3);
+				break;
 		}
 	}
 	if( target && target->type == BL_SKILL ) {
@@ -792,20 +795,29 @@ int64 battle_calc_damage(struct block_list *src,struct block_list *bl,struct Dam
 
 		if( sc->data[SC_SAFETYWALL] && (flag&(BF_SHORT|BF_MAGIC))==BF_SHORT ) {
 			struct skill_unit_group* group = skill_id2group(sc->data[SC_SAFETYWALL]->val3);
-			//uint16 skill_id = sc->data[SC_SAFETYWALL]->val2; (safetywall or steinwand)
+			uint16 skill_id_val = sc->data[SC_SAFETYWALL]->val2;
 			if (group) {
-			//in RE, SW possesses a lifetime equal to group val2, (3x caster hp, or homon formula)
-#ifdef RENEWAL
 				d->dmg_lv = ATK_BLOCK;
+
+				if (skill_id_val == MH_STEINWAND) {
+					if (--group->val2 <= 0)
+						skill_delunitgroup(group);
+					if( (group->val3 - damage) > 0 )
+						group->val3 -= (int)cap_value(damage, INT_MIN, INT_MAX);
+					else
+						skill_delunitgroup(group);
+					return 0;
+				}
+				//in RE, SW possesses a lifetime equal to group val2, (3x caster hp, or homon formula)
+#ifdef RENEWAL
 				if ( ( group->val2 - damage) > 0 ) {
-					group->val2 -= (int)cap_value(damage,INT_MIN,INT_MAX);
+					group->val2 -= (int)cap_value(damage, INT_MIN, INT_MAX);
 				} else
 					skill_delunitgroup(group);
 				return 0;
 #else
-				if (--group->val2<=0)
+				if (--group->val2 <= 0)
 					skill_delunitgroup(group);
-				d->dmg_lv = ATK_BLOCK;
 				return 0;
 #endif
 			}
@@ -851,18 +863,18 @@ int64 battle_calc_damage(struct block_list *src,struct block_list *bl,struct Dam
 		}
 
 		if( (sce = sc->data[SC_MILLENNIUMSHIELD]) && sce->val2 > 0 && damage > 0 ) {
-			clif_skill_nodamage(bl, bl, RK_MILLENNIUMSHIELD, 1, 1);
 			sce->val3 -= (int)cap_value(damage,INT_MIN,INT_MAX); // absorb damage
 			d->dmg_lv = ATK_BLOCK;
-			sc_start(src,bl,SC_STUN,15,0,skill_get_time2(RK_MILLENNIUMSHIELD,sce->val1)); // There is a chance to be stunned when one shield is broken.
 			if( sce->val3 <= 0 ) { // Shield Down
 				sce->val2--;
-				if( sce->val2 > 0 ) {
-					if( sd )
-						clif_millenniumshield(sd,sce->val2);
-					sce->val3 = 1000; // Next Shield
-				} else
-					status_change_end(bl,SC_MILLENNIUMSHIELD,INVALID_TIMER); // All shields down
+				if( sce->val2 >= 0 ) {
+					clif_millenniumshield(bl,sce->val2);
+					if( !sce->val2 )
+						status_change_end(bl,SC_MILLENNIUMSHIELD,INVALID_TIMER); // All shields down
+					else
+						sce->val3 = 1000; // Next shield
+				}
+				status_change_start(src,bl,SC_STUN,10000,0,0,0,0,1000,2);
 			}
 			return 0;
 		}
@@ -877,7 +889,7 @@ int64 battle_calc_damage(struct block_list *src,struct block_list *bl,struct Dam
 			(flag&BF_LONG || sc->data[SC_SPURT])
 			&& rnd()%100 < 20)
 		{
-			if (sd && pc_issit(sd)) pc_setstand(sd); //Stand it to dodge.
+			if (sd && pc_issit(sd)) pc_setstand(sd, true); //Stand it to dodge.
 			clif_skill_nodamage(bl,bl,TK_DODGE,1,1);
 			if (!sc->data[SC_COMBO])
 				sc_start4(src,bl, SC_COMBO, 100, TK_JUMPKICK, src->id, 1, 0, 2000);
@@ -1493,6 +1505,10 @@ int64 battle_addmastery(struct map_session_data *sd,struct block_list *target,in
 			break;
 	}
 
+
+	if(sd->sc.data[SC_GN_CARTBOOST]) // cart boost adds mastery type damage
+		damage += 10*sd->sc.data[SC_GN_CARTBOOST]->val1;
+
 	return damage;
 }
 
@@ -1556,11 +1572,11 @@ static int battle_calc_base_weapon_attack(struct block_list *src, struct status_
  *------------------------------------------
  * Pass damage2 as NULL to not calc it.
  * Flag values:
- * &1: Critical hit
- * &2: Arrow attack
- * &4: Skill is Magic Crasher
- * &8: Skip target size adjustment (Extremity Fist?)
- *&16: Arrow attack but BOW, REVOLVER, RIFLE, SHOTGUN, GATLING or GRENADE type weapon not equipped (i.e. shuriken, kunai and venom knives not affected by DEX)
+ * &1 : Critical hit
+ * &2 : Arrow attack
+ * &4 : Skill is Magic Crasher
+ * &8 : Skip target size adjustment (Extremity Fist?)
+ * &16: Arrow attack but BOW, REVOLVER, RIFLE, SHOTGUN, GATLING or GRENADE type weapon not equipped (i.e. shuriken, kunai and venom knives not affected by DEX)
  *
  * Credits:
  *	Original coder Skoltex
@@ -1675,7 +1691,7 @@ void battle_consume_ammo(TBL_PC*sd, int skill, int lv)
 		if (!qty) qty = 1;
 	}
 
-	if(sd->equip_index[EQI_AMMO]>=0) //Qty check should have been done in skill_check_condition
+	if (sd->equip_index[EQI_AMMO] >= 0) //Qty check should have been done in skill_check_condition
 		pc_delitem(sd,sd->equip_index[EQI_AMMO],qty,0,1,LOG_TYPE_CONSUME);
 
 	sd->state.arrow_atk = 0;
@@ -1853,6 +1869,26 @@ static int battle_skill_damage(struct block_list *src, struct block_list *target
 }
 #endif
 
+/**
+ * Calculates Minstrel/Wanderer bonus for Chorus skills.
+ * @param sd Player who has Chorus skill active
+ * @return Bonus value based on party count
+ */
+static int battle_calc_chorusbonus(struct map_session_data *sd) {
+	int members = 0;
+
+	if (!sd || !sd->status.party_id)
+		return 0;
+
+	members = party_foreachsamemap(party_sub_count_class, sd, MAPID_THIRDMASK, MAPID_MINSTRELWANDERER);
+
+	if (members < 3)
+		return 0; // Bonus remains 0 unless 3 or more Minstrels/Wanderers are in the party.
+	if (members > 7)
+		return 5; // Maximum effect possible from 7 or more Minstrels/Wanderers.
+	return members - 2; // Effect bonus from additional Minstrels/Wanderers if not above the max possible.
+}
+
 struct Damage battle_calc_magic_attack(struct block_list *src,struct block_list *target,uint16 skill_id,uint16 skill_lv,int mflag);
 struct Damage battle_calc_misc_attack(struct block_list *src,struct block_list *target,uint16 skill_id,uint16 skill_lv,int mflag);
 
@@ -1967,12 +2003,12 @@ static bool is_attack_critical(struct Damage wd, struct block_list *src, struct
 	struct map_session_data *tsd = BL_CAST(BL_PC, target);
 
 	if (!first_call)
-		return (wd.type == 0x0a);
+		return (wd.type == DMG_CRITICAL);
 
 	if (skill_id == NPC_CRITICALSLASH || skill_id == LG_PINPOINTATTACK) //Always critical skills
 		return true;
 
-	if( !(wd.type&0x08) && sstatus->cri && (!skill_id ||
+	if( !(wd.type&DMG_MULTI_HIT) && sstatus->cri && (!skill_id ||
 		skill_id == KN_AUTOCOUNTER || skill_id == SN_SHARPSHOOTING ||
 		skill_id == MA_SHARPSHOOTING || skill_id == NJ_KIRIKAGE))
 	{
@@ -1989,7 +2025,7 @@ static bool is_attack_critical(struct Damage wd, struct block_list *src, struct
 
 		//The official equation is *2, but that only applies when sd's do critical.
 		//Therefore, we use the old value 3 on cases when an sd gets attacked by a mob
-		cri -= tstatus->luk*(!sd&&tsd?3:2);
+		cri -= tstatus->luk * ((!sd && tsd) ? 3 : 2);
 
 		if( tsc && tsc->data[SC_SLEEP] )
 			cri <<= 1;
@@ -2203,7 +2239,7 @@ static bool is_attack_hitting(struct Damage wd, struct block_list *src, struct b
 					hitrate -= (10 - (skill_lv - 1));
 				break;
 		}
-	} else if (sd && wd.type&0x08 && wd.div_ == 2) // +1 hit per level of Double Attack on a successful double attack (making sure other multi attack skills do not trigger this) [helvetica]
+	} else if (sd && wd.type&DMG_MULTI_HIT && wd.div_ == 2) // +1 hit per level of Double Attack on a successful double attack (making sure other multi attack skills do not trigger this) [helvetica]
 		hitrate += pc_checkskill(sd,TF_DOUBLE);
 
 	if (sd) {
@@ -2531,11 +2567,6 @@ static struct Damage battle_calc_attack_masteries(struct Damage wd, struct block
 		if (sc) { // Status change considered as masteries
 			uint8 i;
 
-#ifdef RENEWAL
-			if(sc->data[SC_NIBELUNGEN]) //With renewal, the level 4 weapon limitation has beed removed
-				ATK_ADD(wd.masteryAtk, wd.masteryAtk2, sc->data[SC_NIBELUNGEN]->val2);
-#endif
-
 			if (sc->data[SC_MIRACLE])
 				i = 2; //Star anger
 			else
@@ -2776,9 +2807,8 @@ struct Damage battle_calc_skill_base_damage(struct Damage wd, struct block_list
 				if(sd) {
 					short index = sd->equip_index[EQI_AMMO];
 
-					damagevalue = (3 * (sstatus->batk + sstatus->rhw.atk + sd->inventory_data[index]->atk)) * (skill_lv + 5) / 5;
-					if(index >= 0 && sd->inventory_data[index] && sd->inventory_data[index]->type == IT_AMMO)
-						ATK_ADD(wd.damage, wd.damage2, damagevalue);
+					damagevalue = (3 * (sstatus->batk + sstatus->rhw.atk + ((index >= 0 && sd->inventory_data[index]) ? sd->inventory_data[index]->atk : 0))) * (skill_lv + 5) / 5;
+					ATK_ADD(wd.damage, wd.damage2, damagevalue);
 				} else {
 					damagevalue = 5000;
 					ATK_ADD(wd.damage, wd.damage2, damagevalue);
@@ -2851,11 +2881,11 @@ struct Damage battle_calc_skill_base_damage(struct Damage wd, struct block_list
 					RE_ALLATK_ADDRATE(wd, sd->bonus.atk_rate);
 				}
 #ifndef RENEWAL
-				if(is_attack_critical(wd, src, target, skill_id, skill_lv, false) && sd->bonus.crit_atk_rate) { // add +crit damage bonuses here in pre-renewal mode [helvetica]
+				if(sd->bonus.crit_atk_rate && is_attack_critical(wd, src, target, skill_id, skill_lv, false)) { // add +crit damage bonuses here in pre-renewal mode [helvetica]
 					ATK_ADDRATE(wd.damage, wd.damage2, sd->bonus.crit_atk_rate);
 				}
 #endif
-				if(is_attack_critical(wd, src, target, skill_id, skill_lv, false) && sc && sc->data[SC_MTF_CRIDAMAGE]) {
+				if(sc && sc->data[SC_MTF_CRIDAMAGE] && is_attack_critical(wd, src, target, skill_id, skill_lv, false)) {
 					ATK_ADDRATE(wd.damage, wd.damage2, 25);
 					RE_ALLATK_ADDRATE(wd, 25); //Temporary it should be 'bonus.crit_atk_rate'
 				}
@@ -2899,7 +2929,7 @@ static struct Damage battle_calc_multi_attack(struct Damage wd, struct block_lis
 		{	//Success chance is not added, the higher one is used [Skotlex]
 			if( rnd()%100 < ( 5*skill_lv > sd->bonus.double_rate ? 5*skill_lv : sc && sc->data[SC_KAGEMUSYA]?sc->data[SC_KAGEMUSYA]->val1*3:sd->bonus.double_rate ) ) {
 				wd.div_ = skill_get_num(TF_DOUBLE,skill_lv?skill_lv:1);
-				wd.type = 0x08;
+				wd.type = DMG_MULTI_HIT;
 			}
 		}
 		else if( ((sd->weapontype1 == W_REVOLVER && (skill_lv = pc_checkskill(sd,GS_CHAINACTION)) > 0) //Normal Chain Action effect
@@ -2907,14 +2937,13 @@ static struct Damage battle_calc_multi_attack(struct Damage wd, struct block_lis
 			&& rnd()%100 < 5*skill_lv ) //Success rate
 		{
 			wd.div_ = skill_get_num(GS_CHAINACTION,skill_lv);
-			wd.type = 0x08;
+			wd.type = DMG_MULTI_HIT;
 			sc_start(src,src,SC_QD_SHOT_READY,100,target->id,skill_get_time(RL_QD_SHOT,1));
 		}
 		else if(sc && sc->data[SC_FEARBREEZE] && sd->weapontype1==W_BOW
 			&& (i = sd->equip_index[EQI_AMMO]) >= 0 && sd->inventory_data[i] && sd->status.inventory[i].amount > 1)
 		{
 			int chance = rnd()%100;
-			wd.type = 0x08;
 			switch(sc->data[SC_FEARBREEZE]->val1) {
 				case 5: if( chance < 4) { wd.div_ = 5; break; } // 3 % chance to attack 5 times.
 				case 4: if( chance < 7) { wd.div_ = 4; break; } // 6 % chance to attack 4 times.
@@ -2924,6 +2953,8 @@ static struct Damage battle_calc_multi_attack(struct Damage wd, struct block_lis
 			}
 			wd.div_ = min(wd.div_,sd->status.inventory[i].amount);
 			sc->data[SC_FEARBREEZE]->val4 = wd.div_-1;
+			if (wd.div_ > 1)
+				wd.type = DMG_MULTI_HIT;
 		}
 	}
 
@@ -3418,9 +3449,13 @@ static int battle_calc_attack_skill_ratio(struct Damage wd, struct block_list *s
 			break;
 		case RA_WUGDASH:// ATK 300%
 			skillratio += 200;
+			if (sc && sc->data[SC_DANCEWITHWUG])
+				skillratio += 10 * sc->data[SC_DANCEWITHWUG]->val1 * (2 + battle_calc_chorusbonus(sd));
 			break;
 		case RA_WUGSTRIKE:
 			skillratio += -100 + 200 * skill_lv;
+			if (sc && sc->data[SC_DANCEWITHWUG])
+				skillratio += 10 * sc->data[SC_DANCEWITHWUG]->val1 * (2 + battle_calc_chorusbonus(sd));
 			break;
 		case RA_WUGBITE:
 			skillratio += 300 + 200 * skill_lv;
@@ -3463,7 +3498,7 @@ static int battle_calc_attack_skill_ratio(struct Damage wd, struct block_list *s
 			if( sd ) {
 				short index = sd->equip_index[EQI_HAND_R];
 				if( index >= 0 && sd->inventory_data[index] && sd->inventory_data[index]->type == IT_WEAPON )
-				skillratio += sd->inventory_data[index]->weight / 10;// Weight is divided by 10 since 10 weight in coding make 1 whole actual weight. [Rytech]
+					skillratio += sd->inventory_data[index]->weight / 10;// Weight is divided by 10 since 10 weight in coding make 1 whole actual weight. [Rytech]
 			}
 			RE_LVL_DMOD(100);
 			break;
@@ -3521,14 +3556,11 @@ static int battle_calc_attack_skill_ratio(struct Damage wd, struct block_list *s
 			break;
 		case LG_SHIELDSPELL:// [(Casters Base Level x 4) + (Shield DEF x 10) + (Casters VIT x 2)] %
 			if (sd && skill_lv == 1) {
-				int index = sd->equip_index[EQI_HAND_L];
-				struct item_data *shield_data = NULL;
+				short index = sd->equip_index[EQI_HAND_L];
 
 				if (index >= 0 && sd->inventory_data[index] && sd->inventory_data[index]->type == IT_ARMOR)
-					shield_data = sd->inventory_data[index];
+					skillratio += sd->inventory_data[index]->def * 10;
 				skillratio = status_get_lv(src) * 4 + status_get_vit(src) * 2;
-				if (shield_data)
-					skillratio += shield_data->def * 10;
 			} else
 				skillratio = 0; // Prevent damage since level 2 is MATK. [Aleos]
 			break;
@@ -3829,11 +3861,8 @@ static int battle_calc_attack_skill_ratio(struct Damage wd, struct block_list *s
 			{
 				uint16 w = 50;
 				int16 idx = 0;
-				if (sd && (idx = sd->equip_index[EQI_AMMO]) > 0) {
-					struct item_data *id = NULL;
-					if ((id = itemdb_exists(sd->status.inventory[idx].nameid)))
-						w = id->weight;
-				}
+				if (sd && (idx = sd->equip_index[EQI_AMMO]) >= 0 && sd->inventory_data[idx])
+					w = sd->inventory_data[idx]->weight;
 				w /= 10;
 				skillratio += -100 + (max(w,1) * skill_lv * 30); //(custom)
 			}
@@ -3911,11 +3940,15 @@ static int battle_calc_skill_constant_addition(struct Damage wd, struct block_li
 		case RA_WUGDASH:
 			if( sd && sd->weight )
 				atk = (sd->weight / 8) + (30 * pc_checkskill(sd,RA_TOOTHOFWUG));
+			if (sc && sc->data[SC_DANCEWITHWUG])
+				atk += 10 * sc->data[SC_DANCEWITHWUG]->val1 * (2 + battle_calc_chorusbonus(sd));
 			break;
 		case RA_WUGSTRIKE:
 		case RA_WUGBITE:
 			if(sd)
 				atk = (30 * pc_checkskill(sd, RA_TOOTHOFWUG));
+			if (sc && sc->data[SC_DANCEWITHWUG])
+				atk += 10 * sc->data[SC_DANCEWITHWUG]->val1 * (2 + battle_calc_chorusbonus(sd));
 			break;
 		case GC_COUNTERSLASH:
 			atk = sstatus->agi * 2 + (sd ? sd->status.job_level * 4 : 0);
@@ -3955,27 +3988,14 @@ static int battle_calc_skill_constant_addition(struct Damage wd, struct block_li
  *	Initial refactoring by Baalberith
  *	Refined and optimized by helvetica
  */
-struct Damage battle_attack_sc_bonus(struct Damage wd, struct block_list *src, struct block_list *target, uint16 skill_id)
+struct Damage battle_attack_sc_bonus(struct Damage wd, struct block_list *src, uint16 skill_id)
 {
 	struct map_session_data *sd = BL_CAST(BL_PC, src);
 	struct status_change *sc = status_get_sc(src);
 	struct status_data *sstatus = status_get_status_data(src);
-#ifdef RENEWAL
-	struct status_data *tstatus = status_get_status_data(target);
-#endif
-	int chorusbonus = 0;
 
 	if( sd ) {
 		int type;
-		// Minstrel/Wanderer number check for chorus skills.
-		// Bonus remains 0 unless 3 or more Minstrels/Wanderers are in the party.
-		if( sd->status.party_id ) {
-			chorusbonus = party_foreachsamemap(party_sub_count_class, sd, MAPID_THIRDMASK, MAPID_MINSTRELWANDERER);
-			if( chorusbonus > 7 )
-				chorusbonus = 5; // Maximum effect possible from 7 or more Minstrels/Wanderers
-			else if( chorusbonus > 2 )
-				chorusbonus = chorusbonus - 2; // Effect bonus from additional Minstrels/Wanderers if not above the max possible.
-		}
 
 		// Kagerou/Oboro Earth Charm effect +15% wATK
 		ARR_FIND(1, 6, type, sd->talisman[type] > 0);
@@ -3989,35 +4009,19 @@ struct Damage battle_attack_sc_bonus(struct Damage wd, struct block_list *src, s
 
 	//The following are applied on top of current damage and are stackable.
 	if (sc) {
-#ifdef RENEWAL
-		if(sc->data[SC_WATK_ELEMENT])
-			if(skill_id != ASC_METEORASSAULT)
-				ATK_ADDRATE(wd.weaponAtk, wd.weaponAtk2, sc->data[SC_WATK_ELEMENT]->val2);
-		if(sc->data[SC_IMPOSITIO])
-			ATK_ADD(wd.equipAtk, wd.equipAtk2, sc->data[SC_IMPOSITIO]->val2);
-		if(sc->data[SC_VOLCANO])
-			ATK_ADD(wd.equipAtk, wd.equipAtk2, sc->data[SC_VOLCANO]->val2);
-		if(sc->data[SC_DRUMBATTLE]) {
-			if(tstatus->size == SZ_SMALL) {
-				ATK_ADD(wd.equipAtk, wd.equipAtk2, sc->data[SC_DRUMBATTLE]->val2);
-			} else if(tstatus->size == SZ_MEDIUM)
-				ATK_ADD(wd.equipAtk, wd.equipAtk2, 10 * sc->data[SC_DRUMBATTLE]->val1);
-		}
-		if(sc->data[SC_MADNESSCANCEL])
-			ATK_ADD(wd.equipAtk, wd.equipAtk2, 100);
-		if(sc->data[SC_GATLINGFEVER]) {
-			if(tstatus->size == SZ_SMALL) {
-				ATK_ADD(wd.equipAtk, wd.equipAtk2, 10 * sc->data[SC_GATLINGFEVER]->val1);
-			} else if(tstatus->size == SZ_MEDIUM) {
-				ATK_ADD(wd.equipAtk, wd.equipAtk2, 5 * sc->data[SC_GATLINGFEVER]->val1);
-			} else if(tstatus->size == SZ_BIG)
-				ATK_ADD(wd.equipAtk, wd.equipAtk2, sc->data[SC_GATLINGFEVER]->val1);
-		}
-#endif
 #ifndef RENEWAL
 		if( sc->data[SC_TRUESIGHT] )
 			ATK_ADDRATE(wd.damage, wd.damage2, 2*sc->data[SC_TRUESIGHT]->val1);
 #endif
+		if( sc->data[SC_GLOOMYDAY_SK] &&
+		( skill_id == LK_SPIRALPIERCE || skill_id == KN_BRANDISHSPEAR ||
+		  skill_id == CR_SHIELDBOOMERANG || skill_id == PA_SHIELDCHAIN ||
+		  skill_id == RK_HUNDREDSPEAR || skill_id == LG_SHIELDPRESS ) ) {
+			ATK_ADDRATE(wd.damage, wd.damage2, sc->data[SC_GLOOMYDAY_SK]->val2);
+#ifdef RENEWAL
+			ATK_ADDRATE(wd.weaponAtk, wd.weaponAtk2, sc->data[SC_GLOOMYDAY_SK]->val2);
+#endif
+		}
 		if (sc->data[SC_SPIRIT]) {
 			if(skill_id == AS_SONICBLOW && sc->data[SC_SPIRIT]->val2 == SL_ASSASIN){
 				ATK_ADDRATE(wd.damage, wd.damage2, map_flag_gvg(src->m)?25:100); //+25% dmg on woe/+100% dmg on nonwoe
@@ -4073,93 +4077,7 @@ struct Damage battle_attack_sc_bonus(struct Damage wd, struct block_list *src, s
 #endif
 			}
 		}
-		if(sc->data[SC_FIGHTINGSPIRIT]) {
-			ATK_ADD(wd.damage, wd.damage2, sc->data[SC_FIGHTINGSPIRIT]->val1);
-#ifdef RENEWAL
-			ATK_ADD(wd.equipAtk, wd.equipAtk2, sc->data[SC_FIGHTINGSPIRIT]->val1);
-#endif
-		}
-		if(sc->data[SC_SHIELDSPELL_DEF] && sc->data[SC_SHIELDSPELL_DEF]->val1 == 3) {
-			ATK_ADD(wd.damage, wd.damage2, sc->data[SC_SHIELDSPELL_DEF]->val2);
-#ifdef RENEWAL
-			ATK_ADD(wd.equipAtk, wd.equipAtk2, sc->data[SC_SHIELDSPELL_DEF]->val2);
-#endif
-		}
-		if(sc->data[SC_BANDING] && sc->data[SC_BANDING]->val2 > 1) {
-			ATK_ADD(wd.damage, wd.damage2, (10 + 10 * sc->data[SC_BANDING]->val1) * sc->data[SC_BANDING]->val2);
-#ifdef RENEWAL
-			ATK_ADD(wd.equipAtk, wd.equipAtk2, (10 + 10 * sc->data[SC_BANDING]->val1) * sc->data[SC_BANDING]->val2);
-#endif
-		}
-		if(sc->data[SC_INSPIRATION]) {
-			ATK_ADD(wd.damage, wd.damage2, 40 * sc->data[SC_INSPIRATION]->val1 + 3 * sc->data[SC_INSPIRATION]->val2);
-#ifdef RENEWAL
-			ATK_ADD(wd.equipAtk, wd.equipAtk2, 40 * sc->data[SC_INSPIRATION]->val1 + 3 * sc->data[SC_INSPIRATION]->val2);
-#endif
-		}
-		if(sc->data[SC_GT_CHANGE] && sc->data[SC_GT_CHANGE]->val2) {
-			struct block_list *bl; // ATK increase: ATK [{(Caster DEX / 4) + (Caster STR / 2)} x Skill Level / 5]
 
-			if( (bl = map_id2bl(sc->data[SC_GT_CHANGE]->val2)) ) {
-				ATK_ADD(wd.damage, wd.damage2, ( status_get_dex(bl)/4 + status_get_str(bl)/2 ) * sc->data[SC_GT_CHANGE]->val1 / 5 );
-#ifdef RENEWAL
-				ATK_ADD(wd.equipAtk, wd.equipAtk2, (status_get_dex(bl) / 4 + status_get_str(bl) / 2) * sc->data[SC_GT_CHANGE]->val1 / 5);
-#endif
-			}
-		}
-		if(sc->data[SC_PYROTECHNIC_OPTION]) {
-			ATK_ADD(wd.damage, wd.damage2, sc->data[SC_PYROTECHNIC_OPTION]->val2);
-#ifdef RENEWAL
-			ATK_ADD(wd.equipAtk, wd.equipAtk2, sc->data[SC_PYROTECHNIC_OPTION]->val2);
-#endif
-		}
-		if(sc->data[SC_HEATER_OPTION]) {
-			ATK_ADD(wd.damage, wd.damage2, sc->data[SC_HEATER_OPTION]->val2);
-#ifdef RENEWAL
-			ATK_ADD(wd.equipAtk, wd.equipAtk2, sc->data[SC_HEATER_OPTION]->val2);
-#endif
-		}
-		if(sc->data[SC_TROPIC_OPTION]) {
-			ATK_ADD(wd.damage, wd.damage2, sc->data[SC_TROPIC_OPTION]->val2);
-#ifdef RENEWAL
-			ATK_ADD(wd.equipAtk, wd.equipAtk2, sc->data[SC_TROPIC_OPTION]->val2);
-#endif
-		}
-		if(sc->data[SC_GN_CARTBOOST]) {
-			ATK_ADD(wd.damage, wd.damage2, 10 * sc->data[SC_GN_CARTBOOST]->val1);
-#ifdef RENEWAL
-			ATK_ADD(wd.equipAtk, wd.equipAtk2, 10 * sc->data[SC_GN_CARTBOOST]->val1);
-#endif
-		}
-		if(sc->data[SC_RUSHWINDMILL]) {
-			ATK_ADD(wd.damage, wd.damage2, sc->data[SC_RUSHWINDMILL]->val3);
-#ifdef RENEWAL
-			ATK_ADD(wd.equipAtk, wd.equipAtk2, sc->data[SC_RUSHWINDMILL]->val3);
-#endif
-		}
-		if( sc->data[SC_GLOOMYDAY_SK] &&
-		( skill_id == LK_SPIRALPIERCE || skill_id == KN_BRANDISHSPEAR ||
-		  skill_id == CR_SHIELDBOOMERANG || skill_id == PA_SHIELDCHAIN ||
-		  skill_id == RK_HUNDREDSPEAR || skill_id == LG_SHIELDPRESS ) ) {
-			ATK_ADDRATE(wd.damage, wd.damage2, sc->data[SC_GLOOMYDAY_SK]->val2);
-			RE_ALLATK_ADDRATE(wd, sc->data[SC_GLOOMYDAY_SK]->val2);
-		}
-		if( sc->data[SC_DANCEWITHWUG] ) {
-			ATK_ADDRATE(wd.damage, wd.damage2, sc->data[SC_DANCEWITHWUG]->val1 * 2 * chorusbonus);
-#ifdef RENEWAL
-			ATK_ADDRATE(wd.equipAtk, wd.equipAtk2, sc->data[SC_DANCEWITHWUG]->val1 * 2 * chorusbonus);
-#endif
-			if( skill_id == RA_WUGSTRIKE || skill_id == RA_WUGBITE || skill_id == RA_WUGDASH ) {
-				ATK_ADDRATE(wd.damage, wd.damage2, sc->data[SC_DANCEWITHWUG]->val1 * 10 * chorusbonus);
-				RE_ALLATK_ADDRATE(wd, sc->data[SC_DANCEWITHWUG]->val1 * 10 * chorusbonus);
-			}
-		}
-		if(sc->data[SC_SATURDAYNIGHTFEVER]) {
-			ATK_ADD(wd.damage, wd.damage2, 100 * sc->data[SC_SATURDAYNIGHTFEVER]->val1);
-#ifdef RENEWAL
-			ATK_ADD(wd.equipAtk, wd.equipAtk2, 100 * sc->data[SC_SATURDAYNIGHTFEVER]->val1);
-#endif
-		}
 		if(sc->data[SC_ZENKAI] && sstatus->rhw.ele == sc->data[SC_ZENKAI]->val2) {
 			ATK_ADD(wd.damage, wd.damage2, 200);
 #ifdef RENEWAL
@@ -4606,9 +4524,10 @@ struct Damage battle_calc_weapon_final_atk_modifiers(struct Damage wd, struct bl
 			((TBL_PC *)src)->status.weapon == W_2HSWORD
 		)) &&
 		rnd()%100 < tsc->data[SC_REJECTSWORD]->val2
-		) {
+		)
+	{
 		ATK_RATER(wd.damage, 50)
-		status_fix_damage(target,src,wd.damage,clif_damage(target,src,gettick(),0,0,wd.damage,0,0,0));
+		status_fix_damage(target,src,wd.damage,clif_damage(target,src,gettick(),0,0,wd.damage,0,DMG_NORMAL,0));
 		clif_skill_nodamage(target,target,ST_REJECTSWORD,tsc->data[SC_REJECTSWORD]->val1,1);
 		if( --(tsc->data[SC_REJECTSWORD]->val3) <= 0 )
 			status_change_end(target, SC_REJECTSWORD, INVALID_TIMER);
@@ -4624,7 +4543,7 @@ struct Damage battle_calc_weapon_final_atk_modifiers(struct Damage wd, struct bl
 		skill_blown(target, src, skill_get_blewcount(SR_CRESCENTELBOW_AUTOSPELL, tsc->data[SC_CRESCENTELBOW]->val1), unit_getdir(src), 0);
 		clif_skill_damage(target, src, gettick(), status_get_amotion(src), 0, rdamage,
 			1, SR_CRESCENTELBOW_AUTOSPELL, tsc->data[SC_CRESCENTELBOW]->val1, 6); // This is how official does
-		clif_damage(src, target, gettick(), status_get_amotion(src)+1000, 0, rdamage/10, 1, 0, 0);
+		clif_damage(src, target, gettick(), status_get_amotion(src)+1000, 0, rdamage/10, 1, DMG_NORMAL, 0);
 		status_damage(target, src, rdamage, 0, 0, 0);
 		status_damage(src, target, rdamage/10, 0, 0, 1);
 		status_change_end(target, SC_CRESCENTELBOW, INVALID_TIMER);
@@ -4697,14 +4616,14 @@ static struct Damage initialize_weapon_data(struct block_list *src, struct block
 	struct map_session_data *sd = BL_CAST(BL_PC, src);
 	struct Damage wd;
 
-	wd.type=0; //Normal attack
-	wd.div_=skill_id?skill_get_num(skill_id,skill_lv):1;
-	wd.amotion=(skill_id && skill_get_inf(skill_id)&INF_GROUND_SKILL)?0:sstatus->amotion; //Amotion should be 0 for ground skills.
+	wd.type = DMG_NORMAL; //Normal attack
+	wd.div_ = skill_id?skill_get_num(skill_id,skill_lv):1;
+	wd.amotion = (skill_id && skill_get_inf(skill_id)&INF_GROUND_SKILL)?0:sstatus->amotion; //Amotion should be 0 for ground skills.
 	// counter attack DOES obey ASPD delay on official, uncomment if you want the old (bad) behavior [helvetica]
 	/*if(skill_id == KN_AUTOCOUNTER)
 		wd.amotion >>= 1; */
-	wd.dmotion=tstatus->dmotion;
-	wd.blewcount=skill_get_blewcount(skill_id,skill_lv);
+	wd.dmotion = tstatus->dmotion;
+	wd.blewcount =skill_get_blewcount(skill_id,skill_lv);
 	wd.miscflag = wflag;
 	wd.flag = BF_WEAPON; //Initial Flag
 	wd.flag |= (skill_id||wd.miscflag)?BF_SKILL:BF_NORMAL; // Baphomet card's splash damage is counted as a skill. [Inkfish]
@@ -4745,7 +4664,7 @@ static struct Damage initialize_weapon_data(struct block_list *src, struct block
 
 			case TF_DOUBLE: //For NPC used skill.
 			case GS_CHAINACTION:
-				wd.type = 0x08;
+				wd.type = DMG_MULTI_HIT;
 				break;
 
 			case GS_GROUNDDRIFT:
@@ -4754,14 +4673,14 @@ static struct Damage initialize_weapon_data(struct block_list *src, struct block
 			case MS_BOWLINGBASH:
 			case MO_BALKYOUNG:
 			case TK_TURNKICK:
-				wd.blewcount=0;
+				wd.blewcount = 0;
 				break;
 
 			case KN_AUTOCOUNTER:
-				wd.flag=(wd.flag&~BF_SKILLMASK)|BF_NORMAL;
+				wd.flag = (wd.flag&~BF_SKILLMASK)|BF_NORMAL;
 				break;
 			case LK_SPIRALPIERCE:
-				if (!sd) wd.flag=(wd.flag&~(BF_RANGEMASK|BF_WEAPONMASK))|BF_LONG|BF_MISC;
+				if (!sd) wd.flag = (wd.flag&~(BF_RANGEMASK|BF_WEAPONMASK))|BF_LONG|BF_MISC;
 				break;
 
 			// The number of hits is set to 3 by default for use in Inspiration status.
@@ -4784,7 +4703,7 @@ static struct Damage initialize_weapon_data(struct block_list *src, struct block
  * @param wd : weapon damage
  * @param src : bl who did the attack
  * @param target : target of the attack
- * @parem skill_id : id of casted skill, 0 = basic atk
+ * @param skill_id : id of casted skill, 0 = basic atk
  * @param skill_lv : lvl of skill casted
  */
 void battle_do_reflect(int attack_type, struct Damage *wd, struct block_list* src, struct block_list* target, uint16 skill_id, uint16 skill_lv){
@@ -4810,8 +4729,9 @@ void battle_do_reflect(int attack_type, struct Damage *wd, struct block_list* sr
 				if (ssc && ssc->data[SC_DEVOTION] && (d_bl = map_id2bl(ssc->data[SC_DEVOTION]->val1)))
 					isDevotRdamage = true;
 			}
-			rdelay = clif_damage(src, (!isDevotRdamage) ? src : d_bl, tick, wd->amotion, sstatus->dmotion, rdamage, 1, 4, 0);
-			if( tsd ) battle_drain(tsd, src, rdamage, rdamage, sstatus->race, sstatus->class_);
+			rdelay = clif_damage(src, (!isDevotRdamage) ? src : d_bl, tick, wd->amotion, sstatus->dmotion, rdamage, 1, DMG_ENDURE, 0);
+			if( tsd )
+				battle_drain(tsd, src, rdamage, rdamage, sstatus->race, sstatus->class_);
 			//Use Reflect Shield to signal this kind of skill trigger. [Skotlex]
 			battle_delay_damage(tick, wd->amotion,target,(!isDevotRdamage) ? src : d_bl,0,CR_REFLECTSHIELD,0,rdamage,ATK_DEF,rdelay,true);
 			skill_additional_effect(target, (!isDevotRdamage) ? src : d_bl, CR_REFLECTSHIELD, 1, BF_WEAPON|BF_SHORT|BF_NORMAL,ATK_DEF,tick);
@@ -4830,11 +4750,12 @@ void battle_do_reflect(int attack_type, struct Damage *wd, struct block_list* sr
 					if (ssc && ssc->data[SC_DEVOTION] && (d_bl = map_id2bl(ssc->data[SC_DEVOTION]->val1)))
 						isDevotRdamage = true;
 				}
-				if(attack_type == BF_WEAPON && tsc->data[SC_REFLECTDAMAGE] ) // Don't reflect your own damage (Grand Cross)
+				if( attack_type == BF_WEAPON && tsc->data[SC_REFLECTDAMAGE] ) // Don't reflect your own damage (Grand Cross)
 					map_foreachinshootrange(battle_damage_area,target,skill_get_splash(LG_REFLECTDAMAGE,1),BL_CHAR,tick,target,wd->amotion,sstatus->dmotion,rdamage,tstatus->race);
-				else if(attack_type == BF_WEAPON || attack_type == BF_MISC) {
-					rdelay = clif_damage(src, (!isDevotRdamage) ? src : d_bl, tick, wd->amotion, sstatus->dmotion, rdamage, 1, 4, 0);
-					if( tsd ) battle_drain(tsd, src, rdamage, rdamage, sstatus->race, sstatus->class_);
+				else if( attack_type == BF_WEAPON || attack_type == BF_MISC) {
+					rdelay = clif_damage(src, (!isDevotRdamage) ? src : d_bl, tick, wd->amotion, sstatus->dmotion, rdamage, 1, DMG_ENDURE, 0);
+					if( tsd )
+						battle_drain(tsd, src, rdamage, rdamage, sstatus->race, sstatus->class_);
 					// It appears that official servers give skill reflect damage a longer delay
 					battle_delay_damage(tick, wd->amotion,target,(!isDevotRdamage) ? src : d_bl,0,CR_REFLECTSHIELD,0,rdamage,ATK_DEF,rdelay,true);
 					skill_additional_effect(target, (!isDevotRdamage) ? src : d_bl, CR_REFLECTSHIELD, 1, BF_WEAPON|BF_SHORT|BF_NORMAL,ATK_DEF,tick);
@@ -4883,7 +4804,7 @@ static struct Damage battle_calc_weapon_attack(struct block_list *src, struct bl
 
 	//Check for Lucky Dodge
 	if ((!skill_id || skill_id == PA_SACRIFICE) && tstatus->flee2 && rnd()%1000 < tstatus->flee2) {
-		wd.type = 0x0b;
+		wd.type = DMG_LUCY_DODGE;
 		wd.dmg_lv = ATK_LUCKY;
 		if(wd.div_ < 0)
 			wd.div_ *= -1;
@@ -4895,7 +4816,7 @@ static struct Damage battle_calc_weapon_attack(struct block_list *src, struct bl
 
 	// crit check is next since crits always hit on official [helvetica]
 	if (is_attack_critical(wd, src, target, skill_id, skill_lv, true))
-		wd.type = 0x0a;
+		wd.type = DMG_CRITICAL;
 
 	// check if we're landing a hit
 	if(!is_attack_hitting(wd, src, target, skill_id, skill_lv, true))
@@ -4941,7 +4862,7 @@ static struct Damage battle_calc_weapon_attack(struct block_list *src, struct bl
 		}
 
 		// final attack bonuses that aren't affected by cards
-		wd = battle_attack_sc_bonus(wd, src, target, skill_id);
+		wd = battle_attack_sc_bonus(wd, src, skill_id);
 
 		if (sd) { //monsters, homuns and pets have their damage computed directly
 			wd.damage = wd.statusAtk + wd.weaponAtk + wd.equipAtk + wd.masteryAtk;
@@ -4953,7 +4874,7 @@ static struct Damage battle_calc_weapon_attack(struct block_list *src, struct bl
 		}
 #else
 		// final attack bonuses that aren't affected by cards
-		wd = battle_attack_sc_bonus(wd, src, target, skill_id);
+		wd = battle_attack_sc_bonus(wd, src, skill_id);
 #endif
 
 		if (wd.damage + wd.damage2) { //Check if attack ignores DEF
@@ -5401,7 +5322,7 @@ struct Damage battle_calc_magic_attack(struct block_list *src,struct block_list
 							skillratio += (sc->data[SC_SPELLFIST]->val4 * 100) + (sc->data[SC_SPELLFIST]->val1 * 50) - 100;// val4 = used bolt level, val2 = used spellfist level. [Rytech]
 							ad.div_ = 1;// ad mods, to make it work similar to regular hits [Xazax]
 							ad.flag = BF_WEAPON|BF_SHORT;
-							ad.type = 0;
+							ad.type = DMG_NORMAL;
 						}
 						break;
 					case MG_THUNDERSTORM:
@@ -5945,7 +5866,7 @@ struct Damage battle_calc_magic_attack(struct block_list *src,struct block_list
 		MATK_ADDRATE(skill_damage);
 #endif
 
-	//battle_do_reflect(BF_MAGIC,&ad, src, target, skill_id, skill_lv); //WIP [lighta]
+	//battle_do_reflect(BF_MAGIC,&ad, src, target, skill_id, skill_lv); //WIP [lighta] Magic skill has own handler at skill_attack
 	return ad;
 }
 
@@ -6336,7 +6257,7 @@ struct Damage battle_calc_misc_attack(struct block_list *src,struct block_list *
 	if(tstatus->mode&MD_IGNOREMISC && md.flag&(BF_MISC) )	//misc @TODO optimize me
 		md.damage = md.damage2 = 1;
 
-	//battle_do_reflect(BF_MISC,&md, src, target, skill_id, skill_lv); //WIP [lighta]
+	battle_do_reflect(BF_MISC,&md, src, target, skill_id, skill_lv); //WIP [lighta]
 
 	return md;
 }
@@ -6385,10 +6306,11 @@ int64 battle_calc_return_damage(struct block_list* bl, struct block_list *src, i
 	struct map_session_data* sd;
 	int64 rdamage = 0, damage = *dmg;
 	int max_damage = status_get_max_hp(bl);
-	struct status_change* sc;
+	struct status_change *sc, *ssc;
 
 	sd = BL_CAST(BL_PC, bl);
 	sc = status_get_sc(bl);
+	ssc = status_get_sc(src);
 
 	if (flag & BF_SHORT) {//Bounces back part of the damage.
 		if ( !status_reflect && sd && sd->bonus.short_weapon_damage_return ) {
@@ -6435,6 +6357,16 @@ int64 battle_calc_return_damage(struct block_list* bl, struct block_list *src, i
 		}
 	}
 
+	if (ssc && ssc->data[SC_INSPIRATION]) {
+		rdamage += damage / 100;
+#ifdef RENEWAL
+		rdamage = cap_value(rdamage, 1, max_damage);
+#else
+		if( rdamage < 1 )
+			rdamage = 1;
+#endif
+	}
+
 	if( sc && sc->data[SC_KYOMU] ) // Nullify reflecting ability
 		rdamage = 0;
 
@@ -6449,6 +6381,9 @@ void battle_drain(TBL_PC *sd, struct block_list *tbl, int64 rdamage, int64 ldama
 	struct weapon_data *wd;
 	int64 *damage;
 	int thp = 0, tsp = 0, rhp = 0, rsp = 0, hp=0, sp=0, i;
+	if (!CHK_RACE(race) && !CHK_CLASS(class_))
+		return;
+
 	for (i = 0; i < 4; i++) {
 		//First two iterations: Right hand
 		if (i < 2) { wd = &sd->right_weapon; damage = &rdamage; }
@@ -6565,7 +6500,7 @@ int battle_damage_area( struct block_list *bl, va_list ap) {
 			battle_delay_damage(tick, amotion,src,bl,0,CR_REFLECTSHIELD,0,damage,ATK_DEF,0,true);
 		else
 			status_fix_damage(src,bl,damage,0);
-		clif_damage(bl,bl,tick,amotion,dmotion,damage,1,ATK_BLOCK,0);
+		clif_damage(bl,bl,tick,amotion,dmotion,damage,1,DMG_ENDURE,0);
 		skill_additional_effect(src, bl, CR_REFLECTSHIELD, 1, BF_WEAPON|BF_SHORT|BF_NORMAL,ATK_DEF,tick);
 		map_freeblock_unlock();
 	}
@@ -6608,35 +6543,36 @@ enum damage_lv battle_weapon_attack(struct block_list* src, struct block_list* t
 		sd->state.arrow_atk = (sd->status.weapon == W_BOW || (sd->status.weapon >= W_REVOLVER && sd->status.weapon <= W_GRENADE));
 		if (sd->state.arrow_atk)
 		{
-			int index = sd->equip_index[EQI_AMMO];
-			if (index<0) {
+			short index = sd->equip_index[EQI_AMMO];
+			if (index < 0) {
 				clif_arrow_fail(sd,0);
 				return ATK_NONE;
 			}
 			//Ammo check by Ishizu-chan
-			if (sd->inventory_data[index])
-			switch (sd->status.weapon) {
-			case W_BOW:
-				if (sd->inventory_data[index]->look != A_ARROW) {
-					clif_arrow_fail(sd,0);
-					return ATK_NONE;
-				}
-			break;
-			case W_REVOLVER:
-			case W_RIFLE:
-			case W_GATLING:
-			case W_SHOTGUN:
-				if (sd->inventory_data[index]->look != A_BULLET) {
-					clif_arrow_fail(sd,0);
-					return ATK_NONE;
-				}
-			break;
-			case W_GRENADE:
-				if (sd->inventory_data[index]->look != A_GRENADE) {
-					clif_arrow_fail(sd,0);
-					return ATK_NONE;
+			if (sd->inventory_data[index]) {
+				switch (sd->status.weapon) {
+					case W_BOW:
+						if (sd->inventory_data[index]->look != A_ARROW) {
+							clif_arrow_fail(sd,0);
+							return ATK_NONE;
+						}
+						break;
+					case W_REVOLVER:
+					case W_RIFLE:
+					case W_GATLING:
+					case W_SHOTGUN:
+						if (sd->inventory_data[index]->look != A_BULLET) {
+							clif_arrow_fail(sd,0);
+							return ATK_NONE;
+						}
+						break;
+					case W_GRENADE:
+						if (sd->inventory_data[index]->look != A_GRENADE) {
+							clif_arrow_fail(sd,0);
+							return ATK_NONE;
+						}
+						break;
 				}
-			break;
 			}
 		}
 	}
@@ -6655,7 +6591,7 @@ enum damage_lv battle_weapon_attack(struct block_list* src, struct block_list* t
 			uint16 skill_lv = tsc->data[SC_AUTOCOUNTER]->val1;
 
 			clif_skillcastcancel(target); //Remove the casting bar. [Skotlex]
-			clif_damage(src, target, tick, sstatus->amotion, 1, 0, 1, 0, 0); //Display MISS.
+			clif_damage(src, target, tick, sstatus->amotion, 1, 0, 1, DMG_NORMAL, 0); //Display MISS.
 			status_change_end(target, SC_AUTOCOUNTER, INVALID_TIMER);
 			skill_attack(BF_WEAPON,target,target,src,KN_AUTOCOUNTER,skill_lv,tick,0);
 			return ATK_BLOCK;
@@ -6669,7 +6605,7 @@ enum damage_lv battle_weapon_attack(struct block_list* src, struct block_list* t
 		status_change_end(target, SC_BLADESTOP_WAIT, INVALID_TIMER);
 		if(sc_start4(src,src, SC_BLADESTOP, 100, sd?pc_checkskill(sd, MO_BLADESTOP):5, 0, 0, target->id, duration))
 		{	//Target locked.
-			clif_damage(src, target, tick, sstatus->amotion, 1, 0, 1, 0, 0); //Display MISS.
+			clif_damage(src, target, tick, sstatus->amotion, 1, 0, 1, DMG_NORMAL, 0); //Display MISS.
 			clif_bladestop(target, src->id, 1);
 			sc_start4(src,target, SC_BLADESTOP, 100, skill_lv, 0, 0, src->id, duration);
 			return ATK_BLOCK;
@@ -6755,9 +6691,12 @@ enum damage_lv battle_weapon_attack(struct block_list* src, struct block_list* t
 		if( sc->data[SC_GIANTGROWTH] && (wd.flag&BF_SHORT) && rnd()%100 < sc->data[SC_GIANTGROWTH]->val2 )
 			wd.damage *= 3; // Triple Damage
 
-		if( sd && sc->data[SC_FEARBREEZE] && sc->data[SC_FEARBREEZE]->val4 > 0 && sd->status.inventory[sd->equip_index[EQI_AMMO]].amount >= sc->data[SC_FEARBREEZE]->val4 && battle_config.arrow_decrement){
-			pc_delitem(sd,sd->equip_index[EQI_AMMO],sc->data[SC_FEARBREEZE]->val4,0,1,LOG_TYPE_CONSUME);
-			sc->data[SC_FEARBREEZE]->val4 = 0;
+		if( sd && battle_config.arrow_decrement && sc->data[SC_FEARBREEZE] && sc->data[SC_FEARBREEZE]->val4 > 0) {
+			short idx = sd->equip_index[EQI_AMMO];
+			if (idx >= 0 && sd->status.inventory[idx].amount >= sc->data[SC_FEARBREEZE]->val4) {
+				pc_delitem(sd,idx,sc->data[SC_FEARBREEZE]->val4,0,1,LOG_TYPE_CONSUME);
+				sc->data[SC_FEARBREEZE]->val4 = 0;
+			}
 		}
 	}
 	if (sd && sd->state.arrow_atk) //Consume arrow.
@@ -6777,7 +6716,7 @@ enum damage_lv battle_weapon_attack(struct block_list* src, struct block_list* t
 		}
 	}
 
-	wd.dmotion = clif_damage(src, target, tick, wd.amotion, wd.dmotion, wd.damage, wd.div_ , wd.type, wd.damage2);
+	wd.dmotion = clif_damage(src, target, tick, wd.amotion, wd.dmotion, wd.damage, wd.div_ , (enum e_damage_type)wd.type, wd.damage2);
 
 	if (sd && sd->bonus.splash_range > 0 && damage > 0)
 		skill_castend_damage_id(src, target, 0, 1, tick, 0);
@@ -6813,7 +6752,7 @@ enum damage_lv battle_weapon_attack(struct block_list* src, struct block_list* t
 				(d_bl->type == BL_PC && ((TBL_PC*)d_bl)->devotion[sce->val2] == target->id)
 				) && check_distance_bl(target, d_bl, sce->val3) )
 			{
-				clif_damage(d_bl, d_bl, gettick(), 0, 0, damage, 0, 0, 0);
+				clif_damage(d_bl, d_bl, gettick(), 0, 0, damage, 0, DMG_NORMAL, 0);
 				status_fix_damage(NULL, d_bl, damage, 0);
 			}
 			else
@@ -6829,10 +6768,10 @@ enum damage_lv battle_weapon_attack(struct block_list* src, struct block_list* t
 		if( tsc->data[SC_WATER_SCREEN_OPTION] && tsc->data[SC_WATER_SCREEN_OPTION]->val1 ) {
 			struct block_list *e_bl = map_id2bl(tsc->data[SC_WATER_SCREEN_OPTION]->val1);
 			if( e_bl && !status_isdead(e_bl) ) {
-				clif_damage(e_bl,e_bl,tick,wd.amotion,wd.dmotion,damage,wd.div_,wd.type,wd.damage2);
+				clif_damage(e_bl,e_bl,tick,wd.amotion,wd.dmotion,damage,wd.div_,(enum e_damage_type)wd.type,wd.damage2);
 				status_damage(target,e_bl,damage,0,0,0);
 				// Just show damage in target.
-				clif_damage(src, target, tick, wd.amotion, wd.dmotion, damage, wd.div_, wd.type, wd.damage2 );
+				clif_damage(src, target, tick, wd.amotion, wd.dmotion, damage, wd.div_, (enum e_damage_type)wd.type, wd.damage2 );
 				map_freeblock_unlock();
 				return ATK_BLOCK;
 			}
@@ -7821,8 +7760,10 @@ static const struct _battle_data {
 	{ "update_enemy_position",				&battle_config.update_enemy_position,			0,		0,		1,				},
 	{ "devotion_rdamage",					&battle_config.devotion_rdamage,				0,		0,		100,			},
 	{ "feature.autotrade",					&battle_config.feature_autotrade,				1,		0,		1,				},
-	{ "feature.autotrade_direction",		&battle_config.feature_autotrade_direction,		4,		0,		7,				},
-	{ "feature.autotrade_sit",				&battle_config.feature_autotrade_sit,			1,		0,		1,				},
+	{ "feature.autotrade_direction",		&battle_config.feature_autotrade_direction,		4,		-1,		7,				},
+	{ "feature.autotrade_head_direction",	&battle_config.feature_autotrade_head_direction,0,		-1,		2,				},
+	{ "feature.autotrade_sit",				&battle_config.feature_autotrade_sit,			1,		-1,		1,				},
+	{ "feature.autotrade_open_delay",		&battle_config.feature_autotrade_open_delay,	5000,	1000,	INT_MAX,		},
 	{ "disp_serverbank_msg",				&battle_config.disp_serverbank_msg,				0,		0,		1,				},
 	{ "warg_can_falcon",                    &battle_config.warg_can_falcon,                 0,      0,      1,              },
 	{ "path_blown_halt",                    &battle_config.path_blown_halt,                 1,      0,      1,              },
@@ -7846,6 +7787,8 @@ static const struct _battle_data {
 	{ "fame_pharmacy_7",                    &battle_config.fame_pharmacy_7,                 10,     0,      INT_MAX,        },
 	{ "fame_pharmacy_10",                   &battle_config.fame_pharmacy_10,                50,     0,      INT_MAX,        },
 	{ "mail_delay",                         &battle_config.mail_delay,                      1000,   1000,   INT_MAX,        },
+	{ "at_monsterignore",                   &battle_config.autotrade_monsterignore,         0,      0,      1,              },
+	{ "idletime_option",                    &battle_config.idletime_option,                 0x25,   1,      INT_MAX,        },
 };
 #ifndef STATS_OPT_OUT
 /**

+ 6 - 2
src/map/battle.h

@@ -60,8 +60,8 @@ struct Damage {
 #endif
 	int64 damage, /// Right hand damage
 		damage2; /// Left hand damage
-	int type, /// chk clif_damage for type @TODO add an enum ?
-		div_; /// Number of hit
+	char type; /// chk clif_damage for type (clif.h enum e_damage_type)
+	short div_; /// Number of hit
 	int amotion,
 		dmotion;
 	int blewcount; /// Number of knockback
@@ -536,7 +536,9 @@ extern struct Battle_Config
 	// autotrade persistency
 	int feature_autotrade;
 	int feature_autotrade_direction;
+	int feature_autotrade_head_direction;
 	int feature_autotrade_sit;
+	int feature_autotrade_open_delay;
 
 	// Fame points
 	int fame_taekwon_mission;
@@ -562,6 +564,8 @@ extern struct Battle_Config
 	int taekwon_ranker_min_lv;
 	int revive_onwarp;
 	int mail_delay;
+	int autotrade_monsterignore;
+	int idletime_option;
 } battle_config;
 
 void do_init_battle(void);

+ 167 - 76
src/map/buyingstore.c

@@ -1,6 +1,7 @@
 // Copyright (c) Athena Dev Teams - Licensed under GNU GPL
 // For more information, see LICENCE in the main folder
 
+#include "../common/nullpo.h"
 #include "../common/cbasetypes.h"
 #include "../common/db.h"  // ARR_FIND
 #include "../common/malloc.h" // aMalloc, aFree
@@ -31,7 +32,7 @@ struct s_autotrade {
 	int buyer_id;
 	int m;
 	uint16 x, y;
-	unsigned char sex;
+	unsigned char sex, dir, head_dir, sit;
 	char title[MESSAGE_SIZE];
 	int limit;
 	uint16 count;
@@ -41,13 +42,18 @@ struct s_autotrade {
 
 //Autotrader
 static struct s_autotrade **autotraders; ///Autotraders Storage
-static uint16 autotrader_count; ///Autotrader count
+static uint16 autotrader_count, autotrader_loaded_count; ///Autotrader count
 static void do_final_buyingstore_autotrade(void);
 
 /// constants (client-side restrictions)
 #define BUYINGSTORE_MAX_PRICE 99990000
 #define BUYINGSTORE_MAX_AMOUNT 9999
 
+static DBMap *buyingstore_db;
+
+DBMap *buyingstore_getdb(void) {
+	return buyingstore_db;
+}
 
 /// failure constants for clif functions
 enum e_buyingstore_failure
@@ -73,27 +79,34 @@ static unsigned int buyingstore_getuid(void)
 	return ++buyingstore_nextid;
 }
 
+/**
+* Attempt to setup buying store fast check before create new one
+* @param sd
+* @param slots Number of item on the list
+* @return 0 If success, 1 - Cannot open, 2 - Manner penalty, 3 - Mapflag restiction, 4 - Cell restriction
+*/
+char buyingstore_setup(struct map_session_data* sd, unsigned char slots){
+	nullpo_retr(1, sd);
 
-bool buyingstore_setup(struct map_session_data* sd, unsigned char slots){
 	if (!battle_config.feature_buying_store || sd->state.vending || sd->state.buyingstore || sd->state.trading || slots == 0) {
-		return false;
+		return 1;
 	}
 
 	if( sd->sc.data[SC_NOCHAT] && (sd->sc.data[SC_NOCHAT]->val1&MANNER_NOROOM) )
 	{// custom: mute limitation
-		return false;
+		return 2;
 	}
 
 	if( map[sd->bl.m].flag.novending )
 	{// custom: no vending maps
 		clif_displaymessage(sd->fd, msg_txt(sd,276)); // "You can't open a shop on this map"
-		return false;
+		return 3;
 	}
 
 	if( map_getcell(sd->bl.m, sd->bl.x, sd->bl.y, CELL_CHKNOVENDING) )
 	{// custom: no vending cells
 		clif_displaymessage(sd->fd, msg_txt(sd,204)); // "You can't open a shop on this cell."
-		return false;
+		return 4;
 	}
 
 	if( slots > MAX_BUYINGSTORE_SLOTS )
@@ -105,25 +118,36 @@ bool buyingstore_setup(struct map_session_data* sd, unsigned char slots){
 	sd->buyingstore.slots = slots;
 	clif_buyingstore_open(sd);
 
-	return true;
+	return 0;
 }
 
-
-bool buyingstore_create(struct map_session_data* sd, int zenylimit, unsigned char result, const char* storename, const uint8* itemlist, unsigned int count)
+/**
+* Attempt to create new buying store
+* @param sd
+* @param zenylimit
+* @param result
+* @param storename
+* @param *itemlist { <nameid>.W, <amount>.W, <price>.L }*
+* @param count Number of item on the itemlist
+* @return 0 If success, 1 - Cannot open, 2 - Manner penalty, 3 - Mapflag restiction, 4 - Cell restriction, 5 - Invalid count/result, 6 - Cannot give item, 7 - Will be overweight
+*/
+char buyingstore_create(struct map_session_data* sd, int zenylimit, unsigned char result, const char* storename, const uint8* itemlist, unsigned int count)
 {
 	unsigned int i, weight, listidx;
 	char message_sql[MESSAGE_SIZE*2];
 
+	nullpo_retr(1, sd);
+
 	if( !result || count == 0 )
 	{// canceled, or no items
-		return false;
+		return 5;
 	}
 
 	if( !battle_config.feature_buying_store || pc_istrading(sd) || sd->buyingstore.slots == 0 || count > sd->buyingstore.slots || zenylimit <= 0 || zenylimit > sd->status.zeny || !storename[0] )
 	{// disabled or invalid input
 		sd->buyingstore.slots = 0;
 		clif_buyingstore_open_failed(sd, BUYINGSTORE_CREATE, 0);
-		return false;
+		return 1;
 	}
 
 	if( !pc_can_give_items(sd) )
@@ -131,24 +155,24 @@ bool buyingstore_create(struct map_session_data* sd, int zenylimit, unsigned cha
 		sd->buyingstore.slots = 0;
 		clif_displaymessage(sd->fd, msg_txt(sd,246));
 		clif_buyingstore_open_failed(sd, BUYINGSTORE_CREATE, 0);
-		return false;
+		return 6;
 	}
 
 	if( sd->sc.data[SC_NOCHAT] && (sd->sc.data[SC_NOCHAT]->val1&MANNER_NOROOM) )
 	{// custom: mute limitation
-		return false;
+		return 2;
 	}
 
 	if( map[sd->bl.m].flag.novending )
 	{// custom: no vending maps
 		clif_displaymessage(sd->fd, msg_txt(sd,276)); // "You can't open a shop on this map"
-		return false;
+		return 3;
 	}
 
 	if( map_getcell(sd->bl.m, sd->bl.x, sd->bl.y, CELL_CHKNOVENDING) )
 	{// custom: no vending cells
 		clif_displaymessage(sd->fd, msg_txt(sd,204)); // "You can't open a shop on this cell."
-		return false;
+		return 4;
 	}
 
 	weight = sd->weight;
@@ -204,14 +228,14 @@ bool buyingstore_create(struct map_session_data* sd, int zenylimit, unsigned cha
 	{// invalid item/amount/price
 		sd->buyingstore.slots = 0;
 		clif_buyingstore_open_failed(sd, BUYINGSTORE_CREATE, 0);
-		return false;
+		return 5;
 	}
 
 	if( (sd->max_weight*90)/100 < weight )
 	{// not able to carry all wanted items without getting overweight (90%)
 		sd->buyingstore.slots = 0;
 		clif_buyingstore_open_failed(sd, BUYINGSTORE_CREATE_OVERWEIGHT, weight);
-		return false;
+		return 7;
 	}
 
 	// success
@@ -223,7 +247,9 @@ bool buyingstore_create(struct map_session_data* sd, int zenylimit, unsigned cha
 
 	Sql_EscapeString( mmysql_handle, message_sql, sd->message );
 
-	if( Sql_Query( mmysql_handle, "INSERT INTO `%s`(`id`,`account_id`,`char_id`,`sex`,`map`,`x`,`y`,`title`,`limit`,`autotrade`) VALUES( %d, %d, %d, '%c', '%s', %d, %d, '%s', %d, %d );", buyingstore_db, sd->buyer_id, sd->status.account_id, sd->status.char_id, sd->status.sex == 0 ? 'F' : 'M', map[sd->bl.m].name, sd->bl.x, sd->bl.y, message_sql, sd->buyingstore.zenylimit, sd->state.autotrade ) != SQL_SUCCESS ){
+	if( Sql_Query( mmysql_handle, "INSERT INTO `%s`(`id`,`account_id`,`char_id`,`sex`,`map`,`x`,`y`,`title`,`limit`,`autotrade`, `body_direction`, `head_direction`, `sit`) "
+		"VALUES( %d, %d, %d, '%c', '%s', %d, %d, '%s', %d, %d, '%d', '%d', '%d' );",
+		buyingstores_db, sd->buyer_id, sd->status.account_id, sd->status.char_id, sd->status.sex == 0 ? 'F' : 'M', map[sd->bl.m].name, sd->bl.x, sd->bl.y, message_sql, sd->buyingstore.zenylimit, sd->state.autotrade, sd->ud.dir, sd->head_dir, pc_issit(sd) ) != SQL_SUCCESS ){
 		Sql_ShowDebug(mmysql_handle);
 	}
 
@@ -235,38 +261,47 @@ bool buyingstore_create(struct map_session_data* sd, int zenylimit, unsigned cha
 
 	clif_buyingstore_myitemlist(sd);
 	clif_buyingstore_entry(sd);
+	idb_put(buyingstore_db, sd->status.char_id, sd);
 
-	return true;
+	return 0;
 }
 
-
-void buyingstore_close(struct map_session_data* sd)
-{
-	if( sd->state.buyingstore )
-	{
-		if( !sd->state.autotrade ){
-			if( 
-				Sql_Query( mmysql_handle, "DELETE FROM `%s` WHERE buyingstore_id = %d;", buyingstore_items_db, sd->buyer_id ) != SQL_SUCCESS ||
-				Sql_Query( mmysql_handle, "DELETE FROM `%s` WHERE `id` = %d;", buyingstore_db, sd->buyer_id ) != SQL_SUCCESS
-			){
-				Sql_ShowDebug(mmysql_handle);
-			}
+/**
+* Close buying store and clear buying store data from tables
+* @param sd
+*/
+void buyingstore_close(struct map_session_data* sd) {
+	nullpo_retv(sd);
+
+	if( sd->state.buyingstore ) {
+		if( 
+			Sql_Query( mmysql_handle, "DELETE FROM `%s` WHERE buyingstore_id = %d;", buyingstore_items_db, sd->buyer_id ) != SQL_SUCCESS ||
+			Sql_Query( mmysql_handle, "DELETE FROM `%s` WHERE `id` = %d;", buyingstores_db, sd->buyer_id ) != SQL_SUCCESS
+		) {
+			Sql_ShowDebug(mmysql_handle);
 		}
 
-		// invalidate data
 		sd->state.buyingstore = false;
+		sd->buyer_id = 0;
 		memset(&sd->buyingstore, 0, sizeof(sd->buyingstore));
+		idb_remove(buyingstore_db, sd->status.char_id);
 
 		// notify other players
 		clif_buyingstore_disappear_entry(sd);
 	}
 }
 
-
+/**
+* Open buying store from buyer
+* @param sd Player
+* @param account_id Buyer account ID
+*/
 void buyingstore_open(struct map_session_data* sd, int account_id)
 {
 	struct map_session_data* pl_sd;
 
+	nullpo_retv(sd);
+
 	if( !battle_config.feature_buying_store || pc_istrading(sd) )
 	{// not allowed to sell
 		return;
@@ -292,13 +327,21 @@ void buyingstore_open(struct map_session_data* sd, int account_id)
 	clif_buyingstore_itemlist(sd, pl_sd);
 }
 
-
+/**
+* Start transaction
+* @param sd Player/Seller
+* @param account_id Buyer account ID
+* @param *itemlist List of sold items { <index>.W, <nameid>.W, <amount>.W }*
+* @param count Number of item on the itemlist
+*/
 void buyingstore_trade(struct map_session_data* sd, int account_id, unsigned int buyer_id, const uint8* itemlist, unsigned int count)
 {
 	int zeny = 0;
 	unsigned int i, weight, listidx, k;
 	struct map_session_data* pl_sd;
 
+	nullpo_retv(sd);
+
 	if( count == 0 )
 	{// nothing to do
 		return;
@@ -461,7 +504,7 @@ void buyingstore_trade(struct map_session_data* sd, int account_id, unsigned int
 	}
 	else
 	{// continue buying
-		if( Sql_Query( mmysql_handle, "UPDATE `%s` SET `limit` = %d WHERE `id` = %d;", buyingstore_db, pl_sd->buyingstore.zenylimit, pl_sd->buyer_id ) != SQL_SUCCESS ){
+		if( Sql_Query( mmysql_handle, "UPDATE `%s` SET `limit` = %d WHERE `id` = %d;", buyingstores_db, pl_sd->buyingstore.zenylimit, pl_sd->buyer_id ) != SQL_SUCCESS ){
 			Sql_ShowDebug( mmysql_handle );
 		}
 
@@ -484,6 +527,8 @@ bool buyingstore_search(struct map_session_data* sd, unsigned short nameid)
 {
 	unsigned int i;
 
+	nullpo_ret(sd);
+
 	if( !sd->state.buyingstore )
 	{// not buying
 		return false;
@@ -506,6 +551,8 @@ bool buyingstore_searchall(struct map_session_data* sd, const struct s_search_st
 	unsigned int i, idx;
 	struct s_buyingstore_item* it;
 
+	nullpo_ret(sd);
+
 	if( !sd->state.buyingstore )
 	{// not buying
 		return true;
@@ -544,14 +591,17 @@ bool buyingstore_searchall(struct map_session_data* sd, const struct s_search_st
 	return true;
 }
 
-/** Open buyingstore for Autotrader
+/**
+* Open buyingstore for Autotrader
 * @param sd Player as autotrader
 */
 void buyingstore_reopen( struct map_session_data* sd ){
+	nullpo_retv(sd);
+
 	// Ready to open buyingstore for this char
-	if ( sd && autotrader_count > 0 && autotraders){
+	if ( autotrader_count > 0 && autotraders){
 		uint16 i;
-		uint8 *data, *p;
+		uint8 *data, *p, fail = 0;
 		uint16 j, count;
 
 		ARR_FIND(0,autotrader_count,i,autotraders[i] && autotraders[i]->char_id == sd->status.char_id);
@@ -575,28 +625,39 @@ void buyingstore_reopen( struct map_session_data* sd ){
 			p += 8;
 		}
 
+		// Make sure abort all NPCs
+		npc_event_dequeue(sd);
+		pc_cleareventtimer(sd);
+
 		// Open the buyingstore again
-		if( buyingstore_setup( sd, (unsigned char)autotraders[i]->count ) &&
-			buyingstore_create( sd, autotraders[i]->limit, 1, autotraders[i]->title, data, autotraders[i]->count ) )
+		if( (fail = buyingstore_setup( sd, (unsigned char)autotraders[i]->count ) == 0) &&
+			(fail = buyingstore_create( sd, autotraders[i]->limit, 1, autotraders[i]->title, data, autotraders[i]->count ) == 0) )
 		{
-			ShowInfo("Loaded autotrade buyingstore data for '"CL_WHITE"%s"CL_RESET"' with '"CL_WHITE"%d"CL_RESET"' items at "CL_WHITE"%s (%d,%d)"CL_RESET"\n",
-				sd->status.name,count,mapindex_id2name(sd->mapindex),sd->bl.x,sd->bl.y);
+			ShowInfo("Loaded buyingstore for '"CL_WHITE"%s"CL_RESET"' with '"CL_WHITE"%d"CL_RESET"' items at "CL_WHITE"%s (%d,%d)"CL_RESET"\n",
+				sd->status.name, count, mapindex_id2name(sd->mapindex), sd->bl.x, sd->bl.y);
 
 			// Set him to autotrade
-			if (Sql_Query( mmysql_handle, "UPDATE `%s` SET `autotrade` = 1 WHERE `id` = %d;",
-				buyingstore_db, sd->buyer_id ) != SQL_SUCCESS )
+			if (Sql_Query( mmysql_handle, "UPDATE `%s` SET `autotrade` = 1, `body_direction` = '%d', `head_direction` = '%d', `sit` = '%d' "
+				"WHERE `id` = %d;",
+				buyingstores_db, autotraders[i]->dir, autotraders[i]->head_dir, autotraders[i]->sit, sd->buyer_id ) != SQL_SUCCESS )
 			{
 				Sql_ShowDebug( mmysql_handle );
 			}
 
-			// Make him look perfect
-			unit_setdir(&sd->bl,battle_config.feature_autotrade_direction);
-
-			if( battle_config.feature_autotrade_sit )
+			// Make buyer look perfect
+			pc_setdir(sd, autotraders[i]->dir, autotraders[i]->head_dir);
+			clif_changed_dir(&sd->bl, AREA_WOS);
+			if( autotraders[i]->sit ) {
 				pc_setsit(sd);
+				skill_sit(sd, 1);
+				clif_sitting(&sd->bl);
+			}
+
+			// Immediate save
+			chrif_save(sd, 3);
 		}else{
 			// Failed to open the buyingstore, set him offline
-			ShowWarning("Failed to load autotrade buyingstore data for '"CL_WHITE"%s"CL_RESET"' with '"CL_WHITE"%d"CL_RESET"' items\n", sd->status.name, count );
+			ShowError("Failed (%d) to load autotrade buyingstore data for '"CL_WHITE"%s"CL_RESET"' with '"CL_WHITE"%d"CL_RESET"' items\n", fail, sd->status.name, count );
 
 			map_quit( sd );
 		}
@@ -604,7 +665,7 @@ void buyingstore_reopen( struct map_session_data* sd ){
 		aFree(data);
 
 		//If the last autotrade is loaded, clear autotraders [Cydh]
-		if (i+1 >= autotrader_count)
+		if (++autotrader_loaded_count >= autotrader_count)
 			do_final_buyingstore_autotrade();
 	}
 }
@@ -615,21 +676,22 @@ void buyingstore_reopen( struct map_session_data* sd ){
 void do_init_buyingstore_autotrade( void ) {
 	if(battle_config.feature_autotrade) {
 		uint16 i, items = 0;
-		autotrader_count = 0;
+		autotrader_count = autotrader_loaded_count = 0;
 
 		// Get autotrader from table. `map`, `x`, and `y`, aren't used here
 		// Just read player that has data at buyingstore_items [Cydh]
 		if (Sql_Query(mmysql_handle,
-			"SELECT `id`, `account_id`, `char_id`, `sex`, `title`, `limit` "
+			"SELECT `id`, `account_id`, `char_id`, `sex`, `title`, `limit`, `body_direction`, `head_direction`, `sit` "
 			"FROM `%s` "
-			"WHERE `autotrade` = 1 AND `limit` > 0 AND (SELECT COUNT(`buyingstore_id`) FROM `%s` WHERE `buyingstore_id` = `id`) > 0;",
-			buyingstore_db, buyingstore_items_db ) != SQL_SUCCESS )
+			"WHERE `autotrade` = 1 AND `limit` > 0 AND (SELECT COUNT(`buyingstore_id`) FROM `%s` WHERE `buyingstore_id` = `id`) > 0 "
+			"ORDER BY `id`;",
+			buyingstores_db, buyingstore_items_db ) != SQL_SUCCESS )
 		{
 			Sql_ShowDebug(mmysql_handle);
 			return;
 		}
 
-		if( (autotrader_count = (uint32)Sql_NumRows(mmysql_handle)) > 0 ){
+		if( (autotrader_count = (uint16)Sql_NumRows(mmysql_handle)) > 0 ){
 			// Init autotraders
 			CREATE(autotraders, struct s_autotrade *, autotrader_count);
 
@@ -644,48 +706,59 @@ void do_init_buyingstore_autotrade( void ) {
 			while (SQL_SUCCESS == Sql_NextRow(mmysql_handle) && i < autotrader_count) {
 				size_t len;
 				char* data;
+				struct s_autotrade *at = NULL;
 
 				CREATE(autotraders[i], struct s_autotrade, 1);
+				at = autotraders[i];
 
-				Sql_GetData(mmysql_handle, 0, &data, NULL); autotraders[i]->buyer_id = atoi(data);
-				Sql_GetData(mmysql_handle, 1, &data, NULL); autotraders[i]->account_id = atoi(data);
-				Sql_GetData(mmysql_handle, 2, &data, NULL); autotraders[i]->char_id = atoi(data);
-				Sql_GetData(mmysql_handle, 3, &data, NULL); autotraders[i]->sex = (data[0] == 'F') ? 0 : 1;
-				Sql_GetData(mmysql_handle, 4, &data, &len); safestrncpy(autotraders[i]->title, data, min(len + 1, MESSAGE_SIZE));
-				Sql_GetData(mmysql_handle, 5, &data, NULL); autotraders[i]->limit = atoi(data);
-				autotraders[i]->count = 0;
+				Sql_GetData(mmysql_handle, 0, &data, NULL); at->buyer_id = atoi(data);
+				Sql_GetData(mmysql_handle, 1, &data, NULL); at->account_id = atoi(data);
+				Sql_GetData(mmysql_handle, 2, &data, NULL); at->char_id = atoi(data);
+				Sql_GetData(mmysql_handle, 3, &data, NULL); at->sex = (data[0] == 'F') ? 0 : 1;
+				Sql_GetData(mmysql_handle, 4, &data, &len); safestrncpy(at->title, data, min(len + 1, MESSAGE_SIZE));
+				Sql_GetData(mmysql_handle, 5, &data, NULL); at->limit = atoi(data);
+				Sql_GetData(mmysql_handle, 6, &data, NULL); at->dir = atoi(data);
+				Sql_GetData(mmysql_handle, 7, &data, NULL); at->head_dir = atoi(data);
+				Sql_GetData(mmysql_handle, 8, &data, NULL); at->sit = atoi(data);
+				at->count = 0;
+
+				if (battle_config.feature_autotrade_direction >= 0)
+					at->dir = battle_config.feature_autotrade_direction;
+				if (battle_config.feature_autotrade_head_direction >= 0)
+					at->head_dir = battle_config.feature_autotrade_head_direction;
+				if (battle_config.feature_autotrade_sit >= 0)
+					at->sit = battle_config.feature_autotrade_sit;
 
 				// initialize player
-				CREATE(autotraders[i]->sd, struct map_session_data, 1);
-			
-				pc_setnewpc(autotraders[i]->sd, autotraders[i]->account_id, autotraders[i]->char_id, 0, gettick(), autotraders[i]->sex, 0);
-			
-				autotraders[i]->sd->state.autotrade = 1;
-				chrif_authreq(autotraders[i]->sd, true);
+				CREATE(at->sd, struct map_session_data, 1);
+				pc_setnewpc(at->sd, at->account_id, at->char_id, 0, gettick(), at->sex, 0);
+				at->sd->state.autotrade = 1;
+				at->sd->state.monster_ignore = (battle_config.autotrade_monsterignore);
+				chrif_authreq(at->sd, true);
 				i++;
 			}
 			Sql_FreeResult(mmysql_handle);
 
-			//Init items on vending list each autotrader
+			//Init items on buying list each autotrader
 			for (i = 0; i < autotrader_count; i++){
 				struct s_autotrade *at = NULL;
 				uint16 j;
 
-				if (autotraders[i] == NULL)
+				if ((at = autotraders[i]) == NULL)
 					continue;
-				at = autotraders[i];
 
 				if (SQL_ERROR == Sql_Query(mmysql_handle,
 					"SELECT `item_id`, `amount`, `price` "
 					"FROM `%s` "
 					"WHERE `buyingstore_id` = %d "
-					"ORDER BY `index` ASC;", buyingstore_items_db, at->buyer_id ) )
+					"ORDER BY `index` ASC;",
+					buyingstore_items_db, at->buyer_id ) )
 				{
 					Sql_ShowDebug(mmysql_handle);
 					continue;
 				}
 
-				if (!(at->count = (uint32)Sql_NumRows(mmysql_handle))) {
+				if (!(at->count = (uint16)Sql_NumRows(mmysql_handle))) {
 					map_quit(at->sd);
 					continue;
 				}
@@ -708,12 +781,12 @@ void do_init_buyingstore_autotrade( void ) {
 				Sql_FreeResult(mmysql_handle);
 			}
 
-			ShowStatus("Done loading '"CL_WHITE"%d"CL_RESET"' autotraders with '"CL_WHITE"%d"CL_RESET"' items.\n", autotrader_count, items);
+			ShowStatus("Done loading '"CL_WHITE"%d"CL_RESET"' buyingstore autotraders with '"CL_WHITE"%d"CL_RESET"' items.\n", autotrader_count, items);
 		}
 	}
 
 	// Everything is loaded fine, their entries will be reinserted once they are loaded
-	if (Sql_Query( mmysql_handle, "DELETE FROM `%s`;", buyingstore_db ) != SQL_SUCCESS ||
+	if (Sql_Query( mmysql_handle, "DELETE FROM `%s`;", buyingstores_db ) != SQL_SUCCESS ||
 		Sql_Query( mmysql_handle, "DELETE FROM `%s`;", buyingstore_items_db ) != SQL_SUCCESS)
 	{
 		Sql_ShowDebug(mmysql_handle);
@@ -748,3 +821,21 @@ void do_final_buyingstore_autotrade(void) {
 		autotrader_count = 0;
 	}
 }
+
+/**
+ * Initialise the buyingstore module
+ * called in map::do_init
+ */
+void do_final_buyingstore(void) {
+	db_destroy(buyingstore_db);
+	do_final_buyingstore_autotrade();
+}
+
+/**
+ * Destory the buyingstore module
+ * called in map::do_final
+ */
+void do_init_buyingstore(void) {
+	buyingstore_db = idb_alloc(DB_OPT_BASE);
+	buyingstore_nextid = 0;
+}

+ 5 - 2
src/map/buyingstore.h

@@ -22,13 +22,16 @@ struct s_buyingstore
 	unsigned char slots;
 };
 
-bool buyingstore_setup(struct map_session_data* sd, unsigned char slots);
-bool buyingstore_create(struct map_session_data* sd, int zenylimit, unsigned char result, const char* storename, const uint8* itemlist, unsigned int count);
+char buyingstore_setup(struct map_session_data* sd, unsigned char slots);
+char buyingstore_create(struct map_session_data* sd, int zenylimit, unsigned char result, const char* storename, const uint8* itemlist, unsigned int count);
 void buyingstore_close(struct map_session_data* sd);
 void buyingstore_open(struct map_session_data* sd, int account_id);
 void buyingstore_trade(struct map_session_data* sd, int account_id, unsigned int buyer_id, const uint8* itemlist, unsigned int count);
 bool buyingstore_search(struct map_session_data* sd, unsigned short nameid);
 bool buyingstore_searchall(struct map_session_data* sd, const struct s_search_store_search* s);
+DBMap *buyingstore_getdb(void);
+void do_final_buyingstore(void);
+void do_init_buyingstore(void);
 
 void do_init_buyingstore_autotrade( void );
 void buyingstore_reopen( struct map_session_data* sd );

+ 4 - 2
src/map/cashshop.c

@@ -226,14 +226,16 @@ bool cashshop_buylist( struct map_session_data* sd, uint32 kafrapoints, int n, u
 			return false;
 		}
 
-		ARR_FIND( 0, cash_shop_items[tab].count, j, nameid == cash_shop_items[tab].item[j]->nameid );
+		ARR_FIND( 0, cash_shop_items[tab].count, j, nameid == cash_shop_items[tab].item[j]->nameid || nameid == itemdb_viewid(cash_shop_items[tab].item[j]->nameid) );
+
+		nameid = *( item_list + i * 5 ) = cash_shop_items[tab].item[j]->nameid; //item_avail replacement
 
 		if( j == cash_shop_items[tab].count || !itemdb_exists( nameid ) ){
 			clif_cashshop_result( sd, nameid, CASHSHOP_RESULT_ERROR_UNKONWN_ITEM );
 			return false;
 		}else if( !itemdb_isstackable( nameid ) && quantity > 1 ){
 			/* ShowWarning( "Player %s (%d:%d) sent a hexed packet trying to buy %d of nonstackable cash item %hu!\n", sd->status.name, sd->status.account_id, sd->status.char_id, quantity, nameid ); */
-			quantity = 1;
+			quantity = *( item_list + i * 5 + 2 ) = 1;
 		}
 
 		switch( pc_checkadditem( sd, nameid, quantity ) ){

+ 9 - 12
src/map/chrif.c

@@ -287,12 +287,12 @@ int chrif_save(struct map_session_data *sd, int flag) {
 
 	if (flag && sd->state.active) { //Store player data which is quitting
 		//FIXME: SC are lost if there's no connection at save-time because of the way its related data is cleared immediately after this function. [Skotlex]
-	if (chrif_isconnected()) {
-		chrif_save_scdata(sd);
-		chrif_skillcooldown_save(sd);
-		chrif_save_bsdata(sd);
-		chrif_req_login_operation(sd->status.account_id, sd->status.name, 7, 0, 2, sd->status.bank_vault); //save Bank data
-	}
+		if (chrif_isconnected()) {
+			chrif_save_scdata(sd);
+			chrif_skillcooldown_save(sd);
+			chrif_save_bsdata(sd);
+			chrif_req_login_operation(sd->status.account_id, sd->status.name, 7, 0, 2, sd->status.bank_vault); //save Bank data
+		}
 		if ( flag != 3 && !chrif_auth_logout(sd,flag == 1 ? ST_LOGOUT : ST_MAPCHANGE) )
 			ShowError("chrif_save: Failed to set up player %d:%d for proper quitting!\n", sd->status.account_id, sd->status.char_id);
 	}
@@ -1130,6 +1130,9 @@ int chrif_disconnectplayer(int fd) {
 			if( sd->state.vending ){
 				vending_closevending(sd);
 			}
+			else if( sd->state.buyingstore ){
+				buyingstore_close(sd);
+			}
 
 			map_quit(sd); //Remove it.
 		}
@@ -1378,12 +1381,6 @@ int chrif_load_scdata(int fd) {
 
 	pc_scdata_received(sd);
 #endif
-
-	if( sd->state.autotrade ) {
-		buyingstore_reopen( sd );
-		vending_reopen( sd );
-	}
-
 	return 0;
 }
 

+ 106 - 78
src/map/clif.c

@@ -4273,17 +4273,17 @@ void clif_getareachar_unit(struct map_session_data* sd,struct block_list *bl)
 
 //Modifies the type of damage according to status changes [Skotlex]
 //Aegis data specifies that: 4 endure against single hit sources, 9 against multi-hit.
-static inline int clif_calc_delay(int type, int div, int64 damage, int delay)
+static enum e_damage_type clif_calc_delay(char type, int div, int64 damage, int delay)
 {
-	return ( delay == 0 && damage > 0 ) ? ( div > 1 ? 9 : 4 ) : type;
+	return ( delay == 0 && damage > 0 ) ? ( div > 1 ? DMG_MULTI_HIT_ENDURE : DMG_ENDURE ) : (enum e_damage_type)type;
 }
 
 /*==========================================
  * Estimates walk delay based on the damage criteria. [Skotlex]
  *------------------------------------------*/
-static int clif_calc_walkdelay(struct block_list *bl,int delay, int type, int64 damage, int div_)
+static int clif_calc_walkdelay(struct block_list *bl,int delay, char type, int64 damage, int div_)
 {
-	if (type == 4 || type == 9 || damage <=0)
+	if (type == DMG_ENDURE || type == DMG_MULTI_HIT_ENDURE || damage <= 0)
 		return 0;
 
 	if (bl->type == BL_PC) {
@@ -4296,7 +4296,7 @@ static int clif_calc_walkdelay(struct block_list *bl,int delay, int type, int64
 	if (div_ > 1) //Multi-hit skills mean higher delays.
 		delay += battle_config.multihit_delay*(div_-1);
 
-	return delay>0?delay:1; //Return 1 to specify there should be no noticeable delay, but you should stop walking.
+	return (delay > 0) ? delay:1; //Return 1 to specify there should be no noticeable delay, but you should stop walking.
 }
 
 
@@ -4317,7 +4317,7 @@ static int clif_calc_walkdelay(struct block_list *bl,int delay, int type, int64
 ///     10 = critical hit
 ///     11 = lucky dodge
 ///     12 = (touch skill?)
-int clif_damage(struct block_list* src, struct block_list* dst, unsigned int tick, int sdelay, int ddelay, int64 sdamage, int div, int type, int64 sdamage2)
+int clif_damage(struct block_list* src, struct block_list* dst, unsigned int tick, int sdelay, int ddelay, int64 sdamage, int div, enum e_damage_type type, int64 sdamage2)
 {
 	unsigned char buf[33];
 	struct status_change *sc;
@@ -4401,7 +4401,7 @@ int clif_damage(struct block_list* src, struct block_list* dst, unsigned int tic
  *------------------------------------------*/
 void clif_takeitem(struct block_list* src, struct block_list* dst)
 {
-	//clif_damage(src,dst,0,0,0,0,0,1,0);
+	//clif_damage(src,dst,0,0,0,0,0,DMG_PICKUP_ITEM,0);
 	unsigned char buf[32];
 
 	nullpo_retv(src);
@@ -8368,8 +8368,16 @@ void clif_GM_kick(struct map_session_data *sd,struct map_session_data *tsd)
 
 	if( fd > 0 )
 		clif_authfail_fd(fd, 15);
-	else
+	else {
+		// Close vending/buyingstore
+		if (sd) {
+			if (tsd->state.vending)
+				vending_closevending(tsd);
+			else if (tsd->state.buyingstore)
+				buyingstore_close(tsd);
+		}
 		map_quit(tsd);
+	}
 
 	if( sd )
 		clif_GM_kickack(sd,tsd->status.account_id);
@@ -9700,11 +9708,10 @@ void clif_parse_LoadEndAck(int fd,struct map_session_data *sd)
 		// Notify everyone that this char logged in [Skotlex].
 		map_foreachpc(clif_friendslist_toggle_sub, sd->status.account_id, sd->status.char_id, 1);
 
-		// Set the initial idle time
-		sd->idletime = last_tick;
-
-		//Login Event
-		npc_script_event(sd, NPCE_LOGIN);
+		if (!sd->state.autotrade) { // Don't trigger NPC event or opening vending/buyingstore will be failed
+			//Login Event
+			npc_script_event(sd, NPCE_LOGIN);
+		}
 	} else {
 		//For some reason the client "loses" these on warp/map-change.
 		clif_updatestatus(sd,SP_STR);
@@ -9803,7 +9810,8 @@ void clif_parse_LoadEndAck(int fd,struct map_session_data *sd)
 		clif_showvendingboard(&sd->bl,sd->message,0);
 	}
 
-	if(map[sd->bl.m].flag.loadevent) // Lance
+	// Don't trigger NPC event or opening vending/buyingstore will be failed
+	if(!sd->state.autotrade && map[sd->bl.m].flag.loadevent) // Lance
 		npc_script_event(sd, NPCE_LOADMAP);
 
 	if (pc_checkskill(sd, SG_DEVIL) && !pc_nextjobexp(sd))
@@ -9999,7 +10007,8 @@ void clif_parse_WalkToXY(int fd, struct map_session_data *sd)
 	RFIFOPOS(fd, packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[0], &x, &y, NULL);
 
 	//Set last idle time... [Skotlex]
-	sd->idletime = last_tick;
+	if (battle_config.idletime_option&IDLE_WALK)
+		sd->idletime = last_tick;
 
 	unit_walktoxy(&sd->bl, x, y, 4);
 }
@@ -10113,6 +10122,9 @@ void clif_parse_GlobalMessage(int fd, struct map_session_data* sd)
 		sd->cantalk_tick = gettick() + battle_config.min_chat_delay;
 	}
 
+	if (battle_config.idletime_option&IDLE_CHAT)
+		sd->idletime = last_tick;
+
 	if( sd->gcbind ) {
 		channel_send(sd->gcbind,sd,message);
 		return;
@@ -10147,9 +10159,6 @@ void clif_parse_GlobalMessage(int fd, struct map_session_data* sd)
 	map_foreachinrange(npc_chat_sub, &sd->bl, AREA_SIZE, BL_NPC, text, textlen, &sd->bl);
 #endif
 
-	// Reset idle time when using normal chat.
-	sd->idletime = last_tick;
-
 	// Chat logging type 'O' / Global Chat
 	log_chat(LOG_CHAT_GLOBAL, 0, sd->status.char_id, sd->status.account_id, mapindex_id2name(sd->mapindex), sd->bl.x, sd->bl.y, NULL, message);
 }
@@ -10245,6 +10254,9 @@ void clif_parse_Emotion(int fd, struct map_session_data *sd)
 		}
 		sd->emotionlasttime = time(NULL);
 
+		if (battle_config.idletime_option&IDLE_EMOTION)
+			sd->idletime = last_tick;
+
 		if(battle_config.client_reshuffle_dice && emoticon>=E_DICE1 && emoticon<=E_DICE6) {// re-roll dice
 			emoticon = rnd()%6+E_DICE1;
 		}
@@ -10284,13 +10296,13 @@ void clif_parse_ActionRequest_sub(struct map_session_data *sd, int action_type,
 		return;
 	}
 
+	// Statuses that don't let the player sit / attack / talk with NPCs(targeted)
+	// (not all are included in pc_can_attack)
 	if (sd->sc.count &&
 		(sd->sc.data[SC_TRICKDEAD] ||
 		(sd->sc.data[SC_AUTOCOUNTER] && action_type != 0x07) ||
 		 sd->sc.data[SC_BLADESTOP] ||
-		 sd->sc.data[SC__MANHOLE] ||
-		 sd->sc.data[SC_CURSEDCIRCLE_ATKER] ||
-		 sd->sc.data[SC_CURSEDCIRCLE_TARGET] ))
+		 sd->sc.data[SC__MANHOLE] ))
 		return;
 
 	pc_stop_walking(sd, 1);
@@ -10313,9 +10325,6 @@ void clif_parse_ActionRequest_sub(struct map_session_data *sd, int action_type,
 		if( sd->sc.option&OPTION_COSTUME )
 			return;
 
-		if( sd->sc.data[SC_BASILICA] || sd->sc.data[SC__SHADOWFORM] )
-			return;
-
 		if (!battle_config.sdelay_attack_enable && pc_checkskill(sd, SA_FREECAST) <= 0) {
 			if (DIFF_TICK(tick, sd->ud.canact_tick) < 0) {
 				clif_skill_fail(sd, 1, USESKILL_FAIL_SKILLINTERVAL, 0);
@@ -10324,7 +10333,8 @@ void clif_parse_ActionRequest_sub(struct map_session_data *sd, int action_type,
 		}
 
 		pc_delinvincibletimer(sd);
-		sd->idletime = last_tick;
+		if (battle_config.idletime_option&IDLE_ATTACK)
+			sd->idletime = last_tick;
 		unit_attack(&sd->bl, target_id, action_type != 0);
 	break;
 	case 0x02: // sitdown
@@ -10348,7 +10358,9 @@ void clif_parse_ActionRequest_sub(struct map_session_data *sd, int action_type,
 		)) //No sitting during these states either.
 			break;
 
-		sd->idletime = last_tick;
+		if (battle_config.idletime_option&IDLE_SIT)
+			sd->idletime = last_tick;
+
 		skill_sit(sd, 1);
 		pc_setsit(sd);
 		clif_sitting(&sd->bl);
@@ -10360,10 +10372,12 @@ void clif_parse_ActionRequest_sub(struct map_session_data *sd, int action_type,
 			return;
 		}
 
-		sd->idletime = last_tick;
-		pc_setstand(sd);
-		skill_sit(sd, 0);
-		clif_standing(&sd->bl);
+		if (pc_setstand(sd, false)) {
+			if (battle_config.idletime_option&IDLE_SIT)
+				sd->idletime = last_tick;
+			skill_sit(sd, 0);
+			clif_standing(&sd->bl);
+		}
 	break;
 	}
 }
@@ -10444,8 +10458,8 @@ void clif_parse_WisMessage(int fd, struct map_session_data* sd)
 		sd->cantalk_tick = gettick() + battle_config.min_chat_delay;
 	}
 
-	// Reset idle time when using whisper/main chat.
-	sd->idletime = last_tick;
+	if (battle_config.idletime_option&IDLE_CHAT)
+		sd->idletime = last_tick;
 
 	// Chat logging type 'W' / Whisper
 	log_chat(LOG_CHAT_WHISPER, 0, sd->status.char_id, sd->status.account_id, mapindex_id2name(sd->mapindex), sd->bl.x, sd->bl.y, target, message);
@@ -10633,6 +10647,9 @@ void clif_parse_DropItem(int fd, struct map_session_data *sd){
 		if (!pc_dropitem(sd, item_index, item_amount))
 			break;
 
+		if (battle_config.idletime_option&IDLE_DROPITEM)
+			sd->idletime = last_tick;
+
 		return;
 	}
 
@@ -10658,7 +10675,8 @@ void clif_parse_UseItem(int fd, struct map_session_data *sd)
 		return;
 
 	//Whether the item is used or not is irrelevant, the char ain't idle. [Skotlex]
-	sd->idletime = last_tick;
+	if (battle_config.idletime_option&IDLE_USEITEM)
+		sd->idletime = last_tick;
 	n = RFIFOW(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[0])-2;
 
 	if(n <0 || n >= MAX_INVENTORY)
@@ -10704,6 +10722,9 @@ void clif_parse_EquipItem(int fd,struct map_session_data *sd)
 		return;
 	}
 
+	if (battle_config.idletime_option&IDLE_USEITEM)
+		sd->idletime = last_tick;
+
 	//Client doesn't send the position for ammo.
 	if(sd->inventory_data[index]->type == IT_AMMO)
 		pc_equipitem(sd,index,EQP_AMMO);
@@ -10740,6 +10761,9 @@ void clif_parse_UnequipItem(int fd,struct map_session_data *sd)
 
 	index = RFIFOW(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[0])-2;
 
+	if (battle_config.idletime_option&IDLE_USEITEM)
+		sd->idletime = last_tick;
+
 	pc_unequipitem(sd,index,1);
 }
 
@@ -11297,7 +11321,8 @@ void clif_parse_UseSkillToId(int fd, struct map_session_data *sd)
 	}
 
 	// Whether skill fails or not is irrelevant, the char ain't idle. [Skotlex]
-	sd->idletime = last_tick;
+	if (battle_config.idletime_option&IDLE_USESKILLTOID)
+		sd->idletime = last_tick;
 
 	if( sd->npc_id ){
 #ifdef RENEWAL
@@ -11397,7 +11422,8 @@ static void clif_parse_UseSkillToPosSub(int fd, struct map_session_data *sd, uin
 	}
 
 	//Whether skill fails or not is irrelevant, the char ain't idle. [Skotlex]
-	sd->idletime = last_tick;
+	if (battle_config.idletime_option&IDLE_USESKILLTOPOS)
+		sd->idletime = last_tick;
 
 	if( skill_isNotOk(skill_id, sd) )
 		return;
@@ -12177,8 +12203,8 @@ void clif_parse_PartyMessage(int fd, struct map_session_data* sd){
 		sd->cantalk_tick = gettick() + battle_config.min_chat_delay;
 	}
 
-	// Reset idle time when using party chat.
-	sd->idletime = last_tick;
+	if (battle_config.idletime_option&IDLE_CHAT)
+		sd->idletime = last_tick;
 
 	party_send_message(sd, text, textlen);
 }
@@ -12773,8 +12799,8 @@ void clif_parse_GuildMessage(int fd, struct map_session_data* sd){
 		sd->cantalk_tick = gettick() + battle_config.min_chat_delay;
 	}
 
-	// Reset idle time when using guild chat.
-	sd->idletime = last_tick;
+	if (battle_config.idletime_option&IDLE_CHAT)
+		sd->idletime = last_tick;
 
 	if( sd->bg_id )
 		bg_send_message(sd, text, textlen);
@@ -12975,43 +13001,43 @@ void clif_parse_GMKick(int fd, struct map_session_data *sd)
 	}
 
 	switch (target->type) {
-	case BL_PC:
-	{
-		char command[NAME_LENGTH+6];
-		safesnprintf(command,sizeof(command),"%ckick %s", atcommand_symbol, status_get_name(target));
-		is_atcommand(fd, sd, command, 1);
-	}
-	break;
+		case BL_PC:
+		{
+			char command[NAME_LENGTH+6];
+			safesnprintf(command,sizeof(command),"%ckick %s", atcommand_symbol, status_get_name(target));
+			is_atcommand(fd, sd, command, 1);
+		}
+		break;
 
-	/**
-	 * This one does not invoke any atcommand, so we need to check for permissions.
-	 */
-	case BL_MOB:
-	{
-		char command[100];
-		if( !pc_can_use_command(sd, "killmonster", COMMAND_ATCOMMAND)) {
-			clif_GM_kickack(sd, 0);
-			return;
+		/**
+		 * This one does not invoke any atcommand, so we need to check for permissions.
+		 */
+		case BL_MOB:
+		{
+			char command[100];
+			if( !pc_can_use_command(sd, "killmonster", COMMAND_ATCOMMAND)) {
+				clif_GM_kickack(sd, 0);
+				return;
+			}
+			safesnprintf(command,sizeof(command),"/kick %s (%d)", status_get_name(target), status_get_class(target));
+			log_atcommand(sd, command);
+			status_percent_damage(&sd->bl, target, 100, 0, true); // can invalidate 'target'
 		}
-		safesnprintf(command,sizeof(command),"/kick %s (%d)", status_get_name(target), status_get_class(target));
-		log_atcommand(sd, command);
-		status_percent_damage(&sd->bl, target, 100, 0, true); // can invalidate 'target'
-	}
-	break;
+		break;
 
-	case BL_NPC:
-	{
-		struct npc_data* nd = (struct npc_data *)target;
-		if( pc_can_use_command(sd, "unloadnpc", COMMAND_ATCOMMAND)) {
-			npc_unload_duplicates(nd);
-			npc_unload(nd,true);
-			npc_read_event_script();
+		case BL_NPC:
+		{
+			struct npc_data* nd = (struct npc_data *)target;
+			if( pc_can_use_command(sd, "unloadnpc", COMMAND_ATCOMMAND)) {
+				npc_unload_duplicates(nd);
+				npc_unload(nd,true);
+				npc_read_event_script();
+			}
 		}
-	}
-	break;
+		break;
 
-	default:
-		clif_GM_kickack(sd, 0);
+		default:
+			clif_GM_kickack(sd, 0);
 	}
 }
 
@@ -14954,7 +14980,8 @@ void clif_parse_CashShopReqTab(int fd, struct map_session_data *sd) {
 	WFIFOW(fd, 8) = cash_shop_items[tab].count;
 
 	for( j = 0; j < cash_shop_items[tab].count; j++ ) {
-		WFIFOW(fd, 10 + ( 6 * j ) ) = cash_shop_items[tab].item[j]->nameid;
+		struct item_data *id = itemdb_search(cash_shop_items[tab].item[j]->nameid);
+		WFIFOW(fd, 10 + ( 6 * j ) ) = (id->view_id) ? id->view_id : cash_shop_items[tab].item[j]->nameid;
 		WFIFOL(fd, 12 + ( 6 * j ) ) = cash_shop_items[tab].item[j]->price;
 	}
 
@@ -14976,7 +15003,8 @@ void clif_cashshop_list( int fd ){
 		WFIFOW( fd, 6 ) = tab;
 
 		for( i = 0, offset = 8; i < cash_shop_items[tab].count; i++, offset += 6 ){
-			WFIFOW( fd, offset ) = cash_shop_items[tab].item[i]->nameid;
+			struct item_data *id = itemdb_search(cash_shop_items[tab].item[i]->nameid);
+			WFIFOW( fd, offset ) = (id->view_id) ? id->view_id : cash_shop_items[tab].item[i]->nameid;
 			WFIFOL( fd, offset + 2 ) = cash_shop_items[tab].item[i]->price;
 		}
 
@@ -15773,8 +15801,8 @@ void clif_parse_BattleChat(int fd, struct map_session_data* sd){
 		sd->cantalk_tick = gettick() + battle_config.min_chat_delay;
 	}
 
-	// Reset idle time when using battleground chat.
-	sd->idletime = last_tick;
+	if (battle_config.idletime_option&IDLE_CHAT)
+		sd->idletime = last_tick;
 
 	bg_send_message(sd, text, textlen);
 }
@@ -16661,15 +16689,15 @@ int clif_elementalconverter_list(struct map_session_data *sd) {
 /**
  * Rune Knight
  **/
-void clif_millenniumshield(struct map_session_data *sd, short shields ) {
+void clif_millenniumshield(struct block_list *bl, short shields) {
 #if PACKETVER >= 20081217
 	unsigned char buf[10];
 
 	WBUFW(buf,0) = 0x440;
-	WBUFL(buf,2) = sd->bl.id;
+	WBUFL(buf,2) = bl->id;
 	WBUFW(buf,6) = shields;
 	WBUFW(buf,8) = 0;
-	clif_send(buf,packet_len(0x440),&sd->bl,AREA);
+	clif_send(buf,packet_len(0x440),bl,AREA);
 #endif
 }
 /**

+ 18 - 2
src/map/clif.h

@@ -389,6 +389,22 @@ enum e_personalinfo {
 	PINFO_MAX,
 };
 
+enum e_damage_type {
+	DMG_NORMAL = 0,			/// damage [ damage: total damage, div: amount of hits, damage2: assassin dual-wield damage ]
+	DMG_PICKUP_ITEM,		/// pick up item
+	DMG_SIT_DOWN,			/// sit down
+	DMG_STAND_UP,			/// stand up
+	DMG_ENDURE,				/// damage (endure)
+	DMG_SPLASH,				/// (splash?)
+	DMG_SKILL,				/// (skill?)
+	DMG_REPEAT,				/// (repeat damage?)
+	DMG_MULTI_HIT,			/// multi-hit damage
+	DMG_MULTI_HIT_ENDURE,	/// multi-hit damage (endure)
+	DMG_CRITICAL,			/// critical hit
+	DMG_LUCY_DODGE,			/// lucky dodge
+	DMG_TOUCH,				/// (touch skill?)
+};
+
 int clif_setip(const char* ip);
 void clif_setbindip(const char* ip);
 void clif_setport(uint16 port);
@@ -433,7 +449,7 @@ void clif_dropitem(struct map_session_data *sd,int n,int amount);	//self
 void clif_delitem(struct map_session_data *sd,int n,int amount, short reason); //self
 void clif_updatestatus(struct map_session_data *sd,int type);	//self
 void clif_changestatus(struct map_session_data* sd,int type,int val);	//area
-int clif_damage(struct block_list* src, struct block_list* dst, unsigned int tick, int sdelay, int ddelay, int64 sdamage, int div, int type, int64 sdamage2);	// area
+int clif_damage(struct block_list* src, struct block_list* dst, unsigned int tick, int sdelay, int ddelay, int64 sdamage, int div, enum e_damage_type type, int64 sdamage2);	// area
 void clif_takeitem(struct block_list* src, struct block_list* dst);
 void clif_sitting(struct block_list* bl);
 void clif_standing(struct block_list* bl);
@@ -828,7 +844,7 @@ void clif_msgtable_num(int fd, int line, int num);
 
 int clif_elementalconverter_list(struct map_session_data *sd);
 
-void clif_millenniumshield(struct map_session_data *sd, short shields );
+void clif_millenniumshield(struct block_list *bl, short shields);
 
 int clif_spellbook_list(struct map_session_data *sd);
 

+ 4 - 4
src/map/elemental.c

@@ -808,14 +808,14 @@ static bool read_elementaldb_sub(char* str[], int columns, int current) {
 	status->race = atoi(str[20]);
 
 	ele = atoi(str[21]);
-	status->def_ele = ele%10;
-	status->ele_lv = ele/20;
+	status->def_ele = ele%20;
+	status->ele_lv = (unsigned char)floor(ele/20.);
 	if( !CHK_ELEMENT(status->def_ele) ) {
 		ShowWarning("read_elementaldb_sub: Elemental %d has invalid element type %d (max element is %d)\n", db->class_, status->def_ele, ELE_ALL - 1);
 		status->def_ele = ELE_NEUTRAL;
 	}
-	if( status->ele_lv < 1 || status->ele_lv > 4 ) {
-		ShowWarning("read_elementaldb_sub: Elemental %d has invalid element level %d (max is 4)\n", db->class_, status->ele_lv);
+	if( !CHK_ELEMENT_LEVEL(status->ele_lv) ) {
+		ShowWarning("read_elementaldb_sub: Elemental %d has invalid element level %d (max is %d)\n", db->class_, status->ele_lv, MAX_ELE_LEVEL);
 		status->ele_lv = 1;
 	}
 

+ 10 - 2
src/map/map.c

@@ -70,7 +70,7 @@ char map_server_db[32] = "ragnarok";
 Sql* mmysql_handle;
 
 int db_use_sqldbs = 0;
-char buyingstore_db[32] = "buyingstores";
+char buyingstores_db[32] = "buyingstores";
 char buyingstore_items_db[32] = "buyingstore_items";
 char item_db_db[32] = "item_db";
 char item_db2_db[32] = "item_db2";
@@ -1652,6 +1652,9 @@ int map_quit(struct map_session_data *sd) {
 	if (sd->npc_timer_id != INVALID_TIMER) //Cancel the event timer.
 		npc_timerevent_quit(sd);
 
+	if (sd->autotrade_tid != INVALID_TIMER)
+		delete_timer(sd->autotrade_tid, pc_autotrade_timer);
+
 	if (sd->npc_id)
 		npc_event_dequeue(sd);
 
@@ -1765,6 +1768,9 @@ int map_quit(struct map_session_data *sd) {
 	if (sd->state.vending)
 		idb_remove(vending_getdb(), sd->status.char_id);
 
+	if (sd->state.buyingstore)
+		idb_remove(buyingstore_getdb(), sd->status.char_id);
+
 	pc_damage_log_clear(sd,0);
 	party_booking_delete(sd); // Party Booking [Spiria]
 	pc_makesavestatus(sd);
@@ -3584,7 +3590,7 @@ int inter_config_read(char *cfgName)
 			continue;
 
 		if( strcmpi( w1, "buyingstore_db" ) == 0 )
-			strcpy( buyingstore_db, w2 );
+			strcpy( buyingstores_db, w2 );
 		else if( strcmpi( w1, "buyingstore_items_db" ) == 0 )
 			strcpy( buyingstore_items_db, w2 );
 		else if(strcmpi(w1,"item_db_db")==0)
@@ -3878,6 +3884,7 @@ void do_final(void)
 	do_final_cashshop();
 	do_final_channel(); //should be called after final guild
 	do_final_vending();
+	do_final_buyingstore();
 
 	map_db->destroy(map_db, map_db_final);
 
@@ -4188,6 +4195,7 @@ int do_init(int argc, char *argv[])
 	do_init_battleground();
 	do_init_duel();
 	do_init_vending();
+	do_init_buyingstore();
 
 	npc_event_do_oninit();	// Init npcs (OnInit)
 

+ 9 - 5
src/map/map.h

@@ -304,8 +304,8 @@ enum bl_type {
 enum npc_subtype { WARP, SHOP, SCRIPT, CASHSHOP, ITEMSHOP, POINTSHOP, TOMB };
 
 enum e_race {
-	RC_NONE_=-1, //don't give us bonus
-	RC_FORMLESS=0,
+	RC_NONE_ = -1, //don't give us bonus
+	RC_FORMLESS = 0,
 	RC_UNDEAD,
 	RC_BRUTE,
 	RC_PLANT,
@@ -321,7 +321,7 @@ enum e_race {
 };
 
 enum e_classAE {
-	CLASS_NONE=-1, //don't give us bonus
+	CLASS_NONE = -1, //don't give us bonus
 	CLASS_NORMAL = 0,
 	CLASS_BOSS,
 	CLASS_GUARDIAN,
@@ -340,7 +340,8 @@ enum e_race2 {
 	RC2_MAX
 };
 
-enum e_elemen {
+/// Element list
+enum e_element {
 	ELE_NONE=-1,
 	ELE_NEUTRAL=0,
 	ELE_WATER,
@@ -356,6 +357,8 @@ enum e_elemen {
 	ELE_MAX
 };
 
+#define MAX_ELE_LEVEL 4 /// Maximum Element level
+
 enum mob_ai {
 	AI_NONE = 0,
 	AI_ATTACK,
@@ -868,6 +871,7 @@ void map_addmap2db(struct map_data *m);
 void map_removemapdb(struct map_data *m);
 
 #define CHK_ELEMENT(ele) ((ele) > ELE_NONE && (ele) < ELE_MAX) /// Check valid Element
+#define CHK_ELEMENT_LEVEL(lv) ((lv) >= 1 && (lv) <= MAX_ELE_LEVEL) /// Check valid element level
 #define CHK_RACE(race) ((race) > RC_NONE_ && (race) < RC_MAX) /// Check valid Race
 #define CHK_RACE2(race2) ((race2) >= RC2_NONE && (race2) < RC2_MAX) /// Check valid Race2
 #define CHK_CLASS(class_) ((class_) > CLASS_NONE && (class_) < CLASS_MAX) /// Check valid Class
@@ -932,7 +936,7 @@ extern int db_use_sqldbs;
 extern Sql* mmysql_handle;
 extern Sql* logmysql_handle;
 
-extern char buyingstore_db[32];
+extern char buyingstores_db[32];
 extern char buyingstore_items_db[32];
 extern char item_db_db[32];
 extern char item_db2_db[32];

+ 4 - 4
src/map/mercenary.c

@@ -514,16 +514,16 @@ static bool mercenary_readdb_sub(char* str[], int columns, int current)
 	status->race = atoi(str[20]);
 
 	ele = atoi(str[21]);
-	status->def_ele = ele%10;
-	status->ele_lv = ele/20;
+	status->def_ele = ele%20;
+	status->ele_lv = (unsigned char)floor(ele/20.);
 	if( !CHK_ELEMENT(status->def_ele) )
 	{
 		ShowWarning("Mercenary %d has invalid element type %d (max element is %d)\n", db->class_, status->def_ele, ELE_ALL - 1);
 		status->def_ele = ELE_NEUTRAL;
 	}
-	if( status->ele_lv < 1 || status->ele_lv > 4 )
+	if( !CHK_ELEMENT_LEVEL(status->ele_lv) )
 	{
-		ShowWarning("Mercenary %d has invalid element level %d (max is 4)\n", db->class_, status->ele_lv);
+		ShowWarning("Mercenary %d has invalid element level %d (max is %d)\n", db->class_, status->ele_lv, MAX_ELE_LEVEL);
 		status->ele_lv = 1;
 	}
 

+ 26 - 16
src/map/mob.c

@@ -2266,13 +2266,21 @@ int mob_dead(struct mob_data *md, struct block_list *src, int type)
 
 			if (map[m].flag.nobaseexp || !md->db->base_exp)
 				base_exp = 0;
-			else
-				base_exp = (unsigned int)cap_value(md->db->base_exp * per * bonus/100. * map[m].adjust.bexp/100., 1, UINT_MAX);
+			else {
+				double exp = apply_rate2(md->db->base_exp, per, 1);
+				exp = apply_rate(exp, bonus);
+				exp = apply_rate(exp, map[m].adjust.bexp);
+				base_exp = (unsigned int)cap_value(exp, 1, UINT_MAX);
+			}
 
 			if (map[m].flag.nojobexp || !md->db->job_exp || md->dmglog[i].flag == MDLF_HOMUN) //Homun earned job-exp is always lost.
 				job_exp = 0;
-			else
-				job_exp = (unsigned int)cap_value(md->db->job_exp * per * bonus/100. * map[m].adjust.jexp/100., 1, UINT_MAX);
+			else {
+				double exp = apply_rate2(md->db->job_exp, per, 1);
+				exp = apply_rate(exp, bonus);
+				exp = apply_rate(exp, map[m].adjust.jexp);
+				job_exp = (unsigned int)cap_value(exp, 1, UINT_MAX);
+			}
 
 			if ( ( temp = tmpsd[i]->status.party_id)>0 ) {
 				int j;
@@ -2310,8 +2318,10 @@ int mob_dead(struct mob_data *md, struct block_list *src, int type)
 					if( md->dmglog[i].flag != MDLF_PET || battle_config.pet_attack_exp_to_master ) {
 #ifdef RENEWAL_EXP
 						int rate = pc_level_penalty_mod(tmpsd[i], md->level, md->status.class_, 1);
-						base_exp = (unsigned int)cap_value(base_exp * rate / 100, 1, UINT_MAX);
-						job_exp = (unsigned int)cap_value(job_exp * rate / 100, 1, UINT_MAX);
+						if (rate != 100) {
+							base_exp = (unsigned int)cap_value(apply_rate(base_exp, rate), 1, UINT_MAX);
+							job_exp = (unsigned int)cap_value(apply_rate(job_exp, rate), 1, UINT_MAX);
+						}
 #endif
 						pc_gainexp(tmpsd[i], &md->bl, base_exp, job_exp, false);
 					}
@@ -2395,7 +2405,7 @@ int mob_dead(struct mob_data *md, struct block_list *src, int type)
 			}
 #ifdef RENEWAL_DROP
 			if( drop_modifier != 100 ) {
-				drop_rate = drop_rate * drop_modifier / 100;
+				drop_rate = apply_rate(drop_rate, drop_modifier);
 				if( drop_rate < 1 )
 					drop_rate = 1;
 			}
@@ -3768,14 +3778,14 @@ static bool mob_parse_dbrow(char** str)
 	status->race = atoi(str[23]);
 
 	i = atoi(str[24]); //Element
-	status->def_ele = i%10;
-	status->ele_lv = i/20;
+	status->def_ele = i%20;
+	status->ele_lv = (unsigned char)floor(i/20.);
 	if (!CHK_ELEMENT(status->def_ele)) {
 		ShowError("mob_parse_dbrow: Invalid element type %d for monster ID %d (max=%d).\n", status->def_ele, mob_id, ELE_ALL-1);
 		return false;
 	}
-	if (status->ele_lv < 1 || status->ele_lv > 4) {
-		ShowError("mob_parse_dbrow: Invalid element level %d for monster ID %d, must be in range 1-4.\n", status->ele_lv, mob_id);
+	if (!CHK_ELEMENT_LEVEL(status->ele_lv)) {
+		ShowError("mob_parse_dbrow: Invalid element level %d for monster ID %d, must be in range 1-%d.\n", status->ele_lv, mob_id, MAX_ELE_LEVEL);
 		return false;
 	}
 
@@ -4009,9 +4019,9 @@ static int mob_read_sqldb(void)
  *------------------------------------------*/
 static bool mob_readdb_mobavail(char* str[], int columns, int current)
 {
-	int mob_id, k;
+	int mob_id, sprite_id;
 
-	mob_id=atoi(str[0]);
+	mob_id = atoi(str[0]);
 
 	if(mob_db(mob_id) == mob_dummy)	// invalid class (probably undefined in db)
 	{
@@ -4019,13 +4029,13 @@ static bool mob_readdb_mobavail(char* str[], int columns, int current)
 		return false;
 	}
 
-	k=atoi(str[1]);
+	sprite_id = atoi(str[1]);
 
 	memset(&mob_db_data[mob_id]->vd, 0, sizeof(struct view_data));
-	mob_db_data[mob_id]->vd.class_=k;
+	mob_db_data[mob_id]->vd.class_ = sprite_id;
 
 	//Player sprites
-	if(pcdb_checkid(k) && columns==12) {
+	if(pcdb_checkid(sprite_id) && columns==12) {
 		mob_db_data[mob_id]->vd.sex=atoi(str[2]);
 		mob_db_data[mob_id]->vd.hair_style=atoi(str[3]);
 		mob_db_data[mob_id]->vd.hair_color=atoi(str[4]);

+ 75 - 71
src/map/npc.c

@@ -1249,7 +1249,7 @@ int npc_scriptcont(struct map_session_data* sd, int id, bool closing)
 		TBL_NPC* nd=(TBL_NPC*)map_id2bl(id);
 		ShowDebug("npc_scriptcont: %s (sd->npc_id=%d) is not %s (id=%d).\n",
 			nd_sd?(char*)nd_sd->name:"'Unknown NPC'", (int)sd->npc_id,
-		  	nd?(char*)nd->name:"'Unknown NPC'", (int)id);
+			nd?(char*)nd->name:"'Unknown NPC'", (int)id);
 		return 1;
 	}
 
@@ -1332,82 +1332,84 @@ int npc_buysellsel(struct map_session_data* sd, int id, int type)
 *------------------------------------------*/
 int npc_cashshop_buylist(struct map_session_data *sd, int points, int count, unsigned short* item_list)
 {
-    int i, j, amount, new_, w, vt;
+	int i, j, amount, new_, w, vt;
 	unsigned short nameid;
-    struct npc_data *nd = (struct npc_data *)map_id2bl(sd->npc_shopid);
-
-    if( !nd || nd->subtype != CASHSHOP )
-        return 1;
-
-    if( sd->state.trading )
-        return 4;
-
-    new_ = 0;
-    w = 0;
-    vt = 0; // Global Value
-
-    // Validating Process ----------------------------------------------------
-    for( i = 0; i < count; i++ )
-    {
-        nameid = item_list[i*2+1];
-        amount = item_list[i*2+0];
-
-        if( !itemdb_exists(nameid) || amount <= 0 )
-            return 5;
-
-        ARR_FIND(0,nd->u.shop.count,j,nd->u.shop.shop_item[j].nameid == nameid);
-        if( j == nd->u.shop.count || nd->u.shop.shop_item[j].value <= 0 )
-            return 5;
-
-        if( !itemdb_isstackable(nameid) && amount > 1 )
-        {
-            ShowWarning("Player %s (%d:%d) sent a hexed packet trying to buy %d of nonstackable item %hu!\n", sd->status.name, sd->status.account_id, sd->status.char_id, amount, nameid);
-            amount = item_list[i*2+0] = 1;
-        }
-
-        switch( pc_checkadditem(sd,nameid,amount) )
-        {
-            case CHKADDITEM_NEW:
-                new_++;
-                break;
-            case CHKADDITEM_OVERAMOUNT:
-                return 3;
-        }
-
-        vt += nd->u.shop.shop_item[j].value * amount;
-        w += itemdb_weight(nameid) * amount;
-    }
-
-    if( w + sd->weight > sd->max_weight )
-        return 3;
-    if( pc_inventoryblank(sd) < new_ )
-        return 3;
-    if( points > vt ) points = vt;
-
-    // Payment Process ----------------------------------------------------
-    if( sd->kafraPoints < points || sd->cashPoints < (vt - points) )
-        return 6;
+	struct npc_data *nd = (struct npc_data *)map_id2bl(sd->npc_shopid);
+
+	if( !nd || nd->subtype != CASHSHOP )
+		return 1;
+
+	if( sd->state.trading )
+		return 4;
+
+	new_ = 0;
+	w = 0;
+	vt = 0; // Global Value
+
+	// Validating Process ----------------------------------------------------
+	for( i = 0; i < count; i++ )
+	{
+		nameid = item_list[i*2+1];
+		amount = item_list[i*2+0];
+
+		if( !itemdb_exists(nameid) || amount <= 0 )
+			return 5;
+
+		ARR_FIND(0,nd->u.shop.count,j,nd->u.shop.shop_item[j].nameid == nameid || itemdb_viewid(nd->u.shop.shop_item[j].nameid) == nameid);
+		if( j == nd->u.shop.count || nd->u.shop.shop_item[j].value <= 0 )
+			return 5;
+
+		nameid = item_list[i*2+1] = nd->u.shop.shop_item[j].nameid; //item_avail replacement
+
+		if( !itemdb_isstackable(nameid) && amount > 1 )
+		{
+			ShowWarning("Player %s (%d:%d) sent a hexed packet trying to buy %d of nonstackable item %hu!\n", sd->status.name, sd->status.account_id, sd->status.char_id, amount, nameid);
+			amount = item_list[i*2+0] = 1;
+		}
+
+		switch( pc_checkadditem(sd,nameid,amount) )
+		{
+			case CHKADDITEM_NEW:
+				new_++;
+				break;
+			case CHKADDITEM_OVERAMOUNT:
+				return 3;
+		}
+
+		vt += nd->u.shop.shop_item[j].value * amount;
+		w += itemdb_weight(nameid) * amount;
+	}
+
+	if( w + sd->weight > sd->max_weight )
+		return 3;
+	if( pc_inventoryblank(sd) < new_ )
+		return 3;
+	if( points > vt ) points = vt;
+
+	// Payment Process ----------------------------------------------------
+	if( sd->kafraPoints < points || sd->cashPoints < (vt - points) )
+		return 6;
 	pc_paycash(sd,vt,points, LOG_TYPE_NPC);
 
-    // Delivery Process ----------------------------------------------------
-    for( i = 0; i < count; i++ )
-    {
-        struct item item_tmp;
+	// Delivery Process ----------------------------------------------------
+	for( i = 0; i < count; i++ )
+	{
+		struct item item_tmp;
 
-        nameid = item_list[i*2+1];
-        amount = item_list[i*2+0];
+		nameid = item_list[i*2+1];
+		amount = item_list[i*2+0];
 
-        memset(&item_tmp,0,sizeof(item_tmp));
+		memset(&item_tmp,0,sizeof(item_tmp));
 
-        if( !pet_create_egg(sd,nameid) )
-        {
-            item_tmp.nameid = nameid;
-            item_tmp.identify = 1;
-            pc_additem(sd,&item_tmp,amount,LOG_TYPE_NPC);
-        }
-    }
+		if( !pet_create_egg(sd,nameid) )
+		{
+			item_tmp.nameid = nameid;
+			item_tmp.identify = 1;
+			pc_additem(sd,&item_tmp,amount,LOG_TYPE_NPC);
+		}
+	}
 
-    return 0;
+	return 0;
 }
 
 //npc_buylist for script-controlled shops.
@@ -1460,12 +1462,14 @@ int npc_cashshop_buy(struct map_session_data *sd, unsigned short nameid, int amo
 	if( (item = itemdb_exists(nameid)) == NULL )
 		return 5; // Invalid Item
 
-	ARR_FIND(0, nd->u.shop.count, i, nd->u.shop.shop_item[i].nameid == nameid);
+	ARR_FIND(0, nd->u.shop.count, i, nd->u.shop.shop_item[i].nameid == nameid || itemdb_viewid(nd->u.shop.shop_item[i].nameid) == nameid);
 	if( i == nd->u.shop.count )
 		return 5;
 	if( nd->u.shop.shop_item[i].value <= 0 )
 		return 5;
 
+	nameid = nd->u.shop.shop_item[i].nameid; //item_avail replacement
+
 	if(!itemdb_isstackable(nameid) && amount > 1)
 	{
 		ShowWarning("Player %s (%d:%d) sent a hexed packet trying to buy %d of nonstackable item %hu!\n",

+ 140 - 59
src/map/pc.c

@@ -231,7 +231,7 @@ void pc_addspiritball(struct map_session_data *sd,int interval,int max)
 	sd->spirit_timer[i] = tid;
 	sd->spiritball++;
 	if( (sd->class_&MAPID_THIRDMASK) == MAPID_ROYAL_GUARD )
-		clif_millenniumshield(sd,sd->spiritball);
+		clif_millenniumshield(&sd->bl,sd->spiritball);
 	else
 		clif_spiritball(&sd->bl);
 }
@@ -274,7 +274,7 @@ void pc_delspiritball(struct map_session_data *sd,int count,int type)
 
 	if(!type) {
 		if( (sd->class_&MAPID_THIRDMASK) == MAPID_ROYAL_GUARD )
-			clif_millenniumshield(sd,sd->spiritball);
+			clif_millenniumshield(&sd->bl,sd->spiritball);
 		else
 			clif_spiritball(&sd->bl);
 	}
@@ -849,7 +849,7 @@ bool pc_isequipped(struct map_session_data *sd, unsigned short nameid)
 
 	for( i = 0; i < EQI_MAX; i++ )
 	{
-		int8 index = sd->equip_index[i], j;
+		short index = sd->equip_index[i], j;
 		if( index < 0 )
 			continue;
 		if( pc_is_same_equip_index((enum equip_index)i, sd->equip_index, index) )
@@ -1136,6 +1136,7 @@ bool pc_authok(struct map_session_data *sd, int login_id2, time_t expiration_tim
 	sd->npc_timer_id = INVALID_TIMER;
 	sd->pvp_timer = INVALID_TIMER;
 	sd->expiration_tid = INVALID_TIMER;
+	sd->autotrade_tid = INVALID_TIMER;
 
 #ifdef SECURE_NPCTIMEOUT
 	// Initialize to defaults/expected
@@ -1150,6 +1151,7 @@ bool pc_authok(struct map_session_data *sd, int login_id2, time_t expiration_tim
 	sd->cantalk_tick = tick;
 	sd->canskill_tick = tick;
 	sd->cansendmail_tick = tick;
+	sd->idletime = last_tick;
 
 	for(i = 0; i < MAX_SPIRITBALL; i++)
 		sd->spirit_timer[i] = INVALID_TIMER;
@@ -1453,8 +1455,12 @@ void pc_reg_received(struct map_session_data *sd)
 		clif_changeoption( &sd->bl );
 	}
 
-	if( sd->state.autotrade )
+	pc_check_expiration(sd);
+
+	if( sd->state.autotrade ) {
 		clif_parse_LoadEndAck(sd->fd, sd);
+		sd->autotrade_tid = add_timer(gettick() + battle_config.feature_autotrade_open_delay, pc_autotrade_timer, sd->bl.id, 0);
+	}
 }
 
 static int pc_calc_skillpoint(struct map_session_data* sd)
@@ -4612,7 +4618,7 @@ bool pc_isUseitem(struct map_session_data *sd,int n)
 			return false;
 		}
 		if( !pc_inventoryblank(sd) ) {
-			clif_colormes(sd, color_table[COLOR_RED], msg_txt(sd, 1477)); //Item cannot be open when inventory is full
+			clif_colormes(sd, color_table[COLOR_RED], msg_txt(sd, 732)); //Item cannot be open when inventory is full
 			return false;
 		}
 	}
@@ -4725,11 +4731,11 @@ int pc_useitem(struct map_session_data *sd,int n)
 					int e_tick = DIFF_TICK(sd->item_delay[i].tick, tick)/1000;
 					char e_msg[100];
 					if( e_tick > 99 )
-						sprintf(e_msg,msg_txt(sd,379), //Able to use %.1f min later.
-										(double)e_tick / 60);
+						sprintf(e_msg,msg_txt(sd,379), // Item Failed. [%s] is cooling down. Wait %.1f minutes.
+										itemdb_jname(sd->item_delay[i].nameid), (double)e_tick / 60);
 					else
-						sprintf(e_msg,msg_txt(sd,380), //Able to use %d sec later.
-										e_tick+1);
+						sprintf(e_msg,msg_txt(sd,380), // Item Failed. [%s] is cooling down. Wait %d seconds.
+										itemdb_jname(sd->item_delay[i].nameid), e_tick+1);
 					clif_colormes(sd,color_table[COLOR_YELLOW],e_msg);
 					return 0; // Delay has not expired yet
 				}
@@ -5146,8 +5152,11 @@ char pc_setpos(struct map_session_data* sd, unsigned short mapindex, int x, int
 		return 1;
 	}
 
+	if ( sd->state.autotrade && (sd->vender_id || sd->buyer_id) ) // Player with autotrade just causes clif glitch! @ FIXME
+		return 1;
+
 	if( battle_config.revive_onwarp && pc_isdead(sd) ) { //Revive dead people before warping them
-		pc_setstand(sd);
+		pc_setstand(sd, true);
 		pc_setrestartvalue(sd,1);
 	}
 
@@ -6212,38 +6221,41 @@ int pc_checkjoblevelup(struct map_session_data *sd)
 static void pc_calcexp(struct map_session_data *sd, unsigned int *base_exp, unsigned int *job_exp, struct block_list *src)
 {
 	int bonus = 0, vip_bonus_base = 0, vip_bonus_job = 0;
-	struct status_data *status = status_get_status_data(src);
-
-	if( sd->expaddrace[status->race] )
-		bonus += sd->expaddrace[status->race];
-	if( sd->expaddrace[RC_ALL] )
-		bonus += sd->expaddrace[RC_ALL];
-	if( sd->expaddclass[status->class_] )
-		bonus += sd->expaddclass[status->class_];
-	if( sd->expaddclass[CLASS_ALL] )
-		bonus += sd->expaddclass[CLASS_ALL];
-
-	if (battle_config.pk_mode &&
-		(int)(status_get_lv(src) - sd->status.base_level) >= 20)
-		bonus += 15; // pk_mode additional exp if monster >20 levels [Valaris]
-
-	if (sd->sc.data[SC_EXPBOOST]) {	
- 		bonus += sd->sc.data[SC_EXPBOOST]->val1;
-		if( battle_config.vip_bm_increase && pc_isvip(sd) ) // Increase Battle Manual EXP rate for VIP.
-			bonus += ( sd->sc.data[SC_EXPBOOST]->val1 / battle_config.vip_bm_increase );
-	}
+
+	if (src) {
+		struct status_data *status = status_get_status_data(src);
+
+		if( sd->expaddrace[status->race] )
+			bonus += sd->expaddrace[status->race];
+		if( sd->expaddrace[RC_ALL] )
+			bonus += sd->expaddrace[RC_ALL];
+		if( sd->expaddclass[status->class_] )
+			bonus += sd->expaddclass[status->class_];
+		if( sd->expaddclass[CLASS_ALL] )
+			bonus += sd->expaddclass[CLASS_ALL];
+
+		if (battle_config.pk_mode &&
+			(int)(status_get_lv(src) - sd->status.base_level) >= 20)
+			bonus += 15; // pk_mode additional exp if monster >20 levels [Valaris]
 
 #ifdef VIP_ENABLE
-	//EXP bonus for VIP player
-	if (src && src->type == BL_MOB && pc_isvip(sd)) {
-		vip_bonus_base = battle_config.vip_base_exp_increase;
-		vip_bonus_job = battle_config.vip_job_exp_increase;
-	}
+		//EXP bonus for VIP player
+		if (src && src->type == BL_MOB && pc_isvip(sd)) {
+			vip_bonus_base = battle_config.vip_base_exp_increase;
+			vip_bonus_job = battle_config.vip_job_exp_increase;
+		}
 #endif
+	}
+
+	if (&sd->sc && sd->sc.data[SC_EXPBOOST]) {
+		bonus += sd->sc.data[SC_EXPBOOST]->val1;
+		if( battle_config.vip_bm_increase && pc_isvip(sd) ) // Increase Battle Manual EXP rate for VIP.
+			bonus += ( sd->sc.data[SC_EXPBOOST]->val1 / battle_config.vip_bm_increase );
+	}
 
 	*base_exp = (unsigned int) cap_value(*base_exp + (double)*base_exp * (bonus + vip_bonus_base)/100., 1, UINT_MAX);
-	
-	if (sd->sc.data[SC_JEXPBOOST])
+
+	if (&sd->sc && sd->sc.data[SC_JEXPBOOST])
 		bonus += sd->sc.data[SC_JEXPBOOST]->val1;
 
 	*job_exp = (unsigned int) cap_value(*job_exp + (double)*job_exp * (bonus + vip_bonus_job)/100., 1, UINT_MAX);
@@ -6253,10 +6265,11 @@ static void pc_calcexp(struct map_session_data *sd, unsigned int *base_exp, unsi
 /*==========================================
  * Give x exp at sd player and calculate remaining exp for next lvl
  *------------------------------------------*/
-int pc_gainexp(struct map_session_data *sd, struct block_list *src, unsigned int base_exp,unsigned int job_exp,bool quest)
+int pc_gainexp(struct map_session_data *sd, struct block_list *src, unsigned int base_exp, unsigned int job_exp, bool quest)
 {
-	float nextbp=0, nextjp=0;
-	unsigned int nextb=0, nextj=0;
+	float nextbp = 0, nextjp = 0;
+	unsigned int nextb = 0, nextj = 0;
+
 	nullpo_ret(sd);
 
 	if(sd->bl.prev == NULL || pc_isdead(sd))
@@ -6268,7 +6281,7 @@ int pc_gainexp(struct map_session_data *sd, struct block_list *src, unsigned int
 	if(sd->status.guild_id>0)
 		base_exp-=guild_payexp(sd,base_exp);
 
-	if(src) pc_calcexp(sd, &base_exp, &job_exp, src);
+	pc_calcexp(sd, &base_exp, &job_exp, src); // Give (J)EXPBOOST for quests even if src is NULL.
 
 	nextb = pc_nextbaseexp(sd);
 	nextj = pc_nextjobexp(sd);
@@ -7049,7 +7062,7 @@ void pc_respawn(struct map_session_data* sd, clr_type clrtype)
 	if( sd->bg_id && bg_member_respawn(sd) )
 		return; // member revived by battleground
 
-	pc_setstand(sd);
+	pc_setstand(sd, true);
 	pc_setrestartvalue(sd,3);
 	if( pc_setpos(sd, sd->status.save_point.map, sd->status.save_point.x, sd->status.save_point.y, clrtype) )
 		clif_resurrection(&sd->bl, 1); //If warping fails, send a normal stand up packet.
@@ -7080,7 +7093,7 @@ void pc_damage(struct map_session_data *sd,struct block_list *src,unsigned int h
 		return;
 
 	if( pc_issit(sd) ) {
-		pc_setstand(sd);
+		pc_setstand(sd, true);
 		skill_sit(sd,0);
 	}
 
@@ -7461,7 +7474,7 @@ void pc_revive(struct map_session_data *sd,unsigned int hp, unsigned int sp) {
 	if(hp) clif_updatestatus(sd,SP_HP);
 	if(sp) clif_updatestatus(sd,SP_SP);
 
-	pc_setstand(sd);
+	pc_setstand(sd, true);
 	if(battle_config.pc_invincible_time > 0)
 		pc_setinvincibletimer(sd, battle_config.pc_invincible_time);
 
@@ -8066,6 +8079,8 @@ bool pc_jobchange(struct map_session_data *sd,int job, char upper)
 		elemental_delete(sd->ed, 0);
 	if (sd->state.vending)
 		vending_closevending(sd);
+	if (sd->state.buyingstore)
+		buyingstore_close(sd);
 
 	map_foreachinmap(jobchange_killclone, sd->bl.m, BL_MOB, sd->bl.id);
 
@@ -8228,7 +8243,7 @@ void pc_setoption(struct map_session_data *sd,int type)
 	} else if( !( type&OPTION_CART ) && p_type&OPTION_CART ){ //Cart Off
 		clif_clearcart(sd->fd);
 		if(pc_checkskill(sd, MC_PUSHCART) < 10)
-			status_calc_pc(sd,0); //Remove speed penalty.
+			status_calc_pc(sd,SCO_NONE); //Remove speed penalty.
 	}
 #endif
 
@@ -8391,6 +8406,33 @@ bool pc_candrop(struct map_session_data *sd, struct item *item)
 	return (itemdb_isdropable(item, pc_get_group_level(sd)));
 }
 
+/**
+ * Determines whether a player can attack based on status changes
+ *  Why not use status_check_skilluse?
+ *  "src MAY be null to indicate we shouldn't check it, this is a ground-based skill attack."
+ *  Even ground-based attacks should be blocked by these statuses
+ * Called from unit_attack and unit_attack_timer_sub
+ * @retval true Can attack
+ **/
+bool pc_can_attack( struct map_session_data *sd, int target_id ) {
+	nullpo_retr(false, sd);
+
+	if( sd->sc.data[SC_BASILICA] ||
+		sd->sc.data[SC__SHADOWFORM] ||
+		sd->sc.data[SC__MANHOLE] ||
+		sd->sc.data[SC_CURSEDCIRCLE_ATKER] ||
+		sd->sc.data[SC_CURSEDCIRCLE_TARGET] ||
+		sd->sc.data[SC_CRYSTALIZE] ||
+		sd->sc.data[SC_ALL_RIDING] || // The client doesn't let you, this is to make cheat-safe
+		sd->sc.data[SC_TRICKDEAD] ||
+		(sd->sc.data[SC_VOICEOFSIREN] && sd->sc.data[SC_VOICEOFSIREN]->val2 == target_id) ||
+		sd->sc.data[SC_BLADESTOP] ||
+		sd->sc.data[SC_DEEPSLEEP] )
+			return false;
+
+	return true;
+}
+
 /*==========================================
  * Read ram register for player sd
  * get val (int) from reg for player sd
@@ -8873,8 +8915,7 @@ static int pc_checkcombo(struct map_session_data *sd, struct item_data *data) {
 			bool found = false;
 			
 			for( k = 0; k < EQI_MAX; k++ ) {
-				int8 index;
-				index = sd->equip_index[k];
+				short index = sd->equip_index[k];
 				if( index < 0 )
 					continue;
 				if( pc_is_same_equip_index((enum equip_index)k, sd->equip_index, index) )
@@ -9014,8 +9055,8 @@ int pc_load_combo(struct map_session_data *sd) {
 	int i, ret = 0;
 	for( i = 0; i < EQI_MAX; i++ ) {
 		struct item_data *id = NULL;
-		int idx = sd->equip_index[i];
-		if( sd->equip_index[i] < 0 || !(id = sd->inventory_data[idx] ) )
+		short idx = sd->equip_index[i];
+		if( idx < 0 || !(id = sd->inventory_data[idx] ) )
 			continue;
 		if( id->combos_count )
 			ret += pc_checkcombo(sd,id);
@@ -9852,8 +9893,19 @@ int map_night_timer(int tid, unsigned int tick, int id, intptr_t data)
 	return 0;
 }
 
-void pc_setstand(struct map_session_data *sd){
-	nullpo_retv(sd);
+/**
+* Attempt to stand up a player
+* @param sd
+* @param force Ignore the check, ask player to stand up. Used in some cases like pc_damage(), pc_revive(), etc
+* @return True if success, Fals if failed
+*/
+bool pc_setstand(struct map_session_data *sd, bool force){
+	nullpo_ret(sd);
+
+	// Cannot stand yet
+	// TODO: Move to SCS_NOSTAND [Cydh]
+	if (!force && &sd->sc && (sd->sc.data[SC_SITDOWN_FORCE] || sd->sc.data[SC_BANANA_BOMB_SITDOWN]))
+		return false;
 
 	status_change_end(&sd->bl, SC_TENSIONRELAX, INVALID_TIMER);
 	clif_status_load(&sd->bl,SI_SIT,0);
@@ -9861,6 +9913,7 @@ void pc_setstand(struct map_session_data *sd){
 	//Reset sitting tick.
 	sd->ssregen.tick.hp = sd->ssregen.tick.sp = 0;
 	sd->state.dead_sit = sd->vd.dead_sit = 0;
+	return true;
 }
 
 /**
@@ -10730,9 +10783,17 @@ void pc_damage_log_clear(struct map_session_data *sd, int id)
 
 /* Status change data arrived from char-server */
 void pc_scdata_received(struct map_session_data *sd) {
+	// Nothing todo yet
+	return;
+}
+
+/** Check expiration time and rental items
+* @param sd
+*/
+void pc_check_expiration(struct map_session_data *sd) {
 	pc_inventory_rentals(sd);
 
-	if( sd->expiration_time != 0 ) { //Don't display if it's unlimited or unknow value
+	if (sd->expiration_time != 0) { //Don't display if it's unlimited or unknow value
 		time_t exp_time = sd->expiration_time;
 		char tmpstr[1024];
 
@@ -10758,6 +10819,25 @@ int pc_expiration_timer(int tid, unsigned int tick, int id, intptr_t data) {
 	return 0;
 }
 
+int pc_autotrade_timer(int tid, unsigned int tick, int id, intptr_t data) {
+	struct map_session_data *sd = map_id2sd(id);
+
+	if (!sd)
+		return 0;
+
+	sd->autotrade_tid = INVALID_TIMER;
+
+	buyingstore_reopen(sd);
+	vending_reopen(sd);
+
+	if (sd && !sd->vender_id && !sd->buyer_id) {
+		sd->state.autotrade = 0;
+		map_quit(sd);
+	}
+
+	return 0;
+}
+
 /* this timer exists only when a character with a expire timer > 24h is online */
 /* it loops thru online players once an hour to check whether a new < 24h is available */
 int pc_global_expiration_timer(int tid, unsigned int tick, int id, intptr_t data) {
@@ -11080,23 +11160,23 @@ short pc_get_itemgroup_bonus_group(struct map_session_data* sd, uint16 group_id)
 * @param index Known index item in inventory from sd->equip_index[] to compare with specified EQI in *equip_index
 * @return True if item in same inventory index, False if doesn't
 */
-bool pc_is_same_equip_index(enum equip_index eqi, int *equip_index, int8 index) {
-	if (index < 0 || index >= ARRAYLENGTH(equip_index))
-		return false;
+bool pc_is_same_equip_index(enum equip_index eqi, short *equip_index, short index) {
+	if (index < 0 || index >= MAX_INVENTORY)
+		return true;
 	// Dual weapon checks
 	if (eqi == EQI_HAND_R && equip_index[EQI_HAND_L] == index)
 		return true;
 	// Headgear with Mid & Low location
-	else if (eqi == EQI_HEAD_MID && equip_index[EQI_HEAD_LOW] == index)
+	if (eqi == EQI_HEAD_MID && equip_index[EQI_HEAD_LOW] == index)
 		return true;
 	// Headgear with Top & Mid or Low location
-	else if (eqi == EQI_HEAD_TOP && (equip_index[EQI_HEAD_MID] == index || equip_index[EQI_HEAD_LOW] == index))
+	if (eqi == EQI_HEAD_TOP && (equip_index[EQI_HEAD_MID] == index || equip_index[EQI_HEAD_LOW] == index))
 		return true;
 	// Headgear with Mid & Low location
-	else if (eqi == EQI_COSTUME_MID && equip_index[EQI_COSTUME_LOW] == index)
+	if (eqi == EQI_COSTUME_MID && equip_index[EQI_COSTUME_LOW] == index)
 		return true;
 	// Headgear with Top & Mid or Low location
-	else if (eqi == EQI_COSTUME_TOP && (equip_index[EQI_COSTUME_MID] == index || equip_index[EQI_COSTUME_LOW] == index))
+	if (eqi == EQI_COSTUME_TOP && (equip_index[EQI_COSTUME_MID] == index || equip_index[EQI_COSTUME_LOW] == index))
 		return true;
 	return false;
 }
@@ -11130,6 +11210,7 @@ void do_init_pc(void) {
 	add_timer_func_list(pc_talisman_timer, "pc_talisman_timer");
 	add_timer_func_list(pc_global_expiration_timer, "pc_global_expiration_timer");
 	add_timer_func_list(pc_expiration_timer, "pc_expiration_timer");
+	add_timer_func_list(pc_autotrade_timer, "pc_autotrade_timer");
 
 	add_timer(gettick() + autosave_interval, pc_autosave, 0, 0);
 

+ 21 - 3
src/map/pc.h

@@ -252,7 +252,7 @@ struct map_session_data {
 	struct registry save_reg;
 
 	struct item_data* inventory_data[MAX_INVENTORY]; // direct pointers to itemdb entries (faster than doing item_id lookups)
-	int equip_index[EQI_MAX];
+	short equip_index[EQI_MAX];
 	unsigned int weight,max_weight;
 	int cart_weight,cart_num,cart_weight_max;
 	int fd;
@@ -610,6 +610,7 @@ struct map_session_data {
 	time_t expiration_time;
 
 	short last_addeditem_index; /// Index of latest item added
+	int autotrade_tid;
 };
 
 struct eri *pc_sc_display_ers; /// Player's SC display table
@@ -665,6 +666,19 @@ enum ammo_type {
 	A_THROWWEAPON	//9
 };
 
+enum idletime_option {
+	IDLE_WALK          = 0x001,
+	IDLE_USESKILLTOID  = 0x002,
+	IDLE_USESKILLTOPOS = 0x004,
+	IDLE_USEITEM       = 0x008,
+	IDLE_ATTACK        = 0x010,
+	IDLE_CHAT          = 0x020,
+	IDLE_SIT           = 0x040,
+	IDLE_EMOTION       = 0x080,
+	IDLE_DROPITEM      = 0x100,
+	IDLE_ATCOMMAND     = 0x200,
+};
+
 struct {
 	unsigned int base_hp[MAX_LEVEL], base_sp[MAX_LEVEL]; //Storage for the first calculation with hp/sp factor and multiplicator
 	int hp_factor, hp_multiplicator, sp_factor;
@@ -837,6 +851,7 @@ short pc_checkequip(struct map_session_data *sd,int pos);
 bool pc_checkequip2(struct map_session_data *sd, unsigned short nameid, int min, int max);
 
 void pc_scdata_received(struct map_session_data *sd);
+void pc_check_expiration(struct map_session_data *sd);
 int pc_expiration_timer(int tid, unsigned int tick, int id, intptr_t data);
 int pc_global_expiration_timer(int tid, unsigned tick, int id, intptr_t data);
 void pc_expire_check(struct map_session_data *sd);
@@ -995,8 +1010,9 @@ struct map_session_data *pc_get_child(struct map_session_data *sd);
 void pc_bleeding (struct map_session_data *sd, unsigned int diff_tick);
 void pc_regen (struct map_session_data *sd, unsigned int diff_tick);
 
-void pc_setstand(struct map_session_data *sd);
+bool pc_setstand(struct map_session_data *sd, bool force);
 bool pc_candrop(struct map_session_data *sd,struct item *item);
+bool pc_can_attack(struct map_session_data *sd, int target_id);
 
 int pc_jobid2mapid(unsigned short b_class);	// Skotlex
 int pc_mapid2jobid(unsigned short class_, int sex);	// Skotlex
@@ -1106,10 +1122,12 @@ void pc_itemgrouphealrate_clear(struct map_session_data *sd);
 short pc_get_itemgroup_bonus(struct map_session_data* sd, unsigned short nameid);
 short pc_get_itemgroup_bonus_group(struct map_session_data* sd, uint16 group_id);
 
-bool pc_is_same_equip_index(enum equip_index eqi, int *equip_index, int8 index);
+bool pc_is_same_equip_index(enum equip_index eqi, short *equip_index, short index);
 /// Check if player is Taekwon Ranker and the level is >= 90 (battle_config.taekwon_ranker_min_lv)
 #define pc_is_taekwon_ranker(sd) (((sd)->class_&MAPID_UPPERMASK) == MAPID_TAEKWON && (sd)->status.base_level >= battle_config.taekwon_ranker_min_lv && pc_famerank((sd)->status.char_id,MAPID_TAEKWON))
 
+int pc_autotrade_timer(int tid, unsigned int tick, int id, intptr_t data);
+
 #if defined(RENEWAL_DROP) || defined(RENEWAL_EXP)
 int pc_level_penalty_mod(struct map_session_data *sd, int mob_level, uint32 mob_class, int type);
 #endif

+ 37 - 29
src/map/script.c

@@ -308,7 +308,7 @@ const char* parse_syntax_close_sub(const char* p,int* flag);
 const char* parse_syntax(const char* p);
 static int parse_syntax_for_flag = 0;
 
-extern int current_equip_item_index; //for New CARDS Scripts. It contains Inventory Index of the EQUIP_SCRIPT caller item. [Lupus]
+extern short current_equip_item_index; //for New CARDS Scripts. It contains Inventory Index of the EQUIP_SCRIPT caller item. [Lupus]
 int potion_flag=0; //For use on Alchemist improved potions/Potion Pitcher. [Skotlex]
 int potion_hp=0, potion_per_hp=0, potion_sp=0, potion_per_sp=0;
 int potion_target=0;
@@ -4910,7 +4910,7 @@ BUILDIN_FUNC(prompt)
 		sd->state.menu_or_input = 0;
 		pc_setreg(sd, add_str("@menu"), 0xff);
 		script_pushint(st, 0xff);
-		st->state = END;
+		st->state = RUN;
 	}
 	else
 	{// return selected option
@@ -6466,6 +6466,7 @@ BUILDIN_FUNC(checkweight2){
  * getitembound <item id>,<amount>,<type>{,<account ID>};
  * getitembound "<item id>",<amount>,<type>{,<account ID>};
  * Type:
+ *	0 - No bound
  *	1 - Account Bound
  *	2 - Guild Bound
  *	3 - Party Bound
@@ -6509,20 +6510,19 @@ BUILDIN_FUNC(getitem)
 	memset(&it,0,sizeof(it));
 	it.nameid = nameid;
 	it.identify = 1;
+	it.bound = BOUND_NONE;
 
 	if( !strcmp(command,"getitembound") ) {
 		char bound = script_getnum(st,4);
-		if( bound > BOUND_NONE && bound < BOUND_MAX ) {
-			it.bound = bound;
-			if( script_hasdata(st,5) )
-				sd = map_id2sd(script_getnum(st,5));
-			else
-				sd = script_rid2sd(st); // Attached player
-		}
-		else { //Not a correct bound type
+		if( bound < BOUND_NONE || bound >= BOUND_MAX ) {
 			ShowError("script_getitembound: Not a correct bound type! Type=%d\n",bound);
 			return SCRIPT_CMD_FAILURE;
 		}
+		if( script_hasdata(st,5) )
+			sd = map_id2sd(script_getnum(st,5));
+		else
+			sd = script_rid2sd(st); // Attached player
+		it.bound = bound;
 	} else if( script_hasdata(st,4) )
 		sd = map_id2sd(script_getnum(st,4)); // <Account ID>
 	else
@@ -6554,7 +6554,17 @@ BUILDIN_FUNC(getitem)
 }
 
 /*==========================================
+ * getitem2 <item id>,<amount>,<identify>,<refine>,<attribute>,<card1>,<card2>,<card3>,<card4>{,<account ID>};
+ * getitem2 "<item name>",<amount>,<identify>,<refine>,<attribute>,<card1>,<card2>,<card3>,<card4>{,<account ID>};
  *
+ * getitembound2 <item id>,<amount>,<identify>,<refine>,<attribute>,<card1>,<card2>,<card3>,<card4>,<bound type>{,<account ID>};
+ * getitembound2 "<item name>",<amount>,<identify>,<refine>,<attribute>,<card1>,<card2>,<card3>,<card4>,<bound type>{,<account ID>};
+ * Type:
+ *	0 - No bound
+ *	1 - Account Bound
+ *	2 - Guild Bound
+ *	3 - Party Bound
+ *	4 - Character Bound
  *------------------------------------------*/
 BUILDIN_FUNC(getitem2)
 {
@@ -6572,16 +6582,14 @@ BUILDIN_FUNC(getitem2)
 
 	if( !strcmp(command,"getitembound2") ) {
 		bound = script_getnum(st,11);
-		if( bound > BOUND_NONE && bound < BOUND_MAX ) {
-			if( script_hasdata(st,12) )
-				sd = map_id2sd(script_getnum(st,12));
-			else
-				sd = script_rid2sd(st); // Attached player
-		}
-		else {
+		if( bound < BOUND_NONE || bound >= BOUND_MAX ) {
 			ShowError("script_getitembound2: Not a correct bound type! Type=%d\n",bound);
 			return SCRIPT_CMD_FAILURE;
 		}
+		if( script_hasdata(st,12) )
+			sd = map_id2sd(script_getnum(st,12));
+		else
+			sd = script_rid2sd(st); // Attached player
 	} else if( script_hasdata(st,11) )
 		sd = map_id2sd(script_getnum(st,11)); // <Account ID>
 	else
@@ -13137,8 +13145,7 @@ BUILDIN_FUNC(skilleffect)
 
 	/* Ensure we're standing because the following packet causes the client to virtually set the char to stand,
 	 * which leaves the server thinking it still is sitting. */
-	if( pc_issit(sd) ) {
-		pc_setstand(sd);
+	if( pc_issit(sd) && pc_setstand(sd, false) ) {
 		skill_sit(sd, 0);
 		clif_standing(&sd->bl);
 	}
@@ -14006,7 +14013,7 @@ BUILDIN_FUNC(isday)
 BUILDIN_FUNC(isequippedcnt)
 {
 	TBL_PC *sd;
-	int i, j, k, id = 1;
+	int i, id = 1;
 	int ret = 0;
 
 	sd = script_rid2sd(st);
@@ -14016,13 +14023,13 @@ BUILDIN_FUNC(isequippedcnt)
 	}
 
 	for (i=0; id!=0; i++) {
+		short j;
 		FETCH (i+2, id) else id = 0;
 		if (id <= 0)
 			continue;
 
 		for (j=0; j<EQI_MAX; j++) {
-			int index;
-			index = sd->equip_index[j];
+			short index = sd->equip_index[j];
 			if(index < 0)
 				continue;
 			if (pc_is_same_equip_index((enum equip_index)j, sd->equip_index, index))
@@ -14035,6 +14042,7 @@ BUILDIN_FUNC(isequippedcnt)
 				if (sd->inventory_data[index]->nameid == id)
 					ret+= sd->status.inventory[index].amount;
 			} else { //Count cards.
+				short k;
 				if (itemdb_isspecial(sd->status.inventory[index].card[0]))
 					continue; //No cards
 				for(k=0; k<sd->inventory_data[index]->slot; k++) {
@@ -14058,8 +14066,7 @@ BUILDIN_FUNC(isequippedcnt)
 BUILDIN_FUNC(isequipped)
 {
 	TBL_PC *sd;
-	int i, j, k, id = 1;
-	int index, flag;
+	int i, id = 1;
 	int ret = -1;
 	//Original hash to reverse it when full check fails.
 	unsigned int setitem_hash = 0, setitem_hash2 = 0;
@@ -14068,18 +14075,19 @@ BUILDIN_FUNC(isequipped)
 
 	if (!sd) { //If the player is not attached it is a script error anyway... but better prevent the map server from crashing...
 		script_pushint(st,0);
-		return 0;
+		return SCRIPT_CMD_SUCCESS;
 	}
 
 	setitem_hash = sd->bonus.setitem_hash;
 	setitem_hash2 = sd->bonus.setitem_hash2;
 	for (i=0; id!=0; i++) {
+		int flag = 0;
+		short j;
 		FETCH (i+2, id) else id = 0;
 		if (id <= 0)
 			continue;
-		flag = 0;
 		for (j=0; j<EQI_MAX; j++) {
-			index = sd->equip_index[j];
+			short index = sd->equip_index[j];
 			if(index < 0)
 				continue;
 			if (pc_is_same_equip_index((enum equip_index)i, sd->equip_index, index))
@@ -14094,6 +14102,7 @@ BUILDIN_FUNC(isequipped)
 				flag = 1;
 				break;
 			} else { //Cards
+				short k;
 				if (sd->inventory_data[index]->slot == 0 ||
 					itemdb_isspecial(sd->status.inventory[index].card[0]))
 					continue;
@@ -18288,8 +18297,7 @@ BUILDIN_FUNC(stand)
 	if( sd == NULL)
 		return SCRIPT_CMD_FAILURE;
 
-	if( pc_issit(sd) ) {
-		pc_setstand(sd);
+	if( pc_issit(sd) && pc_setstand(sd, false)) {
 		skill_sit(sd, 0);
 		clif_standing(&sd->bl);
 	}

+ 1 - 2
src/map/searchstore.c

@@ -114,7 +114,6 @@ void searchstore_query(struct map_session_data* sd, unsigned char type, unsigned
 	struct s_search_store_search s;
 	searchstore_searchall_t store_searchall;
 	time_t querytime;
-	DBMap *vending_db = vending_getdb();
 
 	if( !battle_config.feature_search_stores ) {
 		return;
@@ -179,7 +178,7 @@ void searchstore_query(struct map_session_data* sd, unsigned char type, unsigned
 	s.card_count = card_count;
 	s.min_price  = min_price;
 	s.max_price  = max_price;
-	iter         = db_iterator(vending_db);
+	iter         = db_iterator((type == SEARCHTYPE_VENDING) ? vending_getdb() : buyingstore_getdb());
 
 	for( pl_sd = dbi_first(iter); dbi_exists(iter);  pl_sd = dbi_next(iter) ) {
 		if( sd == pl_sd ) {// skip own shop, if any

+ 265 - 226
src/map/skill.c

@@ -2081,7 +2081,7 @@ int skill_counter_additional_effect (struct block_list* src, struct block_list *
 	 	!(skill_get_inf(skill_id)&(INF_GROUND_SKILL|INF_SELF_SKILL)) &&
 		(rate=pc_checkskill(sd,HW_SOULDRAIN))>0
 	){	//Soul Drain should only work on targetted spells [Skotlex]
-		if (pc_issit(sd)) pc_setstand(sd); //Character stuck in attacking animation while 'sitting' fix. [Skotlex]
+		if (pc_issit(sd)) pc_setstand(sd, true); //Character stuck in attacking animation while 'sitting' fix. [Skotlex]
 		clif_skill_nodamage(src,bl,HW_SOULDRAIN,rate,1);
 		status_heal(src, 0, status_get_lv(bl)*(95+15*rate)/100, 2);
 	}
@@ -2277,7 +2277,7 @@ int skill_break_equip (struct block_list *src,struct block_list *bl, unsigned sh
 		return 0;
 	if (sd) {
 		for (i = 0; i < EQI_MAX; i++) {
-			int j = sd->equip_index[i];
+			short j = sd->equip_index[i];
 			if (j < 0 || sd->status.inventory[j].attribute == 1 || !sd->inventory_data[j])
 				continue;
 
@@ -2447,6 +2447,36 @@ static int skill_magic_reflect(struct block_list* src, struct block_list* bl, in
 	return 0;
 }
 
+/**
+ * Checks whether a skill can be used in combos or not
+ * @param skill_id: Target skill
+ * @return true: Skill is a combo, false: otherwise
+ * @author Panikon
+ **/
+bool skill_is_combo(uint16 skill_id) {
+	switch(skill_id) {
+		case MO_CHAINCOMBO:
+		case MO_COMBOFINISH:
+		case CH_TIGERFIST:
+		case CH_CHAINCRUSH:
+		case MO_EXTREMITYFIST:
+		case TK_TURNKICK:
+		case TK_STORMKICK:
+		case TK_DOWNKICK:
+		case TK_COUNTER:
+		case TK_JUMPKICK:
+		case HT_POWER:
+		case GC_COUNTERSLASH:
+		case GC_WEAPONCRUSH:
+		case SR_FALLENEMPIRE:
+		case SR_DRAGONCOMBO:
+		case SR_TIGERCANNON:
+		case SR_GATEOFHELL:
+			return true;
+	}
+	return false;
+}
+
 /*
  * Combo handler, start stop combo status
  */
@@ -2777,7 +2807,8 @@ int64 skill_attack (int attack_type, struct block_list* src, struct block_list *
 			{	//Consume one Fragment per hit of the casted skill? [Skotlex]
 				type = tsd?pc_search_inventory (tsd, ITEMID_FRAGMENT_OF_CRYSTAL):0;
 				if (type >= 0) {
-					if ( tsd ) pc_delitem(tsd, type, 1, 0, 1, LOG_TYPE_CONSUME);
+					if ( tsd )
+						pc_delitem(tsd, type, 1, 0, 1, LOG_TYPE_CONSUME);
 					dmg.damage = dmg.damage2 = 0;
 					dmg.dmg_lv = ATK_MISS;
 					tsc->data[SC_SPIRIT]->val3 = skill_id;
@@ -2789,7 +2820,7 @@ int64 skill_attack (int attack_type, struct block_list* src, struct block_list *
 		/**
 		 * Official Magic Reflection Behavior : damage reflected depends on gears caster wears, not target
 		 **/
-		#if MAGIC_REFLECTION_TYPE
+#if MAGIC_REFLECTION_TYPE
 #ifdef RENEWAL
 			if( dmg.dmg_lv != ATK_MISS ) { //Wiz SL cancelled and consumed fragment
 #else
@@ -2819,8 +2850,9 @@ int64 skill_attack (int attack_type, struct block_list* src, struct block_list *
 					dmg.damage -= dmg.damage * (6 * (1+per)) / 100;
 				}
 			}
-		#endif
+#endif
 		}
+
 		if(tsc && tsc->data[SC_MAGICROD] && src == dsrc) {
 			int sp = skill_get_sp(skill_id,skill_lv);
 			dmg.damage = dmg.damage2 = 0;
@@ -2853,47 +2885,51 @@ int64 skill_attack (int attack_type, struct block_list* src, struct block_list *
 	}
 
 	//Skill hit type
-	type=(skill_id==0)?5:skill_get_hit(skill_id);
+	type = (skill_id == 0) ? 5 : skill_get_hit(skill_id);
 
-	switch(skill_id){
-	case SC_TRIANGLESHOT:
-		if(rnd()%100 > (1 + skill_lv) ) dmg.blewcount = 0;
-		break;
-	default:
-		if(damage < dmg.div_ && skill_id != CH_PALMSTRIKE)
-			dmg.blewcount = 0; //only pushback when it hit for other
-		break;
+	switch( skill_id ) {
+		case SC_TRIANGLESHOT:
+			if( rnd()%100 > (1 + skill_lv) )
+				dmg.blewcount = 0;
+			break;
+		default:
+			if (damage < dmg.div_ && skill_id != CH_PALMSTRIKE)
+				dmg.blewcount = 0; //only pushback when it hit for other
+			break;
 	}
 
-	switch(skill_id){
-	case CR_GRANDCROSS:
-	case NPC_GRANDDARKNESS:
-		if(battle_config.gx_disptype) dsrc = src;
-		if(src == bl) type = 4;
-		else flag|=SD_ANIMATION;
-		break;
-	case NJ_TATAMIGAESHI: //For correct knockback.
-		dsrc = src;
-		flag|=SD_ANIMATION;
-		break;
-	case TK_COUNTER: {	//bonus from SG_FRIEND [Komurka]
-		int level;
-		if(sd->status.party_id>0 && (level = pc_checkskill(sd,SG_FRIEND)))
-			party_skill_check(sd, sd->status.party_id, TK_COUNTER,level);
-		}
-		break;
-	case SL_STIN:
-	case SL_STUN:
-		if (skill_lv >= 7){
-			struct status_change *sc = status_get_sc(src);
-			if(sc && !sc->data[SC_SMA])
-				sc_start(src,src,SC_SMA,100,skill_lv,skill_get_time(SL_SMA, skill_lv));
-		}
-		break;
-	case GS_FULLBUSTER:
-		if(sd) //Can't attack nor use items until skill's delay expires. [Skotlex]
-			sd->ud.attackabletime = sd->canuseitem_tick = sd->ud.canact_tick;
-		break;
+	switch( skill_id ) {
+		case CR_GRANDCROSS:
+		case NPC_GRANDDARKNESS:
+			if( battle_config.gx_disptype)
+				dsrc = src;
+			if( src == bl)
+				type = 4;
+			else
+				flag|= SD_ANIMATION;
+			break;
+		case NJ_TATAMIGAESHI: //For correct knockback.
+			dsrc = src;
+			flag|= SD_ANIMATION;
+			break;
+		case TK_COUNTER: {	//bonus from SG_FRIEND [Komurka]
+			int level;
+			if( sd->status.party_id>0 && (level = pc_checkskill(sd,SG_FRIEND)) )
+				party_skill_check(sd, sd->status.party_id, TK_COUNTER,level);
+			}
+			break;
+		case SL_STIN:
+		case SL_STUN:
+			if (skill_lv >= 7) {
+				struct status_change *sc = status_get_sc(src);
+				if (sc && !sc->data[SC_SMA])
+					sc_start(src,src,SC_SMA,100,skill_lv,skill_get_time(SL_SMA, skill_lv));
+			}
+			break;
+		case GS_FULLBUSTER:
+			if (sd) //Can't attack nor use items until skill's delay expires. [Skotlex]
+				sd->ud.attackabletime = sd->canuseitem_tick = sd->ud.canact_tick;
+			break;
 	}
 
 	//combo handling
@@ -2901,114 +2937,114 @@ int64 skill_attack (int attack_type, struct block_list* src, struct block_list *
 
 	//Display damage.
 	switch( skill_id ) {
-	case PA_GOSPEL: //Should look like Holy Cross [Skotlex]
-		dmg.dmotion = clif_skill_damage(dsrc,bl,tick,dmg.amotion,dmg.dmotion, damage, dmg.div_, CR_HOLYCROSS, -1, 5);
-		break;
-	//Skills that need be passed as a normal attack for the client to display correctly.
-	case HVAN_EXPLOSION:
-	case NPC_SELFDESTRUCTION:
-		if(src->type==BL_PC)
-			dmg.blewcount = 10;
-		dmg.amotion = 0; //Disable delay or attack will do no damage since source is dead by the time it takes effect. [Skotlex]
-		// fall through
-	case KN_AUTOCOUNTER:
-	case NPC_CRITICALSLASH:
-	case TF_DOUBLE:
-	case GS_CHAINACTION:
-		dmg.dmotion = clif_damage(src,bl,tick,dmg.amotion,dmg.dmotion,damage,dmg.div_,dmg.type,dmg.damage2);
-		break;
-
-	case AS_SPLASHER:
-		if( flag&SD_ANIMATION ) // the surrounding targets
-			dmg.dmotion = clif_skill_damage(dsrc,bl,tick, dmg.amotion, dmg.dmotion, damage, dmg.div_, skill_id, -1, 5); // needs -1 as skill level
-		else // the central target doesn't display an animation
-			dmg.dmotion = clif_skill_damage(dsrc,bl,tick, dmg.amotion, dmg.dmotion, damage, dmg.div_, skill_id, -2, 5); // needs -2(!) as skill level
-		break;
-	case WL_HELLINFERNO:
-	case SR_EARTHSHAKER:
-		dmg.dmotion = clif_skill_damage(src,bl,tick,dmg.amotion,dmg.dmotion,damage,1,skill_id,-2,6);
-		break;
-	case WL_SOULEXPANSION:
-	case WL_COMET:
-	case KO_MUCHANAGE:
-	case NJ_HUUMA:
-		dmg.dmotion = clif_skill_damage(src,bl,tick,dmg.amotion,dmg.dmotion,damage,dmg.div_,skill_id,skill_lv,8);
-		break;
-	case WL_CHAINLIGHTNING_ATK:
-		dmg.dmotion = clif_skill_damage(src,bl,tick,dmg.amotion,dmg.dmotion,damage,1,WL_CHAINLIGHTNING,-2,6);
-		break;
-	case LG_OVERBRAND:
-	case LG_OVERBRAND_BRANDISH:
-		dmg.amotion = status_get_amotion(src) * 2;
-	case LG_OVERBRAND_PLUSATK:
-		dmg.dmotion = clif_skill_damage(dsrc,bl,tick,status_get_amotion(src),dmg.dmotion,damage,dmg.div_,skill_id,-1,5);
-		break;
-	case EL_FIRE_BOMB:
-	case EL_FIRE_BOMB_ATK:
-	case EL_FIRE_WAVE:
-	case EL_FIRE_WAVE_ATK:
-	case EL_FIRE_MANTLE:
-	case EL_CIRCLE_OF_FIRE:
-	case EL_FIRE_ARROW:
-	case EL_ICE_NEEDLE:
-	case EL_WATER_SCREW:
-	case EL_WATER_SCREW_ATK:
-	case EL_WIND_SLASH:
-	case EL_TIDAL_WEAPON:
-	case EL_ROCK_CRUSHER:
-	case EL_ROCK_CRUSHER_ATK:
-	case EL_HURRICANE:
-	case EL_HURRICANE_ATK:
-	case KO_BAKURETSU:
-	case GN_CRAZYWEED_ATK:
-	case NC_MAGMA_ERUPTION:
-		dmg.dmotion = clif_skill_damage(src,bl,tick,dmg.amotion,dmg.dmotion,damage,dmg.div_,skill_id,-1,5);
-		break;
-	case GN_FIRE_EXPANSION_ACID:
-		dmg.dmotion = clif_skill_damage(dsrc, bl, tick, dmg.amotion, dmg.dmotion, damage, dmg.div_, CR_ACIDDEMONSTRATION, skill_lv, 8);
-		break;
-	case GN_SLINGITEM_RANGEMELEEATK:
-		dmg.dmotion = clif_skill_damage(src,bl,tick,dmg.amotion,dmg.dmotion,damage,dmg.div_,GN_SLINGITEM,-2,6);
-		break;
-	case EL_STONE_RAIN:
-		dmg.dmotion = clif_skill_damage(dsrc,bl,tick,dmg.amotion,dmg.dmotion,damage,dmg.div_,skill_id,-1,(flag&1)?8:5);
-		break;
-	case WM_SEVERE_RAINSTORM_MELEE:
-		dmg.dmotion = clif_skill_damage(src,bl,tick,dmg.amotion,dmg.dmotion,damage,dmg.div_,WM_SEVERE_RAINSTORM,-2,6);
-		break;
-	case WM_REVERBERATION_MELEE:
-	case WM_REVERBERATION_MAGIC:
-		dmg.dmotion = clif_skill_damage(src,bl,tick,dmg.amotion,dmg.dmotion,damage,dmg.div_,WM_REVERBERATION,-2,6);
-		break;
-	case HT_CLAYMORETRAP:
-	case HT_BLASTMINE:
-	case HT_FLASHER:
-	case HT_FREEZINGTRAP:
-	case RA_CLUSTERBOMB:
-	case RA_FIRINGTRAP:
-	case RA_ICEBOUNDTRAP:
-		dmg.dmotion = clif_skill_damage(src,bl,tick, dmg.amotion, dmg.dmotion, damage, dmg.div_, skill_id,flag&SD_LEVEL?-1:skill_lv, 5);
-		if( dsrc != src ) // avoid damage display redundancy
+		case PA_GOSPEL: //Should look like Holy Cross [Skotlex]
+			dmg.dmotion = clif_skill_damage(dsrc,bl,tick,dmg.amotion,dmg.dmotion, damage, dmg.div_, CR_HOLYCROSS, -1, 5);
+			break;
+		//Skills that need be passed as a normal attack for the client to display correctly.
+		case HVAN_EXPLOSION:
+		case NPC_SELFDESTRUCTION:
+			if(src->type == BL_PC)
+				dmg.blewcount = 10;
+			dmg.amotion = 0; //Disable delay or attack will do no damage since source is dead by the time it takes effect. [Skotlex]
+			// fall through
+		case KN_AUTOCOUNTER:
+		case NPC_CRITICALSLASH:
+		case TF_DOUBLE:
+		case GS_CHAINACTION:
+			dmg.dmotion = clif_damage(src,bl,tick,dmg.amotion,dmg.dmotion,damage,dmg.div_,(enum e_damage_type)dmg.type,dmg.damage2);
+			break;
+
+		case AS_SPLASHER:
+			if( flag&SD_ANIMATION ) // the surrounding targets
+				dmg.dmotion = clif_skill_damage(dsrc,bl,tick, dmg.amotion, dmg.dmotion, damage, dmg.div_, skill_id, -1, 5); // needs -1 as skill level
+			else // the central target doesn't display an animation
+				dmg.dmotion = clif_skill_damage(dsrc,bl,tick, dmg.amotion, dmg.dmotion, damage, dmg.div_, skill_id, -2, 5); // needs -2(!) as skill level
+			break;
+		case WL_HELLINFERNO:
+		case SR_EARTHSHAKER:
+			dmg.dmotion = clif_skill_damage(src,bl,tick,dmg.amotion,dmg.dmotion,damage,1,skill_id,-2,6);
+			break;
+		case WL_SOULEXPANSION:
+		case WL_COMET:
+		case KO_MUCHANAGE:
+		case NJ_HUUMA:
+			dmg.dmotion = clif_skill_damage(src,bl,tick,dmg.amotion,dmg.dmotion,damage,dmg.div_,skill_id,skill_lv,8);
+			break;
+		case WL_CHAINLIGHTNING_ATK:
+			dmg.dmotion = clif_skill_damage(src,bl,tick,dmg.amotion,dmg.dmotion,damage,1,WL_CHAINLIGHTNING,-2,6);
+			break;
+		case LG_OVERBRAND:
+		case LG_OVERBRAND_BRANDISH:
+			dmg.amotion = status_get_amotion(src) * 2;
+		case LG_OVERBRAND_PLUSATK:
+			dmg.dmotion = clif_skill_damage(dsrc,bl,tick,status_get_amotion(src),dmg.dmotion,damage,dmg.div_,skill_id,-1,5);
+			break;
+		case EL_FIRE_BOMB:
+		case EL_FIRE_BOMB_ATK:
+		case EL_FIRE_WAVE:
+		case EL_FIRE_WAVE_ATK:
+		case EL_FIRE_MANTLE:
+		case EL_CIRCLE_OF_FIRE:
+		case EL_FIRE_ARROW:
+		case EL_ICE_NEEDLE:
+		case EL_WATER_SCREW:
+		case EL_WATER_SCREW_ATK:
+		case EL_WIND_SLASH:
+		case EL_TIDAL_WEAPON:
+		case EL_ROCK_CRUSHER:
+		case EL_ROCK_CRUSHER_ATK:
+		case EL_HURRICANE:
+		case EL_HURRICANE_ATK:
+		case KO_BAKURETSU:
+		case GN_CRAZYWEED_ATK:
+		case NC_MAGMA_ERUPTION:
+			dmg.dmotion = clif_skill_damage(src,bl,tick,dmg.amotion,dmg.dmotion,damage,dmg.div_,skill_id,-1,5);
+			break;
+		case GN_FIRE_EXPANSION_ACID:
+			dmg.dmotion = clif_skill_damage(dsrc, bl, tick, dmg.amotion, dmg.dmotion, damage, dmg.div_, CR_ACIDDEMONSTRATION, skill_lv, 8);
+			break;
+		case GN_SLINGITEM_RANGEMELEEATK:
+			dmg.dmotion = clif_skill_damage(src,bl,tick,dmg.amotion,dmg.dmotion,damage,dmg.div_,GN_SLINGITEM,-2,6);
+			break;
+		case EL_STONE_RAIN:
+			dmg.dmotion = clif_skill_damage(dsrc,bl,tick,dmg.amotion,dmg.dmotion,damage,dmg.div_,skill_id,-1,(flag&1)?8:5);
+			break;
+		case WM_SEVERE_RAINSTORM_MELEE:
+			dmg.dmotion = clif_skill_damage(src,bl,tick,dmg.amotion,dmg.dmotion,damage,dmg.div_,WM_SEVERE_RAINSTORM,-2,6);
+			break;
+		case WM_REVERBERATION_MELEE:
+		case WM_REVERBERATION_MAGIC:
+			dmg.dmotion = clif_skill_damage(src,bl,tick,dmg.amotion,dmg.dmotion,damage,dmg.div_,WM_REVERBERATION,-2,6);
+			break;
+		case HT_CLAYMORETRAP:
+		case HT_BLASTMINE:
+		case HT_FLASHER:
+		case HT_FREEZINGTRAP:
+		case RA_CLUSTERBOMB:
+		case RA_FIRINGTRAP:
+		case RA_ICEBOUNDTRAP:
+			dmg.dmotion = clif_skill_damage(src,bl,tick, dmg.amotion, dmg.dmotion, damage, dmg.div_, skill_id,flag&SD_LEVEL?-1:skill_lv, 5);
+			if( dsrc != src ) // avoid damage display redundancy
+				break;
+		case HT_LANDMINE:
+			dmg.dmotion = clif_skill_damage(dsrc,bl,tick, dmg.amotion, dmg.dmotion, damage, dmg.div_, skill_id, -1, type);
+			break;
+		case WZ_SIGHTBLASTER:
+			dmg.dmotion = clif_skill_damage(src,bl,tick, dmg.amotion, dmg.dmotion, damage, dmg.div_, skill_id, flag&SD_LEVEL?-1:skill_lv, 5);
+			break;
+		case AB_DUPLELIGHT_MELEE:
+		case AB_DUPLELIGHT_MAGIC:
+			dmg.amotion = 300;/* makes the damage value not overlap with previous damage (when displayed by the client) */
+		default:
+			if( flag&SD_ANIMATION && dmg.div_ < 2 ) //Disabling skill animation doesn't works on multi-hit.
+				type = 5;
+			if( bl->type == BL_SKILL ){
+				TBL_SKILL *su = (TBL_SKILL*)bl;
+				if( su->group && skill_get_inf2(su->group->skill_id)&INF2_TRAP )// show damage on trap targets
+					clif_skill_damage(src,bl,tick, dmg.amotion, dmg.dmotion, damage, dmg.div_, skill_id, flag&SD_LEVEL?-1:skill_lv, 5);
+			}
+			dmg.dmotion = clif_skill_damage(dsrc,bl,tick, dmg.amotion, dmg.dmotion, damage, dmg.div_, skill_id, flag&SD_LEVEL?-1:skill_lv, type);
 			break;
-	case HT_LANDMINE:
-		dmg.dmotion = clif_skill_damage(dsrc,bl,tick, dmg.amotion, dmg.dmotion, damage, dmg.div_, skill_id, -1, type);
-		break;
-	case WZ_SIGHTBLASTER:
-		dmg.dmotion = clif_skill_damage(src,bl,tick, dmg.amotion, dmg.dmotion, damage, dmg.div_, skill_id, flag&SD_LEVEL?-1:skill_lv, 5);
-		break;
-	case AB_DUPLELIGHT_MELEE:
-	case AB_DUPLELIGHT_MAGIC:
-		dmg.amotion = 300;/* makes the damage value not overlap with previous damage (when displayed by the client) */
-	default:
-		if( flag&SD_ANIMATION && dmg.div_ < 2 ) //Disabling skill animation doesn't works on multi-hit.
-			type = 5;
-		if( bl->type == BL_SKILL ){
-			TBL_SKILL *su = (TBL_SKILL*)bl;
-			if( su->group && skill_get_inf2(su->group->skill_id)&INF2_TRAP )// show damage on trap targets
-				clif_skill_damage(src,bl,tick, dmg.amotion, dmg.dmotion, damage, dmg.div_, skill_id, flag&SD_LEVEL?-1:skill_lv, 5);
-		}
-		dmg.dmotion = clif_skill_damage(dsrc,bl,tick, dmg.amotion, dmg.dmotion, damage, dmg.div_, skill_id, flag&SD_LEVEL?-1:skill_lv, type);
-		break;
 	}
 
 	map_freeblock_lock();
@@ -3112,13 +3148,17 @@ int64 skill_attack (int attack_type, struct block_list* src, struct block_list *
 			) && check_distance_bl(bl, d_bl, sce->val3) )
 		{
 			if(!rmdamage){
-				clif_damage(d_bl,d_bl, gettick(), 0, 0, damage, 0, 0, 0);
+				clif_damage(d_bl,d_bl, gettick(), 0, 0, damage, 0, DMG_NORMAL, 0);
 				status_fix_damage(NULL,d_bl, damage, 0);
-			} else {//Reflected magics are done directly on the target not on paladin
-				//This check is only for magical skill.
-				//For BF_WEAPON skills types track var rdamage and function battle_calc_return_damage
-				clif_damage(bl,bl, gettick(), 0, 0, damage, 0, 0, 0);
-				status_fix_damage(bl,bl, damage, 0);
+			} else {
+				bool isDevotRdamage = false;
+				if (battle_config.devotion_rdamage && battle_config.devotion_rdamage > rand()%100)
+					isDevotRdamage = true;
+				// If !isDevotRdamage, reflected magics are done directly on the target not on paladin
+				// This check is only for magical skill.
+				// For BF_WEAPON skills types track var rdamage and function battle_calc_return_damage
+				clif_damage(bl,(!isDevotRdamage) ? bl : d_bl, gettick(), 0, 0, damage, 0, DMG_NORMAL, 0);
+				status_fix_damage(bl,(!isDevotRdamage) ? bl : d_bl, damage, 0);
 			}
 		}
 		else {
@@ -4497,7 +4537,7 @@ int skill_castend_damage_id (struct block_list* src, struct block_list *bl, uint
 		break;
 	case CH_PALMSTRIKE: //	Palm Strike takes effect 1sec after casting. [Skotlex]
 	//	clif_skill_nodamage(src,bl,skill_id,skill_lv,0); //Can't make this one display the correct attack animation delay :/
-		clif_damage(src,bl,tick,status_get_amotion(src),0,-1,1,4,0); //Display an absorbed damage attack.
+		clif_damage(src,bl,tick,status_get_amotion(src),0,-1,1,DMG_ENDURE,0); //Display an absorbed damage attack.
 		skill_addtimerskill(src, tick + (1000+status_get_amotion(src)), bl->id, 0, 0, skill_id, skill_lv, BF_WEAPON, flag);
 		break;
 
@@ -7890,7 +7930,7 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, ui
 				case 3:	// 1000 damage, random armor destroyed
 					{
 						status_fix_damage(src, bl, 1000, 0);
-						clif_damage(src,bl,tick,0,0,1000,0,0,0);
+						clif_damage(src,bl,tick,0,0,1000,0,DMG_NORMAL,0);
 						if( !status_isdead(bl) ) {
 							int where[] = { EQP_ARMOR, EQP_SHIELD, EQP_HELM, EQP_SHOES, EQP_GARMENT };
 							skill_break_equip(src,bl, where[rnd()%5], 10000, BCT_ENEMY);
@@ -7927,14 +7967,14 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, ui
 					break;
 				case 10:	// 6666 damage, atk matk halved, cursed
 					status_fix_damage(src, bl, 6666, 0);
-					clif_damage(src,bl,tick,0,0,6666,0,0,0);
+					clif_damage(src,bl,tick,0,0,6666,0,DMG_NORMAL,0);
 					sc_start(src,bl,SC_INCATKRATE,100,-50,skill_get_time2(skill_id,skill_lv));
 					sc_start(src,bl,SC_INCMATKRATE,100,-50,skill_get_time2(skill_id,skill_lv));
 					sc_start(src,bl,SC_CURSE,skill_lv,100,skill_get_time2(skill_id,skill_lv));
 					break;
 				case 11:	// 4444 damage
 					status_fix_damage(src, bl, 4444, 0);
-					clif_damage(src,bl,tick,0,0,4444,0,0,0);
+					clif_damage(src,bl,tick,0,0,4444,0,DMG_NORMAL,0);
 					break;
 				case 12:	// stun
 					sc_start(src,bl,SC_STUN,100,skill_lv,5000);
@@ -8087,8 +8127,8 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, ui
 						continue;
 					if(map_getcell(src->m,src->x+dx[j],src->y+dy[j],CELL_CHKNOREACH))
 						dx[j] = dy[j] = 0;
-					pc_setpos(dstsd, map_id2index(src->m), src->x+dx[j], src->y+dy[j], CLR_RESPAWN);
-					called++;
+					if (!pc_setpos(dstsd, map_id2index(src->m), src->x+dx[j], src->y+dy[j], CLR_RESPAWN))
+						called++;
 				}
 			}
 			if (sd)
@@ -8290,7 +8330,7 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, ui
 	case ALL_BUYING_STORE:
 		if( sd )
 		{// players only, skill allows 5 buying slots
-			clif_skill_nodamage(src, bl, skill_id, skill_lv, buyingstore_setup(sd, MAX_BUYINGSTORE_SLOTS));
+			clif_skill_nodamage(src, bl, skill_id, skill_lv, buyingstore_setup(sd, MAX_BUYINGSTORE_SLOTS) ? 0 : 1);
 		}
 		break;
 	case RK_ENCHANTBLADE:
@@ -8348,7 +8388,7 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, ui
 			int8 rate = rnd()%100;
 			int16 shields = ((rate < 20) ? 4 : (rate < 50) ? 3 : 2);
 			sc_start4(src,bl,type,100,skill_lv,shields,1000,0,skill_get_time(skill_id,skill_lv));
-			clif_millenniumshield(sd,shields);
+			clif_millenniumshield(bl,shields);
 			clif_skill_nodamage(src,bl,skill_id,1,1);
 		}
 		break;
@@ -8397,7 +8437,7 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, ui
 				if( skill_area_temp[5]&0x10 ) {
 					if( dstsd ) {
 						i = (rnd()%100 < 50) ? 4 : ((rnd()%100 < 80) ? 3 : 2);
-						clif_millenniumshield(dstsd,i);
+						clif_millenniumshield(bl,i);
 						skill_area_temp[5] &= ~0x10;
 						type = SC_MILLENNIUMSHIELD;
 					}
@@ -8843,32 +8883,33 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, ui
 	case WL_SUMMONWB:
 	case WL_SUMMONSTONE:
 		{
-			short element = 0, sctype = 0, pos = -1;
+			short element = 0, sctype = 0, pos = -1, j = 0;
 			struct status_change *sc = status_get_sc(src);
-			if( !sc ) break;
 
-			for( i = SC_SPHERE_1; i <= SC_SPHERE_5; i++ )
-			{
+			if( !sc )
+				break;
+
+			for( i = SC_SPHERE_1; i <= SC_SPHERE_5; i++ ) {
 				if( !sctype && !sc->data[i] )
 					sctype = i; // Take the free SC
-				if( sc->data[i] )
+				if( sc->data[i] ) {
 					pos = max(sc->data[i]->val2,pos);
+					j++;
+				}
 			}
 
-			if( !sctype )
-			{
+			if( !sctype || j >= skill_lv ) {
 				if( sd ) // No free slots to put SC
 					clif_skill_fail(sd,skill_id,USESKILL_FAIL_SUMMON,0);
 				break;
 			}
 
 			pos++; // Used in val2 for SC. Indicates the order of this ball
-			switch( skill_id )
-			{ // Set val1. The SC element for this ball
-			case WL_SUMMONFB:    element = WLS_FIRE;  break;
-			case WL_SUMMONBL:    element = WLS_WIND;  break;
-			case WL_SUMMONWB:    element = WLS_WATER; break;
-			case WL_SUMMONSTONE: element = WLS_STONE; break;
+			switch( skill_id ) { // Set val1. The SC element for this ball
+				case WL_SUMMONFB:    element = WLS_FIRE;  break;
+				case WL_SUMMONBL:    element = WLS_WIND;  break;
+				case WL_SUMMONWB:    element = WLS_WATER; break;
+				case WL_SUMMONSTONE: element = WLS_STONE; break;
 			}
 
 			sc_start4(src,src,(enum sc_type)sctype,100,element,pos,skill_lv,0,skill_get_time(skill_id,skill_lv));
@@ -9110,11 +9151,9 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, ui
 		else if( sd ) {
 			int opt = rnd()%3 + 1;
 			int val = 0, splash = 0;
-			int index = sd->equip_index[EQI_HAND_L];
+			short index = sd->equip_index[EQI_HAND_L];
 			struct item_data *shield_data = NULL;
-			if( index >= 0 && sd->inventory_data[index] && sd->inventory_data[index]->type == IT_ARMOR )
-				shield_data = sd->inventory_data[index];
-			if( !shield_data || shield_data->type != IT_ARMOR ) {	// No shield?
+			if( index < 0 || !(shield_data = sd->inventory_data[index]) || shield_data->type == IT_ARMOR ) {	// No shield?
 				clif_skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0);
 				break;
 			}
@@ -9184,8 +9223,8 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, ui
 
 				case 3: // refine based
 					{
-						struct item *shield = &sd->status.inventory[sd->equip_index[EQI_HAND_L]];
-						if( !shield || !shield->refine ) {
+						struct item *shield = NULL;
+						if( sd->equip_index[EQI_HAND_L] < 0 || !(shield = &sd->status.inventory[sd->equip_index[EQI_HAND_L]]) || !shield->refine ) {
 							clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
 							break;
 						}
@@ -9401,7 +9440,7 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, ui
 				sc_start(src,bl, type, 100, skill_lv,skill_get_time(skill_id, skill_lv));
 		} else if( flag&2 ) {
 			if( src->id != bl->id && battle_check_target(src,bl,BCT_ENEMY) > 0 )
-				status_fix_damage(src,bl,9999,clif_damage(src,bl,tick,0,0,9999,0,0,0));
+				status_fix_damage(src,bl,9999,clif_damage(src,bl,tick,0,0,9999,0,DMG_NORMAL,0));
 		} else if( sd ) {
 			short chance = sstatus->int_/6 + sd->status.job_level/5 + skill_lv*4;
 			if( !sd->status.party_id || (rnd()%100 > chance)) {
@@ -9417,7 +9456,7 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, ui
 			clif_skill_nodamage(src, bl, skill_id, skill_lv,
 				sc_start(src,src,SC_STOP,100,skill_lv,skill_get_time2(skill_id,skill_lv)));
 			if( flag&2 ) // Dealed here to prevent conflicts
-				status_fix_damage(src,bl,9999,clif_damage(src,bl,tick,0,0,9999,0,0,0));
+				status_fix_damage(src,bl,9999,clif_damage(src,bl,tick,0,0,9999,0,DMG_NORMAL,0));
 		}
 		break;
 	case WM_SONG_OF_MANA:
@@ -9722,7 +9761,7 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, ui
 		if( sd ) {
 			short ammo_id;
 			i = sd->equip_index[EQI_AMMO];
-			if( i <= 0 )
+			if( i < 0 )
 				break; // No ammo.
 			ammo_id = sd->inventory_data[i]->nameid;
 			if( ammo_id <= 0 )
@@ -9786,7 +9825,7 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, ui
 	case EL_WIND_STEP: {
 			struct elemental_data *ele = BL_CAST(BL_ELEM, src);
 			if( ele ) {
-				sc_type type2 = type-1;
+				sc_type type2 = (sc_type)(type-1);
 				struct status_change *sc = status_get_sc(&ele->bl);
 
 				if( (sc && sc->data[type2]) || (tsc && tsc->data[type]) ) {
@@ -9814,7 +9853,7 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, ui
 			struct elemental_data *ele = BL_CAST(BL_ELEM, src);
 			if( ele ) {
 				struct status_change *sc = status_get_sc(&ele->bl);
-				sc_type type2 = type-1;
+				sc_type type2 = (sc_type)(type-1);
 
 				clif_skill_nodamage(src,src,skill_id,skill_lv,1);
 				if( (sc && sc->data[type2]) || (tsc && tsc->data[type]) ) {
@@ -12219,8 +12258,8 @@ struct skill_unit_group* skill_unitsetting (struct block_list *src, uint16 skill
 /*==========================================
  *
  *------------------------------------------*/
-void ext_skill_unit_onplace(struct skill_unit *src, struct block_list *bl, unsigned int tick){skill_unit_onplace(src, bl, tick);}
-static int skill_unit_onplace (struct skill_unit *src, struct block_list *bl, unsigned int tick)
+void ext_skill_unit_onplace(struct skill_unit *unit, struct block_list *bl, unsigned int tick){skill_unit_onplace(unit, bl, tick);}
+static int skill_unit_onplace (struct skill_unit *unit, struct block_list *bl, unsigned int tick)
 {
 	struct skill_unit_group *sg;
 	struct block_list *ss;
@@ -12229,14 +12268,14 @@ static int skill_unit_onplace (struct skill_unit *src, struct block_list *bl, un
 	enum sc_type type;
 	uint16 skill_id;
 
-	nullpo_ret(src);
+	nullpo_ret(unit);
 	nullpo_ret(bl);
 
-	if(bl->prev==NULL || !src->alive || status_isdead(bl))
+	if(bl->prev == NULL || !unit->alive || status_isdead(bl))
 		return 0;
 
-	nullpo_ret(sg=src->group);
-	nullpo_ret(ss=map_id2bl(sg->src_id));
+	nullpo_ret(sg = unit->group);
+	nullpo_ret(ss = map_id2bl(sg->src_id));
 
 	if( (skill_get_type(sg->skill_id) == BF_MAGIC && map_getcell(bl->m, bl->x, bl->y, CELL_CHKLANDPROTECTOR) && sg->skill_id != SA_LANDPROTECTOR) ||
 		map_getcell(bl->m, bl->x, bl->y, CELL_CHKMAELSTROM) )
@@ -12271,7 +12310,7 @@ static int skill_unit_onplace (struct skill_unit *src, struct block_list *bl, un
 					const struct TimerData* td = sc->data[type]?get_timer(sc->data[type]->timer):NULL;
 					if( td )
 						sec = DIFF_TICK(td->tick, tick);
-					map_moveblock(bl, src->bl.x, src->bl.y, tick);
+					map_moveblock(bl, unit->bl.x, unit->bl.y, tick);
 					clif_fixpos(bl);
 					sg->val2 = bl->id;
 				}
@@ -12306,7 +12345,7 @@ static int skill_unit_onplace (struct skill_unit *src, struct block_list *bl, un
 			if(bl->type==BL_PC && !working){
 				struct map_session_data *sd = (struct map_session_data *)bl;
 				if((!sd->chatID || battle_config.chat_warpportal)
-					&& sd->ud.to_x == src->bl.x && sd->ud.to_y == src->bl.y)
+					&& sd->ud.to_x == unit->bl.x && sd->ud.to_y == unit->bl.y)
 				{
 					int x = sg->val2>>16;
 					int y = sg->val2&0xffff;
@@ -12346,12 +12385,12 @@ static int skill_unit_onplace (struct skill_unit *src, struct block_list *bl, un
 		case UNT_SUITON:
 			if(!sce)
 				sc_start4(ss, bl,type,100,sg->skill_lv,
-				map_flag_vs(bl->m) || battle_check_target(&src->bl,bl,BCT_ENEMY)>0?1:0, //Send val3 =1 to reduce agi.
+				map_flag_vs(bl->m) || battle_check_target(&unit->bl,bl,BCT_ENEMY)>0?1:0, //Send val3 =1 to reduce agi.
 				0,0,sg->limit);
 			break;
 
 		case UNT_HERMODE:
-			if (sg->src_id!=bl->id && battle_check_target(&src->bl,bl,BCT_PARTY|BCT_GUILD) > 0)
+			if (sg->src_id!=bl->id && battle_check_target(&unit->bl,bl,BCT_PARTY|BCT_GUILD) > 0)
 				status_change_clear_buffs(bl,1); //Should dispell only allies.
 		case UNT_RICHMANKIM:
 		case UNT_ETERNALCHAOS:
@@ -12391,7 +12430,7 @@ static int skill_unit_onplace (struct skill_unit *src, struct block_list *bl, un
 			if (!sce)
 			{
 				sc_start4(ss, bl, type, 100, sg->skill_lv, sg->val1, sg->val2, sg->group_id, sg->limit);
-				if (battle_check_target(&src->bl,bl,BCT_ENEMY)>0)
+				if (battle_check_target(&unit->bl,bl,BCT_ENEMY)>0)
 					skill_additional_effect (ss, bl, sg->skill_id, sg->skill_lv, BF_MISC, ATK_DEF, tick);
 			}
 			break;
@@ -12403,9 +12442,9 @@ static int skill_unit_onplace (struct skill_unit *src, struct block_list *bl, un
 
 		// officially, icewall has no problems existing on occupied cells [ultramage]
 		//	case UNT_ICEWALL: //Destroy the cell. [Skotlex]
-		//		src->val1 = 0;
-		//		if(src->limit + sg->tick > tick + 700)
-		//			src->limit = DIFF_TICK(tick+700,sg->tick);
+		//		unit->val1 = 0;
+		//		if(unit->limit + sg->tick > tick + 700)
+		//			unit->limit = DIFF_TICK(tick+700,sg->tick);
 		//		break;
 
 		case UNT_MOONLIT:
@@ -13441,33 +13480,33 @@ static int skill_unit_effect (struct block_list* bl, va_list ap)
 /*==========================================
  *
  *------------------------------------------*/
-int64 skill_unit_ondamaged (struct skill_unit *src, struct block_list *bl, int64 damage, unsigned int tick)
+int64 skill_unit_ondamaged (struct skill_unit *unit, struct block_list *bl, int64 damage, unsigned int tick)
 {
 	struct skill_unit_group *sg;
 
-	nullpo_ret(src);
-	nullpo_ret(sg=src->group);
+	nullpo_ret(unit);
+	nullpo_ret(sg = unit->group);
 
 	switch( sg->unit_id ) {
-	case UNT_BLASTMINE:
-	case UNT_SKIDTRAP:
-	case UNT_LANDMINE:
-	case UNT_SHOCKWAVE:
-	case UNT_SANDMAN:
-	case UNT_FLASHER:
-	case UNT_CLAYMORETRAP:
-	case UNT_FREEZINGTRAP:
-	case UNT_TALKIEBOX:
-	case UNT_ANKLESNARE:
-	case UNT_ICEWALL:
-	case UNT_REVERBERATION:
-	case UNT_WALLOFTHORN:
-	case UNT_NETHERWORLD:
-		src->val1-=(int)cap_value(damage,INT_MIN,INT_MAX);
-		break;
-	default:
-		damage = 0;
-		break;
+		case UNT_BLASTMINE:
+		case UNT_SKIDTRAP:
+		case UNT_LANDMINE:
+		case UNT_SHOCKWAVE:
+		case UNT_SANDMAN:
+		case UNT_FLASHER:
+		case UNT_CLAYMORETRAP:
+		case UNT_FREEZINGTRAP:
+		case UNT_TALKIEBOX:
+		case UNT_ANKLESNARE:
+		case UNT_ICEWALL:
+		case UNT_REVERBERATION:
+		case UNT_WALLOFTHORN:
+		case UNT_NETHERWORLD:
+			unit->val1 -= (int)cap_value(damage,INT_MIN,INT_MAX);
+			break;
+		default:
+			damage = 0;
+			break;
 	}
 	return damage;
 }
@@ -16511,7 +16550,7 @@ bool skill_check_shadowform(struct block_list *bl, int64 damage, int hit) {
 			return false;
 		}
 
-		status_damage(bl, src, damage, 0, clif_damage(src, src, gettick(), 500, 500, damage, hit, (hit > 1 ? 8 : 0), 0), 0);
+		status_damage(bl, src, damage, 0, clif_damage(src, src, gettick(), 500, 500, damage, hit, (hit > 1 ? DMG_MULTI_HIT : DMG_NORMAL), 0), 0);
 		if( sc && sc->data[SC__SHADOWFORM] && (--sc->data[SC__SHADOWFORM]->val3) <= 0 ) {
 			status_change_end(bl, SC__SHADOWFORM, INVALID_TIMER);
 			if( src->type == BL_PC )

+ 4 - 3
src/map/skill.h

@@ -370,7 +370,7 @@ int skill_blown(struct block_list* src, struct block_list* target, int count, in
 int skill_break_equip(struct block_list *src,struct block_list *bl, unsigned short where, int rate, int flag);
 int skill_strip_equip(struct block_list *src,struct block_list *bl, unsigned short where, int rate, int lv, int time);
 // Skills unit
-struct skill_unit_group* skill_id2group(int group_id);
+struct skill_unit_group *skill_id2group(int group_id);
 struct skill_unit_group *skill_unitsetting(struct block_list* src, uint16 skill_id, uint16 skill_lv, short x, short y, int flag);
 struct skill_unit *skill_initunit (struct skill_unit_group *group, int idx, int x, int y, int val1, int val2);
 int skill_delunit(struct skill_unit *unit);
@@ -379,8 +379,8 @@ int skill_delunitgroup_(struct skill_unit_group *group, const char* file, int li
 #define skill_delunitgroup(group) skill_delunitgroup_(group,__FILE__,__LINE__,__func__)
 int skill_clear_unitgroup(struct block_list *src);
 int skill_clear_group(struct block_list *bl, int flag);
-void ext_skill_unit_onplace(struct skill_unit *src, struct block_list *bl, unsigned int tick);
-int64 skill_unit_ondamaged(struct skill_unit *src,struct block_list *bl,int64 damage,unsigned int tick);
+void ext_skill_unit_onplace(struct skill_unit *unit, struct block_list *bl, unsigned int tick);
+int64 skill_unit_ondamaged(struct skill_unit *unit,struct block_list *bl,int64 damage,unsigned int tick);
 
 int skill_castfix( struct block_list *bl, uint16 skill_id, uint16 skill_lv);
 int skill_castfix_sc( struct block_list *bl, int time);
@@ -1999,6 +1999,7 @@ int skill_elementalanalysis(struct map_session_data *sd, int n, uint16 skill_lv,
 int skill_changematerial(struct map_session_data *sd, int n, unsigned short *item_list);	// Genetic Change Material.
 int skill_get_elemental_type(uint16 skill_id, uint16 skill_lv);
 
+bool skill_is_combo(uint16 skill_id);
 void skill_combo_toogle_inf(struct block_list* bl, uint16 skill_id, int inf);
 void skill_combo(struct block_list* src,struct block_list *dsrc, struct block_list *bl, uint16 skill_id, uint16 skill_lv, int tick);
 

+ 140 - 164
src/map/status.c

@@ -60,7 +60,7 @@ static int atkmods[3][MAX_WEAPON_TYPE];	/// ATK weapon modification for size (si
 static struct eri *sc_data_ers; /// For sc_data entries
 static struct status_data dummy_status;
 
-int current_equip_item_index; /// Contains inventory index of an equipped item. To pass it into the EQUP_SCRIPT [Lupus]
+short current_equip_item_index; /// Contains inventory index of an equipped item. To pass it into the EQUP_SCRIPT [Lupus]
 int current_equip_card_id; /// To prevent card-stacking (from jA) [Skotlex]
 // We need it for new cards 15 Feb 2005, to check if the combo cards are insrerted into the CURRENT weapon only to avoid cards exploits
 
@@ -240,12 +240,7 @@ void initChangeTables(void)
 	add_sc( TF_POISON		, SC_POISON		);
 	set_sc( KN_TWOHANDQUICKEN	, SC_TWOHANDQUICKEN	, SI_TWOHANDQUICKEN	, SCB_ASPD );
 	set_sc( KN_AUTOCOUNTER		, SC_AUTOCOUNTER	, SI_AUTOCOUNTER	, SCB_NONE );
-	set_sc( PR_IMPOSITIO		, SC_IMPOSITIO		, SI_IMPOSITIO		,
-#ifdef RENEWAL
-			SCB_NONE );
-#else
-			SCB_WATK );
-#endif
+	set_sc( PR_IMPOSITIO		, SC_IMPOSITIO		, SI_IMPOSITIO		, SCB_WATK );
 	set_sc( PR_SUFFRAGIUM		, SC_SUFFRAGIUM		, SI_SUFFRAGIUM		, SCB_NONE );
 	set_sc( PR_ASPERSIO		, SC_ASPERSIO		, SI_ASPERSIO		, SCB_ATK_ELE );
 	set_sc( PR_BENEDICTIO		, SC_BENEDICTIO		, SI_BENEDICTIO		, SCB_DEF_ELE );
@@ -344,12 +339,7 @@ void initChangeTables(void)
 	set_sc( SA_FROSTWEAPON		, SC_WATERWEAPON	, SI_WATERWEAPON	, SCB_ATK_ELE );
 	set_sc( SA_LIGHTNINGLOADER	, SC_WINDWEAPON		, SI_WINDWEAPON		, SCB_ATK_ELE );
 	set_sc( SA_SEISMICWEAPON	, SC_EARTHWEAPON	, SI_EARTHWEAPON	, SCB_ATK_ELE );
-	set_sc( SA_VOLCANO		, SC_VOLCANO		, SI_LANDENDOW		,
-#ifdef RENEWAL
-			SCB_NONE );
-#else
-			SCB_WATK );
-#endif
+	set_sc( SA_VOLCANO		, SC_VOLCANO		, SI_LANDENDOW		, SCB_WATK );
 	set_sc( SA_DELUGE		, SC_DELUGE		, SI_LANDENDOW		, SCB_MAXHP );
 	set_sc( SA_VIOLENTGALE		, SC_VIOLENTGALE	, SI_LANDENDOW		, SCB_FLEE );
 	add_sc( SA_REVERSEORCISH	, SC_ORCISH		);
@@ -357,17 +347,8 @@ void initChangeTables(void)
 	set_sc( BD_ENCORE		, SC_DANCING		, SI_BDPLAYING		, SCB_SPEED|SCB_REGEN );
 	set_sc( BD_RICHMANKIM		, SC_RICHMANKIM		, SI_RICHMANKIM	, SCB_NONE	);
 	set_sc( BD_ETERNALCHAOS		, SC_ETERNALCHAOS	, SI_ETERNALCHAOS	, SCB_DEF2 );
-	set_sc( BD_DRUMBATTLEFIELD	, SC_DRUMBATTLE		, SI_DRUMBATTLEFIELD	, SCB_DEF
-#ifndef RENEWAL
-			|SCB_WATK
-#endif
-			);
-	set_sc( BD_RINGNIBELUNGEN	, SC_NIBELUNGEN		, SI_RINGNIBELUNGEN		,
-#ifdef RENEWAL
-			SCB_NONE );
-#else
-			SCB_WATK );
-#endif
+	set_sc( BD_DRUMBATTLEFIELD	, SC_DRUMBATTLE		, SI_DRUMBATTLEFIELD	, SCB_WATK|SCB_DEF );
+	set_sc( BD_RINGNIBELUNGEN	, SC_NIBELUNGEN		, SI_RINGNIBELUNGEN		, SCB_WATK );
 	set_sc( BD_ROKISWEIL		, SC_ROKISWEIL	, SI_ROKISWEIL	, SCB_NONE );
 	set_sc( BD_INTOABYSS		, SC_INTOABYSS	, SI_INTOABYSS	, SCB_NONE );
 	set_sc( BD_SIEGFRIED		, SC_SIEGFRIED		, SI_SIEGFRIED	, SCB_ALL );
@@ -392,12 +373,7 @@ void initChangeTables(void)
 	add_sc( NPC_INVISIBLE		, SC_CLOAKING		);
 	set_sc( LK_AURABLADE		, SC_AURABLADE		, SI_AURABLADE		, SCB_NONE );
 	set_sc( LK_PARRYING		, SC_PARRYING		, SI_PARRYING		, SCB_NONE );
-	set_sc( LK_CONCENTRATION	, SC_CONCENTRATION	, SI_CONCENTRATION	,
-#ifndef RENEWAL
-			SCB_BATK|SCB_WATK|SCB_HIT|SCB_DEF|SCB_DEF2 );
-#else
-			SCB_HIT|SCB_DEF );
-#endif
+	set_sc( LK_CONCENTRATION	, SC_CONCENTRATION	, SI_CONCENTRATION	, SCB_BATK|SCB_WATK|SCB_HIT|SCB_DEF|SCB_DEF2 );
 	set_sc( LK_TENSIONRELAX		, SC_TENSIONRELAX	, SI_TENSIONRELAX	, SCB_REGEN );
 	set_sc( LK_BERSERK		, SC_BERSERK		, SI_BERSERK		, SCB_DEF|SCB_DEF2|SCB_MDEF|SCB_MDEF2|SCB_FLEE|SCB_SPEED|SCB_ASPD|SCB_MAXHP|SCB_REGEN );
 	set_sc( HP_ASSUMPTIO		, SC_ASSUMPTIO		,
@@ -477,18 +453,10 @@ void initChangeTables(void)
 	add_sc( GS_CRACKER		, SC_STUN		);
 	add_sc( GS_DISARM		, SC_STRIPWEAPON	);
 	add_sc( GS_PIERCINGSHOT		, SC_BLEEDING		);
-	set_sc( GS_MADNESSCANCEL	, SC_MADNESSCANCEL	, SI_MADNESSCANCEL	, SCB_ASPD
-#ifndef RENEWAL
-			|SCB_BATK
-#endif
-			);
+	set_sc( GS_MADNESSCANCEL	, SC_MADNESSCANCEL	, SI_MADNESSCANCEL	, SCB_BATK|SCB_ASPD );
 	set_sc( GS_ADJUSTMENT		, SC_ADJUSTMENT		, SI_ADJUSTMENT		, SCB_HIT|SCB_FLEE );
 	set_sc( GS_INCREASING		, SC_INCREASING		, SI_ACCURACY		, SCB_AGI|SCB_DEX|SCB_HIT );
-	set_sc( GS_GATLINGFEVER		, SC_GATLINGFEVER	, SI_GATLINGFEVER	, SCB_FLEE|SCB_SPEED|SCB_ASPD
-#ifndef RENWAL
-			|SCB_BATK
-#endif
-			);
+	set_sc( GS_GATLINGFEVER		, SC_GATLINGFEVER	, SI_GATLINGFEVER	, SCB_BATK|SCB_FLEE|SCB_SPEED|SCB_ASPD );
 	add_sc( NJ_TATAMIGAESHI		, SC_TATAMIGAESHI	);
 	set_sc( NJ_SUITON		, SC_SUITON		, SI_BLANK		, SCB_AGI|SCB_SPEED );
 	add_sc( NJ_HYOUSYOURAKU		, SC_FREEZE		);
@@ -609,7 +577,7 @@ void initChangeTables(void)
 	set_sc( RK_GIANTGROWTH		, SC_GIANTGROWTH	, SI_GIANTGROWTH		, SCB_STR );
 	set_sc( RK_STONEHARDSKIN	, SC_STONEHARDSKIN	, SI_STONEHARDSKIN		, SCB_DEF|SCB_MDEF );
 	set_sc( RK_VITALITYACTIVATION	, SC_VITALITYACTIVATION	, SI_VITALITYACTIVATION		, SCB_REGEN );
-	set_sc( RK_FIGHTINGSPIRIT	, SC_FIGHTINGSPIRIT	, SI_FIGHTINGSPIRIT		, SCB_ASPD );
+	set_sc( RK_FIGHTINGSPIRIT	, SC_FIGHTINGSPIRIT	, SI_FIGHTINGSPIRIT		, SCB_WATK|SCB_ASPD );
 	set_sc( RK_ABUNDANCE		, SC_ABUNDANCE		, SI_ABUNDANCE			, SCB_NONE );
 	set_sc( RK_CRUSHSTRIKE		, SC_CRUSHSTRIKE	, SI_CRUSHSTRIKE		, SCB_NONE );
 	set_sc_with_vfx( RK_DRAGONBREATH_WATER	, SC_FREEZING	, SI_FROSTMISTY			, SCB_ASPD|SCB_SPEED|SCB_DEF|SCB_DEF2 );
@@ -680,15 +648,10 @@ void initChangeTables(void)
 	set_sc( LG_FORCEOFVANGUARD	, SC_FORCEOFVANGUARD	, SI_FORCEOFVANGUARD	, SCB_MAXHP );
 	set_sc( LG_EXEEDBREAK		, SC_EXEEDBREAK		, SI_EXEEDBREAK		, SCB_NONE );
 	set_sc( LG_PRESTIGE		, SC_PRESTIGE		, SI_PRESTIGE		, SCB_DEF );
-	set_sc( LG_BANDING		, SC_BANDING		, SI_BANDING		,
-#ifndef RENEWAL
-			SCB_DEF );
-#else
-			SCB_DEF2 );
-#endif
+	set_sc( LG_BANDING		, SC_BANDING		, SI_BANDING		, SCB_DEF2|SCB_WATK );
 	set_sc( LG_PIETY		, SC_BENEDICTIO		, SI_BENEDICTIO		, SCB_DEF_ELE );
 	set_sc( LG_EARTHDRIVE		, SC_EARTHDRIVE		, SI_EARTHDRIVE		, SCB_DEF|SCB_ASPD );
-	set_sc( LG_INSPIRATION		, SC_INSPIRATION	, SI_INSPIRATION	, SCB_MAXHP|SCB_HIT|SCB_VIT|SCB_AGI|SCB_STR|SCB_DEX|SCB_INT|SCB_LUK);
+	set_sc( LG_INSPIRATION		, SC_INSPIRATION	, SI_INSPIRATION	, SCB_WATK|SCB_STR|SCB_AGI|SCB_VIT|SCB_INT|SCB_DEX|SCB_LUK|SCB_HIT|SCB_MAXHP);
 	set_sc( LG_KINGS_GRACE		, SC_KINGS_GRACE	, SI_KINGS_GRACE	, SCB_NONE );
 
 	/* Shadow Chaser */
@@ -718,7 +681,7 @@ void initChangeTables(void)
 	set_sc( SR_LIGHTNINGWALK		, SC_LIGHTNINGWALK	, SI_LIGHTNINGWALK		, SCB_NONE );
 	set_sc( SR_RAISINGDRAGON		, SC_RAISINGDRAGON	, SI_RAISINGDRAGON		, SCB_REGEN|SCB_MAXHP|SCB_MAXSP );
 	set_sc( SR_GENTLETOUCH_ENERGYGAIN	, SC_GT_ENERGYGAIN	, SI_GENTLETOUCH_ENERGYGAIN	, SCB_NONE );
-	set_sc( SR_GENTLETOUCH_CHANGE		, SC_GT_CHANGE		, SI_GENTLETOUCH_CHANGE		, SCB_MDEF|SCB_ASPD|SCB_MAXHP );
+	set_sc( SR_GENTLETOUCH_CHANGE		, SC_GT_CHANGE		, SI_GENTLETOUCH_CHANGE		, SCB_WATK|SCB_MDEF|SCB_ASPD|SCB_MAXHP );
 	set_sc( SR_GENTLETOUCH_REVITALIZE	, SC_GT_REVITALIZE	, SI_GENTLETOUCH_REVITALIZE	, SCB_MAXHP|SCB_REGEN );
 	add_sc( SR_FLASHCOMBO			, SC_FLASHCOMBO );
 
@@ -726,7 +689,7 @@ void initChangeTables(void)
 	set_sc( WA_SWING_DANCE			, SC_SWINGDANCE			, SI_SWINGDANCE			, SCB_SPEED|SCB_ASPD );
 	set_sc( WA_SYMPHONY_OF_LOVER		, SC_SYMPHONYOFLOVER		, SI_SYMPHONYOFLOVERS		, SCB_MDEF );
 	set_sc( WA_MOONLIT_SERENADE		, SC_MOONLITSERENADE		, SI_MOONLITSERENADE		, SCB_MATK );
-	set_sc( MI_RUSH_WINDMILL		, SC_RUSHWINDMILL		, SI_RUSHWINDMILL		, SCB_NONE  );
+	set_sc( MI_RUSH_WINDMILL		, SC_RUSHWINDMILL		, SI_RUSHWINDMILL		, SCB_WATK  );
 	set_sc( MI_ECHOSONG			, SC_ECHOSONG			, SI_ECHOSONG			, SCB_DEF  );
 	set_sc( MI_HARMONIZE			, SC_HARMONIZE			, SI_HARMONIZE			, SCB_STR|SCB_AGI|SCB_VIT|SCB_INT|SCB_DEX|SCB_LUK );
 	set_sc_with_vfx( WM_POEMOFNETHERWORLD	, SC_NETHERWORLD		, SI_NETHERWORLD		, SCB_NONE );
@@ -736,7 +699,7 @@ void initChangeTables(void)
 	set_sc( WM_GLOOMYDAY			, SC_GLOOMYDAY			, SI_GLOOMYDAY			, SCB_FLEE|SCB_SPEED|SCB_ASPD );
 	set_sc( WM_SONG_OF_MANA			, SC_SONGOFMANA			, SI_SONGOFMANA			, SCB_NONE );
 	set_sc( WM_DANCE_WITH_WUG		, SC_DANCEWITHWUG		, SI_DANCEWITHWUG		, SCB_ASPD );
-	set_sc( WM_SATURDAY_NIGHT_FEVER		, SC_SATURDAYNIGHTFEVER		, SI_SATURDAYNIGHTFEVER		, SCB_DEF|SCB_FLEE|SCB_REGEN );
+	set_sc( WM_SATURDAY_NIGHT_FEVER		, SC_SATURDAYNIGHTFEVER		, SI_SATURDAYNIGHTFEVER		, SCB_BATK|SCB_DEF|SCB_FLEE|SCB_REGEN );
 	set_sc( WM_LERADS_DEW			, SC_LERADSDEW			, SI_LERADSDEW			, SCB_MAXHP );
 	set_sc( WM_MELODYOFSINK			, SC_MELODYOFSINK		, SI_MELODYOFSINK		, SCB_INT );
 	set_sc( WM_BEYOND_OF_WARCRY		, SC_BEYONDOFWARCRY		, SI_WARCRYOFBEYOND		, SCB_STR|SCB_CRI|SCB_MAXHP );
@@ -763,7 +726,6 @@ void initChangeTables(void)
 	set_sc( GN_CARTBOOST			, SC_GN_CARTBOOST	, SI_GN_CARTBOOST			, SCB_SPEED );
 	set_sc( GN_THORNS_TRAP			, SC_THORNSTRAP		, SI_THORNTRAP			, SCB_NONE );
 	set_sc_with_vfx( GN_BLOOD_SUCKER	, SC_BLOODSUCKER	, SI_BLOODSUCKER		, SCB_NONE );
-	add_sc( GN_WALLOFTHORN			, SC_STOP		);
 	set_sc( GN_FIRE_EXPANSION_SMOKE_POWDER	, SC_SMOKEPOWDER	, SI_FIRE_EXPANSION_SMOKE_POWDER, SCB_FLEE );
 	set_sc( GN_FIRE_EXPANSION_TEAR_GAS	, SC_TEARGAS		, SI_FIRE_EXPANSION_TEAR_GAS	, SCB_HIT|SCB_FLEE );
 	set_sc( GN_MANDRAGORA			, SC_MANDRAGORA		, SI_MANDRAGORA			, SCB_INT );
@@ -781,9 +743,9 @@ void initChangeTables(void)
 	set_sc( EL_SOLID_SKIN		, SC_SOLID_SKIN_OPTION		, SI_SOLID_SKIN_OPTION		, SCB_DEF|SCB_MAXHP );
 	set_sc( EL_STONE_SHIELD		, SC_STONE_SHIELD_OPTION	, SI_STONE_SHIELD_OPTION	, SCB_ALL );
 	set_sc( EL_POWER_OF_GAIA	, SC_POWER_OF_GAIA		, SI_POWER_OF_GAIA		, SCB_MAXHP|SCB_DEF|SCB_SPEED );
-	set_sc( EL_PYROTECHNIC		, SC_PYROTECHNIC_OPTION		, SI_PYROTECHNIC_OPTION		, SCB_NONE );
-	set_sc( EL_HEATER		, SC_HEATER_OPTION		, SI_HEATER_OPTION		, SCB_NONE );
-	set_sc( EL_TROPIC		, SC_TROPIC_OPTION		, SI_TROPIC_OPTION		, SCB_NONE );
+	set_sc( EL_PYROTECHNIC		, SC_PYROTECHNIC_OPTION		, SI_PYROTECHNIC_OPTION		, SCB_WATK );
+	set_sc( EL_HEATER		, SC_HEATER_OPTION		, SI_HEATER_OPTION		, SCB_WATK );
+	set_sc( EL_TROPIC		, SC_TROPIC_OPTION		, SI_TROPIC_OPTION		, SCB_WATK );
 	set_sc( EL_AQUAPLAY		, SC_AQUAPLAY_OPTION		, SI_AQUAPLAY_OPTION		, SCB_MATK );
 	set_sc( EL_COOLER		, SC_COOLER_OPTION		, SI_COOLER_OPTION		, SCB_MATK );
 	set_sc( EL_CHILLY_AIR		, SC_CHILLY_AIR_OPTION		, SI_CHILLY_AIR_OPTION		, SCB_MATK );
@@ -1081,6 +1043,7 @@ void initChangeTables(void)
 	StatusChangeFlagTable[SC_PYREXIA] |= SCB_HIT|SCB_FLEE;
 	StatusChangeFlagTable[SC_OBLIVIONCURSE] |= SCB_REGEN;
 	StatusChangeFlagTable[SC_BANDING_DEFENCE] |= SCB_SPEED;
+	StatusChangeFlagTable[SC_SHIELDSPELL_DEF] |= SCB_WATK;
 	StatusChangeFlagTable[SC_SHIELDSPELL_REF] |= SCB_DEF;
 	StatusChangeFlagTable[SC_STOMACHACHE] |= SCB_STR|SCB_AGI|SCB_VIT|SCB_DEX|SCB_INT|SCB_LUK;
 	StatusChangeFlagTable[SC_MYSTERIOUS_POWDER] |= SCB_MAXHP;
@@ -1354,6 +1317,8 @@ int status_damage(struct block_list *src,struct block_list *target,int64 dhp, in
 	int hp = (int)cap_value(dhp,INT_MIN,INT_MAX);
 	int sp = (int)cap_value(dsp,INT_MIN,INT_MAX);
 
+	nullpo_ret(target);
+
 	if(sp && !(target->type&BL_CONSUME))
 		sp = 0; // Not a valid SP target.
 
@@ -1371,7 +1336,7 @@ int status_damage(struct block_list *src,struct block_list *target,int64 dhp, in
 		return (int)skill_unit_ondamaged((struct skill_unit *)target, src, hp, gettick());
 
 	status = status_get_status_data(target);
-	if( status == &dummy_status )
+	if(!status || status == &dummy_status )
 		return 0;
 
 	if ((unsigned int)hp >= status->hp) {
@@ -2790,7 +2755,8 @@ int status_calc_pc_(struct map_session_data* sd, enum e_status_calc_opt opt)
 	const struct status_change *sc = &sd->sc;
 	struct s_skill b_skill[MAX_SKILL]; ///< Previous skill tree
 	int b_weight, b_max_weight, b_cart_weight_max, ///< Previous weight
-	i, index, skill,refinedef=0;
+	i, skill,refinedef=0;
+	short index = -1;
 
 	if (++calculating > 10) // Too many recursive calls!
 		return -1;
@@ -2883,11 +2849,18 @@ int status_calc_pc_(struct map_session_data* sd, enum e_status_calc_opt opt)
 		clif_status_load(&sd->bl, SI_INTRAVISION, 0);
 
 	memset(&sd->special_state,0,sizeof(sd->special_state));
-	memset(&status->max_hp, 0, sizeof(struct status_data)-(sizeof(status->hp)+sizeof(status->sp)));
 
-	// !FIXME: Most of these stuff should be calculated once, but how do I fix the memset above to do that? [Skotlex]
-	if (!sd->state.permanent_speed)
+	if (!sd->state.permanent_speed) {
+		memset(&status->max_hp, 0, sizeof(struct status_data)-(sizeof(status->hp)+sizeof(status->sp)));
 		status->speed = DEFAULT_WALK_SPEED;
+	} else {
+		int pSpeed = status->speed;
+
+		memset(&status->max_hp, 0, sizeof(struct status_data)-(sizeof(status->hp)+sizeof(status->sp)));
+		status->speed = pSpeed;
+	}
+
+	// !FIXME: Most of these stuff should be calculated once, but how do I fix the memset above to do that? [Skotlex]
 	// Give them all modes except these (useful for clones)
 	status->mode = MD_MASK&~(MD_BOSS|MD_PLANT|MD_DETECTOR|MD_ANGRY|MD_TARGETWEAK);
 
@@ -2933,6 +2906,8 @@ int status_calc_pc_(struct map_session_data* sd, enum e_status_calc_opt opt)
 		+ sizeof(sd->skillfixcast)
 		+ sizeof(sd->skillvarcast)
 		+ sizeof(sd->skillfixcastrate)
+		+ sizeof(sd->def_set_race)
+		+ sizeof(sd->mdef_set_race)
 	);
 
 	memset (&sd->bonus, 0,sizeof(sd->bonus));
@@ -4279,6 +4254,7 @@ void status_calc_bl_main(struct block_list *bl, /*enum scb_flag*/int flag)
 
 	if(flag&SCB_SPEED) {
 		struct unit_data *ud = unit_bl2ud(bl);
+
 		status->speed = status_calc_speed(bl, sc, b_status->speed);
 
 		/** [Skotlex]
@@ -4289,7 +4265,7 @@ void status_calc_bl_main(struct block_list *bl, /*enum scb_flag*/int flag)
 		if (ud)
 			ud->state.change_walk_target = ud->state.speed_changed = 1;
 
-		if( bl->type&BL_PC && status->speed < battle_config.max_walk_speed )
+		if( bl->type&BL_PC && !(sd && sd->state.permanent_speed) && status->speed < battle_config.max_walk_speed )
 			status->speed = battle_config.max_walk_speed;
 
 		if( bl->type&BL_HOM && battle_config.hom_setting&HOMSET_COPY_SPEED && ((TBL_HOM*)bl)->master)
@@ -4298,8 +4274,6 @@ void status_calc_bl_main(struct block_list *bl, /*enum scb_flag*/int flag)
 			status->speed = status_get_speed(&((TBL_MER*)bl)->master->bl);
 		if( bl->type&BL_ELEM && ((TBL_ELEM*)bl)->master)
 			status->speed = status_get_speed(&((TBL_ELEM*)bl)->master->bl);
-
-
 	}
 
 	if(flag&SCB_CRI && b_status->cri) {
@@ -4311,7 +4285,6 @@ void status_calc_bl_main(struct block_list *bl, /*enum scb_flag*/int flag)
 		/// After status_calc_critical so the bonus is applied despite if you have or not a sc bugreport:5240
 		if( bl->type == BL_PC && ((TBL_PC*)bl)->status.weapon == W_KATAR )
 			status->cri <<= 1;
-
 	}
 
 	if(flag&SCB_FLEE2 && b_status->flee2) {
@@ -4780,7 +4753,7 @@ static unsigned short status_calc_str(struct block_list *bl, struct status_chang
 	if(sc->data[SC_SAVAGE_STEAK])
 		str += sc->data[SC_SAVAGE_STEAK]->val1;
 	if(sc->data[SC_INSPIRATION])
-		str += sc->data[SC_INSPIRATION]->val2;
+		str += sc->data[SC_INSPIRATION]->val3;
 	if(sc->data[SC_STOMACHACHE])
 		str -= sc->data[SC_STOMACHACHE]->val1;
 	if(sc->data[SC_KYOUGAKU])
@@ -4844,7 +4817,7 @@ static unsigned short status_calc_agi(struct block_list *bl, struct status_chang
 	if(sc->data[SC_DROCERA_HERB_STEAMED])
 		agi += sc->data[SC_DROCERA_HERB_STEAMED]->val1;
 	if(sc->data[SC_INSPIRATION])
-		agi += sc->data[SC_INSPIRATION]->val2;
+		agi += sc->data[SC_INSPIRATION]->val3;
 	if(sc->data[SC_STOMACHACHE])
 		agi -= sc->data[SC_STOMACHACHE]->val1;
 	if(sc->data[SC_KYOUGAKU])
@@ -4896,7 +4869,7 @@ static unsigned short status_calc_vit(struct block_list *bl, struct status_chang
 	if(sc->data[SC_MINOR_BBQ])
 		vit += sc->data[SC_MINOR_BBQ]->val1;
 	if(sc->data[SC_INSPIRATION])
-		vit += sc->data[SC_INSPIRATION]->val2;
+		vit += sc->data[SC_INSPIRATION]->val3;
 	if(sc->data[SC_STOMACHACHE])
 		vit -= sc->data[SC_STOMACHACHE]->val1;
 	if(sc->data[SC_KYOUGAKU])
@@ -4957,14 +4930,14 @@ static unsigned short status_calc_int(struct block_list *bl, struct status_chang
 		int_ -= ((sc->data[SC_MARIONETTE]->val4)>>16)&0xFF;
 	if(sc->data[SC_MARIONETTE2])
 		int_ += ((sc->data[SC_MARIONETTE2]->val4)>>16)&0xFF;
+	if(sc->data[SC_INSPIRATION])
+		int_ += sc->data[SC_INSPIRATION]->val3;
 	if(sc->data[SC_MELODYOFSINK])
 		int_ -= sc->data[SC_MELODYOFSINK]->val3;
 	if(sc->data[SC_MANDRAGORA])
 		int_ -= 4 * sc->data[SC_MANDRAGORA]->val1;
 	if(sc->data[SC_COCKTAIL_WARG_BLOOD])
 		int_ += sc->data[SC_COCKTAIL_WARG_BLOOD]->val1;
-	if(sc->data[SC_INSPIRATION])
-		int_ += sc->data[SC_INSPIRATION]->val2;
 	if(sc->data[SC_STOMACHACHE])
 		int_ -= sc->data[SC_STOMACHACHE]->val1;
 	if(sc->data[SC_KYOUGAKU])
@@ -5033,7 +5006,7 @@ static unsigned short status_calc_dex(struct block_list *bl, struct status_chang
 	if(sc->data[SC_SIROMA_ICE_TEA])
 		dex += sc->data[SC_SIROMA_ICE_TEA]->val1;
 	if(sc->data[SC_INSPIRATION])
-		dex += sc->data[SC_INSPIRATION]->val2;
+		dex += sc->data[SC_INSPIRATION]->val3;
 	if(sc->data[SC_STOMACHACHE])
 		dex -= sc->data[SC_STOMACHACHE]->val1;
 	if(sc->data[SC_KYOUGAKU])
@@ -5088,7 +5061,7 @@ static unsigned short status_calc_luk(struct block_list *bl, struct status_chang
 	if(sc->data[SC_PUTTI_TAILS_NOODLES])
 		luk += sc->data[SC_PUTTI_TAILS_NOODLES]->val1;
 	if(sc->data[SC_INSPIRATION])
-		luk += sc->data[SC_INSPIRATION]->val2;
+		luk += sc->data[SC_INSPIRATION]->val3;
 	if(sc->data[SC_STOMACHACHE])
 		luk -= sc->data[SC_STOMACHACHE]->val1;
 	if(sc->data[SC_KYOUGAKU])
@@ -5120,14 +5093,12 @@ static unsigned short status_calc_batk(struct block_list *bl, struct status_chan
 
 	if(sc->data[SC_ATKPOTION])
 		batk += sc->data[SC_ATKPOTION]->val1;
-#ifndef RENEWAL
-	if(sc->data[SC_MADNESSCANCEL])
-		batk += 100;
-	if(sc->data[SC_GATLINGFEVER])
-		batk += sc->data[SC_GATLINGFEVER]->val3;
-#endif
 	if(sc->data[SC_BATKFOOD])
 		batk += sc->data[SC_BATKFOOD]->val1;
+	if(sc->data[SC_GATLINGFEVER])
+		batk += sc->data[SC_GATLINGFEVER]->val3;
+	if(sc->data[SC_MADNESSCANCEL])
+		batk += 100;
 	if(sc->data[SC_FIRE_INSIGNIA] && sc->data[SC_FIRE_INSIGNIA]->val1 == 2)
 		batk += 50;
 	if(bl->type == BL_ELEM
@@ -5150,10 +5121,8 @@ static unsigned short status_calc_batk(struct block_list *bl, struct status_chan
 		batk += batk * sc->data[SC_INCATKRATE]->val1/100;
 	if(sc->data[SC_PROVOKE])
 		batk += batk * sc->data[SC_PROVOKE]->val3/100;
-#ifndef RENEWAL
 	if(sc->data[SC_CONCENTRATION])
 		batk += batk * sc->data[SC_CONCENTRATION]->val2/100;
-#endif
 	if(sc->data[SC_SKE])
 		batk += batk * 3;
 	if(sc->data[SC_BLOODLUST])
@@ -5169,6 +5138,8 @@ static unsigned short status_calc_batk(struct block_list *bl, struct status_chan
 		batk += batk * sc->data[SC_FLEET]->val3/100;
 	if(sc->data[SC__ENERVATION])
 		batk -= batk * sc->data[SC__ENERVATION]->val2 / 100;
+	if(sc->data[SC_SATURDAYNIGHTFEVER])
+		batk += 100 * sc->data[SC_SATURDAYNIGHTFEVER]->val1;
 	if( sc->data[SC_ZANGETSU] )
 		batk += sc->data[SC_ZANGETSU]->val2;
 	if(sc->data[SC_EQC])
@@ -5195,37 +5166,37 @@ static unsigned short status_calc_watk(struct block_list *bl, struct status_chan
 	if(!sc || !sc->count)
 		return cap_value(watk,0,USHRT_MAX);
 
-	if(sc->data[SC_WATKFOOD])
-		watk += sc->data[SC_WATKFOOD]->val1;
-#ifndef RENEWAL
 	if(sc->data[SC_IMPOSITIO])
 		watk += sc->data[SC_IMPOSITIO]->val2;
-	if(sc->data[SC_VOLCANO])
-		watk += sc->data[SC_VOLCANO]->val2;
+	if(sc->data[SC_WATKFOOD])
+		watk += sc->data[SC_WATKFOOD]->val1;
 	if(sc->data[SC_DRUMBATTLE])
 		watk += sc->data[SC_DRUMBATTLE]->val2;
+	if(sc->data[SC_VOLCANO])
+		watk += sc->data[SC_VOLCANO]->val2;
+	if(sc->data[SC_MERC_ATKUP])
+		watk += sc->data[SC_MERC_ATKUP]->val2;
+	if(sc->data[SC_WATER_BARRIER])
+		watk -= sc->data[SC_WATER_BARRIER]->val3;
 	if(sc->data[SC_NIBELUNGEN]) {
 		if (bl->type != BL_PC)
 			watk += sc->data[SC_NIBELUNGEN]->val2;
+		#ifndef RENEWAL
 		else {
 			TBL_PC *sd = (TBL_PC*)bl;
-			int index = sd->equip_index[sd->state.lr_flag?EQI_HAND_L:EQI_HAND_R];
+			short index = sd->equip_index[sd->state.lr_flag?EQI_HAND_L:EQI_HAND_R];
 
 			if(index >= 0 && sd->inventory_data[index] && sd->inventory_data[index]->wlv == 4)
 				watk += sc->data[SC_NIBELUNGEN]->val2;
 		}
+		#endif
 	}
-	if(sc->data[SC_CONCENTRATION])
-		watk += watk * sc->data[SC_CONCENTRATION]->val2/100;
-#endif
-	if(sc->data[SC_MERC_ATKUP])
-		watk += sc->data[SC_MERC_ATKUP]->val2;
-	if(sc->data[SC_WATER_BARRIER])
-		watk -= sc->data[SC_WATER_BARRIER]->val3;
 	if(sc->data[SC_INCATKRATE])
 		watk += watk * sc->data[SC_INCATKRATE]->val1/100;
 	if(sc->data[SC_PROVOKE])
 		watk += watk * sc->data[SC_PROVOKE]->val3/100;
+	if(sc->data[SC_CONCENTRATION])
+		watk += watk * sc->data[SC_CONCENTRATION]->val2/100;
 	if(sc->data[SC_SKE])
 		watk += watk * 3;
 	if(sc->data[SC_FLEET])
@@ -5234,15 +5205,33 @@ static unsigned short status_calc_watk(struct block_list *bl, struct status_chan
 		watk -= watk * 25/100;
 	if(sc->data[SC_STRIPWEAPON] && bl->type != BL_PC)
 		watk -= watk * sc->data[SC_STRIPWEAPON]->val2/100;
+	if(sc->data[SC_FIGHTINGSPIRIT])
+		watk += sc->data[SC_FIGHTINGSPIRIT]->val1;
+	if(sc->data[SC_SHIELDSPELL_DEF] && sc->data[SC_SHIELDSPELL_DEF]->val1 == 3)
+		watk += sc->data[SC_SHIELDSPELL_DEF]->val2;
+	if(sc->data[SC_BANDING] && sc->data[SC_BANDING]->val2 > 1)
+		watk += (10 + 10 * sc->data[SC_BANDING]->val1) * sc->data[SC_BANDING]->val2;
+	if(sc->data[SC_INSPIRATION])
+		watk += 40 * sc->data[SC_INSPIRATION]->val1 + 3 * sc->data[SC_INSPIRATION]->val2;
+	if(sc->data[SC_GT_CHANGE] && sc->data[SC_GT_CHANGE]->val2)
+		watk += sc->data[SC_GT_CHANGE]->val2;
 	if(sc->data[SC__ENERVATION])
 		watk -= watk * sc->data[SC__ENERVATION]->val2 / 100;
 	if(sc->data[SC_STRIKING])
 		watk += sc->data[SC_STRIKING]->val2;
+	if(sc->data[SC_RUSHWINDMILL])
+		watk += sc->data[SC_RUSHWINDMILL]->val3;
 	if((sc->data[SC_FIRE_INSIGNIA] && sc->data[SC_FIRE_INSIGNIA]->val1 == 2)
 	   || (sc->data[SC_WATER_INSIGNIA] && sc->data[SC_WATER_INSIGNIA]->val1 == 2)
 	   || (sc->data[SC_WIND_INSIGNIA] && sc->data[SC_WIND_INSIGNIA]->val1 == 2)
 	   || (sc->data[SC_EARTH_INSIGNIA] && sc->data[SC_EARTH_INSIGNIA]->val1 == 2))
 		watk += watk / 10;
+	if(sc->data[SC_PYROTECHNIC_OPTION])
+		watk += sc->data[SC_PYROTECHNIC_OPTION]->val2;
+	if(sc->data[SC_HEATER_OPTION])
+		watk += sc->data[SC_HEATER_OPTION]->val2;
+	if(sc->data[SC_TROPIC_OPTION])
+		watk += sc->data[SC_TROPIC_OPTION]->val2;
 	if( sc && sc->data[SC_TIDAL_WEAPON] )
 		watk += watk * sc->data[SC_TIDAL_WEAPON]->val2 / 100;
 	if(bl->type == BL_PC && sc->data[SC_PYROCLASTIC])
@@ -5420,7 +5409,7 @@ static signed short status_calc_hit(struct block_list *bl, struct status_change
 	if(sc->data[SC_CONCENTRATION])
 		hit += sc->data[SC_CONCENTRATION]->val3;
 	if(sc->data[SC_INSPIRATION])
-		hit += 5 * sc->data[SC_INSPIRATION]->val1 + sc->data[SC_INSPIRATION]->val3 / 2;
+		hit += 5 * sc->data[SC_INSPIRATION]->val1 + sc->data[SC_INSPIRATION]->val2 / 2;
 	if(sc->data[SC_ADJUSTMENT])
 		hit -= 30;
 	if(sc->data[SC_INCREASING])
@@ -5641,10 +5630,8 @@ static defType status_calc_def(struct block_list *bl, struct status_change *sc,
 		def += sc->data[SC_SHIELDSPELL_REF]->val2;
 	if( sc->data[SC_PRESTIGE] )
 		def += sc->data[SC_PRESTIGE]->val1;
-#ifndef RENEWAL
 	if( sc->data[SC_BANDING] && sc->data[SC_BANDING]->val2 > 1 )
 		def += (5 + sc->data[SC_BANDING]->val1) * sc->data[SC_BANDING]->val2 / 10;
-#endif
 	if( sc->data[SC_ECHOSONG] )
 		def += def * sc->data[SC_ECHOSONG]->val3 / 100;
 	if( sc->data[SC_SATURDAYNIGHTFEVER] )
@@ -5692,19 +5679,17 @@ static signed short status_calc_def2(struct block_list *bl, struct status_change
 
 	if(sc->data[SC_SUN_COMFORT])
 		def2 += sc->data[SC_SUN_COMFORT]->val2;
-#ifdef RENEWAL
 	if( sc->data[SC_BANDING] && sc->data[SC_BANDING]->val2 > 1 )
 		def2 += (5 + sc->data[SC_BANDING]->val1) * sc->data[SC_BANDING]->val2;
-#endif
 
 	if(sc->data[SC_ANGELUS])
 #ifdef RENEWAL /// The VIT stat bonus is boosted by angelus [RENEWAL]
 		def2 += status_get_vit(bl) / 2 * sc->data[SC_ANGELUS]->val2/100;
 #else
 		def2 += def2 * sc->data[SC_ANGELUS]->val2/100;
+#endif
 	if(sc->data[SC_CONCENTRATION])
 		def2 -= def2 * sc->data[SC_CONCENTRATION]->val4/100;
-#endif
 	if(sc->data[SC_POISON])
 		def2 -= def2 * 25/100;
 	if(sc->data[SC_DPOISON])
@@ -5846,11 +5831,8 @@ static unsigned short status_calc_speed(struct block_list *bl, struct status_cha
 	TBL_PC* sd = BL_CAST(BL_PC, bl);
 	int speed_rate = 100;
 
-	if( sc == NULL )
-		return cap_value(speed,10,USHRT_MAX);
-
-	if (sd && sd->state.permanent_speed)
-		return (short)cap_value(speed,10,USHRT_MAX);
+	if (sc == NULL || (sd && sd->state.permanent_speed))
+		return (unsigned short)cap_value(speed, MIN_WALK_SPEED, MAX_WALK_SPEED);
 
 	if( sd && sd->ud.skilltimer != INVALID_TIMER && (pc_checkskill(sd,SA_FREECAST) > 0 || sd->ud.skill_id == LG_EXEEDBREAK) ) {
 		if( sd->ud.skill_id == LG_EXEEDBREAK )
@@ -6012,7 +5994,7 @@ static unsigned short status_calc_speed(struct block_list *bl, struct status_cha
 	if( sc->data[SC_REBOUND] )
 		speed += max(speed, 100);
 
-	return (short)cap_value(speed,10,USHRT_MAX);
+	return (unsigned short)cap_value(speed, MIN_WALK_SPEED, MAX_WALK_SPEED);
 }
 
 #ifdef RENEWAL_ASPD
@@ -6518,7 +6500,7 @@ const char* status_get_name(struct block_list *bl)
 }
 
 /**
-* Gets the class of the given bl
+* Gets the class/sprite id of the given bl
 * @param bl: Object whose class to get [PC|MOB|PET|HOM|MER|NPC|ELEM]
 * @return class or 0 if any other bl->type than noted above
 **/
@@ -7683,7 +7665,7 @@ int status_change_start(struct block_list* src, struct block_list* bl,enum sc_ty
 	// Strip skills, need to divest something or it fails.
 	case SC_STRIPWEAPON:
 		if (sd && !(flag&4)) { // Apply sc anyway if loading saved sc_data
-			int i;
+			short i;
 			opt_flag = 0; // Reuse to check success condition.
 			if(sd->bonus.unstripable_equip&EQP_WEAPON)
 				return 0;
@@ -7706,7 +7688,7 @@ int status_change_start(struct block_list* src, struct block_list* bl,enum sc_ty
 		if( val2 == 1 ) val2 = 0; // GX effect. Do not take shield off..
 		else
 		if (sd && !(flag&4)) {
-			int i;
+			short i;
 			if(sd->bonus.unstripable_equip&EQP_SHIELD)
 				return 0;
 			i = sd->equip_index[EQI_HAND_L];
@@ -7718,7 +7700,7 @@ int status_change_start(struct block_list* src, struct block_list* bl,enum sc_ty
 	break;
 	case SC_STRIPARMOR:
 		if (sd && !(flag&4)) {
-			int i;
+			short i;
 			if(sd->bonus.unstripable_equip&EQP_ARMOR)
 				return 0;
 			i = sd->equip_index[EQI_ARMOR];
@@ -7730,7 +7712,7 @@ int status_change_start(struct block_list* src, struct block_list* bl,enum sc_ty
 	break;
 	case SC_STRIPHELM:
 		if (sd && !(flag&4)) {
-			int i;
+			short i;
 			if(sd->bonus.unstripable_equip&EQP_HELM)
 				return 0;
 			i = sd->equip_index[EQI_HEAD_TOP];
@@ -7802,7 +7784,7 @@ int status_change_start(struct block_list* src, struct block_list* bl,enum sc_ty
 	break;
 	case SC__STRIPACCESSORY:
 		if( sd ) {
-			int i = -1;
+			short i = -1;
 			if( !(sd->bonus.unstripable_equip&EQP_ACC_L) ) {
 				i = sd->equip_index[EQI_ACC_L];
 				if( i >= 0 && sd->inventory_data[i] && sd->inventory_data[i]->type == IT_ARMOR )
@@ -8215,7 +8197,6 @@ int status_change_start(struct block_list* src, struct block_list* bl,enum sc_ty
 			case SC_MARIONETTE:
 			case SC_MARIONETTE2:
 			case SC_NOCHAT:
-			case SC_CHANGE: // Otherwise your Hp/Sp would get refilled while still within effect of the last invocation.
 			case SC_ABUNDANCE:
 			case SC_FEAR:
 			case SC_TOXIN:
@@ -8933,7 +8914,7 @@ int status_change_start(struct block_list* src, struct block_list* bl,enum sc_ty
 			val2 = 5*val1; // def increase
 			break;
 		case SC_IMPOSITIO:
-			val2 = 5*val1; // Atk increase
+			val2 = 5*val1; // Watk increase
 			break;
 		case SC_MELTDOWN:
 			val2 = 100*val1; // Chance to break weapon
@@ -9455,10 +9436,13 @@ int status_change_start(struct block_list* src, struct block_list* bl,enum sc_ty
 			tick_time = 10000; // [GodLesZ] tick time
 			break;
 		case SC_EXEEDBREAK:
-			val1 *= 100; // 100 * skill_lv
-			if( sd && sd->inventory_data[sd->equip_index[EQI_HAND_R]] ) {
-				val1 += (sd->inventory_data[sd->equip_index[EQI_HAND_R]]->weight/10 * sd->inventory_data[sd->equip_index[EQI_HAND_R]]->wlv * status_get_lv(bl) / 100);
-				val1 += 10 * sd->status.job_level;
+			{
+				short idx = -1;
+				val1 *= 100; // 100 * skill_lv
+				if( sd && (idx = sd->equip_index[EQI_HAND_R]) >= 0 && sd->inventory_data[idx] ) {
+					val1 += (sd->inventory_data[idx]->weight/10 * sd->inventory_data[idx]->wlv * status_get_lv(bl) / 100);
+					val1 += 10 * sd->status.job_level;
+				}
 			}
 			break;
 		case SC_PRESTIGE:
@@ -9476,8 +9460,8 @@ int status_change_start(struct block_list* src, struct block_list* bl,enum sc_ty
 			break;
 		case SC_INSPIRATION:
 			if( sd ) {
-				val2 = status_get_lv(bl) / 10 + (sd?sd->status.job_level:50) / 5; // All stat bonus
-				val3 = (sd?sd->status.job_level:50);
+				val2 = (sd?sd->status.job_level:50);
+				val3 = status_get_lv(bl) / 10 + val2 / 5; //All stat bonus
 			}
 			val4 = tick / 5000;
 			tick_time = 5000; // [GodLesZ] tick time
@@ -9501,8 +9485,10 @@ int status_change_start(struct block_list* src, struct block_list* bl,enum sc_ty
 			{ // Take note there is no def increase as skill desc says. [malufett]
 				struct block_list * src2;
 				val3 = status->agi * val1 / 60; // ASPD increase: [(Target AGI x Skill Level) / 60] %
-				if( (src2 = map_id2bl(val2)) )
+				if( (src2 = map_id2bl(val2)) ) {
+					val2 = (status_get_dex(src2) / 4 + status_get_str(src2) / 2) * val1 / 5; // ATK increase: ATK [{(Caster DEX / 4) + (Caster STR / 2)} x Skill Level / 5]
 					val4 = ( 200/status_get_int(src2) ) * val1; // MDEF decrease: MDEF [(200 / Caster INT) x Skill Level]
+				}
 				if( val4 < 0 )
 					val4 = 0;
 			}
@@ -9937,7 +9923,7 @@ int status_change_start(struct block_list* src, struct block_list* bl,enum sc_ty
 		case SC_STONE:
 		case SC_DEEPSLEEP:
 			if (sd && pc_issit(sd)) // Avoid sprite sync problems.
-				pc_setstand(sd);
+				pc_setstand(sd, true);
 		case SC_TRICKDEAD:
 			status_change_end(bl, SC_DANCING, INVALID_TIMER);
 			// Cancel cast when get status [LuzZza]
@@ -10230,9 +10216,6 @@ int status_change_start(struct block_list* src, struct block_list* bl,enum sc_ty
 			}
 			sce->val2 = 5 * status->max_hp / 100;
 			break;
-		case SC_CHANGE:
-			status_percent_heal(bl, 100, 100);
-			break;
 		case SC_RUN:
 			{
 				struct unit_data *ud = unit_bl2ud(bl);
@@ -10780,7 +10763,7 @@ int status_change_end_(struct block_list* bl, enum sc_type type, int tid, const
 
 		/* 3rd Stuff */
 		case SC_MILLENNIUMSHIELD:
-			clif_millenniumshield(sd,0);
+			clif_millenniumshield(bl, 0);
 			break;
 		case SC_HALLUCINATIONWALK:
 			sc_start(bl,bl,SC_HALLUCINATIONWALK_POSTDELAY,100,sce->val1,skill_get_time2(GC_HALLUCINATIONWALK,sce->val1));
@@ -10790,7 +10773,7 @@ int status_change_end_(struct block_list* bl, enum sc_type type, int tid, const
 				struct block_list* src = map_id2bl(sce->val2);
 				if( tid == -1 || !src)
 					break; // Terminated by Damage
-				status_fix_damage(src,bl,400*sce->val1,clif_damage(bl,bl,gettick(),0,0,400*sce->val1,0,0,0));
+				status_fix_damage(src,bl,400*sce->val1,clif_damage(bl,bl,gettick(),0,0,400*sce->val1,0,DMG_NORMAL,0));
 			}
 			break;
 		case SC_WUGDASH:
@@ -10824,8 +10807,7 @@ int status_change_end_(struct block_list* bl, enum sc_type type, int tid, const
 			sc_start(bl, bl,SC_SITDOWN_FORCE,100,sce->val1,skill_get_time2(WM_SATURDAY_NIGHT_FEVER,sce->val1));
 			break;
 		case SC_SITDOWN_FORCE:
-			if( sd && pc_issit(sd) ) {
-				pc_setstand(sd);
+			if( sd && pc_issit(sd) && pc_setstand(sd, false) ) {
 				clif_standing(bl);
 			}
 			break;
@@ -10884,8 +10866,7 @@ int status_change_end_(struct block_list* bl, enum sc_type type, int tid, const
 			status_change_end(bl,SC_TEARGAS_SOB,INVALID_TIMER);
 			break;
 		case SC_BANANA_BOMB_SITDOWN:
-			if( sd && pc_issit(sd) ) {
-				pc_setstand(sd);
+			if( sd && pc_issit(sd) && pc_setstand(sd, false) ) {
 				skill_sit(sd,0);
 				clif_standing(bl);
 			}
@@ -11554,7 +11535,7 @@ int status_change_timer(int tid, unsigned int tick, int id, intptr_t data)
 	case SC_PYREXIA:
 		if( --(sce->val4) >= 0 ) {
 			map_freeblock_lock();
-			clif_damage(bl,bl,tick,status_get_amotion(bl),status_get_dmotion(bl)+500,100,0,0,0);
+			clif_damage(bl,bl,tick,status_get_amotion(bl),status_get_dmotion(bl)+500,100,0,DMG_NORMAL,0);
 			status_fix_damage(NULL,bl,100,0);
 			if( sc->data[type] ) {
 				sc_timer_next(3000+tick,status_change_timer,bl->id,data);
@@ -11570,7 +11551,7 @@ int status_change_timer(int tid, unsigned int tick, int id, intptr_t data)
 			damage += status->vit * (sce->val1 - 3);
 			unit_skillcastcancel(bl,2);
 			map_freeblock_lock();
-			status_damage(bl, bl, damage, 0, clif_damage(bl,bl,tick,status_get_amotion(bl),status_get_dmotion(bl)+500,damage,1,0,0), 1);
+			status_damage(bl, bl, damage, 0, clif_damage(bl,bl,tick,status_get_amotion(bl),status_get_dmotion(bl)+500,damage,1,DMG_NORMAL,0), 1);
 			if( sc->data[type] ) {
 				sc_timer_next(1000 + tick, status_change_timer, bl->id, data );
 			}
@@ -11627,7 +11608,7 @@ int status_change_timer(int tid, unsigned int tick, int id, intptr_t data)
 	case SC_TOXIN:
 		if( --(sce->val4) >= 0 ) { // Damage is every 10 seconds including 3%sp drain.
 			map_freeblock_lock();
-			clif_damage(bl,bl,tick,status_get_amotion(bl),1,1,0,0,0);
+			clif_damage(bl,bl,tick,status_get_amotion(bl),1,1,0,DMG_NORMAL,0);
 			status_damage(NULL, bl, 1, status->max_sp * 3 / 100, 0, 0); // Cancel dmg only if cancelable
 			if( sc->data[type] ) {
 				sc_timer_next(10000 + tick, status_change_timer, bl->id, data );
@@ -11677,7 +11658,7 @@ int status_change_timer(int tid, unsigned int tick, int id, intptr_t data)
 			int damage = 1000 + 3 * status_get_max_hp(bl) / 100; // Deals fixed (1000 + 3%*MaxHP)
 
 			map_freeblock_lock();
-			clif_damage(bl,bl,tick,0,0,damage,1,9,0); // Damage is like endure effect with no walk delay
+			clif_damage(bl,bl,tick,0,0,damage,1,DMG_MULTI_HIT_ENDURE,0); // Damage is like endure effect with no walk delay
 			status_damage(src, bl, damage, 0, 0, 1);
 			if( sc->data[type]) { // Target still lives. [LimitLine]
 				sc_timer_next(2000 + tick, status_change_timer, bl->id, data);
@@ -11781,7 +11762,7 @@ int status_change_timer(int tid, unsigned int tick, int id, intptr_t data)
 				break;
 			map_freeblock_lock();
 			damage =  200 + 100 * sce->val1 + status_get_int(src);
-			status_damage(src, bl, damage, 0, clif_damage(bl,bl,tick,status->amotion,status->dmotion+200,damage,1,0,0), 0);
+			status_damage(src, bl, damage, 0, clif_damage(bl,bl,tick,status->amotion,status->dmotion+200,damage,1,DMG_NORMAL,0), 0);
 			unit_skillcastcancel(bl,1);
 			if ( sc->data[type] ) {
 				sc_timer_next(1000 + tick, status_change_timer, bl->id, data);
@@ -11888,7 +11869,7 @@ int status_change_timer(int tid, unsigned int tick, int id, intptr_t data)
 			int damage = status->max_hp / 100; // Suggestion 1% each second
 			if( damage >= status->hp ) damage = status->hp - 1; // Do not kill, just keep you with 1 hp minimum
 			map_freeblock_lock();
-			status_fix_damage(NULL,bl,damage,clif_damage(bl,bl,tick,0,0,damage,0,0,0));
+			status_fix_damage(NULL,bl,damage,clif_damage(bl,bl,tick,0,0,damage,0,DMG_NORMAL,0));
 			if( sc->data[type] ) {
 				sc_timer_next(1000 + tick, status_change_timer, bl->id, data);
 			}
@@ -11972,7 +11953,7 @@ int status_change_timer(int tid, unsigned int tick, int id, intptr_t data)
 			int damage = sce->val2;
 
 			map_freeblock_lock();
-			clif_damage(bl, bl, tick, 0, 0, damage, 1, 9, 0);
+			clif_damage(bl, bl, tick, 0, 0, damage, 1, DMG_MULTI_HIT_ENDURE, 0);
 			status_damage(src, bl, damage,0, 0, 1);
 			if( sc->data[type] ) {
 				sc_timer_next(2000 + tick, status_change_timer, bl->id, data);
@@ -12711,47 +12692,42 @@ static bool status_readdb_refine(char* fields[], int columns, int current)
 static bool status_readdb_attrfix(const char *basedir,bool silent)
 {
 	FILE *fp;
-	char line[512], path[512],*p;
-	int entries=0;
+	char line[512], path[512];
+	int entries = 0;
 
 
 	sprintf(path, "%s/attr_fix.txt", basedir);
-	fp=fopen(path,"r");
-	if(fp==NULL) {
-		if(silent==0) ShowError("can't read %s\n", path);
+	fp = fopen(path,"r");
+	if (fp == NULL) {
+		if (silent==0)
+			ShowError("Can't read %s\n", path);
 		return 1;
 	}
-	while(fgets(line, sizeof(line), fp))
-	{
-		char *split[10];
-		int lv,n,i,j;
-		if(line[0]=='/' && line[1]=='/')
-			continue;
-		for(j=0,p=line;j<3 && p;j++) {
-			split[j]=p;
-			p=strchr(p,',');
-			if(p) *p++=0;
-		}
-		if( j < 2 )
+	while (fgets(line, sizeof(line), fp)) {
+		int lv, i, j;
+		if (line[0] == '/' && line[1] == '/')
 			continue;
 
-		lv=atoi(split[0]);
-		n=atoi(split[1]);
+		lv = atoi(line);
+		if (!CHK_ELEMENT_LEVEL(lv))
+			continue;
 
-		for(i=0;i<n && i<ELE_ALL;) {
-			if( !fgets(line, sizeof(line), fp) )
+		for (i = 0; i < ELE_ALL;) {
+			char *p;
+			if (!fgets(line, sizeof(line), fp))
 				break;
-			if(line[0]=='/' && line[1]=='/')
+			if (line[0]=='/' && line[1]=='/')
 				continue;
 
-			for(j=0,p=line;j<n && j<ELE_ALL && p;j++) {
-				while(*p>0 && *p==32) //skipping newline and space (32=' ')
+			for (j = 0, p = line; j < ELE_ALL && p; j++) {
+				while (*p > 0 && *p == 32) //skipping newline and space (32=' ')
 					p++;
-				attr_fix_table[lv-1][i][j]=atoi(p);
-				if(battle_config.attr_recover == 0 && attr_fix_table[lv-1][i][j] < 0)
+				attr_fix_table[lv-1][i][j] = atoi(p);
+				if (battle_config.attr_recover == 0 && attr_fix_table[lv-1][i][j] < 0)
 					attr_fix_table[lv-1][i][j] = 0;
-				p=strchr(p,',');
-				if(p) *p++=0;
+				p = strchr(p,',');
+				if(p)
+					*p++=0;
 			}
 
 			i++;

+ 3 - 3
src/map/status.h

@@ -1535,7 +1535,7 @@ enum e_joint_break
 	BREAK_FLAGS	= BREAK_ANKLE | BREAK_WRIST | BREAK_KNEE | BREAK_SHOULDER | BREAK_WAIST | BREAK_NECK,
 };
 
-extern int current_equip_item_index;
+extern short current_equip_item_index;
 extern int current_equip_card_id;
 
 //Mode definitions to clear up code reading. [Skotlex]
@@ -1556,9 +1556,9 @@ enum e_mode {
 	MD_CHANGETARGET_CHASE	= 0x002000,
 	MD_TARGETWEAK			= 0x004000,
 	MD_RANDOMTARGET			= 0x008000,
-	MD_IGNOREMELEE			= 0x010000,
+	MD_IGNOREMELEE			= 0x010000, //! TODO: Unused yet
 	MD_IGNOREMAGIC			= 0x020000,
-	MD_IGNORERANGED			= 0x040000,
+	MD_IGNORERANGED			= 0x040000, //! TODO: Unused yet
 	MD_MVP					= 0x080000,
 	MD_IGNOREMISC			= 0x100000,
 	MD_KNOCKBACK_IMMUNE		= 0x200000,

+ 57 - 36
src/map/unit.c

@@ -52,7 +52,7 @@ const short diry[8]={1,1,0,-1,-1,-1,0,1}; ///lookup to know where will move to y
 //early declaration
 static int unit_attack_timer(int tid, unsigned int tick, int id, intptr_t data);
 static int unit_walktoxy_timer(int tid, unsigned int tick, int id, intptr_t data);
-
+int unit_unattackable(struct block_list *bl);
 /**
  * Get the unit_data related to the bl
  * @param bl : Object to get the unit_data from
@@ -204,8 +204,19 @@ int unit_teleport_timer(int tid, unsigned int tick, int id, intptr_t data)
  */
 int unit_check_start_teleport_timer(struct block_list *sbl)
 {
-	TBL_PC *msd = unit_get_master(sbl);
+	TBL_PC *msd=NULL;
 	int max_dist = 0;
+	switch(sbl->type) {
+		case BL_HOM:	
+		case BL_ELEM:	
+		case BL_PET:	
+		case BL_MER:	
+			msd = unit_get_master(sbl);
+			break;
+		default:
+			return 0;
+	}
+	
 	switch(sbl->type) {
 		case BL_HOM:	max_dist = AREA_SIZE;			break;
 		case BL_ELEM:	max_dist = MAX_ELEDISTANCE;		break;
@@ -213,7 +224,7 @@ int unit_check_start_teleport_timer(struct block_list *sbl)
 		case BL_MER:	max_dist = MAX_MER_DISTANCE;	break;
 	}
 	// If there is a master and it's a valid type
-	if(msd && (msd->bl.type&BL_PC) && max_dist) { ///TODO the bl.type is an hotfix please dig it to remove it
+	if(msd && max_dist) {
 		int *msd_tid = unit_get_masterteleport_timer(sbl);
 		if(msd_tid == NULL) return 0;
 		if (!check_distance_bl(&msd->bl, sbl, max_dist)) {
@@ -244,23 +255,16 @@ static int unit_walktoxy_timer(int tid, unsigned int tick, int id, intptr_t data
 	uint8 dir;
 	struct block_list *bl;
 	struct unit_data *ud;
-	TBL_PC *sd;
-	TBL_MOB *md;
-	TBL_MER *mrd;
-	TBL_ELEM *ed;
-	TBL_PET *pd;
-	TBL_HOM *hd;
-
+	TBL_PC *sd=NULL;
+	TBL_MOB *md=NULL;
 
 	bl = map_id2bl(id);
 	if(bl == NULL)
 		return 0;
-	sd = BL_CAST(BL_PC, bl);
-	md = BL_CAST(BL_MOB, bl);
-	mrd = BL_CAST(BL_MER, bl);
-	ed = BL_CAST(BL_ELEM, bl);
-	pd = BL_CAST(BL_PET, bl);
-	hd = BL_CAST(BL_HOM, bl);
+	switch(bl->type){ //svoid useless cast, we can only be 1 type
+		case BL_PC: sd = BL_CAST(BL_PC, bl); break;
+		case BL_MOB: md = BL_CAST(BL_MOB, bl); break;
+	}
 	ud = unit_bl2ud(bl);
 
 	if(ud == NULL) return 0;
@@ -305,7 +309,8 @@ static int unit_walktoxy_timer(int tid, unsigned int tick, int id, intptr_t data
 	map_foreachinmovearea(clif_insight, bl, AREA_SIZE, -dx, -dy, sd?BL_ALL:BL_PC, bl);
 	ud->walktimer = INVALID_TIMER;
 
-	if(sd) {
+	switch(bl->type){
+	case BL_PC: {
 		if( sd->touching_id )
 			npc_touchnext_areanpc(sd,false);
 		if(map_getcell(bl->m,x,y,CELL_CHKNPC)) {
@@ -314,14 +319,17 @@ static int unit_walktoxy_timer(int tid, unsigned int tick, int id, intptr_t data
 				return 0;
 		} else
 			sd->areanpc_id=0;
-
+		/* WIP disable [Lighta], currently unsuported 
+		 * this was meant to start the timer if the player move but not his slave...
 		if(sd->md) unit_check_start_teleport_timer(&sd->md->bl);
 		if(sd->ed) unit_check_start_teleport_timer(&sd->ed->bl);
 		if(sd->hd) unit_check_start_teleport_timer(&sd->hd->bl);
 		if(sd->pd) unit_check_start_teleport_timer(&sd->pd->bl);
-		
+		*/
 		pc_cell_basilica(sd);
-	} else if (md) {
+	}
+	break;
+	case BL_MOB: {
 		if( map_getcell(bl->m,x,y,CELL_CHKNPC) ) {
 			if( npc_touch_areanpc2(md) )
 				return 0; // Warped
@@ -342,10 +350,14 @@ static int unit_walktoxy_timer(int tid, unsigned int tick, int id, intptr_t data
 			clif_move(ud);
 		}
 	}
-	else if (hd) unit_check_start_teleport_timer(&hd->bl);
-	else if (ed) unit_check_start_teleport_timer(&ed->bl);
-	else if (pd) unit_check_start_teleport_timer(&pd->bl);
-	else if (mrd) unit_check_start_teleport_timer(&mrd->bl);
+	break;
+	case BL_HOM: 
+	case BL_ELEM:
+	case BL_PET:
+	case BL_MER:
+		unit_check_start_teleport_timer(bl);
+		break;
+	}
 
 	if(tid == INVALID_TIMER) // A directly invoked timer is from battle_stop_walking, therefore the rest is irrelevant.
 		return 0;
@@ -474,11 +486,14 @@ int unit_walktoxy( struct block_list *bl, short x, short y, int flag)
 	if( (battle_config.max_walk_path < wpd.path_len) && (bl->type != BL_NPC) )
 		return 0;
 
-	if (flag&4 && DIFF_TICK(ud->canmove_tick, gettick()) > 0 &&
-		DIFF_TICK(ud->canmove_tick, gettick()) < 2000)
-	{	// Delay walking command. [Skotlex]
-		add_timer(ud->canmove_tick+1, unit_delay_walktoxy_timer, bl->id, (x<<16)|(y&0xFFFF));
-		return 1;
+	if (flag&4){
+		unit_unattackable(bl);
+		unit_stop_attack(bl);
+		if(DIFF_TICK(ud->canmove_tick, gettick()) > 0 && DIFF_TICK(ud->canmove_tick, gettick()) < 2000)
+		{	// Delay walking command. [Skotlex]
+			add_timer(ud->canmove_tick+1, unit_delay_walktoxy_timer, bl->id, (x<<16)|(y&0xFFFF));
+			return 1;
+		}
 	}
 
 	if(!(flag&2) && (!(status_get_mode(bl)&MD_CANMOVE) || !unit_can_move(bl)))
@@ -504,7 +519,7 @@ int unit_walktoxy( struct block_list *bl, short x, short y, int flag)
 		delete_timer( ud->attacktimer, unit_attack_timer );
 		ud->attacktimer = INVALID_TIMER;
 	}
-
+	
 	return unit_walktoxy_sub(bl);
 }
 
@@ -1142,6 +1157,7 @@ int unit_can_move(struct block_list *bl) {
 	))
 		return 0; // Can't move
 
+	// Status changes that block movement
 	if (sc) {
 		if( sc->cant.move // status placed here are ones that cannot be cached by sc->cant.move for they depend on other conditions other than their availability
 			|| (sc->data[SC_FEAR] && sc->data[SC_FEAR]->val2 > 0)
@@ -1268,7 +1284,9 @@ int unit_skilluse_id2(struct block_list *src, int target_id, uint16 skill_id, ui
 		sc = NULL; // Unneeded
 
 	// temp: used to signal combo-skills right now.
-	if (sc && sc->data[SC_COMBO] && (sc->data[SC_COMBO]->val1 == skill_id ||
+	if (sc && sc->data[SC_COMBO] &&
+		skill_is_combo(skill_id) &&
+		(sc->data[SC_COMBO]->val1 == skill_id ||
 		(sd?skill_check_condition_castbegin(sd,skill_id,skill_lv):0) ))
 	{
 		if (sc->data[SC_COMBO]->val2)
@@ -1814,6 +1832,7 @@ int unit_unattackable(struct block_list *bl)
 	struct unit_data *ud = unit_bl2ud(bl);
 	if (ud) {
 		ud->state.attack_continue = 0;
+		ud->target_to = 0;
 		unit_set_target(ud, 0);
 	}
 
@@ -1854,6 +1873,10 @@ int unit_attack(struct block_list *src,int target_id,int continuous)
 			unit_stop_attack(src);
 			return 0;
 		}
+		if( !pc_can_attack(sd, target_id) ) {
+			unit_stop_attack(src);
+			return 0;
+		}
 	}
 	if( battle_check_target(src,target,BCT_ENEMY) <= 0 || !status_check_skilluse(src, target, 0, 0) ) {
 		unit_unattackable(src);
@@ -2066,7 +2089,7 @@ static int unit_attack_timer_sub(struct block_list* src, int tid, unsigned int t
 #ifdef OFFICIAL_WALKPATH
 	   || !path_search_long(NULL, src->m, src->x, src->y, target->x, target->y, CELL_CHKWALL)
 #endif
-	   )
+	   || (sd && !pc_can_attack(sd, target->id)) )
 		return 0; // Can't attack under these conditions
 
 	if (sd && &sd->sc && sd->sc.count && sd->sc.data[SC_HEAT_BARREL_AFTER])
@@ -2158,7 +2181,7 @@ static int unit_attack_timer_sub(struct block_list* src, int tid, unsigned int t
 	}
 
 	if(ud->state.attack_continue) {
-		if( src->type == BL_PC )
+		if (src->type == BL_PC && battle_config.idletime_option&IDLE_ATTACK)
 			((TBL_PC*)src)->idletime = last_tick;
 		ud->attacktimer = add_timer(ud->attackabletime,unit_attack_timer,src->id,0);
 	}
@@ -2390,7 +2413,6 @@ int unit_remove_map_(struct block_list *bl, clr_type clrtype, const char* file,
 				chat_leavechat(sd,0);
 			if(sd->trade_partner)
 				trade_tradecancel(sd);
-			buyingstore_close(sd);
 			searchstore_close(sd);
 			if (sd->menuskill_id != AL_TELEPORT) { //bugreport:8027
 				if (sd->state.storage_flag == 1)
@@ -2428,8 +2450,7 @@ int unit_remove_map_(struct block_list *bl, clr_type clrtype, const char* file,
 			if(sd->duel_group > 0)
 				duel_leave(sd->duel_group, sd);
 
-			if(pc_issit(sd)) {
-				pc_setstand(sd);
+			if(pc_issit(sd) && pc_setstand(sd, false)) {
 				skill_sit(sd,0);
 			}
 			party_send_dot_remove(sd);// minimap dot fix [Kevin]

+ 77 - 47
src/map/vending.c

@@ -39,7 +39,7 @@ struct s_autotrade {
 	int m;
 	uint16 x,
 		y;
-	unsigned char sex;
+	unsigned char sex, dir, head_dir, sit;
 	char title[MESSAGE_SIZE];
 	uint16 count;
 	struct s_autotrade_entry **entries;
@@ -51,7 +51,7 @@ static DBMap *vending_db; ///Db holder the vender : charid -> map_session_data
 
 //Autotrader
 static struct s_autotrade **autotraders; ///Autotraders Storage
-static uint16 autotrader_count; ///Autotrader count
+static uint16 autotrader_count, autotrader_loaded_count; ///Autotrader count
 static void do_final_vending_autotrade(void);
 
 /**
@@ -85,6 +85,7 @@ void vending_closevending(struct map_session_data* sd)
 		}
 		
 		sd->state.vending = false;
+		sd->vender_id = 0;
 		clif_closevendingboard(&sd->bl, 0);
 		idb_remove(vending_db, sd->status.char_id);
 	}
@@ -297,29 +298,30 @@ void vending_purchasereq(struct map_session_data* sd, int aid, int uid, const ui
  *	data := {<index>.w <amount>.w <value>.l}[count]
  * @param count : number of different items
  */
-bool vending_openvending(struct map_session_data* sd, const char* message, const uint8* data, int count) {
+char vending_openvending(struct map_session_data* sd, const char* message, const uint8* data, int count) {
 	int i, j;
 	int vending_skill_lvl;
 	char message_sql[MESSAGE_SIZE*2];
 	
 	nullpo_retr(false,sd);
 
-	if ( pc_isdead(sd) || !sd->state.prevend || pc_istrading(sd))
-		return false; // can't open vendings lying dead || didn't use via the skill (wpe/hack) || can't have 2 shops at once
+	if ( pc_isdead(sd) || !sd->state.prevend || pc_istrading(sd)) {
+		return 1; // can't open vendings lying dead || didn't use via the skill (wpe/hack) || can't have 2 shops at once
+	}
 
 	vending_skill_lvl = pc_checkskill(sd, MC_VENDING);
 	
 	// skill level and cart check
 	if( !vending_skill_lvl || !pc_iscarton(sd) ) {
 		clif_skill_fail(sd, MC_VENDING, USESKILL_FAIL_LEVEL, 0);
-		return false;
+		return 2;
 	}
 
 	// check number of items in shop
 	if( count < 1 || count > MAX_VENDING || count > 2 + vending_skill_lvl )
 	{	// invalid item count
 		clif_skill_fail(sd, MC_VENDING, USESKILL_FAIL_LEVEL, 0);
-		return false;
+		return 3;
 	}
 
 	if (save_settings&2) // Avoid invalid data from saving
@@ -356,7 +358,7 @@ bool vending_openvending(struct map_session_data* sd, const char* message, const
 			sprintf(msg, msg_txt(sd, 733), idb->jname);
 			clif_displaymessage(sd->fd, msg);
 			clif_skill_fail(sd, MC_VENDING, USESKILL_FAIL_LEVEL, 0);
-			return false;
+			return 4;
 		}
 
 		i++; // item successfully added
@@ -367,7 +369,7 @@ bool vending_openvending(struct map_session_data* sd, const char* message, const
 
 	if( i == 0 ) { // no valid item found
 		clif_skill_fail(sd, MC_VENDING, USESKILL_FAIL_LEVEL, 0); // custom reply packet
-		return false;
+		return 5;
 	}
 	sd->state.prevend = 0;
 	sd->state.vending = true;
@@ -377,7 +379,9 @@ bool vending_openvending(struct map_session_data* sd, const char* message, const
 	
 	Sql_EscapeString( mmysql_handle, message_sql, sd->message );
 
-	if( Sql_Query( mmysql_handle, "INSERT INTO `%s`(`id`,`account_id`,`char_id`,`sex`,`map`,`x`,`y`,`title`,`autotrade`) VALUES( %d, %d, %d, '%c', '%s', %d, %d, '%s', %d );", vendings_db, sd->vender_id, sd->status.account_id, sd->status.char_id, sd->status.sex == 0 ? 'F' : 'M', map[sd->bl.m].name, sd->bl.x, sd->bl.y, message_sql, sd->state.autotrade ) != SQL_SUCCESS ){
+	if( Sql_Query( mmysql_handle, "INSERT INTO `%s`(`id`,`account_id`,`char_id`,`sex`,`map`,`x`,`y`,`title`,`autotrade`, `body_direction`, `head_direction`, `sit`) "
+		"VALUES( %d, %d, %d, '%c', '%s', %d, %d, '%s', %d, '%d', '%d', '%d' );",
+		vendings_db, sd->vender_id, sd->status.account_id, sd->status.char_id, sd->status.sex == 0 ? 'F' : 'M', map[sd->bl.m].name, sd->bl.x, sd->bl.y, message_sql, sd->state.autotrade, sd->ud.dir, sd->head_dir, pc_issit(sd) ) != SQL_SUCCESS ){
 		Sql_ShowDebug(mmysql_handle);
 	}
 
@@ -392,7 +396,7 @@ bool vending_openvending(struct map_session_data* sd, const char* message, const
 
 	idb_put(vending_db, sd->status.char_id, sd);
 
-	return true;
+	return 0;
 }
 
 /**
@@ -475,14 +479,17 @@ bool vending_searchall(struct map_session_data* sd, const struct s_search_store_
 	return true;
 }
 
-/** Open vending for Autotrader
+/**
+* Open vending for Autotrader
 * @param sd Player as autotrader
 */
 void vending_reopen( struct map_session_data* sd ){
+	nullpo_retv(sd);
+
 	// Ready to open vending for this char
-	if ( sd && autotrader_count > 0 && autotraders){
+	if ( autotrader_count > 0 && autotraders){
 		uint16 i;
-		uint8 *data, *p;
+		uint8 *data, *p, fail = 0;
 		uint16 j, count;
 
 		ARR_FIND(0,autotrader_count,i,autotraders[i] && autotraders[i]->char_id == sd->status.char_id);
@@ -508,7 +515,7 @@ void vending_reopen( struct map_session_data* sd ){
 			}
 
 			*index = entry->index + 2;
-			*amount = itemdb_isstackable(sd->status.cart[entry->index].id) ? entry->amount : 1;
+			*amount = itemdb_isstackable(sd->status.cart[entry->index].nameid) ? entry->amount : 1;
 			*value = entry->price;
 
 			p += 8;
@@ -517,26 +524,37 @@ void vending_reopen( struct map_session_data* sd ){
 		// Set him into a hacked prevend state
 		sd->state.prevend = 1;
 
+		// Make sure abort all NPCs
+		npc_event_dequeue(sd);
+		pc_cleareventtimer(sd);
+
 		// Open the vending again
-		if( vending_openvending(sd, autotraders[i]->title, data, count) ){
+		if( (fail = vending_openvending(sd, autotraders[i]->title, data, count)) == 0 ){
 			// Set him to autotrade
-			if (Sql_Query( mmysql_handle, "UPDATE `%s` SET `autotrade` = 1 WHERE `id` = %d;",
-				vendings_db, sd->vender_id ) != SQL_SUCCESS )
+			if (Sql_Query( mmysql_handle, "UPDATE `%s` SET `autotrade` = 1, `body_direction` = '%d', `head_direction` = '%d', `sit` = '%d' "
+				"WHERE `id` = %d;",
+				vendings_db, autotraders[i]->dir, autotraders[i]->head_dir, autotraders[i]->sit, sd->vender_id ) != SQL_SUCCESS )
 			{
 				Sql_ShowDebug( mmysql_handle );
 			}
 
-			// Make him look perfect
-			unit_setdir(&sd->bl,battle_config.feature_autotrade_direction);
-
-			if( battle_config.feature_autotrade_sit )
+			// Make vendor look perfect
+			pc_setdir(sd, autotraders[i]->dir, autotraders[i]->head_dir);
+			clif_changed_dir(&sd->bl, AREA_WOS);
+			if( autotraders[i]->sit ) {
 				pc_setsit(sd);
+				skill_sit(sd, 1);
+				clif_sitting(&sd->bl);
+			}
+
+			// Immediate save
+			chrif_save(sd, 3);
 
-			ShowInfo("Loaded autotrade vending data for '"CL_WHITE"%s"CL_RESET"' with '"CL_WHITE"%d"CL_RESET"' items at "CL_WHITE"%s (%d,%d)"CL_RESET"\n",
-				sd->status.name,count,mapindex_id2name(sd->mapindex),sd->bl.x,sd->bl.y);
+			ShowInfo("Loaded vending for '"CL_WHITE"%s"CL_RESET"' with '"CL_WHITE"%d"CL_RESET"' items at "CL_WHITE"%s (%d,%d)"CL_RESET"\n",
+				sd->status.name, count, mapindex_id2name(sd->mapindex), sd->bl.x, sd->bl.y);
 		}else{
 			// Failed to open the vending, set him offline
-			ShowWarning("Failed to load autotrade vending data for '"CL_WHITE"%s"CL_RESET"' with '"CL_WHITE"%d"CL_RESET"' items\n", sd->status.name, count );
+			ShowError("Failed (%d) to load autotrade vending data for '"CL_WHITE"%s"CL_RESET"' with '"CL_WHITE"%d"CL_RESET"' items\n", fail, sd->status.name, count );
 
 			map_quit( sd );
 		}
@@ -544,7 +562,7 @@ void vending_reopen( struct map_session_data* sd ){
 		aFree(data);
 
 		//If the last autotrade is loaded, clear autotraders [Cydh]
-		if (i+1 >= autotrader_count)
+		if (++autotrader_loaded_count >= autotrader_count)
 			do_final_vending_autotrade();
 	}
 }
@@ -555,21 +573,22 @@ void vending_reopen( struct map_session_data* sd ){
 void do_init_vending_autotrade( void ) {
 	if (battle_config.feature_autotrade) {
 		uint16 i, items = 0;
-		autotrader_count = 0;
+		autotrader_count = autotrader_loaded_count = 0;
 
 		// Get autotrader from table. `map`, `x`, and `y`, aren't used here
 		// Just read player that has data at vending_items [Cydh]
 		if (Sql_Query(mmysql_handle,
-			"SELECT `id`, `account_id`, `char_id`, `sex`, `title` "
+			"SELECT `id`, `account_id`, `char_id`, `sex`, `title`, `body_direction`, `head_direction`, `sit` "
 			"FROM `%s` "
-			"WHERE `autotrade` = 1 AND (SELECT COUNT(`vending_id`) FROM `%s` WHERE `vending_id` = `id`) > 0;",
+			"WHERE `autotrade` = 1 AND (SELECT COUNT(`vending_id`) FROM `%s` WHERE `vending_id` = `id`) > 0 "
+			"ORDER BY `id`;",
 			vendings_db, vending_items_db ) != SQL_SUCCESS )
 		{
 			Sql_ShowDebug(mmysql_handle);
 			return;
 		}
 
-		if( (autotrader_count = (uint32)Sql_NumRows(mmysql_handle)) > 0 ){
+		if( (autotrader_count = (uint16)Sql_NumRows(mmysql_handle)) > 0 ){
 			// Init autotraders
 			CREATE(autotraders, struct s_autotrade *, autotrader_count);
 
@@ -584,23 +603,34 @@ void do_init_vending_autotrade( void ) {
 			while (SQL_SUCCESS == Sql_NextRow(mmysql_handle) && i < autotrader_count) {
 				size_t len;
 				char* data;
+				struct s_autotrade *at = NULL;
 
 				CREATE(autotraders[i], struct s_autotrade, 1);
+				at = autotraders[i];
 
-				Sql_GetData(mmysql_handle, 0, &data, NULL); autotraders[i]->vendor_id = atoi(data);
-				Sql_GetData(mmysql_handle, 1, &data, NULL); autotraders[i]->account_id = atoi(data);
-				Sql_GetData(mmysql_handle, 2, &data, NULL); autotraders[i]->char_id = atoi(data);
-				Sql_GetData(mmysql_handle, 3, &data, NULL); autotraders[i]->sex = (data[0] == 'F') ? 0 : 1;
-				Sql_GetData(mmysql_handle, 4, &data, &len); safestrncpy(autotraders[i]->title, data, min(len + 1, MESSAGE_SIZE));
-				autotraders[i]->count = 0;
+				Sql_GetData(mmysql_handle, 0, &data, NULL); at->vendor_id = atoi(data);
+				Sql_GetData(mmysql_handle, 1, &data, NULL); at->account_id = atoi(data);
+				Sql_GetData(mmysql_handle, 2, &data, NULL); at->char_id = atoi(data);
+				Sql_GetData(mmysql_handle, 3, &data, NULL); at->sex = (data[0] == 'F') ? 0 : 1;
+				Sql_GetData(mmysql_handle, 4, &data, &len); safestrncpy(at->title, data, min(len + 1, MESSAGE_SIZE));
+				Sql_GetData(mmysql_handle, 5, &data, NULL); at->dir = atoi(data);
+				Sql_GetData(mmysql_handle, 6, &data, NULL); at->head_dir = atoi(data);
+				Sql_GetData(mmysql_handle, 7, &data, NULL); at->sit = atoi(data);
+				at->count = 0;
+
+				if (battle_config.feature_autotrade_direction >= 0)
+					at->dir = battle_config.feature_autotrade_direction;
+				if (battle_config.feature_autotrade_head_direction >= 0)
+					at->head_dir = battle_config.feature_autotrade_head_direction;
+				if (battle_config.feature_autotrade_sit >= 0)
+					at->sit = battle_config.feature_autotrade_sit;
 
 				// initialize player
-				CREATE(autotraders[i]->sd, struct map_session_data, 1);
-			
-				pc_setnewpc(autotraders[i]->sd, autotraders[i]->account_id, autotraders[i]->char_id, 0, gettick(), autotraders[i]->sex, 0);
-			
-				autotraders[i]->sd->state.autotrade = 1;
-				chrif_authreq(autotraders[i]->sd, true);
+				CREATE(at->sd, struct map_session_data, 1);
+				pc_setnewpc(at->sd, at->account_id, at->char_id, 0, gettick(), at->sex, 0);
+				at->sd->state.autotrade = 1;
+				at->sd->state.monster_ignore = (battle_config.autotrade_monsterignore);
+				chrif_authreq(at->sd, true);
 				i++;
 			}
 			Sql_FreeResult(mmysql_handle);
@@ -610,21 +640,21 @@ void do_init_vending_autotrade( void ) {
 				struct s_autotrade *at = NULL;
 				uint16 j;
 
-				if (autotraders[i] == NULL)
+				if ((at = autotraders[i]) == NULL)
 					continue;
-				at = autotraders[i];
 
 				if (SQL_ERROR == Sql_Query(mmysql_handle,
 					"SELECT `cartinventory_id`, `amount`, `price` "
 					"FROM `%s` "
 					"WHERE `vending_id` = %d "
-					"ORDER BY `index` ASC;", vending_items_db, at->vendor_id ) )
+					"ORDER BY `index` ASC;",
+					vending_items_db, at->vendor_id ) )
 				{
 					Sql_ShowDebug(mmysql_handle);
 					continue;
 				}
 
-				if (!(at->count = (uint32)Sql_NumRows(mmysql_handle))) {
+				if (!(at->count = (uint16)Sql_NumRows(mmysql_handle))) {
 					map_quit(at->sd);
 					continue;
 				}
@@ -647,7 +677,7 @@ void do_init_vending_autotrade( void ) {
 				Sql_FreeResult(mmysql_handle);
 			}
 
-			ShowStatus("Done loading '"CL_WHITE"%d"CL_RESET"' autotraders with '"CL_WHITE"%d"CL_RESET"' items.\n", autotrader_count, items);
+			ShowStatus("Done loading '"CL_WHITE"%d"CL_RESET"' vending autotraders with '"CL_WHITE"%d"CL_RESET"' items.\n", autotrader_count, items);
 		}
 	}
 

+ 1 - 1
src/map/vending.h

@@ -22,7 +22,7 @@ void do_init_vending_autotrade( void );
  
 void vending_reopen( struct map_session_data* sd );
 void vending_closevending(struct map_session_data* sd);
-bool vending_openvending(struct map_session_data* sd, const char* message, const uint8* data, int count);
+char vending_openvending(struct map_session_data* sd, const char* message, const uint8* data, int count);
 void vending_vendinglistreq(struct map_session_data* sd, int id);
 void vending_purchasereq(struct map_session_data* sd, int aid, int uid, const uint8* data, int count);
 bool vending_search(struct map_session_data* sd, unsigned short nameid);

+ 9 - 3
vcproj-10/login-server_sql.vcxproj

@@ -147,6 +147,9 @@
     <ClInclude Include="..\src\login\account.h" />
     <ClInclude Include="..\src\login\ipban.h" />
     <ClInclude Include="..\src\login\login.h" />
+    <ClInclude Include="..\src\login\loginchrif.h" />
+    <ClInclude Include="..\src\login\loginclif.h" />
+    <ClInclude Include="..\src\login\logincnslif.h" />
     <ClInclude Include="..\src\login\loginlog.h" />
     <ClInclude Include="..\src\common\cbasetypes.h" />
     <ClInclude Include="..\src\common\core.h" />
@@ -178,10 +181,13 @@
     <ClCompile Include="..\src\common\mutex.c" />
     <ClCompile Include="..\src\common\raconf.c" />
     <ClCompile Include="..\src\common\thread.c" />
-    <ClCompile Include="..\src\login\account_sql.c" />
-    <ClCompile Include="..\src\login\ipban_sql.c" />
+    <ClCompile Include="..\src\login\account.c" />
+    <ClCompile Include="..\src\login\ipban.c" />
     <ClCompile Include="..\src\login\login.c" />
-    <ClCompile Include="..\src\login\loginlog_sql.c" />
+    <ClCompile Include="..\src\login\loginchrif.c" />
+    <ClCompile Include="..\src\login\loginclif.c" />
+    <ClCompile Include="..\src\login\logincnslif.c" />
+    <ClCompile Include="..\src\login\loginlog.c" />
     <ClCompile Include="..\src\common\core.c" />
     <ClCompile Include="..\src\common\db.c" />
     <ClCompile Include="..\src\common\ers.c" />

+ 23 - 5
vcproj-10/login-server_sql.vcxproj.filters

@@ -1,16 +1,25 @@
 <?xml version="1.0" encoding="utf-8"?>
 <Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
   <ItemGroup>
-    <ClCompile Include="..\src\login\account_sql.c">
+    <ClCompile Include="..\src\login\account.c">
       <Filter>login_sql</Filter>
     </ClCompile>
-    <ClCompile Include="..\src\login\ipban_sql.c">
+    <ClCompile Include="..\src\login\ipban.c">
       <Filter>login_sql</Filter>
     </ClCompile>
     <ClCompile Include="..\src\login\login.c">
       <Filter>login_sql</Filter>
     </ClCompile>
-    <ClCompile Include="..\src\login\loginlog_sql.c">
+    <ClCompile Include="..\src\login\loginchrif.c">
+      <Filter>login_sql</Filter>
+    </ClCompile>
+    <ClCompile Include="..\src\login\loginclif.c">
+      <Filter>login_sql</Filter>
+    </ClCompile>
+    <ClCompile Include="..\src\login\logincnslif.c">
+      <Filter>login_sql</Filter>
+    </ClCompile>
+    <ClCompile Include="..\src\login\loginlog.c">
       <Filter>login_sql</Filter>
     </ClCompile>
     <ClCompile Include="..\src\common\core.c">
@@ -95,10 +104,19 @@
     <ClInclude Include="..\src\login\ipban.h">
       <Filter>login_sql</Filter>
     </ClInclude>
-    <ClInclude Include="..\src\login\loginlog.h">
+    <ClInclude Include="..\src\login\login.h">
       <Filter>login_sql</Filter>
     </ClInclude>
-    <ClInclude Include="..\src\login\login.h">
+    <ClInclude Include="..\src\login\loginchrif.h">
+      <Filter>login_sql</Filter>
+    </ClInclude>
+    <ClInclude Include="..\src\login\loginclif.h">
+      <Filter>login_sql</Filter>
+    </ClInclude>
+    <ClInclude Include="..\src\login\logincnslif.h">
+      <Filter>login_sql</Filter>
+    </ClInclude>
+    <ClInclude Include="..\src\login\loginlog.h">
       <Filter>login_sql</Filter>
     </ClInclude>
     <ClInclude Include="..\src\common\cbasetypes.h">

+ 9 - 3
vcproj-12/login-server_sql.vcxproj

@@ -151,6 +151,9 @@
     <ClInclude Include="..\src\login\ipban.h" />
     <ClInclude Include="..\src\login\login.h" />
     <ClInclude Include="..\src\login\loginlog.h" />
+    <ClInclude Include="..\src\login\loginchrif.h" />
+    <ClInclude Include="..\src\login\loginclif.h" />
+    <ClInclude Include="..\src\login\logincnslif.h" />
     <ClInclude Include="..\src\common\cbasetypes.h" />
     <ClInclude Include="..\src\common\core.h" />
     <ClInclude Include="..\src\common\conf.h" />
@@ -181,10 +184,13 @@
     <ClCompile Include="..\src\common\mutex.c" />
     <ClCompile Include="..\src\common\raconf.c" />
     <ClCompile Include="..\src\common\thread.c" />
-    <ClCompile Include="..\src\login\account_sql.c" />
-    <ClCompile Include="..\src\login\ipban_sql.c" />
+    <ClCompile Include="..\src\login\account.c" />
+    <ClCompile Include="..\src\login\ipban.c" />
     <ClCompile Include="..\src\login\login.c" />
-    <ClCompile Include="..\src\login\loginlog_sql.c" />
+    <ClCompile Include="..\src\login\loginlog.c" />
+    <ClCompile Include="..\src\login\loginchrif.c" />
+    <ClCompile Include="..\src\login\loginclif.c" />
+    <ClCompile Include="..\src\login\logincnslif.c" />
     <ClCompile Include="..\src\common\core.c" />
     <ClCompile Include="..\src\common\conf.c" />
     <ClCompile Include="..\src\common\db.c" />

+ 23 - 5
vcproj-12/login-server_sql.vcxproj.filters

@@ -1,16 +1,25 @@
 <?xml version="1.0" encoding="utf-8"?>
 <Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
   <ItemGroup>
-    <ClCompile Include="..\src\login\account_sql.c">
+    <ClCompile Include="..\src\login\account.c">
       <Filter>login_sql</Filter>
     </ClCompile>
-    <ClCompile Include="..\src\login\ipban_sql.c">
+    <ClCompile Include="..\src\login\ipban.c">
       <Filter>login_sql</Filter>
     </ClCompile>
     <ClCompile Include="..\src\login\login.c">
       <Filter>login_sql</Filter>
     </ClCompile>
-    <ClCompile Include="..\src\login\loginlog_sql.c">
+    <ClCompile Include="..\src\login\loginchrif.c">
+      <Filter>login_sql</Filter>
+    </ClCompile>
+    <ClCompile Include="..\src\login\loginclif.c">
+      <Filter>login_sql</Filter>
+    </ClCompile>
+    <ClCompile Include="..\src\login\logincnslif.c">
+      <Filter>login_sql</Filter>
+    </ClCompile>
+    <ClCompile Include="..\src\login\loginlog.c">
       <Filter>login_sql</Filter>
     </ClCompile>
     <ClCompile Include="..\src\common\core.c">
@@ -93,10 +102,19 @@
     <ClInclude Include="..\src\login\ipban.h">
       <Filter>login_sql</Filter>
     </ClInclude>
-    <ClInclude Include="..\src\login\loginlog.h">
+    <ClInclude Include="..\src\login\login.h">
       <Filter>login_sql</Filter>
     </ClInclude>
-    <ClInclude Include="..\src\login\login.h">
+    <ClInclude Include="..\src\login\loginchrif.h">
+      <Filter>login_sql</Filter>
+    </ClInclude>
+    <ClInclude Include="..\src\login\loginclif.h">
+      <Filter>login_sql</Filter>
+    </ClInclude>
+    <ClInclude Include="..\src\login\logincnslif.h">
+      <Filter>login_sql</Filter>
+    </ClInclude>
+    <ClInclude Include="..\src\login\loginlog.h">
       <Filter>login_sql</Filter>
     </ClInclude>
     <ClInclude Include="..\src\common\cbasetypes.h">

+ 9 - 3
vcproj-13/login-server_sql.vcxproj

@@ -150,6 +150,9 @@
     <ClInclude Include="..\src\login\account.h" />
     <ClInclude Include="..\src\login\ipban.h" />
     <ClInclude Include="..\src\login\login.h" />
+    <ClInclude Include="..\src\login\loginchrif.h" />
+    <ClInclude Include="..\src\login\loginclif.h" />
+    <ClInclude Include="..\src\login\logincnslif.h" />
     <ClInclude Include="..\src\login\loginlog.h" />
     <ClInclude Include="..\src\common\cbasetypes.h" />
     <ClInclude Include="..\src\common\core.h" />
@@ -181,10 +184,13 @@
     <ClCompile Include="..\src\common\mutex.c" />
     <ClCompile Include="..\src\common\raconf.c" />
     <ClCompile Include="..\src\common\thread.c" />
-    <ClCompile Include="..\src\login\account_sql.c" />
-    <ClCompile Include="..\src\login\ipban_sql.c" />
+    <ClCompile Include="..\src\login\account.c" />
+    <ClCompile Include="..\src\login\ipban.c" />
     <ClCompile Include="..\src\login\login.c" />
-    <ClCompile Include="..\src\login\loginlog_sql.c" />
+    <ClCompile Include="..\src\login\loginchrif.c" />
+    <ClCompile Include="..\src\login\loginclif.c" />
+    <ClCompile Include="..\src\login\logincnslif.c" />
+    <ClCompile Include="..\src\login\loginlog.c" />
     <ClCompile Include="..\src\common\core.c" />
     <ClCompile Include="..\src\common\conf.c" />
     <ClCompile Include="..\src\common\db.c" />

+ 24 - 6
vcproj-13/login-server_sql.vcxproj.filters

@@ -1,16 +1,25 @@
 <?xml version="1.0" encoding="utf-8"?>
 <Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
   <ItemGroup>
-    <ClCompile Include="..\src\login\account_sql.c">
+    <ClCompile Include="..\src\login\account.c">
       <Filter>login_sql</Filter>
     </ClCompile>
-    <ClCompile Include="..\src\login\ipban_sql.c">
+    <ClCompile Include="..\src\login\ipban.c">
       <Filter>login_sql</Filter>
     </ClCompile>
     <ClCompile Include="..\src\login\login.c">
       <Filter>login_sql</Filter>
     </ClCompile>
-    <ClCompile Include="..\src\login\loginlog_sql.c">
+    <ClCompile Include="..\src\login\loginchrif.c">
+      <Filter>login_sql</Filter>
+    </ClCompile>
+    <ClCompile Include="..\src\login\loginclif.c">
+      <Filter>login_sql</Filter>
+    </ClCompile>
+    <ClCompile Include="..\src\login\logincnslif.c">
+      <Filter>login_sql</Filter>
+    </ClCompile>
+    <ClCompile Include="..\src\login\loginlog.c">
       <Filter>login_sql</Filter>
     </ClCompile>
     <ClCompile Include="..\src\common\core.c">
@@ -95,10 +104,19 @@
     <ClInclude Include="..\src\login\ipban.h">
       <Filter>login_sql</Filter>
     </ClInclude>
-    <ClInclude Include="..\src\login\loginlog.h">
+    <ClInclude Include="..\src\login\login.h">
       <Filter>login_sql</Filter>
     </ClInclude>
-    <ClInclude Include="..\src\login\login.h">
+    <ClInclude Include="..\src\login\loginchrif.h">
+      <Filter>login_sql</Filter>
+    </ClInclude>
+    <ClInclude Include="..\src\login\loginclif.h">
+      <Filter>login_sql</Filter>
+    </ClInclude>
+    <ClInclude Include="..\src\login\logincnslif.h">
+      <Filter>login_sql</Filter>
+    </ClInclude>
+    <ClInclude Include="..\src\login\loginlog.h">
       <Filter>login_sql</Filter>
     </ClInclude>
     <ClInclude Include="..\src\common\cbasetypes.h">
@@ -208,4 +226,4 @@
       <UniqueIdentifier>{779e8145-9bb2-4a88-9149-60586ab0bdd4}</UniqueIdentifier>
     </Filter>
   </ItemGroup>
-</Project>
+</Project>

+ 27 - 3
vcproj-9/login-server_sql.vcproj

@@ -211,7 +211,7 @@
 				>
 			</File>
 			<File
-				RelativePath="..\src\login\account_sql.c"
+				RelativePath="..\src\login\account.c"
 				>
 			</File>
 			<File
@@ -219,7 +219,7 @@
 				>
 			</File>
 			<File
-				RelativePath="..\src\login\ipban_sql.c"
+				RelativePath="..\src\login\ipban.c"
 				>
 			</File>
 			<File
@@ -230,12 +230,36 @@
 				RelativePath="..\src\login\login.h"
 				>
 			</File>
+			<File
+				RelativePath="..\src\login\loginchrif.c"
+				>
+			</File>
+			<File
+				RelativePath="..\src\login\loginchrif.h"
+				>
+			</File>
+			<File
+				RelativePath="..\src\login\loginclif.c"
+				>
+			</File>
+			<File
+				RelativePath="..\src\login\loginclif.h"
+				>
+			</File>
+			<File
+				RelativePath="..\src\login\logincnslif.c"
+				>
+			</File>
+			<File
+				RelativePath="..\src\login\logincnslif.h"
+				>
+			</File>
 			<File
 				RelativePath="..\src\login\loginlog.h"
 				>
 			</File>
 			<File
-				RelativePath="..\src\login\loginlog_sql.c"
+				RelativePath="..\src\login\loginlog.c"
 				>
 			</File>
 		</Filter>

部分文件因为文件数量过多而无法显示