Преглед изворни кода

Merge branch 'master' into hotfix/issue6912

# Conflicts:
#	conf/battle/feature.conf
#	src/map/packets.hpp
Lemongrass3110 пре 2 година
родитељ
комит
70b54081da
52 измењених фајлова са 2848 додато и 645 уклоњено
  1. 17 18
      athena-start
  2. 3 0
      conf/atcommands.yml
  3. 7 0
      conf/battle/feature.conf
  4. 2 1
      conf/char_athena.conf
  5. 1 1
      db/pre-re/instance_db.yml
  6. 21 7
      db/re/achievement_db.yml
  7. 1 1
      db/re/instance_db.yml
  8. 1 1
      db/re/item_combos.yml
  9. 91 113
      db/re/item_db_equip.yml
  10. 574 0
      db/re/item_db_usable.yml
  11. 11 0
      db/re/quest_db.yml
  12. 66 35
      db/readme.md
  13. 36 1
      doc/script_commands.txt
  14. 4 4
      function.sh
  15. 21 21
      install.sh
  16. 1 1
      npc/events/dumplingfestival.txt
  17. 27 0
      npc/mapflag/nodynamicnpc.txt
  18. 0 9
      npc/other/Global_Functions.txt
  19. 1 1
      npc/quests/quests_13_1.txt
  20. 1 1
      npc/re/cities/dewata.txt
  21. 1 1
      npc/re/instances/EclageInterior.txt
  22. 1 1
      npc/re/instances/PoringVillage.txt
  23. 140 0
      npc/re/mapflag/nodynamicnpc.txt
  24. 1217 0
      npc/re/quests/HelpMeShorty.txt
  25. 1 0
      npc/re/scripts_athena.conf
  26. 15 0
      npc/test/ci/0000_funcs.txt
  27. 35 0
      npc/test/ci/7291.txt
  28. 4 0
      src/char/char.cpp
  29. 30 0
      src/common/utilities.cpp
  30. 25 0
      src/common/utilities.hpp
  31. 34 15
      src/map/atcommand.cpp
  32. 1 0
      src/map/battle.cpp
  33. 1 0
      src/map/battle.hpp
  34. 1 2
      src/map/chrif.cpp
  35. 162 266
      src/map/clif.cpp
  36. 14 30
      src/map/clif.hpp
  37. 0 23
      src/map/clif_packetdb.hpp
  38. 94 0
      src/map/itemdb.cpp
  39. 3 0
      src/map/itemdb.hpp
  40. 2 1
      src/map/map.hpp
  41. 6 1
      src/map/npc.cpp
  42. 47 0
      src/map/packets.hpp
  43. 17 1
      src/map/packets_struct.hpp
  44. 10 9
      src/map/pc.cpp
  45. 1 1
      src/map/pet.cpp
  46. 85 64
      src/map/script.cpp
  47. 1 0
      src/map/script.hpp
  48. 1 0
      src/map/script_constants.hpp
  49. 5 4
      src/map/status.cpp
  50. 2 5
      src/map/unit.cpp
  51. 2 2
      tools/ci/npc.sh
  52. 4 4
      uninstall.sh

+ 17 - 18
athena-start

@@ -20,8 +20,7 @@ print_start() {
 get_status(){
 	PIDFILE=.$1.pid
 	if [ -e ${PIDFILE} ]; then
-		ISRUN=$(ps ax | grep $(cat ${PIDFILE}) | grep $1)
-		PSRUN=$(echo "$ISRUN" | awk '{ print $1 }')
+		PSRUN=$(pgrep -F ${PIDFILE})
 	fi
 	#return ${PSRUN} #seems to cause an issue for some os
 }
@@ -36,11 +35,11 @@ start_serv(){
 		echo "stat_serv, log is enabled"
 		echo "My logfile=${LOGFILE}"
 		if [ -z ${PSRUN} ]; then
-		if [ -e ./${FIFO} ]; then rm "$FIFO"; fi
+			if [ -e ./${FIFO} ]; then rm "$FIFO"; fi
 			mkfifo "$FIFO"; tee "$LOGRUN" < "$FIFO" & "./$1" > "$FIFO" 2>&1 & PID=$!
 			#"./$1" > >(tee "$LOGRUN") 2>&1 & PID=$! #bash only
 			echo "$PID" > .$1.pid
-			echo "Server '$1' started at `date +"%m-%d-%H:%M-%S"`" | tee ${LOGFILE}
+			echo "Server '$1' started at $(date +"%m-%d-%H:%M-%S")" | tee ${LOGFILE}
 		else
 			echo "Cannot start '$1', because it is already running p${PSRUN}" | tee ${LOGFILE}
 		fi
@@ -48,7 +47,7 @@ start_serv(){
 		if [ -z ${PSRUN} ]; then
 			./$1&
 			echo "$!" > .$1.pid
-			echo "Server '$1' started at `date +"%m-%d-%H:%M-%S"`"
+			echo "Server '$1' started at $(date +"%m-%d-%H:%M-%S")"
 		else
 			echo "Cannot start '$1', because it is already running p${PSRUN}"
 		fi
@@ -66,20 +65,20 @@ watch_serv(){
 			LOGFILE="$LOG_DIR/$i.launch.log"
 			LOGRUN="$LOG_DIR/$i.log"
 			FIFO=$i"_fifo"
-	
-			get_status $i
+
+			get_status ${i}
 			#echo "Echo id of $i is ${PSRUN}"
 			if [ -z ${PSRUN} ]; then
 				count=$((count+1))
 				#echo "fifo=$FIFO"
 				echo "server '$i' is down"
 				echo "server '$i' is down" >> ${LOGFILE}
-				echo "restarting server at time at `date +"%m-%d-%H:%M-%S"`" 
-				echo "restarting server at time at `date +"%m-%d-%H:%M-%S"`" >> ${LOGFILE}
+				echo "restarting server at time at $(date +"%m-%d-%H:%M-%S")"
+				echo "restarting server at time at $(date +"%m-%d-%H:%M-%S")" >> ${LOGFILE}
 				if [ -e $FIFO ]; then rm $FIFO; fi
 				mkfifo "$FIFO"; tee "$LOGRUN" < "$FIFO" & "./$i" > "$FIFO" 2>&1 & PID=$!
 				echo "$PID" > .$i.pid
-				if [ $2 ] && [ $2 -lt $count ]; then break; fi   
+				if [ $2 ] && [ $2 -lt $count ]; then break; fi
 			fi
 		done
 		sleep $1
@@ -101,11 +100,11 @@ restart(){
 			fi
 		done
 	done
-  $0 start
+	$0 start
 }
 
 case $1 in
-    'start')
+	'start')
 		print_start
 		check_files
 		echo "Check complete."
@@ -123,7 +122,7 @@ case $1 in
 		done
 		echo "rAthena was started."
 	;;
-    'watch')
+	'watch')
 		if [ ! -d "$LOG_DIR" ]; then mkdir -p $LOG_DIR; fi
 		if [ -z $2 ]; then Restart_count=10; else Restart_count=$2; fi
 		if [ -z $3 ]; then Restart_sleep=3; else Restart_sleep=$3; fi
@@ -134,7 +133,7 @@ case $1 in
 		done
 		watch_serv $Restart_count $Restart_sleep
 		echo "Watching rAthena now."
-	;;	
+	;;
 	'stop')
 		for i in ${W_SRV} ${M_SRV} ${C_SRV} ${L_SRV}
 		do
@@ -155,7 +154,7 @@ case $1 in
 		done
 	;;
 	'restart')
-		 restart
+		 restart "$@"
 	;;
 	'status')
 		for i in ${L_SRV} ${C_SRV} ${M_SRV} ${W_SRV}
@@ -181,7 +180,7 @@ case $1 in
 			'start')
 				echo "syntax: 'start {--enlog}'"
 				echo "This option will start the servers"
-				echo "--enlog will write all terminal output into a log/$servname.log file"
+				echo "--enlog will write all terminal output into a log/\$servname.log file"
 			;;
 			'stop')
 				echo "This option will shut the servers down"
@@ -205,11 +204,11 @@ case $1 in
 			;;
 			'val_runonce')
 				echo "syntax: 'val_runonce'"
-				echo "This option will run valgrin with run-once to check the servers"
+				echo "This option will run valgrind with run-once to check the servers"
 			;;
 			'valchk')
 				echo "syntax: 'valchk'"
-				echo "This option will run valgrin with the servers"
+				echo "This option will run valgrind with the servers"
 			;;
 			*)
 				echo "Please specify a command you would like more info on { start | stop | restart | status | watch }"

+ 3 - 0
conf/atcommands.yml

@@ -1016,6 +1016,9 @@ Body:
   - Command: enchantgradeui
     Help: |
       Opens the enchantgrade UI.
+  - Command: roulette
+    Help: |
+      Opens the roulette UI.
 
 Footer:
   Imports:

+ 7 - 0
conf/battle/feature.conf

@@ -136,6 +136,13 @@ feature.dynamicnpc_rangey: 2
 // Default: no
 feature.dynamicnpc_direction: no
 
+// Itemlink System on informational related commands (Note 1)
+// Generates <ITEML> string for an item and can be used for npctalk, message,
+// dispbottom, and broadcast commands. The result is clickable-item name just
+// like from SHIFT+Click from player's inventory/cart/equipment window.
+// Requires: 2010-00-00RagexeRE or later
+feature.itemlink: on
+
 // Enable the Gold PC timer? (Note 1)
 // Default: yes
 feature.goldpc_active: yes

+ 2 - 1
conf/char_athena.conf

@@ -121,7 +121,8 @@ start_point_doram: lasa_fild01,48,297
 // Format: <id>,<amount>,<position>{:<id>,<amount>,<position>...}
 // To auto-equip an item, include the position where it will be equipped; otherwise, use zero.
 // NOTE: For Doram, this requires client 20151001 or newer.
-start_items: 1201,1,2:2301,1,16
+start_items: 1201,1,2:2301,1,16:23484,1,0
+start_items_pre: 1201,1,2:2301,1,16
 start_items_doram: 1681,1,2:2301,1,16
 
 // Starting zeny for new characters

+ 1 - 1
db/pre-re/instance_db.yml

@@ -67,7 +67,7 @@ Body:
   - Id: 3
     Name: Orc's Memory
     Enter:
-      Map: 1@orcs 
+      Map: 1@orcs
       X: 179
       Y: 15
     AdditionalMaps:

+ 21 - 7
db/re/achievement_db.yml

@@ -1784,6 +1784,12 @@ Body:
     Rewards:
       TitleId: 1034
     Score: 10
+  - Id: 130005 # TODO (Terra Gloria: give achievement when player visit the coronation ceremony)
+    Group: Chatting
+    Name: Who made the king
+    Rewards:
+      TitleID: 1046
+    Score: 10
   - Id: 170000
     Group: Chatting
     Name: Song chamber is not an accident
@@ -2037,30 +2043,38 @@ Body:
     Name: Reborn in Valhalla!
     Condition: " Class == JOB_NOVICE_HIGH "
     Rewards:
-    #  Item: Adventurer_Box_1
+      Item: Adventurer_Box_1
       Script: " specialeffect2 EF_INCAGILITY; sc_start SC_INCREASEAGI,30000,10; "
     Score: 10
   - Id: 200032
     Group: Goal_Level
     Name: The start of another adventure!
     Condition: " BaseLevel == 100 "
-    #Rewards:
-    #  Item: Rebeginer_Box_100
+    Rewards:
+      Item: Rebeginer_Box_100
     Score: 10
   - Id: 200033
     Group: Goal_Level
     Name: With a new mind!(1)
     Condition: " BaseLevel == 170 && (Class >= JOB_RUNE_KNIGHT && Class <= JOB_GUILLOTINE_CROSS_T) "
-    #Rewards:
-    #  Item: Costume_Ticket
+    Rewards:
+      Item: Costume_Ticket
     Score: 50
   - Id: 200034
     Group: Goal_Level
     Name: With a new mind!(2)
     Condition: " BaseLevel == 170 && (Class >= JOB_ROYAL_GUARD && Class <= JOB_SHADOW_CHASER_T) "
-    #Rewards:
-    #  Item: Costume_Ticket
+    Rewards:
+      Item: Costume_Ticket
     Score: 50
+  - Id: 200035
+    Group: Goal_Level
+    Name: Level 200 achieved!
+    Condition: " BaseLevel == 200 "
+    Rewards:
+      Item: C_Magestic_Goat2
+      Script: " specialeffect2 EF_BLESSING; sc_start SC_BLESSING,30000,10; "
+    Score: 100
   - Id: 220000
     Group: Chatting_Create
     Name: Community begin

+ 1 - 1
db/re/instance_db.yml

@@ -67,7 +67,7 @@ Body:
   - Id: 3
     Name: Orc's Memory
     Enter:
-      Map: 1@orcs 
+      Map: 1@orcs
       X: 179
       Y: 15
     AdditionalMaps:

+ 1 - 1
db/re/item_combos.yml

@@ -30333,7 +30333,7 @@ Body:
           - G_Strings    # 2339
       - Combo:
           - Etran_Shirt_TW    # 20910
-          - G_Strings    # 2371
+          - G_Strings_    # 2371
     Script: |
       .@r =  getequiprefinerycnt(EQI_ARMOR);
       bonus bInt,15;

+ 91 - 113
db/re/item_db_equip.yml

@@ -21172,8 +21172,7 @@ Body:
       SoulLinker: true
       Wizard: true
     Locations:
-      Right_Hand: true
-      Left_Hand: true
+      Both_Hand: true
     WeaponLevel: 3
     EquipLevelMin: 100
     Refineable: true
@@ -21401,8 +21400,7 @@ Body:
       All_Third: true
       Fourth: true
     Locations:
-      Right_Hand: true
-      Left_Hand: true
+      Both_Hand: true
     WeaponLevel: 3
     EquipLevelMin: 100
     Refineable: true
@@ -55510,7 +55508,7 @@ Body:
     Script: |
       bonus bMdef,5;
       .@def = 10;
-      if (getpetinfo(PETINFO_ID) == 9026) {
+      if (getpetinfo(PETINFO_EGGID) == 9026) {
          .@def += 5;
          if (getpetinfo(PETINFO_INTIMATE) >= PET_INTIMATE_CORDIAL) {
             .@def += 5;
@@ -60231,8 +60229,7 @@ Body:
       Gunslinger: true
       Rebellion: true
     Locations:
-      Right_Hand: true
-      Left_Hand: true
+      Both_Hand: true
     WeaponLevel: 3
     EquipLevelMin: 100
     Refineable: true
@@ -60263,8 +60260,7 @@ Body:
       Gunslinger: true
       Rebellion: true
     Locations:
-      Right_Hand: true
-      Left_Hand: true
+      Both_Hand: true
     WeaponLevel: 3
     EquipLevelMin: 100
     Refineable: true
@@ -60372,8 +60368,7 @@ Body:
     Jobs:
       Rebellion: true
     Locations:
-      Right_Hand: true
-      Left_Hand: true
+      Both_Hand: true
     WeaponLevel: 3
     EquipLevelMin: 100
     Trade:
@@ -62168,8 +62163,7 @@ Body:
       KagerouOboro: true
       Ninja: true
     Locations:
-      Right_Hand: true
-      Left_Hand: true
+      Both_Hand: true
     WeaponLevel: 3
     EquipLevelMin: 100
     Refineable: true
@@ -62283,8 +62277,7 @@ Body:
       KagerouOboro: true
       Ninja: true
     Locations:
-      Right_Hand: true
-      Left_Hand: true
+      Both_Hand: true
     WeaponLevel: 3
     EquipLevelMin: 100
     Refineable: true
@@ -62403,8 +62396,7 @@ Body:
     Jobs:
       KagerouOboro: true
     Locations:
-      Right_Hand: true
-      Left_Hand: true
+      Both_Hand: true
     WeaponLevel: 3
     EquipLevelMin: 100
     Trade:
@@ -73200,8 +73192,7 @@ Body:
       BardDancer: true
       Hunter: true
     Locations:
-      Right_Hand: true
-      Left_Hand: true
+      Both_Hand: true
     WeaponLevel: 3
     EquipLevelMin: 100
     Refineable: true
@@ -73354,8 +73345,7 @@ Body:
       All_Third: true
       Fourth: true
     Locations:
-      Right_Hand: true
-      Left_Hand: true
+      Both_Hand: true
     WeaponLevel: 3
     EquipLevelMin: 100
     Refineable: true
@@ -73388,8 +73378,7 @@ Body:
       All_Third: true
       Fourth: true
     Locations:
-      Right_Hand: true
-      Left_Hand: true
+      Both_Hand: true
     WeaponLevel: 3
     EquipLevelMin: 100
     Refineable: true
@@ -73556,8 +73545,7 @@ Body:
       All_Third: true
       Fourth: true
     Locations:
-      Right_Hand: true
-      Left_Hand: true
+      Both_Hand: true
     WeaponLevel: 3
     EquipLevelMin: 100
     Trade:
@@ -73588,8 +73576,7 @@ Body:
       All_Third: true
       Fourth: true
     Locations:
-      Right_Hand: true
-      Left_Hand: true
+      Both_Hand: true
     WeaponLevel: 3
     EquipLevelMin: 100
     Trade:
@@ -73816,8 +73803,7 @@ Body:
       All_Third: true
       Fourth: true
     Locations:
-      Right_Hand: true
-      Left_Hand: true
+      Both_Hand: true
     WeaponLevel: 4
     EquipLevelMin: 99
     Trade:
@@ -73855,8 +73841,7 @@ Body:
       All_Third: true
       Fourth: true
     Locations:
-      Right_Hand: true
-      Left_Hand: true
+      Both_Hand: true
     WeaponLevel: 4
     EquipLevelMin: 99
     Trade:
@@ -81479,7 +81464,7 @@ Body:
          bonus bMatkRate,7;
       else
          bonus bMatkRate,5;
-      if (getpetinfo(PETINFO_ID) == 9023) {
+      if (getpetinfo(PETINFO_EGGID) == 9023) {
          if (getpetinfo(PETINFO_INTIMATE) >= PET_INTIMATE_LOYAL)
             bonus bDelayRate,-5;
          else
@@ -84173,9 +84158,13 @@ Body:
       bonus bSPGainValue,50;
       bonus bLongSPGainValue,50;
       bonus bMagicSPGainValue,50;
-      if (getpetinfo(PETINFO_ID) == 9055) {
-         bonus2 bHPRegenRate,(getpetinfo(PETINFO_INTIMATE) >= PET_INTIMATE_LOYAL ? 1000 : 500),5000;
-         bonus2 bSPRegenRate,(getpetinfo(PETINFO_INTIMATE) >= PET_INTIMATE_LOYAL ? 40 : 20),5000;
+      if (getpetinfo(PETINFO_EGGID) == 9055) {
+         if (getpetinfo(PETINFO_INTIMATE) >= PET_INTIMATE_LOYAL)
+            .@factor = 2;
+         else
+            .@factor = 1;
+         bonus2 bHPRegenRate,(.@factor*500),5000;
+         bonus2 bSPRegenRate,(.@factor*20),5000;
       }
     UnEquipScript: |
       heal -5000,-500;
@@ -102634,8 +102623,7 @@ Body:
       Knight: true
       Swordman: true
     Locations:
-      Right_Hand: true
-      Left_Hand: true
+      Both_Hand: true
     WeaponLevel: 3
     EquipLevelMin: 100
     Refineable: true
@@ -102757,8 +102745,7 @@ Body:
       All_Third: true
       Fourth: true
     Locations:
-      Right_Hand: true
-      Left_Hand: true
+      Both_Hand: true
     WeaponLevel: 3
     EquipLevelMin: 100
     Refineable: true
@@ -102817,8 +102804,7 @@ Body:
       All_Third: true
       Fourth: true
     Locations:
-      Right_Hand: true
-      Left_Hand: true
+      Both_Hand: true
     WeaponLevel: 3
     EquipLevelMin: 100
     Trade:
@@ -102974,8 +102960,7 @@ Body:
       All_Third: true
       Fourth: true
     Locations:
-      Right_Hand: true
-      Left_Hand: true
+      Both_Hand: true
     WeaponLevel: 4
     EquipLevelMin: 99
     Trade:
@@ -119016,8 +119001,7 @@ Body:
       Knight: true
       Swordman: true
     Locations:
-      Right_Hand: true
-      Left_Hand: true
+      Both_Hand: true
     WeaponLevel: 3
     EquipLevelMin: 100
     Refineable: true
@@ -119167,8 +119151,7 @@ Body:
       All_Third: true
       Fourth: true
     Locations:
-      Right_Hand: true
-      Left_Hand: true
+      Both_Hand: true
     WeaponLevel: 3
     EquipLevelMin: 100
     Trade:
@@ -120946,8 +120929,7 @@ Body:
     Jobs:
       Assassin: true
     Locations:
-      Right_Hand: true
-      Left_Hand: true
+      Both_Hand: true
     WeaponLevel: 3
     EquipLevelMin: 100
     Refineable: true
@@ -121090,8 +121072,7 @@ Body:
       All_Third: true
       Fourth: true
     Locations:
-      Right_Hand: true
-      Left_Hand: true
+      Both_Hand: true
     WeaponLevel: 3
     EquipLevelMin: 100
     Refineable: true
@@ -121149,8 +121130,7 @@ Body:
       All_Third: true
       Fourth: true
     Locations:
-      Right_Hand: true
-      Left_Hand: true
+      Both_Hand: true
     WeaponLevel: 3
     EquipLevelMin: 100
     Trade:
@@ -121279,8 +121259,7 @@ Body:
       All_Third: true
       Fourth: true
     Locations:
-      Right_Hand: true
-      Left_Hand: true
+      Both_Hand: true
     WeaponLevel: 4
     EquipLevelMin: 99
     Trade:
@@ -121731,8 +121710,7 @@ Body:
       Merchant: true
       Swordman: true
     Locations:
-      Right_Hand: true
-      Left_Hand: true
+      Both_Hand: true
     WeaponLevel: 3
     EquipLevelMin: 100
     Refineable: true
@@ -121893,8 +121871,7 @@ Body:
       All_Third: true
       Fourth: true
     Locations:
-      Right_Hand: true
-      Left_Hand: true
+      Both_Hand: true
     WeaponLevel: 3
     EquipLevelMin: 100
     Refineable: true
@@ -122115,8 +122092,7 @@ Body:
       All_Third: true
       Fourth: true
     Locations:
-      Right_Hand: true
-      Left_Hand: true
+      Both_Hand: true
     WeaponLevel: 4
     EquipLevelMin: 99
     Trade:
@@ -122383,8 +122359,7 @@ Body:
       Gunslinger: true
       Rebellion: true
     Locations:
-      Right_Hand: true
-      Left_Hand: true
+      Both_Hand: true
     WeaponLevel: 3
     EquipLevelMin: 100
     Refineable: true
@@ -122417,8 +122392,7 @@ Body:
       Gunslinger: true
       Rebellion: true
     Locations:
-      Right_Hand: true
-      Left_Hand: true
+      Both_Hand: true
     WeaponLevel: 3
     EquipLevelMin: 100
     Refineable: true
@@ -122452,8 +122426,7 @@ Body:
       Gunslinger: true
       Rebellion: true
     Locations:
-      Right_Hand: true
-      Left_Hand: true
+      Both_Hand: true
     WeaponLevel: 3
     EquipLevelMin: 100
     Refineable: true
@@ -122484,8 +122457,7 @@ Body:
       Gunslinger: true
       Rebellion: true
     Locations:
-      Right_Hand: true
-      Left_Hand: true
+      Both_Hand: true
     WeaponLevel: 3
     EquipLevelMin: 100
     Refineable: true
@@ -143797,30 +143769,34 @@ Body:
     Script: |
       bonus2 bAddSize,Size_All,10;
       bonus2 bMagicAddSize,Size_All,10;
-      if (getpetinfo(PETINFO_ID) == 9112)
+      switch( getpetinfo(PETINFO_EGGID) ) {
+      case 9112:  // Moonlight_Egg
          bonus2 bHPVanishRate,40,4;
-      if (getpetinfo(PETINFO_ID) == 9088) {
+         break;
+      case 9088:  // Angeling_Egg
          bonus bBaseAtk,readparam(bLuk);
          bonus bMatk,readparam(bLuk);
          bonus2 bExpAddClass,Class_All,5;
-      }
-      if (getpetinfo(PETINFO_ID) == 9096) {
+         break;
+      case 9096:  // Cat_O_Nine_Tail_Egg
          bonus2 bAddRace,RC_Demon,30;
          bonus2 bMagicAddRace,RC_Demon,30;
          bonus2 bSubRace,RC_Demon,5;
-      }
-      if (getpetinfo(PETINFO_ID) == 9087) {
+         break;
+      case 9087:  // High_Orc_Egg
          bonus2 bAddRace,RC_Brute,30;
          bonus2 bMagicAddRace,RC_Brute,30;
          bonus2 bSubRace,RC_Brute,5;
-      }
-      if (getpetinfo(PETINFO_ID) == 9069) {
+         break;
+      case 9069:  // Mastering_Egg
          bonus2 bAddRace,RC_Plant,30;
          bonus2 bMagicAddRace,RC_Plant,30;
          bonus2 bSubRace,RC_Plant,5;
-      }
-      if (getpetinfo(PETINFO_ID) == 9106)
+         break;
+      case 9106:  // Metaller_Egg
          bonus3 bAutoSpell,"WM_METALICSOUND",5,150;
+         break;
+      }
   - Id: 410028
     AegisName: Wonder_Egg_Basket_
     Name: Wonder Egg Basket
@@ -143834,23 +143810,39 @@ Body:
     EquipLevelMin: 100
     Script: |
       bonus bAspdRate,10;
-      if (getpetinfo(PETINFO_ID) == 9109 || getpetinfo(PETINFO_ID) == 9112 || getpetinfo(PETINFO_ID) == 9115 || getpetinfo(PETINFO_ID) == 9121) {
+      switch( getpetinfo(PETINFO_EGGID) ) {
+      case 9109:  // Sweet_Drops_Egg
+      case 9112:  // Moonlight_Egg
+      case 9115:  // Bacsojin_Egg2
+      case 9121:  // Orc_Hero_Egg_
+      case 9126:  // Kiel_Egg
+      case 9136:  // Eddga_Egg
          bonus bBaseAtk,200;
          bonus bMatk,200;
          bonus bAllStats,10;
-      }
-      /*Todo Itemid 9126 9136*/
-      if (getpetinfo(PETINFO_ID) == 9088 || getpetinfo(PETINFO_ID) == 9108 || getpetinfo(PETINFO_ID) == 9113) {
+         break;
+      case 9088:  // Angeling_Egg
+      case 9108:  // Xm_Teddybear_Egg
+      case 9113:  // Roost_Of_Skelion
          bonus bBaseAtk,200;
          bonus bMatk,200;
          bonus2 bAddSize,Size_All,10;
          bonus2 bMagicAddSize,Size_All,10;
-      }
-      if (getpetinfo(PETINFO_ID) == 9069 || getpetinfo(PETINFO_ID) == 9087 || getpetinfo(PETINFO_ID) == 9096 || getpetinfo(PETINFO_ID) == 9106 || getpetinfo(PETINFO_ID) == 9117 || getpetinfo(PETINFO_ID) == 9118 || getpetinfo(PETINFO_ID) == 9119 || getpetinfo(PETINFO_ID) == 9120 || getpetinfo(PETINFO_ID) == 9124) {
+         break;
+      case 9069:  // Mastering_Egg
+      case 9087:  // High_Orc_Egg
+      case 9096:  // Cat_O_Nine_Tail_Egg
+      case 9106:  // Metaller_Egg
+      case 9117:  // Contaminated_Wanderer_Egg
+      case 9118:  // Aliot_Egg
+      case 9119:  // Alicel_Egg
+      case 9120:  // Aliza_Egg
+      case 9124:  // Ep17_2_C_Admin2_Egg
          bonus bBaseAtk,200;
          bonus bMatk,200;
          bonus bDef,150;
          bonus bMdef,15;
+         break;
       }
   - Id: 410029
     AegisName: C_Wonder_Egg_Basket
@@ -174531,8 +174523,7 @@ Body:
       All_Third: true
       Fourth: true
     Locations:
-      Right_Hand: true
-      Left_Hand: true
+      Both_Hand: true
     WeaponLevel: 1
     # Flags:
       # BuyingStore: true
@@ -174719,8 +174710,7 @@ Body:
       All_Third: true
       Fourth: true
     Locations:
-      Right_Hand: true
-      Left_Hand: true
+      Both_Hand: true
     WeaponLevel: 4
     EquipLevelMin: 100
     Refineable: true
@@ -175493,8 +175483,7 @@ Body:
       All_Third: true
       Fourth: true
     Locations:
-      Right_Hand: true
-      Left_Hand: true
+      Both_Hand: true
     WeaponLevel: 4
     EquipLevelMin: 100
     Refineable: true
@@ -176425,8 +176414,7 @@ Body:
       All_Third: true
       Fourth: true
     Locations:
-      Right_Hand: true
-      Left_Hand: true
+      Both_Hand: true
     WeaponLevel: 4
     EquipLevelMin: 100
     Refineable: true
@@ -177122,8 +177110,7 @@ Body:
       All_Third: true
       Fourth: true
     Locations:
-      Right_Hand: true
-      Left_Hand: true
+      Both_Hand: true
     WeaponLevel: 4
     EquipLevelMin: 100
     Refineable: true
@@ -177571,8 +177558,7 @@ Body:
       All_Third: true
       Fourth: true
     Locations:
-      Right_Hand: true
-      Left_Hand: true
+      Both_Hand: true
     WeaponLevel: 4
     EquipLevelMin: 100
     Refineable: true
@@ -177620,8 +177606,7 @@ Body:
       All_Third: true
       Fourth: true
     Locations:
-      Right_Hand: true
-      Left_Hand: true
+      Both_Hand: true
     WeaponLevel: 4
     EquipLevelMin: 100
     Refineable: true
@@ -178694,8 +178679,7 @@ Body:
       KagerouOboro: true
       Ninja: true
     Locations:
-      Right_Hand: true
-      Left_Hand: true
+      Both_Hand: true
     WeaponLevel: 4
     EquipLevelMin: 150
     Refineable: true
@@ -179499,8 +179483,7 @@ Body:
       All_Third: true
       Fourth: true
     Locations:
-      Right_Hand: true
-      Left_Hand: true
+      Both_Hand: true
     WeaponLevel: 1
     # Flags:
       # BuyingStore: true
@@ -179709,8 +179692,7 @@ Body:
       All_Third: true
       Fourth: true
     Locations:
-      Right_Hand: true
-      Left_Hand: true
+      Both_Hand: true
     WeaponLevel: 4
     EquipLevelMin: 100
     Refineable: true
@@ -179755,8 +179737,7 @@ Body:
       All_Third: true
       Fourth: true
     Locations:
-      Right_Hand: true
-      Left_Hand: true
+      Both_Hand: true
     WeaponLevel: 4
     EquipLevelMin: 100
     Refineable: true
@@ -179799,8 +179780,7 @@ Body:
       All_Third: true
       Fourth: true
     Locations:
-      Right_Hand: true
-      Left_Hand: true
+      Both_Hand: true
     WeaponLevel: 4
     EquipLevelMin: 100
     Refineable: true
@@ -179845,8 +179825,7 @@ Body:
       All_Third: true
       Fourth: true
     Locations:
-      Right_Hand: true
-      Left_Hand: true
+      Both_Hand: true
     WeaponLevel: 4
     EquipLevelMin: 100
     Refineable: true
@@ -181316,8 +181295,7 @@ Body:
       Gunslinger: true
       Rebellion: true
     Locations:
-      Right_Hand: true
-      Left_Hand: true
+      Both_Hand: true
     WeaponLevel: 1
     # Flags:
       # BuyingStore: true

+ 574 - 0
db/re/item_db_usable.yml

@@ -3802,6 +3802,34 @@ Body:
     Script: |
       itemheal rand(50,100),0;
       /*TODO*/
+  - Id: 11614
+    AegisName: Fresh_Milk
+    Name: Fresh Milk
+    Type: Healing
+    Weight: 30
+    Trade:
+      NoDrop: true
+      NoTrade: true
+      NoCart: true
+      NoStorage: true
+      NoGuildStorage: true
+      NoAuction: true
+    Script: |
+      itemheal rand(27,37),0;
+  - Id: 11615
+    AegisName: Sweet_Potato_
+    Name: Sweet Potato
+    Type: Healing
+    Weight: 20
+    Trade:
+      NoDrop: true
+      NoTrade: true
+      NoCart: true
+      NoStorage: true
+      NoGuildStorage: true
+      NoAuction: true
+    Script: |
+      itemheal rand(15,23),0;
   - Id: 11616
     AegisName: Yummy_Meat
     Name: Delicious Meat
@@ -47350,6 +47378,21 @@ Body:
     Script: |
       specialeffect2 EF_CLAYMORE;
       setmadogear true;
+  - Id: 23278
+    AegisName: HelpmeShorty
+    Name: Help Me Shorty
+    Type: Delayconsume
+    Trade:
+      NoDrop: true
+      NoTrade: true
+      NoSell: true
+      NoCart: true
+      NoStorage: true
+      NoGuildStorage: true
+      NoMail: true
+      NoAuction: true
+    Script: |
+      duplicate_dynamic("Shorty#fgtg01");
   - Id: 23280
     AegisName: N_Fly_Wing_
     Name: Novice Fly Wing
@@ -47539,6 +47582,20 @@ Body:
     Script: |
       input .@megaphone$;
       announce strcharinfo(0) + ": " + .@megaphone$,bc_all,0xFF0000;
+  - Id: 23338
+    AegisName: Comp_Wing_Of_Fly
+    Name: Compressed Fly Wing
+    Type: Delayconsume
+    Weight: 0
+    Trade:
+      NoDrop: true
+      NoTrade: true
+      NoCart: true
+      NoGuildStorage: true
+      NoMail: true
+      NoAuction: true
+    Script: |
+       itemskill "AL_TELEPORT",1;
   - Id: 23360
     AegisName: Psychotropic
     Name: Psychotropic
@@ -47678,6 +47735,397 @@ Body:
     Script: |
       specialeffect2 EF_POTION_BERSERK;
       sc_start SC_INFINITY_DRINK,1800000,0;
+  - Id: 23484
+    AegisName: Firstaid_Box_5
+    Name: First aid Box (5)
+    Type: Usable
+    EquipLevelMin: 5
+    Weight: 0
+    Trade:
+      NoDrop: true
+      NoTrade: true
+      NoCart: true
+      NoStorage: true
+      NoGuildStorage: true
+      NoMail: true
+      NoAuction: true
+    Script: |
+      getitem 11518,10;
+      getitem 11614,20;
+      getitem 12325,15;
+      getitem 22542,1;
+      getitem 23485,1;
+  - Id: 23485
+    AegisName: Firstaid_Box_10
+    Name: First aid Box (10)
+    Type: Usable
+    EquipLevelMin: 10
+    Weight: 0
+    Trade:
+      NoDrop: true
+      NoTrade: true
+      NoCart: true
+      NoStorage: true
+      NoGuildStorage: true
+      NoMail: true
+      NoAuction: true
+    Script: |
+      getitem 11518,15;
+      getitem 11614,40;
+      getitem 12325,15;
+      getitem 22542,1;
+      getitem 23486,1;
+  - Id: 23486
+    AegisName: Firstaid_Box_15
+    Name: First aid Box (15)
+    Type: Usable
+    EquipLevelMin: 15
+    Weight: 0
+    Trade:
+      NoDrop: true
+      NoTrade: true
+      NoCart: true
+      NoStorage: true
+      NoGuildStorage: true
+      NoMail: true
+      NoAuction: true
+    Script: |
+      getitem 11518,20;
+      getitem 11614,60;
+      getitem 12325,10;
+      getitem 22542,2;
+      getitem 23487,1;
+  - Id: 23487
+    AegisName: Firstaid_Box_20
+    Name: First aid Box (20)
+    Type: Usable
+    EquipLevelMin: 20
+    Weight: 0
+    Trade:
+      NoDrop: true
+      NoTrade: true
+      NoCart: true
+      NoStorage: true
+      NoGuildStorage: true
+      NoMail: true
+      NoAuction: true
+    Script: |
+      getitem 11518,15;
+      getitem 11615,80;
+      getitem 12325,10;
+      getitem 22542,2;
+      getitem 23488,1;
+  - Id: 23488
+    AegisName: Firstaid_Box_25
+    Name: First aid Box (25)
+    Type: Usable
+    EquipLevelMin: 25
+    Weight: 0
+    Trade:
+      NoDrop: true
+      NoTrade: true
+      NoCart: true
+      NoStorage: true
+      NoGuildStorage: true
+      NoMail: true
+      NoAuction: true
+    Script: |
+      getitem 11518,20;
+      getitem 11615,100;
+      getitem 12325,5;
+      getitem 22542,3;
+      getitem 23489,1;
+  - Id: 23489
+    AegisName: Firstaid_Box_30
+    Name: First aid Box (30)
+    Type: Usable
+    EquipLevelMin: 30
+    Weight: 0
+    Trade:
+      NoDrop: true
+      NoTrade: true
+      NoCart: true
+      NoStorage: true
+      NoGuildStorage: true
+      NoMail: true
+      NoAuction: true
+    Script: |
+      getitem 11518,25;
+      getitem 11615,120;
+      getitem 12325,5;
+      getitem 22542,3;
+      getitem 23490,1;
+  - Id: 23490
+    AegisName: Firstaid_Box_35
+    Name: First aid Box (35)
+    Type: Usable
+    EquipLevelMin: 35
+    Weight: 0
+    Trade:
+      NoDrop: true
+      NoTrade: true
+      NoCart: true
+      NoStorage: true
+      NoGuildStorage: true
+      NoMail: true
+      NoAuction: true
+    Script: |
+      getitem 11572,15;
+      getitem 22542,3;
+      getitem 23491,1;
+      getitem 23503,5;
+  - Id: 23491
+    AegisName: Firstaid_Box_40
+    Name: First aid Box (40)
+    Type: Usable
+    EquipLevelMin: 40
+    Weight: 0
+    Trade:
+      NoDrop: true
+      NoTrade: true
+      NoCart: true
+      NoStorage: true
+      NoGuildStorage: true
+      NoMail: true
+      NoAuction: true
+    Script: |
+      getitem 11572,15;
+      getitem 22544,2;
+      getitem 23492,1;
+      getitem 23503,6;
+  - Id: 23492
+    AegisName: Firstaid_Box_45
+    Name: First aid Box (45)
+    Type: Usable
+    EquipLevelMin: 45
+    Weight: 0
+    Trade:
+      NoDrop: true
+      NoTrade: true
+      NoCart: true
+      NoStorage: true
+      NoGuildStorage: true
+      NoMail: true
+      NoAuction: true
+    Script: |
+      getitem 11572,20;
+      getitem 22544,2;
+      getitem 23493,1;
+      getitem 23503,7;
+  - Id: 23493
+    AegisName: Firstaid_Box_50
+    Name: First aid Box (50)
+    Type: Usable
+    EquipLevelMin: 50
+    Weight: 0
+    Trade:
+      NoDrop: true
+      NoTrade: true
+      NoCart: true
+      NoStorage: true
+      NoGuildStorage: true
+      NoMail: true
+      NoAuction: true
+    Script: |
+      getitem 11572,20;
+      getitem 22544,2;
+      getitem 23494,1;
+      getitem 23504,7;
+  - Id: 23494
+    AegisName: Firstaid_Box_55
+    Name: First aid Box (55)
+    Type: Usable
+    EquipLevelMin: 55
+    Weight: 0
+    Trade:
+      NoDrop: true
+      NoTrade: true
+      NoCart: true
+      NoStorage: true
+      NoGuildStorage: true
+      NoMail: true
+      NoAuction: true
+    Script: |
+      getitem 11572,25;
+      getitem 22544,2;
+      getitem 23495,1;
+      getitem 23504,8;
+  - Id: 23495
+    AegisName: Firstaid_Box_60
+    Name: First aid Box (60)
+    Type: Usable
+    EquipLevelMin: 60
+    Weight: 0
+    Trade:
+      NoDrop: true
+      NoTrade: true
+      NoCart: true
+      NoStorage: true
+      NoGuildStorage: true
+      NoMail: true
+      NoAuction: true
+    Script: |
+      getitem 11572,25;
+      getitem 23496,1;
+      getitem 23504,9;
+  - Id: 23496
+    AegisName: Firstaid_Box_65
+    Name: First aid Box (65)
+    Type: Usable
+    EquipLevelMin: 65
+    Weight: 0
+    Trade:
+      NoDrop: true
+      NoTrade: true
+      NoCart: true
+      NoStorage: true
+      NoGuildStorage: true
+      NoMail: true
+      NoAuction: true
+    Script: |
+      getitem 11572,30;
+      getitem 23497,1;
+      getitem 23505,5;
+  - Id: 23497
+    AegisName: Firstaid_Box_70
+    Name: First aid Box (70)
+    Type: Usable
+    EquipLevelMin: 70
+    Weight: 0
+    Trade:
+      NoDrop: true
+      NoTrade: true
+      NoCart: true
+      NoStorage: true
+      NoGuildStorage: true
+      NoMail: true
+      NoAuction: true
+    Script: |
+      getitem 11572,30;
+      getitem 23498,1;
+      getitem 23505,6;
+  - Id: 23498
+    AegisName: Firstaid_Box_75
+    Name: First aid Box (75)
+    Type: Usable
+    EquipLevelMin: 75
+    Weight: 0
+    Trade:
+      NoDrop: true
+      NoTrade: true
+      NoCart: true
+      NoStorage: true
+      NoGuildStorage: true
+      NoMail: true
+      NoAuction: true
+    Script: |
+      getitem 11572,35;
+      getitem 23499,1;
+      getitem 23505,7;
+  - Id: 23499
+    AegisName: Firstaid_Box_80
+    Name: First aid Box (80)
+    Type: Usable
+    EquipLevelMin: 80
+    Weight: 0
+    Trade:
+      NoDrop: true
+      NoTrade: true
+      NoCart: true
+      NoStorage: true
+      NoGuildStorage: true
+      NoMail: true
+      NoAuction: true
+    Script: |
+      getitem 11572,35;
+      getitem 23500,1;
+      getitem 23505,8;
+  - Id: 23500
+    AegisName: Firstaid_Box_85
+    Name: First aid Box (85)
+    Type: Usable
+    EquipLevelMin: 85
+    Weight: 0
+    Trade:
+      NoDrop: true
+      NoTrade: true
+      NoCart: true
+      NoStorage: true
+      NoGuildStorage: true
+      NoMail: true
+      NoAuction: true
+    Script: |
+      getitem 11572,40;
+      getitem 22543,2;
+      getitem 23501,1;
+      getitem 23506,7;
+  - Id: 23501
+    AegisName: Firstaid_Box_90
+    Name: First aid Box (90)
+    Type: Usable
+    EquipLevelMin: 90
+    Weight: 0
+    Trade:
+      NoDrop: true
+      NoTrade: true
+      NoCart: true
+      NoStorage: true
+      NoGuildStorage: true
+      NoMail: true
+      NoAuction: true
+    Script: |
+      getitem 11572,40;
+      getitem 22543,2;
+      getitem 23502,1;
+      getitem 23506,8;
+  - Id: 23502
+    AegisName: Firstaid_Box_95
+    Name: First aid Box (95)
+    Type: Usable
+    EquipLevelMin: 95
+    Weight: 0
+    Trade:
+      NoDrop: true
+      NoTrade: true
+      NoCart: true
+      NoStorage: true
+      NoGuildStorage: true
+      NoMail: true
+      NoAuction: true
+    Script: |
+      getitem 11572,45;
+      getitem 22543,2;
+      getitem 23506,8;
+  - Id: 23503
+    AegisName: Red_Potion_B_20
+    Name: "[Event] Crate of 20 Red Potions"
+    Type: Usable
+    Weight: 0
+    Trade:
+      NoDrop: true
+      NoTrade: true
+      NoCart: true
+      NoStorage: true
+      NoGuildStorage: true
+      NoMail: true
+      NoAuction: true
+    Script: |
+      getitem 11570,20;
+  - Id: 23504
+    AegisName: Orange_Potion_B_20
+    Name: "[Event] Crate of 20 Orange Potions"
+    Type: Usable
+    Weight: 0
+    Trade:
+      NoDrop: true
+      NoTrade: true
+      NoCart: true
+      NoStorage: true
+      NoGuildStorage: true
+      NoMail: true
+      NoAuction: true
+    Script: |
+      getitem 11569,20;
   - Id: 23505
     AegisName: Yellow_Potion_B_20
     Name: "[Event] Crate of 20 Yellow Potions"
@@ -47808,6 +48256,132 @@ Body:
     Script: |
       percentheal 5,0;
       sc_start SC_MATKPOTION,600000,3;
+  - Id: 23575
+    AegisName: Adventurer_Box_1
+    Name: Adventurer Box (1)
+    Type: Usable
+    Weight: 0
+    Trade:
+      NoDrop: true
+      NoTrade: true
+      NoCart: true
+      NoStorage: true
+      NoGuildStorage: true
+      NoMail: true
+      NoAuction: true
+    Script: |
+      getitem 11567,100;
+      getitem 12265,5;
+      getitem 22979,5;
+      getitem 23338,50;
+      getitem 23576,1;
+  - Id: 23576
+    AegisName: Adventurer_Box_15
+    Name: Adventurer Box (15)
+    Type: Usable
+    EquipLevelMin: 15
+    Weight: 0
+    Trade:
+      NoDrop: true
+      NoTrade: true
+      NoCart: true
+      NoStorage: true
+      NoGuildStorage: true
+      NoMail: true
+      NoAuction: true
+    Script: |
+      getitem 11567,150;
+      getitem 23338,50;
+      getitem 23577,1;
+  - Id: 23577
+    AegisName: Adventurer_Box_30
+    Name: Adventurer Box (30)
+    Type: Usable
+    EquipLevelMin: 30
+    Weight: 0
+    Trade:
+      NoDrop: true
+      NoTrade: true
+      NoCart: true
+      NoStorage: true
+      NoGuildStorage: true
+      NoMail: true
+      NoAuction: true
+    Script: |
+      getitem 23338,50;
+      getitem 23503,20;
+      getitem 23578,1;
+  - Id: 23578
+    AegisName: Adventurer_Box_45
+    Name: Adventurer Box (45)
+    Type: Usable
+    EquipLevelMin: 45
+    Weight: 0
+    Trade:
+      NoDrop: true
+      NoTrade: true
+      NoCart: true
+      NoStorage: true
+      NoGuildStorage: true
+      NoMail: true
+      NoAuction: true
+    Script: |
+      getitem 23338,50;
+      getitem 23503,25;
+      getitem 23579,1;
+  - Id: 23579
+    AegisName: Adventurer_Box_60
+    Name: Adventurer Box (60)
+    Type: Usable
+    EquipLevelMin: 60
+    Weight: 0
+    Trade:
+      NoDrop: true
+      NoTrade: true
+      NoCart: true
+      NoStorage: true
+      NoGuildStorage: true
+      NoMail: true
+      NoAuction: true
+    Script: |
+      getitem 23338,50;
+      getitem 23504,20;
+      getitem 23580,1;
+  - Id: 23580
+    AegisName: Adventurer_Box_75
+    Name: Adventurer Box (75)
+    Type: Usable
+    EquipLevelMin: 75
+    Weight: 0
+    Trade:
+      NoDrop: true
+      NoTrade: true
+      NoCart: true
+      NoStorage: true
+      NoGuildStorage: true
+      NoMail: true
+      NoAuction: true
+    Script: |
+      getitem 23338,50;
+      getitem 23504,25;
+      getitem 23581,1;
+  - Id: 23581
+    AegisName: Adventurer_Box_90
+    Name: Adventurer Box (90)
+    Type: Usable
+    EquipLevelMin: 90
+    Weight: 0
+    Trade:
+      NoDrop: true
+      NoTrade: true
+      NoCart: true
+      NoStorage: true
+      NoGuildStorage: true
+      NoMail: true
+      NoAuction: true
+    Script: |
+      getitem 23505,20;
+      getitem 23338,50;
   - Id: 23582
     AegisName: E_Wing_Of_Fly_3Day_Box
     Name: "[Gift] Unlimited Fly Wing 3 Day Box"

+ 11 - 0
db/re/quest_db.yml

@@ -9790,6 +9790,17 @@ Body:
   - Id: 14687
     Title: Mysterious Device
     TimeLimit: +1h
+  - Id: 14688
+    Title: My name is Shorty
+  - Id: 14689
+    Title: Help Shorty
+  - Id: 14693
+    Title: To Payon
+  - Id: 14694
+    Title: To Prontera Field
+  - Id: 14695
+    Title: Cheer up, Sunsammi!
+    TimeLimit: +30mn
   - Id: 14699
     Title: Event once a day
     TimeLimit: 4h

+ 66 - 35
db/readme.md

@@ -18,6 +18,7 @@ We want to add our own custom achievement that can be given to a player via an N
 
 #### /db/import/achievement_db.yml
 
+```yml
     - Id: 280000
       Group: None
       Name: Emperio
@@ -30,6 +31,7 @@ We want to add our own custom achievement that can be given to a player via an N
       Reward:
         TitleId: 1036
       Score: 50
+```
 
 
 ### Instances
@@ -38,27 +40,30 @@ We want to add our own customized Housing Instance.
 
 #### /db/import/instance_db.yml
 
-	- Id: 35
-	    Name: Home
-        IdleTimeOut: 900
-        Enter:
-          Map: 1@home
-          X: 24
-          Y: 6
-        AdditionalMaps:
-          - Map: 2@home
-          - Map: 3@home
+```yml
+    - Id: 35
+      Name: Home
+      IdleTimeOut: 900
+      Enter:
+        Map: 1@home
+        X: 24
+        Y: 6
+      AdditionalMaps:
+        - Map: 2@home
+        - Map: 3@home
+```
 
 
 ### Mob Alias
 ---
-We want to give a custom mob a Novice player sprite.
+We want to make Porings look like Baphomet.
 
-#### /db/import/mob_avail.txt
+#### /db/import/mob_avail.yml
 
-    // Structure of Database:
-    // MobID,SpriteID{,Equipment}
-    3850,0
+```yml
+    - Mob: PORING
+      Sprite: BAPHOMET
+```
 
 
 ### Custom Maps
@@ -67,45 +72,71 @@ We want to add our own custom maps. For this we need to add our map names to `im
 
 #### /db/import/map_index.txt
 
+```
     1@home	1250
     2@home
     3@home
     ev_has
     shops
     prt_pvp
+```
 
 
 ### Item Trade Restrictions
 ---
 We want to ensure that specific items cannot be traded, sold, dropped, placed in storage, etc.
 
-#### /db/import/item_trade.txt
-
-    // Legend for 'TradeMask' field (bitmask):
-    // 1   - item can't be dropped
-    // 2   - item can't be traded (nor vended)
-    // 4   - wedded partner can override restriction 2
-    // 8   - item can't be sold to npcs
-    // 16  - item can't be placed in the cart
-    // 32  - item can't be placed in the storage
-    // 64  - item can't be placed in the guild storage
-    // 128 - item can't be attached to mail
-    // 256 - item can't be auctioned
-    // Full outright value = 511
-    34000,511,100	// Old Green Box
-    34001,511,100	// House Keys
-    34002,511,100	// Reputation Journal
+#### /db/import/item_db.yml
+
+```yml
+    - Id: 34000 # Old Green Box
+      Trade:
+        NoDrop: true
+        NoTrade: true
+        TradePartner: true
+        NoSell: true
+        NoCart: true
+        NoStorage: true
+        NoGuildStorage: true
+        NoMail: true
+        NoAuction: true
+    - Id: 34001 # House Keys
+      Trade:
+        NoDrop: true
+        NoTrade: true
+        TradePartner: true
+        NoSell: true
+        NoCart: true
+        NoStorage: true
+        NoGuildStorage: true
+        NoMail: true
+        NoAuction: true
+    - Id: 34002 # Reputation Journal
+      Trade:
+        NoDrop: true
+        NoTrade: true
+        TradePartner: true
+        NoSell: true
+        NoCart: true
+        NoStorage: true
+        NoGuildStorage: true
+        NoMail: true
+        NoAuction: true
+```
 
 
 ### Custom Quests
 ---
 We want to add our own custom quests to the quest_db.
 
-#### /db/import/quest_db.txt
+#### /db/import/quest_db.yml
 
-    // Quest ID,Time Limit,Target1,Val1,Target2,Val2,Target3,Val3,MobID1,NameID1,Rate1,MobID2,NameID2,Rate2,MobID3,NameID3,Rate3,Quest Title
-    89001,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,"Reputation Quest"
-    89002,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,"Reputation Quest"
+```yml
+    - Id: 89001
+      Title: "Reputation Quest"
+    - Id: 89002
+      Title: "Reputation Quest"
+```
 
 
 

+ 36 - 1
doc/script_commands.txt

@@ -1229,6 +1229,12 @@ Don't expect things to run smoothly if you don't make your scripts 'end'.
 
 ---------------------------------------
 
+*close3;
+
+The command is similar to 'close' but the cutin (if any) is cleared after closing.
+
+---------------------------------------
+
 *end;
 
 This command will stop the execution for this particular script. The two
@@ -10276,7 +10282,7 @@ server and the egg will disappear when anyone tries to hatch it.
 This function will return pet information for the pet the invoking character
 currently has active. Valid types are:
 
- PETINFO_ID - Pet ID
+ PETINFO_ID - Pet unique ID
  PETINFO_CLASS - Pet class number as per 'db/(pre-)re/pet_db.yml' - will tell you what kind of a pet it is.
  PETINFO_NAME - Pet name. Will return "null" if there's no pet.
  PETINFO_INTIMATE - Pet friendly level (intimacy score). 1000 is full loyalty.
@@ -10958,6 +10964,29 @@ If <char id> is specified, the specified player is used rather than the attached
 
 ---------------------------------------
 
+*itemlink(<item_id>,<refine>,<card0>,<card1>,<card2>,<card3>,<enchantgrade>{,<RandomIDArray>,<RandomValueArray>,<RandomParamArray>});
+
+Generates an item link string for an item that can be used for npctalk, message,
+dispbottom, and broadcast commands. The result is a clickable-item name just
+like SHIFT+Click from a player's inventory/cart/equipment window. This command can be
+used with mes but the item name will not be clickable. You should use the normal client
+tags for displaying item links in mes dialogues, if the client supports them. 
+
+
+Examples:
+
+	npctalk "Knife [3] : "+itemlink(1201)+"";
+	npctalk "+16 Knife [3] : "+itemlink(1201,16)+"";
+	npctalk "+13 BXB Bapho+VR+EA2+EA1 : "+itemlink(18110,13,4147,4407,4833,4832)+"";
+	setarray .@opt_ids[0],RDMOPT_VAR_ATKPERCENT,RDMOPT_VAR_ATKPERCENT,RDMOPT_VAR_ATTMPOWER,0,0;
+	setarray .@opt_values[0],3,5,20,0,0;
+	setarray .@opt_params[0],0,0,0,0,0;
+	npctalk "+13 BXB Bapho+VR+EA2+EA1 + 3 Options : "+itemlink(18110,13,4147,4407,4833,4832,0,.@opt_ids,.@opt_values,.@opt_params)+"";
+
+
+RandomIDArray, RandomValueArray, and RandomParamArray only works if the
+client (and server) supports the Item Random Options feature (PACKETVER >= 20150225).
+
 ========================
 |14.- Channel commands.|
 ========================
@@ -11227,3 +11256,9 @@ Returns fame rank (start from 1 to MAX_FAME_LIST), else 0.
 Note: Only works with classes that use the ranking system.
 
 ---------------------------------------
+
+*isdead({<account id>})
+
+Returns true if the player is dead else false.
+
+---------------------------------------

+ 4 - 4
function.sh

@@ -4,7 +4,7 @@ M_SRV=map-server
 W_SRV=web-server
 INST_PATH=/opt
 PKG=rathena
-PKG_PATH=$INST_PATH/$PKG
+PKG_PATH="${INST_PATH}/${PKG}"
 
 check_files() {
     for i in ${L_SRV} ${C_SRV} ${M_SRV} ${W_SRV}
@@ -17,11 +17,11 @@ check_files() {
 }
 
 check_inst_right(){
-    if [ ! -w $INST_PATH ]; then echo "You must have sudo right to use this install (write/read permission in /opt/ )" && exit; fi
+    if [ ! -w "${INST_PATH}" ]; then echo "You must have sudo right to use this install (write/read permission in ${INST_PATH}/ )" && exit; fi
 }
 
 inst_launch_workaround(){
-  if [ -d $PKG_PATH ]; then
-    if [ $(pwd) != $PKG_PATH ]; then cd $PKG_PATH; fi
+  if [ -d "${PKG_PATH}" ]; then
+    if [ "$(pwd)" != "${PKG_PATH}" ]; then cd "${PKG_PATH}"; fi
   fi
 }

+ 21 - 21
install.sh

@@ -6,12 +6,12 @@
 
 # NOTE: This requires GNU getopt.  On Mac OS X and FreeBSD, you have to install this
 # separately; see below.
-TEMP=`getopt -o d: -l destdir: -- "$@"`
+TEMP=$(getopt -o d: -l destdir: -- "$@")
 if [ $? != 0 ] ; then echo "Terminating..." >&2 ; exit 1 ; fi
 # Note the quotes around `$TEMP': they are essential!
-eval set -- "$TEMP"
+eval set -- "${TEMP}"
 
-eval set -- "$TEMP"
+eval set -- "${TEMP}"
 while [ $# -gt 0 ]
 do
     case "$1" in
@@ -20,26 +20,26 @@ do
     shift
 done
 
-echo "destdir = $PKG_PATH "
+echo "destdir = ${PKG_PATH} "
 check_inst_right
 check_files
-mkdir -p $PKG_PATH/bin/
-mkdir -p $PKG_PATH/etc/$PKG/
-mkdir -p $PKG_PATH/usr/$PKG/
-mkdir -p $PKG_PATH/var/$PKG/log
+mkdir -p "${PKG_PATH}/bin/"
+mkdir -p "${PKG_PATH}/etc/${PKG}/"
+mkdir -p "${PKG_PATH}/usr/${PKG}/"
+mkdir -p "${PKG_PATH}/var/${PKG}/log"
 
 #we copy all file into opt/ dir and treat dir like normal unix arborescence
-cp -r db/ $PKG_PATH/var/$PKG/db
-if [ -d log ]; then cp -r log/* $PKG_PATH/var/$PKG/log/; fi
-cp -r conf/ $PKG_PATH/etc/$PKG/conf
-cp -r npc/ $PKG_PATH/usr/$PKG/npc
-cp athena-start $PKG_PATH/
-cp *-server* $PKG_PATH/bin/
+cp -r db/ "${PKG_PATH}/var/${PKG}/db"
+if [ -d log ]; then cp -r log/* "${PKG_PATH}/var/${PKG}/log/"; fi
+cp -r conf/ "${PKG_PATH}/etc/${PKG}/conf"
+cp -r npc/ "${PKG_PATH}/usr/${PKG}/npc"
+cp athena-start "${PKG_PATH}/"
+cp *-server* "${PKG_PATH}/bin/"
 
-ln -fs $PKG_PATH/var/$PKG/db/ $PKG_PATH/db
-ln -fs $PKG_PATH/var/$PKG/log/ $PKG_PATH/log
-ln -fs $PKG_PATH/etc/$PKG/conf/ $PKG_PATH/conf
-ln -fs $PKG_PATH/usr/$PKG/npc/ $PKG_PATH/npc
-ln -fs $PKG_PATH/athena-start /usr/bin/$PKG
-for f in $(ls $PKG_PATH/bin/) ; do ln -fs $PKG_PATH/bin/$f $PKG_PATH/$f; done
-echo "Installation is done. You can now control the server with '$PKG start'"
+ln -fs "${PKG_PATH}/var/${PKG}/db/" "${PKG_PATH}/db"
+ln -fs "${PKG_PATH}/var/${PKG}/log/" "${PKG_PATH}/log"
+ln -fs "${PKG_PATH}/etc/${PKG}/conf/" "${PKG_PATH}/conf"
+ln -fs "${PKG_PATH}/usr/${PKG}/npc/" "${PKG_PATH}/npc"
+ln -fs "${PKG_PATH}/athena-start" "/usr/bin/${PKG}"
+for f in $(ls "${PKG_PATH}/bin/") ; do ln -fs "${PKG_PATH}/bin/${f}" "${PKG_PATH}/${f}"; done
+echo "Installation is done. You can now control the server with '${PKG} start'"

+ 1 - 1
npc/events/dumplingfestival.txt

@@ -53,7 +53,7 @@ L_SORRY:
   
 M_FIN:
 	mes "[Exorcist Master Fahae]";
-	mes "The path of fully venquishing evil is far, help me in the way of God.";
+	mes "The path of fully vanquishing evil is far, help me in the way of God.";
 	close;
   
 M_HERB:

+ 27 - 0
npc/mapflag/nodynamicnpc.txt

@@ -0,0 +1,27 @@
+//===== rAthena Script =======================================
+//= Mapflag: No dynamic NPC map setting.
+//===== Description: ========================================= 
+//= Blocks the use of dynamic NPCs on a map.
+//===== Additional Comments: ================================= 
+//= 1.0 Initial script. [Lemongrass]
+//============================================================
+
+// Endless Tower
+1@tower	mapflag	nodynamicnpc
+2@tower	mapflag	nodynamicnpc
+3@tower	mapflag	nodynamicnpc
+4@tower	mapflag	nodynamicnpc
+5@tower	mapflag	nodynamicnpc
+6@tower	mapflag	nodynamicnpc
+
+// Sealed Catacomb
+1@cata	mapflag	nodynamicnpc
+2@cata	mapflag	nodynamicnpc
+
+// Orc's Memory
+1@orcs	mapflag	nodynamicnpc
+2@orcs	mapflag	nodynamicnpc
+
+// Nidhoggur's Nest
+1@nyd	mapflag	nodynamicnpc
+2@nyd	mapflag	nodynamicnpc

+ 0 - 9
npc/other/Global_Functions.txt

@@ -752,15 +752,6 @@ function	script	F_GetPlatinumSkills	{
 	return;
 }
 
-//////////////////////////////////////////////////////////////////////////////////
-// Shortcut : close button and clear cutin.
-//////////////////////////////////////////////////////////////////////////////////
-function	script	close3	{
-	close2;
-	cutin "",255;
-	end;
-}
-
 //////////////////////////////////////////////////////////////////////////////////
 // Return true if the card is a charm (enchant card), false otherwise.
 //////////////////////////////////////////////////////////////////////////////////

+ 1 - 1
npc/quests/quests_13_1.txt

@@ -7481,7 +7481,7 @@ OnTouch:
 	mes "Oh, please don't disturb";
 	mes "it's sleep. It hates that.";
 	mes "By the way, doesn't it";
-	mes "remind you of somehing?";
+	mes "remind you of something?";
 	mes "I mean, like maybe";
 	mes "a vagrant or a hobo?";
 	close;

+ 1 - 1
npc/re/cities/dewata.txt

@@ -366,7 +366,7 @@ dewata,95,203,6	script	Travel Guide#dew	536,{
 	emotion ET_PROFUSELY_SWEAT;
 	next;
 	mes "[Guide]";
-	mes "Even in modern times Borobudur temple is a pilgimage site for many monks worshiping the Buddha. It's also a major tourist attraction, as you can imagine.";
+	mes "Even in modern times Borobudur temple is a pilgrimage site for many monks worshiping the Buddha. It's also a major tourist attraction, as you can imagine.";
 	emotion ET_PROFUSELY_SWEAT;
 	next;
 	mes "[Guide]";

+ 1 - 1
npc/re/instances/EclageInterior.txt

@@ -266,7 +266,7 @@ OnMyMobDead:
 	mes "[Avant]";
 	mes "Did something happen to you?";
 	mes "There was a subtle and magical echo to it.";
-	mes "Immidiately after that, you were thrown here.";
+	mes "Immediately after that, you were thrown here.";
 	next;
 	if (select("It's all because of you!:Shenime said...") == 1) {
 		mes "[Avant]";

+ 1 - 1
npc/re/instances/PoringVillage.txt

@@ -533,7 +533,7 @@ OnMobDead:
 	killmonster 'map_begi$, instance_npcname("poring_village_2") + "::OnMobDead";
 	mapannounce 'map_begi$, "Emily: Hmm.. here is another Boss... It looks strong...", bc_map, 0xFFFF00;
 	sleep 2000;
-	mapannounce 'map_begi$, "Emily: I'm not good at battle.. Let me look for tresure first. See you soon~", bc_map, 0xFFFF00;
+	mapannounce 'map_begi$, "Emily: I'm not good at battle.. Let me look for treasure first. See you soon~", bc_map, 0xFFFF00;
 	sleep 2000;
 	monster 'map_begi$,42,173,"--en--","MD_AMERING",1, instance_npcname("poring_village_2") + "::OnBossDead";
 	end;

+ 140 - 0
npc/re/mapflag/nodynamicnpc.txt

@@ -0,0 +1,140 @@
+//===== rAthena Script =======================================
+//= Mapflag: No dynamic NPC map setting.
+//===== Description: ========================================= 
+//= Blocks the use of dynamic NPCs on a map.
+//===== Additional Comments: ================================= 
+//= 1.0 Initial script. [Lemongrass]
+//============================================================
+
+// Mistwood Maze
+1@mist	mapflag	nodynamicnpc
+
+// Culvert
+1@pump	mapflag	nodynamicnpc
+2@pump	mapflag	nodynamicnpc
+
+// Octopus Cave
+1@cash	mapflag	nodynamicnpc
+
+// Bangungot Hospital 2F
+1@ma_h	mapflag	nodynamicnpc
+
+// Buwaya Cave
+1@ma_c	mapflag	nodynamicnpc
+
+// Bakonawa Lake
+1@ma_b	mapflag	nodynamicnpc
+
+// Wolfchev's Laboratory
+1@lhz	mapflag	nodynamicnpc
+
+// Old Glast Heim
+1@gl_k	mapflag	nodynamicnpc
+2@gl_k	mapflag	nodynamicnpc
+
+// Eclage Interior
+1@ecl	mapflag	nodynamicnpc
+
+// Sara's Memories
+1@sara	mapflag	nodynamicnpc
+
+// Geffen Magic Tournament
+1@gef	mapflag	nodynamicnpc
+1@gef_in	mapflag	nodynamicnpc
+1@ge_st	mapflag	nodynamicnpc
+
+// Horror Toy Factory
+1@xm_d	mapflag	nodynamicnpc
+
+// Faceworm's Nest
+1@face	mapflag	nodynamicnpc
+
+// Ghost Palace
+1@spa	mapflag	nodynamicnpc
+
+// Devil's Tower
+1@tnm1	mapflag	nodynamicnpc
+1@tnm2	mapflag	nodynamicnpc
+1@tnm3	mapflag	nodynamicnpc
+
+// Assault on the Airship
+1@air1	mapflag	nodynamicnpc
+1@air2	mapflag	nodynamicnpc
+
+// Fenrir and Sarah
+1@glast	mapflag	nodynamicnpc
+
+// Wave Mode - Forest
+1@def01	mapflag	nodynamicnpc
+
+// Wave Mode - Sky
+1@def02	mapflag	nodynamicnpc
+
+// Nightmarish Jitterbug
+1@jtb	mapflag	nodynamicnpc
+
+// Isle of Bios
+1@dth1	mapflag	nodynamicnpc
+1@dth2	mapflag	nodynamicnpc
+1@dth3	mapflag	nodynamicnpc
+
+// Morse's Cave
+1@rev	mapflag	nodynamicnpc
+
+// Temple of the Demon God
+1@eom	mapflag	nodynamicnpc
+
+// Central Laboratory
+1@lab	mapflag	nodynamicnpc
+
+// Last room
+1@uns	mapflag	nodynamicnpc
+
+// Charleston in Distress
+1@mcd	mapflag	nodynamicnpc
+
+// Ritual of Blessing
+2@mir	mapflag	nodynamicnpc
+
+// Room of Consciousness
+1@mir	mapflag	nodynamicnpc
+
+// Sky Fortress Invasion
+1@sthb	mapflag	nodynamicnpc
+1@sthc	mapflag	nodynamicnpc
+1@sthd	mapflag	nodynamicnpc
+
+// Heart Hunter War Base 1 + 2
+1@swat	mapflag	nodynamicnpc
+
+// Werner Laboratory central room#1 + 2
+1@slw	mapflag	nodynamicnpc
+
+// Infinite Space
+1@infi	mapflag	nodynamicnpc
+
+// Regenschirm
+1@rgsr	mapflag	nodynamicnpc
+
+// Sealed OS
+1@os_b	mapflag	nodynamicnpc
+
+// OS Occupation + 2nd OS Search
+1@os_a	mapflag	nodynamicnpc
+
+// Cor Memorial
+1@cor	mapflag	nodynamicnpc
+
+// Half Moon In The Daylight
+1@pop1	mapflag	nodynamicnpc
+1@pop2	mapflag	nodynamicnpc
+1@pop3	mapflag	nodynamicnpc
+
+// Weekend Dungeon
+1@md_pay	mapflag	nodynamicnpc
+
+// Friday Dungeon
+1@md_gef	mapflag	nodynamicnpc
+
+// Poring Village
+1@begi	mapflag	nodynamicnpc

+ 1217 - 0
npc/re/quests/HelpMeShorty.txt

@@ -0,0 +1,1217 @@
+//===== rAthena Script =======================================
+//= Dynamic NPC: HelpMeShorty.
+//===== Description: =========================================
+//- [Walkthrough conversion]
+//= Short quest to allow the player to summon, via an item, a dynamic NPC that gives basic game information.
+//= After level 30, the player can get a costume in exchange for the item.
+//===== Changelogs: ==========================================
+//= 1.0 First version. [Capuche]
+//============================================================
+
+// Starter
+izlude,173,210,3	script	Black Shadow#fgtg01	4_CENERE,5,5,{
+	if (isbegin_quest(14688) == 0 && BaseLevel < 31) {
+		mes "[Black Shadow]";
+		mes "Take me! You have to take responsibility for me for making eye contact!";
+		next;
+		mes "[Black Shadow]";
+		mes "If you take me, ^0000cdI'll help too^000000. I'll really help you in many ways! I'll teach you ^0000cda lot of things^000000!!";
+		next;
+		mes "[Shorty Notice]";
+		mes "^EE0000If you choose to accompany Shorty, the item ^0000cd<Help Me Shorty>^EE0000 will be automatically placed in your inventory.";
+		next;
+		mes "[Shorty Notice]";
+		mes "If you use the <Help Me Shorty> item in your ^EE0000inventory, you can summon Shorty and get ^0000cdhelp and advice^EE0000 from it.";
+		next;
+		mes "[Shorty Notice]";
+		mes "^EE0000When you reach level 31 or higher, you can receive a quest that takes you to Shorty. Completing this quest will send you away and receive a reward at the same time.";
+		next;
+		mes "[Shorty Notice]";
+		mes "^EE0000Before reaching level 31, you can send Shorty away at any time via the ^0000cd<Let's Break Up>^EE0000 menu, but you will never be able to make Shorty your ally again after that.";
+		next;
+		if (select( "Well, let's go together.", "I don't want to go together." ) == 2) {
+			mes "[Black Shadow]";
+			mes "Can you do this without my help? You will surely not be able to walk ten steps and think of me. Isn't life about helping each other? It can't be like this.";
+			close;
+		}
+		mes "[Black Shadow]";
+		mes "Phew- I'm glad. Are we friends now?";
+		next;
+		mes "[Black Shadow]";
+		mes "By the way, what's with those eyes...? Are you afraid of me? I was doing this to stand out, wait a minute...";
+		next;
+		cloakonnpcself();
+		cloakoffnpcself("Mysterious Creature#fgtg01" );
+		mes "[Black Shadow]";
+		mes "Is this enough?";
+		next;
+		mes "[Shorty]";
+		mes "My name is Shorty!!";
+		next;
+		mes "[Shorty]";
+		mes "What is your name?";
+		next;
+		select( "My name is " + strcharinfo(0) + "." );
+		mes "[Shorty]";
+		mes "It's an easy name to say. Do you have a bag? That's the one you wear around your waist. It's just the right size for me to fit in.";
+		next;
+		select( "You want to go in the bag?" );
+		mes "[Shorty]";
+		mes "I can fit in your bag. What are you so surprised about? It won't be long. I'll be leaving soon.";
+		next;
+		mes "[Shorty]";
+		mes "Press ^0000cdALT+E^000000 to see the inside of the bag, press the consumption tab and I will be right there.";
+		next;
+		mes "[Shorty]";
+		mes "When you want to call me, press ^0000cd<Help Me Shorty>^000000 twice quickly and I'll come out of your bag and help you. How about it? Isn't it great?";
+		next;
+		mes "[Shorty]";
+		mes "If you don't like me in the bag, take me to ^0000cdSalty^000000. That's my destination, so when the time comes, I will leave ^0000cdyour bag^000000.";
+		next;
+		select( "Where is that person?" );
+		mes "[Shorty]";
+		mes "I'll tell you later. I want to rest for now.";
+		next;
+		mes "[Shorty]";
+		mes "Good luck in the future!";
+		setquest 14688;
+		completequest 14688;
+		getitem 23278,1;	// HelpmeShorty
+		cloakonnpcself("Mysterious Creature#fgtg01" );
+		close;
+	}
+	end;
+
+OnTouch:
+	if (isbegin_quest(14688) == 0 && BaseLevel < 31)
+		npctalk "Save me! Hug me quickly! Save me!", "", bc_self;
+	end;
+
+OnInit:
+	questinfo( QTYPE_QUEST, QMARK_YELLOW, "isbegin_quest(14688) == 0 && BaseLevel < 31" );
+	cloakonnpc();
+	end;
+}
+
+//izlude,173,210,3	script	Mysterious Creature#fgtg01	4_GALAPAGO,5,5,{	// unknown OnTouch effect
+izlude,173,210,3	duplicate(dummy_cloaked_npc)	Mysterious Creature#fgtg01	4_GALAPAGO
+
+izlude,161,199,0	script	#fgtg01	HIDDEN_WARP_NPC,3,5,{
+	end;
+OnTouch:
+	if (isbegin_quest(14688) == 0 && BaseLevel < 31)
+		cloakoffnpcself( "Black Shadow#fgtg01" );
+	end;
+}
+izlude,194,209,0	duplicate(#fgtg01)	#fgtg02	HIDDEN_WARP_NPC,3,5
+izlude,173,210,0	duplicate(#fgtg01)	#fgtg03	HIDDEN_WARP_NPC,3,5
+
+
+// Exchange npc. Step 1 of 2
+payon_in01,193,23,3	script	Chanmo#fgtg01	1_F_ORIENT_01,{
+	if (isbegin_quest(14693) == 1) {
+		cloakoffnpcself( "Shorty#fgtg004" );
+		mes "[Shorty]";
+		mes "Salty! Salty~ I'm here~ Did you wait? Did you wait a long time?";
+		next;
+		mes "[In the inn]";
+		mes "......";
+		next;
+		mes "[Shorty]";
+		mes "Salty, Salty? Salty? Where have you been Salty?";
+		next;
+		mes "[Chanmo]";
+		mes "Who are you talking about?";
+		next;
+		mes "[Shorty]";
+		mes "I came to find Salty.. I came, but there is no Salty.";
+		next;
+		mes "[Chanmo]";
+		mes "Salty? Ah, you're talking about the woman who is staying in the room. I sent her on an errand.";
+		next;
+		mes "[Shorty]";
+		mes "Errands? Why?";
+		next;
+		mes "[Chanmo]";
+		mes "I'm bored. I sent her to get some jelly and acorns.";
+		next;
+		mes "[Shorty]";
+		mes "Hey! Where are you coming from? We came to see Salty, why did you send Salty away~";
+		next;
+		mes "[Chanmo]";
+		mes "I didn't force anyone to come, and I pulled Zug's head and rolled it over. So I sent him out.";
+		next;
+		mes "[Shorty]";
+		mes "Then can I wait here?";
+		next;
+		mes "[Chanmo]";
+		mes "If you're in a hurry, you can help. You can find the";
+		mes "<NAVI>[Coco's nest]<INFO>prt_fild10,318,191,0,101,0</INFO></NAVI> in the south-southwest of Prontera. You can get a bunch of acorns from Coco.";
+		next;
+		mes "[Shorty]";
+		mes "Hm.. Alright. Master, let's go too. To the Coco's nest.";
+		next;
+		select("Are you here?");
+		mes "[Shorty]";
+		mes "I miss Salty. Let's go see Salty.";
+		next;
+		select("Okay then.");
+		mes "[Shorty]";
+		mes "Well then, let's go!!!";
+		cloakonnpcself("Shorty#fgtg004");
+		completequest 14693;
+		setquest 14694;
+		close;
+	}
+	mes "[Chanmo]";
+	mes "I need to pick some acorn and jelly today.";
+	close;
+}
+
+payon_in01,190,24,5	duplicate(dummy_cloaked_npc)	Shorty#fgtg004	4_GALAPAGO
+
+
+// Exchange npc. Step 2 of 2
+prt_fild10,318,191,0	script	#salt01	HIDDEN_WARP_NPC,3,5,{
+	end;
+OnTouch:
+	if (isbegin_quest(14694) == 1)
+		cloakoffnpcself( "Salty#fgtg01" );
+	end;
+}
+prt_fild10,262,39,0	duplicate(#salt01)	#salt02	HIDDEN_WARP_NPC,3,5
+prt_fild10,332,128,0	duplicate(#salt01)	#salt03	HIDDEN_WARP_NPC,3,5
+prt_fild10,222,293,0	duplicate(#salt01)	#salt04	HIDDEN_WARP_NPC,3,5
+// 5?
+prt_fild10,26,196,0	duplicate(#salt01)	#salt06	HIDDEN_WARP_NPC,3,5
+prt_fild10,311,167,0	duplicate(#salt01)	#salt07	HIDDEN_WARP_NPC,3,5
+prt_fild10,298,212,0	duplicate(#salt01)	#salt08	HIDDEN_WARP_NPC,3,5
+
+
+prt_fild10,318,191,3	script	Salty#fgtg01	4_F_ALCHE_A,{
+	if (isbegin_quest(14694) == 1) {
+		mes "[Salty]";
+		mes "Alas! Acorns! Muk-muk, acorn jelly! What do you do with acorns? You eat acorn jelly~";
+		next;
+		cloakoffnpcself( "Shorty#fgtg005" );
+		mes "[Shorty]";
+		mes "You look excited, Salty. Were you happy without me?";
+		next;
+		mes "[Salty]";
+		mes "Huh? Uh-huh??? Shorty!!!!!!!!!!";
+		next;
+		mes "[Shorty]";
+		mes "Did you have fun without me? Did you have fun without me...";
+		next;
+		mes "[Salty]";
+		mes "Shorty, why are you here? One day I received a letter saying you were leaving!!!";
+		next;
+		mes "[Shorty]";
+		mes "There are a number of circumstances. This situation will be explained by my master here.";
+		next;
+		mes "[Salty]";
+		mes "What? You kidnapped our Shorty??";
+		next;
+		select( "No, that's not it..." );
+		mes "[Salty]";
+		mes "Oh yeah? But Shorty! What's going on here?";
+		next;
+		mes "[Shorty]";
+		mes "I went to Payon and came here because the grandmother who cooks muk said you went here.";
+		next;
+		mes "[Salty]";
+		mes "Okay! I'm on the verge of going too, but let's stop at Prontera for a while and then go to Payon. Let's go pack up some stuff and travel the continent!";
+		next;
+		mes "[Shorty]";
+		mes "Yeah! Master, the adventures we had together were fun!";
+		next;
+		mes "[Salty]";
+		mes "I have to say thank you. It looks like you were carrying Shorty in that little bag.";
+		next;
+		mes "[Shorty]";
+		mes "Master, thank you so much.";
+		next;
+		select( "Are you leaving?" );
+		mes "[Shorty]";
+		mes "Our relationship will not end here. Remember that you met me. Salty, is there anything else? Something to remember.";
+		next;
+		mes "[Salty]";
+		mes "Uh-huh, something like that.";
+		next;
+		mes "[Shorty]";
+		mes "Is this all? Penguin? Penguin? Do you like penguins better than me?";
+		next;
+		mes "[Salty]";
+		mes "No, it's not... Originally I bought it in Paros to remember you, but now that I think about it, you're not a penguin, so I just kept it. Isn't it cute?";
+		next;
+		mes "[Shorty]";
+		mes "Cute.";
+		next;
+		mes "[Salty]";
+		mes "I'll give this to you. Thanks for taking care of Shorty! Shorty, let's go!";
+		next;
+		specialeffect EF_TELEPORTATION2;
+		cloakonnpcself();
+		mes "[Shorty]";
+		mes "Master, even if a lot of time passes, don't forget me and become a great adventurer who makes a name for yourself on the continent!";
+		specialeffect EF_TELEPORTATION2, AREA, "Shorty#fgtg005";
+		cloakonnpcself( "Shorty#fgtg005" );
+		erasequest 14694;
+		if (isbegin_quest(14695) > 0)
+			erasequest 14695;
+		delitem 23278,1;	// HelpmeShorty
+		getitem 20267,1;	// C_Penguin_Cap
+		getexp 4000,1000;
+		close;
+	}
+	end;
+
+OnInit:
+	questinfo( QTYPE_QUEST, QMARK_YELLOW, "isbegin_quest(14694) == 1" );
+	cloakonnpc();
+	end;
+}
+
+prt_fild10,315,189,7	duplicate(dummy_cloaked_npc)	Shorty#fgtg005	4_GALAPAGO
+
+
+// Base for the dynamic npc
+izlude,1,1,0	script	Shorty#fgtg01	4_GALAPAGO,{
+	if (isbegin_quest(14689) == 0) {
+		mes "[Shorty]";
+		mes "Good job. If you need my help in the future, you can call me like this.";
+		next;
+		select( "How can you help me?" );
+		mes "[Shorty]";
+		mes "After arriving here for the first time you didn't know anything, right? I'll teach you everything I know. ^0000cdAsk me anything!^000000";
+		next;
+		mes "[Shorty]";
+		mes "And I'll provide you with various good ^0000cdservices^000000 to help you with hunting.";
+		next;
+		mes "[Shorty]";
+		mes "The only thing I can do is ^0000cdincrease agility^000000 to move quickly, and ^0000cdblessing^000000 to increase strength, dexterity and intelligence, but it will definitely help, right?";
+		next;
+		mes "[Shorty]";
+		mes "Yeah! If you call me and don't say anything for a minute, I'll go back into the bag.";
+		next;
+		mes "[Shorty]";
+		mes "And when the business is over, just leave me be. I'll look around for you and go in the bag. I follow along very well.";
+		next;
+		select( "When are you going to find someone named Salty?" );
+		mes "[Shorty]";
+		mes "Not now, master. This Shorty, from the Galapagos knows grace. When you can take off your starter tee and start the adventure on your own, Shorty then goes away.";
+		next;
+		mes "[Shorty]";
+		mes "At that time, even if you hold on to me and cry, I will leave you. Before that, you have to increase your level to about ^0000cdlevel 31^000000. Until then, let's do our best in the future.";
+		setquest 14689;
+		completequest 14689;
+		next;
+	}
+	.@state = isbegin_quest(14693);
+	.@state = ((.@state == 2 && isbegin_quest(14694) == 1) ? 1 : .@state);
+
+	switch( .@state ) {
+	case 0:
+		if (BaseLevel < 31)
+			break;
+		mes "[Shorty]";
+		mes "You has grown wonderfully. So now I want to meet Salty. She'll be worried about me.";
+		next;
+		select( "Now did you get that idea?" );
+		mes "[Shorty]";
+		mes "The last letter from Salty said it was from ^0000cdPayon's inn^000000.";
+		next;
+		mes "[Shorty]";
+		mes "Let's go";
+		mes "find the <NAVI>[Inn]<INFO>payon_in01,193,23,0,101,0</INFO></NAVI> in Payon.";
+		setquest 14693;
+		close;
+	case 1:
+		break;
+	case 2:
+		mes "[Shorty]";
+		mes "Who are you? Who are you talking to me? I don't like people I don't know talking to or touching me.";
+		close;
+	}
+	if (BaseLevel < 31) {
+		switch( rand(4) ) {
+		case 0:
+			mes "[Shorty]";
+			mes "Open the item window and you will see ^0000cdfour tabs^000000. Can you see it?";
+			next;
+			mes "[Shorty]";
+			mes "If you drag and drop items that are not used frequently or should not be sold to NPCs, put them in the ^0000cd<Personal tab>, and press the trade lock button^000000 at the bottom, you will not accidentally sell these items.";
+			break;
+		case 1:
+			mes "[Shorty]";
+			mes "Master, have you ever pressed the ^0000cdF12 key^000000? Small white squares will float around.";
+			next;
+			mes "[Shorty]";
+			mes "Drag the skill icon or item there. If you release it, you can use skills and icons quickly and easily by simply pressing the ^0000cdshortcut button^000000. It's really convenient.";
+			break;
+		case 2:
+			mes "[Shorty]";
+			mes "He said that if you don't need the hats or clothes you got at the academy, you can exchange them for useful stuff at the ^0000cdvending machine at the job change office for each job^000000.";
+			next;
+			mes "[Shorty]";
+			mes "Salty also used it very well. I would like clams if possible.";
+			break;
+		case 3:
+			mes "[Shorty]";
+			mes "Let's leave after checking where the hunt was. There may be monster cards left.";
+			next;
+			mes "[Shorty]";
+			mes "This is a valuable tip that Salty taught me~ Do you have a monster card?";
+			break;
+		}
+		setarray .@menu$[0], "Give me strength!", "Where should I go?", "Isn't there anything good?", "How do I take skills and stats?", "Tell me how to operate." ;
+	}
+	else {
+		switch( rand(4) ) {
+		case 0:
+			mes "[Shorty]";
+			mes "Master, did you know that besides the Midgard continent known to us, you can go to new and mysterious places by boat? Mainly Al... Al... Allegro?";
+			next;
+			mes "[Shorty]";
+			mes "^0000cdAh! You can go by boat from Alberta^000000! Let's go later! Master! It's a new adventure!";
+			break;
+		case 1:
+			mes "[Shorty]";
+			mes "I heard that there are quite a few dungeons that even a young adventurer like yourself can go too.";
+			next;
+			select( "Why am I so stupid!" );
+			mes "[Shorty]";
+			mes "Master, what you're saying? Anyway, underground waterways, Payon's caves, ant dungeons, and shipwrecks. Don't you want to become the ruler of the dungeon, master?";
+			next;
+			select( "If you fight for me." );
+			mes "[Shorty]";
+			mes "Master, you should save your body because you only have one life.";
+			break;
+		case 2:
+			mes "[Shorty]";
+			mes "Is master ready? I really want to go!";
+			next;
+			select( "What is it? Where is it?" );
+			mes "[Shorty]";
+			mes "You don't know Ohiso, Boiso, Saiso, ^0000cdParamarket^000000? What would master do without me, really?";
+			next;
+			mes "[Shorty]";
+			mes "Paramarket is a place that sells items that are hard to find in the market or street stalls in limited quantities and for a limited time. It is a very good place to do business.";
+			next;
+			select( "How do I get there?" );
+			mes "[Shorty]";
+			mes "Talk to the Paramarket Windman in ^0000cdParadise Corridor^000000 and he will send you there.";
+			break;
+		case 3:
+			mes "[Shorty]";
+			mes "Master, sometimes when you want to buy something from a street vendor, you may wonder if they are selling it now and who is selling it.";
+			next;
+			select( "That's right. I can't even look through all the stalls." );
+			mes "[Shorty]";
+			mes "In that case, you can use the ^0000cdcatalog^000000. Currently, it is said that ^0000cdvending machines in Paradise or Alberta's merchant^000000 are selling it.";
+			next;
+			mes "[Shorty]";
+			mes "I want to go shopping today too.";
+			break;
+		}
+		setarray .@menu$[5], "Anything good?";
+	}
+	next;
+	mes "[Shorty]";
+	mes "Do you need more help from me?";
+	next;
+	switch( select( .@menu$[0], .@menu$[1], .@menu$[2], .@menu$[3], .@menu$[4], .@menu$[5 ], "Information guide", "I'm curious about what you're talking about.", "Break up now." ) ) {
+	case 1:
+		switch( checkquest(14695,PLAYTIME) ) {
+		case -1:
+			mes "[Shorty]";
+			mes "Master, I'll give you the help I promised you before. Increased agility and blessing.";
+			next;
+			mes "[Shorty]";
+			mes "But I learned this over the shoulder, and once I do it, my soul gets tired, so I can't give it indefinitely. Although it can take me half an hour to refresh myself.";
+			next;
+			mes "[Shorty]";
+			mes "If you want to know when you can get it again, open the quest window with ALT+U and check the time displayed at the bottom.";
+			next;
+			mes "[Shorty]";
+			mes "Go! Master!!";
+			npcskill "AL_INCAGI",10,99,60;
+			npcskill "AL_BLESSING",10,99,60;
+			setquest 14695;
+			close;
+		case 0:
+		case 1:
+			mes "[Shorty]";
+			mes "Master, it hasn't even been 30 minutes yet. I'm still struggling. I'll give you more strength if you get up, so please wait.";
+			close;
+		case 2:
+			mes "[Shorty]";
+			mes "Go! Master!!";
+			npcskill "AL_INCAGI",10,99,60;
+			npcskill "AL_BLESSING",10,99,60;
+			erasequest 14695;
+			setquest 14695;
+			close;
+		}
+		end;
+	case 2:
+		mes "[Shorty]";
+		mes "What would you like to learn about master?";
+		next;
+		switch( select( "The path of a novice adventurer!", "I want to help people around me.", "I don't like the set path.", "There is no place I want to go." ) ) {
+		case 1:
+			mes "[Shorty]";
+			mes "I think I saw one ^0000cd<Thinking Archer>^000000 teacher near the entrance to Izlude, but he seemed to have a lot of thoughts. Why don't you go and ask what's going on? Let's go, let's go~";
+			close;
+		case 2:
+			mes "[Shorty]";
+			mes "Why don't you meet Paul and Anne at the outside ^0000cdProntera's West Gate ^000000? As I was passing by, I heard that they needed help.";
+			next;
+			mes "[Shorty]";
+			mes "Or why not listen to the anxious middle-aged man in ^0000cdPayon^000000?";
+			close;
+		case 3:
+			mes "[Shorty]";
+			mes "Pressing ^0000cdCtrl+`^000000 will open the map.";
+			next;
+			mes "[Shorty]";
+			mes "Do you see a box for selecting a region in the upper left corner?";
+			next;
+			mes "[Shorty]";
+			mes "After selecting the area you want to see, check the ^0000cdmonster tab^000000 in the upper right corner or press the ^0000cdTab key^000000, you will see the name and level of the monster most distributed in that area.";
+			next;
+			mes "[Shorty]";
+			mes "It would be better to go on an adventure to a place that matches the level of master. If there is ^0000cda lot of level difference with monsters^000000, you can't get 100% of the experience. You have to calculate the level difference as well, right?";
+			next;
+			mes "[Shorty]";
+			mes "If you press the town you want to go to, the navigation menu will appear. Press the ^0000cd<Guide to the target> button at the bottom^000000 and exit the map to get guidance.";
+			close;
+		case 4:
+			mes "[Shorty]";
+			mes "Master, are you bored? Then you can chat with the people around you.";
+			next;
+			mes "[Shorty]";
+			mes "Other than that, it would be okay to try ^0000cdParadise's quest^000000. Do you want to go to Paradise? They say they will give you a weapon if you go.";
+			next;
+			mes "[Shorty]";
+			mes "Where do you want to go? Write ^0000cd/place^000000 in the chat window and compare your current location with the map that appears by pressing the ^0000cdCtrl+` keys^000000.";
+			next;
+			switch( select( "Prontera", "Geffen", "Payon", "Morroc", "Alberta", "Done" ) ) {
+			case 1:
+				mes "[Shorty]";
+				mes "Click on <NAVI>[Prontera]<INFO>prontera,124,76,0,101,0</INFO></NAVI> to find it.";
+				break;
+			case 2:
+				mes "[Shorty]";
+				mes "Click on <NAVI>[Geffen]<INFO>geffen,132,66,0,101,0</INFO></NAVI> to find it.";
+				break;
+			case 3:
+				mes "[Shorty]";
+				mes "Click on <NAVI>[Payon]<INFO>payon,177,111,0,101,0</INFO></NAVI> to find it.";
+				break;
+			case 4:
+				mes "[Shorty]";
+				mes "Click on <NAVI>[Morroc: Paradise Space Traveler]<INFO>morocc,161,97,0,101,0</INFO></NAVI> to find it.";
+				next;
+				mes "[Shorty]";
+				mes "Morroc is where Paradise is, so wouldn't it be ok to walk around?";
+				next;
+				mes "[Shorty]";
+				mes "Go to <NAVI>[Morroc Paradise]<INFO>morocc,206,285,0,101,0</INFO></NAVI>.";
+				break;
+			case 5:
+				mes "[Shorty]";
+				mes "Click on <NAVI>[Alberta]<INFO>alberta,124,67,0,101,0</INFO></NAVI> to find it.";
+				break;
+			case 6:
+				mes "[Shorty]";
+				mes "Okay? Then I'll look around a bit and get back into the bag.";
+				close;
+			}
+			next;
+			mes "[Shorty]";
+			mes "Go and register with Lime Ivenor first, then check the bulletin board next to it.";
+			next;
+			mes "[Shorty]";
+			mes "It would be nice to go around towns and fields to find and help people in need. Those who need help will find it easier to find with a <!> sign above their heads.";
+			close;
+		}
+		end;
+	case 3:
+		mes "[Shorty]";
+		mes "You didn't join the Paradise Club?";
+		next;
+		mes "[Shorty]";
+		mes "Do you want to go to ^0000cdParadise^000000? They say that if you go to Paradise, they will give you weapons.";
+		next;
+		mes "[Shorty]";
+		mes "Where do you want to go? Write /place in the chat window and compare your current location with the map that appears by pressing the ^0000cdCtrl+` keys^^000000. Where is the nearest?";
+		next;
+		switch( select( "Prontera", "Geffen", "Payon", "Morroc", "Alberta", "Done" ) ) {
+		case 1:
+			mes "[Shorty]";
+			mes "Click on <NAVI>[Prontera]<INFO>prontera,124,76,0,101,0</INFO></NAVI> to find it.";
+			break;
+		case 2:
+			mes "[Shorty]";
+			mes "Click on <NAVI>[Geffen]<INFO>geffen,132,66,0,101,0</INFO></NAVI> to find it.";
+			break;
+		case 3:
+			mes "[Shorty]";
+			mes "Click on <NAVI>[Payon]<INFO>payon,177,111,0,101,0</INFO></NAVI> to find it.";
+			break;
+		case 4:
+			mes "[Shorty]";
+			mes "Click on <NAVI>[Morroc: Paradise Space Traveler]<INFO>morocc,161,97,0,101,0</INFO></NAVI> to find it.";
+			next;
+			mes "[Shorty]";
+			mes "Morroc is where Paradise is, so wouldn't it be ok to walk around?";
+			next;
+			mes "[Shorty]";
+			mes "Go to <NAVI>[Morroc Paradise]<INFO>morocc,206,285,0,101,0</INFO></NAVI>.";
+			break;
+		case 5:
+			mes "[Shorty]";
+			mes "Click on <NAVI>[Alberta]<INFO>alberta,124,67,0,101,0</INFO></NAVI> to find it.";
+			break;
+		case 6:
+			mes "[Shorty]";
+			mes "Okay? Then I'll look around a bit and get back into the bag.";
+			close;
+		}
+		next;
+		mes "[Shorty]";
+		mes "^0000cdGo and register with Lime Ivenor first^000000, and then talk to the Boyana Ur and Paradise Logistics staff on either side.";
+		next;
+		mes "[Shorty]";
+		mes "After some quests and training, they gave me equipment.";
+		close;
+	case 4:
+		mes "[Shorty]";
+		mes "What are you curious about with status and skills?";
+		next;
+		switch( select( "I'm curious about skills.", "I'm curious about statuses.", "I'm not interested anymore." ) ) {
+		case 1:
+			mes "[Shorty]";
+			mes "Press the ^0000cdAlt+S keys^000000 to bring up the skill list window.";
+			next;
+			mes "[Shorty]";
+			mes "If you check the item ^0000cd<View skill description> in the upper right corner^000000, you can view the description of the skill when you hover the mouse over the skill icon.";
+			next;
+			mes "[Shorty]";
+			mes "You can learn this skill by consuming ^0000cd<Skill Points>^000000 that can be obtained whenever your job level increases. You can check the skill points you have in the lower left corner of this window.";
+			next;
+			mes "[Shorty]";
+			mes "Master currently has " + SkillPoint + " skill point " + (SkillPoint > 1 ? "s" : "") + ".";
+			next;
+			mes "[Shorty]";
+			mes "Does master know about the skill tree? If master hovers the mouse over the skill they want to learn, the skills and skill levels to learn first will appear.";
+			next;
+			mes "[Shorty]";
+			mes "That's the skill tree. However, the skill points consumed when learning a skill are limited, so you can't learn all the skills.";
+			next;
+			mes "[Shorty]";
+			mes "There are a total of 49 skill points that can be used for learning before master changes to a higher job. This condition is called stagnation.";
+			next;
+			mes "[Shorty]";
+			mes "^0000cdIf your job level is 40 you can change your job to a secondary job^000000, but it is recommended that you change jobs when you reach level 50.";
+			next;
+			mes "[Shorty]";
+			mes "Because it simplifies the transfer process. What greater reason could there be than this?";
+			next;
+			mes "[Shorty]";
+			mes "Don't worry too much if you think you've raised your skills incorrectly.";
+			next;
+			mes "[Shorty]";
+			mes "You can reset it at any time until master changes to a higher job.";
+			next;
+			mes "[Shorty]";
+			mes "Go to <NAVI>[Izlude: The Hypnotist]<INFO>izlude,127,175,0,101,0</INFO></NAVI> for guidance.";
+			close;
+		case 2:
+			mes "[Shorty]";
+			mes "Let's see, master...";
+			next;
+			mes "[Shorty]";
+			mes "I heard that for Taekwon, you should raise mainly Str, Agi, and Dex. For more information,";
+			mes "please visit <NAVI>[Payon: Ahri]<INFO>payon,245,295,0,101,0</INFO></NAVI>.";
+			next;
+			mes "[Shorty]";
+			mes "Don't worry too much if you think you've raised your status incorrectly.";
+			next;
+			mes "[Shorty]";
+			mes "You can reset it at any time until master changes to a higher job.";
+			mes "Go to <NAVI>[Izlude: The Hypnotist]<INFO>izlude,127,175,0,101,0</INFO></NAVI> for guidance.";
+			close;
+		case 3:
+			mes "[Shorty]";
+			mes "Call me anytime if you have any questions. I'll look around a bit and get back into the bag.";
+			close;
+		}
+		end;
+	case 5:
+		switch( select( "Basic Operation", "Shortcut Guide", "Cancel" ) ) {
+		case 1:
+			while(1) {
+				mes "[Shorty]";
+				mes "What are you curious about?";
+				next;
+				switch( select( "Move and Attack", "Talk with NPC", "Check Quest", "Check Status and Skills", "Unequip Equipment", "Trade" ) ) {
+				case 1:
+					mes "[Shorty]";
+					mes "^0000cd<Move>^000000 - Click the place you want to go ^0000cdwith the mouse^000000.";
+					mes "^0000cd<Attack>^000000 - Click the target ^0000cdto attack with the mouse^000000.";
+					next;
+					mes "[Shorty]";
+					mes "How is it? Simple, right? You can also move the viewpoint by holding down the right mouse button and rotating it.";
+					break;
+				case 2:
+					mes "[Shorty]";
+					mes "<Conversation> - Click the target ^0000cdto chat with the mouse^000000.";
+					next;
+					mes "[Shorty]";
+					mes "Sometimes you have to get close to talk to someone.";
+					break;
+				case 3:
+					mes "[Shorty]";
+					mes "Click the ^0000cdquest icon in the main menu located in the upper left corner.";
+					mes "Shortcut: ^0000cdAlt+U^000000";
+					next;
+					mes "[Shorty]";
+					mes "When master receives a quest, the quest information is briefly displayed on the right side of the screen, so you can check it.";
+					next;
+					mes "[Shorty]";
+					mes "If there is a quest you do not want to see in the list of quests, place the mouse pointer over the title and press the right mouse button to send it to the pending tab.";
+					break;
+				case 4:
+					mes "[Shorty]";
+					mes "<Check status> - Click the ^0000cdstatus icon in the main menu located at the top left corner^000000.";
+					mes "Shortcut: ^0000cdAlt+A^000000";
+					next;
+					mes "[Shorty]";
+					mes "<Check skill> - Click the ^0000cdskill icon in the main menu located at the top left corner^000000.";
+					mes "Shortcut: ^0000cdAlt+S^000000";
+					next;
+					mes "[Shorty]";
+					mes "Don't forget! The skill has to be activated by double clicking the icon you want to use and is casted by pressing the left mouse button once!";
+					break;
+				case 5:
+					mes "[Shorty]";
+					mes "<Equip and unequip equipment> - Click the ^0000cdequipment icon in the main menu located at the top left corner^000000.";
+					mes "Shortcut: ^0000cdAlt+Q^000000";
+					next;
+					mes "[Shorty]";
+					mes "Usually, you can equip and disarm by double-clicking the left mouse button in the inventory or equipment window.";
+					next;
+					mes "[Shorty]";
+					mes "Arrows and bullets can only be equipped by dragging them from the inventory to the character in the equipment window.";
+					next;
+					mes "[Shorty]";
+					mes "If master becomes a Merchant, you can pull a cart, which can be borrowed from the Kafra Staff in each town.";
+					break;
+				case 6:
+					mes "[Shorty]";
+					mes "<Store Trade> - Click on the merchant NPC ^0000cdto trade^000000.";
+					mes "<User Transaction> - Right-click the player you want to trade with and select ^0000cdTrade^000000.";
+					next;
+					mes "[Shorty]";
+					mes "<Buy and sell stalls> - Click the mouse on the stalls window.";
+					break;
+				}
+				next;
+			}
+			end;
+		case 2:
+			while(1) {
+				mes "[Shorty]";
+				mes "What are you curious about?";
+				next;
+				switch( select( "Tip of the Day", "Command List", "Skills and Items Shortcut Window", "Shortcut Key List and Registration", "Command List", "Emotion List" ) ) {
+				case 1:
+					mes "[Shorty]";
+					mes "Type ^0000cd/tip^000000 in the chat window.";
+					next;
+					mes "[Shorty]";
+					mes "A lot of life information is contained in this. Whenever I'm bored, I read these.";
+					break;
+				case 2:
+					mes "[Shorty]";
+					mes "Type ^0000cd/h^000000 in the chat window.";
+					next;
+					mes "[Shorty]";
+					mes "You can see the necessary information for Novice adventurers.";
+					break;
+				case 3:
+					mes "[Shorty]";
+					mes "Press the ^0000cdF12 key^000000 to register consumption items, skills, and equipment by dragging them to the shortcut window.";
+					next;
+					mes "[Shorty]";
+					mes "Did you know that quick item use and equipment replacement are essential for effective combat?";
+					break;
+				case 4:
+					mes "[Shorty]";
+					mes "Press the ^0000cdAlt+M keys^000000 to register frequently used shortcut commands~";
+					next;
+					mes "[Shorty]";
+					mes "I use this function to register frequently used commands.";
+					break;
+				case 5:
+					mes "[Shorty]";
+					mes "Press the ^0000cdAlt+Y keys^000000 to check the command list and turn it ON/OFF.";
+					next;
+					mes "[Shorty]";
+					mes "You can check and turn on/off various options by master's choice.";
+					break;
+				case 6:
+					mes "[Shorty]";
+					mes "Press the ^0000cdAlt+L keys^000000 to view and use the list of emotions and shortcut commands.";
+					next;
+					mes "[Shorty]";
+					mes "Master, do you know that conversation isn't just about words? Appropriate use of emotion makes the conversation richer.";
+					break;
+				}
+				next;
+			}
+			end;
+		case 3:
+			mes "[Shorty]";
+			mes "Yes. Call me again if you need me.";
+			close;
+		}
+		end;
+	case 6:
+		mes "[Shorty]";
+		mes "Master doesn't want to succeed? Salty says she's training hard because she wants to succeed.";
+		next;
+		mes "[Shorty]";
+		mes "The tradition is ^0000cdbeing born again in Valhalla and returning to Novice^000000. But it's not a normal Novice, it's called a High Novice.";
+		next;
+		mes "[Shorty]";
+		mes "In other words, it's evolution. People who have passed down say that there are many advantages to getting the job of the past again.";
+		next;
+		mes "[Shorty]";
+		mes "There are many statuses, and above all, if you have a secondary job, you can increase your ^0000cdjob level to 70^000000. You will be able to use more skills.";
+		next;
+		mes "[Shorty]";
+		mes "If you want to succeed, find a person named";
+		mes "<NAVI>[Metheus Sylph]<INFO>yuno_in02,88,164,0,101,0</INFO></NAVI> ^0000cdat Sage Academy in Yuno^000000, and he will guide you.";
+		next;
+		mes "[Shorty]";
+		mes "If you're not interested in transcending, you can just change jobs. But he said that it's better to pass it on when you think about the future.";
+		next;
+		mes "[Shorty]";
+		mes "If you are not interested in changing jobs or transcending...";
+		next;
+		mes "[Shorty]";
+		mes "The continent is full of festivals all year round. How about participating in the festival when master is also free?";
+		next;
+		mes "[Shorty]";
+		mes "Now...";
+		next;
+		mes "[Shorty]";
+		mes "I heard that there will be a winter hunting contest hosted by the royal family at the royal palace! I want to do a hunting contest! I want to do it! Fight monsters!!! Let's go to Prontera!!!!";
+		next;
+		mes "[Shorty]";
+		mes "I'll definitely take you with me, okay?";
+		close;
+	case 7:
+		mes "[Shorty]";
+		mes "Master, I'll tell you the basics, but literally just the basics.";
+		next;
+		mes "[Shorty]";
+		mes "For more information";
+		mes "try using";
+		mes "<URL>[Rune Midgarts Library]<INFO>http://ro.gnjoy.com/guide/runemidgarts/,1024,768</INFO></URL>";
+		next;
+		mes "[Shorty]";
+		mes "What information are you curious about?";
+		next;
+		switch( select("Location Information", "Quest Information", "Hunting Field Information", "System Information", "Cancel" ) ) {
+		case 1:
+			while(1) {
+				mes "[Shorty]";
+				mes "Where are you curious?";
+				next;
+				switch( select( "Midgard Continent", "Far-Star Continent", "Malangdo", "Local Area", "Otherworld", "I'm not interested" ) ) {
+				case 1:
+					mes "[Shorty]";
+					mes "^0000cdMidgard Continent^000000 is said to have the greatest influence of the three kingdoms of Arunafelts, Schwaldzwald, and Rune-Midgarts. Each continent has an airship you can readily use.";
+					next;
+					mes "[Shorty]";
+					mes "^0000cdRune-Midgarts^000000. A kingdom ruled by seven royal families and the power of magic prevails.";
+					next;
+					mes "[Shorty]";
+					mes "The capital is Prontera, the satellite city of Izlude, Geffen where wizards gather, Payon to the east, Alberta where you can go travel to different regions, Aldebaran with the clock tower, and Morroc.";
+					next;
+					mes "[Shorty]";
+					mes "We met at Izlude in Rune-Midgarts Kingdom. Remember? Also Payon with Salty, and Paros from where I left. They're all in Rune-Midgats.";
+					next;
+					mes "[Shorty]";
+					mes "^0000cdArunafelz^000000. This is a church that worships a goddess. So, there is a Pope. Of course, you can't meet anyone, but is it possible? Maybe we can meet at least once.";
+					next;
+					mes "[Shorty]";
+					mes "Arunafelz has a temple capital, Rachel, and a canyon town, Veins.";
+					next;
+					mes "[Shorty]";
+					mes "^0000cdSchwaldzwald^000000 Republic. As the name suggests, there is a president. The capital city, Yuno, is floating in the air, so it's worth a visit. It must be spectacular.";
+					next;
+					mes "[Shorty]";
+					mes "And there is Lighthalzen, where the headquarters of Rekenberg is located, there is the mining town of Einbroch, the mines of Einbech, and there is also the rural village of Hugel.";
+					next;
+					mes "[Shorty]";
+					mes "There are other Utan villages such as Umbala, or Niflheim, the land of the dead. It's a place I've always wanted to visit!";
+					break;
+				case 2:
+					mes "[Shorty]";
+					mes "Many people thought that there was only the continent of Midgard on this land, but one day, people appeared in the form of ^0000cdcats^000000. No one knew where they came from.";
+					next;
+					mes "[Shorty]";
+					mes "These people, called Doram, are from a place far across the sea, the ^0000cdFar-Star Continent^000000. They said that the road was opened for them only recently.";
+					next;
+					mes "[Shorty]";
+					mes "In the Far-Star Continent, the port city ^0000cdLasagna^000000 is the most famous. I want to go there too.";
+					next;
+					mes "[Shorty]";
+					mes "To get here, take the boat from Malangdo or Izlude. Do you remember where we got off?";
+					break;
+				case 3:
+					mes "[Shorty]";
+					mes "You've seen people in the form of cats, called Dorams, who come and go. They haven't had their first contact with this Midgard continent, but now they have taken root and established themselves everywhere.";
+					next;
+					mes "[Shorty]";
+					mes "It turns out that while sailing in a faraway place called the Far-Star continent, they met a storm and fell to a place called Malangdo. Literally, the ship fell from the sky.";
+					next;
+					mes "[Shorty]";
+					mes "Dorams have a lot of magical talents, so if you go to ^0000cdMalangdo^000000 you will have a good time.";
+					next;
+					mes "[Shorty]";
+					mes "^0000cdYou can take the boat^000000 from Izlude. If you have Canned Malangdo that can be obtained in a special way, you can use it to travel to there.";
+					break;
+				case 4:
+					mes "[Shorty]";
+					mes "If you want to feel the atmosphere of a foreign country, you should go to that ^0000cdlocal region^000000. ^0000cdIf you go to Alberta's dock^000000, you can meet the sailors who send people to each local region.";
+					next;
+					mes "[Shorty]";
+					mes "Amatsu, Kunlun, Moscovia, Brasilis, Ayothaya, Dewata, Port Malaya, Rock Ridge, etc. The unknown world is waiting for you.";
+					next;
+					mes "[Shorty]";
+					mes "Doesn't it make your heart flutter just by hearing the words? See you later, master.";
+					break;
+				case 5:
+					mes "[Shorty]";
+					mes "Uh, I know, here. If you go through the eastern part of Morroc or talk to Agent Catson in Paradise, they'll send you there.";
+					next;
+					mes "[Shorty]";
+					mes "You can meet people you've never seen before.";
+					next;
+					mes "[Shorty]";
+					mes "From the base of the Expeditionary Forces of the Three Kingdoms, you can go to Splendid, Eclage, Manuk or El Dicastes.";
+					break;
+				case 6:
+					mes "[Shorty]";
+					mes "Okay? Then I'll look around a bit and get back into the bag.";
+					close;
+				}
+				next;
+			}
+			end;
+		case 2:
+			while(1) {
+				mes "[Shorty]";
+				mes "What quest are you curious about?";
+				next;
+				switch( select( "General Quest", "Monthly Event", "Paradise", "I'm not interested" ) ) {
+				case 1:
+					mes "[Shorty]";
+					mes "^0000cdWhy don't you meet Paul and Anne at^000000 the outside Prontera's West Gate? As I was passing by, I heard that they needed help.";
+					next;
+					mes "[Shorty]";
+					mes "Or why not listen to the anxious middle-aged man from ^0000cdPayon^000000?";
+					next;
+					mes "[Shorty]";
+					mes "For more information on quests";
+					mes "see also";
+					mes "<URL>[Rune Midgarts Library]<INFO>http://ro.gnjoy.com/guide/runemidgarts/questlist.asp,1024,768</INFO></URL>";
+					next;
+					mes "[Shorty]";
+					mes "You will be able to learn about the flow of this world and know what major events are happening.";
+					close;
+				case 2:
+					mes "[Shorty]";
+					mes "The continent is full of festivals all year round. How about participating in the festival when master is also free?";
+					next;
+					mes "[Shorty]";
+					mes "Now...";
+					next;
+					mes "[Shorty]";
+					mes "I heard that there will be a winter hunting contest hosted by the royal family at the royal palace! want to do a hunting contest! I want to do it! Fight monsters!!! Let's go to Prontera!!!!";
+					next;
+					mes "[Shorty]";
+					mes "I'll definitely take you with me, okay?";
+					break;
+				case 3:
+					mes "[Shorty]";
+					mes "^0000cdHave you been to Paradise^000000?";
+					next;
+					mes "[Shorty]";
+					mes "There are many quests waiting for master. Besides, they also provide equipment for beginners.";
+					next;
+					mes "[Shorty]";
+					mes "Of course, it's not free, but you have to solve a simple quest from Paradise. Besides, they also rent out shadow equipment.";
+					next;
+					mes "[Shorty]";
+					mes "If you have any questions, go to the Paradise Office.";
+					break;
+				case 4:
+					mes "[Shorty]";
+					mes "Okay? Then I'll look around a bit and get back into the bag.";
+					close;
+				}
+				next;
+			}
+			end;
+		case 3:
+			while(1) {
+				mes "[Shorty]";
+				mes "I wonder where..";
+				next;
+				switch( select( "Field Hunting Guide", "Dungeon Guide", "Other Dungeon Guide", "Enough" ) ) {
+				case 1:
+					mes "[Shorty]";
+					mes "Press the ^0000cdCtrl+` keys^000000 to open a map.";
+					next;
+					mes "[Shorty]";
+					mes "Do you see a box for selecting an area at the top left? After selecting the area you want to see, check the ^0000cdmonster tab^000000 in the upper right corner or press the ^0000cdTab key^000000, you will see the name and level of the monster most distributed in that area.";
+					next;
+					mes "[Shorty]";
+					mes "It would be better to go on an adventure to a place that matches the level of master. ^0000cdIf there is a lot of level difference with monsters, you can't get 100% of the experience^000000. You have to calculate the level difference as well, right?";
+					next;
+					mes "[Shorty]";
+					mes "If you press the town you want to go to, the navigation menu will appear. Press the ^0000cd<Guide to the target> button at the bottom^000000 and exit the map to get guidance.";
+					break;
+				case 2:
+					mes "[Shorty]";
+					mes "Press the ^0000cdCtrl+` keys^000000 to open a map.";
+					next;
+					mes "[Shorty]";
+					mes "^0000cdDo you see a box where you can select an area^000000 in the upper left? If you select an area you want to check out then you will see it marked with a red box the map with ^0000cdthe name of the main dungeon^000000 written on it." ;
+					next;
+					mes "[Shorty]";
+					mes "Then check the ^0000cdmonster tab in the upper right corner or press the Tab key^000000 to display the level of the dungeon.";
+					next;
+					mes "[Shorty]";
+					mes "Sometimes, some dungeons can only be entered through special quests, so you need to gather information as well, alright?";
+					next;
+					mes "[Shorty]";
+					mes "Why don't you try the underground waterway first? The Swordsman Guild is always recruiting, but I heard it's good for beginner adventurers to gain experience.";
+					break;
+				case 3:
+					mes "[Shorty]";
+					mes "There are a lot of memorial dungeons, nightmare dungeons, illusion dungeons...etc.";
+					next;
+					mes "[Shorty]";
+					mes "^0000cdMemorial Dungeon^000000";
+					mes "In addition to the regular dungeons, there are also special dungeons. Usually, they say that it is a special place created by an unknown power in this world.";
+					next;
+					mes "[Shorty]";
+					mes "A lot of experienced adventurers usually go, but they say there are a few low-level places that can be reached with a little training. Either in Orc Village or Rachel...";
+					next;
+					mes "[Shorty]";
+					mes "It is said to be located in villages and general fields, and there are many places called Dimensional Rifts.";
+					next;
+					mes "[Shorty]";
+					mes "^0000cdNightmare Dungeon^000000";
+					mes "It is said that it is a dungeon that is much more difficult of a challenge than a normal dungeon. Even the monsters are not like the monsters we know of. When you find the dungeon, if someone suspicious tells you that it will send you to a more difficult place, try it out." ;
+					next;
+					mes "[Shorty]";
+					mes "^0000cdIllusion Dungeon^000000";
+					mes "No one knows what kind of dungeon these dungeons are. I guess it's something like a dream or a fantasy. There is a special stone that only comes from here, and everyone says that they aim for the stone and fight for it. They say that the stone can be exchanged for powerful weapons. ";
+					break;
+				case 4:
+					mes "[Shorty]";
+					mes "Okay? Then I'll look around a bit and get back into the bag.";
+					close;
+				}
+				next;
+			}
+			end;
+		case 4:
+			while(1) {
+				mes "[Shorty]";
+				mes "So, what basic info do you want to know?";
+				mes "Refer to <URL>[guide]<INFO>http://ro.gnjoy.com/guide/systeminfo/,1024,768</INFO></URL> for the Basic.";
+				next;
+				switch( select( "Party", "Guilds and Clans", "Smelt", "Enchant", "Family", "Chat" ) ) {
+				case 1:
+					mes "[Shorty]";
+					mes "Sometimes you would like to be with your friends, if they fight in dungeons, enjoy adventures together, or sometimes just sit and chat.";
+					next;
+					mes "[Shorty]";
+					mes "In that case, make a party and do it together.";
+					next;
+					mes "[Shorty]";
+					mes "The party window can be easily opened and with the ^0000cdAlt+Z keys^000000. When evenly distributing experience for hunting, the difference of ^0000cdlevel between party members^000000 can be equal to or less than 15.";
+					next;
+					mes "[Shorty]";
+					mes "If you click the circle on the left in the chat window at the bottom, you can designate a chat partner, and you can send a message only to party members there, so it's useful.";
+					break;
+				case 2:
+					mes "[Shorty]";
+					mes "Sometimes you will want to belong somewhere and feel a bond with someone. In that case, you just need to belong to a ^0000cdguild or clan^000000.";
+					next;
+					mes "[Shorty]";
+					mes "The biggest characteristic of a guild is a battle over a hideout. It is also called a siege.";
+					next;
+					mes "[Shorty]";
+					mes "You can enter a guild that has already been formed by invitation, or you can buy Empelium sold at the office to create a guild.";
+					next;
+					mes "[Shorty]";
+					mes "In addition to the sense of belonging, the guild can enjoy various benefits such as the guild's own skills and dedicated warehouse.";
+					next;
+					mes "[Shorty]";
+					mes "For details";
+					mes "refer to <URL>[Guild Guide]<INFO>http://ro.gnjoy.com/guide/systeminfo/systemGuild1.asp,1024,768</INFO></URL>";
+					next;
+					mes "[Shorty]";
+					mes "If joining a guild is burdensome, then a ^0000cdclan^000000 would be good. It may be difficult to feel as close to others like in a guild, but the clan also has various benefits.";
+					next;
+					mes "[Shorty]";
+					mes "^0000cdThe building in the lower left corner of Prontera Central Fountain^000000 is the Clan Headquarters, so take a look.";
+					break;
+				case 3:
+					mes "[Shorty]";
+					mes "Do you want to make the equipment you have stronger? In that case, you can find the ^0000cdsmelter^000000 in each town.";
+					next;
+					mes "[Shorty]";
+					mes "Smelting requires special items and zeny. It depends on the type and level of the equipment.";
+					next;
+					mes "[Shorty]";
+					mes "One thing to be aware of is that there is a safe refinement level in smelting, and if you proceed with refining above a certain level, the equipment has a chance to be destroyed. You must have good luck.";
+					next;
+					mes "[Shorty]";
+					mes "Oh, besides the smelter in each town, I heard that the^0000cd<Item Mall>^000000 offers a higher success chance of smelting.";
+					next;
+					mes "[Shorty]";
+					mes "If you are curious, go to ^0000cd<cash sales guide>^000000 in each town. They will guide you there.";
+					break;
+				case 4:
+					mes "[Shorty]";
+					mes "If you want to make your equipment stronger or give it a special ability, why not try to ^0000cdenchant^000000 it?";
+					next;
+					mes "[Shorty]";
+					mes "^0000cd<Malangdo>, <Item Mall>^000000, or ^0000cd<Seyablem> and <Leyablem>^000000 which can be found in major towns, are mainly doing this.";
+					next;
+					mes "[Shorty]";
+					mes "Equipment obtained from a specific dungeon or a specific area can only be ^0000cdenchanted^000000 from in that area.";
+					next;
+					mes "[Shorty]";
+					mes "However, not all equipment can be enchanted, so you should check the conditions carefully.";
+					break;
+				case 5:
+					mes "[Shorty]";
+					mes "Are you sure you want to be with another person for the rest of your life? Then you can start a ^0000cdfamily^000000.";
+					next;
+					mes "[Shorty]";
+					mes "^0000cdYou can marry the person you like^000000, or you can adopt a ^0000cdchild^000000. It is also possible for you to be adopted as the child of another couple.";
+					next;
+					mes "[Shorty]";
+					mes "They say that you can gain special marriage skills to use as a family.";
+					next;
+					mes "[Shorty]";
+					mes "If you are interested in getting married, go to the Church of Prontera or the Wedding Hall of Lasagna. You will need some supplies before you can marry. The Church of Prontera can only marry humans.";
+					next;
+					mes "[Shorty]";
+					mes "Adoption requires a wedded couple's level to be 70 or higher and the child to be adopted is a Novice or a first class.";
+					next;
+					mes "[Shorty]";
+					mes "The Family Division of the Prontera District Office says that you can purchase items necessary for a wedding or check with family-related matters, so if you need it, go check it out.";
+					next;
+					mes "[Shorty]";
+					mes "You can refer to";
+					mes "<URL>[Family]<INFO>http://ro.gnjoy.com/guide/systeminfo/systemLovers1.asp,1024,768</INFO></URL> for more details.";
+					break;
+				case 6:
+					mes "[Shorty]";
+					mes "Can you see the message window at the bottom? You can chat with other people by entering your message there or you can also send other message types there as well.";
+					next;
+					mes "[Shorty]";
+					mes "If you want to chat with a small number of people who are neither party nor guild members, you can open a chat window.";
+					next;
+					mes "[Shorty]";
+					mes "You can simply open the chat window with the ^0000cdAlt+C keys^000000. You can also set the maximum number of participants and and a password.";
+					break;
+				}
+				next;
+			}
+			end;
+		case 5:
+			mes "[Shorty]";
+			mes "Okay? Then I'll look around a bit and get back into the bag.";
+			close;
+		}
+		end;
+	case 8:
+		mes "[Shorty]";
+		mes "Aww!!! I'm Shorty! What's my master's name?";
+		next;
+		select( "Master?" );
+		mes "[Shorty]";
+		mes "The first person you meet should be called Master.";
+		next;
+		select( "Are you talking about a teacher?" );
+		mes "[Shorty]";
+		mes "Yeah. That's it. I'm Galapago Shorty, I'm a good talker.";
+		next;
+		select("Why are you here?" );
+		mes "[Shorty]";
+		mes "Salty! Salty is my friend. I'm here to meet Salty. So you must take me to Salty.";
+		next;
+		mes "[Shorty]";
+		mes "Well then, let's go!";
+		close;
+	case 9:
+		mes "[Shorty]";
+		mes "What...? Master...? What? Let's break up? Here? Now? Are you serious?";
+		next;
+		mes "[Shorty]";
+		mes "Is there anything you didn't like? Was I not quiet in my bag? Oh, that's what you didn't like, yes... Then there's nothing I can do.";
+		next;
+		mes "[Shorty]";
+		mes "Then you won't take me to ^0000cdSalty^000000? Salty will be grateful of you for taking me there. I'll pay thanks because Salty knows grace.";
+		next;
+		if (select( "Let's go our separate ways.", "Let's go together until then." ) == 2) {
+			mes "[Shorty]";
+			mes "Okay. Well said, master. Even if I get stuck in the bag, I'll get out of it quickly.";
+			next;
+			mes "[Shorty]";
+			mes "Let's help each other a little bit more. Okay?";
+			close;
+		}
+		mes "[Shorty]";
+		mes "Yes. Master has a stone cold heart. Then there is nothing that can be done. I think our relationship ends here.";
+		next;
+		mes "[Shorty]";
+		mes "I don't know how it was with you, but I enjoyed our time together.";
+		next;
+		mes "[Shorty]";
+		mes "Stay healthy and I bid you well. I hope master will not forget that Galapago was there.";
+		next;
+		mes "[Shorty]";
+		mes "Well then, master, goodbye. Goodbye!";
+		switch( isbegin_quest(14693) ) {
+		case 0:
+			setquest 14693;
+			completequest 14693;
+			break;
+		case 1:
+			completequest 14693;
+			break;
+		case 2:
+			break;
+		}
+		if (isbegin_quest(14694) > 0)
+			erasequest 14694;
+		delitem 23278,1;	// HelpmeShorty
+		close;
+	}
+}

+ 1 - 0
npc/re/scripts_athena.conf

@@ -205,6 +205,7 @@ npc: npc/re/quests/seals/megingard_seal.txt
 // --------------------------------------------------------------
 npc: npc/re/quests/cooking_quest.txt
 //npc: npc/re/quests/cupet.txt
+npc: npc/re/quests/HelpMeShorty.txt
 npc: npc/re/quests/homun_s.txt
 npc: npc/re/quests/juno_monster_society.txt
 npc: npc/re/quests/magic_books.txt

+ 15 - 0
npc/test/ci/0000_funcs.txt

@@ -0,0 +1,15 @@
+function	script	AssertTrue	{
+	if (!getarg(0)) {
+		errormes "AssertTrue failed for " + getarg(1) + ".";
+		return false;
+	}
+	return true;
+}
+
+function	script	AssertEquals	{
+	if (getarg(0) != getarg(1)) {
+		errormes "AssertEquals failed for " + getarg(2) + ": expected " + getarg(0) + ", got " + getarg(1) + ".";
+		return false;
+	}
+	return true;
+}

+ 35 - 0
npc/test/ci/7291.txt

@@ -0,0 +1,35 @@
+-	script	itemlink#ci	-1,{
+OnInit:
+	if( checkre(0) ){
+		if( PACKETVER >= 20200916 ){
+			.@expected$ = "<ITEML>0000213v0%0g&00'00)18X)1ck)00)00+2R,00-00</ITEML>";
+		}else if( PACKETVER >= 20150225 ){
+			// Grade does not exist (clientside) yet
+			.@expected$ = "<ITEMLINK>0000213v0%0g&00(18X(1ck(00(00*2R+00,00</ITEMLINK>";
+		}else if( PACKETVER >= 20100000 ){
+			// Random Options do not exist (clientside) yet
+			.@expected$ = "<ITEMLINK>0000213v0%0g&00(18X(1ck(00(00</ITEMLINK>";
+		}else{
+			// Item Link does not exist (clientside) yet
+			.@expected$ = "Crimson Saber";
+		}
+
+		setarray .@opt_ids,RDMOPT_WEAPON_ATTR_GROUND;
+		.@actual$ = itemlink(13454,16,4399,4608,0,0,0,.@opt_ids,.@opt_dummy,.@opt_dummy);
+		AssertEquals(.@expected$, .@actual$, "Generated itemlink for +16 Earth Crimson Saber");
+	}else{
+		if( PACKETVER >= 20200916 ){
+			.@expected$ = "<ITEML>000021hS%0a&00'00)18X)00)00)00</ITEML>";
+		}else if( PACKETVER >= 20100000 ){
+			// Grade does not exist (clientside) yet// Grade does not exist (clientside) yet
+			.@expected$ = "<ITEMLINK>000021hS%0a&00(18X(00(00(00</ITEMLINK>";
+		}else{
+			// Item Link does not exist (clientside) yet
+			.@expected$ = "Blade";
+		}
+
+		// No Random Options in Pre-Renewal
+		.@actual$ = itemlink(1108,10,4399);
+		AssertEquals(.@expected$, .@actual$, "Generated itemlink for +10 Blade[4]");
+	}
+}

+ 4 - 0
src/char/char.cpp

@@ -3010,7 +3010,11 @@ bool char_config_read(const char* cfgName, bool normal){
 			charserv_config.start_zeny = atoi(w2);
 			if (charserv_config.start_zeny < 0)
 				charserv_config.start_zeny = 0;
+#ifdef RENEWAL
 		} else if (strcmpi(w1, "start_items") == 0) {
+#else
+		} else if (strcmpi(w1, "start_items_pre") == 0) {
+#endif
 			char_config_split_startitem(w1, w2, charserv_config.start_items);
 #if PACKETVER >= 20151001
 		} else if (strcmpi(w1, "start_items_doram") == 0) {

+ 30 - 0
src/common/utilities.cpp

@@ -114,3 +114,33 @@ bool rathena::util::safe_multiplication( int64 a, int64 b, int64& result ){
 	return false;
 #endif
 }
+
+void rathena::util::string_left_pad_inplace(std::string& str, char padding, size_t num)
+{
+	str.insert(0, min(0, num - str.length()), padding);
+}
+
+std::string rathena::util::string_left_pad(const std::string& original, char padding, size_t num)
+{
+	return std::string(num - min(num, original.length()), padding) + original;
+}
+
+constexpr char base62_dictionary[] = {
+	'0', '1', '2', '3', '4', '5', '6', '7',
+	'8', '9', 'a', 'b', 'c', 'd', 'e', 'f',
+	'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
+	'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
+	'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D',
+	'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L',
+	'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T',
+	'U', 'V', 'W', 'X', 'Y', 'Z'
+};
+
+std::string rathena::util::base62_encode( uint32 val ){
+	std::string result = "";
+	while (val != 0) {
+		result = base62_dictionary[(val % 62)] + result;
+		val /= 62;
+	}
+	return result;
+}

+ 25 - 0
src/common/utilities.hpp

@@ -285,6 +285,31 @@ namespace rathena {
 		template <typename T> void tolower( T& string ){
 			std::transform( string.begin(), string.end(), string.begin(), ::tolower );
 		}
+
+		/**
+		* Pad string with arbitrary character in-place
+		* @param str: String to pad
+		* @param padding: Padding character
+		* @param num: Maximum length of padding
+		*/
+		void string_left_pad_inplace(std::string& str, char padding, size_t num);
+
+		/**
+		* Pad string with arbitrary character
+		* @param original: String to pad
+		* @param padding: Padding character
+		* @param num: Maximum length of padding
+		*
+		* @return A copy of original string with padding added
+		*/
+		std::string string_left_pad(const std::string& original, char padding, size_t num);
+
+		/**
+		* Encode base10 number to base62. Originally by lututui
+		* @param val: Base10 Number
+		* @return Base62 string
+		**/
+		std::string base62_encode( uint32 val );
 	}
 }
 

+ 34 - 15
src/map/atcommand.cpp

@@ -4007,7 +4007,7 @@ ACMD_FUNC(idsearch)
 	for(const auto &result : item_array) {
 		std::shared_ptr<item_data> id = result.second;
 
-		sprintf(atcmd_output, msg_txt(sd,78), id->ename.c_str(), id->nameid); // %s: %u
+		sprintf(atcmd_output, msg_txt(sd,78), item_db.create_item_link( id->nameid ).c_str(), id->nameid); // %s: %u
 		clif_displaymessage(fd, atcmd_output);
 	}
 	sprintf(atcmd_output, msg_txt(sd,79), match); // It is %d affair above.
@@ -6678,7 +6678,7 @@ ACMD_FUNC(autolootitem)
 			return -1;
 		}
 		sd->state.autolootid[i] = item_data->nameid; // Autoloot Activated
-		sprintf(atcmd_output, msg_txt(sd,1192), item_data->name.c_str(), item_data->ename.c_str(), item_data->nameid); // Autolooting item: '%s'/'%s' {%u}
+		sprintf(atcmd_output, msg_txt(sd,1192), item_data->name.c_str(), item_db.create_item_link( item_data->nameid ).c_str(), item_data->nameid); // Autolooting item: '%s'/'%s' {%u}
 		clif_displaymessage(fd, atcmd_output);
 		sd->state.autolooting = 1;
 		break;
@@ -6689,7 +6689,7 @@ ACMD_FUNC(autolootitem)
 			return -1;
 		}
 		sd->state.autolootid[i] = 0;
-		sprintf(atcmd_output, msg_txt(sd,1194), item_data->name.c_str(), item_data->ename.c_str(), item_data->nameid); // Removed item: '%s'/'%s' {%u} from your autolootitem list.
+		sprintf(atcmd_output, msg_txt(sd,1194), item_data->name.c_str(), item_db.create_item_link( item_data->nameid ).c_str(), item_data->nameid); // Removed item: '%s'/'%s' {%u} from your autolootitem list.
 		clif_displaymessage(fd, atcmd_output);
 		ARR_FIND(0, AUTOLOOTITEM_SIZE, i, sd->state.autolootid[i] != 0);
 		if (i == AUTOLOOTITEM_SIZE) {
@@ -6717,7 +6717,7 @@ ACMD_FUNC(autolootitem)
 					continue;
 				}
 
-				sprintf(atcmd_output, "'%s'/'%s' {%u}", item_data->name.c_str(), item_data->ename.c_str(), item_data->nameid);
+				sprintf(atcmd_output, "'%s'/'%s' {%u}", item_data->name.c_str(), item_db.create_item_link( item_data->nameid ).c_str(), item_data->nameid);
 				clif_displaymessage(fd, atcmd_output);
 			}
 		}
@@ -7784,9 +7784,9 @@ ACMD_FUNC(mobinfo)
 			int droprate = mob_getdroprate( &sd->bl, mob, mob->dropitem[i].rate, drop_modifier );
 
 			if (id->slots)
-				sprintf(atcmd_output2, " - %s[%d]  %02.02f%%", id->ename.c_str(), id->slots, (float)droprate / 100);
+				sprintf(atcmd_output2, " - %s[%d]  %02.02f%%", item_db.create_item_link( id->nameid ).c_str(), id->slots, (float)droprate / 100);
 			else
-				sprintf(atcmd_output2, " - %s  %02.02f%%", id->ename.c_str(), (float)droprate / 100);
+				sprintf(atcmd_output2, " - %s  %02.02f%%", item_db.create_item_link( id->nameid ).c_str(), (float)droprate / 100);
 			strcat(atcmd_output, atcmd_output2);
 			if (++j % 3 == 0) {
 				clif_displaymessage(fd, atcmd_output);
@@ -7824,14 +7824,14 @@ ACMD_FUNC(mobinfo)
 					j++;
 					if (j == 1) {
 						if (id->slots)
-							sprintf(atcmd_output2, " %s[%d]  %02.02f%%", id->ename.c_str(), id->slots, mvppercent);
+							sprintf(atcmd_output2, " %s[%d]  %02.02f%%", item_db.create_item_link( id->nameid ).c_str(), id->slots, mvppercent);
 						else
-							sprintf(atcmd_output2, " %s  %02.02f%%", id->ename.c_str(), mvppercent);
+							sprintf(atcmd_output2, " %s  %02.02f%%", item_db.create_item_link( id->nameid ).c_str(), mvppercent);
 					} else {
 						if (id->slots)
-							sprintf(atcmd_output2, " - %s[%d]  %02.02f%%", id->ename.c_str(), id->slots, mvppercent);
+							sprintf(atcmd_output2, " - %s[%d]  %02.02f%%", item_db.create_item_link( id->nameid ).c_str(), id->slots, mvppercent);
 						else
-							sprintf(atcmd_output2, " - %s  %02.02f%%", id->ename.c_str(), mvppercent);
+							sprintf(atcmd_output2, " - %s  %02.02f%%", item_db.create_item_link( id->nameid ).c_str(), mvppercent);
 					}
 					strcat(atcmd_output, atcmd_output2);
 				}
@@ -8266,7 +8266,7 @@ ACMD_FUNC(iteminfo)
 		std::shared_ptr<item_data> item_data = result.second;
 
 		sprintf(atcmd_output, msg_txt(sd,1277), // Item: '%s'/'%s'[%d] (%u) Type: %s | Extra Effect: %s
-			item_data->name.c_str(),item_data->ename.c_str(),item_data->slots,item_data->nameid,
+			item_data->name.c_str(), item_db.create_item_link( item_data->nameid ).c_str(),item_data->slots,item_data->nameid,
 			(item_data->type != IT_AMMO) ? itemdb_typename((enum item_types)item_data->type) : itemdb_typename_ammo((e_ammo_type)item_data->subtype),
 			(item_data->script==NULL)? msg_txt(sd,1278) : msg_txt(sd,1279) // None / With script
 		);
@@ -8323,7 +8323,7 @@ ACMD_FUNC(whodrops)
 	for (const auto &result : item_array) {
 		std::shared_ptr<item_data> id = result.second;
 
-		sprintf(atcmd_output, msg_txt(sd,1285), id->ename.c_str(), id->slots, id->nameid); // Item: '%s'[%d] (ID:%u)
+		sprintf(atcmd_output, msg_txt(sd,1285), item_db.create_item_link( id->nameid ).c_str(), id->slots, id->nameid); // Item: '%s'[%d] (ID:%u)
 		clif_displaymessage(fd, atcmd_output);
 
 		if (id->mob[0].chance == 0) {
@@ -9319,9 +9319,9 @@ ACMD_FUNC(itemlist)
 		}
 
 		if( it->refine )
-			StringBuf_Printf(&buf, "%d %s %+d (%s, id: %u)", it->amount, itd->ename.c_str(), it->refine, itd->name.c_str(), it->nameid);
+			StringBuf_Printf(&buf, "%d %s %+d (%s, id: %u)", it->amount, item_db.create_item_link( it->nameid ).c_str(), it->refine, itd->name.c_str(), it->nameid);
 		else
-			StringBuf_Printf(&buf, "%d %s (%s, id: %u)", it->amount, itd->ename.c_str(), itd->name.c_str(), it->nameid);
+			StringBuf_Printf(&buf, "%d %s (%s, id: %u)", it->amount, item_db.create_item_link( it->nameid ).c_str(), itd->name.c_str(), it->nameid);
 
 		if( it->equip ) {
 			char equipstr[CHAT_SIZE_MAX];
@@ -9424,7 +9424,7 @@ ACMD_FUNC(itemlist)
 				if( counter2 != 1 )
 					StringBuf_AppendStr(&buf, ", ");
 
-				StringBuf_Printf(&buf, "#%d %s (id: %u)", counter2, card->ename.c_str(), card->nameid);
+				StringBuf_Printf(&buf, "#%d %s (id: %u)", counter2, item_db.create_item_link( card->nameid ).c_str(), card->nameid);
 			}
 
 			if( counter2 > 0 )
@@ -10821,6 +10821,24 @@ ACMD_FUNC( enchantgradeui ){
 #endif
 }
 
+ACMD_FUNC( roulette ){
+	nullpo_retr( -1, sd );
+
+#if PACKETVER < 20141022
+	sprintf( atcmd_output, msg_txt( sd, 798 ), "2014-10-22" ); // This command requires packet version %s or newer.
+	clif_displaymessage( fd, atcmd_output );
+	return -1;
+#else
+	if( !battle_config.feature_roulette ){
+		clif_displaymessage( fd, msg_txt( sd, 1497 ) ); // Roulette is disabled
+		return -1;
+	}
+
+	clif_roulette_open( sd );
+	return 0;
+#endif
+}
+
 #include "../custom/atcommand.inc"
 
 /**
@@ -11145,6 +11163,7 @@ void atcommand_basecommands(void) {
 		ACMD_DEFR(stylist, ATCMD_NOCONSOLE|ATCMD_NOAUTOTRADE),
 		ACMD_DEF(addfame),
 		ACMD_DEFR(enchantgradeui, ATCMD_NOCONSOLE|ATCMD_NOAUTOTRADE),
+		ACMD_DEFR(roulette, ATCMD_NOCONSOLE|ATCMD_NOAUTOTRADE),
 	};
 	AtCommandInfo* atcommand;
 	int i;

+ 1 - 0
src/map/battle.cpp

@@ -10267,6 +10267,7 @@ static const struct _battle_data {
 
 	{ "feature.barter",                     &battle_config.feature_barter,                  1,      0,      1,              },
 	{ "feature.barter_extended",            &battle_config.feature_barter_extended,         1,      0,      1,              },
+	{ "feature.itemlink",                   &battle_config.feature_itemlink,                1,      0,      1,              },
 	{ "break_mob_equip",                    &battle_config.break_mob_equip,                 0,      0,      1,              },
 	{ "macro_detection_retry",              &battle_config.macro_detection_retry,           3,      1,      INT_MAX,        },
 	{ "macro_detection_timeout",            &battle_config.macro_detection_timeout,         60000,  0,      INT_MAX,        },

+ 1 - 0
src/map/battle.hpp

@@ -563,6 +563,7 @@ struct Battle_Config
 	int discount_item_point_shop;
 	int update_enemy_position;
 	int devotion_rdamage;
+	int feature_itemlink;
 
 	// autotrade persistency
 	int feature_autotrade;

+ 1 - 2
src/map/chrif.cpp

@@ -1637,8 +1637,7 @@ void chrif_parse_ack_vipActive(int fd) {
 
 	// Show info if status changed
 	if (((flag&0x4) || changed) && !sd->vip.disableshowrate) {
-		clif_display_pinfo(sd,ZC_PERSONAL_INFOMATION);
-		//clif_vip_display_info(sd,ZC_PERSONAL_INFOMATION_CHN);
+		clif_display_pinfo( *sd );
 	}
 #endif
 }

+ 162 - 266
src/map/clif.cpp

@@ -79,7 +79,6 @@ static inline int32 client_exp(t_exp exp) {
 static struct eri *delay_clearunit_ers;
 
 struct s_packet_db packet_db[MAX_PACKET_DB + 1];
-int packet_db_ack[MAX_ACK_FUNC + 1];
 // Reuseable global packet buffer to prevent too many allocations
 // Take socket.cpp::socket_max_client_packet into consideration
 static int8 packet_buffer[UINT16_MAX];
@@ -2491,25 +2490,13 @@ void clif_scriptclose(struct map_session_data *sd, int npcid)
  * @param sd : player pointer
  * @param npcid : npc gid to close
  */
-void clif_scriptclear(struct map_session_data *sd, int npcid)
-{
-	struct s_packet_db* info;
-	int16 len;
-	int cmd = 0;
-	int fd;
+void clif_scriptclear( struct map_session_data& sd, int npcid ){
+	struct PACKET_ZC_CLEAR_DIALOG p = {};
 
-	nullpo_retv(sd);
+	p.packetType = HEADER_ZC_CLEAR_DIALOG;
+	p.GID = npcid;
 
-	cmd = packet_db_ack[ZC_CLEAR_DIALOG];
-	if(!cmd) cmd = 0x8d6; //default
-	info = &packet_db[cmd];
-	len = info->len;
-	fd = sd->fd;
-
-	WFIFOHEAD(fd, len);
-	WFIFOW(fd,0)=0x8d6;
-	WFIFOL(fd,info->pos[0])=npcid;
-	WFIFOSET(fd,len);
+	clif_send( &p, sizeof( p ), &sd.bl, SELF );
  }
 
 /*==========================================
@@ -4134,48 +4121,22 @@ void clif_statusupack(struct map_session_data *sd,int type,int ok,int val) {
 ///     0 = failure
 ///     1 = success
 ///     2 = failure due to low level
-void clif_equipitemack(struct map_session_data *sd,int n,int pos,uint8 flag)
-{
-	int fd = 0, cmd = 0, look = 0;
-	struct s_packet_db *info = NULL;
-
-	nullpo_retv(sd);
-
-	cmd = packet_db_ack[ZC_WEAR_EQUIP_ACK];
-	if (!cmd || !(info = &packet_db[cmd]) || !info->len)
-		return;
-
-	fd = sd->fd;
+void clif_equipitemack( struct map_session_data& sd, uint8 flag, int index, int pos ){
+	struct PACKET_ZC_REQ_WEAR_EQUIP_ACK p = {};
 
-	if (flag == ITEM_EQUIP_ACK_OK && sd->inventory_data[n]->equip&EQP_VISIBLE)
-		look = sd->inventory_data[n]->look;
-
-	WFIFOHEAD(fd, info->len);
-	WFIFOW(fd, 0) = cmd;
-	WFIFOW(fd, info->pos[0]) = n+2;
-	switch (cmd) {
-		case 0xaa:
-			WFIFOW(fd, info->pos[1]) = pos;
-#if PACKETVER < 20100629
-			WFIFOW(fd, info->pos[2]) = (flag == ITEM_EQUIP_ACK_OK ? 1 : 0);
-#else
-			WFIFOL(fd, info->pos[2]) = look;
-			WFIFOW(fd, info->pos[3]) = (flag == ITEM_EQUIP_ACK_OK ? 1 : 0);
-#endif
-			break;
-		case 0x8d0:
-			if (flag == ITEM_EQUIP_ACK_FAILLEVEL)
-				flag = 1;
-		case 0x999:
-			if (cmd == 0x999)
-				WFIFOL(fd, info->pos[1]) = pos;
-			else
-				WFIFOW(fd, info->pos[1]) = pos;
-			WFIFOL(fd, info->pos[2]) = look;
-			WFIFOW(fd, info->pos[3]) = flag;
-			break;
+	p.PacketType = HEADER_ZC_REQ_WEAR_EQUIP_ACK;
+	p.index = client_index( index );
+	p.wearLocation = pos;
+#if PACKETVER_MAIN_NUM >= 20101123 || PACKETVER_RE_NUM >= 20100629
+	if( flag == ITEM_EQUIP_ACK_OK && sd.inventory_data[index]->equip&EQP_VISIBLE ){
+		p.wItemSpriteNumber = sd.inventory_data[index]->look;
+	}else{
+		p.wItemSpriteNumber = 0;
 	}
-	WFIFOSET(fd, info->len);
+#endif
+	p.result = flag;
+
+	clif_send( &p, sizeof( p ), &sd.bl, SELF );
 }
 
 
@@ -7346,16 +7307,15 @@ void clif_cart_additem_ack(struct map_session_data *sd, uint8 flag)
 }
 
 // 09B7 <unknow data> (ZC_ACK_OPEN_BANKING)
-void clif_bank_open(struct map_session_data *sd){
-	int fd;
+void clif_bank_open( struct map_session_data& sd ){
+#if PACKETVER >= 20130717
+	struct PACKET_ZC_ACK_OPEN_BANKING p = {};
 
-	nullpo_retv(sd);
-	fd = sd->fd;
+	p.packetType = HEADER_ZC_ACK_OPEN_BANKING;
+	p.unknown = 0;
 
-	WFIFOHEAD(fd,4);
-	WFIFOW(fd,0) = 0x09b7;
-	WFIFOW(fd,2) = 0;
-	WFIFOSET(fd,4);
+	clif_send( &p, sizeof( p ), &sd.bl, SELF );
+#endif
 }
 
 /*
@@ -7378,23 +7338,21 @@ void clif_parse_BankOpen(int fd, struct map_session_data* sd) {
 			//request save ?
 			//chrif_bankdata_request(sd->status.account_id, sd->status.char_id);
 			//on succes open bank ?
-			clif_bank_open(sd);
+			clif_bank_open( *sd );
 		}
 	}
 }
 
 // 09B9 <unknow data> (ZC_ACK_CLOSE_BANKING)
+void clif_bank_close( struct map_session_data& sd ){
+#if PACKETVER >= 20130717
+	struct PACKET_ZC_ACK_CLOSE_BANKING p = {};
 
-void clif_bank_close(struct map_session_data *sd){
-	int fd;
-
-	nullpo_retv(sd);
-	fd = sd->fd;
+	p.packetType = HEADER_ZC_ACK_CLOSE_BANKING;
+	p.unknown = 0;
 
-	WFIFOHEAD(fd,4);
-	WFIFOW(fd,0) = 0x09B9;
-	WFIFOW(fd,2) = 0;
-	WFIFOSET(fd,4);
+	clif_send( &p, sizeof( p ), &sd.bl, SELF );
+#endif
 }
 
 /*
@@ -7412,33 +7370,24 @@ void clif_parse_BankClose(int fd, struct map_session_data* sd) {
 	}
 	if(sd->status.account_id == aid){
 		sd->state.banking = 0;
-		clif_bank_close(sd);
+		clif_bank_close( *sd );
 	}
 }
 
 /*
  * Display how much we got in bank (I suppose)
-  09A6 <Bank_Vault>Q <Reason>W (PACKET_ZC_BANKING_CHECK)
+ * 09A6 <Bank_Vault>Q <Reason>W (ZC_BANKING_CHECK)
  */
-void clif_Bank_Check(struct map_session_data* sd) {
-	unsigned char buf[13];
-	struct s_packet_db* info;
-	int16 len;
-	int cmd = 0;
-
-	nullpo_retv(sd);
+void clif_Bank_Check( struct map_session_data& sd ){
+#if PACKETVER >= 20130717
+	struct PACKET_ZC_BANKING_CHECK p = {};
 
-	cmd = packet_db_ack[ZC_BANKING_CHECK];
-	if(!cmd) cmd = 0x09A6; //default
-	info = &packet_db[cmd];
-	len = info->len;
-	if(!len) return; //version as packet disable
-	// sd->state.banking = 1; //mark opening and closing
+	p.packetType = HEADER_ZC_BANKING_CHECK;
+	p.money = sd.bank_vault;
+	p.reason = 0;
 
-	WBUFW(buf,0) = cmd;
-	WBUFQ(buf,info->pos[0]) = sd->bank_vault; //value
-	WBUFW(buf,info->pos[1]) = 0; //reason
-	clif_send(buf,len,&sd->bl,SELF);
+	clif_send( &p, sizeof( p ), &sd.bl, SELF );
+#endif
 }
 
 /*
@@ -7456,7 +7405,7 @@ void clif_parse_BankCheck(int fd, struct map_session_data* sd) {
 		struct s_packet_db* info = &packet_db[RFIFOW(fd,0)];
 		int aid = RFIFOL(fd,info->pos[0]); //unused should we check vs fd ?
 		if(sd->status.account_id == aid) //since we have it let check it for extra security
-			clif_Bank_Check(sd);
+			clif_Bank_Check( *sd );
 	}
 }
 
@@ -7464,25 +7413,17 @@ void clif_parse_BankCheck(int fd, struct map_session_data* sd) {
  * Acknowledge of deposit some money in bank
   09A8 <Reason>W <Money>Q <balance>L (PACKET_ZC_ACK_BANKING_DEPOSIT)
  */
-void clif_bank_deposit(struct map_session_data *sd, enum e_BANKING_DEPOSIT_ACK reason) {
-	unsigned char buf[17];
-	struct s_packet_db* info;
-	int16 len;
-	int cmd =0;
-
-	nullpo_retv(sd);
+void clif_bank_deposit( struct map_session_data& sd, enum e_BANKING_DEPOSIT_ACK reason ){
+#if PACKETVER >= 20130717
+	struct PACKET_ZC_ACK_BANKING_DEPOSIT p = {};
 
-	cmd = packet_db_ack[ZC_ACK_BANKING_DEPOSIT];
-	if(!cmd) cmd = 0x09A8;
-	info = &packet_db[cmd];
-	len = info->len;
-	if(!len) return; //version as packet disable
+	p.packetType = HEADER_ZC_ACK_BANKING_DEPOSIT;
+	p.money = sd.bank_vault;
+	p.zeny = sd.status.zeny;
+	p.reason = reason;
 
-	WBUFW(buf,0) = cmd;
-	WBUFW(buf,info->pos[0]) = (short)reason;
-	WBUFQ(buf,info->pos[1]) = sd->bank_vault;/* money in the bank */
-	WBUFL(buf,info->pos[2]) = sd->status.zeny;/* how much zeny char has after operation */
-	clif_send(buf,len,&sd->bl,SELF);
+	clif_send( &p, sizeof( p ), &sd.bl, SELF );
+#endif
 }
 
 /*
@@ -7503,7 +7444,7 @@ void clif_parse_BankDeposit(int fd, struct map_session_data* sd) {
 
 		if(sd->status.account_id == aid){
 			enum e_BANKING_DEPOSIT_ACK reason = pc_bank_deposit(sd,max(0,money));
-			clif_bank_deposit(sd,reason);
+			clif_bank_deposit( *sd, reason );
 		}
 	}
 }
@@ -7512,26 +7453,17 @@ void clif_parse_BankDeposit(int fd, struct map_session_data* sd) {
  * Acknowledge of withdrawing some money from bank
   09AA <Reason>W <Money>Q <balance>L (PACKET_ZC_ACK_BANKING_WITHDRAW)
  */
-void clif_bank_withdraw(struct map_session_data *sd,enum e_BANKING_WITHDRAW_ACK reason) {
-	unsigned char buf[17];
-	struct s_packet_db* info;
-	int16 len;
-	int cmd;
-
-	nullpo_retv(sd);
+void clif_bank_withdraw( struct map_session_data& sd, enum e_BANKING_WITHDRAW_ACK reason ){
+#if PACKETVER >= 20130717
+	struct PACKET_ZC_ACK_BANKING_WITHDRAW p = {};
 
-	cmd = packet_db_ack[ZC_ACK_BANKING_WITHDRAW];
-	if(!cmd) cmd = 0x09AA;
-	info = &packet_db[cmd];
-	len = info->len;
-	if(!len) return; //version as packet disable
+	p.packetType = HEADER_ZC_ACK_BANKING_WITHDRAW;
+	p.reason = reason;
+	p.money = sd.bank_vault;
+	p.zeny = sd.status.zeny;
 
-	WBUFW(buf,0) = cmd;
-	WBUFW(buf,info->pos[0]) = (short)reason;
-	WBUFQ(buf,info->pos[1]) = sd->bank_vault;/* money in the bank */
-	WBUFL(buf,info->pos[2]) = sd->status.zeny;/* how much zeny char has after operation */
-
-	clif_send(buf,len,&sd->bl,SELF);
+	clif_send( &p, sizeof( p ), &sd.bl, SELF );
+#endif
 }
 
 /*
@@ -7550,7 +7482,7 @@ void clif_parse_BankWithdraw(int fd, struct map_session_data* sd) {
 		int money = RFIFOL(fd,info->pos[1]);
 		if(sd->status.account_id == aid){
 			enum e_BANKING_WITHDRAW_ACK reason = pc_bank_withdraw(sd,max(0,money));
-			clif_bank_withdraw(sd,reason);
+			clif_bank_withdraw( *sd, reason );
 		}
 	}
 }
@@ -11088,8 +11020,7 @@ void clif_parse_LoadEndAck(int fd,struct map_session_data *sd)
 			map_getmapflag(sd->state.pmap, MF_BEXP) != mapdata->flag[MF_BEXP]
 			)
 		{
-			clif_display_pinfo(sd,ZC_PERSONAL_INFOMATION);
-			//clif_vip_display_info(sd,ZC_PERSONAL_INFOMATION_CHN);
+			clif_display_pinfo( *sd );
 		}
 #endif
 
@@ -12094,7 +12025,7 @@ void clif_parse_EquipItem(int fd,struct map_session_data *sd)
 		return;
 
 	if(!sd->inventory.u.items_inventory[index].identify) {
-		clif_equipitemack(sd,index,0,ITEM_EQUIP_ACK_FAIL);	// fail
+		clif_equipitemack( *sd, ITEM_EQUIP_ACK_FAIL, index ); // fail
 		return;
 	}
 
@@ -19957,19 +19888,16 @@ void clif_update_rankingpoint(map_session_data &sd, int rankingtype, int point)
  *   0 - map adjustment (bexp mapflag), 1 - Premium/VIP adjustment, 2 - Server rate adjustment, 3 - None
 */
 #ifdef VIP_ENABLE
-void clif_display_pinfo(struct map_session_data *sd, int cmdtype) {
-	if (sd) {
-		struct s_packet_db* info;
-		int16 len, szdetails = 13, maxinfotype = PINFO_MAX;
-		int cmd = 0, fd, i = 0;
-		int tot_baseexp = 100, tot_penalty = 100, tot_drop = 100, factor = 1000;
+void clif_display_pinfo( struct map_session_data& sd ){
+#if PACKETVER_MAIN_NUM >= 20110627 || PACKETVER_RE_NUM >= 20110628 || defined(PACKETVER_ZERO)
+	// TODO: Whoever wants to take the blame, fix this crappy logic below - and yes I know indentation is wrong, but CANT TOUCH THIS! *sings* [Lemongrass]
 		int details_bexp[PINFO_MAX], details_drop[PINFO_MAX], details_penalty[PINFO_MAX];
 
 		/**
 		 * EXP
 		 */
 		//0:PCRoom
-		details_bexp[PINFO_BASIC] = map_getmapflag(sd->bl.m, MF_BEXP);
+		details_bexp[PINFO_BASIC] = map_getmapflag( sd.bl.m, MF_BEXP );
 		if (details_bexp[PINFO_BASIC] == 100 || !details_bexp[PINFO_BASIC])
 			details_bexp[PINFO_BASIC] = 0;
 		else {
@@ -19981,7 +19909,7 @@ void clif_display_pinfo(struct map_session_data *sd, int cmdtype) {
 		}
 
 		//1:Premium
-		if (pc_isvip(sd)) {
+		if( pc_isvip( &sd ) ){
 			details_bexp[PINFO_PREMIUM] = battle_config.vip_base_exp_increase * battle_config.base_exp_rate / 100;
 			if (details_bexp[PINFO_PREMIUM] < 0)
 				details_bexp[PINFO_PREMIUM] = 0 - details_bexp[PINFO_PREMIUM];
@@ -20010,7 +19938,7 @@ void clif_display_pinfo(struct map_session_data *sd, int cmdtype) {
 		details_drop[PINFO_BASIC] = 0;
 
 		//1:Premium
-		if (pc_isvip(sd)) {
+		if( pc_isvip( &sd ) ){
 			details_drop[PINFO_PREMIUM] = (battle_config.vip_drop_increase * battle_config.item_rate_common) / 100;
 			if (details_drop[PINFO_PREMIUM] < 0)
 				details_drop[PINFO_PREMIUM] = 0 - details_drop[PINFO_PREMIUM];
@@ -20040,7 +19968,7 @@ void clif_display_pinfo(struct map_session_data *sd, int cmdtype) {
 		details_penalty[PINFO_BASIC] = 0;
 
 		//1:Premium
-		if (pc_isvip(sd)) {
+		if( pc_isvip( &sd ) ){
 			details_penalty[PINFO_PREMIUM] = battle_config.vip_exp_penalty_base;
 			if (details_penalty[PINFO_PREMIUM] == 100)
 				details_penalty[PINFO_PREMIUM] = 0;
@@ -20071,53 +19999,40 @@ void clif_display_pinfo(struct map_session_data *sd, int cmdtype) {
 		//3:TPLUS
 		details_penalty[PINFO_CAFE] = 0;
 
-		cmd = packet_db_ack[cmdtype];
-		info = &packet_db[cmd];
-		len = info->len; //this is the base len without details
-		if(!len) return; //version as packet disable
-
-		if (cmdtype == ZC_PERSONAL_INFOMATION && cmd == 0x08cb) { //0x08cb version
-			szdetails = 7;
-			factor = 1;
-		}
-		else if (cmd == 0x097b) {
-			tot_baseexp *= factor;
-			tot_drop *= factor;
-			tot_penalty *= factor;
-		}
-
-		fd = sd->fd;
-		WFIFOHEAD(fd,len+maxinfotype*szdetails);
-		WFIFOW(fd,0) = cmd;
+	struct PACKET_ZC_PERSONAL_INFOMATION* p = (struct PACKET_ZC_PERSONAL_INFOMATION*)packet_buffer;
 
-		for (i = 0; i < maxinfotype; i++) {
-			WFIFOB(fd,info->pos[4]+(i*szdetails)) = i; //infotype //0 PCRoom, 1 Premium, 2 Server, 3 TPlus
+	p->packetType = HEADER_ZC_PERSONAL_INFOMATION;
+	p->length = sizeof( *p ) + PINFO_MAX * sizeof( p->details[0] );
+#if PACKETVER_MAIN_NUM >= 20120503 || PACKETVER_RE_NUM >= 20120502 || defined(PACKETVER_ZERO)
+	p->total_exp = 100 * 1000;
+	p->total_death = 100 * 1000;
+	p->total_drop = 100 * 1000;
+#else
+	p->total_exp = 100;
+	p->total_death = 100;
+	p->total_drop = 100;
+#endif
 
-			WFIFOL(fd,info->pos[5]+(i*szdetails)) = details_bexp[i]*factor;
-			WFIFOL(fd,info->pos[6]+(i*szdetails)) = details_penalty[i]*factor;
-			WFIFOL(fd,info->pos[7]+(i*szdetails)) = details_drop[i]*factor;
+	for( int i = PINFO_BASIC; i < PINFO_MAX; i++ ){
+		p->details[i].type = i; // infotype 0 PCRoom, 1 Premium, 2 Server, 3 TPlus
 
-			tot_baseexp += details_bexp[i]*factor;
-			tot_drop += details_drop[i]*factor;
-			tot_penalty += details_penalty[i]*factor;
+#if PACKETVER_MAIN_NUM >= 20120503 || PACKETVER_RE_NUM >= 20120502 || defined(PACKETVER_ZERO)
+		p->details[i].exp = details_bexp[i] * 1000;
+		p->details[i].death = details_penalty[i] * 1000;
+		p->details[i].drop = details_drop[i] * 1000;
+#else
+		p->details[i].exp = details_bexp[i];
+		p->details[i].death = details_penalty[i];
+		p->details[i].drop = details_drop[i];
+#endif
 
-			len += szdetails;
-		}
-		WFIFOW(fd,info->pos[0])  = len; //packetlen
-		if (cmd == 0x08cb) { //0x08cb version
-			WFIFOW(fd,info->pos[1])  = tot_baseexp;
-			WFIFOW(fd,info->pos[2])  = tot_penalty;
-			WFIFOW(fd,info->pos[3])  = tot_drop;
-		}
-		else { //2013-08-07aRagexe uses 0x097b
-			WFIFOL(fd,info->pos[1])  = tot_baseexp;
-			WFIFOL(fd,info->pos[2])  = tot_penalty;
-			WFIFOL(fd,info->pos[3])  = tot_drop;
-		}
-		if (cmdtype == ZC_PERSONAL_INFOMATION_CHN)
-			WFIFOW(fd,info->pos[8])  = 0; //activity rate case of event ??
-		WFIFOSET(fd,len);
+		p->total_exp += p->details[i].exp;
+		p->total_death += p->details[i].death;
+		p->total_drop += p->details[i].drop;
 	}
+
+	clif_send( p, p->length, &sd.bl, SELF );
+#endif
 }
 #endif
 
@@ -20134,46 +20049,36 @@ void clif_parse_GMFullStrip(int fd, struct map_session_data *sd) {
 * @param fd
 * @param bl Crimson Marker target
 **/
-void clif_crimson_marker(struct map_session_data *sd, struct block_list *bl, bool remove) {
-	struct s_packet_db* info;
-	int cmd = 0;
-	int16 len;
-	unsigned char buf[11];
-
-	nullpo_retv(sd);
+void clif_crimson_marker( struct map_session_data& sd, struct block_list& bl, bool remove ){
+#if PACKETVER_MAIN_NUM >= 20130731 || PACKETVER_RE_NUM >= 20130707 || defined(PACKETVER_ZERO)
+	struct PACKET_ZC_C_MARKERINFO p = {};
 
-	cmd = packet_db_ack[ZC_C_MARKERINFO];
-	if (!cmd)
-		cmd = 0x09C1; //default
-	info = &packet_db[cmd];
-	if (!(len = info->len))
-		return;
+	p.PacketType = HEADER_ZC_C_MARKERINFO;
+	p.AID = bl.id;
+	if( remove ){
+		p.xPos = -1;
+		p.yPos = -1;
+	}else{
+		p.xPos = bl.x;
+		p.yPos = bl.y;
+	}
 
-	WBUFW(buf, 0) = cmd;
-	WBUFL(buf, info->pos[0]) = bl->id;
-	WBUFW(buf, info->pos[1]) = (remove ? -1 : bl->x);
-	WBUFW(buf, info->pos[2]) = (remove ? -1 : bl->y);
-	clif_send(buf, len, &sd->bl, SELF);
+	clif_send( &p, sizeof( p ), &sd.bl, SELF );
+#endif
 }
 
 /**
  * 02d3 <index>.W (ZC_NOTIFY_BIND_ON_EQUIP)
  **/
-void clif_notify_bindOnEquip(struct map_session_data *sd, int n) {
-	struct s_packet_db *info = NULL;
-	int cmd = 0;
+void clif_notify_bindOnEquip( struct map_session_data& sd, int16 index ){
+#if PACKETVER >= 20070227
+	struct PACKET_ZC_NOTIFY_BIND_ON_EQUIP p = {};
 
-	nullpo_retv(sd);
-
-	cmd = packet_db_ack[ZC_NOTIFY_BIND_ON_EQUIP];
-	info = &packet_db[cmd];
-	if (!cmd || !info->len)
-		return;
+	p.packetType = HEADER_ZC_NOTIFY_BIND_ON_EQUIP;
+	p.index = client_index( index );
 
-	WFIFOHEAD(sd->fd, info->len);
-	WFIFOW(sd->fd, 0) = cmd;
-	WFIFOW(sd->fd, info->pos[0]) = n+2;
-	WFIFOSET(sd->fd, info->len);
+	clif_send( &p, sizeof( p ), &sd.bl, SELF );
+#endif
 }
 
 /**
@@ -20707,25 +20612,21 @@ void clif_parse_roulette_item( int fd, struct map_session_data* sd ){
  * @param fd
  * @param sd
  **/
-void clif_merge_item_ack(struct map_session_data *sd, unsigned short index, unsigned short count, enum MERGE_ITEM_ACK type) {
-	unsigned char buf[9];
-	struct s_packet_db *info = NULL;
-	short cmd = 0;
-
-	nullpo_retv(sd);
+void clif_merge_item_ack( struct map_session_data &sd, enum MERGE_ITEM_ACK type, uint16 index = 0, uint16 count = 0 ){
+#if PACKETVER_MAIN_NUM >= 20120314 || PACKETVER_RE_NUM >= 20120221 || defined(PACKETVER_ZERO)
+	struct PACKET_ZC_ACK_MERGE_ITEM p = {};
 
-	if (!clif_session_isValid(sd))
-		return;
-	if (!(cmd = packet_db_ack[ZC_ACK_MERGE_ITEM]))
-		return;
-	if (!(info = &packet_db[cmd]) || info->len == 0)
-		return;
+	p.packetType = HEADER_ZC_ACK_MERGE_ITEM;
+	if( type == MERGE_ITEM_SUCCESS ){
+		p.index = client_index( index );
+	}else{
+		p.index = index;
+	}
+	p.amount = count;
+	p.reason = type;
 
-	WBUFW(buf, 0) = cmd;
-	WBUFW(buf, info->pos[0]) = index;
-	WBUFW(buf, info->pos[1]) = count;
-	WBUFB(buf, info->pos[2]) = type;
-	clif_send(buf, info->len, &sd->bl, SELF);
+	clif_send( &p, sizeof( p ), &sd.bl, SELF );
+#endif
 }
 
 /**
@@ -20773,41 +20674,37 @@ static bool clif_merge_item_check(struct item_data *id, struct item *it) {
  * ZC 096D <size>.W { <index>.W }* (ZC_MERGE_ITEM_OPEN)
  * @param sd
  **/
-void clif_merge_item_open(struct map_session_data *sd) {
-	unsigned char buf[4 + MAX_INVENTORY*2] = { 0 };
-	unsigned short cmd = 0, n = 0, i = 0, indexes[MAX_INVENTORY] = { 0 };
-	int len = 0;
-	struct s_packet_db *info = NULL;
-	struct item *it;
+void clif_merge_item_open( struct map_session_data& sd ){
+#if PACKETVER_MAIN_NUM >= 20120314 || PACKETVER_RE_NUM >= 20120221 || defined(PACKETVER_ZERO)
+	struct PACKET_ZC_MERGE_ITEM_OPEN* p = (struct PACKET_ZC_MERGE_ITEM_OPEN*)packet_buffer;
 
-	nullpo_retv(sd);
-	if (!clif_session_isValid(sd))
-		return;
-	if (!(cmd = packet_db_ack[ZC_MERGE_ITEM_OPEN]))
-		return;
-	if (!(info = &packet_db[cmd]) || info->len == 0)
-		return;
+	p->packetType = HEADER_ZC_MERGE_ITEM_OPEN;
+	p->packetLen = sizeof( *p );
+
+	int n = 0;
 
 	// Get entries
-	for (i = 0; i < MAX_INVENTORY; i++) {
-		if (!clif_merge_item_check(sd->inventory_data[i], (it = &sd->inventory.u.items_inventory[i])))
+	for( int i = 0; i < MAX_INVENTORY; i++ ){
+		struct item* it = &sd.inventory.u.items_inventory[i];
+
+		if( !clif_merge_item_check( sd.inventory_data[i], it ) ){
 			continue;
-		if (clif_merge_item_has_pair(sd, it))
-			indexes[n++] = i;
-	}
+		}
 
-	if (n < 2) { // No item need to be merged
-		clif_msg(sd, MERGE_ITEM_NOT_AVAILABLE);
-		return;
+		if( clif_merge_item_has_pair( &sd, it ) ){
+			p->items[n++].index = client_index( i );
+			p->packetLen += sizeof( p->items[0] );
+		}
 	}
 
-	WBUFW(buf, 0) = cmd;
-	WBUFW(buf, info->pos[0]) = (len = 4 + n*2);
-	for (i = 0; i < n; i++) {
-		WBUFW(buf, info->pos[1] + i*2) = indexes[i]+2;
+	// No item need to be merged
+	if( n < 2 ){
+		clif_msg( &sd, MERGE_ITEM_NOT_AVAILABLE );
+		return;
 	}
 
-	clif_send(buf, len, &sd->bl, SELF);
+	clif_send( p, p->packetLen, &sd.bl, SELF );
+#endif
 }
 
 /**
@@ -20846,7 +20743,7 @@ void clif_parse_merge_item_req(int fd, struct map_session_data* sd) {
 			continue;
 		indexes[j] = idx;
 		if (j && id->nameid != sd->inventory_data[indexes[0]]->nameid) { // Only can merge 1 kind at once
-			clif_merge_item_ack(sd, 0, 0, MERGE_ITEM_FAILED_NOT_MERGE);
+			clif_merge_item_ack( *sd, MERGE_ITEM_FAILED_NOT_MERGE );
 			return;
 		}
 		count += sd->inventory.u.items_inventory[idx].amount;
@@ -20859,7 +20756,7 @@ void clif_parse_merge_item_req(int fd, struct map_session_data* sd) {
 	}
 
 	if (count >= (id->stack.amount ? id->stack.amount : MAX_AMOUNT)) {
-		clif_merge_item_ack(sd, 0, 0, MERGE_ITEM_FAILED_MAX_COUNT);
+		clif_merge_item_ack( *sd, MERGE_ITEM_FAILED_MAX_COUNT );
 		return;
 	}
 
@@ -20873,7 +20770,7 @@ void clif_parse_merge_item_req(int fd, struct map_session_data* sd) {
 	}
 	sd->inventory.u.items_inventory[indexes[0]].amount = count;
 
-	clif_merge_item_ack(sd, indexes[0]+2, count, MERGE_ITEM_SUCCESS);
+	clif_merge_item_ack( *sd, MERGE_ITEM_SUCCESS, indexes[0], count );
 }
 
 /**
@@ -25126,7 +25023,6 @@ void packetdb_addpacket( uint16 cmd, uint16 length, void (*func)(int, struct map
  *------------------------------------------*/
 void packetdb_readdb(){
 	memset(packet_db,0,sizeof(packet_db));
-	memset(packet_db_ack,0,sizeof(packet_db_ack));
 
 #include "clif_packetdb.hpp"
 #include "clif_shuffle.hpp"

+ 14 - 30
src/map/clif.hpp

@@ -59,25 +59,6 @@ enum e_PacketDBVersion { // packet DB
 #endif
 };
 
-enum e_packet_ack : uint8_t{
-	ZC_ACK_OPEN_BANKING = 0,
-	ZC_ACK_CLOSE_BANKING,
-	ZC_ACK_BANKING_DEPOSIT,
-	ZC_ACK_BANKING_WITHDRAW,
-	ZC_BANKING_CHECK,
-	ZC_PERSONAL_INFOMATION,
-	ZC_PERSONAL_INFOMATION_CHN,
-	ZC_CLEAR_DIALOG,
-	ZC_C_MARKERINFO,
-	ZC_NOTIFY_BIND_ON_EQUIP,
-	ZC_WEAR_EQUIP_ACK,
-	ZC_MERGE_ITEM_OPEN,
-	ZC_ACK_MERGE_ITEM,
-	ZC_BROADCASTING_SPECIAL_ITEM_OBTAIN,
-	//add other here
-	MAX_ACK_FUNC //auto upd len
-};
-
 struct s_packet_db {
 	short len;
 	void (*func)(int, struct map_session_data *);
@@ -214,7 +195,6 @@ enum class e_purchase_result : uint8{
 
 #define packet_len(cmd) packet_db[cmd].len
 extern struct s_packet_db packet_db[MAX_PACKET_DB+1];
-extern int packet_db_ack[MAX_ACK_FUNC + 1];
 
 // local define
 enum send_target : uint8_t {
@@ -516,9 +496,15 @@ enum clif_messages : uint16_t {
 	ADDITEM_TO_CART_FAIL_COUNT = 0x1,
 
 	// clif_equipitemack flags
+#if PACKETVER_MAIN_NUM >= 20121205 || PACKETVER_RE_NUM >= 20121107 || defined(PACKETVER_ZERO)
 	ITEM_EQUIP_ACK_OK = 0,
-	ITEM_EQUIP_ACK_FAIL = 1,
-	ITEM_EQUIP_ACK_FAILLEVEL = 2,
+	ITEM_EQUIP_ACK_FAIL = 2,
+	ITEM_EQUIP_ACK_FAILLEVEL = 1,
+#else
+	ITEM_EQUIP_ACK_OK = 1,
+	ITEM_EQUIP_ACK_FAIL = 0,
+	ITEM_EQUIP_ACK_FAILLEVEL = 0,
+#endif
 	/* -end- */
 
 	//! NOTE: These values below need client version validation
@@ -645,7 +631,7 @@ void clif_parse_NPCMarketPurchase(int fd, struct map_session_data *sd);
 void clif_scriptmes( struct map_session_data& sd, uint32 npcid, const char *mes );
 void clif_scriptnext( struct map_session_data& sd, uint32 npcid );
 void clif_scriptclose(struct map_session_data *sd, int npcid);	//self
-void clif_scriptclear(struct map_session_data *sd, int npcid);	//self
+void clif_scriptclear( struct map_session_data& sd, int npcid ); //self
 void clif_scriptmenu(struct map_session_data* sd, int npcid, const char* mes);	//self
 void clif_scriptinput(struct map_session_data *sd, int npcid);	//self
 void clif_scriptinputstr(struct map_session_data *sd, int npcid);	// self
@@ -669,7 +655,7 @@ void clif_arrowequip(struct map_session_data *sd,int val); //self
 void clif_arrow_fail(struct map_session_data *sd,int type); //self
 void clif_arrow_create_list(struct map_session_data *sd);	//self
 void clif_statusupack(struct map_session_data *sd,int type,int ok,int val);	// self
-void clif_equipitemack(struct map_session_data *sd,int n,int pos,uint8 flag);	// self
+void clif_equipitemack( struct map_session_data& sd, uint8 flag, int index, int pos = 0 ); // self
 void clif_unequipitemack(struct map_session_data *sd,int n,int pos,int ok);	// self
 void clif_misceffect(struct block_list* bl,int type);	// area
 void clif_changeoption_target(struct block_list* bl, struct block_list* target);
@@ -1042,8 +1028,6 @@ void clif_PartyBookingDeleteNotify(struct map_session_data* sd, int index);
 void clif_PartyBookingInsertNotify(struct map_session_data* sd, struct party_booking_ad_info* pb_ad);
 
 /* Bank System [Yommy/Hercules] */
-void clif_bank_deposit (struct map_session_data *sd, enum e_BANKING_DEPOSIT_ACK reason);
-void clif_bank_withdraw (struct map_session_data *sd,enum e_BANKING_WITHDRAW_ACK reason);
 void clif_parse_BankDeposit (int fd, struct map_session_data *sd);
 void clif_parse_BankWithdraw (int fd, struct map_session_data *sd);
 void clif_parse_BankCheck (int fd, struct map_session_data *sd);
@@ -1074,7 +1058,7 @@ void clif_search_store_info_click_ack(struct map_session_data* sd, short x, shor
 void clif_cashshop_result( struct map_session_data* sd, t_itemid item_id, uint16 result );
 void clif_cashshop_open( struct map_session_data* sd, int tab );
 
-void clif_display_pinfo(struct map_session_data *sd, int type);
+void clif_display_pinfo( struct map_session_data& sd );
 
 /// Roulette
 void clif_roulette_open(struct map_session_data* sd);
@@ -1140,15 +1124,15 @@ void clif_channel_msg(struct Channel *channel, const char *msg, unsigned long co
 void clif_ranklist(struct map_session_data *sd, int16 rankingType);
 void clif_update_rankingpoint(map_session_data &sd, int rankingtype, int point);
 
-void clif_crimson_marker(struct map_session_data *sd, struct block_list *bl, bool remove);
+void clif_crimson_marker( struct map_session_data& sd, struct block_list& bl, bool remove );
 
 void clif_showscript(struct block_list* bl, const char* message, enum send_target flag);
 void clif_party_leaderchanged(struct map_session_data *sd, int prev_leader_aid, int new_leader_aid);
 
 void clif_account_name(int fd, uint32 account_id, const char* accname);
-void clif_notify_bindOnEquip(struct map_session_data *sd, int n);
+void clif_notify_bindOnEquip( struct map_session_data& sd, int16 index );
 
-void clif_merge_item_open(struct map_session_data *sd);
+void clif_merge_item_open( struct map_session_data& sd );
 
 void clif_broadcast_obtain_special_item(const char *char_name, t_itemid nameid, t_itemid container, enum BROADCASTING_SPECIAL_ITEM_OBTAIN type);
 

+ 0 - 23
src/map/clif_packetdb.hpp

@@ -6,9 +6,6 @@
 
 	#define packet(cmd,length) packetdb_addpacket(cmd,length,NULL,0)
 	#define parseable_packet(cmd,length,func,...) packetdb_addpacket(cmd,length,func,__VA_ARGS__,0)
-	#define ack_packet(type,cmd,length,...) \
-		packetdb_addpacket(cmd,length,NULL,__VA_ARGS__,0); \
-		packet_db_ack[type] = cmd
 
 	packet(0x0064,55);
 	packet(0x0065,17);
@@ -78,7 +75,6 @@
 	parseable_packet(0x00a7,8,clif_parse_UseItem,2,4);
 	packet( useItemAckType, sizeof( struct PACKET_ZC_USE_ITEM_ACK ) );
 	parseable_packet(0x00a9,6,clif_parse_EquipItem,2,4);
-	ack_packet(ZC_WEAR_EQUIP_ACK,0x00aa,7,2,4,6);
 	parseable_packet(0x00ab,4,clif_parse_UnequipItem,2);
 	packet(0x00ac,7);
 	//packet(0x00ad,-1);
@@ -415,7 +411,6 @@
 	//packet(0x020c,-1);
 	packet(0x020d,-1);
 	packet(0x8b3,-1);
-	ack_packet(ZC_CLEAR_DIALOG,0x8d6,6,2);
 
 // 2004-07-05aSakexe
 #if PACKETVER >= 20040705
@@ -1082,7 +1077,6 @@
 	packet(0x02cd,26);
 	packet(0x02ce,10);
 	parseable_packet(0x02cf,6,clif_parse_MemorialDungeonCommand,2);
-	ack_packet(ZC_NOTIFY_BIND_ON_EQUIP,0x02d3,4,2);
 	packet(0x02d5,2);
 	parseable_packet(0x02d6,6,clif_parse_ViewPlayerEquip,2);
 	parseable_packet(0x02d8,10,clif_parse_configuration,2,6);
@@ -1582,7 +1576,6 @@
 // 2009-12-01aRagexeRE
 #if PACKETVER >= 20091201
 	packet(0x07fc,10);
-	ack_packet(ZC_BROADCASTING_SPECIAL_ITEM_OBTAIN,0x07fd,-1,0);
 	//packet(0x07ff,-1);
 #endif
 
@@ -1720,7 +1713,6 @@
 
 // 2010-06-29aRagexeRE
 #if PACKETVER >= 20100629
-	ack_packet(ZC_WEAR_EQUIP_ACK,0x00AA,9,2,4,6,8);
 	//packet(0x07F1,18);
 	//packet(0x07F2,8);
 	//packet(0x07F3,6);
@@ -1773,7 +1765,6 @@
 	packet(0x0856,-1);
 	packet(0x0857,-1);
 	packet(0x0858,-1);
-	ack_packet(ZC_WEAR_EQUIP_ACK,0x08d0,9,2,4,6,8);
 #endif
 
 // 2011-10-05aRagexeRE
@@ -1924,11 +1915,8 @@
 	packet(0x0977,14); //Monster HP Bar
 	parseable_packet(0x0916,26,clif_parse_GuildInvite2,2);
 	parseable_packet(0x091d,41,clif_parse_PartyBookingRegisterReq,2,4,6);
-	ack_packet(ZC_PERSONAL_INFOMATION,0x08cb,10,2,4,6,8,10,11,13,15); //Still need further information
 	// Merge Item
-	ack_packet(ZC_MERGE_ITEM_OPEN,0x096D,-1,2,4); // ZC_MERGE_ITEM_OPEN
 	parseable_packet(0x096E,-1,clif_parse_merge_item_req,2,4); // CZ_REQ_MERGE_ITEM
-	ack_packet(ZC_ACK_MERGE_ITEM,0x096F,7,2,4,6); // ZC_ACK_MERGE_ITEM
 	parseable_packet(0x0974,2,clif_parse_merge_item_cancel,0); // CZ_CANCEL_MERGE_ITEM
 	parseable_packet(0x0844,2,clif_parse_cashshop_open_request,0);
 	packet(0x0849,16); //clif_cashshop_result
@@ -2033,10 +2021,7 @@
 	packet(0x0977,14); //Monster HP Bar
 	parseable_packet(0x0978,6,clif_parse_reqworldinfo,2);
 	packet(0x0979,50); //ackworldinfo
-	ack_packet(ZC_PERSONAL_INFOMATION,0x097b,16,2,4,8,12,16,17,21,25); //Still need further information
-	//ack_packet(ZC_PERSONAL_INFOMATION_CHN,0x0981,12,2,4,6,8,12,13,15,17,10); // Disabled until further information is found.
 	parseable_packet(0x0998,8,clif_parse_EquipItem,2,4); // CZ_REQ_WEAR_EQUIP_V5
-	ack_packet(ZC_WEAR_EQUIP_ACK,0x0999,11,2,4,8,10); // cz_wear_equipv5
 	packet(0x099a,9); // take_off_equipv5
 	packet(0x099b,8); //maptypeproperty2
 	// New Packets
@@ -2060,16 +2045,11 @@
 
 // 2013-07-17Ragexe
 #if PACKETVER >= 20130717
-	ack_packet(ZC_BANKING_CHECK,0x09A6,12,2,10);
 	parseable_packet(0x09A7,10,clif_parse_BankDeposit,2,6);
-	ack_packet(ZC_ACK_BANKING_DEPOSIT,0x09A8,16,2,4,12);
 	parseable_packet(0x09A9,10,clif_parse_BankWithdraw,2,6);
-	ack_packet(ZC_ACK_BANKING_WITHDRAW,0x09AA,16,2,4,12);
 	parseable_packet(0x09AB,6,clif_parse_BankCheck,2);
 	parseable_packet(0x09B6,6,clif_parse_BankOpen,2);
-	ack_packet(ZC_ACK_OPEN_BANKING,0x09B7,4,2);
 	parseable_packet(0x09B8,6,clif_parse_BankClose,2);
-	ack_packet(ZC_ACK_CLOSE_BANKING,0x09B9,4,2);
 #endif
 
 // 2013-07-31cRagexe
@@ -2080,11 +2060,8 @@
 
 // 2013-08-07Ragexe
 #if PACKETVER >= 20130807
-	ack_packet(ZC_C_MARKERINFO,0x09C1,10,2,6,8);
 	// Merge Item
-	ack_packet(ZC_MERGE_ITEM_OPEN,0x096D,-1,2,4); // ZC_MERGE_ITEM_OPEN
 	parseable_packet(0x096E,-1,clif_parse_merge_item_req,2,4); // CZ_REQ_MERGE_ITEM
-	ack_packet(ZC_ACK_MERGE_ITEM,0x096F,7,2,4,6,7); // ZC_ACK_MERGE_ITEM
 	parseable_packet(0x0974,2,clif_parse_merge_item_cancel,0); // CZ_CANCEL_MERGE_ITEM
 	packet(0x9CD,8); // ZC_MSG_COLOR
 #endif

+ 94 - 0
src/map/itemdb.cpp

@@ -5,6 +5,8 @@
 
 #include <iostream>
 #include <stdlib.h>
+#include <math.h>
+#include <unordered_map>
 
 #include "../common/nullpo.hpp"
 #include "../common/random.hpp"
@@ -1239,6 +1241,98 @@ std::shared_ptr<item_data> ItemDatabase::searchname( const char *name ){
 	return util::umap_find( this->nameToItemDataMap, lowername );
 }
 
+/**
+* Generates an item link string
+* @param data: Item info
+* @return <ITEML> string for the item
+* @author [Cydh]
+**/
+std::string ItemDatabase::create_item_link( struct item& item ){
+	std::shared_ptr<item_data> data = this->find( item.nameid );
+
+	if( data == nullptr ){
+		ShowError( "Tried to create itemlink for unknown item %u.\n", item.nameid );
+		return "Unknown item";
+	}
+
+// All these dates are unconfirmed
+#if PACKETVER >= 20100000
+	if( !battle_config.feature_itemlink ){
+		// Feature is disabled
+		return data->ename;
+	}
+
+	struct item_data* id = data.get();
+
+#if PACKETVER_MAIN_NUM >= 20200916 || PACKETVER_RE_NUM >= 20200724
+	const std::string start_tag = "<ITEML>";
+	const std::string closing_tag = "</ITEML>";
+#else // PACKETVER >= 20100000
+	const std::string start_tag = "<ITEMLINK>";
+	const std::string closing_tag = "</ITEMLINK>";
+#endif
+
+	std::string itemstr = start_tag;
+
+	itemstr += util::string_left_pad(util::base62_encode(id->equip), '0', 5);
+	itemstr += itemdb_isequip2(id) ? "1" : "0";
+	itemstr += util::base62_encode(item.nameid);
+	if (item.refine > 0) {
+		itemstr += "%" + util::string_left_pad(util::base62_encode(item.refine), '0', 2);
+	}
+	if (itemdb_isequip2(id)) {
+		itemstr += "&" + util::string_left_pad(util::base62_encode(id->look), '0', 2);
+	}
+#if PACKETVER_MAIN_NUM >= 20200916 || PACKETVER_RE_NUM >= 20200724
+	itemstr += "'" + util::string_left_pad(util::base62_encode(item.enchantgrade), '0', 2);
+#endif
+
+#if PACKETVER_MAIN_NUM >= 20200916 || PACKETVER_RE_NUM >= 20200724
+	const std::string card_sep = ")";
+	const std::string optid_sep = "+";
+	const std::string optpar_sep = ",";
+	const std::string optval_sep = "-";
+#else
+	const std::string card_sep = "(";
+	const std::string optid_sep = "*";
+	const std::string optpar_sep = "+";
+	const std::string optval_sep = ",";
+#endif
+
+	for (uint8 i = 0; i < MAX_SLOTS; ++i) {
+		itemstr += card_sep + util::string_left_pad(util::base62_encode(item.card[i]), '0', 2);
+	}
+
+#if PACKETVER >= 20150225
+	for (uint8 i = 0; i < MAX_ITEM_RDM_OPT; ++i) {
+		if (item.option[i].id == 0) {
+			break; // ignore options including ones beyond this one since the client won't even display them
+		}
+		// Option ID
+		itemstr += optid_sep + util::string_left_pad(util::base62_encode(item.option[i].id), '0', 2);
+		// Param
+		itemstr += optpar_sep + util::string_left_pad(util::base62_encode(item.option[i].param), '0', 2);
+		// Value
+		itemstr += optval_sep + util::string_left_pad(util::base62_encode(item.option[i].value), '0', 2);
+	}
+#endif
+
+	itemstr += closing_tag;
+	return itemstr;
+#else
+	// Did not exist before that
+	return data->ename;
+#endif
+}
+
+std::string ItemDatabase::create_item_link( t_itemid id ){
+	struct item it = {};
+
+	it.nameid = id;
+
+	return this->create_item_link( it );
+}
+
 ItemDatabase item_db;
 
 /**

+ 3 - 0
src/map/itemdb.hpp

@@ -5,6 +5,7 @@
 #define ITEMDB_HPP
 
 #include <map>
+#include <string>
 #include <vector>
 
 #include "../common/database.hpp"
@@ -1344,6 +1345,8 @@ public:
 	// Additional
 	std::shared_ptr<item_data> searchname( const char* name );
 	std::shared_ptr<item_data> search_aegisname( const char *name );
+	std::string create_item_link( struct item& data );
+	std::string create_item_link( t_itemid id );
 };
 
 extern ItemDatabase item_db;

+ 2 - 1
src/map/map.hpp

@@ -645,12 +645,13 @@ enum e_mapflag : int16 {
 	MF_PRIVATEAIRSHIP_SOURCE,
 	MF_PRIVATEAIRSHIP_DESTINATION,
 	MF_SKILL_DURATION,
-	MF_NOCASHSHOP,
+	MF_NOCASHSHOP, // 70
 	MF_NORODEX,
 	MF_NORENEWALEXPPENALTY,
 	MF_NORENEWALDROPPENALTY,
 	MF_NOPETCAPTURE,
 	MF_NOBUYINGSTORE,
+	MF_NODYNAMICNPC,
 	MF_MAX
 };
 

+ 6 - 1
src/map/npc.cpp

@@ -2286,6 +2286,8 @@ bool npc_scriptcont(struct map_session_data* sd, int id, bool closing){
 			// close
 			case CLOSE:
 				sd->st->state = END;
+				if (sd->st->clear_cutin)
+					clif_cutin(sd,"",255);
 				break;
 			// close2
 			case STOP:
@@ -5805,7 +5807,10 @@ struct npc_data* npc_duplicate_npc_for_player( struct npc_data& nd, struct map_s
 		return nullptr;
 	}
 
-	// TODO: check maps that might forbid usage? maybe create mapflag?
+	if( map_getmapflag( sd.bl.m, MF_NODYNAMICNPC ) ){
+		// It has been confirmed that there is no reply to the client
+		return nullptr;
+	}
 
 	int16 new_x, new_y;
 

+ 47 - 0
src/map/packets.hpp

@@ -367,6 +367,46 @@ struct PACKET_CZ_REQ_CHANGE_MEMBERPOS{
 	struct PACKET_CZ_REQ_CHANGE_MEMBERPOS_sub list[];
 } __attribute__((packed));
 
+struct PACKET_ZC_CLEAR_DIALOG{
+	int16 packetType;
+	uint32 GID;
+} __attribute__((packed));
+
+struct PACKET_ZC_NOTIFY_BIND_ON_EQUIP{
+	int16 packetType;
+	int16 index;
+} __attribute__((packed));
+
+struct PACKET_ZC_BANKING_CHECK{
+	int16 packetType;
+	int64 money;
+	int16 reason;
+} __attribute__((packed));
+
+struct PACKET_ZC_ACK_BANKING_WITHDRAW{
+	int16 packetType;
+	int16 reason;
+	int64 money;
+	int32 zeny;
+} __attribute__((packed));
+
+struct PACKET_ZC_ACK_BANKING_DEPOSIT{
+	int16 packetType;
+	int16 reason;
+	int64 money;
+	int32 zeny;
+} __attribute__((packed));
+
+struct PACKET_ZC_ACK_CLOSE_BANKING{
+	int16 packetType;
+	int16 unknown;
+} __attribute__((packed));
+
+struct PACKET_ZC_ACK_OPEN_BANKING{
+	int16 packetType;
+	int16 unknown;
+} __attribute__((packed));
+
 #if PACKETVER_MAIN_NUM >= 20140508 || PACKETVER_RE_NUM >= 20140508 || defined(PACKETVER_ZERO)
 struct PACKET_ZC_GOLDPCCAFE_POINT{
 	int16 packetType;
@@ -415,15 +455,22 @@ DEFINE_PACKET_HEADER(ZC_ACK_WEAPONREFINE, 0x223)
 DEFINE_PACKET_HEADER(CZ_REQ_MAKINGITEM, 0x25b)
 DEFINE_PACKET_HEADER(ZC_CASH_TIME_COUNTER, 0x298)
 DEFINE_PACKET_HEADER(ZC_CASH_ITEM_DELETE, 0x299)
+DEFINE_PACKET_HEADER(ZC_NOTIFY_BIND_ON_EQUIP, 0x2d3)
 DEFINE_PACKET_HEADER(ZC_FAILED_TRADE_BUYING_STORE_TO_SELLER, 0x824)
 DEFINE_PACKET_HEADER(CZ_SSILIST_ITEM_CLICK, 0x83c)
+DEFINE_PACKET_HEADER(ZC_CLEAR_DIALOG, 0x8d6)
 DEFINE_PACKET_HEADER(ZC_ENTRY_QUEUE_INIT, 0x90e);
+DEFINE_PACKET_HEADER(ZC_BANKING_CHECK, 0x9a6)
+DEFINE_PACKET_HEADER(ZC_ACK_BANKING_DEPOSIT, 0x9a8)
+DEFINE_PACKET_HEADER(ZC_ACK_BANKING_WITHDRAW, 0x9aa)
 DEFINE_PACKET_HEADER(CZ_REQ_CASH_BARGAIN_SALE_ITEM_INFO, 0x9ac)
 DEFINE_PACKET_HEADER(ZC_ACK_CASH_BARGAIN_SALE_ITEM_INFO, 0x9ad)
 DEFINE_PACKET_HEADER(CZ_REQ_APPLY_BARGAIN_SALE_ITEM, 0x9ae)
 DEFINE_PACKET_HEADER(CZ_REQ_REMOVE_BARGAIN_SALE_ITEM, 0x9b0)
 DEFINE_PACKET_HEADER(ZC_NOTIFY_BARGAIN_SALE_SELLING, 0x9b2)
 DEFINE_PACKET_HEADER(ZC_NOTIFY_BARGAIN_SALE_CLOSE, 0x9b3)
+DEFINE_PACKET_HEADER(ZC_ACK_OPEN_BANKING, 0x9b7)
+DEFINE_PACKET_HEADER(ZC_ACK_CLOSE_BANKING, 0x9b9)
 DEFINE_PACKET_HEADER(ZC_ACK_COUNT_BARGAIN_SALE_ITEM, 0x9c4)
 DEFINE_PACKET_HEADER(ZC_ACK_GUILDSTORAGE_LOG, 0x9da)
 DEFINE_PACKET_HEADER(ZC_GOLDPCCAFE_POINT, 0xa15)

+ 17 - 1
src/map/packets_struct.hpp

@@ -4704,7 +4704,23 @@ struct PACKET_ZC_PERSONAL_INFOMATION {
 	struct PACKET_ZC_PERSONAL_INFOMATION_SUB details[];
 } __attribute__((packed));
 DEFINE_PACKET_HEADER(ZC_PERSONAL_INFOMATION, 0x097b);
-#endif  // PACKETVER_MAIN_NUM >= 20120503 || PACKETVER_RE_NUM >= 20120502 || defined(PACKETVER_ZERO)
+#elif PACKETVER_MAIN_NUM >= 20110627 || PACKETVER_RE_NUM >= 20110628
+struct PACKET_ZC_PERSONAL_INFOMATION_SUB {
+	int8 type;
+	int16 exp;
+	int16 death;
+	int16 drop;
+} __attribute__((packed));
+struct PACKET_ZC_PERSONAL_INFOMATION {
+	int16 packetType;
+	int16 length;
+	int16 total_exp;
+	int16 total_death;
+	int16 total_drop;
+	struct PACKET_ZC_PERSONAL_INFOMATION_SUB details[];
+} __attribute__((packed));
+DEFINE_PACKET_HEADER(ZC_PERSONAL_INFOMATION, 0x08cb);
+#endif // PACKETVER_MAIN_NUM >= 20110627 || PACKETVER_RE_NUM >= 20110628
 
 struct PACKET_CZ_REQUEST_ACTNPC {
 	int16 packetType;

+ 10 - 9
src/map/pc.cpp

@@ -9308,7 +9308,7 @@ void pc_close_npc(struct map_session_data *sd,int flag)
 		if (sd->st) {
 			if (sd->st->state == CLOSE) {
 				clif_scriptclose(sd, sd->npc_id);
-				clif_scriptclear(sd, sd->npc_id); // [Ind/Hercules]
+				clif_scriptclear( *sd, sd->npc_id ); // [Ind/Hercules]
 				sd->st->state = END; // Force to end now
 			}
 			if (sd->st->state == END) { // free attached scripts that are waiting
@@ -11562,7 +11562,8 @@ bool pc_equipitem(struct map_session_data *sd,short n,int req_pos,bool equipswit
 		if( equipswitch ){
 			clif_equipswitch_add( sd, n, req_pos, ITEM_EQUIP_ACK_FAIL );
 		}else{
-			clif_equipitemack(sd,0,0,ITEM_EQUIP_ACK_FAIL);
+			// Does not deserve an answer... [Lemongrass]
+			//clif_equipitemack( sd, ITEM_EQUIP_ACK_FAIL, n );
 		}
 		return false;
 	}
@@ -11570,7 +11571,7 @@ bool pc_equipitem(struct map_session_data *sd,short n,int req_pos,bool equipswit
 		if( equipswitch ){
 			clif_equipswitch_add( sd, n, req_pos, ITEM_EQUIP_ACK_FAIL );
 		}else{
-			clif_equipitemack(sd,n,0,ITEM_EQUIP_ACK_FAIL);
+			clif_equipitemack( *sd, ITEM_EQUIP_ACK_FAIL, n );
 		}
 		return false;
 	}
@@ -11586,7 +11587,7 @@ bool pc_equipitem(struct map_session_data *sd,short n,int req_pos,bool equipswit
 		if( equipswitch ){
 			clif_equipswitch_add( sd, n, req_pos, res );
 		}else{
-			clif_equipitemack(sd,n,0,res);	// fail
+			clif_equipitemack( *sd, res, n );	// fail
 		}
 		return false;
 	}
@@ -11600,7 +11601,7 @@ bool pc_equipitem(struct map_session_data *sd,short n,int req_pos,bool equipswit
 		if( equipswitch ){
 			clif_equipswitch_add( sd, n, req_pos, ITEM_EQUIP_ACK_FAIL );
 		}else{
-			clif_equipitemack(sd,n,0,ITEM_EQUIP_ACK_FAIL);	// fail
+			clif_equipitemack( *sd, ITEM_EQUIP_ACK_FAIL, n );	// fail
 		}
 		return false;
 	}
@@ -11608,7 +11609,7 @@ bool pc_equipitem(struct map_session_data *sd,short n,int req_pos,bool equipswit
 		if( equipswitch ){
 			clif_equipswitch_add( sd, n, req_pos, ITEM_EQUIP_ACK_FAIL );
 		}else{
-			clif_equipitemack(sd,n,0,ITEM_EQUIP_ACK_FAIL); //Fail
+			clif_equipitemack( *sd, ITEM_EQUIP_ACK_FAIL, n ); //Fail
 		}
 		return false;
 	}
@@ -11617,7 +11618,7 @@ bool pc_equipitem(struct map_session_data *sd,short n,int req_pos,bool equipswit
 
 	if ( !equipswitch && id->flag.bindOnEquip && !sd->inventory.u.items_inventory[n].bound) {
 		sd->inventory.u.items_inventory[n].bound = (char)battle_config.default_bind_on_equip;
-		clif_notify_bindOnEquip(sd,n);
+		clif_notify_bindOnEquip( *sd, n );
 	}
 
 	if(pos == EQP_ACC) { //Accessories should only go in one of the two.
@@ -11700,7 +11701,7 @@ bool pc_equipitem(struct map_session_data *sd,short n,int req_pos,bool equipswit
 			clif_arrow_fail(sd,3);
 		}
 		else
-			clif_equipitemack(sd,n,pos,ITEM_EQUIP_ACK_OK);
+			clif_equipitemack( *sd, ITEM_EQUIP_ACK_OK, n, pos );
 
 		sd->inventory.u.items_inventory[n].equip = pos;
 	}
@@ -14244,7 +14245,7 @@ void pc_scdata_received(struct map_session_data *sd) {
 
 	clif_weight_limit( sd );
 
-	if( pc_has_permission( sd, PC_PERM_ATTENDANCE ) && pc_attendance_enabled() && !pc_attendance_rewarded_today( sd ) ){
+	if( pc_has_permission( sd, PC_PERM_ATTENDANCE ) && pc_attendance_enabled() && !pc_attendance_rewarded_today( sd ) && pc_attendance_counter(sd) < 200 ){
 		clif_ui_open( *sd, OUT_UI_ATTENDANCE, pc_attendance_counter( sd ) );
 	}
 

+ 1 - 1
src/map/pet.cpp

@@ -1514,7 +1514,7 @@ int pet_equipitem(struct map_session_data *sd,int index)
 	t_itemid nameid = sd->inventory.u.items_inventory[index].nameid;
 
 	if(pet_db_ptr->AcceID == 0 || nameid != pet_db_ptr->AcceID || pd->pet.equip != 0) {
-		clif_equipitemack(sd,0,0,ITEM_EQUIP_ACK_FAIL);
+		clif_equipitemack( *sd, ITEM_EQUIP_ACK_FAIL, index );
 		return 1;
 	}
 

+ 85 - 64
src/map/script.cpp

@@ -4967,7 +4967,7 @@ BUILDIN_FUNC(clear)
 	if (!script_rid2sd(sd))
 		return SCRIPT_CMD_FAILURE;
 
-	clif_scriptclear(sd, st->oid);
+	clif_scriptclear( *sd, st->oid );
 	return SCRIPT_CMD_SUCCESS;
 }
 
@@ -4977,20 +4977,26 @@ BUILDIN_FUNC(clear)
 /// close;
 BUILDIN_FUNC(close)
 {
-	TBL_PC* sd;
+	struct map_session_data* sd;
 
 	if( !script_rid2sd(sd) )
 		return SCRIPT_CMD_SUCCESS;
 
+	const char* command = script_getfuncname( st );
+
 	if( !st->mes_active ) {
 		st->state = END; // Keep backwards compatibility.
-		ShowWarning("Incorrect use of 'close' command!\n");
+		ShowWarning("buildin_close: Incorrect use of '%s' command!\n", command);
 		script_reportsrc(st);
 	} else {
 		st->state = CLOSE;
 		st->mes_active = 0;
 	}
 
+	if( !strcmp(command, "close3") ){
+		st->clear_cutin = true;
+	}
+
 	clif_scriptclose(sd, st->oid);
 	return SCRIPT_CMD_SUCCESS;
 }
@@ -18406,58 +18412,48 @@ BUILDIN_FUNC(checkidlemer)
 
 BUILDIN_FUNC(searchitem)
 {
-	struct script_data* data = script_getdata(st, 2);
-	const char *itemname = script_getstr(st,3);
-	std::map<t_itemid, std::shared_ptr<item_data>> items;
-	int count;
-
-	char* name;
-	int32 start;
-	int32 id;
-	int32 i;
-	TBL_PC* sd = NULL;
-
-	if ((items[0] = item_db.find(strtoul(itemname, nullptr, 10))))
-		count = 1;
-	else
-		count = itemdb_searchname_array(items, MAX_SEARCH, itemname);
-
-	if (!count) {
-		script_pushint(st, 0);
-		return SCRIPT_CMD_SUCCESS;
-	}
+	script_data* data = script_getdata(st, 2);
+	const char *name = reference_getname(data);
 
 	if( !data_isreference(data) )
 	{
-		ShowError("script:searchitem: not a variable\n");
+		ShowError("buildin_searchitem: Argument %s is not a variable.\n", name);
 		script_reportdata(data);
 		st->state = END;
 		return SCRIPT_CMD_FAILURE;// not a variable
 	}
 
-	id = reference_getid(data);
-	start = reference_getindex(data);
-	name = reference_getname(data);
-
-	if( not_server_variable(*name) && !script_rid2sd(sd) )
-	{
-		return SCRIPT_CMD_SUCCESS;// no player attached
-	}
-
 	if( is_string_variable(name) )
 	{// string array
-		ShowError("script:searchitem: not an integer array reference\n");
+		ShowError("buildin_searchitem: Argument %s is not an integer array.\n", name);
 		script_reportdata(data);
 		st->state = END;
 		return SCRIPT_CMD_FAILURE;// not supported
 	}
 
-	for( i = 0; i < count; ++start, ++i )
-	{// Set array
-		set_reg_num( st, sd, reference_uid( id, start ), name, items[i]->nameid, reference_getref( data ) );
+	map_session_data *sd = nullptr;
+
+	if (not_server_variable(*name) && !script_rid2sd(sd))
+	{
+		return SCRIPT_CMD_SUCCESS;// no player attached
+	}
+
+	const char *itemname = script_getstr(st, 3);
+	std::map<t_itemid, std::shared_ptr<item_data>> items;
+
+	itemdb_searchname_array(items, MAX_SEARCH, itemname);
+
+	if (!items.empty()) {
+		int32 id = reference_getid(data);
+		int32 start = reference_getindex(data);
+
+		for (const auto &it : items) { // Set array
+			set_reg_num(st, sd, reference_uid(id, start), name, it.first, reference_getref(data));
+			start++;
+		}
 	}
 
-	script_pushint(st, count);
+	script_pushint64(st, items.size());
 	return SCRIPT_CMD_SUCCESS;
 }
 
@@ -19872,29 +19868,6 @@ BUILDIN_FUNC(unittalk)
 	return SCRIPT_CMD_SUCCESS;
 }
 
-/// Makes the unit do an emotion.
-///
-/// unitemote <unit_id>,<emotion>;
-///
-/// @see ET_* in script_constants.hpp
-BUILDIN_FUNC(unitemote)
-{
-	int emotion;
-	struct block_list* bl;
-
-	emotion = script_getnum(st,3);
-
-	if (emotion < ET_SURPRISE || emotion >= ET_MAX) {
-		ShowWarning("buildin_emotion: Unknown emotion %d (min=%d, max=%d).\n", emotion, ET_SURPRISE, (ET_MAX-1));
-		return SCRIPT_CMD_FAILURE;
-	}
-
-	if (script_rid2bl(2,bl))
-		clif_emotion(bl, emotion);
-
-	return SCRIPT_CMD_SUCCESS;
-}
-
 /// Makes the unit cast the skill on the target or self if no target is specified.
 ///
 /// unitskilluseid <unit_id>,<skill_id>,<skill_lv>{,<target_id>,<casttime>,<cancel>,<Line_ID>};
@@ -23633,7 +23606,7 @@ BUILDIN_FUNC(mergeitem) {
 	if (!script_charid2sd(2, sd))
 		return SCRIPT_CMD_FAILURE;
 
-	clif_merge_item_open(sd);
+	clif_merge_item_open( *sd );
 	return SCRIPT_CMD_SUCCESS;
 }
 
@@ -25230,7 +25203,7 @@ BUILDIN_FUNC(duplicate_dynamic){
 
 	if( dnd == nullptr ){
 		script_pushstrcopy( st, "" );
-		return SCRIPT_CMD_FAILURE;
+		return SCRIPT_CMD_SUCCESS;
 	}else{
 		script_pushstrcopy( st, dnd->exname );
 		return SCRIPT_CMD_SUCCESS;
@@ -26774,6 +26747,41 @@ BUILDIN_FUNC(item_enchant){
 #endif
 }
 
+/**
+* Generate item link string for client
+* itemlink(<item_id>,<refine>,<card0>,<card1>,<card2>,<card3>,<enchantgrade>{,<RandomIDArray>,<RandomValueArray>,<RandomParamArray>});
+* @author [Cydh]
+**/
+BUILDIN_FUNC(itemlink)
+{
+	struct item item = {};
+
+	item.nameid = script_getnum(st, 2);
+	
+	if( !item_db.exists( item.nameid ) ){
+		ShowError( "buildin_itemlink: Item ID %u does not exists.\n", item.nameid );
+		st->state = END;
+		return SCRIPT_CMD_FAILURE;
+	}
+
+	FETCH(3, item.refine);
+	FETCH(4, item.card[0]);
+	FETCH(5, item.card[1]);
+	FETCH(6, item.card[2]);
+	FETCH(7, item.card[3]);
+	FETCH(8, item.enchantgrade);
+
+#if PACKETVER >= 20150225
+	if ( script_hasdata(st,9) && script_getitem_randomoption(st, nullptr, &item, "itemlink", 9) == false) {
+		st->state = END;
+		return SCRIPT_CMD_FAILURE;
+	}
+#endif
+
+	std::string itemlstr = item_db.create_item_link(item);
+	script_pushstrcopy(st, itemlstr.c_str());
+	return SCRIPT_CMD_SUCCESS;
+}
 
 BUILDIN_FUNC(addfame) {
 	struct map_session_data *sd;
@@ -26810,6 +26818,17 @@ BUILDIN_FUNC(getfamerank) {
 	return SCRIPT_CMD_SUCCESS;
 }
 
+BUILDIN_FUNC(isdead) {
+	struct map_session_data *sd;
+
+	if (!script_mapid2sd(2, sd))
+		return SCRIPT_CMD_FAILURE;
+
+	script_pushint(st, pc_isdead(sd));
+
+	return SCRIPT_CMD_SUCCESS;
+}
+
 #include "../custom/script.inc"
 
 // declarations that were supposed to be exported from npc_chat.cpp
@@ -26867,6 +26886,7 @@ struct script_function buildin_func[] = {
 	BUILDIN_DEF(clear,""),
 	BUILDIN_DEF(close,""),
 	BUILDIN_DEF(close2,""),
+	BUILDIN_DEF2(close, "close3", ""),
 	BUILDIN_DEF(menu,"sl*"),
 	BUILDIN_DEF(select,"s*"), //for future jA script compatibility
 	BUILDIN_DEF(prompt,"s*"),
@@ -27277,7 +27297,6 @@ struct script_function buildin_func[] = {
 	BUILDIN_DEF(unitstopattack,"i"),
 	BUILDIN_DEF(unitstopwalk,"i?"),
 	BUILDIN_DEF(unittalk,"is?"),
-	BUILDIN_DEF_DEPRECATED(unitemote,"ii","20170811"),
 	BUILDIN_DEF(unitskilluseid,"ivi????"), // originally by Qamera [Celest]
 	BUILDIN_DEF(unitskillusepos,"iviii???"), // [Celest]
 // <--- [zBuffer] List of unit control commands
@@ -27558,9 +27577,11 @@ struct script_function buildin_func[] = {
 	BUILDIN_DEF(add_reputation_points, "ii?"),
 	BUILDIN_DEF(item_reform, "??"),
 	BUILDIN_DEF(item_enchant, "i?"),
+	BUILDIN_DEF(itemlink, "i?????????"),
 	BUILDIN_DEF(addfame, "i?"),
 	BUILDIN_DEF(getfame, "?"),
 	BUILDIN_DEF(getfamerank, "?"),
+	BUILDIN_DEF(isdead, "?"),
 #include "../custom/script_def.inc"
 
 	{NULL,NULL,NULL},

+ 1 - 0
src/map/script.hpp

@@ -328,6 +328,7 @@ struct script_state {
 	unsigned op2ref : 1;// used by op_2
 	unsigned npc_item_flag : 1;
 	unsigned mes_active : 1;  // Store if invoking character has a NPC dialog box open.
+	unsigned clear_cutin : 1;
 	char* funcname; // Stores the current running function name
 	unsigned int id;
 };

+ 1 - 0
src/map/script_constants.hpp

@@ -536,6 +536,7 @@
 	export_constant(MF_NORENEWALEXPPENALTY);
 	export_constant(MF_NOPETCAPTURE);
 	export_constant(MF_NOBUYINGSTORE);
+	export_constant(MF_NODYNAMICNPC);
 
 	/* setcell types */
 	export_constant(CELL_WALKABLE);

+ 5 - 4
src/map/status.cpp

@@ -12815,8 +12815,9 @@ int status_change_start(struct block_list* src, struct block_list* bl,enum sc_ty
 			break;
 		case SC_C_MARKER:
 			//Send mini-map, don't wait for first timer triggered
-			if (src->type == BL_PC && (sd = map_id2sd(src->id)))
-				clif_crimson_marker(sd, bl, false);
+			if (src->type == BL_PC) {
+				clif_crimson_marker(*(struct map_session_data *)(src), *bl, false);
+			}
 			break;
 		case SC_ITEMSCRIPT: // Shows Buff Icons
 			if (sd)
@@ -13419,7 +13420,7 @@ int status_change_end(struct block_list* bl, enum sc_type type, int tid)
 				ARR_FIND(0,MAX_SKILL_CRIMSON_MARKER,i,caster->c_marker[i] == bl->id);
 				if (i < MAX_SKILL_CRIMSON_MARKER) {
 					caster->c_marker[i] = 0;
-					clif_crimson_marker(caster, bl, true);
+					clif_crimson_marker( *caster, *bl, true );
 				}
 			}
 			break;
@@ -14417,7 +14418,7 @@ TIMER_FUNC(status_change_timer){
 			if (!caster || caster->bl.m != bl->m) //End the SC if caster isn't in same map
 				break;
 			sc_timer_next(1000 + tick);
-			clif_crimson_marker(caster, bl, false);
+			clif_crimson_marker( *caster, *bl, false );
 			return 0;
 		}
 		break;

+ 2 - 5
src/map/unit.cpp

@@ -1858,10 +1858,7 @@ int unit_skilluse_id2(struct block_list *src, int target_id, uint16 skill_id, ui
 		}
 	}
 
-	if (src->type == BL_NPC) // NPC-objects can override cast distance
-		range = AREA_SIZE; // Maximum visible distance before NPC goes out of sight
-	else
-		range = skill_get_range2(src, skill_id, skill_lv, true); // Skill cast distance from database
+	range = skill_get_range2(src, skill_id, skill_lv, true); // Skill cast distance from database
 
 	// New action request received, delete previous action request if not executed yet
 	if(ud->stepaction || ud->steptimer != INVALID_TIMER)
@@ -1877,7 +1874,7 @@ int unit_skilluse_id2(struct block_list *src, int target_id, uint16 skill_id, ui
 
 	// Check range when not using skill on yourself or is a combo-skill during attack
 	// (these are supposed to always have the same range as your attack)
-	if( src->id != target_id && (!combo || ud->attacktimer == INVALID_TIMER) ) {
+	if( src->type != BL_NPC && src->id != target_id && (!combo || ud->attacktimer == INVALID_TIMER) ) {
 		if( skill_get_state(ud->skill_id) == ST_MOVE_ENABLE ) {
 			if( !unit_can_reach_bl(src, target, range + 1, 1, NULL, NULL) )
 				return 0; // Walk-path check failed.

+ 2 - 2
tools/ci/npc.sh

@@ -5,8 +5,8 @@ out=npc/scripts_custom.conf
 printf "\n" >> $out
 echo "// Custom Scripts" >> $out
 
-find npc/custom \( -name "*.txt" \) | xargs -I % echo "npc: %" >> $out
+find npc/custom \( -name "*.txt" \) | sort | xargs -I % echo "npc: %" >> $out
 
 echo "// Test Scripts" >> $out
 
-find npc/test \( -name "*.txt" \) | xargs -I % echo "npc: %" >> $out
+find npc/test \( -name "*.txt" \) | sort | xargs -I % echo "npc: %" >> $out

+ 4 - 4
uninstall.sh

@@ -1,20 +1,20 @@
 #!/bin/sh
 #source var/function
 . ./function.sh
-echo "My pkg path is $PKG_PATH"
+echo "My pkg path is ${PKG_PATH}"
 
 check_inst_right
 read -p "WARNING: This script is experimental. Press Ctrl+C to cancel or Enter to continue." readEnterKey
 case $1 in
 	'bin')
 		echo "Starting binary cleanup"
-		rm -rf $PKG_PATH/bin/*
+		rm -rf "${PKG_PATH:?}"/bin/*
 		echo "Binary files have been deleted"
 	;;
 	'all')
 		echo "Starting uninstall"
-		rm -rf $PKG_PATH
-		rm -rf /usr/bin/$PKG
+		rm -rf "${PKG_PATH:?}"
+		rm -rf "/usr/bin/${PKG:?}"
 		echo "Uninstallation has succeed"
 	;;
 	*)