summaryrefslogtreecommitdiff
path: root/src/map
diff options
context:
space:
mode:
Diffstat (limited to 'src/map')
-rw-r--r--src/map/Makefile99
-rw-r--r--src/map/Makefile.win3299
-rw-r--r--src/map/atcommand.c10039
-rw-r--r--src/map/atcommand.h319
-rw-r--r--src/map/battle.c4412
-rw-r--r--src/map/battle.h428
-rw-r--r--src/map/charcommand.c1794
-rw-r--r--src/map/charcommand.h68
-rw-r--r--src/map/charsave.c516
-rw-r--r--src/map/charsave.h16
-rw-r--r--src/map/chat.c371
-rw-r--r--src/map/chat.h22
-rw-r--r--src/map/chrif.c1572
-rw-r--r--src/map/chrif.h54
-rw-r--r--src/map/clif.c12146
-rw-r--r--src/map/clif.h343
-rw-r--r--src/map/date.c72
-rw-r--r--src/map/date.h17
-rw-r--r--src/map/guild.c1976
-rw-r--r--src/map/guild.h96
-rw-r--r--src/map/intif.c1401
-rw-r--r--src/map/intif.h61
-rw-r--r--src/map/itemdb.c1103
-rw-r--r--src/map/itemdb.h106
-rw-r--r--src/map/log.c956
-rw-r--r--src/map/log.h47
-rw-r--r--src/map/mail.c361
-rw-r--r--src/map/mail.h12
-rw-r--r--src/map/map.c3952
-rw-r--r--src/map/map.h1409
-rw-r--r--src/map/mercenary.c11
-rw-r--r--src/map/mercenary.h11
-rw-r--r--src/map/mob.c5020
-rw-r--r--src/map/mob.h173
-rw-r--r--src/map/npc.c2826
-rw-r--r--src/map/npc.h71
-rw-r--r--src/map/npc_chat.c519
-rw-r--r--src/map/party.c742
-rw-r--r--src/map/party.h47
-rw-r--r--src/map/path.c483
-rw-r--r--src/map/pc.c8305
-rw-r--r--src/map/pc.h251
-rw-r--r--src/map/pcre.h258
-rw-r--r--src/map/pet.c1973
-rw-r--r--src/map/pet.h73
-rw-r--r--src/map/script.c10736
-rw-r--r--src/map/script.h73
-rw-r--r--src/map/skill.c12348
-rw-r--r--src/map/skill.h879
-rw-r--r--src/map/status.c6038
-rw-r--r--src/map/status.h529
-rw-r--r--src/map/storage.c693
-rw-r--r--src/map/storage.h45
-rw-r--r--src/map/trade.c569
-rw-r--r--src/map/trade.h15
-rw-r--r--src/map/vending.c261
-rw-r--r--src/map/vending.h14
57 files changed, 96830 insertions, 0 deletions
diff --git a/src/map/Makefile b/src/map/Makefile
new file mode 100644
index 000000000..f3073d476
--- /dev/null
+++ b/src/map/Makefile
@@ -0,0 +1,99 @@
+all txt: txtobj map-server
+
+sql: sqlobj map-server_sql
+
+txtobj:
+ mkdir txtobj
+
+sqlobj:
+ mkdir sqlobj
+
+COMMON_OBJ = ../common/obj/core.o ../common/obj/socket.o ../common/obj/timer.o \
+ ../common/obj/db.o ../common/obj/plugins.o ../common/obj/lock.o \
+ ../common/obj/nullpo.o ../common/obj/malloc.o ../common/obj/showmsg.o \
+ ../common/obj/utils.o ../common/obj/strlib.o ../common/obj/grfio.o \
+ ../common/obj/graph.o ../common/obj/mapindex.o ../common/obj/ers.o \
+ ../zlib/unz.o
+
+COMMON_H = ../common/core.h ../common/socket.h ../common/timer.h ../common/db.h \
+ ../common/plugins.h ../common/lock.h ../common/nullpo.h ../common/malloc.h \
+ ../common/showmsg.h ../common/utils.h ../common/strlib.h ../common/grfio.h \
+ ../common/graph.h ../common/mapindex.h
+
+OBJECTS = obj/map.o obj/chrif.o obj/clif.o obj/pc.o obj/status.o obj/npc.o \
+ obj/npc_chat.o obj/chat.o obj/path.o obj/itemdb.o obj/mob.o obj/script.o \
+ obj/storage.o obj/skill.o obj/atcommand.o obj/charcommand.o obj/battle.o \
+ obj/intif.o obj/trade.o obj/party.o obj/vending.o obj/guild.o obj/pet.o \
+ obj/log.o obj/mail.o obj/charsave.o obj/date.o $(COMMON_OBJ)
+
+map-server: $(OBJECTS:obj/%=txtobj/%)
+ $(CC) -o ../../$@ $> $(LIBS) $(LIB_S)
+
+map-server_sql: $(OBJECTS:obj/%=sqlobj/%)
+ $(CC) -o ../../$@ $> $(LIB_S)
+
+txtobj/%.o: %.c
+ $(COMPILE.c) -DTXT_ONLY $(OUTPUT_OPTION) $<
+
+sqlobj/%.o: %.c
+ $(COMPILE.c) $(OUTPUT_OPTION) $<
+
+clean:
+ rm -rf *.o ../../map-server ../../map-server_sql sqlobj txtobj
+
+# DO NOT DELETE
+
+txtobj/map.o: map.c map.h chrif.h clif.h npc.h pc.h mob.h chat.h skill.h itemdb.h storage.h party.h pet.h atcommand.h $(COMMON_H)
+txtobj/chrif.o: chrif.c map.h battle.h chrif.h clif.h intif.h pc.h npc.h $(COMMON_H)
+txtobj/clif.o: clif.c map.h chrif.h clif.h mob.h intif.h pc.h npc.h itemdb.h chat.h script.h storage.h party.h guild.h atcommand.h pet.h charcommand.h $(COMMON_H)
+txtobj/pc.o: pc.c map.h clif.h intif.h pc.h npc.h mob.h itemdb.h battle.h skill.h script.h party.h guild.h pet.h trade.h storage.h chat.h vending.h $(COMMON_H)
+txtobj/status.o: status.c pc.h map.h clif.h status.h mob.h itemdb.h battle.h skill.h script.h pet.h guild.h $(COMMON_H)
+txtobj/npc.o: npc.c map.h npc.h clif.h pc.h script.h mob.h itemdb.h battle.h $(COMMON_H)
+txtobj/npc_chat.o: npc_chat.c map.h npc.h clif.h pc.h script.h mob.h itemdb.h battle.h $(COMMON_H)
+txtobj/chat.o: chat.c map.h clif.h pc.h chat.h $(COMMON_H)
+txtobj/path.o: path.c map.h battle.h $(COMMON_H)
+txtobj/itemdb.o: itemdb.c map.h battle.h itemdb.h $(COMMON_H)
+txtobj/mob.o: mob.c map.h clif.h intif.h pc.h mob.h skill.h battle.h npc.h itemdb.h date.h $(COMMON_H)
+txtobj/script.o: script.c itemdb.h map.h pc.h mob.h clif.h intif.h npc.h script.h storage.h skill.h pet.h battle.h log.h $(COMMON_H)
+txtobj/storage.o: storage.c itemdb.h pc.h clif.h intif.h storage.h guild.h $(COMMON_H)
+txtobj/skill.o: skill.c skill.h map.h clif.h pc.h mob.h battle.h itemdb.h script.h date.h $(COMMON_H)
+txtobj/atcommand.o: atcommand.c atcommand.h itemdb.h pc.h map.h skill.h clif.h mob.h intif.h battle.h storage.h guild.h pet.h log.h $(COMMON_H)
+txtobj/battle.o: battle.c battle.h skill.h map.h mob.h pc.h pet.h guild.h $(COMMON_H)
+txtobj/intif.o: intif.c intif.h chrif.h clif.h party.h guild.h storage.h map.h battle.h pet.h $(COMMON_H)
+txtobj/trade.o: trade.c trade.h clif.h itemdb.h map.h pc.h npc.h $(COMMON_H)
+txtobj/party.o: party.c party.h clif.h intif.h pc.h map.h battle.h $(COMMON_H)
+txtobj/vending.o: vending.c vending.h clif.h itemdb.h map.h pc.h $(COMMON_H)
+txtobj/guild.o: guild.c guild.h storage.h battle.h clif.h intif.h pc.h npc.h map.h $(COMMON_H)
+txtobj/pet.o: pet.c pet.h map.h clif.h chrif.h intif.h pc.h itemdb.h battle.h mob.h npc.h script.h $(COMMON_H)
+txtobj/log.o: log.c log.h map.h $(COMMON_H)
+txtobj/charcommand.o: charcommand.c charcommand.h itemdb.h pc.h map.h skill.h clif.h mob.h intif.h battle.h storage.h guild.h pet.h log.h $(COMMON_H)
+txtobj/date.o: date.c date.h $(COMMON_H)
+
+
+sqlobj/map.o: map.c map.h chrif.h clif.h npc.h pc.h mob.h chat.h skill.h itemdb.h storage.h party.h pet.h atcommand.h log.h $(COMMON_H)
+sqlobj/chrif.o: chrif.c map.h battle.h chrif.h clif.h intif.h pc.h npc.h $(COMMON_H)
+sqlobj/clif.o: clif.c map.h chrif.h clif.h mob.h intif.h pc.h npc.h itemdb.h chat.h script.h storage.h party.h guild.h atcommand.h pet.h charcommand.h $(COMMON_H)
+sqlobj/pc.o: pc.c map.h clif.h intif.h pc.h npc.h mob.h itemdb.h battle.h skill.h script.h party.h guild.h pet.h trade.h storage.h chat.h vending.h log.h $(COMMON_H)
+sqlobj/status.o: status.c pc.h map.h clif.h status.h mob.h itemdb.h battle.h skill.h script.h pet.h guild.h $(COMMON_H)
+sqlobj/npc.o: npc.c map.h npc.h clif.h pc.h script.h mob.h itemdb.h battle.h $(COMMON_H)
+sqlobj/npc_chat.o: npc_chat.c map.h npc.h clif.h pc.h script.h mob.h itemdb.h battle.h $(COMMON_H)
+sqlobj/chat.o: chat.c map.h clif.h pc.h chat.h $(COMMON_H)
+sqlobj/path.o: path.c map.h battle.h $(COMMON_H)
+sqlobj/itemdb.o: itemdb.c map.h battle.h itemdb.h $(COMMON_H)
+sqlobj/mob.o: mob.c map.h clif.h intif.h pc.h mob.h skill.h battle.h npc.h itemdb.h log.h date.h $(COMMON_H)
+sqlobj/script.o: script.c itemdb.h map.h pc.h mob.h clif.h intif.h npc.h script.h storage.h skill.h pet.h battle.h log.h $(COMMON_H)
+sqlobj/storage.o: storage.c itemdb.h pc.h clif.h intif.h storage.h guild.h $(COMMON_H)
+sqlobj/skill.o: skill.c skill.h map.h clif.h pc.h mob.h battle.h itemdb.h script.h log.h date.h $(COMMON_H)
+sqlobj/atcommand.o: atcommand.c atcommand.h itemdb.h pc.h map.h skill.h clif.h mob.h intif.h battle.h storage.h guild.h pet.h log.h $(COMMON_H)
+sqlobj/battle.o: battle.c battle.h skill.h map.h mob.h pc.h pet.h guild.h $(COMMON_H)
+sqlobj/intif.o: intif.c intif.h chrif.h clif.h party.h guild.h storage.h map.h battle.h pet.h $(COMMON_H)
+sqlobj/trade.o: trade.c trade.h clif.h itemdb.h map.h pc.h npc.h log.h $(COMMON_H)
+sqlobj/party.o: party.c party.h clif.h intif.h pc.h map.h battle.h $(COMMON_H)
+sqlobj/vending.o: vending.c vending.h clif.h itemdb.h map.h pc.h log.h $(COMMON_H)
+sqlobj/guild.o: guild.c guild.h storage.h battle.h clif.h intif.h pc.h npc.h map.h $(COMMON_H)
+sqlobj/pet.o: pet.c pet.h map.h clif.h chrif.h intif.h pc.h itemdb.h battle.h mob.h npc.h script.h $(COMMON_H)
+sqlobj/mail.o: mail.c mail.h $(COMMON_H)
+sqlobj/log.o: log.c log.h map.h $(COMMON_H)
+sqlobj/charcommand.o: charcommand.c charcommand.h itemdb.h pc.h map.h skill.h clif.h mob.h intif.h battle.h storage.h guild.h pet.h log.h $(COMMON_H)
+sqlobj/charsave.o: charsave.c charsave.h $(COMMON_H)
+sqlobj/date.o: date.c date.h $(COMMON_H)
diff --git a/src/map/Makefile.win32 b/src/map/Makefile.win32
new file mode 100644
index 000000000..4e1be4cc5
--- /dev/null
+++ b/src/map/Makefile.win32
@@ -0,0 +1,99 @@
+# grab a copy of http://www.winimage.com/zLibDll/zlib122.zip
+# and put safely into a subdirectory someplace
+# then point ZLIBDIR at whereever you put it
+#
+
+all: txt sql
+
+txt: txtobj map-server
+
+sql: sqlobj map-server_sql
+
+txtobj:
+ mkdir txtobj
+
+sqlobj:
+ mkdir sqlobj
+
+ZLIBDIR = ../zlib
+PACKETDEF = -DPACKETVER=6 -DNEW_006b -D__WIN32 -DLOCALZLIB
+# OPT = /MDd /D_DEBUG
+OPT =
+LINKOPT = /debug /SUBSYSTEM:CONSOLE
+# OPT = /O2
+CFLAGS = $(OPT) /nologo /I../common /I$(ZLIBDIR) $(PACKETDEF) /D_WIN32
+
+COMMON_OBJ = ../common/core.o ../common/socket.o ../common/timer.o ../common/grfio.o ../common/db.o ../common/lock.o ../common/nullpo.o ../common/malloc.o ../common/showmsg.o ../common/strlib.o ../common/utils.o
+
+LIBS = "WSOCK32.LIB"
+
+# "WSOCK32.LIB" "USER32.LIB" "ADVAPI32.LIB" "MSVCRT.LIB" "OLDNAMES.LIB" "KERNEL32.LIB"
+
+TXTOBJS = txtobj/map.o txtobj/chrif.o txtobj/clif.o txtobj/pc.o txtobj/status.o txtobj/npc.o txtobj/npc_chat.o txtobj/chat.o txtobj/path.o txtobj/itemdb.o txtobj/mob.o txtobj/script.o txtobj/storage.o txtobj/skill.o txtobj/atcommand.o txtobj/charcommand.o txtobj/battle.o txtobj/intif.o txtobj/trade.o txtobj/party.o txtobj/vending.o txtobj/guild.o txtobj/pet.o txtobj/log.o txtobj/date.o $(COMMON_OBJ) $(ZLIBDIR)/inflate.o $(ZLIBDIR)/deflate.o $(ZLIBDIR)/trees.o $(ZLIBDIR)/adler32.o $(ZLIBDIR)/compress.o $(ZLIBDIR)/crc32.o $(ZLIBDIR)/inftrees.o $(ZLIBDIR)/zutil.o $(ZLIBDIR)/inffast.o
+
+SQLOBJS = sqlobj/map.o sqlobj/chrif.o sqlobj/clif.o sqlobj/pc.o sqlobj/status.o sqlobj/npc.o sqlobj/npc_chat.o sqlobj/chat.o sqlobj/path.o sqlobj/itemdb.o sqlobj/mob.o sqlobj/script.o sqlobj/storage.o sqlobj/skill.o sqlobj/atcommand.o sqlobj/charcommand.o sqlobj/battle.o sqlobj/intif.o sqlobj/trade.o sqlobj/party.o sqlobj/vending.o sqlobj/guild.o sqlobj/pet.o sqlobj/log.o sqlobj/date.o sqlobj/charsave.o $(COMMON_OBJ) $(ZLIBDIR)/inflate.o $(ZLIBDIR)/adler32.o $(ZLIBDIR)/crc32.o $(ZLIBDIR)/inftrees.o $(ZLIBDIR)/zutil.o $(ZLIBDIR)/inffast.o
+
+map-server: $(TXTOBJS)
+ link $(LINKOPT) /out:../../$@.exe $(TXTOBJS) $(LIBS)
+
+map-server_sql: $(SQLOBJS)
+ link $(LINKOPT) /out:../../$@.exe $> $(LIBS)
+
+txtobj/%.o: %.c
+ Cl /c $(CFLAGS) -DTXT_ONLY /Fo$@ $<
+
+sqlobj/%.o: %.c
+ Cl /c $(CFLAGS) /Fo$@ $<
+
+%.o: %.c
+ Cl /c $(CFLAGS) /Fo$@ $<
+
+txtobj/map.o: map.c map.h chrif.h clif.h npc.h pc.h mob.h chat.h skill.h itemdb.h storage.h party.h pet.h atcommand.h ../common/core.h ../common/timer.h ../common/db.h ../common/grfio.h ../common/mmo.h ../common/showmsg.h
+txtobj/chrif.o: chrif.c map.h battle.h chrif.h clif.h intif.h pc.h npc.h ../common/socket.h ../common/timer.h ../common/mmo.h ../common/showmsg.h
+txtobj/clif.o: clif.c map.h chrif.h clif.h mob.h intif.h pc.h npc.h itemdb.h chat.h script.h storage.h party.h guild.h atcommand.h pet.h atcommand.h ../common/socket.h ../common/timer.h ../common/mmo.h ../common/version.h ../common/showmsg.h
+txtobj/pc.o: pc.c map.h clif.h intif.h pc.h npc.h mob.h itemdb.h battle.h skill.h script.h party.h guild.h pet.h trade.h storage.h chat.h vending.h ../common/timer.h ../common/mmo.h ../common/db.h ../common/showmsg.h
+txtobj/npc.o: npc.c map.h npc.h clif.h pc.h script.h mob.h itemdb.h battle.h ../common/db.h ../common/timer.h ../common/mmo.h ../common/showmsg.h
+txtobj/chat.o: chat.c map.h clif.h pc.h chat.h ../common/db.h ../common/mmo.h ../common/showmsg.h
+txtobj/path.o: path.c map.h battle.h ../common/mmo.h ../common/showmsg.h
+txtobj/itemdb.o: itemdb.c map.h battle.h itemdb.h ../common/db.h ../common/grfio.h ../common/mmo.h ../common/showmsg.h
+txtobj/mob.o: mob.c map.h clif.h intif.h pc.h mob.h skill.h battle.h npc.h itemdb.h date.h ../common/timer.h ../common/socket.h ../common/mmo.h ../common/showmsg.h
+txtobj/script.o: script.c itemdb.h map.h pc.h mob.h clif.h intif.h npc.h script.h storage.h skill.h pet.h battle.h ../common/timer.h ../common/socket.h ../common/db.h ../common/mmo.h ../common/lock.h ../common/showmsg.h
+txtobj/storage.o: storage.c itemdb.h pc.h clif.h intif.h storage.h guild.h ../common/mmo.h ../common/db.h ../common/showmsg.h
+txtobj/skill.o: skill.c skill.h map.h clif.h pc.h mob.h battle.h itemdb.h script.h date.h ../common/timer.h ../common/mmo.h ../common/showmsg.h
+txtobj/atcommand.o: atcommand.c atcommand.h itemdb.h pc.h map.h skill.h clif.h mob.h intif.h battle.h storage.h guild.h pet.h log.h ../common/socket.h ../common/timer.h ../common/mmo.h ../common/showmsg.h
+txtobj/battle.o: battle.c battle.h skill.h map.h mob.h pc.h pet.h guild.h ../common/timer.h ../common/mmo.h ../common/showmsg.h
+txtobj/intif.o: intif.c intif.h chrif.h clif.h party.h guild.h storage.h map.h battle.h pet.h ../common/socket.h ../common/mmo.h ../common/showmsg.h
+txtobj/trade.o: trade.c trade.h clif.h itemdb.h map.h pc.h npc.h ../common/mmo.h ../common/showmsg.h
+txtobj/party.o: party.c party.h clif.h intif.h pc.h map.h battle.h ../common/db.h ../common/socket.h ../common/timer.h ../common/mmo.h ../common/showmsg.h
+txtobj/vending.o: vending.c vending.h clif.h itemdb.h map.h pc.h ../common/mmo.h ../common/showmsg.h
+txtobj/guild.o: guild.c guild.h storage.h battle.h clif.h intif.h pc.h npc.h map.h ../common/db.h ../common/socket.h ../common/timer.h ../common/mmo.h ../common/showmsg.h
+txtobj/pet.o: pet.c pet.h map.h clif.h chrif.h intif.h pc.h itemdb.h battle.h mob.h npc.h script.h ../common/db.h ../common/socket.h ../common/timer.h ../common/mmo.h ../common/showmsg.h
+txtobj/date.o: date.c date.h ../common/timer.h
+
+sqlobj/map.o: map.c map.h chrif.h clif.h npc.h pc.h mob.h chat.h skill.h itemdb.h storage.h party.h pet.h atcommand.h log.h ../common/core.h ../common/timer.h ../common/db.h ../common/grfio.h ../common/mmo.h ../common/showmsg.h
+sqlobj/chrif.o: chrif.c map.h battle.h chrif.h clif.h intif.h pc.h npc.h ../common/socket.h ../common/timer.h ../common/mmo.h ../common/showmsg.h
+sqlobj/clif.o: clif.c map.h chrif.h clif.h mob.h intif.h pc.h npc.h itemdb.h chat.h script.h storage.h party.h guild.h atcommand.h pet.h atcommand.h ../common/socket.h ../common/timer.h ../common/mmo.h ../common/version.h ../common/showmsg.h
+sqlobj/pc.o: pc.c map.h clif.h intif.h pc.h npc.h mob.h itemdb.h battle.h skill.h script.h party.h guild.h pet.h trade.h storage.h chat.h vending.h log.h ../common/timer.h ../common/mmo.h ../common/db.h ../common/showmsg.h
+sqlobj/npc.o: npc.c map.h npc.h clif.h pc.h script.h mob.h itemdb.h battle.h ../common/db.h ../common/timer.h ../common/mmo.h ../common/showmsg.h
+sqlobj/chat.o: chat.c map.h clif.h pc.h chat.h ../common/db.h ../common/mmo.h ../common/showmsg.h
+sqlobj/path.o: path.c map.h battle.h ../common/mmo.h ../common/showmsg.h
+sqlobj/itemdb.o: itemdb.c map.h battle.h itemdb.h ../common/db.h ../common/grfio.h ../common/mmo.h ../common/showmsg.h
+sqlobj/mob.o: mob.c map.h clif.h intif.h pc.h mob.h skill.h battle.h npc.h itemdb.h log.h date.h ../common/timer.h ../common/socket.h ../common/mmo.h ../common/showmsg.h
+sqlobj/script.o: script.c itemdb.h map.h pc.h mob.h clif.h intif.h npc.h script.h storage.h skill.h pet.h battle.h log.h ../common/timer.h ../common/socket.h ../common/db.h ../common/mmo.h ../common/lock.h ../common/showmsg.h
+sqlobj/storage.o: storage.c itemdb.h pc.h clif.h intif.h storage.h guild.h ../common/mmo.h ../common/db.h ../common/showmsg.h
+sqlobj/skill.o: skill.c skill.h map.h clif.h pc.h mob.h battle.h itemdb.h script.h log.h date.h ../common/timer.h ../common/mmo.h ../common/showmsg.h
+sqlobj/atcommand.o: atcommand.c atcommand.h itemdb.h pc.h map.h skill.h clif.h mob.h intif.h battle.h storage.h guild.h pet.h log.h ../common/socket.h ../common/timer.h ../common/mmo.h ../common/showmsg.h
+sqlobj/battle.o: battle.c battle.h skill.h map.h mob.h pc.h pet.h guild.h ../common/timer.h ../common/mmo.h ../common/showmsg.h
+sqlobj/intif.o: intif.c intif.h chrif.h clif.h party.h guild.h storage.h map.h battle.h pet.h ../common/socket.h ../common/mmo.h ../common/showmsg.h
+sqlobj/trade.o: trade.c trade.h clif.h itemdb.h map.h pc.h npc.h log.h ../common/mmo.h ../common/showmsg.h
+sqlobj/party.o: party.c party.h clif.h intif.h pc.h map.h battle.h ../common/db.h ../common/socket.h ../common/timer.h ../common/mmo.h ../common/showmsg.h
+sqlobj/vending.o: vending.c vending.h clif.h itemdb.h map.h pc.h log.h ../common/mmo.h ../common/showmsg.h
+sqlobj/guild.o: guild.c guild.h storage.h battle.h clif.h intif.h pc.h npc.h map.h ../common/db.h ../common/socket.h ../common/timer.h ../common/mmo.h ../common/showmsg.h
+sqlobj/pet.o: pet.c pet.h map.h clif.h chrif.h intif.h pc.h itemdb.h battle.h mob.h npc.h script.h ../common/db.h ../common/socket.h ../common/timer.h ../common/mmo.h ../common/showmsg.h
+sqlobj/date.o: date.c date.h ../common/timer.h
+sqlobj/mail.o: mail.c mail.h ../common/showmsg.h ../common/strlib.h ../common/utils.h
+sqlobj/log.o: log.c log.h map.h ../common/nullpo.h
+sqlobj/charsave.o: charsave.c charsave.h $(COMMON_H)
+
+clean:
+ rm -rf *.o ../../map-server ../../map-server_sql sqlobj txtobj
diff --git a/src/map/atcommand.c b/src/map/atcommand.c
new file mode 100644
index 000000000..6d2d3cc2b
--- /dev/null
+++ b/src/map/atcommand.c
@@ -0,0 +1,10039 @@
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <math.h>
+
+#include "../common/socket.h"
+#include "../common/timer.h"
+#include "../common/nullpo.h"
+#include "../common/mmo.h"
+#include "../common/core.h"
+#include "../common/showmsg.h"
+
+#include "log.h"
+#include "clif.h"
+#include "chrif.h"
+#include "intif.h"
+#include "itemdb.h"
+#include "map.h"
+#include "pc.h"
+#include "status.h"
+#include "skill.h"
+#include "mob.h"
+#include "pet.h"
+#include "battle.h"
+#include "party.h"
+#include "guild.h"
+#include "atcommand.h"
+#include "script.h"
+#include "npc.h"
+#include "trade.h"
+
+#ifndef TXT_ONLY
+#include "mail.h"
+#endif
+
+static char command_symbol = '@'; // first char of the commands (by [Yor])
+
+char *msg_table[MAX_MSG]; // Server messages (0-499 reserved for GM commands, 500-999 reserved for others)
+
+#define ACMD_FUNC(x) int atcommand_ ## x (const int fd, struct map_session_data* sd, const char* command, const char* message)
+ACMD_FUNC(broadcast);
+ACMD_FUNC(localbroadcast);
+ACMD_FUNC(rura);
+ACMD_FUNC(where);
+ACMD_FUNC(jumpto);
+ACMD_FUNC(jump);
+ACMD_FUNC(who);
+ACMD_FUNC(who2);
+ACMD_FUNC(who3);
+ACMD_FUNC(whomap);
+ACMD_FUNC(whomap2);
+ACMD_FUNC(whomap3);
+ACMD_FUNC(whogm); // by Yor
+ACMD_FUNC(whozeny); // [Valaris]
+ACMD_FUNC(happyhappyjoyjoy); // [Valaris]
+ACMD_FUNC(save);
+ACMD_FUNC(load);
+ACMD_FUNC(speed);
+ACMD_FUNC(storage);
+ACMD_FUNC(guildstorage);
+ACMD_FUNC(option);
+ACMD_FUNC(hide);
+ACMD_FUNC(jobchange);
+ACMD_FUNC(die);
+ACMD_FUNC(kill);
+ACMD_FUNC(alive);
+ACMD_FUNC(kami);
+ACMD_FUNC(heal);
+ACMD_FUNC(item);
+ACMD_FUNC(item2);
+ACMD_FUNC(itemreset);
+ACMD_FUNC(itemcheck);
+ACMD_FUNC(baselevelup);
+ACMD_FUNC(joblevelup);
+ACMD_FUNC(help);
+ACMD_FUNC(help2);
+ACMD_FUNC(gm);
+ACMD_FUNC(pvpoff);
+ACMD_FUNC(pvpon);
+ACMD_FUNC(gvgoff);
+ACMD_FUNC(gvgon);
+ACMD_FUNC(model);
+ACMD_FUNC(go);
+ACMD_FUNC(monster);
+ACMD_FUNC(monstersmall);
+ACMD_FUNC(monsterbig);
+ACMD_FUNC(spawn);
+ACMD_FUNC(killmonster);
+ACMD_FUNC(killmonster2);
+ACMD_FUNC(refine);
+ACMD_FUNC(produce);
+ACMD_FUNC(memo);
+ACMD_FUNC(gat);
+ACMD_FUNC(packet);
+ACMD_FUNC(waterlevel);
+ACMD_FUNC(statuspoint);
+ACMD_FUNC(skillpoint);
+ACMD_FUNC(zeny);
+ACMD_FUNC(param);
+ACMD_FUNC(guildlevelup);
+ACMD_FUNC(makeegg);
+ACMD_FUNC(hatch);
+ACMD_FUNC(petfriendly);
+ACMD_FUNC(pethungry);
+ACMD_FUNC(petrename);
+ACMD_FUNC(recall);
+ACMD_FUNC(recallall);
+ACMD_FUNC(revive);
+ACMD_FUNC(night);
+ACMD_FUNC(day);
+ACMD_FUNC(doom);
+ACMD_FUNC(doommap);
+ACMD_FUNC(raise);
+ACMD_FUNC(raisemap);
+ACMD_FUNC(kick);
+ACMD_FUNC(kickall);
+ACMD_FUNC(allskill);
+ACMD_FUNC(questskill);
+ACMD_FUNC(lostskill);
+ACMD_FUNC(spiritball);
+ACMD_FUNC(party);
+ACMD_FUNC(guild);
+ACMD_FUNC(agitstart);
+ACMD_FUNC(agitend);
+ACMD_FUNC(reloaditemdb);
+ACMD_FUNC(reloadmobdb);
+ACMD_FUNC(reloadskilldb);
+ACMD_FUNC(reloadscript);
+ACMD_FUNC(reloadgmdb); // by Yor
+ACMD_FUNC(reloadatcommand);
+ACMD_FUNC(reloadbattleconf);
+ACMD_FUNC(reloadstatusdb);
+ACMD_FUNC(reloadpcdb);
+ACMD_FUNC(reloadmotd); // [Valaris]
+ACMD_FUNC(mapexit);
+ACMD_FUNC(idsearch);
+ACMD_FUNC(mapinfo);
+ACMD_FUNC(dye); //** by fritz
+ACMD_FUNC(hair_style); //** by fritz
+ACMD_FUNC(hair_color); //** by fritz
+ACMD_FUNC(stat_all); //** by fritz
+ACMD_FUNC(char_block); // by Yor
+ACMD_FUNC(char_ban); // by Yor
+ACMD_FUNC(char_unblock); // by Yor
+ACMD_FUNC(char_unban); // by Yor
+ACMD_FUNC(mount_peco); // by Valaris
+ACMD_FUNC(char_mount_peco); // by Yor
+ACMD_FUNC(guildspy); // [Syrus22]
+ACMD_FUNC(partyspy); // [Syrus22]
+ACMD_FUNC(repairall); // [Valaris]
+ACMD_FUNC(guildrecall); // by Yor
+ACMD_FUNC(partyrecall); // by Yor
+ACMD_FUNC(nuke); // [Valaris]
+ACMD_FUNC(shownpc);
+ACMD_FUNC(hidenpc);
+ACMD_FUNC(loadnpc);
+ACMD_FUNC(unloadnpc);
+ACMD_FUNC(servertime); // by Yor
+ACMD_FUNC(chardelitem); // by Yor
+ACMD_FUNC(jail); // by Yor
+ACMD_FUNC(unjail); // by Yor
+ACMD_FUNC(disguise); // [Valaris]
+ACMD_FUNC(undisguise); // by Yor
+ACMD_FUNC(chardisguise); // Kalaspuff
+ACMD_FUNC(charundisguise); // Kalaspuff
+ACMD_FUNC(email); // by Yor
+ACMD_FUNC(effect);//by Apple
+ACMD_FUNC(character_cart_list); // by Yor
+ACMD_FUNC(addwarp); // by MouseJstr
+ACMD_FUNC(follow); // by MouseJstr
+ACMD_FUNC(skillon); // by MouseJstr
+ACMD_FUNC(skilloff); // by MouseJstr
+ACMD_FUNC(killer); // by MouseJstr
+ACMD_FUNC(npcmove); // by MouseJstr
+ACMD_FUNC(killable); // by MouseJstr
+ACMD_FUNC(charkillable); // by MouseJstr
+ACMD_FUNC(dropall); // by MouseJstr
+ACMD_FUNC(chardropall); // by MouseJstr
+ACMD_FUNC(storeall); // by MouseJstr
+ACMD_FUNC(charstoreall); // by MouseJstr
+ACMD_FUNC(skillid); // by MouseJstr
+ACMD_FUNC(useskill); // by MouseJstr
+ACMD_FUNC(summon);
+ACMD_FUNC(rain);
+ACMD_FUNC(snow);
+ACMD_FUNC(sakura);
+ACMD_FUNC(clouds);
+ACMD_FUNC(clouds2); // [Valaris]
+ACMD_FUNC(fog);
+ACMD_FUNC(fireworks);
+ACMD_FUNC(leaves);
+ACMD_FUNC(adjgmlvl); // by MouseJstr
+ACMD_FUNC(adjcmdlvl); // by MouseJstr
+ACMD_FUNC(trade); // by MouseJstr
+ACMD_FUNC(send); // by davidsiaw
+ACMD_FUNC(setbattleflag); // by MouseJstr
+ACMD_FUNC(unmute); // [Valaris]
+ACMD_FUNC(clearweather); // Dexity
+ACMD_FUNC(uptime); // by MC Cameri
+ACMD_FUNC(changesex); // by MC Cameri
+ACMD_FUNC(mute); // celest
+ACMD_FUNC(refresh); // by MC Cameri
+ACMD_FUNC(petid); // by MC Cameri
+ACMD_FUNC(identify); // by MC Cameri
+ACMD_FUNC(gmotd); // Added by MC Cameri, created by davidsiaw
+ACMD_FUNC(misceffect); // by MC Cameri
+ACMD_FUNC(mobsearch);
+ACMD_FUNC(cleanmap);
+ACMD_FUNC(npctalk);
+ACMD_FUNC(pettalk);
+ACMD_FUNC(users);
+ACMD_FUNC(autoloot); // Improved version imported from Freya.
+
+#ifndef TXT_ONLY
+ACMD_FUNC(checkmail); // [Valaris]
+ACMD_FUNC(listmail); // [Valaris]
+ACMD_FUNC(listnewmail); // [Valaris]
+ACMD_FUNC(readmail); // [Valaris]
+ACMD_FUNC(sendmail); // [Valaris]
+ACMD_FUNC(sendprioritymail); // [Valaris]
+ACMD_FUNC(deletemail); // [Valaris]
+ACMD_FUNC(refreshonline); // [Valaris]
+#endif /* TXT_ONLY */
+
+ACMD_FUNC(skilltree); // by MouseJstr
+
+ACMD_FUNC(marry); // by MouseJstr
+ACMD_FUNC(divorce); // by MouseJstr
+
+ACMD_FUNC(grind); // by MouseJstr
+ACMD_FUNC(grind2); // by MouseJstr
+
+#ifdef DMALLOC
+ACMD_FUNC(dmstart); // by MouseJstr
+ACMD_FUNC(dmtick); // by MouseJstr
+#endif
+
+ACMD_FUNC(jumptoid); // by Dino9021
+ACMD_FUNC(jumptoid2); // by Dino9021
+ACMD_FUNC(recallid); // by Dino9021
+ACMD_FUNC(recallid2); // by Dino9021
+ACMD_FUNC(kickid); // by Dino9021
+ACMD_FUNC(kickid2); // by Dino9021
+ACMD_FUNC(reviveid); // by Dino9021
+ACMD_FUNC(reviveid2); // by Dino9021
+ACMD_FUNC(killid); // by Dino9021
+ACMD_FUNC(killid2); // by Dino9021
+ACMD_FUNC(charkillableid); // by Dino9021
+ACMD_FUNC(charkillableid2); // by Dino9021
+ACMD_FUNC(sound);
+ACMD_FUNC(undisguiseall);
+ACMD_FUNC(disguiseall);
+ACMD_FUNC(changelook);
+ACMD_FUNC(mobinfo); //by Lupus
+ACMD_FUNC(adopt); // by Veider
+
+ACMD_FUNC(version); // by Ancyker
+
+ACMD_FUNC(mutearea); // by MouseJstr
+ACMD_FUNC(shuffle); // by MouseJstr
+ACMD_FUNC(rates); // by MouseJstr
+
+ACMD_FUNC(iteminfo); // Lupus
+ACMD_FUNC(mapflag); // Lupus
+ACMD_FUNC(me); //added by massdriller, code by lordalfa
+ACMD_FUNC(monsterignore); // [Valaris]
+ACMD_FUNC(fakename); //[Valaris]
+ACMD_FUNC(size); //[Valaris]
+ACMD_FUNC(showexp); //moved from charcommand [Kevin]
+ACMD_FUNC(showzeny);
+ACMD_FUNC(showdelay); //moved from charcommand [Kevin]
+ACMD_FUNC(autotrade);// durf
+ACMD_FUNC(changeleader);// [Skotlex]
+ACMD_FUNC(changegm);// durf
+
+// Duel [LuzZza]
+ACMD_FUNC(invite);
+ACMD_FUNC(duel);
+ACMD_FUNC(leave);
+ACMD_FUNC(accept);
+ACMD_FUNC(reject);
+
+ACMD_FUNC(away); // LuzZza
+ACMD_FUNC(main); // LuzZza
+
+ACMD_FUNC(clone); // [Valaris]
+
+/*==========================================
+ *AtCommandInfo atcommand_info[]構造体の定義
+ *------------------------------------------
+ */
+
+// First char of commands is configured in atcommand_athena.conf. Leave @ in this list for default value.
+// to set default level, read atcommand_athena.conf first please.
+static AtCommandInfo atcommand_info[] = {
+ { AtCommand_Rura, "@rura", 40, atcommand_rura },
+ { AtCommand_Warp, "@warp", 40, atcommand_rura },
+ { AtCommand_Where, "@where", 1, atcommand_where },
+ { AtCommand_JumpTo, "@jumpto", 20, atcommand_jumpto }, // + /shift
+ { AtCommand_JumpTo, "@warpto", 20, atcommand_jumpto },
+ { AtCommand_JumpTo, "@goto", 20, atcommand_jumpto },
+ { AtCommand_Jump, "@jump", 40, atcommand_jump },
+ { AtCommand_Who, "@who", 20, atcommand_who },
+ { AtCommand_Who, "@whois", 20, atcommand_who },
+ { AtCommand_Who, "@w", 20, atcommand_who },
+ { AtCommand_Who2, "@who2", 20, atcommand_who2 },
+ { AtCommand_Who3, "@who3", 20, atcommand_who3 },
+ { AtCommand_WhoMap, "@whomap", 20, atcommand_whomap },
+ { AtCommand_WhoMap2, "@whomap2", 20, atcommand_whomap2 },
+ { AtCommand_WhoMap3, "@whomap3", 20, atcommand_whomap3 },
+ { AtCommand_WhoGM, "@whogm", 20, atcommand_whogm }, // by Yor
+ { AtCommand_Save, "@save", 40, atcommand_save },
+ { AtCommand_Load, "@return", 40, atcommand_load },
+ { AtCommand_Load, "@load", 40, atcommand_load },
+ { AtCommand_Speed, "@speed", 40, atcommand_speed },
+ { AtCommand_Storage, "@storage", 1, atcommand_storage },
+ { AtCommand_GuildStorage, "@gstorage", 50, atcommand_guildstorage },
+ { AtCommand_Option, "@option", 40, atcommand_option },
+ { AtCommand_Hide, "@hide", 40, atcommand_hide }, // + /hide
+ { AtCommand_JobChange, "@jobchange", 40, atcommand_jobchange },
+ { AtCommand_JobChange, "@job", 40, atcommand_jobchange },
+ { AtCommand_Die, "@die", 1, atcommand_die },
+ { AtCommand_Kill, "@kill", 60, atcommand_kill },
+ { AtCommand_Alive, "@alive", 60, atcommand_alive },
+ { AtCommand_Kami, "@kami", 40, atcommand_kami },
+ { AtCommand_KamiB, "@kamib", 40, atcommand_kami },
+ { AtCommand_KamiC, "@kamic", 40, atcommand_kami }, //[LuzZza]
+ { AtCommand_Heal, "@heal", 40, atcommand_heal },
+ { AtCommand_Item, "@item", 60, atcommand_item },
+ { AtCommand_Item2, "@item2", 60, atcommand_item2 },
+ { AtCommand_ItemReset, "@itemreset", 40, atcommand_itemreset },
+ { AtCommand_ItemCheck, "@itemcheck", 60, atcommand_itemcheck },
+ { AtCommand_BaseLevelUp, "@lvup", 60, atcommand_baselevelup },
+ { AtCommand_BaseLevelUp, "@blevel", 60, atcommand_baselevelup },
+ { AtCommand_BaseLevelUp, "@baselvlup", 60, atcommand_baselevelup },
+ { AtCommand_JobLevelUp, "@jlevel", 60, atcommand_joblevelup },
+ { AtCommand_JobLevelUp, "@joblvup", 60, atcommand_joblevelup },
+ { AtCommand_JobLevelUp, "@joblvlup", 60, atcommand_joblevelup },
+ { AtCommand_H, "@h", 20, atcommand_help },
+ { AtCommand_Help, "@help", 20, atcommand_help },
+ { AtCommand_H2, "@h2", 20, atcommand_help2 },
+ { AtCommand_Help2, "@help2", 20, atcommand_help2 },
+ { AtCommand_GM, "@gm", 100,atcommand_gm },
+ { AtCommand_PvPOff, "@pvpoff", 40, atcommand_pvpoff },
+ { AtCommand_PvPOn, "@pvpon", 40, atcommand_pvpon },
+ { AtCommand_GvGOff, "@gvgoff", 40, atcommand_gvgoff },
+ { AtCommand_GvGOff, "@gpvpoff", 40, atcommand_gvgoff },
+ { AtCommand_GvGOn, "@gvgon", 40, atcommand_gvgon },
+ { AtCommand_GvGOn, "@gpvpon", 40, atcommand_gvgon },
+ { AtCommand_Model, "@model", 20, atcommand_model },
+ { AtCommand_Go, "@go", 10, atcommand_go },
+ { AtCommand_Spawn, "@monster", 50, atcommand_monster },
+ { AtCommand_Spawn, "@spawn", 50, atcommand_monster },
+ { AtCommand_MonsterSmall, "@monstersmall", 50, atcommand_monstersmall },
+ { AtCommand_MonsterBig, "@monsterbig", 50, atcommand_monsterbig },
+ { AtCommand_KillMonster, "@killmonster", 60, atcommand_killmonster },
+ { AtCommand_KillMonster2, "@killmonster2", 40, atcommand_killmonster2 },
+ { AtCommand_Refine, "@refine", 60, atcommand_refine },
+ { AtCommand_Produce, "@produce", 60, atcommand_produce },
+ { AtCommand_Memo, "@memo", 40, atcommand_memo },
+ { AtCommand_GAT, "@gat", 99, atcommand_gat }, // debug function
+ { AtCommand_Packet, "@packet", 99, atcommand_packet }, // debug function
+ { AtCommand_Packet, "@packetmode", 99, atcommand_packet }, // debug function
+ { AtCommand_WaterLevel, "@waterlevel", 99, atcommand_waterlevel }, // debug function
+ { AtCommand_StatusPoint, "@stpoint", 60, atcommand_statuspoint },
+ { AtCommand_SkillPoint, "@skpoint", 60, atcommand_skillpoint },
+ { AtCommand_Zeny, "@zeny", 60, atcommand_zeny },
+ { AtCommand_Strength, "@str", 60, atcommand_param },
+ { AtCommand_Agility, "@agi", 60, atcommand_param },
+ { AtCommand_Vitality, "@vit", 60, atcommand_param },
+ { AtCommand_Intelligence, "@int", 60, atcommand_param },
+ { AtCommand_Dexterity, "@dex", 60, atcommand_param },
+ { AtCommand_Luck, "@luk", 60, atcommand_param },
+ { AtCommand_GuildLevelUp, "@guildlvup", 60, atcommand_guildlevelup },
+ { AtCommand_GuildLevelUp, "@guildlvlup", 60, atcommand_guildlevelup },
+ { AtCommand_MakeEgg, "@makeegg", 60, atcommand_makeegg },
+ { AtCommand_Hatch, "@hatch", 60, atcommand_hatch },
+ { AtCommand_PetFriendly, "@petfriendly", 40, atcommand_petfriendly },
+ { AtCommand_PetHungry, "@pethungry", 40, atcommand_pethungry },
+ { AtCommand_PetRename, "@petrename", 1, atcommand_petrename },
+ { AtCommand_Recall, "@recall", 60, atcommand_recall }, // + /recall
+ { AtCommand_Revive, "@revive", 60, atcommand_revive },
+ { AtCommand_Night, "@night", 80, atcommand_night },
+ { AtCommand_Day, "@day", 80, atcommand_day },
+ { AtCommand_Doom, "@doom", 80, atcommand_doom },
+ { AtCommand_DoomMap, "@doommap", 80, atcommand_doommap },
+ { AtCommand_Raise, "@raise", 80, atcommand_raise },
+ { AtCommand_RaiseMap, "@raisemap", 80, atcommand_raisemap },
+ { AtCommand_Kick, "@kick", 20, atcommand_kick }, // + right click menu for GM "(name) force to quit"
+ { AtCommand_KickAll, "@kickall", 99, atcommand_kickall },
+ { AtCommand_AllSkill, "@allskill", 60, atcommand_allskill },
+ { AtCommand_AllSkill, "@allskills", 60, atcommand_allskill },
+ { AtCommand_AllSkill, "@skillall", 60, atcommand_allskill },
+ { AtCommand_AllSkill, "@skillsall", 60, atcommand_allskill },
+ { AtCommand_QuestSkill, "@questskill", 40, atcommand_questskill },
+ { AtCommand_LostSkill, "@lostskill", 40, atcommand_lostskill },
+ { AtCommand_SpiritBall, "@spiritball", 40, atcommand_spiritball },
+ { AtCommand_Party, "@party", 1, atcommand_party },
+ { AtCommand_Guild, "@guild", 50, atcommand_guild },
+ { AtCommand_AgitStart, "@agitstart", 60, atcommand_agitstart },
+ { AtCommand_AgitEnd, "@agitend", 60, atcommand_agitend },
+ { AtCommand_MapExit, "@mapexit", 99, atcommand_mapexit },
+ { AtCommand_IDSearch, "@idsearch", 60, atcommand_idsearch },
+ { AtCommand_MapMove, "@mapmove", 40, atcommand_rura }, // /mm command
+ { AtCommand_Broadcast, "@broadcast", 40, atcommand_broadcast }, // /b and /nb command
+ { AtCommand_LocalBroadcast, "@localbroadcast", 40, atcommand_localbroadcast }, // /lb and /nlb command
+ { AtCommand_RecallAll, "@recallall", 80, atcommand_recallall },
+ { AtCommand_ReloadItemDB, "@reloaditemdb", 99, atcommand_reloaditemdb }, // admin command
+ { AtCommand_ReloadMobDB, "@reloadmobdb", 99, atcommand_reloadmobdb }, // admin command
+ { AtCommand_ReloadSkillDB, "@reloadskilldb", 99, atcommand_reloadskilldb }, // admin command
+ { AtCommand_ReloadScript, "@reloadscript", 99, atcommand_reloadscript }, // admin command
+ { AtCommand_ReloadGMDB, "@reloadgmdb", 99, atcommand_reloadgmdb }, // admin command
+ { AtCommand_ReloadAtcommand, "@reloadatcommand", 99, atcommand_reloadatcommand },
+ { AtCommand_ReloadBattleConf, "@reloadbattleconf", 99, atcommand_reloadbattleconf },
+ { AtCommand_ReloadStatusDB, "@reloadstatusdb", 99, atcommand_reloadstatusdb },
+ { AtCommand_ReloadPcDB, "@reloadpcdb", 99, atcommand_reloadpcdb },
+ { AtCommand_ReloadMOTD, "@reloadmotd", 99, atcommand_reloadmotd }, // [Valaris]
+ { AtCommand_MapInfo, "@mapinfo", 99, atcommand_mapinfo },
+ { AtCommand_Dye, "@dye", 40, atcommand_dye }, // by fritz
+ { AtCommand_Dye, "@ccolor", 40, atcommand_dye }, // by fritz
+ { AtCommand_Hstyle, "@hairstyle", 40, atcommand_hair_style }, // by fritz
+ { AtCommand_Hstyle, "@hstyle", 40, atcommand_hair_style }, // by fritz
+ { AtCommand_Hcolor, "@haircolor", 40, atcommand_hair_color }, // by fritz
+ { AtCommand_Hcolor, "@hcolor", 40, atcommand_hair_color }, // by fritz
+ { AtCommand_StatAll, "@statall", 60, atcommand_stat_all }, // by fritz
+ { AtCommand_StatAll, "@statsall", 60, atcommand_stat_all },
+ { AtCommand_StatAll, "@allstats", 60, atcommand_stat_all }, // by fritz
+ { AtCommand_StatAll, "@allstat", 60, atcommand_stat_all }, // by fritz
+ { AtCommand_CharBlock, "@block", 60, atcommand_char_block }, // by Yor
+ { AtCommand_CharBlock, "@charblock", 60, atcommand_char_block }, // by Yor
+ { AtCommand_CharBan, "@ban", 60, atcommand_char_ban }, // by Yor
+ { AtCommand_CharBan, "@banish", 60, atcommand_char_ban }, // by Yor
+ { AtCommand_CharBan, "@charban", 60, atcommand_char_ban }, // by Yor
+ { AtCommand_CharBan, "@charbanish", 60, atcommand_char_ban }, // by Yor
+ { AtCommand_CharUnBlock, "@unblock", 60, atcommand_char_unblock }, // by Yor
+ { AtCommand_CharUnBlock, "@charunblock", 60, atcommand_char_unblock }, // by Yor
+ { AtCommand_CharUnBan, "@unban", 60, atcommand_char_unban }, // by Yor
+ { AtCommand_CharUnBan, "@unbanish", 60, atcommand_char_unban }, // by Yor
+ { AtCommand_CharUnBan, "@charunban", 60, atcommand_char_unban }, // by Yor
+ { AtCommand_CharUnBan, "@charunbanish", 60, atcommand_char_unban }, // by Yor
+ { AtCommand_MountPeco, "@mountpeco", 20, atcommand_mount_peco }, // by Valaris
+ { AtCommand_CharMountPeco, "@charmountpeco", 50, atcommand_char_mount_peco }, // by Yor
+ { AtCommand_GuildSpy, "@guildspy", 60, atcommand_guildspy }, // [Syrus22]
+ { AtCommand_PartySpy, "@partyspy", 60, atcommand_partyspy }, // [Syrus22]
+ { AtCommand_RepairAll, "@repairall", 60, atcommand_repairall }, // [Valaris]
+ { AtCommand_GuildRecall, "@guildrecall", 60, atcommand_guildrecall }, // by Yor
+ { AtCommand_PartyRecall, "@partyrecall", 60, atcommand_partyrecall }, // by Yor
+ { AtCommand_Nuke, "@nuke", 60, atcommand_nuke }, // [Valaris]
+ { AtCommand_Shownpc, "@shownpc", 80, atcommand_shownpc }, // []
+ { AtCommand_Hidenpc, "@hidenpc", 80, atcommand_hidenpc }, // []
+ { AtCommand_Loadnpc, "@loadnpc", 80, atcommand_loadnpc }, // []
+ { AtCommand_Unloadnpc, "@unloadnpc", 80, atcommand_unloadnpc }, // []
+ { AtCommand_ServerTime, "@time", 1, atcommand_servertime }, // by Yor
+ { AtCommand_ServerTime, "@date", 1, atcommand_servertime }, // by Yor
+ { AtCommand_ServerTime, "@server_date", 1, atcommand_servertime }, // by Yor
+ { AtCommand_ServerTime, "@serverdate", 1, atcommand_servertime }, // by Yor
+ { AtCommand_ServerTime, "@server_time", 1, atcommand_servertime }, // by Yor
+ { AtCommand_ServerTime, "@servertime", 1, atcommand_servertime }, // by Yor
+ { AtCommand_CharDelItem, "@chardelitem", 60, atcommand_chardelitem }, // by Yor
+ { AtCommand_Jail, "@jail", 60, atcommand_jail }, // by Yor
+ { AtCommand_UnJail, "@unjail", 60, atcommand_unjail }, // by Yor
+ { AtCommand_UnJail, "@discharge", 60, atcommand_unjail }, // by Yor
+ { AtCommand_Disguise, "@disguise", 20, atcommand_disguise }, // [Valaris]
+ { AtCommand_UnDisguise, "@undisguise", 20, atcommand_undisguise }, // by Yor
+ { AtCommand_CharDisguise, "@chardisguise", 60, atcommand_chardisguise }, // Kalaspuff
+ { AtCommand_CharUnDisguise, "@charundisguise", 60, atcommand_charundisguise }, // Kalaspuff
+ { AtCommand_EMail, "@email", 1, atcommand_email }, // by Yor
+ { AtCommand_Effect, "@effect", 40, atcommand_effect }, // by Apple
+ { AtCommand_Char_Cart_List, "@charcartlist", 40, atcommand_character_cart_list }, // by Yor
+ { AtCommand_Follow, "@follow", 20, atcommand_follow }, // by MouseJstr
+ { AtCommand_AddWarp, "@addwarp", 60, atcommand_addwarp }, // by MouseJstr
+ { AtCommand_SkillOn, "@skillon", 80, atcommand_skillon }, // by MouseJstr
+ { AtCommand_SkillOff, "@skilloff", 80, atcommand_skilloff }, // by MouseJstr
+ { AtCommand_Killer, "@killer", 60, atcommand_killer }, // by MouseJstr
+ { AtCommand_NpcMove, "@npcmove", 20, atcommand_npcmove }, // by MouseJstr
+ { AtCommand_Killable, "@killable", 40, atcommand_killable }, // by MouseJstr
+ { AtCommand_CharKillable, "@charkillable", 40, atcommand_charkillable }, // by MouseJstr
+ { AtCommand_Dropall, "@dropall", 40, atcommand_dropall }, // MouseJstr
+ { AtCommand_Chardropall, "@chardropall", 40, atcommand_chardropall }, // MouseJstr
+ { AtCommand_Storeall, "@storeall", 40, atcommand_storeall }, // MouseJstr
+ { AtCommand_Charstoreall, "@charstoreall", 40, atcommand_charstoreall }, // MouseJstr
+ { AtCommand_Skillid, "@skillid", 40, atcommand_skillid }, // MouseJstr
+ { AtCommand_Useskill, "@useskill", 40, atcommand_useskill }, // MouseJstr
+ { AtCommand_Rain, "@rain", 99, atcommand_rain },
+ { AtCommand_Snow, "@snow", 99, atcommand_snow },
+ { AtCommand_Sakura, "@sakura", 99, atcommand_sakura },
+ { AtCommand_Clouds, "@clouds", 99, atcommand_clouds },
+ { AtCommand_Clouds2, "@clouds2", 99, atcommand_clouds2 },
+ { AtCommand_Fog, "@fog", 99, atcommand_fog },
+ { AtCommand_Fireworks, "@fireworks", 99, atcommand_fireworks },
+ { AtCommand_Leaves, "@leaves", 99, atcommand_leaves },
+ { AtCommand_Summon, "@summon", 60, atcommand_summon },
+ { AtCommand_AdjGmLvl, "@adjgmlvl", 99, atcommand_adjgmlvl },
+ { AtCommand_AdjCmdLvl, "@adjcmdlvl", 99, atcommand_adjcmdlvl },
+ { AtCommand_Trade, "@trade", 60, atcommand_trade },
+ { AtCommand_Send, "@send", 60, atcommand_send },
+ { AtCommand_SetBattleFlag, "@setbattleflag", 99, atcommand_setbattleflag },
+ { AtCommand_UnMute, "@unmute", 60, atcommand_unmute }, // [Valaris]
+ { AtCommand_Clearweather, "@clearweather", 99, atcommand_clearweather }, // Dexity
+ { AtCommand_UpTime, "@uptime", 1, atcommand_uptime }, // by MC Cameri
+// { AtCommand_ChangeSex, "@changesex", 1, atcommand_changesex }, // by MC Cameri <- do we still need this? [Foruken]
+ { AtCommand_Mute, "@mute", 99, atcommand_mute }, // [celest]
+ { AtCommand_Mute, "@red", 99, atcommand_mute }, // [celest]
+ { AtCommand_WhoZeny, "@whozeny", 20, atcommand_whozeny }, // [Valaris]
+ { AtCommand_HappyHappyJoyJoy, "@happyhappyjoyjoy", 40, atcommand_happyhappyjoyjoy }, // [Valaris]
+ { AtCommand_Refresh, "@refresh", 1, atcommand_refresh }, // by MC Cameri
+ { AtCommand_PetId, "@petid", 40, atcommand_petid }, // by MC Cameri
+ { AtCommand_Identify, "@identify", 40, atcommand_identify }, // by MC Cameri
+ { AtCommand_Gmotd, "@gmotd", 20, atcommand_gmotd }, // Added by MC Cameri, created by davidsiaw
+ { AtCommand_MiscEffect, "@misceffect", 50, atcommand_misceffect }, // by MC Cameri
+ { AtCommand_MobSearch, "@mobsearch", 10, atcommand_mobsearch },
+ { AtCommand_CleanMap, "@cleanmap", 40, atcommand_cleanmap },
+ { AtCommand_NpcTalk, "@npctalk", 20, atcommand_npctalk },
+ { AtCommand_PetTalk, "@pettalk", 10, atcommand_pettalk },
+ { AtCommand_Users, "@users", 40, atcommand_users },
+ { AtCommand_ResetState, "/reset", 40, NULL },
+
+#ifndef TXT_ONLY // sql-only commands
+ { AtCommand_CheckMail, "@checkmail", 1, atcommand_listmail }, // [Valaris]
+ { AtCommand_ListMail, "@listmail", 1, atcommand_listmail }, // [Valaris]
+ { AtCommand_ListNewMail, "@listnewmail", 1, atcommand_listmail }, // [Valaris]
+ { AtCommand_ReadMail, "@readmail", 1, atcommand_readmail }, // [Valaris]
+ { AtCommand_DeleteMail, "@deletemail", 1, atcommand_readmail }, // [Valaris]
+ { AtCommand_SendMail, "@sendmail", 1, atcommand_sendmail }, // [Valaris]
+ { AtCommand_SendPriorityMail, "@sendprioritymail", 80, atcommand_sendmail }, // [Valaris]
+ { AtCommand_RefreshOnline, "@refreshonline", 99, atcommand_refreshonline }, // [Valaris]
+
+#endif /* TXT_ONLY */
+ { AtCommand_SkillTree, "@skilltree", 40, atcommand_skilltree }, // [MouseJstr]
+ { AtCommand_Marry, "@marry", 40, atcommand_marry }, // [MouseJstr]
+ { AtCommand_Divorce, "@divorce", 40, atcommand_divorce }, // [MouseJstr]
+ { AtCommand_Grind, "@grind", 99, atcommand_grind }, // [MouseJstr]
+ { AtCommand_Grind2, "@grind2", 99, atcommand_grind2 }, // [MouseJstr]
+
+#ifdef DMALLOC
+ { AtCommand_DMStart, "@dmstart", 99, atcommand_dmstart }, // [MouseJstr]
+ { AtCommand_DMTick, "@dmtick", 99, atcommand_dmtick }, // [MouseJstr]
+#endif
+
+ { AtCommand_JumpToId, "@jumptoid", 20, atcommand_jumptoid }, // [Dino9021]
+ { AtCommand_JumpToId, "@warptoid", 20, atcommand_jumptoid }, // [Dino9021]
+ { AtCommand_JumpToId, "@gotoid", 20, atcommand_jumptoid }, // [Dino9021]
+ { AtCommand_JumpToId2, "@jumptoid2", 20, atcommand_jumptoid2 }, // [Dino9021]
+ { AtCommand_JumpToId2, "@warptoid2", 20, atcommand_jumptoid2 }, // [Dino9021]
+ { AtCommand_JumpToId2, "@gotoid2", 20, atcommand_jumptoid2 }, // [Dino9021]
+ { AtCommand_RecallId, "@recallid", 60, atcommand_recallid }, // [Dino9021]
+ { AtCommand_RecallId2, "@recallid2", 60, atcommand_recallid2 }, // [Dino9021]
+ { AtCommand_KickId, "@kickid", 99, atcommand_kickid }, // [Dino9021]
+ { AtCommand_KickId2, "@kickid2", 99, atcommand_kickid2 }, // [Dino9021]
+ { AtCommand_ReviveId, "@reviveid", 60, atcommand_reviveid }, // [Dino9021]
+ { AtCommand_ReviveId2, "@reviveid2", 60, atcommand_reviveid2 }, // [Dino9021]
+ { AtCommand_KillId, "@killid", 60, atcommand_killid }, // [Dino9021]
+ { AtCommand_KillId2, "@killid2", 60, atcommand_killid2 }, // [Dino9021]
+ { AtCommand_CharKillableId, "@charkillableid", 40, atcommand_charkillableid }, // [Dino9021]
+ { AtCommand_CharKillableId2, "@charkillableid2", 40, atcommand_charkillableid2 }, // [Dino9021]
+ { AtCommand_Sound, "@sound", 40, atcommand_sound },
+ { AtCommand_UndisguiseAll, "@undisguiseall", 99, atcommand_undisguiseall },
+ { AtCommand_DisguiseAll, "@disguiseall", 99, atcommand_disguiseall },
+ { AtCommand_ChangeLook, "@changelook", 99, atcommand_changelook },
+ { AtCommand_AutoLoot, "@autoloot", 10, atcommand_autoloot }, // Upa-Kun
+ { AtCommand_MobInfo, "@mobinfo", 1, atcommand_mobinfo }, // [Lupus]
+ { AtCommand_MobInfo, "@monsterinfo", 1, atcommand_mobinfo }, // [Lupus]
+ { AtCommand_MobInfo, "@mi", 1, atcommand_mobinfo }, // [Lupus]
+ { AtCommand_Adopt, "@adopt", 40, atcommand_adopt }, // [Veider]
+ { AtCommand_Version, "@version", 1, atcommand_version },
+
+ { AtCommand_MuteArea, "@mutearea", 99, atcommand_mutearea }, // MouseJstr
+ { AtCommand_MuteArea, "@stfu", 99, atcommand_mutearea }, // MouseJstr
+ { AtCommand_Shuffle, "@shuffle", 40, atcommand_shuffle }, // MouseJstr
+ { AtCommand_Rates, "@rates", 1, atcommand_rates }, // MouseJstr
+
+ { AtCommand_ItemInfo, "@iteminfo", 1, atcommand_iteminfo }, // [Lupus]
+ { AtCommand_ItemInfo, "@ii", 1, atcommand_iteminfo }, // [Lupus]
+ { AtCommand_MapFlag, "@mapflag", 99, atcommand_mapflag }, // [Lupus]
+
+ { AtCommand_Me, "@me", 20, atcommand_me }, //added by massdriller, code by lordalfa
+ { AtCommand_MonsterIgnore, "@monsterignore", 99, atcommand_monsterignore }, // [Valaris]
+ { AtCommand_FakeName, "@fakename", 20, atcommand_fakename }, // [Valaris]
+ { AtCommand_Size, "@size", 20, atcommand_size },
+ { AtCommand_ShowExp, "@showexp", 10, atcommand_showexp},
+ { AtCommand_ShowZeny, "@showzeny", 10, atcommand_showzeny},
+ { AtCommand_ShowDelay, "@showdelay", 1, atcommand_showdelay},
+ { AtCommand_AutoTrade, "@autotrade", 10, atcommand_autotrade }, // durf
+ { AtCommand_AutoTrade, "@at", 10, atcommand_autotrade },
+ { AtCommand_ChangeGM, "@changegm", 10, atcommand_changegm }, // durf
+ { AtCommand_ChangeLeader, "@changeleader", 10, atcommand_changeleader }, // durf
+ { AtCommand_Invite, "@invite", 1, atcommand_invite }, // By LuzZza
+ { AtCommand_Duel, "@duel", 1, atcommand_duel }, // By LuzZza
+ { AtCommand_Leave, "@leave", 1, atcommand_leave }, // By LuzZza
+ { AtCommand_Accept, "@accept", 1, atcommand_accept }, // By LuzZza
+ { AtCommand_Reject, "@reject", 1, atcommand_reject }, // By LuzZza
+ { AtCommand_Away, "@away", 1, atcommand_away }, // [LuzZza]
+ { AtCommand_Away, "@aw", 1, atcommand_away }, // [LuzZza]
+ { AtCommand_Main, "@main", 1, atcommand_main }, // [LuzZza]
+ { AtCommand_Clone, "@clone", 50, atcommand_clone },
+ { AtCommand_Clone, "@slaveclone", 50, atcommand_clone },
+ { AtCommand_Clone, "@evilclone", 50, atcommand_clone }, // [Valaris]
+
+// add new commands before this line
+ { AtCommand_Unknown, NULL, 1, NULL }
+};
+
+/*=========================================
+ * Generic variables
+ *-----------------------------------------
+ */
+char atcmd_output[200];
+char atcmd_player_name[100];
+char atcmd_temp[100];
+
+/*==========================================
+ * estr_lower (replace strlwr, non ANSI function that doesn't exist in all C compilator)
+ *------------------------------------------
+ */
+char *estr_lower(char *str)
+{
+ int i;
+
+ for (i=0; str[i]; i++)
+ if ((str[i] >= 65) && (str[i] <= 90))
+ str[i] += 32;
+ return str;
+}
+
+// compare function for sorting high to lowest
+int hightolow_compare (const void * a, const void * b)
+{
+ return ( *(int*)b - *(int*)a );
+}
+
+// compare function for sorting lowest to highest
+int lowtohigh_compare (const void * a, const void * b)
+{
+ return ( *(int*)a - *(int*)b );
+}
+
+//-----------------------------------------------------------
+// Return the message string of the specified number by [Yor]
+//-----------------------------------------------------------
+char * msg_txt(int msg_number) {
+ if (msg_number >= 0 && msg_number < MAX_MSG &&
+ msg_table[msg_number] != NULL && msg_table[msg_number][0] != '\0')
+ return msg_table[msg_number];
+
+ return "??";
+}
+
+//-----------------------------------------------------------
+// Returns Players title (from msg_athena.conf) [Lupus]
+//-----------------------------------------------------------
+char * player_title_txt(int level) {
+ if (level < battle_config.title_lvl1)
+ return ""; //w/o any titles
+
+ if (level >= battle_config.title_lvl8)
+ sprintf(atcmd_temp, msg_table[332], level);
+ else
+ if (level >= battle_config.title_lvl7)
+ sprintf(atcmd_temp, msg_table[331], level);
+ else
+ if (level >= battle_config.title_lvl6)
+ sprintf(atcmd_temp, msg_table[330], level);
+ else
+ if (level >= battle_config.title_lvl5)
+ sprintf(atcmd_temp, msg_table[329], level);
+ else
+ if (level >= battle_config.title_lvl4)
+ sprintf(atcmd_temp, msg_table[328], level);
+ else
+ if (level >= battle_config.title_lvl3)
+ sprintf(atcmd_temp, msg_table[327], level);
+ else
+ if (level >= battle_config.title_lvl2)
+ sprintf(atcmd_temp, msg_table[326], level);
+ else
+ sprintf(atcmd_temp, msg_table[325], level); //lvl1
+ return atcmd_temp;
+}
+
+//------------------------------------------------------------
+// E-mail check: return 0 (not correct) or 1 (valid). by [Yor]
+//------------------------------------------------------------
+int e_mail_check(char *email) {
+ char ch;
+ char* last_arobas;
+
+ // athena limits
+ if (strlen(email) < 3 || strlen(email) > 39)
+ return 0;
+
+ // part of RFC limits (official reference of e-mail description)
+ if (strchr(email, '@') == NULL || email[strlen(email)-1] == '@')
+ return 0;
+
+ if (email[strlen(email)-1] == '.')
+ return 0;
+
+ last_arobas = strrchr(email, '@');
+
+ if (strstr(last_arobas, "@.") != NULL ||
+ strstr(last_arobas, "..") != NULL)
+ return 0;
+
+ for(ch = 1; ch < 32; ch++) {
+ if (strchr(last_arobas, ch) != NULL) {
+ return 0;
+ break;
+ }
+ }
+
+ if (strchr(last_arobas, ' ') != NULL ||
+ strchr(last_arobas, ';') != NULL)
+ return 0;
+
+ // all correct
+ return 1;
+}
+
+/*==========================================
+ * get_atcommand_level @コマンドの必要レベルを取得
+ *------------------------------------------
+ */
+int get_atcommand_level(const AtCommandType type) {
+ int i;
+
+ for (i = 0; atcommand_info[i].type != AtCommand_None; i++)
+ if (atcommand_info[i].type == type)
+ return atcommand_info[i].level;
+
+ return 100; // 100: command can not be used
+}
+
+/*==========================================
+ *is_atcommand @コマンドに存在するかどうか確認する
+ *------------------------------------------
+ */
+AtCommandType
+is_atcommand(const int fd, struct map_session_data* sd, const char* message, int gmlvl) {
+ const char* str = message;
+ int s_flag = 0;
+ AtCommandInfo info;
+ AtCommandType type;
+
+ nullpo_retr(AtCommand_None, sd);
+
+ if (!battle_config.allow_atcommand_when_mute &&
+ sd->sc_count && sd->sc_data[SC_NOCHAT].timer != -1) {
+ return AtCommand_Unknown;
+ }
+
+ if (!message || !*message)
+ return AtCommand_None;
+
+ memset(&info, 0, sizeof(info));
+ str += strlen(sd->status.name);
+ while (*str && (isspace(*str) || (s_flag == 0 && *str == ':'))) {
+ if (*str == ':')
+ s_flag = 1;
+ str++;
+ }
+ if (!*str)
+ return AtCommand_None;
+
+ type = atcommand(sd, gmlvl > 0 ? gmlvl : pc_isGM(sd), str, &info);
+ if (type != AtCommand_None) {
+ char command[100];
+ const char* p = str;
+ memset(command, '\0', sizeof(command));
+ memset(atcmd_output, '\0', sizeof(atcmd_output));
+ while (*p && !isspace(*p))
+ p++;
+ if (p - str >= sizeof(command)) // too long
+ return AtCommand_Unknown;
+ strncpy(command, str, p - str);
+ while (isspace(*p))
+ p++;
+
+ if (type == AtCommand_Unknown || info.proc == NULL) {
+ sprintf(atcmd_output, msg_table[153], command); // %s is Unknown Command.
+ clif_displaymessage(fd, atcmd_output);
+ } else {
+ if (info.proc(fd, sd, command, p) != 0) {
+ // Command can not be executed
+ sprintf(atcmd_output, msg_table[154], command); // %s failed.
+ clif_displaymessage(fd, atcmd_output);
+ }
+ }
+
+ return info.type;
+ }
+
+ return AtCommand_None;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+AtCommandType atcommand(struct map_session_data* sd, const int level, const char* message, struct AtCommandInfo* info) {
+ char* p = (char *)message; // it's 'char' and not 'const char' to have possibility to modify the first character if necessary
+
+ if (!info)
+ return AtCommand_None;
+ if (battle_config.atc_gmonly != 0 && !level) // level = pc_isGM(sd)
+ return AtCommand_None;
+ if (!p || !*p) {
+ ShowError("at command message is empty\n");
+ return AtCommand_None;
+ }
+
+ if (*p == command_symbol) { // check first char.
+ char command[101];
+ int i = 0;
+ memset(info, 0, sizeof(AtCommandInfo));
+ sscanf(p, "%100s", command);
+ command[sizeof(command)-1] = '\0';
+
+ while (atcommand_info[i].type != AtCommand_Unknown) {
+ if (strcmpi(command+1, atcommand_info[i].command+1) == 0 && level >= atcommand_info[i].level) {
+ p[0] = atcommand_info[i].command[0]; // set correct first symbol for after.
+ break;
+ }
+ i++;
+ }
+
+ if (atcommand_info[i].type == AtCommand_Unknown) {
+ // doesn't return Unknown if player is normal player (display the text, not display: unknown command)
+ if (level == 0)
+ return AtCommand_None;
+ else
+ return AtCommand_Unknown;
+ } else if((log_config.gm) && (atcommand_info[i].level >= log_config.gm)) {
+ log_atcommand(sd, message);
+ }
+ memcpy(info, &atcommand_info[i], sizeof atcommand_info[i]);
+ } else {
+ return AtCommand_None;
+ }
+
+ return info->type;
+}
+
+/*==========================================
+ * Read Message Data
+ *------------------------------------------
+ */
+int msg_config_read(const char *cfgName) {
+ int msg_number;
+ char line[1024], w1[1024], w2[1024];
+ FILE *fp;
+ static int called = 1;
+
+ if ((fp = fopen(cfgName, "r")) == NULL) {
+ ShowError("Messages file not found: %s\n", cfgName);
+ return 1;
+ }
+
+ if ((--called) == 0)
+ memset(&msg_table[0], 0, sizeof(msg_table[0]) * MAX_MSG);
+ while(fgets(line, sizeof(line)-1, fp)) {
+ if (line[0] == '/' && line[1] == '/')
+ continue;
+ if (sscanf(line, "%[^:]: %[^\r\n]", w1, w2) == 2) {
+ if (strcmpi(w1, "import") == 0) {
+ msg_config_read(w2);
+ } else {
+ msg_number = atoi(w1);
+ if (msg_number >= 0 && msg_number < MAX_MSG) {
+ if (msg_table[msg_number] != NULL)
+ aFree(msg_table[msg_number]);
+ msg_table[msg_number] = (char *)aCalloc(strlen(w2) + 1, sizeof (char));
+ strcpy(msg_table[msg_number],w2);
+ // printf("message #%d: '%s'.\n", msg_number, msg_table[msg_number]);
+ }
+ }
+ }
+ }
+ fclose(fp);
+
+ return 0;
+}
+
+/*==========================================
+ * Cleanup Message Data
+ *------------------------------------------
+ */
+void do_final_msg (void) {
+ int i;
+ for (i = 0; i < MAX_MSG; i++)
+ aFree(msg_table[i]);
+ return;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+static AtCommandInfo* get_atcommandinfo_byname(const char* name) {
+ int i;
+
+ for (i = 0; atcommand_info[i].type != AtCommand_Unknown; i++)
+ if (strcmpi(atcommand_info[i].command + 1, name) == 0)
+ return &atcommand_info[i];
+
+ return NULL;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int atcommand_config_read(const char *cfgName) {
+ char line[1024], w1[1024], w2[1024];
+ AtCommandInfo* p;
+ FILE* fp;
+
+ if ((fp = fopen(cfgName, "r")) == NULL) {
+ ShowError("At commands configuration file not found: %s\n", cfgName);
+ return 1;
+ }
+
+ while (fgets(line, sizeof(line)-1, fp)) {
+ if (line[0] == '/' && line[1] == '/')
+ continue;
+
+ if (sscanf(line, "%1023[^:]:%1023s", w1, w2) != 2)
+ continue;
+ p = get_atcommandinfo_byname(w1);
+ if (p != NULL) {
+ p->level = atoi(w2);
+ if (p->level > 100)
+ p->level = 100;
+ else if (p->level < 0)
+ p->level = 0;
+ }
+
+ if (strcmpi(w1, "import") == 0)
+ atcommand_config_read(w2);
+ else if (strcmpi(w1, "command_symbol") == 0 && w2[0] > 31 &&
+ w2[0] != '/' && // symbol of standard ragnarok GM commands
+ w2[0] != '%' && // symbol of party chat speaking
+ w2[0] != '$' && // symbol of guild chat
+ w2[0] != '#') // symbol of charcommand
+ command_symbol = w2[0];
+ }
+ fclose(fp);
+
+ return 0;
+}
+
+/*==========================================
+ * Duel organizing functions [LuzZza]
+ *------------------------------------------
+ */
+void duel_msg_foreach_sameduel_wos(
+ const unsigned int did, struct map_session_data* sd, char *output)
+{
+ int i;
+ struct map_session_data* msg_sd;
+
+ for(i=0; i<fd_max; i++)
+ if(session[i] && (msg_sd = (struct map_session_data *) session[i]->session_data)
+ && msg_sd->state.auth && msg_sd->duel_group == did && msg_sd != sd)
+
+ clif_disp_onlyself(msg_sd, output, strlen(output));
+
+ return;
+}
+
+void duel_savetime(struct map_session_data* sd) {
+
+ time_t timer;
+ struct tm *t;
+
+ time(&timer);
+ t = localtime(&timer);
+
+ pc_setglobalreg(sd, "PC_LAST_DUEL_TIME",
+ t->tm_mday*24*60 + t->tm_hour*60 + t->tm_min);
+ return;
+}
+
+int duel_checktime(struct map_session_data* sd) {
+
+ int diff;
+ time_t timer;
+ struct tm *t;
+
+ time(&timer);
+ t = localtime(&timer);
+
+ diff = t->tm_mday*24*60 + t->tm_hour*60 + t->tm_min -
+ pc_readglobalreg(sd, "PC_LAST_DUEL_TIME");
+
+ return !(diff >= 0 && diff < battle_config.duel_time_interval);
+}
+
+int duel_showinfo(
+ const unsigned int did, struct map_session_data* sd)
+{
+ int i, p=0;
+ char output[256];
+ struct map_session_data* msg_sd;
+
+ if(duel_list[did].max_players_limit > 0)
+ sprintf(output, msg_txt(370), //" -- Duels: %d/%d, Members: %d/%d, Max players: %d --"
+ did, duel_count,
+ duel_list[did].members_count,
+ duel_list[did].members_count + duel_list[did].invites_count,
+ duel_list[did].max_players_limit);
+ else
+ sprintf(output, msg_txt(371), //" -- Duels: %d/%d, Members: %d/%d --"
+ did, duel_count,
+ duel_list[did].members_count,
+ duel_list[did].members_count + duel_list[did].invites_count);
+
+ clif_disp_onlyself(sd, output, strlen(output));
+
+ for(i=0; i<fd_max; i++)
+ if (session[i] && (msg_sd = (struct map_session_data *) session[i]->session_data)
+ && msg_sd->state.auth && msg_sd->duel_group == did) {
+
+ sprintf(output, " %d. %s", ++p, (unsigned char *) msg_sd->status.name);
+ clif_disp_onlyself(sd, output, strlen(output));
+ }
+
+ return 0;
+}
+
+int duel_create(
+ struct map_session_data* sd, const unsigned int maxpl)
+{
+ int i=1;
+ char output[256];
+
+ while(duel_list[i].members_count > 0 && i < MAX_DUEL) i++;
+ if(i == MAX_DUEL) return 0;
+
+ duel_count++;
+ sd->duel_group = i;
+ duel_list[i].members_count++;
+ duel_list[i].invites_count = 0;
+ duel_list[i].max_players_limit = maxpl;
+
+ strcpy(output, msg_txt(372)); // " -- Duel has been created (@invite/@leave) --"
+ clif_disp_onlyself(sd, output, strlen(output));
+
+ clif_set0199(sd->fd, 1);
+ //clif_misceffect2(&sd->bl, 159);
+ return i;
+}
+
+int duel_invite(
+ const unsigned int did, struct map_session_data* sd,
+ struct map_session_data* target_sd)
+{
+ char output[256];
+
+ sprintf(output, msg_txt(373), // " -- Player %s invites %s to duel --"
+ (unsigned char *)sd->status.name, (unsigned char *)target_sd->status.name);
+
+ duel_msg_foreach_sameduel_wos(did, sd, output);
+
+ target_sd->duel_invite = did;
+ duel_list[did].invites_count++;
+
+ // "Blue -- Player %s invites you to PVP duel (@accept/@reject) --"
+ sprintf(output, msg_txt(374), (unsigned char *)sd->status.name);
+ clif_GMmessage((struct block_list *)target_sd, output, strlen(output)+1, 3);
+ return 0;
+}
+
+int duel_leave(
+ const unsigned int did, struct map_session_data* sd)
+{
+ int i;
+ char output[256];
+ struct map_session_data* msg_sd;
+
+ // " <- Player %s has left duel --"
+ sprintf(output, msg_txt(375), (unsigned char *)sd->status.name);
+ duel_msg_foreach_sameduel_wos(did, sd, output);
+
+ duel_list[did].members_count--;
+
+ if(duel_list[did].members_count == 0) {
+ for (i=0; i<fd_max; i++)
+ if (session[i] && (msg_sd = (struct map_session_data *) session[i]->session_data)
+ && msg_sd->state.auth && msg_sd->duel_invite == did && msg_sd != sd) {
+
+ msg_sd->duel_invite = 0;
+ }
+
+ duel_count--;
+ }
+
+ sd->duel_group = 0;
+ duel_savetime(sd);
+ clif_set0199(sd->fd, 0);
+ return 0;
+}
+
+int duel_accept(
+ const unsigned int did, struct map_session_data* sd)
+{
+ char output[256];
+
+ // " -> Player %s has accepted duel --"
+ sprintf(output, msg_txt(376), (unsigned char *)sd->status.name);
+ duel_msg_foreach_sameduel_wos(did, sd, output);
+
+ duel_list[did].members_count++;
+ sd->duel_group = sd->duel_invite;
+ duel_list[did].invites_count--;
+ sd->duel_invite = 0;
+
+ clif_set0199(sd->fd, 1);
+ //clif_misceffect2(&sd->bl, 159);
+ return 0;
+}
+
+int duel_reject(
+ const unsigned int did, struct map_session_data* sd)
+{
+ char output[256];
+
+ // " -- Player %s has rejected duel --"
+ sprintf(output, msg_txt(377), (unsigned char *)sd->status.name);
+ duel_msg_foreach_sameduel_wos(did, sd, output);
+
+ duel_list[did].invites_count--;
+ sd->duel_invite = 0;
+ return 0;
+}
+
+/*==========================================
+// @ command processing functions
+ *------------------------------------------
+ */
+
+/*==========================================
+ * @send (used for testing packet sends from the client)
+ *------------------------------------------
+ */
+int atcommand_send(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ int i,type=0;
+ int info[20];
+
+ if (!message || !*message || sscanf(message, "%x %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d", &type, &info[1], &info[2], &info[3], &info[4], &info[5], &info[6], &info[7], &info[8], &info[9], &info[10], &info[11], &info[12], &info[13], &info[14], &info[15], &info[16], &info[17], &info[18], &info[19], &info[20]) < 1) {
+ clif_displaymessage(fd, "Please enter a packet number, and - if required - up to 20 additional values.");
+ return -1;
+ }
+
+ if (type > 0 && type < MAX_PACKET_DB) {
+
+ switch (type)
+ {
+ case 0x209: {
+ WFIFOHEAD(fd, packet_db[sd->packet_ver][type].len);
+ WFIFOW(fd,0) = 0x209;
+ WFIFOW(fd,2) = 2;
+ memcpy(WFIFOP(fd, 12), sd->status.name, NAME_LENGTH);
+ WFIFOSET(fd, packet_db[sd->packet_ver][type].len);
+ break;
+ }
+ case 0x1b1:
+ case 0x1c2:
+ //case xxx:
+ // add others here
+ // break;
+ default: {
+ WFIFOHEAD(fd, packet_db[sd->packet_ver][type].len);
+ WFIFOW(fd,0) = type;
+ for(i=1;i<=sizeof(info);i++)
+ if(info[i])
+ WFIFOW(fd,i) = info[i];
+ WFIFOSET(fd, packet_db[sd->packet_ver][type].len);
+ break;
+ }
+ }
+
+ sprintf (atcmd_output, msg_table[258], type, type);
+ clif_displaymessage(fd, atcmd_output);
+ } else {
+ clif_displaymessage(fd, msg_table[259]);
+ }
+
+ return 0;
+}
+
+// @rura
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int atcommand_rura(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ char map_name[MAP_NAME_LENGTH];
+ unsigned short mapindex;
+ int x = 0, y = 0;
+ int m = -1;
+
+ nullpo_retr(-1, sd);
+
+ memset(map_name, '\0', sizeof(map_name));
+
+ if (!message || !*message || sscanf(message, "%15s %d %d", map_name, &x, &y) < 1) {
+ clif_displaymessage(fd, "Please, enter a map (usage: @warp/@rura/@mapmove <mapname> <x> <y>).");
+ return -1;
+ }
+
+ if (x <= 0)
+ x = rand() % 399 + 1;
+ if (y <= 0)
+ y = rand() % 399 + 1;
+
+ if (strstr(map_name, ".gat") == NULL && strstr(map_name, ".afm") == NULL && strlen(map_name) < MAP_NAME_LENGTH-4) // 16 - 4 (.gat)
+ strcat(map_name, ".gat");
+
+ mapindex = mapindex_name2id(map_name);
+ if (mapindex)
+ m = map_mapindex2mapid(mapindex);
+
+ if (!mapindex || m < 0) {
+ clif_displaymessage(fd, msg_table[1]); // Map not found.
+ return -1;
+ }
+
+ if (x > 0 && x < 400 && y > 0 && y < 400) {
+ if (map[m].flag.nowarpto && battle_config.any_warp_GM_min_level > pc_isGM(sd)) {
+ clif_displaymessage(fd, msg_table[247]);
+ return -1;
+ }
+ if (sd->bl.m >= 0 && map[sd->bl.m].flag.nowarp && battle_config.any_warp_GM_min_level > pc_isGM(sd)) {
+ clif_displaymessage(fd, msg_table[248]);
+ return -1;
+ }
+ if (pc_setpos(sd, mapindex, x, y, 3) == 0)
+ clif_displaymessage(fd, msg_table[0]); // Warped.
+ else {
+ clif_displaymessage(fd, msg_table[1]); // Map not found.
+ return -1;
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[2]); // Coordinates out of range.
+ return -1;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * Displays where a character is. Corrected version by Silent. [Skotlex]
+ *------------------------------------------
+ */
+int atcommand_where(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ struct map_session_data *pl_sd = NULL;
+
+ int GM_level, pl_GM_level;
+ memset(atcmd_player_name, '\0', sizeof atcmd_player_name);
+
+ if (!message || !*message || sscanf(message, "%23[^\n]", atcmd_player_name) < 1) {
+ clif_displaymessage(fd, "Please, enter a player name (usage: @where <char name>).");
+ return -1;
+ }
+ pl_sd = map_nick2sd(atcmd_player_name);
+ nullpo_retr(-1, sd);
+
+ if (pl_sd == NULL)
+ return -1;
+
+ if(strncmp(sd->status.name,atcmd_player_name,NAME_LENGTH)==0)
+ return -1;
+
+ GM_level = pc_isGM(sd);//also hide gms depending on settings in battle_athena.conf, show if they are aid [Kevin]
+ pl_GM_level = pc_isGM(pl_sd);
+
+ if (battle_config.hide_GM_session) {
+ if(!(GM_level >= pl_GM_level)) {
+ if (!(battle_config.who_display_aid > 0 && pc_isGM(sd) >= battle_config.who_display_aid)) {
+ return -1;
+ }
+ }
+ }
+
+ snprintf(atcmd_output, sizeof atcmd_output, "%s %s %d %d",
+ atcmd_player_name, mapindex_id2name(pl_sd->mapindex), pl_sd->bl.x, pl_sd->bl.y);
+ clif_displaymessage(fd, atcmd_output);
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int atcommand_jumpto(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ struct map_session_data *pl_sd = NULL;
+
+ nullpo_retr(-1, sd);
+
+ if (!message || !*message || sscanf(message, "%99[^\n]", atcmd_player_name) < 1) {
+ clif_displaymessage(fd, "Please, enter a player name (usage: @jumpto/@warpto/@goto <char name>).");
+ return -1;
+ }
+
+ memset(atcmd_player_name, '\0', sizeof atcmd_player_name);
+ if (sscanf(message, "%23[^\n]", atcmd_player_name) < 1)
+ return -1;
+ if(strncmp(sd->status.name,atcmd_player_name,NAME_LENGTH)==0) //Yourself mate? Tsk tsk tsk.
+ return -1;
+
+ if ((pl_sd = map_nick2sd(atcmd_player_name)) != NULL) {
+ if (pl_sd->bl.m >= 0 && map[pl_sd->bl.m].flag.nowarpto && battle_config.any_warp_GM_min_level > pc_isGM(sd)) {
+ clif_displaymessage(fd, msg_table[247]);
+ return -1;
+ }
+ if (sd->bl.m >= 0 && map[sd->bl.m].flag.nowarp && battle_config.any_warp_GM_min_level > pc_isGM(sd)) {
+ clif_displaymessage(fd, msg_table[248]);
+ return -1;
+ }
+ pc_setpos(sd, pl_sd->mapindex, pl_sd->bl.x, pl_sd->bl.y, 3);
+ sprintf(atcmd_output, msg_table[4], atcmd_player_name); // Jump to %s
+ clif_displaymessage(fd, atcmd_output);
+ } else {
+ clif_displaymessage(fd, msg_table[3]); // Character not found.
+ return -1;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int atcommand_jump(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ int x = 0, y = 0;
+
+ nullpo_retr(-1, sd);
+
+ memset(atcmd_output, '\0', sizeof(atcmd_output));
+
+ sscanf(message, "%d %d", &x, &y);
+
+ if (x <= 0) //If coordinates are 'wrong', random jump.
+ x = -1;
+ if (y <= 0)
+ y = -1;
+ if (sd->bl.m >= 0 && (map[sd->bl.m].flag.nowarp || map[sd->bl.m].flag.nowarpto) && battle_config.any_warp_GM_min_level > pc_isGM(sd)) {
+ clif_displaymessage(fd, msg_table[248]);
+ return -1;
+ }
+ pc_setpos(sd, sd->mapindex, x, y, 3);
+ sprintf(atcmd_output, msg_table[5], sd->bl.x, sd->bl.y); // Jump to %d %d
+ clif_displaymessage(fd, atcmd_output);
+ return 0;
+}
+
+/*==========================================
+ * @who3 = Player name, his location
+ *------------------------------------------
+ */
+int atcommand_who3(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ char temp0[100];
+ struct map_session_data *pl_sd, **pl_allsd;
+ int i, j, count, users;
+ int pl_GM_level, GM_level;
+ char match_text[100];
+ char player_name[NAME_LENGTH];
+
+ nullpo_retr(-1, sd);
+
+ memset(atcmd_output, '\0', sizeof(atcmd_output));
+ memset(match_text, '\0', sizeof(match_text));
+ memset(player_name, '\0', sizeof(player_name));
+
+ if (sscanf(message, "%99[^\n]", match_text) < 1)
+ strcpy(match_text, "");
+ for (j = 0; match_text[j]; j++)
+ match_text[j] = tolower(match_text[j]);
+
+ count = 0;
+ GM_level = pc_isGM(sd);
+ pl_allsd = map_getallusers(&users);
+ for (i = 0; i < users; i++) {
+ if ((pl_sd = pl_allsd[i])) {
+ pl_GM_level = pc_isGM(pl_sd);
+ if (!((battle_config.hide_GM_session || (pl_sd->status.option & OPTION_INVISIBLE)) && (pl_GM_level > GM_level))) { // you can look only lower or same level
+ memcpy(player_name, pl_sd->status.name, NAME_LENGTH);
+ for (j = 0; player_name[j]; j++)
+ player_name[j] = tolower(player_name[j]);
+ if (strstr(player_name, match_text) != NULL) { // search with no case sensitive
+
+ if (battle_config.who_display_aid > 0 && pc_isGM(sd) >= battle_config.who_display_aid) {
+ sprintf(atcmd_output, "(CID:%d/AID:%d) ", pl_sd->status.char_id, pl_sd->status.account_id);
+ } else {
+ atcmd_output[0]=0;
+ }
+ //Player name
+ sprintf(temp0, msg_txt(333), pl_sd->status.name);
+ strcat(atcmd_output,temp0);
+ //Player title, if exists
+ if (pl_GM_level > 0) {
+ //sprintf(temp0, "(%s) ", player_title_txt(pl_GM_level) );
+ sprintf(temp0, msg_txt(334), player_title_txt(pl_GM_level) );
+ strcat(atcmd_output,temp0);
+ }
+ //Players Location: map x y
+ sprintf(temp0, msg_txt(338), mapindex_id2name(pl_sd->mapindex), pl_sd->bl.x, pl_sd->bl.y);
+ strcat(atcmd_output,temp0);
+
+ clif_displaymessage(fd, atcmd_output);
+ count++;
+ }
+ }
+ }
+ }
+
+ if (count == 0)
+ clif_displaymessage(fd, msg_table[28]); // No player found.
+ else if (count == 1)
+ clif_displaymessage(fd, msg_table[29]); // 1 player found.
+ else {
+ sprintf(atcmd_output, msg_table[30], count); // %d players found.
+ clif_displaymessage(fd, atcmd_output);
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * Player name, BLevel, Job,
+ *------------------------------------------
+ */
+int atcommand_who2(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ char temp0[100];
+ struct map_session_data *pl_sd, **pl_allsd;
+ int i, j, count, users;
+ int pl_GM_level, GM_level;
+ char match_text[100];
+ char player_name[NAME_LENGTH];
+
+ nullpo_retr(-1, sd);
+
+ memset(atcmd_output, '\0', sizeof(atcmd_output));
+ memset(match_text, '\0', sizeof(match_text));
+ memset(player_name, '\0', sizeof(player_name));
+
+ if (sscanf(message, "%99[^\n]", match_text) < 1)
+ strcpy(match_text, "");
+ for (j = 0; match_text[j]; j++)
+ match_text[j] = tolower(match_text[j]);
+
+ count = 0;
+ GM_level = pc_isGM(sd);
+ pl_allsd = map_getallusers(&users);
+ for (i = 0; i < users; i++) {
+ if ((pl_sd = pl_allsd[i])) {
+ pl_GM_level = pc_isGM(pl_sd);
+ if (!((battle_config.hide_GM_session || (pl_sd->status.option & OPTION_INVISIBLE)) && (pl_GM_level > GM_level))) { // you can look only lower or same level
+ memcpy(player_name, pl_sd->status.name, NAME_LENGTH);
+ for (j = 0; player_name[j]; j++)
+ player_name[j] = tolower(player_name[j]);
+ if (strstr(player_name, match_text) != NULL) { // search with no case sensitive
+ //Players Name
+ //sprintf(atcmd_output, "Name: %s ", pl_sd->status.name);
+ sprintf(atcmd_output, msg_txt(333), pl_sd->status.name);
+ //Player title, if exists
+ if (pl_GM_level > 0) {
+ //sprintf(temp0, "(%s) ", player_title_txt(pl_GM_level) );
+ sprintf(temp0, msg_txt(334), player_title_txt(pl_GM_level) );
+ strcat(atcmd_output,temp0);
+ }
+ //Players Base Level / Job name
+ //sprintf(temp0, "| L:%d/%d | Job: %s", pl_sd->status.base_level, pl_sd->status.job_level, job_name(pl_sd->status.class_) );
+ sprintf(temp0, msg_txt(337), pl_sd->status.base_level, pl_sd->status.job_level, job_name(pl_sd->status.class_) );
+ strcat(atcmd_output,temp0);
+
+ clif_displaymessage(fd, atcmd_output);
+ count++;
+ }
+ }
+ }
+ }
+
+ if (count == 0)
+ clif_displaymessage(fd, msg_txt(28)); // No player found.
+ else if (count == 1)
+ clif_displaymessage(fd, msg_txt(29)); // 1 player found.
+ else {
+ sprintf(atcmd_output, msg_txt(30), count); // %d players found.
+ clif_displaymessage(fd, atcmd_output);
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * Player name, Playrs Party / Guild name
+ *------------------------------------------
+ */
+int atcommand_who(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ char temp0[100];
+ struct map_session_data *pl_sd, **pl_allsd;
+ int i, j, count, users;
+ int pl_GM_level, GM_level;
+ char match_text[100];
+ char player_name[NAME_LENGTH];
+ struct guild *g;
+ struct party *p;
+
+ nullpo_retr(-1, sd);
+
+ memset(temp0, '\0', sizeof(temp0));
+ memset(atcmd_output, '\0', sizeof(atcmd_output));
+ memset(match_text, '\0', sizeof(match_text));
+ memset(player_name, '\0', sizeof(player_name));
+
+ if (sscanf(message, "%99[^\n]", match_text) < 1)
+ strcpy(match_text, "");
+ for (j = 0; match_text[j]; j++)
+ match_text[j] = tolower(match_text[j]);
+
+ count = 0;
+ GM_level = pc_isGM(sd);
+ pl_allsd = map_getallusers(&users);
+ for (i = 0; i < users; i++) {
+ if ((pl_sd = pl_allsd[i])) {
+ pl_GM_level = pc_isGM(pl_sd);
+ if (!((battle_config.hide_GM_session || (pl_sd->status.option & OPTION_INVISIBLE)) && (pl_GM_level > GM_level))) { // you can look only lower or same level
+ memcpy(player_name, pl_sd->status.name, NAME_LENGTH);
+ for (j = 0; player_name[j]; j++)
+ player_name[j] = tolower(player_name[j]);
+ if (strstr(player_name, match_text) != NULL) { // search with no case sensitive
+ g = guild_search(pl_sd->status.guild_id);
+ p = party_search(pl_sd->status.party_id);
+ //Players Name
+ //sprintf(atcmd_output, "Name: %s ", pl_sd->status.name);
+ sprintf(atcmd_output, msg_txt(333), pl_sd->status.name);
+ //Player title, if exists
+ if (pl_GM_level > 0) {
+ //sprintf(temp0, "(%s) ", player_title_txt(pl_GM_level) );
+ sprintf(temp0, msg_txt(334), player_title_txt(pl_GM_level) );
+ strcat(atcmd_output,temp0);
+ }
+ //Players Party if exists
+ if (p != NULL) {
+ //sprintf(temp0," | Party: '%s'", p->name);
+ sprintf(temp0, msg_txt(335), p->name);
+ strcat(atcmd_output,temp0);
+ }
+ //Players Guild if exists
+ if (g != NULL) {
+ //sprintf(temp0," | Guild: '%s'", g->name);
+ sprintf(temp0, msg_txt(336), g->name);
+ strcat(atcmd_output,temp0);
+ }
+ clif_displaymessage(fd, atcmd_output);
+ count++;
+ }
+ }
+ }
+ }
+
+ if (count == 0)
+ clif_displaymessage(fd, msg_txt(28)); // No player found.
+ else if (count == 1)
+ clif_displaymessage(fd, msg_txt(29)); // 1 player found.
+ else {
+ sprintf(atcmd_output, msg_txt(30), count); // %d players found.
+ clif_displaymessage(fd, atcmd_output);
+ }
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int atcommand_whomap3(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ struct map_session_data *pl_sd, **pl_allsd;
+ int i, count, users;
+ int pl_GM_level, GM_level;
+ int map_id;
+ char map_name[MAP_NAME_LENGTH];
+
+ memset(atcmd_output, '\0', sizeof(atcmd_output));
+ memset(map_name, '\0', sizeof(map_name));
+
+ if (!message || !*message)
+ map_id = sd->bl.m;
+ else {
+ sscanf(message, "%15s", map_name);
+ if (strstr(map_name, ".gat") == NULL && strstr(map_name, ".afm") == NULL && strlen(map_name) < MAP_NAME_LENGTH-4) // 16 - 4 (.gat)
+ strcat(map_name, ".gat");
+ if ((map_id = map_mapname2mapid(map_name)) < 0)
+ map_id = sd->bl.m;
+ }
+
+ count = 0;
+ GM_level = pc_isGM(sd);
+ pl_allsd = map_getallusers(&users);
+ for (i = 0; i < users; i++) {
+ if ((pl_sd = pl_allsd[i])) {
+ pl_GM_level = pc_isGM(pl_sd);
+ if (!((battle_config.hide_GM_session || (pl_sd->status.option & OPTION_INVISIBLE)) && (pl_GM_level > GM_level))) { // you can look only lower or same level
+ if (pl_sd->bl.m == map_id) {
+ if (pl_GM_level > 0)
+ sprintf(atcmd_output, "Name: %s (GM:%d) | Location: %s %d %d", pl_sd->status.name, pl_GM_level, mapindex_id2name(pl_sd->mapindex), pl_sd->bl.x, pl_sd->bl.y);
+ else
+ sprintf(atcmd_output, "Name: %s | Location: %s %d %d", pl_sd->status.name, mapindex_id2name(pl_sd->mapindex), pl_sd->bl.x, pl_sd->bl.y);
+ clif_displaymessage(fd, atcmd_output);
+ count++;
+ }
+ }
+ }
+ }
+
+ if (count == 0)
+ sprintf(atcmd_output, msg_txt(54), map[map_id].name); // No player found in map '%s'.
+ else if (count == 1)
+ sprintf(atcmd_output, msg_txt(55), map[map_id].name); // 1 player found in map '%s'.
+ else {
+ sprintf(atcmd_output, msg_txt(56), count, map[map_id].name); // %d players found in map '%s'.
+ }
+ clif_displaymessage(fd, atcmd_output);
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int atcommand_whomap2(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ struct map_session_data *pl_sd, **pl_allsd;
+ int i, count, users;
+ int pl_GM_level, GM_level;
+ int map_id = 0;
+ char map_name[MAP_NAME_LENGTH];
+
+ nullpo_retr(-1, sd);
+
+ memset(atcmd_output, '\0', sizeof(atcmd_output));
+ memset(map_name, '\0', sizeof(map_name));
+
+ if (!message || !*message)
+ map_id = sd->bl.m;
+ else {
+ sscanf(message, "%15s", map_name);
+ if (strstr(map_name, ".gat") == NULL && strstr(map_name, ".afm") == NULL && strlen(map_name) < MAP_NAME_LENGTH-4) // 16 - 4 (.gat)
+ strcat(map_name, ".gat");
+ if ((map_id = map_mapname2mapid(map_name)) < 0)
+ map_id = sd->bl.m;
+ }
+
+ count = 0;
+ GM_level = pc_isGM(sd);
+ pl_allsd = map_getallusers(&users);
+ for (i = 0; i < users; i++) {
+ if ((pl_sd = pl_allsd[i])) {
+ pl_GM_level = pc_isGM(pl_sd);
+ if (!((battle_config.hide_GM_session || (pl_sd->status.option & OPTION_INVISIBLE)) && (pl_GM_level > GM_level))) { // you can look only lower or same level
+ if (pl_sd->bl.m == map_id) {
+ if (pl_GM_level > 0)
+ sprintf(atcmd_output, "Name: %s (GM:%d) | BLvl: %d | Job: %s (Lvl: %d)", pl_sd->status.name, pl_GM_level, pl_sd->status.base_level, job_name(pl_sd->status.class_), pl_sd->status.job_level);
+ else
+ sprintf(atcmd_output, "Name: %s | BLvl: %d | Job: %s (Lvl: %d)", pl_sd->status.name, pl_sd->status.base_level, job_name(pl_sd->status.class_), pl_sd->status.job_level);
+ clif_displaymessage(fd, atcmd_output);
+ count++;
+ }
+ }
+ }
+ }
+
+ if (count == 0)
+ sprintf(atcmd_output, msg_table[54], map[map_id].name); // No player found in map '%s'.
+ else if (count == 1)
+ sprintf(atcmd_output, msg_table[55], map[map_id].name); // 1 player found in map '%s'.
+ else {
+ sprintf(atcmd_output, msg_table[56], count, map[map_id].name); // %d players found in map '%s'.
+ }
+ clif_displaymessage(fd, atcmd_output);
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int atcommand_whomap(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ char temp0[100];
+ char temp1[100];
+ struct map_session_data *pl_sd, **pl_allsd;
+ int i, count, users;
+ int pl_GM_level, GM_level;
+ int map_id = 0;
+ char map_name[MAP_NAME_LENGTH];
+ struct guild *g;
+ struct party *p;
+
+ nullpo_retr(-1, sd);
+
+ memset(temp0, '\0', sizeof(temp0));
+ memset(temp1, '\0', sizeof(temp1));
+ memset(atcmd_output, '\0', sizeof(atcmd_output));
+ memset(map_name, '\0', sizeof(map_name));
+
+ if (!message || !*message)
+ map_id = sd->bl.m;
+ else {
+ sscanf(message, "%15s", map_name);
+ if (strstr(map_name, ".gat") == NULL && strstr(map_name, ".afm") == NULL && strlen(map_name) < MAP_NAME_LENGTH-4) // 16 - 4 (.gat)
+ strcat(map_name, ".gat");
+ if ((map_id = map_mapname2mapid(map_name)) < 0)
+ map_id = sd->bl.m;
+ }
+
+ count = 0;
+ GM_level = pc_isGM(sd);
+
+ pl_allsd = map_getallusers(&users);
+ for (i = 0; i < users; i++) {
+ if ((pl_sd = pl_allsd[i])) {
+ pl_GM_level = pc_isGM(pl_sd);
+ if (!((battle_config.hide_GM_session || (pl_sd->status.option & OPTION_INVISIBLE)) && (pl_GM_level > GM_level))) { // you can look only lower or same level
+ if (pl_sd->bl.m == map_id) {
+ g = guild_search(pl_sd->status.guild_id);
+ if (g == NULL)
+ sprintf(temp1, "None");
+ else
+ sprintf(temp1, "%s", g->name);
+ p = party_search(pl_sd->status.party_id);
+ if (p == NULL)
+ sprintf(temp0, "None");
+ else
+ sprintf(temp0, "%s", p->name);
+ if (pl_GM_level > 0)
+ sprintf(atcmd_output, "Name: %s (GM:%d) | Party: '%s' | Guild: '%s'", pl_sd->status.name, pl_GM_level, temp0, temp1);
+ else
+ sprintf(atcmd_output, "Name: %s | Party: '%s' | Guild: '%s'", pl_sd->status.name, temp0, temp1);
+ clif_displaymessage(fd, atcmd_output);
+ count++;
+ }
+ }
+ }
+ }
+
+ if (count == 0)
+ sprintf(atcmd_output, msg_table[54], map[map_id].name); // No player found in map '%s'.
+ else if (count == 1)
+ sprintf(atcmd_output, msg_table[55], map[map_id].name); // 1 player found in map '%s'.
+ else {
+ sprintf(atcmd_output, msg_table[56], count, map[map_id].name); // %d players found in map '%s'.
+ }
+ clif_displaymessage(fd, atcmd_output);
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int atcommand_whogm(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ char temp0[100];
+ char temp1[100];
+ struct map_session_data *pl_sd, **pl_allsd;
+ int i, j, count, users;
+ int pl_GM_level, GM_level;
+ char match_text[100];
+ char player_name[NAME_LENGTH];
+ struct guild *g;
+ struct party *p;
+
+ nullpo_retr(-1, sd);
+
+ memset(temp0, '\0', sizeof(temp0));
+ memset(temp1, '\0', sizeof(temp1));
+ memset(atcmd_output, '\0', sizeof(atcmd_output));
+ memset(match_text, '\0', sizeof(match_text));
+ memset(player_name, '\0', sizeof(player_name));
+
+ if (sscanf(message, "%99[^\n]", match_text) < 1)
+ strcpy(match_text, "");
+ for (j = 0; match_text[j]; j++)
+ match_text[j] = tolower(match_text[j]);
+
+ count = 0;
+ GM_level = pc_isGM(sd);
+ pl_allsd = map_getallusers(&users);
+ for (i = 0; i < users; i++) {
+ if ((pl_sd = pl_allsd[i])) {
+ pl_GM_level = pc_isGM(pl_sd);
+ if (pl_GM_level > 0) {
+ if (!((battle_config.hide_GM_session || (pl_sd->status.option & OPTION_INVISIBLE)) && (pl_GM_level > GM_level))) { // you can look only lower or same level
+ memcpy(player_name, pl_sd->status.name, NAME_LENGTH);
+ for (j = 0; player_name[j]; j++)
+ player_name[j] = tolower(player_name[j]);
+ if (strstr(player_name, match_text) != NULL) { // search with no case sensitive
+ sprintf(atcmd_output, "Name: %s (GM:%d) | Location: %s %d %d", pl_sd->status.name, pl_GM_level, mapindex_id2name(pl_sd->mapindex), pl_sd->bl.x, pl_sd->bl.y);
+ clif_displaymessage(fd, atcmd_output);
+ sprintf(atcmd_output, " BLvl: %d | Job: %s (Lvl: %d)", pl_sd->status.base_level, job_name(pl_sd->status.class_), pl_sd->status.job_level);
+ clif_displaymessage(fd, atcmd_output);
+ g = guild_search(pl_sd->status.guild_id);
+ if (g == NULL)
+ sprintf(temp1, "None");
+ else
+ sprintf(temp1, "%s", g->name);
+ p = party_search(pl_sd->status.party_id);
+ if (p == NULL)
+ sprintf(temp0, "None");
+ else
+ sprintf(temp0, "%s", p->name);
+ sprintf(atcmd_output, " Party: '%s' | Guild: '%s'", temp0, temp1);
+ clif_displaymessage(fd, atcmd_output);
+ count++;
+ }
+ }
+ }
+ }
+ }
+
+ if (count == 0)
+ clif_displaymessage(fd, msg_table[150]); // No GM found.
+ else if (count == 1)
+ clif_displaymessage(fd, msg_table[151]); // 1 GM found.
+ else {
+ sprintf(atcmd_output, msg_table[152], count); // %d GMs found.
+ clif_displaymessage(fd, atcmd_output);
+ }
+
+ return 0;
+}
+
+int atcommand_whozeny(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ struct map_session_data *pl_sd, **pl_allsd;
+ int i, j, count,c, users;
+ char match_text[100];
+ char player_name[NAME_LENGTH];
+ int *zeny;
+ int *counted;
+
+ nullpo_retr(-1, sd);
+
+ memset(atcmd_output, '\0', sizeof(atcmd_output));
+ memset(match_text, '\0', sizeof(match_text));
+ memset(player_name, '\0', sizeof(player_name));
+
+ if (sscanf(message, "%99[^\n]", match_text) < 1)
+ strcpy(match_text, "");
+ for (j = 0; match_text[j]; j++)
+ match_text[j] = tolower(match_text[j]);
+
+ count = 0;
+ pl_allsd = map_getallusers(&users);
+ if (users < 1)
+ {
+ clif_displaymessage(fd, msg_table[28]); // No player found.
+ return 0;
+ }
+ zeny = (int *)aCallocA(users, sizeof(int));
+ counted = (int *)aCallocA(users, sizeof(int));
+ for (i = 0; i < users; i++) {
+ if ((pl_sd = pl_allsd[i])) {
+ memcpy(player_name, pl_sd->status.name, NAME_LENGTH);
+ for (j = 0; player_name[j]; j++)
+ player_name[j] = tolower(player_name[j]);
+ if (strstr(player_name, match_text) != NULL) { // search with no case sensitive
+ zeny[count]=pl_sd->status.zeny;
+ counted[i]=0;
+ count++;
+ }
+ }
+ }
+
+ qsort(zeny, count, sizeof(int), hightolow_compare);
+ for (c = 0; c < count && c < 50; c++) {
+ if(!zeny[c])
+ continue;
+ for (i = 0; i < users; i++) {
+ if(!zeny[c])
+ continue;
+ if ((pl_sd = pl_allsd[i]) && counted[i]==0) {
+ if(pl_sd->status.zeny==zeny[c]) {
+ sprintf(atcmd_output, "Name: %s | Zeny: %d", pl_sd->status.name, pl_sd->status.zeny);
+ clif_displaymessage(fd, atcmd_output);
+ zeny[c]=0;
+ counted[i]=1;
+ }
+ }
+ }
+ }
+
+ if (count == 0)
+ clif_displaymessage(fd, msg_table[28]); // No player found.
+ else if (count == 1)
+ clif_displaymessage(fd, msg_table[29]); // 1 player found.
+ else {
+ sprintf(atcmd_output, msg_table[30], count); // %d players found.
+ clif_displaymessage(fd, atcmd_output);
+ }
+
+ aFree(zeny);
+ aFree(counted);
+
+ return 0;
+}
+
+
+// cause random emote on all online players [Valaris]
+int atcommand_happyhappyjoyjoy(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ struct map_session_data *pl_sd, **pl_allsd;
+ int i,e, users;
+
+ nullpo_retr(-1, sd);
+
+ pl_allsd = map_getallusers(&users);
+
+ for (i = 0; i < users; i++) {
+ if ((pl_sd = pl_allsd[i])) {
+ e=rand()%40;
+ if(e==34)
+ e = 0;
+ clif_emotion(&pl_sd->bl,e);
+ }
+ }
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int atcommand_save(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ nullpo_retr(-1, sd);
+
+ pc_setsavepoint(sd, sd->mapindex, sd->bl.x, sd->bl.y);
+ if (sd->status.pet_id > 0 && sd->pd)
+ intif_save_petdata(sd->status.account_id, &sd->pet);
+
+ chrif_save(sd,0);
+
+ clif_displaymessage(fd, msg_table[6]); // Character data respawn point saved.
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int atcommand_load(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ int m;
+
+ nullpo_retr(-1, sd);
+
+ m = map_mapindex2mapid(sd->status.save_point.map);
+ if (m >= 0 && map[m].flag.nowarpto && battle_config.any_warp_GM_min_level > pc_isGM(sd)) {
+ clif_displaymessage(fd, msg_table[249]);
+ return -1;
+ }
+ if (sd->bl.m >= 0 && map[sd->bl.m].flag.nowarp && battle_config.any_warp_GM_min_level > pc_isGM(sd)) {
+ clif_displaymessage(fd, msg_table[248]);
+ return -1;
+ }
+
+ pc_setpos(sd, sd->status.save_point.map, sd->status.save_point.x, sd->status.save_point.y, 0);
+ clif_displaymessage(fd, msg_table[7]); // Warping to respawn point.
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int atcommand_speed(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ int speed;
+
+ nullpo_retr(-1, sd);
+
+ memset(atcmd_output, '\0', sizeof(atcmd_output));
+
+ if (!message || !*message) {
+ sprintf(atcmd_output, "Please, enter a speed value (usage: @speed <%d-%d>).", MIN_WALK_SPEED, MAX_WALK_SPEED);
+ clif_displaymessage(fd, atcmd_output);
+ return -1;
+ }
+
+ speed = atoi(message);
+ if (speed >= MIN_WALK_SPEED && speed <= MAX_WALK_SPEED) {
+ sd->speed = speed;
+ //sd->walktimer = x;
+ //この文を追加 by れ
+ clif_updatestatus(sd, SP_SPEED);
+ clif_displaymessage(fd, msg_table[8]); // Speed changed.
+ } else {
+ sprintf(atcmd_output, "Please, enter a valid speed value (usage: @speed <%d-%d>).", MIN_WALK_SPEED, MAX_WALK_SPEED);
+ clif_displaymessage(fd, atcmd_output);
+ return -1;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int atcommand_storage(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ struct storage *stor; //changes from Freya/Yor
+ nullpo_retr(-1, sd);
+
+ if (sd->state.storage_flag) {
+ clif_displaymessage(fd, msg_table[250]);
+ return -1;
+ }
+
+ if ((stor = account2storage2(sd->status.account_id)) != NULL && stor->storage_status == 1) {
+ clif_displaymessage(fd, msg_table[250]);
+ return -1;
+ }
+
+ storage_storageopen(sd);
+
+ return 0;
+}
+
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int atcommand_guildstorage(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ struct storage *stor; //changes from Freya/Yor
+ nullpo_retr(-1, sd);
+
+ if (sd->status.guild_id > 0) {
+ if (sd->state.storage_flag) {
+ clif_displaymessage(fd, msg_table[251]);
+ return -1;
+ }
+ if ((stor = account2storage2(sd->status.account_id)) != NULL && stor->storage_status == 1) {
+ clif_displaymessage(fd, msg_table[251]);
+ return -1;
+ }
+ storage_guild_storageopen(sd);
+ } else {
+ clif_displaymessage(fd, msg_table[252]);
+ return -1;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int atcommand_option(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ int param1 = 0, param2 = 0, param3 = 0;
+ nullpo_retr(-1, sd);
+
+ if (!message || !*message || sscanf(message, "%d %d %d", &param1, &param2, &param3) < 1 || param1 < 0 || param2 < 0 || param3 < 0) {
+ clif_displaymessage(fd, "Please, enter at least a option (usage: @option <param1:0+> <param2:0+> <param3:0+>).");
+ return -1;
+ }
+
+ sd->opt1 = param1;
+ sd->opt2 = param2;
+ if (!(sd->status.option & CART_MASK) && param3 & CART_MASK) {
+ if (sd->status.class_ == JOB_BABY_MERCHANT)
+ clif_cart_itemlist(sd);
+ clif_cart_equiplist(sd);
+ clif_updatestatus(sd, SP_CARTINFO);
+ }
+ pc_setoption(sd, param3);
+
+ clif_displaymessage(fd, msg_table[9]); // Options changed.
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int atcommand_hide(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ nullpo_retr(-1, sd);
+ if (sd->status.option & OPTION_INVISIBLE) {
+ sd->status.option &= ~OPTION_INVISIBLE;
+ clif_displaymessage(fd, msg_table[10]); // Invisible: Off
+ } else {
+ sd->status.option |= OPTION_INVISIBLE;
+ clif_displaymessage(fd, msg_table[11]); // Invisible: On
+ }
+ clif_changeoption(&sd->bl);
+
+ return 0;
+}
+
+/*==========================================
+ * 転職する upperを指定すると転生や養子にもなれる
+ *------------------------------------------
+ */
+int atcommand_jobchange(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ int job = 0, upper = 0;
+ nullpo_retr(-1, sd);
+
+ if (!message || !*message || sscanf(message, "%d %d", &job, &upper) < 1) {
+
+ int i, found = 0;
+ const struct { char name[16]; int id; } jobs[] = {
+ { "novice", 0 },
+ { "swordsman", 1 },
+ { "mage", 2 },
+ { "archer", 3 },
+ { "acolyte", 4 },
+ { "merchant", 5 },
+ { "thief", 6 },
+ { "knight", 7 },
+ { "priest", 8 },
+ { "priestess", 8 },
+ { "wizard", 9 },
+ { "blacksmith", 10 },
+ { "hunter", 11 },
+ { "assassin", 12 },
+ { "crusader", 14 },
+ { "monk", 15 },
+ { "sage", 16 },
+ { "rogue", 17 },
+ { "alchemist", 18 },
+ { "bard", 19 },
+ { "dancer", 20 },
+ { "super novice", 23 },
+ { "supernovice", 23 },
+ { "high novice", 4001 },
+ { "swordsman high", 4002 },
+ { "mage high", 4003 },
+ { "archer high", 4004 },
+ { "acolyte high", 4005 },
+ { "merchant high", 4006 },
+ { "thief high", 4007 },
+ { "lord knight", 4008 },
+ { "high priest", 4009 },
+ { "high priestess", 4009 },
+ { "high wizard", 4010 },
+ { "whitesmith", 4011 },
+ { "sniper", 4012 },
+ { "assassin cross", 4013 },
+ { "paladin", 4015 },
+ { "champion", 4016 },
+ { "professor", 4017 },
+ { "stalker", 4018 },
+ { "creator", 4019 },
+ { "clown", 4020 },
+ { "gypsy", 4021 },
+ { "baby novice", 4023 },
+ { "baby swordsman", 4024 },
+ { "baby mage", 4025 },
+ { "baby archer", 4026 },
+ { "baby acolyte", 4027 },
+ { "baby merchant", 4028 },
+ { "baby thief", 4029 },
+ { "baby knight", 4030 },
+ { "baby priest", 4031 },
+ { "baby priestess", 4031 },
+ { "baby wizard", 4032 },
+ { "baby blacksmith",4033 },
+ { "baby hunter", 4034 },
+ { "baby assassin", 4035 },
+ { "baby crusader", 4037 },
+ { "baby monk", 4038 },
+ { "baby sage", 4039 },
+ { "baby rogue", 4040 },
+ { "baby alchemist", 4041 },
+ { "baby bard", 4042 },
+ { "baby dancer", 4043 },
+ { "super baby", 4045 },
+ { "taekwon", 4046 },
+ { "taekwon boy", 4046 },
+ { "taekwon girl", 4046 },
+ { "star gladiator", 4047 },
+ { "soul linker", 4049 },
+ };
+
+ for (i=0; i < (int)(sizeof(jobs) / sizeof(jobs[0])); i++) {
+ if (strncmpi(message, jobs[i].name, 16) == 0) {
+ job = jobs[i].id;
+ upper = 0;
+ found = 1;
+ break;
+ }
+ }
+
+ if (!found) {
+ clif_displaymessage(fd, "Please, enter job ID (usage: @job/@jobchange <job ID>).");
+ return -1;
+ }
+ }
+
+ if (job == 37 ||job == 45)
+ return 0;
+
+ if ((job >= 0 && job < MAX_PC_CLASS))
+ {
+ int j;
+
+ for (j=0; j < MAX_INVENTORY; j++) {
+ if(sd->status.inventory[j].nameid>0 && sd->status.inventory[j].equip!=0)
+ pc_unequipitem(sd, j, 3);
+ }
+ if (pc_jobchange(sd, job, upper) == 0)
+ clif_displaymessage(fd, msg_table[12]); // Your job has been changed.
+ else {
+ clif_displaymessage(fd, msg_table[155]); // Impossible to change your job.
+ return -1;
+ }
+ } else {
+ clif_displaymessage(fd, "Please, enter a valid job ID (usage: @job/@jobchange <job ID>).");
+ return -1;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int atcommand_die(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ nullpo_retr(-1, sd);
+ clif_specialeffect(&sd->bl,450,1);
+ pc_damage(NULL, sd, sd->status.hp);
+ clif_displaymessage(fd, msg_table[13]); // A pity! You've died.
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int atcommand_kill(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ struct map_session_data *pl_sd;
+ nullpo_retr(-1, sd);
+
+ memset(atcmd_player_name, '\0', sizeof(atcmd_player_name));
+
+ if (!message || !*message || sscanf(message, "%23[^\n]", atcmd_player_name) < 1) {
+ clif_displaymessage(fd, "Please, enter a player name (usage: @kill <char name>).");
+ return -1;
+ }
+
+ if ((pl_sd = map_nick2sd(atcmd_player_name)) != NULL) {
+ if (pc_isGM(sd) >= pc_isGM(pl_sd)) { // you can kill only lower or same level
+ pc_damage(NULL, pl_sd, pl_sd->status.hp);
+ clif_displaymessage(fd, msg_table[14]); // Character killed.
+ } else {
+ clif_displaymessage(fd, msg_table[81]); // Your GM level don't authorise you to do this action on this player.
+ return -1;
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[3]); // Character not found.
+ return -1;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int atcommand_alive(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ nullpo_retr(-1, sd);
+ if (pc_isdead(sd)) {
+ sd->status.hp = sd->status.max_hp;
+ sd->status.sp = sd->status.max_sp;
+ clif_skill_nodamage(&sd->bl,&sd->bl,ALL_RESURRECTION,4,1);
+ pc_setstand(sd);
+ if (battle_config.pc_invincible_time > 0)
+ pc_setinvincibletimer(sd, battle_config.pc_invincible_time);
+ clif_updatestatus(sd, SP_HP);
+ clif_updatestatus(sd, SP_SP);
+ clif_resurrection(&sd->bl, 1);
+ clif_displaymessage(fd, msg_table[16]); // You've been revived! It's a miracle!
+ return 0;
+ }
+ return -1;
+}
+
+/*==========================================
+ * +kamic [LuzZza]
+ *------------------------------------------
+ */
+int atcommand_kami(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+
+ unsigned long color=0;
+ nullpo_retr(-1, sd);
+
+ memset(atcmd_output, '\0', sizeof(atcmd_output));
+
+ if(*(command + 5) != 'c') {
+
+ if (!message || !*message) {
+ clif_displaymessage(fd, "Please, enter a message (usage: @kami <message>).");
+ return -1;
+ }
+
+ sscanf(message, "%199[^\n]", atcmd_output);
+ intif_GMmessage(atcmd_output, strlen(atcmd_output) + 1, (*(command + 5) == 'b') ? 0x10 : 0);
+
+ } else {
+
+ if(!message || !*message || (sscanf(message, "%lx %199[^\n]", &color, atcmd_output) < 2)) {
+ clif_displaymessage(fd, "Please, enter color and message (usage: @kamic <color> <message>).");
+ return -1;
+ }
+
+ if(color < 0 || color > 0xFFFFFF) {
+ clif_displaymessage(fd, "Invalid color.");
+ return -1;
+ }
+
+ intif_announce(atcmd_output, strlen(atcmd_output) + 1, color, 0);
+ }
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int atcommand_heal(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ int hp = 0, sp = 0; // [Valaris] thanks to fov
+ nullpo_retr(-1, sd);
+
+ sscanf(message, "%d %d", &hp, &sp);
+
+ if (hp == 0 && sp == 0) {
+ hp = sd->status.max_hp - sd->status.hp;
+ sp = sd->status.max_sp - sd->status.sp;
+ } else {
+ if (hp > 0 && (hp > sd->status.max_hp || hp > (sd->status.max_hp - sd->status.hp))) // fix positiv overflow
+ hp = sd->status.max_hp - sd->status.hp;
+ else if (hp < 0 && (hp < -sd->status.max_hp || hp < (1 - sd->status.hp))) // fix negativ overflow
+ hp = 1 - sd->status.hp;
+ if (sp > 0 && (sp > sd->status.max_sp || sp > (sd->status.max_sp - sd->status.sp))) // fix positiv overflow
+ sp = sd->status.max_sp - sd->status.sp;
+ else if (sp < 0 && (sp < -sd->status.max_sp || sp < (1 - sd->status.sp))) // fix negativ overflow
+ sp = 1 - sd->status.sp;
+ }
+
+ if (hp > 0) // display like heal
+ clif_heal(fd, SP_HP, hp);
+ else if (hp < 0) // display like damage
+ clif_damage(&sd->bl,&sd->bl, gettick(), 0, 0, -hp, 0 , 4, 0);
+ if (sp > 0) // no display when we lost SP
+ clif_heal(fd, SP_SP, sp);
+
+ if (hp != 0 || sp != 0) {
+ pc_heal(sd, hp, sp);
+ if (hp >= 0 && sp >= 0)
+ clif_displaymessage(fd, msg_table[17]); // HP, SP recovered.
+ else
+ clif_displaymessage(fd, msg_table[156]); // HP or/and SP modified.
+ } else {
+ clif_displaymessage(fd, msg_table[157]); // HP and SP are already with the good value.
+ return -1;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * @item command (usage: @item <name/id_of_item> <quantity>) (modified by [Yor] for pet_egg)
+ *------------------------------------------
+ */
+int atcommand_item(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ char item_name[100];
+ int number = 0, item_id, flag;
+ struct item item_tmp;
+ struct item_data *item_data;
+ int get_count, i, pet_id;
+ nullpo_retr(-1, sd);
+
+ memset(item_name, '\0', sizeof(item_name));
+
+ if (!message || !*message || sscanf(message, "%99s %d", item_name, &number) < 1) {
+ clif_displaymessage(fd, "Please, enter an item name/id (usage: @item <item name or ID> [quantity]).");
+ return -1;
+ }
+
+ if (number <= 0)
+ number = 1;
+
+ item_id = 0;
+ if ((item_data = itemdb_searchname(item_name)) != NULL ||
+ (item_data = itemdb_exists(atoi(item_name))) != NULL)
+ item_id = item_data->nameid;
+
+ if (item_id >= 500) {
+ get_count = number;
+ // check pet egg
+ pet_id = search_petDB_index(item_id, PET_EGG);
+ if (item_data->type == 4 || item_data->type == 5 ||
+ item_data->type == 7 || item_data->type == 8) {
+ get_count = 1;
+ }
+ for (i = 0; i < number; i += get_count) {
+ // if pet egg
+ if (pet_id >= 0) {
+ sd->catch_target_class = pet_db[pet_id].class_;
+ intif_create_pet(sd->status.account_id, sd->status.char_id,
+ (short)pet_db[pet_id].class_, (short)mob_db(pet_db[pet_id].class_)->lv,
+ (short)pet_db[pet_id].EggID, 0, (short)pet_db[pet_id].intimate,
+ 100, 0, 1, pet_db[pet_id].jname);
+ // if not pet egg
+ } else {
+ memset(&item_tmp, 0, sizeof(item_tmp));
+ item_tmp.nameid = item_id;
+ item_tmp.identify = 1;
+
+ if ((flag = pc_additem((struct map_session_data*)sd, &item_tmp, get_count)))
+ clif_additem((struct map_session_data*)sd, 0, 0, flag);
+ }
+ }
+
+ //Logs (A)dmins items [Lupus]
+ if(log_config.pick > 0 ) {
+ log_pick(sd, "A", 0, item_id, number, NULL);
+ }
+ //Logs
+
+ clif_displaymessage(fd, msg_table[18]); // Item created.
+ } else {
+ clif_displaymessage(fd, msg_table[19]); // Invalid item ID or name.
+ return -1;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int atcommand_item2(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ struct item item_tmp;
+ struct item_data *item_data;
+ char item_name[100];
+ int item_id, number = 0;
+ int identify = 0, refine = 0, attr = 0;
+ int c1 = 0, c2 = 0, c3 = 0, c4 = 0;
+ int flag;
+ int loop, get_count, i;
+ nullpo_retr(-1, sd);
+
+ memset(item_name, '\0', sizeof(item_name));
+
+ if (!message || !*message || sscanf(message, "%99s %d %d %d %d %d %d %d %d", item_name, &number, &identify, &refine, &attr, &c1, &c2, &c3, &c4) < 9) {
+ clif_displaymessage(fd, "Please, enter all informations (usage: @item2 <item name or ID> <quantity>");
+ clif_displaymessage(fd, " <Identify_flag> <refine> <attribut> <Card1> <Card2> <Card3> <Card4>).");
+ return -1;
+ }
+
+ if (number <= 0)
+ number = 1;
+
+ item_id = 0;
+ if ((item_data = itemdb_searchname(item_name)) != NULL ||
+ (item_data = itemdb_exists(atoi(item_name))) != NULL)
+ item_id = item_data->nameid;
+
+ if (item_id > 500) {
+ loop = 1;
+ get_count = number;
+ if (item_data->type == 4 || item_data->type == 5 ||
+ item_data->type == 7 || item_data->type == 8) {
+ loop = number;
+ get_count = 1;
+ if (item_data->type == 7) {
+ identify = 1;
+ refine = 0;
+ }
+ if (item_data->type == 8)
+ refine = 0;
+ if (refine > 10)
+ refine = 10;
+ } else {
+ identify = 1;
+ refine = attr = 0;
+ }
+ for (i = 0; i < loop; i++) {
+ memset(&item_tmp, 0, sizeof(item_tmp));
+ item_tmp.nameid = item_id;
+ item_tmp.identify = identify;
+ item_tmp.refine = refine;
+ item_tmp.attribute = attr;
+ item_tmp.card[0] = c1;
+ item_tmp.card[1] = c2;
+ item_tmp.card[2] = c3;
+ item_tmp.card[3] = c4;
+ if ((flag = pc_additem(sd, &item_tmp, get_count)))
+ clif_additem(sd, 0, 0, flag);
+ }
+
+ //Logs (A)dmins items [Lupus]
+ if(log_config.pick > 0 ) {
+ log_pick(sd, "A", 0, item_tmp.nameid, number, &item_tmp);
+ }
+ //Logs
+
+ clif_displaymessage(fd, msg_table[18]); // Item created.
+ } else {
+ clif_displaymessage(fd, msg_table[19]); // Invalid item ID or name.
+ return -1;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int atcommand_itemreset(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ int i;
+ nullpo_retr(-1, sd);
+
+ for (i = 0; i < MAX_INVENTORY; i++) {
+ if (sd->status.inventory[i].amount && sd->status.inventory[i].equip == 0) {
+
+ //Logs (A)dmins items [Lupus]
+ if(log_config.pick > 0 ) {
+ log_pick(sd, "A", 0, sd->status.inventory[i].nameid, -sd->status.inventory[i].amount, &sd->status.inventory[i]);
+ }
+ //Logs
+
+ pc_delitem(sd, i, sd->status.inventory[i].amount, 0);
+ }
+ }
+ clif_displaymessage(fd, msg_table[20]); // All of your items have been removed.
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int atcommand_itemcheck(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ nullpo_retr(-1, sd);
+ pc_checkitem(sd);
+
+ return 0;
+}
+
+/*==========================================
+ * Atcommand @lvlup
+ *------------------------------------------
+ */
+int atcommand_baselevelup(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ int level=0, i=0;
+ nullpo_retr(-1, sd);
+ level = atoi(message);
+
+ if (!message || !*message || !level) {
+ clif_displaymessage(fd, "Please, enter a level adjustement (usage: @lvup/@blevel/@baselvlup <number of levels>).");
+ return -1;
+ }
+
+ if (level > 0) {
+ if (sd->status.base_level == battle_config.max_base_level) { /* check for max level by Valaris */
+ clif_displaymessage(fd, msg_table[47]); /* Base level can't go any higher. */
+ return -1;
+ } /* End Addition */
+ if ((unsigned int)level > battle_config.max_base_level || (unsigned int)level > (battle_config.max_base_level - sd->status.base_level)) // fix positiv overflow
+ level = battle_config.max_base_level - sd->status.base_level;
+ for (i = 1; i <= level; i++)
+ sd->status.status_point += (sd->status.base_level + i + 14) / 5;
+ sd->status.base_level += level;
+ clif_updatestatus(sd, SP_BASELEVEL);
+ clif_updatestatus(sd, SP_NEXTBASEEXP);
+ clif_updatestatus(sd, SP_STATUSPOINT);
+ status_calc_pc(sd, 0);
+ pc_heal(sd, sd->status.max_hp, sd->status.max_sp);
+ clif_misceffect(&sd->bl, 0);
+ clif_displaymessage(fd, msg_table[21]); /* Base level raised. */
+ } else {
+ if (sd->status.base_level == 1) {
+ clif_displaymessage(fd, msg_table[158]); /* Base level can't go any lower. */
+ return -1;
+ }
+ if (level < -(int)battle_config.max_base_level || level < (1 - (int)sd->status.base_level)) /* fix negativ overflow */
+ level = 1 - sd->status.base_level;
+ if (sd->status.status_point > 0) {
+ for (i = 0; i > level; i--)
+ sd->status.status_point -= (sd->status.base_level + i + 14) / 5;
+ if (sd->status.status_point < 0)
+ sd->status.status_point = 0;
+ clif_updatestatus(sd, SP_STATUSPOINT);
+ } /* to add: remove status points from stats */
+ sd->status.base_level += level;
+ clif_updatestatus(sd, SP_BASELEVEL);
+ clif_updatestatus(sd, SP_NEXTBASEEXP);
+ pc_resetskill(sd); /* Skills are reset */
+ status_calc_pc(sd, 0);
+ clif_displaymessage(fd, msg_table[22]); /* Base level lowered. */
+ }
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int atcommand_joblevelup(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ unsigned int up_level = battle_config.max_job_level;
+ int level=0;
+ nullpo_retr(-1, sd);
+
+ level = atoi(message);
+
+ if (!message || !*message || !level) {
+ clif_displaymessage(fd, "Please, enter a level adjustement (usage: @joblvup/@jlevel/@joblvlup <number of levels>).");
+ return -1;
+ }
+
+ if ((sd->class_&MAPID_UPPERMASK) == MAPID_NOVICE) //Novice
+ up_level = 10;
+ else if ((sd->class_&MAPID_BASEMASK) == MAPID_NOVICE) //S. Novice
+ up_level = battle_config.max_sn_level;
+ else if (sd->class_&JOBL_UPPER && sd->class_&JOBL_2)
+ up_level = battle_config.max_adv_level; //2nd Adv Class
+
+ if (level > 0) {
+ if (sd->status.job_level == up_level) {
+ clif_displaymessage(fd, msg_table[23]); // Job level can't go any higher.
+ return -1;
+ }
+ if ((unsigned int)level > up_level || (unsigned int)level > (up_level - sd->status.job_level)) // fix positiv overflow
+ level = up_level - sd->status.job_level;
+ sd->status.job_level += level;
+ clif_updatestatus(sd, SP_JOBLEVEL);
+ clif_updatestatus(sd, SP_NEXTJOBEXP);
+ sd->status.skill_point += level;
+ clif_updatestatus(sd, SP_SKILLPOINT);
+ status_calc_pc(sd, 0);
+ clif_misceffect(&sd->bl, 1);
+ clif_displaymessage(fd, msg_table[24]); // Job level raised.
+ } else {
+ if (sd->status.job_level == 1) {
+ clif_displaymessage(fd, msg_table[159]); // Job level can't go any lower.
+ return -1;
+ }
+ if (level < -(int)up_level || level < (1 - (int)sd->status.job_level)) // fix negativ overflow
+ level = 1 - sd->status.job_level;
+ sd->status.job_level += level;
+ clif_updatestatus(sd, SP_JOBLEVEL);
+ clif_updatestatus(sd, SP_NEXTJOBEXP);
+ if (sd->status.skill_point > 0) {
+ sd->status.skill_point += level;
+ if (sd->status.skill_point < 0)
+ sd->status.skill_point = 0;
+ clif_updatestatus(sd, SP_SKILLPOINT);
+ } // to add: remove status points from skills
+ status_calc_pc(sd, 0);
+ clif_displaymessage(fd, msg_table[25]); // Job level lowered.
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * @help
+ *------------------------------------------
+ */
+int atcommand_help(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ char buf[2048], w1[2048], w2[2048];
+ int i, gm_level;
+ FILE* fp;
+ nullpo_retr(-1, sd);
+
+ memset(buf, '\0', sizeof(buf));
+
+ if ((fp = fopen(help_txt, "r")) != NULL) {
+ clif_displaymessage(fd, msg_table[26]); /* Help commands: */
+ gm_level = pc_isGM(sd);
+ while(fgets(buf, sizeof(buf) - 1, fp) != NULL) {
+ if (buf[0] == '/' && buf[1] == '/')
+ continue;
+ for (i = 0; buf[i] != '\0'; i++) {
+ if (buf[i] == '\r' || buf[i] == '\n') {
+ buf[i] = '\0';
+ break;
+ }
+ }
+ if (sscanf(buf, "%2047[^:]:%2047[^\n]", w1, w2) < 2)
+ clif_displaymessage(fd, buf);
+ else if (gm_level >= atoi(w1))
+ clif_displaymessage(fd, w2);
+ }
+ fclose(fp);
+ } else {
+ clif_displaymessage(fd, msg_table[27]); /* File help.txt not found. */
+ return -1;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * @help2 - Char commands [Kayla]
+ *------------------------------------------
+ */
+int atcommand_help2(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ char buf[2048], w1[2048], w2[2048];
+ int i, gm_level;
+ FILE* fp;
+ nullpo_retr(-1, sd);
+
+ memset(buf, '\0', sizeof(buf));
+
+ if ((fp = fopen(help2_txt, "r")) != NULL) {
+ clif_displaymessage(fd, msg_table[26]); /* Help commands: */
+ gm_level = pc_isGM(sd);
+ while(fgets(buf, sizeof(buf) - 1, fp) != NULL) {
+ if (buf[0] == '/' && buf[1] == '/')
+ continue;
+ for (i = 0; buf[i] != '\0'; i++) {
+ if (buf[i] == '\r' || buf[i] == '\n') {
+ buf[i] = '\0';
+ break;
+ }
+ }
+ if (sscanf(buf, "%2047[^:]:%2047[^\n]", w1, w2) < 2)
+ clif_displaymessage(fd, buf);
+ else if (gm_level >= atoi(w1))
+ clif_displaymessage(fd, w2);
+ }
+ fclose(fp);
+ } else {
+ clif_displaymessage(fd, msg_table[27]); /* File help.txt not found. */
+ return -1;
+ }
+
+ return 0;
+}
+
+
+/*==========================================
+ * @gm
+ *------------------------------------------
+ */
+int atcommand_gm(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ char password[100];
+ nullpo_retr(-1, sd);
+
+ memset(password, '\0', sizeof(password));
+
+ if (!message || !*message || sscanf(message, "%99[^\n]", password) < 1) {
+ clif_displaymessage(fd, "Please, enter a password (usage: @gm <password>).");
+ return -1;
+ }
+
+ if (pc_isGM(sd)) { /* a GM can not use this function. only a normal player (become gm is not for gm!) */
+ clif_displaymessage(fd, msg_table[50]); /* You already have some GM powers. */
+ return -1;
+ } else
+ chrif_changegm(sd->status.account_id, password, strlen(password) + 1);
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int atcommand_pvpoff(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ struct map_session_data *pl_sd, **pl_allsd;
+ int i, users;
+ nullpo_retr(-1, sd);
+
+ if (battle_config.pk_mode) { //disable command if server is in PK mode [Valaris]
+ clif_displaymessage(fd, msg_table[52]); // This option cannot be used in PK Mode.
+ return -1;
+ }
+
+ if (map[sd->bl.m].flag.pvp) {
+ map[sd->bl.m].flag.pvp = 0;
+ clif_send0199(sd->bl.m, 0);
+
+ pl_allsd = map_getallusers(&users);
+ for (i = 0; i < users; i++) { //人数分ループ
+ if ((pl_sd = pl_allsd[i]) && sd->bl.m == pl_sd->bl.m) {
+ clif_pvpset(pl_sd, 0, 0, 2);
+ if (pl_sd->pvp_timer != -1) {
+ delete_timer(pl_sd->pvp_timer, pc_calc_pvprank_timer);
+ pl_sd->pvp_timer = -1;
+ }
+ }
+ }
+ clif_displaymessage(fd, msg_table[31]); // PvP: Off.
+ } else {
+ clif_displaymessage(fd, msg_table[160]); // PvP is already Off.
+ return -1;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int atcommand_pvpon(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ struct map_session_data *pl_sd, **pl_allsd;
+ int i, users;
+ nullpo_retr(-1, sd);
+
+ if (battle_config.pk_mode) { //disable command if server is in PK mode [Valaris]
+ clif_displaymessage(fd, msg_table[52]); // This option cannot be used in PK Mode.
+ return -1;
+ }
+
+ if (!map[sd->bl.m].flag.pvp && !map[sd->bl.m].flag.nopvp) {
+ map[sd->bl.m].flag.pvp = 1;
+ clif_send0199(sd->bl.m, 1);
+ pl_allsd = map_getallusers(&users);
+ for (i = 0; i < users; i++) {
+ if ((pl_sd = pl_allsd[i]) && sd->bl.m == pl_sd->bl.m && pl_sd->pvp_timer == -1) {
+ pl_sd->pvp_timer = add_timer(gettick() + 200, pc_calc_pvprank_timer, pl_sd->bl.id, 0);
+ pl_sd->pvp_rank = 0;
+ pl_sd->pvp_lastusers = 0;
+ pl_sd->pvp_point = 5;
+ pl_sd->pvp_won = 0;
+ pl_sd->pvp_lost = 0;
+ }
+ }
+ clif_displaymessage(fd, msg_table[32]); // PvP: On.
+ } else {
+ clif_displaymessage(fd, msg_table[161]); // PvP is already On.
+ return -1;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int atcommand_gvgoff(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ nullpo_retr(-1, sd);
+ if (map[sd->bl.m].flag.gvg) {
+ map[sd->bl.m].flag.gvg = 0;
+ clif_send0199(sd->bl.m, 0);
+ clif_displaymessage(fd, msg_table[33]); // GvG: Off.
+ } else {
+ clif_displaymessage(fd, msg_table[162]); // GvG is already Off.
+ return -1;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int atcommand_gvgon(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ nullpo_retr(-1, sd);
+ if (!map[sd->bl.m].flag.gvg) {
+ map[sd->bl.m].flag.gvg = 1;
+ clif_send0199(sd->bl.m, 3);
+ clif_displaymessage(fd, msg_table[34]); // GvG: On.
+ } else {
+ clif_displaymessage(fd, msg_table[163]); // GvG is already On.
+ return -1;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int atcommand_model(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ int hair_style = 0, hair_color = 0, cloth_color = 0;
+ nullpo_retr(-1, sd);
+
+ memset(atcmd_output, '\0', sizeof(atcmd_output));
+
+ if (!message || !*message || sscanf(message, "%d %d %d", &hair_style, &hair_color, &cloth_color) < 1) {
+ sprintf(atcmd_output, "Please, enter at least a value (usage: @model <hair ID: %d-%d> <hair color: %d-%d> <clothes color: %d-%d>).",
+ MIN_HAIR_STYLE, MAX_HAIR_STYLE, MIN_HAIR_COLOR, MAX_HAIR_COLOR, MIN_CLOTH_COLOR, MAX_CLOTH_COLOR);
+ clif_displaymessage(fd, atcmd_output);
+ return -1;
+ }
+
+ if (hair_style >= MIN_HAIR_STYLE && hair_style <= MAX_HAIR_STYLE &&
+ hair_color >= MIN_HAIR_COLOR && hair_color <= MAX_HAIR_COLOR &&
+ cloth_color >= MIN_CLOTH_COLOR && cloth_color <= MAX_CLOTH_COLOR) {
+ /* Removed because this check is TOO strange. [Skotlex]
+ //秒フ色変更
+ if (cloth_color != 0 && sd->status.sex == 1 && (sd->status.class_ == JOB_ASSASSIN || sd->status.class_ == JOB_ROGUE)) {
+ //The hell? Why Rogue/Assassins can't... change their option if they have clothes colors and are males? o.O [Skotlex]
+ //秒フ色未実装職の判定
+ clif_displaymessage(fd, msg_table[35]); // You can't use this command with this class.
+ return -1;
+ } else {
+ */
+ pc_changelook(sd, LOOK_HAIR, hair_style);
+ pc_changelook(sd, LOOK_HAIR_COLOR, hair_color);
+ pc_changelook(sd, LOOK_CLOTHES_COLOR, cloth_color);
+ clif_displaymessage(fd, msg_table[36]); // Appearence changed.
+// }
+ } else {
+ clif_displaymessage(fd, msg_table[37]); // An invalid number was specified.
+ return -1;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * @dye && @ccolor
+ *------------------------------------------
+ */
+int atcommand_dye(const int fd, struct map_session_data* sd, const char* command, const char* message)
+{
+ int cloth_color = 0;
+ nullpo_retr(-1, sd);
+
+ memset(atcmd_output, '\0', sizeof(atcmd_output));
+
+ if (!message || !*message || sscanf(message, "%d", &cloth_color) < 1) {
+ sprintf(atcmd_output, "Please, enter a clothes color (usage: @dye/@ccolor <clothes color: %d-%d>).", MIN_CLOTH_COLOR, MAX_CLOTH_COLOR);
+ clif_displaymessage(fd, atcmd_output);
+ return -1;
+ }
+
+ if (cloth_color >= MIN_CLOTH_COLOR && cloth_color <= MAX_CLOTH_COLOR) {
+ pc_changelook(sd, LOOK_CLOTHES_COLOR, cloth_color);
+ clif_displaymessage(fd, msg_table[36]); // Appearence changed.
+ } else {
+ clif_displaymessage(fd, msg_table[37]); // An invalid number was specified.
+ return -1;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * @hairstyle && @hstyle
+ *------------------------------------------
+ */
+int atcommand_hair_style(const int fd, struct map_session_data* sd, const char* command, const char* message)
+{
+ int hair_style = 0;
+ nullpo_retr(-1, sd);
+
+ memset(atcmd_output, '\0', sizeof(atcmd_output));
+
+ if (!message || !*message || sscanf(message, "%d", &hair_style) < 1) {
+ sprintf(atcmd_output, "Please, enter a hair style (usage: @hairstyle/@hstyle <hair ID: %d-%d>).", MIN_HAIR_STYLE, MAX_HAIR_STYLE);
+ clif_displaymessage(fd, atcmd_output);
+ return -1;
+ }
+
+ if (hair_style >= MIN_HAIR_STYLE && hair_style <= MAX_HAIR_STYLE) {
+ /* Removed because this check is TOO strange. [Skotlex]
+ if (hair_style != 0 && sd->status.sex == 1 && (sd->status.class_ == JOB_ASSASSIN || sd->status.class_ == JOB_ROGUE)) { //???
+ clif_displaymessage(fd, msg_table[35]); // You can't use this command with this class.
+ return -1;
+ } else {
+ */
+ pc_changelook(sd, LOOK_HAIR, hair_style);
+ clif_displaymessage(fd, msg_table[36]); // Appearence changed.
+// }
+ } else {
+ clif_displaymessage(fd, msg_table[37]); // An invalid number was specified.
+ return -1;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * @charhairstyle by [MouseJstr]
+ *------------------------------------------
+ */
+int
+atcommand_charhairstyle(const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ nullpo_retr(-1, sd);
+ return 0;
+}
+
+/*==========================================
+ * @haircolor && @hcolor
+ *------------------------------------------
+ */
+int atcommand_hair_color(const int fd, struct map_session_data* sd, const char* command, const char* message)
+{
+ int hair_color = 0;
+ nullpo_retr(-1, sd);
+
+ memset(atcmd_output, '\0', sizeof(atcmd_output));
+
+ if (!message || !*message || sscanf(message, "%d", &hair_color) < 1) {
+ sprintf(atcmd_output, "Please, enter a hair color (usage: @haircolor/@hcolor <hair color: %d-%d>).", MIN_HAIR_COLOR, MAX_HAIR_COLOR);
+ clif_displaymessage(fd, atcmd_output);
+ return -1;
+ }
+
+ if (hair_color >= MIN_HAIR_COLOR && hair_color <= MAX_HAIR_COLOR) {
+ /* Removed for being such a strange check. [Skotlex]
+ if (hair_color != 0 && sd->status.sex == 1 && (sd->status.class_ == JOB_ASSASSIN || sd->status.class_ == JOB_ROGUE)) {
+ clif_displaymessage(fd, msg_table[35]); // You can't use this command with this class.
+ return -1;
+ } else {
+ */
+ pc_changelook(sd, LOOK_HAIR_COLOR, hair_color);
+ clif_displaymessage(fd, msg_table[36]); // Appearence changed.
+// }
+ } else {
+ clif_displaymessage(fd, msg_table[37]); // An invalid number was specified.
+ return -1;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * @go [city_number or city_name] - Updated by Harbin
+ *------------------------------------------
+ */
+int atcommand_go(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ int i;
+ int town;
+ char map_name[MAP_NAME_LENGTH];
+ int m;
+
+ const struct { char map[MAP_NAME_LENGTH]; int x, y; } data[] = {
+ { MAP_PRONTERA, 156, 191 }, // 0=Prontera
+ { MAP_MORROC, 156, 93 }, // 1=Morroc
+ { MAP_GEFFEN, 119, 59 }, // 2=Geffen
+ { MAP_PAYON, 162, 233 }, // 3=Payon
+ { MAP_ALBERTA, 192, 147 }, // 4=Alberta
+ { MAP_IZLUDE, 128, 114 }, // 5=Izlude
+ { MAP_ALDEBARAN, 140, 131 }, // 6=Al de Baran
+ { MAP_LUTIE, 147, 134 }, // 7=Lutie
+ { MAP_COMODO, 209, 143 }, // 8=Comodo
+ { MAP_YUNO, 157, 51 }, // 9=Yuno
+ { MAP_AMATSU, 198, 84 }, // 10=Amatsu
+ { MAP_GONRYUN, 160, 120 }, // 11=Gon Ryun
+ { MAP_UMBALA, 89, 157 }, // 12=Umbala
+ { MAP_NIFLHEIM, 21, 153 }, // 13=Niflheim
+ { MAP_LOUYANG, 217, 40 }, // 14=Lou Yang
+ { "new_1-1.gat", 53, 111 }, // 15=Training Grounds
+ { MAP_JAIL, 23, 61 }, // 16=Prison
+ { MAP_JAWAII, 249, 127 }, // 17=Jawaii
+ { MAP_AYOTHAYA, 151, 117 }, // 18=Ayothaya
+ { MAP_EINBROCH, 64, 200 }, // 19=Einbroch
+ { MAP_LIGHTHALZEN, 158, 92 }, // 20=Lighthalzen
+ { MAP_EINBECH, 70, 95 }, // 21=Einbech
+ { MAP_HUGEL, 96, 145 }, // 22=Hugel
+ };
+
+ nullpo_retr(-1, sd);
+
+ if(map[sd->bl.m].flag.nogo) {
+ clif_displaymessage(sd->fd,"You can not use @go on this map.");
+ return 0;
+ }
+
+ memset(map_name, '\0', sizeof(map_name));
+ memset(atcmd_output, '\0', sizeof(atcmd_output));
+
+ // get the number
+ town = atoi(message);
+
+ // if no value, display all value
+ if (!message || !*message || sscanf(message, "%15s", map_name) < 1 || town < -3 || town >= (int)(sizeof(data) / sizeof(data[0]))) {
+ clif_displaymessage(fd, msg_table[38]); // Invalid location number or name.
+ clif_displaymessage(fd, msg_table[82]); // Please, use one of this number/name:
+ clif_displaymessage(fd, " 0=Prontera 1=Morroc 2=Geffen");
+ clif_displaymessage(fd, " 3=Payon 4=Alberta 5=Izlude");
+ clif_displaymessage(fd, " 6=Al De Baran 7=Lutie 8=Comodo");
+ clif_displaymessage(fd, " 9=Yuno 10=Amatsu 11=Gon Ryun");
+ clif_displaymessage(fd, " 12=Umbala 13=Niflheim 14=Lou Yang");
+ clif_displaymessage(fd, " 15=Novice Grounds 16=Prison 17=Jawaii");
+ clif_displaymessage(fd, " 18=Ayothaya 19=Einbroch 20=Lighthalzen");
+ clif_displaymessage(fd, " 21=Einbech 22=Hugel");
+ return -1;
+ } else {
+ // get possible name of the city and add .gat if not in the name
+ map_name[MAP_NAME_LENGTH-1] = '\0';
+ for (i = 0; map_name[i]; i++)
+ map_name[i] = tolower(map_name[i]);
+ if (strstr(map_name, ".gat") == NULL && strstr(map_name, ".afm") == NULL && strlen(map_name) < MAP_NAME_LENGTH-4) // 16 - 4 (.gat)
+ strcat(map_name, ".gat");
+ // try to see if it's a name, and not a number (try a lot of possibilities, write errors and abbreviations too)
+ if (strncmp(map_name, "prontera.gat", 3) == 0) { // 3 first characters
+ town = 0;
+ } else if (strncmp(map_name, "morocc.gat", 3) == 0) { // 3 first characters
+ town = 1;
+ } else if (strncmp(map_name, "geffen.gat", 3) == 0) { // 3 first characters
+ town = 2;
+ } else if (strncmp(map_name, "payon.gat", 3) == 0 || // 3 first characters
+ strncmp(map_name, "paion.gat", 3) == 0) { // writing error (3 first characters)
+ town = 3;
+ } else if (strncmp(map_name, "alberta.gat", 3) == 0) { // 3 first characters
+ town = 4;
+ } else if (strncmp(map_name, "izlude.gat", 3) == 0 || // 3 first characters
+ strncmp(map_name, "islude.gat", 3) == 0) { // writing error (3 first characters)
+ town = 5;
+ } else if (strncmp(map_name, "aldebaran.gat", 3) == 0 || // 3 first characters
+ strcmp(map_name, "al.gat") == 0) { // al (de baran)
+ town = 6;
+ } else if (strncmp(map_name, "lutie.gat", 3) == 0 || // name of the city, not name of the map (3 first characters)
+ strcmp(map_name, "christmas.gat") == 0 || // name of the symbol
+ strncmp(map_name, "xmas.gat", 3) == 0 || // 3 first characters
+ strncmp(map_name, "x-mas.gat", 3) == 0) { // writing error (3 first characters)
+ town = 7;
+ } else if (strncmp(map_name, "comodo.gat", 3) == 0) { // 3 first characters
+ town = 8;
+ } else if (strncmp(map_name, "yuno.gat", 3) == 0) { // 3 first characters
+ town = 9;
+ } else if (strncmp(map_name, "amatsu.gat", 3) == 0 || // 3 first characters
+ strncmp(map_name, "ammatsu.gat", 3) == 0) { // writing error (3 first characters)
+ town = 10;
+ } else if (strncmp(map_name, "gonryun.gat", 3) == 0) { // 3 first characters
+ town = 11;
+ } else if (strncmp(map_name, "umbala.gat", 3) == 0) { // 3 first characters
+ town = 12;
+ } else if (strncmp(map_name, "niflheim.gat", 3) == 0) { // 3 first characters
+ town = 13;
+ } else if (strncmp(map_name, "louyang.gat", 3) == 0) { // 3 first characters
+ town = 14;
+ } else if (strncmp(map_name, "new_1-1.gat", 3) == 0 || // 3 first characters (or "newbies")
+ strncmp(map_name, "startpoint.gat", 3) == 0 || // name of the position (3 first characters)
+ strncmp(map_name, "begining.gat", 3) == 0) { // name of the position (3 first characters)
+ town = 15;
+ } else if (strncmp(map_name, "sec_pri.gat", 3) == 0 || // 3 first characters
+ strncmp(map_name, "prison.gat", 3) == 0 || // name of the position (3 first characters)
+ strncmp(map_name, "jails.gat", 3) == 0) { // name of the position
+ town = 16;
+ } else if (strncmp(map_name, "jawaii.gat", 3) == 0 || // 3 first characters
+ strncmp(map_name, "jawai.gat", 3) == 0) { // writing error (3 first characters)
+ town = 17;
+ } else if (strncmp(map_name, "ayothaya.gat", 2) == 0 || // 2 first characters
+ strncmp(map_name, "ayotaya.gat", 2) == 0) { // writing error (2 first characters)
+ town = 18;
+ } else if (strncmp(map_name, "einbroch.gat", 3) == 0 || // 3 first characters
+ strncmp(map_name, "ainbroch.gat", 3) == 0) { // writing error (3 first characters)
+ town = 19;
+ } else if (strncmp(map_name, "lighthalzen.gat", 3) == 0 || // 3 first characters
+ strncmp(map_name, "reichthalzen.gat", 3) == 0) { // 'alternative' name (3 first characters)
+ town = 20;
+ } else if (strncmp(map_name, "einbech.gat", 5) == 0) { // 5 first characters
+ town = 21;
+ } else if (strncmp(map_name, "hugel.gat", 3) == 0) { // 3 first characters
+ town = 22;
+ }
+
+ if (town >= -3 && town <= -1) {
+ if (sd->status.memo_point[-town-1].map) {
+ m = map_mapindex2mapid(sd->status.memo_point[-town-1].map);
+ if (m >= 0 && map[m].flag.nowarpto && battle_config.any_warp_GM_min_level > pc_isGM(sd)) {
+ clif_displaymessage(fd, msg_table[247]);
+ return -1;
+ }
+ if (sd->bl.m >= 0 && map[sd->bl.m].flag.nowarp && battle_config.any_warp_GM_min_level > pc_isGM(sd)) {
+ clif_displaymessage(fd, msg_table[248]);
+ return -1;
+ }
+ if (pc_setpos(sd, sd->status.memo_point[-town-1].map, sd->status.memo_point[-town-1].x, sd->status.memo_point[-town-1].y, 3) == 0) {
+ clif_displaymessage(fd, msg_table[0]); // Warped.
+ } else {
+ clif_displaymessage(fd, msg_table[1]); // Map not found.
+ return -1;
+ }
+ } else {
+ sprintf(atcmd_output, msg_table[164], -town-1); // Your memo point #%d doesn't exist.
+ clif_displaymessage(fd, atcmd_output);
+ return -1;
+ }
+ } else if (town >= 0 && town < (int)(sizeof(data) / sizeof(data[0]))) {
+ m = map_mapname2mapid((char *)data[town].map);
+ if (m >= 0 && map[m].flag.nowarpto && battle_config.any_warp_GM_min_level > pc_isGM(sd)) {
+ clif_displaymessage(fd, msg_table[247]);
+ return -1;
+ }
+ if (sd->bl.m >= 0 && map[sd->bl.m].flag.nowarp && battle_config.any_warp_GM_min_level > pc_isGM(sd)) {
+ clif_displaymessage(fd, msg_table[248]);
+ return -1;
+ }
+ if (pc_setpos(sd, mapindex_name2id((char *)data[town].map), data[town].x, data[town].y, 3) == 0) {
+ clif_displaymessage(fd, msg_table[0]); // Warped.
+ } else {
+ clif_displaymessage(fd, msg_table[1]); // Map not found.
+ return -1;
+ }
+ } else { // if you arrive here, you have an error in town variable when reading of names
+ clif_displaymessage(fd, msg_table[38]); // Invalid location number or name.
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int atcommand_monster(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ char name[NAME_LENGTH];
+ char monster[NAME_LENGTH];
+ int mob_id;
+ int number = 0;
+ int x = 0, y = 0;
+ int count;
+ int i, j, k;
+ int mx, my, range;
+ nullpo_retr(-1, sd);
+
+ memset(name, '\0', sizeof(name));
+ memset(monster, '\0', sizeof(monster));
+ memset(atcmd_output, '\0', sizeof(atcmd_output));
+
+ if (!message || !*message) {
+ clif_displaymessage(fd, msg_table[80]); // Give a display name and monster name/id please.
+ return -1;
+ }
+ if (sscanf(message, "\"%23[^\"]\" %23s %d %d %d", name, monster, &number, &x, &y) > 1 ||
+ sscanf(message, "%23s \"%23[^\"]\" %d %d %d", monster, name, &number, &x, &y) > 1) {
+ //All data can be left as it is.
+ } else if ((count=sscanf(message, "%23s %d %23s %d %d", monster, &number, name, &x, &y)) > 1) {
+ //Here, it is possible name was not given and we are using monster for it.
+ if (count < 3) //Blank mob's name.
+ name[0] = '\0';
+ } else if (sscanf(message, "%23s %23s %d %d %d", name, monster, &number, &x, &y) > 1) {
+ //All data can be left as it is.
+ } else if (sscanf(message, "%23s", monster) > 0) {
+ //As before, name may be already filled.
+ name[0] = '\0';
+ } else {
+ clif_displaymessage(fd, msg_table[80]); // Give a display name and monster name/id please.
+ return -1;
+ }
+
+ if ((mob_id = mobdb_searchname(monster)) == 0) // check name first (to avoid possible name begining by a number)
+ mob_id = mobdb_checkid(atoi(monster));
+
+ if (mob_id == 0) {
+ clif_displaymessage(fd, msg_table[40]); // Invalid monster ID or name.
+ return -1;
+ }
+
+ if (mob_id == MOBID_EMPERIUM) {
+ clif_displaymessage(fd, msg_table[83]); // Cannot spawn emperium.
+ return -1;
+ }
+
+ if (number <= 0)
+ number = 1;
+
+ if (strlen(name) < 1)
+ strcpy(name, "--ja--");
+
+ // If value of atcommand_spawn_quantity_limit directive is greater than or equal to 1 and quantity of monsters is greater than value of the directive
+ if (battle_config.atc_spawn_quantity_limit >= 1 && number > battle_config.atc_spawn_quantity_limit)
+ number = battle_config.atc_spawn_quantity_limit;
+
+ if (battle_config.etc_log)
+ ShowInfo("%s monster='%s' name='%s' id=%d count=%d (%d,%d)\n", command, monster, name, mob_id, number, x, y);
+
+ count = 0;
+ range = (int)sqrt(number) / 2;
+ range = range * 2 + 5; // calculation of an odd number (+ 4 area around)
+ for (i = 0; i < number; i++) {
+ j = 0;
+ k = 0;
+ while(j++ < 8 && k == 0) { // try 8 times to spawn the monster (needed for close area)
+ if (x <= 0)
+ mx = sd->bl.x + (rand() % range - (range / 2));
+ else
+ mx = x;
+ if (y <= 0)
+ my = sd->bl.y + (rand() % range - (range / 2));
+ else
+ my = y;
+ k = mob_once_spawn((struct map_session_data*)sd, "this", mx, my, name, mob_id, 1, "");
+ }
+ count += (k != 0) ? 1 : 0;
+ }
+
+ if (count != 0)
+ if (number == count)
+ clif_displaymessage(fd, msg_table[39]); // All monster summoned!
+ else {
+ sprintf(atcmd_output, msg_table[240], count); // %d monster(s) summoned!
+ clif_displaymessage(fd, atcmd_output);
+ }
+ else {
+ clif_displaymessage(fd, msg_table[40]); // Invalid monster ID or name.
+ return -1;
+ }
+
+ return 0;
+}
+
+// small monster spawning [Valaris]
+int atcommand_monstersmall(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message) {
+ char name[NAME_LENGTH] = "";
+ char monster[NAME_LENGTH] = "";
+ int mob_id = 0;
+ int number = 0;
+ int x = 0;
+ int y = 0;
+ int count;
+ int i;
+
+ nullpo_retr(-1, sd);
+
+ if (!message || !*message) {
+ clif_displaymessage(fd, "Give a monster name/id please.");
+ return -1;
+ }
+
+ if (sscanf(message, "\"%23[^\"]\" %23s %d %d %d", name, monster, &number, &x, &y) < 2 &&
+ sscanf(message, "%23s \"%23[^\"]\" %d %d %d", monster, name, &number, &x, &y) < 2 &&
+ sscanf(message, "%23s %d %23s %d %d", monster, &number, name, &x, &y) < 1) {
+ clif_displaymessage(fd, "Give a monster name/id please.");
+ return -1;
+ }
+
+ // If monster identifier/name argument is a name
+ if ((mob_id = mobdb_searchname(monster)) == 0) // check name first (to avoid possible name begining by a number)
+ mob_id = atoi(monster);
+
+ if (mob_id == 0) {
+ clif_displaymessage(fd, msg_table[40]);
+ return -1;
+ }
+
+ if (mob_id == MOBID_EMPERIUM) {
+ clif_displaymessage(fd, msg_table[83]);
+ return -1;
+ }
+
+ if (mobdb_checkid(mob_id) == 0) {
+ clif_displaymessage(fd, "Invalid monster ID"); // Invalid Monster ID.
+ return -1;
+ }
+
+ if (number <= 0)
+ number = 1;
+
+ if (strlen(name) < 1)
+ strcpy(name, "--ja--");
+
+ // If value of atcommand_spawn_quantity_limit directive is greater than or equal to 1 and quantity of monsters is greater than value of the directive
+ if (battle_config.atc_spawn_quantity_limit >= 1 && number > battle_config.atc_spawn_quantity_limit)
+ number = battle_config.atc_spawn_quantity_limit;
+
+ count = 0;
+ for (i = 0; i < number; i++) {
+ int mx, my;
+ if (x <= 0)
+ mx = sd->bl.x + (rand() % 11 - 5);
+ else
+ mx = x;
+ if (y <= 0)
+ my = sd->bl.y + (rand() % 11 - 5);
+ else
+ my = y;
+ count += (mob_once_spawn((struct map_session_data*)sd, "this", mx, my, name, mob_id+MAX_MOB_DB, 1, "") != 0) ? 1 : 0;
+ }
+
+ if (count != 0)
+ clif_displaymessage(fd, msg_table[39]); // Monster Summoned!!
+ else
+ clif_displaymessage(fd, msg_table[40]); // Invalid Monster ID.
+
+ return 0;
+}
+// big monster spawning [Valaris]
+int atcommand_monsterbig(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message) {
+ char name[NAME_LENGTH] = "";
+ char monster[NAME_LENGTH] = "";
+ int mob_id = 0;
+ int number = 0;
+ int x = 0;
+ int y = 0;
+ int count;
+ int i;
+
+ nullpo_retr(-1, sd);
+
+ if (!message || !*message) {
+ clif_displaymessage(fd, "Give a monster name/id please.");
+ return -1;
+ }
+
+ if (sscanf(message, "\"%23[^\"]\" %23s %d %d %d", name, monster, &number, &x, &y) < 2 &&
+ sscanf(message, "%23s \"%23[^\"]\" %d %d %d", monster, name, &number, &x, &y) < 2 &&
+ sscanf(message, "%23s %d %23s %d %d", monster, &number, name, &x, &y) < 1) {
+ clif_displaymessage(fd, "Give a monster name/id please.");
+ return -1;
+ }
+
+ // If monster identifier/name argument is a name
+ if ((mob_id = mobdb_searchname(monster)) == 0) // check name first (to avoid possible name begining by a number)
+ mob_id = atoi(monster);
+
+ if (mob_id == 0) {
+ clif_displaymessage(fd, msg_table[40]);
+ return -1;
+ }
+
+ if (mob_id == MOBID_EMPERIUM) {
+ clif_displaymessage(fd, msg_table[83]);
+ return -1;
+ }
+
+ if (mobdb_checkid(mob_id) == 0) {
+ clif_displaymessage(fd, "Invalid monster ID"); // Invalid Monster ID.
+ return -1;
+ }
+
+ if (number <= 0)
+ number = 1;
+
+ if (strlen(name) < 1)
+ strcpy(name, "--ja--");
+
+ // If value of atcommand_spawn_quantity_limit directive is greater than or equal to 1 and quantity of monsters is greater than value of the directive
+ if (battle_config.atc_spawn_quantity_limit >= 1 && number > battle_config.atc_spawn_quantity_limit)
+ number = battle_config.atc_spawn_quantity_limit;
+
+ count = 0;
+ for (i = 0; i < number; i++) {
+ int mx, my;
+ if (x <= 0)
+ mx = sd->bl.x + (rand() % 11 - 5);
+ else
+ mx = x;
+ if (y <= 0)
+ my = sd->bl.y + (rand() % 11 - 5);
+ else
+ my = y;
+ count += (mob_once_spawn((struct map_session_data*)sd, "this", mx, my, name, mob_id+2*MAX_MOB_DB, 1, "") != 0) ? 1 : 0;
+ }
+
+ if (count != 0)
+ clif_displaymessage(fd, msg_table[39]); // Monster Summoned!!
+ else
+ clif_displaymessage(fd, msg_table[40]); // Invalid Monster ID.
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+static int atkillmonster_sub(struct block_list *bl, va_list ap) {
+ struct mob_data *md;
+ int flag;
+
+ nullpo_retr(0, ap);
+ nullpo_retr(0, md=(struct mob_data *)bl);
+ flag = va_arg(ap, int);
+
+ if (flag)
+ mob_damage(NULL, md, md->hp, 2);
+ else
+ mob_delete(md);
+
+ return 0;
+}
+void atcommand_killmonster_sub(
+ const int fd, struct map_session_data* sd, const char* message,
+ const int drop)
+{
+ int map_id;
+ char map_name[MAP_NAME_LENGTH];
+
+ if (!sd) return;
+
+ memset(map_name, '\0', sizeof(map_name));
+
+ if (!message || !*message || sscanf(message, "%15s", map_name) < 1)
+ map_id = sd->bl.m;
+ else {
+ if (strstr(map_name, ".gat") == NULL && strstr(map_name, ".afm") == NULL && strlen(map_name) < MAP_NAME_LENGTH-4) // 16 - 4 (.gat)
+ strcat(map_name, ".gat");
+ if ((map_id = map_mapname2mapid(map_name)) < 0)
+ map_id = sd->bl.m;
+ }
+
+ map_foreachinmap(atkillmonster_sub, map_id, BL_MOB, drop);
+
+ clif_displaymessage(fd, msg_table[165]); // All monsters killed!
+
+ return;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int atcommand_killmonster(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ if (!sd) return 0;
+ atcommand_killmonster_sub(fd, sd, message, 1);
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int atcommand_killmonster2(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ if (!sd) return 0;
+ atcommand_killmonster_sub(fd, sd, message, 0);
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int atcommand_refine(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ int i, position = 0, refine = 0, current_position, final_refine;
+ int count;
+ nullpo_retr(-1, sd);
+
+ memset(atcmd_output, '\0', sizeof(atcmd_output));
+
+ if (!message || !*message || sscanf(message, "%d %d", &position, &refine) < 2) {
+ clif_displaymessage(fd, "Please, enter a position and a amount (usage: @refine <equip position> <+/- amount>).");
+ return -1;
+ }
+
+ if (refine < -10)
+ refine = -10;
+ else if (refine > 10)
+ refine = 10;
+ else if (refine == 0)
+ refine = 1;
+
+ count = 0;
+ for (i = 0; i < MAX_INVENTORY; i++) {
+ if (sd->status.inventory[i].nameid && // 該当個所の装備を精錬する
+ (sd->status.inventory[i].equip & position ||
+ (sd->status.inventory[i].equip && !position))) {
+ final_refine = sd->status.inventory[i].refine + refine;
+ if (final_refine > 10)
+ final_refine = 10;
+ else if (final_refine < 0)
+ final_refine = 0;
+ if (sd->status.inventory[i].refine != final_refine) {
+ sd->status.inventory[i].refine = final_refine;
+ current_position = sd->status.inventory[i].equip;
+ pc_unequipitem(sd, i, 3);
+ clif_refine(fd, sd, 0, i, sd->status.inventory[i].refine);
+ clif_delitem(sd, i, 1);
+ clif_additem(sd, i, 1, 0);
+ pc_equipitem(sd, i, current_position);
+ clif_misceffect((struct block_list*)&sd->bl, 3);
+ count++;
+ }
+ }
+ }
+
+ if (count == 0)
+ clif_displaymessage(fd, msg_table[166]); // No item has been refined!
+ else if (count == 1)
+ clif_displaymessage(fd, msg_table[167]); // 1 item has been refined!
+ else {
+ sprintf(atcmd_output, msg_table[168], count); // %d items have been refined!
+ clif_displaymessage(fd, atcmd_output);
+ }
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int atcommand_produce(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ char item_name[100];
+ int item_id, attribute = 0, star = 0;
+ int flag = 0;
+ struct item_data *item_data;
+ struct item tmp_item;
+ nullpo_retr(-1, sd);
+
+ memset(atcmd_output, '\0', sizeof(atcmd_output));
+ memset(item_name, '\0', sizeof(item_name));
+
+ if (!message || !*message || sscanf(message, "%99s %d %d", item_name, &attribute, &star) < 1) {
+ clif_displaymessage(fd, "Please, enter at least an item name/id (usage: @produce <equip name or equip ID> <element> <# of very's>).");
+ return -1;
+ }
+
+ item_id = 0;
+ if ((item_data = itemdb_searchname(item_name)) != NULL ||
+ (item_data = itemdb_exists(atoi(item_name))) != NULL)
+ item_id = item_data->nameid;
+
+ if (itemdb_exists(item_id) &&
+ (item_id <= 500 || item_id > 1099) &&
+ (item_id < 4001 || item_id > 4148) &&
+ (item_id < 7001 || item_id > 10019) &&
+ itemdb_isequip(item_id)) {
+ if (attribute < MIN_ATTRIBUTE || attribute > MAX_ATTRIBUTE)
+ attribute = ATTRIBUTE_NORMAL;
+ if (star < MIN_STAR || star > MAX_STAR)
+ star = 0;
+ memset(&tmp_item, 0, sizeof tmp_item);
+ tmp_item.nameid = item_id;
+ tmp_item.amount = 1;
+ tmp_item.identify = 1;
+ tmp_item.card[0] = 0x00ff;
+ tmp_item.card[1] = ((star * 5) << 8) + attribute;
+ tmp_item.card[2] = GetWord(sd->char_id, 0);
+ tmp_item.card[3] = GetWord(sd->char_id, 1);
+ clif_produceeffect(sd, 0, item_id); // 製造エフェクトパケット
+ clif_misceffect(&sd->bl, 3); // 他人にも成功を通知
+
+ //Logs (A)dmins items [Lupus]
+ if(log_config.pick > 0 ) {
+ log_pick(sd, "A", 0, tmp_item.nameid, 1, &tmp_item);
+ }
+ //Logs
+
+ if ((flag = pc_additem(sd, &tmp_item, 1)))
+ clif_additem(sd, 0, 0, flag);
+ } else {
+ if (battle_config.error_log)
+ ShowError("@produce NOT WEAPON [%d]\n", item_id);
+ if (item_id != 0 && itemdb_exists(item_id))
+ sprintf(atcmd_output, msg_table[169], item_id, item_data->name); // This item (%d: '%s') is not an equipment.
+ else
+ sprintf(atcmd_output, msg_table[170]); // This item is not an equipment.
+ clif_displaymessage(fd, atcmd_output);
+ return -1;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * Sub-function to display actual memo points
+ *------------------------------------------
+ */
+void atcommand_memo_sub(struct map_session_data* sd) {
+ int i;
+
+ if (!sd) return;
+
+ memset(atcmd_output, '\0', sizeof(atcmd_output));
+
+ clif_displaymessage(sd->fd, "Your actual memo positions are (except respawn point):");
+ for (i = MIN_PORTAL_MEMO; i <= MAX_PORTAL_MEMO; i++) {
+ if (sd->status.memo_point[i].map)
+ sprintf(atcmd_output, "%d - %s (%d,%d)", i, mapindex_id2name(sd->status.memo_point[i].map), sd->status.memo_point[i].x, sd->status.memo_point[i].y);
+ else
+ sprintf(atcmd_output, msg_table[171], i); // %d - void
+ clif_displaymessage(sd->fd, atcmd_output);
+ }
+
+ return;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int atcommand_memo(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ int position = 0;
+ nullpo_retr(-1, sd);
+
+ memset(atcmd_output, '\0', sizeof(atcmd_output));
+
+ if (!message || !*message || sscanf(message, "%d", &position) < 1)
+ atcommand_memo_sub(sd);
+ else {
+ if (position >= MIN_PORTAL_MEMO && position <= MAX_PORTAL_MEMO) {
+ if (sd->bl.m >= 0 && (map[sd->bl.m].flag.nowarpto || map[sd->bl.m].flag.nomemo) && battle_config.any_warp_GM_min_level > pc_isGM(sd)) {
+ clif_displaymessage(fd, msg_table[253]);
+ return -1;
+ }
+ if (sd->status.memo_point[position].map) {
+ sprintf(atcmd_output, msg_table[172], position, mapindex_id2name(sd->status.memo_point[position].map), sd->status.memo_point[position].x, sd->status.memo_point[position].y); // You replace previous memo position %d - %s (%d,%d).
+ clif_displaymessage(fd, atcmd_output);
+ }
+ sd->status.memo_point[position].map = map[sd->bl.m].index;
+ sd->status.memo_point[position].x = sd->bl.x;
+ sd->status.memo_point[position].y = sd->bl.y;
+ clif_skill_memo(sd, 0);
+ if (pc_checkskill(sd, AL_WARP) <= (position + 1))
+ clif_displaymessage(fd, msg_table[173]); // Note: you don't have the 'Warp' skill level to use it.
+ atcommand_memo_sub(sd);
+ } else {
+ sprintf(atcmd_output, "Please, enter a valid position (usage: @memo <memo_position:%d-%d>).", MIN_PORTAL_MEMO, MAX_PORTAL_MEMO);
+ clif_displaymessage(fd, atcmd_output);
+ atcommand_memo_sub(sd);
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int atcommand_gat(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ int y;
+ nullpo_retr(-1, sd);
+
+ memset(atcmd_output, '\0', sizeof(atcmd_output));
+
+ for (y = 2; y >= -2; y--) {
+ sprintf(atcmd_output, "%s (x= %d, y= %d) %02X %02X %02X %02X %02X",
+ map[sd->bl.m].name, sd->bl.x - 2, sd->bl.y + y,
+ map_getcell(sd->bl.m, sd->bl.x - 2, sd->bl.y + y, CELL_GETTYPE),
+ map_getcell(sd->bl.m, sd->bl.x - 1, sd->bl.y + y, CELL_GETTYPE),
+ map_getcell(sd->bl.m, sd->bl.x, sd->bl.y + y, CELL_GETTYPE),
+ map_getcell(sd->bl.m, sd->bl.x + 1, sd->bl.y + y, CELL_GETTYPE),
+ map_getcell(sd->bl.m, sd->bl.x + 2, sd->bl.y + y, CELL_GETTYPE));
+
+ clif_displaymessage(fd, atcmd_output);
+ }
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int atcommand_packet(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ static int packet_mode = 0;
+ int i, x = 0, y = 0;
+ nullpo_retr(-1, sd);
+
+ if (strstr(command, "packetmode")) {
+ packet_mode = atoi(message);
+ clif_displaymessage(fd, "Packet mode changed.");
+ return 0;
+ }
+
+ if (!message || !*message || (i = sscanf(message, "%d %d", &x, &y)) < 1) {
+ clif_displaymessage(fd, "Please, enter a status type/flag (usage: @packet <status type> <flag>).");
+ return -1;
+ }
+ if (i == 1) y = 1;
+
+ switch (packet_mode)
+ {
+ case 0:
+ clif_status_change(&sd->bl, x, y);
+ break;
+ case 1:
+ sd->status.skill[sd->cloneskill_id].id=0;
+ sd->status.skill[sd->cloneskill_id].lv=0;
+ sd->status.skill[sd->cloneskill_id].flag=0;
+ sd->cloneskill_id = x;
+ sd->status.skill[x].id = x;
+ sd->status.skill[x].lv = skill_get_max(x);
+ sd->status.skill[x].flag = 13;//cloneskill flag
+ clif_skillinfoblock(sd);
+ break;
+ case 2:
+ clif_skill_nodamage(&sd->bl,&sd->bl,x,y,1);
+ case 3:
+ clif_skill_poseffect(&sd->bl,x,y,sd->bl.x,sd->bl.y,gettick());
+ default:
+ break;
+ //added later
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * @waterlevel [Skotlex]
+ *------------------------------------------
+ */
+
+int atcommand_waterlevel(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ int newlevel;
+ if (!message || !*message || sscanf(message, "%d", &newlevel) < 1) {
+ sprintf(atcmd_output, "%s's current water level: %d", map[sd->bl.m].name, map_waterheight(map[sd->bl.m].name));
+ clif_displaymessage(fd, atcmd_output);
+ return 0;
+ }
+
+ if (map_setwaterheight(sd->bl.m, map[sd->bl.m].name, newlevel)) {
+ if (newlevel > 0)
+ sprintf(atcmd_output, "%s's water level changed to: %d", map[sd->bl.m].name, newlevel);
+ else
+ sprintf(atcmd_output, "Removed %s's water level information.", map[sd->bl.m].name);
+ clif_displaymessage(fd, atcmd_output);
+ } else {
+ sprintf(atcmd_output, "Failed to change %s's water level.", map[sd->bl.m].name);
+ clif_displaymessage(fd, atcmd_output);
+ }
+ return 0;
+}
+
+/*==========================================
+ * @stpoint (Rewritten by [Yor])
+ *------------------------------------------
+ */
+int atcommand_statuspoint(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ int point, new_status_point;
+
+ if (!message || !*message || (point = atoi(message)) == 0) {
+ clif_displaymessage(fd, "Please, enter a number (usage: @stpoint <number of points>).");
+ return -1;
+ }
+
+ new_status_point = (int)sd->status.status_point + point;
+ if (point > 0 && (point > 0x7FFF || new_status_point > 0x7FFF)) // fix positiv overflow
+ new_status_point = 0x7FFF;
+ else if (point < 0 && (point < -0x7FFF || new_status_point < 0)) // fix negativ overflow
+ new_status_point = 0;
+
+ if (new_status_point != (int)sd->status.status_point) {
+ sd->status.status_point = (short)new_status_point;
+ clif_updatestatus(sd, SP_STATUSPOINT);
+ clif_displaymessage(fd, msg_table[174]); // Number of status points changed!
+ } else {
+ if (point < 0)
+ clif_displaymessage(fd, msg_table[41]); // Impossible to decrease the number/value.
+ else
+ clif_displaymessage(fd, msg_table[149]); // Impossible to increase the number/value.
+ return -1;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * @skpoint (Rewritten by [Yor])
+ *------------------------------------------
+ */
+int atcommand_skillpoint(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ int point, new_skill_point;
+ nullpo_retr(-1, sd);
+
+ if (!message || !*message || (point = atoi(message)) == 0) {
+ clif_displaymessage(fd, "Please, enter a number (usage: @skpoint <number of points>).");
+ return -1;
+ }
+
+ new_skill_point = (int)sd->status.skill_point + point;
+ if (point > 0 && (point > 0x7FFF || new_skill_point > 0x7FFF)) // fix positiv overflow
+ new_skill_point = 0x7FFF;
+ else if (point < 0 && (point < -0x7FFF || new_skill_point < 0)) // fix negativ overflow
+ new_skill_point = 0;
+
+ if (new_skill_point != (int)sd->status.skill_point) {
+ sd->status.skill_point = (short)new_skill_point;
+ clif_updatestatus(sd, SP_SKILLPOINT);
+ clif_displaymessage(fd, msg_table[175]); // Number of skill points changed!
+ } else {
+ if (point < 0)
+ clif_displaymessage(fd, msg_table[41]); // Impossible to decrease the number/value.
+ else
+ clif_displaymessage(fd, msg_table[149]); // Impossible to increase the number/value.
+ return -1;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * @zeny (Rewritten by [Yor])
+ *------------------------------------------
+ */
+int atcommand_zeny(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ int zeny, new_zeny;
+ nullpo_retr(-1, sd);
+
+ if (!message || !*message || (zeny = atoi(message)) == 0) {
+ clif_displaymessage(fd, "Please, enter an amount (usage: @zeny <amount>).");
+ return -1;
+ }
+
+ new_zeny = sd->status.zeny + zeny;
+ if (zeny > 0 && (zeny > MAX_ZENY || new_zeny > MAX_ZENY)) // fix positiv overflow
+ new_zeny = MAX_ZENY;
+ else if (zeny < 0 && (zeny < -MAX_ZENY || new_zeny < 0)) // fix negativ overflow
+ new_zeny = 0;
+
+ if (new_zeny != sd->status.zeny) {
+ sd->status.zeny = new_zeny;
+ clif_updatestatus(sd, SP_ZENY);
+ clif_displaymessage(fd, msg_table[176]); // Number of zenys changed!
+ } else {
+ if (zeny < 0)
+ clif_displaymessage(fd, msg_table[41]); // Impossible to decrease the number/value.
+ else
+ clif_displaymessage(fd, msg_table[149]); // Impossible to increase the number/value.
+ return -1;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int atcommand_param(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ int i, index, value = 0, new_value;
+ const char* param[] = { "@str", "@agi", "@vit", "@int", "@dex", "@luk", NULL };
+ short* status[] = {
+ &sd->status.str, &sd->status.agi, &sd->status.vit,
+ &sd->status.int_, &sd->status.dex, &sd->status.luk
+ };
+ nullpo_retr(-1, sd);
+
+ memset(atcmd_output, '\0', sizeof(atcmd_output));
+
+ if (!message || !*message || sscanf(message, "%d", &value) < 1 || value == 0) {
+ sprintf(atcmd_output, "Please, enter a valid value (usage: @str,@agi,@vit,@int,@dex,@luk <+/-adjustement>).");
+ clif_displaymessage(fd, atcmd_output);
+ return -1;
+ }
+
+ index = -1;
+ for (i = 0; param[i] != NULL; i++) {
+ if (strcmpi(command, param[i]) == 0) {
+ index = i;
+ break;
+ }
+ }
+ if (index < 0 || index > MAX_STATUS_TYPE) { // normaly impossible...
+ sprintf(atcmd_output, "Please, enter a valid value (usage: @str,@agi,@vit,@int,@dex,@luk <+/-adjustement>).");
+ clif_displaymessage(fd, atcmd_output);
+ return -1;
+ }
+
+ new_value = (int)*status[index] + value;
+ if (value > 0 && (value > pc_maxparameter(sd) || new_value > pc_maxparameter(sd))) // fix positiv overflow
+ new_value = pc_maxparameter(sd);
+ else if (value < 0 && (value < -(int)pc_maxparameter(sd) || new_value < 1)) // fix negativ overflow
+ new_value = 1;
+
+ if (new_value != (int)*status[index]) {
+ *status[index] = new_value;
+ clif_updatestatus(sd, SP_STR + index);
+ clif_updatestatus(sd, SP_USTR + index);
+ status_calc_pc(sd, 0);
+ clif_displaymessage(fd, msg_table[42]); // Stat changed.
+ } else {
+ if (value < 0)
+ clif_displaymessage(fd, msg_table[41]); // Impossible to decrease the number/value.
+ else
+ clif_displaymessage(fd, msg_table[149]); // Impossible to increase the number/value.
+ return -1;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+//** Stat all by fritz (rewritten by [Yor])
+int atcommand_stat_all(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ int index, count, value = 0, new_value;
+ short* status[] = {
+ &sd->status.str, &sd->status.agi, &sd->status.vit,
+ &sd->status.int_, &sd->status.dex, &sd->status.luk
+ };
+ nullpo_retr(-1, sd);
+
+ if (!message || !*message || sscanf(message, "%d", &value) < 1 || value == 0)
+ value = pc_maxparameter(sd);
+
+ count = 0;
+ for (index = 0; index < (int)(sizeof(status) / sizeof(status[0])); index++) {
+
+ new_value = (int)*status[index] + value;
+ if (value > 0 && (value > pc_maxparameter(sd) || new_value > pc_maxparameter(sd))) // fix positiv overflow
+ new_value = pc_maxparameter(sd);
+ else if (value < 0 && (value < -(int)pc_maxparameter(sd) || new_value < 1)) // fix negative overflow
+ new_value = 1;
+
+ if (new_value != (int)*status[index]) {
+ *status[index] = new_value;
+ clif_updatestatus(sd, SP_STR + index);
+ clif_updatestatus(sd, SP_USTR + index);
+ status_calc_pc(sd, 0);
+ count++;
+ }
+ }
+
+ if (count > 0) // if at least 1 stat modified
+ clif_displaymessage(fd, msg_table[84]); // All stats changed!
+ else {
+ if (value < 0)
+ clif_displaymessage(fd, msg_table[177]); // Impossible to decrease a stat.
+ else
+ clif_displaymessage(fd, msg_table[178]); // Impossible to increase a stat.
+ return -1;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int atcommand_guildlevelup(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ int level = 0;
+ short added_level;
+ struct guild *guild_info;
+ nullpo_retr(-1, sd);
+
+ if (!message || !*message || sscanf(message, "%d", &level) < 1 || level == 0) {
+ clif_displaymessage(fd, "Please, enter a valid level (usage: @guildlvup/@guildlvlup <# of levels>).");
+ return -1;
+ }
+
+ if (sd->status.guild_id <= 0 || (guild_info = guild_search(sd->status.guild_id)) == NULL) {
+ clif_displaymessage(fd, msg_table[43]); // You're not in a guild.
+ return -1;
+ }
+ if (strcmp(sd->status.name, guild_info->master) != 0) {
+ clif_displaymessage(fd, msg_table[44]); // You're not the master of your guild.
+ return -1;
+ }
+
+ added_level = (short)level;
+ if (level > 0 && (level > MAX_GUILDLEVEL || added_level > ((short)MAX_GUILDLEVEL - guild_info->guild_lv))) // fix positiv overflow
+ added_level = (short)MAX_GUILDLEVEL - guild_info->guild_lv;
+ else if (level < 0 && (level < -MAX_GUILDLEVEL || added_level < (1 - guild_info->guild_lv))) // fix negativ overflow
+ added_level = 1 - guild_info->guild_lv;
+
+ if (added_level != 0) {
+ intif_guild_change_basicinfo(guild_info->guild_id, GBI_GUILDLV, &added_level, 2);
+ clif_displaymessage(fd, msg_table[179]); // Guild level changed.
+ } else {
+ clif_displaymessage(fd, msg_table[45]); // Guild level change failed.
+ return -1;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int atcommand_makeegg(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ struct item_data *item_data;
+ int id, pet_id;
+ nullpo_retr(-1, sd);
+
+ if (!message || !*message) {
+ clif_displaymessage(fd, "Please, enter a monter/egg name/id (usage: @makeegg <pet_id>).");
+ return -1;
+ }
+
+ if ((item_data = itemdb_searchname(message)) != NULL) // for egg name
+ id = item_data->nameid;
+ else if ((id = mobdb_searchname(message)) == 0) // for monster name
+ id = atoi(message);
+
+ pet_id = search_petDB_index(id, PET_CLASS);
+ if (pet_id < 0)
+ pet_id = search_petDB_index(id, PET_EGG);
+ if (pet_id >= 0) {
+ sd->catch_target_class = pet_db[pet_id].class_;
+ intif_create_pet(
+ sd->status.account_id, sd->status.char_id,
+ (short)pet_db[pet_id].class_, (short)mob_db(pet_db[pet_id].class_)->lv,
+ (short)pet_db[pet_id].EggID, 0, (short)pet_db[pet_id].intimate,
+ 100, 0, 1, pet_db[pet_id].jname);
+ } else {
+ clif_displaymessage(fd, msg_table[180]); // The monter/egg name/id doesn't exist.
+ return -1;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int atcommand_hatch(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ nullpo_retr(-1, sd);
+ if (sd->status.pet_id <= 0)
+ clif_sendegg(sd);
+ else {
+ clif_displaymessage(fd, msg_table[181]); // You already have a pet.
+ return -1;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int atcommand_petfriendly(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ int friendly;
+ int t;
+ nullpo_retr(-1, sd);
+
+ if (!message || !*message || (friendly = atoi(message)) < 0) {
+ clif_displaymessage(fd, "Please, enter a valid value (usage: @petfriendly <0-1000>).");
+ return -1;
+ }
+
+ if (sd->status.pet_id > 0 && sd->pd) {
+ if (friendly >= 0 && friendly <= 1000) {
+ if (friendly != sd->pet.intimate) {
+ t = sd->pet.intimate;
+ sd->pet.intimate = friendly;
+ clif_send_petstatus(sd);
+ if (battle_config.pet_status_support) {
+ if ((sd->pet.intimate > 0 && t <= 0) ||
+ (sd->pet.intimate <= 0 && t > 0)) {
+ if (sd->bl.prev != NULL)
+ status_calc_pc(sd, 0);
+ else
+ status_calc_pc(sd, 2);
+ }
+ }
+ clif_displaymessage(fd, msg_table[182]); // Pet friendly value changed!
+ } else {
+ clif_displaymessage(fd, msg_table[183]); // Pet friendly is already the good value.
+ return -1;
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[37]); // An invalid number was specified.
+ return -1;
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[184]); // Sorry, but you have no pet.
+ return -1;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int atcommand_pethungry(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ int hungry;
+ nullpo_retr(-1, sd);
+
+ if (!message || !*message || (hungry = atoi(message)) < 0) {
+ clif_displaymessage(fd, "Please, enter a valid number (usage: @pethungry <0-100>).");
+ return -1;
+ }
+
+ if (sd->status.pet_id > 0 && sd->pd) {
+ if (hungry >= 0 && hungry <= 100) {
+ if (hungry != sd->pet.hungry) {
+ sd->pet.hungry = hungry;
+ clif_send_petstatus(sd);
+ clif_displaymessage(fd, msg_table[185]); // Pet hungry value changed!
+ } else {
+ clif_displaymessage(fd, msg_table[186]); // Pet hungry is already the good value.
+ return -1;
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[37]); // An invalid number was specified.
+ return -1;
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[184]); // Sorry, but you have no pet.
+ return -1;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int atcommand_petrename(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ nullpo_retr(-1, sd);
+ if (sd->status.pet_id > 0 && sd->pd) {
+ if (sd->pet.rename_flag != 0) {
+ sd->pet.rename_flag = 0;
+ intif_save_petdata(sd->status.account_id, &sd->pet);
+ clif_send_petstatus(sd);
+ clif_displaymessage(fd, msg_table[187]); // You can now rename your pet.
+ } else {
+ clif_displaymessage(fd, msg_table[188]); // You can already rename your pet.
+ return -1;
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[184]); // Sorry, but you have no pet.
+ return -1;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int
+atcommand_recall(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ struct map_session_data *pl_sd = NULL;
+
+ nullpo_retr(-1, sd);
+
+ if (!message || !*message || sscanf(message, "%23[^\n]", atcmd_player_name) < 1) {
+ clif_displaymessage(fd, "Please, enter a player name (usage: @recall <char name>).");
+ return -1;
+ }
+
+ memset(atcmd_player_name, '\0', sizeof atcmd_player_name);
+ if(sscanf(message, "%23[^\n]", atcmd_player_name) < 1)
+ return -1;
+ if(strncmp(sd->status.name,atcmd_player_name,NAME_LENGTH)==0)
+ return -1;
+
+ if ((pl_sd = map_nick2sd(atcmd_player_name)) != NULL) {
+ if (pc_isGM(sd) >= pc_isGM(pl_sd)) { // you can recall only lower or same level
+ if (sd->bl.m >= 0 && map[sd->bl.m].flag.nowarpto && battle_config.any_warp_GM_min_level > pc_isGM(sd)) {
+ clif_displaymessage(fd, "You are not authorised to warp somenone to your actual map.");
+ return -1;
+ }
+ if (pl_sd->bl.m >= 0 && map[pl_sd->bl.m].flag.nowarp && battle_config.any_warp_GM_min_level > pc_isGM(sd)) {
+ clif_displaymessage(fd, "You are not authorized to warp this player from its actual map.");
+ return -1;
+ }
+ pc_setpos(pl_sd, sd->mapindex, sd->bl.x, sd->bl.y, 2);
+ sprintf(atcmd_output, msg_table[46], atcmd_player_name); // %s recalled!
+ clif_displaymessage(fd, atcmd_output);
+ } else {
+ clif_displaymessage(fd, msg_table[81]); // Your GM level don't authorise you to do this action on this player.
+ return -1;
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[3]); // Character not found.
+ return -1;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int atcommand_revive(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ struct map_session_data *pl_sd;
+ nullpo_retr(-1, sd);
+
+ memset(atcmd_player_name, '\0', sizeof(atcmd_player_name));
+
+ if (!message || !*message || sscanf(message, "%23[^\n]", atcmd_player_name) < 1) {
+ clif_displaymessage(fd, "Please, enter a player name (usage: @revive <char name>).");
+ return -1;
+ }
+
+ if ((pl_sd = map_nick2sd(atcmd_player_name)) != NULL) {
+ if (pc_isdead(pl_sd)) {
+ pl_sd->status.hp = pl_sd->status.max_hp;
+ clif_skill_nodamage(&sd->bl,&sd->bl,ALL_RESURRECTION,4,1);
+ pc_setstand(pl_sd);
+ if (battle_config.pc_invincible_time > 0)
+ pc_setinvincibletimer(pl_sd, battle_config.pc_invincible_time);
+ clif_updatestatus(pl_sd, SP_HP);
+ clif_updatestatus(pl_sd, SP_SP);
+ clif_resurrection(&pl_sd->bl, 1);
+ clif_displaymessage(fd, msg_table[51]); // Character revived.
+ return 0;
+ }
+ return -1;
+ } else {
+ clif_displaymessage(fd, msg_table[3]); // Character not found.
+ return -1;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * charblock command (usage: charblock <player_name>)
+ * This command do a definitiv ban on a player
+ *------------------------------------------
+ */
+int atcommand_char_block(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ nullpo_retr(-1, sd);
+
+ memset(atcmd_player_name, '\0', sizeof(atcmd_player_name));
+
+ if (!message || !*message || sscanf(message, "%99[^\n]", atcmd_player_name) < 1) {
+ clif_displaymessage(fd, "Please, enter a player name (usage: @charblock/@block <name>).");
+ return -1;
+ }
+
+ // check player name
+ if (strlen(atcmd_player_name) < 4) {
+ clif_displaymessage(fd, msg_table[86]); // Sorry, but a player name have at least 4 characters.
+ return -1;
+ } else if (strlen(atcmd_player_name) > 23) {
+ clif_displaymessage(fd, msg_table[87]); // Sorry, but a player name have 23 characters maximum.
+ return -1;
+ } else {
+ chrif_char_ask_name(sd->status.account_id, atcmd_player_name, 1, 0, 0, 0, 0, 0, 0); // type: 1 - block
+ clif_displaymessage(fd, msg_table[88]); // Character name sends to char-server to ask it.
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * charban command (usage: charban <time> <player_name>)
+ * This command do a limited ban on a player
+ * Time is done as follows:
+ * Adjustment value (-1, 1, +1, etc...)
+ * Modified element:
+ * a or y: year
+ * m: month
+ * j or d: day
+ * h: hour
+ * mn: minute
+ * s: second
+ * <example> @ban +1m-2mn1s-6y test_player
+ * this example adds 1 month and 1 second, and substracts 2 minutes and 6 years at the same time.
+ *------------------------------------------
+ */
+int atcommand_char_ban(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ char * modif_p;
+ int year, month, day, hour, minute, second, value;
+ nullpo_retr(-1, sd);
+
+ memset(atcmd_output, '\0', sizeof(atcmd_output));
+ memset(atcmd_player_name, '\0', sizeof(atcmd_player_name));
+
+ if (!message || !*message || sscanf(message, "%s %99[^\n]", atcmd_output, atcmd_player_name) < 2) {
+ clif_displaymessage(fd, "Please, enter ban time and a player name (usage: @charban/@ban/@banish/@charbanish <time> <name>).");
+ return -1;
+ }
+
+ atcmd_output[sizeof(atcmd_output)-1] = '\0';
+
+ modif_p = atcmd_output;
+ year = month = day = hour = minute = second = 0;
+ while (modif_p[0] != '\0') {
+ value = atoi(modif_p);
+ if (value == 0)
+ modif_p++;
+ else {
+ if (modif_p[0] == '-' || modif_p[0] == '+')
+ modif_p++;
+ while (modif_p[0] >= '0' && modif_p[0] <= '9')
+ modif_p++;
+ if (modif_p[0] == 's') {
+ second = value;
+ modif_p++;
+ } else if (modif_p[0] == 'm' && modif_p[1] == 'n') {
+ minute = value;
+ modif_p = modif_p + 2;
+ } else if (modif_p[0] == 'h') {
+ hour = value;
+ modif_p++;
+ } else if (modif_p[0] == 'd' || modif_p[0] == 'j') {
+ day = value;
+ modif_p++;
+ } else if (modif_p[0] == 'm') {
+ month = value;
+ modif_p++;
+ } else if (modif_p[0] == 'y' || modif_p[0] == 'a') {
+ year = value;
+ modif_p++;
+ } else if (modif_p[0] != '\0') {
+ modif_p++;
+ }
+ }
+ }
+ if (year == 0 && month == 0 && day == 0 && hour == 0 && minute == 0 && second == 0) {
+ clif_displaymessage(fd, msg_table[85]); // Invalid time for ban command.
+ return -1;
+ }
+
+ // check player name
+ if (strlen(atcmd_player_name) < 4) {
+ clif_displaymessage(fd, msg_table[86]); // Sorry, but a player name have at least 4 characters.
+ return -1;
+ } else if (strlen(atcmd_player_name) > 23) {
+ clif_displaymessage(fd, msg_table[87]); // Sorry, but a player name have 23 characters maximum.
+ return -1;
+ } else {
+ chrif_char_ask_name(sd->status.account_id, atcmd_player_name, 2, year, month, day, hour, minute, second); // type: 2 - ban
+ clif_displaymessage(fd, msg_table[88]); // Character name sends to char-server to ask it.
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * charunblock command (usage: charunblock <player_name>)
+ *------------------------------------------
+ */
+int atcommand_char_unblock(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ nullpo_retr(-1, sd);
+
+ memset(atcmd_player_name, '\0', sizeof(atcmd_player_name));
+
+ if (!message || !*message || sscanf(message, "%99[^\n]", atcmd_player_name) < 1) {
+ clif_displaymessage(fd, "Please, enter a player name (usage: @charunblock <player_name>).");
+ return -1;
+ }
+
+ // check player name
+ if (strlen(atcmd_player_name) < 4) {
+ clif_displaymessage(fd, msg_table[86]); // Sorry, but a player name have at least 4 characters.
+ return -1;
+ } else if (strlen(atcmd_player_name) > 23) {
+ clif_displaymessage(fd, msg_table[87]); // Sorry, but a player name have 23 characters maximum.
+ return -1;
+ } else {
+ // send answer to login server via char-server
+ chrif_char_ask_name(sd->status.account_id, atcmd_player_name, 3, 0, 0, 0, 0, 0, 0); // type: 3 - unblock
+ clif_displaymessage(fd, msg_table[88]); // Character name sends to char-server to ask it.
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * charunban command (usage: charunban <player_name>)
+ *------------------------------------------
+ */
+int atcommand_char_unban(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ nullpo_retr(-1, sd);
+
+ memset(atcmd_player_name, '\0', sizeof(atcmd_player_name));
+
+ if (!message || !*message || sscanf(message, "%99[^\n]", atcmd_player_name) < 1) {
+ clif_displaymessage(fd, "Please, enter a player name (usage: @charunban <player_name>).");
+ return -1;
+ }
+
+ // check player name
+ if (strlen(atcmd_player_name) < 4) {
+ clif_displaymessage(fd, msg_table[86]); // Sorry, but a player name have at least 4 characters.
+ return -1;
+ } else if (strlen(atcmd_player_name) > 23) {
+ clif_displaymessage(fd, msg_table[87]); // Sorry, but a player name have 23 characters maximum.
+ return -1;
+ } else {
+ // send answer to login server via char-server
+ chrif_char_ask_name(sd->status.account_id, atcmd_player_name, 4, 0, 0, 0, 0, 0, 0); // type: 4 - unban
+ clif_displaymessage(fd, msg_table[88]); // Character name sends to char-server to ask it.
+ }
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int atcommand_night(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ nullpo_retr(-1, sd);
+
+ if (night_flag != 1) {
+ map_night_timer(night_timer_tid, 0, 0, 1);
+ } else {
+ clif_displaymessage(fd, msg_table[89]); // Sorry, it's already the night. Impossible to execute the command.
+ return -1;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int atcommand_day(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ nullpo_retr(-1, sd);
+
+ if (night_flag != 0) {
+ map_day_timer(day_timer_tid, 0, 0, 1);
+ } else {
+ clif_displaymessage(fd, msg_table[90]); // Sorry, it's already the day. Impossible to execute the command.
+ return -1;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int atcommand_doom(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ struct map_session_data *pl_sd, **pl_allsd;
+ int i, users;
+ nullpo_retr(-1, sd);
+ clif_specialeffect(&sd->bl,450,2);
+ pl_allsd = map_getallusers(&users);
+ for(i = 0; i < users; i++) {
+ if ((pl_sd = pl_allsd[i]) && pl_sd->fd != fd && pc_isGM(sd) >= pc_isGM(pl_sd)) { // you can doom only lower or same gm level
+ pc_damage(NULL, pl_sd, pl_sd->status.hp);
+ clif_displaymessage(pl_sd->fd, msg_table[61]); // The holy messenger has given judgement.
+ }
+ }
+ clif_displaymessage(fd, msg_table[62]); // Judgement was made.
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int atcommand_doommap(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ struct map_session_data *pl_sd, **pl_allsd;
+ int i, users;
+ nullpo_retr(-1, sd);
+ clif_specialeffect(&sd->bl,450,3);
+ pl_allsd = map_getallusers(&users);
+ for (i = 0; i < users; i++) {
+ if ((pl_sd = pl_allsd[i]) && pl_sd->fd != fd && sd->bl.m == pl_sd->bl.m &&
+ pc_isGM(sd) >= pc_isGM(pl_sd)) // you can doom only lower or same gm level
+ {
+ pc_damage(NULL, pl_sd, pl_sd->status.hp);
+// clif_specialeffect(&pl_sd->bl,450,1);
+ clif_displaymessage(pl_sd->fd, msg_table[61]); // The holy messenger has given judgement.
+ }
+ }
+ clif_displaymessage(fd, msg_table[62]); // Judgement was made.
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+static void atcommand_raise_sub(struct map_session_data* sd)
+{
+ if (sd && sd->state.auth && pc_isdead(sd)) {
+ clif_skill_nodamage(&sd->bl,&sd->bl,ALL_RESURRECTION,4,1);
+ sd->status.hp = sd->status.max_hp;
+ sd->status.sp = sd->status.max_sp;
+ pc_setstand(sd);
+ clif_updatestatus(sd, SP_HP);
+ clif_updatestatus(sd, SP_SP);
+ clif_resurrection(&sd->bl, 1);
+ if (battle_config.pc_invincible_time > 0)
+ pc_setinvincibletimer(sd, battle_config.pc_invincible_time);
+ clif_displaymessage(sd->fd, msg_table[63]); // Mercy has been shown.
+ }
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int atcommand_raise(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ int i, users;
+ struct map_session_data **all_sd;
+
+ nullpo_retr(-1, sd);
+
+ all_sd = map_getallusers(&users);
+
+ for (i = 0; i < users; i++) {
+ atcommand_raise_sub(all_sd[i]);
+ }
+ clif_displaymessage(fd, msg_table[64]); // Mercy has been granted.
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int atcommand_raisemap(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ struct map_session_data **pl_allsd;
+ int i, users;
+
+ nullpo_retr(-1, sd);
+
+ pl_allsd = map_getallusers(&users);
+
+ for (i = 0; i < users; i++) {
+ if (sd->bl.m == pl_allsd[i]->bl.m)
+ atcommand_raise_sub(pl_allsd[i]);
+ }
+ clif_displaymessage(fd, msg_table[64]); // Mercy has been granted.
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int atcommand_kick(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ struct map_session_data *pl_sd;
+ nullpo_retr(-1, sd);
+
+ memset(atcmd_player_name, '\0', sizeof(atcmd_player_name));
+
+ if (!message || !*message || sscanf(message, "%23[^\n]", atcmd_player_name) < 1) {
+ clif_displaymessage(fd, "Please, enter a player name (usage: @kick <charname>).");
+ return -1;
+ }
+
+ if ((pl_sd = map_nick2sd(atcmd_player_name)) != NULL) {
+ if (pc_isGM(sd) >= pc_isGM(pl_sd)) // you can kick only lower or same gm level
+ clif_GM_kick(sd, pl_sd, 1);
+ else {
+ clif_displaymessage(fd, msg_table[81]); // Your GM level don't authorise you to do this action on this player.
+ return -1;
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[3]); // Character not found.
+ return -1;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int atcommand_kickall(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ struct map_session_data *pl_sd, **pl_allsd;
+ int i, users;
+ nullpo_retr(-1, sd);
+
+ pl_allsd = map_getallusers(&users);
+
+ for (i = 0; i < users; i++) {
+ if ((pl_sd = pl_allsd[i]) && pc_isGM(sd) >= pc_isGM(pl_sd)) { // you can kick only lower or same gm level
+ if (sd->status.account_id != pl_sd->status.account_id)
+ clif_GM_kick(sd, pl_sd, 0);
+ }
+ }
+
+ clif_displaymessage(fd, msg_table[195]); // All players have been kicked!
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int atcommand_allskill(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ nullpo_retr(-1, sd);
+ pc_allskillup(sd); // all skills
+ sd->status.skill_point = 0; // 0 skill points
+ clif_updatestatus(sd, SP_SKILLPOINT); // update
+ clif_displaymessage(fd, msg_table[76]); // You have received all skills.
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int atcommand_questskill(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ int skill_id;
+ nullpo_retr(-1, sd);
+
+ if (!message || !*message || (skill_id = atoi(message)) < 0) {
+ clif_displaymessage(fd, "Please, enter a quest skill number (usage: @questskill <#:0+>).");
+ return -1;
+ }
+
+ if (skill_id >= 0 && skill_id < MAX_SKILL_DB) {
+ if (skill_get_inf2(skill_id) & INF2_QUEST_SKILL) {
+ if (pc_checkskill(sd, skill_id) == 0) {
+ pc_skill(sd, skill_id, 1, 0);
+ clif_displaymessage(fd, msg_table[70]); // You have learned the skill.
+ } else {
+ clif_displaymessage(fd, msg_table[196]); // You already have this quest skill.
+ return -1;
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[197]); // This skill number doesn't exist or isn't a quest skill.
+ return -1;
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[198]); // This skill number doesn't exist.
+ return -1;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int atcommand_lostskill(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ int skill_id;
+ nullpo_retr(-1, sd);
+
+ if (!message || !*message || (skill_id = atoi(message)) < 0) {
+ clif_displaymessage(fd, "Please, enter a quest skill number (usage: @lostskill <#:0+>).");
+ return -1;
+ }
+
+ if (skill_id >= 0 && skill_id < MAX_SKILL) {
+ if (skill_get_inf2(skill_id) & INF2_QUEST_SKILL) {
+ if (pc_checkskill(sd, skill_id) > 0) {
+ sd->status.skill[skill_id].lv = 0;
+ sd->status.skill[skill_id].flag = 0;
+ clif_skillinfoblock(sd);
+ clif_displaymessage(fd, msg_table[71]); // You have forgotten the skill.
+ } else {
+ clif_displaymessage(fd, msg_table[201]); // You don't have this quest skill.
+ return -1;
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[197]); // This skill number doesn't exist or isn't a quest skill.
+ return -1;
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[198]); // This skill number doesn't exist.
+ return -1;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int atcommand_spiritball(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ int number;
+ nullpo_retr(-1, sd);
+
+ if (!message || !*message || (number = atoi(message)) < 0) {
+ clif_displaymessage(fd, "Please, enter a spirit ball number (usage: @spiritball <number: 0-1000>).");
+ return -1;
+ }
+
+ // set max number to avoid server/client crash (500 create big balls of several balls: no visial difference with more)
+ if (number > 500)
+ number = 500;
+
+ if (number >= 0 && number <= 0x7FFF) {
+ if (sd->spiritball != number || number > 499) {
+ if (sd->spiritball > 0)
+ pc_delspiritball(sd, sd->spiritball, 1);
+ sd->spiritball = number;
+ clif_spiritball(sd);
+ // no message, player can look the difference
+ if (number > 1000)
+ clif_displaymessage(fd, msg_table[204]); // WARNING: more than 1000 spiritballs can CRASH your server and/or client!
+ } else {
+ clif_displaymessage(fd, msg_table[205]); // You already have this number of spiritballs.
+ return -1;
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[37]); // An invalid number was specified.
+ return -1;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int atcommand_party(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ char party[NAME_LENGTH];
+ nullpo_retr(-1, sd);
+
+ memset(party, '\0', sizeof(party));
+
+ if (!message || !*message || sscanf(message, "%23[^\n]", party) < 1) {
+ clif_displaymessage(fd, "Please, enter a party name (usage: @party <party_name>).");
+ return -1;
+ }
+
+ party_create(sd, party, 0, 0);
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int atcommand_guild(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ char guild[NAME_LENGTH];
+ int prev;
+ nullpo_retr(-1, sd);
+
+ memset(guild, '\0', sizeof(guild));
+
+ if (!message || !*message || sscanf(message, "%23[^\n]", guild) < 1) {
+ clif_displaymessage(fd, "Please, enter a guild name (usage: @guild <guild_name>).");
+ return -1;
+ }
+
+ prev = battle_config.guild_emperium_check;
+ battle_config.guild_emperium_check = 0;
+ guild_create(sd, guild);
+ battle_config.guild_emperium_check = prev;
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int atcommand_agitstart(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ nullpo_retr(-1, sd);
+ if (agit_flag == 1) {
+ clif_displaymessage(fd, msg_table[73]); // Already it has started siege warfare.
+ return -1;
+ }
+
+ agit_flag = 1;
+ guild_agit_start();
+ clif_displaymessage(fd, msg_table[72]); // Guild siege warfare start!
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int atcommand_agitend(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ nullpo_retr(-1, sd);
+ if (agit_flag == 0) {
+ clif_displaymessage(fd, msg_table[75]); // Siege warfare hasn't started yet.
+ return -1;
+ }
+
+ agit_flag = 0;
+ guild_agit_end();
+ clif_displaymessage(fd, msg_table[74]); // Guild siege warfare end!
+
+ return 0;
+}
+
+/*==========================================
+ * @mapexitでマップサーバーを終了させる
+ *------------------------------------------
+ */
+int atcommand_mapexit(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ struct map_session_data *pl_sd, **pl_allsd;
+ int i, users;
+ nullpo_retr(-1, sd);
+
+ pl_allsd = map_getallusers(&users);
+ for (i = 0; i < users; i++) {
+ if ((pl_sd = pl_allsd[i]) && sd->status.account_id != pl_sd->status.account_id)
+ clif_GM_kick(sd, pl_sd, 0);
+ }
+ clif_GM_kick(sd, sd, 0);
+
+ flush_fifos();
+
+ runflag = 0;
+
+ return 0;
+}
+
+/*==========================================
+ * idsearch <part_of_name>: revrited by [Yor]
+ *------------------------------------------
+ */
+int atcommand_idsearch(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ char item_name[100];
+ unsigned int i, match;
+ struct item_data *item;
+ nullpo_retr(-1, sd);
+
+ memset(item_name, '\0', sizeof(item_name));
+ memset(atcmd_output, '\0', sizeof(atcmd_output));
+
+ if (!message || !*message || sscanf(message, "%99s", item_name) < 0) {
+ clif_displaymessage(fd, "Please, enter a part of item name (usage: @idsearch <part_of_item_name>).");
+ return -1;
+ }
+
+ sprintf(atcmd_output, msg_table[77], item_name); // The reference result of '%s' (name: id):
+ clif_displaymessage(fd, atcmd_output);
+ match = 0;
+ for(i = 0; i < 20000; i++) {
+ if ((item = itemdb_exists(i)) != NULL && strstr(item->jname, item_name) != NULL) {
+ match++;
+ sprintf(atcmd_output, msg_table[78], item->jname, item->nameid); // %s: %d
+ clif_displaymessage(fd, atcmd_output);
+ }
+ }
+ sprintf(atcmd_output, msg_table[79], match); // It is %d affair above.
+ clif_displaymessage(fd, atcmd_output);
+
+ return 0;
+}
+
+/*==========================================
+ * Recall All Characters Online To Your Location
+ *------------------------------------------
+ */
+int atcommand_recallall(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ struct map_session_data *pl_sd, **pl_allsd;
+ int i;
+ int count, users;
+ nullpo_retr(-1, sd);
+
+ memset(atcmd_output, '\0', sizeof(atcmd_output));
+
+ if (sd->bl.m >= 0 && map[sd->bl.m].flag.nowarpto && battle_config.any_warp_GM_min_level > pc_isGM(sd)) {
+ clif_displaymessage(fd, "You are not authorised to warp somenone to your actual map.");
+ return -1;
+ }
+
+ count = 0;
+ pl_allsd = map_getallusers(&users);
+ for (i = 0; i < users; i++) {
+ if ((pl_sd = pl_allsd[i]) && sd->status.account_id != pl_sd->status.account_id &&
+ pc_isGM(sd) >= pc_isGM(pl_sd)) { // you can recall only lower or same level
+ if (pl_sd->bl.m >= 0 && map[pl_sd->bl.m].flag.nowarp && battle_config.any_warp_GM_min_level > pc_isGM(sd))
+ count++;
+ else {
+ if (pc_isdead(pl_sd)) { //Wake them up
+ pc_setstand(pl_sd);
+ pc_setrestartvalue(pl_sd,1);
+ }
+ pc_setpos(pl_sd, sd->mapindex, sd->bl.x, sd->bl.y, 2);
+ }
+ }
+ }
+
+ clif_displaymessage(fd, msg_table[92]); // All characters recalled!
+ if (count) {
+ sprintf(atcmd_output, "Because you are not authorised to warp from some maps, %d player(s) have not been recalled.", count);
+ clif_displaymessage(fd, atcmd_output);
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * Recall online characters of a guild to your location
+ *------------------------------------------
+ */
+int atcommand_guildrecall(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ struct map_session_data *pl_sd, **pl_allsd;
+ int i, users, count;
+ char guild_name[NAME_LENGTH];
+ struct guild *g;
+ nullpo_retr(-1, sd);
+
+ memset(guild_name, '\0', sizeof(guild_name));
+ memset(atcmd_output, '\0', sizeof(atcmd_output));
+
+ if (!message || !*message || sscanf(message, "%23[^\n]", guild_name) < 1) {
+ clif_displaymessage(fd, "Please, enter a guild name/id (usage: @guildrecall <guild_name/id>).");
+ return -1;
+ }
+
+ if (sd->bl.m >= 0 && map[sd->bl.m].flag.nowarpto && battle_config.any_warp_GM_min_level > pc_isGM(sd)) {
+ clif_displaymessage(fd, "You are not authorised to warp somenone to your actual map.");
+ return -1;
+ }
+
+ if ((g = guild_searchname(guild_name)) != NULL || // name first to avoid error when name begin with a number
+ (g = guild_search(atoi(message))) != NULL) {
+ count = 0;
+ pl_allsd = map_getallusers(&users);
+ for (i = 0; i < users; i++) {
+ if ((pl_sd = pl_allsd[i]) && sd->status.account_id != pl_sd->status.account_id &&
+ pl_sd->status.guild_id == g->guild_id) {
+ if (pl_sd->bl.m >= 0 && map[pl_sd->bl.m].flag.nowarp && battle_config.any_warp_GM_min_level > pc_isGM(sd))
+ count++;
+ else
+ pc_setpos(pl_sd, sd->mapindex, sd->bl.x, sd->bl.y, 2);
+ }
+ }
+ sprintf(atcmd_output, msg_table[93], g->name); // All online characters of the %s guild are near you.
+ clif_displaymessage(fd, atcmd_output);
+ if (count) {
+ sprintf(atcmd_output, "Because you are not authorised to warp from some maps, %d player(s) have not been recalled.", count);
+ clif_displaymessage(fd, atcmd_output);
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[94]); // Incorrect name/ID, or no one from the guild is online.
+ return -1;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * Recall online characters of a party to your location
+ *------------------------------------------
+ */
+int atcommand_partyrecall(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ int i;
+ struct map_session_data *pl_sd, **pl_allsd;
+ char party_name[NAME_LENGTH];
+ struct party *p;
+ int count, users;
+ nullpo_retr(-1, sd);
+
+ memset(party_name, '\0', sizeof(party_name));
+ memset(atcmd_output, '\0', sizeof(atcmd_output));
+
+ if (!message || !*message || sscanf(message, "%23[^\n]", party_name) < 1) {
+ clif_displaymessage(fd, "Please, enter a party name/id (usage: @partyrecall <party_name/id>).");
+ return -1;
+ }
+
+ if (sd->bl.m >= 0 && map[sd->bl.m].flag.nowarpto && battle_config.any_warp_GM_min_level > pc_isGM(sd)) {
+ clif_displaymessage(fd, "You are not authorised to warp somenone to your actual map.");
+ return -1;
+ }
+
+ if ((p = party_searchname(party_name)) != NULL || // name first to avoid error when name begin with a number
+ (p = party_search(atoi(message))) != NULL) {
+ count = 0;
+
+ pl_allsd = map_getallusers(&users);
+ for (i = 0; i < users; i++) {
+ if ((pl_sd = pl_allsd[i]) && sd->status.account_id != pl_sd->status.account_id &&
+ pl_sd->status.party_id == p->party_id) {
+ if (pl_sd->bl.m >= 0 && map[pl_sd->bl.m].flag.nowarp && battle_config.any_warp_GM_min_level > pc_isGM(sd))
+ count++;
+ else
+ pc_setpos(pl_sd, sd->mapindex, sd->bl.x, sd->bl.y, 2);
+ }
+ }
+ sprintf(atcmd_output, msg_table[95], p->name); // All online characters of the %s party are near you.
+ clif_displaymessage(fd, atcmd_output);
+ if (count) {
+ sprintf(atcmd_output, "Because you are not authorised to warp from some maps, %d player(s) have not been recalled.", count);
+ clif_displaymessage(fd, atcmd_output);
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[96]); // Incorrect name or ID, or no one from the party is online.
+ return -1;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int atcommand_reloaditemdb(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ nullpo_retr(-1, sd);
+ itemdb_reload();
+ clif_displaymessage(fd, msg_table[97]); // Item database reloaded.
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int atcommand_reloadmobdb(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ nullpo_retr(-1, sd);
+ mob_reload();
+ do_final_pet();
+ read_petdb();
+ clif_displaymessage(fd, msg_table[98]); // Monster database reloaded.
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int atcommand_reloadskilldb(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ nullpo_retr(-1, sd);
+ skill_reload();
+ clif_displaymessage(fd, msg_table[99]); // Skill database reloaded.
+
+ return 0;
+}
+
+/*==========================================
+ * @reloadatcommand
+ * atcommand_athena.conf のリロード
+ *------------------------------------------
+ */
+int
+atcommand_reloadatcommand(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ atcommand_config_read(ATCOMMAND_CONF_FILENAME);
+ clif_displaymessage(fd, msg_table[254]);
+ return 0;
+}
+/*==========================================
+ * @reloadbattleconf
+ * battle_athena.conf のリロード
+ *------------------------------------------
+ */
+int
+atcommand_reloadbattleconf(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ battle_config_read(BATTLE_CONF_FILENAME);
+ mob_reload(); //Needed as well so rate changes take effect.
+ clif_displaymessage(fd, msg_table[255]);
+ return 0;
+}
+/*==========================================
+ * @reloadstatusdb
+ * job_db1.txt job_db2.txt job_db2-2.txt
+ * refine_db.txt size_fix.txt
+ * のリロード
+ *------------------------------------------
+ */
+int
+atcommand_reloadstatusdb(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ status_readdb();
+ clif_displaymessage(fd, msg_table[256]);
+ return 0;
+}
+/*==========================================
+ * @reloadpcdb
+ * exp.txt skill_tree.txt attr_fix.txt
+ * のリロード
+ *------------------------------------------
+ */
+int
+atcommand_reloadpcdb(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ pc_readdb();
+ clif_displaymessage(fd, msg_table[257]);
+ return 0;
+}
+
+/*==========================================
+ * @reloadmotd [Valaris]
+ * Reloads motd.txt
+ *------------------------------------------
+ */
+int
+atcommand_reloadmotd(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ pc_read_motd();
+ clif_displaymessage(fd, msg_table[268]);
+ return 0;
+}
+
+
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int atcommand_reloadscript(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ nullpo_retr(-1, sd);
+ atcommand_broadcast( fd, sd, "@broadcast", "eAthena Server is Rehashing..." );
+ atcommand_broadcast( fd, sd, "@broadcast", "You will feel a bit of lag at this point !" );
+ atcommand_broadcast( fd, sd, "@broadcast", "Reloading NPCs..." );
+ flush_fifos();
+
+ //do_init_npc();
+ script_reload();
+ npc_reload();
+ npc_event_do_oninit();
+
+ clif_displaymessage(fd, msg_table[100]); // Scripts reloaded.
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int atcommand_reloadgmdb( // by [Yor]
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ nullpo_retr(-1, sd);
+ chrif_reloadGMdb();
+
+ clif_displaymessage(fd, msg_table[101]); // Login-server asked to reload GM accounts and their level.
+
+ return 0;
+}
+
+/*==========================================
+ * @mapinfo <map name> [0-3] by MC_Cameri
+ * => Shows information about the map [map name]
+ * 0 = no additional information
+ * 1 = Show users in that map and their location
+ * 2 = Shows NPCs in that map
+ * 3 = Shows the shops/chats in that map (not implemented)
+ *------------------------------------------
+ */
+int atcommand_mapinfo(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ struct map_session_data *pl_sd, **pl_allsd;
+ struct npc_data *nd = NULL;
+ struct chat_data *cd = NULL;
+ char direction[12];
+ int m_id, i, chat_num, users, list = 0;
+ unsigned short m_index;
+ nullpo_retr(-1, sd);
+
+ memset(atcmd_output, '\0', sizeof(atcmd_output));
+ memset(atcmd_player_name, '\0', sizeof(atcmd_player_name));
+ memset(direction, '\0', sizeof(direction));
+
+ sscanf(message, "%d %23[^\n]", &list, atcmd_player_name);
+
+ if (list < 0 || list > 3) {
+ clif_displaymessage(fd, "Please, enter at least a valid list number (usage: @mapinfo <0-3> [map]).");
+ return -1;
+ }
+
+ if (atcmd_player_name[0] == '\0') {
+ memcpy(atcmd_player_name, mapindex_id2name(sd->mapindex), MAP_NAME_LENGTH);
+ atcmd_player_name[MAP_NAME_LENGTH] = '\0';
+ m_id = map_mapindex2mapid(sd->mapindex);
+ } else {
+ if (strstr(atcmd_player_name, ".gat") == NULL && strstr(atcmd_player_name, ".afm") == NULL && strlen(atcmd_player_name) < MAP_NAME_LENGTH-4) // 16 - 4 (.gat)
+ strcat(atcmd_player_name, ".gat");
+ m_id = map_mapname2mapid(atcmd_player_name);
+ }
+ if (m_id < 0) {
+ clif_displaymessage(fd, msg_table[1]); // Map not found.
+ return -1;
+ }
+ m_index = mapindex_name2id(atcmd_player_name); //This one shouldn't fail since the previous seek did not.
+
+ clif_displaymessage(fd, "------ Map Info ------");
+ chat_num = 0;
+ pl_allsd = map_getallusers(&users);
+ for (i = 0; i < users; i++) {
+ if ((pl_sd = pl_allsd[i]) && (cd = (struct chat_data*)map_id2bl(pl_sd->chatID))) {
+ chat_num++;
+ }
+ }
+ sprintf(atcmd_output, "Map Name: %s | Players In Map: %d | NPCs In Map: %d | Chats In Map: %d", atcmd_player_name, map[m_id].users, map[m_id].npc_num, chat_num);
+ clif_displaymessage(fd, atcmd_output);
+ if (map[m_id].flag.alias)
+ strcat(atcmd_output, "This map is an alias (a named clone of some other map).");
+ clif_displaymessage(fd, "------ Map Flags ------");
+ strcpy(atcmd_output,"PvP Flags: ");
+ if (map[m_id].flag.pvp)
+ strcat(atcmd_output, "Pvp ON | ");
+ if (map[m_id].flag.nopvp)
+ strcat(atcmd_output, "NoPvp | ");
+ if (map[m_id].flag.pvp_noguild)
+ strcat(atcmd_output, "NoGuild | ");
+ if (map[m_id].flag.pvp_noparty)
+ strcat(atcmd_output, "NoParty | ");
+ if (map[m_id].flag.pvp_nightmaredrop)
+ strcat(atcmd_output, "NightmareDrop | ");
+ if (map[m_id].flag.pvp_nocalcrank)
+ strcat(atcmd_output, "NoCalcRank | ");
+ clif_displaymessage(fd, atcmd_output);
+
+ strcpy(atcmd_output,"GvG Flags: ");
+ if (map[m_id].flag.gvg)
+ strcat(atcmd_output, "GvG ON | ");
+ if (map[m_id].flag.gvg_dungeon)
+ strcat(atcmd_output, "GvG Dungeon | ");
+ if (map[m_id].flag.gvg_castle)
+ strcat(atcmd_output, "GvG Castle | ");
+ if (map[m_id].flag.gvg_noparty)
+ strcat(atcmd_output, "NoParty | ");
+ clif_displaymessage(fd, atcmd_output);
+
+ strcpy(atcmd_output,"Teleport Flags: ");
+ if (map[m_id].flag.noteleport)
+ strcat(atcmd_output, "NoTeleport | ");
+ if (map[m_id].flag.monster_noteleport)
+ strcat(atcmd_output, "Monster NoTeleport | ");
+ if (map[m_id].flag.nowarp)
+ strcat(atcmd_output, "NoWarp | ");
+ if (map[m_id].flag.nowarpto)
+ strcat(atcmd_output, "NoWarpTo | ");
+ if (map[m_id].flag.noreturn)
+ strcat(atcmd_output, "NoReturn | ");
+ if (map[m_id].flag.nogo)
+ strcat(atcmd_output, "NoGo | ");
+ if (map[m_id].flag.nomemo)
+ strcat(atcmd_output, "NoMemo | ");
+ clif_displaymessage(fd, atcmd_output);
+
+ sprintf(atcmd_output, "No Penalty: %s | No Zeny Penalty: %s", (map[m_id].flag.nopenalty) ? "On" : "Off", (map[m_id].flag.nozenypenalty) ? "On" : "Off");
+ clif_displaymessage(fd, atcmd_output);
+
+ if (map[m_id].flag.nosave) {
+ if (map[m_id].save.x == -1 || map[m_id].save.y == -1 )
+ sprintf(atcmd_output, "No Save, Save Point: %s,Random",mapindex_id2name(map[m_id].save.map));
+ else
+ sprintf(atcmd_output, "No Save, Save Point: %s,%d,%d",
+ mapindex_id2name(map[m_id].save.map),map[m_id].save.x,map[m_id].save.y);
+ clif_displaymessage(fd, atcmd_output);
+ }
+
+ strcpy(atcmd_output,"Weather Flags: ");
+ if (map[m_id].flag.snow)
+ strcat(atcmd_output, "Snow | ");
+ if (map[m_id].flag.fog)
+ strcat(atcmd_output, "Fog | ");
+ if (map[m_id].flag.sakura)
+ strcat(atcmd_output, "Sakura | ");
+ if (map[m_id].flag.clouds)
+ strcat(atcmd_output, "Clouds | ");
+ if (map[m_id].flag.clouds2)
+ strcat(atcmd_output, "Clouds2 | ");
+ if (map[m_id].flag.fireworks)
+ strcat(atcmd_output, "Fireworks | ");
+ if (map[m_id].flag.leaves)
+ strcat(atcmd_output, "Leaves | ");
+ if (map[m_id].flag.rain)
+ strcat(atcmd_output, "Rain | ");
+ if (map[m_id].flag.indoors)
+ strcat(atcmd_output, "Indoors | ");
+ if (map[m_id].flag.nightenabled)
+ strcat(atcmd_output, "Displays Night | ");
+ clif_displaymessage(fd, atcmd_output);
+
+ strcpy(atcmd_output,"Other Flags: ");
+ if (map[m_id].flag.nobranch)
+ strcat(atcmd_output, "NoBranch | ");
+ if (map[m_id].flag.notrade)
+ strcat(atcmd_output, "NoTrade | ");
+ if (map[m_id].flag.noskill)
+ strcat(atcmd_output, "NoSkill | ");
+ if (map[m_id].flag.noicewall)
+ strcat(atcmd_output, "NoIcewall | ");
+ clif_displaymessage(fd, atcmd_output);
+
+ strcpy(atcmd_output,"Other Flags: ");
+ if (map[m_id].flag.nobaseexp)
+ strcat(atcmd_output, "NoBaseEXP | ");
+ if (map[m_id].flag.nojobexp)
+ strcat(atcmd_output, "NoJobEXP | ");
+ if (map[m_id].flag.nomobloot)
+ strcat(atcmd_output, "NoMobLoot | ");
+ if (map[m_id].flag.nomvploot)
+ strcat(atcmd_output, "NoMVPLoot | ");
+ clif_displaymessage(fd, atcmd_output);
+
+
+ switch (list) {
+ case 0:
+ // Do nothing. It's list 0, no additional display.
+ break;
+ case 1:
+ clif_displaymessage(fd, "----- Players in Map -----");
+ for (i = 0; i < users; i++) {
+ if ((pl_sd = pl_allsd[i]) && pl_sd->mapindex == m_index) {
+ sprintf(atcmd_output, "Player '%s' (session #%d) | Location: %d,%d",
+ pl_sd->status.name, pl_sd->fd, pl_sd->bl.x, pl_sd->bl.y);
+ clif_displaymessage(fd, atcmd_output);
+ }
+ }
+ break;
+ case 2:
+ clif_displaymessage(fd, "----- NPCs in Map -----");
+ for (i = 0; i < map[m_id].npc_num;) {
+ nd = map[m_id].npc[i];
+ switch(nd->dir) {
+ case 0: strcpy(direction, "North"); break;
+ case 1: strcpy(direction, "North West"); break;
+ case 2: strcpy(direction, "West"); break;
+ case 3: strcpy(direction, "South West"); break;
+ case 4: strcpy(direction, "South"); break;
+ case 5: strcpy(direction, "South East"); break;
+ case 6: strcpy(direction, "East"); break;
+ case 7: strcpy(direction, "North East"); break;
+ case 9: strcpy(direction, "North"); break;
+ default: strcpy(direction, "Unknown"); break;
+ }
+ sprintf(atcmd_output, "NPC %d: %s | Direction: %s | Sprite: %d | Location: %d %d",
+ ++i, nd->name, direction, nd->class_, nd->bl.x, nd->bl.y);
+ clif_displaymessage(fd, atcmd_output);
+ }
+ break;
+ case 3:
+ clif_displaymessage(fd, "----- Chats in Map -----");
+ for (i = 0; i < users; i++) {
+ if ((pl_sd = pl_allsd[i]) && (cd = (struct chat_data*)map_id2bl(pl_sd->chatID)) &&
+ pl_sd->mapindex == m_index &&
+ cd->usersd[0] == pl_sd) {
+ sprintf(atcmd_output, "Chat %d: %s | Player: %s | Location: %d %d",
+ i, cd->title, pl_sd->status.name, cd->bl.x, cd->bl.y);
+ clif_displaymessage(fd, atcmd_output);
+ sprintf(atcmd_output, " Users: %d/%d | Password: %s | Public: %s",
+ cd->users, cd->limit, cd->pass, (cd->pub) ? "Yes" : "No");
+ clif_displaymessage(fd, atcmd_output);
+ }
+ }
+ break;
+ default: // normally impossible to arrive here
+ clif_displaymessage(fd, "Please, enter at least a valid list number (usage: @mapinfo <0-3> [map]).");
+ return -1;
+ break;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int atcommand_mount_peco(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ nullpo_retr(-1, sd);
+
+ if (!pc_isriding(sd)) { // if actually no peco
+ if (pc_checkskill(sd, KN_RIDING)) {
+ pc_setoption(sd, sd->status.option | 0x0020);
+ clif_displaymessage(fd, msg_table[102]); // Mounted Peco.
+ } else {
+ clif_displaymessage(fd, msg_table[213]); // You can not mount a peco with your job.
+ return -1;
+ }
+ } else { //Dismount
+ pc_setoption(sd, sd->status.option & ~0x0020);
+ clif_displaymessage(fd, msg_table[214]); // Unmounted Peco.
+ }
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int atcommand_char_mount_peco(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ struct map_session_data *pl_sd;
+ nullpo_retr(-1, sd);
+
+ memset(atcmd_player_name, '\0', sizeof(atcmd_player_name));
+
+ if (!message || !*message || sscanf(message, "%23[^\n]", atcmd_player_name) < 1) {
+ clif_displaymessage(fd, "Please, enter a player name (usage: @charmountpeco <char_name>).");
+ return -1;
+ }
+
+ if ((pl_sd = map_nick2sd(atcmd_player_name)) != NULL) {
+
+ if (!pc_isriding(pl_sd)) { // if actually no peco
+ if (pc_checkskill(pl_sd, KN_RIDING)) {
+ pc_setoption(pl_sd, pl_sd->status.option | 0x0020);
+ clif_displaymessage(fd, msg_table[216]); // Mounted Peco.
+ } else {
+ clif_displaymessage(fd, msg_table[217]); // You can not mount a peco with your job.
+ return -1;
+ }
+ } else { //Dismount
+ pc_setoption(pl_sd, pl_sd->status.option & ~0x0020);
+ clif_displaymessage(fd, msg_table[218]); // Unmounted Peco.
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[3]); // Character not found.
+ return -1;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ *Spy Commands by Syrus22
+ *------------------------------------------
+ */
+int atcommand_guildspy(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ char guild_name[NAME_LENGTH];
+ struct guild *g;
+ nullpo_retr(-1, sd);
+
+ memset(guild_name, '\0', sizeof(guild_name));
+ memset(atcmd_output, '\0', sizeof(atcmd_output));
+
+ if (!enable_spy)
+ {
+ clif_displaymessage(fd, "The mapserver has spy command support disabled.");
+ return -1;
+ }
+ if (!message || !*message || sscanf(message, "%23[^\n]", guild_name) < 1) {
+ clif_displaymessage(fd, "Please, enter a guild name/id (usage: @guildspy <guild_name/id>).");
+ return -1;
+ }
+
+ if ((g = guild_searchname(guild_name)) != NULL || // name first to avoid error when name begin with a number
+ (g = guild_search(atoi(message))) != NULL) {
+ if (sd->guildspy == g->guild_id) {
+ sd->guildspy = 0;
+ sprintf(atcmd_output, msg_table[103], g->name); // No longer spying on the %s guild.
+ clif_displaymessage(fd, atcmd_output);
+ } else {
+ sd->guildspy = g->guild_id;
+ sprintf(atcmd_output, msg_table[104], g->name); // Spying on the %s guild.
+ clif_displaymessage(fd, atcmd_output);
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[94]); // Incorrect name/ID, or no one from the guild is online.
+ return -1;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int atcommand_partyspy(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ char party_name[NAME_LENGTH];
+ struct party *p;
+ nullpo_retr(-1, sd);
+
+ memset(party_name, '\0', sizeof(party_name));
+ memset(atcmd_output, '\0', sizeof(atcmd_output));
+
+ if (!enable_spy)
+ {
+ clif_displaymessage(fd, "The mapserver has spy command support disabled.");
+ return -1;
+ }
+
+ if (!message || !*message || sscanf(message, "%23[^\n]", party_name) < 1) {
+ clif_displaymessage(fd, "Please, enter a party name/id (usage: @partyspy <party_name/id>).");
+ return -1;
+ }
+
+ if ((p = party_searchname(party_name)) != NULL || // name first to avoid error when name begin with a number
+ (p = party_search(atoi(message))) != NULL) {
+ if (sd->partyspy == p->party_id) {
+ sd->partyspy = 0;
+ sprintf(atcmd_output, msg_table[105], p->name); // No longer spying on the %s party.
+ clif_displaymessage(fd, atcmd_output);
+ } else {
+ sd->partyspy = p->party_id;
+ sprintf(atcmd_output, msg_table[106], p->name); // Spying on the %s party.
+ clif_displaymessage(fd, atcmd_output);
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[96]); // Incorrect name or ID, or no one from the party is online.
+ return -1;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * @repairall [Valaris]
+ *------------------------------------------
+ */
+int atcommand_repairall(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ int count, i;
+ nullpo_retr(-1, sd);
+
+ count = 0;
+ for (i = 0; i < MAX_INVENTORY; i++) {
+ if (sd->status.inventory[i].nameid && sd->status.inventory[i].attribute == 1) {
+ sd->status.inventory[i].attribute = 0;
+ clif_produceeffect(sd, 0, sd->status.inventory[i].nameid);
+ count++;
+ }
+ }
+
+ if (count > 0) {
+ clif_misceffect(&sd->bl, 3);
+ clif_equiplist(sd);
+ clif_displaymessage(fd, msg_table[107]); // All items have been repaired.
+ } else {
+ clif_displaymessage(fd, msg_table[108]); // No item need to be repaired.
+ return -1;
+ }
+
+ return 0;
+}
+
+// Removed @nuke for now in favor of alchemist marine sphere skill [Valaris]
+int atcommand_nuke(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ struct map_session_data *pl_sd;
+ nullpo_retr(-1, sd);
+
+ memset(atcmd_player_name, '\0', sizeof(atcmd_player_name));
+
+ if (!message || !*message || sscanf(message, "%23[^\n]", atcmd_player_name) < 1) {
+ clif_displaymessage(fd, "Please, enter a player name (usage: @nuke <char name>).");
+ return -1;
+ }
+
+ if ((pl_sd = map_nick2sd(atcmd_player_name)) != NULL) {
+ if (pc_isGM(sd) >= pc_isGM(pl_sd)) { // you can kill only lower or same GM level
+ skill_castend_damage_id(&pl_sd->bl, &pl_sd->bl, NPC_SELFDESTRUCTION, 99, gettick(), 0);
+ clif_displaymessage(fd, msg_table[109]); // Player has been nuked!
+ } else {
+ clif_displaymessage(fd, msg_table[81]); // Your GM level don't authorise you to do this action on this player.
+ return -1;
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[3]); // Character not found.
+ return -1;
+ }
+
+ return 0;
+}
+
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int atcommand_shownpc(const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ char NPCname[NAME_LENGTH];
+ nullpo_retr(-1, sd);
+
+ memset(NPCname, '\0', sizeof(NPCname));
+
+ if (!message || !*message || sscanf(message, "%23[^\n]", NPCname) < 1) {
+ clif_displaymessage(fd, "Please, enter a NPC name (usage: @enablenpc <NPC_name>).");
+ return -1;
+ }
+
+ if (npc_name2id(NPCname) != NULL) {
+ npc_enable(NPCname, 1);
+ clif_displaymessage(fd, msg_table[110]); // Npc Enabled.
+ } else {
+ clif_displaymessage(fd, msg_table[111]); // This NPC doesn't exist.
+ return -1;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int atcommand_hidenpc(const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ char NPCname[NAME_LENGTH];
+ nullpo_retr(-1, sd);
+
+ memset(NPCname, '\0', sizeof(NPCname));
+
+ if (!message || !*message || sscanf(message, "%23[^\n]", NPCname) < 1) {
+ clif_displaymessage(fd, "Please, enter a NPC name (usage: @npcoff <NPC_name>).");
+ return -1;
+ }
+
+ if (npc_name2id(NPCname) != NULL) {
+ npc_enable(NPCname, 0);
+ clif_displaymessage(fd, msg_table[112]); // Npc Disabled.
+ } else {
+ clif_displaymessage(fd, msg_table[111]); // This NPC doesn't exist.
+ return -1;
+ }
+
+ return 0;
+}
+
+int atcommand_loadnpc(const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ FILE *fp;
+
+ if (!message || !*message) {
+ clif_displaymessage(fd, "Please, enter a script file name (usage: @loadnpc <file name>).");
+ return -1;
+ }
+
+ // check if script file exists
+ if ((fp = fopen(message, "r")) == NULL) {
+ clif_displaymessage(fd, msg_table[261]);
+ return -1;
+ }
+ fclose(fp);
+
+ // add to list of script sources and run it
+ npc_addsrcfile((char *)message);
+ npc_parsesrcfile((char *)message);
+
+ clif_displaymessage(fd, msg_table[262]);
+
+ return 0;
+}
+
+int atcommand_unloadnpc(const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ struct npc_data *nd;
+ char NPCname[NAME_LENGTH];
+ nullpo_retr(-1, sd);
+
+ memset(NPCname, '\0', sizeof(NPCname));
+
+ if (!message || !*message || sscanf(message, "%23[^\n]", NPCname) < 1) {
+ clif_displaymessage(fd, "Please, enter a NPC name (usage: @npcoff <NPC_name>).");
+ return -1;
+ }
+
+ if ((nd = npc_name2id(NPCname)) != NULL) {
+ npc_unload(nd);
+ clif_displaymessage(fd, msg_table[112]); // Npc Disabled.
+ } else {
+ clif_displaymessage(fd, msg_table[111]); // This NPC doesn't exist.
+ return -1;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * time in txt for time command (by [Yor])
+ *------------------------------------------
+ */
+char * txt_time(unsigned int duration) {
+ int days, hours, minutes, seconds;
+ char temp[256];
+ static char temp1[256];
+
+ memset(temp, '\0', sizeof(temp));
+ memset(temp1, '\0', sizeof(temp1));
+
+ if (duration < 0)
+ duration = 0;
+
+ days = duration / (60 * 60 * 24);
+ duration = duration - (60 * 60 * 24 * days);
+ hours = duration / (60 * 60);
+ duration = duration - (60 * 60 * hours);
+ minutes = duration / 60;
+ seconds = duration - (60 * minutes);
+
+ if (days < 2)
+ sprintf(temp, msg_table[219], days); // %d day
+ else
+ sprintf(temp, msg_table[220], days); // %d days
+ if (hours < 2)
+ sprintf(temp1, msg_table[221], temp, hours); // %s %d hour
+ else
+ sprintf(temp1, msg_table[222], temp, hours); // %s %d hours
+ if (minutes < 2)
+ sprintf(temp, msg_table[223], temp1, minutes); // %s %d minute
+ else
+ sprintf(temp, msg_table[224], temp1, minutes); // %s %d minutes
+ if (seconds < 2)
+ sprintf(temp1, msg_table[225], temp, seconds); // %s and %d second
+ else
+ sprintf(temp1, msg_table[226], temp, seconds); // %s and %d seconds
+
+ return temp1;
+}
+
+/*==========================================
+ * @time/@date/@server_date/@serverdate/@server_time/@servertime: Display the date/time of the server (by [Yor]
+ * Calculation management of GM modification (@day/@night GM commands) is done
+ *------------------------------------------
+ */
+int atcommand_servertime(const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ struct TimerData * timer_data;
+ struct TimerData * timer_data2;
+ time_t time_server; // variable for number of seconds (used with time() function)
+ struct tm *datetime; // variable for time in structure ->tm_mday, ->tm_sec, ...
+ char temp[256];
+ nullpo_retr(-1, sd);
+
+ memset(temp, '\0', sizeof(temp));
+
+ time(&time_server); // get time in seconds since 1/1/1970
+ datetime = localtime(&time_server); // convert seconds in structure
+ // like sprintf, but only for date/time (Sunday, November 02 2003 15:12:52)
+ strftime(temp, sizeof(temp)-1, msg_table[230], datetime); // Server time (normal time): %A, %B %d %Y %X.
+ clif_displaymessage(fd, temp);
+
+ if (battle_config.night_duration == 0 && battle_config.day_duration == 0) {
+ if (night_flag == 0)
+ clif_displaymessage(fd, msg_table[231]); // Game time: The game is in permanent daylight.
+ else
+ clif_displaymessage(fd, msg_table[232]); // Game time: The game is in permanent night.
+ } else if (battle_config.night_duration == 0)
+ if (night_flag == 1) { // we start with night
+ timer_data = get_timer(day_timer_tid);
+ sprintf(temp, msg_table[233], txt_time(DIFF_TICK(timer_data->tick,gettick())/1000)); // Game time: The game is actualy in night for %s.
+ clif_displaymessage(fd, temp);
+ clif_displaymessage(fd, msg_table[234]); // Game time: After, the game will be in permanent daylight.
+ } else
+ clif_displaymessage(fd, msg_table[231]); // Game time: The game is in permanent daylight.
+ else if (battle_config.day_duration == 0)
+ if (night_flag == 0) { // we start with day
+ timer_data = get_timer(night_timer_tid);
+ sprintf(temp, msg_table[235], txt_time(DIFF_TICK(timer_data->tick,gettick())/1000)); // Game time: The game is actualy in daylight for %s.
+ clif_displaymessage(fd, temp);
+ clif_displaymessage(fd, msg_table[236]); // Game time: After, the game will be in permanent night.
+ } else
+ clif_displaymessage(fd, msg_table[232]); // Game time: The game is in permanent night.
+ else {
+ if (night_flag == 0) {
+ timer_data = get_timer(night_timer_tid);
+ timer_data2 = get_timer(day_timer_tid);
+ sprintf(temp, msg_table[235], txt_time(DIFF_TICK(timer_data->tick,gettick())/1000)); // Game time: The game is actualy in daylight for %s.
+ clif_displaymessage(fd, temp);
+ if (DIFF_TICK(timer_data->tick, timer_data2->tick) > 0)
+ sprintf(temp, msg_table[237], txt_time(DIFF_TICK(timer_data->interval,DIFF_TICK(timer_data->tick,timer_data2->tick)) / 1000)); // Game time: After, the game will be in night for %s.
+ else
+ sprintf(temp, msg_table[237], txt_time(DIFF_TICK(timer_data2->tick,timer_data->tick)/1000)); // Game time: After, the game will be in night for %s.
+ clif_displaymessage(fd, temp);
+ sprintf(temp, msg_table[238], txt_time(timer_data->interval / 1000)); // Game time: A day cycle has a normal duration of %s.
+ clif_displaymessage(fd, temp);
+ } else {
+ timer_data = get_timer(day_timer_tid);
+ timer_data2 = get_timer(night_timer_tid);
+ sprintf(temp, msg_table[233], txt_time(DIFF_TICK(timer_data->tick,gettick()) / 1000)); // Game time: The game is actualy in night for %s.
+ clif_displaymessage(fd, temp);
+ if (DIFF_TICK(timer_data->tick,timer_data2->tick) > 0)
+ sprintf(temp, msg_table[239], txt_time((timer_data->interval - DIFF_TICK(timer_data->tick, timer_data2->tick)) / 1000)); // Game time: After, the game will be in daylight for %s.
+ else
+ sprintf(temp, msg_table[239], txt_time(DIFF_TICK(timer_data2->tick, timer_data->tick) / 1000)); // Game time: After, the game will be in daylight for %s.
+ clif_displaymessage(fd, temp);
+ sprintf(temp, msg_table[238], txt_time(timer_data->interval / 1000)); // Game time: A day cycle has a normal duration of %s.
+ clif_displaymessage(fd, temp);
+ }
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * @chardelitem <item_name_or_ID> <quantity> <player> (by [Yor]
+ * removes <quantity> item from a character
+ * item can be equiped or not.
+ * Inspired from a old command created by RoVeRT
+ *------------------------------------------
+ */
+int atcommand_chardelitem(const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ struct map_session_data *pl_sd;
+ char item_name[100];
+ int i, number = 0, item_id, item_position, count;
+ struct item_data *item_data;
+ nullpo_retr(-1, sd);
+
+ memset(atcmd_player_name, '\0', sizeof(atcmd_player_name));
+ memset(item_name, '\0', sizeof(item_name));
+ memset(atcmd_output, '\0', sizeof(atcmd_output));
+
+ if (!message || !*message || sscanf(message, "%s %d %99[^\n]", item_name, &number, atcmd_player_name) < 3 || number < 1) {
+ clif_displaymessage(fd, "Please, enter an item name/id, a quantity and a player name (usage: @chardelitem <item_name_or_ID> <quantity> <player>).");
+ return -1;
+ }
+
+ item_id = 0;
+ if ((item_data = itemdb_searchname(item_name)) != NULL ||
+ (item_data = itemdb_exists(atoi(item_name))) != NULL)
+ item_id = item_data->nameid;
+
+ if (item_id > 500) {
+ if ((pl_sd = map_nick2sd(atcmd_player_name)) != NULL) {
+ if (pc_isGM(sd) >= pc_isGM(pl_sd)) { // you can kill only lower or same level
+ item_position = pc_search_inventory(pl_sd, item_id);
+ if (item_position >= 0) {
+ count = 0;
+ for(i = 0; i < number && item_position >= 0; i++) {
+
+ //Logs (A)dmins items [Lupus]
+ if(log_config.pick > 0 ) {
+ log_pick(pl_sd, "A", 0, pl_sd->status.inventory[item_position].nameid, -1, &pl_sd->status.inventory[item_position]);
+ }
+ //Logs
+
+ pc_delitem(pl_sd, item_position, 1, 0);
+ count++;
+ item_position = pc_search_inventory(pl_sd, item_id); // for next loop
+ }
+ sprintf(atcmd_output, msg_table[113], count); // %d item(s) removed by a GM.
+ clif_displaymessage(pl_sd->fd, atcmd_output);
+ if (number == count)
+ sprintf(atcmd_output, msg_table[114], count); // %d item(s) removed from the player.
+ else
+ sprintf(atcmd_output, msg_table[115], count, count, number); // %d item(s) removed. Player had only %d on %d items.
+ clif_displaymessage(fd, atcmd_output);
+ } else {
+ clif_displaymessage(fd, msg_table[116]); // Character does not have the item.
+ return -1;
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[81]); // Your GM level don't authorise you to do this action on this player.
+ return -1;
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[3]); // Character not found.
+ return -1;
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[19]); // Invalid item ID or name.
+ return -1;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * @jail <char_name> by [Yor]
+ * Special warp! No check with nowarp and nowarpto flag
+ *------------------------------------------
+ */
+int atcommand_jail(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ struct map_session_data *pl_sd;
+ int x, y;
+ unsigned short m_index;
+ nullpo_retr(-1, sd);
+
+ memset(atcmd_player_name, '\0', sizeof(atcmd_player_name));
+
+ if (!message || !*message || sscanf(message, "%23[^\n]", atcmd_player_name) < 1) {
+ clif_displaymessage(fd, "Please, enter a player name (usage: @jail <char_name>).");
+ return -1;
+ }
+
+ if ((pl_sd = map_nick2sd(atcmd_player_name)) != NULL) {
+ if (pc_isGM(sd) >= pc_isGM(pl_sd)) { // you can jail only lower or same GM
+ switch(rand() % 2) {
+ case 0:
+ x = 24;
+ y = 75;
+ break;
+ default:
+ x = 49;
+ y = 75;
+ break;
+ }
+ m_index = mapindex_name2id(MAP_JAIL);
+ if (pc_setpos(pl_sd, m_index, x, y, 3) == 0) {
+ pc_setsavepoint(pl_sd, m_index, x, y); // Save Char Respawn Point in the jail room [Lupus]
+ clif_displaymessage(pl_sd->fd, msg_table[117]); // GM has send you in jails.
+ clif_displaymessage(fd, msg_table[118]); // Player warped in jails.
+ } else {
+ clif_displaymessage(fd, msg_table[1]); // Map not found.
+ return -1;
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[81]); // Your GM level don't authorise you to do this action on this player.
+ return -1;
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[3]); // Character not found.
+ return -1;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * @unjail/@discharge <char_name> by [Yor]
+ * Special warp! No check with nowarp and nowarpto flag
+ *------------------------------------------
+ */
+int atcommand_unjail(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ struct map_session_data *pl_sd;
+ unsigned short m_index;
+
+ memset(atcmd_player_name, '\0', sizeof(atcmd_player_name));
+
+ if (!message || !*message || sscanf(message, "%23[^\n]", atcmd_player_name) < 1) {
+ clif_displaymessage(fd, "Please, enter a player name (usage: @unjail/@discharge <char_name>).");
+ return -1;
+ }
+
+ if ((pl_sd = map_nick2sd(atcmd_player_name)) != NULL) {
+ m_index = mapindex_name2id(MAP_PRONTERA);
+ if (pc_isGM(sd) >= pc_isGM(pl_sd)) { // you can jail only lower or same GM
+ if (pl_sd->bl.m != map_mapname2mapid(MAP_JAIL)) {
+ clif_displaymessage(fd, msg_table[119]); // This player is not in jails.
+ return -1;
+ } else if (pc_setpos(pl_sd, m_index, 0, 0, 3) == 0) { //old coords: 156,191
+ pc_setsavepoint(pl_sd, m_index, 0, 0); // Save char respawn point in Prontera
+ clif_displaymessage(pl_sd->fd, msg_table[120]); // GM has discharge you.
+ clif_displaymessage(fd, msg_table[121]); // Player warped to Prontera.
+ } else {
+ clif_displaymessage(fd, msg_table[1]); // Map not found.
+ return -1;
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[81]); // Your GM level don't authorise you to do this action on this player.
+ return -1;
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[3]); // Character not found.
+ return -1;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * @disguise <mob_id> by [Valaris] (simplified by [Yor])
+ *------------------------------------------
+ */
+int atcommand_disguise(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ int id = 0;
+ nullpo_retr(-1, sd);
+
+ if (!message || !*message) {
+ clif_displaymessage(fd, "Please, enter a Monster/NPC name/id (usage: @disguise <monster_name_or_monster_ID>).");
+ return -1;
+ }
+
+ if ((id = atoi(message)) > 0)
+ { //Acquired an ID
+ if (!mobdb_checkid(id) && !npcdb_checkid(id))
+ id = 0; //Invalid id for either mobs or npcs.
+ } else { //Acquired a Name
+ if ((id = mobdb_searchname(message)) == 0)
+ {
+ struct npc_data* nd = npc_name2id(message);
+ if (nd != NULL)
+ id = nd->n;
+ }
+ }
+
+ if (id == 0)
+ { // Monster/NPC name/id hasn't been found.
+ clif_displaymessage(fd, msg_table[123]);
+ return -1;
+ }
+
+ /* The previous way....
+ if ((mob_id = mobdb_searchname(message)) == 0) // check name first (to avoid possible name begining by a number)
+ mob_id = atoi(message);
+
+ if ((mob_id >= 46 && mob_id <= 125) || (mob_id >= 700 && mob_id <= 718) || // NPC
+ (mob_id >= 721 && mob_id <= 755) || (mob_id >= 757 && mob_id <= 811) || // NPC
+ (mob_id >= 813 && mob_id <= 858) || // NPC
+ (mob_id > 1000 && mob_id < 1582)) { // monsters
+ */
+ pc_stop_walking(sd,0);
+ clif_clearchar(&sd->bl, 0);
+ sd->disguise = id;
+ sd->state.disguised = 1; // set to override items with disguise script [Valaris]
+ clif_changeoption(&sd->bl);
+ clif_spawnpc(sd);
+ clif_displaymessage(fd, msg_table[122]); // Disguise applied.
+
+ return 0;
+}
+
+/*==========================================
+ * DisguiseAll
+ *------------------------------------------
+ */
+
+int atcommand_disguiseall(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ int mob_id=0, i=0, users;
+ struct map_session_data *pl_sd, **pl_allsd;
+ nullpo_retr(-1, sd);
+
+ if (!message || !*message) {
+ clif_displaymessage(fd, "Please, enter a Monster/NPC name/id (usage: @disguiseall <monster_name_or_monster_ID>).");
+ return -1;
+ }
+
+ if ((mob_id = mobdb_searchname(message)) == 0) // check name first (to avoid possible name begining by a number)
+ mob_id = atoi(message);
+
+ if (mobdb_checkid(mob_id) || npcdb_checkid(mob_id)) { //if mob or npc...
+ pl_allsd = map_getallusers(&users);
+ for(i=0; i < users; i++) {
+ if((pl_sd = pl_allsd[i])) {
+ pc_stop_walking(pl_sd,0);
+ clif_clearchar(&pl_sd->bl, 0);
+ pl_sd->disguise = mob_id;
+ pl_sd->state.disguised = 1; // set to override items with disguise script [Valaris]
+ clif_changeoption(&pl_sd->bl);
+ clif_spawnpc(pl_sd);
+ }
+ }
+ clif_displaymessage(fd, msg_table[122]); // Disguise applied.
+ } else {
+ return -1;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * @undisguise by [Yor]
+ *------------------------------------------
+ */
+int atcommand_undisguise(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ nullpo_retr(-1, sd);
+ if (sd->disguise) {
+ pc_stop_walking(sd,0);
+ clif_clearchar(&sd->bl, 0);
+ sd->disguise = 0;
+ clif_changeoption(&sd->bl);
+ clif_spawnpc(sd);
+ clif_displaymessage(fd, msg_table[124]); // Undisguise applied.
+ } else {
+ clif_displaymessage(fd, msg_table[125]); // You're not disguised.
+ return -1;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * UndisguiseAll
+ *------------------------------------------
+ */
+int atcommand_undisguiseall(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ struct map_session_data *pl_sd, **pl_allsd;
+ int i, users;
+ nullpo_retr(-1, sd);
+
+ pl_allsd = map_getallusers(&users);
+
+ for(i=0; i < users; i++) {
+ if((pl_sd = pl_allsd[i]) && pl_sd->disguise) {
+ pc_stop_walking(pl_sd,0);
+ clif_clearchar(&pl_sd->bl, 0);
+ pl_sd->disguise = 0;
+ clif_changeoption(&pl_sd->bl);
+ clif_spawnpc(pl_sd);
+ }
+ }
+ clif_displaymessage(fd, msg_table[124]); // Undisguise applied.
+
+ return 0;
+}
+
+/*==========================================
+ * @broadcast by [Valaris]
+ *------------------------------------------
+ */
+int atcommand_broadcast(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ nullpo_retr(-1, sd);
+
+ memset(atcmd_output, '\0', sizeof(atcmd_output));
+
+ if (!message || !*message) {
+ clif_displaymessage(fd, "Please, enter a message (usage: @broadcast <message>).");
+ return -1;
+ }
+
+ sprintf(atcmd_output, "%s : %s", sd->status.name, message);
+ intif_GMmessage(atcmd_output, strlen(atcmd_output) + 1, 0);
+
+ return 0;
+}
+
+/*==========================================
+ * @localbroadcast by [Valaris]
+ *------------------------------------------
+ */
+int atcommand_localbroadcast(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ nullpo_retr(-1, sd);
+
+ memset(atcmd_output, '\0', sizeof(atcmd_output));
+
+ if (!message || !*message) {
+ clif_displaymessage(fd, "Please, enter a message (usage: @localbroadcast <message>).");
+ return -1;
+ }
+
+ sprintf(atcmd_output, "%s : %s", sd->status.name, message);
+
+ clif_GMmessage(&sd->bl, atcmd_output, strlen(atcmd_output) + 1, 1); // 1: ALL_SAMEMAP
+
+ return 0;
+}
+
+/*==========================================
+ * @chardisguise <mob_id> <character> by Kalaspuff (based off Valaris' and Yor's work)
+ *------------------------------------------
+ */
+int atcommand_chardisguise(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ int mob_id;
+ char mob_name[NAME_LENGTH];
+ struct map_session_data* pl_sd;
+ nullpo_retr(-1, sd);
+
+ memset(atcmd_player_name, '\0', sizeof(atcmd_player_name));
+ memset(mob_name, '\0', sizeof(mob_name));
+
+ if (!message || !*message || sscanf(message, "%s %23[^\n]", mob_name, atcmd_player_name) < 2) {
+ clif_displaymessage(fd, "Please, enter a Monster/NPC name/id and a player name (usage: @chardisguise <monster_name_or_monster_ID> <char name>).");
+ return -1;
+ }
+
+ if ((mob_id = atoi(mob_name)) > 0)
+ { //Acquired an ID
+ if (!mobdb_checkid(mob_id) && !npcdb_checkid(mob_id))
+ mob_id = 0; //Invalid id for either mobs or npcs.
+ } else { //Acquired a Name
+ if ((mob_id = mobdb_searchname(mob_name)) == 0)
+ {
+ struct npc_data* nd = npc_name2id(mob_name);
+ if (nd != NULL)
+ mob_id = nd->n;
+ }
+ }
+
+ if (mob_id == 0)
+ {
+ clif_displaymessage(fd, msg_table[123]); // Monster/NPC name/id hasn't been found.
+ return -1;
+ }
+
+ if ((pl_sd = map_nick2sd(atcmd_player_name)) != NULL) {
+ if (pc_isGM(sd) >= pc_isGM(pl_sd)) { // you can disguise only lower or same level
+ pc_stop_walking(pl_sd,0);
+ clif_clearchar(&pl_sd->bl, 0);
+ pl_sd->disguise = mob_id;
+ pl_sd->state.disguised = 1; // set to override items with disguise script [Valaris]
+ clif_changeoption(&pl_sd->bl);
+ clif_spawnpc(pl_sd);
+ clif_displaymessage(fd, msg_table[140]); // Character's disguise applied.
+ } else {
+ clif_displaymessage(fd, msg_table[81]); // Your GM level don't authorise you to do this action on this player.
+ return -1;
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[3]); // Character not found.
+ return -1;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * @charundisguise <character> by Kalaspuff (based off Yor's work)
+ *------------------------------------------
+ */
+int atcommand_charundisguise(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ struct map_session_data* pl_sd;
+ nullpo_retr(-1, sd);
+
+ memset(atcmd_player_name, '\0', sizeof(atcmd_player_name));
+
+ if (!message || !*message || sscanf(message, "%23[^\n]", atcmd_player_name) < 1) {
+ clif_displaymessage(fd, "Please, enter a player name (usage: @charundisguise <char name>).");
+ return -1;
+ }
+
+ if ((pl_sd = map_nick2sd(atcmd_player_name)) != NULL) {
+ if (pc_isGM(sd) >= pc_isGM(pl_sd)) { // you can undisguise only lower or same level
+ if (pl_sd->disguise) {
+ pc_stop_walking(pl_sd,0);
+ clif_clearchar(&pl_sd->bl, 0);
+ pl_sd->disguise = 0;
+ clif_changeoption(&pl_sd->bl);
+ clif_spawnpc(pl_sd);
+ clif_displaymessage(fd, msg_table[141]); // Character's undisguise applied.
+ } else {
+ clif_displaymessage(fd, msg_table[142]); // Character is not disguised.
+ return -1;
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[81]); // Your GM level don't authorise you to do this action on this player.
+ return -1;
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[3]); // Character not found.
+ return -1;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * @email <actual@email> <new@email> by [Yor]
+ *------------------------------------------
+ */
+int atcommand_email(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ char actual_email[100];
+ char new_email[100];
+ nullpo_retr(-1, sd);
+
+ memset(actual_email, '\0', sizeof(actual_email));
+ memset(new_email, '\0', sizeof(new_email));
+
+ if (!message || !*message || sscanf(message, "%99s %99s", actual_email, new_email) < 2) {
+ clif_displaymessage(fd, "Please enter 2 emails (usage: @email <actual@email> <new@email>).");
+ return -1;
+ }
+
+ if (e_mail_check(actual_email) == 0) {
+ clif_displaymessage(fd, msg_table[144]); // Invalid actual email. If you have default e-mail, give a@a.com.
+ return -1;
+ } else if (e_mail_check(new_email) == 0) {
+ clif_displaymessage(fd, msg_table[145]); // Invalid new email. Please enter a real e-mail.
+ return -1;
+ } else if (strcmpi(new_email, "a@a.com") == 0) {
+ clif_displaymessage(fd, msg_table[146]); // New email must be a real e-mail.
+ return -1;
+ } else if (strcmpi(actual_email, new_email) == 0) {
+ clif_displaymessage(fd, msg_table[147]); // New email must be different of the actual e-mail.
+ return -1;
+ } else {
+ chrif_changeemail(sd->status.account_id, actual_email, new_email);
+ clif_displaymessage(fd, msg_table[148]); // Information sended to login-server via char-server.
+ }
+
+ return 0;
+}
+
+/*==========================================
+ *@effect
+ *------------------------------------------
+ */
+int atcommand_effect(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ struct map_session_data *pl_sd, **pl_allsd;
+ int type = 0, flag = 0, i, users;
+ nullpo_retr(-1, sd);
+
+ if (!message || !*message || sscanf(message, "%d %d", &type,&flag) < 2) {
+ clif_displaymessage(fd, "Please, enter at least a option (usage: @effect <type+>).");
+ return -1;
+ }
+ if(flag <=0){
+ clif_specialeffect(&sd->bl, type, flag);
+ clif_displaymessage(fd, msg_table[229]); // Your effect has changed.
+ }
+ else{
+ pl_allsd = map_getallusers(&users);
+ for (i = 0; i < users; i++) {
+ if ((pl_sd = pl_allsd[i])) {
+ clif_specialeffect(&pl_sd->bl, type, flag);
+ clif_displaymessage(pl_sd->fd, msg_table[229]); // Your effect has changed.
+ }
+ }
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * @charcartlist <character>: Displays the items list of a player's cart.
+ *------------------------------------------
+ */
+int
+atcommand_character_cart_list(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ char outputtmp[200];
+ struct map_session_data *pl_sd;
+ struct item_data *item_data, *item_temp;
+ int i, j, count, counter, counter2;
+ nullpo_retr(-1, sd);
+
+ memset(atcmd_player_name, '\0', sizeof(atcmd_player_name));
+ memset(atcmd_output, '\0', sizeof(atcmd_output));
+ memset(outputtmp, '\0', sizeof(outputtmp));
+
+ if (!message || !*message || sscanf(message, "%23[^\n]", atcmd_player_name) < 1) {
+ clif_displaymessage(fd, "Please, enter a player name (usage: @charitemlist <char name>).");
+ return -1;
+ }
+
+ if ((pl_sd = map_nick2sd(atcmd_player_name)) != NULL) {
+ if (pc_isGM(sd) >= pc_isGM(pl_sd)) { // you can look items only lower or same level
+ counter = 0;
+ count = 0;
+ for (i = 0; i < MAX_CART; i++) {
+ if (pl_sd->status.cart[i].nameid > 0 && (item_data = itemdb_search(pl_sd->status.cart[i].nameid)) != NULL) {
+ counter = counter + pl_sd->status.cart[i].amount;
+ count++;
+ if (count == 1) {
+ sprintf(atcmd_output, "------ Cart items list of '%s' ------", pl_sd->status.name);
+ clif_displaymessage(fd, atcmd_output);
+ }
+ if (pl_sd->status.cart[i].refine)
+ sprintf(atcmd_output, "%d %s %+d (%s %+d, id: %d)", pl_sd->status.cart[i].amount, item_data->name, pl_sd->status.cart[i].refine, item_data->jname, pl_sd->status.cart[i].refine, pl_sd->status.cart[i].nameid);
+ else
+ sprintf(atcmd_output, "%d %s (%s, id: %d)", pl_sd->status.cart[i].amount, item_data->name, item_data->jname, pl_sd->status.cart[i].nameid);
+ clif_displaymessage(fd, atcmd_output);
+ memset(atcmd_output, '\0', sizeof(atcmd_output));
+ counter2 = 0;
+ for (j = 0; j < item_data->slot; j++) {
+ if (pl_sd->status.cart[i].card[j]) {
+ if ( (item_temp = itemdb_search(pl_sd->status.cart[i].card[j])) != NULL) {
+ if (atcmd_output[0] == '\0')
+ sprintf(outputtmp, " -> (card(s): #%d %s (%s), ", ++counter2, item_temp->name, item_temp->jname);
+ else
+ sprintf(outputtmp, "#%d %s (%s), ", ++counter2, item_temp->name, item_temp->jname);
+ strcat(atcmd_output, outputtmp);
+ }
+ }
+ }
+ if (atcmd_output[0] != '\0') {
+ atcmd_output[strlen(atcmd_output) - 2] = ')';
+ atcmd_output[strlen(atcmd_output) - 1] = '\0';
+ clif_displaymessage(fd, atcmd_output);
+ }
+ }
+ }
+ if (count == 0)
+ clif_displaymessage(fd, "No item found in the cart of this player.");
+ else {
+ sprintf(atcmd_output, "%d item(s) found in %d kind(s) of items.", counter, count);
+ clif_displaymessage(fd, atcmd_output);
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[81]); // Your GM level don't authorise you to do this action on this player.
+ return -1;
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[3]); // Character not found.
+ return -1;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * @killer by MouseJstr
+ * enable killing players even when not in pvp
+ *------------------------------------------
+ */
+int
+atcommand_killer(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ nullpo_retr(-1, sd);
+ sd->special_state.killer = !sd->special_state.killer;
+
+ if(sd->special_state.killer)
+ clif_displaymessage(fd, msg_table[241]);
+ else
+ clif_displaymessage(fd, msg_table[242]);
+
+ return 0;
+}
+
+/*==========================================
+ * @killable by MouseJstr
+ * enable other people killing you
+ *------------------------------------------
+ */
+int
+atcommand_killable(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ nullpo_retr(-1, sd);
+ sd->special_state.killable = !sd->special_state.killable;
+
+ if(sd->special_state.killable)
+ clif_displaymessage(fd, msg_table[242]);
+ else
+ clif_displaymessage(fd, msg_table[241]);
+
+ return 0;
+}
+
+/*==========================================
+ * @charkillable by MouseJstr
+ * enable another player to be killed
+ *------------------------------------------
+ */
+int
+atcommand_charkillable(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ struct map_session_data *pl_sd = NULL;
+ nullpo_retr(-1, sd);
+
+ if (!message || !*message)
+ return -1;
+
+ if((pl_sd=map_nick2sd((char *) message)) == NULL)
+ return -1;
+
+ pl_sd->special_state.killable = !pl_sd->special_state.killable;
+
+ if(pl_sd->special_state.killable)
+ clif_displaymessage(fd, "The player is now killable");
+ else
+ clif_displaymessage(fd, "The player is no longer killable");
+
+ return 0;
+}
+
+
+/*==========================================
+ * @skillon by MouseJstr
+ * turn skills on for the map
+ *------------------------------------------
+ */
+int
+atcommand_skillon(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ nullpo_retr(-1, sd);
+ map[sd->bl.m].flag.noskill = 0;
+ clif_displaymessage(fd, msg_table[244]);
+ return 0;
+}
+
+/*==========================================
+ * @skilloff by MouseJstr
+ * Turn skills off on the map
+ *------------------------------------------
+ */
+int
+atcommand_skilloff(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ nullpo_retr(-1, sd);
+ map[sd->bl.m].flag.noskill = 1;
+ clif_displaymessage(fd, msg_table[243]);
+ return 0;
+}
+
+/*==========================================
+ * @npcmove by MouseJstr
+ *
+ * move a npc
+ *------------------------------------------
+ */
+int
+atcommand_npcmove(const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ int x = 0, y = 0;
+ struct npc_data *nd = 0;
+ nullpo_retr(-1, sd);
+
+
+ if (!message || !*message)
+ return -1;
+
+ memset(atcmd_player_name, '\0', sizeof atcmd_player_name);
+
+ if (sscanf(message, "%d %d %23[^\n]", &x, &y, atcmd_player_name) < 3) {
+ clif_displaymessage(fd, "Usage: @npcmove <X> <Y> <npc_name>");
+ return -1;
+ }
+
+ //Who's idea was this? nullpo's are meant to check pointers that SHOULDN'T be null unless there's a bug... [Skotlex]
+// nullpo_retr(-1, (nd = npc_name2id(atcmd_player_name)));
+
+ if ((nd = npc_name2id(atcmd_player_name)) == NULL)
+ return -1;
+
+ npc_enable(atcmd_player_name, 0);
+ nd->bl.x = x;
+ nd->bl.y = y;
+ npc_enable(atcmd_player_name, 1);
+
+ return 0;
+}
+
+/*==========================================
+ * @addwarp by MouseJstr
+ *
+ * Create a new static warp point.
+ *------------------------------------------
+ */
+int
+atcommand_addwarp(const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ char w1[64], w3[64], w4[64];
+ int x,y,ret;
+ nullpo_retr(-1, sd);
+
+ if (!message || !*message)
+ return -1;
+
+ if (sscanf(message, "%99s %d %d[^\n]", atcmd_player_name, &x, &y ) < 3)
+ return -1;
+
+ sprintf(w1,"%s,%d,%d", mapindex_id2name(sd->mapindex), sd->bl.x, sd->bl.y);
+ sprintf(w3,"%s%d%d%d%d", atcmd_player_name,sd->bl.x, sd->bl.y, x, y);
+ sprintf(w4,"1,1,%s.gat,%d,%d", atcmd_player_name, x, y);
+
+ ret = npc_parse_warp(w1, "warp", w3, w4);
+
+ sprintf(atcmd_output, "New warp NPC => %s",w3);
+
+ clif_displaymessage(fd, atcmd_output);
+
+ return ret;
+}
+
+/*==========================================
+ * @follow by [MouseJstr]
+ *
+ * Follow a player .. staying no more then 5 spaces away
+ *------------------------------------------
+ */
+int
+atcommand_follow(const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ struct map_session_data *pl_sd = NULL;
+ nullpo_retr(-1, sd);
+
+ if (!message || !*message) {
+ if (sd->followtarget == -1)
+ return -1;
+
+ pc_stop_following (sd);
+ clif_displaymessage(fd, "Stop following");
+ return 0;
+ }
+ if ((pl_sd = map_nick2sd((char *) message)) != NULL) {
+ if (sd->followtarget == pl_sd->bl.id)
+ pc_stop_following (sd);
+ else
+ pc_follow(sd, pl_sd->bl.id);
+ return 0;
+ }
+
+ return 1;
+}
+
+
+/*==========================================
+ * @dropall by [MouseJstr]
+ *
+ * Drop all your possession on the ground
+ *------------------------------------------
+ */
+int
+atcommand_dropall(const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ int i;
+ nullpo_retr(-1, sd);
+ for (i = 0; i < MAX_INVENTORY; i++) {
+ if (sd->status.inventory[i].amount) {
+ if(sd->status.inventory[i].equip != 0)
+ pc_unequipitem(sd, i, 3);
+ pc_dropitem(sd, i, sd->status.inventory[i].amount);
+ }
+ }
+ return 0;
+}
+/*==========================================
+ * @chardropall by [MouseJstr]
+ *
+ * Throw all the characters possessions on the ground. Normally
+ * done in response to them being disrespectful of a GM
+ *------------------------------------------
+ */
+int
+atcommand_chardropall(const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ int i;
+ struct map_session_data *pl_sd = NULL;
+ nullpo_retr(-1, sd);
+
+ if (!message || !*message)
+ return -1;
+ if((pl_sd=map_nick2sd((char *) message)) == NULL)
+ return -1;
+ for (i = 0; i < MAX_INVENTORY; i++) {
+ if (pl_sd->status.inventory[i].amount) {
+ if(pl_sd->status.inventory[i].equip != 0)
+ pc_unequipitem(pl_sd, i, 3);
+ pc_dropitem(pl_sd, i, pl_sd->status.inventory[i].amount);
+ }
+ }
+
+ clif_displaymessage(pl_sd->fd, "Ever play 52 card pickup?");
+ clif_displaymessage(fd, "It is done");
+ //clif_displaymessage(fd, "It is offical.. your a jerk");
+
+ return 0;
+}
+/*==========================================
+ * @storeall by [MouseJstr]
+ *
+ * Put everything into storage to simplify your inventory to make
+ * debugging easie
+ *------------------------------------------
+ */
+int
+atcommand_storeall(const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ int i;
+ nullpo_retr(-1, sd);
+ if (storage_storageopen(sd) == 1) {
+ clif_displaymessage(fd, "run this command again..");
+ return 0;
+ }
+ for (i = 0; i < MAX_INVENTORY; i++) {
+ if (sd->status.inventory[i].amount) {
+ if(sd->status.inventory[i].equip != 0)
+ pc_unequipitem(sd, i, 3);
+ storage_storageadd(sd, i, sd->status.inventory[i].amount);
+ }
+ }
+ storage_storageclose(sd);
+
+ clif_displaymessage(fd, "It is done");
+ return 0;
+}
+/*==========================================
+ * @charstoreall by [MouseJstr]
+ *
+ * A way to screw with players who piss you off
+ *------------------------------------------
+ */
+int
+atcommand_charstoreall(const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ int i;
+ struct map_session_data *pl_sd = NULL;
+ nullpo_retr(-1, sd);
+
+ if (!message || !*message)
+ return -1;
+ if((pl_sd=map_nick2sd((char *) message)) == NULL)
+ return -1;
+
+ if (storage_storageopen(pl_sd) == 1) {
+ clif_displaymessage(fd, "Had to open the characters storage window...");
+ clif_displaymessage(fd, "run this command again..");
+ return 0;
+ }
+ for (i = 0; i < MAX_INVENTORY; i++) {
+ if (pl_sd->status.inventory[i].amount) {
+ if(pl_sd->status.inventory[i].equip != 0)
+ pc_unequipitem(pl_sd, i, 3);
+ storage_storageadd(pl_sd, i, sd->status.inventory[i].amount);
+ }
+ }
+ storage_storageclose(pl_sd);
+
+ clif_displaymessage(pl_sd->fd, "Everything you own has been put away for safe keeping.");
+ clif_displaymessage(pl_sd->fd, "go to the nearest kafka to retrieve it..");
+ clif_displaymessage(pl_sd->fd, " -- the management");
+
+ clif_displaymessage(fd, "It is done");
+
+ return 0;
+}
+/*==========================================
+ * @skillid by [MouseJstr]
+ *
+ * lookup a skill by name
+ *------------------------------------------
+ */
+int
+atcommand_skillid(const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ int skillen, idx;
+ nullpo_retr(-1, sd);
+
+ if (!message || !*message)
+ return -1;
+ skillen = strlen(message);
+
+ for (idx = 0; idx < MAX_SKILL_DB; idx++) {
+ if ((skill_db[idx].name != NULL && strnicmp(skill_db[idx].name, message, skillen) == 0) ||
+ (skill_db[idx].desc != NULL && strnicmp(skill_db[idx].desc, message, skillen) == 0))
+ {
+ sprintf(atcmd_output, "skill %d: %s", idx, skill_db[idx].desc);
+ clif_displaymessage(fd, atcmd_output);
+ }
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * @useskill by [MouseJstr]
+ *
+ * A way of using skills without having to find them in the skills menu
+ *------------------------------------------
+ */
+int
+atcommand_useskill(const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ struct map_session_data *pl_sd = NULL;
+ int skillnum;
+ int skilllv;
+ char target[255];
+ nullpo_retr(-1, sd);
+
+ if (!message || !*message)
+ return -1;
+ if(sscanf(message, "%d %d %99[^\n]", &skillnum, &skilllv, target) != 3) {
+ clif_displaymessage(fd, "Usage: @useskill <skillnum> <skillv> <target>");
+ return -1;
+ }
+ if((pl_sd=map_nick2sd(target)) == NULL) {
+ return -1;
+ }
+
+ if (skill_get_inf(skillnum) & INF_GROUND_SKILL)
+ skill_use_pos(sd, pl_sd->bl.x, pl_sd->bl.y, skillnum, skilllv);
+ else
+ skill_use_id(sd, pl_sd->bl.id, skillnum, skilllv);
+
+ return 0;
+}
+
+/*==========================================
+ * @skilltree by [MouseJstr]
+ *
+ * prints the skill tree for a player required to get to a skill
+ *------------------------------------------
+ */
+int
+atcommand_skilltree(const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ struct map_session_data *pl_sd = NULL;
+ int skillnum, skillidx = -1;
+ int meets = 1, j, c=0;
+ char target[NAME_LENGTH], *tbl;
+ struct skill_tree_entry *ent;
+ nullpo_retr(-1, sd);
+
+ if (!message || !*message)
+ return -1;
+
+ if(sscanf(message, "%d %23[^\r\n]", &skillnum, target) != 2) {
+ clif_displaymessage(fd, "Usage: @skilltree <skillnum> <target>");
+ return -1;
+ }
+ if((pl_sd=map_nick2sd(target)) == NULL)
+ return -1;
+
+ c = pc_calc_skilltree_normalize_job(pl_sd);
+
+ tbl = job_name(c);
+
+ sprintf(atcmd_output, "Player is using %s skill tree (%d basic points)",
+ tbl, pc_checkskill(pl_sd, 1));
+ clif_displaymessage(fd, atcmd_output);
+
+ for (j = 0; skill_tree[c][j].id != 0; j++) {
+ if (skill_tree[c][j].id == skillnum) {
+ skillidx = j;
+ break;
+ }
+ }
+
+ if (skillidx == -1) {
+ sprintf(atcmd_output, "I do not believe the player can use that skill");
+ clif_displaymessage(fd, atcmd_output);
+ return 0;
+ }
+
+ ent = &skill_tree[c][skillidx];
+
+ for(j=0;j<5;j++)
+ if( ent->need[j].id &&
+ pc_checkskill(sd,ent->need[j].id) < ent->need[j].lv)
+ {
+ char *desc = (skill_db[ ent->need[j].id ].desc) ? skill_db[ ent->need[j].id ].desc : "Unknown skill";
+ sprintf(atcmd_output, "player requires level %d of skill %s",
+ ent->need[j].lv, desc);
+ clif_displaymessage(fd, atcmd_output);
+ meets = 0;
+ }
+
+ if (meets == 1) {
+ sprintf(atcmd_output, "I believe the player meets all the requirements for that skill");
+ clif_displaymessage(fd, atcmd_output);
+ }
+
+ return 0;
+}
+
+// Hand a ring with partners name on it to this char
+void getring (struct map_session_data *sd)
+{
+ int flag,item_id = 0;
+ struct item item_tmp;
+ if(sd->status.sex==0)
+ item_id = 2635;
+ else
+ item_id = 2634;
+
+ memset(&item_tmp,0,sizeof(item_tmp));
+ item_tmp.nameid=item_id;
+ item_tmp.identify=1;
+ item_tmp.card[0]=255;
+ item_tmp.card[2]=sd->status.partner_id;
+ item_tmp.card[3]=sd->status.partner_id >> 16;
+
+ //Logs (A)dmins items [Lupus]
+ if(log_config.pick > 0 ) {
+ log_pick(sd, "A", 0, item_id, 1, &item_tmp);
+ }
+ //Logs
+
+ if((flag = pc_additem(sd,&item_tmp,1))) {
+ clif_additem(sd,0,0,flag);
+ map_addflooritem(&item_tmp,1,sd->bl.m,sd->bl.x,sd->bl.y,NULL,NULL,NULL,0);
+ }
+
+}
+
+
+/*==========================================
+ * @marry by [MouseJstr], fixed by Lupus
+ *
+ * Marry two players
+ *------------------------------------------
+ */
+int
+atcommand_marry(const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ struct map_session_data *pl_sd1 = NULL;
+ struct map_session_data *pl_sd2 = NULL;
+ char player1[128], player2[128]; //Length used for return error msgs
+
+ nullpo_retr(-1, sd);
+
+ if (!message || !*message || sscanf(message, "%23[^,],%23[^\r\n]", player1, player2) != 2) {
+ clif_displaymessage(fd, "Usage: @marry <player1>,<player2>");
+ return -1;
+ }
+
+ if((pl_sd1=map_nick2sd((char *) player1)) == NULL) {
+ sprintf(player2, "Cannot find player '%s' online", player1);
+ clif_displaymessage(fd, player2);
+ return -1;
+ }
+
+ if((pl_sd2=map_nick2sd((char *) player2)) == NULL) {
+ sprintf(player1, "Cannot find player '%s' online", player2);
+ clif_displaymessage(fd, player1);
+ return -1;
+ }
+
+ if (pc_marriage(pl_sd1, pl_sd2) == 0) {
+ clif_displaymessage(fd, "They are married.. wish them well");
+ clif_wedding_effect(&sd->bl); //wedding effect and music [Lupus]
+ // Auto-give named rings (Aru)
+ getring (pl_sd1);
+ getring (pl_sd2);
+ return 0;
+ }
+ return -1;
+}
+
+/*==========================================
+ * @divorce by [MouseJstr], fixed by [Lupus]
+ *
+ * divorce two players
+ *------------------------------------------
+ */
+int
+atcommand_divorce(const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ struct map_session_data *pl_sd = NULL;
+
+ nullpo_retr(-1, sd);
+
+ if (!message || !*message || sscanf(message, "%23[^\r\n]", atcmd_player_name) != 1) {
+ clif_displaymessage(fd, "Usage: @divorce <player>.");
+ return -1;
+ }
+
+ if((pl_sd=map_nick2sd((char *) atcmd_player_name)) != NULL) {
+ if (pc_divorce(pl_sd) != 0) {
+ sprintf(atcmd_output, "The divorce has failed.. Cannot find player '%s' or his(her) partner online.", atcmd_player_name);
+ clif_displaymessage(fd, atcmd_output);
+ return -1;
+ } else {
+ sprintf(atcmd_output, "'%s' and his(her) partner are now divorced.", atcmd_player_name);
+ clif_displaymessage(fd, atcmd_output);
+ return 0;
+ }
+ }
+ sprintf(atcmd_output, "Cannot find player '%s' online", atcmd_player_name);
+ clif_displaymessage(fd, atcmd_output);
+ return -1;
+}
+
+
+#ifdef DMALLOC
+unsigned long dmark_;
+int
+atcommand_dmstart(const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ dmark_ = dmalloc_mark();
+
+ clif_displaymessage(fd, "debug mark set");
+
+ return 0;
+}
+
+int
+atcommand_dmtick(const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ dmalloc_log_changed ( dmark_, 1, 0, 1 ) ;
+ dmark_ = dmalloc_mark();
+ clif_displaymessage(fd, "malloc changes logged");
+
+ return 0;
+}
+#endif
+
+/*==========================================
+ * @grind by [MouseJstr]
+ *------------------------------------------
+ */
+int
+atcommand_grind(const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ struct map_session_data *pl_sd = NULL;
+ int skillnum;
+ int inf;
+ char target[NAME_LENGTH];
+ nullpo_retr(-1, sd);
+
+ if (!message || !*message)
+ return -1;
+ if(sscanf(message, "%23s", target) != 1) {
+ clif_displaymessage(fd, "Usage: @grind <target>");
+ return -1;
+ }
+ if((pl_sd=map_nick2sd(target)) == NULL)
+ return -1;
+
+ for (skillnum = 1; skillnum < 500; skillnum++) {
+ sd->status.sp = sd->status.max_sp;
+ atcommand_alive(fd, sd, command, message);
+
+ inf = skill_get_inf(skillnum);
+
+ if (inf & INF_GROUND_SKILL)
+ skill_use_pos(sd, pl_sd->bl.x+5, pl_sd->bl.y+5, skillnum, 1);
+ else if (!(inf & INF_SUPPORT_SKILL))
+ skill_use_id(sd, pl_sd->bl.id, skillnum, 1);
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * @grind2 by [MouseJstr]
+ *------------------------------------------
+ */
+int
+atcommand_grind2(const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ int i, x, y, id;
+
+ for (i = 1000; i <2000; i++) {
+ x = sd->bl.x + (rand() % 10 - 5);
+ y = sd->bl.y + (rand() % 10 - 5);
+ id = mob_once_spawn(sd, "this", x, y, "--ja--", i, 1, "");
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * @changelook by [Celest]
+ *------------------------------------------
+ */
+int
+atcommand_changelook(const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ int i, j = 0, k = 0;
+ int pos[6] = { LOOK_HEAD_TOP,LOOK_HEAD_MID,LOOK_HEAD_BOTTOM,LOOK_WEAPON,LOOK_SHIELD,LOOK_SHOES };
+
+ if((i = sscanf(message, "%d %d", &j, &k)) < 1) {
+ clif_displaymessage(fd, "Usage: @changelook [<position>] <view id> -- [] = optional");
+ clif_displaymessage(fd, "Position: 1-Top 2-Middle 3-Bottom 4-Weapon 5-Shield");
+ return -1;
+ } else if (i == 2) {
+ if (j < 1) j = 1;
+ else if (j > 6) j = 6; // 6 = Shoes - for beta clients only perhaps
+ j = pos[j - 1];
+ } else if (i == 1) { // position not defined, use HEAD_TOP as default
+ k = j; // swap
+ j = LOOK_HEAD_TOP;
+ }
+
+ clif_changelook(&sd->bl,j,k);
+
+ return 0;
+}
+
+/*==========================================
+ *Turns on/off Autotrade for a specific player
+ *------------------------------------------
+ *by durf (changed by Lupus)
+ */
+int
+atcommand_autotrade(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ nullpo_retr(-1, sd);
+ if (sd->vender_id) //check if player's vending
+ {
+ sd->state.autotrade = 1;
+ clif_authfail_fd(fd, 15);
+ }
+ else
+ {
+ //"You should be vending to use @Autotrade."
+ clif_displaymessage(fd, msg_txt(549));
+ }
+ return 0;
+}
+
+
+/*==========================================
+ * Changes Master of your Guild to a specified guild member
+ *------------------------------------------
+ *by durf (changed by Lupus)
+ */
+int
+atcommand_changegm(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ struct guild *g;
+ struct map_session_data *pl_sd;
+ nullpo_retr(-1, sd);
+
+ if (sd->status.guild_id == 0 || (g = guild_search(sd->status.guild_id)) == NULL || strcmp(g->master,sd->status.name))
+ {
+ clif_displaymessage(fd, "You need to be a Guild Master to use this command.");
+ return -1;
+ }
+ if (strlen(message)==0)
+ {
+ clif_displaymessage(fd, "Command usage: @changegm <guildmember name>");
+ return -1;
+ }
+
+ if((pl_sd=map_nick2sd((char *) message)) == NULL || pl_sd->status.guild_id != sd->status.guild_id) {
+ clif_displaymessage(fd, "Target character must be online and be a guildmate.");
+ return -1;
+ }
+
+ guild_gm_change(sd->status.guild_id, pl_sd);
+ return 0;
+}
+
+/*==========================================
+ *Changes the leader of a party.
+ *------------------------------------------
+ *by Skotlex
+ */
+int
+atcommand_changeleader(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ struct party *p;
+ struct map_session_data *pl_sd;
+ int mi, pl_mi;
+ nullpo_retr(-1, sd);
+
+ if (sd->status.party_id == 0 || (p = party_search(sd->status.party_id)) == NULL)
+ {
+ clif_displaymessage(fd, "You need to be a party's leader to use this command.");
+ return -1;
+ }
+
+ for (mi = 0; mi < MAX_PARTY && p->member[mi].sd != sd; mi++);
+
+ if (mi == MAX_PARTY)
+ return -1; //Shouldn't happen
+
+ if (!p->member[mi].leader)
+ {
+ clif_displaymessage(fd, "You need to be the party's leader to use this command.");
+ return -1;
+ }
+
+ if (strlen(message)==0)
+ {
+ clif_displaymessage(fd, "Command usage: @changeleader <party member name>");
+ return -1;
+ }
+
+ if((pl_sd=map_nick2sd((char *) message)) == NULL || pl_sd->status.party_id != sd->status.party_id) {
+ clif_displaymessage(fd, "Target character must be online and be in your party.");
+ return -1;
+ }
+
+ for (pl_mi = 0; pl_mi < MAX_PARTY && p->member[pl_mi].sd != pl_sd; pl_mi++);
+
+ if (pl_mi == MAX_PARTY)
+ return -1; //Shouldn't happen
+
+ //Change leadership.
+ p->member[mi].leader = 0;
+ if (p->member[mi].sd->fd)
+ clif_displaymessage(p->member[mi].sd->fd, "Leadership transferred.");
+ p->member[pl_mi].leader = 1;
+ if (p->member[pl_mi].sd->fd)
+ clif_displaymessage(p->member[pl_mi].sd->fd, "You've become the party leader.");
+
+ intif_party_leaderchange(p->party_id,p->member[pl_mi].account_id,p->member[pl_mi].char_id);
+ //Update info.
+ clif_party_main_info(p,-1);
+ clif_party_info(p,-1);
+
+ return 0;
+}
+
+/*==========================================
+ *Turns on/off AutoLoot for a specific player
+ *------------------------------------------
+ *by Upa-Kun
+ */
+int
+atcommand_autoloot(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ int rate;
+ double drate;
+ nullpo_retr(-1, sd);
+ if (!message || !*message) {
+ if (sd->state.autoloot) {
+ sd->state.autoloot = 0;
+ clif_displaymessage(fd, "Autoloot is now off.");
+ return 0;
+ } else {
+ clif_displaymessage(fd, "Usage: autoloot <max drop-rate to loot>.");
+ return -1;
+ }
+ }
+ drate = atof(message);
+ rate = (int)(drate*100);
+ if (rate > 10000) rate = 10000;
+ else if (rate < 0) rate = 0;
+
+ sd->state.autoloot = rate;
+ if (sd->state.autoloot) {
+ snprintf(atcmd_output, sizeof atcmd_output, "Autolooting items with drop rates of %0.02f%% and below.",((double)sd->state.autoloot)/100.);
+ clif_displaymessage(fd, atcmd_output);
+ }else
+ clif_displaymessage(fd, "Autoloot is now off.");
+ return 0;
+}
+
+
+/*==========================================
+ * It is made to rain.
+ *------------------------------------------
+ */
+int
+atcommand_rain(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ nullpo_retr(-1, sd);
+ if (map[sd->bl.m].flag.rain) {
+ map[sd->bl.m].flag.rain=0;
+ clif_weather(sd->bl.m);
+ clif_displaymessage(fd, "The rain has stopped.");
+ } else {
+ map[sd->bl.m].flag.rain=1;
+ clif_weather(sd->bl.m);
+ clif_displaymessage(fd, "It is made to rain.");
+ }
+ return 0;
+}
+/*==========================================
+ * It is made to snow.
+ *------------------------------------------
+ */
+int
+atcommand_snow(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ nullpo_retr(-1, sd);
+ if (map[sd->bl.m].flag.snow) {
+ map[sd->bl.m].flag.snow=0;
+ clif_weather(sd->bl.m);
+ clif_displaymessage(fd, "Snow has stopped falling.");
+ } else {
+ map[sd->bl.m].flag.snow=1;
+ clif_weather(sd->bl.m);
+ clif_displaymessage(fd, "It is made to snow.");
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * Cherry tree snowstorm is made to fall. (Sakura)
+ *------------------------------------------
+ */
+int
+atcommand_sakura(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ nullpo_retr(-1, sd);
+ if (map[sd->bl.m].flag.sakura) {
+ map[sd->bl.m].flag.sakura=0;
+ clif_weather(sd->bl.m);
+ clif_displaymessage(fd, "Cherry tree leaves no longer fall.");
+ } else {
+ map[sd->bl.m].flag.sakura=1;
+ clif_weather(sd->bl.m);
+ clif_displaymessage(fd, "Cherry tree leaves is made to fall.");
+ }
+ return 0;
+}
+
+/*==========================================
+ * Clouds appear.
+ *------------------------------------------
+ */
+int
+atcommand_clouds(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ nullpo_retr(-1, sd);
+ if (map[sd->bl.m].flag.clouds) {
+ map[sd->bl.m].flag.clouds=0;
+ clif_weather(sd->bl.m);
+ clif_displaymessage(fd, "The clouds has disappear.");
+ } else {
+ map[sd->bl.m].flag.clouds=1;
+ clif_weather(sd->bl.m);
+ clif_displaymessage(fd, "Clouds appear.");
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * Different type of clouds using effect 516
+ *------------------------------------------
+ */
+int
+atcommand_clouds2(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ nullpo_retr(-1, sd);
+ if (map[sd->bl.m].flag.clouds2) {
+ map[sd->bl.m].flag.clouds2=0;
+ clif_weather(sd->bl.m);
+ clif_displaymessage(fd, "The alternative clouds disappear.");
+ } else {
+ map[sd->bl.m].flag.clouds2=1;
+ clif_weather(sd->bl.m);
+ clif_displaymessage(fd, "Alternative clouds appear.");
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * Fog hangs over.
+ *------------------------------------------
+ */
+int
+atcommand_fog(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ nullpo_retr(-1, sd);
+ if (map[sd->bl.m].flag.fog) {
+ map[sd->bl.m].flag.fog=0;
+ clif_weather(sd->bl.m);
+ clif_displaymessage(fd, "The fog has gone.");
+ } else {
+ map[sd->bl.m].flag.fog=1;
+ clif_weather(sd->bl.m);
+ clif_displaymessage(fd, "Fog hangs over.");
+ }
+ return 0;
+}
+
+/*==========================================
+ * Fallen leaves fall.
+ *------------------------------------------
+ */
+int
+atcommand_leaves(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ nullpo_retr(-1, sd);
+ if (map[sd->bl.m].flag.leaves) {
+ map[sd->bl.m].flag.leaves=0;
+ clif_weather(sd->bl.m);
+ clif_displaymessage(fd, "Leaves no longer fall.");
+ } else {
+ map[sd->bl.m].flag.leaves=1;
+ clif_weather(sd->bl.m);
+ clif_displaymessage(fd, "Fallen leaves fall.");
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * Fireworks appear.
+ *------------------------------------------
+ */
+int
+atcommand_fireworks(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ nullpo_retr(-1, sd);
+ if (map[sd->bl.m].flag.fireworks) {
+ map[sd->bl.m].flag.fireworks=0;
+ clif_weather(sd->bl.m);
+ clif_displaymessage(fd, "Fireworks are ended.");
+ } else {
+ map[sd->bl.m].flag.fireworks=1;
+ clif_weather(sd->bl.m);
+ clif_displaymessage(fd, "Fireworks are launched.");
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * Clearing Weather Effects by Dexity
+ *------------------------------------------
+ */
+int
+atcommand_clearweather(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ nullpo_retr(-1, sd);
+ map[sd->bl.m].flag.rain=0;
+ map[sd->bl.m].flag.snow=0;
+ map[sd->bl.m].flag.sakura=0;
+ map[sd->bl.m].flag.clouds=0;
+ map[sd->bl.m].flag.clouds2=0;
+ map[sd->bl.m].flag.fog=0;
+ map[sd->bl.m].flag.fireworks=0;
+ map[sd->bl.m].flag.leaves=0;
+ clif_weather(sd->bl.m);
+
+ return 0;
+}
+
+/*===============================================================
+ * Sound Command - plays a sound for everyone! [Codemaster]
+ *---------------------------------------------------------------
+ */
+int
+atcommand_sound(
+ const int fd, struct map_session_data *sd,
+ const char *command, const char *message)
+{
+ char sound_file[100];
+
+ if(!message || !*message || sscanf(message, "%99[^\n]", sound_file) < 1) {
+ clif_displaymessage(fd, "Please, enter a sound filename. (usage: @sound <filename>)");
+ return -1;
+ }
+
+ memset(sound_file, '\0', sizeof(sound_file));
+ if(sscanf(message, "%99[^\n]", sound_file) < 1)
+ return -1;
+
+ if(strstr(sound_file, ".wav") == NULL)
+ strcat(sound_file, ".wav");
+
+ clif_soundeffectall(&sd->bl, sound_file,0);
+
+ return 0;
+}
+
+/*==========================================
+ * MOB Search
+ *------------------------------------------
+ */
+static int atmobsearch_sub(struct block_list *bl,va_list ap)
+{
+ int mob_id,fd;
+ static int number=0;
+ struct mob_data *md;
+
+ nullpo_retr(0, bl);
+
+ if(!ap){
+ number=0;
+ return 0;
+ }
+ mob_id = va_arg(ap,int);
+ fd = va_arg(ap,int);
+
+ md = (struct mob_data *)bl;
+
+ if(md && fd && (mob_id==-1 || (md->class_==mob_id))){
+ snprintf(atcmd_output, sizeof atcmd_output, "%2d[%3d:%3d] %s",
+ ++number,bl->x, bl->y,md->name);
+ clif_displaymessage(fd, atcmd_output);
+ }
+ return 0;
+}
+int atcommand_mobsearch(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ char mob_name[100];
+ int mob_id,map_id = 0;
+
+ nullpo_retr(-1, sd);
+
+ if (sscanf(message, "%99[^\n]", mob_name) < 0)
+ return -1;
+
+ if ((mob_id = atoi(mob_name)) == 0)
+ mob_id = mobdb_searchname(mob_name);
+ if(mob_id > 0 && mobdb_checkid(mob_id) == 0){
+ snprintf(atcmd_output, sizeof atcmd_output, "Invalid mob id %s!",mob_name);
+ clif_displaymessage(fd, atcmd_output);
+ return 0;
+ }
+ if(mob_id == atoi(mob_name) && mob_db(mob_id)->jname)
+ strcpy(mob_name,mob_db(mob_id)->jname); // --ja--
+// strcpy(mob_name,mob_db(mob_id)->name); // --en--
+
+ map_id = sd->bl.m;
+
+ snprintf(atcmd_output, sizeof atcmd_output, "Mob Search... %s %s",
+ mob_name, mapindex_id2name(sd->mapindex));
+ clif_displaymessage(fd, atcmd_output);
+
+ map_foreachinmap(atmobsearch_sub, map_id, BL_MOB, mob_id, fd);
+
+ atmobsearch_sub(&sd->bl,0); // 番号リセット
+
+ return 0;
+}
+/*==========================================
+ * ドロップアイテムの掃除
+ *------------------------------------------
+ */
+/*==========================================
+ * cleanmap
+ *------------------------------------------
+ */
+static int atcommand_cleanmap_sub(struct block_list *bl, va_list ap)
+{
+ nullpo_retr(0, bl);
+ map_clearflooritem(bl->id);
+
+ return 0;
+}
+
+int
+atcommand_cleanmap(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ map_foreachinarea(atcommand_cleanmap_sub, sd->bl.m,
+ sd->bl.x-AREA_SIZE*2, sd->bl.y-AREA_SIZE*2,
+ sd->bl.x+AREA_SIZE*2, sd->bl.y+AREA_SIZE*2,
+ BL_ITEM);
+ clif_displaymessage(fd, "All dropped items have been cleaned up.");
+ return 0;
+}
+
+/*==========================================
+ * NPC/PETに話させる
+ *------------------------------------------
+ */
+int
+atcommand_npctalk(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ char name[NAME_LENGTH],mes[100],temp[100];
+ struct npc_data *nd;
+
+ if (sscanf(message, "%23[^,],%99[^\n]", name, mes) < 2)
+ return -1;
+
+ if (!(nd = npc_name2id(name)))
+ return -1;
+
+ snprintf(temp, sizeof temp ,"%s : %s",name,mes);
+ clif_message(&nd->bl, temp);
+
+ return 0;
+}
+int
+atcommand_pettalk(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ char mes[100],temp[100];
+ struct pet_data *pd;
+
+ nullpo_retr(-1, sd);
+
+ if(!sd->status.pet_id || !(pd=sd->pd))
+ return -1;
+
+ if (sscanf(message, "%99[^\n]", mes) < 1)
+ return -1;
+
+ snprintf(temp, sizeof temp ,"%s : %s",sd->pet.name,mes);
+ clif_message(&pd->bl, temp);
+
+ return 0;
+}
+
+/*==========================================
+ * @users
+ * サーバー内の人数マップを表示させる
+ * 手抜きのため汚くなっているのは仕様です。
+ *------------------------------------------
+ */
+
+static struct dbt *users_db = NULL;
+static int users_all;
+
+static int atcommand_users_sub1(struct map_session_data* sd,va_list va) {
+ int users = (int)(uidb_get(users_db,(unsigned int)sd->mapindex)) + 1;
+ users_all++;
+ uidb_put(users_db,(unsigned int)sd->mapindex,(void *)users);
+ return 0;
+}
+
+static int atcommand_users_sub2(DBKey key,void* val,va_list va) {
+ char buf[256];
+ struct map_session_data* sd = va_arg(va,struct map_session_data*);
+ sprintf(buf,"%s : %d (%d%%)",mapindex_id2name(key.i),(int)val,(int)val * 100 / users_all);
+ clif_displaymessage(sd->fd,buf);
+ return 0;
+}
+
+int
+atcommand_users(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ char buf[256];
+ users_all = 0;
+
+ users_db->clear(users_db, NULL);
+ clif_foreachclient(atcommand_users_sub1);
+ users_db->foreach(users_db,atcommand_users_sub2,sd);
+ sprintf(buf,"all : %d",users_all);
+ clif_displaymessage(fd,buf);
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int
+atcommand_summon(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ char name[NAME_LENGTH];
+ int mob_id = 0;
+ int x = 0;
+ int y = 0;
+ int id = 0;
+ int duration = 0;
+ struct mob_data *md;
+ unsigned int tick=gettick();
+
+ nullpo_retr(-1, sd);
+
+ if (!message || !*message)
+ return -1;
+ if (sscanf(message, "%23s %d", name, &duration) < 1)
+ return -1;
+
+ if (duration < 1)
+ duration =1;
+ else if (duration > 60)
+ duration =60;
+
+ if ((mob_id = atoi(name)) == 0)
+ mob_id = mobdb_searchname(name);
+ if(mob_id == 0 || mobdb_checkid(mob_id) == 0)
+ return -1;
+
+ x = sd->bl.x + (rand() % 10 - 5);
+ y = sd->bl.y + (rand() % 10 - 5);
+
+ id = mob_once_spawn(sd,"this", x, y, "--ja--", mob_id, 1, "");
+ if((md=(struct mob_data *)map_id2bl(id))){
+ md->master_id=sd->bl.id;
+ md->special_state.ai=1;
+ md->mode=md->db->mode|MD_AGGRESSIVE;
+ md->deletetimer=add_timer(tick+(duration*60000),mob_timer_delete,id,0);
+ clif_misceffect2(&md->bl,344);
+ }
+ clif_skill_poseffect(&sd->bl,AM_CALLHOMUN,1,x,y,tick);
+
+ return 0;
+}
+
+
+/*==========================================
+ * @adjcmdlvl by [MouseJstr]
+ *
+ * Temp adjust the GM level required to use a GM command
+ *
+ * Used during beta testing to allow players to use GM commands
+ * for short periods of time
+ *------------------------------------------
+ */
+int
+atcommand_adjcmdlvl(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ int i, newlev;
+ char cmd[100];
+ nullpo_retr(-1, sd);
+
+ if (!message || !*message || sscanf(message, "%d %s", &newlev, cmd) != 2) {
+ clif_displaymessage(fd, "Usage: @adjcmdlvl <lvl> <command>.");
+ return -1;
+ }
+
+ for (i = 0; (atcommand_info[i].command) && atcommand_info[i].type != AtCommand_None; i++)
+ if (strcmpi(cmd, atcommand_info[i].command+1) == 0) {
+ atcommand_info[i].level = newlev;
+ clif_displaymessage(fd, "@command level changed.");
+ return 0;
+ }
+
+ clif_displaymessage(fd, "@command not found.");
+ return -1;
+}
+
+/*==========================================
+ * @adjgmlvl by [MouseJstr]
+ *
+ * Create a temp GM
+ *
+ * Used during beta testing to allow players to use GM commands
+ * for short periods of time
+ *------------------------------------------
+ */
+int
+atcommand_adjgmlvl(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ int newlev;
+ char user[NAME_LENGTH];
+ struct map_session_data *pl_sd;
+ nullpo_retr(-1, sd);
+
+ if (!message || !*message || sscanf(message, "%d %23[^\r\n]", &newlev, user) != 2) {
+ clif_displaymessage(fd, "Usage: @adjgmlvl <lvl> <user>.");
+ return -1;
+ }
+
+ if((pl_sd=map_nick2sd((char *) user)) == NULL)
+ return -1;
+
+ pc_set_gm_level(pl_sd->status.account_id, newlev);
+
+ return 0;
+}
+
+
+/*==========================================
+ * @trade by [MouseJstr]
+ *
+ * Open a trade window with a remote player
+ *
+ * If I have to jump to a remote player one more time, I am
+ * gonna scream!
+ *------------------------------------------
+ */
+int
+atcommand_trade(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ struct map_session_data *pl_sd = NULL;
+ nullpo_retr(-1, sd);
+
+ if (!message || !*message)
+ return -1;
+ if((pl_sd=map_nick2sd((char *) message)) != NULL) {
+ trade_traderequest(sd, pl_sd->bl.id);
+ return 0;
+ }
+ return -1;
+}
+
+/*==========================================
+ * @setbattleflag by [MouseJstr]
+ *
+ * set a battle_config flag without having to reboot
+ */
+int
+atcommand_setbattleflag(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ char flag[128], value[128];
+ nullpo_retr(-1, sd);
+
+ if (!message || !*message || sscanf(message, "%127s %127s", flag, value) != 2) {
+ clif_displaymessage(fd, "Usage: @setbattleflag <flag> <value>.");
+ return -1;
+ }
+
+ if (battle_set_value(flag, value) == 0)
+ clif_displaymessage(fd, "unknown battle_config flag");
+ else
+ clif_displaymessage(fd, "battle_config set as requested");
+
+ return 0;
+}
+
+
+/*===========================
+ * @unmute [Valaris]
+ *===========================
+*/
+int atcommand_unmute(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ struct map_session_data *pl_sd = NULL;
+ nullpo_retr(-1, sd);
+
+ if(!battle_config.muting_players) {
+ clif_displaymessage(fd, "Please enable the muting system before using it.");
+ return 0;
+ }
+
+ if (!message || !*message)
+ return -1;
+
+ if((pl_sd=map_nick2sd((char *) message)) != NULL) {
+ if(pl_sd->sc_data[SC_NOCHAT].timer!=-1) {
+ pl_sd->status.manner = 0; // have to set to 0 first [celest]
+ status_change_end(&pl_sd->bl,SC_NOCHAT,-1);
+ clif_displaymessage(sd->fd,"Player unmuted");
+ }
+ else
+ clif_displaymessage(sd->fd,"Player is not muted");
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * @uptime by MC Cameri
+ *------------------------------------------
+ */
+int
+atcommand_uptime(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ unsigned long seconds = 0, day = 24*60*60, hour = 60*60,
+ minute = 60, days = 0, hours = 0, minutes = 0;
+ nullpo_retr(-1, sd);
+
+ seconds = get_uptime();
+ days = seconds/day;
+ seconds -= (seconds/day>0)?(seconds/day)*day:0;
+ hours = seconds/hour;
+ seconds -= (seconds/hour>0)?(seconds/hour)*hour:0;
+ minutes = seconds/minute;
+ seconds -= (seconds/minute>0)?(seconds/minute)*minute:0;
+
+ snprintf(atcmd_output, sizeof(atcmd_output), msg_table[245], days, hours, minutes, seconds);
+ clif_displaymessage(fd, atcmd_output);
+
+ return 0;
+}
+
+/*==========================================
+ * @changesex <sex>
+ * => Changes one's sex. Argument sex can be
+ * 0 or 1, m or f, male or female.
+ *------------------------------------------
+ */
+int
+atcommand_changesex(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ nullpo_retr(-1, sd);
+ chrif_char_ask_name(sd->status.account_id,sd->status.name, 5,0,0,0,0,0,0);
+ return 0;
+}
+
+/*================================================
+ * @mute - Mutes a player for a set amount of time
+ *------------------------------------------------
+ */
+int atcommand_mute(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ struct map_session_data *pl_sd = NULL;
+ int manner;
+ nullpo_retr(-1, sd);
+
+ if(!battle_config.muting_players) {
+ clif_displaymessage(fd, "Please enable the muting system before using it.");
+ return 0;
+ }
+
+ if (!message || !*message || sscanf(message, "%d %23[^\n]", &manner, atcmd_player_name) < 1) {
+ clif_displaymessage(fd, "Usage: @mute <time> <character name>.");
+ return -1;
+ }
+
+ if ((pl_sd = map_nick2sd(atcmd_player_name)) != NULL) {
+ clif_GM_silence(sd, pl_sd, 0);
+ pl_sd->status.manner -= manner;
+ if(pl_sd->status.manner < 0)
+ status_change_start(&pl_sd->bl,SC_NOCHAT,0,0,0,0,0,0);
+ }
+ else {
+ clif_displaymessage(fd, msg_table[3]); // Character not found.
+ return -1;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * @refresh (like @jumpto <<yourself>>)
+ *------------------------------------------
+ */
+int atcommand_refresh(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ nullpo_retr(-1, sd);
+ clif_refresh(sd);
+ return 0;
+}
+
+/*==========================================
+ * @petid <part of pet name>
+ * => Displays a list of matching pets.
+ *------------------------------------------
+ */
+int
+atcommand_petid(const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ char searchtext[100];
+ char temp0[100];
+ char temp1[100];
+ int cnt = 0, i = 0;
+
+ nullpo_retr(-1, sd);
+
+ if (!message || !*message)
+ return -1;
+ if (sscanf(message, "%99s", searchtext) < 1)
+ return -1;
+ estr_lower(searchtext);
+ snprintf(temp0, sizeof(temp0), "Search results for: %s", searchtext);
+ clif_displaymessage(fd,temp0);
+ while (i < MAX_PET_DB) {
+ strcpy(temp1,pet_db[i].name);
+ strcpy(temp1, estr_lower(temp1));
+ strcpy(temp0,pet_db[i].jname);
+ strcpy(temp0, estr_lower(temp1));
+ if (strstr(temp1, searchtext) || strstr(temp0, searchtext) ) {
+ snprintf(temp0, sizeof(temp0), "ID: %i -- Name: %s", pet_db[i].class_,
+ pet_db[i].jname);
+ if (cnt >= 100) { // Only if there are custom pets
+ clif_displaymessage(fd, "Be more specific, can't send more than"
+ " 100 results.");
+ } else {
+ clif_displaymessage(fd, temp0);
+ }
+ cnt++;
+ }
+ i++;
+ }
+ snprintf(temp0, sizeof(temp0),"%i pets have '%s' in their name.", cnt, searchtext);
+ clif_displaymessage(fd, temp0);
+ return 0;
+}
+
+/*==========================================
+ * @identify
+ * => GM's magnifier.
+ *------------------------------------------
+ */
+int
+atcommand_identify(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ int i,num;
+
+ nullpo_retr(-1, sd);
+
+ for(i=num=0;i<MAX_INVENTORY;i++){
+ if(sd->status.inventory[i].nameid > 0 && sd->status.inventory[i].identify!=1){
+ num++;
+ }
+ }
+ if (num > 0) {
+ clif_item_identify_list(sd);
+ } else {
+ clif_displaymessage(fd,"There are no items to appraise.");
+ }
+ return 0;
+}
+
+/*==========================================
+ * @gmotd (Global MOTD)
+ * by davidsiaw :P
+ *------------------------------------------
+ */
+int
+atcommand_gmotd(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ char buf[256];
+ FILE *fp;
+ nullpo_retr(-1, sd);
+ if( (fp = fopen(motd_txt, "r"))!=NULL){
+ while (fgets(buf, 250, fp) != NULL){
+ int i;
+ for( i=0; buf[i]; i++){
+ if( buf[i]=='\r' || buf[i]=='\n'){
+ buf[i]=0;
+ break;
+ }
+ }
+ intif_GMmessage(buf,strlen(buf)+1,8);
+ }
+ fclose(fp);
+ }
+ return 0;
+}
+
+int atcommand_misceffect(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ int effect = 0;
+ nullpo_retr(-1, sd);
+ if (!message || !*message)
+ return -1;
+ if (sscanf(message, "%d", &effect) < 1)
+ return -1;
+ clif_misceffect(&sd->bl,effect);
+
+ return 0;
+}
+
+int charid2sessionid(int charid)
+{
+ struct map_session_data *pl_sd = NULL;
+
+ pl_sd = map_charid2sd(charid);
+ if (pl_sd) return pl_sd->fd;
+
+ return 0;
+}
+
+int accountid2sessionid(int accountid)
+{
+ struct map_session_data *pl_sd = NULL;
+
+ pl_sd = map_id2sd(accountid);
+ if (pl_sd) return pl_sd->fd;
+
+ return 0;
+}
+
+
+/*==========================================
+ * Jump to a player by PID number
+ * Original by Dino9021
+ * Added in by nsstrunks
+ *------------------------------------------
+ */
+
+int atcommand_jumptoid(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ int cid=0;
+ int session_id=0;
+ struct map_session_data *pl_sd;
+
+ memset(atcmd_player_name, '\0', sizeof(atcmd_player_name));
+ memset(atcmd_output, '\0', sizeof(atcmd_output));
+
+ if (!message || (cid = atoi(message)) == 0 || !*message || sscanf(message, "%99[^\n]", atcmd_player_name) < 1) {
+ clif_displaymessage(fd, "Please, enter a player CID (usage: @jumptoid/@warptoid/@gotoid <char id>).");
+ return -1;
+ }
+
+ cid=atoi(message);
+
+ if ((session_id=charid2sessionid(cid))!=0)
+ {
+ if ((pl_sd = (struct map_session_data *) session[session_id]->session_data) != NULL) {
+ if (pl_sd->bl.m >= 0 && map[pl_sd->bl.m].flag.nowarpto && battle_config.any_warp_GM_min_level > pc_isGM(sd)) {
+ clif_displaymessage(fd, msg_table[247]);
+ return -1;
+ }
+ if (sd->bl.m >= 0 && map[sd->bl.m].flag.nowarp && battle_config.any_warp_GM_min_level > pc_isGM(sd)) {
+ clif_displaymessage(fd, msg_table[248]);
+ return -1;
+ }
+ pc_setpos(sd, pl_sd->mapindex, pl_sd->bl.x, pl_sd->bl.y, 3);
+ sprintf(atcmd_output, msg_table[4], pl_sd->status.name); // Jump to %s
+ clif_displaymessage(fd, atcmd_output);
+ } else {
+ clif_displaymessage(fd, msg_table[154]); // Character not found.
+ return -1;
+ }
+ }
+ else
+ {
+ clif_displaymessage(fd,msg_table[3]);
+ }
+ //printf("Session_id = %d, cid = %d\n",session_id,cid);
+
+ return 0;
+}
+
+/*==========================================
+ * Jump to a player by PID number
+ * Original by Dino9021
+ * Added in by nsstrunks
+ *------------------------------------------
+ */
+
+int atcommand_jumptoid2(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ int aid=0;
+ int session_id=0;
+ struct map_session_data *pl_sd;
+
+ memset(atcmd_player_name, '\0', sizeof(atcmd_player_name));
+ memset(atcmd_output, '\0', sizeof(atcmd_output));
+
+ if (!message || (aid = atoi(message)) == 0 || !*message || sscanf(message, "%99[^\n]", atcmd_player_name) < 1) {
+ clif_displaymessage(fd, "Please, enter a player AID (usage: @jumptoid/@warptoid/@gotoid <account id>).");
+ return -1;
+ }
+
+ aid=atoi(message);
+
+ if ((session_id=accountid2sessionid(aid))!=0)
+ {
+ if ((pl_sd = (struct map_session_data *) session[session_id]->session_data) != NULL) {
+ if (pl_sd->bl.m >= 0 && map[pl_sd->bl.m].flag.nowarpto && battle_config.any_warp_GM_min_level > pc_isGM(sd)) {
+ clif_displaymessage(fd, msg_table[247]);
+ return -1;
+ }
+ if (sd->bl.m >= 0 && map[sd->bl.m].flag.nowarp && battle_config.any_warp_GM_min_level > pc_isGM(sd)) {
+ clif_displaymessage(fd, msg_table[248]);
+ return -1;
+ }
+ pc_setpos(sd, pl_sd->mapindex, pl_sd->bl.x, pl_sd->bl.y, 3);
+ sprintf(atcmd_output, msg_table[4], pl_sd->status.name); // Jump to %s
+ clif_displaymessage(fd, atcmd_output);
+ } else {
+ clif_displaymessage(fd, msg_table[154]); // Character not found.
+ return -1;
+ }
+ }
+ else
+ {
+ clif_displaymessage(fd,msg_table[3]);
+ }
+ //printf("Session_id = %d, aid = %d\n",session_id,aid);
+
+ return 0;
+}
+
+/*==========================================
+ * Recall a player by PID number
+ * Original by Dino9021
+ * Added in by nsstrunks
+ *------------------------------------------
+ */
+int atcommand_recallid(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ int cid=0;
+ int session_id=0;
+ struct map_session_data *pl_sd;
+
+ memset(atcmd_player_name, '\0', sizeof(atcmd_player_name));
+ memset(atcmd_output, '\0', sizeof(atcmd_output));
+
+ if (!message || (cid = atoi(message)) == 0 || !*message || sscanf(message, "%99[^\n]", atcmd_player_name) < 1) {
+ clif_displaymessage(fd, "Please, enter a player CID (usage: @recallid <char id>).");
+ return -1;
+ }
+
+ cid=atoi(message);
+
+ if ((session_id=charid2sessionid(cid))!=0)
+ {
+ if ((pl_sd = (struct map_session_data *) session[session_id]->session_data) != NULL) {
+ if (pc_isGM(sd) >= pc_isGM(pl_sd)) { // you can recall only lower or same level
+ if (pl_sd->bl.m >= 0 && map[pl_sd->bl.m].flag.nowarpto && battle_config.any_warp_GM_min_level > pc_isGM(sd)) {
+ clif_displaymessage(fd, msg_table[247]);
+ return -1;
+ }
+ if (sd->bl.m >= 0 && map[sd->bl.m].flag.nowarp && battle_config.any_warp_GM_min_level > pc_isGM(sd)) {
+ clif_displaymessage(fd, msg_table[248]);
+ return -1;
+ }
+ pc_setpos(pl_sd, sd->mapindex, sd->bl.x, sd->bl.y, 2);
+ sprintf(atcmd_output, msg_table[46], pl_sd->status.name); // Jump to %s
+ clif_displaymessage(fd, atcmd_output);
+ } else {
+ clif_displaymessage(fd, msg_table[81]); // Your GM level don't authorise you to do this action on this player.
+ return -1;
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[154]); // Character not found.
+ return -1;
+ }
+ }
+ else
+ {
+ clif_displaymessage(fd,msg_table[3]);
+ }
+ //printf("Session_id = %d, cid = %d\n",session_id,cid);
+
+ return 0;
+}
+
+/*==========================================
+ * Recall a player by PID number
+ * Original by Dino9021
+ * Added in by nsstrunks
+ *------------------------------------------
+ */
+int atcommand_recallid2(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ int aid=0;
+ int session_id=0;
+ struct map_session_data *pl_sd;
+
+ memset(atcmd_player_name, '\0', sizeof(atcmd_player_name));
+ memset(atcmd_output, '\0', sizeof(atcmd_output));
+
+ if (!message || (aid = atoi(message)) == 0 || !*message || sscanf(message, "%99[^\n]", atcmd_player_name) < 1) {
+ clif_displaymessage(fd, "Please, enter a player AID (usage: @recallid2 <account id>).");
+ return -1;
+ }
+
+ aid=atoi(message);
+
+ if ((session_id=accountid2sessionid(aid))!=0)
+ {
+ if ((pl_sd = (struct map_session_data *) session[session_id]->session_data) != NULL) {
+ if (pc_isGM(sd) >= pc_isGM(pl_sd)) { // you can recall only lower or same level
+ if (pl_sd->bl.m >= 0 && map[pl_sd->bl.m].flag.nowarpto && battle_config.any_warp_GM_min_level > pc_isGM(sd)) {
+ clif_displaymessage(fd, msg_table[247]);
+ return -1;
+ }
+ if (sd->bl.m >= 0 && map[sd->bl.m].flag.nowarp && battle_config.any_warp_GM_min_level > pc_isGM(sd)) {
+ clif_displaymessage(fd, msg_table[248]);
+ return -1;
+ }
+ pc_setpos(pl_sd, sd->mapindex, sd->bl.x, sd->bl.y, 2);
+ sprintf(atcmd_output, msg_table[46], pl_sd->status.name); // Jump to %s
+ clif_displaymessage(fd, atcmd_output);
+ } else {
+ clif_displaymessage(fd, msg_table[81]); // Your GM level don't authorise you to do this action on this player.
+ return -1;
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[154]); // Character not found.
+ return -1;
+ }
+ }
+ else
+ {
+ clif_displaymessage(fd,msg_table[3]);
+ }
+ //printf("Session_id = %d, aid = %d\n",session_id,aid);
+
+ return 0;
+}
+
+/*==========================================
+ * Kick a player by PID number
+ * Original by Dino9021
+ * Added in by nsstrunks
+ *------------------------------------------
+ */
+int atcommand_kickid(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ struct map_session_data *pl_sd;
+ int cid=0;
+ int session_id=0;
+
+ memset(atcmd_player_name, '\0', sizeof(atcmd_player_name));
+
+ if (!message || (cid = atoi(message)) == 0 || !*message || sscanf(message, "%99[^\n]", atcmd_player_name) < 1) {
+ clif_displaymessage(fd, "Please, enter a player CID (usage: @kickid <char id>).");
+ return -1;
+ }
+
+ cid=atoi(message);
+
+ if ((session_id=charid2sessionid(cid))!=0)
+ {
+ if ((pl_sd = (struct map_session_data *) session[session_id]->session_data) != NULL) {
+ if (pc_isGM(sd) >= pc_isGM(pl_sd)) // you can kick only lower or same gm level
+ clif_GM_kick(sd, pl_sd, 1);
+ else {
+ clif_displaymessage(fd, msg_table[81]); // Your GM level don't authorise you to do this action on this player.
+ return -1;
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[3]); // Character not found.
+ return -1;
+ }
+
+ }
+ else
+ {
+ clif_displaymessage(fd,msg_table[3]);
+ }
+ //printf("Session_id = %d, cid = %d\n",session_id,cid);
+
+ return 0;
+}
+
+/*==========================================
+ * Kick a player by PID number
+ * Original by Dino9021
+ * Added in by nsstrunks
+ *------------------------------------------
+ */
+int atcommand_kickid2(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ struct map_session_data *pl_sd;
+ int aid=0;
+ int session_id=0;
+
+ memset(atcmd_player_name, '\0', sizeof(atcmd_player_name));
+
+ if (!message || (aid = atoi(message)) == 0 || !*message || sscanf(message, "%99[^\n]", atcmd_player_name) < 1) {
+ clif_displaymessage(fd, "Please, enter a player AID (usage: @kickid2 <account id>).");
+ return -1;
+ }
+
+ aid=atoi(message);
+
+ if ((session_id=accountid2sessionid(aid))!=0)
+ {
+ if ((pl_sd = (struct map_session_data *) session[session_id]->session_data) != NULL) {
+ if (pc_isGM(sd) >= pc_isGM(pl_sd)) // you can kick only lower or same gm level
+ clif_GM_kick(sd, pl_sd, 1);
+ else {
+ clif_displaymessage(fd, msg_table[81]); // Your GM level don't authorise you to do this action on this player.
+ return -1;
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[3]); // Character not found.
+ return -1;
+ }
+
+ }
+ else
+ {
+ clif_displaymessage(fd,msg_table[3]);
+ }
+ //printf("Session_id = %d, aid = %d\n",session_id,aid);
+
+ return 0;
+}
+
+/*==========================================
+ * Revive a player by PID number
+ * Original by Dino9021
+ * Added in by nsstrunks
+ *------------------------------------------
+ */
+int atcommand_reviveid(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ int cid=0;
+ int session_id=0;
+ struct map_session_data *pl_sd;
+
+ memset(atcmd_player_name, '\0', sizeof(atcmd_player_name));
+
+ if (!message || (cid = atoi(message)) == 0 || !*message || sscanf(message, "%99[^\n]", atcmd_player_name) < 1) {
+ clif_displaymessage(fd, "Please, enter a player CID (usage: @reviveid <char id>).");
+ return -1;
+ }
+
+ cid=atoi(message);
+
+ if ((session_id=charid2sessionid(cid))!=0)
+ {
+ if ((pl_sd = (struct map_session_data *) session[session_id]->session_data) != NULL) {
+ pl_sd->status.hp = pl_sd->status.max_hp;
+ pc_setstand(pl_sd);
+ if (battle_config.pc_invincible_time > 0)
+ pc_setinvincibletimer(sd, battle_config.pc_invincible_time);
+ clif_updatestatus(pl_sd, SP_HP);
+ clif_updatestatus(pl_sd, SP_SP);
+ clif_resurrection(&pl_sd->bl, 1);
+ clif_displaymessage(fd, msg_table[51]); // Character revived.
+ } else {
+ clif_displaymessage(fd, msg_table[3]); // Character not found.
+ return -1;
+ }
+
+ }
+ else
+ {
+ clif_displaymessage(fd,msg_table[3]);
+ }
+ //printf("Session_id = %d, cid = %d\n",session_id,cid);
+
+
+ return 0;
+}
+
+/*==========================================
+ * Revive a player by PID number
+ * Original by Dino9021
+ * Added in by nsstrunks
+ *------------------------------------------
+ */
+int atcommand_reviveid2(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ int aid=0;
+ int session_id=0;
+ struct map_session_data *pl_sd;
+
+ memset(atcmd_player_name, '\0', sizeof(atcmd_player_name));
+
+ if (!message || (aid = atoi(message)) == 0 || !*message || sscanf(message, "%99[^\n]", atcmd_player_name) < 1) {
+ clif_displaymessage(fd, "Please, enter a player AID (usage: @reviveid2 <account id>).");
+ return -1;
+ }
+
+ aid=atoi(message);
+
+ if ((session_id=accountid2sessionid(aid))!=0)
+ {
+ if ((pl_sd = (struct map_session_data *) session[session_id]->session_data) != NULL) {
+ pl_sd->status.hp = pl_sd->status.max_hp;
+ pc_setstand(pl_sd);
+ if (battle_config.pc_invincible_time > 0)
+ pc_setinvincibletimer(sd, battle_config.pc_invincible_time);
+ clif_updatestatus(pl_sd, SP_HP);
+ clif_updatestatus(pl_sd, SP_SP);
+ clif_resurrection(&pl_sd->bl, 1);
+ clif_displaymessage(fd, msg_table[51]); // Character revived.
+ } else {
+ clif_displaymessage(fd, msg_table[3]); // Character not found.
+ return -1;
+ }
+
+ }
+ else
+ {
+ clif_displaymessage(fd,msg_table[3]);
+ }
+ //printf("Session_id = %d, aid = %d\n",session_id,aid);
+
+
+ return 0;
+}
+
+/*==========================================
+ * Kill a player by PID number
+ * Original by Dino9021
+ * Added in by nsstrunks
+ *------------------------------------------
+ */
+int atcommand_killid(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ int cid=0;
+ int session_id=0;
+ struct map_session_data *pl_sd;
+
+ memset(atcmd_player_name, '\0', sizeof(atcmd_player_name));
+
+ if (!message || (cid = atoi(message)) == 0 || !*message || sscanf(message, "%99[^\n]", atcmd_player_name) < 1) {
+ clif_displaymessage(fd, "Please, enter a player CID (usage: @killid <char id>).");
+ return -1;
+ }
+
+ cid=atoi(message);
+
+ if ((session_id=charid2sessionid(cid))!=0)
+ {
+ if ((pl_sd = (struct map_session_data *) session[session_id]->session_data) != NULL) {
+ if (pc_isGM(sd) >= pc_isGM(pl_sd)) { // you can kill only lower or same level
+ pc_damage(NULL, pl_sd, pl_sd->status.hp);
+ clif_displaymessage(fd, msg_table[14]); // Character killed.
+ } else {
+ clif_displaymessage(fd, msg_table[81]); // Your GM level don't authorise you to do this action on this player.
+ return -1;
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[3]); // Character not found.
+ return -1;
+ }
+
+ }
+ else
+ {
+ clif_displaymessage(fd,msg_table[3]);
+ }
+ //printf("Session_id = %d, cid = %d\n",session_id,cid);
+
+ return 0;
+}
+
+/*==========================================
+ * Kill a player by PID number
+ * Original by Dino9021
+ * Added in by nsstrunks
+ *------------------------------------------
+ */
+int atcommand_killid2(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ int aid=0;
+ int session_id=0;
+ struct map_session_data *pl_sd;
+
+ memset(atcmd_player_name, '\0', sizeof(atcmd_player_name));
+
+ if (!message || (aid = atoi(message)) == 0 || !*message || sscanf(message, "%99[^\n]", atcmd_player_name) < 1) {
+ clif_displaymessage(fd, "Please, enter a player AID (usage: @killid2 <account id>).");
+ return -1;
+ }
+
+ aid=atoi(message);
+
+ if ((session_id=accountid2sessionid(aid))!=0)
+ {
+ if ((pl_sd = (struct map_session_data *) session[session_id]->session_data) != NULL) {
+ if (pc_isGM(sd) >= pc_isGM(pl_sd)) { // you can kill only lower or same level
+ pc_damage(NULL, pl_sd, pl_sd->status.hp);
+ clif_displaymessage(fd, msg_table[14]); // Character killed.
+ } else {
+ clif_displaymessage(fd, msg_table[81]); // Your GM level don't authorise you to do this action on this player.
+ return -1;
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[3]); // Character not found.
+ return -1;
+ }
+
+ }
+ else
+ {
+ clif_displaymessage(fd,msg_table[3]);
+ }
+ //printf("Session_id = %d, aid = %d\n",session_id,aid);
+
+ return 0;
+}
+
+/*==========================================
+ * Make a player killable, by PID
+ * Original by Dino9021
+ * Added in by nsstrunks
+ *------------------------------------------
+ */
+int
+atcommand_charkillableid(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ struct map_session_data *pl_sd = NULL;
+ int cid=0;
+ int session_id=0;
+
+ if (!message || (cid = atoi(message)) == 0 || !*message)
+ return -1;
+
+ cid=atoi(message);
+
+ if ((session_id=charid2sessionid(cid))!=0)
+ {
+ if((pl_sd= (struct map_session_data *) session[session_id]->session_data) == NULL)
+ return -1;
+
+ pl_sd->special_state.killable = !pl_sd->special_state.killable;
+
+ if(pl_sd->special_state.killable)
+ clif_displaymessage(fd, "The player is now killable");
+ else
+ clif_displaymessage(fd, "The player is no longer killable");
+ }
+ else
+ {
+ clif_displaymessage(fd,msg_table[3]);
+ }
+ //printf("Session_id = %d, cid = %d\n",session_id,cid);
+ return 0;
+}
+
+
+/*==========================================
+ * Make a player killable, by PID
+ * Original by Dino9021
+ * Added in by nsstrunks
+ *------------------------------------------
+ */
+int
+atcommand_charkillableid2(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ struct map_session_data *pl_sd = NULL;
+ int aid=0;
+ int session_id=0;
+
+ if (!message || (aid = atoi(message)) == 0 || !*message)
+ return -1;
+
+ aid=atoi(message);
+
+ if ((session_id=accountid2sessionid(aid))!=0)
+ {
+ if((pl_sd= (struct map_session_data *) session[session_id]->session_data) == NULL)
+ return -1;
+
+ pl_sd->special_state.killable = !pl_sd->special_state.killable;
+
+ if(pl_sd->special_state.killable)
+ clif_displaymessage(fd, "The player is now killable");
+ else
+ clif_displaymessage(fd, "The player is no longer killable");
+ }
+ else
+ {
+ clif_displaymessage(fd,msg_table[3]);
+ }
+ //printf("Session_id = %d, aid = %d\n",session_id,aid);
+ return 0;
+}
+
+#ifndef TXT_ONLY /* Begin SQL-Only commands */
+
+/*==========================================
+ * Mail System commands by [Valaris]
+ *------------------------------------------
+ */
+int atcommand_listmail(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ if(!mail_server_enable)
+ return 0;
+
+ nullpo_retr(-1, sd);
+
+ if(strlen(command)==12)
+ mail_check(sd,3);
+ else if(strlen(command)==9)
+ mail_check(sd,2);
+ else
+ mail_check(sd,1);
+ return 0;
+}
+
+int atcommand_readmail(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ int index;
+ if(!mail_server_enable)
+ return 0;
+
+ nullpo_retr(-1, sd);
+
+ if (!message || !*message) {
+ clif_displaymessage(sd->fd,"You must specify a message number.");
+ return 0;
+ }
+
+ index = atoi(message);
+ if (index < 1) {
+ clif_displaymessage(sd->fd,"Message number cannot be negative or zero.");
+ return 0;
+ }
+
+ if(strlen(command)==11)
+ mail_delete(sd,index);
+ else
+ mail_read(sd,index);
+
+ return 0;
+}
+
+int atcommand_sendmail(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ char name[NAME_LENGTH],text[80];
+
+ if(!mail_server_enable)
+ return 0;
+
+ nullpo_retr(-1, sd);
+
+ if (!message || !*message) {
+ clif_displaymessage(sd->fd,"You must specify a recipient and a message.");
+ return 0;
+ }
+
+ if ((sscanf(message, "\"%23[^\"]\" %79[^\n]", name, text) < 2) &&
+ (sscanf(message, "%23s %79[^\n]", name, text) < 2)) {
+ clif_displaymessage(sd->fd,"You must specify a recipient and a message.");
+ return 0;
+ }
+
+ if(strlen(command)==17)
+ mail_send(sd,name,text,1);
+ else
+ mail_send(sd,name,text,0);
+
+ return 0;
+}
+
+/*==========================================
+ * Refresh online command for SQL [Valaris]
+ * Will refresh and check online column of
+ * players and set correctly.
+ *------------------------------------------
+ */
+int atcommand_refreshonline(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ send_users_tochar(-1, gettick(), 0, 0);
+ return 0;
+}
+
+#endif /* end sql only */
+
+/*==========================================
+ * Show Monster DB Info v 1.0
+ * originally by [Lupus] eAthena
+ *------------------------------------------
+ */
+int atcommand_mobinfo(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ unsigned char msize[3][7] = {"Small", "Medium", "Large"};
+ unsigned char mrace[12][11] = {"Formless", "Undead", "Beast", "Plant", "Insect", "Fish", "Demon", "Demi-Human", "Angel", "Dragon", "Boss", "Non-Boss"};
+ unsigned char melement[11][8] = {"None", "Neutral", "Water", "Earth", "Fire", "Wind", "Poison", "Holy", "Dark", "Ghost", "Undead"};
+ char atcmd_output2[200];
+ struct item_data *item_data;
+ struct mob_db *mob;
+ int mob_id;
+ int i, j;
+
+ memset(atcmd_output, '\0', sizeof(atcmd_output));
+ memset(atcmd_output2, '\0', sizeof(atcmd_output2));
+
+ if (!message || !*message) {
+ clif_displaymessage(fd, "Please, enter a Monster/ID (usage: @mobinfo <monster_name_or_monster_ID>).");
+ return -1;
+ }
+
+ // If monster identifier/name argument is a name
+ if ((mob_id = mobdb_searchname(message)) == 0) // check name first (to avoid possible name begining by a number)
+ mob_id = mobdb_checkid(atoi(message));
+
+ if (mob_id == 0) {
+ clif_displaymessage(fd, msg_table[40]); // Invalid monster ID or name.
+ return -1;
+ }
+
+ mob = mob_db(mob_id);
+
+ // stats
+ if (mob->mexp)
+ sprintf(atcmd_output, "MVP Monster: '%s'/'%s' (%d)", mob->name, mob->jname, mob_id);
+ else
+ sprintf(atcmd_output, "Monster: '%s'/'%s' (%d)", mob->name, mob->jname, mob_id);
+ clif_displaymessage(fd, atcmd_output);
+ sprintf(atcmd_output, " Level:%d HP:%d SP:%d Base EXP:%d Job EXP:%d", mob->lv, mob->max_hp, mob->max_sp, mob->base_exp, mob->job_exp);
+ clif_displaymessage(fd, atcmd_output);
+ sprintf(atcmd_output, " DEF:%d MDEF:%d STR:%d AGI:%d VIT:%d INT:%d DEX:%d LUK:%d", mob->def, mob->mdef, mob->str, mob->agi, mob->vit, mob->int_, mob->dex, mob->luk);
+ clif_displaymessage(fd, atcmd_output);
+ if (mob->element < 20) {
+ //Element - None, Level 0
+ i = 0;
+ j = 0;
+ } else {
+ i = mob->element % 20 + 1;
+ j = mob->element / 20;
+ }
+ sprintf(atcmd_output, " ATK:%d~%d Range:%d~%d~%d Size:%s Race: %s Element: %s (Lv:%d)", mob->atk1, mob->atk2, mob->range, mob->range2 , mob->range3, msize[mob->size], mrace[mob->race], melement[i], j);
+ clif_displaymessage(fd, atcmd_output);
+ // drops
+ clif_displaymessage(fd, " Drops:");
+ strcpy(atcmd_output, " ");
+ j = 0;
+ for (i = 0; i < 10; i++) {
+ if (mob->dropitem[i].nameid <= 0 || (item_data = itemdb_search(mob->dropitem[i].nameid)) == NULL)
+ continue;
+ if (mob->dropitem[i].p > 0) {
+ sprintf(atcmd_output2, " - %s %02.02f%%", item_data->name, (float)mob->dropitem[i].p / 100);
+ strcat(atcmd_output, atcmd_output2);
+ if (++j % 3 == 0) {
+ clif_displaymessage(fd, atcmd_output);
+ strcpy(atcmd_output, " ");
+ }
+ }
+ }
+ if (j == 0)
+ clif_displaymessage(fd, "This monster has no drops.");
+ else if (j % 3 != 0)
+ clif_displaymessage(fd, atcmd_output);
+ // mvp
+ if (mob->mexp) {
+ sprintf(atcmd_output, " MVP Bonus EXP:%d %02.02f%%", mob->mexp, (float)mob->mexpper / 100);
+ clif_displaymessage(fd, atcmd_output);
+ strcpy(atcmd_output, " MVP Items:");
+ j = 0;
+ for (i = 0; i < 3; i++) {
+ if (mob->mvpitem[i].nameid <= 0 || (item_data = itemdb_search(mob->mvpitem[i].nameid)) == NULL)
+ continue;
+ if (mob->mvpitem[i].p > 0) {
+ j++;
+ if (j == 1)
+ sprintf(atcmd_output2, " %s %02.02f%%", item_data->name, (float)mob->mvpitem[i].p / 100);
+ else
+ sprintf(atcmd_output2, " - %s %02.02f%%", item_data->name, (float)mob->mvpitem[i].p / 100);
+ strcat(atcmd_output, atcmd_output2);
+ }
+ }
+ if (j == 0)
+ clif_displaymessage(fd, "This monster has no MVP prizes.");
+ else
+ clif_displaymessage(fd, atcmd_output);
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * Show Items DB Info v 1.0
+ * originally by [Lupus] eAthena
+ *------------------------------------------
+ */
+int atcommand_iteminfo(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ char *itype[12] = {"Potion/Food", "BUG!", "Usable", "Etc", "Weapon", "Protection", "Card", "Egg", "Pet Acessory", "BUG!", "Arrow"};
+ //, "Lure/Scroll"}; No need, type 11 items are converted to type 2 upon loading [Skotlex]
+
+ struct item_data *item_data;
+ int item_id=0;
+
+ if (!message || !*message) {
+ clif_displaymessage(fd, "Please, enter Item name or its ID (usage: @iteminfo <item_name_or_ID>).");
+ return -1;
+ }
+
+ if ((item_data = itemdb_searchname(message)) != NULL ||
+ (item_data = itemdb_exists(atoi(message))) != NULL)
+ item_id = item_data->nameid;
+
+ if (item_id >= 500) {
+
+ sprintf(atcmd_output, "Item: '%s'/'%s'[%d] (%d) Type: %s | Extra Effect: %s",
+ item_data->name,item_data->jname,item_data->slot,item_id,
+ item_data->type < 12 ? itype[item_data->type] : "BUG!",
+ (item_data->script==NULL)? "None" : "With script"
+ );
+ clif_displaymessage(fd, atcmd_output);
+
+ sprintf(atcmd_output, "NPC Buy:%dz%s, Sell:%dz%s | Weight: %d ", item_data->value_buy, item_data->flag.value_notdc ? "(No Discount!)":"", item_data->value_sell, item_data->flag.value_notoc ? "(No Overcharge!)":"", item_data->weight );
+ clif_displaymessage(fd, atcmd_output);
+
+ if (item_data->maxchance == 10000)
+ strcpy(atcmd_output, " - Available in the shops only");
+ else if (item_data->maxchance)
+ sprintf(atcmd_output, " - Maximal monsters drop chance: %02.02f%%", (float)item_data->maxchance / 100 );
+ else
+ strcpy(atcmd_output, " - Monsters don't drop this item");
+ clif_displaymessage(fd, atcmd_output);
+
+ return 0;
+ }
+
+ clif_displaymessage(fd, "Item not found.");
+ return -1;
+}
+
+/*==========================================
+ * @adopt by [Veider]
+ *
+ * adopt a novice
+ *------------------------------------------
+ */
+int
+atcommand_adopt(const int fd, struct map_session_data* sd,
+const char* command, const char* message)
+{
+ struct map_session_data *pl_sd1 = NULL;
+ struct map_session_data *pl_sd2 = NULL;
+ struct map_session_data *pl_sd3 = NULL;
+ char player1[NAME_LENGTH], player2[NAME_LENGTH], player3[NAME_LENGTH];
+
+ nullpo_retr(-1, sd);
+
+ if (!message || !*message)
+ return -1;
+
+ if (sscanf(message, "%23[^,],%23[^,],%23[^\r\n]", player1, player2, player3) != 3) {
+ clif_displaymessage(fd, "usage: @adopt <player1>,<player2>,<player3>.");
+ return -1;
+ }
+
+ if (battle_config.etc_log)
+ printf("Adopting: --%s--%s--%s--\n",player1,player2,player3);
+
+ if((pl_sd1=map_nick2sd((char *) player1)) == NULL) {
+ sprintf(player2, "Cannot find player %s online", player1);
+ clif_displaymessage(fd, player2);
+ return -1;
+ }
+
+ if((pl_sd2=map_nick2sd((char *) player2)) == NULL) {
+ sprintf(player1, "Cannot find player %s online", player2);
+ clif_displaymessage(fd, player1);
+ return -1;
+ }
+
+ if((pl_sd3=map_nick2sd((char *) player3)) == NULL) {
+ sprintf(player1, "Cannot find player %s online", player3);
+ clif_displaymessage(fd, player1);
+ return -1;
+ }
+
+ if((pl_sd1->status.base_level < 70) || (pl_sd2->status.base_level < 70)){
+ clif_displaymessage(fd, "They are too young to be parents!");
+ return -1;
+ }
+
+ if (pc_adoption(pl_sd1, pl_sd2, pl_sd3) == 0) {
+ clif_displaymessage(fd, "They are family.. wish them luck");
+ return 0;
+ }
+ else
+ return -1;
+}
+
+int atcommand_version(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ const char * revision;
+
+ if ((revision = get_svn_revision()) != 0) {
+ sprintf(atcmd_output,"eAthena Version SVN r%s",revision);
+ clif_displaymessage(fd,atcmd_output);
+ } else
+ clif_displaymessage(fd,"Cannot determine SVN revision");
+
+ return 0;
+}
+
+
+static int atcommand_mutearea_sub(struct block_list *bl,va_list ap)
+{
+
+ int time, id;
+ struct map_session_data *pl_sd = (struct map_session_data *)bl;
+ if (pl_sd == NULL)
+ return 0;
+
+ id = va_arg(ap, int);
+ time = va_arg(ap, int);
+
+ if (id != bl->id && !pc_isGM(pl_sd)) {
+ pl_sd->status.manner -= time;
+ if (pl_sd->status.manner < 0)
+ status_change_start(&pl_sd->bl,SC_NOCHAT,0,0,0,0,0,0);
+ }
+ return 0;
+}
+
+/*==========================================
+ * @mutearea by MouseJstr
+ *------------------------------------------
+ */
+int atcommand_mutearea(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ int time;
+ nullpo_retr(0, sd);
+
+ if(!battle_config.muting_players) {
+ clif_displaymessage(fd, "Please enable the muting system before using it.");
+ return 0;
+ }
+
+ time = atoi(message);
+ if (time <= 0)
+ time = 15; // 15 minutes default
+ map_foreachinarea(atcommand_mutearea_sub,sd->bl.m,
+ sd->bl.x-AREA_SIZE, sd->bl.y-AREA_SIZE,
+ sd->bl.x+AREA_SIZE, sd->bl.y+AREA_SIZE, BL_PC, sd->bl.id, time);
+
+ return 0;
+}
+
+static int atcommand_shuffle_sub(struct block_list *bl,va_list ap)
+{
+ struct map_session_data *pl_sd = (struct map_session_data *) bl;
+ if (bl == NULL)
+ return 0;
+
+ if (!pc_isGM(pl_sd))
+ pc_setpos(pl_sd, pl_sd->mapindex, rand() % 399 + 1, rand() % 399 + 1, 3);
+
+ return 0;
+}
+
+/*==========================================
+ * @shuffle by MouseJstr
+ *------------------------------------------
+ */
+int atcommand_shuffle(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ nullpo_retr(0, sd);
+
+ if (strcmp(message, "area")== 0) {
+ map_foreachinarea(atcommand_shuffle_sub,sd->bl.m,
+ sd->bl.x-AREA_SIZE,sd->bl.y-AREA_SIZE,
+ sd->bl.x+AREA_SIZE, sd->bl.y+AREA_SIZE, BL_PC);
+ } else if (strcmp(message, "map")== 0) {
+ map_foreachinmap(atcommand_shuffle_sub,sd->bl.m, BL_PC);
+ } else if (strcmp(message, "world") == 0) {
+ struct map_session_data **pl_allsd;
+ int i, users;
+ pl_allsd = map_getallusers(&users);
+ for (i = 0; i < users; i++)
+ atcommand_shuffle_sub(&pl_allsd[i]->bl, 0);
+ } else
+ clif_displaymessage(fd, "options are area, map, or world");
+
+ return 0;
+}
+
+int atcommand_rates(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ char buf[255];
+
+ nullpo_retr(0, sd);
+
+ sprintf(buf, "Experience rates: Base %.1fx / Job %.1fx",
+ battle_config.base_exp_rate/100., battle_config.job_exp_rate/100.);
+
+ clif_displaymessage(fd, buf);
+
+ return 0;
+}
+
+/*==========================================
+ * @me by lordalfa
+ * => Displays the OUTPUT string on top of
+ * the Visible players Heads.
+ *------------------------------------------
+ */
+
+int atcommand_me(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ char tempmes[200];
+ nullpo_retr(-1, sd);
+
+ memset(tempmes, '\0', sizeof(tempmes));
+ memset(atcmd_output, '\0', sizeof(atcmd_output));
+
+ if (!message || !*message) {
+ clif_displaymessage(fd, "Please, enter a message (usage: @me <message>).");
+ return -1;
+ }
+
+ sscanf(message, "%199[^\n]", tempmes);
+ sprintf(atcmd_output, "* %s %s *", sd->status.name, tempmes);
+ clif_disp_overhead(sd, atcmd_output);
+
+ return 0;
+
+}
+
+/*==========================================
+ * @size
+ * => Resize your character sprite. [Valaris]
+ *------------------------------------------
+ */
+int atcommand_size(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ int size=0;
+
+ nullpo_retr(-1, sd);
+
+ if (!message || !*message)
+ return -1;
+
+ if (sscanf(message,"%d", &size) < 1)
+ return -1;
+
+ if(sd->state.size) {
+ sd->state.size=0;
+ pc_setpos(sd, sd->mapindex, sd->bl.x, sd->bl.y, 3);
+ }
+
+ if(size==1) {
+ sd->state.size=1;
+ clif_specialeffect(&sd->bl,420,0);
+ } else if(size==2) {
+ sd->state.size=2;
+ clif_specialeffect(&sd->bl,422,0);
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * @monsterignore
+ * => Makes monsters ignore you. [Valaris]
+ *------------------------------------------
+ */
+
+int atcommand_monsterignore(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ nullpo_retr(-1, sd);
+
+ if (!sd->state.monster_ignore) {
+ sd->state.monster_ignore = 1;
+ clif_displaymessage(sd->fd, "Monsters will now ignore you.");
+ } else {
+ sd->state.monster_ignore = 0;
+ clif_displaymessage(sd->fd, "Monsters are no longer ignoring you.");
+ }
+
+ return 0;
+}
+/*==========================================
+ * @fakename
+ * => Gives your character a fake name. [Valaris]
+ *------------------------------------------
+ */
+int atcommand_fakename(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+
+ char name[NAME_LENGTH];
+ nullpo_retr(-1, sd);
+
+ if((!message || !*message) && strlen(sd->fakename) > 1) {
+ sd->fakename[0]='\0';
+ pc_setpos(sd, sd->mapindex, sd->bl.x, sd->bl.y, 3);
+ clif_displaymessage(sd->fd,"Returned to real name.");
+ return 0;
+ }
+
+ if (!message || !*message) {
+ clif_displaymessage(sd->fd,"You must enter a name.");
+ return 0;
+ }
+
+
+ if (sscanf(message, "%23[^\n]", name) < 1) {
+ return 0;
+ }
+
+ if(strlen(name) < 2) {
+ clif_displaymessage(sd->fd,"Fake name must be at least two characters.");
+ return 0;
+ }
+
+ memcpy(sd->fakename,name,NAME_LENGTH-1);
+ clif_charnameack(0, &sd->bl);
+ clif_displaymessage(sd->fd,"Fake name enabled.");
+
+ return 0;
+}
+/*==========================================
+ * @mapflag [flagap name] [1|0|on|off] [map name] by Lupus
+ * => Shows information about the map flags [map name]
+ * Also set flags
+ *------------------------------------------
+ */
+int atcommand_mapflag(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+// WIP
+ return 0;
+}
+
+/*===================================
+ * Remove some messages
+ *-----------------------------------
+ */
+int atcommand_showexp(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ if (sd->state.showexp) {
+ sd->state.showexp = 0;
+ clif_displaymessage(fd, "Gained exp will not be shown.");
+ return 0;
+ }
+
+ sd->state.showexp = 1;
+ clif_displaymessage(fd, "Gained exp is now shown");
+ return 0;
+}
+
+int atcommand_showzeny(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ if (sd->state.showzeny) {
+ sd->state.showzeny = 0;
+ clif_displaymessage(fd, "Gained zeny will not be shown.");
+ return 0;
+ }
+
+ sd->state.showzeny = 1;
+ clif_displaymessage(fd, "Gained zeny is now shown");
+ return 0;
+}
+
+int atcommand_showdelay(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ if (sd->state.showdelay) {
+ sd->state.showdelay = 0;
+ clif_displaymessage(fd, "Skill delay failures won't be shown.");
+ return 0;
+ }
+
+ sd->state.showdelay = 1;
+ clif_displaymessage(fd, "Skill delay failures are shown now.");
+ return 0;
+}
+
+/*==========================================
+ * Duel organizing functions [LuzZza]
+ *
+ * @duel [limit|nick] - create a duel
+ * @invite <nick> - invite player
+ * @accept - accept invitation
+ * @reject - reject invitation
+ * @leave - leave duel
+ *------------------------------------------
+ */
+int atcommand_invite(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ unsigned int did = sd->duel_group;
+ struct map_session_data *target_sd = map_nick2sd((char *)message);
+
+ if(did <= 0) {
+ // "Duel: @invite without @duel."
+ clif_displaymessage(fd, msg_txt(350));
+ return 0;
+ }
+
+ if(duel_list[did].max_players_limit > 0 &&
+ duel_list[did].members_count >= duel_list[did].max_players_limit) {
+
+ // "Duel: Limit of players is reached."
+ clif_displaymessage(fd, msg_txt(351));
+ return 0;
+ }
+
+ if(target_sd == NULL) {
+ // "Duel: Player not found."
+ clif_displaymessage(fd, msg_txt(352));
+ return 0;
+ }
+
+ if(target_sd->duel_group > 0 || target_sd->duel_invite > 0) {
+ // "Duel: Player already in duel."
+ clif_displaymessage(fd, msg_txt(353));
+ return 0;
+ }
+
+ duel_invite(did, sd, target_sd);
+ // "Duel: Invitation has been sent."
+ clif_displaymessage(fd, msg_txt(354));
+ return 0;
+}
+
+int atcommand_duel(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ char output[256];
+ unsigned int maxpl=0, newduel;
+ struct map_session_data *target_sd;
+
+ /* // Commnted because it can be disabled in at-comms conf.
+ if(!battle_config.duel_enable) {
+ clif_displaymessage(fd, "Duel: duel is disable.");
+ return 0;
+ }
+ */
+
+ if(sd->duel_group > 0) {
+ duel_showinfo(sd->duel_group, sd);
+ return 0;
+ }
+
+ if(sd->duel_invite > 0) {
+ // "Duel: @duel without @reject."
+ clif_displaymessage(fd, msg_txt(355));
+ return 0;
+ }
+
+ if(!duel_checktime(sd)) {
+ // "Duel: You can take part in duel only one time per %d minutes."
+ sprintf(output, msg_txt(356), battle_config.duel_time_interval);
+ clif_displaymessage(fd, output);
+ return 0;
+ }
+
+ if(strlen(message) > 0) {
+ if(sscanf(message, "%d", &maxpl) >= 1) {
+ if(maxpl < 2 || maxpl > 65535) {
+ clif_displaymessage(fd, msg_txt(357)); // "Duel: Invalid value."
+ return 0;
+ }
+ duel_create(sd, maxpl);
+ } else {
+ target_sd = map_nick2sd((char *)message);
+ if(target_sd != NULL) {
+ if((newduel = duel_create(sd, 2)) != -1) {
+ if(target_sd->duel_group > 0 || target_sd->duel_invite > 0) {
+ clif_displaymessage(fd, msg_txt(353)); // "Duel: Player already in duel."
+ return 0;
+ }
+ duel_invite(newduel, sd, target_sd);
+ clif_displaymessage(fd, msg_txt(354)); // "Duel: Invitation has been sent."
+ }
+ } else {
+ // "Duel: Player not found."
+ clif_displaymessage(fd, msg_txt(352));
+ return 0;
+ }
+ }
+ } else
+ duel_create(sd, 0);
+
+ return 0;
+}
+
+
+int atcommand_leave(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ if(sd->duel_group <= 0) {
+ // "Duel: @leave without @duel."
+ clif_displaymessage(fd, msg_txt(358));
+ return 0;
+ }
+
+ duel_leave(sd->duel_group, sd);
+ clif_displaymessage(fd, msg_txt(359)); // "Duel: You left the duel."
+ return 0;
+}
+
+int atcommand_accept(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ char output[256];
+
+ if(!duel_checktime(sd)) {
+ // "Duel: You can take part in duel only one time per %d minutes."
+ sprintf(output, msg_txt(356), battle_config.duel_time_interval);
+ clif_displaymessage(fd, output);
+ return 0;
+ }
+
+ if(sd->duel_invite <= 0) {
+ // "Duel: @accept without invititation."
+ clif_displaymessage(fd, msg_txt(360));
+ return 0;
+ }
+
+ duel_accept(sd->duel_invite, sd);
+ // "Duel: Invitation has been accepted."
+ clif_displaymessage(fd, msg_txt(361));
+ return 0;
+}
+
+int atcommand_reject(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ if(sd->duel_invite <= 0) {
+ // "Duel: @reject without invititation."
+ clif_displaymessage(fd, msg_txt(352));
+ return 0;
+ }
+
+ duel_reject(sd->duel_invite, sd);
+ // "Duel: Invitation has been rejected."
+ clif_displaymessage(fd, msg_txt(352));
+ return 0;
+}
+
+/*===================================
+ * Away message (@away, @aw) [LuzZza]
+ *-----------------------------------
+ */
+int atcommand_away(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ if(strlen(message) > 0) {
+ if(strlen(message) > 128)
+ return -1;
+ strcpy(sd->away_message, message);
+ //"Away automessage has been activated."
+ clif_displaymessage(fd, msg_txt(546));
+ } else {
+ if(strlen(sd->away_message) > 0) {
+ sd->away_message[0] = 0;
+ //"Away automessage has been disabled."
+ clif_displaymessage(fd, msg_txt(547));
+ return 0;
+ }
+ //"Usage: @away,@aw <message>. Enter empty message for disable it."
+ clif_displaymessage(fd, msg_txt(548));
+ }
+ return 0;
+}
+
+// @clone/@slaveclone/@evilclone <playername> [Valaris]
+int atcommand_clone(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ int x=0,y=0,flag=0,master=0,i=0;
+ struct map_session_data *pl_sd=NULL;
+
+ if (!message || !*message) {
+ clif_displaymessage(sd->fd,"You must enter a name or character ID.");
+ return 0;
+ }
+
+ if((pl_sd=map_nick2sd((char *)message)) == NULL && (pl_sd=map_charid2sd(atoi(message))) == NULL) {
+ clif_displaymessage(fd, "Player not found.");
+ return 0;
+ }
+
+ if(pc_isGM(pl_sd) > pc_isGM(sd)) {
+ clif_displaymessage(fd, "Cannot clone a player of higher GM level than yourself.");
+ return 0;
+ }
+
+ if (strcmpi(command, "@clone") == 0)
+ flag = 1;
+ else if (strcmpi(command, "@slaveclone") == 0) {
+ flag = 2;
+ master = sd->bl.id;
+ if (battle_config.atc_slave_clone_limit
+ && mob_countslave(&sd->bl) >= battle_config.atc_slave_clone_limit) {
+ clif_displaymessage(fd, "You've reached your slave clones limit.");
+ return 0;
+ }
+ }
+
+ do {
+ x = sd->bl.x + (rand() % 10 - 5);
+ y = sd->bl.y + (rand() % 10 - 5);
+ } while (map_getcell(sd->bl.m,x,y,CELL_CHKNOPASS) && i++ < 10);
+
+ if (i >= 10) {
+ x = sd->bl.x;
+ y = sd->bl.y;
+ }
+
+
+ if((x = mob_clone_spawn(pl_sd, (char*)mapindex_id2name(sd->mapindex), x, y, "", master, 0, flag?1:0, 0)) > 0) {
+ clif_displaymessage(fd, msg_txt(126+flag*2));
+ return 0;
+ }
+ clif_displaymessage(fd, msg_txt(127+flag*2));
+ return 0;
+}
+
+/*===================================
+ * Main chat [LuzZza]
+ * Usage: @main <on|off|message>
+ *-----------------------------------
+ */
+int atcommand_main(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ if(strlen(message) > 0) {
+
+ if(strlen(message) > 128)
+ return -1;
+
+ if(strcmpi(message, "on") == 0) {
+ if(!sd->state.mainchat) {
+ sd->state.mainchat = 1;
+ clif_displaymessage(fd, msg_txt(380)); // Main chat has been activated.
+ } else {
+ clif_displaymessage(fd, msg_txt(381)); // Main chat already activated.
+ }
+ } else if(strcmpi(message, "off") == 0) {
+ if(sd->state.mainchat) {
+ sd->state.mainchat = 0;
+ clif_displaymessage(fd, msg_txt(382)); // Main chat has been disabled.
+ } else {
+ clif_displaymessage(fd, msg_txt(383)); // Main chat already disabled.
+ }
+ } else {
+ if(!sd->state.mainchat) {
+ sd->state.mainchat = 1;
+ clif_displaymessage(fd, msg_txt(380)); // Main chat has been activated.
+ }
+ if (sd->sc_data[SC_NOCHAT].timer != -1) {
+ clif_displaymessage(fd, msg_txt(387));
+ return -1;
+ }
+ sprintf(atcmd_output, msg_txt(386), sd->status.name, message);
+ // I use 0xFE000000 color for signalizing that this message is
+ // main chat message. 0xFE000000 is invalid color, same using
+ // 0xFF000000 for simple (not colored) GM messages. [LuzZza]
+ intif_announce(atcmd_output, strlen(atcmd_output) + 1, 0xFE000000, 0);
+ }
+
+ } else {
+
+ if(sd->state.mainchat)
+ // Main chat currently enabled. Usage: @main <on|off>, @main <message>.
+ clif_displaymessage(fd, msg_txt(384));
+ else
+ // Main chat currently disabled. Usage: @main <on|off>, @main <message>.
+ clif_displaymessage(fd, msg_txt(385));
+ }
+ return 0;
+}
+
+void do_init_atcommand() {
+ users_db = db_alloc(__FILE__,__LINE__,DB_UINT,DB_OPT_BASE,sizeof(int));
+ duel_count = 0;
+ memset(&duel_list[0], 0, sizeof(duel_list));
+ return;
+}
+
+void do_final_atcommand() {
+ users_db->destroy(users_db,NULL);
+}
diff --git a/src/map/atcommand.h b/src/map/atcommand.h
new file mode 100644
index 000000000..5c24778fa
--- /dev/null
+++ b/src/map/atcommand.h
@@ -0,0 +1,319 @@
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#ifndef _ATCOMMAND_H_
+#define _ATCOMMAND_H_
+
+enum AtCommandType {
+ AtCommand_None = -1,
+ AtCommand_Broadcast = 0,
+ AtCommand_LocalBroadcast,
+ AtCommand_MapMove,
+ AtCommand_ResetState,
+ AtCommand_RuraP,
+ AtCommand_Rura,
+ AtCommand_Warp,
+ AtCommand_Where,
+ AtCommand_JumpTo,
+ AtCommand_Jump,
+ AtCommand_Who,
+ AtCommand_Who2,
+ AtCommand_Who3,
+ AtCommand_WhoMap,
+ AtCommand_WhoMap2,
+ AtCommand_WhoMap3,
+ AtCommand_WhoGM,
+ AtCommand_Save,
+ AtCommand_Load,
+ AtCommand_Speed,
+ AtCommand_Storage,
+ AtCommand_GuildStorage,
+ AtCommand_Option,
+ AtCommand_Hide,
+ AtCommand_JobChange,
+ AtCommand_JobChange2,
+ AtCommand_JobChange3,
+ AtCommand_Die,
+ AtCommand_Kill,
+ AtCommand_Alive,
+ AtCommand_Kami,
+ AtCommand_KamiB,
+ AtCommand_KamiC, //LuzZza
+ AtCommand_Heal,
+ AtCommand_Item,
+ AtCommand_Item2,
+ AtCommand_ItemReset,
+ AtCommand_ItemCheck,
+ AtCommand_BaseLevelUp,
+ AtCommand_JobLevelUp,
+ AtCommand_H,
+ AtCommand_Help,
+ AtCommand_H2,
+ AtCommand_Help2,
+ AtCommand_GM,
+ AtCommand_PvPOff,
+ AtCommand_PvPOn,
+ AtCommand_GvGOff,
+ AtCommand_GvGOn,
+ AtCommand_Model,
+ AtCommand_Go,
+ AtCommand_Spawn,
+ AtCommand_Monster,
+ AtCommand_MonsterSmall,
+ AtCommand_MonsterBig,
+ AtCommand_KillMonster,
+ AtCommand_KillMonster2,
+ AtCommand_Refine,
+ AtCommand_Produce,
+ AtCommand_Memo,
+ AtCommand_GAT,
+ AtCommand_Packet,
+ AtCommand_WaterLevel,
+ AtCommand_StatusPoint,
+ AtCommand_SkillPoint,
+ AtCommand_Zeny,
+ AtCommand_Param,
+ AtCommand_Strength,
+ AtCommand_Agility,
+ AtCommand_Vitality,
+ AtCommand_Intelligence,
+ AtCommand_Dexterity,
+ AtCommand_Luck,
+ AtCommand_GuildLevelUp,
+ AtCommand_MakeEgg,
+ AtCommand_PetFriendly,
+ AtCommand_PetHungry,
+ AtCommand_PetRename,
+ AtCommand_Recall,
+ AtCommand_Revive,
+ AtCommand_CharacterStatsAll,
+ AtCommand_CharacterLoad,
+ AtCommand_Night,
+ AtCommand_Day,
+ AtCommand_Doom,
+ AtCommand_DoomMap,
+ AtCommand_Raise,
+ AtCommand_RaiseMap,
+ AtCommand_Kick,
+ AtCommand_KickAll,
+ AtCommand_AllSkill,
+ AtCommand_QuestSkill,
+ AtCommand_LostSkill,
+ AtCommand_SpiritBall,
+ AtCommand_Party,
+ AtCommand_Guild,
+ AtCommand_AgitStart,
+ AtCommand_AgitEnd,
+ AtCommand_MapExit,
+ AtCommand_IDSearch,
+ AtCommand_RecallAll,
+ AtCommand_ReloadItemDB,
+ AtCommand_ReloadMobDB,
+ AtCommand_ReloadSkillDB,
+ AtCommand_ReloadScript,
+ AtCommand_ReloadGMDB,
+ AtCommand_ReloadAtcommand,
+ AtCommand_ReloadBattleConf,
+ AtCommand_ReloadStatusDB,
+ AtCommand_ReloadPcDB,
+ AtCommand_ReloadMOTD, // [Valaris]
+ AtCommand_MapInfo,
+ AtCommand_Dye,
+ AtCommand_Hstyle,
+ AtCommand_Hcolor,
+ AtCommand_StatAll,
+ AtCommand_CharBlock, // by Yor
+ AtCommand_CharBan, // by Yor
+ AtCommand_CharUnBlock, // by Yor
+ AtCommand_CharUnBan, // by Yor
+ AtCommand_MountPeco, // by Valaris
+ AtCommand_CharMountPeco, // by Yor
+ AtCommand_GuildSpy, // [Syrus22]
+ AtCommand_PartySpy, // [Syrus22]
+ AtCommand_RepairAll, // [Valaris]
+ AtCommand_GuildRecall, // by Yor
+ AtCommand_PartyRecall, // by Yor
+ AtCommand_Nuke, // [Valaris]
+ AtCommand_Shownpc,
+ AtCommand_Hidenpc,
+ AtCommand_Loadnpc,
+ AtCommand_Unloadnpc,
+ AtCommand_ServerTime, // by Yor
+ AtCommand_CharDelItem, // by Yor
+ AtCommand_Jail, // by Yor
+ AtCommand_UnJail, // by Yor
+ AtCommand_Disguise, // [Valaris]
+ AtCommand_UnDisguise, // by Yor
+ AtCommand_CharDisguise, // Kalaspuff
+ AtCommand_CharUnDisguise, // Kalaspuff
+ AtCommand_EMail, // by Yor
+ AtCommand_Hatch,
+ AtCommand_Effect, // by Apple
+ AtCommand_Char_Cart_List, // by Yor
+ AtCommand_AddWarp, // by MouseJstr
+ AtCommand_Follow, // by MouseJstr
+ AtCommand_SkillOn, // by MouseJstr
+ AtCommand_SkillOff, // by MouseJstr
+ AtCommand_Killer, // by MouseJstr
+ AtCommand_NpcMove, // by MouseJstr
+ AtCommand_Killable, // by MouseJstr
+ AtCommand_CharKillable, // by MouseJstr
+ AtCommand_Dropall, // by MouseJstr
+ AtCommand_Chardropall, // by MouseJstr
+ AtCommand_Storeall, // by MouseJstr
+ AtCommand_Charstoreall, // by MouseJstr
+ AtCommand_Skillid, // by MouseJstr
+ AtCommand_Useskill, // by MouseJstr
+ AtCommand_Summon,
+ AtCommand_Rain,
+ AtCommand_Snow,
+ AtCommand_Sakura,
+ AtCommand_Clouds,
+ AtCommand_Clouds2, // [Valaris]
+ AtCommand_Fog,
+ AtCommand_Fireworks,
+ AtCommand_Leaves,
+ AtCommand_AdjGmLvl, // MouseJstr
+ AtCommand_AdjCmdLvl, // MouseJstr
+ AtCommand_Trade, // MouseJstr
+ AtCommand_Send,
+ AtCommand_SetBattleFlag,
+ AtCommand_UnMute,
+ AtCommand_Clearweather, // by Dexity
+ AtCommand_UpTime, // by MC Cameri
+ AtCommand_ChangeSex, // by MC Cameri
+ AtCommand_Mute, // [celest]
+ AtCommand_WhoZeny, // [Valaris] <-- LOL...(MC Cameri) worth it.
+ AtCommand_HappyHappyJoyJoy, // [Valaris]
+ AtCommand_Refresh, // by MC Cameri
+ AtCommand_PetId, // by MC Cameri
+ AtCommand_Identify, // by MC Cameri
+ AtCommand_Gmotd, // Added by MC Cameri, created by davidsiaw
+ AtCommand_MiscEffect, // by MC Cameri
+ AtCommand_MobSearch,
+ AtCommand_CleanMap,
+ AtCommand_NpcTalk,
+ AtCommand_PetTalk,
+ AtCommand_Users,
+ // SQL-only commands start
+#ifndef TXT_ONLY
+ AtCommand_CheckMail, // [Valaris]
+ AtCommand_ListMail, // [Valaris]
+ AtCommand_ListNewMail, // [Valaris]
+ AtCommand_ReadMail, // [Valaris]
+ AtCommand_SendMail, // [Valaris]
+ AtCommand_DeleteMail, // [Valaris]
+ AtCommand_SendPriorityMail, // [Valaris]
+ AtCommand_RefreshOnline, // [Valaris]
+ // SQL-only commands end
+#endif
+ AtCommand_SkillTree, // by MouseJstr
+ AtCommand_Marry, // by MouseJstr
+ AtCommand_Divorce, // by MouseJstr
+ AtCommand_Grind, // by MouseJstr
+ AtCommand_Grind2, // by MouseJstr
+
+ AtCommand_Me, //added by massdriller, code by lordalfa
+
+ AtCommand_DMStart, // by MouseJstr
+ AtCommand_DMTick, // by MouseJstr
+
+ AtCommand_JumpToId, // by Dino9021
+ AtCommand_JumpToId2, // by Dino9021
+ AtCommand_RecallId, // by Dino9021
+ AtCommand_RecallId2, // by Dino9021
+ AtCommand_KickId, // by Dino9021
+ AtCommand_KickId2, // by Dino9021
+ AtCommand_ReviveId, // by Dino9021
+ AtCommand_ReviveId2, // by Dino9021
+ AtCommand_KillId, // by Dino9021
+ AtCommand_KillId2, // by Dino9021
+ AtCommand_CharKillableId, // by Dino9021
+ AtCommand_CharKillableId2, // by Dino9021
+ AtCommand_Sound,
+ AtCommand_UndisguiseAll,
+ AtCommand_DisguiseAll,
+ AtCommand_ChangeLook,
+ AtCommand_AutoLoot, //by Upa-Kun
+ AtCommand_MobInfo, //by Lupus
+ AtCommand_Adopt, // by Veider
+ AtCommand_Version, // by Ancyker
+
+ AtCommand_MuteArea, // MouseJstr
+ AtCommand_Shuffle, // MouseJstr
+ AtCommand_Rates, // MouseJstr
+
+ AtCommand_ItemInfo, // Lupus
+ AtCommand_MapFlag, // Lupus
+ AtCommand_MonsterIgnore, // [Valaris]
+ AtCommand_FakeName, // [Valaris]
+ AtCommand_Size, // [Valaris]
+ AtCommand_ShowDelay,
+ AtCommand_ShowExp,
+ AtCommand_ShowZeny,
+ AtCommand_AutoTrade,//durf
+ AtCommand_ChangeGM,//durf
+ AtCommand_ChangeLeader,
+
+ AtCommand_Invite, // By LuzZza
+ AtCommand_Duel, // By LuzZza
+ AtCommand_Leave, // By LuzZza
+ AtCommand_Accept, // By LuzZza
+ AtCommand_Reject, // By LuzZza
+
+ AtCommand_Away, // LuzZza
+ AtCommand_Main, // LuzZza
+
+ AtCommand_Clone, // [Valaris]
+
+ // end <- Ahem, guys, don't place AtCommands after AtCommand_Unknown! [Skotlex]
+ AtCommand_Unknown,
+ AtCommand_MAX
+};
+
+typedef enum AtCommandType AtCommandType;
+
+typedef struct AtCommandInfo {
+ AtCommandType type;
+ const char* command;
+ int level;
+ int (*proc)(const int, struct map_session_data*,
+ const char* command, const char* message);
+} AtCommandInfo;
+
+AtCommandType
+is_atcommand(const int fd, struct map_session_data* sd, const char* message, int gmlvl);
+
+AtCommandType atcommand(
+ struct map_session_data *sd,
+ const int level, const char* message, AtCommandInfo* info);
+int get_atcommand_level(const AtCommandType type);
+
+char * msg_txt(int msg_number); // [Yor]
+char * player_title_txt(int level); // [Lupus]
+
+void do_init_atcommand(void);
+void do_final_atcommand(void);
+
+int atcommand_item(const int fd, struct map_session_data* sd,const char* command, const char* message); // [Valaris]
+int atcommand_rura(const int fd, struct map_session_data* sd,const char* command, const char* message); // [Yor]
+int atcommand_jumpto(const int fd, struct map_session_data* sd, const char* command, const char* message); // [Yor]
+int atcommand_recall(const int fd, struct map_session_data* sd, const char* command, const char* message); // [Yor]
+int atcommand_monster(const int fd, struct map_session_data* sd, const char* command, const char* message);
+
+int duel_leave(const unsigned int did, struct map_session_data* sd); // [LuzZza]
+int duel_reject(const unsigned int did, struct map_session_data* sd); // [LuzZza]
+
+int atcommand_config_read(const char *cfgName);
+int msg_config_read(const char *cfgName);
+void do_final_msg(void);
+
+char *estr_lower(char *str);
+
+int e_mail_check(char *email);
+
+#define MAX_MSG 1000
+extern char *msg_table[MAX_MSG];
+
+#endif
+
diff --git a/src/map/battle.c b/src/map/battle.c
new file mode 100644
index 000000000..91563d939
--- /dev/null
+++ b/src/map/battle.c
@@ -0,0 +1,4412 @@
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+
+#include "battle.h"
+#include "../common/timer.h"
+#include "../common/nullpo.h"
+#include "../common/malloc.h"
+#include "../common/showmsg.h"
+
+#include "map.h"
+#include "pc.h"
+#include "status.h"
+#include "skill.h"
+#include "mob.h"
+#include "itemdb.h"
+#include "clif.h"
+#include "pet.h"
+#include "guild.h"
+#include "party.h"
+
+#define is_boss(bl) status_get_mexp(bl) // Can refine later [Aru]
+
+int attr_fix_table[4][10][10];
+
+struct Battle_Config battle_config;
+
+/*==========================================
+ * 自分を?ックしているMOBの?を?える(foreachclient)
+ *------------------------------------------
+ */
+static int battle_counttargeted_sub(struct block_list *bl, va_list ap)
+{
+ int id, *c, target_lv;
+ struct block_list *src;
+
+ nullpo_retr(0, bl);
+ nullpo_retr(0, ap);
+
+ id = va_arg(ap,int);
+ nullpo_retr(0, c = va_arg(ap, int *));
+ src = va_arg(ap,struct block_list *);
+ target_lv = va_arg(ap,int);
+
+ if (id == bl->id || (src && id == src->id))
+ return 0;
+ if (bl->type == BL_PC) {
+ struct map_session_data *sd = (struct map_session_data *)bl;
+ if (sd && sd->attacktarget == id && sd->attacktimer != -1 && sd->attacktarget_lv >= target_lv)
+ (*c)++;
+ }
+ else if (bl->type == BL_MOB) {
+ struct mob_data *md = (struct mob_data *)bl;
+ if (md && md->target_id == id && md->timer != -1 && md->state.state == MS_ATTACK && md->target_lv >= target_lv)
+ (*c)++;
+ //printf("md->target_lv:%d, target_lv:%d\n", md->target_lv, target_lv);
+ }
+ else if (bl->type == BL_PET) {
+ struct pet_data *pd = (struct pet_data *)bl;
+ if (pd && pd->target_id == id && pd->timer != -1 && pd->state.state == MS_ATTACK && pd->target_lv >= target_lv)
+ (*c)++;
+ }
+
+ return 0;
+}
+/*==========================================
+ * 自分を?ックしている対?ロの?狽返す(汎用)
+ * 戻りは?ョ?狽ナ0以?
+ *------------------------------------------
+ */
+int battle_counttargeted(struct block_list *bl,struct block_list *src,int target_lv)
+{
+ int c = 0;
+ nullpo_retr(0, bl);
+
+ map_foreachinarea(battle_counttargeted_sub, bl->m,
+ bl->x-AREA_SIZE, bl->y-AREA_SIZE,
+ bl->x+AREA_SIZE, bl->y+AREA_SIZE, BL_CHAR,
+ bl->id, &c, src, target_lv);
+
+ return c;
+}
+
+/*==========================================
+ * Get random targetting enemy
+ *------------------------------------------
+ */
+static int battle_gettargeted_sub(struct block_list *bl, va_list ap)
+{
+ struct block_list **bl_list;
+ struct block_list *target;
+ int *c;
+
+ nullpo_retr(0, bl);
+ nullpo_retr(0, ap);
+
+ bl_list = va_arg(ap, struct block_list **);
+ nullpo_retr(0, c = va_arg(ap, int *));
+ nullpo_retr(0, target = va_arg(ap, struct block_list *));
+
+ if (bl->id == target->id)
+ return 0;
+ if (*c >= 24)
+ return 0;
+
+ if (bl->type == BL_PC) {
+ struct map_session_data *sd = (struct map_session_data *)bl;
+ if (!sd || sd->attacktarget != target->id || sd->attacktimer == -1)
+ return 0;
+ } else if (bl->type == BL_MOB) {
+ struct mob_data *md = (struct mob_data *)bl;
+ if (!md || md->target_id != target->id || md->timer == -1 || md->state.state != MS_ATTACK)
+ return 0;
+ } else if (bl->type == BL_PET) {
+ struct pet_data *pd = (struct pet_data *)bl;
+ if (!pd || pd->target_id != target->id || pd->timer == -1 || pd->state.state != MS_ATTACK)
+ return 0;
+ }
+
+ bl_list[(*c)++] = bl;
+ return 0;
+}
+
+int battle_getcurrentskill(struct block_list *bl)
+{ //Returns the current/last skill in use by this bl.
+ switch (bl->type)
+ {
+ case BL_PC:
+ return ((struct map_session_data*)bl)->skillid;
+ case BL_MOB:
+ return ((struct mob_data*)bl)->skillid;
+ case BL_PET:
+ return 0; //Skill data is not stored for pets...
+ break;
+ case BL_SKILL:
+ {
+ struct skill_unit * su = (struct skill_unit*)bl;
+ if (su->group)
+ return su->group->skill_id;
+ }
+ break;
+ }
+ return 0;
+}
+
+//Returns the id of the current targetted character of the passed bl. [Skotlex]
+int battle_gettarget(struct block_list *bl)
+{
+ switch (bl->type)
+ {
+ case BL_PC:
+ return ((struct map_session_data*)bl)->attacktarget;
+ case BL_MOB:
+ return ((struct mob_data*)bl)->target_id;
+ case BL_PET:
+ return ((struct pet_data*)bl)->target_id;
+ }
+ return 0;
+}
+
+struct block_list* battle_gettargeted(struct block_list *target)
+{
+ struct block_list *bl_list[24];
+ int c = 0;
+ nullpo_retr(NULL, target);
+
+ memset(bl_list, 0, sizeof(bl_list));
+ map_foreachinarea(battle_gettargeted_sub, target->m,
+ target->x-AREA_SIZE, target->y-AREA_SIZE,
+ target->x+AREA_SIZE, target->y+AREA_SIZE, BL_CHAR,
+ bl_list, &c, target);
+ if (c == 0 || c > 24)
+ return NULL;
+ return bl_list[rand()%c];
+}
+
+// ダ??[ジの遅延
+struct delay_damage {
+ struct block_list *src;
+ int target;
+ int damage;
+ unsigned short distance;
+ unsigned short skill_lv;
+ unsigned short skill_id;
+ unsigned short dmg_lv;
+ unsigned short flag;
+ unsigned char attack_type;
+};
+
+int battle_delay_damage_sub (int tid, unsigned int tick, int id, int data)
+{
+ struct delay_damage *dat = (struct delay_damage *)data;
+ struct block_list *target = map_id2bl(dat->target);
+ if (target && dat && map_id2bl(id) == dat->src && target->prev != NULL && !status_isdead(target) &&
+ target->m == dat->src->m && check_distance_bl(dat->src, target, dat->distance)) //Check to see if you haven't teleported. [Skotlex]
+ {
+ battle_damage(dat->src, target, dat->damage, dat->flag);
+ if (!status_isdead(target) && (dat->dmg_lv == ATK_DEF || dat->damage > 0) && dat->attack_type)
+ skill_additional_effect(dat->src,target,dat->skill_id,dat->skill_lv,dat->attack_type, tick);
+ }
+ aFree(dat);
+ return 0;
+}
+
+int battle_delay_damage (unsigned int tick, struct block_list *src, struct block_list *target, int attack_type, int skill_id, int skill_lv, int damage, int dmg_lv, int flag)
+{
+ struct delay_damage *dat;
+ nullpo_retr(0, src);
+ nullpo_retr(0, target);
+
+ if (!battle_config.delay_battle_damage) {
+ battle_damage(src, target, damage, flag);
+ if (!status_isdead(target) && (damage > 0 || dmg_lv == ATK_DEF) && attack_type)
+ skill_additional_effect(src, target, skill_id, skill_lv, attack_type, gettick());
+ return 0;
+ }
+ dat = (struct delay_damage *)aCalloc(1, sizeof(struct delay_damage));
+ dat->src = src;
+ dat->target = target->id;
+ dat->skill_id = skill_id;
+ dat->skill_lv = skill_lv;
+ dat->attack_type = attack_type;
+ dat->damage = damage;
+ dat->dmg_lv = dmg_lv;
+ dat->flag = flag;
+ dat->distance = distance_bl(src, target)+10; //Attack should connect regardless unless you teleported.
+ add_timer(tick, battle_delay_damage_sub, src->id, (int)dat);
+
+ return 0;
+}
+
+// 実?ロにHPを操?
+int battle_damage(struct block_list *bl,struct block_list *target,int damage, int flag)
+{
+ struct map_session_data *sd = NULL;
+ struct status_change *sc_data;
+ short *sc_count;
+
+ nullpo_retr(0, target); //blはNULLで呼ばれることがあるので他でチェック
+
+ sc_data = status_get_sc_data(target);
+ sc_count = status_get_sc_count(target);
+
+ if (damage == 0 ||
+ target->prev == NULL ||
+ target->type == BL_PET)
+ return 0;
+
+ if (bl) {
+ if (bl->prev == NULL)
+ return 0;
+ if (bl->type == BL_PC) {
+ nullpo_retr(0, sd = (struct map_session_data *)bl);
+ }
+ }
+
+ if (damage < 0)
+ return battle_heal(bl,target,-damage,0,flag);
+
+ if (!flag && sc_count && *sc_count > 0) {
+ // 凍結?A?ホ化?A?眠を?チ去
+ if (sc_data[SC_FREEZE].timer != -1)
+ status_change_end(target,SC_FREEZE,-1);
+ if (sc_data[SC_STONE].timer!=-1 && sc_data[SC_STONE].val2 == 0)
+ status_change_end(target,SC_STONE,-1);
+ if (sc_data[SC_SLEEP].timer != -1)
+ status_change_end(target,SC_SLEEP,-1);
+ if (sc_data[SC_WINKCHARM].timer != -1)
+ status_change_end(target,SC_WINKCHARM,-1);
+ }
+
+ if (target->type == BL_MOB) { // MOB
+ struct mob_data *md = (struct mob_data *)target;
+ if (md && md->skilltimer != -1 && md->state.skillcastcancel) // 詠?・妨害
+ skill_castcancel(target,0);
+ return mob_damage(bl,md,damage,0);
+ } else if (target->type == BL_PC) { // PC
+ struct map_session_data *tsd = (struct map_session_data *)target;
+ if (!tsd)
+ return 0;
+ if (sc_data[SC_DEVOTION].val1 && bl && battle_getcurrentskill(bl) != PA_PRESSURE)
+ { //Devotion only works on attacks from a source (prevent it from absorbing coma) [Skotlex]
+ struct map_session_data *sd2 = map_id2sd(tsd->sc_data[SC_DEVOTION].val1);
+ if (sd2 && sd2->devotion[sc_data[SC_DEVOTION].val2] == target->id)
+ {
+ clif_damage(bl, &sd2->bl, gettick(), 0, 0, damage, 0, 0, 0);
+ pc_damage(&sd2->bl, sd2, damage);
+ return 0;
+ } else
+ status_change_end(target, SC_DEVOTION, -1);
+ }
+
+ if (tsd->skilltimer != -1) { // 詠?・妨害
+ // フェンカ?[ドや妨害されないスキルかの検?ク
+ if ((!tsd->special_state.no_castcancel || map_flag_gvg(target->m)) && tsd->state.skillcastcancel &&
+ !tsd->special_state.no_castcancel2)
+ skill_castcancel(target,0);
+ }
+ return pc_damage(bl,tsd,damage);
+ } else if (target->type == BL_SKILL)
+ return skill_unit_ondamaged((struct skill_unit *)target, bl, damage, gettick());
+ return 0;
+}
+
+int battle_heal(struct block_list *bl,struct block_list *target,int hp,int sp,int flag)
+{
+ nullpo_retr(0, target); //blはNULLで呼ばれることがあるので他でチェック
+
+ if (target->type == BL_PET)
+ return 0;
+ if (target->type == BL_PC && pc_isdead((struct map_session_data *)target) )
+ return 0;
+ if (hp == 0 && sp == 0)
+ return 0;
+
+ if (hp < 0)
+ return battle_damage(bl,target,-hp,flag);
+
+ if (target->type == BL_MOB)
+ return mob_heal((struct mob_data *)target,hp);
+ else if (target->type == BL_PC)
+ return pc_heal((struct map_session_data *)target,hp,sp);
+ return 0;
+}
+
+// ?U撃停止
+int battle_stopattack(struct block_list *bl)
+{
+ nullpo_retr(0, bl);
+ if (bl->type == BL_MOB)
+ return mob_stopattack((struct mob_data*)bl);
+ else if (bl->type == BL_PC)
+ return pc_stopattack((struct map_session_data*)bl);
+ else if (bl->type == BL_PET)
+ return pet_stopattack((struct pet_data*)bl);
+ return 0;
+}
+
+// Returns whether the given object is moving or not.
+int battle_iswalking(struct block_list *bl) {
+ switch (bl->type)
+ {
+ case BL_PC:
+ return (((struct map_session_data*)bl)->walktimer != -1);
+ case BL_MOB:
+ return (((struct mob_data*)bl)->state.state == MS_WALK);
+ case BL_PET:
+ return (((struct pet_data*)bl)->state.state == MS_WALK);
+ default:
+ return 0;
+ }
+}
+// 移動停止
+int battle_stopwalking(struct block_list *bl,int type)
+{
+ nullpo_retr(0, bl);
+ if (bl->type == BL_MOB)
+ return mob_stop_walking((struct mob_data*)bl,type);
+ else if (bl->type == BL_PC)
+ return pc_stop_walking((struct map_session_data*)bl,type);
+ else if (bl->type == BL_PET)
+ return pet_stop_walking((struct pet_data*)bl,type);
+ return 0;
+}
+
+
+/*==========================================
+ * Does attribute fix modifiers.
+ * Added passing of the chars so that the status changes can affect it. [Skotlex]
+ * Note: Passing src/target == NULL is perfectly valid, it skips SC_ checks.
+ *------------------------------------------
+ */
+int battle_attr_fix(struct block_list *src, struct block_list *target, int damage,int atk_elem,int def_elem)
+{
+ int def_type = def_elem % 10, def_lv = def_elem / 10 / 2;
+ struct status_change *sc_data=NULL, *tsc_data=NULL;
+ int ratio;
+
+ if (src) sc_data = status_get_sc_data(src);
+ if (target) tsc_data = status_get_sc_data(target);
+
+ if (atk_elem < 0 || atk_elem > 9)
+ atk_elem = rand()%9; //?器属?ォランダムで付加
+
+ if (def_type < 0 || def_type > 9 ||
+ def_lv < 1 || def_lv > 4) { // 属 ?ォ値がおかしいのでとりあえずそのまま返す
+ if (battle_config.error_log)
+ ShowError("battle_attr_fix: unknown attr type: atk=%d def_type=%d def_lv=%d\n",atk_elem,def_type,def_lv);
+ //TODO: Remove this debug case once the cause is resolved. [Skotlex]
+ if (src) switch (src->type) {
+ case BL_MOB:
+ ShowDebug("src: Mob %s-%d\n", ((struct mob_data*)src)->name, ((struct mob_data*)src)->class_);
+ break;
+ case BL_PC:
+ ShowDebug("src: Player %s-%d\n", ((struct map_session_data*)src)->status.name, ((struct map_session_data*)src)->bl.id);
+ break;
+ case BL_PET:
+ ShowDebug("src: Pet %s-%d\n", ((struct pet_data*)src)->name, ((struct pet_data*)src)->bl.id);
+ break;
+ case BL_SKILL:
+ ShowDebug("src: Ground Skill id: %d\n", ((struct skill_unit*)src)->group->skill_id);
+ break;
+ default:
+ ShowDebug("unknown source type %d.\n", src->type);
+ }
+ if (target) switch (target->type) {
+ case BL_MOB:
+ ShowDebug("target: Mob %s-%d\n", ((struct mob_data*)target)->name, ((struct mob_data*)target)->class_);
+ break;
+ case BL_PC:
+ ShowDebug("target: Player %s-%d\n", ((struct map_session_data*)target)->status.name, ((struct map_session_data*)target)->bl.id);
+ break;
+ case BL_PET:
+ ShowDebug("target: Pet %s-%d\n", ((struct pet_data*)target)->name, ((struct pet_data*)target)->bl.id);
+ break;
+ case BL_SKILL:
+ ShowDebug("target: Ground Skill id: %d\n", ((struct skill_unit*)target)->group->skill_id);
+ break;
+ default:
+ ShowDebug("unknown target type %d.\n", target->type);
+ }
+ return damage;
+ }
+
+ ratio = attr_fix_table[def_lv-1][atk_elem][def_type];
+ if (sc_data)
+ {
+ if(sc_data[SC_VOLCANO].timer!=-1 && atk_elem == 3)
+ ratio += enchant_eff[sc_data[SC_VOLCANO].val1-1];
+ if(sc_data[SC_VIOLENTGALE].timer!=-1 && atk_elem == 4)
+ ratio += enchant_eff[sc_data[SC_VIOLENTGALE].val1-1];
+ if(sc_data[SC_DELUGE].timer!=-1 && atk_elem == 1)
+ ratio += enchant_eff[sc_data[SC_DELUGE].val1-1];
+ }
+ if (tsc_data)
+ {
+ if(tsc_data[SC_ARMOR_ELEMENT].timer!=-1)
+ {
+ if (tsc_data[SC_ARMOR_ELEMENT].val1 == atk_elem)
+ ratio -= tsc_data[SC_ARMOR_ELEMENT].val2;
+ else
+ if (tsc_data[SC_ARMOR_ELEMENT].val3 == atk_elem)
+ ratio -= tsc_data[SC_ARMOR_ELEMENT].val4;
+ }
+ }
+ return damage*ratio/100;
+}
+
+static int battle_walkdelay_sub(int tid, unsigned int tick, int id, int data)
+{
+ struct block_list *bl = map_id2bl(id);
+ if (!bl || status_isdead(bl))
+ return 0;
+
+ switch (bl->type) {
+ case BL_PC:
+ {
+ struct map_session_data *sd = (struct map_session_data*)bl;
+ if (sd->walktimer != -1)
+ pc_stop_walking (sd,3);
+ if (sd->canmove_tick < tick)
+ sd->canmove_tick = tick + data;
+ }
+ break;
+ case BL_MOB:
+ {
+ struct mob_data *md = (struct mob_data*)bl;
+ if (md->state.state == MS_WALK)
+ mob_stop_walking(md,3);
+ if (md->canmove_tick < tick)
+ md->canmove_tick = tick + data;
+ }
+ break;
+ }
+ return 0;
+}
+/*==========================================
+ * Applies walk delay based on attack type. [Skotlex]
+ *------------------------------------------
+ */
+int battle_walkdelay(struct block_list *bl, unsigned int tick, int adelay, int delay, int div_) {
+
+ if (status_isdead(bl))
+ return 0;
+
+ if (bl->type == BL_PC) {
+ if (battle_config.pc_walk_delay_rate != 100)
+ delay = delay*battle_config.pc_walk_delay_rate/100;
+ } else
+ if (battle_config.walk_delay_rate != 100)
+ delay = delay*battle_config.walk_delay_rate/100;
+
+ if (div_ > 1) //Multi-hit skills mean higher delays.
+ delay += battle_config.multihit_delay*(div_-1);
+
+ if (delay <= 0)
+ return 0;
+
+ //See if it makes sense to set this trigger.
+ switch (bl->type) {
+ case BL_PC:
+ {
+ struct map_session_data *sd = (struct map_session_data*)bl;
+ if (DIFF_TICK(sd->canmove_tick, tick+adelay) > 0)
+ return 0;
+ if (!adelay) //No need of timer.
+ sd->canmove_tick = tick + delay;
+ break;
+ }
+ case BL_MOB:
+ {
+ struct mob_data *md = (struct mob_data*)bl;
+ if (DIFF_TICK(md->canmove_tick, tick+adelay) > 0)
+ return 0;
+ if (!adelay) //No need of timer.
+ md->canmove_tick = tick + delay;
+ break;
+ }
+ default:
+ return 0;
+ }
+ if (adelay > 0)
+ add_timer(tick+adelay, battle_walkdelay_sub, bl->id, delay);
+ return 1;
+}
+
+/*==========================================
+ * ダ??[ジ?ナ?I計算
+ *------------------------------------------
+ */
+int battle_calc_damage(struct block_list *src,struct block_list *bl,int damage,int div_,int skill_num,int skill_lv,int flag)
+{
+ struct map_session_data *sd = NULL;
+ struct mob_data *md = NULL;
+ struct status_change *sc_data, *sc;
+ short *sc_count;
+ int class_;
+
+ nullpo_retr(0, bl);
+
+ if (damage <= 0)
+ return 0;
+
+ class_ = status_get_class(bl);
+
+ if (bl->type == BL_MOB) {
+ md=(struct mob_data *)bl;
+ } else if (bl->type == BL_PC) {
+ sd=(struct map_session_data *)bl;
+ }
+
+ sc_data = status_get_sc_data(bl);
+ sc_count = status_get_sc_count(bl);
+
+ if(flag&BF_LONG && map_getcell(bl->m, bl->x, bl->y, CELL_CHKPNEUMA) &&
+ ((flag&BF_WEAPON && skill_num != NPC_GUIDEDATTACK) ||
+ (flag&BF_MISC && skill_num != PA_PRESSURE) ||
+ (flag&BF_MAGIC && skill_num == ASC_BREAKER))){ // It should block only physical part of Breaker! [Lupus], on the contrary, players all over the boards say it completely blocks Breaker x.x' [Skotlex]
+ return 0;
+ }
+
+ if (sc_count && *sc_count > 0) {
+ //First, sc_*'s that reduce damage to 0.
+ if (sc_data[SC_SAFETYWALL].timer!=-1 && flag&BF_SHORT && (skill_num != NPC_GUIDEDATTACK && skill_num != AM_DEMONSTRATION)
+ ) {
+ // セ?[フティウォ?[ル
+ struct skill_unit_group *group = (struct skill_unit_group *)sc_data[SC_SAFETYWALL].val3;
+ if (group) {
+ if (--group->val2<=0)
+ skill_delunitgroup(group);
+ return 0;
+ } else {
+ status_change_end(bl,SC_SAFETYWALL,-1);
+ }
+ }
+
+ if(sc_data[SC_LANDPROTECTOR].timer!=-1 && flag&BF_MAGIC)
+ return 0;
+
+ /* Moved to battle_calc_weapon_attack for now.
+ if(sc_data[SC_KAUPE].timer != -1 && damage > 0 && !skill_num) {
+ if(rand()%100 < sc_data[SC_KAUPE].val2) {
+ clif_skill_nodamage(bl,bl,SL_KAUPE,sc_data[SC_KAUPE].val1,1);
+ if (--sc_data[SC_KAUPE].val3 <= 0) //We make it work like Safety Wall, even though it only blocks 1 time.
+ status_change_end(bl, SC_KAUPE, -1);
+ return 0;
+ }
+ }
+ */
+
+ if(sc_data[SC_AUTOGUARD].timer != -1 && flag&BF_WEAPON &&
+ rand()%100 < sc_data[SC_AUTOGUARD].val2) {
+ int delay;
+ clif_skill_nodamage(bl,bl,CR_AUTOGUARD,sc_data[SC_AUTOGUARD].val1,1);
+ // different delay depending on skill level [celest]
+ if (sc_data[SC_AUTOGUARD].val1 <= 5)
+ delay = 300;
+ else if (sc_data[SC_AUTOGUARD].val1 > 5 && sc_data[SC_AUTOGUARD].val1 <= 9)
+ delay = 200;
+ else
+ delay = 100;
+ if(sd)
+ sd->canmove_tick = gettick() + delay;
+ else if(md)
+ md->canmove_tick = gettick() + delay;
+
+ if(sc_data[SC_SHRINK].timer != -1 && rand()%100<5*sc_data[SC_AUTOGUARD].val1)
+ skill_blown(bl,src,skill_get_blewcount(CR_SHRINK,1));
+ return 0;
+ }
+
+// -- moonsoul (chance to block attacks with new Lord Knight skill parrying)
+//
+ if(sc_data[SC_PARRYING].timer != -1 && flag&BF_WEAPON &&
+ rand()%100 < sc_data[SC_PARRYING].val2) {
+ clif_skill_nodamage(bl,bl,LK_PARRYING,sc_data[SC_PARRYING].val1,1);
+ return 0;
+ }
+
+ if(sc_data[SC_DODGE].timer != -1 && (flag&BF_LONG || (sc_data[SC_SPURT].timer != -1 && flag&BF_WEAPON))
+ && rand()%100 < 20) {
+ clif_skill_nodamage(bl,bl,TK_DODGE,1,1);
+ if (sc_data[SC_COMBO].timer == -1)
+ status_change_start(bl, SC_COMBO, TK_JUMPKICK, src->id, 0, 0, 2000, 0);
+ return 0;
+ }
+
+ if(sc_data[SC_FOGWALL].timer != -1 && flag&BF_MAGIC
+ && rand()%100 < 75 && !(skill_get_inf(skill_num)&INF_GROUND_SKILL))
+ return 0;
+
+ //Now damage increasing effects
+ if(sc_data[SC_AETERNA].timer!=-1 && skill_num != PA_PRESSURE){
+ damage<<=1;
+ if (skill_num != ASC_BREAKER || flag & BF_MAGIC) //Only end it on the second attack of breaker. [Skotlex]
+ status_change_end( bl,SC_AETERNA,-1 );
+ }
+
+ if(sc_data[SC_SPIDERWEB].timer!=-1) // [Celest]
+ if ((flag&BF_SKILL && skill_get_pl(skill_num)==3) ||
+ (!flag&BF_SKILL && status_get_attack_element(src)==3)) {
+ damage<<=1;
+ status_change_end(bl, SC_SPIDERWEB, -1);
+ }
+
+ //Finally damage reductions....
+ if(sc_data[SC_ASSUMPTIO].timer != -1){
+ if(map_flag_vs(bl->m))
+ damage=damage*2/3; //Receive 66% damage
+ else
+ damage>>=1; //Receive 50% damage
+ }
+
+ if(sc_data[SC_DEFENDER].timer != -1 && flag&BF_LONG && flag&BF_WEAPON)
+ damage=damage*(100-sc_data[SC_DEFENDER].val2)/100;
+
+ if(sc_data[SC_FOGWALL].timer != -1 && flag&BF_LONG && flag&BF_WEAPON)
+ damage >>=1;
+
+ if(sc_data[SC_ENERGYCOAT].timer!=-1 && flag&BF_WEAPON){
+ if(sd){
+ if(sd->status.sp>0){
+ int per = sd->status.sp * 5 / (sd->status.max_sp + 1);
+ sd->status.sp -= sd->status.sp * (per * 5 + 10) / 1000;
+ if( sd->status.sp < 0 ) sd->status.sp = 0;
+ damage -= damage * ((per+1) * 6) / 100;
+ clif_updatestatus(sd,SP_SP);
+ }
+ if(sd->status.sp<=0)
+ status_change_end( bl,SC_ENERGYCOAT,-1 );
+ }
+ else
+ damage -= damage * (sc_data[SC_ENERGYCOAT].val1 * 6) / 100;
+ }
+
+ if(sc_data[SC_REJECTSWORD].timer!=-1 && flag&BF_WEAPON &&
+ // Fixed the condition check [Aalye]
+ (src->type==BL_MOB || (src->type==BL_PC && (((struct map_session_data *)src)->status.weapon == 1 ||
+ ((struct map_session_data *)src)->status.weapon == 2 ||
+ ((struct map_session_data *)src)->status.weapon == 3)))){
+ if(rand()%100 < (15*sc_data[SC_REJECTSWORD].val1)){
+ damage = damage*50/100;
+ clif_damage(bl,src,gettick(),0,0,damage,0,0,0);
+ battle_damage(bl,src,damage,0);
+ clif_skill_nodamage(bl,bl,ST_REJECTSWORD,sc_data[SC_REJECTSWORD].val1,1);
+ if((--sc_data[SC_REJECTSWORD].val2)<=0)
+ status_change_end(bl, SC_REJECTSWORD, -1);
+ }
+ }
+
+ //Finally Kyrie because it may, or not, reduce damage to 0.
+ if(sc_data[SC_KYRIE].timer!=-1){
+ sc=&sc_data[SC_KYRIE];
+ sc->val2-=damage;
+ if(flag&BF_WEAPON || skill_num == TF_THROWSTONE){
+ if(sc->val2>=0)
+ damage=0;
+ else
+ damage=-sc->val2;
+ }
+ if((--sc->val3)<=0 || (sc->val2<=0) || skill_num == AL_HOLYLIGHT)
+ status_change_end(bl, SC_KYRIE, -1);
+ }
+ if (damage <= 0) return 0;
+ }
+
+ //SC effects from caster side.
+ sc_data = status_get_sc_data(src);
+ sc_count = status_get_sc_count(src);
+ if (sc_count && *sc_count > 0) {
+ if(sc_data[SC_FOGWALL].timer != -1 && flag&(BF_LONG|BF_MAGIC)) {
+ if (flag&BF_MAGIC) {
+ if(!(skill_get_inf(skill_num)&INF_GROUND_SKILL) && rand()%100 < 75)
+ return 0;
+ } else
+ damage /=2;
+ }
+ }
+
+ if(md && md->guardian_data) {
+ if(class_ == MOBID_EMPERIUM && (flag&BF_SKILL && //Only a few skills can hit the Emperium.
+ skill_num != PA_PRESSURE && skill_num != MO_TRIPLEATTACK && skill_num != HW_GRAVITATION))
+ return 0;
+ if(src->type == BL_PC) {
+ struct guild *g=guild_search(((struct map_session_data *)src)->status.guild_id);
+ if (g && class_ == MOBID_EMPERIUM && guild_checkskill(g,GD_APPROVAL) <= 0)
+ return 0;
+ if (g && battle_config.guild_max_castles && guild_checkcastles(g)>=battle_config.guild_max_castles)
+ return 0; // [MouseJstr]
+ }
+ }
+
+ if (skill_num != PA_PRESSURE) { // Gloria Domini ignores WoE damage reductions
+ if (map_flag_gvg(bl->m)) { //GvG
+ if (md && md->guardian_data)
+ damage -= damage * (md->guardian_data->castle->defense/100) * (battle_config.castle_defense_rate/100);
+
+ if (flag & BF_SKILL) { //Skills get a different reduction than non-skills. [Skotlex]
+ if (flag&BF_WEAPON)
+ damage = damage * battle_config.gvg_weapon_damage_rate/100;
+ if (flag&BF_MAGIC)
+ damage = damage * battle_config.gvg_magic_damage_rate/100;
+ if (flag&BF_MISC)
+ damage = damage * battle_config.gvg_misc_damage_rate/100;
+ } else { //Normal attacks get reductions based on range.
+ if (flag & BF_SHORT)
+ damage = damage * battle_config.gvg_short_damage_rate/100;
+ if (flag & BF_LONG)
+ damage = damage * battle_config.gvg_long_damage_rate/100;
+ }
+ } else if (battle_config.pk_mode && bl->type == BL_PC) {
+ if (flag & BF_WEAPON) {
+ if (flag & BF_SHORT)
+ damage = damage * 80/100;
+ if (flag & BF_LONG)
+ damage = damage * 70/100;
+ }
+ if (flag & BF_MAGIC)
+ damage = damage * 60/100;
+ if(flag & BF_MISC)
+ damage = damage * 60/100;
+ }
+ if(damage < 1) damage = 1;
+ }
+
+ if(battle_config.skill_min_damage && damage > 0 && damage < div_)
+ {
+ if ((flag&BF_WEAPON && battle_config.skill_min_damage&1)
+ || (flag&BF_MAGIC && battle_config.skill_min_damage&2)
+ || (flag&BF_MISC && battle_config.skill_min_damage&4)
+ )
+ damage = div_;
+ }
+
+ if( md!=NULL && md->hp>0 && damage > 0 ) // 反撃などのMOBスキル判定
+ mobskill_event(md,flag);
+
+ return damage;
+}
+
+/*==========================================
+ * HP/SP吸収の計算
+ *------------------------------------------
+ */
+int battle_calc_drain(int damage, int rate, int per, int val)
+{
+ int diff = 0;
+
+ if (damage <= 0)
+ return 0;
+
+ if (per && rand()%1000 < rate) {
+ diff = (damage * per) / 100;
+ if (diff == 0) {
+ if (per > 0)
+ diff = 1;
+ else
+ diff = -1;
+ }
+ }
+
+ if (val /*&& rand()%1000 < rate*/) { //Absolute leech/penalties have 100% chance. [Skotlex]
+ diff += val;
+ }
+ return diff;
+}
+
+/*==========================================
+ * ?C練ダ??[ジ
+ *------------------------------------------
+ */
+int battle_addmastery(struct map_session_data *sd,struct block_list *target,int dmg,int type)
+{
+ int damage,skill;
+ int race=status_get_race(target);
+ int weapon;
+ damage = dmg;
+
+ nullpo_retr(0, sd);
+
+ // デ?[モンベイン(+3 ?` +30) vs 不死 or 悪魔 (死?lは含めない?H)
+ if((skill = pc_checkskill(sd,AL_DEMONBANE)) > 0 && (battle_check_undead(race,status_get_elem_type(target)) || race==6) )
+ damage += (skill*(int)(3+(sd->status.base_level+1)*0.05)); // submitted by orn
+ //damage += (skill * 3);
+
+ // ビ?[ストベイン(+4 ?` +40) vs 動物 or ?ゥ虫
+ if((skill = pc_checkskill(sd,HT_BEASTBANE)) > 0 && (race==2 || race==4) ) {
+ damage += (skill * 4);
+ if (sd->sc_data[SC_SPIRIT].timer != -1 && sd->sc_data[SC_SPIRIT].val2 == SL_HUNTER)
+ damage += sd->status.str;
+ }
+
+ if(type == 0)
+ weapon = sd->weapontype1;
+ else
+ weapon = sd->weapontype2;
+ switch(weapon)
+ {
+ case 0x01: // Knife
+ case 0x02: // 1HS
+ {
+ // 剣?C練(+4 ?` +40) 片手剣 短剣含む
+ if((skill = pc_checkskill(sd,SM_SWORD)) > 0) {
+ damage += (skill * 4);
+ }
+ break;
+ }
+ case 0x03: // 2HS
+ {
+ // 両手剣?C練(+4 ?` +40) 両手剣
+ if((skill = pc_checkskill(sd,SM_TWOHAND)) > 0) {
+ damage += (skill * 4);
+ }
+ break;
+ }
+ case 0x04: // 1HL
+ case 0x05: // 2HL
+ {
+ // 槍?C練(+4 ?` +40,+5 ?` +50) 槍
+ if((skill = pc_checkskill(sd,KN_SPEARMASTERY)) > 0) {
+ if(!pc_isriding(sd))
+ damage += (skill * 4); // ペコに?謔チてない
+ else
+ damage += (skill * 5); // ペコに?謔チてる
+ }
+ break;
+ }
+ case 0x06: // 1H Axe
+ case 0x07: // 2H Axe by Tato
+ {
+ if((skill = pc_checkskill(sd,AM_AXEMASTERY)) > 0) {
+ damage += (skill * 3);
+ }
+ break;
+ }
+ case 0x08: // Maces
+ {
+ if((skill = pc_checkskill(sd,PR_MACEMASTERY)) > 0) {
+ damage += (skill * 3);
+ }
+ break;
+ }
+ case 0x09: // なし?
+ break;
+ case 0x0a: // Staffs
+ break;
+ case 0x0b: // Bows
+ break;
+ case 0x00: // Bare Hands
+ case 0x0c: // Knuckles
+ {
+ if((skill = pc_checkskill(sd,MO_IRONHAND)) > 0) {
+ damage += (skill * 3);
+ }
+ break;
+ }
+ case 0x0d: // Musical Instrument
+ {
+ // 楽器の練?K(+3 ?` +30) 楽器
+ if((skill = pc_checkskill(sd,BA_MUSICALLESSON)) > 0) {
+ damage += (skill * 3);
+ }
+ break;
+ }
+ case 0x0e: // Whips
+ {
+ // Dance Lesson Skill Effect(+3 damage for every lvl = +30) 鞭
+ if((skill = pc_checkskill(sd,DC_DANCINGLESSON)) > 0) {
+ damage += (skill * 3);
+ }
+ break;
+ }
+ case 0x0f: // Book
+ {
+ // Advance Book Skill Effect(+3 damage for every lvl = +30) {
+ if((skill = pc_checkskill(sd,SA_ADVANCEDBOOK)) > 0) {
+ damage += (skill * 3);
+ }
+ break;
+ }
+ case 0x10: // Katars
+ {
+ if((skill = pc_checkskill(sd,ASC_KATAR)) > 0) {
+ //Advanced Katar Research by zanetheinsane
+ damage += damage*(10 +skill * 2)/100;
+ }
+ // カタ?[ル?C練(+3 ?` +30) カタ?[ル
+ if((skill = pc_checkskill(sd,AS_KATAR)) > 0) {
+ //ソニックブ??[時は別???i1撃に付き1/8適応)
+ damage += (skill * 3);
+ }
+ break;
+ }
+ }
+ return (damage);
+}
+/*==========================================
+ * Calculates the standard damage of a normal attack assuming it hits,
+ * it calculates nothing extra fancy, is needed for magnum break's WATK_ELEMENT bonus. [Skotlex]
+ *------------------------------------------
+ * Pass damage2 as NULL to not calc it.
+ * Flag values:
+ * &1: Critical hit
+ * &2: Arrow attack
+ * &4: Skill is Magic Crasher
+ * &8: Skip target size adjustment (Extremity Fist?)
+ */
+static void battle_calc_base_damage(struct block_list *src, struct block_list *target, int* damage1, int* damage2, int flag)
+{
+ unsigned short baseatk=0, baseatk_=0, atkmin=0, atkmax=0, atkmin_=0, atkmax_=0;
+ struct map_session_data *sd;
+ struct status_change *sc_data = status_get_sc_data(src);
+ int t_size = status_get_size(target);
+
+ if (src->type == BL_PC)
+ sd = (struct map_session_data*)src;
+ else
+ sd = NULL;
+
+ if (!sd)
+ { //Mobs/Pets
+ if ((target->type==BL_MOB && battle_config.enemy_str) ||
+ (target->type==BL_PET && battle_config.pet_str))
+ baseatk = status_get_batk(src);
+
+ if(flag&4)
+ {
+ if (!(flag&1))
+ atkmin = status_get_matk2(src);
+ atkmax = status_get_matk1(src);
+ } else {
+ if (!(flag&1))
+ atkmin = status_get_atk(src);
+ atkmax = status_get_atk2(src);
+ }
+ if (atkmin > atkmax)
+ atkmin = atkmax;
+ } else { //PCs
+ if(flag&4)
+ {
+ baseatk = status_get_matk2(src);
+ if (damage2) baseatk_ = baseatk;
+ } else {
+ baseatk = status_get_batk(src);
+ if (damage2) baseatk_ = baseatk;
+ }
+ //rodatazone says that Overrefine bonuses are part of baseatk
+ if(sd->right_weapon.overrefine>0)
+ baseatk+= rand()%sd->right_weapon.overrefine+1;
+ if (damage2 && sd->left_weapon.overrefine>0)
+ baseatk_+= rand()%sd->left_weapon.overrefine+1;
+
+ atkmax = status_get_atk(src);
+ if (damage2)
+ atkmax_ = status_get_atk(src);
+
+ if (!(flag&1) || (flag&2))
+ { //Normal attacks
+ atkmin = atkmin_ = status_get_dex(src);
+
+ if (sd->equip_index[9] >= 0 && sd->inventory_data[sd->equip_index[9]])
+ atkmin = atkmin*(80 + sd->inventory_data[sd->equip_index[9]]->wlv*20)/100;
+
+ if (atkmin > atkmax)
+ atkmin = atkmax;
+
+ if(damage2)
+ {
+ if (sd->equip_index[8] >= 0 && sd->inventory_data[sd->equip_index[8]])
+ atkmin_ = atkmin_*(80 + sd->inventory_data[sd->equip_index[8]]->wlv*20)/100;
+
+ if (atkmin_ > atkmax_)
+ atkmin_ = atkmax_;
+ }
+
+ if(flag&2)
+ { //Bows
+ atkmin = atkmin*atkmax/100;
+ if (atkmin > atkmax)
+ atkmax = atkmin;
+ }
+ }
+ }
+
+ if (sc_data && sc_data[SC_MAXIMIZEPOWER].timer!=-1)
+ {
+ atkmin = atkmax;
+ atkmin_ = atkmax_;
+ }
+
+ //Weapon Damage calculation
+ if (!(flag&1))
+ {
+ (*damage1) += (atkmax>atkmin? rand()%(atkmax-atkmin):0)+atkmin;
+ if (damage2)
+ (*damage2) += (atkmax_>atkmin_? rand()%(atkmax_-atkmin_) :0) +atkmin_;
+ } else {
+ (*damage1) += atkmax;
+ if (damage2)
+ (*damage2) += atkmax_;
+ }
+
+ if (sd)
+ {
+ //rodatazone says the range is 0~arrow_atk-1 for non crit
+ if (flag&2 && sd->arrow_atk)
+ (*damage1) += ((flag&1)?sd->arrow_atk:rand()%sd->arrow_atk);
+
+ //SizeFix only for players
+ if (!(
+ sd->special_state.no_sizefix ||
+ (sc_data && sc_data[SC_WEAPONPERFECTION].timer!=-1) ||
+ (pc_isriding(sd) && (sd->status.weapon==4 || sd->status.weapon==5) && t_size==1) ||
+ (flag&8)
+ ))
+ {
+ (*damage1) = (*damage1)*(sd->right_weapon.atkmods[t_size])/100;
+ if (damage2)
+ (*damage2) = (*damage2)*(sd->left_weapon.atkmods[t_size])/100;
+ }
+ }
+
+ //Finally, add baseatk
+ (*damage1) += baseatk;
+ if (damage2)
+ (*damage2) += baseatk_;
+ return;
+}
+/*==========================================
+ * battle_calc_weapon_attack (by Skotlex)
+ *------------------------------------------
+ */
+static struct Damage battle_calc_weapon_attack(
+ struct block_list *src,struct block_list *target,int skill_num,int skill_lv,int wflag)
+{
+ struct map_session_data *sd=NULL, *tsd=NULL;
+ struct mob_data *md=NULL, *tmd=NULL;
+ struct pet_data *pd=NULL;//, *tpd=NULL; (Noone can target pets)
+ struct Damage wd;
+ short skill=0;
+ unsigned short skillratio = 100; //Skill dmg modifiers.
+
+ short i;
+ short t_mode = status_get_mode(target), t_size = status_get_size(target);
+ short t_race=0, t_ele=0, s_race=0; //Set to 0 because the compiler does not notices they are NOT gonna be used uninitialized
+ short s_ele, s_ele_;
+ short def1, def2;
+ struct status_change *sc_data = status_get_sc_data(src);
+ struct status_change *t_sc_data = status_get_sc_data(target);
+ struct {
+ unsigned hit : 1; //the attack Hit? (not a miss)
+ unsigned cri : 1; //Critical hit
+ unsigned idef : 1; //Ignore defense
+ unsigned idef2 : 1; //Ignore defense (left weapon)
+ unsigned infdef : 1; //Infinite defense (plants)
+ unsigned arrow : 1; //Attack is arrow-based
+ unsigned rh : 1; //Attack considers right hand (wd.damage)
+ unsigned lh : 1; //Attack considers left hand (wd.damage2)
+ unsigned weapon : 1; //It's a weapon attack (consider VVS, and all that)
+ unsigned cardfix : 1;
+ } flag;
+
+ memset(&wd,0,sizeof(wd));
+ memset(&flag,0,sizeof(flag));
+
+ if(src==NULL || target==NULL)
+ {
+ nullpo_info(NLP_MARK);
+ return wd;
+ }
+ //Initial flag
+ flag.rh=1;
+ flag.weapon=1;
+ flag.cardfix=1;
+ flag.infdef=(t_mode&MD_PLANT?1:0);
+
+ //Initial Values
+ wd.type=0; //Normal attack
+ wd.div_=skill_num?skill_get_num(skill_num,skill_lv):1;
+ wd.amotion=(skill_num && skill_get_inf(skill_num)&INF_GROUND_SKILL)?0:status_get_amotion(src); //Amotion should be 0 for ground skills.
+ if(skill_num == KN_AUTOCOUNTER)
+ wd.amotion >>= 1;
+ wd.dmotion=status_get_dmotion(target);
+ wd.blewcount=skill_get_blewcount(skill_num,skill_lv);
+ wd.flag=BF_SHORT|BF_WEAPON|BF_NORMAL; //Initial Flag
+ wd.dmg_lv=ATK_DEF; //This assumption simplifies the assignation later
+
+ switch (src->type)
+ {
+ case BL_PC:
+ sd=(struct map_session_data *)src;
+ break;
+ case BL_MOB:
+ md=(struct mob_data *)src;
+ break;
+ case BL_PET:
+ pd=(struct pet_data *)src;
+ break;
+ }
+ switch (target->type)
+ {
+ case BL_PC:
+ tsd=(struct map_session_data *)target;
+ if (pd) { //Pets can't target players
+ memset(&wd,0,sizeof(wd));
+ return wd;
+ }
+ break;
+ case BL_MOB:
+ tmd=(struct mob_data *)target;
+ break;
+ case BL_PET://Cannot target pets
+ memset(&wd,0,sizeof(wd));
+ return wd;
+ }
+
+ if(sd) {
+ sd->state.attack_type = BF_WEAPON;
+ if (sd->skillblown[0].id != 0)
+ { //Apply the bonus blewcount. [Skotlex]
+ for (i = 0; i < 5 && sd->skillblown[i].id != 0 && sd->skillblown[i].id != skill_num; i++);
+ if (i < 5 && sd->skillblown[i].id == skill_num)
+ wd.blewcount += sd->skillblown[i].val;
+ }
+ }
+ //Set miscellaneous data that needs be filled regardless of hit/miss
+ if(sd && sd->status.weapon == 11) {
+ wd.flag=(wd.flag&~BF_RANGEMASK)|BF_LONG;
+ flag.arrow = 1;
+ } else if (status_get_range(src) > 3)
+ wd.flag=(wd.flag&~BF_RANGEMASK)|BF_LONG;
+
+
+ if(skill_num){
+ wd.flag=(wd.flag&~BF_SKILLMASK)|BF_SKILL;
+ switch(skill_num)
+ {
+ case AC_DOUBLE:
+ case AC_SHOWER:
+ case AC_CHARGEARROW:
+ case BA_MUSICALSTRIKE:
+ case DC_THROWARROW:
+ case CG_ARROWVULCAN:
+ case AS_VENOMKNIFE:
+ case HT_POWER:
+ wd.flag=(wd.flag&~BF_RANGEMASK)|BF_LONG;
+ flag.arrow = 1;
+ break;
+
+ case HT_PHANTASMIC:
+ wd.flag=(wd.flag&~BF_RANGEMASK)|BF_LONG;
+ flag.arrow = 0;
+ break;
+
+ case MO_FINGEROFFENSIVE:
+ if(sd && battle_config.finger_offensive_type == 0)
+ wd.div_ = sd->spiritball_old;
+ wd.flag=(wd.flag&~BF_RANGEMASK)|BF_LONG;
+ break;
+
+ case CR_SHIELDBOOMERANG:
+ case PA_SHIELDCHAIN:
+ flag.weapon = 0;
+ case AS_GRIMTOOTH:
+ case KN_SPEARBOOMERANG:
+ case NPC_RANGEATTACK:
+ case LK_SPIRALPIERCE:
+ case ASC_BREAKER:
+ case AM_ACIDTERROR:
+ case ITM_TOMAHAWK: //Tomahawk is a ranged attack! [Skotlex]
+ case CR_GRANDCROSS:
+ case NPC_GRANDDARKNESS:
+ wd.flag=(wd.flag&~BF_RANGEMASK)|BF_LONG;
+ break;
+
+ case KN_PIERCE:
+ wd.div_= t_size+1;
+ break;
+
+ case TF_DOUBLE: //For NPC used skill.
+ wd.type = 0x08;
+ break;
+
+ case KN_SPEARSTAB:
+ case KN_BOWLINGBASH:
+ case MO_BALKYOUNG:
+ case TK_TURNKICK:
+ wd.blewcount=0;
+ break;
+
+ case CR_SHIELDCHARGE:
+ flag.weapon = 0;
+ case NPC_PIERCINGATT:
+ wd.flag=(wd.flag&~BF_RANGEMASK)|BF_SHORT;
+ break;
+
+ case KN_AUTOCOUNTER:
+ wd.flag=(wd.flag&~BF_SKILLMASK)|BF_NORMAL;
+ break;
+
+
+ }
+ }
+
+ if (skill_num && battle_config.skillrange_by_distance)
+ { //Skill range based on distance between src/target [Skotlex]
+ if ((sd && battle_config.skillrange_by_distance&1)
+ || (md && battle_config.skillrange_by_distance&2)
+ || (pd && battle_config.skillrange_by_distance&4)
+ ) {
+ if (check_distance_bl(src, target, 3))
+ wd.flag=(wd.flag&~BF_RANGEMASK)|BF_SHORT;
+ else
+ wd.flag=(wd.flag&~BF_RANGEMASK)|BF_LONG;
+ }
+ }
+
+ if(is_boss(target)) //Bosses can't be knocked-back
+ wd.blewcount = 0;
+
+ if (sd)
+ { //Arrow consumption
+ sd->state.arrow_atk = flag.arrow;
+ }
+
+ //Check for counter
+ if(!skill_num)
+ {
+ if(t_sc_data && t_sc_data[SC_AUTOCOUNTER].timer != -1)
+ //If it got here and you had autocounter active, then the direction/range does not matches: critical
+ flag.cri = 1;
+ } //End counter-check
+
+ if (!skill_num && (tsd || battle_config.enemy_perfect_flee))
+ { //Check for Lucky Dodge
+ short flee2 = status_get_flee2(target);
+ if (rand()%1000 < flee2)
+ {
+ wd.type=0x0b;
+ wd.dmg_lv=ATK_LUCKY;
+ return wd;
+ }
+ }
+
+ //Initialize variables that will be used afterwards
+ t_race = status_get_race(target);
+ t_ele = status_get_elem_type(target);
+
+ s_race = status_get_race(src);
+ s_ele = s_ele_ = skill_get_pl(skill_num);
+ if (!skill_num || s_ele == -1) { //Take weapon's element
+ s_ele = status_get_attack_element(src);
+ s_ele_ = status_get_attack_element2(src);
+ if (flag.arrow && sd && sd->arrow_ele)
+ s_ele = sd->arrow_ele;
+ } else if (s_ele == -2) { //Use enchantment's element
+ s_ele = s_ele_ = status_get_attack_sc_element(src);
+ }
+
+ if (sd)
+ { //Set whether damage1 or damage2 (or both) will be used
+ if(sd->weapontype1 == 0 && sd->weapontype2 > 0)
+ {
+ flag.rh=0;
+ flag.lh=1;
+ }
+ if(sd->status.weapon > 16)
+ flag.rh = flag.lh = 1;
+ }
+
+ //Check for critical
+ if(!flag.cri &&
+ (sd || battle_config.enemy_critical_rate) &&
+ (!skill_num || skill_num == KN_AUTOCOUNTER || skill_num == SN_SHARPSHOOTING))
+ {
+ short cri = status_get_critical(src);
+ if (sd)
+ {
+ cri+= sd->critaddrace[t_race];
+ if(flag.arrow)
+ cri += sd->arrow_cri;
+ if(sd->status.weapon == 16)
+ cri <<=1;
+ }
+ //The official equation is *2, but that only applies when sd's do critical.
+ //Therefore, we use the old value 3 on cases when an sd gets attacked by a mob
+ cri -= status_get_luk(target) * (md&&tsd?3:2);
+
+ if(t_sc_data)
+ {
+ if (t_sc_data[SC_SLEEP].timer!=-1 )
+ cri <<=1;
+ if(t_sc_data[SC_JOINTBEAT].timer != -1 &&
+ t_sc_data[SC_JOINTBEAT].val2 == 6) // Always take crits with Neck broken by Joint Beat [DracoRPG]
+ flag.cri=1;
+ }
+ switch (skill_num)
+ {
+ case KN_AUTOCOUNTER:
+ if(!(battle_config.pc_auto_counter_type&1))
+ flag.cri = 1;
+ else
+ cri <<= 1;
+ break;
+ case SN_SHARPSHOOTING:
+ cri += 200;
+ break;
+ }
+ if(tsd && tsd->critical_def)
+ cri = cri*(100-tsd->critical_def)/100;
+ if (rand()%1000 < cri)
+ flag.cri= 1;
+ }
+ if (flag.cri)
+ {
+ wd.type = 0x0a;
+ flag.idef = flag.idef2 = flag.hit = 1;
+ } else { //Check for Perfect Hit
+ if(sd && sd->perfect_hit > 0 && rand()%100 < sd->perfect_hit)
+ flag.hit = 1;
+ if (sc_data && sc_data[SC_FUSION].timer != -1) {
+ flag.hit = 1; //SG_FUSION always hit [Komurka]
+ flag.idef = flag.idef2 = 1; //def ignore [Komurka]
+ }
+ if (skill_num && !flag.hit)
+ switch(skill_num)
+ {
+ case NPC_GUIDEDATTACK:
+ case RG_BACKSTAP:
+ case AM_ACIDTERROR:
+ case MO_INVESTIGATE:
+ case MO_EXTREMITYFIST:
+ case CR_GRANDCROSS:
+ case NPC_GRANDDARKNESS:
+ case PA_SACRIFICE:
+ case TK_COUNTER:
+ case SG_SUN_WARM:
+ case SG_MOON_WARM:
+ case SG_STAR_WARM:
+ flag.hit = 1;
+ break;
+ case CR_SHIELDBOOMERANG:
+ if (sc_data && sc_data[SC_SPIRIT].timer != -1 && sc_data[SC_SPIRIT].val2 == SL_CRUSADER)
+ flag.hit = 1;
+ break;
+ }
+ if ((t_sc_data && !flag.hit) &&
+ (t_sc_data[SC_SLEEP].timer!=-1 ||
+ t_sc_data[SC_STAN].timer!=-1 ||
+ t_sc_data[SC_FREEZE].timer!=-1 ||
+ (t_sc_data[SC_STONE].timer!=-1 && t_sc_data[SC_STONE].val2==0))
+ )
+ flag.hit = 1;
+ }
+
+ if (!flag.hit)
+ { //Hit/Flee calculation
+ short
+ flee = status_get_flee(target),
+ hitrate=80; //Default hitrate
+
+ if(battle_config.agi_penalty_type)
+ {
+ unsigned char target_count; //256 max targets should be a sane max
+ target_count = 1+battle_counttargeted(target,src,battle_config.agi_penalty_count_lv);
+ if(target_count >= battle_config.agi_penalty_count)
+ {
+ if (battle_config.agi_penalty_type == 1)
+ flee = (flee * (100 - (target_count - (battle_config.agi_penalty_count - 1))*battle_config.agi_penalty_num))/100;
+ else //asume type 2: absolute reduction
+ flee -= (target_count - (battle_config.agi_penalty_count - 1))*battle_config.agi_penalty_num;
+ if(flee < 1) flee = 1;
+ }
+ }
+
+ hitrate+= status_get_hit(src) - flee;
+
+ if(sd && flag.arrow)
+ hitrate += sd->arrow_hit;
+ if(skill_num)
+ switch(skill_num)
+ { //Hit skill modifiers
+ case SM_BASH:
+ hitrate += 5*skill_lv;
+ break;
+ case SM_MAGNUM:
+ hitrate += 10*skill_lv;
+ break;
+ case KN_AUTOCOUNTER:
+ hitrate += 20;
+ break;
+ case KN_PIERCE:
+ hitrate += hitrate*(5*skill_lv)/100;
+ break;
+ case PA_SHIELDCHAIN:
+ hitrate += 20;
+ break;
+ case AS_SONICBLOW:
+ if(sd && pc_checkskill(sd,AS_SONICACCEL)>0)
+ hitrate += 50;
+ break;
+ }
+
+ // Weaponry Research hidden bonus
+ if (sd && (skill = pc_checkskill(sd,BS_WEAPONRESEARCH)) > 0)
+ hitrate += hitrate*(2*skill)/100;
+
+ if (hitrate > battle_config.max_hitrate)
+ hitrate = battle_config.max_hitrate;
+ else if (hitrate < battle_config.min_hitrate)
+ hitrate = battle_config.min_hitrate;
+
+ if(rand()%100 >= hitrate)
+ wd.dmg_lv = ATK_FLEE;
+ else if (t_sc_data && t_sc_data[SC_KAUPE].timer != -1 && rand()%100 < t_sc_data[SC_KAUPE].val2) {
+ if (--t_sc_data[SC_KAUPE].val3 <= 0) //We make it work like Safety Wall, even though it only blocks 1 time.
+ status_change_end(target, SC_KAUPE, -1);
+ wd.dmg_lv = ATK_FLEE;
+ } else
+ flag.hit =1;
+ } //End hit/miss calculation
+
+ if(tsd && tsd->special_state.no_weapon_damage)
+ return wd;
+
+ if (flag.hit && !flag.infdef) //No need to do the math for plants
+ { //Hitting attack
+
+//Assuming that 99% of the cases we will not need to check for the flag.rh... we don't.
+//ATK_RATE scales the damage. 100 = no change. 50 is halved, 200 is doubled, etc
+#define ATK_RATE( a ) { wd.damage= wd.damage*(a)/100 ; if(flag.lh) wd.damage2= wd.damage2*(a)/100; }
+#define ATK_RATE2( a , b ) { wd.damage= wd.damage*(a)/100 ; if(flag.lh) wd.damage2= wd.damage2*(b)/100; }
+//Adds dmg%. 100 = +100% (double) damage. 10 = +10% damage
+#define ATK_ADDRATE( a ) { wd.damage+= wd.damage*(a)/100 ; if(flag.lh) wd.damage2+= wd.damage2*(a)/100; }
+#define ATK_ADDRATE2( a , b ) { wd.damage+= wd.damage*(a)/100 ; if(flag.lh) wd.damage2+= wd.damage2*(b)/100; }
+//Adds an absolute value to damage. 100 = +100 damage
+#define ATK_ADD( a ) { wd.damage+= a; if (flag.lh) wd.damage2+= a; }
+#define ATK_ADD2( a , b ) { wd.damage+= a; if (flag.lh) wd.damage2+= b; }
+
+ def1 = status_get_def(target);
+ def2 = status_get_def2(target);
+
+ switch (skill_num)
+ { //Calc base damage according to skill
+ case PA_SACRIFICE:
+ {
+ int hp_dmg = status_get_max_hp(src)* 9/100;
+ battle_damage(src, src, hp_dmg, 0); //Damage to self is always 9%
+ clif_damage(src,src, gettick(), 0, 0, hp_dmg, 0 , 0, 0);
+
+ wd.damage = hp_dmg;
+ wd.damage2 = 0;
+
+ if (sc_data && sc_data[SC_SACRIFICE].timer != -1)
+ {
+ if (--sc_data[SC_SACRIFICE].val2 <= 0)
+ status_change_end(src, SC_SACRIFICE,-1);
+ }
+ break;
+ }
+ case LK_SPIRALPIERCE:
+ if (sd) {
+ short index = sd->equip_index[9];
+
+ if (index >= 0 &&
+ sd->inventory_data[index] &&
+ sd->inventory_data[index]->type == 4)
+ wd.damage = sd->inventory_data[index]->weight*8/100; //80% of weight
+
+ ATK_ADDRATE(50*skill_lv); //Skill modifier applies to weight only.
+ index = status_get_str(src)/10;
+ index = index*index;
+ ATK_ADD(index); //Add str bonus.
+
+ switch (t_size) { //Size-fix. Is this modified by weapon perfection?
+ case 0: //Small: 125%
+ ATK_RATE(125);
+ break;
+ //case 1: //Medium: 100%
+ case 2: //Large: 75%
+ ATK_RATE(75);
+ break;
+ }
+ ATK_RATE(wd.div_*100); //Increase overall damage by number of this
+ //FIXME: (shouldn't something like this apply to ALL weapon skills?) [Skotlex]
+ break;
+ }
+ case CR_SHIELDBOOMERANG:
+ case PA_SHIELDCHAIN:
+ if (sd) {
+ short index = sd->equip_index[8];
+
+ wd.damage = status_get_batk(src);
+ if (flag.lh) wd.damage2 = status_get_batk(src);
+
+ if (index >= 0 &&
+ sd->inventory_data[index] &&
+ sd->inventory_data[index]->type == 5)
+ ATK_ADD(sd->inventory_data[index]->weight/10);
+ break;
+ }
+ default:
+ {
+ battle_calc_base_damage(src, target, &wd.damage, flag.lh?&wd.damage2:NULL,
+ (flag.cri?1:0)|(flag.arrow?2:0)|(skill_num == HW_MAGICCRASHER?4:0)|(skill_num == MO_EXTREMITYFIST?8:0));
+ //Add any bonuses that modify the base baseatk+watk (pre-skills)
+ if(sd)
+ {
+ if (sd->status.weapon < 16 && (sd->atk_rate != 100 || sd->weapon_atk_rate[sd->status.weapon] != 0))
+ ATK_RATE(sd->atk_rate + sd->weapon_atk_rate[sd->status.weapon]);
+
+ if(flag.cri && sd->crit_atk_rate)
+ ATK_ADDRATE(sd->crit_atk_rate);
+
+ if(sd->status.party_id && (skill=pc_checkskill(sd,TK_POWER)) > 0){
+ i = 0;
+ party_foreachsamemap(party_sub_count, sd, 0, &i);
+ ATK_ADDRATE(2*skill*i);
+ }
+ }
+ break;
+ } //End default case
+ } //End switch(skill_num)
+
+ //Skill damage modifiers that stack linearly
+ if(sc_data && skill_num != PA_SACRIFICE)
+ {
+ if(sc_data[SC_OVERTHRUST].timer != -1)
+ skillratio += 5*sc_data[SC_OVERTHRUST].val1;
+ if(sc_data[SC_MAXOVERTHRUST].timer != -1)
+ skillratio += 20*sc_data[SC_MAXOVERTHRUST].val1;
+ if(sc_data[SC_BERSERK].timer != -1)
+ skillratio += 100;
+ }
+ if (!skill_num)
+ {
+ // Random chance to deal multiplied damage - Consider it as part of skill-based-damage
+ if(sd &&
+ sd->random_attack_increase_add > 0 &&
+ sd->random_attack_increase_per &&
+ rand()%100 < sd->random_attack_increase_per
+ )
+ skillratio += sd->random_attack_increase_add;
+
+ ATK_RATE(skillratio);
+ } else { //Skills
+ switch( skill_num )
+ {
+ case SM_BASH:
+ skillratio += 30*skill_lv;
+ break;
+ case SM_MAGNUM:
+ skillratio += 20*skill_lv;
+ break;
+ case MC_MAMMONITE:
+ skillratio += 50*skill_lv;
+ break;
+ case HT_POWER: //FIXME: How exactly is the STR based damage supposed to be done? [Skotlex]
+ skillratio += 10*status_get_str(src);
+ break;
+ case TF_DOUBLE: //This is the mob-used Double Attack. [Skotlex]
+ skillratio += 100;
+ break;
+ case AC_DOUBLE:
+ skillratio += 80+20*skill_lv;
+ break;
+ case AC_SHOWER:
+ skillratio += 5*skill_lv-25;
+ break;
+ case AC_CHARGEARROW:
+ skillratio += 50;
+ break;
+ case KN_PIERCE:
+ skillratio += wd.div_*(100+10*skill_lv)-100;
+ break;
+ case KN_SPEARSTAB:
+ skillratio += 15*skill_lv;
+ break;
+ case KN_SPEARBOOMERANG:
+ skillratio += 50*skill_lv;
+ break;
+ case KN_BRANDISHSPEAR:
+ {
+ int ratio = 100+20*skill_lv;
+ skillratio += ratio-100;
+ if(skill_lv>3 && wflag==1) skillratio += ratio/2;
+ if(skill_lv>6 && wflag==1) skillratio += ratio/4;
+ if(skill_lv>9 && wflag==1) skillratio += ratio/8;
+ if(skill_lv>6 && wflag==2) skillratio += ratio/2;
+ if(skill_lv>9 && wflag==2) skillratio += ratio/4;
+ if(skill_lv>9 && wflag==3) skillratio += ratio/2;
+ break;
+ }
+ case KN_BOWLINGBASH:
+ skillratio+= 40*skill_lv;
+ break;
+ case KN_AUTOCOUNTER:
+ case LK_SPIRALPIERCE:
+ case NPC_CRITICALSLASH:
+ flag.idef= flag.idef2= 1;
+ break;
+ case AS_GRIMTOOTH:
+ skillratio += 20*skill_lv;
+ break;
+ case AS_POISONREACT:
+ skillratio += 30*skill_lv;
+ break;
+ case AS_SONICBLOW:
+ skillratio += 200+50*skill_lv;
+ break;
+ case TF_SPRINKLESAND:
+ skillratio += 30;
+ break;
+ case MC_CARTREVOLUTION:
+ skillratio += 50;
+ if(sd && sd->cart_max_weight > 0 && sd->cart_weight > 0)
+ skillratio += 100*sd->cart_weight/sd->cart_max_weight; // +1% every 1% weight
+ else if (!sd)
+ skillratio += 150; //Max damage for non players.
+ break;
+ case NPC_COMBOATTACK:
+ skillratio += 100*wd.div_ -100;
+ break;
+ case NPC_RANDOMATTACK:
+ skillratio += rand()%150-50;
+ break;
+ case NPC_WATERATTACK:
+ case NPC_GROUNDATTACK:
+ case NPC_FIREATTACK:
+ case NPC_WINDATTACK:
+ case NPC_POISONATTACK:
+ case NPC_HOLYATTACK:
+ case NPC_DARKNESSATTACK:
+ case NPC_UNDEADATTACK:
+ case NPC_TELEKINESISATTACK:
+ skillratio += 25*skill_lv;
+ break;
+ case NPC_GUIDEDATTACK:
+ case NPC_RANGEATTACK:
+ case NPC_PIERCINGATT:
+ break;
+ case RG_BACKSTAP:
+ if(sd && sd->status.weapon == 11 && battle_config.backstab_bow_penalty)
+ skillratio += (200+40*skill_lv)/2;
+ else
+ skillratio += 200+40*skill_lv;
+ break;
+ case RG_RAID:
+ skillratio += 40*skill_lv;
+ break;
+ case RG_INTIMIDATE:
+ skillratio += 30*skill_lv;
+ break;
+ case CR_SHIELDCHARGE:
+ skillratio += 20*skill_lv;
+ break;
+ case CR_SHIELDBOOMERANG:
+ skillratio += 30*skill_lv;
+ if (sc_data && sc_data[SC_SPIRIT].timer != -1 && sc_data[SC_SPIRIT].val2 == SL_CRUSADER)
+ skillratio += 100;
+ break;
+ case NPC_DARKCROSS:
+ case CR_HOLYCROSS:
+ skillratio += 35*skill_lv;
+ break;
+ case CR_GRANDCROSS:
+ case NPC_GRANDDARKNESS:
+ flag.cardfix = 0;
+ break;
+ case AM_DEMONSTRATION:
+ skillratio += 20*skill_lv;
+ flag.cardfix = 0;
+ break;
+ case AM_ACIDTERROR:
+ skillratio += 40*skill_lv;
+ flag.cardfix = 0;
+ break;
+ case MO_FINGEROFFENSIVE:
+ if(battle_config.finger_offensive_type == 0)
+ skillratio+= wd.div_ * (100 + 50*skill_lv) -100;
+ else
+ skillratio+= 50 * skill_lv;
+ break;
+ case MO_INVESTIGATE:
+ skillratio += 75*skill_lv;
+ ATK_RATE(2*(def1 + def2));
+ flag.idef= flag.idef2= 1;
+ break;
+ case MO_EXTREMITYFIST:
+ if (sd)
+ { //Overflow check. [Skotlex]
+ unsigned int ratio = skillratio + 100*(8 + ((sd->status.sp)/10));
+ //You'd need something like 6K SP to reach this max, so should be fine for most purposes.
+ if (ratio > 60000) ratio = 60000; //We leave some room here in case skillratio gets further increased.
+ skillratio = (unsigned short)ratio;
+ sd->status.sp = 0;
+ clif_updatestatus(sd,SP_SP);
+ }
+ flag.idef= flag.idef2= 1;
+ break;
+ case MO_TRIPLEATTACK:
+ skillratio += 20*skill_lv;
+ break;
+ case MO_CHAINCOMBO:
+ skillratio += 50+50*skill_lv;
+ break;
+ case MO_COMBOFINISH:
+ skillratio += 140+60*skill_lv;
+ break;
+ case BA_MUSICALSTRIKE:
+ skillratio += 40*skill_lv-40;
+ break;
+ case DC_THROWARROW:
+ skillratio += 50*skill_lv;
+ break;
+ case CH_TIGERFIST:
+ skillratio += 100*skill_lv-60;
+ break;
+ case CH_CHAINCRUSH:
+ skillratio += 300+100*skill_lv;
+ break;
+ case CH_PALMSTRIKE:
+ skillratio += 100+100*skill_lv;
+ break;
+ case LK_HEADCRUSH:
+ skillratio += 40*skill_lv;
+ break;
+ case LK_JOINTBEAT:
+ skillratio += 10*skill_lv-50;
+ break;
+ case ASC_METEORASSAULT:
+ skillratio += 40*skill_lv-60;
+ flag.cardfix = 0;
+ break;
+ case SN_SHARPSHOOTING:
+ skillratio += 50*skill_lv;
+ break;
+ case CG_ARROWVULCAN:
+ skillratio += 100+100*skill_lv;
+ break;
+ case AS_SPLASHER:
+ skillratio += 100+20*skill_lv;
+ if (sd)
+ skillratio += 20*pc_checkskill(sd,AS_POISONREACT);
+ if(wflag>1) //FIXME: Splash damage... is this the correct method? [Skotlex]
+ skillratio /= wflag;
+ flag.cardfix = 0;
+ break;
+ case ASC_BREAKER:
+ skillratio += 100*skill_lv-100;
+ flag.cardfix = 0;
+ break;
+ case PA_SACRIFICE:
+ //40% less effective on siege maps. [Skotlex]
+ skillratio += 10*skill_lv-10;
+ flag.idef = flag.idef2 = 1;
+ break;
+ case PA_SHIELDCHAIN:
+ skillratio += wd.div_*(100+30*skill_lv)-100;
+ break;
+ case WS_CARTTERMINATION:
+ if(sd && sd->cart_weight > 0)
+ skillratio += sd->cart_weight / (10 * (16 - skill_lv)) - 100;
+ else if (!sd)
+ skillratio += battle_config.max_cart_weight / (10 * (16 - skill_lv));
+ flag.cardfix = 0;
+ break;
+ case TK_DOWNKICK:
+ skillratio += 60 + 20*skill_lv;
+ break;
+ case TK_STORMKICK:
+ skillratio += 60 + 20*skill_lv;
+ break;
+ case TK_TURNKICK:
+ skillratio += 90 + 30*skill_lv;
+ break;
+ case TK_COUNTER:
+ skillratio += 90 + 30*skill_lv;
+ break;
+ case TK_JUMPKICK:
+ skillratio += -70 + 10*skill_lv;
+ if (sc_data && sc_data[SC_COMBO].timer != -1 && sc_data[SC_COMBO].val1 == skill_num)
+ skillratio += 10*status_get_lv(src)/3;
+ break;
+ case KN_CHARGEATK:
+ skillratio += wflag*15; //FIXME: How much is the actual bonus? [Skotlex]
+ break;
+ case HT_PHANTASMIC:
+ skillratio += 50;
+ break;
+ case MO_BALKYOUNG:
+ skillratio += 200;
+ break;
+ }
+
+ ATK_RATE(skillratio);
+
+ //Constant/misc additions from skills
+ switch (skill_num) {
+ case MO_EXTREMITYFIST:
+ ATK_ADD(250 + 150*skill_lv);
+ break;
+ case TK_DOWNKICK:
+ case TK_STORMKICK:
+ case TK_TURNKICK:
+ case TK_COUNTER:
+ case TK_JUMPKICK:
+ //TK_RUN kick damage bonus.
+ if(sd && sd->weapontype1 == 0 && sd->weapontype2 == 0)
+ ATK_ADD(10*pc_checkskill(sd, TK_RUN));
+ break;
+ }
+ }
+ //Here comes a second pass for skills that stack to the previously defined % damage. [Skotlex]
+ skillratio = 100;
+ //Skill damage modifiers that affect linearly stacked damage.
+ if (sc_data && skill_num != PA_SACRIFICE) {
+ if(sc_data[SC_TRUESIGHT].timer != -1)
+ skillratio += 2*sc_data[SC_TRUESIGHT].val1;
+ // It is still not quite decided whether it works on bosses or not...
+ if(sc_data[SC_EDP].timer != -1 /*&& !(t_mode&MD_BOSS)*/ && skill_num != ASC_BREAKER && skill_num != ASC_METEORASSAULT)
+ skillratio += 50 +50*sc_data[SC_EDP].val1;
+ }
+ switch (skill_num) {
+ case AS_SONICBLOW:
+ if (sc_data && sc_data[SC_SPIRIT].timer != -1 && sc_data[SC_SPIRIT].val2 == SL_ASSASIN)
+ skillratio += (map_flag_gvg(src->m))?25:100; //+25% dmg on woe/+100% dmg on nonwoe
+ if(sd && pc_checkskill(sd,AS_SONICACCEL)>0)
+ skillratio += 10;
+ break;
+ }
+ if (sd && sd->skillatk[0].id != 0)
+ {
+ for (i = 0; i < MAX_PC_BONUS && sd->skillatk[i].id != 0 && sd->skillatk[i].id != skill_num; i++);
+ if (i < MAX_PC_BONUS && sd->skillatk[i].id == skill_num)
+ //May seem wrong as it also applies on top of other modifiers, but adding, say, 10%
+ //to 800% dmg -> 810% would make the bonus a little lame. [Skotlex]
+ skillratio += sd->skillatk[i].val;
+ }
+ if (skillratio != 100)
+ ATK_RATE(skillratio);
+ if(sd)
+ {
+ if (skill_num != PA_SACRIFICE && skill_num != MO_INVESTIGATE && !flag.cri)
+ { //Elemental/Racial adjustments
+ char raceele_flag=0, raceele_flag_=0;
+ if(sd->right_weapon.def_ratio_atk_ele & (1<<t_ele) ||
+ sd->right_weapon.def_ratio_atk_race & (1<<t_race) ||
+ sd->right_weapon.def_ratio_atk_race & (is_boss(target)?1<<10:1<<11)
+ )
+ raceele_flag = flag.idef = 1;
+
+ if(sd->left_weapon.def_ratio_atk_ele & (1<<t_ele) ||
+ sd->left_weapon.def_ratio_atk_race & (1<<t_race) ||
+ sd->left_weapon.def_ratio_atk_race & (is_boss(target)?1<<10:1<<11)
+ ) { //Pass effect onto right hand if configured so. [Skotlex]
+ if (battle_config.left_cardfix_to_right && flag.rh)
+ raceele_flag = flag.idef = 1;
+ else
+ raceele_flag_ = flag.idef2 = 1;
+ }
+
+ if (raceele_flag || raceele_flag_)
+ ATK_RATE2(raceele_flag?(def1 + def2):100, raceele_flag_?(def1 + def2):100);
+ }
+
+ //Ignore Defense?
+ if (!flag.idef && (
+ (tmd && sd->right_weapon.ignore_def_mob & (is_boss(target)?2:1)) ||
+ sd->right_weapon.ignore_def_ele & (1<<t_ele) ||
+ sd->right_weapon.ignore_def_race & (1<<t_race) ||
+ sd->right_weapon.ignore_def_race & (is_boss(target)?1<<10:1<<11)
+ ))
+ flag.idef = 1;
+
+ if (!flag.idef2 && (
+ (tmd && sd->left_weapon.ignore_def_mob & (is_boss(target)?2:1)) ||
+ sd->left_weapon.ignore_def_ele & (1<<t_ele) ||
+ sd->left_weapon.ignore_def_race & (1<<t_race) ||
+ sd->left_weapon.ignore_def_race & (is_boss(target)?1<<10:1<<11)
+ )) {
+ if(battle_config.left_cardfix_to_right && flag.rh) //Move effect to right hand. [Skotlex]
+ flag.idef = 1;
+ else
+ flag.idef2 = 1;
+ }
+ }
+
+ if (!flag.idef || !flag.idef2)
+ { //Defense reduction
+ short vit_def;
+ if(battle_config.vit_penalty_type)
+ {
+ unsigned char target_count; //256 max targets should be a sane max
+ target_count = 1 + battle_counttargeted(target,src,battle_config.vit_penalty_count_lv);
+ if(target_count >= battle_config.vit_penalty_count) {
+ if(battle_config.vit_penalty_type == 1) {
+ def1 = (def1 * (100 - (target_count - (battle_config.vit_penalty_count - 1))*battle_config.vit_penalty_num))/100;
+ def2 = (def2 * (100 - (target_count - (battle_config.vit_penalty_count - 1))*battle_config.vit_penalty_num))/100;
+ } else { //Assume type 2
+ def1 -= (target_count - (battle_config.vit_penalty_count - 1))*battle_config.vit_penalty_num;
+ def2 -= (target_count - (battle_config.vit_penalty_count - 1))*battle_config.vit_penalty_num;
+ }
+ }
+ if(def1 < 0 || skill_num == AM_ACIDTERROR) def1 = 0; //Acid Terror ignores only armor defense. [Skotlex]
+ if(def2 < 1) def2 = 1;
+ }
+ //Vitality reduction from rodatazone: http://rodatazone.simgaming.net/mechanics/substats.php#def
+ if (tsd) //Sd vit-eq
+ { //[VIT*0.5] + rnd([VIT*0.3], max([VIT*0.3],[VIT^2/150]-1))
+ vit_def = def2*(def2-15)/150;
+ vit_def = def2/2 + (vit_def>0?rand()%vit_def:0);
+
+ if((battle_check_undead(s_race,status_get_elem_type(src)) || s_race==6) &&
+ (skill=pc_checkskill(tsd,AL_DP)) >0)
+ vit_def += skill*(int)(3 +(tsd->status.base_level+1)*0.04); // submitted by orn
+ } else { //Mob-Pet vit-eq
+ //VIT + rnd(0,[VIT/20]^2-1)
+ vit_def = (def2/20)*(def2/20);
+ vit_def = def2 + (vit_def>0?rand()%vit_def:0);
+ }
+
+ if ((sd && battle_config.player_defense_type)
+ || (md && battle_config.monster_defense_type)
+ || (pd && battle_config.pet_defense_type)
+ )
+ vit_def += def1*battle_config.player_defense_type;
+ else
+ ATK_RATE2(flag.idef?100:100-def1, flag.idef2?100:100-def1);
+ ATK_ADD2(flag.idef?0:-vit_def, flag.idef2?0:-vit_def);
+ }
+
+ //Post skill/vit reduction damage increases
+ if (sc_data && skill_num != LK_SPIRALPIERCE)
+ { //SC skill damages
+ if(sc_data[SC_AURABLADE].timer!=-1)
+ ATK_ADD(20*sc_data[SC_AURABLADE].val1);
+ }
+
+ //Refine bonus
+ if (sd && flag.weapon && skill_num != MO_INVESTIGATE && skill_num != MO_EXTREMITYFIST) {
+ if (skill_num == MO_FINGEROFFENSIVE) //Counts refine bonus multiple times
+ {
+ ATK_ADD2(wd.div_*status_get_atk2(src), wd.div_*status_get_atk_2(src));
+ } else {
+ ATK_ADD2(status_get_atk2(src), status_get_atk_2(src));
+ }
+ }
+
+ //Set to min of 1
+ if (flag.rh && wd.damage < 1) wd.damage = 1;
+ if (flag.lh && wd.damage2 < 1) wd.damage2 = 1;
+
+ if (sd && flag.weapon && skill_num != MO_INVESTIGATE && skill_num != MO_EXTREMITYFIST
+ && skill_num != CR_GRANDCROSS)
+ { //Add mastery damage
+ wd.damage = battle_addmastery(sd,target,wd.damage,0);
+ if (flag.lh) wd.damage2 = battle_addmastery(sd,target,wd.damage2,1);
+
+ if (pc_checkskill(sd,SG_SUN_ANGER) || pc_checkskill(sd,SG_MOON_ANGER) || pc_checkskill(sd,SG_STAR_ANGER))
+ { //SG Anger bonus - ATK_ADDRATE [Komurka]
+ static int type[] = { SG_SUN_ANGER, SG_MOON_ANGER, SG_STAR_ANGER };
+ short t_class = status_get_class(target);
+ for (i = 0; i < 2; i++)
+ {
+ if (t_class == sd->hate_mob[i] && (skill = pc_checkskill(sd,type[i])))
+ {
+ skillratio = (sd->status.base_level + (i==2?status_get_str(src):0) + status_get_dex(src)+ status_get_luk(src))/(skill<4?12-3*skill:1);
+ ATK_ADDRATE(skillratio);
+ break;
+ }
+ }
+ }
+ }
+ } //Here ends flag.hit section, the rest of the function applies to both hitting and missing attacks
+
+ if(skill_num == CR_GRANDCROSS || skill_num == NPC_GRANDDARKNESS)
+ return wd; //Enough, rest is not needed.
+
+ if(sd && (skill=pc_checkskill(sd,BS_WEAPONRESEARCH)) > 0)
+ ATK_ADD(skill*2);
+
+ if(skill_num==TF_POISON)
+ ATK_ADD(15*skill_lv);
+
+ if ((sd && (skill_num || !battle_config.pc_attack_attr_none)) ||
+ (md && (skill_num || !battle_config.mob_attack_attr_none)) ||
+ (pd && (skill_num || !battle_config.pet_attack_attr_none)))
+ { //Elemental attribute fix
+ short t_element = status_get_element(target);
+ if (!(!sd && tsd && battle_config.mob_ghostring_fix && t_ele==8))
+ {
+ if (wd.damage > 0)
+ {
+ wd.damage=battle_attr_fix(src,target,wd.damage,s_ele,t_element);
+ if(skill_num==MC_CARTREVOLUTION) //Cart Revolution applies the element fix once more with neutral element
+ wd.damage = battle_attr_fix(src,target,wd.damage,0,t_element);
+ }
+ if (flag.lh && wd.damage2 > 0)
+ wd.damage2 = battle_attr_fix(src,target,wd.damage2,s_ele_,t_element);
+ }
+ if(sc_data && sc_data[SC_WATK_ELEMENT].timer != -1)
+ { //Descriptions indicate this means adding a percent of a normal attack in another element. [Skotlex]
+ int damage=0;
+ battle_calc_base_damage(src, target, &damage, NULL, (flag.arrow?2:0));
+ damage = damage*sc_data[SC_WATK_ELEMENT].val2/100;
+ damage = battle_attr_fix(src,target,damage,sc_data[SC_WATK_ELEMENT].val1,t_element);
+ ATK_ADD(damage);
+ }
+ }
+
+
+ if ((!flag.rh || wd.damage == 0) && (!flag.lh || wd.damage2 == 0))
+ flag.cardfix = 0; //When the attack does no damage, avoid doing %bonuses
+
+ if (sd)
+ {
+ if (skill_num != CR_SHIELDBOOMERANG) //Only Shield boomerang doesn't takes the Star Crumbs bonus.
+ ATK_ADD2(wd.div_*sd->right_weapon.star, wd.div_*sd->left_weapon.star);
+ if (skill_num==MO_FINGEROFFENSIVE) { //The finger offensive spheres on moment of attack do count. [Skotlex]
+ ATK_ADD(wd.div_*sd->spiritball_old*3);
+ } else {
+ ATK_ADD(wd.div_*sd->spiritball*3);
+ }
+
+ //Card Fix, sd side
+ if (flag.cardfix)
+ {
+ short cardfix = 1000, cardfix_ = 1000;
+ short t_class = status_get_class(target);
+ short t_race2 = status_get_race2(target);
+ if(sd->state.arrow_atk)
+ {
+ cardfix=cardfix*(100+sd->right_weapon.addrace[t_race]+sd->arrow_addrace[t_race])/100;
+ cardfix=cardfix*(100+sd->right_weapon.addele[t_ele]+sd->arrow_addele[t_ele])/100;
+ cardfix=cardfix*(100+sd->right_weapon.addsize[t_size]+sd->arrow_addsize[t_size])/100;
+ cardfix=cardfix*(100+sd->right_weapon.addrace2[t_race2])/100;
+ cardfix=cardfix*(100+sd->right_weapon.addrace[is_boss(target)?10:11]+sd->arrow_addrace[is_boss(target)?10:11])/100;
+ } else { //Melee attack
+ if(!battle_config.left_cardfix_to_right)
+ {
+ cardfix=cardfix*(100+sd->right_weapon.addrace[t_race])/100;
+ cardfix=cardfix*(100+sd->right_weapon.addele[t_ele])/100;
+ cardfix=cardfix*(100+sd->right_weapon.addsize[t_size])/100;
+ cardfix=cardfix*(100+sd->right_weapon.addrace2[t_race2])/100;
+ cardfix=cardfix*(100+sd->right_weapon.addrace[is_boss(target)?10:11])/100;
+
+ if (flag.lh)
+ {
+ cardfix_=cardfix_*(100+sd->left_weapon.addrace[t_race])/100;
+ cardfix_=cardfix_*(100+sd->left_weapon.addele[t_ele])/100;
+ cardfix_=cardfix_*(100+sd->left_weapon.addsize[t_size])/100;
+ cardfix_=cardfix_*(100+sd->left_weapon.addrace2[t_race2])/100;
+ cardfix_=cardfix_*(100+sd->left_weapon.addrace[is_boss(target)?10:11])/100;
+ }
+ } else {
+ cardfix=cardfix*(100+sd->right_weapon.addrace[t_race]+sd->left_weapon.addrace[t_race])/100;
+ cardfix=cardfix*(100+sd->right_weapon.addele[t_ele]+sd->left_weapon.addele[t_ele])/100;
+ cardfix=cardfix*(100+sd->right_weapon.addsize[t_size]+sd->left_weapon.addsize[t_size])/100;
+ cardfix=cardfix*(100+sd->right_weapon.addrace2[t_race2]+sd->left_weapon.addrace2[t_race2])/100;
+ cardfix=cardfix*(100+sd->right_weapon.addrace[is_boss(target)?10:11]+sd->left_weapon.addrace[is_boss(target)?10:11])/100;
+ }
+ }
+
+ for(i=0;i<sd->right_weapon.add_damage_class_count;i++) {
+ if(sd->right_weapon.add_damage_classid[i] == t_class) {
+ cardfix=cardfix*(100+sd->right_weapon.add_damage_classrate[i])/100;
+ break;
+ }
+ }
+
+ if (flag.lh)
+ {
+ for(i=0;i<sd->left_weapon.add_damage_class_count;i++) {
+ if(sd->left_weapon.add_damage_classid[i] == t_class) {
+ cardfix_=cardfix_*(100+sd->left_weapon.add_damage_classrate[i])/100;
+ break;
+ }
+ }
+ }
+
+ if(wd.flag&BF_LONG)
+ cardfix=cardfix*(100+sd->long_attack_atk_rate)/100;
+
+ if (cardfix != 1000 || cardfix_ != 1000)
+ ATK_RATE2(cardfix/10, cardfix_/10); //What happens if you use right-to-left and there's no right weapon, only left?
+ }
+
+ if (skill_num == CR_SHIELDBOOMERANG || skill_num == PA_SHIELDCHAIN) { //Refine bonus applies after cards and elements.
+ short index= sd->equip_index[8];
+ if (index >= 0 &&
+ sd->inventory_data[index] &&
+ sd->inventory_data[index]->type == 5)
+ ATK_ADD(10*sd->status.inventory[index].refine);
+ }
+ } //if (sd)
+
+ //Card Fix, tsd side - Cards always apply on the target. [Skotlex]
+ if (tsd) {
+ short s_size,s_race2,s_class;
+ short cardfix=1000;
+
+ s_size = status_get_size(src);
+ s_race2 = status_get_race2(src);
+ s_class = status_get_class(src);
+
+ cardfix=cardfix*(100-tsd->subele[s_ele])/100;
+ cardfix=cardfix*(100-tsd->subsize[s_size])/100;
+ cardfix=cardfix*(100-tsd->subrace2[s_race2])/100;
+ cardfix=cardfix*(100-tsd->subrace[s_race])/100;
+ cardfix=cardfix*(100-tsd->subrace[is_boss(src)?10:11])/100;
+
+ for(i=0;i<tsd->add_dmg_count;i++) {
+ if(tsd->add_dmg[i].class_ == s_class) {
+ cardfix=cardfix*(100+tsd->add_dmg[i].rate)/100;
+ break;
+ }
+ }
+
+ if(wd.flag&BF_SHORT)
+ cardfix=cardfix*(100-tsd->near_attack_def_rate)/100;
+ else // BF_LONG (there's no other choice)
+ cardfix=cardfix*(100-tsd->long_attack_def_rate)/100;
+
+ if (cardfix != 1000)
+ ATK_RATE(cardfix/10);
+ }
+
+ if(flag.infdef)
+ { //Plants receive 1 damage when hit
+ if (flag.rh && (flag.hit || wd.damage>0))
+ wd.damage = 1;
+ if (flag.lh && (flag.hit || wd.damage2>0))
+ wd.damage2 = 1;
+ if (!(battle_config.skill_min_damage&1)) //Do not return if you are supposed to deal greater damage to plants than 1. [Skotlex]
+ return wd;
+ }
+
+ if(sd && !skill_num && !flag.cri)
+ { //Check for double attack.
+ if(( (skill_lv = 5*pc_checkskill(sd,TF_DOUBLE)) > 0 && sd->weapontype1 == 0x01) ||
+ sd->double_rate > 0) //Success chance is not added, the higher one is used? [Skotlex]
+ if (rand()%100 < (skill_lv>sd->double_rate?skill_lv:sd->double_rate))
+ {
+ wd.damage *=2;
+ wd.div_=skill_get_num(TF_DOUBLE,skill_lv?skill_lv:1);
+ wd.type = 0x08;
+ }
+ }
+
+ if(!flag.rh || wd.damage<1)
+ wd.damage=0;
+
+ if(!flag.lh || wd.damage2<1)
+ wd.damage2=0;
+
+ if (sd)
+ {
+ if (!flag.rh && flag.lh)
+ { //Move lh damage to the rh
+ wd.damage = wd.damage2;
+ wd.damage2 = 0;
+ flag.rh=1;
+ flag.lh=0;
+ } else if(sd->status.weapon > 16)
+ { //Dual-wield
+ if (wd.damage > 0)
+ {
+ skill = pc_checkskill(sd,AS_RIGHT);
+ wd.damage = wd.damage * (50 + (skill * 10))/100;
+ if(wd.damage < 1) wd.damage = 1;
+ }
+ if (wd.damage2 > 0)
+ {
+ skill = pc_checkskill(sd,AS_LEFT);
+ wd.damage2 = wd.damage2 * (30 + (skill * 10))/100;
+ if(wd.damage2 < 1) wd.damage2 = 1;
+ }
+ } else if(sd->status.weapon == 16)
+ { //Katars
+ skill = pc_checkskill(sd,TF_DOUBLE);
+ wd.damage2 = wd.damage * (1 + (skill * 2))/100;
+
+ if(wd.damage > 0 && wd.damage2 < 1) wd.damage2 = 1;
+ flag.lh = 1;
+ }
+ }
+
+ if(wd.damage > 0 || wd.damage2 > 0)
+ {
+ if(wd.damage2<1)
+ wd.damage=battle_calc_damage(src,target,wd.damage,wd.div_,skill_num,skill_lv,wd.flag);
+ else if(wd.damage<1)
+ wd.damage2=battle_calc_damage(src,target,wd.damage2,wd.div_,skill_num,skill_lv,wd.flag);
+ else
+ {
+ int d1=wd.damage+wd.damage2,d2=wd.damage2;
+ wd.damage=battle_calc_damage(src,target,d1,wd.div_,skill_num,skill_lv,wd.flag);
+ wd.damage2=(d2*100/d1)*wd.damage/100;
+ if(wd.damage > 1 && wd.damage2 < 1) wd.damage2=1;
+ wd.damage-=wd.damage2;
+ }
+ }
+
+ if(sd && sd->classchange && tmd && !(t_mode&MD_BOSS) && !tmd->guardian_data && (tmd->class_ < 1324 || tmd->class_ > 1363) && (rand()%10000 < sd->classchange))
+ { //Classchange:
+ struct mob_db *mob;
+ int k, class_;
+ i = 0;
+ do {
+ do {
+ class_ = rand() % MAX_MOB_DB;
+ } while (!mobdb_checkid(class_));
+
+ k = rand() % 1000000;
+ mob = mob_db(class_);
+ } while ((mob->mode&(MD_BOSS|MD_PLANT) || mob->summonper[0] <= k) && (i++) < 2000);
+ if (i< 2000)
+ mob_class_change(((struct mob_data *)target),class_);
+ }
+
+ if (sd && (battle_config.equip_self_break_rate || battle_config.equip_skill_break_rate) &&
+ (wd.damage > 0 || wd.damage2 > 0)) {
+ if (battle_config.equip_self_break_rate) { // Self weapon breaking
+ int breakrate = battle_config.equip_natural_break_rate;
+ if (sd->sc_count) {
+ if(sd->sc_data[SC_OVERTHRUST].timer!=-1)
+ breakrate += 10;
+ if(sd->sc_data[SC_MAXOVERTHRUST].timer!=-1)
+ breakrate += 10;
+ }
+ if(rand() % 10000 < breakrate * battle_config.equip_self_break_rate / 100 || breakrate >= 10000)
+ pc_breakweapon(sd);
+ }
+ if (battle_config.equip_skill_break_rate) { // Target equipment breaking
+ int breakrate[2] = {0,0}; // weapon = 0, armor = 1
+ int breaktime = 5000;
+
+ breakrate[0] += sd->break_weapon_rate; // Break rate from equipment
+ breakrate[1] += sd->break_armor_rate;
+ if (sd->sc_count) {
+ if (sd->sc_data[SC_MELTDOWN].timer!=-1) {
+ breakrate[0] += 100*sd->sc_data[SC_MELTDOWN].val1;
+ breakrate[1] += 70*sd->sc_data[SC_MELTDOWN].val1;
+ breaktime = skill_get_time2(WS_MELTDOWN,1);
+ }
+ }
+ if(rand() % 10000 < breakrate[0] * battle_config.equip_skill_break_rate / 100 || breakrate[0] >= 10000) {
+ if (target->type == BL_PC)
+ pc_breakweapon((struct map_session_data *)target);
+ else
+ status_change_start(target,SC_STRIPWEAPON,1,75,0,0,breaktime,0);
+ }
+ if(rand() % 10000 < breakrate[1] * battle_config.equip_skill_break_rate/100 || breakrate[1] >= 10000) {
+ if (target->type == BL_PC) {
+ struct map_session_data *tsd = (struct map_session_data *)target;
+ pc_breakarmor(tsd);
+ } else
+ status_change_start(target,SC_STRIPSHIELD,1,75,0,0,breaktime,0);
+ }
+ }
+ }
+ return wd;
+}
+
+/*==========================================
+ * battle_calc_magic_attack [DracoRPG]
+ *------------------------------------------
+ */
+struct Damage battle_calc_magic_attack(
+ struct block_list *src,struct block_list *target,int skill_num,int skill_lv,int mflag)
+ {
+ struct map_session_data *sd=NULL, *tsd=NULL;
+ struct mob_data *md=NULL, *tmd=NULL;
+ struct pet_data *pd=NULL;//, *tpd=NULL; (Noone can target pets)
+ struct Damage ad;
+ unsigned short skillratio = 100; //Skill dmg modifiers.
+
+ short i;
+ short t_mode = status_get_mode(target);
+ short t_race, t_size, t_ele, s_race, s_size, s_ele;
+ struct {
+ unsigned imdef : 1;
+ unsigned infdef : 1;
+ unsigned elefix : 1;
+ unsigned cardfix : 1;
+ } flag;
+
+ memset(&ad,0,sizeof(ad));
+ memset(&flag,0,sizeof(flag));
+
+ if(src==NULL || target==NULL)
+ {
+ nullpo_info(NLP_MARK);
+ return ad;
+ }
+ //Initial flag
+ flag.elefix=1;
+ flag.cardfix=1;
+
+ //Initial Values
+ ad.damage = 1;
+ ad.div_=skill_get_num(skill_num,skill_lv);
+ ad.amotion=skill_get_inf(skill_num)&INF_GROUND_SKILL?0:status_get_amotion(src); //Amotion should be 0 for ground skills.
+ ad.dmotion=status_get_dmotion(target);
+ ad.blewcount = skill_get_blewcount(skill_num,skill_lv);
+ ad.flag=BF_MAGIC|BF_LONG|BF_SKILL;
+ ad.dmg_lv=ATK_DEF;
+
+ switch (src->type)
+ {
+ case BL_PC:
+ sd=(struct map_session_data *)src;
+ break;
+ case BL_MOB:
+ md=(struct mob_data *)src;
+ break;
+ case BL_PET:
+ pd=(struct pet_data *)src;
+ break;
+ }
+ switch (target->type)
+ {
+ case BL_PC:
+ tsd=(struct map_session_data *)target;
+ if (pd) { //Pets can't target players
+ memset(&ad,0,sizeof(ad));
+ return ad;
+ }
+ break;
+ case BL_MOB:
+ tmd=(struct mob_data *)target;
+ break;
+ case BL_PET://Cannot target pets
+ memset(&ad,0,sizeof(ad));
+ return ad;
+ }
+
+ //Initialize variables that will be used afterwards
+ t_race = status_get_race(target);
+ t_size = status_get_size(target);
+ t_ele = status_get_elem_type(target);
+
+ s_race = status_get_race(src);
+ s_size = status_get_size(src);
+ s_ele = skill_get_pl(skill_num);
+
+ if (s_ele == -1) // pl=-1 : the skill takes the weapon's element
+ s_ele = status_get_attack_element(src);
+ else if (s_ele == -2) //Use status element
+ s_ele = status_get_attack_sc_element(src);
+
+ if (skill_num == ASC_BREAKER) // Soul Breaker's magical part is neutral, although pl=-1 for the physical part to take weapon element
+ s_ele = 0;
+
+ //Set miscellaneous data that needs be filled
+ if(sd) {
+ sd->state.attack_type = BF_MAGIC;
+ sd->state.arrow_atk = 0;
+ if (sd->skillblown[0].id != 0)
+ { //Apply the bonus blewcount. [Skotlex]
+ for (i = 0; i < MAX_PC_BONUS && sd->skillblown[i].id != 0 && sd->skillblown[i].id != skill_num; i++);
+ if (i < MAX_PC_BONUS && sd->skillblown[i].id == skill_num)
+ ad.blewcount += sd->skillblown[i].val;
+ }
+ }
+
+ if (battle_config.skillrange_by_distance)
+ { //Skill range based on distance between src/target [Skotlex]
+ if ((sd && battle_config.skillrange_by_distance&1)
+ || (md && battle_config.skillrange_by_distance&2)
+ || (pd && battle_config.skillrange_by_distance&4)
+ ) {
+ if (check_distance_bl(src, target, 3))
+ ad.flag=(ad.flag&~BF_RANGEMASK)|BF_SHORT;
+ else
+ ad.flag=(ad.flag&~BF_RANGEMASK)|BF_LONG;
+ }
+ }
+
+ flag.infdef=(t_mode&MD_PLANT?1:0);
+
+ switch(skill_num)
+ {
+ case MG_FIREWALL:
+ if(mflag) { //mflag has a value when it was checked it works against an undead in skill.c [Skotlex]
+ ad.div_ = mflag; //mflag contains the number of hits against undead.
+ ad.blewcount = 0; //No knockback
+ ad.dmotion = 0; //No flinch animation.
+ } else
+ ad.blewcount |= 0x10000;
+ break;
+ case PR_SANCTUARY:
+ ad.blewcount|=0x10000;
+ case AL_HEAL:
+ case WZ_FIREPILLAR:
+ flag.imdef = 1;
+ break;
+ case PR_ASPERSIO:
+ case PF_SOULBURN:
+ case HW_GRAVITATION:
+ case ASC_BREAKER:
+ flag.imdef = 1;
+ flag.elefix = 0;
+ flag.cardfix = 0;
+ break;
+ case PR_TURNUNDEAD:
+ flag.imdef = 1;
+ flag.cardfix = 0;
+ break;
+ case NPC_GRANDDARKNESS:
+ case CR_GRANDCROSS:
+ flag.cardfix = 0;
+ break;
+ }
+
+ if(is_boss(target)) //Bosses can't be knocked-back
+ ad.blewcount = 0;
+
+ if (!flag.infdef) //No need to do the math for plants
+ {
+
+//MATK_RATE scales the damage. 100 = no change. 50 is halved, 200 is doubled, etc
+#define MATK_RATE( a ) { ad.damage= ad.damage*(a)/100; }
+//Adds dmg%. 100 = +100% (double) damage. 10 = +10% damage
+#define MATK_ADDRATE( a ) { ad.damage+= ad.damage*(a)/100; }
+//Adds an absolute value to damage. 100 = +100 damage
+#define MATK_ADD( a ) { ad.damage+= a; }
+
+ switch (skill_num)
+ { //Calc base damage according to skill
+ case AL_HEAL:
+ case PR_BENEDICTIO:
+ ad.damage = skill_calc_heal(src,skill_lv)/2;
+ if (sd)
+ ad.damage += ad.damage * pc_checkskill(sd, HP_MEDITATIO) * 2 / 100;
+ break;
+ case PR_ASPERSIO:
+ ad.damage = 40;
+ break;
+ case PR_SANCTUARY:
+ ad.damage = (skill_lv>6)?388:skill_lv*50;
+ break;
+ case ALL_RESURRECTION:
+ case PR_TURNUNDEAD:
+ if(!tsd && battle_check_undead(t_race,t_ele)){
+ int hp, mhp, thres;
+ hp = status_get_hp(target);
+ mhp = status_get_max_hp(target);
+ thres = (skill_lv * 20) + status_get_luk(src) + status_get_int(src) + status_get_lv(src) + ((200 - hp * 200 / mhp));
+ if(thres > 700) thres = 700;
+ if(rand()%1000 < thres && !(t_mode&MD_BOSS))
+ ad.damage = hp;
+ else
+ ad.damage = status_get_lv(src) + status_get_int(src) + skill_lv * 10;
+ }
+ break;
+ case PF_SOULBURN:
+ if (!tsd) {
+ memset(&ad,0,sizeof(ad));
+ return ad;
+ } else
+ ad.damage = tsd->status.sp * 2;
+ break;
+ case ASC_BREAKER:
+ ad.damage = rand()%500 + 500 + skill_lv * status_get_int(src) * 5;
+ break;
+ case HW_GRAVITATION:
+ ad.damage = 200+200*skill_lv;
+ break;
+ default:
+ {
+ unsigned short matkmin,matkmax;
+
+ matkmin = status_get_matk2(src);
+ matkmax = status_get_matk1(src);
+
+ MATK_ADD(matkmin+(matkmax>matkmin?rand()%(matkmax-matkmin+1):0));
+
+ if(skill_num == MG_NAPALMBEAT || skill_num == HW_NAPALMVULCAN){ // Divide MATK in case of multiple targets skill
+ if(mflag>0)
+ ad.damage/= mflag;
+ else if(battle_config.error_log)
+ ShowError("0 enemies targeted by Napalm Beat/Vulcan, divide per 0 avoided!\n");
+ }
+
+ switch(skill_num){
+ case MG_NAPALMBEAT:
+ skillratio += skill_lv*10-30;
+ break;
+ case MG_SOULSTRIKE:
+ if (battle_check_undead(t_race,t_ele))
+ skillratio += 5*skill_lv;
+ break;
+ case MG_FIREBALL:
+ if(mflag>2)
+ ad.damage = 0;
+ else {
+ int drate[]={100,90,70};
+ MATK_RATE(drate[mflag]);
+ skillratio += 70+10*skill_lv;
+ }
+ break;
+ case MG_FIREWALL:
+ skillratio -= 50;
+ break;
+ case MG_THUNDERSTORM:
+ skillratio -= 20;
+ break;
+ case MG_FROSTDIVER:
+ skillratio += 10*skill_lv;
+ break;
+ case AL_HOLYLIGHT:
+ skillratio += 25;
+ if (sd && sd->sc_data[SC_SPIRIT].timer != -1 && sd->sc_data[SC_SPIRIT].val2 == SL_PRIEST)
+ skillratio *= 5; //Does 5x damage include bonuses from other skills?
+ break;
+ case AL_RUWACH:
+ skillratio += 45;
+ break;
+ case WZ_FROSTNOVA:
+ skillratio += (100+skill_lv*10)*2/3-100;
+ break;
+ case WZ_FIREPILLAR:
+ skillratio -= 80;
+ break;
+ case WZ_SIGHTRASHER:
+ skillratio += 20*skill_lv;
+ break;
+ case WZ_VERMILION:
+ skillratio += 20*skill_lv-20;
+ break;
+ case WZ_WATERBALL:
+ skillratio += 30*skill_lv;
+ break;
+ case WZ_STORMGUST:
+ skillratio += 40*skill_lv;
+ break;
+ case HW_NAPALMVULCAN:
+ skillratio += 10*skill_lv-30;
+ break;
+ case SL_STIN:
+ skillratio += (t_size?-99:10*skill_lv); //target size must be small (0) for full damage.
+ break;
+ case SL_STUN:
+ skillratio += (t_size!=2?5*skill_lv:-99); //Full damage is dealt on small/medium targets
+ break;
+ case SL_SMA:
+ skillratio += -60 + status_get_lv(src); //Base damage is 40% + lv%
+ break;
+ }
+
+ if (sd && sd->skillatk[0].id != 0)
+ {
+ for (i = 0; i < MAX_PC_BONUS && sd->skillatk[i].id != 0 && sd->skillatk[i].id != skill_num; i++)
+ if (i < MAX_PC_BONUS && sd->skillatk[i].id == skill_num)
+ //If we apply skillatk[] as ATK_RATE, it will also affect other skills,
+ //unfortunately this way ignores a skill's constant modifiers...
+ skillratio += sd->skillatk[i].val;
+ }
+
+ MATK_RATE(skillratio);
+
+ //Constant/misc additions from skills
+ if (skill_num == WZ_FIREPILLAR)
+ MATK_ADD(50);
+ }
+ }
+
+ if(sd) {
+ //Ignore Defense?
+ if (!flag.imdef && (
+ sd->ignore_mdef_ele & (1<<t_ele) ||
+ sd->ignore_mdef_race & (1<<t_race) ||
+ sd->ignore_mdef_race & (is_boss(target)?1<<10:1<<11)
+ ))
+ flag.imdef = 1;
+ }
+
+ if(!flag.imdef){
+ if(battle_config.magic_defense_type)
+ ad.damage = ad.damage - (status_get_mdef(target)*battle_config.magic_defense_type) - status_get_mdef2(target);
+ else
+ ad.damage = ad.damage * (100-status_get_mdef(target))/100 - status_get_mdef2(target);
+ }
+
+ if(skill_num == CR_GRANDCROSS || skill_num == NPC_GRANDDARKNESS)
+ { //Apply the physical part of the skill's damage. [Skotlex]
+ struct Damage wd = battle_calc_weapon_attack(src,target,skill_num,skill_lv,mflag);
+ ad.damage = (wd.damage + ad.damage) * (100 + 40*skill_lv)/100;
+ if(src==target)
+ {
+ if (src->type == BL_PC)
+ ad.damage = ad.damage/2;
+ else
+ ad.damage = 0;
+ }
+ }
+
+ if(ad.damage<1)
+ ad.damage=1;
+
+ if (flag.elefix)
+ ad.damage=battle_attr_fix(src, target, ad.damage, s_ele, status_get_element(target));
+
+ if (sd && flag.cardfix) {
+ short t_class = status_get_class(target);
+ short cardfix=100;
+
+ cardfix=cardfix*(100+sd->magic_addrace[t_race])/100;
+ cardfix=cardfix*(100+sd->magic_addele[t_ele])/100;
+ cardfix=cardfix*(100+sd->magic_addsize[t_size])/100;
+ cardfix=cardfix*(100+sd->magic_addrace[is_boss(target)?10:11])/100;
+ for(i=0;i<sd->add_mdmg_count;i++) {
+ if(sd->add_mdmg[i].class_ == t_class) {
+ cardfix=cardfix*(100+sd->add_mdmg[i].rate)/100;
+ continue;
+ }
+ }
+
+ MATK_RATE(cardfix);
+ }
+
+ if (tsd && skill_num != HW_GRAVITATION) { //Card fixes always apply on the target side. [Skotlex]
+ short s_race2=status_get_race2(src);
+ short s_class= status_get_class(src);
+ short cardfix=100;
+
+ cardfix=cardfix*(100-tsd->subele[s_ele])/100;
+ cardfix=cardfix*(100-tsd->subsize[s_size])/100;
+ cardfix=cardfix*(100-tsd->subrace2[s_race2])/100;
+ cardfix=cardfix*(100-tsd->subrace[s_race])/100;
+ cardfix=cardfix*(100-tsd->subrace[is_boss(src)?10:11])/100;
+ for(i=0;i<tsd->add_mdef_count;i++) {
+ if(tsd->add_mdef[i].class_ == s_class) {
+ cardfix=cardfix*(100-tsd->add_mdef[i].rate)/100;
+ continue;
+ }
+ }
+ cardfix=cardfix*(100-tsd->magic_def_rate)/100;
+
+ MATK_RATE(cardfix);
+ }
+ }
+
+ if(!flag.infdef && ad.div_>1 && skill_num != WZ_VERMILION)
+ ad.damage *= ad.div_;
+
+ if (tsd && status_isimmune(target)) {
+ if (sd && battle_config.gtb_pvp_only) { // [MouseJstr]
+ MATK_RATE(100 - battle_config.gtb_pvp_only);
+ } else ad.damage = 0;
+ }
+
+ ad.damage=battle_calc_damage(src,target,ad.damage,ad.div_,skill_num,skill_lv,ad.flag);
+ if (ad.damage == 0) //Magic attack blocked? Consider it a miss so it doesn't invokes additional effects. [Skotlex]
+ ad.dmg_lv = ATK_FLEE;
+ return ad;
+}
+
+/*==========================================
+ * その他ダ??[ジ計算
+ *------------------------------------------
+ */
+struct Damage battle_calc_misc_attack(
+ struct block_list *bl,struct block_list *target,int skill_num,int skill_lv,int flag)
+{
+ int int_=status_get_int(bl);
+ int dex=status_get_dex(bl);
+ int skill,ele,race,size,cardfix,race2,t_mode;
+ struct map_session_data *sd=NULL,*tsd=NULL;
+ int damage=0,div_=1,blewcount=skill_get_blewcount(skill_num,skill_lv);
+ struct Damage md;
+ int damagefix=1;
+
+ int aflag=BF_MISC|BF_SHORT|BF_SKILL;
+
+ if( bl == NULL || target == NULL ){
+ nullpo_info(NLP_MARK);
+ memset(&md,0,sizeof(md));
+ return md;
+ }
+
+ if(target->type == BL_PET) {
+ memset(&md,0,sizeof(md));
+ return md;
+ }
+
+ //Some initial values
+ md.amotion=skill_get_inf(skill_num)&INF_GROUND_SKILL?0:status_get_amotion(bl);
+ md.dmotion=status_get_dmotion(target);
+ md.damage2=0;
+ md.type=0;
+ md.dmg_lv=ATK_DEF;
+
+ if( bl->type == BL_PC && (sd=(struct map_session_data *)bl) ) {
+ sd->state.attack_type = BF_MISC;
+ sd->state.arrow_atk = 0;
+ if (sd->skillblown[0].id != 0)
+ { //Apply the bonus blewcount. [Skotlex]
+ int i;
+ for (i = 0; i < MAX_PC_BONUS && sd->skillblown[i].id != 0 && sd->skillblown[i].id != skill_num; i++);
+ if (i < MAX_PC_BONUS && sd->skillblown[i].id == skill_num)
+ blewcount += sd->skillblown[i].val;
+ }
+ }
+
+ if( target->type==BL_PC )
+ tsd=(struct map_session_data *)target;
+
+ t_mode = status_get_mode(target);
+ ele = skill_get_pl(skill_num);
+ if (ele == -1) //Attack that takes weapon's element for misc attacks? Make it neutral [Skotlex]
+ ele = 0;
+ race = status_get_race(bl);
+ size = status_get_size(bl);
+ race2 = status_get_race(bl);
+
+ switch(skill_num){
+
+ case HT_LANDMINE: // ランドマイン
+ damage=skill_lv*(dex+75)*(100+int_)/100;
+ break;
+
+ case HT_BLASTMINE: // ブラストマイン
+ damage=skill_lv*(dex/2+50)*(100+int_)/100;
+ break;
+
+ case HT_CLAYMORETRAP: // クレイモア?[トラップ
+ damage=skill_lv*(dex/2+75)*(100+int_)/100;
+ break;
+
+ case HT_BLITZBEAT: // ブリッツビ?[ト
+ if( sd==NULL || (skill = pc_checkskill(sd,HT_STEELCROW)) <= 0)
+ skill=0;
+ damage=(dex/10+int_/2+skill*3+40)*2;
+ if(flag > 1)
+ damage /= flag;
+ aflag = (aflag&~BF_RANGEMASK)|BF_LONG;
+ break;
+
+ case TF_THROWSTONE: // ?ホ投げ
+ damage=50;
+ aflag = (aflag&~BF_RANGEMASK)|BF_LONG;
+ break;
+
+ case BA_DISSONANCE: // 不協和音
+ damage=30+skill_lv*10+pc_checkskill(sd,BA_MUSICALLESSON)*3;
+ break;
+
+ case NPC_SELFDESTRUCTION: // 自爆
+ damage = status_get_hp(bl);
+ damagefix = 0;
+ break;
+
+ case NPC_SMOKING: // タバコを吸う
+ damage=3;
+ damagefix=0;
+ break;
+
+ case NPC_DARKBREATH:
+ {
+ struct status_change *sc_data = status_get_sc_data(target);
+ int hitrate=status_get_hit(bl) - status_get_flee(target) + 80;
+ hitrate = ( (hitrate>95)?95: ((hitrate<5)?5:hitrate) );
+ if(sc_data && (sc_data[SC_SLEEP].timer!=-1 || sc_data[SC_STAN].timer!=-1 ||
+ sc_data[SC_FREEZE].timer!=-1 || (sc_data[SC_STONE].timer!=-1 && sc_data[SC_STONE].val2==0) ) )
+ hitrate = 1000000;
+ if(rand()%100 < hitrate) {
+ damage = 500 + (skill_lv-1)*1000 + rand()%1000;
+ if(damage > 9999) damage = 9999;
+ } else
+ md.dmg_lv=ATK_FLEE;
+ }
+ break;
+
+ case SN_FALCONASSAULT: /* ファルコンアサルト */
+ if( sd==NULL || (skill = pc_checkskill(sd,HT_STEELCROW)) <= 0)
+ skill=0;
+ damage=(dex/10+int_/2+skill*3+40)*2; //Blitz Beat Damage
+ damage=damage*(150+70*skill_lv)/100; //Falcon Assault Modifier
+ if(flag > 1)
+ damage /= flag;
+ aflag = (aflag&~BF_RANGEMASK)|BF_LONG;
+ break;
+
+ case PA_PRESSURE:
+ damage=500+300*skill_lv;
+ damagefix=0;
+ aflag = (aflag&~BF_RANGEMASK)|BF_LONG;
+ break;
+
+ case CR_ACIDDEMONSTRATION:
+ //This equation is not official, but it's the closest to the official one
+ //that Viccious Pucca and the other folks at the forums could come up with. [Skotlex]
+ damage = int_ * (int)(sqrt(100*status_get_vit(target))) / 3;
+ if (tsd) damage/=2;
+ aflag = (aflag&~BF_RANGEMASK)|BF_LONG;
+ break;
+ }
+
+ if(damagefix){
+ if(damage<1 && skill_num != NPC_DARKBREATH)
+ damage=1;
+
+ if( tsd ){
+ cardfix=100;
+ cardfix=cardfix*(100-tsd->subele[ele])/100;
+ cardfix=cardfix*(100-tsd->subsize[size])/100;
+ cardfix=cardfix*(100-tsd->subrace2[race2])/100;
+ cardfix=cardfix*(100-tsd->subrace[race])/100;
+ cardfix=cardfix*(100-tsd->subrace[is_boss(bl)?10:11])/100;
+ cardfix=cardfix*(100-tsd->misc_def_rate)/100;
+ if(aflag&BF_SHORT)
+ cardfix=cardfix*(100-tsd->near_attack_def_rate)/100;
+ else // BF_LONG (there's no other choice)
+ cardfix=cardfix*(100-tsd->long_attack_def_rate)/100;
+
+ damage=damage*cardfix/100;
+ }
+ if (sd && skill_num > 0 && sd->skillatk[0].id != 0)
+ {
+ int i;
+ for (i = 0; i < MAX_PC_BONUS && sd->skillatk[i].id != 0 && sd->skillatk[i].id != skill_num; i++);
+ if (i < MAX_PC_BONUS && sd->skillatk[i].id == skill_num)
+ damage += damage*sd->skillatk[i].val/100;
+ }
+
+ if(damage < 0) damage = 0;
+ damage=battle_attr_fix(bl, target, damage, ele, status_get_element(target) ); // 属?ォ?C?ウ
+ }
+
+ div_=skill_get_num( skill_num,skill_lv );
+ if(div_>1)
+ damage*=div_;
+
+ if(damage > 0 && t_mode&MD_PLANT && skill_num != PA_PRESSURE) //Pressure can vaporize plants.
+ damage = 1;
+
+ if(is_boss(target))
+ blewcount = 0;
+
+ if (battle_config.skillrange_by_distance)
+ { //Skill range based on distance between src/target [Skotlex]
+ if ((bl->type == BL_PC && battle_config.skillrange_by_distance&1)
+ || (bl->type == BL_MOB && battle_config.skillrange_by_distance&2)
+ || (bl->type == BL_PET && battle_config.skillrange_by_distance&4)
+ ) {
+ if (check_distance_bl(bl, target, 3))
+ aflag=(aflag&~BF_RANGEMASK)|BF_SHORT;
+ else
+ aflag=(aflag&~BF_RANGEMASK)|BF_LONG;
+ }
+ }
+
+ if (skill_num != PA_PRESSURE) //Pressure ignores all these things...
+ damage=battle_calc_damage(bl,target,damage,div_,skill_num,skill_lv,aflag); // 最終修正
+
+ md.damage=damage;
+ md.div_=div_;
+ md.blewcount=blewcount;
+ md.flag=aflag;
+ return md;
+}
+/*==========================================
+ * ダ??[ジ計算一括??用
+ *------------------------------------------
+ */
+struct Damage battle_calc_attack( int attack_type,
+ struct block_list *bl,struct block_list *target,int skill_num,int skill_lv,int flag)
+{
+ struct Damage d;
+ switch(attack_type){
+ case BF_WEAPON:
+ d = battle_calc_weapon_attack(bl,target,skill_num,skill_lv,flag);
+ break;
+ case BF_MAGIC:
+ d = battle_calc_magic_attack(bl,target,skill_num,skill_lv,flag);
+ break;
+ case BF_MISC:
+ d = battle_calc_misc_attack(bl,target,skill_num,skill_lv,flag);
+ break;
+ default:
+ if (battle_config.error_log)
+ ShowError("battle_calc_attack: unknown attack type! %d\n",attack_type);
+ memset(&d,0,sizeof(d));
+ break;
+ }
+ return d;
+}
+/*==========================================
+ * 通??U撃??まとめ
+ *------------------------------------------
+ */
+int battle_weapon_attack( struct block_list *src,struct block_list *target,
+ unsigned int tick,int flag)
+{
+ struct map_session_data *sd = NULL, *tsd = NULL;
+ struct status_change *sc_data, *tsc_data;
+ int race, ele, damage, rdamage = 0;
+ struct Damage wd;
+
+ nullpo_retr(0, src);
+ nullpo_retr(0, target);
+
+ if (src->prev == NULL || target->prev == NULL)
+ return 0;
+
+ if(src->type == BL_PC)
+ sd = (struct map_session_data *)src;
+
+ if (target->type == BL_PC)
+ tsd = (struct map_session_data *)target;
+
+ sc_data = status_get_sc_data(src);
+ tsc_data = status_get_sc_data(target);
+
+ race = status_get_race(target);
+ ele = status_get_elem_type(target);
+
+ if(sd && sd->status.weapon == 11) {
+ if(sd->equip_index[10] >= 0) {
+ if(battle_config.arrow_decrement)
+ pc_delitem(sd,sd->equip_index[10],1,0);
+ }
+ else {
+ clif_arrow_fail(sd,0);
+ return 0;
+ }
+ }
+
+ //Check for counter attacks that block your attack. [Skotlex]
+ if(tsc_data)
+ {
+ if(tsc_data[SC_AUTOCOUNTER].timer != -1 &&
+ (!sc_data || sc_data[SC_AUTOCOUNTER].timer == -1) &&
+ status_check_skilluse(target, src, KN_AUTOCOUNTER, 0)
+ ) {
+ int dir = map_calc_dir(target,src->x,src->y);
+ int t_dir = status_get_dir(target);
+ int dist = distance_bl(src, target);
+ if(dist <= 0 || (!map_check_dir(dir,t_dir) && dist <= status_get_range(target)+1))
+ {
+ int skilllv = tsc_data[SC_AUTOCOUNTER].val1;
+ clif_skillcastcancel(target); //Remove the casting bar. [Skotlex]
+ clif_damage(src, target, tick, status_get_amotion(src), 1, 0, 1, 0, 0); //Display MISS.
+ status_change_end(target,SC_AUTOCOUNTER,-1);
+ skill_attack(BF_WEAPON,target,target,src,KN_AUTOCOUNTER,skilllv,tick,0);
+ return 0;
+ }
+ }
+ if (tsc_data[SC_BLADESTOP_WAIT].timer != -1 && !is_boss(src)) {
+ int skilllv = tsc_data[SC_BLADESTOP_WAIT].val1;
+ int duration = skill_get_time2(MO_BLADESTOP,skilllv);
+ status_change_end(target, SC_BLADESTOP_WAIT, -1);
+ clif_damage(src, target, tick, status_get_amotion(src), 1, 0, 1, 0, 0); //Display MISS.
+ status_change_start(target, SC_BLADESTOP, skilllv, 2, (int)target, (int)src, duration, 0);
+ skilllv = sd?pc_checkskill(sd, MO_BLADESTOP):1;
+ status_change_start(src, SC_BLADESTOP, skilllv, 1, (int)src, (int)target, duration, 0);
+ return 0;
+ }
+
+ }
+ //Recycled the rdamage variable rather than use a new one... [Skotlex]
+ if(sd && (rdamage = pc_checkskill(sd,MO_TRIPLEATTACK)) > 0 && sd->status.weapon <= 16) // triple blow works with bows ^^ [celest]
+ {
+ int triple_rate= 30 - rdamage; //Base Rate
+ if (sc_data && sc_data[SC_SKILLRATE_UP].timer!=-1 && sc_data[SC_SKILLRATE_UP].val1 == MO_TRIPLEATTACK)
+ {
+ triple_rate+= triple_rate*(sc_data[SC_SKILLRATE_UP].val2)/100;
+ status_change_end(src,SC_SKILLRATE_UP,-1);
+ }
+ if (rand()%100 < triple_rate) return skill_attack(BF_WEAPON,src,src,target,MO_TRIPLEATTACK,rdamage,tick,0);
+ }
+ else if (sc_data && sc_data[SC_SACRIFICE].timer != -1)
+ return skill_attack(BF_WEAPON,src,src,target,PA_SACRIFICE,sc_data[SC_SACRIFICE].val1,tick,0);
+
+ wd = battle_calc_weapon_attack(src,target, 0, 0,0);
+
+ if ((damage = wd.damage + wd.damage2) > 0 && src != target) {
+ rdamage = 0;
+ if (wd.flag & BF_SHORT) {
+ if (tsd && tsd->short_weapon_damage_return)
+ rdamage += damage * tsd->short_weapon_damage_return / 100;
+ if (tsc_data && tsc_data[SC_REFLECTSHIELD].timer != -1) {
+ rdamage += damage * tsc_data[SC_REFLECTSHIELD].val2 / 100;
+ if (rdamage < 1) rdamage = 1;
+ }
+ } else if (wd.flag & BF_LONG) {
+ if (tsd && tsd->long_weapon_damage_return)
+ rdamage += damage * tsd->long_weapon_damage_return / 100;
+ }
+ if (rdamage > 0)
+ clif_damage(src, src, tick, wd.amotion, wd.dmotion, rdamage, 1, 4, 0);
+ }
+
+
+ clif_damage(src, target, tick, wd.amotion, wd.dmotion, wd.damage, wd.div_ , wd.type, wd.damage2);
+ //二?流?カ手とカタ?[ル追撃のミス表示(無?やり?`)
+ if(sd && sd->status.weapon >= 16 && wd.damage2 == 0)
+ clif_damage(src, target, tick+10, wd.amotion, wd.dmotion,0, 1, 0, 0);
+
+ if (sd && sd->splash_range > 0 && (wd.damage > 0 || wd.damage2 > 0))
+ skill_castend_damage_id(src, target, 0, -1, tick, 0);
+
+ map_freeblock_lock();
+
+ battle_delay_damage(tick+wd.amotion, src, target, BF_WEAPON, 0, 0, (wd.damage+wd.damage2), wd.dmg_lv, 0);
+
+ if (wd.dmg_lv == ATK_DEF || wd.damage > 0 || wd.damage2 > 0) //Added counter effect [Skotlex]
+ skill_counter_additional_effect(src, target, 0, 0, BF_WEAPON, tick);
+ if (!status_isdead(target) && (wd.damage > 0 || wd.damage2 > 0)) {
+ if (sd) {
+ int boss = status_get_mode(target)&MD_BOSS;
+ if (
+ (sd->weapon_coma_ele[ele] > 0 && rand()%10000 < sd->weapon_coma_ele[ele]) ||
+ (sd->weapon_coma_race[race] > 0 && rand()%10000 < sd->weapon_coma_race[race]) ||
+ (sd->weapon_coma_race[boss?10:11] > 0 && rand()%10000 < sd->weapon_coma_race[boss?10:11])
+ )
+ status_change_start(target, SC_COMA, 0, 0, 0, 0, 0, 0);
+ }
+ }
+
+ if (sc_data && sc_data[SC_AUTOSPELL].timer != -1 && rand()%100 < sc_data[SC_AUTOSPELL].val4) {
+ int sp = 0, f = 0;
+ int skillid = sc_data[SC_AUTOSPELL].val2;
+ int skilllv = sc_data[SC_AUTOSPELL].val3;
+ int i = rand()%100;
+ if (sc_data[SC_SPIRIT].timer != -1 && sc_data[SC_SPIRIT].val2 == SL_SAGE)
+ i = 0; //Max chance, no skilllv reduction. [Skotlex]
+ if (i >= 50) skilllv -= 2;
+ else if (i >= 15) skilllv--;
+ if (skilllv < 1) skilllv = 1;
+ if (sd) sp = skill_get_sp(skillid,skilllv) * 2 / 3;
+
+ if ((sd && sd->status.sp >= sp) || !sd) {
+ if (skill_get_inf(skillid) & INF_GROUND_SKILL)
+ f = skill_castend_pos2(src, target->x, target->y, skillid, skilllv, tick, flag);
+ else {
+ switch(skill_get_nk(skillid)) {
+ case NK_NO_DAMAGE:/* 支援系 */
+ if((skillid == AL_HEAL || (skillid == ALL_RESURRECTION && !tsd)) && battle_check_undead(race,ele))
+ f = skill_castend_damage_id(src, target, skillid, skilllv, tick, flag);
+ else
+ f = skill_castend_nodamage_id(src, target, skillid, skilllv, tick, flag);
+ break;
+ case NK_SPLASH_DAMAGE:
+ default:
+ f = skill_castend_damage_id(src, target, skillid, skilllv, tick, flag);
+ break;
+ }
+ }
+ if (sd && !f) { pc_heal(sd, 0, -sp); }
+ }
+ }
+ if (sd) {
+ if (wd.flag & BF_WEAPON && src != target && (wd.damage > 0 || wd.damage2 > 0)) {
+ int hp = 0, sp = 0;
+ if (!battle_config.left_cardfix_to_right) { // 二?流?カ手カ?[ドの吸収系効果を右手に追加しない??
+ hp += battle_calc_drain(wd.damage, sd->right_weapon.hp_drain_rate, sd->right_weapon.hp_drain_per, sd->right_weapon.hp_drain_value);
+ hp += battle_calc_drain(wd.damage2, sd->left_weapon.hp_drain_rate, sd->left_weapon.hp_drain_per, sd->left_weapon.hp_drain_value);
+ sp += battle_calc_drain(wd.damage, sd->right_weapon.sp_drain_rate, sd->right_weapon.sp_drain_per, sd->right_weapon.sp_drain_value);
+ sp += battle_calc_drain(wd.damage2, sd->left_weapon.sp_drain_rate, sd->left_weapon.sp_drain_per, sd->left_weapon.sp_drain_value);
+ } else { // 二?流?カ手カ?[ドの吸収系効果を右手に追加する??
+ int hp_drain_rate = sd->right_weapon.hp_drain_rate + sd->left_weapon.hp_drain_rate;
+ int hp_drain_per = sd->right_weapon.hp_drain_per + sd->left_weapon.hp_drain_per;
+ int hp_drain_value = sd->right_weapon.hp_drain_value + sd->left_weapon.hp_drain_value;
+ int sp_drain_rate = sd->right_weapon.sp_drain_rate + sd->left_weapon.sp_drain_rate;
+ int sp_drain_per = sd->right_weapon.sp_drain_per + sd->left_weapon.sp_drain_per;
+ int sp_drain_value = sd->right_weapon.sp_drain_value + sd->left_weapon.sp_drain_value;
+ hp += battle_calc_drain(wd.damage, hp_drain_rate, hp_drain_per, hp_drain_value);
+ sp += battle_calc_drain(wd.damage, sp_drain_rate, sp_drain_per, sp_drain_value);
+ }
+ if (hp && hp + sd->status.hp > sd->status.max_hp)
+ hp = sd->status.max_hp - sd->status.hp;
+ if (sp && sp + sd->status.sp > sd->status.max_sp)
+ sp = sd->status.max_sp - sd->status.sp;
+
+ if (hp || sp)
+ pc_heal(sd, hp, sp);
+
+ if (battle_config.show_hp_sp_drain)
+ { //Display gained values only when they are positive [Skotlex]
+ if (hp > 0)
+ clif_heal(sd->fd, SP_HP, hp);
+ if (sp > 0)
+ clif_heal(sd->fd, SP_SP, sp);
+ }
+
+ if (tsd && sd->sp_drain_type)
+ pc_heal(tsd, 0, -sp);
+ }
+ }
+ if (rdamage > 0) //By sending attack type "none" skill_additional_effect won't be invoked. [Skotlex]
+ battle_delay_damage(tick+wd.amotion, target, src, 0, 0, 0, rdamage, ATK_DEF, 0);
+
+ if (tsc_data) {
+ if (tsc_data[SC_POISONREACT].timer != -1 &&
+ check_distance_bl(src, target, status_get_range(target)+1) &&
+ status_check_skilluse(target, src, TF_POISON, 0)
+ ) { //Poison React
+ if (status_get_elem_type(src) == 5) {
+ tsc_data[SC_POISONREACT].val2 = 0;
+ skill_attack(BF_WEAPON,target,target,src,AS_POISONREACT,sc_data[SC_POISONREACT].val1,tick,0);
+ } else {
+ skill_attack(BF_WEAPON,target,target,src,TF_POISON, 5, tick, flag);
+ --tsc_data[SC_POISONREACT].val2;
+ }
+ if (tsc_data[SC_POISONREACT].val2 <= 0)
+ status_change_end(target, SC_POISONREACT, -1);
+ }
+ if (tsc_data[SC_SPLASHER].timer != -1) //殴ったので対?ロのベナムスプラッシャ?[?態を解?
+ status_change_end(target, SC_SPLASHER, -1);
+ }
+
+ //SG_FUSION hp penalty [Komurka]
+ if (sd && sc_data && sc_data[SC_FUSION].timer!=-1)
+ {
+ int hp=0;
+ if(target->type == BL_PC)
+ {
+ hp = sd->status.max_hp * 8 / 100;
+ if((sd->status.hp * 100/sd->status.max_hp) <= 20)
+ hp = sd->status.hp;
+ }else
+ hp = sd->status.max_hp * 5 / 1000;
+ pc_heal(sd,-hp,0);
+ }
+
+ map_freeblock_unlock();
+ return wd.dmg_lv;
+}
+
+int battle_check_undead(int race,int element)
+{
+ if(battle_config.undead_detect_type == 0) {
+ if(element == 9)
+ return 1;
+ }
+ else if(battle_config.undead_detect_type == 1) {
+ if(race == 1)
+ return 1;
+ }
+ else {
+ if(element == 9 || race == 1)
+ return 1;
+ }
+ return 0;
+}
+
+/*==========================================
+ * Checks the state between two targets (rewritten by Skotlex)
+ * (enemy, friend, party, guild, etc)
+ * See battle.h for possible values/combinations
+ * to be used here (BCT_* constants)
+ * Return value is:
+ * 1: flag holds true (is enemy, party, etc)
+ * -1: flag fails
+ * 0: Invalid target (non-targetable ever)
+ *------------------------------------------
+ */
+int battle_check_target( struct block_list *src, struct block_list *target,int flag)
+{
+ int m,state = 0; //Initial state none
+ int strip_enemy = 1; //Flag which marks whether to remove the BCT_ENEMY status if it's also friend/ally.
+ struct block_list *s_bl= src, *t_bl= target;
+
+ nullpo_retr(0, src);
+ nullpo_retr(0, target);
+
+ m = target->m;
+ if (flag&BCT_ENEMY && !map_flag_gvg(m) && !(status_get_mode(src)&MD_BOSS))
+ { //No offensive stuff while in Basilica.
+ if (map_getcell(m,src->x,src->y,CELL_CHKBASILICA) ||
+ map_getcell(m,target->x,target->y,CELL_CHKBASILICA))
+ return -1;
+ }
+
+ if (target->type == BL_SKILL) //Needed out of the switch in case the ownership needs to be passed skill->mob->master
+ {
+ struct skill_unit *su = (struct skill_unit *)target;
+ if (!su->group)
+ return 0;
+ if (skill_get_inf2(su->group->skill_id)&INF2_TRAP)
+ { //Only a few skills can target traps...
+ switch (battle_getcurrentskill(src))
+ {
+ case HT_REMOVETRAP:
+ case AC_SHOWER:
+ case WZ_HEAVENDRIVE:
+ state |= BCT_ENEMY;
+ strip_enemy = 0;
+ break;
+ default:
+ return 0;
+ }
+ } else if (su->group->skill_id==WZ_ICEWALL)
+ { //Icewall can be hit by anything except skills.
+ if (src->type == BL_SKILL)
+ return 0;
+ state |= BCT_ENEMY;
+ strip_enemy = 0;
+ } else //Excepting traps and icewall, you should not be able to target skills.
+ return 0;
+ if ((t_bl = map_id2bl(su->group->src_id)) == NULL)
+ t_bl = target; //Fallback on the trap itself, otherwise consider this a "versus caster" scenario.
+ }
+
+ switch (t_bl->type)
+ {
+ case BL_PC:
+ {
+ struct map_session_data *sd = (struct map_session_data *)t_bl;
+ if (sd->invincible_timer != -1 || pc_isinvisible(sd))
+ return -1; //Cannot be targeted yet.
+ if (sd->state.monster_ignore && src->type == BL_MOB)
+ return 0; //option to have monsters ignore GMs [Valaris]
+ if (sd->special_state.killable)
+ {
+ state |= BCT_ENEMY; //Universal Victim
+ strip_enemy = 0;
+ }
+ break;
+ }
+ case BL_MOB:
+ {
+ struct mob_data *md = (struct mob_data *)t_bl;
+ if (!agit_flag && md->guardian_data && md->guardian_data->guild_id)
+ return 0; //Disable guardians/emperiums owned by Guilds on non-woe times.
+ if (md->special_state.ai == 2)
+ { //Mines are sort of universal enemies.
+ state |= BCT_ENEMY;
+ strip_enemy = 0;
+ }
+ if (md->master_id && (t_bl = map_id2bl(md->master_id)) == NULL)
+ t_bl = &md->bl; //Fallback on the mob itself, otherwise consider this a "versus master" scenario.
+ break;
+ }
+ case BL_PET:
+ {
+ return 0; //Pets cannot be targetted.
+ }
+ case BL_SKILL: //Skill with no owner? Kinda odd... but.. let it through.
+ break;
+ default: //Invalid target
+ return 0;
+ }
+
+ if (src->type == BL_SKILL)
+ {
+ struct skill_unit *su = (struct skill_unit *)src;
+ if (!su->group)
+ return 0;
+
+ if (su->group->src_id == target->id)
+ {
+ int inf2;
+ inf2 = skill_get_inf2(su->group->skill_id);
+ if (inf2&INF2_NO_TARGET_SELF)
+ return -1;
+ if (inf2&INF2_TARGET_SELF)
+ return 1;
+ }
+ if ((s_bl = map_id2bl(su->group->src_id)) == NULL)
+ s_bl = src; //Fallback on the trap itself, otherwise consider this a "caster versus enemy" scenario.
+ }
+
+ switch (s_bl->type)
+ {
+ case BL_PC:
+ {
+ struct map_session_data *sd = (struct map_session_data *) s_bl;
+ if (sd->special_state.killer)
+ {
+ state |= BCT_ENEMY; //Is on a killing rampage :O
+ strip_enemy = 0;
+ } else
+ if (sd->duel_group && // Duel [LuzZza]
+ !((!battle_config.duel_allow_pvp && map[m].flag.pvp) ||
+ (!battle_config.duel_allow_gvg && map_flag_gvg(m)))) {
+ if (t_bl->type == BL_PC && t_bl != s_bl &&
+ (sd->duel_group == ((struct map_session_data *)t_bl)->duel_group))
+ {
+ state |= BCT_ENEMY;
+ strip_enemy = 0;
+ } else if (t_bl != s_bl) {
+ // You can't target anything out of your duel
+ return 0;
+ }
+ }
+ if (map_flag_gvg(m) && !sd->status.guild_id &&
+ t_bl->type == BL_MOB && ((struct mob_data *)t_bl)->guardian_data)
+ return 0; //If you don't belong to a guild, can't target guardians/emperium.
+ if (t_bl->type != BL_PC)
+ state |= BCT_ENEMY; //Natural enemy.
+ break;
+ }
+ case BL_MOB:
+ {
+ struct mob_data *md = (struct mob_data *)s_bl;
+ if (!agit_flag && md->guardian_data && md->guardian_data->guild_id)
+ return 0; //Disable guardians/emperium owned by Guilds on non-woe times.
+ if (!md->special_state.ai) { //Normal mobs.
+ if (t_bl->type == BL_MOB && !((struct mob_data*)t_bl)->special_state.ai)
+ state |= BCT_PARTY; //Normal mobs with no ai are friends.
+ else
+ state |= BCT_ENEMY; //However, all else are enemies.
+ } else if (t_bl->type != BL_PC)
+ state |= BCT_ENEMY; //Natural enemy for AI mobs are nonplayers.
+ if (md->master_id && (s_bl = map_id2bl(md->master_id)) == NULL)
+ s_bl = &md->bl; //Fallback on the mob itself, otherwise consider this a "from master" scenario.
+ break;
+ }
+ case BL_PET:
+ {
+ struct pet_data *pd = (struct pet_data *)s_bl;
+ if (t_bl->type != BL_MOB && flag&BCT_ENEMY)
+ return 0; //Pet may not attack non-mobs/items.
+ if (t_bl->type == BL_MOB && ((struct mob_data *)t_bl)->guardian_data && flag&BCT_ENEMY)
+ return 0; //pet may not attack Guardians/Emperium
+ if (t_bl->type != BL_PC)
+ state |= BCT_ENEMY; //Stock enemy type.
+ if (pd->msd)
+ s_bl = &pd->msd->bl; //"My master's enemies are my enemies..."
+ break;
+ }
+ case BL_SKILL: //Skill with no owner? Fishy, but let it through.
+ break;
+ default: //Invalid source of attack?
+ return 0;
+ }
+
+ if ((flag&BCT_ALL) == BCT_ALL) { //All actually stands for all players/mobs
+ if (target->type == BL_MOB || target->type == BL_PC)
+ return 1;
+ else
+ return -1;
+ } else if (flag == BCT_NOONE) //Why would someone use this? no clue.
+ return -1;
+
+ if (t_bl == s_bl)
+ { //No need for further testing.
+ state |= BCT_SELF|BCT_PARTY|BCT_GUILD;
+ if (state&BCT_ENEMY && strip_enemy)
+ state&=~BCT_ENEMY;
+ return (flag&state)?1:-1;
+ }
+
+ if (map_flag_vs(m)) { //Check rivalry settings.
+ if (flag&(BCT_PARTY|BCT_ENEMY)) {
+ int s_party = status_get_party_id(s_bl);
+ if (
+ !(map[m].flag.pvp && map[m].flag.pvp_noparty) &&
+ !(map_flag_gvg(m) && map[m].flag.gvg_noparty) &&
+ s_party && s_party == status_get_party_id(t_bl)
+ )
+ state |= BCT_PARTY;
+ else
+ state |= BCT_ENEMY;
+ }
+ if (flag&(BCT_GUILD|BCT_ENEMY)) {
+ int s_guild = status_get_guild_id(s_bl);
+ int t_guild = status_get_guild_id(t_bl);
+ if (
+ !(map[m].flag.pvp && map[m].flag.pvp_noguild) &&
+ s_guild && t_guild && (s_guild == t_guild || guild_idisallied(s_guild, t_guild))
+ )
+ state |= BCT_GUILD;
+ else
+ state |= BCT_ENEMY;
+ }
+ if (state&BCT_ENEMY && battle_config.pk_mode && !map_flag_gvg(m) && s_bl->type == BL_PC && t_bl->type == BL_PC)
+ { //Prevent novice engagement on pk_mode (feature by Valaris)
+ struct map_session_data* sd;
+ if ((sd = (struct map_session_data*)s_bl) &&
+ ((sd->class_&MAPID_UPPERMASK) == MAPID_NOVICE || sd->status.base_level < battle_config.pk_min_level))
+ state&=~BCT_ENEMY;
+ else
+ if ((sd = (struct map_session_data*)t_bl) &&
+ ((sd->class_&MAPID_UPPERMASK) == MAPID_NOVICE || sd->status.base_level < battle_config.pk_min_level))
+ state&=~BCT_ENEMY;
+ }
+ } else { //Non pvp/gvg, check party/guild settings.
+ if (flag&BCT_PARTY || state&BCT_ENEMY) {
+ int s_party = status_get_party_id(s_bl);
+ if (s_party && s_party ==status_get_party_id(t_bl))
+ state |= BCT_PARTY;
+ }
+ if (flag&BCT_GUILD || state&BCT_ENEMY) {
+ int s_guild = status_get_guild_id(s_bl);
+ int t_guild = status_get_guild_id(t_bl);
+ if (s_guild && t_guild && (s_guild == t_guild || guild_idisallied(s_guild, t_guild)))
+ state |= BCT_GUILD;
+ }
+ }
+
+ if (!state) //If not an enemy, nor a guild, nor party, nor yourself, it's neutral.
+ state = BCT_NEUTRAL;
+ //Alliance state takes precedence over enemy one.
+ else if (state&BCT_ENEMY && strip_enemy && state&(BCT_SELF|BCT_PARTY|BCT_GUILD))
+ state&=~BCT_ENEMY;
+
+ return (flag&state)?1:-1;
+}
+/*==========================================
+ * 射程判定
+ *------------------------------------------
+ */
+int battle_check_range(struct block_list *src,struct block_list *bl,int range)
+{
+ int arange;
+
+ nullpo_retr(0, src);
+ nullpo_retr(0, bl);
+
+ if(src->m != bl->m) // 違うマップ
+ return 0;
+
+ arange = distance_bl(src, bl);
+ if( range>0 && range < arange)
+ return 0;
+
+ if( arange<2 ) //No need for path checking.
+ return 1;
+
+ // ?瘧Q物判定
+ return path_search_long(NULL,src->m,src->x,src->y,bl->x,bl->y);
+}
+
+/*==========================================
+ * Return numerical value of a switch configuration (modified by [Yor])
+ * on/off, english, fran軋is, deutsch, espaol
+ *------------------------------------------
+ */
+int battle_config_switch(const char *str) {
+ if (strcmpi(str, "on") == 0 || strcmpi(str, "yes") == 0 || strcmpi(str, "oui") == 0 || strcmpi(str, "ja") == 0 || strcmpi(str, "si") == 0)
+ return 1;
+ if (strcmpi(str, "off") == 0 || strcmpi(str, "no") == 0 || strcmpi(str, "non") == 0 || strcmpi(str, "nein") == 0)
+ return 0;
+ return atoi(str);
+}
+
+static const struct battle_data_short {
+ const char *str;
+ unsigned short *val;
+} battle_data_short[] = { //List here battle_athena options which are type unsigned short!
+ { "warp_point_debug", &battle_config.warp_point_debug },
+ { "enemy_critical_rate", &battle_config.enemy_critical_rate },
+ { "enemy_str", &battle_config.enemy_str },
+ { "enemy_perfect_flee", &battle_config.enemy_perfect_flee },
+ { "casting_rate", &battle_config.cast_rate },
+ { "delay_rate", &battle_config.delay_rate },
+ { "delay_dependon_dex", &battle_config.delay_dependon_dex },
+ { "skill_delay_attack_enable", &battle_config.sdelay_attack_enable },
+ { "left_cardfix_to_right", &battle_config.left_cardfix_to_right },
+ { "player_skill_add_range", &battle_config.pc_skill_add_range },
+ { "skill_out_range_consume", &battle_config.skill_out_range_consume },
+ { "monster_skill_add_range", &battle_config.mob_skill_add_range },
+ { "skillrange_by_distance", &battle_config.skillrange_by_distance },
+ { "skillrange_from_weapon", &battle_config.use_weapon_skill_range },
+ { "player_damage_delay_rate", &battle_config.pc_damage_delay_rate },
+ { "defunit_not_enemy", &battle_config.defnotenemy },
+ { "gvg_traps_target_all", &battle_config.vs_traps_bctall },
+ { "random_monster_checklv", &battle_config.random_monster_checklv },
+ { "attribute_recover", &battle_config.attr_recover },
+ { "flooritem_lifetime", &battle_config.flooritem_lifetime },
+ { "item_auto_get", &battle_config.item_auto_get },
+ { "drop_rate0item", &battle_config.drop_rate0item },
+ { "pvp_exp", &battle_config.pvp_exp },
+ { "gtb_pvp_only", &battle_config.gtb_pvp_only },
+ { "guild_max_castles", &battle_config.guild_max_castles },
+ { "death_penalty_type", &battle_config.death_penalty_type },
+ { "death_penalty_base", &battle_config.death_penalty_base },
+ { "death_penalty_job", &battle_config.death_penalty_job },
+ { "restart_hp_rate", &battle_config.restart_hp_rate },
+ { "restart_sp_rate", &battle_config.restart_sp_rate },
+ { "mvp_hp_rate", &battle_config.mvp_hp_rate },
+ { "monster_hp_rate", &battle_config.monster_hp_rate },
+ { "monster_max_aspd", &battle_config.monster_max_aspd },
+ { "atcommand_gm_only", &battle_config.atc_gmonly },
+ { "atcommand_spawn_quantity_limit", &battle_config.atc_spawn_quantity_limit },
+ { "atcommand_slave_clone_limit", &battle_config.atc_slave_clone_limit},
+ { "gm_all_skill", &battle_config.gm_allskill },
+ { "gm_all_skill_add_abra", &battle_config.gm_allskill_addabra },
+ { "gm_all_equipment", &battle_config.gm_allequip },
+ { "gm_skill_unconditional", &battle_config.gm_skilluncond },
+ { "gm_join_chat", &battle_config.gm_join_chat },
+ { "gm_kick_chat", &battle_config.gm_kick_chat },
+ { "player_skillfree", &battle_config.skillfree },
+ { "player_skillup_limit", &battle_config.skillup_limit },
+ { "weapon_produce_rate", &battle_config.wp_rate },
+ { "potion_produce_rate", &battle_config.pp_rate },
+ { "monster_active_enable", &battle_config.monster_active_enable },
+ { "monster_damage_delay_rate", &battle_config.monster_damage_delay_rate},
+ { "monster_loot_type", &battle_config.monster_loot_type },
+// { "mob_skill_use", &battle_config.mob_skill_use }, //Deprecated
+ { "mob_skill_rate", &battle_config.mob_skill_rate },
+ { "mob_skill_delay", &battle_config.mob_skill_delay },
+ { "mob_count_rate", &battle_config.mob_count_rate },
+ { "mob_spawn_delay", &battle_config.mob_spawn_delay },
+ { "no_spawn_on_player", &battle_config.no_spawn_on_player },
+ { "plant_spawn_delay", &battle_config.plant_spawn_delay },
+ { "boss_spawn_delay", &battle_config.boss_spawn_delay },
+ { "slaves_inherit_speed", &battle_config.slaves_inherit_speed },
+ { "summons_inherit_effects", &battle_config.summons_inherit_effects },
+ { "pc_damage_walk_delay_rate", &battle_config.pc_walk_delay_rate },
+ { "damage_walk_delay_rate", &battle_config.walk_delay_rate },
+ { "multihit_delay", &battle_config.multihit_delay },
+ { "quest_skill_learn", &battle_config.quest_skill_learn },
+ { "quest_skill_reset", &battle_config.quest_skill_reset },
+ { "basic_skill_check", &battle_config.basic_skill_check },
+ { "guild_emperium_check", &battle_config.guild_emperium_check },
+ { "guild_exp_rate", &battle_config.guild_exp_rate },
+ { "guild_exp_limit", &battle_config.guild_exp_limit },
+ { "player_invincible_time", &battle_config.pc_invincible_time },
+ { "pet_catch_rate", &battle_config.pet_catch_rate },
+ { "pet_rename", &battle_config.pet_rename },
+ { "pet_friendly_rate", &battle_config.pet_friendly_rate },
+ { "pet_hungry_delay_rate", &battle_config.pet_hungry_delay_rate },
+ { "pet_hungry_friendly_decrease", &battle_config.pet_hungry_friendly_decrease},
+ { "pet_str", &battle_config.pet_str },
+ { "pet_status_support", &battle_config.pet_status_support },
+ { "pet_attack_support", &battle_config.pet_attack_support },
+ { "pet_damage_support", &battle_config.pet_damage_support },
+ { "pet_support_min_friendly", &battle_config.pet_support_min_friendly },
+ { "pet_support_rate", &battle_config.pet_support_rate },
+ { "pet_attack_exp_to_master", &battle_config.pet_attack_exp_to_master },
+ { "pet_attack_exp_rate", &battle_config.pet_attack_exp_rate },
+ { "pet_lv_rate", &battle_config.pet_lv_rate }, //Skotlex
+ { "pet_max_stats", &battle_config.pet_max_stats }, //Skotlex
+ { "pet_max_atk1", &battle_config.pet_max_atk1 }, //Skotlex
+ { "pet_max_atk2", &battle_config.pet_max_atk2 }, //Skotlex
+ { "pet_disable_in_gvg", &battle_config.pet_no_gvg }, //Skotlex
+ { "skill_min_damage", &battle_config.skill_min_damage },
+ { "finger_offensive_type", &battle_config.finger_offensive_type },
+ { "heal_exp", &battle_config.heal_exp },
+ { "resurrection_exp", &battle_config.resurrection_exp },
+ { "shop_exp", &battle_config.shop_exp },
+ { "combo_delay_rate", &battle_config.combo_delay_rate },
+ { "item_check", &battle_config.item_check },
+ { "item_use_interval", &battle_config.item_use_interval },
+ { "wedding_modifydisplay", &battle_config.wedding_modifydisplay },
+ { "wedding_ignorepalette", &battle_config.wedding_ignorepalette }, //[Skotlex]
+ { "xmas_ignorepalette", &battle_config.xmas_ignorepalette }, // [Valaris]
+ { "natural_heal_weight_rate", &battle_config.natural_heal_weight_rate },
+ { "item_name_override_grffile", &battle_config.item_name_override_grffile},
+ { "item_equip_override_grffile", &battle_config.item_equip_override_grffile}, // [Celest]
+ { "item_slots_override_grffile", &battle_config.item_slots_override_grffile}, // [Celest]
+ { "indoors_override_grffile", &battle_config.indoors_override_grffile}, // [Celest]
+ { "skill_sp_override_grffile", &battle_config.skill_sp_override_grffile}, // [Celest]
+ { "cardillust_read_grffile", &battle_config.cardillust_read_grffile}, // [Celest]
+ { "arrow_decrement", &battle_config.arrow_decrement },
+ { "max_aspd", &battle_config.max_aspd },
+ { "max_walk_speed", &battle_config.max_walk_speed },
+ { "max_lv", &battle_config.max_lv },
+ { "aura_lv", &battle_config.aura_lv },
+ { "max_parameter", &battle_config.max_parameter },
+ { "max_baby_parameter", &battle_config.max_baby_parameter },
+ { "max_def", &battle_config.max_def },
+ { "over_def_bonus", &battle_config.over_def_bonus },
+ { "player_skill_log", &battle_config.pc_skill_log },
+ { "monster_skill_log", &battle_config.mob_skill_log },
+ { "battle_log", &battle_config.battle_log },
+ { "save_log", &battle_config.save_log },
+ { "error_log", &battle_config.error_log },
+ { "etc_log", &battle_config.etc_log },
+ { "save_clothcolor", &battle_config.save_clothcolor },
+ { "undead_detect_type", &battle_config.undead_detect_type },
+ { "player_auto_counter_type", &battle_config.pc_auto_counter_type },
+ { "monster_auto_counter_type", &battle_config.monster_auto_counter_type},
+ { "min_hitrate", &battle_config.min_hitrate },
+ { "max_hitrate", &battle_config.max_hitrate },
+ { "agi_penalty_type", &battle_config.agi_penalty_type },
+ { "agi_penalty_count", &battle_config.agi_penalty_count },
+ { "agi_penalty_num", &battle_config.agi_penalty_num },
+ { "agi_penalty_count_lv", &battle_config.agi_penalty_count_lv },
+ { "vit_penalty_type", &battle_config.vit_penalty_type },
+ { "vit_penalty_count", &battle_config.vit_penalty_count },
+ { "vit_penalty_num", &battle_config.vit_penalty_num },
+ { "vit_penalty_count_lv", &battle_config.vit_penalty_count_lv },
+ { "player_defense_type", &battle_config.player_defense_type },
+ { "monster_defense_type", &battle_config.monster_defense_type },
+ { "pet_defense_type", &battle_config.pet_defense_type },
+ { "magic_defense_type", &battle_config.magic_defense_type },
+ { "player_skill_reiteration", &battle_config.pc_skill_reiteration },
+ { "monster_skill_reiteration", &battle_config.monster_skill_reiteration},
+ { "player_skill_nofootset", &battle_config.pc_skill_nofootset },
+ { "monster_skill_nofootset", &battle_config.monster_skill_nofootset },
+ { "player_cloak_check_type", &battle_config.pc_cloak_check_type },
+ { "monster_cloak_check_type", &battle_config.monster_cloak_check_type },
+ { "sense_type", &battle_config.estimation_type },
+ { "gvg_short_attack_damage_rate", &battle_config.gvg_short_damage_rate },
+ { "gvg_long_attack_damage_rate", &battle_config.gvg_long_damage_rate },
+ { "gvg_weapon_attack_damage_rate", &battle_config.gvg_weapon_damage_rate },
+ { "gvg_magic_attack_damage_rate", &battle_config.gvg_magic_damage_rate },
+ { "gvg_misc_attack_damage_rate", &battle_config.gvg_misc_damage_rate },
+ { "gvg_flee_penalty", &battle_config.gvg_flee_penalty },
+ { "mob_changetarget_byskill", &battle_config.mob_changetarget_byskill},
+ { "player_attack_direction_change", &battle_config.pc_attack_direction_change },
+ { "monster_attack_direction_change", &battle_config.monster_attack_direction_change },
+ { "player_land_skill_limit", &battle_config.pc_land_skill_limit },
+ { "monster_land_skill_limit", &battle_config.monster_land_skill_limit},
+ { "party_skill_penalty", &battle_config.party_skill_penalty },
+ { "monster_class_change_full_recover", &battle_config.monster_class_change_full_recover },
+ { "produce_item_name_input", &battle_config.produce_item_name_input },
+ { "produce_potion_name_input", &battle_config.produce_potion_name_input},
+ { "making_arrow_name_input", &battle_config.making_arrow_name_input },
+ { "holywater_name_input", &battle_config.holywater_name_input },
+ { "cdp_name_input", &battle_config.cdp_name_input },
+ { "display_delay_skill_fail", &battle_config.display_delay_skill_fail },
+ { "display_snatcher_skill_fail", &battle_config.display_snatcher_skill_fail },
+ { "chat_warpportal", &battle_config.chat_warpportal },
+ { "mob_warpportal", &battle_config.mob_warpportal },
+ { "dead_branch_active", &battle_config.dead_branch_active },
+ { "show_steal_in_same_party", &battle_config.show_steal_in_same_party },
+ { "show_party_share_picker", &battle_config.party_show_share_picker },
+ { "party_item_share_type", &battle_config.party_share_type },
+ { "pet_attack_attr_none", &battle_config.pet_attack_attr_none },
+ { "mob_attack_attr_none", &battle_config.mob_attack_attr_none },
+ { "mob_ghostring_fix", &battle_config.mob_ghostring_fix },
+ { "pc_attack_attr_none", &battle_config.pc_attack_attr_none },
+ { "gx_allhit", &battle_config.gx_allhit },
+ { "gx_disptype", &battle_config.gx_disptype },
+ { "devotion_level_difference", &battle_config.devotion_level_difference },
+ { "player_skill_partner_check", &battle_config.player_skill_partner_check},
+ { "hide_GM_session", &battle_config.hide_GM_session },
+ { "invite_request_check", &battle_config.invite_request_check },
+ { "skill_removetrap_type", &battle_config.skill_removetrap_type },
+ { "disp_experience", &battle_config.disp_experience },
+ { "disp_zeny", &battle_config.disp_zeny },
+ { "castle_defense_rate", &battle_config.castle_defense_rate },
+ { "hp_rate", &battle_config.hp_rate },
+ { "sp_rate", &battle_config.sp_rate },
+ { "gm_cant_drop_min_lv", &battle_config.gm_cant_drop_min_lv },
+ { "gm_cant_drop_max_lv", &battle_config.gm_cant_drop_max_lv },
+ { "disp_hpmeter", &battle_config.disp_hpmeter },
+ { "bone_drop", &battle_config.bone_drop },
+ { "buyer_name", &battle_config.buyer_name },
+ { "skill_wall_check", &battle_config.skill_wall_check },
+ { "cell_stack_limit", &battle_config.cell_stack_limit },
+// eAthena additions
+ { "item_logarithmic_drops", &battle_config.logarithmic_drops },
+ { "item_drop_common_min", &battle_config.item_drop_common_min }, // Added by TyrNemesis^
+ { "item_drop_common_max", &battle_config.item_drop_common_max },
+ { "item_drop_equip_min", &battle_config.item_drop_equip_min },
+ { "item_drop_equip_max", &battle_config.item_drop_equip_max },
+ { "item_drop_card_min", &battle_config.item_drop_card_min },
+ { "item_drop_card_max", &battle_config.item_drop_card_max },
+ { "item_drop_mvp_min", &battle_config.item_drop_mvp_min },
+ { "item_drop_mvp_max", &battle_config.item_drop_mvp_max }, // End Addition
+ { "item_drop_heal_min", &battle_config.item_drop_heal_min },
+ { "item_drop_heal_max", &battle_config.item_drop_heal_max },
+ { "item_drop_use_min", &battle_config.item_drop_use_min },
+ { "item_drop_use_max", &battle_config.item_drop_use_max },
+ { "item_drop_treasure_min", &battle_config.item_drop_treasure_min },
+ { "item_drop_treasure_max", &battle_config.item_drop_treasure_max },
+ { "prevent_logout", &battle_config.prevent_logout }, // Added by RoVeRT
+ { "alchemist_summon_reward", &battle_config.alchemist_summon_reward }, // [Valaris]
+ { "max_base_level", &battle_config.max_base_level }, // [Valaris]
+ { "max_job_level", &battle_config.max_job_level },
+ { "max_advanced_job_level", &battle_config.max_adv_level },
+ { "max_super_novice_level", &battle_config.max_sn_level },
+ { "drops_by_luk", &battle_config.drops_by_luk }, // [Valaris]
+ { "drops_by_luk2", &battle_config.drops_by_luk2 }, // [Skotlex]
+ { "equip_natural_break_rate", &battle_config.equip_natural_break_rate },
+ { "equip_self_break_rate", &battle_config.equip_self_break_rate },
+ { "equip_skill_break_rate", &battle_config.equip_skill_break_rate },
+ { "pk_mode", &battle_config.pk_mode }, // [Valaris]
+ { "manner_system", &battle_config.manner_system }, // [Komurka]
+ { "pet_equip_required", &battle_config.pet_equip_required }, // [Valaris]
+ { "multi_level_up", &battle_config.multi_level_up }, // [Valaris]
+ { "backstab_bow_penalty", &battle_config.backstab_bow_penalty },
+ { "night_at_start", &battle_config.night_at_start }, // added by [Yor]
+ { "show_mob_hp", &battle_config.show_mob_hp }, // [Valaris]
+ { "ban_spoof_namer", &battle_config.ban_spoof_namer }, // added by [Yor]
+ { "hack_info_GM_level", &battle_config.hack_info_GM_level }, // added by [Yor]
+ { "any_warp_GM_min_level", &battle_config.any_warp_GM_min_level }, // added by [Yor]
+ { "packet_ver_flag", &battle_config.packet_ver_flag }, // added by [Yor]
+ { "min_hair_style", &battle_config.min_hair_style }, // added by [MouseJstr]
+ { "max_hair_style", &battle_config.max_hair_style }, // added by [MouseJstr]
+ { "min_hair_color", &battle_config.min_hair_color }, // added by [MouseJstr]
+ { "max_hair_color", &battle_config.max_hair_color }, // added by [MouseJstr]
+ { "min_cloth_color", &battle_config.min_cloth_color }, // added by [MouseJstr]
+ { "max_cloth_color", &battle_config.max_cloth_color }, // added by [MouseJstr]
+ { "pet_hair_style", &battle_config.pet_hair_style }, // added by [Skotlex]
+ { "castrate_dex_scale", &battle_config.castrate_dex_scale }, // added by [MouseJstr]
+ { "area_size", &battle_config.area_size }, // added by [MouseJstr]
+ { "muting_players", &battle_config.muting_players}, // added by [Apple]
+ { "zeny_from_mobs", &battle_config.zeny_from_mobs}, // [Valaris]
+ { "mobs_level_up", &battle_config.mobs_level_up}, // [Valaris]
+ { "mobs_level_up_exp_rate", &battle_config.mobs_level_up_exp_rate}, // [Valaris]
+ { "pk_min_level", &battle_config.pk_min_level}, // [celest]
+ { "skill_steal_type", &battle_config.skill_steal_type}, // [celest]
+ { "skill_steal_rate", &battle_config.skill_steal_rate}, // [celest]
+// { "night_darkness_level", &battle_config.night_darkness_level}, // [celest]
+ { "motd_type", &battle_config.motd_type}, // [celest]
+ { "allow_atcommand_when_mute", &battle_config.allow_atcommand_when_mute}, // [celest]
+ { "finding_ore_rate", &battle_config.finding_ore_rate}, // [celest]
+ { "exp_calc_type", &battle_config.exp_calc_type}, // [celest]
+ { "min_skill_delay_limit", &battle_config.min_skill_delay_limit}, // [celest]
+ { "require_glory_guild", &battle_config.require_glory_guild}, // [celest]
+ { "idle_no_share", &battle_config.idle_no_share}, // [celest], for a feature by [MouseJstr]
+ { "party_even_share_bonus", &battle_config.party_even_share_bonus},
+ { "delay_battle_damage", &battle_config.delay_battle_damage}, // [celest]
+ { "display_version", &battle_config.display_version}, // [Ancyker], for a feature by...?
+ { "who_display_aid", &battle_config.who_display_aid}, // [Ancyker], for a feature by...?
+ { "display_hallucination", &battle_config.display_hallucination}, // [Skotlex]
+ { "use_statpoint_table", &battle_config.use_statpoint_table}, // [Skotlex]
+ { "ignore_items_gender", &battle_config.ignore_items_gender}, // [Lupus]
+ { "copyskill_restrict", &battle_config.copyskill_restrict}, // [Aru]
+ { "berserk_cancels_buffs", &battle_config.berserk_cancels_buffs}, // [Aru]
+ { "monster_ai", &battle_config.mob_ai},
+ { "dynamic_mobs", &battle_config.dynamic_mobs},
+ { "mob_remove_damaged", &battle_config.mob_remove_damaged},
+ { "show_hp_sp_drain", &battle_config.show_hp_sp_drain}, // [Skotlex]
+ { "show_hp_sp_gain", &battle_config.show_hp_sp_gain}, // [Skotlex]
+ { "mob_npc_event_type", &battle_config.mob_npc_event_type},
+ { "mob_clear_delay", &battle_config.mob_clear_delay}, // [Valaris]
+ { "character_size", &battle_config.character_size}, // [Lupus]
+ { "mob_max_skilllvl", &battle_config.mob_max_skilllvl}, // [Lupus]
+ { "retaliate_to_master", &battle_config.retaliate_to_master}, // [Skotlex]
+ { "rare_drop_announce", &battle_config.rare_drop_announce}, // [Lupus]
+ { "firewall_hits_on_undead", &battle_config.firewall_hits_on_undead}, // [Skotlex]
+ { "title_lvl1", &battle_config.title_lvl1}, // [Lupus]
+ { "title_lvl2", &battle_config.title_lvl2}, // [Lupus]
+ { "title_lvl3", &battle_config.title_lvl3}, // [Lupus]
+ { "title_lvl4", &battle_config.title_lvl4}, // [Lupus]
+ { "title_lvl5", &battle_config.title_lvl5}, // [Lupus]
+ { "title_lvl6", &battle_config.title_lvl6}, // [Lupus]
+ { "title_lvl7", &battle_config.title_lvl7}, // [Lupus]
+ { "title_lvl8", &battle_config.title_lvl8}, // [Lupus]
+
+ { "duel_enable", &battle_config.duel_enable}, // [LuzZza]
+ { "duel_allow_pvp", &battle_config.duel_allow_pvp}, // [LuzZza]
+ { "duel_allow_gvg", &battle_config.duel_allow_gvg}, // [LuzZza]
+ { "duel_allow_teleport", &battle_config.duel_allow_teleport}, // [LuzZza]
+ { "duel_autoleave_when_die", &battle_config.duel_autoleave_when_die}, //[LuzZza]
+ { "duel_time_interval", &battle_config.duel_time_interval}, // [LuzZza]
+
+ { "skip_teleport_lv1_menu", &battle_config.skip_teleport_lv1_menu}, // [LuzZza]
+ { "allow_skill_without_day", &battle_config.allow_skill_without_day}, // [Komurka]
+};
+
+static const struct battle_data_int {
+ const char *str;
+ int *val;
+} battle_data_int[] = { //List here battle_athena options which are type int!
+ { "item_first_get_time", &battle_config.item_first_get_time },
+ { "item_second_get_time", &battle_config.item_second_get_time },
+ { "item_third_get_time", &battle_config.item_third_get_time },
+ { "mvp_item_first_get_time", &battle_config.mvp_item_first_get_time },
+ { "mvp_item_second_get_time", &battle_config.mvp_item_second_get_time },
+ { "mvp_item_third_get_time", &battle_config.mvp_item_third_get_time },
+ { "base_exp_rate", &battle_config.base_exp_rate },
+ { "job_exp_rate", &battle_config.job_exp_rate },
+ { "zeny_penalty", &battle_config.zeny_penalty },
+ { "mvp_exp_rate", &battle_config.mvp_exp_rate },
+ { "natural_healhp_interval", &battle_config.natural_healhp_interval },
+ { "natural_healsp_interval", &battle_config.natural_healsp_interval },
+ { "natural_heal_skill_interval", &battle_config.natural_heal_skill_interval},
+ { "max_hp", &battle_config.max_hp },
+ { "max_sp", &battle_config.max_sp },
+ { "max_cart_weight", &battle_config.max_cart_weight },
+ { "gvg_eliminate_time", &battle_config.gvg_eliminate_time },
+ { "vending_max_value", &battle_config.vending_max_value },
+// eAthena additions
+ { "item_rate_mvp", &battle_config.item_rate_mvp },
+ { "item_rate_common", &battle_config.item_rate_common }, // Added by RoVeRT
+ { "item_rate_equip", &battle_config.item_rate_equip },
+ { "item_rate_card", &battle_config.item_rate_card }, // End Addition
+ { "item_rate_heal", &battle_config.item_rate_heal }, // Added by Valaris
+ { "item_rate_use", &battle_config.item_rate_use }, // End
+ { "item_rate_treasure", &battle_config.item_rate_treasure }, // End
+ { "day_duration", &battle_config.day_duration }, // added by [Yor]
+ { "night_duration", &battle_config.night_duration }, // added by [Yor]
+ { "mob_remove_delay", &battle_config.mob_remove_delay },
+};
+
+int battle_set_value(char *w1, char *w2) {
+ int i;
+ for(i = 0; i < sizeof(battle_data_short) / (sizeof(battle_data_short[0])); i++)
+ if (strcmpi(w1, battle_data_short[i].str) == 0) {
+ * battle_data_short[i].val = battle_config_switch(w2);
+ return 1;
+ }
+ for(i = 0; i < sizeof(battle_data_int) / (sizeof(battle_data_int[0])); i++)
+ if (strcmpi(w1, battle_data_int[i].str) == 0) {
+ *battle_data_int[i].val = battle_config_switch(w2);
+ return 1;
+ }
+/*
+ int val = battle_config_switch(w2);
+ switch(battle_data[i].size) {
+ case 1:
+ *((unsigned char *) battle_data[i].val) = val;
+ break;
+ case 2:
+ *((unsigned short *) battle_data[i].val) = val;
+ break;
+ case 4:
+ *((unsigned int *) battle_data[i].val) = val;
+ break;
+ }
+ return 1;
+ }
+*/
+ return 0;
+}
+
+void battle_set_defaults() {
+ battle_config.warp_point_debug=0;
+ battle_config.enemy_critical_rate=0;
+ battle_config.enemy_str=1;
+ battle_config.enemy_perfect_flee=0;
+ battle_config.cast_rate=100;
+ battle_config.delay_rate=100;
+ battle_config.delay_dependon_dex=0;
+ battle_config.sdelay_attack_enable=0;
+ battle_config.left_cardfix_to_right=0;
+ battle_config.pc_skill_add_range=0;
+ battle_config.skill_out_range_consume=1;
+ battle_config.mob_skill_add_range=0;
+ battle_config.skillrange_by_distance=6;
+ battle_config.use_weapon_skill_range=0;
+ battle_config.pc_damage_delay_rate=100;
+ battle_config.defnotenemy=0;
+ battle_config.vs_traps_bctall=1;
+ battle_config.random_monster_checklv=1;
+ battle_config.attr_recover=1;
+ battle_config.flooritem_lifetime=LIFETIME_FLOORITEM*1000;
+ battle_config.item_auto_get=0;
+ battle_config.item_first_get_time=3000;
+ battle_config.item_second_get_time=1000;
+ battle_config.item_third_get_time=1000;
+ battle_config.mvp_item_first_get_time=10000;
+ battle_config.mvp_item_second_get_time=10000;
+ battle_config.mvp_item_third_get_time=2000;
+
+ battle_config.drop_rate0item=0;
+ battle_config.base_exp_rate=100;
+ battle_config.job_exp_rate=100;
+ battle_config.pvp_exp=1;
+ battle_config.gtb_pvp_only=0;
+ battle_config.death_penalty_type=0;
+ battle_config.death_penalty_base=0;
+ battle_config.death_penalty_job=0;
+ battle_config.zeny_penalty=0;
+ battle_config.restart_hp_rate=0;
+ battle_config.restart_sp_rate=0;
+ battle_config.mvp_exp_rate=100;
+ battle_config.mvp_hp_rate=100;
+ battle_config.monster_hp_rate=100;
+ battle_config.monster_max_aspd=199;
+ battle_config.atc_gmonly=0;
+ battle_config.atc_spawn_quantity_limit=0;
+ battle_config.atc_slave_clone_limit=0;
+ battle_config.gm_allskill=0;
+ battle_config.gm_allequip=0;
+ battle_config.gm_skilluncond=0;
+ battle_config.gm_join_chat=0;
+ battle_config.gm_kick_chat=0;
+ battle_config.guild_max_castles=0;
+ battle_config.skillfree = 0;
+ battle_config.skillup_limit = 0;
+ battle_config.wp_rate=100;
+ battle_config.pp_rate=100;
+ battle_config.monster_active_enable=1;
+ battle_config.monster_damage_delay_rate=100;
+ battle_config.monster_loot_type=0;
+// battle_config.mob_skill_use=1;
+ battle_config.mob_skill_rate=100;
+ battle_config.mob_skill_delay=100;
+ battle_config.mob_count_rate=100;
+ battle_config.mob_spawn_delay=100;
+ battle_config.no_spawn_on_player=0;
+ battle_config.plant_spawn_delay=100;
+ battle_config.boss_spawn_delay=100;
+ battle_config.slaves_inherit_speed=1;
+ battle_config.summons_inherit_effects=1;
+ battle_config.pc_walk_delay_rate=20;
+ battle_config.walk_delay_rate=100;
+ battle_config.multihit_delay=230;
+ battle_config.quest_skill_learn=0;
+ battle_config.quest_skill_reset=1;
+ battle_config.basic_skill_check=1;
+ battle_config.guild_emperium_check=1;
+ battle_config.guild_exp_limit=50;
+ battle_config.guild_exp_rate=100;
+ battle_config.pc_invincible_time = 5000;
+ battle_config.pet_catch_rate=100;
+ battle_config.pet_rename=0;
+ battle_config.pet_friendly_rate=100;
+ battle_config.pet_hungry_delay_rate=100;
+ battle_config.pet_hungry_friendly_decrease=5;
+ battle_config.pet_str=1;
+ battle_config.pet_status_support=0;
+ battle_config.pet_attack_support=0;
+ battle_config.pet_damage_support=0;
+ battle_config.pet_support_min_friendly=900;
+ battle_config.pet_support_rate=100;
+ battle_config.pet_attack_exp_to_master=0;
+ battle_config.pet_attack_exp_rate=100;
+ battle_config.pet_lv_rate=0; //Skotlex
+ battle_config.pet_max_stats=99; //Skotlex
+ battle_config.pet_max_atk1=750; //Skotlex
+ battle_config.pet_max_atk2=1000; //Skotlex
+ battle_config.pet_no_gvg=0; //Skotlex
+ battle_config.skill_min_damage=6; //Ishizu claims that magic and misc attacks always do at least div_ damage. [Skotlex]
+ battle_config.finger_offensive_type=0;
+ battle_config.heal_exp=0;
+ battle_config.resurrection_exp=0;
+ battle_config.shop_exp=0;
+ battle_config.combo_delay_rate=100;
+ battle_config.item_check=1;
+ battle_config.item_use_interval=500;
+ battle_config.wedding_modifydisplay=0;
+ battle_config.wedding_ignorepalette=0;
+ battle_config.xmas_ignorepalette=0; // [Valaris]
+ battle_config.natural_healhp_interval=6000;
+ battle_config.natural_healsp_interval=8000;
+ battle_config.natural_heal_skill_interval=10000;
+ battle_config.natural_heal_weight_rate=50;
+ battle_config.item_name_override_grffile=1;
+ battle_config.item_equip_override_grffile=0; // [Celest]
+ battle_config.item_slots_override_grffile=0; // [Celest]
+ battle_config.indoors_override_grffile=0; // [Celest]
+ battle_config.skill_sp_override_grffile=0; // [Celest]
+ battle_config.cardillust_read_grffile=0; // [Celest]
+ battle_config.arrow_decrement=1;
+ battle_config.max_aspd = 199;
+ battle_config.max_walk_speed = 300;
+ battle_config.max_hp = 32500;
+ battle_config.max_sp = 32500;
+ battle_config.max_lv = 99; // [MouseJstr]
+ battle_config.aura_lv = 99; // [Skotlex]
+ battle_config.max_parameter = 99;
+ battle_config.max_baby_parameter = 80;
+ battle_config.max_cart_weight = 8000;
+ battle_config.max_def = 99; // [Skotlex]
+ battle_config.over_def_bonus = 0; // [Skotlex]
+ battle_config.pc_skill_log = 0;
+ battle_config.mob_skill_log = 0;
+ battle_config.battle_log = 0;
+ battle_config.save_log = 0;
+ battle_config.error_log = 1;
+ battle_config.etc_log = 1;
+ battle_config.save_clothcolor = 0;
+ battle_config.undead_detect_type = 0;
+ battle_config.pc_auto_counter_type = 1;
+ battle_config.monster_auto_counter_type = 1;
+ battle_config.min_hitrate = 5;
+ battle_config.max_hitrate = 95;
+ battle_config.agi_penalty_type = 1;
+ battle_config.agi_penalty_count = 3;
+ battle_config.agi_penalty_num = 10;
+ battle_config.agi_penalty_count_lv = ATK_FLEE;
+ battle_config.vit_penalty_type = 1;
+ battle_config.vit_penalty_count = 3;
+ battle_config.vit_penalty_num = 5;
+ battle_config.vit_penalty_count_lv = ATK_DEF;
+ battle_config.player_defense_type = 0;
+ battle_config.monster_defense_type = 0;
+ battle_config.pet_defense_type = 0;
+ battle_config.magic_defense_type = 0;
+ battle_config.pc_skill_reiteration = 0;
+ battle_config.monster_skill_reiteration = 0;
+ battle_config.pc_skill_nofootset = 0;
+ battle_config.monster_skill_nofootset = 0;
+ battle_config.pc_cloak_check_type = 1;
+ battle_config.monster_cloak_check_type = 0;
+ battle_config.estimation_type = 3;
+ battle_config.gvg_short_damage_rate = 100;
+ battle_config.gvg_long_damage_rate = 75;
+ battle_config.gvg_weapon_damage_rate = 60;
+ battle_config.gvg_magic_damage_rate = 50;
+ battle_config.gvg_misc_damage_rate = 60;
+ battle_config.gvg_flee_penalty = 20;
+ battle_config.gvg_eliminate_time = 7000;
+ battle_config.mob_changetarget_byskill = 0;
+ battle_config.pc_attack_direction_change = 1;
+ battle_config.monster_attack_direction_change = 1;
+ battle_config.pc_land_skill_limit = 1;
+ battle_config.monster_land_skill_limit = 1;
+ battle_config.party_skill_penalty = 1;
+ battle_config.monster_class_change_full_recover = 0;
+ battle_config.produce_item_name_input = 1;
+ battle_config.produce_potion_name_input = 1;
+ battle_config.making_arrow_name_input = 1;
+ battle_config.holywater_name_input = 1;
+ battle_config.cdp_name_input = 1;
+ battle_config.display_delay_skill_fail = 1;
+ battle_config.display_snatcher_skill_fail = 1;
+ battle_config.chat_warpportal = 0;
+ battle_config.mob_warpportal = 0;
+ battle_config.dead_branch_active = 0;
+ battle_config.vending_max_value = 10000000;
+ battle_config.show_steal_in_same_party = 0;
+ battle_config.party_share_type = 0;
+ battle_config.party_show_share_picker = 0;
+ battle_config.pet_attack_attr_none = 0;
+ battle_config.pc_attack_attr_none = 0;
+ battle_config.mob_attack_attr_none = 0;
+ battle_config.mob_ghostring_fix = 1;
+ battle_config.gx_allhit = 1;
+ battle_config.gx_disptype = 1;
+ battle_config.devotion_level_difference = 10;
+ battle_config.player_skill_partner_check = 1;
+ battle_config.hide_GM_session = 0;
+ battle_config.invite_request_check = 1;
+ battle_config.skill_removetrap_type = 0;
+ battle_config.disp_experience = 0;
+ battle_config.disp_zeny = 0;
+ battle_config.castle_defense_rate = 100;
+ battle_config.hp_rate = 100;
+ battle_config.sp_rate = 100;
+ battle_config.gm_cant_drop_min_lv = 1;
+ battle_config.gm_cant_drop_max_lv = 0;
+ battle_config.disp_hpmeter = 60;
+ battle_config.skill_wall_check = 0;
+ battle_config.cell_stack_limit = 1;
+ battle_config.bone_drop = 0;
+ battle_config.buyer_name = 1;
+
+// eAthena additions
+ battle_config.item_rate_mvp=100;
+ battle_config.item_rate_common = 100;
+ battle_config.item_rate_equip = 100;
+ battle_config.item_rate_card = 100;
+ battle_config.item_rate_heal = 100; // Added by Valaris
+ battle_config.item_rate_use = 100; // End
+ battle_config.item_rate_treasure = 100;
+ battle_config.logarithmic_drops = 0;
+ battle_config.item_drop_common_min=1; // Added by TyrNemesis^
+ battle_config.item_drop_common_max=10000;
+ battle_config.item_drop_equip_min=1;
+ battle_config.item_drop_equip_max=10000;
+ battle_config.item_drop_card_min=1;
+ battle_config.item_drop_card_max=10000;
+ battle_config.item_drop_mvp_min=1;
+ battle_config.item_drop_mvp_max=10000; // End Addition
+ battle_config.item_drop_heal_min=1; // Added by Valaris
+ battle_config.item_drop_heal_max=10000;
+ battle_config.item_drop_use_min=1;
+ battle_config.item_drop_use_max=10000; // End
+ battle_config.item_drop_treasure_min=1;
+ battle_config.item_drop_treasure_max=10000;
+ battle_config.prevent_logout = 10000; // Added by RoVeRT
+ battle_config.max_base_level = 255; // Added by Valaris
+ battle_config.max_job_level = 50;
+ battle_config.max_adv_level = 70;
+ battle_config.max_sn_level = 99;
+ battle_config.drops_by_luk = 0; // [Valaris]
+ battle_config.drops_by_luk2 = 0;
+ battle_config.equip_natural_break_rate = 1;
+ battle_config.equip_self_break_rate = 100; // [Valaris], adapted by [Skotlex]
+ battle_config.equip_skill_break_rate = 100; // [Valaris], adapted by [Skotlex]
+ battle_config.pk_mode = 0; // [Valaris]
+ battle_config.manner_system = 1; // [Valaris]
+ battle_config.pet_equip_required = 0; // [Valaris]
+ battle_config.multi_level_up = 0; // [Valaris]
+ battle_config.backstab_bow_penalty = 0; // Akaru
+ battle_config.night_at_start = 0; // added by [Yor]
+ battle_config.day_duration = 2*60*60*1000; // added by [Yor] (2 hours)
+ battle_config.night_duration = 30*60*1000; // added by [Yor] (30 minutes)
+ battle_config.show_mob_hp = 0; // [Valaris]
+ battle_config.ban_spoof_namer = 5; // added by [Yor] (default: 5 minutes)
+ battle_config.hack_info_GM_level = 60; // added by [Yor] (default: 60, GM level)
+ battle_config.any_warp_GM_min_level = 20; // added by [Yor]
+ battle_config.packet_ver_flag = 1023; // added by [Yor]
+ battle_config.min_hair_style = 0;
+ battle_config.max_hair_style = 23;
+ battle_config.min_hair_color = 0;
+ battle_config.max_hair_color = 9;
+ battle_config.min_cloth_color = 0;
+ battle_config.max_cloth_color = 4;
+ battle_config.pet_hair_style = 100;
+ battle_config.zeny_from_mobs = 0;
+ battle_config.mobs_level_up = 0; // [Valaris]
+ battle_config.mobs_level_up_exp_rate = 1; // [Valaris]
+ battle_config.pk_min_level = 55;
+ battle_config.skill_steal_type = 1;
+ battle_config.skill_steal_rate = 100;
+// battle_config.night_darkness_level = 9;
+ battle_config.motd_type = 0;
+ battle_config.allow_atcommand_when_mute = 0;
+ battle_config.finding_ore_rate = 100;
+ battle_config.castrate_dex_scale = 150;
+ battle_config.area_size = 14;
+ battle_config.exp_calc_type = 1;
+ battle_config.min_skill_delay_limit = 100;
+ battle_config.require_glory_guild = 0;
+ battle_config.idle_no_share = 0;
+ battle_config.party_even_share_bonus = 0;
+ battle_config.delay_battle_damage = 1;
+ battle_config.display_version = 1;
+ battle_config.who_display_aid = 0;
+ battle_config.display_hallucination = 1;
+ battle_config.ignore_items_gender = 1;
+ battle_config.use_statpoint_table = 1;
+ battle_config.mob_ai = 0;
+ battle_config.dynamic_mobs = 1; // use Dynamic Mobs [Wizputer]
+ battle_config.mob_remove_damaged = 1; // Dynamic Mobs - Remove mobs even if damaged [Wizputer]
+ battle_config.mob_remove_delay = 60000;
+ battle_config.show_hp_sp_drain = 0; //Display drained hp/sp from attacks
+ battle_config.show_hp_sp_gain = 1; //Display gained hp/sp from mob-kills
+ battle_config.mob_npc_event_type = 1; //Execute npc-event on player that delivered final blow.
+ battle_config.mob_clear_delay = 0;
+ battle_config.character_size = 3; //3: Peco riders Size=2, Baby Class Riders Size=1 [Lupus]
+ battle_config.mob_max_skilllvl = MAX_SKILL_LEVEL; //max possible level of monsters skills [Lupus]
+ battle_config.retaliate_to_master = 1; //Make mobs retaliate against the master rather than the mob that attacked them. [Skotlex]
+ battle_config.rare_drop_announce = 1; //show global announces for rare items drops (<= 0.01% chance) [Lupus]
+ battle_config.firewall_hits_on_undead = 1;
+ battle_config.title_lvl1 = 1; //Players Titles for @who, etc commands [Lupus]
+ battle_config.title_lvl2 = 10;
+ battle_config.title_lvl3 = 20;
+ battle_config.title_lvl4 = 40;
+ battle_config.title_lvl5 = 50;
+ battle_config.title_lvl6 = 60;
+ battle_config.title_lvl7 = 80;
+ battle_config.title_lvl8 = 99;
+
+ battle_config.duel_enable = 1;
+ battle_config.duel_allow_pvp = 0;
+ battle_config.duel_allow_pvp = 0;
+ battle_config.duel_allow_teleport = 0;
+ battle_config.duel_autoleave_when_die = 1;
+ battle_config.duel_time_interval = 60;
+
+ battle_config.skip_teleport_lv1_menu = 0;
+ battle_config.allow_skill_without_day = 0;
+}
+
+void battle_validate_conf() {
+ if(battle_config.flooritem_lifetime < 1000)
+ battle_config.flooritem_lifetime = LIFETIME_FLOORITEM*1000;
+/* if(battle_config.restart_hp_rate < 0)
+ battle_config.restart_hp_rate = 0;
+ else*/ if(battle_config.restart_hp_rate > 100)
+ battle_config.restart_hp_rate = 100;
+/* if(battle_config.restart_sp_rate < 0)
+ battle_config.restart_sp_rate = 0;
+ else*/ if(battle_config.restart_sp_rate > 100)
+ battle_config.restart_sp_rate = 100;
+ if(battle_config.natural_healhp_interval < NATURAL_HEAL_INTERVAL)
+ battle_config.natural_healhp_interval=NATURAL_HEAL_INTERVAL;
+ if(battle_config.natural_healsp_interval < NATURAL_HEAL_INTERVAL)
+ battle_config.natural_healsp_interval=NATURAL_HEAL_INTERVAL;
+ if(battle_config.natural_heal_skill_interval < NATURAL_HEAL_INTERVAL)
+ battle_config.natural_heal_skill_interval=NATURAL_HEAL_INTERVAL;
+ if(battle_config.natural_heal_weight_rate < 50)
+ battle_config.natural_heal_weight_rate = 50;
+ if(battle_config.natural_heal_weight_rate > 101)
+ battle_config.natural_heal_weight_rate = 101;
+ battle_config.monster_max_aspd = 2000 - battle_config.monster_max_aspd*10;
+ if(battle_config.monster_max_aspd < 10)
+ battle_config.monster_max_aspd = 10;
+ if(battle_config.monster_max_aspd > 1000)
+ battle_config.monster_max_aspd = 1000;
+ battle_config.max_aspd = 2000 - battle_config.max_aspd*10;
+ if(battle_config.max_aspd < 10)
+ battle_config.max_aspd = 10;
+ if(battle_config.max_aspd > 1000)
+ battle_config.max_aspd = 1000;
+
+ if (battle_config.max_walk_speed < 100)
+ battle_config.max_walk_speed = 100;
+ battle_config.max_walk_speed = 100*DEFAULT_WALK_SPEED/battle_config.max_walk_speed;
+ if (battle_config.max_walk_speed < 1)
+ battle_config.max_walk_speed = 1;
+
+ if(battle_config.hp_rate < 1)
+ battle_config.hp_rate = 1;
+ if(battle_config.sp_rate < 1)
+ battle_config.sp_rate = 1;
+ if(battle_config.max_hp > 1000000000)
+ battle_config.max_hp = 1000000000;
+ if(battle_config.max_hp < 100)
+ battle_config.max_hp = 100;
+ if(battle_config.max_sp > 1000000000)
+ battle_config.max_sp = 1000000000;
+ if(battle_config.max_sp < 100)
+ battle_config.max_sp = 100;
+ if(battle_config.max_parameter < 10)
+ battle_config.max_parameter = 10;
+ if(battle_config.max_parameter > 10000)
+ battle_config.max_parameter = 10000;
+ if(battle_config.max_baby_parameter < 10)
+ battle_config.max_baby_parameter = 10;
+ if(battle_config.max_baby_parameter > 10000)
+ battle_config.max_baby_parameter = 10000;
+ if(battle_config.max_cart_weight > 1000000)
+ battle_config.max_cart_weight = 1000000;
+ if(battle_config.max_cart_weight < 100)
+ battle_config.max_cart_weight = 100;
+ battle_config.max_cart_weight *= 10;
+
+ if(battle_config.max_def > 100 && !battle_config.player_defense_type) // added by [Skotlex]
+ battle_config.max_def = 100;
+ if(battle_config.over_def_bonus > 1000)
+ battle_config.over_def_bonus = 1000;
+
+ if(battle_config.min_hitrate > battle_config.max_hitrate)
+ battle_config.min_hitrate = battle_config.max_hitrate;
+
+ if(battle_config.agi_penalty_count < 2)
+ battle_config.agi_penalty_count = 2;
+ if(battle_config.vit_penalty_count < 2)
+ battle_config.vit_penalty_count = 2;
+
+ if(battle_config.guild_exp_limit > 99)
+ battle_config.guild_exp_limit = 99;
+/* if(battle_config.guild_exp_limit < 0)
+ battle_config.guild_exp_limit = 0;*/
+
+ if(battle_config.pet_support_min_friendly > 950) //Capped to 950/1000 [Skotlex]
+ battle_config.pet_support_min_friendly = 950;
+
+ if(battle_config.pet_max_atk1 > battle_config.pet_max_atk2) //Skotlex
+ battle_config.pet_max_atk1 = battle_config.pet_max_atk2;
+
+// if(battle_config.castle_defense_rate < 0)
+// battle_config.castle_defense_rate = 0;
+ if(battle_config.castle_defense_rate > 100)
+ battle_config.castle_defense_rate = 100;
+ if(battle_config.item_drop_common_min < 1) // Added by TyrNemesis^
+ battle_config.item_drop_common_min = 1;
+ if(battle_config.item_drop_common_max > 10000)
+ battle_config.item_drop_common_max = 10000;
+ if(battle_config.item_drop_equip_min < 1)
+ battle_config.item_drop_equip_min = 1;
+ if(battle_config.item_drop_equip_max > 10000)
+ battle_config.item_drop_equip_max = 10000;
+ if(battle_config.item_drop_card_min < 1)
+ battle_config.item_drop_card_min = 1;
+ if(battle_config.item_drop_card_max > 10000)
+ battle_config.item_drop_card_max = 10000;
+ if(battle_config.item_drop_mvp_min < 1)
+ battle_config.item_drop_mvp_min = 1;
+ if(battle_config.item_drop_mvp_max > 10000)
+ battle_config.item_drop_mvp_max = 10000; // End Addition
+
+/* if (battle_config.night_at_start < 0) // added by [Yor]
+ battle_config.night_at_start = 0;
+ else if (battle_config.night_at_start > 1) // added by [Yor]
+ battle_config.night_at_start = 1; */
+ if (battle_config.day_duration != 0 && battle_config.day_duration < 60000) // added by [Yor]
+ battle_config.day_duration = 60000;
+ if (battle_config.night_duration != 0 && battle_config.night_duration < 60000) // added by [Yor]
+ battle_config.night_duration = 60000;
+
+/* if (battle_config.ban_spoof_namer < 0) // added by [Yor]
+ battle_config.ban_spoof_namer = 0;
+ else*/ if (battle_config.ban_spoof_namer > 32767)
+ battle_config.ban_spoof_namer = 32767;
+
+/* if (battle_config.hack_info_GM_level < 0) // added by [Yor]
+ battle_config.hack_info_GM_level = 0;
+ else*/ if (battle_config.hack_info_GM_level > 100)
+ battle_config.hack_info_GM_level = 100;
+
+/* if (battle_config.any_warp_GM_min_level < 0) // added by [Yor]
+ battle_config.any_warp_GM_min_level = 0;
+ else*/ if (battle_config.any_warp_GM_min_level > 100)
+ battle_config.any_warp_GM_min_level = 100;
+
+/* //This is a hassle to keep updated each time there's a new limit to packet_ver_flag.... [Skotlex]
+ // at least 1 client must be accepted
+ if ((battle_config.packet_ver_flag & 255) == 0) // added by [Yor]
+ battle_config.packet_ver_flag = 255; // accept all clients
+*/
+/* Deprecated by dynamix's new night system (using SI_NIGHT)
+ if (battle_config.night_darkness_level <= 0)
+ battle_config.night_darkness_level = 9;
+ else if (battle_config.night_darkness_level > 10) // Celest
+ battle_config.night_darkness_level = 10;
+*/
+/* if (battle_config.motd_type < 0)
+ battle_config.motd_type = 0;
+ else if (battle_config.motd_type > 1)
+ battle_config.motd_type = 1;
+*/
+// if (battle_config.finding_ore_rate < 0)
+// battle_config.finding_ore_rate = 0;
+
+ if (battle_config.vending_max_value > MAX_ZENY || battle_config.vending_max_value==0)
+ battle_config.vending_max_value = MAX_ZENY;
+
+ if (battle_config.min_skill_delay_limit < 10)
+ battle_config.min_skill_delay_limit = 10; // minimum delay of 10ms
+
+ //Spawn delays [Skotlex]
+/* if (battle_config.mob_spawn_delay < 0)
+ battle_config.mob_spawn_delay = 0;
+ if (battle_config.boss_spawn_delay < 0)
+ battle_config.boss_spawn_delay = 0;
+ if (battle_config.plant_spawn_delay < 0)
+ battle_config.plant_spawn_delay = 0;
+*/
+ if (battle_config.no_spawn_on_player > 50)
+ battle_config.no_spawn_on_player = 50;
+ if (battle_config.mob_remove_delay < 15000) //Min 15 sec
+ battle_config.mob_remove_delay = 15000;
+ if (battle_config.dynamic_mobs > 1)
+ battle_config.dynamic_mobs = 1; //The flag will be used in assignations
+ if (battle_config.mob_max_skilllvl> 11 || battle_config.mob_max_skilllvl<1 )
+ battle_config.mob_max_skilllvl = 11;
+
+ if (battle_config.firewall_hits_on_undead < 1)
+ battle_config.firewall_hits_on_undead = 1;
+ else if (battle_config.firewall_hits_on_undead > 255) //The flag passed to battle_calc_damage is limited to 0xff
+ battle_config.firewall_hits_on_undead = 255;
+
+ if (battle_config.prevent_logout > 60000)
+ battle_config.prevent_logout = 60000;
+
+ if (battle_config.mobs_level_up_exp_rate < 1) // [Valaris]
+ battle_config.mobs_level_up_exp_rate = 1;
+
+#ifdef CELL_NOSTACK
+ if (battle_config.cell_stack_limit < 1)
+ battle_config.cell_stack_limit = 1;
+ else
+ if (battle_config.cell_stack_limit > 255)
+ battle_config.cell_stack_limit = 255;
+#else
+ if (battle_config.cell_stack_limit != 1)
+ ShowWarning("Battle setting 'cell_stack_limit' takes no effect as this server was compiled without Cell Stack Limit support.\n");
+#endif
+}
+
+/*==========================================
+ * ?ン定ファイルを読み?桙゙
+ *------------------------------------------
+ */
+int battle_config_read(const char *cfgName)
+{
+ char line[1024], w1[1024], w2[1024];
+ FILE *fp;
+ static int count = 0;
+
+ if ((count++) == 0)
+ battle_set_defaults();
+
+ fp = fopen(cfgName,"r");
+ if (fp == NULL) {
+ ShowError("File not found: %s\n", cfgName);
+ return 1;
+ }
+ while(fgets(line,1020,fp)){
+ if (line[0] == '/' && line[1] == '/')
+ continue;
+ if (sscanf(line, "%[^:]:%s", w1, w2) != 2)
+ continue;
+ if (strcmpi(w1, "import") == 0)
+ battle_config_read(w2);
+ else
+ battle_set_value(w1, w2);
+ }
+ fclose(fp);
+
+ if (--count == 0) {
+ battle_validate_conf();
+ add_timer_func_list(battle_delay_damage_sub, "battle_delay_damage_sub");
+ add_timer_func_list(battle_walkdelay_sub, "battle_walkdelay_sub");
+ }
+
+ return 0;
+}
diff --git a/src/map/battle.h b/src/map/battle.h
new file mode 100644
index 000000000..175aa49c4
--- /dev/null
+++ b/src/map/battle.h
@@ -0,0 +1,428 @@
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#ifndef _BATTLE_H_
+#define _BATTLE_H_
+
+// ダメージ
+struct Damage {
+ int damage,damage2;
+ int type,div_;
+ int amotion,dmotion;
+ int blewcount;
+ int flag;
+ int dmg_lv; //囲まれ減算計算用 0:スキル攻撃 ATK_LUCKY,ATK_FLEE,ATK_DEF
+};
+
+// 属性表(読み込みはpc.c、battle_attr_fixで使用)
+extern int attr_fix_table[4][10][10];
+
+struct map_session_data;
+struct mob_data;
+struct block_list;
+
+// ダメージ計算
+
+struct Damage battle_calc_attack(int attack_type,struct block_list *bl,struct block_list *target,int skill_num,int skill_lv,int flag);
+
+int battle_attr_fix(struct block_list *src, struct block_list *target, int damage,int atk_elem,int def_elem);
+
+// ダメージ最終計算
+int battle_calc_damage(struct block_list *src,struct block_list *bl,int damage,int div_,int skill_num,int skill_lv,int flag);
+enum { // 最終計算のフラグ
+ BF_WEAPON = 0x0001,
+ BF_MAGIC = 0x0002,
+ BF_MISC = 0x0004,
+ BF_SHORT = 0x0010,
+ BF_LONG = 0x0040,
+ BF_SKILL = 0x0100,
+ BF_NORMAL = 0x0200,
+ BF_WEAPONMASK=0x000f,
+ BF_RANGEMASK= 0x00f0,
+ BF_SKILLMASK= 0x0f00,
+};
+
+// 実際にHPを増減
+int battle_walkdelay(struct block_list *bl, unsigned int tick, int adelay, int delay, int div_); //Calcs walk delay based on attack type. [Skotlex]
+int battle_delay_damage (unsigned int tick, struct block_list *src, struct block_list *target, int attack_type, int skill_id, int skill_lv, int damage, int dmg_lv, int flag);
+int battle_damage(struct block_list *bl,struct block_list *target,int damage,int flag);
+int battle_heal(struct block_list *bl,struct block_list *target,int hp,int sp,int flag);
+
+// 攻撃や移動を止める
+int battle_stopattack(struct block_list *bl);
+int battle_iswalking(struct block_list *bl);
+int battle_stopwalking(struct block_list *bl,int type);
+
+// 通常攻撃処理まとめ
+int battle_weapon_attack( struct block_list *bl,struct block_list *target,
+ unsigned int tick,int flag);
+
+// 各種パラメータを得る
+int battle_counttargeted(struct block_list *bl,struct block_list *src,int target_lv);
+struct block_list* battle_gettargeted(struct block_list *target);
+int battle_gettarget(struct block_list *bl);
+int battle_getcurrentskill(struct block_list *bl);
+
+//New definitions [Skotlex]
+#define BCT_ENEMY 0x020000
+//This should be (~BCT_ENEMY&BCT_ALL)
+#define BCT_NOENEMY 0x1d0000
+#define BCT_PARTY 0x040000
+//This should be (~BCT_PARTY&BCT_ALL)
+#define BCT_NOPARTY 0x1b0000
+#define BCT_GUILD 0x080000
+//This should be (~BCT_GUILD&BCT_ALL)
+#define BCT_NOGUILD 0x170000
+#define BCT_ALL 0x1f0000
+#define BCT_NOONE 0x000000
+#define BCT_SELF 0x010000
+#define BCT_NEUTRAL 0x100000
+/*
+enum {
+ BCT_NOENEMY =0x00000,
+ BCT_PARTY =0x10000,
+ BCT_ENEMY =0x40000,
+ BCT_NOPARTY =0x50000,
+ BCT_ALL =0x20000,
+ BCT_NOONE =0x60000,
+ BCT_SELF =0x60000,
+};
+*/
+int battle_check_undead(int race,int element);
+int battle_check_target(struct block_list *src, struct block_list *target,int flag);
+int battle_check_range(struct block_list *src,struct block_list *bl,int range);
+
+// 設定
+
+int battle_config_switch(const char *str); // [Valaris]
+
+extern struct Battle_Config {
+ unsigned short warp_point_debug;
+ unsigned short enemy_critical_rate;
+ unsigned short enemy_str;
+ unsigned short enemy_perfect_flee;
+ unsigned short cast_rate,delay_rate,delay_dependon_dex;
+ unsigned short sdelay_attack_enable;
+ unsigned short left_cardfix_to_right;
+ unsigned short pc_skill_add_range;
+ unsigned short skill_out_range_consume;
+ unsigned short mob_skill_add_range;
+ unsigned short skillrange_by_distance; //[Skotlex]
+ unsigned short use_weapon_skill_range; //[Skotlex]
+ unsigned short pc_damage_delay_rate;
+ unsigned short defnotenemy;
+ unsigned short vs_traps_bctall;
+ unsigned short random_monster_checklv;
+ unsigned short attr_recover;
+ unsigned short flooritem_lifetime;
+ unsigned short item_auto_get;
+ int item_first_get_time;
+ int item_second_get_time;
+ int item_third_get_time;
+ int mvp_item_first_get_time;
+ int mvp_item_second_get_time;
+ int mvp_item_third_get_time;
+ int base_exp_rate,job_exp_rate;
+ unsigned short drop_rate0item;
+ unsigned short death_penalty_type;
+ unsigned short death_penalty_base,death_penalty_job;
+ unsigned short pvp_exp; // [MouseJstr]
+ unsigned short gtb_pvp_only; // [MouseJstr]
+ int zeny_penalty;
+ unsigned short restart_hp_rate;
+ unsigned short restart_sp_rate;
+ int mvp_exp_rate;
+ unsigned short mvp_hp_rate;
+ unsigned short monster_hp_rate;
+ unsigned short monster_max_aspd;
+ unsigned short atc_gmonly;
+ unsigned short atc_spawn_quantity_limit;
+ unsigned short atc_slave_clone_limit;
+ unsigned short gm_allskill;
+ unsigned short gm_allskill_addabra;
+ unsigned short gm_allequip;
+ unsigned short gm_skilluncond;
+ unsigned short gm_join_chat;
+ unsigned short gm_kick_chat;
+ unsigned short skillfree;
+ unsigned short skillup_limit;
+ unsigned short wp_rate;
+ unsigned short pp_rate;
+ unsigned short monster_active_enable;
+ unsigned short monster_damage_delay_rate;
+ unsigned short monster_loot_type;
+ unsigned short mob_skill_rate; //[Skotlex]
+ unsigned short mob_skill_delay; //[Skotlex]
+ unsigned short mob_count_rate;
+ unsigned short no_spawn_on_player; //[Skotlex]
+ unsigned short mob_spawn_delay, plant_spawn_delay, boss_spawn_delay; // [Skotlex]
+ unsigned short slaves_inherit_speed;
+ unsigned short summons_inherit_effects;
+ unsigned short pc_walk_delay_rate; //Adjusts can't walk delay after being hit for players. [Skotlex]
+ unsigned short walk_delay_rate; //Adjusts can't walk delay after being hit. [Skotlex]
+ unsigned short multihit_delay; //Adjusts can't walk delay per hit on multi-hitting skills. [Skotlex]
+ unsigned short quest_skill_learn;
+ unsigned short quest_skill_reset;
+ unsigned short basic_skill_check;
+ unsigned short guild_emperium_check;
+ unsigned short guild_exp_rate; //[Skotlex]
+ unsigned short guild_exp_limit;
+ unsigned short guild_max_castles;
+ unsigned short pc_invincible_time;
+ unsigned short pet_catch_rate;
+ unsigned short pet_rename;
+ unsigned short pet_friendly_rate;
+ unsigned short pet_hungry_delay_rate;
+ unsigned short pet_hungry_friendly_decrease;
+ unsigned short pet_str;
+ unsigned short pet_status_support;
+ unsigned short pet_attack_support;
+ unsigned short pet_damage_support;
+ unsigned short pet_support_min_friendly; //[Skotlex]
+ unsigned short pet_support_rate;
+ unsigned short pet_attack_exp_to_master;
+ unsigned short pet_attack_exp_rate;
+ unsigned short pet_lv_rate; //[Skotlex]
+ unsigned short pet_max_stats; //[Skotlex]
+ unsigned short pet_max_atk1; //[Skotlex]
+ unsigned short pet_max_atk2; //[Skotlex]
+ unsigned short pet_no_gvg; //Disables pets in gvg. [Skotlex]
+ unsigned short skill_min_damage;
+ unsigned short finger_offensive_type;
+ unsigned short heal_exp;
+ unsigned short resurrection_exp;
+ unsigned short shop_exp;
+ unsigned short combo_delay_rate;
+ unsigned short item_check;
+ unsigned short item_use_interval; //[Skotlex]
+ unsigned short wedding_modifydisplay;
+ unsigned short wedding_ignorepalette; //[Skotlex]
+ unsigned short xmas_ignorepalette; // [Valaris]
+ int natural_healhp_interval;
+ int natural_healsp_interval;
+ int natural_heal_skill_interval;
+ unsigned short natural_heal_weight_rate;
+ unsigned short item_name_override_grffile;
+ unsigned short indoors_override_grffile; // [Celest]
+ unsigned short skill_sp_override_grffile; // [Celest]
+ unsigned short cardillust_read_grffile;
+ unsigned short item_equip_override_grffile;
+ unsigned short item_slots_override_grffile;
+ unsigned short arrow_decrement;
+ unsigned short max_aspd;
+ unsigned short max_walk_speed; //Maximum walking speed after buffs [Skotlex]
+ int max_hp;
+ int max_sp;
+ unsigned short max_lv, aura_lv;
+ unsigned short max_parameter, max_baby_parameter;
+ int max_cart_weight;
+ unsigned short pc_skill_log;
+ unsigned short mob_skill_log;
+ unsigned short battle_log;
+ unsigned short save_log;
+ unsigned short error_log;
+ unsigned short etc_log;
+ unsigned short save_clothcolor;
+ unsigned short undead_detect_type;
+ unsigned short pc_auto_counter_type;
+ unsigned short monster_auto_counter_type;
+ unsigned short min_hitrate; //[Skotlex]
+ unsigned short max_hitrate; //[Skotlex]
+ unsigned short agi_penalty_type;
+ unsigned short agi_penalty_count;
+ unsigned short agi_penalty_num;
+ unsigned short vit_penalty_type;
+ unsigned short vit_penalty_count;
+ unsigned short vit_penalty_num;
+ unsigned short player_defense_type;
+ unsigned short monster_defense_type;
+ unsigned short pet_defense_type;
+ unsigned short magic_defense_type;
+ unsigned short pc_skill_reiteration;
+ unsigned short monster_skill_reiteration;
+ unsigned short pc_skill_nofootset;
+ unsigned short monster_skill_nofootset;
+ unsigned short pc_cloak_check_type;
+ unsigned short monster_cloak_check_type;
+ unsigned short estimation_type;
+ unsigned short gvg_short_damage_rate;
+ unsigned short gvg_long_damage_rate;
+ unsigned short gvg_weapon_damage_rate;
+ unsigned short gvg_magic_damage_rate;
+ unsigned short gvg_misc_damage_rate;
+ unsigned short gvg_flee_penalty;
+ int gvg_eliminate_time;
+ unsigned short mob_changetarget_byskill;
+ unsigned short pc_attack_direction_change;
+ unsigned short monster_attack_direction_change;
+ unsigned short pc_land_skill_limit;
+ unsigned short monster_land_skill_limit;
+ unsigned short party_skill_penalty;
+ unsigned short monster_class_change_full_recover;
+ unsigned short produce_item_name_input;
+ unsigned short produce_potion_name_input;
+ unsigned short making_arrow_name_input;
+ unsigned short holywater_name_input;
+ unsigned short cdp_name_input;
+ unsigned short display_delay_skill_fail;
+ unsigned short display_snatcher_skill_fail;
+ unsigned short chat_warpportal;
+ unsigned short mob_warpportal;
+ unsigned short dead_branch_active;
+ unsigned int vending_max_value;
+ unsigned short show_steal_in_same_party;
+ unsigned short party_share_type;
+ unsigned short party_show_share_picker;
+ unsigned short pet_attack_attr_none;
+ unsigned short mob_attack_attr_none;
+ unsigned short mob_ghostring_fix;
+ unsigned short pc_attack_attr_none;
+ int item_rate_mvp, item_rate_common,item_rate_card,item_rate_equip,
+ item_rate_heal, item_rate_use, item_rate_treasure; // Added by RoVeRT, Additional Heal and Usable item rate by Val
+
+ unsigned short logarithmic_drops;
+ unsigned short item_drop_common_min,item_drop_common_max; // Added by TyrNemesis^
+ unsigned short item_drop_card_min,item_drop_card_max;
+ unsigned short item_drop_equip_min,item_drop_equip_max;
+ unsigned short item_drop_mvp_min,item_drop_mvp_max; // End Addition
+ unsigned short item_drop_heal_min,item_drop_heal_max; // Added by Valatris
+ unsigned short item_drop_use_min,item_drop_use_max; //End
+ unsigned short item_drop_treasure_min,item_drop_treasure_max; //by [Skotlex]
+
+ unsigned short prevent_logout; // Added by RoVeRT
+
+ unsigned short alchemist_summon_reward; // [Valaris]
+ unsigned short max_base_level; //Max Base Level [Valaris]
+ unsigned short max_job_level; //Max job level (normal classes) [Skotlex]
+ unsigned short max_sn_level; //Max job level (super novice) [Skotlex]
+ unsigned short max_adv_level; //Max job level (advanced classes) [Skotlex]
+ unsigned short drops_by_luk;
+ unsigned short drops_by_luk2;
+ unsigned short equip_natural_break_rate; //Base Natural break rate for attacks.
+ unsigned short equip_self_break_rate; //Natural & Penalty skills break rate
+ unsigned short equip_skill_break_rate; //Offensive skills break rate
+ unsigned short pet_equip_required;
+ unsigned short multi_level_up;
+ unsigned short pk_mode;
+ unsigned short manner_system;
+ unsigned short show_mob_hp; // end additions [Valaris]
+
+ unsigned short agi_penalty_count_lv;
+ unsigned short vit_penalty_count_lv;
+
+ unsigned short gx_allhit;
+ unsigned short gx_disptype;
+ unsigned short devotion_level_difference;
+ unsigned short player_skill_partner_check;
+ unsigned short hide_GM_session;
+ unsigned short invite_request_check;
+ unsigned short skill_removetrap_type;
+ unsigned short disp_experience;
+ unsigned short disp_zeny;
+ unsigned short castle_defense_rate;
+ unsigned short backstab_bow_penalty;
+ unsigned short hp_rate;
+ unsigned short sp_rate;
+ unsigned short gm_cant_drop_min_lv;
+ unsigned short gm_cant_drop_max_lv;
+ unsigned short disp_hpmeter;
+ unsigned short bone_drop;
+ unsigned short buyer_name;
+
+// eAthena additions
+ unsigned short night_at_start; // added by [Yor]
+ int day_duration; // added by [Yor]
+ int night_duration; // added by [Yor]
+ unsigned short ban_spoof_namer; // added by [Yor]
+ short ban_hack_trade; // added by [Yor]
+ unsigned short hack_info_GM_level; // added by [Yor]
+ unsigned short any_warp_GM_min_level; // added by [Yor]
+ unsigned short packet_ver_flag; // added by [Yor]
+ unsigned short muting_players; // added by [PoW]
+
+ unsigned short min_hair_style; // added by [MouseJstr]
+ unsigned short max_hair_style; // added by [MouseJstr]
+ unsigned short min_hair_color; // added by [MouseJstr]
+ unsigned short max_hair_color; // added by [MouseJstr]
+ unsigned short min_cloth_color; // added by [MouseJstr]
+ unsigned short max_cloth_color; // added by [MouseJstr]
+ unsigned short pet_hair_style; // added by [Skotlex]
+
+ unsigned short castrate_dex_scale; // added by [MouseJstr]
+ unsigned short area_size; // added by [MouseJstr]
+
+ unsigned short max_def, over_def_bonus; //added by [Skotlex]
+
+ unsigned short zeny_from_mobs; // [Valaris]
+ unsigned short mobs_level_up; // [Valaris]
+ unsigned short mobs_level_up_exp_rate; // [Valaris]
+ unsigned short pk_min_level; // [celest]
+ unsigned short skill_steal_type; // [celest]
+ unsigned short skill_steal_rate; // [celest]
+// unsigned short night_darkness_level; // [celest]
+ unsigned short motd_type; // [celest]
+ unsigned short allow_atcommand_when_mute; // [celest]
+ unsigned short finding_ore_rate; // orn
+ unsigned short exp_calc_type;
+ unsigned short min_skill_delay_limit;
+ unsigned short require_glory_guild;
+ unsigned short idle_no_share;
+ unsigned short party_even_share_bonus;
+ unsigned short delay_battle_damage;
+ unsigned short display_version;
+ unsigned short who_display_aid;
+
+ unsigned short display_hallucination; // [Skotlex]
+ unsigned short use_statpoint_table; // [Skotlex]
+
+ unsigned short ignore_items_gender; //[Lupus]
+
+ unsigned short copyskill_restrict; // [Aru]
+ unsigned short berserk_cancels_buffs; // [Aru]
+
+ unsigned short mob_ai; //Configures various mob_ai settings to make them smarter or dumber(official). [Skotlex]
+ unsigned short dynamic_mobs; // Dynamic Mobs [Wizputer] - battle_athena flag implemented by [random]
+ unsigned short mob_remove_damaged; // Dynamic Mobs - Remove mobs even if damaged [Wizputer]
+ int mob_remove_delay; // Dynamic Mobs - delay before removing mobs from a map [Skotlex]
+
+ unsigned short show_hp_sp_drain, show_hp_sp_gain; //[Skotlex]
+
+ unsigned short mob_npc_event_type; //Determines on who the npc_event is executed. [Skotlex]
+ unsigned short mob_clear_delay; // [Valaris]
+
+ unsigned short character_size; // if riders have size=2, and baby class riders size=1 [Lupus]
+ unsigned short mob_max_skilllvl; // Max possible skill level [Lupus]
+ unsigned short rare_drop_announce; // chance <= to show rare drops global announces
+
+ unsigned short retaliate_to_master; //Whether when a mob is attacked by another mob, it will retaliate versus the mob or the mob's master. [Skotlex]
+ unsigned short firewall_hits_on_undead; //Number of hits firewall does at a time on undead. [Skotlex]
+
+ unsigned short title_lvl1; // Players titles [Lupus]
+ unsigned short title_lvl2; // Players titles [Lupus]
+ unsigned short title_lvl3; // Players titles [Lupus]
+ unsigned short title_lvl4; // Players titles [Lupus]
+ unsigned short title_lvl5; // Players titles [Lupus]
+ unsigned short title_lvl6; // Players titles [Lupus]
+ unsigned short title_lvl7; // Players titles [Lupus]
+ unsigned short title_lvl8; // Players titles [Lupus]
+
+ unsigned short duel_enable; // [LuzZza]
+ unsigned short duel_allow_pvp; // [LuzZza]
+ unsigned short duel_allow_gvg; // [LuzZza]
+ unsigned short duel_allow_teleport; // [LuzZza]
+ unsigned short duel_autoleave_when_die; // [LuzZza]
+ unsigned short duel_time_interval; // [LuzZza]
+
+ unsigned short skip_teleport_lv1_menu; // possibility to disable (skip) Teleport Lv1 menu, that have only two lines `Random` and `Cancel` [LuzZza]
+
+ unsigned short allow_skill_without_day; // [Komurka]
+ unsigned short skill_wall_check; // [Skotlex]
+ unsigned short cell_stack_limit; // [Skotlex]
+} battle_config;
+
+extern int battle_config_read(const char *cfgName);
+extern void battle_validate_conf(void);
+extern void battle_set_defaults(void);
+extern int battle_set_value(char *, char *);
+
+#endif
diff --git a/src/map/charcommand.c b/src/map/charcommand.c
new file mode 100644
index 000000000..e2c97b15f
--- /dev/null
+++ b/src/map/charcommand.c
@@ -0,0 +1,1794 @@
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <math.h>
+
+#include "../common/socket.h"
+#include "../common/timer.h"
+#include "../common/nullpo.h"
+
+#include "log.h"
+#include "clif.h"
+#include "chrif.h"
+#include "intif.h"
+#include "itemdb.h"
+#include "map.h"
+#include "pc.h"
+#include "status.h"
+#include "skill.h"
+#include "mob.h"
+#include "pet.h"
+#include "battle.h"
+#include "charcommand.h"
+#include "atcommand.h"
+#include "showmsg.h"
+
+static char command_symbol = '#';
+
+extern char *msg_table[1000]; // Server messages (0-499 reserved for GM commands, 500-999 reserved for others)
+
+#define CCMD_FUNC(x) int charcommand_ ## x (const int fd, struct map_session_data* sd, const char* command, const char* message)
+CCMD_FUNC(jobchange);
+CCMD_FUNC(petrename);
+CCMD_FUNC(petfriendly);
+CCMD_FUNC(stats);
+CCMD_FUNC(option);
+CCMD_FUNC(save);
+CCMD_FUNC(stats_all);
+CCMD_FUNC(reset);
+CCMD_FUNC(spiritball);
+CCMD_FUNC(itemlist);
+CCMD_FUNC(effect);
+CCMD_FUNC(storagelist);
+CCMD_FUNC(item);
+CCMD_FUNC(warp);
+CCMD_FUNC(zeny);
+CCMD_FUNC(fakename);
+CCMD_FUNC(baselevel);
+CCMD_FUNC(joblevel);
+CCMD_FUNC(questskill);
+CCMD_FUNC(lostskill);
+CCMD_FUNC(skreset);
+CCMD_FUNC(streset);
+CCMD_FUNC(model);
+CCMD_FUNC(stpoint);
+CCMD_FUNC(skpoint);
+CCMD_FUNC(changesex);
+CCMD_FUNC(feelreset);
+CCMD_FUNC(help);
+
+
+/*==========================================
+ *CharCommandInfo charcommand_info[]構造体の定義
+ *------------------------------------------
+ */
+
+// First char of commands is configured in charcommand_athena.conf. Leave # in this list for default value.
+// to set default level, read charcommand_athena.conf first please.
+static CharCommandInfo charcommand_info[] = {
+ { CharCommandJobChange, "#job", 60, charcommand_jobchange },
+ { CharCommandJobChange, "#jobchange", 60, charcommand_jobchange },
+ { CharCommandPetRename, "#petrename", 50, charcommand_petrename },
+ { CharCommandPetFriendly, "#petfriendly", 50, charcommand_petfriendly },
+ { CharCommandStats, "#stats", 40, charcommand_stats },
+ { CharCommandOption, "#option", 60, charcommand_option },
+ { CharCommandReset, "#reset", 60, charcommand_reset },
+ { CharCommandSave, "#save", 60, charcommand_save },
+ { CharCommandStatsAll, "#statsall", 40, charcommand_stats_all },
+ { CharCommandSpiritball, "#spiritball", 40, charcommand_spiritball },
+ { CharCommandItemList, "#itemlist", 40, charcommand_itemlist },
+ { CharCommandEffect, "#effect", 40, charcommand_effect },
+ { CharCommandStorageList, "#storagelist", 40, charcommand_storagelist },
+ { CharCommandItem, "#item", 60, charcommand_item },
+ { CharCommandWarp, "#warp", 60, charcommand_warp },
+ { CharCommandWarp, "#rura", 60, charcommand_warp },
+ { CharCommandWarp, "#rura+", 60, charcommand_warp },
+ { CharCommandZeny, "#zeny", 60, charcommand_zeny },
+ { CharCommandFakeName, "#fakename", 20, charcommand_fakename},
+
+ //*********************************Recently added commands*********************************************
+ { CharCommandBaseLevel, "#baselvl", 20, charcommand_baselevel},
+ { CharCommandBaseLevel, "#blvl", 60, charcommand_baselevel},
+ { CharCommandBaseLevel, "#baselvlup", 60, charcommand_baselevel},
+ { CharCommandJobLevel, "#joblvl", 60, charcommand_joblevel},
+ { CharCommandJobLevel, "#jlvl", 60, charcommand_joblevel},
+ { CharCommandJobLevel, "#joblvlup", 60, charcommand_joblevel},
+ { CharCommandQuestSkill, "#questskill", 60, charcommand_questskill },
+ { CharCommandLostSkill, "#lostskill", 60, charcommand_lostskill },
+ { CharCommandSkReset, "#skreset", 60, charcommand_skreset },
+ { CharCommandStReset, "#streset", 60, charcommand_streset },
+ { CharCommandModel, "#model", 50, charcommand_model },
+ { CharCommandSKPoint, "#skpoint", 60, charcommand_skpoint },
+ { CharCommandSTPoint, "#stpoint", 60, charcommand_stpoint },
+ { CharCommandChangeSex, "#changesex", 60, charcommand_changesex },
+ { CharCommandFeelReset, "#feelreset", 60, charcommand_feelreset },
+ { CharCommandHelp, "#help", 20, charcommand_help },
+// add new commands before this line
+ { CharCommand_Unknown, NULL, 1, NULL }
+};
+
+int get_charcommand_level(const CharCommandType type) {
+ int i;
+
+ for (i = 0; charcommand_info[i].type != CharCommand_None; i++)
+ if (charcommand_info[i].type == type)
+ return charcommand_info[i].level;
+
+ return 100; // 100: command can not be used
+}
+
+/*==========================================
+ *is_charcommand @コマンドに存在するかどうか確認する
+ *------------------------------------------
+ */
+CharCommandType
+is_charcommand(const int fd, struct map_session_data* sd, const char* message, int gmlvl) {
+ const char* str = message;
+ int s_flag = 0;
+ CharCommandInfo info;
+ CharCommandType type;
+
+ nullpo_retr(CharCommand_None, sd);
+
+ if (!message || !*message)
+ return CharCommand_None;
+
+ memset(&info, 0, sizeof(info));
+ str += strlen(sd->status.name);
+ while (*str && (isspace(*str) || (s_flag == 0 && *str == ':'))) {
+ if (*str == ':')
+ s_flag = 1;
+ str++;
+ }
+ if (!*str)
+ return CharCommand_None;
+
+ type = charcommand(sd, gmlvl > 0 ? gmlvl : pc_isGM(sd), str, &info);
+ if (type != CharCommand_None) {
+ char command[100];
+ char output[200];
+ const char* p = str;
+ memset(command, '\0', sizeof(command));
+ memset(output, '\0', sizeof(output));
+ while (*p && !isspace(*p))
+ p++;
+ if (p - str >= sizeof(command)) // too long
+ return CharCommand_Unknown;
+ strncpy(command, str, p - str);
+ while (isspace(*p))
+ p++;
+
+ if (type == CharCommand_Unknown || info.proc == NULL) {
+ snprintf(output, sizeof(output),msg_txt(153), command); // %s is Unknown Command.
+ clif_displaymessage(fd, output);
+ } else {
+ if (info.proc(fd, sd, command, p) != 0) {
+ // Command can not be executed
+ snprintf(output, sizeof(output), msg_txt(154), command); // %s failed.
+ clif_displaymessage(fd, output);
+ }
+ }
+
+ return info.type;
+ }
+
+ return CharCommand_None;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+CharCommandType charcommand(struct map_session_data* sd, const int level, const char* message, CharCommandInfo* info) {
+ char* p = (char *)message;
+
+ if (!info)
+ return CharCommand_None;
+ if (battle_config.atc_gmonly != 0 && !level) // level = pc_isGM(sd)
+ return CharCommand_None;
+ if (!p || !*p) {
+ ShowError("char command message is empty\n");
+ return CharCommand_None;
+ }
+
+ if (*p == command_symbol) { // check first char.
+ char command[101];
+ int i = 0;
+ memset(info, 0, sizeof(CharCommandInfo));
+ sscanf(p, "%100s", command);
+ command[sizeof(command)-1] = '\0';
+
+ while (charcommand_info[i].type != CharCommand_Unknown) {
+ if (strcmpi(command+1, charcommand_info[i].command+1) == 0 && level >= charcommand_info[i].level) {
+ p[0] = charcommand_info[i].command[0]; // set correct first symbol for after.
+ break;
+ }
+ i++;
+ }
+
+ if (charcommand_info[i].type == CharCommand_Unknown) {
+ // doesn't return Unknown if player is normal player (display the text, not display: unknown command)
+ if (level == 0)
+ return CharCommand_None;
+ else
+ return CharCommand_Unknown;
+ } else if((log_config.gm) && (charcommand_info[i].level >= log_config.gm)) {
+ log_atcommand(sd, message);
+ }
+ memcpy(info, &charcommand_info[i], sizeof charcommand_info[i]);
+ } else {
+ return CharCommand_None;
+ }
+
+ return info->type;
+}
+
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+static CharCommandInfo* get_charcommandinfo_byname(const char* name) {
+ int i;
+
+ for (i = 0; charcommand_info[i].type != CharCommand_Unknown; i++)
+ if (strcmpi(charcommand_info[i].command + 1, name) == 0)
+ return &charcommand_info[i];
+
+ return NULL;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int charcommand_config_read(const char *cfgName) {
+ char line[1024], w1[1024], w2[1024];
+ CharCommandInfo* p;
+ FILE* fp;
+
+ if ((fp = fopen(cfgName, "r")) == NULL) {
+ ShowError("CharCommands configuration file not found: %s\n", cfgName);
+ return 1;
+ }
+
+ while (fgets(line, sizeof(line)-1, fp)) {
+ if (line[0] == '/' && line[1] == '/')
+ continue;
+
+ if (sscanf(line, "%1023[^:]:%1023s", w1, w2) != 2)
+ continue;
+ p = get_charcommandinfo_byname(w1);
+ if (p != NULL) {
+ p->level = atoi(w2);
+ if (p->level > 100)
+ p->level = 100;
+ else if (p->level < 0)
+ p->level = 0;
+ }
+
+ if (strcmpi(w1, "import") == 0)
+ charcommand_config_read(w2);
+ else if (strcmpi(w1, "command_symbol") == 0 && w2[0] > 31 &&
+ w2[0] != '/' && // symbol of standard ragnarok GM commands
+ w2[0] != '%' && // symbol of party chat speaking
+ w2[0] != '$' && // symbol of guild chat speaking
+ w2[0] != '@') // symbol of atcommand
+ command_symbol = w2[0];
+ }
+ fclose(fp);
+
+ return 0;
+}
+
+/*==========================================
+ * 対象キャラクターを転職させる upper指定で転生や養子も可能
+ *------------------------------------------
+ */
+int charcommand_jobchange(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ char character[100];
+ struct map_session_data* pl_sd;
+ int job = 0, upper = -1;
+
+ memset(character, '\0', sizeof(character));
+
+ if (!message || !*message) {
+ clif_displaymessage(fd, "Please, enter a job and a player name (usage: #job/#jobchange <job ID> <char name>).");
+ return -1;
+ }
+
+ if (sscanf(message, "%d %d %99[^\n]", &job, &upper, character) < 3) { //upper指定してある
+ upper = -1;
+ if (sscanf(message, "%d %99[^\n]", &job, character) < 2) { //upper指定してない上に何か足りない
+ clif_displaymessage(fd, "Please, enter a job and a player name (usage: #job/#jobchange <job ID> <char name>).");
+ return -1;
+ }
+ }
+
+ if ((pl_sd = map_nick2sd(character)) != NULL) {
+ int j;
+ if (pc_isGM(sd) >= pc_isGM(pl_sd)) { // you can change job only to lower or same level
+ if ((job >= 0 && job < MAX_PC_CLASS)) {
+ for (j=0; j < MAX_INVENTORY; j++) {
+ if(pl_sd->status.inventory[j].nameid>0 && pl_sd->status.inventory[j].equip!=0)
+ pc_unequipitem(pl_sd, j, 3);
+ }
+ if (pc_jobchange(pl_sd, job, upper) == 0)
+ clif_displaymessage(fd, msg_table[48]); // Character's job changed.
+ else {
+ clif_displaymessage(fd, msg_table[192]); // Impossible to change the character's job.
+ return -1;
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[49]); // Invalid job ID.
+ return -1;
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[81]); // Your GM level don't authorise you to do this action on this player.
+ return -1;
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[3]); // Character not found.
+ return -1;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int charcommand_petrename(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ char character[NAME_LENGTH];
+ struct map_session_data *pl_sd;
+
+ memset(character, '\0', sizeof(character));
+
+ if (!message || !*message || sscanf(message, "%23[^\n]", character) < 1) {
+ clif_displaymessage(fd, "Please, enter a player name (usage: #petrename <char name>).");
+ return -1;
+ }
+
+ if ((pl_sd = map_nick2sd(character)) != NULL) {
+ if (pl_sd->status.pet_id > 0 && pl_sd->pd) {
+ if (pl_sd->pet.rename_flag != 0) {
+ pl_sd->pet.rename_flag = 0;
+ intif_save_petdata(pl_sd->status.account_id, &pl_sd->pet);
+ clif_send_petstatus(pl_sd);
+ clif_displaymessage(fd, msg_table[189]); // This player can now rename his/her pet.
+ } else {
+ clif_displaymessage(fd, msg_table[190]); // This player can already rename his/her pet.
+ return -1;
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[191]); // Sorry, but this player has no pet.
+ return -1;
+ }
+ } else {
+ clif_displaymessage(fd, msg_txt(3)); // Character not found.
+ return -1;
+ }
+
+ return 0;
+}
+
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int charcommand_petfriendly(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ int friendly = 0;
+ int t = 0;
+ char character[NAME_LENGTH];
+ struct map_session_data *pl_sd;
+
+ memset(character, '\0', sizeof(character));
+ if (!message || !*message || sscanf(message,"%d %23s",&friendly,character) < 2) {
+ clif_displaymessage(fd, "Please, enter a valid value (usage: "
+ "#petfriendly <0-1000> <player>).");
+ return -1;
+ }
+
+ if (((pl_sd = map_nick2sd(character)) != NULL) && pc_isGM(sd)>pc_isGM(pl_sd)) {
+ if (pl_sd->status.pet_id > 0 && pl_sd->pd) {
+ if (friendly >= 0 && friendly <= 1000) {
+ if (friendly != pl_sd->pet.intimate) {
+ t = pl_sd->pet.intimate;
+ pl_sd->pet.intimate = friendly;
+ clif_send_petstatus(pl_sd);
+ clif_pet_emotion(pl_sd->pd,0);
+ if (battle_config.pet_status_support) {
+ if ((pl_sd->pet.intimate > 0 && t <= 0) ||
+ (pl_sd->pet.intimate <= 0 && t > 0)) {
+ if (pl_sd->bl.prev != NULL)
+ status_calc_pc(pl_sd, 0);
+ else
+ status_calc_pc(pl_sd, 2);
+ }
+ }
+ clif_displaymessage(pl_sd->fd, msg_table[182]); // Pet friendly value changed!
+ clif_displaymessage(sd->fd, msg_table[182]); // Pet friendly value changed!
+ } else {
+ clif_displaymessage(fd, msg_table[183]); // Pet friendly is already the good value.
+ return -1;
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[37]); // An invalid number was specified.
+ return -1;
+ }
+ } else {
+ return -1;
+ }
+ } else {
+ clif_displaymessage(fd, msg_txt(3)); // Character not found.
+ return -1;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int charcommand_stats(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ char character[NAME_LENGTH];
+ char job_jobname[100];
+ char output[200];
+ struct map_session_data *pl_sd;
+ int i;
+
+ memset(character, '\0', sizeof(character));
+ memset(job_jobname, '\0', sizeof(job_jobname));
+ memset(output, '\0', sizeof(output));
+
+ if (!message || !*message || sscanf(message, "%23[^\n]", character) < 1) {
+ clif_displaymessage(fd, "Please, enter a player name (usage: #stats <char name>).");
+ return -1;
+ }
+
+ if ((pl_sd = map_nick2sd(character)) != NULL) {
+ struct {
+ const char* format;
+ int value;
+ } output_table[] = {
+ { "Base Level - %d", pl_sd->status.base_level },
+ { job_jobname, pl_sd->status.job_level },
+ { "Hp - %d", pl_sd->status.hp },
+ { "MaxHp - %d", pl_sd->status.max_hp },
+ { "Sp - %d", pl_sd->status.sp },
+ { "MaxSp - %d", pl_sd->status.max_sp },
+ { "Str - %3d", pl_sd->status.str },
+ { "Agi - %3d", pl_sd->status.agi },
+ { "Vit - %3d", pl_sd->status.vit },
+ { "Int - %3d", pl_sd->status.int_ },
+ { "Dex - %3d", pl_sd->status.dex },
+ { "Luk - %3d", pl_sd->status.luk },
+ { "Zeny - %d", pl_sd->status.zeny },
+ { NULL, 0 }
+ };
+ sprintf(job_jobname, "Job - %s %s", job_name(pl_sd->status.class_), "(level %d)");
+ sprintf(output, msg_table[53], pl_sd->status.name); // '%s' stats:
+ clif_displaymessage(fd, output);
+ for (i = 0; output_table[i].format != NULL; i++) {
+ sprintf(output, output_table[i].format, output_table[i].value);
+ clif_displaymessage(fd, output);
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[3]); // Character not found.
+ return -1;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * Character Reset
+ *------------------------------------------
+ */
+int charcommand_reset(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ char character[NAME_LENGTH];
+ char output[200];
+ struct map_session_data *pl_sd;
+
+ memset(character, '\0', sizeof(character));
+ memset(output, '\0', sizeof(output));
+
+ if (!message || !*message || sscanf(message, "%23[^\n]", character) < 1) {
+ clif_displaymessage(fd, "Please, enter a player name (usage: #reset <charname>).");
+ return -1;
+ }
+
+ if ((pl_sd = map_nick2sd(character)) != NULL) {
+ if (pc_isGM(sd) >= pc_isGM(pl_sd)) { // you can reset a character only for lower or same GM level
+ pc_resetstate(pl_sd);
+ pc_resetskill(pl_sd);
+ sprintf(output, msg_table[208], character); // '%s' skill and stats points reseted!
+ clif_displaymessage(fd, output);
+ } else {
+ clif_displaymessage(fd, msg_table[81]); // Your GM level don't authorise you to do this action on this player.
+ return -1;
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[3]); // Character not found.
+ return -1;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int charcommand_option(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ char character[NAME_LENGTH];
+ int opt1 = 0, opt2 = 0, opt3 = 0;
+ struct map_session_data* pl_sd;
+
+ memset(character, '\0', sizeof(character));
+
+ if (!message || !*message ||
+ sscanf(message, "%d %d %d %23[^\n]", &opt1, &opt2, &opt3, character) < 4 ||
+ opt1 < 0 || opt2 < 0 || opt3 < 0) {
+ clif_displaymessage(fd, "Please, enter valid options and a player name (usage: #option <param1> <param2> <param3> <charname>).");
+ return -1;
+ }
+
+ if ((pl_sd = map_nick2sd(character)) != NULL) {
+ if (pc_isGM(sd) >= pc_isGM(pl_sd)) { // you can change option only to lower or same level
+ pl_sd->opt1 = opt1;
+ pl_sd->opt2 = opt2;
+ pc_setoption(pl_sd, opt3);
+ clif_displaymessage(fd, msg_table[58]); // Character's options changed.
+ } else {
+ clif_displaymessage(fd, msg_table[81]); // Your GM level don't authorise you to do this action on this player.
+ return -1;
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[3]); // Character not found.
+ return -1;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int charcommand_save(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ char map_name[MAP_NAME_LENGTH];
+ char character[NAME_LENGTH];
+ struct map_session_data* pl_sd;
+ int x = 0, y = 0;
+ int m;
+
+ memset(map_name, '\0', sizeof(map_name));
+ memset(character, '\0', sizeof(character));
+
+ if (!message || !*message || sscanf(message, "%15s %d %d %23[^\n]", map_name, &x, &y, character) < 4 || x < 0 || y < 0) {
+ clif_displaymessage(fd, "Please, enter a valid save point and a player name (usage: #save <map> <x> <y> <charname>).");
+ return -1;
+ }
+
+ if (strstr(map_name, ".gat") == NULL && strstr(map_name, ".afm") == NULL && strlen(map_name) < MAP_NAME_LENGTH-4) // 16 - 4 (.gat)
+ strcat(map_name, ".gat");
+
+ if ((pl_sd = map_nick2sd(character)) != NULL) {
+ if (pc_isGM(sd) >= pc_isGM(pl_sd)) { // you can change save point only to lower or same gm level
+ m = map_mapname2mapid(map_name);
+ if (m < 0) {
+ clif_displaymessage(fd, msg_table[1]); // Map not found.
+ return -1;
+ } else {
+ if (map[m].flag.nowarpto && battle_config.any_warp_GM_min_level > pc_isGM(sd)) {
+ clif_displaymessage(fd, "You are not authorised to set this map as a save map.");
+ return -1;
+ }
+ pc_setsavepoint(pl_sd, map[m].index, x, y);
+ clif_displaymessage(fd, msg_table[57]); // Character's respawn point changed.
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[81]); // Your GM level don't authorise you to do this action on this player.
+ return -1;
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[3]); // Character not found.
+ return -1;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+//** Character Stats All by fritz
+int charcommand_stats_all(const int fd, struct map_session_data* sd, const char* command, const char* message)
+{
+ char output[1024], gmlevel[1024];
+ int i;
+ int count, users;
+ struct map_session_data *pl_sd, **pl_allsd;
+
+ memset(output, '\0', sizeof(output));
+ memset(gmlevel, '\0', sizeof(gmlevel));
+
+ count = 0;
+ pl_allsd = map_getallusers(&users);
+ for(i = 0; i < users; i++) {
+ if ((pl_sd = pl_allsd[i]))
+ {
+ if (pc_isGM(pl_sd) > 0)
+ sprintf(gmlevel, "| GM Lvl: %d", pc_isGM(pl_sd));
+ else
+ sprintf(gmlevel, " ");
+
+ sprintf(output, "Name: %s | BLvl: %d | Job: %s (Lvl: %d) | HP: %d/%d | SP: %d/%d", pl_sd->status.name, pl_sd->status.base_level, job_name(pl_sd->status.class_), pl_sd->status.job_level, pl_sd->status.hp, pl_sd->status.max_hp, pl_sd->status.sp, pl_sd->status.max_sp);
+ clif_displaymessage(fd, output);
+ sprintf(output, "STR: %d | AGI: %d | VIT: %d | INT: %d | DEX: %d | LUK: %d | Zeny: %d %s", pl_sd->status.str, pl_sd->status.agi, pl_sd->status.vit, pl_sd->status.int_, pl_sd->status.dex, pl_sd->status.luk, pl_sd->status.zeny, gmlevel);
+ clif_displaymessage(fd, output);
+ clif_displaymessage(fd, "--------");
+ count++;
+ }
+ }
+
+ if (count == 0)
+ clif_displaymessage(fd, msg_table[28]); // No player found.
+ else if (count == 1)
+ clif_displaymessage(fd, msg_table[29]); // 1 player found.
+ else {
+ sprintf(output, msg_table[30], count); // %d players found.
+ clif_displaymessage(fd, output);
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * CharSpiritBall Function by PalasX
+ *------------------------------------------
+ */
+int charcommand_spiritball(const int fd, struct map_session_data* sd,const char* command, const char* message)
+{
+ struct map_session_data *pl_sd;
+ char character[NAME_LENGTH];
+ int spirit = 0;
+
+ memset(character, '\0', sizeof(character));
+
+ if(!message || !*message || sscanf(message, "%d %23[^\n]", &spirit, character) < 2 || spirit < 0 || spirit > 1000) {
+ clif_displaymessage(fd, "Usage: @spiritball <number: 0-1000>) <CHARACTER_NAME>.");
+ return -1;
+ }
+
+ if((pl_sd = map_nick2sd(character)) != NULL) {
+ if (spirit >= 0 && spirit <= 0x7FFF) {
+ if (pl_sd->spiritball != spirit || spirit > 999) {
+ if (pl_sd->spiritball > 0)
+ pc_delspiritball(pl_sd, pl_sd->spiritball, 1);
+ pl_sd->spiritball = spirit;
+ clif_spiritball(pl_sd);
+ // no message, player can look the difference
+ if (spirit > 1000)
+ clif_displaymessage(fd, msg_table[204]); // WARNING: more than 1000 spiritballs can CRASH your server and/or client!
+ } else {
+ clif_displaymessage(fd, msg_table[205]); // You already have this number of spiritballs.
+ return -1;
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[37]); // An invalid number was specified.
+ return -1;
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[3]); // Character not found.
+ return -1;
+ }
+ return 0;
+}
+
+/*==========================================
+ * #itemlist <character>: Displays the list of a player's items.
+ *------------------------------------------
+ */
+int
+charcommand_itemlist(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ struct map_session_data *pl_sd;
+ struct item_data *item_data, *item_temp;
+ int i, j, equip, count, counter, counter2;
+ char character[NAME_LENGTH], output[200], equipstr[100], outputtmp[200];
+ nullpo_retr(-1, sd);
+
+ memset(character, '\0', sizeof(character));
+ memset(output, '\0', sizeof(output));
+ memset(equipstr, '\0', sizeof(equipstr));
+ memset(outputtmp, '\0', sizeof(outputtmp));
+
+ if (!message || !*message || sscanf(message, "%23[^\n]", character) < 1) {
+ clif_displaymessage(fd, "Please, enter a player name (usage: #itemlist <char name>).");
+ return -1;
+ }
+
+ if ((pl_sd = map_nick2sd(character)) != NULL) {
+ if (pc_isGM(sd) >= pc_isGM(pl_sd)) { // you can look items only lower or same level
+ counter = 0;
+ count = 0;
+ for (i = 0; i < MAX_INVENTORY; i++) {
+ if (pl_sd->status.inventory[i].nameid > 0 && (item_data = itemdb_search(pl_sd->status.inventory[i].nameid)) != NULL) {
+ counter = counter + pl_sd->status.inventory[i].amount;
+ count++;
+ if (count == 1) {
+ sprintf(output, "------ Items list of '%s' ------", pl_sd->status.name);
+ clif_displaymessage(fd, output);
+ }
+ if ((equip = pl_sd->status.inventory[i].equip)) {
+ strcpy(equipstr, "| equiped: ");
+ if (equip & 4)
+ strcat(equipstr, "robe/gargment, ");
+ if (equip & 8)
+ strcat(equipstr, "left accessory, ");
+ if (equip & 16)
+ strcat(equipstr, "body/armor, ");
+ if ((equip & 34) == 2)
+ strcat(equipstr, "right hand, ");
+ if ((equip & 34) == 32)
+ strcat(equipstr, "left hand, ");
+ if ((equip & 34) == 34)
+ strcat(equipstr, "both hands, ");
+ if (equip & 64)
+ strcat(equipstr, "feet, ");
+ if (equip & 128)
+ strcat(equipstr, "right accessory, ");
+ if ((equip & 769) == 1)
+ strcat(equipstr, "lower head, ");
+ if ((equip & 769) == 256)
+ strcat(equipstr, "top head, ");
+ if ((equip & 769) == 257)
+ strcat(equipstr, "lower/top head, ");
+ if ((equip & 769) == 512)
+ strcat(equipstr, "mid head, ");
+ if ((equip & 769) == 512)
+ strcat(equipstr, "lower/mid head, ");
+ if ((equip & 769) == 769)
+ strcat(equipstr, "lower/mid/top head, ");
+ // remove final ', '
+ equipstr[strlen(equipstr) - 2] = '\0';
+ } else
+ memset(equipstr, '\0', sizeof(equipstr));
+ if (sd->status.inventory[i].refine)
+ sprintf(output, "%d %s %+d (%s %+d, id: %d) %s", pl_sd->status.inventory[i].amount, item_data->name, pl_sd->status.inventory[i].refine, item_data->jname, pl_sd->status.inventory[i].refine, pl_sd->status.inventory[i].nameid, equipstr);
+ else
+ sprintf(output, "%d %s (%s, id: %d) %s", pl_sd->status.inventory[i].amount, item_data->name, item_data->jname, pl_sd->status.inventory[i].nameid, equipstr);
+ clif_displaymessage(fd, output);
+ memset(output, '\0', sizeof(output));
+ counter2 = 0;
+ for (j = 0; j < item_data->slot; j++) {
+ if (pl_sd->status.inventory[i].card[j]) {
+ if ((item_temp = itemdb_search(pl_sd->status.inventory[i].card[j])) != NULL) {
+ if (output[0] == '\0')
+ sprintf(outputtmp, " -> (card(s): #%d %s (%s), ", ++counter2, item_temp->name, item_temp->jname);
+ else
+ sprintf(outputtmp, "#%d %s (%s), ", ++counter2, item_temp->name, item_temp->jname);
+ strcat(output, outputtmp);
+ }
+ }
+ }
+ if (output[0] != '\0') {
+ output[strlen(output) - 2] = ')';
+ output[strlen(output) - 1] = '\0';
+ clif_displaymessage(fd, output);
+ }
+ }
+ }
+ if (count == 0)
+ clif_displaymessage(fd, "No item found on this player.");
+ else {
+ sprintf(output, "%d item(s) found in %d kind(s) of items.", counter, count);
+ clif_displaymessage(fd, output);
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[81]); // Your GM level don't authorise you to do this action on this player.
+ return -1;
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[3]); // Character not found.
+ return -1;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * #effect by [MouseJstr]
+ *
+ * Create a effect localized on another character
+ *------------------------------------------
+ */
+int
+charcommand_effect(const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ struct map_session_data *pl_sd = NULL;
+ char target[255];
+ int type = 0;
+ nullpo_retr(-1, sd);
+
+ if (!message || !*message || sscanf(message, "%d %s", &type, target) != 2) {
+ clif_displaymessage(fd, "usage: #effect <type+> <target>.");
+ return -1;
+ }
+
+ if((pl_sd=map_nick2sd((char *) target)) == NULL)
+ return -1;
+
+ clif_specialeffect(&pl_sd->bl, type, 0);
+ clif_displaymessage(fd, msg_table[229]); // Your effect has changed.
+
+ return 0;
+}
+
+/*==========================================
+ * #storagelist <character>: Displays the items list of a player's storage.
+ *------------------------------------------
+ */
+int
+charcommand_storagelist(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ struct storage *stor;
+ struct map_session_data *pl_sd;
+ struct item_data *item_data, *item_temp;
+ int i, j, count, counter, counter2;
+ char character[NAME_LENGTH], output[200], outputtmp[200];
+ nullpo_retr(-1, sd);
+
+ memset(character, '\0', sizeof(character));
+ memset(output, '\0', sizeof(output));
+ memset(outputtmp, '\0', sizeof(outputtmp));
+
+ if (!message || !*message || sscanf(message, "%23[^\n]", character) < 1) {
+ clif_displaymessage(fd, "Please, enter a player name (usage: #itemlist <char name>).");
+ return -1;
+ }
+
+ if ((pl_sd = map_nick2sd(character)) != NULL) {
+ if (pc_isGM(sd) >= pc_isGM(pl_sd)) { // you can look items only lower or same level
+ if((stor = account2storage2(pl_sd->status.account_id)) != NULL) {
+ counter = 0;
+ count = 0;
+ for (i = 0; i < MAX_STORAGE; i++) {
+ if (stor->storage_[i].nameid > 0 && (item_data = itemdb_search(stor->storage_[i].nameid)) != NULL) {
+ counter = counter + stor->storage_[i].amount;
+ count++;
+ if (count == 1) {
+ sprintf(output, "------ Storage items list of '%s' ------", pl_sd->status.name);
+ clif_displaymessage(fd, output);
+ }
+ if (stor->storage_[i].refine)
+ sprintf(output, "%d %s %+d (%s %+d, id: %d)", stor->storage_[i].amount, item_data->name, stor->storage_[i].refine, item_data->jname, stor->storage_[i].refine, stor->storage_[i].nameid);
+ else
+ sprintf(output, "%d %s (%s, id: %d)", stor->storage_[i].amount, item_data->name, item_data->jname, stor->storage_[i].nameid);
+ clif_displaymessage(fd, output);
+ memset(output, '\0', sizeof(output));
+ counter2 = 0;
+ for (j = 0; j < item_data->slot; j++) {
+ if (stor->storage_[i].card[j]) {
+ if ((item_temp = itemdb_search(stor->storage_[i].card[j])) != NULL) {
+ if (output[0] == '\0')
+ sprintf(outputtmp, " -> (card(s): #%d %s (%s), ", ++counter2, item_temp->name, item_temp->jname);
+ else
+ sprintf(outputtmp, "#%d %s (%s), ", ++counter2, item_temp->name, item_temp->jname);
+ strcat(output, outputtmp);
+ }
+ }
+ }
+ if (output[0] != '\0') {
+ output[strlen(output) - 2] = ')';
+ output[strlen(output) - 1] = '\0';
+ clif_displaymessage(fd, output);
+ }
+ }
+ }
+ if (count == 0)
+ clif_displaymessage(fd, "No item found in the storage of this player.");
+ else {
+ sprintf(output, "%d item(s) found in %d kind(s) of items.", counter, count);
+ clif_displaymessage(fd, output);
+ }
+ } else {
+ clif_displaymessage(fd, "This player has no storage.");
+ return 0;
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[81]); // Your GM level don't authorise you to do this action on this player.
+ return -1;
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[3]); // Character not found.
+ return -1;
+ }
+
+ return 0;
+}
+
+static void
+charcommand_giveitem_sub(struct map_session_data *sd,struct item_data *item_data,int number)
+{
+ int flag = 0;
+ int loop = 1, get_count = number,i;
+ struct item item_tmp;
+
+ if(sd && item_data){
+ if (item_data->type == 4 || item_data->type == 5 ||
+ item_data->type == 7 || item_data->type == 8) {
+ loop = number;
+ get_count = 1;
+ }
+ for (i = 0; i < loop; i++) {
+ memset(&item_tmp, 0, sizeof(item_tmp));
+ item_tmp.nameid = item_data->nameid;
+ item_tmp.identify = 1;
+
+ if ((flag = pc_additem((struct map_session_data*)sd,
+ &item_tmp, get_count)))
+ clif_additem((struct map_session_data*)sd, 0, 0, flag);
+ }
+ //Logs (A)dmins items [Lupus]
+ if(log_config.pick > 0 ) {
+ log_pick(sd, "A", 0, item_tmp.nameid, number, &item_tmp);
+ }
+ //Logs
+
+ }
+}
+/*==========================================
+ * #item command (usage: #item <name/id_of_item> <quantity> <player>)
+ * by MC Cameri
+ *------------------------------------------
+ */
+int charcommand_item(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ char item_name[100];
+ char character[NAME_LENGTH];
+ struct map_session_data *pl_sd;
+ int number = 0, item_id, flag;
+ struct item item_tmp;
+ struct item_data *item_data;
+ int get_count, i, pet_id;
+ char tmp_cmdoutput[1024];
+ nullpo_retr(-1, sd);
+
+ memset(item_name, '\0', sizeof(item_name));
+
+ if (!message || !*message || sscanf(message, "%99s %d %23[^\n]", item_name, &number, character) < 3) {
+ clif_displaymessage(fd, "Please, enter an item name/id (usage: #item <item name or ID> <quantity> <char name>).");
+ return -1;
+ }
+
+ if (number <= 0)
+ number = 1;
+
+ item_id = 0;
+ if ((item_data = itemdb_searchname(item_name)) != NULL ||
+ (item_data = itemdb_exists(atoi(item_name))) != NULL)
+ item_id = item_data->nameid;
+
+ if (item_id >= 500) {
+ get_count = number;
+ // check pet egg
+ pet_id = search_petDB_index(item_id, PET_EGG);
+ if (item_data->type == 4 || item_data->type == 5 ||
+ item_data->type == 7 || item_data->type == 8) {
+ get_count = 1;
+ }
+ if ((pl_sd = map_nick2sd(character)) != NULL) {
+ if (pc_isGM(sd) >= pc_isGM(pl_sd)) { // you can look items only lower or same level
+ for (i = 0; i < number; i += get_count) {
+ // if pet egg
+ if (pet_id >= 0) {
+ pl_sd->catch_target_class = pet_db[pet_id].class_;
+ intif_create_pet(pl_sd->status.account_id, pl_sd->status.char_id,
+ (short)pet_db[pet_id].class_, (short)mob_db(pet_db[pet_id].class_)->lv,
+ (short)pet_db[pet_id].EggID, 0, (short)pet_db[pet_id].intimate,
+ 100, 0, 1, pet_db[pet_id].jname);
+ // if not pet egg
+ } else {
+ memset(&item_tmp, 0, sizeof(item_tmp));
+ item_tmp.nameid = item_id;
+ item_tmp.identify = 1;
+
+ if ((flag = pc_additem(pl_sd, &item_tmp, get_count)))
+ clif_additem(pl_sd, 0, 0, flag);
+ }
+ }
+
+ //Logs (A)dmins items [Lupus]
+ if(log_config.pick > 0 ) {
+ log_pick(sd, "A", 0, item_tmp.nameid, number, &item_tmp);
+ }
+ //Logs
+
+ clif_displaymessage(fd, msg_table[18]); // Item created.
+ } else {
+ clif_displaymessage(fd, msg_table[81]); // Your GM level don't authorise you to do this action on this player.
+ return -1;
+ }
+ } else if(/* from jA's @giveitem */strcmpi(character,"all")==0 || strcmpi(character,"everyone")==0){
+ struct map_session_data **pl_allsd;
+ int users;
+ pl_allsd = map_getallusers(&users);
+ for (i = 0; i < users; i++) {
+ if ((pl_sd = pl_allsd[i])) {
+ charcommand_giveitem_sub(pl_sd,item_data,number);
+ snprintf(tmp_cmdoutput, sizeof(tmp_cmdoutput), "You got %s %d.", item_name,number);
+ clif_displaymessage(pl_sd->fd, tmp_cmdoutput);
+ }
+ }
+ snprintf(tmp_cmdoutput, sizeof(tmp_cmdoutput), "%s received %s %d.","Everyone",item_name,number);
+ clif_displaymessage(fd, tmp_cmdoutput);
+ } else {
+ clif_displaymessage(fd, msg_table[3]); // Character not found.
+ return -1;
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[19]); // Invalid item ID or name.
+ return -1;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * #warp/#rura/#rura+ <mapname> <x> <y> <char name>
+ *------------------------------------------
+ */
+int charcommand_warp(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ char map_name[MAP_NAME_LENGTH];
+ char character[NAME_LENGTH];
+ int x = 0, y = 0;
+ struct map_session_data *pl_sd;
+ int m;
+
+ nullpo_retr(-1, sd);
+
+ memset(map_name, '\0', sizeof(map_name));
+ memset(character, '\0', sizeof(character));
+
+ if (!message || !*message || sscanf(message, "%15s %d %d %23[^\n]", map_name, &x, &y, character) < 4) {
+ clif_displaymessage(fd, "Usage: #warp/#rura/#rura+ <mapname> <x> <y> <char name>");
+ return -1;
+ }
+
+ if (x <= 0)
+ x = rand() % 399 + 1;
+ if (y <= 0)
+ y = rand() % 399 + 1;
+ if (strstr(map_name, ".gat") == NULL && strstr(map_name, ".afm") == NULL && strlen(map_name) < MAP_NAME_LENGTH-4) // 16 - 4 (.gat)
+ strcat(map_name, ".gat");
+
+ if ((pl_sd = map_nick2sd(character)) != NULL) {
+ if (pc_isGM(sd) >= pc_isGM(pl_sd)) { // you can rura+ only lower or same GM level
+ if (x > 0 && x < 400 && y > 0 && y < 400) {
+ m = map_mapname2mapid(map_name);
+ if (m >= 0 && map[m].flag.nowarpto && battle_config.any_warp_GM_min_level > pc_isGM(sd)) {
+ clif_displaymessage(fd, "You are not authorised to warp someone to this map.");
+ return -1;
+ }
+ if (pl_sd->bl.m >= 0 && map[pl_sd->bl.m].flag.nowarp && battle_config.any_warp_GM_min_level > pc_isGM(sd)) {
+ clif_displaymessage(fd, "You are not authorised to warp this player from its actual map.");
+ return -1;
+ }
+ if (pc_setpos(pl_sd, map[m].index, x, y, 3) == 0) {
+ clif_displaymessage(pl_sd->fd, msg_table[0]); // Warped.
+ clif_displaymessage(fd, msg_table[15]); // Player warped (message sends to player too).
+ } else {
+ clif_displaymessage(fd, msg_table[1]); // Map not found.
+ return -1;
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[2]); // Coordinates out of range.
+ return -1;
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[81]); // Your GM level don't authorise you to do this action on this player.
+ return -1;
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[3]); // Character not found.
+ return -1;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * #zeny <charname>
+ *------------------------------------------
+ */
+int charcommand_zeny(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ struct map_session_data *pl_sd;
+ char character[NAME_LENGTH];
+ int zeny = 0, new_zeny;
+ nullpo_retr(-1, sd);
+
+ memset(character, '\0', sizeof(character));
+
+ if (!message || !*message || sscanf(message, "%d %23[^\n]", &zeny, character) < 2 || zeny == 0) {
+ clif_displaymessage(fd, "Please, enter a number and a player name (usage: #zeny <zeny> <name>).");
+ return -1;
+ }
+
+ if ((pl_sd = map_nick2sd(character)) != NULL) {
+ new_zeny = pl_sd->status.zeny + zeny;
+ if (zeny > 0 && (zeny > MAX_ZENY || new_zeny > MAX_ZENY)) // fix positiv overflow
+ new_zeny = MAX_ZENY;
+ else if (zeny < 0 && (zeny < -MAX_ZENY || new_zeny < 0)) // fix negativ overflow
+ new_zeny = 0;
+ if (new_zeny != pl_sd->status.zeny) {
+ pl_sd->status.zeny = new_zeny;
+ clif_updatestatus(pl_sd, SP_ZENY);
+ clif_displaymessage(fd, msg_table[211]); // Character's number of zenys changed!
+ } else {
+ if (zeny < 0)
+ clif_displaymessage(fd, msg_table[41]); // Impossible to decrease the number/value.
+ else
+ clif_displaymessage(fd, msg_table[149]); // Impossible to increase the number/value.
+ return -1;
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[3]); // Character not found.
+ return -1;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * #fakename <char name> <fake name>
+ *------------------------------------------
+ */
+
+int charcommand_fakename(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ struct map_session_data *pl_sd;
+ char name[NAME_LENGTH];
+ char char_name[NAME_LENGTH];
+
+ nullpo_retr(-1, sd);
+
+ name[0] = '\0'; //If you don't pass a second word, name is left as garbage, most definitely not a blank name! [Skotlex]
+ if (!message || !*message || sscanf(message, "%23s %23[^\n]", char_name, name) < 1) {
+ clif_displaymessage(sd->fd,"Usage: #fakename <char name> <fake name>.");
+ clif_displaymessage(sd->fd,"Or: #fakename <char name> to disable.");
+ return 0;
+ }
+
+ if(!(pl_sd = map_nick2sd(char_name))) {
+ clif_displaymessage(sd->fd,"Character not found.");
+ return -1;
+ }
+
+ if(strlen(name) < 1 || !name) {
+ if(strlen(pl_sd->fakename) > 1) {
+ pl_sd->fakename[0]='\0';
+ pc_setpos(pl_sd, pl_sd->mapindex, pl_sd->bl.x, sd->bl.y, 3);
+ clif_displaymessage(sd->fd,"Returned to real name.");
+ } else {
+ clif_displaymessage(sd->fd,"Character does not has a fake name.");
+ }
+ return 0;
+ }
+
+ if(strlen(name) < 2) {
+ clif_displaymessage(sd->fd,"Fake name must be at least two characters.");
+ return 0;
+ }
+
+ memcpy(pl_sd->fakename,name, NAME_LENGTH-1);
+ pc_setpos(pl_sd, pl_sd->mapindex, pl_sd->bl.x, pl_sd->bl.y, 3);
+ clif_displaymessage(sd->fd,"Fake name enabled.");
+
+ return 0;
+}
+
+
+/*==========================================
+ * #baselvl <#> <nickname>
+ * Transferred by: Kevin
+ *------------------------------------------
+*/
+int charcommand_baselevel(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ struct map_session_data *pl_sd;
+ char player[NAME_LENGTH];
+ int level = 0, i;
+ nullpo_retr(-1, sd);
+
+ if (!message || !*message || sscanf(message, "%d %23[^\n]", &level, player) < 2 || level == 0) {
+ clif_displaymessage(fd, "Please, enter a level adjustement and a player name (usage: #baselvl <#> <nickname>).");
+ return -1;
+ }
+
+ if ((pl_sd = map_nick2sd(player)) != NULL) {
+ if (pc_isGM(sd) >= pc_isGM(pl_sd)) { // you can change base level only lower or same gm level
+
+ if (level > 0) {
+ if (pl_sd->status.base_level == battle_config.max_base_level) { // check for max level by Valaris
+ clif_displaymessage(fd, msg_table[91]); // Character's base level can't go any higher.
+ return 0;
+ } // End Addition
+ if ((unsigned int)level > battle_config.max_base_level || (unsigned int)level > (battle_config.max_base_level - pl_sd->status.base_level)) // fix positiv overflow
+ level = battle_config.max_base_level - pl_sd->status.base_level;
+ for (i = 1; i <= level; i++)
+ pl_sd->status.status_point += (pl_sd->status.base_level + i + 14) / 5;
+ pl_sd->status.base_level += level;
+ clif_updatestatus(pl_sd, SP_BASELEVEL);
+ clif_updatestatus(pl_sd, SP_NEXTBASEEXP);
+ clif_updatestatus(pl_sd, SP_STATUSPOINT);
+ status_calc_pc(pl_sd, 0);
+ pc_heal(pl_sd, pl_sd->status.max_hp, pl_sd->status.max_sp);
+ clif_misceffect(&pl_sd->bl, 0);
+ clif_displaymessage(fd, msg_table[65]); // Character's base level raised.
+ } else {
+ if (pl_sd->status.base_level == 1) {
+ clif_displaymessage(fd, msg_table[193]); // Character's base level can't go any lower.
+ return -1;
+ }
+ if (level < -(int)battle_config.max_base_level || level < (1 - (int)pl_sd->status.base_level)) // fix negativ overflow
+ level = 1 - pl_sd->status.base_level;
+ if (pl_sd->status.status_point > 0) {
+ for (i = 0; i > level; i--)
+ pl_sd->status.status_point -= (pl_sd->status.base_level + i + 14) / 5;
+ if (pl_sd->status.status_point < 0)
+ pl_sd->status.status_point = 0;
+ clif_updatestatus(pl_sd, SP_STATUSPOINT);
+ } // to add: remove status points from stats
+ pl_sd->status.base_level += level;
+ clif_updatestatus(pl_sd, SP_BASELEVEL);
+ clif_updatestatus(pl_sd, SP_NEXTBASEEXP);
+ status_calc_pc(pl_sd, 0);
+ clif_displaymessage(fd, msg_table[66]); // Character's base level lowered.
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[81]); // Your GM level don't authorise you to do this action on this player.
+ return -1;
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[3]); // Character not found.
+ return -1;
+ }
+
+ return 0; //正常終了
+}
+
+/*==========================================
+ * #jlvl <#> <nickname>
+ * Transferred by: Kevin
+ *------------------------------------------
+ */
+int charcommand_joblevel(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ struct map_session_data *pl_sd;
+ unsigned int max_level = battle_config.max_job_level;
+ char player[NAME_LENGTH];
+ int level = 0;
+ //転生や養子の場合の元の職業を算出する
+ nullpo_retr(-1, sd);
+
+ if (!message || !*message || sscanf(message, "%d %23[^\n]", &level, player) < 2 || level == 0) {
+ clif_displaymessage(fd, "Please, enter a level adjustement and a player name (usage: #joblvl <#> <nickname>).");
+ return -1;
+ }
+
+ if ((pl_sd = map_nick2sd(player)) != NULL) {
+ if (pc_isGM(sd) >= pc_isGM(pl_sd)) { // you can change job level only lower or same gm level
+ if ((pl_sd->class_&MAPID_UPPERMASK) == MAPID_NOVICE)
+ max_level = 10; //Novice
+ else if ((pl_sd->class_&MAPID_BASEMASK) == MAPID_NOVICE)
+ max_level = battle_config.max_sn_level; //S. Novice
+ else if (pl_sd->class_&JOBL_UPPER && pl_sd->class_&JOBL_2)
+ max_level = battle_config.max_adv_level; //Adv. Class
+
+ if (level > 0) {
+ if (pl_sd->status.job_level == max_level) {
+ clif_displaymessage(fd, msg_table[67]); // Character's job level can't go any higher.
+ return -1;
+ }
+ if (pl_sd->status.job_level + level > max_level)
+ level = max_level - pl_sd->status.job_level;
+ pl_sd->status.job_level += level;
+ clif_updatestatus(pl_sd, SP_JOBLEVEL);
+ clif_updatestatus(pl_sd, SP_NEXTJOBEXP);
+ pl_sd->status.skill_point += level;
+ clif_updatestatus(pl_sd, SP_SKILLPOINT);
+ status_calc_pc(pl_sd, 0);
+ clif_misceffect(&pl_sd->bl, 1);
+ clif_displaymessage(fd, msg_table[68]); // character's job level raised.
+ } else {
+ if (pl_sd->status.job_level == 1) {
+ clif_displaymessage(fd, msg_table[194]); // Character's job level can't go any lower.
+ return -1;
+ }
+ if (pl_sd->status.job_level + level < 1)
+ level = 1 - pl_sd->status.job_level;
+ pl_sd->status.job_level += level;
+ clif_updatestatus(pl_sd, SP_JOBLEVEL);
+ clif_updatestatus(pl_sd, SP_NEXTJOBEXP);
+ if (pl_sd->status.skill_point > 0) {
+ pl_sd->status.skill_point += level;
+ if (pl_sd->status.skill_point < 0)
+ pl_sd->status.skill_point = 0;
+ clif_updatestatus(pl_sd, SP_SKILLPOINT);
+ } // to add: remove status points from skills
+ status_calc_pc(pl_sd, 0);
+ clif_displaymessage(fd, msg_table[69]); // Character's job level lowered.
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[81]); // Your GM level don't authorise you to do this action on this player.
+ return -1;
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[3]); // Character not found.
+ return -1;
+ }
+
+ return 0;
+}
+
+
+/*==========================================
+ * #questskill <skill_#> <nickname>
+ * Transferred by: Kevin
+ *------------------------------------------
+ */
+int charcommand_questskill(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ struct map_session_data *pl_sd;
+ char player[NAME_LENGTH];
+ int skill_id = 0;
+ nullpo_retr(-1, sd);
+
+ if (!message || !*message || sscanf(message, "%d %23[^\n]", &skill_id, player) < 2 || skill_id < 0) {
+ clif_displaymessage(fd, "Please, enter a quest skill number and a player name (usage: #questskill <#:0+> <nickname>).");
+ return -1;
+ }
+
+ if (skill_id >= 0 && skill_id < MAX_SKILL_DB) {
+ if (skill_get_inf2(skill_id) & INF2_QUEST_SKILL) {
+ if ((pl_sd = map_nick2sd(player)) != NULL) {
+ if (pc_checkskill(pl_sd, skill_id) == 0) {
+ pc_skill(pl_sd, skill_id, 1, 0);
+ clif_displaymessage(fd, msg_table[199]); // This player has learned the skill.
+ } else {
+ clif_displaymessage(fd, msg_table[200]); // This player already has this quest skill.
+ return -1;
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[3]); // Character not found.
+ return -1;
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[197]); // This skill number doesn't exist or isn't a quest skill.
+ return -1;
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[198]); // This skill number doesn't exist.
+ return -1;
+ }
+
+ return 0;
+}
+
+
+/*==========================================
+ * #lostskill <skill_#> <nickname>
+ * Transferred by: Kevin
+ *------------------------------------------
+ */
+int charcommand_lostskill(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ struct map_session_data *pl_sd;
+ char player[NAME_LENGTH];
+ int skill_id = 0;
+ nullpo_retr(-1, sd);
+
+ if (!message || !*message || sscanf(message, "%d %23[^\n]", &skill_id, player) < 2 || skill_id < 0) {
+ clif_displaymessage(fd, "Please, enter a quest skill number and a player name (usage: @charlostskill <#:0+> <char_name>).");
+ return -1;
+ }
+
+ if (skill_id >= 0 && skill_id < MAX_SKILL) {
+ if (skill_get_inf2(skill_id) & INF2_QUEST_SKILL) {
+ if ((pl_sd = map_nick2sd(player)) != NULL) {
+ if (pc_checkskill(pl_sd, skill_id) > 0) {
+ pl_sd->status.skill[skill_id].lv = 0;
+ pl_sd->status.skill[skill_id].flag = 0;
+ clif_skillinfoblock(pl_sd);
+ clif_displaymessage(fd, msg_table[202]); // This player has forgotten the skill.
+ } else {
+ clif_displaymessage(fd, msg_table[203]); // This player doesn't have this quest skill.
+ return -1;
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[3]); // Character not found.
+ return -1;
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[197]); // This skill number doesn't exist or isn't a quest skill.
+ return -1;
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[198]); // This skill number doesn't exist.
+ return -1;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * Character Skill Reset
+ *------------------------------------------
+ */
+int charcommand_skreset(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ struct map_session_data *pl_sd;
+ char player[NAME_LENGTH];
+ char tmp_cmdoutput[1024];
+ nullpo_retr(-1, sd);
+
+ if (!message || !*message || sscanf(message, "%23[^\n]", player) < 1) {
+ clif_displaymessage(fd, "Please, enter a player name (usage: @charskreset <charname>).");
+ return -1;
+ }
+
+ if ((pl_sd = map_nick2sd(player)) != NULL) {
+ if (pc_isGM(sd) >= pc_isGM(pl_sd)) { // you can reset skill points only lower or same gm level
+ pc_resetskill(pl_sd);
+ sprintf(tmp_cmdoutput, msg_table[206], player); // '%s' skill points reseted!
+ clif_displaymessage(fd, tmp_cmdoutput);
+ } else {
+ clif_displaymessage(fd, msg_table[81]); // Your GM level don't authorise you to do this action on this player.
+ return -1;
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[3]); // Character not found.
+ return -1;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * Character Stat Reset
+ *------------------------------------------
+ */
+int charcommand_streset(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ struct map_session_data *pl_sd;
+ char player[NAME_LENGTH];
+ char tmp_cmdoutput[1024];
+ nullpo_retr(-1, sd);
+
+ if (!message || !*message || sscanf(message, "%23[^\n]", player) < 1) {
+ clif_displaymessage(fd, "Please, enter a player name (usage: @charstreset <charname>).");
+ return -1;
+ }
+
+ if ((pl_sd = map_nick2sd(player)) != NULL) {
+ if (pc_isGM(sd) >= pc_isGM(pl_sd)) { // you can reset stats points only lower or same gm level
+ pc_resetstate(pl_sd);
+ sprintf(tmp_cmdoutput, msg_table[207], player); // '%s' stats points reseted!
+ clif_displaymessage(fd, tmp_cmdoutput);
+ } else {
+ clif_displaymessage(fd, msg_table[81]); // Your GM level don't authorise you to do this action on this player.
+ return -1;
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[3]); // Character not found.
+ return -1;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * Character Model by chbrules
+ *------------------------------------------
+ */
+int charcommand_model(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ int hair_style = 0, hair_color = 0, cloth_color = 0;
+ struct map_session_data *pl_sd;
+ char player[NAME_LENGTH];
+ char tmp_cmdoutput[1024];
+ nullpo_retr(-1, sd);
+
+ if (!message || !*message || sscanf(message, "%d %d %d %23[^\n]", &hair_style, &hair_color, &cloth_color, player) < 4 || hair_style < 0 || hair_color < 0 || cloth_color < 0) {
+ sprintf(tmp_cmdoutput, "Please, enter a valid model and a player name (usage: @charmodel <hair ID: %d-%d> <hair color: %d-%d> <clothes color: %d-%d> <name>).",
+ MIN_HAIR_STYLE, MAX_HAIR_STYLE, MIN_HAIR_COLOR, MAX_HAIR_COLOR, MIN_CLOTH_COLOR, MAX_CLOTH_COLOR);
+ clif_displaymessage(fd, tmp_cmdoutput);
+ return -1;
+ }
+
+ if ((pl_sd = map_nick2sd(player)) != NULL) {
+ if (hair_style >= MIN_HAIR_STYLE && hair_style <= MAX_HAIR_STYLE &&
+ hair_color >= MIN_HAIR_COLOR && hair_color <= MAX_HAIR_COLOR &&
+ cloth_color >= MIN_CLOTH_COLOR && cloth_color <= MAX_CLOTH_COLOR) {
+ /* Removed this check for being too strange. [Skotlex]
+ if (cloth_color != 0 &&
+ pl_sd->status.sex == 1 &&
+ (pl_sd->status.class_ == JOB_ASSASSIN || pl_sd->status.class_ == JOB_ROGUE)) {
+ clif_displaymessage(fd, msg_table[35]); // You can't use this command with this class.
+ return -1;
+ } else {
+ */
+ pc_changelook(pl_sd, LOOK_HAIR, hair_style);
+ pc_changelook(pl_sd, LOOK_HAIR_COLOR, hair_color);
+ pc_changelook(pl_sd, LOOK_CLOTHES_COLOR, cloth_color);
+ clif_displaymessage(fd, msg_table[36]); // Appearence changed.
+// }
+ } else {
+ clif_displaymessage(fd, msg_table[37]); // An invalid number was specified.
+ return -1;
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[3]); // Character not found.
+ return -1;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * Character Skill Point (Rewritten by [Yor])
+ *------------------------------------------
+ */
+int charcommand_skpoint(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ struct map_session_data *pl_sd;
+ char player[NAME_LENGTH];
+ int new_skill_point;
+ int point = 0;
+ nullpo_retr(-1, sd);
+
+ if (!message || !*message || sscanf(message, "%d %23[^\n]", &point, player) < 2 || point == 0) {
+ clif_displaymessage(fd, "Please, enter a number and a player name (usage: @charskpoint <amount> <name>).");
+ return -1;
+ }
+
+ if ((pl_sd = map_nick2sd(player)) != NULL) {
+ new_skill_point = (int)pl_sd->status.skill_point + point;
+ if (point > 0 && (point > 0x7FFF || new_skill_point > 0x7FFF)) // fix positiv overflow
+ new_skill_point = 0x7FFF;
+ else if (point < 0 && (point < -0x7FFF || new_skill_point < 0)) // fix negativ overflow
+ new_skill_point = 0;
+ if (new_skill_point != (int)pl_sd->status.skill_point) {
+ pl_sd->status.skill_point = new_skill_point;
+ clif_updatestatus(pl_sd, SP_SKILLPOINT);
+ clif_displaymessage(fd, msg_table[209]); // Character's number of skill points changed!
+ } else {
+ if (point < 0)
+ clif_displaymessage(fd, msg_table[41]); // Impossible to decrease the number/value.
+ else
+ clif_displaymessage(fd, msg_table[149]); // Impossible to increase the number/value.
+ return -1;
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[3]); // Character not found.
+ return -1;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * Character Status Point (rewritten by [Yor])
+ *------------------------------------------
+ */
+int charcommand_stpoint(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ struct map_session_data *pl_sd;
+ char player[NAME_LENGTH];
+ int new_status_point;
+ int point = 0;
+ nullpo_retr(-1, sd);
+
+ if (!message || !*message || sscanf(message, "%d %23[^\n]", &point, player) < 2 || point == 0) {
+ clif_displaymessage(fd, "Please, enter a number and a player name (usage: @charstpoint <amount> <name>).");
+ return -1;
+ }
+
+ if ((pl_sd = map_nick2sd(player)) != NULL) {
+ new_status_point = (int)pl_sd->status.status_point + point;
+ if (point > 0 && (point > 0x7FFF || new_status_point > 0x7FFF)) // fix positiv overflow
+ new_status_point = 0x7FFF;
+ else if (point < 0 && (point < -0x7FFF || new_status_point < 0)) // fix negativ overflow
+ new_status_point = 0;
+ if (new_status_point != (int)pl_sd->status.status_point) {
+ pl_sd->status.status_point = new_status_point;
+ clif_updatestatus(pl_sd, SP_STATUSPOINT);
+ clif_displaymessage(fd, msg_table[210]); // Character's number of status points changed!
+ } else {
+ if (point < 0)
+ clif_displaymessage(fd, msg_table[41]); // Impossible to decrease the number/value.
+ else
+ clif_displaymessage(fd, msg_table[149]); // Impossible to increase the number/value.
+ return -1;
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[3]); // Character not found.
+ return -1;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * charchangesex command (usage: charchangesex <player_name>)
+ *------------------------------------------
+ */
+int charcommand_changesex(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ char player[NAME_LENGTH];
+ nullpo_retr(-1, sd);
+
+ if (!message || !*message || sscanf(message, "%23[^\n]", player) < 1) {
+ clif_displaymessage(fd, "Please, enter a player name (usage: @charchangesex <name>).");
+ return -1;
+ }
+
+ // check player name
+ if (strlen(player) < 4) {
+ clif_displaymessage(fd, msg_table[86]); // Sorry, but a player name have at least 4 characters.
+ return -1;
+ } else if (strlen(player) > 23) {
+ clif_displaymessage(fd, msg_table[87]); // Sorry, but a player name have 23 characters maximum.
+ return -1;
+ } else {
+ chrif_char_ask_name(sd->status.account_id, player, 5, 0, 0, 0, 0, 0, 0); // type: 5 - changesex
+ clif_displaymessage(fd, msg_table[88]); // Character name sends to char-server to ask it.
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * Feel (SG save map) Reset
+ *------------------------------------------
+ */
+int charcommand_feelreset(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ char character[NAME_LENGTH];
+ char output[200];
+ struct map_session_data *pl_sd;
+
+ memset(character, '\0', sizeof(character));
+ memset(output, '\0', sizeof(output));
+
+ if (!message || !*message || sscanf(message, "%23[^\n]", character) < 1) {
+ clif_displaymessage(fd, "Please, enter a player name (usage: #feelreset <charname>).");
+ return -1;
+ }
+
+ if ((pl_sd = map_nick2sd(character)) != NULL) {
+ if (pc_isGM(sd) >= pc_isGM(pl_sd)) { // you can reset a character only for lower or same GM level
+ pc_resetfeel(pl_sd);
+ sprintf(output, msg_table[267], character); // '%s' designated maps reseted!
+ clif_displaymessage(fd, output);
+ } else {
+ clif_displaymessage(fd, msg_table[81]); // Your GM level don't authorise you to do this action on this player.
+ return -1;
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[3]); // Character not found.
+ return -1;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * #help - Char commands [Kayla]
+ *------------------------------------------
+ */
+int charcommand_help(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ char buf[2048], w1[2048], w2[2048];
+ int i, gm_level;
+ FILE* fp;
+ nullpo_retr(-1, sd);
+
+ memset(buf, '\0', sizeof(buf));
+
+ if ((fp = fopen(charhelp_txt, "r")) != NULL) {
+ clif_displaymessage(fd, msg_table[26]); /* Help commands: */
+ gm_level = pc_isGM(sd);
+ while(fgets(buf, sizeof(buf) - 1, fp) != NULL) {
+ if (buf[0] == '/' && buf[1] == '/')
+ continue;
+ for (i = 0; buf[i] != '\0'; i++) {
+ if (buf[i] == '\r' || buf[i] == '\n') {
+ buf[i] = '\0';
+ break;
+ }
+ }
+ if (sscanf(buf, "%2047[^:]:%2047[^\n]", w1, w2) < 2)
+ clif_displaymessage(fd, buf);
+ else if (gm_level >= atoi(w1))
+ clif_displaymessage(fd, w2);
+ }
+ fclose(fp);
+ } else {
+ clif_displaymessage(fd, msg_table[27]); /* File help.txt not found. */
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/src/map/charcommand.h b/src/map/charcommand.h
new file mode 100644
index 000000000..741d5eb9e
--- /dev/null
+++ b/src/map/charcommand.h
@@ -0,0 +1,68 @@
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#ifndef _CHARCOMMAND_H_
+#define _CHARCOMMAND_H_
+
+enum CharCommandType {
+ CharCommand_None = -1,
+ CharCommandJobChange,
+ CharCommandPetRename,
+ CharCommandPetFriendly,
+ CharCommandReset,
+ CharCommandStats,
+ CharCommandOption,
+ CharCommandSave,
+ CharCommandStatsAll,
+ CharCommandSpiritball,
+ CharCommandItemList,
+ CharCommandEffect,
+ CharCommandStorageList,
+ CharCommandItem, // by MC Cameri
+ CharCommandWarp,
+ CharCommandZeny,
+ CharCommandFakeName,
+ CharCommandBaseLevel,
+ CharCommandJobLevel,
+ CharCommandQuestSkill,
+ CharCommandLostSkill,
+ CharCommandSkReset,
+ CharCommandStReset,
+ CharCommandModel,
+ CharCommandSKPoint,
+ CharCommandSTPoint,
+ CharCommandChangeSex,
+ CharCommandFeelReset, // Komurka
+ CharCommandHelp,
+
+
+
+#ifdef TXT_ONLY
+/* TXT_ONLY */
+
+/* TXT_ONLY */
+#else
+/* SQL-only */
+
+/* SQL Only */
+#endif
+
+ // End. No more commans after this line.
+ CharCommand_Unknown,
+ CharCommand_MAX
+};
+
+typedef enum CharCommandType CharCommandType;
+typedef struct AtCommandInfo CharCommandInfo;
+
+CharCommandType
+is_charcommand(const int fd, struct map_session_data* sd, const char* message, int gmlvl);
+
+CharCommandType charcommand(
+ struct map_session_data* sd, const int level, const char* message, CharCommandInfo* info);
+int get_charcommand_level(const CharCommandType type);
+
+int charcommand_config_read(const char *cfgName);
+
+#endif
+
diff --git a/src/map/charsave.c b/src/map/charsave.c
new file mode 100644
index 000000000..8afa903b6
--- /dev/null
+++ b/src/map/charsave.c
@@ -0,0 +1,516 @@
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "../common/core.h"
+#include "../common/socket.h"
+#include "../common/timer.h"
+#include "../common/nullpo.h"
+#include "../common/mmo.h"
+#include "../common/strlib.h"
+#include "../common/showmsg.h"
+#include "../common/malloc.h"
+
+#include "charsave.h"
+#include "map.h"
+
+#ifndef TXT_ONLY
+
+struct mmo_charstatus *charsave_loadchar(int charid){
+ int i,j, friends;
+ struct mmo_charstatus *c;
+ char *str_p;
+ friends = 0;
+
+ c = (struct mmo_charstatus *)aMalloc(sizeof(struct mmo_charstatus));
+
+ if(charid <= 0){
+ ShowError("charsave_loadchar() charid <= 0! (%d)", charid);
+ aFree(c);
+ return NULL;
+ }
+
+ //Tested, Mysql 4.1.9+ has no problems with the long query, the buf is 65k big and the sql server needs for it 0.00009 secs on an athlon xp 2400+ WinXP (1GB Mem) .. [Sirius]
+ sprintf(tmp_sql, "SELECT `char_id`,`account_id`,`char_num`,`name`,`class`,`base_level`,`job_level`,`base_exp`,`job_exp`,`zeny`, `str`,`agi`,`vit`,`int`,`dex`,`luk`, `max_hp`,`hp`,`max_sp`,`sp`,`status_point`,`skill_point`, `option`,`karma`,`manner`,`party_id`,`guild_id`,`pet_id`,`hair`,`hair_color`, `clothes_color`,`weapon`,`shield`,`head_top`,`head_mid`,`head_bottom`, `last_map`,`last_x`,`last_y`,`save_map`,`save_x`,`save_y`, `partner_id`, `father`, `mother`, `child`, `fame` FROM `char` WHERE `char_id` = '%d'", charid);
+ if(mysql_query(&charsql_handle, tmp_sql)){
+ ShowSQL("DB error - %s\n",mysql_error(&charsql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ aFree(c);
+ return NULL;
+ }
+
+ charsql_res = mysql_store_result(&charsql_handle);
+ if(mysql_num_rows(charsql_res) <= 0){
+ ShowWarning("charsave_loadchar() -> CHARACTER NOT FOUND! (id: %d)\n", charid);
+ mysql_free_result(charsql_res);
+ aFree(c);
+ return NULL;
+ }
+
+ //fetch data
+ charsql_row = mysql_fetch_row(charsql_res);
+
+ //fill with data
+ c->char_id = charid;
+ c->account_id = atoi(charsql_row[1]);
+ c->char_num = atoi(charsql_row[2]);
+ strcpy(c->name, charsql_row[3]);
+ c->class_ = atoi(charsql_row[4]);
+ c->base_level = atoi(charsql_row[5]);
+ c->job_level = atoi(charsql_row[6]);
+ c->base_exp = atoi(charsql_row[7]);
+ c->job_exp = atoi(charsql_row[8]);
+ c->zeny = atoi(charsql_row[9]);
+ c->str = atoi(charsql_row[10]);
+ c->agi = atoi(charsql_row[11]);
+ c->vit = atoi(charsql_row[12]);
+ c->int_ = atoi(charsql_row[13]);
+ c->dex = atoi(charsql_row[14]);
+ c->luk = atoi(charsql_row[15]);
+ c->max_hp = atoi(charsql_row[16]);
+ c->hp = atoi(charsql_row[17]);
+ c->max_sp = atoi(charsql_row[18]);
+ c->sp = atoi(charsql_row[19]);
+ c->status_point = atoi(charsql_row[20]);
+ c->skill_point = atoi(charsql_row[21]);
+ c->option = atoi(charsql_row[22]);
+ c->karma = atoi(charsql_row[23]);
+ c->manner = atoi(charsql_row[24]);
+ c->party_id = atoi(charsql_row[25]);
+ c->guild_id = atoi(charsql_row[26]);
+ c->pet_id = atoi(charsql_row[27]);
+ c->hair = atoi(charsql_row[28]);
+ c->hair_color = atoi(charsql_row[29]);
+ c->clothes_color = atoi(charsql_row[30]);
+ c->weapon = atoi(charsql_row[31]);
+ c->shield = atoi(charsql_row[32]);
+ c->head_top = atoi(charsql_row[33]);
+ c->head_mid = atoi(charsql_row[34]);
+ c->head_bottom = atoi(charsql_row[35]);
+ c->last_point.map = mapindex_name2id(charsql_row[36]);
+ c->last_point.x = atoi(charsql_row[37]);
+ c->last_point.y = atoi(charsql_row[38]);
+ c->save_point.map = mapindex_name2id(charsql_row[39]);
+ c->save_point.x = atoi(charsql_row[40]);
+ c->save_point.y = atoi(charsql_row[41]);
+ c->partner_id = atoi(charsql_row[42]);
+ c->father = atoi(charsql_row[43]);
+ c->mother = atoi(charsql_row[44]);
+ c->child = atoi(charsql_row[45]);
+ c->fame = atoi(charsql_row[46]);
+
+ mysql_free_result(charsql_res);
+
+ //Check for '0' Savepoint / LastPoint
+ if (c->last_point.x == 0 || c->last_point.y == 0 || c->last_point.map == 0){
+ c->last_point.map = mapindex_name2id(MAP_PRONTERA);
+ c->last_point.x = 100;
+ c->last_point.y = 100;
+ }
+
+ if (c->save_point.x == 0 || c->save_point.y == 0 || c->save_point.map == 0){
+ c->save_point.map = mapindex_name2id(MAP_PRONTERA);
+ c->save_point.x = 100;
+ c->save_point.y = 100;
+ }
+
+
+ //read the memo points
+ sprintf(tmp_sql, "SELECT `memo_id`, `char_id`, `map`, `x`, `y` FROM `memo` WHERE `char_id` = '%d' ORDER BY `memo_id`", charid);
+ if(mysql_query(&charsql_handle, tmp_sql)){
+ ShowSQL("DB error - %s\n",mysql_error(&charsql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ aFree(c);
+ return NULL;
+ }
+
+ charsql_res = mysql_store_result(&charsql_handle);
+ if(charsql_res){
+ for(i = 0; (charsql_row = mysql_fetch_row(charsql_res)); i++){
+ c->memo_point[i].map = mapindex_name2id(charsql_row[2]);
+ c->memo_point[i].x = atoi(charsql_row[3]);
+ c->memo_point[i].y = atoi(charsql_row[4]);
+ }
+ mysql_free_result(charsql_res);
+ }
+
+ //read inventory...
+ str_p = tmp_sql;
+ str_p += sprintf(str_p, "SELECT `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`");
+ for (i = 0; i < MAX_SLOTS; i++)
+ str_p += sprintf(str_p, ", `card%d`", i);
+ str_p += sprintf(str_p, " FROM `inventory` WHERE `char_id` = '%d'", charid);
+ if(mysql_query(&charsql_handle, tmp_sql)){
+ ShowSQL("DB error - %s\n",mysql_error(&charsql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ aFree(c);
+ return NULL;
+ }
+
+ charsql_res = mysql_store_result(&charsql_handle);
+ if(charsql_res){
+ for(i = 0; (charsql_row = mysql_fetch_row(charsql_res)); i++){
+ //c->inventory[i].id = atoi(charsql_row[0]);
+ c->inventory[i].nameid = atoi(charsql_row[0]);
+ c->inventory[i].amount = atoi(charsql_row[1]);
+ c->inventory[i].equip = atoi(charsql_row[2]);
+ c->inventory[i].identify = atoi(charsql_row[3]);
+ c->inventory[i].refine = atoi(charsql_row[4]);
+ c->inventory[i].attribute = atoi(charsql_row[5]);
+ for (j = 0; j < MAX_SLOTS; j++)
+ c->inventory[i].card[j] = atoi(charsql_row[6+j]);
+ }
+ mysql_free_result(charsql_res);
+ }
+
+
+ //cart inventory ..
+ str_p = tmp_sql;
+ str_p += sprintf(str_p, "SELECT `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`");
+ for (i = 0; i < MAX_SLOTS; i++)
+ str_p += sprintf(str_p, ", `card%d`", i);
+ str_p += sprintf(str_p, " FROM `cart_inventory` WHERE `char_id` = '%d'", charid);
+
+ if(mysql_query(&charsql_handle, tmp_sql)){
+ ShowSQL("DB error - %s\n",mysql_error(&charsql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ aFree(c);
+ return NULL;
+ }
+
+ charsql_res = mysql_store_result(&charsql_handle);
+ if(charsql_res){
+ for(i = 0; (charsql_row = mysql_fetch_row(charsql_res)); i++){
+ //c->cart[i].id = atoi(charsql_row[0]);
+ c->cart[i].nameid = atoi(charsql_row[0]);
+ c->cart[i].amount = atoi(charsql_row[1]);
+ c->cart[i].equip = atoi(charsql_row[2]);
+ c->cart[i].identify = atoi(charsql_row[3]);
+ c->cart[i].refine = atoi(charsql_row[4]);
+ c->cart[i].attribute = atoi(charsql_row[5]);
+ for (j = 0; j < MAX_SLOTS; j++)
+ c->cart[i].card[j] = atoi(charsql_row[6+j]);
+ }
+ mysql_free_result(charsql_res);
+ }
+
+
+ //Skills...
+ sprintf(tmp_sql, "SELECT `char_id`, `id`, `lv` FROM `skill` WHERE `char_id` = '%d'", charid);
+ if(mysql_query(&charsql_handle, tmp_sql)){
+ ShowSQL("DB error - %s\n",mysql_error(&charsql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ aFree(c);
+ return NULL;
+ }
+
+ charsql_res = mysql_store_result(&charsql_handle);
+ if(charsql_res){
+ while((charsql_row = mysql_fetch_row(charsql_res))){
+ i = atoi(charsql_row[1]);
+ c->skill[i].id = i;
+ c->skill[i].lv = atoi(charsql_row[2]);
+ }
+ mysql_free_result(charsql_res);
+ }
+/* Reg values are handled by the char server.
+ //Global REG
+ sprintf(tmp_sql, "SELECT `char_id`, `str`, `value` FROM `global_reg_value` WHERE `type` = '3' AND `char_id` = '%d'", charid);
+ if(mysql_query(&charsql_handle, tmp_sql)){
+ ShowSQL("DB error - %s\n",mysql_error(&charsql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ aFree(c);
+ return NULL;
+ }
+
+ charsql_res = mysql_store_result(&charsql_handle);
+ if(charsql_res){
+ for(i = 0; (charsql_row = mysql_fetch_row(charsql_res)); i++){
+ strcpy(c->global_reg[i].str, charsql_row[1]);
+ strcpy(c->global_reg[i].value, charsql_row[2]);
+ }
+ mysql_free_result(charsql_res);
+ c->global_reg_num = i;
+ }
+*/
+ //Shamelessly stolen from its_sparky (ie: thanks) and then assimilated by [Skotlex]
+ //Friend list
+ sprintf(tmp_sql, "SELECT f.friend_account, f.friend_id, c.name FROM friends f LEFT JOIN `char` c ON f.friend_account=c.account_id AND f.friend_id=c.char_id WHERE f.char_id='%d'", charid);
+
+ if(mysql_query(&charsql_handle, tmp_sql)){
+ ShowSQL("DB error - %s\n",mysql_error(&charsql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ sql_res = NULL; //To avoid trying to read data.
+ }
+ else
+ sql_res = mysql_store_result(&charsql_handle);
+
+ if(sql_res)
+ {
+ for(i = 0; (sql_row = mysql_fetch_row(sql_res)) && i<MAX_FRIENDS; i++)
+ {
+ if (sql_row[2] != NULL)
+ {
+ c->friends[i].account_id = atoi(sql_row[0]);
+ c->friends[i].char_id = atoi(sql_row[1]);
+ strncpy(c->friends[i].name, sql_row[2], NAME_LENGTH-1); //The -1 is to avoid losing the ending \0 [Skotlex]
+ }
+ }
+ mysql_free_result(sql_res);
+ }
+
+ ShowInfo("charsql_loadchar(): loading of '%d' (%s) complete.\n", charid, c->name);
+ return c;
+}
+
+int charsave_savechar(int charid, struct mmo_charstatus *c){
+ int i,j;
+ char *str_p;
+// char tmp_str[64];
+// char tmp_str2[512];
+ //First save the 'char'
+ sprintf(tmp_sql ,"UPDATE `char` SET `class`='%d', `base_level`='%d', `job_level`='%d',"
+ "`base_exp`='%d', `job_exp`='%d', `zeny`='%d',"
+ "`max_hp`='%d',`hp`='%d',`max_sp`='%d',`sp`='%d',`status_point`='%d',`skill_point`='%d',"
+ "`str`='%d',`agi`='%d',`vit`='%d',`int`='%d',`dex`='%d',`luk`='%d',"
+ "`option`='%d',`karma`='%d',`manner`='%d',`party_id`='%d',`guild_id`='%d',`pet_id`='%d',"
+ "`hair`='%d',`hair_color`='%d',`clothes_color`='%d',`weapon`='%d',`shield`='%d',`head_top`='%d',`head_mid`='%d',`head_bottom`='%d',"
+ "`last_map`='%s',`last_x`='%d',`last_y`='%d',`save_map`='%s',`save_x`='%d',`save_y`='%d',"
+ "`partner_id`='%d', `father`='%d', `mother`='%d', `child`='%d', `fame`='%d'"
+ "WHERE `account_id`='%d' AND `char_id` = '%d'",
+ c->class_, c->base_level, c->job_level,
+ c->base_exp, c->job_exp, c->zeny,
+ c->max_hp, c->hp, c->max_sp, c->sp, c->status_point, c->skill_point,
+ c->str, c->agi, c->vit, c->int_, c->dex, c->luk,
+ c->option, c->karma, c->manner, c->party_id, c->guild_id, c->pet_id,
+ c->hair, c->hair_color, c->clothes_color,
+ c->weapon, c->shield, c->head_top, c->head_mid, c->head_bottom,
+ mapindex_id2name(c->last_point.map), c->last_point.x, c->last_point.y,
+ mapindex_id2name(c->save_point.map), c->save_point.x, c->save_point.y, c->partner_id, c->father, c->mother,
+ c->child, c->fame, c->account_id, c->char_id
+ );
+ if(mysql_query(&charsql_handle, tmp_sql)){
+ ShowSQL("DB error - %s\n",mysql_error(&charsql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+
+
+ //Save the inventory
+ sprintf(tmp_sql, "DELETE FROM `inventory` WHERE `char_id` = '%d'", charid);
+ if(mysql_query(&charsql_handle, tmp_sql)){
+ ShowSQL("DB error - %s\n",mysql_error(&charsql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ for(i = 0; i < MAX_INVENTORY; i++){
+ if(c->inventory[i].nameid > 0){
+ str_p = tmp_sql;
+ str_p += sprintf(str_p, "INSERT INTO `inventory` (`char_id`, `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`");
+ for (j = 0; j < MAX_SLOTS; j++)
+ str_p += sprintf(str_p, ", `card%d`", j);
+
+ str_p += sprintf(str_p, ") VALUES ('%d', '%d', '%d', '%d', '%d', '%d', '%d'",
+ charid, c->inventory[i].nameid, c->inventory[i].amount, c->inventory[i].equip,
+ c->inventory[i].identify, c->inventory[i].refine, c->inventory[i].attribute);
+
+ for (j = 0; j < MAX_SLOTS; j++)
+ str_p += sprintf(str_p, ", '%d'", c->inventory[i].card[j]);
+
+ strcat(tmp_sql,")");
+
+ if(mysql_query(&charsql_handle, tmp_sql)){
+ ShowSQL("DB error - %s\n",mysql_error(&charsql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ }
+ }
+
+ //Save the cart
+ sprintf(tmp_sql, "DELETE FROM `cart_inventory` WHERE `char_id` = '%d'", charid);
+ if(mysql_query(&charsql_handle, tmp_sql)){
+ ShowSQL("DB error - %s\n",mysql_error(&charsql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ for(i = 0; i < MAX_CART; i++){
+ if(c->cart[i].nameid > 0){
+ str_p = tmp_sql;
+ str_p += sprintf(str_p, "INSERT INTO `cart_inventory` (`char_id`, `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`");
+ for (j = 0; j < MAX_SLOTS; j++)
+ str_p += sprintf(str_p, ", `card%d`", j);
+
+ str_p += sprintf(str_p, ") VALUES ('%d', '%d', '%d', '%d', '%d', '%d', '%d'",
+ charid, c->cart[i].nameid, c->cart[i].amount, c->cart[i].equip,
+ c->cart[i].identify, c->cart[i].refine, c->cart[i].attribute);
+
+ for (j = 0; j < MAX_SLOTS; j++)
+ str_p += sprintf(str_p, ", '%d'", c->cart[i].card[j]);
+
+ strcat(tmp_sql,")");
+
+ if(mysql_query(&charsql_handle, tmp_sql)){
+ ShowSQL("DB error - %s\n",mysql_error(&charsql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ }
+ }
+
+
+ //Save memo points
+ sprintf(tmp_sql, "DELETE FROM `memo` WHERE `char_id` = '%d'", charid);
+ if(mysql_query(&charsql_handle, tmp_sql)){
+ ShowSQL("DB error - %s\n",mysql_error(&charsql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ for(i = 0; i < MAX_MEMOPOINTS; i++){
+ if(c->memo_point[i].map && c->memo_point[i].x > 0 && c->memo_point[i].y > 0){
+ sprintf(tmp_sql, "INSERT INTO `memo` ( `char_id`, `map`, `x`, `y` ) VALUES ('%d', '%s', '%d', '%d')", charid, mapindex_id2name(c->memo_point[i].map), c->memo_point[i].x, c->memo_point[i].y);
+ if(mysql_query(&charsql_handle, tmp_sql)){
+ ShowSQL("DB error - %s\n",mysql_error(&charsql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ }
+ }
+
+
+ //Save skills
+ sprintf(tmp_sql, "DELETE FROM `skill` WHERE `char_id` = '%d'", charid);
+ if(mysql_query(&charsql_handle, tmp_sql)){
+ ShowSQL("DB error - %s\n",mysql_error(&charsql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ for(i = 0; i < MAX_SKILL; i++){
+ if(c->skill[i].id > 0){
+ sprintf(tmp_sql, "INSERT INTO `skill` (`char_id`, `id`, `lv`) VALUES ('%d', '%d', '%d')", charid, c->skill[i].id, c->skill[i].lv);
+ if(mysql_query(&charsql_handle, tmp_sql)){
+ ShowSQL("DB error - %s\n",mysql_error(&charsql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ }
+ }
+
+
+/* Reg values are handled by the char server.
+ //global_reg_value saving
+ sprintf(tmp_sql, "DELETE FROM `global_reg_value` WHERE `type`=3 AND `char_id` = '%d'", charid);
+ if(mysql_query(&charsql_handle, tmp_sql)){
+ ShowSQL("DB error - %s\n",mysql_error(&charsql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ for(i = 0; i < c->global_reg_num; i++){
+ if(c->global_reg[i].str){
+ if(c->global_reg[i].value){
+ //jstrescapecpy(tmp_str, c->global_reg[i].str);
+ sprintf(tmp_sql, "INSERT INTO `global_reg_value` (`char_id`, `str`, `value`) VALUES ('%d', '%s', '%s')", charid, jstrescapecpy(tmp_str,c->global_reg[i].str), jstrescapecpy(tmp_str2,c->global_reg[i].value));
+ if(mysql_query(&charsql_handle, tmp_sql)){
+ ShowSQL("DB error - %s\n",mysql_error(&charsql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ }
+ }
+ }
+*/
+
+ //friendlist saving
+ sprintf(tmp_sql, "DELETE FROM `friends` WHERE `char_id` = '%d'", charid);
+ if(mysql_query(&charsql_handle, tmp_sql)){
+ ShowSQL("DB error - %s\n",mysql_error(&charsql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ for(i = 0; i < MAX_FRIENDS; i++){
+ if(c->friends[i].char_id > 0){
+ sprintf(tmp_sql, "INSERT INTO `friends` (`char_id`, `friend_account`, `friend_id`) VALUES ('%d','%d','%d')", charid, c->friends[i].account_id, c->friends[i].char_id);
+ if(mysql_query(&charsql_handle, tmp_sql)){
+ ShowSQL("DB error - %s\n",mysql_error(&charsql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ }
+ }
+
+ ShowInfo("charsql_savechar(): saving of '%d' (%s) complete.\n", charid, c->name);
+ return 0;
+}
+
+int charsave_load_scdata(int account_id, int char_id)
+{ //Loads character's sc_data
+ struct map_session_data *sd;
+
+ sd = map_id2sd(account_id);
+ if (!sd)
+ {
+ ShowError("charsave_load_scdata: Player of AID %d not found!\n", account_id);
+ return -1;
+ }
+ if (sd->status.char_id != char_id)
+ {
+ ShowError("charsave_load_scdata: Receiving data for account %d, char id does not matches (%d != %d)!\n", account_id, sd->status.char_id, char_id);
+ return -1;
+ }
+ sprintf(tmp_sql, "SELECT `type`, `tick`, `val1`, `val2`, `val3`, `val4` FROM `sc_data`"
+ "WHERE `account_id`='%d' AND `char_id`='%d'", account_id, char_id);
+
+ if(mysql_query(&charsql_handle, tmp_sql)){
+ ShowSQL("DB error - %s\n",mysql_error(&charsql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ return -1;
+ }
+
+ sql_res = mysql_store_result(&charsql_handle);
+ if(sql_res)
+ {
+ while ((sql_row = mysql_fetch_row(sql_res)))
+ {
+ if (atoi(sql_row[1]) < 1)
+ { //Protection against invalid tick values. [Skotlex]
+ ShowWarning("charsave_load_scdata: Received invalid duration (%d ms) for status change %d (character %s)\n", atoi(sql_row[1]), sd->status.name);
+ continue;
+ }
+
+ status_change_start(&sd->bl, atoi(sql_row[0]), atoi(sql_row[2]), atoi(sql_row[3]),
+ atoi(sql_row[4]), atoi(sql_row[5]), atoi(sql_row[1]), 7);
+ }
+ }
+
+ //Once loaded, sc_data must be disposed.
+ sprintf(tmp_sql, "DELETE FROM `sc_data` WHERE `account_id`='%d' AND `char_id`='%d'", account_id, char_id);
+ if(mysql_query(&charsql_handle, tmp_sql)){
+ ShowSQL("DB error - %s\n",mysql_error(&charsql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ return 0;
+}
+
+void charsave_save_scdata(int account_id, int char_id, struct status_change* sc_data, int max_sc)
+{ //Saves character's sc_data.
+ int i,count =0;
+ struct TimerData *timer;
+ unsigned int tick = gettick();
+
+ sprintf(tmp_sql, "INSERT INTO `sc_data` (`account_id`, `char_id`, `type`, `tick`, `val1`, `val2`, `val3`, `val4`) VALUES ");
+
+ for(i = 0; i < max_sc; i++)
+ {
+ if (sc_data[i].timer == -1)
+ continue;
+ timer = get_timer(sc_data[i].timer);
+ if (timer == NULL || timer->func != status_change_timer || DIFF_TICK(timer->tick,tick) < 0)
+ continue;
+
+ sprintf (tmp_sql, "%s ('%d','%d','%hu','%d','%d','%d','%d','%d'),", tmp_sql, account_id, char_id,
+ i, DIFF_TICK(timer->tick,tick), sc_data[i].val1, sc_data[i].val2, sc_data[i].val3, sc_data[i].val4);
+
+ count++;
+ }
+ if (count > 0)
+ {
+ tmp_sql[strlen(tmp_sql)-1] = '\0'; //Remove the trailing comma.
+ if(mysql_query(&charsql_handle, tmp_sql)){
+ ShowSQL("DB error - %s\n",mysql_error(&charsql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ }
+ ShowInfo("charsql_save_scdata(): saved %d status changes of '%d:%d'.\n", count, account_id, char_id);
+ return;
+}
+#endif
diff --git a/src/map/charsave.h b/src/map/charsave.h
new file mode 100644
index 000000000..6fa119e14
--- /dev/null
+++ b/src/map/charsave.h
@@ -0,0 +1,16 @@
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#ifndef _CHARSAVE_H_
+#define _CHARSAVE_H_
+
+#include "status.h"
+
+#ifndef TXT_ONLY
+ struct mmo_charstatus *charsave_loadchar(int charid);
+ int charsave_savechar(int charid, struct mmo_charstatus *c);
+ int charsave_load_scdata(int account_id, int char_id);
+ void charsave_save_scdata(int account_id, int char_id, struct status_change* sc_data, int max_sc);
+#endif
+
+#endif
diff --git a/src/map/chat.c b/src/map/chat.c
new file mode 100644
index 000000000..821cd0858
--- /dev/null
+++ b/src/map/chat.c
@@ -0,0 +1,371 @@
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#include <stdio.h>
+#include <string.h>
+
+#include "../common/nullpo.h"
+#include "../common/malloc.h"
+#include "battle.h"
+#include "chat.h"
+#include "map.h"
+#include "clif.h"
+#include "pc.h"
+#include "npc.h"
+
+int chat_triggerevent(struct chat_data *cd);
+
+/*==========================================
+ * チャットルーム作成
+ *------------------------------------------
+ */
+int chat_createchat(struct map_session_data *sd,int limit,int pub,char* pass,char* title,int titlelen)
+{
+ struct chat_data *cd;
+
+ nullpo_retr(0, sd);
+
+ if (sd->chatID)
+ return 0; //Prevent people abusing the chat system by creating multiple chats, as pointed out by End of Exam. [Skotlex]
+
+ cd = (struct chat_data *) aCalloc(1,sizeof(struct chat_data));
+
+ cd->limit = limit;
+ cd->pub = pub;
+ cd->users = 1;
+ memcpy(cd->pass,pass,8);
+ cd->pass[7]= '\0'; //Overflow check... [Skotlex]
+ if(titlelen>=sizeof(cd->title)-1) titlelen=sizeof(cd->title)-1;
+ memcpy(cd->title,title,titlelen);
+ cd->title[titlelen]=0;
+
+ cd->owner = (struct block_list **)(&cd->usersd[0]);
+ cd->usersd[0] = sd;
+ cd->bl.m = sd->bl.m;
+ cd->bl.x = sd->bl.x;
+ cd->bl.y = sd->bl.y;
+ cd->bl.type = BL_CHAT;
+
+ cd->bl.id = map_addobject(&cd->bl);
+ if(cd->bl.id==0){
+ clif_createchat(sd,1);
+ aFree(cd);
+ return 0;
+ }
+ pc_setchatid(sd,cd->bl.id);
+
+ clif_createchat(sd,0);
+ clif_dispchat(cd,0);
+
+ return 0;
+}
+
+/*==========================================
+ * 既存チャットルームに参加
+ *------------------------------------------
+ */
+int chat_joinchat (struct map_session_data *sd, int chatid, char* pass)
+{
+ struct chat_data *cd;
+
+ nullpo_retr(0, sd);
+ cd = (struct chat_data*)map_id2bl(chatid);
+
+ //No need for a nullpo check. The chatid was sent by the client, if they lag or mess with the packet
+ //a wrong chat id can be received. [Skotlex]
+ if (cd == NULL)
+ return 1;
+ if (cd->bl.m != sd->bl.m || sd->vender_id || sd->chatID || cd->limit <= cd->users) {
+ clif_joinchatfail(sd,0);
+ return 0;
+ }
+ //Allows Gm access to protected room with any password they want by valaris
+ if ((cd->pub == 0 && strncmp(pass, (char *)cd->pass, 8) && (pc_isGM(sd) < battle_config.gm_join_chat || !battle_config.gm_join_chat)) ||
+ chatid == (int)sd->chatID) //Double Chat fix by Alex14, thx CHaNGeTe
+ {
+ clif_joinchatfail(sd,1);
+ return 0;
+ }
+
+ cd->usersd[cd->users] = sd;
+ cd->users++;
+
+ pc_setchatid(sd,cd->bl.id);
+
+ clif_joinchatok(sd,cd); // 新たに参加した人には全員のリスト
+ clif_addchat(cd,sd); // 既に中に居た人には追加した人の報告
+ clif_dispchat(cd,0); // 周囲の人には人数変化報告
+
+ chat_triggerevent(cd); // イベント
+
+ return 0;
+}
+
+/*==========================================
+ * チャットルームから抜ける
+ *------------------------------------------
+ */
+int chat_leavechat(struct map_session_data *sd)
+{
+ struct chat_data *cd;
+ int i,leavechar;
+
+ nullpo_retr(1, sd);
+
+ cd=(struct chat_data*)map_id2bl(sd->chatID);
+ if(cd==NULL)
+ return 1;
+
+ for(i = 0,leavechar=-1;i < cd->users;i++){
+ if(cd->usersd[i] == sd){
+ leavechar=i;
+ break;
+ }
+ }
+ if(leavechar<0) // そのchatに所属していないらしい (バグ時のみ)
+ return -1;
+
+ if(leavechar==0 && cd->users>1 && (*cd->owner)->type==BL_PC){
+ // 所有者だった&他に人が居る&PCのチャット
+ clif_changechatowner(cd,cd->usersd[1]);
+ clif_clearchat(cd,0);
+ }
+
+ // 抜けるPCにも送るのでusersを減らす前に実行
+ clif_leavechat(cd,sd);
+
+ cd->users--;
+ pc_setchatid(sd,0);
+
+ if(cd->users == 0 && (*cd->owner)->type==BL_PC){
+ // 全員居なくなった&PCのチャットなので消す
+ clif_clearchat(cd,0);
+ map_delobject(cd->bl.id); // freeまでしてくれる
+ } else {
+ for(i=leavechar;i < cd->users;i++)
+ cd->usersd[i] = cd->usersd[i+1];
+ if(leavechar==0 && (*cd->owner)->type==BL_PC){
+ // PCのチャットなので所有者が抜けたので位置変更
+ cd->bl.x=cd->usersd[0]->bl.x;
+ cd->bl.y=cd->usersd[0]->bl.y;
+ }
+ clif_dispchat(cd,0);
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * チャットルームの持ち主を譲る
+ *------------------------------------------
+ */
+int chat_changechatowner(struct map_session_data *sd,char *nextownername)
+{
+ struct chat_data *cd;
+ struct map_session_data *tmp_sd;
+ int i, nextowner;
+
+ nullpo_retr(1, sd);
+
+ cd = (struct chat_data*)map_id2bl(sd->chatID);
+ if (cd == NULL || (struct block_list *)sd != (*cd->owner))
+ return 1;
+
+ for(i = 1,nextowner=-1;i < cd->users;i++){
+ if(strcmp(cd->usersd[i]->status.name,nextownername)==0){
+ nextowner=i;
+ break;
+ }
+ }
+ if(nextowner<0) // そんな人は居ない
+ return -1;
+
+ clif_changechatowner(cd,cd->usersd[nextowner]);
+ // 一旦消す
+ clif_clearchat(cd,0);
+
+ // userlistの順番変更 (0が所有者なので)
+ if( (tmp_sd = cd->usersd[0]) == NULL )
+ return 1; //ありえるのかな?
+ cd->usersd[0] = cd->usersd[nextowner];
+ cd->usersd[nextowner] = tmp_sd;
+
+ // 新しい所有者の位置へ変更
+ cd->bl.x=cd->usersd[0]->bl.x;
+ cd->bl.y=cd->usersd[0]->bl.y;
+
+ // 再度表示
+ clif_dispchat(cd,0);
+
+ return 0;
+}
+
+/*==========================================
+ * チャットの状態(タイトル等)を変更
+ *------------------------------------------
+ */
+int chat_changechatstatus(struct map_session_data *sd,int limit,int pub,char* pass,char* title,int titlelen)
+{
+ struct chat_data *cd;
+
+ nullpo_retr(1, sd);
+
+ cd=(struct chat_data*)map_id2bl(sd->chatID);
+ if(cd==NULL || (struct block_list *)sd != (*cd->owner))
+ return 1;
+
+ cd->limit = limit;
+ cd->pub = pub;
+ memcpy(cd->pass,pass,8);
+ cd->pass[7]= '\0'; //Overflow check... [Skotlex]
+ if(titlelen>=sizeof(cd->title)-1) titlelen=sizeof(cd->title)-1;
+ memcpy(cd->title,title,titlelen);
+ cd->title[titlelen]=0;
+
+ clif_changechatstatus(cd);
+ clif_dispchat(cd,0);
+
+ return 0;
+}
+
+/*==========================================
+ * チャットルームから蹴り出す
+ *------------------------------------------
+ */
+int chat_kickchat(struct map_session_data *sd,char *kickusername)
+{
+ struct chat_data *cd;
+ int i;
+
+ nullpo_retr(1, sd);
+
+ cd = (struct chat_data *)map_id2bl(sd->chatID);
+
+ for(i = 0; i < cd->users; i++) {
+ if (strcmp(cd->usersd[i]->status.name, kickusername) == 0) {
+ if (battle_config.gm_kick_chat && pc_isGM(cd->usersd[i]) >= battle_config.gm_kick_chat)
+ //gm kick protection by valaris
+ return 0;
+
+ chat_leavechat(cd->usersd[i]);
+ return 0;
+ }
+ }
+
+ return -1;
+}
+
+/*==========================================
+ * npcチャットルーム作成
+ *------------------------------------------
+ */
+int chat_createnpcchat(struct npc_data *nd,int limit,int pub,int trigger,char* title,int titlelen,const char *ev)
+{
+ struct chat_data *cd;
+
+ nullpo_retr(1, nd);
+
+ cd = (struct chat_data *) aCalloc(1,sizeof(struct chat_data));
+
+ cd->limit = cd->trigger = limit;
+ if(trigger>0)
+ cd->trigger = trigger;
+ cd->pub = pub;
+ cd->users = 0;
+ memcpy(cd->pass,"",1);
+ if(titlelen>=sizeof(cd->title)-1) titlelen=sizeof(cd->title)-1;
+ memcpy(cd->title,title,titlelen);
+ cd->title[titlelen]=0;
+
+ cd->bl.m = nd->bl.m;
+ cd->bl.x = nd->bl.x;
+ cd->bl.y = nd->bl.y;
+ cd->bl.type = BL_CHAT;
+ cd->owner_ = (struct block_list *)nd;
+ cd->owner = &cd->owner_;
+ if (strlen(ev) > 49)
+ { //npc_event is a char[50] [Skotlex]
+ memcpy(cd->npc_event,ev,49);
+ cd->npc_event[49] = '\0';
+ } else
+ memcpy(cd->npc_event,ev,strlen(ev));
+
+ cd->bl.id = map_addobject(&cd->bl);
+ if(cd->bl.id==0){
+ aFree(cd);
+ return 0;
+ }
+ nd->chat_id=cd->bl.id;
+
+ clif_dispchat(cd,0);
+
+ return 0;
+}
+/*==========================================
+ * npcチャットルーム削除
+ *------------------------------------------
+ */
+int chat_deletenpcchat(struct npc_data *nd)
+{
+ struct chat_data *cd;
+
+ nullpo_retr(0, nd);
+ nullpo_retr(0, cd=(struct chat_data*)map_id2bl(nd->chat_id));
+
+ chat_npckickall(cd);
+ clif_clearchat(cd,0);
+ map_delobject(cd->bl.id); // freeまでしてくれる
+ nd->chat_id=0;
+
+ return 0;
+}
+
+/*==========================================
+ * 規定人数以上でイベントが定義されてるなら実行
+ *------------------------------------------
+ */
+int chat_triggerevent(struct chat_data *cd)
+{
+ nullpo_retr(0, cd);
+
+ if(cd->users>=cd->trigger && cd->npc_event[0])
+ npc_event_do(cd->npc_event);
+ return 0;
+}
+
+/*==========================================
+ * イベントの有効化
+ *------------------------------------------
+ */
+int chat_enableevent(struct chat_data *cd)
+{
+ nullpo_retr(0, cd);
+
+ cd->trigger&=0x7f;
+ chat_triggerevent(cd);
+ return 0;
+}
+/*==========================================
+ * イベントの無効化
+ *------------------------------------------
+ */
+int chat_disableevent(struct chat_data *cd)
+{
+ nullpo_retr(0, cd);
+
+ cd->trigger|=0x80;
+ return 0;
+}
+/*==========================================
+ * チャットルームから全員蹴り出す
+ *------------------------------------------
+ */
+int chat_npckickall(struct chat_data *cd)
+{
+ nullpo_retr(0, cd);
+
+ while(cd->users>0){
+ chat_leavechat(cd->usersd[cd->users-1]);
+ }
+ return 0;
+}
diff --git a/src/map/chat.h b/src/map/chat.h
new file mode 100644
index 000000000..1251ad98c
--- /dev/null
+++ b/src/map/chat.h
@@ -0,0 +1,22 @@
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#ifndef _CHAT_H_
+#define _CHAT_H_
+
+#include "map.h"
+
+int chat_createchat(struct map_session_data *,int,int,char*,char*,int);
+int chat_joinchat(struct map_session_data *,int,char*);
+int chat_leavechat(struct map_session_data* );
+int chat_changechatowner(struct map_session_data *,char *);
+int chat_changechatstatus(struct map_session_data *,int,int,char*,char*,int);
+int chat_kickchat(struct map_session_data *,char *);
+
+int chat_createnpcchat(struct npc_data *nd,int limit,int pub,int trigger,char* title,int titlelen,const char *ev);
+int chat_deletenpcchat(struct npc_data *nd);
+int chat_enableevent(struct chat_data *cd);
+int chat_disableevent(struct chat_data *cd);
+int chat_npckickall(struct chat_data *cd);
+
+#endif
diff --git a/src/map/chrif.c b/src/map/chrif.c
new file mode 100644
index 000000000..2ae29ffbd
--- /dev/null
+++ b/src/map/chrif.c
@@ -0,0 +1,1572 @@
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#ifdef _WIN32
+#include <winsock.h>
+#else
+#include <unistd.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#endif
+#include <sys/types.h>
+#include <time.h>
+
+#include "../common/malloc.h"
+#include "socket.h"
+#include "timer.h"
+#include "map.h"
+#include "battle.h"
+#include "chrif.h"
+#include "clif.h"
+#include "intif.h"
+#include "npc.h"
+#include "pc.h"
+#include "status.h"
+#include "nullpo.h"
+#include "showmsg.h"
+#ifndef TXT_ONLY
+#include "charsave.h"
+#endif
+//Updated table (only doc^^) [Sirius]
+//Used Packets: U->2af8
+//Free Packets: F->2af8
+
+struct dbt *auth_db;
+
+static const int packet_len_table[0x3d] = {
+ 60, 3,-1,27,10,-1, 6,-1, // 2af8-2aff: U->2af8, U->2af9, U->2afa, U->2afb, U->2afc, U->2afd, U->2afe, U->2aff
+ 6,-1,18, 7,-1,49,30,10, // 2b00-2b07: U->2b00, U->2b01, U->2b02, U->2b03, U->2b04, U->2b05, U->2b06, U->2b07
+ 6,30,-1,10,86, 7,44,34, // 2b08-2b0f: U->2b08, U->2b09, U->2b0a, U->2b0b, U->2b0c, U->2b0d, U->2b0e, U->2b0f
+ 0,-1,10, 6,11,-1, 0, 0, // 2b10-2b17: U->2b10, U->2b11, U->2b12, U->2b13, U->2b14, U->2b15, U->2b16, U->2b17
+ -1,-1,-1,-1,-1,-1,-1, 7, // 2b18-2b1f: U->2b18, U->2b19, U->2b1a, U->2b1b, U->2b1c, U->2b1d, F->2b1e, U->2b1f
+ -1,-1,-1,-1,-1,-1,-1,-1, // 2b20-2b27: U->2b20, F->2b21, F->2b22, F->2b23, F->2b24, F->2b25, F->2b26, F->2b27
+};
+
+//Used Packets:
+//2af8: Outgoing, chrif_connect -> 'connect to charserver / auth @ charserver'
+//2af9: Incomming, chrif_connectack -> 'answer of the 2af8 login(ok / fail)'
+//2afa: Outgoing, chrif_sendmap -> 'sending our maps'
+//2afb: Incomming, chrif_sendmapack -> 'Maps received successfully / or not ..'
+//2afc: Outgoing, chrif_scdata_request -> request sc_data for pc_authok'ed char. <- new command reuses previous one.
+//2afd: Incomming, chrif_authok -> 'character selected, add to auth db'
+//2afe: Outgoing, send_usercount_tochar -> 'sends player count of this map server to charserver'
+//2aff: Outgoing, send_users_tochar -> 'sends all actual connected character ids to charserver'
+//2b00: Incomming, map_setusers -> 'set the actual usercount? PACKET.2B COUNT.L.. ?' (not sure)
+//2b01: Outgoing, chrif_save -> 'charsave of char XY account XY (complete struct)'
+//2b02: Outgoing, chrif_charselectreq -> 'player returns from ingame to charserver to select another char.., this packets includes sessid etc' ? (not 100% sure)
+//2b03: Incomming, clif_charselectok -> '' (i think its the packet after enterworld?) (not sure)
+//2b04: Incomming, chrif_recvmap -> 'getting maps from charserver of other mapserver's'
+//2b05: Outgoing, chrif_changemapserver -> 'Tell the charserver the mapchange / quest for ok...'
+//2b06: Incomming, chrif_changemapserverack -> 'awnser of 2b05, ok/fail, data: dunno^^'
+//2b07: Incoming, clif_updatemaxid -> Received when updating the max account/char known
+//2b08: Outgoing, chrif_searchcharid -> '...'
+//2b09: Incomming, map_addchariddb -> 'Adds a name to the nick db'
+//2b0a: Outgoing, chrif_changegm -> 'level change of acc/char XY'
+//2b0b: Incomming, chrif_changedgm -> 'answer of 2b0a..'
+//2b0c: Outgoing, chrif_changeemail -> 'change mail address ...'
+//2b0d: Incomming, chrif_changedsex -> 'Change sex of acc XY'
+//2b0e: Outgoing, chrif_char_ask_name -> 'Do some operations (change sex, ban / unban etc)'
+//2b0f: Incomming, chrif_char_ask_name_answer -> 'answer of the 2b0e'
+//2b10: FREE
+//2b11: Outgoing, chrif_changesex -> 'change sex of acc X'
+//2b12: Incomming, chrif_divorce -> 'divorce a wedding of charid X and partner id X'
+//2b13: Incomming, chrif_accountdeletion -> 'Delete acc XX, if the player is on, kick ....'
+//2b14: Incomming, chrif_accountban -> 'not sure: kick the player with message XY'
+//2b15: Incomming, chrif_recvgmaccounts -> 'recive gm accs from charserver (seems to be incomplete !)'
+//2b16: Outgoing, chrif_ragsrvinfo -> 'sends motd / rates ....'
+//2b17: Outgoing, chrif_char_offline -> 'tell the charserver that the char is now offline'
+//2b18: Outgoing, chrif_char_reset_offline -> 'set all players OFF!'
+//2b19: Outgoing, chrif_char_online -> 'tell the charserver that the char .. is online'
+//2b1a: Outgoing, chrif_reqfamelist -> 'Request the fame list (top10)'
+//2b1b: Incomming, chrif_recvfamelist -> 'answer of 2b1a ..... the famelist top10^^'
+//2b1c: Outgoing, chrif_save_scdata -> 'Send sc_data of player for saving.'
+//2b1d: Incomming, chrif_load_scdata -> 'received sc_data of player for loading.'
+//2b1e: FREE
+//2b1f: Incomming, chrif_disconnectplayer -> 'disconnects a player (aid X) with the message XY ... 0x81 ..' [Sirius]
+//2b20: Incomming, chrif_removemap -> 'remove maps of a server (sample: its going offline)' [Sirius]
+//2b21-2b27: FREE
+
+int chrif_connected;
+int char_fd = 0; //Using 0 instead of -1 is safer against crashes. [Skotlex]
+int srvinfo;
+static char char_ip_str[16];
+static int char_ip;
+static int char_port = 6121;
+static char userid[NAME_LENGTH], passwd[NAME_LENGTH];
+static int chrif_state = 0;
+static int char_init_done = 0;
+//Interval at which map server updates online listing. [Valaris]
+#define CHECK_INTERVAL 3600000
+//Interval at which map server sends number of connected users. [Skotlex]
+#define UPDATE_INTERVAL 10000
+//This define should spare writing the check in every function. [Skotlex]
+#define chrif_check(a) { if(!chrif_isconnect()) return a; }
+
+// 設定ファイル読み込み関係
+/*==========================================
+ *
+ *------------------------------------------
+ */
+void chrif_setuserid(char *id)
+{
+ memcpy(userid, id, NAME_LENGTH);
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+void chrif_setpasswd(char *pwd)
+{
+ memcpy(passwd, pwd, NAME_LENGTH);
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+void chrif_setip(char *ip)
+{
+ memcpy(&char_ip_str, ip, 16);
+ char_ip = inet_addr(char_ip_str);
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+void chrif_setport(int port)
+{
+ char_port = port;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int chrif_isconnect(void)
+{
+ return (char_fd > 0 && session[char_fd] != NULL && chrif_state == 2);
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int chrif_save(struct map_session_data *sd, int flag)
+{
+ nullpo_retr(-1, sd);
+ chrif_check(-1);
+ pc_makesavestatus(sd);
+
+ if (sd->state.finalsave)
+ return -1; //Refuse to save a char already tagged for final saving. [Skotlex]
+ //For data sync
+ if (sd->state.storage_flag == 1)
+ storage_storage_save(sd->status.account_id);
+ else if (sd->state.storage_flag == 2)
+ storage_guild_storagesave(sd->status.account_id, sd->status.guild_id);
+
+ //Saving of registry values.
+ if (sd->state.reg_dirty&4)
+ intif_saveregistry(sd, 3); //Save char regs
+ if (sd->state.reg_dirty&2)
+ intif_saveregistry(sd, 2); //Save account regs
+ if (sd->state.reg_dirty&1)
+ intif_saveregistry(sd, 1); //Save account2 regs
+
+#ifndef TXT_ONLY
+ if(charsave_method){ //New 'Local' save
+ charsave_savechar(sd->char_id, &sd->status);
+ if (flag) chrif_char_offline(sd); //Tell char server that character went offline.
+ }else{
+#endif
+ WFIFOHEAD(char_fd, sizeof(sd->status) + 13);
+ WFIFOW(char_fd,0) = 0x2b01;
+ WFIFOW(char_fd,2) = sizeof(sd->status) + 13;
+ WFIFOL(char_fd,4) = sd->bl.id;
+ WFIFOL(char_fd,8) = sd->char_id;
+ WFIFOB(char_fd,12) = flag?1:0; //Flag to tell char-server this character is quitting.
+ memcpy(WFIFOP(char_fd,13), &sd->status, sizeof(sd->status));
+ WFIFOSET(char_fd, WFIFOW(char_fd,2));
+#ifndef TXT_ONLY
+ }
+#endif
+ if (flag) {//Remove the storage from memory.
+ storage_delete(sd->status.account_id);
+ sd->state.finalsave = 1; //Mark the last save as done.
+ }
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int chrif_connect(int fd)
+{
+ ShowStatus("Logging in to char server...\n", char_fd);
+ WFIFOHEAD(fd, 60);
+ WFIFOW(fd,0) = 0x2af8;
+ memcpy(WFIFOP(fd,2), userid, NAME_LENGTH);
+ memcpy(WFIFOP(fd,26), passwd, NAME_LENGTH);
+ WFIFOL(fd,50) = 0;
+ WFIFOL(fd,54) = clif_getip();
+ WFIFOW(fd,58) = clif_getport(); // [Valaris] thanks to fov
+ WFIFOSET(fd,60);
+
+ return 0;
+}
+
+/*==========================================
+ * マップ送信
+ *------------------------------------------
+ */
+int chrif_sendmap(int fd)
+{
+ int i;
+ ShowStatus("Sending maps to char server...\n");
+ WFIFOHEAD(fd, 4 + map_num * 4);
+ WFIFOW(fd,0) = 0x2afa;
+ for(i = 0; i < map_num; i++)
+ WFIFOW(fd,4+i*4) = map[i].index;
+ WFIFOW(fd,2) = 4 + i * 4;
+ WFIFOSET(fd,WFIFOW(fd,2));
+
+ return 0;
+}
+
+/*==========================================
+ * マップ受信
+ *------------------------------------------
+ */
+int chrif_recvmap(int fd)
+{
+ int i, j, ip, port;
+ unsigned char *p = (unsigned char *)&ip;
+ RFIFOHEAD(fd);
+
+ if (chrif_state < 2) // まだ準備中
+ return -1;
+
+ ip = RFIFOL(fd,4);
+ port = RFIFOW(fd,8);
+ for(i = 10, j = 0; i < RFIFOW(fd,2); i += 4, j++) {
+ map_setipport(RFIFOW(fd,i), ip, port);
+// if (battle_config.etc_log)
+// printf("recv map %d %s\n", j, RFIFOP(fd,i));
+ }
+ if (battle_config.etc_log)
+ ShowStatus("recv map on %d.%d.%d.%d:%d (%d maps)\n", p[0], p[1], p[2], p[3], port, j);
+
+ return 0;
+}
+
+/*==========================================
+ * Delete maps of other servers, (if an other mapserver is going OFF)
+ *------------------------------------------
+ */
+int chrif_removemap(int fd){
+ int i, j, ip, port;
+ unsigned char *p = (unsigned char *)&ip;
+ RFIFOHEAD(fd);
+
+ if(chrif_state < 2){
+ return -1; //i dunno, but i know if its 3 the link is ok^^
+ }
+
+ ip = RFIFOL(fd, 4);
+ port = RFIFOW(fd, 8);
+
+ for(i = 10, j = 0; i < RFIFOW(fd, 2); i += 4, j++){
+ map_eraseipport(RFIFOW(fd, i), ip, port);
+ }
+
+ if(battle_config.etc_log){
+ ShowStatus("remove map of server %d.%d.%d.%d:%d (%d maps)\n", p[0], p[1], p[2], p[3], port, j);
+ }
+ return 0;
+}
+
+/*==========================================
+ * マップ鯖間移動のためのデータ準備要求
+ *------------------------------------------
+ */
+int chrif_changemapserver(struct map_session_data *sd, short map, int x, int y, int ip, short port)
+{
+ int i, s_ip=0;
+
+ nullpo_retr(-1, sd);
+
+ chrif_check(-1);
+
+ s_ip = 0;
+ for(i = 0; i < fd_max; i++)
+ if (session[i] && session[i]->session_data == sd) {
+ s_ip = session[i]->client_addr.sin_addr.s_addr;
+ break;
+ }
+
+ WFIFOHEAD(char_fd, 35);
+ WFIFOW(char_fd, 0) = 0x2b05;
+ WFIFOL(char_fd, 2) = sd->bl.id;
+ WFIFOL(char_fd, 6) = sd->login_id1;
+ WFIFOL(char_fd,10) = sd->login_id2;
+ WFIFOL(char_fd,14) = sd->status.char_id;
+ WFIFOW(char_fd,18) = map;
+ WFIFOW(char_fd,20) = x;
+ WFIFOW(char_fd,22) = y;
+ WFIFOL(char_fd,24) = ip;
+ WFIFOW(char_fd,28) = port;
+ WFIFOB(char_fd,30) = sd->status.sex;
+ WFIFOL(char_fd,31) = s_ip;
+ WFIFOSET(char_fd,35);
+
+ return 0;
+}
+
+/*==========================================
+ * マップ鯖間移動ack
+ *------------------------------------------
+ */
+int chrif_changemapserverack(int fd)
+{
+ struct map_session_data *sd;
+ RFIFOHEAD(fd);
+ sd = map_id2sd(RFIFOL(fd,2));
+
+ if (sd == NULL || sd->status.char_id != RFIFOL(fd,14))
+ return -1;
+
+ if (RFIFOL(fd,6) == 1) {
+ if (battle_config.error_log)
+ ShowError("map server change failed.\n");
+ pc_authfail(sd);
+ return 0;
+ }
+ clif_changemapserver(sd, (char*)mapindex_id2name(RFIFOW(fd,18)), RFIFOW(fd,20), RFIFOW(fd,22), RFIFOL(fd,24), RFIFOW(fd,28));
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int chrif_connectack(int fd)
+{
+ RFIFOHEAD(fd);
+ if (RFIFOB(fd,2)) {
+ ShowFatalError("Connection to char-server failed %d.\n", RFIFOB(fd,2));
+ exit(1);
+ }
+ ShowStatus("Successfully logged on to Char Server (Connection: '"CL_WHITE"%d"CL_RESET"').\n",fd);
+ chrif_state = 1;
+ chrif_connected=1;
+
+ chrif_sendmap(fd);
+
+ ShowStatus("Event '"CL_WHITE"OnCharIfInit"CL_RESET"' executed with '"CL_WHITE"%d"CL_RESET"' NPCs.\n", npc_event_doall("OnCharIfInit"));
+ ShowStatus("Event '"CL_WHITE"OnInterIfInit"CL_RESET"' executed with '"CL_WHITE"%d"CL_RESET"' NPCs.\n", npc_event_doall("OnInterIfInit"));
+ if(!char_init_done) {
+ char_init_done = 1;
+ ShowStatus("Event '"CL_WHITE"OnInterIfInitOnce"CL_RESET"' executed with '"CL_WHITE"%d"CL_RESET"' NPCs.\n", npc_event_doall("OnInterIfInitOnce"));
+ }
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int chrif_sendmapack(int fd)
+{
+ RFIFOHEAD(fd);
+ if (RFIFOB(fd,2)) {
+ ShowFatalError("chrif : send map list to char server failed %d\n", RFIFOB(fd,2));
+ exit(1);
+ }
+ memcpy(wisp_server_name, RFIFOP(fd,3), NAME_LENGTH);
+ ShowStatus("Map sending complete. Map Server is now online.\n");
+ chrif_state = 2;
+
+ //Re-save any storages that were modified in the disconnection time. [Skotlex]
+ do_reconnect_storage();
+
+ return 0;
+}
+
+/*==========================================
+ * Request sc_data from charserver [Skotlex]
+ *------------------------------------------
+ */
+int chrif_scdata_request(int account_id, int char_id)
+{
+#ifdef ENABLE_SC_SAVING
+#ifndef TXT_ONLY
+ if (charsave_method)
+ return charsave_load_scdata(account_id, char_id);
+#endif
+ chrif_check(-1);
+
+ WFIFOHEAD(char_fd, 10);
+ WFIFOW(char_fd, 0) = 0x2afc;
+ WFIFOL(char_fd, 2) = account_id;
+ WFIFOL(char_fd, 6) = char_id;
+ WFIFOSET(char_fd,10);
+#endif
+ return 0;
+}
+
+/*==========================================
+ * new auth system [Kevin]
+ *------------------------------------------
+ */
+void chrif_authreq(struct map_session_data *sd)
+{
+ struct auth_node *auth_data;
+ auth_data=idb_get(auth_db, sd->bl.id);
+
+ if(auth_data) {
+ if(auth_data->char_dat &&
+ auth_data->account_id== sd->bl.id &&
+ auth_data->login_id1 == sd->login_id1)
+ { //auth ok
+ pc_authok(sd, auth_data->login_id2, auth_data->connect_until_time, auth_data->char_dat);
+ chrif_scdata_request(auth_data->account_id, auth_data->char_dat->char_id);
+ } else { //auth failed
+ pc_authfail(sd);
+ chrif_char_offline(sd); //Set him offline, the char server likely has it set as online already.
+ }
+ if (auth_data->char_dat)
+ aFree(auth_data->char_dat);
+ idb_remove(auth_db, sd->bl.id);
+ } else { //data from char server has not arrived yet.
+ auth_data = aCalloc(1, sizeof(struct auth_node));
+ auth_data->sd = sd;
+ auth_data->fd = sd->fd;
+ auth_data->account_id = sd->bl.id;
+ auth_data->login_id1 = sd->login_id1;
+ auth_data->node_created = gettick();
+ idb_put(auth_db, sd->bl.id, auth_data);
+ }
+ return;
+}
+
+//character selected, insert into auth db
+void chrif_authok(int fd) {
+ struct auth_node *auth_data;
+ RFIFOHEAD(fd);
+
+ if (map_id2sd(RFIFOL(fd, 4)) != NULL)
+ //Someone with this account is already in! Do not store the info to prevent possible sync exploits. [Skotlex]
+ return;
+
+ if ((auth_data =uidb_get(auth_db, RFIFOL(fd, 4))) != NULL)
+ { //Is the character already awaiting authorization?
+ if (auth_data->sd)
+ {
+ //First, check to see if the session data still exists (avoid dangling pointers)
+ if(session[auth_data->fd] && session[auth_data->fd]->session_data == auth_data->sd)
+ {
+ if (auth_data->char_dat == NULL &&
+ auth_data->account_id == RFIFOL(fd, 4) &&
+ auth_data->login_id1 == RFIFOL(fd, 8))
+ { //Auth Ok
+ pc_authok(auth_data->sd, RFIFOL(fd, 16), RFIFOL(fd, 12), (struct mmo_charstatus*)RFIFOP(fd, 20));
+ chrif_scdata_request(auth_data->account_id, auth_data->sd->status.char_id);
+ } else { //Auth Failed
+ pc_authfail(auth_data->sd);
+ chrif_char_offline(auth_data->sd); //Set him offline, the char server likely has it set as online already.
+ }
+ } //else: Character no longer exists, just go through.
+ }
+ //Delete the data of this node...
+ if (auth_data->char_dat)
+ aFree (auth_data->char_dat);
+ uidb_remove(auth_db, RFIFOL(fd, 4));
+ return;
+ }
+ // Awaiting for client to connect.
+ auth_data = (struct auth_node *)aCalloc(1, sizeof(struct auth_node));
+ auth_data->char_dat = (struct mmo_charstatus *) aCalloc(1, sizeof(struct mmo_charstatus));
+
+ auth_data->account_id=RFIFOL(fd, 4);
+ auth_data->login_id1=RFIFOL(fd, 8);
+ auth_data->connect_until_time=RFIFOL(fd, 12);
+ auth_data->login_id2=RFIFOL(fd, 16);
+ memcpy(auth_data->char_dat,RFIFOP(fd, 20),sizeof(struct mmo_charstatus));
+ auth_data->node_created=gettick();
+ uidb_put(auth_db, RFIFOL(fd, 4), auth_data);
+}
+
+int auth_db_cleanup_sub(DBKey key,void *data,va_list ap)
+{
+ struct auth_node *node=(struct auth_node*)data;
+
+ if(DIFF_TICK(gettick(),node->node_created)>30000) {
+ ShowNotice("Character (aid: %d) not authed within 30 seconds of character select!\n", node->account_id);
+ if (node->char_dat)
+ aFree(node->char_dat);
+ db_remove(auth_db, key);
+ return 1;
+ }
+ return 0;
+}
+
+int auth_db_cleanup(int tid, unsigned int tick, int id, int data) {
+ auth_db->foreach(auth_db, auth_db_cleanup_sub);
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int chrif_charselectreq(struct map_session_data *sd)
+{
+ int i, s_ip;
+
+ nullpo_retr(-1, sd);
+
+ if( !sd || !sd->bl.id || !sd->login_id1 )
+ return -1;
+ chrif_check(-1);
+
+ s_ip = 0;
+ for(i = 0; i < fd_max; i++)
+ if (session[i] && session[i]->session_data == sd) {
+ s_ip = session[i]->client_addr.sin_addr.s_addr;
+ break;
+ }
+
+ WFIFOHEAD(char_fd, 18);
+ WFIFOW(char_fd, 0) = 0x2b02;
+ WFIFOL(char_fd, 2) = sd->bl.id;
+ WFIFOL(char_fd, 6) = sd->login_id1;
+ WFIFOL(char_fd,10) = sd->login_id2;
+ WFIFOL(char_fd,14) = s_ip;
+ WFIFOSET(char_fd,18);
+
+ return 0;
+}
+
+/*==========================================
+ * キャラ名問い合わせ
+ *------------------------------------------
+ */
+int chrif_searchcharid(int char_id)
+{
+ if( !char_id )
+ return -1;
+ chrif_check(-1);
+
+ WFIFOHEAD(char_fd, 6);
+ WFIFOW(char_fd,0) = 0x2b08;
+ WFIFOL(char_fd,2) = char_id;
+ WFIFOSET(char_fd,6);
+
+ return 0;
+}
+
+/*==========================================
+ * GMに変化要求
+ *------------------------------------------
+ */
+int chrif_changegm(int id, const char *pass, int len)
+{
+ if (battle_config.etc_log)
+ ShowInfo("chrif_changegm: account: %d, password: '%s'.\n", id, pass);
+
+ chrif_check(-1);
+
+ WFIFOHEAD(char_fd, len + 8);
+ WFIFOW(char_fd,0) = 0x2b0a;
+ WFIFOW(char_fd,2) = len + 8;
+ WFIFOL(char_fd,4) = id;
+ memcpy(WFIFOP(char_fd,8), pass, len);
+ WFIFOSET(char_fd, len + 8);
+
+ return 0;
+}
+
+/*==========================================
+ * Change Email
+ *------------------------------------------
+ */
+int chrif_changeemail(int id, const char *actual_email, const char *new_email)
+{
+ if (battle_config.etc_log)
+ ShowInfo("chrif_changeemail: account: %d, actual_email: '%s', new_email: '%s'.\n", id, actual_email, new_email);
+
+ chrif_check(-1);
+
+ WFIFOHEAD(char_fd, 86);
+ WFIFOW(char_fd,0) = 0x2b0c;
+ WFIFOL(char_fd,2) = id;
+ memcpy(WFIFOP(char_fd,6), actual_email, 40);
+ memcpy(WFIFOP(char_fd,46), new_email, 40);
+ WFIFOSET(char_fd,86);
+
+ return 0;
+}
+
+/*==========================================
+ * Send message to char-server with a character name to do some operations (by Yor)
+ * Used to ask Char-server about a character name to have the account number to modify account file in login-server.
+ * type of operation:
+ * 1: block
+ * 2: ban
+ * 3: unblock
+ * 4: unban
+ * 5: changesex
+ *------------------------------------------
+ */
+int chrif_char_ask_name(int id, char * character_name, short operation_type, int year, int month, int day, int hour, int minute, int second)
+{
+ chrif_check(-1);
+
+ WFIFOHEAD(char_fd, 44);
+ WFIFOW(char_fd, 0) = 0x2b0e;
+ WFIFOL(char_fd, 2) = id; // account_id of who ask (for answer) -1 if nobody
+ memcpy(WFIFOP(char_fd,6), character_name, NAME_LENGTH);
+ WFIFOW(char_fd, 30) = operation_type; // type of operation
+ if (operation_type == 2) {
+ WFIFOW(char_fd, 32) = year;
+ WFIFOW(char_fd, 34) = month;
+ WFIFOW(char_fd, 36) = day;
+ WFIFOW(char_fd, 38) = hour;
+ WFIFOW(char_fd, 40) = minute;
+ WFIFOW(char_fd, 42) = second;
+ }
+ ShowInfo("chrif : sended 0x2b0e\n");
+ WFIFOSET(char_fd,44);
+
+ return 0;
+}
+
+/*==========================================
+ * 性別変化要求
+ *------------------------------------------
+ */
+int chrif_changesex(int id, int sex) {
+ chrif_check(-1);
+
+ WFIFOHEAD(char_fd, 9);
+ WFIFOW(char_fd,0) = 0x2b11;
+ WFIFOW(char_fd,2) = 9;
+ WFIFOL(char_fd,4) = id;
+ WFIFOB(char_fd,8) = sex;
+ ShowInfo("chrif : sent 0x3000(changesex)\n");
+ WFIFOSET(char_fd,9);
+ return 0;
+}
+
+/*==========================================
+ * Answer after a request about a character name to do some operations (by Yor)
+ * Used to answer of chrif_char_ask_name.
+ * type of operation:
+ * 1: block
+ * 2: ban
+ * 3: unblock
+ * 4: unban
+ * 5: changesex
+ * type of answer:
+ * 0: login-server resquest done
+ * 1: player not found
+ * 2: gm level too low
+ * 3: login-server offline
+ *------------------------------------------
+ */
+int chrif_char_ask_name_answer(int fd)
+{
+ int acc;
+ struct map_session_data *sd;
+ char output[256];
+ char player_name[NAME_LENGTH];
+ RFIFOHEAD(fd);
+
+ acc = RFIFOL(fd,2); // account_id of who has asked (-1 if nobody)
+ memcpy(player_name, RFIFOP(fd,6), NAME_LENGTH-1);
+ player_name[NAME_LENGTH-1] = '\0';
+
+ sd = map_id2sd(acc);
+ if (acc >= 0 && sd != NULL) {
+ if (RFIFOW(fd, 32) == 1) // player not found
+ sprintf(output, "The player '%s' doesn't exist.", player_name);
+ else {
+ switch(RFIFOW(fd, 30)) {
+ case 1: // block
+ switch(RFIFOW(fd, 32)) {
+ case 0: // login-server resquest done
+ sprintf(output, "Login-server has been asked to block the player '%s'.", player_name);
+ break;
+ //case 1: // player not found
+ case 2: // gm level too low
+ sprintf(output, "Your GM level don't authorise you to block the player '%s'.", player_name);
+ break;
+ case 3: // login-server offline
+ sprintf(output, "Login-server is offline. Impossible to block the the player '%s'.", player_name);
+ break;
+ }
+ break;
+ case 2: // ban
+ switch(RFIFOW(fd, 32)) {
+ case 0: // login-server resquest done
+ sprintf(output, "Login-server has been asked to ban the player '%s'.", player_name);
+ break;
+ //case 1: // player not found
+ case 2: // gm level too low
+ sprintf(output, "Your GM level don't authorise you to ban the player '%s'.", player_name);
+ break;
+ case 3: // login-server offline
+ sprintf(output, "Login-server is offline. Impossible to ban the the player '%s'.", player_name);
+ break;
+ }
+ break;
+ case 3: // unblock
+ switch(RFIFOW(fd, 32)) {
+ case 0: // login-server resquest done
+ sprintf(output, "Login-server has been asked to unblock the player '%s'.", player_name);
+ break;
+ //case 1: // player not found
+ case 2: // gm level too low
+ sprintf(output, "Your GM level don't authorise you to unblock the player '%s'.", player_name);
+ break;
+ case 3: // login-server offline
+ sprintf(output, "Login-server is offline. Impossible to unblock the the player '%s'.", player_name);
+ break;
+ }
+ break;
+ case 4: // unban
+ switch(RFIFOW(fd, 32)) {
+ case 0: // login-server resquest done
+ sprintf(output, "Login-server has been asked to unban the player '%s'.", player_name);
+ break;
+ //case 1: // player not found
+ case 2: // gm level too low
+ sprintf(output, "Your GM level don't authorise you to unban the player '%s'.", player_name);
+ break;
+ case 3: // login-server offline
+ sprintf(output, "Login-server is offline. Impossible to unban the the player '%s'.", player_name);
+ break;
+ }
+ break;
+ case 5: // changesex
+ switch(RFIFOW(fd, 32)) {
+ case 0: // login-server resquest done
+ sprintf(output, "Login-server has been asked to change the sex of the player '%s'.", player_name);
+ break;
+ //case 1: // player not found
+ case 2: // gm level too low
+ sprintf(output, "Your GM level don't authorise you to change the sex of the player '%s'.", player_name);
+ break;
+ case 3: // login-server offline
+ sprintf(output, "Login-server is offline. Impossible to change the sex of the the player '%s'.", player_name);
+ break;
+ }
+ break;
+ }
+ }
+ if (output[0] != '\0') {
+ output[sizeof(output)-1] = '\0';
+ clif_displaymessage(sd->fd, output);
+ }
+ } else
+ ShowError("chrif_char_ask_name_answer failed - player not online.\n");
+
+ return 0;
+}
+
+/*==========================================
+ * End of GM change (@GM) (modified by Yor)
+ *------------------------------------------
+ */
+int chrif_changedgm(int fd)
+{
+ int acc, level;
+ struct map_session_data *sd = NULL;
+ RFIFOHEAD(fd);
+
+ acc = RFIFOL(fd,2);
+ level = RFIFOL(fd,6);
+
+ sd = map_id2sd(acc);
+
+ if (battle_config.etc_log)
+ ShowNotice("chrif_changedgm: account: %d, GM level 0 -> %d.\n", acc, level);
+ if (sd != NULL) {
+ if (level > 0)
+ clif_displaymessage(sd->fd, "GM modification success.");
+ else
+ clif_displaymessage(sd->fd, "Failure of GM modification.");
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * 性別変化終了 (modified by Yor)
+ *------------------------------------------
+ */
+int chrif_changedsex(int fd)
+{
+ int acc, sex, i;
+ struct map_session_data *sd;
+ RFIFOHEAD(fd);
+
+ acc = RFIFOL(fd,2);
+ sex = RFIFOL(fd,6);
+ if (battle_config.etc_log)
+ ShowNotice("chrif_changedsex %d.\n", acc);
+ sd = map_id2sd(acc);
+ if (acc > 0) {
+ if (sd != NULL && sd->status.sex != sex) {
+ if (sd->status.sex == 0) {
+ sd->status.sex = 1;
+ sd->sex = 1;
+ } else if (sd->status.sex == 1) {
+ sd->status.sex = 0;
+ sd->sex = 0;
+ }
+ // to avoid any problem with equipment and invalid sex, equipment is unequiped.
+ for (i = 0; i < MAX_INVENTORY; i++) {
+ if (sd->status.inventory[i].nameid && sd->status.inventory[i].equip)
+ pc_unequipitem((struct map_session_data*)sd, i, 2);
+ }
+ // reset skill of some job
+ if ((sd->class_&MAPID_UPPERMASK) == MAPID_BARDDANCER) {
+ // remove specifical skills of Bard classes
+ for(i = 315; i <= 322; i++) {
+ if (sd->status.skill[i].id > 0 && !sd->status.skill[i].flag) {
+ sd->status.skill_point += sd->status.skill[i].lv;
+ sd->status.skill[i].id = 0;
+ sd->status.skill[i].lv = 0;
+ }
+ }
+ // remove specifical skills of Dancer classes
+ for(i = 323; i <= 330; i++) {
+ if (sd->status.skill[i].id > 0 && !sd->status.skill[i].flag) {
+ sd->status.skill_point += sd->status.skill[i].lv;
+ sd->status.skill[i].id = 0;
+ sd->status.skill[i].lv = 0;
+ }
+ }
+ clif_updatestatus(sd, SP_SKILLPOINT);
+ // change job if necessary
+ if (sd->status.sex) //Changed from Dancer
+ sd->status.class_ -= 1;
+ else //Changed from Bard
+ sd->status.class_ += 1;
+ //sd->class_ needs not be updated as both Dancer/Bard are the same.
+ }
+ // save character
+ //chrif_save(sd,1); Character will be saved on session closed -> map_quit
+ sd->login_id1++; // change identify, because if player come back in char within the 5 seconds, he can change its characters
+ // do same modify in login-server for the account, but no in char-server (it ask again login_id1 to login, and don't remember it)
+ clif_displaymessage(sd->fd, "Your sex has been changed (need disconnection by the server)...");
+ clif_setwaitclose(sd->fd); // forced to disconnect for the change
+ }
+ } else {
+ if (sd != NULL) {
+ ShowError("chrif_changedsex failed.\n");
+ }
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * 離婚情報同期要求
+ *------------------------------------------
+ */
+int chrif_divorce(int char_id, int partner_id)
+{
+ struct map_session_data *sd = NULL;
+
+ if (!char_id || !partner_id)
+ return 0;
+
+ nullpo_retr(0, sd = map_nick2sd(map_charid2nick(partner_id)));
+ if (sd->status.partner_id == char_id) {
+ int i;
+ //離婚(相方は既にキャラが消えている筈なので)
+ sd->status.partner_id = 0;
+
+ //相方の結婚指輪を剥奪
+ for(i = 0; i < MAX_INVENTORY; i++)
+ if (sd->status.inventory[i].nameid == WEDDING_RING_M || sd->status.inventory[i].nameid == WEDDING_RING_F)
+ pc_delitem(sd, i, 1, 0);
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * Disconnection of a player (account has been deleted in login-server) by [Yor]
+ *------------------------------------------
+ */
+int chrif_accountdeletion(int fd)
+{
+ int acc;
+ struct map_session_data *sd;
+ RFIFOHEAD(fd);
+
+ acc = RFIFOL(fd,2);
+ if (battle_config.etc_log)
+ ShowNotice("chrif_accountdeletion %d.\n", acc);
+ sd = map_id2sd(acc);
+ if (acc > 0) {
+ if (sd != NULL) {
+ sd->login_id1++; // change identify, because if player come back in char within the 5 seconds, he can change its characters
+ clif_displaymessage(sd->fd, "Your account has been deleted (disconnection)...");
+ clif_setwaitclose(sd->fd); // forced to disconnect for the change
+ }
+ } else {
+ if (sd != NULL)
+ ShowError("chrif_accountdeletion failed - player not online.\n");
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * Disconnection of a player (account has been banned of has a status, from login-server) by [Yor]
+ *------------------------------------------
+ */
+int chrif_accountban(int fd)
+{
+ int acc;
+ struct map_session_data *sd;
+ RFIFOHEAD(fd);
+
+ acc = RFIFOL(fd,2);
+ if (battle_config.etc_log)
+ ShowNotice("chrif_accountban %d.\n", acc);
+ sd = map_id2sd(acc);
+ if (acc > 0) {
+ if (sd != NULL) {
+ sd->login_id1++; // change identify, because if player come back in char within the 5 seconds, he can change its characters
+ if (RFIFOB(fd,6) == 0) { // 0: change of statut, 1: ban
+ switch (RFIFOL(fd,7)) { // status or final date of a banishment
+ case 1: // 0 = Unregistered ID
+ clif_displaymessage(sd->fd, "Your account has 'Unregistered'.");
+ break;
+ case 2: // 1 = Incorrect Password
+ clif_displaymessage(sd->fd, "Your account has an 'Incorrect Password'...");
+ break;
+ case 3: // 2 = This ID is expired
+ clif_displaymessage(sd->fd, "Your account has expired.");
+ break;
+ case 4: // 3 = Rejected from Server
+ clif_displaymessage(sd->fd, "Your account has been rejected from server.");
+ break;
+ case 5: // 4 = You have been blocked by the GM Team
+ clif_displaymessage(sd->fd, "Your account has been blocked by the GM Team.");
+ break;
+ case 6: // 5 = Your Game's EXE file is not the latest version
+ clif_displaymessage(sd->fd, "Your Game's EXE file is not the latest version.");
+ break;
+ case 7: // 6 = Your are Prohibited to log in until %s
+ clif_displaymessage(sd->fd, "Your account has been prohibited to log in.");
+ break;
+ case 8: // 7 = Server is jammed due to over populated
+ clif_displaymessage(sd->fd, "Server is jammed due to over populated.");
+ break;
+ case 9: // 8 = No MSG (actually, all states after 9 except 99 are No MSG, use only this)
+ clif_displaymessage(sd->fd, "Your account has not more authorised.");
+ break;
+ case 100: // 99 = This ID has been totally erased
+ clif_displaymessage(sd->fd, "Your account has been totally erased.");
+ break;
+ default:
+ clif_displaymessage(sd->fd, "Your account has not more authorised.");
+ break;
+ }
+ } else if (RFIFOB(fd,6) == 1) { // 0: change of statut, 1: ban
+ time_t timestamp;
+ char tmpstr[2048];
+ timestamp = (time_t)RFIFOL(fd,7); // status or final date of a banishment
+ strcpy(tmpstr, "Your account has been banished until ");
+ strftime(tmpstr + strlen(tmpstr), 24, "%d-%m-%Y %H:%M:%S", localtime(&timestamp));
+ clif_displaymessage(sd->fd, tmpstr);
+ }
+ clif_setwaitclose(sd->fd); // forced to disconnect for the change
+ }
+ } else {
+ if (sd != NULL)
+ ShowError("chrif_accountban failed - player not online.\n");
+ }
+
+ return 0;
+}
+
+//Disconnect the player out of the game, simple packet
+//packet.w AID.L WHY.B 2+4+1 = 7byte
+int chrif_disconnectplayer(int fd){
+ struct map_session_data *sd;
+ RFIFOHEAD(fd);
+
+ sd = map_id2sd(RFIFOL(fd, 2));
+
+ if(sd == NULL){
+ return -1;
+ }
+
+ if (!sd->fd)
+ { //No connection
+ if (sd->state.autotrade)
+ map_quit(sd); //Remove it.
+ //Else we don't remove it because the char should have a timer to remove the player because it force-quit before,
+ //and we don't want them kicking their previous instance before the 10 secs penalty time passes. [Skotlex]
+ return 0;
+ }
+
+ switch(RFIFOB(fd, 6)){
+ //clif_authfail_fd
+ case 1: //server closed
+ clif_authfail_fd(sd->fd, 1);
+ break;
+
+ case 2: //someone else logged in
+ clif_authfail_fd(sd->fd, 2);
+ break;
+
+ case 3: //server overpopulated
+ clif_authfail_fd(sd->fd, 4);
+
+ break;
+
+ case 4: //out of time payd for .. (avail)
+ clif_authfail_fd(sd->fd, 10);
+ break;
+
+ case 5: //forced to dc by gm
+ clif_authfail_fd(sd->fd, 15);
+ break;
+ }
+
+return 0;
+}
+
+/*==========================================
+ * Request to reload GM accounts and their levels: send to char-server by [Yor]
+ *------------------------------------------
+ */
+int chrif_reloadGMdb(void)
+{
+ chrif_check(-1);
+
+ WFIFOHEAD(char_fd, 2);
+ WFIFOW(char_fd,0) = 0x2af7;
+ WFIFOSET(char_fd, 2);
+
+ return 0;
+}
+
+/*==========================================
+ * Receiving GM accounts and their levels from char-server by [Yor]
+ *------------------------------------------
+ */
+int chrif_recvgmaccounts(int fd)
+{
+ ShowInfo("From login-server: receiving information of '"CL_WHITE"%d"CL_RESET"' GM accounts.\n", pc_read_gm_account(fd));
+ return 0;
+}
+
+/*==========================================
+ * Request/Receive top 10 Fame character list
+ *------------------------------------------
+ */
+int chrif_reqfamelist(void)
+{
+ chrif_check(-1);
+
+ WFIFOHEAD(char_fd, 2);
+ WFIFOW(char_fd,0) = 0x2b1a;
+ WFIFOSET(char_fd, 2);
+
+ return 0;
+}
+
+int chrif_recvfamelist(int fd)
+{ // response from 0x2b1b
+ int num, size;
+ int total = 0, len = 8;
+ RFIFOHEAD(fd);
+
+ memset (smith_fame_list, 0, sizeof(smith_fame_list));
+ memset (chemist_fame_list, 0, sizeof(chemist_fame_list));
+ memset (taekwon_fame_list, 0, sizeof(taekwon_fame_list));
+
+ size = RFIFOW(fd,6); //Blacksmith block size
+ for (num = 0; len < size && num < 10; num++) {
+ memcpy(&smith_fame_list[num], RFIFOP(fd,len), sizeof(struct fame_list));
+ len += sizeof(struct fame_list);
+ }
+ total += num;
+
+ size = RFIFOW(fd,4); //Alchemist block size
+ for (num = 0; len < size && num < 10; num++) {
+ memcpy(&chemist_fame_list[num], RFIFOP(fd,len), sizeof(struct fame_list));
+ len += sizeof(struct fame_list);
+ }
+ total += num;
+
+ size = RFIFOW(fd,2); //Total packet length
+ for (num = 0; len < size && num < 10; num++) {
+ memcpy(&taekwon_fame_list[num], RFIFOP(fd,len), sizeof(struct fame_list));
+ len += sizeof(struct fame_list);
+ }
+ total += num;
+
+ ShowInfo("Received Fame List of '"CL_WHITE"%d"CL_RESET"' characters.\n", total);
+
+ return 0;
+}
+
+int chrif_save_scdata(struct map_session_data *sd)
+{ //parses the sc_data of the player and sends it to the char-server for saving. [Skotlex]
+#ifdef ENABLE_SC_SAVING
+ int i, count=0;
+ unsigned int tick;
+ struct status_change_data data;
+ struct TimerData *timer;
+
+#ifndef TXT_ONLY
+ if(charsave_method) //New 'Local' save
+ {
+ charsave_save_scdata(sd->status.account_id, sd->status.char_id, sd->sc_data, MAX_STATUSCHANGE);
+ return 0;
+ }
+#endif
+
+ chrif_check(-1);
+ tick = gettick();
+
+ WFIFOHEAD(char_fd, 14 + SC_MAX*sizeof(struct status_change_data));
+ WFIFOW(char_fd,0) = 0x2b1c;
+ WFIFOL(char_fd,4) = sd->status.account_id;
+ WFIFOL(char_fd,8) = sd->status.char_id;
+ for (i = 0; i < SC_MAX; i++)
+ {
+ if (sd->sc_data[i].timer == -1)
+ continue;
+ timer = get_timer(sd->sc_data[i].timer);
+ if (timer == NULL || timer->func != status_change_timer || DIFF_TICK(timer->tick,tick) < 0)
+ continue;
+ data.tick = DIFF_TICK(timer->tick,tick); //Duration that is left before ending.
+ data.type = i;
+ data.val1 = sd->sc_data[i].val1;
+ data.val2 = sd->sc_data[i].val2;
+ data.val3 = sd->sc_data[i].val3;
+ data.val4 = sd->sc_data[i].val4;
+ memcpy(WFIFOP(char_fd,14 +count*sizeof(struct status_change_data)),
+ &data, sizeof(struct status_change_data));
+ count++;
+ }
+ if (count == 0)
+ return 0; //Nothing to save.
+ WFIFOW(char_fd,12) = count;
+ WFIFOW(char_fd,2) = 14 +count*sizeof(struct status_change_data); //Total packet size
+ WFIFOSET(char_fd,WFIFOW(char_fd,2));
+#endif
+ return 0;
+}
+
+int chrif_load_scdata(int fd)
+{ //Retrieve and load sc_data for a player. [Skotlex]
+#ifdef ENABLE_SC_SAVING
+ struct map_session_data *sd;
+ struct status_change_data data;
+ int aid, cid, i, count;
+ RFIFOHEAD(fd);
+
+ aid = RFIFOL(fd,4); //Player Account ID
+ cid = RFIFOL(fd,8); //Player Char ID
+
+ sd = map_id2sd(aid);
+ if (!sd)
+ {
+ ShowError("chrif_load_scdata: Player of AID %d not found!\n", aid);
+ return -1;
+ }
+ if (sd->status.char_id != cid)
+ {
+ ShowError("chrif_load_scdata: Receiving data for account %d, char id does not matches (%d != %d)!\n", aid, sd->status.char_id, cid);
+ return -1;
+ }
+ count = RFIFOW(fd,12); //sc_count
+ for (i = 0; i < count; i++)
+ {
+ memcpy(&data, RFIFOP(fd,14 + i*sizeof(struct status_change_data)), sizeof(struct status_change_data));
+ if (data.tick < 1)
+ { //Protection against invalid tick values. [Skotlex]
+ ShowWarning("chrif_load_scdata: Received invalid duration (%d ms) for status change %d (character %s)\n", data.tick, sd->status.name);
+ continue;
+ }
+ status_change_start(&sd->bl, data.type, data.val1, data.val2, data.val3, data.val4, data.tick, 7);
+ }
+#endif
+ return 0;
+}
+
+/*==========================================
+ * Send rates and motd to char server [Wizputer]
+ *------------------------------------------
+ */
+ int chrif_ragsrvinfo(int base_rate, int job_rate, int drop_rate)
+{
+ char buf[256];
+ FILE *fp;
+ int i;
+
+ chrif_check(-1);
+
+ WFIFOHEAD(char_fd, sizeof(buf) + 10);
+ WFIFOW(char_fd,0) = 0x2b16;
+ WFIFOW(char_fd,2) = base_rate;
+ WFIFOW(char_fd,4) = job_rate;
+ WFIFOW(char_fd,6) = drop_rate;
+
+ if ((fp = fopen(motd_txt, "r")) != NULL) {
+ if (fgets(buf, 250, fp) != NULL) {
+ for(i = 0; buf[i]; i++) {
+ if (buf[i] == '\r' || buf[i] == '\n') {
+ buf[i] = 0;
+ break;
+ }
+ }
+ WFIFOW(char_fd,8) = sizeof(buf) + 10;
+ memcpy(WFIFOP(char_fd,10), buf, sizeof(buf));
+ }
+ fclose(fp);
+ } else {
+ memset(buf, 0, sizeof(buf)); //No data found, send empty packets?
+ WFIFOW(char_fd,8) = sizeof(buf) + 10;
+ memcpy(WFIFOP(char_fd,10), buf, sizeof(buf));
+ }
+ WFIFOSET(char_fd,WFIFOW(char_fd,8));
+ return 0;
+}
+
+
+/*=========================================
+ * Tell char-server charcter disconnected [Wizputer]
+ *-----------------------------------------
+ */
+
+int chrif_char_offline(struct map_session_data *sd)
+{
+ chrif_check(-1);
+
+ WFIFOHEAD(char_fd, 10);
+ WFIFOW(char_fd,0) = 0x2b17;
+ WFIFOL(char_fd,2) = sd->status.char_id;
+ WFIFOL(char_fd,6) = sd->status.account_id;
+ WFIFOSET(char_fd,10);
+
+ return 0;
+}
+
+/*=========================================
+ * Tell char-server to reset all chars offline [Wizputer]
+ *-----------------------------------------
+ */
+int chrif_flush_fifo(void) {
+ chrif_check(-1);
+
+ set_nonblocking(char_fd, 0);
+ flush_fifos();
+ set_nonblocking(char_fd, 1);
+
+ return 0;
+}
+
+/*=========================================
+ * Tell char-server to reset all chars offline [Wizputer]
+ *-----------------------------------------
+ */
+int chrif_char_reset_offline(void) {
+ chrif_check(-1);
+
+ WFIFOHEAD(char_fd, 2);
+ WFIFOW(char_fd,0) = 0x2b18;
+ WFIFOSET(char_fd,2);
+
+ return 0;
+}
+
+/*=========================================
+ * Tell char-server charcter is online [Wizputer]
+ *-----------------------------------------
+ */
+
+int chrif_char_online(struct map_session_data *sd)
+{
+ chrif_check(-1);
+
+ WFIFOHEAD(char_fd, 10);
+ WFIFOW(char_fd,0) = 0x2b19;
+ WFIFOL(char_fd,2) = sd->status.char_id;
+ WFIFOL(char_fd,6) = sd->status.account_id;
+ WFIFOSET(char_fd,10);
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int chrif_disconnect_sub(struct map_session_data* sd,va_list va) {
+ if (sd->fd)
+ clif_authfail_fd(sd->fd,1);
+ else
+ map_quit(sd);
+ return 0;
+}
+
+int chrif_disconnect(int fd) {
+ if(fd == char_fd) {
+ char_fd = 0;
+ ShowWarning("Map Server disconnected from Char Server.\n\n");
+ if (kick_on_disconnect)
+ clif_foreachclient(chrif_disconnect_sub);
+ chrif_connected = 0;
+ // 他のmap 鯖のデータを消す
+ map_eraseallipport();
+
+ // 倉庫キャッシュを消す
+ if (kick_on_disconnect)
+ { //Do not clean the storage if players are gonna be left inside. [Skotlex]
+ do_final_storage();
+ do_init_storage();
+ }
+ //Attempt to reconnect in a second. [Skotlex]
+ add_timer(gettick() + 1000, check_connect_char_server, 0, 0);
+ }
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int chrif_parse(int fd)
+{
+ int packet_len, cmd;
+ // only char-server can have an access to here.
+ // so, if it isn't the char-server, we disconnect the session (fd != char_fd).
+ if (fd != char_fd || session[fd]->eof) {
+ if (fd == char_fd && chrif_connected == 1) {
+ chrif_disconnect (fd);
+ }
+ else if (fd != char_fd)
+ ShowDebug("chrif_parse: Disconnecting invalid session #%d (is not the char-server)\n", fd);
+
+ do_close(fd);
+ return 0;
+ }
+
+ while (RFIFOREST(fd) >= 2 && !session[fd]->eof) { //Infinite loop on broken pipe fix. [Skotlex]
+ RFIFOHEAD(fd);
+ cmd = RFIFOW(fd,0);
+ if (cmd < 0x2af8 || cmd >= 0x2af8 + (sizeof(packet_len_table) / sizeof(packet_len_table[0])) ||
+ packet_len_table[cmd-0x2af8] == 0) {
+
+ int r = intif_parse(fd); // intifに渡す
+
+ if (r == 1) continue; // intifで処理した
+ if (r == 2) return 0; // intifで処理したが、データが足りない
+
+ session[fd]->eof = 1;
+ ShowWarning("chrif_parse: session #%d, intif_parse failed -> disconnected.\n", fd);
+ return 0;
+ }
+ packet_len = packet_len_table[cmd-0x2af8];
+ if (packet_len == -1) {
+ if (RFIFOREST(fd) < 4)
+ return 0;
+ packet_len = RFIFOW(fd,2);
+ }
+ if ((int)RFIFOREST(fd) < packet_len)
+ return 0;
+
+ switch(cmd) {
+ case 0x2af9: chrif_connectack(fd); break;
+ case 0x2afb: chrif_sendmapack(fd); chrif_reqfamelist(); break;
+ case 0x2afd: chrif_authok(fd); break;
+ case 0x2b00: map_setusers(fd); break;
+ case 0x2b03: clif_charselectok(RFIFOL(fd,2)); break;
+ case 0x2b04: chrif_recvmap(fd); break;
+ case 0x2b06: chrif_changemapserverack(fd); break;
+ case 0x2b07: clif_updatemaxid(RFIFOL(fd,2), RFIFOL(fd,6)); break;
+ case 0x2b09: map_addchariddb(RFIFOL(fd,2), (char*)RFIFOP(fd,6)); break;
+ case 0x2b0b: chrif_changedgm(fd); break;
+ case 0x2b0d: chrif_changedsex(fd); break;
+ case 0x2b0f: chrif_char_ask_name_answer(fd); break;
+ case 0x2b12: chrif_divorce(RFIFOL(fd,2), RFIFOL(fd,6)); break;
+ case 0x2b13: chrif_accountdeletion(fd); break;
+ case 0x2b14: chrif_accountban(fd); break;
+ case 0x2b15: chrif_recvgmaccounts(fd); break;
+ case 0x2b1b: chrif_recvfamelist(fd); break;
+ case 0x2b1d: chrif_load_scdata(fd); break;
+ case 0x2b1f: chrif_disconnectplayer(fd); break;
+ case 0x2b20: chrif_removemap(fd); break; //Remove maps of a server [Sirius]
+
+ default:
+ if (battle_config.error_log)
+ ShowError("chrif_parse : unknown packet (session #%d): 0x%x. Disconnecting.\n", fd, cmd);
+ session[fd]->eof = 1;
+ return 0;
+ }
+ if (fd == char_fd) //There's the slight chance we lost the connection during parse, in which case this would segfault if not checked [Skotlex]
+ RFIFOSKIP(fd, packet_len);
+ }
+
+ return 0;
+}
+
+int send_usercount_tochar(int tid, unsigned int tick, int id, int data) {
+ int count;
+ static int last_count = 0;
+
+ chrif_check(-1);
+
+ map_getallusers(&count);
+
+ if (count == last_count) //No need to waste packets.
+ return 0;
+ last_count = count;
+
+ WFIFOHEAD(char_fd, 4);
+ WFIFOW(char_fd,0) = 0x2afe;
+ WFIFOW(char_fd,2) = count;
+ WFIFOSET(char_fd,4);
+ return 0;
+}
+
+/*==========================================
+ * timer関数
+ * 今このmap鯖に繋がっているクライアント人数をchar鯖へ送る
+ *------------------------------------------
+ */
+int send_users_tochar(int tid, unsigned int tick, int id, int data) {
+ int count, users=0, i;
+ struct map_session_data **all_sd;
+
+ chrif_check(-1);
+
+ all_sd = map_getallusers(&count);
+ WFIFOHEAD(char_fd, 6+8*users);
+ WFIFOW(char_fd,0) = 0x2aff;
+ for (i = 0; i < count; i++) {
+ if (all_sd[i] &&
+ !((battle_config.hide_GM_session || (all_sd[i]->status.option & OPTION_INVISIBLE)) && pc_isGM(all_sd[i])))
+ {
+ WFIFOL(char_fd,6+8*users) = all_sd[i]->status.account_id;
+ WFIFOL(char_fd,6+8*users+4) = all_sd[i]->status.char_id;
+ users++;
+ }
+ }
+ WFIFOW(char_fd,2) = 6 + 8 * users;
+ WFIFOW(char_fd,4) = users;
+ WFIFOSET(char_fd,6+8*users);
+
+ return 0;
+}
+
+/*==========================================
+ * timer関数
+ * char鯖との接続を確認し、もし切れていたら再度接続する
+ *------------------------------------------
+ */
+int check_connect_char_server(int tid, unsigned int tick, int id, int data) {
+ static int displayed = 0;
+ if (char_fd <= 0 || session[char_fd] == NULL) {
+ if (!displayed) {
+ ShowStatus("Attempting to connect to Char Server. Please wait.\n");
+ displayed = 1;
+ }
+ chrif_state = 0;
+ char_fd = make_connection(char_ip, char_port);
+ if (char_fd == -1)
+ { //Attempt to connect later. [Skotlex]
+ char_fd = 0;
+ return 0;
+ }
+ session[char_fd]->func_parse = chrif_parse;
+ realloc_fifo(char_fd, FIFOSIZE_SERVERLINK, FIFOSIZE_SERVERLINK);
+
+ chrif_connect(char_fd);
+ chrif_connected = (chrif_state == 2);
+#ifndef TXT_ONLY
+ srvinfo = 0;
+#endif /* not TXT_ONLY */
+ } else {
+#ifndef TXT_ONLY
+ if (srvinfo == 0) {
+ chrif_ragsrvinfo(battle_config.base_exp_rate, battle_config.job_exp_rate, battle_config.item_rate_common);
+ srvinfo = 1;
+ }
+#endif /* not TXT_ONLY */
+/* There is no need, the connection is TCP, so the packet is assured to arrive unless the connection dies [Skotlex]
+ //If for some reason the next iteration (10 secs) we are still not connected,
+ //assume the packets got lost, so we need to resend them. [Skotlex]
+ if (chrif_state == 0)
+ chrif_connect(char_fd);
+ else if (chrif_state == 1)
+ chrif_sendmap(char_fd);
+*/
+ }
+ if (chrif_isconnect()) displayed = 0;
+ return 0;
+}
+
+int auth_db_final(DBKey k,void *d,va_list ap) {
+ struct auth_node *node=(struct auth_node*)d;
+ if (node->char_dat)
+ aFree(node->char_dat);
+ return 0;
+}
+
+/*==========================================
+ * 終了
+ *------------------------------------------
+ */
+int do_final_chrif(void)
+{
+ delete_session(char_fd);
+ auth_db->destroy(auth_db, auth_db_final);
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int do_init_chrif(void)
+{
+ add_timer_func_list(check_connect_char_server, "check_connect_char_server");
+ add_timer_func_list(send_usercount_tochar, "send_usercount_tochar");
+ add_timer_func_list(send_users_tochar, "send_users_tochar");
+ add_timer_func_list(auth_db_cleanup, "auth_db_cleanup");
+ add_timer_interval(gettick() + 1000, check_connect_char_server, 0, 0, 10 * 1000);
+#ifdef TXT_ONLY
+ //Txt needs this more frequently because it is used for the online.html file.
+ add_timer_interval(gettick() + 1000, send_users_tochar, 0, 0, UPDATE_INTERVAL);
+#else
+ add_timer_interval(gettick() + 1000, send_users_tochar, 0, 0, CHECK_INTERVAL);
+ add_timer_interval(gettick() + 1000, send_usercount_tochar, 0, 0, UPDATE_INTERVAL);
+#endif
+ add_timer_interval(gettick() + 1000, auth_db_cleanup, 0, 0, 30 * 1000);
+
+ auth_db = db_alloc(__FILE__,__LINE__,DB_INT,DB_OPT_RELEASE_DATA,sizeof(int));
+
+ return 0;
+}
diff --git a/src/map/chrif.h b/src/map/chrif.h
new file mode 100644
index 000000000..1be6d4182
--- /dev/null
+++ b/src/map/chrif.h
@@ -0,0 +1,54 @@
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#ifndef _CHRIF_H_
+#define _CHRIF_H_
+
+struct auth_node{
+ int account_id, login_id1, login_id2, sex, fd;
+ time_t connect_until_time; // # of seconds 1/1/1970 (timestamp): Validity limit of the account (0 = unlimited)
+ struct map_session_data *sd; //Data from logged on char.
+ struct mmo_charstatus *char_dat; //Data from char server.
+ unsigned int node_created; //For node auto-deleting
+};
+
+void chrif_setuserid(char*);
+void chrif_setpasswd(char*);
+void chrif_setip(char*);
+void chrif_setport(int);
+
+int chrif_isconnect(void);
+
+extern int chrif_connected;
+
+void chrif_authreq(struct map_session_data *);
+void chrif_authok(int fd);
+int chrif_save(struct map_session_data*, int flag);
+int chrif_charselectreq(struct map_session_data *);
+void check_fake_id(int fd, struct map_session_data *sd, int target_id);
+int chrif_changemapserver(struct map_session_data *sd,short map,int x,int y,int ip,short port);
+
+int chrif_searchcharid(int char_id);
+int chrif_changegm(int id,const char *pass,int len);
+int chrif_changeemail(int id, const char *actual_email, const char *new_email);
+int chrif_char_ask_name(int id, char * character_name, short operation_type, int year, int month, int day, int hour, int minute, int second);
+int chrif_reloadGMdb(void);
+int chrif_reqfamelist(void);
+int chrif_save_scdata(struct map_session_data *sd);
+int chrif_ragsrvinfo(int base_rate,int job_rate, int drop_rate);
+int chrif_char_offline(struct map_session_data *sd);
+int chrif_char_reset_offline(void);
+int send_users_tochar(int tid, unsigned int tick, int id, int data);
+int chrif_char_online(struct map_session_data *sd);
+int chrif_changesex(int id, int sex);
+int chrif_chardisconnect(struct map_session_data *sd);
+int check_connect_char_server(int tid, unsigned int tick, int id, int data);
+
+int chrif_pcauthok(int fd);
+
+int do_final_chrif(void);
+int do_init_chrif(void);
+
+int chrif_flush_fifo(void);
+
+#endif
diff --git a/src/map/clif.c b/src/map/clif.c
new file mode 100644
index 000000000..77e50d0d6
--- /dev/null
+++ b/src/map/clif.c
@@ -0,0 +1,12146 @@
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#define DUMP_UNKNOWN_PACKET 0
+#define DUMP_ALL_PACKETS 0
+
+#include <stdio.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#ifdef __WIN32
+#define __USE_W32_SOCKETS
+#include <windows.h>
+#else
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#endif
+#include <time.h>
+
+#include "../common/socket.h"
+#include "../common/timer.h"
+#include "../common/malloc.h"
+#include "../common/version.h"
+#include "../common/nullpo.h"
+#include "../common/showmsg.h"
+
+#include "map.h"
+#include "chrif.h"
+#include "clif.h"
+#include "pc.h"
+#include "status.h"
+#include "npc.h"
+#include "itemdb.h"
+#include "chat.h"
+#include "trade.h"
+#include "storage.h"
+#include "script.h"
+#include "skill.h"
+#include "atcommand.h"
+#include "charcommand.h"
+#include "intif.h"
+#include "battle.h"
+#include "mob.h"
+#include "party.h"
+#include "guild.h"
+#include "vending.h"
+#include "pet.h"
+#include "log.h"
+
+struct Clif_Config {
+ int packet_db_ver; //Preferred packet version.
+ int connect_cmd[MAX_PACKET_VER + 1]; //Store the connect command for all versions. [Skotlex]
+} clif_config;
+
+struct packet_db packet_db[MAX_PACKET_VER + 1][MAX_PACKET_DB];
+
+static const int packet_len_table[MAX_PACKET_DB] = {
+ 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+//#0x0040
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 55, 17, 3, 37, 46, -1, 23, -1, 3,108, 3, 2,
+#if PACKETVER < 2
+ 3, 28, 19, 11, 3, -1, 9, 5, 52, 51, 56, 58, 41, 2, 6, 6,
+#else // 78-7b 亀島以降 lv99エフェクト用
+ 3, 28, 19, 11, 3, -1, 9, 5, 54, 53, 58, 60, 41, 2, 6, 6,
+#endif
+//#0x0080
+ 7, 3, 2, 2, 2, 5, 16, 12, 10, 7, 29, 2, -1, -1, -1, 0, // 0x8b changed to 2 (was 23)
+ 7, 22, 28, 2, 6, 30, -1, -1, 3, -1, -1, 5, 9, 17, 17, 6,
+ 23, 6, 6, -1, -1, -1, -1, 8, 7, 6, 7, 4, 7, 0, -1, 6,
+ 8, 8, 3, 3, -1, 6, 6, -1, 7, 6, 2, 5, 6, 44, 5, 3,
+//#0x00C0
+ 7, 2, 6, 8, 6, 7, -1, -1, -1, -1, 3, 3, 6, 3, 2, 27, // 0xcd change to 3 (was 6)
+ 3, 4, 4, 2, -1, -1, 3, -1, 6, 14, 3, -1, 28, 29, -1, -1,
+ 30, 30, 26, 2, 6, 26, 3, 3, 8, 19, 5, 2, 3, 2, 2, 2,
+ 3, 2, 6, 8, 21, 8, 8, 2, 2, 26, 3, -1, 6, 27, 30, 10,
+
+//#0x0100
+ 2, 6, 6, 30, 79, 31, 10, 10, -1, -1, 4, 6, 6, 2, 11, -1,
+ 10, 39, 4, 10, 31, 35, 10, 18, 2, 13, 15, 20, 68, 2, 3, 16,
+ 6, 14, -1, -1, 21, 8, 8, 8, 8, 8, 2, 2, 3, 4, 2, -1,
+ 6, 86, 6, -1, -1, 7, -1, 6, 3, 16, 4, 4, 4, 6, 24, 26,
+//#0x0140
+ 22, 14, 6, 10, 23, 19, 6, 39, 8, 9, 6, 27, -1, 2, 6, 6,
+ 110, 6, -1, -1, -1, -1, -1, 6, -1, 54, 66, 54, 90, 42, 6, 42,
+ -1, -1, -1, -1, -1, 30, -1, 3, 14, 3, 30, 10, 43, 14,186,182,
+ 14, 30, 10, 3, -1, 6,106, -1, 4, 5, 4, -1, 6, 7, -1, -1,
+//#0x0180
+ 6, 3,106, 10, 10, 34, 0, 6, 8, 4, 4, 4, 29, -1, 10, 6,
+#if PACKETVER < 1
+ 90, 86, 24, 6, 30,102, 8, 4, 8, 4, 14, 10, -1, 6, 2, 6,
+#else // 196 comodo以降 状態表示アイコン用
+ 90, 86, 24, 6, 30,102, 9, 4, 8, 4, 14, 10, -1, 6, 2, 6,
+#endif
+ 3, 3, 35, 5, 11, 26, -1, 4, 4, 6, 10, 12, 6, -1, 4, 4,
+ 11, 7, -1, 67, 12, 18,114, 6, 3, 6, 26, 26, 26, 26, 2, 3,
+//#0x01C0, Set 0x1d5=-1
+ 2, 14, 10, -1, 22, 22, 4, 2, 13, 97, 3, 9, 9, 30, 6, 28,
+ 8, 14, 10, 35, 6, -1, 4, 11, 54, 53, 60, 2, -1, 47, 33, 6,
+ 30, 8, 34, 14, 2, 6, 26, 2, 28, 81, 6, 10, 26, 2, -1, -1,
+ -1, -1, 20, 10, 32, 9, 34, 14, 2, 6, 48, 56, -1, 4, 5, 10,
+//#0x200
+ 26, -1, 26, 10, 18, 26, 11, 34, 14, 36, 10, 0, 0, -1, 32, 10, // 0x20c change to 0 (was 19)
+ 22, 0, 26, 26, 42, -1, -1, 2, 2,282,282,10, 10, -1, -1, 66,
+ 10, -1, -1, 8, 10, 2,282, 18, 18, 15, 58, 57, 64, 5, 69, 5,
+ 12, 26, 9, 11, -1, -1, 10, 2, 282, 11, 4, 36, -1,-1, 4, 2,
+ -1, -1, -1, -1, -1, 3, 4, 8, -1, 3, 70, 4, 8,12, 4, 10,
+ 3, 32, -1, 3, 3, 5, 5, 8, 2, 3, -1, -1, 4,-1, 4
+};
+
+// local define
+enum {
+ ALL_CLIENT,
+ ALL_SAMEMAP,
+ AREA,
+ AREA_WOS,
+ AREA_WOC,
+ AREA_WOSC,
+ AREA_CHAT_WOC,
+ CHAT,
+ CHAT_WOS,
+ CHAT_MAINCHAT,
+ PARTY,
+ PARTY_WOS,
+ PARTY_SAMEMAP,
+ PARTY_SAMEMAP_WOS,
+ PARTY_AREA,
+ PARTY_AREA_WOS,
+ GUILD,
+ GUILD_WOS,
+ GUILD_SAMEMAP, // [Valaris]
+ GUILD_SAMEMAP_WOS,
+ GUILD_AREA,
+ GUILD_AREA_WOS, // end additions [Valaris]
+ SELF
+};
+
+//Converts item type in case of pet eggs.
+#define itemtype(a) (a == 7)?4:a
+
+#define WBUFPOS(p,pos,x,y) { unsigned char *__p = (p); __p+=(pos); __p[0] = (x)>>2; __p[1] = ((x)<<6) | (((y)>>4)&0x3f); __p[2] = (y)<<4; }
+#define WBUFPOS2(p,pos,x0,y0,x1,y1) { unsigned char *__p = (p); __p+=(pos); __p[0] = (unsigned char)((x0)>>2); __p[1] = (unsigned char)(((x0)<<6) | (((y0)>>4)&0x3f)); __p[2] = (unsigned char)(((y0)<<4) | (((x1)>>6)&0x0f)); __p[3]=(unsigned char)(((x1)<<2) | (((y1)>>8)&0x03)); __p[4]=(unsigned char)(y1); }
+
+#define WFIFOPOS(fd,pos,x,y) { WBUFPOS (WFIFOP(fd,pos),0,x,y); }
+#define WFIFOPOS2(fd,pos,x0,y0,x1,y1) { WBUFPOS2(WFIFOP(fd,pos),0,x0,y0,x1,y1); }
+
+//To make the assignation of the level based on limits clearer/easier. [Skotlex]
+#define clif_setlevel(lv) (lv<battle_config.max_lv?lv:battle_config.max_lv-(lv<battle_config.aura_lv?1:0));
+
+static char map_ip_str[16];
+static in_addr_t map_ip;
+static in_addr_t bind_ip = INADDR_ANY;
+static int map_port = 5121;
+int map_fd;
+char talkie_mes[MESSAGE_SIZE];
+
+//These two will be used to verify the incoming player's validity.
+//It helps identify their client packet version correctly. [Skotlex]
+static int max_account_id = DEFAULT_MAX_ACCOUNT_ID;
+static int max_char_id = DEFAULT_MAX_CHAR_ID;
+
+int clif_parse (int fd);
+static void clif_hpmeter_single(int fd, struct map_session_data *sd);
+
+/*==========================================
+ * map鯖のip設定
+ *------------------------------------------
+ */
+void clif_setip(char *ip)
+{
+ memcpy(map_ip_str, ip, 16);
+ map_ip = inet_addr(map_ip_str);
+}
+
+void clif_setbindip(char *ip)
+{
+ bind_ip = inet_addr(ip);
+}
+
+/*==========================================
+ * map鯖のport設定
+ *------------------------------------------
+ */
+void clif_setport(int port)
+{
+ map_port = port;
+}
+
+/*==========================================
+ * map鯖のip読み出し
+ *------------------------------------------
+ */
+in_addr_t clif_getip(void)
+{
+ return map_ip;
+}
+
+/*==========================================
+ * map鯖のport読み出し
+ *------------------------------------------
+ */
+int clif_getport(void)
+{
+ return map_port;
+}
+
+/*==========================================
+ * Counts connected players.
+ *------------------------------------------
+ */
+int clif_countusers(void)
+{
+ int users = 0, i;
+ struct map_session_data *sd;
+
+ for(i = 0; i < fd_max; i++) {
+ if (session[i] && (sd = (struct map_session_data*)session[i]->session_data) && sd->state.auth &&
+ !(battle_config.hide_GM_session && pc_isGM(sd)))
+ users++;
+ }
+ return users;
+}
+
+/*==========================================
+ * 全てのclientに対してfunc()実行
+ *------------------------------------------
+ */
+
+int clif_foreachclient(int (*func)(struct map_session_data*, va_list),...) //recoded by sasuke, bug when player count gets higher [Kevin]
+{
+ int i;
+ va_list ap;
+ struct map_session_data *sd;
+
+ va_start(ap,func);
+
+ for(i = 0; i < fd_max; i++) {
+ if ( session[i] ) {
+ sd = (struct map_session_data*)session[i]->session_data;
+ if ( sd && session[i]->func_parse == clif_parse &&
+ sd->state.auth && !sd->state.waitingdisconnect )
+ func(sd, ap);
+ }
+ }
+
+ va_end(ap);
+ return 0;
+}
+
+/*==========================================
+ * clif_sendでAREA*指定時用
+ *------------------------------------------
+ */
+int clif_send_sub(struct block_list *bl, va_list ap)
+{
+ struct block_list *src_bl;
+ struct map_session_data *sd;
+ unsigned char *buf;
+ int len, type;
+
+ nullpo_retr(0, bl);
+ nullpo_retr(0, ap);
+ nullpo_retr(0, sd = (struct map_session_data *)bl);
+
+ if (!sd->fd) //Avoid attempting to send to disconnected chars (may prevent buffer overrun errors?) [Skotlex]
+ return 0;
+
+ buf = va_arg(ap,unsigned char*);
+ len = va_arg(ap,int);
+ nullpo_retr(0, src_bl = va_arg(ap,struct block_list*));
+ type = va_arg(ap,int);
+
+ switch(type) {
+ case AREA_WOS:
+ if (bl == src_bl)
+ return 0;
+ break;
+ case AREA_WOC:
+ if (sd->chatID || bl == src_bl)
+ return 0;
+ break;
+ case AREA_WOSC:
+ {
+ struct map_session_data *ssd = (struct map_session_data *)src_bl;
+ if ((ssd != 0) && (src_bl->type == BL_PC) && (sd->chatID != 0) && (sd->chatID == ssd->chatID))
+ return 0;
+ }
+ break;
+ }
+
+ if (session[sd->fd] != NULL) {
+ WFIFOHEAD(sd->fd, len);
+ if (WFIFOP(sd->fd,0) == buf) {
+ printf("WARNING: Invalid use of clif_send function\n");
+ printf(" Packet x%4x use a WFIFO of a player instead of to use a buffer.\n", WBUFW(buf,0));
+ printf(" Please correct your code.\n");
+ // don't send to not move the pointer of the packet for next sessions in the loop
+ } else {
+ if (packet_db[sd->packet_ver][RBUFW(buf,0)].len) { // packet must exist for the client version
+ memcpy(WFIFOP(sd->fd,0), buf, len);
+ //Check if hidden, better to modify the char's buffer than the
+ //given buffer to prevent intravision affecting the packet as
+ //it's being received by everyone. [Skotlex]
+ if ((sd->special_state.intravision || sd->sc_data[SC_INTRAVISION].timer != -1 ) && bl != src_bl) {
+ short *src_option = status_get_option(src_bl);
+ if(src_option && (*src_option)&(OPTION_HIDE|OPTION_CLOAK))
+ { //optionの修正
+ switch(((unsigned short*)buf)[0])
+ {
+ case 0x119:
+ WFIFOW(sd->fd,10) &= ~(OPTION_HIDE|OPTION_CLOAK);
+ break;
+#if PACKETVER < 4
+ case 0x78:
+#else
+ case 0x1da:
+#endif
+ case 0x7b:
+ case 0x7c:
+ case 0x1d8:
+ WFIFOW(sd->fd,12) &=~(OPTION_HIDE|OPTION_CLOAK);
+ break;
+ }
+ }
+ }
+ WFIFOSET(sd->fd,len);
+ }
+ }
+ }
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int clif_send (unsigned char *buf, int len, struct block_list *bl, int type) {
+ int i;
+ struct map_session_data *sd = NULL;
+ struct party *p = NULL;
+ struct guild *g = NULL;
+ int x0 = 0, x1 = 0, y0 = 0, y1 = 0;
+
+ if (type != ALL_CLIENT &&
+ type != CHAT_MAINCHAT) {
+ nullpo_retr(0, bl);
+ if (bl->type == BL_PC) {
+ sd = (struct map_session_data *)bl;
+ }
+ }
+
+ switch(type) {
+ case ALL_CLIENT: // 全クライアントに送信
+ for (i = 0; i < fd_max; i++) {
+ if (session[i] && (sd = (struct map_session_data *)session[i]->session_data) != NULL && sd->state.auth) {
+ if (packet_db[sd->packet_ver][RBUFW(buf,0)].len) { // packet must exist for the client version
+ WFIFOHEAD(i, len);
+ memcpy(WFIFOP(i,0), buf, len);
+ WFIFOSET(i,len);
+ }
+ }
+ }
+ break;
+ case ALL_SAMEMAP: // 同じマップの全クライアントに送信
+ for(i = 0; i < fd_max; i++) {
+ if (session[i] && (sd = (struct map_session_data*)session[i]->session_data) != NULL &&
+ sd->state.auth && sd->bl.m == bl->m) {
+ if (packet_db[sd->packet_ver][RBUFW(buf,0)].len) { // packet must exist for the client version
+ WFIFOHEAD(i,len);
+ memcpy(WFIFOP(i,0), buf, len);
+ WFIFOSET(i,len);
+ }
+ }
+ }
+ break;
+ case AREA:
+ case AREA_WOS:
+ case AREA_WOC:
+ case AREA_WOSC:
+ map_foreachinarea(clif_send_sub, bl->m, bl->x-AREA_SIZE, bl->y-AREA_SIZE, bl->x+AREA_SIZE, bl->y+AREA_SIZE,
+ BL_PC, buf, len, bl, type);
+ break;
+ case AREA_CHAT_WOC:
+ map_foreachinarea(clif_send_sub, bl->m, bl->x-(AREA_SIZE-5), bl->y-(AREA_SIZE-5),
+ bl->x+(AREA_SIZE-5), bl->y+(AREA_SIZE-5), BL_PC, buf, len, bl, AREA_WOC);
+ break;
+ case CHAT:
+ case CHAT_WOS:
+ {
+ struct chat_data *cd;
+ if (sd) {
+ cd = (struct chat_data*)map_id2bl(sd->chatID);
+ } else if (bl->type == BL_CHAT) {
+ cd = (struct chat_data*)bl;
+ } else break;
+ if (cd == NULL)
+ break;
+ for(i = 0; i < cd->users; i++) {
+ if (type == CHAT_WOS && cd->usersd[i] == sd)
+ continue;
+ if (packet_db[cd->usersd[i]->packet_ver][RBUFW(buf,0)].len) { // packet must exist for the client version
+ if (cd->usersd[i]->fd >0 && session[cd->usersd[i]->fd]) // Added check to see if session exists [PoW]
+ {
+ WFIFOHEAD(cd->usersd[i]->fd,len);
+ memcpy(WFIFOP(cd->usersd[i]->fd,0), buf, len);
+ WFIFOSET(cd->usersd[i]->fd,len);
+ }
+ }
+ }
+ }
+ break;
+ case CHAT_MAINCHAT: //[LuzZza]
+ for(i=1; i<fd_max; i++) {
+ if(session[i] && (sd = (struct map_session_data*)session[i]->session_data) != NULL &&
+ sd->state.mainchat && sd->fd) {
+ memcpy(WFIFOP(sd->fd,0), buf, len);
+ WFIFOSET(sd->fd, len);
+ }
+ }
+ break;
+ case PARTY_AREA: // 同じ画面内の全パーティーメンバに送信
+ case PARTY_AREA_WOS: // 自分以外の同じ画面内の全パーティーメンバに送信
+ x0 = bl->x - AREA_SIZE;
+ y0 = bl->y - AREA_SIZE;
+ x1 = bl->x + AREA_SIZE;
+ y1 = bl->y + AREA_SIZE;
+ case PARTY: // 全パーティーメンバに送信
+ case PARTY_WOS: // 自分以外の全パーティーメンバに送信
+ case PARTY_SAMEMAP: // 同じマップの全パーティーメンバに送信
+ case PARTY_SAMEMAP_WOS: // 自分以外の同じマップの全パーティーメンバに送信
+ if (sd && sd->status.party_id)
+ p = party_search(sd->status.party_id);
+
+ if (p) {
+ for(i=0;i<MAX_PARTY;i++){
+ if ((sd = p->member[i].sd) != NULL) {
+ if (!sd->fd || session[sd->fd] == NULL || sd->state.auth == 0
+ || session[sd->fd]->session_data == NULL || sd->packet_ver > MAX_PACKET_VER)
+ continue;
+
+ if (sd->bl.id == bl->id && (type == PARTY_WOS || type == PARTY_SAMEMAP_WOS || type == PARTY_AREA_WOS))
+ continue;
+
+ if (type != PARTY && type != PARTY_WOS && bl->m != sd->bl.m) // マップチェック
+ continue;
+
+ if ((type == PARTY_AREA || type == PARTY_AREA_WOS) &&
+ (sd->bl.x < x0 || sd->bl.y < y0 || sd->bl.x > x1 || sd->bl.y > y1))
+ continue;
+
+ if (packet_db[sd->packet_ver][RBUFW(buf,0)].len) { // packet must exist for the client version
+ WFIFOHEAD(sd->fd,len);
+ memcpy(WFIFOP(sd->fd,0), buf, len);
+ WFIFOSET(sd->fd,len);
+ }
+ }
+ }
+ if (!enable_spy) //Skip unnecessary parsing. [Skotlex]
+ break;
+ for (i = 1; i < fd_max; i++){ // partyspy [Syrus22]
+ if (session[i] && (sd = (struct map_session_data*)session[i]->session_data) != NULL && sd->state.auth && sd->fd && sd->partyspy) {
+ if (sd->partyspy == p->party_id) {
+ if (sd->fd && packet_db[sd->packet_ver][RBUFW(buf,0)].len) { // packet must exist for the client version
+ WFIFOHEAD(sd->fd,len);
+ memcpy(WFIFOP(sd->fd,0), buf, len);
+ WFIFOSET(sd->fd,len);
+ }
+ }
+ }
+ }
+ }
+ break;
+ case SELF:
+ if (sd && sd->fd && packet_db[sd->packet_ver][RBUFW(buf,0)].len) { // packet must exist for the client version
+ WFIFOHEAD(sd->fd,len);
+ memcpy(WFIFOP(sd->fd,0), buf, len);
+ WFIFOSET(sd->fd,len);
+ }
+ break;
+
+// New definitions for guilds [Valaris] - Cleaned up and reorganized by [Skotlex]
+ case GUILD_AREA:
+ case GUILD_AREA_WOS:
+ x0 = bl->x - AREA_SIZE;
+ y0 = bl->y - AREA_SIZE;
+ x1 = bl->x + AREA_SIZE;
+ y1 = bl->y + AREA_SIZE;
+ case GUILD_SAMEMAP:
+ case GUILD_SAMEMAP_WOS:
+ case GUILD:
+ case GUILD_WOS:
+ if (sd && sd->status.guild_id)
+ g = guild_search(sd->status.guild_id);
+
+ if (g) {
+ for(i = 0; i < g->max_member; i++) {
+ if ((sd = g->member[i].sd) != NULL) {
+ if (!sd->fd || session[sd->fd] == NULL || sd->state.auth == 0
+ || session[sd->fd]->session_data == NULL || sd->packet_ver > MAX_PACKET_VER)
+ continue;
+
+ if (sd->bl.id == bl->id && (type == GUILD_WOS || type == GUILD_SAMEMAP_WOS || type == GUILD_AREA_WOS))
+ continue;
+
+ if (type != GUILD && type != GUILD_WOS && sd->bl.m != bl->m)
+ continue;
+
+ if ((type == GUILD_AREA || type == GUILD_AREA_WOS) &&
+ (sd->bl.x < x0 || sd->bl.y < y0 || sd->bl.x > x1 || sd->bl.y > y1))
+ continue;
+
+ if (packet_db[sd->packet_ver][RBUFW(buf,0)].len) { // packet must exist for the client version
+ WFIFOHEAD(sd->fd,len);
+ memcpy(WFIFOP(sd->fd,0), buf, len);
+ WFIFOSET(sd->fd,len);
+ }
+ }
+ }
+ if (!enable_spy) //Skip unnecessary parsing. [Skotlex]
+ break;
+ for (i = 1; i < fd_max; i++){ // guildspy [Syrus22]
+ if (session[i] && (sd = (struct map_session_data*)session[i]->session_data) != NULL && sd->state.auth && sd->fd && sd->guildspy) {
+ if (sd->guildspy == g->guild_id) {
+ if (packet_db[sd->packet_ver][RBUFW(buf,0)].len) { // packet must exist for the client version
+ WFIFOHEAD(sd->fd,len);
+ memcpy(WFIFOP(sd->fd,0), buf, len);
+ WFIFOSET(sd->fd,len);
+ }
+ }
+ }
+ }
+ }
+ break;
+/* End [Valaris] */
+
+ default:
+ if (battle_config.error_log)
+ ShowError("clif_send: Unrecognized type %d\n",type);
+ return -1;
+ }
+
+ return 0;
+}
+
+//
+// パケット作って送信
+//
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int clif_authok(struct map_session_data *sd) {
+ int fd;
+
+ if (!sd->fd)
+ return 0;
+ fd = sd->fd;
+
+ WFIFOHEAD(fd, packet_len_table[0x73]);
+ WFIFOW(fd, 0) = 0x73;
+ WFIFOL(fd, 2) = gettick();
+ WFIFOPOS(fd, 6, sd->bl.x, sd->bl.y);
+ WFIFOB(fd, 9) = 5;
+ WFIFOB(fd,10) = 5;
+ WFIFOSET(fd,packet_len_table[0x73]);
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int clif_authfail_fd(int fd, int type) {
+ if (!fd || !session[fd] || session[fd]->func_parse != clif_parse) //clif_authfail should only be invoked on players!
+ return 0;
+
+ WFIFOHEAD(fd, packet_len_table[0x81]);
+ WFIFOW(fd,0) = 0x81;
+ WFIFOB(fd,2) = type;
+ WFIFOSET(fd,packet_len_table[0x81]);
+ clif_setwaitclose(fd);
+ return 0;
+}
+
+/*==========================================
+ * Used to know which is the max valid account/char id [Skotlex]
+ *------------------------------------------
+ */
+void clif_updatemaxid(int account_id, int char_id)
+{
+ max_account_id = account_id;
+ max_char_id = char_id;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int clif_charselectok(int id) {
+ struct map_session_data *sd;
+ int fd;
+
+ if ((sd = map_id2sd(id)) == NULL)
+ return 1;
+
+ if (!sd->fd)
+ return 1;
+
+ fd = sd->fd;
+ WFIFOHEAD(fd, packet_len_table[0xb3]);
+ WFIFOW(fd,0) = 0xb3;
+ WFIFOB(fd,2) = 1;
+ WFIFOSET(fd,packet_len_table[0xb3]);
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+static int clif_set009e(struct flooritem_data *fitem,unsigned char *buf) {
+ int view;
+
+ nullpo_retr(0, fitem);
+
+ //009e <ID>.l <name ID>.w <identify flag>.B <X>.w <Y>.w <subX>.B <subY>.B <amount>.w
+ WBUFW(buf, 0) = 0x9e;
+ WBUFL(buf, 2) = fitem->bl.id;
+ if ((view = itemdb_viewid(fitem->item_data.nameid)) > 0)
+ WBUFW(buf, 6) = view;
+ else
+ WBUFW(buf, 6) = fitem->item_data.nameid;
+ WBUFB(buf, 8) = fitem->item_data.identify;
+ WBUFW(buf, 9) = fitem->bl.x;
+ WBUFW(buf,11) = fitem->bl.y;
+ WBUFB(buf,13) = fitem->subx;
+ WBUFB(buf,14) = fitem->suby;
+ WBUFW(buf,15) = fitem->item_data.amount;
+
+ return packet_len_table[0x9e];
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int clif_dropflooritem(struct flooritem_data *fitem) {
+ unsigned char buf[64];
+
+ nullpo_retr(0, fitem);
+
+ if (fitem->item_data.nameid <= 0)
+ return 0;
+ clif_set009e(fitem, buf);
+ clif_send(buf, packet_len_table[0x9e], &fitem->bl, AREA);
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int clif_clearflooritem(struct flooritem_data *fitem, int fd) {
+ unsigned char buf[16];
+
+ nullpo_retr(0, fitem);
+
+ WBUFW(buf,0) = 0xa1;
+ WBUFL(buf,2) = fitem->bl.id;
+
+ if (fd == 0) {
+ clif_send(buf, packet_len_table[0xa1], &fitem->bl, AREA);
+ } else {
+ WFIFOHEAD(fd,packet_len_table[0xa1]);
+ memcpy(WFIFOP(fd,0), buf, 6);
+ WFIFOSET(fd,packet_len_table[0xa1]);
+ }
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int clif_clearchar(struct block_list *bl, int type) {
+ unsigned char buf[16];
+
+ nullpo_retr(0, bl);
+
+ WBUFW(buf,0) = 0x80;
+ WBUFL(buf,2) = bl->id;
+ WBUFB(buf,6) = type;
+ clif_send(buf, packet_len_table[0x80], bl, type == 1 ? AREA : AREA_WOS);
+
+ if(bl->type==BL_PC && ((struct map_session_data *)bl)->disguise) {
+ WBUFL(buf,2) = -bl->id;
+ clif_send(buf, packet_len_table[0x80], bl, AREA);
+ }
+
+ return 0;
+}
+
+static int clif_clearchar_delay_sub(int tid, unsigned int tick, int id, int data) {
+ struct block_list *bl = (struct block_list *)id;
+
+ clif_clearchar(bl,data);
+ aFree(bl);
+ return 0;
+}
+
+int clif_clearchar_delay(unsigned int tick, struct block_list *bl, int type) {
+ struct block_list *tbl;
+ tbl = aCalloc(1, sizeof (struct block_list));
+ memcpy (tbl, bl, sizeof (struct block_list));
+ add_timer(tick, clif_clearchar_delay_sub, (int)tbl, type);
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int clif_clearchar_id(int id, int type, int fd) {
+ unsigned char buf[16];
+
+ WBUFW(buf,0) = 0x80;
+ WBUFL(buf,2) = id;
+ WBUFB(buf,6) = type;
+ WFIFOHEAD(fd, packet_len_table[0x80]);
+ memcpy(WFIFOP(fd,0), buf, 7);
+ WFIFOSET(fd, packet_len_table[0x80]);
+
+ return 0;
+}
+
+//Small define to specify the weapon view sprite, makes code easier to read down below... [Skotlex]
+#define clif_weapon_viewid(sd, n) ((sd->equip_index[n] >= 0 && sd->inventory_data[sd->equip_index[n]])?(\
+ (sd->inventory_data[sd->equip_index[n]]->view_id > 0)?sd->inventory_data[sd->equip_index[n]]->view_id: \
+ sd->status.inventory[sd->equip_index[n]].nameid):0)
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+static int clif_set0078(struct map_session_data *sd, unsigned char *buf) {
+ int sdoption;
+
+ nullpo_retr(0, sd);
+
+ // Disable showing Falcon when player is hide [LuzZza]
+ if(sd->disguise)
+ sdoption = OPTION_INVISIBLE;
+ else {
+ sdoption = sd->status.option;
+ if(sdoption&(OPTION_HIDE|OPTION_CLOAK|OPTION_INVISIBLE))
+ sdoption &= ~OPTION_FALCON;
+ }
+#if PACKETVER < 4
+ memset(buf,0,packet_len_table[0x78]);
+
+ WBUFW(buf,0)=0x78;
+ WBUFL(buf,2)=sd->bl.id;
+ WBUFW(buf,6)=sd->speed;
+ WBUFW(buf,8)=sd->opt1;
+ WBUFW(buf,10)=sd->opt2;
+ WBUFW(buf,12)=sdoption;
+ WBUFW(buf,14)=sd->view_class;
+ WBUFW(buf,16)=sd->status.hair;
+ if (sd->view_class != JOB_WEDDING && sd->view_class !=JOB_XMAS)
+ WBUFW(buf,18) = sd->status.weapon;
+ else
+ WBUFW(buf,18)=0;
+ WBUFW(buf,20)=sd->status.head_bottom;
+ WBUFW(buf,22)=sd->status.shield;
+ WBUFW(buf,24)=sd->status.head_top;
+ WBUFW(buf,26)=sd->status.head_mid;
+ WBUFW(buf,28)=sd->status.hair_color;
+ WBUFW(buf,30)=sd->status.clothes_color;
+ WBUFW(buf,32)=sd->head_dir;
+ WBUFL(buf,34)=sd->status.guild_id;
+ WBUFL(buf,38)=sd->guild_emblem_id;
+ WBUFW(buf,42)=sd->status.manner;
+ WBUFB(buf,44)=sd->status.karma;
+ WBUFB(buf,45)=sd->sex;
+ WBUFPOS(buf,46,sd->bl.x,sd->bl.y);
+ WBUFB(buf,48)|=sd->dir&0x0f;
+ WBUFB(buf,49)=5;
+ WBUFB(buf,50)=5;
+ WBUFB(buf,51)=sd->state.dead_sit;
+ WBUFW(buf,52)=clif_setlevel(sd->status.base_level);
+
+ return packet_len_table[0x78];
+#else
+ memset(buf,0,packet_len_table[0x1d8]);
+
+ WBUFW(buf,0)=0x1d8;
+ WBUFL(buf,2)=sd->bl.id;
+ WBUFW(buf,6)=sd->speed;
+ WBUFW(buf,8)=sd->opt1;
+ WBUFW(buf,10)=sd->opt2;
+ WBUFW(buf,12)=sdoption;
+ WBUFW(buf,14)=sd->view_class;
+ WBUFW(buf,16)=sd->status.hair;
+ if (sd->view_class != JOB_WEDDING && sd->view_class !=JOB_XMAS)
+ WBUFW(buf,18) = clif_weapon_viewid(sd,9);
+ else
+ WBUFW(buf,18) = 0;
+ if (sd->equip_index[8] != sd->equip_index[9] && sd->view_class != JOB_WEDDING && sd->view_class != JOB_XMAS)
+ WBUFW(buf,20) = clif_weapon_viewid(sd,8);
+ else
+ WBUFW(buf,20) = 0;
+ WBUFW(buf,22)=sd->status.head_bottom;
+ WBUFW(buf,24)=sd->status.head_top;
+ WBUFW(buf,26)=sd->status.head_mid;
+ WBUFW(buf,28)=sd->status.hair_color;
+ WBUFW(buf,30)=sd->status.clothes_color;
+ WBUFW(buf,32)=sd->head_dir;
+ WBUFL(buf,34)=sd->status.guild_id;
+ WBUFW(buf,38)=sd->guild_emblem_id;
+ WBUFW(buf,40)=sd->status.manner;
+ WBUFW(buf,42)=sd->opt3;
+ WBUFB(buf,44)=sd->status.karma;
+ WBUFB(buf,45)=sd->sex;
+ WBUFPOS(buf,46,sd->bl.x,sd->bl.y);
+ WBUFB(buf,48)|=sd->dir & 0x0f;
+ WBUFB(buf,49)=5;
+ WBUFB(buf,50)=5;
+ WBUFB(buf,51)=sd->state.dead_sit;
+ WBUFW(buf,52)=clif_setlevel(sd->status.base_level);
+
+ return packet_len_table[0x1d8];
+#endif
+}
+
+// non-moving function for disguises [Valaris]
+static int clif_dis0078(struct map_session_data *sd, unsigned char *buf) {
+
+ nullpo_retr(0, sd);
+
+ memset(buf,0,packet_len_table[0x78]);
+
+ WBUFW(buf,0)=0x78;
+ WBUFL(buf,2)=-sd->bl.id;
+ WBUFW(buf,6)=sd->speed;
+ WBUFW(buf,8)=0;
+ WBUFW(buf,10)=0;
+ WBUFW(buf,12)=sd->status.option;
+ WBUFW(buf,14)=sd->disguise;
+ //WBUFL(buf,34)=sd->status.guild_id;
+ //WBUFL(buf,38)=sd->guild_emblem_id;
+ WBUFW(buf,42)=0;
+ WBUFB(buf,44)=0;
+ WBUFPOS(buf,46,sd->bl.x,sd->bl.y);
+ WBUFB(buf,48)|=sd->dir&0x0f;
+ WBUFB(buf,49)=5;
+ WBUFB(buf,50)=5;
+ WBUFB(buf,51)=sd->state.dead_sit;
+ WBUFW(buf,52)=0;
+
+ return packet_len_table[0x78];
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+static int clif_set007b(struct map_session_data *sd,unsigned char *buf) {
+
+ int sdoption;
+
+ nullpo_retr(0, sd);
+
+ // Disable showing Falcon when player is hide [LuzZza]
+ if(sd->disguise)
+ sdoption = OPTION_INVISIBLE;
+ else {
+ sdoption = sd->status.option;
+ if(sdoption&(OPTION_HIDE|OPTION_CLOAK|OPTION_INVISIBLE))
+ sdoption &= ~OPTION_FALCON;
+ }
+
+#if PACKETVER < 4
+ memset(buf,0,packet_len_table[0x7b]);
+
+ WBUFW(buf,0)=0x7b;
+ WBUFL(buf,2)=sd->bl.id;
+ WBUFW(buf,6)=sd->speed;
+ WBUFW(buf,8)=sd->opt1;
+ WBUFW(buf,10)=sd->opt2;
+ WBUFW(buf,12)=sdoption;
+ WBUFW(buf,14)=sd->view_class;
+ WBUFW(buf,16)=sd->status.hair;
+ if(sd->view_class != JOB_WEDDING && sd->view_class != JOB_XMAS)
+ WBUFW(buf,18)=sd->status.weapon;
+ else
+ WBUFW(buf,18)=0;
+ WBUFW(buf,20)=sd->status.head_bottom;
+ WBUFL(buf,22)=gettick();
+ WBUFW(buf,26)=sd->status.shield;
+ WBUFW(buf,28)=sd->status.head_top;
+ WBUFW(buf,30)=sd->status.head_mid;
+ WBUFW(buf,32)=sd->status.hair_color;
+ WBUFW(buf,34)=sd->status.clothes_color;
+ WBUFW(buf,36)=sd->head_dir;
+ WBUFL(buf,38)=sd->status.guild_id;
+ WBUFL(buf,42)=sd->guild_emblem_id;
+ WBUFW(buf,46)=sd->opt3;
+ WBUFB(buf,48)=sd->status.karma;
+ WBUFB(buf,49)=sd->sex;
+ WBUFPOS2(buf,50,sd->bl.x,sd->bl.y,sd->to_x,sd->to_y);
+ WBUFB(buf,55)=0x88; // Deals with acceleration in directions. [Valaris]
+ WBUFB(buf,56)=5;
+ WBUFB(buf,57)=5;
+ WBUFW(buf,58)=clif_setlevel(sd->status.base_level);
+
+ return packet_len_table[0x7b];
+#else
+ memset(buf,0,packet_len_table[0x1da]);
+
+ WBUFW(buf,0)=0x1da;
+ WBUFL(buf,2)=sd->bl.id;
+ WBUFW(buf,6)=sd->speed;
+ WBUFW(buf,8)=sd->opt1;
+ WBUFW(buf,10)=sd->opt2;
+ WBUFW(buf,12)=sdoption;
+ WBUFW(buf,14)=sd->view_class;
+ WBUFW(buf,16)=sd->status.hair;
+ if(sd->view_class != JOB_WEDDING && sd->view_class != JOB_XMAS)
+ WBUFW(buf,18)= clif_weapon_viewid(sd, 9);
+ else
+ WBUFW(buf,18)=0;
+ if(sd->equip_index[8] != sd->equip_index[9] && sd->view_class != JOB_WEDDING && sd->view_class != JOB_XMAS)
+ WBUFW(buf,20)= clif_weapon_viewid(sd, 8);
+ else
+ WBUFW(buf,20)=0;
+ WBUFW(buf,22)=sd->status.head_bottom;
+ WBUFL(buf,24)=gettick();
+ WBUFW(buf,28)=sd->status.head_top;
+ WBUFW(buf,30)=sd->status.head_mid;
+ WBUFW(buf,32)=sd->status.hair_color;
+ WBUFW(buf,34)=sd->status.clothes_color;
+ WBUFW(buf,36)=sd->head_dir;
+ WBUFL(buf,38)=sd->status.guild_id;
+ WBUFW(buf,42)=sd->guild_emblem_id;
+ WBUFW(buf,44)=sd->status.manner;
+ WBUFW(buf,46)=sd->opt3;
+ WBUFB(buf,48)=sd->status.karma;
+ WBUFB(buf,49)=sd->sex;
+ WBUFPOS2(buf,50,sd->bl.x,sd->bl.y,sd->to_x,sd->to_y);
+ WBUFB(buf,55)=0x88; // Deals with acceleration in directions. [Valaris]
+ WBUFB(buf,56)=5;
+ WBUFB(buf,57)=5;
+ WBUFW(buf,58)=clif_setlevel(sd->status.base_level);
+
+ return packet_len_table[0x1da];
+#endif
+}
+
+// moving function for disguises [Valaris]
+static int clif_dis007b(struct map_session_data *sd,unsigned char *buf) {
+
+ nullpo_retr(0, sd);
+
+ memset(buf,0,packet_len_table[0x7b]);
+
+ WBUFW(buf,0)=0x7b;
+ WBUFL(buf,2)=-sd->bl.id;
+ WBUFW(buf,6)=sd->speed;
+ WBUFW(buf,8)=0;
+ WBUFW(buf,10)=0;
+ WBUFW(buf,12)=sd->status.option;
+ WBUFW(buf,14)=sd->disguise;
+ WBUFL(buf,22)=gettick();
+ //WBUFL(buf,38)=sd->status.guild_id;
+ //WBUFL(buf,42)=sd->guild_emblem_id;
+ WBUFPOS2(buf,50,sd->bl.x,sd->bl.y,sd->to_x,sd->to_y);
+ WBUFB(buf,55)=0x88; // Deals with acceleration in directions. [Valaris]
+ WBUFB(buf,56)=5;
+ WBUFB(buf,57)=5;
+ WBUFW(buf,58)=0;
+
+ return packet_len_table[0x7b];
+}
+
+/*==========================================
+ * クラスチェンジ typeはMobの場合は1で他は0?
+ *------------------------------------------
+ */
+int clif_class_change(struct block_list *bl,int class_,int type)
+{
+ unsigned char buf[16];
+
+ nullpo_retr(0, bl);
+
+ if(class_ >= MAX_PC_CLASS) {
+ WBUFW(buf,0)=0x1b0;
+ WBUFL(buf,2)=bl->id;
+ WBUFB(buf,6)=type;
+ WBUFL(buf,7)=class_;
+
+ clif_send(buf,packet_len_table[0x1b0],bl,AREA);
+ }
+ return 0;
+}
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int clif_mob_class_change(struct mob_data *md, int class_) {
+ unsigned char buf[16];
+ int view = mob_get_viewclass(class_);
+
+ nullpo_retr(0, md);
+
+ if(view >= MAX_PC_CLASS) {
+ WBUFW(buf,0)=0x1b0;
+ WBUFL(buf,2)=md->bl.id;
+ WBUFB(buf,6)=1;
+ WBUFL(buf,7)=view;
+
+ clif_send(buf,packet_len_table[0x1b0],&md->bl,AREA);
+ }
+ return 0;
+}
+// mob equipment [Valaris]
+
+int clif_mob_equip(struct mob_data *md, int nameid) {
+ unsigned char buf[16];
+
+ nullpo_retr(0, md);
+
+ memset(buf,0,packet_len_table[0x1a4]);
+
+ WBUFW(buf,0)=0x1a4;
+ WBUFB(buf,2)=3;
+ WBUFL(buf,3)=md->bl.id;
+ WBUFL(buf,7)=nameid;
+
+ clif_send(buf,packet_len_table[0x1a4],&md->bl,AREA);
+
+ return 0;
+}
+
+/*==========================================
+ * MOB表示1
+ *------------------------------------------
+ */
+static int clif_mob0078(struct mob_data *md, unsigned char *buf)
+{
+ int level, view_class;
+
+ nullpo_retr(0, md);
+
+ level=status_get_lv(&md->bl);
+ view_class = mob_get_viewclass(md->class_);
+ if(pcdb_checkid(view_class)) {
+#if PACKETVER < 4
+ memset(buf,0,packet_len_table[0x78]);
+
+ WBUFW(buf,0)=0x78;
+ WBUFL(buf,2)=md->bl.id;
+ WBUFW(buf,6)=status_get_speed(&md->bl);
+ WBUFW(buf,8)=md->opt1;
+ WBUFW(buf,10)=md->opt2;
+ WBUFW(buf,12)=md->option;
+ WBUFW(buf,14)=view_class;
+ WBUFW(buf,16)=mob_get_hair(md->class_);
+ WBUFW(buf,18)=mob_get_weapon(md->class_);
+ WBUFW(buf,20)=mob_get_head_buttom(md->class_);
+ WBUFW(buf,22)=mob_get_shield(md->class_);
+ WBUFW(buf,24)=mob_get_head_top(md->class_);
+ WBUFW(buf,26)=mob_get_head_mid(md->class_);
+ WBUFW(buf,28)=mob_get_hair_color(md->class_);
+ WBUFW(buf,30)=mob_get_clothes_color(md->class_);
+ WBUFW(buf,32)|=md->dir&0x0f; // head direction
+ if (md->guardian_data && md->guardian_data->guild_id) { // Added guardian emblems [Valaris]
+ WBUFL(buf,34)=md->guardian_data->guild_id;
+ WBUFL(buf,38)=md->guardian_data->emblem_id;
+ }
+ WBUFW(buf,42)=md->opt3;
+ WBUFB(buf,44)=0; // karma
+ WBUFB(buf,45)=mob_get_sex(md->class_);
+ WBUFPOS(buf,46,md->bl.x,md->bl.y);
+ WBUFB(buf,48)|=md->dir&0x0f;
+ WBUFB(buf,49)=5;
+ WBUFB(buf,50)=5;
+ WBUFB(buf,51)=0; // dead or sit state
+ WBUFW(buf,52)=clif_setlevel(level);
+
+ return packet_len_table[0x78];
+#else
+ // Use 0x1d8 packet for monsters with player sprites [Valaris]
+ memset(buf,0,packet_len_table[0x1d8]);
+
+ WBUFW(buf,0)=0x1d8;
+ WBUFL(buf,2)=md->bl.id;
+ WBUFW(buf,6)=status_get_speed(&md->bl);
+ WBUFW(buf,8)=md->opt1;
+ WBUFW(buf,10)=md->opt2;
+ WBUFW(buf,12)=md->option;
+ WBUFW(buf,14)=view_class;
+ WBUFW(buf,16)=mob_get_hair(md->class_);
+ WBUFW(buf,18)=mob_get_weapon(md->class_);
+ WBUFW(buf,20)=mob_get_shield(md->class_);
+ WBUFW(buf,22)=mob_get_head_buttom(md->class_);
+ WBUFW(buf,24)=mob_get_head_top(md->class_);
+ WBUFW(buf,26)=mob_get_head_mid(md->class_);
+ WBUFW(buf,28)=mob_get_hair_color(md->class_);
+ WBUFW(buf,30)=mob_get_clothes_color(md->class_);
+ WBUFW(buf,32)|=md->dir&0x0f; // head direction
+ WBUFL(buf,34)=0; // guild id
+ WBUFW(buf,38)=0; // emblem id
+ WBUFW(buf,40)=0; // manner
+ WBUFW(buf,42)=md->opt3;
+ WBUFB(buf,44)=0; // karma
+ WBUFB(buf,45)=mob_get_sex(md->class_);
+ WBUFPOS(buf,46,md->bl.x,md->bl.y);
+ WBUFB(buf,48)|=md->dir&0x0f;
+ WBUFB(buf,49)=5;
+ WBUFB(buf,50)=5;
+ WBUFB(buf,51)=0; // dead or sit state
+ WBUFW(buf,52)=clif_setlevel(level);
+
+ return packet_len_table[0x1d8];
+#endif
+ } else {
+ // Use 0x78 packet for monsters sprites [Valaris]
+ memset(buf,0,packet_len_table[0x78]);
+
+ WBUFW(buf,0)=0x78;
+ WBUFL(buf,2)=md->bl.id;
+ WBUFW(buf,6)=status_get_speed(&md->bl);
+ WBUFW(buf,8)=md->opt1;
+ WBUFW(buf,10)=md->opt2;
+ WBUFW(buf,12)=md->option;
+ WBUFW(buf,14)=view_class;
+ if (md->guardian_data && md->guardian_data->guild_id) { // Added guardian emblems [Valaris]
+ WBUFL(buf,34)=md->guardian_data->guild_id;
+ WBUFL(buf,38)=md->guardian_data->emblem_id;
+ } // End addition
+ WBUFPOS(buf,46,md->bl.x,md->bl.y);
+ WBUFB(buf,48)|=md->dir&0x0f;
+ WBUFB(buf,49)=5;
+ WBUFB(buf,50)=5;
+ WBUFW(buf,52)=clif_setlevel(level);
+
+ return packet_len_table[0x78];
+ }
+}
+
+/*==========================================
+ * MOB表示2
+ *------------------------------------------
+ */
+static int clif_mob007b(struct mob_data *md, unsigned char *buf) {
+ int level, view_class;
+
+ nullpo_retr(0, md);
+
+ level=status_get_lv(&md->bl);
+ view_class = mob_get_viewclass(md->class_);
+ if(pcdb_checkid(view_class)) {
+#if PACKETVER < 4
+ memset(buf,0,packet_len_table[0x7b]);
+
+ WBUFW(buf,0)=0x7b;
+ WBUFL(buf,2)=md->bl.id;
+ WBUFW(buf,6)=status_get_speed(&md->bl);
+ WBUFW(buf,8)=md->opt1;
+ WBUFW(buf,10)=md->opt2;
+ WBUFW(buf,12)=md->option;
+ WBUFW(buf,14)=view_class;
+ WBUFW(buf,16)=mob_get_hair(md->class_);
+ WBUFW(buf,18)=mob_get_weapon(md->class_);
+ WBUFW(buf,20)=mob_get_head_buttom(md->class_);
+ WBUFL(buf,22)=gettick();
+ WBUFW(buf,26)=mob_get_shield(md->class_);
+ WBUFW(buf,28)=mob_get_head_top(md->class_);
+ WBUFW(buf,30)=mob_get_head_mid(md->class_);
+ WBUFW(buf,32)=mob_get_hair_color(md->class_);
+ WBUFW(buf,34)=mob_get_clothes_color(md->class_);
+ WBUFW(buf,36)=md->dir&0x0f; // head direction
+ if (md->guardian_data && md->guardian_data->guild_id) { // Added guardian emblems [Valaris]
+ WBUFL(buf,38)=md->guardian_data->guild_id;
+ WBUFL(buf,42)=md->guardian_data->emblem_id;
+ }
+ WBUFW(buf,46)=md->opt3;
+ WBUFB(buf,48)=0; // karma
+ WBUFB(buf,49)=mob_get_sex(md->class_);
+ WBUFPOS2(buf,50,md->bl.x,md->bl.y,md->to_x,md->to_y);
+ WBUFB(buf,55)=0x88; // Deals with acceleration in directions. [Valaris]
+ WBUFB(buf,56)=5;
+ WBUFB(buf,57)=5;
+ WBUFW(buf,58)=clif_setlevel(level);
+
+ return packet_len_table[0x7b];
+#else
+ // Use 0x1da packet for monsters with player sprites [Valaris]
+ memset(buf,0,packet_len_table[0x1da]);
+
+ WBUFW(buf,0)=0x1da;
+ WBUFL(buf,2)=md->bl.id;
+ WBUFW(buf,6)=status_get_speed(&md->bl);
+ WBUFW(buf,8)=md->opt1;
+ WBUFW(buf,10)=md->opt2;
+ WBUFW(buf,12)=md->option;
+ WBUFW(buf,14)=view_class;
+ WBUFW(buf,16)=mob_get_hair(md->class_);
+ WBUFW(buf,18)=mob_get_weapon(md->class_);
+ WBUFW(buf,20)=mob_get_shield(md->class_);
+ WBUFW(buf,22)=mob_get_head_buttom(md->class_);
+ WBUFL(buf,24)=gettick();
+ WBUFW(buf,28)=mob_get_head_top(md->class_);
+ WBUFW(buf,30)=mob_get_head_mid(md->class_);
+ WBUFW(buf,32)=mob_get_hair_color(md->class_);
+ WBUFW(buf,34)=mob_get_clothes_color(md->class_);
+ WBUFW(buf,36)=md->dir&0x0f; // head direction
+ if (md->guardian_data && md->guardian_data->guild_id) { // Added guardian emblems [Valaris]
+ WBUFL(buf,38)=md->guardian_data->guild_id;
+ WBUFW(buf,42)=md->guardian_data->emblem_id;
+ }
+ WBUFW(buf,44)=0; // manner
+ WBUFW(buf,46)=md->opt3;
+ WBUFB(buf,48)=0; // karma
+ WBUFB(buf,49)=mob_get_sex(md->class_);
+ WBUFPOS2(buf,50,md->bl.x,md->bl.y,md->to_x,md->to_y);
+ WBUFB(buf,55)=0x88; // Deals with acceleration in directions. [Valaris]
+ WBUFB(buf,56)=5;
+ WBUFB(buf,57)=5;
+ WBUFW(buf,58)=clif_setlevel(level);
+
+ return packet_len_table[0x1da];
+#endif
+ } else {
+ // Use 0x7b packet for monsters sprites [Valaris]
+ memset(buf,0,packet_len_table[0x7b]);
+
+ WBUFW(buf,0)=0x7b;
+ WBUFL(buf,2)=md->bl.id;
+ WBUFW(buf,6)=status_get_speed(&md->bl);
+ WBUFW(buf,8)=md->opt1;
+ WBUFW(buf,10)=md->opt2;
+ WBUFW(buf,12)=md->option;
+ WBUFW(buf,14)=view_class;
+ WBUFL(buf,22)=gettick();
+ if (md->guardian_data && md->guardian_data->guild_id) { // Added guardian emblems [Valaris]
+ WBUFL(buf,38)=md->guardian_data->guild_id;
+ WBUFL(buf,42)=md->guardian_data->emblem_id;
+ } // End addition
+ WBUFPOS2(buf,50,md->bl.x,md->bl.y,md->to_x,md->to_y);
+ WBUFB(buf,55)=0x88; // Deals with acceleration in directions. [Valaris]
+ WBUFB(buf,56)=5;
+ WBUFB(buf,57)=5;
+ level = status_get_lv(&md->bl);
+ WBUFW(buf,58)=clif_setlevel(level);
+
+ return packet_len_table[0x7b];
+ }
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+static int clif_npc0078(struct npc_data *nd, unsigned char *buf) {
+ struct guild *g=NULL;
+ int view_class;
+
+ nullpo_retr(0, nd);
+
+ memset(buf,0,packet_len_table[0x78]);
+
+ if (nd->class_ == 722 && nd->u.scr.guild_id > 0)
+ g=guild_search(nd->u.scr.guild_id);
+
+ if(mobdb_checkid(nd->class_) &&
+ pcdb_checkid((view_class = mob_get_viewclass(nd->class_)))) {
+ //Disguised player sprite
+#if PACKETVER < 4
+ memset(buf,0,packet_len_table[0x78]);
+
+ WBUFW(buf,0)=0x78;
+ WBUFL(buf,2)=nd->bl.id;
+ WBUFW(buf,6)=nd->speed;
+ WBUFW(buf,8)=nd->opt1;
+ WBUFW(buf,10)=nd->opt2;
+ WBUFW(buf,12)=nd->option;
+ WBUFW(buf,14)=view_class;
+ WBUFW(buf,16)=mob_get_hair(nd->class_);
+ WBUFW(buf,18)=mob_get_weapon(nd->class_);
+ WBUFW(buf,20)=mob_get_head_buttom(nd->class_);
+ WBUFW(buf,22)=mob_get_shield(nd->class_);
+ WBUFW(buf,24)=mob_get_head_top(nd->class_);
+ WBUFW(buf,26)=mob_get_head_mid(nd->class_);
+ WBUFW(buf,28)=mob_get_hair_color(nd->class_);
+ WBUFW(buf,30)=mob_get_clothes_color(nd->class_);
+ WBUFW(buf,32)|=nd->dir&0x0f; // head direction
+ if (g) {
+ WBUFL(buf,34)=g->guild_id;
+ WBUFL(buf,38)=g->emblem_id;
+ }
+ WBUFW(buf,42)=nd->opt3;
+ WBUFB(buf,44)=0; // karma
+ WBUFB(buf,45)=mob_get_sex(nd->class_);
+ WBUFPOS(buf,46,nd->bl.x,nd->bl.y);
+ WBUFB(buf,48)|=nd->dir&0x0f;
+ WBUFB(buf,49)=5;
+ WBUFB(buf,50)=5;
+ WBUFB(buf,51)=0; // dead or sit state
+ WBUFW(buf,52)=50; // No level info.
+
+ return packet_len_table[0x78];
+#else
+ // Use 0x1d8 packet for monsters with player sprites [Valaris]
+ memset(buf,0,packet_len_table[0x1d8]);
+
+ WBUFW(buf,0)=0x1d8;
+ WBUFL(buf,2)=nd->bl.id;
+ WBUFW(buf,6)=nd->speed;
+ WBUFW(buf,8)=nd->opt1;
+ WBUFW(buf,10)=nd->opt2;
+ WBUFW(buf,12)=nd->option;
+ WBUFW(buf,14)=view_class;
+ WBUFW(buf,16)=mob_get_hair(nd->class_);
+ WBUFW(buf,18)=mob_get_weapon(nd->class_);
+ WBUFW(buf,20)=mob_get_shield(nd->class_);
+ WBUFW(buf,22)=mob_get_head_buttom(nd->class_);
+ WBUFW(buf,24)=mob_get_head_top(nd->class_);
+ WBUFW(buf,26)=mob_get_head_mid(nd->class_);
+ WBUFW(buf,28)=mob_get_hair_color(nd->class_);
+ WBUFW(buf,30)=mob_get_clothes_color(nd->class_);
+ WBUFW(buf,32)|=nd->dir&0x0f; // head direction
+ WBUFL(buf,34)=0; // guild id
+ WBUFW(buf,38)=0; // emblem id
+ WBUFW(buf,40)=0; // manner
+ WBUFW(buf,42)=nd->opt3;
+ WBUFB(buf,44)=0; // karma
+ WBUFB(buf,45)=mob_get_sex(nd->class_);
+ WBUFPOS(buf,46,nd->bl.x,nd->bl.y);
+ WBUFB(buf,48)|=nd->dir&0x0f;
+ WBUFB(buf,49)=5;
+ WBUFB(buf,50)=5;
+ WBUFB(buf,51)=0; // dead or sit state
+ WBUFW(buf,52)=50; //No level data.
+
+ return packet_len_table[0x1d8];
+#endif
+ }
+ WBUFW(buf,0)=0x78;
+ WBUFL(buf,2)=nd->bl.id;
+ WBUFW(buf,6)=nd->speed;
+ WBUFW(buf,14)=nd->class_;
+ if (g) {
+ WBUFL(buf,22)=g->emblem_id;
+ WBUFL(buf,26)=g->guild_id;
+ // pc packet says the actual location of these are, but they are not. Why the discordance? [Skotlex]
+ // WBUFL(buf,34)=g->emblem_id;
+ // WBUFL(buf,38)=g->guild_id;
+ }
+ WBUFPOS(buf,46,nd->bl.x,nd->bl.y);
+ WBUFB(buf,48)|=nd->dir&0x0f;
+ WBUFB(buf,49)=5;
+ WBUFB(buf,50)=5;
+
+ return packet_len_table[0x78];
+}
+
+// NPC Walking [Valaris]
+static int clif_npc007b(struct npc_data *nd, unsigned char *buf) {
+ struct guild *g=NULL;
+ int view_class;
+
+ nullpo_retr(0, nd);
+
+ memset(buf,0,packet_len_table[0x7b]);
+
+ if (nd->class_ == 722 && nd->u.scr.guild_id > 0)
+ g=guild_search(nd->u.scr.guild_id);
+
+ if(mobdb_checkid(nd->class_) &&
+ pcdb_checkid((view_class = mob_get_viewclass(nd->class_)))) {
+ //Disguised player sprite
+#if PACKETVER < 4
+ memset(buf,0,packet_len_table[0x7b]);
+
+ WBUFW(buf,0)=0x7b;
+ WBUFL(buf,2)=nd->bl.id;
+ WBUFW(buf,6)=nd->speed;
+ WBUFW(buf,8)=nd->opt1;
+ WBUFW(buf,10)=nd->opt2;
+ WBUFW(buf,12)=nd->option;
+ WBUFW(buf,14)=view_class;
+ WBUFW(buf,16)=mob_get_hair(nd->class_);
+ WBUFW(buf,18)=mob_get_weapon(nd->class_);
+ WBUFW(buf,20)=mob_get_head_buttom(nd->class_);
+ WBUFL(buf,22)=gettick();
+ WBUFW(buf,26)=mob_get_shield(nd->class_);
+ WBUFW(buf,28)=mob_get_head_top(nd->class_);
+ WBUFW(buf,30)=mob_get_head_mid(nd->class_);
+ WBUFW(buf,32)=mob_get_hair_color(nd->class_);
+ WBUFW(buf,34)=mob_get_clothes_color(nd->class_);
+ WBUFW(buf,36)=nd->dir&0x0f; // head direction
+ if (g) {
+ WBUFL(buf,38)=g->guild_id;
+ WBUFL(buf,42)=g->emblem_id;
+ }
+ WBUFW(buf,46)=nd->opt3;
+ WBUFB(buf,48)=0; // karma
+ WBUFB(buf,49)=mob_get_sex(nd->class_);
+ WBUFPOS2(buf,50,nd->bl.x,nd->bl.y,nd->to_x,nd->to_y);
+ WBUFB(buf,55)=0x88; // Deals with acceleration in directions. [Valaris]
+ WBUFB(buf,56)=5;
+ WBUFB(buf,57)=5;
+ WBUFW(buf,58)=50; //Ehm.. no level data.
+
+ return packet_len_table[0x7b];
+#else
+ // Use 0x1da packet for monsters with player sprites [Valaris]
+ memset(buf,0,packet_len_table[0x1da]);
+
+ WBUFW(buf,0)=0x1da;
+ WBUFL(buf,2)=nd->bl.id;
+ WBUFW(buf,6)=nd->speed;
+ WBUFW(buf,8)=nd->opt1;
+ WBUFW(buf,10)=nd->opt2;
+ WBUFW(buf,12)=nd->option;
+ WBUFW(buf,14)=view_class;
+ WBUFW(buf,16)=mob_get_hair(nd->class_);
+ WBUFW(buf,18)=mob_get_weapon(nd->class_);
+ WBUFW(buf,20)=mob_get_shield(nd->class_);
+ WBUFW(buf,22)=mob_get_head_buttom(nd->class_);
+ WBUFL(buf,24)=gettick();
+ WBUFW(buf,28)=mob_get_head_top(nd->class_);
+ WBUFW(buf,30)=mob_get_head_mid(nd->class_);
+ WBUFW(buf,32)=mob_get_hair_color(nd->class_);
+ WBUFW(buf,34)=mob_get_clothes_color(nd->class_);
+ WBUFW(buf,36)=nd->dir&0x0f; // head direction
+ if (g) {
+ WBUFL(buf,38)=g->guild_id;
+ WBUFW(buf,42)=g->emblem_id;
+ }
+ WBUFW(buf,44)=0; // manner
+ WBUFW(buf,46)=nd->opt3;
+ WBUFB(buf,48)=0; // karma
+ WBUFB(buf,49)=mob_get_sex(nd->class_);
+ WBUFPOS2(buf,50,nd->bl.x,nd->bl.y,nd->to_x,nd->to_y);
+ WBUFB(buf,55)=0x88; // Deals with acceleration in directions. [Valaris]
+ WBUFB(buf,56)=5;
+ WBUFB(buf,57)=5;
+ WBUFW(buf,58)=50; //No level data.
+
+ return packet_len_table[0x1da];
+#endif
+ }
+
+ WBUFW(buf,0)=0x7b;
+ WBUFL(buf,2)=nd->bl.id;
+ WBUFW(buf,6)=nd->speed;
+ WBUFW(buf,14)=nd->class_;
+ if (g) {
+ WBUFL(buf,22)=g->emblem_id;
+ WBUFL(buf,26)=g->guild_id;
+ // pc packet says the actual location of these are, but they are not. Why the discordance? [Skotlex]
+ // WBUFL(buf,38)=g->emblem_id;
+ // WBUFL(buf,42)=g->guild_id;
+ }
+ WBUFL(buf,22)=gettick();
+ WBUFPOS2(buf,50,nd->bl.x,nd->bl.y,nd->to_x,nd->to_y);
+ WBUFB(buf,55)=0x88; // Deals with acceleration in directions. [Valaris]
+ WBUFB(buf,56)=5;
+ WBUFB(buf,57)=5;
+
+ return packet_len_table[0x7b];
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+static int clif_pet0078(struct pet_data *pd, unsigned char *buf) {
+ int view_class,level;
+
+ nullpo_retr(0, pd);
+
+ level = status_get_lv(&pd->bl);
+ view_class = mob_get_viewclass(pd->class_);
+ if(pcdb_checkid(view_class)) {
+#if PACKETVER < 4
+ memset(buf,0,packet_len_table[0x78]);
+
+ WBUFW(buf,0)=0x78;
+ WBUFL(buf,2)=pd->bl.id;
+ WBUFW(buf,6)=pd->speed;
+ WBUFW(buf,8)= 0; //opt1
+ WBUFW(buf,10)= 0; //opt2
+ WBUFW(buf,12)=pd->db->option;
+ WBUFW(buf,14)=view_class;
+ WBUFW(buf,16)=mob_get_hair(pd->class_);
+ WBUFW(buf,18)=mob_get_weapon(pd->class_);
+ WBUFW(buf,20)=mob_get_head_buttom(pd->class_);
+ WBUFW(buf,22)=mob_get_shield(pd->class_);
+ WBUFW(buf,24)=mob_get_head_top(pd->class_);
+ WBUFW(buf,26)=mob_get_head_mid(pd->class_);
+ WBUFW(buf,28)=mob_get_hair_color(pd->class_);
+ WBUFW(buf,30)=mob_get_clothes_color(pd->class_);
+ WBUFW(buf,32)|=pd->dir&0x0f; // head direction
+ WBUFL(buf,34)=0; //Guild id
+ WBUFL(buf,38)=0; //Guild emblem
+ WBUFW(buf,42)=0; //opt3;
+ WBUFB(buf,44)=0; // karma
+ WBUFB(buf,45)=mob_get_sex(pd->class_);
+ WBUFPOS(buf,46,pd->bl.x,pd->bl.y);
+ WBUFB(buf,48)|=pd->dir&0x0f;
+ WBUFB(buf,49)=5;
+ WBUFB(buf,50)=5;
+ WBUFB(buf,51)=0; // dead or sit state
+ WBUFW(buf,52)=clif_setlevel(level);
+
+ return packet_len_table[0x78];
+#else
+ // Use 0x1d8 packet for pets with player sprites [Valaris]
+ memset(buf,0,packet_len_table[0x1d8]);
+
+ WBUFW(buf,0)=0x1d8;
+ WBUFL(buf,2)=pd->bl.id;
+ WBUFW(buf,6)=pd->speed;
+ WBUFW(buf,8)=0; // opt1
+ WBUFW(buf,10)=0; // opt2
+ WBUFW(buf,12)=pd->db->option;
+ WBUFW(buf,14)=view_class;
+ WBUFW(buf,16)=mob_get_hair(pd->class_);
+ WBUFW(buf,18)=mob_get_weapon(pd->class_);
+ WBUFW(buf,20)=mob_get_shield(pd->class_);
+ WBUFW(buf,22)=mob_get_head_buttom(pd->class_);
+ WBUFW(buf,24)=mob_get_head_top(pd->class_);
+ WBUFW(buf,26)=mob_get_head_mid(pd->class_);
+ WBUFW(buf,28)=mob_get_hair_color(pd->class_);
+ WBUFW(buf,30)=mob_get_clothes_color(pd->class_);
+ WBUFW(buf,32)|=pd->dir&0x0f; // head direction
+ WBUFL(buf,34)=0; // guild id
+ WBUFW(buf,38)=0; // emblem id
+ WBUFW(buf,40)=0; // manner
+ WBUFW(buf,42)=0; // opt3
+ WBUFB(buf,44)=0; // karma
+ WBUFB(buf,45)=mob_get_sex(pd->class_);
+ WBUFPOS(buf,46,pd->bl.x,pd->bl.y);
+ WBUFB(buf,48)|=pd->dir&0x0f;
+ WBUFB(buf,49)=5;
+ WBUFB(buf,50)=5;
+ WBUFB(buf,51)=0; // dead or sit state
+ WBUFW(buf,52)=clif_setlevel(level);
+
+ return packet_len_table[0x1d8];
+#endif
+ } else {
+ memset(buf,0,packet_len_table[0x78]);
+
+ WBUFW(buf,0)=0x78;
+ WBUFL(buf,2)=pd->bl.id;
+ WBUFW(buf,6)=pd->speed;
+ WBUFW(buf,14)=view_class;
+ WBUFW(buf,16)=battle_config.pet_hair_style;
+ if((view_class = itemdb_viewid(pd->equip)) > 0)
+ WBUFW(buf,20)=view_class;
+ else
+ WBUFW(buf,20)=pd->equip;
+ WBUFPOS(buf,46,pd->bl.x,pd->bl.y);
+ WBUFB(buf,48)|=pd->dir&0x0f;
+ WBUFB(buf,49)=0;
+ WBUFB(buf,50)=0;
+ WBUFW(buf,52)=clif_setlevel(level);
+
+ return packet_len_table[0x78];
+ }
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+static int clif_pet007b(struct pet_data *pd, unsigned char *buf) {
+ int view_class,level;
+
+ nullpo_retr(0, pd);
+
+ level = status_get_lv(&pd->bl);
+ view_class = mob_get_viewclass(pd->class_);
+ if(pcdb_checkid(view_class)) {
+#if PACKETVER < 4
+ memset(buf,0,packet_len_table[0x7b]);
+
+ WBUFW(buf,0)=0x7b;
+ WBUFL(buf,2)=pd->bl.id;
+ WBUFW(buf,6)=pd->speed;
+ WBUFW(buf,8)= 0; //opt1;
+ WBUFW(buf,10)= 0; //opt2;
+ WBUFW(buf,12)=pd->db->option;
+ WBUFW(buf,14)=view_class;
+ WBUFW(buf,16)=mob_get_hair(pd->class_);
+ WBUFW(buf,18)=mob_get_weapon(pd->class_);
+ WBUFW(buf,20)=mob_get_head_buttom(pd->class_);
+ WBUFL(buf,22)=gettick();
+ WBUFW(buf,26)=mob_get_shield(pd->class_);
+ WBUFW(buf,28)=mob_get_head_top(pd->class_);
+ WBUFW(buf,30)=mob_get_head_mid(pd->class_);
+ WBUFW(buf,32)=mob_get_hair_color(pd->class_);
+ WBUFW(buf,34)=mob_get_clothes_color(pd->class_);
+ WBUFW(buf,36)=pd->dir&0x0f; // head direction
+ WBUFL(buf,38)=0; // guild id
+ WBUFL(buf,42)=0; // emblem id
+ WBUFW(buf,46)=0; // opt3;
+ WBUFB(buf,48)=0; // karma
+ WBUFB(buf,49)=mob_get_sex(pd->class_);
+ WBUFPOS2(buf,50,pd->bl.x,pd->bl.y,pd->to_x,pd->to_y);
+ WBUFB(buf,55)=0x88; // Deals with acceleration in directions. [Valaris]
+ WBUFB(buf,56)=0; //0? These are always five for mobs and pets, /hmm [Skotlex]
+ WBUFB(buf,57)=0;
+ WBUFW(buf,58)=clif_setlevel(level);
+
+ return packet_len_table[0x7b];
+#else
+ // Use 0x1da packet for monsters with player sprites [Valaris]
+ memset(buf,0,packet_len_table[0x1da]);
+
+ WBUFW(buf,0)=0x1da;
+ WBUFL(buf,2)=pd->bl.id;
+ WBUFW(buf,6)=pd->speed;
+ WBUFW(buf,8)=0; // opt1
+ WBUFW(buf,10)=0; // opt2
+ WBUFW(buf,12)=pd->db->option;
+ WBUFW(buf,14)=view_class;
+ WBUFW(buf,16)=mob_get_hair(pd->class_);
+ WBUFW(buf,18)=mob_get_weapon(pd->class_);
+ WBUFW(buf,20)=mob_get_shield(pd->class_);
+ WBUFW(buf,22)=mob_get_head_buttom(pd->class_);
+ WBUFL(buf,24)=gettick();
+ WBUFW(buf,28)=mob_get_head_top(pd->class_);
+ WBUFW(buf,30)=mob_get_head_mid(pd->class_);
+ WBUFW(buf,32)=mob_get_hair_color(pd->class_);
+ WBUFW(buf,34)=mob_get_clothes_color(pd->class_);
+ WBUFW(buf,36)=pd->dir&0x0f; // head direction
+ WBUFL(buf,38)=0; // guild id
+ WBUFW(buf,42)=0; // emblem id
+ WBUFW(buf,44)=0; // manner
+ WBUFW(buf,46)=0; // opt3
+ WBUFB(buf,48)=0; // karma
+ WBUFB(buf,49)=mob_get_sex(pd->class_);
+ WBUFPOS2(buf,50,pd->bl.x,pd->bl.y,pd->to_x,pd->to_y);
+ WBUFB(buf,55)=0x88; // Deals with acceleration in directions. [Valaris]
+ WBUFB(buf,56)=0;
+ WBUFB(buf,57)=0;
+ WBUFW(buf,58)=clif_setlevel(level);
+
+ return packet_len_table[0x1da];
+#endif
+ } else {
+ // Use 0x7b packet for pets sprites [Valaris]
+ memset(buf,0,packet_len_table[0x7b]);
+
+ WBUFW(buf,0)=0x7b;
+ WBUFL(buf,2)=pd->bl.id;
+ WBUFW(buf,6)=pd->speed;
+ WBUFW(buf,14)=view_class;
+ WBUFW(buf,16)=battle_config.pet_hair_style;
+ if ((view_class = itemdb_viewid(pd->equip)) > 0)
+ WBUFW(buf,20)=view_class;
+ else
+ WBUFW(buf,20)=pd->equip;
+ WBUFL(buf,22)=gettick();
+ WBUFPOS2(buf,50,pd->bl.x,pd->bl.y,pd->to_x,pd->to_y);
+ WBUFB(buf,55)=0x88; // Deals with acceleration in directions. [Valaris]
+ WBUFB(buf,56)=0;
+ WBUFB(buf,57)=0;
+ WBUFW(buf,58)=clif_setlevel(level);
+
+ return packet_len_table[0x7b];
+ }
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+static int clif_set01e1(struct map_session_data *sd, unsigned char *buf) {
+ nullpo_retr(0, sd);
+
+ WBUFW(buf,0)=0x1e1;
+ WBUFL(buf,2)=sd->bl.id;
+ WBUFW(buf,6)=sd->spiritball;
+
+ return packet_len_table[0x1e1];
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+static int clif_set0192(int fd, int m, int x, int y, int type) {
+ WFIFOHEAD(fd, packet_len_table[0x192]);
+ WFIFOW(fd,0) = 0x192;
+ WFIFOW(fd,2) = x;
+ WFIFOW(fd,4) = y;
+ WFIFOW(fd,6) = type;
+ memcpy(WFIFOP(fd,8),map[m].name,MAP_NAME_LENGTH);
+ WFIFOSET(fd,packet_len_table[0x192]);
+
+ return 0;
+}
+
+// new and improved weather display [Valaris]
+int clif_weather_sub(int fd, int type) {
+ WFIFOHEAD(fd, packet_len_table[0x1f3]);
+ WFIFOW(fd,0) = 0x1f3;
+ WFIFOL(fd,2) = -10;
+ WFIFOL(fd,6) = type;
+ WFIFOSET(fd,packet_len_table[0x1f3]);
+
+ return 0;
+}
+
+int clif_weather(int m) {
+ int i;
+
+ struct map_session_data *sd=NULL;
+
+ for(i = 0; i < fd_max; i++) {
+ if (session[i] && (sd = session[i]->session_data) != NULL && sd->state.auth && sd->bl.m == m) {
+ WFIFOHEAD(sd->fd, packet_len_table[0x80]);
+ WFIFOW(sd->fd,0) = 0x80;
+ WFIFOL(sd->fd,2) = -10;
+ WFIFOB(sd->fd,6) = 0;
+ WFIFOSET(sd->fd,packet_len_table[0x80]);
+
+ if (map[sd->bl.m].flag.snow || map[sd->bl.m].flag.clouds || map[sd->bl.m].flag.fog || map[sd->bl.m].flag.fireworks ||
+ map[sd->bl.m].flag.sakura || map[sd->bl.m].flag.leaves || map[sd->bl.m].flag.rain || map[sd->bl.m].flag.clouds2) {
+ WFIFOHEAD(sd->fd, packet_len_table[0x7c]);
+ WFIFOW(sd->fd,0)=0x7c;
+ WFIFOL(sd->fd,2)=-10;
+ WFIFOW(sd->fd,6)=0;
+ WFIFOW(sd->fd,8)=0;
+ WFIFOW(sd->fd,10)=0;
+ WFIFOW(sd->fd,12)=OPTION_INVISIBLE;
+ WFIFOW(sd->fd,20)=100;
+ WFIFOPOS(sd->fd,36,sd->bl.x,sd->bl.y);
+ WFIFOSET(sd->fd,packet_len_table[0x7c]);
+
+ if (map[sd->bl.m].flag.snow)
+ clif_weather_sub(sd->fd, 162);
+ if (map[sd->bl.m].flag.clouds)
+ clif_weather_sub(sd->fd, 233);
+ if (map[sd->bl.m].flag.clouds2)
+ clif_weather_sub(sd->fd, 516);
+ if (map[sd->bl.m].flag.fog)
+ clif_weather_sub(sd->fd, 515);
+ if (map[sd->bl.m].flag.fireworks) {
+ clif_weather_sub(sd->fd, 297);
+ clif_weather_sub(sd->fd, 299);
+ clif_weather_sub(sd->fd, 301);
+ }
+ if (map[sd->bl.m].flag.sakura)
+ clif_weather_sub(sd->fd, 163);
+ if (map[sd->bl.m].flag.leaves)
+ clif_weather_sub(sd->fd, 333);
+ if (map[sd->bl.m].flag.rain)
+ clif_weather_sub(sd->fd, 161);
+ }
+ }
+ }
+
+ return 0;
+}
+
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int clif_spawnpc(struct map_session_data *sd) {
+ unsigned char buf[128];
+
+ nullpo_retr(0, sd);
+
+ clif_set0078(sd, buf);
+
+#if PACKETVER < 4
+ WBUFW(buf, 0) = 0x79;
+ WBUFW(buf,51) = clif_setlevel(sd->status.base_level);
+ clif_send(buf, packet_len_table[0x79], &sd->bl, AREA_WOS);
+#else
+ WBUFW(buf, 0) = 0x1d9;
+ WBUFW(buf,51) = clif_setlevel(sd->status.base_level);
+ clif_send(buf, packet_len_table[0x1d9], &sd->bl, AREA_WOS);
+#endif
+
+ if(sd->disguise > 0) {
+ int len;
+ memset(buf,0,packet_len_table[0x7c]);
+ WBUFW(buf,0)=0x7c;
+ WBUFL(buf,2)=-sd->bl.id;
+ WBUFW(buf,6)=sd->speed;
+ WBUFW(buf,12)=sd->status.option;
+ WBUFW(buf,20)=sd->disguise;
+ WBUFPOS(buf,36,sd->bl.x,sd->bl.y);
+ clif_send(buf,packet_len_table[0x7c],&sd->bl,AREA);
+
+ len = clif_dis0078(sd,buf);
+ clif_send(buf,len,&sd->bl,AREA);
+ }
+
+ if (sd->spiritball > 0)
+ clif_spiritball(sd);
+
+ if (sd->status.guild_id > 0) { // force display of guild emblem [Valaris]
+ struct guild *g = guild_search(sd->status.guild_id);
+ if (g)
+ clif_guild_emblem(sd,g);
+ } // end addition [Valaris]
+
+ if (map[sd->bl.m].flag.snow || map[sd->bl.m].flag.clouds || map[sd->bl.m].flag.fog || map[sd->bl.m].flag.fireworks ||
+ map[sd->bl.m].flag.sakura || map[sd->bl.m].flag.leaves || map[sd->bl.m].flag.rain || map[sd->bl.m].flag.clouds2) {
+ WFIFOHEAD(sd->fd, packet_len_table[0x7c]);
+ WFIFOW(sd->fd,0)=0x7c;
+ WFIFOL(sd->fd,2)=-10;
+ WFIFOW(sd->fd,6)=0;
+ WFIFOW(sd->fd,8)=0;
+ WFIFOW(sd->fd,10)=0;
+ WFIFOW(sd->fd,12)=OPTION_INVISIBLE;
+ WFIFOW(sd->fd,20)=100;
+ WFIFOPOS(sd->fd,36,sd->bl.x,sd->bl.y);
+ WFIFOSET(sd->fd,packet_len_table[0x7c]);
+
+ if (map[sd->bl.m].flag.snow)
+ clif_weather_sub(sd->fd, 162);
+ if (map[sd->bl.m].flag.clouds)
+ clif_weather_sub(sd->fd, 233);
+ if (map[sd->bl.m].flag.clouds2)
+ clif_weather_sub(sd->fd, 516);
+ if (map[sd->bl.m].flag.fog)
+ clif_weather_sub(sd->fd, 515);
+ if (map[sd->bl.m].flag.fireworks) {
+ clif_weather_sub(sd->fd, 297);
+ clif_weather_sub(sd->fd, 299);
+ clif_weather_sub(sd->fd, 301);
+ }
+ if (map[sd->bl.m].flag.sakura)
+ clif_weather_sub(sd->fd, 163);
+ if (map[sd->bl.m].flag.leaves)
+ clif_weather_sub(sd->fd, 333);
+ if (map[sd->bl.m].flag.rain)
+ clif_weather_sub(sd->fd, 161);
+ }
+
+ //New 'night' effect by dynamix [Skotlex]
+ if (night_flag && map[sd->bl.m].flag.nightenabled)
+ { //Display night.
+ if (sd->state.night) //It must be resent because otherwise players get this annoying aura...
+ clif_status_load(&sd->bl, SI_NIGHT, 0);
+ else
+ sd->state.night = 1;
+ clif_status_load(&sd->bl, SI_NIGHT, 1);
+ } else if (sd->state.night) { //Clear night display.
+ clif_status_load(&sd->bl, SI_NIGHT, 0);
+ sd->state.night = 0;
+ }
+
+ if(sd->state.size==2) // tiny/big players [Valaris]
+ clif_specialeffect(&sd->bl,423,0);
+ else if(sd->state.size==1)
+ clif_specialeffect(&sd->bl,421,0);
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int clif_spawnnpc(struct npc_data *nd)
+{
+ unsigned char buf[64];
+ int len;
+
+ nullpo_retr(0, nd);
+
+ if(nd->class_ < 0 || nd->flag&1 || nd->class_ == INVISIBLE_CLASS)
+ return 0;
+
+ if(!mobdb_checkid(nd->class_) ||
+ !pcdb_checkid(mob_get_viewclass(nd->class_))) {
+ //Normal npcs.
+ memset(buf,0,packet_len_table[0x7c]);
+
+ WBUFW(buf,0)=0x7c;
+ WBUFL(buf,2)=nd->bl.id;
+ WBUFW(buf,6)=nd->speed;
+ WBUFW(buf,20)=nd->class_;
+ WBUFPOS(buf,36,nd->bl.x,nd->bl.y);
+
+ clif_send(buf,packet_len_table[0x7c],&nd->bl,AREA);
+ }
+ len = clif_npc0078(nd,buf);
+ clif_send(buf,len,&nd->bl,AREA);
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int clif_spawnmob(struct mob_data *md)
+{
+ unsigned char buf[64];
+ int len;
+ int viewclass = mob_get_viewclass(md->class_);
+
+ if (!pcdb_checkid(viewclass)) {
+ memset(buf,0,packet_len_table[0x7c]);
+
+ WBUFW(buf,0)=0x7c;
+ WBUFL(buf,2)=md->bl.id;
+ WBUFW(buf,6)=md->speed;
+ WBUFW(buf,8)=md->opt1;
+ WBUFW(buf,10)=md->opt2;
+ WBUFW(buf,12)=md->option;
+ WBUFW(buf,20)=viewclass;
+ WBUFPOS(buf,36,md->bl.x,md->bl.y);
+ clif_send(buf,packet_len_table[0x7c],&md->bl,AREA);
+ }
+
+ len = clif_mob0078(md,buf);
+ clif_send(buf,len,&md->bl,AREA);
+
+ if(battle_config.save_clothcolor && pcdb_checkid(viewclass) && mob_get_clothes_color(md->class_) > 0) // [Valaris]
+ clif_changelook(&md->bl, LOOK_CLOTHES_COLOR, mob_get_clothes_color(md->class_));
+
+ if (mob_get_equip(md->class_) > 0) // mob equipment [Valaris]
+ clif_mob_equip(md,mob_get_equip(md->class_));
+
+ if(md->special_state.size==2) // tiny/big mobs [Valaris]
+ clif_specialeffect(&md->bl,423,0);
+ else if(md->special_state.size==1)
+ clif_specialeffect(&md->bl,421,0);
+
+ return 0;
+}
+
+// pet
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int clif_spawnpet(struct pet_data *pd)
+{
+ unsigned char buf[64];
+ int len;
+ int viewclass = mob_get_viewclass(pd->class_);
+
+ if (!pcdb_checkid(viewclass)) {
+ memset(buf,0,packet_len_table[0x7c]);
+
+ WBUFW(buf,0)=0x7c;
+ WBUFL(buf,2)=pd->bl.id;
+ WBUFW(buf,6)=pd->speed;
+ WBUFW(buf,20)=viewclass;
+ WBUFPOS(buf,36,pd->bl.x,pd->bl.y);
+
+ clif_send(buf,packet_len_table[0x7c],&pd->bl,AREA);
+ }
+
+ len = clif_pet0078(pd,buf);
+ clif_send(buf,len,&pd->bl,AREA);
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int clif_movepet(struct pet_data *pd) {
+ unsigned char buf[256];
+ int len;
+
+ nullpo_retr(0, pd);
+
+ len = clif_pet007b(pd,buf);
+ clif_send(buf,len,&pd->bl,AREA);
+
+ return 0;
+}
+
+/*==========================================
+ * npc walking [Valaris]
+ *------------------------------------------
+ */
+int clif_movenpc(struct npc_data *nd) {
+ unsigned char buf[256];
+ int len;
+
+ nullpo_retr(0, nd);
+
+ len = clif_npc007b(nd,buf);
+ clif_send(buf,len,&nd->bl,AREA);
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int clif_servertick(struct map_session_data *sd)
+{
+ int fd;
+
+ nullpo_retr(0, sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd, packet_len_table[0x7f]);
+ WFIFOW(fd,0)=0x7f;
+ WFIFOL(fd,2)=sd->server_tick;
+ WFIFOSET(fd,packet_len_table[0x7f]);
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int clif_walkok(struct map_session_data *sd)
+{
+ int fd;
+
+ nullpo_retr(0, sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd, packet_len_table[0x87]);
+ WFIFOW(fd,0)=0x87;
+ WFIFOL(fd,2)=gettick();
+ WFIFOPOS2(fd,6,sd->bl.x,sd->bl.y,sd->to_x,sd->to_y);
+ WFIFOB(fd,11)=0x88;
+ WFIFOSET(fd,packet_len_table[0x87]);
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int clif_movechar(struct map_session_data *sd) {
+ int fd;
+ int len;
+ unsigned char buf[256];
+
+ nullpo_retr(0, sd);
+
+ fd = sd->fd;
+
+ len = clif_set007b(sd, buf);
+ clif_send(buf, len, &sd->bl, AREA_WOS);
+
+ if (map[sd->bl.m].flag.snow || map[sd->bl.m].flag.clouds || map[sd->bl.m].flag.fog || map[sd->bl.m].flag.fireworks ||
+ map[sd->bl.m].flag.sakura || map[sd->bl.m].flag.leaves || map[sd->bl.m].flag.rain || map[sd->bl.m].flag.clouds2) {
+ memset(buf,0,packet_len_table[0x7b]);
+ WBUFW(buf,0)=0x7b;
+ WBUFL(buf,2)=-10;
+ WBUFW(buf,6)=sd->speed;
+ WBUFW(buf,8)=0;
+ WBUFW(buf,10)=0;
+ WBUFW(buf,12)=OPTION_INVISIBLE;
+ WBUFW(buf,14)=100;
+ WBUFL(buf,22)=gettick();
+ WBUFPOS2(buf,50,sd->bl.x,sd->bl.y,sd->to_x,sd->to_y);
+ WBUFB(buf,56)=5;
+ WBUFB(buf,57)=5;
+ clif_send(buf, len, &sd->bl, SELF);
+ }
+
+ if(sd->disguise) {
+ memset(buf,0,packet_len_table[0x7b]);
+ len = clif_dis007b(sd, buf);
+ clif_send(buf, len, &sd->bl, AREA);
+ return 0;
+ }
+
+ //Stupid client that needs this resent every time someone walks :X
+ if(battle_config.save_clothcolor && sd->status.clothes_color > 0 && ((sd->view_class != JOB_WEDDING && sd->view_class !=JOB_XMAS) ||
+ (sd->view_class==JOB_WEDDING && !battle_config.wedding_ignorepalette) || (sd->view_class==JOB_XMAS && !battle_config.xmas_ignorepalette)))
+ clif_changelook(&sd->bl,LOOK_CLOTHES_COLOR,sd->status.clothes_color);
+
+
+ if(sd->state.size==2) // tiny/big players [Valaris]
+ clif_specialeffect(&sd->bl,423,0);
+ else if(sd->state.size==1)
+ clif_specialeffect(&sd->bl,421,0);
+
+ return 0;
+}
+
+/*==========================================
+ * Delays the map_quit of a player after they are disconnected. [Skotlex]
+ *------------------------------------------
+ */
+static int clif_delayquit(int tid, unsigned int tick, int id, int data) {
+ struct map_session_data *sd = NULL;
+
+ if (chrif_isconnect())
+ { //Remove player from map server
+ if ((sd = map_id2sd(id)) != NULL && sd->fd == 0) //Should be a disconnected player.
+ map_quit(sd);
+ } else //Save later.
+ add_timer(tick + 10000, clif_delayquit, id, 0);
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+void clif_quitsave(int fd,struct map_session_data *sd)
+{
+ if (chrif_isconnect() && (!battle_config.prevent_logout || DIFF_TICK(gettick(), sd->canlog_tick) > battle_config.prevent_logout))
+ map_quit(sd);
+ else if (sd->fd)
+ { //Disassociate session from player (session is deleted after this function was called)
+ //And set a timer to delete this player later.
+ session[fd]->session_data = NULL;
+ sd->fd = 0;
+ add_timer(gettick() + 10000, clif_delayquit, sd->bl.id, 0);
+ }
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+static int clif_waitclose(int tid, unsigned int tick, int id, int data) {
+ if (session[id] && session[id]->func_parse == clif_parse) //Avoid disconnecting non-players, as pointed out by End of Exam [Skotlex]
+ session[id]->eof = 1;
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+void clif_setwaitclose(int fd) {
+ struct map_session_data *sd;
+
+ // if player is not already in the game (double connection probably)
+ if ((sd = (struct map_session_data*)session[fd]->session_data) == NULL) {
+ // limited timer, just to send information.
+ add_timer(gettick() + 1000, clif_waitclose, fd, 0);
+ } else
+ add_timer(gettick() + 5000, clif_waitclose, fd, 0);
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int clif_changemap(struct map_session_data *sd, short map, int x, int y) {
+ int fd;
+
+ nullpo_retr(0, sd);
+
+ fd = sd->fd;
+
+ WFIFOHEAD(fd, packet_len_table[0x91]);
+ WFIFOW(fd,0) = 0x91;
+ memcpy(WFIFOP(fd,2), mapindex_id2name(map), MAP_NAME_LENGTH);
+ WFIFOW(fd,18) = x;
+ WFIFOW(fd,20) = y;
+ WFIFOSET(fd, packet_len_table[0x91]);
+
+ if(pc_isdead(sd)) // If player is dead, and is spawned (such as @refresh) send death packet. [Valaris]
+ clif_clearchar_area(&sd->bl,1);
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int clif_changemapserver(struct map_session_data *sd, char *mapname, int x, int y, int ip, int port) {
+ int fd;
+
+ nullpo_retr(0, sd);
+
+ fd = sd->fd;
+ WFIFOHEAD(fd, packet_len_table[0x92]);
+ WFIFOW(fd,0) = 0x92;
+ //Better not trust the null-terminator is there. [Skotlex]
+ memcpy(WFIFOP(fd,2), mapname, MAP_NAME_LENGTH);
+ WFIFOB(fd,17) = 0; //Null terminator for mapname
+ WFIFOW(fd,18) = x;
+ WFIFOW(fd,20) = y;
+ WFIFOL(fd,22) = ip;
+ WFIFOW(fd,26) = port;
+ WFIFOSET(fd, packet_len_table[0x92]);
+
+ return 0;
+}
+
+int clif_blown(struct block_list *bl) {
+//Previous Aegis versions simply used clif_fixpos, but it seems clif_slide works better on current clients.
+// return clif_fixpos(bl);
+ return clif_slide(bl, bl->x, bl->y);
+
+}
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int clif_fixpos(struct block_list *bl) {
+ unsigned char buf[16];
+
+ nullpo_retr(0, bl);
+
+ WBUFW(buf,0)=0x88;
+
+ if (bl->type ==BL_PC && ((struct map_session_data *)bl)->disguise)
+ WBUFL(buf,2)=-bl->id;
+ else
+ WBUFL(buf,2)=bl->id;
+
+ WBUFW(buf,6)=bl->x;
+ WBUFW(buf,8)=bl->y;
+ clif_send(buf, packet_len_table[0x88], bl, AREA);
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int clif_npcbuysell(struct map_session_data* sd, int id) {
+ int fd;
+
+ nullpo_retr(0, sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd, packet_len_table[0xc4]);
+ WFIFOW(fd,0)=0xc4;
+ WFIFOL(fd,2)=id;
+ WFIFOSET(fd,packet_len_table[0xc4]);
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int clif_buylist(struct map_session_data *sd, struct npc_data *nd) {
+ struct item_data *id;
+ int fd,i,val;
+
+ nullpo_retr(0, sd);
+ nullpo_retr(0, nd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd, 200 * 11 + 4);
+ WFIFOW(fd,0)=0xc6;
+ for(i=0;nd->u.shop_item[i].nameid > 0;i++){
+ id = itemdb_search(nd->u.shop_item[i].nameid);
+ val=nd->u.shop_item[i].value;
+ WFIFOL(fd,4+i*11)=val;
+ if (!id->flag.value_notdc)
+ val=pc_modifybuyvalue(sd,val);
+ WFIFOL(fd,8+i*11)=val;
+ WFIFOB(fd,12+i*11)=itemtype(id->type);
+ if (id->view_id > 0)
+ WFIFOW(fd,13+i*11)=id->view_id;
+ else
+ WFIFOW(fd,13+i*11)=nd->u.shop_item[i].nameid;
+ }
+ WFIFOW(fd,2)=i*11+4;
+ WFIFOSET(fd,WFIFOW(fd,2));
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int clif_selllist(struct map_session_data *sd) {
+ int fd,i,c=0,val;
+
+ nullpo_retr(0, sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd, MAX_INVENTORY * 10 + 4);
+ WFIFOW(fd,0)=0xc7;
+ for(i=0;i<MAX_INVENTORY;i++) {
+ if(sd->status.inventory[i].nameid > 0 && sd->inventory_data[i]) {
+ if (!itemdb_cansell(sd->status.inventory[i].nameid, pc_isGM(sd)))
+ continue;
+
+ val=sd->inventory_data[i]->value_sell;
+ if (val < 0)
+ continue;
+ WFIFOW(fd,4+c*10)=i+2;
+ WFIFOL(fd,6+c*10)=val;
+ if (!sd->inventory_data[i]->flag.value_notoc)
+ val=pc_modifysellvalue(sd,val);
+ WFIFOL(fd,10+c*10)=val;
+ c++;
+ }
+ }
+ WFIFOW(fd,2)=c*10+4;
+ WFIFOSET(fd,WFIFOW(fd,2));
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int clif_scriptmes(struct map_session_data *sd, int npcid, char *mes) {
+ int fd;
+ int slen = strlen(mes) + 9;
+ WFIFOHEAD(fd, slen);
+
+ nullpo_retr(0, sd);
+
+ fd=sd->fd;
+ WFIFOW(fd,0)=0xb4;
+ WFIFOW(fd,2)=slen;
+ WFIFOL(fd,4)=npcid;
+ strcpy((char*)WFIFOP(fd,8),mes);
+ WFIFOSET(fd,WFIFOW(fd,2));
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int clif_scriptnext(struct map_session_data *sd,int npcid) {
+ int fd;
+
+ nullpo_retr(0, sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd, packet_len_table[0xb5]);
+ WFIFOW(fd,0)=0xb5;
+ WFIFOL(fd,2)=npcid;
+ WFIFOSET(fd,packet_len_table[0xb5]);
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int clif_scriptclose(struct map_session_data *sd, int npcid) {
+ int fd;
+
+ nullpo_retr(0, sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd, packet_len_table[0xb6]);
+ WFIFOW(fd,0)=0xb6;
+ WFIFOL(fd,2)=npcid;
+ WFIFOSET(fd,packet_len_table[0xb6]);
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int clif_scriptmenu(struct map_session_data *sd, int npcid, char *mes) {
+ int fd;
+ int slen = strlen(mes) + 8;
+ WFIFOHEAD(fd, slen);
+
+ nullpo_retr(0, sd);
+
+ fd=sd->fd;
+ WFIFOW(fd,0)=0xb7;
+ WFIFOW(fd,2)=slen;
+ WFIFOL(fd,4)=npcid;
+ strcpy((char*)WFIFOP(fd,8),mes);
+ WFIFOSET(fd,WFIFOW(fd,2));
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int clif_scriptinput(struct map_session_data *sd, int npcid) {
+ int fd;
+
+ nullpo_retr(0, sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd, packet_len_table[0x142]);
+ WFIFOW(fd,0)=0x142;
+ WFIFOL(fd,2)=npcid;
+ WFIFOSET(fd,packet_len_table[0x142]);
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int clif_scriptinputstr(struct map_session_data *sd, int npcid) {
+ int fd;
+
+ nullpo_retr(0, sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd, packet_len_table[0x1d4]);
+ WFIFOW(fd,0)=0x1d4;
+ WFIFOL(fd,2)=npcid;
+ WFIFOSET(fd,packet_len_table[0x1d4]);
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int clif_viewpoint(struct map_session_data *sd, int npc_id, int type, int x, int y, int id, int color) {
+ int fd;
+
+ nullpo_retr(0, sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd, packet_len_table[0x144]);
+ WFIFOW(fd,0)=0x144;
+ WFIFOL(fd,2)=npc_id;
+ WFIFOL(fd,6)=type;
+ WFIFOL(fd,10)=x;
+ WFIFOL(fd,14)=y;
+ WFIFOB(fd,18)=id;
+ WFIFOL(fd,19)=color;
+ WFIFOSET(fd,packet_len_table[0x144]);
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int clif_cutin(struct map_session_data *sd, char *image, int type) {
+ int fd;
+
+ nullpo_retr(0, sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd, packet_len_table[0x1b3]);
+ WFIFOW(fd,0)=0x1b3;
+ strncpy((char*)WFIFOP(fd,2),image,64);
+ WFIFOB(fd,66)=type;
+ WFIFOSET(fd,packet_len_table[0x1b3]);
+
+ return 0;
+}
+
+/*==========================================
+ * Fills in card data from the given item and into the buffer. [Skotlex]
+ *------------------------------------------
+ */
+static void clif_addcards(unsigned char* buf, struct item* item)
+{
+ int i=0,j;
+ if (item == NULL) { //Blank data
+ WBUFW(buf,0)=0;
+ WBUFW(buf,2)=0;
+ WBUFW(buf,4)=0;
+ WBUFW(buf,6)=0;
+ return;
+ }
+ if(item->card[0]==(short)0xff00) { //pet eggs
+ WBUFW(buf,0)=0;
+ WBUFW(buf,2)=0;
+ WBUFW(buf,4)=0;
+ WBUFW(buf,6)=item->card[3]; //Pet renamed flag.
+ return;
+ }
+ if(item->card[0]==0x00ff || item->card[0]==0x00fe) { //Forged/created items
+ WBUFW(buf,0)=item->card[0];
+ WBUFW(buf,2)=item->card[1];
+ WBUFW(buf,4)=item->card[2];
+ WBUFW(buf,6)=item->card[3];
+ return;
+ }
+ //Client only receives four cards.. so randomly send them a set of cards. [Skotlex]
+ if (MAX_SLOTS > 4 && (j = itemdb_slot(item->nameid)) > 4)
+ i = rand()%(j-3); //eg: 6 slots, possible i values: 0->3, 1->4, 2->5 => i = rand()%3;
+
+ //Normal items.
+ if (item->card[i] > 0 && (j=itemdb_viewid(item->card[i])) > 0)
+ WBUFW(buf,0)=j;
+ else
+ WBUFW(buf,0)= item->card[i];
+
+ if (item->card[++i] > 0 && (j=itemdb_viewid(item->card[i])) > 0)
+ WBUFW(buf,2)=j;
+ else
+ WBUFW(buf,2)=item->card[i];
+
+ if (item->card[++i] > 0 && (j=itemdb_viewid(item->card[i])) > 0)
+ WBUFW(buf,4)=j;
+ else
+ WBUFW(buf,4)=item->card[i];
+
+ if (item->card[++i] > 0 && (j=itemdb_viewid(item->card[i])) > 0)
+ WBUFW(buf,6)=j;
+ else
+ WBUFW(buf,6)=item->card[i];
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int clif_additem(struct map_session_data *sd, int n, int amount, int fail) {
+ int fd;
+ unsigned char *buf;
+
+ nullpo_retr(0, sd);
+
+ fd = sd->fd;
+ if (!session_isActive(fd)) //Sasuke-
+ return 0;
+
+ WFIFOHEAD(fd,packet_len_table[0xa0]);
+ buf = WFIFOP(fd,0);
+ if(fail) {
+ WBUFW(buf,0)=0xa0;
+ WBUFW(buf,2)=n+2;
+ WBUFW(buf,4)=amount;
+ WBUFW(buf,6)=0;
+ WBUFB(buf,8)=0;
+ WBUFB(buf,9)=0;
+ WBUFB(buf,10)=0;
+ WBUFW(buf,11)=0;
+ WBUFW(buf,13)=0;
+ WBUFW(buf,15)=0;
+ WBUFW(buf,17)=0;
+ WBUFW(buf,19)=0;
+ WBUFB(buf,21)=0;
+ WBUFB(buf,22)=fail;
+ } else {
+ if (n<0 || n>=MAX_INVENTORY || sd->status.inventory[n].nameid <=0 || sd->inventory_data[n] == NULL)
+ return 1;
+
+ WBUFW(buf,0)=0xa0;
+ WBUFW(buf,2)=n+2;
+ WBUFW(buf,4)=amount;
+ if (sd->inventory_data[n]->view_id > 0)
+ WBUFW(buf,6)=sd->inventory_data[n]->view_id;
+ else
+ WBUFW(buf,6)=sd->status.inventory[n].nameid;
+ WBUFB(buf,8)=sd->status.inventory[n].identify;
+ WBUFB(buf,9)=sd->status.inventory[n].attribute;
+ WBUFB(buf,10)=sd->status.inventory[n].refine;
+ clif_addcards(WBUFP(buf,11), &sd->status.inventory[n]);
+ WBUFW(buf,19)=pc_equippoint(sd,n);
+ WBUFB(buf,21)=itemtype(sd->inventory_data[n]->type);
+ WBUFB(buf,22)=fail;
+ }
+
+ WFIFOSET(fd,packet_len_table[0xa0]);
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int clif_delitem(struct map_session_data *sd,int n,int amount)
+{
+ int fd;
+
+ nullpo_retr(0, sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd, packet_len_table[0xaf]);
+ WFIFOW(fd,0)=0xaf;
+ WFIFOW(fd,2)=n+2;
+ WFIFOW(fd,4)=amount;
+
+ WFIFOSET(fd,packet_len_table[0xaf]);
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int clif_itemlist(struct map_session_data *sd)
+{
+ int i,n,fd,arrow=-1;
+ unsigned char *buf;
+
+ nullpo_retr(0, sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd, MAX_INVENTORY * 10 + 4);
+ buf = WFIFOP(fd,0);
+#if PACKETVER < 5
+ WBUFW(buf,0)=0xa3;
+ for(i=0,n=0;i<MAX_INVENTORY;i++){
+ if (sd->status.inventory[i].nameid <=0 || sd->inventory_data[i] == NULL || itemdb_isequip2(sd->inventory_data[i]))
+ continue;
+ WBUFW(buf,n*10+4)=i+2;
+ if (sd->inventory_data[i]->view_id > 0)
+ WBUFW(buf,n*10+6)=sd->inventory_data[i]->view_id;
+ else
+ WBUFW(buf,n*10+6)=sd->status.inventory[i].nameid;
+ WBUFB(buf,n*10+8)=itemtype(sd->inventory_data[i]->type);
+ WBUFB(buf,n*10+9)=sd->status.inventory[i].identify;
+ WBUFW(buf,n*10+10)=sd->status.inventory[i].amount;
+ if (sd->inventory_data[i]->equip == 0x8000) {
+ WBUFW(buf,n*10+12)=0x8000;
+ if (sd->status.inventory[i].equip)
+ arrow=i; // ついでに矢装備チェック
+ } else
+ WBUFW(buf,n*10+12)=0;
+ n++;
+ }
+ if (n) {
+ WBUFW(buf,2)=4+n*10;
+ WFIFOSET(fd,WFIFOW(fd,2));
+ }
+#else
+ WBUFW(buf,0)=0x1ee;
+ for(i=0,n=0;i<MAX_INVENTORY;i++){
+ if(sd->status.inventory[i].nameid <=0 || sd->inventory_data[i] == NULL || itemdb_isequip2(sd->inventory_data[i]))
+ continue;
+ WBUFW(buf,n*18+4)=i+2;
+ if(sd->inventory_data[i]->view_id > 0)
+ WBUFW(buf,n*18+6)=sd->inventory_data[i]->view_id;
+ else
+ WBUFW(buf,n*18+6)=sd->status.inventory[i].nameid;
+ WBUFB(buf,n*18+8)=itemtype(sd->inventory_data[i]->type);
+ WBUFB(buf,n*18+9)=sd->status.inventory[i].identify;
+ WBUFW(buf,n*18+10)=sd->status.inventory[i].amount;
+ if (sd->inventory_data[i]->equip == 0x8000) {
+ WBUFW(buf,n*18+12)=0x8000;
+ if(sd->status.inventory[i].equip)
+ arrow=i; // ついでに矢装備チェック
+ } else
+ WBUFW(buf,n*18+12)=0;
+ clif_addcards(WBUFP(buf, n*18+14), &sd->status.inventory[i]);
+ n++;
+ }
+ if (n) {
+ WBUFW(buf,2)=4+n*18;
+ WFIFOSET(fd,WFIFOW(fd,2));
+ }
+#endif
+ if(arrow >= 0)
+ clif_arrowequip(sd,arrow);
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int clif_equiplist(struct map_session_data *sd)
+{
+ int i,n,fd;
+
+ nullpo_retr(0, sd);
+
+ fd=sd->fd;
+ if (!session_isActive(fd))
+ return 0;
+ WFIFOHEAD(fd, 4 + MAX_INVENTORY * 20);
+ WFIFOW(fd,0)=0xa4;
+ for(i=0,n=0;i<MAX_INVENTORY;i++){
+ if(sd->status.inventory[i].nameid<=0 || sd->inventory_data[i] == NULL || !itemdb_isequip2(sd->inventory_data[i]))
+ continue;
+ WFIFOW(fd,n*20+4)=i+2;
+ if(sd->inventory_data[i]->view_id > 0)
+ WFIFOW(fd,n*20+6)=sd->inventory_data[i]->view_id;
+ else
+ WFIFOW(fd,n*20+6)=sd->status.inventory[i].nameid;
+ WFIFOB(fd,n*20+8)=itemtype(sd->inventory_data[i]->type);
+ WFIFOB(fd,n*20+9)=sd->status.inventory[i].identify;
+ WFIFOW(fd,n*20+10)=pc_equippoint(sd,i);
+ WFIFOW(fd,n*20+12)=sd->status.inventory[i].equip;
+ WFIFOB(fd,n*20+14)=sd->status.inventory[i].attribute;
+ WFIFOB(fd,n*20+15)=sd->status.inventory[i].refine;
+ clif_addcards(WFIFOP(fd, n*20+16), &sd->status.inventory[i]);
+ n++;
+ }
+ if(n){
+ WFIFOW(fd,2)=4+n*20;
+ WFIFOSET(fd,WFIFOW(fd,2));
+ }
+ return 0;
+}
+
+/*==========================================
+ * カプラさんに預けてある消耗品&収集品リスト
+ *------------------------------------------
+ */
+int clif_storageitemlist(struct map_session_data *sd,struct storage *stor)
+{
+ struct item_data *id;
+ int i,n,fd;
+ unsigned char *buf;
+
+ nullpo_retr(0, sd);
+ nullpo_retr(0, stor);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,MAX_STORAGE * 18 + 4);
+ buf = WFIFOP(fd,0);
+#if PACKETVER < 5
+ WBUFW(buf,0)=0xa5;
+ for(i=0,n=0;i<MAX_STORAGE;i++){
+ if(stor->storage_[i].nameid<=0)
+ continue;
+ nullpo_retr(0, id = itemdb_search(stor->storage_[i].nameid));
+ if(itemdb_isequip2(id))
+ continue;
+
+ WBUFW(buf,n*10+4)=i+1;
+ if(id->view_id > 0)
+ WBUFW(buf,n*10+6)=id->view_id;
+ else
+ WBUFW(buf,n*10+6)=stor->storage_[i].nameid;
+ WBUFB(buf,n*10+8)=itemtype(id->type);
+ WBUFB(buf,n*10+9)=stor->storage_[i].identify;
+ WBUFW(buf,n*10+10)=stor->storage_[i].amount;
+ WBUFW(buf,n*10+12)=0;
+ n++;
+ }
+ if(n){
+ WBUFW(buf,2)=4+n*10;
+ WFIFOSET(fd,WFIFOW(fd,2));
+ }
+#else
+ WBUFW(buf,0)=0x1f0;
+ for(i=0,n=0;i<MAX_STORAGE;i++){
+ if(stor->storage_[i].nameid<=0)
+ continue;
+ nullpo_retr(0, id = itemdb_search(stor->storage_[i].nameid));
+ if(itemdb_isequip2(id))
+ continue;
+
+ WBUFW(buf,n*18+4)=i+1;
+ if(id->view_id > 0)
+ WBUFW(buf,n*18+6)=id->view_id;
+ else
+ WBUFW(buf,n*18+6)=stor->storage_[i].nameid;
+ WBUFB(buf,n*18+8)=itemtype(id->type);
+ WBUFB(buf,n*18+9)=stor->storage_[i].identify;
+ WBUFW(buf,n*18+10)=stor->storage_[i].amount;
+ WBUFW(buf,n*18+12)=0;
+ clif_addcards(WBUFP(buf,n*18+14), &stor->storage_[i]);
+ n++;
+ }
+ if(n){
+ WBUFW(buf,2)=4+n*18;
+ WFIFOSET(fd,WFIFOW(fd,2));
+ }
+#endif
+ return 0;
+}
+
+/*==========================================
+ * カプラさんに預けてある装備リスト
+ *------------------------------------------
+ */
+int clif_storageequiplist(struct map_session_data *sd,struct storage *stor)
+{
+ struct item_data *id;
+ int i,n,fd;
+ unsigned char *buf;
+
+ nullpo_retr(0, sd);
+ nullpo_retr(0, stor);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,MAX_STORAGE * 20 + 4);
+ buf = WFIFOP(fd,0);
+ WBUFW(buf,0)=0xa6;
+ for(i=0,n=0;i<MAX_STORAGE;i++){
+ if(stor->storage_[i].nameid<=0)
+ continue;
+ nullpo_retr(0, id = itemdb_search(stor->storage_[i].nameid));
+ if(!itemdb_isequip2(id))
+ continue;
+ WBUFW(buf,n*20+4)=i+1;
+ if(id->view_id > 0)
+ WBUFW(buf,n*20+6)=id->view_id;
+ else
+ WBUFW(buf,n*20+6)=stor->storage_[i].nameid;
+ WBUFB(buf,n*20+8)=itemtype(id->type);
+ WBUFB(buf,n*20+9)=stor->storage_[i].identify;
+ WBUFW(buf,n*20+10)=id->equip;
+ WBUFW(buf,n*20+12)=stor->storage_[i].equip;
+ WBUFB(buf,n*20+14)=stor->storage_[i].attribute;
+ WBUFB(buf,n*20+15)=stor->storage_[i].refine;
+ clif_addcards(WBUFP(buf, n*20+16), &stor->storage_[i]);
+ n++;
+ }
+ if(n){
+ WBUFW(buf,2)=4+n*20;
+ WFIFOSET(fd,WFIFOW(fd,2));
+ }
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int clif_guildstorageitemlist(struct map_session_data *sd,struct guild_storage *stor)
+{
+ struct item_data *id;
+ int i,n,fd;
+ unsigned char *buf;
+
+ nullpo_retr(0, sd);
+ nullpo_retr(0, stor);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd, MAX_GUILD_STORAGE * 18 + 4);
+ buf=WFIFOP(fd,0);
+
+#if PACKETVER < 5
+ WBUFW(buf,0)=0xa5;
+ for(i=0,n=0;i<MAX_GUILD_STORAGE;i++){
+ if(stor->storage_[i].nameid<=0)
+ continue;
+ nullpo_retr(0, id = itemdb_search(stor->storage_[i].nameid));
+ if(itemdb_isequip2(id))
+ continue;
+
+ WBUFW(buf,n*10+4)=i+1;
+ if(id->view_id > 0)
+ WBUFW(buf,n*10+6)=id->view_id;
+ else
+ WBUFW(buf,n*10+6)=stor->storage_[i].nameid;
+ WBUFB(buf,n*10+8)=itemtype(id->type);
+ WBUFB(buf,n*10+9)=stor->storage_[i].identify;
+ WBUFW(buf,n*10+10)=stor->storage_[i].amount;
+ WBUFW(buf,n*10+12)=0;
+ n++;
+ }
+ if(n){
+ WBUFW(buf,2)=4+n*10;
+ WFIFOSET(fd,WFIFOW(fd,2));
+ }
+#else
+ WBUFW(buf,0)=0x1f0;
+ for(i=0,n=0;i<MAX_GUILD_STORAGE;i++){
+ if(stor->storage_[i].nameid<=0)
+ continue;
+ nullpo_retr(0, id = itemdb_search(stor->storage_[i].nameid));
+ if(itemdb_isequip2(id))
+ continue;
+
+ WBUFW(buf,n*18+4)=i+1;
+ if(id->view_id > 0)
+ WBUFW(buf,n*18+6)=id->view_id;
+ else
+ WBUFW(buf,n*18+6)=stor->storage_[i].nameid;
+ WBUFB(buf,n*18+8)=itemtype(id->type);
+ WBUFB(buf,n*18+9)=stor->storage_[i].identify;
+ WBUFW(buf,n*18+10)=stor->storage_[i].amount;
+ WBUFW(buf,n*18+12)=0;
+ clif_addcards(WBUFP(buf,n*18+14), &stor->storage_[i]);
+ n++;
+ }
+ if(n){
+ WBUFW(buf,2)=4+n*18;
+ WFIFOSET(fd,WFIFOW(fd,2));
+ }
+#endif
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int clif_guildstorageequiplist(struct map_session_data *sd,struct guild_storage *stor)
+{
+ struct item_data *id;
+ int i,n,fd;
+ unsigned char *buf;
+
+ nullpo_retr(0, sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd, MAX_GUILD_STORAGE * 20 + 4);
+ buf=WFIFOP(fd,0);
+
+ WBUFW(buf,0)=0xa6;
+ for(i=0,n=0;i<MAX_GUILD_STORAGE;i++){
+ if(stor->storage_[i].nameid<=0)
+ continue;
+ nullpo_retr(0, id = itemdb_search(stor->storage_[i].nameid));
+ if(!itemdb_isequip2(id))
+ continue;
+ WBUFW(buf,n*20+4)=i+1;
+ if(id->view_id > 0)
+ WBUFW(buf,n*20+6)=id->view_id;
+ else
+ WBUFW(buf,n*20+6)=stor->storage_[i].nameid;
+ WBUFB(buf,n*20+8)=itemtype(id->type);
+ WBUFB(buf,n*20+9)=stor->storage_[i].identify;
+ WBUFW(buf,n*20+10)=id->equip;
+ WBUFW(buf,n*20+12)=stor->storage_[i].equip;
+ WBUFB(buf,n*20+14)=stor->storage_[i].attribute;
+ WBUFB(buf,n*20+15)=stor->storage_[i].refine;
+ clif_addcards(WBUFP(buf, n*20+16), &stor->storage_[i]);
+ n++;
+ }
+ if(n){
+ WBUFW(buf,2)=4+n*20;
+ WFIFOSET(fd,WFIFOW(fd,2));
+ }
+ return 0;
+}
+
+// Guild XY locators [Valaris]
+int clif_guild_xy(struct map_session_data *sd)
+{
+unsigned char buf[10];
+
+nullpo_retr(0, sd);
+
+WBUFW(buf,0)=0x1eb;
+WBUFL(buf,2)=sd->status.account_id;
+WBUFW(buf,6)=sd->bl.x;
+WBUFW(buf,8)=sd->bl.y;
+clif_send(buf,packet_len_table[0x1eb],&sd->bl,GUILD_SAMEMAP_WOS);
+
+return 0;
+}
+
+// Guild XY locators [Valaris]
+int clif_guild_xy_remove(struct map_session_data *sd)
+{
+unsigned char buf[10];
+
+nullpo_retr(0, sd);
+
+WBUFW(buf,0)=0x1eb;
+WBUFL(buf,2)=sd->status.account_id;
+WBUFW(buf,6)=-1;
+WBUFW(buf,8)=-1;
+clif_send(buf,packet_len_table[0x1eb],&sd->bl,GUILD_SAMEMAP_WOS);
+
+return 0;
+}
+
+/*==========================================
+ * ステータスを送りつける
+ * 表示専用数字はこの中で計算して送る
+ *------------------------------------------
+ */
+int clif_updatestatus(struct map_session_data *sd,int type)
+{
+ int fd,len=8;
+
+ nullpo_retr(0, sd);
+
+ fd=sd->fd;
+
+ if ( !session_isActive(fd) ) // Invalid pointer fix, by sasuke [Kevin]
+ return 0;
+
+ WFIFOHEAD(fd, 14);
+ WFIFOW(fd,0)=0xb0;
+ WFIFOW(fd,2)=type;
+ switch(type){
+ // 00b0
+ case SP_WEIGHT:
+ pc_checkweighticon(sd);
+ WFIFOW(fd,0)=0xb0;
+ WFIFOW(fd,2)=type; //Added this packet back, Temp fix to the slow motion [Lupus]
+ WFIFOL(fd,4)=sd->weight;
+ break;
+ case SP_MAXWEIGHT:
+ WFIFOL(fd,4)=sd->max_weight;
+ break;
+ case SP_SPEED:
+ WFIFOL(fd,4)=sd->speed;
+ break;
+ case SP_BASELEVEL:
+ WFIFOL(fd,4)=sd->status.base_level;
+ break;
+ case SP_JOBLEVEL:
+ WFIFOL(fd,4)=sd->status.job_level;
+ break;
+ case SP_MANNER:
+ WFIFOL(fd,4)=sd->status.manner;
+ clif_changestatus(&sd->bl,SP_MANNER,sd->status.manner);
+ break;
+ case SP_STATUSPOINT:
+ WFIFOL(fd,4)=sd->status.status_point;
+ break;
+ case SP_SKILLPOINT:
+ WFIFOL(fd,4)=sd->status.skill_point;
+ break;
+ case SP_HIT:
+ WFIFOL(fd,4)=sd->hit;
+ break;
+ case SP_FLEE1:
+ WFIFOL(fd,4)=sd->flee;
+ break;
+ case SP_FLEE2:
+ WFIFOL(fd,4)=sd->flee2/10;
+ break;
+ case SP_MAXHP:
+ WFIFOL(fd,4)=sd->status.max_hp;
+ break;
+ case SP_MAXSP:
+ WFIFOL(fd,4)=sd->status.max_sp;
+ break;
+ case SP_HP:
+ WFIFOL(fd,4)=sd->status.hp;
+ if (sd->status.party_id)
+ clif_party_hp(sd);
+ if (battle_config.disp_hpmeter)
+ clif_hpmeter(sd);
+ break;
+ case SP_SP:
+ WFIFOL(fd,4)=sd->status.sp;
+ break;
+ case SP_ASPD:
+ WFIFOL(fd,4)=sd->aspd;
+ break;
+ case SP_ATK1:
+ WFIFOL(fd,4)=sd->base_atk+sd->right_weapon.watk;
+ break;
+ case SP_DEF1:
+ WFIFOL(fd,4)=sd->def;
+ break;
+ case SP_MDEF1:
+ WFIFOL(fd,4)=sd->mdef;
+ break;
+ case SP_ATK2:
+ WFIFOL(fd,4)=sd->right_weapon.watk2;
+ break;
+ case SP_DEF2:
+ WFIFOL(fd,4)=sd->def2;
+ break;
+ case SP_MDEF2:
+ WFIFOL(fd,4)=sd->mdef2;
+ break;
+ case SP_CRITICAL:
+ WFIFOL(fd,4)=sd->critical/10;
+ break;
+ case SP_MATK1:
+ WFIFOL(fd,4)=sd->matk1;
+ break;
+ case SP_MATK2:
+ WFIFOL(fd,4)=sd->matk2;
+ break;
+
+
+ case SP_ZENY:
+ WFIFOW(fd,0)=0xb1;
+ if(sd->status.zeny < 0)
+ sd->status.zeny = 0;
+ WFIFOL(fd,4)=sd->status.zeny;
+ break;
+ case SP_BASEEXP:
+ WFIFOW(fd,0)=0xb1;
+ WFIFOL(fd,4)=sd->status.base_exp;
+ break;
+ case SP_JOBEXP:
+ WFIFOW(fd,0)=0xb1;
+ WFIFOL(fd,4)=sd->status.job_exp;
+ break;
+ case SP_NEXTBASEEXP:
+ WFIFOW(fd,0)=0xb1;
+ WFIFOL(fd,4)=pc_nextbaseexp(sd);
+ break;
+ case SP_NEXTJOBEXP:
+ WFIFOW(fd,0)=0xb1;
+ WFIFOL(fd,4)=pc_nextjobexp(sd);
+ break;
+
+ // 00be 終了
+ case SP_USTR:
+ case SP_UAGI:
+ case SP_UVIT:
+ case SP_UINT:
+ case SP_UDEX:
+ case SP_ULUK:
+ WFIFOW(fd,0)=0xbe;
+ WFIFOB(fd,4)=pc_need_status_point(sd,type-SP_USTR+SP_STR);
+ len=5;
+ break;
+
+ // 013a 終了
+ case SP_ATTACKRANGE:
+ WFIFOW(fd,0)=0x13a;
+ WFIFOW(fd,2)=sd->attackrange;
+ len=4;
+ break;
+
+ // 0141 終了
+ case SP_STR:
+ WFIFOW(fd,0)=0x141;
+ WFIFOL(fd,2)=type;
+ WFIFOL(fd,6)=sd->status.str;
+ WFIFOL(fd,10)=sd->paramb[0] + sd->parame[0];
+ len=14;
+ break;
+ case SP_AGI:
+ WFIFOW(fd,0)=0x141;
+ WFIFOL(fd,2)=type;
+ WFIFOL(fd,6)=sd->status.agi;
+ WFIFOL(fd,10)=sd->paramb[1] + sd->parame[1];
+ len=14;
+ break;
+ case SP_VIT:
+ WFIFOW(fd,0)=0x141;
+ WFIFOL(fd,2)=type;
+ WFIFOL(fd,6)=sd->status.vit;
+ WFIFOL(fd,10)=sd->paramb[2] + sd->parame[2];
+ len=14;
+ break;
+ case SP_INT:
+ WFIFOW(fd,0)=0x141;
+ WFIFOL(fd,2)=type;
+ WFIFOL(fd,6)=sd->status.int_;
+ WFIFOL(fd,10)=sd->paramb[3] + sd->parame[3];
+ len=14;
+ break;
+ case SP_DEX:
+ WFIFOW(fd,0)=0x141;
+ WFIFOL(fd,2)=type;
+ WFIFOL(fd,6)=sd->status.dex;
+ WFIFOL(fd,10)=sd->paramb[4] + sd->parame[4];
+ len=14;
+ break;
+ case SP_LUK:
+ WFIFOW(fd,0)=0x141;
+ WFIFOL(fd,2)=type;
+ WFIFOL(fd,6)=sd->status.luk;
+ WFIFOL(fd,10)=sd->paramb[5] + sd->parame[5];
+ len=14;
+ break;
+
+ case SP_CARTINFO:
+ WFIFOW(fd,0)=0x121;
+ WFIFOW(fd,2)=sd->cart_num;
+ WFIFOW(fd,4)=sd->cart_max_num;
+ WFIFOL(fd,6)=sd->cart_weight;
+ WFIFOL(fd,10)=sd->cart_max_weight;
+ len=14;
+ break;
+
+ default:
+ if(battle_config.error_log)
+ ShowError("clif_updatestatus : unrecognized type %d\n",type);
+ return 1;
+ }
+ WFIFOSET(fd,len);
+
+ return 0;
+}
+int clif_changestatus(struct block_list *bl,int type,int val)
+{
+ unsigned char buf[12];
+ struct map_session_data *sd = NULL;
+
+ nullpo_retr(0, bl);
+
+ if(bl->type == BL_PC)
+ sd = (struct map_session_data *)bl;
+
+//printf("clif_changestatus id:%d type:%d val:%d\n",bl->id,type,val);
+ if(sd){
+ WBUFW(buf,0)=0x1ab;
+ WBUFL(buf,2)=bl->id;
+ WBUFW(buf,6)=type;
+ switch(type){
+ case SP_MANNER:
+ WBUFL(buf,8)=val;
+ break;
+ default:
+ if(battle_config.error_log)
+ ShowError("clif_changestatus : unrecognized type %d.\n",type);
+ return 1;
+ }
+ clif_send(buf,packet_len_table[0x1ab],bl,AREA_WOS);
+ }
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int clif_changelook(struct block_list *bl,int type,int val)
+{
+
+ unsigned char buf[32];
+ struct map_session_data *sd = NULL;
+
+ nullpo_retr(0, bl);
+
+ if(bl->type == BL_PC)
+ sd = (struct map_session_data *)bl;
+
+#if PACKETVER < 4
+ if(sd && (type == LOOK_WEAPON || type == LOOK_SHIELD) && (sd->view_class == JOB_WEDDING || sd->view_class == JOB_XMAS))
+ val =0;
+ WBUFW(buf,0)=0xc3;
+ WBUFL(buf,2)=bl->id;
+ WBUFB(buf,6)=type;
+ WBUFB(buf,7)=val;
+ clif_send(buf,packet_len_table[0xc3],bl,AREA);
+#else
+ if(sd && (type == LOOK_WEAPON || type == LOOK_SHIELD || type == LOOK_SHOES)) {
+ WBUFW(buf,0)=0x1d7;
+ WBUFL(buf,2)=bl->id;
+ if(type == LOOK_SHOES) {
+ WBUFB(buf,6)=9;
+ if(sd->equip_index[2] >= 0 && sd->inventory_data[sd->equip_index[2]]) {
+ if(sd->inventory_data[sd->equip_index[2]]->view_id > 0)
+ WBUFW(buf,7)=sd->inventory_data[sd->equip_index[2]]->view_id;
+ else
+ WBUFW(buf,7)=sd->status.inventory[sd->equip_index[2]].nameid;
+ } else
+ WBUFW(buf,7)=0;
+ WBUFW(buf,9)=0;
+ }
+ else {
+ WBUFB(buf,6)=2;
+ if(sd->equip_index[9] >= 0 && sd->inventory_data[sd->equip_index[9]] && sd->view_class != JOB_WEDDING && sd->view_class != JOB_XMAS) {
+ if(sd->inventory_data[sd->equip_index[9]]->view_id > 0)
+ WBUFW(buf,7)=sd->inventory_data[sd->equip_index[9]]->view_id;
+ else
+ WBUFW(buf,7)=sd->status.inventory[sd->equip_index[9]].nameid;
+ } else
+ WBUFW(buf,7)=0;
+ if(sd->equip_index[8] >= 0 && sd->equip_index[8] != sd->equip_index[9] && sd->inventory_data[sd->equip_index[8]] &&
+ sd->view_class != JOB_WEDDING && sd->view_class != JOB_XMAS) {
+ if(sd->inventory_data[sd->equip_index[8]]->view_id > 0)
+ WBUFW(buf,9)=sd->inventory_data[sd->equip_index[8]]->view_id;
+ else
+ WBUFW(buf,9)=sd->status.inventory[sd->equip_index[8]].nameid;
+ } else
+ WBUFW(buf,9)=0;
+ }
+ clif_send(buf,packet_len_table[0x1d7],bl,AREA);
+ }
+ else if(sd && (type == LOOK_BASE) && (val > 255))
+ {
+ WBUFW(buf,0)=0x1d7;
+ WBUFL(buf,2)=bl->id;
+ WBUFB(buf,6)=type;
+ WBUFW(buf,7)=val;
+ WBUFW(buf,9)=0;
+ clif_send(buf,packet_len_table[0x1d7],bl,AREA);
+ } else {
+ WBUFW(buf,0)=0xc3;
+ WBUFL(buf,2)=bl->id;
+ WBUFB(buf,6)=type;
+ WBUFB(buf,7)=val;
+ clif_send(buf,packet_len_table[0xc3],bl,AREA);
+ }
+#endif
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int clif_initialstatus(struct map_session_data *sd)
+{
+ int fd;
+ unsigned char *buf;
+
+ nullpo_retr(0, sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,packet_len_table[0xbd]);
+ buf=WFIFOP(fd,0);
+
+ WBUFW(buf,0)=0xbd;
+ WBUFW(buf,2)=sd->status.status_point;
+ WBUFB(buf,4)=(sd->status.str > 255)? 255:sd->status.str;
+ WBUFB(buf,5)=pc_need_status_point(sd,SP_STR);
+ WBUFB(buf,6)=(sd->status.agi > 255)? 255:sd->status.agi;
+ WBUFB(buf,7)=pc_need_status_point(sd,SP_AGI);
+ WBUFB(buf,8)=(sd->status.vit > 255)? 255:sd->status.vit;
+ WBUFB(buf,9)=pc_need_status_point(sd,SP_VIT);
+ WBUFB(buf,10)=(sd->status.int_ > 255)? 255:sd->status.int_;
+ WBUFB(buf,11)=pc_need_status_point(sd,SP_INT);
+ WBUFB(buf,12)=(sd->status.dex > 255)? 255:sd->status.dex;
+ WBUFB(buf,13)=pc_need_status_point(sd,SP_DEX);
+ WBUFB(buf,14)=(sd->status.luk > 255)? 255:sd->status.luk;
+ WBUFB(buf,15)=pc_need_status_point(sd,SP_LUK);
+
+ WBUFW(buf,16) = sd->base_atk + sd->right_weapon.watk;
+ WBUFW(buf,18) = sd->right_weapon.watk2; //atk bonus
+ WBUFW(buf,20) = sd->matk1;
+ WBUFW(buf,22) = sd->matk2;
+ WBUFW(buf,24) = sd->def; // def
+ WBUFW(buf,26) = sd->def2;
+ WBUFW(buf,28) = sd->mdef; // mdef
+ WBUFW(buf,30) = sd->mdef2;
+ WBUFW(buf,32) = sd->hit;
+ WBUFW(buf,34) = sd->flee;
+ WBUFW(buf,36) = sd->flee2/10;
+ WBUFW(buf,38) = sd->critical/10;
+ WBUFW(buf,40) = sd->status.karma;
+ WBUFW(buf,42) = sd->status.manner;
+
+ WFIFOSET(fd,packet_len_table[0xbd]);
+
+ clif_updatestatus(sd,SP_STR);
+ clif_updatestatus(sd,SP_AGI);
+ clif_updatestatus(sd,SP_VIT);
+ clif_updatestatus(sd,SP_INT);
+ clif_updatestatus(sd,SP_DEX);
+ clif_updatestatus(sd,SP_LUK);
+
+ clif_updatestatus(sd,SP_ATTACKRANGE);
+ clif_updatestatus(sd,SP_ASPD);
+
+ return 0;
+}
+
+/*==========================================
+ *矢装備
+ *------------------------------------------
+ */
+int clif_arrowequip(struct map_session_data *sd,int val)
+{
+ int fd;
+
+ nullpo_retr(0, sd);
+
+ if(sd->attacktarget && sd->attacktarget > 0) // [Valaris]
+ sd->attacktarget = 0;
+
+ fd=sd->fd;
+ WFIFOHEAD(fd, packet_len_table[0x013c]);
+ WFIFOW(fd,0)=0x013c;
+ WFIFOW(fd,2)=val+2;//矢のアイテムID
+
+ WFIFOSET(fd,packet_len_table[0x013c]);
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int clif_arrow_fail(struct map_session_data *sd,int type)
+{
+ int fd;
+
+ nullpo_retr(0, sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd, packet_len_table[0x013b]);
+ WFIFOW(fd,0)=0x013b;
+ WFIFOW(fd,2)=type;
+
+ WFIFOSET(fd,packet_len_table[0x013b]);
+
+ return 0;
+}
+
+/*==========================================
+ * 作成可能 矢リスト送信
+ *------------------------------------------
+ */
+int clif_arrow_create_list(struct map_session_data *sd)
+{
+ int i, c, j;
+ int fd;
+
+ nullpo_retr(0, sd);
+
+ fd = sd->fd;
+ WFIFOHEAD(fd, MAX_SKILL_ARROW_DB*2+4);
+ WFIFOW(fd,0) = 0x1ad;
+
+ for (i = 0, c = 0; i < MAX_SKILL_ARROW_DB; i++) {
+ if (skill_arrow_db[i].nameid > 0 &&
+ (j = pc_search_inventory(sd, skill_arrow_db[i].nameid)) >= 0 &&
+ !sd->status.inventory[j].equip)
+ {
+ if ((j = itemdb_viewid(skill_arrow_db[i].nameid)) > 0)
+ WFIFOW(fd,c*2+4) = j;
+ else
+ WFIFOW(fd,c*2+4) = skill_arrow_db[i].nameid;
+ c++;
+ }
+ }
+ WFIFOW(fd,2) = c*2+4;
+ WFIFOSET(fd, WFIFOW(fd,2));
+ if (c > 0) sd->state.produce_flag = 1;
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int clif_statusupack(struct map_session_data *sd,int type,int ok,int val)
+{
+ int fd;
+
+ nullpo_retr(0, sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,packet_len_table[0xbc]);
+ WFIFOW(fd,0)=0xbc;
+ WFIFOW(fd,2)=type;
+ WFIFOB(fd,4)=ok;
+ WFIFOB(fd,5)=val;
+ WFIFOSET(fd,packet_len_table[0xbc]);
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int clif_equipitemack(struct map_session_data *sd,int n,int pos,int ok)
+{
+ int fd;
+
+ nullpo_retr(0, sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,packet_len_table[0xaa]);
+ WFIFOW(fd,0)=0xaa;
+ WFIFOW(fd,2)=n+2;
+ WFIFOW(fd,4)=pos;
+ WFIFOB(fd,6)=ok;
+ WFIFOSET(fd,packet_len_table[0xaa]);
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int clif_unequipitemack(struct map_session_data *sd,int n,int pos,int ok)
+{
+ int fd;
+
+ nullpo_retr(0, sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,packet_len_table[0xac]);
+ WFIFOW(fd,0)=0xac;
+ WFIFOW(fd,2)=n+2;
+ WFIFOW(fd,4)=pos;
+ WFIFOB(fd,6)=ok;
+ WFIFOSET(fd,packet_len_table[0xac]);
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int clif_misceffect(struct block_list* bl,int type)
+{
+ unsigned char buf[32];
+
+ nullpo_retr(0, bl);
+
+ WBUFW(buf,0) = 0x19b;
+ WBUFL(buf,2) = bl->id;
+ WBUFL(buf,6) = type;
+
+ clif_send(buf,packet_len_table[0x19b],bl,AREA);
+
+ return 0;
+}
+int clif_misceffect2(struct block_list *bl, int type) {
+ unsigned char buf[24];
+
+ nullpo_retr(0, bl);
+
+ memset(buf, 0, packet_len_table[0x1f3]);
+
+ WBUFW(buf,0) = 0x1f3;
+ WBUFL(buf,2) = bl->id;
+ WBUFL(buf,6) = type;
+
+ clif_send(buf, packet_len_table[0x1f3], bl, AREA);
+
+ return 0;
+
+}
+/*==========================================
+ * 表示オプション変更
+ *------------------------------------------
+ */
+int clif_changeoption(struct block_list* bl)
+{
+ unsigned char buf[32];
+ short option;
+
+ nullpo_retr(0, bl);
+
+ option = *status_get_option(bl);
+
+ WBUFW(buf,0) = 0x119;
+ if(bl->type==BL_PC && ((struct map_session_data *)bl)->disguise) {
+ WBUFL(buf,2) = bl->id;
+ WBUFW(buf,6) = 0;
+ WBUFW(buf,8) = 0;
+ WBUFW(buf,10) = OPTION_INVISIBLE;
+ WBUFB(buf,12) = 0;
+ clif_send(buf,packet_len_table[0x119],bl,AREA);
+ WBUFL(buf,2) = -bl->id;
+ WBUFW(buf,6) = 0;
+ WBUFW(buf,8) = 0;
+ WBUFW(buf,10) = option;
+ WBUFB(buf,12) = 0;
+ clif_send(buf,packet_len_table[0x119],bl,AREA);
+ } else {
+ WBUFL(buf,2) = bl->id;
+ WBUFW(buf,6) = *status_get_opt1(bl);
+ WBUFW(buf,8) = *status_get_opt2(bl);
+ WBUFW(buf,10) = option;
+ WBUFB(buf,12) = 0; // ??
+ clif_send(buf,packet_len_table[0x119],bl,AREA);
+ }
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int clif_useitemack(struct map_session_data *sd,int index,int amount,int ok)
+{
+ nullpo_retr(0, sd);
+
+ if(!ok) {
+ int fd=sd->fd;
+ WFIFOHEAD(fd,packet_len_table[0xa8]);
+ WFIFOW(fd,0)=0xa8;
+ WFIFOW(fd,2)=index+2;
+ WFIFOW(fd,4)=amount;
+ WFIFOB(fd,6)=ok;
+ WFIFOSET(fd,packet_len_table[0xa8]);
+ }
+ else {
+#if PACKETVER < 3
+ int fd=sd->fd;
+ WFIFOHEAD(fd,packet_len_table[0xa8]);
+ WFIFOW(fd,0)=0xa8;
+ WFIFOW(fd,2)=index+2;
+ WFIFOW(fd,4)=amount;
+ WFIFOB(fd,6)=ok;
+ WFIFOSET(fd,packet_len_table[0xa8]);
+#else
+ unsigned char buf[32];
+
+ WBUFW(buf,0)=0x1c8;
+ WBUFW(buf,2)=index+2;
+ if(sd->inventory_data[index] && sd->inventory_data[index]->view_id > 0)
+ WBUFW(buf,4)=sd->inventory_data[index]->view_id;
+ else
+ WBUFW(buf,4)=sd->status.inventory[index].nameid;
+ WBUFL(buf,6)=sd->bl.id;
+ WBUFW(buf,10)=amount;
+ WBUFB(buf,12)=ok;
+ clif_send(buf,packet_len_table[0x1c8],&sd->bl,AREA);
+#endif
+ }
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int clif_createchat(struct map_session_data *sd,int fail)
+{
+ int fd;
+
+ nullpo_retr(0, sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,packet_len_table[0xd6]);
+ WFIFOW(fd,0)=0xd6;
+ WFIFOB(fd,2)=fail;
+ WFIFOSET(fd,packet_len_table[0xd6]);
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int clif_dispchat(struct chat_data *cd,int fd)
+{
+ unsigned char buf[128]; // 最大title(60バイト)+17
+
+ if(cd==NULL || *cd->owner==NULL)
+ return 1;
+
+ WBUFW(buf,0)=0xd7;
+ WBUFW(buf,2)=strlen((const char*)cd->title)+17;
+ WBUFL(buf,4)=(*cd->owner)->id;
+ WBUFL(buf,8)=cd->bl.id;
+ WBUFW(buf,12)=cd->limit;
+ WBUFW(buf,14)=cd->users;
+ WBUFB(buf,16)=cd->pub;
+ strcpy((char*)WBUFP(buf,17),(const char*)cd->title);
+ if(fd){
+ WFIFOHEAD(fd, WBUFW(buf,2));
+ memcpy(WFIFOP(fd,0),buf,WBUFW(buf,2));
+ WFIFOSET(fd,WBUFW(buf,2));
+ } else {
+ clif_send(buf,WBUFW(buf,2),*cd->owner,AREA_WOSC);
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * chatの状態変更成功
+ * 外部の人用と命令コード(d7->df)が違うだけ
+ *------------------------------------------
+ */
+int clif_changechatstatus(struct chat_data *cd)
+{
+ unsigned char buf[128]; // 最大title(60バイト)+17
+
+ if(cd==NULL || cd->usersd[0]==NULL)
+ return 1;
+
+ WBUFW(buf,0)=0xdf;
+ WBUFW(buf,2)=strlen((char*)cd->title)+17;
+ WBUFL(buf,4)=cd->usersd[0]->bl.id;
+ WBUFL(buf,8)=cd->bl.id;
+ WBUFW(buf,12)=cd->limit;
+ WBUFW(buf,14)=cd->users;
+ WBUFB(buf,16)=cd->pub;
+ strcpy((char*)WBUFP(buf,17),(const char*)cd->title);
+ clif_send(buf,WBUFW(buf,2),&cd->usersd[0]->bl,CHAT);
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int clif_clearchat(struct chat_data *cd,int fd)
+{
+ unsigned char buf[32];
+
+ nullpo_retr(0, cd);
+
+ WBUFW(buf,0)=0xd8;
+ WBUFL(buf,2)=cd->bl.id;
+ if(fd){
+ WFIFOHEAD(fd,packet_len_table[0xd8]);
+ memcpy(WFIFOP(fd,0),buf,packet_len_table[0xd8]);
+ WFIFOSET(fd,packet_len_table[0xd8]);
+ } else {
+ clif_send(buf,packet_len_table[0xd8],*cd->owner,AREA_WOSC);
+ }
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int clif_joinchatfail(struct map_session_data *sd,int fail)
+{
+ int fd;
+
+ nullpo_retr(0, sd);
+
+ fd=sd->fd;
+
+ WFIFOHEAD(fd,packet_len_table[0xda]);
+ WFIFOW(fd,0)=0xda;
+ WFIFOB(fd,2)=fail;
+ WFIFOSET(fd,packet_len_table[0xda]);
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int clif_joinchatok(struct map_session_data *sd,struct chat_data* cd)
+{
+ int fd;
+ int i;
+
+ nullpo_retr(0, sd);
+ nullpo_retr(0, cd);
+
+ fd = sd->fd;
+ if (!session_isActive(fd))
+ return 0;
+ WFIFOHEAD(fd, 8 + (28*cd->users));
+ WFIFOW(fd, 0) = 0xdb;
+ WFIFOW(fd, 2) = 8 + (28*cd->users);
+ WFIFOL(fd, 4) = cd->bl.id;
+ for (i = 0; i < cd->users; i++) {
+ WFIFOL(fd, 8+i*28) = (i!=0) || ((*cd->owner)->type == BL_NPC);
+ memcpy(WFIFOP(fd, 8+i*28+4), cd->usersd[i]->status.name, NAME_LENGTH);
+ }
+ WFIFOSET(fd, WFIFOW(fd, 2));
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int clif_addchat(struct chat_data* cd,struct map_session_data *sd)
+{
+ unsigned char buf[32];
+
+ nullpo_retr(0, sd);
+ nullpo_retr(0, cd);
+
+ WBUFW(buf, 0) = 0x0dc;
+ WBUFW(buf, 2) = cd->users;
+ memcpy(WBUFP(buf, 4),sd->status.name,NAME_LENGTH);
+ clif_send(buf,packet_len_table[0xdc],&sd->bl,CHAT_WOS);
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int clif_changechatowner(struct chat_data* cd,struct map_session_data *sd)
+{
+ unsigned char buf[64];
+
+ nullpo_retr(0, sd);
+ nullpo_retr(0, cd);
+
+ WBUFW(buf, 0) = 0xe1;
+ WBUFL(buf, 2) = 1;
+ memcpy(WBUFP(buf,6),cd->usersd[0]->status.name,NAME_LENGTH);
+ WBUFW(buf,30) = 0xe1;
+ WBUFL(buf,32) = 0;
+ memcpy(WBUFP(buf,36),sd->status.name,NAME_LENGTH);
+
+ clif_send(buf,packet_len_table[0xe1]*2,&sd->bl,CHAT);
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int clif_leavechat(struct chat_data* cd,struct map_session_data *sd)
+{
+ unsigned char buf[32];
+
+ nullpo_retr(0, sd);
+ nullpo_retr(0, cd);
+
+ WBUFW(buf, 0) = 0xdd;
+ WBUFW(buf, 2) = cd->users-1;
+ memcpy(WBUFP(buf,4),sd->status.name,NAME_LENGTH);
+ WBUFB(buf,28) = 0;
+
+ clif_send(buf,packet_len_table[0xdd],&sd->bl,CHAT);
+
+ return 0;
+}
+
+/*==========================================
+ * 取り引き要請受け
+ *------------------------------------------
+ */
+int clif_traderequest(struct map_session_data *sd,char *name)
+{
+ int fd;
+
+ nullpo_retr(0, sd);
+
+ fd=sd->fd;
+
+ WFIFOHEAD(fd,packet_len_table[0xe5]);
+ WFIFOW(fd,0)=0xe5;
+
+ strcpy((char*)WFIFOP(fd,2),name);
+
+ WFIFOSET(fd,packet_len_table[0xe5]);
+
+ return 0;
+}
+
+/*==========================================
+ * 取り引き要求応答
+ *------------------------------------------
+ */
+int clif_tradestart(struct map_session_data *sd,int type)
+{
+ int fd;
+
+ nullpo_retr(0, sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,packet_len_table[0xe7]);
+ WFIFOW(fd,0)=0xe7;
+ WFIFOB(fd,2)=type;
+ WFIFOSET(fd,packet_len_table[0xe7]);
+
+ return 0;
+}
+
+/*==========================================
+ * 相手方からのアイテム追加
+ *------------------------------------------
+ */
+int clif_tradeadditem(struct map_session_data *sd,struct map_session_data *tsd,int index,int amount)
+{
+ int fd;
+
+ nullpo_retr(0, sd);
+ nullpo_retr(0, tsd);
+
+ fd=tsd->fd;
+ WFIFOHEAD(fd,packet_len_table[0xe9]);
+ WFIFOW(fd,0)=0xe9;
+ WFIFOL(fd,2)=amount;
+ if(index==0){
+ WFIFOW(fd,6) = 0; // type id
+ WFIFOB(fd,8) = 0; //identify flag
+ WFIFOB(fd,9) = 0; // attribute
+ WFIFOB(fd,10)= 0; //refine
+ WFIFOW(fd,11)= 0; //card (4w)
+ WFIFOW(fd,13)= 0; //card (4w)
+ WFIFOW(fd,15)= 0; //card (4w)
+ WFIFOW(fd,17)= 0; //card (4w)
+ }
+ else{
+ index-=2; //index fix
+ if(sd->inventory_data[index] && sd->inventory_data[index]->view_id > 0)
+ WFIFOW(fd,6) = sd->inventory_data[index]->view_id;
+ else
+ WFIFOW(fd,6) = sd->status.inventory[index].nameid; // type id
+ WFIFOB(fd,8) = sd->status.inventory[index].identify; //identify flag
+ WFIFOB(fd,9) = sd->status.inventory[index].attribute; // attribute
+ WFIFOB(fd,10)= sd->status.inventory[index].refine; //refine
+ clif_addcards(WFIFOP(fd, 11), &sd->status.inventory[index]);
+ }
+ WFIFOSET(fd,packet_len_table[0xe9]);
+
+ return 0;
+}
+
+/*==========================================
+ * アイテム追加成功/失敗
+ *------------------------------------------
+ */
+int clif_tradeitemok(struct map_session_data *sd,int index,int fail)
+{
+ int fd;
+
+ nullpo_retr(0, sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,packet_len_table[0xea]);
+ WFIFOW(fd,0)=0xea;
+ WFIFOW(fd,2)=index;
+ WFIFOB(fd,4)=fail;
+ WFIFOSET(fd,packet_len_table[0xea]);
+
+ return 0;
+}
+
+/*==========================================
+ * 取り引きok押し
+ *------------------------------------------
+ */
+int clif_tradedeal_lock(struct map_session_data *sd,int fail)
+{
+ int fd;
+
+ nullpo_retr(0, sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,packet_len_table[0xec]);
+ WFIFOW(fd,0)=0xec;
+ WFIFOB(fd,2)=fail; // 0=you 1=the other person
+ WFIFOSET(fd,packet_len_table[0xec]);
+
+ return 0;
+}
+
+/*==========================================
+ * 取り引きがキャンセルされました
+ *------------------------------------------
+ */
+int clif_tradecancelled(struct map_session_data *sd)
+{
+ int fd;
+
+ nullpo_retr(0, sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,packet_len_table[0xee]);
+ WFIFOW(fd,0)=0xee;
+ WFIFOSET(fd,packet_len_table[0xee]);
+
+ return 0;
+}
+
+/*==========================================
+ * 取り引き完了
+ *------------------------------------------
+ */
+int clif_tradecompleted(struct map_session_data *sd,int fail)
+{
+ int fd;
+
+ nullpo_retr(0, sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,packet_len_table[0xf0]);
+ WFIFOW(fd,0)=0xf0;
+ WFIFOB(fd,2)=fail;
+ WFIFOSET(fd,packet_len_table[0xf0]);
+
+ return 0;
+}
+
+/*==========================================
+ * カプラ倉庫のアイテム数を更新
+ *------------------------------------------
+ */
+int clif_updatestorageamount(struct map_session_data *sd,struct storage *stor)
+{
+ int fd;
+
+ nullpo_retr(0, sd);
+ nullpo_retr(0, stor);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,packet_len_table[0xf2]);
+ WFIFOW(fd,0) = 0xf2; // update storage amount
+ WFIFOW(fd,2) = stor->storage_amount; //items
+ WFIFOW(fd,4) = MAX_STORAGE; //items max
+ WFIFOSET(fd,packet_len_table[0xf2]);
+
+ return 0;
+}
+
+/*==========================================
+ * カプラ倉庫にアイテムを追加する
+ *------------------------------------------
+ */
+int clif_storageitemadded(struct map_session_data *sd,struct storage *stor,int index,int amount)
+{
+ int view,fd;
+
+ nullpo_retr(0, sd);
+ nullpo_retr(0, stor);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,packet_len_table[0xf4]);
+ WFIFOW(fd,0) =0xf4; // Storage item added
+ WFIFOW(fd,2) =index+1; // index
+ WFIFOL(fd,4) =amount; // amount
+ if((view = itemdb_viewid(stor->storage_[index].nameid)) > 0)
+ WFIFOW(fd,8) =view;
+ else
+ WFIFOW(fd,8) =stor->storage_[index].nameid; // id
+ WFIFOB(fd,10)=stor->storage_[index].identify; //identify flag
+ WFIFOB(fd,11)=stor->storage_[index].attribute; // attribute
+ WFIFOB(fd,12)=stor->storage_[index].refine; //refine
+ clif_addcards(WFIFOP(fd,13), &stor->storage_[index]);
+ WFIFOSET(fd,packet_len_table[0xf4]);
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int clif_updateguildstorageamount(struct map_session_data *sd,struct guild_storage *stor)
+{
+ int fd;
+
+ nullpo_retr(0, sd);
+ nullpo_retr(0, stor);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,packet_len_table[0xf2]);
+ WFIFOW(fd,0) = 0xf2; // update storage amount
+ WFIFOW(fd,2) = stor->storage_amount; //items
+ WFIFOW(fd,4) = MAX_GUILD_STORAGE; //items max
+ WFIFOSET(fd,packet_len_table[0xf2]);
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int clif_guildstorageitemadded(struct map_session_data *sd,struct guild_storage *stor,int index,int amount)
+{
+ int view,fd;
+
+ nullpo_retr(0, sd);
+ nullpo_retr(0, stor);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,packet_len_table[0xf4]);
+ WFIFOW(fd,0) =0xf4; // Storage item added
+ WFIFOW(fd,2) =index+1; // index
+ WFIFOL(fd,4) =amount; // amount
+ if((view = itemdb_viewid(stor->storage_[index].nameid)) > 0)
+ WFIFOW(fd,8) =view;
+ else
+ WFIFOW(fd,8) =stor->storage_[index].nameid; // id
+ WFIFOB(fd,10)=stor->storage_[index].identify; //identify flag
+ WFIFOB(fd,11)=stor->storage_[index].attribute; // attribute
+ WFIFOB(fd,12)=stor->storage_[index].refine; //refine
+ clif_addcards(WFIFOP(fd,13), &stor->storage_[index]);
+ WFIFOSET(fd,packet_len_table[0xf4]);
+
+ return 0;
+}
+
+/*==========================================
+ * カプラ倉庫からアイテムを取り去る
+ *------------------------------------------
+ */
+int clif_storageitemremoved(struct map_session_data *sd,int index,int amount)
+{
+ int fd;
+
+ nullpo_retr(0, sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,packet_len_table[0xf6]);
+ WFIFOW(fd,0)=0xf6; // Storage item removed
+ WFIFOW(fd,2)=index+1;
+ WFIFOL(fd,4)=amount;
+ WFIFOSET(fd,packet_len_table[0xf6]);
+
+ return 0;
+}
+
+/*==========================================
+ * カプラ倉庫を閉じる
+ *------------------------------------------
+ */
+int clif_storageclose(struct map_session_data *sd)
+{
+ int fd;
+
+ nullpo_retr(0, sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,packet_len_table[0xf8]);
+ WFIFOW(fd,0)=0xf8; // Storage Closed
+ WFIFOSET(fd,packet_len_table[0xf8]);
+
+ return 0;
+}
+
+//
+// callback系 ?
+//
+/*==========================================
+ * PC表示
+ *------------------------------------------
+ */
+void clif_getareachar_pc(struct map_session_data* sd,struct map_session_data* dstsd)
+{
+ int len;
+
+ nullpo_retv(sd);
+ nullpo_retv(dstsd);
+
+ if(dstsd->walktimer != -1){
+#if PACKETVER < 4
+ WFIFOHEAD(sd->fd, packet_len_table[0x7b]);
+#else
+ WFIFOHEAD(sd->fd, packet_len_table[0x1da]);
+#endif
+ len = clif_set007b(dstsd,WFIFOP(sd->fd,0));
+ WFIFOSET(sd->fd,len);
+ if(dstsd->disguise) {
+ WFIFOHEAD(sd->fd,packet_len_table[0x7b]);
+ len = clif_dis007b(dstsd,WFIFOP(sd->fd,0));
+ WFIFOSET(sd->fd,len);
+ }
+ } else {
+#if PACKETVER < 4
+ WFIFOHEAD(sd->fd,packet_len_table[0x78]);
+#else
+ WFIFOHEAD(sd->fd,packet_len_table[0x1d8]);
+#endif
+ len = clif_set0078(dstsd,WFIFOP(sd->fd,0));
+ WFIFOSET(sd->fd,len);
+ if(dstsd->disguise) {
+ WFIFOHEAD(sd->fd,packet_len_table[0x7b]);
+ len = clif_dis0078(dstsd,WFIFOP(sd->fd,0));
+ WFIFOSET(sd->fd,len);
+ }
+ }
+
+ if(dstsd->chatID){
+ struct chat_data *cd;
+ cd=(struct chat_data*)map_id2bl(dstsd->chatID);
+ if(cd->usersd[0]==dstsd)
+ clif_dispchat(cd,sd->fd);
+ }
+ if(dstsd->vender_id){
+ clif_showvendingboard(&dstsd->bl,dstsd->message,sd->fd);
+ }
+ if(dstsd->spiritball > 0) {
+ WFIFOHEAD(sd->fd, packet_len_table[0x1e1]);
+ clif_set01e1(dstsd,WFIFOP(sd->fd,0));
+ WFIFOSET(sd->fd,packet_len_table[0x1e1]);
+ }
+
+ if(battle_config.save_clothcolor && dstsd->status.clothes_color > 0 && ((dstsd->view_class != JOB_WEDDING && dstsd->view_class !=JOB_XMAS) ||
+ (dstsd->view_class==JOB_WEDDING && !battle_config.wedding_ignorepalette) || (dstsd->view_class==JOB_XMAS && !battle_config.xmas_ignorepalette)))
+ clif_changelook(&dstsd->bl, LOOK_CLOTHES_COLOR, dstsd->status.clothes_color);
+
+ if((sd->status.party_id && dstsd->status.party_id == sd->status.party_id) || //Party-mate, or hpdisp setting.
+ (battle_config.disp_hpmeter && (len = pc_isGM(sd)) >= battle_config.disp_hpmeter && len >= pc_isGM(dstsd))
+ )
+ clif_hpmeter_single(sd->fd, dstsd);
+
+ if(sd->status.manner < 0 && battle_config.manner_system)
+ clif_changestatus(&sd->bl,SP_MANNER,sd->status.manner);
+
+ if(sd->state.size==2) // tiny/big players [Valaris]
+ clif_specialeffect(&sd->bl,423,0);
+ else if(sd->state.size==1)
+ clif_specialeffect(&sd->bl,421,0);
+
+ // pvp circle for duel [LuzZza]
+ /*
+ if(dstsd->duel_group)
+ clif_specialeffect(&dstsd->bl, 159, 4);
+ */
+
+}
+
+/*==========================================
+ * NPC表示
+ *------------------------------------------
+ */
+//fixed by Valaris
+void clif_getareachar_npc(struct map_session_data* sd,struct npc_data* nd)
+{
+ int len;
+ nullpo_retv(sd);
+ nullpo_retv(nd);
+ if(nd->class_ < 0 || nd->flag&1 || nd->class_ == INVISIBLE_CLASS)
+ return;
+ if(nd->state.state == MS_WALK){
+ WFIFOHEAD(sd->fd, packet_len_table[0x7b]);
+ len = clif_npc007b(nd,WFIFOP(sd->fd,0));
+ WFIFOSET(sd->fd,len);
+ } else {
+ WFIFOHEAD(sd->fd, packet_len_table[0x78]);
+ len = clif_npc0078(nd,WFIFOP(sd->fd,0));
+ WFIFOSET(sd->fd,len);
+ }
+ if(nd->chat_id){
+ clif_dispchat((struct chat_data*)map_id2bl(nd->chat_id),sd->fd);
+ }
+}
+
+/*==========================================
+ * 移動停止
+ *------------------------------------------
+ */
+int clif_movemob(struct mob_data *md)
+{
+ unsigned char buf[256];
+ int len;
+
+ nullpo_retr(0, md);
+
+ len = clif_mob007b(md,buf);
+ clif_send(buf,len,&md->bl,AREA);
+
+ if(battle_config.save_clothcolor && pcdb_checkid(mob_get_viewclass(md->class_)) && mob_get_clothes_color(md->class_)) // [Valaris]
+ clif_changelook(&md->bl, LOOK_CLOTHES_COLOR, mob_get_clothes_color(md->class_));
+
+ if(mob_get_equip(md->class_) > 0) // mob equipment [Valaris]
+ clif_mob_equip(md,mob_get_equip(md->class_));
+
+ if(md->special_state.size==2) // tiny/big mobs [Valaris]
+ clif_specialeffect(&md->bl,423,0);
+ else if(md->special_state.size==1)
+ clif_specialeffect(&md->bl,421,0);
+
+ return 0;
+}
+
+/*==========================================
+ * モンスターの位置修正
+ *------------------------------------------
+ */
+int clif_fixmobpos(struct mob_data *md)
+{
+ unsigned char buf[256];
+ int len;
+
+ nullpo_retr(0, md);
+
+ if(md->state.state == MS_WALK){
+ len = clif_mob007b(md,buf);
+ clif_send(buf,len,&md->bl,AREA);
+ } else {
+ len = clif_mob0078(md,buf);
+ clif_send(buf,len,&md->bl,AREA);
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * PCの位置修正
+ *------------------------------------------
+ */
+int clif_fixpcpos(struct map_session_data *sd)
+{
+ unsigned char buf[256];
+ int len;
+
+ nullpo_retr(0, sd);
+
+ if(sd->walktimer != -1){
+ len = clif_set007b(sd,buf);
+ clif_send(buf,len,&sd->bl,AREA);
+ } else {
+ len = clif_set0078(sd,buf);
+ clif_send(buf,len,&sd->bl,AREA);
+ }
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int clif_fixpetpos(struct pet_data *pd)
+{
+ unsigned char buf[256];
+ int len;
+
+ nullpo_retr(0, pd);
+
+ if(pd->state.state == MS_WALK){
+ len = clif_pet007b(pd,buf);
+ clif_send(buf,len,&pd->bl,AREA);
+ } else {
+ len = clif_pet0078(pd,buf);
+ clif_send(buf,len,&pd->bl,AREA);
+ }
+
+ return 0;
+}
+
+// npc walking [Valaris]
+int clif_fixnpcpos(struct npc_data *nd)
+{
+ unsigned char buf[256];
+ int len;
+
+ nullpo_retr(0, nd);
+
+ if(nd->state.state == MS_WALK){
+ len = clif_npc007b(nd,buf);
+ clif_send(buf,len,&nd->bl,AREA);
+ } else {
+ len = clif_npc0078(nd,buf);
+ clif_send(buf,len,&nd->bl,AREA);
+ }
+
+ return 0;
+}
+/*==========================================
+ * Modifies the type of damage according to status changes [Skotlex]
+ *------------------------------------------
+ */
+static int clif_calc_delay(struct block_list *dst, int type, int delay)
+{
+ if (type == 1 || type == 4 || type == 0x0a) //Type 1 is the crouching animation, type 4 are non-flinching attacks, 0x0a - crits.
+ return type;
+
+ if (delay == 0)
+ return 9; //Endure type attack (damage delay is 0)
+
+ return type;
+}
+/*==========================================
+ * 通常攻撃エフェクト&ダメージ
+ *------------------------------------------
+ */
+int clif_damage(struct block_list *src,struct block_list *dst,unsigned int tick,int sdelay,int ddelay,int damage,int div,int type,int damage2)
+{
+ unsigned char buf[256];
+ struct status_change *sc_data;
+
+ nullpo_retr(0, src);
+ nullpo_retr(0, dst);
+
+ type = clif_calc_delay(dst, type, ddelay); //Type defaults to 0 for normal attacks.
+
+ sc_data = status_get_sc_data(dst);
+
+ if(sc_data) {
+ if(sc_data[SC_HALLUCINATION].timer != -1) {
+ if(damage > 0)
+ damage = damage*(5+sc_data[SC_HALLUCINATION].val1) + rand()%100;
+ if(damage2 > 0)
+ damage2 = damage2*(5+sc_data[SC_HALLUCINATION].val1) + rand()%100;
+ }
+ }
+
+ WBUFW(buf,0)=0x8a;
+ if(src->type==BL_PC && ((struct map_session_data *)src)->disguise)
+ WBUFL(buf,2)=-src->id;
+ else
+ WBUFL(buf,2)=src->id;
+ if(dst->type==BL_PC && ((struct map_session_data *)dst)->disguise)
+ WBUFL(buf,6)=-dst->id;
+ else
+ WBUFL(buf,6)=dst->id;
+ WBUFL(buf,10)=tick;
+ WBUFL(buf,14)=sdelay;
+ WBUFL(buf,18)=ddelay;
+ WBUFW(buf,22)=(damage > 0x7fff)? 0x7fff:damage;
+ WBUFW(buf,24)=div;
+ WBUFB(buf,26)=type;
+ WBUFW(buf,27)=damage2;
+ clif_send(buf,packet_len_table[0x8a],src,AREA);
+
+ if((src->type==BL_PC && ((struct map_session_data *)src)->disguise) || (dst->type==BL_PC && ((struct map_session_data *)dst)->disguise)) {
+ memset(buf,0,packet_len_table[0x8a]);
+ WBUFW(buf,0)=0x8a;
+ if(src->type==BL_PC && ((struct map_session_data *)src)->disguise)
+ WBUFL(buf,2)=src->id;
+ else
+ WBUFL(buf,2)=-src->id;
+ if(dst->type==BL_PC && ((struct map_session_data *)dst)->disguise)
+ WBUFL(buf,6)=dst->id;
+ else
+ WBUFL(buf,2)=-dst->id;
+ WBUFL(buf,10)=tick;
+ WBUFL(buf,14)=sdelay;
+ WBUFL(buf,18)=ddelay;
+ if(damage > 0)
+ WBUFW(buf,22)=-1;
+ else
+ WBUFW(buf,22)=0;
+ WBUFW(buf,24)=div;
+ WBUFB(buf,26)=type;
+ WBUFW(buf,27)=0;
+ clif_send(buf,packet_len_table[0x8a],src,AREA);
+ }
+ //Because the damage delay must be synced with the client, here is where the can-walk tick must be updated. [Skotlex]
+ if (type != 4 && type != 9 && damage+damage2 > 0) //Non-endure/Non-flinch attack, update walk delay.
+ battle_walkdelay(dst, tick, sdelay, ddelay, div);
+
+ // [Valaris]
+ if(battle_config.save_clothcolor && src->type==BL_MOB &&
+ pcdb_checkid(mob_get_viewclass(((struct mob_data *)src)->class_)) && mob_get_clothes_color(((struct mob_data *)src)->class_) > 0)
+ clif_changelook(src, LOOK_CLOTHES_COLOR, mob_get_clothes_color(((struct mob_data *)src)->class_));
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+void clif_getareachar_mob(struct map_session_data* sd,struct mob_data* md)
+{
+ int len;
+ nullpo_retv(sd);
+ nullpo_retv(md);
+
+ if (session[sd->fd] == NULL)
+ return;
+
+ if(md->state.state == MS_WALK){
+#if PACKETVER < 4
+ WFIFOHEAD(sd->fd,packet_len_table[0x78]);
+#else
+ WFIFOHEAD(sd->fd,packet_len_table[0x1d8]);
+#endif
+ len = clif_mob007b(md,WFIFOP(sd->fd,0));
+ WFIFOSET(sd->fd,len);
+ } else {
+#if PACKETVER < 4
+ WFIFOHEAD(sd->fd,packet_len_table[0x78]);
+#else
+ WFIFOHEAD(sd->fd,packet_len_table[0x1d8]);
+#endif
+ len = clif_mob0078(md,WFIFOP(sd->fd,0));
+ WFIFOSET(sd->fd,len);
+ }
+
+ if(battle_config.save_clothcolor && pcdb_checkid(mob_get_viewclass(md->class_)) && mob_get_clothes_color(md->class_)) // [Valaris]
+ clif_changelook(&md->bl, LOOK_CLOTHES_COLOR, mob_get_clothes_color(md->class_));
+
+ if(mob_get_equip(md->class_) > 0) // mob equipment [Valaris]
+ clif_mob_equip(md,mob_get_equip(md->class_));
+
+ if(md->special_state.size==2) // tiny/big mobs [Valaris]
+ clif_specialeffect(&md->bl,423,0);
+ else if(md->special_state.size==1)
+ clif_specialeffect(&md->bl,421,0);
+
+
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+void clif_getareachar_pet(struct map_session_data* sd,struct pet_data* pd)
+{
+ int len;
+
+ nullpo_retv(sd);
+ nullpo_retv(pd);
+
+ if(pd->state.state == MS_WALK){
+#if PACKETVER < 4
+ WFIFOHEAD(sd->fd,packet_len_table[0x7b]);
+#else
+ WFIFOHEAD(sd->fd,packet_len_table[0x1da]);
+#endif
+ len = clif_pet007b(pd,WFIFOP(sd->fd,0));
+ WFIFOSET(sd->fd,len);
+ } else {
+#if PACKETVER < 4
+ WFIFOHEAD(sd->fd,packet_len_table[0x7b]);
+#else
+ WFIFOHEAD(sd->fd,packet_len_table[0x1da]);
+#endif
+ len = clif_pet0078(pd,WFIFOP(sd->fd,0));
+ WFIFOSET(sd->fd,len);
+ }
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+void clif_getareachar_item(struct map_session_data* sd,struct flooritem_data* fitem)
+{
+ int view,fd;
+
+ nullpo_retv(sd);
+ nullpo_retv(fitem);
+
+ fd=sd->fd;
+ //009d <ID>.l <item ID>.w <identify flag>.B <X>.w <Y>.w <amount>.w <subX>.B <subY>.B
+ WFIFOHEAD(fd,packet_len_table[0x9d]);
+
+ WFIFOW(fd,0)=0x9d;
+ WFIFOL(fd,2)=fitem->bl.id;
+ if((view = itemdb_viewid(fitem->item_data.nameid)) > 0)
+ WFIFOW(fd,6)=view;
+ else
+ WFIFOW(fd,6)=fitem->item_data.nameid;
+ WFIFOB(fd,8)=fitem->item_data.identify;
+ WFIFOW(fd,9)=fitem->bl.x;
+ WFIFOW(fd,11)=fitem->bl.y;
+ WFIFOW(fd,13)=fitem->item_data.amount;
+ WFIFOB(fd,15)=fitem->subx;
+ WFIFOB(fd,16)=fitem->suby;
+
+ WFIFOSET(fd,packet_len_table[0x9d]);
+}
+/*==========================================
+ * 場所スキルエフェクトが視界に入る
+ *------------------------------------------
+ */
+int clif_getareachar_skillunit(struct map_session_data *sd,struct skill_unit *unit)
+{
+ int fd;
+ struct block_list *bl;
+
+ nullpo_retr(0, unit);
+
+ fd=sd->fd;
+ bl=map_id2bl(unit->group->src_id);
+#if PACKETVER < 3
+ WFIFOHEAD(fd,packet_len_table[0x11f]);
+ memset(WFIFOP(fd,0),0,packet_len_table[0x11f]);
+ WFIFOW(fd, 0)=0x11f;
+ WFIFOL(fd, 2)=unit->bl.id;
+ WFIFOL(fd, 6)=unit->group->src_id;
+ WFIFOW(fd,10)=unit->bl.x;
+ WFIFOW(fd,12)=unit->bl.y;
+ WFIFOB(fd,14)=unit->group->unit_id;
+ WFIFOB(fd,15)=0;
+ WFIFOSET(fd,packet_len_table[0x11f]);
+#else
+ WFIFOHEAD(fd,packet_len_table[0x1c9]);
+ memset(WFIFOP(fd,0),0,packet_len_table[0x1c9]);
+ WFIFOW(fd, 0)=0x1c9;
+ WFIFOL(fd, 2)=unit->bl.id;
+ WFIFOL(fd, 6)=unit->group->src_id;
+ WFIFOW(fd,10)=unit->bl.x;
+ WFIFOW(fd,12)=unit->bl.y;
+ WFIFOB(fd,14)=unit->group->unit_id;
+ WFIFOB(fd,15)=1;
+ if(unit->group->unit_id==0xb0) { // Graffiti [Valaris]
+ WFIFOB(fd,16)=1;
+ memcpy(WFIFOP(fd,17),unit->group->valstr,MESSAGE_SIZE);
+ } else {
+ WFIFOL(fd,15+1)=0; //1-4調べた限り固定
+ WFIFOL(fd,15+5)=0; //5-8調べた限り固定
+ //9-12マップごとで一定の77-80とはまた違う4バイトのかなり大きな数字
+ WFIFOL(fd,15+13)=unit->bl.y - 0x12; //13-16ユニットのY座標-18っぽい(Y:17でFF FF FF FF)
+ WFIFOL(fd,15+17)=0x004f37dd; //17-20調べた限り固定
+ WFIFOL(fd,15+21)=0x0012f674; //21-24調べた限り固定
+ WFIFOL(fd,15+25)=0x0012f664; //25-28調べた限り固定
+ WFIFOL(fd,15+29)=0x0012f654; //29-32調べた限り固定
+ WFIFOL(fd,15+33)=0x77527bbc; //33-36調べた限り固定
+ //37-39
+ WFIFOB(fd,15+40)=0x2d; //40調べた限り固定
+ WFIFOL(fd,15+41)=0; //41-44調べた限り0固定
+ WFIFOL(fd,15+45)=0; //45-48調べた限り0固定
+ WFIFOL(fd,15+49)=0; //49-52調べた限り0固定
+ WFIFOL(fd,15+53)=0x0048d919; //53-56調べた限り固定
+ WFIFOL(fd,15+57)=0x0000003e; //57-60調べた限り固定
+ WFIFOL(fd,15+61)=0x0012f66c; //61-64調べた限り固定
+ //65-68
+ //69-72
+ if(bl) WFIFOL(fd,15+73)=bl->y; //73-76術者のY座標
+ WFIFOL(fd,15+77)=unit->bl.m; //77-80マップIDかなぁ?かなり2バイトで足りそうな数字
+ WFIFOB(fd,15+81)=0xaa; //81終端文字0xaa
+ }
+
+ WFIFOSET(fd,packet_len_table[0x1c9]);
+#endif
+ if(unit->group->skill_id == WZ_ICEWALL)
+ clif_set0192(fd,unit->bl.m,unit->bl.x,unit->bl.y,5);
+
+ return 0;
+}
+/*==========================================
+ * 場所スキルエフェクトが視界から消える
+ *------------------------------------------
+ */
+int clif_clearchar_skillunit(struct skill_unit *unit,int fd)
+{
+ nullpo_retr(0, unit);
+
+ WFIFOHEAD(fd,packet_len_table[0x120]);
+ WFIFOW(fd, 0)=0x120;
+ WFIFOL(fd, 2)=unit->bl.id;
+ WFIFOSET(fd,packet_len_table[0x120]);
+ if(unit->group && unit->group->skill_id == WZ_ICEWALL)
+ clif_set0192(fd,unit->bl.m,unit->bl.x,unit->bl.y,unit->val2);
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int clif_01ac(struct block_list *bl)
+{
+ unsigned char buf[32];
+
+ nullpo_retr(0, bl);
+
+ WBUFW(buf, 0) = 0x1ac;
+ WBUFL(buf, 2) = bl->id;
+
+ clif_send(buf,packet_len_table[0x1ac],bl,AREA);
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+ int clif_getareachar(struct block_list* bl,va_list ap)
+{
+ struct map_session_data *sd;
+
+ nullpo_retr(0, bl);
+ nullpo_retr(0, ap);
+
+ sd=va_arg(ap,struct map_session_data*);
+
+ if (sd == NULL || session[sd->fd] == NULL)
+ return 0;
+
+ switch(bl->type){
+ case BL_PC:
+ if(sd==(struct map_session_data*)bl)
+ break;
+ clif_getareachar_pc(sd,(struct map_session_data*) bl);
+ break;
+ case BL_NPC:
+ clif_getareachar_npc(sd,(struct npc_data*) bl);
+ break;
+ case BL_MOB:
+ clif_getareachar_mob(sd,(struct mob_data*) bl);
+ break;
+ case BL_PET:
+ clif_getareachar_pet(sd,(struct pet_data*) bl);
+ break;
+ case BL_ITEM:
+ clif_getareachar_item(sd,(struct flooritem_data*) bl);
+ break;
+ case BL_SKILL:
+ clif_getareachar_skillunit(sd,(struct skill_unit *)bl);
+ break;
+ default:
+ if(battle_config.error_log)
+ ShowError("clif_getareachar: Unrecognized type %d.\n",bl->type);
+ break;
+ }
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int clif_pcoutsight(struct block_list *bl,va_list ap)
+{
+ struct map_session_data *sd,*dstsd;
+
+ nullpo_retr(0, bl);
+ nullpo_retr(0, ap);
+ nullpo_retr(0, sd=va_arg(ap,struct map_session_data*));
+
+ switch(bl->type){
+ case BL_PC:
+ dstsd = (struct map_session_data*) bl;
+ if(sd != dstsd) {
+ clif_clearchar_id(dstsd->bl.id,0,sd->fd);
+ clif_clearchar_id(sd->bl.id,0,dstsd->fd);
+ if(dstsd->disguise || sd->disguise) {
+ clif_clearchar_id(-dstsd->bl.id,0,sd->fd);
+ clif_clearchar_id(-sd->bl.id,0,dstsd->fd);
+ }
+ if(dstsd->chatID){
+ struct chat_data *cd;
+ cd=(struct chat_data*)map_id2bl(dstsd->chatID);
+ if(cd->usersd[0]==dstsd)
+ clif_dispchat(cd,sd->fd);
+ }
+ if(dstsd->vender_id){
+ clif_closevendingboard(&dstsd->bl,sd->fd);
+ }
+ }
+ break;
+ case BL_NPC:
+ if( ((struct npc_data *)bl)->class_ != INVISIBLE_CLASS )
+ clif_clearchar_id(bl->id,0,sd->fd);
+ break;
+ case BL_MOB:
+ case BL_PET:
+ clif_clearchar_id(bl->id,0,sd->fd);
+ break;
+ case BL_ITEM:
+ clif_clearflooritem((struct flooritem_data*)bl,sd->fd);
+ break;
+ case BL_SKILL:
+ clif_clearchar_skillunit((struct skill_unit *)bl,sd->fd);
+ break;
+ }
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int clif_pcinsight(struct block_list *bl,va_list ap)
+{
+ struct map_session_data *sd,*dstsd;
+
+ nullpo_retr(0, bl);
+ nullpo_retr(0, ap);
+ nullpo_retr(0, sd=va_arg(ap,struct map_session_data*));
+
+ switch(bl->type){
+ case BL_PC:
+ dstsd = (struct map_session_data *)bl;
+ if(sd != dstsd) {
+ clif_getareachar_pc(sd,dstsd);
+ clif_getareachar_pc(dstsd,sd);
+ }
+ break;
+ case BL_NPC:
+ clif_getareachar_npc(sd,(struct npc_data*)bl);
+ break;
+ case BL_MOB:
+ clif_getareachar_mob(sd,(struct mob_data*)bl);
+ break;
+ case BL_PET:
+ clif_getareachar_pet(sd,(struct pet_data*)bl);
+ break;
+ case BL_ITEM:
+ clif_getareachar_item(sd,(struct flooritem_data*)bl);
+ break;
+ case BL_SKILL:
+ clif_getareachar_skillunit(sd,(struct skill_unit *)bl);
+ break;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int clif_moboutsight(struct block_list *bl,va_list ap)
+{
+ struct map_session_data *sd;
+ struct mob_data *md;
+
+ nullpo_retr(0, bl);
+ nullpo_retr(0, ap);
+ nullpo_retr(0, md=va_arg(ap,struct mob_data*));
+
+ if(bl->type==BL_PC
+ && ((sd = (struct map_session_data*) bl) != NULL)
+ && session[sd->fd] != NULL) {
+ clif_clearchar_id(md->bl.id,0,sd->fd);
+ }
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int clif_mobinsight(struct block_list *bl,va_list ap)
+{
+ struct map_session_data *sd;
+ struct mob_data *md;
+
+ nullpo_retr(0, bl);
+ nullpo_retr(0, ap);
+
+ md=va_arg(ap,struct mob_data*);
+ if(bl->type==BL_PC
+ && ((sd = (struct map_session_data*) bl) != NULL)
+ && session[sd->fd] != NULL) {
+ clif_getareachar_mob(sd,md);
+ }
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int clif_petoutsight(struct block_list *bl,va_list ap)
+{
+ struct map_session_data *sd;
+ struct pet_data *pd;
+
+ nullpo_retr(0, bl);
+ nullpo_retr(0, ap);
+ nullpo_retr(0, pd=va_arg(ap,struct pet_data*));
+
+ if(bl->type==BL_PC
+ && ((sd = (struct map_session_data*) bl) != NULL)
+ && session[sd->fd] != NULL) {
+ clif_clearchar_id(pd->bl.id,0,sd->fd);
+ }
+
+ return 0;
+}
+
+// npc walking [Valaris]
+int clif_npcoutsight(struct block_list *bl,va_list ap)
+{
+ struct map_session_data *sd;
+ struct npc_data *nd;
+
+ nullpo_retr(0, bl);
+ nullpo_retr(0, ap);
+ nullpo_retr(0, nd=va_arg(ap,struct npc_data*));
+
+ if(bl->type==BL_PC
+ && ((sd = (struct map_session_data*) bl) != NULL)
+ && session[sd->fd] != NULL) {
+ clif_clearchar_id(nd->bl.id,0,sd->fd);
+ }
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int clif_petinsight(struct block_list *bl,va_list ap)
+{
+ struct map_session_data *sd;
+ struct pet_data *pd;
+
+ nullpo_retr(0, bl);
+ nullpo_retr(0, ap);
+
+ pd=va_arg(ap,struct pet_data*);
+ if(bl->type==BL_PC
+ && ((sd = (struct map_session_data*) bl) != NULL)
+ && session[sd->fd] != NULL) {
+ clif_getareachar_pet(sd,pd);
+ }
+
+ return 0;
+}
+
+// npc walking [Valaris]
+int clif_npcinsight(struct block_list *bl,va_list ap)
+{
+ struct map_session_data *sd;
+ struct npc_data *nd;
+
+ nullpo_retr(0, bl);
+ nullpo_retr(0, ap);
+
+ nd=va_arg(ap,struct npc_data*);
+ if(bl->type==BL_PC
+ && ((sd = (struct map_session_data*) bl) != NULL)
+ && session[sd->fd] != NULL) {
+ clif_getareachar_npc(sd,nd);
+ }
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int clif_skillinfo(struct map_session_data *sd,int skillid,int type,int range)
+{
+ int fd,id, inf2;
+
+ nullpo_retr(0, sd);
+
+ fd=sd->fd;
+ if( (id=sd->status.skill[skillid].id) <= 0 )
+ return 0;
+ WFIFOHEAD(fd,packet_len_table[0x147]);
+ WFIFOW(fd,0)=0x147;
+ WFIFOW(fd,2) = id;
+ if(type < 0)
+ WFIFOW(fd,4) = skill_get_inf(id);
+ else
+ WFIFOW(fd,4) = type;
+ WFIFOW(fd,6) = 0;
+ WFIFOW(fd,8) = sd->status.skill[skillid].lv;
+ WFIFOW(fd,10) = skill_get_sp(id,sd->status.skill[skillid].lv);
+ if(range < 0)
+ range = skill_get_range2(&sd->bl, id,sd->status.skill[skillid].lv);
+
+ WFIFOW(fd,12)= range;
+ strncpy(WFIFOP(fd,14), skill_get_name(id), NAME_LENGTH);
+ inf2 = skill_get_inf2(id);
+ if(((!(inf2&INF2_QUEST_SKILL) || battle_config.quest_skill_learn) &&
+ !(inf2&(INF2_WEDDING_SKILL|INF2_SPIRIT_SKILL))) ||
+ (battle_config.gm_allskill > 0 && pc_isGM(sd) >= battle_config.gm_allskill))
+ //WFIFOB(fd,38)= (sd->status.skill[skillid].lv < skill_get_max(id) && sd->status.skill[skillid].flag ==0 )? 1:0;
+ WFIFOB(fd,38)= (sd->status.skill[skillid].lv < skill_tree_get_max(id, sd->status.class_) && sd->status.skill[skillid].flag ==0 )? 1:0;
+ else
+ WFIFOB(fd,38) = 0;
+ WFIFOSET(fd,packet_len_table[0x147]);
+
+ return 0;
+}
+
+/*==========================================
+ * スキルリストを送信する
+ *------------------------------------------
+ */
+int clif_skillinfoblock(struct map_session_data *sd)
+{
+ int fd;
+ int i,c,len=4,id, inf2;
+
+ nullpo_retr(0, sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd, MAX_SKILL * 37 + 4);
+ WFIFOW(fd,0)=0x10f;
+ for ( i = c = 0; i < MAX_SKILL; i++){
+ if( (id=sd->status.skill[i].id)!=0 ){
+ WFIFOW(fd,len ) = id;
+ WFIFOW(fd,len+2) = skill_get_inf(id);
+ WFIFOW(fd,len+4) = 0;
+ WFIFOW(fd,len+6) = sd->status.skill[i].lv;
+ WFIFOW(fd,len+8) = skill_get_sp(id,sd->status.skill[i].lv);
+ WFIFOW(fd,len+10)= skill_get_range2(&sd->bl, id,sd->status.skill[i].lv);
+ strncpy(WFIFOP(fd,len+12), skill_get_name(id), NAME_LENGTH);
+ inf2 = skill_get_inf2(id);
+ if(((!(inf2&INF2_QUEST_SKILL) || battle_config.quest_skill_learn) &&
+ !(inf2&(INF2_WEDDING_SKILL|INF2_SPIRIT_SKILL))) ||
+ (battle_config.gm_allskill > 0 && pc_isGM(sd) >= battle_config.gm_allskill) )
+ //WFIFOB(fd,len+36)= (sd->status.skill[i].lv < skill_get_max(id) && sd->status.skill[i].flag ==0 )? 1:0;
+ WFIFOB(fd,len+36)= (sd->status.skill[i].lv < skill_tree_get_max(id, sd->status.class_) && sd->status.skill[i].flag ==0 )? 1:0;
+ else
+ WFIFOB(fd,len+36) = 0;
+ len+=37;
+ c++;
+ }
+ }
+ WFIFOW(fd,2)=len;
+ WFIFOSET(fd,len);
+
+ return 0;
+}
+
+/*==========================================
+ * スキル割り振り通知
+ *------------------------------------------
+ */
+int clif_skillup(struct map_session_data *sd,int skill_num)
+{
+ int fd;
+
+ nullpo_retr(0, sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,packet_len_table[0x10e]);
+ WFIFOW(fd,0) = 0x10e;
+ WFIFOW(fd,2) = skill_num;
+ WFIFOW(fd,4) = sd->status.skill[skill_num].lv;
+ WFIFOW(fd,6) = skill_get_sp(skill_num,sd->status.skill[skill_num].lv);
+ WFIFOW(fd,8) = skill_get_range2(&sd->bl,skill_num,sd->status.skill[skill_num].lv);
+ //WFIFOB(fd,10) = (sd->status.skill[skill_num].lv < skill_get_max(sd->status.skill[skill_num].id)) ? 1 : 0;
+ WFIFOB(fd,10) = (sd->status.skill[skill_num].lv < skill_tree_get_max(sd->status.skill[skill_num].id, sd->status.class_)) ? 1 : 0;
+ WFIFOSET(fd,packet_len_table[0x10e]);
+
+ return 0;
+}
+
+/*==========================================
+ * スキル詠唱エフェクトを送信する
+ *------------------------------------------
+ */
+int clif_skillcasting(struct block_list* bl,
+ int src_id,int dst_id,int dst_x,int dst_y,int skill_num,int casttime)
+{
+ int pl = skill_get_pl(skill_num);
+ unsigned char buf[32];
+ WBUFW(buf,0) = 0x13e;
+ WBUFL(buf,2) = src_id;
+ WBUFL(buf,6) = dst_id;
+ WBUFW(buf,10) = dst_x;
+ WBUFW(buf,12) = dst_y;
+ WBUFW(buf,14) = skill_num;
+ WBUFL(buf,16) = pl<0?0:pl; //Avoid sending negatives as element [Skotlex]
+ WBUFL(buf,20) = casttime;
+ clif_send(buf,packet_len_table[0x13e], bl, AREA);
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int clif_skillcastcancel(struct block_list* bl)
+{
+ unsigned char buf[16];
+
+ nullpo_retr(0, bl);
+
+ WBUFW(buf,0) = 0x1b9;
+ WBUFL(buf,2) = bl->id;
+ clif_send(buf,packet_len_table[0x1b9], bl, AREA);
+
+ return 0;
+}
+
+/*==========================================
+ * スキル詠唱失敗
+ *------------------------------------------
+ */
+int clif_skill_fail(struct map_session_data *sd,int skill_id,int type,int btype)
+{
+ int fd;
+
+ nullpo_retr(0, sd);
+
+ fd=sd->fd;
+
+ // reset all variables [celest]
+ sd->skillx = sd->skilly = -1;
+ sd->skillid = sd->skilllv = -1;
+ sd->skillitem = sd->skillitemlv = -1;
+
+ if(type==0x4 && !sd->state.showdelay)
+ return 0;
+
+ WFIFOHEAD(fd,packet_len_table[0x110]);
+ WFIFOW(fd,0) = 0x110;
+ WFIFOW(fd,2) = skill_id;
+ WFIFOW(fd,4) = btype;
+ WFIFOW(fd,6) = 0;
+ WFIFOB(fd,8) = 0;
+ WFIFOB(fd,9) = type;
+ WFIFOSET(fd,packet_len_table[0x110]);
+
+ return 0;
+}
+
+/*==========================================
+ * スキル攻撃エフェクト&ダメージ
+ *------------------------------------------
+ */
+int clif_skill_damage(struct block_list *src,struct block_list *dst,
+ unsigned int tick,int sdelay,int ddelay,int damage,int div,int skill_id,int skill_lv,int type)
+{
+ unsigned char buf[64];
+ struct status_change *sc_data;
+
+ nullpo_retr(0, src);
+ nullpo_retr(0, dst);
+
+ type = clif_calc_delay(dst, (type>0)?type:skill_get_hit(skill_id), ddelay);
+ sc_data = status_get_sc_data(dst);
+
+ if(sc_data) {
+ if(sc_data[SC_HALLUCINATION].timer != -1 && damage > 0)
+ damage = damage*(5+sc_data[SC_HALLUCINATION].val1) + rand()%100;
+ }
+
+#if PACKETVER < 3
+ WBUFW(buf,0)=0x114;
+ WBUFW(buf,2)=skill_id;
+ if(src->type==BL_PC && ((struct map_session_data *)src)->disguise)
+ WBUFL(buf,4)=-src->id;
+ else
+ WBUFL(buf,4)=src->id;
+ if(dst->type==BL_PC && ((struct map_session_data *)dst)->disguise)
+ WBUFL(buf,8)=-dst->id;
+ else
+ WBUFL(buf,8)=dst->id;
+ WBUFL(buf,12)=tick;
+ WBUFL(buf,16)=sdelay;
+ WBUFL(buf,20)=ddelay;
+ WBUFW(buf,24)=damage;
+ WBUFW(buf,26)=skill_lv;
+ WBUFW(buf,28)=div;
+ WBUFB(buf,30)=type;
+ clif_send(buf,packet_len_table[0x114],src,AREA);
+#else
+ WBUFW(buf,0)=0x1de;
+ WBUFW(buf,2)=skill_id;
+ if(src->type==BL_PC && ((struct map_session_data *)src)->disguise)
+ WBUFL(buf,4)=-src->id;
+ else
+ WBUFL(buf,4)=src->id;
+ if(dst->type==BL_PC && ((struct map_session_data *)dst)->disguise)
+ WBUFL(buf,8)=-dst->id;
+ else
+ WBUFL(buf,8)=dst->id;
+ WBUFL(buf,12)=tick;
+ WBUFL(buf,16)=sdelay;
+ WBUFL(buf,20)=ddelay;
+ WBUFL(buf,24)=damage;
+ WBUFW(buf,28)=skill_lv;
+ WBUFW(buf,30)=div;
+ WBUFB(buf,32)=type;
+ clif_send(buf,packet_len_table[0x1de],src,AREA);
+#endif
+
+ //Because the damage delay must be synced with the client, here is where the can-walk tick must be updated. [Skotlex]
+ if (type != 4 && type != 9 && damage > 0) //Non-endure/Non-flinch attack, update walk delay.
+ battle_walkdelay(dst, tick, sdelay, ddelay, div);
+ return 0;
+}
+
+/*==========================================
+ * 吹き飛ばしスキル攻撃エフェクト&ダメージ
+ *------------------------------------------
+ */
+int clif_skill_damage2(struct block_list *src,struct block_list *dst,
+ unsigned int tick,int sdelay,int ddelay,int damage,int div,int skill_id,int skill_lv,int type)
+{
+ unsigned char buf[64];
+ struct status_change *sc_data;
+
+ nullpo_retr(0, src);
+ nullpo_retr(0, dst);
+
+ type = clif_calc_delay(dst, (type>0)?type:skill_get_hit(skill_id), ddelay);
+ sc_data = status_get_sc_data(dst);
+
+ if(sc_data) {
+ if(sc_data[SC_HALLUCINATION].timer != -1 && damage > 0)
+ damage = damage*(5+sc_data[SC_HALLUCINATION].val1) + rand()%100;
+ }
+
+ WBUFW(buf,0)=0x115;
+ WBUFW(buf,2)=skill_id;
+ WBUFL(buf,4)=src->id;
+ WBUFL(buf,8)=dst->id;
+ WBUFL(buf,12)=tick;
+ WBUFL(buf,16)=sdelay;
+ WBUFL(buf,20)=ddelay;
+ WBUFW(buf,24)=dst->x;
+ WBUFW(buf,26)=dst->y;
+ WBUFW(buf,28)=damage;
+ WBUFW(buf,30)=skill_lv;
+ WBUFW(buf,32)=div;
+ WBUFB(buf,34)=type;
+ clif_send(buf,packet_len_table[0x115],src,AREA);
+
+ //Because the damage delay must be synced with the client, here is where the can-walk tick must be updated. [Skotlex]
+ if (type != 4 && type != 9 && damage > 0) //Non-endure/Non-flinch attack, update walk delay.
+ battle_walkdelay(dst, tick, sdelay, ddelay, div);
+ return 0;
+}
+
+/*==========================================
+ * 支援/回復スキルエフェクト
+ *------------------------------------------
+ */
+int clif_skill_nodamage(struct block_list *src,struct block_list *dst,
+ int skill_id,int heal,int fail)
+{
+ unsigned char buf[32];
+
+ nullpo_retr(0, src);
+ nullpo_retr(0, dst);
+
+ WBUFW(buf,0)=0x11a;
+ WBUFW(buf,2)=skill_id;
+ WBUFW(buf,4)=(heal > 0x7fff)? 0x7fff:heal;
+ WBUFL(buf,6)=dst->id;
+ WBUFL(buf,10)=src->id;
+ WBUFB(buf,14)=fail;
+ clif_send(buf,packet_len_table[0x11a],src,AREA);
+
+ return 0;
+}
+
+/*==========================================
+ * 場所スキルエフェクト
+ *------------------------------------------
+ */
+int clif_skill_poseffect(struct block_list *src,int skill_id,int val,int x,int y,int tick)
+{
+ unsigned char buf[32];
+
+ nullpo_retr(0, src);
+
+ WBUFW(buf,0)=0x117;
+ WBUFW(buf,2)=skill_id;
+ WBUFL(buf,4)=src->id;
+ WBUFW(buf,8)=val;
+ WBUFW(buf,10)=x;
+ WBUFW(buf,12)=y;
+ WBUFL(buf,14)=tick;
+ clif_send(buf,packet_len_table[0x117],src,AREA);
+
+ return 0;
+}
+
+/*==========================================
+ * 場所スキルエフェクト表示
+ *------------------------------------------
+ */
+int clif_skill_setunit(struct skill_unit *unit)
+{
+ unsigned char buf[128];
+ struct block_list *bl;
+
+ nullpo_retr(0, unit);
+
+ bl=map_id2bl(unit->group->src_id);
+
+#if PACKETVER < 3
+ memset(WBUFP(buf, 0),0,packet_len_table[0x11f]);
+ WBUFW(buf, 0)=0x11f;
+ WBUFL(buf, 2)=unit->bl.id;
+ WBUFL(buf, 6)=unit->group->src_id;
+ WBUFW(buf,10)=unit->bl.x;
+ WBUFW(buf,12)=unit->bl.y;
+ WBUFB(buf,14)=unit->group->unit_id;
+ WBUFB(buf,15)=0;
+ clif_send(buf,packet_len_table[0x11f],&unit->bl,AREA);
+#else
+ memset(WBUFP(buf, 0),0,packet_len_table[0x1c9]);
+ WBUFW(buf, 0)=0x1c9;
+ WBUFL(buf, 2)=unit->bl.id;
+ WBUFL(buf, 6)=unit->group->src_id;
+ WBUFW(buf,10)=unit->bl.x;
+ WBUFW(buf,12)=unit->bl.y;
+ WBUFB(buf,14)=unit->group->unit_id;
+ WBUFB(buf,15)=1;
+ if(unit->group->unit_id==0xb0) { // Graffiti [Valaris]
+ WBUFB(buf,16)=1;
+ memcpy(WBUFP(buf,17),unit->group->valstr,MESSAGE_SIZE);
+ } else {
+ WBUFL(buf,15+1)=0; //1-4調べた限り固定
+ WBUFL(buf,15+5)=0; //5-8調べた限り固定
+ //9-12マップごとで一定の77-80とはまた違う4バイトのかなり大きな数字
+ WBUFL(buf,15+13)=unit->bl.y - 0x12; //13-16ユニットのY座標-18っぽい(Y:17でFF FF FF FF)
+ WBUFL(buf,15+17)=0x004f37dd; //17-20調べた限り固定(0x1b2で0x004fdbddだった)
+ WBUFL(buf,15+21)=0x0012f674; //21-24調べた限り固定
+ WBUFL(buf,15+25)=0x0012f664; //25-28調べた限り固定
+ WBUFL(buf,15+29)=0x0012f654; //29-32調べた限り固定
+ WBUFL(buf,15+33)=0x77527bbc; //33-36調べた限り固定
+ //37-39
+ WBUFB(buf,15+40)=0x2d; //40調べた限り固定
+ WBUFL(buf,15+41)=0; //41-44調べた限り0固定
+ WBUFL(buf,15+45)=0; //45-48調べた限り0固定
+ WBUFL(buf,15+49)=0; //49-52調べた限り0固定
+ WBUFL(buf,15+53)=0x0048d919; //53-56調べた限り固定(0x01b2で0x00495119だった)
+ WBUFL(buf,15+57)=0x0000003e; //57-60調べた限り固定
+ WBUFL(buf,15+61)=0x0012f66c; //61-64調べた限り固定
+ //65-68
+ //69-72
+ if(bl) WBUFL(buf,15+73)=bl->y; //73-76術者のY座標
+ WBUFL(buf,15+77)=unit->bl.m; //77-80マップIDかなぁ?かなり2バイトで足りそうな数字
+ WBUFB(buf,15+81)=0xaa; //81終端文字0xaa
+ }
+ clif_send(buf,packet_len_table[0x1c9],&unit->bl,AREA);
+#endif
+ return 0;
+}
+/*==========================================
+ * 場所スキルエフェクト削除
+ *------------------------------------------
+ */
+int clif_skill_delunit(struct skill_unit *unit)
+{
+ unsigned char buf[16];
+
+ nullpo_retr(0, unit);
+
+ WBUFW(buf, 0)=0x120;
+ WBUFL(buf, 2)=unit->bl.id;
+ clif_send(buf,packet_len_table[0x120],&unit->bl,AREA);
+ return 0;
+}
+/*==========================================
+ * ワープ場所選択
+ *------------------------------------------
+ */
+int clif_skill_warppoint(struct map_session_data *sd,int skill_num,
+ const char *map1,const char *map2,const char *map3,const char *map4)
+{
+ int fd;
+
+ nullpo_retr(0, sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,packet_len_table[0x11c]);
+ WFIFOW(fd,0)=0x11c;
+ WFIFOW(fd,2)=skill_num;
+ strncpy((char*)WFIFOP(fd, 4),map1,MAP_NAME_LENGTH);
+ strncpy((char*)WFIFOP(fd,20),map2,MAP_NAME_LENGTH);
+ strncpy((char*)WFIFOP(fd,36),map3,MAP_NAME_LENGTH);
+ strncpy((char*)WFIFOP(fd,52),map4,MAP_NAME_LENGTH);
+ WFIFOSET(fd,packet_len_table[0x11c]);
+ return 0;
+}
+/*==========================================
+ * メモ応答
+ *------------------------------------------
+ */
+int clif_skill_memo(struct map_session_data *sd,int flag)
+{
+ int fd;
+
+ nullpo_retr(0, sd);
+
+ fd=sd->fd;
+
+ WFIFOHEAD(fd,packet_len_table[0x11e]);
+ WFIFOW(fd,0)=0x11e;
+ WFIFOB(fd,2)=flag;
+ WFIFOSET(fd,packet_len_table[0x11e]);
+ return 0;
+}
+int clif_skill_teleportmessage(struct map_session_data *sd,int flag)
+{
+ int fd;
+
+ nullpo_retr(0, sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,packet_len_table[0x189]);
+ WFIFOW(fd,0)=0x189;
+ WFIFOW(fd,2)=flag;
+ WFIFOSET(fd,packet_len_table[0x189]);
+ return 0;
+}
+
+/*==========================================
+ * モンスター情報
+ *------------------------------------------
+ */
+int clif_skill_estimation(struct map_session_data *sd,struct block_list *dst)
+{
+ struct mob_data *md;
+ unsigned char buf[64];
+ int i;//, fix;
+
+ nullpo_retr(0, sd);
+ nullpo_retr(0, dst);
+
+ if(dst->type!=BL_MOB )
+ return 0;
+ if((md=(struct mob_data *)dst) == NULL)
+ return 0;
+
+ WBUFW(buf, 0)=0x18c;
+ WBUFW(buf, 2)=mob_get_viewclass(md->class_);
+ WBUFW(buf, 4)=md->level;
+ WBUFW(buf, 6)=md->db->size;
+ WBUFL(buf, 8)=md->hp;
+ WBUFW(buf,12)= (battle_config.estimation_type&1?status_get_def(&md->bl):0)
+ +(battle_config.estimation_type&2?status_get_def2(&md->bl):0);
+ WBUFW(buf,14)=md->db->race;
+ WBUFW(buf,16)= (battle_config.estimation_type&1?status_get_mdef(&md->bl):0)
+ +(battle_config.estimation_type&2?status_get_mdef2(&md->bl) - (md->db->vit>>1):0);
+ WBUFW(buf,18)=status_get_elem_type(&md->bl);
+ for(i=0;i<9;i++)
+ WBUFB(buf,20+i)= (unsigned char)battle_attr_fix(NULL,dst,100,i+1,md->def_ele);
+// The following caps negative attributes to 0 since the client displays them as 255-fix. [Skotlex]
+// WBUFB(buf,20+i)= (unsigned char)((fix=battle_attr_fix(NULL,dst,100,i+1,md->def_ele))<0?0:fix);
+
+ if(sd->status.party_id>0)
+ clif_send(buf,packet_len_table[0x18c],&sd->bl,PARTY_AREA);
+ else{
+ WFIFOHEAD(sd->fd,packet_len_table[0x18c]);
+ memcpy(WFIFOP(sd->fd,0),buf,packet_len_table[0x18c]);
+ WFIFOSET(sd->fd,packet_len_table[0x18c]);
+ }
+ return 0;
+}
+/*==========================================
+ * アイテム合成可能リスト
+ *------------------------------------------
+ */
+int clif_skill_produce_mix_list(struct map_session_data *sd,int trigger)
+{
+ int i,c,view,fd;
+ nullpo_retr(0, sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd, MAX_SKILL_PRODUCE_DB * 8 + 8);
+ WFIFOW(fd, 0)=0x18d;
+
+ for(i=0,c=0;i<MAX_SKILL_PRODUCE_DB;i++){
+ if( skill_can_produce_mix(sd,skill_produce_db[i].nameid,trigger, 1) ){
+ if((view = itemdb_viewid(skill_produce_db[i].nameid)) > 0)
+ WFIFOW(fd,c*8+ 4)= view;
+ else
+ WFIFOW(fd,c*8+ 4)= skill_produce_db[i].nameid;
+ WFIFOW(fd,c*8+ 6)= 0x0012;
+ WFIFOL(fd,c*8+ 8)= sd->status.char_id;
+ c++;
+ }
+ }
+ WFIFOW(fd, 2)=c*8+8;
+ WFIFOSET(fd,WFIFOW(fd,2));
+ if(c > 0) sd->state.produce_flag = 1;
+ return 0;
+}
+
+/*==========================================
+ * Sends a status change packet to the object only, used for loading status changes. [Skotlex]
+ *------------------------------------------
+ */
+int clif_status_load(struct block_list *bl,int type, int flag)
+{
+ int fd;
+ if (type == SI_BLANK) //It shows nothing on the client...
+ return 0;
+
+ if (bl->type != BL_PC)
+ return 0;
+
+ fd = ((struct map_session_data*)bl)->fd;
+
+ WFIFOHEAD(fd,packet_len_table[0x196]);
+ WFIFOW(fd,0)=0x0196;
+ WFIFOW(fd,2)=type;
+ WFIFOL(fd,4)=bl->id;
+ WFIFOB(fd,8)=flag; //Status start
+ WFIFOSET(fd, packet_len_table[0x196]);
+ return 0;
+}
+/*==========================================
+ * 状態異常アイコン/メッセージ表示
+ *------------------------------------------
+ */
+int clif_status_change(struct block_list *bl,int type,int flag)
+{
+ unsigned char buf[16];
+
+ if (type == SI_BLANK) //It shows nothing on the client...
+ return 0;
+
+ nullpo_retr(0, bl);
+
+ WBUFW(buf,0)=0x0196;
+ WBUFW(buf,2)=type;
+ WBUFL(buf,4)=bl->id;
+ WBUFB(buf,8)=flag;
+ clif_send(buf,packet_len_table[0x196],bl,AREA);
+ return 0;
+}
+
+/*==========================================
+ * Send message (modified by [Yor])
+ *------------------------------------------
+ */
+int clif_displaymessage(const int fd, char* mes)
+{
+ // invalid pointer?
+ nullpo_retr(-1, mes);
+
+ //Console [Wizputer] //Scrapped, as these are shared by disconnected players =X [Skotlex]
+ if (fd == 0)
+ return 0;
+ else {
+ int len_mes = strlen(mes);
+
+ if (len_mes > 0) { // don't send a void message (it's not displaying on the client chat). @help can send void line.
+ WFIFOHEAD(fd, 5 + len_mes);
+ WFIFOW(fd,0) = 0x8e;
+ WFIFOW(fd,2) = 5 + len_mes; // 4 + len + NULL teminate
+ memcpy(WFIFOP(fd,4), mes, len_mes + 1);
+ WFIFOSET(fd, 5 + len_mes);
+ }
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * 天の声を送信する
+ *------------------------------------------
+ */
+int clif_GMmessage(struct block_list *bl, char* mes, int len, int flag)
+{
+ unsigned char *buf;
+ int lp;
+
+ lp = (flag & 0x10) ? 8 : 4;
+ buf = (unsigned char*)aCallocA(len + lp + 8, sizeof(unsigned char));
+
+ WBUFW(buf,0) = 0x9a;
+ WBUFW(buf,2) = len + lp;
+ WBUFL(buf,4) = 0x65756c62;
+ memcpy(WBUFP(buf,lp), mes, len);
+ flag &= 0x07;
+ clif_send(buf, WBUFW(buf,2), bl,
+ (flag == 1) ? ALL_SAMEMAP :
+ (flag == 2) ? AREA :
+ (flag == 3) ? SELF :
+ ALL_CLIENT);
+ if(buf) aFree(buf);
+
+ return 0;
+}
+
+/*==========================================
+ * グローバルメッセージ
+ *------------------------------------------
+ */
+void clif_GlobalMessage(struct block_list *bl,char *message)
+{
+ char buf[100];
+ int len;
+
+ nullpo_retv(bl);
+
+ if(!message)
+ return;
+
+ len = strlen(message)+1;
+
+ WBUFW(buf,0)=0x8d;
+ WBUFW(buf,2)=len+8;
+ WBUFL(buf,4)=bl->id;
+ strncpy((char *) WBUFP(buf,8),message,len);
+ clif_send((unsigned char *) buf,WBUFW(buf,2),bl,AREA_CHAT_WOC);
+}
+
+/*==========================================
+ * Send main chat message [LuzZza]
+ *------------------------------------------
+ */
+void clif_MainChatMessage(char* message) {
+
+ char buf[100];
+ int len;
+
+ if(!message)
+ return;
+
+ len = strlen(message)+1;
+
+ WBUFW(buf,0)=0x8d;
+ WBUFW(buf,2)=len+8;
+ WBUFL(buf,4)=0;
+ strncpy((char *) WBUFP(buf,8),message,len);
+ clif_send((unsigned char *) buf,WBUFW(buf,2),NULL,CHAT_MAINCHAT);
+}
+
+/*==========================================
+ * Does an announce message in the given color.
+ *------------------------------------------
+ */
+int clif_announce(struct block_list *bl, char* mes, int len, unsigned long color, int flag)
+{
+ unsigned char *buf;
+ buf = (unsigned char*)aCallocA(len + 16, sizeof(unsigned char));
+ WBUFW(buf,0) = 0x1c3;
+ WBUFW(buf,2) = len + 16;
+ WBUFL(buf,4) = color;
+ WBUFW(buf,8) = 0x190; //Font style? Type?
+ WBUFW(buf,10) = 0x0c; //12? Font size?
+ WBUFL(buf,12) = 0; //Unknown!
+ memcpy(WBUFP(buf,16), mes, len);
+
+ flag &= 0x07;
+ clif_send(buf, WBUFW(buf,2), bl,
+ (flag == 1) ? ALL_SAMEMAP :
+ (flag == 2) ? AREA :
+ (flag == 3) ? SELF :
+ ALL_CLIENT);
+
+ if(buf) aFree(buf);
+ return 0;
+}
+/*==========================================
+ * HPSP回復エフェクトを送信する
+ *------------------------------------------
+ */
+int clif_heal(int fd,int type,int val)
+{
+ WFIFOHEAD(fd,packet_len_table[0x13d]);
+ WFIFOW(fd,0)=0x13d;
+ WFIFOW(fd,2)=type;
+ WFIFOW(fd,4)=val;
+ WFIFOSET(fd,packet_len_table[0x13d]);
+
+ return 0;
+}
+
+/*==========================================
+ * 復活する
+ *------------------------------------------
+ */
+int clif_resurrection(struct block_list *bl,int type)
+{
+ unsigned char buf[16];
+
+ nullpo_retr(0, bl);
+
+ WBUFW(buf,0)=0x148;
+ WBUFL(buf,2)=bl->id;
+ WBUFW(buf,6)=type;
+
+ clif_send(buf,packet_len_table[0x148],bl,type==1 ? AREA : AREA_WOS);
+
+ if(bl->type==BL_PC && ((struct map_session_data *)bl)->disguise)
+ clif_spawnpc(((struct map_session_data *)bl));
+
+ return 0;
+}
+
+/*==========================================
+ * PVP実装?(仮)
+ *------------------------------------------
+ */
+int clif_set0199(int fd,int type)
+{
+ WFIFOHEAD(fd,packet_len_table[0x199]);
+ WFIFOW(fd,0)=0x199;
+ WFIFOW(fd,2)=type;
+ WFIFOSET(fd,packet_len_table[0x199]);
+
+ return 0;
+}
+
+/*==========================================
+ * PVP実装?(仮)
+ *------------------------------------------
+ */
+int clif_pvpset(struct map_session_data *sd,int pvprank,int pvpnum,int type)
+{
+ nullpo_retr(0, sd);
+
+ if(map[sd->bl.m].flag.nopvp)
+ return 0;
+
+ if(type == 2) {
+ WFIFOHEAD(sd->fd,packet_len_table[0x19a]);
+ WFIFOW(sd->fd,0) = 0x19a;
+ WFIFOL(sd->fd,2) = sd->bl.id;
+ if(pvprank<=0)
+ pc_calc_pvprank(sd);
+ WFIFOL(sd->fd,6) = pvprank;
+ WFIFOL(sd->fd,10) = pvpnum;
+ WFIFOSET(sd->fd,packet_len_table[0x19a]);
+ } else {
+ unsigned char buf[32];
+
+ WBUFW(buf,0) = 0x19a;
+ WBUFL(buf,2) = sd->bl.id;
+ if(sd->status.option&0x46)
+ // WTF? a -1 to an unsigned value...
+ WBUFL(buf,6) = 0xFFFFFFFF;
+ else
+ if(pvprank<=0)
+ pc_calc_pvprank(sd);
+ WBUFL(buf,6) = pvprank;
+ WBUFL(buf,10) = pvpnum;
+ if(!type)
+ clif_send(buf,packet_len_table[0x19a],&sd->bl,AREA);
+ else
+ clif_send(buf,packet_len_table[0x19a],&sd->bl,ALL_SAMEMAP);
+ }
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int clif_send0199(int map,int type)
+{
+ struct block_list bl;
+ unsigned char buf[16];
+
+ bl.m = map;
+ WBUFW(buf,0)=0x199;
+ WBUFW(buf,2)=type;
+ clif_send(buf,packet_len_table[0x199],&bl,ALL_SAMEMAP);
+
+ return 0;
+}
+
+/*==========================================
+ * 精錬エフェクトを送信する
+ *------------------------------------------
+ */
+int clif_refine(int fd,struct map_session_data *sd,int fail,int index,int val)
+{
+ WFIFOHEAD(fd,packet_len_table[0x188]);
+ WFIFOW(fd,0)=0x188;
+ WFIFOW(fd,2)=fail;
+ WFIFOW(fd,4)=index+2;
+ WFIFOW(fd,6)=val;
+ WFIFOSET(fd,packet_len_table[0x188]);
+
+ return 0;
+}
+
+/*==========================================
+ * Wisp/page is transmitted to the destination player
+ *------------------------------------------
+ */
+int clif_wis_message(int fd, char *nick, char *mes, int mes_len) // R 0097 <len>.w <nick>.24B <message>.?B
+{
+// printf("clif_wis_message(%d, %s, %s)\n", fd, nick, mes);
+
+ WFIFOHEAD(fd, mes_len + NAME_LENGTH + 4);
+ WFIFOW(fd,0) = 0x97;
+ WFIFOW(fd,2) = mes_len + NAME_LENGTH + 4;
+ memcpy(WFIFOP(fd,4), nick, NAME_LENGTH);
+ memcpy(WFIFOP(fd,28), mes, mes_len);
+ WFIFOSET(fd,WFIFOW(fd,2));
+ return 0;
+}
+
+/*==========================================
+ * The transmission result of Wisp/page is transmitted to the source player
+ *------------------------------------------
+ */
+int clif_wis_end(int fd, int flag) // R 0098 <type>.B: 0: success to send wisper, 1: target character is not loged in, 2: ignored by target, 3: everyone ignored by target
+{
+ WFIFOHEAD(fd,packet_len_table[0x98]);
+ WFIFOW(fd,0) = 0x98;
+ WFIFOW(fd,2) = flag;
+ WFIFOSET(fd,packet_len_table[0x98]);
+ return 0;
+}
+
+/*==========================================
+ * キャラID名前引き結果を送信する
+ *------------------------------------------
+ */
+int clif_solved_charname(struct map_session_data *sd,int char_id)
+{
+ char *p= map_charid2nick(char_id);
+ int fd;
+
+ nullpo_retr(0, sd);
+
+ fd=sd->fd;
+ if(p!=NULL){
+ WFIFOHEAD(fd,packet_len_table[0x194]);
+ WFIFOW(fd,0)=0x194;
+ WFIFOL(fd,2)=char_id;
+ memcpy(WFIFOP(fd,6), p, NAME_LENGTH);
+ WFIFOSET(fd,packet_len_table[0x194]);
+ }else{
+ map_reqchariddb(sd,char_id);
+ chrif_searchcharid(char_id);
+ }
+ return 0;
+}
+
+/*==========================================
+ * カードの挿入可能リストを返す
+ *------------------------------------------
+ */
+int clif_use_card(struct map_session_data *sd,int idx)
+{
+ int i,c,ep;
+ int fd=sd->fd;
+
+ nullpo_retr(0, sd);
+ if (idx < 0 || idx >= MAX_INVENTORY) //Crash-fix from bad packets.
+ return 0;
+
+ if (!sd->inventory_data[idx] || sd->inventory_data[idx]->type != 6)
+ return 0; //Avoid parsing invalid item indexes (no card/no item)
+
+ ep=sd->inventory_data[idx]->equip;
+ WFIFOHEAD(fd,MAX_INVENTORY * 2 + 4);
+ WFIFOW(fd,0)=0x017b;
+
+ for(i=c=0;i<MAX_INVENTORY;i++){
+ int j;
+
+ if(sd->inventory_data[i] == NULL)
+ continue;
+ if(sd->inventory_data[i]->type!=4 && sd->inventory_data[i]->type!=5) // 武器防具じゃない
+ continue;
+ if(sd->status.inventory[i].card[0]==0x00ff || sd->status.inventory[i].card[0]==(short)0xff00 || sd->status.inventory[i].card[0]==0x00fe)
+ continue;
+ if(sd->status.inventory[i].identify==0 ) // 未鑑定
+ continue;
+
+ if((sd->inventory_data[i]->equip&ep)==0) // 装備個所が違う
+ continue;
+ if(sd->inventory_data[i]->type==4 && ep==32) // 盾カードと両手武器
+ continue;
+ for(j=0;j<sd->inventory_data[i]->slot;j++){
+ if( sd->status.inventory[i].card[j]==0 )
+ break;
+ }
+ if(j==sd->inventory_data[i]->slot) // すでにカードが一杯
+ continue;
+
+ WFIFOW(fd,4+c*2)=i+2;
+ c++;
+ }
+ WFIFOW(fd,2)=4+c*2;
+ WFIFOSET(fd,WFIFOW(fd,2));
+
+ return 0;
+}
+/*==========================================
+ * カードの挿入終了
+ *------------------------------------------
+ */
+int clif_insert_card(struct map_session_data *sd,int idx_equip,int idx_card,int flag)
+{
+ int fd;
+
+ nullpo_retr(0, sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,packet_len_table[0x17d]);
+ WFIFOW(fd,0)=0x17d;
+ WFIFOW(fd,2)=idx_equip+2;
+ WFIFOW(fd,4)=idx_card+2;
+ WFIFOB(fd,6)=flag;
+ WFIFOSET(fd,packet_len_table[0x17d]);
+ return 0;
+}
+
+/*==========================================
+ * 鑑定可能アイテムリスト送信
+ *------------------------------------------
+ */
+int clif_item_identify_list(struct map_session_data *sd)
+{
+ int i,c;
+ int fd;
+
+ nullpo_retr(0, sd);
+
+ fd=sd->fd;
+
+ WFIFOHEAD(fd,MAX_INVENTORY * 2 + 4);
+ WFIFOW(fd,0)=0x177;
+ for(i=c=0;i<MAX_INVENTORY;i++){
+ if(sd->status.inventory[i].nameid > 0 && sd->status.inventory[i].identify!=1){
+ WFIFOW(fd,c*2+4)=i+2;
+ c++;
+ }
+ }
+ if(c > 0) {
+ WFIFOW(fd,2)=c*2+4;
+ WFIFOSET(fd,WFIFOW(fd,2));
+ }
+ return 0;
+}
+
+/*==========================================
+ * 鑑定結果
+ *------------------------------------------
+ */
+int clif_item_identified(struct map_session_data *sd,int idx,int flag)
+{
+ int fd;
+
+ nullpo_retr(0, sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,packet_len_table[0x179]);
+ WFIFOW(fd, 0)=0x179;
+ WFIFOW(fd, 2)=idx+2;
+ WFIFOB(fd, 4)=flag;
+ WFIFOSET(fd,packet_len_table[0x179]);
+ return 0;
+}
+
+/*==========================================
+ * 修理可能アイテムリスト送信
+ *------------------------------------------
+ */
+int clif_item_repair_list(struct map_session_data *sd,struct map_session_data *dstsd)
+{
+ int i,c;
+ int fd;
+ int nameid;
+
+ nullpo_retr(0, sd);
+ nullpo_retr(0, dstsd);
+
+ fd=sd->fd;
+
+ WFIFOHEAD(fd, MAX_INVENTORY * 13 + 4);
+ WFIFOW(fd,0)=0x1fc;
+ for(i=c=0;i<MAX_INVENTORY;i++){
+ if((nameid=dstsd->status.inventory[i].nameid) > 0 && dstsd->status.inventory[i].attribute!=0){// && skill_can_repair(sd,nameid)){
+ WFIFOW(fd,c*13+4) = i;
+ WFIFOW(fd,c*13+6) = nameid;
+ WFIFOL(fd,c*13+8) = sd->status.char_id;
+ WFIFOL(fd,c*13+12)= dstsd->status.char_id;
+ WFIFOB(fd,c*13+16)= c;
+ c++;
+ }
+ }
+ if(c > 0) {
+ WFIFOW(fd,2)=c*13+4;
+ WFIFOSET(fd,WFIFOW(fd,2));
+ sd->state.produce_flag = 1;
+ sd->repair_target=dstsd;
+ }else
+ clif_skill_fail(sd,sd->skillid,0,0);
+
+ return 0;
+}
+int clif_item_repaireffect(struct map_session_data *sd,int nameid,int flag)
+{
+ int view,fd;
+
+ nullpo_retr(0, sd);
+ fd=sd->fd;
+
+ WFIFOHEAD(fd,packet_len_table[0x1fe]);
+ WFIFOW(fd, 0)=0x1fe;
+ if((view = itemdb_viewid(nameid)) > 0)
+ WFIFOW(fd, 2)=view;
+ else
+ WFIFOW(fd, 2)=nameid;
+ WFIFOB(fd, 4)=flag;
+ WFIFOSET(fd,packet_len_table[0x1fe]);
+
+ return 0;
+}
+
+/*==========================================
+ * Weapon Refining - Taken from jAthena
+ *------------------------------------------
+ */
+int clif_item_refine_list(struct map_session_data *sd)
+{
+ int i,c;
+ int fd;
+ int skilllv;
+ int wlv;
+ int refine_item[5];
+
+ nullpo_retr(0, sd);
+
+ skilllv = pc_checkskill(sd,WS_WEAPONREFINE);
+
+ fd=sd->fd;
+
+ refine_item[0] = -1;
+ refine_item[1] = pc_search_inventory(sd,1010);
+ refine_item[2] = pc_search_inventory(sd,1011);
+ refine_item[3] = refine_item[4] = pc_search_inventory(sd,984);
+
+ WFIFOHEAD(fd, MAX_INVENTORY * 13 + 4);
+ WFIFOW(fd,0)=0x221;
+ for(i=c=0;i<MAX_INVENTORY;i++){
+ if(sd->status.inventory[i].nameid > 0 && sd->status.inventory[i].refine < skilllv &&
+ sd->status.inventory[i].identify==1 && (wlv=itemdb_wlv(sd->status.inventory[i].nameid)) >=1 &&
+ refine_item[wlv]!=-1 && !(sd->status.inventory[i].equip&0x0022)){
+ WFIFOW(fd,c*13+ 4)=i+2;
+ WFIFOW(fd,c*13+ 6)=sd->status.inventory[i].nameid;
+ WFIFOW(fd,c*13+ 8)=0; //TODO: Wonder what are these for? Perhaps ID of weapon's crafter if any?
+ WFIFOW(fd,c*13+10)=0;
+ WFIFOB(fd,c*13+12)=c;
+ c++;
+ }
+ }
+ WFIFOW(fd,2)=c*13+4;
+ WFIFOSET(fd,WFIFOW(fd,2));
+ sd->state.produce_flag = 1;
+
+ return 0;
+}
+
+/*==========================================
+ * アイテムによる一時的なスキル効果
+ *------------------------------------------
+ */
+int clif_item_skill(struct map_session_data *sd,int skillid,int skilllv,const char *name)
+{
+ int fd;
+
+ nullpo_retr(0, sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,packet_len_table[0x147]);
+ WFIFOW(fd, 0)=0x147;
+ WFIFOW(fd, 2)=skillid;
+ WFIFOW(fd, 4)=skill_get_inf(skillid);
+ WFIFOW(fd, 6)=0;
+ WFIFOW(fd, 8)=skilllv;
+ WFIFOW(fd,10)=skill_get_sp(skillid,skilllv);
+ WFIFOW(fd,12)=skill_get_range2(&sd->bl, skillid,skilllv);
+ strncpy((char*)WFIFOP(fd,14),name,NAME_LENGTH);
+ WFIFOB(fd,38)=0;
+ WFIFOSET(fd,packet_len_table[0x147]);
+ return 0;
+}
+
+/*==========================================
+ * カートにアイテム追加
+ *------------------------------------------
+ */
+int clif_cart_additem(struct map_session_data *sd,int n,int amount,int fail)
+{
+ int view,fd;
+ unsigned char *buf;
+
+ nullpo_retr(0, sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,packet_len_table[0x124]);
+ buf=WFIFOP(fd,0);
+ if(n<0 || n>=MAX_CART || sd->status.cart[n].nameid<=0)
+ return 1;
+
+ WBUFW(buf,0)=0x124;
+ WBUFW(buf,2)=n+2;
+ WBUFL(buf,4)=amount;
+ if((view = itemdb_viewid(sd->status.cart[n].nameid)) > 0)
+ WBUFW(buf,8)=view;
+ else
+ WBUFW(buf,8)=sd->status.cart[n].nameid;
+ WBUFB(buf,10)=sd->status.cart[n].identify;
+ WBUFB(buf,11)=sd->status.cart[n].attribute;
+ WBUFB(buf,12)=sd->status.cart[n].refine;
+ clif_addcards(WBUFP(buf,13), &sd->status.cart[n]);
+ WFIFOSET(fd,packet_len_table[0x124]);
+ return 0;
+}
+
+/*==========================================
+ * カートからアイテム削除
+ *------------------------------------------
+ */
+int clif_cart_delitem(struct map_session_data *sd,int n,int amount)
+{
+ int fd;
+
+ nullpo_retr(0, sd);
+
+ fd=sd->fd;
+
+ WFIFOHEAD(fd,packet_len_table[0x125]);
+ WFIFOW(fd,0)=0x125;
+ WFIFOW(fd,2)=n+2;
+ WFIFOL(fd,4)=amount;
+
+ WFIFOSET(fd,packet_len_table[0x125]);
+
+ return 0;
+}
+
+/*==========================================
+ * カートのアイテムリスト
+ *------------------------------------------
+ */
+int clif_cart_itemlist(struct map_session_data *sd)
+{
+ struct item_data *id;
+ int i,n,fd;
+ unsigned char *buf;
+
+ nullpo_retr(0, sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd, MAX_CART * 18 + 4);
+ buf = WFIFOP(fd,0);
+#if PACKETVER < 5
+ for(i=0,n=0;i<MAX_CART;i++){
+ if(sd->status.cart[i].nameid<=0)
+ continue;
+ id = itemdb_search(sd->status.cart[i].nameid);
+ if(itemdb_isequip2(id))
+ continue;
+ WBUFW(buf,n*10+4)=i+2;
+ if(id->view_id > 0)
+ WBUFW(buf,n*10+6)=id->view_id;
+ else
+ WBUFW(buf,n*10+6)=sd->status.cart[i].nameid;
+ WBUFB(buf,n*10+8)=itemtype(id->type);
+ WBUFB(buf,n*10+9)=sd->status.cart[i].identify;
+ WBUFW(buf,n*10+10)=sd->status.cart[i].amount;
+ WBUFW(buf,n*10+12)=0;
+ n++;
+ }
+ if(n){
+ WBUFW(buf,0)=0x123;
+ WBUFW(buf,2)=4+n*10;
+ WFIFOSET(fd,WFIFOW(fd,2));
+ }
+#else
+ for(i=0,n=0;i<MAX_CART;i++){
+ if(sd->status.cart[i].nameid<=0)
+ continue;
+ id = itemdb_search(sd->status.cart[i].nameid);
+ if(itemdb_isequip2(id))
+ continue;
+ WBUFW(buf,n*18+4)=i+2;
+ if(id->view_id > 0)
+ WBUFW(buf,n*18+6)=id->view_id;
+ else
+ WBUFW(buf,n*18+6)=sd->status.cart[i].nameid;
+ WBUFB(buf,n*18+8)=itemtype(id->type);
+ WBUFB(buf,n*18+9)=sd->status.cart[i].identify;
+ WBUFW(buf,n*18+10)=sd->status.cart[i].amount;
+ WBUFW(buf,n*18+12)=0; //Here goes the equip location, which seems unnecessary to fill for the cart data.
+ clif_addcards(WBUFP(buf,n*18+14), &sd->status.cart[i]);
+ n++;
+ }
+ if(n){
+ WBUFW(buf,0)=0x1ef;
+ WBUFW(buf,2)=4+n*18;
+ WFIFOSET(fd,WFIFOW(fd,2));
+ }
+#endif
+ return 0;
+}
+
+/*==========================================
+ * カートの装備品リスト
+ *------------------------------------------
+ */
+int clif_cart_equiplist(struct map_session_data *sd)
+{
+ struct item_data *id;
+ int i,n,fd;
+ unsigned char *buf;
+
+ nullpo_retr(0, sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd, MAX_INVENTORY * 20 + 4);
+ buf = WFIFOP(fd,0);
+
+ for(i=0,n=0;i<MAX_INVENTORY;i++){
+ if(sd->status.cart[i].nameid<=0)
+ continue;
+ id = itemdb_search(sd->status.cart[i].nameid);
+ if(!itemdb_isequip2(id))
+ continue;
+ WBUFW(buf,n*20+4)=i+2;
+ if(id->view_id > 0)
+ WBUFW(buf,n*20+6)=id->view_id;
+ else
+ WBUFW(buf,n*20+6)=sd->status.cart[i].nameid;
+ WBUFB(buf,n*20+8)=itemtype(id->type);
+ WBUFB(buf,n*20+9)=sd->status.cart[i].identify;
+ WBUFW(buf,n*20+10)=id->equip;
+ WBUFW(buf,n*20+12)=sd->status.cart[i].equip;
+ WBUFB(buf,n*20+14)=sd->status.cart[i].attribute;
+ WBUFB(buf,n*20+15)=sd->status.cart[i].refine;
+ clif_addcards(WBUFP(buf, n*20+16), &sd->status.cart[i]);
+ n++;
+ }
+ if(n){
+ WBUFW(buf,0)=0x122;
+ WBUFW(buf,2)=4+n*20;
+ WFIFOSET(fd,WFIFOW(fd,2));
+ }
+ return 0;
+}
+
+/*==========================================
+ * 露店開設
+ *------------------------------------------
+ */
+int clif_openvendingreq(struct map_session_data *sd,int num)
+{
+ int fd;
+
+ nullpo_retr(0, sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,packet_len_table[0x12d]);
+ WFIFOW(fd,0)=0x12d;
+ WFIFOW(fd,2)=num;
+ WFIFOSET(fd,packet_len_table[0x12d]);
+
+ return 0;
+}
+
+/*==========================================
+ * 露店看板表示
+ *------------------------------------------
+ */
+int clif_showvendingboard(struct block_list* bl,char *message,int fd)
+{
+ unsigned char buf[128];
+
+ nullpo_retr(0, bl);
+
+ WBUFW(buf,0)=0x131;
+ WBUFL(buf,2)=bl->id;
+ strncpy((char*)WBUFP(buf,6),message,80);
+ if(fd){
+ WFIFOHEAD(fd,packet_len_table[0x131]);
+ memcpy(WFIFOP(fd,0),buf,packet_len_table[0x131]);
+ WFIFOSET(fd,packet_len_table[0x131]);
+ }else{
+ clif_send(buf,packet_len_table[0x131],bl,AREA_WOS);
+ }
+ return 0;
+}
+
+/*==========================================
+ * 露店看板消去
+ *------------------------------------------
+ */
+int clif_closevendingboard(struct block_list* bl,int fd)
+{
+ unsigned char buf[16];
+
+ nullpo_retr(0, bl);
+
+ WBUFW(buf,0)=0x132;
+ WBUFL(buf,2)=bl->id;
+ if(fd){
+ WFIFOHEAD(fd,packet_len_table[0x132]);
+ memcpy(WFIFOP(fd,0),buf,packet_len_table[0x132]);
+ WFIFOSET(fd,packet_len_table[0x132]);
+ }else{
+ clif_send(buf,packet_len_table[0x132],bl,AREA_WOS);
+ }
+
+ return 0;
+}
+/*==========================================
+ * 露店アイテムリスト
+ *------------------------------------------
+ */
+int clif_vendinglist(struct map_session_data *sd,int id,struct vending *vending)
+{
+ struct item_data *data;
+ int i,n,index,fd;
+ struct map_session_data *vsd;
+ unsigned char *buf;
+
+ nullpo_retr(0, sd);
+ nullpo_retr(0, vending);
+ nullpo_retr(0, vsd=map_id2sd(id));
+
+ fd=sd->fd;
+ WFIFOHEAD(fd, 8+vsd->vend_num*22);
+ buf = WFIFOP(fd,0);
+ for(i=0,n=0;i<vsd->vend_num;i++){
+ if(vending[i].amount<=0)
+ continue;
+ WBUFL(buf,8+n*22)=vending[i].value;
+ WBUFW(buf,12+n*22)=vending[i].amount;
+ WBUFW(buf,14+n*22)=(index=vending[i].index)+2;
+ if(vsd->status.cart[index].nameid <= 0 || vsd->status.cart[index].amount <= 0)
+ continue;
+ data = itemdb_search(vsd->status.cart[index].nameid);
+ WBUFB(buf,16+n*22)=itemtype(data->type);
+ if(data->view_id > 0)
+ WBUFW(buf,17+n*22)=data->view_id;
+ else
+ WBUFW(buf,17+n*22)=vsd->status.cart[index].nameid;
+ WBUFB(buf,19+n*22)=vsd->status.cart[index].identify;
+ WBUFB(buf,20+n*22)=vsd->status.cart[index].attribute;
+ WBUFB(buf,21+n*22)=vsd->status.cart[index].refine;
+ clif_addcards(WBUFP(buf, 22+n*22), &vsd->status.cart[index]);
+ n++;
+ }
+ if(n > 0){
+ WBUFW(buf,0)=0x133;
+ WBUFW(buf,2)=8+n*22;
+ WBUFL(buf,4)=id;
+ WFIFOSET(fd,WFIFOW(fd,2));
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * 露店アイテム購入失敗
+ *------------------------------------------
+*/
+int clif_buyvending(struct map_session_data *sd,int index,int amount,int fail)
+{
+ int fd;
+
+ nullpo_retr(0, sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,packet_len_table[0x135]);
+ WFIFOW(fd,0)=0x135;
+ WFIFOW(fd,2)=index+2;
+ WFIFOW(fd,4)=amount;
+ WFIFOB(fd,6)=fail;
+ WFIFOSET(fd,packet_len_table[0x135]);
+
+ return 0;
+}
+
+/*==========================================
+ * 露店開設成功
+ *------------------------------------------
+*/
+int clif_openvending(struct map_session_data *sd,int id,struct vending *vending)
+{
+ struct item_data *data;
+ int i,n,index,fd;
+ unsigned char *buf;
+
+ nullpo_retr(0, sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd, 8+sd->vend_num*22);
+ buf = WFIFOP(fd,0);
+ for(i=0,n=0;i<sd->vend_num;i++){
+ if (sd->vend_num > 2+pc_checkskill(sd,MC_VENDING)) return 0;
+ WBUFL(buf,8+n*22)=vending[i].value;
+ WBUFW(buf,12+n*22)=(index=vending[i].index)+2;
+ WBUFW(buf,14+n*22)=vending[i].amount;
+ if(sd->status.cart[index].nameid <= 0 || sd->status.cart[index].amount <= 0 || sd->status.cart[index].identify==0 ||
+ sd->status.cart[index].attribute==1) // Prevent unidentified and broken items from being sold [Valaris]
+ continue;
+ data = itemdb_search(sd->status.cart[index].nameid);
+ WBUFB(buf,16+n*22)=itemtype(data->type);
+ if(data->view_id > 0)
+ WBUFW(buf,17+n*22)=data->view_id;
+ else
+ WBUFW(buf,17+n*22)=sd->status.cart[index].nameid;
+ WBUFB(buf,19+n*22)=sd->status.cart[index].identify;
+ WBUFB(buf,20+n*22)=sd->status.cart[index].attribute;
+ WBUFB(buf,21+n*22)=sd->status.cart[index].refine;
+ clif_addcards(WBUFP(buf, 22+n*22), &sd->status.cart[index]);
+ n++;
+ }
+ if(n > 0){
+ WBUFW(buf,0)=0x136;
+ WBUFW(buf,2)=8+n*22;
+ WBUFL(buf,4)=id;
+ WFIFOSET(fd,WFIFOW(fd,2));
+ }
+ return n;
+}
+
+/*==========================================
+ * 露店アイテム販売報告
+ *------------------------------------------
+*/
+int clif_vendingreport(struct map_session_data *sd,int index,int amount)
+{
+ int fd;
+
+ nullpo_retr(0, sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,packet_len_table[0x137]);
+ WFIFOW(fd,0)=0x137;
+ WFIFOW(fd,2)=index+2;
+ WFIFOW(fd,4)=amount;
+ WFIFOSET(fd,packet_len_table[0x137]);
+
+ return 0;
+}
+/*==========================================
+ * パーティ作成完了
+ *------------------------------------------
+ */
+int clif_party_created(struct map_session_data *sd,int flag)
+{
+ int fd;
+
+ // printf("clif_party_message(%s, %d, %s)\n", p->name, account_id, mes);
+
+ nullpo_retr(0, sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,packet_len_table[0xfa]);
+ WFIFOW(fd,0)=0xfa;
+ WFIFOB(fd,2)=flag;
+ WFIFOSET(fd,packet_len_table[0xfa]);
+ return 0;
+}
+
+int clif_party_main_info(struct party *p, int fd)
+{
+ struct map_session_data *sd;
+ int i;
+ unsigned char buf[96];
+
+ for (i=0; i<MAX_PARTY && !p->member[i].leader; i++);
+ if (i >= MAX_PARTY) return 0; //Should never happen...
+ sd = p->member[i].sd;
+ WBUFW(buf,0)=0x1e9;
+ WBUFL(buf,2)= p->member[i].account_id;
+ WBUFL(buf,6)= 0; //We don't know yet what this long is about.
+ WBUFW(buf,10)=sd?sd->bl.x:0;
+ WBUFW(buf,12)=sd?sd->bl.y:0;
+ WBUFB(buf,14)=(p->member[i].online)?0:1; //This byte is also unconfirmed...
+ memcpy(WBUFP(buf,15), p->name, NAME_LENGTH);
+ memcpy(WBUFP(buf,39), p->member[i].name, NAME_LENGTH);
+ memcpy(WBUFP(buf,63), mapindex_id2name(p->member[i].map), MAP_NAME_LENGTH);
+ WBUFB(buf,79) = (p->item&1)?1:0;
+ WBUFB(buf,80) = (p->item&2)?1:0;
+ if(fd>=0){
+ WFIFOHEAD(fd,packet_len_table[0x1e9]);
+ memcpy(WFIFOP(fd,0),buf,packet_len_table[0x1e9]);
+ WFIFOSET(fd,packet_len_table[0x1e9]);
+ return 1;
+ }
+ if (!sd) {
+ for (i=0; i<MAX_PARTY && !p->member[i].sd; i++)
+ if (i >= MAX_PARTY) return 0; //Should never happen...
+ sd=p->member[i].sd;
+ }
+ clif_send(buf,packet_len_table[0x1e9],&sd->bl,PARTY);
+ return 1;
+}
+
+/*==========================================
+ * パーティ情報送信
+ *------------------------------------------
+ */
+int clif_party_info(struct party *p,int fd)
+{
+ unsigned char buf[1024];
+ int i,c;
+ struct map_session_data *sd=NULL;
+
+ nullpo_retr(0, p);
+
+ WBUFW(buf,0)=0xfb;
+ memcpy(WBUFP(buf,4),p->name,NAME_LENGTH);
+ for(i=c=0;i<MAX_PARTY;i++){
+ struct party_member *m=&p->member[i];
+ if(m->account_id>0){
+ if(sd==NULL) sd=m->sd;
+ WBUFL(buf,28+c*46)=m->account_id;
+ memcpy(WBUFP(buf,28+c*46+ 4),m->name,NAME_LENGTH);
+ memcpy(WBUFP(buf,28+c*46+28),mapindex_id2name(m->map),MAP_NAME_LENGTH);
+ WBUFB(buf,28+c*46+44)=(m->leader)?0:1;
+ WBUFB(buf,28+c*46+45)=(m->online)?0:1;
+ c++;
+ }
+ }
+ WBUFW(buf,2)=28+c*46;
+ if(fd>=0){ // fdが設定されてるならそれに送る
+ WFIFOHEAD(fd, 28+c*46);
+ memcpy(WFIFOP(fd,0),buf,WBUFW(buf,2));
+ WFIFOSET(fd,WFIFOW(fd,2));
+ return 9;
+ }
+ if(sd!=NULL)
+ clif_send(buf,WBUFW(buf,2),&sd->bl,PARTY);
+ return 0;
+}
+/*==========================================
+ * パーティ勧誘
+ *------------------------------------------
+ */
+int clif_party_invite(struct map_session_data *sd,struct map_session_data *tsd)
+{
+ int fd;
+ struct party *p;
+
+ nullpo_retr(0, sd);
+ nullpo_retr(0, tsd);
+
+ fd=tsd->fd;
+
+ if( (p=party_search(sd->status.party_id))==NULL )
+ return 0;
+
+ WFIFOHEAD(fd,packet_len_table[0xfe]);
+ WFIFOW(fd,0)=0xfe;
+ WFIFOL(fd,2)=sd->status.account_id;
+ memcpy(WFIFOP(fd,6),p->name,NAME_LENGTH);
+ WFIFOSET(fd,packet_len_table[0xfe]);
+ return 0;
+}
+
+/*==========================================
+ * パーティ勧誘結果
+ *------------------------------------------
+ */
+int clif_party_inviteack(struct map_session_data *sd,char *nick,int flag)
+{
+ int fd;
+
+ nullpo_retr(0, sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,packet_len_table[0xfd]);
+ WFIFOW(fd,0)=0xfd;
+ memcpy(WFIFOP(fd,2),nick,NAME_LENGTH);
+ WFIFOB(fd,26)=flag;
+ WFIFOSET(fd,packet_len_table[0xfd]);
+ return 0;
+}
+
+/*==========================================
+ * パーティ設定送信
+ * flag & 0x001=exp変更ミス
+ * 0x010=item変更ミス
+ * 0x100=一人にのみ送信
+ *------------------------------------------
+ */
+int clif_party_option(struct party *p,struct map_session_data *sd,int flag)
+{
+ unsigned char buf[16];
+
+ nullpo_retr(0, p);
+
+// if(battle_config.etc_log)
+// printf("clif_party_option: %d %d %d\n",p->exp,p->item,flag);
+ if(sd==NULL && flag==0){
+ int i;
+ for(i=0;i<MAX_PARTY;i++)
+ if((sd=map_id2sd(p->member[i].account_id))!=NULL)
+ break;
+ }
+ if(sd==NULL)
+ return 0;
+ WBUFW(buf,0)=0x101;
+ WBUFW(buf,2)=((flag&0x01)?2:p->exp);
+ WBUFW(buf,4)=0; //NOTE: We don't know yet what this is for, it is NOT for item share rules, though. [Skotlex]
+ if(flag==0)
+ clif_send(buf,packet_len_table[0x101],&sd->bl,PARTY);
+ else {
+ WFIFOHEAD(sd->fd,packet_len_table[0x101]);
+ memcpy(WFIFOP(sd->fd,0),buf,packet_len_table[0x101]);
+ WFIFOSET(sd->fd,packet_len_table[0x101]);
+ }
+ return 0;
+}
+/*==========================================
+ * パーティ脱退(脱退前に呼ぶこと)
+ *------------------------------------------
+ */
+int clif_party_leaved(struct party *p,struct map_session_data *sd,int account_id,char *name,int flag)
+{
+ unsigned char buf[64];
+ int i;
+
+ nullpo_retr(0, p);
+
+ WBUFW(buf,0)=0x105;
+ WBUFL(buf,2)=account_id;
+ memcpy(WBUFP(buf,6),name,NAME_LENGTH);
+ WBUFB(buf,30)=flag&0x0f;
+
+ if((flag&0xf0)==0){
+ if(sd==NULL)
+ for(i=0;i<MAX_PARTY;i++)
+ if((sd=p->member[i].sd)!=NULL)
+ break;
+ if (sd!=NULL)
+ clif_send(buf,packet_len_table[0x105],&sd->bl,PARTY);
+ } else if (sd!=NULL) {
+ WFIFOHEAD(sd->fd,packet_len_table[0x105]);
+ memcpy(WFIFOP(sd->fd,0),buf,packet_len_table[0x105]);
+ WFIFOSET(sd->fd,packet_len_table[0x105]);
+ }
+ return 0;
+}
+/*==========================================
+ * パーティメッセージ送信
+ *------------------------------------------
+ */
+int clif_party_message(struct party *p,int account_id,char *mes,int len)
+{
+ struct map_session_data *sd;
+ int i;
+
+ nullpo_retr(0, p);
+
+ for(i=0;i<MAX_PARTY;i++){
+ if((sd=p->member[i].sd)!=NULL)
+ break;
+ }
+ if(sd!=NULL){
+ unsigned char buf[1024];
+ WBUFW(buf,0)=0x109;
+ WBUFW(buf,2)=len+8;
+ WBUFL(buf,4)=account_id;
+ memcpy(WBUFP(buf,8),mes,len);
+ clif_send(buf,len+8,&sd->bl,PARTY);
+ }
+ return 0;
+}
+/*==========================================
+ * パーティ座標通知
+ *------------------------------------------
+ */
+int clif_party_xy(struct map_session_data *sd)
+{
+ unsigned char buf[16];
+
+ nullpo_retr(0, sd);
+
+ WBUFW(buf,0)=0x107;
+ WBUFL(buf,2)=sd->status.account_id;
+ WBUFW(buf,6)=sd->bl.x;
+ WBUFW(buf,8)=sd->bl.y;
+ clif_send(buf,packet_len_table[0x107],&sd->bl,PARTY_SAMEMAP_WOS);
+
+ return 0;
+}
+/*==========================================
+ * パーティHP通知
+ *------------------------------------------
+ */
+int clif_party_hp(struct map_session_data *sd)
+{
+ unsigned char buf[16];
+
+ nullpo_retr(0, sd);
+
+ WBUFW(buf,0)=0x106;
+ WBUFL(buf,2)=sd->status.account_id;
+ WBUFW(buf,6)=(sd->status.hp > 0x7fff)? 0x7fff:sd->status.hp;
+ WBUFW(buf,8)=(sd->status.max_hp > 0x7fff)? 0x7fff:sd->status.max_hp;
+ clif_send(buf,packet_len_table[0x106],&sd->bl,PARTY_AREA_WOS);
+ return 0;
+}
+
+/*==========================================
+ * Sends HP bar to a single fd. [Skotlex]
+ *------------------------------------------
+ */
+static void clif_hpmeter_single(int fd, struct map_session_data *sd)
+{
+ WFIFOHEAD(fd,packet_len_table[0x106]);
+ WFIFOW(fd,0) = 0x106;
+ WFIFOL(fd,2) = sd->status.account_id;
+ WFIFOW(fd,6) = (sd->status.hp > 0x7fff) ? 0x7fff : sd->status.hp;
+ WFIFOW(fd,8) = (sd->status.max_hp > 0x7fff) ? 0x7fff : sd->status.max_hp;
+ WFIFOSET (fd, packet_len_table[0x106]);
+}
+
+/*==========================================
+ * GMへ場所とHP通知
+ *------------------------------------------
+ */
+int clif_hpmeter(struct map_session_data *sd)
+{
+ struct map_session_data *sd2;
+ unsigned char buf[16];
+ int i, x0, y0, x1, y1;
+ int level;
+
+ nullpo_retr(0, sd);
+
+ x0 = sd->bl.x - AREA_SIZE;
+ y0 = sd->bl.y - AREA_SIZE;
+ x1 = sd->bl.x + AREA_SIZE;
+ y1 = sd->bl.y + AREA_SIZE;
+
+ WBUFW(buf,0) = 0x106;
+ WBUFL(buf,2) = sd->status.account_id;
+ WBUFW(buf,6) = (sd->status.hp > 0x7fff) ? 0x7fff : sd->status.hp;
+ WBUFW(buf,8) = (sd->status.max_hp > 0x7fff) ? 0x7fff : sd->status.max_hp;
+ for (i = 0; i < fd_max; i++) {
+ if (session[i] && (sd2 = (struct map_session_data*)session[i]->session_data) && sd != sd2 && sd2->state.auth) {
+ if (sd2->bl.m != sd->bl.m ||
+ sd2->bl.x < x0 || sd2->bl.y < y0 ||
+ sd2->bl.x > x1 || sd2->bl.y > y1 ||
+ (level = pc_isGM(sd2)) < battle_config.disp_hpmeter ||
+ level < pc_isGM(sd))
+ continue;
+ WFIFOHEAD (i, packet_len_table[0x106]);
+ memcpy (WFIFOP(i,0), buf, packet_len_table[0x106]);
+ WFIFOSET (i, packet_len_table[0x106]);
+ }
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * パーティ場所移動(未使用)
+ *------------------------------------------
+ */
+int clif_party_move(struct party *p,struct map_session_data *sd,int online)
+{
+ unsigned char buf[128];
+
+ nullpo_retr(0, sd);
+ nullpo_retr(0, p);
+
+ WBUFW(buf, 0)=0x104;
+ WBUFL(buf, 2)=sd->status.account_id;
+ WBUFL(buf, 6)=0;
+ WBUFW(buf,10)=sd->bl.x;
+ WBUFW(buf,12)=sd->bl.y;
+ WBUFB(buf,14)=!online;
+ memcpy(WBUFP(buf,15),p->name, NAME_LENGTH);
+ memcpy(WBUFP(buf,39),sd->status.name, NAME_LENGTH);
+ memcpy(WBUFP(buf,63),map[sd->bl.m].name, MAP_NAME_LENGTH);
+ clif_send(buf,packet_len_table[0x104],&sd->bl,PARTY);
+ return 0;
+}
+/*==========================================
+ * 攻撃するために移動が必要
+ *------------------------------------------
+ */
+int clif_movetoattack(struct map_session_data *sd,struct block_list *bl)
+{
+ int fd;
+
+ nullpo_retr(0, sd);
+ nullpo_retr(0, bl);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,packet_len_table[0x139]);
+ WFIFOW(fd, 0)=0x139;
+ WFIFOL(fd, 2)=bl->id;
+ WFIFOW(fd, 6)=bl->x;
+ WFIFOW(fd, 8)=bl->y;
+ WFIFOW(fd,10)=sd->bl.x;
+ WFIFOW(fd,12)=sd->bl.y;
+ WFIFOW(fd,14)=sd->attackrange;
+ WFIFOSET(fd,packet_len_table[0x139]);
+ return 0;
+}
+/*==========================================
+ * 製造エフェクト
+ *------------------------------------------
+ */
+int clif_produceeffect(struct map_session_data *sd,int flag,int nameid)
+{
+ int view,fd;
+
+ nullpo_retr(0, sd);
+
+ fd=sd->fd;
+ // 名前の登録と送信を先にしておく
+ if( map_charid2nick(sd->status.char_id)==NULL )
+ map_addchariddb(sd->status.char_id,sd->status.name);
+ clif_solved_charname(sd,sd->status.char_id);
+
+ WFIFOHEAD(fd,packet_len_table[0x18f]);
+ WFIFOW(fd, 0)=0x18f;
+ WFIFOW(fd, 2)=flag;
+ if((view = itemdb_viewid(nameid)) > 0)
+ WFIFOW(fd, 4)=view;
+ else
+ WFIFOW(fd, 4)=nameid;
+ WFIFOSET(fd,packet_len_table[0x18f]);
+ return 0;
+}
+
+// pet
+int clif_catch_process(struct map_session_data *sd)
+{
+ int fd;
+
+ nullpo_retr(0, sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,packet_len_table[0x19e]);
+ WFIFOW(fd,0)=0x19e;
+ WFIFOSET(fd,packet_len_table[0x19e]);
+
+ return 0;
+}
+
+int clif_pet_rulet(struct map_session_data *sd,int data)
+{
+ int fd;
+
+ nullpo_retr(0, sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,packet_len_table[0x1a0]);
+ WFIFOW(fd,0)=0x1a0;
+ WFIFOB(fd,2)=data;
+ WFIFOSET(fd,packet_len_table[0x1a0]);
+
+ return 0;
+}
+
+/*==========================================
+ * pet卵リスト作成
+ *------------------------------------------
+ */
+int clif_sendegg(struct map_session_data *sd)
+{
+ //R 01a6 <len>.w <index>.w*
+ int i,n=0,fd;
+
+ nullpo_retr(0, sd);
+
+ fd=sd->fd;
+ if (battle_config.pet_no_gvg && map_flag_gvg(sd->bl.m))
+ { //Disable pet hatching in GvG grounds during Guild Wars [Skotlex]
+ clif_displaymessage(fd, "Pets are not allowed in Guild Wars.");
+ return 0;
+ }
+ WFIFOHEAD(fd, MAX_INVENTORY * 2 + 4);
+ WFIFOW(fd,0)=0x1a6;
+ if(sd->status.pet_id <= 0) {
+ for(i=0,n=0;i<MAX_INVENTORY;i++){
+ if(sd->status.inventory[i].nameid<=0 || sd->inventory_data[i] == NULL ||
+ sd->inventory_data[i]->type!=7 ||
+ sd->status.inventory[i].amount<=0)
+ continue;
+ WFIFOW(fd,n*2+4)=i+2;
+ n++;
+ }
+ }
+ WFIFOW(fd,2)=4+n*2;
+ WFIFOSET(fd,WFIFOW(fd,2));
+
+ return 0;
+}
+
+int clif_send_petdata(struct map_session_data *sd,int type,int param)
+{
+ int fd;
+
+ nullpo_retr(0, sd);
+ nullpo_retr(0, sd->pd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,packet_len_table[0x1a4]);
+ WFIFOW(fd,0)=0x1a4;
+ WFIFOB(fd,2)=type;
+ WFIFOL(fd,3)=sd->pd->bl.id;
+ WFIFOL(fd,7)=param;
+ WFIFOSET(fd,packet_len_table[0x1a4]);
+
+ return 0;
+}
+
+int clif_send_petstatus(struct map_session_data *sd)
+{
+ int fd;
+
+ nullpo_retr(0, sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,packet_len_table[0x1a2]);
+ WFIFOW(fd,0)=0x1a2;
+ memcpy(WFIFOP(fd,2),sd->pet.name,NAME_LENGTH);
+ WFIFOB(fd,26)=(battle_config.pet_rename == 1)? 0:sd->pet.rename_flag;
+ WFIFOW(fd,27)=sd->pet.level;
+ WFIFOW(fd,29)=sd->pet.hungry;
+ WFIFOW(fd,31)=sd->pet.intimate;
+ WFIFOW(fd,33)=sd->pet.equip;
+ WFIFOSET(fd,packet_len_table[0x1a2]);
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int clif_pet_emotion(struct pet_data *pd,int param)
+{
+ unsigned char buf[16];
+ struct map_session_data *sd;
+
+ nullpo_retr(0, pd);
+ nullpo_retr(0, sd = pd->msd);
+
+ memset(buf,0,packet_len_table[0x1aa]);
+
+ WBUFW(buf,0)=0x1aa;
+ WBUFL(buf,2)=pd->bl.id;
+ if(param >= 100 && sd->petDB->talk_convert_class) {
+ if(sd->petDB->talk_convert_class < 0)
+ return 0;
+ else if(sd->petDB->talk_convert_class > 0) {
+ param -= (pd->class_ - 100)*100;
+ param += (sd->petDB->talk_convert_class - 100)*100;
+ }
+ }
+ WBUFL(buf,6)=param;
+
+ clif_send(buf,packet_len_table[0x1aa],&pd->bl,AREA);
+
+ return 0;
+}
+
+int clif_pet_performance(struct block_list *bl,int param)
+{
+ unsigned char buf[16];
+
+ nullpo_retr(0, bl);
+
+ memset(buf,0,packet_len_table[0x1a4]);
+
+ WBUFW(buf,0)=0x1a4;
+ WBUFB(buf,2)=4;
+ WBUFL(buf,3)=bl->id;
+ WBUFL(buf,7)=param;
+
+ clif_send(buf,packet_len_table[0x1a4],bl,AREA);
+
+ return 0;
+}
+
+int clif_pet_equip(struct pet_data *pd,int nameid)
+{
+ unsigned char buf[16];
+ int view;
+
+ nullpo_retr(0, pd);
+
+ memset(buf,0,packet_len_table[0x1a4]);
+
+ WBUFW(buf,0)=0x1a4;
+ WBUFB(buf,2)=3;
+ WBUFL(buf,3)=pd->bl.id;
+ if((view = itemdb_viewid(nameid)) > 0)
+ WBUFL(buf,7)=view;
+ else
+ WBUFL(buf,7)=nameid;
+
+ clif_send(buf,packet_len_table[0x1a4],&pd->bl,AREA);
+
+ return 0;
+}
+
+int clif_pet_food(struct map_session_data *sd,int foodid,int fail)
+{
+ int fd;
+
+ nullpo_retr(0, sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,packet_len_table[0x1a3]);
+ WFIFOW(fd,0)=0x1a3;
+ WFIFOB(fd,2)=fail;
+ WFIFOW(fd,3)=foodid;
+ WFIFOSET(fd,packet_len_table[0x1a3]);
+
+ return 0;
+}
+
+/*==========================================
+ * オートスペル リスト送信
+ *------------------------------------------
+ */
+int clif_autospell(struct map_session_data *sd,int skilllv)
+{
+ int fd;
+
+ nullpo_retr(0, sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,packet_len_table[0x1cd]);
+ WFIFOW(fd, 0)=0x1cd;
+
+ if(skilllv>0 && pc_checkskill(sd,MG_NAPALMBEAT)>0)
+ WFIFOL(fd,2)= MG_NAPALMBEAT;
+ else
+ WFIFOL(fd,2)= 0x00000000;
+ if(skilllv>1 && pc_checkskill(sd,MG_COLDBOLT)>0)
+ WFIFOL(fd,6)= MG_COLDBOLT;
+ else
+ WFIFOL(fd,6)= 0x00000000;
+ if(skilllv>1 && pc_checkskill(sd,MG_FIREBOLT)>0)
+ WFIFOL(fd,10)= MG_FIREBOLT;
+ else
+ WFIFOL(fd,10)= 0x00000000;
+ if(skilllv>1 && pc_checkskill(sd,MG_LIGHTNINGBOLT)>0)
+ WFIFOL(fd,14)= MG_LIGHTNINGBOLT;
+ else
+ WFIFOL(fd,14)= 0x00000000;
+ if(skilllv>4 && pc_checkskill(sd,MG_SOULSTRIKE)>0)
+ WFIFOL(fd,18)= MG_SOULSTRIKE;
+ else
+ WFIFOL(fd,18)= 0x00000000;
+ if(skilllv>7 && pc_checkskill(sd,MG_FIREBALL)>0)
+ WFIFOL(fd,22)= MG_FIREBALL;
+ else
+ WFIFOL(fd,22)= 0x00000000;
+ if(skilllv>9 && pc_checkskill(sd,MG_FROSTDIVER)>0)
+ WFIFOL(fd,26)= MG_FROSTDIVER;
+ else
+ WFIFOL(fd,26)= 0x00000000;
+
+ WFIFOSET(fd,packet_len_table[0x1cd]);
+ return 0;
+}
+
+/*==========================================
+ * ディボーションの青い糸
+ *------------------------------------------
+ */
+int clif_devotion(struct map_session_data *sd)
+{
+ unsigned char buf[56];
+ int i,n;
+
+ nullpo_retr(0, sd);
+
+ WBUFW(buf,0)=0x1cf;
+ WBUFL(buf,2)=sd->bl.id;
+ for(i=0,n=0;i<5;i++) {
+ if (!sd->devotion[i])
+ continue;
+ WBUFL(buf,6+4*n)=sd->devotion[i];
+ n++;
+ }
+ for(;n<5;n++)
+ WBUFL(buf,6+4*n)=0;
+
+ WBUFB(buf,26)=8;
+ WBUFB(buf,27)=0;
+
+ clif_send(buf,packet_len_table[0x1cf],&sd->bl,AREA);
+ return 0;
+}
+
+int clif_marionette(struct block_list *src, struct block_list *target)
+{
+ unsigned char buf[56];
+ int n;
+
+ WBUFW(buf,0)=0x1cf;
+ WBUFL(buf,2)=src->id;
+ for(n=0;n<5;n++)
+ WBUFL(buf,6+4*n)=0;
+ if (target) //The target goes on the second slot.
+ WBUFL(buf,6+4) = target->id;
+ WBUFB(buf,26)=8;
+ WBUFB(buf,27)=0;
+
+ clif_send(buf,packet_len_table[0x1cf],src,AREA);
+ return 0;
+}
+
+/*==========================================
+ * 氣球
+ *------------------------------------------
+ */
+int clif_spiritball(struct map_session_data *sd)
+{
+ unsigned char buf[16];
+
+ nullpo_retr(0, sd);
+
+ WBUFW(buf,0)=0x1d0;
+ WBUFL(buf,2)=sd->bl.id;
+ WBUFW(buf,6)=sd->spiritball;
+ clif_send(buf,packet_len_table[0x1d0],&sd->bl,AREA);
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int clif_combo_delay(struct block_list *bl,int wait)
+{
+ unsigned char buf[32];
+
+ nullpo_retr(0, bl);
+
+ WBUFW(buf,0)=0x1d2;
+ WBUFL(buf,2)=bl->id;
+ WBUFL(buf,6)=wait;
+ clif_send(buf,packet_len_table[0x1d2],bl,AREA);
+
+ return 0;
+}
+/*==========================================
+ *白刃取り
+ *------------------------------------------
+ */
+int clif_bladestop(struct block_list *src,struct block_list *dst,
+ int _bool)
+{
+ unsigned char buf[32];
+
+ nullpo_retr(0, src);
+ nullpo_retr(0, dst);
+
+ WBUFW(buf,0)=0x1d1;
+ WBUFL(buf,2)=src->id;
+ WBUFL(buf,6)=dst->id;
+ WBUFL(buf,10)=_bool;
+
+ clif_send(buf,packet_len_table[0x1d1],src,AREA);
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int clif_changemapcell(int m,int x,int y,int cell_type,int type)
+{
+ struct block_list bl;
+ unsigned char buf[32];
+
+ bl.m = m;
+ bl.x = x;
+ bl.y = y;
+ WBUFW(buf,0) = 0x192;
+ WBUFW(buf,2) = x;
+ WBUFW(buf,4) = y;
+ WBUFW(buf,6) = cell_type;
+ memcpy(WBUFP(buf,8),map[m].name,MAP_NAME_LENGTH);
+ if(!type)
+ clif_send(buf,packet_len_table[0x192],&bl,AREA);
+ else
+ clif_send(buf,packet_len_table[0x192],&bl,ALL_SAMEMAP);
+
+ return 0;
+}
+
+/*==========================================
+ * MVPエフェクト
+ *------------------------------------------
+ */
+int clif_mvp_effect(struct map_session_data *sd)
+{
+ unsigned char buf[16];
+
+ nullpo_retr(0, sd);
+
+ WBUFW(buf,0)=0x10c;
+ WBUFL(buf,2)=sd->bl.id;
+ clif_send(buf,packet_len_table[0x10c],&sd->bl,AREA);
+ return 0;
+}
+/*==========================================
+ * MVPアイテム所得
+ *------------------------------------------
+ */
+int clif_mvp_item(struct map_session_data *sd,int nameid)
+{
+ int view,fd;
+
+ nullpo_retr(0, sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,packet_len_table[0x10a]);
+ WFIFOW(fd,0)=0x10a;
+ if((view = itemdb_viewid(nameid)) > 0)
+ WFIFOW(fd,2)=view;
+ else
+ WFIFOW(fd,2)=nameid;
+ WFIFOSET(fd,packet_len_table[0x10a]);
+ return 0;
+}
+/*==========================================
+ * MVP経験値所得
+ *------------------------------------------
+ */
+int clif_mvp_exp(struct map_session_data *sd,int exp)
+{
+ int fd;
+
+ nullpo_retr(0, sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,packet_len_table[0x10b]);
+ WFIFOW(fd,0)=0x10b;
+ WFIFOL(fd,2)=exp;
+ WFIFOSET(fd,packet_len_table[0x10b]);
+ return 0;
+}
+
+/*==========================================
+ * ギルド作成可否通知
+ *------------------------------------------
+ */
+int clif_guild_created(struct map_session_data *sd,int flag)
+{
+ int fd;
+
+ nullpo_retr(0, sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,packet_len_table[0x167]);
+ WFIFOW(fd,0)=0x167;
+ WFIFOB(fd,2)=flag;
+ WFIFOSET(fd,packet_len_table[0x167]);
+ return 0;
+}
+/*==========================================
+ * ギルド所属通知
+ *------------------------------------------
+ */
+int clif_guild_belonginfo(struct map_session_data *sd,struct guild *g)
+{
+ int ps,fd;
+
+ nullpo_retr(0, sd);
+ nullpo_retr(0, g);
+
+ fd=sd->fd;
+ ps=guild_getposition(sd,g);
+
+ WFIFOHEAD(fd,packet_len_table[0x16c]);
+ memset(WFIFOP(fd,0),0,packet_len_table[0x16c]);
+ WFIFOW(fd,0)=0x16c;
+ WFIFOL(fd,2)=g->guild_id;
+ WFIFOL(fd,6)=g->emblem_id;
+ WFIFOL(fd,10)=g->position[ps].mode;
+ memcpy(WFIFOP(fd,19),g->name,NAME_LENGTH);
+ WFIFOSET(fd,packet_len_table[0x16c]);
+ return 0;
+}
+/*==========================================
+ * ギルドメンバログイン通知
+ *------------------------------------------
+ */
+int clif_guild_memberlogin_notice(struct guild *g,int idx,int flag)
+{
+ unsigned char buf[64];
+
+ nullpo_retr(0, g);
+
+ // printf("clif_guild_message(%s, %d, %s)\n", g->name, account_id, mes);
+
+ WBUFW(buf, 0)=0x16d;
+ WBUFL(buf, 2)=g->member[idx].account_id;
+ WBUFL(buf, 6)=g->member[idx].char_id;
+ WBUFL(buf,10)=flag;
+ if(g->member[idx].sd==NULL){
+ struct map_session_data *sd=guild_getavailablesd(g);
+ if(sd!=NULL)
+ clif_send(buf,packet_len_table[0x16d],&sd->bl,GUILD);
+ }else
+ clif_send(buf,packet_len_table[0x16d],&g->member[idx].sd->bl,GUILD_WOS);
+ return 0;
+}
+/*==========================================
+ * ギルドマスター通知(14dへの応答)
+ *------------------------------------------
+ */
+int clif_guild_masterormember(struct map_session_data *sd)
+{
+ int type=0x57,fd;
+
+ nullpo_retr(0, sd);
+
+ fd=sd->fd;
+ if(sd->state.gmaster_flag)
+ type=0xd7;
+ WFIFOHEAD(fd,packet_len_table[0x14e]);
+ WFIFOW(fd,0)=0x14e;
+ WFIFOL(fd,2)=type;
+ WFIFOSET(fd,packet_len_table[0x14e]);
+ return 0;
+}
+/*==========================================
+ * Basic Info (Territories [Valaris])
+ *------------------------------------------
+ */
+int clif_guild_basicinfo(struct map_session_data *sd)
+{
+ int fd,i,t=0;
+ struct guild *g;
+ struct guild_castle *gc=NULL;
+
+ nullpo_retr(0, sd);
+
+ fd=sd->fd;
+ g=guild_search(sd->status.guild_id);
+ if(g==NULL)
+ return 0;
+
+ WFIFOHEAD(fd,packet_len_table[0x1b6]);
+ WFIFOW(fd, 0)=0x1b6;//0x150;
+ WFIFOL(fd, 2)=g->guild_id;
+ WFIFOL(fd, 6)=g->guild_lv;
+ WFIFOL(fd,10)=g->connect_member;
+ WFIFOL(fd,14)=g->max_member;
+ WFIFOL(fd,18)=g->average_lv;
+ WFIFOL(fd,22)=g->exp;
+ WFIFOL(fd,26)=g->next_exp;
+ WFIFOL(fd,30)=0; // 上納
+ WFIFOL(fd,34)=0; // VW(性格の悪さ?:性向グラフ左右)
+ WFIFOL(fd,38)=0; // RF(正義の度合い?:性向グラフ上下)
+ WFIFOL(fd,42)=0; // 人数?
+ memcpy(WFIFOP(fd,46),g->name, NAME_LENGTH);
+ memcpy(WFIFOP(fd,70),g->master, NAME_LENGTH);
+
+ for(i=0;i<MAX_GUILDCASTLE;i++){
+ gc=guild_castle_search(i);
+ if(!gc) continue;
+ if(g->guild_id == gc->guild_id) t++;
+ }
+ if (t>=0 && t<=MAX_GUILDCASTLE) //(0=None, 1..24 = N of Casles) [Lupus]
+ strncpy((char*)WFIFOP(fd,94),msg_txt(300+t),20);
+ else
+ strncpy((char*)WFIFOP(fd,94),msg_txt(299),20);
+
+ WFIFOSET(fd,packet_len_table[WFIFOW(fd,0)]);
+ clif_guild_emblem(sd,g); // Guild emblem vanish fix [Valaris]
+ return 0;
+}
+
+/*==========================================
+ * ギルド同盟/敵対情報
+ *------------------------------------------
+ */
+int clif_guild_allianceinfo(struct map_session_data *sd)
+{
+ int fd,i,c;
+ struct guild *g;
+
+ nullpo_retr(0, sd);
+
+ fd=sd->fd;
+ g=guild_search(sd->status.guild_id);
+ if(g==NULL)
+ return 0;
+ WFIFOHEAD(fd, MAX_GUILDALLIANCE * 32 + 4);
+ WFIFOW(fd, 0)=0x14c;
+ for(i=c=0;i<MAX_GUILDALLIANCE;i++){
+ struct guild_alliance *a=&g->alliance[i];
+ if(a->guild_id>0){
+ WFIFOL(fd,c*32+4)=a->opposition;
+ WFIFOL(fd,c*32+8)=a->guild_id;
+ memcpy(WFIFOP(fd,c*32+12),a->name,NAME_LENGTH);
+ c++;
+ }
+ }
+ WFIFOW(fd, 2)=c*32+4;
+ WFIFOSET(fd,WFIFOW(fd,2));
+ return 0;
+}
+
+/*==========================================
+ * ギルドメンバーリスト
+ *------------------------------------------
+ */
+int clif_guild_memberlist(struct map_session_data *sd)
+{
+ int fd;
+ int i,c;
+ struct guild *g;
+
+ nullpo_retr(0, sd);
+
+ fd=sd->fd;
+ if (!fd)
+ return 0;
+ g=guild_search(sd->status.guild_id);
+ if(g==NULL)
+ return 0;
+
+ WFIFOHEAD(fd, g->max_member * 104 + 4);
+ WFIFOW(fd, 0)=0x154;
+ for(i=0,c=0;i<g->max_member;i++){
+ struct guild_member *m=&g->member[i];
+ if(m->account_id==0)
+ continue;
+ WFIFOL(fd,c*104+ 4)=m->account_id;
+ WFIFOL(fd,c*104+ 8)=m->char_id;
+ WFIFOW(fd,c*104+12)=m->hair;
+ WFIFOW(fd,c*104+14)=m->hair_color;
+ WFIFOW(fd,c*104+16)=m->gender;
+ WFIFOW(fd,c*104+18)=m->class_;
+ WFIFOW(fd,c*104+20)=m->lv;
+ WFIFOL(fd,c*104+22)=m->exp;
+ WFIFOL(fd,c*104+26)=m->online;
+ WFIFOL(fd,c*104+30)=m->position;
+ memset(WFIFOP(fd,c*104+34),0,50); // メモ?
+ memcpy(WFIFOP(fd,c*104+84),m->name,NAME_LENGTH);
+ c++;
+ }
+ WFIFOW(fd, 2)=c*104+4;
+ WFIFOSET(fd,WFIFOW(fd,2));
+ return 0;
+}
+/*==========================================
+ * ギルド役職名リスト
+ *------------------------------------------
+ */
+int clif_guild_positionnamelist(struct map_session_data *sd)
+{
+ int i,fd;
+ struct guild *g;
+
+ nullpo_retr(0, sd);
+
+ fd=sd->fd;
+ g=guild_search(sd->status.guild_id);
+ if(g==NULL)
+ return 0;
+ WFIFOHEAD(fd, MAX_GUILDPOSITION * 28 + 4);
+ WFIFOW(fd, 0)=0x166;
+ for(i=0;i<MAX_GUILDPOSITION;i++){
+ WFIFOL(fd,i*28+4)=i;
+ memcpy(WFIFOP(fd,i*28+8),g->position[i].name,NAME_LENGTH);
+ }
+ WFIFOW(fd,2)=i*28+4;
+ WFIFOSET(fd,WFIFOW(fd,2));
+ return 0;
+}
+/*==========================================
+ * ギルド役職情報リスト
+ *------------------------------------------
+ */
+int clif_guild_positioninfolist(struct map_session_data *sd)
+{
+ int i,fd;
+ struct guild *g;
+
+ nullpo_retr(0, sd);
+
+ fd=sd->fd;
+ g=guild_search(sd->status.guild_id);
+ if(g==NULL)
+ return 0;
+ WFIFOHEAD(fd, MAX_GUILDPOSITION * 16 + 4);
+ WFIFOW(fd, 0)=0x160;
+ for(i=0;i<MAX_GUILDPOSITION;i++){
+ struct guild_position *p=&g->position[i];
+ WFIFOL(fd,i*16+ 4)=i;
+ WFIFOL(fd,i*16+ 8)=p->mode;
+ WFIFOL(fd,i*16+12)=i;
+ WFIFOL(fd,i*16+16)=p->exp_mode;
+ }
+ WFIFOW(fd, 2)=i*16+4;
+ WFIFOSET(fd,WFIFOW(fd,2));
+ return 0;
+}
+/*==========================================
+ * ギルド役職変更通知
+ *------------------------------------------
+ */
+int clif_guild_positionchanged(struct guild *g,int idx)
+{
+ struct map_session_data *sd;
+ unsigned char buf[128];
+
+ nullpo_retr(0, g);
+
+ WBUFW(buf, 0)=0x174;
+ WBUFW(buf, 2)=44;
+ WBUFL(buf, 4)=idx;
+ WBUFL(buf, 8)=g->position[idx].mode;
+ WBUFL(buf,12)=idx;
+ WBUFL(buf,16)=g->position[idx].exp_mode;
+ memcpy(WBUFP(buf,20),g->position[idx].name,NAME_LENGTH);
+ if( (sd=guild_getavailablesd(g))!=NULL )
+ clif_send(buf,WBUFW(buf,2),&sd->bl,GUILD);
+ return 0;
+}
+/*==========================================
+ * ギルドメンバ変更通知
+ *------------------------------------------
+ */
+int clif_guild_memberpositionchanged(struct guild *g,int idx)
+{
+ struct map_session_data *sd;
+ unsigned char buf[64];
+
+ nullpo_retr(0, g);
+
+ WBUFW(buf, 0)=0x156;
+ WBUFW(buf, 2)=16;
+ WBUFL(buf, 4)=g->member[idx].account_id;
+ WBUFL(buf, 8)=g->member[idx].char_id;
+ WBUFL(buf,12)=g->member[idx].position;
+ if( (sd=guild_getavailablesd(g))!=NULL )
+ clif_send(buf,WBUFW(buf,2),&sd->bl,GUILD);
+ return 0;
+}
+/*==========================================
+ * ギルドエンブレム送信
+ *------------------------------------------
+ */
+int clif_guild_emblem(struct map_session_data *sd,struct guild *g)
+{
+ int fd;
+
+ nullpo_retr(0, sd);
+ nullpo_retr(0, g);
+
+ fd=sd->fd;
+
+ if(g->emblem_len<=0)
+ return 0;
+ WFIFOHEAD(fd,g->emblem_len+12);
+ WFIFOW(fd,0)=0x152;
+ WFIFOW(fd,2)=g->emblem_len+12;
+ WFIFOL(fd,4)=g->guild_id;
+ WFIFOL(fd,8)=g->emblem_id;
+ memcpy(WFIFOP(fd,12),g->emblem_data,g->emblem_len);
+ WFIFOSET(fd,WFIFOW(fd,2));
+ return 0;
+}
+/*==========================================
+ * ギルドスキル送信
+ *------------------------------------------
+ */
+int clif_guild_skillinfo(struct map_session_data *sd)
+{
+ int fd;
+ int i,id,c,up=1;
+ struct guild *g;
+
+ nullpo_retr(0, sd);
+
+ fd=sd->fd;
+ g=guild_search(sd->status.guild_id);
+ if(g==NULL)
+ return 0;
+ WFIFOHEAD(fd, MAX_GUILDSKILL * 37 + 6);
+ WFIFOW(fd,0)=0x0162;
+ WFIFOW(fd,4)=g->skill_point;
+ for(i=c=0;i<MAX_GUILDSKILL;i++){
+ if(g->skill[i].id>0 && guild_check_skill_require(g,g->skill[i].id)){
+ WFIFOW(fd,c*37+ 6) = id = g->skill[i].id;
+ WFIFOW(fd,c*37+ 8) = guild_skill_get_inf(id);
+ WFIFOW(fd,c*37+10) = 0;
+ WFIFOW(fd,c*37+12) = g->skill[i].lv;
+ WFIFOW(fd,c*37+14) = skill_get_sp(id,g->skill[i].lv);
+ WFIFOW(fd,c*37+16) = skill_get_range(id,g->skill[i].lv);
+ memset(WFIFOP(fd,c*37+18),0,24);
+ if(g->skill[i].lv < guild_skill_get_max(id) && (sd == g->member[0].sd))
+ up = 1;
+ else
+ up = 0;
+ WFIFOB(fd,c*37+42)= up;
+ c++;
+ }
+ }
+ WFIFOW(fd,2)=c*37+6;
+ WFIFOSET(fd,WFIFOW(fd,2));
+ return 0;
+}
+/*==========================================
+ * ギルド告知送信
+ *------------------------------------------
+ */
+int clif_guild_notice(struct map_session_data *sd,struct guild *g)
+{
+ int fd;
+
+ nullpo_retr(0, sd);
+ nullpo_retr(0, g);
+
+ fd = sd->fd;
+
+ if ( !session_isActive(fd) ) //null pointer right here [Kevin]
+ return 0;
+
+ if (fd <= 0)
+ return 0;
+ if(*g->mes1==0 && *g->mes2==0)
+ return 0;
+ WFIFOHEAD(fd,packet_len_table[0x16f]);
+ WFIFOW(fd,0)=0x16f;
+ memcpy(WFIFOP(fd,2),g->mes1,60);
+ memcpy(WFIFOP(fd,62),g->mes2,120);
+ WFIFOSET(fd,packet_len_table[0x16f]);
+ return 0;
+}
+
+/*==========================================
+ * ギルドメンバ勧誘
+ *------------------------------------------
+ */
+int clif_guild_invite(struct map_session_data *sd,struct guild *g)
+{
+ int fd;
+
+ nullpo_retr(0, sd);
+ nullpo_retr(0, g);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,packet_len_table[0x16a]);
+ WFIFOW(fd,0)=0x16a;
+ WFIFOL(fd,2)=g->guild_id;
+ memcpy(WFIFOP(fd,6),g->name,NAME_LENGTH);
+ WFIFOSET(fd,packet_len_table[0x16a]);
+ return 0;
+}
+/*==========================================
+ * ギルドメンバ勧誘結果
+ *------------------------------------------
+ */
+int clif_guild_inviteack(struct map_session_data *sd,int flag)
+{
+ int fd;
+
+ nullpo_retr(0, sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,packet_len_table[0x169]);
+ WFIFOW(fd,0)=0x169;
+ WFIFOB(fd,2)=flag;
+ WFIFOSET(fd,packet_len_table[0x169]);
+ return 0;
+}
+/*==========================================
+ * ギルドメンバ脱退通知
+ *------------------------------------------
+ */
+int clif_guild_leave(struct map_session_data *sd,const char *name,const char *mes)
+{
+ unsigned char buf[128];
+
+ nullpo_retr(0, sd);
+
+ WBUFW(buf, 0)=0x15a;
+ memcpy(WBUFP(buf, 2),name,NAME_LENGTH);
+ memcpy(WBUFP(buf,26),mes,40);
+ clif_send(buf,packet_len_table[0x15a],&sd->bl,GUILD);
+ return 0;
+}
+/*==========================================
+ * ギルドメンバ追放通知
+ *------------------------------------------
+ */
+int clif_guild_explusion(struct map_session_data *sd,const char *name,const char *mes,
+ int account_id)
+{
+ unsigned char buf[128];
+
+ nullpo_retr(0, sd);
+
+ WBUFW(buf, 0)=0x15c;
+ memcpy(WBUFP(buf, 2),name,NAME_LENGTH);
+ memcpy(WBUFP(buf,26),mes,40);
+ memcpy(WBUFP(buf,66),"dummy",NAME_LENGTH);
+ clif_send(buf,packet_len_table[0x15c],&sd->bl,GUILD);
+ return 0;
+}
+/*==========================================
+ * ギルド追放メンバリスト
+ *------------------------------------------
+ */
+int clif_guild_explusionlist(struct map_session_data *sd)
+{
+ int fd;
+ int i,c;
+ struct guild *g;
+
+ nullpo_retr(0, sd);
+
+ fd=sd->fd;
+ g=guild_search(sd->status.guild_id);
+ if(g==NULL)
+ return 0;
+ WFIFOHEAD(fd,MAX_GUILDEXPLUSION * 88 + 4);
+ WFIFOW(fd,0)=0x163;
+ for(i=c=0;i<MAX_GUILDEXPLUSION;i++){
+ struct guild_explusion *e=&g->explusion[i];
+ if(e->account_id>0){
+ memcpy(WFIFOP(fd,c*88+ 4),e->name,NAME_LENGTH);
+ memcpy(WFIFOP(fd,c*88+28),e->acc,24);
+ memcpy(WFIFOP(fd,c*88+52),e->mes,44);
+ c++;
+ }
+ }
+ WFIFOW(fd,2)=c*88+4;
+ WFIFOSET(fd,WFIFOW(fd,2));
+ return 0;
+}
+
+/*==========================================
+ * ギルド会話
+ *------------------------------------------
+ */
+int clif_guild_message(struct guild *g,int account_id,const char *mes,int len)
+{
+ struct map_session_data *sd;
+ unsigned char *buf;
+
+ buf = (unsigned char*)aCallocA(len + 4, sizeof(unsigned char));
+
+ WBUFW(buf, 0) = 0x17f;
+ WBUFW(buf, 2) = len + 4;
+ memcpy(WBUFP(buf,4), mes, len);
+
+ if ((sd = guild_getavailablesd(g)) != NULL)
+ clif_send(buf, WBUFW(buf,2), &sd->bl, GUILD);
+
+ if(buf) aFree(buf);
+
+ return 0;
+}
+/*==========================================
+ * ギルドスキル割り振り通知
+ *------------------------------------------
+ */
+int clif_guild_skillup(struct map_session_data *sd,int skill_num,int lv)
+{
+ int fd;
+
+ nullpo_retr(0, sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,11);
+ WFIFOW(fd,0) = 0x10e;
+ WFIFOW(fd,2) = skill_num;
+ WFIFOW(fd,4) = lv;
+ WFIFOW(fd,6) = skill_get_sp(skill_num,lv);
+ WFIFOW(fd,8) = skill_get_range(skill_num,lv);
+ WFIFOB(fd,10) = 1;
+ WFIFOSET(fd,11);
+ return 0;
+}
+/*==========================================
+ * ギルド同盟要請
+ *------------------------------------------
+ */
+int clif_guild_reqalliance(struct map_session_data *sd,int account_id,const char *name)
+{
+ int fd;
+
+ nullpo_retr(0, sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,packet_len_table[0x171]);
+ WFIFOW(fd,0)=0x171;
+ WFIFOL(fd,2)=account_id;
+ memcpy(WFIFOP(fd,6),name,NAME_LENGTH);
+ WFIFOSET(fd,packet_len_table[0x171]);
+ return 0;
+}
+/*==========================================
+ * ギルド同盟結果
+ *------------------------------------------
+ */
+int clif_guild_allianceack(struct map_session_data *sd,int flag)
+{
+ int fd;
+
+ nullpo_retr(0, sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,packet_len_table[0x173]);
+ WFIFOW(fd,0)=0x173;
+ WFIFOL(fd,2)=flag;
+ WFIFOSET(fd,packet_len_table[0x173]);
+ return 0;
+}
+/*==========================================
+ * ギルド関係解消通知
+ *------------------------------------------
+ */
+int clif_guild_delalliance(struct map_session_data *sd,int guild_id,int flag)
+{
+ int fd;
+
+ nullpo_retr(0, sd);
+
+ fd = sd->fd;
+ if (fd <= 0)
+ return 0;
+ WFIFOHEAD(fd,packet_len_table[0x184]);
+ WFIFOW(fd,0)=0x184;
+ WFIFOL(fd,2)=guild_id;
+ WFIFOL(fd,6)=flag;
+ WFIFOSET(fd,packet_len_table[0x184]);
+ return 0;
+}
+/*==========================================
+ * ギルド敵対結果
+ *------------------------------------------
+ */
+int clif_guild_oppositionack(struct map_session_data *sd,int flag)
+{
+ int fd;
+
+ nullpo_retr(0, sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,packet_len_table[0x181]);
+ WFIFOW(fd,0)=0x181;
+ WFIFOB(fd,2)=flag;
+ WFIFOSET(fd,packet_len_table[0x181]);
+ return 0;
+}
+/*==========================================
+ * ギルド関係追加
+ *------------------------------------------
+ */
+/*int clif_guild_allianceadded(struct guild *g,int idx)
+{
+ unsigned char buf[64];
+ WBUFW(fd,0)=0x185;
+ WBUFL(fd,2)=g->alliance[idx].opposition;
+ WBUFL(fd,6)=g->alliance[idx].guild_id;
+ memcpy(WBUFP(fd,10),g->alliance[idx].name,NAME_LENGTH);
+ clif_send(buf,packet_len_table[0x185],guild_getavailablesd(g),GUILD);
+ return 0;
+}*/
+
+/*==========================================
+ * ギルド解散通知
+ *------------------------------------------
+ */
+int clif_guild_broken(struct map_session_data *sd,int flag)
+{
+ int fd;
+
+ nullpo_retr(0, sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,packet_len_table[0x15e]);
+ WFIFOW(fd,0)=0x15e;
+ WFIFOL(fd,2)=flag;
+ WFIFOSET(fd,packet_len_table[0x15e]);
+ return 0;
+}
+
+/*==========================================
+ * エモーション
+ *------------------------------------------
+ */
+void clif_emotion(struct block_list *bl,int type)
+{
+ unsigned char buf[8];
+
+ nullpo_retv(bl);
+
+ WBUFW(buf,0)=0xc0;
+ WBUFL(buf,2)=bl->id;
+ WBUFB(buf,6)=type;
+ clif_send(buf,packet_len_table[0xc0],bl,AREA);
+}
+
+/*==========================================
+ * トーキーボックス
+ *------------------------------------------
+ */
+void clif_talkiebox(struct block_list *bl,char* talkie)
+{
+ unsigned char buf[86];
+
+ nullpo_retv(bl);
+
+ WBUFW(buf,0)=0x191;
+ WBUFL(buf,2)=bl->id;
+ memcpy(WBUFP(buf,6),talkie,MESSAGE_SIZE);
+ clif_send(buf,packet_len_table[0x191],bl,AREA);
+}
+
+/*==========================================
+ * 結婚エフェクト
+ *------------------------------------------
+ */
+void clif_wedding_effect(struct block_list *bl) {
+ unsigned char buf[6];
+
+ nullpo_retv(bl);
+
+ WBUFW(buf,0) = 0x1ea;
+ WBUFL(buf,2) = bl->id;
+ clif_send(buf, packet_len_table[0x1ea], bl, AREA);
+}
+/*==========================================
+ * あなたに逢いたい使用時名前叫び
+ *------------------------------------------
+
+void clif_callpartner(struct map_session_data *sd)
+{
+ unsigned char buf[26];
+ char *p;
+
+ nullpo_retv(sd);
+
+ if(sd->status.partner_id){
+ WBUFW(buf,0)=0x1e6;
+ p = map_charid2nick(sd->status.partner_id);
+ if(p){
+ memcpy(WBUFP(buf,2),p,NAME_LENGTH);
+ }else{
+ map_reqchariddb(sd,sd->status.partner_id);
+ chrif_searchcharid(sd->status.partner_id);
+ WBUFB(buf,2) = 0;
+ }
+ clif_send(buf,packet_len_table[0x1e6],&sd->bl,AREA);
+ }
+ return;
+}
+*/
+/*==========================================
+ * Adopt baby [Celest]
+ *------------------------------------------
+ */
+void clif_adopt_process(struct map_session_data *sd)
+{
+ int fd;
+ nullpo_retv(sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,packet_len_table[0x1f8]);
+ WFIFOW(fd,0)=0x1f8;
+ WFIFOSET(fd,packet_len_table[0x1f8]);
+}
+/*==========================================
+ * Marry [DracoRPG]
+ *------------------------------------------
+ */
+void clif_marriage_process(struct map_session_data *sd)
+{
+ int fd;
+ nullpo_retv(sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,packet_len_table[0x1e4]);
+ WFIFOW(fd,0)=0x1e4;
+ WFIFOSET(fd,packet_len_table[0x1e4]);
+}
+
+
+/*==========================================
+ * Notice of divorce
+ *------------------------------------------
+ */
+void clif_divorced(struct map_session_data *sd, char *name)
+{
+ int fd;
+ nullpo_retv(sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,packet_len_table[0x205]);
+ WFIFOW(fd,0)=0x205;
+ memcpy(WFIFOP(fd,2), name, NAME_LENGTH);
+ WFIFOSET(fd, packet_len_table[0x205]);
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+void clif_parse_ReqAdopt(int fd, struct map_session_data *sd) {
+ nullpo_retv(sd);
+
+ WFIFOHEAD(fd,packet_len_table[0x1f6]);
+ WFIFOW(fd,0)=0x1f6;
+ WFIFOSET(fd, packet_len_table[0x1f6]);
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+void clif_parse_ReqMarriage(int fd, struct map_session_data *sd) {
+ nullpo_retv(sd);
+
+ WFIFOHEAD(fd,packet_len_table[0x1e2]);
+ WFIFOW(fd,0)=0x1e2;
+ WFIFOSET(fd, packet_len_table[0x1e2]);
+}
+
+/*==========================================
+ * 座る
+ *------------------------------------------
+ */
+void clif_sitting(struct map_session_data *sd)
+{
+ unsigned char buf[64];
+
+ nullpo_retv(sd);
+
+ WBUFW(buf, 0) = 0x8a;
+ WBUFL(buf, 2) = sd->bl.id;
+ WBUFB(buf,26) = 2;
+ clif_send(buf, packet_len_table[0x8a], &sd->bl, AREA);
+
+ if(sd->disguise) {
+ WBUFL(buf, 2) = -sd->bl.id;
+ clif_send(buf, packet_len_table[0x8a], &sd->bl, AREA);
+ }
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int clif_disp_onlyself(struct map_session_data *sd, char *mes, int len)
+{
+ unsigned char *buf;
+
+ nullpo_retr(0, sd);
+
+ buf = (unsigned char*)aCallocA(len + 5, sizeof(unsigned char));
+
+ WBUFW(buf, 0) = 0x17f;
+ WBUFW(buf, 2) = len + 5;
+ memcpy(WBUFP(buf,4), mes, len);
+
+ clif_send(buf, WBUFW(buf,2), &sd->bl, SELF);
+
+ if(buf) aFree(buf);
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+
+int clif_GM_kickack(struct map_session_data *sd, int id)
+{
+ int fd;
+
+ nullpo_retr(0, sd);
+
+ fd = sd->fd;
+ WFIFOHEAD(fd,packet_len_table[0xcd]);
+ WFIFOW(fd,0) = 0xcd;
+ WFIFOL(fd,2) = id;
+ WFIFOSET(fd, packet_len_table[0xcd]);
+ return 0;
+}
+
+void clif_parse_QuitGame(int fd,struct map_session_data *sd);
+
+int clif_GM_kick(struct map_session_data *sd,struct map_session_data *tsd,int type)
+{
+ nullpo_retr(0, tsd);
+
+ if(type)
+ clif_GM_kickack(sd,tsd->status.account_id);
+ tsd->opt1 = tsd->opt2 = 0;
+ WFIFOHEAD(tsd->fd,packet_len_table[0x18b]);
+ WFIFOW(tsd->fd,0) = 0x18b;
+ WFIFOW(tsd->fd,2) = 0;
+ WFIFOSET(tsd->fd,packet_len_table[0x18b]);
+
+ if (tsd->fd)
+ {
+ ShowDebug("clif_GM_kick: Disconnecting session #%d\n", tsd->fd);
+ clif_setwaitclose(tsd->fd);
+ } else { //Player has no session attached, delete it right away. [Skotlex]
+ map_quit(tsd);
+ }
+
+ return 0;
+}
+
+int clif_GM_silence(struct map_session_data *sd, struct map_session_data *tsd, int type)
+{
+ int fd;
+
+ nullpo_retr(0, sd);
+ nullpo_retr(0, tsd);
+
+ fd = tsd->fd;
+ if (fd <= 0)
+ return 0;
+ WFIFOHEAD(fd,packet_len_table[0x14b]);
+ WFIFOW(fd,0) = 0x14b;
+ WFIFOB(fd,2) = 0;
+ memcpy(WFIFOP(fd,3), sd->status.name, NAME_LENGTH);
+ WFIFOSET(fd, packet_len_table[0x14b]);
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+
+int clif_timedout(struct map_session_data *sd)
+{
+ nullpo_retr(0, sd);
+
+ ShowInfo("%sCharacter with Account ID '"CL_WHITE"%d"CL_RESET"' timed out.\n", (pc_isGM(sd))?"GM ":"", sd->bl.id);
+ clif_authfail_fd(sd->fd,3); // Even if player is not on we still send anyway
+ clif_setwaitclose(sd->fd); // Set session to EOF
+ return 0;
+}
+
+/*==========================================
+ * Wis拒否許可応答
+ *------------------------------------------
+ */
+int clif_wisexin(struct map_session_data *sd,int type,int flag)
+{
+ int fd;
+
+ nullpo_retr(0, sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,packet_len_table[0xd1]);
+ WFIFOW(fd,0)=0xd1;
+ WFIFOB(fd,2)=type;
+ WFIFOB(fd,3)=flag;
+ WFIFOSET(fd,packet_len_table[0xd1]);
+
+ return 0;
+}
+/*==========================================
+ * Wis全拒否許可応答
+ *------------------------------------------
+ */
+int clif_wisall(struct map_session_data *sd,int type,int flag)
+{
+ int fd;
+
+ nullpo_retr(0, sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,packet_len_table[0xd2]);
+ WFIFOW(fd,0)=0xd2;
+ WFIFOB(fd,2)=type;
+ WFIFOB(fd,3)=flag;
+ WFIFOSET(fd,packet_len_table[0xd2]);
+
+ return 0;
+}
+/*==========================================
+ * サウンドエフェクト
+ *------------------------------------------
+ */
+void clif_soundeffect(struct map_session_data *sd,struct block_list *bl,char *name,int type)
+{
+ int fd;
+
+ nullpo_retv(sd);
+ nullpo_retv(bl);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,packet_len_table[0x1d3]);
+ WFIFOW(fd,0)=0x1d3;
+ memcpy(WFIFOP(fd,2),name,NAME_LENGTH);
+ WFIFOB(fd,26)=type;
+ WFIFOL(fd,27)=0;
+ WFIFOL(fd,31)=bl->id;
+ WFIFOSET(fd,packet_len_table[0x1d3]);
+
+ return;
+}
+
+int clif_soundeffectall(struct block_list *bl, char *name, int type)
+{
+ unsigned char buf[40];
+ memset(buf, 0, packet_len_table[0x1d3]);
+
+ nullpo_retr(0, bl);
+
+ WBUFW(buf,0)=0x1d3;
+ memcpy(WBUFP(buf,2), name, NAME_LENGTH);
+ WBUFB(buf,26)=type;
+ WBUFL(buf,27)=0;
+ WBUFL(buf,31)=bl->id;
+ clif_send(buf, packet_len_table[0x1d3], bl, AREA);
+
+ return 0;
+}
+
+// displaying special effects (npcs, weather, etc) [Valaris]
+int clif_specialeffect(struct block_list *bl, int type, int flag)
+{
+ unsigned char buf[24];
+
+ nullpo_retr(0, bl);
+
+ memset(buf, 0, packet_len_table[0x1f3]);
+
+ WBUFW(buf,0) = 0x1f3;
+ if(bl->type==BL_PC && ((struct map_session_data *)bl)->disguise)
+ WBUFL(buf,2) = -bl->id;
+ else
+ WBUFL(buf,2) = bl->id;
+ WBUFL(buf,6) = type;
+
+ switch (flag) {
+ case 4:
+ clif_send(buf, packet_len_table[0x1f3], bl, AREA_WOS);
+ break;
+ case 3:
+ clif_send(buf, packet_len_table[0x1f3], bl, ALL_CLIENT);
+ break;
+ case 2:
+ clif_send(buf, packet_len_table[0x1f3], bl, ALL_SAMEMAP);
+ break;
+ case 1:
+ clif_send(buf, packet_len_table[0x1f3], bl, SELF);
+ break;
+ default:
+ clif_send(buf, packet_len_table[0x1f3], bl, AREA);
+ }
+
+ return 0;
+}
+
+// refresh the client's screen, getting rid of any effects
+int clif_refresh(struct map_session_data *sd) {
+ nullpo_retr(-1, sd);
+ clif_changemap(sd,sd->mapindex,sd->bl.x,sd->bl.y);
+ map_foreachinarea(clif_getareachar,sd->bl.m,sd->bl.x-AREA_SIZE,sd->bl.y-AREA_SIZE,sd->bl.x+AREA_SIZE,sd->bl.y+AREA_SIZE,BL_ALL,sd);
+ return 0;
+}
+
+// updates the object's (bl) name on client
+int clif_charnameack (int fd, struct block_list *bl)
+{
+ unsigned char buf[103];
+ int cmd = 0x95;
+
+ nullpo_retr(0, bl);
+
+ WBUFW(buf,0) = cmd;
+
+ if(bl->type==BL_PC && ((struct map_session_data *)bl)->disguise)
+ WBUFL(buf,2) = -bl->id;
+ else
+ WBUFL(buf,2) = bl->id;
+
+ switch(bl->type) {
+ case BL_PC:
+ {
+ struct map_session_data *ssd = (struct map_session_data *)bl;
+ struct party *p = NULL;
+ struct guild *g = NULL;
+
+ nullpo_retr(0, ssd);
+
+ if (strlen(ssd->fakename)>1) {
+ memcpy(WBUFP(buf,6), ssd->fakename, NAME_LENGTH);
+ break;
+ }
+ memcpy(WBUFP(buf,6), ssd->status.name, NAME_LENGTH);
+
+ if (ssd->status.party_id > 0)
+ p = party_search(ssd->status.party_id);
+
+ if (ssd->status.guild_id > 0)
+ g = guild_search(ssd->status.guild_id);
+
+ if (p == NULL && g == NULL)
+ break;
+
+ WBUFW(buf, 0) = cmd = 0x195;
+ if (p)
+ memcpy(WBUFP(buf,30), p->name, NAME_LENGTH);
+ else
+ WBUFB(buf,30) = 0;
+
+ if (g)
+ {
+ int i, ps = -1;
+ for(i = 0; i < g->max_member; i++) {
+ if (g->member[i].account_id == ssd->status.account_id &&
+ g->member[i].char_id == ssd->status.char_id )
+ {
+ ps = g->member[i].position;
+ break;
+ }
+ }
+ if (ps >= 0 && ps < MAX_GUILDPOSITION)
+ {
+ memcpy(WBUFP(buf,54), g->name,NAME_LENGTH);
+ memcpy(WBUFP(buf,78), g->position[ps].name, NAME_LENGTH);
+ } else { //Assume no guild.
+ WBUFB(buf,54) = 0;
+ WBUFB(buf,78) = 0;
+ }
+ } else {
+ WBUFB(buf,54) = 0;
+ WBUFB(buf,78) = 0;
+ }
+ }
+ break;
+ case BL_PET:
+ memcpy(WBUFP(buf,6), ((struct pet_data*)bl)->name, NAME_LENGTH);
+ break;
+ case BL_NPC:
+ memcpy(WBUFP(buf,6), ((struct npc_data*)bl)->name, NAME_LENGTH);
+ break;
+ case BL_MOB:
+ {
+ struct mob_data *md = (struct mob_data *)bl;
+ nullpo_retr(0, md);
+
+ memcpy(WBUFP(buf,6), md->name, NAME_LENGTH);
+ if (md->guardian_data && md->guardian_data->guild_id) {
+ WBUFW(buf, 0) = cmd = 0x195;
+ WBUFB(buf,30) = 0;
+ memcpy(WBUFP(buf,54), md->guardian_data->guild_name, NAME_LENGTH);
+ memcpy(WBUFP(buf,78), md->guardian_data->castle->castle_name, NAME_LENGTH);
+ } else if (battle_config.show_mob_hp == 1) {
+ char mobhp[50];
+ WBUFW(buf, 0) = cmd = 0x195;
+ sprintf(mobhp, "HP: %d/%d", md->hp, md->max_hp);
+ //Even thought mobhp ain't a name, we send it as one so the client
+ //can parse it. [Skotlex]
+ memcpy(WBUFP(buf,30), mobhp, NAME_LENGTH);
+ WBUFB(buf,54) = 0;
+ memcpy(WBUFP(buf,78), mobhp, NAME_LENGTH);
+ }
+ }
+ break;
+ case BL_CHAT: //FIXME: Clients DO request this... what should be done about it? The chat's title may not fit... [Skotlex]
+// memcpy(WBUFP(buf,6), (struct chat*)->title, NAME_LENGTH);
+// break;
+ return 0;
+ default:
+ if (battle_config.error_log)
+ ShowError("clif_parse_GetCharNameRequest : bad type %d(%d)\n", bl->type, bl->id);
+ return 0;
+ }
+
+ // if no receipient specified just update nearby clients
+ if (fd == 0)
+ clif_send(buf, packet_len_table[cmd], bl, AREA);
+ else {
+ WFIFOHEAD(fd, packet_len_table[cmd]);
+ memcpy(WFIFOP(fd, 0), buf, packet_len_table[cmd]);
+ WFIFOSET(fd, packet_len_table[cmd]);
+ }
+
+ return 0;
+}
+
+//Used to update when a char leaves a party/guild. [Skotlex]
+//Needed because when you send a 0x95 packet, the client will not remove the cached party/guild info that is not sent.
+int clif_charnameupdate (struct map_session_data *ssd)
+{
+ unsigned char buf[103];
+ int cmd = 0x195;
+ struct party *p = NULL;
+ struct guild *g = NULL;
+
+ nullpo_retr(0, ssd);
+
+ if (strlen(ssd->fakename)>1)
+ return 0; //No need to update as the party/guild was not displayed anyway.
+
+ WBUFW(buf,0) = cmd;
+
+ if(ssd->disguise)
+ WBUFL(buf,2) = -(ssd->bl.id);
+ else
+ WBUFL(buf,2) = ssd->bl.id;
+
+ memcpy(WBUFP(buf,6), ssd->status.name, NAME_LENGTH);
+
+ if (ssd->status.party_id > 0)
+ p = party_search(ssd->status.party_id);
+
+ if (ssd->status.guild_id > 0)
+ g = guild_search(ssd->status.guild_id);
+
+ if (p)
+ memcpy(WBUFP(buf,30), p->name, NAME_LENGTH);
+ else
+ WBUFB(buf,30) = 0;
+
+ if (g)
+ {
+ int i, ps = -1;
+ for(i = 0; i < g->max_member; i++) {
+ if (g->member[i].account_id == ssd->status.account_id &&
+ g->member[i].char_id == ssd->status.char_id )
+ {
+ ps = g->member[i].position;
+ break;
+ }
+ }
+ if (ps >= 0 && ps < MAX_GUILDPOSITION)
+ {
+ memcpy(WBUFP(buf,54), g->name,NAME_LENGTH);
+ memcpy(WBUFP(buf,78), g->position[ps].name, NAME_LENGTH);
+ } else { //Assume no guild.
+ WBUFB(buf,54) = 0;
+ WBUFB(buf,78) = 0;
+ }
+ } else {
+ WBUFB(buf,54) = 0;
+ WBUFB(buf,78) = 0;
+ }
+
+ // Update nearby clients
+ clif_send(buf, packet_len_table[cmd], &ssd->bl, AREA);
+ return 0;
+}
+
+int clif_slide(struct block_list *bl, int x, int y){
+ unsigned char buf[10];
+
+ nullpo_retr(0, bl);
+
+ WBUFW(buf, 0) = 0x01ff;
+ WBUFL(buf, 2) = bl->id;
+ WBUFW(buf, 6) = x;
+ WBUFW(buf, 8) = y;
+
+ clif_send(buf, packet_len_table[0x1ff], bl, AREA);
+ return 0;
+}
+
+/*------------------------------------------
+ * @me command by lordalfa, rewritten implementation by Skotlex
+ *------------------------------------------
+*/
+int clif_disp_overhead(struct map_session_data *sd, char* mes)
+{
+ unsigned char buf[256]; //This should be more than sufficient, the theorical max is MESSAGE_SIZE+NAME_LENGTH + 8 (pads and extra inserted crap)
+ int len_mes = strlen(mes)+1; //Account for \0
+
+ if (len_mes + 8 >= 256) {
+ if (battle_config.error_log)
+ ShowError("clif_disp_overhead: Message too long (length %d)\n", len_mes);
+ len_mes = 247; //Trunk it to avoid problems.
+ }
+ // send message to others
+ WBUFW(buf,0) = 0x8d;
+ WBUFW(buf,2) = len_mes + 8; // len of message + 8 (command+len+id)
+ WBUFL(buf,4) = sd->bl.id;
+ memcpy(WBUFP(buf,8), mes, len_mes);
+ clif_send(buf, WBUFW(buf,2), &sd->bl, AREA_CHAT_WOC);
+
+ // send back message to the speaker
+ WBUFW(buf,0) = 0x8e;
+ WBUFW(buf, 2) = len_mes + 4;
+ memcpy(WBUFP(buf,4), mes, len_mes);
+ clif_send(buf, WBUFW(buf,2), &sd->bl, SELF);
+
+ return 0;
+}
+
+/*==========================
+ * Minimap fix [Kevin]
+ * Remove dot from minimap
+ *--------------------------
+*/
+int clif_party_xy_remove(struct map_session_data *sd)
+{
+ unsigned char buf[16];
+ nullpo_retr(0, sd);
+ WBUFW(buf,0)=0x107;
+ WBUFL(buf,2)=sd->status.account_id;
+ WBUFW(buf,6)=-1;
+ WBUFW(buf,8)=-1;
+ clif_send(buf,packet_len_table[0x107],&sd->bl,PARTY_SAMEMAP_WOS);
+ return 0;
+}
+
+/*==========================================
+ * Info about Star Glaldiator save map [Komurka]
+ *------------------------------------------
+ */
+void clif_feel_info(struct map_session_data *sd, int feel_level)
+{
+ int fd=sd->fd;
+ WFIFOHEAD(fd,packet_len_table[0x20e]);
+ WFIFOW(fd,0)=0x20e;
+ memcpy(WFIFOP(fd,2),mapindex_id2name(sd->feel_map[feel_level].index), MAP_NAME_LENGTH);
+ WFIFOL(fd,26)=sd->bl.id;
+ WFIFOW(fd,30)=0x100+feel_level;
+ WFIFOSET(fd, packet_len_table[0x20e]);
+}
+
+/*==========================================
+ * Info about Star Glaldiator hate mob [Komurka]
+ *------------------------------------------
+ */
+void clif_hate_mob(struct map_session_data *sd, int skilllv,int mob_id)
+{
+ int fd=sd->fd;
+ WFIFOHEAD(fd,packet_len_table[0x20e]);
+ WFIFOW(fd,0)=0x20e;
+ if (pcdb_checkid(mob_id))
+ strncpy(WFIFOP(fd,2),job_name(mob_id), NAME_LENGTH);
+ else if (mobdb_checkid(mob_id))
+ strncpy(WFIFOP(fd,2),mob_db(mob_id)->jname, NAME_LENGTH);
+ else //Really shouldn't happen...
+ memset(WFIFOP(fd,2), 0, NAME_LENGTH);
+ WFIFOL(fd,26)=sd->bl.id;
+ WFIFOW(fd,30)=0xa00+skilllv-1;
+ WFIFOSET(fd, packet_len_table[0x20e]);
+}
+
+/*==========================================
+ * Info about TaeKwon Do TK_MISSION mob [Skotlex]
+ *------------------------------------------
+ */
+void clif_mission_mob(struct map_session_data *sd, unsigned short mob_id, unsigned short progress)
+{
+ int fd=sd->fd;
+ WFIFOHEAD(fd,packet_len_table[0x20e]);
+ WFIFOW(fd,0)=0x20e;
+ strncpy(WFIFOP(fd,2),mob_db(mob_id)->jname, NAME_LENGTH);
+ WFIFOL(fd,26)=mob_id;
+ WFIFOW(fd,30)=0x1400+progress; //Message to display
+ WFIFOSET(fd, packet_len_table[0x20e]);
+}
+
+// ---------------------
+// clif_guess_PacketVer
+// ---------------------
+// Parses a WantToConnection packet to try to identify which is the packet version used. [Skotlex]
+static int clif_guess_PacketVer(int fd, int get_previous)
+{
+ static int packet_ver = -1;
+ int cmd, packet_len, value; //Value is used to temporarily store account/char_id/sex
+ RFIFOHEAD(fd);
+
+ if (get_previous) //For quick reruns, since the normal code flow is to fetch this once to identify the packet version, then again in the wanttoconnect function. [Skotlex]
+ return packet_ver;
+
+ //By default, start searching on the default one.
+ packet_ver = clif_config.packet_db_ver;
+ cmd = RFIFOW(fd,0);
+ packet_len = RFIFOREST(fd);
+
+ if (
+ cmd == clif_config.connect_cmd[packet_ver] &&
+ packet_len == packet_db[packet_ver][cmd].len &&
+ ((value = RFIFOB(fd, packet_db[packet_ver][cmd].pos[4])) == 0 || value == 1) &&
+ (value = RFIFOL(fd, packet_db[packet_ver][cmd].pos[0])) > 700000 && //Account ID is valid
+ value <= max_account_id &&
+ (value = RFIFOL(fd, packet_db[packet_ver][cmd].pos[1])) > 0 && //Char ID is valid
+ value <= max_char_id &&
+ (int)RFIFOL(fd, packet_db[packet_ver][cmd].pos[2]) > 0 //Login 1 is a positive value (?)
+ )
+ return clif_config.packet_db_ver; //Default packet version found.
+
+ for (packet_ver = MAX_PACKET_VER; packet_ver > 0; packet_ver--)
+ { //Start guessing the version, giving priority to the newer ones. [Skotlex]
+ if (cmd != clif_config.connect_cmd[packet_ver] || //it is not a wanttoconnection for this version.
+ packet_len != packet_db[packet_ver][cmd].len) //The size of the wantoconnection packet does not matches.
+ continue;
+
+ if (
+ (value = RFIFOL(fd, packet_db[packet_ver][cmd].pos[0])) < 700000 || value > max_account_id
+ || (value = RFIFOL(fd, packet_db[packet_ver][cmd].pos[1])) < 1 || value > max_char_id
+ //What is login 1? In my tests it is a very very high value.
+ || (int)RFIFOL(fd, packet_db[packet_ver][cmd].pos[2]) < 1
+ //This check seems redundant, all wanttoconnection packets have the gender on the very
+ //last byte of the packet.
+ || (value = RFIFOB(fd, packet_db[packet_ver][cmd].pos[4])) < 0 || value > 1
+ )
+ continue;
+
+ return packet_ver; //This is our best guess.
+ }
+ packet_ver = -1;
+ return -1;
+}
+
+// ------------
+// clif_parse_*
+// ------------
+// パケット読み取って色々操作
+/*==========================================
+ *
+ *------------------------------------------
+ */
+void clif_parse_WantToConnection(int fd, struct map_session_data *sd)
+{
+ struct map_session_data *old_sd;
+ int cmd, account_id, char_id, login_id1, sex;
+ unsigned int client_tick; //The client tick is a tick, therefore it needs be unsigned. [Skotlex]
+ int packet_ver; // 5: old, 6: 7july04, 7: 13july04, 8: 26july04, 9: 9aug04/16aug04/17aug04, 10: 6sept04, 11: 21sept04, 12: 18oct04, 13: 25oct04 (by [Yor])
+ RFIFOHEAD(fd);
+
+ if (sd) {
+ if (battle_config.error_log)
+ ShowError("clif_parse_WantToConnection : invalid request (character already logged in)?\n");
+ return;
+ }
+
+ packet_ver = clif_guess_PacketVer(fd, 1);
+ cmd = RFIFOW(fd,0);
+
+ if (packet_ver > 0)
+ {
+ account_id = RFIFOL(fd, packet_db[packet_ver][cmd].pos[0]);
+ char_id = RFIFOL(fd, packet_db[packet_ver][cmd].pos[1]);
+ login_id1 = RFIFOL(fd, packet_db[packet_ver][cmd].pos[2]);
+ client_tick = RFIFOL(fd, packet_db[packet_ver][cmd].pos[3]);
+ sex = RFIFOB(fd, packet_db[packet_ver][cmd].pos[4]);
+
+ if ((old_sd = map_id2sd(account_id)) != NULL)
+ { // if same account already connected, we disconnect the 2 sessions
+ //Check for characters with no connection (includes those that are using autotrade) [durf],[Skotlex]
+ if (!old_sd->fd)
+ map_quit(old_sd);
+ else
+ clif_authfail_fd(old_sd->fd, 2); // same id
+ clif_authfail_fd(fd, 8); // still recognizes last connection
+ } else {
+ sd = (struct map_session_data*)aCalloc(1, sizeof(struct map_session_data));
+
+ sd->fd = fd;
+ sd->packet_ver = packet_ver;
+ session[fd]->session_data = sd;
+
+ pc_setnewpc(sd, account_id, char_id, login_id1, client_tick, sex, fd);
+ WFIFOHEAD(fd,4);
+ WFIFOL(fd,0) = sd->bl.id;
+ WFIFOSET(fd,4);
+
+ chrif_authreq(sd);
+ }
+ }
+ return;
+}
+
+/*==========================================
+ * 007d クライアント側マップ読み込み完了
+ * map侵入時に必要なデータを全て送りつける
+ *------------------------------------------
+ */
+void clif_parse_LoadEndAck(int fd,struct map_session_data *sd)
+{
+ if(sd->bl.prev != NULL)
+ return;
+
+ if(sd->npc_id) npc_event_dequeue(sd);
+ clif_skillinfoblock(sd);
+ pc_checkitem(sd);
+
+ // loadendack時
+ // next exp
+ clif_updatestatus(sd,SP_NEXTBASEEXP);
+ clif_updatestatus(sd,SP_NEXTJOBEXP);
+ // skill point
+ clif_updatestatus(sd,SP_SKILLPOINT);
+ // item
+ clif_itemlist(sd);
+ clif_equiplist(sd);
+ // cart
+ if(pc_iscarton(sd)){
+ clif_cart_itemlist(sd);
+ clif_cart_equiplist(sd);
+ clif_updatestatus(sd,SP_CARTINFO);
+ }
+ // param all
+ clif_initialstatus(sd);
+ // party
+ party_send_movemap(sd);
+ // guild
+ guild_send_memberinfoshort(sd,1);
+
+ if(battle_config.pc_invincible_time > 0) {
+ if(map_flag_gvg(sd->bl.m))
+ pc_setinvincibletimer(sd,battle_config.pc_invincible_time<<1);
+ else
+ pc_setinvincibletimer(sd,battle_config.pc_invincible_time);
+ }
+
+ map_addblock(&sd->bl); // ブロック登録
+ clif_spawnpc(sd); // spawn
+
+ // weight max , now
+ clif_updatestatus(sd,SP_MAXWEIGHT);
+ clif_updatestatus(sd,SP_WEIGHT);
+
+ // Show hp after displacement [LuzZza]
+ if(sd->status.party_id)
+ clif_party_hp(sd);
+
+ // set flag, if it's a duel [LuzZza]
+ if(sd->duel_group) {
+ clif_set0199(fd, 1);
+ //clif_misceffect2(&sd->bl, 159);
+ }
+
+ // pvp
+ //if(sd->pvp_timer!=-1 && !battle_config.pk_mode) /PVP Client crash fix* Removed timer deletion
+ // delete_timer(sd->pvp_timer,pc_calc_pvprank_timer);
+ if(map[sd->bl.m].flag.pvp){
+ if(!battle_config.pk_mode) { // remove pvp stuff for pk_mode [Valaris]
+ if (sd->pvp_timer == -1)
+ sd->pvp_timer=add_timer(gettick()+200,pc_calc_pvprank_timer,sd->bl.id,0);
+ sd->pvp_rank=0;
+ sd->pvp_lastusers=0;
+ sd->pvp_point=5;
+ sd->pvp_won=0;
+ sd->pvp_lost=0;
+ }
+ clif_set0199(sd->fd,1);
+ } else {
+ sd->pvp_timer=-1;
+ }
+ if(map_flag_gvg(sd->bl.m))
+ clif_set0199(sd->fd,3);
+
+ // pet
+ if(sd->status.pet_id > 0 && sd->pd && sd->pet.intimate > 0) {
+ map_addblock(&sd->pd->bl);
+ clif_spawnpet(sd->pd);
+ clif_send_petdata(sd,0,0);
+ clif_send_petdata(sd,5,battle_config.pet_hair_style);
+ clif_send_petstatus(sd);
+ }
+
+ if(sd->state.connect_new) {
+ sd->state.connect_new = 0;
+ if(sd->status.class_ != sd->view_class)
+ clif_changelook(&sd->bl,LOOK_BASE,sd->view_class);
+ if(sd->status.pet_id > 0 && sd->pd && sd->pet.intimate > 900)
+ clif_pet_emotion(sd->pd,(sd->pd->class_ - 100)*100 + 50 + pet_hungry_val(sd));
+
+/* Stop players from spawning inside castles [Valaris] */
+ {
+ struct guild_castle *gc=guild_mapname2gc(map[sd->bl.m].name);
+ if (gc)
+ pc_setpos(sd,sd->status.save_point.map,sd->status.save_point.x,sd->status.save_point.y,2);
+ }
+/* End Addition [Valaris] */
+ }
+
+ // view equipment item
+#if PACKETVER < 4
+ clif_changelook(&sd->bl,LOOK_WEAPON,sd->status.weapon);
+ clif_changelook(&sd->bl,LOOK_SHIELD,sd->status.shield);
+#else
+ clif_changelook(&sd->bl,LOOK_WEAPON,0);
+#endif
+ if(battle_config.save_clothcolor && sd->status.clothes_color > 0 && ((sd->view_class != JOB_WEDDING && sd->view_class !=JOB_XMAS) ||
+ (sd->view_class==JOB_WEDDING && !battle_config.wedding_ignorepalette) || (sd->view_class==JOB_XMAS && !battle_config.xmas_ignorepalette)))
+ clif_changelook(&sd->bl,LOOK_CLOTHES_COLOR,sd->status.clothes_color);
+
+ /* There shouldn't be a need for this anymore because... [Skotlex]
+ * 1. sc_data is saved and loaded now.
+ * 2. if it fails (sc_data is not being saved?) players can just reuse the skill.
+ //if(sd->status.hp<sd->status.max_hp>>2 && pc_checkskill(sd,SM_AUTOBERSERK)>0 &&
+ if(sd->status.hp<sd->status.max_hp>>2 && sd->sc_data[SC_AUTOBERSERK].timer != -1 &&
+ (sd->sc_data[SC_PROVOKE].timer==-1 || sd->sc_data[SC_PROVOKE].val2==0 ))
+ // オートバーサーク発動
+ status_change_start(&sd->bl,SC_PROVOKE,10,1,0,0,0,0);
+ */
+ if(battle_config.muting_players && sd->status.manner < 0 && battle_config.manner_system)
+ status_change_start(&sd->bl,SC_NOCHAT,0,0,0,0,0,0);
+
+// Lance
+ if (script_config.event_script_type == 0) {
+ struct npc_data *npc;
+ if ((npc = npc_name2id(script_config.loadmap_event_name))) {
+ run_script(npc->u.scr.script,0,sd->bl.id,npc->bl.id);
+ ShowStatus("Event '"CL_WHITE"%s"CL_RESET"' executed.\n", script_config.loadmap_event_name);
+ }
+ } else {
+ ShowStatus("%d '"CL_WHITE"%s"CL_RESET"' events executed.\n",
+ npc_event_doall_id(script_config.loadmap_event_name, sd->bl.id), script_config.loadmap_event_name);
+ }
+/* These should not be needed anymore. [Skotlex]
+ * - the option is sent on every player packet, why send it?
+ * - There should be no need to do a signum check on map change, it is done on equipment change.
+ * - Trick-dead is finished on pc_setpos
+ * - Night effect is handled on clif_spawnpc
+ // option
+ clif_changeoption(&sd->bl);
+ if(sd->sc_data[SC_TRICKDEAD].timer != -1)
+ status_change_end(&sd->bl,SC_TRICKDEAD,-1);
+ if(sd->sc_data[SC_SIGNUMCRUCIS].timer != -1 && !battle_check_undead(7,sd->def_ele))
+ status_change_end(&sd->bl,SC_SIGNUMCRUCIS,-1);
+ if(sd->special_state.infinite_endure && sd->sc_data[SC_ENDURE].timer == -1)
+ status_change_start(&sd->bl,SC_ENDURE,10,1,0,0,0,0);
+
+ // Required to eliminate glow bugs because it's executed before clif_changeoption [Lance]
+ //New 'night' effect by dynamix [Skotlex]
+ if (night_flag && map[sd->bl.m].flag.nightenabled)
+ { //Display night.
+ if (sd->state.night) //It must be resent because otherwise players get this annoying aura...
+ clif_status_load(&sd->bl, SI_NIGHT, 0);
+ else
+ sd->state.night = 1;
+ clif_status_load(&sd->bl, SI_NIGHT, 1);
+ } else if (sd->state.night) { //Clear night display.
+ clif_status_load(&sd->bl, SI_NIGHT, 0);
+ sd->state.night = 0;
+ }
+*/
+ if (pc_checkskill(sd,SG_KNOWLEDGE) ||
+ pc_checkskill(sd,SG_SUN_COMFORT) ||
+ pc_checkskill(sd,SG_MOON_COMFORT) ||
+ pc_checkskill(sd,SG_STAR_COMFORT))
+ status_calc_pc(sd,0);
+
+ if (pc_checkskill(sd, SG_DEVIL) && sd->status.job_level >= battle_config.max_job_level)
+ clif_status_load(&sd->bl, SI_DEVIL, 1); //blindness [Komurka]
+
+ map_foreachinarea(clif_getareachar,sd->bl.m,sd->bl.x-AREA_SIZE,sd->bl.y-AREA_SIZE,sd->bl.x+AREA_SIZE,sd->bl.y+AREA_SIZE,BL_ALL,sd);
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+void clif_parse_TickSend(int fd, struct map_session_data *sd) {
+ RFIFOHEAD(fd);
+
+ sd->client_tick=RFIFOL(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[0]);
+ sd->server_tick = gettick();
+ clif_servertick(sd);
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+void clif_parse_WalkToXY(int fd, struct map_session_data *sd) {
+ int x, y;
+ int cmd;
+ RFIFOHEAD(fd);
+
+ if (pc_isdead(sd)) {
+ clif_clearchar_area(&sd->bl, 1);
+ return;
+ }
+
+ if (pc_issit(sd)) //No walking when you are sit!
+ return;
+
+ if (sd->npc_id != 0 || sd->vender_id != 0 || sd->state.storage_flag)
+ return;
+
+ if (sd->skilltimer != -1 && pc_checkskill(sd, SA_FREECAST) <= 0) // フリーキャスト
+ return;
+
+ if (sd->chatID)
+ return;
+
+ if (!pc_can_move(sd))
+ return;
+
+ if(sd->sc_data && sd->sc_data[SC_RUN].timer != -1)
+ return;
+
+ if (sd->invincible_timer != -1)
+ pc_delinvincibletimer(sd);
+
+ pc_stopattack(sd);
+
+ cmd = RFIFOW(fd,0);
+ x = RFIFOB(fd,packet_db[sd->packet_ver][cmd].pos[0]) * 4 +
+ (RFIFOB(fd,packet_db[sd->packet_ver][cmd].pos[0] + 1) >> 6);
+ y = ((RFIFOB(fd,packet_db[sd->packet_ver][cmd].pos[0]+1) & 0x3f) << 4) +
+ (RFIFOB(fd,packet_db[sd->packet_ver][cmd].pos[0] + 2) >> 4);
+ //Set last idle time... [Skotlex]
+ sd->idletime = last_tick;
+
+ pc_walktoxy(sd, x, y);
+
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+void clif_parse_QuitGame(int fd, struct map_session_data *sd) {
+// unsigned int tick=gettick();
+// struct skill_unit_group* sg;
+
+ WFIFOHEAD(fd,packet_len_table[0x18b]);
+ WFIFOW(fd,0) = 0x18b;
+
+ /* Rovert's prevent logout option fixed [Valaris] */
+ if (sd->sc_data[SC_CLOAKING].timer==-1 && sd->sc_data[SC_HIDING].timer==-1 &&
+ (!battle_config.prevent_logout || DIFF_TICK(gettick(), sd->canlog_tick) > battle_config.prevent_logout)
+ ) {
+ clif_setwaitclose(fd);
+ WFIFOW(fd,2)=0;
+ } else {
+ WFIFOW(fd,2)=1;
+ }
+ WFIFOSET(fd,packet_len_table[0x18b]);
+}
+
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+void check_fake_id(int fd, struct map_session_data *sd, int target_id) {
+ // if player asks for the fake player (only bot and modified client can see a hiden player)
+/* if (target_id == server_char_id) {
+ char message_to_gm[strlen(msg_txt(536)) + strlen(msg_txt(540)) + strlen(msg_txt(507)) + strlen(msg_txt(508))];
+ sprintf(message_to_gm, msg_txt(536), sd->status.name, sd->status.account_id); // Character '%s' (account: %d) try to use a bot (it tries to detect a fake player).
+ intif_wis_message_to_gm(wisp_server_name, battle_config.hack_info_GM_level, message_to_gm);
+ // if we block people
+ if (battle_config.ban_bot < 0) {
+ chrif_char_ask_name(-1, sd->status.name, 1, 0, 0, 0, 0, 0, 0); // type: 1 - block
+ clif_setwaitclose(sd->fd); // forced to disconnect because of the hack
+ // message about the ban
+ sprintf(message_to_gm, msg_txt(540)); // This player has been definitivly blocked.
+ // if we ban people
+ } else if (battle_config.ban_bot > 0) {
+ chrif_char_ask_name(-1, sd->status.name, 2, 0, 0, 0, 0, battle_config.ban_bot, 0); // type: 2 - ban (year, month, day, hour, minute, second)
+ clif_setwaitclose(sd->fd); // forced to disconnect because of the hack
+ // message about the ban
+ sprintf(message_to_gm, msg_txt(507), battle_config.ban_bot); // This player has been banned for %d minute(s).
+ } else { // impossible to display: we don't send fake player if battle_config.ban_bot is == 0
+ // message about the ban
+ sprintf(message_to_gm, msg_txt(508)); // This player hasn't been banned (Ban option is disabled).
+ }
+ intif_wis_message_to_gm(wisp_server_name, battle_config.hack_info_GM_level, message_to_gm);
+ // send this info cause the bot ask until get an answer, damn spam
+ memset(WPACKETP(0), 0, packet_len_table[0x95]);
+ WPACKETW(0) = 0x95;
+ WPACKETL(2) = server_char_id;
+ strncpy(WPACKETP(6), sd->status.name, 24);
+ SENDPACKET(fd, packet_len_table[0x95]);
+ // take fake player out of screen
+ WPACKETW(0) = 0x80;
+ WPACKETL(2) = server_char_id;
+ WPACKETB(6) = 0;
+ SENDPACKET(fd, packet_len_table[0x80]);
+ // take fake mob out of screen
+ WPACKETW(0) = 0x80;
+ WPACKETL(2) = server_fake_mob_id;
+ WPACKETB(6) = 0;
+ SENDPACKET(fd, packet_len_table[0x80]);
+ }
+
+ // if player asks for the fake mob (only bot and modified client can see a hiden mob)
+ if (target_id == server_fake_mob_id) {
+ int fake_mob;
+ char message_to_gm[strlen(msg_txt(537)) + strlen(msg_txt(540)) + strlen(msg_txt(507)) + strlen(msg_txt(508))];
+ sprintf(message_to_gm, msg_txt(537), sd->status.name, sd->status.account_id); // Character '%s' (account: %d) try to use a bot (it tries to detect a fake mob).
+ intif_wis_message_to_gm(wisp_server_name, battle_config.hack_info_GM_level, message_to_gm);
+ // if we block people
+ if (battle_config.ban_bot < 0) {
+ chrif_char_ask_name(-1, sd->status.name, 1, 0, 0, 0, 0, 0, 0); // type: 1 - block
+ clif_setwaitclose(sd->fd); // forced to disconnect because of the hack
+ // message about the ban
+ sprintf(message_to_gm, msg_txt(540)); // This player has been definitivly blocked.
+ // if we ban people
+ } else if (battle_config.ban_bot > 0) {
+ chrif_char_ask_name(-1, sd->status.name, 2, 0, 0, 0, 0, battle_config.ban_bot, 0); // type: 2 - ban (year, month, day, hour, minute, second)
+ clif_setwaitclose(sd->fd); // forced to disconnect because of the hack
+ // message about the ban
+ sprintf(message_to_gm, msg_txt(507), battle_config.ban_bot); // This player has been banned for %d minute(s).
+ } else { // impossible to display: we don't send fake player if battle_config.ban_bot is == 0
+ // message about the ban
+ sprintf(message_to_gm, msg_txt(508)); // This player hasn't been banned (Ban option is disabled).
+ }
+ intif_wis_message_to_gm(wisp_server_name, battle_config.hack_info_GM_level, message_to_gm);
+ // send this info cause the bot ask until get an answer, damn spam
+ memset(WPACKETP(0), 0, packet_len_table[0x95]);
+ WPACKETW(0) = 0x95;
+ WPACKETL(2) = server_fake_mob_id;
+ fake_mob = fake_mob_list[(sd->bl.m + sd->fd + sd->status.char_id) % (sizeof(fake_mob_list) / sizeof(fake_mob_list[0]))]; // never same mob
+ if (!mobdb_checkid(fake_mob))
+ fake_mob = 1002; // poring (default)
+ strncpy(WPACKETP(6), mob_db[fake_mob].name, 24);
+ SENDPACKET(fd, packet_len_table[0x95]);
+ // take fake mob out of screen
+ WPACKETW(0) = 0x80;
+ WPACKETL(2) = server_fake_mob_id;
+ WPACKETB(6) = 0;
+ SENDPACKET(fd, packet_len_table[0x80]);
+ // take fake player out of screen
+ WPACKETW(0) = 0x80;
+ WPACKETL(2) = server_char_id;
+ WPACKETB(6) = 0;
+ SENDPACKET(fd, packet_len_table[0x80]);
+ }
+*/
+ return;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+void clif_parse_GetCharNameRequest(int fd, struct map_session_data *sd) {
+ int account_id;
+ struct block_list* bl;
+ RFIFOHEAD(fd);
+
+ account_id = RFIFOL(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[0]);
+ if(account_id<0) // for disguises [Valaris]
+ account_id-=account_id*2;
+
+ //Is this possible? Lagged clients could request names of already gone mobs/players. [Skotlex]
+ if ((bl = map_id2bl(account_id)) != NULL)
+ clif_charnameack(fd, bl);
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+void clif_parse_GlobalMessage(int fd, struct map_session_data *sd) { // S 008c <len>.w <str>.?B
+ char *message;
+ unsigned char *buf;
+ RFIFOHEAD(fd);
+
+ message = (char*)RFIFOP(fd,4);
+ if (strlen(message) < strlen(sd->status.name) || //If the incoming string is too short...
+ strncmp(message, sd->status.name, strlen(sd->status.name)) != 0) //Or the name does not matches...
+ {
+ ShowWarning("Hack on global message: character '%s' (account: %d), use an other name to send a (normal) message.\n", sd->status.name, sd->status.account_id);
+ message = (char*)aCallocA(256, sizeof(char));
+ // information is sended to all online GM
+ sprintf(message, "Hack on global message (normal message): character '%s' (account: %d) uses another name.", sd->status.name, sd->status.account_id);
+ intif_wis_message_to_gm(wisp_server_name, battle_config.hack_info_GM_level, message);
+
+ if (strlen((char*)RFIFOP(fd,4)) == 0)
+ strcpy(message, " This player sends a void name and a void message.");
+ else
+ snprintf(message, 255, " This player sends (name:message): '%128s'.", RFIFOP(fd,4));
+ intif_wis_message_to_gm(wisp_server_name, battle_config.hack_info_GM_level, message);
+ // message about the ban
+ if (battle_config.ban_spoof_namer > 0)
+ sprintf(message, " This player has been banned for %d minute(s).", battle_config.ban_spoof_namer);
+ else
+ sprintf(message, " This player hasn't been banned (Ban option is disabled).");
+ intif_wis_message_to_gm(wisp_server_name, battle_config.hack_info_GM_level, message);
+
+ // if we ban people
+ if (battle_config.ban_spoof_namer > 0) {
+ chrif_char_ask_name(-1, sd->status.name, 2, 0, 0, 0, 0, battle_config.ban_spoof_namer, 0); // type: 2 - ban (year, month, day, hour, minute, second)
+ clif_setwaitclose(fd); // forced to disconnect because of the hack
+ }
+ else
+ session[fd]->eof = 1; //Disconnect them too, bad packets can cause problems down the road. [Skotlex]
+
+ if(message) aFree(message);
+ return;
+ }
+
+ if ((is_atcommand(fd, sd, message, 0) != AtCommand_None) ||
+ (is_charcommand(fd, sd, message,0) != CharCommand_None) ||
+ (sd->sc_data &&
+ (sd->sc_data[SC_BERSERK].timer != -1 || //バーサーク時は会話も不可
+ sd->sc_data[SC_NOCHAT].timer != -1 ))) //チャット禁止
+ return;
+
+ buf = (unsigned char*)aCallocA(RFIFOW(fd,2) + 4, sizeof(char));
+
+ // send message to others
+ WBUFW(buf,0) = 0x8d;
+ WBUFW(buf,2) = RFIFOW(fd,2) + 4; // len of message - 4 + 8
+ WBUFL(buf,4) = sd->bl.id;
+ memcpy(WBUFP(buf,8), RFIFOP(fd,4), RFIFOW(fd,2) - 4);
+ clif_send(buf, WBUFW(buf,2), &sd->bl, sd->chatID ? CHAT_WOS : AREA_CHAT_WOC);
+
+ // send back message to the speaker
+ WFIFOHEAD(fd, RFIFOW(fd,2) + 4);
+ memcpy(WFIFOP(fd,0), RFIFOP(fd,0), RFIFOW(fd,2));
+ WFIFOW(fd,0) = 0x8e;
+ WFIFOSET(fd, WFIFOW(fd,2));
+
+#ifdef PCRE_SUPPORT
+ map_foreachinarea(npc_chat_sub, sd->bl.m, sd->bl.x-AREA_SIZE, sd->bl.y-AREA_SIZE, sd->bl.x+AREA_SIZE, sd->bl.y+AREA_SIZE, BL_NPC, RFIFOP(fd,4), strlen(RFIFOP(fd,4)), &sd->bl);
+#endif
+
+ // Celest
+ if ((sd->class_&MAPID_UPPERMASK) == MAPID_SUPER_NOVICE) { //Super Novice.
+ int next = pc_nextbaseexp(sd);
+ char *rfifo = (char*)RFIFOP(fd,4);
+ if (next > 0 && (sd->status.base_exp * 1000 / next)% 100 == 0) {
+ if (sd->state.snovice_flag == 0 && strstr(rfifo, msg_txt(504)))
+ sd->state.snovice_flag = 1;
+ else if (sd->state.snovice_flag == 1) {
+ message = (char*)aCallocA(128, sizeof(char));
+ sprintf(message, msg_txt(505), sd->status.name);
+ if (strstr(rfifo, message))
+ sd->state.snovice_flag = 2;
+ aFree(message);
+ }
+ else if (sd->state.snovice_flag == 2 && strstr(rfifo, msg_txt(506)))
+ sd->state.snovice_flag = 3;
+ else if (sd->state.snovice_flag == 3) {
+ clif_skill_nodamage(&sd->bl,&sd->bl,MO_EXPLOSIONSPIRITS,-1,1);
+ status_change_start(&sd->bl,SkillStatusChangeTable[MO_EXPLOSIONSPIRITS],
+ 17,0,0,0,skill_get_time(MO_EXPLOSIONSPIRITS,1),0 ); //Lv17-> +50 critical (noted by Poki) [Skotlex]
+ sd->state.snovice_flag = 0;
+ }
+ }
+ }
+
+ if(buf) aFree(buf);
+
+ return;
+}
+
+int clif_message(struct block_list *bl, char* msg)
+{
+ unsigned short msg_len = strlen(msg) + 1;
+ unsigned char buf[256];
+
+ nullpo_retr(0, bl);
+
+ WBUFW(buf, 0) = 0x8d;
+ WBUFW(buf, 2) = msg_len + 8;
+ WBUFL(buf, 4) = bl->id;
+ memcpy(WBUFP(buf, 8), msg, msg_len);
+
+ clif_send(buf, WBUFW(buf,2), bl, AREA_CHAT_WOC); // by Gengar
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+void clif_parse_MapMove(int fd, struct map_session_data *sd) {
+// /m /mapmove (as @rura GM command)
+ char output[30]; // 17+4+4=26, 30 max.
+ char map_name[MAP_NAME_LENGTH]; //Err... map names are 15+'\0' in size, not 16+'\0' [Skotlex]
+ RFIFOHEAD(fd);
+
+ if ((battle_config.atc_gmonly == 0 || pc_isGM(sd)) &&
+ (pc_isGM(sd) >= get_atcommand_level(AtCommand_MapMove))) {
+ memcpy(map_name, RFIFOP(fd,2), MAP_NAME_LENGTH-1);
+ map_name[MAP_NAME_LENGTH-1]='\0';
+ sprintf(output, "%s %d %d", map_name, RFIFOW(fd,18), RFIFOW(fd,20));
+ atcommand_rura(fd, sd, "@rura", output);
+ }
+
+ return;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+void clif_changed_dir(struct block_list *bl) {
+ unsigned char buf[64];
+ struct map_session_data *sd = NULL;
+
+ if (bl->type == BL_PC)
+ nullpo_retv (sd=(struct map_session_data *)bl);
+
+ WBUFW(buf,0) = 0x9c;
+ WBUFL(buf,2) = bl->id;
+ if (sd)
+ WBUFW(buf,6) = sd->head_dir;
+ WBUFB(buf,8) = status_get_dir(bl);
+
+ clif_send(buf, packet_len_table[0x9c], bl, AREA_WOS);
+
+ if(sd && sd->disguise) {
+ WBUFL(buf,2) = -bl->id;
+ WBUFW(buf,6) = 0;
+ WBUFB(buf,8) = status_get_dir(bl);
+ clif_send(buf, packet_len_table[0x9c], bl, AREA);
+ }
+
+ return;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+void clif_parse_ChangeDir(int fd, struct map_session_data *sd) {
+ unsigned char headdir, dir;
+ RFIFOHEAD(fd);
+
+ headdir = RFIFOB(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[0]);
+ dir = RFIFOB(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[1]);
+ pc_setdir(sd, dir, headdir);
+
+ clif_changed_dir(&sd->bl);
+ return;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+void clif_parse_Emotion(int fd, struct map_session_data *sd) {
+ unsigned char buf[64];
+ RFIFOHEAD(fd);
+
+ if (battle_config.basic_skill_check == 0 || pc_checkskill(sd, NV_BASIC) >= 2) {
+ if (RFIFOB(fd,2) == 34) {// prevent use of the mute emote [Valaris]
+ clif_skill_fail(sd, 1, 0, 1);
+ return;
+ }
+ // fix flood of emotion icon (ro-proxy): flood only the hacker player
+ if (sd->emotionlasttime >= time(NULL)) {
+ sd->emotionlasttime = time(NULL) + 1; // not more than 1 per second (using /commands the client can spam it)
+ clif_skill_fail(sd, 1, 0, 1);
+ return;
+ }
+ sd->emotionlasttime = time(NULL) + 1; // not more than 1 per second (using /commands the client can spam it)
+
+ WBUFW(buf,0) = 0xc0;
+ WBUFL(buf,2) = sd->bl.id;
+ WBUFB(buf,6) = RFIFOB(fd,2);
+ clif_send(buf, packet_len_table[0xc0], &sd->bl, AREA);
+ } else
+ clif_skill_fail(sd, 1, 0, 1);
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+void clif_parse_HowManyConnections(int fd, struct map_session_data *sd) {
+ WFIFOHEAD(fd,packet_len_table[0xc2]);
+ WFIFOW(fd,0) = 0xc2;
+ WFIFOL(fd,2) = map_getusers();
+ WFIFOSET(fd,packet_len_table[0xc2]);
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+void clif_parse_ActionRequest(int fd, struct map_session_data *sd) {
+ unsigned int tick;
+ unsigned char buf[64];
+ int action_type, target_id;
+ RFIFOHEAD(fd);
+
+ if (pc_isdead(sd)) {
+ clif_clearchar_area(&sd->bl, 1);
+ return;
+ }
+ if (sd->npc_id != 0 || sd->opt1 > 0 || sd->status.option & 2 || sd->state.storage_flag ||
+ (sd->sc_data &&
+ (sd->sc_data[SC_TRICKDEAD].timer != -1 ||
+ sd->sc_data[SC_AUTOCOUNTER].timer != -1 || //オートカウンター
+ sd->sc_data[SC_BLADESTOP].timer != -1 || //白刃取り
+ sd->sc_data[SC_DANCING].timer != -1))) //ダンス中
+ return;
+
+ tick = gettick();
+
+ pc_stop_walking(sd, 0);
+ pc_stopattack(sd);
+
+ target_id = RFIFOL(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[0]);
+ action_type = RFIFOB(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[1]);
+ //Regardless of what they have to do, they have just requested an action, no longer idle. [Skotlex]
+ sd->idletime = last_tick;
+
+ if(target_id<0) // for disguises [Valaris]
+ target_id-=(target_id*2);
+
+ switch(action_type) {
+ case 0x00: // once attack
+ case 0x07: // continuous attack
+ if(sd->sc_data[SC_WEDDING].timer != -1 || sd->view_class==JOB_WEDDING)
+ return;
+ if(sd->sc_data[SC_XMAS].timer != -1 || sd->view_class==JOB_XMAS)
+ return;
+ if (sd->vender_id != 0)
+ return;
+ if (!battle_config.sdelay_attack_enable && pc_checkskill(sd, SA_FREECAST) <= 0) {
+ if (DIFF_TICK(tick, sd->canact_tick) < 0) {
+ clif_skill_fail(sd, 1, 4, 0);
+ return;
+ }
+ }
+ if (sd->invincible_timer != -1)
+ pc_delinvincibletimer(sd);
+ pc_attack(sd, target_id, action_type != 0);
+ break;
+ case 0x02: // sitdown
+ if (battle_config.basic_skill_check == 0 || pc_checkskill(sd, NV_BASIC) >= 3) {
+ if (sd->skilltimer != -1) //No sitting while casting :P
+ break;
+ pc_stopattack(sd);
+ pc_stop_walking(sd, 1);
+ pc_setsit(sd);
+ skill_gangsterparadise(sd, 1); // ギャングスターパラダイス設定 fixed Valaris
+ skill_rest(sd, 1); // TK_HPTIME sitting down mode [Dralnu]
+ clif_sitting(sd);
+ } else
+ clif_skill_fail(sd, 1, 0, 2);
+ break;
+ case 0x03: // standup
+ pc_setstand(sd);
+ skill_gangsterparadise(sd, 0); // ギャングスターパラダイス解除 fixed Valaris
+ skill_rest(sd, 0); // TK_HPTIME standing up mode [Dralnu]
+ WBUFW(buf, 0) = 0x8a;
+ WBUFL(buf, 2) = sd->bl.id;
+ WBUFB(buf,26) = 3;
+ clif_send(buf, packet_len_table[0x8a], &sd->bl, AREA);
+ if(sd->disguise) {
+ WBUFL(buf, 2) = -sd->bl.id;
+ clif_send(buf, packet_len_table[0x8a], &sd->bl, AREA);
+ }
+ break;
+ }
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+void clif_parse_Restart(int fd, struct map_session_data *sd) {
+ RFIFOHEAD(fd);
+
+ switch(RFIFOB(fd,2)) {
+ case 0x00:
+ if (pc_isdead(sd)) {
+ pc_setstand(sd);
+ pc_setrestartvalue(sd, 3);
+ pc_setpos(sd, sd->status.save_point.map, sd->status.save_point.x, sd->status.save_point.y, 2);
+ }
+ // in case the player's status somehow wasn't updated yet [Celest]
+ else if (sd->status.hp <= 0)
+ pc_setdead(sd);
+ break;
+ case 0x01:
+ /* Rovert's Prevent logout option - Fixed [Valaris] */
+ if (!battle_config.prevent_logout || DIFF_TICK(gettick(), sd->canlog_tick) > battle_config.prevent_logout)
+ {
+ //map_quit(sd); //A clif_quitsave is sent inmediately after this, so no need to quit yet. [Skotlex]
+ chrif_charselectreq(sd);
+ } else {
+ WFIFOHEAD(fd,packet_len_table[0x18b]);
+ WFIFOW(fd,0)=0x18b;
+ WFIFOW(fd,2)=1;
+
+ WFIFOSET(fd,packet_len_table[0x018b]);
+ }
+ break;
+ }
+}
+
+/*==========================================
+ * Transmission of a wisp (S 0096 <len>.w <nick>.24B <message>.?B)
+ *------------------------------------------
+ */
+void clif_parse_Wis(int fd, struct map_session_data *sd) { // S 0096 <len>.w <nick>.24B <message>.?B // rewritten by [Yor]
+ char *gm_command;
+ struct map_session_data *dstsd;
+ int i=0;
+ struct npc_data *npc;
+ char split_data[10][50];
+ int j=0,k=0;
+ char *whisper_tmp;
+ char output[256];
+ RFIFOHEAD(fd);
+
+ //printf("clif_parse_Wis: message: '%s'.\n", RFIFOP(fd,28));
+
+ gm_command = (char*)aCallocA(strlen((const char*)RFIFOP(fd,28)) + 28, sizeof(char)); // 24+3+(RFIFOW(fd,2)-28)+1 or 24+3+(strlen(RFIFOP(fd,28))+1 (size can be wrong with hacker)
+
+ sprintf(gm_command, "%s : %s", sd->status.name, RFIFOP(fd,28));
+ if ((is_charcommand(fd, sd, gm_command, 0) != CharCommand_None) ||
+ (is_atcommand(fd, sd, gm_command, 0) != AtCommand_None) ||
+ (sd && sd->sc_data &&
+ (sd->sc_data[SC_BERSERK].timer!=-1 || //バーサーク時は会話も不可
+ sd->sc_data[SC_NOCHAT].timer != -1))) //チャット禁止
+ {
+ if(gm_command) aFree(gm_command);
+ return;
+ }
+
+ if(gm_command) aFree(gm_command);
+
+ //Chat Logging type 'W' / Whisper
+ if(log_config.chat&1 //we log everything then
+ || ( log_config.chat&2 //if Whisper bit is on
+ && ( !agit_flag || !(log_config.chat&16) ))) //if WOE ONLY flag is off or AGIT is OFF
+ log_chat("W", 0, sd->status.char_id, sd->status.account_id, (char*)mapindex_id2name(sd->mapindex), sd->bl.x, sd->bl.y, (char*)RFIFOP(fd, 4), (char*)RFIFOP(fd, 28));
+
+
+//-------------------------------------------------------//
+// Lordalfa - Paperboy - To whisper NPC commands //
+//-------------------------------------------------------//
+if ((strncasecmp((const char*)RFIFOP(fd,4),"NPC:",4) == 0) && (strlen((const char*)RFIFOP(fd,4)) >4)) {
+ whisper_tmp = (char*) RFIFOP(fd,4) + 4;
+ if ((npc = npc_name2id(whisper_tmp)))
+ {
+ whisper_tmp=(char *)aCallocA(strlen((char *)(RFIFOP(fd,28))+1),sizeof(char));
+ whisper_tmp[0]=0;
+
+ sprintf(whisper_tmp, "%s", (const char*)RFIFOP(fd,28));
+ for( j=0;whisper_tmp[j]!='\0';j++)
+ {
+ if(whisper_tmp[j]!='#')
+ {
+ split_data[i][j-k]=whisper_tmp[j];
+ }
+ else
+ {
+ split_data[i][j-k]='\0';
+ k=j+1;
+ i++;
+ }
+ } // Splits the message using '#' as separators
+ split_data[i][j-k]='\0';
+
+ aFree(whisper_tmp);
+ whisper_tmp=(char *)aCallocA(15,sizeof(char));
+ whisper_tmp[0]=0;
+
+ for (j=0;j<=10;j++)
+ {
+ sprintf(whisper_tmp, "@whispervar%d$", j);
+ set_var(sd,whisper_tmp,(char *) split_data[j]);
+ }//You don't need to zero them, just reset them [Kevin]
+
+ aFree(whisper_tmp);
+ whisper_tmp=(char *)aCallocA(strlen(npc->name)+18,sizeof(char));
+ whisper_tmp[0]=0;
+
+ sprintf(whisper_tmp, "%s::OnWhisperGlobal", npc->name);
+ npc_event(sd,whisper_tmp,0); // Calls the NPC label
+ return;
+
+ aFree(whisper_tmp); //I rewrote it a little to use memory allocation, a bit more stable =P [Kevin]
+ whisper_tmp = NULL;
+ } //should have just removed the one below that was a my bad =P
+}
+
+ // Main chat [LuzZza]
+ if(strcmpi((const char*)RFIFOP(fd,4), main_chat_nick) == 0) {
+ if(!sd->state.mainchat) {
+ sd->state.mainchat = 1;
+ clif_displaymessage(fd, msg_txt(380)); // Main chat has been activated.
+ }
+ if (sd->sc_data[SC_NOCHAT].timer != -1) {
+ clif_displaymessage(fd, msg_txt(387));
+ return;
+ }
+ sprintf(output, msg_txt(386), sd->status.name, (char *)RFIFOP(fd,28));
+ intif_announce(output, strlen(output) + 1, 0xFE000000, 0);
+ return;
+ }
+
+ // searching destination character
+ dstsd = map_nick2sd((char*)RFIFOP(fd,4));
+ // player is not on this map-server
+ if (dstsd == NULL ||
+ // At this point, don't send wisp/page if it's not exactly the same name, because (example)
+ // if there are 'Test' player on an other map-server and 'test' player on this map-server,
+ // and if we ask for 'Test', we must not contact 'test' player
+ // so, we send information to inter-server, which is the only one which decide (and copy correct name).
+ strcmp(dstsd->status.name, (const char*)RFIFOP(fd,4)) != 0) // not exactly same name
+ // send message to inter-server
+ intif_wis_message(sd, (char*)RFIFOP(fd,4), (char*)RFIFOP(fd,28), RFIFOW(fd,2)-28);
+ // player is on this map-server
+ else {
+ // if you send to your self, don't send anything to others
+ if (dstsd->fd == fd) // but, normaly, it's impossible!
+ clif_wis_message(fd, wisp_server_name, "You can not page yourself. Sorry.", strlen("You can not page yourself. Sorry.") + 1);
+ // otherwise, send message and answer immediatly
+ else {
+ if (dstsd->ignoreAll == 1) {
+ if (dstsd->status.option & OPTION_INVISIBLE && pc_isGM(sd) < pc_isGM(dstsd))
+ clif_wis_end(fd, 1); // 1: target character is not loged in
+ else
+ clif_wis_end(fd, 3); // 3: everyone ignored by target
+ } else {
+ // if player ignore the source character
+ for(i = 0; i < MAX_IGNORE_LIST; i++)
+ if (strcmp(dstsd->ignore[i].name, sd->status.name) == 0) {
+ clif_wis_end(fd, 2); // 2: ignored by target
+ break;
+ }
+ // if source player not found in ignore list
+ if (i == MAX_IGNORE_LIST) {
+ if(strlen(dstsd->away_message) > 0) { // Send away automessage [LuzZza]
+ //(Automessage has been sent)
+ sprintf(output, "%s %s", (char*)RFIFOP(fd,28),msg_txt(543));
+ clif_wis_message(dstsd->fd, sd->status.name, output, strlen(output) + 1);
+ clif_wis_end(fd, 0); // 0: success to send wisper
+ if(dstsd->state.autotrade)
+ //"Away [AT] - \"%s\""
+ sprintf(output, msg_txt(544), dstsd->away_message);
+ else
+ //"Away - \"%s\""
+ sprintf(output, msg_txt(545), dstsd->away_message);
+ clif_wis_message(fd, dstsd->status.name, output, strlen(output) + 1);
+ } else { // Normal message
+ clif_wis_message(dstsd->fd, sd->status.name, (char*)RFIFOP(fd,28), RFIFOW(fd,2) - 28);
+ clif_wis_end(fd, 0); // 0: success to send wisper
+ }
+ }
+ }
+ }
+ }
+
+ return;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+void clif_parse_GMmessage(int fd, struct map_session_data *sd) {
+// /b
+ RFIFOHEAD(fd);
+ if ((battle_config.atc_gmonly == 0 || pc_isGM(sd)) &&
+ (pc_isGM(sd) >= get_atcommand_level(AtCommand_Broadcast)))
+ intif_GMmessage((char*)RFIFOP(fd,4), RFIFOW(fd,2)-4, 0);
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+void clif_parse_TakeItem(int fd, struct map_session_data *sd) {
+ struct flooritem_data *fitem;
+ int map_object_id;
+ RFIFOHEAD(fd);
+
+ map_object_id = RFIFOL(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[0]);
+
+ fitem = (struct flooritem_data*)map_id2bl(map_object_id);
+
+ if (pc_isdead(sd)) {
+ clif_clearchar_area(&sd->bl, 1);
+ return;
+ }
+
+ if (fitem == NULL || fitem->bl.type != BL_ITEM || fitem->bl.m != sd->bl.m)
+ return;
+
+ if( sd->npc_id!=0 || sd->vender_id != 0 || sd->opt1 > 0 || sd->trade_partner!=0 ||
+ pc_iscloaking(sd) || pc_ischasewalk(sd) || //Disable cloaking/chasewalking characters from looting [Skotlex]
+ sd->sc_data[SC_TRICKDEAD].timer != -1 || //死んだふり
+ sd->sc_data[SC_BLADESTOP].timer != -1 || //白刃取り
+ sd->sc_data[SC_NOCHAT].timer!=-1 ) //会話禁止
+ {
+ clif_additem(sd,0,0,6); // send fail packet! [Valaris]
+ return;
+ }
+
+ pc_takeitem(sd, fitem);
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+void clif_parse_DropItem(int fd, struct map_session_data *sd) {
+ int item_index, item_amount;
+ RFIFOHEAD(fd);
+
+ if (pc_isdead(sd)) {
+ clif_clearchar_area(&sd->bl, 1);
+ return;
+ }
+ if (sd->npc_id != 0 || sd->vender_id != 0 || sd->opt1 > 0 || sd->trade_partner != 0 ||
+ sd->sc_data[SC_AUTOCOUNTER].timer != -1 || //オートカウンター
+ sd->sc_data[SC_BLADESTOP].timer != -1)//白刃取り
+ return;
+
+ item_index = RFIFOW(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[0])-2;
+ item_amount = RFIFOW(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[1]);
+ pc_dropitem(sd, item_index, item_amount);
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+void clif_parse_UseItem(int fd, struct map_session_data *sd) {
+ RFIFOHEAD(fd);
+
+ if (pc_isdead(sd)) {
+ clif_clearchar_area(&sd->bl, 1);
+ return;
+ }
+ if (sd->vender_id != 0 || (sd->opt1 > 0 && sd->opt1 != OPT1_STONEWAIT) || sd->trade_partner != 0)
+ return;
+ if (sd->npc_id!=0 && sd->npc_id != sd->npc_item_flag) //This flag enables you to use items while in an NPC. [Skotlex]
+ return;
+ if (sd->sc_data[SC_TRICKDEAD].timer != -1 || //死んだふり
+ sd->sc_data[SC_BLADESTOP].timer != -1 || //白刃取り
+ sd->sc_data[SC_BERSERK].timer!=-1 || //バーサーク
+ sd->sc_data[SC_NOCHAT].timer!=-1 ||
+ sd->sc_data[SC_GRAVITATION].timer!=-1) //会話禁止
+ return;
+
+ if (sd->invincible_timer != -1)
+ pc_delinvincibletimer(sd);
+
+ //Whether the item is used or not is irrelevant, the char ain't idle. [Skotlex]
+ sd->idletime = last_tick;
+ pc_useitem(sd,RFIFOW(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[0])-2);
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+void clif_parse_EquipItem(int fd,struct map_session_data *sd)
+{
+ int index;
+ RFIFOHEAD(fd);
+
+ if(pc_isdead(sd)) {
+ clif_clearchar_area(&sd->bl,1);
+ return;
+ }
+ index = RFIFOW(fd,2)-2;
+ if (index < 0 || index >= MAX_INVENTORY)
+ return; //Out of bounds check.
+
+ if(sd->npc_id!=0 && sd->npc_id != sd->npc_item_flag)
+ return;
+
+ if(sd->vender_id != 0 || sd->trade_partner != 0)
+ return;
+
+ if(sd->sc_data[SC_BLADESTOP].timer!=-1 || sd->sc_data[SC_BERSERK].timer!=-1 ) return;
+
+ if(sd->status.inventory[index].identify != 1) { // 未鑑定
+ clif_equipitemack(sd,index,0,0); // fail
+ return;
+ }
+ //ペット用装備であるかないか
+ if(sd->inventory_data[index]) {
+ if(sd->inventory_data[index]->type != 8){
+ if(sd->inventory_data[index]->type == 10)
+ RFIFOW(fd,4)=0x8000; // 矢を無理やり装備できるように(−−;
+ pc_equipitem(sd,index,RFIFOW(fd,4));
+ } else
+ pet_equipitem(sd,index);
+ }
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+void clif_parse_UnequipItem(int fd,struct map_session_data *sd)
+{
+ int index;
+ RFIFOHEAD(fd);
+
+ if(pc_isdead(sd)) {
+ clif_clearchar_area(&sd->bl,1);
+ return;
+ }
+ if(sd->npc_id!=0 || sd->vender_id != 0 || sd->opt1 > 0 || sd->trade_partner != 0)
+ return;
+ index = RFIFOW(fd,2)-2;
+
+ pc_unequipitem(sd,index,1);
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+void clif_parse_NpcClicked(int fd,struct map_session_data *sd)
+{
+ RFIFOHEAD(fd);
+
+ if(pc_isdead(sd)) {
+ clif_clearchar_area(&sd->bl,1);
+ return;
+ }
+ if(sd->npc_id!=0 || sd->vender_id != 0 || sd->trade_partner != 0 || RFIFOL(fd,2) < 0) //Clicked on a negative ID? Player disguised as NPC! [Skotlex]
+ return;
+ npc_click(sd,RFIFOL(fd,2));
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+void clif_parse_NpcBuySellSelected(int fd,struct map_session_data *sd)
+{
+ RFIFOHEAD(fd);
+ if (sd->trade_partner != 0)
+ return;
+ npc_buysellsel(sd,RFIFOL(fd,2),RFIFOB(fd,6));
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+void clif_parse_NpcBuyListSend(int fd,struct map_session_data *sd)
+{
+ int fail=0,n;
+ unsigned short *item_list;
+ RFIFOHEAD(fd);
+
+ n = (RFIFOW(fd,2)-4) /4;
+ item_list = (unsigned short*)RFIFOP(fd,4);
+
+ if (sd->trade_partner != 0)
+ fail = 1;
+ else
+ fail = npc_buylist(sd,n,item_list);
+
+ WFIFOHEAD(fd,packet_len_table[0xca]);
+ WFIFOW(fd,0)=0xca;
+ WFIFOB(fd,2)=fail;
+ WFIFOSET(fd,packet_len_table[0xca]);
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+void clif_parse_NpcSellListSend(int fd,struct map_session_data *sd)
+{
+ int fail=0,n;
+ unsigned short *item_list;
+ RFIFOHEAD(fd);
+
+ n = (RFIFOW(fd,2)-4) /4;
+ item_list = (unsigned short*)RFIFOP(fd,4);
+
+ if (sd->trade_partner != 0)
+ fail = 1;
+ else
+ fail = npc_selllist(sd,n,item_list);
+
+ WFIFOHEAD(fd,packet_len_table[0xcb]);
+ WFIFOW(fd,0)=0xcb;
+ WFIFOB(fd,2)=fail;
+ WFIFOSET(fd,packet_len_table[0xcb]);
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+void clif_parse_CreateChatRoom(int fd,struct map_session_data *sd)
+{
+ RFIFOHEAD(fd);
+ if(battle_config.basic_skill_check == 0 || pc_checkskill(sd,NV_BASIC) >= 4){
+ chat_createchat(sd,RFIFOW(fd,4),RFIFOB(fd,6),(char*)RFIFOP(fd,7),(char*)RFIFOP(fd,15),RFIFOW(fd,2)-15);
+ } else
+ clif_skill_fail(sd,1,0,3);
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+void clif_parse_ChatAddMember(int fd,struct map_session_data *sd)
+{
+ RFIFOHEAD(fd);
+ chat_joinchat(sd,RFIFOL(fd,2),(char*)RFIFOP(fd,6));
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+void clif_parse_ChatRoomStatusChange(int fd,struct map_session_data *sd)
+{
+ RFIFOHEAD(fd);
+ chat_changechatstatus(sd,RFIFOW(fd,4),RFIFOB(fd,6),(char*)RFIFOP(fd,7),(char*)RFIFOP(fd,15),RFIFOW(fd,2)-15);
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+void clif_parse_ChangeChatOwner(int fd,struct map_session_data *sd)
+{
+ RFIFOHEAD(fd);
+ chat_changechatowner(sd,(char*)RFIFOP(fd,6));
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+void clif_parse_KickFromChat(int fd,struct map_session_data *sd)
+{
+ RFIFOHEAD(fd);
+ chat_kickchat(sd,(char*)RFIFOP(fd,2));
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+void clif_parse_ChatLeave(int fd,struct map_session_data *sd)
+{
+ chat_leavechat(sd);
+}
+
+/*==========================================
+ * 取引要請を相手に送る
+ *------------------------------------------
+ */
+void clif_parse_TradeRequest(int fd,struct map_session_data *sd)
+{
+ RFIFOHEAD(fd);
+
+ if(battle_config.basic_skill_check == 0 || pc_checkskill(sd,NV_BASIC) >= 1){
+ trade_traderequest(sd,RFIFOL(sd->fd,2));
+ } else
+ clif_skill_fail(sd,1,0,0);
+}
+
+/*==========================================
+ * 取引要請
+ *------------------------------------------
+ */
+void clif_parse_TradeAck(int fd,struct map_session_data *sd)
+{
+ RFIFOHEAD(fd);
+ trade_tradeack(sd,RFIFOB(sd->fd,2));
+}
+
+/*==========================================
+ * アイテム追加
+ *------------------------------------------
+ */
+void clif_parse_TradeAddItem(int fd,struct map_session_data *sd)
+{
+ RFIFOHEAD(fd);
+ trade_tradeadditem(sd,RFIFOW(sd->fd,2),RFIFOL(sd->fd,4));
+}
+
+/*==========================================
+ * アイテム追加完了(ok押し)
+ *------------------------------------------
+ */
+void clif_parse_TradeOk(int fd,struct map_session_data *sd)
+{
+ trade_tradeok(sd);
+}
+
+/*==========================================
+ * 取引キャンセル
+ *------------------------------------------
+ */
+void clif_parse_TradeCancel(int fd,struct map_session_data *sd)
+{
+ trade_tradecancel(sd);
+}
+
+/*==========================================
+ * 取引許諾(trade押し)
+ *------------------------------------------
+ */
+void clif_parse_TradeCommit(int fd,struct map_session_data *sd)
+{
+ trade_tradecommit(sd);
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+void clif_parse_StopAttack(int fd,struct map_session_data *sd)
+{
+ pc_stopattack(sd);
+}
+
+/*==========================================
+ * カートへアイテムを移す
+ *------------------------------------------
+ */
+void clif_parse_PutItemToCart(int fd,struct map_session_data *sd)
+{
+ RFIFOHEAD(fd);
+
+ if(sd->npc_id!=0 || sd->vender_id != 0 || sd->trade_partner != 0)
+ return;
+ pc_putitemtocart(sd,RFIFOW(fd,2)-2,RFIFOL(fd,4));
+}
+/*==========================================
+ * カートからアイテムを出す
+ *------------------------------------------
+ */
+void clif_parse_GetItemFromCart(int fd,struct map_session_data *sd)
+{
+ RFIFOHEAD(fd);
+
+ if(sd->npc_id!=0 || sd->vender_id != 0 || sd->trade_partner != 0) return;
+ pc_getitemfromcart(sd,RFIFOW(fd,2)-2,RFIFOL(fd,4));
+}
+
+/*==========================================
+ * 付属品(鷹,ペコ,カート)をはずす
+ *------------------------------------------
+ */
+void clif_parse_RemoveOption(int fd,struct map_session_data *sd)
+{
+ pc_setoption(sd,0);
+}
+
+/*==========================================
+ * チェンジカート
+ *------------------------------------------
+ */
+void clif_parse_ChangeCart(int fd,struct map_session_data *sd)
+{
+ RFIFOHEAD(fd);
+ pc_setcart(sd,RFIFOW(fd,2));
+}
+
+/*==========================================
+ * ステータスアップ
+ *------------------------------------------
+ */
+void clif_parse_StatusUp(int fd,struct map_session_data *sd)
+{
+ RFIFOHEAD(fd);
+ pc_statusup(sd,RFIFOW(fd,2));
+}
+
+/*==========================================
+ * スキルレベルアップ
+ *------------------------------------------
+ */
+void clif_parse_SkillUp(int fd,struct map_session_data *sd)
+{
+ RFIFOHEAD(fd);
+ pc_skillup(sd,RFIFOW(fd,2));
+}
+
+/*==========================================
+ * スキル使用(ID指定)
+ *------------------------------------------
+ */
+void clif_parse_UseSkillToId(int fd, struct map_session_data *sd) {
+ int skillnum, skilllv, lv, target_id;
+ unsigned int tick = gettick();
+ RFIFOHEAD(fd);
+
+ if (sd->chatID || sd->npc_id != 0 || sd->vender_id != 0 || sd->state.storage_flag || pc_issit(sd))
+ return;
+
+ skilllv = RFIFOW(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[0]);
+ if (skilllv < 1) skilllv = 1; //No clue, I have seen the client do this with guild skills :/ [Skotlex]
+ skillnum = RFIFOW(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[1]);
+ target_id = RFIFOL(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[2]);
+
+ //Whether skill fails or not is irrelevant, the char ain't idle. [Skotlex]
+ sd->idletime = last_tick;
+
+ if (skillnotok(skillnum, sd))
+ return;
+
+ if (sd->skilltimer != -1) {
+ if (skillnum != SA_CASTCANCEL)
+ return;
+ } else if (DIFF_TICK(tick, sd->canact_tick) < 0 &&
+ // allow monk combos to ignore this delay [celest]
+ !(sd->sc_count && sd->sc_data[SC_COMBO].timer!=-1 &&
+ (skillnum == MO_EXTREMITYFIST ||
+ skillnum == MO_CHAINCOMBO ||
+ skillnum == MO_COMBOFINISH ||
+ skillnum == CH_PALMSTRIKE ||
+ skillnum == CH_TIGERFIST ||
+ skillnum == CH_CHAINCRUSH))) {
+ clif_skill_fail(sd, skillnum, 4, 0);
+ return;
+ }
+
+ if ((sd->sc_data[SC_TRICKDEAD].timer != -1 && skillnum != NV_TRICKDEAD) ||
+ sd->sc_data[SC_BERSERK].timer != -1 || sd->sc_data[SC_NOCHAT].timer != -1 ||
+ sd->sc_data[SC_WEDDING].timer != -1 || sd->view_class == JOB_WEDDING ||
+ sd->sc_data[SC_XMAS].timer != -1 || sd->view_class == JOB_XMAS)
+
+ return;
+ if (sd->invincible_timer != -1)
+ pc_delinvincibletimer(sd);
+
+ if(target_id<0) // for disguises [Valaris]
+ target_id-=target_id*2;
+
+ if (sd->skillitem >= 0 && sd->skillitem == skillnum) {
+ if (skilllv != sd->skillitemlv)
+ skilllv = sd->skillitemlv;
+ skill_use_id(sd, target_id, skillnum, skilllv);
+ } else {
+ sd->skillitem = sd->skillitemlv = -1;
+ if (skillnum == MO_EXTREMITYFIST) {
+ if ((sd->sc_data[SC_COMBO].timer == -1 ||
+ (sd->sc_data[SC_COMBO].val1 != MO_COMBOFINISH &&
+ sd->sc_data[SC_COMBO].val1 != CH_TIGERFIST &&
+ sd->sc_data[SC_COMBO].val1 != CH_CHAINCRUSH))) {
+ if (!sd->state.skill_flag ) {
+ sd->state.skill_flag = 1;
+ clif_skillinfo(sd, MO_EXTREMITYFIST, INF_ATTACK_SKILL, -1);
+ return;
+ } else if (sd->bl.id == target_id) {
+ clif_skillinfo(sd, MO_EXTREMITYFIST, INF_ATTACK_SKILL, -1);
+ return;
+ }
+ }
+ }
+ if (skillnum == TK_JUMPKICK) {
+ if (sd->sc_data[SC_COMBO].timer == -1 ||
+ sd->sc_data[SC_COMBO].val1 != TK_JUMPKICK) {
+ if (!sd->state.skill_flag ) {
+ sd->state.skill_flag = 1;
+ clif_skillinfo(sd, TK_JUMPKICK, INF_ATTACK_SKILL, -1);
+ return;
+ } else if (sd->bl.id == target_id) {
+ clif_skillinfo(sd, TK_JUMPKICK, INF_ATTACK_SKILL, -1);
+ return;
+ }
+ }
+ }
+
+
+ if ((lv = pc_checkskill(sd, skillnum)) > 0) {
+ if (skilllv > lv)
+ skilllv = lv;
+ skill_use_id(sd, target_id, skillnum, skilllv);
+ if (sd->state.skill_flag)
+ sd->state.skill_flag = 0;
+ }
+ }
+}
+
+/*==========================================
+ * スキル使用(場所指定)
+ *------------------------------------------
+ */
+void clif_parse_UseSkillToPosSub(int fd, struct map_session_data *sd, int skilllv, int skillnum, int x, int y, int skillmoreinfo)
+{
+ int lv;
+ unsigned int tick = gettick();
+ RFIFOHEAD(fd);
+
+ //Whether skill fails or not is irrelevant, the char ain't idle. [Skotlex]
+ sd->idletime = last_tick;
+
+ if (skillnotok(skillnum, sd))
+ return;
+
+ if (skillmoreinfo != -1) {
+ if (pc_issit(sd)) {
+ clif_skill_fail(sd, skillnum, 0, 0);
+ return;
+ }
+ memcpy(talkie_mes, RFIFOP(fd,skillmoreinfo), MESSAGE_SIZE);
+ talkie_mes[MESSAGE_SIZE-1] = '\0'; //Overflow protection [Skotlex]
+ }
+
+ if (sd->skilltimer != -1)
+ return;
+ else if (DIFF_TICK(tick, sd->canact_tick) < 0)
+ {
+ clif_skill_fail(sd, skillnum, 4, 0);
+ return;
+ }
+
+ if ((sd->sc_data[SC_TRICKDEAD].timer != -1 && skillnum != NV_TRICKDEAD) ||
+ sd->sc_data[SC_BERSERK].timer != -1 || sd->sc_data[SC_NOCHAT].timer != -1 ||
+ sd->sc_data[SC_WEDDING].timer != -1 || sd->view_class == JOB_WEDDING ||
+ sd->sc_data[SC_XMAS].timer != -1 || sd->view_class == JOB_XMAS)
+ return;
+ if (sd->invincible_timer != -1)
+ pc_delinvincibletimer(sd);
+ if (sd->skillitem >= 0 && sd->skillitem == skillnum) {
+ if (skilllv != sd->skillitemlv)
+ skilllv = sd->skillitemlv;
+ skill_use_pos(sd, x, y, skillnum, skilllv);
+ } else {
+ sd->skillitem = sd->skillitemlv = -1;
+ if ((lv = pc_checkskill(sd, skillnum)) > 0) {
+ if (skilllv > lv)
+ skilllv = lv;
+ skill_use_pos(sd, x, y, skillnum,skilllv);
+ }
+ }
+}
+
+
+void clif_parse_UseSkillToPos(int fd, struct map_session_data *sd) {
+ RFIFOHEAD(fd);
+
+ if (sd->npc_id != 0 || sd->vender_id != 0 || sd->chatID || sd->state.storage_flag || pc_issit(sd))
+ return;
+
+ clif_parse_UseSkillToPosSub(fd, sd,
+ RFIFOW(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[0]), //skill lv
+ RFIFOW(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[1]), //skill num
+ RFIFOW(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[2]), //pos x
+ RFIFOW(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[3]), //pos y
+ -1 //Skill more info.
+ );
+}
+
+void clif_parse_UseSkillToPosMoreInfo(int fd, struct map_session_data *sd) {
+ RFIFOHEAD(fd);
+
+ if (sd->npc_id != 0 || sd->vender_id != 0 || sd->chatID || sd->state.storage_flag) return;
+
+ clif_parse_UseSkillToPosSub(fd, sd,
+ RFIFOW(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[0]), //Skill lv
+ RFIFOW(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[1]), //Skill num
+ RFIFOW(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[2]), //pos x
+ RFIFOW(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[3]), //pos y
+ packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[4] //skill more info
+ );
+}
+/*==========================================
+ * スキル使用(map指定)
+ *------------------------------------------
+ */
+void clif_parse_UseSkillMap(int fd,struct map_session_data *sd)
+{
+ RFIFOHEAD(fd);
+
+ if(sd->chatID) return;
+
+ if (sd->npc_id!=0 || sd->vender_id != 0 || (sd->sc_data &&
+ (sd->sc_data[SC_TRICKDEAD].timer != -1 ||
+ sd->sc_data[SC_BERSERK].timer!=-1 ||
+ sd->sc_data[SC_NOCHAT].timer!=-1 ||
+ sd->sc_data[SC_WEDDING].timer!=-1 ||
+ sd->view_class==JOB_WEDDING ||
+ sd->sc_data[SC_XMAS].timer != -1 ||
+ sd->view_class == JOB_XMAS)))
+ return;
+
+ if(sd->invincible_timer != -1)
+ pc_delinvincibletimer(sd);
+
+ skill_castend_map(sd,RFIFOW(fd,2),(char*)RFIFOP(fd,4));
+}
+/*==========================================
+ * メモ要求
+ *------------------------------------------
+ */
+void clif_parse_RequestMemo(int fd,struct map_session_data *sd)
+{
+ if (!pc_isdead(sd))
+ pc_memo(sd,-1);
+}
+/*==========================================
+ * アイテム合成
+ *------------------------------------------
+ */
+void clif_parse_ProduceMix(int fd,struct map_session_data *sd)
+{
+ RFIFOHEAD(fd);
+
+ if (!sd->state.produce_flag)
+ return;
+ sd->state.produce_flag = 0;
+ if (sd->npc_id) {
+ //Make it fail to avoid shop exploits where you sell something different than you see.
+ clif_skill_fail(sd,sd->skillid,0,0);
+ return;
+ }
+ skill_produce_mix(sd,0,RFIFOW(fd,2),RFIFOW(fd,4),RFIFOW(fd,6),RFIFOW(fd,8), 1);
+}
+/*==========================================
+ * 武器修理
+ *------------------------------------------
+ */
+void clif_parse_RepairItem(int fd, struct map_session_data *sd)
+{
+ RFIFOHEAD(fd);
+
+ if (!sd->state.produce_flag)
+ return;
+ sd->state.produce_flag = 0;
+ if (sd->npc_id) {
+ //Make it fail to avoid shop exploits where you sell something different than you see.
+ clif_skill_fail(sd,sd->skillid,0,0);
+ return;
+ }
+ skill_repairweapon(sd,RFIFOW(fd,2));
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+void clif_parse_WeaponRefine(int fd, struct map_session_data *sd) {
+ int idx;
+ RFIFOHEAD(fd);
+
+ if (!sd->state.produce_flag) //Packet exploit?
+ return;
+ sd->state.produce_flag = 0;
+ if (sd->npc_id) {
+ //Make it fail to avoid shop exploits where you sell something different than you see.
+ clif_skill_fail(sd,sd->skillid,0,0);
+ return;
+ }
+ idx = RFIFOW(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[0]);
+ skill_weaponrefine(sd, idx-2);
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+void clif_parse_NpcSelectMenu(int fd,struct map_session_data *sd)
+{
+ RFIFOHEAD(fd);
+
+ sd->npc_menu=RFIFOB(fd,6);
+ npc_scriptcont(sd,RFIFOL(fd,2));
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+void clif_parse_NpcNextClicked(int fd,struct map_session_data *sd)
+{
+ RFIFOHEAD(fd);
+#ifdef __WIN32
+ //For some extraordinarily eerie reason that noone has figured out yet,
+ //windows native compiles NEED this nullpo_retv or the function is not found!
+ nullpo_retv(sd);
+#endif
+ npc_scriptcont(sd,RFIFOL(fd,2));
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+void clif_parse_NpcAmountInput(int fd,struct map_session_data *sd)
+{
+ RFIFOHEAD(fd);
+#define RFIFOL_(fd,pos) (*(int*)(session[fd]->rdata+session[fd]->rdata_pos+(pos)))
+ //Input Value overflow Exploit FIX
+ sd->npc_amount=RFIFOL_(fd,6); //fixed by Lupus. npc_amount is (int) but was RFIFOL changing it to (unsigned int)
+#undef RFIFOL_
+
+ npc_scriptcont(sd,RFIFOL(fd,2));
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+void clif_parse_NpcStringInput(int fd,struct map_session_data *sd)
+{
+ RFIFOHEAD(fd);
+
+ if(RFIFOW(fd,2)-7 >= sizeof(sd->npc_str)){
+ ShowWarning("clif: input string too long !\n");
+ memcpy(sd->npc_str,RFIFOP(fd,8),sizeof(sd->npc_str));
+ sd->npc_str[sizeof(sd->npc_str)-1]=0;
+ } else
+ strcpy(sd->npc_str,(char*)RFIFOP(fd,8));
+ npc_scriptcont(sd,RFIFOL(fd,4));
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+void clif_parse_NpcCloseClicked(int fd,struct map_session_data *sd)
+{
+ RFIFOHEAD(fd);
+ npc_scriptcont(sd,RFIFOL(fd,2));
+}
+
+/*==========================================
+ * アイテム鑑定
+ *------------------------------------------
+ */
+void clif_parse_ItemIdentify(int fd,struct map_session_data *sd)
+{
+ RFIFOHEAD(fd);
+ skill_identify(sd,RFIFOW(fd,2)-2);
+}
+/*==========================================
+ * 矢作成
+ *------------------------------------------
+ */
+void clif_parse_SelectArrow(int fd,struct map_session_data *sd)
+{
+ RFIFOHEAD(fd);
+ if (!sd->state.produce_flag)
+ return;
+ sd->state.produce_flag = 0;
+ if (sd->npc_id) { //Make it fail to avoid shop exploits where you sell something different than you see.
+ clif_skill_fail(sd,sd->skillid,0,0);
+ return;
+ }
+ skill_arrow_create(sd,RFIFOW(fd,2));
+}
+/*==========================================
+ * オートスペル受信
+ *------------------------------------------
+ */
+void clif_parse_AutoSpell(int fd,struct map_session_data *sd)
+{
+ RFIFOHEAD(fd);
+ skill_autospell(sd,RFIFOW(fd,2));
+}
+/*==========================================
+ * カード使用
+ *------------------------------------------
+ */
+void clif_parse_UseCard(int fd,struct map_session_data *sd)
+{
+ RFIFOHEAD(fd);
+ if (sd->trade_partner != 0)
+ return;
+ clif_use_card(sd,RFIFOW(fd,2)-2);
+}
+/*==========================================
+ * カード挿入装備選択
+ *------------------------------------------
+ */
+void clif_parse_InsertCard(int fd,struct map_session_data *sd)
+{
+ RFIFOHEAD(fd);
+ if (sd->trade_partner != 0)
+ return;
+ pc_insert_card(sd,RFIFOW(fd,2)-2,RFIFOW(fd,4)-2);
+}
+
+/*==========================================
+ * 0193 キャラID名前引き
+ *------------------------------------------
+ */
+void clif_parse_SolveCharName(int fd, struct map_session_data *sd) {
+ int char_id;
+ RFIFOHEAD(fd);
+
+ char_id = RFIFOL(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[0]);
+ clif_solved_charname(sd, char_id);
+}
+
+/*==========================================
+ * 0197 /resetskill /resetstate
+ *------------------------------------------
+ */
+void clif_parse_ResetChar(int fd, struct map_session_data *sd) {
+ RFIFOHEAD(fd);
+ if ((battle_config.atc_gmonly == 0 || pc_isGM(sd)) &&
+ pc_isGM(sd) >= get_atcommand_level(AtCommand_ResetState)) {
+ switch(RFIFOW(fd,2)){
+ case 0:
+ pc_resetstate(sd);
+ break;
+ case 1:
+ pc_resetskill(sd);
+ break;
+ }
+ }
+}
+
+/*==========================================
+ * 019c /lb等
+ *------------------------------------------
+ */
+void clif_parse_LGMmessage(int fd, struct map_session_data *sd) {
+ unsigned char buf[512];
+ RFIFOHEAD(fd);
+
+ if ((battle_config.atc_gmonly == 0 || pc_isGM(sd)) &&
+ (pc_isGM(sd) >= get_atcommand_level(AtCommand_LocalBroadcast))) {
+ WBUFW(buf,0) = 0x9a;
+ WBUFW(buf,2) = RFIFOW(fd,2);
+ memcpy(WBUFP(buf,4), RFIFOP(fd,4), RFIFOW(fd,2) - 4);
+ clif_send(buf, RFIFOW(fd,2), &sd->bl, ALL_SAMEMAP);
+ }
+}
+
+/*==========================================
+ * カプラ倉庫へ入れる
+ *------------------------------------------
+ */
+void clif_parse_MoveToKafra(int fd, struct map_session_data *sd) {
+ int item_index, item_amount;
+ RFIFOHEAD(fd);
+
+ if (sd->npc_id != 0 || sd->vender_id != 0 || sd->trade_partner != 0 || !sd->state.storage_flag)
+ return;
+
+ item_index = RFIFOW(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[0])-2;
+ item_amount = RFIFOL(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[1]);
+ if (item_index < 0 || item_index >= MAX_INVENTORY)
+ return;
+
+ if (sd->state.storage_flag == 1)
+ storage_storageadd(sd, item_index, item_amount);
+ else if (sd->state.storage_flag == 2)
+ storage_guild_storageadd(sd, item_index, item_amount);
+}
+
+/*==========================================
+ * カプラ倉庫から出す
+ *------------------------------------------
+ */
+void clif_parse_MoveFromKafra(int fd,struct map_session_data *sd) {
+ int item_index, item_amount;
+ RFIFOHEAD(fd);
+
+ if (sd->npc_id != 0 || sd->vender_id != 0 || sd->trade_partner != 0 || !sd->state.storage_flag)
+ return;
+
+ item_index = RFIFOW(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[0])-1;
+ item_amount = RFIFOL(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[1]);
+
+ if (sd->state.storage_flag == 1)
+ storage_storageget(sd, item_index, item_amount);
+ else if(sd->state.storage_flag == 2)
+ storage_guild_storageget(sd, item_index, item_amount);
+}
+
+/*==========================================
+ * カプラ倉庫へカートから入れる
+ *------------------------------------------
+ */
+void clif_parse_MoveToKafraFromCart(int fd, struct map_session_data *sd) {
+ RFIFOHEAD(fd);
+
+ if (sd->npc_id != 0 || sd->vender_id != 0 || sd->trade_partner != 0 || !sd->state.storage_flag)
+ return;
+
+ if (sd->state.storage_flag == 1)
+ storage_storageaddfromcart(sd, RFIFOW(fd,2) - 2, RFIFOL(fd,4));
+ else if (sd->state.storage_flag == 2)
+ storage_guild_storageaddfromcart(sd, RFIFOW(fd,2) - 2, RFIFOL(fd,4));
+}
+
+/*==========================================
+ * カプラ倉庫から出す
+ *------------------------------------------
+ */
+void clif_parse_MoveFromKafraToCart(int fd, struct map_session_data *sd) {
+ RFIFOHEAD(fd);
+
+ if (sd->npc_id != 0 || sd->vender_id != 0 || sd->trade_partner != 0 || !sd->state.storage_flag)
+ return;
+ if (sd->state.storage_flag == 1)
+ storage_storagegettocart(sd, RFIFOW(fd,2)-1, RFIFOL(fd,4));
+ else if (sd->state.storage_flag == 2)
+ storage_guild_storagegettocart(sd, RFIFOW(fd,2)-1, RFIFOL(fd,4));
+}
+
+/*==========================================
+ * カプラ倉庫を閉じる
+ *------------------------------------------
+ */
+void clif_parse_CloseKafra(int fd, struct map_session_data *sd) {
+ if (sd->state.storage_flag == 1)
+ storage_storageclose(sd);
+ else if (sd->state.storage_flag == 2)
+ storage_guild_storageclose(sd);
+}
+
+/*==========================================
+ * パーティを作る
+ *------------------------------------------
+ */
+void clif_parse_CreateParty(int fd, struct map_session_data *sd) {
+ RFIFOHEAD(fd);
+ if (battle_config.basic_skill_check == 0 || pc_checkskill(sd,NV_BASIC) >= 7) {
+ party_create(sd,(char*)RFIFOP(fd,2),0,0);
+ } else
+ clif_skill_fail(sd,1,0,4);
+}
+
+/*==========================================
+ * パーティを作る
+ *------------------------------------------
+ */
+void clif_parse_CreateParty2(int fd, struct map_session_data *sd) {
+ if (battle_config.basic_skill_check == 0 || pc_checkskill(sd,NV_BASIC) >= 7){
+ RFIFOHEAD(fd);
+ party_create(sd,(char*)RFIFOP(fd,2),RFIFOB(fd,26),RFIFOB(fd,27));
+ } else
+ clif_skill_fail(sd,1,0,4);
+}
+
+/*==========================================
+ * パーティに勧誘
+ *------------------------------------------
+ */
+void clif_parse_PartyInvite(int fd, struct map_session_data *sd) {
+ RFIFOHEAD(fd);
+ party_invite(sd, RFIFOL(fd,2));
+}
+
+/*==========================================
+ * パーティ勧誘返答
+ *------------------------------------------
+ */
+void clif_parse_ReplyPartyInvite(int fd,struct map_session_data *sd) {
+ RFIFOHEAD(fd);
+ if(battle_config.basic_skill_check == 0 || pc_checkskill(sd,NV_BASIC) >= 5){
+ party_reply_invite(sd,RFIFOL(fd,2),RFIFOL(fd,6));
+ } else {
+ party_reply_invite(sd,RFIFOL(fd,2),-1);
+ clif_skill_fail(sd,1,0,4);
+ }
+}
+
+/*==========================================
+ * パーティ脱退要求
+ *------------------------------------------
+ */
+void clif_parse_LeaveParty(int fd, struct map_session_data *sd) {
+ party_leave(sd);
+}
+
+/*==========================================
+ * パーティ除名要求
+ *------------------------------------------
+ */
+void clif_parse_RemovePartyMember(int fd, struct map_session_data *sd) {
+ RFIFOHEAD(fd);
+ party_removemember(sd,RFIFOL(fd,2),(char*)RFIFOP(fd,6));
+}
+
+/*==========================================
+ * パーティ設定変更要求
+ *------------------------------------------
+ */
+void clif_parse_PartyChangeOption(int fd, struct map_session_data *sd) {
+ RFIFOHEAD(fd);
+ party_changeoption(sd, RFIFOW(fd,2), RFIFOW(fd,4));
+}
+
+/*==========================================
+ * パーティメッセージ送信要求
+ *------------------------------------------
+ */
+void clif_parse_PartyMessage(int fd, struct map_session_data *sd) {
+ RFIFOHEAD(fd);
+
+ if (is_charcommand(fd, sd, (char*)RFIFOP(fd,4), 0) != CharCommand_None ||
+ is_atcommand(fd, sd, (char*)RFIFOP(fd,4), 0) != AtCommand_None ||
+ (sd->sc_data &&
+ (sd->sc_data[SC_BERSERK].timer!=-1 || //バーサーク時は会話も不可
+ sd->sc_data[SC_NOCHAT].timer!=-1))) //チャット禁止
+ return;
+
+ party_send_message(sd, (char*)RFIFOP(fd,4), RFIFOW(fd,2)-4);
+}
+
+/*==========================================
+ * 露店閉鎖
+ *------------------------------------------
+ */
+void clif_parse_CloseVending(int fd, struct map_session_data *sd) {
+ vending_closevending(sd);
+}
+
+/*==========================================
+ * 露店アイテムリスト要求
+ *------------------------------------------
+ */
+void clif_parse_VendingListReq(int fd, struct map_session_data *sd) {
+ RFIFOHEAD(fd);
+
+ vending_vendinglistreq(sd,RFIFOL(fd,2));
+ if(sd->npc_id)
+ npc_event_dequeue(sd);
+}
+
+/*==========================================
+ * 露店アイテム購入
+ *------------------------------------------
+ */
+void clif_parse_PurchaseReq(int fd, struct map_session_data *sd) {
+ RFIFOHEAD(fd);
+ if (sd->trade_partner != 0)
+ return;
+ vending_purchasereq(sd, RFIFOW(fd,2), RFIFOL(fd,4), RFIFOP(fd,8));
+}
+
+/*==========================================
+ * 露店開設
+ *------------------------------------------
+ */
+void clif_parse_OpenVending(int fd,struct map_session_data *sd) {
+ RFIFOHEAD(fd);
+ if (sd->trade_partner != 0)
+ return;
+ vending_openvending(sd, RFIFOW(fd,2), (char*)RFIFOP(fd,4), RFIFOB(fd,84), RFIFOP(fd,85));
+}
+
+/*==========================================
+ * ギルドを作る
+ *------------------------------------------
+ */
+void clif_parse_CreateGuild(int fd,struct map_session_data *sd) {
+ RFIFOHEAD(fd);
+ guild_create(sd, (char*)RFIFOP(fd,6));
+}
+
+/*==========================================
+ * ギルドマスターかどうか確認
+ *------------------------------------------
+ */
+void clif_parse_GuildCheckMaster(int fd, struct map_session_data *sd) {
+ clif_guild_masterormember(sd);
+}
+
+/*==========================================
+ * ギルド情報要求
+ *------------------------------------------
+ */
+void clif_parse_GuildRequestInfo(int fd, struct map_session_data *sd) {
+ RFIFOHEAD(fd);
+ switch(RFIFOL(fd,2)){
+ case 0: // ギルド基本情報、同盟敵対情報
+ clif_guild_basicinfo(sd);
+ clif_guild_allianceinfo(sd);
+ break;
+ case 1: // メンバーリスト、役職名リスト
+ clif_guild_positionnamelist(sd);
+ clif_guild_memberlist(sd);
+ break;
+ case 2: // 役職名リスト、役職情報リスト
+ clif_guild_positionnamelist(sd);
+ clif_guild_positioninfolist(sd);
+ break;
+ case 3: // スキルリスト
+ clif_guild_skillinfo(sd);
+ break;
+ case 4: // 追放リスト
+ clif_guild_explusionlist(sd);
+ break;
+ default:
+ if (battle_config.error_log)
+ ShowError("clif: guild request info: unknown type %d\n", RFIFOL(fd,2));
+ break;
+ }
+}
+
+/*==========================================
+ * ギルド役職変更
+ *------------------------------------------
+ */
+void clif_parse_GuildChangePositionInfo(int fd, struct map_session_data *sd) {
+ int i;
+ RFIFOHEAD(fd);
+
+ for(i = 4; i < RFIFOW(fd,2); i += 40 ){
+ guild_change_position(sd, RFIFOL(fd,i), RFIFOL(fd,i+4), RFIFOL(fd,i+12), (char*)RFIFOP(fd,i+16));
+ }
+}
+
+/*==========================================
+ * ギルドメンバ役職変更
+ *------------------------------------------
+ */
+void clif_parse_GuildChangeMemberPosition(int fd, struct map_session_data *sd) {
+ int i;
+ RFIFOHEAD(fd);
+
+ for(i=4;i<RFIFOW(fd,2);i+=12){
+ guild_change_memberposition(sd->status.guild_id,
+ RFIFOL(fd,i),RFIFOL(fd,i+4),RFIFOL(fd,i+8));
+ }
+}
+
+/*==========================================
+ * ギルドエンブレム要求
+ *------------------------------------------
+ */
+void clif_parse_GuildRequestEmblem(int fd,struct map_session_data *sd) {
+ struct guild *g;
+ RFIFOHEAD(fd);
+ g=guild_search(RFIFOL(fd,2));
+ if(g!=NULL)
+ clif_guild_emblem(sd,g);
+}
+
+/*==========================================
+ * ギルドエンブレム変更
+ *------------------------------------------
+ */
+void clif_parse_GuildChangeEmblem(int fd,struct map_session_data *sd) {
+ RFIFOHEAD(fd);
+ guild_change_emblem(sd,RFIFOW(fd,2)-4,(char*)RFIFOP(fd,4));
+}
+
+/*==========================================
+ * ギルド告知変更
+ *------------------------------------------
+ */
+void clif_parse_GuildChangeNotice(int fd,struct map_session_data *sd) {
+ RFIFOHEAD(fd);
+ guild_change_notice(sd,RFIFOL(fd,2),(char*)RFIFOP(fd,6),(char*)RFIFOP(fd,66));
+}
+
+/*==========================================
+ * ギルド勧誘
+ *------------------------------------------
+ */
+void clif_parse_GuildInvite(int fd,struct map_session_data *sd) {
+ RFIFOHEAD(fd);
+ guild_invite(sd,RFIFOL(fd,2));
+}
+
+/*==========================================
+ * ギルド勧誘返信
+ *------------------------------------------
+ */
+void clif_parse_GuildReplyInvite(int fd,struct map_session_data *sd) {
+ RFIFOHEAD(fd);
+ guild_reply_invite(sd,RFIFOL(fd,2),RFIFOB(fd,6));
+}
+
+/*==========================================
+ * ギルド脱退
+ *------------------------------------------
+ */
+void clif_parse_GuildLeave(int fd,struct map_session_data *sd) {
+ RFIFOHEAD(fd);
+ guild_leave(sd,RFIFOL(fd,2),RFIFOL(fd,6),RFIFOL(fd,10),(char*)RFIFOP(fd,14));
+}
+
+/*==========================================
+ * ギルド追放
+ *------------------------------------------
+ */
+void clif_parse_GuildExplusion(int fd,struct map_session_data *sd) {
+ RFIFOHEAD(fd);
+ guild_explusion(sd,RFIFOL(fd,2),RFIFOL(fd,6),RFIFOL(fd,10),(char*)RFIFOP(fd,14));
+}
+
+/*==========================================
+ * ギルド会話
+ *------------------------------------------
+ */
+void clif_parse_GuildMessage(int fd,struct map_session_data *sd) {
+ RFIFOHEAD(fd);
+
+ if (is_charcommand(fd, sd, (char*)RFIFOP(fd, 4), 0) != CharCommand_None ||
+ is_atcommand(fd, sd, (char*)RFIFOP(fd, 4), 0) != AtCommand_None ||
+ (sd->sc_data &&
+ (sd->sc_data[SC_BERSERK].timer!=-1 || //バーサーク時は会話も不可
+ sd->sc_data[SC_NOCHAT].timer!=-1))) //チャット禁止
+ return;
+
+ guild_send_message(sd, (char*)RFIFOP(fd,4), RFIFOW(fd,2)-4);
+}
+
+/*==========================================
+ * ギルド同盟要求
+ *------------------------------------------
+ */
+void clif_parse_GuildRequestAlliance(int fd, struct map_session_data *sd) {
+ RFIFOHEAD(fd);
+ guild_reqalliance(sd,RFIFOL(fd,2));
+}
+
+/*==========================================
+ * ギルド同盟要求返信
+ *------------------------------------------
+ */
+void clif_parse_GuildReplyAlliance(int fd, struct map_session_data *sd) {
+ RFIFOHEAD(fd);
+ guild_reply_reqalliance(sd,RFIFOL(fd,2),RFIFOL(fd,6));
+}
+
+/*==========================================
+ * ギルド関係解消
+ *------------------------------------------
+ */
+void clif_parse_GuildDelAlliance(int fd, struct map_session_data *sd) {
+ RFIFOHEAD(fd);
+ guild_delalliance(sd,RFIFOL(fd,2),RFIFOL(fd,6));
+}
+
+/*==========================================
+ * ギルド敵対
+ *------------------------------------------
+ */
+void clif_parse_GuildOpposition(int fd, struct map_session_data *sd) {
+ RFIFOHEAD(fd);
+ guild_opposition(sd,RFIFOL(fd,2));
+}
+
+/*==========================================
+ * ギルド解散
+ *------------------------------------------
+ */
+void clif_parse_GuildBreak(int fd, struct map_session_data *sd) {
+ RFIFOHEAD(fd);
+ guild_break(sd,(char*)RFIFOP(fd,2));
+}
+
+// pet
+void clif_parse_PetMenu(int fd, struct map_session_data *sd) {
+ RFIFOHEAD(fd);
+ pet_menu(sd,RFIFOB(fd,2));
+}
+
+void clif_parse_CatchPet(int fd, struct map_session_data *sd) {
+ RFIFOHEAD(fd);
+ pet_catch_process2(sd,RFIFOL(fd,2));
+}
+
+void clif_parse_SelectEgg(int fd, struct map_session_data *sd) {
+ RFIFOHEAD(fd);
+ pet_select_egg(sd,RFIFOW(fd,2)-2);
+}
+
+void clif_parse_SendEmotion(int fd, struct map_session_data *sd) {
+ if(sd->pd) {
+ RFIFOHEAD(fd);
+ clif_pet_emotion(sd->pd,RFIFOL(fd,2));
+ }
+}
+
+void clif_parse_ChangePetName(int fd, struct map_session_data *sd) {
+ RFIFOHEAD(fd);
+ pet_change_name(sd,(char*)RFIFOP(fd,2));
+}
+
+// Kick (right click menu for GM "(name) force to quit")
+void clif_parse_GMKick(int fd, struct map_session_data *sd) {
+ struct block_list *target;
+ int tid;
+
+ RFIFOHEAD(fd);
+ tid = RFIFOL(fd,2);
+
+ if ((battle_config.atc_gmonly == 0 || pc_isGM(sd)) &&
+ (pc_isGM(sd) >= get_atcommand_level(AtCommand_Kick))) {
+ target = map_id2bl(tid);
+ if (target) {
+ if (target->type == BL_PC) {
+ struct map_session_data *tsd = (struct map_session_data *)target;
+ if (pc_isGM(sd) > pc_isGM(tsd))
+ clif_GM_kick(sd, tsd, 1);
+ else
+ clif_GM_kickack(sd, 0);
+ } else if (target->type == BL_MOB) {
+ struct mob_data *md = (struct mob_data *)target;
+ sd->state.attack_type = 0;
+ mob_damage(&sd->bl, md, md->hp, 2);
+ } else
+ clif_GM_kickack(sd, 0);
+ } else
+ clif_GM_kickack(sd, 0);
+ }
+}
+
+/*==========================================
+ * /shift
+ *------------------------------------------
+ */
+void clif_parse_Shift(int fd, struct map_session_data *sd) { // Rewriten by [Yor]
+ char player_name[NAME_LENGTH+1];
+
+ memset(player_name, '\0', sizeof(player_name));
+
+ if ((battle_config.atc_gmonly == 0 || pc_isGM(sd)) &&
+ (pc_isGM(sd) >= get_atcommand_level(AtCommand_JumpTo))) {
+ RFIFOHEAD(fd);
+ memcpy(player_name, RFIFOP(fd,2), NAME_LENGTH);
+ atcommand_jumpto(fd, sd, "@jumpto", player_name); // as @jumpto
+ }
+
+ return;
+}
+
+/*==========================================
+ * /recall
+ *------------------------------------------
+ */
+void clif_parse_Recall(int fd, struct map_session_data *sd) { // Added by RoVeRT
+ char player_name[25];
+
+ memset(player_name, '\0', sizeof(player_name));
+
+ if ((battle_config.atc_gmonly == 0 || pc_isGM(sd)) &&
+ (pc_isGM(sd) >= get_atcommand_level(AtCommand_Recall))) {
+ RFIFOHEAD(fd);
+ memcpy(player_name, RFIFOP(fd,2), NAME_LENGTH);
+ atcommand_recall(fd, sd, "@recall", player_name); // as @recall
+ }
+
+ return;
+}
+
+/*==========================================
+ * /monster /item rewriten by [Yor]
+ *------------------------------------------
+ */
+void clif_parse_GM_Monster_Item(int fd, struct map_session_data *sd) {
+ char monster_item_name[NAME_LENGTH+10]; //Additional space is for logging, eg: "@monster Poring"
+ int level;
+
+ memset(monster_item_name, '\0', sizeof(monster_item_name));
+
+ if (battle_config.atc_gmonly == 0 || pc_isGM(sd)) {
+ RFIFOHEAD(fd);
+ memcpy(monster_item_name, RFIFOP(fd,2), NAME_LENGTH);
+
+ if (mobdb_searchname(monster_item_name) != 0) {
+ if (pc_isGM(sd) >= (level =get_atcommand_level(AtCommand_Monster)))
+ {
+ atcommand_monster(fd, sd, "@spawn", monster_item_name); // as @spawn
+ if(log_config.gm && level >= log_config.gm)
+ { //Log action. [Skotlex]
+ snprintf(monster_item_name, sizeof(monster_item_name)-1, "@spawn %s", RFIFOP(fd,2));
+ log_atcommand(sd, monster_item_name);
+ }
+ }
+ } else if (itemdb_searchname(monster_item_name) != NULL) {
+ if (pc_isGM(sd) >= (level = get_atcommand_level(AtCommand_Item)))
+ {
+ atcommand_item(fd, sd, "@item", monster_item_name); // as @item
+ if(log_config.gm && level >= log_config.gm)
+ { //Log action. [Skotlex]
+ snprintf(monster_item_name, sizeof(monster_item_name)-1, "@item %s", RFIFOP(fd,2));
+ log_atcommand(sd, monster_item_name);
+ }
+ }
+ }
+ }
+}
+
+void clif_parse_GMHide(int fd, struct map_session_data *sd) { // Modified by [Yor]
+ if ((battle_config.atc_gmonly == 0 || pc_isGM(sd)) &&
+ (pc_isGM(sd) >= get_atcommand_level(AtCommand_Hide))) {
+ if (sd->status.option & OPTION_INVISIBLE) {
+ sd->status.option &= ~OPTION_INVISIBLE;
+ clif_displaymessage(fd, "Invisible: Off.");
+ } else {
+ sd->status.option |= OPTION_INVISIBLE;
+ clif_displaymessage(fd, "Invisible: On.");
+ }
+ clif_changeoption(&sd->bl);
+ }
+}
+
+/*==========================================
+ * GMによるチャット禁止時間付与
+ *------------------------------------------
+ */
+void clif_parse_GMReqNoChat(int fd,struct map_session_data *sd)
+{
+ int type, limit, level;
+ struct block_list *bl;
+ struct map_session_data *dstsd;
+
+ if(!battle_config.muting_players) {
+ clif_displaymessage(fd, "Muting is disabled.");
+ return;
+ }
+
+ RFIFOHEAD(fd);
+ bl = map_id2bl(RFIFOL(fd,2));
+ if (!bl || bl->type != BL_PC)
+ return;
+ nullpo_retv(dstsd =(struct map_session_data *)bl);
+
+ type = RFIFOB(fd,6);
+ limit = RFIFOW(fd,7);
+ if (type == 0)
+ limit = 0 - limit;
+
+ //Temporarily disable chars from muting themselves due to the mysterious "DON'T USE BOT!" message. [Skotlex]
+ //Also, if type is 2 and the ids don't match, this is a crafted hacked packet!
+ //So for now, type 2is just totally disabled.
+ if (type == 2/*&& sd->bl.id != dstsd->bl.id*/)
+ return;
+
+ if (
+ ((level = pc_isGM(sd)) > pc_isGM(dstsd) && level >= get_atcommand_level(AtCommand_Mute))
+ || (type == 2 && !level)) {
+ clif_GM_silence(sd, dstsd, ((type == 2) ? 1 : type));
+ if (battle_config.manner_system)
+ {
+ dstsd->status.manner -= limit;
+ if(dstsd->status.manner < 0)
+ status_change_start(bl,SC_NOCHAT,0,0,0,0,0,0);
+ else
+ {
+ dstsd->status.manner = 0;
+ status_change_end(bl,SC_NOCHAT,-1);
+ }
+ }
+ ShowDebug("GMReqNoChat: name:%s type:%d limit:%d manner:%d\n", dstsd->status.name, type, limit, dstsd->status.manner);
+ }
+
+ return;
+}
+/*==========================================
+ * GMによるチャット禁止時間参照(?)
+ *------------------------------------------
+ */
+void clif_parse_GMReqNoChatCount(int fd, struct map_session_data *sd)
+{
+ int tid;
+ RFIFOHEAD(fd);
+ tid = RFIFOL(fd,2);
+
+ WFIFOHEAD(fd,packet_len_table[0x1e0]);
+ WFIFOW(fd,0) = 0x1e0;
+ WFIFOL(fd,2) = tid;
+ sprintf((char*)WFIFOP(fd,6),"%d",tid);
+// memcpy(WFIFOP(fd,6), "TESTNAME", 24);
+ WFIFOSET(fd, packet_len_table[0x1e0]);
+
+ return;
+}
+
+void clif_parse_PMIgnore(int fd, struct map_session_data *sd) { // Rewritten by [Yor]
+ char output[512];
+ char *nick; // S 00cf <nick>.24B <type>.B: 00 (/ex nick) deny speech from nick, 01 (/in nick) allow speech from nick
+ int i, pos;
+ RFIFOHEAD(fd);
+
+ memset(output, '\0', sizeof(output));
+
+ nick = (char*)RFIFOP(fd,2); // speed up
+ RFIFOB(fd,NAME_LENGTH+1) = '\0'; // to be sure that the player name have at maximum 23 characters (nick range: [2]->[26])
+ //printf("Ignore: char '%s' state: %d\n", nick, RFIFOB(fd,26));
+
+ WFIFOHEAD(fd,packet_len_table[0xd1]);
+ WFIFOW(fd,0) = 0x0d1; // R 00d1 <type>.B <fail>.B: type: 0: deny, 1: allow, fail: 0: success, 1: fail
+ WFIFOB(fd,2) = RFIFOB(fd,26);
+ // do nothing only if nick can not exist
+ if (strlen(nick) < 4) {
+ WFIFOB(fd,3) = 1; // fail
+ WFIFOSET(fd, packet_len_table[0x0d1]);
+ if (RFIFOB(fd,26) == 0) // type
+ clif_wis_message(fd, wisp_server_name, "It's impossible to block this player.", strlen("It's impossible to block this player.") + 1);
+ else
+ clif_wis_message(fd, wisp_server_name, "It's impossible to unblock this player.", strlen("It's impossible to unblock this player.") + 1);
+ return;
+ // name can exist
+ } else {
+ // deny action (we add nick only if it's not already exist
+ if (RFIFOB(fd,26) == 0) { // type
+ pos = -1;
+ for(i = 0; i < MAX_IGNORE_LIST; i++) {
+ if (strcmp(sd->ignore[i].name, nick) == 0) {
+ WFIFOB(fd,3) = 1; // fail
+ WFIFOSET(fd, packet_len_table[0x0d1]);
+ clif_wis_message(fd, wisp_server_name, "This player is already blocked.", strlen("This player is already blocked.") + 1);
+ if (strcmp(wisp_server_name, nick) == 0) { // to found possible bot users who automaticaly ignore people.
+ sprintf(output, "Character '%s' (account: %d) has tried AGAIN to block wisps from '%s' (wisp name of the server). Bot user?", sd->status.name, sd->status.account_id, wisp_server_name);
+ intif_wis_message_to_gm(wisp_server_name, battle_config.hack_info_GM_level, output);
+ }
+ return;
+ } else if (pos == -1 && sd->ignore[i].name[0] == '\0')
+ pos = i;
+ }
+ // if a position is found and name not found, we add it in the list
+ if (pos != -1) {
+ memcpy(sd->ignore[pos].name, nick, NAME_LENGTH-1);
+ WFIFOB(fd,3) = 0; // success
+ WFIFOSET(fd, packet_len_table[0x0d1]);
+ if (strcmp(wisp_server_name, nick) == 0) { // to found possible bot users who automaticaly ignore people.
+ sprintf(output, "Character '%s' (account: %d) has tried to block wisps from '%s' (wisp name of the server). Bot user?", sd->status.name, sd->status.account_id, wisp_server_name);
+ intif_wis_message_to_gm(wisp_server_name, battle_config.hack_info_GM_level, output);
+ // send something to be inform and force bot to ignore twice... If GM receiving block + block again, it's a bot :)
+ clif_wis_message(fd, wisp_server_name, "Add me in your ignore list, doesn't block my wisps.", strlen("Add me in your ignore list, doesn't block my wisps.") + 1);
+ }
+ } else {
+ WFIFOB(fd,3) = 1; // fail
+ WFIFOSET(fd, packet_len_table[0x0d1]);
+ clif_wis_message(fd, wisp_server_name, "You can not block more people.", strlen("You can not block more people.") + 1);
+ if (strcmp(wisp_server_name, nick) == 0) { // to found possible bot users who automaticaly ignore people.
+ sprintf(output, "Character '%s' (account: %d) has tried to block wisps from '%s' (wisp name of the server). Bot user?", sd->status.name, sd->status.account_id, wisp_server_name);
+ intif_wis_message_to_gm(wisp_server_name, battle_config.hack_info_GM_level, output);
+ }
+ }
+ // allow action (we remove all same nicks if they exist)
+ } else {
+ pos = -1;
+ for(i = 0; i < MAX_IGNORE_LIST; i++)
+ if (strcmp(sd->ignore[i].name, nick) == 0) {
+ memset(sd->ignore[i].name, 0, sizeof(sd->ignore[i].name));
+ if (pos == -1) {
+ WFIFOB(fd,3) = 0; // success
+ WFIFOSET(fd, packet_len_table[0x0d1]);
+ pos = i; // don't break, to remove ALL same nick
+ }
+ }
+ if (pos == -1) {
+ WFIFOB(fd,3) = 1; // fail
+ WFIFOSET(fd, packet_len_table[0x0d1]);
+ clif_wis_message(fd, wisp_server_name, "This player is not blocked by you.", strlen("This player is not blocked by you.") + 1);
+ }
+ }
+ }
+
+// for(i = 0; i < MAX_IGNORE_LIST; i++) // for debug only
+// if (sd->ignore[i].name[0] != '\0')
+// printf("Ignored player: '%s'\n", sd->ignore[i].name);
+
+ return;
+}
+
+void clif_parse_PMIgnoreAll(int fd, struct map_session_data *sd) { // Rewritten by [Yor]
+ //printf("Ignore all: state: %d\n", RFIFOB(fd,2));
+ RFIFOHEAD(fd);
+ if (RFIFOB(fd,2) == 0) {// S 00d0 <type>len.B: 00 (/exall) deny all speech, 01 (/inall) allow all speech
+ WFIFOHEAD(fd,packet_len_table[0xd2]);
+ WFIFOW(fd,0) = 0x0d2; // R 00d2 <type>.B <fail>.B: type: 0: deny, 1: allow, fail: 0: success, 1: fail
+ WFIFOB(fd,2) = 0;
+ if (sd->ignoreAll == 0) {
+ sd->ignoreAll = 1;
+ WFIFOB(fd,3) = 0; // success
+ WFIFOSET(fd, packet_len_table[0x0d2]);
+ } else {
+ WFIFOB(fd,3) = 1; // fail
+ WFIFOSET(fd, packet_len_table[0x0d2]);
+ clif_wis_message(fd, wisp_server_name, "You already block everyone.", strlen("You already block everyone.") + 1);
+ }
+ } else {
+ WFIFOHEAD(fd,packet_len_table[0xd2]);
+ WFIFOW(fd,0) = 0x0d2; // R 00d2 <type>.B <fail>.B: type: 0: deny, 1: allow, fail: 0: success, 1: fail
+ WFIFOB(fd,2) = 1;
+ if (sd->ignoreAll == 1) {
+ sd->ignoreAll = 0;
+ WFIFOB(fd,3) = 0; // success
+ WFIFOSET(fd, packet_len_table[0x0d2]);
+ } else {
+ WFIFOB(fd,3) = 1; // fail
+ WFIFOSET(fd, packet_len_table[0x0d2]);
+ clif_wis_message(fd, wisp_server_name, "You already allow everyone.", strlen("You already allow everyone.") + 1);
+ }
+ }
+
+ return;
+}
+
+/*==========================================
+ * Wis拒否リスト
+ *------------------------------------------
+ */
+ int pstrcmp(const void *a, const void *b)
+{
+ return strcmp((char *)a, (char *)b);
+}
+void clif_parse_PMIgnoreList(int fd,struct map_session_data *sd)
+{
+ int i,j=0,count=0;
+
+ qsort (sd->ignore[0].name, MAX_IGNORE_LIST, sizeof(sd->ignore[0].name), pstrcmp);
+ for(i = 0; i < MAX_IGNORE_LIST; i++){ //中身があるのを数える
+ if(sd->ignore[i].name[0] != 0)
+ count++;
+ }
+ WFIFOHEAD(fd, 4 + (NAME_LENGTH * count));
+ WFIFOW(fd,0) = 0xd4;
+ WFIFOW(fd,2) = 4 + (NAME_LENGTH * count);
+ for(i = 0; i < MAX_IGNORE_LIST; i++){
+ if(sd->ignore[i].name[0] != 0){
+ memcpy(WFIFOP(fd, 4 + j * 24),sd->ignore[i].name, NAME_LENGTH);
+ j++;
+ }
+ }
+ WFIFOSET(fd, WFIFOW(fd,2));
+ if(count >= MAX_IGNORE_LIST) //満タンなら最後の1個を消す
+ sd->ignore[MAX_IGNORE_LIST - 1].name[0] = 0;
+
+ return;
+}
+
+/*==========================================
+ * スパノビの/doridoriによるSPR2倍
+ *------------------------------------------
+ */
+void clif_parse_NoviceDoriDori(int fd, struct map_session_data *sd) {
+ int level;
+ sd->doridori_counter = 1;
+ if ((sd->class_&MAPID_UPPERMASK) == MAPID_TAEKWON
+ && sd->state.rest && (level = pc_checkskill(sd,TK_SPTIME)))
+ status_change_start(&sd->bl,SkillStatusChangeTable[TK_SPTIME],level,0,0,0,skill_get_time(TK_SPTIME, level),0);
+ return;
+}
+/*==========================================
+ * スパノビの爆裂波動
+ *------------------------------------------
+ */
+void clif_parse_NoviceExplosionSpirits(int fd, struct map_session_data *sd)
+{
+ if(sd){
+ int nextbaseexp=pc_nextbaseexp(sd);
+ if (battle_config.etc_log){
+ if(nextbaseexp != 0)
+ ShowInfo("SuperNovice explosionspirits!! %d %d %d %d\n",sd->bl.id,sd->status.class_,sd->status.base_exp,(int)((double)1000*sd->status.base_exp/nextbaseexp));
+ else
+ ShowInfo("SuperNovice explosionspirits!! %d %d %d 000\n",sd->bl.id,sd->status.class_,sd->status.base_exp);
+ }
+ if((sd->class_&MAPID_UPPERMASK) == MAPID_SUPER_NOVICE && sd->status.base_exp > 0 && nextbaseexp > 0 && (int)((double)1000*sd->status.base_exp/nextbaseexp)%100==0){
+ clif_skill_nodamage(&sd->bl,&sd->bl,MO_EXPLOSIONSPIRITS,5,1);
+ status_change_start(&sd->bl,SkillStatusChangeTable[MO_EXPLOSIONSPIRITS],5,0,0,0,skill_get_time(MO_EXPLOSIONSPIRITS,5),0 );
+ }
+ }
+ return;
+}
+
+// random notes:
+// 0x214: monster/player info ?
+
+/*==========================================
+ * Friends List
+ *------------------------------------------
+ */
+void clif_friendslist_toggle(struct map_session_data *sd,int account_id, int char_id, int online)
+{ //Toggles a single friend online/offline [Skotlex]
+ int i;
+
+ //Seek friend.
+ for (i = 0; i < MAX_FRIENDS && sd->status.friends[i].char_id &&
+ (sd->status.friends[i].char_id != char_id || sd->status.friends[i].account_id != account_id); i++);
+
+ if(i == MAX_FRIENDS || sd->status.friends[i].char_id == 0)
+ return; //Not found
+
+ WFIFOHEAD(sd->fd,packet_len_table[0x206]);
+ WFIFOW(sd->fd, 0) = 0x206;
+ WFIFOL(sd->fd, 2) = sd->status.friends[i].account_id;
+ WFIFOL(sd->fd, 6) = sd->status.friends[i].char_id;
+ WFIFOB(sd->fd,10) = !online; //Yeah, a 1 here means "logged off", go figure...
+
+ WFIFOSET(sd->fd, packet_len_table[0x206]);
+}
+
+//Subfunction called from clif_foreachclient to toggle friends on/off [Skotlex]
+int clif_friendslist_toggle_sub(struct map_session_data *sd,va_list ap)
+{
+ int account_id, char_id, online;
+ account_id = va_arg(ap, int);
+ char_id = va_arg(ap, int);
+ online = va_arg(ap, int);
+ clif_friendslist_toggle(sd, account_id, char_id, online);
+ return 0;
+}
+
+//For sending the whole friends list.
+void clif_friendslist_send(struct map_session_data *sd) {
+ int i = 0, n;
+
+ // Send friends list
+ WFIFOHEAD(sd->fd, MAX_FRIENDS * 32 + 4);
+ WFIFOW(sd->fd, 0) = 0x201;
+ for(i = 0; i < MAX_FRIENDS && sd->status.friends[i].char_id; i++)
+ {
+ WFIFOL(sd->fd, 4 + 32 * i + 0) = sd->status.friends[i].account_id;
+ WFIFOL(sd->fd, 4 + 32 * i + 4) = sd->status.friends[i].char_id;
+ memcpy(WFIFOP(sd->fd, 4 + 32 * i + 8), &sd->status.friends[i].name, NAME_LENGTH);
+ }
+
+ WFIFOW(sd->fd,2) = 4 + 32 * i;
+ WFIFOSET(sd->fd, WFIFOW(sd->fd,2));
+
+ for (n = 0; n < i; n++)
+ { //Sending the online players
+ if (map_charid2sd(sd->status.friends[n].char_id))
+ clif_friendslist_toggle(sd, sd->status.friends[n].account_id, sd->status.friends[n].char_id, 1);
+ }
+}
+
+
+// Status for adding friend - 0: successfull 1: not exist/rejected 2: over limit
+void clif_friendslist_reqack(struct map_session_data *sd, struct map_session_data *f_sd, int type)
+{
+ int fd;
+ nullpo_retv(sd);
+
+ fd = sd->fd;
+ WFIFOHEAD(fd,packet_len_table[0x209]);
+ WFIFOW(fd,0) = 0x209;
+ WFIFOW(fd,2) = type;
+ if (f_sd)
+ {
+ WFIFOW(fd,4) = f_sd->status.account_id;
+ WFIFOW(fd,8) = f_sd->status.char_id;
+ memcpy(WFIFOP(fd, 12), f_sd->status.name,NAME_LENGTH);
+ }
+ WFIFOSET(fd, packet_len_table[0x209]);
+}
+
+void clif_parse_FriendsListAdd(int fd, struct map_session_data *sd) {
+ struct map_session_data *f_sd;
+ int i, f_fd;
+ RFIFOHEAD(fd);
+
+ f_sd = map_nick2sd((char*)RFIFOP(fd,2));
+
+ // Friend doesn't exist (no player with this name)
+ if (f_sd == NULL) {
+ clif_displaymessage(fd, msg_txt(3));
+ return;
+ }
+
+ // Friend already exists
+ for (i = 0; i < MAX_FRIENDS && sd->status.friends[i].char_id != 0; i++) {
+ if (sd->status.friends[i].char_id == f_sd->status.char_id) {
+ clif_displaymessage(fd, "Friend already exists.");
+ return;
+ }
+ }
+
+ if (i == MAX_FRIENDS) {
+ //No space, list full.
+ clif_friendslist_reqack(sd, f_sd, 2);
+ return;
+ }
+
+ f_fd = f_sd->fd;
+ WFIFOHEAD(fd,packet_len_table[0x207]);
+ WFIFOW(f_fd,0) = 0x207;
+ WFIFOL(f_fd,2) = sd->status.account_id;
+ WFIFOL(f_fd,6) = sd->status.char_id;
+ memcpy(WFIFOP(f_fd,10), sd->status.name, NAME_LENGTH);
+ WFIFOSET(f_fd, packet_len_table[0x207]);
+
+ return;
+}
+
+void clif_parse_FriendsListReply(int fd, struct map_session_data *sd) {
+ //<W: id> <L: Player 1 chara ID> <L: Player 1 AID> <B: Response>
+ struct map_session_data *f_sd;
+ int char_id, account_id;
+ char reply;
+ RFIFOHEAD(fd);
+
+ account_id = RFIFOL(fd,2);
+ char_id = RFIFOL(fd,6);
+ reply = RFIFOB(fd,10);
+ //printf ("reply: %d %d %d\n", char_id, id, reply);
+
+ f_sd = map_id2sd(account_id); //The account id is the same as the bl.id of players.
+ if (f_sd == NULL)
+ return;
+
+ if (reply == 0)
+ clif_friendslist_reqack(f_sd, sd, 1);
+ else {
+ int i;
+ // Find an empty slot
+ for (i = 0; i < MAX_FRIENDS; i++)
+ if (f_sd->status.friends[i].char_id == 0)
+ break;
+ if (i == MAX_FRIENDS) {
+ clif_friendslist_reqack(f_sd, sd, 2);
+ return;
+ }
+
+ f_sd->status.friends[i].account_id = sd->status.account_id;
+ f_sd->status.friends[i].char_id = sd->status.char_id;
+ memcpy(f_sd->status.friends[i].name, sd->status.name, NAME_LENGTH);
+ clif_friendslist_reqack(f_sd, sd, 0);
+
+// clif_friendslist_send(sd); //This is not needed anymore.
+ }
+
+ return;
+}
+
+void clif_parse_FriendsListRemove(int fd, struct map_session_data *sd) {
+ // 0x203 </o> <ID to be removed W 4B>
+ int account_id, char_id;
+ int i, j;
+ RFIFOHEAD(fd);
+
+ account_id = RFIFOL(fd,2);
+ char_id = RFIFOL(fd,6);
+
+ // Search friend
+ for (i = 0; i < MAX_FRIENDS &&
+ (sd->status.friends[i].char_id != char_id || sd->status.friends[i].account_id != account_id); i++);
+
+ if (i == MAX_FRIENDS) {
+ clif_displaymessage(fd, "Name not found in list.");
+ return;
+ }
+
+ // move all chars down
+ for(j = i + 1; j < MAX_FRIENDS; j++)
+ memcpy(&sd->status.friends[j-1], &sd->status.friends[j], sizeof(sd->status.friends[0]));
+
+ memset(&sd->status.friends[MAX_FRIENDS-1], 0, sizeof(sd->status.friends[MAX_FRIENDS-1]));
+ clif_displaymessage(fd, "Friend removed");
+
+ WFIFOHEAD(fd,packet_len_table[0x20a]);
+ WFIFOW(fd,0) = 0x20a;
+ WFIFOL(fd,2) = account_id;
+ WFIFOL(fd,6) = char_id;
+ WFIFOSET(fd, packet_len_table[0x20a]);
+// clif_friendslist_send(sd); //This is not needed anymore.
+
+ return;
+}
+
+/*==========================================
+ * /killall
+ *------------------------------------------
+ */
+void clif_parse_GMKillAll(int fd,struct map_session_data *sd)
+{
+ char message[50];
+
+ strncpy(message,sd->status.name, NAME_LENGTH);
+ is_atcommand(fd, sd, strcat(message," : @kickall"),0);
+
+ return;
+}
+
+/*==========================================
+ * /pvpinfo
+ *------------------------------------------
+ */
+void clif_parse_PVPInfo(int fd,struct map_session_data *sd)
+{
+ WFIFOHEAD(fd,packet_len_table[0x210]);
+ WFIFOW(fd,0) = 0x210;
+ //WFIFOL(fd,2) = 0; // not sure what for yet
+ //WFIFOL(fd,6) = 0;
+ WFIFOL(fd,10) = sd->pvp_won; // times won
+ WFIFOL(fd,14) = sd->pvp_lost; // times lost
+ WFIFOL(fd,18) = sd->pvp_point;
+ WFIFOSET(fd, packet_len_table[0x210]);
+
+ return;
+}
+
+/*==========================================
+ * /blacksmith
+ *------------------------------------------
+ */
+void clif_parse_Blacksmith(int fd,struct map_session_data *sd)
+{
+ int i;
+ char *name;
+
+ WFIFOHEAD(fd,packet_len_table[0x219]);
+ WFIFOW(fd,0) = 0x219;
+ for (i = 0; i < 10; i++) {
+ if (smith_fame_list[i].id > 0) {
+ if (strcmp(smith_fame_list[i].name, "-") == 0 &&
+ (name = map_charid2nick(smith_fame_list[i].id)) != NULL)
+ {
+ strncpy((char *)(WFIFOP(fd, 2 + 24 * i)), name, NAME_LENGTH);
+ } else
+ strncpy((char *)(WFIFOP(fd, 2 + 24 * i)), smith_fame_list[i].name, NAME_LENGTH);
+ } else
+ strncpy((char *)(WFIFOP(fd, 2 + 24 * i)), "None", 5);
+ WFIFOL(fd, 242 + i * 4) = smith_fame_list[i].fame;
+ }
+ WFIFOSET(fd, packet_len_table[0x219]);
+}
+
+int clif_fame_blacksmith(struct map_session_data *sd, int points)
+{
+ int fd = sd->fd;
+ WFIFOHEAD(fd,packet_len_table[0x21b]);
+ WFIFOW(fd,0) = 0x21b;
+ WFIFOL(fd,2) = points;
+ WFIFOL(fd,6) = sd->status.fame;
+ WFIFOSET(fd, packet_len_table[0x21b]);
+
+ return 0;
+}
+
+/*==========================================
+ * /alchemist
+ *------------------------------------------
+ */
+void clif_parse_Alchemist(int fd,struct map_session_data *sd)
+{
+ int i;
+ char *name;
+
+ WFIFOHEAD(fd,packet_len_table[0x21a]);
+ WFIFOW(fd,0) = 0x21a;
+ for (i = 0; i < 10; i++) {
+ if (chemist_fame_list[i].id > 0) {
+ if (strcmp(chemist_fame_list[i].name, "-") == 0 &&
+ (name = map_charid2nick(chemist_fame_list[i].id)) != NULL)
+ {
+ memcpy(WFIFOP(fd, 2 + 24 * i), name, NAME_LENGTH);
+ } else
+ memcpy(WFIFOP(fd, 2 + 24 * i), chemist_fame_list[i].name, NAME_LENGTH);
+ } else
+ memcpy(WFIFOP(fd, 2 + 24 * i), "None", NAME_LENGTH);
+ WFIFOL(fd, 242 + i * 4) = chemist_fame_list[i].fame;
+ }
+ WFIFOSET(fd, packet_len_table[0x21a]);
+}
+
+int clif_fame_alchemist(struct map_session_data *sd, int points)
+{
+ int fd = sd->fd;
+ WFIFOHEAD(fd,packet_len_table[0x21c]);
+ WFIFOW(fd,0) = 0x21c;
+ WFIFOL(fd,2) = points;
+ WFIFOL(fd,6) = sd->status.fame;
+ WFIFOSET(fd, packet_len_table[0x21c]);
+
+ return 0;
+}
+
+/*==========================================
+ * /taekwon
+ *------------------------------------------
+ */
+void clif_parse_Taekwon(int fd,struct map_session_data *sd)
+{
+ int i;
+ char *name;
+
+ WFIFOHEAD(fd,packet_len_table[0x226]);
+ WFIFOW(fd,0) = 0x226;
+ for (i = 0; i < 10; i++) {
+ if (taekwon_fame_list[i].id > 0) {
+ if (strcmp(taekwon_fame_list[i].name, "-") == 0 &&
+ (name = map_charid2nick(taekwon_fame_list[i].id)) != NULL)
+ {
+ memcpy(WFIFOP(fd, 2 + 24 * i), name, NAME_LENGTH);
+ } else
+ memcpy(WFIFOP(fd, 2 + 24 * i), taekwon_fame_list[i].name, NAME_LENGTH);
+ } else
+ memcpy(WFIFOP(fd, 2 + 24 * i), "None", NAME_LENGTH);
+ WFIFOL(fd, 242 + i * 4) = taekwon_fame_list[i].fame;
+ }
+ WFIFOSET(fd, packet_len_table[0x226]);
+}
+
+int clif_fame_taekwon(struct map_session_data *sd, int points)
+{
+ int fd = sd->fd;
+ WFIFOHEAD(fd,packet_len_table[0x224]);
+ WFIFOW(fd,0) = 0x224;
+ WFIFOL(fd,2) = points;
+ WFIFOL(fd,6) = sd->status.fame;
+ WFIFOSET(fd, packet_len_table[0x224]);
+
+ return 0;
+}
+
+/*==========================================
+ * PK Ranking table?
+ *------------------------------------------
+ */
+void clif_parse_RankingPk(int fd,struct map_session_data *sd)
+{
+ int i;
+
+ WFIFOHEAD(fd,packet_len_table[0x238]);
+ WFIFOW(fd,0) = 0x238;
+ for(i=0;i<10;i++){
+ memcpy(WFIFOP(fd,i*24+2), "Unknown", NAME_LENGTH);
+ WFIFOL(fd,i*4+242) = 0;
+ }
+ WFIFOSET(fd, packet_len_table[0x238]);
+ return;
+}
+
+/*==========================================
+ * SG Feel save OK [Komurka]
+ *------------------------------------------
+ */
+void clif_parse_FeelSaveOk(int fd,struct map_session_data *sd)
+{
+ if (sd->feel_level!=-1)
+ {
+ char feel_var[3][NAME_LENGTH] = {"PC_FEEL_SUN","PC_FEEL_MOON","PC_FEEL_STAR"};
+ sd->feel_map[sd->feel_level].index = map[sd->bl.m].index;
+ sd->feel_map[sd->feel_level].m = sd->bl.m;
+ pc_setglobalreg(sd,feel_var[sd->feel_level],map[sd->bl.m].index);
+
+ WFIFOHEAD(fd,packet_len_table[0x20e]);
+ WFIFOW(fd,0)=0x20e;
+ memcpy(WFIFOP(fd,2),map[sd->bl.m].name, MAP_NAME_LENGTH);
+ WFIFOL(fd,26)=sd->bl.id;
+ WFIFOW(fd,30)=sd->feel_level;
+ WFIFOSET(fd, packet_len_table[0x20e]);
+ sd->feel_level = -1;
+ if (pc_checkskill(sd,SG_KNOWLEDGE)) status_calc_pc(sd,0);
+ }
+}
+
+/*==========================================
+ * Question about Star Glaldiator save map [Komurka]
+ *------------------------------------------
+ */
+void clif_parse_ReqFeel(int fd, struct map_session_data *sd) {
+ WFIFOHEAD(fd,packet_len_table[0x253]);
+ WFIFOW(fd,0)=0x253;
+ WFIFOSET(fd, packet_len_table[0x253]);
+}
+
+/*==========================================
+ * パケットデバッグ
+ *------------------------------------------
+ */
+void clif_parse_debug(int fd,struct map_session_data *sd)
+{
+ int i, cmd, len;
+ RFIFOHEAD(fd);
+
+ cmd = RFIFOW(fd,0);
+ len = sd?packet_db[sd->packet_ver][cmd].len:RFIFOREST(fd); //With no session, just read the remaining in the buffer.
+ ShowDebug("packet debug 0x%4X\n",cmd);
+ printf("---- 00-01-02-03-04-05-06-07-08-09-0A-0B-0C-0D-0E-0F");
+ for(i=0;i<len;i++){
+ if((i&15)==0)
+ printf("\n%04X ",i);
+ printf("%02X ",RFIFOB(fd,i));
+ }
+ printf("\n");
+}
+
+/*==========================================
+ * クライアントからのパケット解析
+ * socket.cのdo_parsepacketから呼び出される
+ *------------------------------------------
+ */
+int clif_parse(int fd) {
+ int packet_len = 0, cmd, packet_ver, dump = 0;
+ struct map_session_data *sd;
+ RFIFOHEAD(fd);
+
+ if (fd <= 0)
+ { //Just in case, there are some checks for this later down below anyway which should be removed. [Skotlex]
+ ShowError("clif_parse: Received invalid session %d\n", fd);
+ return 0;
+ }
+
+ sd = (struct map_session_data*)session[fd]->session_data;
+
+ // 接続が切れてるので後始末
+ if (!chrif_isconnect() && kick_on_disconnect)
+ {
+ ShowInfo("Closing session #%d (Not connected to Char server)\n", fd);
+ if (sd && sd->state.auth)
+ clif_quitsave(fd, sd); // the function doesn't send to inter-server/char-server if it is not connected [Yor]
+ do_close(fd);
+ return 0;
+ } else
+ if (session[fd]->eof) {
+ if (sd && sd->state.autotrade) {
+ //Disassociate character from the socket connection.
+ session[fd]->session_data = NULL;
+ sd->fd = 0;
+ ShowInfo("%sCharacter '"CL_WHITE"%s"CL_RESET"' logged off (using @autotrade).\n", (pc_isGM(sd))?"GM ":"",sd->status.name); // Player logout display [Valaris]
+ } else if (sd && sd->state.auth) {
+ clif_quitsave(fd, sd); // the function doesn't send to inter-server/char-server if it is not connected [Yor]
+ if (sd->status.name != NULL)
+ ShowInfo("%sCharacter '"CL_WHITE"%s"CL_RESET"' logged off.\n", (pc_isGM(sd))?"GM ":"",sd->status.name); // Player logout display [Valaris]
+ else
+ ShowInfo("%sCharacter with Account ID '"CL_WHITE"%d"CL_RESET"' logged off.\n", (pc_isGM(sd))?"GM ":"", sd->bl.id); // Player logout display [Yor]
+ } else {
+ unsigned char *ip = (unsigned char *) &session[fd]->client_addr.sin_addr;
+ ShowInfo("Player not identified with IP '"CL_WHITE"%d.%d.%d.%d"CL_RESET"' logged off.\n", ip[0],ip[1],ip[2],ip[3]);
+ }
+ do_close(fd);
+ return 0;
+ }
+
+ if (RFIFOREST(fd) < 2)
+ return 0;
+
+// printf("clif_parse: connection #%d, packet: 0x%x (with being read: %d bytes).\n", fd, RFIFOW(fd,0), RFIFOREST(fd));
+
+ cmd = RFIFOW(fd,0);
+
+ // 管理用パケット処理
+ if (cmd >= 30000) {
+ switch(cmd) {
+ case 0x7530: { // Athena情報所得
+ WFIFOHEAD(fd, 10);
+ WFIFOW(fd,0) = 0x7531;
+ WFIFOB(fd,2) = ATHENA_MAJOR_VERSION;
+ WFIFOB(fd,3) = ATHENA_MINOR_VERSION;
+ WFIFOB(fd,4) = ATHENA_REVISION;
+ WFIFOB(fd,5) = ATHENA_RELEASE_FLAG;
+ WFIFOB(fd,6) = ATHENA_OFFICIAL_FLAG;
+ WFIFOB(fd,7) = ATHENA_SERVER_MAP;
+ WFIFOW(fd,8) = ATHENA_MOD_VERSION;
+ WFIFOSET(fd,10);
+ RFIFOSKIP(fd,2);
+ break;
+ }
+ case 0x7532: // 接続の切断
+ ShowWarning("clif_parse: session #%d disconnected for sending packet 0x04%x\n", fd, cmd);
+ session[fd]->eof=1;
+ break;
+ }
+ ShowWarning("Ignoring incoming packet (command: 0x%04x, session: %d)\n", cmd, fd);
+ return 0;
+ }
+
+ // get packet version before to parse
+ packet_ver = 0;
+ if (sd) {
+ packet_ver = sd->packet_ver;
+ if (packet_ver < 0 || packet_ver > MAX_PACKET_VER) { // This should never happen unless we have some corrupted memory issues :X [Skotlex]
+ session[fd]->eof = 1;
+ return 0;
+ }
+ } else {
+ // check authentification packet to know packet version
+ packet_ver = clif_guess_PacketVer(fd, 0);
+ // check if version is accepted
+ if (packet_ver < 5 || // reject really old client versions
+ (packet_ver <= 9 && (battle_config.packet_ver_flag & 1) == 0) || // older than 6sept04
+ (packet_ver > 9 && (battle_config.packet_ver_flag & 1<<(packet_ver-9)) == 0) ||
+ packet_ver > MAX_PACKET_VER) // no packet version support yet
+ {
+ ShowInfo("clif_parse: Disconnecting session #%d for not having latest client version (has version %d).\n", fd, packet_ver);
+ WFIFOHEAD(fd, 23);
+ WFIFOW(fd,0) = 0x6a;
+ WFIFOB(fd,2) = 5; // 05 = Game's EXE is not the latest version
+ WFIFOSET(fd,23);
+ packet_len = RFIFOREST(fd);
+ RFIFOSKIP(fd, packet_len);
+ clif_setwaitclose(fd);
+ if (session[fd]->func_send) //socket.c doesn't wants to send the data when left on it's own... [Skotlex]
+ session[fd]->func_send(fd);
+ return 0;
+ }
+ }
+
+ // ゲーム用以外パケットか、認証を終える前に0072以外が来たら、切断する
+ if (cmd >= MAX_PACKET_DB || packet_db[packet_ver][cmd].len == 0) { // if packet is not inside these values: session is incorrect?? or auth packet is unknown
+ ShowWarning("clif_parse: Received unsupported packet (packet 0x%04x, %d bytes received), disconnecting session #%d.\n", cmd, RFIFOREST(fd), fd);
+ session[fd]->eof = 1;
+ return 0;
+ }
+
+ // パケット長を計算
+ packet_len = packet_db[packet_ver][cmd].len;
+ if (packet_len == -1) {
+ if (RFIFOREST(fd) < 4)
+ return 0; // 可変長パケットで長さの所までデータが来てない
+
+ packet_len = RFIFOW(fd,2);
+ if (packet_len < 4 || packet_len > 32768) {
+ ShowWarning("clif_parse: Packet 0x%04x specifies invalid packet_len (%d), disconnecting session #%d.\n", cmd, packet_len, fd);
+ session[fd]->eof =1;
+ return 0;
+ }
+ }
+ if ((int)RFIFOREST(fd) < packet_len)
+ return 0; // まだ1パケット分データが揃ってない
+
+ #if DUMP_ALL_PACKETS
+ dump = 1;
+ #endif
+
+ if (sd && sd->state.auth == 1 && sd->state.waitingdisconnect == 1) { // 切断待ちの場合パケットを処理しない
+
+ } else if (packet_db[packet_ver][cmd].func) {
+ if (sd
+ || packet_db[packet_ver][cmd].func == clif_parse_WantToConnection
+ || packet_db[packet_ver][cmd].func == clif_parse_debug
+ ) //Only execute the function when there's an sd (except for debug/wanttoconnect packets)
+ packet_db[packet_ver][cmd].func(fd, sd);
+ } else {
+ // 不明なパケット
+ if (battle_config.error_log) {
+#if DUMP_UNKNOWN_PACKET
+ {
+ int i;
+ FILE *fp;
+ char packet_txt[256] = "save/packet.txt";
+ time_t now;
+ dump = 1;
+
+ if ((fp = fopen(packet_txt, "a")) == NULL) {
+ ShowError("clif.c: cant write [%s] !!! data is lost !!!\n", packet_txt);
+ return 1;
+ } else {
+ time(&now);
+ if (sd && sd->state.auth) {
+ if (sd->status.name != NULL)
+ fprintf(fp, "%sPlayer with account ID %d (character ID %d, player name %s) sent wrong packet:\n",
+ asctime(localtime(&now)), sd->status.account_id, sd->status.char_id, sd->status.name);
+ else
+ fprintf(fp, "%sPlayer with account ID %d sent wrong packet:\n", asctime(localtime(&now)), sd->bl.id);
+ } else if (sd) // not authentified! (refused by char-server or disconnect before to be authentified)
+ fprintf(fp, "%sPlayer with account ID %d sent wrong packet:\n", asctime(localtime(&now)), sd->bl.id);
+
+ fprintf(fp, "\t---- 00-01-02-03-04-05-06-07-08-09-0A-0B-0C-0D-0E-0F");
+ for(i = 0; i < packet_len; i++) {
+ if ((i & 15) == 0)
+ fprintf(fp, "\n\t%04X ", i);
+ fprintf(fp, "%02X ", RFIFOB(fd,i));
+ }
+ fprintf(fp, "\n\n");
+ fclose(fp);
+ }
+ }
+#endif
+ }
+ }
+
+ if (dump) {
+ int i;
+ if (fd)
+ ShowDebug("\nclif_parse: session #%d, packet 0x%04x, length %d, version %d\n", fd, cmd, packet_len, packet_ver);
+ printf("---- 00-01-02-03-04-05-06-07-08-09-0A-0B-0C-0D-0E-0F");
+ for(i = 0; i < packet_len; i++) {
+ if ((i & 15) == 0)
+ printf("\n%04X ",i);
+ printf("%02X ", RFIFOB(fd,i));
+ }
+ printf("\n");
+ if (sd && sd->state.auth) {
+ if (sd->status.name != NULL)
+ printf("\nAccount ID %d, character ID %d, player name %s.\n",
+ sd->status.account_id, sd->status.char_id, sd->status.name);
+ else
+ printf("\nAccount ID %d.\n", sd->bl.id);
+ } else if (sd) // not authentified! (refused by char-server or disconnect before to be authentified)
+ printf("\nAccount ID %d.\n", sd->bl.id);
+ }
+
+ RFIFOSKIP(fd, packet_len);
+
+ return 0;
+}
+
+/*==========================================
+ * パケットデータベース読み込み
+ *------------------------------------------
+ */
+static int packetdb_readdb(void)
+{
+ FILE *fp;
+ char line[1024];
+ int ln=0;
+ int cmd,i,j,k,packet_ver;
+ char *str[64],*p,*str2[64],*p2,w1[64],w2[64];
+
+ struct {
+ void (*func)(int, struct map_session_data *);
+ char *name;
+ } clif_parse_func[]={
+ {clif_parse_WantToConnection,"wanttoconnection"},
+ {clif_parse_LoadEndAck,"loadendack"},
+ {clif_parse_TickSend,"ticksend"},
+ {clif_parse_WalkToXY,"walktoxy"},
+ {clif_parse_QuitGame,"quitgame"},
+ {clif_parse_GetCharNameRequest,"getcharnamerequest"},
+ {clif_parse_GlobalMessage,"globalmessage"},
+ {clif_parse_MapMove,"mapmove"},
+ {clif_parse_ChangeDir,"changedir"},
+ {clif_parse_Emotion,"emotion"},
+ {clif_parse_HowManyConnections,"howmanyconnections"},
+ {clif_parse_ActionRequest,"actionrequest"},
+ {clif_parse_Restart,"restart"},
+ {clif_parse_Wis,"wis"},
+ {clif_parse_GMmessage,"gmmessage"},
+ {clif_parse_TakeItem,"takeitem"},
+ {clif_parse_DropItem,"dropitem"},
+ {clif_parse_UseItem,"useitem"},
+ {clif_parse_EquipItem,"equipitem"},
+ {clif_parse_UnequipItem,"unequipitem"},
+ {clif_parse_NpcClicked,"npcclicked"},
+ {clif_parse_NpcBuySellSelected,"npcbuysellselected"},
+ {clif_parse_NpcBuyListSend,"npcbuylistsend"},
+ {clif_parse_NpcSellListSend,"npcselllistsend"},
+ {clif_parse_CreateChatRoom,"createchatroom"},
+ {clif_parse_ChatAddMember,"chataddmember"},
+ {clif_parse_ChatRoomStatusChange,"chatroomstatuschange"},
+ {clif_parse_ChangeChatOwner,"changechatowner"},
+ {clif_parse_KickFromChat,"kickfromchat"},
+ {clif_parse_ChatLeave,"chatleave"},
+ {clif_parse_TradeRequest,"traderequest"},
+ {clif_parse_TradeAck,"tradeack"},
+ {clif_parse_TradeAddItem,"tradeadditem"},
+ {clif_parse_TradeOk,"tradeok"},
+ {clif_parse_TradeCancel,"tradecancel"},
+ {clif_parse_TradeCommit,"tradecommit"},
+ {clif_parse_StopAttack,"stopattack"},
+ {clif_parse_PutItemToCart,"putitemtocart"},
+ {clif_parse_GetItemFromCart,"getitemfromcart"},
+ {clif_parse_RemoveOption,"removeoption"},
+ {clif_parse_ChangeCart,"changecart"},
+ {clif_parse_StatusUp,"statusup"},
+ {clif_parse_SkillUp,"skillup"},
+ {clif_parse_UseSkillToId,"useskilltoid"},
+ {clif_parse_UseSkillToPos,"useskilltopos"},
+ {clif_parse_UseSkillToPosMoreInfo,"useskilltoposinfo"},
+ {clif_parse_UseSkillMap,"useskillmap"},
+ {clif_parse_RequestMemo,"requestmemo"},
+ {clif_parse_ProduceMix,"producemix"},
+ {clif_parse_NpcSelectMenu,"npcselectmenu"},
+ {clif_parse_NpcNextClicked,"npcnextclicked"},
+ {clif_parse_NpcAmountInput,"npcamountinput"},
+ {clif_parse_NpcStringInput,"npcstringinput"},
+ {clif_parse_NpcCloseClicked,"npccloseclicked"},
+ {clif_parse_ItemIdentify,"itemidentify"},
+ {clif_parse_SelectArrow,"selectarrow"},
+ {clif_parse_AutoSpell,"autospell"},
+ {clif_parse_UseCard,"usecard"},
+ {clif_parse_InsertCard,"insertcard"},
+ {clif_parse_RepairItem,"repairitem"},
+ {clif_parse_WeaponRefine,"weaponrefine"},
+ {clif_parse_SolveCharName,"solvecharname"},
+ {clif_parse_ResetChar,"resetchar"},
+ {clif_parse_LGMmessage,"lgmmessage"},
+ {clif_parse_MoveToKafra,"movetokafra"},
+ {clif_parse_MoveFromKafra,"movefromkafra"},
+ {clif_parse_MoveToKafraFromCart,"movetokafrafromcart"},
+ {clif_parse_MoveFromKafraToCart,"movefromkafratocart"},
+ {clif_parse_CloseKafra,"closekafra"},
+ {clif_parse_CreateParty,"createparty"},
+ {clif_parse_CreateParty2,"createparty2"},
+ {clif_parse_PartyInvite,"partyinvite"},
+ {clif_parse_ReplyPartyInvite,"replypartyinvite"},
+ {clif_parse_LeaveParty,"leaveparty"},
+ {clif_parse_RemovePartyMember,"removepartymember"},
+ {clif_parse_PartyChangeOption,"partychangeoption"},
+ {clif_parse_PartyMessage,"partymessage"},
+ {clif_parse_CloseVending,"closevending"},
+ {clif_parse_VendingListReq,"vendinglistreq"},
+ {clif_parse_PurchaseReq,"purchasereq"},
+ {clif_parse_OpenVending,"openvending"},
+ {clif_parse_CreateGuild,"createguild"},
+ {clif_parse_GuildCheckMaster,"guildcheckmaster"},
+ {clif_parse_GuildRequestInfo,"guildrequestinfo"},
+ {clif_parse_GuildChangePositionInfo,"guildchangepositioninfo"},
+ {clif_parse_GuildChangeMemberPosition,"guildchangememberposition"},
+ {clif_parse_GuildRequestEmblem,"guildrequestemblem"},
+ {clif_parse_GuildChangeEmblem,"guildchangeemblem"},
+ {clif_parse_GuildChangeNotice,"guildchangenotice"},
+ {clif_parse_GuildInvite,"guildinvite"},
+ {clif_parse_GuildReplyInvite,"guildreplyinvite"},
+ {clif_parse_GuildLeave,"guildleave"},
+ {clif_parse_GuildExplusion,"guildexplusion"},
+ {clif_parse_GuildMessage,"guildmessage"},
+ {clif_parse_GuildRequestAlliance,"guildrequestalliance"},
+ {clif_parse_GuildReplyAlliance,"guildreplyalliance"},
+ {clif_parse_GuildDelAlliance,"guilddelalliance"},
+ {clif_parse_GuildOpposition,"guildopposition"},
+ {clif_parse_GuildBreak,"guildbreak"},
+ {clif_parse_PetMenu,"petmenu"},
+ {clif_parse_CatchPet,"catchpet"},
+ {clif_parse_SelectEgg,"selectegg"},
+ {clif_parse_SendEmotion,"sendemotion"},
+ {clif_parse_ChangePetName,"changepetname"},
+ {clif_parse_GMKick,"gmkick"},
+ {clif_parse_GMHide,"gmhide"},
+ {clif_parse_GMReqNoChat,"gmreqnochat"},
+ {clif_parse_GMReqNoChatCount,"gmreqnochatcount"},
+ {clif_parse_NoviceDoriDori,"sndoridori"},
+ {clif_parse_NoviceExplosionSpirits,"snexplosionspirits"},
+ {clif_parse_PMIgnore,"wisexin"},
+ {clif_parse_PMIgnoreList,"wisexlist"},
+ {clif_parse_PMIgnoreAll,"wisall"},
+ {clif_parse_FriendsListAdd,"friendslistadd"},
+ {clif_parse_FriendsListRemove,"friendslistremove"},
+ {clif_parse_FriendsListReply,"friendslistreply"},
+ {clif_parse_GMKillAll,"killall"},
+ {clif_parse_Recall,"summon"},
+ {clif_parse_GM_Monster_Item,"itemmonster"},
+ {clif_parse_Shift,"shift"},
+ {clif_parse_Blacksmith,"blacksmith"},
+ {clif_parse_Alchemist,"alchemist"},
+ {clif_parse_Taekwon,"taekwon"},
+ {clif_parse_RankingPk,"rankingpk"},
+ {clif_parse_FeelSaveOk,"feelsaveok"},
+ {clif_parse_debug,"debug"},
+
+ {NULL,NULL}
+ };
+
+ sprintf(line, "%s/packet_db.txt", db_path);
+ if( (fp=fopen(line,"r"))==NULL ){
+ ShowFatalError("can't read %s\n", line);
+ exit(1);
+ }
+
+ clif_config.packet_db_ver = MAX_PACKET_VER;
+ packet_ver = MAX_PACKET_VER; // read into packet_db's version by default
+ while(fgets(line,1020,fp)){
+ if(line[0]=='/' && line[1]=='/')
+ continue;
+ if (sscanf(line,"%[^:]: %[^\r\n]",w1,w2) == 2) {
+ if(strcmpi(w1,"packet_ver")==0) {
+ int prev_ver = packet_ver;
+ packet_ver = atoi(w2);
+ if (packet_ver > MAX_PACKET_VER)
+ { //Check to avoid overflowing. [Skotlex]
+ ShowWarning("The packet_db table only has support up to version %d\n", MAX_PACKET_VER);
+ break;
+ }
+ // copy from previous version into new version and continue
+ // - indicating all following packets should be read into the newer version
+ memcpy(&packet_db[packet_ver], &packet_db[prev_ver], sizeof(packet_db[0]));
+ continue;
+ } else if(strcmpi(w1,"packet_db_ver")==0) {
+ //This is the preferred version.
+ if(strcmpi(w2,"default")==0)
+ clif_config.packet_db_ver = MAX_PACKET_VER;
+ else {
+ // to manually set the packet DB version
+ clif_config.packet_db_ver = atoi(w2);
+ // check for invalid version
+ if (clif_config.packet_db_ver > MAX_PACKET_VER ||
+ clif_config.packet_db_ver < 0)
+ clif_config.packet_db_ver = MAX_PACKET_VER;
+ }
+ continue;
+ }
+ }
+
+ memset(str,0,sizeof(str));
+ for(j=0,p=line;j<4 && p;j++){
+ str[j]=p;
+ p=strchr(p,',');
+ if(p) *p++=0;
+ }
+ if(str[0]==NULL)
+ continue;
+ cmd=strtol(str[0],(char **)NULL,0);
+ if(cmd<=0 || cmd>=MAX_PACKET_DB)
+ continue;
+ if(str[1]==NULL){
+ ShowError("packet_db: packet len error\n");
+ continue;
+ }
+ k = atoi(str[1]);
+ packet_db[packet_ver][cmd].len = k;
+
+ if(str[2]==NULL){
+ packet_db[packet_ver][cmd].func = NULL;
+ ln++;
+ continue;
+ }
+ for(j=0;j<sizeof(clif_parse_func)/sizeof(clif_parse_func[0]);j++){
+ if(clif_parse_func[j].name != NULL &&
+ strcmp(str[2],clif_parse_func[j].name)==0)
+ {
+ if (packet_db[packet_ver][cmd].func != clif_parse_func[j].func)
+ { //If we are updating a function, we need to zero up the previous one. [Skotlex]
+ for(i=0;i<MAX_PACKET_DB;i++){
+ if (packet_db[packet_ver][i].func == clif_parse_func[j].func)
+ {
+ memset(&packet_db[packet_ver][i], 0, sizeof(struct packet_db));
+ break;
+ }
+ }
+ }
+ packet_db[packet_ver][cmd].func = clif_parse_func[j].func;
+ break;
+ }
+ }
+ // set the identifying cmd for the packet_db version
+ if (strcmp(str[2],"wanttoconnection")==0)
+ clif_config.connect_cmd[packet_ver] = cmd;
+
+ if(str[3]==NULL){
+ ShowError("packet_db: packet error\n");
+ exit(1);
+ }
+ for(j=0,p2=str[3];p2;j++){
+ str2[j]=p2;
+ p2=strchr(p2,':');
+ if(p2) *p2++=0;
+ k = atoi(str2[j]);
+ // if (packet_db[packet_ver][cmd].pos[j] != k && clif_config.prefer_packet_db) // not used for now
+ packet_db[packet_ver][cmd].pos[j] = k;
+ }
+
+ ln++;
+// if(packet_db[clif_config.packet_db_ver][cmd].len > 2 /* && packet_db[cmd].pos[0] == 0 */)
+// printf("packet_db ver %d: %d 0x%x %d %s %p\n",packet_ver,ln,cmd,packet_db[packet_ver][cmd].len,str[2],packet_db[packet_ver][cmd].func);
+ }
+ if (!clif_config.connect_cmd[clif_config.packet_db_ver])
+ { //Locate the nearest version that we still support. [Skotlex]
+ for(j = clif_config.packet_db_ver; j >= 0 && !clif_config.connect_cmd[j]; j--);
+
+ clif_config.packet_db_ver = j?j:MAX_PACKET_VER;
+ }
+ ShowStatus("Done reading packet database from '"CL_WHITE"%s"CL_RESET"'. Using default packet version: "CL_WHITE"%d"CL_RESET".\n", "packet_db.txt", clif_config.packet_db_ver);
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int do_init_clif(void) {
+#ifndef __WIN32
+ int i;
+#endif
+
+ clif_config.packet_db_ver = -1; // the main packet version of the DB
+ memset(clif_config.connect_cmd, 0, sizeof(clif_config.connect_cmd)); //The default connect command will be determined after reading the packet_db [Skotlex]
+
+ memset(packet_db,0,sizeof(packet_db));
+ //Using the packet_db file is the only way to set up packets now [Skotlex]
+ packetdb_readdb();
+
+ set_defaultparse(clif_parse);
+#ifdef __WIN32
+ //if (!make_listen_port(map_port)) {
+ if (!make_listen_bind(bind_ip,map_port)) {
+ ShowFatalError("cant bind game port\n");
+ exit(1);
+ }
+#else
+ for(i = 0; i < 10; i++) {
+ //if (make_listen_port(map_port))
+ if (make_listen_bind(bind_ip,map_port))
+ break;
+ sleep(20);
+ }
+ if (i == 10) {
+ ShowFatalError("cant bind game port\n");
+ exit(1);
+ }
+#endif
+
+ add_timer_func_list(clif_waitclose, "clif_waitclose");
+ add_timer_func_list(clif_clearchar_delay_sub, "clif_clearchar_delay_sub");
+ add_timer_func_list(clif_delayquit, "clif_delayquit");
+
+ return 0;
+}
+
diff --git a/src/map/clif.h b/src/map/clif.h
new file mode 100644
index 000000000..64bb102a2
--- /dev/null
+++ b/src/map/clif.h
@@ -0,0 +1,343 @@
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#ifndef _CLIF_H_
+#define _CLIF_H_
+
+#ifdef __WIN32
+typedef unsigned int in_addr_t;
+#else
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#endif
+
+#include "map.h"
+
+// protocol version
+#define PACKETVER 6
+
+// packet DB
+#define MAX_PACKET_DB 0x25f
+#define MAX_PACKET_VER 25
+
+struct packet_db {
+ short len;
+ void (*func)(int, struct map_session_data *);
+ short pos[20];
+};
+
+extern struct packet_db packet_db[MAX_PACKET_VER + 1][MAX_PACKET_DB];
+
+void clif_setip(char*);
+void clif_setbindip(char*);
+void clif_setport(int);
+
+in_addr_t clif_getip(void);
+int clif_getport(void);
+int clif_countusers(void);
+void clif_setwaitclose(int);
+
+int clif_authok(struct map_session_data *);
+int clif_authfail_fd(int,int);
+void clif_updatemaxid(int, int);
+int clif_charselectok(int);
+void check_fake_id(int fd, struct map_session_data *sd, int target_id);
+int clif_dropflooritem(struct flooritem_data *);
+int clif_clearflooritem(struct flooritem_data *,int);
+int clif_clearchar(struct block_list*,int); // area or fd
+int clif_clearchar_delay(unsigned int,struct block_list *,int);
+#define clif_clearchar_area(bl,type) clif_clearchar(bl,type)
+int clif_clearchar_id(int,int,int);
+int clif_spawnpc(struct map_session_data*); //area
+int clif_spawnnpc(struct npc_data*); // area
+int clif_spawnmob(struct mob_data*); // area
+int clif_spawnpet(struct pet_data*); // area
+int clif_walkok(struct map_session_data*); // self
+int clif_movechar(struct map_session_data*); // area
+int clif_movemob(struct mob_data*); //area
+int clif_movepet(struct pet_data *pd); //area
+int clif_movenpc(struct npc_data *nd); // [Valaris]
+int clif_changemap(struct map_session_data*,short,int,int); //self
+int clif_changemapserver(struct map_session_data*,char*,int,int,int,int); //self
+int clif_blown(struct block_list *); // area
+int clif_slide(struct block_list *,int,int); // area
+int clif_fixpos(struct block_list *); // area
+int clif_fixmobpos(struct mob_data *md);
+int clif_fixpcpos(struct map_session_data *sd);
+int clif_fixpetpos(struct pet_data *pd);
+int clif_fixnpcpos(struct npc_data *nd); // [Valaris]
+int clif_npcbuysell(struct map_session_data*,int); //self
+int clif_buylist(struct map_session_data*,struct npc_data*); //self
+int clif_selllist(struct map_session_data*); //self
+int clif_scriptmes(struct map_session_data*,int,char*); //self
+int clif_scriptnext(struct map_session_data*,int); //self
+int clif_scriptclose(struct map_session_data*,int); //self
+int clif_scriptmenu(struct map_session_data*,int,char*); //self
+int clif_scriptinput(struct map_session_data*,int); //self
+int clif_scriptinputstr(struct map_session_data *sd,int npcid); // self
+int clif_cutin(struct map_session_data*,char*,int); //self
+int clif_viewpoint(struct map_session_data*,int,int,int,int,int,int); //self
+int clif_additem(struct map_session_data*,int,int,int); //self
+int clif_delitem(struct map_session_data*,int,int); //self
+int clif_updatestatus(struct map_session_data*,int); //self
+int clif_changestatus(struct block_list*,int,int); //area
+int clif_damage(struct block_list *,struct block_list *,unsigned int,int,int,int,int,int,int); // area
+#define clif_takeitem(src,dst) clif_damage(src,dst,0,0,0,0,0,1,0)
+int clif_changelook(struct block_list *,int,int); // area
+int clif_arrowequip(struct map_session_data *sd,int val); //self
+int clif_arrow_fail(struct map_session_data *sd,int type); //self
+int clif_arrow_create_list(struct map_session_data *sd); //self
+int clif_statusupack(struct map_session_data *,int,int,int); // self
+int clif_equipitemack(struct map_session_data *,int,int,int); // self
+int clif_unequipitemack(struct map_session_data *,int,int,int); // self
+int clif_misceffect(struct block_list*,int); // area
+int clif_misceffect2(struct block_list *bl,int type);
+int clif_changeoption(struct block_list*); // area
+int clif_useitemack(struct map_session_data*,int,int,int); // self
+void clif_GlobalMessage(struct block_list *bl,char *message);
+int clif_createchat(struct map_session_data*,int); // self
+int clif_dispchat(struct chat_data*,int); // area or fd
+int clif_joinchatfail(struct map_session_data*,int); // self
+int clif_joinchatok(struct map_session_data*,struct chat_data*); // self
+int clif_addchat(struct chat_data*,struct map_session_data*); // chat
+int clif_changechatowner(struct chat_data*,struct map_session_data*); // chat
+int clif_clearchat(struct chat_data*,int); // area or fd
+int clif_leavechat(struct chat_data*,struct map_session_data*); // chat
+int clif_changechatstatus(struct chat_data*); // chat
+int clif_refresh(struct map_session_data*); // self
+
+int clif_fame_blacksmith(struct map_session_data *, int);
+int clif_fame_alchemist(struct map_session_data *, int);
+int clif_fame_taekwon(struct map_session_data *, int);
+
+void clif_emotion(struct block_list *bl,int type);
+void clif_talkiebox(struct block_list *bl,char* talkie);
+void clif_wedding_effect(struct block_list *bl);
+void clif_divorced(struct map_session_data *sd, char *);
+//void clif_sitting(int fd, struct map_session_data *sd);
+//void clif_callpartner(struct map_session_data *sd);
+void clif_adopt_process(struct map_session_data *sd);
+void clif_sitting(struct map_session_data *sd);
+void clif_soundeffect(struct map_session_data *sd,struct block_list *bl,char *name,int type);
+int clif_soundeffectall(struct block_list *bl, char *name, int type);
+
+// trade
+int clif_traderequest(struct map_session_data *sd,char *name);
+int clif_tradestart(struct map_session_data *sd,int type);
+int clif_tradeadditem(struct map_session_data *sd,struct map_session_data *tsd,int index,int amount);
+int clif_tradeitemok(struct map_session_data *sd,int index,int fail);
+int clif_tradedeal_lock(struct map_session_data *sd,int fail);
+int clif_tradecancelled(struct map_session_data *sd);
+int clif_tradecompleted(struct map_session_data *sd,int fail);
+
+// storage
+#include "storage.h"
+int clif_storageitemlist(struct map_session_data *sd,struct storage *stor);
+int clif_storageequiplist(struct map_session_data *sd,struct storage *stor);
+int clif_updatestorageamount(struct map_session_data *sd,struct storage *stor);
+int clif_storageitemadded(struct map_session_data *sd,struct storage *stor,int index,int amount);
+int clif_storageitemremoved(struct map_session_data *sd,int index,int amount);
+int clif_storageclose(struct map_session_data *sd);
+int clif_guildstorageitemlist(struct map_session_data *sd,struct guild_storage *stor);
+int clif_guildstorageequiplist(struct map_session_data *sd,struct guild_storage *stor);
+int clif_updateguildstorageamount(struct map_session_data *sd,struct guild_storage *stor);
+int clif_guildstorageitemadded(struct map_session_data *sd,struct guild_storage *stor,int index,int amount);
+
+int clif_pcinsight(struct block_list *,va_list); // map_forallinmovearea callback
+int clif_pcoutsight(struct block_list *,va_list); // map_forallinmovearea callback
+int clif_mobinsight(struct block_list *,va_list); // map_forallinmovearea callback
+int clif_moboutsight(struct block_list *,va_list); // map_forallinmovearea callback
+int clif_petoutsight(struct block_list *bl,va_list ap);
+int clif_petinsight(struct block_list *bl,va_list ap);
+int clif_npcoutsight(struct block_list *bl,va_list ap);
+int clif_npcinsight(struct block_list *bl,va_list ap);
+
+int clif_class_change(struct block_list *bl,int class_,int type);
+int clif_mob_class_change(struct mob_data *md,int class_);
+int clif_mob_equip(struct mob_data *md,int nameid); // [Valaris]
+
+int clif_skillinfo(struct map_session_data *sd,int skillid,int type,int range);
+int clif_skillinfoblock(struct map_session_data *sd);
+int clif_skillup(struct map_session_data *sd,int skill_num);
+
+int clif_skillcasting(struct block_list* bl,
+ int src_id,int dst_id,int dst_x,int dst_y,int skill_num,int casttime);
+int clif_skillcastcancel(struct block_list* bl);
+int clif_skill_fail(struct map_session_data *sd,int skill_id,int type,int btype);
+int clif_skill_damage(struct block_list *src,struct block_list *dst,
+ unsigned int tick,int sdelay,int ddelay,int damage,int div,
+ int skill_id,int skill_lv,int type);
+int clif_skill_damage2(struct block_list *src,struct block_list *dst,
+ unsigned int tick,int sdelay,int ddelay,int damage,int div,
+ int skill_id,int skill_lv,int type);
+int clif_skill_nodamage(struct block_list *src,struct block_list *dst,
+ int skill_id,int heal,int fail);
+int clif_skill_poseffect(struct block_list *src,int skill_id,
+ int val,int x,int y,int tick);
+int clif_skill_estimation(struct map_session_data *sd,struct block_list *dst);
+int clif_skill_warppoint(struct map_session_data *sd,int skill_num,
+ const char *map1,const char *map2,const char *map3,const char *map4);
+int clif_skill_memo(struct map_session_data *sd,int flag);
+int clif_skill_teleportmessage(struct map_session_data *sd,int flag);
+int clif_skill_produce_mix_list(struct map_session_data *sd,int trigger);
+
+int clif_produceeffect(struct map_session_data *sd,int flag,int nameid);
+
+int clif_skill_setunit(struct skill_unit *unit);
+int clif_skill_delunit(struct skill_unit *unit);
+
+int clif_01ac(struct block_list *bl);
+
+int clif_autospell(struct map_session_data *sd,int skilllv);
+int clif_devotion(struct map_session_data *sd);
+int clif_marionette(struct block_list *src, struct block_list *target);
+int clif_spiritball(struct map_session_data *sd);
+int clif_combo_delay(struct block_list *src,int wait);
+int clif_bladestop(struct block_list *src,struct block_list *dst,int bool_);
+int clif_changemapcell(int m,int x,int y,int cell_type,int type);
+
+int clif_status_load(struct block_list *bl,int type, int flag);
+int clif_status_change(struct block_list *bl,int type,int flag);
+
+int clif_wis_message(int fd,char *nick,char *mes,int mes_len);
+int clif_wis_end(int fd,int flag);
+
+int clif_solved_charname(struct map_session_data *sd,int char_id);
+int clif_charnameack(int fd, struct block_list *bl);
+int clif_charnameupdate(struct map_session_data *ssd);
+
+int clif_use_card(struct map_session_data *sd,int idx);
+int clif_insert_card(struct map_session_data *sd,int idx_equip,int idx_card,int flag);
+
+int clif_itemlist(struct map_session_data *sd);
+int clif_equiplist(struct map_session_data *sd);
+
+int clif_cart_additem(struct map_session_data*,int,int,int);
+int clif_cart_delitem(struct map_session_data*,int,int);
+int clif_cart_itemlist(struct map_session_data *sd);
+int clif_cart_equiplist(struct map_session_data *sd);
+
+int clif_item_identify_list(struct map_session_data *sd);
+int clif_item_identified(struct map_session_data *sd,int idx,int flag);
+int clif_item_repair_list (struct map_session_data *sd, struct map_session_data *dstsd);
+int clif_item_repaireffect(struct map_session_data *sd, int nameid, int flag);
+int clif_item_refine_list(struct map_session_data *sd);
+
+int clif_item_skill(struct map_session_data *sd,int skillid,int skilllv,const char *name);
+
+int clif_mvp_effect(struct map_session_data *sd);
+int clif_mvp_item(struct map_session_data *sd,int nameid);
+int clif_mvp_exp(struct map_session_data *sd,int exp);
+void clif_changed_dir(struct block_list *bl);
+
+// vending
+int clif_openvendingreq(struct map_session_data *sd,int num);
+int clif_showvendingboard(struct block_list* bl,char *message,int fd);
+int clif_closevendingboard(struct block_list* bl,int fd);
+int clif_vendinglist(struct map_session_data *sd,int id,struct vending *vending);
+int clif_buyvending(struct map_session_data *sd,int index,int amount,int fail);
+int clif_openvending(struct map_session_data *sd,int id,struct vending *vending);
+int clif_vendingreport(struct map_session_data *sd,int index,int amount);
+
+int clif_movetoattack(struct map_session_data *sd,struct block_list *bl);
+
+// party
+int clif_party_created(struct map_session_data *sd,int flag);
+int clif_party_main_info(struct party *p, int fd);
+int clif_party_info(struct party *p,int fd);
+int clif_party_invite(struct map_session_data *sd,struct map_session_data *tsd);
+int clif_party_inviteack(struct map_session_data *sd,char *nick,int flag);
+int clif_party_option(struct party *p,struct map_session_data *sd,int flag);
+int clif_party_leaved(struct party *p,struct map_session_data *sd,int account_id,char *name,int flag);
+int clif_party_message(struct party *p,int account_id,char *mes,int len);
+int clif_party_move(struct party *p,struct map_session_data *sd,int online);
+int clif_party_xy(struct map_session_data *sd);
+int clif_party_hp(struct map_session_data *sd);
+int clif_hpmeter(struct map_session_data *sd);
+
+// guild
+int clif_guild_created(struct map_session_data *sd,int flag);
+int clif_guild_belonginfo(struct map_session_data *sd,struct guild *g);
+int clif_guild_basicinfo(struct map_session_data *sd);
+int clif_guild_allianceinfo(struct map_session_data *sd);
+int clif_guild_memberlist(struct map_session_data *sd);
+int clif_guild_skillinfo(struct map_session_data *sd);
+int clif_guild_memberlogin_notice(struct guild *g,int idx,int flag);
+int clif_guild_invite(struct map_session_data *sd,struct guild *g);
+int clif_guild_inviteack(struct map_session_data *sd,int flag);
+int clif_guild_leave(struct map_session_data *sd,const char *name,const char *mes);
+int clif_guild_explusion(struct map_session_data *sd,const char *name,const char *mes,
+ int account_id);
+int clif_guild_positionchanged(struct guild *g,int idx);
+int clif_guild_memberpositionchanged(struct guild *g,int idx);
+int clif_guild_emblem(struct map_session_data *sd,struct guild *g);
+int clif_guild_notice(struct map_session_data *sd,struct guild *g);
+int clif_guild_message(struct guild *g,int account_id,const char *mes,int len);
+int clif_guild_skillup(struct map_session_data *sd,int skill_num,int lv);
+int clif_guild_reqalliance(struct map_session_data *sd,int account_id,const char *name);
+int clif_guild_allianceack(struct map_session_data *sd,int flag);
+int clif_guild_delalliance(struct map_session_data *sd,int guild_id,int flag);
+int clif_guild_oppositionack(struct map_session_data *sd,int flag);
+int clif_guild_broken(struct map_session_data *sd,int flag);
+int clif_guild_xy(struct map_session_data *sd);
+int clif_guild_xy_remove(struct map_session_data *sd);
+
+
+// atcommand
+int clif_displaymessage(const int fd,char* mes);
+int clif_disp_onlyself(struct map_session_data *sd,char *mes,int len);
+int clif_GMmessage(struct block_list *bl,char* mes,int len,int flag);
+void clif_MainChatMessage(char* message); //luzza
+int clif_announce(struct block_list *bl, char* mes, int len, unsigned long color, int flag);
+int clif_heal(int fd,int type,int val);
+int clif_resurrection(struct block_list *bl,int type);
+int clif_set0199(int fd,int type);
+int clif_pvpset(struct map_session_data *sd, int pvprank, int pvpnum,int type);
+int clif_send0199(int map,int type);
+int clif_refine(int fd,struct map_session_data *sd,int fail,int index,int val);
+
+//petsystem
+int clif_catch_process(struct map_session_data *sd);
+int clif_pet_rulet(struct map_session_data *sd,int data);
+int clif_sendegg(struct map_session_data *sd);
+int clif_send_petdata(struct map_session_data *sd,int type,int param);
+int clif_send_petstatus(struct map_session_data *sd);
+int clif_pet_emotion(struct pet_data *pd,int param);
+int clif_pet_performance(struct block_list *bl,int param);
+int clif_pet_equip(struct pet_data *pd,int nameid);
+int clif_pet_food(struct map_session_data *sd,int foodid,int fail);
+
+//friends list
+int clif_friendslist_toggle_sub(struct map_session_data *sd,va_list ap);
+void clif_friendslist_send(struct map_session_data *sd);
+void clif_friendslist_reqack(struct map_session_data *sd, struct map_session_data *f_sd, int type);
+
+// [Valaris]
+int clif_mob_hp(struct mob_data *md);
+int clif_weather_sub(int fd, int type); // [Valaris]
+int clif_weather(int m); // [Valaris]
+int clif_specialeffect(struct block_list *bl,int type, int flag); // special effects [Valaris]
+int clif_message(struct block_list *bl, char* msg); // messages (from mobs/npcs) [Valaris]
+
+int clif_GM_kickack(struct map_session_data *sd,int id);
+int clif_GM_kick(struct map_session_data *sd,struct map_session_data *tsd,int type);
+int clif_GM_silence(struct map_session_data *sd,struct map_session_data *tsd,int type);
+int clif_timedout(struct map_session_data *sd);
+
+int clif_foreachclient(int (*)(struct map_session_data*,va_list),...);
+int clif_disp_overhead(struct map_session_data *sd, char* mes);
+
+int do_final_clif(void);
+int do_init_clif(void);
+
+
+int clif_party_xy_remove(struct map_session_data *sd); //Fix for minimap [Kevin]
+void clif_parse_ReqFeel(int fd, struct map_session_data *sd);
+void clif_feel_info(struct map_session_data *sd, int feel_level);
+void clif_hate_mob(struct map_session_data *sd, int skilllv,int mob_id);
+void clif_mission_mob(struct map_session_data *sd, unsigned short mob_id, unsigned short progress);
+#endif
+
+
diff --git a/src/map/date.c b/src/map/date.c
new file mode 100644
index 000000000..3bb7dca66
--- /dev/null
+++ b/src/map/date.c
@@ -0,0 +1,72 @@
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#include "date.h"
+#include <time.h>
+
+int date_get_year(void)
+{
+ time_t t;
+ struct tm * lt;
+ t = time(NULL);
+ lt = localtime(&t);
+ return lt->tm_year+1900;
+}
+int date_get_month(void)
+{
+ time_t t;
+ struct tm * lt;
+ t = time(NULL);
+ lt = localtime(&t);
+ return lt->tm_mon+1;
+}
+int date_get_day(void)
+{
+ time_t t;
+ struct tm * lt;
+ t = time(NULL);
+ lt = localtime(&t);
+ return lt->tm_mday;
+}
+int date_get_hour(void)
+{
+ time_t t;
+ struct tm * lt;
+ t = time(NULL);
+ lt = localtime(&t);
+ return lt->tm_hour;
+}
+
+int date_get_min(void)
+{
+ time_t t;
+ struct tm * lt;
+ t = time(NULL);
+ lt = localtime(&t);
+ return lt->tm_min;
+}
+
+int date_get_sec(void)
+{
+ time_t t;
+ struct tm * lt;
+ t = time(NULL);
+ lt = localtime(&t);
+ return lt->tm_sec;
+}
+
+int is_day_of_sun(void)
+{
+ return date_get_day()%2 == 0;
+}
+
+int is_day_of_moon(void)
+{
+ return date_get_day()%2 == 1;
+}
+
+int is_day_of_star(void)
+{
+ return date_get_day()%5 == 0;
+}
+
diff --git a/src/map/date.h b/src/map/date.h
new file mode 100644
index 000000000..2dfbf58dd
--- /dev/null
+++ b/src/map/date.h
@@ -0,0 +1,17 @@
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#ifndef _DATE_H_
+#define _DATE_H_
+#endif
+
+int date_get_year(void);
+int date_get_month(void);
+int date_get_day(void);
+int date_get_hour(void);
+int date_get_min(void);
+int date_get_sec(void);
+
+int is_day_of_sun(void);
+int is_day_of_moon(void);
+int is_day_of_star(void);
diff --git a/src/map/guild.c b/src/map/guild.c
new file mode 100644
index 000000000..9a12b0425
--- /dev/null
+++ b/src/map/guild.c
@@ -0,0 +1,1976 @@
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "map.h"
+#include "guild.h"
+#include "storage.h"
+#include "timer.h"
+#include "socket.h"
+#include "nullpo.h"
+#include "malloc.h"
+#include "battle.h"
+#include "npc.h"
+#include "pc.h"
+#include "status.h"
+#include "mob.h"
+#include "intif.h"
+#include "clif.h"
+#include "skill.h"
+#include "showmsg.h"
+#include "log.h"
+
+static struct guild* guild_cache; //For fast retrieval of the same guild over and over. [Skotlex]
+static struct dbt *guild_db;
+static struct dbt *castle_db;
+static struct dbt *guild_expcache_db;
+static struct dbt *guild_infoevent_db;
+static struct dbt *guild_castleinfoevent_db;
+
+struct eventlist {
+ char name[50];
+ struct eventlist *next;
+};
+
+// ギルドのEXPキャッシュのフラッシュに関連する定数
+#define GUILD_SEND_XY_INVERVAL 5000 // 座標やHP送信の間隔
+#define GUILD_PAYEXP_INVERVAL 10000 // 間隔(キャッシュの最大生存時間、ミリ秒)
+#define GUILD_PAYEXP_LIST 8192 // キャッシュの最大数
+
+// ギルドのEXPキャッシュ
+struct guild_expcache {
+ int guild_id, account_id, char_id, exp;
+};
+
+struct{
+ int id;
+ int max;
+ struct{
+ short id;
+ short lv;
+ }need[6];
+} guild_skill_tree[MAX_GUILDSKILL];
+
+// timer for auto saving guild data during WoE
+#define GUILD_SAVE_INTERVAL 300000
+int guild_save_timer = -1;
+
+int guild_payexp_timer(int tid,unsigned int tick,int id,int data);
+int guild_gvg_eliminate_timer(int tid,unsigned int tick,int id,int data);
+int guild_save_sub(int tid,unsigned int tick,int id,int data);
+static int guild_send_xy_timer(int tid,unsigned int tick,int id,int data);
+
+// ギルドスキルdbのアクセサ(今は直打ちで代用)
+// Modified for new skills [Sara]
+int guild_skill_get_inf(int id)
+{
+ switch(id) {
+ case GD_BATTLEORDER:
+ case GD_REGENERATION:
+ case GD_RESTORE:
+ case GD_EMERGENCYCALL:
+ return 4;
+ }
+ return 0;
+}
+
+ // Modified [Komurka]
+int guild_skill_get_max (int id)
+{
+ if (id < GD_SKILLBASE || id > GD_SKILLBASE+MAX_GUILDSKILL)
+ return 0;
+ return guild_skill_tree[id-GD_SKILLBASE].max;
+}
+
+// ギルドスキルがあるか確認
+int guild_checkskill(struct guild *g,int id)
+{
+ int idx = id-GD_SKILLBASE;
+ if (idx < 0 || idx >= MAX_GUILDSKILL)
+ return 0;
+ return g->skill[idx].lv;
+}
+
+/*==========================================
+ * guild_skill_tree.txt reading - from jA [Komurka]
+ *------------------------------------------
+ */
+int guild_read_guildskill_tree_db(void)
+{
+ int i,k,id=0,ln=0;
+ FILE *fp;
+ char line[1024],*p;
+
+ memset(guild_skill_tree,0,sizeof(guild_skill_tree));
+ sprintf(line, "%s/guild_skill_tree.txt", db_path);
+ if( (fp=fopen(line,"r"))==NULL){
+ ShowError("can't read %s\n", line);
+ return -1;
+ }
+ while(fgets(line,1020,fp)){
+ char *split[50];
+ if(line[0]=='/' && line[1]=='/')
+ continue;
+ for(i=0,p=line;i<12 && p;i++){
+ split[i]=p;
+ p=strchr(p,',');
+ if(p) *p++=0;
+ }
+ if(i<12)
+ continue;
+ id = atoi(split[0]) - GD_SKILLBASE;
+ if(id<0 || id>=MAX_GUILDSKILL)
+ continue;
+ guild_skill_tree[id].id=atoi(split[0]);
+ guild_skill_tree[id].max=atoi(split[1]);
+ if (guild_skill_tree[id].id==GD_GLORYGUILD && battle_config.require_glory_guild && guild_skill_tree[id].max==0) guild_skill_tree[id].max=1;
+ for(k=0;k<5;k++){
+ guild_skill_tree[id].need[k].id=atoi(split[k*2+2]);
+ guild_skill_tree[id].need[k].lv=atoi(split[k*2+3]);
+ }
+ ln++;
+ }
+ fclose(fp);
+ ShowStatus("Done reading '"CL_WHITE"%d"CL_RESET"' entries in '"CL_WHITE"%s"CL_RESET"'.\n",ln,"guild_skill_tree.txt");
+
+ return 0;
+}
+
+/*==========================================
+ * Guild skill check - from jA [Komurka]
+ *------------------------------------------
+ */
+int guild_check_skill_require(struct guild *g,int id)
+{
+ int i;
+ int idx = id-GD_SKILLBASE;
+
+ if(g == NULL)
+ return 0;
+
+ if (idx < 0 || idx >= MAX_GUILDSKILL)
+ return 0;
+
+ for(i=0;i<5;i++)
+ {
+ if(guild_skill_tree[idx].need[i].id == 0) break;
+ if(guild_skill_tree[idx].need[i].lv > guild_checkskill(g,guild_skill_tree[idx].need[i].id))
+ return 0;
+ }
+ return 1;
+}
+
+static int guild_read_castledb(void)
+{
+ FILE *fp;
+ char line[1024];
+ int j,ln=0;
+ char *str[32],*p;
+ struct guild_castle *gc;
+
+ sprintf(line, "%s/castle_db.txt", db_path);
+ if( (fp=fopen(line,"r"))==NULL){
+ ShowError("can't read %s\n", line);
+ return -1;
+ }
+
+ while(fgets(line,1020,fp)){
+ if(line[0]=='/' && line[1]=='/')
+ continue;
+ memset(str,0,sizeof(str));
+ for(j=0,p=line;j<6 && p;j++){
+ str[j]=p;
+ p=strchr(p,',');
+ if(p) *p++=0;
+ }
+ if (j < 4) //Insufficient data for castle. [Skotlex]
+ {
+ ShowError("castle_db.txt: invalid line '%s'\n", line);
+ continue;
+ }
+
+ gc=(struct guild_castle *)aCalloc(1,sizeof(struct guild_castle));
+ gc->castle_id=atoi(str[0]);
+ memcpy(gc->map_name,str[1],MAP_NAME_LENGTH-1);
+ memcpy(gc->castle_name,str[2],NAME_LENGTH-1);
+ memcpy(gc->castle_event,str[3],NAME_LENGTH-1);
+
+ idb_put(castle_db,gc->castle_id,gc);
+
+ //intif_guild_castle_info(gc->castle_id);
+
+ ln++;
+ }
+ fclose(fp);
+ ShowStatus("Done reading '"CL_WHITE"%d"CL_RESET"' entries in '"CL_WHITE"%s"CL_RESET"'.\n",ln,"castle_db.txt");
+ return 0;
+}
+
+// 初期化
+void do_init_guild(void)
+{
+ guild_db=db_alloc(__FILE__,__LINE__,DB_INT,DB_OPT_RELEASE_DATA,sizeof(int));
+ castle_db=db_alloc(__FILE__,__LINE__,DB_INT,DB_OPT_RELEASE_DATA,sizeof(int));
+ guild_expcache_db=db_alloc(__FILE__,__LINE__,DB_INT,DB_OPT_RELEASE_DATA,sizeof(int));
+ guild_infoevent_db=db_alloc(__FILE__,__LINE__,DB_INT,DB_OPT_BASE,sizeof(int));
+ guild_castleinfoevent_db=db_alloc(__FILE__,__LINE__,DB_INT,DB_OPT_BASE,sizeof(int));
+
+ guild_read_castledb();
+
+ guild_read_guildskill_tree_db(); //guild skill tree [Komurka]
+
+ add_timer_func_list(guild_gvg_eliminate_timer,"guild_gvg_eliminate_timer");
+ add_timer_func_list(guild_payexp_timer,"guild_payexp_timer");
+ add_timer_func_list(guild_save_sub, "guild_save_sub");
+ add_timer_func_list(guild_send_xy_timer, "guild_send_xy_timer");
+ add_timer_interval(gettick()+GUILD_PAYEXP_INVERVAL,guild_payexp_timer,0,0,GUILD_PAYEXP_INVERVAL);
+ add_timer_interval(gettick()+GUILD_SEND_XY_INVERVAL,guild_send_xy_timer,0,0,GUILD_SEND_XY_INVERVAL);
+}
+
+
+// 検索
+struct guild *guild_search(int guild_id)
+{
+ if(guild_cache && guild_cache->guild_id == guild_id)
+ return guild_cache;
+ guild_cache = idb_get(guild_db,guild_id);
+ return guild_cache;
+}
+int guild_searchname_sub(DBKey key,void *data,va_list ap)
+{
+ struct guild *g=(struct guild *)data,**dst;
+ char *str;
+ str=va_arg(ap,char *);
+ dst=va_arg(ap,struct guild **);
+ if(strcmpi(g->name,str)==0)
+ *dst=g;
+ return 0;
+}
+// ギルド名検索
+struct guild* guild_searchname(char *str)
+{
+ struct guild *g=NULL;
+ guild_db->foreach(guild_db,guild_searchname_sub,str,&g);
+ return g;
+}
+struct guild_castle *guild_castle_search(int gcid)
+{
+ return idb_get(castle_db,gcid);
+}
+
+// mapnameに対応したアジトのgcを返す
+struct guild_castle *guild_mapname2gc(char *mapname)
+{
+ int i;
+ struct guild_castle *gc=NULL;
+ for(i=0;i<MAX_GUILDCASTLE;i++){
+ gc=guild_castle_search(i);
+ if(!gc) continue;
+ if(strcmp(gc->map_name,mapname)==0) return gc;
+ }
+ return NULL;
+}
+
+struct guild_castle *guild_mapindex2gc(short mapname)
+{
+ int i;
+ struct guild_castle *gc=NULL;
+ for(i=0;i<MAX_GUILDCASTLE;i++){
+ gc=guild_castle_search(i);
+ if(!gc) continue;
+ if(strcmp(gc->map_name,mapindex_id2name(mapname))==0) return gc;
+ }
+ return NULL;
+}
+
+
+
+// ログイン中のギルドメンバーの1人のsdを返す
+struct map_session_data *guild_getavailablesd(struct guild *g)
+{
+ int i;
+
+ nullpo_retr(NULL, g);
+
+ for(i=0;i<g->max_member;i++)
+ if(g->member[i].sd!=NULL)
+ return g->member[i].sd;
+ return NULL;
+}
+
+// ギルドメンバーのインデックスを返す
+int guild_getindex(struct guild *g,int account_id,int char_id)
+{
+ int i;
+ if(g==NULL)
+ return -1;
+ for(i=0;i<g->max_member;i++)
+ if( g->member[i].account_id==account_id &&
+ g->member[i].char_id==char_id )
+ return i;
+ return -1;
+}
+// ギルドメンバーの役職を返す
+int guild_getposition(struct map_session_data *sd,struct guild *g)
+{
+ int i;
+
+ nullpo_retr(-1, sd);
+
+ if(g==NULL && (g=guild_search(sd->status.guild_id))==NULL)
+ return -1;
+ for(i=0;i<g->max_member;i++)
+ if( g->member[i].account_id==sd->status.account_id &&
+ g->member[i].char_id==sd->status.char_id )
+ return g->member[i].position;
+ return -1;
+}
+
+// メンバー情報の作成
+void guild_makemember(struct guild_member *m,struct map_session_data *sd)
+{
+ nullpo_retv(sd);
+
+ memset(m,0,sizeof(struct guild_member));
+ m->account_id =sd->status.account_id;
+ m->char_id =sd->status.char_id;
+ m->hair =sd->status.hair;
+ m->hair_color =sd->status.hair_color;
+ m->gender =sd->sex;
+ m->class_ =sd->status.class_;
+ m->lv =sd->status.base_level;
+// m->exp =0;
+// m->exp_payper =0;
+ m->online =1;
+ m->position =MAX_GUILDPOSITION-1;
+ memcpy(m->name,sd->status.name,NAME_LENGTH-1);
+ return;
+}
+// ギルド競合確認
+int guild_check_conflict(struct map_session_data *sd)
+{
+ nullpo_retr(0, sd);
+
+ intif_guild_checkconflict(sd->status.guild_id,
+ sd->status.account_id,sd->status.char_id);
+ return 0;
+}
+
+// ギルドのEXPキャッシュをinter鯖にフラッシュする
+int guild_payexp_timer_sub(DBKey dataid, void *data, va_list ap)
+{
+ int i, *dellist, *delp;
+ struct guild_expcache *c;
+ struct guild *g;
+ double exp2;
+
+ c = (struct guild_expcache *)data;
+ dellist = va_arg(ap,int *);
+ delp = va_arg(ap,int *);
+
+ if (*delp >= GUILD_PAYEXP_LIST ||
+ (g = guild_search(c->guild_id)) == NULL ||
+ (i = guild_getindex(g, c->account_id, c->char_id)) < 0)
+ return 0;
+
+ // It is *already* fixed... this would be more appropriate ^^; [celest]
+ exp2 = g->member[i].exp + c->exp;
+ g->member[i].exp = (exp2 > 0x7FFFFFFF) ? 0x7FFFFFFF : (int)exp2;
+
+ //fixed a bug in exp overflow [Kevin]
+ //g->member[i].exp += c->exp;
+ //if(g->member[i].exp < 0)
+ //g->member[i].exp = 0x7FFFFFFF;
+
+ intif_guild_change_memberinfo(g->guild_id,c->account_id,c->char_id,
+ GMI_EXP,&g->member[i].exp,sizeof(g->member[i].exp));
+ c->exp=0;
+
+ dellist[(*delp)++]=dataid.i;
+ return 0;
+}
+
+int guild_payexp_timer(int tid, unsigned int tick, int id, int data)
+{
+ int dellist[GUILD_PAYEXP_LIST], delp = 0, i;
+ guild_expcache_db->foreach(guild_expcache_db, guild_payexp_timer_sub, dellist, &delp);
+ for (i = 0; i < delp; i++)
+ idb_remove(guild_expcache_db, dellist[i]);
+ return 0;
+}
+
+//Taken from party_send_xy_timer_sub. [Skotlex]
+int guild_send_xy_timer_sub(DBKey key,void *data,va_list ap)
+{
+ struct guild *g=(struct guild *)data;
+ int i;
+
+ nullpo_retr(0, g);
+
+ for(i=0;i<g->max_member;i++){
+ struct map_session_data *sd;
+ if((sd=g->member[i].sd)!=NULL){
+ if(sd->guild_x!=sd->bl.x || sd->guild_y!=sd->bl.y){
+ clif_guild_xy(sd);
+ sd->guild_x=sd->bl.x;
+ sd->guild_y=sd->bl.y;
+ }
+ }
+ }
+ return 0;
+}
+
+//Code from party_send_xy_timer [Skotlex]
+static int guild_send_xy_timer(int tid,unsigned int tick,int id,int data)
+{
+ guild_db->foreach(guild_db,guild_send_xy_timer_sub,tick);
+ return 0;
+}
+
+int guild_send_dot_remove(struct map_session_data *sd)
+{
+ if (sd->status.guild_id)
+ clif_guild_xy_remove(sd);
+ return 0;
+}
+//------------------------------------------------------------------------
+
+// 作成要求
+int guild_create(struct map_session_data *sd,char *name)
+{
+ nullpo_retr(0, sd);
+
+ if(sd->status.guild_id==0){
+ if(!battle_config.guild_emperium_check || pc_search_inventory(sd,714) >= 0) {
+ struct guild_member m;
+ guild_makemember(&m,sd);
+ m.position=0;
+ intif_guild_create(name,&m);
+ } else
+ clif_guild_created(sd,3); // エンペリウムがいない
+ }else
+ clif_guild_created(sd,1); // すでに所属している
+
+ return 0;
+}
+
+// 作成可否
+int guild_created(int account_id,int guild_id)
+{
+ struct map_session_data *sd=map_id2sd(account_id);
+
+ if(sd==NULL)
+ return 0;
+ if(guild_id>0) {
+ //struct guild *g;
+ sd->status.guild_id=guild_id;
+ sd->state.guild_sent=0;
+ clif_guild_created(sd,0);
+ if(battle_config.guild_emperium_check)
+ pc_delitem(sd,pc_search_inventory(sd,714),1,0); // エンペリウム消耗
+ } else {
+ clif_guild_created(sd,2); // 作成失敗(同名ギルド存在)
+ }
+ return 0;
+}
+
+// 情報要求
+int guild_request_info(int guild_id)
+{
+// if(battle_config.etc_log)
+// printf("guild_request_info\n");
+ return intif_guild_request_info(guild_id);
+}
+// イベント付き情報要求
+int guild_npc_request_info(int guild_id,const char *event)
+{
+ struct eventlist *ev;
+
+ if( guild_search(guild_id) ){
+ if(event && *event)
+ npc_event_do(event);
+ return 0;
+ }
+
+ if(event==NULL || *event==0)
+ return guild_request_info(guild_id);
+
+ ev=(struct eventlist *)aCalloc(1,sizeof(struct eventlist));
+ memcpy(ev->name,event,strlen(event));
+ //The one in the db becomes the next event from this.
+ ev->next=idb_put(guild_infoevent_db,guild_id,ev);
+ return guild_request_info(guild_id);
+}
+
+// 所属キャラの確認
+int guild_check_member(const struct guild *g)
+{
+ int i, users;
+ struct map_session_data *sd, **all_sd;
+
+ nullpo_retr(0, g);
+
+ all_sd = map_getallusers(&users);
+
+ for(i=0;i<users;i++){
+ if((sd=all_sd[i])){
+ if(sd->status.guild_id==g->guild_id){
+ int j,f=1;
+ for(j=0;j<MAX_GUILD;j++){ // データがあるか
+ if( g->member[j].account_id==sd->status.account_id &&
+ g->member[j].char_id==sd->status.char_id)
+ f=0;
+ }
+ if(f){
+ sd->status.guild_id=0;
+ sd->state.guild_sent=0;
+ sd->guild_emblem_id=0;
+ if(battle_config.error_log)
+ ShowWarning("guild: check_member %d[%s] is not member\n",sd->status.account_id,sd->status.name);
+ }
+ }
+ }
+ }
+ return 0;
+}
+// 情報所得失敗(そのIDのキャラを全部未所属にする)
+int guild_recv_noinfo(int guild_id)
+{
+ int i, users;
+ struct map_session_data *sd, **all_sd;
+
+ all_sd = map_getallusers(&users);
+
+ for(i=0;i<users;i++){
+ if((sd=all_sd[i])){
+ if(sd->status.guild_id==guild_id)
+ sd->status.guild_id=0;
+ }
+ }
+ return 0;
+}
+// 情報所得
+int guild_recv_info(struct guild *sg)
+{
+ struct guild *g,before;
+ int i,bm,m;
+ struct eventlist *ev,*ev2;
+
+ nullpo_retr(0, sg);
+
+ if((g=idb_get(guild_db,sg->guild_id))==NULL){
+ struct map_session_data *sd;
+ g=(struct guild *)aCalloc(1,sizeof(struct guild));
+ idb_put(guild_db,sg->guild_id,g);
+ before=*sg;
+
+ // 最初のロードなのでユーザーのチェックを行う
+ guild_check_member(sg);
+ //If the guild master is online the first time the guild_info is received, that means he was the first to join,
+ //and as such, his guild skills should be blocked to avoid login/logout abuse [Skotlex]
+ if ((sd = map_nick2sd(sg->master)) != NULL)
+ {
+ guild_block_skill(sd, 300000);
+ //Also set the guild master flag.
+ sd->state.gmaster_flag = g;
+ clif_charnameupdate(sd); // [LuzZza]
+ }
+ }else
+ before=*g;
+ memcpy(g,sg,sizeof(struct guild));
+
+ for(i=bm=m=0;i<g->max_member;i++){ // sdの設定と人数の確認
+ if(g->member[i].account_id>0){
+ struct map_session_data *sd = map_id2sd(g->member[i].account_id);
+ if (sd && sd->status.char_id == g->member[i].char_id &&
+ sd->status.guild_id == g->guild_id &&
+ !sd->state.waitingdisconnect) {
+ g->member[i].sd = sd;
+ clif_charnameupdate(sd); // [LuzZza]
+ } else g->member[i].sd = NULL;
+ m++;
+ }else
+ g->member[i].sd=NULL;
+ if(before.member[i].account_id>0)
+ bm++;
+ }
+
+ for(i=0;i<g->max_member;i++){ // 情報の送信
+ struct map_session_data *sd = g->member[i].sd;
+ if( sd==NULL )
+ continue;
+
+ if( before.guild_lv!=g->guild_lv || bm!=m ||
+ before.max_member!=g->max_member ){
+ clif_guild_basicinfo(sd); // 基本情報送信
+ clif_guild_emblem(sd,g); // エンブレム送信
+ }
+
+ if(bm!=m){ // メンバー情報送信
+ clif_guild_memberlist(g->member[i].sd);
+ }
+
+ if( before.skill_point!=g->skill_point)
+ clif_guild_skillinfo(sd); // スキル情報送信
+
+ if( sd->state.guild_sent==0){ // 未送信なら所属情報も送る
+ clif_guild_belonginfo(sd,g);
+ clif_guild_notice(sd,g);
+ sd->guild_emblem_id=g->emblem_id;
+ sd->state.guild_sent=1;
+ }
+ }
+
+ // イベントの発生
+ if( (ev=idb_remove(guild_infoevent_db,sg->guild_id))!=NULL ){
+ while(ev){
+ npc_event_do(ev->name);
+ ev2=ev->next;
+ aFree(ev);
+ ev=ev2;
+ }
+ }
+
+ return 0;
+}
+
+
+// ギルドへの勧誘
+int guild_invite(struct map_session_data *sd,int account_id)
+{
+ struct map_session_data *tsd;
+ struct guild *g;
+ int i;
+
+ nullpo_retr(0, sd);
+
+ tsd= map_id2sd(account_id);
+ g=guild_search(sd->status.guild_id);
+
+ if(tsd==NULL || g==NULL)
+ return 0;
+ if(!battle_config.invite_request_check) {
+ if (tsd->party_invite>0 || tsd->trade_partner) { // 相手が取引中かどうか
+ clif_guild_inviteack(sd,0);
+ return 0;
+ }
+ }
+ if( tsd->status.guild_id>0 || tsd->guild_invite>0 ){ // 相手の所属確認
+ clif_guild_inviteack(sd,0);
+ return 0;
+ }
+
+ // 定員確認
+ for(i=0;i<g->max_member;i++)
+ if(g->member[i].account_id==0)
+ break;
+ if(i==g->max_member){
+ clif_guild_inviteack(sd,3);
+ return 0;
+ }
+
+ tsd->guild_invite=sd->status.guild_id;
+ tsd->guild_invite_account=sd->status.account_id;
+
+ clif_guild_invite(tsd,g);
+ return 0;
+}
+// ギルド勧誘への返答
+int guild_reply_invite(struct map_session_data *sd,int guild_id,int flag)
+{
+ struct map_session_data *tsd;
+
+ nullpo_retr(0, sd);
+
+ //nullpo_retr(0, tsd= map_id2sd( sd->guild_invite_account ));
+ //I checked the code, and there's no "check" for the case where the guy
+ //that invites another to a guild quits the map-server before being replied.
+ //Hence that's a valid null pointer scenario. :) [Skotlex]
+ if ((tsd= map_id2sd( sd->guild_invite_account )) == NULL)
+ { //Do we send a "invitation failed" msg or something to the player?
+ //Or should we accept the invitation and add it to the guild anyway?
+ //afterall, guild_invite holds the guild id that the player was invited to.
+ sd->guild_invite=0;
+ sd->guild_invite_account=0;
+ return 0;
+ }
+
+ if(sd->guild_invite!=guild_id) // 勧誘とギルドIDが違う
+ return 0;
+
+ if(flag==1){ // 承諾
+ struct guild_member m;
+ struct guild *g;
+ int i;
+
+ // 定員確認
+ if( (g=guild_search(tsd->status.guild_id))==NULL ){
+ sd->guild_invite=0;
+ sd->guild_invite_account=0;
+ return 0;
+ }
+ for(i=0;i<g->max_member;i++)
+ if(g->member[i].account_id==0)
+ break;
+ if(i==g->max_member){
+ sd->guild_invite=0;
+ sd->guild_invite_account=0;
+ clif_guild_inviteack(tsd,3);
+ return 0;
+ }
+
+
+ //inter鯖へ追加要求
+ guild_makemember(&m,sd);
+ intif_guild_addmember( sd->guild_invite, &m );
+ return 0;
+ }else{ // 拒否
+ sd->guild_invite=0;
+ sd->guild_invite_account=0;
+ clif_guild_inviteack(tsd,1);
+ }
+ return 0;
+}
+// ギルドメンバが追加された
+int guild_member_added(int guild_id,int account_id,int char_id,int flag)
+{
+ struct map_session_data *sd= map_id2sd(account_id),*sd2;
+ struct guild *g;
+
+ if( (g=guild_search(guild_id))==NULL )
+ return 0;
+
+ if(sd==NULL || sd->guild_invite==0){
+ // キャラ側に登録できなかったため脱退要求を出す
+ if (flag == 0) {
+ if(battle_config.error_log)
+ ShowError("guild: member added error %d is not online\n",account_id);
+ intif_guild_leave(guild_id,account_id,char_id,0,"**登録失敗**");
+ }
+ return 0;
+ }
+ sd2 = map_id2sd(sd->guild_invite_account);
+ sd->guild_invite = 0;
+ sd->guild_invite_account = 0;
+
+ if(flag==1){ // 失敗
+ if( sd2!=NULL )
+ clif_guild_inviteack(sd2,3);
+ return 0;
+ }
+
+ // 成功
+ sd->state.guild_sent=0;
+ sd->status.guild_id=guild_id;
+
+ if( sd2!=NULL )
+ clif_guild_inviteack(sd2,2);
+
+ // いちおう競合確認
+ guild_check_conflict(sd);
+ //Next line commented because it do nothing, look at guild_recv_info [LuzZza]
+ //clif_charnameupdate(sd); //Update display name [Skotlex]
+ return 0;
+}
+
+// ギルド脱退要求
+int guild_leave(struct map_session_data *sd,int guild_id,
+ int account_id,int char_id,const char *mes)
+{
+ struct guild *g;
+ int i;
+
+ nullpo_retr(0, sd);
+
+ g = guild_search(sd->status.guild_id);
+
+ if(g==NULL)
+ return 0;
+
+ if( sd->status.account_id!=account_id ||
+ sd->status.char_id!=char_id || sd->status.guild_id!=guild_id)
+ return 0;
+
+ for(i=0;i<g->max_member;i++){ // 所属しているか
+ if( g->member[i].account_id==sd->status.account_id &&
+ g->member[i].char_id==sd->status.char_id ){
+ intif_guild_leave(g->guild_id,sd->status.account_id,sd->status.char_id,0,mes);
+ return 0;
+ }
+ }
+ return 0;
+}
+// ギルド追放要求
+int guild_explusion(struct map_session_data *sd,int guild_id,
+ int account_id,int char_id,const char *mes)
+{
+ struct guild *g;
+ int i,ps;
+
+ nullpo_retr(0, sd);
+
+ g = guild_search(sd->status.guild_id);
+
+ if(g==NULL)
+ return 0;
+
+ if( sd->status.guild_id!=guild_id)
+ return 0;
+
+ if( (ps=guild_getposition(sd,g))<0 || !(g->position[ps].mode&0x0010) )
+ return 0; // 処罰権限無し
+
+ for(i=0;i<g->max_member;i++){ // 所属しているか
+ if( g->member[i].account_id==account_id &&
+ g->member[i].char_id==char_id ){
+ intif_guild_leave(g->guild_id,account_id,char_id,1,mes);
+ memset(&g->member[i],0,sizeof(struct guild_member));
+ return 0;
+ }
+ }
+ return 0;
+}
+// ギルドメンバが脱退した
+int guild_member_leaved(int guild_id,int account_id,int char_id,int flag,
+ const char *name,const char *mes)
+{
+ struct map_session_data *sd=map_charid2sd(char_id);
+ struct guild *g=guild_search(guild_id);
+
+ if(g!=NULL){
+ int i;
+ for(i=0;i<g->max_member;i++) {
+ if( g->member[i].account_id==account_id &&
+ g->member[i].char_id==char_id ){
+ struct map_session_data *sd2=sd;
+ if(sd2==NULL)
+ sd2=guild_getavailablesd(g);
+ else
+ {
+ if(flag==0)
+ clif_guild_leave(sd2,name,mes);
+ else
+ clif_guild_explusion(sd2,name,mes,account_id);
+ }
+ memset(&g->member[i],0,sizeof(struct guild_member));
+ }
+ // メンバーリストを全員に再通知
+ for(i=0;i<g->max_member;i++){
+ if( g->member[i].sd!=NULL )
+ clif_guild_memberlist(g->member[i].sd);
+ }
+ }
+ }
+ if(sd!=NULL) {
+ if (sd->status.guild_id==guild_id){
+ guild_send_dot_remove(sd);
+ sd->status.guild_id=0;
+ sd->guild_emblem_id=0;
+ sd->state.guild_sent=0;
+ clif_charnameupdate(sd); //Update display name [Skotlex]
+ }
+ }
+
+ return 0;
+}
+// ギルドメンバのオンライン状態/Lv更新送信
+int guild_send_memberinfoshort(struct map_session_data *sd,int online)
+{
+ struct guild *g;
+
+ nullpo_retr(0, sd);
+
+ if(sd->status.guild_id<=0)
+ return 0;
+ g=guild_search(sd->status.guild_id);
+ if(g==NULL)
+ return 0;
+
+ intif_guild_memberinfoshort(g->guild_id,
+ sd->status.account_id,sd->status.char_id,online,sd->status.base_level,sd->status.class_);
+
+ if( !online ){ // ログアウトするならsdをクリアして終了
+ int i=guild_getindex(g,sd->status.account_id,sd->status.char_id);
+ if(i>=0)
+ g->member[i].sd=NULL;
+ return 0;
+ }
+
+ if( sd->state.guild_sent!=0 ) // ギルド初期送信データは送信済み
+ return 0;
+
+ // 競合確認
+ guild_check_conflict(sd);
+
+ // あるならギルド初期送信データ送信
+ if( (g=guild_search(sd->status.guild_id))!=NULL ){
+ guild_check_member(g); // 所属を確認する
+ if(sd->status.guild_id==g->guild_id){
+ clif_guild_belonginfo(sd,g);
+ clif_guild_notice(sd,g);
+ sd->state.guild_sent=1;
+ sd->guild_emblem_id=g->emblem_id;
+ }
+ }
+ return 0;
+}
+// ギルドメンバのオンライン状態/Lv更新通知
+int guild_recv_memberinfoshort(int guild_id,int account_id,int char_id,int online,int lv,int class_)
+{
+ int i,alv,c,idx=-1,om=0,oldonline=-1;
+ struct guild *g=guild_search(guild_id);
+ if(g==NULL)
+ return 0;
+ for(i=0,alv=0,c=0,om=0;i<g->max_member;i++){
+ struct guild_member *m=&g->member[i];
+ if(m->account_id==account_id && m->char_id==char_id ){
+ oldonline=m->online;
+ m->online=online;
+ m->lv=lv;
+ m->class_=class_;
+ idx=i;
+ }
+ if(m->account_id>0){
+ alv+=m->lv;
+ c++;
+ }
+ if(m->online)
+ om++;
+ }
+ if(idx == -1 || c == 0) {
+ // ギルドのメンバー外なので追放扱いする
+ struct map_session_data *sd = map_id2sd(account_id);
+ if(sd && sd->char_id == char_id) {
+ sd->status.guild_id=0;
+ sd->guild_emblem_id=0;
+ sd->state.guild_sent=0;
+ }
+ if(battle_config.error_log)
+ ShowWarning("guild: not found member %d,%d on %d[%s]\n", account_id,char_id,guild_id,g->name);
+ return 0;
+ }
+ g->average_lv=alv/c;
+ g->connect_member=om;
+
+ if(oldonline!=online) // オンライン状態が変わったので通知
+ clif_guild_memberlogin_notice(g,idx,online);
+
+ for(i=0;i<g->max_member;i++){ // sd再設定
+ struct map_session_data *sd= map_id2sd(g->member[i].account_id);
+ if (sd && sd->status.char_id == g->member[i].char_id &&
+ sd->status.guild_id == g->guild_id &&
+ !sd->state.waitingdisconnect)
+ g->member[i].sd = sd;
+ else sd = NULL;
+ }
+
+ // ここにクライアントに送信処理が必要
+
+ return 0;
+}
+// ギルド会話送信
+int guild_send_message(struct map_session_data *sd,char *mes,int len)
+{
+ nullpo_retr(0, sd);
+
+ if(sd->status.guild_id==0)
+ return 0;
+ intif_guild_message(sd->status.guild_id,sd->status.account_id,mes,len);
+ guild_recv_message(sd->status.guild_id,sd->status.account_id,mes,len);
+
+ //Chatlogging type 'G'
+ if(log_config.chat&1 //we log everything then
+ || ( log_config.chat&8 //if Guild bit is on
+ && ( !agit_flag || !(log_config.chat&16) ))) //if WOE ONLY flag is off or AGIT is OFF
+ log_chat("G", sd->status.guild_id, sd->status.char_id, sd->status.account_id, (char*)mapindex_id2name(sd->mapindex), sd->bl.x, sd->bl.y, NULL, mes);
+
+ return 0;
+}
+// ギルド会話受信
+int guild_recv_message(int guild_id,int account_id,char *mes,int len)
+{
+ struct guild *g;
+ if( (g=guild_search(guild_id))==NULL)
+ return 0;
+ clif_guild_message(g,account_id,mes,len);
+ return 0;
+}
+// ギルドメンバの役職変更
+int guild_change_memberposition(int guild_id,int account_id,int char_id,int idx)
+{
+ return intif_guild_change_memberinfo(
+ guild_id,account_id,char_id,GMI_POSITION,&idx,sizeof(idx));
+}
+// ギルドメンバの役職変更通知
+int guild_memberposition_changed(struct guild *g,int idx,int pos)
+{
+ nullpo_retr(0, g);
+
+ g->member[idx].position=pos;
+ clif_guild_memberpositionchanged(g,idx);
+
+ // Update char position in client [LuzZza]
+ if(g->member[idx].sd != NULL)
+ clif_charnameupdate(g->member[idx].sd);
+ return 0;
+}
+// ギルド役職変更
+int guild_change_position(struct map_session_data *sd,int idx,
+ int mode,int exp_mode,const char *name)
+{
+ struct guild_position p;
+
+ nullpo_retr(0, sd);
+
+ if(exp_mode>battle_config.guild_exp_limit)
+ exp_mode=battle_config.guild_exp_limit;
+ if(exp_mode<0)exp_mode=0;
+ p.mode=mode;
+ p.exp_mode=exp_mode;
+ memcpy(p.name,name,NAME_LENGTH-1);
+ p.name[NAME_LENGTH-1] = '\0'; //Security check... [Skotlex]
+ return intif_guild_position(sd->status.guild_id,idx,&p);
+}
+// ギルド役職変更通知
+int guild_position_changed(int guild_id,int idx,struct guild_position *p)
+{
+ struct guild *g=guild_search(guild_id);
+ int i;
+ if(g==NULL)
+ return 0;
+ memcpy(&g->position[idx],p,sizeof(struct guild_position));
+ clif_guild_positionchanged(g,idx);
+
+ // Update char name in client [LuzZza]
+ for(i=0;i<g->max_member;i++)
+ if(g->member[i].position == idx && g->member[i].sd != NULL)
+ clif_charnameupdate(g->member[i].sd);
+ return 0;
+}
+// ギルド告知変更
+int guild_change_notice(struct map_session_data *sd,int guild_id,const char *mes1,const char *mes2)
+{
+ nullpo_retr(0, sd);
+
+ if(guild_id!=sd->status.guild_id)
+ return 0;
+ return intif_guild_notice(guild_id,mes1,mes2);
+}
+// ギルド告知変更通知
+int guild_notice_changed(int guild_id,const char *mes1,const char *mes2)
+{
+ int i;
+ struct map_session_data *sd;
+ struct guild *g=guild_search(guild_id);
+ if(g==NULL)
+ return 0;
+
+ memcpy(g->mes1,mes1,60);
+ memcpy(g->mes2,mes2,120);
+
+ for(i=0;i<g->max_member;i++){
+ if((sd=g->member[i].sd)!=NULL)
+ clif_guild_notice(sd,g);
+ }
+ return 0;
+}
+// ギルドエンブレム変更
+int guild_change_emblem(struct map_session_data *sd,int len,const char *data)
+{
+ struct guild *g;
+ nullpo_retr(0, sd);
+
+ if (battle_config.require_glory_guild &&
+ !((g = guild_search(sd->status.guild_id)) && guild_checkskill(g, GD_GLORYGUILD)>0)) {
+ clif_skill_fail(sd,GD_GLORYGUILD,0,0);
+ return 0;
+ }
+
+ return intif_guild_emblem(sd->status.guild_id,len,data);
+}
+// ギルドエンブレム変更通知
+int guild_emblem_changed(int len,int guild_id,int emblem_id,const char *data)
+{
+ int i;
+ struct map_session_data *sd;
+ struct guild *g=guild_search(guild_id);
+ if(g==NULL)
+ return 0;
+
+ memcpy(g->emblem_data,data,len);
+ g->emblem_len=len;
+ g->emblem_id=emblem_id;
+
+ for(i=0;i<g->max_member;i++){
+ if((sd=g->member[i].sd)!=NULL){
+ sd->guild_emblem_id=emblem_id;
+ clif_guild_belonginfo(sd,g);
+ clif_guild_emblem(sd,g);
+ }
+ }
+ return 0;
+}
+
+static void* create_expcache(DBKey key, va_list args) {
+ struct guild_expcache *c;
+ struct map_session_data *sd = va_arg(args, struct map_session_data*);
+ c = (struct guild_expcache *)aCallocA(1, sizeof(struct guild_expcache));
+ c->guild_id = sd->status.guild_id;
+ c->account_id = sd->status.account_id;
+ c->char_id = sd->status.char_id;
+ return c;
+}
+// ギルドのEXP上納
+int guild_payexp(struct map_session_data *sd,int exp)
+{
+ struct guild *g;
+ struct guild_expcache *c;
+ int per, exp2;
+ double tmp;
+
+ nullpo_retr(0, sd);
+
+ if (sd->status.guild_id == 0 ||
+ (g = guild_search(sd->status.guild_id)) == NULL ||
+ (per = g->position[guild_getposition(sd,g)].exp_mode) <= 0)
+ return 0;
+
+ if (per > 100) per = 100;
+
+ if ((exp2 = exp * per / 100) <= 0)
+ return 0;
+
+ c = guild_expcache_db->ensure(guild_expcache_db, i2key(sd->status.char_id), create_expcache, sd);
+ tmp = c->exp;
+ if (battle_config.guild_exp_rate != 100)
+ tmp += battle_config.guild_exp_rate*exp2/100;
+ else
+ tmp += exp2;
+ c->exp = (tmp > 0x7fffffff) ? 0x7fffffff : (int)tmp;
+ return exp2;
+}
+
+// Celest
+int guild_getexp(struct map_session_data *sd,int exp)
+{
+ struct guild *g;
+ struct guild_expcache *c;
+ double tmp;
+ nullpo_retr(0, sd);
+
+ if (sd->status.guild_id == 0 || (g = guild_search(sd->status.guild_id)) == NULL)
+ return 0;
+
+ c = guild_expcache_db->ensure(guild_expcache_db, i2key(sd->status.char_id), create_expcache, sd);
+ tmp = c->exp + exp;
+ c->exp = (tmp > 0x7fffffff) ? 0x7fffffff : (int)tmp;
+ return exp;
+}
+
+// スキルポイント割り振り
+int guild_skillup(struct map_session_data *sd,int skill_num,int flag)
+{
+ struct guild *g;
+ int idx = skill_num - GD_SKILLBASE;
+
+ nullpo_retr(0, sd);
+
+ if(idx < 0 || idx >= MAX_GUILDSKILL)
+
+ return 0;
+ if(sd->status.guild_id==0 || (g=guild_search(sd->status.guild_id))==NULL)
+ return 0;
+ if(strcmp(sd->status.name,g->master))
+ return 0;
+
+ if( (g->skill_point>0 || flag&1) &&
+ g->skill[idx].id!=0 &&
+ g->skill[idx].lv < guild_skill_get_max(skill_num) ){
+ intif_guild_skillup(g->guild_id,skill_num,sd->status.account_id,flag);
+ }
+ status_calc_pc (sd, 0); // Celest
+
+ return 0;
+}
+// スキルポイント割り振り通知
+int guild_skillupack(int guild_id,int skill_num,int account_id)
+{
+ struct map_session_data *sd=map_id2sd(account_id);
+ struct guild *g=guild_search(guild_id);
+ int i;
+ if(g==NULL)
+ return 0;
+ if(sd!=NULL)
+ clif_guild_skillup(sd,skill_num,g->skill[skill_num-GD_SKILLBASE].lv);
+ // 全員に通知
+ for(i=0;i<g->max_member;i++)
+ if((sd=g->member[i].sd)!=NULL)
+ clif_guild_skillinfo(sd);
+ return 0;
+}
+
+// ギルド同盟数所得
+int guild_get_alliance_count(struct guild *g,int flag)
+{
+ int i,c;
+
+ nullpo_retr(0, g);
+
+ for(i=c=0;i<MAX_GUILDALLIANCE;i++){
+ if( g->alliance[i].guild_id>0 &&
+ g->alliance[i].opposition==flag )
+ c++;
+ }
+ return c;
+}
+
+// Blocks all guild skills which have a common delay time.
+void guild_block_skill(struct map_session_data *sd, int time) {
+ int skill_num[] = { GD_BATTLEORDER, GD_REGENERATION, GD_RESTORE, GD_EMERGENCYCALL };
+ int i;
+ for (i = 0; i < 4; i++)
+ pc_blockskill_start(sd, skill_num[i], time);
+}
+
+// 同盟関係かどうかチェック
+// 同盟なら1、それ以外は0
+int guild_check_alliance(int guild_id1, int guild_id2, int flag)
+{
+ struct guild *g;
+ int i;
+
+ g = guild_search(guild_id1);
+ if (g == NULL)
+ return 0;
+
+ for (i=0; i<MAX_GUILDALLIANCE; i++)
+ if ((g->alliance[i].guild_id == guild_id2) && (g->alliance[i].opposition == flag))
+ return 1;
+
+ return 0;
+}
+// ギルド同盟要求
+int guild_reqalliance(struct map_session_data *sd,int account_id)
+{
+ struct map_session_data *tsd= map_id2sd(account_id);
+ struct guild *g[2];
+ int i;
+
+ if(agit_flag) { // Disable alliance creation during woe [Valaris]
+ clif_displaymessage(sd->fd,"Alliances cannot be made during Guild Wars!");
+ return 0;
+ } // end addition [Valaris]
+
+
+ nullpo_retr(0, sd);
+
+ if(tsd==NULL || tsd->status.guild_id<=0)
+ return 0;
+
+ g[0]=guild_search(sd->status.guild_id);
+ g[1]=guild_search(tsd->status.guild_id);
+
+ if(g[0]==NULL || g[1]==NULL)
+ return 0;
+
+ if( guild_get_alliance_count(g[0],0)>=3 ) // 同盟数確認
+ clif_guild_allianceack(sd,4);
+ if( guild_get_alliance_count(g[1],0)>=3 )
+ clif_guild_allianceack(sd,3);
+
+ if( tsd->guild_alliance>0 ){ // 相手が同盟要請状態かどうか確認
+ clif_guild_allianceack(sd,1);
+ return 0;
+ }
+
+ for(i=0;i<MAX_GUILDALLIANCE;i++){ // すでに同盟状態か確認
+ if( g[0]->alliance[i].guild_id==tsd->status.guild_id &&
+ g[0]->alliance[i].opposition==0){
+ clif_guild_allianceack(sd,0);
+ return 0;
+ }
+ }
+
+ tsd->guild_alliance=sd->status.guild_id;
+ tsd->guild_alliance_account=sd->status.account_id;
+
+ clif_guild_reqalliance(tsd,sd->status.account_id,g[0]->name);
+ return 0;
+}
+// ギルド勧誘への返答
+int guild_reply_reqalliance(struct map_session_data *sd,int account_id,int flag)
+{
+ struct map_session_data *tsd;
+
+ nullpo_retr(0, sd);
+ nullpo_retr(0, tsd= map_id2sd( account_id ));
+
+ if(sd->guild_alliance!=tsd->status.guild_id) // 勧誘とギルドIDが違う
+ return 0;
+
+ if(flag==1){ // 承諾
+ int i;
+
+ struct guild *g,*tg; // 同盟数再確認
+ g=guild_search(sd->status.guild_id);
+ tg=guild_search(tsd->status.guild_id);
+
+ if(g==NULL || guild_get_alliance_count(g,0)>=3){
+ clif_guild_allianceack(sd,4);
+ clif_guild_allianceack(tsd,3);
+ return 0;
+ }
+ if(tg==NULL || guild_get_alliance_count(tg,0)>=3){
+ clif_guild_allianceack(sd,3);
+ clif_guild_allianceack(tsd,4);
+ return 0;
+ }
+
+ for(i=0;i<MAX_GUILDALLIANCE;i++){
+ if(g->alliance[i].guild_id==tsd->status.guild_id &&
+ g->alliance[i].opposition==1)
+ intif_guild_alliance( sd->status.guild_id,tsd->status.guild_id,
+ sd->status.account_id,tsd->status.account_id,9 );
+ }
+ for(i=0;i<MAX_GUILDALLIANCE;i++){
+ if(tg->alliance[i].guild_id==sd->status.guild_id &&
+ tg->alliance[i].opposition==1)
+ intif_guild_alliance( tsd->status.guild_id,sd->status.guild_id,
+ tsd->status.account_id,sd->status.account_id,9 );
+ }
+
+ // inter鯖へ同盟要請
+ intif_guild_alliance( sd->status.guild_id,tsd->status.guild_id,
+ sd->status.account_id,tsd->status.account_id,0 );
+ return 0;
+ }else{ // 拒否
+ sd->guild_alliance=0;
+ sd->guild_alliance_account=0;
+ if(tsd!=NULL)
+ clif_guild_allianceack(tsd,3);
+ }
+ return 0;
+}
+// ギルド関係解消
+int guild_delalliance(struct map_session_data *sd,int guild_id,int flag)
+{
+ if(agit_flag) { // Disable alliance breaking during woe [Valaris]
+ clif_displaymessage(sd->fd,"Alliances cannot be broken during Guild Wars!");
+ return 0;
+ } // end addition [Valaris]
+
+ nullpo_retr(0, sd);
+
+ intif_guild_alliance( sd->status.guild_id,guild_id,
+ sd->status.account_id,0,flag|8 );
+ return 0;
+}
+// ギルド敵対
+int guild_opposition(struct map_session_data *sd,int char_id)
+{
+ struct map_session_data *tsd=map_id2sd(char_id);
+ struct guild *g;
+ int i;
+
+ nullpo_retr(0, sd);
+
+ g=guild_search(sd->status.guild_id);
+ if(g==NULL || tsd==NULL)
+ return 0;
+
+ if( guild_get_alliance_count(g,1)>=3 ) // 敵対数確認
+ clif_guild_oppositionack(sd,1);
+
+ for(i=0;i<MAX_GUILDALLIANCE;i++){ // すでに関係を持っているか確認
+ if(g->alliance[i].guild_id==tsd->status.guild_id){
+ if(g->alliance[i].opposition==1){ // すでに敵対
+ clif_guild_oppositionack(sd,2);
+ return 0;
+ }else // 同盟破棄
+ intif_guild_alliance( sd->status.guild_id,tsd->status.guild_id,
+ sd->status.account_id,tsd->status.account_id,8 );
+ }
+ }
+
+ // inter鯖に敵対要請
+ intif_guild_alliance( sd->status.guild_id,tsd->status.guild_id,
+ sd->status.account_id,tsd->status.account_id,1 );
+ return 0;
+}
+// ギルド同盟/敵対通知
+int guild_allianceack(int guild_id1,int guild_id2,int account_id1,int account_id2,
+ int flag,const char *name1,const char *name2)
+{
+ struct guild *g[2];
+ int guild_id[2];
+ const char *guild_name[2];
+ struct map_session_data *sd[2];
+ int j,i;
+
+ guild_id[0] = guild_id1;
+ guild_id[1] = guild_id2;
+ guild_name[0] = name1;
+ guild_name[1] = name2;
+ sd[0] = map_id2sd(account_id1);
+ sd[1] = map_id2sd(account_id2);
+
+ g[0]=guild_search(guild_id1);
+ g[1]=guild_search(guild_id2);
+
+ if(sd[0]!=NULL && (flag&0x0f)==0){
+ sd[0]->guild_alliance=0;
+ sd[0]->guild_alliance_account=0;
+ }
+
+ if(flag&0x70){ // 失敗
+ for(i=0;i<2-(flag&1);i++)
+ if( sd[i]!=NULL )
+ clif_guild_allianceack(sd[i],((flag>>4)==i+1)?3:4);
+ return 0;
+ }
+// if(battle_config.etc_log)
+// printf("guild alliance_ack %d %d %d %d %d %s %s\n",guild_id1,guild_id2,account_id1,account_id2,flag,name1,name2);
+
+ if(!(flag&0x08)){ // 関係追加
+ for(i=0;i<2-(flag&1);i++)
+ if(g[i]!=NULL)
+ for(j=0;j<MAX_GUILDALLIANCE;j++)
+ if(g[i]->alliance[j].guild_id==0){
+ g[i]->alliance[j].guild_id=guild_id[1-i];
+ memcpy(g[i]->alliance[j].name,guild_name[1-i],NAME_LENGTH-1);
+ g[i]->alliance[j].opposition=flag&1;
+ break;
+ }
+ }else{ // 関係解消
+ for(i=0;i<2-(flag&1);i++){
+ if(g[i]!=NULL)
+ for(j=0;j<MAX_GUILDALLIANCE;j++)
+ if( g[i]->alliance[j].guild_id==guild_id[1-i] &&
+ g[i]->alliance[j].opposition==(flag&1)){
+ g[i]->alliance[j].guild_id=0;
+ break;
+ }
+ if( sd[i]!=NULL ) // 解消通知
+ clif_guild_delalliance(sd[i],guild_id[1-i],(flag&1));
+ }
+ }
+
+ if((flag&0x0f)==0){ // 同盟通知
+ if( sd[1]!=NULL )
+ clif_guild_allianceack(sd[1],2);
+ }else if((flag&0x0f)==1){ // 敵対通知
+ if( sd[0]!=NULL )
+ clif_guild_oppositionack(sd[0],0);
+ }
+
+
+ for(i=0;i<2-(flag&1);i++){ // 同盟/敵対リストの再送信
+ struct map_session_data *sd;
+ if(g[i]!=NULL)
+ for(j=0;j<g[i]->max_member;j++)
+ if((sd=g[i]->member[j].sd)!=NULL)
+ clif_guild_allianceinfo(sd);
+ }
+ return 0;
+}
+// ギルド解散通知用
+int guild_broken_sub(DBKey key,void *data,va_list ap)
+{
+ struct guild *g=(struct guild *)data;
+ int guild_id=va_arg(ap,int);
+ int i,j;
+ struct map_session_data *sd=NULL;
+
+ nullpo_retr(0, g);
+
+ for(i=0;i<MAX_GUILDALLIANCE;i++){ // 関係を破棄
+ if(g->alliance[i].guild_id==guild_id){
+ for(j=0;j<g->max_member;j++)
+ if( (sd=g->member[j].sd)!=NULL )
+ clif_guild_delalliance(sd,guild_id,g->alliance[i].opposition);
+ intif_guild_alliance(g->guild_id, guild_id,0,0,g->alliance[i].opposition|8);
+ g->alliance[i].guild_id=0;
+ }
+ }
+ return 0;
+}
+
+//Invoked on Castles when a guild is broken. [Skotlex]
+int castle_guild_broken_sub(DBKey key,void *data,va_list ap)
+{
+ struct guild_castle *gc=(struct guild_castle *)data;
+ int guild_id=va_arg(ap,int);
+
+ nullpo_retr(0, gc);
+
+ if (gc->guild_id == guild_id)
+ { //Save the new 'owner', this should invoke guardian clean up and other such things.
+ gc->guild_id = 0;
+ guild_castledatasave(gc->castle_id, 1, 0);
+ }
+ return 0;
+}
+
+//Innvoked on /breakguild "Guild name"
+int guild_broken(int guild_id,int flag)
+{
+ struct guild *g=guild_search(guild_id);
+// struct guild_castle *gc=NULL;
+ struct map_session_data *sd;
+ int i;
+// char *name;;
+
+ if(flag!=0 || g==NULL)
+ return 0;
+
+ //we call castle_event::OnGuildBreak of all castlesof the guild
+ //you can set all castle_events in the castle_db.txt
+/* name=(char *)aCalloc(50,sizeof(char)); //24 char = event name, + space for "::OnGuildBreak"
+ for(i=0;i<MAX_GUILDCASTLE;i++){
+ if( (gc=guild_castle_search(i)) != NULL ){
+ if(gc->guild_id == guild_id){
+ memcpy(name,gc->castle_event,50);
+ npc_event_do(strcat(name,"::OnGuildBreak"));
+ }
+ }
+ }
+ free(name);
+*/
+ for(i=0;i<g->max_member;i++){ // ギルド解散を通知
+ if((sd=g->member[i].sd)!=NULL){
+ if(sd->state.storage_flag == 2)
+ storage_guild_storage_quit(sd,1);
+ sd->status.guild_id=0;
+ sd->state.guild_sent=0;
+ clif_guild_broken(g->member[i].sd,0);
+ clif_charnameupdate(sd); // [LuzZza]
+ }
+ }
+
+ guild_db->foreach(guild_db,guild_broken_sub,guild_id);
+ castle_db->foreach(castle_db,castle_guild_broken_sub,guild_id);
+ if (guild_cache && guild_cache->guild_id == guild_id)
+ guild_cache = NULL;
+ guild_storage_delete(guild_id);
+ idb_remove(guild_db,guild_id);
+ return 0;
+}
+
+//Changes the Guild Master to the specified player. [Skotlex]
+int guild_gm_change(int guild_id, struct map_session_data *sd)
+{
+ struct guild *g;
+ nullpo_retr(0, sd);
+
+ if (sd->status.guild_id != guild_id)
+ return 0;
+
+ g=guild_search(guild_id);
+
+ nullpo_retr(0, g);
+
+ if (strcmp(g->master, sd->status.name) == 0) //Nothing to change.
+ return 0;
+
+ //Notify servers that master has changed.
+ intif_guild_change_gm(guild_id, sd->status.name, strlen(sd->status.name));
+ return 1;
+}
+
+//Notification from Char server that a guild's master has changed. [Skotlex]
+int guild_gm_changed(int guild_id, int pos)
+{
+ struct guild *g;
+ struct guild_member gm;
+
+ g=guild_search(guild_id);
+
+ if (!g || pos < 0 || pos > g->max_member)
+ return 0;
+
+ memcpy(&gm, &g->member[pos], sizeof (struct guild_member));
+ memcpy(&g->member[pos], &g->member[0], sizeof(struct guild_member));
+ memcpy(&g->member[0], &gm, sizeof(struct guild_member));
+
+ g->member[pos].position = g->member[0].position;
+ g->member[0].position = 0; //Position 0: guild Master.
+ strcpy(g->master, g->member[0].name);
+
+ if (g->member[pos].sd && g->member[pos].sd->fd)
+ {
+ clif_displaymessage(g->member[pos].sd->fd, "You no longer are the Guild Master.");
+ g->member[pos].sd->state.gmaster_flag = 0;
+ }
+
+ if (g->member[0].sd && g->member[0].sd->fd)
+ {
+ clif_displaymessage(g->member[0].sd->fd, "You have become the Guild Master!");
+ g->member[0].sd->state.gmaster_flag = g;
+ }
+ return 1;
+}
+
+// ギルド解散
+int guild_break(struct map_session_data *sd,char *name)
+{
+ struct guild *g;
+ int i;
+
+ nullpo_retr(0, sd);
+
+ if( (g=guild_search(sd->status.guild_id))==NULL )
+ return 0;
+ if(strcmp(g->name,name)!=0)
+ return 0;
+ if(strcmp(sd->status.name,g->master)!=0)
+ return 0;
+ for(i=0;i<g->max_member;i++){
+ if( g->member[i].account_id>0 && (
+ g->member[i].account_id!=sd->status.account_id ||
+ g->member[i].char_id!=sd->status.char_id ))
+ break;
+ }
+ if(i<g->max_member){
+ clif_guild_broken(sd,2);
+ return 0;
+ }
+
+ intif_guild_break(g->guild_id);
+ return 0;
+}
+
+// ギルド城データ要求
+int guild_castledataload(int castle_id,int index)
+{
+ return intif_guild_castle_dataload(castle_id,index);
+}
+// ギルド城情報所得時イベント追加
+int guild_addcastleinfoevent(int castle_id,int index,const char *name)
+{
+ struct eventlist *ev;
+ int code=castle_id|(index<<16);
+
+ if( name==NULL || *name==0 )
+ return 0;
+
+ ev=(struct eventlist *)aCalloc(1,sizeof(struct eventlist));
+ memcpy(ev->name,name,sizeof(ev->name));
+ //The next event becomes whatever was currently stored.
+ ev->next= idb_put(guild_castleinfoevent_db,code,ev);
+ return 0;
+}
+
+// ギルド城データ要求返信
+int guild_castledataloadack(int castle_id,int index,int value)
+{
+ struct guild_castle *gc=guild_castle_search(castle_id);
+ int code=castle_id|(index<<16);
+ struct eventlist *ev,*ev2;
+
+ if(gc==NULL){
+ return 0;
+ }
+ switch(index){
+ case 1:
+ gc->guild_id = value;
+ if (value && guild_search(value)==NULL) //Request guild data which will be required for spawned guardians. [Skotlex]
+ guild_request_info(value);
+ break;
+ case 2: gc->economy = value; break;
+ case 3: gc->defense = value; break;
+ case 4: gc->triggerE = value; break;
+ case 5: gc->triggerD = value; break;
+ case 6: gc->nextTime = value; break;
+ case 7: gc->payTime = value; break;
+ case 8: gc->createTime = value; break;
+ case 9: gc->visibleC = value; break;
+ case 10:
+ case 11:
+ case 12:
+ case 13:
+ case 14:
+ case 15:
+ case 16:
+ case 17:
+ gc->guardian[index-10].visible = value; break;
+ case 18:
+ case 19:
+ case 20:
+ case 21:
+ case 22:
+ case 23:
+ case 24:
+ case 25:
+ gc->guardian[index-18].hp = value; break;
+ default:
+ ShowError("guild_castledataloadack ERROR!! (Not found index=%d)\n", index);
+ return 0;
+ }
+ if( (ev=idb_remove(guild_castleinfoevent_db,code))!=NULL ){
+ while(ev){
+ npc_event_do(ev->name);
+ ev2=ev->next;
+ aFree(ev);
+ ev=ev2;
+ }
+ }
+ return 1;
+}
+// ギルド城データ変更要求
+int guild_castledatasave(int castle_id,int index,int value)
+{
+ if (index == 1)
+ { //The castle's owner has changed? Update Guardian ownership, too. [Skotlex]
+ struct guild_castle *gc = guild_castle_search(castle_id);
+ int m = -1;
+ if (gc) m = map_mapname2mapid(gc->map_name);
+ if (m != -1)
+ map_foreachinmap(mob_guardian_guildchange, m, BL_MOB);
+ }
+ return intif_guild_castle_datasave(castle_id,index,value);
+}
+
+// ギルド城データ変更通知
+int guild_castledatasaveack(int castle_id,int index,int value)
+{
+ struct guild_castle *gc=guild_castle_search(castle_id);
+ if(gc==NULL){
+ return 0;
+ }
+ switch(index){
+ case 1: gc->guild_id = value; break;
+ case 2: gc->economy = value; break;
+ case 3: gc->defense = value; break;
+ case 4: gc->triggerE = value; break;
+ case 5: gc->triggerD = value; break;
+ case 6: gc->nextTime = value; break;
+ case 7: gc->payTime = value; break;
+ case 8: gc->createTime = value; break;
+ case 9: gc->visibleC = value; break;
+ case 10:
+ case 11:
+ case 12:
+ case 13:
+ case 14:
+ case 15:
+ case 16:
+ case 17:
+ gc->guardian[index-10].visible = value; break;
+ case 18:
+ case 19:
+ case 20:
+ case 21:
+ case 22:
+ case 23:
+ case 24:
+ case 25:
+ gc->guardian[index-18].hp = value; break;
+ default:
+ ShowError("guild_castledatasaveack ERROR!! (Not found index=%d)\n", index);
+ return 0;
+ }
+ return 1;
+}
+
+// ギルドデータ一括受信(初期化時)
+int guild_castlealldataload(int len,struct guild_castle *gc)
+{
+ int i;
+ int n = (len-4) / sizeof(struct guild_castle), ev = -1;
+
+ nullpo_retr(0, gc);
+
+ //Last owned castle in the list invokes ::OnAgitinit
+ for(i = 0; i < n; i++) {
+ if ((gc + i)->guild_id)
+ ev = i;
+ }
+
+ // 城データ格納とギルド情報要求
+ for(i = 0; i < n; i++, gc++) {
+ struct guild_castle *c = guild_castle_search(gc->castle_id);
+ if (!c) {
+ ShowError("guild_castlealldataload Castle id=%d not found.\n", gc->castle_id);
+ continue;
+ }
+ memcpy(&c->guild_id,&gc->guild_id,
+ sizeof(struct guild_castle) - ((int)&c->guild_id - (int)c) );
+ if( c->guild_id ){
+ if(i!=ev)
+ guild_request_info(c->guild_id);
+ else
+ guild_npc_request_info(c->guild_id, "::OnAgitInit");
+ }
+ }
+ if (ev == -1) //No castles owned, invoke OnAgitInit as it is.
+ npc_event_doall("OnAgitInit");
+ return 0;
+}
+
+int guild_agit_start(void)
+{ // Run All NPC_Event[OnAgitStart]
+ int c = npc_event_doall("OnAgitStart");
+ ShowStatus("NPC_Event:[OnAgitStart] Run (%d) Events by @AgitStart.\n",c);
+ // Start auto saving
+ guild_save_timer = add_timer_interval (gettick() + GUILD_SAVE_INTERVAL, guild_save_sub, 0, 0, GUILD_SAVE_INTERVAL);
+ return 0;
+}
+
+int guild_agit_end(void)
+{ // Run All NPC_Event[OnAgitEnd]
+ int c = npc_event_doall("OnAgitEnd");
+ ShowStatus("NPC_Event:[OnAgitEnd] Run (%d) Events by @AgitEnd.\n",c);
+ // Stop auto saving
+ delete_timer (guild_save_timer, guild_save_sub);
+ return 0;
+}
+
+int guild_gvg_eliminate_timer(int tid,unsigned int tick,int id,int data)
+{ // Run One NPC_Event[OnAgitEliminate]
+ char *name = (char*)data;
+ size_t len = (name) ? strlen(name) : 0;
+ // the rest is dangerous, but let it crash,
+ // if this happens, it's ruined anyway
+ int c=0;
+
+ if(agit_flag) // Agit not already End
+ {
+ char *evname=(char*)aMalloc( (len + 10) * sizeof(char));
+ memcpy(evname,name,len - 5);
+ strcpy(evname + len - 5,"Eliminate");
+ c = npc_event_do(evname);
+ ShowStatus("NPC_Event:[%s] Run (%d) Events.\n",evname,c);
+ aFree(evname); // [Lance] Should fix this
+ }
+ if(name) aFree(name);
+ return 0;
+}
+
+static int Ghp[MAX_GUILDCASTLE][MAX_GUARDIANS]; // so save only if HP are changed // experimental code [Yor]
+static int Gid[MAX_GUILDCASTLE];
+int guild_save_sub(int tid,unsigned int tick,int id,int data)
+{
+ struct guild_castle *gc;
+ int i,j;
+
+ for(i = 0; i < MAX_GUILDCASTLE; i++) { // [Yor]
+ gc = guild_castle_search(i);
+ if (!gc) continue;
+ if (gc->guild_id != Gid[i]) {
+ // Re-save guild id if its owner guild has changed
+ guild_castledatasave(gc->castle_id, 1, gc->guild_id);
+ Gid[i] = gc->guild_id;
+ }
+ for (j = 0; j < MAX_GUARDIANS; j++)
+ {
+ if (gc->guardian[j].visible && Ghp[i][j] != gc->guardian[j].hp)
+ guild_castledatasave(gc->castle_id, 18+j, gc->guardian[j].hp);
+ }
+ }
+
+ return 0;
+}
+
+int guild_agit_break(struct mob_data *md)
+{ // Run One NPC_Event[OnAgitBreak]
+ char *evname;
+
+ nullpo_retr(0, md);
+
+ evname=(char *)aCallocA(strlen(md->npc_event) + 1, sizeof(char));
+
+ strcpy(evname,md->npc_event);
+// Now By User to Run [OnAgitBreak] NPC Event...
+// It's a little impossible to null point with player disconnect in this!
+// But Script will be stop, so nothing...
+// Maybe will be changed in the futher..
+// int c = npc_event_do(evname);
+ if(!agit_flag) return 0; // Agit already End
+ add_timer(gettick()+battle_config.gvg_eliminate_time,guild_gvg_eliminate_timer,md->bl.m,(int)evname);
+ return 0;
+}
+
+// [MouseJstr]
+// How many castles does this guild have?
+int guild_checkcastles(struct guild *g) {
+ int i,nb_cas=0, id,cas_id=0;
+ struct guild_castle *gc;
+ id=g->guild_id;
+ for(i=0;i<MAX_GUILDCASTLE;i++){
+ gc=guild_castle_search(i);
+ cas_id=gc->guild_id;
+ if(g->guild_id==cas_id)
+ nb_cas=nb_cas+1;
+ } //end for
+ return nb_cas;
+}
+
+// [MouseJstr]
+// is this guild allied with this castle?
+int guild_isallied(struct guild *g, struct guild_castle *gc)
+{
+ int i;
+
+ nullpo_retr(0, g);
+
+ if(g->guild_id == gc->guild_id)
+ return 1;
+
+ if (gc->guild_id == 0)
+ return 0;
+
+
+ for(i=0;i<MAX_GUILDALLIANCE;i++)
+ if(g->alliance[i].guild_id == gc->guild_id) {
+ if(g->alliance[i].opposition == 0)
+ return 1;
+ else
+ return 0;
+ }
+
+ return 0;
+}
+
+int guild_idisallied(int guild_id, int guild_id2)
+{
+ int i;
+ struct guild *g;
+
+ if (guild_id <= 0 || guild_id2 <= 0)
+ return 0;
+
+ if(guild_id == guild_id2)
+ return 1;
+
+ g = guild_search(guild_id);
+
+ nullpo_retr(0, g);
+
+
+ for(i=0;i<MAX_GUILDALLIANCE;i++)
+ if(g->alliance[i].guild_id == guild_id2) {
+ if(g->alliance[i].opposition == 0)
+ return 1;
+ else
+ return 0;
+ }
+
+ return 0;
+}
+
+static int guild_infoevent_db_final(DBKey key,void *data,va_list ap)
+{
+ aFree(data);
+ return 0;
+}
+void do_final_guild(void)
+{
+ guild_db->destroy(guild_db,NULL);
+ castle_db->destroy(castle_db,NULL);
+ guild_expcache_db->destroy(guild_expcache_db,NULL);
+ guild_infoevent_db->destroy(guild_infoevent_db,guild_infoevent_db_final);
+ guild_castleinfoevent_db->destroy(guild_castleinfoevent_db,guild_infoevent_db_final);
+}
diff --git a/src/map/guild.h b/src/map/guild.h
new file mode 100644
index 000000000..eaca29f46
--- /dev/null
+++ b/src/map/guild.h
@@ -0,0 +1,96 @@
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#ifndef _GUILD_H_
+#define _GUILD_H_
+
+struct map_session_data;
+struct mob_data;
+struct guild;
+struct guild_member;
+struct guild_position;
+struct guild_castle;
+
+int guild_skill_get_inf(int id);
+int guild_skill_get_max(int id);
+
+int guild_checkskill(struct guild *g,int id);
+int guild_check_skill_require(struct guild *g,int id); // [Komurka]
+int guild_checkcastles(struct guild *g); // [MouseJstr]
+int guild_isallied(struct guild *g, struct guild_castle *gc);
+int guild_idisallied(int guild_id, int guild_id2); //Checks alliance based on guild Ids. [Skotlex]
+
+void do_init_guild(void);
+struct guild *guild_search(int guild_id);
+struct guild *guild_searchname(char *str);
+struct guild_castle *guild_castle_search(int gcid);
+
+struct guild_castle *guild_mapname2gc(char *mapname);
+struct guild_castle *guild_mapindex2gc(short mapname);
+
+struct map_session_data *guild_getavailablesd(struct guild *g);
+int guild_getindex(struct guild *g,int account_id,int char_id);
+int guild_getposition(struct map_session_data *sd,struct guild *g);
+int guild_payexp(struct map_session_data *sd,int exp);
+int guild_getexp(struct map_session_data *sd,int exp); // [Celest]
+
+int guild_create(struct map_session_data *sd,char *name);
+int guild_created(int account_id,int guild_id);
+int guild_request_info(int guild_id);
+int guild_recv_noinfo(int guild_id);
+int guild_recv_info(struct guild *sg);
+int guild_npc_request_info(int guild_id,const char *ev);
+int guild_invite(struct map_session_data *sd,int account_id);
+int guild_reply_invite(struct map_session_data *sd,int guild_id,int flag);
+int guild_member_added(int guild_id,int account_id,int char_id,int flag);
+int guild_leave(struct map_session_data *sd,int guild_id,
+ int account_id,int char_id,const char *mes);
+int guild_member_leaved(int guild_id,int account_id,int char_id,int flag,
+ const char *name,const char *mes);
+int guild_explusion(struct map_session_data *sd,int guild_id,
+ int account_id,int char_id,const char *mes);
+int guild_skillup(struct map_session_data *sd,int skill_num,int flag);
+void guild_block_skill(struct map_session_data *sd, int time);
+int guild_reqalliance(struct map_session_data *sd,int account_id);
+int guild_reply_reqalliance(struct map_session_data *sd,int account_id,int flag);
+int guild_alliance(int guild_id1,int guild_id2,int account_id1,int account_id2);
+int guild_allianceack(int guild_id1,int guild_id2,int account_id1,int account_id2,
+ int flag,const char *name1,const char *name2);
+int guild_delalliance(struct map_session_data *sd,int guild_id,int flag);
+int guild_opposition(struct map_session_data *sd,int char_id);
+int guild_check_alliance(int guild_id1, int guild_id2, int flag);
+
+int guild_send_memberinfoshort(struct map_session_data *sd,int online);
+int guild_recv_memberinfoshort(int guild_id,int account_id,int char_id,int online,int lv,int class_);
+int guild_change_memberposition(int guild_id,int account_id,int char_id,int idx);
+int guild_memberposition_changed(struct guild *g,int idx,int pos);
+int guild_change_position(struct map_session_data *sd,int idx,
+ int mode,int exp_mode,const char *name);
+int guild_position_changed(int guild_id,int idx,struct guild_position *p);
+int guild_change_notice(struct map_session_data *sd,int guild_id,const char *mes1,const char *mes2);
+int guild_notice_changed(int guild_id,const char *mes1,const char *mes2);
+int guild_change_emblem(struct map_session_data *sd,int len,const char *data);
+int guild_emblem_changed(int len,int guild_id,int emblem_id,const char *data);
+int guild_send_message(struct map_session_data *sd,char *mes,int len);
+int guild_recv_message(int guild_id,int account_id,char *mes,int len);
+int guild_send_dot_remove(struct map_session_data *sd);
+int guild_skillupack(int guild_id,int skill_num,int account_id);
+int guild_break(struct map_session_data *sd,char *name);
+int guild_broken(int guild_id,int flag);
+int guild_gm_change(int guild_id, struct map_session_data *sd);
+int guild_gm_changed(int guild_id, int pos);
+
+int guild_addcastleinfoevent(int castle_id,int index,const char *name);
+int guild_castledataload(int castle_id,int index);
+int guild_castledataloadack(int castle_id,int index,int value);
+int guild_castledatasave(int castle_id,int index,int value);
+int guild_castledatasaveack(int castle_id,int index,int value);
+int guild_castlealldataload(int len,struct guild_castle *gc);
+
+int guild_agit_start(void);
+int guild_agit_end(void);
+int guild_agit_break(struct mob_data *md);
+
+void do_final_guild(void);
+
+#endif
diff --git a/src/map/intif.c b/src/map/intif.c
new file mode 100644
index 000000000..6b53d5c0d
--- /dev/null
+++ b/src/map/intif.c
@@ -0,0 +1,1401 @@
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#include <sys/types.h>
+#ifdef _WIN32
+#include <winsock.h>
+#else
+#include <sys/socket.h>
+#include <netinet/in.h>
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#ifndef _WIN32
+#include <sys/time.h>
+#include <sys/ioctl.h>
+#include <unistd.h>
+#include <arpa/inet.h>
+#endif
+#include <signal.h>
+#include <fcntl.h>
+#include <string.h>
+
+#include "../common/showmsg.h"
+
+#include "socket.h"
+#include "timer.h"
+#include "map.h"
+#include "battle.h"
+#include "chrif.h"
+#include "clif.h"
+#include "pc.h"
+#include "intif.h"
+#include "storage.h"
+#include "party.h"
+#include "guild.h"
+#include "pet.h"
+#include "nullpo.h"
+#include "malloc.h"
+
+static const int packet_len_table[]={
+ -1,-1,27,-1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //0x3800-0x380f
+ -1, 7, 0, 0, 0, 0, 0, 0, -1,11, 0, 0, 0, 0, 0, 0, //0x3810
+ 39,-1,15,15, 14,19, 7,-1, 0, 0, 0, 0, 0, 0, 0, 0, //0x3820
+ 10,-1,15, 0, 79,19, 7,-1, 0,-1,-1,-1, 14,67,186,-1, //0x3830
+ 9, 9,-1,10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //0x3840
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 11,-1, 7, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //0x3880
+};
+
+extern int char_fd; // inter serverのfdはchar_fdを使う
+#define inter_fd (char_fd) // エイリアス
+
+//-----------------------------------------------------------------
+// inter serverへの送信
+
+int CheckForCharServer(void) {
+ return ((char_fd <= 0) || session[char_fd] == NULL || session[char_fd]->wdata == NULL);
+}
+
+// pet
+int intif_create_pet(int account_id,int char_id,short pet_class,short pet_lv,short pet_egg_id,
+ short pet_equip,short intimate,short hungry,char rename_flag,char incuvate,char *pet_name)
+{
+ if (CheckForCharServer())
+ return 0;
+ WFIFOHEAD(inter_fd, 24 + NAME_LENGTH);
+ WFIFOW(inter_fd,0) = 0x3080;
+ WFIFOL(inter_fd,2) = account_id;
+ WFIFOL(inter_fd,6) = char_id;
+ WFIFOW(inter_fd,10) = pet_class;
+ WFIFOW(inter_fd,12) = pet_lv;
+ WFIFOW(inter_fd,14) = pet_egg_id;
+ WFIFOW(inter_fd,16) = pet_equip;
+ WFIFOW(inter_fd,18) = intimate;
+ WFIFOW(inter_fd,20) = hungry;
+ WFIFOB(inter_fd,22) = rename_flag;
+ WFIFOB(inter_fd,23) = incuvate;
+ memcpy(WFIFOP(inter_fd,24),pet_name,NAME_LENGTH);
+ WFIFOSET(inter_fd,24+NAME_LENGTH);
+
+ return 0;
+}
+
+int intif_request_petdata(int account_id,int char_id,int pet_id)
+{
+ if (CheckForCharServer())
+ return 0;
+ WFIFOHEAD(inter_fd, 14);
+ WFIFOW(inter_fd,0) = 0x3081;
+ WFIFOL(inter_fd,2) = account_id;
+ WFIFOL(inter_fd,6) = char_id;
+ WFIFOL(inter_fd,10) = pet_id;
+ WFIFOSET(inter_fd,14);
+
+ return 0;
+}
+
+int intif_save_petdata(int account_id,struct s_pet *p)
+{
+ if (CheckForCharServer())
+ return 0;
+ WFIFOHEAD(inter_fd, sizeof(struct s_pet) + 8);
+ WFIFOW(inter_fd,0) = 0x3082;
+ WFIFOW(inter_fd,2) = sizeof(struct s_pet) + 8;
+ WFIFOL(inter_fd,4) = account_id;
+ memcpy(WFIFOP(inter_fd,8),p,sizeof(struct s_pet));
+ WFIFOSET(inter_fd,WFIFOW(inter_fd,2));
+
+ return 0;
+}
+
+int intif_delete_petdata(int pet_id)
+{
+ if (CheckForCharServer())
+ return 0;
+ WFIFOHEAD(inter_fd,6);
+ WFIFOW(inter_fd,0) = 0x3083;
+ WFIFOL(inter_fd,2) = pet_id;
+ WFIFOSET(inter_fd,6);
+
+ return 0;
+}
+
+// GMメッセージを送信
+int intif_GMmessage(char* mes,int len,int flag)
+{
+ int lp = (flag&0x10) ? 8 : 4;
+
+ // Send to the local players
+ clif_GMmessage(NULL, mes, len, flag);
+
+ if (CheckForCharServer())
+ return 0;
+
+ WFIFOHEAD(inter_fd,lp + len + 4);
+ WFIFOW(inter_fd,0) = 0x3000;
+ WFIFOW(inter_fd,2) = lp + len + 4;
+ WFIFOL(inter_fd,4) = 0xFF000000; //"invalid" color signals standard broadcast.
+ WFIFOL(inter_fd,8) = 0x65756c62;
+ memcpy(WFIFOP(inter_fd,4+lp), mes, len);
+ WFIFOSET(inter_fd, WFIFOW(inter_fd,2));
+ return 0;
+}
+
+int intif_announce(char* mes,int len, unsigned long color, int flag)
+{
+ // Send to the local players
+ if(color == 0xFE000000) // This is main chat message [LuzZza]
+ clif_MainChatMessage(mes);
+ else
+ clif_announce(NULL, mes, len, color, flag);
+
+ if (CheckForCharServer())
+ return 0;
+ WFIFOHEAD(inter_fd, 8 + len);
+ WFIFOW(inter_fd,0) = 0x3000;
+ WFIFOW(inter_fd,2) = 8 + len;
+ WFIFOL(inter_fd,4) = color;
+ memcpy(WFIFOP(inter_fd,8), mes, len);
+ WFIFOSET(inter_fd, WFIFOW(inter_fd,2));
+ return 0;
+}
+
+// The transmission of Wisp/Page to inter-server (player not found on this server)
+int intif_wis_message(struct map_session_data *sd, char *nick, char *mes, int mes_len) {
+ nullpo_retr(0, sd);
+ if (CheckForCharServer())
+ return 0;
+
+ WFIFOHEAD(inter_fd,mes_len + 52);
+ WFIFOW(inter_fd,0) = 0x3001;
+ WFIFOW(inter_fd,2) = mes_len + 52;
+ memcpy(WFIFOP(inter_fd,4), sd->status.name, NAME_LENGTH);
+ memcpy(WFIFOP(inter_fd,4+NAME_LENGTH), nick, NAME_LENGTH);
+ memcpy(WFIFOP(inter_fd,4+2*NAME_LENGTH), mes, mes_len);
+ WFIFOSET(inter_fd, WFIFOW(inter_fd,2));
+
+ if (battle_config.etc_log)
+ ShowInfo("intif_wis_message from %s to %s (message: '%s')\n", sd->status.name, nick, mes);
+
+ return 0;
+}
+
+// The reply of Wisp/page
+int intif_wis_replay(int id, int flag) {
+ if (CheckForCharServer())
+ return 0;
+ WFIFOHEAD(inter_fd,7);
+ WFIFOW(inter_fd,0) = 0x3002;
+ WFIFOL(inter_fd,2) = id;
+ WFIFOB(inter_fd,6) = flag; // flag: 0: success to send wisper, 1: target character is not loged in?, 2: ignored by target
+ WFIFOSET(inter_fd,7);
+
+ if (battle_config.etc_log)
+ ShowInfo("intif_wis_replay: id: %d, flag:%d\n", id, flag);
+
+ return 0;
+}
+
+// The transmission of GM only Wisp/Page from server to inter-server
+int intif_wis_message_to_gm(char *Wisp_name, int min_gm_level, char *mes) {
+ int mes_len;
+ if (CheckForCharServer())
+ return 0;
+ mes_len = strlen(mes) + 1; // + null
+ WFIFOHEAD(inter_fd, mes_len + 30);
+ WFIFOW(inter_fd,0) = 0x3003;
+ WFIFOW(inter_fd,2) = mes_len + 30;
+ memcpy(WFIFOP(inter_fd,4), Wisp_name, NAME_LENGTH);
+ WFIFOW(inter_fd,4+NAME_LENGTH) = (short)min_gm_level;
+ memcpy(WFIFOP(inter_fd,6+NAME_LENGTH), mes, mes_len);
+ WFIFOSET(inter_fd, WFIFOW(inter_fd,2));
+
+ if (battle_config.etc_log)
+ ShowNotice("intif_wis_message_to_gm: from: '%s', min level: %d, message: '%s'.\n", Wisp_name, min_gm_level, mes);
+
+ return 0;
+}
+
+int intif_regtostr(char* str, struct global_reg *reg, int qty) {
+ int len =0, i;
+
+ for (i = 0; i < qty; i++) {
+ len+= sprintf(str+len, "%s", reg[i].str)+1; //We add 1 to consider the '\0' in place.
+ len+= sprintf(str+len, "%s", reg[i].value)+1;
+ }
+ return len;
+}
+
+//Request for saving registry values.
+int intif_saveregistry(struct map_session_data *sd, int type)
+{
+ struct global_reg *reg;
+ int count;
+
+ if (CheckForCharServer())
+ return -1;
+
+ switch (type) {
+ case 3: //Character reg
+ reg = sd->save_reg.global;
+ count = sd->save_reg.global_num;
+ sd->state.reg_dirty &= ~0x4;
+ break;
+ case 2: //Account reg
+ reg = sd->save_reg.account;
+ count = sd->save_reg.account_num;
+ sd->state.reg_dirty &= ~0x2;
+ break;
+ case 1: //Account2 reg
+ reg = sd->save_reg.account2;
+ count = sd->save_reg.account2_num;
+ sd->state.reg_dirty &= ~0x1;
+ break;
+ default: //Broken code?
+ if (battle_config.error_log)
+ ShowError("intif_saveregistry: Invalid type %d\n", type);
+ return -1;
+ }
+ WFIFOHEAD(inter_fd, 288 * MAX_REG_NUM+13);
+ WFIFOW(inter_fd,0)=0x3004;
+ WFIFOL(inter_fd,4)=sd->status.account_id;
+ WFIFOL(inter_fd,8)=sd->status.char_id;
+ WFIFOB(inter_fd,12)=type;
+ if(count ==0){
+ WFIFOW(inter_fd,2)=13;
+ }else{
+ int i,p;
+ for (p=13,i = 0; i < count; i++) {
+ if (reg[i].str[0] && reg[i].value != 0) {
+ p+= sprintf(WFIFOP(inter_fd,p), "%s", reg[i].str)+1; //We add 1 to consider the '\0' in place.
+ p+= sprintf(WFIFOP(inter_fd,p), "%s", reg[i].value)+1;
+ }
+ }
+ WFIFOW(inter_fd,2)=p;
+ }
+ WFIFOSET(inter_fd,WFIFOW(inter_fd,2));
+ return 0;
+}
+
+//Request the registries for this player.
+int intif_request_registry(struct map_session_data *sd, int flag)
+{
+ nullpo_retr(0, sd);
+
+ sd->save_reg.account2_num = -1;
+ sd->save_reg.account_num = -1;
+ sd->save_reg.global_num = -1;
+
+ if (CheckForCharServer())
+ return 0;
+
+ WFIFOHEAD(inter_fd,6);
+ WFIFOW(inter_fd,0) = 0x3005;
+ WFIFOL(inter_fd,2) = sd->status.account_id;
+ WFIFOL(inter_fd,6) = sd->status.char_id;
+ WFIFOB(inter_fd,10) = (flag&1?1:0); //Request Acc Reg 2
+ WFIFOB(inter_fd,11) = (flag&2?1:0); //Request Acc Reg
+ WFIFOB(inter_fd,12) = (flag&4?1:0); //Request Char Reg
+ WFIFOSET(inter_fd,13);
+
+ return 0;
+}
+
+// 倉庫データ要求
+int intif_request_storage(int account_id)
+{
+ if (CheckForCharServer())
+ return 0;
+ WFIFOHEAD(inter_fd,6);
+ WFIFOW(inter_fd,0) = 0x3010;
+ WFIFOL(inter_fd,2) = account_id;
+ WFIFOSET(inter_fd,6);
+ return 0;
+}
+// 倉庫データ送信
+int intif_send_storage(struct storage *stor)
+{
+ if (CheckForCharServer())
+ return 0;
+ nullpo_retr(0, stor);
+ WFIFOHEAD(inter_fd,sizeof(struct storage)+8);
+ WFIFOW(inter_fd,0) = 0x3011;
+ WFIFOW(inter_fd,2) = sizeof(struct storage)+8;
+ WFIFOL(inter_fd,4) = stor->account_id;
+ memcpy( WFIFOP(inter_fd,8),stor, sizeof(struct storage) );
+ WFIFOSET(inter_fd,WFIFOW(inter_fd,2));
+ return 0;
+}
+
+int intif_request_guild_storage(int account_id,int guild_id)
+{
+ if (CheckForCharServer())
+ return 0;
+ WFIFOHEAD(inter_fd,10);
+ WFIFOW(inter_fd,0) = 0x3018;
+ WFIFOL(inter_fd,2) = account_id;
+ WFIFOL(inter_fd,6) = guild_id;
+ WFIFOSET(inter_fd,10);
+ return 0;
+}
+int intif_send_guild_storage(int account_id,struct guild_storage *gstor)
+{
+ if (CheckForCharServer())
+ return 0;
+ WFIFOHEAD(inter_fd,sizeof(struct guild_storage)+12);
+ WFIFOW(inter_fd,0) = 0x3019;
+ WFIFOW(inter_fd,2) = sizeof(struct guild_storage)+12;
+ WFIFOL(inter_fd,4) = account_id;
+ WFIFOL(inter_fd,8) = gstor->guild_id;
+ memcpy( WFIFOP(inter_fd,12),gstor, sizeof(struct guild_storage) );
+ WFIFOSET(inter_fd,WFIFOW(inter_fd,2));
+ return 0;
+}
+
+// パーティ作成要求
+int intif_create_party(struct map_session_data *sd,char *name,int item,int item2)
+{
+ if (CheckForCharServer())
+ return 0;
+ nullpo_retr(0, sd);
+
+ WFIFOHEAD(inter_fd,64);
+ WFIFOW(inter_fd,0) = 0x3020;
+ WFIFOL(inter_fd,2) = sd->status.account_id;
+ WFIFOL(inter_fd,6) = sd->status.char_id;
+ memcpy(WFIFOP(inter_fd,10),name, NAME_LENGTH);
+ memcpy(WFIFOP(inter_fd,34),sd->status.name,NAME_LENGTH);
+ WFIFOW(inter_fd,58) = sd->mapindex;
+ WFIFOW(inter_fd,60)= sd->status.base_level;
+ WFIFOB(inter_fd,62)= item;
+ WFIFOB(inter_fd,63)= item2;
+ WFIFOSET(inter_fd,64);
+ return 0;
+}
+// パーティ情報要求
+int intif_request_partyinfo(int party_id)
+{
+ if (CheckForCharServer())
+ return 0;
+ WFIFOHEAD(inter_fd,6);
+ WFIFOW(inter_fd,0) = 0x3021;
+ WFIFOL(inter_fd,2) = party_id;
+ WFIFOSET(inter_fd,6);
+// if(battle_config.etc_log)
+// printf("intif: request party info\n");
+ return 0;
+}
+// パーティ追加要求
+int intif_party_addmember(int party_id,struct map_session_data *sd)
+{
+ if (CheckForCharServer())
+ return 0;
+
+ WFIFOHEAD(inter_fd,42);
+ WFIFOW(inter_fd,0)=0x3022;
+ WFIFOL(inter_fd,2)=party_id;
+ WFIFOL(inter_fd,6)=sd->status.account_id;
+ WFIFOL(inter_fd,10)=sd->status.char_id;
+ memcpy(WFIFOP(inter_fd,14),sd->status.name,NAME_LENGTH);
+ WFIFOW(inter_fd,38) = sd->mapindex;
+ WFIFOW(inter_fd,40)=sd->status.base_level;
+ WFIFOSET(inter_fd,42);
+ return 1;
+}
+// パーティ設定変更
+int intif_party_changeoption(int party_id,int account_id,int exp,int flag)
+{
+ if (CheckForCharServer())
+ return 0;
+ WFIFOHEAD(inter_fd,14);
+ WFIFOW(inter_fd,0)=0x3023;
+ WFIFOL(inter_fd,2)=party_id;
+ WFIFOL(inter_fd,6)=account_id;
+ WFIFOW(inter_fd,10)=exp;
+ WFIFOW(inter_fd,12)=flag;
+ WFIFOSET(inter_fd,14);
+ return 0;
+}
+// パーティ脱退要求
+int intif_party_leave(int party_id,int account_id, int char_id)
+{
+ if (CheckForCharServer())
+ return 0;
+ WFIFOHEAD(inter_fd,14);
+ WFIFOW(inter_fd,0)=0x3024;
+ WFIFOL(inter_fd,2)=party_id;
+ WFIFOL(inter_fd,6)=account_id;
+ WFIFOL(inter_fd,10)=char_id;
+ WFIFOSET(inter_fd,14);
+ return 0;
+}
+// パーティ移動要求
+int intif_party_changemap(struct map_session_data *sd,int online)
+{
+ if (CheckForCharServer())
+ return 0;
+ if(!sd)
+ return 0;
+
+ WFIFOHEAD(inter_fd,19);
+ WFIFOW(inter_fd,0)=0x3025;
+ WFIFOL(inter_fd,2)=sd->status.party_id;
+ WFIFOL(inter_fd,6)=sd->status.account_id;
+ WFIFOL(inter_fd,10)=sd->status.char_id;
+ WFIFOW(inter_fd,14)=sd->mapindex;
+ WFIFOB(inter_fd,16)=online;
+ WFIFOW(inter_fd,17)=sd->status.base_level;
+ WFIFOSET(inter_fd,19);
+ return 1;
+}
+// パーティー解散要求
+int intif_break_party(int party_id)
+{
+ if (CheckForCharServer())
+ return 0;
+ WFIFOHEAD(inter_fd,6);
+ WFIFOW(inter_fd,0)=0x3026;
+ WFIFOL(inter_fd,2)=party_id;
+ WFIFOSET(inter_fd,6);
+ return 0;
+}
+// パーティ会話送信
+int intif_party_message(int party_id,int account_id,char *mes,int len)
+{
+ if (CheckForCharServer())
+ return 0;
+// if(battle_config.etc_log)
+// printf("intif_party_message: %s\n",mes);
+ WFIFOHEAD(inter_fd,len + 12);
+ WFIFOW(inter_fd,0)=0x3027;
+ WFIFOW(inter_fd,2)=len+12;
+ WFIFOL(inter_fd,4)=party_id;
+ WFIFOL(inter_fd,8)=account_id;
+ memcpy(WFIFOP(inter_fd,12),mes,len);
+ WFIFOSET(inter_fd,len+12);
+ return 0;
+}
+// パーティ競合チェック要求
+int intif_party_checkconflict(int party_id,int account_id,int char_id)
+{
+ if (CheckForCharServer())
+ return 0;
+ WFIFOHEAD(inter_fd,10 + NAME_LENGTH);
+ WFIFOW(inter_fd,0)=0x3028;
+ WFIFOL(inter_fd,2)=party_id;
+ WFIFOL(inter_fd,6)=account_id;
+ WFIFOL(inter_fd,10)=char_id;
+ WFIFOSET(inter_fd,14);
+ return 0;
+}
+
+int intif_party_leaderchange(int party_id,int account_id,int char_id)
+{
+ if (CheckForCharServer())
+ return 0;
+ WFIFOHEAD(inter_fd,14);
+ WFIFOW(inter_fd,0)=0x3029;
+ WFIFOL(inter_fd,2)=party_id;
+ WFIFOL(inter_fd,6)=account_id;
+ WFIFOL(inter_fd,10)=char_id;
+ WFIFOSET(inter_fd,14);
+ return 0;
+}
+
+
+// ギルド作成要求
+int intif_guild_create(const char *name,const struct guild_member *master)
+{
+ if (CheckForCharServer())
+ return 0;
+ nullpo_retr(0, master);
+
+ WFIFOHEAD(inter_fd,sizeof(struct guild_member)+(8+NAME_LENGTH));
+ WFIFOW(inter_fd,0)=0x3030;
+ WFIFOW(inter_fd,2)=sizeof(struct guild_member)+(8+NAME_LENGTH);
+ WFIFOL(inter_fd,4)=master->account_id;
+ memcpy(WFIFOP(inter_fd,8),name,NAME_LENGTH);
+ memcpy(WFIFOP(inter_fd,8+NAME_LENGTH),master,sizeof(struct guild_member));
+ WFIFOSET(inter_fd,WFIFOW(inter_fd,2));
+ return 0;
+}
+// ギルド情報要求
+int intif_guild_request_info(int guild_id)
+{
+ if (CheckForCharServer())
+ return 0;
+ WFIFOHEAD(inter_fd,6);
+ WFIFOW(inter_fd,0) = 0x3031;
+ WFIFOL(inter_fd,2) = guild_id;
+ WFIFOSET(inter_fd,6);
+ return 0;
+}
+// ギルドメンバ追加要求
+int intif_guild_addmember(int guild_id,struct guild_member *m)
+{
+ if (CheckForCharServer())
+ return 0;
+ WFIFOHEAD(inter_fd,sizeof(struct guild_member)+8);
+ WFIFOW(inter_fd,0) = 0x3032;
+ WFIFOW(inter_fd,2) = sizeof(struct guild_member)+8;
+ WFIFOL(inter_fd,4) = guild_id;
+ memcpy(WFIFOP(inter_fd,8),m,sizeof(struct guild_member));
+ WFIFOSET(inter_fd,WFIFOW(inter_fd,2));
+ return 0;
+}
+
+int intif_guild_change_gm(int guild_id, const char* name, int len)
+{
+ if (CheckForCharServer())
+ return 0;
+ WFIFOHEAD(inter_fd, len + 8);
+ WFIFOW(inter_fd, 0)=0x3033;
+ WFIFOW(inter_fd, 2)=len+8;
+ WFIFOL(inter_fd, 4)=guild_id;
+ memcpy(WFIFOP(inter_fd,8),name,len);
+ WFIFOSET(inter_fd,len+8);
+ return 0;
+}
+
+// ギルドメンバ脱退/追放要求
+int intif_guild_leave(int guild_id,int account_id,int char_id,int flag,const char *mes)
+{
+ if (CheckForCharServer())
+ return 0;
+ WFIFOHEAD(inter_fd, 55);
+ WFIFOW(inter_fd, 0) = 0x3034;
+ WFIFOL(inter_fd, 2) = guild_id;
+ WFIFOL(inter_fd, 6) = account_id;
+ WFIFOL(inter_fd,10) = char_id;
+ WFIFOB(inter_fd,14) = flag;
+ memcpy(WFIFOP(inter_fd,15),mes,40);
+ WFIFOSET(inter_fd,55);
+ return 0;
+}
+// ギルドメンバのオンライン状況/Lv更新要求
+int intif_guild_memberinfoshort(int guild_id,
+ int account_id,int char_id,int online,int lv,int class_)
+{
+ if (CheckForCharServer())
+ return 0;
+ WFIFOHEAD(inter_fd, 19);
+ WFIFOW(inter_fd, 0) = 0x3035;
+ WFIFOL(inter_fd, 2) = guild_id;
+ WFIFOL(inter_fd, 6) = account_id;
+ WFIFOL(inter_fd,10) = char_id;
+ WFIFOB(inter_fd,14) = online;
+ WFIFOW(inter_fd,15) = lv;
+ WFIFOW(inter_fd,17) = class_;
+ WFIFOSET(inter_fd,19);
+ return 0;
+}
+// ギルド解散通知
+int intif_guild_break(int guild_id)
+{
+ if (CheckForCharServer())
+ return 0;
+ WFIFOHEAD(inter_fd, 6);
+ WFIFOW(inter_fd, 0) = 0x3036;
+ WFIFOL(inter_fd, 2) = guild_id;
+ WFIFOSET(inter_fd,6);
+ return 0;
+}
+// ギルド会話送信
+int intif_guild_message(int guild_id,int account_id,char *mes,int len)
+{
+ if (CheckForCharServer())
+ return 0;
+ WFIFOHEAD(inter_fd, len + 12);
+ WFIFOW(inter_fd,0)=0x3037;
+ WFIFOW(inter_fd,2)=len+12;
+ WFIFOL(inter_fd,4)=guild_id;
+ WFIFOL(inter_fd,8)=account_id;
+ memcpy(WFIFOP(inter_fd,12),mes,len);
+ WFIFOSET(inter_fd,len+12);
+
+ return 0;
+}
+// ギルド競合チェック要求
+int intif_guild_checkconflict(int guild_id,int account_id,int char_id)
+{
+ if (CheckForCharServer())
+ return 0;
+ WFIFOHEAD(inter_fd, 14);
+ WFIFOW(inter_fd, 0)=0x3038;
+ WFIFOL(inter_fd, 2)=guild_id;
+ WFIFOL(inter_fd, 6)=account_id;
+ WFIFOL(inter_fd,10)=char_id;
+ WFIFOSET(inter_fd,14);
+ return 0;
+}
+// ギルド基本情報変更要求
+int intif_guild_change_basicinfo(int guild_id,int type,const void *data,int len)
+{
+ if (CheckForCharServer())
+ return 0;
+ WFIFOHEAD(inter_fd, len + 10);
+ WFIFOW(inter_fd,0)=0x3039;
+ WFIFOW(inter_fd,2)=len+10;
+ WFIFOL(inter_fd,4)=guild_id;
+ WFIFOW(inter_fd,8)=type;
+ memcpy(WFIFOP(inter_fd,10),data,len);
+ WFIFOSET(inter_fd,len+10);
+ return 0;
+}
+// ギルドメンバ情報変更要求
+int intif_guild_change_memberinfo(int guild_id,int account_id,int char_id,
+ int type,const void *data,int len)
+{
+ if (CheckForCharServer())
+ return 0;
+ WFIFOHEAD(inter_fd, len + 18);
+ WFIFOW(inter_fd, 0)=0x303a;
+ WFIFOW(inter_fd, 2)=len+18;
+ WFIFOL(inter_fd, 4)=guild_id;
+ WFIFOL(inter_fd, 8)=account_id;
+ WFIFOL(inter_fd,12)=char_id;
+ WFIFOW(inter_fd,16)=type;
+ memcpy(WFIFOP(inter_fd,18),data,len);
+ WFIFOSET(inter_fd,len+18);
+ return 0;
+}
+// ギルド役職変更要求
+int intif_guild_position(int guild_id,int idx,struct guild_position *p)
+{
+ if (CheckForCharServer())
+ return 0;
+ WFIFOHEAD(inter_fd, sizeof(struct guild_position)+12);
+ WFIFOW(inter_fd,0)=0x303b;
+ WFIFOW(inter_fd,2)=sizeof(struct guild_position)+12;
+ WFIFOL(inter_fd,4)=guild_id;
+ WFIFOL(inter_fd,8)=idx;
+ memcpy(WFIFOP(inter_fd,12),p,sizeof(struct guild_position));
+ WFIFOSET(inter_fd,WFIFOW(inter_fd,2));
+ return 0;
+}
+// ギルドスキルアップ要求
+int intif_guild_skillup(int guild_id,int skill_num,int account_id,int flag)
+{
+ if (CheckForCharServer())
+ return 0;
+ WFIFOHEAD(inter_fd,14);
+ WFIFOW(inter_fd, 0)=0x303c;
+ WFIFOL(inter_fd, 2)=guild_id;
+ WFIFOL(inter_fd, 6)=skill_num;
+ WFIFOL(inter_fd,10)=account_id;
+ //WFIFOL(inter_fd,14)=flag;
+ WFIFOSET(inter_fd,14);
+ return 0;
+}
+// ギルド同盟/敵対要求
+int intif_guild_alliance(int guild_id1,int guild_id2,int account_id1,int account_id2,int flag)
+{
+ if (CheckForCharServer())
+ return 0;
+ WFIFOHEAD(inter_fd,19);
+ WFIFOW(inter_fd, 0)=0x303d;
+ WFIFOL(inter_fd, 2)=guild_id1;
+ WFIFOL(inter_fd, 6)=guild_id2;
+ WFIFOL(inter_fd,10)=account_id1;
+ WFIFOL(inter_fd,14)=account_id2;
+ WFIFOB(inter_fd,18)=flag;
+ WFIFOSET(inter_fd,19);
+ return 0;
+}
+// ギルド告知変更要求
+int intif_guild_notice(int guild_id,const char *mes1,const char *mes2)
+{
+ if (CheckForCharServer())
+ return 0;
+ WFIFOHEAD(inter_fd,186);
+ WFIFOW(inter_fd,0)=0x303e;
+ WFIFOL(inter_fd,2)=guild_id;
+ memcpy(WFIFOP(inter_fd,6),mes1,60);
+ memcpy(WFIFOP(inter_fd,66),mes2,120);
+ WFIFOSET(inter_fd,186);
+ return 0;
+}
+// ギルドエンブレム変更要求
+int intif_guild_emblem(int guild_id,int len,const char *data)
+{
+ if (CheckForCharServer())
+ return 0;
+ if(guild_id<=0 || len<0 || len>2000)
+ return 0;
+ WFIFOHEAD(inter_fd,len + 12);
+ WFIFOW(inter_fd,0)=0x303f;
+ WFIFOW(inter_fd,2)=len+12;
+ WFIFOL(inter_fd,4)=guild_id;
+ WFIFOL(inter_fd,8)=0;
+ memcpy(WFIFOP(inter_fd,12),data,len);
+ WFIFOSET(inter_fd,len+12);
+ return 0;
+}
+//現在のギルド城占領ギルドを調べる
+int intif_guild_castle_dataload(int castle_id,int index)
+{
+ if (CheckForCharServer())
+ return 0;
+ WFIFOHEAD(inter_fd,5);
+ WFIFOW(inter_fd,0)=0x3040;
+ WFIFOW(inter_fd,2)=castle_id;
+ WFIFOB(inter_fd,4)=index;
+ WFIFOSET(inter_fd,5);
+ return 0;
+}
+
+//ギルド城占領ギルド変更要求
+int intif_guild_castle_datasave(int castle_id,int index, int value)
+{
+ if (CheckForCharServer())
+ return 0;
+ WFIFOHEAD(inter_fd,9);
+ WFIFOW(inter_fd,0)=0x3041;
+ WFIFOW(inter_fd,2)=castle_id;
+ WFIFOB(inter_fd,4)=index;
+ WFIFOL(inter_fd,5)=value;
+ WFIFOSET(inter_fd,9);
+ return 0;
+}
+
+//-----------------------------------------------------------------
+// Packets receive from inter server
+
+// Wisp/Page reception
+int intif_parse_WisMessage(int fd) { // rewritten by [Yor]
+ struct map_session_data* sd;
+ char *wisp_source;
+ char name[NAME_LENGTH];
+ int id, i;
+ RFIFOHEAD(fd);
+ id=RFIFOL(fd,4);
+ i=0; //,j=0;
+
+// if(battle_config.etc_log)
+// printf("intif_parse_wismessage: %d %s %s %s\n",id,RFIFOP(fd,6),RFIFOP(fd,30),RFIFOP(fd,54) );
+ memcpy(name, RFIFOP(fd,32), NAME_LENGTH);
+ name[NAME_LENGTH-1] = '\0'; //In case name arrived without it's terminator. [Skotlex]
+ sd=(struct map_session_data *) map_nick2sd(name); // 送信先を探す
+ if(sd!=NULL && strcmp(sd->status.name, name) == 0){
+ if(sd->ignoreAll == 1)
+ intif_wis_replay(RFIFOL(fd,4), 2); // 受信拒否
+ else {
+ wisp_source = (char *) RFIFOP(fd,8); // speed up [Yor]
+ for(i=0;i<MAX_IGNORE_LIST;i++){ //拒否リストに名前があるかどうか判定してあれば拒否
+ if(strcmp(sd->ignore[i].name, wisp_source)==0){
+ break;
+ }
+ }
+ if(i==MAX_IGNORE_LIST) // run out of list, so we are not ignored
+ {
+ clif_wis_message(sd->fd, wisp_source, (char*)RFIFOP(fd,56),RFIFOW(fd,2)-56);
+ intif_wis_replay(id,0); // 送信成功
+ }
+ else
+ intif_wis_replay(id, 2); // 受信拒否
+ }
+ }else
+ intif_wis_replay(id,1); // そんな人いません
+ return 0;
+}
+
+// Wisp/page transmission result reception
+int intif_parse_WisEnd(int fd) {
+ struct map_session_data* sd;
+ RFIFOHEAD(fd);
+
+ if (battle_config.etc_log)
+ ShowInfo("intif_parse_wisend: player: %s, flag: %d\n", RFIFOP(fd,2), RFIFOB(fd,26)); // flag: 0: success to send wisper, 1: target character is not loged in?, 2: ignored by target
+ sd = (struct map_session_data *)map_nick2sd((char *) RFIFOP(fd,2));
+ if (sd != NULL)
+ clif_wis_end(sd->fd, RFIFOB(fd,26));
+
+ return 0;
+}
+
+// Received wisp message from map-server via char-server for ALL gm
+int mapif_parse_WisToGM(int fd) { // 0x3003/0x3803 <packet_len>.w <wispname>.24B <min_gm_level>.w <message>.?B
+ int i, min_gm_level, mes_len;
+ struct map_session_data *pl_sd;
+ char Wisp_name[NAME_LENGTH];
+ char mbuf[255];
+ char *message;
+ RFIFOHEAD(fd);
+
+ mes_len = RFIFOW(fd,2) - 30;
+ message = (char *) (mes_len >= 255 ? (char *) aMallocA(mes_len) : mbuf);
+
+ min_gm_level = (int)RFIFOW(fd,28);
+ memcpy(Wisp_name, RFIFOP(fd,4), NAME_LENGTH);
+ Wisp_name[NAME_LENGTH-1] = '\0';
+ memcpy(message, RFIFOP(fd,30), mes_len);
+ message[mes_len-1] = '\0';
+ // information is sended to all online GM
+ for (i = 0; i < fd_max; i++)
+ if (session[i] && (pl_sd = (struct map_session_data *) session[i]->session_data) && pl_sd->state.auth)
+ if (pc_isGM(pl_sd) >= min_gm_level)
+ clif_wis_message(i, Wisp_name, message, strlen(message) + 1);
+
+ if (message != mbuf)
+ aFree(message);
+
+ return 0;
+}
+
+// アカウント変数通知
+int intif_parse_Registers(int fd) {
+ int j,p,len,max, flag;
+ struct map_session_data *sd;
+ struct global_reg *reg;
+ int *qty;
+ RFIFOHEAD(fd);
+
+ if( (sd=map_id2sd(RFIFOL(fd,4)))==NULL)
+ return 1;
+
+ if (RFIFOB(fd,12) == 3 && sd->status.char_id != RFIFOL(fd,8))
+ return 1; //Character registry from another character.
+
+ flag = (sd->save_reg.global_num == -1 || sd->save_reg.account_num == -1 || sd->save_reg.account2_num == -1);
+
+ switch (RFIFOB(fd,12)) {
+ case 3: //Character Registry
+ reg = sd->save_reg.global;
+ qty = &sd->save_reg.global_num;
+ max = GLOBAL_REG_NUM;
+ break;
+ case 2: //Account Registry
+ reg = sd->save_reg.account;
+ qty = &sd->save_reg.account_num;
+ max = ACCOUNT_REG_NUM;
+ break;
+ case 1: //Account2 Registry
+ reg = sd->save_reg.account2;
+ qty = &sd->save_reg.account2_num;
+ max = ACCOUNT_REG2_NUM;
+ break;
+ default:
+ if (battle_config.error_log)
+ ShowError("intif_parse_Registers: Unrecognized type %d\n",RFIFOB(fd,12));
+ return 0;
+ }
+ for(j=0,p=13;j<max && p<RFIFOW(fd,2);j++){
+ sscanf(RFIFOP(fd,p), "%31c%n", reg[j].str,&len);
+ reg[j].str[len]='\0';
+ p += len+1; //+1 to skip the '\0' between strings.
+ sscanf(RFIFOP(fd,p), "%255c%n", reg[j].value,&len);
+ reg[j].value[len]='\0';
+ p += len+1;
+ }
+ *qty = j;
+
+ if (flag && sd->save_reg.global_num > -1 && sd->save_reg.account_num > -1 && sd->save_reg.account2_num > -1)
+ pc_reg_received(sd); //Received all registry values, execute init scripts and what-not. [Skotlex]
+ return 1;
+}
+
+// 倉庫データ受信
+int intif_parse_LoadStorage(int fd) {
+ struct storage *stor;
+ struct map_session_data *sd;
+ RFIFOHEAD(fd);
+
+ stor = account2storage( RFIFOL(fd,4));
+
+ if (stor->storage_status == 1) { // Already open.. lets ignore this update
+ if (battle_config.error_log)
+ ShowWarning("intif_parse_LoadStorage: storage received for a client already open\n");
+ return 0;
+ }
+
+ if (RFIFOW(fd,2)-8 != sizeof(struct storage)) {
+ if (battle_config.error_log)
+ ShowError("intif_parse_LoadStorage: data size error %d %d\n", RFIFOW(fd,2)-8, sizeof(struct storage));
+ return 1;
+ }
+ sd=map_id2sd( RFIFOL(fd,4) );
+ if(sd==NULL){
+ if(battle_config.error_log)
+ ShowError("intif_parse_LoadStorage: user not found %d\n",RFIFOL(fd,4));
+ return 1;
+ }
+ if(battle_config.save_log)
+ ShowInfo("intif_openstorage: %d\n",RFIFOL(fd,4) );
+ memcpy(stor,RFIFOP(fd,8),sizeof(struct storage));
+ stor->dirty=0;
+ stor->storage_status=1;
+ sd->state.storage_flag = 1;
+ clif_storageitemlist(sd,stor);
+ clif_storageequiplist(sd,stor);
+ clif_updatestorageamount(sd,stor);
+
+ return 0;
+}
+
+// 倉庫データ送信成功
+int intif_parse_SaveStorage(int fd)
+{
+ RFIFOHEAD(fd);
+ if(battle_config.save_log)
+ ShowInfo("intif_savestorage: done %d %d\n",RFIFOL(fd,2),RFIFOB(fd,6) );
+ storage_storage_saved(RFIFOL(fd,2));
+ return 0;
+}
+
+int intif_parse_LoadGuildStorage(int fd)
+{
+ struct guild_storage *gstor;
+ struct map_session_data *sd;
+ int guild_id;
+ RFIFOHEAD(fd);
+ guild_id = RFIFOL(fd,8);
+ if(guild_id > 0) {
+ gstor=guild2storage(guild_id);
+ if(!gstor) {
+ if(battle_config.error_log)
+ ShowWarning("intif_parse_LoadGuildStorage: error guild_id %d not exist\n",guild_id);
+ return 1;
+ }
+ if( RFIFOW(fd,2)-12 != sizeof(struct guild_storage) ){
+ gstor->storage_status = 0;
+ if(battle_config.error_log)
+ ShowError("intif_parse_LoadGuildStorage: data size error %d %d\n",RFIFOW(fd,2)-12 , sizeof(struct guild_storage));
+ return 1;
+ }
+ sd=map_id2sd( RFIFOL(fd,4) );
+ if(sd==NULL){
+ if(battle_config.error_log)
+ ShowError("intif_parse_LoadGuildStorage: user not found %d\n",RFIFOL(fd,4));
+ return 1;
+ }
+ if(battle_config.save_log)
+ ShowInfo("intif_open_guild_storage: %d\n",RFIFOL(fd,4) );
+ memcpy(gstor,RFIFOP(fd,12),sizeof(struct guild_storage));
+ gstor->storage_status = 1;
+ sd->state.storage_flag = 2;
+ clif_guildstorageitemlist(sd,gstor);
+ clif_guildstorageequiplist(sd,gstor);
+ clif_updateguildstorageamount(sd,gstor);
+ }
+ return 0;
+}
+int intif_parse_SaveGuildStorage(int fd)
+{
+ RFIFOHEAD(fd);
+ if(battle_config.save_log) {
+ ShowInfo("intif_save_guild_storage: done %d %d %d\n",RFIFOL(fd,2),RFIFOL(fd,6),RFIFOB(fd,10) );
+ }
+ storage_guild_storagesaved(RFIFOL(fd,2), RFIFOL(fd,6));
+ return 0;
+}
+
+// パーティ作成可否
+int intif_parse_PartyCreated(int fd)
+{
+ RFIFOHEAD(fd);
+ if(battle_config.etc_log)
+ ShowInfo("intif: party created by account %d\n\n", RFIFOL(fd,2));
+ party_created(RFIFOL(fd,2), RFIFOL(fd,6),RFIFOB(fd,10),RFIFOL(fd,11), (char *)RFIFOP(fd,15));
+ return 0;
+}
+// パーティ情報
+int intif_parse_PartyInfo(int fd)
+{
+ RFIFOHEAD(fd);
+ if( RFIFOW(fd,2)==8){
+ if(battle_config.error_log)
+ ShowWarning("intif: party noinfo %d\n",RFIFOL(fd,4));
+ party_recv_noinfo(RFIFOL(fd,4));
+ return 0;
+ }
+
+// printf("intif: party info %d\n",RFIFOL(fd,4));
+ if( RFIFOW(fd,2)!=sizeof(struct party)+4 ){
+ if(battle_config.error_log)
+ ShowError("intif: party info : data size error %d %d %d\n",RFIFOL(fd,4),RFIFOW(fd,2),sizeof(struct party)+4);
+ }
+ party_recv_info((struct party *)RFIFOP(fd,4));
+ return 0;
+}
+// パーティ追加通知
+int intif_parse_PartyMemberAdded(int fd)
+{
+ RFIFOHEAD(fd);
+ if(battle_config.etc_log)
+ ShowInfo("intif: party member added Party (%d), Account(%d), Char(%d)\n",RFIFOL(fd,2),RFIFOL(fd,6),RFIFOL(fd,10));
+ party_member_added(RFIFOL(fd,2),RFIFOL(fd,6),RFIFOL(fd,10), RFIFOB(fd, 14));
+ return 0;
+}
+// パーティ設定変更通知
+int intif_parse_PartyOptionChanged(int fd)
+{
+ RFIFOHEAD(fd);
+ party_optionchanged(RFIFOL(fd,2),RFIFOL(fd,6),RFIFOW(fd,10),RFIFOW(fd,12),RFIFOB(fd,14));
+ return 0;
+}
+// パーティ脱退通知
+int intif_parse_PartyMemberLeaved(int fd)
+{
+ RFIFOHEAD(fd);
+ if(battle_config.etc_log)
+ ShowInfo("intif: party member leaved: Party(%d), Account(%d), Char(%d)\n",RFIFOL(fd,2),RFIFOL(fd,6),RFIFOL(fd,10));
+ party_member_leaved(RFIFOL(fd,2),RFIFOL(fd,6),RFIFOL(fd,10));
+ return 0;
+}
+// パーティ解散通知
+int intif_parse_PartyBroken(int fd)
+{
+ RFIFOHEAD(fd);
+ party_broken(RFIFOL(fd,2));
+ return 0;
+}
+// パーティ移動通知
+int intif_parse_PartyMove(int fd)
+{
+ RFIFOHEAD(fd);
+ party_recv_movemap(RFIFOL(fd,2),RFIFOL(fd,6),RFIFOL(fd,10),RFIFOW(fd,14),RFIFOB(fd,16),RFIFOW(fd,17));
+ return 0;
+}
+// パーティメッセージ
+int intif_parse_PartyMessage(int fd)
+{
+ RFIFOHEAD(fd);
+// if(battle_config.etc_log)
+// printf("intif_parse_PartyMessage: %s\n",RFIFOP(fd,12));
+ party_recv_message(RFIFOL(fd,4),RFIFOL(fd,8),(char *) RFIFOP(fd,12),RFIFOW(fd,2)-12);
+ return 0;
+}
+
+// ギルド作成可否
+int intif_parse_GuildCreated(int fd)
+{
+ RFIFOHEAD(fd);
+ guild_created(RFIFOL(fd,2),RFIFOL(fd,6));
+ return 0;
+}
+// ギルド情報
+int intif_parse_GuildInfo(int fd)
+{
+ RFIFOHEAD(fd);
+ if( RFIFOW(fd,2)==8){
+ if(battle_config.error_log)
+ ShowWarning("intif: guild noinfo %d\n",RFIFOL(fd,4));
+ guild_recv_noinfo(RFIFOL(fd,4));
+ return 0;
+ }
+
+// if(battle_config.etc_log)
+// printf("intif: guild info %d\n",RFIFOL(fd,4));
+ if( RFIFOW(fd,2)!=sizeof(struct guild)+4 ){
+ if(battle_config.error_log)
+ ShowError("intif: guild info : data size error Gid: %d recv size: %d Expected size: %d\n",RFIFOL(fd,4),RFIFOW(fd,2),sizeof(struct guild)+4);
+ }
+ guild_recv_info((struct guild *)RFIFOP(fd,4));
+ return 0;
+}
+// ギルドメンバ追加通知
+int intif_parse_GuildMemberAdded(int fd)
+{
+ RFIFOHEAD(fd);
+ if(battle_config.etc_log)
+ ShowInfo("intif: guild member added %d %d %d %d\n",RFIFOL(fd,2),RFIFOL(fd,6),RFIFOL(fd,10),RFIFOB(fd,14));
+ guild_member_added(RFIFOL(fd,2),RFIFOL(fd,6),RFIFOL(fd,10),RFIFOB(fd,14));
+ return 0;
+}
+// ギルドメンバ脱退/追放通知
+int intif_parse_GuildMemberLeaved(int fd)
+{
+ RFIFOHEAD(fd);
+ guild_member_leaved(RFIFOL(fd,2),RFIFOL(fd,6),RFIFOL(fd,10),RFIFOB(fd,14),
+ (char *) RFIFOP(fd,55), (char *) RFIFOP(fd,15));
+ return 0;
+}
+
+// ギルドメンバオンライン状態/Lv変更通知
+int intif_parse_GuildMemberInfoShort(int fd)
+{
+ RFIFOHEAD(fd);
+ guild_recv_memberinfoshort(RFIFOL(fd,2),RFIFOL(fd,6),RFIFOL(fd,10),RFIFOB(fd,14),RFIFOW(fd,15),RFIFOW(fd,17));
+ return 0;
+}
+// ギルド解散通知
+int intif_parse_GuildBroken(int fd)
+{
+ RFIFOHEAD(fd);
+ guild_broken(RFIFOL(fd,2),RFIFOB(fd,6));
+ return 0;
+}
+
+// ギルド基本情報変更通知
+int intif_parse_GuildBasicInfoChanged(int fd)
+{
+ int type, guild_id, dd;
+ void *data;
+ struct guild *g;
+ short dw;
+ RFIFOHEAD(fd);
+ type=RFIFOW(fd,8);
+ guild_id=RFIFOL(fd,4);
+ data=RFIFOP(fd,10);
+ g=guild_search(guild_id);
+ dw=*((short *)data);
+ dd=*((int *)data);
+ if( g==NULL )
+ return 0;
+ switch(type){
+ case GBI_EXP: g->exp=dd; break;
+ case GBI_GUILDLV: g->guild_lv=dw; break;
+ case GBI_SKILLPOINT: g->skill_point=dd; break;
+ }
+ return 0;
+}
+// ギルドメンバ情報変更通知
+int intif_parse_GuildMemberInfoChanged(int fd)
+{
+ int type, guild_id, account_id, char_id, idx, dd;
+ void* data;
+ struct guild *g;
+ RFIFOHEAD(fd);
+ type=RFIFOW(fd,16);
+ guild_id=RFIFOL(fd,4);
+ account_id=RFIFOL(fd,8);
+ char_id=RFIFOL(fd,12);
+ data=RFIFOP(fd,18);
+ g=guild_search(guild_id);
+ dd=*((int *)data);
+ if( g==NULL )
+ return 0;
+ idx=guild_getindex(g,account_id,char_id);
+ switch(type){
+ case GMI_POSITION:
+ g->member[idx].position=dd;
+ guild_memberposition_changed(g,idx,dd);
+ break;
+ case GMI_EXP:
+ g->member[idx].exp=dd;
+ break;
+ case GMI_HAIR:
+ g->member[idx].hair=dd;
+ break;
+ case GMI_HAIR_COLOR:
+ g->member[idx].hair_color=dd;
+ break;
+ case GMI_GENDER:
+ g->member[idx].gender=dd;
+ break;
+ case GMI_CLASS:
+ g->member[idx].class_=dd;
+ break;
+ case GMI_LEVEL:
+ g->member[idx].lv=dd;
+ break;
+ }
+ return 0;
+}
+
+// ギルド役職変更通知
+int intif_parse_GuildPosition(int fd)
+{
+ RFIFOHEAD(fd);
+ if( RFIFOW(fd,2)!=sizeof(struct guild_position)+12 ){
+ if(battle_config.error_log)
+ ShowError("intif: guild info : data size error\n %d %d %d",RFIFOL(fd,4),RFIFOW(fd,2),sizeof(struct guild_position)+12);
+ }
+ guild_position_changed(RFIFOL(fd,4),RFIFOL(fd,8),(struct guild_position *)RFIFOP(fd,12));
+ return 0;
+}
+// ギルドスキル割り振り通知
+int intif_parse_GuildSkillUp(int fd)
+{
+ RFIFOHEAD(fd);
+ guild_skillupack(RFIFOL(fd,2),RFIFOL(fd,6),RFIFOL(fd,10));
+ return 0;
+}
+// ギルド同盟/敵対通知
+int intif_parse_GuildAlliance(int fd)
+{
+ RFIFOHEAD(fd);
+ guild_allianceack(RFIFOL(fd,2),RFIFOL(fd,6),RFIFOL(fd,10),RFIFOL(fd,14),
+ RFIFOB(fd,18),(char *) RFIFOP(fd,19),(char *) RFIFOP(fd,43));
+ return 0;
+}
+// ギルド告知変更通知
+int intif_parse_GuildNotice(int fd)
+{
+ RFIFOHEAD(fd);
+ guild_notice_changed(RFIFOL(fd,2),(char *) RFIFOP(fd,6),(char *) RFIFOP(fd,66));
+ return 0;
+}
+// ギルドエンブレム変更通知
+int intif_parse_GuildEmblem(int fd)
+{
+ RFIFOHEAD(fd);
+ guild_emblem_changed(RFIFOW(fd,2)-12,RFIFOL(fd,4),RFIFOL(fd,8), (char *)RFIFOP(fd,12));
+ return 0;
+}
+// ギルド会話受信
+int intif_parse_GuildMessage(int fd)
+{
+ RFIFOHEAD(fd);
+ guild_recv_message(RFIFOL(fd,4),RFIFOL(fd,8),(char *) RFIFOP(fd,12),RFIFOW(fd,2)-12);
+ return 0;
+}
+// ギルド城データ要求返信
+int intif_parse_GuildCastleDataLoad(int fd)
+{
+ RFIFOHEAD(fd);
+ return guild_castledataloadack(RFIFOW(fd,2),RFIFOB(fd,4),RFIFOL(fd,5));
+}
+// ギルド城データ変更通知
+int intif_parse_GuildCastleDataSave(int fd)
+{
+ RFIFOHEAD(fd);
+ return guild_castledatasaveack(RFIFOW(fd,2),RFIFOB(fd,4),RFIFOL(fd,5));
+}
+
+// ギルド城データ一括受信(初期化時)
+int intif_parse_GuildCastleAllDataLoad(int fd)
+{
+ RFIFOHEAD(fd);
+ return guild_castlealldataload(RFIFOW(fd,2),(struct guild_castle *)RFIFOP(fd,4));
+}
+
+int intif_parse_GuildMasterChanged(int fd)
+{
+ RFIFOHEAD(fd);
+ return guild_gm_changed(RFIFOL(fd,2),RFIFOL(fd,6));
+}
+
+// pet
+int intif_parse_CreatePet(int fd)
+{
+ RFIFOHEAD(fd);
+ pet_get_egg(RFIFOL(fd,2),RFIFOL(fd,7),RFIFOB(fd,6));
+
+ return 0;
+}
+
+int intif_parse_RecvPetData(int fd)
+{
+ struct s_pet p;
+ int len;
+ RFIFOHEAD(fd);
+ len=RFIFOW(fd,2);
+ if(sizeof(struct s_pet)!=len-9) {
+ if(battle_config.etc_log)
+ ShowError("intif: pet data: data size error %d %d\n",sizeof(struct s_pet),len-9);
+ }
+ else{
+ memcpy(&p,RFIFOP(fd,9),sizeof(struct s_pet));
+ pet_recv_petdata(RFIFOL(fd,4),&p,RFIFOB(fd,8));
+ }
+
+ return 0;
+}
+int intif_parse_SavePetOk(int fd)
+{
+ RFIFOHEAD(fd);
+ if(RFIFOB(fd,6) == 1) {
+ if(battle_config.error_log)
+ ShowError("pet data save failure\n");
+ }
+
+ return 0;
+}
+
+int intif_parse_DeletePetOk(int fd)
+{
+ RFIFOHEAD(fd);
+ if(RFIFOB(fd,2) == 1) {
+ if(battle_config.error_log)
+ ShowError("pet data delete failure\n");
+ }
+
+ return 0;
+}
+//-----------------------------------------------------------------
+// inter serverからの通信
+// エラーがあれば0(false)を返すこと
+// パケットが処理できれば1,パケット長が足りなければ2を返すこと
+int intif_parse(int fd)
+{
+ int packet_len, cmd;
+ RFIFOHEAD(fd);
+ cmd = RFIFOW(fd,0);
+ // パケットのID確認
+ if(cmd<0x3800 || cmd>=0x3800+(sizeof(packet_len_table)/sizeof(packet_len_table[0])) ||
+ packet_len_table[cmd-0x3800]==0){
+ return 0;
+ }
+ // パケットの長さ確認
+ packet_len = packet_len_table[cmd-0x3800];
+ if(packet_len==-1){
+ if(RFIFOREST(fd)<4)
+ return 2;
+ packet_len = RFIFOW(fd,2);
+ }
+// if(battle_config.etc_log)
+// printf("intif_parse %d %x %d %d\n",fd,cmd,packet_len,RFIFOREST(fd));
+ if((int)RFIFOREST(fd)<packet_len){
+ return 2;
+ }
+ // 処理分岐
+ switch(cmd){
+ case 0x3800:
+ if (RFIFOL(fd,4) == 0xFF000000) //Normal announce.
+ clif_GMmessage(NULL,(char *) RFIFOP(fd,8),packet_len-8,0);
+ else if (RFIFOL(fd,4) == 0xFE000000) //Main chat message [LuzZza]
+ clif_MainChatMessage((char *)RFIFOP(fd,8));
+ else //Color announce.
+ clif_announce(NULL,(char *) RFIFOP(fd,8),packet_len-8,RFIFOL(fd,4),0);
+ break;
+ case 0x3801: intif_parse_WisMessage(fd); break;
+ case 0x3802: intif_parse_WisEnd(fd); break;
+ case 0x3803: mapif_parse_WisToGM(fd); break;
+ case 0x3804: intif_parse_Registers(fd); break;
+ case 0x3810: intif_parse_LoadStorage(fd); break;
+ case 0x3811: intif_parse_SaveStorage(fd); break;
+ case 0x3818: intif_parse_LoadGuildStorage(fd); break;
+ case 0x3819: intif_parse_SaveGuildStorage(fd); break;
+ case 0x3820: intif_parse_PartyCreated(fd); break;
+ case 0x3821: intif_parse_PartyInfo(fd); break;
+ case 0x3822: intif_parse_PartyMemberAdded(fd); break;
+ case 0x3823: intif_parse_PartyOptionChanged(fd); break;
+ case 0x3824: intif_parse_PartyMemberLeaved(fd); break;
+ case 0x3825: intif_parse_PartyMove(fd); break;
+ case 0x3826: intif_parse_PartyBroken(fd); break;
+ case 0x3827: intif_parse_PartyMessage(fd); break;
+ case 0x3830: intif_parse_GuildCreated(fd); break;
+ case 0x3831: intif_parse_GuildInfo(fd); break;
+ case 0x3832: intif_parse_GuildMemberAdded(fd); break;
+ case 0x3834: intif_parse_GuildMemberLeaved(fd); break;
+ case 0x3835: intif_parse_GuildMemberInfoShort(fd); break;
+ case 0x3836: intif_parse_GuildBroken(fd); break;
+ case 0x3837: intif_parse_GuildMessage(fd); break;
+ case 0x3839: intif_parse_GuildBasicInfoChanged(fd); break;
+ case 0x383a: intif_parse_GuildMemberInfoChanged(fd); break;
+ case 0x383b: intif_parse_GuildPosition(fd); break;
+ case 0x383c: intif_parse_GuildSkillUp(fd); break;
+ case 0x383d: intif_parse_GuildAlliance(fd); break;
+ case 0x383e: intif_parse_GuildNotice(fd); break;
+ case 0x383f: intif_parse_GuildEmblem(fd); break;
+ case 0x3840: intif_parse_GuildCastleDataLoad(fd); break;
+ case 0x3841: intif_parse_GuildCastleDataSave(fd); break;
+ case 0x3842: intif_parse_GuildCastleAllDataLoad(fd); break;
+ case 0x3843: intif_parse_GuildMasterChanged(fd); break;
+ case 0x3880: intif_parse_CreatePet(fd); break;
+ case 0x3881: intif_parse_RecvPetData(fd); break;
+ case 0x3882: intif_parse_SavePetOk(fd); break;
+ case 0x3883: intif_parse_DeletePetOk(fd); break;
+ default:
+ if(battle_config.error_log)
+ ShowError("intif_parse : unknown packet %d %x\n",fd,RFIFOW(fd,0));
+ return 0;
+ }
+ // パケット読み飛ばし
+ RFIFOSKIP(fd,packet_len);
+ return 1;
+}
diff --git a/src/map/intif.h b/src/map/intif.h
new file mode 100644
index 000000000..0d0fc563d
--- /dev/null
+++ b/src/map/intif.h
@@ -0,0 +1,61 @@
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#ifndef _INTIF_H_
+#define _INFIF_H_
+
+int intif_parse(int fd);
+
+int intif_GMmessage(char* mes,int len,int flag);
+int intif_announce(char* mes,int len, unsigned long color, int flag);
+
+int intif_wis_message(struct map_session_data *sd,char *nick,char *mes,int mes_len);
+int intif_wis_message_to_gm(char *Wisp_name, int min_gm_level, char *mes);
+
+int intif_saveregistry(struct map_session_data *sd, int type);
+int intif_request_registry(struct map_session_data *sd, int flag);
+
+int intif_request_storage(int account_id);
+int intif_send_storage(struct storage *stor);
+int intif_request_guild_storage(int account_id, int guild_id);
+int intif_send_guild_storage(int account_id, struct guild_storage *gstor);
+
+
+int intif_create_party(struct map_session_data *sd,char *name,int item,int item2);
+int intif_request_partyinfo(int party_id);
+int intif_party_addmember(int party_id, struct map_session_data *sd);
+int intif_party_changeoption(int party_id, int account_id, int exp, int item);
+int intif_party_leave(int party_id,int account_id, int char_id);
+int intif_party_changemap(struct map_session_data *sd, int online);
+int intif_break_party(int party_id);
+int intif_party_message(int party_id, int account_id, char *mes,int len);
+int intif_party_checkconflict(int party_id,int account_id,int char_id);
+int intif_party_leaderchange(int party_id,int account_id,int char_id);
+
+
+int intif_guild_create(const char *name, const struct guild_member *master);
+int intif_guild_request_info(int guild_id);
+int intif_guild_addmember(int guild_id, struct guild_member *m);
+int intif_guild_leave(int guild_id, int account_id, int char_id, int flag, const char *mes);
+int intif_guild_memberinfoshort(int guild_id, int account_id, int char_id, int online, int lv, int class_);
+int intif_guild_break(int guild_id);
+int intif_guild_message(int guild_id, int account_id, char *mes, int len);
+int intif_guild_checkconflict(int guild_id, int account_id, int char_id);
+int intif_guild_change_gm(int guild_id, const char* name, int len);
+int intif_guild_change_basicinfo(int guild_id, int type, const void *data, int len);
+int intif_guild_change_memberinfo(int guild_id, int account_id, int char_id, int type, const void *data, int len);
+int intif_guild_position(int guild_id, int idx, struct guild_position *p);
+int intif_guild_skillup(int guild_id, int skill_num, int account_id, int flag);
+int intif_guild_alliance(int guild_id1, int guild_id2, int account_id1, int account_id2, int flag);
+int intif_guild_notice(int guild_id, const char *mes1, const char *mes2);
+int intif_guild_emblem(int guild_id, int len, const char *data);
+int intif_guild_castle_dataload(int castle_id, int index);
+int intif_guild_castle_datasave(int castle_id, int index, int value);
+
+int intif_create_pet(int account_id, int char_id, short pet_type, short pet_lv, short pet_egg_id,
+ short pet_equip, short intimate, short hungry, char rename_flag, char incuvate, char *pet_name);
+int intif_request_petdata(int account_id, int char_id, int pet_id);
+int intif_save_petdata(int account_id, struct s_pet *p);
+int intif_delete_petdata(int pet_id);
+
+#endif
diff --git a/src/map/itemdb.c b/src/map/itemdb.c
new file mode 100644
index 000000000..667ab10af
--- /dev/null
+++ b/src/map/itemdb.c
@@ -0,0 +1,1103 @@
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "../common/nullpo.h"
+#include "../common/malloc.h"
+#include "../common/showmsg.h"
+#include "map.h"
+#include "grfio.h"
+#include "battle.h"
+#include "itemdb.h"
+#include "script.h"
+#include "pc.h"
+
+#define MAX_RANDITEM 2000
+#define MAX_ITEMGROUP 32
+// ** ITEMDB_OVERRIDE_NAME_VERBOSE **
+// 定義すると、itemdb.txtとgrfで名前が異なる場合、表示します.
+//#define ITEMDB_OVERRIDE_NAME_VERBOSE 1
+
+static struct dbt* item_db;
+
+static struct random_item_data blue_box[MAX_RANDITEM], violet_box[MAX_RANDITEM], card_album[MAX_RANDITEM], gift_box[MAX_RANDITEM], scroll[MAX_RANDITEM], finding_ore[MAX_RANDITEM];
+static int blue_box_count=0, violet_box_count=0, card_album_count=0, gift_box_count=0, scroll_count=0, finding_ore_count = 0;
+static int blue_box_default=0, violet_box_default=0, card_album_default=0, gift_box_default=0, scroll_default=0, finding_ore_default = 0;
+
+static struct item_group itemgroup_db[MAX_ITEMGROUP];
+
+/*==========================================
+ * 名前で検索用
+ *------------------------------------------
+ */
+// name = item alias, so we should find items aliases first. if not found then look for "jname" (full name)
+int itemdb_searchname_sub(DBKey key,void *data,va_list ap)
+{
+ struct item_data *item=(struct item_data *)data,**dst;
+ char *str;
+ str=va_arg(ap,char *);
+ dst=va_arg(ap,struct item_data **);
+ if( strcmpi(item->name,str)==0 ) //by lupus
+ *dst=item;
+ return 0;
+}
+
+/*==========================================
+ * 名前で検索用
+ *------------------------------------------
+ */
+int itemdb_searchjname_sub(int key,void *data,va_list ap)
+{
+ struct item_data *item=(struct item_data *)data,**dst;
+ char *str;
+ str=va_arg(ap,char *);
+ dst=va_arg(ap,struct item_data **);
+ if( strcmpi(item->jname,str)==0 )
+ *dst=item;
+ return 0;
+}
+/*==========================================
+ * 名前で検索
+ *------------------------------------------
+ */
+struct item_data* itemdb_searchname(const char *str)
+{
+ struct item_data *item=NULL;
+ item_db->foreach(item_db,itemdb_searchname_sub,str,&item);
+ return item;
+}
+
+/*==========================================
+ * 箱系アイテム検索
+ *------------------------------------------
+ */
+int itemdb_searchrandomid(int flags)
+{
+ int nameid=0,i,index,count;
+ struct random_item_data *list=NULL;
+
+ struct {
+ int nameid,count;
+ struct random_item_data *list;
+ } data[7];
+
+ // for BCC32 compile error
+ data[0].nameid = 0; data[0].count = 0; data[0].list = NULL;
+ data[1].nameid = blue_box_default; data[1].count = blue_box_count; data[1].list = blue_box;
+ data[2].nameid = violet_box_default; data[2].count = violet_box_count; data[2].list = violet_box;
+ data[3].nameid = card_album_default; data[3].count = card_album_count; data[3].list = card_album;
+ data[4].nameid = gift_box_default; data[4].count = gift_box_count; data[4].list = gift_box;
+ data[5].nameid = scroll_default; data[5].count = scroll_count; data[5].list = scroll;
+ data[6].nameid = finding_ore_default; data[6].count = finding_ore_count; data[6].list = finding_ore;
+
+ if(flags>=1 && flags<=6){
+ nameid=data[flags].nameid;
+ count=data[flags].count;
+ list=data[flags].list;
+
+ if(count > 0) {
+ for(i=0;i<1000;i++) {
+ index = rand()%count;
+ if( rand()%1000000 < list[index].per) {
+ nameid = list[index].nameid;
+ break;
+ }
+ }
+ }
+ }
+ return nameid;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int itemdb_group (int nameid)
+{
+ int i, j;
+ for (i=0; i < MAX_ITEMGROUP; i++) {
+ for (j=0; j < itemgroup_db[i].qty && itemgroup_db[i].id[j]; j++) {
+ if (itemgroup_db[i].id[j] == nameid)
+ return i;
+ }
+ }
+ return -1;
+}
+int itemdb_searchrandomgroup (int groupid)
+{
+ if (groupid < 0 || groupid >= MAX_ITEMGROUP ||
+ itemgroup_db[groupid].qty == 0 || itemgroup_db[groupid].id[0] == 0)
+ return 0;
+
+ return itemgroup_db[groupid].id[ rand()%itemgroup_db[groupid].qty ];
+}
+
+/*==========================================
+ * DBの存在確認
+ *------------------------------------------
+ */
+struct item_data* itemdb_exists(int nameid)
+{
+ return idb_get(item_db,nameid);
+}
+
+/*==========================================
+ * Converts the jobid from the format in itemdb
+ * to the format used by the map server. [Skotlex]
+ *------------------------------------------
+ */
+static void itemdb_jobid2mapid(unsigned short *bclass, int jobmask)
+{
+ int i;
+ bclass[0]= bclass[1]= bclass[2]= 0;
+ //Base classes
+ for (i = JOB_NOVICE; i <= JOB_THIEF; i++)
+ {
+ if (jobmask & 1<<i)
+ bclass[0] |= 1<<(MAPID_NOVICE+i);
+ }
+ //2-1 classes
+ if (jobmask & 1<<JOB_KNIGHT)
+ bclass[1] |= 1<<MAPID_SWORDMAN;
+ if (jobmask & 1<<JOB_PRIEST)
+ bclass[1] |= 1<<MAPID_ACOLYTE;
+ if (jobmask & 1<<JOB_WIZARD)
+ bclass[1] |= 1<<MAPID_MAGE;
+ if (jobmask & 1<<JOB_BLACKSMITH)
+ bclass[1] |= 1<<MAPID_MERCHANT;
+ if (jobmask & 1<<JOB_HUNTER)
+ bclass[1] |= 1<<MAPID_ARCHER;
+ if (jobmask & 1<<JOB_ASSASSIN)
+ bclass[1] |= 1<<MAPID_THIEF;
+ //2-2 classes
+ if (jobmask & 1<<JOB_CRUSADER)
+ bclass[2] |= 1<<MAPID_SWORDMAN;
+ if (jobmask & 1<<JOB_MONK)
+ bclass[2] |= 1<<MAPID_ACOLYTE;
+ if (jobmask & 1<<JOB_SAGE)
+ bclass[2] |= 1<<MAPID_MAGE;
+ if (jobmask & 1<<JOB_ALCHEMIST)
+ bclass[2] |= 1<<MAPID_MERCHANT;
+ if (jobmask & 1<<JOB_BARD)
+ bclass[2] |= 1<<MAPID_ARCHER;
+ if (jobmask & 1<<JOB_DANCER)
+ bclass[2] |= 1<<MAPID_ARCHER;
+ if (jobmask & 1<<JOB_ROGUE)
+ bclass[2] |= 1<<MAPID_THIEF;
+ //Special classes that don't fit above.
+ if (jobmask & 1<<JOB_SUPER_NOVICE)
+ bclass[1] |= 1<<MAPID_NOVICE;
+ if (jobmask & 1<<24) //Taekwon boy
+ bclass[0] |= 1<<MAPID_TAEKWON;
+ if (jobmask & 1<<25) //Star Gladiator
+ bclass[1] |= 1<<MAPID_TAEKWON;
+ if (jobmask & 1<<26) //Soul Linker
+ bclass[2] |= 1<<MAPID_TAEKWON;
+}
+
+static void* create_item_data(DBKey key, va_list args) {
+ struct item_data *id;
+ int nameid = key.i;
+
+ id=(struct item_data *)aCalloc(1,sizeof(struct item_data));
+ id->nameid=nameid;
+ id->value_buy=10;
+ id->value_sell=id->value_buy/2;
+ id->weight=10;
+ id->sex=2;
+ id->class_base[0]=0xff;
+ id->class_base[1]=0xff;
+ id->class_base[2]=0xff;
+ id->class_upper=5;
+
+ if(nameid>500 && nameid<600)
+ id->type=0; //heal item
+ else if(nameid>600 && nameid<700)
+ id->type=2; //use item
+ else if((nameid>700 && nameid<1100) ||
+ (nameid>7000 && nameid<8000))
+ id->type=3; //correction
+ else if(nameid>=1750 && nameid<1771)
+ id->type=10; //arrow
+ else if(nameid>1100 && nameid<2000)
+ id->type=4; //weapon
+ else if((nameid>2100 && nameid<3000) ||
+ (nameid>5000 && nameid<6000))
+ id->type=5; //armor
+ else if(nameid>4000 && nameid<5000)
+ id->type=6; //card
+ else if(nameid>9000 && nameid<10000)
+ id->type=7; //egg
+ else if(nameid>10000)
+ id->type=8; //petequip
+ return id;
+}
+/*==========================================
+ * DBの検索
+ *------------------------------------------
+ */
+struct item_data* itemdb_search(int nameid)
+{
+ return idb_ensure(item_db,nameid,create_item_data);
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int itemdb_isequip(int nameid)
+{
+ int type=itemdb_type(nameid);
+ if(type==0 || type==2 || type==3 || type==6 || type==10)
+ return 0;
+ return 1;
+}
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int itemdb_isequip2(struct item_data *data)
+{
+ if(data) {
+ int type=data->type;
+ if(type==0 || type==2 || type==3 || type==6 || type==10)
+ return 0;
+ else
+ return 1;
+ }
+ return 0;
+}
+
+/*==========================================
+ * Trade Restriction functions [Skotlex]
+ *------------------------------------------
+ */
+int itemdb_isdropable(int nameid, int gmlv)
+{
+ struct item_data* item = itemdb_exists(nameid);
+ return (item && (!(item->flag.trade_restriction&1) || gmlv >= item->gm_lv_trade_override));
+}
+
+int itemdb_cantrade(int nameid, int gmlv, int gmlv2)
+{
+ struct item_data* item = itemdb_exists(nameid);
+ return (item && (!(item->flag.trade_restriction&2) || gmlv >= item->gm_lv_trade_override || gmlv2 >= item->gm_lv_trade_override));
+}
+
+int itemdb_canpartnertrade(int nameid, int gmlv, int gmlv2)
+{
+ struct item_data* item = itemdb_exists(nameid);
+ return (item && (!(item->flag.trade_restriction&(2|4)) || gmlv >= item->gm_lv_trade_override || gmlv2 >= item->gm_lv_trade_override));
+}
+
+int itemdb_cansell(int nameid, int gmlv)
+{
+ struct item_data* item = itemdb_exists(nameid);
+ return (item && (!(item->flag.trade_restriction&8) || gmlv >= item->gm_lv_trade_override));
+}
+
+int itemdb_cancartstore(int nameid, int gmlv)
+{
+ struct item_data* item = itemdb_exists(nameid);
+ return (item && (!(item->flag.trade_restriction&16) || gmlv >= item->gm_lv_trade_override));
+}
+
+int itemdb_canstore(int nameid, int gmlv)
+{
+ struct item_data* item = itemdb_exists(nameid);
+ return (item && (!(item->flag.trade_restriction&32) || gmlv >= item->gm_lv_trade_override));
+}
+
+int itemdb_canguildstore(int nameid, int gmlv)
+{
+ struct item_data* item = itemdb_exists(nameid);
+ return (item && (!(item->flag.trade_restriction&64) || gmlv >= item->gm_lv_trade_override));
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int itemdb_isequip3(int nameid)
+{
+ int type=itemdb_type(nameid);
+ if(type==4 || type==5 || type == 8)
+ return 1;
+ return 0;
+}
+
+/*==========================================
+ * ランダムアイテム出現データの読み込み
+ *------------------------------------------
+ */
+static int itemdb_read_randomitem(void)
+{
+ FILE *fp;
+ char line[1024];
+ int ln=0;
+ int nameid,i,j;
+ char *str[10],*p;
+
+ const struct {
+ char filename[64];
+ struct random_item_data *pdata;
+ int *pcount,*pdefault;
+ } data[] = {
+ {"item_bluebox.txt", blue_box, &blue_box_count, &blue_box_default },
+ {"item_violetbox.txt", violet_box, &violet_box_count, &violet_box_default },
+ {"item_cardalbum.txt", card_album, &card_album_count, &card_album_default },
+ {"item_giftbox.txt", gift_box, &gift_box_count, &gift_box_default },
+ {"item_scroll.txt", scroll, &scroll_count, &scroll_default },
+ {"item_findingore.txt", finding_ore,&finding_ore_count, &finding_ore_default },
+ };
+
+ for(i=0;i<sizeof(data)/sizeof(data[0]);i++){
+ struct random_item_data *pd=data[i].pdata;
+ int *pc=data[i].pcount;
+ int *pdefault=data[i].pdefault;
+ char *fn=(char *) data[i].filename;
+
+ *pdefault = 0;
+ sprintf(line, "%s/%s", db_path, fn);
+ if( (fp=fopen(line,"r"))==NULL ){
+ ShowError("can't read %s\n",line);
+ continue;
+ }
+
+ while(fgets(line,1020,fp)){
+ if(line[0]=='/' && line[1]=='/')
+ continue;
+ memset(str,0,sizeof(str));
+ for(j=0,p=line;j<3 && p;j++){
+ str[j]=p;
+ p=strchr(p,',');
+ if(p) *p++=0;
+ }
+
+ if(str[0]==NULL)
+ continue;
+
+ nameid=atoi(str[0]);
+ if(nameid<0 || nameid>=20000)
+ continue;
+ if(nameid == 0) {
+ if(str[2])
+ *pdefault = atoi(str[2]);
+ continue;
+ }
+
+ if(str[2]){
+ pd[ *pc ].nameid = nameid;
+ pd[(*pc)++].per = atoi(str[2]);
+ }
+
+ if(ln >= MAX_RANDITEM)
+ break;
+ ln++;
+ }
+ fclose(fp);
+ if (*pc > 0) {
+ ShowStatus("Done reading '"CL_WHITE"%d"CL_RESET"' entries in '"CL_WHITE"%s"CL_RESET"'.\n",*pc,fn);
+ }
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * アイテム使用可能フラグのオーバーライド
+ *------------------------------------------
+ */
+static int itemdb_read_itemavail (void)
+{
+ FILE *fp;
+ int nameid, j, k, ln = 0;
+ char line[1024], *str[10], *p;
+ struct item_data *id;
+
+ sprintf(line, "%s/item_avail.txt", db_path);
+ if ((fp = fopen(line,"r")) == NULL) {
+ ShowError("can't read %s\n", line);
+ return -1;
+ }
+
+ while (fgets(line, sizeof(line) - 1, fp)) {
+ if (line[0] == '/' && line[1] == '/')
+ continue;
+ memset(str, 0, sizeof(str));
+ for (j = 0, p = line; j < 2 && p; j++) {
+ str[j] = p;
+ p = strchr(p, ',');
+ if(p) *p++ = 0;
+ }
+
+ if (j < 2 || str[0] == NULL ||
+ (nameid = atoi(str[0])) < 0 || nameid >= 20000 || !(id = itemdb_exists(nameid)))
+ continue;
+
+ k = atoi(str[1]);
+ if (k > 0) {
+ id->flag.available = 1;
+ id->view_id = k;
+ } else
+ id->flag.available = 0;
+ ln++;
+ }
+ fclose(fp);
+ ShowStatus("Done reading '"CL_WHITE"%d"CL_RESET"' entries in '"CL_WHITE"%s"CL_RESET"'.\n", ln, "item_avail.txt");
+
+ return 0;
+}
+
+/*==========================================
+ * read item group data
+ *------------------------------------------
+ */
+static int itemdb_read_itemgroup(void)
+{
+ FILE *fp;
+ char line[1024];
+ int ln=0;
+ int groupid,j,k;
+ char *str[31],*p;
+
+ sprintf(line, "%s/item_group_db.txt", db_path);
+ if( (fp=fopen(line,"r"))==NULL ){
+ ShowError("can't read %s\n", line);
+ return -1;
+ }
+
+ while(fgets(line,1020,fp)){
+ if(line[0]=='/' && line[1]=='/')
+ continue;
+ memset(str,0,sizeof(str));
+ for(j=0,p=line;j<31 && p;j++){
+ str[j]=p;
+ p=strchr(p,',');
+ if(p) *p++=0;
+ }
+ if(str[0]==NULL)
+ continue;
+
+ groupid = atoi(str[0]);
+ if (groupid < 0 || groupid >= MAX_ITEMGROUP)
+ continue;
+
+ for (j=1; j<=30; j++) {
+ if (!str[j])
+ break;
+ k=atoi(str[j]);
+ if (k < 0 || k >= 20000 || !itemdb_exists(k))
+ continue;
+ //printf ("%d[%d] = %d\n", groupid, j-1, k);
+ itemgroup_db[groupid].id[j-1] = k;
+ itemgroup_db[groupid].qty=j;
+ }
+ for (j=1; j<30; j++) { //Cleanup the contents. [Skotlex]
+ if (itemgroup_db[groupid].id[j-1] == 0 &&
+ itemgroup_db[groupid].id[j] != 0)
+ {
+ itemgroup_db[groupid].id[j-1] = itemgroup_db[groupid].id[j];
+ itemgroup_db[groupid].id[j] = 0;
+ itemgroup_db[groupid].qty = j;
+ }
+ }
+ ln++;
+ }
+ fclose(fp);
+ ShowStatus("Done reading '"CL_WHITE"%d"CL_RESET"' entries in '"CL_WHITE"%s"CL_RESET"'.\n",ln,"item_group_db.txt");
+ return 0;
+}
+
+/*==========================================
+ * アイテムの名前テーブルを読み込む
+ *------------------------------------------
+ */
+static int itemdb_read_itemnametable(void)
+{
+ char *buf,*p;
+ int s;
+
+ buf=(char *) grfio_reads("data\\idnum2itemdisplaynametable.txt",&s);
+
+ if(buf==NULL)
+ return -1;
+
+ buf[s]=0;
+ for(p=buf;p-buf<s;){
+ int nameid;
+ char buf2[64]; //Why 64? What's this for, other than holding an item's name? [Skotlex]
+
+ if( sscanf(p,"%d#%[^#]#",&nameid,buf2)==2 ){
+
+#ifdef ITEMDB_OVERRIDE_NAME_VERBOSE
+ if( itemdb_exists(nameid) &&
+ strncmp(itemdb_search(nameid)->jname,buf2,ITEM_NAME_LENGTH)!=0 ){
+ ShowNotice("[override] %d %s => %s\n",nameid
+ ,itemdb_search(nameid)->jname,buf2);
+ }
+#endif
+
+ memcpy(itemdb_search(nameid)->jname,buf2,ITEM_NAME_LENGTH-1);
+ }
+
+ p=strchr(p,10);
+ if(!p) break;
+ p++;
+ }
+ aFree(buf);
+ ShowStatus("Done reading '"CL_WHITE"%s"CL_RESET"'.\n","data\\idnum2itemdisplaynametable.txt");
+
+ return 0;
+}
+
+/*==========================================
+ * カードイラストのリソース名前テーブルを読み込む
+ *------------------------------------------
+ */
+static int itemdb_read_cardillustnametable(void)
+{
+ char *buf,*p;
+ int s;
+
+ buf=(char *) grfio_reads("data\\num2cardillustnametable.txt",&s);
+
+ if(buf==NULL)
+ return -1;
+
+ buf[s]=0;
+ for(p=buf;p-buf<s;){
+ int nameid;
+ char buf2[64];
+
+ if( sscanf(p,"%d#%[^#]#",&nameid,buf2)==2 ){
+ strcat(buf2,".bmp");
+ memcpy(itemdb_search(nameid)->cardillustname,buf2,64);
+ }
+
+ p=strchr(p,10);
+ if(!p) break;
+ p++;
+ }
+ aFree(buf);
+ ShowStatus("Done reading '"CL_WHITE"%s"CL_RESET"'.\n","data\\num2cardillustnametable.txt");
+
+ return 0;
+}
+
+//
+// 初期化
+//
+/*==========================================
+ *
+ *------------------------------------------
+ */
+static int itemdb_read_itemslottable(void)
+{
+ char *buf, *p;
+ int s;
+
+ buf = (char *)grfio_reads("data\\itemslottable.txt", &s);
+ if (buf == NULL)
+ return -1;
+ buf[s] = 0;
+ for (p = buf; p - buf < s; ) {
+ int nameid, equip;
+ struct item_data* item;
+ sscanf(p, "%d#%d#", &nameid, &equip);
+ item = itemdb_search(nameid);
+ if (item && itemdb_isequip2(item))
+ item->equip = equip;
+ p = strchr(p, 10);
+ if(!p) break;
+ p++;
+ p=strchr(p, 10);
+ if(!p) break;
+ p++;
+ }
+ aFree(buf);
+ ShowStatus("Done reading '"CL_WHITE"%s"CL_RESET"'.\n","data\\itemslottable.txt");
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+static int itemdb_read_itemslotcounttable(void)
+{
+ char *buf, *p;
+ int s;
+
+ buf = (char *)grfio_reads("data\\itemslotcounttable.txt", &s);
+ if (buf == NULL)
+ return -1;
+ buf[s] = 0;
+ for (p = buf; p - buf < s;){
+ int nameid, slot;
+ sscanf(p, "%d#%d#", &nameid, &slot);
+ if (slot > MAX_SLOTS)
+ {
+ ShowWarning("itemdb_read_itemslotcounttable: Item %d specifies %d slots, but the server only supports up to %d\n", nameid, slot, MAX_SLOTS);
+ slot = MAX_SLOTS;
+ }
+ itemdb_slot(nameid) = slot;
+ p = strchr(p,10);
+ if(!p) break;
+ p++;
+ p = strchr(p,10);
+ if(!p) break;
+ p++;
+ }
+ aFree(buf);
+ ShowStatus("Done reading '"CL_WHITE"%s"CL_RESET"'.\n", "data\\itemslotcounttable.txt");
+
+ return 0;
+}
+
+/*==========================================
+ * 装備制限ファイル読み出し
+ *------------------------------------------
+ */
+static int itemdb_read_noequip(void)
+{
+ FILE *fp;
+ char line[1024];
+ int ln=0;
+ int nameid,j;
+ char *str[32],*p;
+ struct item_data *id;
+
+ sprintf(line, "%s/item_noequip.txt", db_path);
+ if( (fp=fopen(line,"r"))==NULL ){
+ ShowError("can't read %s\n", line);
+ return -1;
+ }
+ while(fgets(line,1020,fp)){
+ if(line[0]=='/' && line[1]=='/')
+ continue;
+ memset(str,0,sizeof(str));
+ for(j=0,p=line;j<2 && p;j++){
+ str[j]=p;
+ p=strchr(p,',');
+ if(p) *p++=0;
+ }
+ if(str[0]==NULL)
+ continue;
+
+ nameid=atoi(str[0]);
+ if(nameid<=0 || nameid>=20000 || !(id=itemdb_exists(nameid)))
+ continue;
+
+ id->flag.no_equip=atoi(str[1]);
+
+ ln++;
+
+ }
+ fclose(fp);
+ if (ln > 0) {
+ ShowStatus("Done reading '"CL_WHITE"%d"CL_RESET"' entries in '"CL_WHITE"%s"CL_RESET"'.\n",ln,"item_noequip.txt");
+ }
+ return 0;
+}
+
+/*==========================================
+ * Reads item trade restrictions [Skotlex]
+ *------------------------------------------
+ */
+static int itemdb_read_itemtrade(void)
+{
+ FILE *fp;
+ int nameid, j, flag, gmlv, ln = 0;
+ char line[1024], *str[10], *p;
+ struct item_data *id;
+
+ sprintf(line, "%s/item_trade.txt", db_path);
+ if ((fp = fopen(line,"r")) == NULL) {
+ ShowError("can't read %s\n", line);
+ return -1;
+ }
+
+ while (fgets(line, sizeof(line) - 1, fp)) {
+ if (line[0] == '/' && line[1] == '/')
+ continue;
+ memset(str, 0, sizeof(str));
+ for (j = 0, p = line; j < 3 && p; j++) {
+ str[j] = p;
+ p = strchr(p, ',');
+ if(p) *p++ = 0;
+ }
+
+ if (j < 3 || str[0] == NULL ||
+ (nameid = atoi(str[0])) < 0 || nameid >= 20000 || !(id = itemdb_exists(nameid)))
+ continue;
+
+ flag = atoi(str[1]);
+ gmlv = atoi(str[2]);
+
+ if (flag > 0 && flag < 128 && gmlv > 0) { //Check range
+ id->flag.trade_restriction = flag;
+ id->gm_lv_trade_override = gmlv;
+ ln++;
+ }
+ }
+ fclose(fp);
+ ShowStatus("Done reading '"CL_WHITE"%d"CL_RESET"' entries in '"CL_WHITE"%s"CL_RESET"'.\n", ln, "item_trade.txt");
+
+ return 0;
+}
+
+/*======================================
+ * Applies gender restrictions according to settings. [Skotlex]
+ *======================================
+ */
+static int itemdb_gendercheck(struct item_data *id)
+{
+ if (id->nameid == WEDDING_RING_M) //Grom Ring
+ return 1;
+ if (id->nameid == WEDDING_RING_F) //Bride Ring
+ return 0;
+ if (id->look == 13 && id->type == 4) //Musical instruments are always male-only
+ return 1;
+ if (id->look == 14 && id->type == 4) //Whips are always female-only
+ return 0;
+
+ return (battle_config.ignore_items_gender?2:id->sex);
+}
+#ifndef TXT_ONLY
+
+/*======================================
+* SQL
+*===================================
+*/
+static int itemdb_read_sqldb(void)
+{
+ unsigned short nameid;
+ struct item_data *id;
+ char script[65535 + 2 + 1]; // Maximum length of MySQL TEXT type (65535) + 2 bytes for curly brackets + 1 byte for terminator
+ char *item_db_name[] = { item_db_db, item_db2_db };
+ long unsigned int ln = 0;
+ int i;
+
+ // ----------
+
+ for (i = 0; i < 2; i++) {
+ sprintf(tmp_sql, "SELECT * FROM `%s`", item_db_name[i]);
+
+ // Execute the query; if the query execution succeeded...
+ if (mysql_query(&mmysql_handle, tmp_sql) == 0) {
+ sql_res = mysql_store_result(&mmysql_handle);
+
+ // If the storage of the query result succeeded...
+ if (sql_res) {
+ // Parse each row in the query result into sql_row
+ while ((sql_row = mysql_fetch_row(sql_res)))
+ {
+ /* +----+--------------+---------------+------+-----------+------------+--------+--------+---------+-------+-------+------------+---------------+-----------------+--------------+-------------+------+------------+--------------+
+ | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 |
+ +----+--------------+---------------+------+-----------+------------+--------+--------+---------+-------+-------+------------+---------------+-----------------+--------------+-------------+------+------------+--------------+
+ | id | name_english | name_japanese | type | price_buy | price_sell | weight | attack | defence | range | slots | equip_jobs | equip_upper | equip_genders | equip_locations | weapon_level | equip_level | refineable | view | script |
+ +----+--------------+---------------+------+-----------+------------+--------+--------+---------+-------+-------+------------+---------------+-----------------+--------------+-------------+------+------------+--------------+ */
+
+ nameid = atoi(sql_row[0]);
+
+ // If the identifier is not within the valid range, process the next row
+ if (nameid == 0 || nameid >= 20000)
+ continue;
+
+ ln++;
+
+ // ----------
+ id = itemdb_search(nameid);
+
+ memcpy(id->name, sql_row[1], ITEM_NAME_LENGTH-1);
+ memcpy(id->jname, sql_row[2], ITEM_NAME_LENGTH-1);
+
+ id->type = atoi(sql_row[3]);
+ if (id->type == 11)
+ { //Items that are consumed upon target confirmation
+ //(yggdrasil leaf, spells & pet lures) [Skotlex]
+ id->type = 2;
+ id->flag.delay_consume=1;
+ }
+
+ // If price_buy is not NULL and price_sell is not NULL...
+ if ((sql_row[4] != NULL) && (sql_row[5] != NULL)) {
+ id->value_buy = atoi(sql_row[4]);
+ id->value_sell = atoi(sql_row[5]);
+ }
+ // If price_buy is not NULL and price_sell is NULL...
+ else if ((sql_row[4] != NULL) && (sql_row[5] == NULL)) {
+ id->value_buy = atoi(sql_row[4]);
+ id->value_sell = atoi(sql_row[4]) / 2;
+ }
+ // If price_buy is NULL and price_sell is not NULL...
+ else if ((sql_row[4] == NULL) && (sql_row[5] != NULL)) {
+ id->value_buy = atoi(sql_row[5]) * 2;
+ id->value_sell = atoi(sql_row[5]);
+ }
+ // If price_buy is NULL and price_sell is NULL...
+ if ((sql_row[4] == NULL) && (sql_row[5] == NULL)) {
+ id->value_buy = 0;
+ id->value_sell = 0;
+ }
+
+ id->weight = atoi(sql_row[6]);
+ id->atk = (sql_row[7] != NULL) ? atoi(sql_row[7]) : 0;
+ id->def = (sql_row[8] != NULL) ? atoi(sql_row[8]) : 0;
+ id->range = (sql_row[9] != NULL) ? atoi(sql_row[9]) : 0;
+ id->slot = (sql_row[10] != NULL) ? atoi(sql_row[10]) : 0;
+ if (id->slot > MAX_SLOTS)
+ {
+ ShowWarning("itemdb_read_sqldb: Item %d (%s) specifies %d slots, but the server only supports up to %d\n", nameid, id->jname, id->slot, MAX_SLOTS);
+ id->slot = MAX_SLOTS;
+ }
+ itemdb_jobid2mapid(id->class_base, (sql_row[11] != NULL) ? atoi(sql_row[11]) : 0);
+ id->class_upper= (sql_row[12] != NULL) ? atoi(sql_row[12]) : 0;
+ id->sex = (sql_row[13] != NULL) ? atoi(sql_row[13]) : 0;
+ id->equip = (sql_row[14] != NULL) ? atoi(sql_row[14]) : 0;
+ id->wlv = (sql_row[15] != NULL) ? atoi(sql_row[15]) : 0;
+ id->elv = (sql_row[16] != NULL) ? atoi(sql_row[16]) : 0;
+ id->flag.no_refine = (sql_row[17] == NULL || atoi(sql_row[17]) == 1)?0:1;
+ id->look = (sql_row[18] != NULL) ? atoi(sql_row[18]) : 0;
+ id->view_id = 0;
+ id->sex = itemdb_gendercheck(id); //Apply gender filtering.
+
+ // ----------
+
+ if (id->script)
+ aFree(id->script);
+ if (sql_row[19] != NULL) {
+ if (sql_row[19][0] == '{')
+ id->script = parse_script((unsigned char *) sql_row[19], 0);
+ else {
+ sprintf(script, "{%s}", sql_row[19]);
+ id->script = parse_script((unsigned char *) script, 0);
+ }
+ } else id->script = NULL;
+
+ // ----------
+
+ id->flag.available = 1;
+ id->flag.value_notdc = 0;
+ id->flag.value_notoc = 0;
+ }
+
+ ShowStatus("Done reading '"CL_WHITE"%lu"CL_RESET"' entries in '"CL_WHITE"%s"CL_RESET"'.\n", ln, item_db_name[i]);
+ ln = 0;
+ } else {
+ ShowSQL("DB error (%s) - %s\n",item_db_name[i], mysql_error(&mmysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+
+ // Free the query result
+ mysql_free_result(sql_res);
+ } else {
+ ShowSQL("DB error (%s) - %s\n",item_db_name[i], mysql_error(&mmysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ }
+
+ return 0;
+}
+#endif /* not TXT_ONLY */
+
+/*==========================================
+ * アイテムデータベースの読み込み
+ *------------------------------------------
+ */
+static int itemdb_readdb(void)
+{
+ FILE *fp;
+ char line[1024];
+ int ln=0,lines=0;
+ int nameid,j;
+ char *str[32],*p,*np;
+ struct item_data *id;
+ int i=0;
+ char *filename[]={ "item_db.txt","item_db2.txt" };
+
+ for(i=0;i<2;i++){
+ sprintf(line, "%s/%s", db_path, filename[i]);
+ fp=fopen(line,"r");
+ if(fp==NULL){
+ if(i>0)
+ continue;
+ ShowFatalError("can't read %s\n",line);
+ exit(1);
+ }
+
+ lines=0;
+ while(fgets(line,1020,fp)){
+ lines++;
+ if(line[0]=='/' && line[1]=='/')
+ continue;
+ memset(str,0,sizeof(str));
+ for(j=0,np=p=line;j<19 && p;j++){
+ str[j]=p;
+ p=strchr(p,',');
+ if(p){ *p++=0; np=p; }
+ }
+ if(str[0]==NULL)
+ continue;
+
+ nameid=atoi(str[0]);
+ if(nameid<=0 || nameid>=20000)
+ continue;
+ if (j < 19)
+ { //Crash-fix on broken item lines. [Skotlex]
+ ShowWarning("Reading %s: Insufficient fields for item with id %d, skipping.\n", filename[i], nameid);
+ continue;
+ }
+ ln++;
+
+ //ID,Name,Jname,Type,Price,Sell,Weight,ATK,DEF,Range,Slot,Job,Job Upper,Gender,Loc,wLV,eLV,refineable,View
+ id=itemdb_search(nameid);
+ memcpy(id->name, str[1], ITEM_NAME_LENGTH-1);
+ memcpy(id->jname, str[2], ITEM_NAME_LENGTH-1);
+ id->type=atoi(str[3]);
+ if (id->type == 11)
+ { //Items that are consumed upon target confirmation
+ //(yggdrasil leaf, spells & pet lures) [Skotlex]
+ id->type = 2;
+ id->flag.delay_consume=1;
+ }
+
+ {
+ int buy = atoi(str[4]), sell = atoi(str[5]);
+ // if buying price > selling price * 2 consider it valid and don't change it [celest]
+ if (buy && sell && buy > sell*2){
+ id->value_buy = buy;
+ id->value_sell = sell;
+ } else {
+ // buy≠sell*2 は item_value_db.txt で指定してください。
+ if (sell) { // sell値を優先とする
+ id->value_buy = sell*2;
+ id->value_sell = sell;
+ } else {
+ id->value_buy = buy;
+ id->value_sell = buy/2;
+ }
+ }
+ // check for bad prices that can possibly cause exploits
+ if (id->value_buy*75/100 < id->value_sell*124/100) {
+ ShowWarning ("Item %s [%d] buying:%d < selling:%d\n",
+ id->name, id->nameid, id->value_buy*75/100, id->value_sell*124/100);
+ }
+ }
+ id->weight=atoi(str[6]);
+ id->atk=atoi(str[7]);
+ id->def=atoi(str[8]);
+ id->range=atoi(str[9]);
+ id->slot=atoi(str[10]);
+ if (id->slot > MAX_SLOTS)
+ {
+ ShowWarning("itemdb_readdb: Item %d (%s) specifies %d slots, but the server only supports up to %d\n", nameid, id->jname, id->slot, MAX_SLOTS);
+ id->slot = MAX_SLOTS;
+ }
+ itemdb_jobid2mapid(id->class_base, atoi(str[11]));
+ id->class_upper = atoi(str[12]);
+ id->sex = atoi(str[13]);
+ if(id->equip != atoi(str[14])){
+ id->equip=atoi(str[14]);
+ }
+ id->wlv=atoi(str[15]);
+ id->elv=atoi(str[16]);
+ id->flag.no_refine = atoi(str[17])?0:1; //If the refine column is 1, no_refine is 0
+ id->look=atoi(str[18]);
+ id->flag.available=1;
+ id->flag.value_notdc=0;
+ id->flag.value_notoc=0;
+ id->view_id=0;
+ id->sex = itemdb_gendercheck(id); //Apply gender filtering.
+
+ if (id->script) {
+ aFree(id->script);
+ id->script=NULL;
+ }
+ if((p=strchr(np,'{'))==NULL)
+ continue;
+ id->script = parse_script((unsigned char *) p,lines);
+ }
+ fclose(fp);
+ if (ln > 0) {
+ ShowStatus("Done reading '"CL_WHITE"%d"CL_RESET"' entries in '"CL_WHITE"%s"CL_RESET"'.\n",ln,filename[i]);
+ }
+ ln=0; // reset to 0
+ }
+ return 0;
+}
+
+/*====================================
+ * Removed item_value_db, don't re-add
+ *------------------------------------
+ */
+static void itemdb_read(void)
+{
+#ifndef TXT_ONLY
+ if (db_use_sqldbs)
+ itemdb_read_sqldb();
+ else
+#endif
+ itemdb_readdb();
+
+ itemdb_read_itemgroup();
+ itemdb_read_randomitem();
+ itemdb_read_itemavail();
+ itemdb_read_noequip();
+ itemdb_read_itemtrade();
+ if (battle_config.cardillust_read_grffile)
+ itemdb_read_cardillustnametable();
+ if (battle_config.item_equip_override_grffile)
+ itemdb_read_itemslottable();
+ if (battle_config.item_slots_override_grffile)
+ itemdb_read_itemslotcounttable();
+ if (battle_config.item_name_override_grffile)
+ itemdb_read_itemnametable();
+}
+
+/*==========================================
+ * Initialize / Finalize
+ *------------------------------------------
+ */
+static int itemdb_final_sub (DBKey key,void *data,va_list ap)
+{
+ int flag;
+ struct item_data *id = (struct item_data *)data;
+
+ flag = va_arg(ap, int);
+ if (id->script)
+ {
+ aFree(id->script);
+ id->script = NULL;
+ }
+ // Whether to clear the item data
+ if (flag)
+ aFree(id);
+
+ return 0;
+}
+
+void itemdb_reload(void)
+{
+ // free up all item scripts first
+ item_db->foreach(item_db, itemdb_final_sub, 0);
+ itemdb_read();
+}
+
+void do_final_itemdb(void)
+{
+ item_db->destroy(item_db, itemdb_final_sub, 1);
+}
+
+int do_init_itemdb(void)
+{
+ item_db = db_alloc(__FILE__,__LINE__,DB_INT,DB_OPT_BASE,sizeof(int));
+ itemdb_read();
+
+ return 0;
+}
diff --git a/src/map/itemdb.h b/src/map/itemdb.h
new file mode 100644
index 000000000..0064d17b4
--- /dev/null
+++ b/src/map/itemdb.h
@@ -0,0 +1,106 @@
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#ifndef _ITEMDB_H_
+#define _ITEMDB_H_
+
+#include "map.h"
+
+struct item_data {
+ int nameid;
+ char name[ITEM_NAME_LENGTH],jname[ITEM_NAME_LENGTH];
+ char prefix[NAME_LENGTH],suffix[NAME_LENGTH];
+ char cardillustname[64];
+ int value_buy;
+ int value_sell;
+ int type;
+ int maxchance; //For logs, for external game info, for scripts: Max drop chance of this item (e.g. 0.01% , etc.. if it = 0, then monsters don't drop it) [Lupus]
+ int sex;
+ int equip;
+ int weight;
+ int atk;
+ int def;
+ int range;
+ int slot;
+ int look;
+ int elv;
+ int wlv;
+//Lupus: I rearranged order of these fields due to compatibility with ITEMINFO script command
+// some script commands should be revised as well...
+ unsigned short class_base[3]; //Specifies if the base can wear this item (split in 3 indexes per type: 1-1, 2-1, 2-2)
+ unsigned class_upper : 3; //Specifies if the upper-type can equip it (1: normal, 2: upper, 3: baby)
+ unsigned char *script; // 攻撃,防御の属性設定もこの中で可能かな?
+ struct {
+ unsigned available : 1;
+ unsigned value_notdc : 1;
+ unsigned value_notoc : 1;
+ unsigned no_equip : 3;
+ unsigned no_use : 1;
+ unsigned no_refine : 1; // [celest]
+ unsigned delay_consume : 1; // Signifies items that are not consumed inmediately upon double-click [Skotlex]
+ unsigned trade_restriction : 7; //Item restrictions mask [Skotlex]
+ } flag;
+ short gm_lv_trade_override; //GM-level to override trade_restriction
+ int view_id;
+};
+
+struct random_item_data {
+ int nameid;
+ int per;
+};
+
+struct item_group {
+ int qty; //Counts amount of items in the group.
+ int id[30]; // 120 bytes
+};
+
+struct item_data* itemdb_searchname(const char *name);
+struct item_data* itemdb_search(int nameid);
+struct item_data* itemdb_exists(int nameid);
+#define itemdb_type(n) itemdb_search(n)->type
+#define itemdb_atk(n) itemdb_search(n)->atk
+#define itemdb_def(n) itemdb_search(n)->def
+#define itemdb_look(n) itemdb_search(n)->look
+#define itemdb_weight(n) itemdb_search(n)->weight
+#define itemdb_equip(n) itemdb_search(n)->equip
+#define itemdb_usescript(n) itemdb_search(n)->script
+#define itemdb_equipscript(n) itemdb_search(n)->script
+#define itemdb_wlv(n) itemdb_search(n)->wlv
+#define itemdb_range(n) itemdb_search(n)->range
+#define itemdb_slot(n) itemdb_search(n)->slot
+#define itemdb_available(n) (itemdb_exists(n) && itemdb_search(n)->flag.available)
+#define itemdb_viewid(n) (itemdb_search(n)->view_id)
+int itemdb_group(int nameid);
+
+int itemdb_searchrandomid(int flags);
+int itemdb_searchrandomgroup(int groupid);
+
+#define itemdb_value_buy(n) itemdb_search(n)->value_buy
+#define itemdb_value_sell(n) itemdb_search(n)->value_sell
+#define itemdb_value_notdc(n) itemdb_search(n)->flag.value_notdc
+#define itemdb_value_notoc(n) itemdb_search(n)->flag.value_notoc
+#define itemdb_canrefine(n) itemdb_search(n)->flag.no_refine
+//Item trade restrictions [Skotlex]
+int itemdb_isdropable(int nameid, int gmlv);
+int itemdb_cantrade(int nameid, int gmlv, int gmlv2);
+int itemdb_cansell(int nameid, int gmlv);
+int itemdb_canstore(int nameid, int gmlv);
+int itemdb_canguildstore(int nameid, int gmlv);
+int itemdb_cancartstore(int nameid, int gmlv);
+int itemdb_canpartnertrade(int nameid, int gmlv, int gmlv2);
+
+int itemdb_isequip(int);
+int itemdb_isequip2(struct item_data *);
+int itemdb_isequip3(int);
+
+// itemdb_equipマクロとitemdb_equippointとの違いは
+// 前者が鯖側dbで定義された値そのものを返すのに対し
+// 後者はsessiondataを考慮した鞍側での装備可能場所
+// すべての組み合わせを返す
+
+void itemdb_reload(void);
+
+void do_final_itemdb(void);
+int do_init_itemdb(void);
+
+#endif
diff --git a/src/map/log.c b/src/map/log.c
new file mode 100644
index 000000000..0879023fa
--- /dev/null
+++ b/src/map/log.c
@@ -0,0 +1,956 @@
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+// Logging functions by Azndragon & Codemaster
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "../common/strlib.h"
+#include "../common/nullpo.h"
+#include "../common/showmsg.h"
+#include "itemdb.h"
+#include "map.h"
+#include "log.h"
+
+#ifndef SQL_DEBUG
+
+#define mysql_query(_x, _y) mysql_real_query(_x, _y, strlen(_y)) //supports ' in names and runs faster [Kevin]
+
+#else
+
+#define mysql_query(_x, _y) debug_mysql_query(__FILE__, __LINE__, _x, _y)
+
+#endif
+
+struct Log_Config log_config;
+
+char timestring[255];
+time_t curtime;
+
+//FILTER OPTIONS
+//0 = Don't log
+//1 = Log any item
+//Bits: ||
+//2 - Healing items (0)
+//3 - Etc Items(3) + Arrows (10)
+//4 - Usable Items(2) + Scrolls,Lures(11)
+//5 - Weapon(4)
+//6 - Shields,Armor,Headgears,Accessories,etc(5)
+//7 - Cards(6)
+//8 - Pet Accessories(8) + Eggs(7) (well, monsters don't drop 'em but we'll use the same system for ALL logs)
+//9 - Log expensive items ( >= price_log)
+//10 - Log big amount of items ( >= amount_log)
+//11 - Log refined items (if their refine >= refine_log )
+//12 - Log rare items (if their drop chance <= rare_log )
+
+//check if this item should be logged according the settings
+int should_log_item(int filter, int nameid, int amount) {
+ struct item_data *item_data;
+ if (nameid<501 || (item_data= itemdb_search(nameid)) == NULL) return 0;
+ if ( (filter&1) || // Filter = 1, we log any item
+ (filter&2 && item_data->type == 0 ) || //healing items
+ (filter&4 && (item_data->type == 3 || item_data->type == 10) ) || //etc+arrows
+ (filter&8 && (item_data->type == 2 || item_data->type == 11) ) || //usable
+ (filter&16 && item_data->type == 4 ) || //weapon
+ (filter&32 && item_data->type == 5 ) || //armor
+ (filter&64 && item_data->type == 6 ) || //cards
+ (filter&128 && (item_data->type == 7 || item_data->type == 8) ) || //eggs+pet access
+ (filter&256 && item_data->value_buy >= log_config.price_items_log ) || //expensive items
+ (filter&512 && amount >= log_config.amount_items_log ) || //big amount of items
+ (filter&2048 && ((item_data->maxchance <= log_config.rare_items_log) || item_data->nameid == 714) ) //Rare items or Emperium
+ ) return item_data->nameid;
+
+ return 0;
+}
+
+int log_branch(struct map_session_data *sd)
+{
+#ifndef TXT_ONLY
+ char t_name[NAME_LENGTH*2];
+#endif
+ FILE *logfp;
+
+ if(log_config.enable_logs <= 0)
+ return 0;
+ nullpo_retr(0, sd);
+#ifndef TXT_ONLY
+ if(log_config.sql_logs > 0)
+ {
+ sprintf(tmp_sql, "INSERT DELAYED INTO `%s` (`branch_date`, `account_id`, `char_id`, `char_name`, `map`) VALUES (NOW(), '%d', '%d', '%s', '%s')",
+ log_config.log_branch_db, sd->status.account_id, sd->status.char_id, jstrescapecpy(t_name, sd->status.name), mapindex_id2name(sd->mapindex));
+ if(mysql_query(&logmysql_handle, tmp_sql))
+ {
+ ShowSQL("DB error - %s\n",mysql_error(&logmysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ } else {
+#endif
+ if((logfp=fopen(log_config.log_branch,"a+")) != NULL) {
+ time(&curtime);
+ strftime(timestring, 254, "%m/%d/%Y %H:%M:%S", localtime(&curtime));
+ fprintf(logfp,"%s - %s[%d:%d]\t%s%s", timestring, sd->status.name, sd->status.account_id, sd->status.char_id, mapindex_id2name(sd->mapindex), RETCODE);
+ fclose(logfp);
+ }
+#ifndef TXT_ONLY
+ }
+#endif
+ return 0;
+}
+
+
+int log_pick(struct map_session_data *sd, char *type, int mob_id, int nameid, int amount, struct item *itm)
+{
+ FILE *logfp;
+ char *mapname;
+ int obj_id;
+
+ if(log_config.enable_logs <= 0)
+ return 0;
+ nullpo_retr(0, sd);
+ //Should we log this item? [Lupus]
+ if (!should_log_item(log_config.pick,nameid, amount))
+ return 0; //we skip logging this items set - they doesn't met our logging conditions [Lupus]
+
+ //either PLAYER or MOB (here we get map name and objects ID)
+ if(mob_id) {
+ struct mob_data *md = (struct mob_data*)sd;
+ obj_id = mob_id;
+ mapname = map[md->m].name;
+ } else {
+ obj_id = sd->char_id;
+ mapname = (char*)mapindex_id2name(sd->mapindex);
+ }
+ if(mapname==NULL)
+ mapname="";
+
+#ifndef TXT_ONLY
+ if(log_config.sql_logs > 0)
+ {
+ if (itm==NULL) {
+ //We log common item
+ sprintf(tmp_sql, "INSERT DELAYED INTO `%s` (`time`, `char_id`, `type`, `nameid`, `amount`, `map`) VALUES (NOW(), '%d', '%s', '%d', '%d', '%s')",
+ log_config.log_pick_db, obj_id, type, nameid, amount, mapname);
+ } else {
+ //We log Extended item
+ sprintf(tmp_sql, "INSERT DELAYED INTO `%s` (`time`, `char_id`, `type`, `nameid`, `amount`, `refine`, `card0`, `card1`, `card2`, `card3`, `map`) VALUES (NOW(), '%d', '%s', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%s')",
+ log_config.log_pick_db, obj_id, type, itm->nameid, amount, itm->refine, itm->card[0], itm->card[1], itm->card[2], itm->card[3], mapname);
+ }
+
+ if(mysql_query(&logmysql_handle, tmp_sql))
+ {
+ ShowSQL("DB error - %s\n",mysql_error(&logmysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ } else {
+#endif
+ if((logfp=fopen(log_config.log_pick,"a+")) != NULL) {
+ time_t curtime;
+ time(&curtime);
+ strftime(timestring, 254, "%m/%d/%Y %H:%M:%S", localtime(&curtime));
+
+ if (itm==NULL) {
+ //We log common item
+ fprintf(logfp,"%s - %d\t%s\t%d,%d,%s%s",
+ timestring, obj_id, type, nameid, amount, mapname, RETCODE);
+
+ } else {
+ //We log Extended item
+ fprintf(logfp,"%s - %d\t%s\t%d,%d,%d,%d,%d,%d,%d,%s%s",
+ timestring, obj_id, type, itm->nameid, amount, itm->refine, itm->card[0], itm->card[1], itm->card[2], itm->card[3], mapname, RETCODE);
+ }
+ fclose(logfp);
+ }
+#ifndef TXT_ONLY
+ }
+#endif
+ return 1; //Logged
+}
+
+int log_zeny(struct map_session_data *sd, char *type, struct map_session_data *src_sd, int amount)
+{
+// FILE *logfp;
+ if(log_config.enable_logs <= 0 || (log_config.zeny!=1 && abs(amount)<log_config.zeny))
+ return 0;
+
+ nullpo_retr(0, sd);
+#ifndef TXT_ONLY
+ if(log_config.sql_logs > 0)
+ {
+ sprintf(tmp_sql, "INSERT DELAYED INTO `%s` (`time`, `char_id`, `src_id`, `type`, `amount`, `map`) VALUES (NOW(), '%d', '%d', '%s', '%d', '%s')",
+ log_config.log_zeny_db, sd->char_id, src_sd->char_id, type, amount, mapindex_id2name(sd->mapindex));
+ if(mysql_query(&logmysql_handle, tmp_sql))
+ {
+ ShowSQL("DB error - %s\n",mysql_error(&logmysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ } else {
+#endif
+// if((logfp=fopen(log_config.log_zeny,"a+")) != NULL) {
+// time(&curtime);
+// strftime(timestring, 254, "%m/%d/%Y %H:%M:%S", localtime(&curtime));
+// fprintf(logfp,"%s - %s[%d]\t%s[%d]\t%d\t%s", timestring, sd->status.name, sd->status.account_id, target_sd->status.name, target_sd->status.account_id, sd->deal.zeny, RETCODE);
+// fclose(logfp);
+// }
+#ifndef TXT_ONLY
+ }
+#endif
+ return 0;
+}
+
+
+int log_drop(struct map_session_data *sd, int monster_id, int *log_drop)
+{
+ FILE *logfp;
+ int i,flag = 0;
+
+ if(log_config.enable_logs <= 0)
+ return 0;
+ nullpo_retr(0, sd);
+ for (i = 0; i<10; i++) { //Should we log these items? [Lupus]
+ flag += should_log_item(log_config.drop,log_drop[i],1);
+ }
+ if (flag==0) return 0; //we skip logging this items set - they doesn't met our logging conditions [Lupus]
+
+#ifndef TXT_ONLY
+ if(log_config.sql_logs > 0)
+ {
+ sprintf(tmp_sql, "INSERT DELAYED INTO `%s` (`drop_date`, `kill_char_id`, `monster_id`, `item1`, `item2`, `item3`, `item4`, `item5`, `item6`, `item7`, `item8`, `item9`, `itemCard`, `map`) VALUES (NOW(), '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%s') ", log_config.log_drop_db, sd->status.char_id, monster_id, log_drop[0], log_drop[1], log_drop[2], log_drop[3], log_drop[4], log_drop[5], log_drop[6], log_drop[7], log_drop[8], log_drop[9], mapindex_id2name(sd->mapindex));
+ if(mysql_query(&logmysql_handle, tmp_sql))
+ {
+ ShowSQL("DB error - %s\n",mysql_error(&logmysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ } else {
+#endif
+ if((logfp=fopen(log_config.log_drop,"a+")) != NULL) {
+
+
+ time_t curtime;
+ time(&curtime);
+ strftime(timestring, 254, "%m/%d/%Y %H:%M:%S", localtime(&curtime));
+ fprintf(logfp,"%s - %s[%d:%d]\t%d\t%d,%d,%d,%d,%d,%d,%d,%d,%d,%d%s", timestring, sd->status.name, sd->status.account_id, sd->status.char_id, monster_id, log_drop[0], log_drop[1], log_drop[2], log_drop[3], log_drop[4], log_drop[5], log_drop[6], log_drop[7], log_drop[8], log_drop[9], RETCODE);
+ fclose(logfp);
+ }
+#ifndef TXT_ONLY
+ }
+#endif
+ return 1; //Logged
+}
+
+int log_mvpdrop(struct map_session_data *sd, int monster_id, int *log_mvp)
+{
+ FILE *logfp;
+
+ if(log_config.enable_logs <= 0)
+ return 0;
+ nullpo_retr(0, sd);
+#ifndef TXT_ONLY
+ if(log_config.sql_logs > 0)
+ {
+ sprintf(tmp_sql, "INSERT DELAYED INTO `%s` (`mvp_date`, `kill_char_id`, `monster_id`, `prize`, `mvpexp`, `map`) VALUES (NOW(), '%d', '%d', '%d', '%d', '%s') ", log_config.log_mvpdrop_db, sd->status.char_id, monster_id, log_mvp[0], log_mvp[1], mapindex_id2name(sd->mapindex));
+ if(mysql_query(&logmysql_handle, tmp_sql))
+ {
+ ShowSQL("DB error - %s\n",mysql_error(&logmysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ } else {
+#endif
+ if((logfp=fopen(log_config.log_mvpdrop,"a+")) != NULL) {
+ time(&curtime);
+ strftime(timestring, 254, "%m/%d/%Y %H:%M:%S", localtime(&curtime));
+ fprintf(logfp,"%s - %s[%d:%d]\t%d\t%d,%d%s", timestring, sd->status.name, sd->status.account_id, sd->status.char_id, monster_id, log_mvp[0], log_mvp[1], RETCODE);
+ fclose(logfp);
+ }
+#ifndef TXT_ONLY
+ }
+#endif
+ return 0;
+}
+
+int log_present(struct map_session_data *sd, int source_type, int nameid)
+{
+ FILE *logfp;
+#ifndef TXT_ONLY
+ char t_name[NAME_LENGTH*2];
+#endif
+
+ if(log_config.enable_logs <= 0)
+ return 0;
+ nullpo_retr(0, sd);
+ if(!should_log_item(log_config.present,nameid,1)) return 0; //filter [Lupus]
+#ifndef TXT_ONLY
+ if(log_config.sql_logs > 0)
+ {
+ sprintf(tmp_sql, "INSERT DELAYED INTO `%s` (`present_date`, `src_id`, `account_id`, `char_id`, `char_name`, `nameid`, `map`) VALUES (NOW(), '%d', '%d', '%d', '%s', '%d', '%s') ",
+ log_config.log_present_db, source_type, sd->status.account_id, sd->status.char_id, jstrescapecpy(t_name, sd->status.name), nameid, mapindex_id2name(sd->mapindex));
+ if(mysql_query(&logmysql_handle, tmp_sql))
+ {
+ ShowSQL("DB error - %s\n",mysql_error(&logmysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ } else {
+#endif
+ if((logfp=fopen(log_config.log_present,"a+")) != NULL) {
+ time(&curtime);
+ strftime(timestring, 254, "%m/%d/%Y %H:%M:%S", localtime(&curtime));
+ fprintf(logfp,"%s - %s[%d:%d]\t%d\t%d%s", timestring, sd->status.name, sd->status.account_id, sd->status.char_id, source_type, nameid, RETCODE);
+ fclose(logfp);
+ }
+#ifndef TXT_ONLY
+ }
+#endif
+ return 0;
+}
+
+int log_produce(struct map_session_data *sd, int nameid, int slot1, int slot2, int slot3, int success)
+{
+ FILE *logfp;
+#ifndef TXT_ONLY
+ char t_name[NAME_LENGTH*2];
+#endif
+
+ if(log_config.enable_logs <= 0)
+ return 0;
+ nullpo_retr(0, sd);
+ if(!should_log_item(log_config.produce,nameid,1)) return 0; //filter [Lupus]
+#ifndef TXT_ONLY
+ if(log_config.sql_logs > 0)
+ {
+ sprintf(tmp_sql, "INSERT DELAYED INTO `%s` (`produce_date`, `account_id`, `char_id`, `char_name`, `nameid`, `slot1`, `slot2`, `slot3`, `map`, `success`) VALUES (NOW(), '%d', '%d', '%s', '%d', '%d', '%d', '%d', '%s', '%d') ",
+ log_config.log_produce_db, sd->status.account_id, sd->status.char_id, jstrescapecpy(t_name, sd->status.name), nameid, slot1, slot2, slot3, mapindex_id2name(sd->mapindex), success);
+ if(mysql_query(&logmysql_handle, tmp_sql))
+ {
+ ShowSQL("DB error - %s\n",mysql_error(&logmysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ } else {
+#endif
+ if((logfp=fopen(log_config.log_produce,"a+")) != NULL) {
+ time(&curtime);
+ strftime(timestring, 254, "%m/%d/%Y %H:%M:%S", localtime(&curtime));
+ fprintf(logfp,"%s - %s[%d:%d]\t%d\t%d,%d,%d\t%d%s", timestring, sd->status.name, sd->status.account_id, sd->status.char_id, nameid, slot1, slot2, slot3, success, RETCODE);
+ fclose(logfp);
+ }
+#ifndef TXT_ONLY
+ }
+#endif
+ return 0;
+}
+
+int log_refine(struct map_session_data *sd, int n, int success)
+{
+ FILE *logfp;
+ int log_card[MAX_SLOTS];
+ int item_level;
+ int i;
+#ifndef TXT_ONLY
+ char t_name[NAME_LENGTH*2];
+#endif
+
+ if(log_config.enable_logs <= 0)
+ return 0;
+
+ nullpo_retr(0, sd);
+
+ if(success == 0)
+ item_level = sd->status.inventory[n].refine; //leaving there 0 wasn't informative! we have SUCCESS field anyways
+ else
+ item_level = sd->status.inventory[n].refine + 1;
+ if(!should_log_item(log_config.refine,sd->status.inventory[n].nameid,1) || log_config.refine_items_log<item_level) return 0; //filter [Lupus]
+ for(i=0;i<MAX_SLOTS;i++)
+ log_card[i] = sd->status.inventory[n].card[i];
+
+#ifndef TXT_ONLY
+ if(log_config.sql_logs > 0)
+ {
+ char *str_p = tmp_sql;
+ str_p += sprintf(str_p, "INSERT DELAYED INTO `%s` (`refine_date`, `account_id`, `char_id`, `char_name`, `nameid`, `refine`"
+ ", `map`, `success`, `item_level`", log_config.log_refine_db);
+
+ for (i=0; i < MAX_SLOTS; i++)
+ str_p += sprintf(str_p, ", `card%d`", i);
+
+ str_p += sprintf(str_p, ") VALUES (NOW(), '%d', '%d', '%s', '%d', '%d', '%s', '%d', '%d'",
+ sd->status.account_id, sd->status.char_id, jstrescapecpy(t_name, sd->status.name),
+ sd->status.inventory[n].nameid, sd->status.inventory[n].refine, mapindex_id2name(sd->mapindex), success, item_level);
+
+ for(i=0; i<MAX_SLOTS; i++)
+ str_p += sprintf(str_p, ", '%d'", log_card[i]);
+
+ strcat(tmp_sql,")");
+
+ if(mysql_query(&logmysql_handle, tmp_sql))
+ {
+ ShowSQL("DB error - %s\n",mysql_error(&logmysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ } else {
+#endif
+ if((logfp=fopen(log_config.log_refine,"a+")) != NULL) {
+ time(&curtime);
+ strftime(timestring, 254, "%m/%d/%Y %H:%M:%S", localtime(&curtime));
+ fprintf(logfp,"%s - %s[%d:%d]\t%d,%d\t",
+ timestring, sd->status.name, sd->status.account_id, sd->status.char_id,
+ sd->status.inventory[n].nameid, sd->status.inventory[n].refine);
+
+ for (i=0; i<MAX_SLOTS; i++)
+ fprintf(logfp,"%d,",log_card[i]);
+
+ fprintf(logfp,"\t%d,%d%s", success, item_level, RETCODE);
+ fclose(logfp);
+ }
+#ifndef TXT_ONLY
+ }
+#endif
+ return 0;
+}
+
+int log_tostorage(struct map_session_data *sd,int n, int guild)
+{
+ FILE *logfp;
+ int i;
+
+ if(log_config.enable_logs <= 0 || log_config.storage == 0 || log_config.log_storage[0] == '\0')
+ return 0;
+
+ nullpo_retr(0, sd);
+ if(sd->status.inventory[n].nameid==0 || sd->inventory_data[n] == NULL)
+ return 1;
+
+ if(sd->status.inventory[n].amount < 0)
+ return 1;
+
+ if((logfp=fopen(log_config.log_trade,"a+")) != NULL) {
+ time(&curtime);
+ strftime(timestring, 254, "%m/%d/%Y %H:%M:%S", localtime(&curtime));
+ fprintf(logfp,"%s - to %s: %s[%d:%d]\t%d\t%d\t%d\t",
+ timestring, guild ? "guild_storage": "storage", sd->status.name, sd->status.account_id, sd->status.char_id,
+ sd->status.inventory[n].nameid, sd->status.inventory[n].amount, sd->status.inventory[n].refine);
+
+ for (i=0; i<MAX_SLOTS; i++)
+ fprintf(logfp, "%d,", sd->status.inventory[n].card[i]);
+
+ fprintf(logfp, "%s", RETCODE);
+ fclose(logfp);
+ }
+ return 0;
+}
+
+int log_fromstorage(struct map_session_data *sd,int n, int guild)
+{
+ FILE *logfp;
+ int i;
+
+ if(log_config.enable_logs <= 0 || log_config.storage == 0 || log_config.log_storage[0] == '\0')
+ return 0;
+
+ nullpo_retr(0, sd);
+
+ if(sd->status.inventory[n].nameid==0 || sd->inventory_data[n] == NULL)
+ return 1;
+
+ if(sd->status.inventory[n].amount < 0)
+ return 1;
+
+ if((logfp=fopen(log_config.log_trade,"a+")) != NULL) {
+ time(&curtime);
+ fprintf(logfp,"%s - from %s: %s[%d:%d]\t%d\t%d\t%d\t",
+ timestring, guild ? "guild_storage": "storage", sd->status.name, sd->status.account_id, sd->status.char_id,
+ sd->status.inventory[n].nameid, sd->status.inventory[n].amount, sd->status.inventory[n].refine);
+
+ for (i=0; i<MAX_SLOTS; i++)
+ fprintf(logfp, "%d,", sd->status.inventory[n].card[i]);
+
+ fprintf(logfp, "%s", RETCODE);
+
+ fclose(logfp);
+ }
+ return 0;
+}
+
+int log_trade(struct map_session_data *sd, struct map_session_data *target_sd, int n,int amount)
+{
+ FILE *logfp;
+ int log_nameid, log_amount, log_refine, log_card[MAX_SLOTS];
+ int i;
+#ifndef TXT_ONLY
+ char t_name[NAME_LENGTH*2],t_name2[NAME_LENGTH*2];
+#endif
+
+ if(log_config.enable_logs <= 0)
+ return 0;
+
+ nullpo_retr(0, sd);
+
+ if(sd->status.inventory[n].nameid==0 || amount <= 0 || sd->status.inventory[n].amount<amount || sd->inventory_data[n] == NULL)
+ return 1;
+
+ if(sd->status.inventory[n].amount < 0)
+ return 1;
+ if(!should_log_item(log_config.trade,sd->status.inventory[n].nameid,sd->status.inventory[n].amount)) return 0; //filter [Lupus]
+ log_nameid = sd->status.inventory[n].nameid;
+ log_amount = sd->status.inventory[n].amount;
+ log_refine = sd->status.inventory[n].refine;
+
+ for(i=0;i<MAX_SLOTS;i++)
+ log_card[i] = sd->status.inventory[n].card[i];
+
+#ifndef TXT_ONLY
+ if(log_config.sql_logs > 0)
+ {
+ char *str_p = tmp_sql;
+ str_p += sprintf(str_p, "INSERT DELAYED INTO `%s` (`trade_date`, `src_account_id`, `src_char_id`, `src_char_name`, `des_account_id`, `des_char_id`, `des_char_name`, `nameid`, `amount`, `refine`, `map`",
+ log_config.log_trade_db);
+
+ for (i=0; i < MAX_SLOTS; i++)
+ str_p += sprintf(str_p, ", `card%d`", i);
+
+ str_p += sprintf(str_p, ") VALUES (NOW(), '%d', '%d', '%s', '%d', '%d', '%s', '%d', '%d', '%d', '%s'",
+ sd->status.account_id, sd->status.char_id, jstrescapecpy(t_name, sd->status.name),
+ target_sd->status.account_id, target_sd->status.char_id, jstrescapecpy(t_name2, target_sd->status.name),
+ log_nameid, log_amount, log_refine, mapindex_id2name(sd->mapindex));
+
+ for(i=0; i<MAX_SLOTS; i++)
+ str_p += sprintf(str_p, ", '%d'", log_card[i]);
+
+ strcat(tmp_sql, ")");
+
+ if(mysql_query(&logmysql_handle, tmp_sql))
+ {
+ ShowSQL("DB error - %s\n",mysql_error(&logmysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ } else {
+#endif
+ if((logfp=fopen(log_config.log_trade,"a+")) != NULL) {
+ time(&curtime);
+ strftime(timestring, 254, "%m/%d/%Y %H:%M:%S", localtime(&curtime));
+ fprintf(logfp,"%s - %s[%d:%d]\t%s[%d:%d]\t%d\t%d\t%d\t",
+ timestring, sd->status.name, sd->status.account_id, sd->status.char_id,
+ target_sd->status.name, target_sd->status.account_id, target_sd->status.char_id,
+ log_nameid, log_amount, log_refine);
+
+ for (i=0; i<MAX_SLOTS; i++)
+ fprintf(logfp, "%d,", sd->status.inventory[n].card[i]);
+
+ fprintf(logfp, "%s", RETCODE);
+
+ fclose(logfp);
+ }
+#ifndef TXT_ONLY
+ }
+#endif
+ return 0;
+}
+
+int log_vend(struct map_session_data *sd,struct map_session_data *vsd,int n,int amount, int zeny)
+{
+ FILE *logfp;
+ int log_nameid, log_amount, log_refine, log_card[MAX_SLOTS];
+ int i;
+#ifndef TXT_ONLY
+ char t_name[NAME_LENGTH*2],t_name2[NAME_LENGTH*2];
+#endif
+
+ if(log_config.enable_logs <= 0)
+ return 0;
+ nullpo_retr(0, sd);
+
+ if(sd->status.inventory[n].nameid==0 || amount <= 0 || sd->status.inventory[n].amount<amount || sd->inventory_data[n] == NULL)
+ return 1;
+ if(sd->status.inventory[n].amount< 0)
+ return 1;
+ if(!should_log_item(log_config.vend,sd->status.inventory[n].nameid,sd->status.inventory[n].amount)) return 0; //filter [Lupus]
+ log_nameid = sd->status.inventory[n].nameid;
+ log_amount = sd->status.inventory[n].amount;
+ log_refine = sd->status.inventory[n].refine;
+ for(i=0;i<MAX_SLOTS;i++)
+ log_card[i] = sd->status.inventory[n].card[i];
+
+#ifndef TXT_ONLY
+ if(log_config.sql_logs > 0)
+ {
+ char *str_p = tmp_sql;
+ str_p += sprintf(str_p, "INSERT DELAYED INTO `%s` (`vend_date`, `vend_account_id`, `vend_char_id`, `vend_char_name`, `buy_account_id`, `buy_char_id`, `buy_char_name`, `nameid`, `amount`, `refine`, `map`, `zeny`",
+ log_config.log_vend_db);
+
+ for (i=0; i < MAX_SLOTS; i++)
+ str_p += sprintf(str_p, ", `card%d`", i);
+
+ str_p += sprintf(str_p, ") VALUES (NOW(), '%d', '%d', '%s', '%d', '%d', '%s', '%d', '%d', '%d', '%s', '%d'",
+ sd->status.account_id, sd->status.char_id, jstrescapecpy(t_name, sd->status.name),
+ vsd->status.account_id, vsd->status.char_id, jstrescapecpy(t_name2, vsd->status.name),
+ log_nameid, log_amount, log_refine, mapindex_id2name(sd->mapindex), zeny);
+
+ for(i=0; i<MAX_SLOTS; i++)
+ str_p += sprintf(str_p, ", '%d'", log_card[i]);
+
+ strcat(tmp_sql, ")");
+
+ if(mysql_query(&logmysql_handle, tmp_sql))
+ {
+ ShowSQL("DB error - %s\n",mysql_error(&logmysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ } else {
+#endif
+ if((logfp=fopen(log_config.log_vend,"a+")) != NULL) {
+ time(&curtime);
+ strftime(timestring, 254, "%m/%d/%Y %H:%M:%S", localtime(&curtime));
+ fprintf(logfp,"%s - %s[%d:%d]\t%s[%d:%d]\t%d\t%d\t%d\t",
+ timestring, sd->status.name, sd->status.account_id, sd->status.char_id,
+ vsd->status.name, vsd->status.account_id, vsd->status.char_id,
+ log_nameid, log_amount, log_refine);
+
+ for(i=0; i<MAX_SLOTS; i++)
+ fprintf(logfp, "%d,", sd->status.inventory[n].card[i]);
+
+ fprintf(logfp, "\t%d%s", zeny, RETCODE);
+ fclose(logfp);
+ }
+#ifndef TXT_ONLY
+ }
+#endif
+ return 0;
+}
+
+int log_atcommand(struct map_session_data *sd, const char *message)
+{
+ FILE *logfp;
+#ifndef TXT_ONLY
+ char t_name[NAME_LENGTH*2];
+ char t_msg[MESSAGE_SIZE*2+1]; //These are the contents of an @ call, so there shouldn't be overflow danger here?
+#endif
+
+ if(log_config.enable_logs <= 0)
+ return 0;
+ nullpo_retr(0, sd);
+#ifndef TXT_ONLY
+ if(log_config.sql_logs > 0)
+ {
+ sprintf(tmp_sql, "INSERT DELAYED INTO `%s` (`atcommand_date`, `account_id`, `char_id`, `char_name`, `map`, `command`) VALUES(NOW(), '%d', '%d', '%s', '%s', '%s') ",
+ log_config.log_gm_db, sd->status.account_id, sd->status.char_id, jstrescapecpy(t_name, sd->status.name), mapindex_id2name(sd->mapindex), jstrescapecpy(t_msg, (char *)message));
+ if(mysql_query(&logmysql_handle, tmp_sql))
+ {
+ ShowSQL("DB error - %s\n",mysql_error(&logmysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ } else {
+#endif
+ if((logfp=fopen(log_config.log_gm,"a+")) != NULL) {
+ time(&curtime);
+ strftime(timestring, 254, "%m/%d/%Y %H:%M:%S", localtime(&curtime));
+ fprintf(logfp,"%s - %s[%d]: %s%s",timestring,sd->status.name,sd->status.account_id,message,RETCODE);
+ fclose(logfp);
+ }
+#ifndef TXT_ONLY
+ }
+#endif
+ return 0;
+}
+
+int log_npc(struct map_session_data *sd, const char *message)
+{ //[Lupus]
+ FILE *logfp;
+ #ifndef TXT_ONLY
+ char t_name[NAME_LENGTH*2];
+ char t_msg[255+1]; //it's 255 chars MAX.
+ #endif
+
+ if(log_config.enable_logs <= 0)
+ return 0;
+ nullpo_retr(0, sd);
+#ifndef TXT_ONLY
+ if(log_config.sql_logs > 0)
+ {
+ sprintf(tmp_sql, "INSERT DELAYED INTO `%s` (`npc_date`, `account_id`, `char_id`, `char_name`, `map`, `mes`) VALUES(NOW(), '%d', '%d', '%s', '%s', '%s') ",
+ log_config.log_npc_db, sd->status.account_id, sd->status.char_id, jstrescapecpy(t_name, sd->status.name), mapindex_id2name(sd->mapindex), jstrescapecpy(t_msg, (char *)message));
+ if(mysql_query(&logmysql_handle, tmp_sql))
+ {
+ ShowSQL("DB error - %s\n",mysql_error(&logmysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ } else {
+#endif
+ if((logfp=fopen(log_config.log_npc,"a+")) != NULL) {
+ time(&curtime);
+ strftime(timestring, 254, "%m/%d/%Y %H:%M:%S", localtime(&curtime));
+ fprintf(logfp,"%s - %s[%d]: %s%s",timestring,sd->status.name,sd->status.account_id,message,RETCODE);
+ fclose(logfp);
+ }
+#ifndef TXT_ONLY
+ }
+#endif
+ return 0;
+}
+
+//ChatLogging
+// Log CHAT (currently only: Party, Guild, Whisper)
+// LOGGING FILTERS [Lupus]
+//=============================================================
+//0 = Don't log at all
+//1 = Log any chat messages
+//Advanced Filter Bits: ||
+//2 - Log Whisper messages
+//3 - Log Party messages
+//4 - Log Guild messages
+//5 - Log Common messages (not implemented)
+//6 - Don't log when WOE is on
+//Example:
+//log_chat: 1 = logs ANY messages
+//log_chat: 6 = logs both Whisper & Party messages
+//log_chat: 8 = logs only Guild messages
+//log_chat: 18 = logs only Whisper, when WOE is off
+
+int log_chat(char *type, int type_id, int src_charid, int src_accid, char *map, int x, int y, char *dst_charname, char *message){
+#ifndef TXT_ONLY
+ char t_msg[MESSAGE_SIZE*2+1]; //Chat line fully escaped, with an extra space just in case.
+#else
+ FILE *logfp;
+#endif
+
+ //Check ON/OFF
+ if(log_config.chat <= 0)
+ return 0; //Deactivated
+
+#ifndef TXT_ONLY
+ if(log_config.sql_logs > 0){
+ sprintf(tmp_sql, "INSERT DELAYED INTO `%s` (`time`, `type`, `type_id`, `src_charid`, `src_accountid`, `src_map`, `src_map_x`, `src_map_y`, `dst_charname`, `message`) VALUES (NOW(), '%s', '%d', '%d', '%d', '%s', '%d', '%d', '%s', '%s')",
+ log_config.log_chat_db, type, type_id, src_charid, src_accid, map, x, y, dst_charname, jstrescapecpy(t_msg, (char *)message));
+
+ if(mysql_query(&logmysql_handle, tmp_sql)){
+ ShowSQL("DB error - %s\n",mysql_error(&logmysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ return -1;
+ }else{
+ return 0;
+ }
+ }
+#endif
+
+#ifdef TXT_ONLY
+ if((logfp = fopen(log_config.log_chat, "a+")) != NULL){
+ time(&curtime);
+ strftime(timestring, 254, "%m/%d/%Y %H:%M:%S", localtime(&curtime));
+ //DATE - type,type_id,src_charid,src_accountid,src_map,src_x,src_y,dst_charname,message
+ fprintf(logfp, "%s - %s,%d,%d,%d,%s,%d,%d,%s,%s%s",
+ timestring, type, type_id, src_charid, src_accid, map, x, y, dst_charname, message, RETCODE);
+ fclose(logfp);
+ return 0;
+ }else{
+ return -1;
+ }
+#endif
+return -1;
+}
+
+
+void log_set_defaults(void)
+{
+ memset(&log_config, 0, sizeof(log_config));
+
+ //LOG FILTER Default values
+ log_config.refine_items_log = 5; //log refined items, with refine >= +7
+ log_config.rare_items_log = 100; //log rare items. drop chance <= 1%
+ log_config.price_items_log = 1000; //1000z
+ log_config.amount_items_log = 100;
+}
+
+int log_config_read(char *cfgName)
+{
+ static int count = 0;
+ char line[1024], w1[1024], w2[1024];
+ FILE *fp;
+
+ if ((count++) == 0)
+ log_set_defaults();
+
+ if((fp = fopen(cfgName, "r")) == NULL)
+ {
+ ShowError("Log configuration file not found at: %s\n", cfgName);
+ return 1;
+ }
+
+ while(fgets(line, sizeof(line) -1, fp))
+ {
+ if(line[0] == '/' && line[1] == '/')
+ continue;
+
+ if(sscanf(line, "%[^:]: %[^\r\n]", w1, w2) == 2)
+ {
+ if(strcmpi(w1,"enable_logs") == 0) {
+ log_config.enable_logs = (atoi(w2));
+ } else if(strcmpi(w1,"sql_logs") == 0) {
+ log_config.sql_logs = (atoi(w2));
+//start of common filter settings
+ } else if(strcmpi(w1,"rare_items_log") == 0) {
+ log_config.rare_items_log = (atoi(w2));
+ } else if(strcmpi(w1,"refine_items_log") == 0) {
+ log_config.refine_items_log = (atoi(w2));
+ } else if(strcmpi(w1,"price_items_log") == 0) {
+ log_config.price_items_log = (atoi(w2));
+ } else if(strcmpi(w1,"amount_items_log") == 0) {
+ log_config.amount_items_log = (atoi(w2));
+//end of common filter settings
+ } else if(strcmpi(w1,"log_branch") == 0) {
+ log_config.branch = (atoi(w2));
+ } else if(strcmpi(w1,"log_pick") == 0) {
+ log_config.pick = (atoi(w2));
+ } else if(strcmpi(w1,"log_drop") == 0) {
+ log_config.drop = (atoi(w2));
+ } else if(strcmpi(w1,"log_steal") == 0) {
+ log_config.steal = (atoi(w2));
+ } else if(strcmpi(w1,"log_mvpdrop") == 0) {
+ log_config.mvpdrop = (atoi(w2));
+ } else if(strcmpi(w1,"log_present") == 0) {
+ log_config.present = (atoi(w2));
+ } else if(strcmpi(w1,"log_produce") == 0) {
+ log_config.produce = (atoi(w2));
+ } else if(strcmpi(w1,"log_refine") == 0) {
+ log_config.refine = (atoi(w2));
+ } else if(strcmpi(w1,"log_trade") == 0) {
+ log_config.trade = (atoi(w2));
+ } else if(strcmpi(w1,"log_storage") == 0) {
+ log_config.storage = (atoi(w2));
+ } else if(strcmpi(w1,"log_vend") == 0) {
+ log_config.vend = (atoi(w2));
+ } else if(strcmpi(w1,"log_zeny") == 0) {
+ log_config.zeny = (atoi(w2));
+ } else if(strcmpi(w1,"log_gm") == 0) {
+ log_config.gm = (atoi(w2));
+ } else if(strcmpi(w1,"log_npc") == 0) {
+ log_config.npc = (atoi(w2));
+ } else if(strcmpi(w1, "log_chat") == 0) {
+ log_config.chat = (atoi(w2));
+ }
+
+#ifndef TXT_ONLY
+ else if(strcmpi(w1, "log_branch_db") == 0) {
+ strcpy(log_config.log_branch_db, w2);
+ if(log_config.branch == 1)
+ ShowNotice("Logging Dead Branch Usage to table `%s`\n", w2);
+ } else if(strcmpi(w1, "log_pick_db") == 0) {
+ strcpy(log_config.log_pick_db, w2);
+ if(log_config.pick == 1)
+ ShowNotice("Logging Item Picks to table `%s`\n", w2);
+ } else if(strcmpi(w1, "log_zeny_db") == 0) {
+ strcpy(log_config.log_zeny_db, w2);
+ if(log_config.zeny == 1)
+ ShowNotice("Logging Zeny to table `%s`\n", w2);
+ } else if(strcmpi(w1, "log_drop_db") == 0) {
+ strcpy(log_config.log_drop_db, w2);
+ if(log_config.drop == 1)
+ ShowNotice("Logging Item Drops to table `%s`\n", w2);
+ } else if(strcmpi(w1, "log_mvpdrop_db") == 0) {
+ strcpy(log_config.log_mvpdrop_db, w2);
+ if(log_config.mvpdrop == 1)
+ ShowNotice("Logging MVP Drops to table `%s`\n", w2);
+ } else if(strcmpi(w1, "log_present_db") == 0) {
+ strcpy(log_config.log_present_db, w2);
+ if(log_config.present == 1)
+ ShowNotice("Logging Present Usage & Results to table `%s`\n", w2);
+ } else if(strcmpi(w1, "log_produce_db") == 0) {
+ strcpy(log_config.log_produce_db, w2);
+ if(log_config.produce == 1)
+ ShowNotice("Logging Producing to table `%s`\n", w2);
+ } else if(strcmpi(w1, "log_refine_db") == 0) {
+ strcpy(log_config.log_refine_db, w2);
+ if(log_config.refine == 1)
+ ShowNotice("Logging Refining to table `%s`\n", w2);
+ } else if(strcmpi(w1, "log_trade_db") == 0) {
+ strcpy(log_config.log_trade_db, w2);
+ if(log_config.trade == 1)
+ ShowNotice("Logging Item Trades to table `%s`\n", w2);
+// } else if(strcmpi(w1, "log_storage_db") == 0) {
+// strcpy(log_config.log_storage_db, w2);
+// if(log_config.storage == 1)
+// {
+// printf("Logging Item Storages");
+// printf(" to table `%s`\n", w2);
+// }
+ } else if(strcmpi(w1, "log_vend_db") == 0) {
+ strcpy(log_config.log_vend_db, w2);
+ if(log_config.vend == 1)
+ ShowNotice("Logging Vending to table `%s`\n", w2);
+ } else if(strcmpi(w1, "log_gm_db") == 0) {
+ strcpy(log_config.log_gm_db, w2);
+ if(log_config.gm > 0)
+ ShowNotice("Logging GM Level %d Commands to table `%s`\n", log_config.gm, w2);
+ } else if(strcmpi(w1, "log_npc_db") == 0) {
+ strcpy(log_config.log_npc_db, w2);
+ if(log_config.npc > 0)
+ ShowNotice("Logging NPC 'logmes' to table `%s`\n", w2);
+ } else if(strcmpi(w1, "log_chat_db") == 0) {
+ strcpy(log_config.log_chat_db, w2);
+ if(log_config.chat > 0)
+ ShowNotice("Logging CHAT to table `%s`\n", w2);
+ }
+#endif
+
+ else if(strcmpi(w1, "log_branch_file") == 0) {
+ strcpy(log_config.log_branch, w2);
+ if(log_config.branch > 0 && log_config.sql_logs < 1)
+ ShowNotice("Logging Dead Branch Usage to file `%s`.txt\n", w2);
+ } else if(strcmpi(w1, "log_drop_file") == 0) {
+ strcpy(log_config.log_drop, w2);
+ if(log_config.drop > 0 && log_config.sql_logs < 1)
+ ShowNotice("Logging Item Drops to file `%s`.txt\n", w2);
+ } else if(strcmpi(w1, "log_pick_file") == 0) {
+ strcpy(log_config.log_pick, w2);
+ if(log_config.pick > 0 && log_config.sql_logs < 1)
+ ShowNotice("Logging Item Picks to file `%s`.txt\n", w2);
+ } else if(strcmpi(w1, "log_zeny_file") == 0) {
+ strcpy(log_config.log_zeny, w2);
+ if(log_config.zeny > 0 && log_config.sql_logs < 1)
+ ShowNotice("Logging Zeny to file `%s`.txt\n", w2);
+ } else if(strcmpi(w1, "log_mvpdrop_file") == 0) {
+ strcpy(log_config.log_mvpdrop, w2);
+ if(log_config.mvpdrop > 0 && log_config.sql_logs < 1)
+ ShowNotice("Logging MVP Drops to file `%s`.txt\n", w2);
+ } else if(strcmpi(w1, "log_present_file") == 0) {
+ strcpy(log_config.log_present, w2);
+ if(log_config.present > 0 && log_config.sql_logs < 1)
+ ShowNotice("Logging Present Usage & Results to file `%s`.txt\n", w2);
+ } else if(strcmpi(w1, "log_produce_file") == 0) {
+ strcpy(log_config.log_produce, w2);
+ if(log_config.produce > 0 && log_config.sql_logs < 1)
+ ShowNotice("Logging Producing to file `%s`.txt\n", w2);
+ } else if(strcmpi(w1, "log_refine_file") == 0) {
+ strcpy(log_config.log_refine, w2);
+ if(log_config.refine > 0 && log_config.sql_logs < 1)
+ ShowNotice("Logging Refining to file `%s`.txt\n", w2);
+ } else if(strcmpi(w1, "log_trade_file") == 0) {
+ strcpy(log_config.log_trade, w2);
+ if(log_config.trade > 0 && log_config.sql_logs < 1)
+ ShowNotice("Logging Item Trades to file `%s`.txt\n", w2);
+ } else if(strcmpi(w1, "log_storage_file") == 0) {
+ strcpy(log_config.log_storage, w2);
+ if(log_config.storage > 0 && log_config.sql_logs < 1)
+ ShowNotice("Logging Item Storages to file `%s`.txt\n", w2);
+ } else if(strcmpi(w1, "log_vend_file") == 0) {
+ strcpy(log_config.log_vend, w2);
+ if(log_config.vend > 0 && log_config.sql_logs < 1)
+ ShowNotice("Logging Vending to file `%s`.txt\n", w2);
+ } else if(strcmpi(w1, "log_gm_file") == 0) {
+ strcpy(log_config.log_gm, w2);
+ if(log_config.gm > 0 && log_config.sql_logs < 1)
+ ShowNotice("Logging GM Level %d Commands to file `%s`.txt\n", log_config.gm, w2);
+ } else if(strcmpi(w1, "log_npc_file") == 0) {
+ strcpy(log_config.log_npc, w2);
+ if(log_config.npc > 0 && log_config.sql_logs < 1)
+ ShowNotice("Logging NPC 'logmes' to file `%s`.txt\n", w2);
+ } else if(strcmpi(w1, "log_chat_file") == 0) {
+ strcpy(log_config.log_chat, w2);
+ if(log_config.chat > 0 && log_config.sql_logs < 1)
+ ShowNotice("Logging CHAT to file `%s`.txt\n", w2);
+ //support the import command, just like any other config
+ } else if(strcmpi(w1,"import") == 0) {
+ log_config_read(w2);
+ }
+ }
+ }
+
+ fclose(fp);
+ return 0;
+}
diff --git a/src/map/log.h b/src/map/log.h
new file mode 100644
index 000000000..e650d453e
--- /dev/null
+++ b/src/map/log.h
@@ -0,0 +1,47 @@
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#ifndef _LOG_H_
+#define _LOG_H_
+
+#include "map.h"
+
+#ifndef TXT_ONLY
+
+extern char db_server_logdb[32];
+
+#endif //NOT TXT_ONLY
+
+int log_branch(struct map_session_data *sd);
+int log_pick(struct map_session_data *sd, char *type, int mob_id, int nameid, int amount, struct item *itm);
+int log_zeny(struct map_session_data *sd, char *type, struct map_session_data *src_sd, int amount);
+int log_drop(struct map_session_data *sd, int monster_id, int *log_drop);
+int log_mvpdrop(struct map_session_data *sd, int monster_id, int *log_mvp);
+int log_present(struct map_session_data *sd, int source_type, int nameid);
+int log_produce(struct map_session_data *sd, int nameid, int slot1, int slot2, int slot3, int success);
+int log_refine(struct map_session_data *sd, int n, int success);
+int log_trade(struct map_session_data *sd,struct map_session_data *target_sd,int n,int amount);
+int log_tostorage(struct map_session_data *sd,int n, int guild);
+int log_fromstorage(struct map_session_data *sd,int n, int guild);
+
+int log_vend(struct map_session_data *sd,struct map_session_data *vsd,int n,int amount,int zeny);
+int log_atcommand(struct map_session_data *sd, const char *message);
+int log_npc(struct map_session_data *sd, const char *message);
+int log_chat(char *type, int type_id, int src_charid, int src_accid, char *map, int x, int y, char *dst_charname, char *message);
+
+int log_config_read(char *cfgName);
+
+int should_log_item(int filter, int nameid, int amount); //log filter check
+
+extern struct Log_Config {
+ int enable_logs;
+ int sql_logs;
+ int rare_items_log,refine_items_log,price_items_log,amount_items_log;
+ int branch, pick, drop, steal, mvpdrop, present, produce, refine, trade, vend, zeny, gm, npc, storage, chat;
+ char log_branch[32], log_pick[32], log_zeny[32], log_drop[32], log_mvpdrop[32], log_present[32], log_produce[32], log_refine[32], log_trade[32], log_vend[32], log_gm[32], log_npc[32], log_storage[32], log_chat[32];
+ char log_branch_db[32], log_pick_db[32], log_zeny_db[32], log_drop_db[32], log_mvpdrop_db[32], log_present_db[32], log_produce_db[32], log_refine_db[32], log_trade_db[32], log_vend_db[32], log_gm_db[32], log_npc_db[32], log_chat_db[32];
+ int uptime;
+ char log_uptime[32];
+} log_config;
+
+#endif
diff --git a/src/map/mail.c b/src/map/mail.c
new file mode 100644
index 000000000..6244a57c7
--- /dev/null
+++ b/src/map/mail.c
@@ -0,0 +1,361 @@
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#ifndef TXT_ONLY
+// Mail System for eAthena SQL
+// Created by Valaris
+// moved all strings to msg_athena.conf [Lupus]
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "../common/strlib.h"
+#include "../common/socket.h"
+#include "../common/timer.h"
+#include "../common/nullpo.h"
+#include "../common/showmsg.h"
+
+#include "map.h"
+#include "clif.h"
+#include "chrif.h"
+#include "intif.h"
+#include "atcommand.h"
+#include "pc.h"
+#include "mail.h"
+
+#ifndef TXT_ONLY
+ #ifndef SQL_DEBUG
+
+ #define mysql_query(_x, _y) mysql_real_query(_x, _y, strlen(_y))
+
+ #else
+
+ #define mysql_query(_x, _y) debug_mysql_query(__FILE__, __LINE__, _x, _y)
+
+ #endif
+#endif
+
+int MAIL_CHECK_TIME = 120000;
+int mail_timer;
+//extern char *msg_table[1000]; // Server messages (0-499 reserved for GM commands, 500-999 reserved for others)
+
+int mail_check(struct map_session_data *sd,int type)
+{
+ int i = 0, new_ = 0, priority = 0;
+ char message[50];
+
+ nullpo_retr (0, sd);
+
+ sprintf(tmp_sql,"SELECT `message_id`,`to_account_id`,`from_char_name`,`read_flag`,`priority`,`check_flag` "
+ "FROM `%s` WHERE `to_account_id` = \"%d\" ORDER by `message_id`", mail_db, sd->status.account_id);
+
+ if (mysql_query(&mail_handle, tmp_sql)) {
+ ShowSQL("DB error - %s\n",mysql_error(&mail_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ return 0;
+ }
+
+ mail_res = mysql_store_result(&mail_handle);
+ if(mail_res) {
+ if (mysql_num_rows(mail_res) == 0) {
+ //clif_displaymessage(sd->fd,"You have no messages.");
+ clif_displaymessage(sd->fd, msg_txt(516));
+
+ mysql_free_result(mail_res);
+ return 0;
+ }
+
+ while ((mail_row = mysql_fetch_row(mail_res))) {
+ i++;
+ if(!atoi(mail_row[5])) {
+ sprintf(tmp_sql,"UPDATE `%s` SET `check_flag`='1' WHERE `message_id`= \"%d\"", mail_db, atoi(mail_row[0]));
+ if(mysql_query(&mail_handle, tmp_sql) ) {
+ ShowSQL("DB error - %s\n",mysql_error(&mail_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ }
+
+ if(!atoi(mail_row[3])) {
+ new_++;
+ if(atoi(mail_row[4]))
+ priority++;
+ if(type==2 || type==3) {
+ if(atoi(mail_row[4])) {
+ //sprintf(message, "%d - From : %s (New - Priority)", i, mail_row[2]);
+ sprintf(message, msg_txt(511), i, mail_row[2]);
+
+ clif_displaymessage(sd->fd, jstrescape(message));
+ } else {
+ //sprintf(message, "%d - From : %s (New)", i, mail_row[2]);
+ sprintf(message, msg_txt(512), i, mail_row[2]);
+ clif_displaymessage(sd->fd, jstrescape(message));
+ }
+ }
+ } else if(type==2){
+ //sprintf(message, "%d - From : %s", i, mail_row[2]);
+ sprintf(message, msg_txt(513), i, mail_row[2]);
+ clif_displaymessage(sd->fd, jstrescape(message));
+ }
+ }
+
+ mysql_free_result(mail_res);
+ } else {
+ ShowSQL("DB error - %s\n",mysql_error(&mail_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ return 0;
+ }
+
+ if(i>0 && new_>0 && type==1) {
+ //sprintf(message, "You have %d new messages.", new_);
+ sprintf(message, msg_txt(514), new_);
+
+ clif_displaymessage(sd->fd, jstrescape(message));
+ }
+ if(i>0 && new_>0 && priority>0 && type==1) {
+ //sprintf(message, "You have %d unread priority messages.", priority);
+ sprintf(message, msg_txt(515), priority);
+ clif_displaymessage(sd->fd, jstrescape(message));
+ }
+ if(!new_) {
+ //clif_displaymessage(sd->fd, "You have no new messages.");
+ clif_displaymessage(sd->fd, msg_txt(516));
+ }
+
+ return 0;
+}
+
+int mail_read(struct map_session_data *sd, int message_id)
+{
+
+ char message[80];
+
+ nullpo_retr (0, sd);
+
+ sprintf(tmp_sql,"SELECT `message_id`,`to_account_id`,`from_char_name`,`message`,`read_flag`,`priority`,`check_flag` from `%s` WHERE `to_account_id` = \"%d\" ORDER by `message_id` LIMIT %d, 1",mail_db,sd->status.account_id,message_id-1);
+
+ if (mysql_query(&mail_handle, tmp_sql)) {
+ ShowSQL("DB error - %s\n",mysql_error(&mail_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ return 0;
+ }
+
+ mail_res = mysql_store_result(&mail_handle);
+ if(mail_res) {
+ if (mysql_num_rows(mail_res) == 0) {
+ mysql_free_result(mail_res);
+ //clif_displaymessage(sd->fd, "Message not found.");
+ clif_displaymessage(sd->fd, msg_txt(517));
+ return 0;
+ }
+
+ if ((mail_row = mysql_fetch_row(mail_res))) {
+ if(!atoi(mail_row[6])) {
+ sprintf(tmp_sql,"UPDATE `%s` SET `check_flag`='1' WHERE `message_id`= \"%d\"", mail_db, atoi(mail_row[0]));
+ if(mysql_query(&mail_handle, tmp_sql) ) {
+ ShowSQL("DB error - %s\n",mysql_error(&mail_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ }
+
+ //sprintf(message, "Reading message from %s", mail_row[2]);
+ sprintf(message, msg_txt(518), mail_row[2]);
+ clif_displaymessage(sd->fd, jstrescape(message));
+
+ sprintf(message, "%s", mail_row[3]);
+ clif_displaymessage(sd->fd, jstrescape(message));
+
+ sprintf(tmp_sql,"UPDATE `%s` SET `read_flag`='1' WHERE `message_id`= \"%d\"", mail_db, atoi(mail_row[0]));
+ if(mysql_query(&mail_handle, tmp_sql) ) {
+ ShowSQL("DB error - %s\n",mysql_error(&mail_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ }
+
+ mysql_free_result(mail_res);
+
+ } else {
+ ShowSQL("DB error - %s\n",mysql_error(&mail_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+
+ return 0;
+}
+
+int mail_delete(struct map_session_data *sd, int message_id)
+{
+ nullpo_retr (0, sd);
+
+ sprintf(tmp_sql,"SELECT `message_id`,`to_account_id`,`read_flag`,`priority`,`check_flag` from `%s` WHERE `to_account_id` = \"%d\" ORDER by `message_id` LIMIT %d, 1",mail_db,sd->status.account_id,message_id-1);
+
+ if (mysql_query(&mail_handle, tmp_sql)) {
+ ShowSQL("DB error - %s\n",mysql_error(&mail_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ return 0;
+ }
+
+ mail_res = mysql_store_result(&mail_handle);
+ if(mail_res) {
+ if (mysql_num_rows(mail_res) == 0) {
+ mysql_free_result(mail_res);
+ //clif_displaymessage(sd->fd, "Message not found.");
+ clif_displaymessage(sd->fd, msg_txt(517));
+ return 0;
+ }
+
+ if ((mail_row = mysql_fetch_row(mail_res))) {
+ if(!atoi(mail_row[2]) && atoi(mail_row[3])) {
+ mysql_free_result(mail_res);
+ //clif_displaymessage(sd->fd,"Cannot delete unread priority mail.");
+ clif_displaymessage(sd->fd,msg_txt(519));
+
+ return 0;
+ }
+ if(!atoi(mail_row[4])) {
+ mysql_free_result(mail_res);
+ //clif_displaymessage(sd->fd,"You have recieved new mail, use @listmail before deleting.");
+ clif_displaymessage(sd->fd,msg_txt(520));
+ return 0;
+ }
+ sprintf(tmp_sql,"DELETE FROM `%s` WHERE `message_id` = \"%d\"", mail_db, atoi(mail_row[0]));
+ if(mysql_query(&mail_handle, tmp_sql) ) {
+ mysql_free_result(mail_res);
+ ShowSQL("DB error - %s\n",mysql_error(&mail_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ return 0;
+ }
+ //else clif_displaymessage(sd->fd,"Message deleted.");
+ else clif_displaymessage(sd->fd,msg_txt(521));
+ }
+
+ mysql_free_result(mail_res);
+
+ } else {
+ ShowSQL("DB error - %s\n",mysql_error(&mail_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+
+ return 0;
+}
+
+int mail_send(struct map_session_data *sd, char *name, char *message, int flag)
+{
+ nullpo_retr (0, sd);
+
+ if(pc_isGM(sd) < 80 && sd->mail_counter > 0) {
+ //clif_displaymessage(sd->fd,"You must wait 10 minutes before sending another message");
+ clif_displaymessage(sd->fd,msg_txt(522));
+ return 0;
+ }
+
+ if(strcmp(name,"*")==0) {
+ if(pc_isGM(sd) < 80) {
+ //clif_displaymessage(sd->fd, "Access Denied.");
+ clif_displaymessage(sd->fd, msg_txt(523));
+ return 0;
+ }
+ else
+ sprintf(tmp_sql,"SELECT DISTINCT `account_id` FROM `%s` WHERE `account_id` <> '%d' ORDER BY `account_id`", char_db, sd->status.account_id);
+ }
+ else
+ sprintf(tmp_sql,"SELECT `account_id`,`name` FROM `%s` WHERE `name` = \"%s\"", char_db, jstrescape(name));
+
+ if (mysql_query(&mail_handle, tmp_sql)) {
+ ShowSQL("DB error - %s\n",mysql_error(&mail_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ return 0;
+ }
+
+ mail_res = mysql_store_result(&mail_handle);
+ if(mail_res) {
+ if (mysql_num_rows(mail_res) == 0) {
+ mysql_free_result(mail_res);
+ //clif_displaymessage(sd->fd,"Character does not exist.");
+ clif_displaymessage(sd->fd,msg_txt(524));
+ return 0;
+ }
+
+ while ((mail_row = mysql_fetch_row(mail_res))) {
+ if(strcmp(name,"*")==0) {
+ sprintf(tmp_sql, "INSERT DELAYED INTO `%s` (`to_account_id`,`from_account_id`,`from_char_name`,`message`,`priority`)"
+ " VALUES ('%d', '%d', '%s', '%s', '%d')",mail_db, atoi(mail_row[0]), sd->status.account_id, sd->status.name, jstrescape(message), flag);
+ }
+ else {
+ sprintf(tmp_sql, "INSERT DELAYED INTO `%s` (`to_account_id`,`to_char_name`,`from_account_id`,`from_char_name`,`message`,`priority`)"
+ " VALUES ('%d', '%s', '%d', '%s', '%s', '%d')",mail_db, atoi(mail_row[0]), mail_row[1], sd->status.account_id, sd->status.name, jstrescape(message), flag);
+ if(pc_isGM(sd) < 80)
+ sd->mail_counter=5;
+ }
+
+ if(mysql_query(&mail_handle, tmp_sql) ) {
+ mysql_free_result(mail_res);
+ ShowSQL("DB error - %s\n",mysql_error(&mail_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ return 0;
+ }
+ }
+ }
+
+ //clif_displaymessage(sd->fd,"Mail has been sent.");
+ clif_displaymessage(sd->fd,msg_txt(525));
+
+ return 0;
+}
+
+int mail_check_timer(int tid,unsigned int tick,int id,int data)
+{
+ struct map_session_data *sd = NULL;
+ int i;
+
+ if(mail_timer != tid)
+ return 0;
+
+ sprintf(tmp_sql,"SELECT DISTINCT `to_account_id` FROM `%s` WHERE `read_flag` = '0' AND `check_flag` = '0'", mail_db);
+
+ if (mysql_query(&mail_handle, tmp_sql)) {
+ ShowSQL("DB error - %s\n",mysql_error(&mail_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ mail_timer=add_timer(gettick()+MAIL_CHECK_TIME,mail_check_timer,0,0);
+ return 0;
+ }
+
+ mail_res = mysql_store_result(&mail_handle);
+
+ if (mail_res) {
+ if (mysql_num_rows(mail_res) == 0) {
+ mysql_free_result(mail_res);
+ mail_timer=add_timer(gettick()+MAIL_CHECK_TIME,mail_check_timer,0,0);
+ return 0;
+ }
+
+ while ((mail_row = mysql_fetch_row(mail_res))) {
+ for (i = 0; i < fd_max; i++) {
+ if (session[i] && (sd = (struct map_session_data *) session[i]->session_data) && sd->state.auth){
+ if(pc_isGM(sd) < 80 && sd->mail_counter > 0)
+ sd->mail_counter--;
+ if(sd->status.account_id==atoi(mail_row[0]))
+ //clif_displaymessage(sd->fd, "You have new mail.");
+ clif_displaymessage(sd->fd, msg_txt(526));
+ }
+ }
+ }
+ }
+
+ sprintf(tmp_sql,"UPDATE `%s` SET `check_flag`='1' WHERE `check_flag`= '0' ", mail_db);
+ if(mysql_query(&mail_handle, tmp_sql) ) {
+ ShowSQL("DB error - %s\n",mysql_error(&mail_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+
+ mail_timer=add_timer(gettick()+MAIL_CHECK_TIME,mail_check_timer,0,0);
+ return 0;
+}
+
+int do_init_mail(void)
+{
+ add_timer_func_list(mail_check_timer,"mail_check_timer");
+ mail_timer=add_timer(gettick()+MAIL_CHECK_TIME,mail_check_timer,0,0);
+ return 0;
+}
+
+#endif
diff --git a/src/map/mail.h b/src/map/mail.h
new file mode 100644
index 000000000..2460de238
--- /dev/null
+++ b/src/map/mail.h
@@ -0,0 +1,12 @@
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+// Mail System for eAthena
+// Created by Valaris
+
+int mail_check(struct map_session_data *sd, int type);
+int mail_read(struct map_session_data *sd, int message_id);
+int mail_delete(struct map_session_data *sd, int message_id);
+int mail_send(struct map_session_data *sd, char *name, char *message, int flag);
+
+int do_init_mail(void);
diff --git a/src/map/map.c b/src/map/map.c
new file mode 100644
index 000000000..06ddd7cb1
--- /dev/null
+++ b/src/map/map.c
@@ -0,0 +1,3952 @@
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#ifdef _WIN32
+#include <winsock.h>
+#else
+#include <netdb.h>
+#include <unistd.h>
+#endif
+#include <math.h>
+
+#include "../common/core.h"
+#include "../common/timer.h"
+#include "../common/grfio.h"
+#include "../common/malloc.h"
+#include "../common/socket.h"
+#include "../common/showmsg.h"
+#include "../common/version.h"
+#include "../common/nullpo.h"
+
+#include "map.h"
+#include "chrif.h"
+#include "clif.h"
+#include "intif.h"
+#include "npc.h"
+#include "pc.h"
+#include "status.h"
+#include "mob.h"
+#include "chat.h"
+#include "itemdb.h"
+#include "storage.h"
+#include "skill.h"
+#include "trade.h"
+#include "party.h"
+#include "battle.h"
+#include "script.h"
+#include "guild.h"
+#include "pet.h"
+#include "atcommand.h"
+#include "charcommand.h"
+
+#include "log.h"
+
+#include "charsave.h"
+
+
+// maybe put basic macros to somewhere else
+#define swap(a,b) ((a == b) || ((a ^= b), (b ^= a), (a ^= b)))
+
+#ifndef TXT_ONLY
+
+#include "mail.h" // mail system [Valaris]
+
+MYSQL mmysql_handle;
+MYSQL_RES* sql_res ;
+MYSQL_ROW sql_row ;
+char tmp_sql[65535]="";
+
+MYSQL lmysql_handle;
+MYSQL_RES* lsql_res ;
+MYSQL_ROW lsql_row ;
+
+MYSQL logmysql_handle; //For the log database - fix by [Maeki]
+MYSQL_RES* logsql_res ;
+MYSQL_ROW logsql_row ;
+
+MYSQL mail_handle; // mail system [Valaris]
+MYSQL_RES* mail_res ;
+MYSQL_ROW mail_row ;
+
+int map_server_port = 3306;
+char map_server_ip[16] = "127.0.0.1";
+char map_server_id[32] = "ragnarok";
+char map_server_pw[32] = "ragnarok";
+char map_server_db[32] = "ragnarok";
+char default_codepage[32] = ""; //Feature by irmin.
+int db_use_sqldbs = 0;
+
+int login_server_port = 3306;
+char login_server_ip[16] = "127.0.0.1";
+char login_server_id[32] = "ragnarok";
+char login_server_pw[32] = "ragnarok";
+char login_server_db[32] = "ragnarok";
+
+char item_db_db[32] = "item_db";
+char item_db2_db[32] = "item_db2";
+char mob_db_db[32] = "mob_db";
+char mob_db2_db[32] = "mob_db2";
+
+// SQL for databases not supported yet. [Valaris]
+int db_use_newsqldbs = 0;
+
+char abra_sqldb[32]="abra_db";
+char attr_fix_sqldb[32]="attr_fix";
+char cast_sqldb[32]="cast_db";
+char castle_sqldb[32]="castle_db";
+char create_arrow_sqldb[32]="create_arrow_db";
+char exp_sqldb[32]="exp";
+char exp_guild_sqldb[32]="exp_guild";
+char item_bluebox_sqldb[32]="item_bluebox";
+char item_cardalbum_sqldb[32]="item_cardalbum";
+char item_giftbox_sqldb[32]="item_giftbox";
+char item_scroll_sqldb[32]="item_scroll";
+char item_violetbox_sqldb[32]="item_violetbox";
+char job_sqldb1[32]="job_db1";
+char mob_boss_sqldb[32]="mob_boss";
+char mob_branch_sqldb[32]="mob_branch";
+char mob_poring_sqldb[32]="mob_poring";
+char mob_skill_sqldb[32]="mob_skill_db";
+char pet_sqldb[32]="pet_db";
+char produce_sqldb[32]="produce_db";
+char refine_sqldb[32]="refine_db";
+char size_fix_sqldb[32]="size_fix";
+char skill_sqldb[32]="skill_db";
+char skill_require_sqldb[32]="skill_require_db";
+char skill_tree_sqldb[32]="skill_tree";
+// End [Valaris]
+
+char login_db[32] = "login";
+char login_db_level[32] = "level";
+char login_db_account_id[32] = "account_id";
+
+int log_db_port = 3306;
+char log_db_ip[16] = "127.0.0.1";
+char log_db_id[32] = "ragnarok";
+char log_db_pw[32] = "ragnarok";
+char log_db[32] = "log";
+
+int mail_server_port = 3306;
+char mail_server_ip[16] = "127.0.0.1";
+char mail_server_id[32] = "ragnarok";
+char mail_server_pw[32] = "ragnarok";
+char mail_server_db[32] = "ragnarok";
+int mail_server_enable = 0;
+
+char gm_db[32] = "login";
+char gm_db_level[32] = "level";
+char gm_db_account_id[32] = "account_id";
+
+int read_gm_interval = 600000;
+
+char char_db[32] = "char";
+
+char mail_db[32] = "mail";
+
+char charsql_host[40] = "localhost";
+int charsql_port = 3306;
+char charsql_user[32] = "ragnarok";
+char charsql_pass[32] = "eAthena";
+char charsql_db[40] = "ragnarok";
+MYSQL charsql_handle;
+MYSQL_RES* charsql_res;
+MYSQL_ROW charsql_row;
+
+#ifdef MAPREGSQL
+// [zBuffer] SQL Mapreg
+MYSQL mapregsql_handle;
+MYSQL_RES* mapregsql_res ;
+MYSQL_ROW mapregsql_row;
+#endif
+
+#endif /* not TXT_ONLY */
+
+int lowest_gm_level = 1;
+
+// This param using for sending mainchat
+// messages like whispers to this nick. [LuzZza]
+char main_chat_nick[16] = "Main";
+
+char *INTER_CONF_NAME;
+char *LOG_CONF_NAME;
+char *MAP_CONF_NAME;
+char *BATTLE_CONF_FILENAME;
+char *ATCOMMAND_CONF_FILENAME;
+char *CHARCOMMAND_CONF_FILENAME;
+char *SCRIPT_CONF_NAME;
+char *MSG_CONF_NAME;
+char *GRF_PATH_FILENAME;
+
+// 極力 staticでロ?カルに?める
+static struct dbt * id_db=NULL;
+static struct dbt * pc_db=NULL;
+static struct dbt * map_db=NULL;
+static struct dbt * charid_db=NULL;
+
+static int map_users=0;
+static struct block_list *objects[MAX_FLOORITEM];
+static int first_free_object_id=0,last_object_id=0;
+
+#define block_free_max 1048576
+struct block_list *block_free[block_free_max];
+static int block_free_count = 0, block_free_lock = 0;
+
+#define BL_LIST_MAX 1048576
+static struct block_list *bl_list[BL_LIST_MAX];
+static int bl_list_count = 0;
+
+static char afm_dir[1024] = ""; // [Valaris]
+
+struct map_data map[MAX_MAP_PER_SERVER];
+int map_num = 0;
+
+int map_port=0;
+
+int autosave_interval = DEFAULT_AUTOSAVE_INTERVAL;
+int charsave_method = 0; //Default 'OLD' Save method (SQL ONLY!) [Sirius]
+int agit_flag = 0;
+int night_flag = 0; // 0=day, 1=night [Yor]
+int kick_on_disconnect = 1;
+
+struct charid2nick {
+ char nick[NAME_LENGTH];
+ int req_id;
+};
+
+// ォ゙ォテォラォュォ罩テォキォ袮ラ鯑ォユォ鬮ー(map_athana.conf?ェホread_map_from_cacheェヌヲ・)
+// 0:ララ鯑ェキェハェ、 1:゙ェ?ワチ 2:?ワチ
+int map_read_flag = READ_FROM_GAT;
+char map_cache_file[256]="db/map.info"; // ォ゙ォテォラォュォ罩テォキォ雖ユォ。ォ、ォ・」
+
+char db_path[256] = "db";
+char motd_txt[256] = "conf/motd.txt";
+char help_txt[256] = "conf/help.txt";
+char help2_txt[256] = "conf/help2.txt";
+char charhelp_txt[256] = "conf/charhelp.txt";
+
+char wisp_server_name[NAME_LENGTH] = "Server"; // can be modified in char-server configuration file
+
+int console = 0;
+int enable_spy = 0; //To enable/disable @spy commands, which consume too much cpu time when sending packets. [Skotlex]
+
+static const int dirx[8]={0,-1,-1,-1,0,1,1,1};
+static const int diry[8]={1,1,0,-1,-1,-1,0,1};
+
+/*==========================================
+ * 全map鯖?計での接??設定
+ * (char鯖から送られてくる)
+ *------------------------------------------
+ */
+void map_setusers(int fd)
+{
+ RFIFOHEAD(fd);
+ WFIFOHEAD(fd, 2);
+
+ map_users = RFIFOL(fd,2);
+ // send some anser
+ WFIFOW(fd,0) = 0x2718;
+ WFIFOSET(fd,2);
+}
+
+/*==========================================
+ * 全map鯖?計での接??取得 (/wへの?答用)
+ *------------------------------------------
+ */
+int map_getusers(void) {
+ return map_users;
+}
+
+
+//Distance functions, taken from http://www.flipcode.com/articles/article_fastdistance.shtml
+int check_distance(int dx, int dy, int distance) {
+ //In this case, we just do a square comparison. Add 1 tile grace for diagonal range checks.
+ return (dx*dx + dy*dy <= distance*distance + (dx&&dy?1:0));
+}
+
+unsigned int distance(int dx, int dy) {
+ unsigned int min, max;
+
+ if ( dx < 0 ) dx = -dx;
+ if ( dy < 0 ) dy = -dy;
+ //There appears to be something wrong with the aproximation below when either dx/dy is 0! [Skotlex]
+ if ( dx == 0 ) return dy;
+ if ( dy == 0 ) return dx;
+
+ if ( dx < dy )
+ {
+ min = dx;
+ max = dy;
+ } else {
+ min = dy;
+ max = dx;
+ }
+ // coefficients equivalent to ( 123/128 * max ) and ( 51/128 * min )
+ return ((( max << 8 ) + ( max << 3 ) - ( max << 4 ) - ( max << 1 ) +
+ ( min << 7 ) - ( min << 5 ) + ( min << 3 ) - ( min << 1 )) >> 8 );
+}
+
+//
+// block削除の安全性確保?理
+//
+
+/*==========================================
+ * blockをfreeするときfreeの?わりに呼ぶ
+ * ロックされているときはバッファにためる
+ *------------------------------------------
+ */
+int map_freeblock (struct block_list *bl)
+{
+ if (block_free_lock == 0 || block_free_count >= block_free_max)
+ {
+ aFree(bl);
+ bl = NULL;
+ if (block_free_count >= block_free_max)
+ if (battle_config.error_log)
+ ShowWarning("map_freeblock: too many free block! %d %d\n",
+ block_free_count, block_free_lock);
+ } else
+ block_free[block_free_count++] = bl;
+
+ return block_free_lock;
+}
+/*==========================================
+ * blockのfreeを一市Iに禁止する
+ *------------------------------------------
+ */
+int map_freeblock_lock (void)
+{
+ return ++block_free_lock;
+}
+
+/*==========================================
+ * blockのfreeのロックを解除する
+ * このとき、ロックが完全になくなると
+ * バッファにたまっていたblockを全部削除
+ *------------------------------------------
+ */
+int map_freeblock_unlock (void)
+{
+ if ((--block_free_lock) == 0) {
+ int i;
+ for (i = 0; i < block_free_count; i++)
+ { //Directly calling aFree shouldn't be a leak, as Free remembers the size the original pointed to memory was allocated with? [Skotlex]
+ aFree(block_free[i]);
+ block_free[i] = NULL;
+ }
+ block_free_count = 0;
+ } else if (block_free_lock < 0) {
+ if (battle_config.error_log)
+ ShowError("map_freeblock_unlock: lock count < 0 !\n");
+ block_free_lock = 0; // 次回以降のロックに支障が出てくるのでリセット
+ }
+
+ return block_free_lock;
+}
+
+// map_freeblock_lock() を呼んで map_freeblock_unlock() を呼ばない
+// 関数があったので、定期的にblock_free_lockをリセットするようにする。
+// この関数は、do_timer() のトップレベルから呼ばれるので、
+// block_free_lock を直接いじっても支障無いはず。
+
+int map_freeblock_timer (int tid, unsigned int tick, int id, int data)
+{
+ if (block_free_lock > 0) {
+ ShowError("map_freeblock_timer: block_free_lock(%d) is invalid.\n", block_free_lock);
+ block_free_lock = 1;
+ map_freeblock_unlock();
+ }
+
+ return 0;
+}
+
+//
+// block化?理
+//
+/*==========================================
+ * map[]のblock_listから?がっている場合に
+ * bl->prevにbl_headのアドレスを入れておく
+ *------------------------------------------
+ */
+static struct block_list bl_head;
+
+#ifdef CELL_NOSTACK
+/*==========================================
+ * These pair of functions update the counter of how many objects
+ * lie on a tile.
+ *------------------------------------------
+ */
+void map_addblcell(struct block_list *bl)
+{
+ if(bl->m<0 || bl->x<0 || bl->x>=map[bl->m].xs
+ || bl->y<0 || bl->y>=map[bl->m].ys || !(bl->type&BL_CHAR))
+ return;
+ map[bl->m].cell_bl[bl->x+bl->y*map[bl->m].xs]++;
+ return;
+}
+
+void map_delblcell(struct block_list *bl)
+{
+ if(bl->m <0 || bl->x<0 || bl->x>=map[bl->m].xs
+ || bl->y<0 || bl->y>=map[bl->m].ys || !(bl->type&BL_CHAR))
+ return;
+ map[bl->m].cell_bl[bl->x+bl->y*map[bl->m].xs]--;
+}
+#endif
+
+/*==========================================
+ * Adds a block to the map.
+ * If flag is 1, then the block was just added
+ * otherwise it is part of a transition.
+ *------------------------------------------
+ */
+int map_addblock_sub (struct block_list *bl, int flag)
+{
+ int m, x, y, pos;
+
+ nullpo_retr(0, bl);
+
+ if (bl->prev != NULL) {
+ if(battle_config.error_log)
+ ShowError("map_addblock error : bl->prev != NULL\n");
+ return 0;
+ }
+
+ m = bl->m;
+ x = bl->x;
+ y = bl->y;
+ if (m < 0 || m >= map_num ||
+ x < 0 || x >= map[m].xs ||
+ y < 0 || y >= map[m].ys)
+ return 1;
+
+#ifdef CELL_NOSTACK
+ map_addblcell(bl);
+#endif
+
+ pos = x/BLOCK_SIZE+(y/BLOCK_SIZE)*map[m].bxs;
+ if (bl->type == BL_MOB) {
+ bl->next = map[m].block_mob[pos];
+ bl->prev = &bl_head;
+ if (bl->next) bl->next->prev = bl;
+ map[m].block_mob[pos] = bl;
+ map[m].block_mob_count[pos]++;
+ } else {
+ bl->next = map[m].block[pos];
+ bl->prev = &bl_head;
+ if (bl->next) bl->next->prev = bl;
+ map[m].block[pos] = bl;
+ map[m].block_count[pos]++;
+ if (bl->type == BL_PC && flag)
+ {
+ struct map_session_data* sd;
+ if (map[m].users++ == 0 && battle_config.dynamic_mobs) //Skotlex
+ map_spawnmobs(m);
+ sd = (struct map_session_data*)bl;
+ if (battle_config.pet_no_gvg && map_flag_gvg(m) && sd->pd)
+ { //Return the pet to egg. [Skotlex]
+ clif_displaymessage(sd->fd, "Pets are not allowed in Guild Wars.");
+ pet_menu(sd, 3); //Option 3 is return to egg.
+ }
+ }
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * Removes a block from the map.
+ * If flag is 1, then the block is removed for good
+ * otherwise it is part of a transition.
+ *------------------------------------------
+ */
+int map_delblock_sub (struct block_list *bl, int flag)
+{
+ int b;
+ nullpo_retr(0, bl);
+
+ // ?にblocklistから?けている
+ if (bl->prev == NULL) {
+ if (bl->next != NULL) {
+ // prevがNULLでnextがNULLでないのは有ってはならない
+ if(battle_config.error_log)
+ ShowError("map_delblock error : bl->next!=NULL\n");
+ }
+ return 0;
+ }
+
+#ifdef CELL_NOSTACK
+ map_delblcell(bl);
+#endif
+
+ b = bl->x/BLOCK_SIZE+(bl->y/BLOCK_SIZE)*map[bl->m].bxs;
+
+ if (bl->type == BL_PC && flag)
+ if (--map[bl->m].users == 0 && battle_config.dynamic_mobs) //[Skotlex]
+ map_removemobs(bl->m);
+
+ if (bl->next)
+ bl->next->prev = bl->prev;
+ if (bl->prev == &bl_head) {
+ // リストの頭なので、map[]のblock_listを更新する
+ if (bl->type == BL_MOB) {
+ map[bl->m].block_mob[b] = bl->next;
+ if ((map[bl->m].block_mob_count[b]--) < 0)
+ map[bl->m].block_mob_count[b] = 0;
+ } else {
+ map[bl->m].block[b] = bl->next;
+ if((map[bl->m].block_count[b]--) < 0)
+ map[bl->m].block_count[b] = 0;
+ }
+ } else {
+ bl->prev->next = bl->next;
+ }
+ bl->next = NULL;
+ bl->prev = NULL;
+
+ return 0;
+}
+
+/*==========================================
+ * Moves a block a x/y target position. [Skotlex]
+ * Pass flag as 1 to prevent doing skill_unit_move checks
+ * (which are executed by default on BL_CHAR types)
+ *------------------------------------------
+ */
+int map_moveblock(struct block_list *bl, int x1, int y1, unsigned int tick) {
+ int x0 = bl->x, y0 = bl->y;
+ int moveblock = ( x0/BLOCK_SIZE != x1/BLOCK_SIZE || y0/BLOCK_SIZE != y1/BLOCK_SIZE);
+
+ if (!bl->prev) {
+ //Block not in map, just update coordinates, but do naught else.
+ bl->x = x1;
+ bl->y = y1;
+ return 0;
+ }
+ //TODO: Perhaps some outs of bounds checking should be placed here?
+ if (bl->type&BL_CHAR)
+ skill_unit_move(bl,tick,2);
+
+ if (moveblock) map_delblock_sub(bl,0);
+#ifdef CELL_NOSTACK
+ else map_delblcell(bl);
+#endif
+ bl->x = x1;
+ bl->y = y1;
+ if (moveblock) map_addblock_sub(bl,0);
+#ifdef CELL_NOSTACK
+ else map_addblcell(bl);
+#endif
+ if (bl->type&BL_CHAR)
+ skill_unit_move(bl,tick,3);
+ return 0;
+}
+
+/*==========================================
+ * 周?のPC人?を?える (unused)
+ *------------------------------------------
+ */
+int map_countnearpc (int m, int x, int y)
+{
+ int bx, by, c = 0;
+ struct block_list *bl=NULL;
+
+ if (map[m].users == 0)
+ return 0;
+ for (by = y/BLOCK_SIZE-AREA_SIZE/BLOCK_SIZE-1; by<=y/BLOCK_SIZE+AREA_SIZE/BLOCK_SIZE+1; by++) {
+ if (by < 0 || by >= map[m].bys)
+ continue;
+ for (bx = x/BLOCK_SIZE-AREA_SIZE/BLOCK_SIZE-1; bx <= x/BLOCK_SIZE+AREA_SIZE/BLOCK_SIZE+1; bx++) {
+ if (bx < 0 || bx >= map[m].bxs)
+ continue;
+ bl = map[m].block[bx+by*map[m].bxs];
+ while(bl) {
+ if (bl->type == BL_PC)
+ c++;
+ bl = bl->next;
+ }
+ }
+ }
+
+ return c;
+}
+
+/*==========================================
+ * Counts specified number of objects on given cell.
+ *------------------------------------------
+ */
+int map_count_oncell(int m, int x, int y, int type) {
+ int bx,by;
+ struct block_list *bl=NULL;
+ int i,c;
+ int count = 0;
+
+ if (x < 0 || y < 0 || (x >= map[m].xs) || (y >= map[m].ys))
+ return 0;
+ bx = x/BLOCK_SIZE;
+ by = y/BLOCK_SIZE;
+
+ if (type == 0 || type != BL_MOB)
+ {
+ bl = map[m].block[bx+by*map[m].bxs];
+ c = map[m].block_count[bx+by*map[m].bxs];
+ for(i=0;i<c && bl;i++,bl=bl->next)
+ if(bl->x == x && bl->y == y && bl->type == (type?type:BL_PC)) count++;
+ }
+
+ if (type == 0 || type == BL_MOB)
+ {
+ bl = map[m].block_mob[bx+by*map[m].bxs];
+ c = map[m].block_mob_count[bx+by*map[m].bxs];
+ for(i=0;i<c && bl;i++,bl=bl->next){
+ if(bl->x == x && bl->y == y) count++;
+ }
+ }
+ return count;
+}
+/*
+ * ォサォ・セェホフェヒフクェトェアェソォケォュォ・讚ヒォテォネェレェケ
+ */
+struct skill_unit *map_find_skill_unit_oncell(struct block_list *target,int x,int y,int skill_id,struct skill_unit *out_unit)
+{
+ int m,bx,by;
+ struct block_list *bl;
+ int i,c;
+ struct skill_unit *unit;
+ m = target->m;
+
+ if (x < 0 || y < 0 || (x >= map[m].xs) || (y >= map[m].ys))
+ return NULL;
+ bx = x/BLOCK_SIZE;
+ by = y/BLOCK_SIZE;
+
+ bl = map[m].block[bx+by*map[m].bxs];
+ c = map[m].block_count[bx+by*map[m].bxs];
+ for(i=0;i<c && bl;i++,bl=bl->next){
+ if (bl->x != x || bl->y != y || bl->type != BL_SKILL)
+ continue;
+ unit = (struct skill_unit *) bl;
+ if (unit==out_unit || !unit->alive ||
+ !unit->group || unit->group->skill_id!=skill_id)
+ continue;
+ if (battle_check_target(&unit->bl,target,unit->group->target_flag)>0)
+ return unit;
+ }
+ return NULL;
+}
+
+/*==========================================
+ * map m (x0,y0)-(x1,y1)?の全objに?して
+ * funcを呼ぶ
+ * type!=0 ならその種類のみ
+ *------------------------------------------
+ */
+int map_foreachinarea(int (*func)(struct block_list*,va_list),int m,int x0,int y0,int x1,int y1,int type,...) {
+ va_list ap;
+ int bx,by;
+ int returnCount =0; //total sum of returned values of func() [Skotlex]
+ struct block_list *bl=NULL;
+ int blockcount=bl_list_count,i,c;
+
+ if (m < 0)
+ return 0;
+ va_start(ap,type);
+ if (x1 < x0)
+ { //Swap range
+ bx = x0;
+ x0 = x1;
+ x1 = bx;
+ }
+ if (y1 < y0)
+ {
+ bx = y0;
+ y0 = y1;
+ y1 = bx;
+ }
+ if (x0 < 0) x0 = 0;
+ if (y0 < 0) y0 = 0;
+ if (x1 >= map[m].xs) x1 = map[m].xs-1;
+ if (y1 >= map[m].ys) y1 = map[m].ys-1;
+
+ if (type&~BL_MOB)
+ for (by = y0 / BLOCK_SIZE; by <= y1 / BLOCK_SIZE; by++) {
+ for(bx=x0/BLOCK_SIZE;bx<=x1/BLOCK_SIZE;bx++){
+ bl = map[m].block[bx+by*map[m].bxs];
+ c = map[m].block_count[bx+by*map[m].bxs];
+ for(i=0;i<c && bl;i++,bl=bl->next){
+ if(bl && bl->type&type && bl->x>=x0 && bl->x<=x1 && bl->y>=y0 && bl->y<=y1 && bl_list_count<BL_LIST_MAX)
+ bl_list[bl_list_count++]=bl;
+ }
+ }
+ }
+ if(type&BL_MOB)
+ for(by=y0/BLOCK_SIZE;by<=y1/BLOCK_SIZE;by++){
+ for(bx=x0/BLOCK_SIZE;bx<=x1/BLOCK_SIZE;bx++){
+ bl = map[m].block_mob[bx+by*map[m].bxs];
+ c = map[m].block_mob_count[bx+by*map[m].bxs];
+ for(i=0;i<c && bl;i++,bl=bl->next){
+ if(bl && bl->x>=x0 && bl->x<=x1 && bl->y>=y0 && bl->y<=y1 && bl_list_count<BL_LIST_MAX)
+ bl_list[bl_list_count++]=bl;
+ }
+ }
+ }
+
+ if(bl_list_count>=BL_LIST_MAX) {
+ if(battle_config.error_log)
+ ShowWarning("map_foreachinarea: block count too many!\n");
+ }
+
+ map_freeblock_lock(); // メモリからの解放を禁止する
+
+ for(i=blockcount;i<bl_list_count;i++)
+ if(bl_list[i]->prev) // 有?かどうかチェック
+ returnCount += func(bl_list[i],ap);
+
+ map_freeblock_unlock(); // 解放を許可する
+
+ va_end(ap);
+ bl_list_count = blockcount;
+ return returnCount; //[Skotlex]
+}
+
+/*==========================================
+ * 矩形(x0,y0)-(x1,y1)が(dx,dy)移動した暫フ
+ * 領域外になる領域(矩形かL字形)?のobjに
+ * ?してfuncを呼ぶ
+ *
+ * dx,dyは-1,0,1のみとする(どんな値でもいいっぽい?)
+ *------------------------------------------
+ */
+int map_foreachinmovearea(int (*func)(struct block_list*,va_list),int m,int x0,int y0,int x1,int y1,int dx,int dy,int type,...) {
+ int bx,by;
+ int returnCount =0; //total sum of returned values of func() [Skotlex]
+ struct block_list *bl=NULL;
+ va_list ap;
+ int blockcount=bl_list_count,i,c;
+
+ va_start(ap,type);
+ if (x1 < x0)
+ { //Swap range
+ bx = x0;
+ x0 = x1;
+ x1 = bx;
+ }
+ if (y1 < y0)
+ {
+ bx = y0;
+ y0 = y1;
+ y1 = bx;
+ }
+ if(dx==0 || dy==0){
+ // 矩形領域の場合
+ if(dx==0){
+ if(dy<0){
+ y0=y1+dy+1;
+ } else {
+ y1=y0+dy-1;
+ }
+ } else if(dy==0){
+ if(dx<0){
+ x0=x1+dx+1;
+ } else {
+ x1=x0+dx-1;
+ }
+ }
+ if(x0<0) x0=0;
+ if(y0<0) y0=0;
+ if(x1>=map[m].xs) x1=map[m].xs-1;
+ if(y1>=map[m].ys) y1=map[m].ys-1;
+ for(by=y0/BLOCK_SIZE;by<=y1/BLOCK_SIZE;by++){
+ for(bx=x0/BLOCK_SIZE;bx<=x1/BLOCK_SIZE;bx++){
+ if (type&~BL_MOB) {
+ bl = map[m].block[bx+by*map[m].bxs];
+ c = map[m].block_count[bx+by*map[m].bxs];
+ for(i=0;i<c && bl;i++,bl=bl->next){
+ if(bl && bl->type&type && bl->x>=x0 && bl->x<=x1 && bl->y>=y0 && bl->y<=y1 && bl_list_count<BL_LIST_MAX)
+ bl_list[bl_list_count++]=bl;
+ }
+ }
+ if (type&BL_MOB) {
+ bl = map[m].block_mob[bx+by*map[m].bxs];
+ c = map[m].block_mob_count[bx+by*map[m].bxs];
+ for(i=0;i<c && bl;i++,bl=bl->next){
+ if(bl && bl->x>=x0 && bl->x<=x1 && bl->y>=y0 && bl->y<=y1 && bl_list_count<BL_LIST_MAX)
+ bl_list[bl_list_count++]=bl;
+ }
+ }
+ }
+ }
+ }else{
+ // L字領域の場合
+
+ if(x0<0) x0=0;
+ if(y0<0) y0=0;
+ if(x1>=map[m].xs) x1=map[m].xs-1;
+ if(y1>=map[m].ys) y1=map[m].ys-1;
+ for(by=y0/BLOCK_SIZE;by<=y1/BLOCK_SIZE;by++){
+ for(bx=x0/BLOCK_SIZE;bx<=x1/BLOCK_SIZE;bx++){
+ if (type & ~BL_MOB) {
+ bl = map[m].block[bx+by*map[m].bxs];
+ c = map[m].block_count[bx+by*map[m].bxs];
+ for(i=0;i<c && bl;i++,bl=bl->next){
+ if(!bl || !(bl->x>=x0 && bl->x<=x1 && bl->y>=y0 && bl->y<=y1))
+ continue;
+ if(bl && bl->type&type && ((dx>0 && bl->x<x0+dx) || (dx<0 && bl->x>x1+dx) ||
+ (dy>0 && bl->y<y0+dy) || (dy<0 && bl->y>y1+dy)) &&
+ bl_list_count<BL_LIST_MAX)
+ bl_list[bl_list_count++]=bl;
+ }
+ }
+ if (type & BL_MOB) {
+ bl = map[m].block_mob[bx+by*map[m].bxs];
+ c = map[m].block_mob_count[bx+by*map[m].bxs];
+ for(i=0;i<c && bl;i++,bl=bl->next){
+ if(!bl || !(bl->x>=x0 && bl->x<=x1 && bl->y>=y0 && bl->y<=y1))
+ continue;
+ if(bl && ((dx>0 && bl->x<x0+dx) || (dx<0 && bl->x>x1+dx) ||
+ (dy>0 && bl->y<y0+dy) || (dy<0 && bl->y>y1+dy)) &&
+ bl_list_count<BL_LIST_MAX)
+ bl_list[bl_list_count++]=bl;
+ }
+ }
+ }
+ }
+
+ }
+
+ if(bl_list_count>=BL_LIST_MAX) {
+ if(battle_config.error_log)
+ ShowWarning("map_foreachinmovearea: block count too many!\n");
+ }
+
+ map_freeblock_lock(); // メモリからの解放を禁止する
+
+ for(i=blockcount;i<bl_list_count;i++)
+ if(bl_list[i]->prev) { // 有?かどうかチェック
+ if (bl_list[i]->type == BL_PC
+ && session[((struct map_session_data *) bl_list[i])->fd] == NULL)
+ continue;
+ returnCount += func(bl_list[i],ap);
+ }
+
+ map_freeblock_unlock(); // 解放を許可する
+
+ va_end(ap);
+ bl_list_count = blockcount;
+ return returnCount;
+}
+
+// -- moonsoul (added map_foreachincell which is a rework of map_foreachinarea but
+// which only checks the exact single x/y passed to it rather than an
+// area radius - may be more useful in some instances)
+//
+int map_foreachincell(int (*func)(struct block_list*,va_list),int m,int x,int y,int type,...) {
+ int bx,by;
+ int returnCount =0; //total sum of returned values of func() [Skotlex]
+ struct block_list *bl=NULL;
+ va_list ap;
+ int blockcount=bl_list_count,i,c;
+
+ va_start(ap,type);
+
+ by=y/BLOCK_SIZE;
+ bx=x/BLOCK_SIZE;
+
+ if(type&~BL_MOB)
+ {
+ bl = map[m].block[bx+by*map[m].bxs];
+ c = map[m].block_count[bx+by*map[m].bxs];
+ for(i=0;i<c && bl;i++,bl=bl->next)
+ {
+ if(bl && bl->type&type && bl->x==x && bl->y==y && bl_list_count<BL_LIST_MAX)
+ bl_list[bl_list_count++]=bl;
+ }
+ }
+
+ if(type&BL_MOB)
+ {
+ bl = map[m].block_mob[bx+by*map[m].bxs];
+ c = map[m].block_mob_count[bx+by*map[m].bxs];
+ for(i=0;i<c && bl;i++,bl=bl->next)
+ {
+ if(bl && bl->x==x && bl->y==y && bl_list_count<BL_LIST_MAX)
+ bl_list[bl_list_count++]=bl;
+ }
+ }
+
+ if(bl_list_count>=BL_LIST_MAX) {
+ if(battle_config.error_log)
+ ShowWarning("map_foreachincell: block count too many!\n");
+ }
+
+ map_freeblock_lock(); // メモリからの解放を禁止する
+
+ for(i=blockcount;i<bl_list_count;i++)
+ if(bl_list[i]->prev) // 有?かどうかチェック
+ returnCount += func(bl_list[i],ap);
+
+ map_freeblock_unlock(); // 解放を許可する
+
+ va_end(ap);
+ bl_list_count = blockcount;
+ return returnCount;
+}
+
+/*============================================================
+* For checking a path between two points (x0, y0) and (x1, y1)
+*------------------------------------------------------------
+ */
+int map_foreachinpath(int (*func)(struct block_list*,va_list),int m,int x0,int y0,int x1,int y1,int range,int type,...)
+{
+ int returnCount =0; //total sum of returned values of func() [Skotlex]
+//////////////////////////////////////////////////////////////
+//
+// sharp shooting 3 [Skotlex]
+//
+//////////////////////////////////////////////////////////////
+// problem:
+// Same as Sharp Shooting 1. Hits all targets within range of
+// the line.
+// (t1,t2 t3 and t4 get hit)
+//
+// target 1
+// x t4
+// t2
+// t3 x
+// x
+// S
+//////////////////////////////////////////////////////////////
+// Methodology:
+// My trigonometrics and math is a little rusty... so the approach I am writing
+// here is basicly do a double for to check for all targets in the square that
+// contains the initial and final positions (area range increased to match the
+// radius given), then for each object to test, calculate the distance to the
+// path and include it if the range fits and the target is in the line (0<k<1,
+// as they call it).
+// The implementation I took as reference is found at
+// http://astronomy.swin.edu.au/~pbourke/geometry/pointline/
+// (they have a link to a C implementation, too)
+// This approach is a lot like #2 commented on this function, which I have no
+// idea why it was commented. I won't use doubles/floats, but pure int math for speed purposes
+// The range considered is always the same no matter how close/far the target is because that's
+// how SharpShooting works currently in kRO.
+
+ //Generic map_foreach* variables.
+ va_list ap;
+ int i, blockcount = bl_list_count;
+ struct block_list *bl;
+ int c, bx, by;
+ //method specific variables
+ int magnitude2; //The square of the magnitude
+ int k, xi, yi, xu, yu;
+ int mx0 = x0, mx1 = x1, my0 = y0, my1 = y1;
+
+ //Avoid needless calculations by not getting the sqrt right away.
+ #define MAGNITUDE2(x0, y0, x1, y1) (((x1)-(x0))*((x1)-(x0)) + ((y1)-(y0))*((y1)-(y0)))
+
+ if (m < 0)
+ return 0;
+
+ va_start(ap,type);
+
+ //Expand target area to cover range.
+ if (mx0 > mx1)
+ {
+ mx0+=range;
+ mx1-=range;
+ } else {
+ mx0-=range;
+ mx1+=range;
+ }
+ if (my0 > my1)
+ {
+ my0+=range;
+ my1-=range;
+ } else {
+ my0-=range;
+ my1+=range;
+ }
+
+ //The two fors assume mx0 < mx1 && my0 < my1
+ if (mx0 > mx1)
+ {
+ k = mx1;
+ mx1 = mx0;
+ mx0 = k;
+ }
+ if (my0 > my1)
+ {
+ k = my1;
+ my1 = my0;
+ my0 = k;
+ }
+
+ if (mx0 < 0) mx0 = 0;
+ if (my0 < 0) my0 = 0;
+ if (mx1 >= map[m].xs) mx1 = map[m].xs-1;
+ if (my1 >= map[m].ys) my1 = map[m].ys-1;
+
+ range*=range<<8; //Values are shifted later on for higher precision using int math.
+ magnitude2 = MAGNITUDE2(x0,y0, x1,y1);
+ if (magnitude2 < 1) //Same begin and ending point, can't trace path.
+ return 0;
+
+ if (type & ~BL_MOB)
+ for (by = my0 / BLOCK_SIZE; by <= my1 / BLOCK_SIZE; by++) {
+ for(bx=mx0/BLOCK_SIZE;bx<=mx1/BLOCK_SIZE;bx++){
+ bl = map[m].block[bx+by*map[m].bxs];
+ c = map[m].block_count[bx+by*map[m].bxs];
+ for(i=0;i<c && bl;i++,bl=bl->next){
+ if(bl && bl->type&type && bl_list_count<BL_LIST_MAX)
+ {
+ xi = bl->x;
+ yi = bl->y;
+
+ k = (xi-x0)*(x1-x0) + (yi-y0)*(y1-y0);
+ if (k < 0)// || k > magnitude2) //No check to see if it lies after the target's point.
+ continue;
+
+ //All these shifts are to increase the precision of the intersection point and distance considering how it's
+ //int math.
+ k = (k<<4)/magnitude2; //k will be between 1~16 instead of 0~1
+ xi<<=4;
+ yi<<=4;
+ xu= (x0<<4) +k*(x1-x0);
+ yu= (y0<<4) +k*(y1-y0);
+ k = MAGNITUDE2(xi, yi, xu, yu);
+
+ //If all dot coordinates were <<4 the square of the magnitude is <<8
+ if (k > range)
+ continue;
+
+ bl_list[bl_list_count++]=bl;
+ }
+ }
+ }
+ }
+ if(type&BL_MOB)
+ for(by=my0/BLOCK_SIZE;by<=my1/BLOCK_SIZE;by++){
+ for(bx=mx0/BLOCK_SIZE;bx<=mx1/BLOCK_SIZE;bx++){
+ bl = map[m].block_mob[bx+by*map[m].bxs];
+ c = map[m].block_mob_count[bx+by*map[m].bxs];
+ for(i=0;i<c && bl;i++,bl=bl->next){
+ if(bl && bl_list_count<BL_LIST_MAX)
+ {
+ xi = bl->x;
+ yi = bl->y;
+ k = (xi-x0)*(x1-x0) + (yi-y0)*(y1-y0);
+ if (k < 0)// || k > magnitude2) //No check to see if it lies after the target's point.
+ continue;
+
+ k = (k<<4)/magnitude2; //k will be between 1~16 instead of 0~1
+ xi<<=4;
+ yi<<=4;
+ xu= (x0<<4) +k*(x1-x0);
+ yu= (y0<<4) +k*(y1-y0);
+ k = MAGNITUDE2(xi, yi, xu, yu);
+
+ //If all dot coordinates were <<4 the square of the magnitude is <<8
+ if (k > range)
+ continue;
+
+ bl_list[bl_list_count++]=bl;
+ }
+ }
+ }
+ }
+
+ if(bl_list_count>=BL_LIST_MAX) {
+ if(battle_config.error_log)
+ ShowWarning("map_foreachinpath: block count too many!\n");
+ }
+
+ map_freeblock_lock(); // メモリからの解放を禁止する
+
+ for(i=blockcount;i<bl_list_count;i++)
+ if(bl_list[i]->prev) // 有?かどうかチェック
+ returnCount += func(bl_list[i],ap);
+
+ map_freeblock_unlock(); // 解放を許可する
+
+ va_end(ap);
+ bl_list_count = blockcount;
+ return returnCount; //[Skotlex]
+
+}
+
+// Copy of map_foreachincell, but applied to the whole map. [Skotlex]
+int map_foreachinmap(int (*func)(struct block_list*,va_list),int m,int type,...) {
+ int b, bsize;
+ int returnCount =0; //total sum of returned values of func() [Skotlex]
+ struct block_list *bl=NULL;
+ va_list ap;
+ int blockcount=bl_list_count,i,c;
+
+ va_start(ap,type);
+
+ bsize = map[m].bxs * map[m].bys;
+ if(type&~BL_MOB)
+ {
+ for(b=0;b<bsize;b++){
+ bl = map[m].block[b];
+ c = map[m].block_count[b];
+ for(i=0;i<c && bl;i++,bl=bl->next)
+ {
+ if(bl && bl->type&type && bl_list_count<BL_LIST_MAX)
+ bl_list[bl_list_count++]=bl;
+ }
+ }
+ }
+
+ if(type&BL_MOB)
+ {
+ for(b=0;b<bsize;b++){
+ bl = map[m].block_mob[b];
+ c = map[m].block_mob_count[b];
+ for(i=0;i<c && bl;i++,bl=bl->next)
+ {
+ if(bl && bl_list_count<BL_LIST_MAX)
+ bl_list[bl_list_count++]=bl;
+ }
+ }
+ }
+
+ if(bl_list_count>=BL_LIST_MAX) {
+ if(battle_config.error_log)
+ ShowWarning("map_foreachinmap: block count too many!\n");
+ }
+
+ map_freeblock_lock(); // メモリからの解放を禁止する
+
+ for(i=blockcount;i<bl_list_count;i++)
+ if(bl_list[i]->prev) // 有?かどうかチェック
+ returnCount += func(bl_list[i],ap);
+
+ map_freeblock_unlock(); // 解放を許可する
+
+ va_end(ap);
+ bl_list_count = blockcount;
+ return returnCount;
+}
+
+/*==========================================
+ * 床アイテムやエフェクト用の一三bj割り?て
+ * object[]への保存とid_db登?まで
+ *
+ * bl->idもこの中で設定して問題無い?
+ *------------------------------------------
+ */
+int map_addobject(struct block_list *bl) {
+ int i;
+ if( bl == NULL ){
+ ShowWarning("map_addobject nullpo?\n");
+ return 0;
+ }
+ if(first_free_object_id<2 || first_free_object_id>=MAX_FLOORITEM)
+ first_free_object_id=2;
+ for(i=first_free_object_id;i<MAX_FLOORITEM && objects[i];i++);
+ if(i>=MAX_FLOORITEM){
+ if(battle_config.error_log)
+ ShowWarning("no free object id\n");
+ return 0;
+ }
+ first_free_object_id=i;
+ if(last_object_id<i)
+ last_object_id=i;
+ objects[i]=bl;
+ idb_put(id_db,i,bl);
+ return i;
+}
+
+/*==========================================
+ * 一三bjectの解放
+ * map_delobjectのfreeしないバ?ジョン
+ *------------------------------------------
+ */
+int map_delobjectnofree(int id) {
+ if(objects[id]==NULL)
+ return 0;
+
+ map_delblock(objects[id]);
+ idb_remove(id_db,id);
+ objects[id]=NULL;
+
+ if(first_free_object_id>id)
+ first_free_object_id=id;
+
+ while(last_object_id>2 && objects[last_object_id]==NULL)
+ last_object_id--;
+
+ return 0;
+}
+
+/*==========================================
+ * 一三bjectの解放
+ * block_listからの削除、id_dbからの削除
+ * object dataのfree、object[]へのNULL代入
+ *
+ * addとの??性が無いのが?になる
+ *------------------------------------------
+ */
+int map_delobject(int id) {
+ struct block_list *obj = objects[id];
+
+ if(obj==NULL)
+ return 0;
+
+ map_delobjectnofree(id);
+ map_freeblock(obj);
+
+ return 0;
+}
+
+/*==========================================
+ * 全一三bj相手にfuncを呼ぶ
+ *
+ *------------------------------------------
+ */
+void map_foreachobject(int (*func)(struct block_list*,va_list),int type,...) {
+ int i;
+ int blockcount=bl_list_count;
+ va_list ap;
+
+ va_start(ap,type);
+
+ for(i=2;i<=last_object_id;i++){
+ if(objects[i]){
+ if(!(objects[i]->type&type))
+ continue;
+ if(bl_list_count>=BL_LIST_MAX) {
+ if(battle_config.error_log)
+ ShowWarning("map_foreachobject: too many blocks !\n");
+ break;
+ }
+ bl_list[bl_list_count++]=objects[i];
+ }
+ }
+
+ map_freeblock_lock();
+
+ for(i=blockcount;i<bl_list_count;i++)
+ if( bl_list[i]->prev || bl_list[i]->next )
+ func(bl_list[i],ap);
+
+ map_freeblock_unlock();
+
+ va_end(ap);
+ bl_list_count = blockcount;
+}
+
+/*==========================================
+ * 床アイテムを消す
+ *
+ * data==0の暫ヘtimerで消えた殊 * data!=0の暫ヘ拾う等で消えた暫ニして動作
+ *
+ * 後者は、map_clearflooritem(id)へ
+ * map.h?で#defineしてある
+ *------------------------------------------
+ */
+int map_clearflooritem_timer(int tid,unsigned int tick,int id,int data) {
+ struct flooritem_data *fitem=NULL;
+
+ fitem = (struct flooritem_data *)objects[id];
+ if(fitem==NULL || fitem->bl.type!=BL_ITEM || (!data && fitem->cleartimer != tid)){
+ if(battle_config.error_log)
+ ShowError("map_clearflooritem_timer : error\n");
+ return 1;
+ }
+ if(data)
+ delete_timer(fitem->cleartimer,map_clearflooritem_timer);
+ else if(fitem->item_data.card[0] == (short)0xff00)
+ intif_delete_petdata( MakeDWord(fitem->item_data.card[1],fitem->item_data.card[2]) );
+ clif_clearflooritem(fitem,0);
+ map_delobject(fitem->bl.id);
+
+ return 0;
+}
+
+/*==========================================
+ * (m,x,y)の周?rangeマス?の空き(=侵入可能)cellの
+ * ?から適?なマス目の座標をx+(y<<16)で返す
+ *
+ * 現?range=1でアイテムドロップ用途のみ
+ *------------------------------------------
+ */
+int map_searchrandfreecell(int m,int x,int y,int range) {
+ int free_cell,i,j;
+ int* free_cells;
+
+ if (range < 0)
+ return -1;
+
+ //FIXME: Would it be quicker to hardcode an array of 9 since this function is always called with range 1?
+ free_cells = aCalloc((2*range+1)*(2*range+1), sizeof(int)); //better use more memory than having to wipe twice the cells. [Skotlex]
+
+ for(free_cell=0,i=-range;i<=range;i++){
+ if(i+y<0 || i+y>=map[m].ys)
+ continue;
+ for(j=-range;j<=range;j++){
+ if(j+x<0 || j+x>=map[m].xs)
+ continue;
+ if(map_getcell(m,j+x,i+y,CELL_CHKNOPASS))
+ continue;
+ if(map_count_oncell(m,j+x,i+y, BL_ITEM) > 1) //Avoid item stacking to prevent against exploits. [Skotlex]
+ continue;
+ free_cells[free_cell++] = j+x+((i+y)<<16);
+ }
+ }
+ if(free_cell==0)
+ {
+ aFree(free_cells);
+ return -1;
+ }
+ free_cell=free_cells[rand()%free_cell];
+ aFree(free_cells);
+ return free_cell;
+/*
+ for(i=-range;i<=range;i++){
+ if(i+y<0 || i+y>=map[m].ys)
+ continue;
+ for(j=-range;j<=range;j++){
+ if(j+x<0 || j+x>=map[m].xs)
+ continue;
+ if(map_getcell(m,j+x,i+y,CELL_CHKNOPASS))
+ continue;
+ if(map_count_oncell(m,j+x,i+y, BL_ITEM) > 1) //Avoid item stacking to prevent against exploits. [Skotlex]
+ continue;
+ if(free_cell==0){
+ x+=j;
+ y+=i;
+ i=range+1;
+ break;
+ }
+ free_cell--;
+ }
+ }
+
+ return x+(y<<16);
+*/
+}
+
+/*==========================================
+ * (m,x,y)を中心に3x3以?に床アイテム設置
+ *
+ * item_dataはamount以外をcopyする
+ *------------------------------------------
+ */
+int map_addflooritem(struct item *item_data,int amount,int m,int x,int y,struct map_session_data *first_sd,
+ struct map_session_data *second_sd,struct map_session_data *third_sd,int type) {
+ int xy,r;
+ unsigned int tick;
+ struct flooritem_data *fitem=NULL;
+
+ nullpo_retr(0, item_data);
+
+ if((xy=map_searchrandfreecell(m,x,y,1))<0)
+ return 0;
+ r=rand();
+
+ fitem = (struct flooritem_data *)aCalloc(1,sizeof(*fitem));
+ fitem->bl.type=BL_ITEM;
+ fitem->bl.prev = fitem->bl.next = NULL;
+ fitem->bl.m=m;
+ fitem->bl.x=xy&0xffff;
+ fitem->bl.y=(xy>>16)&0xffff;
+ fitem->bl.id = map_addobject(&fitem->bl);
+ if(fitem->bl.id==0){
+ aFree(fitem);
+ return 0;
+ }
+
+ tick = gettick();
+ if(first_sd) {
+ fitem->first_get_id = first_sd->bl.id;
+ if(type)
+ fitem->first_get_tick = tick + battle_config.mvp_item_first_get_time;
+ else
+ fitem->first_get_tick = tick + battle_config.item_first_get_time;
+ }
+ if(second_sd) {
+ fitem->second_get_id = second_sd->bl.id;
+ if(type)
+ fitem->second_get_tick = tick + battle_config.mvp_item_first_get_time + battle_config.mvp_item_second_get_time;
+ else
+ fitem->second_get_tick = tick + battle_config.item_first_get_time + battle_config.item_second_get_time;
+ }
+ if(third_sd) {
+ fitem->third_get_id = third_sd->bl.id;
+ if(type)
+ fitem->third_get_tick = tick + battle_config.mvp_item_first_get_time + battle_config.mvp_item_second_get_time + battle_config.mvp_item_third_get_time;
+ else
+ fitem->third_get_tick = tick + battle_config.item_first_get_time + battle_config.item_second_get_time + battle_config.item_third_get_time;
+ }
+
+ memcpy(&fitem->item_data,item_data,sizeof(*item_data));
+ fitem->item_data.amount=amount;
+ fitem->subx=(r&3)*3+3;
+ fitem->suby=((r>>2)&3)*3+3;
+ fitem->cleartimer=add_timer(gettick()+battle_config.flooritem_lifetime,map_clearflooritem_timer,fitem->bl.id,0);
+
+ map_addblock(&fitem->bl);
+ clif_dropflooritem(fitem);
+
+ return fitem->bl.id;
+}
+
+static void* create_charid2nick(DBKey key, va_list args) {
+ struct charid2nick *p;
+ p = (struct charid2nick *)aCallocA(1, sizeof (struct charid2nick));
+ return p;
+}
+/*==========================================
+ * charid_dbへ追加(返信待ちがあれば返信)
+ *------------------------------------------
+ */
+void map_addchariddb(int charid, char *name) {
+ struct charid2nick *p;
+ int req = 0;
+
+ p = idb_ensure(charid_db,charid,create_charid2nick);
+ req = p->req_id;
+ p->req_id = 0;
+ //We overwrite the nick anyway in case a different one arrived.
+ memcpy(p->nick, name, NAME_LENGTH);
+
+ if (req) {
+ struct map_session_data *sd = map_id2sd(req);
+ if (sd) clif_solved_charname(sd,charid);
+ }
+}
+
+/*==========================================
+ * charid_dbへ追加(返信要求のみ)
+ *------------------------------------------
+ */
+int map_reqchariddb(struct map_session_data * sd,int charid) {
+ struct charid2nick *p=NULL;
+
+ nullpo_retr(0, sd);
+
+ p = (struct charid2nick*)idb_get(charid_db,charid);
+ if(p) return 0; //Nothing to request, we already have the name!
+ p = (struct charid2nick *)aCalloc(1,sizeof(struct charid2nick));
+ p->req_id=sd->bl.id;
+ idb_put(charid_db,charid,p);
+ return 0;
+}
+
+/*==========================================
+ * id_dbへblを追加
+ *------------------------------------------
+ */
+void map_addiddb(struct block_list *bl) {
+ nullpo_retv(bl);
+
+ if (bl->type == BL_PC)
+ idb_put(pc_db,bl->id,bl);
+ idb_put(id_db,bl->id,bl);
+}
+
+/*==========================================
+ * id_dbからblを削除
+ *------------------------------------------
+ */
+void map_deliddb(struct block_list *bl) {
+ nullpo_retv(bl);
+
+ if (bl->type == BL_PC)
+ idb_remove(pc_db,bl->id);
+ idb_remove(id_db,bl->id);
+}
+
+/*==========================================
+ * PCのquit?理 map.c?分
+ *
+ * quit?理の主?が違うような?もしてきた
+ *------------------------------------------
+ */
+int map_quit(struct map_session_data *sd) {
+
+ //nullpo_retr(0, sd); //Utterly innecessary, all invokations to this function already have an SD non-null check.
+ //Learn to use proper coding and stop relying on nullpo_'s for safety :P [Skotlex]
+
+
+ if(!sd->state.waitingdisconnect) {
+ if (sd->state.event_disconnect) {
+ if (script_config.event_script_type == 0) {
+ struct npc_data *npc;
+ if ((npc = npc_name2id(script_config.logout_event_name))) {
+ run_script(npc->u.scr.script,0,sd->bl.id,npc->bl.id); // PCLogoutNPC
+ ShowStatus("Event '"CL_WHITE"%s"CL_RESET"' executed.\n", script_config.logout_event_name);
+ }
+ } else {
+ ShowStatus("%d '"CL_WHITE"%s"CL_RESET"' events executed.\n",
+ npc_event_doall_id(script_config.logout_event_name, sd->bl.id), script_config.logout_event_name);
+ }
+ }
+
+ if(sd->chatID) // チャットから出る
+ chat_leavechat(sd);
+
+ if(sd->trade_partner) // 取引を中?する
+ trade_tradecancel(sd);
+
+ if(sd->party_invite>0) // パ?ティ?誘を拒否する
+ party_reply_invite(sd,sd->party_invite_account,0);
+
+ if(sd->guild_invite>0) // ギルド?誘を拒否する
+ guild_reply_invite(sd,sd->guild_invite,0);
+ if(sd->guild_alliance>0) // ギルド同盟?誘を拒否する
+ guild_reply_reqalliance(sd,sd->guild_alliance_account,0);
+
+ // Force exiting from duel and rejecting
+ // all duel invitations when player quit [LuzZza]
+ if(sd->duel_group > 0)
+ duel_leave(sd->duel_group, sd);
+
+ if(sd->duel_invite > 0)
+ duel_reject(sd->duel_invite, sd);
+
+ party_send_logout(sd); // パ?ティのログアウトメッセ?ジ送信
+
+ party_send_dot_remove(sd);//minimap dot fix [Kevin]
+
+ guild_send_memberinfoshort(sd,0); // ギルドのログアウトメッセ?ジ送信
+
+ guild_send_dot_remove(sd);
+
+ pc_cleareventtimer(sd); // イベントタイマを破棄する
+
+ // check if we've been authenticated [celest]
+ if (sd->state.auth)
+ skill_castcancel(&sd->bl,0); // 詠唱を中?する
+
+ skill_stop_dancing(&sd->bl);// ダンス/演奏中?
+
+ //Status that are not saved...
+ if(sd->sc_count) {
+ if(sd->sc_data[SC_HIDING].timer!=-1)
+ status_change_end(&sd->bl,SC_HIDING,-1);
+ if(sd->sc_data[SC_CLOAKING].timer!=-1)
+ status_change_end(&sd->bl,SC_CLOAKING,-1);
+ if(sd->sc_data[SC_RUN].timer!=-1)
+ status_change_end(&sd->bl,SC_RUN,-1);
+ if(sd->sc_data[SC_SPURT].timer!=-1)
+ status_change_end(&sd->bl,SC_SPURT,-1);
+ if(sd->sc_data && sd->sc_data[SC_BERSERK].timer!=-1)
+ status_change_end(&sd->bl,SC_BERSERK,-1);
+ }
+ skill_clear_unitgroup(&sd->bl); // スキルユニットグル?プの削除
+
+ // check if we've been authenticated [celest]
+ if (sd->state.auth) {
+ skill_cleartimerskill(&sd->bl);
+ pc_stop_walking(sd,0);
+ pc_stopattack(sd);
+ pc_stop_following(sd);
+ pc_delinvincibletimer(sd);
+ }
+ pc_delspiritball(sd,sd->spiritball,1);
+ skill_gangsterparadise(sd,0);
+ skill_unit_move(&sd->bl,gettick(),4);
+
+ if (sd->state.auth)
+ status_calc_pc(sd,4);
+ // skill_clear_unitgroup(&sd->bl); // [Sara-chan]
+
+ if (!(sd->status.option & OPTION_INVISIBLE))
+ clif_clearchar_area(&sd->bl,2);
+
+ chrif_save_scdata(sd); //Save status changes, then clear'em out from memory. [Skotlex]
+ status_change_clear(&sd->bl,1);
+
+ if(sd->status.pet_id && sd->pd) {
+ pet_lootitem_drop(sd->pd,sd);
+ pet_remove_map(sd);
+ if(sd->pet.intimate <= 0) {
+ intif_delete_petdata(sd->status.pet_id);
+ sd->status.pet_id = 0;
+ sd->pd = NULL;
+ sd->petDB = NULL;
+ }
+ else
+ intif_save_petdata(sd->status.account_id,&sd->pet);
+ }
+
+ if(pc_isdead(sd))
+ pc_setrestartvalue(sd,2);
+
+ pc_clean_skilltree(sd);
+
+ //The storage closing routines will save the char if needed. [Skotlex]
+ if (!sd->state.storage_flag)
+ chrif_save(sd,1);
+ else if (sd->state.storage_flag == 1)
+ storage_storage_quit(sd,1);
+ else if (sd->state.storage_flag == 2)
+ storage_guild_storage_quit(sd,1);
+
+ map_delblock(&sd->bl);
+ } else { //Try to free some data, without saving anything (this could be invoked on map server change. [Skotlex]
+ if (sd->bl.prev != NULL)
+ { //Remove from map...
+ if (!(sd->status.option & OPTION_INVISIBLE))
+ clif_clearchar_area(&sd->bl,2);
+ map_delblock(&sd->bl);
+ }
+ if (sd->pd)
+ pet_remove_map(sd);
+ }
+
+ if (sd->stack) {
+ script_free_stack(sd->stack);
+ sd->stack= NULL;
+ }
+
+// chrif_char_offline(sd); //chrif_save handles this now.
+
+ //Do we really need to remove the name?
+ idb_remove(charid_db,sd->status.char_id);
+ idb_remove(id_db,sd->bl.id);
+ idb_remove(pc_db,sd->bl.id);
+
+ // Notify friends that this char logged out. [Skotlex]
+ clif_foreachclient(clif_friendslist_toggle_sub, sd->status.account_id, sd->status.char_id, 0);
+
+ if(sd->reg)
+ { //Double logout already freed pointer fix... [Skotlex]
+ aFree(sd->reg);
+ sd->reg = NULL;
+ sd->reg_num = 0;
+ }
+
+ if(sd->regstr)
+ {
+ aFree(sd->regstr);
+ sd->regstr = NULL;
+ sd->regstr_num = 0;
+ }
+
+ if(!sd->fd) //There is no session connected, and as such socket.c won't free the data, we must do it. [Skotlex]
+ aFree(sd);
+ return 0;
+}
+
+/*==========================================
+ * id番?のPCを探す。居なければNULL
+ *------------------------------------------
+ */
+struct map_session_data * map_id2sd(int id) {
+// Now using pc_db to handle all players, should be quicker than both previous methods at a small expense of more memory. [Skotlex]
+ if (id <= 0) return NULL;
+ return (struct map_session_data*)idb_get(pc_db,id);
+}
+
+/*==========================================
+ * char_id番?の名前を探す
+ *------------------------------------------
+ */
+char * map_charid2nick(int id) {
+ struct charid2nick *p = (struct charid2nick*)idb_get(charid_db,id);
+
+ if(p==NULL)
+ return NULL;
+ return p->nick;
+}
+
+struct map_session_data * map_charid2sd(int id) {
+ int i, users;
+ struct map_session_data **all_sd;
+
+ if (id <= 0) return 0;
+
+ all_sd = map_getallusers(&users);
+ for(i = 0; i < users; i++)
+ if (all_sd[i] && all_sd[i]->status.char_id == id)
+ return all_sd[i];
+
+ return NULL;
+}
+
+/*==========================================
+ * Search session data from a nick name
+ * (without sensitive case if necessary)
+ * return map_session_data pointer or NULL
+ *------------------------------------------
+ */
+struct map_session_data * map_nick2sd(char *nick) {
+ int i, quantity=0, nicklen, users;
+ struct map_session_data *sd = NULL;
+ struct map_session_data *pl_sd = NULL, **pl_allsd;
+
+ if (nick == NULL)
+ return NULL;
+
+ nicklen = strlen(nick);
+
+ pl_allsd = map_getallusers(&users);
+
+ for (i = 0; i < users; i++) {
+ pl_sd = pl_allsd[i];
+ // Without case sensitive check (increase the number of similar character names found)
+ if (strnicmp(pl_sd->status.name, nick, nicklen) == 0) {
+ // Strict comparison (if found, we finish the function immediatly with correct value)
+ if (strcmp(pl_sd->status.name, nick) == 0)
+ return pl_sd;
+ quantity++;
+ sd = pl_sd;
+ }
+ }
+ // Here, the exact character name is not found
+ // We return the found index of a similar account ONLY if there is 1 similar character
+ if (quantity == 1)
+ return sd;
+
+ // Exact character name is not found and 0 or more than 1 similar characters have been found ==> we say not found
+ return NULL;
+}
+
+/*==========================================
+ * id番?の物を探す
+ * 一三bjectの場合は配列を引くのみ
+ *------------------------------------------
+ */
+struct block_list * map_id2bl(int id)
+{
+ struct block_list *bl=NULL;
+ if(id >= 0 && id < sizeof(objects)/sizeof(objects[0]))
+ bl = objects[id];
+ else
+ bl = idb_get(id_db,id);
+
+ return bl;
+}
+
+static int map_getallpc_sub(DBKey key,void * data,va_list ap)
+{
+ struct map_session_data *sd = (struct map_session_data*) data;
+ if (!sd->state.auth || sd->state.waitingdisconnect || sd->state.finalsave)
+ return 1; //Do not count in not-yet authenticated characters or ready to disconnect ones.
+
+ return 0;
+}
+
+/*==========================================
+ * Returns an array of all players in the server (includes non connected ones) [Skotlex]
+ * The int pointer given returns the count of elements in the array.
+ * If null is passed, it is requested that the memory be freed (for shutdown), and null is returned.
+ *------------------------------------------
+ */
+struct map_session_data** map_getallusers(int *users) {
+ static struct map_session_data **all_sd=NULL;
+ static unsigned int all_count = 0;
+
+ if (users == NULL)
+ { //Free up data
+ if (all_sd) aFree(all_sd);
+ all_sd = NULL;
+ return NULL;
+ }
+
+ if (all_sd == NULL)
+ { //Init data
+ all_count = pc_db->size(pc_db); //This is the real number of chars in the db, better use this than the actual "online" count.
+ if (all_count < 1)
+ all_count = 10; //Allow room for at least 10 chars.
+ all_sd = aCalloc(all_count, sizeof(struct map_session_data*)); //it's actually just the size of a pointer.
+ }
+
+ if (all_count < pc_db->size(pc_db))
+ {
+ all_count = pc_db->size(pc_db)+10; //Give some room to prevent doing reallocs often.
+ all_sd = aRealloc(all_sd, all_count*sizeof(struct map_session_data*));
+ }
+ *users = pc_db->getall(pc_db,(void**)all_sd,all_count,map_getallpc_sub);
+ if (*users > all_count) //Which should be impossible...
+ *users = all_count;
+ return all_sd;
+}
+
+/*==========================================
+ * id_db?の全てにfuncを?行
+ *------------------------------------------
+ */
+int map_foreachiddb(int (*func)(DBKey,void*,va_list),...) {
+ va_list ap;
+
+ va_start(ap,func);
+ id_db->foreach(id_db,func,ap);
+ va_end(ap);
+ return 0;
+}
+
+/*==========================================
+ * map.npcへ追加 (warp等の領域持ちのみ)
+ *------------------------------------------
+ */
+int map_addnpc(int m,struct npc_data *nd) {
+ int i;
+ if(m<0 || m>=map_num)
+ return -1;
+ for(i=0;i<map[m].npc_num && i<MAX_NPC_PER_MAP;i++)
+ if(map[m].npc[i]==NULL)
+ break;
+ if(i==MAX_NPC_PER_MAP){
+ if(battle_config.error_log)
+ ShowWarning("too many NPCs in one map %s\n",map[m].name);
+ return -1;
+ }
+ if(i==map[m].npc_num){
+ map[m].npc_num++;
+ }
+
+ nullpo_retr(0, nd);
+
+ map[m].npc[i]=nd;
+ nd->n = i;
+ idb_put(id_db,nd->bl.id,nd);
+
+ return i;
+}
+
+void map_removenpc(void) {
+ int i,m,n=0;
+
+ for(m=0;m<map_num;m++) {
+ for(i=0;i<map[m].npc_num && i<MAX_NPC_PER_MAP;i++) {
+ if(map[m].npc[i]!=NULL) {
+ clif_clearchar_area(&map[m].npc[i]->bl,2);
+ map_delblock(&map[m].npc[i]->bl);
+ idb_remove(id_db,map[m].npc[i]->bl.id);
+ if(map[m].npc[i]->bl.subtype==SCRIPT) {
+ aFree(map[m].npc[i]->u.scr.script);
+ aFree(map[m].npc[i]->u.scr.label_list);
+ }
+ aFree(map[m].npc[i]);
+ map[m].npc[i] = NULL;
+ n++;
+ }
+ }
+ }
+
+ ShowStatus("Successfully removed and freed from memory '"CL_WHITE"%d"CL_RESET"' NPCs.\n",n);
+}
+
+/*=========================================
+ * Dynamic Mobs [Wizputer]
+ *-----------------------------------------
+ */
+
+// allocates a struct when it there is place free in the cache,
+// and returns NULL otherwise
+// -- i'll just leave the old code in case it's needed ^^;
+struct mob_list* map_addmobtolist(unsigned short m)
+{
+ size_t i;
+ for (i = 0; i < MAX_MOB_LIST_PER_MAP; i++) {
+ if (map[m].moblist[i] == NULL) {
+ map[m].moblist[i] = (struct mob_list *) aMalloc (sizeof(struct mob_list));
+ return map[m].moblist[i];
+ }
+ }
+ return NULL;
+}
+
+void map_spawnmobs(int m)
+{
+ int i, k=0;
+ if (map[m].mob_delete_timer != -1)
+ { //Mobs have not been removed yet [Skotlex]
+ delete_timer(map[m].mob_delete_timer, map_removemobs_timer);
+ map[m].mob_delete_timer = -1;
+ return;
+ }
+ for(i=0; i<MAX_MOB_LIST_PER_MAP; i++)
+ if(map[m].moblist[i]!=NULL)
+ {
+ k+=map[m].moblist[i]->num;
+ npc_parse_mob2(map[m].moblist[i],1);
+ }
+
+ if (battle_config.etc_log && k > 0)
+ {
+ ShowStatus("Map %s: Spawned '"CL_WHITE"%d"CL_RESET"' mobs.\n",map[m].name, k);
+ }
+}
+
+int mob_cache_cleanup_sub(struct block_list *bl, va_list ap) {
+ struct mob_data *md = (struct mob_data *)bl;
+ nullpo_retr(0, md);
+
+ //When not to remove:
+ //Mob has the cached flag on 0
+ if (!md->special_state.cached)
+ return 0;
+ if (!battle_config.mob_remove_damaged &&
+ md->hp < md->db->max_hp) //don't use status_get_maxhp for speed (by the time you have to remove a mob, their status changes should have expired anyway)
+ return 0; //Do not remove damaged mobs.
+
+ mob_remove_map(md, 0);
+ map_deliddb(&md->bl);
+ aFree(md);
+ md = NULL;
+
+ return 1;
+}
+
+int map_removemobs_timer(int tid, unsigned int tick, int id, int data)
+{
+ int k;
+ if (id < 0 || id >= MAX_MAP_PER_SERVER)
+ { //Incorrect map id!
+ if (battle_config.error_log)
+ ShowError("map_removemobs_timer error: timer %d points to invalid map %d\n",tid, id);
+ return 0;
+ }
+ if (map[id].mob_delete_timer != tid)
+ { //Incorrect timer call!
+ if (battle_config.error_log)
+ ShowError("map_removemobs_timer mismatch: %d != %d (map %s)\n",map[id].mob_delete_timer, tid, map[id].name);
+ return 0;
+ }
+ map[id].mob_delete_timer = -1;
+ if (map[id].users > 0) //Map not empty!
+ return 1;
+ k = map_foreachinmap(mob_cache_cleanup_sub, id, BL_MOB);
+
+ if (battle_config.etc_log && k > 0)
+ ShowStatus("Map %s: Removed '"CL_WHITE"%d"CL_RESET"' mobs.\n",map[id].name, k);
+
+ return 1;
+}
+
+void map_removemobs(int m)
+{
+ if (map[m].mob_delete_timer != -1)
+ return; //Mobs are already scheduled for removal
+
+ map[m].mob_delete_timer = add_timer(gettick()+battle_config.mob_remove_delay, map_removemobs_timer, m, 0);
+}
+
+/*==========================================
+ * map名からmap番?へ?換
+ *------------------------------------------
+ */
+int map_mapname2mapid(char *name) {
+ unsigned short map_index;
+ map_index = mapindex_name2id(name);
+ if (!map_index)
+ return -1;
+ return map_mapindex2mapid(map_index);
+}
+
+/*==========================================
+ * Returns the map of the given mapindex. [Skotlex]
+ *------------------------------------------
+ */
+int map_mapindex2mapid(unsigned short mapindex) {
+ struct map_data *md=NULL;
+
+ if (!mapindex)
+ return -1;
+
+ md = (struct map_data*)uidb_get(map_db,(unsigned int)mapindex);
+ if(md==NULL || md->gat==NULL)
+ return -1;
+ return md->m;
+}
+
+/*==========================================
+ * 他鯖map名からip,port?換
+ *------------------------------------------
+ */
+int map_mapname2ipport(unsigned short name,int *ip,int *port) {
+ struct map_data_other_server *mdos=NULL;
+
+ mdos = (struct map_data_other_server*)uidb_get(map_db,(unsigned int)name);
+ if(mdos==NULL || mdos->gat) //If gat isn't null, this is a local map.
+ return -1;
+ *ip=mdos->ip;
+ *port=mdos->port;
+ return 0;
+}
+
+/*==========================================
+ * Checks if both dirs point in the same direction.
+ *------------------------------------------
+ */
+int map_check_dir(int s_dir,int t_dir) {
+ if(s_dir == t_dir)
+ return 0;
+ switch(s_dir) {
+ case 0:
+ if(t_dir == 7 || t_dir == 1 || t_dir == 0)
+ return 0;
+ break;
+ case 1:
+ if(t_dir == 0 || t_dir == 2 || t_dir == 1)
+ return 0;
+ break;
+ case 2:
+ if(t_dir == 1 || t_dir == 3 || t_dir == 2)
+ return 0;
+ break;
+ case 3:
+ if(t_dir == 2 || t_dir == 4 || t_dir == 3)
+ return 0;
+ break;
+ case 4:
+ if(t_dir == 3 || t_dir == 5 || t_dir == 4)
+ return 0;
+ break;
+ case 5:
+ if(t_dir == 4 || t_dir == 6 || t_dir == 5)
+ return 0;
+ break;
+ case 6:
+ if(t_dir == 5 || t_dir == 7 || t_dir == 6)
+ return 0;
+ break;
+ case 7:
+ if(t_dir == 6 || t_dir == 0 || t_dir == 7)
+ return 0;
+ break;
+ }
+ return 1;
+}
+
+/*==========================================
+ * Returns the direction of the given cell in absolute relation to the char
+ * (regardless of where the char is facing)
+ *------------------------------------------
+ */
+int map_calc_dir( struct block_list *src,int x,int y) {
+ int dir=0;
+ int dx,dy;
+
+ nullpo_retr(0, src);
+
+ dx=x-src->x;
+ dy=y-src->y;
+ if( dx==0 && dy==0 ){ // 彼我の場所一致
+ dir=0; // 上
+ }else if( dx>=0 && dy>=0 ){ // 方向的に右上
+ dir=7; // 右上
+ if( dx*2-1<dy ) dir=0; // 上
+ if( dx>dy*2 ) dir=6; // 右
+ }else if( dx>=0 && dy<=0 ){ // 方向的に右下
+ dir=5; // 右下
+ if( dx*2-1<-dy ) dir=4; // 下
+ if( dx>-dy*2 ) dir=6; // 右
+ }else if( dx<=0 && dy<=0 ){ // 方向的に左下
+ dir=3; // 左下
+ if( dx*2+1>dy ) dir=4; // 下
+ if( dx<dy*2 ) dir=2; // 左
+ }else{ // 方向的に左上
+ dir=1; // 左上
+ if( -dx*2-1<dy ) dir=0; // 上
+ if( -dx>dy*2 ) dir=2; // 左
+ }
+ return dir;
+}
+
+/*==========================================
+ * Randomizes target cell x,y to a random walkable cell that
+ * has the same distance from object as given coordinates do. [Skotlex]
+ *------------------------------------------
+ */
+int map_random_dir(struct block_list *bl, short *x, short *y) {
+ short xi = *x-bl->x;
+ short yi = *y-bl->y;
+ short i=0, j;
+ int dist2 = xi*xi + yi*yi;
+ short dist = (short)sqrt(dist2);
+ short segment;
+
+ if (dist < 1) dist =1;
+
+ do {
+ j = rand()%8; //Pick a random direction
+ segment = rand()%dist; //Pick a random interval from the whole vector in that direction
+ xi = bl->x + segment*dirx[j];
+ segment = (short)sqrt(dist2 - segment*segment); //The complement of the previously picked segment
+ yi = bl->y + segment*diry[j];
+ } while (map_getcell(bl->m,xi,yi,CELL_CHKNOPASS) && (++i)<100);
+ if (i < 100) {
+ *x = xi;
+ *y = yi;
+ return 1;
+ }
+ return 0;
+}
+// gat系
+/*==========================================
+ * (m,x,y)の状態を調べる
+ *------------------------------------------
+ */
+
+int map_getcell(int m,int x,int y,cell_t cellchk)
+{
+ return (m < 0 || m >= MAX_MAP_PER_SERVER) ? 0 : map_getcellp(&map[m],x,y,cellchk);
+}
+
+int map_getcellp(struct map_data* m,int x,int y,cell_t cellchk)
+{
+ int type, type2;
+#ifdef CELL_NOSTACK
+ int type3;
+#endif
+
+ nullpo_ret(m);
+
+ if(x<0 || x>=m->xs-1 || y<0 || y>=m->ys-1)
+ {
+ if(cellchk==CELL_CHKNOPASS) return 1;
+ return 0;
+ }
+ type = m->gat[x+y*m->xs];
+ type2 = m->cell[x+y*m->xs];
+#ifdef CELL_NOSTACK
+ type3 = m->cell_bl[x+y*m->xs];
+#endif
+
+ switch(cellchk)
+ {
+ case CELL_CHKPASS:
+#ifdef CELL_NOSTACK
+ if (type3 >= battle_config.cell_stack_limit) return 0;
+#endif
+ return (type!=1 && type!=5 && !(type2&(CELL_MOONLIT|CELL_ICEWALL)));
+ case CELL_CHKNOPASS:
+#ifdef CELL_NOSTACK
+ if (type3 >= battle_config.cell_stack_limit) return 1;
+#endif
+ return (type==1 || type==5 || type2&(CELL_MOONLIT|CELL_ICEWALL));
+ case CELL_CHKWALL:
+ return (type==1/* || type2&CELL_ICEWALL*/); //Uncomment to prevent sniping/casting through the icewall. [Skotlex]
+ case CELL_CHKWATER:
+ return (type==3);
+ case CELL_CHKGROUND:
+ return (type==5 || type2&CELL_ICEWALL);
+ case CELL_GETTYPE:
+ return type;
+ case CELL_GETCELLTYPE:
+ return type2;
+ case CELL_CHKNPC:
+ return (type2&CELL_NPC);
+ case CELL_CHKPNEUMA:
+ return (type2&CELL_PNEUMA);
+ case CELL_CHKSAFETYWALL:
+ return (type2&CELL_SAFETYWALL);
+ case CELL_CHKBASILICA:
+ return (type2&CELL_BASILICA);
+ case CELL_CHKLANDPROTECTOR:
+ return (type2&CELL_LANDPROTECTOR);
+ case CELL_CHKMOONLIT:
+ return (type2&CELL_MOONLIT);
+ case CELL_CHKREGEN:
+ return (type2&CELL_REGEN);
+ case CELL_CHKICEWALL:
+ return (type2&CELL_ICEWALL);
+ default:
+ return 0;
+ }
+}
+
+/*==========================================
+ * (m,x,y)の状態を設定する
+ *------------------------------------------
+ */
+void map_setcell(int m,int x,int y,int cell)
+{
+ int j;
+ if(x<0 || x>=map[m].xs || y<0 || y>=map[m].ys)
+ return;
+ j=x+y*map[m].xs;
+
+ switch (cell) {
+ case CELL_SETNPC:
+ map[m].cell[j] |= CELL_NPC;
+ break;
+ case CELL_CLRNPC:
+ map[m].cell[j] &= ~CELL_NPC;
+ break;
+ case CELL_SETICEWALL:
+ map[m].cell[j] |= CELL_ICEWALL;
+ break;
+ case CELL_CLRICEWALL:
+ map[m].cell[j] &= ~CELL_ICEWALL;
+ break;
+ case CELL_SETBASILICA:
+ map[m].cell[j] |= CELL_BASILICA;
+ break;
+ case CELL_CLRBASILICA:
+ map[m].cell[j] &= ~CELL_BASILICA;
+ break;
+ case CELL_SETPNEUMA:
+ map[m].cell[j] |= CELL_PNEUMA;
+ break;
+ case CELL_CLRPNEUMA:
+ map[m].cell[j] &= ~CELL_PNEUMA;
+ break;
+ case CELL_SETSAFETYWALL:
+ map[m].cell[j] |= CELL_SAFETYWALL;
+ break;
+ case CELL_CLRSAFETYWALL:
+ map[m].cell[j] &= ~CELL_SAFETYWALL;
+ break;
+ case CELL_SETMOONLIT:
+ map[m].cell[j] |= CELL_MOONLIT;
+ break;
+ case CELL_CLRMOONLIT:
+ map[m].cell[j] &= ~CELL_MOONLIT;
+ break;
+ case CELL_SETLANDPROTECTOR:
+ map[m].cell[j] |= CELL_LANDPROTECTOR;
+ break;
+ case CELL_CLRLANDPROTECTOR:
+ map[m].cell[j] &= ~CELL_LANDPROTECTOR;
+ break;
+ case CELL_SETREGEN:
+ map[m].cell[j] |= CELL_REGEN;
+ break;
+ default:
+ map[m].gat[j] = cell;
+ break;
+ }
+}
+static void* create_map_data_other_server(DBKey key, va_list args) {
+ struct map_data_other_server *mdos;
+ unsigned short mapindex = (unsigned short)key.ui;
+ mdos=(struct map_data_other_server *)aCalloc(1,sizeof(struct map_data_other_server));
+ mdos->index = mapindex;
+ memcpy(mdos->name, mapindex_id2name(mapindex), MAP_NAME_LENGTH);
+ return mdos;
+}
+/*==========================================
+ * 他鯖管理のマップをdbに追加
+ *------------------------------------------
+ */
+int map_setipport(unsigned short mapindex,unsigned long ip,int port) {
+ struct map_data_other_server *mdos=NULL;
+
+ mdos=(struct map_data_other_server *)uidb_ensure(map_db,(unsigned int)mapindex, create_map_data_other_server);
+
+ if(mdos->gat) //Local map,Do nothing. Give priority to our own local maps over ones from another server. [Skotlex]
+ return 0;
+ if(ip == clif_getip() && port == clif_getport()) {
+ //That's odd, we received info that we are the ones with this map, but... we don't have it.
+ ShowFatalError("map_setipport : received info that this map-server SHOULD have map '%s', but it is not loaded.\n",mapindex_id2name(mapindex));
+ exit(1);
+ }
+ mdos->ip = ip;
+ mdos->port = port;
+ return 1;
+}
+
+/*==========================================
+ * 他鯖管理のマップを全て削除
+ *------------------------------------------
+ */
+int map_eraseallipport_sub(DBKey key,void *data,va_list va) {
+ struct map_data_other_server *mdos = (struct map_data_other_server*)data;
+ if(mdos->gat == NULL) {
+ db_remove(map_db,key);
+ aFree(mdos);
+ }
+ return 0;
+}
+
+int map_eraseallipport(void) {
+ map_db->foreach(map_db,map_eraseallipport_sub);
+ return 1;
+}
+
+/*==========================================
+ * 他鯖管理のマップをdbから削除
+ *------------------------------------------
+ */
+int map_eraseipport(unsigned short mapindex,unsigned long ip,int port)
+{
+ struct map_data_other_server *mdos;
+// unsigned char *p=(unsigned char *)&ip;
+
+ mdos = uidb_get(map_db,(unsigned int)mapindex);
+ if(!mdos || mdos->gat) //Map either does not exists or is a local map.
+ return 0;
+
+ if(mdos->ip==ip && mdos->port == port) {
+ uidb_remove(map_db,(unsigned int)mapindex);
+ aFree(mdos);
+ return 1;
+ }
+ return 0;
+}
+
+// 初期化周り
+/*==========================================
+ * 水場高さ設定
+ *------------------------------------------
+ */
+static struct waterlist_ {
+ char mapname[MAP_NAME_LENGTH];
+ int waterheight;
+} *waterlist=NULL;
+
+#define NO_WATER 1000000
+
+static int map_setwaterheight_sub(int m, int wh) {
+ char fn[256];
+ char *gat;
+ int x,y;
+ struct gat_1cell {float high[4]; int type;} *p = NULL;
+
+ if (m < 0)
+ return 0;
+
+ sprintf(fn,"data\\%s",mapindex_id2name(map[m].index));
+
+ // read & convert fn
+ // again, might not need to be unsigned char
+ gat = (char *) grfio_read (fn);
+ if (gat == NULL)
+ return 0;
+
+ for (y = 0; y < map[m].ys; y++) {
+ p = (struct gat_1cell*)(gat+y*map[m].xs*20+14);
+ for (x = 0; x < map[m].xs; x++) {
+ if (wh != NO_WATER && p->type == 0) //Set water cell
+ map[m].gat[x+y*map[m].xs] = (p->high[0]>wh || p->high[1]>wh || p->high[2]>wh || p->high[3]>wh) ? 3 : 0;
+ else //Remove water cell
+ map[m].gat[x+y*map[m].xs] = p->type==3?0:p->type;
+ p++;
+ }
+ }
+ aFree(gat);
+ return 1;
+}
+int map_setwaterheight(int m, char *mapname, int height) {
+ int i=0;
+ if (height < 0)
+ height = NO_WATER;
+ if(waterlist){
+ for(i=0;waterlist[i].mapname[0] && i < MAX_MAP_PER_SERVER;i++)
+ if(strcmp(waterlist[i].mapname,mapname)==0) {
+ waterlist[i].waterheight = height;
+ }
+ }
+ if (i < MAX_MAP_PER_SERVER) {
+ memcpy(waterlist[i].mapname,mapname, MAP_NAME_LENGTH-1);
+ waterlist[i].waterheight = height;
+ }
+ return map_setwaterheight_sub(m, height);
+}
+
+int map_waterheight(char *mapname) {
+ if(waterlist){
+ int i;
+ for(i=0;waterlist[i].mapname[0] && i < MAX_MAP_PER_SERVER;i++)
+ if(strcmp(waterlist[i].mapname,mapname)==0)
+ return waterlist[i].waterheight;
+ }
+ return NO_WATER;
+}
+
+static void map_readwater(char *watertxt) {
+ char line[1024],w1[1024];
+ FILE *fp=NULL;
+ int n=0;
+
+ fp=fopen(watertxt,"r");
+ if(fp==NULL){
+ ShowError("file not found: %s\n",watertxt);
+ return;
+ }
+ if(waterlist==NULL)
+ waterlist = (struct waterlist_*)aCallocA(MAX_MAP_PER_SERVER,sizeof(*waterlist));
+ while(fgets(line,1020,fp) && n < MAX_MAP_PER_SERVER){
+ int wh,count;
+ if(line[0] == '/' && line[1] == '/')
+ continue;
+ if((count=sscanf(line,"%s%d",w1,&wh)) < 1){
+ continue;
+ }
+ memcpy(waterlist[n].mapname,w1, MAP_NAME_LENGTH-1);
+ if(count >= 2)
+ waterlist[n].waterheight = wh;
+ else
+ waterlist[n].waterheight = 3;
+ n++;
+ }
+ fclose(fp);
+}
+/*==========================================
+* マップキャッシュに追加する
+*===========================================*/
+
+// マップキャッシュの最大値
+#define MAX_MAP_CACHE 768
+
+//各マップごとの最小限情報を入れるもの、READ_FROM_BITMAP用
+struct map_cache_info {
+ char fn[32];//ファイル名
+ int xs,ys; //幅と高さ
+ int water_height;
+ int pos; // データが入れてある場所
+ int compressed; // zilb通せるようにする為の予約
+ int compressed_len; // zilb通せるようにする為の予約
+}; // 56 byte
+
+struct map_cache_head {
+ int sizeof_header;
+ int sizeof_map;
+ // 上の2つ改変不可
+ int nmaps; // マップの個数
+ int filesize;
+};
+
+struct {
+ struct map_cache_head head;
+ struct map_cache_info *map;
+ FILE *fp;
+ int dirty;
+} map_cache;
+
+static int map_cache_open(char *fn);
+static void map_cache_close(void);
+static int map_cache_read(struct map_data *m);
+static int map_cache_write(struct map_data *m);
+
+static int map_cache_open(char *fn)
+{
+ if (map_cache.fp)
+ map_cache_close();
+ map_cache.fp = fopen(fn, "r+b");
+ if (map_cache.fp) {
+ fread(&map_cache.head,1,sizeof(struct map_cache_head),map_cache.fp);
+ fseek(map_cache.fp,0,SEEK_END);
+ if(
+ map_cache.head.sizeof_header == sizeof(struct map_cache_head) &&
+ map_cache.head.sizeof_map == sizeof(struct map_cache_info) &&
+ map_cache.head.filesize == ftell(map_cache.fp)
+ ) {
+ // キャッシュ読み甲ン成功
+ map_cache.map = (struct map_cache_info *) aMalloc(sizeof(struct map_cache_info) * map_cache.head.nmaps);
+ fseek(map_cache.fp,sizeof(struct map_cache_head),SEEK_SET);
+ fread(map_cache.map,sizeof(struct map_cache_info),map_cache.head.nmaps,map_cache.fp);
+ return 1;
+ }
+ fclose(map_cache.fp);
+ }
+ // 読み甲ンに失敗したので新規に作成する
+ map_cache.fp = fopen(fn,"wb");
+ if(map_cache.fp) {
+ memset(&map_cache.head,0,sizeof(struct map_cache_head));
+ map_cache.map = (struct map_cache_info *) aCalloc(sizeof(struct map_cache_info),MAX_MAP_CACHE);
+ map_cache.head.nmaps = MAX_MAP_CACHE;
+ map_cache.head.sizeof_header = sizeof(struct map_cache_head);
+ map_cache.head.sizeof_map = sizeof(struct map_cache_info);
+
+ map_cache.head.filesize = sizeof(struct map_cache_head);
+ map_cache.head.filesize += sizeof(struct map_cache_info) * map_cache.head.nmaps;
+
+ map_cache.dirty = 1;
+ return 1;
+ }
+ return 0;
+}
+
+static void map_cache_close(void)
+{
+ if(!map_cache.fp) { return; }
+ if(map_cache.dirty) {
+ fseek(map_cache.fp,0,SEEK_SET);
+ fwrite(&map_cache.head,1,sizeof(struct map_cache_head),map_cache.fp);
+ fwrite(map_cache.map,map_cache.head.nmaps,sizeof(struct map_cache_info),map_cache.fp);
+ }
+ fclose(map_cache.fp);
+ aFree(map_cache.map);
+ map_cache.fp = NULL;
+ return;
+}
+
+int map_cache_read(struct map_data *m)
+{
+ int i;
+ if(!map_cache.fp) { return 0; }
+ for(i = 0;i < map_cache.head.nmaps ; i++) {
+ if(!strcmp(m->name,map_cache.map[i].fn)) {
+ if(map_cache.map[i].water_height != map_waterheight(m->name)) {
+ // 水場の高さが違うので読み直し
+ return 0;
+ } else if(map_cache.map[i].compressed == 0) {
+ // 非圧縮ファイル
+ int size = map_cache.map[i].xs * map_cache.map[i].ys;
+ m->xs = map_cache.map[i].xs;
+ m->ys = map_cache.map[i].ys;
+ m->gat = (unsigned char *)aCalloc(m->xs * m->ys,sizeof(unsigned char));
+ fseek(map_cache.fp,map_cache.map[i].pos,SEEK_SET);
+ if(fread(m->gat,1,size,map_cache.fp) == size) {
+ // 成功
+ return 1;
+ } else {
+ // なぜかファイル後半が欠けてるので読み直し
+ m->xs = 0; m->ys = 0; aFree(m->gat); m->gat = NULL;
+ return 0;
+ }
+ } else if(map_cache.map[i].compressed == 1) {
+ // 圧縮フラグ=1 : zlib
+ unsigned char *buf;
+ unsigned long dest_len;
+ int size_compress = map_cache.map[i].compressed_len;
+ m->xs = map_cache.map[i].xs;
+ m->ys = map_cache.map[i].ys;
+ m->gat = (unsigned char *)aMalloc(m->xs * m->ys * sizeof(unsigned char));
+ buf = (unsigned char*)aMalloc(size_compress);
+ fseek(map_cache.fp,map_cache.map[i].pos,SEEK_SET);
+ if(fread(buf,1,size_compress,map_cache.fp) != size_compress) {
+ // なぜかファイル後半が欠けてるので読み直し
+ ShowError("fread error\n");
+ aFree(m->gat); m->xs = 0; m->ys = 0; m->gat = NULL;
+ aFree(buf);
+ return 0;
+ }
+ dest_len = m->xs * m->ys;
+ decode_zip(m->gat,&dest_len,buf,size_compress);
+ if(dest_len != map_cache.map[i].xs * map_cache.map[i].ys) {
+ // 正常に解凍が出来てない
+ aFree(m->gat); m->xs = 0; m->ys = 0; m->gat = NULL;
+ aFree(buf);
+ return 0;
+ }
+ aFree(buf);
+ return 1;
+ }
+ }
+ }
+ return 0;
+}
+
+static int map_cache_write(struct map_data *m)
+{
+ int i;
+ unsigned long len_new , len_old;
+ char *write_buf;
+ if(!map_cache.fp) { return 0; }
+ for(i = 0;i < map_cache.head.nmaps ; i++) {
+ if(!strcmp(m->name,map_cache.map[i].fn)) {
+ // 同じエントリーがあれば上書き
+ if(map_cache.map[i].compressed == 0) {
+ len_old = map_cache.map[i].xs * map_cache.map[i].ys;
+ } else if(map_cache.map[i].compressed == 1) {
+ len_old = map_cache.map[i].compressed_len;
+ } else {
+ // サポートされてない形式なので長さ0
+ len_old = 0;
+ }
+ if(map_read_flag == 2) {
+ // 圧縮保存
+ // さすがに2倍に膨れる事はないという事で
+ write_buf = (char *) aMalloc(m->xs * m->ys * 2);
+ len_new = m->xs * m->ys * 2;
+ encode_zip((unsigned char *) write_buf,&len_new,m->gat,m->xs * m->ys);
+ map_cache.map[i].compressed = 1;
+ map_cache.map[i].compressed_len = len_new;
+ } else {
+ len_new = m->xs * m->ys;
+ write_buf = (char *) m->gat;
+ map_cache.map[i].compressed = 0;
+ map_cache.map[i].compressed_len = 0;
+ }
+ if(len_new <= len_old) {
+ // サイズが同じか小さくなったので場所は変わらない
+ fseek(map_cache.fp,map_cache.map[i].pos,SEEK_SET);
+ fwrite(write_buf,1,len_new,map_cache.fp);
+ } else {
+ // 新しい場所に登録
+ fseek(map_cache.fp,map_cache.head.filesize,SEEK_SET);
+ fwrite(write_buf,1,len_new,map_cache.fp);
+ map_cache.map[i].pos = map_cache.head.filesize;
+ map_cache.head.filesize += len_new;
+ }
+ map_cache.map[i].xs = m->xs;
+ map_cache.map[i].ys = m->ys;
+ map_cache.map[i].water_height = map_waterheight(m->name);
+ map_cache.dirty = 1;
+ if(map_read_flag == 2) {
+ aFree(write_buf);
+ }
+ return 0;
+ }
+ }
+ // 同じエントリが無ければ書き甲゚る場所を探す
+ for(i = 0;i < map_cache.head.nmaps ; i++) {
+ if(map_cache.map[i].fn[0] == 0) {
+ // 新しい場所に登録
+ if(map_read_flag == 2) {
+ write_buf = (char *) aMalloc(m->xs * m->ys * 2);
+ len_new = m->xs * m->ys * 2;
+ encode_zip((unsigned char *) write_buf,&len_new,m->gat,m->xs * m->ys);
+ map_cache.map[i].compressed = 1;
+ map_cache.map[i].compressed_len = len_new;
+ } else {
+ len_new = m->xs * m->ys;
+ write_buf = (char *) m->gat;
+ map_cache.map[i].compressed = 0;
+ map_cache.map[i].compressed_len = 0;
+ }
+ strncpy(map_cache.map[i].fn,m->name,sizeof(map_cache.map[0].fn));
+ fseek(map_cache.fp,map_cache.head.filesize,SEEK_SET);
+ fwrite(write_buf,1,len_new,map_cache.fp);
+ map_cache.map[i].pos = map_cache.head.filesize;
+ map_cache.map[i].xs = m->xs;
+ map_cache.map[i].ys = m->ys;
+ map_cache.map[i].water_height = map_waterheight(m->name);
+ map_cache.head.filesize += len_new;
+ map_cache.dirty = 1;
+ if(map_read_flag == 2) {
+ aFree(write_buf);
+ }
+ return 0;
+ }
+ }
+ // 書き甲゚なかった
+ return 1;
+}
+
+/*==========================================
+ * ?み?むmapを追加する
+ *------------------------------------------
+ */
+int map_addmap(char *mapname) {
+ if (strcmpi(mapname,"clear")==0) {
+ map_num=0;
+ return 0;
+ }
+
+ if (map_num >= MAX_MAP_PER_SERVER - 1) {
+ ShowError("Could not add map '"
+ CL_WHITE"%s"CL_RESET"', the limit of maps has been reached.\n",mapname);
+ return 1;
+ }
+ memcpy(map[map_num].name, mapname, MAP_NAME_LENGTH-1);
+ map_num++;
+ return 0;
+}
+
+/*==========================================
+ * Removes the map in the index passed.
+ *------------------------------------------
+ */
+static void map_delmapid(int id)
+{
+ ShowNotice("Removing map [ %s ] from maplist\n",map[id].name);
+ memmove(map+id, map+id+1, sizeof(map[0])*(map_num-id-1));
+ map_num--;
+}
+
+/*==========================================
+ * ?み?むmapを削除する
+ *------------------------------------------
+ */
+int map_delmap(char *mapname) {
+
+ int i;
+
+ if (strcmpi(mapname, "all") == 0) {
+ map_num = 0;
+ return 0;
+ }
+
+ for(i = 0; i < map_num; i++) {
+ if (strcmp(map[i].name, mapname) == 0) {
+ map_delmapid(i);
+ return 1;
+ }
+ }
+ return 0;
+}
+
+////////////////////////////////////////////////
+
+/*
+ Advanced Fusion Maps Support
+ (c) 2003-2004, The Fusion Project
+ - AlexKreuz
+
+ The following code has been provided by me for eAthena
+ under the GNU GPL. It provides Advanced Fusion
+ Map, the map format desgined by me for Fusion, support
+ for the eAthena emulator.
+
+ I understand that because it is under the GPL
+ that other emulators may very well use this code in their
+ GNU project as well.
+
+ The AFM map format was not originally a part of the GNU
+ GPL. It originated from scratch by my own hand. I understand
+ that distributing this code to read the AFM maps with eAthena
+ causes the GPL to apply to this code. But the actual AFM
+ maps are STILL copyrighted to the Fusion Project. By choosing
+
+ In exchange for that 'act of faith' I ask for the following.
+
+ A) Give credit where it is due. If you use this code, do not
+ place your name on the changelog. Credit should be given
+ to AlexKreuz.
+ B) As an act of courtesy, ask me and let me know that you are putting
+ AFM support in your project. You will have my blessings if you do.
+ C) Use the code in its entirety INCLUDING the copyright message.
+ Although the code provided may now be GPL, the AFM maps are not
+ and so I ask you to display the copyright message on the STARTUP
+ SCREEN as I have done here. (refer to core.c)
+ "Advanced Fusion Maps (c) 2003-2004 The Fusion Project"
+
+ Without this copyright, you are NOT entitled to bundle or distribute
+ the AFM maps at all. On top of that, your "support" for AFM maps
+ becomes just as shady as your "support" for Gravity GRF files.
+
+ The bottom line is this. I know that there are those of you who
+ would like to use this code but aren't going to want to provide the
+ proper credit. I know this because I speak frome experience. If
+ you are one of those people who is going to try to get around my
+ requests, then save your breath because I don't want to hear it.
+
+ I have zero faith in GPL and I know and accept that if you choose to
+ not display the copyright for the AFMs then there is absolutely nothing
+ I can do about it. I am not about to start a legal battle over something
+ this silly.
+
+ Provide the proper credit because you believe in the GPL. If you choose
+ not to and would rather argue about it, consider the GPL failed.
+
+ October 18th, 2004
+ - AlexKreuz
+ - The Fusion Project
+ */
+static int map_loadafm (struct map_data *m, char *fn)
+{
+ // check if .afm file exists
+ FILE *afm_file = fopen(fn, "r");
+ if (afm_file != NULL) {
+ int x,y,xs,ys;
+ char afm_line[65535];
+ int afm_size[2];
+ char *str;
+
+ str = fgets(afm_line, sizeof(afm_line)-1, afm_file);
+ str = fgets(afm_line, sizeof(afm_line)-1, afm_file);
+ str = fgets(afm_line, sizeof(afm_line)-1, afm_file);
+ sscanf(str , "%d%d", &afm_size[0], &afm_size[1]);
+
+ xs = m->xs = afm_size[0];
+ ys = m->ys = afm_size[1];
+ // check this, unsigned where it might not need to be
+ m->gat = (unsigned char*)aCallocA(xs * ys, 1);
+
+ for (y = 0; y < ys; y++) {
+ str = fgets(afm_line, sizeof(afm_line)-1, afm_file);
+ for (x = 0; x < xs; x++)
+ m->gat[x+y*xs] = str[x]-48;
+ }
+
+ fclose(afm_file);
+ return 1;
+ }
+
+ return 0;
+}
+/*==================================
+ * .AFM format
+ *----------------------------------
+ */
+int map_readafm (struct map_data *m)
+{
+ char afm_name[256] = "";
+ char fn[256], *p;
+
+ // convert map name to .afm
+ if(!strstr(m->name, ".afm")) {
+ // check if it's necessary to replace the extension - speeds up loading a bit
+ strncpy(afm_name, m->name, strlen(m->name) - 4);
+ strcat(afm_name, ".afm");
+ }
+
+ sprintf(fn, "%s\\%s", afm_dir, afm_name);
+ for (p = &fn[0]; *p != 0; p++)
+ if (*p == '\\') *p = '/'; // * At the time of Unix
+
+ return map_loadafm(m, fn);
+}
+/*==================================
+ * .AF2 format
+ *----------------------------------
+ */
+int map_readaf2 (struct map_data *m)
+{
+ FILE *af2_file;
+ char af2_name[256] = "";
+ char fn[256], *p, *out;
+
+ // convert map name to .af2
+ p = out = m->name;
+ while ((p = strchr(p, '/')) != NULL)
+ out = ++p;
+ strncpy (af2_name, out, strlen(out));
+ // grr, this is so troublesome >.< [celest]
+ p = strrchr (af2_name, '.');
+ if (p) *p++ = 0;
+ strcat(af2_name, ".af2");
+ sprintf(fn, "%s\\%s", afm_dir, af2_name);
+ for (p = &fn[0]; *p != 0; p++)
+ if (*p == '\\') *p = '/'; // * At the time of Unix
+
+ // check if .af2 file exists
+ af2_file = fopen(fn, "r");
+ if (af2_file != NULL) {
+ char out_file[256];
+
+ fclose(af2_file);
+
+ // convert map name to .out
+ strncpy (out_file, out, strlen(out));
+ p = strrchr (out_file, '.');
+ if (p) *p++ = 0;
+ strcat(out_file, ".out");
+
+ // unzip .out file and use loadafm()
+ if (deflate_file(fn, out_file) &&
+ map_loadafm(m, out_file))
+ {
+ unlink (out_file);
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+
+/*==========================================
+ * マップ1枚読み甲ン
+ * ===================================================*/
+//static int map_readmap(int m,char *fn, char *alias, int *map_cache, int maxmap) {
+
+/*==================================
+ * .GAT format
+ *----------------------------------
+ */
+int map_readgat (struct map_data *m)
+{
+ char fn[256], *pt;
+ char *gat;
+ int wh,x,y,xs,ys;
+ struct gat_1cell {float high[4]; int type;} *p = NULL;
+
+ if (strstr(m->name,".gat") == NULL)
+ return 0;
+
+ if ((pt = strstr(m->name,"<")) != NULL) { // [MouseJstr]
+ char buf[64];
+ *pt++ = '\0';
+ sprintf(buf,"data\\%s", pt);
+ m->alias = aStrdup(buf);
+ }
+
+ sprintf(fn,"data\\%s",m->name);
+
+ // read & convert fn
+ // again, might not need to be unsigned char
+ gat = (char *) grfio_read (fn);
+ if (gat == NULL)
+ return 0;
+
+ xs = m->xs = *(int*)(gat+6);
+ ys = m->ys = *(int*)(gat+10);
+ m->gat = (unsigned char *)aCallocA(m->xs * m->ys, sizeof(unsigned char));
+
+ wh = map_waterheight(m->name);
+ for (y = 0; y < ys; y++) {
+ p = (struct gat_1cell*)(gat+y*xs*20+14);
+ for (x = 0; x < xs; x++) {
+ if (wh != NO_WATER && p->type == 0)
+ // 水場判定
+ m->gat[x+y*xs] = (p->high[0]>wh || p->high[1]>wh || p->high[2]>wh || p->high[3]>wh) ? 3 : 0;
+ else
+ m->gat[x+y*xs] = p->type;
+ p++;
+ }
+ }
+
+ aFree(gat);
+
+ return 1;
+}
+
+//////////////////////////////////////////////////////
+
+static int map_cache_init (void);
+static int map_readafm_init (void);
+static int map_readaf2_init (void);
+static int map_readgat_init (void);
+
+// Todo: Properly implement this system as plugins/safer code [Celest]
+enum {
+ MAP_CACHE = 0, // jAthena map cache
+ MAP_AFM, // Advanced Fusion Map
+ MAP_AF2, // Advanced Fusion Map
+ MAP_GAT, // GRF map
+ MAP_MAXSOURCE
+};
+// in descending order
+int (*mapsource_init[MAP_MAXSOURCE])(void) = {
+ map_cache_init,
+ map_readafm_init,
+ map_readaf2_init,
+ map_readgat_init
+};
+int (*mapsource_read[MAP_MAXSOURCE])(struct map_data *) = {
+ map_cache_read,
+ map_readafm,
+ map_readaf2,
+ map_readgat
+};
+void (*mapsource_final[MAP_MAXSOURCE])(void) = {
+ map_cache_close,
+ NULL,
+ NULL,
+ NULL
+};
+
+static int map_cache_init (void)
+{
+ if (map_read_flag >= READ_FROM_BITMAP && map_cache_open(map_cache_file)) {
+ ShowMessage("[cache] ");
+ return 1;
+ }
+
+ return 0;
+}
+static int map_readafm_init (void)
+{
+ ShowMessage("[afm] ");
+ return 1;
+}
+static int map_readaf2_init (void)
+{
+ // check if AFM loading is available,
+ // otherwise disable AF2 loading
+ if (mapsource_read[1] != NULL) {
+ ShowMessage("[af2] ");
+ return 1;
+ }
+
+ return 0;
+}
+static int map_readgat_init (void)
+{
+ ShowMessage("[gat] ");
+ return 1;
+}
+
+/*======================================
+ * Initiate maps loading stage
+ *--------------------------------------
+ */
+int map_readallmaps (void)
+{
+ // pre-loading stage
+ int i;
+ int maps_removed = 0;
+ int maps_cached = 0;
+
+ ShowMessage(CL_GREEN"[Status]"CL_RESET": Loading Maps with... "CL_WHITE);
+
+ for (i = 0; i < MAP_MAXSOURCE; i++) {
+ if (mapsource_init[i] && // check if source requires initialisation
+ mapsource_init[i]() == 0) // if init failed
+ {
+ // remove all loading methods associated with this source
+ mapsource_init[i] = NULL;
+ mapsource_read[i] = NULL;
+ mapsource_final[i] = NULL;
+ }
+ }
+
+ ShowMessage(CL_RESET"\n");
+
+ // initiate map loading
+ for (i = 0; i < map_num; i++)
+ {
+ int success = 0;
+ static int lasti = -1;
+ static int last_time = -1;
+ int j = i*20/map_num;
+
+ // show progress
+ if (map_num && //avoid map-server crashing if there are 0 maps
+ (j != lasti || last_time != time(0)))
+ {
+ char progress[21] = " ";
+ char c = '-';
+ int k;
+
+ lasti = j;
+ printf("\r");
+ ShowStatus("Progress: [");
+ for (k=0; k < j; k++) progress[k] = '#';
+ printf(progress);
+ last_time = (int)time(0);
+ switch(last_time % 4) {
+ case 0: c='\\'; break;
+ case 1: c='|'; break;
+ case 2: c='/'; break;
+ case 3: c='-'; break;
+ }
+ printf("] Working: [%c]",c);
+ fflush(stdout);
+ }
+
+ // pre-init some data
+ map[i].alias = NULL;
+ map[i].m = i;
+ memset (map[i].moblist, 0, sizeof(map[i].moblist)); //Initialize moblist [Skotlex]
+ map[i].mob_delete_timer = -1; //Initialize timer [Skotlex]
+ if (battle_config.pk_mode)
+ map[i].flag.pvp = 1; // make all maps pvp for pk_mode [Valaris]
+
+ for (j = 0; j < MAP_MAXSOURCE; j++)
+ {
+ if (mapsource_read[j] && // check if map source is valid
+ mapsource_read[j](&map[i])) // check if map source is available
+ {
+ // successful, now initialise map
+ size_t size;
+ char *alias;
+
+ if (map[i].alias && (alias = strstr(map[i].name, "<")) != NULL) { // alias has been set by one of the sources
+ *alias++ = '\0';
+ }
+ if (map[i].alias)
+ map[i].index = mapindex_name2id(map[i].alias);
+ else
+ map[i].index = mapindex_name2id(map[i].name);
+
+ if (!map[i].index) {
+ if (map[i].alias)
+ ShowWarning("Map %s (alias %s) is not in the map-index cache!\n", map[i].name, map[i].alias);
+ else
+ ShowWarning("Map %s is not in the map-index cache!\n", map[i].name);
+ success = 0; //Can't load a map that isn't in our cache.
+ if (map[i].gat) {
+ aFree(map[i].gat);
+ map[i].gat = NULL;
+ }
+ break;
+ }
+ if (uidb_get(map_db,(unsigned int)map[i].index) != NULL) {
+ ShowWarning("Map %s already loaded!\n", map[i].name);
+ success = 0; //Can't load a map already in the db
+ if (map[i].gat) {
+ aFree(map[i].gat);
+ map[i].gat = NULL;
+ }
+ break;
+ }
+
+ map[i].cell = (unsigned char *)aCalloc(map[i].xs * map[i].ys, sizeof(unsigned char));
+#ifdef CELL_NOSTACK
+ map[i].cell_bl = (unsigned char *)aCalloc(map[i].xs * map[i].ys, sizeof(unsigned char));
+#endif
+
+ map[i].bxs = (map[i].xs + BLOCK_SIZE - 1) / BLOCK_SIZE;
+ map[i].bys = (map[i].ys + BLOCK_SIZE - 1) / BLOCK_SIZE;
+
+ size = map[i].bxs * map[i].bys * sizeof(struct block_list*);
+ map[i].block = (struct block_list**)aCalloc(size, 1);
+ map[i].block_mob = (struct block_list**)aCalloc(size, 1);
+
+ size = map[i].bxs * map[i].bys * sizeof(int);
+ map[i].block_count = (int*)aCallocA(size, 1);
+ memset(map[i].block_count, 0, size);
+
+ map[i].block_mob_count = (int*)aCallocA(size, 1);
+ memset(map[i].block_mob_count, 0, size);
+
+ uidb_put(map_db, (unsigned int)map[i].index, &map[i]);
+
+ // cache our map if necessary
+ if (j != MAP_CACHE && mapsource_read[MAP_CACHE] != NULL) { // map data is not cached yet
+ map_cache_write(&map[i]);
+ maps_cached++;
+ }
+
+ // next map
+ success = 1;
+ break;
+ }
+ }
+
+ // no sources have been found, so remove map from list
+ if (!success) {
+ map_delmapid(i);
+ maps_removed++;
+ i--;
+ }
+ }
+
+ // unload map sources
+ for (i = 0; i < MAP_MAXSOURCE; i++) {
+ if (mapsource_final[i])
+ mapsource_final[i]();
+ }
+
+ // finished map loading
+ aFree(waterlist);
+ printf("\r");
+ ShowInfo("Successfully loaded '"CL_WHITE"%d"CL_RESET"' maps.%30s\n",map_num,"");
+
+ if (maps_removed)
+ ShowNotice("Maps Removed: '"CL_WHITE"%d"CL_RESET"'\n",maps_removed);
+ if (maps_cached)
+ ShowNotice("Maps Added to Cache: '"CL_WHITE"%d"CL_RESET"'\n",maps_cached);
+
+ return 0;
+}
+
+////////////////////////////////////////////////////////////////////////
+
+static int map_ip_set_ = 0;
+static int char_ip_set_ = 0;
+//static int bind_ip_set_ = 0;
+
+/*==========================================
+ * Console Command Parser [Wizputer]
+ *------------------------------------------
+ */
+int parse_console(char *buf) {
+ char *type,*command,*map, *buf2;
+ int x = 0, y = 0;
+ int m, n;
+ struct map_session_data *sd;
+
+ sd = (struct map_session_data*)aCalloc(sizeof(*sd), 1);
+
+ sd->fd = 0;
+ strcpy( sd->status.name , "console");
+
+ type = (char *)aMallocA(64);
+ command = (char *)aMallocA(64);
+ map = (char *)aMallocA(64);
+ buf2 = (char *)aMallocA(72);
+
+ memset(type,0,64);
+ memset(command,0,64);
+ memset(map,0,64);
+ memset(buf2,0,72);
+
+ if ( ( n = sscanf(buf, "%[^:]:%[^:]:%99s %d %d[^\n]", type , command , map , &x , &y )) < 5 )
+ if ( ( n = sscanf(buf, "%[^:]:%[^\n]", type , command )) < 2 )
+ n = sscanf(buf,"%[^\n]",type);
+
+ if ( n == 5 ) {
+ if (x <= 0) {
+ x = rand() % 399 + 1;
+ sd->bl.x = x;
+ } else {
+ sd->bl.x = x;
+ }
+
+ if (y <= 0) {
+ y = rand() % 399 + 1;
+ sd->bl.y = y;
+ } else {
+ sd->bl.y = y;
+ }
+
+ m = map_mapname2mapid(map);
+ if ( m >= 0 )
+ sd->bl.m = m;
+ else {
+ ShowWarning("Console: Unknown map\n");
+ goto end;
+ }
+ }
+
+ ShowInfo("Type of command: %s || Command: %s || Map: %s Coords: %d %d\n",type,command,map,x,y);
+
+ if ( strcmpi("admin",type) == 0 && n == 5 ) {
+ sprintf(buf2,"console: %s",command);
+ if( is_atcommand(sd->fd,sd,buf2,99) == AtCommand_None )
+ printf("Console: not atcommand\n");
+ } else if ( strcmpi("server",type) == 0 && n == 2 ) {
+ if ( strcmpi("shutdown", command) == 0 || strcmpi("exit",command) == 0 || strcmpi("quit",command) == 0 ) {
+ runflag = 0;
+ }
+ } else if ( strcmpi("help",type) == 0 ) {
+ ShowNotice("To use GM commands:\n");
+ printf("admin:<gm command>:<map of \"gm\"> <x> <y>\n");
+ printf("You can use any GM command that doesn't require the GM.\n");
+ printf("No using @item or @warp however you can use @charwarp\n");
+ printf("The <map of \"gm\"> <x> <y> is for commands that need coords of the GM\n");
+ printf("IE: @spawn\n");
+ printf("To shutdown the server:\n");
+ printf("server:shutdown\n");
+ }
+
+ end:
+ aFree(buf);
+ aFree(type);
+ aFree(command);
+ aFree(map);
+ aFree(buf2);
+ aFree(sd);
+
+ return 0;
+}
+
+/*==========================================
+ * 設定ファイルを?み?む
+ *------------------------------------------
+ */
+int map_config_read(char *cfgName) {
+ char line[1024], w1[1024], w2[1024];
+ FILE *fp;
+ struct hostent *h = NULL;
+
+ fp = fopen(cfgName,"r");
+ if (fp == NULL) {
+ ShowFatalError("Map configuration file not found at: %s\n", cfgName);
+ exit(1);
+ }
+ while(fgets(line, sizeof(line) -1, fp)) {
+ if (line[0] == '/' && line[1] == '/')
+ continue;
+ if (sscanf(line, "%[^:]: %[^\r\n]", w1, w2) == 2) {
+ if(strcmpi(w1,"timestamp_format")==0){
+ strncpy(timestamp_format, w2, 20);
+ } else if(strcmpi(w1,"console_silent")==0){
+ msg_silent = 0; //To always allow the next line to show up.
+ ShowInfo("Console Silent Setting: %d\n", atoi(w2));
+ msg_silent = atoi(w2);
+ } else if (strcmpi(w1, "userid")==0){
+ chrif_setuserid(w2);
+ } else if (strcmpi(w1, "passwd") == 0) {
+ chrif_setpasswd(w2);
+ } else if (strcmpi(w1, "char_ip") == 0) {
+ char_ip_set_ = 1;
+ h = gethostbyname (w2);
+ if(h != NULL) {
+ ShowInfo("Char Server IP Address : '"CL_WHITE"%s"CL_RESET"' -> '"CL_WHITE"%d.%d.%d.%d"CL_RESET"'.\n", w2, (unsigned char)h->h_addr[0], (unsigned char)h->h_addr[1], (unsigned char)h->h_addr[2], (unsigned char)h->h_addr[3]);
+ sprintf(w2,"%d.%d.%d.%d", (unsigned char)h->h_addr[0], (unsigned char)h->h_addr[1], (unsigned char)h->h_addr[2], (unsigned char)h->h_addr[3]);
+ }
+ chrif_setip(w2);
+ } else if (strcmpi(w1, "char_port") == 0) {
+ chrif_setport(atoi(w2));
+ } else if (strcmpi(w1, "map_ip") == 0) {
+ map_ip_set_ = 1;
+ h = gethostbyname (w2);
+ if (h != NULL) {
+ ShowInfo("Map Server IP Address : '"CL_WHITE"%s"CL_RESET"' -> '"CL_WHITE"%d.%d.%d.%d"CL_RESET"'.\n", w2, (unsigned char)h->h_addr[0], (unsigned char)h->h_addr[1], (unsigned char)h->h_addr[2], (unsigned char)h->h_addr[3]);
+ sprintf(w2, "%d.%d.%d.%d", (unsigned char)h->h_addr[0], (unsigned char)h->h_addr[1], (unsigned char)h->h_addr[2], (unsigned char)h->h_addr[3]);
+ }
+ clif_setip(w2);
+ } else if (strcmpi(w1, "bind_ip") == 0) {
+ //bind_ip_set_ = 1;
+ h = gethostbyname (w2);
+ if (h != NULL) {
+ ShowInfo("Map Server IP Address : '"CL_WHITE"%s"CL_RESET"' -> '"CL_WHITE"%d.%d.%d.%d"CL_RESET"'.\n", w2, (unsigned char)h->h_addr[0], (unsigned char)h->h_addr[1], (unsigned char)h->h_addr[2], (unsigned char)h->h_addr[3]);
+ sprintf(w2, "%d.%d.%d.%d", (unsigned char)h->h_addr[0], (unsigned char)h->h_addr[1], (unsigned char)h->h_addr[2], (unsigned char)h->h_addr[3]);
+ }
+ clif_setbindip(w2);
+ } else if (strcmpi(w1, "map_port") == 0) {
+ clif_setport(atoi(w2));
+ map_port = (atoi(w2));
+ } else if (strcmpi(w1, "water_height") == 0) {
+ map_readwater(w2);
+ } else if (strcmpi(w1, "map") == 0) {
+ map_addmap(w2);
+ } else if (strcmpi(w1, "delmap") == 0) {
+ map_delmap(w2);
+ } else if (strcmpi(w1, "npc") == 0) {
+ npc_addsrcfile(w2);
+ } else if (strcmpi(w1, "delnpc") == 0) {
+ npc_delsrcfile(w2);
+ } else if (strcmpi(w1, "autosave_time") == 0) {
+ autosave_interval = atoi(w2) * 1000;
+ if (autosave_interval <= 0)
+ autosave_interval = DEFAULT_AUTOSAVE_INTERVAL;
+ } else if (strcmpi(w1, "motd_txt") == 0) {
+ strcpy(motd_txt, w2);
+ } else if (strcmpi(w1, "help_txt") == 0) {
+ strcpy(help_txt, w2);
+ } else if (strcmpi(w1, "help2_txt") == 0) {
+ strcpy(help2_txt, w2);
+ } else if (strcmpi(w1, "charhelp_txt") == 0) {
+ strcpy(charhelp_txt, w2);
+ } else if (strcmpi(w1, "mapreg_txt") == 0) {
+ strcpy(mapreg_txt, w2);
+ } else if(strcmpi(w1,"read_map_from_cache") == 0){
+ if (atoi(w2) == 2)
+ map_read_flag = READ_FROM_BITMAP_COMPRESSED;
+ else if (atoi(w2) == 1)
+ map_read_flag = READ_FROM_BITMAP;
+ else
+ map_read_flag = READ_FROM_GAT;
+ } else if(strcmpi(w1,"map_cache_file") == 0) {
+ strncpy(map_cache_file,w2,255);
+ } else if(strcmpi(w1,"db_path") == 0) {
+ strncpy(db_path,w2,255);
+ } else if(strcmpi(w1,"afm_dir") == 0) {
+ strcpy(afm_dir, w2);
+ } else if (strcmpi(w1, "console") == 0) {
+ if(strcmpi(w2,"on") == 0 || strcmpi(w2,"yes") == 0 ) {
+ console = 1;
+ ShowNotice("Console Commands are enabled.\n");
+ }
+ } else if (strcmpi(w1, "enable_spy") == 0) {
+ if(strcmpi(w2,"on") == 0 || strcmpi(w2,"yes") == 0 )
+ enable_spy = 1;
+ else
+ enable_spy = 0;
+ } else if (strcmpi(w1, "import") == 0) {
+ map_config_read(w2);
+ }
+ }
+ }
+ fclose(fp);
+
+ return 0;
+}
+
+int inter_config_read(char *cfgName)
+{
+ int i;
+ char line[1024],w1[1024],w2[1024];
+ FILE *fp;
+
+ fp=fopen(cfgName,"r");
+ if(fp==NULL){
+ ShowError("File not found: '%s'.\n",cfgName);
+ return 1;
+ }
+ while(fgets(line,1020,fp)){
+ if(line[0] == '/' && line[1] == '/')
+ continue;
+ i=sscanf(line,"%[^:]: %[^\r\n]",w1,w2);
+ if(i!=2)
+ continue;
+ if(strcmpi(w1,"kick_on_disconnect")==0){
+ kick_on_disconnect = battle_config_switch(w2);
+ } else if(strcmpi(w1,"party_share_level")==0){
+ party_share_level = battle_config_switch(w2);
+ } else if(strcmpi(w1,"lowest_gm_level")==0){
+ lowest_gm_level = atoi(w2);
+
+ /* Main chat nick [LuzZza] */
+ } else if(strcmpi(w1, "main_chat_nick")==0){
+ strcpy(main_chat_nick, w2);
+
+ #ifndef TXT_ONLY
+ } else if(strcmpi(w1,"charsave_method")==0){
+ charsave_method = atoi(w2); //New char saving method.
+ } else if(strcmpi(w1,"item_db_db")==0){
+ strcpy(item_db_db,w2);
+ } else if(strcmpi(w1,"mob_db_db")==0){
+ strcpy(mob_db_db,w2);
+ } else if(strcmpi(w1,"item_db2_db")==0){
+ strcpy(item_db2_db,w2);
+ } else if(strcmpi(w1,"mob_db2_db")==0){
+ strcpy(mob_db2_db,w2);
+ } else if(strcmpi(w1,"login_db_level")==0){
+ strcpy(login_db_level,w2);
+ } else if(strcmpi(w1,"login_db_account_id")==0){
+ strcpy(login_db_account_id,w2);
+ } else if(strcmpi(w1,"login_db")==0){
+ strcpy(login_db,w2);
+ } else if (strcmpi(w1, "char_db") == 0) {
+ strcpy(char_db, w2);
+ } else if(strcmpi(w1,"gm_db_level")==0){
+ strcpy(gm_db_level,w2);
+ } else if(strcmpi(w1,"gm_db_account_id")==0){
+ strcpy(gm_db_account_id,w2);
+ } else if(strcmpi(w1,"gm_db")==0){
+ strcpy(gm_db,w2);
+ //Map Server SQL DB
+ } else if(strcmpi(w1,"map_server_ip")==0){
+ strcpy(map_server_ip, w2);
+ } else if(strcmpi(w1,"map_server_port")==0){
+ map_server_port=atoi(w2);
+ } else if(strcmpi(w1,"map_server_id")==0){
+ strcpy(map_server_id, w2);
+ } else if(strcmpi(w1,"map_server_pw")==0){
+ strcpy(map_server_pw, w2);
+ } else if(strcmpi(w1,"map_server_db")==0){
+ strcpy(map_server_db, w2);
+ } else if(strcmpi(w1,"default_codepage")==0){
+ strcpy(default_codepage, w2);
+ } else if(strcmpi(w1,"use_sql_db")==0){
+ db_use_sqldbs = battle_config_switch(w2);
+ ShowStatus ("Using SQL dbs: %s\n",w2);
+ } else if(strcmpi(w1,"use_new_sql_db")==0){
+ db_use_newsqldbs = battle_config_switch(w2);
+ ShowStatus ("Using New SQL dbs: %s\n",w2);
+ //Login Server SQL DB
+ } else if(strcmpi(w1,"login_server_ip")==0){
+ strcpy(login_server_ip, w2);
+ } else if(strcmpi(w1,"login_server_port")==0){
+ login_server_port = atoi(w2);
+ } else if(strcmpi(w1,"login_server_id")==0){
+ strcpy(login_server_id, w2);
+ } else if(strcmpi(w1,"login_server_pw")==0){
+ strcpy(login_server_pw, w2);
+ } else if(strcmpi(w1,"login_server_db")==0){
+ strcpy(login_server_db, w2);
+ } else if(strcmpi(w1,"read_gm_interval")==0){
+ read_gm_interval = ( atoi(w2) * 60 * 1000 ); // Minutes multiplied by 60 secs per min by 1000 milliseconds per second
+ }else if(strcmpi(w1, "char_server_ip") == 0){
+ strcpy(charsql_host, w2);
+ }else if(strcmpi(w1, "char_server_port") == 0){
+ charsql_port = atoi(w2);
+ }else if(strcmpi(w1, "char_server_id") == 0){
+ strcpy(charsql_user, w2);
+ }else if(strcmpi(w1, "char_server_pw") == 0){
+ strcpy(charsql_pass, w2);
+ }else if(strcmpi(w1, "char_server_db") == 0){
+ strcpy(charsql_db, w2);
+ } else if(strcmpi(w1,"log_db")==0) {
+ strcpy(log_db, w2);
+ } else if(strcmpi(w1,"log_db_ip")==0) {
+ strcpy(log_db_ip, w2);
+ } else if(strcmpi(w1,"log_db")==0) {
+ strcpy(log_db, w2);
+ } else if(strcmpi(w1,"log_db_id")==0) {
+ strcpy(log_db_id, w2);
+ } else if(strcmpi(w1,"log_db_pw")==0) {
+ strcpy(log_db_pw, w2);
+ } else if(strcmpi(w1,"log_db_port")==0) {
+ log_db_port = atoi(w2);
+ // Mail Server SQL
+ } else if(strcmpi(w1,"mail_server_enable")==0){
+ mail_server_enable = battle_config_switch(w2);
+ ShowStatus ("Using Mail Server: %s\n",w2);
+ } else if(strcmpi(w1,"mail_server_ip")==0){
+ strcpy(mail_server_ip, w2);
+ } else if(strcmpi(w1,"mail_server_port")==0){
+ mail_server_port=atoi(w2);
+ } else if(strcmpi(w1,"mail_server_id")==0){
+ strcpy(mail_server_id, w2);
+ } else if(strcmpi(w1,"mail_server_pw")==0){
+ strcpy(mail_server_pw, w2);
+ } else if(strcmpi(w1,"mail_server_db")==0){
+ strcpy(mail_server_db, w2);
+ } else if(strcmpi(w1,"mail_db")==0) {
+ strcpy(mail_db, w2);
+ #endif
+ //support the import command, just like any other config
+ } else if(strcmpi(w1,"import")==0){
+ inter_config_read(w2);
+ }
+ }
+ fclose(fp);
+
+ return 0;
+}
+
+#ifndef TXT_ONLY
+/*=======================================
+ * MySQL Init
+ *---------------------------------------
+ */
+
+int map_sql_init(void){
+
+ mysql_init(&mmysql_handle);
+
+ //DB connection start
+ ShowInfo("Connecting to the Map DB Server....\n");
+ if(!mysql_real_connect(&mmysql_handle, map_server_ip, map_server_id, map_server_pw,
+ map_server_db ,map_server_port, (char *)NULL, 0)) {
+ //pointer check
+ ShowSQL("DB error - %s\n",mysql_error(&mmysql_handle));
+ exit(1);
+ }
+ else {
+ ShowStatus("connect success! (Map Server Connection)\n");
+ }
+
+ mysql_init(&lmysql_handle);
+
+ //DB connection start
+ ShowInfo("Connecting to the Login DB Server....\n");
+ if(!mysql_real_connect(&lmysql_handle, login_server_ip, login_server_id, login_server_pw,
+ login_server_db ,login_server_port, (char *)NULL, 0)) {
+ //pointer check
+ ShowSQL("DB error - %s\n",mysql_error(&lmysql_handle));
+ exit(1);
+ }
+ else {
+ ShowStatus ("connect success! (Login Server Connection)\n");
+ }
+
+#ifdef MAPREGSQL
+ // [zBuffer] SQL Mapreg connection start
+ ShowInfo("Connect Mapreg DB Server....\n");
+ if(!mysql_real_connect(&mapregsql_handle, map_server_ip, map_server_id, map_server_pw,
+ map_server_db ,map_server_port, (char *)NULL, 0)) {
+ //pointer check
+ ShowSQL("DB error - %s\n",mysql_error(&mapregsql_handle));
+ exit(1);
+ } else {
+ ShowStatus ("Connect success! (Mapreg DB Connection)\n");
+ if( strlen(default_codepage) > 0 ) {
+ sprintf( tmp_sql, "SET NAMES %s", default_codepage );
+ if (mysql_query(&mapregsql_handle, tmp_sql)) {
+ ShowSQL("DB error - %s\n",mysql_error(&mapregsql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ }
+ }
+#endif
+ if(mail_server_enable) { // mail system [Valaris]
+ mysql_init(&mail_handle);
+ ShowInfo("Connecting to the Mail DB Server....\n");
+ if(!mysql_real_connect(&mail_handle, mail_server_ip, mail_server_id, mail_server_pw,
+ mail_server_db ,mail_server_port, (char *)NULL, 0)) {
+ ShowSQL("DB error - %s\n",mysql_error(&mail_handle));
+ exit(1);
+ }
+ if( strlen(default_codepage) > 0 ) {
+ sprintf( tmp_sql, "SET NAMES %s", default_codepage );
+ if (mysql_query(&mail_handle, tmp_sql)) {
+ ShowSQL("DB error - %s\n",mysql_error(&mail_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ }
+ }
+ if( strlen(default_codepage) > 0 ) {
+ sprintf( tmp_sql, "SET NAMES %s", default_codepage );
+ if (mysql_query(&mmysql_handle, tmp_sql)) {
+ ShowSQL("DB error - %s\n",mysql_error(&mmysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ if (mysql_query(&lmysql_handle, tmp_sql)) {
+ ShowSQL("DB error - %s\n",mysql_error(&lmysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ }
+ return 0;
+}
+
+int map_sql_close(void){
+ mysql_close(&mmysql_handle);
+ ShowStatus("Close Map DB Connection....\n");
+
+ mysql_close(&lmysql_handle);
+ ShowStatus("Close Login DB Connection....\n");
+
+#ifdef MAPREGSQL
+ // [zBuffer] SQL Mapreg connection stop
+ mysql_close(&mapregsql_handle);
+ ShowStatus("Close Mapreg DB Connection....\n");
+#endif
+
+ if (log_config.sql_logs)
+//Updating this if each time there's a log_config addition is too much of a hassle. [Skotlex]
+ /*&& (log_config.branch || log_config.drop || log_config.mvpdrop ||
+ log_config.present || log_config.produce || log_config.refine || log_config.trade))*/
+ {
+ mysql_close(&logmysql_handle);
+ ShowStatus("Close Log DB Connection....\n");
+ }
+
+ return 0;
+}
+
+int log_sql_init(void){
+
+ mysql_init(&logmysql_handle);
+
+ //DB connection start
+ ShowInfo(""CL_WHITE"[SQL]"CL_RESET": Connecting to the Log Database "CL_WHITE"%s"CL_RESET" At "CL_WHITE"%s"CL_RESET"...\n",log_db,log_db_ip);
+ if(!mysql_real_connect(&logmysql_handle, log_db_ip, log_db_id, log_db_pw,
+ log_db ,log_db_port, (char *)NULL, 0)) {
+ //pointer check
+ ShowSQL("DB error - %s\n",mysql_error(&logmysql_handle));
+ exit(1);
+ }
+
+ ShowStatus(""CL_WHITE"[SQL]"CL_RESET": Successfully '"CL_GREEN"connected"CL_RESET"' to Database '"CL_WHITE"%s"CL_RESET"'.\n", log_db);
+ if( strlen(default_codepage) > 0 ) {
+ sprintf( tmp_sql, "SET NAMES %s", default_codepage );
+ if (mysql_query(&logmysql_handle, tmp_sql)) {
+ ShowSQL("DB error - %s\n",mysql_error(&logmysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ }
+ return 0;
+}
+#endif /* not TXT_ONLY */
+
+int map_db_final(DBKey k,void *d,va_list ap)
+{
+ // Not needed actually, these are already freed. [Lance]
+ //struct map_data_other_server *mdos = (struct map_data_other_server*)d;
+ //if(mdos->gat == NULL)
+ // aFree(mdos);
+ return 0;
+}
+int nick_db_final(void *k,void *d,va_list ap)
+{
+ char *p = (char *) d;
+ if (p) aFree(p);
+ return 0;
+}
+
+int cleanup_sub(struct block_list *bl, va_list ap) {
+ nullpo_retr(0, bl);
+
+ switch(bl->type) {
+ case BL_PC:
+ map_quit((struct map_session_data *) bl);
+ break;
+ case BL_NPC:
+ npc_unload((struct npc_data *)bl);
+ break;
+ case BL_MOB:
+ mob_unload((struct mob_data *)bl);
+ break;
+ case BL_PET:
+ //There is no need for this, the pet is removed together with the player. [Skotlex]
+// pet_remove_map(((struct pet_data *)bl)->msd);
+ break;
+ case BL_ITEM:
+ map_clearflooritem(bl->id);
+ break;
+ case BL_SKILL:
+ skill_delunit((struct skill_unit *) bl);
+ break;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * map鯖終了・理
+ *------------------------------------------
+ */
+void do_final(void) {
+ int i, j;
+ struct map_session_data **pl_allsd;
+
+ ShowStatus("Terminating...\n");
+
+ // we probably don't need the cache open at all times 'yet', so this is closed by mapsource_final [celest]
+ //map_cache_close();
+ grfio_final();
+
+ for (i = 0; i < map_num; i++)
+ if (map[i].m >= 0)
+ map_foreachinmap(cleanup_sub, i, BL_ALL);
+
+ //Scan any remaining players (between maps?) to kick them out. [Skotlex]
+ pl_allsd = map_getallusers(&j);
+ for (i = 0; i < j; i++)
+ map_quit(pl_allsd[i]);
+
+ chrif_char_reset_offline();
+ chrif_flush_fifo();
+
+ do_final_atcommand();
+ do_final_chrif(); // この内部でキャラを全て切断する
+ do_final_npc();
+// map_removenpc();
+ do_final_script();
+ do_final_itemdb();
+ do_final_storage();
+ do_final_guild();
+ do_final_party();
+ do_final_pc();
+ do_final_pet();
+ do_final_mob();
+ do_final_msg();
+
+ map_getallusers(NULL); //Clear the memory allocated for this array.
+
+ map_db->destroy(map_db, map_db_final);
+
+ for (i=0; i<map_num; i++) {
+ if(map[i].gat) aFree(map[i].gat);
+ if(map[i].cell) aFree(map[i].cell);
+#ifdef CELL_NOSTACK
+ if(map[i].cell_bl) aFree(map[i].cell_bl);
+#endif
+ if(map[i].block) aFree(map[i].block);
+ if(map[i].block_mob) aFree(map[i].block_mob);
+ if(map[i].block_count) aFree(map[i].block_count);
+ if(map[i].block_mob_count) aFree(map[i].block_mob_count);
+ if(battle_config.dynamic_mobs) { //Dynamic mobs flag by [random]
+ for (j=0; j<MAX_MOB_LIST_PER_MAP; j++)
+ if (map[i].moblist[j]) aFree(map[i].moblist[j]);
+ }
+ }
+
+ mapindex_final();
+
+ id_db->destroy(id_db, NULL);
+ pc_db->destroy(pc_db, NULL);
+ charid_db->destroy(charid_db, NULL);
+
+//#endif
+
+#ifndef TXT_ONLY
+ map_sql_close();
+ if(charsave_method)
+ charsql_db_init(0); //Connecting to chardb
+#endif /* not TXT_ONLY */
+ ShowStatus("Successfully terminated.\n");
+}
+
+/*======================================================
+ * Map-Server Version Screen [MC Cameri]
+ *------------------------------------------------------
+ */
+void map_helpscreen(int flag) { // by MC Cameri
+ puts("Usage: map-server [options]");
+ puts("Options:");
+ puts(CL_WHITE" Commands\t\t\tDescription"CL_RESET);
+ puts("-----------------------------------------------------------------------------");
+ puts(" --help, --h, --?, /? Displays this help screen");
+ puts(" --map-config <file> Load map-server configuration from <file>");
+ puts(" --battle-config <file> Load battle configuration from <file>");
+ puts(" --atcommand-config <file> Load atcommand configuration from <file>");
+ puts(" --charcommand-config <file> Load charcommand configuration from <file>");
+ puts(" --script-config <file> Load script configuration from <file>");
+ puts(" --msg-config <file> Load message configuration from <file>");
+ puts(" --grf-path-file <file> Load grf path file configuration from <file>");
+ puts(" --sql-config <file> Load inter-server configuration from <file>");
+ puts(" (SQL Only)");
+ puts(" --log-config <file> Load logging configuration from <file>");
+ puts(" (SQL Only)");
+ puts(" --version, --v, -v, /v Displays the server's version");
+ puts("\n");
+ if (flag) exit(1);
+}
+
+/*======================================================
+ * Map-Server Version Screen [MC Cameri]
+ *------------------------------------------------------
+ */
+void map_versionscreen(int flag) {
+ printf("CL_WHITE" "eAthena version %d.%02d.%02d, Athena Mod version %d" CL_RESET"\n",
+ ATHENA_MAJOR_VERSION, ATHENA_MINOR_VERSION, ATHENA_REVISION,
+ ATHENA_MOD_VERSION);
+ puts(CL_GREEN "Website/Forum:" CL_RESET "\thttp://eathena.deltaanime.net/");
+ puts(CL_GREEN "Download URL:" CL_RESET "\thttp://eathena.systeminplace.net/");
+ puts(CL_GREEN "IRC Channel:" CL_RESET "\tirc://irc.deltaanime.net/#athena");
+ puts("\nOpen " CL_WHITE "readme.html" CL_RESET " for more information.");
+ if (ATHENA_RELEASE_FLAG) ShowNotice("This version is not for release.\n");
+ if (flag) exit(1);
+}
+
+/*======================================================
+ * Map-Server Init and Command-line Arguments [Valaris]
+ *------------------------------------------------------
+ */
+void set_server_type(void)
+{
+ SERVER_TYPE = ATHENA_SERVER_MAP;
+}
+int do_init(int argc, char *argv[]) {
+ int i;
+
+#ifdef GCOLLECT
+ GC_enable_incremental();
+#endif
+
+ INTER_CONF_NAME="conf/inter_athena.conf";
+ LOG_CONF_NAME="conf/log_athena.conf";
+ MAP_CONF_NAME = "conf/map_athena.conf";
+ BATTLE_CONF_FILENAME = "conf/battle_athena.conf";
+ ATCOMMAND_CONF_FILENAME = "conf/atcommand_athena.conf";
+ CHARCOMMAND_CONF_FILENAME = "conf/charcommand_athena.conf";
+ SCRIPT_CONF_NAME = "conf/script_athena.conf";
+ MSG_CONF_NAME = "conf/msg_athena.conf";
+ GRF_PATH_FILENAME = "conf/grf-files.txt";
+
+ chrif_connected = 0;
+
+ srand(gettick());
+
+ for (i = 1; i < argc ; i++) {
+ if (strcmp(argv[i], "--help") == 0 || strcmp(argv[i], "--h") == 0 || strcmp(argv[i], "--?") == 0 || strcmp(argv[i], "/?") == 0)
+ map_helpscreen(1);
+ else if (strcmp(argv[i], "--version") == 0 || strcmp(argv[i], "--v") == 0 || strcmp(argv[i], "-v") == 0 || strcmp(argv[i], "/v") == 0)
+ map_versionscreen(1);
+ else if (strcmp(argv[i], "--map_config") == 0 || strcmp(argv[i], "--map-config") == 0)
+ MAP_CONF_NAME=argv[i+1];
+ else if (strcmp(argv[i],"--battle_config") == 0 || strcmp(argv[i],"--battle-config") == 0)
+ BATTLE_CONF_FILENAME = argv[i+1];
+ else if (strcmp(argv[i],"--atcommand_config") == 0 || strcmp(argv[i],"--atcommand-config") == 0)
+ ATCOMMAND_CONF_FILENAME = argv[i+1];
+ else if (strcmp(argv[i],"--charcommand_config") == 0 || strcmp(argv[i],"--charcommand-config") == 0)
+ CHARCOMMAND_CONF_FILENAME = argv[i+1];
+ else if (strcmp(argv[i],"--script_config") == 0 || strcmp(argv[i],"--script-config") == 0)
+ SCRIPT_CONF_NAME = argv[i+1];
+ else if (strcmp(argv[i],"--msg_config") == 0 || strcmp(argv[i],"--msg-config") == 0)
+ MSG_CONF_NAME = argv[i+1];
+ else if (strcmp(argv[i],"--grf_path_file") == 0 || strcmp(argv[i],"--grf-path-file") == 0)
+ GRF_PATH_FILENAME = argv[i+1];
+#ifndef TXT_ONLY
+ else if (strcmp(argv[i],"--inter_config") == 0 || strcmp(argv[i],"--inter-config") == 0)
+ INTER_CONF_NAME = argv[i+1];
+#endif
+ else if (strcmp(argv[i],"--log_config") == 0 || strcmp(argv[i],"--log-config") == 0)
+ LOG_CONF_NAME = argv[i+1];
+ else if (strcmp(argv[i],"--run_once") == 0) // close the map-server as soon as its done.. for testing [Celest]
+ runflag = 0;
+ }
+
+ map_config_read(MAP_CONF_NAME);
+
+ if ((naddr_ == 0) && (map_ip_set_ == 0 || char_ip_set_ == 0)) {
+ ShowError("\nUnable to determine your IP address... please edit the map_athena.conf file and set it.\n");
+ ShowError("(127.0.0.1 is valid if you have no network interface)\n");
+ }
+
+ if (map_ip_set_ == 0 || char_ip_set_ == 0) {
+ // The map server should know what IP address it is running on
+ // - MouseJstr
+ int localaddr = ntohl(addr_[0]);
+ unsigned char *ptr = (unsigned char *) &localaddr;
+ char buf[16];
+ sprintf(buf, "%d.%d.%d.%d", ptr[0], ptr[1], ptr[2], ptr[3]);;
+ if (naddr_ != 1)
+ ShowNotice("Multiple interfaces detected.. using %s as our IP address\n", buf);
+ else
+ ShowInfo("Defaulting to %s as our IP address\n", buf);
+ if (map_ip_set_ == 0)
+ clif_setip(buf);
+ if (char_ip_set_ == 0)
+ chrif_setip(buf);
+ if (ptr[0] == 192 && ptr[1] == 168)
+ ShowError("\nFirewall detected.. \n edit lan_support.conf and map_athena.conf\n\n");
+ }
+
+ if (SHOW_DEBUG_MSG)
+ ShowNotice("Server running in '"CL_WHITE"Debug Mode"CL_RESET"'.\n");
+
+
+ battle_config_read(BATTLE_CONF_FILENAME);
+ msg_config_read(MSG_CONF_NAME);
+ atcommand_config_read(ATCOMMAND_CONF_FILENAME);
+ charcommand_config_read(CHARCOMMAND_CONF_FILENAME);
+ script_config_read(SCRIPT_CONF_NAME);
+ inter_config_read(INTER_CONF_NAME);
+ log_config_read(LOG_CONF_NAME);
+
+ id_db = db_alloc(__FILE__,__LINE__,DB_INT,DB_OPT_BASE,sizeof(int));
+ pc_db = db_alloc(__FILE__,__LINE__,DB_INT,DB_OPT_BASE,sizeof(int)); //Added for reliable map_id2sd() use. [Skotlex]
+ map_db = db_alloc(__FILE__,__LINE__,DB_UINT,DB_OPT_BASE,sizeof(int));
+ charid_db = db_alloc(__FILE__,__LINE__,DB_INT,DB_OPT_RELEASE_DATA,sizeof(int));
+#ifndef TXT_ONLY
+ map_sql_init();
+ if(charsave_method)
+ charsql_db_init(1); //Connecting to chardb
+#endif /* not TXT_ONLY */
+
+ mapindex_init();
+ grfio_init(GRF_PATH_FILENAME);
+
+ map_readallmaps();
+
+ add_timer_func_list(map_freeblock_timer, "map_freeblock_timer");
+ add_timer_func_list(map_clearflooritem_timer, "map_clearflooritem_timer");
+ add_timer_func_list(map_removemobs_timer, "map_removemobs_timer");
+ add_timer_interval(gettick()+1000, map_freeblock_timer, 0, 0, 60*1000);
+
+ do_init_atcommand();
+ do_init_chrif();
+ do_init_clif();
+ do_init_script();
+ do_init_itemdb();
+ do_init_mob(); // npcの初期化・でmob_spawnして、mob_dbを?照するのでinit_npcより先
+ do_init_pc();
+ do_init_status();
+ do_init_party();
+ do_init_guild();
+ do_init_storage();
+ do_init_skill();
+ do_init_pet();
+ do_init_npc();
+
+#ifndef TXT_ONLY /* mail system [Valaris] */
+ if(mail_server_enable)
+ do_init_mail();
+
+ if (log_config.sql_logs)
+ {
+ log_sql_init();
+ }
+#endif /* not TXT_ONLY */
+
+ npc_event_do_oninit(); // npcのOnInitイベント?行
+
+ if ( console ) {
+ set_defaultconsoleparse(parse_console);
+ start_console();
+ }
+
+ if (battle_config.pk_mode == 1)
+ ShowNotice("Server is running on '"CL_WHITE"PK Mode"CL_RESET"'.\n");
+
+ ShowStatus("Server is '"CL_GREEN"ready"CL_RESET"' and listening on port '"CL_WHITE"%d"CL_RESET"'.\n\n", map_port);
+
+ return 0;
+}
+
+int compare_item(struct item *a, struct item *b) {
+
+ if (a->nameid == b->nameid &&
+ a->identify == b->identify &&
+ a->refine == b->refine &&
+ a->attribute == b->attribute)
+ {
+ int i;
+ for (i = 0; i < MAX_SLOTS && (a->card[i] == b->card[i]); i++);
+ return (i == MAX_SLOTS);
+ }
+ return 0;
+}
+
+#ifndef TXT_ONLY
+int charsql_db_init(int method){
+
+ if(method == 1){ //'INIT / START'
+ ShowInfo("Connecting to 'character' Database... ");
+ mysql_init(&charsql_handle);
+
+ if(!mysql_real_connect(&charsql_handle, charsql_host, charsql_user, charsql_pass, charsql_db, charsql_port, (char *)NULL, 0)){
+ ShowSQL("DB error - %s\n",mysql_error(&charsql_handle));
+ exit(1);
+ }else{
+ printf("success.\n");
+ if( strlen(default_codepage) > 0 ) {
+ sprintf( tmp_sql, "SET NAMES %s", default_codepage );
+ if (mysql_query(&charsql_handle, tmp_sql)) {
+ ShowSQL("DB error - %s\n",mysql_error(&charsql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ }
+ }
+ }else if(method == 0){ //'FINAL' / Shutdown
+ ShowInfo("Closing 'character' Database connection ... ");
+ mysql_close(&charsql_handle);
+ printf("done.\n");
+ }
+ return 0;
+}
+#endif
diff --git a/src/map/map.h b/src/map/map.h
new file mode 100644
index 000000000..c56e4c886
--- /dev/null
+++ b/src/map/map.h
@@ -0,0 +1,1409 @@
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#ifndef _MAP_H_
+#define _MAP_H_
+
+#include <stdarg.h>
+#include "../common/mmo.h"
+#include "../common/mapindex.h"
+#include "../common/db.h"
+
+//Uncomment to enable the Cell Stack Limit mod. (EXPERIMENTAL)
+//It's only config is the battle_config cell_stack_limit.
+//Only chars affected are those defined in BL_CHAR (mobs and players currently)
+//#define CELL_NOSTACK
+
+#define MAX_PC_CLASS 4050
+#define PC_CLASS_BASE 0
+#define PC_CLASS_BASE2 (PC_CLASS_BASE + 4001)
+#define PC_CLASS_BASE3 (PC_CLASS_BASE2 + 22)
+#define MAX_NPC_PER_MAP 512
+#define BLOCK_SIZE 8
+#define AREA_SIZE battle_config.area_size
+#define LIFETIME_FLOORITEM 60
+#define DAMAGELOG_SIZE 30
+#define LOOTITEM_SIZE 10
+#define MAX_STATUSCHANGE 250
+//Quick defines to know which are the min-max common ailments. [Skotlex]
+//Because of the way the headers are included.. these must be replaced for actual values.
+//Remember to update as needed! Min is SC_STONE and max is SC_DPOISON currently.
+#define SC_COMMON_MIN 0
+#define SC_COMMON_MAX 10
+
+#define MAX_SKILL_LEVEL 100
+#define MAX_SKILLUNITGROUP 32
+#define MAX_MOBSKILLUNITGROUP 8
+#define MAX_SKILLUNITGROUPTICKSET 32
+#define MAX_SKILLTIMERSKILL 32
+#define MAX_MOBSKILLTIMERSKILL 10
+#define MAX_MOBSKILL 32
+#define MAX_MOB_LIST_PER_MAP 128
+#define MAX_EVENTQUEUE 2
+#define MAX_EVENTTIMER 32
+#define NATURAL_HEAL_INTERVAL 500
+#define MAX_FLOORITEM 500000
+#define MAX_LEVEL 255
+#define MAX_WALKPATH 32
+#define MAX_DROP_PER_MAP 48
+#define MAX_IGNORE_LIST 80
+#define MAX_VENDING 12
+#define MOBID_EMPERIUM 1288
+
+#define MAX_PC_BONUS 10
+#define MAX_DUEL 1024
+
+//These mark the ID of the jobs, as expected by the client. [Skotlex]
+enum {
+ JOB_NOVICE,
+ JOB_SWORDMAN,
+ JOB_MAGE,
+ JOB_ARCHER,
+ JOB_ACOLYTE,
+ JOB_MERCHANT,
+ JOB_THIEF,
+ JOB_KNIGHT,
+ JOB_PRIEST,
+ JOB_WIZARD,
+ JOB_BLACKSMITH,
+ JOB_HUNTER,
+ JOB_ASSASSIN,
+ JOB_KNIGHT2,
+ JOB_CRUSADER,
+ JOB_MONK,
+ JOB_SAGE,
+ JOB_ROGUE,
+ JOB_ALCHEMIST,
+ JOB_BARD,
+ JOB_DANCER,
+ JOB_CRUSADER2,
+ JOB_WEDDING,
+ JOB_SUPER_NOVICE,
+ JOB_GUNSLINGER,
+ JOB_NINJA,
+ JOB_XMAS,
+
+ JOB_NOVICE_HIGH = 4001,
+ JOB_SWORDMAN_HIGH,
+ JOB_MAGE_HIGH,
+ JOB_ARCHER_HIGH,
+ JOB_ACOLYTE_HIGH,
+ JOB_MERCHANT_HIGH,
+ JOB_THIEF_HIGH,
+ JOB_LORD_KNIGHT,
+ JOB_HIGH_PRIEST,
+ JOB_HIGH_WIZARD,
+ JOB_WHITESMITH,
+ JOB_SNIPER,
+ JOB_ASSASSIN_CROSS,
+ JOB_LORD_KNIGHT2,
+ JOB_PALADIN,
+ JOB_CHAMPION,
+ JOB_PROFESSOR,
+ JOB_STALKER,
+ JOB_CREATOR,
+ JOB_CLOWN,
+ JOB_GYPSY,
+ JOB_PALADIN2,
+
+ JOB_BABY,
+ JOB_BABY_SWORDMAN,
+ JOB_BABY_MAGE,
+ JOB_BABY_ARCHER,
+ JOB_BABY_ACOLYTE,
+ JOB_BABY_MERCHANT,
+ JOB_BABY_THIEF,
+ JOB_BABY_KNIGHT,
+ JOB_BABY_PRIEST,
+ JOB_BABY_WIZARD,
+ JOB_BABY_BLACKSMITH,
+ JOB_BABY_HUNTER,
+ JOB_BABY_ASSASSIN,
+ JOB_BABY_KNIGHT2,
+ JOB_BABY_CRUSADER,
+ JOB_BABY_MONK,
+ JOB_BABY_SAGE,
+ JOB_BABY_ROGUE,
+ JOB_BABY_ALCHEMIST,
+ JOB_BABY_BARD,
+ JOB_BABY_DANCER,
+ JOB_BABY_CRUSADER2,
+ JOB_SUPER_BABY,
+
+ JOB_TAEKWON,
+ JOB_STAR_GLADIATOR,
+ JOB_STAR_GLADIATOR2,
+ JOB_SOUL_LINKER,
+};
+
+//The following system marks a different job ID system used by the map server,
+//which makes a lot more sense than the normal one. [Skotlex]
+//
+//These marks the "level" of the job.
+#define JOBL_2_1 0x100 //256
+#define JOBL_2_2 0x200 //512
+#define JOBL_2 0x300
+
+#define JOBL_UPPER 0x1000 //4096
+#define JOBL_BABY 0x2000 //8192
+
+//for filtering and quick checking.
+#define MAPID_UPPERMASK 0x0fff
+#define MAPID_BASEMASK 0x00ff
+//First Jobs
+//Note the oddity of the novice:
+//Super Novices are considered the 2-1 version of the novice! Novices are considered a first class type, too...
+enum {
+ MAPID_NOVICE = 0x0,
+ MAPID_SWORDMAN,
+ MAPID_MAGE,
+ MAPID_ARCHER,
+ MAPID_ACOLYTE,
+ MAPID_MERCHANT,
+ MAPID_THIEF,
+ MAPID_TAEKWON,
+ MAPID_WEDDING,
+ MAPID_XMAS, // [Valaris]
+//2_1 classes
+ MAPID_SUPER_NOVICE = JOBL_2_1|0x0,
+ MAPID_KNIGHT,
+ MAPID_WIZARD,
+ MAPID_HUNTER,
+ MAPID_PRIEST,
+ MAPID_BLACKSMITH,
+ MAPID_ASSASSIN,
+ MAPID_STAR_GLADIATOR,
+//2_2 classes
+ MAPID_CRUSADER = JOBL_2_2|0x1,
+ MAPID_SAGE,
+ MAPID_BARDDANCER,
+ MAPID_MONK,
+ MAPID_ALCHEMIST,
+ MAPID_ROGUE,
+ MAPID_SOUL_LINKER,
+//1-1, advanced
+ MAPID_NOVICE_HIGH = JOBL_UPPER|0x0,
+ MAPID_SWORDMAN_HIGH,
+ MAPID_MAGE_HIGH,
+ MAPID_ARCHER_HIGH,
+ MAPID_ACOLYTE_HIGH,
+ MAPID_MERCHANT_HIGH,
+ MAPID_THIEF_HIGH,
+//2_1 advanced
+ MAPID_LORD_KNIGHT = JOBL_UPPER|JOBL_2_1|0x1,
+ MAPID_HIGH_WIZARD,
+ MAPID_SNIPER,
+ MAPID_HIGH_PRIEST,
+ MAPID_WHITESMITH,
+ MAPID_ASSASSIN_CROSS,
+//2_2 advanced
+ MAPID_PALADIN = JOBL_UPPER|JOBL_2_2|0x1,
+ MAPID_PROFESSOR,
+ MAPID_CLOWNGYPSY,
+ MAPID_CHAMPION,
+ MAPID_CREATOR,
+ MAPID_STALKER,
+//1-1 baby
+ MAPID_BABY = JOBL_BABY|0x0,
+ MAPID_BABY_SWORDMAN,
+ MAPID_BABY_MAGE,
+ MAPID_BABY_ARCHER,
+ MAPID_BABY_ACOLYTE,
+ MAPID_BABY_MERCHANT,
+ MAPID_BABY_THIEF,
+ MAPID_BABY_TAEKWON,
+//2_1 baby
+ MAPID_SUPER_BABY = JOBL_BABY|JOBL_2_1|0x0,
+ MAPID_BABY_KNIGHT,
+ MAPID_BABY_WIZARD,
+ MAPID_BABY_HUNTER,
+ MAPID_BABY_PRIEST,
+ MAPID_BABY_BLACKSMITH,
+ MAPID_BABY_ASSASSIN,
+ MAPID_BABY_STAR_GLADIATOR,
+//2_2 baby
+ MAPID_BABY_CRUSADER = JOBL_BABY|JOBL_2_2|0x1,
+ MAPID_BABY_SAGE,
+ MAPID_BABY_BARDDANCER,
+ MAPID_BABY_MONK,
+ MAPID_BABY_ALCHEMIST,
+ MAPID_BABY_ROGUE,
+ MAPID_BABY_SOUL_LINKER,
+};
+
+//Don't change this, as the client seems to always send/receive 80 characters as it currently is. [Skotlex]
+#define MESSAGE_SIZE 80
+
+#define DEFAULT_AUTOSAVE_INTERVAL 60*1000
+
+#define OPTION_SIGHT 0x0001
+#define OPTION_HIDE 0x0002
+#define OPTION_CLOAK 0x0004
+
+#define OPTION_FALCON 0x0010
+#define OPTION_RIDING 0x0020
+#define OPTION_INVISIBLE 0x0040
+#define OPTION_ORCISH 0x0800
+
+#define OPTION_WEDDING 0x1000
+#define OPTION_RUWACH 0x2000
+#define OPTION_CHASEWALK 0x4000
+
+#define OPTION_FLYING 0x8000
+
+//TODO: Get these Missing options...
+#define OPTION_SIGHTTRASHER 0x0001
+
+//Specifies maps where players may hit each other
+#define map_flag_vs(m) (map[m].flag.pvp || map[m].flag.gvg_dungeon || map[m].flag.gvg || (agit_flag && map[m].flag.gvg_castle))
+//Specifies maps that have special GvG/WoE restrictions
+#define map_flag_gvg(m) (map[m].flag.gvg || (agit_flag && map[m].flag.gvg_castle))
+
+//This stackable implementation does not means a BL can be more than one type at a time, but it's
+//meant to make it easier to check for multiple types at a time on invocations such as
+// map_foreach* calls [Skotlex]
+enum {
+ BL_NUL = 0x000,
+ BL_PC = 0x001,
+ BL_MOB = 0x002,
+ BL_PET = 0x004,
+ BL_ITEM = 0x008,
+ BL_SKILL = 0x010,
+ BL_NPC = 0x020,
+ BL_CHAT = 0x040
+};
+
+//For common mapforeach calls. Since pets cannot be affected, they aren't included here yet.
+#define BL_CHAR (BL_PC|BL_MOB)
+#define BL_ALL 0xfff
+
+enum { WARP, SHOP, SCRIPT, MONS };
+
+struct block_list {
+ struct block_list *next,*prev;
+ int id;
+ short m,x,y;
+ unsigned char type;
+ unsigned char subtype;
+};
+
+struct walkpath_data {
+ unsigned char path_len,path_pos,path_half;
+ unsigned char path[MAX_WALKPATH];
+};
+struct shootpath_data {
+ int rx,ry,len;
+ int x[MAX_WALKPATH];
+ int y[MAX_WALKPATH];
+};
+
+struct script_reg {
+ int index;
+ int data;
+};
+struct script_regstr {
+ int index;
+ char data[256];
+};
+struct status_change {
+ int timer;
+ int val1,val2,val3,val4;
+};
+struct vending {
+ short index;
+ unsigned short amount;
+ unsigned int value;
+};
+
+struct weapon_data {
+ int atkmods[3];
+ // all the variables except atkmods get zero'ed in each call of status_calc_pc
+ // NOTE: if you want to add a non-zeroed variable, you need to update the memset call
+ // in status_calc_pc as well! All the following are automatically zero'ed. [Skotlex]
+ int watk;
+ int watk2;
+ int atk_ele;
+ int overrefine;
+ int star;
+ int ignore_def_ele;
+ int ignore_def_race;
+ int def_ratio_atk_ele;
+ int def_ratio_atk_race;
+ int addele[10];
+ int addrace[12];
+ int addrace2[12];
+ int addsize[3];
+
+ short ignore_def_mob;
+ short hp_drain_rate;
+ short hp_drain_per;
+ short hp_drain_value;
+ short sp_drain_rate;
+ short sp_drain_per;
+ short sp_drain_value;
+ short add_damage_classid[MAX_PC_BONUS];
+ int add_damage_classrate[MAX_PC_BONUS];
+ int add_damage_class_count;
+};
+
+struct skill_unit_group;
+struct skill_unit {
+ struct block_list bl;
+
+ struct skill_unit_group *group;
+
+ int limit;
+ int val1,val2;
+ short alive,range;
+};
+struct skill_unit_group {
+ int src_id;
+ int party_id;
+ int guild_id;
+ int map;
+ int target_flag; //Holds BCT_* flag for battle_check_target
+ int bl_flag; //Holds BL_* flag for map_foreachin* functions
+ unsigned int tick;
+ int limit,interval;
+
+ int skill_id,skill_lv;
+ int val1,val2,val3;
+ char *valstr;
+ int unit_id;
+ int group_id;
+ int unit_count,alive_count;
+ struct skill_unit *unit;
+};
+struct skill_unit_group_tickset {
+ unsigned int tick;
+ int id;
+};
+struct skill_timerskill {
+ int timer;
+ int src_id;
+ int target_id;
+ int map;
+ short x,y;
+ short skill_id,skill_lv;
+ int type;
+ int flag;
+};
+
+struct npc_data;
+struct pet_db;
+struct item_data;
+struct square;
+
+struct map_session_data {
+ struct block_list bl;
+ //NOTE: When deciding to add a flag to state or special_state, take into consideration that state is preserved in
+ //status_calc_pc, while special_state is recalculated in each call. [Skotlex]
+ struct {
+ unsigned auth : 1;
+ unsigned change_walk_target : 1;
+ unsigned attack_continue : 1;
+ unsigned menu_or_input : 1;
+ unsigned dead_sit : 2;
+ unsigned skillcastcancel : 1;
+ unsigned waitingdisconnect : 1;
+ unsigned lr_flag : 2;
+ unsigned connect_new : 1;
+ unsigned arrow_atk : 1;
+ unsigned attack_type : 3;
+ unsigned skill_flag : 1;
+ unsigned gangsterparadise : 1;
+ unsigned rest : 1;
+ unsigned produce_flag : 1;
+ unsigned storage_flag : 2; //0: closed, 1: Normal Storage open, 2: guild storage open [Skotlex]
+ unsigned snovice_flag : 4;
+ // originally by Qamera, adapted by celest
+ unsigned event_death : 1;
+ unsigned event_kill : 1;
+ unsigned event_disconnect : 1;
+ // Abracadabra bugfix by Aru
+ unsigned abra_flag : 1;
+ unsigned autotrade : 1; //By Fantik
+ unsigned perfect_hiding : 1; // [Valaris]
+ unsigned reg_dirty : 3; //By Skotlex (marks whether registry variables have been saved or not yet)
+ unsigned showdelay :1;
+ unsigned showexp :1;
+ unsigned showzeny :1;
+ unsigned mainchat :1; //[LuzZza]
+ unsigned disguised :1; //[Valaris]
+ unsigned deal_locked :2;
+ unsigned party_sent :1;
+ unsigned guild_sent :1;
+ unsigned monster_ignore :1; // for monsters to ignore a character [Valaris] [zzo]
+ unsigned size :2; // for tiny/large types
+ unsigned night :1; //Holds whether or not the player currently has the SI_NIGHT effect on. [Skotlex]
+ unsigned finalsave :1; //Signals whether the final save for the char was done or not yet. Meant to prevent exploits and the like. [Skotlex]
+ unsigned short autoloot;
+ struct guild *gmaster_flag;
+ } state;
+ struct {
+ unsigned killer : 1;
+ unsigned killable : 1;
+ unsigned restart_full_recover : 1;
+ unsigned no_castcancel : 1;
+ unsigned no_castcancel2 : 1;
+ unsigned no_sizefix : 1;
+ unsigned no_magic_damage : 1;
+ unsigned no_weapon_damage : 1;
+ unsigned no_gemstone : 1;
+ unsigned infinite_endure : 1;
+ unsigned intravision : 1; // Maya Purple Card effect allowing to see Hiding/Cloaking people [DracoRPG]
+ } special_state;
+ int char_id, login_id1, login_id2, sex;
+ unsigned short class_; //This is the internal job ID used by the map server to simplify comparisons/queries/etc. [Skotlex]
+
+ int packet_ver; // 5: old, 6: 7july04, 7: 13july04, 8: 26july04, 9: 9aug04/16aug04/17aug04, 10: 6sept04, 11: 21sept04, 12: 18oct04, 13: 25oct04 ... 18
+ struct mmo_charstatus status;
+ struct registry save_reg;
+
+ struct item_data *inventory_data[MAX_INVENTORY];
+ short equip_index[11];
+ unsigned int weight,max_weight;
+ int cart_weight,cart_max_weight,cart_num,cart_max_num;
+ int fd;
+ unsigned short mapindex;
+ short to_x,to_y;
+ short speed,prev_speed;
+ short opt1,opt2,opt3;
+ unsigned char dir,head_dir;
+ unsigned int client_tick,server_tick;
+ struct walkpath_data walkpath;
+ int walktimer;
+ int npc_id,areanpc_id,npc_shopid;
+ int npc_item_flag; //Marks the npc_id with which you can use items during interactions with said npc (see script command enable_itemuse)
+ int npc_pos;
+ int npc_menu;
+ int npc_amount;
+ struct script_stack *stack;
+ unsigned char *npc_script,*npc_scriptroot;
+ int npc_scriptstate;
+ char npc_str[256];
+ unsigned int chatID;
+ time_t idletime;
+
+ struct{
+ char name[NAME_LENGTH];
+ } ignore[MAX_IGNORE_LIST];
+ int ignoreAll;
+
+ int attacktimer;
+
+ int attacktarget;
+ short attacktarget_lv;
+ unsigned int attackabletime;
+
+ int followtimer; // [MouseJstr]
+ int followtarget;
+
+ time_t emotionlasttime; // to limit flood with emotion packets
+
+ int skilltimer;
+ int skilltarget;
+ short skillx,skilly;
+ short skillid,skilllv;
+ short skillitem,skillitemlv;
+ short skillid_old,skilllv_old;
+ short skillid_dance,skilllv_dance;
+ struct skill_unit_group skillunit[MAX_SKILLUNITGROUP];
+ struct skill_unit_group_tickset skillunittick[MAX_SKILLUNITGROUPTICKSET];
+ struct skill_timerskill skilltimerskill[MAX_SKILLTIMERSKILL];
+ char blockskill[MAX_SKILL]; // [celest]
+ //unsigned int skillstatictimer[MAX_SKILL];
+ unsigned short timerskill_count; // [celest]
+ int cloneskill_id;
+ struct map_session_data *repair_target;
+
+ int invincible_timer;
+ unsigned int canact_tick;
+ unsigned int canmove_tick;
+ unsigned int canlog_tick;
+ unsigned int canregen_tick;
+ unsigned int canuseitem_tick; // [Skotlex]
+ int hp_sub,sp_sub;
+ int inchealhptick,inchealsptick,inchealspirithptick,inchealspiritsptick;
+
+ short view_class;
+ short weapontype1,weapontype2;
+ short disguise; // [Valaris]
+
+ struct weapon_data right_weapon;
+ struct weapon_data left_weapon;
+
+ int paramc[6],paramcard[6];
+
+ // here start arrays to be globally zeroed at the beginning of status_calc_pc()
+
+ int paramb[6];
+ int parame[6];
+ int subele[10];
+ int subrace[12];
+ int subrace2[12];
+ int subsize[3];
+ int addeff[SC_COMMON_MAX-SC_COMMON_MIN+1];
+ int addeff2[SC_COMMON_MAX-SC_COMMON_MIN+1];
+ int reseff[SC_COMMON_MAX-SC_COMMON_MIN+1];
+ int weapon_coma_ele[10];
+ int weapon_coma_race[12];
+ int weapon_atk[16];
+ int weapon_atk_rate[16];
+ int arrow_addele[10];
+ int arrow_addrace[12];
+ int arrow_addsize[3];
+ int arrow_addeff[SC_COMMON_MAX-SC_COMMON_MIN+1];
+ int arrow_addeff2[SC_COMMON_MAX-SC_COMMON_MIN+1];
+ int magic_addele[10];
+ int magic_addrace[12];
+ int magic_addsize[3];
+ int critaddrace[12];
+ int expaddrace[12];
+ int itemhealrate[7];
+ int addeff3[SC_COMMON_MAX-SC_COMMON_MIN+1];
+ short addeff3_type[SC_COMMON_MAX-SC_COMMON_MIN+1];
+ short sp_gain_race[12];
+ short unequip_losehp[11];
+ short unequip_losesp[11];
+ // zeroed arrays end here.
+ // zeroed structures start here
+ struct {
+ short id, lv, rate;
+ } autospell[MAX_PC_BONUS], autospell2[MAX_PC_BONUS];
+ struct { //skillatk raises bonus dmg% of skills, skillblown increases bonus blewcount for some skills.
+ short id, val;
+ } skillatk[MAX_PC_BONUS], skillblown[MAX_PC_BONUS];
+ struct {
+ short class_, rate;
+ } add_def[MAX_PC_BONUS], add_mdef[MAX_PC_BONUS],
+ add_dmg[MAX_PC_BONUS], add_mdmg[MAX_PC_BONUS];
+ struct {
+ short id, group;
+ int race, rate;
+ } add_drop[MAX_PC_BONUS];
+ // zeroed structures end here
+ // zeroed vars start here.
+ int hit;
+ int flee, flee2;
+ int critical;
+ int aspd;
+ int def, def2;
+ int mdef, mdef2;
+ int def_ele;
+ int matk1, matk2;
+ int base_atk;
+ int arrow_atk,arrow_ele,arrow_cri,arrow_hit,arrow_range;
+ int nhealhp,nhealsp,nshealhp,nshealsp,nsshealhp,nsshealsp;
+ int critical_def,double_rate;
+ int long_attack_atk_rate; //Long range atk rate, not weapon based. [Skotlex]
+ int near_attack_def_rate,long_attack_def_rate,magic_def_rate,misc_def_rate;
+ int ignore_mdef_ele;
+ int ignore_mdef_race;
+ int perfect_hit;
+ int perfect_hit_add;
+ int get_zeny_rate;
+ int get_zeny_num; //Added Get Zeny Rate [Skotlex]
+ int double_add_rate;
+ int short_weapon_damage_return,long_weapon_damage_return;
+ int magic_damage_return; // AppleGirl Was Here
+ int random_attack_increase_add,random_attack_increase_per; // [Valaris]
+ int break_weapon_rate,break_armor_rate;
+ int crit_atk_rate;
+ int hp_loss_rate;
+ int sp_loss_rate;
+ int classchange; // [Valaris]
+ unsigned int setitem_hash, setitem_hash2; //Split in 2 because shift operations only work on int ranges. [Skotlex]
+
+ short attackrange,attackrange_;
+ short splash_range, splash_add_range;
+ short add_steal_rate;
+ short hp_loss_value;
+ short sp_loss_value;
+ short hp_loss_type;
+ short sp_drain_type;
+ short sp_gain_value, hp_gain_value;
+ short add_drop_count;
+ unsigned short unbreakable; // chance to prevent ANY equipment breaking [celest]
+ unsigned short unbreakable_equip; //100% break resistance on certain equipment
+ unsigned short unstripable_equip;
+ short no_regen;
+ short add_def_count,add_mdef_count;
+ short add_dmg_count,add_mdmg_count;
+
+ // zeroed vars end here.
+
+ int amotion,dmotion;
+ int castrate,delayrate,hprate,sprate,dsprate;
+ int atk_rate;
+ int aspd_rate,speed_rate,hprecov_rate,sprecov_rate;
+ int matk_rate;
+ int critical_rate,hit_rate,flee_rate,flee2_rate,def_rate,def2_rate,mdef_rate,mdef2_rate;
+ int speed_add_rate, aspd_add_rate;
+
+ int hp_loss_tick;
+ int sp_loss_tick;
+
+ int itemid;
+ short itemindex; //Used item's index in sd->inventory [Skotlex]
+
+ short catch_target_class; // pet catching, stores a pet class to catch (short now) [zzo]
+
+ short spiritball, spiritball_old;
+ int spirit_timer[MAX_SKILL_LEVEL];
+
+ int die_counter;
+ short doridori_counter;
+ char potion_success_counter;
+
+ int reg_num;
+ struct script_reg *reg;
+ int regstr_num;
+ struct script_regstr *regstr;
+
+ struct status_change sc_data[MAX_STATUSCHANGE];
+ short sc_count;
+ short mission_mobid; //Stores the target mob_id for TK_MISSION
+ short mission_count; //Stores the bounty kill count for TK_MISSION
+ int devotion[5]; //Stores the char IDs of chars devoted to.
+
+ int trade_partner;
+ struct {
+ struct {
+ int index, amount;
+ } item[10];
+ int zeny, weight;
+ } deal;
+
+ int party_invite,party_invite_account;
+ short party_x,party_y; // should be short [zzo]
+
+ int guild_invite,guild_invite_account;
+ int guild_emblem_id,guild_alliance,guild_alliance_account;
+ short guild_x,guild_y; // For guildmate position display. [Skotlex] should be short [zzo]
+ int guildspy; // [Syrus22]
+ int partyspy; // [Syrus22]
+
+ int vender_id;
+ int vend_num;
+ char message[MESSAGE_SIZE];
+ struct vending vending[MAX_VENDING];
+
+ struct s_pet pet;
+ struct pet_db *petDB;
+ struct pet_data *pd;
+ int pet_hungry_timer;
+
+ struct{
+ int m; //-1 - none, other: map index corresponding to map name.
+ unsigned short index; //map index
+ }feel_map[3];// 0 - Sun; 1 - Moon; 2 - Stars
+ int feel_level;
+ short hate_mob[3];
+
+ unsigned int pvp_timer;
+ short pvp_point;
+ unsigned short pvp_rank, pvp_lastusers;
+ unsigned short pvp_won, pvp_lost;
+
+ char eventqueue[MAX_EVENTQUEUE][50];
+ int eventtimer[MAX_EVENTTIMER];
+ unsigned short eventcount; // [celest]
+
+ unsigned char change_level; // [celest]
+
+ char fakename[NAME_LENGTH]; // fake names [Valaris]
+
+#ifndef TXT_ONLY
+ int mail_counter; // mail counter for mail system [Valaris]
+#endif
+
+ int duel_group; // duel vars [LuzZza]
+ int duel_invite;
+
+ char away_message[128]; // [LuzZza]
+
+};
+
+struct {
+ int members_count;
+ int invites_count;
+ int max_players_limit;
+} duel_list[MAX_DUEL];
+
+int duel_count;
+
+struct npc_timerevent_list {
+ int timer,pos;
+};
+struct npc_label_list {
+ char name[NAME_LENGTH];
+ int pos;
+};
+struct npc_item_list {
+ unsigned int nameid,value;
+};
+struct npc_data {
+ struct block_list bl;
+ short n;
+ short class_,dir;
+ short speed;
+ unsigned char name[NAME_LENGTH];
+ unsigned char exname[NAME_LENGTH];
+ int chat_id;
+ short opt1,opt2,opt3,option;
+ short flag;
+ int walktimer; // [Valaris]
+ short to_x,to_y; // [Valaris]
+ struct walkpath_data walkpath;
+ unsigned int next_walktime;
+ unsigned int canmove_tick;
+
+ struct { // [Valaris]
+ unsigned state : 8;
+ unsigned change_walk_target : 1;
+ unsigned walk_easy : 1;
+ } state;
+
+ char eventqueue[MAX_EVENTQUEUE][50];
+ int eventtimer[MAX_EVENTTIMER];
+ short arenaflag;
+
+ void *chatdb;
+
+ union {
+ struct {
+ unsigned char *script;
+ short xs,ys;
+ int guild_id;
+ int timer,timerid,timeramount,nexttimer,rid;
+ unsigned int timertick;
+ struct npc_timerevent_list *timer_event;
+ int label_list_num;
+ struct npc_label_list *label_list;
+ int src_id;
+ } scr;
+ struct npc_item_list shop_item[1];
+ struct {
+ short xs,ys;
+ short x,y;
+ unsigned short mapindex;
+ } warp;
+ } u;
+ //Do NOT place anything afterwards... shop data NPC will override any variables from here and on! [Skotlex]
+};
+
+//For quick linking to a guardian's info. [Skotlex]
+struct guardian_data {
+ int number; //0-MAX_GUARDIANS-1 = Guardians. MAX_GUARDIANS = Emperium.
+ int guild_id;
+ int emblem_id;
+ int guardup_lv; //Level of GD_GUARDUP skill.
+ char guild_name[NAME_LENGTH];
+ struct guild_castle* castle;
+};
+
+struct mob_data {
+ struct block_list bl;
+ struct mob_db *db; //For quick data access (saves doing mob_db(md->class_) all the time) [Skotlex]
+ char name[NAME_LENGTH];
+ struct {
+ unsigned size : 2; //Small/Big monsters.
+ unsigned cached : 1; //Cached mobs for dynamic mob unloading [Skotlex]
+ unsigned ai : 3; //Special ai for summoned monsters.
+ } special_state; //Special mob information that does not needs to be zero'ed on mob respawn.
+ struct {
+ unsigned state : 8;
+ unsigned skillstate : 8;
+ unsigned aggressive : 1; //Signals whether the mob AI is in aggressive mode or reactive mode. [Skotlex]
+ unsigned targettype : 1;
+ unsigned steal_flag : 1;
+ unsigned steal_coin_flag : 1;
+ unsigned skillcastcancel : 1;
+ unsigned change_walk_target : 1;
+ unsigned walk_easy : 1;
+ unsigned soul_change_flag : 1; // Celest
+ unsigned alchemist: 1;
+ int provoke_flag; // Celest
+ } state;
+ struct status_change sc_data[MAX_STATUSCHANGE];
+ struct walkpath_data walkpath;
+ struct guardian_data* guardian_data;
+ struct item *lootitem;
+ struct {
+ int id;
+ int dmg;
+ } dmglog[DAMAGELOG_SIZE];
+ unsigned long tdmg; //Stores total damage given to the mob, for exp calculations. [Skotlex]
+ short n;
+ short base_class,class_,dir,mode,level;
+ short m,x0,y0,xs,ys;
+ short to_x,to_y;
+ short target_dir;
+ short speed;
+ short attacked_count;
+ short target_lv;
+ int timer;
+ int hp, max_hp;
+ int target_id,attacked_id;
+ int spawndelay1,spawndelay2;
+ unsigned int attackabletime, canmove_tick, next_walktime;
+ unsigned int last_deadtime,last_spawntime,last_thinktime,last_linktime;
+ short move_fail_count;
+ short lootitem_count;
+ short sc_count;
+ short opt1,opt2,opt3,option;
+ short min_chase;
+
+ int deletetimer;
+ int skilltimer;
+ int skilltarget;
+ int def_ele;
+ int master_id,master_dist;
+
+ short skillx,skilly,skillid,skilllv,skillidx;
+ unsigned int skilldelay[MAX_MOBSKILL];
+ struct skill_timerskill skilltimerskill[MAX_MOBSKILLTIMERSKILL];
+ struct skill_unit_group skillunit[MAX_MOBSKILLUNITGROUP];
+ struct skill_unit_group_tickset skillunittick[MAX_SKILLUNITGROUPTICKSET];
+ char npc_event[50];
+};
+
+struct pet_data {
+ struct block_list bl;
+ short n;
+ short class_,dir;
+ struct mob_db *db;
+ short speed;
+ char name[NAME_LENGTH];
+ struct {
+ unsigned state : 8 ;
+ unsigned skillstate : 8 ;
+ unsigned change_walk_target : 1 ;
+ unsigned casting_flag :1 ;//Skotlex: Used to identify when we are casting.
+ short skillbonus;
+ } state;
+ int timer;
+ short to_x,to_y;
+ short equip;
+ struct walkpath_data walkpath;
+ int target_id;
+ short target_lv;
+ int move_fail_count;
+ unsigned int attackabletime,next_walktime,last_thinktime;
+ short rate_fix; //Support rate as modified by intimacy (1000 = 100%) [Skotlex]
+ struct pet_status { //Pet Status data
+ short level;
+ short atk1,atk2;
+ short str,agi,vit,int_,dex,luk;
+ } *status; //[Skotlex]
+
+ struct pet_recovery { //Stat recovery
+ unsigned short type; //Status Change id
+ unsigned short delay; //How long before curing (secs).
+ int timer;
+ } *recovery; //[Valaris] / Reimplemented by [Skotlex]
+
+ struct pet_bonus {
+ unsigned short type; //bStr, bVit?
+ unsigned short val; //Qty
+ unsigned short duration; //in secs
+ unsigned short delay; //Time before recasting (secs)
+ int timer;
+ } *bonus; //[Valaris] / Reimplemented by [Skotlex]
+
+ struct pet_skill_attack { //Attack Skill
+ unsigned short id;
+ unsigned short lv;
+ unsigned short div_; //0 = Normal skill. >0 = Fixed damage (lv), fixed div_.
+ unsigned short rate; //Base chance of skill ocurrance (10 = 10% of attacks)
+ unsigned short bonusrate; //How being 100% loyal affects cast rate (10 = At 1000 intimacy->rate+10%
+ } *a_skill; //[Skotlex]
+
+ struct pet_skill_support { //Support Skill
+ unsigned short id;
+ unsigned short lv;
+ unsigned short hp; //Max HP% for skill to trigger (50 -> 50% for Magnificat)
+ unsigned short sp; //Max SP% for skill to trigger (100 = no check)
+ unsigned short delay; //Time (secs) between being able to recast.
+ int timer;
+ } *s_skill; //[Skotlex]
+
+ struct pet_loot {
+ struct item *item;
+ unsigned short count;
+ unsigned short weight;
+ unsigned short max;
+ int timer;
+ } *loot; //[Valaris] / Rewritten by [Skotlex]
+
+ struct skill_timerskill skilltimerskill[MAX_MOBSKILLTIMERSKILL]; // [Valaris]
+ struct skill_unit_group skillunit[MAX_MOBSKILLUNITGROUP]; // [Valaris]
+ struct skill_unit_group_tickset skillunittick[MAX_SKILLUNITGROUPTICKSET]; // [Valaris]
+ struct map_session_data *msd;
+};
+
+enum { MS_IDLE,MS_WALK,MS_ATTACK,MS_DEAD,MS_DELAY };
+
+enum { NONE_ATTACKABLE,ATTACKABLE };
+
+enum { ATK_LUCKY=1,ATK_FLEE,ATK_DEF}; // 囲まれペナルティ計算用
+
+// For equipment breaking/stripping effects
+enum {
+ EQP_WEAPON = 1, // Both weapons
+ EQP_ARMOR = 2, // Armor
+ EQP_SHIELD = 4, // Shield
+ EQP_HELM = 8, // Top-head headgear
+};
+
+// Mob List Held in memory for Dynamic Mobs [Wizputer]
+struct mob_list {
+ int m,x,y,xs,ys,class_,num,delay1,delay2,level;
+ char mobname[NAME_LENGTH],eventname[NAME_LENGTH];
+};
+
+struct map_data {
+ char name[MAP_NAME_LENGTH];
+ unsigned short index; //Index is the map index used by the mapindex* functions.
+ unsigned char *gat; // NULLなら下のmap_data_other_serverとして扱う
+ unsigned char *cell; //Contains temporary cell data that is set/unset on tiles.
+#ifdef CELL_NOSTACK
+ unsigned char *cell_bl; //Holds amount of bls in any given cell.
+#endif
+ char *alias; // [MouseJstr]
+ struct block_list **block;
+ struct block_list **block_mob;
+ int *block_count,*block_mob_count;
+ int m;
+ short xs,ys;
+ short bxs,bys;
+ int npc_num;
+ int users;
+ struct {
+ unsigned alias : 1;
+ unsigned nomemo : 1;
+ unsigned noteleport : 1;
+ unsigned noreturn : 1;
+ unsigned monster_noteleport : 1;
+ unsigned nosave : 1;
+ unsigned nobranch : 1;
+ unsigned nopenalty : 1;
+ unsigned pvp : 1;
+ unsigned pvp_noparty : 1;
+ unsigned pvp_noguild : 1;
+ unsigned pvp_nightmaredrop :1;
+ unsigned pvp_nocalcrank : 1;
+ unsigned gvg_castle : 1;
+ unsigned gvg : 1; // Now it identifies gvg versus maps that are active 24/7
+ unsigned gvg_dungeon : 1; // Celest
+ unsigned gvg_noparty : 1;
+ unsigned nozenypenalty : 1;
+ unsigned notrade : 1;
+ unsigned noskill : 1;
+ unsigned nowarp : 1;
+ unsigned nowarpto : 1;
+ unsigned nopvp : 1; // [Valaris]
+ unsigned noicewall : 1; // [Valaris]
+ unsigned snow : 1; // [Valaris]
+ unsigned clouds : 1;
+ unsigned clouds2 : 1; // [Valaris]
+ unsigned fog : 1; // [Valaris]
+ unsigned fireworks : 1;
+ unsigned sakura : 1; // [Valaris]
+ unsigned leaves : 1; // [Valaris]
+ unsigned rain : 1; // [Valaris]
+ unsigned indoors : 1; // celest
+ unsigned nogo : 1; // [Valaris]
+ unsigned nobaseexp : 1; // [Lorky] added by Lupus
+ unsigned nojobexp : 1; // [Lorky]
+ unsigned nomobloot : 1; // [Lorky]
+ unsigned nomvploot : 1; // [Lorky]
+ unsigned nightenabled :1; //For night display. [Skotlex]
+ } flag;
+ struct point save;
+ struct npc_data *npc[MAX_NPC_PER_MAP];
+ struct {
+ int drop_id;
+ int drop_type;
+ int drop_per;
+ } drop_list[MAX_DROP_PER_MAP];
+ struct mob_list *moblist[MAX_MOB_LIST_PER_MAP]; // [Wizputer]
+ int mob_delete_timer; // [Skotlex]
+};
+
+struct map_data_other_server {
+ char name[MAP_NAME_LENGTH];
+ unsigned short index; //Index is the map index used by the mapindex* functions.
+ unsigned char *gat; // NULL固定にして判断
+ unsigned long ip;
+ unsigned int port;
+};
+
+struct flooritem_data {
+ struct block_list bl;
+ unsigned char subx,suby;
+ int cleartimer;
+ int first_get_id,second_get_id,third_get_id;
+ unsigned int first_get_tick,second_get_tick,third_get_tick;
+ struct item item_data;
+};
+
+enum {
+ SP_SPEED,SP_BASEEXP,SP_JOBEXP,SP_KARMA,SP_MANNER,SP_HP,SP_MAXHP,SP_SP, // 0-7
+ SP_MAXSP,SP_STATUSPOINT,SP_0a,SP_BASELEVEL,SP_SKILLPOINT,SP_STR,SP_AGI,SP_VIT, // 8-15
+ SP_INT,SP_DEX,SP_LUK,SP_CLASS,SP_ZENY,SP_SEX,SP_NEXTBASEEXP,SP_NEXTJOBEXP, // 16-23
+ SP_WEIGHT,SP_MAXWEIGHT,SP_1a,SP_1b,SP_1c,SP_1d,SP_1e,SP_1f, // 24-31
+ SP_USTR,SP_UAGI,SP_UVIT,SP_UINT,SP_UDEX,SP_ULUK,SP_26,SP_27, // 32-39
+ SP_28,SP_ATK1,SP_ATK2,SP_MATK1,SP_MATK2,SP_DEF1,SP_DEF2,SP_MDEF1, // 40-47
+ SP_MDEF2,SP_HIT,SP_FLEE1,SP_FLEE2,SP_CRITICAL,SP_ASPD,SP_36,SP_JOBLEVEL, // 48-55
+ SP_UPPER,SP_PARTNER,SP_CART,SP_FAME,SP_UNBREAKABLE, //56-60
+ SP_CARTINFO=99, // 99
+
+ SP_BASEJOB=119, // 100+19 - celest
+ SP_BASECLASS=120, //Hmm.. why 100+19? I just use the next one... [Skotlex]
+
+ // original 1000-
+ SP_ATTACKRANGE=1000, SP_ATKELE,SP_DEFELE, // 1000-1002
+ SP_CASTRATE, SP_MAXHPRATE, SP_MAXSPRATE, SP_SPRATE, // 1003-1006
+ SP_ADDELE, SP_ADDRACE, SP_ADDSIZE, SP_SUBELE, SP_SUBRACE, // 1007-1011
+ SP_ADDEFF, SP_RESEFF, // 1012-1013
+ SP_BASE_ATK,SP_ASPD_RATE,SP_HP_RECOV_RATE,SP_SP_RECOV_RATE,SP_SPEED_RATE, // 1014-1018
+ SP_CRITICAL_DEF,SP_NEAR_ATK_DEF,SP_LONG_ATK_DEF, // 1019-1021
+ SP_DOUBLE_RATE, SP_DOUBLE_ADD_RATE, SP_MATK, SP_MATK_RATE, // 1022-1025
+ SP_IGNORE_DEF_ELE,SP_IGNORE_DEF_RACE, // 1026-1027
+ SP_ATK_RATE,SP_SPEED_ADDRATE,SP_ASPD_ADDRATE, // 1028-1030
+ SP_MAGIC_ATK_DEF,SP_MISC_ATK_DEF, // 1031-1032
+ SP_IGNORE_MDEF_ELE,SP_IGNORE_MDEF_RACE, // 1033-1034
+ SP_MAGIC_ADDELE,SP_MAGIC_ADDRACE,SP_MAGIC_ADDSIZE, // 1035-1037
+ SP_PERFECT_HIT_RATE,SP_PERFECT_HIT_ADD_RATE,SP_CRITICAL_RATE,SP_GET_ZENY_NUM,SP_ADD_GET_ZENY_NUM, // 1038-1042
+ SP_ADD_DAMAGE_CLASS,SP_ADD_MAGIC_DAMAGE_CLASS,SP_ADD_DEF_CLASS,SP_ADD_MDEF_CLASS, // 1043-1046
+ SP_ADD_MONSTER_DROP_ITEM,SP_DEF_RATIO_ATK_ELE,SP_DEF_RATIO_ATK_RACE,SP_ADD_SPEED, // 1047-1050
+ SP_HIT_RATE,SP_FLEE_RATE,SP_FLEE2_RATE,SP_DEF_RATE,SP_DEF2_RATE,SP_MDEF_RATE,SP_MDEF2_RATE, // 1051-1057
+ SP_SPLASH_RANGE,SP_SPLASH_ADD_RANGE,SP_AUTOSPELL,SP_HP_DRAIN_RATE,SP_SP_DRAIN_RATE, // 1058-1062
+ SP_SHORT_WEAPON_DAMAGE_RETURN,SP_LONG_WEAPON_DAMAGE_RETURN,SP_WEAPON_COMA_ELE,SP_WEAPON_COMA_RACE, // 1063-1066
+ SP_ADDEFF2,SP_BREAK_WEAPON_RATE,SP_BREAK_ARMOR_RATE,SP_ADD_STEAL_RATE, // 1067-1070
+ SP_MAGIC_DAMAGE_RETURN,SP_RANDOM_ATTACK_INCREASE,SP_ALL_STATS,SP_AGI_VIT,SP_AGI_DEX_STR,SP_PERFECT_HIDE, // 1071-1076
+ SP_DISGUISE,SP_CLASSCHANGE, // 1077-1078
+ SP_HP_DRAIN_VALUE,SP_SP_DRAIN_VALUE, // 1079-1080
+ SP_WEAPON_ATK,SP_WEAPON_ATK_RATE, // 1081-1082
+ SP_DELAYRATE, // 1083
+
+ SP_RESTART_FULL_RECOVER=2000,SP_NO_CASTCANCEL,SP_NO_SIZEFIX,SP_NO_MAGIC_DAMAGE,SP_NO_WEAPON_DAMAGE,SP_NO_GEMSTONE, // 2000-2005
+ SP_NO_CASTCANCEL2,SP_INFINITE_ENDURE,SP_UNBREAKABLE_WEAPON,SP_UNBREAKABLE_ARMOR, SP_UNBREAKABLE_HELM, // 2006-2010
+ SP_UNBREAKABLE_SHIELD, SP_LONG_ATK_RATE, // 2011-2012
+
+ SP_CRIT_ATK_RATE, SP_CRITICAL_ADDRACE, SP_NO_REGEN, SP_ADDEFF_WHENHIT, SP_AUTOSPELL_WHENHIT, // 2013-2017
+ SP_SKILL_ATK, SP_UNSTRIPABLE, SP_ADD_DAMAGE_BY_CLASS, // 2018-2020
+ SP_SP_GAIN_VALUE, SP_IGNORE_DEF_MOB, SP_HP_LOSS_RATE, SP_ADDRACE2, SP_HP_GAIN_VALUE, // 2021-2025
+ SP_SUBSIZE, SP_DAMAGE_WHEN_UNEQUIP, SP_ADD_ITEM_HEAL_RATE, SP_LOSESP_WHEN_UNEQUIP, SP_EXP_ADDRACE, // 2026-2030
+ SP_SP_GAIN_RACE, SP_SUBRACE2, SP_ADDEFF_WHENHIT_SHORT, // 2031-2033
+ SP_UNSTRIPABLE_WEAPON,SP_UNSTRIPABLE_ARMOR,SP_UNSTRIPABLE_HELM,SP_UNSTRIPABLE_SHIELD, // 2034-2037
+ SP_INTRAVISION, SP_ADD_MONSTER_DROP_ITEMGROUP, SP_SP_LOSS_RATE, // 2038-2040
+ SP_ADD_SKILL_BLOW //2041
+};
+
+enum {
+ LOOK_BASE,LOOK_HAIR,LOOK_WEAPON,LOOK_HEAD_BOTTOM,LOOK_HEAD_TOP,LOOK_HEAD_MID,LOOK_HAIR_COLOR,LOOK_CLOTHES_COLOR,LOOK_SHIELD,LOOK_SHOES
+};
+
+// CELLs for non-permanent cell-based effects (Pneuma, Basilica, Npcs, etc)
+#define CELL_NPC 0x1
+#define CELL_REGEN 0x2
+#define CELL_PNEUMA 0x4
+#define CELL_SAFETYWALL 0x8
+#define CELL_LANDPROTECTOR 0x10
+#define CELL_BASILICA 0x20
+#define CELL_MOONLIT 0x40
+#define CELL_ICEWALL 0x80
+/*
+ * map_getcell()で使用されるフラグ
+ */
+typedef enum {
+ CELL_CHKWALL=0, // 壁(セルタイプ1)
+ CELL_CHKWATER, // 水場(セルタイプ3)
+ CELL_CHKGROUND, // 地面障害物(セルタイプ5)
+ CELL_CHKPASS, // 通過可能(セルタイプ1,5以外)
+ CELL_CHKNOPASS, // 通過不可(セルタイプ1,5)
+ CELL_GETTYPE, // セルタイプを返す
+ CELL_GETCELLTYPE,
+ CELL_CHKNPC=0x10, // タッチタイプのNPC(セルタイプ0x80フラグ)
+ CELL_CHKREGEN, // cells that improve regeneration
+ CELL_CHKPNEUMA,
+ CELL_CHKSAFETYWALL,
+ CELL_CHKBASILICA, // バジリカ(セルタイプ0x40フラグ)
+ CELL_CHKLANDPROTECTOR,
+ CELL_CHKMOONLIT,
+ CELL_CHKICEWALL,
+} cell_t;
+// map_setcell()で使用されるフラグ
+enum {
+ CELL_SETNPC=0x10, // タッチタイプのNPCをセット
+ CELL_CLRNPC,
+ CELL_SETBASILICA, // バジリカをセット
+ CELL_CLRBASILICA, // バジリカをクリア
+ CELL_SETREGEN, // set regen cell
+ CELL_SETLANDPROTECTOR, //Set/Clear Magnetic Earth
+ CELL_CLRLANDPROTECTOR,
+ CELL_SETPNEUMA,
+ CELL_CLRPNEUMA,
+ CELL_SETSAFETYWALL,
+ CELL_CLRSAFETYWALL,
+ CELL_SETMOONLIT,
+ CELL_CLRMOONLIT,
+ CELL_SETICEWALL,
+ CELL_CLRICEWALL,
+};
+
+struct chat_data {
+ struct block_list bl;
+
+ unsigned char pass[8]; /* password */
+ unsigned char title[61]; /* room title MAX 60 */
+ unsigned char limit; /* join limit */
+ unsigned char trigger;
+ unsigned char users; /* current users */
+ unsigned char pub; /* room attribute */
+ struct map_session_data *usersd[20];
+ struct block_list *owner_;
+ struct block_list **owner;
+ char npc_event[50];
+};
+
+extern struct map_data map[];
+extern int map_num;
+extern int autosave_interval;
+extern int agit_flag;
+extern int night_flag; // 0=day, 1=night [Yor]
+extern int kick_on_disconnect; //To allow inter-server reconnections without kicking players out [Skotlex]
+extern int enable_spy; //Determines if @spy commands are active.
+extern char db_path[256];
+
+// gat?ヨァ
+int map_getcell(int,int,int,cell_t);
+int map_getcellp(struct map_data*,int,int,cell_t);
+void map_setcell(int,int,int,int);
+extern int map_read_flag; // 0: grfォユォ。ォ、ォ・1: ォュォ罩テォキォ・2: ォュォ罩テォキォ・?)
+enum {
+ READ_FROM_GAT, READ_FROM_AFM,
+ READ_FROM_BITMAP, CREATE_BITMAP,
+ READ_FROM_BITMAP_COMPRESSED, CREATE_BITMAP_COMPRESSED
+};
+
+extern char motd_txt[];
+extern char help_txt[];
+extern char help2_txt[];
+extern char charhelp_txt[];
+
+extern char talkie_mes[];
+
+extern char wisp_server_name[];
+
+// 鯖全体情報
+void map_setusers(int);
+int map_getusers(void);
+// block削除関連
+int map_freeblock(struct block_list *bl);
+int map_freeblock_lock(void);
+int map_freeblock_unlock(void);
+// block関連
+int map_addblock_sub(struct block_list *, int);
+int map_delblock_sub(struct block_list *, int);
+#define map_addblock(bl) map_addblock_sub(bl,1)
+#define map_delblock(bl) map_delblock_sub(bl,1)
+int map_moveblock(struct block_list *, int, int, unsigned int);
+int map_foreachinarea(int (*)(struct block_list*,va_list),int,int,int,int,int,int,...);
+// -- moonsoul (added map_foreachincell)
+int map_foreachincell(int (*)(struct block_list*,va_list),int,int,int,int,...);
+int map_foreachinmovearea(int (*)(struct block_list*,va_list),int,int,int,int,int,int,int,int,...);
+int map_foreachinpath(int (*func)(struct block_list*,va_list),int m,int x0,int y0,int x1,int y1,int range,int type,...); // Celest
+int map_foreachinmap(int (*)(struct block_list*,va_list),int,int,...);
+int map_countnearpc(int,int,int);
+//block関連に追加
+int map_count_oncell(int m,int x,int y,int type);
+struct skill_unit *map_find_skill_unit_oncell(struct block_list *,int x,int y,int skill_id,struct skill_unit *);
+// 一時的object関連
+int map_addobject(struct block_list *);
+int map_delobject(int);
+int map_delobjectnofree(int id);
+void map_foreachobject(int (*)(struct block_list*,va_list),int,...);
+//
+int map_quit(struct map_session_data *);
+// npc
+int map_addnpc(int,struct npc_data *);
+
+// 床アイテム関連
+int map_clearflooritem_timer(int,unsigned int,int,int);
+int map_removemobs_timer(int,unsigned int,int,int);
+#define map_clearflooritem(id) map_clearflooritem_timer(0,0,id,1)
+int map_addflooritem(struct item *,int,int,int,int,struct map_session_data *,struct map_session_data *,struct map_session_data *,int);
+int map_searchrandfreecell(int,int,int,int);
+
+// キャラid=>キャラ名 変換関連
+void map_addchariddb(int charid,char *name);
+void map_delchariddb(int charid);
+int map_reqchariddb(struct map_session_data * sd,int charid);
+char * map_charid2nick(int);
+struct map_session_data * map_charid2sd(int);
+
+struct map_session_data * map_id2sd(int);
+struct block_list * map_id2bl(int);
+int map_mapindex2mapid(unsigned short mapindex);
+int map_mapname2mapid(char*);
+int map_mapname2ipport(unsigned short,int*,int*);
+int map_setipport(unsigned short map,unsigned long ip,int port);
+int map_eraseipport(unsigned short map,unsigned long ip,int port);
+int map_eraseallipport(void);
+void map_addiddb(struct block_list *);
+void map_deliddb(struct block_list *bl);
+struct map_session_data** map_getallusers(int *users);
+int map_foreachiddb(int (*)(DBKey,void*,va_list),...);
+void map_addnickdb(struct map_session_data *);
+struct map_session_data * map_nick2sd(char*);
+int compare_item(struct item *a, struct item *b);
+
+// その他
+int map_check_dir(int s_dir,int t_dir);
+int map_calc_dir( struct block_list *src,int x,int y);
+int map_random_dir(struct block_list *bl, short *x, short *y); // [Skotlex]
+
+// Water functions...
+//
+int map_setwaterheight(int m, char *mapname, int height);
+int map_waterheight(char *mapname);
+
+// path.cより
+int path_search(struct walkpath_data*,int,int,int,int,int,int);
+int path_search_long(struct shootpath_data *,int,int,int,int,int);
+int path_blownpos(int m,int x0,int y0,int dx,int dy,int count);
+
+// distance related functions [Skotlex]
+#define check_distance_bl(bl1, bl2, distance) check_distance((bl1)->x - (bl2)->x, (bl1)->y - (bl2)->y, distance)
+#define check_distance_blxy(bl, x1, y1, distance) check_distance((bl)->x-(x1), (bl)->y-(y1), distance)
+#define check_distance_xy(x0, y0, x1, y1, distance) check_distance((x0)-(x1), (y0)-(y1), distance)
+int check_distance(int dx, int dy, int distance);
+
+#define distance_bl(bl1, bl2) distance((bl1)->x - (bl2)->x, (bl1)->y - (bl2)->y)
+#define distance_blxy(bl, x1, y1) distance((bl)->x-(x1), (bl)->y-(y1))
+#define distance_xy(x0, y0, x1, y1) distance((x0)-(x1), (y0)-(y1))
+unsigned int distance(int dx, int dy);
+
+int cleanup_sub(struct block_list *bl, va_list ap);
+
+void map_helpscreen(int flag); // [Valaris]
+int map_delmap(char *mapname);
+
+struct mob_list* map_addmobtolist(unsigned short m); // [Wizputer]
+void map_spawnmobs(int); // [Wizputer]
+void map_removemobs(int); // [Wizputer]
+
+//Added for own save method
+int charsql_db_init(int method);
+
+extern char *INTER_CONF_NAME;
+extern char *LOG_CONF_NAME;
+extern char *MAP_CONF_NAME;
+extern char *BATTLE_CONF_FILENAME;
+extern char *ATCOMMAND_CONF_FILENAME;
+extern char *CHARCOMMAND_CONF_FILENAME;
+extern char *SCRIPT_CONF_NAME;
+extern char *MSG_CONF_NAME;
+extern char *GRF_PATH_FILENAME;
+
+
+extern int charsave_method; //needed ..
+
+#ifndef TXT_ONLY
+
+// MySQL
+#ifdef __WIN32
+#include <my_global.h>
+#include <my_sys.h>
+#endif
+#include <mysql.h>
+
+extern char tmp_sql[65535];
+
+extern int db_use_sqldbs;
+extern MYSQL mmysql_handle;
+extern MYSQL_RES* sql_res ;
+extern MYSQL_ROW sql_row ;
+
+extern MYSQL lmysql_handle;
+extern MYSQL_RES* lsql_res ;
+extern MYSQL_ROW lsql_row ;
+
+extern MYSQL charsql_handle;
+extern MYSQL_RES* charsql_res;
+extern MYSQL_ROW charsql_row;
+
+extern MYSQL logmysql_handle;
+extern MYSQL_RES* logsql_res ;
+extern MYSQL_ROW logsql_row ;
+
+extern int mail_server_enable;
+extern MYSQL mail_handle;
+extern MYSQL_RES* mail_res ;
+extern MYSQL_ROW mail_row ;
+
+extern char item_db_db[32];
+extern char item_db2_db[32];
+extern char mob_db_db[32];
+extern char mob_db2_db[32];
+extern char login_db[32];
+
+// SQL for databases not supported yet. [Valaris]
+extern int db_use_newsqldbs;
+
+extern char abra_sqldb[32];
+extern char attr_fix_sqldb[32];
+extern char cast_sqldb[32];
+extern char castle_sqldb[32];
+extern char create_arrow_sqldb[32];
+extern char exp_sqldb[32];
+extern char exp_guild_sqldb[32];
+extern char item_bluebox_sqldb[32];
+extern char item_cardalbum_sqldb[32];
+extern char item_giftbox_sqldb[32];
+extern char item_scroll_sqldb[32];
+extern char item_violetbox_sqldb[32];
+extern char job_sqldb1[32];
+extern char mob_boss_sqldb[32];
+extern char mob_branch_sqldb[32];
+extern char mob_poring_sqldb[32];
+extern char mob_skill_sqldb[32];
+extern char pet_sqldb[32];
+extern char produce_sqldb[32];
+extern char refine_sqldb[32];
+extern char size_fix_sqldb[32];
+extern char skill_sqldb[32];
+extern char skill_require_sqldb[32];
+extern char skill_tree_sqldb[32];
+// End [Valaris]
+
+extern char login_db_level[32];
+extern char login_db_account_id[32];
+
+extern char gm_db[32];
+extern char gm_db_level[32];
+extern char gm_db_account_id[32];
+
+extern int read_gm_interval;
+
+extern char char_db[32];
+
+#ifdef MAPREGSQL
+// [zBuffer] SQL Mapreg
+extern MYSQL mapregsql_handle;
+extern MYSQL_RES* mapregsql_res ;
+extern MYSQL_ROW mapregsql_row;
+#endif
+
+extern char mail_db[32];
+
+#endif /* not TXT_ONLY */
+
+extern int lowest_gm_level;
+extern char main_chat_nick[16];
+
+#endif
diff --git a/src/map/mercenary.c b/src/map/mercenary.c
new file mode 100644
index 000000000..704963649
--- /dev/null
+++ b/src/map/mercenary.c
@@ -0,0 +1,11 @@
+// Homunculus and future Mercenary system code go here [Celest]
+
+int do_init_merc (void)
+{
+ // read DB's
+}
+
+int do_final_merc (void)
+{
+ // clean up
+} \ No newline at end of file
diff --git a/src/map/mercenary.h b/src/map/mercenary.h
new file mode 100644
index 000000000..704963649
--- /dev/null
+++ b/src/map/mercenary.h
@@ -0,0 +1,11 @@
+// Homunculus and future Mercenary system code go here [Celest]
+
+int do_init_merc (void)
+{
+ // read DB's
+}
+
+int do_final_merc (void)
+{
+ // clean up
+} \ No newline at end of file
diff --git a/src/map/mob.c b/src/map/mob.c
new file mode 100644
index 000000000..b315c5c43
--- /dev/null
+++ b/src/map/mob.c
@@ -0,0 +1,5020 @@
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <math.h>
+
+#include "timer.h"
+#include "socket.h"
+#include "db.h"
+#include "nullpo.h"
+#include "malloc.h"
+#include "map.h"
+#include "clif.h"
+#include "intif.h"
+#include "pc.h"
+#include "status.h"
+#include "mob.h"
+#include "guild.h"
+#include "itemdb.h"
+#include "skill.h"
+#include "battle.h"
+#include "party.h"
+#include "npc.h"
+#include "log.h"
+#include "showmsg.h"
+#include "script.h"
+#include "atcommand.h"
+#include "date.h"
+
+#define MIN_MOBTHINKTIME 100
+#define MIN_MOBLINKTIME 1000
+
+#define MOB_LAZYSKILLPERC 10 // Probability for mobs far from players from doing their IDLE skill. (rate of 1000 minute)
+#define MOB_LAZYMOVEPERC 50 // Move probability in the negligent mode MOB (rate of 1000 minute)
+#define MOB_LAZYWARPPERC 20 // Warp probability in the negligent mode MOB (rate of 1000 minute)
+
+//Dynamic mob database, allows saving of memory when there's big gaps in the mob_db [Skotlex]
+struct mob_db *mob_db_data[MAX_MOB_DB+1];
+struct mob_db *mob_dummy = NULL; //Dummy mob to be returned when a non-existant one is requested.
+
+struct mob_db *mob_db(int index) { if (index < 0 || index > MAX_MOB_DB || mob_db_data[index] == NULL) return mob_dummy; return mob_db_data[index]; }
+
+#define CLASSCHANGE_BOSS_NUM 21
+
+/*==========================================
+ * Local prototype declaration (only required thing)
+ *------------------------------------------
+ */
+static int mob_makedummymobdb(int);
+static int mob_timer(int,unsigned int,int,int);
+static int mob_spawn_guardian_sub(int,unsigned int,int,int);
+int mobskill_use(struct mob_data *md,unsigned int tick,int event);
+int mobskill_deltimer(struct mob_data *md );
+int mob_skillid2skillidx(int class_,int skillid);
+int mobskill_use_id(struct mob_data *md,struct block_list *target,int skill_idx);
+
+/*==========================================
+ * Mob is searched with a name.
+ *------------------------------------------
+ */
+int mobdb_searchname(const char *str)
+{
+ int i;
+ struct mob_db* mob;
+ for(i=0;i<=MAX_MOB_DB;i++){
+ mob = mob_db(i);
+ if(mob == mob_dummy) //Skip dummy mobs.
+ continue;
+ if(strcmpi(mob->name,str)==0 || strcmp(mob->jname,str)==0 ||
+ memcmp(mob->name,str,NAME_LENGTH)==0 || memcmp(mob->jname,str,NAME_LENGTH)==0)
+ return i;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * Id Mob is checked.
+ *------------------------------------------
+ */
+int mobdb_checkid(const int id)
+{
+ if (mob_db(id) == mob_dummy)
+ return 0;
+ if (mob_is_clone(id)) //checkid is used mostly for random ID based code, therefore clone mobs are out of the question.
+ return 0;
+ return id;
+}
+
+/*==========================================
+ * The minimum data set for MOB spawning
+ *------------------------------------------
+ */
+int mob_spawn_dataset(struct mob_data *md,const char *mobname,int class_)
+{
+ nullpo_retr(0, md);
+
+ md->bl.prev=NULL;
+ md->bl.next=NULL;
+
+ md->base_class = md->class_ = class_;
+ md->db = mob_db(class_);
+
+ if(strcmp(mobname,"--en--")==0)
+ strncpy(md->name,md->db->name,NAME_LENGTH-1);
+ else if(strcmp(mobname,"--ja--")==0)
+ strncpy(md->name,md->db->jname,NAME_LENGTH-1);
+ else
+ strncpy(md->name,mobname,NAME_LENGTH-1);
+
+ md->n = 0;
+ md->bl.id= npc_get_new_npc_id();
+
+ memset(&md->state,0,sizeof(md->state));
+ md->timer = -1;
+ md->target_id=0;
+ md->attacked_id=0;
+ md->attacked_count=0;
+ md->speed=md->db->speed;
+
+ return 0;
+}
+
+/*==========================================
+ * Fetches a random mob_id [Skotlex]
+ * type: Where to fetch from:
+ * 0: dead branch list
+ * 1: poring list
+ * 2: bloody branch list
+ * flag:
+ * &1: Apply the summon success chance found in the list.
+ * &2: Apply a monster check level.
+ * lv: Mob level to check against
+ *------------------------------------------
+ */
+
+int mob_get_random_id(int type, int flag, int lv) {
+ struct mob_db *mob;
+ int i=0, k=0, class_;
+ if(type < 0 || type >= MAX_RANDOMMONSTER) {
+ if (battle_config.error_log)
+ ShowError("mob_get_random_id: Invalid type (%d) of random monster.\n", type);
+ return 0;
+ }
+ do {
+ class_ = rand() % MAX_MOB_DB;
+ if (flag&1)
+ k = rand() % 1000000;
+ mob = mob_db(class_);
+ } while ((mob == mob_dummy || mob->summonper[type] <= k ||
+ (flag&2 && lv < mob->lv)) && (i++) < MAX_MOB_DB);
+ if(i >= MAX_MOB_DB)
+ class_ = mob_db_data[0]->summonper[type];
+ return class_;
+}
+
+/*==========================================
+ * The MOB appearance for one time (for scripts)
+ *------------------------------------------
+ */
+int mob_once_spawn (struct map_session_data *sd, char *mapname,
+ int x, int y, const char *mobname, int class_, int amount, const char *event)
+{
+ struct mob_data *md = NULL;
+ int m, count, lv = 255;
+ int i, j;
+
+ if(sd) lv = sd->status.base_level;
+
+ if(sd && strcmp(mapname,"this")==0)
+ m = sd->bl.m;
+ else
+ m = map_mapname2mapid(mapname);
+
+ if (m < 0 || amount <= 0 || (class_ >= 0 && class_ <= 1000) || class_ > MAX_MOB_DB + 2*MAX_MOB_DB) // 値が異常なら召喚を止める
+ return 0;
+
+ if (sd) { //even if the coords were wrong, spawn mob anyways (but look for most suitable coords first) Got from Freya [Lupus]
+ if (x <= 0 || y <= 0) {
+ if (x <= 0) x = sd->bl.x + rand() % 3 - 1;
+ if (y <= 0) y = sd->bl.y + rand() % 3 - 1;
+ if (map_getcell(m, x, y, CELL_CHKNOPASS)) {
+ x = sd->bl.x;
+ y = sd->bl.y;
+ }
+ }
+ } else if (x <= 0 || y <= 0) {
+ i = j = 0;
+ do {
+ x = rand() % (map[m].xs - 2) + 1;
+ y = rand() % (map[m].ys - 2) + 1;
+ } while ((i = map_getcell(m, x, y, CELL_CHKNOPASS)) && j++ < 64);
+ if (i) { // not solved?
+ x = 0;
+ y = 0;
+ }
+ }
+
+ for (count = 0; count < amount; count++) {
+ md = (struct mob_data *)aCalloc(1,sizeof(struct mob_data));
+
+ if (class_ > 2*MAX_MOB_DB) { // large/tiny mobs [Valaris]
+ md->special_state.size = 2;
+ class_ -= 2*MAX_MOB_DB;
+ } else if (class_ > MAX_MOB_DB) {
+ md->special_state.size = 1;
+ class_ -= MAX_MOB_DB;
+ }
+
+ if (class_ < 0) {
+ class_ = mob_get_random_id(-class_ -1, battle_config.random_monster_checklv?3:1, lv);
+ if (!class_) {
+ aFree(md);
+ return 0;
+ }
+ if (battle_config.dead_branch_active)
+ //Behold Aegis's masterful decisions yet again...
+ //"I understand the "Aggressive" part, but the "Can Move" and "Can Attack" is just stupid" - Poki#3
+ md->mode = mob_db(class_)->mode|MD_AGGRESSIVE|MD_CANATTACK|MD_CANMOVE;
+ }
+
+
+ if(mob_db(class_)->mode & MD_LOOTER)
+ md->lootitem = (struct item *)aCalloc(LOOTITEM_SIZE,sizeof(struct item));
+
+ mob_spawn_dataset (md, mobname, class_);
+ md->bl.m = m;
+ md->bl.x = x;
+ md->bl.y = y;
+ md->m = m;
+ md->x0 = x;
+ md->y0 = y;
+ //md->xs = 0;
+ //md->ys = 0;
+ md->spawndelay1 = -1; // 一度のみフラグ
+ md->spawndelay2 = -1; // 一度のみフラグ
+
+ //better safe than sorry, current md->npc_event has a size of 50
+ if (strlen(event) < 50)
+ memcpy(md->npc_event, event, strlen(event));
+
+ md->bl.type = BL_MOB;
+ map_addiddb (&md->bl);
+ mob_spawn (md->bl.id);
+
+ if(class_ == MOBID_EMPERIUM) { // emperium hp based on defense level [Valaris]
+ struct guild_castle *gc = guild_mapname2gc(map[md->bl.m].name);
+ struct guild *g = gc?guild_search(gc->guild_id):NULL;
+ if(gc) {
+ md->max_hp += 2000 * gc->defense;
+ md->hp = md->max_hp;
+ md->guardian_data = aCalloc(1, sizeof(struct guardian_data));
+ md->guardian_data->castle = gc;
+ md->guardian_data->number = MAX_GUARDIANS;
+ md->guardian_data->guild_id = gc->guild_id;
+ if (g)
+ {
+ md->guardian_data->emblem_id = g->emblem_id;
+ memcpy(md->guardian_data->guild_name, g->name, NAME_LENGTH);
+ }
+ else if (gc->guild_id) //Guild not yet available, retry in 5.
+ add_timer(gettick()+5000,mob_spawn_guardian_sub,md->bl.id,md->guardian_data->guild_id);
+ }
+ } // end addition [Valaris]
+ }
+ return (amount > 0) ? md->bl.id : 0;
+}
+/*==========================================
+ * The MOB appearance for one time (& area specification for scripts)
+ *------------------------------------------
+ */
+int mob_once_spawn_area(struct map_session_data *sd,char *mapname,
+ int x0,int y0,int x1,int y1,
+ const char *mobname,int class_,int amount,const char *event)
+{
+ int x,y,i,max,lx=-1,ly=-1,id=0;
+ int m;
+
+ if(strcmp(mapname,"this")==0)
+ m=sd->bl.m;
+ else
+ m=map_mapname2mapid(mapname);
+
+ max=(y1-y0+1)*(x1-x0+1)*3;
+ if(max>1000)max=1000;
+
+ if(m<0 || amount<=0 || mob_db(class_) == mob_dummy) // A summon is stopped if a value is unusual
+ return 0;
+
+ for(i=0;i<amount;i++){
+ int j=0;
+ do{
+ x=rand()%(x1-x0+1)+x0;
+ y=rand()%(y1-y0+1)+y0;
+ } while (map_getcell(m,x,y,CELL_CHKNOPASS) && (++j)<max);
+ // freya }while( ( (c=map_getcell(m,x,y))==1 || c==5)&& (++j)<max );
+ if(j>=max){
+ if(lx>=0){ // Since reference went wrong, the place which boiled before is used.
+ x=lx;
+ y=ly;
+ }else
+ return 0; // Since reference of the place which boils first went wrong, it stops.
+ }
+ if(x==0||y==0) ShowWarning("mob_once_spawn_area: xory=0, x=%d,y=%d,x0=%d,y0=%d\n",x,y,x0,y0);
+ id=mob_once_spawn(sd,mapname,x,y,mobname,class_,1,event);
+ lx=x;
+ ly=y;
+ }
+ return id;
+}
+/*==========================================
+ * Set a Guardian's guild data [Skotlex]
+ *------------------------------------------
+ */
+static int mob_spawn_guardian_sub(int tid,unsigned int tick,int id,int data)
+{ //Needed because the guild_data may not be available at guardian spawn time.
+ struct block_list* bl = map_id2bl(id);
+ struct mob_data* md;
+ struct guild* g;
+
+ if (bl == NULL) //It is possible mob was already removed from map when the castle has no owner. [Skotlex]
+ return 0;
+
+ if (bl->type != BL_MOB || (md = (struct mob_data*)bl) == NULL)
+ {
+ ShowError("mob_spawn_guardian_sub: Block error!\n");
+ return 0;
+ }
+
+ nullpo_retr(0, md->guardian_data);
+ g = guild_search(data);
+
+ if (g == NULL)
+ { //Liberate castle, if the guild is not found this is an error! [Skotlex]
+ ShowError("mob_spawn_guardian_sub: Couldn't load guild %d!\n",data);
+ if (md->class_ == MOBID_EMPERIUM)
+ { //Not sure this is the best way, but otherwise we'd be invoking this for ALL guardians spawned later on.
+ md->guardian_data->guild_id = 0;
+ if (md->guardian_data->castle->guild_id) //Free castle up.
+ {
+ ShowNotice("Clearing ownership of castle %d (%s)\n", md->guardian_data->castle->castle_id, md->guardian_data->castle->castle_name);
+ guild_castledatasave(md->guardian_data->castle->castle_id, 1, 0);
+ }
+ } else {
+ if (md->guardian_data->castle->guardian[md->guardian_data->number].visible)
+ { //Safe removal of guardian.
+ md->guardian_data->castle->guardian[md->guardian_data->number].visible = 0;
+ guild_castledatasave(md->guardian_data->castle->castle_id, 10+md->guardian_data->number,0);
+ guild_castledatasave(md->guardian_data->castle->castle_id, 18+md->guardian_data->number,0);
+ }
+ mob_delete(md); //Remove guardian.
+ }
+ return 0;
+ }
+ md->guardian_data->emblem_id = g->emblem_id;
+ memcpy (md->guardian_data->guild_name, g->name, NAME_LENGTH);
+ md->guardian_data->guardup_lv = guild_checkskill(g,GD_GUARDUP);
+ return 0;
+}
+
+/*==========================================
+ * Summoning Guardians [Valaris]
+ *------------------------------------------
+ */
+int mob_spawn_guardian(struct map_session_data *sd,char *mapname,
+ int x,int y,const char *mobname,int class_,int amount,const char *event,int guardian)
+{
+ struct mob_data *md=NULL;
+ struct guild *g=NULL;
+ struct guild_castle *gc;
+
+ int m,count=1;
+
+ if( sd && strcmp(mapname,"this")==0)
+ m=sd->bl.m;
+ else
+ m=map_mapname2mapid(mapname);
+
+ if(m<0 || amount<=0 || (class_>=0 && class_<=1000) || class_>MAX_MOB_DB) // Invalid monster classes
+ return 0;
+
+ if(class_<0)
+ return 0;
+
+ if(guardian < 0 || guardian >= MAX_GUARDIANS)
+ {
+ ShowError("mob_spawn_guardian: Invalid guardian index %d for guardian %d (castle map %s)\n", guardian, class_, map[m].name);
+ return 0;
+ }
+ if (amount > 1)
+ ShowWarning("mob_spawn_guardian: Spawning %d guardians in position %d (castle map %s)\n", amount, map[m].name);
+
+ if(sd){
+ if(x<=0) x=sd->bl.x;
+ if(y<=0) y=sd->bl.y;
+ }
+ else if(x<=0 || y<=0)
+ ShowWarning("mob_spawn_guardian: Invalid coordinates (%d,%d)\n",x,y);
+
+ gc=guild_mapname2gc(map[m].name);
+ if (gc == NULL)
+ {
+ ShowError("mob_spawn_guardian: No castle set at map %s\n", map[m].name);
+ return 0;
+ }
+ if (!gc->guild_id)
+ ShowWarning("mob_spawn_guardian: Spawning guardian %d on a castle with no guild (castle map %s)\n", class_, map[m].name);
+ else
+ g = guild_search(gc->guild_id);
+
+ if (gc->guardian[guardian].id)
+ ShowWarning("mob_spawn_guardian: Spawning guardian in position %d which already has a guardian (castle map %s)\n", guardian, map[m].name);
+
+ for(count=0;count<amount;count++){
+ md=(struct mob_data *) aCalloc(1, sizeof(struct mob_data));
+ mob_spawn_dataset(md,mobname,class_);
+ md->bl.m=m;
+ md->bl.x=x;
+ md->bl.y=y;
+ md->m =m;
+ md->x0=x;
+ md->y0=y;
+ md->xs=0;
+ md->ys=0;
+ md->spawndelay1=-1; // Only once is a flag.
+ md->spawndelay2=-1; // Only once is a flag.
+
+ //better safe than sorry, current md->npc_event has a size of 50 [Skotlex]
+ if (strlen(event) < 50)
+ memcpy(md->npc_event, event, strlen(event));
+
+ md->bl.type=BL_MOB;
+ map_addiddb(&md->bl);
+ mob_spawn(md->bl.id);
+
+ md->max_hp += 2000 * gc->defense;
+ md->guardian_data = aCalloc(1, sizeof(struct guardian_data));
+ md->guardian_data->number = guardian;
+ md->guardian_data->guild_id = gc->guild_id;
+ md->guardian_data->castle = gc;
+ md->hp = gc->guardian[guardian].hp;
+ gc->guardian[guardian].id = md->bl.id;
+ if (g)
+ {
+ md->guardian_data->emblem_id = g->emblem_id;
+ memcpy (md->guardian_data->guild_name, g->name, NAME_LENGTH);
+ md->guardian_data->guardup_lv = guild_checkskill(g,GD_GUARDUP);
+ } else if (md->guardian_data->guild_id)
+ add_timer(gettick()+5000,mob_spawn_guardian_sub,md->bl.id,md->guardian_data->guild_id);
+ }
+
+ return (amount>0)?md->bl.id:0;
+}
+
+/*==========================================
+ * Is MOB in the state in which the present movement is possible or not?
+ *------------------------------------------
+ */
+int mob_can_move(struct mob_data *md)
+{
+ nullpo_retr(0, md);
+
+ if(DIFF_TICK(md->canmove_tick, gettick()) > 0 || md->skilltimer != -1 || (md->opt1 > 0 && md->opt1 != OPT1_STONEWAIT) || md->option&OPTION_HIDE)
+ return 0;
+ // アンクル中で動けないとか
+ if( md->sc_data[SC_ANKLE].timer != -1 || //アンクルスネア
+ md->sc_data[SC_AUTOCOUNTER].timer != -1 || //オートカウンター
+ md->sc_data[SC_BLADESTOP].timer != -1 || //白刃取り
+ md->sc_data[SC_SPIDERWEB].timer != -1 || //スパイダーウェッブ
+ (md->sc_data[SC_DANCING].timer !=-1 && md->sc_data[SC_DANCING].val1 == CG_HERMODE) || //cannot move while Hermod is active.
+ (md->sc_data[SC_GOSPEL].timer !=-1 && md->sc_data[SC_GOSPEL].val4 == BCT_SELF) || // cannot move while gospel is in effect
+ md->sc_data[SC_STOP].timer != -1 ||
+ md->sc_data[SC_CLOSECONFINE].timer != -1 ||
+ md->sc_data[SC_CLOSECONFINE2].timer != -1
+ )
+ return 0;
+
+ return 1;
+}
+
+/*==========================================
+ * Time calculation concerning one step next to mob
+ *------------------------------------------
+ */
+static int calc_next_walk_step(struct mob_data *md)
+{
+ nullpo_retr(0, md);
+
+ if(md->walkpath.path_pos>=md->walkpath.path_len)
+ return -1;
+ if(md->walkpath.path[md->walkpath.path_pos]&1)
+ return status_get_speed(&md->bl)*14/10;
+ return status_get_speed(&md->bl);
+}
+
+static int mob_walktoxy_sub(struct mob_data *md);
+
+/*==========================================
+ * Mob Walk processing
+ *------------------------------------------
+ */
+static int mob_walk(struct mob_data *md,unsigned int tick,int data)
+{
+ int moveblock;
+ int i;
+ static int dirx[8]={0,-1,-1,-1,0,1,1,1};
+ static int diry[8]={1,1,0,-1,-1,-1,0,1};
+ int x,y,dx,dy;
+
+ nullpo_retr(0, md);
+
+ md->state.state=MS_IDLE;
+ if(md->walkpath.path_pos>=md->walkpath.path_len || md->walkpath.path_pos!=data)
+ return 0;
+
+ md->walkpath.path_half ^= 1;
+ if(md->walkpath.path_half==0){
+ md->walkpath.path_pos++;
+ if(md->state.change_walk_target){
+ mob_walktoxy_sub(md);
+ return 0;
+ }
+ }
+ else {
+ if(md->walkpath.path[md->walkpath.path_pos]>=8)
+ return 1;
+
+ x = md->bl.x;
+ y = md->bl.y;
+#ifndef CELL_NOSTACK
+ if(map_getcell(md->bl.m,x,y,CELL_CHKNOPASS)) {
+ mob_stop_walking(md,1);
+ return 0;
+ }
+#endif
+ md->dir=md->walkpath.path[md->walkpath.path_pos];
+ dx = dirx[md->dir];
+ dy = diry[md->dir];
+
+ if (map_getcell(md->bl.m,x+dx,y+dy,CELL_CHKBASILICA) && !(status_get_mode(&md->bl)&MD_BOSS)) {
+ mob_stop_walking(md,1);
+ return 0;
+ }
+
+ if (map_getcell(md->bl.m,x+dx,y+dy,CELL_CHKNOPASS)) {
+ mob_walktoxy_sub(md);
+ return 0;
+ }
+
+ moveblock = ( x/BLOCK_SIZE != (x+dx)/BLOCK_SIZE || y/BLOCK_SIZE != (y+dy)/BLOCK_SIZE);
+
+ md->state.state=MS_WALK;
+ map_foreachinmovearea(clif_moboutsight,md->bl.m,x-AREA_SIZE,y-AREA_SIZE,x+AREA_SIZE,y+AREA_SIZE,dx,dy,BL_PC,md);
+
+ if ( md->min_chase > md->db->range2)
+ md->min_chase--;
+
+ x += dx;
+ y += dy;
+ map_moveblock(&md->bl, x, y, tick);
+
+ map_foreachinmovearea(clif_mobinsight,md->bl.m,x-AREA_SIZE,y-AREA_SIZE,x+AREA_SIZE,y+AREA_SIZE,-dx,-dy,BL_PC,md);
+ md->state.state=MS_IDLE;
+
+ if(md->option&4)
+ skill_check_cloaking(&md->bl);
+ }
+ if((i=calc_next_walk_step(md))>0){
+ i = i>>1;
+ if(i < 1 && md->walkpath.path_half == 0)
+ i = 1;
+
+ if(md->walkpath.path_pos>=md->walkpath.path_len)
+ clif_fixmobpos(md); // とまったときに位置の再送信
+ else {
+ md->timer=add_timer(tick+i,mob_timer,md->bl.id,md->walkpath.path_pos);
+ md->state.state=MS_WALK;
+ }
+ }
+ return 0;
+}
+
+/*==========================================
+ * Reachability to a Specification ID existence place
+ * state indicates type of 'seek' mob should do:
+ * - MSS_LOOT: Looking for item, path must be easy.
+ * - MSS_RUSH: Chasing attacking player, path is determined by mob_ai&1
+ * - MSS_FOLLOW: Initiative/support seek, path must be easy.
+ *------------------------------------------
+ */
+int mob_can_reach(struct mob_data *md,struct block_list *bl,int range, int state)
+{
+ int dx,dy;
+ struct walkpath_data wpd;
+ int i, easy = 0;
+
+ nullpo_retr(0, md);
+ nullpo_retr(0, bl);
+ switch (state) {
+ case MSS_RUSH:
+ easy = (battle_config.mob_ai&1?0:1);
+ break;
+ case MSS_LOOT:
+ case MSS_FOLLOW:
+ default:
+ easy = 1;
+ break;
+ }
+
+ if( md->bl.m != bl->m) // 違うャbプ
+ return 0;
+
+ if( md->bl.x==bl->x && md->bl.y==bl->y ) // 同じマス
+ return 1;
+
+ if( range>0 && !check_distance_bl(&md->bl, bl, range))
+ return 0;
+
+ dx=abs(bl->x - md->bl.x);
+ dy=abs(bl->y - md->bl.y);
+ // Obstacle judging
+ wpd.path_len=0;
+ wpd.path_pos=0;
+ wpd.path_half=0;
+ if(path_search(&wpd,md->bl.m,md->bl.x,md->bl.y,bl->x,bl->y,easy)!=-1)
+ return 1;
+
+ if(bl->type!=BL_PC && bl->type!=BL_MOB)
+ return 0;
+
+ // It judges whether it can adjoin or not.
+ dx=(dx>0)?1:((dx<0)?-1:0);
+ dy=(dy>0)?1:((dy<0)?-1:0);
+ if(path_search(&wpd,md->bl.m,md->bl.x,md->bl.y,bl->x-dx,bl->y-dy,easy)!=-1)
+ return 1;
+ for(i=0;i<9;i++){
+ if(path_search(&wpd,md->bl.m,md->bl.x,md->bl.y,bl->x-1+i/3,bl->y-1+i%3,easy)!=-1)
+ return 1;
+ }
+ return 0;
+}
+
+/*==========================================
+ * Links nearby mobs (supportive mobs)
+ *------------------------------------------
+ */
+static int mob_linksearch(struct block_list *bl,va_list ap)
+{
+ struct mob_data *md;
+ int class_;
+ struct block_list *target;
+ unsigned int tick;
+
+ nullpo_retr(0, bl);
+ nullpo_retr(0, ap);
+ md=(struct mob_data *)bl;
+ class_ = va_arg(ap, int);
+ target = va_arg(ap, struct block_list *);
+ tick=va_arg(ap, unsigned int);
+
+ if (md->class_ == class_ && DIFF_TICK(md->last_linktime, tick) < MIN_MOBLINKTIME
+ && (!md->target_id || md->state.targettype == NONE_ATTACKABLE))
+ {
+ md->last_linktime = tick;
+ if( mob_can_reach(md,target,md->db->range2, MSS_FOLLOW) ){ // Reachability judging
+ md->target_id = target->id;
+ md->attacked_count = 0;
+ md->state.targettype = ATTACKABLE;
+ md->state.aggressive = (status_get_mode(&md->bl)&MD_ANGRY)?1:0;
+ md->min_chase=md->db->range3;
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * Attack processing of mob
+ *------------------------------------------
+ */
+static int mob_attack(struct mob_data *md,unsigned int tick,int data)
+{
+ struct block_list *tbl=NULL;
+
+ int range;
+
+ nullpo_retr(0, md);
+
+ md->min_chase=md->db->range3;
+ md->state.state=MS_IDLE;
+ md->state.skillstate=MSS_IDLE;
+
+ if( md->skilltimer!=-1 ) // スキル使用中
+ return 0;
+
+ if((tbl=map_id2bl(md->target_id))==NULL || !status_check_skilluse(&md->bl, tbl, 0, 0)){
+ md->target_id=0;
+ md->state.targettype = NONE_ATTACKABLE;
+ return 0;
+ }
+
+ if (!check_distance_bl(&md->bl, tbl, md->db->range3)){
+ mob_stopattack(md);
+ return 0;
+ }
+
+ range = md->db->range;
+ if (range <= 3)
+ range++; //Melee attackers get a bonus range cell when attacking.
+
+ /* It seems mobs always teleport the last two tiles when chasing players, so do not give them this bonus range tile.[Skotlex]
+ if(battle_iswalking(tbl)) range++;
+ */
+ if(!check_distance_bl(&md->bl, tbl, range))
+ return 0;
+ if(battle_config.monster_attack_direction_change)
+ md->dir=map_calc_dir(&md->bl, tbl->x,tbl->y ); // 向き設定
+
+ if (status_get_mode(&md->bl)&MD_ASSIST && DIFF_TICK(md->last_linktime, tick) < MIN_MOBLINKTIME)
+ { // Link monsters nearby [Skotlex]
+ md->last_linktime = tick;
+ map_foreachinarea(mob_linksearch, md->bl.m,
+ md->bl.x-md->db->range2, md->bl.y-md->db->range2,
+ md->bl.x+md->db->range2, md->bl.y+md->db->range2,
+ BL_MOB, md->class_, tbl, tick);
+ }
+
+ md->state.skillstate=md->state.aggressive?MSS_ANGRY:MSS_BERSERK;
+ if( mobskill_use(md,tick,-1) ) // スキル使用
+ return 0;
+
+ if(md->sc_data && md->sc_data[SC_WINKCHARM].timer != -1)
+ clif_emotion(&md->bl, 3);
+ else
+ md->target_lv = battle_weapon_attack(&md->bl,tbl,tick,0);
+
+ if(!(battle_config.monster_cloak_check_type&2) && md->sc_data[SC_CLOAKING].timer != -1)
+ status_change_end(&md->bl,SC_CLOAKING,-1);
+
+ //Mobs can't move if they can't attack neither.
+ //Use the attack delay for next can attack try
+ //But use the attack motion to know when it can start moving. [Skotlex]
+ md->attackabletime = tick + status_get_adelay(&md->bl);
+ md->canmove_tick = tick + status_get_amotion(&md->bl);
+
+ md->timer=add_timer(md->attackabletime,mob_timer,md->bl.id,0);
+ md->state.state=MS_ATTACK;
+
+ return 0;
+}
+
+/*==========================================
+ * The attack of PC which is attacking id is stopped.
+ * The callback function of clif_foreachclient
+ *------------------------------------------
+ */
+int mob_stopattacked(struct map_session_data *sd,va_list ap)
+{
+ int id;
+
+ nullpo_retr(0, sd);
+ nullpo_retr(0, ap);
+
+ id=va_arg(ap,int);
+ if(sd->attacktarget==id)
+ pc_stopattack(sd);
+ return 0;
+}
+/*==========================================
+ * The timer in which the mob's states changes
+ *------------------------------------------
+ */
+int mob_changestate(struct mob_data *md,int state,int type)
+{
+ unsigned int tick;
+ int i;
+
+ nullpo_retr(0, md);
+
+ if(md->timer != -1)
+ delete_timer(md->timer,mob_timer);
+ md->timer=-1;
+ md->state.state=state;
+
+ switch(state){
+ case MS_WALK:
+ if((i=calc_next_walk_step(md))>0){
+ i = i>>2;
+ md->timer=add_timer(gettick()+i,mob_timer,md->bl.id,0);
+ }
+ else
+ md->state.state=MS_IDLE;
+ break;
+ case MS_ATTACK:
+ tick = gettick();
+ i=DIFF_TICK(md->attackabletime,tick);
+ if(i>0 && i<2000)
+ md->timer=add_timer(md->attackabletime,mob_timer,md->bl.id,0);
+ else if(type) {
+ md->attackabletime = tick + status_get_amotion(&md->bl);
+ md->timer=add_timer(md->attackabletime,mob_timer,md->bl.id,0);
+ }
+ else {
+ md->attackabletime = tick + 1;
+ md->timer=add_timer(md->attackabletime,mob_timer,md->bl.id,0);
+ }
+ break;
+ case MS_DELAY:
+ md->timer=add_timer(gettick()+type,mob_timer,md->bl.id,0);
+ break;
+ case MS_DEAD:
+ skill_castcancel(&md->bl,0);
+// mobskill_deltimer(md);
+ md->state.skillstate=MSS_DEAD;
+ md->last_deadtime=gettick();
+ // Since it died, all aggressors' attack to this mob is stopped.
+ clif_foreachclient(mob_stopattacked,md->bl.id);
+ skill_unit_move(&md->bl,gettick(),4);
+ status_change_clear(&md->bl,2); // ステータス異常を解除する
+ skill_clear_unitgroup(&md->bl); // 全てのスキルユニットグループを削除する
+ skill_cleartimerskill(&md->bl);
+ if(md->deletetimer!=-1)
+ delete_timer(md->deletetimer,mob_timer_delete);
+ md->deletetimer=-1;
+ md->hp = md->target_id = md->attacked_id = md->attacked_count = 0;
+ md->state.targettype = NONE_ATTACKABLE;
+ break;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * timer processing of mob (timer function)
+ * It branches to a walk and an attack.
+ *------------------------------------------
+ */
+static int mob_timer(int tid,unsigned int tick,int id,int data)
+{
+ struct mob_data *md;
+ struct block_list *bl;
+
+ if( (bl=map_id2bl(id)) == NULL ){ //攻撃してきた敵がもういないのは正常のようだ
+ return 1;
+ }
+
+ if(!bl || !bl->type || bl->type!=BL_MOB)
+ return 1;
+
+ nullpo_retr(1, md=(struct mob_data*)bl);
+
+ if(md->timer != tid){
+ if(battle_config.error_log)
+ ShowError("mob_timer %d != %d\n",md->timer,tid);
+ return 0;
+ }
+ md->timer=-1;
+ if(md->bl.prev == NULL || md->state.state == MS_DEAD)
+ return 1;
+
+ map_freeblock_lock();
+ switch(md->state.state){
+ case MS_WALK:
+ mob_walk(md,tick,data);
+ break;
+ case MS_ATTACK:
+ mob_attack(md,tick,data);
+ break;
+ case MS_DELAY:
+ mob_changestate(md,MS_IDLE,0);
+ break;
+ default:
+ if(battle_config.error_log)
+ ShowError("mob_timer : %d ?\n",md->state.state);
+ break;
+ }
+
+ if (md->timer == -1)
+ mob_changestate(md,MS_WALK,0);
+
+ map_freeblock_unlock();
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+static int mob_walktoxy_sub(struct mob_data *md)
+{
+ struct walkpath_data wpd;
+ int x,y;
+ static int dirx[8]={0,-1,-1,-1,0,1,1,1};
+ static int diry[8]={1,1,0,-1,-1,-1,0,1};
+
+ nullpo_retr(0, md);
+
+ memset(&wpd, 0, sizeof(wpd));
+
+ if(path_search(&wpd,md->bl.m,md->bl.x,md->bl.y,md->to_x,md->to_y,md->state.walk_easy))
+ return 1;
+ if (wpd.path[0] >= 8)
+ return 1;
+ x = md->bl.x+dirx[wpd.path[0]];
+ y = md->bl.y+diry[wpd.path[0]];
+ if (map_getcell(md->bl.m,x,y,CELL_CHKBASILICA) && !(status_get_mode(&md->bl)&MD_BOSS)) {
+ md->state.change_walk_target=0;
+ return 1;
+ }
+
+ memcpy(&md->walkpath,&wpd,sizeof(wpd));
+
+ md->state.change_walk_target=0;
+ mob_changestate(md,MS_WALK,0);
+ clif_movemob(md);
+
+ return 0;
+}
+
+/*==========================================
+ * mob move start
+ *------------------------------------------
+ */
+int mob_walktoxy(struct mob_data *md,int x,int y,int easy)
+{
+ struct walkpath_data wpd;
+
+ nullpo_retr(0, md);
+
+ if(md->bl.prev == NULL || md->state.state == MS_DEAD) //Just-in-case check to prevent dead mobs from moving. [Skotlex]
+ return 1;
+
+ if(md->state.state == MS_WALK && path_search(&wpd,md->bl.m,md->bl.x,md->bl.y,x,y,easy) )
+ return 1;
+
+ md->state.walk_easy = easy;
+ md->to_x=x;
+ md->to_y=y;
+
+ if (md->sc_data[SC_CONFUSION].timer != -1) //Randomize target direction.
+ map_random_dir(&md->bl, &md->to_x, &md->to_y);
+
+ if(md->state.state == MS_WALK)
+ md->state.change_walk_target=1;
+ else
+ return mob_walktoxy_sub(md);
+
+ return 0;
+}
+
+/*==========================================
+ * mob spawn with delay (timer function)
+ *------------------------------------------
+ */
+static int mob_delayspawn(int tid, unsigned int tick, int m, int n)
+{
+ mob_spawn(m);
+ return 0;
+}
+
+/*==========================================
+ * spawn timing calculation
+ *------------------------------------------
+ */
+int mob_setdelayspawn(int id)
+{
+ unsigned int spawntime, spawntime1, spawntime2, spawntime3;
+ struct mob_data *md;
+ struct block_list *bl;
+
+ if ((bl = map_id2bl(id)) == NULL || bl->type != BL_MOB)
+ return -1;
+ nullpo_retr(-1, md = (struct mob_data*)bl);
+
+ // Processing of MOB which is not revitalized
+ if (md->spawndelay1 == -1 && md->spawndelay2 == -1 && md->n == 0) {
+ if (md->lootitem) {
+ aFree(md->lootitem);
+ md->lootitem = NULL;
+ }
+ if (md->guardian_data)
+ {
+ if (md->guardian_data->number < MAX_GUARDIANS)
+ md->guardian_data->castle->guardian[md->guardian_data->number].id = 0;
+ aFree (md->guardian_data);
+ md->guardian_data = NULL;
+ }
+ map_deliddb(&md->bl);
+ map_delblock(bl); //In case it wasn't done before invoking the function.
+ map_freeblock(bl);
+ return 0;
+ }
+
+ spawntime1 = md->last_spawntime + md->spawndelay1;
+ spawntime2 = md->last_deadtime + md->spawndelay2;
+ spawntime3 = gettick() + 5000 + rand()%5000; //Lupus
+ // spawntime = max(spawntime1,spawntime2,spawntime3);
+ if (DIFF_TICK(spawntime1, spawntime2) > 0)
+ spawntime = spawntime1;
+ else
+ spawntime = spawntime2;
+ if (DIFF_TICK(spawntime3, spawntime) > 0)
+ spawntime = spawntime3;
+
+ add_timer(spawntime, mob_delayspawn, id, 0);
+ return 0;
+}
+
+static int mob_count_sub(struct block_list *bl,va_list ap)
+{
+ return 1;
+}
+
+/*==========================================
+ * Mob spawning. Initialization is also variously here.
+ *------------------------------------------
+ */
+int mob_spawn (int id)
+{
+ int x, y, i = 0;
+ unsigned int c, tick = gettick();
+ struct mob_data *md;
+ struct block_list *bl;
+
+ if ((bl = map_id2bl(id)) == NULL || bl->type != BL_MOB)
+ return -1;
+ nullpo_retr(-1, md = (struct mob_data*)bl);
+
+ md->last_spawntime = tick;
+ if (md->bl.prev != NULL)
+ map_delblock(&md->bl);
+ else {
+ if(md->class_ != md->base_class){ // クラスチェンジしたMob
+ md->class_ = md->base_class;
+ md->db = mob_db(md->base_class);
+ memcpy(md->name,md->db->jname,NAME_LENGTH);
+ md->speed=md->db->speed;
+ }
+ }
+ md->bl.m = md->m;
+ do {
+ if (md->x0 == 0 && md->y0 == 0) {
+ x = rand()%(map[md->bl.m].xs-2)+1;
+ y = rand()%(map[md->bl.m].ys-2)+1;
+ } else {
+ x = md->x0+rand()%(md->xs+1)-md->xs/2;
+ y = md->y0+rand()%(md->ys+1)-md->ys/2;
+ }
+ i++;
+ if (battle_config.no_spawn_on_player && i <= battle_config.no_spawn_on_player)
+ { //Avoid spawning on the view-range of players. [Skotlex]
+ if (map_foreachinarea(mob_count_sub, md->bl.m,
+ x-AREA_SIZE, y-AREA_SIZE, x+AREA_SIZE, y+AREA_SIZE,
+ BL_PC) > 0)
+ continue;
+ }
+ } while(map_getcell(md->bl.m,x,y,CELL_CHKNOPASS) && i < 50);
+
+ if (i >= 50) {
+ if (md->spawndelay1 != -1 || md->spawndelay2 == -1)
+ // retry again later
+ add_timer(tick+5000,mob_delayspawn,id,0);
+ return 1;
+ }
+
+ md->to_x = md->bl.x = x;
+ md->to_y = md->bl.y = y;
+ md->dir = 0;
+ md->target_dir = 0;
+
+ memset(&md->state, 0, sizeof(md->state));
+ md->attacked_id = 0;
+ md->attacked_count = 0;
+ md->target_id = 0;
+ md->mode = 0;
+ md->move_fail_count = 0;
+
+ if (!md->speed)
+ md->speed = md->db->speed;
+ md->def_ele = md->db->element;
+
+ if (!md->level) // [Valaris]
+ md->level=md->db->lv;
+
+ md->master_id = 0;
+ md->master_dist = 0;
+
+ md->state.state = MS_IDLE;
+ md->state.skillstate = MSS_IDLE;
+ md->timer = -1;
+ md->last_thinktime = tick;
+ md->next_walktime = tick+rand()%50+5000;
+ md->attackabletime = tick;
+ md->canmove_tick = tick;
+ md->last_linktime = tick;
+
+ /* Guardians should be spawned using mob_spawn_guardian! [Skotlex]
+ * and the Emperium is spawned using mob_once_spawn.
+ md->guild_id = 0;
+ if (md->class_ >= 1285 && md->class_ <= 1288) {
+ struct guild_castle *gc=guild_mapname2gc(map[md->bl.m].name);
+ if(gc)
+ md->guild_id = gc->guild_id;
+ }
+ */
+
+ md->deletetimer = -1;
+
+ md->skilltimer = -1;
+ for (i = 0, c = tick-1000*3600*10; i < MAX_MOBSKILL; i++)
+ md->skilldelay[i] = c;
+ md->skillid = 0;
+ md->skilllv = 0;
+
+ memset(md->dmglog, 0, sizeof(md->dmglog));
+ md->tdmg = 0;
+ if (md->lootitem)
+ memset(md->lootitem, 0, sizeof(md->lootitem));
+ md->lootitem_count = 0;
+
+ for (i = 0; i < MAX_MOBSKILLTIMERSKILL; i++)
+ md->skilltimerskill[i].timer = -1;
+
+ for (i = 0; i < MAX_STATUSCHANGE; i++) {
+ md->sc_data[i].timer = -1;
+ md->sc_data[i].val1 = md->sc_data[i].val2 = md->sc_data[i].val3 = md->sc_data[i].val4 = 0;
+ }
+ md->sc_count = 0;
+ md->opt1 = md->opt2 = md->opt3 = md->option = 0;
+
+ if(md->db->option){ // Added for carts, falcons and pecos for cloned monsters. [Valaris]
+ if(md->db->option & 0x0008)
+ md->option |= 0x0008;
+ if(md->db->option & 0x0080)
+ md->option |= 0x0080;
+ if(md->db->option & 0x0100)
+ md->option |= 0x0100;
+ if(md->db->option & 0x0200)
+ md->option |= 0x0200;
+ if(md->db->option & 0x0400)
+ md->option |= 0x0400;
+ if(md->db->option & OPTION_FALCON)
+ md->option |= OPTION_FALCON;
+ if(md->db->option & OPTION_RIDING)
+ md->option |= OPTION_RIDING;
+ }
+
+ memset(md->skillunit, 0, sizeof(md->skillunit));
+ memset(md->skillunittick, 0, sizeof(md->skillunittick));
+
+ md->max_hp = md->db->max_hp;
+ if(md->special_state.size==1) // change for sized monsters [Valaris]
+ md->max_hp/=2;
+ else if(md->special_state.size==2)
+ md->max_hp*=2;
+ md->hp = md->max_hp;
+
+ map_addblock(&md->bl);
+ skill_unit_move(&md->bl,tick,1);
+
+ clif_spawnmob(md);
+
+ return 0;
+}
+
+/*==========================================
+ * The stop of MOB's attack
+ *------------------------------------------
+ */
+int mob_stopattack(struct mob_data *md)
+{
+ md->target_id = 0;
+ md->state.targettype = NONE_ATTACKABLE;
+ md->attacked_id = 0;
+ md->attacked_count = 0;
+ return 0;
+}
+/*==========================================
+ * The stop of MOB's walking
+ *------------------------------------------
+ */
+int mob_stop_walking(struct mob_data *md,int type)
+{
+ nullpo_retr(0, md);
+
+ if(md->state.state == MS_WALK || md->state.state == MS_IDLE) {
+ int dx=0,dy=0;
+
+ md->walkpath.path_len=0;
+ if(type&2 && mob_can_move(md)){
+ dx=md->to_x-md->bl.x;
+ if(dx<0)
+ dx=-1;
+ else if(dx>0)
+ dx=1;
+ dy=md->to_y-md->bl.y;
+ if(dy<0)
+ dy=-1;
+ else if(dy>0)
+ dy=1;
+ }
+ md->to_x=md->bl.x+dx;
+ md->to_y=md->bl.y+dy;
+ if(dx!=0 || dy!=0){
+ mob_walktoxy_sub(md);
+ return 0;
+ }
+ mob_changestate(md,MS_IDLE,0);
+ }
+ if(type&0x01)
+ clif_fixmobpos(md);
+
+ return 0;
+}
+
+
+/*==========================================
+ * Determines if the mob can change target. [Skotlex]
+ *------------------------------------------
+ */
+static int mob_can_changetarget(struct mob_data* md, struct block_list* target, int mode)
+{
+ switch (md->state.skillstate) {
+ case MSS_BERSERK: //Only Assist, Angry or Aggressive+CastSensor mobs can change target while attacking.
+ if (mode&(MD_ASSIST|MD_ANGRY) || (mode&(MD_AGGRESSIVE|MD_CASTSENSOR)) == (MD_AGGRESSIVE|MD_CASTSENSOR))
+ return (battle_config.mob_ai&4 || check_distance_bl(&md->bl, target, 3));
+ else
+ return 0;
+ case MSS_RUSH:
+ return (mode&MD_AGGRESSIVE);
+ case MSS_FOLLOW:
+ case MSS_ANGRY:
+ case MSS_IDLE:
+ case MSS_WALK:
+ case MSS_LOOT:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+/*==========================================
+ * Determination for an attack of a monster
+ *------------------------------------------
+ */
+int mob_target(struct mob_data *md,struct block_list *bl,int dist)
+{
+ nullpo_retr(0, md);
+ nullpo_retr(0, bl);
+
+ // Nothing will be carried out if there is no mind of changing TAGE by TAGE ending.
+ if((md->target_id > 0 && md->state.targettype == ATTACKABLE) && !mob_can_changetarget(md, bl, status_get_mode(&md->bl)) &&
+ // if the monster was provoked ignore the above rule [celest]
+ !(md->state.provoke_flag && md->state.provoke_flag == bl->id))
+ return 0;
+
+ if(!status_check_skilluse(&md->bl, bl, 0, 0))
+ return 0;
+
+ md->target_id = bl->id; // Since there was no disturbance, it locks on to target.
+ if(bl->type == BL_PC || bl->type == BL_MOB)
+ md->state.targettype = ATTACKABLE;
+ else
+ md->state.targettype = NONE_ATTACKABLE;
+ if (md->state.provoke_flag)
+ md->state.provoke_flag = 0;
+ md->min_chase=dist+md->db->range2;
+ if(md->min_chase>26)
+ md->min_chase=26;
+ return 0;
+}
+
+/*==========================================
+ * The ?? routine of an active monster
+ *------------------------------------------
+ */
+static int mob_ai_sub_hard_activesearch(struct block_list *bl,va_list ap)
+{
+ struct mob_data *md;
+ struct block_list **target;
+ int dist;
+
+ nullpo_retr(0, bl);
+ nullpo_retr(0, ap);
+ md=va_arg(ap,struct mob_data *);
+ target= va_arg(ap,struct block_list**);
+
+ //If can't seek yet, not an enemy, or you can't attack it, skip.
+ if ((*target) == bl || battle_check_target(&md->bl,bl,BCT_ENEMY)<=0 || !status_check_skilluse(&md->bl, bl, 0, 0))
+ return 0;
+
+ switch (bl->type)
+ {
+ case BL_PC:
+ case BL_MOB:
+ if((dist=distance_bl(&md->bl, bl)) < md->db->range2
+ && (md->db->range > 6 || mob_can_reach(md,bl,dist+1, MSS_FOLLOW))
+ && ((*target) == NULL || !check_distance_bl(&md->bl, *target, dist)) //New target closer than previous one.
+ ) {
+ (*target) = bl;
+ md->target_id=bl->id;
+ md->state.targettype = ATTACKABLE;
+ md->state.aggressive = (status_get_mode(&md->bl)&MD_ANGRY)?1:0;
+ md->min_chase= md->db->range3;
+ return 1;
+ }
+ break;
+ }
+ return 0;
+}
+
+/*==========================================
+ * chase target-change routine.
+ *------------------------------------------
+ */
+static int mob_ai_sub_hard_changechase(struct block_list *bl,va_list ap)
+{
+ struct mob_data *md;
+ struct block_list **target;
+
+ nullpo_retr(0, bl);
+ nullpo_retr(0, ap);
+ md=va_arg(ap,struct mob_data *);
+ target= va_arg(ap,struct block_list**);
+
+ //If can't seek yet, not an enemy, or you can't attack it, skip.
+ if ((*target) == bl || battle_check_target(&md->bl,bl,BCT_ENEMY)<=0 || !status_check_skilluse(&md->bl, bl, 0, 0))
+ return 0;
+
+ switch (bl->type)
+ {
+ case BL_PC:
+ case BL_MOB:
+ if(check_distance_bl(&md->bl, bl, md->db->range) &&
+ battle_check_range (&md->bl, bl, md->db->range)
+ ) {
+ (*target) = bl;
+ md->target_id=bl->id;
+ md->state.targettype = ATTACKABLE;
+ md->state.aggressive = (status_get_mode(&md->bl)&MD_ANGRY)?1:0;
+ md->min_chase= md->db->range3;
+ return 1;
+ }
+ break;
+ }
+ return 0;
+}
+
+
+/*==========================================
+ * loot monster item search
+ *------------------------------------------
+ */
+static int mob_ai_sub_hard_lootsearch(struct block_list *bl,va_list ap)
+{
+ struct mob_data* md;
+ int dist,*itc;
+
+ nullpo_retr(0, bl);
+ nullpo_retr(0, ap);
+ nullpo_retr(0, md=va_arg(ap,struct mob_data *));
+ nullpo_retr(0, itc=va_arg(ap,int *));
+
+ if(!md->lootitem || (battle_config.monster_loot_type == 1 && md->lootitem_count >= LOOTITEM_SIZE))
+ return 0;
+
+ if((dist=distance_bl(&md->bl, bl)) < md->db->range2 &&
+ mob_can_reach(md,bl,dist, MSS_LOOT) && rand()%1000<1000/(++(*itc)))
+ { // It is made a probability, such as within the limits PC.
+ md->target_id=bl->id;
+ md->state.targettype = NONE_ATTACKABLE;
+ md->min_chase=md->db->range3;
+ md->next_walktime = gettick() + 500; //So that the mob may go after the item inmediately.
+ }
+ return 0;
+}
+
+/*==========================================
+ * Processing of slave monsters
+ *------------------------------------------
+ */
+static int mob_ai_sub_hard_slavemob(struct mob_data *md,unsigned int tick)
+{
+ struct block_list *bl;
+ int old_dist;
+
+ nullpo_retr(0, md);
+
+ bl=map_id2bl(md->master_id);
+
+ if (!bl || status_isdead(bl)) { //主が死亡しているか見つからない
+ if(md->special_state.ai>0)
+ mob_timer_delete(0, 0, md->bl.id, 0);
+ else
+ mob_damage(NULL,md,md->hp,0);
+ return 0;
+ }
+
+ if(status_get_mode(&md->bl)&MD_CANMOVE)
+ { //If the mob can move, follow around. [Check by Skotlex]
+
+ if(bl->m != md->bl.m || md->master_dist > 30)
+ { // Since it is not in the same map (or is way to far), just warp it
+ mob_warp(md,bl->m,bl->x,bl->y,3);
+ return 0;
+ }
+
+ // Distance with between slave and master is measured.
+ old_dist=md->master_dist;
+ md->master_dist=distance_bl(&md->bl, bl);
+
+ // Since the master was in near immediately before, teleport is carried out and it pursues.
+ if(old_dist<10 && md->master_dist>18){
+ mob_warp(md,-1,bl->x,bl->y,3);
+ return 0;
+ }
+
+ // Although there is the master, since it is somewhat far, it approaches.
+ if((!md->target_id || md->state.targettype == NONE_ATTACKABLE) && mob_can_move(md) &&
+ md->master_dist<md->db->range3 && (md->walkpath.path_pos>=md->walkpath.path_len || md->walkpath.path_len==0)){
+ int i=0,dx,dy,ret;
+ if(md->master_dist>AREA_SIZE/2 && DIFF_TICK(md->next_walktime,tick)<3000) { //Allow it to cut down the walk time to chase back. [Skotlex]
+ do {
+ if(i<=5){
+ dx=bl->x - md->bl.x;
+ dy=bl->y - md->bl.y;
+ if(dx<0) dx+=(rand()%-dx)/2; //On the minimum, half the distance between slave/master. [Skotlex]
+ else if(dx>0) dx-=(rand()%dx)/2;
+ if(dy<0) dy+=(rand()%-dy)/2;
+ else if(dy>0) dy-=(rand()%dy)/2;
+ }else{
+ dx=bl->x - md->bl.x + rand()%11- 5;
+ dy=bl->y - md->bl.y + rand()%11- 5;
+ }
+
+ ret=mob_walktoxy(md,md->bl.x+dx,md->bl.y+dy,0);
+ i++;
+ } while(ret && i<10);
+ md->next_walktime=tick+1000;
+ }
+ }
+ } else if (bl->m != md->bl.m && map_flag_gvg(md->bl.m)) {
+ //Delete the summoned mob if it's in a gvg ground and the master is elsewhere. [Skotlex]
+ if(md->special_state.ai>0)
+ mob_timer_delete(0, 0, md->bl.id, 0);
+ else
+ mob_damage(NULL,md,md->hp,0);
+ return 0;
+ }
+
+ //Avoid attempting to lock the master's target too often to avoid unnecessary overload. [Skotlex]
+ if (DIFF_TICK(md->last_linktime, tick) < MIN_MOBLINKTIME && (!md->target_id || md->state.targettype == NONE_ATTACKABLE)) {
+ md->last_linktime = tick;
+ switch (bl->type) {
+ case BL_MOB:
+ {
+ struct mob_data *mmd= (struct mob_data*)bl;
+ struct block_list *tbl;
+ if(mmd->target_id>0 && mmd->state.targettype == ATTACKABLE &&
+ (tbl=map_id2bl(mmd->target_id)) && status_check_skilluse(&md->bl, tbl, 0, 0)
+ ) {
+ md->target_id=tbl->id;
+ md->state.targettype = ATTACKABLE;
+ md->state.aggressive = (status_get_mode(&md->bl)&MD_ANGRY)?1:0;
+ md->min_chase=md->db->range2+distance_bl(&md->bl, tbl);
+ }
+ }
+ break;
+ case BL_PC:
+ {
+ struct map_session_data *msd = (struct map_session_data*)bl;
+ struct block_list *tbl = NULL;
+ if(msd->attacktarget)
+ tbl = map_id2bl(msd->attacktarget);
+ else if (msd->skilltarget)
+ tbl = map_id2bl(msd->skilltarget);
+ if(tbl && status_check_skilluse(&md->bl, tbl, 0, 0)) {
+ md->target_id=tbl->id;
+ md->state.targettype = ATTACKABLE;
+ md->state.aggressive = (status_get_mode(&md->bl)&MD_ANGRY)?1:0;
+ md->min_chase=md->db->range2+distance_bl(&md->bl, tbl);
+ }
+ }
+ break;
+ }
+ }
+ return 0;
+}
+
+/*==========================================
+ * A lock of target is stopped and mob moves to a standby state.
+ *------------------------------------------
+ */
+int mob_unlocktarget(struct mob_data *md,int tick)
+{
+ nullpo_retr(0, md);
+
+ md->target_id=0;
+ md->state.targettype = NONE_ATTACKABLE;
+ md->state.skillstate=MSS_IDLE;
+ md->next_walktime=tick+rand()%3000+3000;
+ return 0;
+}
+/*==========================================
+ * Random walk
+ *------------------------------------------
+ */
+int mob_randomwalk(struct mob_data *md,int tick)
+{
+ const int retrycount=20;
+ int speed;
+
+ nullpo_retr(0, md);
+
+ speed=status_get_speed(&md->bl);
+ if(DIFF_TICK(md->next_walktime,tick)<0){
+ int i,x,y,c,d=12-md->move_fail_count;
+ int mask[8][2] = {{0,1},{-1,1},{-1,0},{-1,-1},{0,-1},{1,-1},{1,0},{1,1}};
+ if(d<5) d=5;
+ for(i=0;i<retrycount;i++){ // Search of a movable place
+ int r=rand();
+ x=r%(d*2+1)-d;
+ y=r/(d*2+1)%(d*2+1)-d;
+ if (md->target_dir){
+ if (x<0) x=0-x;
+ if (y<0) y=0-y;
+ x *= mask[md->target_dir-1][0];
+ y *= mask[md->target_dir-1][1];
+ }
+ x+=md->bl.x;
+ y+=md->bl.y;
+
+ if((map_getcell(md->bl.m,x,y,CELL_CHKPASS)) && mob_walktoxy(md,x,y,1)==0){
+ md->move_fail_count=0;
+ break;
+ }
+ if(i+1>=retrycount){
+ md->move_fail_count++;
+ md->target_dir = 0;
+ if(md->move_fail_count>1000){
+ if(battle_config.error_log)
+ ShowWarning("MOB cant move. random spawn %d, class = %d\n",md->bl.id,md->class_);
+ md->move_fail_count=0;
+ mob_spawn(md->bl.id);
+ }
+ }
+ }
+ for(i=c=0;i<md->walkpath.path_len;i++){ // The next walk start time is calculated.
+ if(md->walkpath.path[i]&1)
+ c+=speed*14/10;
+ else
+ c+=speed;
+ }
+ md->next_walktime = tick+rand()%3000+3000+c;
+ md->state.skillstate=MSS_WALK;
+ return 1;
+ }
+ return 0;
+}
+
+/*==========================================
+ * AI of MOB whose is near a Player
+ *------------------------------------------
+ */
+static int mob_ai_sub_hard(struct block_list *bl,va_list ap)
+{
+ struct mob_data *md;
+ struct block_list *tbl = NULL, *abl = NULL;
+ unsigned int tick;
+ int i, dx, dy, dist;
+ int attack_type = 0;
+ int mode;
+ int search_size = AREA_SIZE*2;
+ int blind_flag = 0;
+
+ nullpo_retr(0, bl);
+ nullpo_retr(0, ap);
+
+ md = (struct mob_data*)bl;
+ tick = va_arg(ap, unsigned int);
+
+ if(md->bl.prev == NULL || md->state.state == MS_DEAD)
+ return 1;
+
+ if (DIFF_TICK(tick, md->last_thinktime) < MIN_MOBTHINKTIME)
+ return 0;
+ md->last_thinktime = tick;
+
+ if (md->skilltimer != -1){ // Casting skill, or has died
+ if (DIFF_TICK (tick, md->next_walktime) > MIN_MOBTHINKTIME)
+ md->next_walktime = tick;
+ return 0;
+ }
+
+ // Abnormalities
+ if((md->opt1 > 0 && md->opt1 != OPT1_STONEWAIT) || md->state.state == MS_DELAY || md->sc_data[SC_BLADESTOP].timer != -1)
+ return 0;
+
+ if (md->sc_data && md->sc_data[SC_BLIND].timer != -1)
+ blind_flag = 1;
+
+ mode = status_get_mode(&md->bl);
+
+ if (md->target_id)
+ { //Check validity of current target. [Skotlex]
+ tbl = map_id2bl(md->target_id);
+ if (!tbl || tbl->m != md->bl.m || !status_check_skilluse(&md->bl, tbl, 0, 0))
+ { //Unlock current target.
+ if (md->state.state == MS_WALK && (battle_config.mob_ai&8 || !tbl)) //Inmediately stop chasing.
+ mob_stop_walking(md, 2);
+ mob_unlocktarget(md, tick-(battle_config.mob_ai&8?3000:0)); //Imediately do random walk.
+ tbl = NULL;
+ }
+ }
+
+ // Check for target change.
+ if (md->attacked_id && mode&MD_CANATTACK && md->attacked_id != md->target_id)
+ {
+ abl = map_id2bl(md->attacked_id);
+ if (abl && (!tbl || mob_can_changetarget(md, abl, mode))) {
+ if (md->bl.m != abl->m || abl->prev == NULL ||
+ (dist = distance_bl(&md->bl, abl)) >= 32 ||
+ battle_check_target(bl, abl, BCT_ENEMY) <= 0 ||
+ (battle_config.mob_ai&2 && !status_check_skilluse(bl, abl, 0, 0)) ||
+ !mob_can_reach(md, abl, dist+2, MSS_RUSH)) //Some more cells of grace...
+ { //Can't attack back
+ if (md->attacked_count++ > 3) {
+ if (mobskill_use(md, tick, MSC_RUDEATTACKED) == 0 &&
+ mode&MD_CANMOVE && mob_can_move(md))
+ {
+ int dist = rand() % 10 + 1;//後退する距離
+ int dir = map_calc_dir(abl, bl->x, bl->y);
+ int mask[8][2] = {{0,1},{-1,1},{-1,0},{-1,-1},{0,-1},{1,-1},{1,0},{1,1}};
+ mob_walktoxy(md, md->bl.x + dist * mask[dir][0], md->bl.y + dist * mask[dir][1], 0);
+ md->next_walktime = tick + 500;
+ }
+ }
+ } else if (!(battle_config.mob_ai&2) && !status_check_skilluse(bl, abl, 0, 0)) {
+ //Can't attack back, but didn't invoke a rude attacked skill...
+ md->attacked_id = 0; //Simply unlock, shouldn't attempt to run away when in dumb_ai mode.
+ } else if (blind_flag && dist > 2 && DIFF_TICK(tick,md->next_walktime) < 0) { //Blinded, but can reach
+ if (!md->target_id)
+ { //Attempt to follow new target
+ if (mode&MD_CANMOVE && mob_can_move(md)) { // why is it moving to the target when the mob can't see the player? o.o
+ dx = abl->x - md->bl.x;
+ dy = abl->y - md->bl.y;
+ md->next_walktime = tick + 1000;
+ mob_walktoxy(md, md->bl.x+dx, md->bl.y+dy, 0);
+ }
+ }
+ } else { //Attackable
+ if (!tbl || dist < md->db->range || !check_distance_bl(&md->bl, tbl, dist)
+ || battle_gettarget(tbl) != md->bl.id)
+ { //Change if the new target is closer than the actual one
+ //or if the previous target is not attacking the mob. [Skotlex]
+ md->target_id = md->attacked_id; // set target
+ md->state.targettype = ATTACKABLE;
+ md->state.aggressive = 0; //Retaliating.
+ attack_type = 1;
+ md->attacked_count = 0;
+ md->min_chase = dist + md->db->range2;
+ if (md->min_chase > 26)
+ md->min_chase = 26;
+ tbl = abl; //Set the new target
+ }
+ }
+ }
+ }
+ if (md->attacked_id) {
+ if (md->state.aggressive && md->attacked_id == md->target_id)
+ md->state.aggressive = 0; //No longer aggressive, change to retaliate AI.
+ md->attacked_id = 0; //Clear it since it's been checked for already.
+ }
+
+ // Processing of slave monster, is it needed when there's a target to deal with?
+ if (md->master_id > 0 && !tbl)
+ mob_ai_sub_hard_slavemob(md, tick);
+
+ // Scan area for targets
+ if ((mode&MD_AGGRESSIVE && battle_config.monster_active_enable && !tbl) ||
+ (mode&MD_ANGRY && md->state.skillstate == MSS_FOLLOW)
+ ) {
+ search_size = (blind_flag) ? 3 : md->db->range2;
+ map_foreachinarea (mob_ai_sub_hard_activesearch, md->bl.m,
+ md->bl.x-search_size,md->bl.y-search_size,
+ md->bl.x+search_size,md->bl.y+search_size,
+ md->special_state.ai?BL_CHAR:BL_PC,
+ md, &tbl);
+ } else if (mode&MD_CHANGECHASE && (md->state.skillstate == MSS_RUSH || md->state.skillstate == MSS_FOLLOW)) {
+ search_size = (blind_flag && md->db->range>3) ? 3 : md->db->range;
+ map_foreachinarea (mob_ai_sub_hard_changechase, md->bl.m,
+ md->bl.x-search_size,md->bl.y-search_size,
+ md->bl.x+search_size,md->bl.y+search_size,
+ md->special_state.ai?BL_CHAR:BL_PC,
+ md, &tbl);
+ }
+
+ // Scan area for items to loot, avoid trying to loot of the mob is full and can't consume the items.
+ if (!md->target_id && mode&MD_LOOTER && md->lootitem &&
+ (md->lootitem_count < LOOTITEM_SIZE || battle_config.monster_loot_type != 1))
+ {
+ i = 0;
+ search_size = (blind_flag) ? 3 : md->db->range2;
+ map_foreachinarea (mob_ai_sub_hard_lootsearch, md->bl.m,
+ md->bl.x-search_size, md->bl.y-search_size,
+ md->bl.x+search_size, md->bl.y+search_size,
+ BL_ITEM, md, &i);
+ }
+
+ if (tbl)
+ { //Target exists, attack or loot as applicable.
+ if (tbl->type != BL_ITEM)
+ { //Attempt to attack.
+ //At this point we know the target is attackable, we just gotta check if the range matches.
+ if (blind_flag && DIFF_TICK(tick,md->next_walktime) < 0 && !check_distance_bl(&md->bl, tbl, 1))
+ { //Run towards the enemy when out of range?
+ md->target_id = 0;
+ md->state.targettype = NONE_ATTACKABLE;
+ if (!(mode & MD_CANMOVE) || !mob_can_move(md))
+ return 0;
+ dx = tbl->x - md->bl.x;
+ dy = tbl->y - md->bl.y;
+ md->next_walktime = tick + 1000;
+ mob_walktoxy(md, md->bl.x+dx, md->bl.y+dy, 0);
+ return 0;
+ }
+ if (!battle_check_range (&md->bl, tbl, md->db->range))
+ { //Out of range...
+ if (!(mode & MD_CANMOVE))
+ { //Can't chase.
+ mob_unlocktarget(md,tick);
+ return 0;
+ }
+ if (!mob_can_move(md)) //Wait until you can move?
+ return 0;
+ //Follow up
+ md->state.skillstate = md->state.aggressive?MSS_FOLLOW:MSS_RUSH;
+ mobskill_use (md, tick, -1);
+ if (md->timer != -1 && md->state.state != MS_ATTACK &&
+ (DIFF_TICK (md->next_walktime, tick) < 0 ||
+ !(battle_config.mob_ai&1) ||
+ check_distance_blxy(tbl, md->to_x, md->to_y, md->db->range)) //Current target tile is still within attack range.
+ ) {
+ return 0; //No need to follow, already doing it?
+ }
+ search_size = (blind_flag) ? 3 : ((md->min_chase > md->db->range2) ? md->min_chase : md->db->range2);
+ if (!mob_can_reach(md, tbl, search_size, MSS_RUSH))
+ { //Can't reach
+ mob_unlocktarget(md,tick);
+ return 0;
+ }
+ //Target reachable. Locate suitable spot to move to.
+ i = 0;
+ dx = tbl->x - md->bl.x;
+ dy = tbl->y - md->bl.y;
+ if (dx < 0) dx++;
+ else if (dx > 0) dx--;
+ if (dy < 0) dy++;
+ else if (dy > 0) dy--;
+ while (i < 5 && mob_walktoxy(md, md->bl.x + dx, md->bl.y + dy, 0))
+ { //Attempt to chase to nearby blocks
+ dx = tbl->x - md->bl.x + rand()%3 - 1;
+ dy = tbl->y - md->bl.y + rand()%3 - 1;
+ i++;
+ }
+ if (i==5)
+ { //Failed? Try going away from the target before retrying.
+ if (dx < 0) dx = 2;
+ else if (dx > 0) dx = -2;
+ if (dy < 0) dy = 2;
+ else if (dy > 0) dy = -2;
+ }
+ md->next_walktime = tick + 500;
+ mob_walktoxy (md, md->bl.x+dx, md->bl.y+dy, 0);
+ return 0;
+ }
+ //Target within range, engage
+ md->state.skillstate = md->state.aggressive?MSS_ANGRY:MSS_BERSERK;
+ if (md->state.state == MS_WALK)
+ mob_stop_walking (md, 1);
+ else if (md->state.state == MS_ATTACK)
+ return 0; //Ah, we are already attacking.
+ mob_changestate(md, MS_ATTACK, attack_type);
+ return 0;
+ } else { //Target is BL_ITEM, attempt loot.
+ struct flooritem_data *fitem;
+
+ if ((dist = distance_bl(&md->bl, tbl)) >= md->min_chase || (blind_flag && dist >= 4) || md->lootitem == NULL)
+ { //Can't loot...
+ mob_unlocktarget (md, tick);
+ if (md->state.state == MS_WALK)
+ mob_stop_walking(md,0);
+ return 0;
+ }
+ if (dist)
+ { //Still not within loot range.
+ if (!(mode & MD_CANMOVE))
+ { //A looter that can't move? Real smart.
+ mob_unlocktarget(md,tick);
+ return 0;
+ }
+ if (!mob_can_move(md)) // 動けない状態にある
+ return 0;
+ md->state.skillstate = MSS_LOOT; // ルート時スキル使用
+ mobskill_use(md, tick, -1);
+ if (md->timer != -1 && md->state.state != MS_ATTACK &&
+ (DIFF_TICK(md->next_walktime,tick) < 0 ||
+ check_distance_blxy(tbl, md->to_x, md->to_y, 0)))
+ { //Already on the way to looting.
+ return 0;
+ }
+ md->next_walktime = tick + 500;
+ dx = tbl->x - md->bl.x;
+ dy = tbl->y - md->bl.y;
+ if (mob_walktoxy(md, md->bl.x+dx, md->bl.y+dy, 0))
+ mob_unlocktarget(md, tick); //Can't loot...
+ return 0;
+ }
+ //Within looting range.
+ if (md->state.state == MS_ATTACK)
+ return 0; //Busy attacking?
+ if (md->state.state == MS_WALK)
+ mob_stop_walking(md,0);
+
+ fitem = (struct flooritem_data *)tbl;
+ if (md->lootitem_count < LOOTITEM_SIZE) {
+ memcpy (&md->lootitem[md->lootitem_count++], &fitem->item_data, sizeof(md->lootitem[0]));
+ if(log_config.pick > 0) //Logs items, taken by (L)ooter Mobs [Lupus]
+ log_pick((struct map_session_data*)md, "L", md->class_, md->lootitem[md->lootitem_count-1].nameid, md->lootitem[md->lootitem_count-1].amount, &md->lootitem[md->lootitem_count-1]);
+ } else if (battle_config.monster_loot_type == 1) { //Can't loot, stuffed!
+ mob_unlocktarget(md,tick);
+ return 0;
+ } else { //Destroy first looted item...
+ if (md->lootitem[0].card[0] == (short)0xff00)
+ intif_delete_petdata( MakeDWord(md->lootitem[0].card[1],md->lootitem[0].card[2]) );
+ for (i = 0; i < LOOTITEM_SIZE - 1; i++)
+ memcpy (&md->lootitem[i], &md->lootitem[i+1], sizeof(md->lootitem[0]));
+ memcpy (&md->lootitem[LOOTITEM_SIZE-1], &fitem->item_data, sizeof(md->lootitem[0]));
+ }
+ //Clear item.
+ map_clearflooritem (tbl->id);
+ mob_unlocktarget (md,tick);
+ return 0;
+ }
+ }
+
+ // When there's no target, it is idling.
+ if (mobskill_use(md, tick, -1))
+ return 0;
+
+ // Nothing else to do... except random walking.
+ if (mode&MD_CANMOVE && mob_can_move(md))
+ {
+ if (DIFF_TICK(md->next_walktime, tick) > 7000 &&
+ (md->walkpath.path_len == 0 || md->walkpath.path_pos >= md->walkpath.path_len))
+ md->next_walktime = tick + 3000 * rand() % 2000;
+ // Random movement
+ if (mob_randomwalk(md,tick))
+ return 0;
+ }
+
+ // Since he has finished walking, it stands by.
+ if (md->walkpath.path_len == 0 || md->walkpath.path_pos >= md->walkpath.path_len)
+ md->state.skillstate = MSS_IDLE;
+ return 0;
+}
+
+/*==========================================
+ * Serious processing for mob in PC field of view (foreachclient)
+ *------------------------------------------
+ */
+static int mob_ai_sub_foreachclient(struct map_session_data *sd,va_list ap)
+{
+ unsigned int tick;
+ nullpo_retr(0, sd);
+ nullpo_retr(0, ap);
+
+ tick=va_arg(ap,unsigned int);
+ map_foreachinarea(mob_ai_sub_hard,sd->bl.m,
+ sd->bl.x-AREA_SIZE*2,sd->bl.y-AREA_SIZE*2,
+ sd->bl.x+AREA_SIZE*2,sd->bl.y+AREA_SIZE*2,
+ BL_MOB,tick);
+
+ return 0;
+}
+
+/*==========================================
+ * Serious processing for mob in PC field of view (interval timer function)
+ *------------------------------------------
+ */
+static int mob_ai_hard(int tid,unsigned int tick,int id,int data)
+{
+ clif_foreachclient(mob_ai_sub_foreachclient,tick);
+
+ return 0;
+}
+
+/*==========================================
+ * Negligent mode MOB AI (PC is not in near)
+ *------------------------------------------
+ */
+static int mob_ai_sub_lazy(DBKey key,void * data,va_list app)
+{
+ struct mob_data *md = (struct mob_data *)data;
+ va_list ap;
+ unsigned int tick;
+ int mode;
+
+ nullpo_retr(0, md);
+ nullpo_retr(0, app);
+
+ if(md->bl.type!=BL_MOB)
+ return 0;
+
+ ap = va_arg(app, va_list);
+ tick=va_arg(ap,unsigned int);
+
+ if(DIFF_TICK(tick,md->last_thinktime)<MIN_MOBTHINKTIME*10)
+ return 0;
+ md->last_thinktime=tick;
+
+ if (md->bl.prev==NULL || md->state.state == MS_DEAD)
+ return 1;
+
+ if(md->skilltimer!=-1){
+ if(DIFF_TICK(tick,md->next_walktime)>MIN_MOBTHINKTIME*10)
+ md->next_walktime=tick;
+ return 0;
+ }
+
+ // 取り巻きモンスターの処理(呼び戻しされた時)
+ if (md->master_id > 0) {
+ mob_ai_sub_hard_slavemob (md,tick);
+ return 0;
+ }
+
+ mode = status_get_mode(&md->bl);
+ if(DIFF_TICK(md->next_walktime,tick)<0 &&
+ (mode&MD_CANMOVE) && mob_can_move(md) ){
+
+ if( map[md->bl.m].users>0 ){
+ // Since PC is in the same map, somewhat better negligent processing is carried out.
+
+ // It sometimes moves.
+ if(rand()%1000<MOB_LAZYMOVEPERC)
+ mob_randomwalk(md,tick);
+ // MOB which is not not the summons MOB but BOSS, either sometimes reboils.
+ else if( rand()%1000<MOB_LAZYWARPPERC && md->x0<=0 && md->master_id!=0 &&
+ !(mode&MD_BOSS))
+ mob_spawn(md->bl.id);
+ else if(rand()%1000<MOB_LAZYSKILLPERC) //Chance to do a mob's idle skill.
+ mobskill_use(md, tick, -1);
+ }else{
+ // Since PC is not even in the same map, suitable processing is carried out even if it takes.
+
+ // MOB which is not BOSS which is not Summons MOB, either -- a case -- sometimes -- leaping
+ if( rand()%1000<MOB_LAZYWARPPERC && md->x0<=0 && md->master_id!=0 &&
+ !(mode&MD_BOSS))
+ mob_warp(md,-1,-1,-1,-1);
+ }
+
+ md->next_walktime = tick+rand()%10000+5000;
+ }
+ return 0;
+}
+
+/*==========================================
+ * Negligent processing for mob outside PC field of view (interval timer function)
+ *------------------------------------------
+ */
+static int mob_ai_lazy(int tid,unsigned int tick,int id,int data)
+{
+ map_foreachiddb(mob_ai_sub_lazy,tick);
+
+ return 0;
+}
+
+/*==========================================
+ * The structure object for item drop with delay
+ * Since it is only two being able to pass [ int ] a timer function
+ * Data is put in and passed to this structure object.
+ *------------------------------------------
+ */
+struct delay_item_drop {
+ int m,x,y;
+ struct item item_data;
+ struct map_session_data *first_sd,*second_sd,*third_sd;
+};
+
+/*==========================================
+ * Initializes the delay drop structure for mob-dropped items.
+ *------------------------------------------
+ */
+static struct delay_item_drop* mob_setdropitem(int nameid, int qty, int m, int x, int y,
+ struct map_session_data* first_sd, struct map_session_data* second_sd, struct map_session_data* third_sd)
+{
+ struct delay_item_drop *drop = aCalloc(1, sizeof (struct delay_item_drop));
+ drop->item_data.nameid = nameid;
+ drop->item_data.amount = qty;
+ drop->item_data.identify = !itemdb_isequip3(nameid);
+ drop->m = m;
+ drop->x = x;
+ drop->y = y;
+ drop->first_sd = first_sd;
+ drop->second_sd = second_sd;
+ drop->third_sd = third_sd;
+ return drop;
+};
+
+/*==========================================
+ * Initializes the delay drop structure for mob-looted items.
+ *------------------------------------------
+ */
+static struct delay_item_drop* mob_setlootitem(struct item* item, int m, int x, int y,
+ struct map_session_data* first_sd, struct map_session_data* second_sd, struct map_session_data* third_sd)
+{
+ struct delay_item_drop *drop = aCalloc(1, sizeof (struct delay_item_drop));
+ memcpy(&drop->item_data, item, sizeof(struct item));
+ drop->m = m;
+ drop->x = x;
+ drop->y = y;
+ drop->first_sd = first_sd;
+ drop->second_sd = second_sd;
+ drop->third_sd = third_sd;
+ return drop;
+};
+
+/*==========================================
+ * item drop with delay (timer function)
+ *------------------------------------------
+ */
+static int mob_delay_item_drop(int tid,unsigned int tick,int id,int data)
+{
+ struct delay_item_drop *ditem;
+ ditem=(struct delay_item_drop *)id;
+
+ map_addflooritem(&ditem->item_data,1,ditem->m,ditem->x,ditem->y,ditem->first_sd,ditem->second_sd,ditem->third_sd,0);
+ aFree(ditem);
+ return 0;
+}
+
+/*==========================================
+ * Sets a timer to drop an item on the ground
+ * Also performs logging and autoloot if enabled.
+ * rate is the drop-rate of the item, required for autoloot.
+ *------------------------------------------
+ * by [Skotlex]
+ */
+static void mob_item_drop(struct mob_data *md, unsigned int tick, struct delay_item_drop * ditem, int loot, int drop_rate)
+{
+ if(log_config.pick > 0)
+ { //Logs items, dropped by mobs [Lupus]
+ if (loot)
+ log_pick((struct map_session_data*)md, "L", md->class_, ditem->item_data.nameid, -ditem->item_data.amount, &ditem->item_data);
+ else
+ log_pick((struct map_session_data*)md, "M", md->class_, ditem->item_data.nameid, -ditem->item_data.amount, NULL);
+ }
+
+ if (ditem->first_sd && ditem->first_sd->state.autoloot &&
+ (drop_rate <= ditem->first_sd->state.autoloot ||
+ ditem->first_sd->state.autoloot >= 10000) //Fetch 100% drops
+ && pc_additem(ditem->first_sd,&ditem->item_data,ditem->item_data.amount) == 0)
+ { //Autolooted.
+ if(log_config.pick > 0)
+ log_pick(ditem->first_sd, "P", 0, ditem->item_data.nameid, ditem->item_data.amount, &ditem->item_data);
+ aFree(ditem);
+ } else
+ add_timer(tick, mob_delay_item_drop, (int)ditem, 0);
+}
+
+/*==========================================
+ * mob data is erased.
+ *------------------------------------------
+ */
+void mob_unload(struct mob_data *md)
+{
+ nullpo_retv(md);
+ mob_remove_map(md, 0);
+ map_deliddb(&md->bl);
+ map_freeblock((struct block_list*)md);
+}
+
+int mob_remove_map(struct mob_data *md, int type)
+{
+ nullpo_retr(1, md);
+
+ if(md->bl.prev == NULL)
+ return 1;
+ mob_changestate(md,MS_DEAD,0);
+ clif_clearchar_area(&md->bl,type);
+ map_delblock(&md->bl);
+ if (md->lootitem){
+ aFree(md->lootitem);
+ md->lootitem = NULL;
+ }
+ if (md->guardian_data)
+ {
+ aFree(md->guardian_data);
+ md->guardian_data = NULL;
+ }
+ return 0;
+}
+int mob_delete(struct mob_data *md)
+{
+ nullpo_retr(1, md);
+
+ mob_remove_map(md, 1);
+ if(pcdb_checkid(mob_get_viewclass(md->class_))) //Player mobs are not removed automatically by the client.
+ clif_clearchar_delay(gettick()+3000,&md->bl,0);
+ if(mob_is_clone(md->class_))
+ mob_clone_delete(md->class_);
+ mob_deleteslave(md);
+ mob_setdelayspawn(md->bl.id);
+ return 0;
+}
+int mob_timer_delete(int tid, unsigned int tick, int id, int data)
+{
+ struct mob_data *md=(struct mob_data *)map_id2bl(id);
+ nullpo_retr(0, md);
+
+//for Alchemist CANNIBALIZE [Lupus]
+ mob_remove_map(md, 3);
+ mob_setdelayspawn(md->bl.id);
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int mob_deleteslave_sub(struct block_list *bl,va_list ap)
+{
+ struct mob_data *md;
+ int id;
+
+ nullpo_retr(0, bl);
+ nullpo_retr(0, ap);
+ nullpo_retr(0, md = (struct mob_data *)bl);
+
+ id=va_arg(ap,int);
+ if(md->master_id > 0 && md->master_id == id )
+ mob_damage(NULL,md,md->hp,1);
+ return 0;
+}
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int mob_deleteslave(struct mob_data *md)
+{
+ nullpo_retr(0, md);
+
+ map_foreachinmap(mob_deleteslave_sub, md->bl.m, BL_MOB,md->bl.id);
+ return 0;
+}
+
+/*==========================================
+ * It is the damage of sd to damage to md.
+ *------------------------------------------
+ */
+int mob_damage(struct block_list *src,struct mob_data *md,int damage,int type)
+{
+ int i,count,minpos,mindmg;
+ struct map_session_data *sd = NULL,*tmpsd[DAMAGELOG_SIZE];
+ struct {
+ struct party *p;
+ int id,base_exp,job_exp,zeny;
+ } pt[DAMAGELOG_SIZE];
+ int pnum=0;
+ int mvp_damage,max_hp;
+ unsigned int tick = gettick();
+ struct map_session_data *mvp_sd = NULL, *second_sd = NULL,*third_sd = NULL;
+ struct block_list *master = NULL;
+ double temp;
+ struct item item;
+ int ret, mode;
+ int drop_rate;
+ int base_drop_delay;
+ int race;
+
+ nullpo_retr(0, md); //srcはNULLで呼ばれる場合もあるので、他でチェック
+
+ max_hp = status_get_max_hp(&md->bl);
+ race = status_get_race(&md->bl);
+
+ if(src && src->type == BL_PC) {
+ sd = (struct map_session_data *)src;
+ mvp_sd = sd;
+ }
+
+ if(md->bl.prev==NULL){
+ if(battle_config.error_log==1)
+ ShowError("mob_damage : BlockError!!\n");
+ return 0;
+ }
+
+ if(md->state.state==MS_DEAD || md->hp<=0) {
+ if(md->bl.prev != NULL) {
+ mob_changestate(md,MS_DEAD,0);
+ mobskill_use(md,tick,-1); // It is skill at the time of death.
+ clif_clearchar_area(&md->bl,1);
+ map_delblock(&md->bl);
+ mob_setdelayspawn(md->bl.id);
+ }
+ return 0;
+ }
+
+/* The stop walking code is triggered in battle_walkdelay which is invoked from clif_damage after a timer.
+ * So the mob should stop walking in sync with the time the "attack" hits the mob. If this is bugged then the
+ * fault must be looked at in battle_walkdelay, not here. [Skotlex]
+ if(md->sc_data[SC_ENDURE].timer == -1) // Stop the walking [Lance]
+ mob_stop_walking(md,1);
+*/
+ if(damage > max_hp>>2)
+ skill_stop_dancing(&md->bl);
+
+ if(md->hp > max_hp)
+ md->hp = max_hp;
+
+ // The amount of overkill rounds to hp.
+ if(damage>md->hp)
+ damage=md->hp;
+ md->hp-=damage;
+ md->tdmg+=damage; //Store total damage...
+
+ if(!(type&2)) {
+ int id = 0;
+ if (src) {
+ switch (src->type) {
+ case BL_PC:
+ id = sd->status.char_id;
+ if(md->attacked_id <= 0)
+ md->attacked_id = sd->bl.id;
+ break;
+ case BL_PET:
+ {
+ struct pet_data *pd = (struct pet_data*)src;
+ if (battle_config.pet_attack_exp_to_master) {
+ id = pd->msd->status.char_id;
+ damage=(damage*battle_config.pet_attack_exp_rate)/100; //Modify logged damage accordingly.
+ }
+ //Let mobs retaliate against the pet's master [Skotlex]
+ if(md->attacked_id <= 0)
+ md->attacked_id = pd->msd->bl.id;
+ break;
+ }
+ case BL_MOB:
+ {
+ struct mob_data* md2 = (struct mob_data*)src;
+ if(md2->special_state.ai && md2->master_id) {
+ struct map_session_data* msd = map_id2sd(md2->master_id);
+ if (msd) id = msd->status.char_id;
+ }
+ if(md->attacked_id <= 0)
+ { //Let players decide whether to retaliate versus the master or the mob. [Skotlex]
+ if (md2->master_id && battle_config.retaliate_to_master)
+ md->attacked_id = md2->master_id;
+ else
+ md->attacked_id = md2->bl.id;
+ }
+ break;
+ }
+ }
+ }
+ //Log damage...
+ if (id && damage > 0) {
+ for(i=0,minpos=DAMAGELOG_SIZE-1,mindmg=0x7fffffff;i<DAMAGELOG_SIZE;i++){
+ if(md->dmglog[i].id==id)
+ break;
+ if(md->dmglog[i].id==0) { //Store data in first empty slot.
+ md->dmglog[i].id = id;
+ break;
+ }
+ if(md->dmglog[i].dmg<mindmg){
+ minpos=i;
+ mindmg=md->dmglog[i].dmg;
+ }
+ }
+ if(i<DAMAGELOG_SIZE)
+ md->dmglog[i].dmg+=damage;
+ else {
+ md->dmglog[minpos].id=id;
+ md->dmglog[minpos].dmg=damage;
+ }
+ }
+ }
+
+ if(md->guardian_data && md->guardian_data->number < MAX_GUARDIANS) { // guardian hp update [Valaris] (updated by [Skotlex])
+ if ((md->guardian_data->castle->guardian[md->guardian_data->number].hp = md->hp) <= 0)
+ {
+ guild_castledatasave(md->guardian_data->castle->castle_id, 10+md->guardian_data->number,0);
+ guild_castledatasave(md->guardian_data->castle->castle_id, 18+md->guardian_data->number,0);
+ }
+ } // end addition
+
+ if(md->option&OPTION_HIDE)
+ status_change_end(&md->bl, SC_HIDING, -1);
+ if(md->option&OPTION_CLOAK)
+ status_change_end(&md->bl, SC_CLOAKING, -1);
+
+ if(md->special_state.ai == 2 && //スフィアーマイン
+ src && md->master_id == src->id)
+ {
+ md->state.alchemist = 1;
+ md->target_dir = map_calc_dir(src,md->bl.x,md->bl.y)+1;
+ mobskill_use(md, tick, MSC_ALCHEMIST);
+ }
+
+ if (battle_config.show_mob_hp)
+ clif_charnameack (0, &md->bl);
+
+ if(md->hp > 0)
+ return damage;
+
+ //Not the most correct way ever, but this is totally custom anyway.... [Skotlex]
+ if (md->sc_data[SC_KAIZEL].timer != -1) {
+ max_hp = status_get_max_hp(&md->bl);
+ mob_heal(md, 10*md->sc_data[SC_KAIZEL].val1*max_hp/100);
+ clif_resurrection(&md->bl, 1);
+ status_change_start(&md->bl,SkillStatusChangeTable[SL_KAIZEL],10,0,0,0,skill_get_time2(SL_KAIZEL, md->sc_data[SC_KAIZEL].val1),0);
+ status_change_end(&md->bl,SC_KAIZEL,-1);
+ return damage;
+ }
+
+ // ----- ここから死亡処理 -----
+
+ mode = status_get_mode(&md->bl); //Mode will be used for various checks regarding exp/drops.
+
+ //changestate will clear all status effects, so we need to know if RICHMANKIM is in effect before then. [Skotlex]
+ //I just recycled ret because it isn't used until much later and I didn't want to add a new variable for it.
+ ret = (md->sc_data[SC_RICHMANKIM].timer != -1)?(25 + 11*md->sc_data[SC_RICHMANKIM].val1):0;
+
+ map_freeblock_lock();
+ mob_changestate(md,MS_DEAD,0);
+ mobskill_use(md,tick,-1); // 死亡時スキル
+
+ memset(tmpsd,0,sizeof(tmpsd));
+ memset(pt,0,sizeof(pt));
+
+ max_hp = status_get_max_hp(&md->bl);
+
+ if(src && src->type == BL_MOB)
+ mob_unlocktarget((struct mob_data *)src,tick);
+
+ base_drop_delay = battle_config.delay_battle_damage?0:500;
+ if(sd) {
+ int sp = 0, hp = 0;
+ if (sd->state.attack_type == BF_MAGIC)
+ base_drop_delay = 500;
+ if (sd->state.attack_type == BF_MAGIC && sd->skilltarget == md->bl.id && (i=pc_checkskill(sd,HW_SOULDRAIN))>0)
+ { //Soul Drain should only work on targetted spells [Skotlex]
+ if (pc_issit(sd)) pc_setstand(sd); //Character stuck in attacking animation while 'sitting' fix. [Skotlex]
+ clif_skill_nodamage(src,&md->bl,HW_SOULDRAIN,i,1);
+ sp += (status_get_lv(&md->bl))*(65+15*i)/100;
+ }
+ sp += sd->sp_gain_value;
+ sp += sd->sp_gain_race[race];
+ sp += sd->sp_gain_race[mode&MD_BOSS?10:11];
+ hp += sd->hp_gain_value;
+ if (sp > 0) {
+ if(sd->status.sp + sp > sd->status.max_sp)
+ sp = sd->status.max_sp - sd->status.sp;
+ sd->status.sp += sp;
+ if (sp > 0 && battle_config.show_hp_sp_gain)
+ clif_heal(sd->fd,SP_SP,sp);
+ }
+ if (hp > 0) {
+ if(sd->status.hp + hp > sd->status.max_hp)
+ hp = sd->status.max_hp - sd->status.hp;
+ sd->status.hp += hp;
+ if (hp > 0 && battle_config.show_hp_sp_gain)
+ clif_heal(sd->fd,SP_HP,hp);
+ }
+ if (sd->mission_mobid == md->class_) { //TK_MISSION [Skotlex]
+ //Recycling hp for new random target id...
+ if (++sd->mission_count >= 100 && (hp = mob_get_random_id(0, 0, sd->status.base_level)))
+ {
+ pc_addfame(sd, 1);
+ sd->mission_mobid = hp;
+ pc_setglobalreg(sd,"TK_MISSION_ID", hp);
+ sd->mission_count = 0;
+ clif_mission_mob(sd, hp, 0);
+ }
+ pc_setglobalreg(sd,"TK_MISSION_COUNT", sd->mission_count);
+ }
+ }
+
+ // map外に消えた人は計算から除くので
+ // overkill分は無いけどsumはmax_hpとは違う
+
+ for(i=0,count=0,mvp_damage=0;i<DAMAGELOG_SIZE;i++){
+ if(md->dmglog[i].id==0)
+ break; //Reached end of log.
+ count++; //Count an attacker even if he is dead/logged-out.
+ tmpsd[i] = map_charid2sd(md->dmglog[i].id);
+ if(tmpsd[i] == NULL)
+ continue;
+ if(tmpsd[i]->bl.m != md->bl.m || pc_isdead(tmpsd[i]))
+ continue;
+
+ if(mvp_damage<md->dmglog[i].dmg){
+ third_sd = second_sd;
+ second_sd = mvp_sd;
+ mvp_sd=tmpsd[i];
+ mvp_damage=md->dmglog[i].dmg;
+ }
+ }
+
+ // [MouseJstr]
+ if((map[md->bl.m].flag.pvp == 0) || (battle_config.pvp_exp == 1)) {
+
+ // 経験値の分配
+ for(i=0;i<DAMAGELOG_SIZE;i++){
+ int pid,flag=1,zeny=0;
+ unsigned long base_exp,job_exp;
+ double per;
+ struct party *p;
+ if(tmpsd[i]==NULL || tmpsd[i]->bl.m != md->bl.m || pc_isdead(tmpsd[i]))
+ continue;
+
+ if (battle_config.exp_calc_type) // eAthena's exp formula based on max hp.
+ per = (double)md->dmglog[i].dmg/(double)max_hp;
+ else //jAthena's exp formula based on total damage.
+ per = (double)md->dmglog[i].dmg/(double)md->tdmg;
+
+ if (count>1)
+ per *= (9.+(double)((count > 6)? 6:count))/10.; //attackers count bonus.
+
+ base_exp = (unsigned long)md->db->base_exp;
+ job_exp = (unsigned long)md->db->job_exp;
+
+ if (ret)
+ per += per*ret/100.; //SC_RICHMANKIM bonus. [Skotlex]
+
+ if(sd) {
+ if (sd->expaddrace[race])
+ per += per*sd->expaddrace[race]/100.;
+ per += per*sd->expaddrace[mode&MD_BOSS?10:11]/100.;
+ }
+ if (battle_config.pk_mode && (md->db->lv - tmpsd[i]->status.base_level >= 20))
+ per *= 1.15; // pk_mode additional exp if monster >20 levels [Valaris]
+
+ //SG additional exp from Blessings [Komurka] - probably can be optimalized ^^;;
+ if(md->class_ == tmpsd[i]->hate_mob[0] && (battle_config.allow_skill_without_day || is_day_of_sun()))
+ per += per*10*pc_checkskill(tmpsd[i],SG_SUN_BLESS)/100.;
+ else if(md->class_ == tmpsd[i]->hate_mob[1] && (battle_config.allow_skill_without_day || is_day_of_moon()))
+ per += per*10*pc_checkskill(tmpsd[i],SG_MOON_BLESS)/100.;
+ else if(md->class_ == tmpsd[i]->hate_mob[2] && (battle_config.allow_skill_without_day || is_day_of_star()))
+ per += per*20*pc_checkskill(tmpsd[i],SG_STAR_BLESS)/100.;
+
+ if(md->special_state.size==1) // change experience for different sized monsters [Valaris]
+ per /=2.;
+ else if(md->special_state.size==2)
+ per *=2.;
+ if(md->master_id) {
+ if(((master = map_id2bl(md->master_id)) && status_get_mode(master)&MD_BOSS) || // check if its master is a boss (MVP's and minibosses)
+ md->special_state.ai) { // for summoned creatures [Valaris]
+ per = 0;
+ }
+ } else {
+ if(battle_config.zeny_from_mobs) {
+ if(md->level > 0) zeny=(int) ((md->level+rand()%md->level)*per); // zeny calculation moblv + random moblv [Valaris]
+ if(md->db->mexp > 0)
+ zeny*=rand()%250;
+ if(md->special_state.size==1 && zeny >=2) // change zeny for different sized monsters [Valaris]
+ zeny/=2;
+ else if(md->special_state.size==2 && zeny >1)
+ zeny*=2;
+ }
+ if(battle_config.mobs_level_up && md->level > md->db->lv) { // [Valaris]
+ base_exp+=(unsigned long) (((md->level-md->db->lv)*((md->db->base_exp))*(battle_config.mobs_level_up_exp_rate/100)));
+ job_exp+=(unsigned long) (((md->level-md->db->lv)*((md->db->job_exp))*(battle_config.mobs_level_up_exp_rate/100)));
+ }
+ }
+
+ if (per > 4) per = 4; //Limit gained exp to quadro the mob's exp. [3->4 Komurka]
+ base_exp = (unsigned long)(base_exp*per);
+ job_exp = (unsigned long)(job_exp*per);
+
+ if (base_exp > 0x7fffffff) base_exp = 0x7fffffff;
+ else if (base_exp < 1) base_exp = 1;
+
+ if (job_exp > 0x7fffffff) job_exp = 0x7fffffff;
+ else if (job_exp < 1) job_exp = 1;
+
+ //mapflags: noexp check [Lorky]
+ if (map[md->bl.m].flag.nobaseexp == 1) base_exp=0;
+ if (map[md->bl.m].flag.nojobexp == 1) job_exp=0;
+ //end added Lorky
+ if((pid=tmpsd[i]->status.party_id)>0){ // パーティに入っている
+ int j;
+ for(j=0;j<pnum;j++) // 公平パーティリストにいるかどうか
+ if(pt[j].id==pid)
+ break;
+ if(j==pnum){ // いないときは公平かどうか確認
+ if((p=party_search(pid))!=NULL && p->exp!=0){
+ pt[pnum].id=pid;
+ pt[pnum].p=p;
+ pt[pnum].base_exp=base_exp;
+ pt[pnum].job_exp=job_exp;
+ if(battle_config.zeny_from_mobs)
+ pt[pnum].zeny=zeny; // zeny share [Valaris]
+ pnum++;
+ flag=0;
+ }
+ }else{ // いるときは公平
+ if (pt[j].base_exp +base_exp < 0x7fffffff)
+ pt[j].base_exp+=base_exp;
+ else
+ pt[j].base_exp = 0x7fffffff;
+ if (pt[j].job_exp +job_exp < 0x7fffffff)
+ pt[j].job_exp+=job_exp;
+ else
+ pt[j].job_exp = 0x7fffffff;
+ if(battle_config.zeny_from_mobs)
+ pt[j].zeny+=zeny; // zeny share [Valaris]
+ flag=0;
+ }
+ }
+ if(flag) { // added zeny from mobs [Valaris]
+ if(base_exp > 0 || job_exp > 0)
+ pc_gainexp(tmpsd[i],base_exp,job_exp);
+ if (battle_config.zeny_from_mobs && zeny > 0) {
+ pc_getzeny(tmpsd[i],zeny); // zeny from mobs [Valaris]
+ }
+ }
+
+ }
+ // 公平分配
+ for(i=0;i<pnum;i++)
+ party_exp_share(pt[i].p,md->bl.m,pt[i].base_exp,pt[i].job_exp,pt[i].zeny);
+
+ // item drop
+ if (!(type&1)) {
+ int drop_ore = -1, drop_items = 0; //slot N for DROP LOG, number of dropped items
+ int log_item[10]; //8 -> 10 Lupus
+ memset(&log_item,0,sizeof(log_item));
+ for (i = 0; i < 10; i++) { // 8 -> 10 Lupus
+ struct delay_item_drop *ditem;
+
+ if ((master && status_get_mode(master) & MD_BOSS) || // check if its master is a boss (MVP's and minibosses)
+ (md->special_state.ai &&
+ (battle_config.alchemist_summon_reward == 0 || //Noone gives items
+ (md->class_ != 1142 && battle_config.alchemist_summon_reward == 1) //Non Marine spheres don't drop items
+ ))) // Added [Valaris]
+ break; // End
+ //mapflag: noloot check [Lorky]
+ if (map[md->bl.m].flag.nomobloot) break;;
+ //end added [Lorky]
+
+ if (md->db->dropitem[i].nameid <= 0)
+ continue;
+ drop_rate = md->db->dropitem[i].p;
+ if (drop_rate <= 0 && !battle_config.drop_rate0item)
+ drop_rate = 1;
+ // change drops depending on monsters size [Valaris]
+ if(md->special_state.size==1 && drop_rate >= 2)
+ drop_rate/=2;
+ else if(md->special_state.size==2 && drop_rate > 0)
+ drop_rate*=2;
+ //Drops affected by luk as a fixed increase [Valaris]
+ if (src && battle_config.drops_by_luk > 0)
+ drop_rate += status_get_luk(src)*battle_config.drops_by_luk/100;
+ //Drops affected by luk as a % increase [Skotlex]
+ if (src && battle_config.drops_by_luk2 > 0)
+ drop_rate += (int)(0.5+drop_rate*status_get_luk(src)*battle_config.drops_by_luk2/10000.0);
+ if (sd && battle_config.pk_mode == 1 && (md->db->lv - sd->status.base_level >= 20))
+ drop_rate = (int)(drop_rate*1.25); // pk_mode increase drops if 20 level difference [Valaris]
+
+ if (drop_rate < rand() % 10000 + 1) { //fixed 0.01% impossible drops bug [Lupus]
+ drop_ore = i; //we remember an empty slot to put there ORE DISCOVERY drop later.
+ continue;
+ }
+ drop_items++; //we count if there were any drops
+
+ ditem = mob_setdropitem(md->db->dropitem[i].nameid, 1, md->bl.m, md->bl.x, md->bl.y, mvp_sd, second_sd, third_sd);
+ log_item[i] = ditem->item_data.nameid;
+
+ //A Rare Drop Global Announce by Lupus
+ if(drop_rate<=battle_config.rare_drop_announce) {
+ struct item_data *i_data;
+ char message[128];
+ i_data = itemdb_exists(ditem->item_data.nameid);
+ sprintf (message, msg_txt(541), (mvp_sd?mvp_sd->status.name:"???"), md->db->jname, i_data->jname, (float)drop_rate/100);
+ //MSG: "'%s' won %s's %s (chance: %%%0.02f)"
+ intif_GMmessage(message,strlen(message)+1,0);
+ }
+ // Announce first, or else ditem will be freed. [Lance]
+ mob_item_drop(md, tick+base_drop_delay+i, ditem, 0, drop_rate);
+ }
+
+ // Ore Discovery [Celest]
+ if (sd == mvp_sd && map[md->bl.m].flag.nomobloot==0 && pc_checkskill(sd,BS_FINDINGORE)>0 && battle_config.finding_ore_rate/10 >= rand()%10000) {
+ struct delay_item_drop *ditem;
+ ditem = mob_setdropitem(itemdb_searchrandomid(6), 1, md->bl.m, md->bl.x, md->bl.y, mvp_sd, second_sd, third_sd);
+ if (drop_ore<0) drop_ore=8; //we have only 10 slots in LOG, there's a check to not overflow (9th item usually a card, so we use 8th slot)
+ log_item[drop_ore] = ditem->item_data.nameid; //it's for logging only
+ drop_items++; //we count if there were any drops
+ mob_item_drop(md, tick+base_drop_delay+drop_ore, ditem, 0, battle_config.finding_ore_rate/10);
+ }
+
+ //this drop log contains ALL dropped items + ORE (if there was ORE Recovery) [Lupus]
+ if(sd && log_config.drop > 0 && drop_items) //we check were there any drops.. and if not - don't write the log
+ log_drop(sd, md->class_, log_item); //mvp_sd
+
+ if(sd/* && sd->state.attack_type == BF_WEAPON*/) { //Player reports indicate this SHOULD work with all skills. [Skotlex]
+ int itemid = 0;
+ for (i = 0; i < sd->add_drop_count; i++) {
+ struct delay_item_drop *ditem;
+ if (sd->add_drop[i].id < 0)
+ continue;
+ if (sd->add_drop[i].race & (1<<race) ||
+ sd->add_drop[i].race & 1<<(mode&MD_BOSS?10:11))
+ {
+ //check if the bonus item drop rate should be multiplied with mob level/10 [Lupus]
+ if(sd->add_drop[i].rate<0)
+ //it's negative, then it should be multiplied. e.g. for Mimic,Myst Case Cards, etc
+ // rate = base_rate * (mob_level/10) + 1
+ drop_rate = -sd->add_drop[i].rate*(md->level/10)+1;
+ else
+ //it's positive, then it goes as it is
+ drop_rate = sd->add_drop[i].rate;
+ if (drop_rate < rand()%10000 +1)
+ continue;
+ itemid = (sd->add_drop[i].id > 0) ? sd->add_drop[i].id :
+ itemdb_searchrandomgroup(sd->add_drop[i].group);
+
+ ditem = mob_setdropitem(itemid, 1, md->bl.m, md->bl.x, md->bl.y, mvp_sd, second_sd, third_sd);
+ mob_item_drop(md, tick+base_drop_delay+20+i, ditem, 0, drop_rate);
+ }
+ }
+ if(sd->get_zeny_num && rand()%100 < sd->get_zeny_rate) //Gets get_zeny_num per level +/-10% [Skotlex]
+ pc_getzeny(sd,md->db->lv*sd->get_zeny_num*(90+rand()%21)/100);
+ }
+ if(md->lootitem) {
+ for(i=0;i<md->lootitem_count;i++) {
+ struct delay_item_drop *ditem;
+
+ ditem = mob_setlootitem(&md->lootitem[i], md->bl.m, md->bl.x, md->bl.y, mvp_sd, second_sd, third_sd);
+ mob_item_drop(md, tick+base_drop_delay+40+i, ditem, 1, 10000);
+ }
+ }
+ }
+
+ // mvp処理
+ if(mvp_sd && md->db->mexp > 0 && !md->special_state.ai){
+ int log_mvp[2] = {0};
+ int j;
+ int mexp;
+ temp = ((double)md->db->mexp * (9.+(double)count)/10.); //[Gengar]
+ mexp = (temp > 2147483647.)? 0x7fffffff:(int)temp;
+
+ //mapflag: noexp check [Lorky]
+ if (map[md->bl.m].flag.nobaseexp == 1 || map[md->bl.m].flag.nojobexp == 1) mexp=1;
+ //end added [Lorky]
+
+ if(mexp < 1) mexp = 1;
+ clif_mvp_effect(mvp_sd); // エフェクト
+ clif_mvp_exp(mvp_sd,mexp);
+ pc_gainexp(mvp_sd,mexp,0);
+ log_mvp[1] = mexp;
+ for(j=0;j<3;j++){
+ i = rand() % 3;
+ //mapflag: noloot check [Lorky]
+ if (map[md->bl.m].flag.nomvploot == 1) break;
+ //end added Lorky
+
+ if(md->db->mvpitem[i].nameid <= 0)
+ continue;
+ drop_rate = md->db->mvpitem[i].p;
+ if(drop_rate <= 0 && !battle_config.drop_rate0item)
+ drop_rate = 1;
+ if(drop_rate <= rand()%10000+1) //if ==0, then it doesn't drop
+ continue;
+ memset(&item,0,sizeof(item));
+ item.nameid=md->db->mvpitem[i].nameid;
+ item.identify=!itemdb_isequip3(item.nameid);
+ clif_mvp_item(mvp_sd,item.nameid);
+ log_mvp[0] = item.nameid;
+ if(mvp_sd->weight*2 > mvp_sd->max_weight)
+ map_addflooritem(&item,1,mvp_sd->bl.m,mvp_sd->bl.x,mvp_sd->bl.y,mvp_sd,second_sd,third_sd,1);
+ else if((ret = pc_additem(mvp_sd,&item,1))) {
+ clif_additem(sd,0,0,ret);
+ map_addflooritem(&item,1,mvp_sd->bl.m,mvp_sd->bl.x,mvp_sd->bl.y,mvp_sd,second_sd,third_sd,1);
+ }
+
+ //A Rare MVP Drop Global Announce by Lupus
+ if(drop_rate<=battle_config.rare_drop_announce) {
+ struct item_data *i_data;
+ char message[128];
+ i_data = itemdb_exists(item.nameid);
+ sprintf (message, msg_txt(541), mvp_sd?mvp_sd->status.name :"???", md->db->jname, i_data->jname, (float)drop_rate/100);
+ //MSG: "'%s' won %s's %s (chance: %%%0.02f)"
+ intif_GMmessage(message,strlen(message)+1,0);
+ }
+
+ if(log_config.pick > 0) {//Logs items, MVP prizes [Lupus]
+ log_pick((struct map_session_data*)md, "M", md->class_, item.nameid, -1, NULL);
+ log_pick(mvp_sd, "P", 0, item.nameid, 1, NULL);
+ }
+
+ if(log_config.mvpdrop > 0)
+ log_mvpdrop(mvp_sd, md->class_, log_mvp);
+
+ break;
+ }
+
+ }
+
+ } // [MouseJstr]
+
+ // <Agit> NPC Event [OnAgitBreak]
+ if(md->npc_event[0] && strcmp(((md->npc_event)+strlen(md->npc_event)-13),"::OnAgitBreak") == 0) {
+ ShowNotice("MOB.C: Run NPC_Event[OnAgitBreak].\n");
+ if (agit_flag == 1) //Call to Run NPC_Event[OnAgitBreak]
+ guild_agit_break(md);
+ }
+
+ // SCRIPT実行
+ if(md->npc_event[0]){
+// if(battle_config.battle_log)
+// printf("mob_damage : run event : %s\n",md->npc_event);
+ if(src && src->type == BL_PET)
+ sd = ((struct pet_data *)src)->msd;
+ if(sd && battle_config.mob_npc_event_type)
+ npc_event(sd,md->npc_event,0);
+ else if(mvp_sd)
+ npc_event(mvp_sd,md->npc_event,0);
+
+ } else if (mvp_sd) {
+//lordalfa
+ pc_setglobalreg(mvp_sd,"killedrid",(md->class_));
+ if (script_config.event_script_type == 0) {
+ struct npc_data *npc;
+ if ((npc = npc_name2id("NPCKillEvent"))) {
+ run_script(npc->u.scr.script,0,mvp_sd->bl.id,npc->bl.id); // NPCKillNPC
+ ShowStatus("Event '"CL_WHITE"NPCKillEvent"CL_RESET"' executed.\n");
+ }
+ } else {
+ ShowStatus("%d '"CL_WHITE"%s"CL_RESET"' events executed.\n",
+ npc_event_doall_id("NPCKillEvent", mvp_sd->bl.id), "NPCKillEvent");
+ }
+}
+//[lordalfa]
+ (battle_config.mob_clear_delay) ? clif_clearchar_delay(tick+battle_config.mob_clear_delay,&md->bl,1) : clif_clearchar_area(&md->bl,1);
+// clif_clearchar_area(&md->bl,1); //eh? Why send the same packet twice? [Skotlex]
+ if(md->level) md->level=0;
+ map_delblock(&md->bl);
+ if(pcdb_checkid(mob_get_viewclass(md->class_)))
+ clif_clearchar_delay(tick+3000,&md->bl,0);
+ if(mob_is_clone(md->class_))
+ mob_clone_delete(md->class_);
+ mob_deleteslave(md);
+ mob_setdelayspawn(md->bl.id);
+ map_freeblock_unlock();
+
+ return damage;
+}
+
+int mob_guardian_guildchange(struct block_list *bl,va_list ap)
+{
+ struct mob_data *md;
+ struct guild* g;
+
+ nullpo_retr(0, bl);
+ nullpo_retr(0, md = (struct mob_data *)bl);
+
+ if (!md->guardian_data)
+ return 0;
+
+ if (md->guardian_data->castle->guild_id == 0)
+ { //Castle with no owner? Delete the guardians.
+ if (md->class_ == MOBID_EMPERIUM)
+ { //But don't delete the emperium, just clear it's guild-data
+ md->guardian_data->guild_id = 0;
+ md->guardian_data->emblem_id = 0;
+ md->guardian_data->guild_name[0] = '\0';
+ } else {
+ if (md->guardian_data->castle->guardian[md->guardian_data->number].visible)
+ { //Safe removal of guardian.
+ md->guardian_data->castle->guardian[md->guardian_data->number].visible = 0;
+ guild_castledatasave(md->guardian_data->castle->castle_id, 10+md->guardian_data->number,0);
+ guild_castledatasave(md->guardian_data->castle->castle_id, 18+md->guardian_data->number,0);
+ }
+ mob_delete(md); //Remove guardian.
+ }
+ return 0;
+ }
+
+ g = guild_search(md->guardian_data->castle->guild_id);
+ if (g == NULL)
+ { //Properly remove guardian info from Castle data.
+ ShowError("mob_guardian_guildchange: New Guild (id %d) does not exists!\n", md->guardian_data->guild_id);
+ md->guardian_data->castle->guardian[md->guardian_data->number].visible = 0;
+ guild_castledatasave(md->guardian_data->castle->castle_id, 10+md->guardian_data->number,0);
+ guild_castledatasave(md->guardian_data->castle->castle_id, 18+md->guardian_data->number,0);
+ mob_delete(md);
+ return 0;
+ }
+
+ md->guardian_data->guild_id = md->guardian_data->castle->guild_id;
+ md->guardian_data->emblem_id = g->emblem_id;
+ md->guardian_data->guardup_lv = guild_checkskill(g,GD_GUARDUP);
+ memcpy(md->guardian_data->guild_name, g->name, NAME_LENGTH);
+
+ return 1;
+}
+
+/*==========================================
+ * Pick a random class for the mob
+ *------------------------------------------
+ */
+int mob_random_class (int *value, size_t count)
+{
+ nullpo_retr(0, value);
+
+ // no count specified, look into the array manually, but take only max 5 elements
+ if (count < 1) {
+ count = 0;
+ while(count < 5 && mobdb_checkid(value[count])) count++;
+ if(count < 1) // nothing found
+ return 0;
+ } else {
+ // check if at least the first value is valid
+ if(mobdb_checkid(value[0]) == 0)
+ return 0;
+ }
+ //Pick a random value, hoping it exists. [Skotlex]
+ return mobdb_checkid(value[rand()%count]);
+}
+
+/*==========================================
+ * Change mob base class
+ *------------------------------------------
+ */
+int mob_class_change (struct mob_data *md, int class_)
+{
+ unsigned int tick = gettick();
+ int i, c, hp_rate;
+
+ nullpo_retr(0, md);
+
+ if (md->bl.prev == NULL)
+ return 0;
+
+ hp_rate = md->hp*100/status_get_max_hp(&md->bl);
+ clif_mob_class_change(md,class_);
+ md->class_ = class_;
+ md->db = mob_db(class_);
+ md->max_hp = md->db->max_hp; //Update the mob's max HP
+ if (battle_config.monster_class_change_full_recover) {
+ md->hp = md->max_hp;
+ memset(md->dmglog, 0, sizeof(md->dmglog));
+ md->tdmg = 0;
+ } else
+ md->hp = md->max_hp*hp_rate/100;
+ if(md->hp > md->max_hp) md->hp = md->max_hp;
+ else if(md->hp < 1) md->hp = 1;
+
+ memcpy(md->name,md->db->jname,NAME_LENGTH-1);
+ memset(&md->state,0,sizeof(md->state));
+ md->attacked_id = 0;
+ md->target_id = 0;
+ md->move_fail_count = 0;
+
+ md->speed = md->db->speed;
+ md->def_ele = md->db->element;
+
+ mob_changestate(md,MS_IDLE,0);
+ skill_castcancel(&md->bl,0);
+ md->state.skillstate = MSS_IDLE;
+ md->last_thinktime = tick;
+ md->next_walktime = tick+rand()%50+5000;
+ md->attackabletime = tick;
+ md->canmove_tick = tick;
+ md->last_linktime = tick;
+
+ for(i=0,c=tick-1000*3600*10;i<MAX_MOBSKILL;i++)
+ md->skilldelay[i] = c;
+ md->skillid=0;
+ md->skilllv=0;
+
+ if(md->lootitem == NULL && md->db->mode&MD_LOOTER)
+ md->lootitem=(struct item *)aCalloc(LOOTITEM_SIZE,sizeof(struct item));
+
+ skill_clear_unitgroup(&md->bl);
+ skill_cleartimerskill(&md->bl);
+
+ clif_clearchar_area(&md->bl,0);
+ clif_spawnmob(md);
+
+ if (battle_config.show_mob_hp)
+ clif_charnameack(0, &md->bl);
+
+ return 0;
+}
+
+/*==========================================
+ * mob回復
+ *------------------------------------------
+ */
+int mob_heal(struct mob_data *md,int heal)
+{
+ int max_hp;
+
+ nullpo_retr(0, md);
+ max_hp = status_get_max_hp(&md->bl);
+
+ md->hp += heal;
+ if( max_hp < md->hp )
+ md->hp = max_hp;
+
+ if(md->guardian_data && md->guardian_data->number < MAX_GUARDIANS) { // guardian hp update [Valaris] (updated by [Skotlex])
+ if ((md->guardian_data->castle->guardian[md->guardian_data->number].hp = md->hp) <= 0)
+ {
+ guild_castledatasave(md->guardian_data->castle->castle_id, 10+md->guardian_data->number,0);
+ guild_castledatasave(md->guardian_data->castle->castle_id, 18+md->guardian_data->number,0);
+ }
+ } // end addition
+
+ if (battle_config.show_mob_hp)
+ clif_charnameack(0, &md->bl);
+
+ return 0;
+}
+
+
+/*==========================================
+ * Added by RoVeRT
+ *------------------------------------------
+ */
+int mob_warpslave_sub(struct block_list *bl,va_list ap)
+{
+ struct mob_data *md=(struct mob_data *)bl;
+ struct block_list *master;
+ int x,y,range,i=0;
+ master = va_arg(ap, struct block_list*);
+ range = va_arg(ap, int);
+
+ if(md->master_id!=master->id)
+ return 0;
+
+ do {
+ x = master->x - range/2 + rand()%range;
+ y = master->y - range/2 + rand()%range;
+ } while (map_getcell(master->m,x,y,CELL_CHKNOPASS) && i<25);
+
+ if (i == 100)
+ mob_warp(md, master->m, master->x, master->y,2);
+ else
+ mob_warp(md, master->m, x, y,2);
+
+ return 1;
+}
+
+/*==========================================
+ * Added by RoVeRT
+ * Warps slaves. Range is the area around the master that they can
+ * appear in randomly.
+ *------------------------------------------
+ */
+int mob_warpslave(struct block_list *bl, int range)
+{
+ if (range < 1)
+ range = 1; //Min range needed to avoid crashes and stuff. [Skotlex]
+
+ return map_foreachinmap(mob_warpslave_sub, bl->m, BL_MOB, bl, range);
+}
+
+/*==========================================
+ * mobワープ
+ *------------------------------------------
+ */
+int mob_warp(struct mob_data *md,int m,int x,int y,int type)
+{
+ int i=0,xs=0,ys=0,bx=x,by=y;
+ int tick = gettick();
+
+ nullpo_retr(0, md);
+
+ if( md->bl.prev==NULL )
+ return 0;
+
+ if( m<0 ) m=md->bl.m;
+
+ if(type >= 0) {
+ if(map[md->bl.m].flag.monster_noteleport)
+ return 0;
+ if(md->sc_count) { //Clear a few status changes (taken directly from pc_setpos). [Skotlex]
+ if(md->sc_data[SC_TRICKDEAD].timer != -1)
+ status_change_end(&md->bl, SC_TRICKDEAD, -1);
+ if(md->sc_data[SC_BLADESTOP].timer!=-1)
+ status_change_end(&md->bl,SC_BLADESTOP,-1);
+ if(md->sc_data && md->sc_data[SC_RUN].timer!=-1)
+ status_change_end(&md->bl,SC_RUN,-1);
+ if(md->sc_data[SC_DANCING].timer!=-1)
+ skill_stop_dancing(&md->bl);
+ if (md->sc_data[SC_DEVOTION].timer!=-1)
+ status_change_end(&md->bl,SC_DEVOTION,-1);
+ if (md->sc_data[SC_CLOSECONFINE].timer!=-1)
+ status_change_end(&md->bl,SC_CLOSECONFINE,-1);
+ if (md->sc_data[SC_CLOSECONFINE2].timer!=-1)
+ status_change_end(&md->bl,SC_CLOSECONFINE2,-1);
+ if (md->sc_data[SC_RUN].timer!=-1)
+ status_change_end(&md->bl,SC_RUN,-1);
+ }
+ clif_clearchar_area(&md->bl,type);
+ }
+ skill_unit_move(&md->bl,tick,4);
+ map_delblock(&md->bl);
+
+ if(bx>0 && by>0){ // 位置指定の場合周囲9セルを探索
+ xs=ys=9;
+ }
+
+ while( ( x<0 || y<0 || map_getcell(m,x,y,CELL_CHKNOPASS)) && (i++)<1000 ){
+ if( xs>0 && ys>0 && i<250 ){ // 指定位置付近の探索
+ x=bx+rand()%xs-xs/2;
+ y=by+rand()%ys-ys/2;
+ }else{ // 完全ランダム探索
+ x=rand()%(map[m].xs-2)+1;
+ y=rand()%(map[m].ys-2)+1;
+ }
+ }
+ md->dir=0;
+ if(i<1000){
+ md->bl.x=md->to_x=x;
+ md->bl.y=md->to_y=y;
+ md->bl.m=m;
+ }else {
+ m=md->bl.m;
+ if(battle_config.error_log==1)
+ ShowWarning("MOB %d warp failed, class = %d\n",md->bl.id,md->class_);
+ }
+
+ md->target_id=0; // タゲを解除する
+ md->state.targettype=NONE_ATTACKABLE;
+ md->attacked_id=0;
+ if (md->master_id)
+ md->master_dist = 0; //Assume mob warped to leader. [Skotlex]
+ md->state.skillstate=MSS_IDLE;
+ mob_changestate(md,MS_IDLE,0);
+
+ if(type>0 && i==1000) {
+ if(battle_config.battle_log)
+ ShowInfo("MOB %d warp to (%d,%d), class = %d\n",md->bl.id,x,y,md->class_);
+ }
+
+ map_addblock(&md->bl);
+ skill_unit_move(&md->bl,tick,1);
+ if(type>0)
+ {
+ clif_spawnmob(md);
+ mob_warpslave(&md->bl,AREA_SIZE);
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * 画面内の取り巻きの数計算用(foreachinarea)
+ *------------------------------------------
+ */
+int mob_countslave_sub(struct block_list *bl,va_list ap)
+{
+ int id,*c;
+ struct mob_data *md;
+ id=va_arg(ap,int);
+
+ c=va_arg(ap,int *);
+ md = (struct mob_data *)bl;
+
+ if( md->master_id==id ) {
+ (*c)++;
+ return 1;
+ }
+ return 0;
+}
+
+/*==========================================
+ * 画面内の取り巻きの数計算
+ *------------------------------------------
+ */
+int mob_countslave(struct block_list *bl)
+{
+ int c=0;
+ map_foreachinmap(mob_countslave_sub, bl->m, BL_MOB,bl->id,&c);
+ return c;
+}
+/*==========================================
+ * Summons amount slaves contained in the value[5] array using round-robin. [adapted by Skotlex]
+ *------------------------------------------
+ */
+int mob_summonslave(struct mob_data *md2,int *value,int amount,int skill_id)
+{
+ struct mob_data *md;
+ int bx,by,m,count = 0,class_,k;
+
+ nullpo_retr(0, md2);
+ nullpo_retr(0, value);
+
+ bx=md2->bl.x;
+ by=md2->bl.y;
+ m=md2->bl.m;
+
+ if(mobdb_checkid(value[0]) == 0)
+ return 0;
+
+ while(count < 5 && mobdb_checkid(value[count])) count++;
+ if(count < 1) return 0;
+
+ for(k=0;k<amount;k++) {
+ int x=0,y=0,i=0;
+ class_ = value[k%count]; //Summon slaves in round-robin fashion. [Skotlex]
+
+ if (mobdb_checkid(class_) == 0)
+ continue;
+
+ md=(struct mob_data *)aCalloc(1,sizeof(struct mob_data));
+ if(mob_db(class_)->mode&MD_LOOTER)
+ md->lootitem=(struct item *)aCalloc(LOOTITEM_SIZE,sizeof(struct item));
+
+ while((x<=0 || y<=0 || map_getcell(m,x,y,CELL_CHKNOPASS)) && (i++)<100){
+ x=rand()%9-4+bx;
+ y=rand()%9-4+by;
+ }
+ if(i>=100){
+ x=bx;
+ y=by;
+ }
+
+ mob_spawn_dataset(md,"--ja--",class_);
+ md->bl.m=m;
+ md->bl.x=x;
+ md->bl.y=y;
+
+ md->m =m;
+ md->x0=x;
+ md->y0=y;
+ md->xs=0;
+ md->ys=0;
+ if (battle_config.slaves_inherit_speed && (skill_id != NPC_METAMORPHOSIS && skill_id != NPC_TRANSFORMATION))
+ md->speed=md2->speed;
+ md->special_state.cached= battle_config.dynamic_mobs; //[Skotlex]
+ md->spawndelay1=-1; // 一度のみフラグ
+ md->spawndelay2=-1; // 一度のみフラグ
+
+ if (!battle_config.monster_class_change_full_recover &&
+ (skill_id == NPC_TRANSFORMATION || skill_id == NPC_METAMORPHOSIS))
+ { //Scale HP
+ md->hp = (md->max_hp*md2->hp)/md2->max_hp;
+ }
+
+ memset(md->npc_event,0,sizeof(md->npc_event));
+ md->bl.type=BL_MOB;
+ map_addiddb(&md->bl);
+ mob_spawn(md->bl.id);
+ clif_skill_nodamage(&md->bl,&md->bl,skill_id,amount,1);
+
+ if(skill_id == NPC_SUMMONSLAVE)
+ md->master_id=md2->bl.id;
+ }
+ return 0;
+}
+
+/*==========================================
+ *MOBskillから該当skillidのskillidxを返す
+ *------------------------------------------
+ */
+int mob_skillid2skillidx(int class_,int skillid)
+{
+ int i, max = mob_db(class_)->maxskill;
+ struct mob_skill *ms=mob_db(class_)->skill;
+
+ if(ms==NULL)
+ return -1;
+
+ for(i=0;i<max;i++){
+ if(ms[i].skill_id == skillid)
+ return i;
+ }
+ return -1;
+
+}
+
+//
+// MOBスキル
+//
+
+/*==========================================
+ * スキル使用(詠唱完了、ID指定)
+ *------------------------------------------
+ */
+int mobskill_castend_id( int tid, unsigned int tick, int id,int data )
+{
+ struct mob_data* md=NULL;
+ struct block_list *bl;
+ int inf;
+//Code cleanup. Insert any code that should be executed if the skill fails here.
+#define skill_failed(md) { md->skillid = md->skilllv = -1; }
+
+ if((md = (struct mob_data*)map_id2bl(id)) == NULL ) //詠唱したMobがもういないというのは良くある正常処理
+ return 0;
+
+ if( md->bl.type!=BL_MOB || md->bl.prev==NULL )
+ return 0;
+
+ if( md->skilltimer != tid ) {
+ if (battle_config.error_log)
+ ShowError("mobskill_castend_id: Timer mismatch %d!=%d\n", md->skilltimer, tid);
+ md->skilltimer = -1;
+ return 0;
+ }
+
+ md->skilltimer=-1;
+
+ if((bl = map_id2bl(md->skilltarget)) == NULL || bl->prev==NULL || md->bl.m != bl->m) {
+ skill_failed(md);
+ return 0;
+ }
+
+ if(md->skillid != NPC_EMOTION)
+ //Set afterskill delay.
+ md->last_thinktime=tick + (tid==-1?status_get_adelay(&md->bl):status_get_amotion(&md->bl));
+
+ if(md->skillid == RG_BACKSTAP) {
+ int dir = map_calc_dir(&md->bl,bl->x,bl->y),t_dir = status_get_dir(bl);
+ if(bl->type != BL_SKILL && (check_distance_bl(&md->bl, bl, 0) || map_check_dir(dir,t_dir))) {
+ skill_failed(md);
+ return 0;
+ }
+ }
+
+ inf = skill_get_inf(md->skillid);
+ if((inf&INF_ATTACK_SKILL ||
+ (inf&INF_SELF_SKILL && md->bl.id != bl->id && skill_get_nk(md->skillid) != NK_NO_DAMAGE))
+ && battle_check_target(&md->bl,bl, BCT_ENEMY)<=0 ) {
+ skill_failed(md);
+ return 0;
+ }
+
+ if(tid != -1)
+ {
+ if (md->skillid == -1 ||!status_check_skilluse(&md->bl, bl, md->skillid, 1)) {
+ skill_failed(md);
+ return 0;
+ }
+ if(!check_distance_bl(&md->bl, bl, skill_get_range2(&md->bl, md->skillid,md->skilllv) + battle_config.mob_skill_add_range)) {
+ skill_failed(md);
+ return 0;
+ }
+ }
+ md->skilldelay[md->skillidx]=tick;
+
+ if(battle_config.mob_skill_log)
+ ShowInfo("MOB skill castend skill=%d, class = %d\n",md->skillid,md->class_);
+// mob_stop_wShowInfo(md,0);
+
+ switch( skill_get_nk(md->skillid) )
+ {
+ case NK_NO_DAMAGE:// 支援系
+ skill_castend_nodamage_id(&md->bl,bl,md->skillid,md->skilllv,tick,0);
+ break;
+ // 攻撃系/吹き飛ばし系
+ case NK_SPLASH_DAMAGE:
+ default:
+ skill_castend_damage_id(&md->bl,bl,md->skillid,md->skilllv,tick,0);
+ break;
+ }
+
+ if (md->sc_count && md->sc_data[SC_MAGICPOWER].timer != -1 && md->skillid != HW_MAGICPOWER)
+ status_change_end(&md->bl, SC_MAGICPOWER, -1);
+
+ if (md->db->skill[md->skillidx].emotion >= 0)
+ clif_emotion(&md->bl, md->db->skill[md->skillidx].emotion);
+
+ //TODO: This value is used for the "afterskill" mob condition, what could one do to clean it other than
+ //to use a "previous skill" struct variable?
+ //md->skillid = md->skilllv = -1; //Clean up skill-data for battle_getcurrentskill references. [Skotlex]
+ return 0;
+}
+
+/*==========================================
+ * スキル使用(詠唱完了、場所指定)
+ *------------------------------------------
+ */
+int mobskill_castend_pos( int tid, unsigned int tick, int id,int data )
+{
+ struct mob_data* md=NULL;
+ int maxcount;
+
+//Code cleanup. Insert any code that should be executed if the skill fails here.
+#define skill_failed(md) { md->skillid = md->skilllv = -1; }
+
+ nullpo_retr(0, md=(struct mob_data *)map_id2bl(id));
+
+ if( md->bl.type!=BL_MOB || md->bl.prev==NULL )
+ return 0;
+
+ if( md->skilltimer != tid ) {
+ if (battle_config.error_log)
+ ShowError("mobskill_castend_pos: Timer mismatch %d!=%d\n", md->skilltimer, tid);
+ md->skilltimer = -1;
+ return 0;
+ }
+
+ md->skilltimer=-1;
+
+ if (tid != -1)
+ { //Avoid unnecessary checks for instant-cast skills. [Skotlex]
+ if (md->skillid == -1 || !status_check_skilluse(&md->bl, NULL, md->skillid, 1)) {
+ skill_failed(md);
+ return 0;
+ }
+ if(!check_distance_blxy(&md->bl, md->skillx, md->skilly, skill_get_range2(&md->bl, md->skillid,md->skilllv) + battle_config.mob_skill_add_range)) {
+ skill_failed(md);
+ return 0;
+ }
+ }
+
+ if (!battle_config.monster_skill_reiteration &&
+ skill_get_unit_flag (md->skillid) & UF_NOREITERATION &&
+ skill_check_unit_range (md->bl.m, md->skillx, md->skilly, md->skillid, md->skilllv)) {
+ skill_failed(md);
+ return 0;
+ }
+
+ if(battle_config.monster_skill_nofootset &&
+ skill_get_unit_flag (md->skillid) & UF_NOFOOTSET &&
+ skill_check_unit_range2(&md->bl, md->bl.m, md->skillx, md->skilly, md->skillid, md->skilllv)) {
+ skill_failed(md);
+ return 0;
+ }
+ if(battle_config.monster_land_skill_limit) {
+ maxcount = skill_get_maxcount(md->skillid);
+ if(maxcount > 0) {
+ int i,c;
+ for(i=c=0;i<MAX_MOBSKILLUNITGROUP;i++) {
+ if(md->skillunit[i].alive_count > 0 && md->skillunit[i].skill_id == md->skillid)
+ c++;
+ }
+ if(c >= maxcount) {
+ skill_failed(md);
+ return 0;
+ }
+ }
+ }
+
+ md->skilldelay[md->skillidx]=tick;
+
+ //Set afterskill delay.
+ md->last_thinktime=tick + (tid==-1?status_get_adelay(&md->bl):status_get_amotion(&md->bl));
+
+ if(battle_config.mob_skill_log)
+ ShowInfo("MOB skill castend skill=%d, class = %d\n",md->skillid,md->class_);
+// mob_stop_walking(md,0);
+
+ skill_castend_pos2(&md->bl,md->skillx,md->skilly,md->skillid,md->skilllv,tick,0);
+
+ if (md->db->skill[md->skillidx].emotion >= 0)
+ clif_emotion(&md->bl, md->db->skill[md->skillidx].emotion);
+
+ //TODO: This value is used for the "afterskill" mob condition, what could one do to clean it other than
+ //to use a "previous skill" struct variable?
+ //md->skillid = md->skilllv = -1; //Clean up skill data for future references to battle_getcurrentskill [Skotlex]
+ return 0;
+}
+
+/*==========================================
+ * Skill use (an aria start, ID specification)
+ *------------------------------------------
+ */
+int mobskill_use_id(struct mob_data *md,struct block_list *target,int skill_idx)
+{
+ int casttime;
+ struct mob_skill *ms;
+ int skill_id, skill_lv, forcecast = 0;
+ int selfdestruct_flag = 0;
+
+ nullpo_retr(0, md);
+ nullpo_retr(0, ms=&md->db->skill[skill_idx]);
+
+ if( target==NULL && (target=map_id2bl(md->target_id))==NULL )
+ return 0;
+
+ if( target->prev==NULL || md->bl.prev==NULL )
+ return 0;
+
+ skill_id=ms->skill_id;
+ skill_lv=ms->skill_lv;
+
+ if(map_flag_gvg(md->bl.m) && skill_db[skill_id].nocast & 4)
+ return 0;
+ if(skill_get_inf2(skill_id)&INF2_NO_TARGET_SELF && md->bl.id == target->id)
+ return 0;
+
+ if(!status_check_skilluse(&md->bl, target, skill_id, 0))
+ return 0;
+
+ if(md->sc_data && md->sc_data[SC_WINKCHARM].timer != -1 && target->type == BL_PC) {
+ clif_emotion(&md->bl, 3);
+ return 0;
+ }
+
+ // 射程と障害物チェック
+ if(!battle_check_range(&md->bl,target,skill_get_range2(&md->bl,skill_id,skill_lv)))
+ return 0;
+
+// delay=skill_delayfix(&md->bl, skill_id, skill_lv, 0);
+
+ casttime=skill_castfix(&md->bl, skill_id, skill_lv, ms->casttime);
+ md->state.skillcastcancel=ms->cancel;
+ md->skilldelay[skill_idx]=gettick();
+
+ switch(skill_id){ /* 何か特殊な処理が必要 */
+ case ALL_RESURRECTION: /* リザレクション */
+ if(target->type != BL_PC && battle_check_undead(status_get_race(target),status_get_elem_type(target))){ /* 敵がアンデッドなら */
+ forcecast=1; /* ターンアンデットと同じ詠唱時間 */
+ casttime=skill_castfix(&md->bl, PR_TURNUNDEAD,skill_lv, 0);
+ }
+ break;
+ case MO_EXTREMITYFIST: /*阿修羅覇鳳拳*/
+ case SA_MAGICROD:
+ case SA_SPELLBREAKER:
+ forcecast=1;
+ break;
+ case NPC_SUMMONSLAVE:
+ case NPC_SUMMONMONSTER:
+ if(md->master_id!=0)
+ return 0;
+ break;
+ case NPC_SELFDESTRUCTION:
+ if (casttime == 0 && md->special_state.ai == 2) {
+ casttime = skill_get_time(skill_id,skill_lv);
+ selfdestruct_flag = 1;
+ }
+ break;
+ }
+
+ if(battle_config.mob_skill_log)
+ ShowInfo("MOB skill use target_id=%d skill=%d lv=%d cast=%d, class = %d\n",target->id,skill_id,skill_lv,casttime,md->class_);
+
+ if (casttime || forcecast) { // 詠唱が必要
+ if (!selfdestruct_flag)
+ mob_stop_walking(md,1); // 歩行停止
+ clif_skillcasting(&md->bl, md->bl.id, target->id, 0,0, skill_id, casttime);
+ }
+
+ if (casttime <= 0) // 詠唱の無いものはキャンセルされない
+ md->state.skillcastcancel = 0;
+
+ md->skilltarget = target->id;
+ md->skillx = 0;
+ md->skilly = 0;
+ md->skillid = skill_id;
+ md->skilllv = skill_lv;
+ md->skillidx = skill_idx;
+
+ if(!(battle_config.monster_cloak_check_type&2) && md->sc_data[SC_CLOAKING].timer != -1 && md->skillid != AS_CLOAKING)
+ status_change_end(&md->bl,SC_CLOAKING,-1);
+
+ if( casttime>0 ){
+ md->skilltimer =
+ add_timer( gettick()+casttime, mobskill_castend_id, md->bl.id, 0 );
+ }else{
+ md->skilltimer = -1;
+ mobskill_castend_id(md->skilltimer,gettick(),md->bl.id, 0);
+ }
+
+ return 1;
+}
+/*==========================================
+ * スキル使用(場所指定)
+ *------------------------------------------
+ */
+int mobskill_use_pos( struct mob_data *md,
+ int skill_x, int skill_y, int skill_idx)
+{
+ int casttime=0;
+ struct mob_skill *ms;
+ struct block_list bl;
+ int skill_id, skill_lv;
+
+ nullpo_retr(0, md);
+ nullpo_retr(0, ms=&md->db->skill[skill_idx]);
+
+ if( md->bl.prev==NULL )
+ return 0;
+
+ skill_id=ms->skill_id;
+ skill_lv=ms->skill_lv;
+
+ if(map_flag_gvg(md->bl.m) && skill_db[skill_id].nocast & 4)
+ return 0;
+ if(!status_check_skilluse(&md->bl, NULL, skill_id, 0))
+ return 0;
+
+ // 射程と障害物チェック
+ bl.type = BL_NUL;
+ bl.m = md->bl.m;
+ bl.x = skill_x;
+ bl.y = skill_y;
+ if(!battle_check_range(&md->bl,&bl,skill_get_range2(&md->bl,skill_id,skill_lv)))
+ return 0;
+
+// delay=skill_delayfix(&md->bl, skill_id, skill_lv, 0);
+ casttime=skill_castfix(&md->bl,skill_id, skill_lv, ms->casttime);
+ md->skilldelay[skill_idx]=gettick();
+ md->state.skillcastcancel=ms->cancel;
+
+ if(battle_config.mob_skill_log)
+ ShowInfo("MOB skill use target_pos=(%d,%d) skill=%d lv=%d cast=%d, class = %d\n",
+ skill_x,skill_y,skill_id,skill_lv,casttime,md->class_);
+
+ if( casttime>0 ) { // A cast time is required.
+ mob_stop_walking(md,1); // 歩行停止
+ clif_skillcasting( &md->bl,
+ md->bl.id, 0, skill_x,skill_y, skill_id,casttime);
+ }
+
+ if( casttime<=0 ) // A skill without a cast time wont be cancelled.
+ md->state.skillcastcancel=0;
+
+
+ md->skillx = skill_x;
+ md->skilly = skill_y;
+ md->skilltarget = 0;
+ md->skillid = skill_id;
+ md->skilllv = skill_lv;
+ md->skillidx = skill_idx;
+ if(!(battle_config.monster_cloak_check_type&2) && md->sc_data[SC_CLOAKING].timer != -1)
+ status_change_end(&md->bl,SC_CLOAKING,-1);
+ if( casttime>0 ){
+ md->skilltimer =
+ add_timer( gettick()+casttime, mobskill_castend_pos, md->bl.id, 0 );
+ }else{
+ md->skilltimer = -1;
+ mobskill_castend_pos(md->skilltimer,gettick(),md->bl.id, 0);
+ }
+
+ return 1;
+}
+
+
+/*==========================================
+ * Friendly Mob whose HP is decreasing by a nearby MOB is looked for.
+ *------------------------------------------
+ */
+int mob_getfriendhpltmaxrate_sub(struct block_list *bl,va_list ap)
+{
+ int rate;
+ struct block_list **fr;
+ struct mob_data *md;
+
+ nullpo_retr(0, bl);
+ nullpo_retr(0, ap);
+ nullpo_retr(0, md=va_arg(ap,struct mob_data *));
+ rate=va_arg(ap,int);
+ fr=va_arg(ap,struct block_list **);
+
+ if( md->bl.id == bl->id )
+ return 0;
+
+ if ((*fr) != NULL) //A friend was already found.
+ return 0;
+
+ if (battle_check_target(&md->bl,bl,BCT_ENEMY)>0)
+ return 0;
+
+ if (status_get_hp(bl) < status_get_max_hp(bl) * rate / 100)
+ (*fr) = bl;
+ return 0;
+}
+struct block_list *mob_getfriendhpltmaxrate(struct mob_data *md,int rate)
+{
+ struct block_list *fr=NULL;
+ const int r=8;
+ int type = BL_MOB;
+
+ nullpo_retr(NULL, md);
+
+ if (md->special_state.ai) //Summoned creatures. [Skotlex]
+ type = BL_PC;
+
+ map_foreachinarea(mob_getfriendhpltmaxrate_sub, md->bl.m,
+ md->bl.x-r ,md->bl.y-r, md->bl.x+r, md->bl.y+r,
+ type,md,rate,&fr);
+ return fr;
+}
+/*==========================================
+ * Check hp rate of its master
+ *------------------------------------------
+ */
+struct block_list *mob_getmasterhpltmaxrate(struct mob_data *md,int rate)
+{
+ if (md && md->master_id > 0) {
+ struct block_list *bl = map_id2bl(md->master_id);
+ if (status_get_hp(bl) < status_get_max_hp(bl) * rate / 100)
+ return bl;
+ }
+
+ return NULL;
+}
+/*==========================================
+ * What a status state suits by nearby MOB is looked for.
+ *------------------------------------------
+ */
+int mob_getfriendstatus_sub(struct block_list *bl,va_list ap)
+{
+ int cond1,cond2;
+ struct mob_data **fr, *md, *mmd;
+ int flag=0;
+
+ nullpo_retr(0, bl);
+ nullpo_retr(0, ap);
+ nullpo_retr(0, md=(struct mob_data *)bl);
+ nullpo_retr(0, mmd=va_arg(ap,struct mob_data *));
+
+ if( mmd->bl.id == bl->id )
+ return 0;
+ if (battle_check_target(&mmd->bl,bl,BCT_ENEMY)>0)
+ return 0;
+ cond1=va_arg(ap,int);
+ cond2=va_arg(ap,int);
+ fr=va_arg(ap,struct mob_data **);
+ if( cond2==-1 ){
+ int j;
+ for(j=SC_COMMON_MIN;j<=SC_COMMON_MAX && !flag;j++){
+ if ((flag=(md->sc_data[j].timer!=-1))) //Once an effect was found, break out. [Skotlex]
+ break;
+ }
+ }else
+ flag=( md->sc_data[cond2].timer!=-1 );
+ if( flag^( cond1==MSC_FRIENDSTATUSOFF ) )
+ (*fr)=md;
+
+ return 0;
+}
+struct mob_data *mob_getfriendstatus(struct mob_data *md,int cond1,int cond2)
+{
+ struct mob_data *fr=NULL;
+ const int r=8;
+
+ nullpo_retr(0, md);
+
+ map_foreachinarea(mob_getfriendstatus_sub, md->bl.m,
+ md->bl.x-r ,md->bl.y-r, md->bl.x+r, md->bl.y+r,
+ BL_MOB,md,cond1,cond2,&fr);
+ return fr;
+}
+
+/*==========================================
+ * Skill use judging
+ *------------------------------------------
+ */
+int mobskill_use(struct mob_data *md, unsigned int tick, int event)
+{
+ struct mob_skill *ms;
+ struct block_list *fbl = NULL; //Friend bl, which can either be a BL_PC or BL_MOB depending on the situation. [Skotlex]
+ struct mob_data *fmd = NULL;
+ int i;
+
+ nullpo_retr (0, md);
+ nullpo_retr (0, ms = md->db->skill);
+
+ if (battle_config.mob_skill_rate == 0 || md->skilltimer != -1)
+ return 0;
+
+ for (i = 0; i < md->db->maxskill; i++) {
+ int c2 = ms[i].cond2, flag = 0;
+
+ // ディレイ中
+ if (DIFF_TICK(tick, md->skilldelay[i]) < ms[i].delay)
+ continue;
+
+ // 状態判定
+ if (ms[i].state >= 0 && ms[i].state != md->state.skillstate)
+ continue;
+
+ if (rand() % 10000 > ms[i].permillage) //Lupus (max value = 10000)
+ continue;
+
+ // 条件判定
+ flag = (event == ms[i].cond1);
+ //Avoid entering on defined events to avoid "hyper-active skill use" due to the overflow of calls to this function
+ //in battle. The only exception is MSC_SKILLUSED which explicitly uses the event value to trigger. [Skotlex]
+ if (!flag && (event == -1 || event == MSC_SKILLUSED)){
+ switch (ms[i].cond1)
+ {
+ case MSC_ALWAYS:
+ flag = 1; break;
+ case MSC_MYHPLTMAXRATE: // HP< maxhp%
+ {
+ int max_hp = status_get_max_hp(&md->bl);
+ flag = (md->hp < max_hp * c2 / 100); break;
+ }
+ case MSC_MYSTATUSON: // status[num] on
+ case MSC_MYSTATUSOFF: // status[num] off
+ if (!md->sc_data) {
+ flag = 0;
+ } else if (ms[i].cond2 == -1) {
+ int j;
+ for (j = SC_COMMON_MIN; j <= SC_COMMON_MAX; j++)
+ if ((flag = (md->sc_data[j].timer != -1)) != 0)
+ break;
+ } else {
+ flag = (md->sc_data[ms[i].cond2].timer != -1);
+ }
+ flag ^= (ms[i].cond1 == MSC_MYSTATUSOFF); break;
+ case MSC_FRIENDHPLTMAXRATE: // friend HP < maxhp%
+ flag = ((fbl = mob_getfriendhpltmaxrate(md, ms[i].cond2)) != NULL); break;
+ case MSC_FRIENDSTATUSON: // friend status[num] on
+ case MSC_FRIENDSTATUSOFF: // friend status[num] off
+ flag = ((fmd = mob_getfriendstatus(md, ms[i].cond1, ms[i].cond2)) != NULL); break;
+ case MSC_SLAVELT: // slave < num
+ flag = (mob_countslave(&md->bl) < c2 ); break;
+ case MSC_ATTACKPCGT: // attack pc > num
+ flag = (battle_counttargeted(&md->bl, NULL, 0) > c2); break;
+ case MSC_SLAVELE: // slave <= num
+ flag = (mob_countslave(&md->bl) <= c2 ); break;
+ case MSC_ATTACKPCGE: // attack pc >= num
+ flag = (battle_counttargeted(&md->bl, NULL, 0) >= c2); break;
+ case MSC_AFTERSKILL:
+ flag = (md->skillid == c2); break;
+ case MSC_SKILLUSED: // specificated skill used
+ flag = ((event & 0xffff) == MSC_SKILLUSED && ((event >> 16) == c2 || c2 == 0)); break;
+ case MSC_RUDEATTACKED:
+ flag = (!md->attacked_id && md->attacked_count > 0);
+ if (flag) md->attacked_count = 0; //Rude attacked count should be reset after the skill condition is met. Thanks to Komurka [Skotlex]
+ break;
+ case MSC_MASTERHPLTMAXRATE:
+ flag = ((fbl = mob_getmasterhpltmaxrate(md, ms[i].cond2)) != NULL); break;
+ case MSC_MASTERATTACKED:
+ flag = (md->master_id > 0 && battle_counttargeted(map_id2bl(md->master_id), NULL, 0) > 0); break;
+ case MSC_ALCHEMIST:
+ flag = (md->state.alchemist);
+ break;
+ }
+ }
+
+ if (!flag)
+ continue; //Skill requisite failed to be fulfilled.
+
+ //Execute skill
+ if (skill_get_inf(ms[i].skill_id) & INF_GROUND_SKILL) {
+ // 場所指定
+ struct block_list *bl = NULL;
+ int x = 0, y = 0;
+ if (ms[i].target <= MST_AROUND) {
+ switch (ms[i].target) {
+ case MST_TARGET:
+ case MST_AROUND5:
+ case MST_AROUND6:
+ case MST_AROUND7:
+ case MST_AROUND8:
+ bl = map_id2bl(md->target_id);
+ break;
+ case MST_MASTER:
+ bl = &md->bl;
+ if (md->master_id)
+ bl = map_id2bl(md->master_id);
+ if (bl) //Otherwise, fall through.
+ break;
+ case MST_FRIEND:
+ if (fbl)
+ {
+ bl = fbl;
+ break;
+ } else if (fmd) {
+ bl= &fmd->bl;
+ break;
+ } // else fall through
+ default:
+ bl = &md->bl;
+ break;
+ }
+ if (bl != NULL) {
+ x = bl->x; y=bl->y;
+ }
+ }
+ if (x <= 0 || y <= 0)
+ continue;
+ // 自分の周囲
+ if (ms[i].target >= MST_AROUND1) {
+ int bx = x, by = y, i = 0, m = bl->m, r = (ms[i].target-MST_AROUND1) +1;
+ do {
+ bx = x + rand() % (r*2+1) - r;
+ by = y + rand() % (r*2+1) - r;
+ } while (map_getcell(m, bx, by, CELL_CHKNOPASS) && (i++) < 1000);
+ if (i < 1000){
+ x = bx; y = by;
+ }
+ }
+ // 相手の周囲
+ if (ms[i].target >= MST_AROUND5) {
+ int bx = x, by = y, i = 0, m = bl->m, r = (ms[i].target-MST_AROUND5) + 1;
+ do {
+ bx = x + rand() % (r*2+1) - r;
+ by = y + rand() % (r*2+1) - r;
+ } while (map_getcell(m, bx, by, CELL_CHKNOPASS) && (i++) < 1000);
+ if (i < 1000){
+ x = bx; y = by;
+ }
+ }
+ if (!mobskill_use_pos(md, x, y, i))
+ return 0;
+ } else {
+ // ID指定
+ if (ms[i].target <= MST_MASTER) {
+ struct block_list *bl;
+ switch (ms[i].target) {
+ case MST_TARGET:
+ bl = map_id2bl(md->target_id);
+ break;
+ case MST_MASTER:
+ bl = &md->bl;
+ if (md->master_id)
+ bl = map_id2bl(md->master_id);
+ if (bl) //Otherwise, fall through.
+ break;
+ case MST_FRIEND:
+ if (fbl) {
+ bl = fbl;
+ break;
+ } else if (fmd) {
+ bl = &fmd->bl;
+ break;
+ } // else fall through
+ default:
+ bl = &md->bl;
+ break;
+ }
+ if (bl && !mobskill_use_id(md, bl, i))
+ return 0;
+ }
+ }
+ return 1;
+ }
+
+ return 0;
+}
+/*==========================================
+ * Skill use event processing
+ *------------------------------------------
+ */
+int mobskill_event(struct mob_data *md, int flag)
+{
+ int tick = gettick();
+ nullpo_retr(0, md);
+
+ if (flag == -1 && mobskill_use(md, tick, MSC_CASTTARGETED))
+ return 1;
+ if ((flag & BF_SHORT) && mobskill_use(md, tick, MSC_CLOSEDATTACKED))
+ return 1;
+ if ((flag & BF_LONG) && mobskill_use(md, tick, MSC_LONGRANGEATTACKED))
+ return 1;
+ return 0;
+}
+
+/*==========================================
+ * スキル用タイマー削除
+ *------------------------------------------
+ */
+int mobskill_deltimer(struct mob_data *md )
+{
+ nullpo_retr(0, md);
+
+ if( md->skilltimer!=-1 ){
+ if( skill_get_inf( md->skillid )& INF_GROUND_SKILL )
+ delete_timer( md->skilltimer, mobskill_castend_pos );
+ else
+ delete_timer( md->skilltimer, mobskill_castend_id );
+ md->skilltimer=-1;
+ }
+ return 0;
+}
+
+// Player cloned mobs. [Valaris]
+int mob_is_clone(int class_)
+{
+ if(class_ >= MOB_CLONE_START && class_ <= MOB_CLONE_END)
+ return 1;
+
+ return 0;
+}
+
+//Flag values:
+//&1: Set special ai (fight mobs, not players)
+//If mode is not passed, a default aggressive mode is used.
+//If master_id is passed, clone is attached to him.
+//Returns: ID of newly crafted copy.
+int mob_clone_spawn(struct map_session_data *sd, char *map, int x, int y, const char *event, int master_id, int mode, int flag, unsigned int duration)
+{
+ int class_;
+ int i,j,inf,skill_id;
+ struct mob_skill *ms;
+
+ nullpo_retr(0, sd);
+
+ for(class_=MOB_CLONE_START; class_<MOB_CLONE_END; class_++){
+ if(mob_db_data[class_]==NULL)
+ break;
+ }
+
+ if(class_>MOB_CLONE_END)
+ return 0;
+
+ mob_db_data[class_]=(struct mob_db*)aCalloc(1, sizeof(struct mob_db));
+ mob_db_data[class_]->view_class=sd->status.class_;
+ sprintf(mob_db_data[class_]->name,sd->status.name);
+ sprintf(mob_db_data[class_]->jname,sd->status.name);
+ mob_db_data[class_]->lv=status_get_lv(&sd->bl);
+ mob_db_data[class_]->max_hp=status_get_max_hp(&sd->bl);
+ mob_db_data[class_]->max_sp=0;
+ mob_db_data[class_]->base_exp=1;
+ mob_db_data[class_]->job_exp=1;
+ mob_db_data[class_]->range=status_get_range(&sd->bl);
+ mob_db_data[class_]->atk1=status_get_batk(&sd->bl); //Base attack as minimum damage.
+ mob_db_data[class_]->atk2=mob_db_data[class_]->atk1 + status_get_atk(&sd->bl)+status_get_atk2(&sd->bl); //batk + weapon dmg
+ mob_db_data[class_]->def=status_get_def(&sd->bl);
+ mob_db_data[class_]->mdef=status_get_mdef(&sd->bl);
+ mob_db_data[class_]->str=status_get_str(&sd->bl);
+ mob_db_data[class_]->agi=status_get_agi(&sd->bl);
+ mob_db_data[class_]->vit=status_get_vit(&sd->bl);
+ mob_db_data[class_]->int_=status_get_int(&sd->bl);
+ mob_db_data[class_]->dex=status_get_dex(&sd->bl);
+ mob_db_data[class_]->luk=status_get_luk(&sd->bl);
+ mob_db_data[class_]->range2=AREA_SIZE*2/3; //Chase area of 2/3rds of a screen.
+ mob_db_data[class_]->range3=AREA_SIZE; //Let them have the same view-range as players.
+ mob_db_data[class_]->size=status_get_size(&sd->bl);
+ mob_db_data[class_]->race=status_get_race(&sd->bl);
+ mob_db_data[class_]->element=status_get_element(&sd->bl);
+ mob_db_data[class_]->mode=mode?mode:(MD_AGGRESSIVE|MD_ASSIST|MD_CANATTACK|MD_CANMOVE);
+ mob_db_data[class_]->speed=status_get_speed(&sd->bl);
+ mob_db_data[class_]->adelay=status_get_adelay(&sd->bl);
+ mob_db_data[class_]->amotion=status_get_amotion(&sd->bl);
+ mob_db_data[class_]->dmotion=status_get_dmotion(&sd->bl);
+ mob_db_data[class_]->sex=sd->status.sex;
+ mob_db_data[class_]->hair=sd->status.hair;
+ mob_db_data[class_]->hair_color=sd->status.hair_color;
+#if PACKETVER < 4
+ mob_db_data[class_]->weapon = sd->status.weapon;
+ mob_db_data[class_]->shield = sd->status.shield;
+#else
+ if (sd->equip_index[9] >= 0 && sd->inventory_data[sd->equip_index[9]] && sd->view_class != JOB_WEDDING && sd->view_class != JOB_XMAS) {
+ if (sd->inventory_data[sd->equip_index[9]]->view_id > 0)
+ mob_db_data[class_]->weapon=sd->inventory_data[sd->equip_index[9]]->view_id;
+ else
+ mob_db_data[class_]->weapon=sd->status.inventory[sd->equip_index[9]].nameid;
+ } else
+ mob_db_data[class_]->shield=0;
+ if (sd->equip_index[8] >= 0 && sd->equip_index[8] != sd->equip_index[9] && sd->inventory_data[sd->equip_index[8]] && sd->view_class != JOB_WEDDING && sd->view_class != JOB_XMAS) {
+ if (sd->inventory_data[sd->equip_index[8]]->view_id > 0)
+ mob_db_data[class_]->shield=sd->inventory_data[sd->equip_index[8]]->view_id;
+ else
+ mob_db_data[class_]->shield=sd->status.inventory[sd->equip_index[8]].nameid;
+ } else
+ mob_db_data[class_]->shield=0;
+#endif
+ mob_db_data[class_]->head_top=sd->status.head_top;
+ mob_db_data[class_]->head_mid=sd->status.head_mid;
+ mob_db_data[class_]->head_buttom=sd->status.head_bottom;
+ mob_db_data[class_]->option=sd->status.option;
+ mob_db_data[class_]->clothes_color=sd->status.clothes_color;
+
+ //Skill copy [Skotlex]
+ ms = &mob_db_data[class_]->skill[0];
+ //Go Backwards to give better priority to advanced skills.
+ for (i=0,j = MAX_SKILL_TREE-1;j>=0 && i< MAX_MOBSKILL ;j--) {
+ skill_id = skill_tree[sd->status.class_][j].id;
+ if (!skill_id || sd->status.skill[skill_id].lv < 1 || (skill_get_inf2(skill_id)&(INF2_WEDDING_SKILL|INF2_GUILD_SKILL)))
+ continue;
+ memset (&ms[i], 0, sizeof(struct mob_skill));
+ ms[i].skill_id = skill_id;
+ ms[i].skill_lv = sd->status.skill[skill_id].lv;
+ ms[i].state = -1;
+ ms[i].permillage = 500; //Default chance for moving/idle skills.
+ ms[i].emotion = -1;
+ ms[i].cancel = 0;
+ ms[i].delay = 5000+skill_delayfix(&sd->bl,skill_id, ms[i].skill_lv, 0);
+ ms[i].casttime = skill_castfix(&sd->bl,skill_id, ms[i].skill_lv, 0);
+
+ inf = skill_get_inf(skill_id);
+ if (inf&INF_ATTACK_SKILL) {
+ ms[i].target = MST_TARGET;
+ ms[i].cond1 = MSC_ALWAYS;
+ if (skill_get_range(skill_id, ms[i].skill_lv) > 3) {
+ ms[i].state = MSS_RUSH;
+ } else {
+ ms[i].state = MSS_BERSERK;
+ ms[i].permillage = 2500;
+ }
+ } else if(inf&INF_GROUND_SKILL) {
+ //Normal aggressive mob, disable skills that cannot help them fight
+ //against players (those with flags UF_NOMOB and UF_NOPC are specific
+ //to always aid players!) [Skotlex]
+ if (!(flag&1) && skill_get_unit_flag(skill_id)&(UF_NOMOB|UF_NOPC))
+ continue;
+ ms[i].permillage = 1000;
+ if (skill_get_inf2(skill_id)&INF2_TRAP) { //Traps!
+ ms[i].state = MSS_IDLE;
+ ms[i].target = MST_AROUND2;
+ ms[i].delay = 60000;
+ } else if (skill_get_unit_target(skill_id) == BCT_ENEMY) { //Target Enemy
+ ms[i].target = MST_TARGET;
+ ms[i].cond1 = MSC_ALWAYS;
+ } else { //Target allies
+ ms[i].target = MST_FRIEND;
+ ms[i].cond1 = MSC_FRIENDHPLTMAXRATE;
+ ms[i].cond2 = 95;
+ }
+ } else if (inf&INF_SELF_SKILL) {
+ if (skill_get_nk(skill_id) != NK_NO_DAMAGE) { //Offensive skill
+ ms[i].target = MST_TARGET;
+ ms[i].state = MSS_BERSERK;
+ } else //Self skill
+ ms[i].target = MST_SELF;
+ ms[i].cond1 = MSC_MYHPLTMAXRATE;
+ ms[i].cond2 = 90;
+ ms[i].permillage = 2000;
+ //Delay: Remove the stock 5 secs and add half of the support time.
+ ms[i].delay += -5000 +(skill_get_time(skill_id, ms[i].skill_lv) + skill_get_time2(skill_id, ms[i].skill_lv))/2;
+ if (ms[i].delay < 5000)
+ ms[i].delay = 5000; //With a minimum of 5 secs.
+ } else if (inf&INF_SUPPORT_SKILL) {
+ ms[i].target = MST_FRIEND;
+ ms[i].cond1 = MSC_FRIENDHPLTMAXRATE;
+ ms[i].cond2 = 90;
+ if (skill_id == AL_HEAL)
+ ms[i].permillage = 5000; //Higher skill rate usage for heal.
+ else if (skill_id == ALL_RESURRECTION)
+ ms[i].cond2 = 1;
+ //Delay: Remove the stock 5 secs and add half of the support time.
+ ms[i].delay += -5000 +(skill_get_time(skill_id, ms[i].skill_lv) + skill_get_time2(skill_id, ms[i].skill_lv))/2;
+ if (ms[i].delay < 2000)
+ ms[i].delay = 2000; //With a minimum of 2 secs.
+
+ if (i+1 < MAX_MOBSKILL) { //duplicate this so it also triggers on self.
+ memcpy(&ms[i+1], &ms[i], sizeof(struct mob_skill));
+ mob_db_data[class_]->maxskill = ++i;
+ ms[i].target = MST_SELF;
+ ms[i].cond1 = MSC_MYHPLTMAXRATE;
+ }
+ } else {
+ switch (skill_id) { //Certain Special skills that are passive, and thus, never triggered.
+ case MO_TRIPLEATTACK:
+ case TF_DOUBLE:
+ ms[i].state = MSS_BERSERK;
+ ms[i].target = MST_TARGET;
+ ms[i].cond1 = MSC_ALWAYS;
+ ms[i].permillage = skill_id==TF_DOUBLE?(ms[i].skill_lv*500):(3000-ms[i].skill_lv*100);
+ ms[i].delay -= 5000; //Remove the added delay as these could trigger on "all hits".
+ break;
+ default: //Untreated Skill
+ continue;
+ }
+ }
+ if (battle_config.mob_skill_rate!= 100)
+ ms[i].permillage = ms[i].permillage*battle_config.mob_skill_rate/100;
+ if (battle_config.mob_skill_delay != 100)
+ ms[i].delay = ms[i].delay*battle_config.mob_skill_delay/100;
+
+ mob_db_data[class_]->maxskill = ++i;
+ }
+ //Finally, spawn it.
+ i = mob_once_spawn(sd,(char*)map,x,y,"--en--",class_,1,event);
+ if ((master_id || flag || duration) && i) { //Further manipulate crafted char.
+ struct mob_data* md = (struct mob_data*)map_id2bl(i);
+ if (md && md->bl.type == BL_MOB) {
+ if (flag&1) //Friendly Character
+ md->special_state.ai = 1;
+ if (master_id) //Attach to Master
+ md->master_id = master_id;
+ if (duration) //Auto Delete after a while.
+ md->deletetimer = add_timer (gettick() + duration, mob_timer_delete, i, 0);
+ }
+#if 0
+ //I am playing with this for packet-research purposes, enable it if you want, but don't remove it :X [Skotlex]
+ //Guardian data
+ if (sd->status.guild_id) {
+ struct guild* g = guild_search(sd->status.guild_id);
+ md->guardian_data = aCalloc(1, sizeof(struct guardian_data));
+ md->guardian_data->castle = NULL;
+ md->guardian_data->number = MAX_GUARDIANS;
+ md->guardian_data->guild_id = sd->status.guild_id;
+ if (g)
+ {
+ md->guardian_data->emblem_id = g->emblem_id;
+ memcpy(md->guardian_data->guild_name, g->name, NAME_LENGTH);
+ }
+ }
+#endif
+ }
+
+ return i;
+}
+
+int mob_clone_delete(int class_)
+{
+ int i;
+ for(i=MOB_CLONE_START; i<MOB_CLONE_END; i++){
+ if(i==class_ && mob_db_data[i]!=NULL){
+ aFree(mob_db_data[i]);
+ mob_db_data[i]=NULL;
+ break;
+ }
+ }
+ return 0;
+}
+
+//
+// 初期化
+//
+/*==========================================
+ * Since un-setting [ mob ] up was used, it is an initial provisional value setup.
+ *------------------------------------------
+ */
+static int mob_makedummymobdb(int class_)
+{
+ if (mob_dummy != NULL)
+ {
+ if (mob_db(class_) == mob_dummy)
+ return 1; //Using the mob_dummy data already. [Skotlex]
+ if (class_ > 0 && class_ <= MAX_MOB_DB)
+ { //Remove the mob data so that it uses the dummy data instead.
+ aFree(mob_db_data[class_]);
+ mob_db_data[class_] = NULL;
+ }
+ return 0;
+ }
+ //Initialize dummy data.
+ mob_dummy = (struct mob_db*)aCalloc(1, sizeof(struct mob_db)); //Initializing the dummy mob.
+ sprintf(mob_dummy->name,"DUMMY");
+ sprintf(mob_dummy->jname,"Dummy");
+ mob_dummy->lv=1;
+ mob_dummy->max_hp=1000;
+ mob_dummy->max_sp=1;
+ mob_dummy->base_exp=2;
+ mob_dummy->job_exp=1;
+ mob_dummy->range=1;
+ mob_dummy->atk1=7;
+ mob_dummy->atk2=10;
+ mob_dummy->def=0;
+ mob_dummy->mdef=0;
+ mob_dummy->str=1;
+ mob_dummy->agi=1;
+ mob_dummy->vit=1;
+ mob_dummy->int_=1;
+ mob_dummy->dex=6;
+ mob_dummy->luk=2;
+ mob_dummy->range2=10;
+ mob_dummy->range3=10;
+ mob_dummy->race=0;
+ mob_dummy->element=0;
+ mob_dummy->mode=0;
+ mob_dummy->speed=300;
+ mob_dummy->adelay=1000;
+ mob_dummy->amotion=500;
+ mob_dummy->dmotion=500;
+ return 0;
+}
+
+//Adjusts the drop rate of item according to the criteria given. [Skotlex]
+static int mob_drop_adjust(int rate, int rate_adjust, int rate_min, int rate_max)
+{
+ if (battle_config.logarithmic_drops && rate_adjust > 0) //Logarithmic drops equation by Ishizu-Chan
+ //Equation: Droprate(x,y) = x * (5 - log(x)) ^ (ln(y) / ln(5))
+ //x is the normal Droprate, y is the Modificator.
+ rate = (int)(rate * pow((5.0 - log10(rate)), (log(rate_adjust/100.) / log(5.0))) + 0.5);
+ else //Classical linear rate adjustment.
+ rate = rate*rate_adjust/100;
+ return (rate>rate_max)?rate_max:((rate<rate_min)?rate_min:rate);
+}
+/*==========================================
+ * mob_db.txt reading
+ *------------------------------------------
+ */
+static int mob_readdb(void)
+{
+ FILE *fp;
+ char line[1024];
+ char *filename[]={ "mob_db.txt","mob_db2.txt" };
+ int class_, i, fi;
+
+ for(fi=0;fi<2;fi++){
+ sprintf(line, "%s/%s", db_path, filename[fi]);
+ fp=fopen(line,"r");
+ if(fp==NULL){
+ if(fi>0)
+ continue;
+ return -1;
+ }
+ while(fgets(line,1020,fp)){
+ double exp, maxhp;
+ char *str[60], *p, *np; // 55->60 Lupus
+
+ if(line[0] == '/' && line[1] == '/')
+ continue;
+
+ for(i=0,p=line;i<60;i++){
+ if((np=strchr(p,','))!=NULL){
+ str[i]=p;
+ *np=0;
+ p=np+1;
+ } else
+ str[i]=p;
+ }
+
+ class_ = atoi(str[0]);
+ if (class_ == 0)
+ continue; //Leave blank lines alone... [Skotlex]
+
+ if (class_ <= 1000 || class_ > MAX_MOB_DB)
+ {
+ ShowWarning("Mob with ID: %d not loaded. ID must be in range [%d-%d]\n", class_, 1000, MAX_MOB_DB);
+ continue;
+ } else if (pcdb_checkid(class_))
+ {
+ ShowWarning("Mob with ID: %d not loaded. That ID is reserved for player classes.\n");
+ continue;
+ }
+ if (mob_db_data[class_] == NULL)
+ mob_db_data[class_] = aCalloc(1, sizeof (struct mob_data));
+
+ mob_db_data[class_]->view_class = class_;
+ memcpy(mob_db_data[class_]->name, str[1], NAME_LENGTH-1);
+ memcpy(mob_db_data[class_]->jname, str[2], NAME_LENGTH-1);
+ mob_db_data[class_]->lv = atoi(str[3]);
+ mob_db_data[class_]->max_hp = atoi(str[4]);
+ mob_db_data[class_]->max_sp = atoi(str[5]);
+
+ exp = (double)atoi(str[6]) * (double)battle_config.base_exp_rate / 100.;
+ if (exp < 0) exp = 0;
+ else if (exp > 0x7fffffff) exp = 0x7fffffff;
+ mob_db_data[class_]->base_exp = (int)exp;
+
+ exp = (double)atoi(str[7]) * (double)battle_config.job_exp_rate / 100.;
+ if (exp < 0) exp = 0;
+ else if (exp > 0x7fffffff) exp = 0x7fffffff;
+ mob_db_data[class_]->job_exp = (int)exp;
+
+ mob_db_data[class_]->range=atoi(str[8]);
+ mob_db_data[class_]->atk1=atoi(str[9]);
+ mob_db_data[class_]->atk2=atoi(str[10]);
+ mob_db_data[class_]->def=atoi(str[11]);
+ mob_db_data[class_]->mdef=atoi(str[12]);
+ mob_db_data[class_]->str=atoi(str[13]);
+ mob_db_data[class_]->agi=atoi(str[14]);
+ mob_db_data[class_]->vit=atoi(str[15]);
+ mob_db_data[class_]->int_=atoi(str[16]);
+ mob_db_data[class_]->dex=atoi(str[17]);
+ mob_db_data[class_]->luk=atoi(str[18]);
+ mob_db_data[class_]->range2=atoi(str[19]);
+ mob_db_data[class_]->range3=atoi(str[20]);
+ mob_db_data[class_]->size=atoi(str[21]);
+ mob_db_data[class_]->race=atoi(str[22]);
+ mob_db_data[class_]->element=atoi(str[23]);
+ mob_db_data[class_]->mode=atoi(str[24]);
+ mob_db_data[class_]->speed=atoi(str[25]);
+ mob_db_data[class_]->adelay=atoi(str[26]);
+ mob_db_data[class_]->amotion=atoi(str[27]);
+ mob_db_data[class_]->dmotion=atoi(str[28]);
+
+ for(i=0;i<10;i++){ // 8 -> 10 Lupus
+ int rate = 0,rate_adjust,type,ratemin,ratemax;
+ struct item_data *id;
+ mob_db_data[class_]->dropitem[i].nameid=atoi(str[29+i*2]);
+ type = itemdb_type(mob_db_data[class_]->dropitem[i].nameid);
+ rate = atoi(str[30+i*2]);
+ if (class_ >= 1324 && class_ <= 1363)
+ { //Treasure box drop rates [Skotlex]
+ rate_adjust = battle_config.item_rate_treasure;
+ ratemin = battle_config.item_drop_treasure_min;
+ ratemax = battle_config.item_drop_treasure_max;
+ }
+ else switch (type)
+ {
+ case 0:
+ rate_adjust = battle_config.item_rate_heal;
+ ratemin = battle_config.item_drop_heal_min;
+ ratemax = battle_config.item_drop_heal_max;
+ break;
+ case 2:
+ rate_adjust = battle_config.item_rate_use;
+ ratemin = battle_config.item_drop_use_min;
+ ratemax = battle_config.item_drop_use_max;
+ break;
+ case 4:
+ case 5:
+ case 8: // Changed to include Pet Equip
+ rate_adjust = battle_config.item_rate_equip;
+ ratemin = battle_config.item_drop_equip_min;
+ ratemax = battle_config.item_drop_equip_max;
+ break;
+ case 6:
+ rate_adjust = battle_config.item_rate_card;
+ ratemin = battle_config.item_drop_card_min;
+ ratemax = battle_config.item_drop_card_max;
+ break;
+ default:
+ rate_adjust = battle_config.item_rate_common;
+ ratemin = battle_config.item_drop_common_min;
+ ratemax = battle_config.item_drop_common_max;
+ break;
+ }
+ mob_db_data[class_]->dropitem[i].p = mob_drop_adjust(rate, rate_adjust, ratemin, ratemax);
+
+ //calculate and store Max available drop chance of the item
+ id = itemdb_search(mob_db_data[class_]->dropitem[i].nameid);
+ if (mob_db_data[class_]->dropitem[i].p) {
+ if (id->maxchance==10000 || (id->maxchance < mob_db_data[class_]->dropitem[i].p) ) {
+ //item has bigger drop chance or sold in shops
+ id->maxchance = mob_db_data[class_]->dropitem[i].p;
+ }
+ }
+ }
+ // MVP EXP Bonus, Chance: MEXP,ExpPer
+ mob_db_data[class_]->mexp=atoi(str[49])*battle_config.mvp_exp_rate/100;
+ mob_db_data[class_]->mexpper=atoi(str[50]);
+ //Now that we know if it is an mvp or not,
+ //apply battle_config modifiers [Skotlex]
+ maxhp = (double)mob_db_data[class_]->max_hp;
+ if (mob_db_data[class_]->mexp > 0)
+ { //Mvp
+ if (battle_config.mvp_hp_rate != 100)
+ maxhp = maxhp * (double)battle_config.mvp_hp_rate /100.;
+ } else if (battle_config.monster_hp_rate != 100) //Normal mob
+ maxhp = maxhp * (double)battle_config.monster_hp_rate /100.;
+ if (maxhp < 1) maxhp = 1;
+ else if (maxhp > 0x7fffffff) maxhp = 0x7fffffff;
+ mob_db_data[class_]->max_hp = (int)maxhp;
+
+ // MVP Drops: MVP1id,MVP1per,MVP2id,MVP2per,MVP3id,MVP3per
+ for(i=0;i<3;i++){
+ struct item_data *id;
+ int rate=atoi(str[52+i*2]);
+ mob_db_data[class_]->mvpitem[i].nameid=atoi(str[51+i*2]);
+ mob_db_data[class_]->mvpitem[i].p= mob_drop_adjust(rate, battle_config.item_rate_mvp,
+ battle_config.item_drop_mvp_min, battle_config.item_drop_mvp_max);
+
+ //calculate and store Max available drop chance of the MVP item
+ id = itemdb_search(mob_db_data[class_]->mvpitem[i].nameid);
+ if (mob_db_data[class_]->mvpitem[i].p) {
+ if (id->maxchance==10000 || (id->maxchance < mob_db_data[class_]->mvpitem[i].p/10+1) ) {
+ //item has bigger drop chance or sold in shops
+ id->maxchance = mob_db_data[class_]->mvpitem[i].p/10+1; //reduce MVP drop info to not spoil common drop rate
+ }
+ }
+ }
+
+ if (mob_db_data[class_]->max_hp <= 0) {
+ ShowWarning ("Mob %d (%s) has no HP, using poring data for it\n", class_, mob_db_data[class_]->jname);
+ mob_makedummymobdb(class_);
+ }
+ }
+ fclose(fp);
+ ShowStatus("Done reading '"CL_WHITE"%s"CL_RESET"'.\n",filename[fi]);
+ }
+ return 0;
+}
+
+/*==========================================
+ * MOB display graphic change data reading
+ *------------------------------------------
+ */
+static int mob_readdb_mobavail(void)
+{
+ FILE *fp;
+ char line[1024];
+ int ln=0;
+ int class_,j,k;
+ char *str[20],*p,*np;
+
+ sprintf(line, "%s/mob_avail.txt", db_path);
+ if( (fp=fopen(line,"r"))==NULL ){
+ ShowError("can't read %s\n", line);
+ return -1;
+ }
+
+ while(fgets(line,1020,fp)){
+ if(line[0]=='/' && line[1]=='/')
+ continue;
+ memset(str,0,sizeof(str));
+
+ for(j=0,p=line;j<12;j++){
+ if((np=strchr(p,','))!=NULL){
+ str[j]=p;
+ *np=0;
+ p=np+1;
+ } else
+ str[j]=p;
+ }
+
+ if(str[0]==NULL)
+ continue;
+
+ class_=atoi(str[0]);
+ if (class_ == 0)
+ continue; //Leave blank lines alone... [Skotlex]
+
+ if(mob_db(class_) == mob_dummy) // 値が異常なら処理しない。
+ continue;
+
+ k=atoi(str[1]);
+ if(k < 0)
+ continue;
+ if (j > 3 && k > 23 && k < 69)
+ k += 3977; // advanced job/baby class
+ mob_db_data[class_]->view_class=k;
+
+ //Player sprites
+ if(pcdb_checkid(k)) {
+ mob_db_data[class_]->sex=atoi(str[2]);
+ mob_db_data[class_]->hair=atoi(str[3]);
+ mob_db_data[class_]->hair_color=atoi(str[4]);
+ mob_db_data[class_]->weapon=atoi(str[5]);
+ mob_db_data[class_]->shield=atoi(str[6]);
+ mob_db_data[class_]->head_top=atoi(str[7]);
+ mob_db_data[class_]->head_mid=atoi(str[8]);
+ mob_db_data[class_]->head_buttom=atoi(str[9]);
+ mob_db_data[class_]->option=atoi(str[10])&~0x46;
+ mob_db_data[class_]->clothes_color=atoi(str[11]); // Monster player dye option - Valaris
+ }
+ else if(atoi(str[2]) > 0) mob_db_data[class_]->equip=atoi(str[2]); // mob equipment [Valaris]
+
+ ln++;
+ }
+ fclose(fp);
+ ShowStatus("Done reading '"CL_WHITE"%d"CL_RESET"' entries in '"CL_WHITE"%s"CL_RESET"'.\n",ln,"mob_avail.txt");
+ return 0;
+}
+
+/*==========================================
+ * Reading of random monster data
+ *------------------------------------------
+ */
+static int mob_read_randommonster(void)
+{
+ FILE *fp;
+ char line[1024];
+ char *str[10],*p;
+ int i,j;
+
+ const char* mobfile[] = {
+ "mob_branch.txt",
+ "mob_poring.txt",
+ "mob_boss.txt" };
+
+ for(i=0;i<MAX_RANDOMMONSTER;i++){
+ mob_db_data[0]->summonper[i] = 1002; // 設定し忘れた場合はポリンが出るようにしておく
+ sprintf(line, "%s/%s", db_path, mobfile[i]);
+ fp=fopen(line,"r");
+ if(fp==NULL){
+ ShowError("can't read %s\n",line);
+ return -1;
+ }
+ while(fgets(line,1020,fp)){
+ int class_,per;
+ if(line[0] == '/' && line[1] == '/')
+ continue;
+ memset(str,0,sizeof(str));
+ for(j=0,p=line;j<3 && p;j++){
+ str[j]=p;
+ p=strchr(p,',');
+ if(p) *p++=0;
+ }
+
+ if(str[0]==NULL || str[2]==NULL)
+ continue;
+
+ class_ = atoi(str[0]);
+ per=atoi(str[2]);
+ if(mob_db(class_) != mob_dummy)
+ mob_db_data[class_]->summonper[i]=per;
+ }
+ fclose(fp);
+ ShowStatus("Done reading '"CL_WHITE"%s"CL_RESET"'.\n",mobfile[i]);
+ }
+ return 0;
+}
+
+/*==========================================
+ * mob_skill_db.txt reading
+ *------------------------------------------
+ */
+static int mob_readskilldb(void)
+{
+ FILE *fp;
+ char line[1024];
+ int i;
+
+ const struct {
+ char str[32];
+ int id;
+ } cond1[] = {
+ { "always", MSC_ALWAYS },
+ { "myhpltmaxrate", MSC_MYHPLTMAXRATE },
+ { "friendhpltmaxrate",MSC_FRIENDHPLTMAXRATE },
+ { "mystatuson", MSC_MYSTATUSON },
+ { "mystatusoff", MSC_MYSTATUSOFF },
+ { "friendstatuson", MSC_FRIENDSTATUSON },
+ { "friendstatusoff", MSC_FRIENDSTATUSOFF },
+ { "attackpcgt", MSC_ATTACKPCGT },
+ { "attackpcge", MSC_ATTACKPCGE },
+ { "slavelt", MSC_SLAVELT },
+ { "slavele", MSC_SLAVELE },
+ { "closedattacked", MSC_CLOSEDATTACKED },
+ { "longrangeattacked",MSC_LONGRANGEATTACKED },
+ { "skillused", MSC_SKILLUSED },
+ { "afterskill", MSC_AFTERSKILL },
+ { "casttargeted", MSC_CASTTARGETED },
+ { "rudeattacked", MSC_RUDEATTACKED },
+ { "masterhpltmaxrate",MSC_MASTERHPLTMAXRATE },
+ { "masterattacked", MSC_MASTERATTACKED },
+ { "alchemist", MSC_ALCHEMIST },
+ }, cond2[] ={
+ { "anybad", -1 },
+ { "stone", SC_STONE },
+ { "freeze", SC_FREEZE },
+ { "stan", SC_STAN },
+ { "sleep", SC_SLEEP },
+ { "poison", SC_POISON },
+ { "curse", SC_CURSE },
+ { "silence", SC_SILENCE },
+ { "confusion", SC_CONFUSION },
+ { "blind", SC_BLIND },
+ { "hiding", SC_HIDING },
+ { "sight", SC_SIGHT },
+ }, state[] = {
+ { "any", -1 },
+ { "idle", MSS_IDLE },
+ { "walk", MSS_WALK },
+ { "loot", MSS_LOOT },
+ { "dead", MSS_DEAD },
+ { "attack", MSS_BERSERK }, //Retaliating attack
+ { "angry", MSS_ANGRY }, //Preemptive attack (aggressive mobs)
+ { "chase", MSS_RUSH }, //Chase escaping target
+ { "follow", MSS_FOLLOW }, //Preemptive chase (aggressive mobs)
+ }, target[] = {
+ { "target", MST_TARGET },
+ { "self", MST_SELF },
+ { "friend", MST_FRIEND },
+ { "master", MST_MASTER },
+ { "around5", MST_AROUND5 },
+ { "around6", MST_AROUND6 },
+ { "around7", MST_AROUND7 },
+ { "around8", MST_AROUND8 },
+ { "around1", MST_AROUND1 },
+ { "around2", MST_AROUND2 },
+ { "around3", MST_AROUND3 },
+ { "around4", MST_AROUND4 },
+ { "around", MST_AROUND },
+ };
+
+ int x;
+ char *filename[]={ "mob_skill_db.txt","mob_skill_db2.txt" };
+
+ if (!battle_config.mob_skill_rate) {
+ ShowStatus("Mob skill use disabled. Not reading mob skills.\n");
+ return 0;
+ }
+ for(x=0;x<2;x++){
+ sprintf(line, "%s/%s", db_path, filename[x]);
+ fp=fopen(line,"r");
+ if(fp==NULL){
+ if(x==0)
+ ShowError("can't read %s\n",line);
+ continue;
+ }
+ while(fgets(line,1020,fp)){
+ char *sp[20],*p;
+ int mob_id;
+ struct mob_skill *ms, gms;
+ int j=0;
+
+ if(line[0] == '/' && line[1] == '/')
+ continue;
+
+ memset(sp,0,sizeof(sp));
+ for(i=0,p=line;i<18 && p;i++){
+ sp[i]=p;
+ if((p=strchr(p,','))!=NULL)
+ *p++=0;
+ }
+ if(i == 0 || (mob_id=atoi(sp[0]))== 0 || (mob_id > 0 && mob_db(mob_id) == mob_dummy))
+ continue;
+ if(i < 18) {
+ ShowError("Insufficient number of fields for Mob Skill (Mob ID[%s], Name[%s], Skill:[%s/Lv%s])\n", sp[0], i>1?sp[1]:"?", i>3?sp[3]:"?", i>4?sp[4]:"?");
+ continue;
+ }
+ if( strcmp(sp[1],"clear")==0 ){
+ if (mob_id < 0)
+ continue;
+ memset(mob_db_data[mob_id]->skill,0,sizeof(struct mob_skill));
+ mob_db_data[mob_id]->maxskill=0;
+ continue;
+ }
+
+ if (mob_id < 0)
+ { //Prepare global skill. [Skotlex]
+ memset(&gms, 0, sizeof (struct mob_skill));
+ ms = &gms;
+ } else {
+ for(i=0;i<MAX_MOBSKILL;i++)
+ if( (ms=&mob_db_data[mob_id]->skill[i])->skill_id == 0)
+ break;
+ if(i==MAX_MOBSKILL){
+ ShowWarning("mob_skill: readdb: too many skill ! [%s] in %d[%s]\n",
+ sp[1],mob_id,mob_db_data[mob_id]->jname);
+ continue;
+ }
+ }
+
+ ms->state=atoi(sp[2]);
+ for(j=0;j<sizeof(state)/sizeof(state[0]);j++){
+ if( strcmp(sp[2],state[j].str)==0)
+ ms->state=state[j].id;
+ }
+
+ //Skill ID
+ j=atoi(sp[3]);
+ if (j<=0 || j>MAX_SKILL_DB) //fixed Lupus
+ {
+ if (mob_id < 0)
+ ShowWarning("Invalid Skill ID (%d) for all mobs\n", j);
+ else
+ ShowWarning("Invalid Skill ID (%d) for mob %d (%s)\n", j, mob_id, mob_db_data[mob_id]->jname);
+ continue;
+ }
+ ms->skill_id=j;
+ //Skill lvl
+ j= atoi(sp[4])<=0 ? 1 : atoi(sp[4]);
+ ms->skill_lv= j>battle_config.mob_max_skilllvl ? battle_config.mob_max_skilllvl : j; //we strip max skill level
+
+ //Apply battle_config modifiers to rate (permillage) and delay [Skotlex]
+ ms->permillage=atoi(sp[5]);
+ if (battle_config.mob_skill_rate != 100)
+ ms->permillage = ms->permillage*battle_config.mob_skill_rate/100;
+ ms->casttime=atoi(sp[6]);
+ ms->delay=atoi(sp[7]);
+ if (battle_config.mob_skill_delay != 100)
+ ms->delay = ms->delay*battle_config.mob_skill_delay/100;
+ ms->cancel=atoi(sp[8]);
+ if( strcmp(sp[8],"yes")==0 ) ms->cancel=1;
+ ms->target=atoi(sp[9]);
+ for(j=0;j<sizeof(target)/sizeof(target[0]);j++){
+ if( strcmp(sp[9],target[j].str)==0)
+ ms->target=target[j].id;
+ }
+ ms->cond1=-1;
+ for(j=0;j<sizeof(cond1)/sizeof(cond1[0]);j++){
+ if( strcmp(sp[10],cond1[j].str)==0)
+ ms->cond1=cond1[j].id;
+ }
+ ms->cond2=atoi(sp[11]);
+ for(j=0;j<sizeof(cond2)/sizeof(cond2[0]);j++){
+ if( strcmp(sp[11],cond2[j].str)==0)
+ ms->cond2=cond2[j].id;
+ }
+ ms->val[0]=atoi(sp[12]);
+ ms->val[1]=atoi(sp[13]);
+ ms->val[2]=atoi(sp[14]);
+ ms->val[3]=atoi(sp[15]);
+ ms->val[4]=atoi(sp[16]);
+ if(sp[17] != NULL && strlen(sp[17])>2)
+ ms->emotion=atoi(sp[17]);
+ else
+ ms->emotion=-1;
+ if (mob_id < 0)
+ { //Set this skill to ALL mobs. [Skotlex]
+ mob_id *= -1;
+ for (i = 1; i < MAX_MOB_DB; i++)
+ {
+ if (mob_db_data[i] == NULL)
+ continue;
+ if (mob_db_data[i]->mode&MD_BOSS)
+ {
+ if (!(mob_id&2)) //Skill not for bosses
+ continue;
+ } else
+ if (!(mob_id&1)) //Skill not for normal enemies.
+ continue;
+
+ for(j=0;j<MAX_MOBSKILL;j++)
+ if( mob_db_data[i]->skill[j].skill_id == 0)
+ break;
+ if(j==MAX_MOBSKILL)
+ continue;
+
+ memcpy (&mob_db_data[i]->skill[j], ms, sizeof(struct mob_skill));
+ mob_db_data[i]->maxskill=j+1;
+ }
+ } else //Skill set on a single mob.
+ mob_db_data[mob_id]->maxskill=i+1;
+ }
+ fclose(fp);
+ ShowStatus("Done reading '"CL_WHITE"%s"CL_RESET"'.\n",filename[x]);
+ }
+ return 0;
+}
+/*==========================================
+ * mob_race_db.txt reading
+ *------------------------------------------
+ */
+static int mob_readdb_race(void)
+{
+ FILE *fp;
+ char line[1024];
+ int race,j,k;
+ char *str[20],*p,*np;
+
+ sprintf(line, "%s/mob_race2_db.txt", db_path);
+ if( (fp=fopen(line,"r"))==NULL ){
+ ShowError("can't read %s\n", line);
+ return -1;
+ }
+
+ while(fgets(line,1020,fp)){
+ if(line[0]=='/' && line[1]=='/')
+ continue;
+ memset(str,0,sizeof(str));
+
+ for(j=0,p=line;j<12;j++){
+ if((np=strchr(p,','))!=NULL){
+ str[j]=p;
+ *np=0;
+ p=np+1;
+ } else
+ str[j]=p;
+ }
+ if(str[0]==NULL)
+ continue;
+
+ race=atoi(str[0]);
+ if (race < 0 || race >= MAX_MOB_RACE_DB)
+ continue;
+
+ for (j=1; j<20; j++) {
+ if (!str[j])
+ break;
+ k=atoi(str[j]);
+ if (mob_db(k) == mob_dummy)
+ continue;
+ mob_db_data[k]->race2 = race;
+ }
+ }
+ fclose(fp);
+ ShowStatus("Done reading '"CL_WHITE"%s"CL_RESET"'.\n","mob_race2_db.txt");
+ return 0;
+}
+
+#ifndef TXT_ONLY
+/*==========================================
+ * SQL reading
+ *------------------------------------------
+ */
+static int mob_read_sqldb(void)
+{
+ const char unknown_str[NAME_LENGTH] ="unknown";
+ int i, fi, class_;
+ double exp, maxhp;
+ long unsigned int ln = 0;
+ char *mob_db_name[] = { mob_db_db, mob_db2_db };
+
+ //For easier handling of converting. [Skotlex]
+#define TO_INT(a) (sql_row[a]==NULL?0:atoi(sql_row[a]))
+#define TO_STR(a) (sql_row[a]==NULL?unknown_str:sql_row[a])
+
+ for (fi = 0; fi < 2; fi++) {
+ sprintf (tmp_sql, "SELECT * FROM `%s`", mob_db_name[fi]);
+ if (mysql_query(&mmysql_handle, tmp_sql)) {
+ ShowSQL("DB error (%s) - %s\n", mob_db_name[fi], mysql_error(&mmysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ continue;
+ }
+ sql_res = mysql_store_result(&mmysql_handle);
+ if (sql_res) {
+ while((sql_row = mysql_fetch_row(sql_res))){
+ class_ = TO_INT(0);
+ if (class_ <= 1000 || class_ > MAX_MOB_DB)
+ {
+ ShowWarning("Mob with ID: %d not loaded. ID must be in range [%d-%d]\n", class_, 1000, MAX_MOB_DB);
+ continue;
+ } else if (pcdb_checkid(class_))
+ {
+ ShowWarning("Mob with ID: %d not loaded. That ID is reserved for Upper Classes.\n");
+ continue;
+ }
+ if (mob_db_data[class_] == NULL)
+ mob_db_data[class_] = aCalloc(1, sizeof (struct mob_data));
+
+ ln++;
+
+ mob_db_data[class_]->view_class = class_;
+ memcpy(mob_db_data[class_]->name, TO_STR(1), NAME_LENGTH-1);
+ memcpy(mob_db_data[class_]->jname, TO_STR(2), NAME_LENGTH-1);
+ mob_db_data[class_]->lv = TO_INT(3);
+ mob_db_data[class_]->max_hp = TO_INT(4);
+ mob_db_data[class_]->max_sp = TO_INT(5);
+
+ exp = (double)TO_INT(6) * (double)battle_config.base_exp_rate / 100.;
+ if (exp < 0) exp = 0;
+ else if (exp > 0x7fffffff) exp = 0x7fffffff;
+ mob_db_data[class_]->base_exp = (int)exp;
+
+ exp = (double)TO_INT(7) * (double)battle_config.job_exp_rate / 100.;
+ if (exp < 0) exp = 0;
+ else if (exp > 0x7fffffff) exp = 0x7fffffff;
+ mob_db_data[class_]->job_exp = (int)exp;
+
+ mob_db_data[class_]->range = TO_INT(8);
+ mob_db_data[class_]->atk1 = TO_INT(9);
+ mob_db_data[class_]->atk2 = TO_INT(10);
+ mob_db_data[class_]->def = TO_INT(11);
+ mob_db_data[class_]->mdef = TO_INT(12);
+ mob_db_data[class_]->str = TO_INT(13);
+ mob_db_data[class_]->agi = TO_INT(14);
+ mob_db_data[class_]->vit = TO_INT(15);
+ mob_db_data[class_]->int_ = TO_INT(16);
+ mob_db_data[class_]->dex = TO_INT(17);
+ mob_db_data[class_]->luk = TO_INT(18);
+ mob_db_data[class_]->range2 = TO_INT(19);
+ mob_db_data[class_]->range3 = TO_INT(20);
+ mob_db_data[class_]->size = TO_INT(21);
+ mob_db_data[class_]->race = TO_INT(22);
+ mob_db_data[class_]->element = TO_INT(23);
+ mob_db_data[class_]->mode = TO_INT(24);
+ mob_db_data[class_]->speed = TO_INT(25);
+ mob_db_data[class_]->adelay = TO_INT(26);
+ mob_db_data[class_]->amotion = TO_INT(27);
+ mob_db_data[class_]->dmotion = TO_INT(28);
+
+ for (i = 0; i < 10; i++){ // 8 -> 10 Lupus
+ int rate = 0, rate_adjust, type, ratemin, ratemax;
+ struct item_data *id;
+ mob_db_data[class_]->dropitem[i].nameid=TO_INT(29+i*2);
+ type = itemdb_type(mob_db_data[class_]->dropitem[i].nameid);
+ rate = TO_INT(30+i*2);
+ if (class_ >= 1324 && class_ <= 1363)
+ { //Treasure box drop rates [Skotlex]
+ rate_adjust = battle_config.item_rate_treasure;
+ ratemin = battle_config.item_drop_treasure_min;
+ ratemax = battle_config.item_drop_treasure_max;
+ }
+ else switch(type)
+ {
+ case 0: // Added by Valaris
+ rate_adjust = battle_config.item_rate_heal;
+ ratemin = battle_config.item_drop_heal_min;
+ ratemax = battle_config.item_drop_heal_max;
+ break;
+ case 2:
+ rate_adjust = battle_config.item_rate_use;
+ ratemin = battle_config.item_drop_use_min;
+ ratemax = battle_config.item_drop_use_max; // End
+ break;
+ case 4:
+ case 5:
+ case 8: // Changed to include Pet Equip
+ rate_adjust = battle_config.item_rate_equip;
+ ratemin = battle_config.item_drop_equip_min;
+ ratemax = battle_config.item_drop_equip_max;
+ break;
+ case 6:
+ rate_adjust = battle_config.item_rate_card;
+ ratemin = battle_config.item_drop_card_min;
+ ratemax = battle_config.item_drop_card_max;
+ break;
+ default:
+ rate_adjust = battle_config.item_rate_common;
+ ratemin = battle_config.item_drop_common_min;
+ ratemax = battle_config.item_drop_common_max;
+ break;
+ }
+ mob_db_data[class_]->dropitem[i].p = mob_drop_adjust(rate, rate_adjust, ratemin, ratemax);
+
+ //calculate and store Max available drop chance of the item
+ id = itemdb_search(mob_db_data[class_]->dropitem[i].nameid);
+ if (mob_db_data[class_]->dropitem[i].p) {
+ if (id->maxchance==10000 || (id->maxchance < mob_db_data[class_]->dropitem[i].p) ) {
+ //item has bigger drop chance or sold in shops
+ id->maxchance = mob_db_data[class_]->dropitem[i].p;
+ }
+ }
+ }
+ // MVP EXP Bonus, Chance: MEXP,ExpPer
+ mob_db_data[class_]->mexp = TO_INT(49) * battle_config.mvp_exp_rate / 100;
+ mob_db_data[class_]->mexpper = TO_INT(50);
+ //Now that we know if it is an mvp or not,
+ //apply battle_config modifiers [Skotlex]
+ maxhp = (double)mob_db_data[class_]->max_hp;
+ if (mob_db_data[class_]->mexp > 0)
+ { //Mvp
+ if (battle_config.mvp_hp_rate != 100)
+ maxhp = maxhp * (double)battle_config.mvp_hp_rate /100.;
+ } else if (battle_config.monster_hp_rate != 100) //Normal mob
+ maxhp = maxhp * (double)battle_config.monster_hp_rate /100.;
+ if (maxhp < 0) maxhp = 1;
+ else if (maxhp > 0x7fffffff) maxhp = 0x7fffffff;
+ mob_db_data[class_]->max_hp = (int)maxhp;
+
+ // MVP Drops: MVP1id,MVP1per,MVP2id,MVP2per,MVP3id,MVP3per
+ for (i=0; i<3; i++) {
+ struct item_data *id;
+ mob_db_data[class_]->mvpitem[i].nameid = TO_INT(51+i*2);
+ mob_db_data[class_]->mvpitem[i].p = mob_drop_adjust(TO_INT(52+i*2),
+ battle_config.item_rate_mvp, battle_config.item_drop_mvp_min, battle_config.item_drop_mvp_max);
+
+ //calculate and store Max available drop chance of the MVP item
+ id = itemdb_search(mob_db_data[class_]->mvpitem[i].nameid);
+ if (mob_db_data[class_]->mvpitem[i].p) {
+ if (id->maxchance==10000 || (id->maxchance < mob_db_data[class_]->mvpitem[i].p/10+1) ) {
+ //item has bigger drop chance or sold in shops
+ id->maxchance = mob_db_data[class_]->mvpitem[i].p/10+1; //reduce MVP drop info to not spoil common drop rate
+ }
+ }
+ }
+ if (mob_db_data[class_]->max_hp <= 0) {
+ ShowWarning ("Mob %d (%s) has no HP, using poring data for it\n", class_, mob_db_data[class_]->jname);
+ mob_makedummymobdb(class_);
+ }
+ }
+
+ mysql_free_result(sql_res);
+ ShowStatus("Done reading '"CL_WHITE"%lu"CL_RESET"' entries in '"CL_WHITE"%s"CL_RESET"'.\n", ln, mob_db_name[fi]);
+ ln = 0;
+ }
+ }
+ return 0;
+}
+#endif /* not TXT_ONLY */
+
+void mob_reload(void)
+{
+ int i;
+#ifndef TXT_ONLY
+ if(db_use_sqldbs)
+ mob_read_sqldb();
+ else
+#endif /* TXT_ONLY */
+ mob_readdb();
+
+ mob_readdb_mobavail();
+ mob_read_randommonster();
+
+ //Mob skills need to be cleared before re-reading them. [Skotlex]
+ for (i = 0; i < MAX_MOB_DB; i++)
+ if (mob_db_data[i])
+ {
+ memset(&mob_db_data[i]->skill,0,sizeof(mob_db_data[i]->skill));
+ mob_db_data[i]->maxskill=0;
+ }
+ mob_readskilldb();
+ mob_readdb_race();
+}
+
+/*==========================================
+ * Circumference initialization of mob
+ *------------------------------------------
+ */
+int do_init_mob(void)
+{ //Initialize the mob database
+ memset(mob_db_data,0,sizeof(mob_db_data)); //Clear the array
+ mob_db_data[0] = aCalloc(1, sizeof (struct mob_data)); //This mob is used for random spawns
+ mob_makedummymobdb(0); //The first time this is invoked, it creates the dummy mob
+
+#ifndef TXT_ONLY
+ if(db_use_sqldbs)
+ mob_read_sqldb();
+ else
+#endif /* TXT_ONLY */
+ mob_readdb();
+
+ mob_readdb_mobavail();
+ mob_read_randommonster();
+ mob_readskilldb();
+ mob_readdb_race();
+
+ add_timer_func_list(mob_timer,"mob_timer");
+ add_timer_func_list(mob_delayspawn,"mob_delayspawn");
+ add_timer_func_list(mob_delay_item_drop,"mob_delay_item_drop");
+ add_timer_func_list(mob_ai_hard,"mob_ai_hard");
+ add_timer_func_list(mob_ai_lazy,"mob_ai_lazy");
+ add_timer_func_list(mobskill_castend_id,"mobskill_castend_id");
+ add_timer_func_list(mobskill_castend_pos,"mobskill_castend_pos");
+ add_timer_func_list(mob_timer_delete,"mob_timer_delete");
+ add_timer_func_list(mob_spawn_guardian_sub,"mob_spawn_guardian_sub");
+ add_timer_interval(gettick()+MIN_MOBTHINKTIME,mob_ai_hard,0,0,MIN_MOBTHINKTIME);
+ add_timer_interval(gettick()+MIN_MOBTHINKTIME*10,mob_ai_lazy,0,0,MIN_MOBTHINKTIME*10);
+
+ return 0;
+}
+
+/*==========================================
+ * Clean memory usage.
+ *------------------------------------------
+ */
+int do_final_mob(void)
+{
+ int i;
+ if (mob_dummy)
+ {
+ aFree(mob_dummy);
+ mob_dummy = NULL;
+ }
+ for (i = 0; i <= MAX_MOB_DB; i++)
+ {
+ if (mob_db_data[i] != NULL)
+ {
+ aFree(mob_db_data[i]);
+ mob_db_data[i] = NULL;
+ }
+ }
+
+ return 0;
+}
diff --git a/src/map/mob.h b/src/map/mob.h
new file mode 100644
index 000000000..e126a3bfb
--- /dev/null
+++ b/src/map/mob.h
@@ -0,0 +1,173 @@
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#ifndef _MOB_H_
+#define _MOB_H_
+
+#define MAX_RANDOMMONSTER 3
+#define MAX_MOB_RACE_DB 6
+#define MAX_MOB_DB 10000
+ /* Change this to increase the table size in your mob_db to accomodate
+ a larger mob database. Be sure to note that IDs 4001 to 4048 are reserved for advanced/baby/expanded classes.
+ */
+
+// These define the range of available IDs for clones. [Valaris]
+#define MOB_CLONE_START 9001
+#define MOB_CLONE_END 10000
+
+struct mob_skill {
+ short state;
+ short skill_id,skill_lv;
+ short permillage;
+ int casttime,delay;
+ short cancel;
+ short cond1,cond2;
+ short target;
+ int val[5];
+ short emotion;
+};
+
+struct mob_db {
+ char name[NAME_LENGTH],jname[NAME_LENGTH];
+ short lv;
+ int max_hp,max_sp;
+ int base_exp,job_exp;
+ int atk1,atk2;
+ int def,mdef;
+ int str,agi,vit,int_,dex,luk;
+ int range,range2,range3;
+ int size,race,element,mode;
+ short race2; // celest
+ int speed,adelay,amotion,dmotion;
+ int mexp,mexpper;
+ struct { int nameid,p; } dropitem[10]; //8 -> 10 Lupus
+ struct { int nameid,p; } mvpitem[3];
+ int view_class,sex;
+ short hair,hair_color,weapon,shield,head_top,head_mid,head_buttom,option,clothes_color; // [Valaris]
+ int equip; // [Valaris]
+ int summonper[MAX_RANDOMMONSTER];
+ int maxskill;
+ struct mob_skill skill[MAX_MOBSKILL];
+};
+
+enum {
+ MST_TARGET = 0,
+ MST_SELF,
+ MST_FRIEND,
+ MST_MASTER,
+ MST_AROUND5,
+ MST_AROUND6,
+ MST_AROUND7,
+ MST_AROUND8,
+ MST_AROUND1,
+ MST_AROUND2,
+ MST_AROUND3,
+ MST_AROUND4,
+ MST_AROUND = MST_AROUND4,
+
+ MSC_ALWAYS = 0x0000,
+ MSC_MYHPLTMAXRATE,
+ MSC_FRIENDHPLTMAXRATE,
+ MSC_MYSTATUSON,
+ MSC_MYSTATUSOFF,
+ MSC_FRIENDSTATUSON,
+ MSC_FRIENDSTATUSOFF,
+ MSC_ATTACKPCGT,
+ MSC_ATTACKPCGE,
+ MSC_SLAVELT,
+ MSC_SLAVELE,
+ MSC_CLOSEDATTACKED,
+ MSC_LONGRANGEATTACKED,
+ MSC_AFTERSKILL,
+ MSC_SKILLUSED ,
+ MSC_CASTTARGETED,
+ MSC_RUDEATTACKED,
+ MSC_MASTERHPLTMAXRATE,
+ MSC_MASTERATTACKED,
+ MSC_ALCHEMIST,
+};
+
+//Mob skill states.
+enum {
+ MSS_IDLE,
+ MSS_WALK,
+ MSS_LOOT,
+ MSS_DEAD,
+ MSS_BERSERK, //Aggressive mob attacking
+ MSS_ANGRY, //Mob retaliating from being attacked.
+ MSS_RUSH, //Mob following a player after being attacked.
+ MSS_FOLLOW, //Mob following a player without being attacked.
+};
+
+struct mob_db* mob_db(int class_);
+int mobdb_searchname(const char *str);
+int mobdb_checkid(const int id);
+int mob_once_spawn(struct map_session_data *sd,char *mapname,
+ int x,int y,const char *mobname,int class_,int amount,const char *event);
+int mob_once_spawn_area(struct map_session_data *sd,char *mapname,
+ int x0,int y0,int x1,int y1,
+ const char *mobname,int class_,int amount,const char *event);
+
+int mob_spawn_guardian(struct map_session_data *sd,char *mapname, // Spawning Guardians [Valaris]
+ int x,int y,const char *mobname,int class_,int amount,const char *event,int guardian); // Spawning Guardians [Valaris]
+int mob_guardian_guildchange(struct block_list *bl,va_list ap); //Change Guardian's ownership. [Skotlex]
+
+int mob_walktoxy(struct mob_data *md,int x,int y,int easy);
+int mob_randomwalk(struct mob_data *md,int tick);
+int mob_can_move(struct mob_data *md);
+
+int mob_target(struct mob_data *md,struct block_list *bl,int dist);
+int mob_unlocktarget(struct mob_data *md,int tick);
+int mob_stop_walking(struct mob_data *md,int type);
+int mob_stopattack(struct mob_data *);
+int mob_spawn(int);
+int mob_setdelayspawn(int);
+int mob_damage(struct block_list *,struct mob_data*,int,int);
+int mob_changestate(struct mob_data *md,int state,int type);
+int mob_heal(struct mob_data*,int);
+
+//Defines to speed up search.
+#define mob_get_viewclass(class_) mob_db(class_)->view_class
+#define mob_get_sex(class_) mob_db(class_)->sex
+#define mob_get_hair(class_) mob_db(class_)->hair
+#define mob_get_hair_color(class_) mob_db(class_)->hair_color
+#define mob_get_weapon(class_) mob_db(class_)->weapon
+#define mob_get_shield(class_) mob_db(class_)->shield
+#define mob_get_head_top(class_) mob_db(class_)->head_top
+#define mob_get_head_mid(class_) mob_db(class_)->head_mid
+#define mob_get_head_buttom(class_) mob_db(class_)->head_buttom
+#define mob_get_clothes_color(class_) mob_db(class_)->clothes_color
+#define mob_get_equip(class_) mob_db(class_)->equip
+
+int do_init_mob(void);
+int do_final_mob(void);
+
+void mob_unload(struct mob_data *md);
+int mob_remove_map(struct mob_data *md, int type);
+int mob_delete(struct mob_data *md);
+int mob_timer_delete(int tid, unsigned int tick, int id, int data);
+
+int mob_deleteslave(struct mob_data *md);
+
+
+int mob_random_class (int *value, size_t count);
+int mob_get_random_id(int type, int flag, int lv);
+int mob_class_change(struct mob_data *md,int class_);
+int mob_warp(struct mob_data *md,int m,int x,int y,int type);
+int mob_warpslave(struct block_list *bl, int range);
+
+int mobskill_use(struct mob_data *md,unsigned int tick,int event);
+int mobskill_event(struct mob_data *md,int flag);
+int mobskill_castend_id( int tid, unsigned int tick, int id,int data );
+int mobskill_castend_pos( int tid, unsigned int tick, int id,int data );
+int mob_summonslave(struct mob_data *md2,int *value,int amount,int skill_id);
+int mob_countslave(struct block_list *bl);
+
+int mob_is_clone(int class_);
+
+int mob_clone_spawn(struct map_session_data *sd, char *map, int x, int y, const char *event, int master_id, int mode, int flag, unsigned int duration);
+int mob_clone_delete(int class_);
+
+void mob_reload(void);
+
+#endif
diff --git a/src/map/npc.c b/src/map/npc.c
new file mode 100644
index 000000000..7042ea9aa
--- /dev/null
+++ b/src/map/npc.c
@@ -0,0 +1,2826 @@
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <string.h>
+#include <math.h>
+#include <time.h>
+
+#include "timer.h"
+#include "nullpo.h"
+#include "malloc.h"
+#include "map.h"
+#include "log.h"
+#include "npc.h"
+#include "clif.h"
+#include "intif.h"
+#include "pc.h"
+#include "status.h"
+#include "itemdb.h"
+#include "script.h"
+#include "mob.h"
+#include "pet.h"
+#include "battle.h"
+#include "skill.h"
+#include "grfio.h"
+#include "showmsg.h"
+
+#ifdef _WIN32
+#undef isspace
+#define isspace(x) (x == ' ' || x == '\t')
+#endif
+
+struct npc_src_list {
+ struct npc_src_list * next;
+// struct npc_src_list * prev; //[Shinomori]
+ char name[4];
+};
+
+static struct npc_src_list *npc_src_first=NULL;
+static struct npc_src_list *npc_src_last=NULL;
+static int npc_id=START_NPC_NUM;
+static int npc_warp=0;
+static int npc_shop=0;
+static int npc_script=0;
+static int npc_mob=0;
+static int npc_delay_mob=0;
+static int npc_cache_mob=0;
+char *current_file = NULL;
+int npc_get_new_npc_id(void){ return npc_id++; }
+
+static struct dbt *ev_db;
+static struct dbt *npcname_db;
+
+struct event_data {
+ struct npc_data *nd;
+ int pos;
+};
+static struct tm ev_tm_b; // 時計イベント用
+
+static int npc_walktimer(int,unsigned int,int,int); // [Valaris]
+static int npc_walktoxy_sub(struct npc_data *nd); // [Valaris]
+
+/*==========================================
+ * NPCの無効化/有効化
+ * npc_enable
+ * npc_enable_sub 有効時にOnTouchイベントを実行
+ *------------------------------------------
+ */
+int npc_enable_sub( struct block_list *bl, va_list ap )
+{
+ struct map_session_data *sd;
+ struct npc_data *nd;
+ //char *name=(char *)aCallocA(50,sizeof(char)); // fixed [Shinomori]
+
+ nullpo_retr(0, bl);
+ nullpo_retr(0, ap);
+ nullpo_retr(0, nd=va_arg(ap,struct npc_data *));
+ if(bl->type == BL_PC && (sd=(struct map_session_data *)bl)){
+ char name[50]; // need 24 + 9 for the "::OnTouch"
+
+ if (nd->flag&1) // 無効化されている
+ return 1;
+
+ if(sd->areanpc_id==nd->bl.id)
+ return 1;
+ sd->areanpc_id=nd->bl.id;
+
+ snprintf(name, 50, "%s::OnTouch", nd->exname); // exname to be specific. exname is the unique identifier for script events. [Lance]
+ npc_event(sd,name,0);
+ }
+ //aFree(name);
+ return 0;
+}
+int npc_enable(const char *name,int flag)
+{
+ struct npc_data *nd= strdb_get(npcname_db,(unsigned char*)name);
+ if (nd==NULL)
+ return 0;
+
+ if (flag&1) { // 有効化
+ nd->flag&=~1;
+ clif_spawnnpc(nd);
+ }else if (flag&2){
+ nd->flag&=~1;
+ nd->option = 0x0000;
+ clif_changeoption(&nd->bl);
+ }else if (flag&4){
+ nd->flag|=1;
+ nd->option = 0x0002;
+ clif_changeoption(&nd->bl);
+ }else{ // 無効化
+ nd->flag|=1;
+ clif_clearchar(&nd->bl,0);
+ }
+ if(flag&3 && (nd->u.scr.xs > 0 || nd->u.scr.ys >0))
+ map_foreachinarea( npc_enable_sub,nd->bl.m,nd->bl.x-nd->u.scr.xs,nd->bl.y-nd->u.scr.ys,nd->bl.x+nd->u.scr.xs,nd->bl.y+nd->u.scr.ys,BL_PC,nd);
+
+ return 0;
+}
+
+/*==========================================
+ * NPCを名前で探す
+ *------------------------------------------
+ */
+struct npc_data* npc_name2id(const char *name)
+{
+ return (struct npc_data *) strdb_get(npcname_db,(unsigned char*)name);
+}
+
+/*==========================================
+ * イベントキューのイベント処理
+ *------------------------------------------
+ */
+int npc_event_dequeue(struct map_session_data *sd)
+{
+ nullpo_retr(0, sd);
+
+ sd->npc_id=0;
+ if (sd->eventqueue[0][0]) { // キューのイベント処理
+ size_t ev;
+
+ // find an empty place in eventtimer list
+ for(ev=0;ev<MAX_EVENTTIMER;ev++)
+ if( sd->eventtimer[ev]==-1 )
+ break;
+ if(ev<MAX_EVENTTIMER)
+ { // generate and insert the timer
+ int i;
+ // copy the first event name
+ char *name=(char *)aMalloc(50*sizeof(char));
+ memcpy(name,sd->eventqueue[0],50);
+ // shift queued events down by one
+ for(i=1;i<MAX_EVENTQUEUE;i++)
+ memcpy(sd->eventqueue[i-1],sd->eventqueue[i],50);
+ // clear the last event
+ sd->eventqueue[MAX_EVENTQUEUE-1][0]=0;
+ // add the timer
+ sd->eventtimer[ev]=add_timer(gettick()+100,pc_eventtimer,sd->bl.id,(int)name);//!!todo!!
+
+ }else
+ ShowWarning("npc_event_dequeue: event timer is full !\n");
+ }
+ return 0;
+}
+
+/*==========================================
+ * イベントの遅延実行
+ *------------------------------------------
+ */
+int npc_event_timer(int tid,unsigned int tick,int id,int data)
+{
+ unsigned char *eventname = (unsigned char *)data;
+ struct event_data *ev = strdb_get(ev_db,eventname);
+ struct npc_data *nd;
+ struct map_session_data *sd=map_id2sd(id);
+ size_t i;
+
+ if((ev==NULL || (nd=ev->nd)==NULL))
+ {
+ if(battle_config.error_log)
+ ShowWarning("npc_event: event not found [%s]\n",eventname);
+ }
+ else
+ {
+ for(i=0;i<MAX_EVENTTIMER;i++) {
+ if( nd->eventtimer[i]==tid ) {
+ nd->eventtimer[i]=-1;
+ npc_event(sd,eventname,0); // sd NULL check is within
+ break;
+ }
+ }
+ if(i==MAX_EVENTTIMER && battle_config.error_log)
+ ShowWarning("npc_event_timer: event timer not found [%s]!\n",eventname);
+ }
+
+ aFree(eventname);
+ return 0;
+}
+
+int npc_timer_event(const unsigned char *eventname) // Added by RoVeRT
+{
+ struct event_data *ev=strdb_get(ev_db,(unsigned char*)eventname);
+ struct npc_data *nd;
+// int xs,ys;
+
+ if((ev==NULL || (nd=ev->nd)==NULL)){
+ ShowWarning("npc_event: event not found [%s]\n",eventname);
+ return 0;
+ }
+
+ run_script(nd->u.scr.script,ev->pos,nd->bl.id,nd->bl.id);
+
+ return 0;
+}
+/*
+int npc_timer_sub_sub(DBKey key,void *data,va_list ap) // Added by RoVeRT
+{
+ char *p=(char *)key;
+ struct event_data *ev=(struct event_data *)data;
+ int *c=va_arg(ap,int *);
+ int tick=0,ctick=gettick();
+ char temp[10];
+ char event[100];
+
+ if(ev->nd->bl.id==(int)*c && (p=strchr(p,':')) && p && strncasecmp("::OnTimer",p,8)==0 ){
+ sscanf(&p[9],"%s",temp);
+ tick=atoi(temp);
+
+ strcpy( event, ev->nd->name);
+ strcat( event, p);
+
+ if (ctick >= ev->nd->lastaction && ctick - ev->nd->timer >= tick) {
+ npc_timer_event(event);
+ ev->nd->lastaction = ctick;
+ }
+ }
+ return 0;
+}
+
+int npc_timer_sub(DBKey key,void *data,va_list ap) // Added by RoVeRT
+{
+ struct npc_data *nd=(struct npc_data*)data;
+
+ if(nd->timer == -1)
+ return 0;
+
+ sv_db->foreach(ev_db,npc_timer_sub_sub,&nd->bl.id);
+
+ return 0;
+}
+
+int npc_timer(int tid,unsigned int tick,int id,int data) // Added by RoVeRT
+{
+ npcname_db->foreach(npcname_db,npc_timer_sub);
+
+ aFree((void*)data);
+ return 0;
+}*/
+/*==========================================
+ * イベント用ラベルのエクスポート
+ * npc_parse_script->strdb_foreachから呼ばれる
+ *------------------------------------------
+ */
+int npc_event_export(char *lname,void *data,va_list ap)
+{
+ int pos=(int)data;
+ struct npc_data *nd=va_arg(ap,struct npc_data *);
+
+ if ((lname[0]=='O' || lname[0]=='o')&&(lname[1]=='N' || lname[1]=='n')) {
+ struct event_data *ev;
+ unsigned char buf[51];
+ char *p=strchr(lname,':');
+ // エクスポートされる
+ ev=(struct event_data *) aCalloc(sizeof(struct event_data), 1);
+ if (ev==NULL) {
+ ShowFatalError("npc_event_export: out of memory !\n");
+ exit(1);
+ }else if (p==NULL || (p-lname)>NAME_LENGTH) {
+ ShowFatalError("npc_event_export: label name error !\n");
+ exit(1);
+ }else{
+ ev->nd=nd;
+ ev->pos=pos;
+ *p='\0';
+ sprintf(buf,"%s::%s",nd->exname,lname);
+ *p=':';
+ strdb_put(ev_db,buf,ev);
+ }
+ }
+ return 0;
+}
+
+/*==========================================
+ * 全てのNPCのOn*イベント実行
+ *------------------------------------------
+ */
+int npc_event_doall_sub(DBKey key,void *data,va_list ap)
+{
+ unsigned char*p = key.str;
+ struct event_data *ev;
+ int *c;
+ int rid;
+ unsigned char *name;
+
+ ev=(struct event_data *)data;
+ c=va_arg(ap,int *);
+ name=va_arg(ap,unsigned char *);
+ rid=va_arg(ap, int);
+
+ if( (p=strchr(p,':')) && p && strcmpi(name,p)==0 ){
+ run_script(ev->nd->u.scr.script,ev->pos,rid,ev->nd->bl.id);
+ (*c)++;
+ }
+
+ return 0;
+}
+int npc_event_doall(const unsigned char *name)
+{
+ int c=0;
+ unsigned char buf[64]="::";
+
+ strncpy(buf+2,name,62);
+ ev_db->foreach(ev_db,npc_event_doall_sub,&c,buf,0);
+ return c;
+}
+int npc_event_doall_id(const unsigned char *name, int rid)
+{
+ int c=0;
+ unsigned char buf[64]="::";
+
+ strncpy(buf+2,name,62);
+ ev_db->foreach(ev_db,npc_event_doall_sub,&c,buf,rid);
+ return c;
+}
+
+int npc_event_do_sub(DBKey key,void *data,va_list ap)
+{
+ unsigned char *p = key.str;
+ struct event_data *ev;
+ int *c;
+ const unsigned char *name;
+
+ nullpo_retr(0, ev=(struct event_data *)data);
+ nullpo_retr(0, ap);
+ nullpo_retr(0, c=va_arg(ap,int *));
+
+ name=va_arg(ap,const unsigned char *);
+
+ if (p && strcmpi(name,p)==0 ) {
+ run_script(ev->nd->u.scr.script,ev->pos,0,ev->nd->bl.id);
+ (*c)++;
+ }
+
+ return 0;
+}
+int npc_event_do(const unsigned char *name)
+{
+ int c=0;
+
+ if (*name==':' && name[1]==':') {
+ return npc_event_doall(name+2);
+ }
+
+ ev_db->foreach(ev_db,npc_event_do_sub,&c,name);
+ return c;
+}
+
+/*==========================================
+ * 時計イベント実行
+ *------------------------------------------
+ */
+int npc_event_do_clock(int tid,unsigned int tick,int id,int data)
+{
+ time_t timer;
+ struct tm *t;
+ char buf[64];
+ char *day="";
+ int c=0;
+
+ time(&timer);
+ t=localtime(&timer);
+
+ switch (t->tm_wday) {
+ case 0: day = "Sun"; break;
+ case 1: day = "Mon"; break;
+ case 2: day = "Tue"; break;
+ case 3: day = "Wed"; break;
+ case 4: day = "Thu"; break;
+ case 5: day = "Fri"; break;
+ case 6: day = "Sat"; break;
+ }
+
+ if (t->tm_min != ev_tm_b.tm_min ) {
+ sprintf(buf,"OnMinute%02d",t->tm_min);
+ c+=npc_event_doall(buf);
+ sprintf(buf,"OnClock%02d%02d",t->tm_hour,t->tm_min);
+ c+=npc_event_doall(buf);
+ sprintf(buf,"On%s%02d%02d",day,t->tm_hour,t->tm_min);
+ c+=npc_event_doall(buf);
+ }
+ if (t->tm_hour!= ev_tm_b.tm_hour) {
+ sprintf(buf,"OnHour%02d",t->tm_hour);
+ c+=npc_event_doall(buf);
+ }
+ if (t->tm_mday!= ev_tm_b.tm_mday) {
+ sprintf(buf,"OnDay%02d%02d",t->tm_mon+1,t->tm_mday);
+ c+=npc_event_doall(buf);
+ }
+ memcpy(&ev_tm_b,t,sizeof(ev_tm_b));
+ return c;
+}
+/*==========================================
+ * OnInitイベント実行(&時計イベント開始)
+ *------------------------------------------
+ */
+int npc_event_do_oninit(void)
+{
+// int c = npc_event_doall("OnInit");
+ ShowStatus("Event '"CL_WHITE"OnInit"CL_RESET"' executed with '"
+ CL_WHITE"%d"CL_RESET"' NPCs.\n",npc_event_doall("OnInit"));
+
+ add_timer_interval(gettick()+100,
+ npc_event_do_clock,0,0,1000);
+
+ return 0;
+}
+/*==========================================
+ * OnTimer NPC event - by RoVeRT
+ *------------------------------------------
+ */
+int npc_addeventtimer(struct npc_data *nd,int tick,const char *name)
+{
+ int i;
+ for(i=0;i<MAX_EVENTTIMER;i++)
+ if( nd->eventtimer[i]==-1 )
+ break;
+ if(i<MAX_EVENTTIMER){
+ unsigned char *evname=(unsigned char *) aCallocA(NAME_LENGTH, sizeof(char));
+ if(evname==NULL){
+ ShowFatalError("npc_addeventtimer: out of memory !\n");exit(1);
+ }
+ memcpy(evname,name,NAME_LENGTH-1);
+ nd->eventtimer[i]=add_timer(gettick()+tick,
+ npc_event_timer,nd->bl.id,(int)evname);
+ }else
+ ShowWarning("npc_addtimer: event timer is full !\n");
+
+ return 0;
+}
+
+int npc_deleventtimer(struct npc_data *nd,const unsigned char *name)
+{
+ int i;
+ for(i=0;i<MAX_EVENTTIMER;i++)
+ if( nd->eventtimer[i]!=-1 && strcmp(
+ (unsigned char *)(get_timer(nd->eventtimer[i])->data), name)==0 ){
+ delete_timer(nd->eventtimer[i],npc_event_timer);
+ nd->eventtimer[i]=-1;
+ break;
+ }
+
+ return 0;
+}
+
+int npc_cleareventtimer(struct npc_data *nd)
+{
+ int i;
+ for(i=0;i<MAX_EVENTTIMER;i++)
+ if( nd->eventtimer[i]!=-1 ){
+ delete_timer(nd->eventtimer[i],npc_event_timer);
+ nd->eventtimer[i]=-1;
+ }
+
+ return 0;
+}
+
+int npc_do_ontimer_sub(DBKey key,void *data,va_list ap)
+{
+ unsigned char *p = key.str;
+ struct event_data *ev = (struct event_data *)data;
+ int *c = va_arg(ap,int *);
+// struct map_session_data *sd=va_arg(ap,struct map_session_data *);
+ int option = va_arg(ap,int);
+ int tick = 0;
+ char temp[10];
+ char event[50];
+
+ if(ev->nd->bl.id == (int)*c && (p = strchr(p,':')) && strnicmp("::OnTimer",p,8) == 0){
+ sscanf(&p[9], "%s", temp);
+ tick = atoi(temp);
+
+ strcpy(event, ev->nd->name);
+ strcat(event, p);
+
+ if (option!=0) {
+ npc_addeventtimer(ev->nd, tick, event);
+ } else {
+ npc_deleventtimer(ev->nd, event);
+ }
+ }
+ return 0;
+}
+int npc_do_ontimer(int npc_id, int option)
+{
+ ev_db->foreach(ev_db, npc_do_ontimer_sub, &npc_id, option);
+ return 0;
+}
+/*==========================================
+ * タイマーイベント用ラベルの取り込み
+ * npc_parse_script->strdb_foreachから呼ばれる
+ *------------------------------------------
+ */
+int npc_timerevent_import(char *lname,void *data,va_list ap)
+{
+ int pos=(int)data;
+ struct npc_data *nd=va_arg(ap,struct npc_data *);
+ int t=0,i=0;
+
+ if(sscanf(lname,"OnTimer%d%n",&t,&i)==1 && lname[i]==':') {
+ // タイマーイベント
+ struct npc_timerevent_list *te=nd->u.scr.timer_event;
+ int j,i=nd->u.scr.timeramount;
+ if(te==NULL) te=(struct npc_timerevent_list*)aMallocA(sizeof(struct npc_timerevent_list));
+ else te= (struct npc_timerevent_list*)aRealloc( te, sizeof(struct npc_timerevent_list) * (i+1) );
+ if(te==NULL){
+ ShowFatalError("npc_timerevent_import: out of memory !\n");
+ exit(1);
+ }
+ for(j=0;j<i;j++){
+ if(te[j].timer>t){
+ memmove(te+j+1,te+j,sizeof(struct npc_timerevent_list)*(i-j));
+ break;
+ }
+ }
+ te[j].timer=t;
+ te[j].pos=pos;
+ nd->u.scr.timer_event=te;
+ nd->u.scr.timeramount=i+1;
+ }
+ return 0;
+}
+/*==========================================
+ * タイマーイベント実行
+ *------------------------------------------
+ */
+int npc_timerevent(int tid,unsigned int tick,int id,int data)
+{
+ int next,t;
+ struct npc_data* nd=(struct npc_data *)map_id2bl(id);
+ struct npc_timerevent_list *te;
+ if( nd==NULL || nd->u.scr.nexttimer<0 ){
+ ShowError("npc_timerevent: ??\n");
+ return 0;
+ }
+ nd->u.scr.timertick=tick;
+ te=nd->u.scr.timer_event+ nd->u.scr.nexttimer;
+ nd->u.scr.timerid = -1;
+
+ t = nd->u.scr.timer+=data;
+ nd->u.scr.nexttimer++;
+ if( nd->u.scr.timeramount>nd->u.scr.nexttimer ){
+ next= nd->u.scr.timer_event[ nd->u.scr.nexttimer ].timer - t;
+ nd->u.scr.timerid = add_timer(tick+next,npc_timerevent,id,next);
+ }
+
+ run_script(nd->u.scr.script,te->pos,nd->u.scr.rid,nd->bl.id);
+ return 0;
+}
+/*==========================================
+ * タイマーイベント開始
+ *------------------------------------------
+ */
+int npc_timerevent_start(struct npc_data *nd, int rid)
+{
+ int j,n, next;
+
+ nullpo_retr(0, nd);
+
+ n=nd->u.scr.timeramount;
+ if( nd->u.scr.nexttimer>=0 || n==0 )
+ return 0;
+
+ for(j=0;j<n;j++){
+ if( nd->u.scr.timer_event[j].timer > nd->u.scr.timer )
+ break;
+ }
+ if(j>=n) // check if there is a timer to use !!BEFORE!! you write stuff to the structures [Shinomori]
+ return 0;
+
+ nd->u.scr.nexttimer=j;
+ nd->u.scr.timertick=gettick();
+ if (rid >= 0) nd->u.scr.rid=rid; // changed to: attaching to given rid by default [Shinomori]
+ // if rid is less than 0 leave it unchanged [celest]
+
+ next = nd->u.scr.timer_event[j].timer - nd->u.scr.timer;
+ nd->u.scr.timerid = add_timer(gettick()+next,npc_timerevent,nd->bl.id,next);
+ return 0;
+}
+/*==========================================
+ * タイマーイベント終了
+ *------------------------------------------
+ */
+int npc_timerevent_stop(struct npc_data *nd)
+{
+ nullpo_retr(0, nd);
+
+ if( nd->u.scr.nexttimer>=0 ){
+ nd->u.scr.nexttimer = -1;
+ nd->u.scr.timer += (int)(gettick() - nd->u.scr.timertick);
+ if(nd->u.scr.timerid!=-1)
+ delete_timer(nd->u.scr.timerid,npc_timerevent);
+ nd->u.scr.timerid = -1;
+ nd->u.scr.rid = 0;
+ }
+ return 0;
+}
+/*==========================================
+ * タイマー値の所得
+ *------------------------------------------
+ */
+int npc_gettimerevent_tick(struct npc_data *nd)
+{
+ int tick;
+
+ nullpo_retr(0, nd);
+
+ tick=nd->u.scr.timer;
+
+ if( nd->u.scr.nexttimer>=0 )
+ tick += (int)(gettick() - nd->u.scr.timertick);
+ return tick;
+}
+/*==========================================
+ * タイマー値の設定
+ *------------------------------------------
+ */
+int npc_settimerevent_tick(struct npc_data *nd,int newtimer)
+{
+ int flag;
+
+ nullpo_retr(0, nd);
+
+ flag= nd->u.scr.nexttimer;
+
+ npc_timerevent_stop(nd);
+ nd->u.scr.timer=newtimer;
+ if(flag>=0)
+ npc_timerevent_start(nd, -1);
+ return 0;
+}
+
+/*==========================================
+ * イベント型のNPC処理
+ *------------------------------------------
+ */
+int npc_event (struct map_session_data *sd, const unsigned char *eventname, int mob_kill)
+{
+ struct event_data *ev=strdb_get(ev_db,(unsigned char*)eventname);
+ struct npc_data *nd;
+ int xs,ys;
+ unsigned char mobevent[100];
+
+ if (sd == NULL)
+ nullpo_info(NLP_MARK);
+
+ if (ev == NULL && eventname && strcmp(((eventname)+strlen(eventname)-9),"::OnTouch") == 0)
+ return 1;
+
+ if (ev == NULL || (nd = ev->nd) == NULL) {
+ if (mob_kill) {
+ strcpy( mobevent, eventname);
+ strcat( mobevent, "::OnMyMobDead");
+ ev = strdb_get(ev_db, mobevent);
+ if (ev == NULL || (nd = ev->nd) == NULL) {
+ if (strnicmp(eventname, "GM_MONSTER",10) != 0)
+ ShowError("npc_event: event not found [%s]\n", mobevent);
+ return 0;
+ }
+ } else {
+ if (battle_config.error_log)
+ ShowError("npc_event: event not found [%s]\n", eventname);
+ return 0;
+ }
+ }
+
+ xs=nd->u.scr.xs;
+ ys=nd->u.scr.ys;
+ if (xs>=0 && ys>=0 && (strcmp(((eventname)+strlen(eventname)-6),"Global") != 0) ) {
+ if (nd->bl.m != sd->bl.m )
+ return 1;
+ if ( xs>0 && (sd->bl.x<nd->bl.x-xs/2 || nd->bl.x+xs/2<sd->bl.x) )
+ return 1;
+ if ( ys>0 && (sd->bl.y<nd->bl.y-ys/2 || nd->bl.y+ys/2<sd->bl.y) )
+ return 1;
+ }
+
+ if ( sd->npc_id!=0) {
+// if (battle_config.error_log)
+// printf("npc_event: npc_id != 0\n");
+ int i;
+ for(i=0;i<MAX_EVENTQUEUE;i++)
+ if (!sd->eventqueue[i][0])
+ break;
+ if (i==MAX_EVENTQUEUE) {
+ if (battle_config.error_log)
+ ShowWarning("npc_event: event queue is full !\n");
+ }else{
+// if (battle_config.etc_log)
+// printf("npc_event: enqueue\n");
+ memcpy(sd->eventqueue[i],eventname,50);
+ }
+ return 1;
+ }
+ if (nd->flag&1) { // 無効化されている
+ npc_event_dequeue(sd);
+ return 0;
+ }
+
+ sd->npc_id=nd->bl.id;
+ sd->npc_pos=run_script(nd->u.scr.script,ev->pos,sd->bl.id,nd->bl.id);
+ return 0;
+}
+
+
+int npc_command_sub(DBKey key,void *data,va_list ap)
+{
+ unsigned char *p = key.str;
+ struct event_data *ev=(struct event_data *)data;
+ unsigned char *npcname=va_arg(ap,char *);
+ char *command=va_arg(ap,char *);
+ unsigned char temp[100];
+
+ if(strcmp(ev->nd->name,npcname)==0 && (p=strchr(p,':')) && p && strnicmp("::OnCommand",p,10)==0 ){
+ sscanf(&p[11],"%s",temp);
+
+ if (strcmp(command,temp)==0)
+ run_script(ev->nd->u.scr.script,ev->pos,0,ev->nd->bl.id);
+ }
+
+ return 0;
+}
+
+int npc_command(struct map_session_data *sd,const unsigned char *npcname,char *command)
+{
+ ev_db->foreach(ev_db,npc_command_sub,npcname,command);
+
+ return 0;
+}
+/*==========================================
+ * 接触型のNPC処理
+ *------------------------------------------
+ */
+int npc_touch_areanpc(struct map_session_data *sd,int m,int x,int y)
+{
+ int i,f=1;
+ int xs,ys;
+
+ nullpo_retr(1, sd);
+
+ if(sd->npc_id)
+ return 1;
+
+ for(i=0;i<map[m].npc_num;i++) {
+ if (map[m].npc[i]->flag&1) { // 無効化されている
+ f=0;
+ continue;
+ }
+
+ switch(map[m].npc[i]->bl.subtype) {
+ case WARP:
+ xs=map[m].npc[i]->u.warp.xs;
+ ys=map[m].npc[i]->u.warp.ys;
+ break;
+ case SCRIPT:
+ xs=map[m].npc[i]->u.scr.xs;
+ ys=map[m].npc[i]->u.scr.ys;
+ break;
+ default:
+ continue;
+ }
+ if (x >= map[m].npc[i]->bl.x-xs/2 && x < map[m].npc[i]->bl.x-xs/2+xs &&
+ y >= map[m].npc[i]->bl.y-ys/2 && y < map[m].npc[i]->bl.y-ys/2+ys)
+ break;
+ }
+ if (i==map[m].npc_num) {
+ if (f) {
+ if (battle_config.error_log)
+ ShowError("npc_touch_areanpc : some bug \n");
+ }
+ return 1;
+ }
+ switch(map[m].npc[i]->bl.subtype) {
+ case WARP:
+ // hidden chars cannot use warps -- is it the same for scripts too?
+ if (sd->status.option&6 ||
+ (!battle_config.duel_allow_teleport && sd->duel_group)) // duel rstrct [LuzZza]
+ break;
+ skill_stop_dancing(&sd->bl);
+ pc_setpos(sd,map[m].npc[i]->u.warp.mapindex,map[m].npc[i]->u.warp.x,map[m].npc[i]->u.warp.y,0);
+ break;
+ case SCRIPT:
+ {
+ //char *name=(char *)aCallocA(50,sizeof(char)); // fixed [Shinomori]
+ char name[50]; // need 24 max + 9 for "::OnTouch"
+
+ if(sd->areanpc_id == map[m].npc[i]->bl.id)
+ return 1;
+ sd->areanpc_id = map[m].npc[i]->bl.id;
+
+ sprintf(name,"%s::OnTouch", map[m].npc[i]->exname); // It goes here too. exname being the unique identifier. [Lance]
+
+ if( npc_event(sd,name,0)>0 )
+ npc_click(sd,map[m].npc[i]->bl.id);
+ //aFree(name);
+ break;
+ }
+ }
+ return 0;
+}
+
+/*==========================================
+ * 近くかどうかの判定
+ *------------------------------------------
+ */
+int npc_checknear(struct map_session_data *sd,int id)
+{
+ struct npc_data *nd;
+
+ nullpo_retr(0, sd);
+
+ nd=(struct npc_data *)map_id2bl(id);
+ if (nd==NULL || nd->bl.type!=BL_NPC) {
+ if (battle_config.error_log)
+ ShowWarning("no such npc : %d\n",id);
+ return 1;
+ }
+
+ if (nd->class_<0) // イベント系は常にOK
+ return 0;
+
+ // エリア判定
+ if (nd->bl.m!=sd->bl.m ||
+ nd->bl.x<sd->bl.x-AREA_SIZE-1 || nd->bl.x>sd->bl.x+AREA_SIZE+1 ||
+ nd->bl.y<sd->bl.y-AREA_SIZE-1 || nd->bl.y>sd->bl.y+AREA_SIZE+1)
+ return 1;
+
+ return 0;
+}
+
+/*==========================================
+ * NPCのオープンチャット発言
+ *------------------------------------------
+ */
+int npc_globalmessage(const char *name,char *mes)
+{
+ struct npc_data *nd=(struct npc_data *) strdb_get(npcname_db,(unsigned char*)name);
+ char temp[100];
+
+ if (!nd)
+ return 0;
+
+ snprintf(temp, sizeof temp ,"%s : %s",name,mes);
+ clif_GlobalMessage(&nd->bl,temp);
+
+ return 0;
+}
+
+/*==========================================
+ * クリック時のNPC処理
+ *------------------------------------------
+ */
+int npc_click(struct map_session_data *sd,int id)
+{
+ struct npc_data *nd;
+
+ nullpo_retr(1, sd);
+
+ if (sd->npc_id != 0) {
+ if (battle_config.error_log)
+ ShowError("npc_click: npc_id != 0\n");
+ return 1;
+ }
+
+ if (npc_checknear(sd,id))
+ return 1;
+
+ nd=(struct npc_data *)map_id2bl(id);
+
+ if (nd->flag&1) // 無効化されている
+ return 1;
+
+ sd->npc_id=id;
+ switch(nd->bl.subtype) {
+ case SHOP:
+ clif_npcbuysell(sd,id);
+ npc_event_dequeue(sd);
+ break;
+ case SCRIPT:
+ sd->npc_pos=run_script(nd->u.scr.script,0,sd->bl.id,id);
+ break;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int npc_scriptcont(struct map_session_data *sd,int id)
+{
+ struct npc_data *nd;
+
+ nullpo_retr(1, sd);
+
+ if (id!=sd->npc_id)
+ return 1;
+ if (npc_checknear(sd,id))
+ return 1;
+
+ nd=(struct npc_data *)map_id2bl(id);
+
+ sd->npc_pos=run_script(nd->u.scr.script,sd->npc_pos,sd->bl.id,id);
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int npc_buysellsel(struct map_session_data *sd,int id,int type)
+{
+ struct npc_data *nd;
+
+ nullpo_retr(1, sd);
+
+ if (npc_checknear(sd,id))
+ return 1;
+
+ nd=(struct npc_data *)map_id2bl(id);
+ if (nd->bl.subtype!=SHOP) {
+ if (battle_config.error_log)
+ ShowError("no such shop npc : %d\n",id);
+ sd->npc_id=0;
+ return 1;
+ }
+ if (nd->flag&1) // 無効化されている
+ return 1;
+
+ sd->npc_shopid=id;
+ if (type==0) {
+ clif_buylist(sd,nd);
+ } else {
+ clif_selllist(sd);
+ }
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int npc_buylist(struct map_session_data *sd,int n,unsigned short *item_list)
+{
+ struct npc_data *nd;
+ double z;
+ int i,j,w,skill,itemamount=0,new_=0;
+
+ nullpo_retr(3, sd);
+ nullpo_retr(3, item_list);
+
+ if (npc_checknear(sd,sd->npc_shopid))
+ return 3;
+
+ nd=(struct npc_data*)map_id2bl(sd->npc_shopid);
+ if (nd->bl.subtype!=SHOP)
+ return 3;
+
+ for(i=0,w=0,z=0;i<n;i++) {
+ for(j=0;nd->u.shop_item[j].nameid;j++) {
+ if (nd->u.shop_item[j].nameid==item_list[i*2+1])
+ break;
+ }
+ if (nd->u.shop_item[j].nameid==0)
+ return 3;
+
+ if (itemdb_isequip3(nd->u.shop_item[j].nameid) && item_list[i*2] > 1)
+ { //Exploit? You can't buy more than 1 of equipment types o.O
+ ShowWarning("Player %s (%d:%d) sent a hexed packet trying to buy %d of nonstackable item %d!\n",
+ sd->status.name, sd->status.account_id, sd->status.char_id, item_list[i*2], nd->u.shop_item[j].nameid);
+ item_list[i*2] = 1;
+ }
+ if (itemdb_value_notdc(nd->u.shop_item[j].nameid))
+ z+=(double)nd->u.shop_item[j].value * item_list[i*2];
+ else
+ z+=(double)pc_modifybuyvalue(sd,nd->u.shop_item[j].value) * item_list[i*2];
+ itemamount+=item_list[i*2];
+
+ switch(pc_checkadditem(sd,item_list[i*2+1],item_list[i*2])) {
+ case ADDITEM_EXIST:
+ break;
+ case ADDITEM_NEW:
+ new_++;
+ break;
+ case ADDITEM_OVERAMOUNT:
+ return 2;
+ }
+
+ w+=itemdb_weight(item_list[i*2+1]) * item_list[i*2];
+ }
+ if (z > (double)sd->status.zeny)
+ return 1; // zeny不足
+ if (w+sd->weight > sd->max_weight)
+ return 2; // 重量超過
+ if (pc_inventoryblank(sd)<new_)
+ return 3; // 種類数超過
+
+ //Logs (S)hopping Zeny [Lupus]
+ if(log_config.zeny > 0 )
+ log_zeny(sd, "S", sd, -(int)z);
+ //Logs
+
+ pc_payzeny(sd,(int)z);
+ for(i=0;i<n;i++) {
+ struct item item_tmp;
+
+ memset(&item_tmp,0,sizeof(item_tmp));
+ item_tmp.nameid = item_list[i*2+1];
+ item_tmp.identify = 1; // npc販売アイテムは鑑定済み
+
+ pc_additem(sd,&item_tmp,item_list[i*2]);
+
+ //Logs items, Bought in NPC (S)hop [Lupus]
+ if(sd && log_config.pick > 0 )
+ log_pick(sd, "S", 0, item_tmp.nameid, item_list[i*2], NULL);
+ //Logs
+ }
+
+ //商人経験値
+ if (battle_config.shop_exp > 0 && z > 0 && (skill = pc_checkskill(sd,MC_DISCOUNT)) > 0) {
+ if (sd->status.skill[MC_DISCOUNT].flag != 0)
+ skill = sd->status.skill[MC_DISCOUNT].flag - 2;
+ if (skill > 0) {
+ z = z * (double)skill * (double)battle_config.shop_exp/10000.;
+ if (z < 1)
+ z = 1;
+ pc_gainexp(sd,0,(int)z);
+ }
+ }
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int npc_selllist(struct map_session_data *sd,int n,unsigned short *item_list)
+{
+ double z;
+ int i,skill,itemamount=0;
+
+ nullpo_retr(1, sd);
+ nullpo_retr(1, item_list);
+
+ if (npc_checknear(sd,sd->npc_shopid))
+ return 1;
+ for(i=0,z=0;i<n;i++) {
+ int nameid;
+ if (item_list[i*2]-2 <0 || item_list[i*2]-2 >=MAX_INVENTORY)
+ return 1;
+ nameid=sd->status.inventory[item_list[i*2]-2].nameid;
+ if (nameid == 0 ||
+ sd->status.inventory[item_list[i*2]-2].amount < item_list[i*2+1])
+ return 1;
+ if (itemdb_value_notoc(nameid))
+ z+=(double)itemdb_value_sell(nameid) * item_list[i*2+1];
+ else
+ z+=(double)pc_modifysellvalue(sd,itemdb_value_sell(nameid)) * item_list[i*2+1];
+ itemamount+=item_list[i*2+1];
+ }
+
+ if (z > MAX_ZENY) z = MAX_ZENY;
+
+ //Logs (S)hopping Zeny [Lupus]
+ if(log_config.zeny > 0 )
+ log_zeny(sd, "S", sd, (int)z);
+ //Logs
+
+ pc_getzeny(sd,(int)z);
+ for(i=0;i<n;i++) {
+ int item_id=item_list[i*2]-2;
+ if( sd->status.inventory[item_id].nameid>0 && sd->inventory_data[item_id] != NULL &&
+ sd->inventory_data[item_id]->type==7 && sd->status.inventory[item_id].amount>0 &&
+ sd->status.inventory[item_id].card[0] == (short)0xff00)
+ if(search_petDB_index(sd->status.inventory[item_id].nameid, PET_EGG) >= 0)
+ intif_delete_petdata(MakeDWord(sd->status.inventory[item_id].card[1],sd->status.inventory[item_id].card[2]));
+
+ //Logs items, Sold to NPC (S)hop [Lupus]
+ if(sd && log_config.pick > 0 )
+ log_pick(sd, "S", 0, sd->status.inventory[item_id].nameid, -item_list[i*2+1], &sd->status.inventory[item_id]);
+ //Logs
+
+ pc_delitem(sd,item_id,item_list[i*2+1],0);
+ }
+
+ //商人経験値
+ if (battle_config.shop_exp > 0 && z > 0 && (skill = pc_checkskill(sd,MC_OVERCHARGE)) > 0) {
+ if (sd->status.skill[MC_OVERCHARGE].flag != 0)
+ skill = sd->status.skill[MC_OVERCHARGE].flag - 2;
+ if (skill > 0) {
+ z = z * (double)skill * (double)battle_config.shop_exp/10000.;
+ if (z < 1)
+ z = 1;
+ pc_gainexp(sd,0,(int)z);
+ }
+ }
+
+ return 0;
+
+}
+
+// [Valaris] NPC Walking
+
+/*==========================================
+ * Time calculation concerning one step next to npc
+ *------------------------------------------
+ */
+static int calc_next_walk_step(struct npc_data *nd)
+{
+ nullpo_retr(0, nd);
+
+ if(nd->walkpath.path_pos>=nd->walkpath.path_len)
+ return -1;
+ if(nd->walkpath.path[nd->walkpath.path_pos]&1)
+ return status_get_speed(&nd->bl)*14/10;
+ return status_get_speed(&nd->bl);
+}
+
+
+/*==========================================
+ * npc Walk processing
+ *------------------------------------------
+ */
+static int npc_walk(struct npc_data *nd,unsigned int tick,int data)
+{
+ int i;
+ static int dirx[8]={0,-1,-1,-1,0,1,1,1};
+ static int diry[8]={1,1,0,-1,-1,-1,0,1};
+ int x,y,dx,dy;
+
+ nullpo_retr(0, nd);
+
+ nd->state.state=MS_IDLE;
+ if(nd->walkpath.path_pos>=nd->walkpath.path_len || nd->walkpath.path_pos!=data)
+ return 0;
+
+ nd->walkpath.path_half ^= 1;
+ if(nd->walkpath.path_half==0){
+ nd->walkpath.path_pos++;
+ if(nd->state.change_walk_target){
+ npc_walktoxy_sub(nd);
+ return 0;
+ }
+ }
+ else {
+ if(nd->walkpath.path[nd->walkpath.path_pos]>=8)
+ return 1;
+
+ x = nd->bl.x;
+ y = nd->bl.y;
+ if(map_getcell(nd->bl.m,x,y,CELL_CHKNOPASS)) {
+ npc_stop_walking(nd,1);
+ return 0;
+ }
+ nd->dir=nd->walkpath.path[nd->walkpath.path_pos];
+ dx = dirx[nd->dir];
+ dy = diry[nd->dir];
+
+ if(map_getcell(nd->bl.m,x+dx,y+dy,CELL_CHKNOPASS)) {
+ npc_walktoxy_sub(nd);
+ return 0;
+ }
+
+ nd->state.state=MS_WALK;
+ map_foreachinmovearea(clif_npcoutsight,nd->bl.m,x-AREA_SIZE,y-AREA_SIZE,x+AREA_SIZE,y+AREA_SIZE,dx,dy,BL_PC,nd);
+
+ x += dx;
+ y += dy;
+ map_moveblock(&nd->bl, x, y, tick);
+
+ map_foreachinmovearea(clif_npcinsight,nd->bl.m,x-AREA_SIZE,y-AREA_SIZE,x+AREA_SIZE,y+AREA_SIZE,-dx,-dy,BL_PC,nd);
+ nd->state.state=MS_IDLE;
+ }
+ if((i=calc_next_walk_step(nd))>0){
+ i = i>>1;
+ if(i < 1 && nd->walkpath.path_half == 0)
+ i = 1;
+ nd->walktimer=add_timer(tick+i,npc_walktimer,nd->bl.id,nd->walkpath.path_pos);
+ nd->state.state=MS_WALK;
+
+ if(nd->walkpath.path_pos>=nd->walkpath.path_len)
+ clif_fixnpcpos(nd); // When npc stops, retransmission current of a position.
+
+ }
+ return 0;
+}
+
+int npc_changestate(struct npc_data *nd,int state,int type)
+{
+ int i;
+
+ nullpo_retr(0, nd);
+
+ if(nd->walktimer != -1)
+ delete_timer(nd->walktimer,npc_walktimer);
+ nd->walktimer=-1;
+ nd->state.state=state;
+
+ switch(state){
+ case MS_WALK:
+ if((i=calc_next_walk_step(nd))>0){
+ i = i>>2;
+ nd->walktimer=add_timer(gettick()+i,npc_walktimer,nd->bl.id,0);
+ }
+ else
+ nd->state.state=MS_IDLE;
+ break;
+ case MS_DELAY:
+ nd->walktimer=add_timer(gettick()+type,npc_walktimer,nd->bl.id,0);
+ break;
+
+ }
+
+ return 0;
+}
+
+static int npc_walktimer(int tid,unsigned int tick,int id,int data)
+{
+ struct npc_data *nd;
+
+ nd=(struct npc_data*)map_id2bl(id);
+ if(nd == NULL || nd->bl.type != BL_NPC)
+ return 1;
+
+ if(nd->walktimer != tid){
+ return 0;
+ }
+
+ nd->walktimer=-1;
+
+ if(nd->bl.prev == NULL)
+ return 1;
+
+ switch(nd->state.state){
+ case MS_WALK:
+ npc_walk(nd,tick,data);
+ break;
+ case MS_DELAY:
+ npc_changestate(nd,MS_IDLE,0);
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
+
+static int npc_walktoxy_sub(struct npc_data *nd)
+{
+ struct walkpath_data wpd;
+
+ nullpo_retr(0, nd);
+
+ if(path_search(&wpd,nd->bl.m,nd->bl.x,nd->bl.y,nd->to_x,nd->to_y,nd->state.walk_easy))
+ return 1;
+ memcpy(&nd->walkpath,&wpd,sizeof(wpd));
+
+ nd->state.change_walk_target=0;
+ npc_changestate(nd,MS_WALK,0);
+
+ clif_movenpc(nd);
+
+ return 0;
+}
+
+int npc_walktoxy(struct npc_data *nd,int x,int y,int easy)
+{
+ struct walkpath_data wpd;
+
+ nullpo_retr(0, nd);
+
+ if(nd->state.state == MS_WALK && path_search(&wpd,nd->bl.m,nd->bl.x,nd->bl.y,x,y,0) )
+ return 1;
+
+ nd->state.walk_easy = easy;
+ nd->to_x=x;
+ nd->to_y=y;
+ if(nd->state.state == MS_WALK) {
+ nd->state.change_walk_target=1;
+ } else {
+ return npc_walktoxy_sub(nd);
+ }
+
+ return 0;
+}
+
+int npc_stop_walking(struct npc_data *nd,int type)
+{
+ nullpo_retr(0, nd);
+
+ if(nd->state.state == MS_WALK || nd->state.state == MS_IDLE) {
+ int dx=0,dy=0;
+
+ nd->walkpath.path_len=0;
+ if(type&4){
+ dx=nd->to_x-nd->bl.x;
+ if(dx<0)
+ dx=-1;
+ else if(dx>0)
+ dx=1;
+ dy=nd->to_y-nd->bl.y;
+ if(dy<0)
+ dy=-1;
+ else if(dy>0)
+ dy=1;
+ }
+ nd->to_x=nd->bl.x+dx;
+ nd->to_y=nd->bl.y+dy;
+ if(dx!=0 || dy!=0){
+ npc_walktoxy_sub(nd);
+ return 0;
+ }
+ npc_changestate(nd,MS_IDLE,0);
+ }
+ if(type&0x01)
+ clif_fixnpcpos(nd);
+ if(type&0x02) {
+ int delay=status_get_dmotion(&nd->bl);
+ unsigned int tick = gettick();
+ if(nd->canmove_tick < tick)
+ nd->canmove_tick = tick + delay;
+ }
+
+ return 0;
+}
+
+int npc_remove_map (struct npc_data *nd)
+{
+ int m,i;
+ nullpo_retr(1, nd);
+
+ if(nd->bl.prev == NULL || nd->bl.m < 0)
+ return 1; //Not assigned to a map.
+ m = nd->bl.m;
+#ifdef PCRE_SUPPORT
+ npc_chat_finalize(nd);
+#endif
+ clif_clearchar_area(&nd->bl,2);
+ strdb_remove(npcname_db, (nd->bl.subtype < SCRIPT) ? nd->name : nd->exname);
+ //Remove corresponding NPC CELLs
+ if (nd->bl.subtype == WARP) {
+ int j, xs, ys, x, y;
+ x = nd->bl.x;
+ y = nd->bl.y;
+ xs = nd->u.warp.xs;
+ ys = nd->u.warp.ys;
+
+ for (i = 0; i < ys; i++) {
+ for (j = 0; j < xs; j++) {
+ if (map_getcell(m, x-xs/2+j, y-ys/2+i, CELL_CHKNPC))
+ map_setcell(m, x-xs/2+j, y-ys/2+i, CELL_CLRNPC);
+ }
+ }
+ }
+ map_delblock(&nd->bl);
+ map_deliddb(&nd->bl);
+ //Remove npc from map[].npc list. [Skotlex]
+ for(i=0;i<map[m].npc_num && map[m].npc[i] != nd;i++);
+ if (i >= map[m].npc_num) return 2; //failed to find it?
+
+ map[m].npc_num--;
+ for(; i<map[m].npc_num; i++)
+ map[m].npc[i]=map[m].npc[i+1];
+ return 0;
+}
+
+static int npc_unload_ev(DBKey key,void *data,va_list ap) {
+ struct event_data *ev=(struct event_data *)data;
+ unsigned char *npcname=va_arg(ap,unsigned char *);
+
+ if(strcmp(ev->nd->exname,npcname)==0){
+ db_remove(ev_db, key);
+ return 1;
+ }
+ return 0;
+}
+
+int npc_unload (struct npc_data *nd)
+{
+ npc_remove_map (nd);
+
+ if (nd->chat_id) {
+ struct chat_data *cd = (struct chat_data*)map_id2bl(nd->chat_id);
+ if (cd) aFree (cd);
+ cd = NULL;
+ }
+ if (nd->bl.subtype == SCRIPT) {
+ ev_db->foreach(ev_db,npc_unload_ev,nd->exname); //Clean up all events related.
+ if (nd->u.scr.timerid != -1)
+ delete_timer(nd->u.scr.timerid, npc_timerevent);
+ npc_cleareventtimer (nd);
+ if (nd->u.scr.timer_event)
+ aFree(nd->u.scr.timer_event);
+ if (nd->u.scr.src_id == 0) {
+ if(nd->u.scr.script) {
+ aFree(nd->u.scr.script);
+ nd->u.scr.script = NULL;
+ }
+ if (nd->u.scr.label_list) {
+ aFree(nd->u.scr.label_list);
+ nd->u.scr.label_list = NULL;
+ }
+ }
+ }
+ aFree(nd);
+
+ return 0;
+}
+
+//
+// 初期化関係
+//
+
+/*==========================================
+ * 読み込むnpcファイルのクリア
+ *------------------------------------------
+ */
+void npc_clearsrcfile (void)
+{
+ struct npc_src_list *p = npc_src_first, *p2;
+
+ while (p) {
+ p2 = p;
+ p = p->next;
+ aFree(p2);
+ }
+ npc_src_first = NULL;
+ npc_src_last = NULL;
+}
+/*==========================================
+ * 読み込むnpcファイルの追加
+ *------------------------------------------
+ */
+void npc_addsrcfile (char *name)
+{
+ struct npc_src_list *nsl;
+
+ if (strcmpi(name, "clear") == 0) {
+ npc_clearsrcfile();
+ return;
+ }
+
+ // prevent multiple insert of source files
+ nsl = npc_src_first;
+ while (nsl)
+ { // found the file, no need to insert it again
+ if (0 == strcmp(name, nsl->name))
+ return;
+ nsl = nsl->next;
+ }
+
+ nsl = (struct npc_src_list *) aCalloc (1, sizeof(*nsl) + strlen(name));
+ nsl->next = NULL;
+ strncpy(nsl->name, name, strlen(name) + 1);
+ if (npc_src_first == NULL)
+ npc_src_first = nsl;
+ if (npc_src_last)
+ npc_src_last->next = nsl;
+ npc_src_last = nsl;
+}
+/*==========================================
+ * 読み込むnpcファイルの削除
+ *------------------------------------------
+ */
+void npc_delsrcfile (char *name)
+{
+ struct npc_src_list *p = npc_src_first, *pp = NULL, **lp = &npc_src_first;
+
+ if (strcmpi(name, "all") == 0) {
+ npc_clearsrcfile();
+ return;
+ }
+
+ while (p) {
+ if (strcmp(p->name, name) == 0) {
+ *lp = p->next;
+ if (npc_src_last == p)
+ npc_src_last = pp;
+ aFree(p);
+ break;
+ }
+ lp = &p->next;
+ pp = p;
+ p = p->next;
+ }
+}
+
+/*==========================================
+ * warp行解析
+ *------------------------------------------
+ */
+int npc_parse_warp (char *w1,char *w2,char *w3,char *w4)
+{
+ int x, y, xs, ys, to_x, to_y, m;
+ int i, j;
+ char mapname[MAP_NAME_LENGTH], to_mapname[MAP_NAME_LENGTH];
+ struct npc_data *nd;
+
+ // 引数の個数チェック
+ if (sscanf(w1, "%15[^,],%d,%d", mapname, &x, &y) != 3 ||
+ sscanf(w4, "%d,%d,%15[^,],%d,%d", &xs, &ys, to_mapname, &to_x, &to_y) != 5) {
+ ShowError("bad warp line : %s\n", w3);
+ return 1;
+ }
+
+ m = map_mapname2mapid(mapname);
+ i = mapindex_name2id(to_mapname);
+ if (!i) {
+ ShowError("bad warp line (destination map not found): %s\n", w3);
+ return 1;
+ }
+
+ nd = (struct npc_data *) aCalloc (1, sizeof(struct npc_data));
+
+ nd->bl.id = npc_get_new_npc_id();
+ nd->n = map_addnpc(m, nd);
+ nd->bl.prev = nd->bl.next = NULL;
+ nd->bl.m = m;
+ nd->bl.x = x;
+ nd->bl.y = y;
+ memcpy(nd->name, w3, NAME_LENGTH-1);
+ memcpy(nd->exname, w3, NAME_LENGTH-1);
+
+ if (!battle_config.warp_point_debug)
+ nd->class_ = WARP_CLASS;
+ else
+ nd->class_ = WARP_DEBUG_CLASS;
+ nd->speed = 200;
+
+ nd->u.warp.mapindex = (short)i;
+ xs += 2;
+ ys += 2;
+ nd->u.warp.x = to_x;
+ nd->u.warp.y = to_y;
+ nd->u.warp.xs = xs;
+ nd->u.warp.ys = ys;
+
+ for (i = 0; i < ys; i++) {
+ for (j = 0; j < xs; j++) {
+ if (map_getcell(m, x-xs/2+j, y-ys/2+i, CELL_CHKNOPASS))
+ continue;
+ map_setcell(m, x-xs/2+j, y-ys/2+i, CELL_SETNPC);
+ }
+ }
+
+ npc_warp++;
+ nd->bl.type = BL_NPC;
+ nd->bl.subtype = WARP;
+ map_addblock(&nd->bl);
+ clif_spawnnpc(nd);
+ strdb_put(npcname_db, nd->name, nd);
+
+ return 0;
+}
+
+/*==========================================
+ * shop行解析
+ *------------------------------------------
+ */
+static int npc_parse_shop (char *w1, char *w2, char *w3, char *w4)
+{
+ #define MAX_SHOPITEM 100
+ char *p;
+ int x, y, dir, m, pos = 0;
+ char mapname[MAP_NAME_LENGTH];
+ struct npc_data *nd;
+
+ if (strcmp(w1, "-") == 0) {
+ x = 0; y = 0; dir = 0; m = -1;
+ } else {
+ // 引数の個数チェック
+ if (sscanf(w1, "%15[^,],%d,%d,%d", mapname, &x, &y, &dir) != 4 ||
+ strchr(w4, ',') == NULL) {
+ ShowError("bad shop line : %s\n", w3);
+ return 1;
+ }
+ m = map_mapname2mapid(mapname);
+ }
+
+ nd = (struct npc_data *) aCalloc (1, sizeof(struct npc_data) +
+ sizeof(nd->u.shop_item[0]) * (MAX_SHOPITEM + 1));
+ p = strchr(w4, ',');
+
+ while (p && pos < MAX_SHOPITEM) {
+ int nameid, value;
+ struct item_data *id;
+ p++;
+ if (sscanf(p, "%d:%d", &nameid, &value) != 2)
+ break;
+ nd->u.shop_item[pos].nameid = nameid;
+ id = itemdb_search(nameid);
+ if (value < 0)
+ value = id->value_buy;
+ nd->u.shop_item[pos].value = value;
+ // check for bad prices that can possibly cause exploits
+ if (value*75/100 < id->value_sell*124/100) {
+ printf("\r"); //Carriage return to clear the 'loading..' line. [Skotlex]
+ ShowWarning ("Item %s [%d] buying price (%d) is less than selling price (%d)\n",
+ id->name, id->nameid, value*75/100, id->value_sell*124/100);
+ }
+ //for logs filters, atcommands and iteminfo script command
+ if (id->maxchance<=0)
+ id->maxchance = 10000; //10000 (100% drop chance)would show that the item's sold in NPC Shop
+
+ pos++;
+ p = strchr(p, ',');
+ }
+ if (pos == 0) {
+ aFree(nd);
+ return 1;
+ }
+ nd->u.shop_item[pos++].nameid = 0;
+
+ nd->bl.prev = nd->bl.next = NULL;
+ nd->bl.m = m;
+ nd->bl.x = x;
+ nd->bl.y = y;
+ nd->bl.id = npc_get_new_npc_id();
+ nd->dir = dir;
+ nd->flag = 0;
+ memcpy(nd->name, w3, NAME_LENGTH-1);
+ nd->name[NAME_LENGTH-1] = '\0';
+ nd->class_ = m==-1?0:atoi(w4);
+ nd->speed = 200;
+
+ nd = (struct npc_data *)aRealloc(nd,
+ sizeof(struct npc_data) + sizeof(nd->u.shop_item[0]) * pos);
+
+ npc_shop++;
+ nd->bl.type = BL_NPC;
+ nd->bl.subtype = SHOP;
+ if (m >= 0) {
+ nd->n = map_addnpc(m,nd);
+ map_addblock(&nd->bl);
+ clif_spawnnpc(nd);
+ } else
+ // we skip map_addnpc, but still add it to the list of ID's
+ map_addiddb(&nd->bl);
+ strdb_put(npcname_db, nd->name,nd);
+
+ return 0;
+}
+
+/*==========================================
+ * NPCのラベルデータコンバート
+ *------------------------------------------
+ */
+int npc_convertlabel_db (DBKey key, void *data, va_list ap)
+{
+ unsigned char *lname = key.str;
+ int pos = (int)data;
+ struct npc_data *nd;
+ struct npc_label_list *lst;
+ int num;
+ char *p;
+ char c;
+
+ nullpo_retr(0, ap);
+ nullpo_retr(0, nd = va_arg(ap,struct npc_data *));
+
+ lst = nd->u.scr.label_list;
+ num = nd->u.scr.label_list_num;
+ if (!lst) {
+ lst = (struct npc_label_list *) aCallocA (1, sizeof(struct npc_label_list));
+ num = 0;
+ } else
+ lst = (struct npc_label_list *) aRealloc (lst, sizeof(struct npc_label_list)*(num+1));
+
+ // In case of labels not terminated with ':', for user defined function support
+ p = lname;
+ while(isalnum(*(unsigned char*)p) || *p == '_') { p++; }
+ c = *p;
+ *p='\0';
+
+ // here we check if the label fit into the buffer
+ if (strlen(lname) > 23) {
+ ShowError("npc_parse_script: label name longer than 23 chars! '%s'\n (%s)", lname, current_file);
+ exit(1);
+ }
+ memcpy(lst[num].name, lname, strlen(lname)+1); //including EOS
+
+ *p = c;
+ lst[num].pos = pos;
+ nd->u.scr.label_list = lst;
+ nd->u.scr.label_list_num = num+1;
+
+ return 0;
+}
+
+/*==========================================
+ * script行解析
+ *------------------------------------------
+ */
+static void npc_parse_script_line(unsigned char *p,int *curly_count,int line) {
+ int i = strlen((char *)p),j;
+ int string_flag = 0;
+ static int comment_flag = 0;
+ for(j = 0; j < i ; j++) {
+ if(comment_flag) {
+ if(p[j] == '*' && p[j+1] == '/') {
+ // マルチラインコメント終了
+ j++;
+ (*curly_count)--;
+ comment_flag = 0;
+ }
+ } else if(string_flag) {
+ if(p[j] == '"') {
+ string_flag = 0;
+ } else if(p[j] == '\\' && p[j-1]<=0x7e) {
+ // エスケープ
+ j++;
+ }
+ } else {
+ if(p[j] == '"') {
+ string_flag = 1;
+ } else if(p[j] == '}') {
+ if(*curly_count == 0) {
+ break;
+ } else {
+ (*curly_count)--;
+ }
+ } else if(p[j] == '{') {
+ (*curly_count)++;
+ } else if(p[j] == '/' && p[j+1] == '/') {
+ // コメント
+ break;
+ } else if(p[j] == '/' && p[j+1] == '*') {
+ // マルチラインコメント
+ j++;
+ (*curly_count)++;
+ comment_flag = 1;
+ }
+ }
+ }
+ if(string_flag) {
+ printf("Missing '\"' at file %s line %d\n",current_file,line);
+ exit(1);
+ }
+}
+
+// Like npc_parse_script, except it's sole use is to skip the contents of a script. [Skotlex]
+static int npc_skip_script (char *w1,char *w2,char *w3,char *w4,char *first_line,FILE *fp,int *lines)
+{
+ unsigned char *srcbuf = NULL;
+ int srcsize = 65536;
+ int startline = 0;
+ unsigned char line[1024];
+ int curly_count = 0;
+
+ srcbuf = (unsigned char *)aCallocA(srcsize, sizeof(char));
+ if (strchr(first_line, '{')) {
+ strcpy((char *)srcbuf, strchr(first_line, '{'));
+ startline = *lines;
+ } else
+ srcbuf[0] = 0;
+ npc_parse_script_line(srcbuf,&curly_count,*lines);
+ while (curly_count > 0) {
+ fgets ((char *)line, 1020, fp);
+ (*lines)++;
+ npc_parse_script_line(line,&curly_count,*lines);
+ if (feof(fp))
+ break;
+ if (strlen((char *)srcbuf) + strlen((char *)line) + 1 >= (size_t)srcsize) {
+ srcsize += 65536;
+ srcbuf = (unsigned char *)aRealloc(srcbuf, srcsize);
+ memset(srcbuf + srcsize - 65536, '\0', 65536);
+ }
+ if (srcbuf[0] != '{') {
+ if (strchr((char *) line,'{')) {
+ strcpy((char *) srcbuf, strchr((const char *) line, '{'));
+ startline = *lines;
+ }
+ } else
+ strcat((char *) srcbuf, (const char *) line);
+ }
+ if(curly_count > 0)
+ ShowError("Missing right curly at file %s, line %d\n",current_file, *lines);
+ aFree(srcbuf);
+ return 0;
+}
+
+static int npc_parse_script (char *w1,char *w2,char *w3,char *w4,char *first_line,FILE *fp,int *lines)
+{
+ int x, y, dir = 0, m, xs = 0, ys = 0, class_ = 0; // [Valaris] thanks to fov
+ char mapname[MAP_NAME_LENGTH];
+ unsigned char *srcbuf = NULL, *script;
+ int srcsize = 65536;
+ int startline = 0;
+ unsigned char line[1024];
+ int i;
+ struct npc_data *nd;
+ int evflag = 0;
+ struct dbt *label_db;
+ char *p;
+ struct npc_label_list *label_dup = NULL;
+ int label_dupnum = 0;
+ int src_id = 0;
+
+ if (strcmp(w1, "-") == 0) {
+ x = 0; y = 0; m = -1;
+ } else {
+ // 引数の個数チェック
+ if (sscanf(w1, "%15[^,],%d,%d,%d", mapname, &x, &y, &dir) != 4 ||
+ (strcmp(w2, "script") == 0 && strchr(w4,',') == NULL)) {
+ ShowError("bad script line (in file %s): %s\n", current_file, w3);
+ return 1;
+ }
+ m = map_mapname2mapid(mapname);
+ }
+
+ if (strcmp(w2, "script") == 0){
+ // parsing script with curly
+ int curly_count = 0;
+ srcbuf = (unsigned char *)aCallocA(srcsize, sizeof(char));
+ if (strchr(first_line, '{')) {
+ strcpy((char *)srcbuf, strchr(first_line, '{'));
+ startline = *lines;
+ } else
+ srcbuf[0] = 0;
+ npc_parse_script_line(srcbuf,&curly_count,*lines);
+ while (curly_count > 0) {
+ fgets ((char *)line, 1020, fp);
+ (*lines)++;
+ npc_parse_script_line(line,&curly_count,*lines);
+ if (feof(fp))
+ break;
+ if (strlen((char *)srcbuf) + strlen((char *)line) + 1 >= (size_t)srcsize) {
+ srcsize += 65536;
+ srcbuf = (unsigned char *)aRealloc(srcbuf, srcsize);
+ memset(srcbuf + srcsize - 65536, '\0', 65536);
+ }
+ if (srcbuf[0] != '{') {
+ if (strchr((char *) line,'{')) {
+ strcpy((char *) srcbuf, strchr((const char *) line, '{'));
+ startline = *lines;
+ }
+ } else
+ strcat((char *) srcbuf, (const char *) line);
+ }
+ if(curly_count > 0) {
+ ShowError("Missing right curly at file %s, line %d\n",current_file, *lines);
+ script = NULL;
+ } else {
+ // printf("Ok line %d\n",*lines);
+ script = (unsigned char *) parse_script((unsigned char *) srcbuf, startline);
+ }
+ if (script == NULL) {
+ // script parse error?
+ aFree(srcbuf);
+ return 1;
+ }
+ } else {
+ // duplicateする
+ char srcname[128];
+ struct npc_data *nd2;
+ if (sscanf(w2, "duplicate(%[^)])", srcname) != 1) {
+ ShowError("bad duplicate name (in %s)! : %s", current_file, w2);
+ return 0;
+ }
+ if ((nd2 = npc_name2id(srcname)) == NULL) {
+ ShowError("bad duplicate name (in %s)! (not exist) : %s\n", current_file, srcname);
+ return 0;
+ }
+ script = (unsigned char *)nd2->u.scr.script;
+ label_dup = nd2->u.scr.label_list;
+ label_dupnum = nd2->u.scr.label_list_num;
+ src_id = nd2->bl.id;
+
+ }// end of スクリプト解析
+
+ nd = (struct npc_data *)aCalloc(1, sizeof(struct npc_data));
+
+ if (m == -1){
+ // スクリプトコピー用のダミーNPC
+ } else if (sscanf(w4, "%d,%d,%d", &class_, &xs, &ys) == 3) {
+ // 接触型NPC
+ int i, j;
+
+ if (xs >= 0) xs = xs * 2 + 1;
+ if (ys >= 0) ys = ys * 2 + 1;
+
+ if (class_ >= 0) {
+ for (i = 0; i < ys; i++) {
+ for (j = 0; j < xs; j++) {
+ if (map_getcell(m, x - xs/2 + j, y - ys/2 + i, CELL_CHKNOPASS))
+ continue;
+ map_setcell(m, x - xs/2 + j, y - ys/2 + i, CELL_SETNPC);
+ }
+ }
+ }
+ nd->u.scr.xs = xs;
+ nd->u.scr.ys = ys;
+ } else {
+ // クリック型NPC
+ class_ = atoi(w4);
+ nd->u.scr.xs = 0;
+ nd->u.scr.ys = 0;
+ }
+
+ if (class_ < 0 && m >= 0) { // イベント型NPC
+ evflag = 1;
+ }
+
+ while ((p = strchr(w3,':'))) {
+ if (p[1] == ':') break;
+ }
+ if (p) {
+ *p = 0;
+ memcpy(nd->name, w3, NAME_LENGTH-1);
+ memcpy(nd->exname, p+2, NAME_LENGTH-1);
+ } else {
+ memcpy(nd->name, w3, NAME_LENGTH-1);
+ memcpy(nd->exname, w3, NAME_LENGTH-1);
+ }
+
+ nd->bl.prev = nd->bl.next = NULL;
+ nd->bl.m = m;
+ nd->bl.x = x;
+ nd->bl.y = y;
+ nd->bl.id = npc_get_new_npc_id();
+ nd->dir = dir;
+// nd->flag = 0;
+ nd->class_ = class_;
+ nd->speed = 200;
+ nd->u.scr.script = script;
+ nd->u.scr.src_id = src_id;
+/* Cleaned up above with memset...
+ nd->chat_id = 0;
+ nd->option = 0;
+ nd->opt1 = 0;
+ nd->opt2 = 0;
+ nd->opt3 = 0;
+*/
+ nd->walktimer = -1;
+
+ npc_script++;
+ nd->bl.type = BL_NPC;
+ nd->bl.subtype = SCRIPT;
+// Cleaned up above...
+// memset (nd->eventqueue, 0, sizeof(nd->eventqueue));
+ for (i = 0; i < MAX_EVENTTIMER; i++)
+ nd->eventtimer[i] = -1;
+ if (m >= 0) {
+ nd->n = map_addnpc(m, nd);
+ map_addblock(&nd->bl);
+
+ if (evflag) { // イベント型
+ struct event_data *ev = (struct event_data *)aCalloc(1, sizeof(struct event_data));
+ ev->nd = nd;
+ ev->pos = 0;
+ strdb_put(ev_db, nd->exname, ev);
+ } else
+ clif_spawnnpc(nd);
+ } else {
+ // we skip map_addnpc, but still add it to the list of ID's
+ map_addiddb(&nd->bl);
+ }
+ strdb_put(npcname_db, nd->exname, nd);
+
+ //-----------------------------------------
+ // ラベルデータの準備
+ if (srcbuf){
+ // script本体がある場合の処理
+ // ラベルデータのコンバート
+ label_db = script_get_label_db();
+ label_db->foreach(label_db, npc_convertlabel_db, nd);
+
+ // もう使わないのでバッファ解放
+ aFree(srcbuf);
+ } else {
+ // duplicate
+ nd->u.scr.label_list = label_dup; // ラベルデータ共有
+ nd->u.scr.label_list_num = label_dupnum;
+ }
+
+ //-----------------------------------------
+ // イベント用ラベルデータのエクスポート
+ for (i = 0; i < nd->u.scr.label_list_num; i++){
+ char *lname = nd->u.scr.label_list[i].name;
+ int pos = nd->u.scr.label_list[i].pos;
+
+ if ((lname[0] == 'O' || lname[0] == 'o') && (lname[1] == 'N' || lname[1] == 'n')) {
+ // this check is useless here because the buffer is only 24 chars
+ // and already overwritten if this is here is reached
+ // I leave the check anyway but place it correctly to npc_convertlabel_db
+ if (strlen(lname)>NAME_LENGTH-1) {
+ ShowError("npc_parse_script: label name longer than %d chars! '%s' (%s)\n", NAME_LENGTH-1, lname, current_file);
+ exit(1);
+ } else {
+ struct event_data *ev;
+ unsigned char buf[51];
+ // 51 comes from: 24 for npc name + 24 for label + 2 for a "::" and 1 for EOS
+ sprintf(buf,"%s::%s",nd->exname,lname);
+
+ // remember the label is max 50 chars + eos; see the strdb_init below
+ // generate the data and insert it
+ ev=(struct event_data *)aCalloc(1,sizeof(struct event_data));
+ ev->nd=nd;
+ ev->pos=pos;
+ if (strdb_put(ev_db,buf,ev) != NULL) //There was already another event of the same name?
+ ShowWarning("npc_parse_script : duplicate event %s (%s)\n",buf, current_file);
+ }
+ }
+ }
+
+ //-----------------------------------------
+ // ラベルデータからタイマーイベント取り込み
+ for (i = 0; i < nd->u.scr.label_list_num; i++){
+ int t = 0, k = 0;
+ char *lname = nd->u.scr.label_list[i].name;
+ int pos = nd->u.scr.label_list[i].pos;
+ if (sscanf(lname, "OnTimer%d%n", &t, &k) == 1 && lname[k] == '\0') {
+ // タイマーイベント
+ struct npc_timerevent_list *te = nd->u.scr.timer_event;
+ int j, k = nd->u.scr.timeramount;
+ if (te == NULL)
+ te = (struct npc_timerevent_list *)aCallocA(1,sizeof(struct npc_timerevent_list));
+ else
+ te = (struct npc_timerevent_list *)aRealloc( te, sizeof(struct npc_timerevent_list) * (k+1) );
+ for (j = 0; j < k; j++){
+ if (te[j].timer > t){
+ memmove(te+j+1, te+j, sizeof(struct npc_timerevent_list)*(k-j));
+ break;
+ }
+ }
+ te[j].timer = t;
+ te[j].pos = pos;
+ nd->u.scr.timer_event = te;
+ nd->u.scr.timeramount = k+1;
+ }
+ }
+ nd->u.scr.nexttimer = -1;
+ nd->u.scr.timerid = -1;
+
+ return 0;
+}
+
+/*==========================================
+ * function行解析
+ *------------------------------------------
+ */
+static int npc_parse_function (char *w1, char *w2, char *w3, char *w4, char *first_line, FILE *fp, int *lines)
+{
+ unsigned char *srcbuf, *script, *p;
+ int srcsize = 65536;
+ int startline = 0;
+ char line[1024];
+ int curly_count = 0;
+ struct dbt *user_db;
+
+ // スクリプトの解析
+ srcbuf = (unsigned char *) aCallocA (srcsize, sizeof(char));
+ if (strchr(first_line,'{')) {
+ strcpy(srcbuf, strchr(first_line,'{'));
+ startline = *lines;
+ } else
+ srcbuf[0] = 0;
+ npc_parse_script_line(srcbuf,&curly_count,*lines);
+
+ while (curly_count > 0) {
+ fgets(line, sizeof(line) - 1, fp);
+ (*lines)++;
+ npc_parse_script_line(line,&curly_count,*lines);
+ if (feof(fp))
+ break;
+ if (strlen(srcbuf)+strlen(line)+1 >= (unsigned int)srcsize) {
+ srcsize += 65536;
+ srcbuf = (char *)aRealloc(srcbuf, srcsize);
+ memset(srcbuf + srcsize - 65536, '\0', 65536);
+ }
+ if (srcbuf[0]!='{') {
+ if (strchr(line,'{')) {
+ strcpy(srcbuf, strchr(line,'{'));
+ startline = *lines;
+ }
+ } else
+ strcat(srcbuf,line);
+ }
+ if(curly_count > 0) {
+ ShowError("Missing right curly at file %s, line %d\n",current_file, *lines);
+ script = NULL;
+ } else {
+ script = parse_script(srcbuf, startline);
+ }
+ if (script == NULL) {
+ // script parse error?
+ aFree(srcbuf);
+ return 1;
+ }
+
+ p = (char *) aCallocA (50, sizeof(char));
+ strncpy(p, w3, 50);
+
+ user_db = script_get_userfunc_db();
+ strdb_put(user_db, p, script);
+
+ // もう使わないのでバッファ解放
+ aFree(srcbuf);
+
+// printf("function %s => %p\n",p,script);
+
+ return 0;
+}
+
+
+/*==========================================
+ * Parse Mob 1 - Parse mob list into each map
+ * Parse Mob 2 - Actually Spawns Mob
+ * [Wizputer]
+ * If cached =1, it is a dynamic cached mob
+ *------------------------------------------
+ */
+int npc_parse_mob2 (struct mob_list *mob, int cached)
+{
+ int i;
+ struct mob_data *md;
+
+ for (i = 0; i < mob->num; i++) {
+ md = (struct mob_data *) aCalloc (1, sizeof(struct mob_data));
+
+ md->bl.prev = NULL;
+ md->bl.next = NULL;
+ md->bl.m = mob->m;
+ md->bl.x = mob->x;
+ md->bl.y = mob->y;
+ md->level = mob->level;
+ memcpy(md->name, mob->mobname, NAME_LENGTH-1);
+ md->n = i;
+ //FIXME: This implementation is not stable, npc scripts will stop working once MAX_MOB_DB changes value! [Skotlex]
+ if(mob->class_ > 2*MAX_MOB_DB){ // large/tiny mobs [Valaris]
+ md->special_state.size=2;
+ md->base_class = md->class_ = mob->class_-2*MAX_MOB_DB;
+ } else if (mob->class_ > MAX_MOB_DB) {
+ md->special_state.size=1;
+ md->base_class = md->class_ = mob->class_-MAX_MOB_DB;
+ } else
+ md->base_class = md->class_ = mob->class_;
+ md->bl.id = npc_get_new_npc_id();
+ md->db = mob_db(mob->class_);
+ md->m = mob->m;
+ md->x0 = mob->x;
+ md->y0 = mob->y;
+ md->xs = mob->xs;
+ md->ys = mob->ys;
+ md->spawndelay1 = mob->delay1;
+ md->spawndelay2 = mob->delay2;
+
+ md->special_state.cached = cached; //If cached, mob is dynamically removed
+ md->timer = -1;
+ md->speed = mob_db(mob->class_)->speed;
+
+ if (mob_db(mob->class_)->mode & MD_LOOTER)
+ md->lootitem = (struct item *)aCalloc(LOOTITEM_SIZE, sizeof(struct item));
+ else
+ md->lootitem = NULL;
+
+ if (strlen(mob->eventname) >= 4) {
+ memcpy(md->npc_event, mob->eventname, NAME_LENGTH-1);
+ } else if (strlen(mob->eventname) == 1) { //Portable monster big/small implementation. [Skotlex]
+ int size = atoi(mob->eventname);
+ if (size & 2)
+ md->special_state.size=1;
+ else if (size & 4)
+ md->special_state.size=2;
+ }
+
+ md->bl.type = BL_MOB;
+ map_addiddb(&md->bl);
+ mob_spawn(md->bl.id);
+ }
+
+ return 0;
+}
+
+int npc_parse_mob (char *w1, char *w2, char *w3, char *w4)
+{
+ int level, mode;
+ char mapname[MAP_NAME_LENGTH];
+ char mobname[NAME_LENGTH];
+ struct mob_list mob;
+
+ memset(&mob, 0, sizeof(struct mob_list));
+
+ // 引数の個数チェック
+ if (sscanf(w1, "%15[^,],%d,%d,%d,%d", mapname, &mob.x, &mob.y, &mob.xs, &mob.ys) < 3 ||
+ sscanf(w4, "%d,%d,%d,%d,%23s", &mob.class_, &mob.num, &mob.delay1, &mob.delay2, mob.eventname) < 2 ) {
+ ShowError("bad monster line : %s %s %s (file %s)\n", w1, w3, w4, current_file);
+ return 1;
+ }
+
+ mob.m = map_mapname2mapid(mapname);
+ if (mob.m < 0) {
+ ShowError("wrong map name : %s %s (file %s)\n", w1,w3, current_file);
+ return 1;
+ }
+
+ // check monster ID if exists!
+ if (mobdb_checkid(mob.class_)==0) {
+ ShowError("bad monster ID : %s %s (file %s)\n", w3, w4, current_file);
+ return 1;
+ }
+
+ if (mob.num < 1 || mob.num>1000 ) {
+ ShowError("wrong number of monsters : %s %s (file %s)\n", w3, w4, current_file);
+ return 1;
+ }
+ if (mob.num > 1 && battle_config.mob_count_rate != 100) {
+ if ((mob.num = mob.num * battle_config.mob_count_rate / 100) < 1)
+ mob.num = 1;
+ }
+
+ //Apply the spawn delay fix [Skotlex]
+ mode = mob_db(mob.class_)->mode;
+ if (mode & MD_BOSS) { //Bosses
+ if (battle_config.boss_spawn_delay != 100)
+ {
+ mob.delay1 = mob.delay1*battle_config.boss_spawn_delay/100;
+ mob.delay2 = mob.delay2*battle_config.boss_spawn_delay/100;
+ }
+ } else if (mode&MD_PLANT) { //Plants
+ if (battle_config.plant_spawn_delay != 100)
+ {
+ mob.delay1 = mob.delay1*battle_config.plant_spawn_delay/100;
+ mob.delay2 = mob.delay2*battle_config.plant_spawn_delay/100;
+ }
+ } else if (battle_config.mob_spawn_delay != 100)
+ { //Normal mobs
+ mob.delay1 = mob.delay1*battle_config.mob_spawn_delay/100;
+ mob.delay2 = mob.delay2*battle_config.mob_spawn_delay/100;
+ }
+
+ // parse MOB_NAME,[MOB LEVEL]
+ if (sscanf(w3, "%23[^,],%d", mobname, &level) > 1)
+ mob.level = level;
+
+ if (strcmp(mobname, "--en--") == 0)
+ memcpy(mob.mobname, mob_db(mob.class_)->name, NAME_LENGTH-1);
+ else if (strcmp(mobname, "--ja--") == 0)
+ memcpy(mob.mobname, mob_db(mob.class_)->jname, NAME_LENGTH-1);
+ else memcpy(mob.mobname, mobname, NAME_LENGTH-1);
+
+ if( mob.delay1<0 || mob.delay2<0 || mob.delay1>0xfffffff || mob.delay2>0xfffffff) {
+ ShowError("wrong monsters spawn delays : %s %s (file %s)\n", w3, w4, current_file);
+ return 1;
+ }
+
+ if( !battle_config.dynamic_mobs || mob.delay1 || mob.delay2 ) {
+ npc_parse_mob2(&mob,0);
+ npc_delay_mob += mob.num;
+ } else {
+ struct mob_list *dynmob = map_addmobtolist(mob.m);
+ if( dynmob ) {
+ memcpy(dynmob, &mob, sizeof(struct mob_list));
+ // check if target map has players
+ // (usually shouldn't occur when map server is just starting,
+ // but not the case when we do @reloadscript
+ if (map[mob.m].users > 0)
+ npc_parse_mob2(&mob,1);
+ npc_cache_mob += mob.num;
+ } else {
+ // mobcache is full
+ // create them as delayed with one second
+ mob.delay1 = 1000;
+ npc_parse_mob2(&mob,0);
+ npc_delay_mob += mob.num;
+ }
+ }
+
+ npc_mob++;
+
+ return 0;
+}
+
+/*==========================================
+ * マップフラグ行の解析
+ *------------------------------------------
+ */
+static int npc_parse_mapflag (char *w1, char *w2, char *w3, char *w4)
+{
+ int m;
+ char mapname[MAP_NAME_LENGTH];
+
+ // 引数の個数チェック
+ if (sscanf(w1, "%15[^,]",mapname) != 1)
+ return 1;
+
+ m = map_mapname2mapid(mapname);
+ if (m < 0)
+ return 1;
+
+//マップフラグ
+ if (strcmpi(w3, "nosave") == 0) {
+ char savemap[MAP_NAME_LENGTH];
+ int savex, savey;
+ if (strcmp(w4, "SavePoint") == 0) {
+ map[m].save.map = 0;
+ map[m].save.x = -1;
+ map[m].save.y = -1;
+ } else if (sscanf(w4, "%15[^,],%d,%d", savemap, &savex, &savey) == 3) {
+ map[m].save.map = mapindex_name2id(savemap);
+ map[m].save.x = savex;
+ map[m].save.y = savey;
+ if (!map[m].save.map) {
+ ShowWarning("Specified save point map '%s' for mapflag 'nosave' not found (file %s), using 'SavePoint'.\n",savemap,current_file);
+ map[m].save.x = -1;
+ map[m].save.y = -1;
+ }
+ }
+ map[m].flag.nosave = 1;
+ }
+ else if (strcmpi(w3,"nomemo")==0) {
+ map[m].flag.nomemo=1;
+ }
+ else if (strcmpi(w3,"noteleport")==0) {
+ map[m].flag.noteleport=1;
+ }
+ else if (strcmpi(w3,"nowarp")==0) {
+ map[m].flag.nowarp=1;
+ }
+ else if (strcmpi(w3,"nowarpto")==0) {
+ map[m].flag.nowarpto=1;
+ }
+ else if (strcmpi(w3,"noreturn")==0) {
+ map[m].flag.noreturn=1;
+ }
+ else if (strcmpi(w3,"monster_noteleport")==0) {
+ map[m].flag.monster_noteleport=1;
+ }
+ else if (strcmpi(w3,"nobranch")==0) {
+ map[m].flag.nobranch=1;
+ }
+ else if (strcmpi(w3,"nopenalty")==0) {
+ map[m].flag.nopenalty=1;
+ }
+ else if (strcmpi(w3,"pvp")==0) {
+ map[m].flag.pvp=1;
+ }
+ else if (strcmpi(w3,"pvp_noparty")==0) {
+ map[m].flag.pvp_noparty=1;
+ }
+ else if (strcmpi(w3,"pvp_noguild")==0) {
+ map[m].flag.pvp_noguild=1;
+ }
+ else if (strcmpi(w3, "pvp_nightmaredrop") == 0) {
+ char drop_arg1[16], drop_arg2[16];
+ int drop_id = 0, drop_type = 0, drop_per = 0;
+ if (sscanf(w4, "%[^,],%[^,],%d", drop_arg1, drop_arg2, &drop_per) == 3) {
+ int i;
+ if (strcmp(drop_arg1, "random") == 0)
+ drop_id = -1;
+ else if (itemdb_exists((drop_id = atoi(drop_arg1))) == NULL)
+ drop_id = 0;
+ if (strcmp(drop_arg2, "inventory") == 0)
+ drop_type = 1;
+ else if (strcmp(drop_arg2,"equip") == 0)
+ drop_type = 2;
+ else if (strcmp(drop_arg2,"all") == 0)
+ drop_type = 3;
+
+ if (drop_id != 0){
+ for (i = 0; i < MAX_DROP_PER_MAP; i++) {
+ if (map[m].drop_list[i].drop_id == 0){
+ map[m].drop_list[i].drop_id = drop_id;
+ map[m].drop_list[i].drop_type = drop_type;
+ map[m].drop_list[i].drop_per = drop_per;
+ break;
+ }
+ }
+ map[m].flag.pvp_nightmaredrop = 1;
+ }
+ }
+ }
+ else if (strcmpi(w3,"pvp_nocalcrank")==0) {
+ map[m].flag.pvp_nocalcrank=1;
+ }
+ else if (strcmpi(w3,"gvg")==0) {
+ map[m].flag.gvg=1;
+ }
+ else if (strcmpi(w3,"gvg_noparty")==0) {
+ map[m].flag.gvg_noparty=1;
+ }
+ else if (strcmpi(w3,"gvg_dungeon")==0) {
+ map[m].flag.gvg_dungeon=1;
+ }
+ else if (strcmpi(w3,"gvg_castle")==0) {
+ map[m].flag.gvg_castle=1;
+ }
+ else if (strcmpi(w3,"nozenypenalty")==0) {
+ map[m].flag.nozenypenalty=1;
+ }
+ else if (strcmpi(w3,"notrade")==0) {
+ map[m].flag.notrade=1;
+ }
+ else if (strcmpi(w3,"noskill")==0) {
+ map[m].flag.noskill=1;
+ }
+ else if (battle_config.pk_mode && strcmpi(w3,"nopvp")==0) { // nopvp for pk mode [Valaris]
+ map[m].flag.nopvp=1;
+ map[m].flag.pvp=0;
+ }
+ else if (strcmpi(w3,"noicewall")==0) { // noicewall [Valaris]
+ map[m].flag.noicewall=1;
+ }
+ else if (strcmpi(w3,"snow")==0) { // snow [Valaris]
+ map[m].flag.snow=1;
+ }
+ else if (strcmpi(w3,"clouds")==0) {
+ map[m].flag.clouds=1;
+ }
+ else if (strcmpi(w3,"clouds2")==0) { // clouds2 [Valaris]
+ map[m].flag.clouds2=1;
+ }
+ else if (strcmpi(w3,"fog")==0) { // fog [Valaris]
+ map[m].flag.fog=1;
+ }
+ else if (strcmpi(w3,"fireworks")==0) {
+ map[m].flag.fireworks=1;
+ }
+ else if (strcmpi(w3,"sakura")==0) { // sakura [Valaris]
+ map[m].flag.sakura=1;
+ }
+ else if (strcmpi(w3,"leaves")==0) { // leaves [Valaris]
+ map[m].flag.leaves=1;
+ }
+ else if (strcmpi(w3,"rain")==0) { // rain [Valaris]
+ map[m].flag.rain=1;
+ }
+ else if (strcmpi(w3,"indoors")==0) { // celest
+ map[m].flag.indoors=1;
+ }
+ else if (strcmpi(w3,"nightenabled")==0) { // Skotlex
+ map[m].flag.nightenabled=1;
+ }
+ else if (strcmpi(w3,"nogo")==0) { // celest
+ map[m].flag.nogo=1;
+ }
+ else if (strcmpi(w3,"noexp")==0) { // Lorky
+ map[m].flag.nobaseexp=1;
+ map[m].flag.nojobexp=1;
+ }
+ else if (strcmpi(w3,"nobaseexp")==0) { // Lorky
+ map[m].flag.nobaseexp=1;
+ }
+ else if (strcmpi(w3,"nojobexp")==0) { // Lorky
+ map[m].flag.nojobexp=1;
+ }
+ else if (strcmpi(w3,"noloot")==0) { // Lorky
+ map[m].flag.nomobloot=1;
+ map[m].flag.nomvploot=1;
+ }
+ else if (strcmpi(w3,"nomobloot")==0) { // Lorky
+ map[m].flag.nomobloot=1;
+ }
+ else if (strcmpi(w3,"nomvploot")==0) { // Lorky
+ map[m].flag.nomvploot=1;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * Setting up map cells
+ *------------------------------------------
+ */
+static int npc_parse_mapcell (char *w1, char *w2, char *w3, char *w4)
+{
+ int m, cell, x, y, x0, y0, x1, y1;
+ char type[24], mapname[MAP_NAME_LENGTH];
+
+ if (sscanf(w1, "%15[^,]", mapname) != 1)
+ return 1;
+
+ m = map_mapname2mapid(mapname);
+ if (m < 0)
+ return 1;
+
+ if (sscanf(w3, "%23[^,],%d,%d,%d,%d", type, &x0, &y0, &x1, &y1) < 4) {
+ ShowError("Bad setcell line : %s\n",w3);
+ return 1;
+ }
+ cell = strtol(type, (char **)NULL, 0);
+ //printf ("0x%x %d %d %d %d\n", cell, x0, y0, x1, y1);
+
+ if (x0 > x1) { int t = x0; x0 = x1; x1 = t; }
+ if (y0 > y1) { int t = y0; y0 = y1; y1 = t; }
+
+ for (x = x0; x <= x1; x++) {
+ for (y = y0; y <= y1; y++) {
+ map_setcell(m, x, y, cell);
+ //printf ("setcell 0x%x %d %d %d\n", cell, m, x, y);
+ }
+ }
+
+ return 0;
+}
+
+void npc_parsesrcfile (char *name)
+{
+ int m, lines = 0;
+ char line[1024];
+
+ FILE *fp = fopen (name,"r");
+ if (fp == NULL) {
+ ShowError ("File not found : %s\n", name);
+ exit(1);
+ }
+ current_file = name;
+
+ while (fgets(line, sizeof(line) - 1, fp)) {
+ char w1[1024], w2[1024], w3[1024], w4[1024], mapname[1024];
+ int i, j, w4pos, count;
+ lines++;
+
+ if (line[0] == '/' && line[1] == '/')
+ continue;
+ // 不要なスペースやタブの連続は詰める
+ for (i = j = 0; line[i]; i++) {
+ if (line[i]==' ') {
+ if (!((line[i+1] && (isspace((unsigned char)line[i+1]) || line[i+1]==',')) ||
+ (j && line[j-1]==',')))
+ line[j++]=' ';
+ } else if (line[i]=='\t') {
+ if (!(j && line[j-1]=='\t'))
+ line[j++]='\t';
+ } else
+ line[j++]=line[i];
+ }
+ line[j] = '\0'; //Forget to terminate the string. From [jA 1091]
+ // 最初はタブ区切りでチェックしてみて、ダメならスペース区切りで確認
+ if ((count = sscanf(line,"%[^\t]\t%[^\t]\t%[^\t\r\n]\t%n%[^\t\r\n]", w1, w2, w3, &w4pos, w4)) < 3 &&
+ (count = sscanf(line,"%s%s%s%n%s", w1, w2, w3, &w4pos, w4)) < 3) {
+ continue;
+ }
+ // マップの存在確認
+ if (strcmp(w1,"-") !=0 && strcmpi(w1,"function") != 0 ){
+ sscanf(w1,"%[^,]",mapname);
+ if (!mapindex_name2id(mapname)) { //Incorrect map
+ ShowError("Invalid map '%s' in line %d, file %s\n", mapname, lines, current_file);
+ if (strcmpi(w2,"script") == 0 && count > 3) //we must skip the script info...
+ npc_skip_script(w1,w2,w3,w4,line+w4pos,fp,&lines);
+ continue;
+ }
+ if ((m = map_mapname2mapid(mapname)) < 0) {
+ // "mapname" is not assigned to this server
+ // we must skip the script info...
+ if (strcmpi(w2,"script") == 0 && count > 3)
+ npc_skip_script(w1,w2,w3,w4,line+w4pos,fp,&lines);
+ continue;
+ }
+ }
+ if (strcmpi(w2,"warp") == 0 && count > 3) {
+ npc_parse_warp(w1,w2,w3,w4);
+ } else if (strcmpi(w2,"shop") == 0 && count > 3) {
+ npc_parse_shop(w1,w2,w3,w4);
+ } else if (strcmpi(w2,"script") == 0 && count > 3) {
+ if (strcmpi(w1,"function") == 0) {
+ npc_parse_function(w1,w2,w3,w4,line+w4pos,fp,&lines);
+ } else {
+ npc_parse_script(w1,w2,w3,w4,line+w4pos,fp,&lines);
+ }
+ } else if ((i = 0, sscanf(w2,"duplicate%n",&i), (i > 0 && w2[i] == '(')) && count > 3) {
+ npc_parse_script(w1,w2,w3,w4,line+w4pos,fp,&lines);
+ } else if (strcmpi(w2,"monster") == 0 && count > 3) {
+ npc_parse_mob(w1,w2,w3,w4);
+ } else if (strcmpi(w2,"mapflag") == 0 && count >= 3) {
+ npc_parse_mapflag(w1,w2,w3,w4);
+ } else if (strcmpi(w2,"setcell") == 0 && count >= 3) {
+ npc_parse_mapcell(w1,w2,w3,w4);
+ } else {
+ ShowError("Probably TAB is missing: %s %s %s %s line '%i', file '%s'\n",w1,w2,w3,w4,lines,current_file); //Lupus
+ }
+ }
+ fclose(fp);
+
+ return;
+}
+
+static int npc_read_indoors (void)
+{
+ char *buf, *p;
+ int s, m;
+
+ buf = (char *)grfio_reads("data\\indoorrswtable.txt",&s);
+ if (buf == NULL)
+ return -1;
+ buf[s] = 0;
+
+ for (p = buf; p - buf < s; ) {
+ char map_name[64];
+ if (sscanf(p, "%15[^#]#", map_name) == 1) {
+ size_t pos = strlen(map_name) - 4; // replace '.xxx' extension
+ memcpy(map_name+pos,".gat",4); // with '.gat'
+ if ((m = map_mapname2mapid(map_name)) >= 0)
+ map[m].flag.indoors = 1;
+ }
+
+ p = strchr(p, 10);
+ if (!p) break;
+ p++;
+ }
+ aFree(buf);
+ ShowStatus("Done reading '"CL_WHITE"%s"CL_RESET"'.\n","data\\indoorrswtable.txt");
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+
+static int npc_cleanup_sub (struct block_list *bl, va_list ap) {
+ nullpo_retr(0, bl);
+
+ switch(bl->type) {
+ case BL_NPC:
+ npc_unload((struct npc_data *)bl);
+ break;
+ case BL_MOB:
+ mob_unload((struct mob_data *)bl);
+ break;
+ }
+
+ return 0;
+}
+
+static int npc_cleanup_dbsub(DBKey key,void * data,va_list app) {
+ return npc_cleanup_sub((struct block_list*)data, 0);
+}
+
+int npc_reload (void)
+{
+ struct npc_src_list *nsl;
+ int m, i;
+ time_t last_time = time(0);
+ int busy = 0, npc_new_min = npc_id;
+ char c = '-';
+
+ for (m = 0; m < map_num; m++) {
+ map_foreachinmap(npc_cleanup_sub, m, 0);
+ if(battle_config.dynamic_mobs) { //dynamic check by [random]
+ for (i = 0; i < MAX_MOB_LIST_PER_MAP; i++)
+ if (map[m].moblist[i]) aFree(map[m].moblist[i]);
+ memset (map[m].moblist, 0, sizeof(map[m].moblist));
+ }
+ map[m].npc_num = 0;
+ }
+ //Remove any npcs/mobs that weren't caught by the previous loop. [Skotlex]
+ map_foreachiddb(npc_cleanup_dbsub);
+
+ // anything else we should cleanup?
+ // Reloading npc's now
+ ev_db->clear(ev_db,NULL);
+ npcname_db->clear(npcname_db,NULL);
+ npc_warp = npc_shop = npc_script = 0;
+ npc_mob = npc_cache_mob = npc_delay_mob = 0;
+
+ for (nsl = npc_src_first; nsl; nsl = nsl->next) {
+ npc_parsesrcfile(nsl->name);
+ if (script_config.verbose_mode) {
+ printf("\r");
+ ShowStatus("Loading NPCs... %-53s", nsl->name);
+ } else {
+ if (last_time != time(0)) {
+ printf("\r");
+ ShowStatus("Loading NPCs... Working: ");
+ last_time = time(0);
+ switch(busy) {
+ case 0: c='\\'; busy++; break;
+ case 1: c='|'; busy++; break;
+ case 2: c='/'; busy++; break;
+ case 3: c='-'; busy=0;
+ }
+ printf("[%c]",c);
+ }
+ }
+ fflush(stdout);
+ }
+ printf("\r");
+ ShowInfo ("Done loading '"CL_WHITE"%d"CL_RESET"' NPCs:%30s\n\t-'"
+ CL_WHITE"%d"CL_RESET"' Warps\n\t-'"
+ CL_WHITE"%d"CL_RESET"' Shops\n\t-'"
+ CL_WHITE"%d"CL_RESET"' Scripts\n\t-'"
+ CL_WHITE"%d"CL_RESET"' Mobs\n\t-'"
+ CL_WHITE"%d"CL_RESET"' Mobs Cached\n\t-'"
+ CL_WHITE"%d"CL_RESET"' Mobs Not Cached\n",
+ npc_id - npc_new_min, "", npc_warp, npc_shop, npc_script, npc_mob, npc_cache_mob, npc_delay_mob);
+
+ //Execute the OnInit event for freshly loaded npcs. [Skotlex]
+ ShowStatus("Event '"CL_WHITE"OnInit"CL_RESET"' executed with '"
+ CL_WHITE"%d"CL_RESET"' NPCs.\n",npc_event_doall("OnInit"));
+ return 0;
+}
+
+/*==========================================
+ * 終了
+ *------------------------------------------
+ */
+int do_final_npc(void)
+{
+ int i;
+ struct block_list *bl;
+ struct npc_data *nd;
+ struct mob_data *md;
+ struct pet_data *pd;
+
+ for (i = START_NPC_NUM; i < npc_id; i++){
+ if ((bl = map_id2bl(i))){
+ if (bl->type == BL_NPC && (nd = (struct npc_data *)bl)){
+ npc_unload(nd);
+ } else if (bl->type == BL_MOB && (md = (struct mob_data *)bl)){
+ if (md->lootitem)
+ aFree(md->lootitem);
+ aFree(md);
+ } else if (bl->type == BL_PET && (pd = (struct pet_data *)bl)){
+ aFree(pd);
+ }
+ }
+ }
+
+ ev_db->destroy(ev_db, NULL);
+ //There is no free function for npcname_db because at this point there shouldn't be any npcs left!
+ //So if there is anything remaining, let the memory manager catch it and report it.
+ npcname_db->destroy(npcname_db, NULL);
+
+ npc_clearsrcfile();
+
+ return 0;
+}
+
+/*==========================================
+ * npc初期化
+ *------------------------------------------
+ */
+int do_init_npc(void)
+{
+ struct npc_src_list *nsl;
+ time_t last_time = time(0);
+ int busy = 0;
+ char c = '-';
+
+ // indoorrswtable.txt and etcinfo.txt [Celest]
+ if (battle_config.indoors_override_grffile)
+ npc_read_indoors();
+
+ // comparing only the first 24 chars of labels that are 50 chars long isn't that nice
+ // will cause "duplicated" labels where actually no dup is...
+ ev_db = db_alloc(__FILE__,__LINE__,DB_STRING,DB_OPT_DUP_KEY|DB_OPT_RELEASE_DATA,51);
+ npcname_db = db_alloc(__FILE__,__LINE__,DB_STRING,DB_OPT_BASE,NAME_LENGTH);
+
+ memset(&ev_tm_b, -1, sizeof(ev_tm_b));
+
+ for (nsl = npc_src_first; nsl; nsl = nsl->next) {
+ npc_parsesrcfile(nsl->name);
+ current_file = NULL;
+ printf("\r");
+ if (script_config.verbose_mode)
+ ShowStatus ("Loading NPCs... %-53s", nsl->name);
+ else {
+ ShowStatus("Loading NPCs... Working: ");
+ if (last_time != time(0)) {
+ last_time = time(0);
+ switch(busy) {
+ case 0: c='\\'; busy++; break;
+ case 1: c='|'; busy++; break;
+ case 2: c='/'; busy++; break;
+ case 3: c='-'; busy=0;
+ }
+ }
+ printf("[%c]",c);
+ }
+ fflush(stdout);
+ }
+ printf("\r");
+ ShowInfo ("Done loading '"CL_WHITE"%d"CL_RESET"' NPCs:%30s\n\t-'"
+ CL_WHITE"%d"CL_RESET"' Warps\n\t-'"
+ CL_WHITE"%d"CL_RESET"' Shops\n\t-'"
+ CL_WHITE"%d"CL_RESET"' Scripts\n\t-'"
+ CL_WHITE"%d"CL_RESET"' Mobs\n\t-'"
+ CL_WHITE"%d"CL_RESET"' Mobs Cached\n\t-'"
+ CL_WHITE"%d"CL_RESET"' Mobs Not Cached\n",
+ npc_id - START_NPC_NUM, "", npc_warp, npc_shop, npc_script, npc_mob, npc_cache_mob, npc_delay_mob);
+
+ add_timer_func_list(npc_walktimer,"npc_walktimer"); // [Valaris]
+ add_timer_func_list(npc_event_timer,"npc_event_timer");
+ add_timer_func_list(npc_event_do_clock,"npc_event_do_clock");
+ add_timer_func_list(npc_timerevent,"npc_timerevent");
+
+ return 0;
+}
+// [Lance]
+ int npc_changename(const char *name, const char *newname, short look){
+ struct npc_data *nd= (struct npc_data *) strdb_remove(npcname_db,(unsigned char*)name);
+ if (nd==NULL)
+ return 0;
+ npc_enable(name,0);
+ strcpy(nd->name,newname);
+ nd->class_ = look;
+ strdb_put(npcname_db,nd->name,nd);
+ npc_enable(newname,1);
+ return 0;
+}
diff --git a/src/map/npc.h b/src/map/npc.h
new file mode 100644
index 000000000..ad482d7d1
--- /dev/null
+++ b/src/map/npc.h
@@ -0,0 +1,71 @@
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#ifndef _NPC_H_
+#define _NPC_H_
+
+#define START_NPC_NUM 110000000
+
+#define WARP_CLASS 45
+#define WARP_DEBUG_CLASS 722
+#define INVISIBLE_CLASS 32767
+
+//Checks if a given id is a valid npc id. [Skotlex]
+//Since new npcs are added all the time, the max valid value is the one before the first mob (Scorpion = 1001)
+#define npcdb_checkid(id) ((id >= 46 && id <= 125) || (id >= 700 && id <= 1000))
+
+#ifdef PCRE_SUPPORT
+void npc_chat_finalize(struct npc_data *nd);
+#endif
+int npc_chat_sub(struct block_list *bl, va_list ap);
+int npc_event_dequeue(struct map_session_data *sd);
+int npc_event_timer(int tid,unsigned int tick,int id,int data);
+int npc_event(struct map_session_data *sd,const unsigned char *npcname,int);
+int npc_timer_event(const unsigned char *eventname); // Added by RoVeRT
+int npc_command(struct map_session_data *sd,const unsigned char *npcname,char *command);
+int npc_touch_areanpc(struct map_session_data *,int,int,int);
+int npc_click(struct map_session_data *,int);
+int npc_scriptcont(struct map_session_data *,int);
+int npc_checknear(struct map_session_data *,int);
+int npc_buysellsel(struct map_session_data *,int,int);
+int npc_buylist(struct map_session_data *,int,unsigned short *);
+int npc_selllist(struct map_session_data *,int,unsigned short *);
+int npc_parse_mob(char *w1,char *w2,char *w3,char *w4);
+int npc_parse_mob2 (struct mob_list *, int cached); // [Wizputer]
+int npc_parse_warp(char *w1,char *w2,char *w3,char *w4);
+int npc_globalmessage(const char *name,char *mes);
+
+int npc_enable(const char *name,int flag);
+int npc_changename(const char *name, const char *newname, short look); // [Lance]
+struct npc_data* npc_name2id(const char *name);
+
+int npc_walktoxy(struct npc_data *nd,int x,int y,int easy); // npc walking [Valaris]
+int npc_stop_walking(struct npc_data *nd,int type);
+int npc_changestate(struct npc_data *nd,int state,int type);
+
+int npc_get_new_npc_id(void);
+
+void npc_addsrcfile(char *);
+void npc_delsrcfile(char *);
+void npc_parsesrcfile(char *);
+int do_final_npc(void);
+int do_init_npc(void);
+int npc_event_do_oninit(void);
+int npc_do_ontimer(int,int);
+
+int npc_event_doall(const unsigned char *name);
+int npc_event_do(const unsigned char *name);
+int npc_event_doall_id(const unsigned char *name, int id);
+
+int npc_timerevent_start(struct npc_data *nd, int rid);
+int npc_timerevent_stop(struct npc_data *nd);
+int npc_gettimerevent_tick(struct npc_data *nd);
+int npc_settimerevent_tick(struct npc_data *nd,int newtimer);
+int npc_remove_map(struct npc_data *nd);
+int npc_unload(struct npc_data *nd);
+int npc_reload(void);
+
+extern char *current_file;
+
+#endif
+
diff --git a/src/map/npc_chat.c b/src/map/npc_chat.c
new file mode 100644
index 000000000..6c5b513e8
--- /dev/null
+++ b/src/map/npc_chat.c
@@ -0,0 +1,519 @@
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#ifdef PCRE_SUPPORT
+
+#include <stdio.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#ifdef __WIN32
+#define __USE_W32_SOCKETS
+#include <windows.h>
+#else
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#endif
+#include <time.h>
+
+#include "../common/timer.h"
+#include "../common/malloc.h"
+#include "../common/version.h"
+#include "../common/nullpo.h"
+#include "../common/showmsg.h"
+
+#include "map.h"
+#include "status.h"
+#include "npc.h"
+#include "chat.h"
+#include "script.h"
+#include "battle.h"
+
+#include "pcre.h"
+
+/**
+ * Written by MouseJstr in a vision... (2/21/2005)
+ *
+ * This allows you to make npc listen for spoken text (global
+ * messages) and pattern match against that spoken text using perl
+ * regular expressions.
+ *
+ * Please feel free to copy this code into your own personal ragnarok
+ * servers or distributions but please leave my name. Also, please
+ * wait until I've put it into the main eA branch which means I
+ * believe it is ready for distribution.
+ *
+ * So, how do people use this?
+ *
+ * The first and most important function is defpattern
+ *
+ * defpattern 1, "[^:]+: (.*) loves (.*)", "label";
+ *
+ * this defines a new pattern in set 1 using perl syntax
+ * (http://www.troubleshooters.com/codecorn/littperl/perlreg.htm)
+ * and tells it to jump to the supplied label when the pattern
+ * is matched.
+ *
+ * each of the matched Groups will result in a variable being
+ * set ($p1$ through $p9$ with $p0$ being the entire string)
+ * before the script gets executed.
+ *
+ * activatepset 1;
+ *
+ * This activates a set of patterns.. You can have many pattern
+ * sets defined and many active all at once. This feature allows
+ * you to set up "conversations" and ever changing expectations of
+ * the pattern matcher
+ *
+ * deactivatepset 1;
+ *
+ * turns off a pattern set;
+ *
+ * deactivatepset -1;
+ *
+ * turns off ALL pattern sets;
+ *
+ * deletepset 1;
+ *
+ * deletes a pset
+ */
+
+/* Structure containing all info associated with a single pattern
+ block */
+
+struct pcrematch_entry {
+ struct pcrematch_entry *next_;
+ char *pattern_;
+ pcre *pcre_;
+ pcre_extra *pcre_extra_;
+ char *label_;
+};
+
+/* A set of patterns that can be activated and deactived with a single
+ command */
+
+struct pcrematch_set {
+ struct pcrematch_set *next_, *prev_;
+ struct pcrematch_entry *head_;
+ int setid_;
+};
+
+/*
+ * Entire data structure hung off a NPC
+ *
+ * The reason I have done it this way (a void * in npc_data and then
+ * this) was to reduce the number of patches that needed to be applied
+ * to a ragnarok distribution to bring this code online. I
+ * also wanted people to be able to grab this one file to get updates
+ * without having to do a large number of changes.
+ */
+
+struct npc_parse {
+ struct pcrematch_set *active_;
+ struct pcrematch_set *inactive_;
+};
+
+
+/**
+ * delete everythign associated with a entry
+ *
+ * This does NOT do the list management
+ */
+
+void finalize_pcrematch_entry(struct pcrematch_entry *e) {
+//TODO: For some odd reason this causes a already-free'd error under Windows, but not *nix! [Skotlex]
+#ifndef _WIN32
+ if (e->pcre_) {
+ free(e->pcre_);
+ e->pcre_ = NULL;
+ }
+#endif
+ if (e->pcre_extra_) {
+ free(e->pcre_extra_);
+ e->pcre_ = NULL;
+ }
+ aFree(e->pattern_);
+ aFree(e->label_);
+}
+
+/**
+ * Lookup (and possibly create) a new set of patterns by the set id
+ */
+static struct pcrematch_set * lookup_pcreset(struct npc_data *nd,int setid)
+{
+ struct pcrematch_set *pcreset;
+ struct npc_parse *npcParse = (struct npc_parse *) nd->chatdb;
+ if (npcParse == NULL)
+ nd->chatdb = npcParse = (struct npc_parse *)
+ aCalloc(sizeof(struct npc_parse), 1);
+
+ pcreset = npcParse->active_;
+
+ while (pcreset != NULL) {
+ if (pcreset->setid_ == setid)
+ break;
+ pcreset = pcreset->next_;
+ }
+ if (pcreset == NULL)
+ pcreset = npcParse->inactive_;
+
+ while (pcreset != NULL) {
+ if (pcreset->setid_ == setid)
+ break;
+ pcreset = pcreset->next_;
+ }
+
+ if (pcreset == NULL) {
+ pcreset = (struct pcrematch_set *)
+ aCalloc(sizeof(struct pcrematch_set), 1);
+ pcreset->next_ = npcParse->inactive_;
+ if (pcreset->next_ != NULL)
+ pcreset->next_->prev_ = pcreset;
+ pcreset->prev_ = 0;
+ npcParse->inactive_ = pcreset;
+ pcreset->setid_ = setid;
+ }
+
+ return pcreset;
+}
+
+/**
+ * activate a set of patterns.
+ *
+ * if the setid does not exist, this will silently return
+ */
+
+static void activate_pcreset(struct npc_data *nd,int setid) {
+ struct pcrematch_set *pcreset;
+ struct npc_parse *npcParse = (struct npc_parse *) nd->chatdb;
+ if (npcParse == NULL)
+ return; // Nothing to activate...
+ pcreset = npcParse->inactive_;
+ while (pcreset != NULL) {
+ if (pcreset->setid_ == setid)
+ break;
+ pcreset = pcreset->next_;
+ }
+ if (pcreset == NULL)
+ return; // not in inactive list
+ if (pcreset->next_ != NULL)
+ pcreset->next_->prev_ = pcreset->prev_;
+ if (pcreset->prev_ != NULL)
+ pcreset->prev_->next_ = pcreset->next_;
+ else
+ npcParse->inactive_ = pcreset->next_;
+
+ pcreset->prev_ = NULL;
+ pcreset->next_ = npcParse->active_;
+ if (pcreset->next_ != NULL)
+ pcreset->next_->prev_ = pcreset;
+ npcParse->active_ = pcreset;
+}
+
+/**
+ * deactivate a set of patterns.
+ *
+ * if the setid does not exist, this will silently return
+ */
+
+static void deactivate_pcreset(struct npc_data *nd,int setid) {
+ struct pcrematch_set *pcreset;
+ struct npc_parse *npcParse = (struct npc_parse *) nd->chatdb;
+ if (npcParse == NULL)
+ return; // Nothing to deactivate...
+ if (setid == -1) {
+ while(npcParse->active_ != NULL)
+ deactivate_pcreset(nd, npcParse->active_->setid_);
+ return;
+ }
+ pcreset = npcParse->active_;
+ while (pcreset != NULL) {
+ if (pcreset->setid_ == setid)
+ break;
+ pcreset = pcreset->next_;
+ }
+ if (pcreset == NULL)
+ return; // not in active list
+ if (pcreset->next_ != NULL)
+ pcreset->next_->prev_ = pcreset->prev_;
+ if (pcreset->prev_ != NULL)
+ pcreset->prev_->next_ = pcreset->next_;
+ else
+ npcParse->active_ = pcreset->next_;
+
+ pcreset->prev_ = NULL;
+ pcreset->next_ = npcParse->inactive_;
+ if (pcreset->next_ != NULL)
+ pcreset->next_->prev_ = pcreset;
+ npcParse->inactive_ = pcreset;
+}
+
+/**
+ * delete a set of patterns.
+ */
+static void delete_pcreset(struct npc_data *nd,int setid) {
+ int active = 1;
+ struct pcrematch_set *pcreset;
+ struct npc_parse *npcParse = (struct npc_parse *) nd->chatdb;
+ if (npcParse == NULL)
+ return; // Nothing to deactivate...
+ pcreset = npcParse->active_;
+ while (pcreset != NULL) {
+ if (pcreset->setid_ == setid)
+ break;
+ pcreset = pcreset->next_;
+ }
+ if (pcreset == NULL) {
+ active = 0;
+ pcreset = npcParse->inactive_;
+ while (pcreset != NULL) {
+ if (pcreset->setid_ == setid)
+ break;
+ pcreset = pcreset->next_;
+ }
+ }
+ if (pcreset == NULL)
+ return;
+
+ if (pcreset->next_ != NULL)
+ pcreset->next_->prev_ = pcreset->prev_;
+ if (pcreset->prev_ != NULL)
+ pcreset->prev_->next_ = pcreset->next_;
+
+ if(active)
+ npcParse->active_ = pcreset->next_;
+ else
+ npcParse->inactive_ = pcreset->next_;
+
+ pcreset->prev_ = NULL;
+ pcreset->next_ = NULL;
+
+ while (pcreset->head_) {
+ struct pcrematch_entry *n = pcreset->head_->next_;
+ finalize_pcrematch_entry(pcreset->head_);
+ aFree(pcreset->head_); // Cleanin' the last ones.. [Lance]
+ pcreset->head_ = n;
+ }
+
+ aFree(pcreset);
+}
+
+/**
+ * create a new pattern entry
+ */
+static struct pcrematch_entry *create_pcrematch_entry(struct pcrematch_set * set) {
+ struct pcrematch_entry * e = (struct pcrematch_entry *)
+ aCalloc(sizeof(struct pcrematch_entry), 1);
+ struct pcrematch_entry * last = set->head_;
+
+ // Normally we would have just stuck it at the end of the list but
+ // this doesn't sink up with peoples usage pattern. They wanted
+ // the items defined first to have a higher priority then the
+ // items defined later.. as a result, we have to do some work up
+ // front..
+
+ /* if we are the first pattern, stick us at the end */
+ if (last == NULL) {
+ set->head_ = e;
+ return e;
+ }
+
+ /* Look for the last entry */
+ while (last->next_ != NULL)
+ last = last->next_;
+
+ last->next_ = e;
+ e->next_ = NULL;
+
+ return e;
+}
+
+/**
+ * define/compile a new pattern
+ */
+
+void npc_chat_def_pattern(struct npc_data *nd, int setid,
+ const char *pattern, const char *label)
+{
+ const char *err;
+ int erroff;
+
+ struct pcrematch_set * s = lookup_pcreset(nd, setid);
+ struct pcrematch_entry *e = create_pcrematch_entry(s);
+ e->pattern_ = aStrdup(pattern);
+ e->label_ = aStrdup(label);
+ e->pcre_ = pcre_compile(pattern, PCRE_CASELESS, &err, &erroff, NULL);
+ e->pcre_extra_ = pcre_study(e->pcre_, 0, &err);
+}
+
+/**
+ * Delete everything associated with a NPC concerning the pattern
+ * matching code
+ *
+ * this could be more efficent but.. how often do you do this?
+ */
+void npc_chat_finalize(struct npc_data *nd)
+{
+ struct npc_parse *npcParse = (struct npc_parse *) nd->chatdb;
+ if (npcParse == NULL)
+ return;
+
+ while(npcParse->active_)
+ delete_pcreset(nd, npcParse->active_->setid_);
+
+ while(npcParse->inactive_)
+ delete_pcreset(nd, npcParse->inactive_->setid_);
+
+ // Additional cleaning up [Lance]
+ aFree(npcParse);
+}
+
+/**
+ * Handler called whenever a global message is spoken in a NPC's area
+ */
+int npc_chat_sub(struct block_list *bl, va_list ap)
+{
+ struct npc_data *nd = (struct npc_data *)bl;
+ struct npc_parse *npcParse = (struct npc_parse *) nd->chatdb;
+ unsigned char *msg;
+ int len, pos, i;
+ struct map_session_data *sd;
+ struct npc_label_list *lst;
+ struct pcrematch_set *pcreset;
+
+ // Not interested in anything you might have to say...
+ if (npcParse == NULL || npcParse->active_ == NULL)
+ return 0;
+
+ msg = va_arg(ap,unsigned char*);
+ len = va_arg(ap,int);
+ sd = va_arg(ap,struct map_session_data *);
+
+ // grab the active list
+ pcreset = npcParse->active_;
+
+ // interate across all active sets
+ while (pcreset != NULL) {
+ struct pcrematch_entry *e = pcreset->head_;
+ // interate across all patterns in that set
+ while (e != NULL) {
+ int offsets[20];
+ char buf[255];
+ // perform pattern match
+ int r = pcre_exec(e->pcre_, e->pcre_extra_, msg, len, 0,
+ 0, offsets, sizeof(offsets) / sizeof(offsets[0]));
+ if (r >= 0) {
+ // save out the matched strings
+ switch (r) {
+ case 10:
+ memcpy(buf, &msg[offsets[18]], offsets[19]);
+ buf[offsets[19]] = '\0';
+ set_var(sd, "$p9$", buf);
+ case 9:
+ memcpy(buf, &msg[offsets[16]], offsets[17]);
+ buf[offsets[17]] = '\0';
+ set_var(sd, "$p8$", buf);
+ case 8:
+ memcpy(buf, &msg[offsets[14]], offsets[15]);
+ buf[offsets[15]] = '\0';
+ set_var(sd, "$p7$", buf);
+ case 7:
+ memcpy(buf, &msg[offsets[12]], offsets[13]);
+ buf[offsets[13]] = '\0';
+ set_var(sd, "$p6$", buf);
+ case 6:
+ memcpy(buf, &msg[offsets[10]], offsets[11]);
+ buf[offsets[11]] = '\0';
+ set_var(sd, "$p5$", buf);
+ case 5:
+ memcpy(buf, &msg[offsets[8]], offsets[9]);
+ buf[offsets[9]] = '\0';
+ set_var(sd, "$p4$", buf);
+ case 4:
+ memcpy(buf, &msg[offsets[6]], offsets[7]);
+ buf[offsets[7]] = '\0';
+ set_var(sd, "$p3$", buf);
+ case 3:
+ memcpy(buf, &msg[offsets[4]], offsets[5]);
+ buf[offsets[5]] = '\0';
+ set_var(sd, "$p2$", buf);
+ case 2:
+ memcpy(buf, &msg[offsets[2]], offsets[3]);
+ buf[offsets[3]] = '\0';
+ set_var(sd, "$p1$", buf);
+ case 1:
+ memcpy(buf, &msg[offsets[0]], offsets[1]);
+ buf[offsets[1]] = '\0';
+ set_var(sd, "$p0$", buf);
+ }
+
+ // find the target label.. this sucks..
+ lst=nd->u.scr.label_list;
+ pos = -1;
+ for (i = 0; i < nd->u.scr.label_list_num; i++) {
+ if (strncmp(lst[i].name, e->label_, sizeof(lst[i].name)) == 0) {
+ pos = lst[i].pos;
+ break;
+ }
+ }
+ if (pos == -1) {
+ ShowWarning("Unable to find label: %s", e->label_);
+ // unable to find label... do something..
+ return 0;
+ }
+ // run the npc script
+ run_script(nd->u.scr.script,pos,sd->bl.id,nd->bl.id);
+ return 0;
+ }
+ e = e->next_;
+ }
+ pcreset = pcreset->next_;
+ }
+
+ return 0;
+}
+
+// Various script builtins used to support these functions
+
+int buildin_defpattern(struct script_state *st) {
+ int setid=conv_num(st,& (st->stack->stack_data[st->start+2]));
+ char *pattern=conv_str(st,& (st->stack->stack_data[st->start+3]));
+ char *label=conv_str(st,& (st->stack->stack_data[st->start+4]));
+ struct npc_data *nd=(struct npc_data *)map_id2bl(st->oid);
+
+ npc_chat_def_pattern(nd, setid, pattern, label);
+
+ return 0;
+}
+
+int buildin_activatepset(struct script_state *st) {
+ int setid=conv_num(st,& (st->stack->stack_data[st->start+2]));
+ struct npc_data *nd=(struct npc_data *)map_id2bl(st->oid);
+
+ activate_pcreset(nd, setid);
+
+ return 0;
+}
+int buildin_deactivatepset(struct script_state *st) {
+ int setid=conv_num(st,& (st->stack->stack_data[st->start+2]));
+ struct npc_data *nd=(struct npc_data *)map_id2bl(st->oid);
+
+ deactivate_pcreset(nd, setid);
+
+ return 0;
+}
+int buildin_deletepset(struct script_state *st) {
+ int setid=conv_num(st,& (st->stack->stack_data[st->start+2]));
+ struct npc_data *nd=(struct npc_data *)map_id2bl(st->oid);
+
+ delete_pcreset(nd, setid);
+
+ return 0;
+}
+
+
+#endif
diff --git a/src/map/party.c b/src/map/party.c
new file mode 100644
index 000000000..e0b32df0f
--- /dev/null
+++ b/src/map/party.c
@@ -0,0 +1,742 @@
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "../common/timer.h"
+#include "../common/socket.h"
+#include "../common/nullpo.h"
+#include "../common/malloc.h"
+#include "../common/showmsg.h"
+
+#include "party.h"
+#include "pc.h"
+#include "map.h"
+#include "battle.h"
+#include "intif.h"
+#include "clif.h"
+#include "log.h"
+#include "skill.h"
+#include "status.h"
+
+#define PARTY_SEND_XY_INVERVAL 1000 // 座標やHP送信の間隔
+
+static struct dbt* party_db;
+static struct party* party_cache = NULL; //party in cache for skipping consecutive lookups. [Skotlex]
+int party_share_level = 10;
+int party_send_xy_timer(int tid,unsigned int tick,int id,int data);
+/*==========================================
+ * 終了
+ *------------------------------------------
+ */
+void do_final_party(void)
+{
+ party_db->destroy(party_db,NULL);
+}
+// 初期化
+void do_init_party(void)
+{
+ party_db=db_alloc(__FILE__,__LINE__,DB_INT,DB_OPT_RELEASE_DATA,sizeof(int));
+ add_timer_func_list(party_send_xy_timer,"party_send_xy_timer");
+ add_timer_interval(gettick()+PARTY_SEND_XY_INVERVAL,party_send_xy_timer,0,0,PARTY_SEND_XY_INVERVAL);
+}
+
+// 検索
+struct party *party_search(int party_id)
+{
+ if (party_cache && party_cache->party_id == party_id)
+ return party_cache;
+
+ party_cache = idb_get(party_db,party_id);
+ return party_cache;
+}
+int party_searchname_sub(DBKey key,void *data,va_list ap)
+{
+ struct party *p=(struct party *)data,**dst;
+ char *str;
+ str=va_arg(ap,char *);
+ dst=va_arg(ap,struct party **);
+ if(strncmpi(p->name,str,NAME_LENGTH)==0)
+ *dst=p;
+ return 0;
+}
+// パーティ名検索
+struct party* party_searchname(char *str)
+{
+ struct party *p=NULL;
+ party_db->foreach(party_db,party_searchname_sub,str,&p);
+ return p;
+}
+// 作成要求
+int party_create(struct map_session_data *sd,char *name,int item,int item2)
+{
+ nullpo_retr(0, sd);
+
+ if(sd->status.party_id==0)
+ intif_create_party(sd,name,item,item2);
+ else
+ clif_party_created(sd,2);
+ return 0;
+}
+
+// 作成可否
+int party_created(int account_id,int char_id,int fail,int party_id,char *name)
+{
+ struct map_session_data *sd;
+ sd=map_id2sd(account_id);
+
+ nullpo_retr(0, sd);
+ if (sd->status.char_id != char_id)
+ return 0; //unlikely failure...
+
+ if(fail==0){
+ struct party *p;
+ sd->status.party_id=party_id;
+ if(idb_get(party_db,party_id)!=NULL){
+ ShowFatalError("party: id already exists!\n");
+ exit(1);
+ }
+ p=(struct party *)aCalloc(1,sizeof(struct party));
+ p->party_id=party_id;
+ memcpy(p->name, name, NAME_LENGTH);
+ idb_put(party_db,party_id,p);
+ clif_party_created(sd,0); //Success message
+ clif_charnameupdate(sd); //Update other people's display. [Skotlex]
+ }else{
+ clif_party_created(sd,1);
+ }
+ return 0;
+}
+
+// 情報要求
+int party_request_info(int party_id)
+{
+ return intif_request_partyinfo(party_id);
+}
+
+// 所属キャラの確認
+int party_check_member(struct party *p)
+{
+ int i, users;
+ struct map_session_data *sd, **all_sd;
+
+ nullpo_retr(0, p);
+
+ all_sd = map_getallusers(&users);
+
+ for(i=0;i<users;i++)
+ {
+ if((sd = all_sd[i]) && sd->status.party_id==p->party_id)
+ {
+ int j,f=1;
+ for(j=0;j<MAX_PARTY;j++){
+ if(p->member[j].account_id==sd->status.account_id &&
+ p->member[j].char_id==sd->status.char_id)
+ {
+ f=0;
+ break;
+ }
+ }
+
+ if(f){
+ sd->status.party_id=0;
+ if(battle_config.error_log)
+ ShowWarning("party: check_member %d[%s] is not member\n",sd->status.account_id,sd->status.name);
+ }
+ }
+ }
+ return 0;
+}
+
+// 情報所得失敗(そのIDのキャラを全部未所属にする)
+int party_recv_noinfo(int party_id)
+{
+ int i, users;
+ struct map_session_data *sd, **all_sd;
+
+ all_sd = map_getallusers(&users);
+
+ for(i=0;i<users;i++){
+ if((sd = all_sd[i]) && sd->status.party_id==party_id)
+ sd->status.party_id=0;
+ }
+ return 0;
+}
+
+static void* create_party(DBKey key, va_list args) {
+ struct party *p;
+ p=(struct party *)aCalloc(1,sizeof(struct party));
+ return p;
+}
+// 情報所得
+int party_recv_info(struct party *sp)
+{
+ struct map_session_data *sd;
+ struct party *p;
+ int i;
+
+ nullpo_retr(0, sp);
+
+ p= idb_ensure(party_db, sp->party_id, create_party);
+ if (!p->party_id) //party just received.
+ party_check_member(sp);
+ memcpy(p,sp,sizeof(struct party));
+
+ for(i=0;i<MAX_PARTY;i++){ // sdの設定
+ if (!p->member[i].account_id) {
+ p->member[i].sd=NULL;
+ continue;
+ }
+ sd = map_id2sd(p->member[i].account_id);
+ p->member[i].sd = (sd!=NULL && sd->status.party_id==p->party_id && sd->status.char_id == p->member[i].char_id && !sd->state.waitingdisconnect)?sd:NULL;
+ }
+
+
+ for(i=0;i<MAX_PARTY;i++){ // 設定情報の送信
+ sd = p->member[i].sd;
+ if(!sd)
+ continue;
+ // Refresh hp/xy state [LuzZza]
+ clif_party_hp(sd);
+ clif_party_xy(sd);
+ if(sd->state.party_sent==0){
+ clif_party_main_info(p,-1);
+ clif_party_option(p,sd,0x100);
+ clif_party_info(p,-1);
+ sd->state.party_sent=1;
+ }
+ }
+
+ return 0;
+}
+
+// パーティへの勧誘
+int party_invite(struct map_session_data *sd,int account_id)
+{
+ struct map_session_data *tsd= map_id2sd(account_id);
+ struct party *p=party_search(sd->status.party_id);
+ int i,flag=0;
+
+ nullpo_retr(0, sd);
+
+ if(tsd==NULL || p==NULL)
+ return 0;
+ if(!battle_config.invite_request_check) {
+ if (tsd->guild_invite>0 || tsd->trade_partner) { // 相手が取引中かどうか
+ clif_party_inviteack(sd,tsd->status.name,0);
+ return 0;
+ }
+ }
+ if( tsd->status.party_id>0 || tsd->party_invite>0 ){ // 相手の所属確認
+ clif_party_inviteack(sd,tsd->status.name,0);
+ return 0;
+ }
+ for(i=0;i<MAX_PARTY;i++){
+ if(p->member[i].account_id == 0) //Room for a new member.
+ flag = 1;
+ if(p->member[i].account_id==account_id && p->member[i].char_id==tsd->status.char_id){
+ clif_party_inviteack(sd,tsd->status.name,0);
+ return 0;
+ }
+ }
+ if (!flag) { //Full party.
+ clif_party_inviteack(sd,tsd->status.name,2);
+ return 0;
+ }
+
+ tsd->party_invite=sd->status.party_id;
+ tsd->party_invite_account=sd->status.account_id;
+
+ clif_party_invite(sd,tsd);
+ return 1;
+}
+// パーティ勧誘への返答
+int party_reply_invite(struct map_session_data *sd,int account_id,int flag)
+{
+ struct map_session_data *tsd= map_id2sd(account_id);
+
+ nullpo_retr(0, sd);
+
+ if(flag==1){ // 承諾
+ //inter鯖へ追加要求
+ intif_party_addmember( sd->party_invite, sd);
+ return 0;
+ }
+ else { // 拒否
+ sd->party_invite=0;
+ sd->party_invite_account=0;
+ if(tsd==NULL)
+ return 0;
+ clif_party_inviteack(tsd,sd->status.name,1);
+ }
+ return 0;
+}
+// パーティが追加された
+int party_member_added(int party_id,int account_id,int char_id, int flag)
+{
+ struct map_session_data *sd = map_id2sd(account_id),*sd2;
+
+ if(sd == NULL || sd->status.char_id != char_id){
+ if (flag == 0) {
+ if(battle_config.error_log)
+ ShowError("party: member added error %d is not online\n",account_id);
+ intif_party_leave(party_id,account_id,char_id); // キャラ側i登録eきhかaたたぁ脱憎要求も出す
+ }
+ return 0;
+ }
+
+ sd->party_invite=0;
+ sd->party_invite_account=0;
+
+ sd2=map_id2sd(sd->party_invite_account);
+ if (sd2)
+ clif_party_inviteack(sd2,sd->status.name,flag?2:0);
+ if(flag)
+ return 0;
+
+ sd->state.party_sent=0;
+ sd->status.party_id=party_id;
+
+ party_check_conflict(sd);
+ clif_charnameupdate(sd); //Update char name's display [Skotlex]
+ clif_party_hp(sd);
+ clif_party_xy(sd);
+ return 0;
+}
+// パーティ除名要求
+int party_removemember(struct map_session_data *sd,int account_id,char *name)
+{
+ struct party *p;
+ int i;
+
+ nullpo_retr(0, sd);
+
+ if( (p = party_search(sd->status.party_id)) == NULL )
+ return 0;
+
+ for(i=0;i<MAX_PARTY;i++){ // リーダーかどうかチェック
+ if(p->member[i].account_id==sd->status.account_id && p->member[i].char_id==sd->status.char_id) {
+ if(p->member[i].leader)
+ break;
+ return 0;
+ }
+ }
+ if (i >= MAX_PARTY) //Request from someone not in party? o.O
+ return 0;
+
+ for(i=0;i<MAX_PARTY;i++){ // 所属しているか調べる
+ if(p->member[i].account_id==account_id && strncmp(p->member[i].name,name,NAME_LENGTH)==0)
+ {
+ intif_party_leave(p->party_id,account_id,p->member[i].char_id);
+ return 1;
+ }
+ }
+ return 0;
+}
+
+// パーティ脱退要求
+int party_leave(struct map_session_data *sd)
+{
+ struct party *p;
+ int i;
+
+ nullpo_retr(0, sd);
+
+ if( (p = party_search(sd->status.party_id)) == NULL )
+ return 0;
+
+ for(i=0;i<MAX_PARTY;i++){ // 所属しているか
+ if(p->member[i].account_id==sd->status.account_id && p->member[i].char_id==sd->status.char_id){
+ intif_party_leave(p->party_id,sd->status.account_id,sd->status.char_id);
+ return 0;
+ }
+ }
+ return 0;
+}
+// パーティメンバが脱退した
+int party_member_leaved(int party_id,int account_id,int char_id)
+{
+ struct map_session_data *sd=map_id2sd(account_id);
+ struct party *p=party_search(party_id);
+ if (sd && sd->status.char_id != char_id) //Wrong target
+ sd = NULL;
+ if(p!=NULL){
+ int i;
+ for(i=0;i<MAX_PARTY;i++)
+ if(p->member[i].account_id==account_id &&
+ p->member[i].char_id==char_id){
+ clif_party_leaved(p,sd,account_id,p->member[i].name,0x00);
+ p->member[i].account_id=0;
+ p->member[i].char_id=0;
+ p->member[i].sd=NULL;
+ }
+ }
+ if(sd!=NULL && sd->status.party_id==party_id){
+ sd->status.party_id=0;
+ sd->state.party_sent=0;
+ clif_charnameupdate(sd); //Update name display [Skotlex]
+ }
+ return 0;
+}
+// パーティ解散通知
+int party_broken(int party_id)
+{
+ struct party *p;
+ int i;
+ if( (p=party_search(party_id))==NULL )
+ return 0;
+
+ for(i=0;i<MAX_PARTY;i++){
+ if(p->member[i].sd!=NULL){
+ clif_party_leaved(p,p->member[i].sd,
+ p->member[i].account_id,p->member[i].name,0x10);
+ p->member[i].sd->status.party_id=0;
+ p->member[i].sd->state.party_sent=0;
+ }
+ }
+ if (party_cache && party_cache->party_id == party_id)
+ party_cache = NULL;
+ idb_remove(party_db,party_id);
+ return 0;
+}
+// パーティの設定変更要求
+int party_changeoption(struct map_session_data *sd,int exp,int flag)
+{
+ nullpo_retr(0, sd);
+
+ if( sd->status.party_id==0)
+ return 0;
+ intif_party_changeoption(sd->status.party_id,sd->status.account_id,exp,flag);
+ return 0;
+}
+// パーティの設定変更通知
+int party_optionchanged(int party_id,int account_id,int exp,int item,int flag)
+{
+ struct party *p;
+ struct map_session_data *sd=map_id2sd(account_id);
+ if( (p=party_search(party_id))==NULL)
+ return 0;
+
+ if(!(flag&0x01)) p->exp=exp;
+ if(!(flag&0x10)) p->item=item;
+ clif_party_option(p,sd,flag);
+ return 0;
+}
+
+// パーティメンバの移動通知
+int party_recv_movemap(int party_id,int account_id,int char_id, unsigned short map,int online,int lv)
+{
+ struct party *p;
+ int i;
+ if( (p=party_search(party_id))==NULL)
+ return 0;
+ for(i=0;i<MAX_PARTY;i++){
+ struct map_session_data *sd;
+ struct party_member *m=&p->member[i];
+ /* This can never happen...
+ if( m == NULL ){
+ ShowError("party_recv_movemap nullpo?\n");
+ return 0;
+ }
+ */
+ if(m->account_id==account_id && m->char_id==char_id){
+ m->map = map;
+ m->online=online;
+ m->lv=lv;
+ //Check if they still exist on this map server
+ sd = map_id2sd(m->account_id);
+ p->member[i].sd=(sd!=NULL && sd->status.party_id==p->party_id && sd->status.char_id == m->char_id && !sd->state.waitingdisconnect)?sd:NULL;
+ //if so, clear their coordinates as they just changed maps.
+ if (p->member[i].sd) {
+ sd->party_x=-1;
+ sd->party_y=-1;
+ }
+ break;
+ }
+ }
+ if(i==MAX_PARTY){
+ if(battle_config.error_log)
+ ShowError("party: not found member %d on %d[%s]",account_id,party_id,p->name);
+ return 0;
+ }
+
+ clif_party_info(p,-1);
+ return 0;
+}
+
+// パーティメンバの移動
+int party_send_movemap(struct map_session_data *sd)
+{
+ struct party *p;
+
+ nullpo_retr(0, sd);
+
+ if( sd->status.party_id==0 )
+ return 0;
+ intif_party_changemap(sd,1);
+
+ if( sd->state.party_sent ) // もうパーティデータは送信済み
+ return 0;
+
+ // 競合確認
+ party_check_conflict(sd);
+
+ // あるならパーティ情報送信
+ if( (p=party_search(sd->status.party_id))!=NULL ){
+ party_check_member(p); // 所属を確認する
+ if(sd->status.party_id==p->party_id){
+ clif_party_main_info(p,sd->fd);
+ clif_party_option(p,sd,0x100);
+ clif_party_info(p,sd->fd);
+ sd->state.party_sent=1;
+ }
+ }
+
+ return 0;
+}
+// パーティメンバのログアウト
+int party_send_logout(struct map_session_data *sd)
+{
+ struct party *p;
+
+ nullpo_retr(0, sd);
+
+ if( sd->status.party_id>0 )
+ intif_party_changemap(sd,0);
+
+ // sdが無効になるのでパーティ情報から削除
+ if( (p=party_search(sd->status.party_id))!=NULL ){
+ int i;
+ for(i=0;i<MAX_PARTY;i++)
+ if(p->member[i].sd==sd)
+ p->member[i].sd=NULL;
+ }
+
+ return 0;
+}
+// パーティメッセージ送信
+int party_send_message(struct map_session_data *sd,char *mes,int len)
+{
+ if(sd->status.party_id==0)
+ return 0;
+ intif_party_message(sd->status.party_id,sd->status.account_id,mes,len);
+ party_recv_message(sd->status.party_id,sd->status.account_id,mes,len);
+ //Chat Logging support Type 'P'
+ if(log_config.chat&1 //we log everything then
+ || ( log_config.chat&4 //if Party bit is on
+ && ( !agit_flag || !(log_config.chat&16) ))) //if WOE ONLY flag is off or AGIT is OFF
+ log_chat("P", sd->status.party_id, sd->status.char_id, sd->status.account_id, (char*)mapindex_id2name(sd->mapindex), sd->bl.x, sd->bl.y, NULL, mes);
+
+ return 0;
+}
+
+// パーティメッセージ受信
+int party_recv_message(int party_id,int account_id,char *mes,int len)
+{
+ struct party *p;
+ if( (p=party_search(party_id))==NULL)
+ return 0;
+ clif_party_message(p,account_id,mes,len);
+ return 0;
+}
+// パーティ競合確認
+int party_check_conflict(struct map_session_data *sd)
+{
+ nullpo_retr(0, sd);
+
+ intif_party_checkconflict(sd->status.party_id,sd->status.account_id,sd->status.char_id);
+ return 0;
+}
+
+int party_skill_check(struct map_session_data *sd, int party_id, int skillid, int skilllv)
+{
+ struct party *p;
+ struct map_session_data *p_sd;
+ int i;
+
+ if(!party_id || (p=party_search(party_id))==NULL)
+ return 0;
+ for(i=0;i<MAX_PARTY;i++){
+ if ((p_sd = p->member[i].sd) == NULL)
+ continue;
+ switch(skillid) {
+ case TK_COUNTER: //Increase Triple Attack rate of Monks.
+ if((p_sd->class_&MAPID_UPPERMASK) == MAPID_MONK
+ && sd->bl.m == p_sd->bl.m
+ && pc_checkskill(p_sd,MO_TRIPLEATTACK)) {
+ int rate = 50 +50*skilllv; //+100/150/200% success rate
+ status_change_start(&p_sd->bl,SC_SKILLRATE_UP,MO_TRIPLEATTACK,rate,0,0,skill_get_time(SG_FRIEND, 1),0);
+ }
+ break;
+ case MO_TRIPLEATTACK: //Increase Counter rate of Star Gladiators
+ if((p_sd->class_&MAPID_UPPERMASK) == MAPID_STAR_GLADIATOR
+ && sd->bl.m == p_sd->bl.m
+ && pc_checkskill(p_sd,TK_COUNTER)) {
+ int rate = 50 +50*pc_checkskill(p_sd,TK_COUNTER); //+100/150/200% success rate
+ status_change_start(&p_sd->bl,SC_SKILLRATE_UP,TK_COUNTER,rate,0,0,skill_get_time(SG_FRIEND, 1),0);
+ }
+ break;
+ case AM_TWILIGHT2: //Twilight Pharmacy, requires Super Novice
+ if ((p_sd->class_&MAPID_UPPERMASK) == MAPID_SUPER_NOVICE
+ && sd->bl.m == p_sd->bl.m)
+ return 1;
+ break;
+ case AM_TWILIGHT3: //Twilight Pharmacy, Requires Taekwon
+ if ((p_sd->class_&MAPID_NOVICE) == MAPID_TAEKWON
+ && sd->bl.m == p_sd->bl.m)
+ return 1;
+ break;
+ }
+ }
+ return 0;
+}
+
+// 位置やHP通知用
+int party_send_xy_timer_sub(DBKey key,void *data,va_list ap)
+{
+ struct party *p=(struct party *)data;
+ int i;
+
+ nullpo_retr(0, p);
+
+ for(i=0;i<MAX_PARTY;i++){
+ struct map_session_data *sd;
+ if((sd=p->member[i].sd)!=NULL){
+ // 座標通知
+ if(sd->party_x!=sd->bl.x || sd->party_y!=sd->bl.y){
+ clif_party_xy(sd);
+ sd->party_x=sd->bl.x;
+ sd->party_y=sd->bl.y;
+ }
+ }
+ }
+ return 0;
+}
+// 位置やHP通知
+int party_send_xy_timer(int tid,unsigned int tick,int id,int data)
+{
+ party_db->foreach(party_db,party_send_xy_timer_sub,tick);
+ return 0;
+}
+
+// 位置通知クリア
+int party_send_xy_clear(struct party *p)
+{
+ int i;
+
+ nullpo_retr(0, p);
+
+ for(i=0;i<MAX_PARTY;i++){
+ struct map_session_data *sd;
+ if((sd=p->member[i].sd)!=NULL){
+ sd->party_x=-1;
+ sd->party_y=-1;
+ }
+ }
+ return 0;
+}
+
+// exp share and added zeny share [Valaris]
+int party_exp_share(struct party *p,int map,int base_exp,int job_exp,int zeny)
+{
+ struct map_session_data* sd[MAX_PARTY];
+ int i;
+ short c, bonus =100; // modified [Valaris]
+ unsigned long base, job;
+
+ nullpo_retr(0, p);
+
+ for (i = c = 0; i < MAX_PARTY; i++)
+ if ((sd[c] = p->member[i].sd)!=NULL && sd[c]->bl.m == map && !pc_isdead(sd[c])) {
+ if (battle_config.idle_no_share && (pc_issit(sd[c]) || sd[c]->chatID || (sd[c]->idletime < (last_tick - battle_config.idle_no_share))))
+ continue;
+ c++;
+ }
+ if (c < 1)
+ return 0;
+ if (battle_config.party_even_share_bonus) //Valaris's even share exp bonus equation.
+ bonus += (battle_config.party_even_share_bonus*c*(c-1)/10); //Changed Valaris's bonus switch to an equation [Skotlex]
+ else //Official kRO/iRO sites state that the even share bonus is 10% per additional party member.
+ bonus += (c-1)*10;
+ base = (unsigned long)(base_exp/c)*bonus/100;
+ job = (unsigned long)(job_exp/c)*bonus/100;
+ if (base > 0x7fffffff)
+ base = 0x7fffffff;
+ if (job > 0x7fffffff)
+ job = 0x7fffffff;
+ for (i = 0; i < c; i++)
+ {
+ pc_gainexp(sd[i], base, job);
+ if (battle_config.zeny_from_mobs) // zeny from mobs [Valaris]
+ pc_getzeny(sd[i],bonus*zeny/(c*100));
+ }
+ return 0;
+}
+
+int party_send_dot_remove(struct map_session_data *sd)
+{
+ if (sd->status.party_id)
+ clif_party_xy_remove(sd);
+ return 0;
+}
+
+// To use for Taekwon's "Fighting Chant"
+// int c = 0;
+// party_foreachsamemap(party_sub_count, sd, 0, &c);
+int party_sub_count(struct block_list *bl, va_list ap)
+{
+ int *c = va_arg(ap, int*);
+
+ (*c)++;
+ return 1;
+}
+
+// 同じマップのパーティメンバー全体に処理をかける
+// type==0 同じマップ
+// !=0 画面内
+void party_foreachsamemap(int (*func)(struct block_list*,va_list),struct map_session_data *sd,int type,...)
+{
+ struct party *p;
+ va_list ap;
+ int i;
+ int x0,y0,x1,y1;
+ struct block_list *list[MAX_PARTY];
+ int blockcount=0;
+
+ nullpo_retv(sd);
+
+ if((p=party_search(sd->status.party_id))==NULL)
+ return;
+
+ x0=sd->bl.x-AREA_SIZE;
+ y0=sd->bl.y-AREA_SIZE;
+ x1=sd->bl.x+AREA_SIZE;
+ y1=sd->bl.y+AREA_SIZE;
+
+ va_start(ap,type);
+
+ for(i=0;i<MAX_PARTY;i++){
+ struct party_member *m=&p->member[i];
+ if(m->sd!=NULL){
+ if(sd->bl.m!=m->sd->bl.m)
+ continue;
+ if(type!=0 &&
+ (m->sd->bl.x<x0 || m->sd->bl.y<y0 ||
+ m->sd->bl.x>x1 || m->sd->bl.y>y1 ) )
+ continue;
+ list[blockcount++]=&m->sd->bl;
+ }
+ }
+
+ map_freeblock_lock(); // メモリからの解放を禁止する
+
+ for(i=0;i<blockcount;i++)
+ if(list[i]->prev) // 有効かどうかチェック
+ func(list[i],ap);
+
+ map_freeblock_unlock(); // 解放を許可する
+
+ va_end(ap);
+}
diff --git a/src/map/party.h b/src/map/party.h
new file mode 100644
index 000000000..4e9601d68
--- /dev/null
+++ b/src/map/party.h
@@ -0,0 +1,47 @@
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#ifndef _PARTY_H_
+#define _PARTY_H_
+
+#include <stdarg.h>
+
+extern int party_share_level;
+struct party;
+struct map_session_data;
+struct block_list;
+
+void do_init_party(void);
+void do_final_party(void);
+struct party *party_search(int party_id);
+struct party* party_searchname(char *str);
+
+int party_create(struct map_session_data *sd,char *name, int item, int item2);
+int party_created(int account_id,int char_id,int fail,int party_id,char *name);
+int party_request_info(int party_id);
+int party_invite(struct map_session_data *sd,int account_id);
+int party_member_added(int party_id,int account_id,int char_id,int flag);
+int party_leave(struct map_session_data *sd);
+int party_removemember(struct map_session_data *sd,int account_id,char *name);
+int party_member_leaved(int party_id,int account_id,int char_id);
+int party_reply_invite(struct map_session_data *sd,int account_id,int flag);
+int party_recv_noinfo(int party_id);
+int party_recv_info(struct party *sp);
+int party_recv_movemap(int party_id,int account_id,int char_id, unsigned short map,int online,int lv);
+int party_broken(int party_id);
+int party_optionchanged(int party_id,int account_id,int exp,int item,int flag);
+int party_changeoption(struct map_session_data *sd,int exp,int item);
+int party_send_movemap(struct map_session_data *sd);
+int party_send_logout(struct map_session_data *sd);
+int party_send_message(struct map_session_data *sd,char *mes,int len);
+int party_recv_message(int party_id,int account_id,char *mes,int len);
+int party_check_conflict(struct map_session_data *sd);
+int party_skill_check(struct map_session_data *sd, int party_id, int skillid, int skilllv);
+int party_send_xy_clear(struct party *p);
+int party_exp_share(struct party *p,int map,int base_exp,int job_exp,int zeny);
+int party_send_dot_remove(struct map_session_data *sd);
+int party_sub_count(struct block_list *bl, va_list ap);
+void party_foreachsamemap(int (*func)(struct block_list *,va_list),struct map_session_data *sd,int type,...);
+
+
+#endif
diff --git a/src/map/path.c b/src/map/path.c
new file mode 100644
index 000000000..ad5c06f0f
--- /dev/null
+++ b/src/map/path.c
@@ -0,0 +1,483 @@
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "map.h"
+#include "battle.h"
+#include "nullpo.h"
+#include "../common/showmsg.h"
+
+#define MAX_HEAP 150
+
+struct tmp_path { short x,y,dist,before,cost; char dir,flag;};
+#define calc_index(x,y) (((x)+(y)*MAX_WALKPATH) & (MAX_WALKPATH*MAX_WALKPATH-1))
+
+/*==========================================
+ * 経路探索補助heap push
+ *------------------------------------------
+ */
+static void push_heap_path(int *heap,struct tmp_path *tp,int index)
+{
+ int i,h;
+
+ if( heap == NULL || tp == NULL ){
+ ShowError("push_heap_path nullpo\n");
+ return;
+ }
+
+ heap[0]++;
+
+ for(h=heap[0]-1,i=(h-1)/2;
+ h>0 && tp[index].cost<tp[heap[i+1]].cost;
+ i=(h-1)/2)
+ heap[h+1]=heap[i+1],h=i;
+ heap[h+1]=index;
+}
+
+/*==========================================
+ * 経路探索補助heap update
+ * costが減ったので根の方へ移動
+ *------------------------------------------
+ */
+static void update_heap_path(int *heap,struct tmp_path *tp,int index)
+{
+ int i,h;
+
+ nullpo_retv(heap);
+ nullpo_retv(tp);
+
+ for(h=0;h<heap[0];h++)
+ if(heap[h+1]==index)
+ break;
+ if(h==heap[0]){
+ ShowError("update_heap_path bug\n");
+ exit(1);
+ }
+ for(i=(h-1)/2;
+ h>0 && tp[index].cost<tp[heap[i+1]].cost;
+ i=(h-1)/2)
+ heap[h+1]=heap[i+1],h=i;
+ heap[h+1]=index;
+}
+
+/*==========================================
+ * 経路探索補助heap pop
+ *------------------------------------------
+ */
+static int pop_heap_path(int *heap,struct tmp_path *tp)
+{
+ int i,h,k;
+ int ret,last;
+
+ nullpo_retr(-1, heap);
+ nullpo_retr(-1, tp);
+
+ if(heap[0]<=0)
+ return -1;
+ ret=heap[1];
+ last=heap[heap[0]];
+ heap[0]--;
+
+ for(h=0,k=2;k<heap[0];k=k*2+2){
+ if(tp[heap[k+1]].cost>tp[heap[k]].cost)
+ k--;
+ heap[h+1]=heap[k+1], h=k;
+ }
+ if(k==heap[0])
+ heap[h+1]=heap[k], h=k-1;
+
+ for(i=(h-1)/2;
+ h>0 && tp[heap[i+1]].cost>tp[last].cost;
+ i=(h-1)/2)
+ heap[h+1]=heap[i+1],h=i;
+ heap[h+1]=last;
+
+ return ret;
+}
+
+/*==========================================
+ * 現在の点のcost計算
+ *------------------------------------------
+ */
+static int calc_cost(struct tmp_path *p,int x1,int y1)
+{
+ int xd,yd;
+
+ nullpo_retr(0, p);
+
+ xd=x1-p->x;
+ if(xd<0) xd=-xd;
+ yd=y1-p->y;
+ if(yd<0) yd=-yd;
+ return (xd+yd)*10+p->dist;
+}
+
+/*==========================================
+ * 必要ならpathを追加/修正する
+ *------------------------------------------
+ */
+static int add_path(int *heap,struct tmp_path *tp,int x,int y,int dist,int dir,int before,int x1,int y1)
+{
+ int i;
+
+ nullpo_retr(0, heap);
+ nullpo_retr(0, tp);
+
+ i=calc_index(x,y);
+
+ if(tp[i].x==x && tp[i].y==y){
+ if(tp[i].dist>dist){
+ tp[i].dist=dist;
+ tp[i].dir=dir;
+ tp[i].before=before;
+ tp[i].cost=calc_cost(&tp[i],x1,y1);
+ if(tp[i].flag)
+ push_heap_path(heap,tp,i);
+ else
+ update_heap_path(heap,tp,i);
+ tp[i].flag=0;
+ }
+ return 0;
+ }
+
+ if(tp[i].x || tp[i].y)
+ return 1;
+
+ tp[i].x=x;
+ tp[i].y=y;
+ tp[i].dist=dist;
+ tp[i].dir=dir;
+ tp[i].before=before;
+ tp[i].cost=calc_cost(&tp[i],x1,y1);
+ tp[i].flag=0;
+ push_heap_path(heap,tp,i);
+
+ return 0;
+}
+
+
+/*==========================================
+ * (x,y)が移動不可能地帯かどうか
+ * flag 0x10000 遠距離攻撃判定
+ *------------------------------------------
+ */
+static int can_place(struct map_data *m,int x,int y,int flag)
+{
+ nullpo_retr(0, m);
+
+ if(map_getcellp(m,x,y,CELL_CHKPASS))
+ return 1;
+ if((flag&0x10000)&&map_getcellp(m,x,y,CELL_CHKGROUND))
+ return 1;
+ return 0;
+}
+
+/*==========================================
+ * (x0,y0)から(x1,y1)へ1歩で移動可能か計算
+ *------------------------------------------
+ */
+static int can_move(struct map_data *m,int x0,int y0,int x1,int y1,int flag)
+{
+ nullpo_retr(0, m);
+
+ if(x0-x1<-1 || x0-x1>1 || y0-y1<-1 || y0-y1>1)
+ return 0;
+ if(x1<0 || y1<0 || x1>=m->xs || y1>=m->ys)
+ return 0;
+ if(flag&0x20000) //Flag to ignore everything, for use with Taekwon's Jump skill currently. [Skotlex]
+ return 1;
+#ifndef CELL_NOSTACK
+ //In no-stack mode, do not check current cell.
+ if(!can_place(m,x0,y0,flag))
+ return 0;
+#endif
+ if(!can_place(m,x1,y1,flag))
+ return 0;
+ if(x0==x1 || y0==y1)
+ return 1;
+ if(!can_place(m,x0,y1,flag) || !can_place(m,x1,y0,flag))
+ return 0;
+ return 1;
+}
+
+/*==========================================
+ * (x0,y0)から(dx,dy)方向へcountセル分
+ * 吹き飛ばしたあとの座標を所得
+ *------------------------------------------
+ */
+int path_blownpos(int m,int x0,int y0,int dx,int dy,int count)
+{
+ struct map_data *md;
+
+ if(!map[m].gat)
+ return -1;
+ md=&map[m];
+
+ if(count>15){ // 最大10マスに制限
+ if(battle_config.error_log)
+ ShowWarning("path_blownpos: count too many %d !\n",count);
+ count=15;
+ }
+ if(dx>1 || dx<-1 || dy>1 || dy<-1){
+ if(battle_config.error_log)
+ ShowError("path_blownpos: illeagal dx=%d or dy=%d !\n",dx,dy);
+ dx=(dx>=0)?1:((dx<0)?-1:0);
+ dy=(dy>=0)?1:((dy<0)?-1:0);
+ }
+
+ while( (count--)>0 && (dx!=0 || dy!=0) ){
+ if( !can_move(md,x0,y0,x0+dx,y0+dy,0) ){
+ int fx=(dx!=0 && can_move(md,x0,y0,x0+dx,y0,0));
+ int fy=(dy!=0 && can_move(md,x0,y0,x0,y0+dy,0));
+ if( fx && fy ){
+ if(rand()&1) dx=0;
+ else dy=0;
+ }
+ if( !fx ) dx=0;
+ if( !fy ) dy=0;
+ }
+ x0+=dx;
+ y0+=dy;
+ }
+ return (x0<<16)|y0;
+}
+
+/*==========================================
+ * タヒ袮ヘ?ェャハヲメェォェノェヲェォェレェケ
+ *------------------------------------------
+ */
+#define swap(x,y) { int t; t = x; x = y; y = t; }
+int path_search_long(struct shootpath_data *spd,int m,int x0,int y0,int x1,int y1)
+{
+ int dx, dy;
+ int wx = 0, wy = 0;
+ int weight;
+ struct map_data *md;
+
+ if (!map[m].gat)
+ return 0;
+ md = &map[m];
+
+ dx = (x1 - x0);
+ if (dx < 0) {
+ swap(x0, x1);
+ swap(y0, y1);
+ dx = -dx;
+ }
+ dy = (y1 - y0);
+
+ if (spd) {
+ spd->rx = spd->ry = 0;
+ spd->len = 1;
+ spd->x[0] = x0;
+ spd->y[0] = y0;
+ }
+
+ if (map_getcellp(md,x1,y1,CELL_CHKWALL))
+ return 0;
+
+ if (dx > abs(dy)) {
+ weight = dx;
+ if (spd)
+ spd->ry=1;
+ } else {
+ weight = abs(y1 - y0);
+ if (spd)
+ spd->rx=1;
+ }
+
+ while (x0 != x1 || y0 != y1) {
+ if (map_getcellp(md,x0,y0,CELL_CHKWALL))
+ return 0;
+ wx += dx;
+ wy += dy;
+ if (wx >= weight) {
+ wx -= weight;
+ x0 ++;
+ }
+ if (wy >= weight) {
+ wy -= weight;
+ y0 ++;
+ } else if (wy < 0) {
+ wy += weight;
+ y0 --;
+ }
+ if (spd && spd->len<MAX_WALKPATH) {
+ spd->x[spd->len] = x0;
+ spd->y[spd->len] = y0;
+ spd->len++;
+ }
+ }
+
+ return 1;
+}
+
+/*==========================================
+ * path探索 (x0,y0)->(x1,y1)
+ *------------------------------------------
+ */
+int path_search(struct walkpath_data *wpd,int m,int x0,int y0,int x1,int y1,int flag)
+{
+ int heap[MAX_HEAP+1];
+ struct tmp_path tp[MAX_WALKPATH*MAX_WALKPATH];
+ int i,rp,x,y;
+ struct map_data *md;
+ int dx,dy;
+
+ nullpo_retr(0, wpd);
+
+ if(!map[m].gat)
+ return -1;
+ md=&map[m];
+ if(x1<0 || x1>=md->xs || y1<0 || y1>=md->ys || map_getcellp(md,x1,y1,CELL_CHKNOPASS))
+ return -1;
+
+ // easy
+ dx = (x1-x0<0) ? -1 : 1;
+ dy = (y1-y0<0) ? -1 : 1;
+ for(x=x0,y=y0,i=0;x!=x1 || y!=y1;){
+ if(i>=sizeof(wpd->path))
+ return -1;
+ if(x!=x1 && y!=y1){
+ if(!can_move(md,x,y,x+dx,y+dy,flag))
+ break;
+ x+=dx;
+ y+=dy;
+ wpd->path[i++]=(dx<0) ? ((dy>0)? 1 : 3) : ((dy<0)? 5 : 7);
+ } else if(x!=x1){
+ if(!can_move(md,x,y,x+dx,y ,flag))
+ break;
+ x+=dx;
+ wpd->path[i++]=(dx<0) ? 2 : 6;
+ } else if(y!=y1){
+ if(!can_move(md,x,y,x ,y+dy,flag))
+ break;
+ y+=dy;
+ wpd->path[i++]=(dy>0) ? 0 : 4;
+ }
+ }
+ if (x==x1 && y==y1) { //easy path successful.
+ wpd->path_len=i;
+ wpd->path_pos=0;
+ wpd->path_half=0;
+ return 0;
+ }
+
+ if(flag&1)
+ return -1;
+
+ memset(tp,0,sizeof(tp));
+
+ i=calc_index(x0,y0);
+ tp[i].x=x0;
+ tp[i].y=y0;
+ tp[i].dist=0;
+ tp[i].dir=0;
+ tp[i].before=0;
+ tp[i].cost=calc_cost(&tp[i],x1,y1);
+ tp[i].flag=0;
+ heap[0]=0;
+ push_heap_path(heap,tp,calc_index(x0,y0));
+ while(1){
+ int e=0,fromdir;
+
+ if(heap[0]==0)
+ return -1;
+ rp=pop_heap_path(heap,tp);
+ x=tp[rp].x;
+ y=tp[rp].y;
+ if(x==x1 && y==y1){
+ int len,j;
+
+ for(len=0,i=rp;len<100 && i!=calc_index(x0,y0);i=tp[i].before,len++);
+ if(len==100 || len>=sizeof(wpd->path))
+ return -1;
+ wpd->path_len=len;
+ wpd->path_pos=0;
+ wpd->path_half=0;
+ for(i=rp,j=len-1;j>=0;i=tp[i].before,j--)
+ wpd->path[j]=tp[i].dir;
+
+ return 0;
+ }
+ fromdir=tp[rp].dir;
+ if(can_move(md,x,y,x+1,y-1,flag))
+ e+=add_path(heap,tp,x+1,y-1,tp[rp].dist+14,5,rp,x1,y1);
+ if(can_move(md,x,y,x+1,y ,flag))
+ e+=add_path(heap,tp,x+1,y ,tp[rp].dist+10,6,rp,x1,y1);
+ if(can_move(md,x,y,x+1,y+1,flag))
+ e+=add_path(heap,tp,x+1,y+1,tp[rp].dist+14,7,rp,x1,y1);
+ if(can_move(md,x,y,x ,y+1,flag))
+ e+=add_path(heap,tp,x ,y+1,tp[rp].dist+10,0,rp,x1,y1);
+ if(can_move(md,x,y,x-1,y+1,flag))
+ e+=add_path(heap,tp,x-1,y+1,tp[rp].dist+14,1,rp,x1,y1);
+ if(can_move(md,x,y,x-1,y ,flag))
+ e+=add_path(heap,tp,x-1,y ,tp[rp].dist+10,2,rp,x1,y1);
+ if(can_move(md,x,y,x-1,y-1,flag))
+ e+=add_path(heap,tp,x-1,y-1,tp[rp].dist+14,3,rp,x1,y1);
+ if(can_move(md,x,y,x ,y-1,flag))
+ e+=add_path(heap,tp,x ,y-1,tp[rp].dist+10,4,rp,x1,y1);
+ tp[rp].flag=1;
+ if(e || heap[0]>=MAX_HEAP-5)
+ return -1;
+ }
+ return -1;
+}
+
+/*==========================================
+
+ * path探索 (x0,y0)->(x1,y1)
+
+ *------------------------------------------
+
+ */
+
+#ifdef PATH_STANDALONETEST
+char gat[64][64]={
+ {0,0,0,0,0,0,0,0,0,0},
+ {0,0,0,0,0,0,0,0,0,0},
+ {0,0,0,0,0,0,0,0,0,0},
+ {0,0,0,0,0,0,0,0,0,0},
+ {0,0,0,0,1,0,0,0,0,0},
+};
+struct map_data map[1];
+
+/*==========================================
+ * 経路探索ルーチン単体テスト用main関数
+ *------------------------------------------
+ */
+void main(int argc,char *argv[])
+{
+ struct walkpath_data wpd;
+
+ map[0].gat=gat;
+ map[0].xs=64;
+ map[0].ys=64;
+
+ path_search(&wpd,0,3,4,5,4);
+ path_search(&wpd,0,5,4,3,4);
+ path_search(&wpd,0,6,4,3,4);
+ path_search(&wpd,0,7,4,3,4);
+ path_search(&wpd,0,4,3,4,5);
+ path_search(&wpd,0,4,2,4,5);
+ path_search(&wpd,0,4,1,4,5);
+ path_search(&wpd,0,4,5,4,3);
+ path_search(&wpd,0,4,6,4,3);
+ path_search(&wpd,0,4,7,4,3);
+ path_search(&wpd,0,7,4,3,4);
+ path_search(&wpd,0,8,4,3,4);
+ path_search(&wpd,0,9,4,3,4);
+ path_search(&wpd,0,10,4,3,4);
+ path_search(&wpd,0,11,4,3,4);
+ path_search(&wpd,0,12,4,3,4);
+ path_search(&wpd,0,13,4,3,4);
+ path_search(&wpd,0,14,4,3,4);
+ path_search(&wpd,0,15,4,3,4);
+ path_search(&wpd,0,16,4,3,4);
+ path_search(&wpd,0,17,4,3,4);
+ path_search(&wpd,0,18,4,3,4);
+}
+#endif
diff --git a/src/map/pc.c b/src/map/pc.c
new file mode 100644
index 000000000..94aa4c602
--- /dev/null
+++ b/src/map/pc.c
@@ -0,0 +1,8305 @@
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <time.h>
+
+#include "socket.h" // [Valaris]
+#include "timer.h"
+
+#include "malloc.h"
+#include "map.h"
+#include "chrif.h"
+#include "clif.h"
+#include "intif.h"
+#include "pc.h"
+#include "status.h"
+#include "npc.h"
+#include "mob.h"
+#include "pet.h"
+#include "itemdb.h"
+#include "script.h"
+#include "battle.h"
+#include "skill.h"
+#include "party.h"
+#include "guild.h"
+#include "chat.h"
+#include "trade.h"
+#include "storage.h"
+#include "vending.h"
+#include "nullpo.h"
+#include "atcommand.h"
+#include "log.h"
+#include "showmsg.h"
+#include "core.h"
+
+#ifndef TXT_ONLY // mail system [Valaris]
+#include "mail.h"
+#endif
+
+#define PVP_CALCRANK_INTERVAL 1000 // PVP順位計算の間隔
+static int exp_table[14][MAX_LEVEL];
+static short statp[MAX_LEVEL];
+
+// h-files are for declarations, not for implementations... [Shinomori]
+struct skill_tree_entry skill_tree[MAX_PC_CLASS][MAX_SKILL_TREE];
+// timer for night.day implementation
+int day_timer_tid;
+int night_timer_tid;
+
+static int dirx[8]={0,-1,-1,-1,0,1,1,1};
+static int diry[8]={1,1,0,-1,-1,-1,0,1};
+
+struct fame_list smith_fame_list[10];
+struct fame_list chemist_fame_list[10];
+struct fame_list taekwon_fame_list[10];
+
+static unsigned int equip_pos[11]={0x0080,0x0008,0x0040,0x0004,0x0001,0x0200,0x0100,0x0010,0x0020,0x0002,0x8000};
+
+static struct gm_account *gm_account = NULL;
+static int GM_num = 0;
+
+#define MOTD_LINE_SIZE 128
+char motd_text[MOTD_LINE_SIZE][256]; // Message of the day buffer [Valaris]
+
+int pc_isGM(struct map_session_data *sd) {
+ int i;
+
+ nullpo_retr(0, sd);
+
+ if(sd->bl.type!=BL_PC )
+ return 0;
+
+
+ //For console [Wizputer] //Unfortunately the console is "broken" because it shares fd 0 with disconnected players. [Skotlex]
+// if ( sd->fd == 0 )
+// return 99;
+
+ for(i = 0; i < GM_num; i++)
+ if (gm_account[i].account_id == sd->status.account_id)
+ return gm_account[i].level;
+ return 0;
+
+}
+
+int pc_iskiller(struct map_session_data *src, struct map_session_data *target) {
+ nullpo_retr(0, src);
+
+ if(src->bl.type!=BL_PC )
+ return 0;
+ if (src->special_state.killer)
+ return 1;
+
+ if(target->bl.type!=BL_PC )
+ return 0;
+
+ if (target->special_state.killable)
+ return 1;
+
+ return 0;
+}
+
+
+int pc_set_gm_level(int account_id, int level) {
+ int i;
+ for (i = 0; i < GM_num; i++) {
+ if (account_id == gm_account[i].account_id) {
+ gm_account[i].level = level;
+ return 0;
+ }
+ }
+
+ GM_num++;
+ gm_account = (struct gm_account *) aRealloc(gm_account, sizeof(struct gm_account) * GM_num);
+ gm_account[GM_num - 1].account_id = account_id;
+ gm_account[GM_num - 1].level = level;
+ return 0;
+}
+
+static int pc_invincible_timer(int tid,unsigned int tick,int id,int data) {
+ struct map_session_data *sd;
+
+ if( (sd=(struct map_session_data *)map_id2sd(id)) == NULL || sd->bl.type!=BL_PC )
+ return 1;
+
+ if(sd->invincible_timer != tid){
+ if(battle_config.error_log)
+ ShowError("invincible_timer %d != %d\n",sd->invincible_timer,tid);
+ return 0;
+ }
+ sd->invincible_timer=-1;
+ skill_unit_move(&sd->bl,tick,1);
+
+ return 0;
+}
+
+int pc_setinvincibletimer(struct map_session_data *sd,int val) {
+ nullpo_retr(0, sd);
+
+ if(sd->invincible_timer != -1)
+ delete_timer(sd->invincible_timer,pc_invincible_timer);
+ sd->invincible_timer = add_timer(gettick()+val,pc_invincible_timer,sd->bl.id,0);
+ return 0;
+}
+
+int pc_delinvincibletimer(struct map_session_data *sd) {
+ nullpo_retr(0, sd);
+
+ if(sd->invincible_timer != -1) {
+ delete_timer(sd->invincible_timer,pc_invincible_timer);
+ sd->invincible_timer = -1;
+ }
+ skill_unit_move(&sd->bl,gettick(),1);
+ return 0;
+}
+
+static int pc_spiritball_timer(int tid,unsigned int tick,int id,int data) {
+ struct map_session_data *sd;
+
+ if( (sd=(struct map_session_data *)map_id2sd(id)) == NULL || sd->bl.type!=BL_PC )
+ return 1;
+
+ if(sd->spirit_timer[0] != tid){
+ if(battle_config.error_log)
+ ShowError("spirit_timer %d != %d\n",sd->spirit_timer[0],tid);
+ return 0;
+ }
+
+ if(sd->spiritball <= 0) {
+ if(battle_config.error_log)
+ ShowError("Spiritballs are already 0 when pc_spiritball_timer gets called");
+ sd->spiritball = 0;
+ return 0;
+ }
+
+ sd->spiritball--;
+ // I leave this here as bad example [Shinomori]
+ //memcpy( &sd->spirit_timer[0], &sd->spirit_timer[1], sizeof(sd->spirit_timer[0]) * sd->spiritball );
+ memmove( sd->spirit_timer+0, sd->spirit_timer+1, (sd->spiritball)*sizeof(int) );
+ sd->spirit_timer[sd->spiritball]=-1;
+
+ clif_spiritball(sd);
+
+ return 0;
+}
+
+int pc_addspiritball(struct map_session_data *sd,int interval,int max) {
+ nullpo_retr(0, sd);
+
+ if(max > MAX_SKILL_LEVEL)
+ max = MAX_SKILL_LEVEL;
+ if(sd->spiritball < 0)
+ sd->spiritball = 0;
+
+ if(sd->spiritball >= max) {
+ if(sd->spirit_timer[0] != -1)
+ delete_timer(sd->spirit_timer[0],pc_spiritball_timer);
+ // I leave this here as bad example [Shinomori]
+ //memcpy( &sd->spirit_timer[0], &sd->spirit_timer[1], sizeof(sd->spirit_timer[0]) * (sd->spiritball - 1));
+ memmove( sd->spirit_timer+0, sd->spirit_timer+1, (sd->spiritball - 1)*sizeof(int) );
+ //sd->spirit_timer[sd->spiritball-1] = -1; // intentionally, but will be overwritten
+ } else
+ sd->spiritball++;
+
+ sd->spirit_timer[sd->spiritball-1] = add_timer(gettick()+interval,pc_spiritball_timer,sd->bl.id,0);
+ clif_spiritball(sd);
+
+ return 0;
+}
+
+int pc_delspiritball(struct map_session_data *sd,int count,int type) {
+ int i;
+
+ nullpo_retr(0, sd);
+
+ if(sd->spiritball <= 0) {
+ sd->spiritball = 0;
+ return 0;
+ }
+
+ if(count > sd->spiritball)
+ count = sd->spiritball;
+ sd->spiritball -= count;
+ if(count > MAX_SKILL_LEVEL)
+ count = MAX_SKILL_LEVEL;
+
+ for(i=0;i<count;i++) {
+ if(sd->spirit_timer[i] != -1) {
+ delete_timer(sd->spirit_timer[i],pc_spiritball_timer);
+ sd->spirit_timer[i] = -1;
+ }
+ }
+ for(i=count;i<MAX_SKILL_LEVEL;i++) {
+ sd->spirit_timer[i-count] = sd->spirit_timer[i];
+ sd->spirit_timer[i] = -1;
+ }
+
+ if(!type)
+ clif_spiritball(sd);
+
+ return 0;
+}
+
+// Increases a player's and displays a notice to him
+void pc_addfame(struct map_session_data *sd,int count) {
+ nullpo_retv(sd);
+ sd->status.fame += count;
+ if(sd->status.fame > MAX_FAME)
+ sd->status.fame = MAX_FAME;
+ switch(sd->class_&MAPID_UPPERMASK){
+ case MAPID_BLACKSMITH: // Blacksmith
+ clif_fame_blacksmith(sd,count);
+ break;
+ case MAPID_ALCHEMIST: // Alchemist
+ clif_fame_alchemist(sd,count);
+ break;
+ case MAPID_TAEKWON: // Taekwon
+ clif_fame_taekwon(sd,count);
+ break;
+ }
+ //FIXME: Is this needed? It places unnecessary stress on the char server.... >.< [Skotlex]
+ chrif_save(sd,0); // Save to allow up-to-date fame list refresh
+ chrif_reqfamelist(); // Refresh the fame list
+}
+
+// Check whether a player ID is in the Top 10 fame list of its job
+int pc_istop10fame(int char_id,int job) {
+ int i;
+ switch(job){
+ case MAPID_BLACKSMITH: // Blacksmith
+ for(i=0;i<10;i++){
+ if(smith_fame_list[i].id==char_id)
+ return smith_fame_list[i].fame;
+ }
+ break;
+ case MAPID_ALCHEMIST: // Alchemist
+ for(i=0;i<10;i++){
+ if(chemist_fame_list[i].id==char_id)
+ return chemist_fame_list[i].fame;
+ }
+ break;
+ case MAPID_TAEKWON: // Taekwon
+ for(i=0;i<10;i++){
+ if(taekwon_fame_list[i].id==char_id)
+ return taekwon_fame_list[i].fame;
+ }
+ break;
+ }
+
+ return 0;
+}
+
+int pc_setrestartvalue(struct map_session_data *sd,int type) {
+ //?生や養子の場合の元の職業を算出する
+
+ nullpo_retr(0, sd);
+
+ //-----------------------
+ // 死亡した
+ if(sd->special_state.restart_full_recover || // オシリスカ?ド
+ sd->state.snovice_flag == 4) { // [Celest]
+ sd->status.hp=sd->status.max_hp;
+ sd->status.sp=sd->status.max_sp;
+ if (sd->state.snovice_flag == 4) {
+ sd->state.snovice_flag = 0;
+ status_change_start(&sd->bl,SkillStatusChangeTable[MO_STEELBODY],1,0,0,0,skill_get_time(MO_STEELBODY,1),0 );
+ }
+ }
+ else {
+ if((sd->class_&MAPID_BASEMASK) == MAPID_NOVICE && !(sd->class_&JOBL_2) && battle_config.restart_hp_rate < 50) { //ノビは半分回復
+ sd->status.hp=(sd->status.max_hp)/2;
+ }
+ else {
+ if(battle_config.restart_hp_rate <= 0)
+ sd->status.hp = 1;
+ else {
+ sd->status.hp = sd->status.max_hp * battle_config.restart_hp_rate /100;
+ if(sd->status.hp <= 0)
+ sd->status.hp = 1;
+ }
+ }
+ if(battle_config.restart_sp_rate > 0) {
+ int sp = sd->status.max_sp * battle_config.restart_sp_rate /100;
+ if(sd->status.sp < sp)
+ sd->status.sp = sp;
+ }
+ }
+ if(type&1)
+ clif_updatestatus(sd,SP_HP);
+ if(type&1)
+ clif_updatestatus(sd,SP_SP);
+
+ /* removed exp penalty on spawn [Valaris] */
+
+ if(type&2 && (sd->class_&MAPID_UPPERMASK) != MAPID_NOVICE && battle_config.zeny_penalty > 0 && !map[sd->bl.m].flag.nozenypenalty) {
+ int zeny = (int)((double)sd->status.zeny * (double)battle_config.zeny_penalty / 10000.);
+ if(zeny < 1) zeny = 1;
+ sd->status.zeny -= zeny;
+ if(sd->status.zeny < 0) sd->status.zeny = 0;
+ clif_updatestatus(sd,SP_ZENY);
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * Determines if the player can move based on status changes. [Skotlex]
+ *------------------------------------------
+ */
+int pc_can_move(struct map_session_data *sd)
+{
+ if (sd->canmove_tick > gettick() || (sd->opt1 > 0 && sd->opt1 != OPT1_STONEWAIT) ||
+ sd->sc_data[SC_ANKLE].timer != -1 ||
+ sd->sc_data[SC_AUTOCOUNTER].timer !=-1 ||
+ sd->sc_data[SC_TRICKDEAD].timer !=-1 ||
+ sd->sc_data[SC_BLADESTOP].timer !=-1 ||
+ sd->sc_data[SC_SPIDERWEB].timer !=-1 ||
+ (sd->sc_data[SC_DANCING].timer !=-1 && sd->sc_data[SC_DANCING].val4 && sd->sc_data[SC_LONGING].timer == -1) ||
+ (sd->sc_data[SC_DANCING].timer !=-1 && sd->sc_data[SC_DANCING].val1 == CG_HERMODE) || //cannot move while Hermod is active.
+ (sd->sc_data[SC_GOSPEL].timer !=-1 && sd->sc_data[SC_GOSPEL].val4 == BCT_SELF) || // cannot move while gospel is in effect
+ sd->sc_data[SC_STOP].timer != -1 ||
+ sd->sc_data[SC_CLOSECONFINE].timer != -1 ||
+ sd->sc_data[SC_CLOSECONFINE2].timer != -1
+ )
+ return 0;
+
+ if ((sd->status.option & 2) && pc_checkskill(sd, RG_TUNNELDRIVE) <= 0)
+ return 0;
+
+ return 1;
+}
+
+/*==========================================
+ Determines if the GM can give / drop / trade / vend items [Lupus]
+ Args: GM Level (current player GM level)
+ * Returns
+ 1 = this GM can't do it
+ 0 = this one can do it
+ *------------------------------------------
+ */
+int pc_can_give_items(int level) {
+ return ( level >= battle_config.gm_cant_drop_min_lv
+ && level <= battle_config.gm_cant_drop_max_lv);
+}
+
+/*==========================================
+ * ロ?カルプロトタイプ宣言 (必要な物のみ)
+ *------------------------------------------
+ */
+static int pc_walktoxy_sub(struct map_session_data *);
+
+/*==========================================
+ * saveに必要なステ?タス修正を行なう
+ *------------------------------------------
+ */
+int pc_makesavestatus(struct map_session_data *sd)
+{
+ nullpo_retr(0, sd);
+
+ if (sd->state.finalsave)
+ return 0; //Nothing to change.
+
+ // 秒フ色は色?弊害が多いので保存?象にはしない
+ if(!battle_config.save_clothcolor)
+ sd->status.clothes_color=0;
+
+ // 死亡?態だったのでhpを1、位置をセ?ブ場所に?更
+ if(!sd->state.waitingdisconnect) {
+ if(pc_isdead(sd)){
+ pc_setrestartvalue(sd,0);
+ memcpy(&sd->status.last_point,&sd->status.save_point,sizeof(sd->status.last_point));
+ } else {
+ sd->status.last_point.map = sd->mapindex;
+ sd->status.last_point.x = sd->bl.x;
+ sd->status.last_point.y = sd->bl.y;
+ }
+
+ // セ?ブ禁止マップだったので指定位置に移動
+ if(map[sd->bl.m].flag.nosave){
+ struct map_data *m=&map[sd->bl.m];
+ if(m->save.map)
+ memcpy(&sd->status.last_point,&m->save,sizeof(sd->status.last_point));
+ else
+ memcpy(&sd->status.last_point,&sd->status.save_point,sizeof(sd->status.last_point));
+ }
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * 接?暫フ初期化
+ *------------------------------------------
+ */
+int pc_setnewpc(struct map_session_data *sd, int account_id, int char_id, int login_id1, unsigned int client_tick, int sex, int fd) {
+ nullpo_retr(0, sd);
+
+ sd->bl.id = account_id;
+ sd->char_id = char_id;
+ sd->login_id1 = login_id1;
+ sd->login_id2 = 0; // at this point, we can not know the value :(
+ sd->client_tick = client_tick;
+ sd->sex = sex;
+ sd->state.auth = 0;
+ sd->bl.type = BL_PC;
+ sd->canact_tick = sd->canmove_tick = gettick();
+ sd->canlog_tick = gettick();
+ sd->state.waitingdisconnect = 0;
+
+ return 0;
+}
+
+int pc_equippoint(struct map_session_data *sd,int n)
+{
+ int ep = 0;
+ //?生や養子の場合の元の職業を算出する
+
+ nullpo_retr(0, sd);
+
+ if(sd->inventory_data[n]) {
+ ep = sd->inventory_data[n]->equip;
+ if(sd->inventory_data[n]->look == 1 || sd->inventory_data[n]->look == 2 || sd->inventory_data[n]->look == 6) {
+ if(ep == 2 && (pc_checkskill(sd,AS_LEFT) > 0 || (sd->class_&MAPID_UPPERMASK) == MAPID_ASSASSIN))
+ return 34;
+ }
+ }
+ return ep;
+}
+
+int pc_setinventorydata(struct map_session_data *sd)
+{
+ int i,id;
+
+ nullpo_retr(0, sd);
+
+ for(i=0;i<MAX_INVENTORY;i++) {
+ id = sd->status.inventory[i].nameid;
+ sd->inventory_data[i] = itemdb_search(id);
+ }
+ return 0;
+}
+
+int pc_calcweapontype(struct map_session_data *sd)
+{
+ nullpo_retr(0, sd);
+
+ if(sd->weapontype1 != 0 && sd->weapontype2 == 0)
+ sd->status.weapon = sd->weapontype1;
+ if(sd->weapontype1 == 0 && sd->weapontype2 != 0)// 左手武器 Only
+ sd->status.weapon = sd->weapontype2;
+ else if(sd->weapontype1 == 1 && sd->weapontype2 == 1)// ?短?
+ sd->status.weapon = 0x11;
+ else if(sd->weapontype1 == 2 && sd->weapontype2 == 2)// ??手?
+ sd->status.weapon = 0x12;
+ else if(sd->weapontype1 == 6 && sd->weapontype2 == 6)// ??手斧
+ sd->status.weapon = 0x13;
+ else if( (sd->weapontype1 == 1 && sd->weapontype2 == 2) ||
+ (sd->weapontype1 == 2 && sd->weapontype2 == 1) ) // 短? - ?手?
+ sd->status.weapon = 0x14;
+ else if( (sd->weapontype1 == 1 && sd->weapontype2 == 6) ||
+ (sd->weapontype1 == 6 && sd->weapontype2 == 1) ) // 短? - 斧
+ sd->status.weapon = 0x15;
+ else if( (sd->weapontype1 == 2 && sd->weapontype2 == 6) ||
+ (sd->weapontype1 == 6 && sd->weapontype2 == 2) ) // ?手? - 斧
+ sd->status.weapon = 0x16;
+ else
+ sd->status.weapon = sd->weapontype1;
+
+ return 0;
+}
+
+int pc_setequipindex(struct map_session_data *sd)
+{
+ int i,j;
+
+ nullpo_retr(0, sd);
+
+ for(i=0;i<11;i++)
+ sd->equip_index[i] = -1;
+
+ for(i=0;i<MAX_INVENTORY;i++) {
+ if(sd->status.inventory[i].nameid <= 0)
+ continue;
+ if(sd->status.inventory[i].equip) {
+ for(j=0;j<11;j++)
+ if(sd->status.inventory[i].equip & equip_pos[j])
+ sd->equip_index[j] = i;
+ if(sd->status.inventory[i].equip & 0x0002) {
+ if(sd->inventory_data[i])
+ sd->weapontype1 = sd->inventory_data[i]->look;
+ else
+ sd->weapontype1 = 0;
+ }
+ if(sd->status.inventory[i].equip & 0x0020) {
+ if(sd->inventory_data[i]) {
+ if(sd->inventory_data[i]->type == 4) {
+ if(sd->status.inventory[i].equip == 0x0020)
+ sd->weapontype2 = sd->inventory_data[i]->look;
+ else
+ sd->weapontype2 = 0;
+ }
+ else
+ sd->weapontype2 = 0;
+ }
+ else
+ sd->weapontype2 = 0;
+ }
+ }
+ }
+ pc_calcweapontype(sd);
+
+ return 0;
+}
+
+int pc_isequip(struct map_session_data *sd,int n)
+{
+ struct item_data *item;
+ //?生や養子の場合の元の職業を算出する
+
+ nullpo_retr(0, sd);
+
+ item = sd->inventory_data[n];
+
+ if( battle_config.gm_allequip>0 && pc_isGM(sd)>=battle_config.gm_allequip )
+ return 1;
+
+ if(item == NULL)
+ return 0;
+ if(item->elv && sd->status.base_level < (unsigned int)item->elv)
+ return 0;
+ if(item->sex != 2 && sd->status.sex != item->sex)
+ return 0;
+ if(map[sd->bl.m].flag.pvp && (item->flag.no_equip&1)) //optimized by Lupus
+ return 0;
+ if(map_flag_gvg(sd->bl.m) && (item->flag.no_equip>1)) //optimized by Lupus
+ return 0;
+ if((item->equip & 0x0002 || item->equip & 0x0020) && item->type == 4 && sd->sc_data[SC_STRIPWEAPON].timer != -1) // Also works with left-hand weapons [DracoRPG]
+ return 0;
+ if(item->equip & 0x0020 && item->type == 5 && sd->sc_data[SC_STRIPSHIELD].timer != -1) // Also works with left-hand weapons [DracoRPG]
+ return 0;
+ if(item->equip & 0x0010 && sd->sc_data[SC_STRIPARMOR].timer != -1)
+ return 0;
+ if(item->equip & 0x0100 && sd->sc_data[SC_STRIPHELM].timer != -1)
+ return 0;
+
+ if (sd->sc_data[SC_SPIRIT].timer != -1 && sd->sc_data[SC_SPIRIT].val2 == SL_SUPERNOVICE) {
+ //Spirit of Super Novice equip bonuses. [Skotlex]
+ if (sd->status.base_level > 90 && item->equip & 0x301)
+ return 1; //Can equip all helms
+ if (sd->status.base_level > 96 && item->equip & 0x022 && item->type == 4)
+ switch(item->look) { //In weapons, the look determines type of weapon.
+ case 0x01: //Level 4 Knives are equippable.. this means all knives, I'd guess?
+ case 0x02: //All 1H swords
+ case 0x06: //All 1H Axes
+ case 0x08: //All Maces
+ case 0x0a: //All Staffs
+ return 1;
+ }
+ }
+ //Not equipable by class. [Skotlex]
+ if (!(1<<(sd->class_&MAPID_BASEMASK)&item->class_base[(sd->class_&JOBL_2_1)?1:((sd->class_&JOBL_2_2)?2:0)]))
+ return 0;
+
+ //Not equipable by upper class. [Skotlex]
+ if(!(1<<((sd->class_&JOBL_UPPER)?1:((sd->class_&JOBL_BABY)?2:0))&item->class_upper))
+ return 0;
+
+ return 1;
+}
+
+//装備破壊
+int pc_break_equip(struct map_session_data *sd, unsigned short where)
+{
+ int i, j;
+
+ nullpo_retr(-1, sd);
+ if (sd->unbreakable_equip & where)
+ return 0;
+ if (sd->unbreakable >= rand()%100)
+ return 0;
+ if (where == EQP_WEAPON && (sd->status.weapon == 0 || //Bare fists should not break :P
+ sd->status.weapon == 6 || sd->status.weapon == 7 || sd->status.weapon == 8 || // Axes and Maces can't be broken [DracoRPG]
+ sd->status.weapon == 10 || sd->status.weapon == 15 //Rods and Books can't be broken [Skotlex]
+ ))
+ return 0;
+ switch (where) {
+ case EQP_WEAPON:
+ i = SC_CP_WEAPON;
+ break;
+ case EQP_ARMOR:
+ i = SC_CP_ARMOR;
+ break;
+ case EQP_SHIELD:
+ i = SC_CP_SHIELD;
+ break;
+ case EQP_HELM:
+ i = SC_CP_HELM;
+ break;
+ default:
+ return 0;
+ }
+ if (sd->sc_count && sd->sc_data[i].timer != -1)
+ return 0;
+
+ for (i = 0; i < 11; i++) {
+ if ((j = sd->equip_index[i]) > 0 && sd->status.inventory[j].attribute != 1 &&
+ ((where == EQP_HELM && i == 6) ||
+ (where == EQP_ARMOR && i == 7) ||
+ (where == EQP_WEAPON && (i == 8 || i == 9) && sd->inventory_data[j]->type == 4) ||
+ (where == EQP_SHIELD && i == 9 && sd->inventory_data[j]->type == 5)))
+ {
+ sd->status.inventory[j].attribute = 1;
+ pc_unequipitem(sd, j, 3);
+ clif_equiplist(sd);
+ return 1;
+ }
+ }
+
+ return 1;
+}
+
+/*==========================================
+ * session idに問題無し
+ * char鯖から送られてきたステ?タスを設定
+ *------------------------------------------
+ */
+int pc_authok(struct map_session_data *sd, int login_id2, time_t connect_until_time, struct mmo_charstatus *st)
+{
+ struct party *p;
+ struct guild *g;
+ int i;
+ unsigned long tick = gettick();
+
+ if (sd->state.auth) //Temporary debug. [Skotlex]
+ {
+ ShowDebug("pc_authok: Received auth ok for already authorized client (account id %d)!\n", sd->bl.id);
+ return 1;
+ }
+
+ sd->login_id2 = login_id2;
+ memcpy(&sd->status, st, sizeof(*st));
+
+ if (sd->status.sex != sd->sex) {
+ clif_authfail_fd(sd->fd, 0);
+ return 1;
+ }
+
+ //Set the map-server used job id. [Skotlex]
+ sd->class_ = pc_jobid2mapid((unsigned short) sd->status.class_);
+
+ //Initializations to null/0 unneeded since map_session_data was filled with 0 upon allocation.
+ // 基本的な初期化
+ sd->state.connect_new = 1;
+
+ sd->view_class = sd->status.class_;
+ sd->speed = DEFAULT_WALK_SPEED;
+ sd->walktimer = -1;
+ sd->attacktimer = -1;
+ sd->followtimer = -1; // [MouseJstr]
+ sd->skilltimer = -1;
+ sd->skillitem = -1;
+ sd->skillitemlv = -1;
+ sd->invincible_timer = -1;
+
+ sd->canact_tick = tick;
+ sd->canmove_tick = tick;
+ sd->canregen_tick = tick;
+ sd->attackabletime = tick;
+
+ for(i = 0; i < MAX_SKILL_LEVEL; i++)
+ sd->spirit_timer[i] = -1;
+ for(i = 0; i < MAX_SKILLTIMERSKILL; i++)
+ sd->skilltimerskill[i].timer = -1;
+
+
+ if (battle_config.item_auto_get)
+ sd->state.autoloot = 10000;
+
+ if (battle_config.disp_experience)
+ sd->state.showexp = 1;
+ if (battle_config.disp_zeny)
+ sd->state.showzeny = 1;
+
+ if (battle_config.display_delay_skill_fail)
+ sd->state.showdelay = 1;
+
+ // Request all registries.
+ intif_request_registry(sd,7);
+
+ // アイテムチェック
+ pc_setinventorydata(sd);
+ pc_checkitem(sd);
+
+ // pet
+ sd->pet_hungry_timer = -1;
+
+ // ステ?タス異常の初期化
+ for(i = 0; i < MAX_STATUSCHANGE; i++) {
+ sd->sc_data[i].timer=-1;
+ }
+ sd->sc_count=0;
+ if ((battle_config.atc_gmonly == 0 || pc_isGM(sd)) &&
+ (pc_isGM(sd) >= get_atcommand_level(AtCommand_Hide)))
+ sd->status.option &= (OPTION_MASK | OPTION_INVISIBLE);
+ else
+ sd->status.option &= OPTION_MASK;
+
+ // パ?ティ??係の初期化
+ sd->party_x = -1;
+ sd->party_y = -1;
+ sd->guild_x = -1;
+ sd->guild_y = -1;
+
+ // イベント?係の初期化
+ for(i = 0; i < MAX_EVENTTIMER; i++)
+ sd->eventtimer[i] = -1;
+
+ // Moved PVP timer initialisation before set_pos
+ sd->pvp_timer = -1;
+
+ // 位置の設定
+ if ((i=pc_setpos(sd,sd->status.last_point.map, sd->status.last_point.x, sd->status.last_point.y, 0)) != 0) {
+ if(battle_config.error_log)
+ ShowError ("Last_point_map %s - id %d not found (error code %d)\n", mapindex_id2name(sd->status.last_point.map), sd->status.last_point.map, i);
+
+ // try warping to a default map instead (church graveyard)
+ if (pc_setpos(sd, mapindex_name2id(MAP_PRONTERA), 273, 354, 0) != 0) {
+ // if we fail again
+ clif_authfail_fd(sd->fd, 0);
+ return 1;
+ }
+ }
+
+ // pet
+ if (sd->status.pet_id > 0)
+ intif_request_petdata(sd->status.account_id, sd->status.char_id, sd->status.pet_id);
+
+ // パ?ティ、ギルドデ?タの要求
+ if (sd->status.party_id > 0 && (p = party_search(sd->status.party_id)) == NULL)
+ party_request_info(sd->status.party_id);
+ if (sd->status.guild_id > 0)
+ {
+ if ((g = guild_search(sd->status.guild_id)) == NULL)
+ guild_request_info(sd->status.guild_id);
+ else if (strcmp(sd->status.name,g->master) == 0)
+ { //Block Guild Skills to prevent logout/login reuse exploiting. [Skotlex]
+ guild_block_skill(sd, 300000);
+ //Also set the Guild Master flag.
+ sd->state.gmaster_flag = g;
+ }
+ }
+
+ // 通知
+
+ clif_authok(sd);
+ map_addiddb(&sd->bl);
+ if (map_charid2nick(sd->status.char_id) == NULL)
+ map_addchariddb(sd->status.char_id, sd->status.name);
+
+ // Notify everyone that this char logged in [Skotlex].
+ clif_foreachclient(clif_friendslist_toggle_sub, sd->status.account_id, sd->status.char_id, 1);
+
+ sd->feel_level=-1;
+ //Until the reg values arrive, set them to not require trigger...
+ sd->state.event_death = 1;
+ sd->state.event_kill = 1;
+ sd->state.event_disconnect = 1;
+
+ if (night_flag) {
+ char tmpstr[1024];
+ strcpy(tmpstr, msg_txt(500)); // Actually, it's the night...
+ clif_wis_message(sd->fd, wisp_server_name, tmpstr, strlen(tmpstr)+1);
+ //Night packet is sent when it finishes loading the map. [Skotlex]
+ }
+
+ // ステ?タス初期計算など
+ status_calc_pc(sd,1);
+
+ sd->state.auth = 1; //Do not auth him until the initial stats have been placed.
+ { //Add IP field
+ unsigned char *ip = (unsigned char *) &session[sd->fd]->client_addr.sin_addr;
+ if (pc_isGM(sd))
+ ShowInfo("GM Character '"CL_WHITE"%s"CL_RESET"' logged in. (Acc. ID: '"CL_WHITE"%d"CL_RESET"', Connection: '"CL_WHITE"%d"CL_RESET"', Packet Ver: '"CL_WHITE"%d"CL_RESET"', IP: '"CL_WHITE"%d.%d.%d.%d"CL_RESET"', GM Level '"CL_WHITE"%d"CL_RESET"').\n", sd->status.name, sd->status.account_id, sd->fd, sd->packet_ver, ip[0],ip[1],ip[2],ip[3], pc_isGM(sd));
+ else
+ ShowInfo("Character '"CL_WHITE"%s"CL_RESET"' logged in. (Account ID: '"CL_WHITE"%d"CL_RESET"', Connection: '"CL_WHITE"%d"CL_RESET"', Packet Ver: '"CL_WHITE"%d"CL_RESET"', IP: '"CL_WHITE"%d.%d.%d.%d"CL_RESET"').\n", sd->status.name, sd->status.account_id, sd->fd, sd->packet_ver, ip[0],ip[1],ip[2],ip[3]);
+ }
+
+ // Send friends list
+ clif_friendslist_send(sd);
+
+ if (battle_config.display_version == 1){
+ char buf[256];
+ sprintf(buf, "eAthena SVN version: %s", get_svn_revision());
+ clif_displaymessage(sd->fd, buf);
+ }
+
+ // Message of the Day [Valaris]
+ {
+ int ln;
+ for(ln=0; motd_text[ln][0] && ln < MOTD_LINE_SIZE; ln++) {
+ if (battle_config.motd_type)
+ clif_disp_onlyself(sd,motd_text[ln],strlen(motd_text[ln]));
+ else
+ clif_displaymessage(sd->fd, motd_text[ln]);
+ }
+ }
+
+#ifndef TXT_ONLY
+ if(mail_server_enable)
+ mail_check(sd,1); // check mail at login [Valaris]
+#endif
+
+ // message of the limited time of the account
+ if (connect_until_time != 0) { // don't display if it's unlimited or unknow value
+ char tmpstr[1024];
+ strftime(tmpstr, sizeof(tmpstr) - 1, msg_txt(501), localtime(&connect_until_time)); // "Your account time limit is: %d-%m-%Y %H:%M:%S."
+ clif_wis_message(sd->fd, wisp_server_name, tmpstr, strlen(tmpstr)+1);
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * Closes a connection because it failed to be authenticated from the char server.
+ *------------------------------------------
+ */
+int pc_authfail(struct map_session_data *sd) {
+
+ if (sd->state.auth) //Temporary debug. [Skotlex]
+ ShowDebug("pc_authfail: Received auth fail for already authentified client (account id %d)!\n", sd->bl.id);
+
+ if (!sd->fd)
+ ShowDebug("pc_authfail: Received auth fail for a player with no connection (account id %d)!\n", sd->bl.id);
+
+ clif_authfail_fd(sd->fd, 0);
+ return 0;
+}
+
+/*==========================================
+ * Invoked once after the char/account/account2 registry variables are received. [Skotlex]
+ *------------------------------------------
+ */
+int pc_reg_received(struct map_session_data *sd)
+{
+ int i,j;
+ char feel_var[3][NAME_LENGTH] = {"PC_FEEL_SUN","PC_FEEL_MOON","PC_FEEL_STAR"};
+ char hate_var[3][NAME_LENGTH] = {"PC_HATE_MOB_SUN","PC_HATE_MOB_MOON","PC_HATE_MOB_STAR"};
+
+ sd->change_level = pc_readglobalreg(sd,"jobchange_level");
+ sd->die_counter = pc_readglobalreg(sd,"PC_DIE_COUNTER");
+
+ if (pc_checkskill(sd, TK_MISSION)) {
+ sd->mission_mobid = pc_readglobalreg(sd,"TK_MISSION_ID");
+ sd->mission_count = pc_readglobalreg(sd,"TK_MISSION_COUNT");
+ }
+
+ //SG map and mob read [Komurka]
+ for(i=0;i<3;i++) //for now - someone need to make reading from txt/sql
+ {
+ if ((j = pc_readglobalreg(sd,feel_var[i]))!=0) {
+ sd->feel_map[i].index = j;
+ sd->feel_map[i].m = map_mapindex2mapid(j);
+ } else {
+ sd->feel_map[i].index = 0;
+ sd->feel_map[i].m = -1;
+ }
+ sd->hate_mob[i] = pc_readglobalreg(sd,hate_var[i]) - 1;
+
+ }
+
+ if ((i = pc_checkskill(sd,RG_PLAGIARISM)) > 0) {
+ sd->cloneskill_id = pc_readglobalreg(sd,"CLONE_SKILL");
+ if (sd->cloneskill_id > 0) {
+ sd->status.skill[sd->cloneskill_id].id = sd->cloneskill_id;
+ sd->status.skill[sd->cloneskill_id].lv = pc_readglobalreg(sd,"CLONE_SKILL_LV");
+ if (i < sd->status.skill[sd->cloneskill_id].lv)
+ sd->status.skill[sd->cloneskill_id].lv = i;
+ sd->status.skill[sd->cloneskill_id].flag = 13; //cloneskill flag
+ clif_skillinfoblock(sd);
+ }
+ }
+
+ // Automated script events
+ if (script_config.event_requires_trigger) {
+ sd->state.event_death = pc_readglobalreg(sd, script_config.die_event_name);
+ sd->state.event_kill = pc_readglobalreg(sd, script_config.kill_event_name);
+ sd->state.event_disconnect = pc_readglobalreg(sd, script_config.logout_event_name);
+ // if script triggers are not required
+ } else {
+ sd->state.event_death = 1;
+ sd->state.event_kill = 1;
+ sd->state.event_disconnect = 1;
+ }
+
+ if (script_config.event_script_type == 0) {
+ struct npc_data *npc;
+ if ((npc = npc_name2id(script_config.login_event_name))) {
+ run_script(npc->u.scr.script,0,sd->bl.id,npc->bl.id); // PCLoginNPC
+ ShowStatus("Event '"CL_WHITE"%s"CL_RESET"' executed.\n", script_config.login_event_name);
+ }
+ } else {
+ ShowStatus("%d '"CL_WHITE"%s"CL_RESET"' events executed.\n",
+ npc_event_doall_id(script_config.login_event_name, sd->bl.id), script_config.login_event_name);
+ }
+
+ return 0;
+}
+
+static int pc_calc_skillpoint(struct map_session_data* sd)
+{
+ int i,skill,inf2,skill_point=0;
+
+ nullpo_retr(0, sd);
+
+ for(i=1;i<MAX_SKILL;i++){
+ if( (skill = pc_checkskill(sd,i)) > 0) {
+ inf2 = skill_get_inf2(i);
+ if((!(inf2&INF2_QUEST_SKILL) || battle_config.quest_skill_learn) &&
+ !(inf2&(INF2_WEDDING_SKILL|INF2_SPIRIT_SKILL)) //Do not count wedding/link skills. [Skotlex]
+ ) {
+ if(!sd->status.skill[i].flag)
+ skill_point += skill;
+ else if(sd->status.skill[i].flag > 2 && sd->status.skill[i].flag != 13) {
+ skill_point += (sd->status.skill[i].flag - 2);
+ }
+ }
+ }
+ }
+
+ return skill_point;
+}
+
+
+/*==========================================
+ * ?えられるスキルの計算
+ *------------------------------------------
+ */
+int pc_calc_skilltree(struct map_session_data *sd)
+{
+ int i,id=0,flag;
+ int c=0;
+
+ nullpo_retr(0, sd);
+
+ c = pc_mapid2jobid(pc_calc_skilltree_normalize_job(sd), sd->status.sex);
+
+ for(i=0;i<MAX_SKILL;i++){
+ if (sd->status.skill[i].flag != 13) //Don't touch plagiarized skills
+ sd->status.skill[i].id=0; //First clear skills.
+ }
+ for(i=0;i<MAX_SKILL;i++){
+ if (sd->status.skill[i].flag && sd->status.skill[i].flag != 13){
+ sd->status.skill[i].lv=(sd->status.skill[i].flag==1)?0:sd->status.skill[i].flag-2;
+ sd->status.skill[i].flag=0;
+ }
+ else
+ if(sd->sc_data[SC_SPIRIT].timer != -1 && sd->sc_data[SC_SPIRIT].val2 == SL_BARDDANCER && i >= DC_HUMMING && i<= DC_SERVICEFORYOU)
+ { //Enable Bard/Dancer spirit linked skills.
+ if (sd->status.sex) { //Link dancer skills to bard.
+ sd->status.skill[i].id=i;
+ sd->status.skill[i].lv=sd->status.skill[i-8].lv; // Set the level to the same as the linking skill
+ sd->status.skill[i].flag=1; // Tag it as a non-savable, non-uppable, bonus skill
+ } else { //Link bard skills to dancer.
+ sd->status.skill[i-8].id=i-8;
+ sd->status.skill[i-8].lv=sd->status.skill[i].lv; // Set the level to the same as the linking skill
+ sd->status.skill[i-8].flag=1; // Tag it as a non-savable, non-uppable, bonus skill
+ }
+ }
+ }
+
+ if (battle_config.gm_allskill > 0 && pc_isGM(sd) >= battle_config.gm_allskill){
+ for(i=0;i<MAX_SKILL;i++){
+ if (skill_get_inf2(i)&(INF2_NPC_SKILL|INF2_GUILD_SKILL)) //Only skills you can't have are npc/guild ones
+ continue;
+ if (skill_get_max(i) > 0)
+ sd->status.skill[i].id=i;
+ }
+ return 0;
+ }
+ do {
+ flag=0;
+ for(i=0;i < MAX_SKILL_TREE && (id=skill_tree[c][i].id)>0;i++){
+ int j,f=1;
+ if(!battle_config.skillfree) {
+ for(j=0;j<5;j++) {
+ if( skill_tree[c][i].need[j].id &&
+ pc_checkskill(sd,skill_tree[c][i].need[j].id) <
+ skill_tree[c][i].need[j].lv) {
+ f=0;
+ break;
+ }
+ }
+ if (sd->status.job_level < skill_tree[c][i].joblv)
+ f=0;
+ else if (pc_checkskill(sd, NV_BASIC) < 9 && id != NV_BASIC && !(skill_get_inf2(id)&INF2_QUEST_SKILL))
+ f=0; // Do not unlock normal skills when Basic Skills is not maxed out (can happen because of skill reset)
+ }
+ if(sd->status.skill[id].id==0 ){
+ if(sd->sc_data[SC_SPIRIT].timer != -1 && skill_get_inf2(id)&INF2_SPIRIT_SKILL) { //Enable Spirit Skills. [Skotlex]
+ sd->status.skill[id].id=id;
+ sd->status.skill[id].lv=1;
+ sd->status.skill[id].flag=1; //So it is not saved, and tagged as a "bonus" skill.
+ flag=1;
+ } else if (f){
+ sd->status.skill[id].id=id;
+ flag=1;
+ }
+ }
+ }
+ } while(flag);
+ if ((sd->class_&MAPID_UPPERMASK) == MAPID_TAEKWON && sd->status.base_level >= 90 && pc_istop10fame(sd->char_id, MAPID_TAEKWON)) {
+ //Grant all Taekwon Tree, but only as bonus skills in case they drop from ranking. [Skotlex]
+ for(i=0;i < MAX_SKILL_TREE && (id=skill_tree[c][i].id)>0;i++){
+ if ((skill_get_inf2(id)&(INF2_QUEST_SKILL|INF2_WEDDING_SKILL)))
+ continue; //Do not include Quest/Wedding skills.
+ if(sd->status.skill[id].id==0 ){
+ sd->status.skill[id].id=id;
+ sd->status.skill[id].flag=1; //So it is not saved, and tagged as a "bonus" skill.
+ } else
+ sd->status.skill[id].flag=sd->status.skill[id].lv+2;
+ sd->status.skill[id].lv= skill_tree_get_max(id, sd->status.class_);
+ }
+ }
+
+ return 0;
+}
+
+// Make sure all the skills are in the correct condition
+// before persisting to the backend.. [MouseJstr]
+int pc_clean_skilltree(struct map_session_data *sd) {
+ int i;
+ for (i = 0; i < MAX_SKILL; i++){
+ if (sd->status.skill[i].flag == 13){
+ sd->status.skill[i].id = 0;
+ sd->status.skill[i].lv = 0;
+ sd->status.skill[i].flag = 0;
+ }
+ }
+
+ return 0;
+}
+
+int pc_calc_skilltree_normalize_job(struct map_session_data *sd) {
+ int skill_point;
+ int c = sd->class_;
+
+ if (!battle_config.skillup_limit || !(sd->class_&JOBL_2) || (sd->class_&MAPID_UPPERMASK) == MAPID_SUPER_NOVICE)
+ return c; //Only Normalize non-first classes (and non-super novice)
+
+ skill_point = pc_calc_skillpoint(sd);
+ if(skill_point < 9)
+ c = MAPID_NOVICE;
+ else if (sd->status.skill_point >= (int)sd->status.job_level
+ && ((sd->change_level > 0 && skill_point < sd->change_level+8) || skill_point < 58)) {
+ //Send it to first class.
+ c &= MAPID_BASEMASK;
+ }
+ if (sd->class_&JOBL_UPPER) //Convert to Upper
+ c |= JOBL_UPPER;
+ else if (sd->class_&JOBL_BABY) //Convert to Baby
+ c |= JOBL_BABY;
+
+ return c;
+}
+
+/*==========================================
+ * 重量アイコンの確認
+ *------------------------------------------
+ */
+int pc_checkweighticon(struct map_session_data *sd)
+{
+ int flag=0;
+
+ nullpo_retr(0, sd);
+
+ //Consider the battle option 50% criteria....
+ if(sd->weight*100 >= sd->max_weight*battle_config.natural_heal_weight_rate)
+ flag=1;
+ if(sd->weight*10 >= sd->max_weight*9)
+ flag=2;
+
+ if(flag==1){
+ if(sd->sc_data[SC_WEIGHT50].timer==-1)
+ status_change_start(&sd->bl,SC_WEIGHT50,0,0,0,0,0,0);
+ }else{
+ if(sd->sc_data[SC_WEIGHT50].timer!=-1)
+ status_change_end(&sd->bl,SC_WEIGHT50,-1);
+ }
+ if(flag==2){
+ if(sd->sc_data[SC_WEIGHT90].timer==-1)
+ status_change_start(&sd->bl,SC_WEIGHT90,0,0,0,0,0,0);
+ }else{
+ if(sd->sc_data[SC_WEIGHT90].timer!=-1)
+ status_change_end(&sd->bl,SC_WEIGHT90,-1);
+ }
+ return 0;
+}
+
+/*==========================================
+ * ? 備品による能力等のボ?ナス設定
+ *------------------------------------------
+ */
+int pc_bonus(struct map_session_data *sd,int type,int val)
+{
+ int i;
+ nullpo_retr(0, sd);
+
+ switch(type){
+ case SP_STR:
+ case SP_AGI:
+ case SP_VIT:
+ case SP_INT:
+ case SP_DEX:
+ case SP_LUK:
+ if(sd->state.lr_flag != 2)
+ sd->parame[type-SP_STR]+=val;
+ break;
+ case SP_ATK1:
+ if(!sd->state.lr_flag)
+ sd->right_weapon.watk+=val;
+ else if(sd->state.lr_flag == 1)
+ sd->left_weapon.watk+=val;
+ break;
+ case SP_ATK2:
+ if(!sd->state.lr_flag)
+ sd->right_weapon.watk2+=val;
+ else if(sd->state.lr_flag == 1)
+ sd->left_weapon.watk2+=val;
+ break;
+ case SP_BASE_ATK:
+ if(sd->state.lr_flag != 2)
+ sd->base_atk+=val;
+ break;
+ case SP_MATK1:
+ if(sd->state.lr_flag != 2)
+ sd->matk1 += val;
+ break;
+ case SP_MATK2:
+ if(sd->state.lr_flag != 2)
+ sd->matk2 += val;
+ break;
+ case SP_MATK:
+ if(sd->state.lr_flag != 2) {
+ sd->matk1 += val;
+ sd->matk2 += val;
+ }
+ break;
+ case SP_DEF1:
+ if(sd->state.lr_flag != 2)
+ sd->def+=val;
+ break;
+ case SP_DEF2:
+ if(sd->state.lr_flag != 2)
+ sd->def2+=val;
+ break;
+ case SP_MDEF1:
+ if(sd->state.lr_flag != 2)
+ sd->mdef+=val;
+ break;
+ case SP_MDEF2:
+ if(sd->state.lr_flag != 2)
+ sd->mdef+=val;
+ break;
+ case SP_HIT:
+ if(sd->state.lr_flag != 2)
+ sd->hit+=val;
+ else
+ sd->arrow_hit+=val;
+ break;
+ case SP_FLEE1:
+ if(sd->state.lr_flag != 2)
+ sd->flee+=val;
+ break;
+ case SP_FLEE2:
+ if(sd->state.lr_flag != 2)
+ sd->flee2+=val*10;
+ break;
+ case SP_CRITICAL:
+ if(sd->state.lr_flag != 2)
+ sd->critical+=val*10;
+ else
+ sd->arrow_cri += val*10;
+ break;
+ case SP_ATKELE:
+ if(!sd->state.lr_flag)
+ sd->right_weapon.atk_ele=val;
+ else if(sd->state.lr_flag == 1)
+ sd->left_weapon.atk_ele=val;
+ else if(sd->state.lr_flag == 2)
+ sd->arrow_ele=val;
+ break;
+ case SP_DEFELE:
+ if(sd->state.lr_flag != 2)
+ sd->def_ele=val;
+ break;
+ case SP_MAXHP:
+ if(sd->state.lr_flag != 2)
+ sd->status.max_hp+=val;
+ break;
+ case SP_MAXSP:
+ if(sd->state.lr_flag != 2)
+ sd->status.max_sp+=val;
+ break;
+ case SP_CASTRATE:
+ if(sd->state.lr_flag != 2)
+ sd->castrate+=val;
+ break;
+ case SP_MAXHPRATE:
+ if(sd->state.lr_flag != 2)
+ sd->hprate+=val;
+ break;
+ case SP_MAXSPRATE:
+ if(sd->state.lr_flag != 2)
+ sd->sprate+=val;
+ break;
+ case SP_SPRATE:
+ if(sd->state.lr_flag != 2)
+ sd->dsprate+=val;
+ break;
+ case SP_ATTACKRANGE:
+ if(!sd->state.lr_flag)
+ sd->attackrange += val;
+ else if(sd->state.lr_flag == 1)
+ sd->attackrange_ += val;
+ else if(sd->state.lr_flag == 2)
+ sd->arrow_range += val;
+ break;
+ case SP_ADD_SPEED: //Raw increase
+ if(sd->state.lr_flag != 2)
+ sd->speed -= val;
+ break;
+ case SP_SPEED_RATE: //Non stackable increase
+ if(sd->state.lr_flag != 2 && sd->speed_rate > 100-val)
+ sd->speed_rate = 100-val;
+ break;
+ case SP_SPEED_ADDRATE: //Stackable increase
+ if(sd->state.lr_flag != 2)
+ sd->speed_add_rate = sd->speed_add_rate * (100-val)/100;
+ break;
+ case SP_ASPD: //Raw increase
+ if(sd->state.lr_flag != 2)
+ sd->aspd -= val*10;
+ break;
+ case SP_ASPD_RATE: //Non stackable increase
+ if(sd->state.lr_flag != 2 && sd->aspd_rate > 100-val)
+ sd->aspd_rate = 100-val;
+ break;
+ case SP_ASPD_ADDRATE: //Stackable increase - Made it linear as per rodatazone
+ if(sd->state.lr_flag != 2)
+ sd->aspd_add_rate -= val;
+ break;
+ case SP_HP_RECOV_RATE:
+ if(sd->state.lr_flag != 2)
+ sd->hprecov_rate += val;
+ break;
+ case SP_SP_RECOV_RATE:
+ if(sd->state.lr_flag != 2)
+ sd->sprecov_rate += val;
+ break;
+ case SP_CRITICAL_DEF:
+ if(sd->state.lr_flag != 2)
+ sd->critical_def += val;
+ break;
+ case SP_NEAR_ATK_DEF:
+ if(sd->state.lr_flag != 2)
+ sd->near_attack_def_rate += val;
+ break;
+ case SP_LONG_ATK_DEF:
+ if(sd->state.lr_flag != 2)
+ sd->long_attack_def_rate += val;
+ break;
+ case SP_DOUBLE_RATE:
+ if(sd->state.lr_flag == 0 && sd->double_rate < val)
+ sd->double_rate = val;
+ break;
+ case SP_DOUBLE_ADD_RATE:
+ if(sd->state.lr_flag == 0)
+ sd->double_add_rate += val;
+ break;
+ case SP_MATK_RATE:
+ if(sd->state.lr_flag != 2)
+ sd->matk_rate += val;
+ break;
+ case SP_IGNORE_DEF_ELE:
+ if(!sd->state.lr_flag)
+ sd->right_weapon.ignore_def_ele |= 1<<val;
+ else if(sd->state.lr_flag == 1)
+ sd->left_weapon.ignore_def_ele |= 1<<val;
+ break;
+ case SP_IGNORE_DEF_RACE:
+ if(!sd->state.lr_flag)
+ sd->right_weapon.ignore_def_race |= 1<<val;
+ else if(sd->state.lr_flag == 1)
+ sd->left_weapon.ignore_def_race |= 1<<val;
+ break;
+ case SP_ATK_RATE:
+ if(sd->state.lr_flag != 2)
+ sd->atk_rate += val;
+ break;
+ case SP_MAGIC_ATK_DEF:
+ if(sd->state.lr_flag != 2)
+ sd->magic_def_rate += val;
+ break;
+ case SP_MISC_ATK_DEF:
+ if(sd->state.lr_flag != 2)
+ sd->misc_def_rate += val;
+ break;
+ case SP_IGNORE_MDEF_ELE:
+ if(sd->state.lr_flag != 2)
+ sd->ignore_mdef_ele |= 1<<val;
+ break;
+ case SP_IGNORE_MDEF_RACE:
+ if(sd->state.lr_flag != 2)
+ sd->ignore_mdef_race |= 1<<val;
+ break;
+ case SP_PERFECT_HIT_RATE:
+ if(sd->state.lr_flag != 2 && sd->perfect_hit < val)
+ sd->perfect_hit = val;
+ break;
+ case SP_PERFECT_HIT_ADD_RATE:
+ if(sd->state.lr_flag != 2)
+ sd->perfect_hit_add += val;
+ break;
+ case SP_CRITICAL_RATE:
+ if(sd->state.lr_flag != 2)
+ sd->critical_rate+=val;
+ break;
+ case SP_DEF_RATIO_ATK_ELE:
+ if(!sd->state.lr_flag)
+ sd->right_weapon.def_ratio_atk_ele |= 1<<val;
+ else if(sd->state.lr_flag == 1)
+ sd->left_weapon.def_ratio_atk_ele |= 1<<val;
+ break;
+ case SP_DEF_RATIO_ATK_RACE:
+ if(!sd->state.lr_flag)
+ sd->right_weapon.def_ratio_atk_race |= 1<<val;
+ else if(sd->state.lr_flag == 1)
+ sd->left_weapon.def_ratio_atk_race |= 1<<val;
+ break;
+ case SP_HIT_RATE:
+ if(sd->state.lr_flag != 2)
+ sd->hit_rate += val;
+ break;
+ case SP_FLEE_RATE:
+ if(sd->state.lr_flag != 2)
+ sd->flee_rate += val;
+ break;
+ case SP_FLEE2_RATE:
+ if(sd->state.lr_flag != 2)
+ sd->flee2_rate += val;
+ break;
+ case SP_DEF_RATE:
+ if(sd->state.lr_flag != 2)
+ sd->def_rate += val;
+ break;
+ case SP_DEF2_RATE:
+ if(sd->state.lr_flag != 2)
+ sd->def2_rate += val;
+ break;
+ case SP_MDEF_RATE:
+ if(sd->state.lr_flag != 2)
+ sd->mdef_rate += val;
+ break;
+ case SP_MDEF2_RATE:
+ if(sd->state.lr_flag != 2)
+ sd->mdef2_rate += val;
+ break;
+ case SP_RESTART_FULL_RECOVER:
+ if(sd->state.lr_flag != 2)
+ sd->special_state.restart_full_recover = 1;
+ break;
+ case SP_NO_CASTCANCEL:
+ if(sd->state.lr_flag != 2)
+ sd->special_state.no_castcancel = 1;
+ break;
+ case SP_NO_CASTCANCEL2:
+ if(sd->state.lr_flag != 2)
+ sd->special_state.no_castcancel2 = 1;
+ break;
+ case SP_NO_SIZEFIX:
+ if(sd->state.lr_flag != 2)
+ sd->special_state.no_sizefix = 1;
+ break;
+ case SP_NO_MAGIC_DAMAGE:
+ if(sd->state.lr_flag != 2)
+ sd->special_state.no_magic_damage = 1;
+ break;
+ case SP_NO_WEAPON_DAMAGE:
+ if(sd->state.lr_flag != 2)
+ sd->special_state.no_weapon_damage = 1;
+ break;
+ case SP_NO_GEMSTONE:
+ if(sd->state.lr_flag != 2)
+ sd->special_state.no_gemstone = 1;
+ break;
+ case SP_INFINITE_ENDURE:
+ if(sd->state.lr_flag != 2)
+ sd->special_state.infinite_endure = 1;
+ break;
+ case SP_INTRAVISION: // Maya Purple Card effect allowing to see Hiding/Cloaking people [DracoRPG]
+ if(sd->state.lr_flag != 2)
+ sd->special_state.intravision = 1;
+ break;
+ case SP_SPLASH_RANGE:
+ if(sd->state.lr_flag != 2 && sd->splash_range < val)
+ sd->splash_range = val;
+ break;
+ case SP_SPLASH_ADD_RANGE:
+ if(sd->state.lr_flag != 2)
+ sd->splash_add_range += val;
+ break;
+ case SP_SHORT_WEAPON_DAMAGE_RETURN:
+ if(sd->state.lr_flag != 2)
+ sd->short_weapon_damage_return += val;
+ break;
+ case SP_LONG_WEAPON_DAMAGE_RETURN:
+ if(sd->state.lr_flag != 2)
+ sd->long_weapon_damage_return += val;
+ break;
+ case SP_MAGIC_DAMAGE_RETURN: //AppleGirl Was Here
+ if(sd->state.lr_flag != 2)
+ sd->magic_damage_return += val;
+ break;
+ case SP_ALL_STATS: // [Valaris]
+ if(sd->state.lr_flag!=2) {
+ sd->parame[SP_STR-SP_STR]+=val;
+ sd->parame[SP_AGI-SP_STR]+=val;
+ sd->parame[SP_VIT-SP_STR]+=val;
+ sd->parame[SP_INT-SP_STR]+=val;
+ sd->parame[SP_DEX-SP_STR]+=val;
+ sd->parame[SP_LUK-SP_STR]+=val;
+ }
+ break;
+ case SP_AGI_VIT: // [Valaris]
+ if(sd->state.lr_flag!=2) {
+ sd->parame[SP_AGI-SP_STR]+=val;
+ sd->parame[SP_VIT-SP_STR]+=val;
+ }
+ break;
+ case SP_AGI_DEX_STR: // [Valaris]
+ if(sd->state.lr_flag!=2) {
+ sd->parame[SP_AGI-SP_STR]+=val;
+ sd->parame[SP_DEX-SP_STR]+=val;
+ sd->parame[SP_STR-SP_STR]+=val;
+ }
+ break;
+ case SP_PERFECT_HIDE: // [Valaris]
+ if(sd->state.lr_flag!=2) {
+ sd->state.perfect_hiding=1;
+ }
+ break;
+ case SP_DISGUISE: // Disguise script for items [Valaris]
+ if(sd->state.lr_flag!=2 && !sd->state.disguised && !pc_isriding(sd)) {
+ clif_clearchar(&sd->bl, 0);
+ sd->disguise=val;
+ clif_changeoption(&sd->bl);
+ clif_spawnpc(sd);
+ }
+ break;
+ case SP_UNBREAKABLE:
+ if(sd->state.lr_flag!=2) {
+ sd->unbreakable += val;
+ }
+ break;
+ case SP_UNBREAKABLE_WEAPON:
+ if(sd->state.lr_flag != 2)
+ sd->unbreakable_equip |= EQP_WEAPON;
+ break;
+ case SP_UNBREAKABLE_ARMOR:
+ if(sd->state.lr_flag != 2)
+ sd->unbreakable_equip |= EQP_ARMOR;
+ break;
+ case SP_UNBREAKABLE_HELM:
+ if(sd->state.lr_flag != 2)
+ sd->unbreakable_equip |= EQP_HELM;
+ break;
+ case SP_UNBREAKABLE_SHIELD:
+ if(sd->state.lr_flag != 2)
+ sd->unbreakable_equip |= EQP_SHIELD;
+ break;
+ case SP_CLASSCHANGE: // [Valaris]
+ if(sd->state.lr_flag !=2){
+ sd->classchange=val;
+ }
+ break;
+ case SP_LONG_ATK_RATE:
+ //if(sd->state.lr_flag != 2 && sd->long_attack_atk_rate < val)
+ // sd->long_attack_atk_rate = val;
+ if(sd->state.lr_flag != 2) //[Lupus] it should stack, too. As any other cards rate bonuses
+ sd->long_attack_atk_rate+=val;
+ break;
+ case SP_BREAK_WEAPON_RATE:
+ if(sd->state.lr_flag != 2)
+ sd->break_weapon_rate+=val;
+ break;
+ case SP_BREAK_ARMOR_RATE:
+ if(sd->state.lr_flag != 2)
+ sd->break_armor_rate+=val;
+ break;
+ case SP_ADD_STEAL_RATE:
+ if(sd->state.lr_flag != 2)
+ sd->add_steal_rate+=val;
+ break;
+ case SP_DELAYRATE:
+ if(sd->state.lr_flag != 2)
+ sd->delayrate+=val;
+ break;
+ case SP_CRIT_ATK_RATE:
+ if(sd->state.lr_flag != 2)
+ sd->crit_atk_rate += val;
+ break;
+ case SP_NO_REGEN:
+ if(sd->state.lr_flag != 2)
+ sd->no_regen = val;
+ break;
+ case SP_UNSTRIPABLE_WEAPON:
+ if(sd->state.lr_flag != 2)
+ sd->unstripable_equip |= EQP_WEAPON;
+ break;
+ case SP_UNSTRIPABLE:
+ case SP_UNSTRIPABLE_ARMOR:
+ if(sd->state.lr_flag != 2)
+ sd->unstripable_equip |= EQP_ARMOR;
+ break;
+ case SP_UNSTRIPABLE_HELM:
+ if(sd->state.lr_flag != 2)
+ sd->unstripable_equip |= EQP_HELM;
+ break;
+ case SP_UNSTRIPABLE_SHIELD:
+ if(sd->state.lr_flag != 2)
+ sd->unstripable_equip |= EQP_SHIELD;
+ break;
+ case SP_HP_DRAIN_VALUE:
+ if(!sd->state.lr_flag) {
+ sd->right_weapon.hp_drain_value += val;
+ }
+ else if(sd->state.lr_flag == 1) {
+ sd->left_weapon.hp_drain_value += val;
+ }
+ break;
+ case SP_SP_DRAIN_VALUE:
+ if(!sd->state.lr_flag) {
+ sd->right_weapon.sp_drain_value += val;
+ }
+ else if(sd->state.lr_flag == 1) {
+ sd->left_weapon.sp_drain_value += val;
+ }
+ break;
+ case SP_SP_GAIN_VALUE:
+ if(!sd->state.lr_flag)
+ sd->sp_gain_value += val;
+ break;
+ case SP_IGNORE_DEF_MOB: // 0:normal monsters only, 1:affects boss monsters as well
+ if(!sd->state.lr_flag)
+ sd->right_weapon.ignore_def_mob |= 1<<val;
+ else if(sd->state.lr_flag == 1)
+ sd->left_weapon.ignore_def_mob |= 1<<val;
+ break;
+ case SP_HP_GAIN_VALUE:
+ if(!sd->state.lr_flag)
+ sd->hp_gain_value += val;
+ break;
+ case SP_DAMAGE_WHEN_UNEQUIP:
+ if(!sd->state.lr_flag) {
+ for (i=0; i<11; i++) {
+ //I think this one is bugged, notice how it uses the item_db info rather
+ // than inventory equipped position index [Skotlex]
+// if (sd->inventory_data[current_equip_item_index]->equip & equip_pos[i]) {
+ if(sd->status.inventory[current_equip_item_index].equip & equip_pos[i]) {
+ sd->unequip_losehp[i] += val;
+ break;
+ }
+ }
+ }
+ break;
+ case SP_LOSESP_WHEN_UNEQUIP:
+ if(!sd->state.lr_flag) {
+ for (i=0; i<11; i++) {
+// if (sd->inventory_data[current_equip_item_index]->equip & equip_pos[i]) {
+ if(sd->status.inventory[current_equip_item_index].equip & equip_pos[i]) {
+ sd->unequip_losesp[i] += val;
+ break;
+ }
+ }
+ }
+ break;
+ default:
+ if(battle_config.error_log)
+ ShowWarning("pc_bonus: unknown type %d %d !\n",type,val);
+ break;
+ }
+ return 0;
+}
+
+/*==========================================
+ * ? 備品による能力等のボ?ナス設定
+ *------------------------------------------
+ */
+int pc_bonus2(struct map_session_data *sd,int type,int type2,int val)
+{
+ int i;
+
+ nullpo_retr(0, sd);
+
+ switch(type){
+ case SP_ADDELE:
+ if(!sd->state.lr_flag)
+ sd->right_weapon.addele[type2]+=val;
+ else if(sd->state.lr_flag == 1)
+ sd->left_weapon.addele[type2]+=val;
+ else if(sd->state.lr_flag == 2)
+ sd->arrow_addele[type2]+=val;
+ break;
+ case SP_ADDRACE:
+ if(!sd->state.lr_flag)
+ sd->right_weapon.addrace[type2]+=val;
+ else if(sd->state.lr_flag == 1)
+ sd->left_weapon.addrace[type2]+=val;
+ else if(sd->state.lr_flag == 2)
+ sd->arrow_addrace[type2]+=val;
+ break;
+ case SP_ADDSIZE:
+ if(!sd->state.lr_flag)
+ sd->right_weapon.addsize[type2]+=val;
+ else if(sd->state.lr_flag == 1)
+ sd->left_weapon.addsize[type2]+=val;
+ else if(sd->state.lr_flag == 2)
+ sd->arrow_addsize[type2]+=val;
+ break;
+ case SP_SUBELE:
+ if(sd->state.lr_flag != 2)
+ sd->subele[type2]+=val;
+ break;
+ case SP_SUBRACE:
+ if(sd->state.lr_flag != 2)
+ sd->subrace[type2]+=val;
+ break;
+ case SP_ADDEFF:
+ if (type2 < SC_COMMON_MIN || type2 > SC_COMMON_MAX) {
+ ShowWarning("pc_bonus2 (Add Effect): %d is not supported.\n", type2);
+ break;
+ }
+ if(sd->state.lr_flag != 2)
+ sd->addeff[type2-SC_COMMON_MIN]+=val;
+ else
+ sd->arrow_addeff[type2-SC_COMMON_MIN]+=val;
+ break;
+ case SP_ADDEFF2:
+ if (type2 < SC_COMMON_MIN || type2 > SC_COMMON_MAX) {
+ ShowWarning("pc_bonus2 (Add Effect2): %d is not supported.\n", type2);
+ break;
+ }
+ if(sd->state.lr_flag != 2)
+ sd->addeff2[type2-SC_COMMON_MIN]+=val;
+ else
+ sd->arrow_addeff2[type2-SC_COMMON_MIN]+=val;
+ break;
+ case SP_RESEFF:
+ if (type2 < SC_COMMON_MIN || type2 > SC_COMMON_MAX) {
+ ShowWarning("pc_bonus2 (Resist Effect): %d is not supported.\n", type2);
+ break;
+ }
+ if(sd->state.lr_flag != 2)
+ sd->reseff[type2-SC_COMMON_MIN]+=val;
+ break;
+ case SP_MAGIC_ADDELE:
+ if(sd->state.lr_flag != 2)
+ sd->magic_addele[type2]+=val;
+ break;
+ case SP_MAGIC_ADDRACE:
+ if(sd->state.lr_flag != 2)
+ sd->magic_addrace[type2]+=val;
+ break;
+ case SP_MAGIC_ADDSIZE:
+ if(sd->state.lr_flag != 2)
+ sd->magic_addsize[type2]+=val;
+ break;
+ case SP_ADD_DAMAGE_CLASS:
+ if(!sd->state.lr_flag) {
+ for(i=0;i<sd->right_weapon.add_damage_class_count;i++) {
+ if(sd->right_weapon.add_damage_classid[i] == type2) {
+ sd->right_weapon.add_damage_classrate[i] += val;
+ break;
+ }
+ }
+ if(i >= sd->right_weapon.add_damage_class_count && sd->right_weapon.add_damage_class_count < 10) {
+ sd->right_weapon.add_damage_classid[sd->right_weapon.add_damage_class_count] = type2;
+ sd->right_weapon.add_damage_classrate[sd->right_weapon.add_damage_class_count] += val;
+ sd->right_weapon.add_damage_class_count++;
+ }
+ }
+ else if(sd->state.lr_flag == 1) {
+ for(i=0;i<sd->left_weapon.add_damage_class_count;i++) {
+ if(sd->left_weapon.add_damage_classid[i] == type2) {
+ sd->left_weapon.add_damage_classrate[i] += val;
+ break;
+ }
+ }
+ if(i >= sd->left_weapon.add_damage_class_count && sd->left_weapon.add_damage_class_count < 10) {
+ sd->left_weapon.add_damage_classid[sd->left_weapon.add_damage_class_count] = type2;
+ sd->left_weapon.add_damage_classrate[sd->left_weapon.add_damage_class_count] += val;
+ sd->left_weapon.add_damage_class_count++;
+ }
+ }
+ break;
+ case SP_ADD_MAGIC_DAMAGE_CLASS:
+ if(sd->state.lr_flag != 2) {
+ for(i=0;i<sd->add_mdmg_count;i++) {
+ if(sd->add_mdmg[i].class_ == type2) {
+ sd->add_mdmg[i].rate += val;
+ break;
+ }
+ }
+ if(i >= sd->add_mdmg_count && sd->add_mdmg_count < MAX_PC_BONUS) {
+ sd->add_mdmg[sd->add_mdmg_count].class_ = type2;
+ sd->add_mdmg[sd->add_mdmg_count].rate += val;
+ sd->add_mdmg_count++;
+ }
+ }
+ break;
+ case SP_ADD_DEF_CLASS:
+ if(sd->state.lr_flag != 2) {
+ for(i=0;i<sd->add_def_count;i++) {
+ if(sd->add_def[i].class_ == type2) {
+ sd->add_def[i].rate += val;
+ break;
+ }
+ }
+ if(i >= sd->add_def_count && sd->add_def_count < MAX_PC_BONUS) {
+ sd->add_def[sd->add_def_count].class_ = type2;
+ sd->add_def[sd->add_def_count].rate += val;
+ sd->add_def_count++;
+ }
+ }
+ break;
+ case SP_ADD_MDEF_CLASS:
+ if(sd->state.lr_flag != 2) {
+ for(i=0;i<sd->add_mdef_count;i++) {
+ if(sd->add_mdef[i].class_ == type2) {
+ sd->add_mdef[i].rate += val;
+ break;
+ }
+ }
+ if(i >= sd->add_mdef_count && sd->add_mdef_count < MAX_PC_BONUS) {
+ sd->add_mdef[sd->add_mdef_count].class_ = type2;
+ sd->add_mdef[sd->add_mdef_count].rate += val;
+ sd->add_mdef_count++;
+ }
+ }
+ break;
+ case SP_HP_DRAIN_RATE:
+ if(!sd->state.lr_flag) {
+ sd->right_weapon.hp_drain_rate += type2;
+ sd->right_weapon.hp_drain_per += val;
+ }
+ else if(sd->state.lr_flag == 1) {
+ sd->left_weapon.hp_drain_rate += type2;
+ sd->left_weapon.hp_drain_per += val;
+ }
+ break;
+ case SP_HP_DRAIN_VALUE:
+ if(!sd->state.lr_flag) {
+ sd->right_weapon.hp_drain_value += type2;
+ }
+ else if(sd->state.lr_flag == 1) {
+ sd->left_weapon.hp_drain_value += type2;
+ }
+ sd->sp_drain_type = val;
+ break;
+ case SP_SP_DRAIN_RATE:
+ if(!sd->state.lr_flag) {
+ sd->right_weapon.sp_drain_rate += type2;
+ sd->right_weapon.sp_drain_per += val;
+ }
+ else if(sd->state.lr_flag == 1) {
+ sd->left_weapon.sp_drain_rate += type2;
+ sd->left_weapon.sp_drain_per += val;
+ }
+ sd->sp_drain_type = 0;
+ break;
+ case SP_SP_DRAIN_VALUE:
+ if(!sd->state.lr_flag) {
+ sd->right_weapon.sp_drain_value += type2;
+ }
+ else if(sd->state.lr_flag == 1) {
+ sd->left_weapon.sp_drain_value += type2;
+ }
+ sd->sp_drain_type = val;
+ break;
+ case SP_GET_ZENY_NUM:
+ if(sd->state.lr_flag != 2 && sd->get_zeny_rate < val)
+ {
+ sd->get_zeny_rate = val;
+ sd->get_zeny_num = type2;
+ }
+ break;
+ case SP_ADD_GET_ZENY_NUM:
+ if(sd->state.lr_flag != 2)
+ {
+ sd->get_zeny_rate += val;
+ sd->get_zeny_num += type2;
+ }
+ break;
+ case SP_WEAPON_COMA_ELE:
+ if(sd->state.lr_flag != 2)
+ sd->weapon_coma_ele[type2] += val;
+ break;
+ case SP_WEAPON_COMA_RACE:
+ if(sd->state.lr_flag != 2)
+ sd->weapon_coma_race[type2] += val;
+ break;
+ case SP_RANDOM_ATTACK_INCREASE: // [Valaris]
+ if(sd->state.lr_flag !=2){
+ sd->random_attack_increase_add = type2;
+ sd->random_attack_increase_per += val;
+ }
+ break;
+ case SP_WEAPON_ATK:
+ if(sd->state.lr_flag != 2)
+ sd->weapon_atk[type2]+=val;
+ break;
+ case SP_WEAPON_ATK_RATE:
+ if(sd->state.lr_flag != 2)
+ sd->weapon_atk_rate[type2]+=val;
+ break;
+ case SP_CRITICAL_ADDRACE:
+ if(sd->state.lr_flag != 2)
+ sd->critaddrace[type2] += val*10;
+ break;
+ case SP_ADDEFF_WHENHIT:
+ if (type2 < SC_COMMON_MIN || type2 > SC_COMMON_MAX) {
+ ShowWarning("pc_bonus2 (Add Effect when hit): %d is not supported.\n", type2);
+ break;
+ }
+ if(sd->state.lr_flag != 2) {
+ sd->addeff3[type2-SC_COMMON_MIN]+=val;
+ sd->addeff3_type[type2-SC_COMMON_MIN]=1;
+ }
+ break;
+ case SP_ADDEFF_WHENHIT_SHORT:
+ if (type2 < SC_COMMON_MIN || type2 > SC_COMMON_MAX) {
+ ShowWarning("pc_bonus2 (Add Effect when hit short): %d is not supported.\n", type2);
+ break;
+ }
+ if(sd->state.lr_flag != 2) {
+ sd->addeff3[type2-SC_COMMON_MIN]+=val;
+ sd->addeff3_type[type2-SC_COMMON_MIN]=0;
+ }
+ break;
+ case SP_SKILL_ATK:
+ for (i = 0; i < MAX_PC_BONUS && sd->skillatk[i].id != 0 && sd->skillatk[i].id != type2; i++);
+ if (i == MAX_PC_BONUS)
+ { //Better mention this so the array length can be updated. [Skotlex]
+ ShowDebug("run_script: bonus2 bSkillAtk reached it's limit (%d skills per character), bonus skill %d (+%d%%) lost.\n", MAX_PC_BONUS, type2, val);
+ break;
+ }
+ if(sd->state.lr_flag != 2) {
+ if (sd->skillatk[i].id == type2)
+ sd->skillatk[i].val += val;
+ else {
+ sd->skillatk[i].id = type2;
+ sd->skillatk[i].val = val;
+ }
+ }
+ break;
+ case SP_ADD_SKILL_BLOW:
+ for (i = 0; i < MAX_PC_BONUS && sd->skillblown[i].id != 0 && sd->skillblown[i].id != type2; i++);
+ if (i == MAX_PC_BONUS)
+ { //Better mention this so the array length can be updated. [Skotlex]
+ ShowDebug("run_script: bonus2 bSkillBlown reached it's limit (%d skills per character), bonus skill %d (+%d%%) lost.\n", MAX_PC_BONUS, type2, val);
+ break;
+ }
+ if(sd->state.lr_flag != 2) {
+ if (sd->skillblown[i].id == type2)
+ sd->skillblown[i].val += val;
+ else {
+ sd->skillblown[i].id = type2;
+ sd->skillblown[i].val = val;
+ }
+ }
+ break;
+ case SP_ADD_DAMAGE_BY_CLASS:
+ if(sd->state.lr_flag != 2) {
+ for(i=0;i<sd->add_dmg_count;i++) {
+ if(sd->add_dmg[i].class_ == type2) {
+ sd->add_dmg[i].rate += val;
+ break;
+ }
+ }
+ if(i >= sd->add_dmg_count && sd->add_dmg_count < MAX_PC_BONUS) {
+ sd->add_dmg[sd->add_dmg_count].class_ = type2;
+ sd->add_dmg[sd->add_dmg_count].rate += val;
+ sd->add_dmg_count++;
+ }
+ }
+ break;
+ case SP_HP_LOSS_RATE:
+ if(sd->state.lr_flag != 2) {
+ sd->hp_loss_value = type2;
+ sd->hp_loss_rate = val;
+ }
+ break;
+ case SP_ADDRACE2:
+ if (!(type2 > 0 && type2 < MAX_MOB_RACE_DB))
+ break;
+ if(sd->state.lr_flag != 2)
+ sd->right_weapon.addrace2[type2] += val;
+ else
+ sd->left_weapon.addrace2[type2] += val;
+ break;
+ case SP_SUBSIZE:
+ if(sd->state.lr_flag != 2)
+ sd->subsize[type2]+=val;
+ break;
+ case SP_SUBRACE2:
+ if(sd->state.lr_flag != 2)
+ sd->subrace2[type2]+=val;
+ break;
+ case SP_ADD_ITEM_HEAL_RATE:
+ if(sd->state.lr_flag != 2)
+ sd->itemhealrate[type2 - 1] += val;
+ break;
+ case SP_EXP_ADDRACE:
+ if(sd->state.lr_flag != 2)
+ sd->expaddrace[type2]+=val;
+ break;
+ case SP_SP_GAIN_RACE:
+ if(sd->state.lr_flag != 2)
+ sd->sp_gain_race[type2]+=val;
+ break;
+ case SP_ADD_MONSTER_DROP_ITEM:
+ if (sd->state.lr_flag != 2) {
+ for(i = 0; i < sd->add_drop_count; i++) {
+ if(sd->add_drop[i].id == type2) {
+ sd->add_drop[i].race |= (1<<10)|(1<<11);
+ if(sd->add_drop[i].rate < val)
+ sd->add_drop[i].rate = val;
+ break;
+ }
+ }
+ if(i >= sd->add_drop_count && sd->add_drop_count < MAX_PC_BONUS) {
+ sd->add_drop[sd->add_drop_count].id = type2;
+ // all monsters, including boss and non boss monsters
+ sd->add_drop[sd->add_drop_count].race |= (1<<10)|(1<<11);
+ sd->add_drop[sd->add_drop_count].rate = val;
+ sd->add_drop_count++;
+ }
+ }
+ break;
+ case SP_ADD_MONSTER_DROP_ITEMGROUP:
+ if (sd->state.lr_flag != 2) {
+ for(i = 0; i < sd->add_drop_count; i++) {
+ if(sd->add_drop[i].group == type2) {
+ sd->add_drop[i].id = 0;
+ sd->add_drop[i].race |= (1<<10)|(1<<11);
+ if(sd->add_drop[i].rate < val)
+ sd->add_drop[i].rate = val;
+ break;
+ }
+ }
+ if(i >= sd->add_drop_count && sd->add_drop_count < MAX_PC_BONUS) {
+ sd->add_drop[sd->add_drop_count].group = type2;
+ sd->add_drop[sd->add_drop_count].id = 0;
+ // all monsters, including boss and non boss monsters
+ sd->add_drop[sd->add_drop_count].race |= (1<<10)|(1<<11);
+ sd->add_drop[sd->add_drop_count].rate = val;
+ sd->add_drop_count++;
+ }
+ }
+ break;
+ case SP_SP_LOSS_RATE:
+ if(sd->state.lr_flag != 2) {
+ sd->sp_loss_value = type2;
+ sd->sp_loss_rate = val;
+ }
+ break;
+
+ default:
+ if(battle_config.error_log)
+ ShowWarning("pc_bonus2: unknown type %d %d %d!\n",type,type2,val);
+ break;
+ }
+ return 0;
+}
+
+int pc_bonus3(struct map_session_data *sd,int type,int type2,int type3,int val)
+{
+ int i;
+ nullpo_retr(0, sd);
+
+ switch(type){
+ case SP_ADD_MONSTER_DROP_ITEM:
+ if(sd->state.lr_flag != 2) {
+ for(i=0;i<sd->add_drop_count;i++) {
+ if(sd->add_drop[i].id == type2) {
+ sd->add_drop[i].race |= 1<<type3;
+ if(sd->add_drop[i].rate < val)
+ sd->add_drop[i].rate = val;
+ break;
+ }
+ }
+ if(i >= sd->add_drop_count && sd->add_drop_count < MAX_PC_BONUS) {
+ sd->add_drop[sd->add_drop_count].id = type2;
+ sd->add_drop[sd->add_drop_count].race |= 1<<type3;
+ sd->add_drop[sd->add_drop_count].rate = val;
+ sd->add_drop_count++;
+ }
+ }
+ break;
+ case SP_AUTOSPELL:
+ if(sd->state.lr_flag != 2) {
+ for (i = 0; i < MAX_PC_BONUS; i++) {
+ if (sd->autospell[i].id == 0 ||
+ (sd->autospell[i].id == type2 && sd->autospell[i].lv < type3) ||
+ (sd->autospell[i].id == type2 && sd->autospell[i].lv == type3 && sd->autospell[i].rate < val))
+ {
+ sd->autospell[i].id = type2;
+ sd->autospell[i].lv = type3;
+ sd->autospell[i].rate = val;
+ break;
+ }
+ }
+ }
+ break;
+ case SP_AUTOSPELL_WHENHIT:
+ if(sd->state.lr_flag != 2) {
+ for (i = 0; i < MAX_PC_BONUS; i++) {
+ if (sd->autospell2[i].id == 0 ||
+ (sd->autospell2[i].id == type2 && sd->autospell2[i].lv < type3) ||
+ (sd->autospell2[i].id == type2 && sd->autospell2[i].lv == type3 && sd->autospell2[i].rate < val))
+ {
+ sd->autospell2[i].id = type2;
+ sd->autospell2[i].lv = type3;
+ sd->autospell2[i].rate = val;
+ break;
+ }
+ }
+ }
+ break;
+ case SP_HP_LOSS_RATE:
+ if(sd->state.lr_flag != 2) {
+ sd->hp_loss_value = type2;
+ sd->hp_loss_rate = type3;
+ sd->hp_loss_type = val;
+ }
+ break;
+ case SP_SP_DRAIN_RATE:
+ if(!sd->state.lr_flag) {
+ sd->right_weapon.sp_drain_rate += type2;
+ sd->right_weapon.sp_drain_per += type3;
+ }
+ else if(sd->state.lr_flag == 1) {
+ sd->left_weapon.sp_drain_rate += type2;
+ sd->left_weapon.sp_drain_per += type3;
+ }
+ sd->sp_drain_type = val;
+ break;
+ case SP_ADD_MONSTER_DROP_ITEMGROUP:
+ if (sd->state.lr_flag != 2) {
+ for(i = 0; i < sd->add_drop_count; i++) {
+ if(sd->add_drop[i].group == type2) {
+ sd->add_drop[i].id = 0;
+ sd->add_drop[i].race |= 1<<type3;
+ if(sd->add_drop[i].rate < val)
+ sd->add_drop[i].rate = val;
+ break;
+ }
+ }
+ if(i >= sd->add_drop_count && sd->add_drop_count < 10) {
+ sd->add_drop[sd->add_drop_count].group = type2;
+ sd->add_drop[sd->add_drop_count].id = 0;
+ // all monsters, including boss and non boss monsters
+ sd->add_drop[sd->add_drop_count].race |= 1<<type3;
+ sd->add_drop[sd->add_drop_count].rate = val;
+ sd->add_drop_count++;
+ }
+ }
+ break;
+
+ default:
+ if(battle_config.error_log)
+ ShowWarning("pc_bonus3: unknown type %d %d %d %d!\n",type,type2,type3,val);
+ break;
+ }
+
+ return 0;
+}
+
+int pc_bonus4(struct map_session_data *sd,int type,int type2,int type3,int type4,int val)
+{
+ int i;
+ nullpo_retr(0, sd);
+
+ switch(type){
+ case SP_AUTOSPELL:
+ if(sd->state.lr_flag != 2) {
+ for (i = 0; i < MAX_PC_BONUS; i++) {
+ if (sd->autospell[i].id == 0 ||
+ (sd->autospell[i].id == type2 && sd->autospell[i].lv < type3) ||
+ (sd->autospell[i].id == type2 && sd->autospell[i].lv == type3 && sd->autospell[i].rate < type4))
+ {
+ sd->autospell[i].id = (val) ? type2 : -type2; // val = 0: self, 1: enemy
+ sd->autospell[i].lv = type3;
+ sd->autospell[i].rate = type4;
+ break;
+ }
+ }
+ }
+ break;
+ case SP_AUTOSPELL_WHENHIT:
+ if(sd->state.lr_flag != 2) {
+ for (i = 0; i < MAX_PC_BONUS; i++) {
+ if (sd->autospell2[i].id == 0 ||
+ (sd->autospell2[i].id == type2 && sd->autospell2[i].lv < type3) ||
+ (sd->autospell2[i].id == type2 && sd->autospell2[i].lv == type3 && sd->autospell2[i].rate < type4))
+ {
+ sd->autospell2[i].id = (val) ? type2 : -type2; // val = 0: self, 1: enemy
+ sd->autospell2[i].lv = type3;
+ sd->autospell2[i].rate = type4;
+ break;
+ }
+ }
+ }
+ break;
+ default:
+ if(battle_config.error_log)
+ ShowWarning("pc_bonus4: unknown type %d %d %d %d %d!\n",type,type2,type3,type4,val);
+ break;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * スクリプトによるスキル所得
+ *------------------------------------------
+ */
+int pc_skill(struct map_session_data *sd,int id,int level,int flag)
+{
+ nullpo_retr(0, sd);
+
+ if(level>MAX_SKILL_LEVEL){
+ if(battle_config.error_log)
+ ShowError("support card skill only!\n");
+ return 0;
+ }
+ if(!flag && (sd->status.skill[id].id == id || level == 0)){ // クエスト所得ならここで?件を確認して送信する
+ sd->status.skill[id].lv=level;
+ status_calc_pc(sd,0);
+ clif_skillinfoblock(sd);
+ }
+ else if(flag==2 && (sd->status.skill[id].id == id || level == 0)){ // クエスト所得ならここで?件を確認して送信する
+ sd->status.skill[id].lv+=level;
+ status_calc_pc(sd,0);
+ clif_skillinfoblock(sd);
+ }
+ else if(sd->status.skill[id].lv < level){ // ?えられるがlvが小さいなら
+ if(sd->status.skill[id].id==id)
+ sd->status.skill[id].flag=sd->status.skill[id].lv+2; // lvを記憶
+ else {
+ sd->status.skill[id].id=id;
+ sd->status.skill[id].flag=1; // cardスキルとする
+ }
+ sd->status.skill[id].lv=level;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int pc_blockskill_end(int tid,unsigned int tick,int id,int data)
+{
+ struct map_session_data *sd = map_id2sd(id);
+ if (data <= 0 || data >= MAX_SKILL)
+ return 0;
+ if (sd) sd->blockskill[data] = 0;
+
+ return 1;
+}
+int pc_blockskill_start (struct map_session_data *sd, int skillid, int tick)
+{
+ nullpo_retr (-1, sd);
+
+ if (skillid >= 10000 && skillid < 10015)
+ skillid -= 9500;
+ else if (skillid < 1 || skillid > MAX_SKILL)
+ return -1;
+
+ sd->blockskill[skillid] = 1;
+ return add_timer(gettick()+tick,pc_blockskill_end,sd->bl.id,skillid);
+}
+
+/*==========================================
+ * カ?ド?入
+ *------------------------------------------
+ */
+int pc_insert_card(struct map_session_data *sd,int idx_card,int idx_equip)
+{
+ int i, ep;
+ int nameid, cardid;
+
+ nullpo_retr(0, sd);
+
+ if(idx_card < 0 || idx_card >= MAX_INVENTORY || !sd->inventory_data[idx_card])
+ return 0; //Invalid card index.
+
+ if(idx_equip < 0 || idx_equip >= MAX_INVENTORY || !sd->inventory_data[idx_equip])
+ return 0; //Invalid item index.
+
+ nameid=sd->status.inventory[idx_equip].nameid;
+ cardid=sd->status.inventory[idx_card].nameid;
+ ep=sd->inventory_data[idx_card]->equip;
+
+ if( nameid <= 0 || cardid <= 0 ||
+ (sd->inventory_data[idx_equip]->type!=4 && sd->inventory_data[idx_equip]->type!=5)|| // ? 備じゃない
+ sd->inventory_data[idx_card]->type!=6 || // Prevent Hack [Ancyker]
+ sd->status.inventory[idx_equip].identify==0 || // 未鑑定
+ sd->status.inventory[idx_equip].card[0]==0x00ff || // 製造武器
+ sd->status.inventory[idx_equip].card[0]==0x00fe ||
+ sd->status.inventory[idx_equip].card[0]==(short)0xff00 ||
+ !(sd->inventory_data[idx_equip]->equip&ep) || // ? 備個所違い
+ (sd->inventory_data[idx_equip]->type==4 && ep==32) || // ? 手武器と盾カ?ド
+ sd->status.inventory[idx_equip].equip){
+
+ clif_insert_card(sd,idx_equip,idx_card,1);
+ return 0;
+ }
+ for(i=0;i<sd->inventory_data[idx_equip]->slot;i++){
+ if( sd->status.inventory[idx_equip].card[i] == 0){
+ // 空きスロットがあったので差し?む
+ sd->status.inventory[idx_equip].card[i]=cardid;
+
+ // カ?ドは減らす
+ clif_insert_card(sd,idx_equip,idx_card,0);
+ pc_delitem(sd,idx_card,1,1);
+ return 0;
+ }
+ }
+ clif_insert_card(sd,idx_equip,idx_card,1);
+ return 0;
+}
+
+//
+// アイテム物
+//
+
+/*==========================================
+ * スキルによる買い値修正
+ *------------------------------------------
+ */
+int pc_modifybuyvalue(struct map_session_data *sd,int orig_value)
+{
+ int skill,val = orig_value,rate1 = 0,rate2 = 0;
+ if((skill=pc_checkskill(sd,MC_DISCOUNT))>0) // ディスカウント
+ rate1 = 5+skill*2-((skill==10)? 1:0);
+ if((skill=pc_checkskill(sd,RG_COMPULSION))>0) // コムパルションディスカウント
+ rate2 = 5+skill*4;
+ if(rate1 < rate2) rate1 = rate2;
+ if(rate1)
+ val = (int)((double)orig_value*(double)(100-rate1)/100.);
+ if(val < 0) val = 0;
+ if(orig_value > 0 && val < 1) val = 1;
+
+ return val;
+}
+
+/*==========================================
+ * スキルによる?り値修正
+ *------------------------------------------
+ */
+int pc_modifysellvalue(struct map_session_data *sd,int orig_value)
+{
+ int skill,val = orig_value,rate = 0;
+ if((skill=pc_checkskill(sd,MC_OVERCHARGE))>0) // オ?バ?チャ?ジ
+ rate = 5+skill*2-((skill==10)? 1:0);
+ if(rate)
+ val = (int)((double)orig_value*(double)(100+rate)/100.);
+ if(val < 0) val = 0;
+ if(orig_value > 0 && val < 1) val = 1;
+
+ return val;
+}
+
+/*==========================================
+ * アイテムを買った暫ノ、新しいアイテム欄を使うか、
+ * 3万個制限にかかるか確認
+ *------------------------------------------
+ */
+int pc_checkadditem(struct map_session_data *sd,int nameid,int amount)
+{
+ int i;
+
+ nullpo_retr(0, sd);
+
+ if(itemdb_isequip(nameid))
+ return ADDITEM_NEW;
+
+ for(i=0;i<MAX_INVENTORY;i++){
+ if(sd->status.inventory[i].nameid==nameid){
+ if(sd->status.inventory[i].amount+amount > MAX_AMOUNT)
+ return ADDITEM_OVERAMOUNT;
+ return ADDITEM_EXIST;
+ }
+ }
+
+ if(amount > MAX_AMOUNT)
+ return ADDITEM_OVERAMOUNT;
+ return ADDITEM_NEW;
+}
+
+/*==========================================
+ * 空きアイテム欄の個?
+ *------------------------------------------
+ */
+int pc_inventoryblank(struct map_session_data *sd)
+{
+ int i,b;
+
+ nullpo_retr(0, sd);
+
+ for(i=0,b=0;i<MAX_INVENTORY;i++){
+ if(sd->status.inventory[i].nameid==0)
+ b++;
+ }
+
+ return b;
+}
+
+/*==========================================
+ * お金を?う
+ *------------------------------------------
+ */
+int pc_payzeny(struct map_session_data *sd,int zeny)
+{
+ double z;
+
+ nullpo_retr(0, sd);
+
+ z = (double)sd->status.zeny;
+ if(sd->status.zeny<zeny || z - (double)zeny > MAX_ZENY)
+ return 1;
+ sd->status.zeny-=zeny;
+ clif_updatestatus(sd,SP_ZENY);
+
+ return 0;
+}
+
+/*==========================================
+ * お金を得る
+ *------------------------------------------
+ */
+int pc_getzeny(struct map_session_data *sd,int zeny)
+{
+ double z;
+
+ nullpo_retr(0, sd);
+
+ z = (double)sd->status.zeny;
+ if(z + (double)zeny > MAX_ZENY) {
+ zeny = 0;
+ sd->status.zeny = MAX_ZENY;
+ }
+ sd->status.zeny+=zeny;
+ clif_updatestatus(sd,SP_ZENY);
+ if(zeny > 0 && sd->state.showzeny){
+ char output[255];
+ sprintf(output, "Gained %dz.", zeny);
+ clif_disp_onlyself(sd,output,strlen(output));
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * アイテムを探して、インデックスを返す
+ *------------------------------------------
+ */
+int pc_search_inventory(struct map_session_data *sd,int item_id)
+{
+ int i;
+
+ nullpo_retr(-1, sd);
+
+ for(i=0;i<MAX_INVENTORY;i++) {
+ if(sd->status.inventory[i].nameid == item_id &&
+ (sd->status.inventory[i].amount > 0 || item_id == 0))
+ return i;
+ }
+
+ return -1;
+}
+
+/*==========================================
+ * アイテム追加。個?のみitem構造?の?字を無視
+ *------------------------------------------
+ */
+int pc_additem(struct map_session_data *sd,struct item *item_data,int amount)
+{
+ struct item_data *data;
+ int i;
+ long w;
+
+ nullpo_retr(1, sd);
+ nullpo_retr(1, item_data);
+
+ if(item_data->nameid <= 0 || amount <= 0)
+ return 1;
+ data = itemdb_search(item_data->nameid);
+ w = data->weight*amount;
+ if(w + (long)sd->weight > (long)sd->max_weight || w + (long)sd->weight < 0) //Weight overflow check?
+ return 2;
+
+ i = MAX_INVENTORY;
+
+ if (!itemdb_isequip2(data)){
+ // 装 備品ではないので、既所有品なら個数のみ変化させる
+ for (i = 0; i < MAX_INVENTORY; i++)
+ if(sd->status.inventory[i].nameid == item_data->nameid &&
+ sd->status.inventory[i].card[0] == item_data->card[0] &&
+ sd->status.inventory[i].card[1] == item_data->card[1] &&
+ sd->status.inventory[i].card[2] == item_data->card[2] &&
+ sd->status.inventory[i].card[3] == item_data->card[3])
+ {
+ if (amount < 0 || amount > MAX_AMOUNT || sd->status.inventory[i].amount + amount > MAX_AMOUNT)
+ return 5;
+ sd->status.inventory[i].amount += amount;
+ clif_additem(sd,i,amount,0);
+ break;
+ }
+ }
+ if (i >= MAX_INVENTORY){
+ // 装 備品か未所有品だったので空き欄へ追加
+ i = pc_search_inventory(sd,0);
+ if(i >= 0) {
+ // clear equips field first, just in case
+ if (item_data->equip != 0)
+ item_data->equip = 0;
+ memcpy(&sd->status.inventory[i], item_data, sizeof(sd->status.inventory[0]));
+ sd->status.inventory[i].amount = amount;
+ sd->inventory_data[i] = data;
+ clif_additem(sd,i,amount,0);
+ }
+ else return 4;
+ }
+ sd->weight += (int)w;
+ clif_updatestatus(sd,SP_WEIGHT);
+
+ return 0;
+}
+
+/*==========================================
+ * アイテムを減らす
+ *------------------------------------------
+ */
+int pc_delitem(struct map_session_data *sd,int n,int amount,int type)
+{
+ nullpo_retr(1, sd);
+
+ if(sd->status.inventory[n].nameid==0 || amount <= 0 || sd->status.inventory[n].amount<amount || sd->inventory_data[n] == NULL)
+ return 1;
+
+ sd->status.inventory[n].amount -= amount;
+ sd->weight -= sd->inventory_data[n]->weight*amount ;
+ if(sd->status.inventory[n].amount<=0){
+ if(sd->status.inventory[n].equip)
+ pc_unequipitem(sd,n,3);
+ memset(&sd->status.inventory[n],0,sizeof(sd->status.inventory[0]));
+ sd->inventory_data[n] = NULL;
+ }
+ if(!(type&1))
+ clif_delitem(sd,n,amount);
+ if(!(type&2))
+ clif_updatestatus(sd,SP_WEIGHT);
+
+ return 0;
+}
+
+/*==========================================
+ * アイテムを落す
+ *------------------------------------------
+ */
+int pc_dropitem(struct map_session_data *sd,int n,int amount)
+{
+ nullpo_retr(1, sd);
+
+ if(n < 0 || n >= MAX_INVENTORY)
+ return 1;
+
+ if(amount <= 0)
+ return 1;
+
+ if (sd->status.inventory[n].nameid <= 0 ||
+ sd->status.inventory[n].amount < amount ||
+ sd->trade_partner != 0 || sd->vender_id != 0 ||
+ sd->status.inventory[n].amount <= 0)
+ return 1;
+
+ if (!pc_candrop(sd,sd->status.inventory[n].nameid))
+ { //The client does not likes being silently ignored, so we send it a del of 0 qty
+ clif_delitem(sd,n,0);
+ clif_displaymessage (sd->fd, msg_txt(263));
+ return 1;
+ }
+
+ //Logs items, dropped by (P)layers [Lupus]
+ if(log_config.pick > 0 )
+ log_pick(sd, "P", 0, sd->status.inventory[n].nameid, -amount, (struct item*)&sd->status.inventory[n]);
+ //Logs
+
+ if (map_addflooritem(&sd->status.inventory[n], amount, sd->bl.m, sd->bl.x, sd->bl.y, NULL, NULL, NULL, 0) != 0)
+ pc_delitem(sd, n, amount, 0);
+ else
+ clif_delitem(sd,n,0);
+
+ return 0;
+}
+
+/*==========================================
+ * アイテムを拾う
+ *------------------------------------------
+ */
+int pc_takeitem(struct map_session_data *sd,struct flooritem_data *fitem)
+{
+ int flag=0;
+ unsigned int tick = gettick();
+ struct map_session_data *first_sd = NULL,*second_sd = NULL,*third_sd = NULL;
+ struct party *p=NULL;
+
+ nullpo_retr(0, sd);
+ nullpo_retr(0, fitem);
+
+ if(!check_distance_bl(&fitem->bl, &sd->bl, 2) && sd->skillid!=BS_GREED)
+ return 0; // 距離が遠い
+
+ if (sd->status.party_id)
+ p = party_search(sd->status.party_id);
+
+ if(fitem->first_get_id > 0 && fitem->first_get_id != sd->bl.id) {
+ first_sd = map_id2sd(fitem->first_get_id);
+ if(DIFF_TICK(tick,fitem->first_get_tick) < 0) {
+ if (!(p && p->item&1 &&
+ first_sd && first_sd->status.party_id == sd->status.party_id
+ )) {
+ clif_additem(sd,0,0,6);
+ return 0;
+ }
+ }
+ else if(fitem->second_get_id > 0 && fitem->second_get_id != sd->bl.id) {
+ second_sd = map_id2sd(fitem->second_get_id);
+ if(DIFF_TICK(tick, fitem->second_get_tick) < 0) {
+ if(!(p && p->item&1 &&
+ ((first_sd && first_sd->status.party_id == sd->status.party_id) ||
+ (second_sd && second_sd->status.party_id == sd->status.party_id))
+ )) {
+ clif_additem(sd,0,0,6);
+ return 0;
+ }
+ }
+ else if(fitem->third_get_id > 0 && fitem->third_get_id != sd->bl.id) {
+ third_sd = map_id2sd(fitem->third_get_id);
+ if(DIFF_TICK(tick,fitem->third_get_tick) < 0) {
+ if(!(p && p->item&1 &&
+ ((first_sd && first_sd->status.party_id == sd->status.party_id) ||
+ (second_sd && second_sd->status.party_id == sd->status.party_id) ||
+ (third_sd && third_sd->status.party_id == sd->status.party_id))
+ )) {
+ clif_additem(sd,0,0,6);
+ return 0;
+ }
+ }
+ }
+ }
+ }
+ first_sd = NULL; //First_sd will store who picked up the item.
+ if (p && p->item&2) { //item distribution to party members.
+ first_sd = NULL;
+ if (battle_config.party_share_type) { //Round Robin
+ int i;
+ i = p->itemc;
+ do {
+ i++;
+ if (i >= MAX_PARTY)
+ i = 0; // reset counter to 1st person in party so it'll stop when it reaches "itemc"
+ if ((second_sd=p->member[i].sd)==NULL || sd->bl.m != second_sd->bl.m)
+ continue;
+
+ if (pc_additem(second_sd,&fitem->item_data,fitem->item_data.amount))
+ continue; //Chosen char can't pick up loot.
+ //Successful pick.
+ first_sd = second_sd;
+ break;
+ } while (i != p->itemc);
+ // Skip to the current receiver of an item, so the next pick should not go to him again.
+ p->itemc = i;
+ } else { //Random pick
+ struct map_session_data*psd[MAX_PARTY];
+ int i, count=0;
+ //Collect pick candidates
+ for (i = 0; i < MAX_PARTY; i++) {
+ if ((psd[count]=p->member[i].sd) && psd[count]->bl.m == sd->bl.m)
+ count++;
+ }
+ if (count > 0) { //Pick a random member.
+ do {
+ i = rand()%count;
+ if (pc_additem(psd[i],&fitem->item_data,fitem->item_data.amount))
+ { //Discard this receiver.
+ psd[i] = psd[count-1];
+ count--;
+ } else { //Successful pick.
+ first_sd = psd[i];
+ break;
+ }
+ } while (count > 0);
+ }
+ }
+ }
+ if (!first_sd) { //Noone has picked it up yet...
+ if ((flag = pc_additem(sd,&fitem->item_data,fitem->item_data.amount))) {
+ clif_additem(sd,0,0,flag);
+ return 1;
+ }
+ first_sd = sd;
+ }
+ if(log_config.pick) //Logs items, taken by (P)layers [Lupus]
+ log_pick(first_sd, "P", 0, fitem->item_data.nameid, fitem->item_data.amount, (struct item*)&fitem->item_data);
+ //Logs
+ if(battle_config.party_show_share_picker && first_sd != sd){
+ char output[80];
+ sprintf(output, "%s acquired the item.",first_sd->status.name);
+ clif_disp_onlyself(sd,output,strlen(output));
+ }
+
+ //Display pickup animation.
+ if(sd->attacktimer != -1)
+ pc_stopattack(sd);
+
+ clif_takeitem(&sd->bl,&fitem->bl);
+ map_clearflooritem(fitem->bl.id);
+ return 0;
+}
+
+int pc_isUseitem(struct map_session_data *sd,int n)
+{
+ struct item_data *item;
+ int nameid;
+
+ nullpo_retr(0, sd);
+
+ item = sd->inventory_data[n];
+ nameid = sd->status.inventory[n].nameid;
+
+ if(item == NULL)
+ return 0;
+ //Not consumable item
+ if(item->type != 0 && item->type != 2)
+ return 0;
+ //Anodyne (can't use Anodyne's Endure at GVG)
+ if((nameid == 605) && map_flag_gvg(sd->bl.m))
+ return 0;
+ //Fly Wing (can't use at GVG and when noteleport flag is on)
+ if(nameid == 601 && (map[sd->bl.m].flag.noteleport || map_flag_gvg(sd->bl.m))) {
+ clif_skill_teleportmessage(sd,0);
+ return 0;
+ }
+ //Fly Wing (can't use when you in duel) [LuzZza]
+ if(nameid == 601 && (!battle_config.duel_allow_teleport && sd->duel_group)) {
+ clif_displaymessage(sd->fd, "Duel: Can't use this item in duel.");
+ return 0;
+ }
+ //Butterfly Wing (can't use noreturn flag is on and if duel)
+ if(nameid == 602 && map[sd->bl.m].flag.noreturn)
+ return 0;
+ //BW (can't use when you in duel) [LuzZza]
+ if(nameid == 602 && (!battle_config.duel_allow_teleport && sd->duel_group)) {
+ clif_displaymessage(sd->fd, "Duel: Can't use this item in duel.");
+ return 0;
+ }
+ //Dead Branch & Bloody Branch & Porings Box (can't use at GVG and when nobranch flag is on)
+ if((nameid == 604 || nameid == 12103 || nameid == 12109) && (map[sd->bl.m].flag.nobranch || map_flag_gvg(sd->bl.m)))
+ return 0;
+ //Gender check
+ if(item->sex != 2 && sd->status.sex != item->sex)
+ return 0;
+ //Required level check
+ if(item->elv && sd->status.base_level < (unsigned int)item->elv)
+ return 0;
+
+ //Not equipable by class. [Skotlex]
+ if (!(1<<(sd->class_&MAPID_BASEMASK)&item->class_base[(sd->class_&JOBL_2_1)?1:((sd->class_&JOBL_2_2)?2:0)]))
+ return 0;
+
+ //Not usable by upper class. [Skotlex]
+ if(!(1<<((sd->class_&JOBL_UPPER)?1:((sd->class_&JOBL_BABY)?2:0))&item->class_upper))
+ return 0;
+
+ //Dead Branch & Bloody Branch & Porings Box
+ if((log_config.branch > 0) && (nameid == 604 || nameid == 12103 || nameid == 12109))
+ log_branch(sd);
+
+ return 1;
+}
+
+/*==========================================
+ * アイテムを使う
+ *------------------------------------------
+ */
+int pc_useitem(struct map_session_data *sd,int n)
+{
+ int amount;
+
+ nullpo_retr(1, sd);
+
+ if(n >=0 && n < MAX_INVENTORY) {
+ unsigned char *script;
+ sd->itemid = sd->status.inventory[n].nameid;
+ sd->itemindex = n;
+ amount = sd->status.inventory[n].amount;
+ if(sd->status.inventory[n].nameid <= 0 ||
+ sd->status.inventory[n].amount <= 0 ||
+ gettick() < sd->canuseitem_tick || //Prevent mass item usage. [Skotlex]
+ sd->sc_data[SC_BERSERK].timer!=-1 ||
+ sd->sc_data[SC_MARIONETTE].timer!=-1 ||
+ sd->sc_data[SC_GRAVITATION].timer!=-1 ||
+ //Cannot use Potions/Healing items while under Gospel.
+ (sd->sc_data[SC_GOSPEL].timer!=-1 && sd->sc_data[SC_GOSPEL].val4 != BCT_SELF && sd->inventory_data[n]->type == 0) ||
+ (pc_issit(sd) && (sd->itemid == 605 || sd->itemid == 606)) ||
+ //added item_noequip.txt items check by Maya&[Lupus]
+ (map[sd->bl.m].flag.pvp && (sd->inventory_data[n]->flag.no_equip&1) ) || // PVP
+ (map_flag_gvg(sd->bl.m) && (sd->inventory_data[n]->flag.no_equip>1) ) || // GVG
+ !pc_isUseitem(sd,n) ) {
+ clif_useitemack(sd,n,0,0);
+ return 1;
+ }
+ script = sd->inventory_data[n]->script;
+ amount = sd->status.inventory[n].amount;
+ //Check if the item is to be consumed inmediately [Skotlex]
+ if (sd->inventory_data[n]->flag.delay_consume)
+ clif_useitemack(sd,n,amount,1);
+ else {
+ clif_useitemack(sd,n,amount-1,1);
+
+ //Logs (C)onsumable items [Lupus]
+ if(log_config.pick > 0 ) {
+ log_pick(sd, "C", 0, sd->status.inventory[n].nameid, -1, &sd->status.inventory[n]);
+ }
+ //Logs
+
+ pc_delitem(sd,n,1,1);
+ }
+ if(sd->status.inventory[n].card[0]==0x00fe && pc_istop10fame(MakeDWord(sd->status.inventory[n].card[2],sd->status.inventory[n].card[3]), MAPID_ALCHEMIST)) {
+ potion_flag = 2; // Famous player's potions have 50% more efficiency
+ if (sd->sc_data[SC_SPIRIT].timer != -1 && sd->sc_data[SC_SPIRIT].val2 == SL_ROGUE)
+ potion_flag = 3; //Even more effective potions.
+ }
+ sd->canuseitem_tick= gettick() + battle_config.item_use_interval; //Update item use time.
+ run_script(script,0,sd->bl.id,0);
+ potion_flag = 0;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * カ?トアイテム追加。個?のみitem構造?の?字を無視
+ *------------------------------------------
+ */
+int pc_cart_additem(struct map_session_data *sd,struct item *item_data,int amount)
+{
+ struct item_data *data;
+ int i,w;
+
+ nullpo_retr(1, sd);
+ nullpo_retr(1, item_data);
+
+ if(item_data->nameid <= 0 || amount <= 0)
+ return 1;
+ data = itemdb_search(item_data->nameid);
+
+ if(!itemdb_cancartstore(item_data->nameid, pc_isGM(sd)))
+ { //Check item trade restrictions [Skotlex]
+ clif_displaymessage (sd->fd, msg_txt(264));
+ return 1;
+ }
+
+ if((w=data->weight*amount) + sd->cart_weight > sd->cart_max_weight)
+ return 1;
+
+ i=MAX_CART;
+ if(!itemdb_isequip2(data)){
+ // 装 備品ではないので、既所有品なら個数のみ変化させる
+ for(i=0;i<MAX_CART;i++){
+ if(sd->status.cart[i].nameid==item_data->nameid &&
+ sd->status.cart[i].card[0] == item_data->card[0] && sd->status.cart[i].card[1] == item_data->card[1] &&
+ sd->status.cart[i].card[2] == item_data->card[2] && sd->status.cart[i].card[3] == item_data->card[3]){
+ if(sd->status.cart[i].amount+amount > MAX_AMOUNT)
+ return 1;
+ sd->status.cart[i].amount+=amount;
+ clif_cart_additem(sd,i,amount,0);
+ break;
+ }
+ }
+ }
+ if(i >= MAX_CART){
+ // 装 備品か未所有品だったので空き欄へ追加
+ for(i=0;i<MAX_CART;i++){
+ if(sd->status.cart[i].nameid==0){
+ memcpy(&sd->status.cart[i],item_data,sizeof(sd->status.cart[0]));
+ sd->status.cart[i].amount=amount;
+ sd->cart_num++;
+ clif_cart_additem(sd,i,amount,0);
+ break;
+ }
+ }
+ if(i >= MAX_CART)
+ return 1;
+ }
+ sd->cart_weight += w;
+ clif_updatestatus(sd,SP_CARTINFO);
+
+ return 0;
+}
+
+/*==========================================
+ * カ?トアイテムを減らす
+ *------------------------------------------
+ */
+int pc_cart_delitem(struct map_session_data *sd,int n,int amount,int type)
+{
+ nullpo_retr(1, sd);
+
+ if(sd->status.cart[n].nameid==0 ||
+ sd->status.cart[n].amount<amount)
+ return 1;
+
+ sd->status.cart[n].amount -= amount;
+ sd->cart_weight -= itemdb_weight(sd->status.cart[n].nameid)*amount ;
+ if(sd->status.cart[n].amount <= 0){
+ memset(&sd->status.cart[n],0,sizeof(sd->status.cart[0]));
+ sd->cart_num--;
+ }
+ if(!type) {
+ clif_cart_delitem(sd,n,amount);
+ clif_updatestatus(sd,SP_CARTINFO);
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * カ?トへアイテム移動
+ *------------------------------------------
+ */
+int pc_putitemtocart(struct map_session_data *sd,int idx,int amount) {
+ struct item *item_data;
+
+ nullpo_retr(0, sd);
+
+ if (idx < 0 || idx >= MAX_CART) //Invalid index check [Skotlex]
+ return 1;
+
+ item_data = &sd->status.inventory[idx];
+
+ if (item_data->nameid==0 || amount < 1 || item_data->amount<amount || sd->vender_id)
+ return 1;
+
+ if (pc_cart_additem(sd,item_data,amount) == 0)
+ return pc_delitem(sd,idx,amount,0);
+
+ return 1;
+}
+
+/*==========================================
+ * カ?ト?のアイテム?確認(個?の差分を返す)
+ *------------------------------------------
+ */
+int pc_cartitem_amount(struct map_session_data *sd,int idx,int amount)
+{
+ struct item *item_data;
+
+ nullpo_retr(-1, sd);
+ nullpo_retr(-1, item_data=&sd->status.cart[idx]);
+
+ if( item_data->nameid==0 || !item_data->amount)
+ return -1;
+ return item_data->amount-amount;
+}
+/*==========================================
+ * カ?トからアイテム移動
+ *------------------------------------------
+ */
+
+int pc_getitemfromcart(struct map_session_data *sd,int idx,int amount)
+{
+ struct item *item_data;
+ int flag;
+
+ nullpo_retr(0, sd);
+
+ if (idx < 0 || idx >= MAX_CART) //Invalid index check [Skotlex]
+ return 1;
+
+ item_data=&sd->status.cart[idx];
+
+ if( item_data->nameid==0 || amount < 1 || item_data->amount<amount || sd->vender_id )
+ return 1;
+ if((flag = pc_additem(sd,item_data,amount)) == 0)
+ return pc_cart_delitem(sd,idx,amount,0);
+
+ clif_additem(sd,0,0,flag);
+ return 1;
+}
+
+/*==========================================
+ * スティル品公開
+ *------------------------------------------
+ */
+int pc_show_steal(struct block_list *bl,va_list ap)
+{
+ struct map_session_data *sd;
+ int itemid;
+ int type;
+
+ struct item_data *item=NULL;
+ char output[100];
+
+ nullpo_retr(0, bl);
+ nullpo_retr(0, ap);
+ nullpo_retr(0, sd=va_arg(ap,struct map_session_data *));
+
+ itemid=va_arg(ap,int);
+ type=va_arg(ap,int);
+
+ if(!type){
+ if((item=itemdb_exists(itemid))==NULL)
+ sprintf(output,"%s stole an Unknown Item.",sd->status.name);
+ else
+ sprintf(output,"%s stole %s.",sd->status.name,item->jname);
+ clif_displaymessage( ((struct map_session_data *)bl)->fd, output);
+ }else{
+ sprintf(output,"%s has not stolen the item because of being overweight.",sd->status.name);
+ clif_displaymessage( ((struct map_session_data *)bl)->fd, output);
+ }
+
+ return 0;
+}
+/*==========================================
+ *
+ *------------------------------------------
+ */
+//** pc.c: Small Steal Item fix by fritz
+int pc_steal_item(struct map_session_data *sd,struct block_list *bl)
+{
+ int i,j,skill,itemid,flag;
+ struct mob_data *md;
+ struct item tmp_item;
+
+ if(!sd || !bl || bl->type != BL_MOB)
+ return 0;
+
+ md=(struct mob_data *)bl;
+
+ if(md->state.steal_flag || status_get_mode(bl)&MD_BOSS || md->master_id ||
+ (md->class_>=1324 && md->class_<1364) || // prevent stealing from treasure boxes [Valaris]
+ map[md->bl.m].flag.nomobloot || // check noloot map flag [Lorky]
+ md->sc_data[SC_STONE].timer != -1 || md->sc_data[SC_FREEZE].timer != -1 //status change check
+ )
+ return 0;
+
+ skill = battle_config.skill_steal_type == 1
+ ? (sd->paramc[4] - md->db->dex)/2 + pc_checkskill(sd,TF_STEAL)*6 + 10
+ : sd->paramc[4] - md->db->dex + pc_checkskill(sd,TF_STEAL)*3 + 10;
+
+ if (skill < 1)
+ return 0;
+
+ j = i = rand()%10; //Pick one mobs drop slot.
+ do {
+ //if it's empty, we check one by one, till find an item
+ i--;
+ if(i<0)
+ i=9; //9th slot
+ itemid = md->db->dropitem[i].nameid;
+ //now try all 10 slots till success
+ if(itemid <= 0 || (itemdb_type(itemid) == 6 && pc_checkskill(sd,TF_STEAL) <= 5))
+ continue;
+ } while (i != j &&
+ rand() % 10000 > ((md->db->dropitem[i].p * skill) / 100 + sd->add_steal_rate)); //fixed rate. From Freya [Lupus]
+
+ if (i == j)
+ return 0;
+
+ md->state.steal_flag = 1;
+
+ memset(&tmp_item,0,sizeof(tmp_item));
+ tmp_item.nameid = itemid;
+ tmp_item.amount = 1;
+ tmp_item.identify = !itemdb_isequip3(itemid);
+ flag = pc_additem(sd,&tmp_item,1);
+
+ if(battle_config.show_steal_in_same_party)
+ party_foreachsamemap(pc_show_steal,sd,1,sd,tmp_item.nameid,flag?1:0);
+ if(flag)
+ clif_additem(sd,0,0,flag);
+ else
+ { //Only invoke logs if item was successfully added (otherwise logs lie about actual item transaction)
+ //Logs items, Stolen from mobs [Lupus]
+ if(log_config.pick > 0 ) {
+ log_pick((struct map_session_data*)md, "M", md->class_, itemid, -1, NULL);
+ log_pick(sd, "P", 0, itemid, 1, NULL);
+ }
+
+ if(log_config.steal) { //this drop log contains ALL stolen items [Lupus]
+ int log_item[10]; //for stolen items logging Lupus
+ memset(&log_item,0,sizeof(log_item));
+ log_item[i] = itemid; //i == monster's drop slot
+ log_drop(sd, md->class_, log_item);
+ }
+
+ //A Rare Steal Global Announce by Lupus
+ if(md->db->dropitem[i].p<=battle_config.rare_drop_announce) {
+ struct item_data *i_data;
+ char message[128];
+ i_data = itemdb_exists(itemid);
+ sprintf (message, msg_txt(542), (sd->status.name != NULL)?sd->status.name :"GM", md->db->jname, i_data->jname, (float)md->db->dropitem[i].p/100);
+ //MSG: "'%s' stole %s's %s (chance: %%%0.02f)"
+ intif_GMmessage(message,strlen(message)+1,0);
+ }
+ }
+ return 1;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int pc_steal_coin(struct map_session_data *sd,struct block_list *bl)
+{
+ if(sd != NULL && bl != NULL && bl->type == BL_MOB) {
+ int rate,skill;
+ struct mob_data *md=(struct mob_data *)bl;
+ if(md && !md->state.steal_coin_flag) {
+ if (md->sc_data && (md->sc_data[SC_STONE].timer != -1 || md->sc_data[SC_FREEZE].timer != -1))
+ return 0;
+ skill = pc_checkskill(sd,RG_STEALCOIN)*10;
+ rate = skill + (sd->status.base_level - md->db->lv)*3 + sd->paramc[4]*2 + sd->paramc[5]*2;
+ if(rand()%1000 < rate) {
+ pc_getzeny(sd,md->db->lv*10 + rand()%100);
+ md->state.steal_coin_flag = 1;
+ return 1;
+ }
+ }
+ }
+
+ return 0;
+}
+//
+//
+//
+/*==========================================
+ * Set's a player position.
+ * Return values:
+ * 0 - Success.
+ * 1 - Invalid map index.
+ * 2 - Map not in this map-server, and failed to locate alternate map-server.
+ * 3 - Failed to warp player because it was in transition between maps.
+ *------------------------------------------
+ */
+int pc_setpos(struct map_session_data *sd,unsigned short mapindex,int x,int y,int clrtype)
+{
+ int m;
+
+ nullpo_retr(0, sd);
+
+ if (!mapindex || !mapindex_id2name(mapindex)) {
+ ShowDebug("pc_setpos: Passed mapindex(%d) is invalid!\n", mapindex);
+ return 1;
+ }
+ if(sd->state.auth && sd->bl.prev == NULL)
+ { //Should NOT move a character while it is not in a map (changing between maps, for example)
+ //state.auth helps identifies if this is the initial setpos rather than a normal map-change set pos.
+ if (battle_config.etc_log)
+ ShowInfo("pc_setpos failed: Attempted to relocate player %s (%d:%d) while it is still between maps.\n", sd->status.name, sd->status.account_id, sd->status.char_id);
+ return 3;
+ }
+ if(sd->chatID) // チャットから出る
+ chat_leavechat(sd);
+ if(sd->trade_partner) // 取引を中?する
+ trade_tradecancel(sd);
+ if(sd->state.storage_flag == 1)
+ storage_storage_quit(sd,0); // 倉庫を開いてるなら保存する
+ else if (sd->state.storage_flag == 2)
+ storage_guild_storage_quit(sd,0);
+
+ if(sd->party_invite>0) // パ?ティ?誘を拒否する
+ party_reply_invite(sd,sd->party_invite_account,0);
+ if(sd->guild_invite>0) // ギルド?誘を拒否する
+ guild_reply_invite(sd,sd->guild_invite,0);
+ if(sd->guild_alliance>0) // ギルド同盟?誘を拒否する
+ guild_reply_reqalliance(sd,sd->guild_alliance_account,0);
+
+ // Delete timer before the player moved to hise repawn point
+ if (sd->pvp_timer != -1 && !battle_config.pk_mode) {
+ delete_timer(sd->pvp_timer, pc_calc_pvprank_timer);
+ sd->pvp_timer = -1;
+ }
+
+ skill_castcancel(&sd->bl,0); // 詠唱中?
+ pc_stop_walking(sd,0); // ?行中?
+ pc_stopattack(sd); // 攻?中?
+
+ if(pc_issit(sd)) {
+ pc_setstand(sd);
+ skill_gangsterparadise(sd,0);
+ }
+
+ m=map_mapindex2mapid(mapindex);
+
+ if (sd->sc_count) {
+ if (sd->sc_data[SC_TRICKDEAD].timer != -1)
+ status_change_end(&sd->bl, SC_TRICKDEAD, -1);
+ if (sd->sc_data[SC_BLADESTOP].timer!=-1)
+ status_change_end(&sd->bl,SC_BLADESTOP,-1);
+ if (sd->sc_data[SC_RUN].timer!=-1)
+ status_change_end(&sd->bl,SC_RUN,-1);
+ if (sd->sc_data[SC_DANCING].timer!=-1) // clear dance effect when warping [Valaris]
+ skill_stop_dancing(&sd->bl);
+ if (sd->sc_data[SC_DEVOTION].timer!=-1)
+ status_change_end(&sd->bl,SC_DEVOTION,-1);
+ if (sd->sc_data[SC_CLOSECONFINE].timer!=-1)
+ status_change_end(&sd->bl,SC_CLOSECONFINE,-1);
+ if (sd->sc_data[SC_CLOSECONFINE2].timer!=-1)
+ status_change_end(&sd->bl,SC_CLOSECONFINE2,-1);
+ if (sd->sc_data[SC_RUN].timer!=-1)
+ status_change_end(&sd->bl,SC_RUN,-1);
+ if (sd->bl.m != m) { //Cancel some map related stuff.
+ if (sd->sc_data[SC_WARM].timer != -1)
+ status_change_end(&sd->bl,SC_WARM,-1);
+ if (sd->sc_data[SC_SUN_COMFORT].timer != -1)
+ status_change_end(&sd->bl,SC_SUN_COMFORT,-1);
+ if (sd->sc_data[SC_MOON_COMFORT].timer != -1)
+ status_change_end(&sd->bl,SC_MOON_COMFORT,-1);
+ if (sd->sc_data[SC_STAR_COMFORT].timer != -1)
+ status_change_end(&sd->bl,SC_STAR_COMFORT,-1);
+ }
+ }
+
+ if(sd->status.option&OPTION_HIDE)
+ status_change_end(&sd->bl, SC_HIDING, -1);
+ if(pc_iscloaking(sd))
+ status_change_end(&sd->bl, SC_CLOAKING, -1);
+ if(pc_ischasewalk(sd))
+ status_change_end(&sd->bl, SC_CHASEWALK, -1);
+
+ if(sd->status.pet_id > 0 && sd->pd && sd->pet.intimate > 0) {
+ pet_stopattack(sd->pd);
+ pet_changestate(sd->pd,MS_IDLE,0);
+ }
+
+ if(m<0){
+ if(sd->mapindex){
+ int ip,port;
+ if(map_mapname2ipport(mapindex,&ip,&port)==0){
+ skill_stop_dancing(&sd->bl);
+ skill_unit_move(&sd->bl,gettick(),4);
+ clif_clearchar_area(&sd->bl,clrtype&0xffff);
+ skill_gangsterparadise(sd,0);
+ map_delblock(&sd->bl);
+ if(sd->status.pet_id > 0 && sd->pd) {
+ if(sd->pd->bl.m != m && sd->pet.intimate <= 0) {
+ pet_remove_map(sd);
+ intif_delete_petdata(sd->status.pet_id);
+ sd->status.pet_id = 0;
+ sd->pd = NULL;
+ sd->petDB = NULL;
+ if(battle_config.pet_status_support)
+ status_calc_pc(sd,2);
+ }
+ else if(sd->pet.intimate > 0) {
+ pet_stopattack(sd->pd);
+ pet_changestate(sd->pd,MS_IDLE,0);
+ clif_clearchar_area(&sd->pd->bl,clrtype&0xffff);
+ map_delblock(&sd->pd->bl);
+ }
+ }
+ sd->mapindex = mapindex;
+ sd->bl.x=x;
+ sd->bl.y=y;
+ sd->state.waitingdisconnect=1;
+ pc_clean_skilltree(sd);
+
+ if(sd->status.pet_id > 0 && sd->pd)
+ intif_save_petdata(sd->status.account_id,&sd->pet);
+ //The storage close routines save the char data. [Skotlex]
+ if (!sd->state.storage_flag)
+ chrif_save(sd,1);
+ else if (sd->state.storage_flag == 1) {
+ storage_storage_quit(sd,1);
+ } else if (sd->state.storage_flag == 2)
+ storage_guild_storage_quit(sd,1);
+
+ chrif_changemapserver(sd, mapindex, x, y, ip, (short)port);
+ return 0;
+ }
+ }
+ return 2;
+ }
+
+ if(x <0 || x >= map[m].xs || y <0 || y >= map[m].ys)
+ x=y=0;
+ if((x==0 && y==0) ||
+ (map_getcell(m,x,y,CELL_CHKNOPASS) && !map_getcell(m, x, y, CELL_CHKICEWALL))
+ ){ //We allow placing players on top of an ICEWALL tile to prevent force-warping players when an ice wall is placed
+ //at spawn points from warps and the like. [Skotlex]
+ if(x||y) {
+ if(battle_config.error_log)
+ ShowError("pc_setpos: attempt to place player on non-walkable tile (%s-%d,%d)\n",mapindex_id2name(mapindex),x,y);
+ }
+ do {
+ x=rand()%(map[m].xs-2)+1;
+ y=rand()%(map[m].ys-2)+1;
+ } while(map_getcell(m,x,y,CELL_CHKNOPASS));
+ }
+
+ if(sd->mapindex && sd->bl.prev != NULL){
+ skill_unit_move(&sd->bl,gettick(),4);
+ clif_clearchar_area(&sd->bl,clrtype&0xffff);
+ skill_gangsterparadise(sd,0);
+
+ map_delblock(&sd->bl);
+ // pet
+ if(sd->status.pet_id > 0 && sd->pd) {
+ if(sd->pd->bl.m != m && sd->pet.intimate <= 0) {
+ pet_remove_map(sd);
+ intif_delete_petdata(sd->status.pet_id);
+ sd->status.pet_id = 0;
+ sd->pd = NULL;
+ sd->petDB = NULL;
+ if(battle_config.pet_status_support)
+ status_calc_pc(sd,2);
+ pc_clean_skilltree(sd);
+
+ if (sd->state.storage_flag == 1)
+ storage_storageclose(sd);
+ else if (sd->state.storage_flag == 2)
+ storage_guild_storageclose(sd);
+ }
+ else if(sd->pet.intimate > 0) {
+ pet_stopattack(sd->pd);
+ pet_changestate(sd->pd,MS_IDLE,0);
+ clif_clearchar_area(&sd->pd->bl,clrtype&0xffff);
+ map_delblock(&sd->pd->bl);
+ }
+ }
+ clif_changemap(sd,map[m].index,x,y); // [MouseJstr]
+ }
+
+ if (sd->mapindex != mapindex) //minimap dot fix [Kevin]
+ {
+ party_send_dot_remove(sd);
+ guild_send_dot_remove(sd);
+ }
+
+ sd->mapindex = mapindex;
+ sd->bl.m = m;
+ sd->to_x = x;
+ sd->to_y = y;
+
+ // moved and changed dance effect stopping
+
+ sd->bl.x = x;
+ sd->bl.y = y;
+
+ if(sd->status.pet_id > 0 && sd->pd && sd->pet.intimate > 0) {
+ sd->pd->bl.m = m;
+ sd->pd->bl.x = sd->pd->to_x = x;
+ sd->pd->bl.y = sd->pd->to_y = y;
+ sd->pd->dir = sd->dir;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * PCのランダムワ?プ
+ *------------------------------------------
+ */
+int pc_randomwarp(struct map_session_data *sd, int type) {
+ int x,y,i=0;
+ int m;
+
+ nullpo_retr(0, sd);
+
+ m=sd->bl.m;
+
+ if (map[sd->bl.m].flag.noteleport) // テレポ?ト禁止
+ return 0;
+
+ do{
+ x=rand()%(map[m].xs-2)+1;
+ y=rand()%(map[m].ys-2)+1;
+ }while(map_getcell(m,x,y,CELL_CHKNOPASS) && (i++)<1000 );
+
+ if (i < 1000)
+ pc_setpos(sd,map[sd->bl.m].index,x,y,type);
+
+ return 0;
+}
+
+/*==========================================
+ * 現在位置のメモ
+ *------------------------------------------
+ */
+int pc_memo(struct map_session_data *sd, int i) {
+ int skill;
+ int j;
+
+ nullpo_retr(0, sd);
+
+ skill = pc_checkskill(sd, AL_WARP);
+
+ if (i >= MIN_PORTAL_MEMO)
+ i -= MIN_PORTAL_MEMO;
+ else if (map[sd->bl.m].flag.nomemo || (map[sd->bl.m].flag.nowarpto && battle_config.any_warp_GM_min_level > pc_isGM(sd))) {
+ clif_skill_teleportmessage(sd, 1);
+ return 0;
+ }
+
+ if (skill < 1) {
+ clif_skill_memo(sd,2);
+ }
+
+ if (skill < 2 || i < -1 || i > 2) {
+ clif_skill_memo(sd, 1);
+ return 0;
+ }
+
+ for(j = 0 ; j < 3; j++) {
+ if (sd->status.memo_point[j].map == map[sd->bl.m].index) {
+ i = j;
+ break;
+ }
+ }
+
+ if (i == -1) {
+ for(i = skill - 3; i >= 0; i--) {
+ memcpy(&sd->status.memo_point[i+1],&sd->status.memo_point[i],
+ sizeof(struct point));
+ }
+ i = 0;
+ }
+ sd->status.memo_point[i].map = map[sd->bl.m].index;
+ sd->status.memo_point[i].x = sd->bl.x;
+ sd->status.memo_point[i].y = sd->bl.y;
+
+ clif_skill_memo(sd, 0);
+
+ return 1;
+}
+
+/*==========================================
+ * pc駆け足要求
+ *------------------------------------------
+ */
+int pc_run(struct map_session_data *sd, int skilllv, int dir)
+{
+ int i,to_x,to_y,dir_x,dir_y;
+
+ nullpo_retr(0, sd);
+
+ if (!pc_can_move(sd)) {
+ if(sd->sc_data[SC_RUN].timer!=-1)
+ status_change_end(&sd->bl,SC_RUN,-1);
+ return 0;
+ }
+
+ to_x = sd->bl.x;
+ to_y = sd->bl.y;
+ dir_x = dirx[dir];
+ dir_y = diry[dir];
+
+ for(i=0;i<AREA_SIZE;i++)
+ {
+ if(!map_getcell(sd->bl.m,to_x+dir_x,to_y+dir_y,CELL_CHKPASS))
+ break;
+
+ to_x += dir_x;
+ to_y += dir_y;
+ }
+
+ //進めない場合 駆け足終了 障害物で止まった場合スパート状態解除
+ if(to_x == sd->bl.x && to_y == sd->bl.y){
+ if(sd->sc_data[SC_RUN].timer!=-1)
+ status_change_end(&sd->bl,SC_RUN,-1);
+ } else
+ pc_walktoxy(sd, to_x, to_y);
+
+ return 1;
+}
+/*==========================================
+ * PCの向居ているほうにstep分歩く
+ *------------------------------------------
+ */
+int pc_walktodir(struct map_session_data *sd,int step)
+{
+ int i,to_x,to_y,dir_x,dir_y;
+
+ nullpo_retr(0, sd);
+
+ to_x = sd->bl.x;
+ to_y = sd->bl.y;
+ dir_x = dirx[(int)sd->dir];
+ dir_y = diry[(int)sd->dir];
+
+ for(i=0;i<step;i++)
+ {
+ if(map_getcell(sd->bl.m,to_x+dir_x,to_y+dir_y,CELL_CHKNOPASS))
+ break;
+
+ if(map_getcell(sd->bl.m,to_x+dir_x,to_y+dir_y,CELL_CHKPASS))
+ {
+ to_x += dir_x;
+ to_y += dir_y;
+ continue;
+ }
+ break;
+ }
+ pc_walktoxy(sd, sd->to_x, sd->to_y);
+
+ return 1;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int pc_can_reach(struct map_session_data *sd,int x,int y)
+{
+ struct walkpath_data wpd;
+
+ nullpo_retr(0, sd);
+
+ if( sd->bl.x==x && sd->bl.y==y ) // 同じマス
+ return 1;
+
+ // 障害物判定
+ wpd.path_len=0;
+ wpd.path_pos=0;
+ wpd.path_half=0;
+ return (path_search(&wpd,sd->bl.m,sd->bl.x,sd->bl.y,x,y,0)!=-1)?1:0;
+}
+
+//
+// ? 行物
+//
+/*==========================================
+ * 次の1?にかかる史ヤを計算
+ *------------------------------------------
+ */
+static int calc_next_walk_step(struct map_session_data *sd)
+{
+ nullpo_retr(0, sd);
+
+ if(sd->walkpath.path_pos>=sd->walkpath.path_len)
+ return -1;
+ if(sd->walkpath.path[sd->walkpath.path_pos]&1)
+ return sd->speed*14/10;
+
+ return sd->speed;
+}
+
+/*==========================================
+ * 半?進む(timer??)
+ *------------------------------------------
+ */
+static int pc_walk(int tid,unsigned int tick,int id,int data)
+{
+ struct map_session_data *sd;
+ int i, x, y, dx, dy;
+
+ if ((sd = map_id2sd(id)) == NULL)
+ return 0;
+
+ if(sd->walktimer != tid){
+ if(battle_config.error_log)
+ ShowError("pc_walk %d != %d\n", sd->walktimer, tid);
+ return 0;
+ }
+
+ sd->walktimer = -1;
+
+ if (sd->walkpath.path_pos >= sd->walkpath.path_len ||
+ sd->walkpath.path_pos != data)
+ return 0;
+
+ //?いたので息吹のタイマ?を初期化
+ sd->inchealspirithptick = 0;
+ sd->inchealspiritsptick = 0;
+
+ sd->walkpath.path_half ^= 1;
+ if (sd->walkpath.path_half == 0) { // マス目中心へ途
+ sd->walkpath.path_pos++;
+ if (sd->state.change_walk_target) {
+ pc_walktoxy_sub(sd);
+ return 0;
+ }
+ } else { // マス目境界へ途
+ if (sd->walkpath.path[sd->walkpath.path_pos] >= 8)
+ return 1;
+ x = sd->bl.x;
+ y = sd->bl.y;
+#ifndef CELL_NOSTACK
+ if (map_getcell(sd->bl.m,x,y,CELL_CHKNOPASS)) {
+ pc_stop_walking(sd,1);
+ return 0;
+ }
+#endif
+ sd->dir = sd->head_dir = sd->walkpath.path[sd->walkpath.path_pos];
+ dx = dirx[(int)sd->dir];
+ dy = diry[(int)sd->dir];
+ if (map_getcell(sd->bl.m,x+dx,y+dy,CELL_CHKNOPASS)) {
+ pc_walktoxy_sub(sd);
+ return 0;
+ }
+ sd->walktimer = 1; // temporarily set (so that in clif_set007x the player will still appear as walking)
+ map_foreachinmovearea(clif_pcoutsight, sd->bl.m,
+ x-AREA_SIZE, y-AREA_SIZE, x+AREA_SIZE, y+AREA_SIZE,
+ dx, dy, BL_ALL, sd);
+
+ sd->walktimer = -1; // set back so not to disturb future pc_stop_walking calls
+ x += dx;
+ y += dy;
+ map_moveblock(&sd->bl, x, y, tick);
+ sd->walktimer = 1; // temporarily set (so that in clif_set007x the player will still appear as walking)
+
+ map_foreachinmovearea (clif_pcinsight, sd->bl.m,
+ x-AREA_SIZE, y-AREA_SIZE, x+AREA_SIZE, y+AREA_SIZE,
+ -dx, -dy, BL_ALL, sd);
+ sd->walktimer = -1; // set back so not to disturb future pc_stop_walking calls
+
+ if (pc_iscloaking(sd)) // クロ?キングの消滅?査
+ skill_check_cloaking(&sd->bl);
+ /* 被ディボ?ション?査 */
+ if (sd->sc_count) {
+ if (sd->sc_data[SC_DANCING].timer != -1)
+ skill_unit_move_unit_group((struct skill_unit_group *)sd->sc_data[SC_DANCING].val2, sd->bl.m, dx, dy);
+ }
+ if (map_getcell(sd->bl.m,x,y,CELL_CHKNPC))
+ npc_touch_areanpc(sd,sd->bl.m,x,y);
+ else
+ sd->areanpc_id = 0;
+ }
+
+ if ((i = calc_next_walk_step(sd)) > 0) {
+ i = i>>1;
+ if (i < 1 && sd->walkpath.path_half == 0)
+ i = 1;
+ sd->walktimer = add_timer (tick+i, pc_walk, id, sd->walkpath.path_pos);
+ }
+ else if(sd->sc_data[SC_RUN].timer!=-1) //Keep trying to run.
+ pc_run(sd, sd->sc_data[SC_RUN].val1, sd->sc_data[SC_RUN].val2);
+ else { //Stopped walking. Update to_x and to_y to current location [Skotlex]
+ sd->to_x = sd->bl.x;
+ sd->to_y = sd->bl.y;
+ }
+ return 0;
+}
+
+/*==========================================
+ * 移動可能か確認して、可能なら?行開始
+ *------------------------------------------
+ */
+static int pc_walktoxy_sub (struct map_session_data *sd)
+{
+ struct walkpath_data wpd;
+ int i;
+
+ nullpo_retr(1, sd);
+
+ if(path_search(&wpd, sd->bl.m, sd->bl.x, sd->bl.y, sd->to_x, sd->to_y, 0))
+ return 1;
+
+ memcpy(&sd->walkpath, &wpd, sizeof(wpd));
+
+ clif_walkok(sd);
+ sd->state.change_walk_target = 0;
+
+ if ((i = calc_next_walk_step(sd)) > 0){
+ i = i >> 2;
+ sd->walktimer = add_timer(gettick()+i, pc_walk, sd->bl.id, 0);
+ }
+ clif_movechar(sd);
+
+ return 0;
+}
+
+/*==========================================
+ * pc? 行要求
+ *------------------------------------------
+ */
+int pc_walktoxy (struct map_session_data *sd, int x, int y)
+{
+ nullpo_retr(0, sd);
+
+ sd->to_x = x;
+ sd->to_y = y;
+ if (sd->sc_data[SC_CONFUSION].timer != -1) //Randomize the target position
+ map_random_dir(&sd->bl, &sd->to_x, &sd->to_y);
+
+ if (sd->walktimer != -1)
+ { //There was a timer-mismatch here. pc_walktoxy_sub does not clears previous pc_walk timers! [Skotlex]
+ sd->state.change_walk_target = 1;
+ } else {
+ pc_walktoxy_sub(sd);
+ }
+
+ if (sd->state.gmaster_flag) {
+ struct guild *g = sd->state.gmaster_flag;
+ int skill, guildflag = 0;
+ if ((skill = guild_checkskill(g, GD_LEADERSHIP)) > 0) guildflag |= skill<<12;
+ if ((skill = guild_checkskill(g, GD_GLORYWOUNDS)) > 0) guildflag |= skill<<8;
+ if ((skill = guild_checkskill(g, GD_SOULCOLD)) > 0) guildflag |= skill<<4;
+ if ((skill = guild_checkskill(g, GD_HAWKEYES)) > 0) guildflag |= skill;
+ if (guildflag)
+ map_foreachinarea (skill_guildaura_sub, sd->bl.m,
+ sd->bl.x-2, sd->bl.y-2, sd->bl.x+2, sd->bl.y+2, BL_PC,
+ sd->bl.id, sd->status.guild_id, &guildflag);
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * ? 行停止
+ *------------------------------------------
+ */
+int pc_stop_walking (struct map_session_data *sd, int type)
+{
+ nullpo_retr(0, sd);
+
+ if (sd->walktimer != -1) {
+ if(type&2 && pc_can_move(sd)){
+ int dx, dy;
+ dx=sd->to_x-sd->bl.x;
+ if(dx<0)
+ dx=-1;
+ else if(dx>0)
+ dx=1;
+ dy=sd->to_y-sd->bl.y;
+ if(dy<0)
+ dy=-1;
+ else if(dy>0)
+ dy=1;
+ if(dx!=0 || dy!=0){
+ sd->to_x=sd->bl.x+dx;
+ sd->to_y=sd->bl.y+dy;
+ sd->state.change_walk_target = 1;
+ return 0;
+ }
+ }
+ delete_timer(sd->walktimer, pc_walk);
+ sd->walktimer = -1;
+ }
+ sd->walkpath.path_len = 0;
+ sd->to_x = sd->bl.x;
+ sd->to_y = sd->bl.y;
+ if (type & 0x01)
+ clif_fixpos(&sd->bl);
+ if (sd->sc_data[SC_RUN].timer != -1)
+ status_change_end(&sd->bl, SC_RUN, -1);
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int pc_movepos(struct map_session_data *sd,int dst_x,int dst_y,int checkpath)
+{
+ int dx,dy;
+
+ struct walkpath_data wpd;
+
+ nullpo_retr(0, sd);
+
+ if(checkpath && path_search(&wpd,sd->bl.m,sd->bl.x,sd->bl.y,dst_x,dst_y,0))
+ return 1;
+
+ sd->dir = sd->head_dir = map_calc_dir(&sd->bl, dst_x,dst_y);
+
+ dx = dst_x - sd->bl.x;
+ dy = dst_y - sd->bl.y;
+
+ map_foreachinmovearea(clif_pcoutsight,sd->bl.m,sd->bl.x-AREA_SIZE,sd->bl.y-AREA_SIZE,sd->bl.x+AREA_SIZE,sd->bl.y+AREA_SIZE,dx,dy,BL_ALL,sd);
+
+ map_moveblock(&sd->bl, dst_x, dst_y, gettick());
+
+ map_foreachinmovearea(clif_pcinsight,sd->bl.m,sd->bl.x-AREA_SIZE,sd->bl.y-AREA_SIZE,sd->bl.x+AREA_SIZE,sd->bl.y+AREA_SIZE,-dx,-dy,BL_ALL,sd);
+
+ if (pc_iscloaking(sd)) // クロ?キングの消滅?査
+ skill_check_cloaking(&sd->bl);
+
+ if(sd->status.pet_id > 0 && sd->pd && sd->pet.intimate > 0) {
+ struct pet_data *pd = sd->pd;
+ int flag = 0;
+
+ //Check if pet needs to be teleported. [Skotlex]
+ if (!checkpath && path_search(&wpd,pd->bl.m,pd->bl.x,pd->bl.y,dst_x,dst_y,0))
+ flag = 1;
+ else if (!check_distance_bl(&sd->bl, &pd->bl, AREA_SIZE)) //Too far, teleport.
+ flag = 2;
+ if (flag) {
+ pet_stopattack(pd);
+ pet_changestate(pd,MS_IDLE,0);
+ if (flag == 2) clif_clearchar_area(&pd->bl,3);
+ map_moveblock(&pd->bl, dst_x, dst_y, gettick());
+ pd->dir = sd->dir;
+ pd->to_x = dst_x;
+ pd->to_y = dst_y;
+ if (flag == 2) clif_fixpos(&pd->bl);
+ else clif_slide(&pd->bl,pd->bl.x,pd->bl.y);
+ }
+ }
+ if(map_getcell(sd->bl.m,sd->bl.x,sd->bl.y,CELL_CHKNPC))
+ npc_touch_areanpc(sd,sd->bl.m,sd->bl.x,sd->bl.y);
+ else
+ sd->areanpc_id=0;
+ return 0;
+}
+
+//
+// 武器??
+//
+/*==========================================
+ * スキルの?索 所有していた場合Lvが返る
+ *------------------------------------------
+ */
+int pc_checkskill(struct map_session_data *sd,int skill_id)
+{
+ if(sd == NULL) return 0;
+ if( skill_id>=10000 ){
+ struct guild *g;
+ if( sd->status.guild_id>0 && (g=guild_search(sd->status.guild_id))!=NULL)
+ return guild_checkskill(g,skill_id);
+ return 0;
+ }
+
+ if(sd->status.skill[skill_id].id == skill_id)
+ return (sd->status.skill[skill_id].lv);
+
+ return 0;
+}
+
+/*==========================================
+ * 武器?更によるスキルの??チェック
+ * 引?:
+ * struct map_session_data *sd セッションデ?タ
+ * int nameid ?備品ID
+ * 返り値:
+ * 0 ?更なし
+ * -1 スキルを解除
+ *------------------------------------------
+ */
+int pc_checkallowskill(struct map_session_data *sd)
+{
+ nullpo_retr(0, sd);
+
+ if(!sd->sc_count)
+ return 0;
+
+ // Skills requiring specific weapon types
+ if(sd->sc_data[SC_TWOHANDQUICKEN].timer!=-1 && !(skill_get_weapontype(KN_TWOHANDQUICKEN)&(1<<sd->status.weapon)))
+ status_change_end(&sd->bl,SC_TWOHANDQUICKEN,-1);
+ if(sd->sc_data[SC_ONEHAND].timer!=-1 && !(skill_get_weapontype(KN_ONEHAND)&(1<<sd->status.weapon)))
+ status_change_end(&sd->bl,SC_ONEHAND,-1);
+ if(sd->sc_data[SC_AURABLADE].timer!=-1 && !(skill_get_weapontype(LK_AURABLADE)&(1<<sd->status.weapon)))
+ // Aura Blade requires any weapon but bare fists
+ status_change_end(&sd->bl,SC_AURABLADE,-1);
+ if(sd->sc_data[SC_PARRYING].timer!=-1 && !(skill_get_weapontype(LK_PARRYING)&(1<<sd->status.weapon)))
+ status_change_end(&sd->bl,SC_PARRYING,-1);
+ if(sd->sc_data[SC_SPEARSQUICKEN].timer!=-1 && !(skill_get_weapontype(CR_SPEARQUICKEN)&(1<<sd->status.weapon)))
+ // Spear Quicken requires a Two-handed spear
+ status_change_end(&sd->bl,SC_SPEARSQUICKEN,-1);
+ if(sd->sc_data[SC_ADRENALINE].timer!=-1 && !(skill_get_weapontype(BS_ADRENALINE)&(1<<sd->status.weapon)))
+ status_change_end(&sd->bl,SC_ADRENALINE,-1);
+ if(sd->sc_data[SC_ADRENALINE2].timer!=-1 && !(skill_get_weapontype(BS_ADRENALINE2)&(1<<sd->status.weapon)))
+ status_change_end(&sd->bl,SC_ADRENALINE2,-1);
+ if( sd->sc_data[SC_SPURT].timer!=-1 && sd->status.weapon)
+ // Spurt requires bare hands (feet, in fact xD)
+ status_change_end(&sd->bl,SC_SPURT,-1);
+
+ if(sd->status.shield <= 0) { // Skills requiring a shield
+ if(sd->sc_data[SC_AUTOGUARD].timer!=-1) // Guard
+ status_change_end(&sd->bl,SC_AUTOGUARD,-1);
+ if(sd->sc_data[SC_DEFENDER].timer!=-1) // Defending Aura
+ status_change_end(&sd->bl,SC_DEFENDER,-1);
+ if(sd->sc_data[SC_REFLECTSHIELD].timer!=-1) // Shield Reflect
+ status_change_end(&sd->bl,SC_REFLECTSHIELD,-1);
+ }
+ return 0;
+}
+
+/*==========================================
+ * ? 備品のチェック
+ *------------------------------------------
+ */
+int pc_checkequip(struct map_session_data *sd,int pos)
+{
+ int i;
+
+ nullpo_retr(-1, sd);
+
+ for(i=0;i<11;i++){
+ if(pos & equip_pos[i])
+ return sd->equip_index[i];
+ }
+
+ return -1;
+}
+
+/*==========================================
+ * ?生職や養子職の元の職業を返す
+ *------------------------------------------
+ */
+struct pc_base_job pc_calc_base_job(int b_class)
+{
+ struct pc_base_job bj;
+ if(b_class < JOB_NOVICE_HIGH){
+ if (b_class == JOB_KNIGHT2)
+ bj.job = JOB_KNIGHT;
+ else if (b_class == JOB_CRUSADER2)
+ bj.job = JOB_CRUSADER;
+ else
+ bj.job = b_class;
+ bj.upper = 0;
+ }else if(b_class >= JOB_NOVICE_HIGH && b_class <= JOB_PALADIN2){ //High Jobs
+ if (b_class == JOB_LORD_KNIGHT2)
+ bj.job = JOB_KNIGHT;
+ else if (b_class == JOB_PALADIN2)
+ bj.job = JOB_CRUSADER;
+ else
+ bj.job = b_class - JOB_NOVICE_HIGH;
+ bj.upper = 1;
+ }else if(b_class >= JOB_TAEKWON && b_class <= JOB_SOUL_LINKER){
+ if (b_class == JOB_STAR_GLADIATOR2)
+ bj.job = 24 + JOB_STAR_GLADIATOR - JOB_TAEKWON;
+ else
+ bj.job = 24 + b_class - JOB_TAEKWON;
+ bj.upper = 0;
+ }else{ //Baby Classes
+ if (b_class == JOB_SUPER_BABY)
+ bj.job = JOB_SUPER_NOVICE;
+ else if (b_class == JOB_BABY_KNIGHT2)
+ bj.job = JOB_KNIGHT;
+ else if (b_class == JOB_BABY_CRUSADER2)
+ bj.job = JOB_CRUSADER;
+ else
+ bj.job = b_class - JOB_BABY;
+ bj.upper = 2;
+ }
+
+ if(bj.job == JOB_NOVICE){
+ bj.type = 0;
+ }else if(bj.job <= JOB_THIEF || bj.job == JOB_TAEKWON){
+ bj.type = 1;
+ }else{
+ bj.type = 2;
+ }
+
+ return bj;
+}
+
+/*==========================================
+ * For quick calculating [Celest]
+ *------------------------------------------
+ */
+int pc_calc_base_job2 (int b_class)
+{
+ if(b_class < JOB_NOVICE_HIGH)
+ {
+ if (b_class == JOB_KNIGHT2)
+ return JOB_KNIGHT;
+ if (b_class == JOB_CRUSADER2)
+ return JOB_CRUSADER;
+ return b_class;
+ }
+ if(b_class >= JOB_NOVICE_HIGH && b_class < JOB_BABY)
+ {
+ if (b_class == JOB_LORD_KNIGHT2)
+ return JOB_KNIGHT;
+ if (b_class == JOB_PALADIN2)
+ return JOB_CRUSADER;
+ return b_class - JOB_NOVICE_HIGH;
+ }
+ if(b_class >= JOB_TAEKWON && b_class <= JOB_SOUL_LINKER )
+ {
+ if (b_class == JOB_STAR_GLADIATOR2)
+ return 24 + JOB_STAR_GLADIATOR - JOB_TAEKWON;
+ return 24 + b_class - JOB_TAEKWON;
+ }
+ //Baby Classes
+ {
+ if (b_class == JOB_SUPER_BABY)
+ return JOB_SUPER_NOVICE;
+ if (b_class == JOB_BABY_KNIGHT2)
+ return JOB_KNIGHT;
+ if (b_class == JOB_BABY_CRUSADER2)
+ return JOB_CRUSADER;
+ return b_class - JOB_BABY;
+ }
+}
+
+/*==========================================
+ * Convert's from the client's lame Job ID system
+ * to the map server's 'makes sense' system. [Skotlex]
+ *------------------------------------------
+ */
+unsigned short pc_jobid2mapid(unsigned short b_class)
+{
+ int class_ = 0;
+ if (b_class >= JOB_BABY && b_class <= JOB_SUPER_BABY)
+ {
+ if (b_class == JOB_SUPER_BABY)
+ b_class = JOB_SUPER_NOVICE;
+ else
+ b_class -= JOB_BABY;
+ class_|= JOBL_BABY;
+ }
+ else if (b_class >= JOB_NOVICE_HIGH && b_class <= JOB_PALADIN2)
+ {
+ b_class -= JOB_NOVICE_HIGH;
+ class_|= JOBL_UPPER;
+ }
+ if (b_class >= JOB_KNIGHT && b_class <= JOB_KNIGHT2)
+ class_|= JOBL_2_1;
+ else if (b_class >= JOB_CRUSADER && b_class <= JOB_CRUSADER2)
+ class_|= JOBL_2_2;
+ switch (b_class)
+ {
+ case JOB_NOVICE:
+ case JOB_SWORDMAN:
+ case JOB_MAGE:
+ case JOB_ARCHER:
+ case JOB_ACOLYTE:
+ case JOB_MERCHANT:
+ case JOB_THIEF:
+ class_ |= b_class;
+ break;
+ case JOB_KNIGHT:
+ case JOB_KNIGHT2:
+ case JOB_CRUSADER:
+ case JOB_CRUSADER2:
+ class_ |= MAPID_SWORDMAN;
+ break;
+ case JOB_PRIEST:
+ case JOB_MONK:
+ class_ |= MAPID_ACOLYTE;
+ break;
+ case JOB_WIZARD:
+ case JOB_SAGE:
+ class_ |= MAPID_MAGE;
+ break;
+ case JOB_BLACKSMITH:
+ case JOB_ALCHEMIST:
+ class_ |= MAPID_MERCHANT;
+ break;
+ case JOB_HUNTER:
+ case JOB_BARD:
+ case JOB_DANCER:
+ class_ |= MAPID_ARCHER;
+ break;
+ case JOB_ASSASSIN:
+ case JOB_ROGUE:
+ class_ |= MAPID_THIEF;
+ break;
+
+ case JOB_STAR_GLADIATOR:
+ case JOB_STAR_GLADIATOR2:
+ class_ |= JOBL_2_1;
+ class_ |= MAPID_TAEKWON;
+ break;
+ case JOB_SOUL_LINKER:
+ class_ |= JOBL_2_2;
+ case JOB_TAEKWON:
+ class_ |= MAPID_TAEKWON;
+ break;
+ case JOB_WEDDING:
+ class_ = MAPID_WEDDING;
+ break;
+ case JOB_SUPER_NOVICE: //Super Novices are considered 2-1 novices. [Skotlex]
+ class_ |= JOBL_2_1;
+ break;
+ case JOB_XMAS:
+ class_ = MAPID_XMAS;
+ break;
+ default:
+ ShowError("pc_jobid2mapid: Unrecognized job %d!\n", b_class);
+ return 0;
+ }
+ return class_;
+}
+
+//Reverts the map-style class id to the client-style one.
+unsigned short pc_mapid2jobid(unsigned short class_, int sex) {
+ switch(class_) {
+ case MAPID_NOVICE:
+ return JOB_NOVICE;
+ case MAPID_SWORDMAN:
+ return JOB_SWORDMAN;
+ case MAPID_MAGE:
+ return JOB_MAGE;
+ case MAPID_ARCHER:
+ return JOB_ARCHER;
+ case MAPID_ACOLYTE:
+ return JOB_ACOLYTE;
+ case MAPID_MERCHANT:
+ return JOB_MERCHANT;
+ case MAPID_THIEF:
+ return JOB_THIEF;
+ case MAPID_TAEKWON:
+ return JOB_TAEKWON;
+ case MAPID_WEDDING:
+ return JOB_WEDDING;
+ case MAPID_XMAS: // [Valaris]
+ return JOB_XMAS;
+ //2_1 classes
+ case MAPID_SUPER_NOVICE:
+ return JOB_SUPER_NOVICE;
+ case MAPID_KNIGHT:
+ return JOB_KNIGHT;
+ case MAPID_WIZARD:
+ return JOB_WIZARD;
+ case MAPID_HUNTER:
+ return JOB_HUNTER;
+ case MAPID_PRIEST:
+ return JOB_PRIEST;
+ case MAPID_BLACKSMITH:
+ return JOB_BLACKSMITH;
+ case MAPID_ASSASSIN:
+ return JOB_ASSASSIN;
+ case MAPID_STAR_GLADIATOR:
+ return JOB_STAR_GLADIATOR;
+ //2_2 classes
+ case MAPID_CRUSADER:
+ return JOB_CRUSADER;
+ case MAPID_SAGE:
+ return JOB_SAGE;
+ case MAPID_BARDDANCER:
+ return sex?JOB_BARD:JOB_DANCER;
+ case MAPID_MONK:
+ return JOB_MONK;
+ case MAPID_ALCHEMIST:
+ return JOB_ALCHEMIST;
+ case MAPID_ROGUE:
+ return JOB_ROGUE;
+ case MAPID_SOUL_LINKER:
+ return JOB_SOUL_LINKER;
+ //1-1: advanced
+ case MAPID_NOVICE_HIGH:
+ return JOB_NOVICE_HIGH;
+ case MAPID_SWORDMAN_HIGH:
+ return JOB_SWORDMAN_HIGH;
+ case MAPID_MAGE_HIGH:
+ return JOB_MAGE_HIGH;
+ case MAPID_ARCHER_HIGH:
+ return JOB_ARCHER_HIGH;
+ case MAPID_ACOLYTE_HIGH:
+ return JOB_ACOLYTE_HIGH;
+ case MAPID_MERCHANT_HIGH:
+ return JOB_MERCHANT_HIGH;
+ case MAPID_THIEF_HIGH:
+ return JOB_THIEF_HIGH;
+ //2_1 advanced
+ case MAPID_LORD_KNIGHT:
+ return JOB_LORD_KNIGHT;
+ case MAPID_HIGH_WIZARD:
+ return JOB_HIGH_WIZARD;
+ case MAPID_SNIPER:
+ return JOB_SNIPER;
+ case MAPID_HIGH_PRIEST:
+ return JOB_HIGH_PRIEST;
+ case MAPID_WHITESMITH:
+ return JOB_WHITESMITH;
+ case MAPID_ASSASSIN_CROSS:
+ return JOB_ASSASSIN_CROSS;
+ //2_2 advanced
+ case MAPID_PALADIN:
+ return JOB_PALADIN;
+ case MAPID_PROFESSOR:
+ return JOB_PROFESSOR;
+ case MAPID_CLOWNGYPSY:
+ return sex?JOB_CLOWN:JOB_GYPSY;
+ case MAPID_CHAMPION:
+ return JOB_CHAMPION;
+ case MAPID_CREATOR:
+ return JOB_CREATOR;
+ case MAPID_STALKER:
+ return JOB_STALKER;
+ //1-1 baby
+ case MAPID_BABY:
+ return JOB_BABY;
+ case MAPID_BABY_SWORDMAN:
+ return JOB_BABY_SWORDMAN;
+ case MAPID_BABY_MAGE:
+ return JOB_BABY_MAGE;
+ case MAPID_BABY_ARCHER:
+ return JOB_BABY_ARCHER;
+ case MAPID_BABY_ACOLYTE:
+ return JOB_BABY_ACOLYTE;
+ case MAPID_BABY_MERCHANT:
+ return JOB_BABY_MERCHANT;
+ case MAPID_BABY_THIEF:
+ return JOB_BABY_THIEF;
+ //2_1 baby
+ case MAPID_SUPER_BABY:
+ return JOB_SUPER_BABY;
+ case MAPID_BABY_KNIGHT:
+ return JOB_BABY_KNIGHT;
+ case MAPID_BABY_WIZARD:
+ return JOB_BABY_WIZARD;
+ case MAPID_BABY_HUNTER:
+ return JOB_BABY_HUNTER;
+ case MAPID_BABY_PRIEST:
+ return JOB_BABY_PRIEST;
+ case MAPID_BABY_BLACKSMITH:
+ return JOB_BABY_BLACKSMITH;
+ case MAPID_BABY_ASSASSIN:
+ return JOB_BABY_ASSASSIN;
+ //2_2 baby
+ case MAPID_BABY_CRUSADER:
+ return JOB_BABY_CRUSADER;
+ case MAPID_BABY_SAGE:
+ return JOB_BABY_SAGE;
+ case MAPID_BABY_BARDDANCER:
+ return sex?JOB_BABY_BARD:JOB_BABY_DANCER;
+ case MAPID_BABY_MONK:
+ return JOB_BABY_MONK;
+ case MAPID_BABY_ALCHEMIST:
+ return JOB_BABY_ALCHEMIST;
+ case MAPID_BABY_ROGUE:
+ return JOB_BABY_ROGUE;
+ default:
+ ShowError("pc_mapid2jobid: Unrecognized job %d!\n", class_);
+ return 0;
+ }
+}
+
+/*====================================================
+ * This function return the name of the job (by [Yor])
+ *----------------------------------------------------
+ */
+char * job_name(int class_) {
+ switch (class_) {
+ case JOB_NOVICE:
+ case JOB_SWORDMAN:
+ case JOB_MAGE:
+ case JOB_ARCHER:
+ case JOB_ACOLYTE:
+ case JOB_MERCHANT:
+ case JOB_THIEF:
+ return msg_txt(550 - JOB_NOVICE+class_);
+
+ case JOB_KNIGHT:
+ case JOB_PRIEST:
+ case JOB_WIZARD:
+ case JOB_BLACKSMITH:
+ case JOB_HUNTER:
+ case JOB_ASSASSIN:
+ return msg_txt(557 - JOB_KNIGHT+class_);
+
+ case JOB_KNIGHT2:
+ return msg_txt(557);
+
+ case JOB_CRUSADER:
+ case JOB_MONK:
+ case JOB_SAGE:
+ case JOB_ROGUE:
+ case JOB_ALCHEMIST:
+ case JOB_BARD:
+ case JOB_DANCER:
+ return msg_txt(563 - JOB_CRUSADER+class_);
+
+ case JOB_CRUSADER2:
+ return msg_txt(563);
+
+ case JOB_WEDDING:
+ case JOB_SUPER_NOVICE:
+ case JOB_GUNSLINGER:
+ case JOB_NINJA:
+ case JOB_XMAS:
+ return msg_txt(570 - JOB_WEDDING+class_);
+
+ case JOB_NOVICE_HIGH:
+ case JOB_SWORDMAN_HIGH:
+ case JOB_MAGE_HIGH:
+ case JOB_ARCHER_HIGH:
+ case JOB_ACOLYTE_HIGH:
+ case JOB_MERCHANT_HIGH:
+ case JOB_THIEF_HIGH:
+ return msg_txt(575 - JOB_NOVICE_HIGH+class_);
+
+ case JOB_LORD_KNIGHT:
+ case JOB_HIGH_PRIEST:
+ case JOB_HIGH_WIZARD:
+ case JOB_WHITESMITH:
+ case JOB_SNIPER:
+ case JOB_ASSASSIN_CROSS:
+ return msg_txt(582 - JOB_LORD_KNIGHT+class_);
+
+ case JOB_LORD_KNIGHT2:
+ return msg_txt(582);
+
+ case JOB_PALADIN:
+ case JOB_CHAMPION:
+ case JOB_PROFESSOR:
+ case JOB_STALKER:
+ case JOB_CREATOR:
+ case JOB_CLOWN:
+ case JOB_GYPSY:
+ return msg_txt(588 - JOB_PALADIN + class_);
+
+ case JOB_PALADIN2:
+ return msg_txt(588);
+
+ case JOB_BABY:
+ case JOB_BABY_SWORDMAN:
+ case JOB_BABY_MAGE:
+ case JOB_BABY_ARCHER:
+ case JOB_BABY_ACOLYTE:
+ case JOB_BABY_MERCHANT:
+ case JOB_BABY_THIEF:
+ return msg_txt(595 - JOB_BABY + class_);
+
+ case JOB_BABY_KNIGHT:
+ case JOB_BABY_PRIEST:
+ case JOB_BABY_WIZARD:
+ case JOB_BABY_BLACKSMITH:
+ case JOB_BABY_HUNTER:
+ case JOB_BABY_ASSASSIN:
+ return msg_txt(602 - JOB_BABY_KNIGHT + class_);
+
+ case JOB_BABY_KNIGHT2:
+ return msg_txt(602);
+
+ case JOB_BABY_CRUSADER:
+ case JOB_BABY_MONK:
+ case JOB_BABY_SAGE:
+ case JOB_BABY_ROGUE:
+ case JOB_BABY_ALCHEMIST:
+ case JOB_BABY_BARD:
+ case JOB_BABY_DANCER:
+ return msg_txt(608 - JOB_BABY_CRUSADER +class_);
+
+ case JOB_BABY_CRUSADER2:
+ return msg_txt(608);
+
+ case JOB_SUPER_BABY:
+ return msg_txt(615);
+
+ case JOB_TAEKWON:
+ return msg_txt(616);
+ case JOB_STAR_GLADIATOR:
+ case JOB_STAR_GLADIATOR2:
+ return msg_txt(617);
+ case JOB_SOUL_LINKER:
+ return msg_txt(618);
+
+ default:
+ return msg_txt(650);
+ }
+}
+
+/*==========================================
+ * PCの攻? (timer??)
+ *------------------------------------------
+ */
+int pc_attack_timer(int tid,unsigned int tick,int id,int data)
+{
+ struct map_session_data *sd;
+ struct block_list *bl;
+ int skill,range;
+
+ sd=map_id2sd(id);
+ if(sd == NULL)
+ return 0;
+
+ //Should we disable this line? Ctrl+click and then going away "IS" idling... [Skotlex]
+ sd->idletime = last_tick;
+
+ if(sd->attacktimer != tid){
+ if(battle_config.error_log)
+ ShowError("pc_attack_timer %d != %d\n",sd->attacktimer,tid);
+ return 0;
+ }
+ sd->attacktimer=-1;
+
+ if(sd->bl.prev == NULL)
+ return 0;
+
+ bl=map_id2bl(sd->attacktarget);
+ if(bl==NULL || bl->prev == NULL)
+ return 0;
+
+ // 同じmapでないなら攻?しない
+ // PCが死んでても攻?しない
+ if(sd->bl.m != bl->m)
+ return 0;
+
+ if(!status_check_skilluse(&sd->bl, bl, 0, 0))
+ return 0;
+
+ if(sd->skilltimer != -1 && pc_checkskill(sd,SA_FREECAST) <= 0)
+ return 0;
+
+ if(!battle_config.sdelay_attack_enable && DIFF_TICK(sd->canact_tick,tick) > 0 && pc_checkskill(sd,SA_FREECAST) <= 0)
+ {
+ if (tid == -1) { //player requested attack.
+ clif_skill_fail(sd,1,4,0);
+ return 0;
+ }
+ //Otherwise, we are in a combo-attack, delay this until your canact time is over. [Skotlex]
+ if(sd->state.attack_continue) {
+ sd->attackabletime = sd->canact_tick;
+ sd->attacktimer=add_timer(sd->attackabletime,pc_attack_timer,sd->bl.id,0);
+ }
+ return 0;
+ }
+
+ if(sd->status.weapon == 11 && sd->equip_index[10] < 0)
+ {
+ clif_arrow_fail(sd,0);
+ return 0;
+ }
+
+ range = sd->attackrange;
+ if(sd->status.weapon != 11) range++;
+ if(battle_iswalking(bl)) range++;
+ if(!battle_check_range(&sd->bl,bl,range) ) {
+ if(pc_can_reach(sd,bl->x,bl->y))
+ clif_movetoattack(sd,bl);
+ return 0;
+ }
+
+ if(battle_config.pc_attack_direction_change)
+ sd->dir=sd->head_dir=map_calc_dir(&sd->bl, bl->x,bl->y ); // 向き設定
+
+ if(sd->walktimer != -1)
+ pc_stop_walking(sd,1);
+
+ if(DIFF_TICK(sd->attackabletime,tick) <= 0) {
+ map_freeblock_lock();
+ sd->attacktarget_lv = battle_weapon_attack(&sd->bl,bl,tick,0);
+
+ if(!(battle_config.pc_cloak_check_type&2) && sd->sc_data[SC_CLOAKING].timer != -1)
+ status_change_end(&sd->bl,SC_CLOAKING,-1);
+
+ if(sd->status.pet_id > 0 && sd->pd && sd->petDB && battle_config.pet_attack_support)
+ pet_target_check(sd,bl,0);
+
+ map_freeblock_unlock();
+
+ if(sd->skilltimer != -1 && (skill = pc_checkskill(sd,SA_FREECAST)) > 0 ) // フリ?キャスト
+ sd->attackabletime = tick + ((sd->aspd<<1)*(150 - skill*5)/100);
+ else
+ sd->attackabletime = tick + (sd->aspd<<1);
+ }
+
+ if(sd->state.attack_continue)
+ sd->attacktimer=add_timer(sd->attackabletime,pc_attack_timer,sd->bl.id,0);
+
+ return 0;
+}
+
+/*==========================================
+ * 攻?要求
+ * typeが1なら??攻?
+ *------------------------------------------
+ */
+int pc_attack(struct map_session_data *sd,int target_id,int type)
+{
+ struct block_list *bl;
+
+ nullpo_retr(0, sd);
+
+ bl=map_id2bl(target_id);
+ if(bl==NULL)
+ return 1;
+
+ if(bl->type==BL_NPC) { // monster npcs [Valaris]
+ npc_click(sd,target_id); // submitted by leinsirk10 [Celest]
+ return 0;
+ }
+
+ if(battle_check_target(&sd->bl,bl,BCT_ENEMY) <= 0 || !status_check_skilluse(&sd->bl, bl, 0, 0))
+ return 1;
+ if(sd->attacktimer != -1)
+ { //Just change target/type. [Skotlex]
+ sd->attacktarget=target_id;
+ sd->state.attack_continue=type;
+ return 0;
+ }
+
+ sd->attacktarget=target_id;
+ sd->state.attack_continue=type;
+
+ if(sd->attackabletime > gettick()){ //Do attack next time it is possible. [Skotlex]
+ sd->attacktimer=add_timer(sd->attackabletime,pc_attack_timer,sd->bl.id,0);
+ } else { //Attack NOW.
+ pc_attack_timer(-1,gettick(),sd->bl.id,0);
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * ??攻?停止
+ *------------------------------------------
+ */
+int pc_stopattack(struct map_session_data *sd)
+{
+ nullpo_retr(0, sd);
+
+ if(sd->attacktimer != -1) {
+ delete_timer(sd->attacktimer,pc_attack_timer);
+ sd->attacktimer=-1;
+ }
+ sd->attacktarget=0;
+ sd->state.attack_continue=0;
+
+ return 0;
+}
+
+int pc_follow_timer(int tid,unsigned int tick,int id,int data)
+{
+ struct map_session_data *sd, *tsd;
+
+ sd = map_id2sd(id);
+ nullpo_retr(0, sd);
+
+ if (sd->followtimer != tid){
+ if(battle_config.error_log)
+ ShowError("pc_follow_timer %d != %d\n",sd->followtimer,tid);
+ sd->followtimer = -1;
+ return 0;
+ }
+
+ sd->followtimer = -1;
+ if (pc_isdead(sd))
+ return 0;
+
+ if ((tsd = map_id2sd(sd->followtarget)) != NULL)
+ {
+ if (pc_isdead(tsd))
+ return 0;
+
+ // either player or target is currently detached from map blocks (could be teleporting),
+ // but still connected to this map, so we'll just increment the timer and check back later
+ if (sd->bl.prev != NULL && tsd->bl.prev != NULL &&
+ sd->skilltimer == -1 && sd->attacktimer == -1 && sd->walktimer == -1)
+ {
+ if((sd->bl.m == tsd->bl.m) && pc_can_reach(sd,tsd->bl.x,tsd->bl.y)) {
+ if (!check_distance_bl(&sd->bl, &tsd->bl, 5) && pc_can_move(sd))
+ pc_walktoxy(sd,tsd->bl.x,tsd->bl.y);
+ } else
+ pc_setpos(sd, tsd->mapindex, tsd->bl.x, tsd->bl.y, 3);
+ }
+ sd->followtimer = add_timer(
+ tick + sd->aspd + rand() % 1000, // increase time a bit to loosen up map's load
+ pc_follow_timer, sd->bl.id, 0);
+ }
+ return 0;
+}
+
+int pc_stop_following (struct map_session_data *sd)
+{
+ nullpo_retr(0, sd);
+
+ if (sd->followtimer != -1) {
+ delete_timer(sd->followtimer,pc_follow_timer);
+ sd->followtimer = -1;
+ }
+ sd->followtarget = -1;
+
+ return 0;
+}
+
+int pc_follow(struct map_session_data *sd,int target_id)
+{
+ struct block_list *bl = map_id2bl(target_id);
+ if (bl == NULL || bl->type != BL_PC)
+ return 1;
+ if (sd->followtimer != -1)
+ pc_stop_following(sd);
+
+ sd->followtarget = target_id;
+ pc_follow_timer(-1,gettick(),sd->bl.id,0);
+
+ return 0;
+}
+
+int pc_checkbaselevelup(struct map_session_data *sd)
+{
+ int next = pc_nextbaseexp(sd);
+
+ nullpo_retr(0, sd);
+
+ if(sd->status.base_exp >= next && next > 0){
+
+ // base側レベルアップ?理
+ sd->status.base_exp -= next;
+
+ sd->status.base_level ++;
+ if (battle_config.pet_lv_rate && sd->pd) //<Skotlex> update pet's level
+ status_calc_pet(sd,0);
+ if (battle_config.use_statpoint_table)
+ { // Taken from pc_resetstate. [Skotlex]
+ int lv = sd->status.base_level;
+ if (lv >= MAX_LEVEL) lv = MAX_LEVEL - 1;
+ else if (lv < 1) lv = 1;
+ sd->status.status_point += statp[lv] - statp[lv-1];
+ } else //Estimated way.
+ sd->status.status_point += (sd->status.base_level+14) / 5 ;
+ clif_updatestatus(sd,SP_STATUSPOINT);
+ clif_updatestatus(sd,SP_BASELEVEL);
+ clif_updatestatus(sd,SP_NEXTBASEEXP);
+ status_calc_pc(sd,0);
+ pc_heal(sd,sd->status.max_hp,sd->status.max_sp);
+
+ //スパノビはキリエ、イムポ、マニピ、グロ、サフラLv1がかかる
+ if((sd->class_&MAPID_UPPERMASK) == MAPID_SUPER_NOVICE || (sd->class_&MAPID_UPPERMASK) == MAPID_TAEKWON){
+ status_change_start(&sd->bl,SkillStatusChangeTable[PR_KYRIE],1,0,0,0,skill_get_time(PR_KYRIE,1),0 );
+ status_change_start(&sd->bl,SkillStatusChangeTable[PR_IMPOSITIO],1,0,0,0,skill_get_time(PR_IMPOSITIO,1),0 );
+ status_change_start(&sd->bl,SkillStatusChangeTable[PR_MAGNIFICAT],1,0,0,0,skill_get_time(PR_MAGNIFICAT,1),0 );
+ status_change_start(&sd->bl,SkillStatusChangeTable[PR_GLORIA],1,0,0,0,skill_get_time(PR_GLORIA,1),0 );
+ status_change_start(&sd->bl,SkillStatusChangeTable[PR_SUFFRAGIUM],1,0,0,0,skill_get_time(PR_SUFFRAGIUM,1),0 );
+ }
+
+ clif_misceffect(&sd->bl,0);
+ //LORDALFA - LVLUPEVENT
+ if (script_config.event_script_type == 0) {
+ struct npc_data *npc;
+ if ((npc = npc_name2id(script_config.baselvup_event_name))) {
+ run_script(npc->u.scr.script,0,sd->bl.id,npc->bl.id); // PCLvlUPNPC
+ ShowStatus("Event '"CL_WHITE"%s"CL_RESET"' executed.\n",script_config.baselvup_event_name);
+ }
+ } else {
+ ShowStatus("%d '"CL_WHITE"%s"CL_RESET"' events executed.\n",
+ npc_event_doall_id(script_config.baselvup_event_name, sd->bl.id), script_config.baselvup_event_name);
+ }
+
+ return 1;
+ }
+
+ return 0;
+}
+
+int pc_checkjoblevelup(struct map_session_data *sd)
+{
+ int next = pc_nextjobexp(sd);
+
+ nullpo_retr(0, sd);
+
+ if(sd->status.job_exp >= next && next > 0){
+ // job側レベルアップ?理
+ sd->status.job_exp -= next;
+ sd->status.job_level ++;
+ clif_updatestatus(sd,SP_JOBLEVEL);
+ clif_updatestatus(sd,SP_NEXTJOBEXP);
+ sd->status.skill_point ++;
+ clif_updatestatus(sd,SP_SKILLPOINT);
+ status_calc_pc(sd,0);
+
+ clif_misceffect(&sd->bl,1);
+ if (pc_checkskill(sd, SG_DEVIL) && sd->status.job_level >= battle_config.max_job_level)
+ clif_status_change(&sd->bl,SI_DEVIL, 1); //Permanent blind effect from SG_DEVIL.
+
+ if (script_config.event_script_type == 0) {
+ struct npc_data *npc;
+ if ((npc = npc_name2id(script_config.joblvup_event_name))) {
+ run_script(npc->u.scr.script,0,sd->bl.id,npc->bl.id); // PCLvlUPNPC
+ ShowStatus("Event '"CL_WHITE"%s"CL_RESET"' executed.\n",script_config.joblvup_event_name);
+ }
+ } else {
+ ShowStatus("%d '"CL_WHITE"%s"CL_RESET"' events executed.\n",
+ npc_event_doall_id(script_config.joblvup_event_name, sd->bl.id), script_config.joblvup_event_name);
+ }
+
+ return 1;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * ??値取得
+ *------------------------------------------
+ */
+int pc_gainexp(struct map_session_data *sd,int base_exp,int job_exp)
+{
+ char output[256];
+ float nextbp=0, nextjp=0;
+ int nextb=0, nextj=0;
+ nullpo_retr(0, sd);
+
+ if(sd->bl.prev == NULL || pc_isdead(sd))
+ return 0;
+
+ if((battle_config.pvp_exp == 0) && map[sd->bl.m].flag.pvp) // [MouseJstr]
+ return 0; // no exp on pvp maps
+
+ if(sd->status.guild_id>0){ // ギルドに上納
+ base_exp-=guild_payexp(sd,base_exp);
+ if(base_exp < 0)
+ base_exp = 0;
+ }
+
+ if(!battle_config.multi_level_up && pc_nextbaseafter(sd) && sd->status.base_exp+base_exp >= pc_nextbaseafter(sd)) {
+ base_exp = pc_nextbaseafter(sd) - sd->status.base_exp;
+ if (base_exp < 0)
+ base_exp = 0;
+ }
+ nextb = pc_nextbaseexp(sd);
+ nextj = pc_nextjobexp(sd);
+ if (nextb > 0)
+ nextbp = (float) base_exp / (float) nextb;
+ if (nextj > 0)
+ nextjp = (float) job_exp / (float) nextj;
+
+ sd->status.base_exp += base_exp;
+ if(sd->status.base_exp < 0)
+ sd->status.base_exp = 0;
+
+ while(pc_checkbaselevelup(sd)) ;
+
+ clif_updatestatus(sd,SP_BASEEXP);
+ if(!battle_config.multi_level_up && pc_nextjobafter(sd) && sd->status.job_exp+job_exp >= pc_nextjobafter(sd)) {
+ job_exp = pc_nextjobafter(sd) - sd->status.job_exp;
+ if (job_exp < 0)
+ job_exp = 0;
+ }
+
+ sd->status.job_exp += job_exp;
+ if(sd->status.job_exp < 0)
+ sd->status.job_exp = 0;
+
+ while(pc_checkjoblevelup(sd)) ;
+
+ clif_updatestatus(sd,SP_JOBEXP);
+
+ if(sd->state.showexp){
+ sprintf(output,
+ "Experienced Gained Base:%d (%.2f%%) Job:%d (%.2f%%)",base_exp,nextbp*(float)100,job_exp,nextjp*(float)100);
+ clif_disp_onlyself(sd,output,strlen(output));
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * base level側必要??値計算
+ *------------------------------------------
+ */
+int pc_nextbaseexp(struct map_session_data *sd)
+{
+ int i =0;
+
+ nullpo_retr(0, sd);
+
+ if(sd->status.base_level>=MAX_LEVEL || sd->status.base_level<=0)
+ return 0;
+
+ i = (sd->class_&JOBL_UPPER)?4:0; //4 is the base for upper, 0 for normal/baby ones.
+
+ if ((sd->class_&MAPID_UPPERMASK) == MAPID_NOVICE)
+ ; //Add 0, it's novice.
+ else if ((sd->class_&MAPID_UPPERMASK) == MAPID_SUPER_NOVICE)
+ i = 3; //Super Novice/Super Baby
+ else
+ i+= (sd->class_&JOBL_2)?2:1; //Add 2 for second classes, add 1 for first classes.
+
+ return exp_table[i][sd->status.base_level-1];
+}
+
+/*==========================================
+ * job level側必要??値計算
+ *------------------------------------------
+ */
+int pc_nextjobexp(struct map_session_data *sd)
+{
+ int i;
+
+ nullpo_retr(0, sd);
+
+ if(sd->status.job_level>=MAX_LEVEL || sd->status.job_level<=0)
+ return 0;
+
+ i = (sd->class_&JOBL_UPPER)?11:7; //11 is the base for upper, 7 for normal/baby ones.
+
+ if ((sd->class_&MAPID_UPPERMASK) == MAPID_NOVICE)
+ ; //Add 0, it's novice.
+ else if ((sd->class_&MAPID_UPPERMASK) == MAPID_SUPER_NOVICE)
+ i = 10; //Super Novice/Super Baby
+ else if ((sd->class_&MAPID_UPPERMASK) == MAPID_STAR_GLADIATOR) {
+ i = 13; //Star Gladiator - slow JExp (as for 2nd class)
+ if (sd->status.job_level >= battle_config.max_job_level)
+ return 0; //Since SG aren't really an advanced class... [Skotlex]
+ } else
+ i+= (sd->class_&JOBL_2)?2:1; //Add 2 for second classes, add 1 for first classes.
+
+ return exp_table[i][sd->status.job_level-1];
+}
+
+/*==========================================
+ * base level after next [Valaris]
+ *------------------------------------------
+ */
+int pc_nextbaseafter(struct map_session_data *sd)
+{
+ int i;
+
+ nullpo_retr(0, sd);
+
+ if(sd->status.base_level>=MAX_LEVEL || sd->status.base_level<=0)
+ return 0;
+
+ i = (sd->class_&JOBL_UPPER)?4:0; //4 is the base for upper, 0 for normal/baby ones.
+
+ if ((sd->class_&MAPID_UPPERMASK) == MAPID_NOVICE)
+ ; //Add 0, it's novice.
+ else if ((sd->class_&MAPID_UPPERMASK) == MAPID_SUPER_NOVICE)
+ i = 3; //Super Novice/Super Baby
+ else
+ i+= (sd->class_&JOBL_2)?2:1; //Add 2 for second classes, add 1 for first classes.
+
+ return exp_table[i][sd->status.base_level];
+}
+
+/*==========================================
+ * job level after next [Valaris]
+ *------------------------------------------
+ */
+int pc_nextjobafter(struct map_session_data *sd)
+{
+ int i;
+
+ nullpo_retr(0, sd);
+
+ if(sd->status.job_level>=MAX_LEVEL || sd->status.job_level<=0)
+ return 0;
+
+ i = (sd->class_&JOBL_UPPER)?11:7; //11 is the base for upper, 7 for normal/baby ones.
+
+ if ((sd->class_&MAPID_UPPERMASK) == MAPID_NOVICE)
+ ; //Add 0, it's novice.
+ else if ((sd->class_&MAPID_UPPERMASK) == MAPID_SUPER_NOVICE)
+ i = 10; //Super Novice/Super Baby
+ else if ((sd->class_&MAPID_UPPERMASK) == MAPID_STAR_GLADIATOR) {
+ i = 13; //Star Gladiator - slow JExp (as for 2nd class)
+ if (sd->status.job_level >= battle_config.max_job_level)
+ return 0; //Since SG aren't really an advanced class... [Skotlex]
+ } else
+ i+= (sd->class_&JOBL_2)?2:1; //Add 2 for second classes, add 1 for first classes.
+
+ return exp_table[i][sd->status.job_level];
+}
+/*==========================================
+
+ * 必要ステ?タスポイント計算
+ *------------------------------------------
+ */
+int pc_need_status_point(struct map_session_data *sd,int type)
+{
+ int val;
+
+ nullpo_retr(-1, sd);
+
+ if(type<SP_STR || type>SP_LUK)
+ return -1;
+ val =
+ type==SP_STR ? sd->status.str :
+ type==SP_AGI ? sd->status.agi :
+ type==SP_VIT ? sd->status.vit :
+ type==SP_INT ? sd->status.int_:
+ type==SP_DEX ? sd->status.dex : sd->status.luk;
+
+ return (val+9)/10+1;
+}
+
+/*==========================================
+ * 能力値成長
+ *------------------------------------------
+ */
+int pc_statusup(struct map_session_data *sd,int type)
+{
+ int max, need,val = 0;
+
+ nullpo_retr(0, sd);
+
+ max = pc_maxparameter(sd);
+
+ need=pc_need_status_point(sd,type);
+ if(type<SP_STR || type>SP_LUK || need<0 || need>sd->status.status_point){
+ clif_statusupack(sd,type,0,0);
+ return 1;
+ }
+ switch(type){
+ case SP_STR:
+ if(sd->status.str >= max) {
+ clif_statusupack(sd,type,0,0);
+ return 1;
+ }
+ val= ++sd->status.str;
+ break;
+ case SP_AGI:
+ if(sd->status.agi >= max) {
+ clif_statusupack(sd,type,0,0);
+ return 1;
+ }
+ val= ++sd->status.agi;
+ break;
+ case SP_VIT:
+ if(sd->status.vit >= max) {
+ clif_statusupack(sd,type,0,0);
+ return 1;
+ }
+ val= ++sd->status.vit;
+ break;
+ case SP_INT:
+ if(sd->status.int_ >= max) {
+ clif_statusupack(sd,type,0,0);
+ return 1;
+ }
+ val= ++sd->status.int_;
+ break;
+ case SP_DEX:
+ if(sd->status.dex >= max) {
+ clif_statusupack(sd,type,0,0);
+ return 1;
+ }
+ val= ++sd->status.dex;
+ break;
+ case SP_LUK:
+ if(sd->status.luk >= max) {
+ clif_statusupack(sd,type,0,0);
+ return 1;
+ }
+ val= ++sd->status.luk;
+ break;
+ }
+ sd->status.status_point-=need;
+ if(need!=pc_need_status_point(sd,type)){
+ clif_updatestatus(sd,type-SP_STR+SP_USTR);
+ }
+ clif_updatestatus(sd,SP_STATUSPOINT);
+ clif_updatestatus(sd,type);
+ status_calc_pc(sd,0);
+ clif_statusupack(sd,type,1,val);
+
+ return 0;
+}
+
+/*==========================================
+ * 能力値成長
+ *------------------------------------------
+ */
+int pc_statusup2(struct map_session_data *sd,int type,int val)
+{
+ int max;
+ nullpo_retr(0, sd);
+
+ max = pc_maxparameter(sd);
+
+ if(type<SP_STR || type>SP_LUK){
+ clif_statusupack(sd,type,0,0);
+ return 1;
+ }
+ switch(type){
+ case SP_STR:
+ if(sd->status.str + val >= max)
+ val = max;
+ else if(sd->status.str + val < 1)
+ val = 1;
+ else
+ val += sd->status.str;
+ sd->status.str = val;
+ break;
+ case SP_AGI:
+ if(sd->status.agi + val >= max)
+ val = max;
+ else if(sd->status.agi + val < 1)
+ val = 1;
+ else
+ val += sd->status.agi;
+ sd->status.agi = val;
+ break;
+ case SP_VIT:
+ if(sd->status.vit + val >= max)
+ val = max;
+ else if(sd->status.vit + val < 1)
+ val = 1;
+ else
+ val += sd->status.vit;
+ sd->status.vit = val;
+ break;
+ case SP_INT:
+ if(sd->status.int_ + val >= max)
+ val = max;
+ else if(sd->status.int_ + val < 1)
+ val = 1;
+ else
+ val += sd->status.int_;
+ sd->status.int_ = val;
+ break;
+ case SP_DEX:
+ if(sd->status.dex + val >= max)
+ val = max;
+ else if(sd->status.dex + val < 1)
+ val = 1;
+ else
+ val += sd->status.dex;
+ sd->status.dex = val;
+ break;
+ case SP_LUK:
+ if(sd->status.luk + val >= max)
+ val = max;
+ else if(sd->status.luk + val < 1)
+ val = 1;
+ else
+ val = sd->status.luk + val;
+ sd->status.luk = val;
+ break;
+ }
+ clif_updatestatus(sd,type-SP_STR+SP_USTR);
+ clif_updatestatus(sd,type);
+ status_calc_pc(sd,0);
+ clif_statusupack(sd,type,1,val);
+
+ return 0;
+}
+
+/*==========================================
+ * スキルポイント割り振り
+ *------------------------------------------
+ */
+int pc_skillup(struct map_session_data *sd,int skill_num)
+{
+ nullpo_retr(0, sd);
+
+ if( skill_num>=10000 ){
+ guild_skillup(sd,skill_num,0);
+ return 0;
+ }
+
+ if( sd->status.skill_point>0 &&
+ sd->status.skill[skill_num].id!=0 &&
+ //sd->status.skill[skill_num].lv < skill_get_max(skill_num) ) - celest
+ sd->status.skill[skill_num].lv < skill_tree_get_max(skill_num, sd->status.class_) )
+ {
+ sd->status.skill[skill_num].lv++;
+ sd->status.skill_point--;
+ status_calc_pc(sd,0);
+ clif_skillup(sd,skill_num);
+ clif_updatestatus(sd,SP_SKILLPOINT);
+ clif_skillinfoblock(sd);
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * /allskill
+ *------------------------------------------
+ */
+int pc_allskillup(struct map_session_data *sd)
+{
+ int i,id;
+
+ nullpo_retr(0, sd);
+
+ for(i=0;i<MAX_SKILL;i++){
+ sd->status.skill[i].id=0;
+ if (sd->status.skill[i].flag && sd->status.skill[i].flag != 13){ // cardスキルなら、
+ sd->status.skill[i].lv=(sd->status.skill[i].flag==1)?0:sd->status.skill[i].flag-2; // 本?のlvに
+ sd->status.skill[i].flag=0; // flagは0にしておく
+ }
+ }
+
+ if (battle_config.gm_allskill > 0 && pc_isGM(sd) >= battle_config.gm_allskill){
+ // 全てのスキル
+ for(i=0;i<MAX_SKILL;i++){
+ if(!(skill_get_inf2(i)&(INF2_NPC_SKILL|INF2_GUILD_SKILL))) //Get ALL skills except npc/guild ones. [Skotlex]
+ if (i!=SG_DEVIL) //and except SG_DEVIL [Komurka]
+ sd->status.skill[i].lv=skill_get_max(i); //Nonexistant skills should return a max of 0 anyway.
+ }
+ }
+ else {
+ int inf2;
+ for(i=0;i < MAX_SKILL_TREE && (id=skill_tree[sd->status.class_][i].id)>0;i++){
+ inf2 = skill_get_inf2(id);
+ if(sd->status.skill[id].id==0 &&
+ (!(inf2&INF2_QUEST_SKILL) || battle_config.quest_skill_learn) &&
+ !(inf2&(INF2_WEDDING_SKILL|INF2_SPIRIT_SKILL)) &&
+ (id!=SG_DEVIL))
+ {
+ sd->status.skill[id].id = id; // celest
+ sd->status.skill[id].lv = skill_tree_get_max(id, sd->status.class_); // celest
+ }
+ }
+ }
+ status_calc_pc(sd,0);
+
+ return 0;
+}
+
+/*==========================================
+ * /resetlvl
+ *------------------------------------------
+ */
+int pc_resetlvl(struct map_session_data* sd,int type)
+{
+ int i;
+
+ nullpo_retr(0, sd);
+
+ for(i=1;i<MAX_SKILL;i++){
+ sd->status.skill[i].lv = 0;
+ }
+
+ if(type == 1){
+ sd->status.skill_point=0;
+ sd->status.base_level=1;
+ sd->status.job_level=1;
+ sd->status.base_exp=sd->status.base_exp=0;
+ sd->status.job_exp=sd->status.job_exp=0;
+ if(sd->status.option !=0)
+ sd->status.option = 0;
+
+ sd->status.str=1;
+ sd->status.agi=1;
+ sd->status.vit=1;
+ sd->status.int_=1;
+ sd->status.dex=1;
+ sd->status.luk=1;
+ if(sd->status.class_ == JOB_NOVICE_HIGH)
+ sd->status.status_point=100; // not 88 [celest]
+ // give platinum skills upon changing
+ pc_skill(sd,142,1,0);
+ pc_skill(sd,143,1,0);
+ }
+
+ if(type == 2){
+ sd->status.skill_point=0;
+ sd->status.base_level=1;
+ sd->status.job_level=1;
+ sd->status.base_exp=0;
+ sd->status.job_exp=0;
+ }
+ if(type == 3){
+ sd->status.base_level=1;
+ sd->status.base_exp=0;
+ }
+ if(type == 4){
+ sd->status.job_level=1;
+ sd->status.job_exp=0;
+ }
+
+ clif_updatestatus(sd,SP_STATUSPOINT);
+ clif_updatestatus(sd,SP_STR);
+ clif_updatestatus(sd,SP_AGI);
+ clif_updatestatus(sd,SP_VIT);
+ clif_updatestatus(sd,SP_INT);
+ clif_updatestatus(sd,SP_DEX);
+ clif_updatestatus(sd,SP_LUK);
+ clif_updatestatus(sd,SP_BASELEVEL);
+ clif_updatestatus(sd,SP_JOBLEVEL);
+ clif_updatestatus(sd,SP_STATUSPOINT);
+ clif_updatestatus(sd,SP_NEXTBASEEXP);
+ clif_updatestatus(sd,SP_NEXTJOBEXP);
+ clif_updatestatus(sd,SP_SKILLPOINT);
+
+ clif_updatestatus(sd,SP_USTR); // Updates needed stat points - Valaris
+ clif_updatestatus(sd,SP_UAGI);
+ clif_updatestatus(sd,SP_UVIT);
+ clif_updatestatus(sd,SP_UINT);
+ clif_updatestatus(sd,SP_UDEX);
+ clif_updatestatus(sd,SP_ULUK); // End Addition
+
+ for(i=0;i<11;i++) { // unequip items that can't be equipped by base 1 [Valaris]
+ if(sd->equip_index[i] >= 0)
+ if(!pc_isequip(sd,sd->equip_index[i]))
+ pc_unequipitem(sd,sd->equip_index[i],2);
+ }
+
+ if ((type == 1 || type == 2 || type == 3) && sd->status.party_id) {
+ //Send map-change packet to do a level range check and break party settings. [Skotlex]
+ party_send_movemap(sd);
+ }
+ clif_skillinfoblock(sd);
+ status_calc_pc(sd,0);
+
+ return 0;
+}
+/*==========================================
+ * /resetstate
+ *------------------------------------------
+ */
+int pc_resetstate(struct map_session_data* sd)
+{
+ nullpo_retr(0, sd);
+
+ if (battle_config.use_statpoint_table)
+ { // New statpoint table used here - Dexity
+ int lv;
+ // allow it to just read the last entry [celest]
+ lv = sd->status.base_level < MAX_LEVEL ? sd->status.base_level : MAX_LEVEL - 1;
+
+ sd->status.status_point = statp[lv];
+ if (sd->class_&JOBL_UPPER)
+ sd->status.status_point+=52; // extra 52+48=100 stat points
+ } else { //Use new stat-calculating equation [Skotlex]
+#define sumsp(a) (((a-1)/10 +2)*(5*((a-1)/10 +1) + (a-1)%10) -10)
+ int add=0;
+ add += sumsp(sd->status.str);
+ add += sumsp(sd->status.agi);
+ add += sumsp(sd->status.vit);
+ add += sumsp(sd->status.int_);
+ add += sumsp(sd->status.dex);
+ add += sumsp(sd->status.luk);
+ sd->status.status_point+=add;
+ }
+
+ sd->status.str=1;
+ sd->status.agi=1;
+ sd->status.vit=1;
+ sd->status.int_=1;
+ sd->status.dex=1;
+ sd->status.luk=1;
+
+ clif_updatestatus(sd,SP_STR);
+ clif_updatestatus(sd,SP_AGI);
+ clif_updatestatus(sd,SP_VIT);
+ clif_updatestatus(sd,SP_INT);
+ clif_updatestatus(sd,SP_DEX);
+ clif_updatestatus(sd,SP_LUK);
+
+ clif_updatestatus(sd,SP_USTR); // Updates needed stat points - Valaris
+ clif_updatestatus(sd,SP_UAGI);
+ clif_updatestatus(sd,SP_UVIT);
+ clif_updatestatus(sd,SP_UINT);
+ clif_updatestatus(sd,SP_UDEX);
+ clif_updatestatus(sd,SP_ULUK); // End Addition
+
+ clif_updatestatus(sd,SP_STATUSPOINT);
+ status_calc_pc(sd,0);
+
+ return 0;
+}
+
+/*==========================================
+ * /resetskill
+ *------------------------------------------
+ */
+int pc_resetskill(struct map_session_data* sd)
+{
+ int i, skill, inf2;
+ nullpo_retr(0, sd);
+
+ if (pc_checkskill(sd, SG_DEVIL) && sd->status.job_level >= battle_config.max_job_level)
+ clif_status_load(&sd->bl, SI_DEVIL, 0); //Remove perma blindness due to skill-reset. [Skotlex]
+
+ for (i = 1; i < MAX_SKILL; i++) {
+ if ((skill = sd->status.skill[i].lv) > 0) {
+ inf2 = skill_get_inf2(i);
+ if ((!(inf2&INF2_QUEST_SKILL) || battle_config.quest_skill_learn) &&
+ !(inf2&(INF2_WEDDING_SKILL|INF2_SPIRIT_SKILL))) //Avoid reseting wedding/linker skills.
+ {
+ if (!sd->status.skill[i].flag)
+ sd->status.skill_point += skill;
+ else if (sd->status.skill[i].flag > 2 && sd->status.skill[i].flag != 13)
+ sd->status.skill_point += (sd->status.skill[i].flag - 2);
+ sd->status.skill[i].lv = 0;
+ }
+ else if (battle_config.quest_skill_reset && (inf2&INF2_QUEST_SKILL))
+ {
+ sd->status.skill[i].lv = 0;
+ sd->status.skill[i].flag = 0;
+ }
+ } else {
+ sd->status.skill[i].lv = 0;
+ }
+ }
+ clif_updatestatus(sd,SP_SKILLPOINT);
+ clif_skillinfoblock(sd);
+ status_calc_pc(sd,0);
+
+ return 0;
+}
+
+/*==========================================
+ * /resetfeel [Komurka]
+ *------------------------------------------
+ */
+int pc_resetfeel(struct map_session_data* sd)
+{
+ int i;
+ char feel_var[3][NAME_LENGTH] = {"PC_FEEL_SUN","PC_FEEL_MOON","PC_FEEL_STAR"};
+ nullpo_retr(0, sd);
+
+ for (i=0; i<3; i++)
+ {
+ sd->feel_map[i].m = -1;
+ sd->feel_map[i].index = 0;
+ pc_setglobalreg(sd,feel_var[i],0);
+ }
+
+ return 0;
+}
+
+static int pc_respawn(int tid,unsigned int tick,int id,int data)
+{
+ struct map_session_data *sd = map_id2sd(id);
+ if (sd && pc_isdead(sd))
+ { //Auto-respawn [Skotlex]
+ pc_setstand(sd);
+ pc_setrestartvalue(sd,3);
+ pc_setpos(sd,sd->status.save_point.map,sd->status.save_point.x,sd->status.save_point.y,0);
+ }
+ return 0;
+}
+/*==========================================
+ * pcにダメ?ジを?える
+ *------------------------------------------
+ */
+int pc_damage(struct block_list *src,struct map_session_data *sd,int damage)
+{
+ int i=0,j=0, resurrect_flag=0;
+
+ nullpo_retr(0, sd);
+
+ // ?に死んでいたら無?
+ if(pc_isdead(sd))
+ return 0;
+ // 座ってたら立ち上がる
+ if(pc_issit(sd)) {
+ pc_setstand(sd);
+ skill_gangsterparadise(sd,0);
+ }
+
+ // ? いていたら足を止める
+ if (sd->sc_data) {
+ if (sd->sc_data[SC_ENDURE].timer != -1 && (src != NULL && src->type == BL_MOB) && !map_flag_gvg(sd->bl.m)) {
+ if (!sd->special_state.infinite_endure && (--sd->sc_data[SC_ENDURE].val2) < 0)
+ status_change_end(&sd->bl, SC_ENDURE, -1);
+ }
+ if (sd->sc_data[SC_GRAVITATION].timer != -1 &&
+ sd->sc_data[SC_GRAVITATION].val3 == BCT_SELF) {
+ struct skill_unit_group *sg = (struct skill_unit_group *)sd->sc_data[SC_GRAVITATION].val4;
+ if (sg) {
+ skill_delunitgroup(sg);
+ status_change_end(&sd->bl, SC_GRAVITATION, -1);
+ }
+ }
+
+ }
+
+ // 演奏/ダンスの中?
+ if(damage > sd->status.max_hp>>2)
+ skill_stop_dancing(&sd->bl);
+
+ if (damage < sd->status.hp)
+ sd->status.hp-=damage;
+ else
+ sd->status.hp = 0;
+
+ if(sd->status.pet_id > 0 && sd->pd && sd->petDB && battle_config.pet_damage_support)
+ pet_target_check(sd,src,1);
+
+ if (sd->sc_data[SC_TRICKDEAD].timer != -1)
+ status_change_end(&sd->bl, SC_TRICKDEAD, -1);
+ if(sd->status.option&OPTION_HIDE)
+ status_change_end(&sd->bl, SC_HIDING, -1);
+ if(pc_iscloaking(sd))
+ status_change_end(&sd->bl, SC_CLOAKING, -1);
+ if(pc_ischasewalk(sd))
+ status_change_end(&sd->bl, SC_CHASEWALK, -1);
+
+ clif_updatestatus(sd,SP_HP);
+
+ if(sd->status.hp>0){
+ if(sd->status.hp<sd->status.max_hp>>2 && sd->sc_data[SC_AUTOBERSERK].timer != -1 &&
+ (sd->sc_data[SC_PROVOKE].timer==-1 || sd->sc_data[SC_PROVOKE].val2==0 ))
+ // オ?トバ?サ?ク?動
+ status_change_start(&sd->bl,SC_PROVOKE,10,1,0,0,0,0);
+
+ sd->canlog_tick = gettick();
+
+ return damage;
+ }
+
+ if(sd->vender_id)
+ vending_closevending(sd);
+
+ if(sd->status.pet_id > 0 && sd->pd &&
+ !map[sd->bl.m].flag.nopenalty) {
+ if(sd->petDB) {
+ sd->pet.intimate -= sd->petDB->die;
+ if(sd->pet.intimate < 0)
+ sd->pet.intimate = 0;
+ clif_send_petdata(sd,1,sd->pet.intimate);
+ }
+ }
+
+ // Leave duel if you die [LuzZza]
+ if(battle_config.duel_autoleave_when_die) {
+ if(sd->duel_group > 0)
+ duel_leave(sd->duel_group, sd);
+ if(sd->duel_invite > 0)
+ duel_reject(sd->duel_invite, sd);
+ }
+
+ pc_stop_walking(sd,0);
+ skill_castcancel(&sd->bl,0); // 詠唱の中止
+ skill_stop_dancing(&sd->bl); //You should stop dancing when dead... [Skotlex]
+ if (sd->sc_data[SC_GOSPEL].timer != -1 && sd->sc_data[SC_GOSPEL].val4 == BCT_SELF)
+ { //Remove Gospel [Skotlex]
+ struct skill_unit_group *sg = (struct skill_unit_group *)sd->sc_data[SC_GOSPEL].val3;
+ if (sg)
+ skill_delunitgroup(sg);
+ }
+ clif_clearchar_area(&sd->bl,1);
+
+ if (src && src->type == BL_PC) {
+ struct map_session_data *ssd = (struct map_session_data *)src;
+ if (ssd) {
+ if (sd->state.event_death)
+ pc_setglobalreg(sd,"killerrid",(ssd->status.account_id));
+ if (ssd->state.event_kill) {
+ if (script_config.event_script_type == 0) {
+ struct npc_data *npc;
+ if ((npc = npc_name2id(script_config.kill_event_name))) {
+ run_script(npc->u.scr.script,0,sd->bl.id,npc->bl.id); // PCKillNPC
+ ShowStatus("Event '"CL_WHITE"%s"CL_RESET"' executed.\n", script_config.kill_event_name);
+ }
+ } else {
+ ShowStatus("%d '"CL_WHITE"%s"CL_RESET"' events executed.\n",
+ npc_event_doall_id(script_config.kill_event_name, sd->bl.id), script_config.kill_event_name);
+ }
+ }
+ if (battle_config.pk_mode && ssd->status.manner >= 0 && battle_config.manner_system) {
+ ssd->status.manner -= 5;
+ if(ssd->status.manner < 0)
+ status_change_start(src,SC_NOCHAT,0,0,0,0,0,0);
+
+ // PK/Karma system code (not enabled yet) [celest]
+ // originally from Kade Online, so i don't know if any of these is correct ^^;
+ // note: karma is measured REVERSE, so more karma = more 'evil' / less honourable,
+ // karma going down = more 'good' / more honourable.
+ // The Karma System way...
+ /*if (sd->status.karma > ssd->status.karma) { // If player killed was more evil
+ sd->status.karma--;
+ ssd->status.karma--;
+ }
+ else if (sd->status.karma < ssd->status.karma) // If player killed was more good
+ ssd->status.karma++;*/
+
+ // or the PK System way...
+ /* if (sd->status.karma > 0) // player killed is dishonourable?
+ ssd->status.karma--; // honour points earned
+ sd->status.karma++; // honour points lost */
+ // To-do: Receive exp on certain occasions
+ }
+ }
+ }
+
+ if (sd->state.event_death) {
+ if (script_config.event_script_type == 0) {
+ struct npc_data *npc;
+ if ((npc = npc_name2id(script_config.die_event_name))) {
+ run_script(npc->u.scr.script,0,sd->bl.id,npc->bl.id); // PCDeathNPC
+ ShowStatus("Event '"CL_WHITE"%s"CL_RESET"' executed.\n", script_config.die_event_name);
+ }
+ } else {
+ ShowStatus("%d '"CL_WHITE"%s"CL_RESET"' events executed.\n",
+ npc_event_doall_id(script_config.die_event_name, sd->bl.id), script_config.die_event_name);
+ }
+ }
+
+// PK/Karma system code (not enabled yet) [celest]
+ /*if(sd->status.karma > 0) {
+ int eq_num=0,eq_n[MAX_INVENTORY];
+ memset(eq_n,0,sizeof(eq_n));
+ for(i=0;i<MAX_INVENTORY;i++){
+ int k;
+ for(k=0;k<MAX_INVENTORY;k++){
+ if(eq_n[k] <= 0){
+ eq_n[k]=i;
+ break;
+ }
+ }
+ eq_num++;
+ }
+ if(eq_num > 0){
+ int n = eq_n[rand()%eq_num];
+ if(rand()%10000 < sd->status.karma){
+ if(sd->status.inventory[n].equip)
+ pc_unequipitem(sd,n,0);
+ pc_dropitem(sd,n,1);
+ }
+ }
+ }*/
+
+ if(battle_config.bone_drop==2
+ || (battle_config.bone_drop==1 && map[sd->bl.m].flag.pvp)){ // ドクロドロップ
+ struct item item_tmp;
+ memset(&item_tmp,0,sizeof(item_tmp));
+ item_tmp.nameid=7420; //PVP Skull item ID
+ item_tmp.identify=1;
+ item_tmp.card[0]=0x00fe;
+ item_tmp.card[1]=0;
+ item_tmp.card[2]=GetWord(sd->char_id,0); // CharId
+ item_tmp.card[3]=GetWord(sd->char_id,1);
+ map_addflooritem(&item_tmp,1,sd->bl.m,sd->bl.x,sd->bl.y,NULL,NULL,NULL,0);
+ }
+
+ // activate Steel body if a super novice dies at 99+% exp [celest]
+ if ((sd->class_&MAPID_UPPERMASK) == MAPID_SUPER_NOVICE) {
+ if ((i=pc_nextbaseexp(sd))<=0)
+ i=sd->status.base_exp;
+ if (i>0 && (j=sd->status.base_exp*1000/i)>=990 && j<1000)
+ sd->state.snovice_flag = 4;
+ }
+
+ for(i = 0; i < 5; i++)
+ if (sd->devotion[i]){
+ struct map_session_data *devsd = map_id2sd(sd->devotion[i]);
+ if (devsd) status_change_end(&devsd->bl,SC_DEVOTION,-1);
+ }
+
+ pc_setdead(sd);
+ skill_unit_move(&sd->bl,gettick(),4);
+
+ pc_setglobalreg(sd,"PC_DIE_COUNTER",++sd->die_counter); //死にカウンタ?書き?み
+ // changed penalty options, added death by player if pk_mode [Valaris]
+ if(battle_config.death_penalty_type && sd->state.snovice_flag != 4
+ && (sd->class_&MAPID_UPPERMASK) != MAPID_NOVICE // only novices will receive no penalty
+ && !map[sd->bl.m].flag.nopenalty && !map_flag_gvg(sd->bl.m)
+ && !(sd->sc_count && sd->sc_data[SC_BABY].timer!=-1))
+ {
+ if(battle_config.death_penalty_type==1 && battle_config.death_penalty_base > 0)
+ sd->status.base_exp -= (int) ((double)pc_nextbaseexp(sd) * (double)battle_config.death_penalty_base/10000);
+ if(battle_config.pk_mode && src && src->type==BL_PC)
+ sd->status.base_exp -= (int) ((double)pc_nextbaseexp(sd) * (double)battle_config.death_penalty_base/10000);
+ else if(battle_config.death_penalty_type==2 && battle_config.death_penalty_base > 0) {
+ if(pc_nextbaseexp(sd) > 0)
+ sd->status.base_exp -= (int) ((double)sd->status.base_exp * (double)battle_config.death_penalty_base/10000);
+ if(battle_config.pk_mode && src && src->type==BL_PC)
+ sd->status.base_exp -= (int) ((double)sd->status.base_exp * (double)battle_config.death_penalty_base/10000);
+ }
+ if(sd->status.base_exp < 0)
+ sd->status.base_exp = 0;
+ clif_updatestatus(sd,SP_BASEEXP);
+
+ if(battle_config.death_penalty_type==1 && battle_config.death_penalty_job > 0)
+ sd->status.job_exp -= (int) ((double)pc_nextjobexp(sd) * (double)battle_config.death_penalty_job/10000);
+ if(battle_config.pk_mode && src && src->type==BL_PC)
+ sd->status.job_exp -= (int) ((double)pc_nextjobexp(sd) * (double)battle_config.death_penalty_job/10000);
+ else if(battle_config.death_penalty_type==2 && battle_config.death_penalty_job > 0) {
+ if(pc_nextjobexp(sd) > 0)
+ sd->status.job_exp -= (int) ((double)sd->status.job_exp * (double)battle_config.death_penalty_job/10000);
+ if(battle_config.pk_mode && src && src->type==BL_PC)
+ sd->status.job_exp -= (int) ((double)sd->status.job_exp * (double)battle_config.death_penalty_job/10000);
+ }
+ if(sd->status.job_exp < 0)
+ sd->status.job_exp = 0;
+ clif_updatestatus(sd,SP_JOBEXP);
+ }
+ if(src && src->type==BL_MOB) {
+ struct mob_data *md=(struct mob_data *)src;
+ if(md && md->target_id != 0 && md->target_id==sd->bl.id) { // reset target id when player dies
+ md->target_id=0;
+ mob_changestate(md,MS_WALK,0);
+ }
+ if(battle_config.mobs_level_up && md && md->state.state!=MS_DEAD &&
+ md->level < battle_config.max_base_level && !md->guardian_data // Guardians should not level. [Skotlex]
+ ) { // monster level up [Valaris]
+ clif_misceffect(&md->bl,0);
+ md->level++;
+ md->hp+=(int) (sd->status.max_hp*.1);
+ if (battle_config.show_mob_hp)
+ clif_charnameack (0, &md->bl);
+ }
+ }
+ //Clear these data here so that SC_BABY check may work. [Skotlex]
+ resurrect_flag = (sd->sc_data[SC_KAIZEL].timer != -1)?sd->sc_data[SC_KAIZEL].val1:0; //Auto-resurrect later in the code.
+ status_change_clear(&sd->bl,0); // ステ?タス異常を解除する
+ clif_updatestatus(sd,SP_HP);
+ status_calc_pc(sd,0);
+ sd->canregen_tick = gettick();
+
+
+ //ナイトメアモ?ドアイテムドロップ
+ if(map[sd->bl.m].flag.pvp_nightmaredrop){ // Moved this outside so it works when PVP isnt enabled and during pk mode [Ancyker]
+ for(j=0;j<MAX_DROP_PER_MAP;j++){
+ int id = map[sd->bl.m].drop_list[j].drop_id;
+ int type = map[sd->bl.m].drop_list[j].drop_type;
+ int per = map[sd->bl.m].drop_list[j].drop_per;
+ if(id == 0)
+ continue;
+ if(id == -1){//ランダムドロップ
+ int eq_num=0,eq_n[MAX_INVENTORY];
+ memset(eq_n,0,sizeof(eq_n));
+ //先ず?備しているアイテム?をカウント
+ for(i=0;i<MAX_INVENTORY;i++){
+ int k;
+ if( (type == 1 && !sd->status.inventory[i].equip)
+ || (type == 2 && sd->status.inventory[i].equip)
+ || type == 3){
+ //InventoryIndexを格納
+ for(k=0;k<MAX_INVENTORY;k++){
+ if(eq_n[k] <= 0){
+ eq_n[k]=i;
+ break;
+ }
+ }
+ eq_num++;
+ }
+ }
+ if(eq_num > 0){
+ int n = eq_n[rand()%eq_num];//該?アイテムの中からランダム
+ if(rand()%10000 < per){
+ if(sd->status.inventory[n].equip)
+ pc_unequipitem(sd,n,3);
+ pc_dropitem(sd,n,1);
+ }
+ }
+ }
+ else if(id > 0){
+ for(i=0;i<MAX_INVENTORY;i++){
+ if(sd->status.inventory[i].nameid == id//ItemIDが一致していて
+ && rand()%10000 < per//ドロップ率判定もOKで
+ && ((type == 1 && !sd->status.inventory[i].equip)//タイプ判定もOKならドロップ
+ || (type == 2 && sd->status.inventory[i].equip)
+ || type == 3) ){
+ if(sd->status.inventory[i].equip)
+ pc_unequipitem(sd,i,3);
+ pc_dropitem(sd,i,1);
+ break;
+ }
+ }
+ }
+ }
+ }
+ // pvp
+ if( map[sd->bl.m].flag.pvp && !battle_config.pk_mode){ // disable certain pvp functions on pk_mode [Valaris]
+ //ランキング計算
+ if (!map[sd->bl.m].flag.pvp_nocalcrank) {
+ sd->pvp_point -= 5;
+ sd->pvp_lost++;
+ if (src && src->type == BL_PC) {
+ struct map_session_data *ssd = (struct map_session_data *)src;
+ if (ssd) { ssd->pvp_point++; ssd->pvp_won++; }
+ }
+ }
+ // ?制送還
+ if( sd->pvp_point < 0 ){
+ sd->pvp_point=0;
+ add_timer(gettick()+1000, pc_respawn,sd->bl.id,0);
+ return damage;
+ }
+ }
+ //GvG
+ if(map_flag_gvg(sd->bl.m)){
+ add_timer(gettick()+1000, pc_respawn,sd->bl.id,0);
+ return damage;
+ }
+
+ if (sd->state.snovice_flag == 4 || resurrect_flag) {
+ if (sd->state.snovice_flag == 4 || sd->special_state.restart_full_recover) {
+ sd->status.hp = sd->status.max_hp;
+ sd->status.sp = sd->status.max_sp;
+ } else { //10% life per each level in Kaizel
+ sd->status.hp = 10*resurrect_flag*sd->status.max_hp/100;
+ }
+ clif_skill_nodamage(&sd->bl,&sd->bl,ALL_RESURRECTION,1,1);
+ pc_setstand(sd);
+ clif_updatestatus(sd, SP_HP);
+ clif_updatestatus(sd, SP_SP);
+ clif_resurrection(&sd->bl, 1);
+ sd->state.snovice_flag = 0;
+ if(battle_config.pc_invincible_time)
+ pc_setinvincibletimer(sd, battle_config.pc_invincible_time);
+ if (resurrect_flag)
+ status_change_start(&sd->bl,SkillStatusChangeTable[PR_KYRIE],10,0,0,0,skill_get_time2(SL_KAIZEL, resurrect_flag),0);
+ else
+ status_change_start(&sd->bl,SkillStatusChangeTable[MO_STEELBODY],1,0,0,0,skill_get_time(MO_STEELBODY,1),0 );
+ return 0;
+ }
+
+ return damage;
+}
+
+//
+// script? 連
+//
+/*==========================================
+ * script用PCステ?タス?み出し
+ *------------------------------------------
+ */
+int pc_readparam(struct map_session_data *sd,int type)
+{
+ int val=0;
+
+ nullpo_retr(0, sd);
+
+ switch(type){
+ case SP_SKILLPOINT:
+ val= sd->status.skill_point;
+ break;
+ case SP_STATUSPOINT:
+ val= sd->status.status_point;
+ break;
+ case SP_ZENY:
+ val= sd->status.zeny;
+ break;
+ case SP_BASELEVEL:
+ val= sd->status.base_level;
+ break;
+ case SP_JOBLEVEL:
+ val= sd->status.job_level;
+ break;
+ case SP_CLASS:
+ if(val>=24 && val < 45)
+ val+=3978;
+ else
+ val= sd->status.class_;
+ break;
+ case SP_BASEJOB: //Base job, extracting upper type.
+ val= pc_mapid2jobid(sd->class_&MAPID_UPPERMASK, sd->status.sex);
+ break;
+ case SP_UPPER:
+ val= sd->class_&JOBL_UPPER?1:(sd->class_&JOBL_BABY?2:0);
+ break;
+ case SP_BASECLASS: //Extract base class tree. [Skotlex]
+ val= pc_mapid2jobid(sd->class_&MAPID_BASEMASK, sd->status.sex);
+ break;
+ case SP_SEX:
+ val= sd->sex;
+ break;
+ case SP_WEIGHT:
+ val= sd->weight;
+ break;
+ case SP_MAXWEIGHT:
+ val= sd->max_weight;
+ break;
+ case SP_BASEEXP:
+ val= sd->status.base_exp;
+ break;
+ case SP_JOBEXP:
+ val= sd->status.job_exp;
+ break;
+ case SP_NEXTBASEEXP:
+ val= pc_nextbaseexp(sd);
+ break;
+ case SP_NEXTJOBEXP:
+ val= pc_nextjobexp(sd);
+ break;
+ case SP_HP:
+ val= sd->status.hp;
+ break;
+ case SP_MAXHP:
+ val= sd->status.max_hp;
+ break;
+ case SP_SP:
+ val= sd->status.sp;
+ break;
+ case SP_MAXSP:
+ val= sd->status.max_sp;
+ break;
+ case SP_STR:
+ val= sd->status.str;
+ break;
+ case SP_AGI:
+ val= sd->status.agi;
+ break;
+ case SP_VIT:
+ val= sd->status.vit;
+ break;
+ case SP_INT:
+ val= sd->status.int_;
+ break;
+ case SP_DEX:
+ val= sd->status.dex;
+ break;
+ case SP_LUK:
+ val= sd->status.luk;
+ break;
+ case SP_KARMA: // celest
+ val = sd->status.karma;
+ break;
+ case SP_MANNER:
+ val = sd->status.manner;
+ break;
+ case SP_FAME:
+ val= sd->status.fame;
+ break;
+ }
+
+ return val;
+}
+
+/*==========================================
+ * script用PCステ?タス設定
+ *------------------------------------------
+ */
+int pc_setparam(struct map_session_data *sd,int type,int val)
+{
+ int i = 0,up_level = battle_config.max_job_level;
+
+ nullpo_retr(0, sd);
+
+ switch(type){
+ case SP_BASELEVEL:
+ if ((val+ sd->status.base_level) > battle_config.max_base_level) //Capping to max
+ val = battle_config.max_base_level - sd->status.base_level;
+ if (val > (int)sd->status.base_level) {
+ for (i = 1; i <= (val - (int)sd->status.base_level); i++)
+ sd->status.status_point += (sd->status.base_level + i + 14) / 5 ;
+ }
+ sd->status.base_level = val;
+ sd->status.base_exp = 0;
+ clif_updatestatus(sd, SP_BASELEVEL);
+ clif_updatestatus(sd, SP_NEXTBASEEXP);
+ clif_updatestatus(sd, SP_STATUSPOINT);
+ clif_updatestatus(sd, SP_BASEEXP);
+ status_calc_pc(sd, 0);
+ pc_heal(sd, sd->status.max_hp, sd->status.max_sp);
+ break;
+ case SP_JOBLEVEL:
+ if ((sd->class_&MAPID_UPPERMASK) == MAPID_NOVICE)
+ up_level = 10; //Novice & Baby Novice have 10 Job Levels only
+ else if ((sd->class_&MAPID_UPPERMASK) == MAPID_SUPER_NOVICE) //Super Novice & Super Baby can go up to 99
+ up_level = battle_config.max_sn_level;
+ else if (sd->class_&JOBL_UPPER && sd->class_&JOBL_2) //3rd Job has 70 Job Levels
+ up_level = battle_config.max_adv_level;
+ if (val >= (int)sd->status.job_level) {
+ if (val > up_level) val = up_level;
+ sd->status.skill_point += (val-sd->status.job_level);
+ sd->status.job_level = val;
+ sd->status.job_exp = 0;
+ clif_updatestatus(sd, SP_JOBLEVEL);
+ clif_updatestatus(sd, SP_NEXTJOBEXP);
+ clif_updatestatus(sd, SP_JOBEXP);
+ clif_updatestatus(sd, SP_SKILLPOINT);
+ status_calc_pc(sd, 0);
+ clif_misceffect(&sd->bl, 1);
+ } else {
+ sd->status.job_level = val;
+ sd->status.job_exp = 0;
+ clif_updatestatus(sd, SP_JOBLEVEL);
+ clif_updatestatus(sd, SP_NEXTJOBEXP);
+ clif_updatestatus(sd, SP_JOBEXP);
+ status_calc_pc(sd, 0);
+ }
+ clif_updatestatus(sd,type);
+ break;
+ case SP_SKILLPOINT:
+ sd->status.skill_point = val;
+ break;
+ case SP_STATUSPOINT:
+ sd->status.status_point = val;
+ break;
+ case SP_ZENY:
+ if(val <= MAX_ZENY) {
+ // MAX_ZENY 以下なら代入
+ sd->status.zeny = val;
+ } else {
+ sd->status.zeny = MAX_ZENY;
+ /* Could someone explain the comments below? I have no idea what they are trying to do...
+ * if you want to give someone so much zeny, just set their zeny to the max. [Skotlex]
+ if(sd->status.zeny > val) {
+ // Zeny が減少しているなら代入
+ sd->status.zeny = val;
+ } else if(sd->status.zeny <= MAX_ZENY) {
+ // Zeny が増加していて、現在の値がMAX_ZENY 以下ならMAX_ZENY
+ sd->status.zeny = MAX_ZENY;
+ } else {
+ // Zeny が増加していて、現在の値がMAX_ZENY より下なら増加分を無視
+ ;
+ }
+ */
+ }
+ break;
+ case SP_BASEEXP:
+ if(pc_nextbaseexp(sd) > 0) {
+ sd->status.base_exp = val;
+ if(sd->status.base_exp < 0)
+ sd->status.base_exp=0;
+ pc_checkbaselevelup(sd);
+ }
+ break;
+ case SP_JOBEXP:
+ if(pc_nextjobexp(sd) > 0) {
+ sd->status.job_exp = val;
+ if(sd->status.job_exp < 0)
+ sd->status.job_exp=0;
+ pc_checkjoblevelup(sd);
+ }
+ break;
+ case SP_SEX:
+ sd->sex = val;
+ break;
+ case SP_WEIGHT:
+ sd->weight = val;
+ break;
+ case SP_MAXWEIGHT:
+ sd->max_weight = val;
+ break;
+ case SP_HP:
+ sd->status.hp = val;
+ break;
+ case SP_MAXHP:
+ sd->status.max_hp = val;
+ break;
+ case SP_SP:
+ sd->status.sp = val;
+ break;
+ case SP_MAXSP:
+ sd->status.max_sp = val;
+ break;
+ case SP_STR:
+ sd->status.str = val;
+ break;
+ case SP_AGI:
+ sd->status.agi = val;
+ break;
+ case SP_VIT:
+ sd->status.vit = val;
+ break;
+ case SP_INT:
+ sd->status.int_ = val;
+ break;
+ case SP_DEX:
+ sd->status.dex = val;
+ break;
+ case SP_LUK:
+ sd->status.luk = val;
+ break;
+ case SP_KARMA:
+ sd->status.karma = val;
+ break;
+ case SP_MANNER:
+ sd->status.manner = val;
+ break;
+ case SP_FAME:
+ sd->status.fame = val;
+ break;
+ }
+ clif_updatestatus(sd,type);
+
+ return 0;
+}
+
+/*==========================================
+ * HP/SP回復
+ *------------------------------------------
+ */
+int pc_heal(struct map_session_data *sd,int hp,int sp)
+{
+// if(battle_config.battle_log)
+// printf("heal %d %d\n",hp,sp);
+
+ nullpo_retr(0, sd);
+
+ if(pc_checkoverhp(sd)) {
+ if(hp > 0)
+ hp = 0;
+ }
+ if(pc_checkoversp(sd)) {
+ if(sp > 0)
+ sp = 0;
+ }
+
+ if(sd->sc_count && sd->sc_data[SC_BERSERK].timer!=-1) //バ?サ?ク中は回復させないらしい
+ return 0;
+
+ if(hp+sd->status.hp>sd->status.max_hp)
+ hp=sd->status.max_hp-sd->status.hp;
+ if(sp+sd->status.sp>sd->status.max_sp)
+ sp=sd->status.max_sp-sd->status.sp;
+ sd->status.hp+=hp;
+ if(sd->status.hp <= 0) {
+ sd->status.hp = 0;
+ pc_damage(NULL,sd,1);
+ hp = 0;
+ }
+ sd->status.sp+=sp;
+ if(sd->status.sp <= 0)
+ sd->status.sp = 0;
+ if(hp)
+ clif_updatestatus(sd,SP_HP);
+ if(sp)
+ clif_updatestatus(sd,SP_SP);
+
+ if(sd->status.hp>=sd->status.max_hp>>2 && sd->sc_data[SC_AUTOBERSERK].timer != -1 &&
+ (sd->sc_data[SC_PROVOKE].timer!=-1 && sd->sc_data[SC_PROVOKE].val2==1 ))
+ status_change_end(&sd->bl,SC_PROVOKE,-1); //End auto berserk.
+
+ return hp + sp;
+}
+
+/*==========================================
+ * HP/SP回復
+ *------------------------------------------
+ */
+int pc_itemheal(struct map_session_data *sd,int hp,int sp)
+{
+ int bonus, type;
+// if(battle_config.battle_log)
+// printf("heal %d %d\n",hp,sp);
+
+ nullpo_retr(0, sd);
+
+ if(sd->sc_count && sd->sc_data[SC_GOSPEL].timer!=-1) //バ?サ?ク中は回復させないらしい
+ return 0;
+
+ if(pc_checkoverhp(sd)) {
+ if(hp > 0)
+ hp = 0;
+ }
+ if(pc_checkoversp(sd)) {
+ if(sp > 0)
+ sp = 0;
+ }
+
+ if(hp > 0) {
+ bonus = (sd->paramc[2]<<1) + 100 + pc_checkskill(sd,SM_RECOVERY)*10
+ + pc_checkskill(sd,AM_LEARNINGPOTION)*5;
+ // A potion produced by an Alchemist in the Fame Top 10 gets +50% effect [DracoRPG]
+ bonus += (potion_flag==2)?50:(potion_flag==3?100:0);
+ if ((type = itemdb_group(sd->itemid)) > 0 && type <= 7)
+ bonus = bonus * (100+sd->itemhealrate[type - 1]) / 100;
+ if(bonus != 100)
+ hp = hp * bonus / 100;
+ }
+ if(sp > 0) {
+ bonus = (sd->paramc[3]<<1) + 100 + pc_checkskill(sd,MG_SRECOVERY)*10
+ + pc_checkskill(sd,AM_LEARNINGPOTION)*5;
+ bonus += (potion_flag==2)?50:(potion_flag==3?100:0);
+ if(bonus != 100)
+ sp = sp * bonus / 100;
+ }
+ if(hp+sd->status.hp>sd->status.max_hp)
+ hp=sd->status.max_hp-sd->status.hp;
+ if(sp+sd->status.sp>sd->status.max_sp)
+ sp=sd->status.max_sp-sd->status.sp;
+ sd->status.hp+=hp;
+ if(sd->status.hp <= 0) {
+ sd->status.hp = 0;
+ pc_damage(NULL,sd,1);
+ hp = 0;
+ }
+ sd->status.sp+=sp;
+ if(sd->status.sp <= 0)
+ sd->status.sp = 0;
+ if(hp)
+ clif_updatestatus(sd,SP_HP);
+ if(sp)
+ clif_updatestatus(sd,SP_SP);
+
+ return 0;
+}
+
+/*==========================================
+ * HP/SP回復
+ *------------------------------------------
+ */
+int pc_percentheal(struct map_session_data *sd,int hp,int sp)
+{
+ nullpo_retr(0, sd);
+
+ if(pc_checkoverhp(sd)) {
+ if(hp > 0)
+ hp = 0;
+ }
+ if(pc_checkoversp(sd)) {
+ if(sp > 0)
+ sp = 0;
+ }
+ if(hp) {
+ if(hp >= 100) {
+ sd->status.hp = sd->status.max_hp;
+ }
+ else if(hp <= -100) {
+ sd->status.hp = 0;
+ pc_damage(NULL,sd,1);
+ }
+ else {
+ sd->status.hp += sd->status.max_hp*hp/100;
+ if(sd->status.hp > sd->status.max_hp)
+ sd->status.hp = sd->status.max_hp;
+ if(sd->status.hp <= 0) {
+ sd->status.hp = 0;
+ pc_damage(NULL,sd,1);
+ hp = 0;
+ }
+ }
+ }
+ if(sp) {
+ if(sp >= 100) {
+ sd->status.sp = sd->status.max_sp;
+ }
+ else if(sp <= -100) {
+ sd->status.sp = 0;
+ }
+ else {
+ sd->status.sp += sd->status.max_sp*sp/100;
+ if(sd->status.sp > sd->status.max_sp)
+ sd->status.sp = sd->status.max_sp;
+ if(sd->status.sp < 0)
+ sd->status.sp = 0;
+ }
+ }
+ if(hp)
+ clif_updatestatus(sd,SP_HP);
+ if(sp)
+ clif_updatestatus(sd,SP_SP);
+
+ return 0;
+}
+
+/*==========================================
+ * 職?更
+ * 引? job 職業 0〜23
+ * upper 通常 0, ?生 1, 養子 2, そのまま -1
+ * Rewrote to make it tidider [Celest]
+ *------------------------------------------
+ */
+int pc_jobchange(struct map_session_data *sd,int job, int upper)
+{
+ int i;
+ int b_class = 0;
+ //?生や養子の場合の元の職業を算出する
+ struct pc_base_job s_class = pc_calc_base_job(sd->status.class_);
+
+ nullpo_retr(0, sd);
+
+ if (job < 0)
+ return 1;
+ if (upper < 0 || upper > 2) //現在?生かどうかを判?する
+ upper = s_class.upper;
+
+ b_class = job; //通常職ならjobそのまんま
+ if (job < JOB_SUPER_NOVICE) {
+ if (upper == 1)
+ b_class += JOB_NOVICE_HIGH;
+ else if (upper == 2) //養子に結婚はないけどどうせ次で蹴られるからいいや
+ b_class += JOB_BABY;
+ } else if (job == JOB_SUPER_NOVICE) {
+ if (upper == 1) //?生にスパノビは存在しないのでお?り
+ return 1;
+ else if (upper == 2)
+ b_class = JOB_SUPER_BABY;
+ } else if (job < JOB_SUPER_BABY-JOB_NOVICE_HIGH+JOB_SUPER_NOVICE+2) {
+ // Min is SuperNovice +1 -> Becomes Novice High [Skotlex]
+ // Max is SuperBaby-NoviceHigh+1 -> Becomes Super Baby
+ b_class += JOB_NOVICE_HIGH - JOB_SUPER_NOVICE -1;
+ } else if (job >= JOB_TAEKWON && job <= JOB_SOUL_LINKER) {
+ if (upper > 0)
+ return 1;
+ } else if (job < JOB_NOVICE_HIGH || job > JOB_SOUL_LINKER) //Invalid value
+ return 1;
+
+ job = pc_calc_base_job2 (b_class); // check base class [celest]
+
+ if((sd->status.sex == 0 && job == JOB_BARD) || (sd->status.sex == 1 && job == JOB_DANCER))
+ return 1;
+
+ // check if we are changing from 1st to 2nd job
+ if ((job >= JOB_KNIGHT && job <= JOB_CRUSADER2) || (job >= JOB_STAR_GLADIATOR && job <= JOB_SOUL_LINKER)) {
+ if ((s_class.job > JOB_NOVICE && s_class.job < JOB_KNIGHT) || s_class.job == JOB_TAEKWON)
+ sd->change_level = sd->status.job_level;
+ else
+ sd->change_level = 40;
+ }
+ else
+ sd->change_level = 0;
+
+ pc_setglobalreg (sd, "jobchange_level", sd->change_level);
+
+ sd->status.class_ = sd->view_class = b_class;
+ sd->class_ = pc_jobid2mapid(sd->status.class_);
+ sd->status.job_level=1;
+ sd->status.job_exp=0;
+ clif_updatestatus(sd,SP_JOBLEVEL);
+ clif_updatestatus(sd,SP_JOBEXP);
+ clif_updatestatus(sd,SP_NEXTJOBEXP);
+
+ for(i=0;i<11;i++) {
+ if(sd->equip_index[i] >= 0)
+ if(!pc_isequip(sd,sd->equip_index[i]))
+ pc_unequipitem(sd,sd->equip_index[i],2); // ?備外し
+ }
+
+ clif_changelook(&sd->bl,LOOK_BASE,sd->view_class); // move sprite update to prevent client crashes with incompatible equipment [Valaris]
+
+ if(battle_config.save_clothcolor &&
+ sd->status.clothes_color > 0 &&
+ ((sd->view_class != JOB_WEDDING && sd->view_class !=JOB_XMAS) || (sd->view_class==JOB_WEDDING && !battle_config.wedding_ignorepalette) ||
+ (sd->view_class==JOB_XMAS && !battle_config.xmas_ignorepalette)))
+ clif_changelook(&sd->bl,LOOK_CLOTHES_COLOR,sd->status.clothes_color);
+ if(battle_config.muting_players && sd->status.manner < 0 && battle_config.manner_system)
+ clif_changestatus(&sd->bl,SP_MANNER,sd->status.manner);
+
+ if(pc_isriding(sd)) { // remove peco status if changing into invalid class [Valaris]
+ if(!(pc_checkskill(sd,KN_RIDING)))
+ pc_setoption(sd,sd->status.option&~OPTION_RIDING);
+ else
+ pc_setriding(sd);
+ }
+
+ status_calc_pc(sd,0);
+ pc_checkallowskill(sd);
+ pc_equiplookall(sd);
+ clif_equiplist(sd);
+ chrif_save(sd,0); //Why are we saving it?
+ chrif_reqfamelist();
+
+ return 0;
+}
+
+/*==========================================
+ * 見た目?更
+ *------------------------------------------
+ */
+int pc_equiplookall(struct map_session_data *sd)
+{
+ nullpo_retr(0, sd);
+
+#if PACKETVER < 4
+ clif_changelook(&sd->bl,LOOK_WEAPON,sd->status.weapon);
+ clif_changelook(&sd->bl,LOOK_SHIELD,sd->status.shield);
+#else
+ clif_changelook(&sd->bl,LOOK_WEAPON,0);
+ clif_changelook(&sd->bl,LOOK_SHOES,0);
+#endif
+ clif_changelook(&sd->bl,LOOK_HEAD_BOTTOM,sd->status.head_bottom);
+ clif_changelook(&sd->bl,LOOK_HEAD_TOP,sd->status.head_top);
+ clif_changelook(&sd->bl,LOOK_HEAD_MID,sd->status.head_mid);
+
+ return 0;
+}
+
+/*==========================================
+ * 見た目?更
+ *------------------------------------------
+ */
+int pc_changelook(struct map_session_data *sd,int type,int val)
+{
+ nullpo_retr(0, sd);
+
+ switch(type){
+ case LOOK_HAIR: //Use the battle_config limits! [Skotlex]
+ if (val < battle_config.min_hair_style)
+ val = battle_config.min_hair_style;
+ else if (val > battle_config.max_hair_style)
+ val = battle_config.max_hair_style;
+ if (sd->status.hair != val)
+ {
+ sd->status.hair=val;
+ if (sd->status.guild_id) //Update Guild Window. [Skotlex]
+ intif_guild_change_memberinfo(sd->status.guild_id,sd->status.account_id,sd->status.char_id,
+ GMI_HAIR,&sd->status.hair,sizeof(sd->status.hair));
+ }
+ break;
+ case LOOK_WEAPON:
+ sd->status.weapon=val;
+ break;
+ case LOOK_HEAD_BOTTOM:
+ sd->status.head_bottom=val;
+ break;
+ case LOOK_HEAD_TOP:
+ sd->status.head_top=val;
+ break;
+ case LOOK_HEAD_MID:
+ sd->status.head_mid=val;
+ break;
+ case LOOK_HAIR_COLOR: //Use the battle_config limits! [Skotlex]
+ if (val < battle_config.min_hair_color)
+ val = battle_config.min_hair_color;
+ else if (val > battle_config.max_hair_color)
+ val = battle_config.max_hair_color;
+ if (sd->status.hair_color != val)
+ {
+ sd->status.hair_color=val;
+ if (sd->status.guild_id) //Update Guild Window. [Skotlex]
+ intif_guild_change_memberinfo(sd->status.guild_id,sd->status.account_id,sd->status.char_id,
+ GMI_HAIR_COLOR,&sd->status.hair_color,sizeof(sd->status.hair_color));
+ }
+ break;
+ case LOOK_CLOTHES_COLOR: //Use the battle_config limits! [Skotlex]
+ if (val < battle_config.min_cloth_color)
+ val = battle_config.min_cloth_color;
+ else if (val > battle_config.max_cloth_color)
+ val = battle_config.max_cloth_color;
+ sd->status.clothes_color=val;
+ break;
+ case LOOK_SHIELD:
+ sd->status.shield=val;
+ break;
+ case LOOK_SHOES:
+ break;
+ }
+
+ if((type==LOOK_CLOTHES_COLOR) && ((sd->view_class==JOB_WEDDING && battle_config.wedding_ignorepalette) ||
+ (sd->view_class==JOB_XMAS && battle_config.xmas_ignorepalette)))
+ return 0;
+
+ clif_changelook(&sd->bl,type,val);
+
+ return 0;
+}
+
+/*==========================================
+ * 付?品(鷹,ペコ,カ?ト)設定
+ *------------------------------------------
+ */
+int pc_setoption(struct map_session_data *sd,int type)
+{
+ nullpo_retr(0, sd);
+ if (type&OPTION_RIDING && !(sd->status.option&OPTION_RIDING) && (sd->class_&MAPID_BASEMASK) == MAPID_SWORDMAN)
+ { //We are going to mount. [Skotlex]
+ switch (sd->status.class_)
+ {
+ case JOB_KNIGHT:
+ sd->status.class_ = sd->view_class = JOB_KNIGHT2;
+ break;
+ case JOB_CRUSADER:
+ sd->status.class_ = sd->view_class = JOB_CRUSADER2;
+ break;
+ case JOB_LORD_KNIGHT:
+ sd->status.class_ = sd->view_class = JOB_LORD_KNIGHT2;
+ break;
+ case JOB_PALADIN:
+ sd->status.class_ = sd->view_class = JOB_PALADIN2;
+ break;
+ case JOB_BABY_KNIGHT:
+ sd->status.class_ = sd->view_class = JOB_BABY_KNIGHT2;
+ break;
+ case JOB_BABY_CRUSADER:
+ sd->status.class_ = sd->view_class = JOB_BABY_CRUSADER2;
+ break;
+ }
+ clif_status_load(&sd->bl,SI_RIDING,1);
+ status_calc_pc(sd,0); //Mounting/Umounting affects walk and attack speeds.
+ }
+ else if (!(type&OPTION_RIDING) && sd->status.option&OPTION_RIDING && (sd->class_&MAPID_BASEMASK) == MAPID_SWORDMAN)
+ { //We are going to dismount.
+ switch (sd->status.class_)
+ {
+ case JOB_KNIGHT2:
+ sd->status.class_ = sd->view_class = JOB_KNIGHT;
+ break;
+ case JOB_CRUSADER2:
+ sd->status.class_ = sd->view_class = JOB_CRUSADER;
+ break;
+ case JOB_LORD_KNIGHT2:
+ sd->status.class_ = sd->view_class = JOB_LORD_KNIGHT;
+ break;
+ case JOB_PALADIN2:
+ sd->status.class_ = sd->view_class = JOB_PALADIN;
+ break;
+ case JOB_BABY_KNIGHT2:
+ sd->status.class_ = sd->view_class = JOB_BABY_KNIGHT;
+ break;
+ case JOB_BABY_CRUSADER2:
+ sd->status.class_ = sd->view_class = JOB_BABY_CRUSADER;
+ break;
+ }
+ clif_status_load(&sd->bl,SI_RIDING,0);
+ status_calc_pc(sd,0); //Mounting/Umounting affects walk and attack speeds.
+ }
+ if (type&OPTION_FALCON && !(sd->status.option&OPTION_FALCON)) //Falcon ON
+ clif_status_load(&sd->bl,SI_FALCON,1);
+ else if (!(type&OPTION_FALCON) && sd->status.option&OPTION_FALCON) //Falcon OFF
+ clif_status_load(&sd->bl,SI_FALCON,0);
+
+ //SG flying [Komurka]
+ if (type&OPTION_FLYING && !(sd->status.option&OPTION_FLYING)) //Flying ON
+ {
+ if (sd->status.class_==JOB_STAR_GLADIATOR) sd->status.class_ = sd->view_class = JOB_STAR_GLADIATOR2;
+ }
+ else if (!(type&OPTION_FLYING) && sd->status.option&OPTION_FLYING) //Flying OFF
+ {
+ if (sd->status.class_==JOB_STAR_GLADIATOR2) sd->status.class_ = sd->view_class = JOB_STAR_GLADIATOR;
+ }
+
+ sd->status.option=type;
+ clif_changeoption(&sd->bl);
+ status_calc_pc(sd,0);
+ return 0;
+}
+
+/*==========================================
+ * カ?ト設定
+ *------------------------------------------
+ */
+int pc_setcart(struct map_session_data *sd,int type)
+{
+ int cart[6]={0x0000,0x0008,0x0080,0x0100,0x0200,0x0400};
+ int option, i;
+ nullpo_retr(0, sd);
+
+ if (type < 0 || type > 5)
+ return 0; //Never trust the values sent by the client! [Skotlex]
+
+ option = sd->status.option;
+ for (i = 0; i < 6; i++)
+ { //This should preserve the current option, only modifying the cart bit.
+ if (i == type)
+ option |= cart[i];
+ else
+ option &= ~cart[i];
+ }
+ if(pc_checkskill(sd,MC_PUSHCART)>0){ // プッシュカ?トスキル所持
+ if(!pc_iscarton(sd)){ // カ?トを付けていない
+ pc_setoption(sd,option);
+ clif_cart_itemlist(sd);
+ clif_cart_equiplist(sd);
+ clif_updatestatus(sd,SP_CARTINFO);
+ clif_status_change(&sd->bl,SI_INCREASEAGI,0); //0x0c is 12, Increase Agi??
+ }
+ else{
+ pc_setoption(sd,option);
+ }
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * 鷹設定
+ *------------------------------------------
+ */
+int pc_setfalcon(struct map_session_data *sd)
+{
+ if(pc_checkskill(sd,HT_FALCON)>0){ // ファルコンマスタリ?スキル所持
+ pc_setoption(sd,sd->status.option|0x0010);
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * ペコペコ設定
+ *------------------------------------------
+ */
+int pc_setriding(struct map_session_data *sd)
+{
+ if((pc_checkskill(sd,KN_RIDING)>0)){ // ライディングスキル所持
+ pc_setoption(sd,sd->status.option|OPTION_RIDING);
+ }
+ return 0;
+}
+
+/*==========================================
+ * アイテムドロップ可不可判定
+ *------------------------------------------
+ */
+int pc_candrop(struct map_session_data *sd,int item_id)
+{
+ int level = pc_isGM(sd);
+ if ( pc_can_give_items(level) ) //check if this GM level can drop items
+ return 0;
+ return (itemdb_isdropable(item_id, level));
+}
+
+/*==========================================
+ * script用??の値を?む
+ *------------------------------------------
+ */
+int pc_readreg(struct map_session_data *sd,int reg)
+{
+ int i;
+
+ nullpo_retr(0, sd);
+
+ for(i=0;i<sd->reg_num;i++)
+ if(sd->reg[i].index==reg)
+ return sd->reg[i].data;
+
+ return 0;
+}
+/*==========================================
+ * script用??の値を設定
+ *------------------------------------------
+ */
+int pc_setreg(struct map_session_data *sd,int reg,int val)
+{
+ int i;
+
+ nullpo_retr(0, sd);
+
+ for (i = 0; i < sd->reg_num; i++) {
+ if (sd->reg[i].index == reg){
+ sd->reg[i].data = val;
+ return 0;
+ }
+ }
+ sd->reg_num++;
+ sd->reg = (struct script_reg *) aRealloc(sd->reg, sizeof(*(sd->reg)) * sd->reg_num);
+ memset(sd->reg + (sd->reg_num - 1), 0, sizeof(struct script_reg));
+ sd->reg[i].index = reg;
+ sd->reg[i].data = val;
+
+ return 0;
+}
+
+/*==========================================
+ * script用文字列??の値を?む
+ *------------------------------------------
+ */
+char *pc_readregstr(struct map_session_data *sd,int reg)
+{
+ int i;
+
+ nullpo_retr(0, sd);
+
+ for(i=0;i<sd->regstr_num;i++)
+ if(sd->regstr[i].index==reg)
+ return sd->regstr[i].data;
+
+ return NULL;
+}
+/*==========================================
+ * script用文字列??の値を設定
+ *------------------------------------------
+ */
+int pc_setregstr(struct map_session_data *sd,int reg,char *str)
+{
+ int i;
+
+ nullpo_retr(0, sd);
+
+ if(strlen(str)+1 >= sizeof(sd->regstr[0].data)){
+ ShowWarning("pc_setregstr: string too long !\n");
+ return 0;
+ }
+
+ for(i=0;i<sd->regstr_num;i++)
+ if(sd->regstr[i].index==reg){
+ strcpy(sd->regstr[i].data,str);
+ return 0;
+ }
+
+ sd->regstr_num++;
+ sd->regstr = (struct script_regstr *) aRealloc(sd->regstr, sizeof(sd->regstr[0]) * sd->regstr_num);
+ if(sd->regstr==NULL){
+ ShowFatalError("out of memory : pc_setreg\n");
+ exit(1);
+ }
+ memset(sd->regstr + (sd->regstr_num - 1), 0, sizeof(struct script_regstr));
+ sd->regstr[i].index = reg;
+ strcpy(sd->regstr[i].data, str);
+
+ return 0;
+}
+
+int pc_readregistry(struct map_session_data *sd,char *reg,int type) {
+ struct global_reg *sd_reg;
+ int i,max;
+
+ nullpo_retr(0, sd);
+ switch (type) {
+ case 3: //Char reg
+ sd_reg = sd->save_reg.global;
+ max = sd->save_reg.global_num;
+ break;
+ case 2: //Account reg
+ sd_reg = sd->save_reg.account;
+ max = sd->save_reg.account_num;
+ break;
+ case 1: //Account2 reg
+ sd_reg = sd->save_reg.account2;
+ max = sd->save_reg.account2_num;
+ break;
+ default:
+ return 0;
+ }
+ if (max == -1) {
+ if (battle_config.error_log)
+ ShowError("pc_readregistry: Trying to read reg value %s (type %d) before it's been loaded!\n", reg, type);
+ //This really shouldn't happen, so it's possible the data was lost somewhere, we should request it again.
+ intif_request_registry(sd,type==3?4:type);
+ return 0;
+ }
+ for(i=0;i<max;i++){
+ if(strcmp(sd_reg[i].str,reg)==0)
+ return atoi(sd_reg[i].value);
+ }
+ return 0;
+}
+
+char* pc_readregistry_str(struct map_session_data *sd,char *reg,int type) {
+ struct global_reg *sd_reg;
+ int i,max;
+
+ nullpo_retr(0, sd);
+ switch (type) {
+ case 3: //Char reg
+ sd_reg = sd->save_reg.global;
+ max = sd->save_reg.global_num;
+ break;
+ case 2: //Account reg
+ sd_reg = sd->save_reg.account;
+ max = sd->save_reg.account_num;
+ break;
+ case 1: //Account2 reg
+ sd_reg = sd->save_reg.account2;
+ max = sd->save_reg.account2_num;
+ break;
+ default:
+ return NULL;
+ }
+ if (max == -1) {
+ if (battle_config.error_log)
+ ShowError("pc_readregistry: Trying to read reg value %s (type %d) before it's been loaded!\n", reg, type);
+ //This really shouldn't happen, so it's possible the data was lost somewhere, we should request it again.
+ intif_request_registry(sd,type==3?4:type);
+ return NULL;
+ }
+ for(i=0;i<max;i++){
+ if(strcmp(sd_reg[i].str,reg)==0)
+ return sd_reg[i].value;
+ }
+ return NULL;
+}
+
+int pc_setregistry(struct map_session_data *sd,char *reg,int val,int type) {
+ struct global_reg *sd_reg;
+ int i,*max, regmax;
+
+ nullpo_retr(0, sd);
+ if (type == 3) { //Some special character reg values...
+ if(strcmp(reg,"PC_DIE_COUNTER") == 0 && sd->die_counter != val){
+ sd->die_counter = val;
+ // status_calc_pc(sd,0); //I doubt this is needed....
+ } else if(strcmp(reg,script_config.die_event_name) == 0){
+ sd->state.event_death = val;
+ } else if(strcmp(reg,script_config.kill_event_name) == 0){
+ sd->state.event_kill = val;
+ } else if(strcmp(reg,script_config.logout_event_name) == 0){
+ sd->state.event_disconnect = val;
+ }
+ }
+ switch (type) {
+ case 3: //Char reg
+ sd_reg = sd->save_reg.global;
+ max = &sd->save_reg.global_num;
+ regmax = GLOBAL_REG_NUM;
+ break;
+ case 2: //Account reg
+ sd_reg = sd->save_reg.account;
+ max = &sd->save_reg.account_num;
+ regmax = ACCOUNT_REG_NUM;
+ break;
+ case 1: //Account2 reg
+ sd_reg = sd->save_reg.account2;
+ max = &sd->save_reg.account2_num;
+ regmax = ACCOUNT_REG2_NUM;
+ break;
+ default:
+ return 0;
+ }
+ if (*max == -1) {
+ if(battle_config.error_log)
+ ShowError("pc_setregistry : refusing to set %s (type %d) until vars are received.\n", reg, type);
+ return 1;
+ }
+
+ // delete reg
+ if (val == 0) {
+ for(i = 0; i < *max; i++) {
+ if (strcmp(sd_reg[i].str, reg) == 0) {
+ if (i != *max - 1)
+ memcpy(&sd_reg[i], &sd_reg[*max - 1], sizeof(struct global_reg));
+ memset(&sd_reg[*max - 1], 0, sizeof(struct global_reg));
+ (*max)--;
+ sd->state.reg_dirty |= 1<<(type-1); //Mark this registry as "need to be saved"
+ break;
+ }
+ }
+ return 0;
+ }
+ // change value if found
+ for(i = 0; i < *max; i++) {
+ if (strcmp(sd_reg[i].str, reg) == 0) {
+ sprintf(sd_reg[i].value, "%d", val);
+ sd->state.reg_dirty |= 1<<(type-1);
+ return 0;
+ }
+ }
+
+ // add value if not found
+ if (i < regmax) {
+ memset(&sd_reg[i], 0, sizeof(struct global_reg));
+ strncpy(sd_reg[i].str, reg, 32);
+ sprintf(sd_reg[i].value, "%d", val);
+ (*max)++;
+ sd->state.reg_dirty |= 1<<(type-1);
+ return 0;
+ }
+
+ if(battle_config.error_log)
+ ShowError("pc_setregistry : couldn't set %s, limit of registries reached (%d)\n", reg, regmax);
+
+ return 1;
+}
+
+int pc_setregistry_str(struct map_session_data *sd,char *reg,char *val,int type) {
+ struct global_reg *sd_reg;
+ int i,*max, regmax;
+
+ nullpo_retr(0, sd);
+ if (reg[strlen(reg)-1] != '$') {
+ if(battle_config.error_log)
+ ShowError("pc_setregistry_str : reg %s must be string (end in '$') to use this!\n", reg);
+ return 1;
+ }
+
+ switch (type) {
+ case 3: //Char reg
+ sd_reg = sd->save_reg.global;
+ max = &sd->save_reg.global_num;
+ regmax = GLOBAL_REG_NUM;
+ break;
+ case 2: //Account reg
+ sd_reg = sd->save_reg.account;
+ max = &sd->save_reg.account_num;
+ regmax = ACCOUNT_REG_NUM;
+ break;
+ case 1: //Account2 reg
+ sd_reg = sd->save_reg.account2;
+ max = &sd->save_reg.account2_num;
+ regmax = ACCOUNT_REG2_NUM;
+ break;
+ default:
+ return 0;
+ }
+ if (*max == -1) {
+ if(battle_config.error_log)
+ ShowError("pc_setregistry_str : refusing to set %s (type %d) until vars are received.\n", reg, type);
+ return 1;
+ }
+
+ // delete reg
+ if (strcmp(val,"")==0) {
+ for(i = 0; i < *max; i++) {
+ if (strcmp(sd_reg[i].str, reg) == 0) {
+ if (i != *max - 1)
+ memcpy(&sd_reg[i], &sd_reg[*max - 1], sizeof(struct global_reg));
+ memset(&sd_reg[*max - 1], 0, sizeof(struct global_reg));
+ (*max)--;
+ sd->state.reg_dirty |= 1<<(type-1); //Mark this registry as "need to be saved"
+ if (type!=3) intif_saveregistry(sd,type);
+ break;
+ }
+ }
+ return 0;
+ }
+ // change value if found
+ for(i = 0; i < *max; i++) {
+ if (strcmp(sd_reg[i].str, reg) == 0) {
+ strncpy(sd_reg[i].value, val, 256);
+ sd->state.reg_dirty |= 1<<(type-1); //Mark this registry as "need to be saved"
+ if (type!=3) intif_saveregistry(sd,type);
+ return 0;
+ }
+ }
+
+ // add value if not found
+ if (i < regmax) {
+ memset(&sd_reg[i], 0, sizeof(struct global_reg));
+ strncpy(sd_reg[i].str, reg, 32);
+ strncpy(sd_reg[i].value, val, 256);
+ (*max)++;
+ sd->state.reg_dirty |= 1<<(type-1); //Mark this registry as "need to be saved"
+ if (type!=3) intif_saveregistry(sd,type);
+ return 0;
+ }
+
+ if(battle_config.error_log)
+ ShowError("pc_setregistry : couldn't set %s, limit of registries reached (%d)\n", reg, regmax);
+
+ return 1;
+}
+
+/*==========================================
+ * イベントタイマ??理
+ *------------------------------------------
+ */
+int pc_eventtimer(int tid,unsigned int tick,int id,int data)
+{
+ struct map_session_data *sd=map_id2sd(id);
+ char *p = (char *)data;
+ int i;
+ if(sd==NULL)
+ return 0;
+
+ for(i=0;i < MAX_EVENTTIMER;i++){
+ if( sd->eventtimer[i]==tid ){
+ sd->eventtimer[i]=-1;
+ npc_event(sd,p,0);
+ break;
+ }
+ }
+ if (p) aFree(p);
+ if(i==MAX_EVENTTIMER) {
+ if(battle_config.error_log)
+ ShowError("pc_eventtimer: no such event timer\n");
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * イベントタイマ?追加
+ *------------------------------------------
+ */
+int pc_addeventtimer(struct map_session_data *sd,int tick,const char *name)
+{
+ int i;
+
+ nullpo_retr(0, sd);
+
+ for(i=0;i<MAX_EVENTTIMER;i++)
+ if( sd->eventtimer[i]==-1 )
+ break;
+ if(i<MAX_EVENTTIMER){
+ char *evname = aStrdup(name);
+ //char *evname=(char *)aMallocA((strlen(name)+1)*sizeof(char));
+ //memcpy(evname,name,(strlen(name)+1));
+ sd->eventtimer[i]=add_timer(gettick()+tick,
+ pc_eventtimer,sd->bl.id,(int)evname);
+ sd->eventcount++;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * イベントタイマ?削除
+ *------------------------------------------
+ */
+int pc_deleventtimer(struct map_session_data *sd,const char *name)
+{
+ int i;
+
+ nullpo_retr(0, sd);
+
+ if (sd->eventcount <= 0)
+ return 0;
+
+ for(i=0;i<MAX_EVENTTIMER;i++)
+ if( sd->eventtimer[i]!=-1 ) {
+ char *p = (char *)(get_timer(sd->eventtimer[i])->data);
+ if(p && strcmp(p, name)==0) {
+ delete_timer(sd->eventtimer[i],pc_eventtimer);
+ sd->eventtimer[i]=-1;
+ sd->eventcount--;
+ aFree(p);
+ break;
+ }
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * イベントタイマ?カウント値追加
+ *------------------------------------------
+ */
+int pc_addeventtimercount(struct map_session_data *sd,const char *name,int tick)
+{
+ int i;
+
+ nullpo_retr(0, sd);
+
+ for(i=0;i<MAX_EVENTTIMER;i++)
+ if( sd->eventtimer[i]!=-1 && strcmp(
+ (char *)(get_timer(sd->eventtimer[i])->data), name)==0 ){
+ addtick_timer(sd->eventtimer[i],tick);
+ break;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * イベントタイマ?全削除
+ *------------------------------------------
+ */
+int pc_cleareventtimer(struct map_session_data *sd)
+{
+ int i;
+
+ nullpo_retr(0, sd);
+
+ if (sd->eventcount <= 0)
+ return 0;
+
+ for(i=0;i<MAX_EVENTTIMER;i++)
+ if( sd->eventtimer[i]!=-1 ){
+ char *p = (char *)(get_timer(sd->eventtimer[i])->data);
+ delete_timer(sd->eventtimer[i],pc_eventtimer);
+ sd->eventtimer[i]=-1;
+ if (p) aFree(p);
+ }
+
+ return 0;
+}
+
+//
+// ? 備物
+//
+/*==========================================
+ * アイテムを?備する
+ *------------------------------------------
+ */
+int pc_equipitem(struct map_session_data *sd,int n,int pos)
+{
+ int i,nameid, arrow;
+ struct item_data *id;
+ //?生や養子の場合の元の職業を算出する
+
+ nullpo_retr(0, sd);
+
+ nameid = sd->status.inventory[n].nameid;
+ id = sd->inventory_data[n];
+ pos = pc_equippoint(sd,n);
+ if(battle_config.battle_log)
+ ShowInfo("equip %d(%d) %x:%x\n",nameid,n,id->equip,pos);
+ if(!pc_isequip(sd,n) || !pos || sd->status.inventory[n].attribute==1 ) { // [Valaris]
+ clif_equipitemack(sd,n,0,0); // fail
+ return 0;
+ }
+
+// -- moonsoul (if player is berserk then cannot equip)
+//
+ if(sd->sc_count && sd->sc_data[SC_BERSERK].timer!=-1){
+ clif_equipitemack(sd,n,0,0); // fail
+ return 0;
+ }
+
+ if(pos==0x88){ // アクセサリ用例外?理
+ int epor=0;
+ if(sd->equip_index[0] >= 0)
+ epor |= sd->status.inventory[sd->equip_index[0]].equip;
+ if(sd->equip_index[1] >= 0)
+ epor |= sd->status.inventory[sd->equip_index[1]].equip;
+ epor &= 0x88;
+ pos = epor == 0x08 ? 0x80 : 0x08;
+ }
+
+ // 二刀流?理
+ if ((pos==0x22) // 一?、?備要求箇所が二刀流武器かチェックする
+ && (id->equip==2) // ? 手武器
+ && (pc_checkskill(sd, AS_LEFT) > 0 || (sd->class_&MAPID_UPPERMASK) == MAPID_ASSASSIN) ) // 左手修?有
+ {
+ int tpos=0;
+ if(sd->equip_index[8] >= 0)
+ tpos |= sd->status.inventory[sd->equip_index[8]].equip;
+ if(sd->equip_index[9] >= 0)
+ tpos |= sd->status.inventory[sd->equip_index[9]].equip;
+ tpos &= 0x02;
+ pos = tpos == 0x02 ? 0x20 : 0x02;
+ }
+
+ arrow=pc_search_inventory(sd,pc_checkequip(sd,9)); // Added by RoVeRT
+ for(i=0;i<11;i++) {
+ if(sd->equip_index[i] >= 0 && sd->status.inventory[sd->equip_index[i]].equip&pos) {
+ pc_unequipitem(sd,sd->equip_index[i],2);
+ }
+ }
+ // 弓矢?備
+ if(pos==0x8000){
+ clif_arrowequip(sd,n);
+ clif_arrow_fail(sd,3); // 3=矢が?備できました
+ }
+ else
+ clif_equipitemack(sd,n,pos,1);
+
+ for(i=0;i<11;i++) {
+ if(pos & equip_pos[i])
+ sd->equip_index[i] = n;
+ }
+ sd->status.inventory[n].equip=pos;
+
+ if(sd->status.inventory[n].equip & 0x0002) {
+ if(sd->inventory_data[n])
+ sd->weapontype1 = sd->inventory_data[n]->look;
+ else
+ sd->weapontype1 = 0;
+ pc_calcweapontype(sd);
+ clif_changelook(&sd->bl,LOOK_WEAPON,sd->status.weapon);
+ }
+ if(sd->status.inventory[n].equip & 0x0020) {
+ if(sd->inventory_data[n]) {
+ if(sd->inventory_data[n]->type == 4) {
+ sd->status.shield = 0;
+ if(sd->status.inventory[n].equip == 0x0020)
+ sd->weapontype2 = sd->inventory_data[n]->look;
+ else
+ sd->weapontype2 = 0;
+ }
+ else if(sd->inventory_data[n]->type == 5) {
+ sd->status.shield = sd->inventory_data[n]->look;
+ sd->weapontype2 = 0;
+ }
+ }
+ else
+ sd->status.shield = sd->weapontype2 = 0;
+ pc_calcweapontype(sd);
+ clif_changelook(&sd->bl,LOOK_SHIELD,sd->status.shield);
+ }
+ if(sd->status.inventory[n].equip & 0x0001) {
+ if(sd->inventory_data[n])
+ sd->status.head_bottom = sd->inventory_data[n]->look;
+ else
+ sd->status.head_bottom = 0;
+ clif_changelook(&sd->bl,LOOK_HEAD_BOTTOM,sd->status.head_bottom);
+ }
+ if(sd->status.inventory[n].equip & 0x0100) {
+ if(sd->inventory_data[n])
+ sd->status.head_top = sd->inventory_data[n]->look;
+ else
+ sd->status.head_top = 0;
+ clif_changelook(&sd->bl,LOOK_HEAD_TOP,sd->status.head_top);
+ }
+ if(sd->status.inventory[n].equip & 0x0200) {
+ if(sd->inventory_data[n])
+ sd->status.head_mid = sd->inventory_data[n]->look;
+ else
+ sd->status.head_mid = 0;
+ clif_changelook(&sd->bl,LOOK_HEAD_MID,sd->status.head_mid);
+ }
+ if(sd->status.inventory[n].equip & 0x0040)
+ clif_changelook(&sd->bl,LOOK_SHOES,0);
+
+ pc_checkallowskill(sd); // ?備品でスキルか解除されるかチェック
+ if (itemdb_look(sd->status.inventory[n].nameid) == 11 && (arrow >= 0)){ // Added by RoVeRT
+ clif_arrowequip(sd,arrow);
+ sd->status.inventory[arrow].equip=32768;
+ }
+ status_calc_pc(sd,0);
+
+ if(sd->special_state.infinite_endure) {
+ if(sd->sc_data[SC_ENDURE].timer == -1)
+ status_change_start(&sd->bl,SC_ENDURE,10,1,0,0,0,0);
+ }
+ else {
+ if(sd->sc_count && sd->sc_data[SC_ENDURE].timer != -1 && sd->sc_data[SC_ENDURE].val2)
+ status_change_end(&sd->bl,SC_ENDURE,-1);
+ }
+
+ if(sd->sc_count) {
+ if (sd->sc_data[SC_SIGNUMCRUCIS].timer != -1 && !battle_check_undead(7,sd->def_ele))
+ status_change_end(&sd->bl,SC_SIGNUMCRUCIS,-1);
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * ? 備した物を外す
+ * type:
+ * 0 - only unequip
+ * 1 - calculate status after unequipping
+ * 2 - force unequip
+ *------------------------------------------
+ */
+int pc_unequipitem(struct map_session_data *sd,int n,int flag)
+{
+ short hp = 0, sp = 0;
+ nullpo_retr(0, sd);
+
+// -- moonsoul (if player is berserk then cannot unequip)
+//
+ if(!(flag&2) && sd->sc_count && (sd->sc_data[SC_BLADESTOP].timer!=-1 || sd->sc_data[SC_BERSERK].timer!=-1)){
+ clif_unequipitemack(sd,n,0,0);
+ return 0;
+ }
+
+ if(battle_config.battle_log)
+ ShowInfo("unequip %d %x:%x\n",n,pc_equippoint(sd,n),sd->status.inventory[n].equip);
+ if(sd->status.inventory[n].equip){
+ int i;
+ for(i=0;i<11;i++) {
+ if(sd->status.inventory[n].equip & equip_pos[i]) {
+ sd->equip_index[i] = -1;
+ if(sd->unequip_losehp[i] > 0) {
+ hp += sd->unequip_losehp[i];
+ sd->unequip_losehp[i] = 0;
+ }
+ if(sd->unequip_losesp[i] > 0) {
+ sp += sd->unequip_losesp[i];
+ sd->unequip_losesp[i] = 0;
+ }
+ }
+ }
+ if(sd->status.inventory[n].equip & 0x0002) {
+ sd->weapontype1 = 0;
+ sd->status.weapon = sd->weapontype2;
+ pc_calcweapontype(sd);
+ clif_changelook(&sd->bl,LOOK_WEAPON,sd->status.weapon);
+ if(sd->sc_data[SC_DANCING].timer!=-1) //When unequipping, stop dancing. [Skotlex]
+ skill_stop_dancing(&sd->bl);
+ }
+ if(sd->status.inventory[n].equip & 0x0020) {
+ sd->status.shield = sd->weapontype2 = 0;
+ pc_calcweapontype(sd);
+ clif_changelook(&sd->bl,LOOK_SHIELD,sd->status.shield);
+ }
+ if(sd->status.inventory[n].equip & 0x0001) {
+ sd->status.head_bottom = 0;
+ clif_changelook(&sd->bl,LOOK_HEAD_BOTTOM,sd->status.head_bottom);
+ }
+ if(sd->status.inventory[n].equip & 0x0100) {
+ sd->status.head_top = 0;
+ clif_changelook(&sd->bl,LOOK_HEAD_TOP,sd->status.head_top);
+ }
+ if(sd->status.inventory[n].equip & 0x0200) {
+ sd->status.head_mid = 0;
+ clif_changelook(&sd->bl,LOOK_HEAD_MID,sd->status.head_mid);
+ }
+ if(sd->status.inventory[n].equip & 0x0040)
+ clif_changelook(&sd->bl,LOOK_SHOES,0);
+
+ clif_unequipitemack(sd,n,sd->status.inventory[n].equip,1);
+ sd->status.inventory[n].equip=0;
+ if(flag&1)
+ pc_checkallowskill(sd);
+ if(sd->weapontype1 == 0 && sd->weapontype2 == 0)
+ skill_enchant_elemental_end(&sd->bl,-1); //武器持ち誓えは無?件で?性付?解除
+ } else {
+ clif_unequipitemack(sd,n,0,0);
+ }
+
+ if(flag&1) {
+ status_calc_pc(sd,0);
+ if(sd->sc_count && sd->sc_data[SC_SIGNUMCRUCIS].timer != -1 && !battle_check_undead(7,sd->def_ele))
+ status_change_end(&sd->bl,SC_SIGNUMCRUCIS,-1);
+ }
+
+ if (hp > 0 || sp > 0) {
+ if (hp > sd->status.hp)
+ hp = sd->status.hp;
+ if (sp > sd->status.sp)
+ sp = sd->status.sp;
+ pc_heal(sd,-hp,-sp);
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * アイテムのindex番?を詰めたり
+ * ? 備品の?備可能チェックを行なう
+ *------------------------------------------
+ */
+int pc_checkitem(struct map_session_data *sd)
+{
+ int i,j,k,id,calc_flag = 0;
+ struct item_data *it=NULL;
+
+ nullpo_retr(0, sd);
+
+ if (sd->vender_id) //Avoid reorganizing items when we are vending, as that leads to exploits (pointed out by End of Exam)
+ return 0;
+
+ // 所持品空き詰め
+ for(i=j=0;i<MAX_INVENTORY;i++){
+ if( (id=sd->status.inventory[i].nameid)==0)
+ continue;
+ if( battle_config.item_check && !itemdb_available(id) ){
+ if(battle_config.error_log)
+ ShowWarning("illegal item id %d in %d[%s] inventory.\n",id,sd->bl.id,sd->status.name);
+ pc_delitem(sd,i,sd->status.inventory[i].amount,3);
+ continue;
+ }
+ if(i>j){
+ memcpy(&sd->status.inventory[j],&sd->status.inventory[i],sizeof(struct item));
+ sd->inventory_data[j] = sd->inventory_data[i];
+ }
+ j++;
+ }
+ if(j < MAX_INVENTORY)
+ memset(&sd->status.inventory[j],0,sizeof(struct item)*(MAX_INVENTORY-j));
+ for(k=j;k<MAX_INVENTORY;k++)
+ sd->inventory_data[k] = NULL;
+
+ // カ?ト?空き詰め
+ for(i=j=0;i<MAX_CART;i++){
+ if( (id=sd->status.cart[i].nameid)==0 )
+ continue;
+ if( battle_config.item_check && !itemdb_available(id) ){
+ if(battle_config.error_log)
+ ShowWarning("illeagal item id %d in %d[%s] cart.\n",id,sd->bl.id,sd->status.name);
+ pc_cart_delitem(sd,i,sd->status.cart[i].amount,1);
+ continue;
+ }
+ if(i>j){
+ memcpy(&sd->status.cart[j],&sd->status.cart[i],sizeof(struct item));
+ }
+ j++;
+ }
+ if(j < MAX_CART)
+ memset(&sd->status.cart[j],0,sizeof(struct item)*(MAX_CART-j));
+
+ // ? 備位置チェック
+
+ for(i=0;i<MAX_INVENTORY;i++){
+
+ it=sd->inventory_data[i];
+
+ if(sd->status.inventory[i].nameid==0)
+ continue;
+ if(sd->status.inventory[i].equip & ~pc_equippoint(sd,i)) {
+ sd->status.inventory[i].equip=0;
+ calc_flag = 1;
+ }
+ //?備制限チェック
+ if(sd->status.inventory[i].equip && (map[sd->bl.m].flag.pvp||map[sd->bl.m].flag.gvg) && (it->flag.no_equip&1)){//PVP check for forbiden items. optimized by [Lupus]
+ sd->status.inventory[i].equip=0;
+ calc_flag = 1;
+ }else if(sd->status.inventory[i].equip && map_flag_gvg(sd->bl.m) && (it->flag.no_equip>1)){//GvG optimized by [Lupus]
+ sd->status.inventory[i].equip=0;
+ calc_flag = 1;
+ }
+ }
+
+ pc_setequipindex(sd);
+ if(calc_flag)
+ status_calc_pc(sd,2);
+
+ return 0;
+}
+
+int pc_checkoverhp(struct map_session_data *sd)
+{
+ nullpo_retr(0, sd);
+
+ if(sd->status.hp == sd->status.max_hp)
+ return 1;
+ if(sd->status.hp > sd->status.max_hp) {
+ sd->status.hp = sd->status.max_hp;
+ clif_updatestatus(sd,SP_HP);
+ return 2;
+ }
+
+ return 0;
+}
+
+int pc_checkoversp(struct map_session_data *sd)
+{
+ nullpo_retr(0, sd);
+
+ if(sd->status.sp == sd->status.max_sp)
+ return 1;
+ if(sd->status.sp > sd->status.max_sp) {
+ sd->status.sp = sd->status.max_sp;
+ clif_updatestatus(sd,SP_SP);
+ return 2;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * PVP順位計算用(foreachinarea)
+ *------------------------------------------
+ */
+int pc_calc_pvprank_sub(struct block_list *bl,va_list ap)
+{
+ struct map_session_data *sd1,*sd2=NULL;
+
+ nullpo_retr(0, bl);
+ nullpo_retr(0, ap);
+ nullpo_retr(0, sd1=(struct map_session_data *)bl);
+ nullpo_retr(0, sd2=va_arg(ap,struct map_session_data *));
+
+ if( sd1->pvp_point > sd2->pvp_point )
+ sd2->pvp_rank++;
+ return 0;
+}
+/*==========================================
+ * PVP順位計算
+ *------------------------------------------
+ */
+int pc_calc_pvprank(struct map_session_data *sd)
+{
+ int old;
+ struct map_data *m;
+
+ nullpo_retr(0, sd);
+ nullpo_retr(0, m=&map[sd->bl.m]);
+
+ old=sd->pvp_rank;
+
+ if( !(m->flag.pvp) )
+ return 0;
+ sd->pvp_rank=1;
+ map_foreachinmap(pc_calc_pvprank_sub,sd->bl.m,BL_PC,sd);
+ if(old!=sd->pvp_rank || sd->pvp_lastusers!=m->users)
+ clif_pvpset(sd,sd->pvp_rank,sd->pvp_lastusers=m->users,0);
+ return sd->pvp_rank;
+}
+/*==========================================
+ * PVP順位計算(timer)
+ *------------------------------------------
+ */
+int pc_calc_pvprank_timer(int tid,unsigned int tick,int id,int data)
+{
+ struct map_session_data *sd=NULL;
+ if(battle_config.pk_mode) // disable pvp ranking if pk_mode on [Valaris]
+ return 0;
+
+ sd=map_id2sd(id);
+ if(sd==NULL)
+ return 0;
+ sd->pvp_timer=-1;
+ if( pc_calc_pvprank(sd)>0 )
+ sd->pvp_timer=add_timer(
+ gettick()+PVP_CALCRANK_INTERVAL,
+ pc_calc_pvprank_timer,id,data);
+ return 0;
+}
+
+/*==========================================
+ * sdは結婚しているか(?婚の場合は相方のchar_idを返す)
+ *------------------------------------------
+ */
+int pc_ismarried(struct map_session_data *sd)
+{
+ if(sd == NULL)
+ return -1;
+ if(sd->status.partner_id > 0)
+ return sd->status.partner_id;
+ else
+ return 0;
+}
+/*==========================================
+ * sdがdstsdと結婚(dstsd→sdの結婚?理も同暫ノ行う)
+ *------------------------------------------
+ */
+int pc_marriage(struct map_session_data *sd,struct map_session_data *dstsd)
+{
+ if(sd == NULL || dstsd == NULL ||
+ sd->status.partner_id > 0 || dstsd->status.partner_id > 0 ||
+ sd->class_&JOBL_BABY)
+ return -1;
+ sd->status.partner_id = dstsd->status.char_id;
+ dstsd->status.partner_id = sd->status.char_id;
+ return 0;
+}
+
+/*==========================================
+ * sdが離婚(相手はsd->status.partner_idに依る)(相手も同暫ノ離婚?結婚指輪自動?奪)
+ *------------------------------------------
+ */
+int pc_divorce(struct map_session_data *sd)
+{
+ struct map_session_data *p_sd;
+ if (sd == NULL || !pc_ismarried(sd))
+ return -1;
+
+ if ((p_sd = map_charid2sd(sd->status.partner_id)) != NULL) {
+ int i;
+ if (p_sd->status.partner_id != sd->status.char_id || sd->status.partner_id != p_sd->status.char_id) {
+ ShowWarning("pc_divorce: Illegal partner_id sd=%d p_sd=%d\n", sd->status.partner_id, p_sd->status.partner_id);
+ return -1;
+ }
+ sd->status.partner_id = 0;
+ p_sd->status.partner_id = 0;
+ for (i = 0; i < MAX_INVENTORY; i++) {
+ if (sd->status.inventory[i].nameid == WEDDING_RING_M || sd->status.inventory[i].nameid == WEDDING_RING_F)
+ pc_delitem(sd, i, 1, 0);
+ if (p_sd->status.inventory[i].nameid == WEDDING_RING_M || p_sd->status.inventory[i].nameid == WEDDING_RING_F)
+ pc_delitem(p_sd, i, 1, 0);
+ }
+ clif_divorced(sd, p_sd->status.name);
+ clif_divorced(p_sd, sd->status.name);
+ } else {
+ ShowError("pc_divorce: p_sd nullpo\n");
+ return -1;
+ }
+ return 0;
+}
+
+/*==========================================
+ * sd - father dstsd - mother jasd - child
+ */
+int pc_adoption(struct map_session_data *sd,struct map_session_data *dstsd, struct map_session_data *jasd)
+{
+ int j;
+ if (sd == NULL || dstsd == NULL || jasd == NULL ||
+ sd->status.partner_id <= 0 || dstsd->status.partner_id <= 0 ||
+ sd->status.partner_id != dstsd->status.char_id || dstsd->status.partner_id != sd->status.char_id ||
+ sd->status.child > 0 || dstsd->status.child || jasd->status.father > 0 || jasd->status.mother > 0)
+ return -1;
+ jasd->status.father = sd->status.char_id;
+ jasd->status.mother = dstsd->status.char_id;
+ sd->status.child = jasd->status.char_id;
+ dstsd->status.child = jasd->status.char_id;
+
+ for (j=0; j < MAX_INVENTORY; j++) {
+ if(jasd->status.inventory[j].nameid>0 && jasd->status.inventory[j].equip!=0)
+ pc_unequipitem(jasd, j, 3);
+ }
+ if (pc_jobchange(jasd, 4023, 0) == 0)
+ { //Success, and give Junior the Baby skills. [Skotlex]
+ pc_skill(jasd,WE_BABY,1,0);
+ pc_skill(jasd,WE_CALLPARENT,1,0);
+ clif_displaymessage(jasd->fd, msg_txt(12)); // Your job has been changed.
+ //We should also grant the parent skills to the parents [Skotlex]
+ pc_skill(sd,WE_CALLBABY,1,0);
+ pc_skill(dstsd,WE_CALLBABY,1,0);
+ } else {
+ clif_displaymessage(jasd->fd, msg_txt(155)); // Impossible to change your job.
+ return -1;
+ }
+ return 0;
+}
+
+/*==========================================
+ * sdの相方のmap_session_dataを返す
+ *------------------------------------------
+ */
+struct map_session_data *pc_get_partner(struct map_session_data *sd)
+{
+ //struct map_session_data *p_sd = NULL;
+ //char *nick;
+ //if(sd == NULL || !pc_ismarried(sd))
+ // return NULL;
+ //nick=map_charid2nick(sd->status.partner_id);
+ //if (nick==NULL)
+ // return NULL;
+ //if((p_sd=map_nick2sd(nick)) == NULL )
+ // return NULL;
+
+ if (sd && pc_ismarried(sd))
+ // charid2sd returns NULL if not found
+ return map_charid2sd(sd->status.partner_id);
+
+ return NULL;
+}
+
+struct map_session_data *pc_get_father (struct map_session_data *sd)
+{
+ if (sd && sd->class_&JOBL_BABY && sd->status.father > 0)
+ // charid2sd returns NULL if not found
+ return map_charid2sd(sd->status.father);
+
+ return NULL;
+}
+
+struct map_session_data *pc_get_mother (struct map_session_data *sd)
+{
+ if (sd && sd->class_&JOBL_BABY && sd->status.mother > 0)
+ // charid2sd returns NULL if not found
+ return map_charid2sd(sd->status.mother);
+
+ return NULL;
+}
+
+struct map_session_data *pc_get_child (struct map_session_data *sd)
+{
+ if (sd && pc_ismarried(sd) && sd->status.child > 0)
+ // charid2sd returns NULL if not found
+ return map_charid2sd(sd->status.child);
+
+ return NULL;
+}
+
+//
+// 自然回復物
+//
+/*==========================================
+ * SP回復量計算
+ *------------------------------------------
+ */
+static int natural_heal_tick,natural_heal_prev_tick,natural_heal_diff_tick;
+static int pc_spheal(struct map_session_data *sd)
+{
+ int a = natural_heal_diff_tick;
+
+ nullpo_retr(0, sd);
+
+ if(pc_issit(sd))
+ a += a;
+ if (sd->sc_count) {
+ if (sd->sc_data[SC_MAGNIFICAT].timer!=-1) // マグニフィカ?ト
+ a += a;
+ if (sd->sc_data[SC_REGENERATION].timer != -1)
+ a *= sd->sc_data[SC_REGENERATION].val1;
+ }
+ // Re-added back to status_calc
+ //if((skill = pc_checkskill(sd,HP_MEDITATIO)) > 0) //Increase natural SP regen with Meditatio [DracoRPG]
+ //a += a*skill*3/100;
+
+ if (sd->status.guild_id > 0) {
+ struct guild_castle *gc = guild_mapindex2gc(sd->mapindex); // Increased guild castle regen [Valaris]
+ if(gc) {
+ struct guild *g = guild_search(sd->status.guild_id);
+ if(g && g->guild_id == gc->guild_id)
+ a += a;
+ } // end addition [Valaris]
+ }
+
+ if (map_getcell(sd->bl.m,sd->bl.x,sd->bl.y,CELL_CHKREGEN))
+ a += a;
+
+ return a;
+}
+
+/*==========================================
+ * HP回復量計算
+ *------------------------------------------
+ */
+static int pc_hpheal(struct map_session_data *sd)
+{
+ int a = natural_heal_diff_tick;
+
+ nullpo_retr(0, sd);
+
+ if(pc_issit(sd))
+ a += a;
+ if (sd->sc_count) {
+ if (sd->sc_data[SC_MAGNIFICAT].timer != -1) // Modified by RoVeRT
+ a += a;
+ if (sd->sc_data[SC_REGENERATION].timer != -1)
+ a *= sd->sc_data[SC_REGENERATION].val1;
+ }
+ if (sd->status.guild_id > 0) {
+ struct guild_castle *gc = guild_mapindex2gc(sd->mapindex); // Increased guild castle regen [Valaris]
+ if(gc) {
+ struct guild *g = guild_search(sd->status.guild_id);
+ if(g && g->guild_id == gc->guild_id)
+ a += a;
+ } // end addition [Valaris]
+ }
+
+ if (map_getcell(sd->bl.m,sd->bl.x,sd->bl.y,CELL_CHKREGEN))
+ a += a;
+
+ return a;
+}
+
+static int pc_natural_heal_hp(struct map_session_data *sd)
+{
+ int bhp;
+ int inc_num,bonus,hp_flag;
+
+ nullpo_retr(0, sd);
+
+ if (sd->no_regen & 1)
+ return 0;
+
+ if(pc_checkoverhp(sd)) {
+ sd->hp_sub = sd->inchealhptick = 0;
+ return 0;
+ }
+
+ bhp=sd->status.hp;
+ hp_flag = (pc_checkskill(sd,SM_MOVINGRECOVERY) > 0 && sd->walktimer != -1);
+
+ if(sd->walktimer == -1) {
+ inc_num = pc_hpheal(sd);
+ if(sd->sc_data[SC_TENSIONRELAX].timer!=-1 ){ // テンションリラックス
+ sd->hp_sub += 2*inc_num;
+ sd->inchealhptick += 3*natural_heal_diff_tick;
+ } else {
+ sd->hp_sub += inc_num;
+ sd->inchealhptick += natural_heal_diff_tick;
+ }
+ }
+ else if(hp_flag) {
+ inc_num = pc_hpheal(sd);
+ sd->hp_sub += inc_num;
+ sd->inchealhptick = 0;
+ }
+ else {
+ sd->hp_sub = sd->inchealhptick = 0;
+ return 0;
+ }
+
+ if(sd->hp_sub >= battle_config.natural_healhp_interval) {
+ bonus = sd->nhealhp;
+ if(hp_flag) {
+ bonus >>= 2;
+ if(bonus <= 0) bonus = 1;
+ }
+ while(sd->hp_sub >= battle_config.natural_healhp_interval) {
+ sd->hp_sub -= battle_config.natural_healhp_interval;
+ if(sd->status.hp + bonus <= sd->status.max_hp)
+ sd->status.hp += bonus;
+ else {
+ sd->status.hp = sd->status.max_hp;
+ sd->hp_sub = sd->inchealhptick = 0;
+ }
+ }
+ }
+ if(bhp!=sd->status.hp)
+ clif_updatestatus(sd,SP_HP);
+
+ if(sd->nshealhp > 0) {
+ if(sd->inchealhptick >= battle_config.natural_heal_skill_interval && sd->status.hp < sd->status.max_hp) {
+ bonus = sd->nshealhp;
+ while(sd->inchealhptick >= battle_config.natural_heal_skill_interval) {
+ sd->inchealhptick -= battle_config.natural_heal_skill_interval;
+ if(sd->status.hp + bonus <= sd->status.max_hp)
+ sd->status.hp += bonus;
+ else {
+ bonus = sd->status.max_hp - sd->status.hp;
+ sd->status.hp = sd->status.max_hp;
+ sd->hp_sub = sd->inchealhptick = 0;
+ }
+ clif_heal(sd->fd,SP_HP,bonus);
+ }
+ }
+ }
+ else sd->inchealhptick = 0;
+
+ return 0;
+}
+
+static int pc_natural_heal_sp(struct map_session_data *sd)
+{
+ int bsp;
+ int inc_num,bonus;
+
+ nullpo_retr(0, sd);
+
+ if (sd->no_regen & 2)
+ return 0;
+
+ if(pc_checkoversp(sd)) {
+ sd->sp_sub = sd->inchealsptick = 0;
+ return 0;
+ }
+
+ bsp=sd->status.sp;
+
+ inc_num = pc_spheal(sd);
+ if(sd->sc_data[SC_EXPLOSIONSPIRITS].timer == -1 || (sd->sc_data[SC_SPIRIT].timer!=-1 && sd->sc_data[SC_SPIRIT].val2 == SL_MONK))
+ sd->sp_sub += inc_num;
+ if(sd->walktimer == -1)
+ sd->inchealsptick += natural_heal_diff_tick;
+ else sd->inchealsptick = 0;
+
+ if(sd->sp_sub >= battle_config.natural_healsp_interval){
+ bonus = sd->nhealsp;;
+ while(sd->sp_sub >= battle_config.natural_healsp_interval){
+ sd->sp_sub -= battle_config.natural_healsp_interval;
+ if(sd->status.sp + bonus <= sd->status.max_sp)
+ sd->status.sp += bonus;
+ else {
+ sd->status.sp = sd->status.max_sp;
+ sd->sp_sub = sd->inchealsptick = 0;
+ }
+ }
+ }
+
+ if(bsp != sd->status.sp)
+ clif_updatestatus(sd,SP_SP);
+
+ if(sd->nshealsp > 0) {
+ if(sd->inchealsptick >= battle_config.natural_heal_skill_interval && sd->status.sp < sd->status.max_sp) {
+ if(sd->doridori_counter && (sd->class_&MAPID_UPPERMASK) == MAPID_SUPER_NOVICE) {
+ bonus = sd->nshealsp*2;
+ sd->doridori_counter = 0;
+ } else
+ bonus = sd->nshealsp;
+ while(sd->inchealsptick >= battle_config.natural_heal_skill_interval) {
+ sd->inchealsptick -= battle_config.natural_heal_skill_interval;
+ if(sd->status.sp + bonus <= sd->status.max_sp)
+ sd->status.sp += bonus;
+ else {
+ bonus = sd->status.max_sp - sd->status.sp;
+ sd->status.sp = sd->status.max_sp;
+ sd->sp_sub = sd->inchealsptick = 0;
+ }
+ clif_heal(sd->fd,SP_SP,bonus);
+ }
+ }
+ }
+ else sd->inchealsptick = 0;
+
+ return 0;
+}
+
+static int pc_spirit_heal_hp(struct map_session_data *sd)
+{
+ int bonus_hp,interval = battle_config.natural_heal_skill_interval;
+
+ nullpo_retr(0, sd);
+
+ if(pc_checkoverhp(sd)) {
+ sd->inchealspirithptick = 0;
+ return 0;
+ }
+
+ sd->inchealspirithptick += natural_heal_diff_tick;
+
+ if(sd->weight*100/sd->max_weight >= battle_config.natural_heal_weight_rate)
+ interval += interval;
+
+ if(sd->inchealspirithptick >= interval) {
+ bonus_hp = sd->nsshealhp;
+ if(sd->doridori_counter && pc_checkskill(sd,TK_HPTIME) > 0) {
+ //TK_HPTIME doridori provided bonus [Dralnu]
+ bonus_hp += sd->nsshealhp;
+ if (!sd->nsshealsp) //If there's sp regen, this gets clear in the next function. [Skotlex]
+ sd->doridori_counter = 0;
+ }
+ while(sd->inchealspirithptick >= interval) {
+ if(pc_issit(sd)) {
+ sd->inchealspirithptick -= interval;
+ if(sd->status.hp < sd->status.max_hp) {
+ if(sd->status.hp + bonus_hp <= sd->status.max_hp)
+ sd->status.hp += bonus_hp;
+ else {
+ bonus_hp = sd->status.max_hp - sd->status.hp;
+ sd->status.hp = sd->status.max_hp;
+ }
+ clif_heal(sd->fd,SP_HP,bonus_hp);
+ sd->inchealspirithptick = 0;
+ }
+ }else{
+ sd->inchealspirithptick -= natural_heal_diff_tick;
+ break;
+ }
+ }
+ }
+
+ return 0;
+}
+static int pc_spirit_heal_sp(struct map_session_data *sd)
+{
+ int bonus_sp,interval = battle_config.natural_heal_skill_interval;
+
+ nullpo_retr(0, sd);
+
+ if(pc_checkoversp(sd)) {
+ sd->inchealspiritsptick = 0;
+ return 0;
+ }
+
+ sd->inchealspiritsptick += natural_heal_diff_tick;
+
+ if(sd->weight*100/sd->max_weight >= battle_config.natural_heal_weight_rate)
+ interval += interval;
+
+ if(sd->inchealspiritsptick >= interval) {
+ bonus_sp = sd->nsshealsp;
+ if(sd->doridori_counter && pc_checkskill(sd,TK_SPTIME) > 0) {
+ //TK_SPTIME doridori provided bonus [Dralnu]
+ bonus_sp += sd->nsshealsp;
+ sd->doridori_counter = 0;
+ }
+ while(sd->inchealspiritsptick >= interval) {
+ if(pc_issit(sd)) {
+ sd->inchealspiritsptick -= interval;
+ if(sd->status.sp < sd->status.max_sp) {
+ if(sd->status.sp + bonus_sp <= sd->status.max_sp)
+ sd->status.sp += bonus_sp;
+ else {
+ bonus_sp = sd->status.max_sp - sd->status.sp;
+ sd->status.sp = sd->status.max_sp;
+ }
+ clif_heal(sd->fd,SP_SP,bonus_sp);
+ sd->inchealspiritsptick = 0;
+ }
+ }else{
+ sd->inchealspiritsptick -= natural_heal_diff_tick;
+ break;
+ }
+ }
+ }
+
+ return 0;
+}
+
+static int pc_bleeding (struct map_session_data *sd)
+{
+ int hp = 0, sp = 0;
+ nullpo_retr(0, sd);
+
+ if (sd->hp_loss_value > 0) {
+ sd->hp_loss_tick += natural_heal_diff_tick;
+ if (sd->hp_loss_tick >= sd->hp_loss_rate) {
+ do {
+ hp += sd->hp_loss_value;
+ sd->hp_loss_tick -= sd->hp_loss_rate;
+ } while (sd->hp_loss_tick >= sd->hp_loss_rate);
+ sd->hp_loss_tick = 0;
+ }
+ }
+
+ if (sd->sp_loss_value > 0) {
+ sd->sp_loss_tick += natural_heal_diff_tick;
+ if (sd->sp_loss_tick >= sd->sp_loss_rate) {
+ do {
+ sp += sd->sp_loss_value;
+ sd->sp_loss_tick -= sd->sp_loss_rate;
+ } while (sd->sp_loss_tick >= sd->sp_loss_rate);
+ sd->sp_loss_tick = 0;
+ }
+ }
+
+ if (hp > 0 || sp > 0)
+ pc_heal(sd,-hp,-sp);
+
+ return 0;
+}
+
+/*==========================================
+ * HP/SP 自然回復 各クライアント
+ *------------------------------------------
+ */
+
+static int pc_natural_heal_sub(struct map_session_data *sd,va_list ap) {
+ int tick;
+
+ nullpo_retr(0, sd);
+ tick = va_arg(ap,int);
+
+// -- moonsoul (if conditions below altered to disallow natural healing if under berserk status)
+ if (pc_isdead(sd) || pc_ishiding(sd) ||
+ //-- cannot regen for 5 minutes after using Berserk --- [Celest]
+ (sd->sc_count && (
+ (sd->sc_data[SC_POISON].timer != -1 && sd->sc_data[SC_SLOWPOISON].timer == -1) ||
+ (sd->sc_data[SC_DPOISON].timer != -1 && sd->sc_data[SC_SLOWPOISON].timer == -1) ||
+ sd->sc_data[SC_BERSERK].timer != -1 ||
+ sd->sc_data[SC_TRICKDEAD].timer != -1
+ ))
+ ) { //Cannot heal neither natural or special.
+ sd->hp_sub = sd->inchealhptick = sd->inchealspirithptick = 0;
+ sd->sp_sub = sd->inchealsptick = sd->inchealspiritsptick = 0;
+ } else {
+ if (DIFF_TICK (tick, sd->canregen_tick)<0 ||
+ sd->weight*100/sd->max_weight >= battle_config.natural_heal_weight_rate) { //Cannot heal natural HP/SP
+ sd->hp_sub = sd->inchealhptick = 0;
+ sd->sp_sub = sd->inchealsptick = 0;
+ } else { //natural heal
+ pc_natural_heal_hp(sd);
+ if(sd->sc_count && (
+ sd->sc_data[SC_EXTREMITYFIST].timer != -1 ||
+ sd->sc_data[SC_DANCING].timer != -1 ||
+ sd->sc_data[SC_BLEEDING].timer != -1
+ )) //No SP natural heal.
+ sd->sp_sub = sd->inchealsptick = 0;
+ else
+ pc_natural_heal_sp(sd);
+ sd->canregen_tick = tick;
+ }
+ //Sitting Healing
+ if (sd->nsshealhp)
+ pc_spirit_heal_hp(sd);
+ if (sd->nsshealsp)
+ pc_spirit_heal_sp(sd);
+ }
+ if (sd->hp_loss_value > 0 || sd->sp_loss_value > 0)
+ pc_bleeding(sd);
+ else
+ sd->hp_loss_tick = sd->sp_loss_tick = 0;
+
+ return 0;
+}
+
+/*==========================================
+ * HP/SP自然回復 (interval timer??)
+ *------------------------------------------
+ */
+int pc_natural_heal(int tid,unsigned int tick,int id,int data)
+{
+ natural_heal_tick = tick;
+ natural_heal_diff_tick = DIFF_TICK(natural_heal_tick,natural_heal_prev_tick);
+ clif_foreachclient(pc_natural_heal_sub, tick);
+
+ natural_heal_prev_tick = tick;
+ return 0;
+}
+
+/*==========================================
+ * セ?ブポイントの保存
+ *------------------------------------------
+ */
+int pc_setsavepoint(struct map_session_data *sd, short mapindex,int x,int y)
+{
+ nullpo_retr(0, sd);
+
+ sd->status.save_point.map = mapindex;
+ sd->status.save_point.x = x;
+ sd->status.save_point.y = y;
+
+ return 0;
+}
+
+/*==========================================
+ * 自動セ?ブ 各クライアント
+ *------------------------------------------
+ */
+static int last_save_fd,save_flag;
+static int pc_autosave_sub(struct map_session_data *sd,va_list ap)
+{
+ nullpo_retr(0, sd);
+
+ Assert((sd->status.pet_id == 0 || sd->pd == 0) || sd->pd->msd == sd);
+
+ if(save_flag==0 && sd->fd>last_save_fd && !sd->state.waitingdisconnect)
+ {
+ // pet
+ if(sd->status.pet_id > 0 && sd->pd)
+ intif_save_petdata(sd->status.account_id,&sd->pet);
+
+ chrif_save(sd,0);
+ save_flag=1;
+ last_save_fd = sd->fd;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * 自動セ?ブ (timer??)
+ *------------------------------------------
+ */
+int pc_autosave(int tid,unsigned int tick,int id,int data)
+{
+ int interval;
+
+ save_flag=0;
+ clif_foreachclient(pc_autosave_sub);
+ if(save_flag==0)
+ last_save_fd=0;
+
+ interval = autosave_interval/(clif_countusers()+1);
+ if(interval <= 0)
+ interval = 1;
+ add_timer(gettick()+interval,pc_autosave,0,0);
+
+ return 0;
+}
+
+int pc_read_gm_account(int fd)
+{
+#ifdef TXT_ONLY
+ int i = 0;
+ RFIFOHEAD(fd);
+#endif
+ if (gm_account != NULL)
+ aFree(gm_account);
+ GM_num = 0;
+#ifdef TXT_ONLY
+ gm_account = (struct gm_account *) aCallocA(((RFIFOW(fd,2) - 4) / 5), sizeof(struct gm_account));
+ for (i = 4; i < RFIFOW(fd,2); i = i + 5) {
+ gm_account[GM_num].account_id = RFIFOL(fd,i);
+ gm_account[GM_num].level = (int)RFIFOB(fd,i+4);
+ //printf("GM account: %d -> level %d\n", gm_account[GM_num].account_id, gm_account[GM_num].level);
+ GM_num++;
+ }
+#else
+ sprintf (tmp_sql, "SELECT `%s`,`%s` FROM `%s` WHERE `%s`>='%d'",gm_db_account_id,gm_db_level,gm_db,gm_db_level,lowest_gm_level);
+ if(mysql_query(&lmysql_handle, tmp_sql) ) {
+ ShowSQL("DB error - %s\n",mysql_error(&lmysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ return 0;
+ }
+ lsql_res = mysql_store_result(&lmysql_handle);
+ if (lsql_res) {
+ gm_account = (struct gm_account *) aCallocA((size_t)mysql_num_rows(lsql_res), sizeof(struct gm_account));
+ while ((lsql_row = mysql_fetch_row(lsql_res))) {
+ gm_account[GM_num].account_id = atoi(lsql_row[0]);
+ gm_account[GM_num].level = atoi(lsql_row[1]);
+ ShowNotice("GM account: %d -> level %d\n", gm_account[GM_num].account_id, gm_account[GM_num].level);
+ GM_num++;
+ }
+ }
+
+ mysql_free_result(lsql_res);
+#endif /* TXT_ONLY */
+ return GM_num;
+}
+
+/*================================================
+ * timer to do the day [Yor]
+ * data: 0 = called by timer, 1 = gmcommand/script
+ *------------------------------------------------
+ */
+int map_day_timer(int tid, unsigned int tick, int id, int data)
+{
+ char tmp_soutput[1024];
+ struct map_session_data *pl_sd;
+
+ if (data == 0 && battle_config.day_duration <= 0) // if we want a day
+ return 0;
+
+ if (night_flag != 0) {
+ int i;
+ night_flag = 0; // 0=day, 1=night [Yor]
+
+ for(i = 0; i < fd_max; i++) {
+ if (session[i] && (pl_sd = (struct map_session_data *) session[i]->session_data) && pl_sd->state.auth && pl_sd->fd)
+ {
+ if (pl_sd->state.night) {
+ clif_status_load(&pl_sd->bl, SI_NIGHT, 0); //New night effect by dynamix [Skotlex]
+ pl_sd->state.night = 0;
+ }
+ }
+ }
+
+ strcpy(tmp_soutput, (data == 0) ? msg_txt(502) : msg_txt(60)); // The day has arrived!
+ intif_GMmessage(tmp_soutput, strlen(tmp_soutput) + 1, 0);
+ }
+
+ return 0;
+}
+
+/*================================================
+ * timer to do the night [Yor]
+ * data: 0 = called by timer, 1 = gmcommand/script
+ *------------------------------------------------
+ */
+int map_night_timer(int tid, unsigned int tick, int id, int data)
+{
+ char tmp_soutput[1024];
+ struct map_session_data *pl_sd;
+
+ if (data == 0 && battle_config.night_duration <= 0) // if we want a night
+ return 0;
+
+ if (night_flag == 0) {
+ int i;
+ night_flag = 1; // 0=day, 1=night [Yor]
+ for(i = 0; i < fd_max; i++) {
+ if (session[i] && (pl_sd = (struct map_session_data *) session[i]->session_data) && pl_sd->state.auth && pl_sd->fd)
+ {
+ if (!pl_sd->state.night && map[pl_sd->bl.m].flag.nightenabled) {
+ clif_status_load(&pl_sd->bl, SI_NIGHT, 1); //New night effect by dynamix [Skotlex]
+ pl_sd->state.night = 1;
+ }
+ }
+ }
+ strcpy(tmp_soutput, (data == 0) ? msg_txt(503) : msg_txt(59)); // The night has fallen...
+ intif_GMmessage(tmp_soutput, strlen(tmp_soutput) + 1, 0);
+ }
+
+ return 0;
+}
+
+void pc_setstand(struct map_session_data *sd){
+ nullpo_retv(sd);
+
+ if(sd->sc_count && sd->sc_data[SC_TENSIONRELAX].timer!=-1)
+ status_change_end(&sd->bl,SC_TENSIONRELAX,-1);
+
+ sd->state.dead_sit = 0;
+}
+
+//
+// 初期化物
+//
+/*==========================================
+ * 設定ファイル?み?む
+ * exp.txt 必要??値
+ * job_db1.txt 重量,hp,sp,攻?速度
+ * job_db2.txt job能力値ボ?ナス
+ * skill_tree.txt 各職?のスキルツリ?
+ * attr_fix.txt ?性修正テ?ブル
+ * size_fix.txt サイズ補正テ?ブル
+ * refine_db.txt 精?デ?タテ?ブル
+ *------------------------------------------
+ */
+int pc_readdb(void)
+{
+ int i,j,k;
+ FILE *fp;
+ char line[1024],*p;
+
+ // 必要??値?み?み
+ memset(exp_table,0,sizeof(exp_table));
+ sprintf(line, "%s/exp.txt", db_path);
+ fp=fopen(line, "r");
+ if(fp==NULL){
+ ShowError("can't read %s\n", line);
+ return 1;
+ }
+ i=0;
+ while(fgets(line, sizeof(line)-1, fp)){
+ int bn,b1,b2,b3,b4,b5,b6,jn,j1,j2,j3,j4,j5,j6;
+ if(line[0]=='/' && line[1]=='/')
+ continue;
+ if(sscanf(line,"%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d",&bn,&b1,&b2,&b3,&b4,&b5,&b6,&jn,&j1,&j2,&j3,&j4,&j5,&j6)!=14)
+ continue;
+ exp_table[0][i]=bn;
+ exp_table[1][i]=b1;
+ exp_table[2][i]=b2;
+ exp_table[3][i]=b3;
+ exp_table[4][i]=b4;
+ exp_table[5][i]=b5;
+ exp_table[6][i]=b6;
+ exp_table[7][i]=jn;
+ exp_table[8][i]=j1;
+ exp_table[9][i]=j2;
+ exp_table[10][i]=j3;
+ exp_table[11][i]=j4;
+ exp_table[12][i]=j5;
+ exp_table[13][i]=j6;
+ i++;
+ }
+ if (i > battle_config.max_base_level)
+ { //Empty Base level columns
+ for (j = battle_config.max_base_level-1; j < i && exp_table[0][j]>0; j++)
+ {
+ exp_table[0][j]=0;
+ exp_table[1][j]=0;
+ exp_table[2][j]=0;
+ exp_table[3][j]=0;
+ exp_table[4][j]=0;
+ exp_table[5][j]=0;
+ exp_table[6][j]=0;
+ }
+ }
+ if (i > battle_config.max_sn_level)
+ { //Empty SN job exp columns
+ for (j = battle_config.max_sn_level-1; j < i && exp_table[10][j]>0; j++)
+ exp_table[10][j]=0;
+ }
+ if (i > battle_config.max_adv_level)
+ { //Empty Adv Jobs columns
+ for (j = battle_config.max_adv_level-1; j < i && exp_table[13][j]>0; j++)
+ exp_table[13][j]=0;
+ }
+ if (i > battle_config.max_job_level)
+ { //Empty normal Job columns
+ for (j = battle_config.max_job_level-1; j < i &&
+ (exp_table[8][j]>0 || exp_table[9][j]>0 || exp_table[12][j]>0); j++)
+ {
+ exp_table[8][j]=0; //1st Job
+ exp_table[9][j]=0; //2nd Job
+ exp_table[12][j]=0; //Adv 1st Job
+ }
+ }
+ fclose(fp);
+ ShowStatus("Done reading '"CL_WHITE"%s"CL_RESET"'.\n","exp.txt");
+
+ // スキルツリ?
+ memset(skill_tree,0,sizeof(skill_tree));
+ sprintf(line, "%s/skill_tree.txt", db_path);
+ fp=fopen(line,"r");
+ if(fp==NULL){
+ ShowError("can't read %s\n", line);
+ return 1;
+ }
+
+ while(fgets(line, sizeof(line)-1, fp)){
+ char *split[50];
+ int f=0, m=3;
+ if(line[0]=='/' && line[1]=='/')
+ continue;
+ for(j=0,p=line;j<14 && p;j++){
+ split[j]=p;
+ p=strchr(p,',');
+ if(p) *p++=0;
+ }
+ if(j<13)
+ continue;
+ if (j == 14) {
+ f=1; // MinJobLvl has been added
+ m++;
+ }
+ // check for bounds [celest]
+ if (atoi(split[0]) >= MAX_PC_CLASS)
+ continue;
+ k = atoi(split[1]); //This is to avoid adding two lines for the same skill. [Skotlex]
+ for(j = 0; j < MAX_SKILL_TREE && skill_tree[atoi(split[0])][j].id && skill_tree[atoi(split[0])][j].id != k; j++);
+ if (j == MAX_SKILL_TREE)
+ {
+ ShowWarning("Unable to load skill %d into job %d's tree. Maximum number of skills per class has been reached.\n", k, atoi(split[0]));
+ continue;
+ }
+ skill_tree[atoi(split[0])][j].id=k;
+ skill_tree[atoi(split[0])][j].max=atoi(split[2]);
+ if (f) skill_tree[atoi(split[0])][j].joblv=atoi(split[3]);
+
+ for(k=0;k<5;k++){
+ skill_tree[atoi(split[0])][j].need[k].id=atoi(split[k*2+m]);
+ skill_tree[atoi(split[0])][j].need[k].lv=atoi(split[k*2+m+1]);
+ }
+ }
+ fclose(fp);
+ ShowStatus("Done reading '"CL_WHITE"%s"CL_RESET"'.\n","skill_tree.txt");
+
+ // ?性修正テ?ブル
+ for(i=0;i<4;i++)
+ for(j=0;j<10;j++)
+ for(k=0;k<10;k++)
+ attr_fix_table[i][j][k]=100;
+
+ sprintf(line, "%s/attr_fix.txt", db_path);
+ fp=fopen(line,"r");
+ if(fp==NULL){
+ ShowError("can't read %s\n", line);
+ return 1;
+ }
+ while(fgets(line, sizeof(line)-1, fp)){
+ char *split[10];
+ int lv,n;
+ if(line[0]=='/' && line[1]=='/')
+ continue;
+ for(j=0,p=line;j<3 && p;j++){
+ split[j]=p;
+ p=strchr(p,',');
+ if(p) *p++=0;
+ }
+ lv=atoi(split[0]);
+ n=atoi(split[1]);
+
+ for(i=0;i<n;){
+ if( !fgets(line, sizeof(line)-1, fp) )
+ break;
+ if(line[0]=='/' && line[1]=='/')
+ continue;
+
+ for(j=0,p=line;j<n && p;j++){
+ while(*p==32 && *p>0)
+ p++;
+ attr_fix_table[lv-1][i][j]=atoi(p);
+ if(battle_config.attr_recover == 0 && attr_fix_table[lv-1][i][j] < 0)
+ attr_fix_table[lv-1][i][j] = 0;
+ p=strchr(p,',');
+ if(p) *p++=0;
+ }
+
+ i++;
+ }
+ }
+ fclose(fp);
+ ShowStatus("Done reading '"CL_WHITE"%s"CL_RESET"'.\n","attr_fix.txt");
+
+ // スキルツリ?
+ memset(statp,0,sizeof(statp));
+ i=1;
+ j=45; // base points
+ sprintf(line, "%s/statpoint.txt", db_path);
+ fp=fopen(line,"r");
+ if(fp == NULL){
+ ShowStatus("Can't read '"CL_WHITE"%s"CL_RESET"'... Generating DB.\n",line);
+ //return 1;
+ } else {
+ while(fgets(line, sizeof(line)-1, fp)){
+ if(line[0]=='/' && line[1]=='/')
+ continue;
+ if ((j=atoi(line))<0)
+ j=0;
+ if (i >= MAX_LEVEL)
+ break;
+ statp[i]=j;
+ i++;
+ }
+ fclose(fp);
+ ShowStatus("Done reading '"CL_WHITE"%s"CL_RESET"'.\n","statpoint.txt");
+ }
+ // generate the remaining parts of the db if necessary
+ for (; i < MAX_LEVEL; i++) {
+ j += (i+15)/5;
+ statp[i] = j;
+ }
+
+ return 0;
+}
+
+// Read MOTD on startup. [Valaris]
+int pc_read_motd(void) {
+ FILE *fp;
+ int ln=0,i=0;
+
+ memset(motd_text,0,sizeof(motd_text));
+ if ((fp = fopen(motd_txt, "r")) != NULL) {
+ while ((ln < MOTD_LINE_SIZE) && fgets(motd_text[ln], sizeof(motd_text[ln])-1, fp) != NULL) {
+ if(motd_text[ln][0] == '/' && motd_text[ln][1] == '/')
+ continue;
+ for(i=0; motd_text[ln][i]; i++) {
+ if (motd_text[ln][i] == '\r' || motd_text[ln][i]== '\n') {
+ if(i)
+ motd_text[ln][i]=0;
+ else
+ motd_text[ln][0]=' ';
+ ln++;
+ break;
+ }
+ }
+ }
+ fclose(fp);
+ }
+ else if(battle_config.error_log)
+ ShowWarning("In function pc_read_motd() -> File '"CL_WHITE"%s"CL_RESET"' not found.\n", motd_txt);
+
+ return 0;
+}
+
+/*==========================================
+ * pc? 係初期化
+ *------------------------------------------
+ */
+void do_final_pc(void) {
+ if (gm_account)
+ aFree(gm_account);
+ return;
+}
+int do_init_pc(void) {
+ pc_readdb();
+ pc_read_motd(); // Read MOTD [Valaris]
+
+ add_timer_func_list(pc_walk, "pc_walk");
+ add_timer_func_list(pc_attack_timer, "pc_attack_timer");
+ add_timer_func_list(pc_natural_heal, "pc_natural_heal");
+ add_timer_func_list(pc_invincible_timer, "pc_invincible_timer");
+ add_timer_func_list(pc_eventtimer, "pc_eventtimer");
+ add_timer_func_list(pc_calc_pvprank_timer, "pc_calc_pvprank_timer");
+ add_timer_func_list(pc_autosave, "pc_autosave");
+ add_timer_func_list(pc_spiritball_timer, "pc_spiritball_timer");
+ add_timer_func_list(pc_blockskill_end, "pc_blockskill_end");
+ add_timer_func_list(pc_follow_timer, "pc_follow_timer");
+ add_timer_interval((natural_heal_prev_tick = gettick() + NATURAL_HEAL_INTERVAL), pc_natural_heal, 0, 0, NATURAL_HEAL_INTERVAL);
+ add_timer(gettick() + autosave_interval, pc_autosave, 0, 0);
+#ifndef TXT_ONLY
+ pc_read_gm_account(0);
+#endif /* not TXT_ONLY */
+
+ if (battle_config.day_duration > 0 && battle_config.night_duration > 0) {
+ int day_duration = battle_config.day_duration;
+ int night_duration = battle_config.night_duration;
+ // add night/day timer (by [yor])
+ add_timer_func_list(map_day_timer, "map_day_timer"); // by [yor]
+ add_timer_func_list(map_night_timer, "map_night_timer"); // by [yor]
+
+ if (!battle_config.night_at_start) {
+ night_flag = 0; // 0=day, 1=night [Yor]
+ day_timer_tid = add_timer_interval(gettick() + day_duration + night_duration, map_day_timer, 0, 0, day_duration + night_duration);
+ night_timer_tid = add_timer_interval(gettick() + day_duration, map_night_timer, 0, 0, day_duration + night_duration);
+ } else {
+ night_flag = 1; // 0=day, 1=night [Yor]
+ day_timer_tid = add_timer_interval(gettick() + night_duration, map_day_timer, 0, 0, day_duration + night_duration);
+ night_timer_tid = add_timer_interval(gettick() + day_duration + night_duration, map_night_timer, 0, 0, day_duration + night_duration);
+ }
+ }
+
+ return 0;
+}
diff --git a/src/map/pc.h b/src/map/pc.h
new file mode 100644
index 000000000..e7a9c85c5
--- /dev/null
+++ b/src/map/pc.h
@@ -0,0 +1,251 @@
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#ifndef _PC_H_
+#define _PC_H_
+
+#include "map.h"
+
+#define OPTION_MASK 0xd7b8
+#define CART_MASK 0x788
+
+//Update this max as necessary. 53 is the value needed for Super Baby currently
+#define MAX_SKILL_TREE 53
+
+#define pc_setdead(sd) ((sd)->state.dead_sit = 1)
+#define pc_setsit(sd) ((sd)->state.dead_sit = 2)
+#define pc_isdead(sd) ((sd)->state.dead_sit == 1)
+#define pc_issit(sd) ((sd)->state.dead_sit == 2)
+#define pc_setdir(sd,b,h) ((sd)->dir = (b) ,(sd)->head_dir = (h) )
+#define pc_setchatid(sd,n) ((sd)->chatID = n)
+#define pc_ishiding(sd) ((sd)->status.option&(OPTION_HIDE|OPTION_CLOAK|OPTION_CHASEWALK))
+#define pc_iscloaking(sd) (!((sd)->status.option&OPTION_CHASEWALK) && ((sd)->status.option&OPTION_CLOAK))
+#define pc_ischasewalk(sd) ((sd)->status.option&OPTION_CHASEWALK)
+#define pc_iscarton(sd) ((sd)->status.option&CART_MASK)
+#define pc_isfalcon(sd) ((sd)->status.option&OPTION_FALCON)
+#define pc_isriding(sd) ((sd)->status.option&OPTION_RIDING)
+#define pc_isinvisible(sd) ((sd)->status.option&OPTION_INVISIBLE)
+#define pc_is50overweight(sd) (sd->weight*2 >= sd->max_weight)
+#define pc_is90overweight(sd) (sd->weight*10 >= sd->max_weight*9)
+#define pc_maxparameter(sd) ((sd->class_&JOBL_BABY) ? battle_config.max_baby_parameter : battle_config.max_parameter)
+//Checks if the given class value corresponds to a player class. [Skotlex]
+#define pcdb_checkid(class_) ((class_ >= JOB_NOVICE && class_ <= JOB_XMAS) || (class_ >= JOB_NOVICE_HIGH && class_ <= JOB_SOUL_LINKER))
+
+int pc_isGM(struct map_session_data *sd);
+int pc_iskiller(struct map_session_data *src, struct map_session_data *target); // [MouseJstr]
+int pc_getrefinebonus(int lv,int type);
+int pc_can_move(struct map_session_data *sd); //[Skotlex]
+int pc_can_give_items(int level); //[Lupus]
+
+int pc_setrestartvalue(struct map_session_data *sd,int type);
+int pc_makesavestatus(struct map_session_data *);
+int pc_setnewpc(struct map_session_data*,int,int,int,unsigned int,int,int);
+int pc_authok(struct map_session_data*, int, time_t, struct mmo_charstatus *);
+int pc_authfail(struct map_session_data *);
+int pc_reg_received(struct map_session_data *sd);
+
+int pc_isequip(struct map_session_data *sd,int n);
+int pc_equippoint(struct map_session_data *sd,int n);
+
+int pc_break_equip(struct map_session_data *, unsigned short);
+#define pc_breakweapon(sd) (pc_break_equip(sd, EQP_WEAPON))
+#define pc_breakarmor(sd) (pc_break_equip(sd, EQP_ARMOR))
+#define pc_breakshield(sd) (pc_break_equip(sd, EQP_SHIELD))
+#define pc_breakhelm(sd) (pc_break_equip(sd, EQP_HELM))
+
+int pc_checkskill(struct map_session_data *sd,int skill_id);
+int pc_checkallowskill(struct map_session_data *sd);
+int pc_checkequip(struct map_session_data *sd,int pos);
+
+int pc_calc_skilltree(struct map_session_data *sd);
+int pc_calc_skilltree_normalize_job(struct map_session_data *sd);
+int pc_clean_skilltree(struct map_session_data *sd);
+
+int pc_checkoverhp(struct map_session_data*);
+int pc_checkoversp(struct map_session_data*);
+
+int pc_can_reach(struct map_session_data*,int,int);
+int pc_walktoxy(struct map_session_data*,int,int);
+int pc_stop_walking(struct map_session_data*,int);
+int pc_movepos(struct map_session_data*,int,int,int);
+int pc_setpos(struct map_session_data*,unsigned short,int,int,int);
+int pc_setsavepoint(struct map_session_data*,short,int,int);
+int pc_randomwarp(struct map_session_data *sd,int type);
+int pc_memo(struct map_session_data *sd,int i);
+int pc_remove_map(struct map_session_data *sd,int clrtype);
+
+int pc_checkadditem(struct map_session_data*,int,int);
+int pc_inventoryblank(struct map_session_data*);
+int pc_search_inventory(struct map_session_data *sd,int item_id);
+int pc_payzeny(struct map_session_data*,int);
+int pc_additem(struct map_session_data*,struct item*,int);
+int pc_getzeny(struct map_session_data*,int);
+int pc_delitem(struct map_session_data*,int,int,int);
+int pc_checkitem(struct map_session_data*);
+
+int pc_cart_additem(struct map_session_data *sd,struct item *item_data,int amount);
+int pc_cart_delitem(struct map_session_data *sd,int n,int amount,int type);
+int pc_putitemtocart(struct map_session_data *sd,int idx,int amount);
+int pc_getitemfromcart(struct map_session_data *sd,int idx,int amount);
+int pc_cartitem_amount(struct map_session_data *sd,int idx,int amount);
+
+int pc_takeitem(struct map_session_data*,struct flooritem_data*);
+int pc_dropitem(struct map_session_data*,int,int);
+
+int pc_checkweighticon(struct map_session_data *sd);
+
+int pc_bonus(struct map_session_data*,int,int);
+int pc_bonus2(struct map_session_data *sd,int,int,int);
+int pc_bonus3(struct map_session_data *sd,int,int,int,int);
+int pc_bonus4(struct map_session_data *sd,int,int,int,int,int);
+int pc_skill(struct map_session_data*,int,int,int);
+
+int pc_blockskill_start (struct map_session_data*,int,int); // [celest]
+
+int pc_insert_card(struct map_session_data *sd,int idx_card,int idx_equip);
+
+int pc_steal_item(struct map_session_data *sd,struct block_list *bl);
+int pc_steal_coin(struct map_session_data *sd,struct block_list *bl);
+
+int pc_modifybuyvalue(struct map_session_data*,int);
+int pc_modifysellvalue(struct map_session_data*,int);
+
+int pc_attack(struct map_session_data*,int,int);
+int pc_stopattack(struct map_session_data*);
+
+int pc_follow(struct map_session_data*, int); // [MouseJstr]
+int pc_stop_following(struct map_session_data*);
+
+int pc_checkbaselevelup(struct map_session_data *sd);
+int pc_checkjoblevelup(struct map_session_data *sd);
+int pc_gainexp(struct map_session_data*,int,int);
+int pc_nextbaseexp(struct map_session_data *);
+int pc_nextbaseafter(struct map_session_data *); // [Valaris]
+int pc_nextjobexp(struct map_session_data *);
+int pc_nextjobafter(struct map_session_data *); // [Valaris]
+int pc_need_status_point(struct map_session_data *,int);
+int pc_statusup(struct map_session_data*,int);
+int pc_statusup2(struct map_session_data*,int,int);
+int pc_skillup(struct map_session_data*,int);
+int pc_allskillup(struct map_session_data*);
+int pc_resetlvl(struct map_session_data*,int type);
+int pc_resetstate(struct map_session_data*);
+int pc_resetskill(struct map_session_data*);
+int pc_resetfeel(struct map_session_data*);
+int pc_equipitem(struct map_session_data*,int,int);
+int pc_unequipitem(struct map_session_data*,int,int);
+int pc_checkitem(struct map_session_data*);
+int pc_useitem(struct map_session_data*,int);
+
+int pc_damage(struct block_list *,struct map_session_data*,int);
+int pc_heal(struct map_session_data *,int,int);
+int pc_itemheal(struct map_session_data *sd,int hp,int sp);
+int pc_percentheal(struct map_session_data *sd,int,int);
+int pc_jobchange(struct map_session_data *,int, int);
+int pc_setoption(struct map_session_data *,int);
+int pc_setcart(struct map_session_data *sd,int type);
+int pc_setfalcon(struct map_session_data *sd);
+int pc_setriding(struct map_session_data *sd);
+int pc_changelook(struct map_session_data *,int,int);
+int pc_equiplookall(struct map_session_data *sd);
+
+int pc_readparam(struct map_session_data*,int);
+int pc_setparam(struct map_session_data*,int,int);
+int pc_readreg(struct map_session_data*,int);
+int pc_setreg(struct map_session_data*,int,int);
+char *pc_readregstr(struct map_session_data *sd,int reg);
+int pc_setregstr(struct map_session_data *sd,int reg,char *str);
+
+#define pc_readglobalreg(sd,reg) pc_readregistry(sd,reg,3)
+#define pc_setglobalreg(sd,reg,val) pc_setregistry(sd,reg,val,3)
+#define pc_readglobalreg_str(sd,reg) pc_readregistry_str(sd,reg,3)
+#define pc_setglobalreg_str(sd,reg,val) pc_setregistry_str(sd,reg,val,3)
+#define pc_readaccountreg(sd,reg) pc_readregistry(sd,reg,2)
+#define pc_setaccountreg(sd,reg,val) pc_setregistry(sd,reg,val,2)
+#define pc_readaccountregstr(sd,reg) pc_readregistry_str(sd,reg,2)
+#define pc_setaccountregstr(sd,reg,val) pc_setregistry_str(sd,reg,val,2)
+#define pc_readaccountreg2(sd,reg) pc_readregistry(sd,reg,1)
+#define pc_setaccountreg2(sd,reg,val) pc_setregistry(sd,reg,val,1)
+#define pc_readaccountreg2str(sd,reg) pc_readregistry_str(sd,reg,1)
+#define pc_setaccountreg2str(sd,reg,val) pc_setregistry_str(sd,reg,val,1)
+int pc_readregistry(struct map_session_data*,char*,int);
+int pc_setregistry(struct map_session_data*,char*,int,int);
+char *pc_readregistry_str(struct map_session_data*,char*,int);
+int pc_setregistry_str(struct map_session_data*,char*,char*,int);
+
+int pc_addeventtimer(struct map_session_data *sd,int tick,const char *name);
+int pc_deleventtimer(struct map_session_data *sd,const char *name);
+int pc_cleareventtimer(struct map_session_data *sd);
+int pc_addeventtimercount(struct map_session_data *sd,const char *name,int tick);
+
+int pc_calc_pvprank(struct map_session_data *sd);
+int pc_calc_pvprank_timer(int tid,unsigned int tick,int id,int data);
+
+int pc_ismarried(struct map_session_data *sd);
+int pc_marriage(struct map_session_data *sd,struct map_session_data *dstsd);
+int pc_divorce(struct map_session_data *sd);
+int pc_adoption(struct map_session_data *sd,struct map_session_data *dstsd,struct map_session_data *jasd);
+struct map_session_data *pc_get_partner(struct map_session_data *sd);
+struct map_session_data *pc_get_father(struct map_session_data *sd);
+struct map_session_data *pc_get_mother(struct map_session_data *sd);
+struct map_session_data *pc_get_child(struct map_session_data *sd);
+
+int pc_set_gm_level(int account_id, int level);
+void pc_setstand(struct map_session_data *sd);
+int pc_break_equip(struct map_session_data *sd, unsigned short where);
+int pc_candrop(struct map_session_data *sd,int item_id);
+
+struct pc_base_job{
+ int job; //職業、ただし転生職や養子職の場合は元の職業を返す(廃プリ→プリ)
+ int type; //ノビ 0, 一次職 1, 二次職 2, スパノビ 3
+ int upper; //通常 0, 転生 1, 養子 2
+};
+
+struct pc_base_job pc_calc_base_job(int b_class);//転生や養子職の元の職業を返す
+int pc_calc_base_job2(int b_class); // Celest
+unsigned short pc_jobid2mapid(unsigned short b_class); // Skotlex
+unsigned short pc_mapid2jobid(unsigned short class_, int sex); // Skotlex
+
+char * job_name(int class_);
+
+struct skill_tree_entry {
+ short id;
+ unsigned char max;
+ unsigned char joblv;
+ struct {
+ short id;
+ unsigned char lv;
+ } need[5];
+}; // Celest
+extern struct skill_tree_entry skill_tree[MAX_PC_CLASS][MAX_SKILL_TREE];
+
+int pc_read_gm_account(int fd);
+int pc_setinvincibletimer(struct map_session_data *sd,int);
+int pc_delinvincibletimer(struct map_session_data *sd);
+int pc_addspiritball(struct map_session_data *sd,int,int);
+int pc_delspiritball(struct map_session_data *sd,int,int);
+void pc_addfame(struct map_session_data *sd,int count);
+int pc_istop10fame(int char_id, int job);
+int pc_eventtimer(int tid,unsigned int tick,int id,int data); // for npc_dequeue
+
+int pc_run(struct map_session_data *sd, int skilllv, int dir);
+
+extern struct fame_list smith_fame_list[10];
+extern struct fame_list chemist_fame_list[10];
+extern struct fame_list taekwon_fame_list[10];
+
+int pc_readdb(void);
+int do_init_pc(void);
+void do_final_pc(void);
+
+enum {ADDITEM_EXIST,ADDITEM_NEW,ADDITEM_OVERAMOUNT};
+
+// timer for night.day
+extern int day_timer_tid;
+extern int night_timer_tid;
+int map_day_timer(int,unsigned int,int,int); // by [yor]
+int map_night_timer(int,unsigned int,int,int); // by [yor]
+
+int pc_read_motd(void); // [Valaris]
+
+#endif
diff --git a/src/map/pcre.h b/src/map/pcre.h
new file mode 100644
index 000000000..244e55e0d
--- /dev/null
+++ b/src/map/pcre.h
@@ -0,0 +1,258 @@
+/*************************************************
+* Perl-Compatible Regular Expressions *
+*************************************************/
+
+/* In its original form, this is the .in file that is transformed by
+"configure" into pcre.h.
+
+ Copyright (c) 1997-2005 University of Cambridge
+
+-----------------------------------------------------------------------------
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ * Neither the name of the University of Cambridge nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+-----------------------------------------------------------------------------
+*/
+
+#ifndef _PCRE_H
+#define _PCRE_H
+
+/* The file pcre.h is build by "configure". Do not edit it; instead
+make changes to pcre.in. */
+
+#define PCRE_MAJOR 6
+#define PCRE_MINOR 3
+#define PCRE_DATE 15-Aug-2005
+
+/* Win32 uses DLL by default; it needs special stuff for exported functions. */
+
+#ifdef _WIN32
+# ifdef PCRE_DEFINITION
+# ifdef DLL_EXPORT
+# define PCRE_DATA_SCOPE __declspec(dllexport)
+# endif
+# else
+# ifndef PCRE_STATIC
+# define PCRE_DATA_SCOPE extern __declspec(dllimport)
+# endif
+# endif
+#endif
+
+/* For other operating systems, we use the standard "extern". */
+
+#ifndef PCRE_DATA_SCOPE
+# ifdef __cplusplus
+# define PCRE_DATA_SCOPE extern "C"
+# else
+# define PCRE_DATA_SCOPE extern
+# endif
+#endif
+
+/* Have to include stdlib.h in order to ensure that size_t is defined;
+it is needed here for malloc. */
+
+#include <stdlib.h>
+
+/* Allow for C++ users */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Options */
+
+#define PCRE_CASELESS 0x00000001
+#define PCRE_MULTILINE 0x00000002
+#define PCRE_DOTALL 0x00000004
+#define PCRE_EXTENDED 0x00000008
+#define PCRE_ANCHORED 0x00000010
+#define PCRE_DOLLAR_ENDONLY 0x00000020
+#define PCRE_EXTRA 0x00000040
+#define PCRE_NOTBOL 0x00000080
+#define PCRE_NOTEOL 0x00000100
+#define PCRE_UNGREEDY 0x00000200
+#define PCRE_NOTEMPTY 0x00000400
+#define PCRE_UTF8 0x00000800
+#define PCRE_NO_AUTO_CAPTURE 0x00001000
+#define PCRE_NO_UTF8_CHECK 0x00002000
+#define PCRE_AUTO_CALLOUT 0x00004000
+#define PCRE_PARTIAL 0x00008000
+#define PCRE_DFA_SHORTEST 0x00010000
+#define PCRE_DFA_RESTART 0x00020000
+#define PCRE_FIRSTLINE 0x00040000
+
+/* Exec-time and get/set-time error codes */
+
+#define PCRE_ERROR_NOMATCH (-1)
+#define PCRE_ERROR_NULL (-2)
+#define PCRE_ERROR_BADOPTION (-3)
+#define PCRE_ERROR_BADMAGIC (-4)
+#define PCRE_ERROR_UNKNOWN_NODE (-5)
+#define PCRE_ERROR_NOMEMORY (-6)
+#define PCRE_ERROR_NOSUBSTRING (-7)
+#define PCRE_ERROR_MATCHLIMIT (-8)
+#define PCRE_ERROR_CALLOUT (-9) /* Never used by PCRE itself */
+#define PCRE_ERROR_BADUTF8 (-10)
+#define PCRE_ERROR_BADUTF8_OFFSET (-11)
+#define PCRE_ERROR_PARTIAL (-12)
+#define PCRE_ERROR_BADPARTIAL (-13)
+#define PCRE_ERROR_INTERNAL (-14)
+#define PCRE_ERROR_BADCOUNT (-15)
+#define PCRE_ERROR_DFA_UITEM (-16)
+#define PCRE_ERROR_DFA_UCOND (-17)
+#define PCRE_ERROR_DFA_UMLIMIT (-18)
+#define PCRE_ERROR_DFA_WSSIZE (-19)
+#define PCRE_ERROR_DFA_RECURSE (-20)
+
+/* Request types for pcre_fullinfo() */
+
+#define PCRE_INFO_OPTIONS 0
+#define PCRE_INFO_SIZE 1
+#define PCRE_INFO_CAPTURECOUNT 2
+#define PCRE_INFO_BACKREFMAX 3
+#define PCRE_INFO_FIRSTBYTE 4
+#define PCRE_INFO_FIRSTCHAR 4 /* For backwards compatibility */
+#define PCRE_INFO_FIRSTTABLE 5
+#define PCRE_INFO_LASTLITERAL 6
+#define PCRE_INFO_NAMEENTRYSIZE 7
+#define PCRE_INFO_NAMECOUNT 8
+#define PCRE_INFO_NAMETABLE 9
+#define PCRE_INFO_STUDYSIZE 10
+#define PCRE_INFO_DEFAULT_TABLES 11
+
+/* Request types for pcre_config() */
+
+#define PCRE_CONFIG_UTF8 0
+#define PCRE_CONFIG_NEWLINE 1
+#define PCRE_CONFIG_LINK_SIZE 2
+#define PCRE_CONFIG_POSIX_MALLOC_THRESHOLD 3
+#define PCRE_CONFIG_MATCH_LIMIT 4
+#define PCRE_CONFIG_STACKRECURSE 5
+#define PCRE_CONFIG_UNICODE_PROPERTIES 6
+
+/* Bit flags for the pcre_extra structure */
+
+#define PCRE_EXTRA_STUDY_DATA 0x0001
+#define PCRE_EXTRA_MATCH_LIMIT 0x0002
+#define PCRE_EXTRA_CALLOUT_DATA 0x0004
+#define PCRE_EXTRA_TABLES 0x0008
+
+/* Types */
+
+struct real_pcre; /* declaration; the definition is private */
+typedef struct real_pcre pcre;
+
+/* The structure for passing additional data to pcre_exec(). This is defined in
+such as way as to be extensible. Always add new fields at the end, in order to
+remain compatible. */
+
+typedef struct pcre_extra {
+ unsigned long int flags; /* Bits for which fields are set */
+ void *study_data; /* Opaque data from pcre_study() */
+ unsigned long int match_limit; /* Maximum number of calls to match() */
+ void *callout_data; /* Data passed back in callouts */
+ const unsigned char *tables; /* Pointer to character tables */
+} pcre_extra;
+
+/* The structure for passing out data via the pcre_callout_function. We use a
+structure so that new fields can be added on the end in future versions,
+without changing the API of the function, thereby allowing old clients to work
+without modification. */
+
+typedef struct pcre_callout_block {
+ int version; /* Identifies version of block */
+ /* ------------------------ Version 0 ------------------------------- */
+ int callout_number; /* Number compiled into pattern */
+ int *offset_vector; /* The offset vector */
+ const char *subject; /* The subject being matched */
+ int subject_length; /* The length of the subject */
+ int start_match; /* Offset to start of this match attempt */
+ int current_position; /* Where we currently are in the subject */
+ int capture_top; /* Max current capture */
+ int capture_last; /* Most recently closed capture */
+ void *callout_data; /* Data passed in with the call */
+ /* ------------------- Added for Version 1 -------------------------- */
+ int pattern_position; /* Offset to next item in the pattern */
+ int next_item_length; /* Length of next item in the pattern */
+ /* ------------------------------------------------------------------ */
+} pcre_callout_block;
+
+/* Indirection for store get and free functions. These can be set to
+alternative malloc/free functions if required. Special ones are used in the
+non-recursive case for "frames". There is also an optional callout function
+that is triggered by the (?) regex item. For Virtual Pascal, these definitions
+have to take another form. */
+
+#ifndef VPCOMPAT
+PCRE_DATA_SCOPE void *(*pcre_malloc)(size_t);
+PCRE_DATA_SCOPE void (*pcre_free)(void *);
+PCRE_DATA_SCOPE void *(*pcre_stack_malloc)(size_t);
+PCRE_DATA_SCOPE void (*pcre_stack_free)(void *);
+PCRE_DATA_SCOPE int (*pcre_callout)(pcre_callout_block *);
+#else /* VPCOMPAT */
+PCRE_DATA_SCOPE void *pcre_malloc(size_t);
+PCRE_DATA_SCOPE void pcre_free(void *);
+PCRE_DATA_SCOPE void *pcre_stack_malloc(size_t);
+PCRE_DATA_SCOPE void pcre_stack_free(void *);
+PCRE_DATA_SCOPE int pcre_callout(pcre_callout_block *);
+#endif /* VPCOMPAT */
+
+/* Exported PCRE functions */
+
+PCRE_DATA_SCOPE pcre *pcre_compile(const char *, int, const char **, int *,
+ const unsigned char *);
+PCRE_DATA_SCOPE pcre *pcre_compile2(const char *, int, int *, const char **,
+ int *, const unsigned char *);
+PCRE_DATA_SCOPE int pcre_config(int, void *);
+PCRE_DATA_SCOPE int pcre_copy_named_substring(const pcre *, const char *,
+ int *, int, const char *, char *, int);
+PCRE_DATA_SCOPE int pcre_copy_substring(const char *, int *, int, int, char *,
+ int);
+PCRE_DATA_SCOPE int pcre_dfa_exec(const pcre *, const pcre_extra *,
+ const char *, int, int, int, int *, int , int *, int);
+PCRE_DATA_SCOPE int pcre_exec(const pcre *, const pcre_extra *, const char *,
+ int, int, int, int *, int);
+PCRE_DATA_SCOPE void pcre_free_substring(const char *);
+PCRE_DATA_SCOPE void pcre_free_substring_list(const char **);
+PCRE_DATA_SCOPE int pcre_fullinfo(const pcre *, const pcre_extra *, int,
+ void *);
+PCRE_DATA_SCOPE int pcre_get_named_substring(const pcre *, const char *,
+ int *, int, const char *, const char **);
+PCRE_DATA_SCOPE int pcre_get_stringnumber(const pcre *, const char *);
+PCRE_DATA_SCOPE int pcre_get_substring(const char *, int *, int, int,
+ const char **);
+PCRE_DATA_SCOPE int pcre_get_substring_list(const char *, int *, int,
+ const char ***);
+PCRE_DATA_SCOPE int pcre_info(const pcre *, int *, int *);
+PCRE_DATA_SCOPE const unsigned char *pcre_maketables(void);
+PCRE_DATA_SCOPE int pcre_refcount(pcre *, int);
+PCRE_DATA_SCOPE pcre_extra *pcre_study(const pcre *, int, const char **);
+PCRE_DATA_SCOPE const char *pcre_version(void);
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* End of pcre.h */
diff --git a/src/map/pet.c b/src/map/pet.c
new file mode 100644
index 000000000..48e62cfb4
--- /dev/null
+++ b/src/map/pet.c
@@ -0,0 +1,1973 @@
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "db.h"
+#include "timer.h"
+#include "socket.h"
+#include "nullpo.h"
+#include "malloc.h"
+#include "pc.h"
+#include "status.h"
+#include "map.h"
+#include "intif.h"
+#include "clif.h"
+#include "chrif.h"
+#include "pet.h"
+#include "itemdb.h"
+#include "battle.h"
+#include "mob.h"
+#include "npc.h"
+#include "script.h"
+#include "skill.h"
+#include "showmsg.h"
+
+#define MIN_PETTHINKTIME 100
+
+struct pet_db pet_db[MAX_PET_DB];
+
+static int dirx[8]={0,-1,-1,-1,0,1,1,1};
+static int diry[8]={1,1,0,-1,-1,-1,0,1};
+
+static int pet_timer(int tid,unsigned int tick,int id,int data);
+static int pet_walktoxy_sub(struct pet_data *pd);
+
+static int calc_next_walk_step(struct pet_data *pd)
+{
+ nullpo_retr(0, pd);
+
+ Assert((pd->msd == 0) || (pd->msd->pd == pd));
+
+ if(pd->walkpath.path_pos>=pd->walkpath.path_len)
+ return -1;
+ if(pd->walkpath.path[pd->walkpath.path_pos]&1)
+ return pd->speed*14/10;
+ return pd->speed;
+}
+
+static int pet_performance_val(struct map_session_data *sd)
+{
+ nullpo_retr(0, sd);
+
+ Assert((sd->status.pet_id == 0 || sd->pd == 0) || sd->pd->msd == sd);
+
+ if(sd->pet.intimate > 900)
+ return (sd->petDB->s_perfor > 0)? 4:3;
+ else if(sd->pet.intimate > 750)
+ return 2;
+ else
+ return 1;
+}
+
+int pet_hungry_val(struct map_session_data *sd)
+{
+ nullpo_retr(0, sd);
+
+ Assert((sd->status.pet_id == 0 || sd->pd == 0) || sd->pd->msd == sd);
+
+ if(sd->pet.hungry > 90)
+ return 4;
+ else if(sd->pet.hungry > 75)
+ return 3;
+ else if(sd->pet.hungry > 25)
+ return 2;
+ else if(sd->pet.hungry > 10)
+ return 1;
+ else
+ return 0;
+}
+
+static int pet_can_reach(struct pet_data *pd,int x,int y)
+{
+ struct walkpath_data wpd;
+
+ nullpo_retr(0, pd);
+
+ Assert((pd->msd == 0) || (pd->msd->pd == pd));
+
+ if( pd->bl.x==x && pd->bl.y==y ) // 同じマス
+ return 1;
+
+ // 障害物判定
+ wpd.path_len=0;
+ wpd.path_pos=0;
+ wpd.path_half=0;
+ return (path_search(&wpd,pd->bl.m,pd->bl.x,pd->bl.y,x,y,0)!=-1)?1:0;
+}
+
+static int pet_calc_pos(struct pet_data *pd,int tx,int ty,int dir)
+{
+ int x,y,dx,dy;
+ int i,j=0,k;
+
+ nullpo_retr(0, pd);
+
+ Assert((pd->msd == 0) || (pd->msd->pd == pd));
+
+ pd->to_x = tx;
+ pd->to_y = ty;
+
+ if(dir >= 0 && dir < 8) {
+ dx = -dirx[dir]*2;
+ dy = -diry[dir]*2;
+ x = tx + dx;
+ y = ty + dy;
+ if(!(j=pet_can_reach(pd,x,y))) {
+ if(dx > 0) x--;
+ else if(dx < 0) x++;
+ if(dy > 0) y--;
+ else if(dy < 0) y++;
+ if(!(j=pet_can_reach(pd,x,y))) {
+ for(i=0;i<12;i++) {
+ k = rand()%8;
+ dx = -dirx[k]*2;
+ dy = -diry[k]*2;
+ x = tx + dx;
+ y = ty + dy;
+ if((j=pet_can_reach(pd,x,y)))
+ break;
+ else {
+ if(dx > 0) x--;
+ else if(dx < 0) x++;
+ if(dy > 0) y--;
+ else if(dy < 0) y++;
+ if((j=pet_can_reach(pd,x,y)))
+ break;
+ }
+ }
+ if(!j) {
+ x = tx;
+ y = ty;
+ if(!pet_can_reach(pd,x,y))
+ return 1;
+ }
+ }
+ }
+ }
+ else
+ return 1;
+
+ pd->to_x = x;
+ pd->to_y = y;
+ return 0;
+}
+
+static int pet_unlocktarget(struct pet_data *pd)
+{
+ nullpo_retr(0, pd);
+
+ pd->target_id=0;
+
+ return 0;
+}
+
+static int pet_attack(struct pet_data *pd,unsigned int tick,int data)
+{
+ struct block_list *target;
+
+ short range;
+
+ nullpo_retr(0, pd);
+
+ Assert((pd->msd == 0) || (pd->msd->pd == pd));
+
+ target= map_id2bl(pd->target_id);
+
+ if(!status_check_skilluse(&pd->bl, target, 0, 0))
+ return 0;
+
+ if(target == NULL || pd->bl.m != target->m || target->prev == NULL ||
+ !check_distance_bl(&pd->bl, target, pd->db->range3))
+ {
+ pet_unlocktarget(pd);
+ return 0;
+ }
+
+ range = pd->db->range;
+ if (battle_iswalking(&pd->bl)) range++;
+ if (battle_iswalking(target)) range++;
+ if(!check_distance_bl(&pd->bl, target, range))
+ return 0;
+ if(battle_config.monster_attack_direction_change)
+ pd->dir=map_calc_dir(&pd->bl, target->x,target->y );
+
+ clif_fixpetpos(pd);
+
+ pd->target_lv = battle_weapon_attack(&pd->bl,target,tick,0);
+
+ pd->attackabletime = tick + status_get_adelay(&pd->bl);
+
+ pd->timer=add_timer(pd->attackabletime,pet_timer,pd->bl.id,0);
+ pd->state.state=MS_ATTACK;
+ return 0;
+}
+
+static int petskill_castend(struct pet_data *pd,unsigned int tick,int data);
+static int petskill_castend2(struct pet_data *pd, struct block_list *target, short skill_id, short skill_lv, short skill_x, short skill_y, unsigned int tick);
+
+/*==========================================
+ * Pet Attack Skill [Skotlex]
+ *------------------------------------------
+ */
+static int pet_attackskill(struct pet_data *pd, unsigned int tick, int data)
+{
+
+ struct block_list *bl;
+
+ nullpo_retr(0, pd);
+ Assert((pd->msd == 0) || (pd->msd->pd == pd));
+
+ bl=map_id2bl(pd->target_id);
+ if(bl == NULL || pd->bl.m != bl->m || bl->prev == NULL ||
+ !check_distance_bl(&pd->bl, bl, pd->db->range3))
+ {
+ pet_unlocktarget(pd);
+ return 0;
+ }
+
+ petskill_use(pd, bl, pd->a_skill->id, pd->a_skill->lv, tick);
+ return 0;
+}
+
+struct castend_delay { //[Skotlex] For passing skill info after casting
+ struct pet_data *src;
+ int target;
+ short id;
+ short lv;
+ short x,y;
+};
+
+/*==========================================
+ * Pet Skill Use [Skotlex]
+ *------------------------------------------
+ */
+int petskill_use(struct pet_data *pd, struct block_list *target, short skill_id, short skill_lv, unsigned int tick)
+{
+ int casttime;
+ struct castend_delay *dat;
+
+ nullpo_retr(0, pd);
+ Assert((pd->msd == 0) || (pd->msd->pd == pd));
+
+ if(pd->state.casting_flag)
+ return 1; //Will not interrupt an already casting skill.
+
+ if(!status_check_skilluse(&pd->bl, target, skill_id, 0))
+ return 0; //Cannot target....
+
+ if(pd->timer != -1) //Cancel whatever else the pet is doing.
+ delete_timer(pd->timer, pet_timer);
+
+ if(battle_config.monster_attack_direction_change)
+ pd->dir=map_calc_dir(&pd->bl, target->x, target->y );
+ clif_fixpetpos(pd);
+
+ //Casting time
+ casttime=skill_castfix(&pd->bl, skill_id, skill_lv, 0);
+
+ pet_stop_walking(pd,1);
+ pd->attackabletime = tick;
+ pd->state.state=MS_ATTACK;
+
+ if (casttime > 0)
+ {
+ pd->attackabletime += casttime;
+
+ dat = (struct castend_delay *)aCalloc(1, sizeof(struct castend_delay));
+ dat->src = pd;
+ dat->target = target->id;
+ dat->id = skill_id;
+ dat->lv = skill_lv;
+ dat->x = target->x;
+ dat->y = target->y;
+
+ pd->state.casting_flag = 1;
+ if (skill_get_inf(skill_id) & INF_GROUND_SKILL)
+ clif_skillcasting( &pd->bl, pd->bl.id, 0, dat->x, dat->y, skill_id,casttime);
+ else
+ clif_skillcasting( &pd->bl, pd->bl.id, dat->target, 0,0, skill_id,casttime);
+
+ pd->timer = add_timer(pd->attackabletime,pet_timer,pd->bl.id,(int)dat);
+ } else {
+ petskill_castend2(pd, target, skill_id, skill_lv, target->x, target->y, tick);
+ }
+ return 0;
+}
+
+/*==========================================
+ * Pet Attack Cast End [Skotlex]
+ *------------------------------------------
+ */
+static int petskill_castend(struct pet_data *pd,unsigned int tick,int data)
+{
+ struct castend_delay *dat = (struct castend_delay *)data;
+ struct block_list *target = map_id2bl(dat->target);
+ pd->state.casting_flag = 0;
+ if (dat && pd == dat->src)
+ petskill_castend2(pd, target, dat->id, dat->lv, dat->x, dat->y, tick);
+ aFree(dat);
+ return 0;
+}
+
+/*==========================================
+ * Pet Attack Cast End2 [Skotlex]
+ *------------------------------------------
+ */
+static int petskill_castend2(struct pet_data *pd, struct block_list *target, short skill_id, short skill_lv, short skill_x, short skill_y, unsigned int tick)
+{ //Invoked after the casting time has passed.
+ short delaytime =0;
+
+ nullpo_retr(0, pd);
+
+ pd->state.state=MS_IDLE;
+
+ if (skill_get_inf(skill_id) & INF_GROUND_SKILL)
+ { //Area skill
+ skill_castend_pos2(&pd->bl, skill_x, skill_y, skill_id, skill_lv, tick,0);
+ } else { //Targeted Skill
+ if (!target || !status_check_skilluse(&pd->bl, target, skill_id, 1))
+ return 0;
+ //Skills with inf = 4 (cast on self) have view range (assumed party skills)
+ if(!check_distance_bl(&pd->bl, target,
+ (skill_get_inf(skill_id) & INF_SELF_SKILL?battle_config.area_size:skill_get_range2(&pd->bl, skill_id, skill_lv))))
+ return 0;
+ switch( skill_get_nk(skill_id) )
+ {
+ case NK_NO_DAMAGE:
+ skill_castend_nodamage_id(&pd->bl,target, skill_id, skill_lv,tick, 0);
+ break;
+ case NK_SPLASH_DAMAGE:
+ default:
+ skill_castend_damage_id(&pd->bl,target,skill_id,skill_lv,tick,0);
+ break;
+ }
+ }
+
+ if (pd->timer != -1) //The above skill casting could had changed the state (Abracadabra?)
+ return 0;
+
+ delaytime = skill_delayfix(&pd->bl,skill_id, skill_lv, 0);
+ if (delaytime < MIN_PETTHINKTIME)
+ delaytime = status_get_adelay(&pd->bl);
+ pd->attackabletime = tick + delaytime;
+ if (pd->target_id)
+ { //Resume attacking
+ pd->state.state=MS_ATTACK;
+ pd->timer=add_timer(pd->attackabletime,pet_timer,pd->bl.id,0);
+ }
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+static int pet_walk(struct pet_data *pd,unsigned int tick,int data)
+{
+ int i;
+ int x,y,dx,dy;
+
+ nullpo_retr(0, pd);
+
+ pd->state.state=MS_IDLE;
+ if(pd->walkpath.path_pos >= pd->walkpath.path_len || pd->walkpath.path_pos != data)
+ return 0;
+
+ pd->walkpath.path_half ^= 1;
+ if(pd->walkpath.path_half==0){
+ pd->walkpath.path_pos++;
+ if(pd->state.change_walk_target){
+ pet_walktoxy_sub(pd);
+ return 0;
+ }
+ }
+ else {
+ if(pd->walkpath.path[pd->walkpath.path_pos] >= 8)
+ return 1;
+
+ x = pd->bl.x;
+ y = pd->bl.y;
+
+ pd->dir=pd->walkpath.path[pd->walkpath.path_pos];
+ dx = dirx[pd->dir];
+ dy = diry[pd->dir];
+
+ if(map_getcell(pd->bl.m,x+dx,y+dy,CELL_CHKNOPASS)){
+ pet_walktoxy_sub(pd);
+ return 0;
+ }
+
+ pd->state.state=MS_WALK;
+ map_foreachinmovearea(clif_petoutsight,pd->bl.m,x-AREA_SIZE,y-AREA_SIZE,x+AREA_SIZE,y+AREA_SIZE,dx,dy,BL_PC,pd);
+
+ x += dx;
+ y += dy;
+ map_moveblock(&pd->bl, x, y, tick);
+
+ map_foreachinmovearea(clif_petinsight,pd->bl.m,x-AREA_SIZE,y-AREA_SIZE,x+AREA_SIZE,y+AREA_SIZE,-dx,-dy,BL_PC,pd);
+ pd->state.state=MS_IDLE;
+ }
+ if((i=calc_next_walk_step(pd))>0){
+ i = i>>1;
+ if(i < 1 && pd->walkpath.path_half == 0)
+ i = 1;
+ pd->timer=add_timer(tick+i,pet_timer,pd->bl.id,pd->walkpath.path_pos);
+ pd->state.state=MS_WALK;
+
+ if(pd->walkpath.path_pos >= pd->walkpath.path_len)
+ clif_fixpetpos(pd);
+ }
+ return 0;
+}
+
+int pet_stopattack(struct pet_data *pd)
+{
+ nullpo_retr(0, pd);
+
+ Assert((pd->msd == 0) || (pd->msd->pd == pd));
+
+ pd->target_id=0;
+ if(pd->state.state == MS_ATTACK)
+ pet_changestate(pd,MS_IDLE,0);
+
+ return 0;
+}
+
+int pet_target_check(struct map_session_data *sd,struct block_list *bl,int type)
+{
+ struct pet_data *pd;
+ int rate;
+
+ pd = sd->pd;
+
+ Assert((pd->msd == 0) || (pd->msd->pd == pd));
+
+ if(bl == NULL || bl->type != BL_MOB || bl->prev == NULL ||
+ sd->pet.intimate < battle_config.pet_support_min_friendly ||
+ sd->pet.hungry < 1 ||
+ pd->class_ == status_get_class(bl) ||
+ pd->state.state == MS_DELAY)
+ return 0;
+
+ if(pd->bl.m != bl->m ||
+ !check_distance_bl(&pd->bl, bl, pd->db->range2))
+ return 0;
+
+ if (!status_check_skilluse(&pd->bl, bl, 0, 0))
+ return 0;
+
+ if(!type) {
+ rate = sd->petDB->attack_rate;
+ rate = rate * pd->rate_fix/1000;
+ if(sd->petDB->attack_rate > 0 && rate <= 0)
+ rate = 1;
+ } else {
+ rate = sd->petDB->defence_attack_rate;
+ rate = rate * pd->rate_fix/1000;
+ if(sd->petDB->defence_attack_rate > 0 && rate <= 0)
+ rate = 1;
+ }
+ if(rand()%10000 < rate)
+ {
+ if(pd->target_id == 0 || rand()%10000 < sd->petDB->change_target_rate)
+ pd->target_id = bl->id;
+ }
+
+ return 0;
+}
+/*==========================================
+ * Pet SC Check [Skotlex]
+ *------------------------------------------
+ */
+int pet_sc_check(struct map_session_data *sd, int type)
+{
+ struct pet_data *pd;
+
+ nullpo_retr(0, sd);
+ pd = sd->pd;
+
+ if (pd == NULL ||
+ (battle_config.pet_equip_required && pd->equip == 0) ||
+ pd->recovery == NULL ||
+ pd->recovery->timer != -1 ||
+ pd->recovery->type != type)
+ return 1;
+
+ pd->recovery->timer = add_timer(gettick()+pd->recovery->delay*1000,pet_recovery_timer,sd->bl.id,0);
+
+ return 0;
+}
+
+int pet_changestate(struct pet_data *pd,int state,int type)
+{
+ unsigned int tick;
+ int i;
+
+ nullpo_retr(0, pd);
+
+ Assert((pd->msd == 0) || (pd->msd->pd == pd));
+
+ if (pd->state.casting_flag)
+ skill_castcancel(&pd->bl, 0);
+ if(pd->timer != -1)
+ delete_timer(pd->timer,pet_timer);
+ pd->timer=-1;
+ pd->state.state=state;
+
+ switch(state) {
+ case MS_WALK:
+ if((i=calc_next_walk_step(pd)) > 0){
+ i = i>>2;
+ pd->timer=add_timer(gettick()+i,pet_timer,pd->bl.id,0);
+ } else
+ pd->state.state=MS_IDLE;
+ break;
+ case MS_ATTACK:
+ tick = gettick();
+ i=DIFF_TICK(pd->attackabletime,tick);
+ if(i>0 && i<2000)
+ pd->timer=add_timer(pd->attackabletime,pet_timer,pd->bl.id,0);
+ else
+ pd->timer=add_timer(tick+1,pet_timer,pd->bl.id,0);
+ break;
+ case MS_DELAY:
+ pd->timer=add_timer(gettick()+type,pet_timer,pd->bl.id,0);
+ break;
+ }
+
+ return 0;
+}
+
+static int pet_timer(int tid,unsigned int tick,int id,int data)
+{
+ struct pet_data *pd;
+
+ pd=(struct pet_data*)map_id2bl(id);
+ if(pd == NULL || pd->bl.type != BL_PET)
+ return 1;
+
+ Assert((pd->msd == 0) || (pd->msd->pd == pd));
+
+ if(pd->timer != tid){
+ if(battle_config.error_log)
+ ShowError("pet_timer %d != %d\n",pd->timer,tid);
+ return 0;
+ }
+ pd->timer=-1;
+
+ if(pd->bl.prev == NULL)
+ return 1;
+
+ switch(pd->state.state){
+ case MS_WALK:
+ pet_walk(pd,tick,data);
+ break;
+ case MS_ATTACK:
+ if (pd->msd == NULL) //Is this even possible?
+ break;
+ if (pc_isdead(pd->msd))
+ { //Stop attacking when master died.
+ pet_stopattack(pd);
+ break;
+ }
+ if (pd->state.casting_flag)
+ { //There is a skill being cast.
+ petskill_castend(pd, tick, data);
+ break;
+ }
+ if (battle_config.pet_status_support &&
+ pd->a_skill &&
+ (!battle_config.pet_equip_required || pd->equip > 0) &&
+ (rand()%100 < (pd->a_skill->rate +pd->msd->pet.intimate*pd->a_skill->bonusrate/1000))
+ )
+ { //Skotlex: Use pet's skill
+ pet_attackskill(pd,tick,data);
+ break;
+ }
+ pet_attack(pd,tick,data);
+ break;
+ case MS_DELAY:
+ pet_changestate(pd,MS_IDLE,0);
+ break;
+ default:
+ if(battle_config.error_log)
+ ShowError("pet_timer : %d ?\n",pd->state.state);
+ break;
+ }
+
+ return 0;
+}
+
+static int pet_walktoxy_sub(struct pet_data *pd)
+{
+ struct walkpath_data wpd;
+
+ nullpo_retr(0, pd);
+
+ Assert((pd->msd == 0) || (pd->msd->pd == pd));
+
+ if(path_search(&wpd,pd->bl.m,pd->bl.x,pd->bl.y,pd->to_x,pd->to_y,0))
+ return 1;
+ memcpy(&pd->walkpath,&wpd,sizeof(wpd));
+
+ pd->state.change_walk_target=0;
+ pet_changestate(pd,MS_WALK,0);
+ clif_movepet(pd);
+// if(battle_config.etc_log)
+// printf("walkstart\n");
+
+ return 0;
+}
+
+int pet_walktoxy(struct pet_data *pd,int x,int y)
+{
+ struct walkpath_data wpd;
+
+ nullpo_retr(0, pd);
+
+ Assert((pd->msd == 0) || (pd->msd->pd == pd));
+
+ if(pd->state.state == MS_WALK && path_search(&wpd,pd->bl.m,pd->bl.x,pd->bl.y,x,y,0))
+ return 1;
+
+ pd->to_x=x;
+ pd->to_y=y;
+
+ if(pd->state.state == MS_WALK) {
+ pd->state.change_walk_target=1;
+ } else {
+ return pet_walktoxy_sub(pd);
+ }
+
+ return 0;
+}
+
+int pet_stop_walking(struct pet_data *pd,int type)
+{
+ nullpo_retr(0, pd);
+
+ Assert((pd->msd == 0) || (pd->msd->pd == pd));
+
+ if(pd->state.state == MS_WALK || pd->state.state == MS_IDLE) {
+ pd->walkpath.path_len=0;
+ pd->to_x=pd->bl.x;
+ pd->to_y=pd->bl.y;
+ }
+ if(type&0x01)
+ clif_fixpetpos(pd);
+ if(type&~0xff)
+ pet_changestate(pd,MS_DELAY,type>>8);
+ else
+ pet_changestate(pd,MS_IDLE,0);
+
+ return 0;
+}
+
+static int pet_hungry(int tid,unsigned int tick,int id,int data)
+{
+ struct map_session_data *sd;
+ int interval,t;
+
+
+ sd=map_id2sd(id);
+ if(sd==NULL)
+ return 1;
+
+ Assert((sd->status.pet_id == 0 || sd->pd == 0) || sd->pd->msd == sd);
+
+ if(sd->pet_hungry_timer != tid){
+ if(battle_config.error_log)
+ ShowError("pet_hungry_timer %d != %d\n",sd->pet_hungry_timer,tid);
+ return 0;
+ }
+ sd->pet_hungry_timer = -1;
+ if(!sd->status.pet_id || !sd->pd || !sd->petDB)
+ return 1;
+
+ sd->pet.hungry--;
+ t = sd->pet.intimate;
+ if(sd->pet.hungry < 0) {
+ if(sd->pd->target_id > 0)
+ pet_stopattack(sd->pd);
+ sd->pet.hungry = 0;
+ sd->pet.intimate -= battle_config.pet_hungry_friendly_decrease;
+ if(sd->pet.intimate <= 0) {
+ sd->pet.intimate = 0;
+ if(battle_config.pet_status_support && t > 0) {
+ if(sd->bl.prev != NULL)
+ status_calc_pc(sd,0);
+ else
+ status_calc_pc(sd,2);
+ }
+ }
+ status_calc_pet(sd, 0);
+ clif_send_petdata(sd,1,sd->pet.intimate);
+ }
+ clif_send_petdata(sd,2,sd->pet.hungry);
+
+ if(battle_config.pet_hungry_delay_rate != 100)
+ interval = (sd->petDB->hungry_delay*battle_config.pet_hungry_delay_rate)/100;
+ else
+ interval = sd->petDB->hungry_delay;
+ if(interval <= 0)
+ interval = 1;
+ sd->pet_hungry_timer = add_timer(tick+interval,pet_hungry,sd->bl.id,0);
+
+ return 0;
+}
+
+int search_petDB_index(int key,int type)
+{
+ int i;
+
+ for(i=0;i<MAX_PET_DB;i++) {
+ if(pet_db[i].class_ <= 0)
+ continue;
+ switch(type) {
+ case PET_CLASS:
+ if(pet_db[i].class_ == key)
+ return i;
+ break;
+ case PET_CATCH:
+ if(pet_db[i].itemID == key)
+ return i;
+ break;
+ case PET_EGG:
+ if(pet_db[i].EggID == key)
+ return i;
+ break;
+ case PET_EQUIP:
+ if(pet_db[i].AcceID == key)
+ return i;
+ break;
+ case PET_FOOD:
+ if(pet_db[i].FoodID == key)
+ return i;
+ break;
+ default:
+ return -1;
+ }
+ }
+ return -1;
+}
+
+int pet_hungry_timer_delete(struct map_session_data *sd)
+{
+ nullpo_retr(0, sd);
+
+ if(sd->pet_hungry_timer != -1) {
+ delete_timer(sd->pet_hungry_timer,pet_hungry);
+ sd->pet_hungry_timer = -1;
+ }
+
+ return 0;
+}
+
+int pet_remove_map(struct map_session_data *sd)
+{
+ nullpo_retr(0, sd);
+
+ Assert((sd->status.pet_id == 0 || sd->pd == 0) || sd->pd->msd == sd);
+
+ if(sd->status.pet_id && sd->pd) {
+
+ struct pet_data *pd=sd->pd; // [Valaris]
+ skill_cleartimerskill(&pd->bl); //Just in case pets get a timer-based skill.
+ //[Skotlex] clear bonus data
+ if (pd->status)
+ {
+ aFree(pd->status);
+ pd->status = NULL;
+ }
+ if (pd->a_skill)
+ {
+ aFree(pd->a_skill);
+ pd->a_skill = NULL;
+ }
+ if (pd->s_skill)
+ {
+ if (pd->s_skill->timer != -1)
+ {
+ if (sd->pd->s_skill->id)
+ delete_timer(sd->pd->s_skill->timer, pet_skill_support_timer);
+ else
+ delete_timer(sd->pd->s_skill->timer, pet_heal_timer);
+ }
+ aFree(pd->s_skill);
+ pd->s_skill = NULL;
+ }
+ if(pd->recovery)
+ {
+ if(pd->recovery->timer != -1)
+ delete_timer(pd->recovery->timer, pet_recovery_timer);
+ aFree(pd->recovery);
+ pd->recovery = NULL;
+ }
+ if(pd->bonus)
+ {
+ if (pd->bonus->timer != -1)
+ delete_timer(pd->bonus->timer, pet_skill_bonus_timer);
+ aFree(pd->bonus);
+ pd->bonus = NULL;
+ }
+ if (pd->loot)
+ {
+ if (pd->loot->item)
+ aFree(pd->loot->item);
+ // if (pd->loot->timer != -1)
+ // delete_timer(pd->loot->timer, pet_loot_timer);
+ aFree (pd->loot);
+ pd->loot = NULL;
+ }
+ pd->state.skillbonus=-1;
+ if(sd->state.perfect_hiding) sd->state.perfect_hiding=0; // end additions
+
+ pet_changestate(sd->pd,MS_IDLE,0);
+ if(sd->pet_hungry_timer != -1)
+ pet_hungry_timer_delete(sd);
+ clif_clearchar_area(&sd->pd->bl,0);
+ map_delblock(&sd->pd->bl);
+ map_deliddb(&sd->pd->bl);
+ aFree(sd->pd);
+ sd->pd = NULL;
+ }
+ return 0;
+}
+struct delay_item_drop {
+ int m,x,y;
+ int nameid,amount;
+ struct map_session_data *first_sd,*second_sd,*third_sd;
+};
+
+struct delay_item_drop2 {
+ int m,x,y;
+ struct item item_data;
+ struct map_session_data *first_sd,*second_sd,*third_sd;
+};
+
+int pet_performance(struct map_session_data *sd)
+{
+ struct pet_data *pd;
+
+ nullpo_retr(0, sd);
+ nullpo_retr(0, pd=sd->pd);
+
+ Assert((sd->status.pet_id == 0 || sd->pd == 0) || sd->pd->msd == sd);
+
+ pet_stop_walking(pd,2000<<8);
+ clif_pet_performance(&pd->bl,rand()%pet_performance_val(sd) + 1);
+ // ルートしたItemを落とさせる
+ pet_lootitem_drop(pd,NULL);
+
+ return 0;
+}
+
+int pet_return_egg(struct map_session_data *sd)
+{
+ struct item tmp_item;
+ int flag;
+
+ nullpo_retr(0, sd);
+
+ Assert((sd->status.pet_id == 0 || sd->pd == 0) || sd->pd->msd == sd);
+
+ if(sd->status.pet_id && sd->pd) {
+ // ルートしたItemを落とさせる
+ pet_lootitem_drop(sd->pd,sd);
+ pet_remove_map(sd);
+ sd->status.pet_id = 0;
+ sd->pd = NULL;
+
+ if(sd->petDB == NULL)
+ return 1;
+ memset(&tmp_item,0,sizeof(tmp_item));
+ tmp_item.nameid = sd->petDB->EggID;
+ tmp_item.identify = 1;
+ tmp_item.card[0] = (short)0xff00;
+ tmp_item.card[1] = GetWord(sd->pet.pet_id,0);
+ tmp_item.card[2] = GetWord(sd->pet.pet_id,1);
+ tmp_item.card[3] = sd->pet.rename_flag;
+ if((flag = pc_additem(sd,&tmp_item,1))) {
+ clif_additem(sd,0,0,flag);
+ map_addflooritem(&tmp_item,1,sd->bl.m,sd->bl.x,sd->bl.y,NULL,NULL,NULL,0);
+ }
+ sd->pet.incuvate = 1;
+ if(battle_config.pet_status_support && sd->pet.intimate > 0) {
+ if(sd->bl.prev != NULL)
+ status_calc_pc(sd,0);
+ else
+ status_calc_pc(sd,2);
+ }
+
+ intif_save_petdata(sd->status.account_id,&sd->pet);
+ chrif_save(sd,0); //FIXME: Do we really need to save the char when returning to pet? Seems like a waste, and unexploitable as the pet data is just moved to an item in the inventory. [Skotlex]
+
+ sd->pet.rename_flag = 0; //Prevents future captured pets from starting as "beloved" [Skotlex]
+ sd->petDB = NULL;
+ }
+
+ return 0;
+}
+
+int pet_data_init(struct map_session_data *sd)
+{
+ struct pet_data *pd;
+ int i=0,interval=0;
+
+ nullpo_retr(1, sd);
+
+ Assert((sd->status.pet_id == 0 || sd->pd == 0) || sd->pd->msd == sd);
+
+ if(sd->status.account_id != sd->pet.account_id || sd->status.char_id != sd->pet.char_id ||
+ sd->status.pet_id != sd->pet.pet_id) {
+ sd->status.pet_id = 0;
+ return 1;
+ }
+
+ i = search_petDB_index(sd->pet.class_,PET_CLASS);
+ if(i < 0) {
+ sd->status.pet_id = 0;
+ return 1;
+ }
+ sd->petDB = &pet_db[i];
+ sd->pd = pd = (struct pet_data *)aCalloc(1,sizeof(struct pet_data));
+ pd->bl.m = sd->bl.m;
+ pd->bl.x = pd->to_x = sd->bl.x;
+ pd->bl.y = pd->to_y = sd->bl.y;
+ pet_calc_pos(pd,sd->bl.x,sd->bl.y,sd->dir);
+ pd->bl.x = pd->to_x;
+ pd->bl.y = pd->to_y;
+ pd->bl.id = npc_get_new_npc_id();
+ memcpy(pd->name, sd->pet.name, NAME_LENGTH-1);
+ pd->class_ = sd->pet.class_;
+ pd->db = mob_db(pd->class_);
+ pd->equip = sd->pet.equip;
+ pd->dir = sd->dir;
+ pd->speed = sd->petDB->speed;
+ pd->bl.subtype = MONS;
+ pd->bl.type = BL_PET;
+ pd->state.state = MS_IDLE;
+ pd->timer = -1;
+ pd->next_walktime = pd->attackabletime = pd->last_thinktime = gettick();
+ pd->msd = sd;
+
+ map_addiddb(&pd->bl);
+
+ // initialise
+ if (battle_config.pet_lv_rate) //[Skotlex]
+ pd->status = (struct pet_status *) aCalloc(1,sizeof(struct pet_status));
+
+ status_calc_pet(sd,1);
+
+ pd->state.skillbonus = -1;
+ if (battle_config.pet_status_support) //Skotlex
+ run_script(pet_db[i].script,0,sd->bl.id,0);
+
+ for(i=0;i<MAX_MOBSKILLTIMERSKILL;i++)
+ pd->skilltimerskill[i].timer = -1;
+
+ if(sd->pet_hungry_timer != -1)
+ pet_hungry_timer_delete(sd);
+ if(battle_config.pet_hungry_delay_rate != 100)
+ interval = (sd->petDB->hungry_delay*battle_config.pet_hungry_delay_rate)/100;
+ else
+ interval = sd->petDB->hungry_delay;
+ if(interval <= 0)
+ interval = 1;
+ sd->pet_hungry_timer = add_timer(gettick()+interval,pet_hungry,sd->bl.id,0);
+ return 0;
+}
+
+int pet_birth_process(struct map_session_data *sd)
+{
+ nullpo_retr(1, sd);
+
+ Assert((sd->status.pet_id == 0 || sd->pd == 0) || sd->pd->msd == sd);
+
+ if(sd->status.pet_id && sd->pet.incuvate == 1) {
+ sd->status.pet_id = 0;
+ return 1;
+ }
+
+ sd->pet.incuvate = 0;
+ sd->pet.account_id = sd->status.account_id;
+ sd->pet.char_id = sd->status.char_id;
+ sd->status.pet_id = sd->pet.pet_id;
+ if(pet_data_init(sd)) {
+ sd->status.pet_id = 0;
+ sd->pet.incuvate = 1;
+ sd->pet.account_id = 0;
+ sd->pet.char_id = 0;
+ return 1;
+ }
+
+ intif_save_petdata(sd->status.account_id,&sd->pet);
+ chrif_save(sd,0); //FIXME: As before, is it REALLY Needed to save the char for hatching a pet? [Skotlex]
+
+ map_addblock(&sd->pd->bl);
+ clif_spawnpet(sd->pd);
+ clif_send_petdata(sd,0,0);
+ clif_send_petdata(sd,5,battle_config.pet_hair_style);
+ clif_pet_equip(sd->pd,sd->pet.equip);
+ clif_send_petstatus(sd);
+
+ Assert((sd->status.pet_id == 0 || sd->pd == 0) || sd->pd->msd == sd);
+
+ return 0;
+}
+
+int pet_recv_petdata(int account_id,struct s_pet *p,int flag)
+{
+ struct map_session_data *sd;
+
+ sd = map_id2sd(account_id);
+ if(sd == NULL)
+ return 1;
+ if(flag == 1) {
+ sd->status.pet_id = 0;
+ return 1;
+ }
+ memcpy(&sd->pet,p,sizeof(struct s_pet));
+ if(sd->pet.incuvate == 1)
+ pet_birth_process(sd);
+ else {
+ pet_data_init(sd);
+ if(sd->pd && sd->bl.prev != NULL) {
+ map_addblock(&sd->pd->bl);
+ clif_spawnpet(sd->pd);
+ clif_send_petdata(sd,0,0);
+ clif_send_petdata(sd,5,battle_config.pet_hair_style);
+// clif_pet_equip(sd->pd,sd->pet.equip);
+ clif_send_petstatus(sd);
+ }
+ }
+ if(battle_config.pet_status_support && sd->pet.intimate > 0) {
+ if(sd->bl.prev != NULL)
+ status_calc_pc(sd,0);
+ else
+ status_calc_pc(sd,2);
+ }
+
+ return 0;
+}
+
+int pet_select_egg(struct map_session_data *sd,short egg_index)
+{
+ nullpo_retr(0, sd);
+
+ if(sd->status.inventory[egg_index].card[0] == (short)0xff00)
+ intif_request_petdata(sd->status.account_id, sd->status.char_id, MakeDWord(sd->status.inventory[egg_index].card[1], sd->status.inventory[egg_index].card[2]) );
+ else {
+ if(battle_config.error_log)
+ ShowError("wrong egg item inventory %d\n",egg_index);
+ }
+ pc_delitem(sd,egg_index,1,0);
+
+ return 0;
+}
+
+int pet_catch_process1(struct map_session_data *sd,int target_class)
+{
+ nullpo_retr(0, sd);
+
+ sd->catch_target_class = target_class;
+ clif_catch_process(sd);
+
+ return 0;
+}
+
+int pet_catch_process2(struct map_session_data *sd,int target_id)
+{
+ struct mob_data *md;
+ int i=0,pet_catch_rate=0;
+
+ nullpo_retr(1, sd);
+
+ if (sd->itemid > 0)
+ { //Consume the pet lure [Skotlex]
+ if ((i = sd->itemindex) == -1 ||
+ sd->status.inventory[i].nameid != sd->itemid ||
+ !sd->inventory_data[i]->flag.delay_consume ||
+ sd->status.inventory[i].amount < 1
+ )
+ { //Something went wrong, items moved or they tried an exploit.
+ clif_pet_rulet(sd,0);
+ sd->catch_target_class = -1;
+ return 1;
+ }
+ //Delete the item
+ sd->itemid = sd->itemindex = -1;
+ pc_delitem(sd,i,1,0);
+ }
+
+ md=(struct mob_data*)map_id2bl(target_id);
+ if(!md || md->bl.type != BL_MOB || md->bl.prev == NULL){
+ clif_pet_rulet(sd,0);
+ sd->catch_target_class = -1;
+ return 1;
+ }
+
+ i = search_petDB_index(md->class_,PET_CLASS);
+ //catch_target_class == 0 is used for universal lures. [Skotlex]
+ //for now universal lures do not include bosses.
+ if (sd->catch_target_class == 0 && !(md->db->mode&0x20))
+ sd->catch_target_class = md->class_;
+ if(i < 0 || sd->catch_target_class != md->class_) {
+ clif_emotion(&md->bl, 7); //mob will do /ag if wrong lure is used on them.
+ clif_pet_rulet(sd,0);
+ sd->catch_target_class = -1;
+ return 1;
+ }
+
+ //target_idによる敵→卵判定
+// if(battle_config.etc_log)
+// printf("mob_id = %d, mob_class = %d\n",md->bl.id,md->class_);
+ //成功の場合
+ pet_catch_rate = (pet_db[i].capture + (sd->status.base_level - md->db->lv)*30 + sd->paramc[5]*20)*(200 - md->hp*100/md->db->max_hp)/100;
+ if(pet_catch_rate < 1) pet_catch_rate = 1;
+ if(battle_config.pet_catch_rate != 100)
+ pet_catch_rate = (pet_catch_rate*battle_config.pet_catch_rate)/100;
+
+ if(rand()%10000 < pet_catch_rate) {
+ mob_remove_map(md,0);
+ mob_setdelayspawn(md->bl.id);
+ clif_pet_rulet(sd,1);
+// if(battle_config.etc_log)
+// printf("rulet success %d\n",target_id);
+ intif_create_pet(sd->status.account_id,sd->status.char_id,pet_db[i].class_,mob_db(pet_db[i].class_)->lv,
+ pet_db[i].EggID,0,pet_db[i].intimate,100,0,1,pet_db[i].jname);
+ }
+ else
+ {
+ sd->catch_target_class = -1;
+ clif_pet_rulet(sd,0);
+ }
+
+ return 0;
+}
+
+int pet_get_egg(int account_id,int pet_id,int flag)
+{ //This function is invoked when a new pet has been created, and at no other time!
+ struct map_session_data *sd;
+ struct item tmp_item;
+ int i=0,ret=0;
+
+ if(!flag) {
+ sd = map_id2sd(account_id);
+ if(sd == NULL)
+ return 1;
+
+ i = search_petDB_index(sd->catch_target_class,PET_CLASS);
+ sd->catch_target_class = -1;
+
+ if(i >= 0) {
+ memset(&tmp_item,0,sizeof(tmp_item));
+ tmp_item.nameid = pet_db[i].EggID;
+ tmp_item.identify = 1;
+ tmp_item.card[0] = (short)0xff00;
+ tmp_item.card[1] = GetWord(pet_id,0);
+ tmp_item.card[2] = GetWord(pet_id,1);
+ tmp_item.card[3] = 0; //New pets are not named.
+ if((ret = pc_additem(sd,&tmp_item,1))) {
+ clif_additem(sd,0,0,ret);
+ map_addflooritem(&tmp_item,1,sd->bl.m,sd->bl.x,sd->bl.y,NULL,NULL,NULL,0);
+ }
+ }
+ else
+ intif_delete_petdata(pet_id);
+ }
+
+ return 0;
+}
+
+int pet_menu(struct map_session_data *sd,int menunum)
+{
+ nullpo_retr(0, sd);
+ if (sd->pd == NULL)
+ return 1;
+
+ switch(menunum) {
+ case 0:
+ clif_send_petstatus(sd);
+ break;
+ case 1:
+ pet_food(sd);
+ break;
+ case 2:
+ pet_performance(sd);
+ break;
+ case 3:
+ pet_return_egg(sd);
+ break;
+ case 4:
+ pet_unequipitem(sd);
+ break;
+ }
+ return 0;
+}
+
+int pet_change_name(struct map_session_data *sd,char *name)
+{
+ int i;
+
+ nullpo_retr(1, sd);
+
+ if((sd->pd == NULL) || (sd->pet.rename_flag == 1 && battle_config.pet_rename == 0))
+ return 1;
+
+ for(i=0;i<NAME_LENGTH && name[i];i++){
+ if( !(name[i]&0xe0) || name[i]==0x7f)
+ return 1;
+ }
+
+ pet_stop_walking(sd->pd,1);
+
+ memcpy(sd->pet.name, name, NAME_LENGTH-1);
+ memcpy(sd->pd->name, name, NAME_LENGTH-1);
+
+ clif_clearchar_area(&sd->pd->bl,0);
+ clif_spawnpet(sd->pd);
+ clif_send_petdata(sd,0,0);
+ clif_send_petdata(sd,5,battle_config.pet_hair_style);
+ sd->pet.rename_flag = 1;
+ clif_pet_equip(sd->pd,sd->pet.equip);
+ clif_send_petstatus(sd);
+
+ return 0;
+}
+
+int pet_equipitem(struct map_session_data *sd,int index)
+{
+ int nameid;
+
+ nullpo_retr(1, sd);
+
+ nameid = sd->status.inventory[index].nameid;
+ if(sd->petDB == NULL)
+ return 1;
+ if(sd->petDB->AcceID == 0 || nameid != sd->petDB->AcceID || sd->pet.equip != 0) {
+ clif_equipitemack(sd,0,0,0);
+ return 1;
+ }
+ else {
+ pc_delitem(sd,index,1,0);
+ sd->pet.equip = sd->pd->equip = nameid;
+ status_calc_pc(sd,0);
+ clif_pet_equip(sd->pd,nameid);
+ if (battle_config.pet_equip_required)
+ { //Skotlex: start support timers if needd
+ if (sd->pd->s_skill && sd->pd->s_skill->timer == -1)
+ {
+ if (sd->pd->s_skill->id)
+ sd->pd->s_skill->timer=add_timer(gettick()+sd->pd->s_skill->delay*1000, pet_skill_support_timer, sd->bl.id, 0);
+ else
+ sd->pd->s_skill->timer=add_timer(gettick()+sd->pd->s_skill->delay*1000, pet_heal_timer, sd->bl.id, 0);
+ }
+ if (sd->pd->bonus && sd->pd->bonus->timer == -1)
+ sd->pd->bonus->timer=add_timer(gettick()+sd->pd->bonus->delay*1000, pet_skill_bonus_timer, sd->bl.id, 0);
+ }
+ }
+
+ return 0;
+}
+
+int pet_unequipitem(struct map_session_data *sd)
+{
+ struct item tmp_item;
+ int nameid,flag;
+
+ nullpo_retr(1, sd);
+
+ if(sd->petDB == NULL)
+ return 1;
+ if(sd->pet.equip == 0)
+ return 1;
+
+ nameid = sd->pet.equip;
+ sd->pet.equip = sd->pd->equip = 0;
+ status_calc_pc(sd,0);
+ clif_pet_equip(sd->pd,0);
+ memset(&tmp_item,0,sizeof(tmp_item));
+ tmp_item.nameid = nameid;
+ tmp_item.identify = 1;
+ if((flag = pc_additem(sd,&tmp_item,1))) {
+ clif_additem(sd,0,0,flag);
+ map_addflooritem(&tmp_item,1,sd->bl.m,sd->bl.x,sd->bl.y,NULL,NULL,NULL,0);
+ }
+ if (battle_config.pet_equip_required)
+ { //Skotlex: halt support timers if needed
+ if (sd->pd->s_skill && sd->pd->s_skill->timer != -1)
+ {
+ if (sd->pd->s_skill->id)
+ delete_timer(sd->pd->s_skill->timer, pet_skill_support_timer);
+ else
+ delete_timer(sd->pd->s_skill->timer, pet_heal_timer);
+ sd->pd->s_skill->timer = -1;
+ }
+ if (sd->pd->bonus && sd->pd->bonus->timer != -1)
+ {
+ delete_timer(sd->pd->bonus->timer, pet_skill_bonus_timer);
+ sd->pd->bonus->timer = -1;
+ }
+ }
+
+ return 0;
+}
+
+int pet_food(struct map_session_data *sd)
+{
+ int i,k,t;
+
+ nullpo_retr(1, sd);
+
+ Assert((sd->status.pet_id == 0 || sd->pd == 0) || sd->pd->msd == sd);
+
+ if(sd->petDB == NULL)
+ return 1;
+ i=pc_search_inventory(sd,sd->petDB->FoodID);
+ if(i < 0) {
+ clif_pet_food(sd,sd->petDB->FoodID,0);
+ return 1;
+ }
+ pc_delitem(sd,i,1,0);
+ t = sd->pet.intimate;
+ if(sd->pet.hungry > 90)
+ sd->pet.intimate -= sd->petDB->r_full;
+ else if(sd->pet.hungry > 75) {
+ if(battle_config.pet_friendly_rate != 100)
+ k = (sd->petDB->r_hungry * battle_config.pet_friendly_rate)/100;
+ else
+ k = sd->petDB->r_hungry;
+ k = k >> 1;
+ if(k <= 0)
+ k = 1;
+ sd->pet.intimate += k;
+ }
+ else {
+ if(battle_config.pet_friendly_rate != 100)
+ k = (sd->petDB->r_hungry * battle_config.pet_friendly_rate)/100;
+ else
+ k = sd->petDB->r_hungry;
+ sd->pet.intimate += k;
+ }
+ if(sd->pet.intimate <= 0) {
+ sd->pet.intimate = 0;
+ if(battle_config.pet_status_support && t > 0) {
+ if(sd->bl.prev != NULL)
+ status_calc_pc(sd,0);
+ else
+ status_calc_pc(sd,2);
+ }
+ }
+ else if(sd->pet.intimate > 1000)
+ sd->pet.intimate = 1000;
+ status_calc_pet(sd, 0);
+ sd->pet.hungry += sd->petDB->fullness;
+ if(sd->pet.hungry > 100)
+ sd->pet.hungry = 100;
+
+ clif_send_petdata(sd,2,sd->pet.hungry);
+ clif_send_petdata(sd,1,sd->pet.intimate);
+ clif_pet_food(sd,sd->petDB->FoodID,1);
+
+ return 0;
+}
+
+static int pet_randomwalk(struct pet_data *pd,int tick)
+{
+ const int retrycount=20;
+ int speed;
+
+ nullpo_retr(0, pd);
+
+ Assert((pd->msd == 0) || (pd->msd->pd == pd));
+
+ speed = status_get_speed(&pd->bl);
+
+ if(DIFF_TICK(pd->next_walktime,tick) < 0){
+ int i,x,y,c,d=12-pd->move_fail_count;
+ if(d<5) d=5;
+ for(i=0;i<retrycount;i++){
+ int r=rand();
+ x=pd->bl.x+r%(d*2+1)-d;
+ y=pd->bl.y+r/(d*2+1)%(d*2+1)-d;
+ if((map_getcell(pd->bl.m,x,y,CELL_CHKPASS))&&( pet_walktoxy(pd,x,y)==0)){
+ pd->move_fail_count=0;
+ break;
+ }
+ if(i+1>=retrycount){
+ pd->move_fail_count++;
+ if(pd->move_fail_count>1000){
+ if(battle_config.error_log)
+ ShowWarning("PET cant move. hold position %d, class = %d\n",pd->bl.id,pd->class_);
+ pd->move_fail_count=0;
+ pet_changestate(pd,MS_DELAY,60000);
+ return 0;
+ }
+ }
+ }
+ for(i=c=0;i<pd->walkpath.path_len;i++){
+ if(pd->walkpath.path[i]&1)
+ c+=speed*14/10;
+ else
+ c+=speed;
+ }
+ pd->next_walktime = tick+rand()%3000+3000+c;
+
+ return 1;
+ }
+ return 0;
+}
+
+static int pet_ai_sub_hard(struct pet_data *pd,unsigned int tick)
+{
+ struct map_session_data *sd = pd->msd;
+ struct block_list *bl = NULL;
+ int dist,i=0,dx,dy,ret;
+ int mode,race;
+
+ nullpo_retr(0, pd);
+
+ sd = pd->msd;
+
+ Assert((sd->status.pet_id == 0 || sd->pd == 0) || sd->pd->msd == sd);
+
+ if(pd->bl.prev == NULL || sd == NULL || sd->bl.prev == NULL)
+ return 0;
+
+ if(DIFF_TICK(tick,pd->last_thinktime) < MIN_PETTHINKTIME)
+ return 0;
+ pd->last_thinktime=tick;
+
+ if(pd->state.state == MS_DELAY || pd->bl.m != sd->bl.m)
+ return 0;
+ // ペットによるルート
+ if(!pd->target_id && pd->loot && pd->loot->count < pd->loot->max && DIFF_TICK(gettick(),pd->loot->timer)>0)
+ map_foreachinarea(pet_ai_sub_hard_lootsearch,pd->bl.m,
+ pd->bl.x-6,pd->bl.y-6, //If pet_ai_sub_hard_lootsearch limits itself to a range of 5, WHY use AREA_SIZE here? o.O [Skotlex]
+ pd->bl.x+6,pd->bl.y+6,
+// pd->bl.x-AREA_SIZE*2,pd->bl.y-AREA_SIZE*2,
+// pd->bl.x+AREA_SIZE*2,pd->bl.y+AREA_SIZE*2,
+ BL_ITEM,pd,&i);
+
+ if(sd->pet.intimate > 0) {
+ dist = distance_bl(&sd->bl, &pd->bl);
+ if(dist > 12) {
+ if(pd->target_id > 0)
+ pet_unlocktarget(pd);
+ if(pd->timer != -1 && pd->state.state == MS_WALK && check_distance_blxy(&sd->bl, pd->to_x, pd->to_y, 3))
+ return 0;
+ pd->speed = (sd->speed>>1);
+ if(pd->speed <= 0)
+ pd->speed = 1;
+ pet_calc_pos(pd,sd->bl.x,sd->bl.y,sd->dir);
+ if(pet_walktoxy(pd,pd->to_x,pd->to_y))
+ pet_randomwalk(pd,tick);
+ }
+ else if(pd->target_id - MAX_FLOORITEM > 0) { //Mob targeted
+ mode=pd->db->mode;
+ race=pd->db->race;
+ bl= map_id2bl(pd->target_id);
+ if(bl == NULL || pd->bl.m != bl->m || bl->prev == NULL ||
+ !check_distance_bl(&pd->bl, bl, pd->db->range3))
+ pet_unlocktarget(pd);
+ else if(!battle_check_range(&pd->bl,bl,pd->db->range) && !pd->state.casting_flag){ //Skotlex Don't interrupt a casting spell when targed moved
+ if(pd->timer != -1 && pd->state.state == MS_WALK && check_distance_blxy(bl, pd->to_x, pd->to_y, 2))
+ return 0;
+ if(!pet_can_reach(pd, bl->x, bl->y))
+ pet_unlocktarget(pd);
+ else {
+ i=0;
+ pd->speed = status_get_speed(&pd->bl);
+ do {
+ if(i==0) { // 最初はAEGISと同じ方法で検索
+ dx=bl->x - pd->bl.x;
+ dy=bl->y - pd->bl.y;
+ if(dx<0) dx++;
+ else if(dx>0) dx--;
+ if(dy<0) dy++;
+ else if(dy>0) dy--;
+ }
+ else { // だめならAthena式(ランダム)
+ dx=bl->x - pd->bl.x + rand()%3 - 1;
+ dy=bl->y - pd->bl.y + rand()%3 - 1;
+ }
+ ret=pet_walktoxy(pd,pd->bl.x+dx,pd->bl.y+dy);
+ i++;
+ } while(ret && i<5);
+
+ if(ret) { // 移動不可能な所からの攻撃なら2歩下る
+ if(dx<0) dx=2;
+ else if(dx>0) dx=-2;
+ if(dy<0) dy=2;
+ else if(dy>0) dy=-2;
+ pet_walktoxy(pd,pd->bl.x+dx,pd->bl.y+dy);
+ }
+ }
+ }
+ else {
+ if(pd->state.state==MS_WALK)
+ pet_stop_walking(pd,1);
+ if(pd->state.state==MS_ATTACK)
+ return 0;
+ pet_changestate(pd,MS_ATTACK,0);
+ }
+ }
+ else if(pd->target_id > 0 && pd->loot){ //Item Targeted, attempt loot
+ struct block_list *bl_item;
+ struct flooritem_data *fitem;
+
+ bl_item = map_id2bl(pd->target_id);
+ if(bl_item == NULL || bl_item->type != BL_ITEM ||bl_item->m != pd->bl.m ||
+ (dist=distance_bl(&pd->bl, bl_item))>=5){
+ // 遠すぎるかアイテムがなくなった
+ pet_unlocktarget(pd);
+ }
+ else if(dist){
+ if(pd->timer != -1 && pd->state.state!=MS_ATTACK && (DIFF_TICK(pd->next_walktime,tick)<0 || !check_distance_blxy(bl_item, pd->to_x, pd->to_y, 0)))
+ return 0; // 既に移動中
+
+ pd->next_walktime=tick+500;
+ dx=bl_item->x - pd->bl.x;
+ dy=bl_item->y - pd->bl.y;
+
+ ret=pet_walktoxy(pd,pd->bl.x+dx,pd->bl.y+dy);
+ }
+ else{ // アイテムまでたどり着いた
+ fitem = (struct flooritem_data *)bl_item;
+ if(pd->state.state==MS_ATTACK)
+ return 0; // 攻撃中
+ if(pd->state.state==MS_WALK){ // 歩行中なら停止
+ pet_stop_walking(pd,1);
+ }
+ if(pd->loot->count < pd->loot->max){
+ memcpy(&pd->loot->item[pd->loot->count++],&fitem->item_data,sizeof(pd->loot->item[0]));
+ pd->loot->weight += itemdb_search(fitem->item_data.nameid)->weight*fitem->item_data.amount;
+ map_clearflooritem(bl_item->id);
+ pet_unlocktarget(pd);
+ }
+ else { //Maxed out on carried items
+ pet_unlocktarget(pd);
+ return 0;
+ }
+ }
+ }
+ else {
+ if(dist <= 3 || pd->state.casting_flag || (pd->timer != -1 && pd->state.state == MS_WALK && check_distance_blxy(&sd->bl, pd->to_x,pd->to_y, 3)))
+ return 0;
+ pd->speed = status_get_speed(&pd->bl);
+ pet_calc_pos(pd,sd->bl.x,sd->bl.y,sd->dir);
+ if(pet_walktoxy(pd,pd->to_x,pd->to_y))
+ pet_randomwalk(pd,tick);
+ }
+ }
+ else {
+ pd->speed = status_get_speed(&pd->bl);
+ if(pd->state.state == MS_ATTACK)
+ pet_stopattack(pd);
+ pet_randomwalk(pd,tick);
+ }
+
+ return 0;
+}
+
+static int pet_ai_sub_foreachclient(struct map_session_data *sd,va_list ap)
+{
+ unsigned int tick;
+
+ nullpo_retr(0, sd);
+ nullpo_retr(0, ap);
+
+ tick=va_arg(ap,unsigned int);
+ if(sd->status.pet_id && sd->pd && sd->petDB)
+ pet_ai_sub_hard(sd->pd,tick);
+
+ return 0;
+}
+
+static int pet_ai_hard(int tid,unsigned int tick,int id,int data)
+{
+ clif_foreachclient(pet_ai_sub_foreachclient,tick);
+
+ return 0;
+}
+
+int pet_ai_sub_hard_lootsearch(struct block_list *bl,va_list ap)
+{
+ struct pet_data* pd;
+ int *itc;
+
+ nullpo_retr(0, bl);
+ nullpo_retr(0, ap);
+ nullpo_retr(0, pd=va_arg(ap,struct pet_data *));
+ nullpo_retr(0, itc=va_arg(ap,int *));
+
+ if(!pd->target_id){
+ struct flooritem_data *fitem = (struct flooritem_data *)bl;
+ struct map_session_data *sd = NULL;
+ // ルート権無し
+ if(fitem && fitem->first_get_id>0)
+ sd = map_id2sd(fitem->first_get_id);
+ // Removed [Valaris]
+ //if((pd->lootitem_weight + (itemdb_search(fitem->item_data.))->weight * fitem->item_data.amount) > battle_config.pet_weight)
+ // return 0;
+
+ if(pd->loot == NULL || pd->loot->item == NULL || (pd->loot->count >= pd->loot->max) || (sd && sd->pd != pd))
+ return 0;
+ if(bl->m == pd->bl.m && check_distance_bl(&pd->bl, bl, 5)){
+ if( pet_can_reach(pd,bl->x,bl->y) // 到達可能性判定
+ && rand()%1000<1000/(++(*itc)) ){ // 範囲内PCで等確率にする
+ pd->target_id=bl->id;
+ }
+ }
+ }
+ return 0;
+}
+int pet_lootitem_drop(struct pet_data *pd,struct map_session_data *sd)
+{
+ int i,flag=0;
+
+ if(pd){
+ if(pd->loot) {
+ for(i=0;i<pd->loot->count;i++) {
+ struct delay_item_drop2 *ditem;
+
+ ditem=(struct delay_item_drop2 *)aCalloc(1,sizeof(struct delay_item_drop2));
+ memcpy(&ditem->item_data,&pd->loot->item[i],sizeof(pd->loot->item[0]));
+ ditem->m = pd->bl.m;
+ ditem->x = pd->bl.x;
+ ditem->y = pd->bl.y;
+ ditem->first_sd = 0;
+ ditem->second_sd = 0;
+ ditem->third_sd = 0;
+ // 落とさないで直接PCのItem欄へ
+ if(sd){
+ if((flag = pc_additem(sd,&ditem->item_data,ditem->item_data.amount))){
+ clif_additem(sd,0,0,flag);
+ map_addflooritem(&ditem->item_data,ditem->item_data.amount,ditem->m,ditem->x,ditem->y,ditem->first_sd,ditem->second_sd,ditem->third_sd,0);
+ }
+ aFree(ditem);
+ }
+ else
+ add_timer(gettick()+540+i,pet_delay_item_drop2,(int)ditem,0);
+ }
+ //The smart thing to do is use pd->loot->max (thanks for pointing it out, Shinomori)
+ memset(pd->loot->item,0,pd->loot->max * sizeof(struct item));
+ pd->loot->count = 0;
+ pd->loot->weight = 0;
+ pd->loot->timer = gettick()+10000; // 10*1000msの間拾わない
+ }
+ }
+ return 1;
+}
+
+int pet_delay_item_drop2(int tid,unsigned int tick,int id,int data)
+{
+ struct delay_item_drop2 *ditem;
+
+ ditem=(struct delay_item_drop2 *)id;
+
+ map_addflooritem(&ditem->item_data,ditem->item_data.amount,ditem->m,ditem->x,ditem->y,ditem->first_sd,ditem->second_sd,ditem->third_sd,0);
+
+ aFree(ditem);
+ return 0;
+}
+
+/*==========================================
+ * pet bonus giving skills [Valaris] / Rewritten by [Skotlex]
+ *------------------------------------------
+ */
+int pet_skill_bonus_timer(int tid,unsigned int tick,int id,int data)
+{
+ struct map_session_data *sd=map_id2sd(id);
+ struct pet_data *pd;
+ int timer = 0;
+
+ if(sd == NULL || sd->pd==NULL || sd->pd->bonus == NULL)
+ return 1;
+
+ pd=sd->pd;
+
+ if(pd->bonus->timer != tid) {
+ if(battle_config.error_log)
+ {
+ ShowError("pet_skill_bonus_timer %d != %d\n",pd->bonus->timer,tid);
+ pd->bonus->timer = -1;
+ }
+ return 0;
+ }
+
+ // determine the time for the next timer
+ if (pd->state.skillbonus == 0) {
+ // pet bonuses are not active at the moment, so,
+ pd->state.skillbonus = 1;
+ timer = pd->bonus->duration*1000; // the duration for pet bonuses to be in effect
+ } else if (pd->state.skillbonus == 1) {
+ // pet bonuses are already active, so,
+ pd->state.skillbonus = 0;
+ timer = pd->bonus->delay*1000; // the duration until pet bonuses will be reactivated again
+ if (timer <= 0) //Always active bonus
+ timer = MIN_PETTHINKTIME;
+ }
+
+ // add/remove our bonuses
+ status_calc_pc(sd, 0);
+
+ // wait for the next timer
+ pd->bonus->timer=add_timer(tick+timer,pet_skill_bonus_timer,sd->bl.id,0);
+
+ return 0;
+}
+
+/*==========================================
+ * pet recovery skills [Valaris] / Rewritten by [Skotlex]
+ *------------------------------------------
+ */
+int pet_recovery_timer(int tid,unsigned int tick,int id,int data)
+{
+ struct map_session_data *sd=(struct map_session_data*)map_id2bl(id);
+ struct pet_data *pd;
+
+ if(sd==NULL || sd->pd == NULL || sd->pd->recovery == NULL)
+ return 1;
+
+ pd=sd->pd;
+
+ if(pd->recovery == NULL || pd->recovery->timer != tid) {
+ if(battle_config.error_log)
+ {
+ if (pd->recovery)
+ ShowError("pet_recovery_timer %d != %d\n",pd->recovery->timer,tid);
+ else
+ ShowError("pet_recovery_timer called with no recovery skill defined (tid=%d)\n",tid);
+ }
+ return 0;
+ }
+
+ if(sd->sc_data && sd->sc_data[pd->recovery->type].timer != -1)
+ { //Display a heal animation?
+ //Detoxify is chosen for now.
+ clif_skill_nodamage(&pd->bl,&sd->bl,TF_DETOXIFY,1,1);
+ status_change_end(&sd->bl,pd->recovery->type,-1);
+ clif_emotion(&pd->bl, 33);
+ }
+
+ pd->recovery->timer = -1;
+
+ return 0;
+}
+
+int pet_heal_timer(int tid,unsigned int tick,int id,int data)
+{
+ struct map_session_data *sd=(struct map_session_data*)map_id2bl(id);
+ struct pet_data *pd;
+ short rate = 100;
+
+ if(sd==NULL || sd->bl.type!=BL_PC || sd->pd == NULL)
+ return 1;
+
+ pd=sd->pd;
+
+ if(pd->s_skill == NULL || pd->s_skill->timer != tid) {
+ if(battle_config.error_log)
+ {
+ if (pd->s_skill)
+ ShowError("pet_heal_timer %d != %d\n",pd->s_skill->timer,tid);
+ else
+ ShowError("pet_heal_timer called with no support skill defined (tid=%d)\n",tid);
+ }
+ return 0;
+ }
+
+ if(pc_isdead(sd) ||
+ (rate = sd->status.sp*100/sd->status.max_sp) > pd->s_skill->sp ||
+ (rate = sd->status.hp*100/sd->status.max_hp) > pd->s_skill->hp ||
+ (rate = pd->state.casting_flag) || //Another skill is in effect
+ (rate = pd->state.state) == MS_WALK) //Better wait until the pet stops moving (MS_WALK is 2)
+ { //Wait (how long? 1 sec for every 10% of remaining)
+ pd->s_skill->timer=add_timer(gettick()+(rate>10?rate:10)*100,pet_heal_timer,sd->bl.id,0);
+ return 0;
+ }
+
+ if (pd->state.state == MS_ATTACK)
+ pet_stopattack(pd);
+ clif_skill_nodamage(&pd->bl,&sd->bl,AL_HEAL,pd->s_skill->lv,1);
+ pc_heal(sd,pd->s_skill->lv,0);
+
+ pd->s_skill->timer=add_timer(tick+pd->s_skill->delay*1000,pet_heal_timer,sd->bl.id,0);
+
+ return 0;
+}
+
+/*==========================================
+ * pet support skills [Skotlex]
+ *------------------------------------------
+ */
+int pet_skill_support_timer(int tid,unsigned int tick,int id,int data)
+{
+ struct map_session_data *sd=(struct map_session_data*)map_id2bl(id);
+ struct pet_data *pd;
+ short rate = 100;
+ if(sd==NULL || sd->bl.type!=BL_PC || sd->pd == NULL)
+ return 1;
+
+ pd=sd->pd;
+
+ if(pd->s_skill == NULL || pd->s_skill->timer != tid) {
+ if(battle_config.error_log)
+ {
+ if (pd->s_skill)
+ ShowError("pet_skill_support_timer %d != %d\n",pd->s_skill->timer,tid);
+ else
+ ShowError("pet_skill_support_timer called with no support skill defined (tid=%d)\n",tid);
+ }
+ return 0;
+ }
+
+ if(pc_isdead(sd) ||
+ (rate = sd->status.sp*100/sd->status.max_sp) > pd->s_skill->sp ||
+ (rate = sd->status.hp*100/sd->status.max_hp) > pd->s_skill->hp ||
+ (rate = pd->state.casting_flag) || //Another skill is in effect
+ (rate = pd->state.state) == MS_WALK) //Better wait until the pet stops moving (MS_WALK is 2)
+ { //Wait (how long? 1 sec for every 10% of remaining)
+ pd->s_skill->timer=add_timer(gettick()+(rate>10?rate:10)*100,pet_skill_support_timer,sd->bl.id,0);
+ return 0;
+ }
+
+ if (pd->state.state == MS_ATTACK)
+ pet_stopattack(pd);
+ petskill_use(pd, &sd->bl, pd->s_skill->id, pd->s_skill->lv, tick);
+
+ pd->s_skill->timer=add_timer(tick+pd->s_skill->delay*1000,pet_skill_support_timer,sd->bl.id,0);
+
+ return 0;
+}
+
+/*==========================================
+ *ペットデータ読み込み
+ *------------------------------------------
+ */
+int read_petdb()
+{
+ FILE *fp;
+ char line[1024];
+ int nameid,i,k;
+ int j=0;
+ int lines;
+ char *filename[]={"pet_db.txt","pet_db2.txt"};
+ char *str[32],*p,*np;
+
+
+//Remove any previous scripts in case reloaddb was invoked.
+ for(j =0; j < MAX_PET_DB; j++)
+ if (pet_db[j].script) {
+ aFree(pet_db[j].script);
+ pet_db[j].script = NULL;
+ }
+ j = 0;
+ memset(pet_db,0,sizeof(pet_db));
+ for(i=0;i<2;i++){
+ sprintf(line, "%s/%s", db_path, filename[i]);
+ fp=fopen(line,"r");
+ if(fp==NULL){
+ if(i>0)
+ continue;
+ ShowError("can't read %s\n",line);
+ return -1;
+ }
+ lines = 0;
+ while(fgets(line,1020,fp) && j < MAX_PET_DB){
+
+ lines++;
+
+ if(line[0] == '/' && line[1] == '/')
+ continue;
+
+ for(k=0,p=line;k<20;k++){
+ if((np=strchr(p,','))!=NULL){
+ str[k]=p;
+ *np=0;
+ p=np+1;
+ } else {
+ str[k]=p;
+ p+=strlen(p);
+ }
+ }
+
+ nameid=atoi(str[0]);
+ if(nameid<=0 || nameid>2000)
+ continue;
+
+ //MobID,Name,JName,ItemID,EggID,AcceID,FoodID,"Fullness (1回の餌での満腹度増加率%)","HungryDeray (/min)","R_Hungry (空腹時餌やり親密度増加率%)","R_Full (とても満腹時餌やり親密度減少率%)","Intimate (捕獲時親密度%)","Die (死亡時親密度減少率%)","Capture (捕獲率%)",(Name)
+ pet_db[j].class_ = nameid;
+ memcpy(pet_db[j].name,str[1],NAME_LENGTH-1);
+ memcpy(pet_db[j].jname,str[2],NAME_LENGTH-1);
+ pet_db[j].itemID=atoi(str[3]);
+ pet_db[j].EggID=atoi(str[4]);
+ pet_db[j].AcceID=atoi(str[5]);
+ pet_db[j].FoodID=atoi(str[6]);
+ pet_db[j].fullness=atoi(str[7]);
+ pet_db[j].hungry_delay=atoi(str[8])*1000;
+ pet_db[j].r_hungry=atoi(str[9]);
+ if(pet_db[j].r_hungry <= 0)
+ pet_db[j].r_hungry=1;
+ pet_db[j].r_full=atoi(str[10]);
+ pet_db[j].intimate=atoi(str[11]);
+ pet_db[j].die=atoi(str[12]);
+ pet_db[j].capture=atoi(str[13]);
+ pet_db[j].speed=atoi(str[14]);
+ pet_db[j].s_perfor=(char)atoi(str[15]);
+ pet_db[j].talk_convert_class=atoi(str[16]);
+ pet_db[j].attack_rate=atoi(str[17]);
+ pet_db[j].defence_attack_rate=atoi(str[18]);
+ pet_db[j].change_target_rate=atoi(str[19]);
+ pet_db[j].script = NULL;
+ if((np=strchr(p,'{'))==NULL)
+ continue;
+ pet_db[j].script = parse_script((unsigned char *) np,lines);
+ j++;
+ }
+ if (j >= MAX_PET_DB)
+ ShowWarning("read_petdb: Reached max number of pets [%d]. Remaining pets were not read.\n ", MAX_PET_DB);
+ fclose(fp);
+ ShowStatus("Done reading '"CL_WHITE"%d"CL_RESET"' pets in '"CL_WHITE"%s"CL_RESET"'.\n",j,filename[i]);
+ }
+ return 0;
+}
+
+/*==========================================
+ * スキル関係初期化処理
+ *------------------------------------------
+ */
+int do_init_pet(void)
+{
+ memset(pet_db,0,sizeof(pet_db));
+ read_petdb();
+
+ add_timer_func_list(pet_timer,"pet_timer");
+ add_timer_func_list(pet_hungry,"pet_hungry");
+ add_timer_func_list(pet_ai_hard,"pet_ai_hard");
+ add_timer_func_list(pet_skill_bonus_timer,"pet_skill_bonus_timer"); // [Valaris]
+ add_timer_func_list(pet_delay_item_drop2,"pet_delay_item_drop2");
+ add_timer_func_list(pet_skill_support_timer, "pet_skill_support_timer"); // [Skotlex]
+ add_timer_func_list(pet_recovery_timer,"pet_recovery_timer"); // [Valaris]
+ add_timer_func_list(pet_heal_timer,"pet_heal_timer"); // [Valaris]
+ add_timer_interval(gettick()+MIN_PETTHINKTIME,pet_ai_hard,0,0,MIN_PETTHINKTIME);
+
+ return 0;
+}
+
+int do_final_pet(void) {
+ int i;
+ for(i = 0;i < MAX_PET_DB; i++) {
+ if(pet_db[i].script) {
+ aFree(pet_db[i].script);
+ pet_db[i].script = NULL;
+ }
+ }
+ return 0;
+}
diff --git a/src/map/pet.h b/src/map/pet.h
new file mode 100644
index 000000000..feadafdf3
--- /dev/null
+++ b/src/map/pet.h
@@ -0,0 +1,73 @@
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#ifndef _PET_H_
+#define _PET_H_
+
+#define MAX_PET_DB 300
+#define MAX_PETLOOT_SIZE 30 // [Valaris] - Changed to MAX_PETLOOT_SIZE [Skotlex]
+
+struct pet_db {
+ short class_;
+ char name[NAME_LENGTH],jname[NAME_LENGTH];
+ short itemID;
+ short EggID;
+ short AcceID;
+ short FoodID;
+ int fullness;
+ int hungry_delay;
+ int r_hungry;
+ int r_full;
+ int intimate;
+ int die;
+ int capture;
+ int speed;
+ char s_perfor;
+ int talk_convert_class;
+ int attack_rate;
+ int defence_attack_rate;
+ int change_target_rate;
+ unsigned char *script;
+};
+extern struct pet_db pet_db[MAX_PET_DB];
+
+enum { PET_CLASS,PET_CATCH,PET_EGG,PET_EQUIP,PET_FOOD };
+
+int pet_hungry_val(struct map_session_data *sd);
+int pet_target_check(struct map_session_data *sd,struct block_list *bl,int type);
+int pet_sc_check(struct map_session_data *sd, int type); //Skotlex
+int pet_stopattack(struct pet_data *pd);
+int pet_changestate(struct pet_data *pd,int state,int type);
+int pet_walktoxy(struct pet_data *pd,int x,int y);
+int pet_stop_walking(struct pet_data *pd,int type);
+int search_petDB_index(int key,int type);
+int pet_hungry_timer_delete(struct map_session_data *sd);
+int pet_remove_map(struct map_session_data *sd);
+int pet_data_init(struct map_session_data *sd);
+int pet_birth_process(struct map_session_data *sd);
+int pet_recv_petdata(int account_id,struct s_pet *p,int flag);
+int pet_select_egg(struct map_session_data *sd,short egg_index);
+int pet_catch_process1(struct map_session_data *sd,int target_class);
+int pet_catch_process2(struct map_session_data *sd,int target_id);
+int pet_get_egg(int account_id,int pet_id,int flag);
+int pet_menu(struct map_session_data *sd,int menunum);
+int pet_change_name(struct map_session_data *sd,char *name);
+int pet_equipitem(struct map_session_data *sd,int index);
+int pet_unequipitem(struct map_session_data *sd);
+int pet_food(struct map_session_data *sd);
+int pet_lootitem_drop(struct pet_data *pd,struct map_session_data *sd);
+int pet_delay_item_drop2(int tid,unsigned int tick,int id,int data);
+int pet_ai_sub_hard_lootsearch(struct block_list *bl,va_list ap);
+int petskill_use(struct pet_data *pd, struct block_list *target, short skill_id, short skill_lv, unsigned int tick); // [Skotlex]
+int pet_skill_support_timer(int tid, unsigned int tick, int id, int data); // [Skotlex]
+int pet_skill_bonus_timer(int tid,unsigned int tick,int id,int data); // [Valaris]
+int pet_recovery_timer(int tid,unsigned int tick,int id,int data); // [Valaris]
+int pet_heal_timer(int tid,unsigned int tick,int id,int data); // [Valaris]
+int pet_skillsupport_timer(int tid,unsigned int tick,int id,int data); // [Skotlex]
+
+int read_petdb(void);
+int do_init_pet(void);
+int do_final_pet(void);
+
+#endif
+
diff --git a/src/map/script.c b/src/map/script.c
new file mode 100644
index 000000000..a2a3f1a28
--- /dev/null
+++ b/src/map/script.c
@@ -0,0 +1,10736 @@
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+//#define DEBUG_FUNCIN
+//#define DEBUG_DISP
+//#define DEBUG_RUN
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <math.h>
+
+#ifndef _WIN32
+#include <sys/time.h>
+#endif
+
+#include <time.h>
+
+#include "../common/socket.h"
+#include "../common/timer.h"
+#include "../common/malloc.h"
+#include "../common/lock.h"
+#include "../common/nullpo.h"
+
+#include "map.h"
+#include "clif.h"
+#include "chrif.h"
+#include "itemdb.h"
+#include "pc.h"
+#include "status.h"
+#include "script.h"
+#include "storage.h"
+#include "mob.h"
+#include "npc.h"
+#include "pet.h"
+#include "intif.h"
+#include "skill.h"
+#include "chat.h"
+#include "battle.h"
+#include "party.h"
+#include "guild.h"
+#include "atcommand.h"
+#include "charcommand.h"
+#include "log.h"
+#include "showmsg.h"
+#if !defined(TXT_ONLY) && defined(MAPREGSQL)
+#include "strlib.h"
+#endif
+
+#define SCRIPT_BLOCK_SIZE 256
+
+#define FETCH(n, t) \
+ if(st->end>st->start+(n)) \
+ (t)=conv_num(st,&(st->stack->stack_data[st->start+(n)]));
+
+enum { LABEL_NEXTLINE=1,LABEL_START };
+static unsigned char * script_buf = NULL;
+static int script_pos,script_size;
+
+char *str_buf;
+int str_pos,str_size;
+static struct str_data_struct {
+ int type;
+ int str;
+ int backpatch;
+ int label;
+ int (*func)(struct script_state *);
+ int val;
+ int next;
+} *str_data = NULL;
+int str_num=LABEL_START,str_data_size;
+int str_hash[16];
+
+static struct dbt *mapreg_db=NULL;
+static struct dbt *mapregstr_db=NULL;
+static int mapreg_dirty=-1;
+char mapreg_txt[256]="save/mapreg.txt";
+#define MAPREG_AUTOSAVE_INTERVAL (10*1000)
+
+static struct dbt *scriptlabel_db=NULL;
+static struct dbt *userfunc_db=NULL;
+
+struct dbt* script_get_label_db(){ return scriptlabel_db; }
+struct dbt* script_get_userfunc_db(){ return userfunc_db; }
+
+static char pos[11][100] = {"頭","体","左手","右手","ローブ","靴","アクセサリー1","アクセサリー2","頭2","頭3","装着していない"};
+
+struct Script_Config script_config;
+
+static int parse_cmd;
+
+// for advanced scripting support ( nested if, switch, while, for, do-while, function, etc )
+// [Eoe / jA 1080, 1081, 1094, 1164]
+enum { TYPE_NULL = 0 , TYPE_IF , TYPE_SWITCH , TYPE_WHILE , TYPE_FOR , TYPE_DO , TYPE_USERFUNC};
+static struct {
+ struct {
+ int type;
+ int index;
+ int count;
+ int flag;
+ } curly[256]; // 右カッコの情報
+ int curly_count; // 右カッコの数
+ int index; // スクリプト内で使用した構文の数
+} syntax;
+unsigned char* parse_curly_close(unsigned char *p);
+unsigned char* parse_syntax_close(unsigned char *p);
+unsigned char* parse_syntax_close_sub(unsigned char *p,int *flag);
+unsigned char* parse_syntax(unsigned char *p);
+static int parse_syntax_for_flag = 0;
+
+extern int current_equip_item_index; //for New CARS Scripts. It contains Inventory Index of the EQUIP_SCRIPT caller item. [Lupus]
+int potion_flag=0; //For use on Alchemist improved potions/Potion Pitcher. [Skotlex]
+int potion_hp=0, potion_per_hp=0, potion_sp=0, potion_per_sp=0;
+int potion_target=0;
+
+#if !defined(TXT_ONLY) && defined(MAPREGSQL)
+// [zBuffer] SQL Mapreg Saving/Loading Database Declaration
+char mapregsql_db[32] = "mapreg";
+char mapregsql_db_varname[32] = "varname";
+char mapregsql_db_index[32] = "index";
+char mapregsql_db_value[32] = "value";
+char tmp_sql[65535];
+// --------------------------------------------------------
+#endif
+
+/*==========================================
+ * ローカルプロトタイプ宣言 (必要な物のみ)
+ *------------------------------------------
+ */
+unsigned char* parse_subexpr(unsigned char *,int);
+#ifndef TXT_ONLY
+int buildin_query_sql(struct script_state *st);
+#endif
+int buildin_atoi(struct script_state *st);
+int buildin_axtoi(struct script_state *st);
+int buildin_mes(struct script_state *st);
+int buildin_goto(struct script_state *st);
+int buildin_callsub(struct script_state *st);
+int buildin_callfunc(struct script_state *st);
+int buildin_return(struct script_state *st);
+int buildin_getarg(struct script_state *st);
+int buildin_next(struct script_state *st);
+int buildin_close(struct script_state *st);
+int buildin_close2(struct script_state *st);
+int buildin_menu(struct script_state *st);
+int buildin_rand(struct script_state *st);
+int buildin_warp(struct script_state *st);
+int buildin_areawarp(struct script_state *st);
+int buildin_warpchar(struct script_state *st); // [LuzZza]
+int buildin_warpparty(struct script_state *st); //[Fredzilla]
+int buildin_warpguild(struct script_state *st); //[Fredzilla]
+int buildin_heal(struct script_state *st);
+int buildin_itemheal(struct script_state *st);
+int buildin_percentheal(struct script_state *st);
+int buildin_jobchange(struct script_state *st);
+int buildin_input(struct script_state *st);
+int buildin_setlook(struct script_state *st);
+int buildin_set(struct script_state *st);
+int buildin_setarray(struct script_state *st);
+int buildin_cleararray(struct script_state *st);
+int buildin_copyarray(struct script_state *st);
+int buildin_getarraysize(struct script_state *st);
+int buildin_deletearray(struct script_state *st);
+int buildin_getelementofarray(struct script_state *st);
+int buildin_getitem(struct script_state *st);
+int buildin_getitem2(struct script_state *st);
+int buildin_getnameditem(struct script_state *st);
+int buildin_grouprandomitem(struct script_state *st);
+int buildin_makeitem(struct script_state *st);
+int buildin_delitem(struct script_state *st);
+int buildin_delitem2(struct script_state *st);
+int buildin_enableitemuse(struct script_state *st);
+int buildin_disableitemuse(struct script_state *st);
+int buildin_viewpoint(struct script_state *st);
+int buildin_countitem(struct script_state *st);
+int buildin_countitem2(struct script_state *st);
+int buildin_checkweight(struct script_state *st);
+int buildin_readparam(struct script_state *st);
+int buildin_getcharid(struct script_state *st);
+int buildin_getpartyname(struct script_state *st);
+int buildin_getpartymember(struct script_state *st);
+int buildin_getguildname(struct script_state *st);
+int buildin_getguildmaster(struct script_state *st);
+int buildin_getguildmasterid(struct script_state *st);
+int buildin_strcharinfo(struct script_state *st);
+int buildin_getequipid(struct script_state *st);
+int buildin_getequipname(struct script_state *st);
+int buildin_getbrokenid(struct script_state *st); // [Valaris]
+int buildin_repair(struct script_state *st); // [Valaris]
+int buildin_getequipisequiped(struct script_state *st);
+int buildin_getequipisenableref(struct script_state *st);
+int buildin_getequipisidentify(struct script_state *st);
+int buildin_getequiprefinerycnt(struct script_state *st);
+int buildin_getequipweaponlv(struct script_state *st);
+int buildin_getequippercentrefinery(struct script_state *st);
+int buildin_successrefitem(struct script_state *st);
+int buildin_failedrefitem(struct script_state *st);
+int buildin_cutin(struct script_state *st);
+int buildin_cutincard(struct script_state *st);
+int buildin_statusup(struct script_state *st);
+int buildin_statusup2(struct script_state *st);
+int buildin_bonus(struct script_state *st);
+int buildin_bonus2(struct script_state *st);
+int buildin_bonus3(struct script_state *st);
+int buildin_bonus4(struct script_state *st);
+int buildin_skill(struct script_state *st);
+int buildin_addtoskill(struct script_state *st); // [Valaris]
+int buildin_guildskill(struct script_state *st);
+int buildin_getskilllv(struct script_state *st);
+int buildin_getgdskilllv(struct script_state *st);
+int buildin_basicskillcheck(struct script_state *st);
+int buildin_getgmlevel(struct script_state *st);
+int buildin_end(struct script_state *st);
+int buildin_checkoption(struct script_state *st);
+int buildin_setoption(struct script_state *st);
+int buildin_setcart(struct script_state *st);
+int buildin_checkcart(struct script_state *st); // check cart [Valaris]
+int buildin_setfalcon(struct script_state *st);
+int buildin_checkfalcon(struct script_state *st); // check falcon [Valaris]
+int buildin_setriding(struct script_state *st);
+int buildin_checkriding(struct script_state *st); // check for pecopeco [Valaris]
+int buildin_savepoint(struct script_state *st);
+int buildin_gettimetick(struct script_state *st);
+int buildin_gettime(struct script_state *st);
+int buildin_gettimestr(struct script_state *st);
+int buildin_openstorage(struct script_state *st);
+int buildin_guildopenstorage(struct script_state *st);
+int buildin_itemskill(struct script_state *st);
+int buildin_produce(struct script_state *st);
+int buildin_monster(struct script_state *st);
+int buildin_areamonster(struct script_state *st);
+int buildin_killmonster(struct script_state *st);
+int buildin_killmonsterall(struct script_state *st);
+int buildin_clone(struct script_state *st);
+int buildin_doevent(struct script_state *st);
+int buildin_donpcevent(struct script_state *st);
+int buildin_addtimer(struct script_state *st);
+int buildin_deltimer(struct script_state *st);
+int buildin_addtimercount(struct script_state *st);
+int buildin_initnpctimer(struct script_state *st);
+int buildin_stopnpctimer(struct script_state *st);
+int buildin_startnpctimer(struct script_state *st);
+int buildin_setnpctimer(struct script_state *st);
+int buildin_getnpctimer(struct script_state *st);
+int buildin_attachnpctimer(struct script_state *st); // [celest]
+int buildin_detachnpctimer(struct script_state *st); // [celest]
+int buildin_playerattached(struct script_state *st); // [Skotlex]
+int buildin_announce(struct script_state *st);
+int buildin_mapannounce(struct script_state *st);
+int buildin_areaannounce(struct script_state *st);
+int buildin_getusers(struct script_state *st);
+int buildin_getmapusers(struct script_state *st);
+int buildin_getareausers(struct script_state *st);
+int buildin_getareadropitem(struct script_state *st);
+int buildin_enablenpc(struct script_state *st);
+int buildin_disablenpc(struct script_state *st);
+int buildin_enablearena(struct script_state *st); // Added by RoVeRT
+int buildin_disablearena(struct script_state *st); // Added by RoVeRT
+int buildin_hideoffnpc(struct script_state *st);
+int buildin_hideonnpc(struct script_state *st);
+int buildin_sc_start(struct script_state *st);
+int buildin_sc_start2(struct script_state *st);
+int buildin_sc_start4(struct script_state *st);
+int buildin_sc_end(struct script_state *st);
+int buildin_getscrate(struct script_state *st);
+int buildin_debugmes(struct script_state *st);
+int buildin_catchpet(struct script_state *st);
+int buildin_birthpet(struct script_state *st);
+int buildin_resetlvl(struct script_state *st);
+int buildin_resetstatus(struct script_state *st);
+int buildin_resetskill(struct script_state *st);
+int buildin_changebase(struct script_state *st);
+int buildin_changesex(struct script_state *st);
+int buildin_waitingroom(struct script_state *st);
+int buildin_delwaitingroom(struct script_state *st);
+int buildin_enablewaitingroomevent(struct script_state *st);
+int buildin_disablewaitingroomevent(struct script_state *st);
+int buildin_getwaitingroomstate(struct script_state *st);
+int buildin_warpwaitingpc(struct script_state *st);
+int buildin_attachrid(struct script_state *st);
+int buildin_detachrid(struct script_state *st);
+int buildin_isloggedin(struct script_state *st);
+int buildin_setmapflagnosave(struct script_state *st);
+int buildin_setmapflag(struct script_state *st);
+int buildin_removemapflag(struct script_state *st);
+int buildin_pvpon(struct script_state *st);
+int buildin_pvpoff(struct script_state *st);
+int buildin_gvgon(struct script_state *st);
+int buildin_gvgoff(struct script_state *st);
+int buildin_emotion(struct script_state *st);
+int buildin_maprespawnguildid(struct script_state *st);
+int buildin_agitstart(struct script_state *st); // <Agit>
+int buildin_agitend(struct script_state *st);
+int buildin_agitcheck(struct script_state *st); // <Agitcheck>
+int buildin_flagemblem(struct script_state *st); // Flag Emblem
+int buildin_getcastlename(struct script_state *st);
+int buildin_getcastledata(struct script_state *st);
+int buildin_setcastledata(struct script_state *st);
+int buildin_requestguildinfo(struct script_state *st);
+int buildin_getequipcardcnt(struct script_state *st);
+int buildin_successremovecards(struct script_state *st);
+int buildin_failedremovecards(struct script_state *st);
+int buildin_marriage(struct script_state *st);
+int buildin_wedding_effect(struct script_state *st);
+int buildin_divorce(struct script_state *st);
+int buildin_ispartneron(struct script_state *st); // MouseJstr
+int buildin_getpartnerid(struct script_state *st); // MouseJstr
+int buildin_getchildid(struct script_state *st); // Skotlex
+int buildin_getmotherid(struct script_state *st); // Lupus
+int buildin_getfatherid(struct script_state *st); // Lupus
+int buildin_warppartner(struct script_state *st); // MouseJstr
+int buildin_getitemname(struct script_state *st);
+int buildin_getitemslots(struct script_state *st);
+int buildin_makepet(struct script_state *st);
+int buildin_getexp(struct script_state *st);
+int buildin_getinventorylist(struct script_state *st);
+int buildin_getskilllist(struct script_state *st);
+int buildin_clearitem(struct script_state *st);
+int buildin_classchange(struct script_state *st);
+int buildin_misceffect(struct script_state *st);
+int buildin_soundeffect(struct script_state *st);
+int buildin_soundeffectall(struct script_state *st);
+int buildin_setcastledata(struct script_state *st);
+int buildin_mapwarp(struct script_state *st);
+int buildin_inittimer(struct script_state *st);
+int buildin_stoptimer(struct script_state *st);
+int buildin_cmdothernpc(struct script_state *st);
+int buildin_mobcount(struct script_state *st);
+int buildin_strmobinfo(struct script_state *st); // Script for displaying mob info [Valaris]
+int buildin_guardian(struct script_state *st); // Script for displaying mob info [Valaris]
+int buildin_guardianinfo(struct script_state *st); // Script for displaying mob info [Valaris]
+int buildin_petskillbonus(struct script_state *st); // petskillbonus [Valaris]
+int buildin_petrecovery(struct script_state *st); // pet skill for curing status [Valaris]
+int buildin_petloot(struct script_state *st); // pet looting [Valaris]
+int buildin_petheal(struct script_state *st); // pet healing [Valaris]
+//int buildin_petmag(struct script_state *st); // pet magnificat [Valaris]
+int buildin_petskillattack(struct script_state *st); // pet skill attacks [Skotlex]
+int buildin_petskillattack2(struct script_state *st); // pet skill attacks [Skotlex]
+int buildin_petskillsupport(struct script_state *st); // pet support skill [Valaris]
+int buildin_skilleffect(struct script_state *st); // skill effects [Celest]
+int buildin_npcskilleffect(struct script_state *st); // skill effects for npcs [Valaris]
+int buildin_specialeffect(struct script_state *st); // special effect script [Valaris]
+int buildin_specialeffect2(struct script_state *st); // special effect script [Valaris]
+int buildin_nude(struct script_state *st); // nude [Valaris]
+int buildin_atcommand(struct script_state *st); // [MouseJstr]
+int buildin_charcommand(struct script_state *st); // [MouseJstr]
+int buildin_movenpc(struct script_state *st); // [MouseJstr]
+int buildin_message(struct script_state *st); // [MouseJstr]
+int buildin_npctalk(struct script_state *st); // [Valaris]
+int buildin_hasitems(struct script_state *st); // [Valaris]
+int buildin_getlook(struct script_state *st); //Lorky [Lupus]
+int buildin_getsavepoint(struct script_state *st); //Lorky [Lupus]
+int buildin_npcspeed(struct script_state *st); // [Valaris]
+int buildin_npcwalkto(struct script_state *st); // [Valaris]
+int buildin_npcstop(struct script_state *st); // [Valaris]
+int buildin_getmapxy(struct script_state *st); //get map position for player/npc/pet/mob by Lorky [Lupus]
+int buildin_checkoption1(struct script_state *st); // [celest]
+int buildin_checkoption2(struct script_state *st); // [celest]
+int buildin_guildgetexp(struct script_state *st); // [celest]
+int buildin_guildchangegm(struct script_state *st); // [Skotlex]
+int buildin_skilluseid(struct script_state *st); // originally by Qamera [celest]
+int buildin_skillusepos(struct script_state *st); // originally by Qamera [celest]
+int buildin_logmes(struct script_state *st); // [Lupus]
+int buildin_summon(struct script_state *st); // [celest]
+int buildin_isnight(struct script_state *st); // [celest]
+int buildin_isday(struct script_state *st); // [celest]
+int buildin_isequipped(struct script_state *st); // [celest]
+int buildin_isequippedcnt(struct script_state *st); // [celest]
+int buildin_cardscnt(struct script_state *st); // [Lupus]
+int buildin_getrefine(struct script_state *st); // [celest]
+int buildin_adopt(struct script_state *st);
+int buildin_night(struct script_state *st);
+int buildin_day(struct script_state *st);
+int buildin_getusersname(struct script_state *st); //jA commands added [Lupus]
+int buildin_dispbottom(struct script_state *st);
+int buildin_recovery(struct script_state *st);
+int buildin_getpetinfo(struct script_state *st);
+int buildin_checkequipedcard(struct script_state *st);
+int buildin_globalmes(struct script_state *st);
+int buildin_jump_zero(struct script_state *st);
+int buildin_select(struct script_state *st);
+int buildin_getmapmobs(struct script_state *st); //jA addition end
+int buildin_unequip(struct script_state *st); // unequip [Spectre]
+int buildin_getstrlen(struct script_state *st); //strlen [valaris]
+int buildin_charisalpha(struct script_state *st);//isalpha [valaris]
+int buildin_fakenpcname(struct script_state *st); // [Lance]
+int buildin_compare(struct script_state *st); // Lordalfa, to bring strstr to Scripting Engine
+int buildin_getiteminfo(struct script_state *st); //[Lupus] returns Items Buy / sell Price, etc info
+int buildin_getequipcardid(struct script_state *st); //[Lupus] returns card id from quipped item card slot N
+// [zBuffer] List of mathematics commands --->
+int buildin_sqrt(struct script_state *st);
+int buildin_pow(struct script_state *st);
+int buildin_distance(struct script_state *st);
+// <--- [zBuffer] List of mathematics commands
+// [zBuffer] List of dynamic var commands --->
+int buildin_getd(struct script_state *st);
+int buildin_setd(struct script_state *st);
+// <--- [zBuffer] List of dynamic var commands
+int buildin_petstat(struct script_state *st); // [Lance] Pet Stat Rq: Dubby
+int buildin_callshop(struct script_state *st); // [Skotlex]
+void push_val(struct script_stack *stack,int type,int val);
+int run_func(struct script_state *st);
+
+int mapreg_setreg(int num,int val);
+int mapreg_setregstr(int num,const char *str);
+
+int buildin_setitemscript(struct script_state *st);
+int buildin_disguise(struct script_state *st);
+int buildin_undisguise(struct script_state *st);
+
+#ifdef PCRE_SUPPORT
+int buildin_defpattern(struct script_state *st); // MouseJstr
+int buildin_activatepset(struct script_state *st); // MouseJstr
+int buildin_deactivatepset(struct script_state *st); // MouseJstr
+int buildin_deletepset(struct script_state *st); // MouseJstr
+#endif
+
+struct {
+ int (*func)(struct script_state *);
+ char *name;
+ char *arg;
+} buildin_func[]={
+ {buildin_axtoi,"axtoi","s"},
+#ifndef TXT_ONLY
+ {buildin_query_sql, "query_sql", "s*"},
+#endif
+ {buildin_atoi,"atoi","s"},
+ {buildin_mes,"mes","s"},
+ {buildin_next,"next",""},
+ {buildin_close,"close",""},
+ {buildin_close2,"close2",""},
+ {buildin_menu,"menu","*"},
+ {buildin_goto,"goto","l"},
+ {buildin_callsub,"callsub","i*"},
+ {buildin_callfunc,"callfunc","s*"},
+ {buildin_return,"return","*"},
+ {buildin_getarg,"getarg","i"},
+ {buildin_jobchange,"jobchange","i*"},
+ {buildin_input,"input","*"},
+ {buildin_warp,"warp","sii"},
+ {buildin_areawarp,"areawarp","siiiisii"},
+ {buildin_warpchar,"warpchar","siii"}, // [LuzZza]
+ {buildin_warpparty,"warpparty","siii"}, // [Fredzilla]
+ {buildin_warpguild,"warpguild","siii"}, // [Fredzilla]
+ {buildin_setlook,"setlook","ii"},
+ {buildin_set,"set","ii"},
+ {buildin_setarray,"setarray","ii*"},
+ {buildin_cleararray,"cleararray","iii"},
+ {buildin_copyarray,"copyarray","iii"},
+ {buildin_getarraysize,"getarraysize","i"},
+ {buildin_deletearray,"deletearray","ii"},
+ {buildin_getelementofarray,"getelementofarray","ii"},
+ {buildin_getitem,"getitem","ii**"},
+ {buildin_getitem2,"getitem2","iiiiiiiii*"},
+ {buildin_getnameditem,"getnameditem","is"},
+ {buildin_grouprandomitem,"groupranditem","i"},
+ {buildin_makeitem,"makeitem","iisii"},
+ {buildin_delitem,"delitem","ii"},
+ {buildin_delitem2,"delitem2","iiiiiiiii"},
+ {buildin_enableitemuse,"enable_items",""},
+ {buildin_disableitemuse,"disable_items",""},
+ {buildin_cutin,"cutin","si"},
+ {buildin_cutincard,"cutincard","i"},
+ {buildin_viewpoint,"viewpoint","iiiii"},
+ {buildin_heal,"heal","ii"},
+ {buildin_itemheal,"itemheal","ii"},
+ {buildin_percentheal,"percentheal","ii"},
+ {buildin_rand,"rand","i*"},
+ {buildin_countitem,"countitem","i"},
+ {buildin_countitem2,"countitem2","iiiiiiii"},
+ {buildin_checkweight,"checkweight","ii"},
+ {buildin_readparam,"readparam","i*"},
+ {buildin_getcharid,"getcharid","i*"},
+ {buildin_getpartyname,"getpartyname","i"},
+ {buildin_getpartymember,"getpartymember","i"},
+ {buildin_getguildname,"getguildname","i"},
+ {buildin_getguildmaster,"getguildmaster","i"},
+ {buildin_getguildmasterid,"getguildmasterid","i"},
+ {buildin_strcharinfo,"strcharinfo","i"},
+ {buildin_getequipid,"getequipid","i"},
+ {buildin_getequipname,"getequipname","i"},
+ {buildin_getbrokenid,"getbrokenid","i"}, // [Valaris]
+ {buildin_repair,"repair","i"}, // [Valaris]
+ {buildin_getequipisequiped,"getequipisequiped","i"},
+ {buildin_getequipisenableref,"getequipisenableref","i"},
+ {buildin_getequipisidentify,"getequipisidentify","i"},
+ {buildin_getequiprefinerycnt,"getequiprefinerycnt","i"},
+ {buildin_getequipweaponlv,"getequipweaponlv","i"},
+ {buildin_getequippercentrefinery,"getequippercentrefinery","i"},
+ {buildin_successrefitem,"successrefitem","i"},
+ {buildin_failedrefitem,"failedrefitem","i"},
+ {buildin_statusup,"statusup","i"},
+ {buildin_statusup2,"statusup2","ii"},
+ {buildin_bonus,"bonus","ii"},
+ {buildin_bonus2,"bonus2","iii"},
+ {buildin_bonus3,"bonus3","iiii"},
+ {buildin_bonus4,"bonus4","iiiii"},
+ {buildin_skill,"skill","ii*"},
+ {buildin_addtoskill,"addtoskill","ii*"}, // [Valaris]
+ {buildin_guildskill,"guildskill","ii"},
+ {buildin_getskilllv,"getskilllv","i"},
+ {buildin_getgdskilllv,"getgdskilllv","ii"},
+ {buildin_basicskillcheck,"basicskillcheck","*"},
+ {buildin_getgmlevel,"getgmlevel","*"},
+ {buildin_end,"end",""},
+// {buildin_end,"break",""}, this might confuse advanced scripting support [Eoe]
+ {buildin_checkoption,"checkoption","i"},
+ {buildin_setoption,"setoption","i"},
+ {buildin_setcart,"setcart",""},
+ {buildin_checkcart,"checkcart","*"}, //fixed by Lupus (added '*')
+ {buildin_setfalcon,"setfalcon",""},
+ {buildin_checkfalcon,"checkfalcon","*"}, //fixed by Lupus (fixed wrong pointer, added '*')
+ {buildin_setriding,"setriding",""},
+ {buildin_checkriding,"checkriding","*"}, //fixed by Lupus (fixed wrong pointer, added '*')
+ {buildin_savepoint,"save","sii"},
+ {buildin_savepoint,"savepoint","sii"},
+ {buildin_gettimetick,"gettimetick","i"},
+ {buildin_gettime,"gettime","i"},
+ {buildin_gettimestr,"gettimestr","si"},
+ {buildin_openstorage,"openstorage",""},
+ {buildin_guildopenstorage,"guildopenstorage","*"},
+ {buildin_itemskill,"itemskill","iis"},
+ {buildin_produce,"produce","i"},
+ {buildin_monster,"monster","siisii*"},
+ {buildin_areamonster,"areamonster","siiiisii*"},
+ {buildin_killmonster,"killmonster","ss"},
+ {buildin_killmonsterall,"killmonsterall","s"},
+ {buildin_clone,"clone","siisi*"},
+ {buildin_doevent,"doevent","s"},
+ {buildin_donpcevent,"donpcevent","s"},
+ {buildin_addtimer,"addtimer","is"},
+ {buildin_deltimer,"deltimer","s"},
+ {buildin_addtimercount,"addtimercount","si"},
+ {buildin_initnpctimer,"initnpctimer","*"},
+ {buildin_stopnpctimer,"stopnpctimer","*"},
+ {buildin_startnpctimer,"startnpctimer","*"},
+ {buildin_setnpctimer,"setnpctimer","*"},
+ {buildin_getnpctimer,"getnpctimer","i*"},
+ {buildin_attachnpctimer,"attachnpctimer","*"}, // attached the player id to the npc timer [Celest]
+ {buildin_detachnpctimer,"detachnpctimer","*"}, // detached the player id from the npc timer [Celest]
+ {buildin_playerattached,"playerattached",""}, // returns id of the current attached player. [Skotlex]
+ {buildin_announce,"announce","si*"},
+ {buildin_mapannounce,"mapannounce","ssi*"},
+ {buildin_areaannounce,"areaannounce","siiiisi*"},
+ {buildin_getusers,"getusers","i"},
+ {buildin_getmapusers,"getmapusers","s"},
+ {buildin_getareausers,"getareausers","siiii"},
+ {buildin_getareadropitem,"getareadropitem","siiiii"},
+ {buildin_enablenpc,"enablenpc","s"},
+ {buildin_disablenpc,"disablenpc","s"},
+ {buildin_enablearena,"enablearena",""}, // Added by RoVeRT
+ {buildin_disablearena,"disablearena",""}, // Added by RoVeRT
+ {buildin_hideoffnpc,"hideoffnpc","s"},
+ {buildin_hideonnpc,"hideonnpc","s"},
+ {buildin_sc_start,"sc_start","iii*"},
+ {buildin_sc_start2,"sc_start2","iiii*"},
+ {buildin_sc_start4,"sc_start4","iiiiii*"},
+ {buildin_sc_end,"sc_end","i"},
+ {buildin_getscrate,"getscrate","ii*"},
+ {buildin_debugmes,"debugmes","s"},
+ {buildin_catchpet,"pet","i"},
+ {buildin_birthpet,"bpet",""},
+ {buildin_resetlvl,"resetlvl","i"},
+ {buildin_resetstatus,"resetstatus",""},
+ {buildin_resetskill,"resetskill",""},
+ {buildin_changebase,"changebase","i"},
+ {buildin_changesex,"changesex",""},
+ {buildin_waitingroom,"waitingroom","si*"},
+ {buildin_warpwaitingpc,"warpwaitingpc","sii"},
+ {buildin_delwaitingroom,"delwaitingroom","*"},
+ {buildin_enablewaitingroomevent,"enablewaitingroomevent","*"},
+ {buildin_disablewaitingroomevent,"disablewaitingroomevent","*"},
+ {buildin_getwaitingroomstate,"getwaitingroomstate","i*"},
+ {buildin_warpwaitingpc,"warpwaitingpc","sii*"},
+ {buildin_attachrid,"attachrid","i"},
+ {buildin_detachrid,"detachrid",""},
+ {buildin_isloggedin,"isloggedin","i"},
+ {buildin_setmapflagnosave,"setmapflagnosave","ssii"},
+ {buildin_setmapflag,"setmapflag","si"},
+ {buildin_removemapflag,"removemapflag","si"},
+ {buildin_pvpon,"pvpon","s"},
+ {buildin_pvpoff,"pvpoff","s"},
+ {buildin_gvgon,"gvgon","s"},
+ {buildin_gvgoff,"gvgoff","s"},
+ {buildin_emotion,"emotion","i*"},
+ {buildin_maprespawnguildid,"maprespawnguildid","sii"},
+ {buildin_agitstart,"agitstart",""}, // <Agit>
+ {buildin_agitend,"agitend",""},
+ {buildin_agitcheck,"agitcheck","i"}, // <Agitcheck>
+ {buildin_flagemblem,"flagemblem","i"}, // Flag Emblem
+ {buildin_getcastlename,"getcastlename","s"},
+ {buildin_getcastledata,"getcastledata","si*"},
+ {buildin_setcastledata,"setcastledata","sii"},
+ {buildin_requestguildinfo,"requestguildinfo","i*"},
+ {buildin_getequipcardcnt,"getequipcardcnt","i"},
+ {buildin_successremovecards,"successremovecards","i"},
+ {buildin_failedremovecards,"failedremovecards","ii"},
+ {buildin_marriage,"marriage","s"},
+ {buildin_wedding_effect,"wedding",""},
+ {buildin_divorce,"divorce",""},
+ {buildin_ispartneron,"ispartneron",""},
+ {buildin_getpartnerid,"getpartnerid",""},
+ {buildin_getchildid,"getchildid",""},
+ {buildin_getmotherid,"getmotherid",""},
+ {buildin_getfatherid,"getfatherid",""},
+ {buildin_warppartner,"warppartner","sii"},
+ {buildin_getitemname,"getitemname","i"},
+ {buildin_getitemslots,"getitemslots","i"},
+ {buildin_makepet,"makepet","i"},
+ {buildin_getexp,"getexp","ii"},
+ {buildin_getinventorylist,"getinventorylist",""},
+ {buildin_getskilllist,"getskilllist",""},
+ {buildin_clearitem,"clearitem",""},
+ {buildin_classchange,"classchange","ii"},
+ {buildin_misceffect,"misceffect","i"},
+ {buildin_soundeffect,"soundeffect","si"},
+ {buildin_soundeffectall,"soundeffectall","si"}, // SoundEffectAll [Codemaster]
+ {buildin_strmobinfo,"strmobinfo","ii"}, // display mob data [Valaris]
+ {buildin_guardian,"guardian","siisii*i"}, // summon guardians
+ {buildin_guardianinfo,"guardianinfo","i"}, // display guardian data [Valaris]
+ {buildin_petskillbonus,"petskillbonus","iiii"}, // [Valaris]
+ {buildin_petrecovery,"petrecovery","ii"}, // [Valaris]
+ {buildin_petloot,"petloot","i"}, // [Valaris]
+ {buildin_petheal,"petheal","iiii"}, // [Valaris]
+// {buildin_petmag,"petmag","iiii"}, // [Valaris]
+ {buildin_petskillattack,"petskillattack","iiii"}, // [Skotlex]
+ {buildin_petskillattack2,"petskillattack2","iiiii"}, // [Valaris]
+ {buildin_petskillsupport,"petskillsupport","iiiii"}, // [Skotlex]
+ {buildin_skilleffect,"skilleffect","ii"}, // skill effect [Celest]
+ {buildin_npcskilleffect,"npcskilleffect","iiii"}, // npc skill effect [Valaris]
+ {buildin_specialeffect,"specialeffect","i"}, // npc skill effect [Valaris]
+ {buildin_specialeffect2,"specialeffect2","i"}, // skill effect on players[Valaris]
+ {buildin_nude,"nude",""}, // nude command [Valaris]
+ {buildin_mapwarp,"mapwarp","ssii"}, // Added by RoVeRT
+ {buildin_inittimer,"inittimer",""},
+ {buildin_stoptimer,"stoptimer",""},
+ {buildin_cmdothernpc,"cmdothernpc","ss"},
+ {buildin_atcommand,"atcommand","*"}, // [MouseJstr]
+ {buildin_charcommand,"charcommand","*"}, // [MouseJstr]
+// {buildin_movenpc,"movenpc","siis"}, // [MouseJstr]
+ {buildin_message,"message","s*"}, // [MouseJstr]
+ {buildin_npctalk,"npctalk","*"}, // [Valaris]
+ {buildin_hasitems,"hasitems","*"}, // [Valaris]
+ {buildin_mobcount,"mobcount","ss"},
+ {buildin_getlook,"getlook","i"},
+ {buildin_getsavepoint,"getsavepoint","i"},
+ {buildin_npcspeed,"npcspeed","i"}, // [Valaris]
+ {buildin_npcwalkto,"npcwalkto","ii"}, // [Valaris]
+ {buildin_npcstop,"npcstop",""}, // [Valaris]
+ {buildin_getmapxy,"getmapxy","siii*"}, //by Lorky [Lupus]
+ {buildin_checkoption1,"checkoption1","i"},
+ {buildin_checkoption2,"checkoption2","i"},
+ {buildin_guildgetexp,"guildgetexp","i"},
+ {buildin_guildchangegm,"guildchangegm","is"},
+ {buildin_skilluseid,"skilluseid","ii"}, // originally by Qamera [Celest]
+ {buildin_skilluseid,"doskill","ii"}, // since a lot of scripts would already use 'doskill'...
+ {buildin_skillusepos,"skillusepos","iiii"}, // [Celest]
+ {buildin_logmes,"logmes","s"}, //this command actls as MES but rints info into LOG file either SQL/TXT [Lupus]
+ {buildin_summon,"summon","si*"}, // summons a slave monster [Celest]
+ {buildin_isnight,"isnight",""}, // check whether it is night time [Celest]
+ {buildin_isday,"isday",""}, // check whether it is day time [Celest]
+ {buildin_isequipped,"isequipped","i*"}, // check whether another item/card has been equipped [Celest]
+ {buildin_isequippedcnt,"isequippedcnt","i*"}, // check how many items/cards are being equipped [Celest]
+ {buildin_cardscnt,"cardscnt","i*"}, // check how many items/cards are being equipped in the same arm [Lupus]
+ {buildin_getrefine,"getrefine","*"}, // returns the refined number of the current item, or an item with index specified [celest]
+ {buildin_adopt,"adopt","sss"}, // allows 2 parents to adopt a child
+ {buildin_night,"night",""}, // sets the server to night time
+ {buildin_day,"day",""}, // sets the server to day time
+#ifdef PCRE_SUPPORT
+ {buildin_defpattern, "defpattern", "iss"}, // Define pattern to listen for [MouseJstr]
+ {buildin_activatepset, "activatepset", "i"}, // Activate a pattern set [MouseJstr]
+ {buildin_deactivatepset, "deactivatepset", "i"}, // Deactive a pattern set [MouseJstr]
+ {buildin_deletepset, "deletepset", "i"}, // Delete a pattern set [MouseJstr]
+#endif
+ {buildin_dispbottom,"dispbottom","s"}, //added from jA [Lupus]
+ {buildin_getusersname,"getusersname","*"},
+ {buildin_recovery,"recovery",""},
+ {buildin_getpetinfo,"getpetinfo","i"},
+ {buildin_checkequipedcard,"checkequipedcard","i"},
+ {buildin_jump_zero,"jump_zero","ii"}, //for future jA script compatibility
+ {buildin_select,"select","*"}, //for future jA script compatibility
+ {buildin_globalmes,"globalmes","s*"},
+ {buildin_getmapmobs,"getmapmobs","s"}, //end jA addition
+ {buildin_unequip,"unequip","i"}, // unequip command [Spectre]
+ {buildin_getstrlen,"getstrlen","s"}, //strlen [Valaris]
+ {buildin_charisalpha,"charisalpha","si"}, //isalpha [Valaris]
+ {buildin_fakenpcname,"fakenpcname","ssi"}, // [Lance]
+ {buildin_compare,"compare","ss"}, // Lordalfa - To bring strstr to scripting Engine.
+ {buildin_getiteminfo,"getiteminfo","ii"}, //[Lupus] returns Items Buy / sell Price, etc info
+ {buildin_getequipcardid,"getequipcardid","ii"}, //[Lupus] returns CARD ID or other info from CARD slot N of equipped item
+ // [zBuffer] List of mathematics commands --->
+ {buildin_sqrt,"sqrt","i"},
+ {buildin_pow,"pow","ii"},
+ {buildin_distance,"distance","iiii"},
+ // <--- [zBuffer] List of mathematics commands
+ // [zBuffer] List of dynamic var commands --->
+ {buildin_getd,"getd","*"},
+ {buildin_setd,"setd","*"},
+ // <--- [zBuffer] List of dynamic var commands
+ {buildin_petstat,"petstat","i"},
+ {buildin_callshop,"callshop","si"}, // [Skotlex]
+ {buildin_setitemscript,"setitemscript","is"}, //Set NEW item bonus script. Lupus
+ {buildin_disguise,"disguise","i"}, //disguise player. Lupus
+ {buildin_undisguise,"undisguise","i"}, //undisguise player. Lupus
+ {NULL,NULL,NULL},
+};
+
+enum {
+ C_NOP,C_POS,C_INT,C_PARAM,C_FUNC,C_STR,C_CONSTSTR,C_ARG,
+ C_NAME,C_EOL, C_RETINFO,
+ C_USERFUNC, C_USERFUNC_POS, // user defined functions
+
+ C_LOR,C_LAND,C_LE,C_LT,C_GE,C_GT,C_EQ,C_NE, //operator
+ C_XOR,C_OR,C_AND,C_ADD,C_SUB,C_MUL,C_DIV,C_MOD,C_NEG,C_LNOT,C_NOT,C_R_SHIFT,C_L_SHIFT
+};
+
+//Reports on the console the src of an script error.
+static void report_src(struct script_state *st) {
+ struct block_list *bl;
+ if (!st->oid) return; //Can't report source.
+ bl = map_id2bl(st->oid);
+ if (!bl) return;
+ switch (bl->type) {
+ case BL_NPC:
+ ShowDebug("Source (NPC): %s at %s (%d,%d)\n", ((struct npc_data *)bl)->name, mapindex_id2name(bl->m), bl->x, bl->y);
+ break;
+ default:
+ ShowDebug("Source (Non-NPC): %s (%d,%d)\n", mapindex_id2name(bl->m), bl->x, bl->y);
+ break;
+ }
+}
+/*==========================================
+ * 文字列のハッシュを計算
+ *------------------------------------------
+ */
+static int calc_hash(const unsigned char *p)
+{
+ int h=0;
+ while(*p){
+ h=(h<<1)+(h>>3)+(h>>5)+(h>>8);
+ h+=*p++;
+ }
+ return h&15;
+}
+
+/*==========================================
+ * str_dataの中に名前があるか検索する
+ *------------------------------------------
+ */
+// 既存のであれば番号、無ければ-1
+static int search_str(const unsigned char *p)
+{
+ int i;
+ i=str_hash[calc_hash(p)];
+ while(i){
+ if(strcmp(str_buf+str_data[i].str,(char *) p)==0){
+ return i;
+ }
+ i=str_data[i].next;
+ }
+ return -1;
+}
+
+/*==========================================
+ * str_dataに名前を登録
+ *------------------------------------------
+ */
+// 既存のであれば番号、無ければ登録して新規番号
+static int add_str(const unsigned char *p)
+{
+ int i;
+ char *lowcase;
+
+ lowcase=aStrdup((char *) p);
+ for(i=0;lowcase[i];i++)
+ lowcase[i]=tolower(lowcase[i]);
+ if((i=search_str((unsigned char *) lowcase))>=0){
+ aFree(lowcase);
+ return i;
+ }
+ aFree(lowcase);
+
+ i=calc_hash(p);
+ if(str_hash[i]==0){
+ str_hash[i]=str_num;
+ } else {
+ i=str_hash[i];
+ for(;;){
+ if(strcmp(str_buf+str_data[i].str,(char *) p)==0){
+ return i;
+ }
+ if(str_data[i].next==0)
+ break;
+ i=str_data[i].next;
+ }
+ str_data[i].next=str_num;
+ }
+ if(str_num>=str_data_size){
+ str_data_size+=128;
+ str_data=(struct str_data_struct *) aRealloc(str_data,sizeof(str_data[0])*str_data_size);
+ memset(str_data + (str_data_size - 128), '\0', 128);
+ }
+ while(str_pos+(int)strlen((char *) p)+1>=str_size){
+ str_size+=256;
+ str_buf=(char *)aRealloc(str_buf,str_size);
+ memset(str_buf + (str_size - 256), '\0', 256);
+ }
+ strcpy(str_buf+str_pos, (char *) p);
+ str_data[str_num].type=C_NOP;
+ str_data[str_num].str=str_pos;
+ str_data[str_num].next=0;
+ str_data[str_num].func=NULL;
+ str_data[str_num].backpatch=-1;
+ str_data[str_num].label=-1;
+ str_pos+=(int)strlen( (char *) p)+1;
+ return str_num++;
+}
+
+
+/*==========================================
+ * スクリプトバッファサイズの確認と拡張
+ *------------------------------------------
+ */
+static void check_script_buf(int size)
+{
+ if(script_pos+size>=script_size){
+ script_size+=SCRIPT_BLOCK_SIZE;
+ script_buf=(unsigned char *)aRealloc(script_buf,script_size);
+ memset(script_buf + script_size - SCRIPT_BLOCK_SIZE, '\0',
+ SCRIPT_BLOCK_SIZE);
+ }
+}
+
+/*==========================================
+ * スクリプトバッファに1バイト書き込む
+ *------------------------------------------
+ */
+static void add_scriptb(int a)
+{
+ check_script_buf(1);
+ script_buf[script_pos++]=a;
+}
+
+/*==========================================
+ * スクリプトバッファにデータタイプを書き込む
+ *------------------------------------------
+ */
+static void add_scriptc(int a)
+{
+ while(a>=0x40){
+ add_scriptb((a&0x3f)|0x40);
+ a=(a-0x40)>>6;
+ }
+ add_scriptb(a&0x3f);
+}
+
+/*==========================================
+ * スクリプトバッファに整数を書き込む
+ *------------------------------------------
+ */
+static void add_scripti(int a)
+{
+ while(a>=0x40){
+ add_scriptb(a|0xc0);
+ a=(a-0x40)>>6;
+ }
+ add_scriptb(a|0x80);
+}
+
+/*==========================================
+ * スクリプトバッファにラベル/変数/関数を書き込む
+ *------------------------------------------
+ */
+// 最大16Mまで
+static void add_scriptl(int l)
+{
+ int backpatch = str_data[l].backpatch;
+
+ switch(str_data[l].type){
+ case C_POS:
+ case C_USERFUNC_POS:
+ add_scriptc(C_POS);
+ add_scriptb(str_data[l].label);
+ add_scriptb(str_data[l].label>>8);
+ add_scriptb(str_data[l].label>>16);
+ break;
+ case C_NOP:
+ case C_USERFUNC:
+ // ラベルの可能性があるのでbackpatch用データ埋め込み
+ add_scriptc(C_NAME);
+ str_data[l].backpatch=script_pos;
+ add_scriptb(backpatch);
+ add_scriptb(backpatch>>8);
+ add_scriptb(backpatch>>16);
+ break;
+ case C_INT:
+ add_scripti(str_data[l].val);
+ break;
+ default:
+ // もう他の用途と確定してるので数字をそのまま
+ add_scriptc(C_NAME);
+ add_scriptb(l);
+ add_scriptb(l>>8);
+ add_scriptb(l>>16);
+ break;
+ }
+}
+
+/*==========================================
+ * ラベルを解決する
+ *------------------------------------------
+ */
+void set_label(int l,int pos)
+{
+ int i,next;
+
+ str_data[l].type=(str_data[l].type == C_USERFUNC ? C_USERFUNC_POS : C_POS);
+ str_data[l].label=pos;
+ for(i=str_data[l].backpatch;i>=0 && i!=0x00ffffff;){
+ next=(*(int*)(script_buf+i)) & 0x00ffffff;
+ script_buf[i-1]=(str_data[l].type == C_USERFUNC ? C_USERFUNC_POS : C_POS);
+ script_buf[i]=pos;
+ script_buf[i+1]=pos>>8;
+ script_buf[i+2]=pos>>16;
+ i=next;
+ }
+}
+
+/*==========================================
+ * スペース/コメント読み飛ばし
+ *------------------------------------------
+ */
+static unsigned char *skip_space(unsigned char *p)
+{
+ while(1){
+ while(isspace(*p))
+ p++;
+ if(p[0]=='/' && p[1]=='/'){
+ while(*p && *p!='\n')
+ p++;
+ } else if(p[0]=='/' && p[1]=='*'){
+ p++;
+ while(*p && (p[-1]!='*' || p[0]!='/'))
+ p++;
+ if(*p) p++;
+ } else
+ break;
+ }
+ return p;
+}
+
+/*==========================================
+ * 1単語スキップ
+ *------------------------------------------
+ */
+static unsigned char *skip_word(unsigned char *p)
+{
+ // prefix
+ if(*p=='$') p++; // MAP鯖内共有変数用
+ if(*p=='@') p++; // 一時的変数用(like weiss)
+ if(*p=='#') p++; // account変数用
+ if(*p=='#') p++; // ワールドaccount変数用
+
+ while(isalnum(*p)||*p=='_'|| *p>=0x81)
+ if(*p>=0x81 && p[1]){
+ p+=2;
+ } else
+ p++;
+
+ // postfix
+ if(*p=='$') p++; // 文字列変数
+
+ return p;
+}
+
+static unsigned char *startptr;
+static int startline;
+
+/*==========================================
+ * エラーメッセージ出力
+ *------------------------------------------
+ */
+static void disp_error_message(const char *mes,const unsigned char *pos)
+{
+ int line,c=0,i;
+ unsigned char *p,*linestart,*lineend;
+
+ for(line=startline,p=startptr;p && *p;line++){
+ linestart=p;
+ lineend=(unsigned char *) strchr((char *) p,'\n');
+ if(lineend){
+ c=*lineend;
+ *lineend=0;
+ }
+ if(lineend==NULL || pos<lineend){
+ fprintf(stderr, "\r"); //To not printout the error next to the spinner...
+ ShowError(" "); //Better error display [Skotlex]
+ if (current_file) {
+ printf("%s in "CL_WHITE"\'%s\'"CL_RESET" line "CL_WHITE"\'%d\'"CL_RESET" : ", mes, current_file, line);
+ } else {
+ printf("%s line "CL_WHITE"\'%d\'"CL_RESET" : ", mes, line);
+ }
+ for(i=0;(linestart[i]!='\r') && (linestart[i]!='\n') && linestart[i];i++){
+ if(linestart+i!=pos)
+ printf("%c",linestart[i]);
+ else
+ printf("\'%c\'",linestart[i]);
+ }
+ printf("\a\n");
+ if(lineend)
+ *lineend=c;
+ return;
+ }
+ *lineend=c;
+ p=lineend+1;
+ }
+}
+
+/*==========================================
+ * 項の解析
+ *------------------------------------------
+ */
+unsigned char* parse_simpleexpr(unsigned char *p)
+{
+ int i;
+ p=skip_space(p);
+
+#ifdef DEBUG_FUNCIN
+ if(battle_config.etc_log)
+ ShowDebug("parse_simpleexpr %s\n",p);
+#endif
+ if(*p==';' || *p==','){
+ disp_error_message("unexpected expr end",p);
+ exit(1);
+ }
+ if(*p=='('){
+
+ p=parse_subexpr(p+1,-1);
+ p=skip_space(p);
+ if((*p++)!=')'){
+ disp_error_message("unmatch ')'",p);
+ exit(1);
+ }
+ } else if(isdigit(*p) || ((*p=='-' || *p=='+') && isdigit(p[1]))){
+ char *np;
+ i=strtoul((char *) p,&np,0);
+ add_scripti(i);
+ p=(unsigned char *) np;
+ } else if(*p=='"'){
+ add_scriptc(C_STR);
+ p++;
+ while(*p && *p!='"'){
+ if(p[-1]<=0x7e && *p=='\\')
+ p++;
+ else if(*p=='\n'){
+ disp_error_message("unexpected newline @ string",p);
+ exit(1);
+ }
+ add_scriptb(*p++);
+ }
+ if(!*p){
+ disp_error_message("unexpected eof @ string",p);
+ exit(1);
+ }
+ add_scriptb(0);
+ p++; //'"'
+ } else {
+ int c,l;
+ char *p2;
+ // label , register , function etc
+ if(skip_word(p)==p && !(*p==')' && p[-1]=='(')){
+ disp_error_message("unexpected character",p);
+ exit(1);
+ }
+
+ p2=(char *) skip_word(p);
+ c=*p2; *p2=0; // 名前をadd_strする
+ l=add_str(p);
+
+ parse_cmd=l; // warn_*_mismatch_paramnumのために必要
+
+ *p2=c;
+ p=(unsigned char *) p2;
+
+ if(str_data[l].type!=C_FUNC && c=='['){
+ // array(name[i] => getelementofarray(name,i) )
+ add_scriptl(search_str((unsigned char *) "getelementofarray"));
+ add_scriptc(C_ARG);
+ add_scriptl(l);
+ p=parse_subexpr(p+1,-1);
+ p=skip_space(p);
+ if((*p++)!=']'){
+ disp_error_message("unmatch ']'",p);
+ exit(1);
+ }
+ add_scriptc(C_FUNC);
+ } else if(str_data[l].type == C_USERFUNC || str_data[l].type == C_USERFUNC_POS) {
+ add_scriptl(search_str((unsigned char*)"callsub"));
+ add_scriptc(C_ARG);
+ add_scriptl(l);
+ }else
+ add_scriptl(l);
+
+ }
+
+#ifdef DEBUG_FUNCIN
+ if(battle_config.etc_log)
+ ShowDebug("parse_simpleexpr end %s\n",p);
+#endif
+ return p;
+}
+
+/*==========================================
+ * 式の解析
+ *------------------------------------------
+ */
+unsigned char* parse_subexpr(unsigned char *p,int limit)
+{
+ int op,opl,len;
+ char *tmpp;
+
+#ifdef DEBUG_FUNCIN
+ if(battle_config.etc_log)
+ ShowDebug("parse_subexpr %s\n",p);
+#endif
+ p=skip_space(p);
+
+ if(*p=='-'){
+ tmpp=(char *) skip_space((unsigned char *) (p+1));
+ if(*tmpp==';' || *tmpp==','){
+ add_scriptl(LABEL_NEXTLINE);
+ p++;
+ return p;
+ }
+ }
+ tmpp=(char *) p;
+ if((op=C_NEG,*p=='-') || (op=C_LNOT,*p=='!') || (op=C_NOT,*p=='~')){
+ p=parse_subexpr(p+1,100);
+ add_scriptc(op);
+ } else
+ p=parse_simpleexpr(p);
+ p=skip_space(p);
+ while(((op=C_ADD,opl=6,len=1,*p=='+') ||
+ (op=C_SUB,opl=6,len=1,*p=='-') ||
+ (op=C_MUL,opl=7,len=1,*p=='*') ||
+ (op=C_DIV,opl=7,len=1,*p=='/') ||
+ (op=C_MOD,opl=7,len=1,*p=='%') ||
+ (op=C_FUNC,opl=8,len=1,*p=='(') ||
+ (op=C_LAND,opl=1,len=2,*p=='&' && p[1]=='&') ||
+ (op=C_AND,opl=5,len=1,*p=='&') ||
+ (op=C_LOR,opl=0,len=2,*p=='|' && p[1]=='|') ||
+ (op=C_OR,opl=4,len=1,*p=='|') ||
+ (op=C_XOR,opl=3,len=1,*p=='^') ||
+ (op=C_EQ,opl=2,len=2,*p=='=' && p[1]=='=') ||
+ (op=C_NE,opl=2,len=2,*p=='!' && p[1]=='=') ||
+ (op=C_R_SHIFT,opl=5,len=2,*p=='>' && p[1]=='>') ||
+ (op=C_GE,opl=2,len=2,*p=='>' && p[1]=='=') ||
+ (op=C_GT,opl=2,len=1,*p=='>') ||
+ (op=C_L_SHIFT,opl=5,len=2,*p=='<' && p[1]=='<') ||
+ (op=C_LE,opl=2,len=2,*p=='<' && p[1]=='=') ||
+ (op=C_LT,opl=2,len=1,*p=='<')) && opl>limit){
+ p+=len;
+ if(op==C_FUNC){
+ int i=0,func=parse_cmd;
+ const char *plist[128];
+
+ if(str_data[parse_cmd].type == C_FUNC){
+ // 通常の関数
+ add_scriptc(C_ARG);
+ } else if(str_data[parse_cmd].type == C_USERFUNC || str_data[parse_cmd].type == C_USERFUNC_POS) {
+ // ユーザー定義関数呼び出し
+ parse_cmd = search_str((unsigned char*)"callsub");
+ i++;
+ } else {
+ disp_error_message(
+ "expect command, missing function name or calling undeclared function",(unsigned char *) tmpp
+ );
+ exit(0);
+ }
+ func=parse_cmd;
+
+ do {
+ plist[i]=(char *) p;
+ p=parse_subexpr(p,-1);
+ p=skip_space(p);
+ if(*p==',') p++;
+ else if(*p!=')' && script_config.warn_func_no_comma){
+ disp_error_message("expect ',' or ')' at func params",p);
+ }
+ p=skip_space(p);
+ i++;
+ } while(*p && *p!=')' && i<128);
+ plist[i]=(char *) p;
+ if(*(p++)!=')'){
+ disp_error_message("func request '(' ')'",p);
+ exit(1);
+ }
+
+ if (str_data[func].type == C_FUNC && script_config.warn_func_mismatch_paramnum) {
+ const char *arg = buildin_func[str_data[func].val].arg;
+ int j = 0;
+ for (; arg[j]; j++) if (arg[j] == '*') break;
+ if (!(i <= 1 && j == 0) && ((arg[j] == 0 && i != j) || (arg[j] == '*' && i < j))) {
+ disp_error_message("illegal number of parameters",(unsigned char *) (plist[(i<j)?i:j]));
+ }
+ }
+ } else {
+ p=parse_subexpr(p,opl);
+ }
+ add_scriptc(op);
+ p=skip_space(p);
+ }
+#ifdef DEBUG_FUNCIN
+ if(battle_config.etc_log)
+ ShowDebug("parse_subexpr end %s\n",p);
+#endif
+ return p; /* return first untreated operator */
+}
+
+/*==========================================
+ * 式の評価
+ *------------------------------------------
+ */
+unsigned char* parse_expr(unsigned char *p)
+{
+#ifdef DEBUG_FUNCIN
+ if(battle_config.etc_log)
+ ShowDebug("parse_expr %s\n",p);
+#endif
+ switch(*p){
+ case ')': case ';': case ':': case '[': case ']':
+ case '}':
+ disp_error_message("unexpected char",p);
+ exit(1);
+ }
+ p=parse_subexpr(p,-1);
+#ifdef DEBUG_FUNCIN
+ if(battle_config.etc_log)
+ ShowDebug("parse_expr end %s\n",p);
+#endif
+ return p;
+}
+
+/*==========================================
+ * 行の解析
+ *------------------------------------------
+ */
+unsigned char* parse_line(unsigned char *p)
+{
+ int i=0,cmd;
+ const char *plist[128];
+ unsigned char *p2;
+ char end;
+
+ p=skip_space(p);
+ if(*p==';')
+ return p + 1;
+
+ p = skip_space(p);
+ if(p[0] == '{') {
+ syntax.curly[syntax.curly_count].type = TYPE_NULL;
+ syntax.curly[syntax.curly_count].count = -1;
+ syntax.curly[syntax.curly_count].index = -1;
+ syntax.curly_count++;
+ return p + 1;
+ } else if(p[0] == '}') {
+ return parse_curly_close(p);
+ }
+
+ // 構文関連の処理
+ p2 = parse_syntax(p);
+ if(p2 != NULL) { return p2; }
+
+ // 最初は関数名
+ p2=(char *) p;
+ p=parse_simpleexpr(p);
+ p=skip_space(p);
+
+ if(str_data[parse_cmd].type == C_FUNC){
+ // 通常の関数
+ add_scriptc(C_ARG);
+ } else if(str_data[parse_cmd].type == C_USERFUNC || str_data[parse_cmd].type == C_USERFUNC_POS) {
+ // ユーザー定義関数呼び出し
+ parse_cmd = search_str((unsigned char*)"callsub");
+ i++;
+ } else {
+ disp_error_message(
+ "expect command, missing function name or calling undeclared function", (unsigned char *)p2
+ );
+// exit(0);
+ }
+ cmd=parse_cmd;
+
+ if(parse_syntax_for_flag) {
+ end = ')';
+ } else {
+ end = ';';
+ }
+ while(p && *p && *p!=end && i<128){
+ plist[i]=(char *) p;
+
+ p=parse_expr(p);
+ p=skip_space(p);
+ // 引数区切りの,処理
+ if(*p==',') p++;
+ else if(*p!=end && script_config.warn_cmd_no_comma && 0 <= i ){
+ if(parse_syntax_for_flag) {
+ disp_error_message("expect ',' or ')' at cmd params",p);
+ } else {
+ disp_error_message("expect ',' or ';' at cmd params",p);
+ }
+ }
+ p=skip_space(p);
+ i++;
+ }
+ plist[i]=(char *) p;
+ if(!p || *(p++)!=end){
+ if(parse_syntax_for_flag) {
+ disp_error_message("need ')'",p);
+ } else {
+ disp_error_message("need ';'",p);
+ }
+ exit(1);
+ }
+ add_scriptc(C_FUNC);
+
+ // if, for , while の閉じ判定
+ p = parse_syntax_close(p);
+
+ if( str_data[cmd].type==C_FUNC && script_config.warn_cmd_mismatch_paramnum){
+ const char *arg=buildin_func[str_data[cmd].val].arg;
+ int j=0;
+ for(j=0;arg[j];j++) if(arg[j]=='*')break;
+ if( (arg[j]==0 && i!=j) || (arg[j]=='*' && i<j) ){
+ disp_error_message("illegal number of parameters",(unsigned char *) (plist[(i<j)?i:j]));
+ }
+ }
+
+
+ return p;
+}
+
+
+// { ... } の閉じ処理
+unsigned char* parse_curly_close(unsigned char *p) {
+ if(syntax.curly_count <= 0) {
+ disp_error_message("unexpected string",p);
+ return p + 1;
+ } else if(syntax.curly[syntax.curly_count-1].type == TYPE_NULL) {
+ syntax.curly_count--;
+ // if, for , while の閉じ判定
+ p = parse_syntax_close(p + 1);
+ return p;
+ } else if(syntax.curly[syntax.curly_count-1].type == TYPE_SWITCH) {
+ // switch() 閉じ判定
+ int pos = syntax.curly_count-1;
+ unsigned char label[256];
+ int l;
+ // 一時変数を消す
+ sprintf(label,"set $@__SW%x_VAL,0;",syntax.curly[pos].index);
+ syntax.curly[syntax.curly_count++].type = TYPE_NULL;
+ parse_line(label);
+ syntax.curly_count--;
+
+ // 無条件で終了ポインタに移動
+ sprintf(label,"goto __SW%x_FIN;",syntax.curly[pos].index);
+ syntax.curly[syntax.curly_count++].type = TYPE_NULL;
+ parse_line(label);
+ syntax.curly_count--;
+
+ // 現在地のラベルを付ける
+ sprintf(label,"__SW%x_%x",syntax.curly[pos].index,syntax.curly[pos].count);
+ l=add_str(label);
+ if(str_data[l].label!=-1){
+ disp_error_message("dup label ",p);
+ exit(1);
+ }
+ set_label(l,script_pos);
+
+ if(syntax.curly[pos].flag) {
+ // default が存在する
+ sprintf(label,"goto __SW%x_DEF;",syntax.curly[pos].index);
+ syntax.curly[syntax.curly_count++].type = TYPE_NULL;
+ parse_line(label);
+ syntax.curly_count--;
+ }
+
+ // 終了ラベルを付ける
+ sprintf(label,"__SW%x_FIN",syntax.curly[pos].index);
+ l=add_str(label);
+ if(str_data[l].label!=-1){
+ disp_error_message("dup label ",p);
+ exit(1);
+ }
+ set_label(l,script_pos);
+
+ syntax.curly_count--;
+ return p+1;
+ } else {
+ disp_error_message("unexpected string",p);
+ return p + 1;
+ }
+}
+
+// 構文関連の処理
+// break, case, continue, default, do, for, function,
+// if, switch, while をこの内部で処理します。
+unsigned char* parse_syntax(unsigned char *p) {
+ switch(p[0]) {
+ case 'b':
+ if(!strncmp(p,"break",5) && !isalpha(*(p + 5))) {
+ // break の処理
+ char label[256];
+ int pos = syntax.curly_count - 1;
+ while(pos >= 0) {
+ if(syntax.curly[pos].type == TYPE_DO) {
+ sprintf(label,"goto __DO%x_FIN;",syntax.curly[pos].index);
+ break;
+ } else if(syntax.curly[pos].type == TYPE_FOR) {
+ sprintf(label,"goto __FR%x_FIN;",syntax.curly[pos].index);
+ break;
+ } else if(syntax.curly[pos].type == TYPE_WHILE) {
+ sprintf(label,"goto __WL%x_FIN;",syntax.curly[pos].index);
+ break;
+ } else if(syntax.curly[pos].type == TYPE_SWITCH) {
+ sprintf(label,"goto __SW%x_FIN;",syntax.curly[pos].index);
+ break;
+ }
+ pos--;
+ }
+ if(pos < 0) {
+ disp_error_message("unexpected 'break'",p);
+ } else {
+ syntax.curly[syntax.curly_count++].type = TYPE_NULL;
+ parse_line(label);
+ syntax.curly_count--;
+ }
+ p = skip_word(p);
+ p++;
+ // if, for , while の閉じ判定
+ p = parse_syntax_close(p + 1);
+ return p;
+ }
+ break;
+ case 'c':
+ if(!strncmp(p,"case",4) && !isalpha(*(p + 4))) {
+ // case の処理
+ if(syntax.curly_count <= 0 || syntax.curly[syntax.curly_count - 1].type != TYPE_SWITCH) {
+ disp_error_message("unexpected 'case' ",p);
+ return p+1;
+ } else {
+ char *p2;
+ char label[256];
+ int l;
+ int pos = syntax.curly_count-1;
+ if(syntax.curly[pos].count != 1) {
+ // FALLTHRU 用のジャンプ
+ sprintf(label,"goto __SW%x_%xJ;",syntax.curly[pos].index,syntax.curly[pos].count);
+ syntax.curly[syntax.curly_count++].type = TYPE_NULL;
+ parse_line(label);
+ syntax.curly_count--;
+
+ // 現在地のラベルを付ける
+ sprintf(label,"__SW%x_%x",syntax.curly[pos].index,syntax.curly[pos].count);
+ l=add_str(label);
+ if(str_data[l].label!=-1){
+ disp_error_message("dup label ",p);
+ exit(1);
+ }
+ set_label(l,script_pos);
+ }
+ // switch 判定文
+ p = skip_word(p);
+ p = skip_space(p);
+ p2 = p;
+ p = skip_word(p);
+ p = skip_space(p);
+ if(*p != ':') {
+ disp_error_message("expect ':'",p);
+ exit(1);
+ }
+ *p = 0;
+ sprintf(label,"if(%s != $@__SW%x_VAL) goto __SW%x_%x;",
+ p2,syntax.curly[pos].index,syntax.curly[pos].index,syntax.curly[pos].count+1);
+ syntax.curly[syntax.curly_count++].type = TYPE_NULL;
+ *p = ':';
+ // 2回parse しないとダメ
+ p2 = parse_line(label);
+ parse_line(p2);
+ syntax.curly_count--;
+ if(syntax.curly[pos].count != 1) {
+ // FALLTHRU 終了後のラベル
+ sprintf(label,"__SW%x_%xJ",syntax.curly[pos].index,syntax.curly[pos].count);
+ l=add_str(label);
+ if(str_data[l].label!=-1){
+ disp_error_message("dup label ",p);
+ exit(1);
+ }
+ set_label(l,script_pos);
+ }
+ // 一時変数を消す
+ sprintf(label,"set $@__SW%x_VAL,0;",syntax.curly[pos].index);
+ syntax.curly[syntax.curly_count++].type = TYPE_NULL;
+ parse_line(label);
+ syntax.curly_count--;
+ syntax.curly[pos].count++;
+ }
+ return p + 1;
+ } else if(!strncmp(p,"continue",8) && !isalpha(*(p + 8))) {
+ // continue の処理
+ char label[256];
+ int pos = syntax.curly_count - 1;
+ while(pos >= 0) {
+ if(syntax.curly[pos].type == TYPE_DO) {
+ sprintf(label,"goto __DO%x_NXT;",syntax.curly[pos].index);
+ syntax.curly[pos].flag = 1; // continue 用のリンク張るフラグ
+ break;
+ } else if(syntax.curly[pos].type == TYPE_FOR) {
+ sprintf(label,"goto __FR%x_NXT;",syntax.curly[pos].index);
+ break;
+ } else if(syntax.curly[pos].type == TYPE_WHILE) {
+ sprintf(label,"goto __WL%x_NXT;",syntax.curly[pos].index);
+ break;
+ }
+ pos--;
+ }
+ if(pos < 0) {
+ disp_error_message("unexpected 'continue'",p);
+ } else {
+ syntax.curly[syntax.curly_count++].type = TYPE_NULL;
+ parse_line(label);
+ syntax.curly_count--;
+ }
+ p = skip_word(p);
+ p++;
+ // if, for , while の閉じ判定
+ p = parse_syntax_close(p + 1);
+ return p;
+ }
+ break;
+ case 'd':
+ if(!strncmp(p,"default",7) && !isalpha(*(p + 7))) {
+ // switch - default の処理
+ if(syntax.curly_count <= 0 || syntax.curly[syntax.curly_count - 1].type != TYPE_SWITCH) {
+ disp_error_message("unexpected 'delault'",p);
+ return p+1;
+ } else if(syntax.curly[syntax.curly_count - 1].flag) {
+ disp_error_message("dup 'delault'",p);
+ return p+1;
+ } else {
+ char label[256];
+ int l;
+ int pos = syntax.curly_count-1;
+ // 現在地のラベルを付ける
+ p = skip_word(p);
+ p = skip_space(p);
+ if(*p != ':') {
+ disp_error_message("need ':'",p);
+ }
+ p++;
+ sprintf(label,"__SW%x_%x",syntax.curly[pos].index,syntax.curly[pos].count);
+ l=add_str(label);
+ if(str_data[l].label!=-1){
+ disp_error_message("dup label ",p);
+ exit(1);
+ }
+ set_label(l,script_pos);
+
+ // 無条件で次のリンクに飛ばす
+ sprintf(label,"goto __SW%x_%x;",syntax.curly[pos].index,syntax.curly[pos].count+1);
+ syntax.curly[syntax.curly_count++].type = TYPE_NULL;
+ parse_line(label);
+ syntax.curly_count--;
+
+ // default のラベルを付ける
+ sprintf(label,"__SW%x_DEF",syntax.curly[pos].index);
+ l=add_str(label);
+ if(str_data[l].label!=-1){
+ disp_error_message("dup label ",p);
+ exit(1);
+ }
+ set_label(l,script_pos);
+
+ syntax.curly[syntax.curly_count - 1].flag = 1;
+ syntax.curly[pos].count++;
+
+ p = skip_word(p);
+ return p + 1;
+ }
+ } else if(!strncmp(p,"do",2) && !isalpha(*(p + 2))) {
+ int l;
+ char label[256];
+ p=skip_word(p);
+ p=skip_space(p);
+
+ syntax.curly[syntax.curly_count].type = TYPE_DO;
+ syntax.curly[syntax.curly_count].count = 1;
+ syntax.curly[syntax.curly_count].index = syntax.index++;
+ syntax.curly[syntax.curly_count].flag = 0;
+ // 現在地のラベル形成する
+ sprintf(label,"__DO%x_BGN",syntax.curly[syntax.curly_count].index);
+ l=add_str(label);
+ if(str_data[l].label!=-1){
+ disp_error_message("dup label ",p);
+ exit(1);
+ }
+ set_label(l,script_pos);
+ syntax.curly_count++;
+ return p;
+ }
+ break;
+ case 'f':
+ if(!strncmp(p,"for",3) && !isalpha(*(p + 3))) {
+ int l;
+ unsigned char label[256];
+ int pos = syntax.curly_count;
+ syntax.curly[syntax.curly_count].type = TYPE_FOR;
+ syntax.curly[syntax.curly_count].count = 1;
+ syntax.curly[syntax.curly_count].index = syntax.index++;
+ syntax.curly[syntax.curly_count].flag = 0;
+ syntax.curly_count++;
+
+ p=skip_word(p);
+ p=skip_space(p);
+
+ if(*p != '(') {
+ disp_error_message("need '('",p);
+ return p+1;
+ }
+ p++;
+
+ // 初期化文を実行する
+ syntax.curly[syntax.curly_count++].type = TYPE_NULL;
+ p=parse_line(p);
+ syntax.curly_count--;
+
+ // 条件判断開始のラベル形成する
+ sprintf(label,"__FR%x_J",syntax.curly[pos].index);
+ l=add_str(label);
+ if(str_data[l].label!=-1){
+ disp_error_message("dup label ",p);
+ exit(1);
+ }
+ set_label(l,script_pos);
+
+ if(*p == ';') {
+ // for(;;) のパターンなので必ず真
+ ;
+ } else {
+ // 条件が偽なら終了地点に飛ばす
+ sprintf(label,"__FR%x_FIN",syntax.curly[pos].index);
+ add_scriptl(add_str("jump_zero"));
+ add_scriptc(C_ARG);
+ p=parse_expr(p);
+ p=skip_space(p);
+ add_scriptl(add_str(label));
+ add_scriptc(C_FUNC);
+ }
+ if(*p != ';') {
+ disp_error_message("need ';'",p);
+ return p+1;
+ }
+ p++;
+
+ // ループ開始に飛ばす
+ sprintf(label,"goto __FR%x_BGN;",syntax.curly[pos].index);
+ syntax.curly[syntax.curly_count++].type = TYPE_NULL;
+ parse_line(label);
+ syntax.curly_count--;
+
+ // 次のループへのラベル形成する
+ sprintf(label,"__FR%x_NXT",syntax.curly[pos].index);
+ l=add_str(label);
+ if(str_data[l].label!=-1){
+ disp_error_message("dup label ",p);
+ exit(1);
+ }
+ set_label(l,script_pos);
+
+ // 次のループに入る時の処理
+ // for 最後の '(' を ';' として扱うフラグ
+ parse_syntax_for_flag = 1;
+ syntax.curly[syntax.curly_count++].type = TYPE_NULL;
+ p=parse_line(p);
+ syntax.curly_count--;
+ parse_syntax_for_flag = 0;
+
+ // 条件判定処理に飛ばす
+ sprintf(label,"goto __FR%x_J;",syntax.curly[pos].index);
+ syntax.curly[syntax.curly_count++].type = TYPE_NULL;
+ parse_line(label);
+ syntax.curly_count--;
+
+ // ループ開始のラベル付け
+ sprintf(label,"__FR%x_BGN",syntax.curly[pos].index);
+ l=add_str(label);
+ if(str_data[l].label!=-1){
+ disp_error_message("dup label ",p);
+ exit(1);
+ }
+ set_label(l,script_pos);
+ return p;
+ } else if(!strncmp(p,"function",8) && !isalpha(*(p + 8))) {
+ unsigned char *func_name;
+ // function
+ p=skip_word(p);
+ p=skip_space(p);
+ // function - name
+ func_name = p;
+ p=skip_word(p);
+ if(*skip_space(p) == ';') {
+ // 関数の宣言 - 名前を登録して終わり
+ unsigned char c = *p;
+ int l;
+ *p = 0;
+ l=add_str(func_name);
+ *p = c;
+ if(str_data[l].type == C_NOP) {
+ str_data[l].type = C_USERFUNC;
+ }
+ return skip_space(p) + 1;
+ } else {
+ // 関数の中身
+ char label[256];
+ unsigned char c = *p;
+ int l;
+ syntax.curly[syntax.curly_count].type = TYPE_USERFUNC;
+ syntax.curly[syntax.curly_count].count = 1;
+ syntax.curly[syntax.curly_count].index = syntax.index++;
+ syntax.curly[syntax.curly_count].flag = 0;
+ syntax.curly_count++;
+
+ // 関数終了まで飛ばす
+ sprintf(label,"goto __FN%x_FIN;",syntax.curly[syntax.curly_count-1].index);
+ syntax.curly[syntax.curly_count++].type = TYPE_NULL;
+ parse_line(label);
+ syntax.curly_count--;
+
+ // 関数名のラベルを付ける
+ *p = 0;
+ l=add_str(func_name);
+ if(str_data[l].type == C_NOP) {
+ str_data[l].type = C_USERFUNC;
+ }
+ if(str_data[l].label!=-1){
+ *p=c;
+ disp_error_message("dup label ",p);
+ exit(1);
+ }
+ set_label(l,script_pos);
+ strdb_put(scriptlabel_db,func_name,(void*)script_pos); // 外部用label db登録
+ *p = c;
+ return skip_space(p);
+ }
+ }
+ break;
+ case 'i':
+ if(!strncmp(p,"if",2) && !isalpha(*(p + 2))) {
+ // if() の処理
+ char label[256];
+ p=skip_word(p);
+ p=skip_space(p);
+
+ syntax.curly[syntax.curly_count].type = TYPE_IF;
+ syntax.curly[syntax.curly_count].count = 1;
+ syntax.curly[syntax.curly_count].index = syntax.index++;
+ syntax.curly[syntax.curly_count].flag = 0;
+ sprintf(label,"__IF%x_%x",syntax.curly[syntax.curly_count].index,syntax.curly[syntax.curly_count].count);
+ syntax.curly_count++;
+ add_scriptl(add_str("jump_zero"));
+ add_scriptc(C_ARG);
+ p=parse_expr(p);
+ p=skip_space(p);
+ add_scriptl(add_str(label));
+ add_scriptc(C_FUNC);
+ return p;
+ }
+ break;
+ case 's':
+ if(!strncmp(p,"switch",6) && !isalpha(*(p + 6))) {
+ // switch() の処理
+ char label[256];
+ syntax.curly[syntax.curly_count].type = TYPE_SWITCH;
+ syntax.curly[syntax.curly_count].count = 1;
+ syntax.curly[syntax.curly_count].index = syntax.index++;
+ syntax.curly[syntax.curly_count].flag = 0;
+ sprintf(label,"$@__SW%x_VAL",syntax.curly[syntax.curly_count].index);
+ syntax.curly_count++;
+ add_scriptl(add_str((unsigned char*)"set"));
+ add_scriptc(C_ARG);
+ add_scriptl(add_str(label));
+ p=skip_word(p);
+ p=skip_space(p);
+ p=parse_expr(p);
+ p=skip_space(p);
+ if(*p != '{') {
+ disp_error_message("need '{'",p);
+ }
+ add_scriptc(C_FUNC);
+ return p + 1;
+ }
+ break;
+ case 'w':
+ if(!strncmp(p,"while",5) && !isalpha(*(p + 5))) {
+ int l;
+ char label[256];
+ p=skip_word(p);
+ p=skip_space(p);
+
+ syntax.curly[syntax.curly_count].type = TYPE_WHILE;
+ syntax.curly[syntax.curly_count].count = 1;
+ syntax.curly[syntax.curly_count].index = syntax.index++;
+ syntax.curly[syntax.curly_count].flag = 0;
+ // 条件判断開始のラベル形成する
+ sprintf(label,"__WL%x_NXT",syntax.curly[syntax.curly_count].index);
+ l=add_str(label);
+ if(str_data[l].label!=-1){
+ disp_error_message("dup label ",p);
+ exit(1);
+ }
+ set_label(l,script_pos);
+
+ // 条件が偽なら終了地点に飛ばす
+ sprintf(label,"__WL%x_FIN",syntax.curly[syntax.curly_count].index);
+ add_scriptl(add_str("jump_zero"));
+ add_scriptc(C_ARG);
+ p=parse_expr(p);
+ p=skip_space(p);
+ add_scriptl(add_str(label));
+ add_scriptc(C_FUNC);
+ syntax.curly_count++;
+ return p;
+ }
+ break;
+ }
+ return NULL;
+}
+
+unsigned char* parse_syntax_close(unsigned char *p) {
+ // if(...) for(...) hoge(); のように、1度閉じられたら再度閉じられるか確認する
+ int flag;
+
+ do {
+ p = parse_syntax_close_sub(p,&flag);
+ } while(flag);
+ return p;
+}
+
+// if, for , while , do の閉じ判定
+// flag == 1 : 閉じられた
+// flag == 0 : 閉じられない
+unsigned char* parse_syntax_close_sub(unsigned char *p,int *flag) {
+ unsigned char label[256];
+ int pos = syntax.curly_count - 1;
+ int l;
+ *flag = 1;
+
+ if(syntax.curly_count <= 0) {
+ *flag = 0;
+ return p;
+ } else if(syntax.curly[pos].type == TYPE_IF) {
+ char *p2 = p;
+ // if 最終場所へ飛ばす
+ sprintf(label,"goto __IF%x_FIN;",syntax.curly[pos].index);
+ syntax.curly[syntax.curly_count++].type = TYPE_NULL;
+ parse_line(label);
+ syntax.curly_count--;
+
+ // 現在地のラベルを付ける
+ sprintf(label,"__IF%x_%x",syntax.curly[pos].index,syntax.curly[pos].count);
+ l=add_str(label);
+ if(str_data[l].label!=-1){
+ disp_error_message("dup label ",p);
+ exit(1);
+ }
+ set_label(l,script_pos);
+
+ syntax.curly[pos].count++;
+ p = skip_space(p);
+ if(!syntax.curly[pos].flag && !strncmp(p,"else",4) && !isalpha(*(p + 4))) {
+ // else or else - if
+ p = skip_word(p);
+ p = skip_space(p);
+ if(!strncmp(p,"if",2) && !isalpha(*(p + 2))) {
+ // else - if
+ p=skip_word(p);
+ p=skip_space(p);
+ sprintf(label,"__IF%x_%x",syntax.curly[pos].index,syntax.curly[pos].count);
+ add_scriptl(add_str("jump_zero"));
+ add_scriptc(C_ARG);
+ p=parse_expr(p);
+ p=skip_space(p);
+ add_scriptl(add_str(label));
+ add_scriptc(C_FUNC);
+ *flag = 0;
+ return p;
+ } else {
+ // else
+ if(!syntax.curly[pos].flag) {
+ syntax.curly[pos].flag = 1;
+ *flag = 0;
+ return p;
+ }
+ }
+ }
+ // if 閉じ
+ syntax.curly_count--;
+ // 最終地のラベルを付ける
+ sprintf(label,"__IF%x_FIN",syntax.curly[pos].index);
+ l=add_str(label);
+ if(str_data[l].label!=-1){
+ disp_error_message("dup label ",p);
+ exit(1);
+ }
+ set_label(l,script_pos);
+ if(syntax.curly[pos].flag == 1) {
+ // このifに対するelseじゃないのでポインタの位置は同じ
+ return p2;
+ }
+ return p;
+ } else if(syntax.curly[pos].type == TYPE_DO) {
+ int l;
+ char label[256];
+ unsigned char *p2;
+
+ if(syntax.curly[pos].flag) {
+ // 現在地のラベル形成する(continue でここに来る)
+ sprintf(label,"__DO%x_NXT",syntax.curly[pos].index);
+ l=add_str(label);
+ if(str_data[l].label!=-1){
+ disp_error_message("dup label ",p);
+ exit(1);
+ }
+ set_label(l,script_pos);
+ }
+
+ // 条件が偽なら終了地点に飛ばす
+ p = skip_space(p);
+ p2 = skip_word(p);
+ if(p2 - p != 5 || strncmp("while",p,5)) {
+ disp_error_message("need 'while'",p);
+ }
+ p = p2;
+
+ sprintf(label,"__DO%x_FIN",syntax.curly[pos].index);
+ add_scriptl(add_str("jump_zero"));
+ add_scriptc(C_ARG);
+ p=parse_expr(p);
+ p=skip_space(p);
+ add_scriptl(add_str(label));
+ add_scriptc(C_FUNC);
+
+ // 開始地点に飛ばす
+ sprintf(label,"goto __DO%x_BGN;",syntax.curly[pos].index);
+ syntax.curly[syntax.curly_count++].type = TYPE_NULL;
+ parse_line(label);
+ syntax.curly_count--;
+
+ // 条件終了地点のラベル形成する
+ sprintf(label,"__DO%x_FIN",syntax.curly[pos].index);
+ l=add_str(label);
+ if(str_data[l].label!=-1){
+ disp_error_message("dup label ",p);
+ exit(1);
+ }
+ set_label(l,script_pos);
+ p = skip_space(p);
+ if(*p != ';') {
+ disp_error_message("need ';'",p);
+ return p+1;
+ }
+ p++;
+ syntax.curly_count--;
+ return p;
+ } else if(syntax.curly[pos].type == TYPE_FOR) {
+ // 次のループに飛ばす
+ sprintf(label,"goto __FR%x_NXT;",syntax.curly[pos].index);
+ syntax.curly[syntax.curly_count++].type = TYPE_NULL;
+ parse_line(label);
+ syntax.curly_count--;
+
+ // for 終了のラベル付け
+ sprintf(label,"__FR%x_FIN",syntax.curly[pos].index);
+ l=add_str(label);
+ if(str_data[l].label!=-1){
+ disp_error_message("dup label ",p);
+ exit(1);
+ }
+ set_label(l,script_pos);
+ syntax.curly_count--;
+ return p;
+ } else if(syntax.curly[pos].type == TYPE_WHILE) {
+ // while 条件判断へ飛ばす
+ sprintf(label,"goto __WL%x_NXT;",syntax.curly[pos].index);
+ syntax.curly[syntax.curly_count++].type = TYPE_NULL;
+ parse_line(label);
+ syntax.curly_count--;
+
+ // while 終了のラベル付け
+ sprintf(label,"__WL%x_FIN",syntax.curly[pos].index);
+ l=add_str(label);
+ if(str_data[l].label!=-1){
+ disp_error_message("dup label ",p);
+ exit(1);
+ }
+ set_label(l,script_pos);
+ syntax.curly_count--;
+ return p;
+ } else if(syntax.curly[syntax.curly_count-1].type == TYPE_USERFUNC) {
+ int pos = syntax.curly_count-1;
+ char label[256];
+ int l;
+ // 戻す
+ sprintf(label,"return;");
+ syntax.curly[syntax.curly_count++].type = TYPE_NULL;
+ parse_line(label);
+ syntax.curly_count--;
+
+ // 現在地のラベルを付ける
+ sprintf(label,"__FN%x_FIN",syntax.curly[pos].index);
+ l=add_str(label);
+ if(str_data[l].label!=-1){
+ disp_error_message("dup label ",p);
+ exit(1);
+ }
+ set_label(l,script_pos);
+ syntax.curly_count--;
+ return p + 1;
+ } else {
+ *flag = 0;
+ return p;
+ }
+}
+
+/*==========================================
+ * 組み込み関数の追加
+ *------------------------------------------
+ */
+static void add_buildin_func(void)
+{
+ int i,n;
+ for(i=0;buildin_func[i].func;i++){
+ n=add_str((unsigned char *) buildin_func[i].name);
+ str_data[n].type=C_FUNC;
+ str_data[n].val=i;
+ str_data[n].func=buildin_func[i].func;
+ }
+}
+
+/*==========================================
+ * 定数データベースの読み込み
+ *------------------------------------------
+ */
+static void read_constdb(void)
+{
+ FILE *fp;
+ char line[1024],name[1024];
+ int val,n,i,type;
+
+ sprintf(line, "%s/const.txt", db_path);
+ fp=fopen(line, "r");
+ if(fp==NULL){
+ ShowError("can't read %s\n", line);
+ return ;
+ }
+ while(fgets(line,1020,fp)){
+ if(line[0]=='/' && line[1]=='/')
+ continue;
+ type=0;
+ if(sscanf(line,"%[A-Za-z0-9_],%d,%d",name,&val,&type)>=2 ||
+ sscanf(line,"%[A-Za-z0-9_] %d %d",name,&val,&type)>=2){
+ for(i=0;name[i];i++)
+ name[i]=tolower(name[i]);
+ n=add_str((const unsigned char *) name);
+ if(type==0)
+ str_data[n].type=C_INT;
+ else
+ str_data[n].type=C_PARAM;
+ str_data[n].val=val;
+ }
+ }
+ fclose(fp);
+}
+
+/*==========================================
+ * スクリプトの解析
+ *------------------------------------------
+ */
+unsigned char* parse_script(unsigned char *src,int line)
+{
+ unsigned char *p, *tmpp;
+ int i;
+ static int first = 1;
+
+ if (first) {
+ add_buildin_func();
+ read_constdb();
+ }
+ first = 0;
+
+//////////////////////////////////////////////
+// additional check on the input to filter empty scripts ("{}" and "{ }")
+ p = src;
+ p = skip_space(p);
+ if (*p != '{') {
+ disp_error_message("not found '{'", p);
+ return NULL;
+ }
+ p++;
+ p = skip_space(p);
+ if (*p == '}') {
+ // an empty function, just return
+ return NULL;
+ }
+ script_buf = (unsigned char *) aCallocA(SCRIPT_BLOCK_SIZE, sizeof(unsigned char));
+ script_pos = 0;
+ script_size = SCRIPT_BLOCK_SIZE;
+ str_data[LABEL_NEXTLINE].type = C_NOP;
+ str_data[LABEL_NEXTLINE].backpatch = -1;
+ str_data[LABEL_NEXTLINE].label = -1;
+ for (i = LABEL_START; i < str_num; i++) {
+ if (
+ str_data[i].type == C_POS || str_data[i].type == C_NAME ||
+ str_data[i].type == C_USERFUNC || str_data[i].type == C_USERFUNC_POS
+ ) {
+ str_data[i].type = C_NOP;
+ str_data[i].backpatch = -1;
+ str_data[i].label = -1;
+ }
+ }
+
+ //Labels must be reparsed for the script....
+ scriptlabel_db->clear(scriptlabel_db, NULL);
+
+ // for error message
+ startptr = src;
+ startline = line;
+
+ while (p && *p && (*p != '}' || syntax.curly_count != 0)) {
+ p = skip_space(p);
+ // labelだけ特殊処理
+ tmpp = skip_space(skip_word(p));
+ if (*tmpp == ':' && !(!strncmp(p,"default",7) && !isalpha(*(p + 7)))) {
+ int l, c;
+ c = *skip_word(p);
+ *skip_word(p) = 0;
+ l = add_str(p);
+ if (str_data[l].label != -1) {
+ *skip_word(p) = c;
+ disp_error_message("dup label ", p);
+ exit(1);
+ }
+ set_label(l, script_pos);
+ strdb_put(scriptlabel_db, p, (void*)script_pos); // 外部用label db登録
+ *skip_word(p) = c;
+ p = tmpp + 1;
+ continue;
+ }
+
+ // 他は全部一緒くた
+ p = parse_line(p);
+ p = skip_space(p);
+ add_scriptc(C_EOL);
+
+ set_label(LABEL_NEXTLINE, script_pos);
+ str_data[LABEL_NEXTLINE].type = C_NOP;
+ str_data[LABEL_NEXTLINE].backpatch = -1;
+ str_data[LABEL_NEXTLINE].label = -1;
+ }
+
+ add_scriptc(C_NOP);
+
+ script_size = script_pos;
+ script_buf = (unsigned char *)aRealloc(script_buf, script_pos + 1);
+
+ // 未解決のラベルを解決
+ for (i = LABEL_START; i < str_num; i++) {
+ if (str_data[i].type == C_NOP) {
+ int j, next;
+ str_data[i].type = C_NAME;
+ str_data[i].label = i;
+ for (j = str_data[i].backpatch; j >= 0 && j != 0x00ffffff; ) {
+ next = (*(int*)(script_buf+j)) & 0x00ffffff;
+ script_buf[j] = i;
+ script_buf[j+1] = i>>8;
+ script_buf[j+2] = i>>16;
+ j = next;
+ }
+ }
+ }
+
+#ifdef DEBUG_DISP
+ for (i = 0; i < script_pos; i++) {
+ if ((i & 15) == 0) printf("%04x : ", i);
+ printf("%02x ", script_buf[i]);
+ if((i&15) == 15) printf("\n");
+ }
+ printf("\n");
+#endif
+
+ startptr = NULL; //Clear pointer to prevent future references to a src that may be free'd. [Skotlex]
+ return (unsigned char *) script_buf;
+}
+
+//
+// 実行系
+//
+enum {RUN = 0,STOP,END,RERUNLINE,GOTO,RETFUNC};
+
+/*==========================================
+ * ridからsdへの解決
+ *------------------------------------------
+ */
+struct map_session_data *script_rid2sd(struct script_state *st)
+{
+ struct map_session_data *sd=map_id2sd(st->rid);
+ if(!sd){
+ ShowError("script_rid2sd: fatal error ! player not attached!\n");
+ report_src(st);
+ }
+ return sd;
+}
+
+
+/*==========================================
+ * 変数の読み取り
+ *------------------------------------------
+ */
+int get_val(struct script_state*st,struct script_data* data)
+{
+ struct map_session_data *sd=NULL;
+ if(data->type==C_NAME){
+ char *name=str_buf+str_data[data->u.num&0x00ffffff].str;
+ char prefix=*name;
+ char postfix=name[strlen(name)-1];
+
+ if(prefix!='$'){
+ if((sd=script_rid2sd(st))==NULL)
+ ShowError("get_val error name?:%s\n",name);
+ }
+ if(postfix=='$'){
+
+ data->type=C_CONSTSTR;
+ if( prefix=='@'){
+ if(sd)
+ data->u.str = pc_readregstr(sd,data->u.num);
+ }else if(prefix=='$'){
+ data->u.str = (char *)idb_get(mapregstr_db,data->u.num);
+ }else if(prefix=='#'){
+ if( name[1]=='#'){
+ if(sd)
+ data->u.str = pc_readaccountreg2str(sd,name);
+ }else{
+ if(sd)
+ data->u.str = pc_readaccountregstr(sd,name);
+ }
+ }else{
+ if(sd)
+ data->u.str = pc_readglobalreg_str(sd,name);
+ } // [zBuffer]
+ /*else{
+ ShowWarning("script: get_val: illegal scope string variable.\n");
+ data->u.str = "!!ERROR!!";
+ }*/
+ if( data->u.str == NULL )
+ data->u.str ="";
+
+ }else{
+
+ data->type=C_INT;
+ if(str_data[data->u.num&0x00ffffff].type==C_INT){
+ data->u.num = str_data[data->u.num&0x00ffffff].val;
+ }else if(str_data[data->u.num&0x00ffffff].type==C_PARAM){
+ if(sd)
+ data->u.num = pc_readparam(sd,str_data[data->u.num&0x00ffffff].val);
+ }else if(prefix=='@'){
+ if(sd)
+ data->u.num = pc_readreg(sd,data->u.num);
+ }else if(prefix=='$'){
+ data->u.num = (int)idb_get(mapreg_db,data->u.num);
+ }else if(prefix=='#'){
+ if( name[1]=='#'){
+ if(sd)
+ data->u.num = pc_readaccountreg2(sd,name);
+ }else{
+ if(sd)
+ data->u.num = pc_readaccountreg(sd,name);
+ }
+ }else{
+ if(sd)
+ data->u.num = pc_readglobalreg(sd,name);
+ }
+ }
+ }
+ return 0;
+}
+/*==========================================
+ * 変数の読み取り2
+ *------------------------------------------
+ */
+void* get_val2(struct script_state*st,int num)
+{
+ struct script_data dat;
+ dat.type=C_NAME;
+ dat.u.num=num;
+ get_val(st,&dat);
+ if( dat.type==C_INT ) return (void*)dat.u.num;
+ else return (void*)dat.u.str;
+}
+
+/*==========================================
+ * 変数設定用
+ *------------------------------------------
+ */
+static int set_reg(struct map_session_data *sd,int num,char *name,void *v)
+{
+ char prefix=*name;
+ char postfix=name[strlen(name)-1];
+
+ if( postfix=='$' ){
+ char *str=(char*)v;
+ if( prefix=='@'){
+ pc_setregstr(sd,num,str);
+ }else if(prefix=='$') {
+ mapreg_setregstr(num,str);
+ }else if(prefix=='#') {
+ if( name[1]=='#' )
+ pc_setaccountreg2str(sd,name,str);
+ else
+ pc_setaccountregstr(sd,name,str);
+ }else{
+ pc_setglobalreg_str(sd,name,str);
+ } // [zBuffer]
+
+ /*else{
+ ShowWarning("script: set_reg: illegal scope string variable !");
+ }*/
+ }else{
+ // 数値
+ int val = (int)v;
+ if(str_data[num&0x00ffffff].type==C_PARAM){
+ pc_setparam(sd,str_data[num&0x00ffffff].val,val);
+ }else if(prefix=='@') {
+ pc_setreg(sd,num,val);
+ }else if(prefix=='$') {
+ mapreg_setreg(num,val);
+ }else if(prefix=='#') {
+ if( name[1]=='#' )
+ pc_setaccountreg2(sd,name,val);
+ else
+ pc_setaccountreg(sd,name,val);
+ }else{
+ pc_setglobalreg(sd,name,val);
+ }
+ }
+ return 0;
+}
+
+int set_var(struct map_session_data *sd, char *name, void *val)
+{
+ return set_reg(sd, add_str((unsigned char *) name), name, val);
+}
+
+/*==========================================
+ * 文字列への変換
+ *------------------------------------------
+ */
+char* conv_str(struct script_state *st,struct script_data *data)
+{
+ get_val(st,data);
+ if(data->type==C_INT){
+ char *buf;
+ buf=(char *)aCallocA(ITEM_NAME_LENGTH,sizeof(char));
+ snprintf(buf,ITEM_NAME_LENGTH, "%d",data->u.num);
+ data->type=C_STR;
+ data->u.str=buf;
+#if 1
+ } else if(data->type==C_NAME){
+ // テンポラリ。本来無いはず
+ data->type=C_CONSTSTR;
+ data->u.str=str_buf+str_data[data->u.num].str;
+#endif
+ }
+ return data->u.str;
+}
+
+/*==========================================
+ * 数値へ変換
+ *------------------------------------------
+ */
+int conv_num(struct script_state *st,struct script_data *data)
+{
+ char *p;
+ get_val(st,data);
+ if(data->type==C_STR || data->type==C_CONSTSTR){
+ p=data->u.str;
+ data->u.num = atoi(p);
+ if(data->type==C_STR)
+ aFree(p);
+ data->type=C_INT;
+ }
+ return data->u.num;
+}
+
+/*==========================================
+ * スタックへ数値をプッシュ
+ *------------------------------------------
+ */
+void push_val(struct script_stack *stack,int type,int val)
+{
+ if(stack->sp >= stack->sp_max){
+ stack->sp_max += 64;
+ stack->stack_data = (struct script_data *)aRealloc(stack->stack_data,
+ sizeof(stack->stack_data[0]) * stack->sp_max);
+ memset(stack->stack_data + (stack->sp_max - 64), 0,
+ 64 * sizeof(*(stack->stack_data)));
+ }
+// if(battle_config.etc_log)
+// printf("push (%d,%d)-> %d\n",type,val,stack->sp);
+ stack->stack_data[stack->sp].type=type;
+ stack->stack_data[stack->sp].u.num=val;
+ stack->sp++;
+}
+
+/*==========================================
+ * スタックへ文字列をプッシュ
+ *------------------------------------------
+ */
+void push_str(struct script_stack *stack,int type,unsigned char *str)
+{
+ if(stack->sp>=stack->sp_max){
+ stack->sp_max += 64;
+ stack->stack_data = (struct script_data *)aRealloc(stack->stack_data,
+ sizeof(stack->stack_data[0]) * stack->sp_max);
+ memset(stack->stack_data + (stack->sp_max - 64), '\0',
+ 64 * sizeof(*(stack->stack_data)));
+ }
+// if(battle_config.etc_log)
+// printf("push (%d,%x)-> %d\n",type,str,stack->sp);
+ stack->stack_data[stack->sp].type=type;
+ stack->stack_data[stack->sp].u.str=(char *) str;
+ stack->sp++;
+}
+
+/*==========================================
+ * スタックへ複製をプッシュ
+ *------------------------------------------
+ */
+void push_copy(struct script_stack *stack,int pos)
+{
+ switch(stack->stack_data[pos].type){
+ case C_CONSTSTR:
+ push_str(stack,C_CONSTSTR,(unsigned char *) stack->stack_data[pos].u.str);
+ break;
+ case C_STR:
+ push_str(stack,C_STR,(unsigned char *) aStrdup(stack->stack_data[pos].u.str));
+ break;
+ default:
+ push_val(stack,stack->stack_data[pos].type,stack->stack_data[pos].u.num);
+ break;
+ }
+}
+
+/*==========================================
+ * スタックからポップ
+ *------------------------------------------
+ */
+void pop_stack(struct script_stack* stack,int start,int end)
+{
+ int i;
+ for(i=start;i<end;i++){
+ if(stack->stack_data[i].type==C_STR){
+ aFree(stack->stack_data[i].u.str);
+ stack->stack_data[i].type=C_INT; //Might not be correct, but it's done in case to prevent pointer errors later on. [Skotlex]
+ }
+ }
+ if(stack->sp>end){
+ memmove(&stack->stack_data[start],&stack->stack_data[end],sizeof(stack->stack_data[0])*(stack->sp-end));
+ }
+ stack->sp-=end-start;
+}
+
+/*==========================================
+ * Free's the whole stack. Invoked when clearing a character. [Skotlex]
+ *------------------------------------------
+ */
+void script_free_stack(struct script_stack* stack)
+{
+ int i;
+ for (i = 0; i < stack->sp; i++)
+ {
+ if(stack->stack_data[i].type==C_STR)
+ {
+ //ShowDebug ("script_free_stack: freeing %p at sp=%d.\n", stack->stack_data[i].u.str, i);
+ aFree(stack->stack_data[i].u.str);
+ stack->stack_data[i].type = C_INT;
+ }
+ }
+ aFree (stack->stack_data);
+ aFree (stack);
+}
+
+int axtoi(char *hexStg) {
+ int n = 0; // position in string
+ int m = 0; // position in digit[] to shift
+ int count; // loop index
+ int intValue = 0; // integer value of hex string
+ int digit[11]; // hold values to convert
+ while (n < 10) {
+ if (hexStg[n]=='\0')
+ break;
+ if (hexStg[n] > 0x29 && hexStg[n] < 0x40 ) //if 0 to 9
+ digit[n] = hexStg[n] & 0x0f; //convert to int
+ else if (hexStg[n] >='a' && hexStg[n] <= 'f') //if a to f
+ digit[n] = (hexStg[n] & 0x0f) + 9; //convert to int
+ else if (hexStg[n] >='A' && hexStg[n] <= 'F') //if A to F
+ digit[n] = (hexStg[n] & 0x0f) + 9; //convert to int
+ else break;
+ n++;
+ }
+ count = n;
+ m = n - 1;
+ n = 0;
+ while(n < count) {
+ // digit[n] is value of hex digit at position n
+ // (m << 2) is the number of positions to shift
+ // OR the bits into return value
+ intValue = intValue | (digit[n] << (m << 2));
+ m--; // adjust the position to set
+ n++; // next digit to process
+ }
+ return (intValue);
+}
+
+// [Lance] Hex string to integer converter
+int buildin_axtoi(struct script_state *st)
+{
+ char *hex = conv_str(st,& (st->stack->stack_data[st->start+2]));
+ push_val(st->stack, C_INT, axtoi(hex));
+ return 0;
+}
+
+//
+// 埋め込み関数
+//
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int buildin_mes(struct script_state *st)
+{
+ conv_str(st,& (st->stack->stack_data[st->start+2]));
+ clif_scriptmes(script_rid2sd(st),st->oid,st->stack->stack_data[st->start+2].u.str);
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int buildin_goto(struct script_state *st)
+{
+ int pos;
+
+ if (st->stack->stack_data[st->start+2].type != C_POS){
+ int func = st->stack->stack_data[st->start+2].u.num;
+ ShowMessage("script: goto '"CL_WHITE"%s"CL_RESET"': not label!\n", str_buf + str_data[func].str);
+ st->state = END;
+ return 1;
+ }
+
+ pos = conv_num(st,& (st->stack->stack_data[st->start+2]));
+ st->pos = pos;
+ st->state = GOTO;
+ return 0;
+}
+
+/*==========================================
+ * ユーザー定義関数の呼び出し
+ *------------------------------------------
+ */
+int buildin_callfunc(struct script_state *st)
+{
+ char *scr;
+ char *str=conv_str(st,& (st->stack->stack_data[st->start+2]));
+
+ if( (scr=(char *) strdb_get(userfunc_db,(unsigned char*)str)) ){
+ int i,j;
+ for(i=st->start+3,j=0;i<st->end;i++,j++)
+ push_copy(st->stack,i);
+
+ push_val(st->stack,C_INT,j); // 引数の数をプッシュ
+ push_val(st->stack,C_INT,st->stack->defsp); // 現在の基準スタックポインタをプッシュ
+ push_val(st->stack,C_INT,(int)st->script); // 現在のスクリプトをプッシュ
+ push_val(st->stack,C_RETINFO,st->pos); // 現在のスクリプト位置をプッシュ
+
+ st->pos=0;
+ st->script=scr;
+ st->stack->defsp=st->start+4+j;
+ st->state=GOTO;
+ }else{
+ ShowWarning("script:callfunc: function not found! [%s]\n",str);
+ st->state=END;
+ return 1;
+ }
+ return 0;
+}
+/*==========================================
+ * サブルーティンの呼び出し
+ *------------------------------------------
+ */
+int buildin_callsub(struct script_state *st)
+{
+ int pos=conv_num(st,& (st->stack->stack_data[st->start+2]));
+ int i,j;
+ if(st->stack->stack_data[st->start+2].type != C_POS && st->stack->stack_data[st->start+2].type != C_USERFUNC_POS) {
+ ShowError("script: callsub: not label !\n");
+ st->state=END;
+ return 1;
+ } else {
+ for(i=st->start+3,j=0;i<st->end;i++,j++)
+ push_copy(st->stack,i);
+
+ push_val(st->stack,C_INT,j); // 引数の数をプッシュ
+ push_val(st->stack,C_INT,st->stack->defsp); // 現在の基準スタックポインタをプッシュ
+ push_val(st->stack,C_INT,(int)st->script); // 現在のスクリプトをプッシュ
+ push_val(st->stack,C_RETINFO,st->pos); // 現在のスクリプト位置をプッシュ
+
+ st->pos=pos;
+ st->stack->defsp=st->start+4+j;
+ st->state=GOTO;
+ }
+ return 0;
+}
+
+/*==========================================
+ * 引数の所得
+ *------------------------------------------
+ */
+int buildin_getarg(struct script_state *st)
+{
+ int num=conv_num(st,& (st->stack->stack_data[st->start+2]));
+ int max,stsp;
+ if( st->stack->defsp<4 || st->stack->stack_data[st->stack->defsp-1].type!=C_RETINFO ){
+ ShowWarning("script:getarg without callfunc or callsub!\n");
+ st->state=END;
+ return 1;
+ }
+ max=conv_num(st,& (st->stack->stack_data[st->stack->defsp-4]));
+ stsp=st->stack->defsp - max -4;
+ if( num >= max ){
+ ShowWarning("script:getarg arg1(%d) out of range(%d) !\n",num,max);
+ st->state=END;
+ return 1;
+ }
+ push_copy(st->stack,stsp+num);
+ return 0;
+}
+
+/*==========================================
+ * サブルーチン/ユーザー定義関数の終了
+ *------------------------------------------
+ */
+int buildin_return(struct script_state *st)
+{
+ if(st->end>st->start+2){ // 戻り値有り
+ push_copy(st->stack,st->start+2);
+ }
+ st->state=RETFUNC;
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int buildin_next(struct script_state *st)
+{
+ st->state=STOP;
+ clif_scriptnext(script_rid2sd(st),st->oid);
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int buildin_close(struct script_state *st)
+{
+ st->state=END;
+ clif_scriptclose(script_rid2sd(st),st->oid);
+ return 0;
+}
+int buildin_close2(struct script_state *st)
+{
+ st->state=STOP;
+ clif_scriptclose(script_rid2sd(st),st->oid);
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int buildin_menu(struct script_state *st)
+{
+ char *buf;
+ int len,i;
+ struct map_session_data *sd;
+
+ sd=script_rid2sd(st);
+
+ if(sd->state.menu_or_input==0){
+ st->state=RERUNLINE;
+ sd->state.menu_or_input=1;
+ for(i=st->start+2,len=16;i<st->end;i+=2){
+ conv_str(st,& (st->stack->stack_data[i]));
+ len+=(int)strlen(st->stack->stack_data[i].u.str)+1;
+ }
+ buf=(char *)aCallocA(len+1,sizeof(char));
+ buf[0]=0;
+ for(i=st->start+2,len=0;i<st->end;i+=2){
+ strcat(buf,st->stack->stack_data[i].u.str);
+ strcat(buf,":");
+ }
+ clif_scriptmenu(script_rid2sd(st),st->oid,buf);
+ aFree(buf);
+ } else if(sd->npc_menu==0xff){ // cansel
+ sd->state.menu_or_input=0;
+ st->state=END;
+ } else { // goto動作
+ sd->state.menu_or_input=0;
+ if(sd->npc_menu>0){
+ //Skip empty menu entries which weren't displayed on the client (blackhole89)
+ for(i=st->start+2;i<=(st->start+sd->npc_menu*2) && sd->npc_menu<(st->end-st->start)/2;i+=2)
+ {
+ if((int)strlen(st->stack->stack_data[i].u.str) < 1)
+ sd->npc_menu++; //Empty selection which wasn't displayed on the client.
+ }
+ if(sd->npc_menu >= (st->end-st->start)/2) {
+ //Invalid selection.
+ st->state=END;
+ return 0;
+ }
+ if( st->stack->stack_data[st->start+sd->npc_menu*2+1].type!=C_POS ){
+ ShowError("script: menu: not label !\n");
+ st->state=END;
+ return 1;
+ }
+ pc_setreg(sd,add_str((unsigned char *) "@menu"),sd->npc_menu);
+ st->pos= conv_num(st,& (st->stack->stack_data[st->start+sd->npc_menu*2+1]));
+ st->state=GOTO;
+ }
+ }
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int buildin_rand(struct script_state *st)
+{
+ int range;
+
+ if (st->end > st->start+3){
+ int min, max;
+ min = conv_num(st,& (st->stack->stack_data[st->start+2]));
+ max = conv_num(st,& (st->stack->stack_data[st->start+3]));
+ if (max == min){ //Why would someone do this?
+ push_val(st->stack,C_INT,min);
+ return 0;
+ }
+ if (max < min){
+ int tmp = min;
+ min = max;
+ max = tmp;
+ }
+ range = max - min + 1;
+ if (range == 0) range = 1;
+ push_val(st->stack,C_INT,rand()%range+min);
+ } else {
+ range = conv_num(st,& (st->stack->stack_data[st->start+2]));
+ if (range == 0) range = 1;
+ push_val(st->stack,C_INT,rand()%range);
+ }
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int buildin_warp(struct script_state *st)
+{
+ int x,y;
+ char *str;
+ struct map_session_data *sd=script_rid2sd(st);
+
+ str=conv_str(st,& (st->stack->stack_data[st->start+2]));
+ x=conv_num(st,& (st->stack->stack_data[st->start+3]));
+ y=conv_num(st,& (st->stack->stack_data[st->start+4]));
+ if(strcmp(str,"Random")==0)
+ pc_randomwarp(sd,3);
+ else if(strcmp(str,"SavePoint")==0){
+ if(map[sd->bl.m].flag.noreturn) // 蝶禁止
+ return 0;
+
+ pc_setpos(sd,sd->status.save_point.map,
+ sd->status.save_point.x,sd->status.save_point.y,3);
+ }else if(strcmp(str,"Save")==0){
+ if(map[sd->bl.m].flag.noreturn) // 蝶禁止
+ return 0;
+
+ pc_setpos(sd,sd->status.save_point.map,
+ sd->status.save_point.x,sd->status.save_point.y,3);
+ }else
+ pc_setpos(sd,mapindex_name2id(str),x,y,0);
+ return 0;
+}
+/*==========================================
+ * エリア指定ワープ
+ *------------------------------------------
+ */
+int buildin_areawarp_sub(struct block_list *bl,va_list ap)
+{
+ int x,y;
+ unsigned int map;
+ map=va_arg(ap, unsigned int);
+ x=va_arg(ap,int);
+ y=va_arg(ap,int);
+ if(map == 0)
+ pc_randomwarp((struct map_session_data *)bl,3);
+ else
+ pc_setpos((struct map_session_data *)bl,map,x,y,0);
+ return 0;
+}
+int buildin_areawarp(struct script_state *st)
+{
+ int x,y,m;
+ unsigned int index;
+ char *str;
+ char *mapname;
+ int x0,y0,x1,y1;
+
+ mapname=conv_str(st,& (st->stack->stack_data[st->start+2]));
+ x0=conv_num(st,& (st->stack->stack_data[st->start+3]));
+ y0=conv_num(st,& (st->stack->stack_data[st->start+4]));
+ x1=conv_num(st,& (st->stack->stack_data[st->start+5]));
+ y1=conv_num(st,& (st->stack->stack_data[st->start+6]));
+ str=conv_str(st,& (st->stack->stack_data[st->start+7]));
+ x=conv_num(st,& (st->stack->stack_data[st->start+8]));
+ y=conv_num(st,& (st->stack->stack_data[st->start+9]));
+
+ if( (m=map_mapname2mapid(mapname))< 0)
+ return 0;
+
+ if(strcmp(str,"Random")==0)
+ index = 0;
+ else if(!(index=mapindex_name2id(str)))
+ return 0;
+
+ map_foreachinarea(buildin_areawarp_sub,
+ m,x0,y0,x1,y1,BL_PC, index,x,y );
+ return 0;
+}
+
+/*==========================================
+ * warpchar [LuzZza]
+ * Useful for warp one player from
+ * another player npc-session.
+ * Using: warpchar "mapname.gat",x,y,Char_ID;
+ *------------------------------------------
+ */
+int buildin_warpchar(struct script_state *st)
+{
+ int x,y,a,i;
+ char *str;
+ struct map_session_data *sd, **pl_allsd;
+ int users;
+
+ str=conv_str(st,& (st->stack->stack_data[st->start+2]));
+ x=conv_num(st,& (st->stack->stack_data[st->start+3]));
+ y=conv_num(st,& (st->stack->stack_data[st->start+4]));
+ a=conv_num(st,& (st->stack->stack_data[st->start+5]));
+
+ pl_allsd = map_getallusers(&users);
+
+ for(i=0; i<users; i++) {
+ sd = pl_allsd[i];
+ if(sd->status.char_id == a) {
+
+ if(strcmp(str, "Random") == 0)
+ pc_randomwarp(sd, 3);
+
+ else if(strcmp(str, "SavePoint") == 0)
+ pc_setpos(sd, sd->status.save_point.map,
+ sd->status.save_point.x, sd->status.save_point.y, 3);
+
+ else
+ pc_setpos(sd, mapindex_name2id(str), x, y, 3);
+ }
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * Warpparty - [Fredzilla]
+ * Syntax: warpparty "mapname.gat",x,y,Party_ID;
+ *------------------------------------------
+ */
+int buildin_warpparty(struct script_state *st)
+{
+ int x,y;
+ char *str;
+ int p;
+ int i;
+ unsigned short mapindex;
+ struct map_session_data *pl_sd, **pl_allsd;
+ struct map_session_data *sd;
+ int users;
+ str=conv_str(st,& (st->stack->stack_data[st->start+2]));
+ x=conv_num(st,& (st->stack->stack_data[st->start+3]));
+ y=conv_num(st,& (st->stack->stack_data[st->start+4]));
+ p=conv_num(st,& (st->stack->stack_data[st->start+5]));
+ sd=script_rid2sd(st);
+ if(map[sd->bl.m].flag.noreturn || map[sd->bl.m].flag.nowarpto)
+ return 0;
+
+ if(p < 1)
+ return 0;
+
+ pl_allsd = map_getallusers(&users);
+
+ if(strcmp(str,"Random")==0)
+ {
+
+ for (i = 0; i < users; i++)
+ {
+ if ((pl_sd = pl_allsd[i]) && pl_sd->status.party_id == p)
+ {
+ if(map[pl_sd->bl.m].flag.nowarp)
+ continue;
+ pc_randomwarp(pl_sd,3);
+ }
+ }
+ }
+ else if(strcmp(str,"SavePointAll")==0)
+ {
+ if(map[sd->bl.m].flag.noreturn)
+ return 0;
+
+ for (i = 0; i < users; i++)
+ {
+ if ((pl_sd = pl_allsd[i]) && pl_sd->status.party_id == p)
+ {
+ if(map[pl_sd->bl.m].flag.noreturn)
+ continue;
+ pc_setpos(pl_sd,pl_sd->status.save_point.map,pl_sd->status.save_point.x,pl_sd->status.save_point.y,3);
+ }
+ }
+ }
+ else if(strcmp(str,"SavePoint")==0)
+ {
+ if(map[sd->bl.m].flag.noreturn)
+ return 0;
+
+ mapindex=sd->status.save_point.map;
+ x=sd->status.save_point.x;
+ y=sd->status.save_point.y;
+ for (i = 0; i < users; i++)
+ {
+ if ((pl_sd = pl_allsd[i]) && pl_sd->status.party_id == p)
+ {
+ if(map[pl_sd->bl.m].flag.noreturn)
+ continue;
+ pc_setpos(pl_sd,mapindex,x,y,3);
+ }
+ }
+ }
+ else
+ {
+ mapindex = mapindex_name2id(str);
+ for (i = 0; i < users; i++)
+ {
+ if ((pl_sd = pl_allsd[i]) && pl_sd->status.party_id == p)
+ {
+ if(map[pl_sd->bl.m].flag.noreturn || map[pl_sd->bl.m].flag.nowarp)
+ continue;
+ pc_setpos(pl_sd,mapindex,x,y,3);
+ }
+ }
+ }
+ return 0;
+}
+/*==========================================
+ * Warpguild - [Fredzilla]
+ * Syntax: warpguild "mapname.gat",x,y,Guild_ID;
+ *------------------------------------------
+ */
+int buildin_warpguild(struct script_state *st)
+{
+ int x,y;
+ unsigned short mapindex;
+ char *str;
+ int g;
+ int i;
+ struct map_session_data *pl_sd, **pl_allsd;
+ int users;
+ struct map_session_data *sd;
+ str=conv_str(st,& (st->stack->stack_data[st->start+2]));
+ x=conv_num(st,& (st->stack->stack_data[st->start+3]));
+ y=conv_num(st,& (st->stack->stack_data[st->start+4]));
+ g=conv_num(st,& (st->stack->stack_data[st->start+5]));
+ sd=script_rid2sd(st);
+
+ if(map[sd->bl.m].flag.noreturn || map[sd->bl.m].flag.nowarpto)
+ return 0;
+
+ if(g < 1)
+ return 0;
+
+ pl_allsd = map_getallusers(&users);
+
+ if(strcmp(str,"Random")==0)
+ {
+
+ for (i = 0; i < users; i++)
+ {
+ if ((pl_sd = pl_allsd[i]) && pl_sd->status.guild_id == g)
+ {
+ if(map[pl_sd->bl.m].flag.nowarp)
+ continue;
+ pc_randomwarp(pl_sd,3);
+ }
+ }
+ }
+ else if(strcmp(str,"SavePointAll")==0)
+ {
+ if(map[sd->bl.m].flag.noreturn)
+ return 0;
+
+ for (i = 0; i < users; i++)
+ {
+ if ((pl_sd = pl_allsd[i]) && pl_sd->status.guild_id == g)
+ {
+ if(map[pl_sd->bl.m].flag.noreturn)
+ continue;
+ pc_setpos(pl_sd,pl_sd->status.save_point.map,pl_sd->status.save_point.x,pl_sd->status.save_point.y,3);
+ }
+ }
+ }
+ else if(strcmp(str,"SavePoint")==0)
+ {
+ if(map[sd->bl.m].flag.noreturn)
+ return 0;
+
+ mapindex=sd->status.save_point.map;
+ x=sd->status.save_point.x;
+ y=sd->status.save_point.y;
+ for (i = 0; i < users; i++)
+ {
+ if ((pl_sd = pl_allsd[i]) && pl_sd->status.guild_id == g)
+ {
+ if(map[pl_sd->bl.m].flag.noreturn)
+ continue;
+ pc_setpos(pl_sd,mapindex,x,y,3);
+ }
+ }
+ }
+ else
+ {
+ mapindex = mapindex_name2id(str);
+ for (i = 0; i < users; i++)
+ {
+ if ((pl_sd = pl_allsd[i]) && pl_sd->status.guild_id == g)
+ {
+ if(map[pl_sd->bl.m].flag.noreturn || map[pl_sd->bl.m].flag.nowarp)
+ continue;
+ pc_setpos(pl_sd,mapindex,x,y,3);
+ }
+ }
+ }
+ return 0;
+}
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int buildin_heal(struct script_state *st)
+{
+ int hp,sp;
+
+ hp=conv_num(st,& (st->stack->stack_data[st->start+2]));
+ sp=conv_num(st,& (st->stack->stack_data[st->start+3]));
+ pc_heal(script_rid2sd(st),hp,sp);
+ return 0;
+}
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int buildin_itemheal(struct script_state *st)
+{
+ int hp,sp;
+
+ hp=conv_num(st,& (st->stack->stack_data[st->start+2]));
+ sp=conv_num(st,& (st->stack->stack_data[st->start+3]));
+
+ if(potion_flag==1) {
+ potion_hp = hp;
+ potion_sp = sp;
+ return 0;
+ }
+
+ pc_itemheal(script_rid2sd(st),hp,sp);
+ return 0;
+}
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int buildin_percentheal(struct script_state *st)
+{
+ int hp,sp;
+
+ hp=conv_num(st,& (st->stack->stack_data[st->start+2]));
+ sp=conv_num(st,& (st->stack->stack_data[st->start+3]));
+
+ if(potion_flag==1) {
+ potion_per_hp = hp;
+ potion_per_sp = sp;
+ return 0;
+ }
+
+ pc_percentheal(script_rid2sd(st),hp,sp);
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int buildin_jobchange(struct script_state *st)
+{
+ int job, upper=-1;
+
+ job=conv_num(st,& (st->stack->stack_data[st->start+2]));
+ if( st->end>st->start+3 )
+ upper=conv_num(st,& (st->stack->stack_data[st->start+3]));
+
+ if ((job >= 0 && job < MAX_PC_CLASS))
+ pc_jobchange(script_rid2sd(st),job, upper);
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int buildin_input(struct script_state *st)
+{
+ struct map_session_data *sd=NULL;
+ int num=(st->end>st->start+2)?st->stack->stack_data[st->start+2].u.num:0;
+ char *name=(char *) ((st->end>st->start+2)?str_buf+str_data[num&0x00ffffff].str:"");
+// char prefix=*name;
+ char postfix=name[strlen(name)-1];
+
+ sd=script_rid2sd(st);
+ if(sd->state.menu_or_input){
+ sd->state.menu_or_input=0;
+ if( postfix=='$' ){
+ // 文字列
+ if(st->end>st->start+2){ // 引数1個
+ set_reg(sd,num,name,(void*)sd->npc_str);
+ }else{
+ ShowError("buildin_input: string discarded !!\n");
+ return 1;
+ }
+ return 0;
+ }
+ // commented by Lupus (check Value Number Input fix in clif.c)
+ // readded by Yor: set ammount to 0 instead of cancel trade.
+ // ** Fix by fritz :X keeps people from abusing old input bugs
+ if (sd->npc_amount < 0) { //** If input amount is less then 0
+// clif_tradecancelled(sd); // added "Deal has been cancelled" message by Valaris
+// buildin_close(st); // ** close
+ sd->npc_amount = 0;
+ } else if ((unsigned int)sd->npc_amount > battle_config.vending_max_value) // new fix by Yor
+ sd->npc_amount = battle_config.vending_max_value;
+
+ // 数値
+ if(st->end>st->start+2){ // 引数1個
+ set_reg(sd,num,name,(void*)sd->npc_amount);
+ } else {
+ // ragemu互換のため
+ pc_setreg(sd,add_str((unsigned char *) "l14"),sd->npc_amount);
+ }
+ return 0;
+ }
+ //state.menu_or_input = 0
+ st->state=RERUNLINE;
+ if(postfix=='$')
+ clif_scriptinputstr(sd,st->oid);
+ else
+ clif_scriptinput(sd,st->oid);
+ sd->state.menu_or_input=1;
+ return 0;
+}
+
+/*==========================================
+ * 変数設定
+ *------------------------------------------
+ */
+int buildin_set(struct script_state *st)
+{
+ struct map_session_data *sd=NULL;
+ int num=st->stack->stack_data[st->start+2].u.num;
+ char *name=str_buf+str_data[num&0x00ffffff].str;
+ char prefix=*name;
+ char postfix=name[strlen(name)-1];
+
+ if( st->stack->stack_data[st->start+2].type!=C_NAME ){
+ ShowError("script: buildin_set: not name\n");
+ return 1;
+ }
+
+ if( prefix!='$' )
+ sd=script_rid2sd(st);
+
+
+ if( postfix=='$' ){
+ // 文字列
+ char *str = conv_str(st,& (st->stack->stack_data[st->start+3]));
+ set_reg(sd,num,name,(void*)str);
+ }else{
+ // 数値
+ int val = conv_num(st,& (st->stack->stack_data[st->start+3]));
+ set_reg(sd,num,name,(void*)val);
+ }
+
+ return 0;
+}
+/*==========================================
+ * 配列変数設定
+ *------------------------------------------
+ */
+int buildin_setarray(struct script_state *st)
+{
+ struct map_session_data *sd=NULL;
+ int num=st->stack->stack_data[st->start+2].u.num;
+ char *name=str_buf+str_data[num&0x00ffffff].str;
+ char prefix=*name;
+ char postfix=name[strlen(name)-1];
+ int i,j;
+
+ if( prefix!='$' && prefix!='@' ){
+ ShowWarning("buildin_setarray: illegal scope !\n");
+ return 1;
+ }
+ if( prefix!='$' )
+ sd=script_rid2sd(st);
+
+ for(j=0,i=st->start+3; i<st->end && j<128;i++,j++){
+ void *v;
+ if( postfix=='$' )
+ v=(void*)conv_str(st,& (st->stack->stack_data[i]));
+ else
+ v=(void*)conv_num(st,& (st->stack->stack_data[i]));
+ set_reg( sd, num+(j<<24), name, v);
+ }
+ return 0;
+}
+/*==========================================
+ * 配列変数クリア
+ *------------------------------------------
+ */
+int buildin_cleararray(struct script_state *st)
+{
+ struct map_session_data *sd=NULL;
+ int num=st->stack->stack_data[st->start+2].u.num;
+ char *name=str_buf+str_data[num&0x00ffffff].str;
+ char prefix=*name;
+ char postfix=name[strlen(name)-1];
+ int sz=conv_num(st,& (st->stack->stack_data[st->start+4]));
+ int i;
+ void *v;
+
+ if( prefix!='$' && prefix!='@' ){
+ ShowWarning("buildin_cleararray: illegal scope !\n");
+ return 1;
+ }
+ if( prefix!='$' )
+ sd=script_rid2sd(st);
+
+ if( postfix=='$' )
+ v=(void*)conv_str(st,& (st->stack->stack_data[st->start+3]));
+ else
+ v=(void*)conv_num(st,& (st->stack->stack_data[st->start+3]));
+
+ for(i=0;i<sz;i++)
+ set_reg(sd,num+(i<<24),name,v);
+ return 0;
+}
+/*==========================================
+ * 配列変数コピー
+ *------------------------------------------
+ */
+int buildin_copyarray(struct script_state *st)
+{
+ struct map_session_data *sd=NULL;
+ int num=st->stack->stack_data[st->start+2].u.num;
+ char *name=str_buf+str_data[num&0x00ffffff].str;
+ char prefix=*name;
+ char postfix=name[strlen(name)-1];
+ int num2=st->stack->stack_data[st->start+3].u.num;
+ char *name2=str_buf+str_data[num2&0x00ffffff].str;
+ char prefix2=*name2;
+ char postfix2=name2[strlen(name2)-1];
+ int sz=conv_num(st,& (st->stack->stack_data[st->start+4]));
+ int i;
+
+ if( prefix!='$' && prefix!='@' && prefix2!='$' && prefix2!='@' ){
+ ShowWarning("buildin_copyarray: illegal scope !\n");
+ return 1;
+ }
+ if( (postfix=='$' || postfix2=='$') && postfix!=postfix2 ){
+ ShowError("buildin_copyarray: type mismatch !\n");
+ return 1;
+ }
+ if( prefix!='$' || prefix2!='$' )
+ sd=script_rid2sd(st);
+
+ // if two array is the same and (num > num2), bottom-up copy is required [Eoe / jA 1116]
+ if((num & 0x00FFFFFF) == (num2 & 0x00FFFFFF) && (num & 0xFF000000) > (num2 & 0xFF000000)) {
+ for(i=sz-1;i>=0;i--)
+ set_reg(sd,num+(i<<24),name, get_val2(st,num2+(i<<24)) );
+ } else {
+ for(i=0;i<sz;i++)
+ set_reg(sd,num+(i<<24),name, get_val2(st,num2+(i<<24)) );
+ }
+
+ return 0;
+}
+/*==========================================
+ * 配列変数のサイズ所得
+ *------------------------------------------
+ */
+static int getarraysize(struct script_state *st,int num,int postfix)
+{
+ int i=(num>>24),c=-1; // Moded to -1 because even if the first element is 0, it will still report as 1 [Lance]
+ for(;i<128;i++){
+ // num must be the first elements of array [Eoe / jA 1127]
+ void *v=get_val2(st,(num & 0x00FFFFFF)+(i<<24));
+ if(postfix=='$' && *((char*)v) ) c=i;
+ if(postfix!='$' && (int)v )c=i;
+ }
+ return c+1;
+}
+int buildin_getarraysize(struct script_state *st)
+{
+ int num=st->stack->stack_data[st->start+2].u.num;
+ char *name=str_buf+str_data[num&0x00ffffff].str;
+ char prefix=*name;
+ char postfix=name[strlen(name)-1];
+
+ if( prefix!='$' && prefix!='@' ){
+ ShowWarning("buildin_copyarray: illegal scope !\n");
+ return 1;
+ }
+
+ push_val(st->stack,C_INT,getarraysize(st,num,postfix) );
+ return 0;
+}
+/*==========================================
+ * 配列変数から要素削除
+ *------------------------------------------
+ */
+int buildin_deletearray(struct script_state *st)
+{
+ struct map_session_data *sd=NULL;
+ int num=st->stack->stack_data[st->start+2].u.num;
+ char *name=str_buf+str_data[num&0x00ffffff].str;
+ char prefix=*name;
+ char postfix=name[strlen(name)-1];
+ int count=1;
+ int i,sz=getarraysize(st,num,postfix)-(num>>24)-count+1;
+
+
+ if( (st->end > st->start+3) )
+ count=conv_num(st,& (st->stack->stack_data[st->start+3]));
+
+ if( prefix!='$' && prefix!='@' ){
+ ShowWarning("buildin_deletearray: illegal scope !\n");
+ return 1;
+ }
+ if( prefix!='$' )
+ sd=script_rid2sd(st);
+
+ for(i=0;i<sz;i++){
+ set_reg(sd,num+(i<<24),name, get_val2(st,num+((i+count)<<24) ) );
+ }
+ for(;i<(128-(num>>24));i++){
+ if( postfix!='$' ) set_reg(sd,num+(i<<24),name, 0);
+ if( postfix=='$' ) set_reg(sd,num+(i<<24),name, (void *) "");
+ }
+ return 0;
+}
+
+/*==========================================
+ * 指定要素を表す値(キー)を所得する
+ *------------------------------------------
+ */
+int buildin_getelementofarray(struct script_state *st)
+{
+ if( st->stack->stack_data[st->start+2].type==C_NAME ){
+ int i=conv_num(st,& (st->stack->stack_data[st->start+3]));
+ if(i>127 || i<0){
+ ShowWarning("script: getelementofarray (operator[]): param2 illegal number %d\n",i);
+ push_val(st->stack,C_INT,0);
+ return 1;
+ }else{
+ push_val(st->stack,C_NAME,
+ (i<<24) | st->stack->stack_data[st->start+2].u.num );
+ }
+ }else{
+ ShowError("script: getelementofarray (operator[]): param1 not name !\n");
+ push_val(st->stack,C_INT,0);
+ }
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int buildin_setlook(struct script_state *st)
+{
+ int type,val;
+
+ type=conv_num(st,& (st->stack->stack_data[st->start+2]));
+ val=conv_num(st,& (st->stack->stack_data[st->start+3]));
+
+ pc_changelook(script_rid2sd(st),type,val);
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int buildin_cutin(struct script_state *st)
+{
+ int type;
+
+ conv_str(st,& (st->stack->stack_data[st->start+2]));
+ type=conv_num(st,& (st->stack->stack_data[st->start+3]));
+
+ clif_cutin(script_rid2sd(st),st->stack->stack_data[st->start+2].u.str,type);
+
+ return 0;
+}
+/*==========================================
+ * カードのイラストを表示する
+ *------------------------------------------
+ */
+int buildin_cutincard(struct script_state *st)
+{
+ int itemid;
+ struct item_data *i_data;
+
+ itemid=conv_num(st,& (st->stack->stack_data[st->start+2]));
+
+ i_data = itemdb_exists(itemid);
+ if (i_data)
+ clif_cutin(script_rid2sd(st),i_data->cardillustname,4);
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int buildin_viewpoint(struct script_state *st)
+{
+ int type,x,y,id,color;
+
+ type=conv_num(st,& (st->stack->stack_data[st->start+2]));
+ x=conv_num(st,& (st->stack->stack_data[st->start+3]));
+ y=conv_num(st,& (st->stack->stack_data[st->start+4]));
+ id=conv_num(st,& (st->stack->stack_data[st->start+5]));
+ color=conv_num(st,& (st->stack->stack_data[st->start+6]));
+
+ clif_viewpoint(script_rid2sd(st),st->oid,type,x,y,id,color);
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int buildin_countitem(struct script_state *st)
+{
+ int nameid=0,count=0,i;
+ struct map_session_data *sd;
+
+ struct script_data *data;
+
+ sd = script_rid2sd(st);
+
+ data=&(st->stack->stack_data[st->start+2]);
+ get_val(st,data);
+ if( data->type==C_STR || data->type==C_CONSTSTR ){
+ const char *name=conv_str(st,data);
+ struct item_data *item_data;
+ if( (item_data = itemdb_searchname(name)) != NULL)
+ nameid=item_data->nameid;
+ }else
+ nameid=conv_num(st,data);
+
+ if (nameid>=500) //if no such ID then skip this iteration
+ for(i=0;i<MAX_INVENTORY;i++){
+ if(sd->status.inventory[i].nameid==nameid)
+ count+=sd->status.inventory[i].amount;
+ }
+ else{
+ if(battle_config.error_log)
+ ShowError("wrong item ID : countitem(%i)\n",nameid);
+ push_val(st->stack,C_INT,0);
+ return 1;
+ }
+ push_val(st->stack,C_INT,count);
+ return 0;
+}
+
+/*==========================================
+ * countitem2(nameID,Identified,Refine,Attribute,Card0,Card1,Card2,Card3) [Lupus]
+ * returns number of items that met the conditions
+ *------------------------------------------
+ */
+int buildin_countitem2(struct script_state *st)
+{
+ int nameid=0,count=0,i;
+ int iden,ref,attr,c1,c2,c3,c4;
+ struct map_session_data *sd;
+
+ struct script_data *data;
+
+ sd = script_rid2sd(st);
+
+ data=&(st->stack->stack_data[st->start+2]);
+ get_val(st,data);
+ if( data->type==C_STR || data->type==C_CONSTSTR ){
+ const char *name=conv_str(st,data);
+ struct item_data *item_data;
+ if( (item_data = itemdb_searchname(name)) != NULL)
+ nameid=item_data->nameid;
+ }else
+ nameid=conv_num(st,data);
+
+ iden=conv_num(st,& (st->stack->stack_data[st->start+3]));
+ ref=conv_num(st,& (st->stack->stack_data[st->start+4]));
+ attr=conv_num(st,& (st->stack->stack_data[st->start+5]));
+ c1=conv_num(st,& (st->stack->stack_data[st->start+6]));
+ c2=conv_num(st,& (st->stack->stack_data[st->start+7]));
+ c3=conv_num(st,& (st->stack->stack_data[st->start+8]));
+ c4=conv_num(st,& (st->stack->stack_data[st->start+9]));
+
+ if (nameid>=500) //if no such ID then skip this iteration
+ for(i=0;i<MAX_INVENTORY;i++){
+ if(sd->status.inventory[i].nameid<=0 || sd->inventory_data[i] == NULL ||
+ sd->status.inventory[i].amount<=0 || sd->status.inventory[i].nameid!=nameid ||
+ sd->status.inventory[i].identify!=iden || sd->status.inventory[i].refine!=ref ||
+ sd->status.inventory[i].attribute!=attr || sd->status.inventory[i].card[0]!=c1 ||
+ sd->status.inventory[i].card[1]!=c2 || sd->status.inventory[i].card[2]!=c3 ||
+ sd->status.inventory[i].card[3]!=c4)
+ continue;
+
+ count+=sd->status.inventory[i].amount;
+ }
+ else{
+ if(battle_config.error_log)
+ ShowError("wrong item ID : countitem2(%i)\n",nameid);
+ push_val(st->stack,C_INT,0);
+ return 1;
+ }
+ push_val(st->stack,C_INT,count);
+
+ return 0;
+}
+
+/*==========================================
+ * 重量チェック
+ *------------------------------------------
+ */
+int buildin_checkweight(struct script_state *st)
+{
+ int nameid=0,amount,i;
+ unsigned long weight;
+ struct map_session_data *sd;
+ struct script_data *data;
+
+ sd = script_rid2sd(st);
+
+ data=&(st->stack->stack_data[st->start+2]);
+ get_val(st,data);
+ if( data->type==C_STR || data->type==C_CONSTSTR ){
+ const char *name=conv_str(st,data);
+ struct item_data *item_data = itemdb_searchname(name);
+ if( item_data )
+ nameid=item_data->nameid;
+ }else
+ nameid=conv_num(st,data);
+
+ amount=conv_num(st,& (st->stack->stack_data[st->start+3]));
+ if ( amount<=0 || nameid<500 ) { //if get wrong item ID or amount<=0, don't count weight of non existing items
+ push_val(st->stack,C_INT,0);
+ ShowError("buildin_checkweight: Wrong item ID or amount.\n");
+ return 1;
+ }
+
+ weight = itemdb_weight(nameid)*amount;
+ if(amount > MAX_AMOUNT || weight + sd->weight > sd->max_weight){
+ push_val(st->stack,C_INT,0);
+ } else {
+ //Check if the inventory ain't full.
+ //TODO: Currently does not checks if you can just stack it on top of another item you already have....
+
+ i = pc_search_inventory(sd,0);
+ if (i >= 0) //Empty slot available.
+ push_val(st->stack,C_INT,1);
+ else //Inventory full
+ push_val(st->stack,C_INT,0);
+
+ }
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int buildin_getitem(struct script_state *st)
+{
+ int nameid,nameidsrc,amount,flag = 0;
+ struct item item_tmp;
+ struct map_session_data *sd;
+ struct script_data *data;
+
+ sd = script_rid2sd(st);
+
+ data=&(st->stack->stack_data[st->start+2]);
+ get_val(st,data);
+ if( data->type==C_STR || data->type==C_CONSTSTR ){
+ const char *name=conv_str(st,data);
+ struct item_data *item_data = itemdb_searchname(name);
+ nameid=512; //Apple item ID
+ if( item_data != NULL)
+ nameid=item_data->nameid;
+ }else
+ nameid=conv_num(st,data);
+
+ if ( ( amount=conv_num(st,& (st->stack->stack_data[st->start+3])) ) <= 0) {
+ return 0; //return if amount <=0, skip the useles iteration
+ }
+ //Violet Box, Blue Box, etc - random item pick
+ if((nameidsrc = nameid)<0) { // Save real ID of the source Box [Lupus]
+ nameid=itemdb_searchrandomid(-nameid);
+
+ if(log_config.present > 0)
+ log_present(sd, -nameidsrc, nameid); //fixed missing ID by Lupus
+
+ flag = 1;
+ }
+
+ if(nameid > 0) {
+ memset(&item_tmp,0,sizeof(item_tmp));
+ item_tmp.nameid=nameid;
+ if(!flag)
+ item_tmp.identify=1;
+ else
+ item_tmp.identify=!itemdb_isequip3(nameid);
+ if( st->end>st->start+5 ) //アイテムを指定したIDに渡す
+ sd=map_id2sd(conv_num(st,& (st->stack->stack_data[st->start+5])));
+ if(sd == NULL) //アイテムを渡す相手がいなかったらお帰り
+ return 0;
+ if((flag = pc_additem(sd,&item_tmp,amount))) {
+ clif_additem(sd,0,0,flag);
+ if(pc_candrop(sd,nameid))
+ map_addflooritem(&item_tmp,amount,sd->bl.m,sd->bl.x,sd->bl.y,NULL,NULL,NULL,0);
+ }
+
+ //Logs items, got from (N)PC scripts [Lupus]
+ if(log_config.pick > 0 ) {
+ log_pick(sd, "N", 0, nameid, amount, NULL);
+ }
+ //Logs
+
+ }
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int buildin_getitem2(struct script_state *st)
+{
+ int nameid,amount,flag = 0;
+ int iden,ref,attr,c1,c2,c3,c4;
+ struct item_data *item_data;
+ struct item item_tmp;
+ struct map_session_data *sd;
+ struct script_data *data;
+
+ sd = script_rid2sd(st);
+
+ data=&(st->stack->stack_data[st->start+2]);
+ get_val(st,data);
+ if( data->type==C_STR || data->type==C_CONSTSTR ){
+ const char *name=conv_str(st,data);
+ struct item_data *item_data = itemdb_searchname(name);
+ nameid=512; //Apple item ID
+ if( item_data )
+ nameid=item_data->nameid;
+ }else
+ nameid=conv_num(st,data);
+
+ amount=conv_num(st,& (st->stack->stack_data[st->start+3]));
+ iden=conv_num(st,& (st->stack->stack_data[st->start+4]));
+ ref=conv_num(st,& (st->stack->stack_data[st->start+5]));
+ attr=conv_num(st,& (st->stack->stack_data[st->start+6]));
+ c1=conv_num(st,& (st->stack->stack_data[st->start+7]));
+ c2=conv_num(st,& (st->stack->stack_data[st->start+8]));
+ c3=conv_num(st,& (st->stack->stack_data[st->start+9]));
+ c4=conv_num(st,& (st->stack->stack_data[st->start+10]));
+ if( st->end>st->start+11 ) //アイテムを指定したIDに渡す
+ sd=map_id2sd(conv_num(st,& (st->stack->stack_data[st->start+11])));
+ if(sd == NULL) //アイテムを渡す相手がいなかったらお帰り
+ return 0;
+
+ if(nameid<0) { // ランダム
+ nameid=itemdb_searchrandomid(-nameid);
+ flag = 1;
+ }
+
+ if(nameid > 0) {
+ memset(&item_tmp,0,sizeof(item_tmp));
+ item_data=itemdb_exists(nameid);
+ if (item_data == NULL)
+ return -1;
+ if(item_data->type==4 || item_data->type==5){
+ if(ref > 10) ref = 10;
+ }
+ else if(item_data->type==7) {
+ iden = 1;
+ ref = 0;
+ }
+ else {
+ iden = 1;
+ ref = attr = 0;
+ }
+
+ item_tmp.nameid=nameid;
+ if(!flag)
+ item_tmp.identify=iden;
+ else if(item_data->type==4 || item_data->type==5)
+ item_tmp.identify=0;
+ item_tmp.refine=ref;
+ item_tmp.attribute=attr;
+ item_tmp.card[0]=c1;
+ item_tmp.card[1]=c2;
+ item_tmp.card[2]=c3;
+ item_tmp.card[3]=c4;
+ if((flag = pc_additem(sd,&item_tmp,amount))) {
+ clif_additem(sd,0,0,flag);
+ map_addflooritem(&item_tmp,amount,sd->bl.m,sd->bl.x,sd->bl.y,NULL,NULL,NULL,0);
+ }
+
+ //Logs items, got from (N)PC scripts [Lupus]
+ if(log_config.pick > 0 ) {
+ log_pick(sd, "N", 0, nameid, amount, &item_tmp);
+ }
+ //Logs
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * gets an item with someone's name inscribed [Skotlex]
+ * getinscribeditem item_num, character_name
+ * Returned Qty is always 1, only works on equip-able
+ * equipment
+ *------------------------------------------
+ */
+int buildin_getnameditem(struct script_state *st)
+{
+ int nameid;
+ struct item item_tmp;
+ struct map_session_data *sd, *tsd;
+ struct script_data *data;
+
+ sd = script_rid2sd(st);
+ if (sd == NULL)
+ { //Player not attached!
+ push_val(st->stack,C_INT,0);
+ return 0;
+ }
+
+ data=&(st->stack->stack_data[st->start+2]);
+ get_val(st,data);
+ if( data->type==C_STR || data->type==C_CONSTSTR ){
+ const char *name=conv_str(st,data);
+ struct item_data *item_data = itemdb_searchname(name);
+ if( item_data == NULL)
+ { //Failed
+ push_val(st->stack,C_INT,0);
+ return 0;
+ }
+ nameid = item_data->nameid;
+ }else
+ nameid = conv_num(st,data);
+
+ if(!itemdb_exists(nameid) || !itemdb_isequip3(nameid))
+ { //We don't allow non-equipable/stackable items to be named
+ //to avoid any qty exploits that could happen because of it.
+ push_val(st->stack,C_INT,0);
+ return 0;
+ }
+
+ data=&(st->stack->stack_data[st->start+3]);
+ get_val(st,data);
+ if( data->type==C_STR || data->type==C_CONSTSTR ) //Char Name
+ tsd=map_nick2sd(conv_str(st,data));
+ else //Char Id was given
+ tsd=map_charid2sd(conv_num(st,data));
+
+ if( tsd == NULL )
+ { //Failed
+ push_val(st->stack,C_INT,0);
+ return 0;
+ }
+
+ memset(&item_tmp,0,sizeof(item_tmp));
+ item_tmp.nameid=nameid;
+ item_tmp.amount=1;
+ item_tmp.identify=1;
+ item_tmp.card[0]=254; //we don't use 255! because for example SIGNED WEAPON shouldn't get TOP10 BS Fame bonus [Lupus]
+ item_tmp.card[2]=tsd->status.char_id;
+ item_tmp.card[3]=tsd->status.char_id >> 16;
+ if(pc_additem(sd,&item_tmp,1)) {
+ push_val(st->stack,C_INT,0);
+ return 0; //Failed to add item, we will not drop if they don't fit
+ }
+
+ //Logs items, got from (N)PC scripts [Lupus]
+ if(log_config.pick > 0 ) {
+ log_pick(sd, "N", 0, item_tmp.nameid, item_tmp.amount, &item_tmp);
+ }
+ //Logs
+
+ push_val(st->stack,C_INT,1);
+ return 0;
+}
+
+/*==========================================
+ * gets a random item ID from an item group [Skotlex]
+ * groupranditem group_num
+ *------------------------------------------
+ */
+int buildin_grouprandomitem(struct script_state *st)
+{
+ int group;
+
+ group = conv_num(st,& (st->stack->stack_data[st->start+2]));
+ push_val(st->stack, C_INT, itemdb_searchrandomgroup(group));
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int buildin_makeitem(struct script_state *st)
+{
+ int nameid,amount,flag = 0;
+ int x,y,m;
+ char *mapname;
+ struct item item_tmp;
+ struct script_data *data;
+
+ data=&(st->stack->stack_data[st->start+2]);
+ get_val(st,data);
+ if( data->type==C_STR || data->type==C_CONSTSTR ){
+ const char *name=conv_str(st,data);
+ struct item_data *item_data = itemdb_searchname(name);
+ nameid=512; //Apple Item ID
+ if( item_data )
+ nameid=item_data->nameid;
+ }else
+ nameid=conv_num(st,data);
+
+ amount=conv_num(st,& (st->stack->stack_data[st->start+3]));
+ mapname =conv_str(st,& (st->stack->stack_data[st->start+4]));
+ x =conv_num(st,& (st->stack->stack_data[st->start+5]));
+ y =conv_num(st,& (st->stack->stack_data[st->start+6]));
+
+ if(strcmp(mapname,"this")==0)
+ {
+ struct map_session_data *sd;
+ sd = script_rid2sd(st);
+ if (!sd) return 0; //Failed...
+ m=sd->bl.m;
+ } else
+ m=map_mapname2mapid(mapname);
+
+ if(nameid<0) { // ランダム
+ nameid=itemdb_searchrandomid(-nameid);
+ flag = 1;
+ }
+
+ if(nameid > 0) {
+ memset(&item_tmp,0,sizeof(item_tmp));
+ item_tmp.nameid=nameid;
+ if(!flag)
+ item_tmp.identify=1;
+ else
+ item_tmp.identify=!itemdb_isequip3(nameid);
+
+ map_addflooritem(&item_tmp,amount,m,x,y,NULL,NULL,NULL,0);
+ }
+
+ return 0;
+}
+/*==========================================
+ * script DELITEM command (fixed 2 bugs by Lupus, added deletion priority by Lupus)
+ *------------------------------------------
+ */
+int buildin_delitem(struct script_state *st)
+{
+ int nameid=0,amount,i,important_item=0;
+ struct map_session_data *sd;
+ struct script_data *data;
+
+ sd = script_rid2sd(st);
+
+ data=&(st->stack->stack_data[st->start+2]);
+ get_val(st,data);
+ if( data->type==C_STR || data->type==C_CONSTSTR ){
+ const char *name=conv_str(st,data);
+ struct item_data *item_data = itemdb_searchname(name);
+ //nameid=512;
+ if( item_data )
+ nameid=item_data->nameid;
+ }else
+ nameid=conv_num(st,data);
+
+ amount=conv_num(st,& (st->stack->stack_data[st->start+3]));
+
+ if (nameid<500 || amount<=0 ) {//by Lupus. Don't run FOR if u got wrong item ID or amount<=0
+ //eprintf("wrong item ID or amount<=0 : delitem %i,\n",nameid,amount);
+ return 0;
+ }
+ //1st pass
+ //here we won't delete items with CARDS, named items but we count them
+ for(i=0;i<MAX_INVENTORY;i++){
+ //we don't delete wrong item or equipped item
+ if(sd->status.inventory[i].nameid<=0 || sd->inventory_data[i] == NULL ||
+ sd->status.inventory[i].amount<=0 || sd->status.inventory[i].nameid!=nameid )
+ continue;
+ //1 egg uses 1 cell in the inventory. so it's ok to delete 1 pet / per cycle
+ if(sd->inventory_data[i]->type==7 && sd->status.inventory[i].card[0] == (short)0xff00 && search_petDB_index(nameid, PET_EGG) >= 0 ){
+ intif_delete_petdata( MakeDWord(sd->status.inventory[i].card[1], sd->status.inventory[i].card[2]) );
+ //clear egg flag. so it won't be put in IMPORTANT items (eggs look like item with 2 cards ^_^)
+ sd->status.inventory[i].card[1] = sd->status.inventory[i].card[0] = 0;
+ //now this egg'll be deleted as a common unimportant item
+ }
+ //is this item important? does it have cards? or Player's name? or Refined/Upgraded
+ if( sd->status.inventory[i].card[0] || sd->status.inventory[i].card[1] ||
+ sd->status.inventory[i].card[2] || sd->status.inventory[i].card[3] || sd->status.inventory[i].refine) {
+ //this is important item, count it
+ important_item++;
+ continue;
+ }
+
+ if(sd->status.inventory[i].amount>=amount){
+
+ //Logs items, got from (N)PC scripts [Lupus]
+ if(log_config.pick > 0 ) {
+ log_pick(sd, "N", 0, sd->status.inventory[i].nameid, -amount, &sd->status.inventory[i]);
+ }
+ //Logs
+
+ pc_delitem(sd,i,amount,0);
+ return 0; //we deleted exact amount of items. now exit
+ } else {
+ amount-=sd->status.inventory[i].amount;
+
+ //Logs items, got from (N)PC scripts [Lupus]
+ if(log_config.pick > 0 ) {
+ log_pick(sd, "N", 0, sd->status.inventory[i].nameid, -sd->status.inventory[i].amount, &sd->status.inventory[i]);
+ }
+ //Logs
+
+ pc_delitem(sd,i,sd->status.inventory[i].amount,0);
+ }
+ }
+ //2nd pass
+ //now if there WERE items with CARDs/REFINED/NAMED... and if we still have to delete some items. we'll delete them finally
+ if (important_item>0 && amount>0)
+ for(i=0;i<MAX_INVENTORY;i++){
+ //we don't delete wrong item
+ if(sd->status.inventory[i].nameid<=0 || sd->inventory_data[i] == NULL ||
+ sd->status.inventory[i].amount<=0 || sd->status.inventory[i].nameid!=nameid )
+ continue;
+
+ if(sd->status.inventory[i].amount>=amount){
+
+ //Logs items, got from (N)PC scripts [Lupus]
+ if(log_config.pick > 0 ) {
+ log_pick(sd, "N", 0, sd->status.inventory[i].nameid, -amount, &sd->status.inventory[i]);
+ }
+ //Logs
+
+ pc_delitem(sd,i,amount,0);
+ return 0; //we deleted exact amount of items. now exit
+ } else {
+ amount-=sd->status.inventory[i].amount;
+
+ //Logs items, got from (N)PC scripts [Lupus]
+ if(log_config.pick > 0 ) {
+ log_pick(sd, "N", 0, sd->status.inventory[i].nameid, -sd->status.inventory[i].amount, &sd->status.inventory[i]);
+ }
+ //Logs
+
+ pc_delitem(sd,i,sd->status.inventory[i].amount,0);
+ }
+ }
+
+ return 0;
+}
+
+/*==========================================
+* advanced version of delitem [modified by Mihilion]
+*------------------------------------------
+*/
+int buildin_delitem2(struct script_state *st)
+{
+ int nameid=0,amount,i=0;
+ int iden,ref,attr,c1,c2,c3,c4;
+ struct map_session_data *sd;
+ struct script_data *data;
+
+ sd = script_rid2sd(st);
+
+ data=&(st->stack->stack_data[st->start+2]);
+ get_val(st,data);
+ if( data->type==C_STR || data->type==C_CONSTSTR ){
+ const char *name=conv_str(st,data);
+ struct item_data *item_data = itemdb_searchname(name);
+ //nameid=512;
+ if( item_data )
+ nameid=item_data->nameid;
+ }else
+ nameid=conv_num(st,data);
+
+ amount=conv_num(st,& (st->stack->stack_data[st->start+3]));
+ iden=conv_num(st,& (st->stack->stack_data[st->start+4]));
+ ref=conv_num(st,& (st->stack->stack_data[st->start+5]));
+ attr=conv_num(st,& (st->stack->stack_data[st->start+6]));
+ c1=conv_num(st,& (st->stack->stack_data[st->start+7]));
+ c2=conv_num(st,& (st->stack->stack_data[st->start+8]));
+ c3=conv_num(st,& (st->stack->stack_data[st->start+9]));
+ c4=conv_num(st,& (st->stack->stack_data[st->start+10]));
+
+ if (nameid<500 || amount<=0 ) {//by Lupus. Don't run FOR if u got wrong item ID or amount<=0
+ //eprintf("wrong item ID or amount<=0 : delitem %i,\n",nameid,amount);
+ return 0;
+ }
+
+ for(i=0;i<MAX_INVENTORY;i++){
+ //we don't delete wrong item or equipped item
+ if(sd->status.inventory[i].nameid<=0 || sd->inventory_data[i] == NULL ||
+ sd->status.inventory[i].amount<=0 || sd->status.inventory[i].nameid!=nameid ||
+ sd->status.inventory[i].identify!=iden || sd->status.inventory[i].refine!=ref ||
+ sd->status.inventory[i].attribute!=attr || sd->status.inventory[i].card[0]!=c1 ||
+ sd->status.inventory[i].card[1]!=c2 || sd->status.inventory[i].card[2]!=c3 ||
+ sd->status.inventory[i].card[3]!=c4)
+ continue;
+ //1 egg uses 1 cell in the inventory. so it's ok to delete 1 pet / per cycle
+ if(sd->inventory_data[i]->type==7 && sd->status.inventory[i].card[0] == (short)0xff00 && search_petDB_index(nameid, PET_EGG) >= 0 ){
+ intif_delete_petdata( MakeDWord(sd->status.inventory[i].card[1], sd->status.inventory[i].card[2]) );
+ //clear egg flag. so it won't be put in IMPORTANT items (eggs look like item with 2 cards ^_^)
+ sd->status.inventory[i].card[1] = sd->status.inventory[i].card[0] = 0;
+ //now this egg'll be deleted as a common unimportant item
+ }
+
+ if(sd->status.inventory[i].amount>=amount){
+
+ //Logs items, got from (N)PC scripts [Lupus]
+ if(log_config.pick > 0 ) {
+ log_pick(sd, "N", 0, sd->status.inventory[i].nameid, -amount, &sd->status.inventory[i]);
+ }
+ //Logs
+
+ pc_delitem(sd,i,amount,0);
+ return 0; //we deleted exact amount of items. now exit
+ } else {
+ amount-=sd->status.inventory[i].amount;
+
+ //Logs items, got from (N)PC scripts [Lupus]
+ if(log_config.pick > 0 ) {
+ log_pick(sd, "N", 0, sd->status.inventory[i].nameid, -sd->status.inventory[i].amount, &sd->status.inventory[i]);
+ }
+ //Logs
+
+ pc_delitem(sd,i,sd->status.inventory[i].amount,0);
+ }
+ }
+ return 0;
+}
+
+/*==========================================
+ * Enables/Disables use of items while in an NPC [Skotlex]
+ *------------------------------------------
+ */
+int buildin_enableitemuse(struct script_state *st) {
+ struct map_session_data *sd;
+ sd=script_rid2sd(st);
+ if (sd)
+ sd->npc_item_flag = st->oid;
+ return 0;
+}
+
+int buildin_disableitemuse(struct script_state *st) {
+ struct map_session_data *sd;
+ sd=script_rid2sd(st);
+ if (sd)
+ sd->npc_item_flag = 0;
+ return 0;
+}
+
+/*==========================================
+ *キャラ関係のパラメータ取得
+ *------------------------------------------
+ */
+int buildin_readparam(struct script_state *st)
+{
+ int type;
+ struct map_session_data *sd;
+
+ type=conv_num(st,& (st->stack->stack_data[st->start+2]));
+ if( st->end>st->start+3 )
+ sd=map_nick2sd(conv_str(st,& (st->stack->stack_data[st->start+3])));
+ else
+ sd=script_rid2sd(st);
+
+ if(sd==NULL){
+ push_val(st->stack,C_INT,-1);
+ return 0;
+ }
+
+ push_val(st->stack,C_INT,pc_readparam(sd,type));
+
+ return 0;
+}
+/*==========================================
+ *キャラ関係のID取得
+ *------------------------------------------
+ */
+int buildin_getcharid(struct script_state *st)
+{
+ int num;
+ struct map_session_data *sd;
+
+ num=conv_num(st,& (st->stack->stack_data[st->start+2]));
+ if( st->end>st->start+3 )
+ sd=map_nick2sd(conv_str(st,& (st->stack->stack_data[st->start+3])));
+ else
+ sd=script_rid2sd(st);
+ if(sd==NULL){
+ push_val(st->stack,C_INT,-1);
+ return 0;
+ }
+ if(num==0)
+ push_val(st->stack,C_INT,sd->status.char_id);
+ if(num==1)
+ push_val(st->stack,C_INT,sd->status.party_id);
+ if(num==2)
+ push_val(st->stack,C_INT,sd->status.guild_id);
+ if(num==3)
+ push_val(st->stack,C_INT,sd->status.account_id);
+ return 0;
+}
+/*==========================================
+ *指定IDのPT名取得
+ *------------------------------------------
+ */
+char *buildin_getpartyname_sub(int party_id)
+{
+ struct party *p;
+
+ p=NULL;
+ p=party_search(party_id);
+
+ if(p!=NULL){
+ char *buf;
+ buf=(char *)aCallocA(NAME_LENGTH,sizeof(char));
+ memcpy(buf, p->name, NAME_LENGTH-1);
+ return buf;
+ }
+
+ return 0;
+}
+int buildin_getpartyname(struct script_state *st)
+{
+ char *name;
+ int party_id;
+
+ party_id=conv_num(st,& (st->stack->stack_data[st->start+2]));
+ name=buildin_getpartyname_sub(party_id);
+ if(name != NULL)
+ push_str(st->stack,C_STR,(unsigned char *)name);
+ else
+ push_str(st->stack,C_CONSTSTR, (unsigned char *) "null");
+
+ return 0;
+}
+/*==========================================
+ *指定IDのPT人数とメンバーID取得
+ *------------------------------------------
+ */
+int buildin_getpartymember(struct script_state *st)
+{
+ struct party *p;
+ int i,j=0;
+
+ p=NULL;
+ p=party_search(conv_num(st,& (st->stack->stack_data[st->start+2])));
+
+ if(p!=NULL){
+ for(i=0;i<MAX_PARTY;i++){
+ if(p->member[i].account_id){
+// printf("name:%s %d\n",p->member[i].name,i);
+ mapreg_setregstr(add_str((unsigned char *) "$@partymembername$")+(i<<24),p->member[i].name);
+ j++;
+ }
+ }
+ }
+ mapreg_setreg(add_str((unsigned char *) "$@partymembercount"),j);
+
+ return 0;
+}
+/*==========================================
+ *指定IDのギルド名取得
+ *------------------------------------------
+ */
+char *buildin_getguildname_sub(int guild_id)
+{
+ struct guild *g=NULL;
+ g=guild_search(guild_id);
+
+ if(g!=NULL){
+ char *buf;
+ buf=(char *)aCallocA(NAME_LENGTH,sizeof(char));
+ memcpy(buf, g->name, NAME_LENGTH-1);
+ return buf;
+ }
+ return NULL;
+}
+int buildin_getguildname(struct script_state *st)
+{
+ char *name;
+ int guild_id=conv_num(st,& (st->stack->stack_data[st->start+2]));
+ name=buildin_getguildname_sub(guild_id);
+ if(name != NULL)
+ push_str(st->stack,C_STR,(unsigned char *) name);
+ else
+ push_str(st->stack,C_CONSTSTR,(unsigned char *) "null");
+ return 0;
+}
+
+/*==========================================
+ *指定IDのGuildMaster名取得
+ *------------------------------------------
+ */
+char *buildin_getguildmaster_sub(int guild_id)
+{
+ struct guild *g=NULL;
+ g=guild_search(guild_id);
+
+ if(g!=NULL){
+ char *buf;
+ buf=(char *)aCallocA(NAME_LENGTH,sizeof(char));
+ memcpy(buf, g->master, NAME_LENGTH-1);
+ return buf;
+ }
+
+ return 0;
+}
+int buildin_getguildmaster(struct script_state *st)
+{
+ char *master;
+ int guild_id=conv_num(st,& (st->stack->stack_data[st->start+2]));
+ master=buildin_getguildmaster_sub(guild_id);
+ if(master!=0)
+ push_str(st->stack,C_STR,(unsigned char *) master);
+ else
+ push_str(st->stack,C_CONSTSTR,(unsigned char *) "null");
+ return 0;
+}
+
+int buildin_getguildmasterid(struct script_state *st)
+{
+ char *master;
+ struct map_session_data *sd=NULL;
+ int guild_id=conv_num(st,& (st->stack->stack_data[st->start+2]));
+ master=buildin_getguildmaster_sub(guild_id);
+ if(master!=0){
+ if((sd=map_nick2sd(master)) == NULL){
+ push_val(st->stack,C_INT,0);
+ return 0;
+ }
+ push_val(st->stack,C_INT,sd->status.char_id);
+ }else{
+ push_val(st->stack,C_INT,0);
+ }
+ return 0;
+}
+
+/*==========================================
+ * キャラクタの名前
+ *------------------------------------------
+ */
+int buildin_strcharinfo(struct script_state *st)
+{
+ struct map_session_data *sd;
+ int num;
+
+ sd=script_rid2sd(st);
+ num=conv_num(st,& (st->stack->stack_data[st->start+2]));
+ if(num==0){
+ char *buf;
+ buf=(char *)aCallocA(NAME_LENGTH,sizeof(char));
+ memcpy(buf, sd->status.name, NAME_LENGTH-1);
+ push_str(st->stack,C_STR,(unsigned char *) buf);
+ }
+ if(num==1){
+ char *buf;
+ buf=buildin_getpartyname_sub(sd->status.party_id);
+ if(buf!=0)
+ push_str(st->stack,C_STR,(unsigned char *) buf);
+ else
+ push_str(st->stack,C_CONSTSTR,(unsigned char *) "");
+ }
+ if(num==2){
+ char *buf;
+ buf=buildin_getguildname_sub(sd->status.guild_id);
+ if(buf != NULL)
+ push_str(st->stack,C_STR,(unsigned char *) buf);
+ else
+ push_str(st->stack,C_CONSTSTR,(unsigned char *) "");
+ }
+
+ return 0;
+}
+
+unsigned int equip[10]={0x0100,0x0010,0x0020,0x0002,0x0004,0x0040,0x0008,0x0080,0x0200,0x0001};
+
+/*==========================================
+ * GetEquipID(Pos); Pos: 1-10
+ *------------------------------------------
+ */
+int buildin_getequipid(struct script_state *st)
+{
+ int i,num;
+ struct map_session_data *sd;
+ struct item_data* item;
+
+ sd=script_rid2sd(st);
+ if(sd == NULL)
+ {
+ ShowError("getequipid: sd == NULL\n");
+ return 0;
+ }
+ num=conv_num(st,& (st->stack->stack_data[st->start+2]));
+ i=pc_checkequip(sd,equip[num-1]);
+ if(i >= 0){
+ item=sd->inventory_data[i];
+ if(item)
+ push_val(st->stack,C_INT,item->nameid);
+ else
+ push_val(st->stack,C_INT,0);
+ }else{
+ push_val(st->stack,C_INT,-1);
+ }
+ return 0;
+}
+
+/*==========================================
+ * 装備名文字列(精錬メニュー用)
+ *------------------------------------------
+ */
+int buildin_getequipname(struct script_state *st)
+{
+ int i,num;
+ struct map_session_data *sd;
+ struct item_data* item;
+ char *buf;
+
+ buf=(char *)aCallocA(64,sizeof(char));
+ sd=script_rid2sd(st);
+ num=conv_num(st,& (st->stack->stack_data[st->start+2]));
+ i=pc_checkequip(sd,equip[num-1]);
+ if(i >= 0){
+ item=sd->inventory_data[i];
+ if(item)
+ sprintf(buf,"%s-[%s]",pos[num-1],item->jname);
+ else
+ sprintf(buf,"%s-[%s]",pos[num-1],pos[10]);
+ }else{
+ sprintf(buf,"%s-[%s]",pos[num-1],pos[10]);
+ }
+ push_str(st->stack,C_STR,(unsigned char *) buf);
+
+ return 0;
+}
+
+/*==========================================
+ * getbrokenid [Valaris]
+ *------------------------------------------
+ */
+int buildin_getbrokenid(struct script_state *st)
+{
+ int i,num,id=0,brokencounter=0;
+ struct map_session_data *sd;
+
+ sd=script_rid2sd(st);
+
+ num=conv_num(st,& (st->stack->stack_data[st->start+2]));
+ for(i=0; i<MAX_INVENTORY; i++) {
+ if(sd->status.inventory[i].attribute==1){
+ brokencounter++;
+ if(num==brokencounter){
+ id=sd->status.inventory[i].nameid;
+ break;
+ }
+ }
+ }
+
+ push_val(st->stack,C_INT,id);
+
+ return 0;
+}
+
+/*==========================================
+ * repair [Valaris]
+ *------------------------------------------
+ */
+int buildin_repair(struct script_state *st)
+{
+ int i,num;
+ int repaircounter=0;
+ struct map_session_data *sd;
+
+
+ sd=script_rid2sd(st);
+
+ num=conv_num(st,& (st->stack->stack_data[st->start+2]));
+ for(i=0; i<MAX_INVENTORY; i++) {
+ if(sd->status.inventory[i].attribute==1){
+ repaircounter++;
+ if(num==repaircounter){
+ sd->status.inventory[i].attribute=0;
+ clif_equiplist(sd);
+ clif_produceeffect(sd, 0, sd->status.inventory[i].nameid);
+ clif_misceffect(&sd->bl, 3);
+ clif_displaymessage(sd->fd,"Item has been repaired.");
+ break;
+ }
+ }
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * 装備チェック
+ *------------------------------------------
+ */
+int buildin_getequipisequiped(struct script_state *st)
+{
+ int i,num;
+ struct map_session_data *sd;
+
+ num=conv_num(st,& (st->stack->stack_data[st->start+2]));
+ sd=script_rid2sd(st);
+
+ if ((num - 1) >= (sizeof(equip) / sizeof(equip[0])))
+ i = -1;
+ else
+ i=pc_checkequip(sd,equip[num-1]);
+
+ if(i >= 0)
+ push_val(st->stack,C_INT,1);
+ else
+ push_val(st->stack,C_INT,0);
+
+ return 0;
+}
+
+/*==========================================
+ * 装備品精錬可能チェック
+ *------------------------------------------
+ */
+int buildin_getequipisenableref(struct script_state *st)
+{
+ int i,num;
+ struct map_session_data *sd;
+
+ num=conv_num(st,& (st->stack->stack_data[st->start+2]));
+ sd=script_rid2sd(st);
+ i=pc_checkequip(sd,equip[num-1]);
+ if(i >= 0 && num<7 && sd->inventory_data[i] && !sd->inventory_data[i]->flag.no_refine)
+ {
+ push_val(st->stack,C_INT,1);
+ } else {
+ push_val(st->stack,C_INT,0);
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * 装備品鑑定チェック
+ *------------------------------------------
+ */
+int buildin_getequipisidentify(struct script_state *st)
+{
+ int i,num;
+ struct map_session_data *sd;
+
+ num=conv_num(st,& (st->stack->stack_data[st->start+2]));
+ sd=script_rid2sd(st);
+ i=pc_checkequip(sd,equip[num-1]);
+ if(i >= 0)
+ push_val(st->stack,C_INT,sd->status.inventory[i].identify);
+ else
+ push_val(st->stack,C_INT,0);
+
+ return 0;
+}
+
+/*==========================================
+ * 装備品精錬度
+ *------------------------------------------
+ */
+int buildin_getequiprefinerycnt(struct script_state *st)
+{
+ int i,num;
+ struct map_session_data *sd;
+
+ num=conv_num(st,& (st->stack->stack_data[st->start+2]));
+ sd=script_rid2sd(st);
+ i=pc_checkequip(sd,equip[num-1]);
+ if(i >= 0)
+ push_val(st->stack,C_INT,sd->status.inventory[i].refine);
+ else
+ push_val(st->stack,C_INT,0);
+
+ return 0;
+}
+
+/*==========================================
+ * 装備品武器LV
+ *------------------------------------------
+ */
+int buildin_getequipweaponlv(struct script_state *st)
+{
+ int i,num;
+ struct map_session_data *sd;
+
+ num=conv_num(st,& (st->stack->stack_data[st->start+2]));
+ sd=script_rid2sd(st);
+ i=pc_checkequip(sd,equip[num-1]);
+ if(i >= 0 && sd->inventory_data[i])
+ push_val(st->stack,C_INT,sd->inventory_data[i]->wlv);
+ else
+ push_val(st->stack,C_INT,0);
+
+ return 0;
+}
+
+/*==========================================
+ * 装備品精錬成功率
+ *------------------------------------------
+ */
+int buildin_getequippercentrefinery(struct script_state *st)
+{
+ int i,num;
+ struct map_session_data *sd;
+
+ num=conv_num(st,& (st->stack->stack_data[st->start+2]));
+ sd=script_rid2sd(st);
+ i=pc_checkequip(sd,equip[num-1]);
+ if(i >= 0 && sd->status.inventory[i].nameid && sd->status.inventory[i].refine < MAX_REFINE)
+ push_val(st->stack,C_INT,percentrefinery[itemdb_wlv(sd->status.inventory[i].nameid)][(int)sd->status.inventory[i].refine]);
+ else
+ push_val(st->stack,C_INT,0);
+
+ return 0;
+}
+
+/*==========================================
+ * 精錬成功
+ *------------------------------------------
+ */
+int buildin_successrefitem(struct script_state *st)
+{
+ int i,num,ep;
+ struct map_session_data *sd;
+
+ num=conv_num(st,& (st->stack->stack_data[st->start+2]));
+ sd=script_rid2sd(st);
+ i=pc_checkequip(sd,equip[num-1]);
+ if(i >= 0) {
+ ep=sd->status.inventory[i].equip;
+
+ if(log_config.refine > 0)
+ log_refine(sd, i, 1);
+
+ //Logs items, got from (N)PC scripts [Lupus]
+ if(log_config.pick > 0 ) {
+ log_pick(sd, "N", 0, sd->status.inventory[i].nameid, -1, &sd->status.inventory[i]);
+ }
+ //Logs
+
+ sd->status.inventory[i].refine++;
+ pc_unequipitem(sd,i,2);
+
+ clif_refine(sd->fd,sd,0,i,sd->status.inventory[i].refine);
+ clif_delitem(sd,i,1);
+
+ //Logs items, got from (N)PC scripts [Lupus]
+ if(log_config.pick > 0 ) {
+ log_pick(sd, "N", 0, sd->status.inventory[i].nameid, 1, &sd->status.inventory[i]);
+ }
+ //Logs
+
+ clif_additem(sd,i,1,0);
+ pc_equipitem(sd,i,ep);
+ clif_misceffect(&sd->bl,3);
+ if(sd->status.inventory[i].refine == 10 && sd->status.inventory[i].card[0] == 0x00ff && sd->char_id == MakeDWord(sd->status.inventory[i].card[2],sd->status.inventory[i].card[3])){ // Fame point system [DracoRPG]
+ switch (sd->inventory_data[i]->wlv){
+ case 1:
+ pc_addfame(sd,1); // Success to refine to +10 a lv1 weapon you forged = +1 fame point
+ break;
+ case 2:
+ pc_addfame(sd,25); // Success to refine to +10 a lv2 weapon you forged = +25 fame point
+ break;
+ case 3:
+ pc_addfame(sd,1000); // Success to refine to +10 a lv3 weapon you forged = +1000 fame point
+ break;
+ }
+ }
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * 精錬失敗
+ *------------------------------------------
+ */
+int buildin_failedrefitem(struct script_state *st)
+{
+ int i,num;
+ struct map_session_data *sd;
+
+ num=conv_num(st,& (st->stack->stack_data[st->start+2]));
+ sd=script_rid2sd(st);
+ i=pc_checkequip(sd,equip[num-1]);
+ if(i >= 0) {
+ if(log_config.refine > 0)
+ log_refine(sd, i, 0);
+
+ //Logs items, got from (N)PC scripts [Lupus]
+ if(log_config.pick > 0 ) {
+ log_pick(sd, "N", 0, sd->status.inventory[i].nameid, -1, &sd->status.inventory[i]);
+ }
+ //Logs
+
+ sd->status.inventory[i].refine = 0;
+ pc_unequipitem(sd,i,3);
+ // 精錬失敗エフェクトのパケット
+ clif_refine(sd->fd,sd,1,i,sd->status.inventory[i].refine);
+
+ pc_delitem(sd,i,1,0);
+ // 他の人にも失敗を通知
+ clif_misceffect(&sd->bl,2);
+ }
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int buildin_statusup(struct script_state *st)
+{
+ int type;
+ struct map_session_data *sd;
+
+ type=conv_num(st,& (st->stack->stack_data[st->start+2]));
+ sd=script_rid2sd(st);
+ pc_statusup(sd,type);
+
+ return 0;
+}
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int buildin_statusup2(struct script_state *st)
+{
+ int type,val;
+ struct map_session_data *sd;
+
+ type=conv_num(st,& (st->stack->stack_data[st->start+2]));
+ val=conv_num(st,& (st->stack->stack_data[st->start+3]));
+ sd=script_rid2sd(st);
+ pc_statusup2(sd,type,val);
+
+ return 0;
+}
+/*==========================================
+ * 装備品による能力値ボーナス
+ *------------------------------------------
+ */
+int buildin_bonus(struct script_state *st)
+{
+ int type,val;
+ struct map_session_data *sd;
+
+ type=conv_num(st,& (st->stack->stack_data[st->start+2]));
+ val=conv_num(st,& (st->stack->stack_data[st->start+3]));
+ sd=script_rid2sd(st);
+ pc_bonus(sd,type,val);
+
+ return 0;
+}
+/*==========================================
+ * 装備品による能力値ボーナス
+ *------------------------------------------
+ */
+int buildin_bonus2(struct script_state *st)
+{
+ int type,type2,val;
+ struct map_session_data *sd;
+
+ type=conv_num(st,& (st->stack->stack_data[st->start+2]));
+ type2=conv_num(st,& (st->stack->stack_data[st->start+3]));
+ val=conv_num(st,& (st->stack->stack_data[st->start+4]));
+ sd=script_rid2sd(st);
+ pc_bonus2(sd,type,type2,val);
+
+ return 0;
+}
+/*==========================================
+ * 装備品による能力値ボーナス
+ *------------------------------------------
+ */
+int buildin_bonus3(struct script_state *st)
+{
+ int type,type2,type3,val;
+ struct map_session_data *sd;
+
+ type=conv_num(st,& (st->stack->stack_data[st->start+2]));
+ type2=conv_num(st,& (st->stack->stack_data[st->start+3]));
+ type3=conv_num(st,& (st->stack->stack_data[st->start+4]));
+ val=conv_num(st,& (st->stack->stack_data[st->start+5]));
+ sd=script_rid2sd(st);
+ pc_bonus3(sd,type,type2,type3,val);
+
+ return 0;
+}
+
+int buildin_bonus4(struct script_state *st)
+{
+ int type,type2,type3,type4,val;
+ struct map_session_data *sd;
+
+ type=conv_num(st,& (st->stack->stack_data[st->start+2]));
+ type2=conv_num(st,& (st->stack->stack_data[st->start+3]));
+ type3=conv_num(st,& (st->stack->stack_data[st->start+4]));
+ type4=conv_num(st,& (st->stack->stack_data[st->start+5]));
+ val=conv_num(st,& (st->stack->stack_data[st->start+6]));
+ sd=script_rid2sd(st);
+ pc_bonus4(sd,type,type2,type3,type4,val);
+
+ return 0;
+}
+/*==========================================
+ * スキル所得
+ *------------------------------------------
+ */
+int buildin_skill(struct script_state *st)
+{
+ int id,level,flag=1;
+ struct map_session_data *sd;
+
+ id=conv_num(st,& (st->stack->stack_data[st->start+2]));
+ level=conv_num(st,& (st->stack->stack_data[st->start+3]));
+ if( st->end>st->start+4 )
+ flag=conv_num(st,&(st->stack->stack_data[st->start+4]) );
+ sd=script_rid2sd(st);
+ pc_skill(sd,id,level,flag);
+
+ return 0;
+}
+
+// add x levels of skill (stackable) [Valaris]
+int buildin_addtoskill(struct script_state *st)
+{
+ int id,level,flag=2;
+ struct map_session_data *sd;
+
+ id=conv_num(st,& (st->stack->stack_data[st->start+2]));
+ level=conv_num(st,& (st->stack->stack_data[st->start+3]));
+ if( st->end>st->start+4 )
+ flag=conv_num(st,&(st->stack->stack_data[st->start+4]) );
+ sd=script_rid2sd(st);
+ pc_skill(sd,id,level,flag);
+
+ return 0;
+}
+
+/*==========================================
+ * ギルドスキル取得
+ *------------------------------------------
+ */
+int buildin_guildskill(struct script_state *st)
+{
+ int id,level,flag=0;
+ struct map_session_data *sd;
+ int i=0;
+
+ id=conv_num(st,& (st->stack->stack_data[st->start+2]));
+ level=conv_num(st,& (st->stack->stack_data[st->start+3]));
+ if( st->end>st->start+4 )
+ flag=conv_num(st,&(st->stack->stack_data[st->start+4]) );
+ sd=script_rid2sd(st);
+ for(i=0;i<level;i++)
+ guild_skillup(sd,id,flag);
+
+ return 0;
+}
+/*==========================================
+ * スキルレベル所得
+ *------------------------------------------
+ */
+int buildin_getskilllv(struct script_state *st)
+{
+ int id=conv_num(st,& (st->stack->stack_data[st->start+2]));
+ push_val(st->stack,C_INT, pc_checkskill( script_rid2sd(st) ,id) );
+ return 0;
+}
+/*==========================================
+ * getgdskilllv(Guild_ID, Skill_ID);
+ * skill_id = 10000 : GD_APPROVAL
+ * 10001 : GD_KAFRACONTRACT
+ * 10002 : GD_GUARDIANRESEARCH
+ * 10003 : GD_GUARDUP
+ * 10004 : GD_EXTENSION
+ *------------------------------------------
+ */
+int buildin_getgdskilllv(struct script_state *st)
+{
+ int guild_id=conv_num(st,& (st->stack->stack_data[st->start+2]));
+ int skill_id=conv_num(st,& (st->stack->stack_data[st->start+3]));
+ struct guild *g=guild_search(guild_id);
+ push_val(st->stack,C_INT, (g==NULL)?-1:guild_checkskill(g,skill_id) );
+ return 0;
+/*
+ struct map_session_data *sd=NULL;
+ struct guild *g=NULL;
+ int skill_id;
+
+ skill_id=conv_num(st,& (st->stack->stack_data[st->start+2]));
+ sd=script_rid2sd(st);
+ if(sd && sd->status.guild_id > 0) g=guild_search(sd->status.guild_id);
+ if(sd && g) {
+ push_val(st->stack,C_INT, guild_checkskill(g,skill_id+9999) );
+ } else {
+ push_val(st->stack,C_INT,-1);
+ }
+ return 0;
+*/
+}
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int buildin_basicskillcheck(struct script_state *st)
+{
+ push_val(st->stack,C_INT, battle_config.basic_skill_check);
+ return 0;
+}
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int buildin_getgmlevel(struct script_state *st)
+{
+ push_val(st->stack,C_INT, pc_isGM(script_rid2sd(st)));
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int buildin_end(struct script_state *st)
+{
+ st->state = END;
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int buildin_checkoption(struct script_state *st)
+{
+ int type;
+ struct map_session_data *sd;
+
+ type=conv_num(st,& (st->stack->stack_data[st->start+2]));
+ sd=script_rid2sd(st);
+
+ if(sd->status.option & type){
+ push_val(st->stack,C_INT,1);
+ } else {
+ push_val(st->stack,C_INT,0);
+ }
+
+ return 0;
+}
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int buildin_checkoption1(struct script_state *st)
+{
+ int type;
+ struct map_session_data *sd;
+
+ type=conv_num(st,& (st->stack->stack_data[st->start+2]));
+ sd=script_rid2sd(st);
+
+ if(sd->opt1 & type){
+ push_val(st->stack,C_INT,1);
+ } else {
+ push_val(st->stack,C_INT,0);
+ }
+
+ return 0;
+}
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int buildin_checkoption2(struct script_state *st)
+{
+ int type;
+ struct map_session_data *sd;
+
+ type=conv_num(st,& (st->stack->stack_data[st->start+2]));
+ sd=script_rid2sd(st);
+
+ if(sd->opt2 & type){
+ push_val(st->stack,C_INT,1);
+ } else {
+ push_val(st->stack,C_INT,0);
+ }
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int buildin_setoption(struct script_state *st)
+{
+ int type;
+ struct map_session_data *sd;
+
+ type=conv_num(st,& (st->stack->stack_data[st->start+2]));
+ sd=script_rid2sd(st);
+ pc_setoption(sd,type);
+
+ return 0;
+}
+
+/*==========================================
+ * Checkcart [Valaris]
+ *------------------------------------------
+ */
+
+int buildin_checkcart(struct script_state *st)
+{
+ struct map_session_data *sd;
+
+ sd=script_rid2sd(st);
+
+ if(pc_iscarton(sd)){
+ push_val(st->stack,C_INT,1);
+ } else {
+ push_val(st->stack,C_INT,0);
+ }
+ return 0;
+}
+
+/*==========================================
+ * カートを付ける
+ *------------------------------------------
+ */
+int buildin_setcart(struct script_state *st)
+{
+ struct map_session_data *sd;
+
+ sd=script_rid2sd(st);
+ pc_setcart(sd,1);
+
+ return 0;
+}
+
+/*==========================================
+ * checkfalcon [Valaris]
+ *------------------------------------------
+ */
+
+int buildin_checkfalcon(struct script_state *st)
+{
+ struct map_session_data *sd;
+
+ sd=script_rid2sd(st);
+
+ if(pc_isfalcon(sd)){
+ push_val(st->stack,C_INT,1);
+ } else {
+ push_val(st->stack,C_INT,0);
+ }
+
+ return 0;
+}
+
+
+/*==========================================
+ * 鷹を付ける
+ *------------------------------------------
+ */
+int buildin_setfalcon(struct script_state *st)
+{
+ struct map_session_data *sd;
+
+ sd=script_rid2sd(st);
+ pc_setfalcon(sd);
+
+ return 0;
+}
+
+/*==========================================
+ * Checkcart [Valaris]
+ *------------------------------------------
+ */
+
+int buildin_checkriding(struct script_state *st)
+{
+ struct map_session_data *sd;
+
+ sd=script_rid2sd(st);
+
+ if(pc_isriding(sd)){
+ push_val(st->stack,C_INT,1);
+ } else {
+ push_val(st->stack,C_INT,0);
+ }
+
+ return 0;
+}
+
+
+/*==========================================
+ * ペコペコ乗り
+ *------------------------------------------
+ */
+int buildin_setriding(struct script_state *st)
+{
+ struct map_session_data *sd;
+
+ sd=script_rid2sd(st);
+ pc_setriding(sd);
+
+ return 0;
+}
+
+/*==========================================
+ * セーブポイントの保存
+ *------------------------------------------
+ */
+int buildin_savepoint(struct script_state *st)
+{
+ int x,y;
+ short map;
+ char *str;
+
+ str=conv_str(st,& (st->stack->stack_data[st->start+2]));
+ x=conv_num(st,& (st->stack->stack_data[st->start+3]));
+ y=conv_num(st,& (st->stack->stack_data[st->start+4]));
+ map = mapindex_name2id(str);
+ if (map)
+ pc_setsavepoint(script_rid2sd(st),map,x,y);
+ return 0;
+}
+
+/*==========================================
+ * GetTimeTick(0: System Tick, 1: Time Second Tick)
+ *------------------------------------------
+ */
+int buildin_gettimetick(struct script_state *st) /* Asgard Version */
+{
+ int type;
+ time_t timer;
+ struct tm *t;
+
+ type=conv_num(st,& (st->stack->stack_data[st->start+2]));
+
+ switch(type){
+ case 2:
+ //type 2:(Get the number of seconds elapsed since 00:00 hours, Jan 1, 1970 UTC
+ // from the system clock.)
+ push_val(st->stack,C_INT,(int)time(NULL));
+ break;
+ case 1:
+ //type 1:(Second Ticks: 0-86399, 00:00:00-23:59:59)
+ time(&timer);
+ t=localtime(&timer);
+ push_val(st->stack,C_INT,((t->tm_hour)*3600+(t->tm_min)*60+t->tm_sec));
+ break;
+ case 0:
+ default:
+ //type 0:(System Ticks)
+ push_val(st->stack,C_INT,gettick());
+ break;
+ }
+ return 0;
+}
+
+/*==========================================
+ * GetTime(Type);
+ * 1: Sec 2: Min 3: Hour
+ * 4: WeekDay 5: MonthDay 6: Month
+ * 7: Year
+ *------------------------------------------
+ */
+int buildin_gettime(struct script_state *st) /* Asgard Version */
+{
+ int type;
+ time_t timer;
+ struct tm *t;
+
+ type=conv_num(st,& (st->stack->stack_data[st->start+2]));
+
+ time(&timer);
+ t=localtime(&timer);
+
+ switch(type){
+ case 1://Sec(0~59)
+ push_val(st->stack,C_INT,t->tm_sec);
+ break;
+ case 2://Min(0~59)
+ push_val(st->stack,C_INT,t->tm_min);
+ break;
+ case 3://Hour(0~23)
+ push_val(st->stack,C_INT,t->tm_hour);
+ break;
+ case 4://WeekDay(0~6)
+ push_val(st->stack,C_INT,t->tm_wday);
+ break;
+ case 5://MonthDay(01~31)
+ push_val(st->stack,C_INT,t->tm_mday);
+ break;
+ case 6://Month(01~12)
+ push_val(st->stack,C_INT,t->tm_mon+1);
+ break;
+ case 7://Year(20xx)
+ push_val(st->stack,C_INT,t->tm_year+1900);
+ break;
+ default://(format error)
+ push_val(st->stack,C_INT,-1);
+ break;
+ }
+ return 0;
+}
+
+/*==========================================
+ * GetTimeStr("TimeFMT", Length);
+ *------------------------------------------
+ */
+int buildin_gettimestr(struct script_state *st)
+{
+ char *tmpstr;
+ char *fmtstr;
+ int maxlen;
+ time_t now = time(NULL);
+
+ fmtstr=conv_str(st,& (st->stack->stack_data[st->start+2]));
+ maxlen=conv_num(st,& (st->stack->stack_data[st->start+3]));
+
+ tmpstr=(char *)aCallocA(maxlen+1,sizeof(char));
+ strftime(tmpstr,maxlen,fmtstr,localtime(&now));
+ tmpstr[maxlen]='\0';
+
+ push_str(st->stack,C_STR,(unsigned char *) tmpstr);
+ return 0;
+}
+
+/*==========================================
+ * カプラ倉庫を開く
+ *------------------------------------------
+ */
+int buildin_openstorage(struct script_state *st)
+{
+ storage_storageopen(script_rid2sd(st));
+ return 0;
+}
+
+int buildin_guildopenstorage(struct script_state *st)
+{
+ struct map_session_data *sd=script_rid2sd(st);
+ int ret;
+ ret = storage_guild_storageopen(sd);
+ push_val(st->stack,C_INT,ret);
+ return 0;
+}
+
+/*==========================================
+ * アイテムによるスキル発動
+ *------------------------------------------
+ */
+int buildin_itemskill(struct script_state *st)
+{
+ int id,lv;
+ char *str;
+ struct map_session_data *sd=script_rid2sd(st);
+
+ id=conv_num(st,& (st->stack->stack_data[st->start+2]));
+ lv=conv_num(st,& (st->stack->stack_data[st->start+3]));
+ str=conv_str(st,& (st->stack->stack_data[st->start+4]));
+
+ // 詠唱中にスキルアイテムは使用できない
+ if(sd->skilltimer != -1)
+ return 0;
+
+ sd->skillitem=id;
+ sd->skillitemlv=lv;
+ clif_item_skill(sd,id,lv,str);
+ return 0;
+}
+/*==========================================
+ * アイテム作成
+ *------------------------------------------
+ */
+int buildin_produce(struct script_state *st)
+{
+ int trigger;
+ struct map_session_data *sd=script_rid2sd(st);
+
+ if( sd->state.produce_flag == 1) return 0;
+ trigger=conv_num(st,& (st->stack->stack_data[st->start+2]));
+ clif_skill_produce_mix_list(sd,trigger);
+ return 0;
+}
+/*==========================================
+ * NPCでペット作る
+ *------------------------------------------
+ */
+int buildin_makepet(struct script_state *st)
+{
+ struct map_session_data *sd = script_rid2sd(st);
+ struct script_data *data;
+ int id,pet_id;
+
+ data=&(st->stack->stack_data[st->start+2]);
+ get_val(st,data);
+
+ id=conv_num(st,data);
+
+ pet_id = search_petDB_index(id, PET_CLASS);
+
+ if (pet_id < 0)
+ pet_id = search_petDB_index(id, PET_EGG);
+ if (pet_id >= 0 && sd) {
+ sd->catch_target_class = pet_db[pet_id].class_;
+ intif_create_pet(
+ sd->status.account_id, sd->status.char_id,
+ (short)pet_db[pet_id].class_, (short)mob_db(pet_db[pet_id].class_)->lv,
+ (short)pet_db[pet_id].EggID, 0, (short)pet_db[pet_id].intimate,
+ 100, 0, 1, pet_db[pet_id].jname);
+ }
+
+ return 0;
+}
+/*==========================================
+ * NPCで経験値上げる
+ *------------------------------------------
+ */
+int buildin_getexp(struct script_state *st)
+{
+ struct map_session_data *sd = script_rid2sd(st);
+ int base=0,job=0;
+
+ base=conv_num(st,& (st->stack->stack_data[st->start+2]));
+ job =conv_num(st,& (st->stack->stack_data[st->start+3]));
+ if(base<0 || job<0)
+ return 0;
+ if(sd)
+ pc_gainexp(sd,base,job);
+
+ return 0;
+}
+
+/*==========================================
+ * Gain guild exp [Celest]
+ *------------------------------------------
+ */
+int buildin_guildgetexp(struct script_state *st)
+{
+ struct map_session_data *sd = script_rid2sd(st);
+ int exp;
+
+ exp = conv_num(st,& (st->stack->stack_data[st->start+2]));
+ if(exp < 0)
+ return 0;
+ if(sd && sd->status.guild_id > 0)
+ guild_getexp (sd, exp);
+
+ return 0;
+}
+
+/*==========================================
+ * Changes the guild master of a guild [Skotlex]
+ *------------------------------------------
+ */
+int buildin_guildchangegm(struct script_state *st)
+{
+ struct map_session_data *sd;
+ int guild_id;
+ char *name;
+
+ guild_id = conv_num(st,& (st->stack->stack_data[st->start+2]));
+ name = conv_str(st,& (st->stack->stack_data[st->start+3]));
+ sd=map_nick2sd(name);
+
+ if (!sd)
+ push_val(st->stack,C_INT,0);
+ else
+ push_val(st->stack,C_INT,guild_gm_change(guild_id, sd));
+
+ return 0;
+}
+
+/*==========================================
+ * モンスター発生
+ *------------------------------------------
+ */
+int buildin_monster(struct script_state *st)
+{
+ int class_,amount,x,y;
+ char *str,*map,*event="";
+
+ map =conv_str(st,& (st->stack->stack_data[st->start+2]));
+ x =conv_num(st,& (st->stack->stack_data[st->start+3]));
+ y =conv_num(st,& (st->stack->stack_data[st->start+4]));
+ str =conv_str(st,& (st->stack->stack_data[st->start+5]));
+ class_=conv_num(st,& (st->stack->stack_data[st->start+6]));
+ amount=conv_num(st,& (st->stack->stack_data[st->start+7]));
+ if( st->end>st->start+8 )
+ event=conv_str(st,& (st->stack->stack_data[st->start+8]));
+
+ if (class_ >= 0 && !mobdb_checkid(class_)) {
+ ShowWarning("buildin_monster: Attempted to spawn non-existing monster class %d\n", class_);
+ return 1;
+ }
+ mob_once_spawn(map_id2sd(st->rid),map,x,y,str,class_,amount,event);
+ return 0;
+}
+/*==========================================
+ * モンスター発生
+ *------------------------------------------
+ */
+int buildin_areamonster(struct script_state *st)
+{
+ int class_,amount,x0,y0,x1,y1;
+ char *str,*map,*event="";
+
+ map =conv_str(st,& (st->stack->stack_data[st->start+2]));
+ x0 =conv_num(st,& (st->stack->stack_data[st->start+3]));
+ y0 =conv_num(st,& (st->stack->stack_data[st->start+4]));
+ x1 =conv_num(st,& (st->stack->stack_data[st->start+5]));
+ y1 =conv_num(st,& (st->stack->stack_data[st->start+6]));
+ str =conv_str(st,& (st->stack->stack_data[st->start+7]));
+ class_=conv_num(st,& (st->stack->stack_data[st->start+8]));
+ amount=conv_num(st,& (st->stack->stack_data[st->start+9]));
+ if( st->end>st->start+10 )
+ event=conv_str(st,& (st->stack->stack_data[st->start+10]));
+
+ mob_once_spawn_area(map_id2sd(st->rid),map,x0,y0,x1,y1,str,class_,amount,event);
+ return 0;
+}
+/*==========================================
+ * モンスター削除
+ *------------------------------------------
+ */
+int buildin_killmonster_sub(struct block_list *bl,va_list ap)
+{
+ char *event=va_arg(ap,char *);
+ int allflag=va_arg(ap,int);
+
+ if(!allflag){
+ if(strcmp(event,((struct mob_data *)bl)->npc_event)==0)
+ mob_delete((struct mob_data *)bl);
+ return 0;
+ }else if(allflag){
+ if(((struct mob_data *)bl)->spawndelay1==-1 && ((struct mob_data *)bl)->spawndelay2==-1)
+ mob_delete((struct mob_data *)bl);
+ return 0;
+ }
+ return 0;
+}
+int buildin_killmonster(struct script_state *st)
+{
+ char *mapname,*event;
+ int m,allflag=0;
+ mapname=conv_str(st,& (st->stack->stack_data[st->start+2]));
+ event=conv_str(st,& (st->stack->stack_data[st->start+3]));
+ if(strcmp(event,"All")==0)
+ allflag = 1;
+
+ if( (m=map_mapname2mapid(mapname))<0 )
+ return 0;
+ map_foreachinmap(buildin_killmonster_sub, m, BL_MOB, event ,allflag);
+ return 0;
+}
+
+int buildin_killmonsterall_sub(struct block_list *bl,va_list ap)
+{
+ mob_delete((struct mob_data *)bl);
+ return 0;
+}
+int buildin_killmonsterall(struct script_state *st)
+{
+ char *mapname;
+ int m;
+ mapname=conv_str(st,& (st->stack->stack_data[st->start+2]));
+
+ if( (m=map_mapname2mapid(mapname))<0 )
+ return 0;
+ map_foreachinmap(buildin_killmonsterall_sub,
+ m,BL_MOB);
+ return 0;
+}
+
+/*==========================================
+ * Creates a clone of a player.
+ * clone map, x, y, event, char_id, master_id, mode, flag, duration
+ *------------------------------------------
+ */
+int buildin_clone(struct script_state *st) {
+ struct map_session_data *sd, *msd=NULL;
+ int char_id,master_id=0,x,y, mode = 0, flag = 0;
+ unsigned int duration = 0;
+ char *map,*event="";
+
+ map=conv_str(st,& (st->stack->stack_data[st->start+2]));
+ x=conv_num(st,& (st->stack->stack_data[st->start+3]));
+ y=conv_num(st,& (st->stack->stack_data[st->start+4]));
+ event=conv_str(st,& (st->stack->stack_data[st->start+5]));
+ char_id=conv_num(st,& (st->stack->stack_data[st->start+6]));
+
+ if( st->end>st->start+7 )
+ master_id=conv_num(st,& (st->stack->stack_data[st->start+7]));
+
+ if( st->end>st->start+8 )
+ mode=conv_num(st,& (st->stack->stack_data[st->start+8]));
+
+ if( st->end>st->start+9 )
+ flag=conv_num(st,& (st->stack->stack_data[st->start+9]));
+
+ if( st->end>st->start+10 )
+ duration=conv_num(st,& (st->stack->stack_data[st->start+10]));
+
+ sd = map_charid2sd(char_id);
+ if (master_id) {
+ msd = map_charid2sd(master_id);
+ if (msd)
+ master_id = msd->bl.id;
+ else
+ master_id = 0;
+ }
+ if (sd) //Return ID of newly crafted clone.
+ push_val(st->stack,C_INT,mob_clone_spawn(sd, map, x, y, event, master_id, mode, flag, 1000*duration));
+ else //Failed to create clone.
+ push_val(st->stack,C_INT,0);
+
+ return 0;
+}
+/*==========================================
+ * イベント実行
+ *------------------------------------------
+ */
+int buildin_doevent(struct script_state *st)
+{
+ char *event;
+ event=conv_str(st,& (st->stack->stack_data[st->start+2]));
+ npc_event(map_id2sd(st->rid),event,0);
+ return 0;
+}
+/*==========================================
+ * NPC主体イベント実行
+ *------------------------------------------
+ */
+int buildin_donpcevent(struct script_state *st)
+{
+ char *event;
+ event=conv_str(st,& (st->stack->stack_data[st->start+2]));
+ npc_event_do(event);
+ return 0;
+}
+/*==========================================
+ * イベントタイマー追加
+ *------------------------------------------
+ */
+int buildin_addtimer(struct script_state *st)
+{
+ char *event;
+ int tick;
+ tick=conv_num(st,& (st->stack->stack_data[st->start+2]));
+ event=conv_str(st,& (st->stack->stack_data[st->start+3]));
+ pc_addeventtimer(script_rid2sd(st),tick,event);
+ return 0;
+}
+/*==========================================
+ * イベントタイマー削除
+ *------------------------------------------
+ */
+int buildin_deltimer(struct script_state *st)
+{
+ char *event;
+ event=conv_str(st,& (st->stack->stack_data[st->start+2]));
+ pc_deleventtimer(script_rid2sd(st),event);
+ return 0;
+}
+/*==========================================
+ * イベントタイマーのカウント値追加
+ *------------------------------------------
+ */
+int buildin_addtimercount(struct script_state *st)
+{
+ char *event;
+ int tick;
+ event=conv_str(st,& (st->stack->stack_data[st->start+2]));
+ tick=conv_num(st,& (st->stack->stack_data[st->start+3]));
+ pc_addeventtimercount(script_rid2sd(st),event,tick);
+ return 0;
+}
+
+/*==========================================
+ * NPCタイマー初期化
+ *------------------------------------------
+ */
+int buildin_initnpctimer(struct script_state *st)
+{
+ struct npc_data *nd;
+ if( st->end > st->start+2 )
+ nd=npc_name2id(conv_str(st,& (st->stack->stack_data[st->start+2])));
+ else
+ nd=(struct npc_data *)map_id2bl(st->oid);
+
+ npc_settimerevent_tick(nd,0);
+ npc_timerevent_start(nd, st->rid);
+ return 0;
+}
+/*==========================================
+ * NPCタイマー開始
+ *------------------------------------------
+ */
+int buildin_startnpctimer(struct script_state *st)
+{
+ struct npc_data *nd;
+ if( st->end > st->start+2 )
+ nd=npc_name2id(conv_str(st,& (st->stack->stack_data[st->start+2])));
+ else
+ nd=(struct npc_data *)map_id2bl(st->oid);
+
+ npc_timerevent_start(nd, st->rid);
+ return 0;
+}
+/*==========================================
+ * NPCタイマー停止
+ *------------------------------------------
+ */
+int buildin_stopnpctimer(struct script_state *st)
+{
+ struct npc_data *nd;
+ if( st->end > st->start+2 )
+ nd=npc_name2id(conv_str(st,& (st->stack->stack_data[st->start+2])));
+ else
+ nd=(struct npc_data *)map_id2bl(st->oid);
+
+ npc_timerevent_stop(nd);
+ return 0;
+}
+/*==========================================
+ * NPCタイマー情報所得
+ *------------------------------------------
+ */
+int buildin_getnpctimer(struct script_state *st)
+{
+ struct npc_data *nd;
+ int type=conv_num(st,& (st->stack->stack_data[st->start+2]));
+ int val=0;
+ if( st->end > st->start+3 )
+ nd=npc_name2id(conv_str(st,& (st->stack->stack_data[st->start+3])));
+ else
+ nd=(struct npc_data *)map_id2bl(st->oid);
+
+ switch(type){
+ case 0: val=npc_gettimerevent_tick(nd); break;
+ case 1: val= (nd->u.scr.nexttimer>=0); break;
+ case 2: val= nd->u.scr.timeramount; break;
+ }
+ push_val(st->stack,C_INT,val);
+ return 0;
+}
+/*==========================================
+ * NPCタイマー値設定
+ *------------------------------------------
+ */
+int buildin_setnpctimer(struct script_state *st)
+{
+ int tick;
+ struct npc_data *nd;
+ tick=conv_num(st,& (st->stack->stack_data[st->start+2]));
+ if( st->end > st->start+3 )
+ nd=npc_name2id(conv_str(st,& (st->stack->stack_data[st->start+3])));
+ else
+ nd=(struct npc_data *)map_id2bl(st->oid);
+
+ npc_settimerevent_tick(nd,tick);
+ return 0;
+}
+
+/*==========================================
+ * attaches the player rid to the timer [Celest]
+ *------------------------------------------
+ */
+int buildin_attachnpctimer(struct script_state *st)
+{
+ struct map_session_data *sd;
+ struct npc_data *nd;
+
+ nd=(struct npc_data *)map_id2bl(st->oid);
+ if( st->end > st->start+2 ) {
+ char *name = conv_str(st,& (st->stack->stack_data[st->start+2]));
+ sd=map_nick2sd(name);
+ } else {
+ sd = script_rid2sd(st);
+ }
+
+ if (sd==NULL)
+ return 0;
+
+ nd->u.scr.rid = sd->bl.id;
+ return 0;
+}
+
+/*==========================================
+ * detaches a player rid from the timer [Celest]
+ *------------------------------------------
+ */
+int buildin_detachnpctimer(struct script_state *st)
+{
+ struct npc_data *nd;
+ if( st->end > st->start+2 )
+ nd=npc_name2id(conv_str(st,& (st->stack->stack_data[st->start+2])));
+ else
+ nd=(struct npc_data *)map_id2bl(st->oid);
+
+ nd->u.scr.rid = 0;
+ return 0;
+}
+
+/*==========================================
+ * To avoid "player not attached" script errors, this function is provided,
+ * it checks if there is a player attached to the current script. [Skotlex]
+ * If no, returns 0, if yes, returns the char_id of the attached player.
+ *------------------------------------------
+ */
+int buildin_playerattached(struct script_state *st)
+{
+ struct map_session_data *sd;
+ if (st->rid == 0 || (sd = map_id2sd(st->rid)) == NULL)
+ push_val(st->stack,C_INT,0);
+ else
+ push_val(st->stack,C_INT,st->rid);
+ return 0;
+}
+
+/*==========================================
+ * 天の声アナウンス
+ *------------------------------------------
+ */
+int buildin_announce(struct script_state *st)
+{
+ char *str, *color=NULL;
+ int flag;
+ str=conv_str(st,& (st->stack->stack_data[st->start+2]));
+ flag=conv_num(st,& (st->stack->stack_data[st->start+3]));
+ if (st->end>st->start+4)
+ color=conv_str(st,& (st->stack->stack_data[st->start+4]));
+
+ if(flag&0x0f){
+ struct block_list *bl=(flag&0x08)? map_id2bl(st->oid) :
+ (struct block_list *)script_rid2sd(st);
+ if (color)
+ clif_announce(bl,str,(int)strlen(str)+1, strtol(color, (char **)NULL, 0),flag);
+ else
+ clif_GMmessage(bl,str,(int)strlen(str)+1,flag);
+ }else {
+ if (color)
+ intif_announce(str,(int)strlen(str)+1, strtol(color, (char **)NULL, 0), flag);
+ else
+ intif_GMmessage(str,(int)strlen(str)+1,flag);
+ }
+ return 0;
+}
+/*==========================================
+ * 天の声アナウンス(特定マップ)
+ *------------------------------------------
+ */
+int buildin_mapannounce_sub(struct block_list *bl,va_list ap)
+{
+ char *str, *color;
+ int len,flag;
+ str=va_arg(ap,char *);
+ len=va_arg(ap,int);
+ flag=va_arg(ap,int);
+ color=va_arg(ap,char *);
+ if (color)
+ clif_announce(bl,str,len, strtol(color, (char **)NULL, 0), flag|3);
+ else
+ clif_GMmessage(bl,str,len,flag|3);
+ return 0;
+}
+int buildin_mapannounce(struct script_state *st)
+{
+ char *mapname,*str, *color=NULL;
+ int flag,m;
+
+ mapname=conv_str(st,& (st->stack->stack_data[st->start+2]));
+ str=conv_str(st,& (st->stack->stack_data[st->start+3]));
+ flag=conv_num(st,& (st->stack->stack_data[st->start+4]));
+ if (st->end>st->start+5)
+ color=conv_str(st,& (st->stack->stack_data[st->start+5]));
+
+ if( (m=map_mapname2mapid(mapname))<0 )
+ return 0;
+
+ map_foreachinmap(buildin_mapannounce_sub,
+ m, BL_PC, str,strlen(str)+1,flag&0x10, color);
+ return 0;
+}
+/*==========================================
+ * 天の声アナウンス(特定エリア)
+ *------------------------------------------
+ */
+int buildin_areaannounce(struct script_state *st)
+{
+ char *map,*str,*color=NULL;
+ int flag,m;
+ int x0,y0,x1,y1;
+
+ map=conv_str(st,& (st->stack->stack_data[st->start+2]));
+ x0=conv_num(st,& (st->stack->stack_data[st->start+3]));
+ y0=conv_num(st,& (st->stack->stack_data[st->start+4]));
+ x1=conv_num(st,& (st->stack->stack_data[st->start+5]));
+ y1=conv_num(st,& (st->stack->stack_data[st->start+6]));
+ str=conv_str(st,& (st->stack->stack_data[st->start+7]));
+ flag=conv_num(st,& (st->stack->stack_data[st->start+8]));
+ if (st->end>st->start+9)
+ color=conv_str(st,& (st->stack->stack_data[st->start+9]));
+
+ if( (m=map_mapname2mapid(map))<0 )
+ return 0;
+
+ map_foreachinarea(buildin_mapannounce_sub,
+ m,x0,y0,x1,y1,BL_PC, str,strlen(str)+1,flag&0x10, color);
+ return 0;
+}
+
+/*==========================================
+ * ユーザー数所得
+ *------------------------------------------
+ */
+int buildin_getusers(struct script_state *st)
+{
+ int flag=conv_num(st,& (st->stack->stack_data[st->start+2]));
+ struct block_list *bl=map_id2bl((flag&0x08)?st->oid:st->rid);
+ int val=0;
+ switch(flag&0x07){
+ case 0: val=map[bl->m].users; break;
+ case 1: val=map_getusers(); break;
+ }
+ push_val(st->stack,C_INT,val);
+ return 0;
+}
+/*==========================================
+ * Works like @WHO - displays all online users names in window
+ *------------------------------------------
+ */
+int buildin_getusersname(struct script_state *st)
+{
+ struct map_session_data *pl_sd = NULL, **pl_allsd;
+ int i=0,disp_num=1, users;
+
+ pl_allsd = map_getallusers(&users);
+
+ for (i=0;i<users;i++)
+ {
+ pl_sd = pl_allsd[i];
+ if( !(battle_config.hide_GM_session && pc_isGM(pl_sd)) )
+ {
+ if((disp_num++)%10==0)
+ clif_scriptnext(script_rid2sd(st),st->oid);
+ clif_scriptmes(script_rid2sd(st),st->oid,pl_sd->status.name);
+ }
+ }
+ return 0;
+}
+/*==========================================
+ * マップ指定ユーザー数所得
+ *------------------------------------------
+ */
+int buildin_getmapusers(struct script_state *st)
+{
+ char *str;
+ int m;
+ str=conv_str(st,& (st->stack->stack_data[st->start+2]));
+ if( (m=map_mapname2mapid(str))< 0){
+ push_val(st->stack,C_INT,-1);
+ return 0;
+ }
+ push_val(st->stack,C_INT,map[m].users);
+ return 0;
+}
+/*==========================================
+ * エリア指定ユーザー数所得
+ *------------------------------------------
+ */
+int buildin_getareausers_sub(struct block_list *bl,va_list ap)
+{
+ int *users=va_arg(ap,int *);
+ (*users)++;
+ return 0;
+}
+int buildin_getareausers(struct script_state *st)
+{
+ char *str;
+ int m,x0,y0,x1,y1,users=0;
+ str=conv_str(st,& (st->stack->stack_data[st->start+2]));
+ x0=conv_num(st,& (st->stack->stack_data[st->start+3]));
+ y0=conv_num(st,& (st->stack->stack_data[st->start+4]));
+ x1=conv_num(st,& (st->stack->stack_data[st->start+5]));
+ y1=conv_num(st,& (st->stack->stack_data[st->start+6]));
+ if( (m=map_mapname2mapid(str))< 0){
+ push_val(st->stack,C_INT,-1);
+ return 0;
+ }
+ map_foreachinarea(buildin_getareausers_sub,
+ m,x0,y0,x1,y1,BL_PC,&users);
+ push_val(st->stack,C_INT,users);
+ return 0;
+}
+
+/*==========================================
+ * エリア指定ドロップアイテム数所得
+ *------------------------------------------
+ */
+int buildin_getareadropitem_sub(struct block_list *bl,va_list ap)
+{
+ int item=va_arg(ap,int);
+ int *amount=va_arg(ap,int *);
+ struct flooritem_data *drop=(struct flooritem_data *)bl;
+
+ if(drop->item_data.nameid==item)
+ (*amount)+=drop->item_data.amount;
+
+ return 0;
+}
+int buildin_getareadropitem(struct script_state *st)
+{
+ char *str;
+ int m,x0,y0,x1,y1,item,amount=0;
+ struct script_data *data;
+
+ str=conv_str(st,& (st->stack->stack_data[st->start+2]));
+ x0=conv_num(st,& (st->stack->stack_data[st->start+3]));
+ y0=conv_num(st,& (st->stack->stack_data[st->start+4]));
+ x1=conv_num(st,& (st->stack->stack_data[st->start+5]));
+ y1=conv_num(st,& (st->stack->stack_data[st->start+6]));
+
+ data=&(st->stack->stack_data[st->start+7]);
+ get_val(st,data);
+ if( data->type==C_STR || data->type==C_CONSTSTR ){
+ const char *name=conv_str(st,data);
+ struct item_data *item_data = itemdb_searchname(name);
+ item=512;
+ if( item_data )
+ item=item_data->nameid;
+ }else
+ item=conv_num(st,data);
+
+ if( (m=map_mapname2mapid(str))< 0){
+ push_val(st->stack,C_INT,-1);
+ return 0;
+ }
+ map_foreachinarea(buildin_getareadropitem_sub,
+ m,x0,y0,x1,y1,BL_ITEM,item,&amount);
+ push_val(st->stack,C_INT,amount);
+ return 0;
+}
+/*==========================================
+ * NPCの有効化
+ *------------------------------------------
+ */
+int buildin_enablenpc(struct script_state *st)
+{
+ char *str;
+ str=conv_str(st,& (st->stack->stack_data[st->start+2]));
+ npc_enable(str,1);
+ return 0;
+}
+/*==========================================
+ * NPCの無効化
+ *------------------------------------------
+ */
+int buildin_disablenpc(struct script_state *st)
+{
+ char *str;
+ str=conv_str(st,& (st->stack->stack_data[st->start+2]));
+ npc_enable(str,0);
+ return 0;
+}
+
+int buildin_enablearena(struct script_state *st) // Added by RoVeRT
+{
+ struct npc_data *nd=(struct npc_data *)map_id2bl(st->oid);
+ struct chat_data *cd;
+
+
+ if(nd==NULL || (cd=(struct chat_data *)map_id2bl(nd->chat_id))==NULL)
+ return 0;
+
+ npc_enable(nd->name,1);
+ nd->arenaflag=1;
+
+ if(cd->users>=cd->trigger && cd->npc_event[0])
+ npc_timer_event(cd->npc_event);
+
+ return 0;
+}
+int buildin_disablearena(struct script_state *st) // Added by RoVeRT
+{
+ struct npc_data *nd=(struct npc_data *)map_id2bl(st->oid);
+ nd->arenaflag=0;
+
+ return 0;
+}
+/*==========================================
+ * 隠れているNPCの表示
+ *------------------------------------------
+ */
+int buildin_hideoffnpc(struct script_state *st)
+{
+ char *str;
+ str=conv_str(st,& (st->stack->stack_data[st->start+2]));
+ npc_enable(str,2);
+ return 0;
+}
+/*==========================================
+ * NPCをハイディング
+ *------------------------------------------
+ */
+int buildin_hideonnpc(struct script_state *st)
+{
+ char *str;
+ str=conv_str(st,& (st->stack->stack_data[st->start+2]));
+ npc_enable(str,4);
+ return 0;
+}
+/*==========================================
+ * 状態異常にかかる
+ *------------------------------------------
+ */
+int buildin_sc_start(struct script_state *st)
+{
+ struct block_list *bl;
+ int type,tick,val1,val4=0;
+ type=conv_num(st,& (st->stack->stack_data[st->start+2]));
+ tick=conv_num(st,& (st->stack->stack_data[st->start+3]));
+ val1=conv_num(st,& (st->stack->stack_data[st->start+4]));
+ if( st->end>st->start+5 ) //指定したキャラを状態異常にする
+ bl = map_id2bl(conv_num(st,& (st->stack->stack_data[st->start+5])));
+ else
+ bl = map_id2bl(st->rid);
+
+ if (potion_flag==1 && potion_target) {
+ bl = map_id2bl(potion_target);
+ tick/=2; //Thrown potions only last half.
+ val4 = 1; //Mark that this was a thrown sc_effect
+ }
+ if (bl)
+ status_change_start(bl,type,val1,0,0,val4,tick,0);
+ return 0;
+}
+
+/*==========================================
+ * 状態異常にかかる(確率指定)
+ *------------------------------------------
+ */
+int buildin_sc_start2(struct script_state *st)
+{
+ struct block_list *bl;
+ int type,tick,val1,val4=0,per;
+ type=conv_num(st,& (st->stack->stack_data[st->start+2]));
+ tick=conv_num(st,& (st->stack->stack_data[st->start+3]));
+ val1=conv_num(st,& (st->stack->stack_data[st->start+4]));
+ per=conv_num(st,& (st->stack->stack_data[st->start+5]));
+ if( st->end>st->start+6 ) //指定したキャラを状態異常にする
+ bl = map_id2bl(conv_num(st,& (st->stack->stack_data[st->start+6])));
+ else
+ bl = map_id2bl(st->rid);
+
+ if (potion_flag==1 && potion_target) {
+ bl = map_id2bl(potion_target);
+ tick/=2;
+ val4 = 1;
+ }
+
+ if(bl && rand()%10000 < per)
+ status_change_start(bl,type,val1,0,0,val4,tick,0);
+ return 0;
+}
+
+/*==========================================
+ * Starts a SC_ change with the four values passed. [Skotlex]
+ * Final optional argument is the ID of player to affect.
+ * sc_start4 type, duration, val1, val2, val3, val4, <id>;
+ *------------------------------------------
+ */
+int buildin_sc_start4(struct script_state *st)
+{
+ struct block_list *bl;
+ int type,tick,val1,val2,val3,val4;
+ type=conv_num(st,& (st->stack->stack_data[st->start+2]));
+ tick=conv_num(st,& (st->stack->stack_data[st->start+3]));
+ val1=conv_num(st,& (st->stack->stack_data[st->start+4]));
+ val2=conv_num(st,& (st->stack->stack_data[st->start+5]));
+ val3=conv_num(st,& (st->stack->stack_data[st->start+6]));
+ val4=conv_num(st,& (st->stack->stack_data[st->start+7]));
+ if( st->end>st->start+8 )
+ bl = map_id2bl(conv_num(st,& (st->stack->stack_data[st->start+8])));
+ else
+ bl = map_id2bl(st->rid);
+
+ if (potion_flag==1 && potion_target) {
+ bl = map_id2bl(potion_target);
+ tick/=2;
+ }
+ if (bl)
+ status_change_start(bl,type,val1,val2,val3,val4,tick,0);
+ return 0;
+}
+
+/*==========================================
+ * 状態異常が直る
+ *------------------------------------------
+ */
+int buildin_sc_end(struct script_state *st)
+{
+ struct block_list *bl;
+ int type;
+ type=conv_num(st,& (st->stack->stack_data[st->start+2]));
+ bl = map_id2bl(st->rid);
+
+ if (potion_flag==1 && potion_target)
+ bl = map_id2bl(potion_target);
+
+ if (bl)
+ status_change_end(bl,type,-1);
+ return 0;
+}
+/*==========================================
+ * 状態異常耐性を計算した確率を返す
+ *------------------------------------------
+ */
+int buildin_getscrate(struct script_state *st)
+{
+ struct block_list *bl;
+ int sc_def,type,rate;
+
+ type=conv_num(st,& (st->stack->stack_data[st->start+2]));
+ rate=conv_num(st,& (st->stack->stack_data[st->start+3]));
+ if( st->end>st->start+4 ) //指定したキャラの耐性を計算する
+ bl = map_id2bl(conv_num(st,& (st->stack->stack_data[st->start+6])));
+ else
+ bl = map_id2bl(st->rid);
+
+ sc_def = status_get_sc_def(bl,type);
+
+ rate = rate * sc_def / 100;
+ push_val(st->stack,C_INT,rate);
+
+ return 0;
+
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int buildin_debugmes(struct script_state *st)
+{
+ conv_str(st,& (st->stack->stack_data[st->start+2]));
+ ShowDebug("script debug : %d %d : %s\n",st->rid,st->oid,st->stack->stack_data[st->start+2].u.str);
+ return 0;
+}
+
+/*==========================================
+ *捕獲アイテム使用
+ *------------------------------------------
+ */
+int buildin_catchpet(struct script_state *st)
+{
+ int pet_id;
+ struct map_session_data *sd;
+ pet_id= conv_num(st,& (st->stack->stack_data[st->start+2]));
+ sd=script_rid2sd(st);
+ pet_catch_process1(sd,pet_id);
+ return 0;
+}
+
+/*==========================================
+ *携帯卵孵化機使用
+ *------------------------------------------
+ */
+int buildin_birthpet(struct script_state *st)
+{
+ struct map_session_data *sd;
+ sd=script_rid2sd(st);
+ clif_sendegg(sd);
+ return 0;
+}
+
+/*==========================================
+ * Added - AppleGirl For Advanced Classes, (Updated for Cleaner Script Purposes)
+ *------------------------------------------
+ */
+int buildin_resetlvl(struct script_state *st)
+{
+ struct map_session_data *sd;
+
+ int type=conv_num(st,& (st->stack->stack_data[st->start+2]));
+
+ sd=script_rid2sd(st);
+ pc_resetlvl(sd,type);
+ return 0;
+}
+/*==========================================
+ * ステータスリセット
+ *------------------------------------------
+ */
+int buildin_resetstatus(struct script_state *st)
+{
+ struct map_session_data *sd;
+ sd=script_rid2sd(st);
+ pc_resetstate(sd);
+ return 0;
+}
+
+/*==========================================
+ * script command resetskill
+ *------------------------------------------
+ */
+int buildin_resetskill(struct script_state *st)
+{
+ struct map_session_data *sd;
+ sd=script_rid2sd(st);
+ pc_resetskill(sd);
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int buildin_changebase(struct script_state *st)
+{
+ struct map_session_data *sd=NULL;
+ int vclass;
+
+ if( st->end>st->start+3 )
+ sd=map_id2sd(conv_num(st,& (st->stack->stack_data[st->start+3])));
+ else
+ sd=script_rid2sd(st);
+
+ if(sd == NULL)
+ return 0;
+
+ vclass = conv_num(st,& (st->stack->stack_data[st->start+2]));
+ if(vclass == JOB_WEDDING)
+ {
+ if (!battle_config.wedding_modifydisplay || //Do not show the wedding sprites
+ sd->class_&JOBL_BABY //Baby classes screw up when showing wedding sprites. [Skotlex]
+ )
+ return 0;
+ }
+
+// if(vclass==22) {
+// pc_unequipitem(sd,sd->equip_index[9],0); // 装備外
+// }
+
+ sd->view_class = vclass;
+
+ return 0;
+}
+
+/*==========================================
+ * 性別変換
+ *------------------------------------------
+ */
+int buildin_changesex(struct script_state *st) {
+ struct map_session_data *sd = NULL;
+ sd = script_rid2sd(st);
+
+ if (sd->status.sex == 0) {
+ sd->status.sex = 1;
+ sd->sex = 1;
+ if ((sd->class_&MAPID_UPPERMASK) == MAPID_BARDDANCER)
+ sd->status.class_ -= 1;
+ } else if (sd->status.sex == 1) {
+ sd->status.sex = 0;
+ sd->sex = 0;
+ if ((sd->class_&MAPID_UPPERMASK) == MAPID_BARDDANCER)
+ sd->status.class_ += 1;
+ }
+ chrif_char_ask_name(-1, sd->status.name, 5, 0, 0, 0, 0, 0, 0); // type: 5 - changesex
+ chrif_save(sd,0);
+ return 0;
+}
+
+/*==========================================
+ * npcチャット作成
+ *------------------------------------------
+ */
+int buildin_waitingroom(struct script_state *st)
+{
+ char *name,*ev="";
+ int limit, trigger = 0,pub=1;
+ name=conv_str(st,& (st->stack->stack_data[st->start+2]));
+ limit= conv_num(st,& (st->stack->stack_data[st->start+3]));
+ if(limit==0)
+ pub=3;
+
+ if( (st->end > st->start+5) ){
+ struct script_data* data=&(st->stack->stack_data[st->start+5]);
+ get_val(st,data);
+ if(data->type==C_INT){
+ // 新Athena仕様(旧Athena仕様と互換性あり)
+ ev=conv_str(st,& (st->stack->stack_data[st->start+4]));
+ trigger=conv_num(st,& (st->stack->stack_data[st->start+5]));
+ }else{
+ // eathena仕様
+ trigger=conv_num(st,& (st->stack->stack_data[st->start+4]));
+ ev=conv_str(st,& (st->stack->stack_data[st->start+5]));
+ }
+ }else{
+ // 旧Athena仕様
+ if( st->end > st->start+4 )
+ ev=conv_str(st,& (st->stack->stack_data[st->start+4]));
+ }
+ chat_createnpcchat( (struct npc_data *)map_id2bl(st->oid),
+ limit,pub,trigger,name,(int)strlen(name)+1,ev);
+ return 0;
+}
+/*==========================================
+ * Works like 'announce' but outputs in the common chat window
+ *------------------------------------------
+ */
+int buildin_globalmes(struct script_state *st)
+{
+ struct block_list *bl = map_id2bl(st->oid);
+ struct npc_data *nd = (struct npc_data *)bl;
+ char *name=NULL,*mes;
+
+ mes=conv_str(st,& (st->stack->stack_data[st->start+2])); // メッセージの取得
+ if(mes==NULL) return 0;
+
+ if(st->end>st->start+3){ // NPC名の取得(123#456)
+ name=conv_str(st,& (st->stack->stack_data[st->start+3]));
+ } else {
+ name=nd->name;
+ }
+
+ npc_globalmessage(name,mes); // グローバルメッセージ送信
+
+ return 0;
+}
+/*==========================================
+ * npcチャット削除
+ *------------------------------------------
+ */
+int buildin_delwaitingroom(struct script_state *st)
+{
+ struct npc_data *nd;
+ if( st->end > st->start+2 )
+ nd=npc_name2id(conv_str(st,& (st->stack->stack_data[st->start+2])));
+ else
+ nd=(struct npc_data *)map_id2bl(st->oid);
+ chat_deletenpcchat(nd);
+ return 0;
+}
+/*==========================================
+ * npcチャット全員蹴り出す
+ *------------------------------------------
+ */
+int buildin_waitingroomkickall(struct script_state *st)
+{
+ struct npc_data *nd;
+ struct chat_data *cd;
+
+ if( st->end > st->start+2 )
+ nd=npc_name2id(conv_str(st,& (st->stack->stack_data[st->start+2])));
+ else
+ nd=(struct npc_data *)map_id2bl(st->oid);
+
+ if(nd==NULL || (cd=(struct chat_data *)map_id2bl(nd->chat_id))==NULL )
+ return 0;
+ chat_npckickall(cd);
+ return 0;
+}
+
+/*==========================================
+ * npcチャットイベント有効化
+ *------------------------------------------
+ */
+int buildin_enablewaitingroomevent(struct script_state *st)
+{
+ struct npc_data *nd;
+ struct chat_data *cd;
+
+ if( st->end > st->start+2 )
+ nd=npc_name2id(conv_str(st,& (st->stack->stack_data[st->start+2])));
+ else
+ nd=(struct npc_data *)map_id2bl(st->oid);
+
+ if(nd==NULL || (cd=(struct chat_data *)map_id2bl(nd->chat_id))==NULL )
+ return 0;
+ chat_enableevent(cd);
+ return 0;
+}
+
+/*==========================================
+ * npcチャットイベント無効化
+ *------------------------------------------
+ */
+int buildin_disablewaitingroomevent(struct script_state *st)
+{
+ struct npc_data *nd;
+ struct chat_data *cd;
+
+ if( st->end > st->start+2 )
+ nd=npc_name2id(conv_str(st,& (st->stack->stack_data[st->start+2])));
+ else
+ nd=(struct npc_data *)map_id2bl(st->oid);
+
+ if(nd==NULL || (cd=(struct chat_data *)map_id2bl(nd->chat_id))==NULL )
+ return 0;
+ chat_disableevent(cd);
+ return 0;
+}
+/*==========================================
+ * npcチャット状態所得
+ *------------------------------------------
+ */
+int buildin_getwaitingroomstate(struct script_state *st)
+{
+ struct npc_data *nd;
+ struct chat_data *cd;
+ int val=0,type;
+ type=conv_num(st,& (st->stack->stack_data[st->start+2]));
+ if( st->end > st->start+3 )
+ nd=npc_name2id(conv_str(st,& (st->stack->stack_data[st->start+3])));
+ else
+ nd=(struct npc_data *)map_id2bl(st->oid);
+
+ if(nd==NULL || (cd=(struct chat_data *)map_id2bl(nd->chat_id))==NULL ){
+ push_val(st->stack,C_INT,-1);
+ return 0;
+ }
+
+ switch(type){
+ case 0: val=cd->users; break;
+ case 1: val=cd->limit; break;
+ case 2: val=cd->trigger&0x7f; break;
+ case 3: val=((cd->trigger&0x80)>0); break;
+ case 32: val=(cd->users >= cd->limit); break;
+ case 33: val=(cd->users >= cd->trigger); break;
+
+ case 4:
+ push_str(st->stack,C_CONSTSTR,(unsigned char *) cd->title);
+ return 0;
+ case 5:
+ push_str(st->stack,C_CONSTSTR,(unsigned char *) cd->pass);
+ return 0;
+ case 16:
+ push_str(st->stack,C_CONSTSTR,(unsigned char *) cd->npc_event);
+ return 0;
+ }
+ push_val(st->stack,C_INT,val);
+ return 0;
+}
+
+/*==========================================
+ * チャットメンバー(規定人数)ワープ
+ *------------------------------------------
+ */
+int buildin_warpwaitingpc(struct script_state *st)
+{
+ int x,y,i,n;
+ char *str;
+ struct npc_data *nd=(struct npc_data *)map_id2bl(st->oid);
+ struct chat_data *cd;
+
+ if(nd==NULL || (cd=(struct chat_data *)map_id2bl(nd->chat_id))==NULL )
+ return 0;
+
+ n=cd->trigger&0x7f;
+ str=conv_str(st,& (st->stack->stack_data[st->start+2]));
+ x=conv_num(st,& (st->stack->stack_data[st->start+3]));
+ y=conv_num(st,& (st->stack->stack_data[st->start+4]));
+
+ if( st->end > st->start+5 )
+ n=conv_num(st,& (st->stack->stack_data[st->start+5]));
+
+ for(i=0;i<n;i++){
+ struct map_session_data *sd=cd->usersd[0]; // リスト先頭のPCを次々に。
+
+ mapreg_setreg(add_str((unsigned char *) "$@warpwaitingpc")+(i<<24),sd->bl.id);
+
+ if(strcmp(str,"Random")==0)
+ pc_randomwarp(sd,3);
+ else if(strcmp(str,"SavePoint")==0){
+ if(map[sd->bl.m].flag.noteleport) // テレポ禁止
+ return 0;
+
+ pc_setpos(sd,sd->status.save_point.map,
+ sd->status.save_point.x,sd->status.save_point.y,3);
+ }else
+ pc_setpos(sd,mapindex_name2id(str),x,y,0);
+ }
+ mapreg_setreg(add_str((unsigned char *) "$@warpwaitingpcnum"),n);
+ return 0;
+}
+/*==========================================
+ * RIDのアタッチ
+ *------------------------------------------
+ */
+int buildin_attachrid(struct script_state *st)
+{
+ st->rid=conv_num(st,& (st->stack->stack_data[st->start+2]));
+ push_val(st->stack,C_INT, (map_id2sd(st->rid)!=NULL));
+ return 0;
+}
+/*==========================================
+ * RIDのデタッチ
+ *------------------------------------------
+ */
+int buildin_detachrid(struct script_state *st)
+{
+ st->rid=0;
+ return 0;
+}
+/*==========================================
+ * 存在チェック
+ *------------------------------------------
+ */
+int buildin_isloggedin(struct script_state *st)
+{
+ push_val(st->stack,C_INT, map_id2sd(
+ conv_num(st,& (st->stack->stack_data[st->start+2])) )!=NULL );
+ return 0;
+}
+
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+enum { MF_NOMEMO,MF_NOTELEPORT,MF_NOSAVE,MF_NOBRANCH,MF_NOPENALTY,MF_NOZENYPENALTY,
+ MF_PVP,MF_PVP_NOPARTY,MF_PVP_NOGUILD,MF_GVG,MF_GVG_NOPARTY,MF_NOTRADE,MF_NOSKILL,
+ MF_NOWARP,MF_NOPVP,MF_NOICEWALL,MF_SNOW,MF_FOG,MF_SAKURA,MF_LEAVES,MF_RAIN,
+ MF_INDOORS,MF_NOGO,MF_CLOUDS,MF_CLOUDS2,MF_FIREWORKS,MF_GVG_CASTLE,MF_GVG_DUNGEON,MF_NIGHTENABLED,
+ MF_NOBASEEXP, MF_NOJOBEXP, MF_NOMOBLOOT, MF_NOMVPLOOT, MF_NORETURN, MF_NOWARPTO, MF_NIGHTMAREDROP
+ };
+
+int buildin_setmapflagnosave(struct script_state *st)
+{
+ int m,x,y;
+ unsigned short mapindex;
+ char *str,*str2;
+
+ str=conv_str(st,& (st->stack->stack_data[st->start+2]));
+ str2=conv_str(st,& (st->stack->stack_data[st->start+3]));
+ x=conv_num(st,& (st->stack->stack_data[st->start+4]));
+ y=conv_num(st,& (st->stack->stack_data[st->start+5]));
+ m = map_mapname2mapid(str);
+ mapindex = mapindex_name2id(str2);
+
+ if(m >= 0 && mapindex) {
+ map[m].flag.nosave=1;
+ map[m].save.map=mapindex;
+ map[m].save.x=x;
+ map[m].save.y=y;
+ }
+
+ return 0;
+}
+
+int buildin_setmapflag(struct script_state *st)
+{
+ int m,i;
+ char *str;
+
+ str=conv_str(st,& (st->stack->stack_data[st->start+2]));
+ i=conv_num(st,& (st->stack->stack_data[st->start+3]));
+ m = map_mapname2mapid(str);
+ if(m >= 0) {
+ switch(i) {
+ case MF_NOMEMO:
+ map[m].flag.nomemo=1;
+ break;
+ case MF_NOTELEPORT:
+ map[m].flag.noteleport=1;
+ break;
+ case MF_NOBRANCH:
+ map[m].flag.nobranch=1;
+ break;
+ case MF_NOPENALTY:
+ map[m].flag.nopenalty=1;
+ break;
+ case MF_NOZENYPENALTY:
+ map[m].flag.nozenypenalty=1;
+ break;
+ case MF_PVP:
+ map[m].flag.pvp=1;
+ break;
+ case MF_PVP_NOPARTY:
+ map[m].flag.pvp_noparty=1;
+ break;
+ case MF_PVP_NOGUILD:
+ map[m].flag.pvp_noguild=1;
+ break;
+ case MF_GVG:
+ map[m].flag.gvg=1;
+ break;
+ case MF_GVG_NOPARTY:
+ map[m].flag.gvg_noparty=1;
+ break;
+ case MF_GVG_DUNGEON:
+ map[m].flag.gvg_dungeon=1;
+ break;
+ case MF_GVG_CASTLE:
+ map[m].flag.gvg_castle=1;
+ break;
+ case MF_NOTRADE:
+ map[m].flag.notrade=1;
+ break;
+ case MF_NOSKILL:
+ map[m].flag.noskill=1;
+ break;
+ case MF_NOWARP:
+ map[m].flag.nowarp=1;
+ break;
+ case MF_NOPVP:
+ map[m].flag.nopvp=1;
+ break;
+ case MF_NOICEWALL: // [Valaris]
+ map[m].flag.noicewall=1;
+ break;
+ case MF_SNOW: // [Valaris]
+ map[m].flag.snow=1;
+ break;
+ case MF_CLOUDS:
+ map[m].flag.clouds=1;
+ break;
+ case MF_CLOUDS2: // [Valaris]
+ map[m].flag.clouds2=1;
+ break;
+ case MF_FOG: // [Valaris]
+ map[m].flag.fog=1;
+ break;
+ case MF_FIREWORKS:
+ map[m].flag.fireworks=1;
+ break;
+ case MF_SAKURA: // [Valaris]
+ map[m].flag.sakura=1;
+ break;
+ case MF_LEAVES: // [Valaris]
+ map[m].flag.leaves=1;
+ break;
+ case MF_RAIN: // [Valaris]
+ map[m].flag.rain=1;
+ break;
+ case MF_INDOORS: // celest
+ map[m].flag.indoors=1;
+ break;
+ case MF_NIGHTENABLED:
+ map[m].flag.nightenabled=1;
+ break;
+ case MF_NOGO: // celest
+ map[m].flag.nogo=1;
+ break;
+ case MF_NOBASEEXP:
+ map[m].flag.nobaseexp=1;
+ break;
+ case MF_NOJOBEXP:
+ map[m].flag.nojobexp=1;
+ break;
+ case MF_NOMOBLOOT:
+ map[m].flag.nomobloot=1;
+ break;
+ case MF_NOMVPLOOT:
+ map[m].flag.nomvploot=1;
+ break;
+ case MF_NORETURN:
+ map[m].flag.noreturn=1;
+ break;
+ case MF_NOWARPTO:
+ map[m].flag.nowarpto=1;
+ break;
+ case MF_NIGHTMAREDROP:
+ map[m].flag.pvp_nightmaredrop=1;
+ break;
+ }
+ }
+
+ return 0;
+}
+
+int buildin_removemapflag(struct script_state *st)
+{
+ int m,i;
+ char *str;
+
+ str=conv_str(st,& (st->stack->stack_data[st->start+2]));
+ i=conv_num(st,& (st->stack->stack_data[st->start+3]));
+ m = map_mapname2mapid(str);
+ if(m >= 0) {
+ switch(i) {
+ case MF_NOMEMO:
+ map[m].flag.nomemo=0;
+ break;
+ case MF_NOTELEPORT:
+ map[m].flag.noteleport=0;
+ break;
+ case MF_NOSAVE:
+ map[m].flag.nosave=0;
+ break;
+ case MF_NOBRANCH:
+ map[m].flag.nobranch=0;
+ break;
+ case MF_NOPENALTY:
+ map[m].flag.nopenalty=0;
+ break;
+ case MF_PVP:
+ map[m].flag.pvp=0;
+ break;
+ case MF_PVP_NOPARTY:
+ map[m].flag.pvp_noparty=0;
+ break;
+ case MF_PVP_NOGUILD:
+ map[m].flag.pvp_noguild=0;
+ break;
+ case MF_GVG:
+ map[m].flag.gvg=0;
+ break;
+ case MF_GVG_NOPARTY:
+ map[m].flag.gvg_noparty=0;
+ break;
+ case MF_GVG_DUNGEON:
+ map[m].flag.gvg_dungeon=0;
+ break;
+ case MF_GVG_CASTLE:
+ map[m].flag.gvg_castle=0;
+ break;
+ case MF_NOZENYPENALTY:
+ map[m].flag.nozenypenalty=0;
+ break;
+ case MF_NOSKILL:
+ map[m].flag.noskill=0;
+ break;
+ case MF_NOWARP:
+ map[m].flag.nowarp=0;
+ break;
+ case MF_NOPVP:
+ map[m].flag.nopvp=0;
+ break;
+ case MF_NOICEWALL: // [Valaris]
+ map[m].flag.noicewall=0;
+ break;
+ case MF_SNOW: // [Valaris]
+ map[m].flag.snow=0;
+ break;
+ case MF_CLOUDS:
+ map[m].flag.clouds=0;
+ break;
+ case MF_CLOUDS2: // [Valaris]
+ map[m].flag.clouds2=0;
+ break;
+ case MF_FOG: // [Valaris]
+ map[m].flag.fog=0;
+ break;
+ case MF_FIREWORKS:
+ map[m].flag.fireworks=0;
+ break;
+ case MF_SAKURA: // [Valaris]
+ map[m].flag.sakura=0;
+ break;
+ case MF_LEAVES: // [Valaris]
+ map[m].flag.leaves=0;
+ break;
+ case MF_RAIN: // [Valaris]
+ map[m].flag.rain=0;
+ break;
+ case MF_INDOORS: // celest
+ map[m].flag.indoors=0;
+ break;
+ case MF_NIGHTENABLED:
+ map[m].flag.nightenabled=0;
+ break;
+ case MF_NOGO: // celest
+ map[m].flag.nogo=0;
+ break;
+ case MF_NOBASEEXP:
+ map[m].flag.nobaseexp=0;
+ break;
+ case MF_NOJOBEXP:
+ map[m].flag.nojobexp=0;
+ break;
+ case MF_NOMOBLOOT:
+ map[m].flag.nomobloot=0;
+ break;
+ case MF_NOMVPLOOT:
+ map[m].flag.nomvploot=0;
+ break;
+ case MF_NORETURN:
+ map[m].flag.noreturn=0;
+ break;
+ case MF_NOWARPTO:
+ map[m].flag.nowarpto=0;
+ break;
+ case MF_NIGHTMAREDROP:
+ map[m].flag.pvp_nightmaredrop=0;
+ break;
+ }
+ }
+
+ return 0;
+}
+
+int buildin_pvpon(struct script_state *st)
+{
+ int m,i,users;
+ char *str;
+ struct map_session_data *pl_sd=NULL, **pl_allsd;
+
+ str=conv_str(st,& (st->stack->stack_data[st->start+2]));
+ m = map_mapname2mapid(str);
+ if(m >= 0 && !map[m].flag.pvp && !map[m].flag.nopvp) {
+ map[m].flag.pvp = 1;
+ clif_send0199(m,1);
+
+ if(battle_config.pk_mode) // disable ranking functions if pk_mode is on [Valaris]
+ return 0;
+
+ pl_allsd = map_getallusers(&users);
+
+ for(i=0;i<users;i++)
+ {
+ if ((pl_sd = pl_allsd[i]) && m == pl_sd->bl.m && pl_sd->pvp_timer == -1)
+ {
+ pl_sd->pvp_timer=add_timer(gettick()+200,pc_calc_pvprank_timer,pl_sd->bl.id,0);
+ pl_sd->pvp_rank=0;
+ pl_sd->pvp_lastusers=0;
+ pl_sd->pvp_point=5;
+ pl_sd->pvp_won = 0;
+ pl_sd->pvp_lost = 0;
+ }
+ }
+ }
+ return 0;
+}
+
+int buildin_pvpoff(struct script_state *st)
+{
+ int m,i,users;
+ char *str;
+ struct map_session_data *pl_sd=NULL, **pl_allsd;
+
+ str=conv_str(st,& (st->stack->stack_data[st->start+2]));
+ m = map_mapname2mapid(str);
+ if(m >= 0 && map[m].flag.pvp && !map[m].flag.nopvp) { //fixed Lupus
+ map[m].flag.pvp = 0;
+ clif_send0199(m,0);
+
+ if(battle_config.pk_mode) // disable ranking options if pk_mode is on [Valaris]
+ return 0;
+
+ pl_allsd = map_getallusers(&users);
+
+ for(i=0;i<users;i++)
+ {
+ if((pl_sd=pl_allsd[i]) && m == pl_sd->bl.m)
+ {
+ clif_pvpset(pl_sd,0,0,2);
+ if(pl_sd->pvp_timer != -1) {
+ delete_timer(pl_sd->pvp_timer,pc_calc_pvprank_timer);
+ pl_sd->pvp_timer = -1;
+ }
+ }
+ }
+ }
+
+ return 0;
+}
+
+int buildin_gvgon(struct script_state *st)
+{
+ int m;
+ char *str;
+
+ str=conv_str(st,& (st->stack->stack_data[st->start+2]));
+ m = map_mapname2mapid(str);
+ if(m >= 0 && !map[m].flag.gvg) {
+ map[m].flag.gvg = 1;
+ clif_send0199(m,3);
+ }
+
+ return 0;
+}
+int buildin_gvgoff(struct script_state *st)
+{
+ int m;
+ char *str;
+
+ str=conv_str(st,& (st->stack->stack_data[st->start+2]));
+ m = map_mapname2mapid(str);
+ if(m >= 0 && map[m].flag.gvg) {
+ map[m].flag.gvg = 0;
+ clif_send0199(m,0);
+ }
+
+ return 0;
+}
+/*==========================================
+ * Shows an emoticon on top of the player/npc
+ * emotion emotion#, <target: 0 - NPC, 1 - PC>
+ *------------------------------------------
+ */
+//Optional second parameter added by [Skotlex]
+int buildin_emotion(struct script_state *st)
+{
+ int type;
+ int player=0;
+
+ type=conv_num(st,& (st->stack->stack_data[st->start+2]));
+ if(type < 0 || type > 100)
+ return 0;
+
+ if( st->end>st->start+3 )
+ player=conv_num(st,& (st->stack->stack_data[st->start+3]));
+
+ if (player) {
+ struct map_session_data *sd = script_rid2sd(st);
+ if (sd)
+ clif_emotion(&sd->bl,type);
+ } else
+ clif_emotion(map_id2bl(st->oid),type);
+ return 0;
+}
+
+int buildin_maprespawnguildid_sub(struct block_list *bl,va_list ap)
+{
+ int g_id=va_arg(ap,int);
+ int flag=va_arg(ap,int);
+ struct map_session_data *sd=NULL;
+ struct mob_data *md=NULL;
+
+ if(bl->type == BL_PC)
+ sd=(struct map_session_data*)bl;
+ if(bl->type == BL_MOB)
+ md=(struct mob_data *)bl;
+
+ if(sd){
+ if((sd->status.guild_id == g_id) && (flag&1))
+ pc_setpos(sd,sd->status.save_point.map,sd->status.save_point.x,sd->status.save_point.y,3);
+ else if((sd->status.guild_id != g_id) && (flag&2))
+ pc_setpos(sd,sd->status.save_point.map,sd->status.save_point.x,sd->status.save_point.y,3);
+ else if (sd->status.guild_id == 0) // Warp out players not in guild [Valaris]
+ pc_setpos(sd,sd->status.save_point.map,sd->status.save_point.x,sd->status.save_point.y,3); // end addition [Valaris]
+ }
+ if(md && flag&4){
+ if(!md->guardian_data && md->class_ != MOBID_EMPERIUM)
+ mob_delete(md);
+ }
+ return 0;
+}
+int buildin_maprespawnguildid(struct script_state *st)
+{
+ char *mapname=conv_str(st,& (st->stack->stack_data[st->start+2]));
+ int g_id=conv_num(st,& (st->stack->stack_data[st->start+3]));
+ int flag=conv_num(st,& (st->stack->stack_data[st->start+4]));
+
+ int m=map_mapname2mapid(mapname);
+
+ if(m) map_foreachinmap(buildin_maprespawnguildid_sub,m,BL_CHAR,g_id,flag);
+ return 0;
+}
+
+int buildin_agitstart(struct script_state *st)
+{
+ if(agit_flag==1) return 0; // Agit already Start.
+ agit_flag=1;
+ guild_agit_start();
+ return 0;
+}
+
+int buildin_agitend(struct script_state *st)
+{
+ if(agit_flag==0) return 0; // Agit already End.
+ agit_flag=0;
+ guild_agit_end();
+ return 0;
+}
+/*==========================================
+ * agitcheck 1; // choice script
+ * if(@agit_flag == 1) goto agit;
+ * if(agitcheck(0) == 1) goto agit;
+ *------------------------------------------
+ */
+int buildin_agitcheck(struct script_state *st)
+{
+ struct map_session_data *sd;
+ int cond;
+
+ cond=conv_num(st,& (st->stack->stack_data[st->start+2]));
+
+ if(cond == 0) {
+ if (agit_flag==1) push_val(st->stack,C_INT,1);
+ if (agit_flag==0) push_val(st->stack,C_INT,0);
+ } else {
+ sd=script_rid2sd(st);
+ if (agit_flag==1) pc_setreg(sd,add_str((unsigned char *) "@agit_flag"),1);
+ if (agit_flag==0) pc_setreg(sd,add_str((unsigned char *) "@agit_flag"),0);
+ }
+ return 0;
+}
+int buildin_flagemblem(struct script_state *st)
+{
+ int g_id=conv_num(st,& (st->stack->stack_data[st->start+2]));
+
+ if(g_id < 0) return 0;
+
+// printf("Script.c: [FlagEmblem] GuildID=%d, Emblem=%d.\n", g->guild_id, g->emblem_id);
+ ((struct npc_data *)map_id2bl(st->oid))->u.scr.guild_id = g_id;
+ return 0;
+}
+
+int buildin_getcastlename(struct script_state *st)
+{
+ char *mapname=conv_str(st,& (st->stack->stack_data[st->start+2]));
+ struct guild_castle *gc;
+ int i;
+ char *buf=NULL;
+ for(i=0;i<MAX_GUILDCASTLE;i++){
+ if( (gc=guild_castle_search(i)) != NULL ){
+ if(strcmp(mapname,gc->map_name)==0){
+ buf=(char *)aCallocA(NAME_LENGTH,sizeof(char));
+ memcpy(buf, gc->castle_name, NAME_LENGTH-1);
+ break;
+ }
+ }
+ }
+ if(buf)
+ push_str(st->stack,C_STR,(unsigned char *) buf);
+ else
+ push_str(st->stack,C_CONSTSTR,(unsigned char *) "");
+ return 0;
+}
+
+int buildin_getcastledata(struct script_state *st)
+{
+ char *mapname=conv_str(st,& (st->stack->stack_data[st->start+2]));
+ int index=conv_num(st,& (st->stack->stack_data[st->start+3]));
+ char *event=NULL;
+ struct guild_castle *gc;
+ int i,j;
+
+ if( st->end>st->start+4 && index==0){
+ for(i=0,j=-1;i<MAX_GUILDCASTLE;i++)
+ if( (gc=guild_castle_search(i)) != NULL &&
+ strcmp(mapname,gc->map_name)==0 )
+ j=i;
+ if(j>=0){
+ event=conv_str(st,& (st->stack->stack_data[st->start+4]));
+ guild_addcastleinfoevent(j,17,event);
+ }
+ }
+
+ for(i=0;i<MAX_GUILDCASTLE;i++){
+ if( (gc=guild_castle_search(i)) != NULL ){
+ if(strcmp(mapname,gc->map_name)==0){
+ switch(index){
+ case 0: for(j=1;j<26;j++) guild_castledataload(gc->castle_id,j); break; // Initialize[AgitInit]
+ case 1: push_val(st->stack,C_INT,gc->guild_id); break;
+ case 2: push_val(st->stack,C_INT,gc->economy); break;
+ case 3: push_val(st->stack,C_INT,gc->defense); break;
+ case 4: push_val(st->stack,C_INT,gc->triggerE); break;
+ case 5: push_val(st->stack,C_INT,gc->triggerD); break;
+ case 6: push_val(st->stack,C_INT,gc->nextTime); break;
+ case 7: push_val(st->stack,C_INT,gc->payTime); break;
+ case 8: push_val(st->stack,C_INT,gc->createTime); break;
+ case 9: push_val(st->stack,C_INT,gc->visibleC); break;
+ case 10:
+ case 11:
+ case 12:
+ case 13:
+ case 14:
+ case 15:
+ case 16:
+ case 17:
+ push_val(st->stack,C_INT,gc->guardian[index-10].visible); break;
+ case 18:
+ case 19:
+ case 20:
+ case 21:
+ case 22:
+ case 23:
+ case 24:
+ case 25:
+ push_val(st->stack,C_INT,gc->guardian[index-18].hp); break;
+ default:
+ push_val(st->stack,C_INT,0); break;
+ }
+ return 0;
+ }
+ }
+ }
+ push_val(st->stack,C_INT,0);
+ return 0;
+}
+
+int buildin_setcastledata(struct script_state *st)
+{
+ char *mapname=conv_str(st,& (st->stack->stack_data[st->start+2]));
+ int index=conv_num(st,& (st->stack->stack_data[st->start+3]));
+ int value=conv_num(st,& (st->stack->stack_data[st->start+4]));
+ struct guild_castle *gc;
+ int i;
+
+ for(i=0;i<MAX_GUILDCASTLE;i++){
+ if( (gc=guild_castle_search(i)) != NULL ){
+ if(strcmp(mapname,gc->map_name)==0){
+ // Save Data byself First
+ switch(index){
+ case 1: gc->guild_id = value; break;
+ case 2: gc->economy = value; break;
+ case 3: gc->defense = value; break;
+ case 4: gc->triggerE = value; break;
+ case 5: gc->triggerD = value; break;
+ case 6: gc->nextTime = value; break;
+ case 7: gc->payTime = value; break;
+ case 8: gc->createTime = value; break;
+ case 9: gc->visibleC = value; break;
+ case 10:
+ case 11:
+ case 12:
+ case 13:
+ case 14:
+ case 15:
+ case 16:
+ case 17:
+ gc->guardian[index-10].visible = value; break;
+ case 18:
+ case 19:
+ case 20:
+ case 21:
+ case 22:
+ case 23:
+ case 24:
+ case 25:
+ gc->guardian[index-18].hp = value; break;
+ default: return 0;
+ }
+ guild_castledatasave(gc->castle_id,index,value);
+ return 0;
+ }
+ }
+ }
+ return 0;
+}
+
+/* =====================================================================
+ * ギルド情報を要求する
+ * ---------------------------------------------------------------------
+ */
+int buildin_requestguildinfo(struct script_state *st)
+{
+ int guild_id=conv_num(st,& (st->stack->stack_data[st->start+2]));
+ char *event=NULL;
+
+ if( st->end>st->start+3 )
+ event=conv_str(st,& (st->stack->stack_data[st->start+3]));
+
+ if(guild_id>0)
+ guild_npc_request_info(guild_id,event);
+ return 0;
+}
+
+/* =====================================================================
+ * カードの数を得る
+ * ---------------------------------------------------------------------
+ */
+int buildin_getequipcardcnt(struct script_state *st)
+{
+ int i,num;
+ struct map_session_data *sd;
+ int c=MAX_SLOTS;
+
+ num=conv_num(st,& (st->stack->stack_data[st->start+2]));
+ sd=script_rid2sd(st);
+ i=pc_checkequip(sd,equip[num-1]);
+ if(sd->status.inventory[i].card[0] == 0x00ff){ // 製造武器はカードなし
+ push_val(st->stack,C_INT,0);
+ return 0;
+ }
+ do{
+ if( (sd->status.inventory[i].card[c-1] > 4000 &&
+ sd->status.inventory[i].card[c-1] < 5000) ||
+ itemdb_type(sd->status.inventory[i].card[c-1]) == 6){ // [Celest]
+ push_val(st->stack,C_INT,(c));
+ return 0;
+ }
+ }while(c--);
+ push_val(st->stack,C_INT,0);
+ return 0;
+}
+
+/* ================================================================
+ * カード取り外し成功
+ * ----------------------------------------------------------------
+ */
+int buildin_successremovecards(struct script_state *st)
+{
+ int i,j,num,cardflag=0,flag;
+ struct map_session_data *sd;
+ struct item item_tmp;
+ int c=MAX_SLOTS;
+
+ num=conv_num(st,& (st->stack->stack_data[st->start+2]));
+ sd=script_rid2sd(st);
+ i=pc_checkequip(sd,equip[num-1]);
+ if(sd->status.inventory[i].card[0]==0x00ff){ // 製造武器は処理しない
+ return 0;
+ }
+ do{
+ if( (sd->status.inventory[i].card[c-1] > 4000 &&
+ sd->status.inventory[i].card[c-1] < 5000) ||
+ itemdb_type(sd->status.inventory[i].card[c-1]) == 6){ // [Celest]
+
+ cardflag = 1;
+ item_tmp.id=0,item_tmp.nameid=sd->status.inventory[i].card[c-1];
+ item_tmp.equip=0,item_tmp.identify=1,item_tmp.refine=0;
+ item_tmp.attribute=0;
+ for (j = 0; j < MAX_SLOTS; j++)
+ item_tmp.card[j]=0;
+
+ //Logs items, got from (N)PC scripts [Lupus]
+ if(log_config.pick > 0 ) {
+ log_pick(sd, "N", 0, item_tmp.nameid, 1, NULL);
+ }
+ //Logs
+
+ if((flag=pc_additem(sd,&item_tmp,1))){ // 持てないならドロップ
+ clif_additem(sd,0,0,flag);
+ map_addflooritem(&item_tmp,1,sd->bl.m,sd->bl.x,sd->bl.y,NULL,NULL,NULL,0);
+ }
+ }
+ }while(c--);
+
+ if(cardflag == 1){ // カードを取り除いたアイテム所得
+ flag=0;
+ item_tmp.id=0,item_tmp.nameid=sd->status.inventory[i].nameid;
+ item_tmp.equip=0,item_tmp.identify=1,item_tmp.refine=sd->status.inventory[i].refine;
+ item_tmp.attribute=sd->status.inventory[i].attribute;
+
+ //Logs items, got from (N)PC scripts [Lupus]
+ if(log_config.pick > 0 ) {
+ log_pick(sd, "N", 0, sd->status.inventory[i].nameid, -1, &sd->status.inventory[i]);
+ }
+ //Logs
+
+ for (j = 0; j < MAX_SLOTS; j++)
+ item_tmp.card[j]=0;
+ pc_delitem(sd,i,1,0);
+
+ //Logs items, got from (N)PC scripts [Lupus]
+ if(log_config.pick > 0 ) {
+ log_pick(sd, "N", 0, item_tmp.nameid, 1, &item_tmp);
+ }
+ //Logs
+
+ if((flag=pc_additem(sd,&item_tmp,1))){ // もてないならドロップ
+ clif_additem(sd,0,0,flag);
+ map_addflooritem(&item_tmp,1,sd->bl.m,sd->bl.x,sd->bl.y,NULL,NULL,NULL,0);
+ }
+ clif_misceffect(&sd->bl,3);
+ return 0;
+ }
+ return 0;
+}
+
+/* ================================================================
+ * カード取り外し失敗 slot,type
+ * type=0: 両方損失、1:カード損失、2:武具損失、3:損失無し
+ * ----------------------------------------------------------------
+ */
+int buildin_failedremovecards(struct script_state *st)
+{
+ int i,j,num,cardflag=0,flag,typefail;
+ struct map_session_data *sd;
+ struct item item_tmp;
+ int c=MAX_SLOTS;
+
+ num=conv_num(st,& (st->stack->stack_data[st->start+2]));
+ typefail=conv_num(st,& (st->stack->stack_data[st->start+3]));
+ sd=script_rid2sd(st);
+ i=pc_checkequip(sd,equip[num-1]);
+ if(sd->status.inventory[i].card[0]==0x00ff){ // 製造武器は処理しない
+ return 0;
+ }
+ do{
+ if( (sd->status.inventory[i].card[c-1] > 4000 &&
+ sd->status.inventory[i].card[c-1] < 5000) ||
+ itemdb_type(sd->status.inventory[i].card[c-1]) == 6){ // [Celest]
+
+ cardflag = 1;
+
+ if(typefail == 2){ // 武具のみ損失なら、カードは受け取らせる
+ item_tmp.id=0,item_tmp.nameid=sd->status.inventory[i].card[c-1];
+ item_tmp.equip=0,item_tmp.identify=1,item_tmp.refine=0;
+ item_tmp.attribute=0;
+ for (j = 0; j < MAX_SLOTS; j++)
+ item_tmp.card[j]=0;
+
+ //Logs items, got from (N)PC scripts [Lupus]
+ if(log_config.pick > 0 ) {
+ log_pick(sd, "N", 0, item_tmp.nameid, 1, NULL);
+ }
+ //Logs
+
+ if((flag=pc_additem(sd,&item_tmp,1))){
+ clif_additem(sd,0,0,flag);
+ map_addflooritem(&item_tmp,1,sd->bl.m,sd->bl.x,sd->bl.y,NULL,NULL,NULL,0);
+ }
+ }
+ }
+ }while(c--);
+
+ if(cardflag == 1){
+
+ if(typefail == 0 || typefail == 2){ // 武具損失
+ //Logs items, got from (N)PC scripts [Lupus]
+ if(log_config.pick > 0 ) {
+ log_pick(sd, "N", 0, sd->status.inventory[i].nameid, -1, &sd->status.inventory[i]);
+ }
+ //Logs
+
+ pc_delitem(sd,i,1,0);
+ clif_misceffect(&sd->bl,2);
+ return 0;
+ }
+ if(typefail == 1){ // カードのみ損失(武具を返す)
+ flag=0;
+ item_tmp.id=0,item_tmp.nameid=sd->status.inventory[i].nameid;
+ item_tmp.equip=0,item_tmp.identify=1,item_tmp.refine=sd->status.inventory[i].refine;
+ item_tmp.attribute=sd->status.inventory[i].attribute;
+
+ //Logs items, got from (N)PC scripts [Lupus]
+ if(log_config.pick > 0 ) {
+ log_pick(sd, "N", 0, sd->status.inventory[i].nameid, -1, &sd->status.inventory[i]);
+ }
+ //Logs
+
+ for (j = 0; j < MAX_SLOTS; j++)
+ item_tmp.card[j]=0;
+ pc_delitem(sd,i,1,0);
+
+ //Logs items, got from (N)PC scripts [Lupus]
+ if(log_config.pick > 0 ) {
+ log_pick(sd, "N", 0, item_tmp.nameid, 1, &item_tmp);
+ }
+ //Logs
+
+ if((flag=pc_additem(sd,&item_tmp,1))){
+ clif_additem(sd,0,0,flag);
+ map_addflooritem(&item_tmp,1,sd->bl.m,sd->bl.x,sd->bl.y,NULL,NULL,NULL,0);
+ }
+ }
+ clif_misceffect(&sd->bl,2);
+ return 0;
+ }
+ return 0;
+}
+
+int buildin_mapwarp(struct script_state *st) // Added by RoVeRT
+{
+ int x,y,m;
+ char *str;
+ char *mapname;
+ int x0,y0,x1,y1;
+
+ mapname=conv_str(st,& (st->stack->stack_data[st->start+2]));
+ x0=0;
+ y0=0;
+ x1=map[map_mapname2mapid(mapname)].xs;
+ y1=map[map_mapname2mapid(mapname)].ys;
+ str=conv_str(st,& (st->stack->stack_data[st->start+3]));
+ x=conv_num(st,& (st->stack->stack_data[st->start+4]));
+ y=conv_num(st,& (st->stack->stack_data[st->start+5]));
+
+ if( (m=map_mapname2mapid(mapname))< 0)
+ return 0;
+
+ map_foreachinarea(buildin_areawarp_sub,
+ m,x0,y0,x1,y1,BL_PC, str,x,y );
+ return 0;
+}
+
+int buildin_cmdothernpc(struct script_state *st) // Added by RoVeRT
+{
+ char *npc,*command;
+
+ npc=conv_str(st,& (st->stack->stack_data[st->start+2]));
+ command=conv_str(st,& (st->stack->stack_data[st->start+3]));
+
+ npc_command(map_id2sd(st->rid),npc,command);
+ return 0;
+}
+
+int buildin_inittimer(struct script_state *st) // Added by RoVeRT
+{
+// struct npc_data *nd=(struct npc_data*)map_id2bl(st->oid);
+// nd->lastaction=nd->timer=gettick();
+
+ npc_do_ontimer(st->oid, 1);
+
+ return 0;
+}
+
+int buildin_stoptimer(struct script_state *st) // Added by RoVeRT
+{
+// struct npc_data *nd=(struct npc_data*)map_id2bl(st->oid);
+// nd->lastaction=nd->timer=-1;
+
+ npc_do_ontimer(st->oid, 0);
+
+ return 0;
+}
+
+int buildin_mobcount_sub(struct block_list *bl,va_list ap) // Added by RoVeRT
+{
+ char *event=va_arg(ap,char *);
+ int *c=va_arg(ap,int *);
+
+ if(strcmp(event,((struct mob_data *)bl)->npc_event)==0)
+ (*c)++;
+ return 0;
+}
+
+int buildin_mobcount(struct script_state *st) // Added by RoVeRT
+{
+ char *mapname,*event;
+ int m,c=0;
+ mapname=conv_str(st,& (st->stack->stack_data[st->start+2]));
+ event=conv_str(st,& (st->stack->stack_data[st->start+3]));
+
+ if( (m=map_mapname2mapid(mapname))<0 ) {
+ push_val(st->stack,C_INT,-1);
+ return 0;
+ }
+ map_foreachinmap(buildin_mobcount_sub, m, BL_MOB, event,&c );
+
+ push_val(st->stack,C_INT, (c));
+
+ return 0;
+}
+int buildin_marriage(struct script_state *st)
+{
+ char *partner=conv_str(st,& (st->stack->stack_data[st->start+2]));
+ struct map_session_data *sd=script_rid2sd(st);
+ struct map_session_data *p_sd=map_nick2sd(partner);
+
+ if(sd==NULL || p_sd==NULL || pc_marriage(sd,p_sd) < 0){
+ push_val(st->stack,C_INT,0);
+ return 0;
+ }
+ push_val(st->stack,C_INT,1);
+ return 0;
+}
+int buildin_wedding_effect(struct script_state *st)
+{
+ struct map_session_data *sd=script_rid2sd(st);
+ struct block_list *bl;
+
+ if(sd==NULL) {
+ bl=map_id2bl(st->oid);
+ } else
+ bl=&sd->bl;
+ clif_wedding_effect(bl);
+ return 0;
+}
+int buildin_divorce(struct script_state *st)
+{
+ struct map_session_data *sd=script_rid2sd(st);
+ if(sd==NULL || pc_divorce(sd) < 0){
+ push_val(st->stack,C_INT,0);
+ return 0;
+ }
+ push_val(st->stack,C_INT,1);
+ return 0;
+}
+
+int buildin_ispartneron(struct script_state *st)
+{
+ struct map_session_data *sd=script_rid2sd(st);
+ struct map_session_data *p_sd=NULL;
+
+ if(sd==NULL || !pc_ismarried(sd) ||
+ (p_sd=map_charid2sd(sd->status.partner_id)) == NULL) {
+ push_val(st->stack,C_INT,0);
+ return 0;
+ }
+
+ push_val(st->stack,C_INT,1);
+ return 0;
+}
+
+int buildin_getpartnerid(struct script_state *st)
+{
+ struct map_session_data *sd=script_rid2sd(st);
+ if (sd == NULL) {
+ push_val(st->stack,C_INT,0);
+ return 0;
+ }
+
+ push_val(st->stack,C_INT,sd->status.partner_id);
+ return 0;
+}
+
+int buildin_getchildid(struct script_state *st)
+{
+ struct map_session_data *sd=script_rid2sd(st);
+ if (sd == NULL) {
+ push_val(st->stack,C_INT,0);
+ return 0;
+ }
+
+ push_val(st->stack,C_INT,sd->status.child);
+ return 0;
+}
+
+int buildin_getmotherid(struct script_state *st)
+{
+ struct map_session_data *sd=script_rid2sd(st);
+ if (sd == NULL) {
+ push_val(st->stack,C_INT,0);
+ return 0;
+ }
+
+ push_val(st->stack,C_INT,sd->status.mother);
+ return 0;
+}
+
+int buildin_getfatherid(struct script_state *st)
+{
+ struct map_session_data *sd=script_rid2sd(st);
+ if (sd == NULL) {
+ push_val(st->stack,C_INT,0);
+ return 0;
+ }
+
+ push_val(st->stack,C_INT,sd->status.father);
+ return 0;
+}
+
+int buildin_warppartner(struct script_state *st)
+{
+ int x,y;
+ unsigned short mapindex;
+ char *str;
+ struct map_session_data *sd=script_rid2sd(st);
+ struct map_session_data *p_sd=NULL;
+
+ if(sd==NULL || !pc_ismarried(sd) ||
+ (p_sd=map_charid2sd(sd->status.partner_id)) == NULL) {
+ push_val(st->stack,C_INT,0);
+ return 0;
+ }
+
+ str=conv_str(st,& (st->stack->stack_data[st->start+2]));
+ x=conv_num(st,& (st->stack->stack_data[st->start+3]));
+ y=conv_num(st,& (st->stack->stack_data[st->start+4]));
+
+ mapindex = mapindex_name2id(str);
+ if (mapindex) {
+ pc_setpos(p_sd,mapindex,x,y,0);
+ push_val(st->stack,C_INT,1);
+ } else
+ push_val(st->stack,C_INT,0);
+ return 0;
+}
+
+/*================================================
+ * Script for Displaying MOB Information [Valaris]
+ *------------------------------------------------
+ */
+int buildin_strmobinfo(struct script_state *st)
+{
+
+ int num=conv_num(st,& (st->stack->stack_data[st->start+2]));
+ int class_=conv_num(st,& (st->stack->stack_data[st->start+3]));
+
+ if((class_>=0 && class_<=1000) || class_ >2000)
+ return 0;
+
+ switch (num) {
+ case 1:
+ {
+ char *buf;
+ buf=(char *) aCallocA(NAME_LENGTH, sizeof(char));
+// buf=mob_db(class_)->name;
+// for string assignments you would need to go for c++ [Shinomori]
+ memcpy(buf, mob_db(class_)->name, NAME_LENGTH-1);
+ push_str(st->stack,C_STR,(unsigned char *) buf);
+ break;
+ }
+ case 2:
+ {
+ char *buf;
+ buf=(char *) aCallocA(NAME_LENGTH, sizeof(char));
+// buf=mob_db(class_).jname;
+// for string assignments you would need to go for c++ [Shinomori]
+ memcpy(buf,mob_db(class_)->jname, NAME_LENGTH-1);
+ push_str(st->stack,C_STR,(unsigned char *) buf);
+ break;
+ }
+ case 3:
+ push_val(st->stack,C_INT,mob_db(class_)->lv);
+ break;
+ case 4:
+ push_val(st->stack,C_INT,mob_db(class_)->max_hp);
+ break;
+ case 5:
+ push_val(st->stack,C_INT,mob_db(class_)->max_sp);
+ break;
+ case 6:
+ push_val(st->stack,C_INT,mob_db(class_)->base_exp);
+ break;
+ case 7:
+ push_val(st->stack,C_INT,mob_db(class_)->job_exp);
+ break;
+ }
+ return 0;
+}
+
+/*==========================================
+ * Summon guardians [Valaris]
+ *------------------------------------------
+ */
+int buildin_guardian(struct script_state *st)
+{
+ int class_=0,amount=1,x=0,y=0,guardian=0;
+ char *str,*map,*event="";
+
+ map =conv_str(st,& (st->stack->stack_data[st->start+2]));
+ x =conv_num(st,& (st->stack->stack_data[st->start+3]));
+ y =conv_num(st,& (st->stack->stack_data[st->start+4]));
+ str =conv_str(st,& (st->stack->stack_data[st->start+5]));
+ class_=conv_num(st,& (st->stack->stack_data[st->start+6]));
+ amount=conv_num(st,& (st->stack->stack_data[st->start+7]));
+ event=conv_str(st,& (st->stack->stack_data[st->start+8]));
+ if( st->end>st->start+9 )
+ guardian=conv_num(st,& (st->stack->stack_data[st->start+9]));
+
+ mob_spawn_guardian(map_id2sd(st->rid),map,x,y,str,class_,amount,event,guardian);
+
+ return 0;
+}
+
+/*================================================
+ * Script for Displaying Guardian Info [Valaris]
+ *------------------------------------------------
+ */
+int buildin_guardianinfo(struct script_state *st)
+{
+ int guardian=conv_num(st,& (st->stack->stack_data[st->start+2]));
+ struct map_session_data *sd=script_rid2sd(st);
+ struct guild_castle *gc=guild_mapname2gc(map[sd->bl.m].name);
+
+ if (guardian < 0 || guardian >= MAX_GUARDIANS || gc==NULL)
+ {
+ push_val(st->stack,C_INT,-1);
+ return 0;
+ }
+
+ if(gc->guardian[guardian].visible)
+ push_val(st->stack,C_INT,gc->guardian[guardian].hp);
+ else push_val(st->stack,C_INT,-1);
+
+ return 0;
+}
+/*==========================================
+ * IDからItem名
+ *------------------------------------------
+ */
+int buildin_getitemname(struct script_state *st)
+{
+ int item_id=0;
+ struct item_data *i_data;
+ char *item_name;
+ struct script_data *data;
+
+ data=&(st->stack->stack_data[st->start+2]);
+ get_val(st,data);
+
+ if( data->type==C_STR || data->type==C_CONSTSTR ){
+ const char *name=conv_str(st,data);
+ struct item_data *item_data = itemdb_searchname(name);
+ if( item_data )
+ item_id=item_data->nameid;
+ }else
+ item_id=conv_num(st,data);
+
+ i_data = itemdb_exists(item_id);
+ if (i_data == NULL)
+ {
+ push_str(st->stack,C_CONSTSTR,(unsigned char *) "null");
+ return 0;
+ }
+ item_name=(char *)aCallocA(ITEM_NAME_LENGTH,sizeof(char));
+
+ memcpy(item_name, i_data->jname, ITEM_NAME_LENGTH-1);
+ push_str(st->stack,C_STR,(unsigned char *) item_name);
+ return 0;
+}
+/*==========================================
+ * Returns number of slots an item has. [Skotlex]
+ *------------------------------------------
+ */
+int buildin_getitemslots(struct script_state *st)
+{
+ int item_id;
+ struct item_data *i_data;
+
+ item_id=conv_num(st,& (st->stack->stack_data[st->start+2]));
+
+ i_data = itemdb_exists(item_id);
+
+ if (i_data)
+ push_val(st->stack,C_INT,i_data->slot);
+ else
+ push_val(st->stack,C_INT,-1);
+ return 0;
+}
+
+/*==========================================
+ * Returns some values of an item [Lupus]
+ * Price, Weight, etc...
+ getiteminfo(itemID,n), where n
+ 0 value_buy;
+ 1 value_sell;
+ 2 type;
+ 3 maxchance = Max drop chance of this item e.g. 1 = 0.01% , etc..
+ if = 0, then monsters don't drop it at all (rare or a quest item)
+ if = 10000, then this item is sold in NPC shops only
+ 4 sex;
+ 5 equip;
+ 6 weight;
+ 7 atk;
+ 8 def;
+ 9 range;
+ 10 slot;
+ 11 look;
+ 12 elv;
+ 13 wlv;
+ *------------------------------------------
+ */
+int buildin_getiteminfo(struct script_state *st)
+{
+ int item_id,n;
+ int *item_arr;
+ struct item_data *i_data;
+
+ item_id = conv_num(st,& (st->stack->stack_data[st->start+2]));
+ n = conv_num(st,& (st->stack->stack_data[st->start+3]));
+ i_data = itemdb_exists(item_id);
+
+ if (i_data && n>=0 && n<14) {
+ item_arr = (int*)&i_data->value_buy;
+ push_val(st->stack,C_INT,item_arr[n]);
+ } else
+ push_val(st->stack,C_INT,-1);
+ return 0;
+}
+
+/*==========================================
+ * Returns value from equipped item slot n [Lupus]
+ getequipcardid(num,slot)
+ where
+ num = eqip position slot
+ slot = 0,1,2,3 (Card Slot N)
+
+ This func returns CARD ID, 255,254,-255 (for card 0, if the item is produced)
+ it's useful when you want to check item cards or if it's signed
+ Useful for such quests as "Sign this refined item with players name" etc
+ Hat[0] +4 -> Player's Hat[0] +4
+ *------------------------------------------
+ */
+int buildin_getequipcardid(struct script_state *st)
+{
+ int i,num,slot;
+ struct map_session_data *sd;
+
+ num=conv_num(st,& (st->stack->stack_data[st->start+2]));
+ slot=conv_num(st,& (st->stack->stack_data[st->start+3]));
+ sd=script_rid2sd(st);
+ i=pc_checkequip(sd,equip[num-1]);
+ if(i >= 0 && slot>=0 && slot<4)
+ push_val(st->stack,C_INT,sd->status.inventory[i].card[slot]);
+ else
+ push_val(st->stack,C_INT,0);
+
+ return 0;
+}
+
+/*==========================================
+ * petskillbonus [Valaris] //Rewritten by [Skotlex]
+ *------------------------------------------
+ */
+
+int buildin_petskillbonus(struct script_state *st)
+{
+ struct pet_data *pd;
+
+ struct map_session_data *sd=script_rid2sd(st);
+
+ if(sd==NULL || sd->pd==NULL)
+ return 0;
+
+ pd=sd->pd;
+ if (pd->bonus)
+ { //Clear previous bonus
+ if (pd->bonus->timer != -1)
+ delete_timer(pd->bonus->timer, pet_skill_bonus_timer);
+ } else //init
+ pd->bonus = (struct pet_bonus *) aCalloc(1, sizeof(struct pet_bonus));
+
+ pd->bonus->type=conv_num(st,& (st->stack->stack_data[st->start+2]));
+ pd->bonus->val=conv_num(st,& (st->stack->stack_data[st->start+3]));
+ pd->bonus->duration=conv_num(st,& (st->stack->stack_data[st->start+4]));
+ pd->bonus->delay=conv_num(st,& (st->stack->stack_data[st->start+5]));
+
+ if (pd->state.skillbonus == -1)
+ pd->state.skillbonus=0; // waiting state
+
+ // wait for timer to start
+ if (battle_config.pet_equip_required && pd->equip == 0)
+ pd->bonus->timer=-1;
+ else
+ pd->bonus->timer=add_timer(gettick()+pd->bonus->delay*1000, pet_skill_bonus_timer, sd->bl.id, 0);
+
+ return 0;
+}
+
+/*==========================================
+ * pet looting [Valaris] //Rewritten by [Skotlex]
+ *------------------------------------------
+ */
+int buildin_petloot(struct script_state *st)
+{
+ int max;
+ struct pet_data *pd;
+ struct map_session_data *sd=script_rid2sd(st);
+
+ if(sd==NULL || sd->pd==NULL)
+ return 0;
+
+ max=conv_num(st,& (st->stack->stack_data[st->start+2]));
+
+ if(max < 1)
+ max = 1; //Let'em loot at least 1 item.
+ else if (max > MAX_PETLOOT_SIZE)
+ max = MAX_PETLOOT_SIZE;
+
+ pd = sd->pd;
+ if (pd->loot != NULL)
+ { //Release whatever was there already and reallocate memory
+ pet_lootitem_drop(pd, pd->msd);
+ aFree(pd->loot->item);
+ }
+ else
+ pd->loot = (struct pet_loot *)aCalloc(1, sizeof(struct pet_loot));
+
+ pd->loot->item = (struct item *)aCalloc(max,sizeof(struct item));
+ memset(pd->loot->item,0,max * sizeof(struct item));
+
+ pd->loot->max=max;
+ pd->loot->count = 0;
+ pd->loot->weight = 0;
+ pd->loot->timer = gettick();
+
+ return 0;
+}
+/*==========================================
+ * PCの所持品情報読み取り
+ *------------------------------------------
+ */
+int buildin_getinventorylist(struct script_state *st)
+{
+ struct map_session_data *sd=script_rid2sd(st);
+ unsigned char card_var[NAME_LENGTH];
+
+ int i,j=0,k;
+ if(!sd) return 0;
+ for(i=0;i<MAX_INVENTORY;i++){
+ if(sd->status.inventory[i].nameid > 0 && sd->status.inventory[i].amount > 0){
+ pc_setreg(sd,add_str((unsigned char *) "@inventorylist_id")+(j<<24),sd->status.inventory[i].nameid);
+ pc_setreg(sd,add_str((unsigned char *) "@inventorylist_amount")+(j<<24),sd->status.inventory[i].amount);
+ pc_setreg(sd,add_str((unsigned char *) "@inventorylist_equip")+(j<<24),sd->status.inventory[i].equip);
+ pc_setreg(sd,add_str((unsigned char *) "@inventorylist_refine")+(j<<24),sd->status.inventory[i].refine);
+ pc_setreg(sd,add_str((unsigned char *) "@inventorylist_identify")+(j<<24),sd->status.inventory[i].identify);
+ pc_setreg(sd,add_str((unsigned char *) "@inventorylist_attribute")+(j<<24),sd->status.inventory[i].attribute);
+ for (k = 0; k < MAX_SLOTS; k++)
+ {
+ sprintf(card_var, "@inventorylist_card%d",k+1);
+ pc_setreg(sd,add_str(card_var)+(j<<24),sd->status.inventory[i].card[k]);
+ }
+ j++;
+ }
+ }
+ pc_setreg(sd,add_str((unsigned char *) "@inventorylist_count"),j);
+ return 0;
+}
+
+int buildin_getskilllist(struct script_state *st)
+{
+ struct map_session_data *sd=script_rid2sd(st);
+ int i,j=0;
+ if(!sd) return 0;
+ for(i=0;i<MAX_SKILL;i++){
+ if(sd->status.skill[i].id > 0 && sd->status.skill[i].lv > 0){
+ pc_setreg(sd,add_str((unsigned char *) "@skilllist_id")+(j<<24),sd->status.skill[i].id);
+ pc_setreg(sd,add_str((unsigned char *)"@skilllist_lv")+(j<<24),sd->status.skill[i].lv);
+ pc_setreg(sd,add_str((unsigned char *)"@skilllist_flag")+(j<<24),sd->status.skill[i].flag);
+ j++;
+ }
+ }
+ pc_setreg(sd,add_str((unsigned char *) "@skilllist_count"),j);
+ return 0;
+}
+
+int buildin_clearitem(struct script_state *st)
+{
+ struct map_session_data *sd=script_rid2sd(st);
+ int i;
+ if(sd==NULL) return 0;
+ for (i=0; i<MAX_INVENTORY; i++) {
+ if (sd->status.inventory[i].amount) {
+
+ //Logs items, got from (N)PC scripts [Lupus]
+ if(log_config.pick > 0 ) {
+ log_pick(sd, "N", 0, sd->status.inventory[i].nameid, -sd->status.inventory[i].amount, &sd->status.inventory[i]);
+ }
+ //Logs
+
+ pc_delitem(sd, i, sd->status.inventory[i].amount, 0);
+ }
+ }
+ return 0;
+}
+
+/*==========================================
+ Disguise Player (returns Mob/NPC ID if success, 0 on fail) [Lupus]
+ *------------------------------------------
+ */
+int buildin_disguise(struct script_state *st)
+{
+ struct map_session_data *sd=script_rid2sd(st);
+ int id;
+
+ id = conv_num(st,& (st->stack->stack_data[st->start+2]));
+
+ if (!mobdb_checkid(id) && !npcdb_checkid(id)) {
+ push_val(st->stack,C_INT,0);
+ return 0;
+ }
+
+ pc_stop_walking(sd,0);
+ clif_clearchar(&sd->bl, 0);
+ sd->disguise = id;
+ sd->state.disguised = 1; // set to override items with disguise script [Valaris]
+ clif_changeoption(&sd->bl);
+ clif_spawnpc(sd);
+
+ push_val(st->stack,C_INT,id);
+ return 0;
+}
+
+/*==========================================
+ Undisguise Player (returns 1 if success, 0 on fail) [Lupus]
+ *------------------------------------------
+ */
+int buildin_undisguise(struct script_state *st)
+{
+ struct map_session_data *sd=script_rid2sd(st);
+
+ if (sd->disguise) {
+ pc_stop_walking(sd,0);
+ clif_clearchar(&sd->bl, 0);
+ sd->disguise = 0;
+ clif_changeoption(&sd->bl);
+ clif_spawnpc(sd);
+ push_val(st->stack,C_INT,0);
+ } else {
+ push_val(st->stack,C_INT,1);
+ }
+ return 0;
+}
+
+/*==========================================
+ * NPCクラスチェンジ
+ * classは変わりたいclass
+ * typeは通常0なのかな?
+ *------------------------------------------
+ */
+int buildin_classchange(struct script_state *st)
+{
+ int _class,type;
+ struct block_list *bl=map_id2bl(st->oid);
+
+ if(bl==NULL) return 0;
+
+ _class=conv_num(st,& (st->stack->stack_data[st->start+2]));
+ type=conv_num(st,& (st->stack->stack_data[st->start+3]));
+ clif_class_change(bl,_class,type);
+ return 0;
+}
+
+/*==========================================
+ * NPCから発生するエフェクト
+ *------------------------------------------
+ */
+int buildin_misceffect(struct script_state *st)
+{
+ int type;
+
+ type=conv_num(st,& (st->stack->stack_data[st->start+2]));
+ if(st->oid)
+ clif_misceffect2(map_id2bl(st->oid),type);
+ else{
+ struct map_session_data *sd=script_rid2sd(st);
+ if(sd)
+ clif_misceffect2(&sd->bl,type);
+ }
+ return 0;
+}
+/*==========================================
+ * サウンドエフェクト
+ *------------------------------------------
+ */
+int buildin_soundeffect(struct script_state *st)
+{
+
+ // Redundn
+ struct map_session_data *sd=script_rid2sd(st);
+ char *name;
+ int type=0;
+
+
+ name=conv_str(st,& (st->stack->stack_data[st->start+2]));
+ type=conv_num(st,& (st->stack->stack_data[st->start+3]));
+ if(sd){
+ if(st->oid)
+ clif_soundeffect(sd,map_id2bl(st->oid),name,type);
+ else{
+ clif_soundeffect(sd,&sd->bl,name,type);
+ }
+ }
+ return 0;
+}
+
+int buildin_soundeffectall(struct script_state *st)
+{
+ // [Lance] - Improved.
+ struct map_session_data *sd=NULL;
+ char *name;
+ int type=0;
+
+ name=conv_str(st,& (st->stack->stack_data[st->start+2]));
+ type=conv_num(st,& (st->stack->stack_data[st->start+3]));
+ //if(sd)
+ //{
+ if(st->oid)
+ clif_soundeffectall(map_id2bl(st->oid),name,type);
+ else
+ if((sd=script_rid2sd(st)))
+ clif_soundeffectall(&sd->bl,name,type);
+ //}
+ return 0;
+}
+/*==========================================
+ * pet status recovery [Valaris] / Rewritten by [Skotlex]
+ *------------------------------------------
+ */
+int buildin_petrecovery(struct script_state *st)
+{
+ struct pet_data *pd;
+ struct map_session_data *sd=script_rid2sd(st);
+
+ if(sd==NULL || sd->pd==NULL)
+ return 0;
+
+ pd=sd->pd;
+
+ if (pd->recovery)
+ { //Halt previous bonus
+ if (pd->recovery->timer != -1)
+ delete_timer(pd->recovery->timer, pet_recovery_timer);
+ } else //Init
+ pd->recovery = (struct pet_recovery *)aCalloc(1, sizeof(struct pet_recovery));
+
+ pd->recovery->type=conv_num(st,& (st->stack->stack_data[st->start+2]));
+ pd->recovery->delay=conv_num(st,& (st->stack->stack_data[st->start+3]));
+
+ pd->recovery->timer=-1;
+
+ return 0;
+}
+
+/*==========================================
+ * pet healing [Valaris] //Rewritten by [Skotlex]
+ *------------------------------------------
+ */
+int buildin_petheal(struct script_state *st)
+{
+ struct pet_data *pd;
+ struct map_session_data *sd=script_rid2sd(st);
+
+ if(sd==NULL || sd->pd==NULL)
+ return 0;
+
+ pd=sd->pd;
+ if (pd->s_skill)
+ { //Clear previous skill
+ if (pd->s_skill->timer != -1)
+ {
+ if (pd->s_skill->id)
+ delete_timer(pd->s_skill->timer, pet_skill_support_timer);
+ else
+ delete_timer(pd->s_skill->timer, pet_heal_timer);
+ }
+ } else //init memory
+ pd->s_skill = (struct pet_skill_support *) aCalloc(1, sizeof(struct pet_skill_support));
+
+ pd->s_skill->id=0; //This id identifies that it IS petheal rather than pet_skillsupport
+ //Use the lv as the amount to heal
+ pd->s_skill->lv=conv_num(st,& (st->stack->stack_data[st->start+2]));
+ pd->s_skill->delay=conv_num(st,& (st->stack->stack_data[st->start+3]));
+ pd->s_skill->hp=conv_num(st,& (st->stack->stack_data[st->start+4]));
+ pd->s_skill->sp=conv_num(st,& (st->stack->stack_data[st->start+5]));
+
+ //Use delay as initial offset to avoid skill/heal exploits
+ if (battle_config.pet_equip_required && pd->equip == 0)
+ pd->s_skill->timer=-1;
+ else
+ pd->s_skill->timer=add_timer(gettick()+pd->s_skill->delay*1000,pet_heal_timer,sd->bl.id,0);
+
+ return 0;
+}
+
+/*==========================================
+ * pet attack skills [Valaris] //Rewritten by [Skotlex]
+ *------------------------------------------
+ */
+int buildin_petskillattack(struct script_state *st)
+{
+ struct pet_data *pd;
+ struct map_session_data *sd=script_rid2sd(st);
+
+ if(sd==NULL || sd->pd==NULL)
+ return 0;
+
+ pd=sd->pd;
+ if (pd->a_skill == NULL)
+ pd->a_skill = (struct pet_skill_attack *)aCalloc(1, sizeof(struct pet_skill_attack));
+
+ pd->a_skill->id=conv_num(st,& (st->stack->stack_data[st->start+2]));
+ pd->a_skill->lv=conv_num(st,& (st->stack->stack_data[st->start+3]));
+ pd->a_skill->div_ = 0;
+ pd->a_skill->rate=conv_num(st,& (st->stack->stack_data[st->start+4]));
+ pd->a_skill->bonusrate=conv_num(st,& (st->stack->stack_data[st->start+5]));
+
+ return 0;
+}
+
+/*==========================================
+ * pet attack skills [Valaris]
+ *------------------------------------------
+ */
+int buildin_petskillattack2(struct script_state *st)
+{
+ struct pet_data *pd;
+ struct map_session_data *sd=script_rid2sd(st);
+
+ if(sd==NULL || sd->pd==NULL)
+ return 0;
+
+ pd=sd->pd;
+ if (pd->a_skill == NULL)
+ pd->a_skill = (struct pet_skill_attack *)aCalloc(1, sizeof(struct pet_skill_attack));
+
+ pd->a_skill->id=conv_num(st,& (st->stack->stack_data[st->start+2]));
+ pd->a_skill->lv=conv_num(st,& (st->stack->stack_data[st->start+3]));
+ pd->a_skill->div_ = conv_num(st,& (st->stack->stack_data[st->start+4]));
+ pd->a_skill->rate=conv_num(st,& (st->stack->stack_data[st->start+5]));
+ pd->a_skill->bonusrate=conv_num(st,& (st->stack->stack_data[st->start+6]));
+
+ return 0;
+}
+
+/*==========================================
+ * pet support skills [Skotlex]
+ *------------------------------------------
+ */
+int buildin_petskillsupport(struct script_state *st)
+{
+ struct pet_data *pd;
+ struct map_session_data *sd=script_rid2sd(st);
+
+ if(sd==NULL || sd->pd==NULL)
+ return 0;
+
+ pd=sd->pd;
+ if (pd->s_skill)
+ { //Clear previous skill
+ if (pd->s_skill->timer != -1)
+ {
+ if (pd->s_skill->id)
+ delete_timer(pd->s_skill->timer, pet_skill_support_timer);
+ else
+ delete_timer(pd->s_skill->timer, pet_heal_timer);
+ }
+ } else //init memory
+ pd->s_skill = (struct pet_skill_support *) aCalloc(1, sizeof(struct pet_skill_support));
+
+ pd->s_skill->id=conv_num(st,& (st->stack->stack_data[st->start+2]));
+ pd->s_skill->lv=conv_num(st,& (st->stack->stack_data[st->start+3]));
+ pd->s_skill->delay=conv_num(st,& (st->stack->stack_data[st->start+4]));
+ pd->s_skill->hp=conv_num(st,& (st->stack->stack_data[st->start+5]));
+ pd->s_skill->sp=conv_num(st,& (st->stack->stack_data[st->start+6]));
+
+ //Use delay as initial offset to avoid skill/heal exploits
+ if (battle_config.pet_equip_required && pd->equip == 0)
+ pd->s_skill->timer=-1;
+ else
+ pd->s_skill->timer=add_timer(gettick()+pd->s_skill->delay*1000,pet_skill_support_timer,sd->bl.id,0);
+
+ return 0;
+}
+
+/*==========================================
+ * Scripted skill effects [Celest]
+ *------------------------------------------
+ */
+int buildin_skilleffect(struct script_state *st)
+{
+ struct map_session_data *sd;
+
+ int skillid=conv_num(st,& (st->stack->stack_data[st->start+2]));
+ int skilllv=conv_num(st,& (st->stack->stack_data[st->start+3]));
+ sd=script_rid2sd(st);
+
+ clif_skill_nodamage(&sd->bl,&sd->bl,skillid,skilllv,1);
+
+ return 0;
+}
+
+/*==========================================
+ * NPC skill effects [Valaris]
+ *------------------------------------------
+ */
+int buildin_npcskilleffect(struct script_state *st)
+{
+ struct npc_data *nd=(struct npc_data *)map_id2bl(st->oid);
+
+ int skillid=conv_num(st,& (st->stack->stack_data[st->start+2]));
+ int skilllv=conv_num(st,& (st->stack->stack_data[st->start+3]));
+ int x=conv_num(st,& (st->stack->stack_data[st->start+4]));
+ int y=conv_num(st,& (st->stack->stack_data[st->start+5]));
+
+ clif_skill_poseffect(&nd->bl,skillid,skilllv,x,y,gettick());
+
+ return 0;
+}
+
+/*==========================================
+ * Special effects [Valaris]
+ *------------------------------------------
+ */
+int buildin_specialeffect(struct script_state *st)
+{
+ struct block_list *bl=map_id2bl(st->oid);
+
+ if(bl==NULL)
+ return 0;
+
+ clif_specialeffect(bl,conv_num(st,& (st->stack->stack_data[st->start+2])), 0);
+
+ return 0;
+}
+
+int buildin_specialeffect2(struct script_state *st)
+{
+ struct map_session_data *sd=script_rid2sd(st);
+
+ if(sd==NULL)
+ return 0;
+
+ clif_specialeffect(&sd->bl,conv_num(st,& (st->stack->stack_data[st->start+2])), 0);
+
+ return 0;
+}
+
+/*==========================================
+ * Nude [Valaris]
+ *------------------------------------------
+ */
+
+int buildin_nude(struct script_state *st)
+{
+ struct map_session_data *sd=script_rid2sd(st);
+ int i,calcflag=0;
+
+ if(sd==NULL)
+ return 0;
+
+ for(i=0;i<11;i++)
+ if(sd->equip_index[i] >= 0) {
+ if(!calcflag)
+ calcflag=1;
+ pc_unequipitem(sd,sd->equip_index[i],2);
+ }
+
+ if(calcflag)
+ status_calc_pc(sd,1);
+
+ return 0;
+}
+
+/*==========================================
+ * gmcommand [MouseJstr]
+ *
+ * suggested on the forums...
+ * splitted into atcommand & charcommand by [Skotlex]
+ *------------------------------------------
+ */
+
+int buildin_atcommand(struct script_state *st)
+{
+ struct map_session_data *sd;
+ char *cmd;
+
+ sd = script_rid2sd(st);
+ if (!sd)
+ return 0;
+ cmd = conv_str(st,& (st->stack->stack_data[st->start+2]));
+ is_atcommand(sd->fd, sd, cmd, 99);
+
+ return 0;
+}
+
+int buildin_charcommand(struct script_state *st)
+{
+ struct map_session_data *sd;
+ char *cmd;
+
+ sd = script_rid2sd(st);
+ if (!sd)
+ return 0;
+ cmd = conv_str(st,& (st->stack->stack_data[st->start+2]));
+ is_charcommand(sd->fd, sd, cmd, 99);
+
+ return 0;
+}
+
+
+/*==========================================
+ * Displays a message for the player only (like system messages like "you got an apple" )
+ *------------------------------------------
+ */
+int buildin_dispbottom(struct script_state *st)
+{
+ struct map_session_data *sd=script_rid2sd(st);
+ char *message;
+ message=conv_str(st,& (st->stack->stack_data[st->start+2]));
+ if(sd)
+ clif_disp_onlyself(sd,message,(int)strlen(message));
+ return 0;
+}
+
+/*==========================================
+ * All The Players Full Recovery
+ (HP/SP full restore and resurrect if need)
+ *------------------------------------------
+ */
+int buildin_recovery(struct script_state *st)
+{
+ struct map_session_data *sd, **all_sd;
+ int i = 0, users;
+
+ all_sd = map_getallusers(&users);
+
+ for (i = 0; i < users; i++)
+ {
+ sd = all_sd[i];
+ sd->status.hp = sd->status.max_hp;
+ sd->status.sp = sd->status.max_sp;
+ clif_updatestatus(sd, SP_HP);
+ clif_updatestatus(sd, SP_SP);
+ if(pc_isdead(sd)){
+ pc_setstand(sd);
+ clif_resurrection(&sd->bl, 1);
+ }
+ clif_displaymessage(sd->fd,"You have been recovered!");
+ }
+ return 0;
+}
+/*==========================================
+ * Get your pet info: getpetinfo(n)
+ * n -> 0:pet_id 1:pet_class 2:pet_name
+ 3:friendly 4:hungry
+ *------------------------------------------
+ */
+int buildin_getpetinfo(struct script_state *st)
+{
+ struct map_session_data *sd=script_rid2sd(st);
+ int type=conv_num(st,& (st->stack->stack_data[st->start+2]));
+
+ if(sd && sd->status.pet_id){
+ switch(type){
+ case 0:
+ push_val(st->stack,C_INT,sd->status.pet_id);
+ break;
+ case 1:
+ push_val(st->stack,C_INT,sd->pet.class_);
+ break;
+ case 2:
+ if(sd->pet.name)
+ { //Shamelessly copied from strcharinfo() [Skotlex]
+ char *buf;
+ buf=(char *)aCallocA(NAME_LENGTH,sizeof(char));
+ memcpy(buf, sd->pet.name, NAME_LENGTH-1);
+ push_str(st->stack,C_STR,(unsigned char *) buf);
+ }
+ else
+ push_str(st->stack,C_CONSTSTR, (unsigned char *) "null");
+ break;
+ case 3:
+ //if(sd->pet.intimate)
+ push_val(st->stack,C_INT,sd->pet.intimate);
+ break;
+ case 4:
+ //if(sd->pet.hungry)
+ push_val(st->stack,C_INT,sd->pet.hungry);
+ break;
+ default:
+ push_val(st->stack,C_INT,0);
+ break;
+ }
+ }else{
+ push_val(st->stack,C_INT,0);
+ }
+ return 0;
+}
+/*==========================================
+ * Shows wether your inventory(and equips) contain
+ selected card or not.
+ checkequipedcard(4001);
+ *------------------------------------------
+ */
+int buildin_checkequipedcard(struct script_state *st)
+{
+ struct map_session_data *sd=script_rid2sd(st);
+ int n,i,c=0;
+ c=conv_num(st,& (st->stack->stack_data[st->start+2]));
+
+ if(sd){
+ for(i=0;i<MAX_INVENTORY;i++){
+ if(sd->status.inventory[i].nameid > 0 && sd->status.inventory[i].amount){
+ for(n=0;n<MAX_SLOTS;n++){
+ if(sd->status.inventory[i].card[n]==c){
+ push_val(st->stack,C_INT,1);
+ return 0;
+ }
+ }
+ }
+ }
+ }
+ push_val(st->stack,C_INT,0);
+ return 0;
+}
+
+int buildin_jump_zero(struct script_state *st) {
+ int sel;
+ sel=conv_num(st,& (st->stack->stack_data[st->start+2]));
+ if(!sel) {
+ int pos;
+ if( st->stack->stack_data[st->start+3].type!=C_POS ){
+ ShowError("script: jump_zero: not label !\n");
+ st->state=END;
+ return 0;
+ }
+
+ pos=conv_num(st,& (st->stack->stack_data[st->start+3]));
+ st->pos=pos;
+ st->state=GOTO;
+ // printf("script: jump_zero: jumpto : %d\n",pos);
+ } else {
+ // printf("script: jump_zero: fail\n");
+ }
+ return 0;
+}
+
+int buildin_select(struct script_state *st)
+{
+ char *buf;
+ int len,i;
+ struct map_session_data *sd;
+
+ sd=script_rid2sd(st);
+
+ if(sd->state.menu_or_input==0){
+ st->state=RERUNLINE;
+ sd->state.menu_or_input=1;
+ for(i=st->start+2,len=16;i<st->end;i++){
+ conv_str(st,& (st->stack->stack_data[i]));
+ len+=(int)strlen(st->stack->stack_data[i].u.str)+1;
+ }
+ buf=(char *)aCalloc(len+1,sizeof(char));
+ buf[0]=0;
+ for(i=st->start+2,len=0;i<st->end;i++){
+ strcat(buf,st->stack->stack_data[i].u.str);
+ strcat(buf,":");
+ }
+ clif_scriptmenu(script_rid2sd(st),st->oid,buf);
+ aFree(buf);
+ } else if(sd->npc_menu==0xff){ // cansel
+ sd->state.menu_or_input=0;
+ st->state=END;
+ } else {
+// pc_setreg(sd,add_str((unsigned char *) "l15"),sd->npc_menu);
+ pc_setreg(sd,add_str((unsigned char *) "@menu"),sd->npc_menu);
+ sd->state.menu_or_input=0;
+ push_val(st->stack,C_INT,sd->npc_menu);
+ }
+ return 0;
+}
+
+/*==========================================
+ * GetMapMobs
+ returns mob counts on a set map:
+ e.g. GetMapMobs("prontera.gat")
+ use "this" - for player's map
+ *------------------------------------------
+ */
+int buildin_getmapmobs(struct script_state *st)
+{
+ char *str=NULL;
+ int m=-1,bx,by,i;
+ int count=0,c;
+ struct block_list *bl;
+
+ str=conv_str(st,& (st->stack->stack_data[st->start+2]));
+
+ if(strcmp(str,"this")==0){
+ struct map_session_data *sd=script_rid2sd(st);
+ if(sd)
+ m=sd->bl.m;
+ else{
+ push_val(st->stack,C_INT,-1);
+ return 0;
+ }
+ }else
+ m=map_mapname2mapid(str);
+
+ if(m < 0){
+ push_val(st->stack,C_INT,-1);
+ return 0;
+ }
+
+ for(by=0;by<=(map[m].ys-1)/BLOCK_SIZE;by++){
+ for(bx=0;bx<=(map[m].xs-1)/BLOCK_SIZE;bx++){
+ bl = map[m].block_mob[bx+by*map[m].bxs];
+ c = map[m].block_mob_count[bx+by*map[m].bxs];
+ for(i=0;i<c && bl;i++,bl=bl->next){
+ if(bl->x>=0 && bl->x<=map[m].xs-1 && bl->y>=0 && bl->y<=map[m].ys-1)
+ count++;
+ }
+ }
+ }
+ push_val(st->stack,C_INT,count);
+ return 0;
+}
+
+/*==========================================
+ * movenpc [MouseJstr]
+ *------------------------------------------
+ */
+
+int buildin_movenpc(struct script_state *st)
+{
+ struct map_session_data *sd;
+ char *map,*npc;
+ int x,y;
+
+ sd = script_rid2sd(st);
+
+ map = conv_str(st,& (st->stack->stack_data[st->start+2]));
+ x = conv_num(st,& (st->stack->stack_data[st->start+3]));
+ y = conv_num(st,& (st->stack->stack_data[st->start+4]));
+ npc = conv_str(st,& (st->stack->stack_data[st->start+5]));
+
+ return 0;
+}
+
+/*==========================================
+ * message [MouseJstr]
+ *------------------------------------------
+ */
+
+int buildin_message(struct script_state *st)
+{
+ struct map_session_data *sd;
+ char *msg,*player;
+ struct map_session_data *pl_sd = NULL;
+
+ sd = script_rid2sd(st);
+
+ player = conv_str(st,& (st->stack->stack_data[st->start+2]));
+ msg = conv_str(st,& (st->stack->stack_data[st->start+3]));
+
+ if((pl_sd=map_nick2sd((char *) player)) == NULL)
+ return 0;
+ clif_displaymessage(pl_sd->fd, msg);
+
+ return 0;
+}
+
+/*==========================================
+ * npctalk (sends message to surrounding
+ * area) [Valaris]
+ *------------------------------------------
+ */
+
+int buildin_npctalk(struct script_state *st)
+{
+ char *str;
+ char message[255];
+
+ struct npc_data *nd=(struct npc_data *)map_id2bl(st->oid);
+ str=conv_str(st,& (st->stack->stack_data[st->start+2]));
+
+ if(nd) {
+ memcpy(message, nd->name, NAME_LENGTH);
+ strcat(message," : ");
+ strncat(message,str, 254); //Prevent overflow possibility. [Skotlex]
+ clif_message(&(nd->bl), message);
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * hasitems (checks to see if player has any
+ * items on them, if so will return a 1)
+ * [Valaris]
+ *------------------------------------------
+ */
+
+int buildin_hasitems(struct script_state *st)
+{
+ int i;
+ struct map_session_data *sd;
+
+ sd=script_rid2sd(st);
+
+ for(i=0; i<MAX_INVENTORY; i++) {
+ if(sd->status.inventory[i].amount && sd->status.inventory[i].nameid!=2364 && sd->status.inventory[i].nameid!=2365) {
+ push_val(st->stack,C_INT,1);
+ return 0;
+ }
+ }
+
+ push_val(st->stack,C_INT,0);
+
+ return 0;
+}
+// change npc walkspeed [Valaris]
+int buildin_npcspeed(struct script_state *st)
+{
+ struct npc_data *nd=(struct npc_data *)map_id2bl(st->oid);
+ int x=0;
+
+ x=conv_num(st,& (st->stack->stack_data[st->start+2]));
+
+ if(nd) {
+ nd->speed=x;
+ }
+
+ return 0;
+}
+// make an npc walk to a position [Valaris]
+int buildin_npcwalkto(struct script_state *st)
+{
+ struct npc_data *nd=(struct npc_data *)map_id2bl(st->oid);
+ int x=0,y=0;
+
+ x=conv_num(st,& (st->stack->stack_data[st->start+2]));
+ y=conv_num(st,& (st->stack->stack_data[st->start+3]));
+
+ if(nd) {
+ npc_walktoxy(nd,x,y,0);
+ }
+
+ return 0;
+}
+// stop an npc's movement [Valaris]
+int buildin_npcstop(struct script_state *st)
+{
+ struct npc_data *nd=(struct npc_data *)map_id2bl(st->oid);
+
+ if(nd) {
+ if(nd->state.state==MS_WALK)
+ npc_stop_walking(nd,1);
+ }
+
+ return 0;
+}
+
+
+/*==========================================
+ * getlook char info. getlook(arg)
+ *------------------------------------------
+ */
+int buildin_getlook(struct script_state *st){
+ int type,val;
+ struct map_session_data *sd;
+ sd=script_rid2sd(st);
+
+ type=conv_num(st,& (st->stack->stack_data[st->start+2]));
+ val=-1;
+ switch(type){
+ case LOOK_HAIR: //1
+ val=sd->status.hair;
+ break;
+ case LOOK_WEAPON: //2
+ val=sd->status.weapon;
+ break;
+ case LOOK_HEAD_BOTTOM: //3
+ val=sd->status.head_bottom;
+ break;
+ case LOOK_HEAD_TOP: //4
+ val=sd->status.head_top;
+ break;
+ case LOOK_HEAD_MID: //5
+ val=sd->status.head_mid;
+ break;
+ case LOOK_HAIR_COLOR: //6
+ val=sd->status.hair_color;
+ break;
+ case LOOK_CLOTHES_COLOR: //7
+ val=sd->status.clothes_color;
+ break;
+ case LOOK_SHIELD: //8
+ val=sd->status.shield;
+ break;
+ case LOOK_SHOES: //9
+ break;
+ }
+
+ push_val(st->stack,C_INT,val);
+ return 0;
+}
+
+/*==========================================
+ * get char save point. argument: 0- map name, 1- x, 2- y
+ *------------------------------------------
+*/
+int buildin_getsavepoint(struct script_state *st)
+{
+ int x,y,type;
+ char *mapname;
+ struct map_session_data *sd;
+
+ sd=script_rid2sd(st);
+
+ type=conv_num(st,& (st->stack->stack_data[st->start+2]));
+
+ x=sd->status.save_point.x;
+ y=sd->status.save_point.y;
+ switch(type){
+ case 0:
+ mapname=(char *) aCallocA(MAP_NAME_LENGTH+1, sizeof(char));
+ memcpy(mapname, mapindex_id2name(sd->status.save_point.map), MAP_NAME_LENGTH);
+ mapname[MAP_NAME_LENGTH]='\0';
+ push_str(st->stack,C_STR,(unsigned char *) mapname);
+ break;
+ case 1:
+ push_val(st->stack,C_INT,x);
+ break;
+ case 2:
+ push_val(st->stack,C_INT,y);
+ break;
+ }
+ return 0;
+}
+
+/*==========================================
+ * Get position for char/npc/pet/mob objects. Added by Lorky
+ *
+ * int getMapXY(MapName$,MaxX,MapY,type,[CharName$]);
+ * where type:
+ * MapName$ - String variable for output map name
+ * MapX - Integer variable for output coord X
+ * MapY - Integer variable for output coord Y
+ * type - type of object
+ * 0 - Character coord
+ * 1 - NPC coord
+ * 2 - Pet coord
+ * 3 - Mob coord (not released)
+ * CharName$ - Name object. If miss or "this" the current object
+ *
+ * Return:
+ * 0 - success
+ * -1 - some error, MapName$,MapX,MapY contains unknown value.
+ *------------------------------------------
+*/
+int buildin_getmapxy(struct script_state *st){
+ struct map_session_data *sd=NULL;
+ struct npc_data *nd;
+ struct pet_data *pd;
+
+ int num;
+ char *name;
+ char prefix;
+
+ int x,y,type;
+ char mapname[MAP_NAME_LENGTH+1];
+ memset(mapname, 0, sizeof(mapname));
+
+ if( st->stack->stack_data[st->start+2].type!=C_NAME ){
+ ShowWarning("script: buildin_getmapxy: not mapname variable\n");
+ push_val(st->stack,C_INT,-1);
+ return 0;
+ }
+ if( st->stack->stack_data[st->start+3].type!=C_NAME ){
+ ShowWarning("script: buildin_getmapxy: not mapx variable\n");
+ push_val(st->stack,C_INT,-1);
+ return 0;
+ }
+ if( st->stack->stack_data[st->start+4].type!=C_NAME ){
+ ShowWarning("script: buildin_getmapxy: not mapy variable\n");
+ push_val(st->stack,C_INT,-1);
+ return 0;
+ }
+
+//??????????? >>> Possible needly check function parameters on C_STR,C_INT,C_INT <<< ???????????//
+ type=conv_num(st,& (st->stack->stack_data[st->start+5]));
+
+ switch (type){
+ case 0: //Get Character Position
+ if( st->end>st->start+6 )
+ sd=map_nick2sd(conv_str(st,& (st->stack->stack_data[st->start+6])));
+ else
+ sd=script_rid2sd(st);
+
+ if ( sd==NULL ) { //wrong char name or char offline
+ push_val(st->stack,C_INT,-1);
+ return 0;
+ }
+
+
+ x=sd->bl.x;
+ y=sd->bl.y;
+ memcpy(mapname,mapindex_id2name(sd->mapindex), MAP_NAME_LENGTH);
+ break;
+ case 1: //Get NPC Position
+ if( st->end > st->start+6 )
+ nd=npc_name2id(conv_str(st,& (st->stack->stack_data[st->start+6])));
+ else
+ nd=(struct npc_data *)map_id2bl(st->oid);
+
+ if ( nd==NULL ) { //wrong npc name or char offline
+ push_val(st->stack,C_INT,-1);
+ return 0;
+ }
+
+ x=nd->bl.x;
+ y=nd->bl.y;
+ memcpy(mapname, map[nd->bl.m].name, MAP_NAME_LENGTH);
+ break;
+ case 2: //Get Pet Position
+ if( st->end>st->start+6 )
+ sd=map_nick2sd(conv_str(st,& (st->stack->stack_data[st->start+6])));
+ else
+ sd=script_rid2sd(st);
+
+ if ( sd==NULL ) { //wrong char name or char offline
+ push_val(st->stack,C_INT,-1);
+ return 0;
+ }
+
+ pd=sd->pd;
+
+ if(pd==NULL){ //pet data not found
+ push_val(st->stack,C_INT,-1);
+ return 0;
+ }
+ x=pd->bl.x;
+ y=pd->bl.y;
+ memcpy(mapname, map[pd->bl.m].name, MAP_NAME_LENGTH);
+ break;
+
+ case 3: //Get Mob Position
+ push_val(st->stack,C_INT,-1);
+ return 0;
+ default: //Wrong type parameter
+ push_val(st->stack,C_INT,-1);
+ return 0;
+ }
+
+ //Set MapName$
+ num=st->stack->stack_data[st->start+2].u.num;
+ name=(char *)(str_buf+str_data[num&0x00ffffff].str);
+ prefix=*name;
+
+ if( prefix!='$' )
+ sd=script_rid2sd(st);
+ else
+ sd=NULL;
+
+ set_reg(sd,num,name,(void*)mapname);
+
+ //Set MapX
+ num=st->stack->stack_data[st->start+3].u.num;
+ name=(char *)(str_buf+str_data[num&0x00ffffff].str);
+ prefix=*name;
+
+ if( prefix!='$' )
+ sd=script_rid2sd(st);
+ else
+ sd=NULL;
+ set_reg(sd,num,name,(void*)x);
+
+
+ //Set MapY
+ num=st->stack->stack_data[st->start+4].u.num;
+ name=(char *)(str_buf+str_data[num&0x00ffffff].str);
+ prefix=*name;
+
+ if( prefix!='$' )
+ sd=script_rid2sd(st);
+ else
+ sd=NULL;
+
+ set_reg(sd,num,name,(void*)y);
+
+ //Return Success value
+ push_val(st->stack,C_INT,0);
+ return 0;
+}
+
+/*=====================================================
+ * Allows players to use a skill - by Qamera
+ *-----------------------------------------------------
+ */
+int buildin_skilluseid (struct script_state *st)
+{
+ int skid,sklv;
+ struct map_session_data *sd;
+
+ skid=conv_num(st,& (st->stack->stack_data[st->start+2]));
+ sklv=conv_num(st,& (st->stack->stack_data[st->start+3]));
+ sd=script_rid2sd(st);
+ skill_use_id(sd,sd->status.account_id,skid,sklv);
+
+ return 0;
+}
+
+/*=====================================================
+ * Allows players to use a skill on a position [Celest]
+ *-----------------------------------------------------
+ */
+int buildin_skillusepos(struct script_state *st)
+{
+ int skid,sklv,x,y;
+ struct map_session_data *sd;
+
+ skid=conv_num(st,& (st->stack->stack_data[st->start+2]));
+ sklv=conv_num(st,& (st->stack->stack_data[st->start+3]));
+ x=conv_num(st,& (st->stack->stack_data[st->start+4]));
+ y=conv_num(st,& (st->stack->stack_data[st->start+5]));
+
+ sd=script_rid2sd(st);
+ skill_use_pos(sd,x,y,skid,sklv);
+
+ return 0;
+}
+
+/*==========================================
+ * Allows player to write NPC logs (i.e. Bank NPC, etc) [Lupus]
+ *------------------------------------------
+ */
+int buildin_logmes(struct script_state *st)
+{
+ if (log_config.npc <= 0 ) return 0;
+ conv_str(st,& (st->stack->stack_data[st->start+2]));
+ log_npc(script_rid2sd(st),st->stack->stack_data[st->start+2].u.str);
+ return 0;
+}
+
+int buildin_summon(struct script_state *st)
+{
+ int _class, id;
+ char *str,*event="";
+ struct map_session_data *sd;
+ struct mob_data *md;
+
+ sd=script_rid2sd(st);
+ if (sd) {
+ int tick = gettick();
+ str =conv_str(st,& (st->stack->stack_data[st->start+2]));
+ _class=conv_num(st,& (st->stack->stack_data[st->start+3]));
+ if( st->end>st->start+4 )
+ event=conv_str(st,& (st->stack->stack_data[st->start+4]));
+
+ id=mob_once_spawn(sd, "this", 0, 0, str,_class,1,event);
+ if((md=(struct mob_data *)map_id2bl(id))){
+ md->master_id=sd->bl.id;
+ md->special_state.ai=1;
+ md->mode=mob_db(md->class_)->mode|0x04;
+ md->deletetimer=add_timer(tick+60000,mob_timer_delete,id,0);
+ clif_misceffect2(&md->bl,344);
+ }
+ clif_skill_poseffect(&sd->bl,AM_CALLHOMUN,1,sd->bl.x,sd->bl.y,tick);
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * Checks whether it is daytime/nighttime
+ *------------------------------------------
+ */
+int buildin_isnight(struct script_state *st)
+{
+ push_val(st->stack,C_INT, (night_flag == 1));
+ return 0;
+}
+
+int buildin_isday(struct script_state *st)
+{
+ push_val(st->stack,C_INT, (night_flag == 0));
+ return 0;
+}
+
+/*================================================
+ * Check whether another item/card has been
+ * equipped - used for 2/15's cards patch [celest]
+ *------------------------------------------------
+ */
+// leave this here, just in case
+#if 0
+int buildin_isequipped(struct script_state *st)
+{
+ struct map_session_data *sd;
+ int i, j, k, id = 1;
+ int ret = -1;
+
+ sd = script_rid2sd(st);
+
+ for (i=0; id!=0; i++) {
+ int flag = 0;
+
+ FETCH (i+2, id) else id = 0;
+ if (id <= 0)
+ continue;
+
+ for (j=0; j<10; j++) {
+ int index, type;
+ index = sd->equip_index[j];
+ if(index < 0) continue;
+ if(j == 9 && sd->equip_index[8] == index) continue;
+ if(j == 5 && sd->equip_index[4] == index) continue;
+ if(j == 6 && (sd->equip_index[5] == index || sd->equip_index[4] == index)) continue;
+ type = itemdb_type(id);
+
+ if(sd->inventory_data[index]) {
+ if (type == 4 || type == 5) {
+ if (sd->inventory_data[index]->nameid == id)
+ flag = 1;
+ } else if (type == 6) {
+ for(k=0; k<sd->inventory_data[index]->slot; k++) {
+ if (sd->status.inventory[index].card[0]!=0x00ff &&
+ sd->status.inventory[index].card[0]!=0x00fe &&
+ sd->status.inventory[index].card[0]!=(short)0xff00 &&
+ sd->status.inventory[index].card[k] == id) {
+ flag = 1;
+ break;
+ }
+ }
+ }
+ if (flag) break;
+ }
+ }
+ if (ret == -1)
+ ret = flag;
+ else
+ ret &= flag;
+ if (!ret) break;
+ }
+
+ push_val(st->stack,C_INT,ret);
+ return 0;
+}
+#endif
+
+/*================================================
+ * Check how many items/cards in the list are
+ * equipped - used for 2/15's cards patch [celest]
+ *------------------------------------------------
+ */
+int buildin_isequippedcnt(struct script_state *st)
+{
+ struct map_session_data *sd;
+ int i, j, k, id = 1;
+ int ret = 0;
+
+ sd = script_rid2sd(st);
+
+ for (i=0; id!=0; i++) {
+ FETCH (i+2, id) else id = 0;
+ if (id <= 0)
+ continue;
+
+ for (j=0; j<10; j++) {
+ int index, type;
+ index = sd->equip_index[j];
+ if(index < 0) continue;
+ if(j == 9 && sd->equip_index[8] == index) continue;
+ if(j == 5 && sd->equip_index[4] == index) continue;
+ if(j == 6 && (sd->equip_index[5] == index || sd->equip_index[4] == index)) continue;
+ type = itemdb_type(id);
+
+ if(sd->inventory_data[index]) {
+ if (type == 4 || type == 5) {
+ if (sd->inventory_data[index]->nameid == id)
+ ret++; //[Lupus]
+ } else if (type == 6) {
+ for(k=0; k<sd->inventory_data[index]->slot; k++) {
+ if (sd->status.inventory[index].card[0]!=0x00ff &&
+ sd->status.inventory[index].card[0]!=0x00fe &&
+ sd->status.inventory[index].card[0]!=(short)0xff00 &&
+ sd->status.inventory[index].card[k] == id) {
+ ret++; //[Lupus]
+ }
+ }
+ }
+ }
+ }
+ }
+
+ push_val(st->stack,C_INT,ret);
+ return 0;
+}
+
+/*================================================
+ * Check whether another card has been
+ * equipped - used for 2/15's cards patch [celest]
+ * -- Items checked cannot be reused in another
+ * card set to prevent exploits
+ *------------------------------------------------
+ */
+int buildin_isequipped(struct script_state *st)
+{
+ struct map_session_data *sd;
+ int i, j, k, id = 1;
+ int index, type, flag;
+ int ret = -1;
+
+ sd = script_rid2sd(st);
+
+ if (!sd) { //If the player is not attached it is a script error anyway... but better prevent the map server from crashing...
+ push_val(st->stack,C_INT,0);
+ return 0;
+ }
+
+ for (i=0; id!=0; i++)
+ {
+ FETCH (i+2, id) else id = 0;
+ if (id <= 0)
+ continue;
+
+ type = itemdb_type(id);
+ flag = 0;
+ for (j=0; j<10; j++)
+ {
+ index = sd->equip_index[j];
+ if(index < 0) continue;
+ if(j == 9 && sd->equip_index[8] == index) continue;
+ if(j == 5 && sd->equip_index[4] == index) continue;
+ if(j == 6 && (sd->equip_index[5] == index || sd->equip_index[4] == index)) continue;
+
+ if(!sd->inventory_data[index])
+ continue;
+
+ switch (type)
+ {
+ case 4:
+ case 5:
+ if (sd->inventory_data[index]->nameid == id)
+ flag = 1;
+ break;
+ case 6:
+ if (
+ sd->inventory_data[index]->slot == 0 ||
+ sd->status.inventory[index].card[0] == 0x00ff ||
+ sd->status.inventory[index].card[0] == 0x00fe ||
+ sd->status.inventory[index].card[0] == (short)0xff00)
+ continue;
+
+ for (k = 0; k < sd->inventory_data[index]->slot; k++)
+ { //New hash system which should support up to 4 slots on any equipment. [Skotlex]
+ unsigned int hash = 0;
+ if (sd->status.inventory[index].card[k] != id)
+ continue;
+
+ hash = 1<<((j<5?j:j-5)*4 + k);
+ // check if card is already used by another set
+ if ((j<5?sd->setitem_hash:sd->setitem_hash2) & hash)
+ continue;
+
+ // We have found a match
+ flag = 1;
+ // Set hash so this card cannot be used by another
+ if (j<5)
+ sd->setitem_hash |= hash;
+ else
+ sd->setitem_hash2 |= hash;
+ break;
+ }
+ //Case 6 end
+ break;
+ }
+ if (flag) break;
+ }
+ if (ret == -1)
+ ret = flag;
+ else
+ ret &= flag;
+ if (!ret) break;
+ }
+
+ push_val(st->stack,C_INT,ret);
+ return 0;
+}
+
+/*================================================
+ * Check how many given inserted cards in the CURRENT
+ * weapon - used for 2/15's cards patch [Lupus]
+ *------------------------------------------------
+ */
+int buildin_cardscnt(struct script_state *st)
+{
+ struct map_session_data *sd;
+ int i, k, id = 1;
+ int ret = 0;
+ int index, type;
+
+ sd = script_rid2sd(st);
+
+ for (i=0; id!=0; i++) {
+ FETCH (i+2, id) else id = 0;
+ if (id <= 0)
+ continue;
+
+ index = current_equip_item_index; //we get CURRENT WEAPON inventory index from status.c [Lupus]
+ if(index < 0) continue;
+
+ type = itemdb_type(id);
+
+ if(sd->inventory_data[index]) {
+ if (type == 4 || type == 5) {
+ if (sd->inventory_data[index]->nameid == id)
+ ret++;
+ } else if (type == 6) {
+ for(k=0; k<sd->inventory_data[index]->slot; k++) {
+ if (sd->status.inventory[index].card[0]!=0x00ff &&
+ sd->status.inventory[index].card[0]!=0x00fe &&
+ sd->status.inventory[index].card[0]!=(short)0xff00 &&
+ sd->status.inventory[index].card[k] == id) {
+ ret++;
+ }
+ }
+ }
+ }
+ }
+ push_val(st->stack,C_INT,ret);
+// push_val(st->stack,C_INT,current_equip_item_index);
+ return 0;
+}
+
+/*=======================================================
+ * Returns the refined number of the current item, or an
+ * item with inventory index specified
+ *-------------------------------------------------------
+ */
+int buildin_getrefine(struct script_state *st)
+{
+ struct map_session_data *sd;
+ if ((sd = script_rid2sd(st))!= NULL)
+ push_val(st->stack, C_INT, sd->status.inventory[current_equip_item_index].refine);
+ return 0;
+}
+
+/*=======================================================
+ * Allows 2 Parents to adopt a character as a Baby
+ *-------------------------------------------------------
+ */
+int buildin_adopt(struct script_state *st)
+{
+ int ret;
+
+ char *parent1 = conv_str(st,& (st->stack->stack_data[st->start+2]));
+ char *parent2 = conv_str(st,& (st->stack->stack_data[st->start+3]));
+ char *child = conv_str(st,& (st->stack->stack_data[st->start+4]));
+
+ struct map_session_data *p1_sd = map_nick2sd(parent1);
+ struct map_session_data *p2_sd = map_nick2sd(parent2);
+ struct map_session_data *c_sd = map_nick2sd(child);
+
+ if (!p1_sd || !p2_sd || !c_sd ||
+ p1_sd->status.base_level < 70 ||
+ p2_sd->status.base_level < 70)
+ return 0;
+
+ ret = pc_adoption(p1_sd, p2_sd, c_sd);
+ push_val(st->stack, C_INT, ret);
+
+ return 0;
+}
+
+/*=======================================================
+ * Day/Night controls
+ *-------------------------------------------------------
+ */
+int buildin_night(struct script_state *st)
+{
+ if (night_flag != 1) map_night_timer(night_timer_tid, 0, 0, 1);
+ return 0;
+}
+int buildin_day(struct script_state *st)
+{
+ if (night_flag != 0) map_day_timer(day_timer_tid, 0, 0, 1);
+ return 0;
+}
+
+//=======================================================
+// Unequip [Spectre]
+//-------------------------------------------------------
+int buildin_unequip(struct script_state *st)
+{
+ int i;
+ size_t num;
+ struct map_session_data *sd;
+
+ num = conv_num(st,& (st->stack->stack_data[st->start+2])) - 1;
+ sd=script_rid2sd(st);
+ if(sd!=NULL && num<10)
+ {
+ i=pc_checkequip(sd,equip[num]);
+ pc_unequipitem(sd,i,2);
+ return 0;
+ }
+ return 0;
+}
+
+//=======================================================
+// strlen [Valaris]
+//-------------------------------------------------------
+int buildin_getstrlen(struct script_state *st) {
+
+ char *str = str=conv_str(st,& (st->stack->stack_data[st->start+2]));
+ int len = (str) ? (int)strlen(str) : 0;
+
+ push_val(st->stack,C_INT,len);
+ return 0;
+}
+
+//=======================================================
+// isalpha [Valaris]
+//-------------------------------------------------------
+int buildin_charisalpha(struct script_state *st) {
+
+ char *str=conv_str(st,& (st->stack->stack_data[st->start+2]));
+ int pos=conv_num(st,& (st->stack->stack_data[st->start+3]));
+
+ int val = ( str && pos>0 && (unsigned int)pos<strlen(str) ) ? isalpha( str[pos] ) : 0;
+
+ push_val(st->stack,C_INT,val);
+ return 0;
+}
+
+// [Lance]
+int buildin_fakenpcname(struct script_state *st)
+{
+ char *name;
+ char *newname;
+ int look;
+ name = conv_str(st,& (st->stack->stack_data[st->start+2]));
+ newname = conv_str(st,& (st->stack->stack_data[st->start+3]));
+ look = conv_num(st,& (st->stack->stack_data[st->start+4]));
+ if(look > 32767 || look < -32768) {
+ ShowError("buildin_fakenpcname: Invalid look value %d\n",look);
+ return 1; // Safety measure to prevent runtime errors
+ }
+ npc_changename(name,newname,(short)look);
+ return 0;
+}
+
+int buildin_atoi(struct script_state *st) {
+ char *value;
+ value = conv_str(st,& (st->stack->stack_data[st->start+2]));
+ push_val(st->stack, C_INT, atoi(value));
+ return 0;
+}
+
+//-----------------------------------------------------------------------//
+// BRING STRSTR TO SCRIPTING ENGINE - LORDALFA START //
+//-----------------------------------------------------------------------//
+int buildin_compare(struct script_state *st) {
+ char *message;
+ char *cmpstring;
+ int j;
+ message = conv_str(st,& (st->stack->stack_data[st->start+2]));
+ cmpstring = conv_str(st,& (st->stack->stack_data[st->start+3]));
+ for (j = 0; message[j]; j++)
+ message[j] = tolower(message[j]);
+ for (j = 0; cmpstring[j]; j++)
+ cmpstring[j] = tolower(cmpstring[j]);
+ push_val(st->stack,C_INT,(strstr(message,cmpstring) != NULL));
+ return 0;
+}
+
+//-----------------------------------------------------------------------//
+// BRING STRSTR TO SCRIPTING ENGINE - LORDALFA END //
+//-----------------------------------------------------------------------//
+// [zBuffer] List of mathematics commands --->
+int buildin_sqrt(struct script_state *st){
+ double i, a;
+ i = conv_num(st, &(st->stack->stack_data[st->start+2]));
+ a = sqrt(i);
+ push_val(st->stack, C_INT, (int)a);
+ return 0;
+}
+
+int buildin_pow(struct script_state *st){
+ double i, a, b;
+ a = conv_num(st, &(st->stack->stack_data[st->start+2]));
+ b = conv_num(st, &(st->stack->stack_data[st->start+3]));
+ i = pow(a,b);
+ push_val(st->stack, C_INT, (int)i);
+ return 0;
+}
+int buildin_distance(struct script_state *st){
+ int x0, y0, x1, y1;
+
+ x0 = conv_num(st, &(st->stack->stack_data[st->start+2]));
+ y0 = conv_num(st, &(st->stack->stack_data[st->start+3]));
+ x1 = conv_num(st, &(st->stack->stack_data[st->start+4]));
+ y1 = conv_num(st, &(st->stack->stack_data[st->start+5]));
+
+ push_val(st->stack, C_INT, distance(x0-x1, y0-y1));
+ return 0;
+}
+
+// <--- [zBuffer] List of mathematics commands
+// [zBuffer] List of dynamic var commands --->
+void setd_sub(struct map_session_data *sd, char *varname, int elem, void *value)
+{
+ set_reg(sd, add_str((unsigned char *) varname)+(elem<<24), varname, value);
+ return;
+}
+
+int buildin_setd(struct script_state *st)
+{
+ struct map_session_data *sd=NULL;
+ char varname[100], *buffer;
+ char *value;
+ int elem;
+ buffer = conv_str(st, & (st->stack->stack_data[st->start+2]));
+ value = conv_str(st, & (st->stack->stack_data[st->start+3]));
+
+ if(sscanf(buffer, "%[^[][%d]", varname, &elem) < 2)
+ elem = 0;
+
+ if(st->rid)
+ sd = script_rid2sd(st);
+
+ if(varname[strlen(varname)-1] != '$') {
+ setd_sub(sd, varname, elem, (void *)atoi(value));
+ } else {
+ setd_sub(sd, varname, elem, (void *)value);
+ }
+
+ return 0;
+}
+
+#ifndef TXT_ONLY
+int buildin_query_sql(struct script_state *st) {
+ char *name, *query;
+ int num, i = 0;
+ struct map_session_data *sd = (st->rid)? script_rid2sd(st) : NULL;
+
+ query = conv_str(st,& (st->stack->stack_data[st->start+2]));
+ strcpy(tmp_sql, query);
+ if(mysql_query(&mmysql_handle,tmp_sql)){
+ ShowSQL("DB error - %s\n",mysql_error(&mmysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ return 1;
+ }
+
+ if(st->end > st->start+3) {
+ if(st->stack->stack_data[st->start+3].type != C_NAME){
+ ShowWarning("buildin_query_sql: 2nd parameter is not a variable!\n");
+ } else {
+ num=st->stack->stack_data[st->start+3].u.num;
+ name=(char *)(str_buf+str_data[num&0x00ffffff].str);
+ if((sql_res = mysql_store_result(&mmysql_handle))){
+ if(name[strlen(name)-1] != '$') {
+ while(i<128 && (sql_row = mysql_fetch_row(sql_res))){
+ setd_sub(sd, name, i, (void *)atoi(sql_row[0]));
+ i++;
+ }
+ } else {
+ while(i<128 && (sql_row = mysql_fetch_row(sql_res))){
+ setd_sub(sd, name, i, (void *)sql_row[0]);
+ i++;
+ }
+ }
+ mysql_free_result(sql_res);
+ }
+ }
+ }
+
+ return 0;
+}
+#endif
+
+int buildin_getd (struct script_state *st)
+{
+ char varname[100], *buffer;
+ //struct script_data dat;
+ int elem;
+
+ buffer = conv_str(st, & (st->stack->stack_data[st->start+2]));
+
+ if(sscanf(buffer, "%[^[][%d]", varname, &elem) < 2)
+ elem = 0;
+
+ /*dat.type=C_NAME;
+ dat.u.num=add_str((unsigned char *) varname)+(elem<<24);
+ get_val(st,&dat);
+
+ if(dat.type == C_INT)
+ push_val(st->stack, C_INT, dat.u.num);
+ else if(dat.type == C_CONSTSTR){
+ buffer = aStrdup((char *)dat.u.str);
+ // dat.u.str holds the actual pointer to the data, must be duplicated.
+ // It will be freed later. Tested.
+ push_str(st->stack, C_STR, buffer);
+ }*/
+
+ // Push the 'pointer' so it's more flexible [Lance]
+ push_val(st->stack,C_NAME,
+ (elem<<24) | add_str((unsigned char *) varname));
+
+ return 0;
+}
+
+// <--- [zBuffer] List of dynamic var commands
+// Pet stat [Lance]
+int buildin_petstat(struct script_state *st){
+ struct map_session_data *sd = NULL;
+ char *tmp;
+ int flag = conv_num(st, & (st->stack->stack_data[st->start+2]));
+ sd = script_rid2sd(st);
+ if(!sd || !sd->pet.pet_id){
+ if(flag == 2)
+ push_str(st->stack, C_CONSTSTR, "");
+ else
+ push_val(st->stack, C_INT, 0);
+ }
+ else {
+ switch(flag){
+ case 1:
+ push_val(st->stack, C_INT, (int)sd->pet.class_);
+ break;
+ case 2:
+ tmp = aStrdup(sd->pet.name);
+ push_str(st->stack, C_STR, tmp);
+ break;
+ case 3:
+ push_val(st->stack, C_INT, (int)sd->pet.level);
+ break;
+ case 4:
+ push_val(st->stack, C_INT, (int)sd->pet.hungry);
+ break;
+ case 5:
+ push_val(st->stack, C_INT, (int)sd->pet.intimate);
+ break;
+ default:
+ push_val(st->stack, C_INT, 0);
+ break;
+ }
+ }
+ return 0;
+}
+
+int buildin_callshop(struct script_state *st)
+{
+ struct map_session_data *sd = NULL;
+ struct npc_data *nd;
+ char *shopname;
+ int flag = 0;
+ sd = script_rid2sd(st);
+ if (!sd) {
+ push_val(st->stack,C_INT,0);
+ return 0;
+ }
+ shopname = conv_str(st, & (st->stack->stack_data[st->start+2]));
+ if( st->end>st->start+3 )
+ flag = conv_num(st, & (st->stack->stack_data[st->start+3]));
+ nd = npc_name2id(shopname);
+ if (!nd || nd->bl.type!=BL_NPC || nd->bl.subtype!=SHOP) {
+ ShowError("buildin_callshop: Shop [%s] not found (or NPC is not shop type)", shopname);
+ push_val(st->stack,C_INT,0);
+ return 1;
+ }
+
+ switch (flag) {
+ case 1: //Buy window
+ npc_buysellsel(sd,nd->bl.id,0);
+ break;
+ case 2: //Sell window
+ npc_buysellsel(sd,nd->bl.id,1);
+ break;
+ default: //Show menu
+ clif_npcbuysell(sd,nd->bl.id);
+ break;
+ }
+ sd->npc_shopid = nd->bl.id;
+ push_val(st->stack,C_INT,1);
+ return 0;
+}
+/*==========================================
+ * Returns some values of an item [Lupus]
+ * Price, Weight, etc...
+ setiteminfo(itemID,"{new item bonus script}");
+ *------------------------------------------
+ */
+int buildin_setitemscript(struct script_state *st)
+{
+ int item_id;
+ char *script;
+ struct item_data *i_data;
+
+ item_id = conv_num(st,& (st->stack->stack_data[st->start+2]));
+ script = conv_str(st,& (st->stack->stack_data[st->start+3]));
+ i_data = itemdb_exists(item_id);
+
+ if (i_data && script!=NULL && script[0]=='{') {
+ if(i_data->script!=NULL)
+ aFree(i_data->script);
+ i_data->script = parse_script((unsigned char *) script, 0);
+ push_val(st->stack,C_INT,1);
+ } else
+ push_val(st->stack,C_INT,0);
+ return 0;
+}
+
+/* Work In Progress [Lupus]
+int buildin_addmonsterdrop(struct script_state *st)
+{
+ int class_,item_id,chance;
+ class_=conv_num(st,& (st->stack->stack_data[st->start+2]));
+ item_id=conv_num(st,& (st->stack->stack_data[st->start+3]));
+ chance=conv_num(st,& (st->stack->stack_data[st->start+4]));
+ if(class_>1000 && item_id>500 && chance>0) {
+ push_val(st->stack,C_INT,1);
+ } else {
+ push_val(st->stack,C_INT,0);
+ }
+}
+
+int buildin_delmonsterdrop(struct script_state *st)
+{
+ int class_,item_id;
+ class_=conv_num(st,& (st->stack->stack_data[st->start+2]));
+ item_id=conv_num(st,& (st->stack->stack_data[st->start+3]));
+ if(class_>1000 && item_id>500) {
+ push_val(st->stack,C_INT,1);
+ } else {
+ push_val(st->stack,C_INT,0);
+ }
+}
+*/
+
+//
+// 実行部main
+//
+/*==========================================
+ * コマンドの読み取り
+ *------------------------------------------
+ */
+static int unget_com_data=-1;
+int get_com(unsigned char *script,int *pos)
+{
+ int i,j;
+ if(unget_com_data>=0){
+ i=unget_com_data;
+ unget_com_data=-1;
+ return i;
+ }
+ if(script[*pos]>=0x80){
+ return C_INT;
+ }
+ i=0; j=0;
+ while(script[*pos]>=0x40){
+ i=script[(*pos)++]<<j;
+ j+=6;
+ }
+ return i+(script[(*pos)++]<<j);
+}
+
+/*==========================================
+ * コマンドのプッシュバック
+ *------------------------------------------
+ */
+void unget_com(int c)
+{
+ if(unget_com_data!=-1){
+ if(battle_config.error_log)
+ ShowError("unget_com can back only 1 data\n");
+ }
+ unget_com_data=c;
+}
+
+/*==========================================
+ * 数値の所得
+ *------------------------------------------
+ */
+int get_num(unsigned char *script,int *pos)
+{
+ int i,j;
+ i=0; j=0;
+ while(script[*pos]>=0xc0){
+ i+=(script[(*pos)++]&0x7f)<<j;
+ j+=6;
+ }
+ return i+((script[(*pos)++]&0x7f)<<j);
+}
+
+/*==========================================
+ * スタックから値を取り出す
+ *------------------------------------------
+ */
+int pop_val(struct script_state* st)
+{
+ if(st->stack->sp<=0)
+ return 0;
+ st->stack->sp--;
+ get_val(st,&(st->stack->stack_data[st->stack->sp]));
+ if(st->stack->stack_data[st->stack->sp].type==C_INT)
+ return st->stack->stack_data[st->stack->sp].u.num;
+ return 0;
+}
+
+#define isstr(c) ((c).type==C_STR || (c).type==C_CONSTSTR)
+
+/*==========================================
+ * 加算演算子
+ *------------------------------------------
+ */
+void op_add(struct script_state* st)
+{
+ st->stack->sp--;
+ get_val(st,&(st->stack->stack_data[st->stack->sp]));
+ get_val(st,&(st->stack->stack_data[st->stack->sp-1]));
+
+ if(isstr(st->stack->stack_data[st->stack->sp]) || isstr(st->stack->stack_data[st->stack->sp-1])){
+ conv_str(st,&(st->stack->stack_data[st->stack->sp]));
+ conv_str(st,&(st->stack->stack_data[st->stack->sp-1]));
+ }
+ if(st->stack->stack_data[st->stack->sp].type==C_INT){ // ii
+ st->stack->stack_data[st->stack->sp-1].u.num += st->stack->stack_data[st->stack->sp].u.num;
+ } else { // ssの予定
+ char *buf;
+ buf=(char *)aCallocA(strlen(st->stack->stack_data[st->stack->sp-1].u.str)+
+ strlen(st->stack->stack_data[st->stack->sp].u.str)+1,sizeof(char));
+ strcpy(buf,st->stack->stack_data[st->stack->sp-1].u.str);
+ strcat(buf,st->stack->stack_data[st->stack->sp].u.str);
+ if(st->stack->stack_data[st->stack->sp-1].type==C_STR)
+ {
+ aFree(st->stack->stack_data[st->stack->sp-1].u.str);
+ st->stack->stack_data[st->stack->sp-1].type=C_INT;
+ }
+ if(st->stack->stack_data[st->stack->sp].type==C_STR)
+ {
+ aFree(st->stack->stack_data[st->stack->sp].u.str);
+ st->stack->stack_data[st->stack->sp].type=C_INT;
+ }
+ st->stack->stack_data[st->stack->sp-1].type=C_STR;
+ st->stack->stack_data[st->stack->sp-1].u.str=buf;
+ }
+}
+
+/*==========================================
+ * 二項演算子(文字列)
+ *------------------------------------------
+ */
+void op_2str(struct script_state *st,int op,int sp1,int sp2)
+{
+ char *s1=st->stack->stack_data[sp1].u.str,
+ *s2=st->stack->stack_data[sp2].u.str;
+ int a=0;
+
+ switch(op){
+ case C_EQ:
+ a= (strcmp(s1,s2)==0);
+ break;
+ case C_NE:
+ a= (strcmp(s1,s2)!=0);
+ break;
+ case C_GT:
+ a= (strcmp(s1,s2)> 0);
+ break;
+ case C_GE:
+ a= (strcmp(s1,s2)>=0);
+ break;
+ case C_LT:
+ a= (strcmp(s1,s2)< 0);
+ break;
+ case C_LE:
+ a= (strcmp(s1,s2)<=0);
+ break;
+ default:
+ ShowWarning("script: illegal string operator\n");
+ break;
+ }
+
+ push_val(st->stack,C_INT,a);
+
+ if(st->stack->stack_data[sp1].type==C_STR)
+ {
+ aFree(s1);
+ st->stack->stack_data[sp1].type=C_INT;
+ }
+ if(st->stack->stack_data[sp2].type==C_STR)
+ {
+ aFree(s2);
+ st->stack->stack_data[sp2].type=C_INT;
+ }
+}
+/*==========================================
+ * 二項演算子(数値)
+ *------------------------------------------
+ */
+void op_2num(struct script_state *st,int op,int i1,int i2)
+{
+ switch(op){
+ case C_SUB:
+ i1-=i2;
+ break;
+ case C_MUL:
+ {
+ #ifndef _MSC_VER
+ long long res = i1 * i2;
+ #else
+ __int64 res = i1 * i2;
+ #endif
+ if (res > 2147483647 )
+ i1 = 2147483647;
+ else
+ i1*=i2;
+ }
+ break;
+ case C_DIV:
+ if (i2 != 0)
+ i1/=i2;
+ else
+ ShowWarning("op_2num: Attempted to divide by 0 in a script (operation C_DIV)!\n");
+ break;
+ case C_MOD:
+ if (i2 != 0)
+ i1%=i2;
+ else
+ ShowWarning("op_2num: Attempted to divide by 0 in a script (operation C_MOD)!\n");
+ break;
+ case C_AND:
+ i1&=i2;
+ break;
+ case C_OR:
+ i1|=i2;
+ break;
+ case C_XOR:
+ i1^=i2;
+ break;
+ case C_LAND:
+ i1=i1&&i2;
+ break;
+ case C_LOR:
+ i1=i1||i2;
+ break;
+ case C_EQ:
+ i1=i1==i2;
+ break;
+ case C_NE:
+ i1=i1!=i2;
+ break;
+ case C_GT:
+ i1=i1>i2;
+ break;
+ case C_GE:
+ i1=i1>=i2;
+ break;
+ case C_LT:
+ i1=i1<i2;
+ break;
+ case C_LE:
+ i1=i1<=i2;
+ break;
+ case C_R_SHIFT:
+ i1=i1>>i2;
+ break;
+ case C_L_SHIFT:
+ i1=i1<<i2;
+ break;
+ }
+ push_val(st->stack,C_INT,i1);
+}
+/*==========================================
+ * 二項演算子
+ *------------------------------------------
+ */
+void op_2(struct script_state *st,int op)
+{
+ int i1,i2;
+ char *s1=NULL,*s2=NULL;
+
+ i2=pop_val(st);
+ if( isstr(st->stack->stack_data[st->stack->sp]) )
+ s2=st->stack->stack_data[st->stack->sp].u.str;
+
+ i1=pop_val(st);
+ if( isstr(st->stack->stack_data[st->stack->sp]) )
+ s1=st->stack->stack_data[st->stack->sp].u.str;
+
+ if( s1!=NULL && s2!=NULL ){
+ // ss => op_2str
+ op_2str(st,op,st->stack->sp,st->stack->sp+1);
+ }else if( s1==NULL && s2==NULL ){
+ // ii => op_2num
+ op_2num(st,op,i1,i2);
+ }else{
+ // si,is => error
+ ShowWarning("script: op_2: int&str, str&int not allow.");
+ push_val(st->stack,C_INT,0);
+ }
+}
+
+/*==========================================
+ * 単項演算子
+ *------------------------------------------
+ */
+void op_1num(struct script_state *st,int op)
+{
+ int i1;
+ i1=pop_val(st);
+ switch(op){
+ case C_NEG:
+ i1=-i1;
+ break;
+ case C_NOT:
+ i1=~i1;
+ break;
+ case C_LNOT:
+ i1=!i1;
+ break;
+ }
+ push_val(st->stack,C_INT,i1);
+}
+
+
+/*==========================================
+ * 関数の実行
+ *------------------------------------------
+ */
+int run_func(struct script_state *st)
+{
+ int i,start_sp,end_sp,func;
+
+ end_sp=st->stack->sp;
+ for(i=end_sp-1;i>=0 && st->stack->stack_data[i].type!=C_ARG;i--);
+ if(i==0){
+ if(battle_config.error_log)
+ ShowError("function not found\n");
+// st->stack->sp=0;
+ st->state=END;
+ report_src(st);
+ return 1;
+ }
+ start_sp=i-1;
+ st->start=i-1;
+ st->end=end_sp;
+
+ func=st->stack->stack_data[st->start].u.num;
+ if( st->stack->stack_data[st->start].type!=C_NAME || str_data[func].type!=C_FUNC ){
+ ShowMessage ("run_func: '"CL_WHITE"%s"CL_RESET"' (type %d) is not function and command!\n",
+ str_buf + str_data[func].str, str_data[func].type);
+// st->stack->sp=0;
+ st->state=END;
+ report_src(st);
+ return 1;
+ }
+#ifdef DEBUG_RUN
+ if(battle_config.etc_log) {
+ ShowDebug("run_func : %s? (%d(%d)) sp=%d (%d...%d)\n",str_buf+str_data[func].str, func, str_data[func].type, st->stack->sp, st->start, st->end);
+ ShowDebug("stack dump :");
+ for(i=0;i<end_sp;i++){
+ switch(st->stack->stack_data[i].type){
+ case C_INT:
+ printf(" int(%d)",st->stack->stack_data[i].u.num);
+ break;
+ case C_NAME:
+ printf(" name(%s)",str_buf+str_data[st->stack->stack_data[i].u.num].str);
+ break;
+ case C_ARG:
+ printf(" arg");
+ break;
+ case C_POS:
+ printf(" pos(%d)",st->stack->stack_data[i].u.num);
+ break;
+ case C_STR:
+ printf(" str(%s)",st->stack->stack_data[i].u.str);
+ break;
+ case C_CONSTSTR:
+ printf(" cstr(%s)",st->stack->stack_data[i].u.str);
+ break;
+ default:
+ printf(" %d,%d",st->stack->stack_data[i].type,st->stack->stack_data[i].u.num);
+ }
+ }
+ printf("\n");
+ }
+#endif
+ if(str_data[func].func){
+ if (str_data[func].func(st)) //Report error
+ report_src(st);
+ } else {
+ if(battle_config.error_log)
+ ShowError("run_func : %s? (%d(%d))\n",str_buf+str_data[func].str,func,str_data[func].type);
+ push_val(st->stack,C_INT,0);
+ report_src(st);
+ }
+
+ // Stack's datum are used when re-run functions [Eoe]
+ if(st->state != RERUNLINE) {
+ pop_stack(st->stack,start_sp,end_sp);
+ }
+
+ if(st->state==RETFUNC){
+ // ユーザー定義関数からの復帰
+ int olddefsp=st->stack->defsp;
+ int i;
+
+ pop_stack(st->stack,st->stack->defsp,start_sp); // 復帰に邪魔なスタック削除
+ if(st->stack->defsp<4 || st->stack->stack_data[st->stack->defsp-1].type!=C_RETINFO){
+ ShowWarning("script:run_func(return) return without callfunc or callsub!\n");
+ st->state=END;
+ report_src(st);
+ return 1;
+ }
+ i = conv_num(st,& (st->stack->stack_data[st->stack->defsp-4])); // 引数の数所得
+ st->pos=conv_num(st,& (st->stack->stack_data[st->stack->defsp-1])); // スクリプト位置の復元
+ st->script=(char*)conv_num(st,& (st->stack->stack_data[st->stack->defsp-2])); // スクリプトを復元
+ st->stack->defsp=conv_num(st,& (st->stack->stack_data[st->stack->defsp-3])); // 基準スタックポインタを復元
+
+ pop_stack(st->stack,olddefsp-4-i,olddefsp); // 要らなくなったスタック(引数と復帰用データ)削除
+
+ st->state=GOTO;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * スクリプトの実行メイン部分
+ *------------------------------------------
+ */
+int run_script_main(struct script_state *st)
+{
+ int c/*,rerun_pos*/;
+ int cmdcount=script_config.check_cmdcount;
+ int gotocount=script_config.check_gotocount;
+ struct script_stack *stack=st->stack;
+
+ if(st->state == RERUNLINE) {
+ st->state = RUN;
+ run_func(st);
+ if(st->state == GOTO){
+ st->state = RUN;
+ }
+ } else {
+ st->state = RUN;
+ }
+ while( st->state == RUN) {
+ c= get_com((unsigned char *) st->script,&st->pos);
+ switch(c){
+ case C_EOL:
+ if(stack->sp!=stack->defsp){
+ if(stack->sp > stack->defsp)
+ { //sp > defsp is valid in cases when you invoke functions and don't use the returned value. [Skotlex]
+ //Since sp is supposed to be defsp in these cases, we could assume the extra stack elements are unneeded.
+ if (battle_config.etc_log)
+ ShowWarning("Clearing unused stack stack.sp(%d) -> default(%d)\n",stack->sp,stack->defsp);
+ pop_stack(stack, stack->defsp, stack->sp); //Clear out the unused stack-section.
+ } else if(battle_config.error_log)
+ ShowError("stack.sp(%d) != default(%d)\n",stack->sp,stack->defsp);
+ stack->sp=stack->defsp;
+ }
+ // rerun_pos=st->pos;
+ break;
+ case C_INT:
+ push_val(stack,C_INT,get_num((unsigned char *) st->script,&st->pos));
+ break;
+ case C_POS:
+ case C_NAME:
+ push_val(stack,c,(*(int*)(st->script+st->pos))&0xffffff);
+ st->pos+=3;
+ break;
+ case C_ARG:
+ push_val(stack,c,0);
+ break;
+ case C_STR:
+ push_str(stack,C_CONSTSTR,(unsigned char *) (st->script+st->pos));
+ while(st->script[st->pos++]);
+ break;
+ case C_FUNC:
+ run_func(st);
+ if(st->state==GOTO){
+ // rerun_pos=st->pos;
+ st->state=0;
+ if( gotocount>0 && (--gotocount)<=0 ){
+ ShowError("run_script: infinity loop !\n");
+ st->state=END;
+ }
+ }
+ break;
+
+ case C_ADD:
+ op_add(st);
+ break;
+
+ case C_SUB:
+ case C_MUL:
+ case C_DIV:
+ case C_MOD:
+ case C_EQ:
+ case C_NE:
+ case C_GT:
+ case C_GE:
+ case C_LT:
+ case C_LE:
+ case C_AND:
+ case C_OR:
+ case C_XOR:
+ case C_LAND:
+ case C_LOR:
+ case C_R_SHIFT:
+ case C_L_SHIFT:
+ op_2(st,c);
+ break;
+
+ case C_NEG:
+ case C_NOT:
+ case C_LNOT:
+ op_1num(st,c);
+ break;
+
+ case C_NOP:
+ st->state=END;
+ break;
+
+ default:
+ if(battle_config.error_log)
+ ShowError("unknown command : %d @ %d\n",c,pos);
+ st->state=END;
+ break;
+ }
+ if( cmdcount>0 && (--cmdcount)<=0 ){
+ ShowError("run_script: infinity loop !\n");
+ st->state=END;
+ }
+ }
+ switch(st->state){
+ case STOP:
+ break;
+ case END:
+ {
+ struct map_session_data *sd=map_id2sd(st->rid);
+ st->pos=-1;
+ if(sd && sd->npc_id==st->oid)
+ npc_event_dequeue(sd);
+ }
+ break;
+ case RERUNLINE:
+ // Do not call function of commands two time! [ Eoe / jA 1094 ]
+ // For example: select "1", "2", callsub(...);
+ // If current script position is changed, callsub will be called two time.
+ //
+ // {
+ // st->pos=rerun_pos;
+ // }
+ break;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * スクリプトの実行
+ *------------------------------------------
+ */
+int run_script(unsigned char *script,int pos,int rid,int oid)
+{
+ struct script_state st;
+ struct map_session_data *sd;
+ unsigned char *rootscript = script;
+
+ //Variables for backing up the previous script and restore it if needed. [Skotlex]
+ unsigned char *bck_script = NULL;
+ unsigned char *bck_scriptroot = NULL;
+ int bck_scriptstate = 0;
+ struct script_stack *bck_stack = NULL;
+
+ if (script == NULL || pos < 0)
+ return -1;
+ memset(&st, 0, sizeof(struct script_state));
+
+ if ((sd = map_id2sd(rid)) && sd->stack && sd->npc_scriptroot == rootscript){
+ // we have a stack for the same script, should continue exec.
+ st.script = sd->npc_script;
+ st.stack = sd->stack;
+ st.state = sd->npc_scriptstate;
+ // and clear vars
+ sd->stack = NULL;
+ sd->npc_script = NULL;
+ sd->npc_scriptroot = NULL;
+ sd->npc_scriptstate = 0;
+ } else {
+ // the script is different, make new script_state and stack
+ st.stack = aCalloc (1, sizeof(struct script_stack));
+ st.stack->sp = 0;
+ st.stack->sp_max = 64;
+ st.stack->stack_data = (struct script_data *) aCalloc (st.stack->sp_max,sizeof(st.stack->stack_data[0]));
+ st.stack->defsp = st.stack->sp;
+ st.state = RUN;
+ st.script = rootscript;
+
+ if (sd && sd->stack) { // if there's a sd and a stack - back it up and restore it if possible.
+ bck_script = sd->npc_script;
+ bck_scriptroot = sd->npc_scriptroot;
+ bck_scriptstate = sd->npc_scriptstate;
+ bck_stack = sd->stack;
+ sd->stack = NULL;
+ }
+ }
+ st.pos = pos;
+ st.rid = rid;
+ st.oid = oid;
+ // let's run that stuff
+ run_script_main(&st);
+
+ sd = map_id2sd(st.rid);
+ if (st.state != END && sd) {
+ // script is not finished, store data in sd.
+ sd->npc_script = st.script;
+ sd->npc_scriptroot = rootscript;
+ sd->npc_scriptstate = st.state;
+ sd->stack = st.stack;
+ if (bck_stack) //Get rid of the backup as it can't be restored.
+ script_free_stack (bck_stack);
+ } else {
+ // we are done with stuff, free the stack
+ script_free_stack (st.stack);
+ // and if there was a sd associated - zero vars.
+ if (sd) {
+ //Clear or restore previous script.
+ sd->npc_script = bck_script;
+ sd->npc_scriptroot = bck_scriptroot;
+ sd->npc_scriptstate = bck_scriptstate;
+ sd->stack = bck_stack;
+ //Since the script is done, save any changed account variables [Skotlex]
+ if (sd->state.reg_dirty&2)
+ intif_saveregistry(sd,2);
+ if (sd->state.reg_dirty&1)
+ intif_saveregistry(sd,1);
+ }
+ }
+
+ return st.pos;
+}
+
+
+/*==========================================
+ * マップ変数の変更
+ *------------------------------------------
+ */
+int mapreg_setreg(int num,int val)
+{
+#if !defined(TXT_ONLY) && defined(MAPREGSQL)
+ int i=num>>24;
+ char *name=str_buf+str_data[num&0x00ffffff].str;
+ char tmp_str[64];
+#endif
+
+ if(val!=0) {
+
+#if !defined(TXT_ONLY) && defined(MAPREGSQL)
+ if(name[1] != '@' && idb_get(mapreg_db,num) == NULL) {
+ sprintf(tmp_sql,"INSERT INTO `%s`(`%s`,`%s`,`%s`) VALUES ('%s','%d','%d')",mapregsql_db,mapregsql_db_varname,mapregsql_db_index,mapregsql_db_value,jstrescapecpy(tmp_str,name),i,val);
+ if(mysql_query(&mmysql_handle,tmp_sql)){
+ ShowSQL("DB error - %s\n",mysql_error(&mmysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ }
+#endif
+ idb_put(mapreg_db,num,(void*)val);
+ // else
+ } else { // [zBuffer]
+#if !defined(TXT_ONLY) && defined(MAPREGSQL)
+ if(name[1] != '@') { // Remove from database because it is unused.
+ sprintf(tmp_sql,"DELETE FROM `%s` WHERE `%s`='%s' AND `%s`='%d'",mapregsql_db,mapregsql_db_varname,name,mapregsql_db_index,i);
+ if(mysql_query(&mmysql_handle,tmp_sql)){
+ ShowSQL("DB error - %s\n",mysql_error(&mmysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ }
+#endif
+ idb_remove(mapreg_db,num);
+ }
+
+ mapreg_dirty=1;
+ return 0;
+}
+/*==========================================
+ * 文字列型マップ変数の変更
+ *------------------------------------------
+ */
+int mapreg_setregstr(int num,const char *str)
+{
+ char *p;
+#if !defined(TXT_ONLY) && defined(MAPREGSQL)
+ char tmp_str[64];
+ char tmp_str2[512];
+ int i=num>>24; // [zBuffer]
+ char *name=str_buf+str_data[num&0x00ffffff].str;
+#endif
+
+ if( str==NULL || *str==0 ){
+#if !defined(TXT_ONLY) && defined(MAPREGSQL)
+ if(name[1] != '@') {
+ sprintf(tmp_sql,"DELETE FROM `%s` WHERE `%s`='%s' AND `%s`='%d'",mapregsql_db,mapregsql_db_varname,name,mapregsql_db_index,i);
+ if(mysql_query(&mmysql_handle,tmp_sql)){
+ ShowSQL("DB error - %s\n",mysql_error(&mmysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ }
+#endif
+ idb_remove(mapregstr_db,num);
+ mapreg_dirty=1;
+ return 0;
+ }
+ p=(char *)aCallocA(strlen(str)+1, sizeof(char));
+ strcpy(p,str);
+
+ if (idb_put(mapregstr_db,num,p))
+ ;
+#if !defined(TXT_ONLY) && defined(MAPREGSQL)
+ else { //put returned null, so we must insert.
+ sprintf(tmp_sql,"INSERT INTO `%s`(`%s`,`%s`,`%s`) VALUES ('%s','%d','%s')",mapregsql_db,mapregsql_db_varname,mapregsql_db_index,mapregsql_db_value,jstrescapecpy(tmp_str,name),i,jstrescapecpy(tmp_str2,p));
+ if(mysql_query(&mmysql_handle,tmp_sql)){
+ ShowSQL("DB error - %s\n",mysql_error(&mmysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ }
+#endif
+ mapreg_dirty=1;
+ return 0;
+}
+
+/*==========================================
+ * 永続的マップ変数の読み込み
+ *------------------------------------------
+ */
+static int script_load_mapreg(void)
+{
+#if defined(TXT_ONLY) || !defined(MAPREGSQL)
+ FILE *fp;
+ char line[1024];
+
+ if( (fp=fopen(mapreg_txt,"rt"))==NULL )
+ return -1;
+
+ while(fgets(line,sizeof(line),fp)){
+ char buf1[256],buf2[1024],*p;
+ int n,v,s,i;
+ if( sscanf(line,"%255[^,],%d\t%n",buf1,&i,&n)!=2 &&
+ (i=0,sscanf(line,"%[^\t]\t%n",buf1,&n)!=1) )
+ continue;
+ if( buf1[strlen(buf1)-1]=='$' ){
+ if( sscanf(line+n,"%[^\n\r]",buf2)!=1 ){
+ ShowError("%s: %s broken data !\n",mapreg_txt,buf1);
+ continue;
+ }
+ p=(char *)aCallocA(strlen(buf2) + 1,sizeof(char));
+ strcpy(p,buf2);
+ s= add_str((unsigned char *) buf1);
+ idb_put(mapregstr_db,(i<<24)|s,p);
+ }else{
+ if( sscanf(line+n,"%d",&v)!=1 ){
+ ShowError("%s: %s broken data !\n",mapreg_txt,buf1);
+ continue;
+ }
+ s= add_str((unsigned char *) buf1);
+ idb_put(mapreg_db,(i<<24)|s,(void*)v);
+ }
+ }
+ fclose(fp);
+ mapreg_dirty=0;
+ return 0;
+#else
+ // SQL mapreg code start [zBuffer]
+ /*
+ 0 1 2
+ +-------------------------+
+ | varname | index | value |
+ +-------------------------+
+ */
+ int perfomance = gettick_nocache();
+ sprintf(tmp_sql,"SELECT * FROM `%s`",mapregsql_db);
+ ShowInfo("Querying script_load_mapreg ...\n");
+ if(mysql_query(&mapregsql_handle, tmp_sql) ) {
+ ShowSQL("DB error - %s\n",mysql_error(&mapregsql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ return -1;
+ }
+ ShowInfo("Success! Returning results ...\n");
+ mapregsql_res = mysql_store_result(&mapregsql_handle);
+ if (mapregsql_res) {
+ while ((mapregsql_row = mysql_fetch_row(mapregsql_res))) {
+ char buf1[33], *p = NULL;
+ int i,v,s;
+ strcpy(buf1,mapregsql_row[0]);
+ if( buf1[strlen(buf1)-1]=='$' ){
+ i = atoi(mapregsql_row[1]);
+ p=(char *)aCallocA(strlen(mapregsql_row[2]) + 1,sizeof(char));
+ strcpy(p,mapregsql_row[2]);
+ s= add_str((unsigned char *) buf1);
+ idb_put(mapregstr_db,(i<<24)|s,p);
+ }else{
+ s= add_str((unsigned char *) buf1);
+ v= atoi(mapregsql_row[2]);
+ i = atoi(mapregsql_row[1]);
+ idb_put(mapreg_db,(i<<24)|s,(void *)v);
+ }
+ }
+ }
+ ShowInfo("Freeing results...\n");
+ mysql_free_result(mapregsql_res);
+ mapreg_dirty=0;
+ perfomance = (gettick_nocache() - perfomance) / 1000;
+ ShowInfo("SQL Mapreg Loading Completed Under %d Seconds.\n",perfomance);
+ return 0;
+#endif /* TXT_ONLY */
+}
+/*==========================================
+ * 永続的マップ変数の書き込み
+ *------------------------------------------
+ */
+static int script_save_mapreg_intsub(DBKey key,void *data,va_list ap)
+{
+#if defined(TXT_ONLY) || !defined(MAPREGSQL)
+ FILE *fp=va_arg(ap,FILE*);
+ int num=key.i&0x00ffffff, i=key.i>>24;
+ char *name=str_buf+str_data[num].str;
+ if( name[1]!='@' ){
+ if(i==0)
+ fprintf(fp,"%s\t%d\n", name, (int)data);
+ else
+ fprintf(fp,"%s,%d\t%d\n", name, i, (int)data);
+ }
+ return 0;
+#else
+ int num=key.i&0x00ffffff, i=key.i>>24; // [zBuffer]
+ char *name=str_buf+str_data[num].str;
+ if ( name[1] != '@') {
+ sprintf(tmp_sql,"UPDATE `%s` SET `%s`='%d' WHERE `%s`='%s' AND `%s`='%d'",mapregsql_db,mapregsql_db_value,(int)data,mapregsql_db_varname,name,mapregsql_db_index,i);
+ if(mysql_query(&mapregsql_handle, tmp_sql) ) {
+ ShowSQL("DB error - %s\n",mysql_error(&mapregsql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ }
+ return 0;
+#endif
+}
+static int script_save_mapreg_strsub(DBKey key,void *data,va_list ap)
+{
+#if defined(TXT_ONLY) || !defined(MAPREGSQL)
+ FILE *fp=va_arg(ap,FILE*);
+ int num=key.i&0x00ffffff, i=key.i>>24;
+ char *name=str_buf+str_data[num].str;
+ if( name[1]!='@' ){
+ if(i==0)
+ fprintf(fp,"%s\t%s\n", name, (char *)data);
+ else
+ fprintf(fp,"%s,%d\t%s\n", name, i, (char *)data);
+ }
+ return 0;
+#else
+ char tmp_str2[512];
+ int num=key.i&0x00ffffff, i=key.i>>24;
+ char *name=str_buf+str_data[num].str;
+ if ( name[1] != '@') {
+ sprintf(tmp_sql,"UPDATE `%s` SET `%s`='%s' WHERE `%s`='%s' AND `%s`='%d'",mapregsql_db,mapregsql_db_value,jstrescapecpy(tmp_str2,(char *)data),mapregsql_db_varname,name,mapregsql_db_index,i);
+ if(mysql_query(&mapregsql_handle, tmp_sql) ) {
+ ShowSQL("DB error - %s\n",mysql_error(&mapregsql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ }
+ return 0;
+#endif
+}
+static int script_save_mapreg(void)
+{
+#if defined(TXT_ONLY) || !defined(MAPREGSQL)
+ FILE *fp;
+ int lock;
+
+ if( (fp=lock_fopen(mapreg_txt,&lock))==NULL ) {
+ ShowError("script_save_mapreg: Unable to lock-open file [%s]\n",mapreg_txt);
+ return -1;
+ }
+ mapreg_db->foreach(mapreg_db,script_save_mapreg_intsub,fp);
+ mapregstr_db->foreach(mapregstr_db,script_save_mapreg_strsub,fp);
+ lock_fclose(fp,mapreg_txt,&lock);
+#else
+ int perfomance = gettick_nocache();
+ mapreg_db->foreach(mapreg_db,script_save_mapreg_intsub); // [zBuffer]
+ mapregstr_db->foreach(mapregstr_db,script_save_mapreg_strsub);
+ perfomance = (gettick_nocache() - perfomance) / 1000;
+ ShowInfo("Mapreg saved in %d seconds.\n", perfomance);
+#endif
+ mapreg_dirty=0;
+ return 0;
+}
+static int script_autosave_mapreg(int tid,unsigned int tick,int id,int data)
+{
+ if(mapreg_dirty)
+ if (script_save_mapreg() == -1)
+ ShowError("Failed to save the mapreg data!\n");
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+static int set_posword(char *p)
+{
+ char* np,* str[15];
+ int i=0;
+ for(i=0;i<11;i++) {
+ if((np=strchr(p,','))!=NULL) {
+ str[i]=p;
+ *np=0;
+ p=np+1;
+ } else {
+ str[i]=p;
+ p+=strlen(p);
+ }
+ if(str[i])
+ strcpy(pos[i],str[i]);
+ }
+ return 0;
+}
+
+int script_config_read_sub(char *cfgName)
+{
+ int i;
+ char line[1024],w1[1024],w2[1024];
+ FILE *fp;
+
+
+ fp = fopen(cfgName, "r");
+ if (fp == NULL) {
+ ShowError("file not found: [%s]\n", cfgName);
+ return 1;
+ }
+ while (fgets(line, sizeof(line) - 1, fp)) {
+ if (line[0] == '/' && line[1] == '/')
+ continue;
+ i = sscanf(line,"%[^:]: %[^\r\n]",w1,w2);
+ if (i != 2)
+ continue;
+ if(strcmpi(w1,"refine_posword")==0) {
+ set_posword(w2);
+ }
+ else if(strcmpi(w1,"verbose_mode")==0) {
+ script_config.verbose_mode = battle_config_switch(w2);
+ }
+ else if(strcmpi(w1,"warn_func_no_comma")==0) {
+ script_config.warn_func_no_comma = battle_config_switch(w2);
+ }
+ else if(strcmpi(w1,"warn_cmd_no_comma")==0) {
+ script_config.warn_cmd_no_comma = battle_config_switch(w2);
+ }
+ else if(strcmpi(w1,"warn_func_mismatch_paramnum")==0) {
+ script_config.warn_func_mismatch_paramnum = battle_config_switch(w2);
+ }
+ else if(strcmpi(w1,"warn_cmd_mismatch_paramnum")==0) {
+ script_config.warn_cmd_mismatch_paramnum = battle_config_switch(w2);
+ }
+ else if(strcmpi(w1,"check_cmdcount")==0) {
+ script_config.check_cmdcount = battle_config_switch(w2);
+ }
+ else if(strcmpi(w1,"check_gotocount")==0) {
+ script_config.check_gotocount = battle_config_switch(w2);
+ }
+ else if(strcmpi(w1,"event_script_type")==0) {
+ script_config.event_script_type = battle_config_switch(w2);
+ }
+ else if(strcmpi(w1,"event_requires_trigger")==0) {
+ script_config.event_requires_trigger = battle_config_switch(w2);
+ }
+ else if(strcmpi(w1,"die_event_name")==0) {
+ strncpy(script_config.die_event_name, w2, NAME_LENGTH-1);
+ if (strlen(script_config.die_event_name) != strlen(w2))
+ ShowWarning("script_config_read: Event label truncated (max length is 23 chars): %d\n", script_config.die_event_name);
+ }
+ else if(strcmpi(w1,"kill_event_name")==0) {
+ strncpy(script_config.kill_event_name, w2, NAME_LENGTH-1);
+ if (strlen(script_config.kill_event_name) != strlen(w2))
+ ShowWarning("script_config_read: Event label truncated (max length is 23 chars): %d\n", script_config.kill_event_name);
+ }
+ else if(strcmpi(w1,"login_event_name")==0) {
+ strncpy(script_config.login_event_name, w2, NAME_LENGTH-1);
+ if (strlen(script_config.login_event_name) != strlen(w2))
+ ShowWarning("script_config_read: Event label truncated (max length is 23 chars): %d\n", script_config.login_event_name);
+ }
+ else if(strcmpi(w1,"logout_event_name")==0) {
+ strncpy(script_config.logout_event_name, w2, NAME_LENGTH-1);
+ if (strlen(script_config.logout_event_name) != strlen(w2))
+ ShowWarning("script_config_read: Event label truncated (max length is 23 chars): %d\n", script_config.logout_event_name);
+ }
+ else if(strcmpi(w1,"loadmap_event_name")==0) {
+ strncpy(script_config.loadmap_event_name, w2, NAME_LENGTH-1);
+ if (strlen(script_config.loadmap_event_name) != strlen(w2))
+ ShowWarning("script_config_read: Event label truncated (max length is 23 chars): %d\n", script_config.loadmap_event_name);
+ }
+ else if(strcmpi(w1,"baselvup_event_name")==0) {
+ strncpy(script_config.baselvup_event_name, w2, NAME_LENGTH-1);
+ if (strlen(script_config.baselvup_event_name) != strlen(w2))
+ ShowWarning("script_config_read: Event label truncated (max length is 23 chars): %d\n", script_config.baselvup_event_name);
+ }
+ else if(strcmpi(w1,"joblvup_event_name")==0) {
+ strncpy(script_config.joblvup_event_name, w2, NAME_LENGTH-1);
+ if (strlen(script_config.joblvup_event_name) != strlen(w2))
+ ShowWarning("script_config_read: Event label truncated (max length is 23 chars): %d\n", script_config.joblvup_event_name);
+ }
+ else if(strcmpi(w1,"import")==0){
+ script_config_read_sub(w2);
+ }
+ }
+ fclose(fp);
+
+ return 0;
+}
+
+int script_config_read(char *cfgName)
+{ //Script related variables should be initialized once! [Skotlex]
+
+ memset (&script_config, 0, sizeof(script_config));
+ script_config.verbose_mode = 0;
+ script_config.warn_func_no_comma = 1;
+ script_config.warn_cmd_no_comma = 1;
+ script_config.warn_func_mismatch_paramnum = 1;
+ script_config.warn_cmd_mismatch_paramnum = 1;
+ script_config.check_cmdcount = 65535;
+ script_config.check_gotocount = 2048;
+
+ script_config.event_script_type = 0;
+ script_config.event_requires_trigger = 1;
+
+ return script_config_read_sub(cfgName);
+}
+
+/*==========================================
+ * 終了
+ *------------------------------------------
+ */
+int do_final_script()
+{
+ if(mapreg_dirty>=0)
+ script_save_mapreg();
+
+ mapreg_db->destroy(mapreg_db,NULL);
+ mapregstr_db->destroy(mapregstr_db,NULL);
+ scriptlabel_db->destroy(scriptlabel_db,NULL);
+ userfunc_db->destroy(userfunc_db,NULL);
+
+ if (str_data)
+ aFree(str_data);
+ if (str_buf)
+ aFree(str_buf);
+
+ return 0;
+}
+/*==========================================
+ * 初期化
+ *------------------------------------------
+ */
+int do_init_script()
+{
+ mapreg_db= db_alloc(__FILE__,__LINE__,DB_INT,DB_OPT_BASE,sizeof(int));
+ mapregstr_db=db_alloc(__FILE__,__LINE__,DB_INT,DB_OPT_RELEASE_DATA,sizeof(int));
+ userfunc_db=db_alloc(__FILE__,__LINE__,DB_STRING,DB_OPT_RELEASE_BOTH,50);
+ scriptlabel_db=db_alloc(__FILE__,__LINE__,DB_STRING,DB_OPT_ALLOW_NULL_DATA,50);
+
+ script_load_mapreg();
+
+ add_timer_func_list(script_autosave_mapreg,"script_autosave_mapreg");
+ add_timer_interval(gettick()+MAPREG_AUTOSAVE_INTERVAL,
+ script_autosave_mapreg,0,0,MAPREG_AUTOSAVE_INTERVAL);
+
+ return 0;
+}
+
+int script_reload()
+{
+ if(mapreg_dirty>=0)
+ script_save_mapreg();
+
+ mapreg_db->clear(mapreg_db, NULL);
+ mapregstr_db->clear(mapreg_db, NULL);
+ userfunc_db->clear(mapreg_db, NULL);
+ scriptlabel_db->clear(mapreg_db, NULL);
+
+ script_load_mapreg();
+ return 0;
+}
diff --git a/src/map/script.h b/src/map/script.h
new file mode 100644
index 000000000..ecd326de9
--- /dev/null
+++ b/src/map/script.h
@@ -0,0 +1,73 @@
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#ifndef _SCRIPT_H_
+#define _SCRIPT_H_
+
+extern int potion_flag; //For use on Alchemist improved potions/Potion Pitcher. [Skotlex]
+extern int potion_hp, potion_per_hp, potion_sp, potion_per_sp;
+extern int potion_target;
+
+extern struct Script_Config {
+ unsigned verbose_mode : 1;
+ unsigned warn_func_no_comma : 1;
+ unsigned warn_cmd_no_comma : 1;
+ unsigned warn_func_mismatch_paramnum : 1;
+ unsigned warn_cmd_mismatch_paramnum : 1;
+ int check_cmdcount;
+ int check_gotocount;
+
+ unsigned event_script_type : 1;
+ unsigned event_requires_trigger : 1;
+ char die_event_name[NAME_LENGTH];
+ char kill_event_name[NAME_LENGTH];
+ char login_event_name[NAME_LENGTH];
+ char logout_event_name[NAME_LENGTH];
+ char loadmap_event_name[NAME_LENGTH];
+ char baselvup_event_name[NAME_LENGTH];
+ char joblvup_event_name[NAME_LENGTH];
+} script_config;
+
+struct script_data {
+ int type;
+ union {
+ int num;
+ char *str;
+ } u;
+};
+
+// Moved defsp from script_state to script_stack since
+// it must be saved when script state is RERUNLINE. [Eoe / jA 1094]
+struct script_stack {
+ int sp,sp_max,defsp;
+ struct script_data *stack_data;
+};
+struct script_state {
+ struct script_stack *stack;
+ int start,end;
+ int pos,state;
+ int rid,oid;
+ unsigned char *script,*new_script;
+ int new_pos,new_defsp;
+};
+
+unsigned char * parse_script(unsigned char *,int);
+int run_script(unsigned char *,int,int,int);
+
+int set_var(struct map_session_data *sd, char *name, void *val);
+int conv_num(struct script_state *st,struct script_data *data);
+char* conv_str(struct script_state *st,struct script_data *data);
+
+struct dbt* script_get_label_db(void);
+struct dbt* script_get_userfunc_db(void);
+
+int script_config_read(char *cfgName);
+void script_free_stack(struct script_stack*);
+int do_init_script(void);
+int do_final_script(void);
+int script_reload(void);
+
+extern char mapreg_txt[];
+
+#endif
+
diff --git a/src/map/skill.c b/src/map/skill.c
new file mode 100644
index 000000000..7c393fd40
--- /dev/null
+++ b/src/map/skill.c
@@ -0,0 +1,12348 @@
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#include "timer.h"
+#include "nullpo.h"
+#include "malloc.h"
+
+#include "skill.h"
+#include "map.h"
+#include "clif.h"
+#include "pc.h"
+#include "status.h"
+#include "pet.h"
+#include "mob.h"
+#include "battle.h"
+#include "party.h"
+#include "itemdb.h"
+#include "script.h"
+#include "intif.h"
+#include "log.h"
+#include "chrif.h"
+#include "guild.h"
+#include "showmsg.h"
+#include "grfio.h"
+#include "date.h"
+
+#define SKILLUNITTIMER_INVERVAL 100
+#define swap(x,y) { int t; t = x; x = y; y = t; }
+
+int skill_names_id[MAX_SKILL_DB];
+const struct skill_name_db skill_names[] = {
+ { AC_CHARGEARROW, "AC_CHARGEARROW", "Arrow_Repel" } ,
+ { AC_CONCENTRATION, "AC_CONCENTRATION", "Improve_Concentration" } ,
+ { AC_DOUBLE, "AC_DOUBLE", "Double_Strafe" } ,
+ { AC_MAKINGARROW, "AC_MAKINGARROW", "Arrow_Crafting" } ,
+ { AC_OWL, "AC_OWL", "Owl's_Eye" } ,
+ { AC_SHOWER, "AC_SHOWER", "Arrow_Shower" } ,
+ { AC_VULTURE, "AC_VULTURE", "Vulture's_Eye" } ,
+ { ALL_RESURRECTION, "ALL_RESURRECTION", "Resurrection" } ,
+ { AL_ANGELUS, "AL_ANGELUS", "Angelus" } ,
+ { AL_BLESSING, "AL_BLESSING", "Blessing" } ,
+ { AL_CRUCIS, "AL_CRUCIS", "Signum_Crusis" } ,
+ { AL_CURE, "AL_CURE", "Cure" } ,
+ { AL_DECAGI, "AL_DECAGI", "Decrease_AGI" } ,
+ { AL_DEMONBANE, "AL_DEMONBANE", "Demon_Bane" } ,
+ { AL_DP, "AL_DP", "Divine_Protection" } ,
+ { AL_HEAL, "AL_HEAL", "Heal" } ,
+ { AL_HOLYLIGHT, "AL_HOLYLIGHT", "Holy_Light" } ,
+ { AL_HOLYWATER, "AL_HOLYWATER", "Aqua_Benedicta" } ,
+ { AL_INCAGI, "AL_INCAGI", "Increase_AGI" } ,
+ { AL_PNEUMA, "AL_PNEUMA", "Pneuma" } ,
+ { AL_RUWACH, "AL_RUWACH", "Ruwach" } ,
+ { AL_TELEPORT, "AL_TELEPORT", "Teleport" } ,
+ { AL_WARP, "AL_WARP", "Warp_Portal" } ,
+ { AM_ACIDTERROR, "AM_ACIDTERROR", "Acid_Terror" } ,
+ { AM_AXEMASTERY, "AM_AXEMASTERY", "Axe_Mastery" } ,
+ { AM_BERSERKPITCHER, "AM_BERSERKPITCHER", "Aid_Berserk_Potion" } ,
+ { AM_BIOETHICS, "AM_BIOETHICS", "Bioethics" } ,
+ { AM_CALLHOMUN, "AM_CALLHOMUN", "Call_Homunculus" } ,
+ { AM_CANNIBALIZE, "AM_CANNIBALIZE", "Summon_Flora" } ,
+ { AM_CP_ARMOR, "AM_CP_ARMOR", "Synthetic_Armor" } ,
+ { AM_CP_HELM, "AM_CP_HELM", "Biochemical_Helm" } ,
+ { AM_CP_SHIELD, "AM_CP_SHIELD", "Synthetized_Shield" } ,
+ { AM_CP_WEAPON, "AM_CP_WEAPON", "Alchemical_Weapon" } ,
+ { AM_CULTIVATION, "AM_CULTIVATION", "Cultivation" } ,
+ { AM_DEMONSTRATION, "AM_DEMONSTRATION", "Bomb" } ,
+ { AM_LEARNINGPOTION, "AM_LEARNINGPOTION", "Potion_Research" } ,
+ { AM_PHARMACY, "AM_PHARMACY", "Prepare_Potion" } ,
+ { AM_POTIONPITCHER, "AM_POTIONPITCHER", "Aid_Potion" } ,
+ { AM_REST, "AM_REST", "Vaporize" } ,
+ { AM_RESURRECTHOMUN, "AM_RESURRECTHOMUN", "Homunculus_Resurrection" } ,
+ { AM_SPHEREMINE, "AM_SPHEREMINE", "Summon_Marine_Sphere" } ,
+ { AM_TWILIGHT1, "AM_TWILIGHT1", "Twilight_Pharmacy_1" } ,
+ { AM_TWILIGHT2, "AM_TWILIGHT2", "Twilight_Pharmacy_2" } ,
+ { AM_TWILIGHT3, "AM_TWILIGHT3", "Twilight_Pharmacy_3" } ,
+ { ASC_BREAKER, "ASC_BREAKER", "Soul_Destroyer" } ,
+ { ASC_CDP, "ASC_CDP", "Create_Deadly_Poison" } ,
+ { ASC_EDP, "ASC_EDP", "Enchant_Deadly_Poison" } ,
+ { ASC_KATAR, "ASC_KATAR", "Advanced_Katar_Mastery" } ,
+ { ASC_METEORASSAULT, "ASC_METEORASSAULT", "Meteor_Assault" } ,
+ { AS_CLOAKING, "AS_CLOAKING", "Cloaking" } ,
+ { AS_ENCHANTPOISON, "AS_ENCHANTPOISON", "Enchant_Poison" } ,
+ { AS_GRIMTOOTH, "AS_GRIMTOOTH", "Grimtooth" } ,
+ { AS_KATAR, "AS_KATAR", "Katar_Mastery" } ,
+ { AS_LEFT, "AS_LEFT", "Lefthand_Mastery" } ,
+ { AS_POISONREACT, "AS_POISONREACT", "Poison_React" } ,
+ { AS_RIGHT, "AS_RIGHT", "Righthand_Mastery" } ,
+ { AS_SONICACCEL, "AS_SONICACCEL", "Sonic_Acceleration" } ,
+ { AS_SONICBLOW, "AS_SONICBLOW", "Sonic_Blow" } ,
+ { AS_SPLASHER, "AS_SPLASHER", "Venom_Splasher" } ,
+ { AS_VENOMDUST, "AS_VENOMDUST", "Venom_Dust" } ,
+ { AS_VENOMKNIFE, "AS_VENOMKNIFE", "Throw_Venom_Knife" } ,
+ { BA_APPLEIDUN, "BA_APPLEIDUN", "Song_of_Lutie" } ,
+ { BA_ASSASSINCROSS, "BA_ASSASSINCROSS", "Impressive_Riff" } ,
+ { BA_DISSONANCE, "BA_DISSONANCE", "Unchained_Serenade" } ,
+ { BA_FROSTJOKE, "BA_FROSTJOKE", "Unbarring_Octave" } ,
+ { BA_MUSICALLESSON, "BA_MUSICALLESSON", "Music_Lessons" } ,
+ { BA_MUSICALSTRIKE, "BA_MUSICALSTRIKE", "Melody_Strike" } ,
+ { BA_PANGVOICE, "BA_PANGVOICE", "Pang_Voice" } ,
+ { BA_POEMBRAGI, "BA_POEMBRAGI", "Magic_Strings" } ,
+ { BA_WHISTLE, "BA_WHISTLE", "Perfect_Tablature" } ,
+ { BD_ADAPTATION, "BD_ADAPTATION", "Amp" } ,
+ { BD_DRUMBATTLEFIELD, "BD_DRUMBATTLEFIELD", "Battle_Theme" } ,
+ { BD_ENCORE, "BD_ENCORE", "Encore" } ,
+ { BD_ETERNALCHAOS, "BD_ETERNALCHAOS", "Down_Tempo" } ,
+ { BD_INTOABYSS, "BD_INTOABYSS", "Power_Cord" } ,
+ { BD_LULLABY, "BD_LULLABY", "Lullaby" } ,
+ { BD_RICHMANKIM, "BD_RICHMANKIM", "Mental_Sensing" } ,
+ { BD_RINGNIBELUNGEN, "BD_RINGNIBELUNGEN", "Harmonic_Lick" } ,
+ { BD_ROKISWEIL, "BD_ROKISWEIL", "Classical_Pluck" } ,
+ { BD_SIEGFRIED, "BD_SIEGFRIED", "Acoustic_Rhythm" } ,
+ { BS_ADRENALINE, "BS_ADRENALINE", "Adrenaline_Rush" } ,
+ { BS_ADRENALINE2, "BS_ADRENALINE2", "Advanced_Adrenaline_Rush" } ,
+ { BS_AXE, "BS_AXE", "Smith_Axe" } ,
+ { BS_DAGGER, "BS_DAGGER", "Smith_Dagger" } ,
+ { BS_ENCHANTEDSTONE, "BS_ENCHANTEDSTONE", "Enchantedstone_Craft" } ,
+ { BS_FINDINGORE, "BS_FINDINGORE", "Ore_Discovery" } ,
+ { BS_GREED, "BS_GREED", "Greed" } ,
+ { BS_HAMMERFALL, "BS_HAMMERFALL", "Hammer_Fall" } ,
+ { BS_HILTBINDING, "BS_HILTBINDING", "Hilt_Binding" } ,
+ { BS_IRON, "BS_IRON", "Iron_Tempering" } ,
+ { BS_KNUCKLE, "BS_KNUCKLE", "Smith_Knucklebrace" } ,
+ { BS_MACE, "BS_MACE", "Smith_Mace" } ,
+ { BS_MAXIMIZE, "BS_MAXIMIZE", "Power_Maximize" } ,
+ { BS_ORIDEOCON, "BS_ORIDEOCON", "Oridecon_Research" } ,
+ { BS_OVERTHRUST, "BS_OVERTHRUST", "Power-Thrust" } ,
+ { BS_REPAIRWEAPON, "BS_REPAIRWEAPON", "Weapon_Repair" } ,
+ { BS_SKINTEMPER, "BS_SKINTEMPER", "Skin_Tempering" } ,
+ { BS_SPEAR, "BS_SPEAR", "Smith_Spear" } ,
+ { BS_STEEL, "BS_STEEL", "Steel_Tempering" } ,
+ { BS_SWORD, "BS_SWORD", "Smith_Sword" } ,
+ { BS_TWOHANDSWORD, "BS_TWOHANDSWORD", "Smith_Two-handed_Sword" } ,
+ { BS_UNFAIRLYTRICK, "BS_UNFAIRLYTRICK", "Unfair_Trick" } ,
+ { BS_WEAPONPERFECT, "BS_WEAPONPERFECT", "Weapon_Perfection" } ,
+ { BS_WEAPONRESEARCH, "BS_WEAPONRESEARCH", "Weaponry_Research" } ,
+ { CG_ARROWVULCAN, "CG_ARROWVULCAN", "Vulcan_Arrow" } ,
+ { CG_HERMODE, "CG_HERMODE", "Wand_of_Hermode" } ,
+ { CG_LONGINGFREEDOM, "CG_LONGINGFREEDOM", "Longing_for_Freedom" } ,
+ { CG_MARIONETTE, "CG_MARIONETTE", "Marionette_Control" } ,
+ { CG_MOONLIT, "CG_MOONLIT", "Sheltering_Bliss" } ,
+ { CG_TAROTCARD, "CG_TAROTCARD", "Tarot_Card_of_Fate" } ,
+ { CH_CHAINCRUSH, "CH_CHAINCRUSH", "Chain_Crush_Combo" } ,
+ { CH_PALMSTRIKE, "CH_PALMSTRIKE", "Raging_Palm_Strike" } ,
+ { CH_SOULCOLLECT, "CH_SOULCOLLECT", "Zen" } ,
+ { CH_TIGERFIST, "CH_TIGERFIST", "Glacier_Fist" } ,
+ { CR_ACIDDEMONSTRATION, "CR_ACIDDEMONSTRATION", "Acid_Demonstration" } ,
+ { CR_ALCHEMY, "CR_ALCHEMY", "Alchemy" } ,
+ { CR_CULTIVATION, "CR_CULTIVATION", "Plant_Cultivation" } ,
+ { CR_SLIMPITCHER, "CR_SLIMPITCHER", "Slim_Pitcher" } ,
+ { CR_FULLPROTECTION, "CR_FULLPROTECTION", "Full_Protection" } ,
+ { CR_SYNTHESISPOTION, "CR_SYNTHESISPOTION", "Potion_Synthesis" } ,
+ { CR_AUTOGUARD, "CR_AUTOGUARD", "Guard" } ,
+ { CR_DEFENDER, "CR_DEFENDER", "Defending_Aura" } ,
+ { CR_DEVOTION, "CR_DEVOTION", "Sacrifice" } ,
+ { CR_GRANDCROSS, "CR_GRANDCROSS", "Grand_Cross" } ,
+ { CR_HOLYCROSS, "CR_HOLYCROSS", "Holy_Cross" } ,
+ { CR_PROVIDENCE, "CR_PROVIDENCE", "Resistant_Souls" } ,
+ { CR_REFLECTSHIELD, "CR_REFLECTSHIELD", "Shield_Reflect" } ,
+ { CR_SHIELDBOOMERANG, "CR_SHIELDBOOMERANG", "Shield_Boomerang" } ,
+ { CR_SHIELDCHARGE, "CR_SHIELDCHARGE", "Smite" } ,
+ { CR_SHRINK, "CR_SHRINK", "Shrink" } ,
+ { CR_SPEARQUICKEN, "CR_SPEARQUICKEN", "Spear_Quicken" } ,
+ { CR_TRUST, "CR_TRUST", "Faith" } ,
+ { DC_DANCINGLESSON, "DC_DANCINGLESSON", "Dance_Lessons" } ,
+ { DC_DONTFORGETME, "DC_DONTFORGETME", "Slow_Grace" } ,
+ { DC_FORTUNEKISS, "DC_FORTUNEKISS", "Lady_Luck" } ,
+ { DC_HUMMING, "DC_HUMMING", "Focus_Ballet" } ,
+ { DC_SCREAM, "DC_SCREAM", "Dazzler" } ,
+ { DC_SERVICEFORYOU, "DC_SERVICEFORYOU", "Gypsy's_Kiss" } ,
+ { DC_THROWARROW, "DC_THROWARROW", "Slinging_Arrow" } ,
+ { DC_UGLYDANCE, "DC_UGLYDANCE", "Hip_Shaker" } ,
+ { DC_WINKCHARM, "DC_WINKCHARM", "Sexy_Wink" } ,
+ { GD_APPROVAL, "GD_APPROVAL", "Official_Guild_Approval" } ,
+ { GD_BATTLEORDER, "GD_BATTLEORDER", "Battle_Command" } ,
+ { GD_DEVELOPMENT, "GD_DEVELOPMENT", "Permanent_Development" } ,
+ { GD_EMERGENCYCALL, "GD_EMERGENCYCALL", "Urgent_Call" } ,
+ { GD_EXTENSION, "GD_EXTENSION", "Guild_Extension" } ,
+ { GD_GLORYGUILD, "GD_GLORYGUILD", "Glory_of_Guild" } ,
+ { GD_GLORYWOUNDS, "GD_GLORYWOUNDS", "Glorious_Wounds" } ,
+ { GD_GUARDUP, "GD_GUARDUP", "Strengthen_Guardian" } ,
+ { GD_LEADERSHIP, "GD_LEADERSHIP", "Great_Leadership" } ,
+ { GD_HAWKEYES, "GD_HAWKEYES", "Sharp_Gaze" } ,
+ { GD_KAFRACONTRACT, "GD_KAFRACONTRACT", "Contract_with_Kafra" } ,
+ { GD_REGENERATION, "GD_REGENERATION", "Regeneration" } ,
+ { GD_RESTORE, "GD_RESTORE", "Restoration" } ,
+ { GD_SOULCOLD, "GD_SOULCOLD", "Cold_Heart" } ,
+ { HP_ASSUMPTIO, "HP_ASSUMPTIO", "Assumptio" } ,
+ { HP_BASILICA, "HP_BASILICA", "Basilica" } ,
+ { HP_MANARECHARGE, "HP_MANARECHARGE", "Mana_Recharge" } ,
+ { HP_MEDITATIO, "HP_MEDITATIO", "Meditatio" } ,
+ { HT_ANKLESNARE, "HT_ANKLESNARE", "Ankle_Snare" } ,
+ { HT_BEASTBANE, "HT_BEASTBANE", "Beast_Bane" } ,
+ { HT_BLASTMINE, "HT_BLASTMINE", "Blast_Mine" } ,
+ { HT_BLITZBEAT, "HT_BLITZBEAT", "Blitz_Beat" } ,
+ { HT_CLAYMORETRAP, "HT_CLAYMORETRAP", "Claymore_Trap" } ,
+ { HT_DETECTING, "HT_DETECTING", "Detect" } ,
+ { HT_FALCON, "HT_FALCON", "Falconry_Mastery" } ,
+ { HT_FLASHER, "HT_FLASHER", "Flasher" } ,
+ { HT_FREEZINGTRAP, "HT_FREEZINGTRAP", "Freezing_Trap" } ,
+ { HT_LANDMINE, "HT_LANDMINE", "Land_Mine" } ,
+ { HT_PHANTASMIC, "HT_PHANTASMIC", "Phantasmic_Arrow" } ,
+ { HT_POWER, "HT_POWER", "Beast_Strafing" } ,
+ { HT_REMOVETRAP, "HT_REMOVETRAP", "Remove_Trap" } ,
+ { HT_SANDMAN, "HT_SANDMAN", "Sandman" } ,
+ { HT_SHOCKWAVE, "HT_SHOCKWAVE", "Shockwave_Trap" } ,
+ { HT_SKIDTRAP, "HT_SKIDTRAP", "Skid_Trap" } ,
+ { HT_SPRINGTRAP, "HT_SPRINGTRAP", "Spring_Trap" } ,
+ { HT_STEELCROW, "HT_STEELCROW", "Steel_Crow" } ,
+ { HT_TALKIEBOX, "HT_TALKIEBOX", "Talkie_Box" } ,
+ { HW_GANBANTEIN, "HW_GANBANTEIN", "Ganbantein" } ,
+ { HW_GRAVITATION, "HW_GRAVITATION", "Gravitation_Field" } ,
+ { HW_MAGICCRASHER, "HW_MAGICCRASHER", "Stave_Crasher" } ,
+ { HW_MAGICPOWER, "HW_MAGICPOWER", "Mystical_Amplification" } ,
+ { HW_NAPALMVULCAN, "HW_NAPALMVULCAN", "Napalm_Vulcan" } ,
+ { HW_SOULDRAIN, "HW_SOULDRAIN", "Soul_Drain" } ,
+ { ITM_TOMAHAWK, "ITM_TOMAHAWK", "Tomahawk_Throwing" } ,
+ { KN_AUTOCOUNTER, "KN_AUTOCOUNTER", "Counter_Attack" } ,
+ { KN_BOWLINGBASH, "KN_BOWLINGBASH", "Bowling_Bash" } ,
+ { KN_BRANDISHSPEAR, "KN_BRANDISHSPEAR", "Brandish_Spear" } ,
+ { KN_CAVALIERMASTERY, "KN_CAVALIERMASTERY", "Cavalier_Mastery" } ,
+ { KN_CHARGEATK, "KN_CHARGEATK", "Charge_Attack" } ,
+ { KN_ONEHAND, "KN_ONEHAND", "Onehand_Quicken" } ,
+ { KN_PIERCE, "KN_PIERCE", "Pierce" } ,
+ { KN_RIDING, "KN_RIDING", "Peco_Peco_Ride" } ,
+ { KN_SPEARBOOMERANG, "KN_SPEARBOOMERANG", "Spear_Boomerang" } ,
+ { KN_SPEARMASTERY, "KN_SPEARMASTERY", "Spear_Mastery" } ,
+ { KN_SPEARSTAB, "KN_SPEARSTAB", "Spear_Stab" } ,
+ { KN_TWOHANDQUICKEN, "KN_TWOHANDQUICKEN", "Twohand_Quicken" } ,
+ { LK_AURABLADE, "LK_AURABLADE", "Aura_Blade" } ,
+ { LK_BERSERK, "LK_BERSERK", "Frenzy" } ,
+ { LK_CONCENTRATION, "LK_CONCENTRATION", "Spear_Dynamo" } ,
+ { LK_HEADCRUSH, "LK_HEADCRUSH", "Traumatic_Blow" } ,
+ { LK_JOINTBEAT, "LK_JOINTBEAT", "Vital_Strike" } ,
+ { LK_PARRYING, "LK_PARRYING", "Parry" } ,
+ { LK_SPIRALPIERCE, "LK_SPIRALPIERCE", "Clashing_Spiral" } ,
+ { LK_TENSIONRELAX, "LK_TENSIONRELAX", "Relax" } ,
+ { MC_CARTREVOLUTION, "MC_CARTREVOLUTION", "Cart_Revolution" } ,
+ { MC_CHANGECART, "MC_CHANGECART", "Change_Cart" } ,
+ { MC_DISCOUNT, "MC_DISCOUNT", "Discount" } ,
+ { MC_IDENTIFY, "MC_IDENTIFY", "Item_Appraisal" } ,
+ { MC_INCCARRY, "MC_INCCARRY", "Enlarge_Weight_Limit" } ,
+ { MC_LOUD, "MC_LOUD", "Crazy_Uproar" } ,
+ { MC_MAMMONITE, "MC_MAMMONITE", "Mammonite" } ,
+ { MC_OVERCHARGE, "MC_OVERCHARGE", "Overcharge" } ,
+ { MC_PUSHCART, "MC_PUSHCART", "Pushcart" } ,
+ { MC_VENDING, "MC_VENDING", "Vending" } ,
+ { MG_COLDBOLT, "MG_COLDBOLT", "Cold_Bolt" } ,
+ { MG_ENERGYCOAT, "MG_ENERGYCOAT", "Energy_Coat" } ,
+ { MG_FIREBALL, "MG_FIREBALL", "Fire_Ball" } ,
+ { MG_FIREBOLT, "MG_FIREBOLT", "Fire_Bolt" } ,
+ { MG_FIREWALL, "MG_FIREWALL", "Fire_Wall" } ,
+ { MG_FROSTDIVER, "MG_FROSTDIVER", "Frost_Diver" } ,
+ { MG_LIGHTNINGBOLT, "MG_LIGHTNINGBOLT", "Lightening_Bolt" } ,
+ { MG_NAPALMBEAT, "MG_NAPALMBEAT", "Napalm_Beat" } ,
+ { MG_SAFETYWALL, "MG_SAFETYWALL", "Safety_Wall" } ,
+ { MG_SIGHT, "MG_SIGHT", "Sight" } ,
+ { MG_SOULSTRIKE, "MG_SOULSTRIKE", "Soul_Strike" } ,
+ { MG_SRECOVERY, "MG_SRECOVERY", "Increase_SP_Recovery" } ,
+ { MG_STONECURSE, "MG_STONECURSE", "Stone_Curse" } ,
+ { MG_THUNDERSTORM, "MG_THUNDERSTORM", "Thunderstorm" } ,
+ { MO_ABSORBSPIRITS, "MO_ABSORBSPIRITS", "Spiritual_Sphere_Absorption" } ,
+ { MO_BALKYOUNG, "MO_BALKYOUNG", "Ki_Explosion" } ,
+ { MO_BLADESTOP, "MO_BLADESTOP", "Root" } ,
+ { MO_BODYRELOCATION, "MO_BODYRELOCATION", "Snap" } ,
+ { MO_CALLSPIRITS, "MO_CALLSPIRITS", "Summon_Spirit_Sphere" } ,
+ { MO_CHAINCOMBO, "MO_CHAINCOMBO", "Raging_Quadruple_Blow" } ,
+ { MO_COMBOFINISH, "MO_COMBOFINISH", "Raging_Thrust" } ,
+ { MO_DODGE, "MO_DODGE", "Flee" } ,
+ { MO_EXPLOSIONSPIRITS, "MO_EXPLOSIONSPIRITS", "Fury" } ,
+ { MO_EXTREMITYFIST, "MO_EXTREMITYFIST", "Guillotine_Fist" } ,
+ { MO_FINGEROFFENSIVE, "MO_FINGEROFFENSIVE", "Throw_Spirit_Sphere" } ,
+ { MO_INVESTIGATE, "MO_INVESTIGATE", "Occult_Impaction" } ,
+ { MO_IRONHAND, "MO_IRONHAND", "Iron_Fists" } ,
+ { MO_KITRANSLATION, "MO_KITRANSLATION", "Ki_Translation" } ,
+ { MO_SPIRITSRECOVERY, "MO_SPIRITSRECOVERY", "Spiritual_Cadence" } ,
+ { MO_STEELBODY, "MO_STEELBODY", "Mental_Strength" } ,
+ { MO_TRIPLEATTACK, "MO_TRIPLEATTACK", "Raging_Trifecta_Blow" } ,
+ { NPC_AGIUP, "NPC_AGIUP", "NPC_AGIUP" } ,
+ { NPC_ATTRICHANGE, "NPC_ATTRICHANGE", "NPC_ATTRICHANGE" } ,
+ { NPC_BARRIER, "NPC_BARRIER", "NPC_BARRIER" } ,
+ { NPC_BLINDATTACK, "NPC_BLINDATTACK", "NPC_BLINDATTACK" } ,
+ { NPC_BLOODDRAIN, "NPC_BLOODDRAIN", "NPC_BLOODDRAIN" } ,
+ { NPC_BREAKARMOR, "NPC_BREAKARMOR", "NPC_BREAKARMOR" } ,
+ { NPC_BREAKHELM, "NPC_BREAKHELM", "NPC_BREAKHELM" } ,
+ { NPC_BREAKSHIELD, "NPC_BREAKSHIELD", "NPC_BREAKSHIELD" } ,
+ { NPC_BREAKWEAPON, "NPC_BREAKWEAPON", "NPC_BREAKWEAPON" } ,
+ { NPC_CALLSLAVE, "NPC_CALLSLAVE", "NPC_CALLSLAVE" } ,
+ { NPC_CHANGEDARKNESS, "NPC_CHANGEDARKNESS", "NPC_CHANGEDARKNESS" } ,
+ { NPC_CHANGEFIRE, "NPC_CHANGEFIRE", "NPC_CHANGEFIRE" } ,
+ { NPC_CHANGEGROUND, "NPC_CHANGEGROUND", "NPC_CHANGEGROUND" } ,
+ { NPC_CHANGEHOLY, "NPC_CHANGEHOLY", "NPC_CHANGEHOLY" } ,
+ { NPC_CHANGEPOISON, "NPC_CHANGEPOISON", "NPC_CHANGEPOISON" } ,
+ { NPC_CHANGETELEKINESIS, "NPC_CHANGETELEKINESIS", "NPC_CHANGETELEKINESIS" } ,
+ { NPC_CHANGEUNDEAD, "NPC_CHANGEUNDEAD", "NPC_CHANGEUNDEAD" } ,
+ { NPC_CHANGEWATER, "NPC_CHANGEWATER", "NPC_CHANGEWATER" } ,
+ { NPC_CHANGEWIND, "NPC_CHANGEWIND", "NPC_CHANGEWIND" } ,
+ { NPC_COMBOATTACK, "NPC_COMBOATTACK", "NPC_COMBOATTACK" } ,
+ { NPC_CRITICALSLASH, "NPC_CRITICALSLASH", "NPC_CRITICALSLASH" } ,
+ { NPC_CURSEATTACK, "NPC_CURSEATTACK", "NPC_CURSEATTACK" } ,
+ { NPC_DARKBLESSING, "NPC_DARKBLESSING", "NPC_DARKBLESSING" } ,
+ { NPC_DARKBREATH, "NPC_DARKBREATH", "NPC_DARKBREATH" } ,
+ { NPC_DARKCROSS, "NPC_DARKCROSS", "NPC_DARKCROSS" } ,
+ { NPC_DARKNESSATTACK, "NPC_DARKNESSATTACK", "NPC_DARKNESSATTACK" } ,
+ { NPC_DARKSTRIKE, "NPC_DARKSTRIKE", "NPC_DARKSTRIKE" } ,
+ { NPC_DARKTHUNDER, "NPC_DARKTHUNDER", "NPC_DARKTHUNDER" } ,
+ { NPC_DEFENDER, "NPC_DEFENDER", "NPC_DEFENDER" } ,
+ { NPC_EMOTION, "NPC_EMOTION", "NPC_EMOTION" } ,
+ { NPC_EMOTION_ON, "NPC_EMOTION_ON", "NPC_EMOTION_ON" } ,
+ { NPC_ENERGYDRAIN, "NPC_ENERGYDRAIN", "NPC_ENERGYDRAIN" } ,
+ { NPC_FIREATTACK, "NPC_FIREATTACK", "NPC_FIREATTACK" } ,
+ { NPC_GRANDDARKNESS, "NPC_GRANDDARKNESS", "NPC_GRANDDARKNESS" } ,
+ { NPC_GROUNDATTACK, "NPC_GROUNDATTACK", "NPC_GROUNDATTACK" } ,
+ { NPC_GUIDEDATTACK, "NPC_GUIDEDATTACK", "NPC_GUIDEDATTACK" } ,
+ { NPC_HALLUCINATION, "NPC_HALLUCINATION", "NPC_HALLUCINATION" } ,
+ { NPC_HOLYATTACK, "NPC_HOLYATTACK", "NPC_HOLYATTACK" } ,
+ { NPC_INVISIBLE, "NPC_INVISIBLE", "NPC_INVISIBLE" } ,
+ { NPC_KEEPING, "NPC_KEEPING", "NPC_KEEPING" } ,
+ { NPC_LICK, "NPC_LICK", "NPC_LICK" } ,
+ { NPC_MAGICALATTACK, "NPC_MAGICALATTACK", "NPC_MAGICALATTACK" } ,
+ { NPC_MENTALBREAKER, "NPC_MENTALBREAKER", "NPC_MENTALBREAKER" } ,
+ { NPC_METAMORPHOSIS, "NPC_METAMORPHOSIS", "NPC_METAMORPHOSIS" } ,
+ { NPC_PETRIFYATTACK, "NPC_PETRIFYATTACK", "NPC_PETRIFYATTACK" } ,
+ { NPC_PIERCINGATT, "NPC_PIERCINGATT", "NPC_PIERCINGATT" } ,
+ { NPC_POISON, "NPC_POISON", "NPC_POISON" } ,
+ { NPC_POISONATTACK, "NPC_POISONATTACK", "NPC_POISONATTACK" } ,
+ { NPC_POWERUP, "NPC_POWERUP", "NPC_POWERUP" } ,
+ { NPC_PROVOCATION, "NPC_PROVOCATION", "NPC_PROVOCATION" } ,
+ { NPC_RANDOMATTACK, "NPC_RANDOMATTACK", "NPC_RANDOMATTACK" } ,
+ { NPC_RANDOMMOVE, "NPC_RANDOMMOVE", "NPC_RANDOMMOVE" } ,
+ { NPC_RANGEATTACK, "NPC_RANGEATTACK", "NPC_RANGEATTACK" } ,
+ { NPC_REBIRTH, "NPC_REBIRTH", "NPC_REBIRTH" } ,
+ { NPC_REVENGE, "NPC_REVENGE", "NPC_REVENGE" } ,
+ { NPC_RUN, "NPC_RUN", "NPC_RUN" },
+ { NPC_SELFDESTRUCTION, "NPC_SELFDESTRUCTION", "Kabooooom!" } ,
+ { NPC_SIEGEMODE, "NPC_SIEGEMODE", "NPC_SIEGEMODE" } ,
+ { NPC_SILENCEATTACK, "NPC_SILENCEATTACK", "NPC_SILENCEATTACK" } ,
+ { NPC_SLEEPATTACK, "NPC_SLEEPATTACK", "NPC_SLEEPATTACK" } ,
+ { NPC_SMOKING, "NPC_SMOKING", "NPC_SMOKING" } ,
+ { NPC_SPEEDUP, "NPC_SPEEDUP", "NPC_SPEEDUP" } ,
+ { NPC_SPLASHATTACK, "NPC_SPLASHATTACK", "NPC_SPLASHATTACK" } ,
+ { NPC_STOP, "NPC_STOP", "NPC_STOP" } ,
+ { NPC_STUNATTACK, "NPC_STUNATTACK", "NPC_STUNATTACK" } ,
+ { NPC_SUICIDE, "NPC_SUICIDE", "NPC_SUICIDE" } ,
+ { NPC_SUMMONMONSTER, "NPC_SUMMONMONSTER", "NPC_SUMMONMONSTER" } ,
+ { NPC_SUMMONSLAVE, "NPC_SUMMONSLAVE", "NPC_SUMMONSLAVE" } ,
+ { NPC_TELEKINESISATTACK, "NPC_TELEKINESISATTACK", "NPC_TELEKINESISATTACK" } ,
+ { NPC_TRANSFORMATION, "NPC_TRANSFORMATION", "NPC_TRANSFORMATION" } ,
+ { NPC_UNDEADATTACK, "NPC_UNDEADATTACK", "NPC_UNDEADATTACK" } ,
+ { NPC_WATERATTACK, "NPC_WATERATTACK", "NPC_WATERATTACK" } ,
+ { NPC_WINDATTACK, "NPC_WINDATTACK", "NPC_WINDATTACK" } ,
+ { NV_BASIC, "NV_BASIC", "Basic_Skill" } ,
+ { NV_FIRSTAID, "NV_FIRSTAID", "First Aid" } ,
+ { NV_TRICKDEAD, "NV_TRICKDEAD", "Play_Dead" } ,
+ { PA_GOSPEL, "PA_GOSPEL", "Battle_Chant" } ,
+ { PA_PRESSURE, "PA_PRESSURE", "Gloria_Domini" } ,
+ { PA_SACRIFICE, "PA_SACRIFICE", "Martyr's_Reckoning" } ,
+ { PA_SHIELDCHAIN, "PA_SHIELDCHAIN", "Shield_Chain" } ,
+ { PF_DOUBLECASTING, "PF_DOUBLECASTING", "Double_Casting" } ,
+ { PF_FOGWALL, "PF_FOGWALL", "Blinding_Mist" } ,
+ { PF_HPCONVERSION, "PF_HPCONVERSION", "Indulge" } ,
+ { PF_MEMORIZE, "PF_MEMORIZE", "Foresight" } ,
+ { PF_MINDBREAKER, "PF_MINDBREAKER", "Mind_Breaker" } ,
+ { PF_SOULBURN, "PF_SOULBURN", "Soul_Siphon" } ,
+ { PF_SOULCHANGE, "PF_SOULCHANGE", "Soul_Exhale" } ,
+ { PF_SPIDERWEB, "PF_SPIDERWEB", "Fiber_Lock" } ,
+ { PR_ASPERSIO, "PR_ASPERSIO", "Aspersio" } ,
+ { PR_BENEDICTIO, "PR_BENEDICTIO", "B.S_Sacramenti" } ,
+ { PR_GLORIA, "PR_GLORIA", "Gloria" } ,
+ { PR_IMPOSITIO, "PR_IMPOSITIO", "Impositio_Manus" } ,
+ { PR_KYRIE, "PR_KYRIE", "Kyrie_Eleison" } ,
+ { PR_LEXAETERNA, "PR_LEXAETERNA", "Lex_Aeterna" } ,
+ { PR_LEXDIVINA, "PR_LEXDIVINA", "Lex_Divina" } ,
+ { PR_MACEMASTERY, "PR_MACEMASTERY", "Mace_Mastery" } ,
+ { PR_MAGNIFICAT, "PR_MAGNIFICAT", "Magnificat" } ,
+ { PR_MAGNUS, "PR_MAGNUS", "Magnus_Exorcismus" } ,
+ { PR_REDEMPTIO, "PR_REDEMPTIO", "Redemptio" } ,
+ { PR_SANCTUARY, "PR_SANCTUARY", "Sanctuary" } ,
+ { PR_SLOWPOISON, "PR_SLOWPOISON", "Slow_Poison" } ,
+ { PR_STRECOVERY, "PR_STRECOVERY", "Status_Recovery" } ,
+ { PR_SUFFRAGIUM, "PR_SUFFRAGIUM", "Suffragium" } ,
+ { PR_TURNUNDEAD, "PR_TURNUNDEAD", "Turn_Undead" } ,
+ { RG_BACKSTAP, "RG_BACKSTAP", "Back_Stab" } ,
+ { RG_CLEANER, "RG_CLEANER", "Remover" } ,
+ { RG_CLOSECONFINE, "RG_CLOSECONFINE", "Close_Confine"} ,
+ { RG_COMPULSION, "RG_COMPULSION", "Haggle" } ,
+ { RG_FLAGGRAFFITI, "RG_FLAGGRAFFITI", "Piece" } ,
+ { RG_GANGSTER, "RG_GANGSTER", "Slyness" } ,
+ { RG_GRAFFITI, "RG_GRAFFITI", "Scribble" } ,
+ { RG_INTIMIDATE, "RG_INTIMIDATE", "Snatch" } ,
+ { RG_PLAGIARISM, "RG_PLAGIARISM", "Intimidate" } ,
+ { RG_RAID, "RG_RAID", "Sightless_Mind" } ,
+ { RG_SNATCHER, "RG_SNATCHER", "Gank" } ,
+ { RG_STEALCOIN, "RG_STEALCOIN", "Mug" } ,
+ { RG_STRIPARMOR, "RG_STRIPARMOR", "Divest_Armor" } ,
+ { RG_STRIPHELM, "RG_STRIPHELM", "Divest_Helm" } ,
+ { RG_STRIPSHIELD, "RG_STRIPSHIELD", "Divest_Shield" } ,
+ { RG_STRIPWEAPON, "RG_STRIPWEAPON", "Divest_Weapon" } ,
+ { RG_TUNNELDRIVE, "RG_TUNNELDRIVE", "Stalk" } ,
+ { SA_ABRACADABRA, "SA_ABRACADABRA", "Hocus-pocus" } ,
+ { SA_ADVANCEDBOOK, "SA_ADVANCEDBOOK", "Advanced_Book" } ,
+ { SA_AUTOSPELL, "SA_AUTOSPELL", "Hindsight" } ,
+ { SA_CASTCANCEL, "SA_CASTCANCEL", "Cast_Cancel" } ,
+ { SA_CLASSCHANGE, "SA_CLASSCHANGE", "Class_Change" } ,
+ { SA_CREATECON, "SA_CREATECON", "Create_Elemental_Converter" } ,
+ { SA_COMA, "SA_COMA", "Coma" } ,
+ { SA_DEATH, "SA_DEATH", "Grim_Reaper" } ,
+ { SA_DELUGE, "SA_DELUGE", "Deluge" } ,
+ { SA_DISPELL, "SA_DISPELL", "Dispell" } ,
+ { SA_DRAGONOLOGY, "SA_DRAGONOLOGY", "Dragonology" } ,
+ { SA_ELEMENTFIRE, "SA_ELEMENTFIRE", "Elemental_Change_Fire" } ,
+ { SA_ELEMENTGROUND, "SA_ELEMENTGROUND", "Elemental_Change_Earth" } ,
+ { SA_ELEMENTWATER, "SA_ELEMENTWATER", "Elemental_Change_Water" } ,
+ { SA_ELEMENTWIND, "SA_ELEMENTWIND", "Elemental_Change_Wind" } ,
+ { SA_FLAMELAUNCHER, "SA_FLAMELAUNCHER", "Endow_Blaze" } ,
+ { SA_FORTUNE, "SA_FORTUNE", "Gold_Digger" } ,
+ { SA_FREECAST, "SA_FREECAST", "Free_Cast" } ,
+ { SA_FROSTWEAPON, "SA_FROSTWEAPON", "Endow_Tsunami" } ,
+ { SA_FULLRECOVERY, "SA_FULLRECOVERY", "Rejuvenation" } ,
+ { SA_GRAVITY, "SA_GRAVITY", "Gravity" } ,
+ { SA_INSTANTDEATH, "SA_INSTANTDEATH", "Suicide" } ,
+ { SA_LANDPROTECTOR, "SA_LANDPROTECTOR", "Magnetic_Earth" } ,
+ { SA_LEVELUP, "SA_LEVELUP", "Leveling" } ,
+ { SA_LIGHTNINGLOADER, "SA_LIGHTNINGLOADER", "Endow_Tornado" } ,
+ { SA_MAGICROD, "SA_MAGICROD", "Magic_Rod" } ,
+ { SA_MONOCELL, "SA_MONOCELL", "Mono_Cell" } ,
+ { SA_QUESTION, "SA_QUESTION", "Questioning" } ,
+ { SA_REVERSEORCISH, "SA_REVERSEORCISH", "Grampus_Morph" } ,
+ { SA_SEISMICWEAPON, "SA_SEISMICWEAPON", "Endow_Quake" } ,
+ { SA_SPELLBREAKER, "SA_SPELLBREAKER", "Spell_Breaker" } ,
+ { SA_SUMMONMONSTER, "SA_SUMMONMONSTER", "Monster_Chant" } ,
+ { SA_TAMINGMONSTER, "SA_TAMINGMONSTER", "Beastly_Hypnosis" } ,
+ { SA_VIOLENTGALE, "SA_VIOLENTGALE", "Whirlwind" } ,
+ { SA_VOLCANO, "SA_VOLCANO", "Volcano" } ,
+ { SG_DEVIL, "SG_DEVIL", "Devil_of_the_Sun,_Moon_and_Stars" } ,
+ { SG_FEEL, "SG_FEEL", "Feeling_of_the_Sun,_Moon_and_Star" } ,
+ { SG_FRIEND, "SG_FRIEND", "Companion_of_the_Sun_and_Moon" } ,
+ { SG_FUSION, "SG_FUSION", "Union_of_the_Sun,_Moon_and_Stars" } ,
+ { SG_HATE, "SG_HATE", "Hatred_of_the_Sun,_Moon_and_Stars" } ,
+ { SG_KNOWLEDGE, "SG_KNOWLEDGE", "Knowledge_of_the_Sun,_Moon_and_Stars" } ,
+ { SG_MOON_ANGER, "SG_MOON_ANGER", "Fury_of_the_Moon" } ,
+ { SG_MOON_BLESS, "SG_MOON_BLESS", "Bless_of_the_Moon" } ,
+ { SG_MOON_COMFORT, "SG_MOON_COMFORT", "Comfort_of_the_Moon" } ,
+ { SG_MOON_WARM, "SG_MOON_WARM", "Warmth_of_the_Moon" } ,
+ { SG_STAR_ANGER, "SG_STAR_ANGER", "Fury_of_the_Stars" } ,
+ { SG_STAR_BLESS, "SG_STAR_BLESS", "Bless_of_the_Stars" } ,
+ { SG_STAR_COMFORT, "SG_STAR_COMFORT", "Comfort_of_the_Stars" } ,
+ { SG_STAR_WARM, "SG_STAR_WARM", "Warmth_of_the_Stars" } ,
+ { SG_SUN_ANGER, "SG_SUN_ANGER", "Fury_of_the_Sun" } ,
+ { SG_SUN_BLESS, "SG_SUN_BLESS", "Bless_of_the_Sun" } ,
+ { SG_SUN_COMFORT, "SG_SUN_COMFORT", "Comfort_of_the_Sun" } ,
+ { SG_SUN_WARM, "SG_SUN_WARM", "Warmth_of_the_Sun" } ,
+ { SL_ALCHEMIST, "SL_ALCHEMIST", "Spirit_of_Alchemist" } ,
+ { SL_ASSASIN, "SL_ASSASIN", "Spirit_of_Assassin" } ,
+ { SL_BARDDANCER, "SL_BARDDANCER", "Spirit_of_Bard_and_Dancer" } ,
+ { SL_BLACKSMITH, "SL_BLACKSMITH", "Spirit_of_Blacksmith" } ,
+ { SL_CRUSADER, "SL_CRUSADER", "Spirit_of_Crusader" } ,
+ { SL_HIGH, "SL_HIGH", "Spirit_of_Advanced_1st_Class" } ,
+ { SL_HUNTER, "SL_HUNTER", "Spirit_of_Hunter" } ,
+ { SL_KAAHI, "SL_KAAHI", "Kaahi" } ,
+ { SL_KAINA, "SL_KAINA", "Kaina" } ,
+ { SL_KAITE, "SL_KAITE", "Kaite" } ,
+ { SL_KAIZEL, "SL_KAIZEL", "Kaizel" } ,
+ { SL_KAUPE, "SL_KAUPE", "Kaupe" } ,
+ { SL_KNIGHT, "SL_KNIGHT", "Spirit_of_Knight" } ,
+ { SL_MONK, "SL_MONK", "Spirit_of_Monk" } ,
+ { SL_PRIEST, "SL_PRIEST", "Spirit_of_Priest" } ,
+ { SL_ROGUE, "SL_ROGUE", "Spirit_of_Rogue" } ,
+ { SL_SAGE, "SL_SAGE", "Spirit_of_Sage" } ,
+ { SL_SKA, "SL_SKA", "Eska" } ,
+ { SL_SKE, "SL_SKE", "Eske" } ,
+ { SL_SMA, "SL_SMA", "Esma" } ,
+ { SL_SOULLINKER, "SL_SOULLINKER", "Spirit_of_Soul_Linker" } ,
+ { SL_STAR, "SL_STAR", "Spirit_of_Stars" } ,
+ { SL_STIN, "SL_STIN", "Estin" } ,
+ { SL_STUN, "SL_STUN", "Estun" } ,
+ { SL_SUPERNOVICE, "SL_SUPERNOVICE", "Spirit_of_Super_Novice" } ,
+ { SL_SWOO, "SL_SWOO", "Eswoo" } ,
+ { SL_WIZARD, "SL_WIZARD", "Spirit_of_Wizard" } ,
+ { SM_AUTOBERSERK, "SM_AUTOBERSERK", "Berserk" } ,
+ { SM_BASH, "SM_BASH", "Bash" } ,
+ { SM_ENDURE, "SM_ENDURE", "Endure" } ,
+ { SM_FATALBLOW, "SM_FATALBLOW", "Fatal_Blow" } ,
+ { SM_MAGNUM, "SM_MAGNUM", "Magnum_Break" } ,
+ { SM_MOVINGRECOVERY, "SM_MOVINGRECOVERY", "HP_Recovery_While_Moving" } ,
+ { SM_PROVOKE, "SM_PROVOKE", "Provoke" } ,
+ { SM_RECOVERY, "SM_RECOVERY", "Increase_HP_Recovery" } ,
+ { SM_SWORD, "SM_SWORD", "Sword_Mastery" } ,
+ { SM_TWOHAND, "SM_TWOHAND", "Two-Handed_Sword_Mastery" } ,
+ { SN_FALCONASSAULT, "SN_FALCONASSAULT", "Falcon_Assault" } ,
+ { SN_SHARPSHOOTING, "SN_SHARPSHOOTING", "Focused_Arrow_Strike" } ,
+ { SN_SIGHT, "SN_SIGHT", "Falcon_Eyes" } ,
+ { SN_WINDWALK, "SN_WINDWALK", "Wind_Walker" } ,
+ { ST_CHASEWALK, "ST_CHASEWALK", "Stealth" } ,
+ { ST_REJECTSWORD, "ST_REJECTSWORD", "Counter_Instinct" } ,
+ { ST_PRESERVE, "ST_PRESERVE", "Preserve" } ,
+ { ST_FULLSTRIP, "ST_FULLSTRIP", "Full_Divestment" } ,
+ { TF_BACKSLIDING, "TF_BACKSLIDING", "Back_Slide" } ,
+ { TF_DETOXIFY, "TF_DETOXIFY", "Detoxify" } ,
+ { TF_DOUBLE, "TF_DOUBLE", "Double_Attack" } ,
+ { TF_HIDING, "TF_HIDING", "Hiding" } ,
+ { TF_MISS, "TF_MISS", "Improve_Dodge" } ,
+ { TF_PICKSTONE, "TF_PICKSTONE", "Find_Stone" } ,
+ { TF_POISON, "TF_POISON", "Envenom" } ,
+ { TF_SPRINKLESAND, "TF_SPRINKLESAND", "Sand_Attack" } ,
+ { TF_STEAL, "TF_STEAL", "Steal" } ,
+ { TF_THROWSTONE, "TF_THROWSTONE", "Stone_Fling" } ,
+ { TK_COUNTER, "TK_COUNTER", "Spin_Kick" } ,
+ { TK_DODGE, "TK_DODGE", "Sprint" } ,
+ { TK_DOWNKICK, "TK_DOWNKICK", "Heel_Drop" } ,
+ { TK_HIGHJUMP, "TK_HIGHJUMP", "Taekwon_Jump" } ,
+ { TK_HPTIME, "TK_HPTIME", "Peaceful_Break" } ,
+ { TK_JUMPKICK, "TK_JUMPKICK", "Flying_Kick" } ,
+ { TK_MISSION, "TK_MISSION", "Mission" } ,
+ { TK_POWER, "TK_POWER", "Kihop" } ,
+ { TK_READYCOUNTER, "TK_READYCOUNTER", "Spin_Kick_Stance" } ,
+ { TK_READYDOWN, "TK_READYDOWN", "Heel_Drop_Stance" } ,
+ { TK_READYSTORM, "TK_READYSTORM", "Tornado_Stance" } ,
+ { TK_READYTURN, "TK_READYTURN", "Roundhouse_Stance" } ,
+ { TK_RUN, "TK_RUN", "Sprint" } ,
+ { TK_SEVENWIND, "TK_SEVENWIND", "Mild_Wind" } ,
+ { TK_SPTIME, "TK_SPTIME", "Happy_Break" } ,
+ { TK_STORMKICK, "TK_STORMKICK", "Storm_Kick" } ,
+ { TK_TURNKICK, "TK_TURNKICK", "Turn_Kick" } ,
+ { WE_BABY, "WE_BABY", "Mom,_Dad,_I_love_you!" } ,
+ { WE_CALLBABY, "WE_CALLBABY", "Come_to_me,_honey~" } ,
+ { WE_CALLPARENT, "WE_CALLPARENT", "Mom,_Dad,_I_miss_you!" } ,
+ { WE_CALLPARTNER, "WE_CALLPARTNER", "Romantic_Rendezvous" } ,
+ { WE_FEMALE, "WE_FEMALE", "Loving_Touch" } ,
+ { WE_MALE, "WE_MALE", "Undying_Love" } ,
+ { WS_CARTBOOST, "WS_CARTBOOST", "Cart_Boost" } ,
+ { WS_CARTTERMINATION, "WS_CARTTERMINATION", "Cart_Termination" } ,
+ { WS_CREATECOIN, "WS_CREATECOIN", "Coin_Craft" } ,
+ { WS_CREATENUGGET, "WS_CREATENUGGET", "Nugget_Craft" } ,
+ { WS_MELTDOWN, "WS_MELTDOWN", "Shattering_Strike" } ,
+ { WS_OVERTHRUSTMAX, "WS_OVERTHRUSTMAX", "Max_Power-Thust" } ,
+ { WS_SYSTEMCREATE, "WS_SYSTEMCREATE", "Auto_Attacking_Machine_Craft" } ,
+ { WS_WEAPONREFINE, "WS_WEAPONREFINE", "Weapon_Refine" } ,
+ { WZ_EARTHSPIKE, "WZ_EARTHSPIKE", "Earth_Spike" } ,
+ { WZ_ESTIMATION, "WZ_ESTIMATION", "Sense" } ,
+ { WZ_FIREPILLAR, "WZ_FIREPILLAR", "Fire_Pillar" } ,
+ { WZ_FROSTNOVA, "WZ_FROSTNOVA", "Frost_Nova" } ,
+ { WZ_HEAVENDRIVE, "WZ_HEAVENDRIVE", "Heaven's_Drive" } ,
+ { WZ_ICEWALL, "WZ_ICEWALL", "Ice_Wall" } ,
+ { WZ_JUPITEL, "WZ_JUPITEL", "Jupitel_Thunder" } ,
+ { WZ_METEOR, "WZ_METEOR", "Meteor_Storm" } ,
+ { WZ_QUAGMIRE, "WZ_QUAGMIRE", "Quagmire" } ,
+ { WZ_SIGHTBLASTER, "WZ_SIGHTBLASTER", "Sight_Blaster" } ,
+ { WZ_SIGHTRASHER, "WZ_SIGHTRASHER", "Sightrasher" } ,
+ { WZ_STORMGUST, "WZ_STORMGUST", "Storm_Gust" } ,
+ { WZ_VERMILION, "WZ_VERMILION", "Lord_of_Vermilion" } ,
+ { WZ_WATERBALL, "WZ_WATERBALL", "Water_Ball" } ,
+ { 0, "UNKNOWN_SKILL", "Unknown_Skill" }
+};
+
+static const int dirx[8]={0,-1,-1,-1,0,1,1,1};
+static const int diry[8]={1,1,0,-1,-1,-1,0,1};
+
+static int rdamage;
+
+/* スキルデ?タベ?ス */
+struct skill_db skill_db[MAX_SKILL_DB];
+
+/* アイテム??ャデ?タベ?ス */
+struct skill_produce_db skill_produce_db[MAX_SKILL_PRODUCE_DB];
+
+/* 矢??ャスキルデ?タベ?ス */
+struct skill_arrow_db skill_arrow_db[MAX_SKILL_ARROW_DB];
+
+/* アブラカダブラ?動スキルデ?タベ?ス */
+struct skill_abra_db skill_abra_db[MAX_SKILL_ABRA_DB];
+
+// macros to check for out of bounds errors [celest]
+// i: Skill ID, l: Skill Level, var: Value to return after checking
+// for values that don't require level just put a one (putting 0 will trigger return 0; instead
+// for values that might need to use a different function just skill_chk would suffice.
+#define skill_chk(i, l) \
+ if (i >= 10000 && i < 10015) {i -= 9500;} \
+ if (i < 1 || i > MAX_SKILL_DB) {return 0;} \
+ if (l <= 0 || l > MAX_SKILL_LEVEL) {return 0;}
+#define skill_get(var, i, l) \
+ { skill_chk(i, l); return var; }
+
+// Skill DB
+int skill_get_hit( int id ){ skill_get (skill_db[id].hit, id, 1); }
+int skill_get_inf( int id ){ skill_chk (id, 1); return (id < 500 || id > 1000) ? skill_db[id].inf : guild_skill_get_inf(id); }
+int skill_get_pl( int id ){ skill_get (skill_db[id].pl, id, 1); }
+int skill_get_nk( int id ){ skill_get (skill_db[id].nk, id, 1); }
+int skill_get_max( int id ){ skill_chk (id, 1); return (id < 500 || id > 1000) ? skill_db[id].max : guild_skill_get_max(id); }
+int skill_get_range( int id , int lv ){ skill_chk (id, lv); return (id < 500 || id > 1000) ? skill_db[id].range[lv-1] : 0; }
+int skill_get_hp( int id ,int lv ){ skill_get (skill_db[id].hp[lv-1], id, lv); }
+int skill_get_sp( int id ,int lv ){ skill_get (skill_db[id].sp[lv-1], id, lv); }
+int skill_get_zeny( int id ,int lv ){ skill_get (skill_db[id].zeny[lv-1], id, lv); }
+int skill_get_num( int id ,int lv ){ skill_get (skill_db[id].num[lv-1], id, lv); }
+int skill_get_cast( int id ,int lv ){ skill_get (skill_db[id].cast[lv-1], id, lv); }
+int skill_get_delay( int id ,int lv ){ skill_get (skill_db[id].delay[lv-1], id, lv); }
+int skill_get_time( int id ,int lv ){ skill_get (skill_db[id].upkeep_time[lv-1], id, lv); }
+int skill_get_time2( int id ,int lv ){ skill_get (skill_db[id].upkeep_time2[lv-1], id, lv); }
+int skill_get_castdef( int id ){ skill_get (skill_db[id].cast_def_rate, id, 1); }
+int skill_get_weapontype( int id ){ skill_get (skill_db[id].weapon, id, 1); }
+int skill_get_inf2( int id ){ skill_get (skill_db[id].inf2, id, 1); }
+int skill_get_castcancel( int id ){ skill_get (skill_db[id].castcancel, id, 1); }
+int skill_get_maxcount( int id ){ skill_get (skill_db[id].maxcount, id, 1); }
+int skill_get_blewcount( int id ,int lv ){ skill_get (skill_db[id].blewcount[lv-1], id, lv); }
+int skill_get_mhp( int id ,int lv ){ skill_get (skill_db[id].mhp[lv-1], id, lv); }
+int skill_get_castnodex( int id ,int lv ){ skill_get (skill_db[id].castnodex[lv-1], id, lv); }
+int skill_get_delaynodex( int id ,int lv ){ skill_get (skill_db[id].delaynodex[lv-1], id, lv); }
+int skill_get_delaynowalk( int id ,int lv ){ skill_get (skill_db[id].delaynowalk[lv-1], id, lv); }
+int skill_get_nocast ( int id ){ skill_get (skill_db[id].nocast, id, 1); }
+int skill_get_type( int id ){ skill_get (skill_db[id].skill_type, id, 1); }
+int skill_get_unit_id ( int id, int flag ){ skill_get (skill_db[id].unit_id[flag], id, 1); }
+int skill_get_unit_layout_type( int id ,int lv ){ skill_get (skill_db[id].unit_layout_type[lv-1], id, lv); }
+int skill_get_unit_interval( int id ){ skill_get (skill_db[id].unit_interval, id, 1); }
+int skill_get_unit_range( int id ){ skill_get (skill_db[id].unit_range, id, 1); }
+int skill_get_unit_target( int id ){ skill_get ((skill_db[id].unit_target&BCT_ALL), id, 1); }
+int skill_get_unit_bl_target( int id ){ skill_get ((skill_db[id].unit_target&BL_ALL), id, 1); }
+int skill_get_unit_flag( int id ){ skill_get (skill_db[id].unit_flag, id, 1); }
+const char* skill_get_name( int id ){
+ if (id >= 10000 && id < 10015) id -= 9500;
+ if (id < 1 || id > MAX_SKILL_DB || skill_db[id].name==NULL) return "UNKNOWN_SKILL"; //Can't use skill_chk because we return a string.
+ return skill_db[id].name;
+}
+
+int skill_tree_get_max(int id, int b_class){
+ int i, skillid;
+ for(i=0;(skillid=skill_tree[b_class][i].id)>0;i++)
+ if (id == skillid) return skill_tree[b_class][i].max;
+ return skill_get_max (id);
+}
+
+/* プ?トタイプ */
+int skill_check_condition( struct map_session_data *sd,int type);
+int skill_castend_damage_id( struct block_list* src, struct block_list *bl,int skillid,int skilllv,unsigned int tick,int flag );
+int skill_frostjoke_scream(struct block_list *bl,va_list ap);
+int status_change_timer_sub(struct block_list *bl, va_list ap);
+int skill_attack_area(struct block_list *bl,va_list ap);
+int skill_clear_element_field(struct block_list *bl);
+struct skill_unit_group *skill_locate_element_field(struct block_list *bl); // [Skotlex]
+int skill_graffitiremover(struct block_list *bl, va_list ap); // [Valaris]
+int skill_greed(struct block_list *bl, va_list ap);
+int skill_landprotector(struct block_list *bl, va_list ap);
+int skill_ganbatein(struct block_list *bl, va_list ap);
+int skill_trap_splash(struct block_list *bl, va_list ap);
+int skill_count_target(struct block_list *bl, va_list ap);
+struct skill_unit_group_tickset *skill_unitgrouptickset_search(struct block_list *bl,struct skill_unit_group *sg,int tick);
+static int skill_unit_onplace(struct skill_unit *src,struct block_list *bl,unsigned int tick);
+static int skill_unit_onleft(int skill_id, struct block_list *bl,unsigned int tick);
+int skill_unit_effect(struct block_list *bl,va_list ap);
+int skill_castend_delay (struct block_list* src, struct block_list *bl,int skillid,int skilllv,unsigned int tick,int flag);
+static void skill_moonlit(struct block_list* src, struct block_list* partner, int skilllv);
+static int skill_check_pc_partner(struct map_session_data *sd, int skill_id, int* skill_lv, int range, int cast_flag);
+
+int enchant_eff[5] = { 10, 14, 17, 19, 20 };
+int deluge_eff[5] = { 5, 9, 12, 14, 15 };
+
+//Returns actual skill range taking into account attack range and AC_OWL [Skotlex]
+int skill_get_range2(struct block_list *bl, int id, int lv) {
+ int range = skill_get_range(id, lv);
+ if(range < 0) {
+ if (battle_config.use_weapon_skill_range)
+ return status_get_range(bl);
+ range *=-1;
+ }
+ //TODO: Find a way better than hardcoding the list of skills affected by AC_VULTURE.
+ if (id == AC_SHOWER || id == AC_DOUBLE || id == HT_BLITZBEAT || id == AC_CHARGEARROW
+ || id == SN_FALCONASSAULT || id == SN_SHARPSHOOTING || id == HT_POWER) {
+ if (bl->type == BL_PC)
+ range += pc_checkskill((struct map_session_data *)bl, AC_VULTURE);
+ else
+ range += 10; //Assume level 10?
+ }
+ return range;
+}
+
+// Making plagiarize check its own function [Aru]
+int can_copy(struct map_session_data *sd, int skillid)
+{
+ // Never copy NPC/Wedding Skills
+ if (skill_get_inf2(skillid)&(INF2_NPC_SKILL|INF2_WEDDING_SKILL))
+ return 0;
+
+ // High-class skills
+ if((skillid >= LK_AURABLADE && skillid <= ASC_CDP) || (skillid >= ST_PRESERVE && skillid <= CR_CULTIVATION))
+ {
+ if(battle_config.copyskill_restrict == 2)
+ return 0;
+ else if(battle_config.copyskill_restrict)
+ return (sd->status.class_ == JOB_STALKER);
+ }
+
+ return 1;
+}
+
+// [MouseJstr] - skill ok to cast? and when?
+int skillnotok(int skillid, struct map_session_data *sd)
+{
+ nullpo_retr (1, sd);
+ //if (sd == 0)
+ //return 0;
+ //return 1;
+ // I think it was meant to be "no skills allowed when not a valid sd"
+
+ if (!(skillid >= 10000 && skillid < 10015))
+ if ((skillid > MAX_SKILL) || (skillid < 0))
+ return 1;
+
+ {
+ int i = skillid;
+ if (i >= 10000 && i < 10015)
+ i -= 9500;
+ if (sd->blockskill[i] > 0)
+ return 1;
+ }
+
+ if (pc_isGM(sd) >= 20)
+ return 0; // gm's can do anything damn thing they want
+
+ // Check skill restrictions [Celest]
+ if(!map_flag_vs(sd->bl.m) && skill_get_nocast (skillid) & 1)
+ return 1;
+ if(map[sd->bl.m].flag.pvp && skill_get_nocast (skillid) & 2)
+ return 1;
+ if(map_flag_gvg(sd->bl.m) && skill_get_nocast (skillid) & 4)
+ return 1;
+ if (agit_flag && skill_get_nocast (skillid) & 8)
+ return 1;
+ if (battle_config.pk_mode && !map[sd->bl.m].flag.nopvp && skill_get_nocast (skillid) & 16)
+ return 1;
+
+ switch (skillid) {
+ case AL_WARP:
+ case AL_TELEPORT:
+ case MC_VENDING:
+ case MC_IDENTIFY:
+ return 0; // always allowed
+ default:
+ return (map[sd->bl.m].flag.noskill);
+ }
+}
+
+/* スキルユニットの配置?報を返す */
+struct skill_unit_layout skill_unit_layout[MAX_SKILL_UNIT_LAYOUT];
+int firewall_unit_pos;
+int icewall_unit_pos;
+
+struct skill_unit_layout *skill_get_unit_layout (int skillid, int skilllv, struct block_list *src, int x, int y)
+{
+ int pos = skill_get_unit_layout_type(skillid,skilllv);
+ int dir;
+
+ if (pos != -1)
+ return &skill_unit_layout[pos];
+
+ if (src->x == x && src->y == y)
+ dir = 2;
+ else
+ dir = map_calc_dir(src,x,y);
+
+ if (skillid == MG_FIREWALL)
+ return &skill_unit_layout [firewall_unit_pos + dir];
+ else if (skillid == WZ_ICEWALL)
+ return &skill_unit_layout [icewall_unit_pos + dir];
+
+ ShowError("Unknown unit layout for skill %d, %d\n",skillid,skilllv);
+ return &skill_unit_layout[0];
+}
+
+/*==========================================
+ * スキル追加?果
+ *------------------------------------------
+ */
+int skill_additional_effect (struct block_list* src, struct block_list *bl, int skillid, int skilllv, int attack_type, unsigned int tick)
+{
+ /* MOB追加?果スキル用 */
+ const int sc[]={
+ SC_POISON, SC_BLIND, SC_SILENCE, SC_STAN,
+ SC_STONE, SC_CURSE, SC_SLEEP
+ };
+ const int sc2[]={ //Note: We use Sonic Blow's stun duration for the confusion lasting time (dummy value): 12 secs at lv7
+ MG_STONECURSE,MG_FROSTDIVER,NPC_STUNATTACK,
+ NPC_SLEEPATTACK,TF_POISON,NPC_CURSEATTACK,
+ NPC_SILENCEATTACK,AS_SONICBLOW,NPC_BLINDATTACK,
+ LK_HEADCRUSH
+ };
+
+ struct map_session_data *sd=NULL;
+ struct map_session_data *dstsd=NULL;
+ struct mob_data *md=NULL;
+ struct mob_data *dstmd=NULL;
+ struct pet_data *pd=NULL;
+
+ int skill,skill2;
+ int rate;
+
+ int sc_def_mdef,sc_def_vit,sc_def_int,sc_def_luk;
+
+ nullpo_retr(0, src);
+ nullpo_retr(0, bl);
+
+ if(skillid < 0)
+ { // remove the debug print when this case is finished
+ ShowDebug("skill_additional_effect: skillid=%i\ncall: %p %p %i %i %i %i",skillid,
+ src, bl,skillid,skilllv,attack_type,tick);
+ return 0;
+ }
+ if(skillid > 0 && skilllv <= 0) return 0; // don't forget auto attacks! - celest
+
+ switch (src->type) {
+ case BL_PC:
+ sd = (struct map_session_data *)src;
+ break;
+ case BL_MOB:
+ md = (struct mob_data *)src;
+ break;
+ case BL_PET:
+ pd = (struct pet_data *)src; // [Valaris]
+ break;
+ }
+
+ switch (bl->type) {
+ case BL_PC:
+ dstsd=(struct map_session_data *)bl;
+ break;
+ case BL_MOB:
+ dstmd=(struct mob_data *)bl;
+ break;
+ default:
+ return 0;
+ }
+
+ //??ロの耐?ォ
+ sc_def_mdef = status_get_sc_def_mdef(bl);
+ sc_def_vit = status_get_sc_def_vit(bl);
+ sc_def_int = status_get_sc_def_int(bl);
+ sc_def_luk = status_get_sc_def_luk(bl);
+
+ switch(skillid){
+ case 0: // Normal attacks (no skill used)
+ {
+ struct status_change *sc_data;
+ struct status_change *tsc_data;
+ if(sd) {
+ // Automatic trigger of Blitz Beat
+ if (pc_isfalcon(sd) && sd->status.weapon == 11 && (skill=pc_checkskill(sd,HT_BLITZBEAT))>0 &&
+ rand()%1000 <= sd->paramc[5]*10/3+1 ) {
+ int lv=(sd->status.job_level+9)/10;
+ skill_castend_damage_id(src,bl,HT_BLITZBEAT,(skill<lv)?skill:lv,tick,0xf00000);
+ }
+ // Gank
+ if(dstmd && !dstmd->state.steal_flag && sd->status.weapon != 11 && (skill=pc_checkskill(sd,RG_SNATCHER)) > 0 &&
+ (skill*15 + 55) + (skill2 = pc_checkskill(sd,TF_STEAL))*10 > rand()%1000) {
+ if(pc_steal_item(sd,bl))
+ clif_skill_nodamage(src,bl,TF_STEAL,skill2,1);
+ else if (battle_config.display_snatcher_skill_fail)
+ clif_skill_fail(sd,skillid,0,0);
+ }
+ // Chance to trigger Taekwon kicks [Dralnu]
+ if(sd->sc_data[SC_READYSTORM].timer != -1 && sd->sc_data[SC_COMBO].timer == -1 && rand()%100 < 15) {
+ rate = 2000 - 4 * status_get_agi(src) - 2 * status_get_dex(src);
+ status_change_start(src,SC_COMBO, TK_STORMKICK,0,0,0,rate,0);
+ } else if(sd->sc_data[SC_READYDOWN].timer != -1 && sd->sc_data[SC_COMBO].timer == -1 && rand()%100 < 15) {
+ rate = 2000 - 4 * status_get_agi(src) - 2 * status_get_dex(src);
+ status_change_start(src,SC_COMBO, TK_DOWNKICK,0,0,0,rate,0);
+ } else if(sd->sc_data[SC_READYTURN].timer != -1 && sd->sc_data[SC_COMBO].timer == -1 && rand()%100 < 15) {
+ rate = 2000 - 4 * status_get_agi(src) - 2 * status_get_dex(src);
+ status_change_start(src,SC_COMBO, TK_TURNKICK,0,0,0,rate,0);
+ } else if(sd->sc_data[SC_READYCOUNTER].timer != -1 && sd->sc_data[SC_COMBO].timer == -1) //additional chance from SG_FRIEND [Komurka]
+ {
+ rate = 20;
+ if (sd->sc_data[SC_SKILLRATE_UP].timer != -1 && sd->sc_data[SC_SKILLRATE_UP].val1 == TK_COUNTER) {
+ rate += rate*sd->sc_data[SC_SKILLRATE_UP].val2/100;
+ status_change_end(src,SC_SKILLRATE_UP,-1);
+ }
+ if (rand()%100 < rate) {
+ rate = 2000 - 4 * status_get_agi(src) - 2 * status_get_dex(src);
+ status_change_start(src,SC_COMBO, TK_COUNTER,bl->id,0,0,rate,0);
+ }
+ }
+ }
+
+ sc_data = status_get_sc_data(src);
+ tsc_data = status_get_sc_data(bl);
+
+ // Enchant Poison gives a chance to poison attacked enemies
+ if(sc_data && sc_data[SC_ENCPOISON].timer != -1 && tsc_data && tsc_data[SC_POISON].timer == -1 &&
+ rand() % 100 < sc_data[SC_ENCPOISON].val1 * sc_def_vit / 100)
+ status_change_start(bl,SC_POISON,sc_data[SC_ENCPOISON].val1,0,0,0,skill_get_time2(AS_ENCHANTPOISON,sc_data[SC_ENCPOISON].val1),0);
+ // Enchant Deadly Poison gives a chance to deadly poison attacked enemies
+ if(sc_data && sc_data[SC_EDP].timer != -1 && !(status_get_mode(bl)&MD_BOSS) && tsc_data && tsc_data[SC_DPOISON].timer == -1 &&
+ rand() % 100 < tsc_data[SC_EDP].val2 * sc_def_vit / 100)
+ status_change_start(bl,SC_DPOISON,sc_data[SC_EDP].val1,0,0,0,skill_get_time2(ASC_EDP,sc_data[SC_EDP].val1),0);
+
+ if (tsc_data && tsc_data[SC_KAAHI].timer != -1) {
+ if (dstsd && dstsd->status.sp < 5*tsc_data[SC_KAAHI].val1)
+ ; //Not enough SP to cast
+ else {
+ rate = battle_heal(bl, bl, 200*tsc_data[SC_KAAHI].val1, -5*tsc_data[SC_KAAHI].val1, 1);
+ if(dstsd && dstsd->fd)
+ clif_heal(dstsd->fd,SP_HP,rate);
+ }
+ }
+ }
+ break;
+
+ case SM_BASH: /* バッシュ?i急??U??j */
+ if( sd && skilllv > 5 && pc_checkskill(sd,SM_FATALBLOW)>0 ){
+ if( rand()%100 < (5*(skilllv-5)+(int)sd->status.base_level/10)*sc_def_vit/100 ) //TODO: How much % per base level it actually is?
+ status_change_start(bl,SC_STAN,skilllv,0,0,0,skill_get_time2(SM_FATALBLOW,skilllv),0);
+ }
+ break;
+
+ case AS_VENOMKNIFE:
+ if (sd) //Poison chance must be that of Envenom. [Skotlex]
+ skilllv = pc_checkskill(sd, TF_POISON);
+ case TF_POISON: /* インベナム */
+ case AS_SPLASHER: /* ベナムスプラッシャ? */
+ if(rand()%100< (2*skilllv+10)*sc_def_vit/100 )
+ status_change_start(bl,SC_POISON,skilllv,0,0,0,skill_get_time2(skillid,skilllv),0);
+ else{
+ if(sd && skillid==TF_POISON)
+ clif_skill_fail(sd,skillid,0,0);
+ }
+ break;
+
+ case AS_SONICBLOW: /* ソニックブ?? */
+ if( rand()%100 < (2*skilllv+10)*sc_def_vit/100 )
+ status_change_start(bl,SC_STAN,skilllv,0,0,0,skill_get_time2(skillid,skilllv),0);
+ break;
+
+ case AS_GRIMTOOTH:
+ {
+ struct status_change *sc_data = status_get_sc_data(bl);
+ if (sd)
+ {
+ if (sc_data && sc_data[SC_SLOWDOWN].timer == -1)
+ status_change_start(bl,SC_SLOWDOWN,0,0,0,0,skill_get_time2(skillid, skilllv),0);
+ }
+ else
+ if (sc_data && sc_data[SC_STOP].timer == -1)
+ status_change_start(bl,SC_STOP,0,0,0,0,skill_get_time2(skillid, skilllv), 0);
+ break;
+ }
+ case MG_FROSTDIVER: /* フ?ストダイバ? */
+ case WZ_FROSTNOVA: /* フ?ストノヴァ */
+ {
+ struct status_change *sc_data = status_get_sc_data(bl);
+ rate = (skilllv*3+35)*sc_def_mdef/100-(status_get_int(bl)+status_get_luk(bl))/15;
+ if (rate <= 5)
+ rate = 5;
+ if(sc_data && sc_data[SC_FREEZE].timer == -1 && rand()%100 < rate)
+ status_change_start(bl,SC_FREEZE,skilllv,0,0,0,skill_get_time2(skillid,skilllv)*(1-sc_def_mdef/100),0);
+ }
+ break;
+
+ case WZ_STORMGUST: /* スト?ムガスト */
+ {
+ struct status_change *sc_data = status_get_sc_data(bl);
+ if(sc_data) {
+ sc_data[SC_FREEZE].val3++;
+ if(sc_data[SC_FREEZE].val3 >= 3)
+ status_change_start(bl,SC_FREEZE,skilllv,0,0,0,skill_get_time2(skillid,skilllv),0);
+ }
+ }
+ break;
+
+ case WZ_METEOR:
+ if(rand()%100 < 3*skilllv*sc_def_vit/100)
+ status_change_start(bl,SC_STAN,skilllv,0,0,0,skill_get_time2(skillid,skilllv),0);
+ break;
+
+ case WZ_VERMILION:
+ if(rand()%100 < 4*skilllv*sc_def_int/100)
+ status_change_start(bl,SC_BLIND,skilllv,0,0,0,skill_get_time2(skillid,skilllv),0);
+ break;
+
+ case HT_FREEZINGTRAP: /* フリ?ジングトラップ */
+ if(rand()%100 < (3*skilllv+35)*sc_def_mdef/100)
+ status_change_start(bl,SC_FREEZE,skilllv,0,0,0,skill_get_time2(skillid,skilllv),0);
+ break;
+
+ case HT_FLASHER: /* Flasher */
+ if (!(status_get_mode(bl) & (MD_BOSS|MD_PLANT)) &&
+ rand()%100 < (10*skilllv+30)*sc_def_int/100)
+ status_change_start(bl,SC_BLIND,skilllv,0,0,0,skill_get_time2(skillid,skilllv),0);
+ break;
+
+ case HT_LANDMINE: /* ランドマイン */
+ if( rand()%100 < (5*skilllv+30)*sc_def_vit/100 )
+ status_change_start(bl,SC_STAN,skilllv,0,0,0,skill_get_time2(skillid,skilllv),0);
+ break;
+
+ case HT_SHOCKWAVE: //it can't affect mobs, because they have no SP...
+ if(dstsd){
+ dstsd->status.sp -= dstsd->status.sp*(15*skilllv+5)/100;
+ clif_updatestatus(dstsd,SP_SP);
+ }
+ break;
+
+ case HT_SANDMAN: /* サンドマン */
+ if( rand()%100 < (10*skilllv+40)*sc_def_int/100 )
+ status_change_start(bl,SC_SLEEP,skilllv,0,0,0,skill_get_time2(skillid,skilllv),0);
+ break;
+
+ case TF_SPRINKLESAND: /* ?サまき */
+ if( rand()%100 < 20*sc_def_int/100 )
+ status_change_start(bl,SC_BLIND,skilllv,0,0,0,skill_get_time2(skillid,skilllv),0);
+ break;
+
+ case TF_THROWSTONE: /* ?ホ投げ */
+ if( rand()%100 < 3*sc_def_vit/100 )
+ status_change_start(bl,SC_STAN,skilllv,0,0,0,skill_get_time2(skillid,skilllv),0);
+ if( rand()%100 < 3*sc_def_int/100 )
+ status_change_start(bl,SC_BLIND,skilllv,0,0,0,skill_get_time2(skillid,skilllv),0);
+ break;
+
+ case NPC_DARKCROSS:
+ case CR_HOLYCROSS: /* ホ?リ?クロス */
+ if( rand()%100 < 3*skilllv*sc_def_int/100 )
+ status_change_start(bl,SC_BLIND,skilllv,0,0,0,skill_get_time2(skillid,skilllv),0);
+ break;
+
+ case CR_GRANDCROSS: /* グランドク?ス */
+ case NPC_GRANDDARKNESS: /*闇グランドク?ス*/
+ {
+ int race = status_get_race(bl);
+ if( (battle_check_undead(race,status_get_elem_type(bl)) || race == 6) && rand()%100 < 100000*sc_def_int/100) //??ァ付?だが完全耐?ォには無?
+ status_change_start(bl,SC_BLIND,skilllv,0,0,0,skill_get_time2(skillid,skilllv),0);
+ }
+ break;
+
+ case AM_ACIDTERROR:
+ if (rand()%100 < (skilllv*3)*sc_def_vit/100 ) {
+ status_change_start(bl,SC_BLEEDING,skilllv,0,0,0,skill_get_time2(skillid,skilllv),0);
+ }
+ if (dstsd && rand()%100 < skill_get_time(skillid,skilllv) * battle_config.equip_skill_break_rate / 100) { //fixed
+ if(pc_breakarmor(dstsd))
+ clif_emotion(bl,23);
+ }
+
+ break;
+
+ case AM_DEMONSTRATION:
+ if (dstsd && rand()%10000 < skilllv * battle_config.equip_skill_break_rate )
+ pc_breakweapon(dstsd);
+ break;
+
+ case CR_SHIELDCHARGE: /* シ?ルドチャ?ジ */
+ if( rand()%100 < (15 + skilllv*5)*sc_def_vit/100 )
+ status_change_start(bl,SC_STAN,skilllv,0,0,0,skill_get_time2(skillid,skilllv),0);
+ break;
+
+ case PA_PRESSURE: /* プレッシャ? */
+ if (dstsd) {
+ dstsd->status.sp -= dstsd->status.sp * (15 + 5 * skilllv) / 100;
+ clif_updatestatus(dstsd,SP_SP);
+ }
+ break;
+
+ case RG_RAID: /* サプライズアタック */
+ if( rand()%100 < (10+3*skilllv)*sc_def_vit/100 )
+ status_change_start(bl,SC_STAN,skilllv,0,0,0,skill_get_time2(skillid,skilllv),0);
+ if( rand()%100 < (10+3*skilllv)*sc_def_int/100 )
+ status_change_start(bl,SC_BLIND,skilllv,0,0,0,skill_get_time2(skillid,skilllv),0);
+ break;
+
+ case BA_FROSTJOKE:
+ if(rand()%100 < (15+5*skilllv)*sc_def_mdef/100)
+ status_change_start(bl,SC_FREEZE,skilllv,0,0,0,skill_get_time2(skillid,skilllv),0);
+ break;
+
+ case DC_SCREAM:
+ if( rand()%100 < (25+5*skilllv)*sc_def_vit/100 )
+ status_change_start(bl,SC_STAN,skilllv,0,0,0,skill_get_time2(skillid,skilllv),0);
+ break;
+
+ case BD_LULLABY: /* 子守唄 */
+ if( rand()%100 < 15*sc_def_int/100 )
+ status_change_start(bl,SC_SLEEP,skilllv,0,0,0,skill_get_time2(skillid,skilllv),0);
+ break;
+
+ case DC_UGLYDANCE:
+ if (dstsd) {
+ int skill, sp = 5+5*skilllv;
+ if(sd && (skill=pc_checkskill(sd,DC_DANCINGLESSON)))
+ sp += 5+skill;
+ dstsd->status.sp -= sp;
+ if(dstsd->status.sp<0)
+ dstsd->status.sp=0;
+ clif_updatestatus(dstsd,SP_SP);
+ }
+ break;
+ case SL_STUN:
+ if (status_get_size(bl)==1 && rand()%100 < (30+10*skilllv)*sc_def_vit/100 ) //Only stuns mid-sized mobs.
+ status_change_start(bl,SC_STAN,skilllv,0,0,0,skill_get_time(skillid,skilllv),0);
+ break;
+ case SG_SUN_WARM:
+ case SG_MOON_WARM:
+ case SG_STAR_WARM:
+ if (dstsd) {
+ dstsd->status.sp -= 5;
+ if(dstsd->status.sp < 0)
+ dstsd->status.sp = 0;
+ clif_updatestatus(dstsd,SP_SP);
+ }
+ break;
+
+ /* MOBの追加?果付きスキル */
+
+ case NPC_PETRIFYATTACK:
+ if(rand()%100 < sc_def_mdef)
+ status_change_start(bl,sc[skillid-NPC_POISON],skilllv,0,0,0,skill_get_time2(skillid,skilllv),0);
+ break;
+ case NPC_POISON:
+ case NPC_SILENCEATTACK:
+ case NPC_STUNATTACK:
+ if(rand()%100 < sc_def_vit && src->type!=BL_PET)
+ status_change_start(bl,sc[skillid-NPC_POISON],skilllv,0,0,0,skill_get_time2(skillid,skilllv),0);
+ if(src->type==BL_PET)
+ status_change_start(bl,sc[skillid-NPC_POISON],skilllv,0,0,0,skilllv*1000,0);
+ break;
+ case NPC_CURSEATTACK:
+ if(rand()%100 < sc_def_luk)
+ status_change_start(bl,sc[skillid-NPC_POISON],skilllv,0,0,0,skill_get_time2(skillid,skilllv),0);
+ break;
+ case NPC_SLEEPATTACK:
+ case NPC_BLINDATTACK:
+ if(rand()%100 < sc_def_int)
+ status_change_start(bl,sc[skillid-NPC_POISON],skilllv,0,0,0,skill_get_time2(skillid,skilllv),0);
+ break;
+
+ case NPC_MENTALBREAKER:
+ if(dstsd) {
+ int sp = dstsd->status.max_sp*(10+skilllv)/100;
+ if(sp < 1) sp = 1;
+ pc_heal(dstsd,0,-sp);
+ }
+ break;
+
+ case CH_TIGERFIST:
+ if (rand()%100 < (10 + skilllv*10)*sc_def_vit/100) {
+ int sec = skill_get_time2 (skillid,skilllv) - status_get_agi(bl)/10;
+ if (dstsd) {
+ dstsd->canmove_tick += sec;
+ dstsd->canact_tick += sec;
+ } else if (dstmd)
+ dstmd->canmove_tick += sec;
+ }
+ break;
+
+ case LK_SPIRALPIERCE:
+ if (rand()%100 < (15 + skilllv*5)*sc_def_vit/100)
+ status_change_start(bl,SC_STOP,0,0,0,0,skill_get_time2(skillid,skilllv),0);
+ break;
+
+ case ST_REJECTSWORD: /* フリ?ジングトラップ */
+ if( rand()%100 < (skilllv*15) )
+ status_change_start(bl,SC_AUTOCOUNTER,skilllv,0,0,0,skill_get_time2(skillid,skilllv),0);
+ break;
+
+ case PF_FOGWALL: /* ホ?リ?ク?ス */
+ if (src != bl) {
+ struct status_change *sc_data = status_get_sc_data(bl);
+ if (sc_data && sc_data[SC_DELUGE].timer == -1 && !(status_get_mode(bl)&MD_BOSS))
+ status_change_start(bl,SC_BLIND,skilllv,0,0,0,skill_get_time2(skillid,skilllv),0);
+ }
+ break;
+
+ case LK_HEADCRUSH: /* ヘッドクラッシュ */
+ {
+ //??が良く分からないので適?に
+ int race = status_get_race(bl);
+ if (!(battle_check_undead(race, status_get_elem_type(bl)) || race == 6) && rand()%100 < 50 * sc_def_vit/100)
+ status_change_start(bl, SC_BLEEDING, skilllv, 0, 0, 0, skill_get_time2(skillid,skilllv), 0);
+ }
+ break;
+
+ case LK_JOINTBEAT: /* ジョイントビ?ト */
+ //??が良く分からないので適?に
+ if( rand()%100 < (5*skilllv+5)*sc_def_vit/100 )
+ status_change_start(bl,SkillStatusChangeTable[skillid],skilllv,0,0,0,skill_get_time2(skillid,skilllv),0);
+ break;
+
+ case PF_SPIDERWEB: /* スパイダ?ウェッブ */
+ {
+ int sec = skill_get_time2(skillid,skilllv);
+ if(map[src->m].flag.pvp) //PvPでは?S束時間半減?H
+ sec = sec/2;
+ battle_stopwalking(bl,1);
+ status_change_start(bl,SkillStatusChangeTable[skillid],skilllv,0,0,0,sec,0);
+ }
+ break;
+
+ case ASC_METEORASSAULT: /* ?テオアサルト */
+ //Any enemies hit by this skill will receive Stun, Darkness, or external bleeding status ailment with a 5%+5*SkillLV% chance.
+ if( rand()%100 < (5+skilllv*5) ) //5%+5*SkillLV%
+ switch(rand()%3) {
+ case 0:
+ status_change_start(bl,SC_BLIND,skilllv,0,0,0,skill_get_time2(skillid,1),0);
+ break;
+ case 1:
+ status_change_start(bl,SC_STAN,skilllv,0,0,0,skill_get_time2(skillid,2),0);
+ break;
+ default:
+ status_change_start(bl,SC_BLEEDING,skilllv,0,0,0,skill_get_time2(skillid,3),0);
+ }
+ break;
+
+ case HW_NAPALMVULCAN: /* ナパ?ムバルカン */
+ // skilllv*5%の確率で呪い
+ if (rand()%10000 < 5*skilllv*sc_def_luk)
+ status_change_start(bl,SC_CURSE,7,0,0,0,skill_get_time2(NPC_CURSEATTACK,7),0);
+ break;
+
+ case WS_CARTTERMINATION: // Cart termination
+ if (rand() % 10000 < 5 * skilllv * sc_def_vit)
+ status_change_start(bl,SC_STAN,skilllv,0,0,0,skill_get_time2(skillid,skilllv),0);
+ break;
+
+ case CR_ACIDDEMONSTRATION:
+ if (dstsd) {
+ if (rand()%10000 < skilllv * battle_config.equip_skill_break_rate)
+ pc_breakweapon(dstsd);
+ // separate chances?
+ if (rand()%10000 < skilllv * battle_config.equip_skill_break_rate)
+ pc_breakarmor(dstsd);
+ }
+ break;
+
+ case TK_DOWNKICK:
+ if(rand()%100 < 100*sc_def_vit/100 )
+ status_change_start(bl,SC_STAN,skilllv,0,0,0,skill_get_time(skillid,skilllv),0);
+ break;
+
+ case TK_JUMPKICK:
+ { //Cancel out Soul Linker status of the target. [Skotlex]
+ struct status_change *sc_data = status_get_sc_data(bl);
+ if (sc_data) {
+ if (sc_data[SC_PRESERVE].timer != -1) //preserve blocks the cleaning
+ break;
+ //Remove pitched potions effect.
+ if (sc_data[SC_ASPDPOTION0].timer != -1 && sc_data[SC_ASPDPOTION0].val4)
+ status_change_end(bl, SC_ASPDPOTION0, -1);
+ if (sc_data[SC_ASPDPOTION1].timer != -1 && sc_data[SC_ASPDPOTION1].val4)
+ status_change_end(bl, SC_ASPDPOTION1, -1);
+ if (sc_data[SC_ASPDPOTION2].timer != -1 && sc_data[SC_ASPDPOTION2].val4)
+ status_change_end(bl, SC_ASPDPOTION2, -1);
+ if (sc_data[SC_ASPDPOTION3].timer != -1 && sc_data[SC_ASPDPOTION3].val4)
+ status_change_end(bl, SC_ASPDPOTION3, -1);
+ if (sc_data[SC_SPIRIT].timer != -1)
+ status_change_end(bl, SC_SPIRIT, -1);
+ if (sc_data[SC_ONEHAND].timer != -1)
+ status_change_end(bl, SC_ONEHAND, -1);
+ if (sc_data[SC_ADRENALINE2].timer != -1)
+ status_change_end(bl, SC_ADRENALINE2, -1);
+ }
+ }
+ break;
+ case MO_BALKYOUNG: //Note: attack_type is passed as BF_WEAPON for the actual target, BF_MISC for the splash-affected mobs.
+ if(attack_type == BF_MISC && rand()%100 < 70*sc_def_vit/100 ) //70% base stun chance...
+ status_change_start(bl,SC_STAN,skilllv,0,0,0,skill_get_time2(skillid,skilllv),0);
+ break;
+ }
+
+ if (md && battle_config.summons_inherit_effects && md->master_id && md->special_state.ai)
+ { //Pass heritage to Master for status causing effects. [Skotlex]
+ sd = map_id2sd(md->master_id);
+ }
+
+ if(sd && skillid != MC_CARTREVOLUTION && skillid != AM_DEMONSTRATION && skillid != CR_REFLECTSHIELD && attack_type&BF_WEAPON){ /* カ?ドによる追加?果 */
+ int i, type;
+ int sc_def_card=100;
+
+ for(i=SC_COMMON_MIN;i<=SC_COMMON_MAX;i++){
+ type=i-SC_COMMON_MIN;
+ if (!sd->addeff[type] && (!sd->state.arrow_atk || !sd->arrow_addeff[type]))
+ continue; //Code Speedup.
+ //??ロに?態異?
+ switch (i) {
+ case SC_STONE:
+ case SC_FREEZE:
+ sc_def_card=sc_def_mdef;
+ break;
+ case SC_STAN:
+ case SC_POISON:
+ case SC_DPOISON:
+ case SC_SILENCE:
+ case SC_BLEEDING:
+ sc_def_card=sc_def_vit;
+ break;
+ case SC_SLEEP:
+ case SC_CONFUSION:
+ case SC_BLIND:
+ sc_def_card=sc_def_int;
+ break;
+ case SC_CURSE:
+ sc_def_card=sc_def_luk;
+ }
+
+ if (rand()%10000 < (sd->addeff[type]+(sd->state.arrow_atk?sd->arrow_addeff[type]:0))*sc_def_card/100 )
+ { //Inflicted status effect.
+ if(battle_config.battle_log)
+ ShowInfo("PC %d skill_additional_effect: caused status effect (pos %d): %d\n",sd->bl.id,i,sd->addeff[type]);
+ status_change_start(bl,i,7,0,0,0,skill_get_time2(sc2[type],7),0);
+ }
+ }
+ }
+
+ //Reports say that autospell effects get triggered on skills and pretty much everything including splash attacks. [Skotlex]
+ //Here we use the nk value to trigger spells only on damage causing skills (otherwise stuff like AL_HEAL will trigger them)
+ if(sd && !status_isdead(bl) && src != bl &&
+ (!skillid || skillid == KN_AUTOCOUNTER || skillid == CR_REFLECTSHIELD || skill_get_nk(skillid)!=NK_NO_DAMAGE))
+ {
+ struct block_list *tbl;
+ int i, auto_skillid, auto_skilllv, rate;
+
+ for (i = 0; i < MAX_PC_BONUS; i++) {
+ if (sd->autospell[i].id == 0)
+ break;
+
+ auto_skillid = (sd->autospell[i].id > 0) ? sd->autospell[i].id : -sd->autospell[i].id;
+
+ if (auto_skillid == skillid) //Prevents skill from retriggering themselves. [Skotlex]
+ continue;
+
+ auto_skilllv = (sd->autospell[i].lv > 0) ? sd->autospell[i].lv : 1;
+ rate = (!sd->state.arrow_atk) ? sd->autospell[i].rate : sd->autospell[i].rate / 2;
+
+ if (rand()%1000 > rate)
+ continue;
+ if (sd->autospell[i].id < 0)
+ tbl = src;
+ else
+ tbl = bl;
+
+ if (tbl != src && !battle_check_range(src, tbl, skill_get_range2(src, auto_skillid, auto_skilllv)))
+ continue; //Autoskills DO check for target-src range. [Skotlex]
+
+ if (skill_get_inf(auto_skillid) & INF_GROUND_SKILL)
+ skill_castend_pos2(src, tbl->x, tbl->y, auto_skillid, auto_skilllv, tick, 0);
+ else {
+ switch (skill_get_nk(auto_skillid)) {
+ case NK_NO_DAMAGE:
+ skill_castend_nodamage_id(src, tbl, auto_skillid, auto_skilllv, tick, 0);
+ break;
+ case NK_SPLASH_DAMAGE:
+ default:
+ skill_castend_damage_id(src, tbl, auto_skillid, auto_skilllv, tick, 0);
+ break;
+ }
+ }
+ }
+ }
+ return 0;
+}
+
+/* Splitted off from skill_additional_effect, which is never called when the
+ * attack skill kills the enemy. Place in this function counter status effects
+ * when using skills (eg: Asura's sp regen penalty, or counter-status effects
+ * from cards) that will take effect on the source, not the target. [Skotlex]
+ * Note: Currently this function only applies to Extremity Fist and BF_WEAPON
+ * type of skills, so not every instance of skill_additional_effect needs a call
+ * to this one.
+ */
+int skill_counter_additional_effect (struct block_list* src, struct block_list *bl, int skillid, int skilllv, int attack_type, unsigned int tick)
+{
+ const int sc2[]={
+ MG_STONECURSE,MG_FROSTDIVER,NPC_STUNATTACK,
+ NPC_SLEEPATTACK,TF_POISON,NPC_CURSEATTACK,
+ NPC_SILENCEATTACK,AS_SONICBLOW,NPC_BLINDATTACK,
+ LK_HEADCRUSH
+ };
+
+ struct map_session_data *sd=NULL;
+ struct map_session_data *dstsd=NULL;
+ struct mob_data *md=NULL;
+ struct mob_data *dstmd=NULL;
+// struct pet_data *pd=NULL; Pet's can't be inflicted!
+
+ int sc_def_mdef,sc_def_vit,sc_def_int,sc_def_luk;
+
+ nullpo_retr(0, src);
+ nullpo_retr(0, bl);
+
+ if(skillid < 0)
+ { // remove the debug print when this case is finished
+ ShowDebug("skill_counter_additional_effect: skillid=%i\ncall: %p %p %i %i %i %i",skillid,
+ src, bl,skillid,skilllv,attack_type,tick);
+ return 0;
+ }
+ if(skillid > 0 && skilllv <= 0) return 0; // don't forget auto attacks! - celest
+
+ switch (src->type) {
+ case BL_PC:
+ sd = (struct map_session_data *)src;
+ break;
+ case BL_MOB:
+ md = (struct mob_data *)src;
+ break;
+ case BL_PET: //Only mobs/players can be affected. [Skotlex]
+// pd = (struct pet_data *)src;
+// break;
+ default:
+ return 0;
+ }
+
+ switch (bl->type) {
+ case BL_PC:
+ dstsd=(struct map_session_data *)bl;
+ break;
+ case BL_MOB:
+ dstmd=(struct mob_data *)bl;
+ break;
+ default:
+ return 0;
+ }
+
+ //自分の耐?ォ
+ sc_def_mdef = status_get_sc_def_mdef(src);
+ sc_def_vit = status_get_sc_def_vit(src);
+ sc_def_int = status_get_sc_def_int(src);
+ sc_def_luk = status_get_sc_def_luk(src);
+
+ switch(skillid){
+ case 0: //Normal Attack - Nothing here yet.
+ break;
+ case MO_EXTREMITYFIST: /* 阿?C羅覇凰? */
+ //阿?C羅を使うと5分間自然回復しないようになる
+ status_change_start(src,SkillStatusChangeTable[skillid],skilllv,0,0,0,skill_get_time2(skillid,skilllv),0 );
+ break;
+ }
+
+ if((sd||dstsd) && skillid != MC_CARTREVOLUTION && attack_type&BF_WEAPON){ /* カ?ドによる追加?果 */
+ int i, type;
+ int sc_def_card=100;
+
+ for(i=SC_COMMON_MIN;i<=SC_COMMON_MAX;i++){
+ type=i-SC_COMMON_MIN;
+
+ switch (i) {
+ case SC_STONE:
+ case SC_FREEZE:
+ sc_def_card=sc_def_mdef;
+ break;
+ case SC_STAN:
+ case SC_POISON:
+ case SC_DPOISON:
+ case SC_SILENCE:
+ case SC_BLEEDING:
+ sc_def_card=sc_def_vit;
+ break;
+ case SC_SLEEP:
+ case SC_CONFUSION:
+ case SC_BLIND:
+ sc_def_card=sc_def_int;
+ break;
+ case SC_CURSE:
+ sc_def_card=sc_def_luk;
+ }
+
+ if (sd && (rand()%10000 < (sd->addeff2[type]+(sd->state.arrow_atk?sd->arrow_addeff2[type]:0))*sc_def_card/100 ))
+ { //Self infliced status from attacking.
+ if(battle_config.battle_log)
+ ShowInfo("PC %d skill_addeff: self inflicted effect (pos %d): %d\n",src->id,i,sd->addeff2[type]);
+ status_change_start(src,i,7,0,0,0,skill_get_time2(sc2[type],7),0);
+ }
+ if (dstsd &&
+ (dstsd->addeff3_type[type] == 1 || ((sd && sd->state.arrow_atk) || (status_get_range(src)>2))) &&
+ (rand()%10000 < dstsd->addeff3[type]*sc_def_card/100)
+ ) { //Counter status effect.
+ if(battle_config.battle_log)
+ ShowInfo("PC %d skill_addeff: counter inflicted effect (pos %d): %d\n",src->id,i,dstsd->addeff3[type]);
+ status_change_start(src,i,7,0,0,0,skill_get_time2(sc2[type],7),0);
+ }
+ }
+ }
+
+ //Trigger counter-spells to retaliate against damage causing skills. [Skotlex]
+ if(dstsd && !status_isdead(bl) && src != bl &&(!skillid || skill_get_nk(skillid)!=NK_NO_DAMAGE))
+ {
+ struct block_list *tbl;
+ int i, skillid, skilllv, rate;
+
+ for (i = 0; i < MAX_PC_BONUS; i++) {
+ if (dstsd->autospell2[i].id == 0)
+ break;
+
+ skillid = (dstsd->autospell2[i].id > 0) ? dstsd->autospell2[i].id : -dstsd->autospell2[i].id;
+ skilllv = (dstsd->autospell2[i].lv > 0) ? dstsd->autospell2[i].lv : 1;
+ rate = ((sd && !sd->state.arrow_atk) || (status_get_range(src)<=2)) ?
+ dstsd->autospell2[i].rate : dstsd->autospell2[i].rate / 2;
+
+ if (rand()%1000 > rate)
+ continue;
+ if (dstsd->autospell2[i].id < 0)
+ tbl = bl;
+ else
+ tbl = src;
+
+ if (skill_get_inf(skillid) & INF_GROUND_SKILL)
+ skill_castend_pos2(bl, tbl->x, tbl->y, skillid, skilllv, tick, 0);
+ else {
+ switch (skill_get_nk(skillid)) {
+ case NK_NO_DAMAGE:
+ skill_castend_nodamage_id(bl, tbl, skillid, skilllv, tick, 0);
+ break;
+ case NK_SPLASH_DAMAGE:
+ default:
+ skill_castend_damage_id(bl, tbl, skillid, skilllv, tick, 0);
+ break;
+ }
+ }
+ }
+ }
+ return 0;
+}
+
+/*=========================================================================
+ Used to knock back players, monsters, traps, etc
+ If count&0xf00000, the direction is send in the 6th byte.
+ If count&0x10000, the direction is to the back of the target, otherwise is away from the src.
+ If count&0x20000, position update packets must not be sent.
+-------------------------------------------------------------------------*/
+int skill_blown( struct block_list *src, struct block_list *target,int count)
+{
+ int dx=0,dy=0,nx,ny;
+ int x=target->x,y=target->y;
+ int dir,ret;
+ struct map_session_data *sd=NULL;
+ struct mob_data *md=NULL;
+ struct pet_data *pd=NULL;
+ struct skill_unit *su=NULL;
+ struct status_change* sc_data=NULL;
+
+ nullpo_retr(0, src);
+ nullpo_retr(0, target);
+
+ if (src != target && map_flag_gvg(target->m) && target->type != BL_SKILL)
+ return 0; //No knocking back in WoE, except for skills... because traps CAN be knocked back.
+
+ switch (target->type) {
+ case BL_PC:
+ sd=(struct map_session_data *)target;
+ break;
+ case BL_MOB:
+ md=(struct mob_data *)target;
+ break;
+ case BL_PET:
+ pd=(struct pet_data *)target;
+ break;
+ case BL_SKILL:
+ su=(struct skill_unit *)target;
+ break;
+ default:
+ return 0;
+ }
+ if (target->type != BL_SKILL)
+ sc_data = status_get_sc_data(target);
+
+ if (count&0xf00000)
+ dir = (count>>20)&0xf;
+ else if (count&0x10000 || (target->x==src->x && target->y==src->y))
+ dir = status_get_dir(target);
+ else
+ dir = map_calc_dir(target,src->x,src->y);
+ if (dir>=0 && dir<8){
+ dx = -dirx[dir];
+ dy = -diry[dir];
+ }
+
+ ret=path_blownpos(target->m,x,y,dx,dy,count&0xffff);
+ nx=ret>>16;
+ ny=ret&0xffff;
+
+ battle_stopwalking(target,0);
+
+ dx = nx - x;
+ dy = ny - y;
+
+ if(sd) /* ?面外に?oたので?チ去 */
+ map_foreachinmovearea(clif_pcoutsight,target->m,x-AREA_SIZE,y-AREA_SIZE,x+AREA_SIZE,y+AREA_SIZE,dx,dy,BL_ALL,sd);
+ else if(md)
+ map_foreachinmovearea(clif_moboutsight,target->m,x-AREA_SIZE,y-AREA_SIZE,x+AREA_SIZE,y+AREA_SIZE,dx,dy,BL_PC,md);
+ else if(pd)
+ map_foreachinmovearea(clif_petoutsight,target->m,x-AREA_SIZE,y-AREA_SIZE,x+AREA_SIZE,y+AREA_SIZE,dx,dy,BL_PC,pd);
+
+ if (sc_data) {
+ if (sc_data[SC_DANCING].timer != -1) { //Move the song/dance [Skotlex]
+ if (sc_data[SC_DANCING].val1 == CG_MOONLIT) //Cancel Moonlight Petals if moved from casting position. [Skotlex]
+ skill_stop_dancing(target);
+ else
+ skill_unit_move_unit_group((struct skill_unit_group *)sc_data[SC_DANCING].val2, target->m, dx, dy);
+ }
+ if (sc_data[SC_CLOSECONFINE].timer != -1)
+ status_change_end(target, SC_CLOSECONFINE, -1);
+ if (sc_data[SC_CLOSECONFINE2].timer != -1)
+ status_change_end(target, SC_CLOSECONFINE2, -1);
+ }
+
+ if(su){
+ skill_unit_move_unit_group(su->group,target->m,dx,dy);
+ }else{
+ map_moveblock(target, nx, ny, gettick());
+ }
+
+ if(sd) /* ?面?に入ってきたので表示 */
+ map_foreachinmovearea(clif_pcinsight,target->m,nx-AREA_SIZE,ny-AREA_SIZE,nx+AREA_SIZE,ny+AREA_SIZE,-dx,-dy,BL_ALL,sd);
+ else if(md)
+ map_foreachinmovearea(clif_mobinsight,target->m,nx-AREA_SIZE,ny-AREA_SIZE,nx+AREA_SIZE,ny+AREA_SIZE,-dx,-dy,BL_PC,md);
+ else if(pd)
+ map_foreachinmovearea(clif_petinsight,target->m,nx-AREA_SIZE,ny-AREA_SIZE,nx+AREA_SIZE,ny+AREA_SIZE,-dx,-dy,BL_PC,pd);
+
+ if(!(count&0x20000))
+ clif_blown(target);
+
+ return 0;
+}
+
+/*
+ * =========================================================================
+ * スキル?U??果??まとめ
+ * flagの?明?B16?i?
+ * 00XRTTff
+ * ff = magicで計算に渡される?j
+ * TT = パケットのtype部分(0でデフォルト?j
+ * X = パケットのスキルLv
+ * R = 予約?iskill_area_subで使用する?j
+ *-------------------------------------------------------------------------
+ */
+
+int skill_attack( int attack_type, struct block_list* src, struct block_list *dsrc,
+ struct block_list *bl,int skillid,int skilllv,unsigned int tick,int flag )
+{
+ struct Damage dmg;
+ struct status_change *sc_data;
+ struct map_session_data *sd=NULL, *tsd=NULL;
+ int type,lv,damage;
+ static int tmpdmg = 0;
+
+ if(skillid > 0 && skilllv <= 0) return 0;
+
+ rdamage = 0;
+ nullpo_retr(0, src); //Source is the master behind the attack (player/mob/pet)
+ nullpo_retr(0, dsrc); //dsrc is the actual originator of the damage, can be the same as src, or a skill casted by src.
+ nullpo_retr(0, bl); //Target to be attacked.
+
+ if(src->prev == NULL || dsrc->prev == NULL || bl->prev == NULL)
+ return 0;
+ //When caster is not the src of attack, this is a ground skill, and as such, do the relevant target checking. [Skotlex]
+ if (src != dsrc && !status_check_skilluse(NULL, bl, skillid, 1))
+ return 0;
+
+ //Note that splash attacks often only check versus the targetted mob, those around the splash area normally don't get checked for being hidden/cloaked/etc. [Skotlex]
+ if ((skill_get_nk(skillid) == NK_SPLASH_DAMAGE || skillid == ASC_METEORASSAULT || skillid == SN_SHARPSHOOTING)
+ && !status_check_skilluse(dsrc, bl, skillid, 1))
+ return 0;
+
+ //uncomment the following to do a check between caster and target. [Skotlex]
+ //eg: if you want storm gust to do no damage if the caster runs to another map after invoking the skill.
+// if(src->m != bl->m)
+// return 0;
+
+ //Uncomment the following to disable trap-ground skills from hitting when the caster is dead [Skotlex]
+ //eg: You cast meteor and then are killed, if you uncomment the following the meteors that fall afterwards cause no damage.
+// if(src != dsrc && status_isdead(src))
+// return 0;
+
+ if (dsrc->type == BL_PC)
+ sd = (struct map_session_data *)dsrc;
+ if (bl->type == BL_PC)
+ tsd = (struct map_session_data *)bl;
+
+//Shouldn't be needed, skillnotok's return value is highly unlikely to have changed after you started casting. [Skotlex]
+// if(dsrc->type == BL_PC && skillnotok(skillid, (struct map_session_data *)dsrc))
+// return 0; // [MouseJstr]
+// Is this check really needed? FrostNova won't hurt you if you step right where the caster is?
+ if(skillid == WZ_FROSTNOVA && dsrc->x == bl->x && dsrc->y == bl->y) //使用スキルがフ?ストノヴァで?Adsrcとblが同じ??鰍ネら何もしない
+ return 0;
+ if(sd && sd->chatID) //術者がPCでチャット中なら何もしない
+ return 0;
+
+//何もしない判定ここまで
+
+ sc_data = status_get_sc_data(bl);
+ if (attack_type&BF_MAGIC && sc_data && sc_data[SC_KAITE].timer != -1 && src == dsrc
+ && !(status_get_mode(src)&MD_BOSS) && (sd || status_get_lv(dsrc) <= 80) //Works on players or mobs with level under 80.
+ ) { //Bounce back the skill.
+ if (--sc_data[SC_KAITE].val2 <= 0)
+ status_change_end(bl, SC_KAITE, -1);
+ bl = src; //Just make the skill attack yourself @.@
+ sc_data = status_get_sc_data(bl);
+ tsd = (bl->type == BL_PC)?(struct map_session_data *)bl:NULL;
+ if (sc_data && sc_data[SC_SPIRIT].timer != -1 && sc_data[SC_SPIRIT].val2 == SL_WIZARD)
+ return 0; //Spirit of Wizard blocks bounced back spells.
+ }
+
+ type=-1;
+ lv=(flag>>20)&0xf;
+ dmg=battle_calc_attack(attack_type,src,bl,skillid,skilllv,flag&0xff ); //ダ??ジ計算
+
+ //Skotlex: Adjusted to the new system
+ if(src->type==BL_PET && (struct pet_data *)src)
+ { // [Valaris]
+ struct pet_data *pd = (struct pet_data *)src;
+ if (pd->a_skill && pd->a_skill->div_ && pd->a_skill->id == skillid)
+ {
+ int element = skill_get_pl(skillid);
+ if (skillid == -1)
+ element = status_get_attack_element(src);
+ dmg.damage=battle_attr_fix(src, bl, skilllv, element, status_get_element(bl));
+ dmg.damage2=0;
+ dmg.div_= pd->a_skill->div_;
+ }
+ }
+
+//マジックロッド?理ここから
+ if(attack_type&BF_MAGIC && sc_data && sc_data[SC_MAGICROD].timer != -1 && src == dsrc) { //魔法攻?でマジックロッド?態でsrc=dsrcなら
+ dmg.damage = dmg.damage2 = 0; //ダメ?ジ0
+ dmg.dmg_lv = ATK_FLEE; //This will prevent skill additional effect from taking effect. [Skotlex]
+ if(tsd) {
+ int sp = skill_get_sp(skillid,skilllv); //使用されたスキルのSPを吸?
+ sp = sp * sc_data[SC_MAGICROD].val2 / 100; //吸?率計算
+ if(skillid == WZ_WATERBALL && skilllv > 1) //ウォ?タ?ボ?ルLv1以上
+ sp = sp/((skilllv|1)*(skilllv|1)); //さらに計算?
+ if(sp > 0x7fff) sp = 0x7fff; //SP多すぎの場合は理論最大値
+ else if(sp < 1) sp = 1; //1以下の場合は1
+ if(tsd->status.sp + sp > tsd->status.max_sp) { //回復SP+現在のSPがMSPより大きい場合
+ sp = tsd->status.max_sp - tsd->status.sp; //SPをMSP-現在SPにする
+ tsd->status.sp = tsd->status.max_sp; //現在のSPにMSPを代入
+ }
+ else //回復SP+現在のSPがMSPより小さい場合は回復SPを加算
+ tsd->status.sp += sp;
+ clif_heal(tsd->fd,SP_SP,sp); //SP回復エフェクトの表示
+ tsd->canact_tick = tick + skill_delayfix(bl, SA_MAGICROD, sc_data[SC_MAGICROD].val1, 0);
+ }
+ clif_skill_nodamage(bl,bl,SA_MAGICROD,sc_data[SC_MAGICROD].val1,1); //マジックロッドエフェクトを表示
+ }
+//マジックロッド?理ここまで
+
+ damage = dmg.damage + dmg.damage2;
+
+ if(lv==15)
+ lv=-1;
+
+ if( flag&0xff00 )
+ type=(flag&0xff00)>>8;
+
+ if((damage <= 0 || damage < dmg.div_)
+ && skillid != CH_PALMSTRIKE) //Palm Strike is the only skill that will knockback even if it misses. [Skotlex]
+ dmg.blewcount = 0;
+
+ if(skillid == CR_GRANDCROSS||skillid == NPC_GRANDDARKNESS) {//グランドクロス
+ if(battle_config.gx_disptype) dsrc = src; // 敵ダメ?ジ白文字表示
+ if(src == bl) type = 4; // 反動はダメ?ジモ?ションなし
+ }
+
+//使用者がPCの??の??ここから
+ if(sd) {
+ //Sorry for removing the Japanese comments, but they were actually distracting
+ //from the actual code and I couldn't understand a thing anyway >.< [Skotlex]
+ if (skillid && sd->sc_data[SC_COMBO].timer != -1)
+ status_change_end(src,SC_COMBO,-1); //Interrupt previous combo if you used a skill already. [Skotlex]
+ switch(skillid)
+ {
+ case MO_TRIPLEATTACK:
+ {
+ int delay = 1000 - 4 * status_get_agi(src) - 2 * status_get_dex(src);
+ if (damage < status_get_hp(bl) &&
+ pc_checkskill(sd, MO_CHAINCOMBO) > 0)
+ delay += 300 * battle_config.combo_delay_rate / 100;
+ status_change_start(src,SC_COMBO,MO_TRIPLEATTACK,skilllv,0,0,delay,0);
+ sd->attackabletime = sd->canmove_tick = tick + delay;
+ clif_combo_delay(src, delay);
+
+ if (sd->status.party_id>0) //bonus from SG_FRIEND [Komurka]
+ party_skill_check(sd, sd->status.party_id, MO_TRIPLEATTACK, skilllv);
+ break;
+ }
+ case MO_CHAINCOMBO:
+ {
+ int delay = 1000 - 4 * status_get_agi(src) - 2 * status_get_dex(src);
+ if(damage < status_get_hp(bl) &&
+ (pc_checkskill(sd, MO_COMBOFINISH) > 0 && sd->spiritball > 0))
+ delay += 300 * battle_config.combo_delay_rate /100;
+ status_change_start(src,SC_COMBO,MO_CHAINCOMBO,skilllv,0,0,delay,0);
+ sd->attackabletime = sd->canmove_tick = tick + delay;
+ clif_combo_delay(src,delay);
+ break;
+ }
+ case MO_COMBOFINISH:
+ {
+ int delay = 700 - 4 * status_get_agi(src) - 2 * status_get_dex(src);
+ if(damage < status_get_hp(bl) &&
+ (
+ (pc_checkskill(sd, MO_EXTREMITYFIST) > 0 && sd->spiritball >= 4 && sd->sc_data[SC_EXPLOSIONSPIRITS].timer != -1) ||
+ (pc_checkskill(sd, CH_TIGERFIST) > 0 && sd->spiritball > 0) ||
+ (pc_checkskill(sd, CH_CHAINCRUSH) > 0 && sd->spiritball > 1)
+ ))
+ delay += 300 * battle_config.combo_delay_rate /100;
+ status_change_start(src,SC_COMBO,MO_COMBOFINISH,skilllv,0,0,delay,0);
+ sd->attackabletime = sd->canmove_tick = tick + delay;
+ clif_combo_delay(src,delay);
+ break;
+ }
+ case CH_TIGERFIST:
+ { //Tigerfist is now a combo-only skill. [Skotlex]
+ int delay = 1000 - 4 * status_get_agi(src) - 2 * status_get_dex(src);
+ if(damage < status_get_hp(bl) &&
+ (
+ (pc_checkskill(sd, MO_EXTREMITYFIST) > 0 && sd->spiritball >= 3 && sd->sc_data[SC_EXPLOSIONSPIRITS].timer != -1) ||
+ (pc_checkskill(sd, CH_CHAINCRUSH) > 0)
+ ))
+ delay += 300 * battle_config.combo_delay_rate /100;
+ status_change_start(src,SC_COMBO,CH_TIGERFIST,skilllv,0,0,delay,0);
+ sd->attackabletime = sd->canmove_tick = tick + delay;
+ clif_combo_delay(src,delay);
+ break;
+ }
+ case CH_CHAINCRUSH:
+ {
+ int delay = 1000 - 4 * status_get_agi(src) - 2 * status_get_dex(src);
+ if(damage < status_get_hp(bl))
+ delay += 300 * battle_config.combo_delay_rate /100;
+ status_change_start(src,SC_COMBO,CH_CHAINCRUSH,skilllv,0,0,delay,0);
+ sd->attackabletime = sd->canmove_tick = tick + delay;
+ clif_combo_delay(src,delay);
+ break;
+ }
+ case AC_DOUBLE:
+ {
+ int race = status_get_race(bl);
+ if((race == 2 || race == 4) && damage < status_get_hp(bl) && pc_checkskill(sd, HT_POWER)) {
+ //TODO: This code was taken from Triple Blows,is this even how it should be? [Skotlex]
+ status_change_start(src,SC_COMBO,HT_POWER,bl->id,0,0,2000,0);
+ clif_combo_delay(src,2000);
+ }
+ break;
+ }
+ case TK_COUNTER:
+ { //bonus from SG_FRIEND [Komurka]
+ int level;
+ if(sd->status.party_id>0 && (level = pc_checkskill(sd,SG_FRIEND)))
+ party_skill_check(sd, sd->status.party_id, TK_COUNTER,level);
+ }
+ case TK_STORMKICK:
+ case TK_DOWNKICK:
+ case TK_TURNKICK:
+ // Delay normal attack table until skill's delay has passed. Let's make it skip one attack motion. [Skotlex]
+ sd->attackabletime = sd->canmove_tick = tick + status_get_amotion(&sd->bl);
+ break;
+ case SL_STIN:
+ case SL_STUN:
+ if (skilllv >= 7 && sd->sc_data[SC_COMBO].timer == -1)
+ status_change_start(src,SC_COMBO,SL_SMA,skilllv,0,0,skill_get_time2(skillid, skilllv),0);
+ break;
+ } //Switch End
+ }
+ if(attack_type&BF_WEAPON && damage > 0 && src != bl && src == dsrc)
+ { //武器スキル&ダメ?ジあり&使用者と?象者が違う&src=dsrc
+ if(dmg.flag&BF_SHORT) { //近距離攻?時?※
+ if(tsd && tsd->short_weapon_damage_return > 0)
+ { //近距離攻?跳ね返し?※
+ rdamage += damage * tsd->short_weapon_damage_return / 100;
+ if(rdamage < 1) rdamage = 1;
+ }
+ if(sc_data && sc_data[SC_REFLECTSHIELD].timer != -1) { //リフレクトシ?ルド時
+ rdamage += damage * sc_data[SC_REFLECTSHIELD].val2 / 100; //跳ね返し計算
+ if(rdamage < 1) rdamage = 1;
+ }
+ }
+ else if(dmg.flag&BF_LONG) { //遠距離攻?時?※
+ if(tsd && tsd->long_weapon_damage_return > 0)
+ { //遠距離攻?跳ね返し?※
+ rdamage += damage * tsd->long_weapon_damage_return / 100;
+ if(rdamage < 1) rdamage = 1;
+ }
+ }
+ } else
+ // magic_damage_return by [AppleGirl] and [Valaris]
+ if(attack_type&BF_MAGIC && damage > 0 && src != bl && src == dsrc)
+ {
+ if(tsd && tsd->magic_damage_return > 0 )
+ {
+ rdamage += damage * tsd->magic_damage_return / 100;
+ if(rdamage < 1) rdamage = 1;
+ }
+ }
+
+//武器スキル?ここまで
+ switch(skillid){
+ case AS_SPLASHER:
+ clif_skill_damage(dsrc,bl,tick,dmg.amotion,dmg.dmotion, damage, dmg.div_, skillid, -1, 5);
+ break;
+ case ASC_BREAKER: // [celest]
+ if (attack_type&BF_WEAPON) { // the 1st attack won't really deal any damage
+ tmpdmg = damage; // store the temporary weapon damage
+ } else { // only display damage for the 2nd attack
+ if (tmpdmg == 0 || damage == 0) // if one or both attack(s) missed, display a 'miss'
+ clif_skill_damage(dsrc, bl, tick, dmg.amotion, dmg.dmotion, 0, dmg.div_, skillid, skilllv, type);
+ damage += tmpdmg; // add weapon and magic damage
+ tmpdmg = 0; // clear the temporary weapon damage
+ if (damage > 0) // if both attacks missed, do not display a 2nd 'miss'
+ clif_skill_damage(dsrc, bl, tick, dmg.amotion, dmg.dmotion, damage, dmg.div_, skillid, skilllv, type);
+ }
+ break;
+ case NPC_SELFDESTRUCTION:
+ if(src->type==BL_PC)
+ dmg.blewcount = 10;
+ break;
+ case KN_AUTOCOUNTER: //Skills that need be passed as a normal attack for the client to display correctly.
+ case SN_SHARPSHOOTING:
+ case TF_DOUBLE:
+ clif_damage(src,bl,tick,dmg.amotion,dmg.dmotion,damage,dmg.div_,dmg.type,dmg.damage2);
+ break;
+ default:
+ clif_skill_damage(dsrc,bl,tick,dmg.amotion,dmg.dmotion, damage, dmg.div_, skillid, (lv!=0)?lv:skilllv, (skillid==0)? 5:type );
+ }
+
+ map_freeblock_lock();
+
+ if(damage > 0 && dmg.flag&BF_SKILL && tsd
+ && pc_checkskill(tsd,RG_PLAGIARISM) && sc_data[SC_PRESERVE].timer == -1
+ && damage < tsd->status.hp)
+ { //Updated to not be able to copy skills if the blow will kill you. [Skotlex]
+ if ((!tsd->status.skill[skillid].id || tsd->status.skill[skillid].flag >= 13) &&
+ can_copy(tsd,skillid)) // Split all the check into their own function [Aru]
+ {
+ //?に?んでいるスキルがあれば該?スキルを消す
+ if (tsd->cloneskill_id && tsd->status.skill[tsd->cloneskill_id].flag == 13){
+ tsd->status.skill[tsd->cloneskill_id].id = 0;
+ tsd->status.skill[tsd->cloneskill_id].lv = 0;
+ tsd->status.skill[tsd->cloneskill_id].flag = 0;
+ }
+ tsd->cloneskill_id = skillid;
+ tsd->status.skill[skillid].id = skillid;
+ tsd->status.skill[skillid].lv = skilllv;
+ if ((lv = pc_checkskill(tsd,RG_PLAGIARISM)) < skilllv)
+ tsd->status.skill[skillid].lv = lv;
+ tsd->status.skill[skillid].flag = 13;//cloneskill flag
+ pc_setglobalreg(tsd, "CLONE_SKILL", tsd->cloneskill_id);
+ pc_setglobalreg(tsd, "CLONE_SKILL_LV", tsd->status.skill[skillid].lv);
+ clif_skillinfoblock(tsd);
+ }
+ }
+ if (skillid != WZ_HEAVENDRIVE && bl->type == BL_SKILL && damage > 0) {
+ struct skill_unit* su = (struct skill_unit*)bl;
+ if (su->group && skill_get_inf2(su->group->skill_id)&INF2_TRAP)
+ damage = 0; //Only Heaven's drive may damage traps. [Skotlex]
+ }
+ if ((skillid || flag) && !(attack_type&BF_WEAPON)) { // do not really deal damage for ASC_BREAKER's 1st attack
+ battle_damage(src,bl,damage, 0); //Deal damage before knockback to allow stuff like firewall+storm gust combo.
+ if (!status_isdead(bl) && (dmg.dmg_lv == ATK_DEF || damage > 0))
+ skill_additional_effect(src,bl,skillid,skilllv,attack_type,tick);
+ }
+
+ //Only knockback if it's still alive, otherwise a "ghost" is left behind. [Skotlex]
+ if (dmg.blewcount > 0 && !status_isdead(bl))
+ skill_blown(dsrc,bl,dmg.blewcount);
+
+ //Delayed damage must be dealt after the knockback (it needs to know actual position of target)
+ if ((skillid || flag) && attack_type&BF_WEAPON && skillid != ASC_BREAKER) { // do not really deal damage for ASC_BREAKER's 1st attack
+ battle_delay_damage(tick+dmg.amotion,src,bl,attack_type,skillid,skilllv,damage,dmg.dmg_lv,0);
+ }
+
+ if(skillid == RG_INTIMIDATE && damage > 0 && !(status_get_mode(bl)&MD_BOSS)/* && !map_flag_gvg(src->m)*/) {
+ int s_lv = status_get_lv(src),t_lv = status_get_lv(bl);
+ int rate = 50 + skilllv * 5;
+ rate = rate + (s_lv - t_lv);
+ if(rand()%100 < rate)
+ skill_addtimerskill(src,tick + 800,bl->id,0,0,skillid,skilllv,0,flag);
+ }
+
+ if (dmg.dmg_lv == ATK_DEF || damage > 0) //Counter status effects [Skotlex]
+ skill_counter_additional_effect(dsrc,bl,skillid,skilllv,attack_type,tick);
+
+ /* ダ??ジがあるなら追加?果判定 */
+ if(!status_isdead(bl) && bl->type==BL_MOB && src!=bl) /* スキル使用?件のMOBスキル */
+ {
+ struct mob_data *md=(struct mob_data *)bl;
+// nullpo_retr(0, md); //Just so you know.. these are useless. When you cast a pointer, the pointer still is the same, so if bl is not null, the after-casted pointer will never be nulll :/ [Skotlex]
+ if(battle_config.mob_changetarget_byskill && sd)
+ {
+ int target ;
+ target=md->target_id;
+ md->target_id=src->id;
+ mobskill_use(md,tick,MSC_SKILLUSED|(skillid<<16));
+ md->target_id=target;
+ }
+ else
+ mobskill_use(md,tick,MSC_SKILLUSED|(skillid<<16));
+ }
+
+ if(sd && dmg.flag&BF_WEAPON && src != bl && src == dsrc && damage > 0) {
+ int hp = 0,sp = 0;
+ if(sd->right_weapon.hp_drain_rate && sd->right_weapon.hp_drain_per > 0 && dmg.damage > 0 && rand()%1000 < sd->right_weapon.hp_drain_rate) {
+ hp += (dmg.damage * sd->right_weapon.hp_drain_per)/100;
+ if(sd->right_weapon.hp_drain_rate > 0 && hp < 1) hp = 1;
+ else if(sd->right_weapon.hp_drain_rate < 0 && hp > -1) hp = -1;
+ }
+ if(sd->left_weapon.hp_drain_rate && sd->left_weapon.hp_drain_per > 0 && dmg.damage2 > 0 && rand()%1000 < sd->left_weapon.hp_drain_rate) {
+ hp += (dmg.damage2 * sd->left_weapon.hp_drain_per)/100;
+ if(sd->left_weapon.hp_drain_rate > 0 && hp < 1) hp = 1;
+ else if(sd->left_weapon.hp_drain_rate < 0 && hp > -1) hp = -1;
+ }
+ if(sd->right_weapon.sp_drain_rate > 0 && sd->right_weapon.sp_drain_per > 0 && dmg.damage > 0 && rand()%1000 < sd->right_weapon.sp_drain_rate) {
+ sp += (dmg.damage * sd->right_weapon.sp_drain_per)/100;
+ if(sd->right_weapon.sp_drain_rate > 0 && sp < 1) sp = 1;
+ else if(sd->right_weapon.sp_drain_rate < 0 && sp > -1) sp = -1;
+ }
+ if(sd->left_weapon.sp_drain_rate > 0 && sd->left_weapon.sp_drain_per > 0 && dmg.damage2 > 0 && rand()%1000 < sd->left_weapon.sp_drain_rate) {
+ sp += (dmg.damage2 * sd->left_weapon.sp_drain_per)/100;
+ if(sd->left_weapon.sp_drain_rate > 0 && sp < 1) sp = 1;
+ else if(sd->left_weapon.sp_drain_rate < 0 && sp > -1) sp = -1;
+ }
+ if(hp || sp)
+ pc_heal(sd,hp,sp);
+ if (sd->sp_drain_type && bl->type == BL_PC)
+ battle_heal(NULL,bl,0,-sp,0);
+ }
+
+ if (/*(skillid || flag) &&*/ rdamage>0) { //Is the skillid/flag check really necessary? [Skotlex]
+ if (attack_type&BF_WEAPON)
+ battle_delay_damage(tick+dmg.amotion,bl,src,0,0,0,rdamage,ATK_DEF,0);
+ else
+ battle_damage(bl,src,rdamage,0);
+ clif_damage(src,src,tick, dmg.amotion,0,rdamage,1,4,0);
+ //Use Reflect Shield to signal this kind of skill trigger. [Skotlex]
+ skill_additional_effect(bl,src,CR_REFLECTSHIELD, 1,BF_WEAPON,tick);
+ }
+
+ if (!(flag & 1) &&
+ (
+ skillid == MG_COLDBOLT || skillid == MG_FIREBOLT || skillid == MG_LIGHTNINGBOLT
+ ) &&
+ (sc_data = status_get_sc_data(src)) &&
+ sc_data[SC_DOUBLECAST].timer != -1 &&
+ rand() % 100 < 40+10*sc_data[SC_DOUBLECAST].val1)
+ {
+ skill_castend_delay (src, bl, skillid, skilllv, tick + dmg.div_*dmg.amotion, flag|1);
+ }
+
+ map_freeblock_unlock();
+
+ return (dmg.damage+dmg.damage2); /* ?ダ?を返す */
+}
+
+/*==========================================
+ * スキル範??U?用(map_foreachinareaから呼ばれる)
+ * flagについて?F16?i?を確認
+ * MSB <- 00fTffff ->LSB
+ * T =タ?ゲット選?用(BCT_*)
+ * ffff=自由に使用可能
+ * 0 =予約?B0に固定
+ *------------------------------------------
+ */
+static int skill_area_temp[8]; /* 一時???B必要なら使う?B */
+static int skill_unit_temp[8]; /* For storing skill_unit ids as players move in/out of them. [Skotlex] */
+static int skill_unit_index=0; //Well, yeah... am too lazy to pass pointers around :X
+typedef int (*SkillFunc)(struct block_list *,struct block_list *,int,int,unsigned int,int);
+int skill_area_sub( struct block_list *bl,va_list ap )
+{
+ struct block_list *src;
+ int skill_id,skill_lv,flag;
+ unsigned int tick;
+ SkillFunc func;
+
+ nullpo_retr(0, bl);
+ nullpo_retr(0, ap);
+
+ if(bl->type!=BL_PC && bl->type!=BL_MOB && bl->type!=BL_SKILL)
+ return 0;
+
+ src=va_arg(ap,struct block_list *); //ここではsrcの値を??ニしていないのでNULLチェックはしない
+ skill_id=va_arg(ap,int);
+ skill_lv=va_arg(ap,int);
+ tick=va_arg(ap,unsigned int);
+ flag=va_arg(ap,int);
+ func=va_arg(ap,SkillFunc);
+
+ if(battle_check_target(src,bl,flag) > 0)
+ func(src,bl,skill_id,skill_lv,tick,flag);
+ return 0;
+}
+
+static int skill_check_unit_range_sub( struct block_list *bl,va_list ap )
+{
+ struct skill_unit *unit;
+ int *c;
+ int skillid,g_skillid;
+
+ nullpo_retr(0, bl);
+ nullpo_retr(0, ap);
+ nullpo_retr(0, unit = (struct skill_unit *)bl);
+ nullpo_retr(0, c = va_arg(ap,int *));
+
+ if(bl->prev == NULL || bl->type != BL_SKILL)
+ return 0;
+
+ if(!unit->alive)
+ return 0;
+
+ skillid = va_arg(ap,int);
+ g_skillid = unit->group->skill_id;
+
+ switch (skillid)
+ {
+ case MG_SAFETYWALL:
+ case AL_PNEUMA:
+ if(g_skillid != MG_SAFETYWALL && g_skillid != AL_PNEUMA)
+ return 0;
+ break;
+ case AL_WARP:
+ case HT_SKIDTRAP:
+ case HT_LANDMINE:
+ case HT_ANKLESNARE:
+ case HT_SHOCKWAVE:
+ case HT_SANDMAN:
+ case HT_FLASHER:
+ case HT_FREEZINGTRAP:
+ case HT_BLASTMINE:
+ case HT_CLAYMORETRAP:
+ case HT_TALKIEBOX:
+ case HP_BASILICA:
+ //Non stackable on themselves and traps (including venom dust which does not has the trap inf2 set)
+ if (skillid != g_skillid && !(skill_get_inf2(g_skillid)&INF2_TRAP) && g_skillid != AS_VENOMDUST)
+ return 0;
+ break;
+ default: //Avoid stacking with same kind of trap. [Skotlex]
+ if (g_skillid != skillid)
+ return 0;
+ break;
+ }
+
+ (*c)++;
+
+ return 1;
+}
+
+int skill_check_unit_range(int m,int x,int y,int skillid,int skilllv)
+{
+ int c = 0;
+ int range = skill_get_unit_range(skillid);
+ int layout_type = skill_get_unit_layout_type(skillid,skilllv);
+ if (layout_type==-1 || layout_type>MAX_SQUARE_LAYOUT) {
+ ShowError("skill_check_unit_range: unsupported layout type %d for skill %d\n",layout_type,skillid);
+ return 0;
+ }
+
+ // とりあえず?ウ方形のユニットレイアウトのみ対応
+ range += layout_type;
+ map_foreachinarea(skill_check_unit_range_sub,m,
+ x-range,y-range,x+range,y+range,BL_SKILL,&c,skillid);
+
+ return c;
+}
+
+static int skill_check_unit_range2_sub( struct block_list *bl,va_list ap )
+{
+ int *c;
+ int skillid;
+
+
+ nullpo_retr(0, bl);
+ nullpo_retr(0, ap);
+ nullpo_retr(0, c = va_arg(ap,int *));
+
+ if(bl->prev == NULL)
+ return 0;
+
+ if(status_isdead(bl))
+ return 0;
+
+ skillid = va_arg(ap,int);
+ if (skillid==HP_BASILICA && bl->type==BL_PC)
+ return 0;
+
+ if (skillid==AM_DEMONSTRATION && bl->type==BL_MOB && ((struct mob_data*)bl)->class_ == MOBID_EMPERIUM)
+ return 0; //Allow casting Bomb/Demonstration Right under emperium [Skotlex]
+
+ (*c)++;
+
+ return 1;
+}
+
+int skill_check_unit_range2(struct block_list *bl, int m,int x,int y,int skillid, int skilllv)
+{
+ int c = 0, range, type;
+
+ switch (skillid) { // to be expanded later
+ case WZ_ICEWALL:
+ range = 2;
+ break;
+ default:
+ {
+ int layout_type = skill_get_unit_layout_type(skillid,skilllv);
+ if (layout_type==-1 || layout_type>MAX_SQUARE_LAYOUT) {
+ ShowError("skill_check_unit_range2: unsupported layout type %d for skill %d\n",layout_type,skillid);
+ return 0;
+ }
+ // とりあえず?ウ方形のユニットレイアウトのみ対応
+ range = skill_get_unit_range(skillid) + layout_type;
+ }
+ break;
+ }
+
+ // if the caster is a monster/NPC, only check for players
+ // otherwise just check characters
+ if (bl->type == BL_PC)
+ type = BL_CHAR;
+ else
+ type = BL_PC;
+
+ map_foreachinarea(skill_check_unit_range2_sub, m,
+ x - range, y - range, x + range, y + range,
+ type, &c, skillid);
+
+ return c;
+}
+
+int skill_guildaura_sub (struct block_list *bl,va_list ap)
+{
+ struct map_session_data *sd;
+ int gid, id, *flag;
+
+ nullpo_retr(0, sd = (struct map_session_data *)bl);
+ nullpo_retr(0, ap);
+
+ id = va_arg(ap,int);
+ gid = va_arg(ap,int);
+ if (sd->status.guild_id != gid)
+ return 0;
+ nullpo_retr(0, flag = va_arg(ap,int *));
+
+ if (flag && *flag > 0) {
+ if (sd->sc_count && sd->sc_data[SC_GUILDAURA].timer != -1) {
+ if (sd->sc_data[SC_GUILDAURA].val4 != *flag) {
+ sd->sc_data[SC_GUILDAURA].val4 = *flag;
+ status_calc_pc (sd, 0);
+ }
+ return 0;
+ }
+ status_change_start(&sd->bl, SC_GUILDAURA, 1, id, 0, *flag, 0, 0);
+ }
+
+ return 0;
+}
+
+/*=========================================================================
+ * 範?スキル使用???ャ分けここから
+ */
+/* ??ロの?をカウントする?B?iskill_area_temp[0]を?炎化しておくこと?j */
+int skill_area_sub_count(struct block_list *src,struct block_list *target,int skillid,int skilllv,unsigned int tick,int flag)
+{
+ if(skill_area_temp[0] < 0xffff)
+ skill_area_temp[0]++;
+ return 1;
+}
+
+int skill_count_water(struct block_list *src,int range)
+{
+ int i,x,y,cnt = 0,size = range*2+1;
+ struct skill_unit *unit;
+
+ for (i=0;i<size*size;i++) {
+ x = src->x+(i%size-range);
+ y = src->y+(i/size-range);
+ if (map_getcell(src->m,x,y,CELL_CHKWATER)) {
+ cnt++;
+ continue;
+ }
+ unit = map_find_skill_unit_oncell(src,x,y,SA_DELUGE,NULL);
+ if (unit) {
+ cnt++;
+ skill_delunit(unit);
+ }
+ }
+ return cnt;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+static int skill_timerskill(int tid, unsigned int tick, int id,int data )
+{
+ struct map_session_data *sd = NULL;
+ struct mob_data *md = NULL;
+ struct pet_data *pd = NULL;
+ struct block_list *src = map_id2bl(id),*target;
+ struct skill_timerskill *skl = NULL;
+ int range;
+
+ nullpo_retr(0, src);
+
+ if(src->type == BL_PC) {
+ nullpo_retr(0, sd = (struct map_session_data *)src);
+ skl = &sd->skilltimerskill[data];
+ }
+ else if(src->type == BL_MOB) {
+ nullpo_retr(0, md = (struct mob_data *)src);
+ skl = &md->skilltimerskill[data];
+ }
+ else if(src->type == BL_PET) { // [Valaris]
+ nullpo_retr(0, pd = (struct pet_data *)src);
+ skl = &pd->skilltimerskill[data];
+ }
+
+ else
+ return 0;
+
+ nullpo_retr(0, skl);
+
+ skl->timer = -1;
+
+ if (sd) {
+ sd->timerskill_count--;
+ }
+
+ //Check moved here because otherwise the timer is not reset to -1 and later on we'll see problems when clearing. [Skotlex]
+ if(src->prev == NULL)
+ return 0;
+
+ if(skl->target_id) {
+ struct block_list tbl;
+ target = map_id2bl(skl->target_id);
+ if(skl->skill_id == RG_INTIMIDATE) {
+ if(target == NULL) {
+ target = &tbl; //?炎化してないのにアドレス突っ?んでいいのかな?H
+ target->type = BL_NUL;
+ target->m = src->m;
+ target->prev = target->next = NULL;
+ }
+ }
+ if(target == NULL)
+ return 0;
+
+ if(target->prev == NULL && skl->skill_id != RG_INTIMIDATE)
+ return 0;
+ if(src->m != target->m)
+ return 0;
+ if(sd && pc_isdead(sd))
+ return 0;
+ if(target->type == BL_PC && pc_isdead((struct map_session_data *)target) && skl->skill_id != RG_INTIMIDATE)
+ return 0;
+
+ switch(skl->skill_id) {
+ case RG_INTIMIDATE:
+ if(sd && !map[src->m].flag.noteleport) {
+ int x,y,i,j;
+ pc_randomwarp(sd,3);
+ for(i=0;i<16;i++) {
+ j = rand()%8;
+ x = sd->bl.x + dirx[j];
+ y = sd->bl.y + diry[j];
+ if(map_getcell(sd->bl.m,x,y,CELL_CHKPASS))
+ break;
+ }
+ if(i >= 16) {
+ x = sd->bl.x;
+ y = sd->bl.y;
+ }
+ if(target->prev != NULL) {
+ if(target->type == BL_PC && !pc_isdead((struct map_session_data *)target))
+ pc_setpos((struct map_session_data *)target,map[sd->bl.m].index,x,y,3);
+ else if(target->type == BL_MOB)
+ mob_warp((struct mob_data *)target,-1,x,y,3);
+ }
+ }
+ else if(md && !map[src->m].flag.monster_noteleport) {
+ int x,y,i,j;
+ mob_warp(md,-1,-1,-1,3);
+ for(i=0;i<16;i++) {
+ j = rand()%8;
+ x = md->bl.x + dirx[j];
+ y = md->bl.y + diry[j];
+ if(map_getcell(md->bl.m,x,y,CELL_CHKPASS))
+ break;
+ }
+ if(i >= 16) {
+ x = md->bl.x;
+ y = md->bl.y;
+ }
+ if(target->prev != NULL) {
+ if(target->type == BL_PC && !pc_isdead((struct map_session_data *)target))
+ pc_setpos((struct map_session_data *)target,map[md->bl.m].index,x,y,3);
+ else if(target->type == BL_MOB)
+ mob_warp((struct mob_data *)target,-1,x,y,3);
+ }
+ }
+ break;
+
+ case BA_FROSTJOKE: /* 寒いジョ?ク */
+ case DC_SCREAM: /* スクリ?ム */
+ range=battle_config.area_size; //視界全?
+ map_foreachinarea(skill_frostjoke_scream,skl->map,skl->x-range,skl->y-range,
+ skl->x+range,skl->y+range,BL_CHAR,src,skl->skill_id,skl->skill_lv,tick);
+ break;
+
+ case WZ_WATERBALL:
+ if (skl->type>1) {
+ skl->timer = 0; // skill_addtimerskillで使用されないように
+ skill_addtimerskill(src,tick+150,target->id,0,0,skl->skill_id,skl->skill_lv,skl->type-1,skl->flag);
+ skl->timer = -1;
+ }
+ skill_attack(BF_MAGIC,src,src,target,skl->skill_id,skl->skill_lv,tick,skl->flag);
+ if (skl->type <= 1) { // partial fix: it still doesn't end if the target dies
+ // should put outside of the switch, but since this is the only
+ // mage targetted spell for now,
+ struct status_change *sc_data = status_get_sc_data(src);
+ if(sc_data && sc_data[SC_MAGICPOWER].timer != -1) //マジックパ??の?果?I了
+ status_change_end(src,SC_MAGICPOWER,-1);
+ }
+ break;
+ default:
+ skill_attack(skl->type,src,src,target,skl->skill_id,skl->skill_lv,tick,skl->flag);
+ break;
+ }
+ }
+ else {
+ if(src->m != skl->map)
+ return 0;
+ switch(skl->skill_id) {
+ case WZ_METEOR:
+ if(skl->type >= 0) {
+ skill_unitsetting(src,skl->skill_id,skl->skill_lv,skl->type>>16,skl->type&0xFFFF,skl->flag);
+ clif_skill_poseffect(src,skl->skill_id,skl->skill_lv,skl->x,skl->y,tick);
+ }
+ else
+ skill_unitsetting(src,skl->skill_id,skl->skill_lv,skl->x,skl->y,skl->flag);
+ break;
+ }
+ }
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int skill_addtimerskill(struct block_list *src,unsigned int tick,int target,int x,int y,int skill_id,int skill_lv,int type,int flag)
+{
+ int i;
+
+ nullpo_retr(1, src);
+
+ if(src->type == BL_PC) {
+ struct map_session_data *sd = (struct map_session_data *)src;
+ nullpo_retr(1, sd);
+ for(i=0;i<MAX_SKILLTIMERSKILL;i++) {
+ if(sd->skilltimerskill[i].timer == -1) {
+ sd->skilltimerskill[i].timer = add_timer(tick, skill_timerskill, src->id, i);
+ sd->skilltimerskill[i].src_id = src->id;
+ sd->skilltimerskill[i].target_id = target;
+ sd->skilltimerskill[i].skill_id = skill_id;
+ sd->skilltimerskill[i].skill_lv = skill_lv;
+ sd->skilltimerskill[i].map = src->m;
+ sd->skilltimerskill[i].x = x;
+ sd->skilltimerskill[i].y = y;
+ sd->skilltimerskill[i].type = type;
+ sd->skilltimerskill[i].flag = flag;
+ sd->timerskill_count++;
+
+ return 0;
+ }
+ }
+ return 1;
+ }
+ else if(src->type == BL_MOB) {
+ struct mob_data *md = (struct mob_data *)src;
+ nullpo_retr(1, md);
+ for(i=0;i<MAX_MOBSKILLTIMERSKILL;i++) {
+ if(md->skilltimerskill[i].timer == -1) {
+ md->skilltimerskill[i].timer = add_timer(tick, skill_timerskill, src->id, i);
+ md->skilltimerskill[i].src_id = src->id;
+ md->skilltimerskill[i].target_id = target;
+ md->skilltimerskill[i].skill_id = skill_id;
+ md->skilltimerskill[i].skill_lv = skill_lv;
+ md->skilltimerskill[i].map = src->m;
+ md->skilltimerskill[i].x = x;
+ md->skilltimerskill[i].y = y;
+ md->skilltimerskill[i].type = type;
+ md->skilltimerskill[i].flag = flag;
+
+ return 0;
+ }
+ }
+ return 1;
+ }
+ else if(src->type == BL_PET) { // [Valaris]
+ struct pet_data *pd = (struct pet_data *)src;
+ nullpo_retr(1, pd);
+ for(i=0;i<MAX_MOBSKILLTIMERSKILL;i++) {
+ if(pd->skilltimerskill[i].timer == -1) {
+ pd->skilltimerskill[i].timer = add_timer(tick, skill_timerskill, src->id, i);
+ pd->skilltimerskill[i].src_id = src->id;
+ pd->skilltimerskill[i].target_id = target;
+ pd->skilltimerskill[i].skill_id = skill_id;
+ pd->skilltimerskill[i].skill_lv = skill_lv;
+ pd->skilltimerskill[i].map = src->m;
+ pd->skilltimerskill[i].x = x;
+ pd->skilltimerskill[i].y = y;
+ pd->skilltimerskill[i].type = type;
+ pd->skilltimerskill[i].flag = flag;
+
+ return 0;
+ }
+ }
+ return 1;
+ }
+ return 1;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int skill_cleartimerskill(struct block_list *src)
+{
+ int i;
+
+ nullpo_retr(0, src);
+
+ if(src->type == BL_PC) {
+ struct map_session_data *sd = (struct map_session_data *)src;
+ nullpo_retr(0, sd);
+
+ if (sd->timerskill_count <= 0)
+ return 0;
+
+ for(i=0;i<MAX_SKILLTIMERSKILL && sd->timerskill_count > 0;i++) {
+ if(sd->skilltimerskill[i].timer != -1) {
+ delete_timer(sd->skilltimerskill[i].timer, skill_timerskill);
+ sd->skilltimerskill[i].timer = -1;
+ sd->timerskill_count--;
+ }
+ }
+ }
+ else if(src->type == BL_MOB) {
+ struct mob_data *md = (struct mob_data *)src;
+ nullpo_retr(0, md);
+ for(i=0;i<MAX_MOBSKILLTIMERSKILL;i++) {
+ if(md->skilltimerskill[i].timer != -1) {
+ delete_timer(md->skilltimerskill[i].timer, skill_timerskill);
+ md->skilltimerskill[i].timer = -1;
+ }
+ }
+ }
+ else if(src->type == BL_PET) { // Ya forgot this one, Valaris. [Skotlex]
+ struct pet_data *pd = (struct pet_data *)src;
+ nullpo_retr(1, pd);
+ for(i=0;i<MAX_MOBSKILLTIMERSKILL;i++) {
+ if(pd->skilltimerskill[i].timer != -1) {
+ delete_timer(pd->skilltimerskill[i].timer, skill_timerskill);
+ pd->skilltimerskill[i].timer = -1;
+ }
+ }
+ }
+ return 0;
+}
+
+struct castend_delay {
+ struct block_list *src;
+ int target;
+ int id;
+ int lv;
+ int flag;
+};
+int skill_castend_delay_sub (int tid, unsigned int tick, int id, int data)
+{
+ struct castend_delay *dat = (struct castend_delay *)data;
+ struct block_list *target = map_id2bl(dat->target);
+
+ if (target && dat && map_id2bl(id) == dat->src && target->prev != NULL)
+ skill_castend_damage_id(dat->src, target, dat->id, dat->lv, tick, dat->flag);
+ aFree(dat);
+ return 0;
+}
+int skill_castend_delay (struct block_list* src, struct block_list *bl,int skillid,int skilllv,unsigned int tick,int flag)
+{
+ struct castend_delay *dat;
+ nullpo_retr(0, src);
+ nullpo_retr(0, bl);
+
+ dat = (struct castend_delay *)aCalloc(1, sizeof(struct castend_delay));
+ dat->src = src;
+ dat->target = bl->id;
+ dat->id = skillid;
+ dat->lv = skilllv;
+ dat->flag = flag;
+ add_timer (tick, skill_castend_delay_sub, src->id, (int)dat);
+
+ return 0;
+}
+
+/* 範?スキル使用???ャ分けここまで
+ * -------------------------------------------------------------------------
+ */
+
+/*==========================================
+ * スキル使用?i詠?・完了?AID指定?U?系?j
+ * ?iスパゲッティに向けて1?前?i?I(ダ?ポ)?j
+ *------------------------------------------
+ */
+int skill_castend_damage_id (struct block_list* src, struct block_list *bl,int skillid,int skilllv,unsigned int tick,int flag)
+{
+ struct map_session_data *sd = NULL, *tsd = NULL;
+ struct status_change *sc_data;
+
+ if(skillid < 0)
+ { // remove the debug print when this case is finished
+ ShowDebug("skill_castend_damage_id: skillid=%i\ncall: %p %p %i %i %i %i",skillid,
+ src, bl,skillid,skilllv,tick,flag);
+ return 0;
+ }
+ if (skillid > 0 && skilllv <= 0) return 0;
+
+ nullpo_retr(1, src);
+ nullpo_retr(1, bl);
+
+ if (src->m != bl->m)
+ return 1;
+
+ if (src->type == BL_PC) {
+ nullpo_retr(1, sd = (struct map_session_data *)src);
+ }
+
+ if (bl->prev == NULL)
+ return 1;
+ if (bl->type == BL_PC) {
+ nullpo_retr(1, tsd = (struct map_session_data *)bl);
+ }
+
+ if ((skillid == CR_GRANDCROSS || skillid == NPC_GRANDDARKNESS) && src != bl)
+ bl = src;
+
+ if (status_isdead(src) || (src != bl && status_isdead(bl)))
+ return 1;
+
+ sc_data = status_get_sc_data(src);
+
+ map_freeblock_lock();
+
+ switch(skillid)
+ {
+ /* ?器?U?系スキル */
+ case SM_BASH: /* バッシュ */
+ case MC_MAMMONITE: /* ?マ?ナイト */
+ case TF_DOUBLE:
+ case AC_DOUBLE: /* ダブルストレイフィング */
+ case AC_SHOWER: /* ア??シャ?? */
+ case AS_SONICBLOW: /* ソニックブ?? */
+ case KN_PIERCE: /* ピア?ス */
+ case KN_SPEARBOOMERANG: /* スピアブ??ラン */
+ case KN_BRANDISHSPEAR: /* ブランディッシュスピア */
+ case TF_POISON: /* インベナム */
+ case TF_SPRINKLESAND: /* ?サまき */
+ case AC_CHARGEARROW: /* チャ?ジア?? */
+ case RG_RAID: /* サプライズアタック */
+ case RG_INTIMIDATE: /* インティミデイト */
+ case AM_ACIDTERROR: /* アシッドテラ? */
+ case BA_MUSICALSTRIKE: /* ミュ?ジカルストライク */
+ case DC_THROWARROW: /* 矢?ち */
+ case BA_DISSONANCE: /* 不協和音 */
+ case CR_HOLYCROSS: /* ホ?リ?ク?ス */
+ case NPC_DARKCROSS:
+ case CR_SHIELDCHARGE:
+ case CR_SHIELDBOOMERANG:
+ /* 以下MOB?用 */
+ /* ???U??ASP減?ュ?U??A遠距離?U??A防御無視?U??A多段?U? */
+ case NPC_PIERCINGATT:
+ case NPC_MENTALBREAKER:
+ case NPC_RANGEATTACK:
+ case NPC_CRITICALSLASH:
+ case NPC_COMBOATTACK:
+ /* 必中?U??A毒?U??A暗??U??A沈??U??Aスタン?U? */
+ case NPC_GUIDEDATTACK:
+ case NPC_POISON:
+ case NPC_BLINDATTACK:
+ case NPC_SILENCEATTACK:
+ case NPC_STUNATTACK:
+ /* ?ホ化?U??A呪い?U??A?眠?U??AランダムATK?U? */
+ case NPC_PETRIFYATTACK:
+ case NPC_CURSEATTACK:
+ case NPC_SLEEPATTACK:
+ case NPC_RANDOMATTACK:
+ /* ???ォ?U??A地??ォ?U??A火??ォ?U??A風??ォ?U? */
+ case NPC_WATERATTACK:
+ case NPC_GROUNDATTACK:
+ case NPC_FIREATTACK:
+ case NPC_WINDATTACK:
+ /* 毒??ォ?U??A?ケ??ォ?U??A闇??ォ?U??A念??ォ?U??ASP減?ュ?U? */
+ case NPC_POISONATTACK:
+ case NPC_HOLYATTACK:
+ case NPC_DARKNESSATTACK:
+ case NPC_TELEKINESISATTACK:
+ case NPC_UNDEADATTACK:
+ case NPC_BREAKARMOR:
+ case NPC_BREAKWEAPON:
+ case NPC_BREAKHELM:
+ case NPC_BREAKSHIELD:
+ case LK_AURABLADE: /* オ?ラブレ?ド */
+ case LK_SPIRALPIERCE: /* スパイラルピア?ス */
+ case LK_HEADCRUSH: /* ヘッドクラッシュ */
+ case LK_JOINTBEAT: /* ジョイントビ?ト */
+ case CG_ARROWVULCAN: /* ア??バルカン */
+ case HW_MAGICCRASHER: /* マジッククラッシャ? */
+ case ASC_METEORASSAULT: /* ?テオアサルト */
+ case ITM_TOMAHAWK:
+ case MO_TRIPLEATTACK:
+ case CH_CHAINCRUSH: /* 連柱崩? */
+ case CH_TIGERFIST: /* 伏虎? */
+ case PA_SHIELDCHAIN: // Shield Chain
+ case PA_SACRIFICE: // Sacrifice, Aru's style.
+ case WS_CARTTERMINATION: // Cart Termination
+ case AS_VENOMKNIFE:
+ case HT_PHANTASMIC:
+ case HT_POWER:
+ case TK_DOWNKICK:
+ case TK_COUNTER:
+ skill_attack(BF_WEAPON,src,src,bl,skillid,skilllv,tick,flag);
+ break;
+
+ case MO_COMBOFINISH:
+ if (!(flag&1) && sc_data && sc_data[SC_SPIRIT].timer != -1 && sc_data[SC_SPIRIT].val2 == SL_MONK)
+ { //Becomes a splash attack when Soul Linked.
+ map_foreachinarea(skill_area_sub,
+ bl->m,bl->x-5,bl->y-5,bl->x+5,bl->y+5,BL_CHAR,
+ src,skillid,skilllv,tick, flag|BCT_ENEMY|1,
+ skill_castend_damage_id);
+ } else
+ skill_attack(BF_WEAPON,src,src,bl,skillid,skilllv,tick,flag);
+ break;
+
+ case TK_STORMKICK: // Taekwon kicks [Dralnu]
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ map_foreachinarea(skill_attack_area, src->m,
+ src->x-2, src->y-2, src->x+2, src->y+2, BL_CHAR,
+ BF_WEAPON, src, src, skillid, skilllv, tick, flag, BCT_ENEMY);
+ break;
+ case TK_JUMPKICK:
+ if(sd) {
+ if (!pc_can_move(sd))
+ return 0;
+ skill_attack(BF_WEAPON,src,src,bl,skillid,skilllv,tick,flag);
+ pc_movepos(sd,bl->x,bl->y,0);
+ clif_slide(src,bl->x,bl->y);
+ }
+ break;
+ case ASC_BREAKER: /* ソウルブレ?カ? */ // [DracoRPG]
+ // Separate weapon and magic attacks
+ skill_attack(BF_WEAPON,src,src,bl,skillid,skilllv,tick,flag);
+ skill_attack(BF_MAGIC,src,src,bl,skillid,skilllv,tick,flag);
+ break;
+
+ case SN_SHARPSHOOTING: /* シャ?プシュ?ティング */
+ // Does it stop if touch an obstacle? it shouldn't shoot trough walls
+ map_foreachinpath (skill_attack_area,src->m,src->x,src->y,bl->x,bl->y,2,BL_CHAR,
+ BF_WEAPON,src,src,skillid,skilllv,tick,flag,BCT_ENEMY); // varargs
+ break;
+
+ case MO_INVESTIGATE: /* ?勁 */
+ skill_attack(BF_WEAPON,src,src,bl,skillid,skilllv,tick,flag);
+ if (sc_data && sc_data[SC_BLADESTOP].timer != -1)
+ status_change_end(src,SC_BLADESTOP,-1);
+ break;
+
+ case RG_BACKSTAP: /* バックスタブ */
+ {
+ int dir = map_calc_dir(src, bl->x, bl->y), t_dir = status_get_dir(bl);
+ if ((!check_distance_bl(src, bl, 0) && !map_check_dir(dir, t_dir)) || bl->type == BL_SKILL) {
+ if (sc_data && sc_data[SC_HIDING].timer != -1)
+ status_change_end(src, SC_HIDING, -1); // ハイディング解?
+ skill_attack(BF_WEAPON, src, src, bl, skillid, skilllv, tick, flag);
+ dir = dir < 4 ? dir+4 : dir-4; // change direction [Celest]
+ if (tsd)
+ tsd->dir = dir;
+ else if (bl->type == BL_MOB) {
+ struct mob_data *tmd = (struct mob_data *)bl;
+ if (tmd) tmd->dir = dir;
+ }
+ clif_changed_dir(bl);
+ }
+ else if (sd)
+ clif_skill_fail(sd,skillid,0,0);
+ }
+ break;
+
+ case MO_FINGEROFFENSIVE: /* 指? */
+ {
+ skill_attack(BF_WEAPON,src,src,bl,skillid,skilllv,tick,flag);
+ if (battle_config.finger_offensive_type && sd) {
+ int i;
+ for (i = 1; i < sd->spiritball_old; i++)
+ skill_addtimerskill(src, tick + i * 200, bl->id, 0, 0, skillid, skilllv, BF_WEAPON, flag);
+ sd->canmove_tick = tick + (sd->spiritball_old - 1) * 200;
+ }
+ if (sc_data && sc_data[SC_BLADESTOP].timer != -1)
+ status_change_end(src,SC_BLADESTOP,-1);
+ }
+ break;
+
+ case MO_CHAINCOMBO: /* 連打?カ */
+ {
+ skill_attack(BF_WEAPON,src,src,bl,skillid,skilllv,tick,flag);
+ if (sc_data && sc_data[SC_BLADESTOP].timer != -1)
+ status_change_end(src,SC_BLADESTOP,-1);
+ }
+ break;
+
+ case KN_CHARGEATK:
+ case MO_EXTREMITYFIST: /* 阿?C羅覇鳳? */
+ {
+ if(sd && !check_distance_bl(src, bl, 1)) { //Need to move to target.
+ struct walkpath_data wpd;
+ int dx,dy,speed;
+
+ if (!pc_can_move(sd)) { //You need to be able to move to attack/reach target.
+ clif_skill_fail(sd,skillid,0,0);
+ break;
+ }
+ dx = bl->x - sd->bl.x;
+ dy = bl->y - sd->bl.y;
+ if(dx > 0) dx++;
+ else if(dx < 0) dx--;
+ if (dy > 0) dy++;
+ else if(dy < 0) dy--;
+ if(dx == 0 && dy == 0) dx++;
+ if (path_search(&wpd,src->m,sd->bl.x,sd->bl.y,sd->bl.x+dx,sd->bl.y+dy,1) == -1) {
+ dx = bl->x - sd->bl.x;
+ dy = bl->y - sd->bl.y;
+ if(path_search(&wpd,src->m,sd->bl.x,sd->bl.y,sd->bl.x+dx,sd->bl.y+dy,1) == -1) {
+ clif_skill_fail(sd,skillid,0,0);
+ break;
+ }
+ }
+ sd->to_x = sd->bl.x + dx;
+ sd->to_y = sd->bl.y + dy;
+ if (skillid == KN_CHARGEATK) //Store distance in flag [Skotlex]
+ flag = wpd.path_len; //Path_len is a pretty good approximate of the distance.
+ if (skillid != MO_EXTREMITYFIST || battle_check_target(src, bl, BCT_ENEMY) > 0) //Check must be done here because EF should be broken this way.. [Skotlex]
+ skill_attack(BF_WEAPON,src,src,bl,skillid,skilllv,tick,flag);
+ else
+ clif_skill_fail(sd,skillid,0,0);
+ speed = sd->speed;
+ sd->speed = 20; //Simulate ultra fast walk speed. [Skotlex]
+// clif_updatestatus(sd, SP_SPEED); //Not sure yet if this is needed.
+ clif_walkok(sd);
+ clif_movechar(sd);
+ if(dx < 0) dx = -dx;
+ if(dy < 0) dy = -dy;
+ sd->attackabletime = sd->canmove_tick = tick + 100 + sd->speed * ((dx > dy)? dx:dy);
+ if(sd->canact_tick < sd->canmove_tick)
+ sd->canact_tick = sd->canmove_tick;
+ sd->speed = speed;
+// clif_updatestatus(sd, SP_SPEED);
+ pc_movepos(sd,sd->to_x,sd->to_y,1);
+ }
+ else //Assume minimum distance of 1 for Charge.
+ skill_attack(BF_WEAPON,src,src,bl,skillid,skilllv,tick,skillid == KN_CHARGEATK?1:flag);
+ if (skillid == MO_EXTREMITYFIST && sc_data)
+ {
+ if (sc_data[SC_EXPLOSIONSPIRITS].timer != -1)
+ status_change_end(src, SC_EXPLOSIONSPIRITS, -1);
+ if (sc_data[SC_BLADESTOP].timer != -1)
+ status_change_end(src,SC_BLADESTOP,-1);
+ if (sc_data[SC_COMBO].timer != -1) //This is one is here to make combo end even if skill failed.
+ status_change_end(src,SC_COMBO,-1);
+ }
+ }
+ break;
+
+ /* ?器系範??U?スキル */
+ case AS_GRIMTOOTH: /* グリムトゥ?ス */
+ case MC_CARTREVOLUTION: /* カ?トレヴォリュ?ション */
+ case NPC_SPLASHATTACK: /* スプラッシュアタック */
+ if(flag&1){
+ /* 個別にダ??ジを?える */
+ if(bl->id!=skill_area_temp[1]){
+ skill_attack(BF_WEAPON,src,src,bl,skillid,skilllv,tick,
+ 0x0500);
+ }
+ } else {
+ int ar = 1;
+ int x = bl->x, y = bl->y;
+ switch (skillid) {
+ case NPC_SPLASHATTACK:
+ ar=3;
+ break;
+ }
+
+ skill_area_temp[1]=bl->id;
+ skill_area_temp[2]=x;
+ skill_area_temp[3]=y;
+ /* まずタ?ゲットに?U?を加える */
+ skill_attack(BF_WEAPON,src,src,bl,skillid,skilllv,tick,0);
+ /* その後タ?ゲット以外の範??の敵全?に??を?sう */
+ map_foreachinarea(skill_area_sub,
+ bl->m,x-ar,y-ar,x+ar,y+ar,BL_CHAR,
+ src,skillid,skilllv,tick, flag|BCT_ENEMY|1,
+ skill_castend_damage_id);
+ }
+ break;
+
+ case AS_SPLASHER:
+ if (flag & 1) { //Invoked from map_foreachinarea, skill_area_temp[0] holds number of targets to divide damage by.
+ if (bl->id != skill_area_temp[1])
+ skill_attack(BF_WEAPON, src, src, bl, skillid, skilllv, tick, skill_area_temp[0]);
+ else
+ skill_attack(BF_WEAPON, src, src, bl, skillid, skilllv, tick, 0);
+ } else {
+ skill_area_temp[0] = 0;
+ skill_area_temp[1] = bl->id;
+ map_foreachinarea(skill_area_sub, bl->m,
+ bl->x-2, bl->y-2, bl->x+2, bl->y+2, BL_CHAR,
+ src, skillid, skilllv, tick, BCT_ENEMY, skill_area_sub_count);
+ skill_area_temp[0]--; //Substract one, the original target shouldn't count. [Skotlex]
+ map_foreachinarea(skill_area_sub, bl->m,
+ bl->x-1, bl->y-1, bl->x+1, bl->y+1, BL_CHAR,
+ src, skillid, skilllv, tick, BCT_ENEMY|1,
+ skill_castend_damage_id);
+ }
+ break;
+
+
+ case SM_MAGNUM:
+ if(flag&1){
+ int dist = distance_blxy (bl, skill_area_temp[2], skill_area_temp[3]);
+ skill_attack(BF_WEAPON,src,src,bl,skillid,skilllv,tick,
+ 0x0500|dist);
+ } else {
+ skill_area_temp[1]=src->id;
+ skill_area_temp[2]=src->x;
+ skill_area_temp[3]=src->y;
+ map_foreachinarea(skill_area_sub,
+ src->m,src->x-2,src->y-2,src->x+2,src->y+2,BL_CHAR,
+ src,skillid,skilllv,tick, flag|BCT_ENEMY|1,
+ skill_castend_damage_id);
+ status_change_start (src,SC_WATK_ELEMENT,3,20,0,0,10000,0); //Initiate 10% of your damage becomes fire element.
+ clif_skill_nodamage (src,src,skillid,skilllv,1);
+ }
+ break;
+
+ case KN_BOWLINGBASH: /* ボウリングバッシュ */
+ if(flag&1){
+ /* 個別にダ??ジを?える */
+ if(bl->id!=skill_area_temp[1])
+ skill_attack(BF_WEAPON,src,src,bl,skillid,skilllv,tick,0x0500);
+ } else {
+ int i,c; /* 他?lから聞いた動きなので間違ってる可能?ォ大??率が?いっす?? */
+ /* まずタ?[ゲットに?U撃を加える */
+ c = skill_get_blewcount(skillid,skilllv);
+ if(map_flag_gvg(bl->m) || status_get_mexp(bl))
+ c = 0;
+ for(i=0;i<c;i++){
+ skill_blown(src,bl,0x20000|1);
+ skill_area_temp[0]=0;
+ map_foreachinarea(skill_area_sub,
+ bl->m,bl->x-1,bl->y-1,bl->x+1,bl->y+1,BL_CHAR,
+ src,skillid,skilllv,tick, flag|BCT_ENEMY ,
+ skill_area_sub_count);
+ if(skill_area_temp[0]>1) break;
+ }
+ clif_blown(bl); //Update target pos.
+ skill_area_temp[1]=bl->id;
+ /* その後タ?ゲット以外の範??の敵全?に??を?sう */
+ map_foreachinarea(skill_area_sub,
+ bl->m,bl->x-1,bl->y-1,bl->x+1,bl->y+1,BL_CHAR,
+ src,skillid,skilllv,tick, flag|BCT_ENEMY|1,
+ skill_castend_damage_id);
+ skill_attack(BF_WEAPON,src,src,bl,skillid,skilllv,tick,0);
+ }
+ break;
+
+ case KN_SPEARSTAB: /* スピアスタブ */
+ if(flag&1){
+ /* 個別にダ??[ジを与える */
+ if (bl->id==skill_area_temp[1])
+ break;
+ if (skill_attack(BF_WEAPON,src,src,bl,skillid,skilllv,tick,0x0500) && !status_get_mexp(bl))
+ skill_blown(src,bl,skill_area_temp[2]);
+ } else {
+ int x=bl->x,y=bl->y,i,dir;
+ /* まずタ?[ゲットに?U撃を加える */
+ dir = map_calc_dir(bl,src->x,src->y);
+ skill_area_temp[1] = bl->id;
+ skill_area_temp[2] = skill_get_blewcount(skillid,skilllv)|dir<<20;
+ if (skill_attack(BF_WEAPON,src,src,bl,skillid,skilllv,tick,0) && !status_get_mexp(bl))
+ skill_blown(src,bl,skill_area_temp[2]);
+ for (i=0;i<4;i++) {
+ map_foreachinarea(skill_area_sub,bl->m,x,y,x,y,BL_CHAR,
+ src,skillid,skilllv,tick,flag|BCT_ENEMY|1,
+ skill_castend_damage_id);
+ x += dirx[dir];
+ y += diry[dir];
+ }
+ }
+ break;
+
+ case TK_TURNKICK:
+ case MO_BALKYOUNG: //Active part of the attack. Skill-attack [Skotlex]
+ {
+ skill_area_temp[1] = bl->id; //NOTE: This is used in skill_castend_nodamage_id to avoid affecting the target.
+ if (skill_attack(BF_WEAPON,src,src,bl,skillid,skilllv,tick,0))
+ map_foreachinarea(skill_area_sub,bl->m,bl->x-1,bl->y-1,bl->x+1,bl->y+1,BL_CHAR,
+ src,skillid,skilllv,tick,flag|BCT_ENEMY|1,
+ skill_castend_nodamage_id);
+ }
+ break;
+ case CH_PALMSTRIKE: // Palm Strike takes effect 1sec after casting. [Skotlex]
+ // clif_skill_nodamage(src,bl,skillid,skilllv,0); //Can't make this one display the correct attack animation delay :/
+ clif_damage(src,bl,tick,status_get_amotion(src),0,0,1,4,0); //Displays MISS, but better than nothing :X
+ skill_addtimerskill(src, tick + 1000, bl->id, 0, 0, skillid, skilllv, BF_WEAPON, flag);
+ break;
+
+ case ALL_RESURRECTION: /* リザレクション */
+ case PR_TURNUNDEAD: /* タ?ンアンデッド */
+ if (battle_check_undead(status_get_race(bl),status_get_elem_type(bl)))
+ skill_attack(BF_MAGIC,src,src,bl,skillid,skilllv,tick,flag);
+ break;
+
+ /* 魔法系スキル */
+ case MG_SOULSTRIKE: /* ソウルストライク */
+ case NPC_DARKSTRIKE: /*闇ソウルストライク*/
+ case MG_COLDBOLT: /* コ?[ルドボルト */
+ case MG_FIREBOLT: /* ファイア?[ボルト */
+ case MG_LIGHTNINGBOLT: /* ライトニングボルト */
+ case WZ_EARTHSPIKE: /* ア?[ススパイク */
+ case AL_HEAL: /* ヒ?[ル */
+ case AL_HOLYLIGHT: /* ホ?[リ?[ライト */
+ case WZ_JUPITEL: /* ユピテルサンダ?[ */
+ case NPC_DARKTHUNDER: /*闇ユピテル*/
+ case NPC_MAGICALATTACK: /* MOB:魔法打??U? */
+ case PR_ASPERSIO: /* アスペルシオ */
+ case MG_FROSTDIVER: /* フ?ストダイバ?[ */
+ case WZ_SIGHTBLASTER:
+ skill_attack(BF_MAGIC,src,src,bl,skillid,skilllv,tick,flag);
+ break;
+
+ case WZ_WATERBALL: /* ウォ?タ?ボ?ル */
+ skill_attack(BF_MAGIC,src,src,bl,skillid,skilllv,tick,flag);
+ if (skilllv>1) {
+ int range = skilllv > 5 ? 2 : skilllv/2;
+ //Rain doesn't affect WATERBALL (Rain has been removed at kRO) [Lupus]
+ //int cnt = (!map[src->m].flag.rain) ? skill_count_water(src,range) - 1 : skill_get_num(skillid,skilllv) - 1;
+ int cnt = (src->type==BL_PC)?skill_count_water(src,range) - 1:(skilllv>3?24:8);
+ if (cnt > 0)
+ skill_addtimerskill(src,tick+150,bl->id,0,0,
+ skillid,skilllv,cnt,flag);
+ }
+ break;
+
+ case PR_BENEDICTIO: /* ?ケ??~福 */
+ { //Should attack undead and demons. [Skotlex]
+ int race = status_get_race(bl);
+ if (battle_check_undead(race, status_get_elem_type(bl)) || race == 6)
+ skill_attack(BF_MAGIC, src, src, bl, skillid, skilllv, tick, flag);
+ }
+ break;
+
+ /* 魔法系範??U?スキル */
+ case MG_NAPALMBEAT: /* ナパ?ムビ?ト */
+ case MG_FIREBALL: /* ファイヤ?ボ?ル */
+ case WZ_SIGHTRASHER: /* サイトラッシャ?[ */
+ if (flag & 1) {
+ /* 個別にダ??ジを?える */
+ if (bl->id != skill_area_temp[1]){
+ if(skillid == MG_FIREBALL){ /* ファイヤ?ボ?ルなら中?Sからの距離を計算 */
+ skill_area_temp[0] = distance_blxy(bl, skill_area_temp[2], skill_area_temp[3]);
+ }
+ skill_attack(BF_MAGIC,src,src,bl,skillid,skilllv,tick,
+ skill_area_temp[0]| 0x0500);
+ }
+ } else {
+ int ar;
+ skill_area_temp[0]=0;
+ skill_area_temp[1]=bl->id;
+ switch (skillid) {
+ case MG_NAPALMBEAT:
+ ar = 1;
+ /* ナパ?[ムビ?[トは分散ダ??[ジなので敵の?狽?狽ヲる */
+ map_foreachinarea(skill_area_sub,
+ bl->m,bl->x-ar,bl->y-ar,bl->x+ar,bl->y+ar,BL_CHAR,
+ src,skillid,skilllv,tick,flag|BCT_ENEMY,
+ skill_area_sub_count);
+ break;
+ case MG_FIREBALL:
+ ar = 2;
+ skill_area_temp[2]=bl->x;
+ skill_area_temp[3]=bl->y;
+ break;
+ case WZ_SIGHTRASHER:
+ default:
+ ar = 3;
+ bl = src;
+ status_change_end(src,SC_SIGHT,-1);
+ break;
+ }
+ if (skillid==WZ_SIGHTRASHER) {
+ /* スキルエフェクト表示 */
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ } else {
+ /* タ?[ゲットに?U撃を加える(スキルエフェクト表示) */
+ skill_attack(BF_MAGIC,src,src,bl,skillid,skilllv,tick,
+ skill_area_temp[0]);
+ }
+ /* タ?[ゲット以外の範囲内の敵全体に??を?sう */
+ map_foreachinarea(skill_area_sub,
+ bl->m,bl->x-ar,bl->y-ar,bl->x+ar,bl->y+ar,BL_CHAR,
+ src,skillid,skilllv,tick, flag|BCT_ENEMY|1,
+ skill_castend_damage_id);
+ }
+ break;
+
+ case HW_NAPALMVULCAN: // Fixed By SteelViruZ
+ if (flag & 1) {
+ if (bl->id != skill_area_temp[1])
+ skill_attack(BF_MAGIC, src, src, bl, skillid, skilllv, tick, skill_area_temp[0]);
+ } else {
+ skill_area_temp[0] = 0;
+ skill_area_temp[1] = bl->id;
+ map_foreachinarea(skill_area_sub, bl->m,
+ bl->x-1, bl->y-1, bl->x+1, bl->y+1, BL_CHAR,
+ src, skillid, skilllv, tick, flag|BCT_ENEMY,
+ skill_area_sub_count);
+ skill_attack(BF_MAGIC, src, src, bl, skillid, skilllv, tick, skill_area_temp[0]);
+ map_foreachinarea(skill_area_sub, bl->m,
+ bl->x-1, bl->y-1, bl->x+1, bl->y+1, BL_CHAR,
+ src, skillid, skilllv, tick, flag|BCT_ENEMY|1,
+ skill_castend_damage_id);
+ }
+ break;
+
+ case WZ_FROSTNOVA: /* フ?ストノヴァ */
+ map_foreachinarea(skill_attack_area, src->m,
+ src->x-5, src->y-5, bl->x+5, bl->y+5, BL_CHAR,
+ BF_MAGIC, src, src, skillid, skilllv, tick, flag, BCT_ENEMY);
+ break;
+
+ case SL_STIN:
+ case SL_STUN:
+ case SL_SMA:
+ if (sd && bl->type != BL_MOB) {
+ status_change_start(src,SC_STAN,skilllv,0,0,0,3000,0);
+ clif_skill_fail(sd,skillid,0,0);
+ break;
+ }
+ skill_attack(BF_MAGIC,src,src,bl,skillid,skilllv,tick,flag);
+ break;
+
+ /* その他 */
+ case HT_BLITZBEAT: /* ブリッツビ?ト */
+ if (flag & 1) { //Invoked from map_foreachinarea, skill_area_temp[0] holds number of targets to divide damage by.
+ skill_attack(BF_MISC, src, src, bl, skillid, skilllv, tick, skill_area_temp[0]);
+ } else {
+ skill_area_temp[0] = 0;
+ skill_area_temp[1] = bl->id;
+ if (flag & 0xf00000) //Warning, 0x100000 is currently BCT_NEUTRAL, so don't mix it when asking for the enemy. [Skotlex]
+ map_foreachinarea(skill_area_sub, bl->m,
+ bl->x-1, bl->y-1, bl->x+1, bl->y+1, BL_CHAR,
+ src, skillid, skilllv, tick, BCT_ENEMY, skill_area_sub_count);
+ map_foreachinarea(skill_area_sub, bl->m,
+ bl->x-1, bl->y-1, bl->x+1, bl->y+1, BL_CHAR,
+ src, skillid, skilllv, tick, BCT_ENEMY|1,
+ skill_castend_damage_id);
+ }
+ break;
+
+ case CR_GRANDCROSS: /* グランドク?ス */
+ case NPC_GRANDDARKNESS: /*闇グランドク?ス*/
+ /* スキルユニット配置 */
+ skill_castend_pos2(src,bl->x,bl->y,skillid,skilllv,tick,0);
+ if(sd)
+ sd->canmove_tick = tick + 900;
+ else if(src->type == BL_MOB)
+ mob_changestate((struct mob_data *)src,MS_DELAY,900);
+ break;
+
+ case NPC_DARKBREATH:
+ clif_emotion(src,7);
+ skill_attack(BF_MISC,src,src,bl,skillid,skilllv,tick,flag);
+ break;
+
+ case SN_FALCONASSAULT: /* ファルコンアサルト */
+ case PA_PRESSURE: /* プレッシャ? */
+ case CR_ACIDDEMONSTRATION: // Acid Demonstration
+ case TF_THROWSTONE: /* ?ホ投げ */
+ case NPC_SMOKING: /* スモ?キング */
+ skill_attack(BF_MISC,src,src,bl,skillid,skilllv,tick,flag);
+ break;
+
+ // Celest
+ case PF_SOULBURN:
+ if (rand()%100 < (skilllv < 5 ? 30 + skilllv * 10 : 70)) {
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ if (skilllv == 5)
+ skill_attack(BF_MAGIC,src,src,bl,skillid,skilllv,tick,0 );
+ if (tsd) {
+ tsd->status.sp = 0;
+ clif_updatestatus(tsd,SP_SP);
+ }
+ } else {
+ clif_skill_nodamage(src,src,skillid,skilllv,1);
+ if (skilllv == 5)
+ skill_attack(BF_MAGIC,src,src,src,skillid,skilllv,tick,0 );
+ if (sd) {
+ sd->status.sp = 0;
+ clif_updatestatus(sd,SP_SP);
+ }
+ }
+ if (sd) pc_blockskill_start (sd, skillid, (skilllv < 5 ? 10000: 15000));
+ break;
+
+ case NPC_SELFDESTRUCTION: /* 自爆 */
+ if (flag & 1) {
+ /* 個別にダ??[ジを与える */
+ if (bl->id != skill_area_temp[1])
+ skill_attack(BF_MISC, src, src, bl, NPC_SELFDESTRUCTION, skilllv, tick, flag);
+ } else {
+ skill_area_temp[1] = bl->id;
+ if(bl->type==BL_PC)
+ skill_area_temp[2] = 999999;
+ else
+ skill_area_temp[2] = status_get_hp(src);
+ clif_skill_nodamage(src, src, NPC_SELFDESTRUCTION, -1, 1);
+ if(bl->type==BL_PC)
+ map_foreachinarea(skill_area_sub, bl->m,
+ bl->x-10, bl->y-10, bl->x+10, bl->y+10, BL_CHAR,
+ src, skillid, skilllv, tick, flag|BCT_ENEMY|1,
+ skill_castend_damage_id);
+ else
+ map_foreachinarea(skill_area_sub, bl->m,
+ bl->x-5, bl->y-5, bl->x+5, bl->y+5, BL_CHAR,
+ src, skillid, skilllv, tick, flag|BCT_ENEMY|1,
+ skill_castend_damage_id);
+ battle_damage(src, src, skill_area_temp[2], 0);
+ }
+ break;
+
+ /* HP吸?/HP吸?魔法 */
+ case NPC_BLOODDRAIN:
+ case NPC_ENERGYDRAIN:
+ {
+ int heal = skill_attack( (skillid == NPC_BLOODDRAIN) ? BF_WEAPON : BF_MAGIC,
+ src, src, bl, skillid, skilllv, tick, flag);
+ if (heal > 0){
+ struct block_list tbl;
+ tbl.id = 0;
+ tbl.type = BL_NUL;
+ tbl.m = src->m;
+ tbl.x = src->x;
+ tbl.y = src->y;
+ clif_skill_nodamage(&tbl, src, AL_HEAL, heal, 1);
+ battle_heal(NULL, src, heal, 0, 0);
+ }
+ }
+ break;
+
+ case 0:
+ if(sd) {
+ if (flag & 3){
+ if (bl->id != skill_area_temp[1])
+ skill_attack(BF_WEAPON, src, src, bl, skillid, skilllv, tick, 0x0500);
+ } else {
+ int ar = sd->splash_range;
+ skill_area_temp[1] = bl->id;
+ map_foreachinarea(skill_area_sub,
+ bl->m, bl->x - ar, bl->y - ar, bl->x + ar, bl->y + ar, BL_CHAR,
+ src, skillid, skilllv, tick, flag | BCT_ENEMY | 1,
+ skill_castend_damage_id);
+ }
+ }
+ break;
+
+ default:
+ ShowWarning("Unknown skill used:%d\n",skillid);
+ map_freeblock_unlock();
+ return 1;
+ }
+
+ map_freeblock_unlock();
+
+ return 0;
+}
+
+/*==========================================
+ * スキル使用?i詠?・完了?AID指定支援系?j
+ *------------------------------------------
+ */
+int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, int skillid, int skilllv, unsigned int tick, int flag)
+{
+ struct map_session_data *sd = NULL;
+ struct map_session_data *dstsd = NULL;
+ struct mob_data *md = NULL;
+ struct mob_data *dstmd = NULL;
+ int i;
+ int sc_def_vit, sc_def_mdef;
+// int sc_dex, sc_luk;
+
+ if(skillid < 0)
+ { // remove the debug print when this case is finished
+ ShowDebug("skill_castend_damage_id: skillid=%i\ncall: %p %p %i %i %i %i",skillid,
+ src, bl,skillid,skilllv,tick,flag);
+ return 0;
+ }
+ if(skillid > 0 && skilllv <= 0) return 0; // celest
+
+ nullpo_retr(1, src);
+ nullpo_retr(1, bl);
+
+ if (src->m != bl->m)
+ return 1;
+
+ if (src->type == BL_PC) {
+ nullpo_retr (1, sd = (struct map_session_data *)src);
+ } else if (src->type == BL_MOB) {
+ nullpo_retr (1, md = (struct mob_data *)src);
+ }
+
+ sc_def_vit = status_get_sc_def_vit (bl);
+ sc_def_mdef = status_get_sc_def_mdef (bl);
+
+ if (bl->type == BL_PC){
+ nullpo_retr (1, dstsd = (struct map_session_data *)bl);
+ } else if (bl->type == BL_MOB){
+ nullpo_retr (1, dstmd = (struct mob_data *)bl);
+ }
+
+ if(bl->prev == NULL)
+ return 1;
+ if(sd && pc_isdead(sd))
+ return 1;
+ if(dstsd && pc_isdead(dstsd) && skillid != ALL_RESURRECTION && skillid != PR_REDEMPTIO)
+ return 1;
+//Shouldn't be needed, skillnotok's return value is highly unlikely to have changed after you started casting. [Skotlex]
+// if (sd && skillnotok(skillid, sd)) // [MouseJstr]
+// return 0;
+
+ //Check for undead skills that convert a no-damage skill into a damage one. [Skotlex]
+ switch (skillid) {
+ case AL_HEAL:
+ case ALL_RESURRECTION:
+ case PR_ASPERSIO:
+ if (battle_check_undead(status_get_race(bl),status_get_elem_type(bl))) {
+ if (battle_check_target(src, bl, BCT_ENEMY) < 1) {
+ //Offensive heal does not works on non-enemies. [Skotlex]
+ if (sd) clif_skill_fail(sd,skillid,0,0);
+ return 0;
+ }
+ if(!sd) {
+ //Prevent non-players from casting offensive heal. [Skotlex]
+ clif_emotion(src, 4);
+ return 0;
+ }
+ return skill_castend_damage_id (src, bl, skillid, skilllv, tick, flag);
+ }
+ break;
+ }
+
+ map_freeblock_lock();
+ switch(skillid)
+ {
+ case AL_HEAL: /* ヒ?ル */
+ {
+ int heal = skill_calc_heal(src, skilllv);
+ int heal_get_jobexp;
+ int skill;
+ struct status_change *sc_data = status_get_sc_data(bl);
+
+
+ if (skilllv > 10)
+ heal = 9999; //9999ヒ?[ル
+ if (status_isimmune(bl) || (dstmd && dstmd->class_ == MOBID_EMPERIUM))
+ heal=0; /* ?金蟲カ?ド?iヒ?ル量0?j */
+ if (sd) {
+ if ((skill = pc_checkskill(sd, HP_MEDITATIO)) > 0) // ?ディテイティオ
+ heal += heal * skill * 2 / 100;
+ if (sd && dstsd && sd->status.partner_id == dstsd->status.char_id &&
+ (sd->class_&MAPID_UPPERMASK) == MAPID_SUPER_NOVICE && sd->status.sex == 0) //自分も?象もPC、?象が自分のパ?トナ?、自分がスパノビ、自分が♀なら
+ heal = heal*2; //スパノビの嫁が旦那にヒ?ルすると2倍になる
+ }
+
+ if (sc_data && sc_data[SC_KAITE].timer != -1
+ && !(status_get_mode(src)&MD_BOSS)
+ ) { //Bounce back heal
+ if (--sc_data[SC_KAITE].val2 <= 0)
+ status_change_end(bl, SC_KAITE, -1);
+ if (src == bl) heal=0; //When you try to heal yourself and you are under Kaite, the heal is voided.
+ clif_skill_nodamage (src, src, skillid, heal, 1);
+ heal_get_jobexp = battle_heal(NULL,src,heal,0,0);
+ } else {
+ clif_skill_nodamage (src, bl, skillid, heal, 1);
+ heal_get_jobexp = battle_heal(NULL,bl,heal,0,0);
+ }
+
+ // JOB??値獲得
+ if(sd && dstsd && heal > 0 && sd != dstsd && battle_config.heal_exp > 0){
+ heal_get_jobexp = heal_get_jobexp * battle_config.heal_exp / 100;
+ if (heal_get_jobexp <= 0)
+ heal_get_jobexp = 1;
+ if (bl->type == BL_PC) // Give heal experience only when healing players [Harbin]
+ pc_gainexp (sd, 0, heal_get_jobexp);
+ }
+ }
+ break;
+
+ case PR_REDEMPTIO:
+ if (sd && !(flag&1)) {
+ if (sd->status.party_id == 0) {
+ clif_skill_fail(sd,skillid,0,0);
+ break;
+ }
+ skill_area_temp[0] = 0;
+ party_foreachsamemap(skill_area_sub,
+ sd,1,
+ src,skillid,skilllv,tick, flag|BCT_PARTY|1,
+ skill_castend_nodamage_id);
+ if (skill_area_temp[0] == 0) {
+ clif_skill_fail(sd,skillid,0,0);
+ break;
+ }
+ skill_area_temp[0] = 5 - skill_area_temp[0]; // The actual penalty...
+ if (skill_area_temp[0] > 0 && !map[src->m].flag.nopenalty) { //Apply penalty
+ sd->status.base_exp -= pc_nextbaseexp(sd) * skill_area_temp[0] * 2/1000; //0.2% penalty per each.
+ sd->status.job_exp -= pc_nextjobexp(sd) * skill_area_temp[0] * 2/1000;
+ clif_updatestatus(sd,SP_BASEEXP);
+ clif_updatestatus(sd,SP_JOBEXP);
+ }
+ status_change_start(src,SkillStatusChangeTable[skillid],skilllv,0,0,0,0,0); //SC_COMA :P
+ break;
+ } else if (dstsd && pc_isdead(dstsd) && flag&1) { //Revive
+ skill_area_temp[0]++; //Count it in, then fall-through to the Resurrection code.
+ skilllv = 3; //Resurrection level 3 is used
+ } else //Invalid target, skip resurrection.
+ break;
+
+ case ALL_RESURRECTION: /* リザレクション */
+ if(sd && map_flag_gvg(bl->m))
+ { //No reviving in WoE grounds!
+ clif_skill_fail(sd,skillid,0,0);
+ break;
+ }
+ if(dstsd) {
+ int per = 0;
+ if (map[bl->m].flag.pvp && dstsd->pvp_point < 0)
+ break; /* PVPで復活不可能?態 */
+
+ if (pc_isdead(dstsd)) { /* 死亡判定 */
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ switch(skilllv){
+ case 1: per=10; break;
+ case 2: per=30; break;
+ case 3: per=50; break;
+ case 4: per=80; break;
+ }
+ dstsd->status.hp = dstsd->status.max_hp * per / 100;
+ if (dstsd->status.hp <= 0) dstsd->status.hp = 1;
+ if (dstsd->special_state.restart_full_recover) { /* オシリスカ?ド */
+ dstsd->status.hp = dstsd->status.max_hp;
+ dstsd->status.sp = dstsd->status.max_sp;
+ }
+ pc_setstand(dstsd);
+ if(battle_config.pc_invincible_time > 0)
+ pc_setinvincibletimer(dstsd, battle_config.pc_invincible_time);
+ clif_updatestatus(dstsd, SP_HP);
+ clif_resurrection(bl, 1);
+ if(sd && sd != dstsd && battle_config.resurrection_exp > 0) {
+ int exp = 0,jexp = 0;
+ int lv = dstsd->status.base_level - sd->status.base_level, jlv = dstsd->status.job_level - sd->status.job_level;
+ if(lv > 0) {
+ exp = (int)((double)dstsd->status.base_exp * (double)lv * (double)battle_config.resurrection_exp / 1000000.);
+ if (exp < 1) exp = 1;
+ }
+ if(jlv > 0) {
+ jexp = (int)((double)dstsd->status.job_exp * (double)lv * (double)battle_config.resurrection_exp / 1000000.);
+ if (jexp < 1) jexp = 1;
+ }
+ if(exp > 0 || jexp > 0)
+ pc_gainexp (sd, exp, jexp);
+ }
+ }
+ }
+ break;
+
+ case AL_DECAGI: /* 速度減?ュ */
+ if (status_isimmune(bl)) {
+ clif_skill_nodamage(src,bl,skillid,skilllv,0);
+ break;
+ }
+ if (rand() % 100 < (40 + skilllv * 2 + (status_get_lv(src) + status_get_int(src))/5 +(sc_def_mdef-100))) { //0 defense is sc_def_mdef == 100! [Skotlex]
+ i = skill_get_time(skillid,skilllv);
+ if (bl->type == BL_PC) i/=2; //Halved duration for Players
+ status_change_start (bl, SkillStatusChangeTable[skillid], skilllv, 0, 0, 0, i, 0);
+ clif_skill_nodamage (src, bl, skillid, skilllv, 1);
+ }
+ break;
+
+ case AL_CRUCIS:
+ if (flag & 1) {
+ int race = status_get_race (bl), ele = status_get_elem_type (bl);
+ if (battle_check_target (src, bl, BCT_ENEMY) && (race == 6 || battle_check_undead (race, ele))) {
+ int slv = status_get_lv (src),tlv = status_get_lv (bl);
+ int rate = 23 + skilllv*4 + slv - tlv;
+ if (rand()%100 < rate)
+ status_change_start(bl,SkillStatusChangeTable[skillid],skilllv,0,0,0,0,0);
+ }
+ } else {
+ map_foreachinarea(skill_area_sub,
+ src->m, src->x-15, src->y-15, src->x+15, src->y+15, BL_CHAR,
+ src, skillid, skilllv, tick, flag|BCT_ENEMY|1,
+ skill_castend_nodamage_id);
+ clif_skill_nodamage(src, bl, skillid, skilllv, 1);
+ }
+ break;
+
+ case PR_LEXDIVINA: /* レックスディビ?ナ */
+ {
+ struct status_change *sc_data = status_get_sc_data(bl);
+ if (status_isimmune(bl)) {
+ clif_skill_nodamage(src,bl,skillid,skilllv,0);
+ break;
+ }
+ if (sc_data && sc_data[SC_SILENCE].timer != -1) {
+ status_change_end(bl,SC_SILENCE, -1);
+ clif_skill_nodamage (src, bl, skillid, skilllv, 1);
+ } else if (rand() % 100 < sc_def_vit) {
+ status_change_start(bl,SkillStatusChangeTable[skillid],skilllv,0,0,0,skill_get_time(skillid,skilllv),0);
+ clif_skill_nodamage (src, bl, skillid, skilllv, 1);
+ } else
+ clif_skill_nodamage (src, bl, skillid, skilllv, 0);
+ }
+ break;
+
+ case SA_ABRACADABRA:
+ {
+ int abra_skillid = 0, abra_skilllv;
+ if (sd)
+ { //Crash-fix [Skotlex]
+ //require 1 yellow gemstone even with mistress card or Into the Abyss
+ if ((i = pc_search_inventory(sd, 715)) < 0 )
+ { //bug fixed by Lupus (item pos can be 0, too!)
+ clif_skill_fail(sd,skillid,0,0);
+ break;
+ }
+ pc_delitem(sd, i, 1, 0);
+ }
+ do {
+ abra_skillid = rand() % MAX_SKILL_ABRA_DB;
+ if (skill_abra_db[abra_skillid].req_lv > skilllv ||
+ rand()%10000 >= skill_abra_db[abra_skillid].per || //dbに基づくレベル?確率判定
+ (abra_skillid >= NPC_PIERCINGATT && abra_skillid <= NPC_SUMMONMONSTER) || //NPCスキルはダ?
+ skill_get_unit_flag(abra_skillid) & UF_DANCE) //演奏スキルはダ?
+ abra_skillid = 0; // reset to get a new id
+ } while (abra_skillid == 0);
+ abra_skilllv = skill_get_max(abra_skillid) > skilllv ? skilllv : skill_get_max(abra_skillid);
+ clif_skill_nodamage (src, bl, skillid, skilllv, 1);
+ if (sd)
+ { //Crash-protection against Abracadabra casting pets
+ sd->skillitem = abra_skillid;
+ sd->skillitemlv = abra_skilllv;
+ sd->state.abra_flag = 1;
+ clif_item_skill (sd, abra_skillid, abra_skilllv, "Abracadabra");
+ } else if(src->type == BL_PET)
+ { // [Skotlex]
+ struct pet_data *pd = (struct pet_data *)src;
+ int inf = skill_get_inf(abra_skillid);
+ if (inf&INF_SELF_SKILL || inf&INF_SUPPORT_SKILL) {
+ nullpo_retr(1,(struct map_session_data *)pd->msd);
+ petskill_use(pd, &pd->msd->bl, abra_skillid, abra_skilllv, tick);
+ } else //Assume offensive skills
+ petskill_use(pd, bl, abra_skillid, abra_skilllv, tick);
+ }
+ }
+ break;
+
+ case SA_COMA:
+ if (status_isimmune(bl)) {
+ clif_skill_nodamage(src,bl,skillid,skilllv,0);
+ break;
+ }
+ status_change_start(bl,SkillStatusChangeTable[skillid],skilllv,0,0,0,skill_get_time2(skillid,skilllv),0 );
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ break;
+ case SA_FULLRECOVERY:
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ if (status_isimmune(bl))
+ break;
+ if (dstsd) pc_heal (dstsd, dstsd->status.max_hp, dstsd->status.max_sp);
+ else if (dstmd) dstmd->hp = status_get_max_hp(bl);
+ break;
+ case SA_SUMMONMONSTER:
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ if (sd) mob_once_spawn(sd,map[src->m].name,src->x,src->y,"--ja--",-1,1,"");
+ break;
+ case SA_LEVELUP:
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ if (sd && pc_nextbaseexp(sd)) pc_gainexp(sd, pc_nextbaseexp(sd) * 10 / 100, 0);
+ break;
+ case SA_INSTANTDEATH:
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ battle_damage(NULL,src,status_get_hp(src)-1,0);
+ break;
+ case SA_QUESTION:
+ case SA_GRAVITY:
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ break;
+ case SA_CLASSCHANGE:
+ {
+ //クラスチェンジ用ボスモンスタ?ID
+ static int changeclass[]={1038,1039,1046,1059,1086,1087,1112,1115
+ ,1157,1159,1190,1272,1312,1373,1492};
+ int class_ = mob_random_class (changeclass,sizeof(changeclass)/sizeof(changeclass[0]));
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ if(dstmd) mob_class_change(dstmd,class_);
+ }
+ break;
+ case SA_MONOCELL:
+ {
+ static int poringclass[]={1002};
+ int class_ = mob_random_class (poringclass,sizeof(poringclass)/sizeof(poringclass[0]));
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ if(dstmd) mob_class_change(dstmd,class_);
+ }
+ break;
+ case SA_DEATH:
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ battle_damage(NULL,bl,status_get_max_hp(bl),1);
+ break;
+ case SA_REVERSEORCISH:
+ status_change_start(bl, SkillStatusChangeTable[skillid], skilllv,0,0,0,skill_get_time(skillid, skilllv),0);
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ break;
+ case SA_FORTUNE:
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ if(sd) pc_getzeny(sd,status_get_lv(bl)*100);
+ break;
+ case SA_TAMINGMONSTER:
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ if (sd && dstmd) {
+ for (i = 0; i < MAX_PET_DB; i++) {
+ if (dstmd->class_ == pet_db[i].class_) {
+ pet_catch_process1 (sd, dstmd->class_);
+ break;
+ }
+ }
+ }
+ break;
+
+ case AL_INCAGI: /* 速度?加 */
+ case AL_BLESSING: /* ブレッシング */
+ case PR_SLOWPOISON:
+ case PR_IMPOSITIO: /* イムポシティオマヌス */
+ case PR_LEXAETERNA: /* レックスエ?テルナ */
+ case PR_SUFFRAGIUM: /* サフラギウム */
+ case PR_BENEDICTIO: /* ?ケ??~福 */
+ if (status_isimmune(bl))
+ clif_skill_nodamage(src,bl,skillid,skilllv,0);
+ else {
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ status_change_start(bl,SkillStatusChangeTable[skillid],skilllv,0,0,0,skill_get_time(skillid,skilllv),0 );
+ }
+ break;
+
+ case CR_PROVIDENCE: /* プ?ヴィデンス */
+ if (status_isimmune(bl))
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ else {
+ if(sd && dstsd){ //Check they are not another crusader [Skotlex]
+ if ((dstsd->class_&MAPID_UPPERMASK) == MAPID_CRUSADER) {
+ clif_skill_fail(sd,skillid,0,0);
+ map_freeblock_unlock();
+ return 1;
+ }
+ }
+ status_change_start(bl,SkillStatusChangeTable[skillid],skilllv,0,0,0,skill_get_time(skillid,skilllv),0 );
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ }
+ break;
+
+ case CG_MARIONETTE: /* マリオネットコント??ル */
+ {
+ struct status_change *sc_data = status_get_sc_data(src);
+ struct status_change *tsc_data = status_get_sc_data(bl);
+ int sc = SkillStatusChangeTable[skillid];
+ int sc2 = SC_MARIONETTE2;
+
+ if(sc_data && tsc_data){
+ if (sc_data[sc].timer == -1 && tsc_data[sc2].timer == -1) {
+ status_change_start (src,sc,skilllv,0,bl->id,0,skill_get_time(skillid,skilllv),0);
+ status_change_start (bl,sc2,skilllv,0,src->id,0,skill_get_time(skillid,skilllv),0);
+ clif_marionette(src, bl);
+ }
+ else if (sc_data[sc].timer != -1 && tsc_data[sc2].timer != -1 &&
+ sc_data[sc].val3 == bl->id && tsc_data[sc2].val3 == src->id) {
+ status_change_end(src, sc, -1);
+ status_change_end(bl, sc2, -1);
+ clif_marionette(src, 0);
+ }
+ else {
+ if (sd) clif_skill_fail(sd,skillid,0,0);
+ map_freeblock_unlock();
+ return 1;
+ }
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ }
+ }
+ break;
+
+ case RG_CLOSECONFINE:
+ if (status_isimmune(bl))
+ clif_skill_nodamage(src,bl,skillid,skilllv,0);
+ else {
+ status_change_start (bl,SkillStatusChangeTable[skillid],skilllv,src->id,0,0,skill_get_time(skillid,skilllv),0);
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ }
+ break;
+ case SA_FLAMELAUNCHER: // added failure chance and chance to break weapon if turned on [Valaris]
+ case SA_FROSTWEAPON:
+ case SA_LIGHTNINGLOADER:
+ case SA_SEISMICWEAPON:
+ if (dstsd) {
+ if (status_isimmune(bl)) {
+ clif_skill_nodamage(src,bl,skillid,skilllv,0);
+ break;
+ }
+ if(dstsd->status.weapon == 0 ||
+ dstsd->sc_data[SC_FIREWEAPON].timer != -1 ||
+ dstsd->sc_data[SC_WATERWEAPON].timer != -1 ||
+ dstsd->sc_data[SC_WINDWEAPON].timer != -1 ||
+ dstsd->sc_data[SC_EARTHWEAPON].timer != -1 ||
+ dstsd->sc_data[SC_SHADOWWEAPON].timer != -1 ||
+ dstsd->sc_data[SC_GHOSTWEAPON].timer != -1 ||
+ dstsd->sc_data[SC_ENCPOISON].timer != -1) {
+ if (sd) clif_skill_fail(sd,skillid,0,0);
+ clif_skill_nodamage(src,bl,skillid,skilllv,0);
+ break;
+ }
+ }
+ if (sd) {
+ int i = pc_search_inventory (sd, skill_db[skillid].itemid[0]);
+ if(i < 0 || sd->status.inventory[i].amount < skill_db[skillid].amount[0]) {
+ clif_skill_fail(sd,skillid,0,0);
+ break;
+ }
+ pc_delitem(sd, i, skill_db[skillid].amount[0], 0);
+ }
+
+ if(skilllv < 4 && rand()%100 > (60+skilllv*10) ) { // 100% success rate at lv4 & 5, but lasts longer at lv5
+ if (sd) clif_skill_fail(sd,skillid,0,0);
+ clif_skill_nodamage(src,bl,skillid,skilllv,0);
+ if(dstsd && battle_config.equip_self_break_rate) {
+ if(sd && sd != dstsd) clif_displaymessage(sd->fd,"You broke target's weapon");
+ pc_breakweapon(dstsd);
+ }
+ break;
+ } else {
+ status_change_start(bl,SkillStatusChangeTable[skillid],skilllv,0,0,0,skill_get_time(skillid,skilllv),0 );
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ }
+ break;
+
+ case PR_ASPERSIO: /* アスペルシオ */
+ if (status_isimmune(bl) || dstmd) {
+ clif_skill_nodamage(src,bl,skillid,skilllv,0);
+ break;
+ }
+ status_change_start(bl,SkillStatusChangeTable[skillid],skilllv,0,0,0,skill_get_time(skillid,skilllv),0 );
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ break;
+
+ case TK_SEVENWIND:
+ {
+ int sc=-1;
+ switch(skilllv){
+ case 1:
+ sc=SC_EARTHWEAPON;
+ break;
+ case 2:
+ sc=SC_WINDWEAPON;
+ break;
+ case 3:
+ sc=SC_WATERWEAPON;
+ break;
+ case 4:
+ sc=SC_FIREWEAPON;
+ break;
+ case 5:
+ sc=SC_GHOSTWEAPON;
+ break;
+ case 6:
+ sc=SC_SHADOWWEAPON;
+ break;
+ case 7:
+ sc=SC_ASPERSIO;
+ break;
+ }
+ status_change_start(bl,sc,skilllv,0,0,0,skill_get_time(skillid,skilllv),0 );
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ }
+ break;
+
+ case PR_KYRIE: /* キリエエレイソン */
+ if (status_isimmune(bl)) {
+ clif_skill_nodamage(bl,bl,skillid,skilllv,0);
+ break;
+ }
+ status_change_start(bl,SkillStatusChangeTable[skillid],skilllv,0,0,0,skill_get_time(skillid,skilllv),0 );
+ clif_skill_nodamage(bl,bl,skillid,skilllv,1);
+ break;
+
+ case LK_BERSERK: /* バ?サ?ク */
+ if(battle_config.berserk_cancels_buffs)
+ {
+ status_change_end(bl,SC_ONEHAND,-1);
+ status_change_end(bl,SC_TWOHANDQUICKEN,-1);
+ status_change_end(bl,SC_CONCENTRATION,-1);
+ status_change_end(bl,SC_PARRYING,-1);
+ status_change_end(bl,SC_ENDURE,-1);
+ status_change_end(bl,SC_AURABLADE,-1);
+ }
+ case KN_AUTOCOUNTER: /* オ?トカウンタ? */
+ case KN_TWOHANDQUICKEN: /* ツ?ハンドクイッケン */
+ case KN_ONEHAND:
+ case CR_SPEARQUICKEN: /* スピアクイッケン */
+ case CR_REFLECTSHIELD:
+ case AS_POISONREACT: /* ポイズンリアクト */
+ case MC_LOUD: /* ラウドボイス */
+ case MG_ENERGYCOAT: /* エナジ?コ?ト */
+ case MG_SIGHT: /* サイト */
+ case AL_RUWACH: /* ルアフ */
+ case MO_EXPLOSIONSPIRITS: // 爆裂波動
+ case MO_STEELBODY: // 金?
+ case LK_AURABLADE: /* オ?ラブレ?ド */
+ case LK_PARRYING: /* パリイング */
+ case LK_CONCENTRATION: /* コンセントレ?ション */
+ case WS_CARTBOOST: /* カ?トブ?スト */
+ case SN_SIGHT: /* トゥル?サイト */
+ case WS_MELTDOWN: /* ?ルトダウン */
+ case WS_OVERTHRUSTMAX: // Overthrust Max [Celest]
+ case ST_REJECTSWORD: /* リジェクトソ?ド */
+ case HW_MAGICPOWER: /* 魔法力?? */
+ case PF_MEMORIZE: /* ?モライズ */
+ case PA_SACRIFICE:
+ case ASC_EDP: // [Celest]
+ case NPC_STOP:
+ case WZ_SIGHTBLASTER:
+ status_change_start(bl,SkillStatusChangeTable[skillid],skilllv,0,0,0,skill_get_time(skillid,skilllv),0 );
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ break;
+
+ case CG_MOONLIT: /* 月明りの泉に落ちる花びら */
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ if (sd && battle_config.player_skill_partner_check) {
+ skill_check_pc_partner(sd, skillid, &skilllv, 1, 1);
+ } else
+ skill_moonlit(bl, NULL, skilllv); //The knockback must be invoked before starting the effect which places down the map cells. [Skotlex]
+
+ break;
+
+ case HP_ASSUMPTIO:
+ if (flag&1)
+ status_change_start(bl,SkillStatusChangeTable[skillid],skilllv,0,0,0,skill_get_time(skillid,skilllv),0 );
+ else
+ {
+ map_foreachinarea(skill_area_sub,
+ bl->m, bl->x-1, bl->y-1, bl->x+1, bl->y+1, BL_PC,
+ src, skillid, skilllv, tick, flag|BCT_ALL|1,
+ skill_castend_nodamage_id);
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ }
+ break;
+
+ case SM_ENDURE: /* インデュア */
+ status_change_start(bl,SkillStatusChangeTable[skillid],skilllv,0,0,0,skill_get_time(skillid,skilllv),0 );
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ if (sd)
+ pc_blockskill_start (sd, skillid, 10000);
+ break;
+
+ case AS_ENCHANTPOISON: // Prevent spamming [Valaris]
+ if (dstsd) {
+ if(dstsd->sc_data[SC_FIREWEAPON].timer != -1 ||
+ dstsd->sc_data[SC_WATERWEAPON].timer != -1 ||
+ dstsd->sc_data[SC_WINDWEAPON].timer != -1 ||
+ dstsd->sc_data[SC_EARTHWEAPON].timer != -1 ||
+ dstsd->sc_data[SC_SHADOWWEAPON].timer != -1 ||
+ dstsd->sc_data[SC_GHOSTWEAPON].timer != -1 ||
+ dstsd->sc_data[SC_ENCPOISON].timer != -1) {
+ clif_skill_nodamage(src,bl,skillid,skilllv,0);
+ clif_skill_fail(sd,skillid,0,0);
+ break;
+ }
+ }
+ status_change_start(bl,SkillStatusChangeTable[skillid],skilllv,0,0,0,skill_get_time(skillid,skilllv),0 );
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ break;
+
+ case LK_TENSIONRELAX: /* テンションリラックス */
+ if (sd) {
+ pc_setsit(sd);
+ clif_sitting(sd);
+ }
+ status_change_start(bl,SkillStatusChangeTable[skillid],skilllv,0,0,0,skill_get_time(skillid,skilllv),0 );
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ break;
+
+ case MC_CHANGECART:
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ break;
+
+ case TK_MISSION:
+ if (sd) {
+ int id;
+ if (sd->mission_mobid && (sd->mission_count || rand()%100)) { //Cannot change target when already have one
+ clif_mission_mob(sd, sd->mission_mobid, sd->mission_count);
+ clif_skill_fail(sd,skillid,0,0);
+ break;
+ }
+ id = mob_get_random_id(0,0, sd->status.base_level);
+ if (!id) {
+ clif_skill_fail(sd,skillid,0,0);
+ break;
+ }
+ sd->mission_mobid = id;
+ sd->mission_count = 0;
+ pc_setglobalreg(sd,"TK_MISSION_ID", id);
+ clif_mission_mob(sd, id, 0);
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ }
+ break;
+
+ case AC_CONCENTRATION: /* ?W中力向? */
+ {
+ int range = 1;
+ status_change_start(bl,SkillStatusChangeTable[skillid],skilllv,0,0,0,skill_get_time(skillid,skilllv),0 );
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ map_foreachinarea( status_change_timer_sub,
+ src->m, src->x-range, src->y-range, src->x+range,src->y+range,BL_CHAR,
+ src,SkillStatusChangeTable[skillid],tick);
+ }
+ break;
+
+ case SM_PROVOKE: /* プ?ボック */
+ {
+ struct status_change *sc_data = status_get_sc_data(bl);
+
+ /* MVPmobと不死には?かない */
+ if((status_get_mode(bl)&MD_BOSS) || battle_check_undead(status_get_race(bl),status_get_elem_type(bl))) { //不死には?かない
+ map_freeblock_unlock();
+ return 1;
+ }
+
+ if (rand()%100 > 50 + 3*skilllv + status_get_lv(src) - status_get_lv(bl)) //TODO: How much does base level affects? Dummy value of 1% per level difference used. [Skotlex]
+ {
+ clif_skill_nodamage(src,bl,skillid,skilllv,0);
+ if (sd)
+ clif_skill_fail(sd,skillid,0,0);
+ map_freeblock_unlock();
+ return 0;
+ }
+ status_change_start(bl,SkillStatusChangeTable[skillid],skilllv,0,0,0,skill_get_time(skillid,skilllv),0 );
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+
+ if(dstmd && dstmd->skilltimer!=-1 && dstmd->state.skillcastcancel) // 詠?・妨害
+ skill_castcancel(bl,0);
+ if(dstsd && dstsd->skilltimer!=-1 && (!dstsd->special_state.no_castcancel || map_flag_gvg(bl->m))
+ && dstsd->state.skillcastcancel && !dstsd->special_state.no_castcancel2)
+ skill_castcancel(bl,0);
+
+ if(sc_data){
+ if(sc_data[SC_FREEZE].timer!=-1)
+ status_change_end(bl,SC_FREEZE,-1);
+ if(sc_data[SC_STONE].timer!=-1 && sc_data[SC_STONE].val2==0)
+ status_change_end(bl,SC_STONE,-1);
+ if(sc_data[SC_SLEEP].timer!=-1)
+ status_change_end(bl,SC_SLEEP,-1);
+ }
+
+ if(dstmd) {
+ dstmd->state.provoke_flag = src->id;
+ mob_target(dstmd,src,skill_get_range2(src,skillid,skilllv));
+ }
+ }
+ break;
+
+ case CR_DEVOTION: /* ディボ?ション */
+ if(sd && dstsd)
+ {
+ //??カや養子の??の元の?E業を算?oする
+
+ int lv = sd->status.base_level - dstsd->status.base_level;
+ if (lv < 0) lv = -lv;
+ if (lv > battle_config.devotion_level_difference ||
+ (dstsd->sc_data[SC_DEVOTION].timer != -1 && dstsd->sc_data[SC_DEVOTION].val1 != src->id) || //Avoid overriding [Skotlex]
+ (dstsd->class_&MAPID_UPPERMASK) == MAPID_CRUSADER) {
+ clif_skill_fail(sd,skillid,0,0);
+ map_freeblock_unlock();
+ return 1;
+ }
+ //Look for an empty slot (or reuse in case you cast it twice in the same char. [Skotlex]
+ for (i = 0; i < skilllv && i < 5 && sd->devotion[i]!=bl->id && sd->devotion[i]; i++);
+ if (i == skilllv)
+ {
+ clif_skill_fail(sd,skillid,0,0);
+ map_freeblock_unlock();
+ return 1;
+ }
+ sd->devotion[i] = bl->id;
+ status_change_start(bl,SkillStatusChangeTable[skillid],src->id,i,skill_get_range2(src,skillid,skilllv),skill_get_time2(skillid, skilllv),1000,0);
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ clif_devotion(sd);
+ }
+ else
+ if (sd)
+ clif_skill_fail(sd,skillid,0,0);
+ break;
+
+ case MO_CALLSPIRITS: // ?功
+ if(sd) {
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ pc_addspiritball(sd,skill_get_time(skillid,skilllv),skilllv);
+ }
+ break;
+
+ case CH_SOULCOLLECT: // 狂?功
+ if(sd) {
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ for (i = 0; i < 5; i++)
+ pc_addspiritball(sd,skill_get_time(skillid,skilllv),5);
+ }
+ break;
+
+ case MO_KITRANSLATION:
+ if(dstsd) {
+ pc_addspiritball(dstsd,skill_get_time(skillid,skilllv),5);
+ }
+ break;
+
+ case TK_TURNKICK:
+ case MO_BALKYOUNG: //Passive part of the attack. Splash knock-back+stun. [Skotlex]
+ if (skill_area_temp[1] != bl->id) {
+ skill_blown(src,bl,skill_get_blewcount(skillid,skilllv));
+ skill_additional_effect(src,bl,skillid,skilllv,BF_MISC,tick); //Use Misc rather than weapon to signal passive pushback
+ }
+ break;
+
+ case MO_BLADESTOP: // 白?n取り
+ status_change_start(src,SkillStatusChangeTable[skillid],skilllv,0,0,0,skill_get_time(skillid,skilllv),0 );
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ break;
+
+ case MO_ABSORBSPIRITS: // ?奪
+ i = 0;
+ if (dstsd && dstsd->spiritball > 0 &&
+ ((sd && sd == dstsd) || map_flag_vs(src->m)))
+ {
+ i = dstsd->spiritball * 7;
+ pc_delspiritball(dstsd,dstsd->spiritball,0);
+ } else if (dstmd && //??ロがモンスタ?の??
+ //20%の確率で??ロのLv*2のSPを回復する?B?ャ功したときはタ?ゲット(σ?Д?)σ????!!
+ !(status_get_mode(bl)&MD_BOSS) && rand() % 100 < 20)
+ {
+ i = 2 * dstmd->db->lv;
+ mob_target(dstmd,src,0);
+ }
+ if (sd){
+ if (i > 0x7FFF)
+ i = 0x7FFF;
+ if (sd->status.sp + i > sd->status.max_sp)
+ i = sd->status.max_sp - sd->status.sp;
+ if (i) {
+ sd->status.sp += i;
+ clif_heal(sd->fd,SP_SP,i);
+ }
+ }
+ clif_skill_nodamage(src,bl,skillid,skilllv,0);
+ break;
+
+ case AC_MAKINGARROW: /* 矢??ャ */
+ if(sd) {
+ clif_arrow_create_list(sd);
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ }
+ break;
+
+ case AM_PHARMACY: /* ポ?ション??ャ */
+ if(sd) {
+ clif_skill_produce_mix_list(sd,22);
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ }
+ break;
+
+ case SA_CREATECON:
+ if(sd) {
+ clif_skill_produce_mix_list(sd,23);
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ }
+ break;
+
+ case BS_HAMMERFALL: /* ハンマ?フォ?ル */
+ if(dstsd && dstsd->special_state.no_weapon_damage) {
+ clif_skill_nodamage(src,bl,skillid,skilllv,0);
+ break;
+ }
+ if(rand() % 100 < (20 + 10 * skilllv) * sc_def_vit / 100 )
+ status_change_start(bl,SC_STAN,skilllv,0,0,0,skill_get_time2(skillid,skilllv),0);
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ break;
+ case RG_RAID: /* サプライズアタック */
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ map_foreachinarea(skill_area_sub,
+ bl->m,bl->x-1,bl->y-1,bl->x+1,bl->y+1,BL_CHAR,
+ src,skillid,skilllv,tick, flag|BCT_ENEMY|1,
+ skill_castend_damage_id);
+ status_change_end(src, SC_HIDING, -1); // ハイディング解?
+ break;
+
+ case ASC_METEORASSAULT: /* ?テオアサルト */
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ map_foreachinarea(skill_area_sub,
+ bl->m,bl->x-2,bl->y-2,bl->x+2,bl->y+2,BL_CHAR,
+ src,skillid,skilllv,tick, flag|BCT_ENEMY|1,
+ skill_castend_damage_id);
+ break;
+
+ case KN_BRANDISHSPEAR: /*ブランディッシュスピア*/
+ {
+ int c,n=4,ar;
+ int dir = map_calc_dir(src,bl->x,bl->y);
+ struct square tc;
+ int x=bl->x,y=bl->y;
+ ar=skilllv/3;
+ skill_brandishspear_first(&tc,dir,x,y);
+ skill_brandishspear_dir(&tc,dir,4);
+ /* 範?C */
+ if(skilllv == 10){
+ for(c=1;c<4;c++){
+ map_foreachinarea(skill_area_sub,
+ bl->m,tc.val1[c],tc.val2[c],tc.val1[c],tc.val2[c],BL_CHAR,
+ src,skillid,skilllv,tick, flag|BCT_ENEMY|n,
+ skill_castend_damage_id);
+ }
+ }
+ /* 範?BA */
+ if(skilllv > 6){
+ skill_brandishspear_dir(&tc,dir,-1);
+ n--;
+ }else{
+ skill_brandishspear_dir(&tc,dir,-2);
+ n-=2;
+ }
+
+ if(skilllv > 3){
+ for(c=0;c<5;c++){
+ map_foreachinarea(skill_area_sub,
+ bl->m,tc.val1[c],tc.val2[c],tc.val1[c],tc.val2[c],BL_CHAR,
+ src,skillid,skilllv,tick, flag|BCT_ENEMY|n,
+ skill_castend_damage_id);
+ if(skilllv > 6 && n==3 && c==4){
+ skill_brandishspear_dir(&tc,dir,-1);
+ n--;c=-1;
+ }
+ }
+ }
+ /* 範?@ */
+ for(c=0;c<10;c++){
+ if(c==0||c==5) skill_brandishspear_dir(&tc,dir,-1);
+ map_foreachinarea(skill_area_sub,
+ bl->m,tc.val1[c%5],tc.val2[c%5],tc.val1[c%5],tc.val2[c%5],BL_CHAR,
+ src,skillid,skilllv,tick, flag|BCT_ENEMY|1,
+ skill_castend_damage_id);
+ }
+ }
+ break;
+
+ /* パ?ティスキル */
+ case AL_ANGELUS: /* エンジェラス */
+ case PR_MAGNIFICAT: /* マグニフィカ?ト */
+ case PR_GLORIA: /* グ?リア */
+ case SN_WINDWALK: /* ウインドウォ?ク */
+ if (sd == NULL || sd->status.party_id == 0 || (flag & 1)) {
+ /* 個別の?? */
+ if(status_isimmune(bl)) {
+ clif_skill_nodamage(bl,bl,skillid,skilllv,0);
+ break;
+ }
+ status_change_start(bl,SkillStatusChangeTable[skillid],skilllv,0,0,0,skill_get_time(skillid,skilllv),0);
+ clif_skill_nodamage(bl,bl,skillid,skilllv,1);
+ } else if (sd) {
+ /* パ?ティ全?への?? */
+ party_foreachsamemap (skill_area_sub,
+ sd,1,
+ src,skillid,skilllv,tick, flag|BCT_PARTY|1,
+ skill_castend_nodamage_id);
+ }
+ break;
+
+ case BS_ADRENALINE: /* アドレナリンラッシュ */
+ case BS_ADRENALINE2:
+ case BS_WEAPONPERFECT: /* ウェポンパ?フェクション */
+ case BS_OVERTHRUST: /* オ?バ?トラスト */
+ if (sd == NULL || sd->status.party_id == 0 || (flag & 1)) {
+ /* 個別の?? */
+ status_change_start(bl,SkillStatusChangeTable[skillid],skilllv,(src == bl)? 1:0,0,0,skill_get_time(skillid,skilllv),0);
+ clif_skill_nodamage(bl,bl,skillid,skilllv,1);
+ } else if (sd) {
+ /* パ?ティ全?への?? */
+ party_foreachsamemap(skill_area_sub,
+ sd,1,
+ src,skillid,skilllv,tick, flag|BCT_PARTY|1,
+ skill_castend_nodamage_id);
+ }
+ break;
+
+ case BS_MAXIMIZE:
+ case NV_TRICKDEAD:
+ case CR_DEFENDER:
+ case CR_AUTOGUARD:
+ case TK_READYSTORM:
+ case TK_READYDOWN:
+ case TK_READYTURN:
+ case TK_READYCOUNTER:
+ case TK_DODGE:
+ case CR_SHRINK:
+ {
+ struct status_change *tsc_data = status_get_sc_data(bl);
+ int sc = SkillStatusChangeTable[skillid];
+ if (tsc_data && tsc_data[sc].timer != -1)
+ status_change_end(bl, sc, -1);
+ else
+ status_change_start(bl,sc,skilllv,0,0,0,skill_get_time(skillid,skilllv),0);
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ }
+ break;
+ case SL_KAITE:
+ case SL_KAAHI:
+ case SL_KAIZEL:
+ case SL_KAUPE:
+ if (sd) {
+ if (!dstsd || !(
+ (sd->sc_data[SC_SPIRIT].timer != -1 && sd->sc_data[SC_SPIRIT].val2 == SL_SOULLINKER) ||
+ dstsd->char_id == sd->char_id ||
+ dstsd->char_id == sd->status.partner_id ||
+ dstsd->char_id == sd->status.child
+ )) {
+ status_change_start(src,SC_STAN,skilllv,0,0,0,3000,0);
+ clif_skill_fail(sd,skillid,0,0);
+ break;
+ }
+ }
+ status_change_start(bl,SkillStatusChangeTable[skillid],skilllv,0,0,0,skill_get_time(skillid, skilllv),0);
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ break;
+ case SM_AUTOBERSERK: // Celest
+ {
+ struct status_change *tsc_data = status_get_sc_data(bl);
+ int sc = SkillStatusChangeTable[skillid];
+ if (tsc_data && tsc_data[sc].timer != -1)
+ status_change_end(bl, sc, -1);
+ else
+ status_change_start(bl,sc,skilllv,0,0,0,0,0);
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ }
+ break;
+ case TF_HIDING: /* ハイディング */
+ case ST_CHASEWALK: /* ハイディング */
+ {
+ struct status_change *tsc_data = status_get_sc_data(bl);
+ int sc = SkillStatusChangeTable[skillid];
+ if (tsc_data && tsc_data[sc].timer != -1)
+ status_change_end(bl, sc, -1);
+ else
+ status_change_start(bl,sc,skilllv,0,0,0,skill_get_time(skillid,skilllv),0);
+ clif_skill_nodamage(src,bl,skillid,-1,1); // Don't display the skill name as it is a hiding skill
+ }
+ break;
+ case TK_RUN:
+ {
+ struct status_change *sc_data = status_get_sc_data(bl);
+ int type = SkillStatusChangeTable[skillid];
+ if(!sc_data)
+ break;
+ if (sc_data[type].timer!=-1)
+ status_change_end(bl,type,-1);
+ else{
+ status_change_start(bl,type,skilllv,status_get_dir(bl),0,0,0,0);
+ }
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ }
+ break;
+ case AS_CLOAKING: /* ク??キング */
+ {
+ struct status_change *tsc_data = status_get_sc_data(bl);
+ int sc=SkillStatusChangeTable[skillid];
+ if(tsc_data && tsc_data[sc].timer!=-1 )
+ /* 解?怩キる */
+ status_change_end(bl, sc, -1);
+ else
+ /* 付加する */
+ { //Avoid cloaking with no wall and low skill level. [Skotlex]
+ if (sd && skilllv < 3 && skill_check_cloaking(bl))
+ clif_skill_fail(sd,skillid,0,0);
+ else
+ status_change_start(bl,sc,skilllv,0,0,0,skill_get_time(skillid,skilllv),0);
+ }
+ clif_skill_nodamage(src,bl,skillid,-1,1);
+ }
+ break;
+
+ /* ?地スキル */
+ case BD_LULLABY: /* 子守唄 */
+ case BD_RICHMANKIM: /* ニヨルドの宴 */
+ case BD_ETERNALCHAOS: /* 永遠の?ャ沌 */
+ case BD_DRUMBATTLEFIELD: /* ?太鼓の響き */
+ case BD_RINGNIBELUNGEN: /* ニ?ベルングの指輪 */
+ case BD_ROKISWEIL: /* ?キの叫び */
+ case BD_INTOABYSS: /* ?[淵の中に */
+ case BD_SIEGFRIED: /* 不死?gのジ?クフリ?ド */
+ case BA_DISSONANCE: /* 不協和音 */
+ case BA_POEMBRAGI: /* ブラギの? */
+ case BA_WHISTLE: /* 口笛 */
+ case BA_ASSASSINCROSS: /* 夕陽のアサシンク?ス */
+ case BA_APPLEIDUN: /* イドゥンの林檎 */
+ case DC_UGLYDANCE: /* 自分?沁閧ネダンス */
+ case DC_HUMMING: /* ハミング */
+ case DC_DONTFORGETME: /* 私を忘れないで?c */
+ case DC_FORTUNEKISS: /* ?K運のキス */
+ case DC_SERVICEFORYOU: /* サ?ビスフォ?ユ? */
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ skill_unitsetting(src,skillid,skilllv,src->x,src->y,0);
+ break;
+
+ case HP_BASILICA: /* バジリカ */
+ case CG_HERMODE: // Wand of Hermod
+ {
+ struct skill_unit_group *sg;
+ battle_stopwalking(src,1);
+ skill_clear_unitgroup(src);
+ sg = skill_unitsetting(src,skillid,skilllv,src->x,src->y,0);
+ if(skillid == CG_HERMODE)
+ status_change_start(src,SC_DANCING,skillid,0,0,sg->group_id,skill_get_time(skillid,skilllv),0);
+ else
+ status_change_start(src,SkillStatusChangeTable[skillid],skilllv,0,BCT_SELF,sg->group_id,
+ skill_get_time(skillid,skilllv),0);
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ }
+ break;
+
+ case PA_GOSPEL: /* ゴスペル */
+ {
+ struct status_change *sc_data = status_get_sc_data(src);
+ if (!sc_data) break;
+ if (sc_data[SC_GOSPEL].timer != -1 && sc_data[SC_GOSPEL].val4 == BCT_SELF) {
+ status_change_end(src,SC_GOSPEL,-1);
+ } else {
+ struct skill_unit_group *sg = skill_unitsetting(src,skillid,skilllv,src->x,src->y,0);
+ if (sc_data[SC_GOSPEL].timer != -1)
+ status_change_end(src,SC_GOSPEL,-1); //Was under someone else's Gospel. [Skotlex]
+ status_change_start(src,SkillStatusChangeTable[skillid],skilllv,0,(int)sg,BCT_SELF,skill_get_time(skillid,skilllv),0);
+ }
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ }
+ break;
+
+ case BD_ADAPTATION: /* アドリブ */
+ {
+ struct status_change *sc_data = status_get_sc_data(src);
+ if(sc_data && sc_data[SC_DANCING].timer!=-1){
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ skill_stop_dancing(src);
+ }
+ }
+ break;
+
+ case BA_FROSTJOKE: /* 寒いジョ?ク */
+ case DC_SCREAM: /* スクリ?ム */
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ skill_addtimerskill(src,tick+2000,bl->id,src->x,src->y,skillid,skilllv,0,flag);
+ if (md) { // Mobは?れないから?Aスキル名を叫ばせてみる
+ char temp[128];
+ if (strlen(md->name) + strlen(skill_db[skillid].desc) > 120)
+ break; //Message won't fit on buffer. [Skotlex]
+ sprintf(temp,"%s : %s !!",md->name,skill_db[skillid].desc);
+ clif_GlobalMessage(&md->bl,temp);
+ }
+ break;
+
+
+ case BA_PANGVOICE://パンボイス
+ if(status_get_mode(bl)&MD_BOSS){
+ clif_skill_nodamage(src,bl,skillid,skilllv,0);
+ if(sd)
+ clif_skill_fail(sd,skillid,0,0);
+ break;
+ }
+ if(rand()%100 < 50){
+ status_change_start(bl,SC_CONFUSION,7,0,0,0,10000+7000,0);
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ }else{
+ clif_skill_nodamage(src,bl,skillid,skilllv,0);
+ if(sd)
+ clif_skill_fail(sd,skillid,0,0);
+ }
+ break;
+
+ case DC_WINKCHARM://魅惑のウィンク
+ if(dstsd){
+ if(rand()%100 < 30) {
+ status_change_start(bl,SC_CONFUSION,7,0,0,0,10000+7000,0);
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ }else{
+ clif_skill_nodamage(src,bl,skillid,skilllv,0);
+ if(sd)
+ clif_skill_fail(sd,skillid,0,0);
+ }
+ }else if(dstmd)
+ {
+ int race = status_get_race(bl);
+ if(!(status_get_mode(bl)&MD_BOSS) && status_get_lv(src)>status_get_lv(bl) && (race == 6 || race == 7 || race == 8) && rand()%100 < 70) {
+ status_change_start(bl,SkillStatusChangeTable[skillid],skilllv,0,0,0,10000,0);
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ } else{
+ clif_skill_nodamage(src,bl,skillid,skilllv,0);
+ if(sd)
+ clif_skill_fail(sd,skillid,0,0);
+ }
+ }
+ break;
+
+ case TF_STEAL: // スティ?ル
+ if(sd) {
+ if(pc_steal_item(sd,bl))
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ else
+ clif_skill_fail(sd,skillid,0x0a,0);
+ }
+ break;
+
+ case RG_STEALCOIN: // スティ?ルコイン
+ if(sd) {
+ if(pc_steal_coin(sd,bl)) {
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ mob_target((struct mob_data *)bl,src,skill_get_range2(src,skillid,skilllv));
+ }
+ else
+ clif_skill_fail(sd,skillid,0,0);
+ }
+ break;
+
+ case MG_STONECURSE: /* スト?ンカ?ス */
+ {
+ struct status_change *sc_data = status_get_sc_data(bl);
+ // Level 6-10 doesn't consume a red gem if it fails [celest]
+ int i, gem_flag = 1, fail_flag = 0;
+ if (status_get_mode(bl)&MD_BOSS) {
+ if (sd) clif_skill_fail(sd,skillid,0,0);
+ break;
+ }
+ if(status_isimmune(bl) || !sc_data)
+ break;
+ if (sc_data[SC_STONE].timer != -1) {
+ status_change_end(bl,SC_STONE,-1);
+ if (sd) {
+ fail_flag = 1;
+ clif_skill_fail(sd,skillid,0,0);
+ }
+ }
+ else if( rand()%100 < (skilllv*4+20)*status_get_sc_def(bl, SC_STONE)/100
+ && !battle_check_undead(status_get_race(bl),status_get_elem_type(bl)))
+ {
+ status_change_start(bl,SC_STONE,skilllv,0,0,0,skill_get_time2(skillid,skilllv),0);
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ } else if(sd) {
+ if (skilllv > 5) gem_flag = 0;
+ clif_skill_fail(sd,skillid,0,0);
+ fail_flag = 1;
+ }
+ if (dstmd)
+ mob_target(dstmd,src,skill_get_range2(src,skillid,skilllv));
+ if (sd && gem_flag) {
+ if ((i=pc_search_inventory(sd, skill_db[skillid].itemid[0])) < 0 ) {
+ // if (!fail_flag) clif_skill_fail(sd,skillid,0,0); This is actually a bug! Altough the gem is checked in skill_check_condition...
+ break;
+ }
+ if (sd->sc_data[SC_SPIRIT].timer != -1 && sd->sc_data[SC_SPIRIT].val2 == SL_WIZARD)
+ break; //Do not delete the gemstone.
+ pc_delitem(sd, i, skill_db[skillid].amount[0], 0);
+ }
+ }
+ break;
+
+ case NV_FIRSTAID: /* ?急手? */
+ clif_skill_nodamage(src,bl,skillid,5,1);
+ battle_heal(NULL,bl,5,0,0);
+ break;
+
+ case AL_CURE: /* キュア? */
+ if(status_isimmune(bl)) {
+ clif_skill_nodamage(src,bl,skillid,skilllv,0);
+ break;
+ }
+ status_change_end(bl, SC_SILENCE , -1 );
+ status_change_end(bl, SC_BLIND , -1 );
+ status_change_end(bl, SC_CONFUSION, -1 );
+ if( battle_check_undead(status_get_race(bl),status_get_elem_type(bl)) ){//アンデッドなら暗闇?果
+ status_change_start(bl, SC_CONFUSION,1,0,0,0,6000,0);
+ }
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ break;
+
+ case TF_DETOXIFY: /* 解毒 */
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ status_change_end(bl, SC_POISON , -1 );
+ status_change_end(bl, SC_DPOISON , -1 );
+ break;
+
+ case PR_STRECOVERY: /* リカバリ? */
+ {
+ if(status_isimmune(bl)) {
+ clif_skill_nodamage(src,bl,skillid,skilllv,0);
+ break;
+ }
+ status_change_end(bl, SC_FREEZE , -1 );
+ status_change_end(bl, SC_STONE , -1 );
+ status_change_end(bl, SC_SLEEP , -1 );
+ status_change_end(bl, SC_STAN , -1 );
+ if( battle_check_undead(status_get_race(bl),status_get_elem_type(bl)) ){//アンデッドなら暗闇?果
+ if(rand()%100 < (100-(status_get_int(bl)/2+status_get_vit(bl)/3+status_get_luk(bl)/10))) {
+ status_change_start(bl, SC_BLIND,1,0,0,0,
+ 1000 * 30 * (100-(status_get_int(bl)+status_get_vit(bl))/2)/100,0);
+ }
+ }
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ if(dstmd){
+ dstmd->attacked_id=0;
+ dstmd->target_id=0;
+ dstmd->state.targettype = NONE_ATTACKABLE;
+ dstmd->state.skillstate=MSS_IDLE;
+ dstmd->next_walktime=tick+rand()%3000+3000;
+ }
+ }
+ break;
+
+ case WZ_ESTIMATION: /* モンスタ??報 */
+ if(sd) {
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ clif_skill_estimation((struct map_session_data *)src,bl);
+ }
+ break;
+
+ case BS_REPAIRWEAPON: /* ?器?C? */
+ if(sd && dstsd)
+ clif_item_repair_list(sd,dstsd);
+ break;
+
+ case MC_IDENTIFY: /* アイテム鑑定 */
+ if(sd)
+ clif_item_identify_list(sd);
+ break;
+
+ // Weapon Refining [Celest]
+ case WS_WEAPONREFINE:
+ if(sd)
+ clif_item_refine_list(sd);
+ break;
+
+ case MC_VENDING: /* 露店開?ン */
+ if(sd)
+ { //Prevent vending of GMs with unnecessary Level to trade/drop. [Skotlex]
+ if ( pc_can_give_items(pc_isGM(sd)) )
+ clif_skill_fail(sd,skillid,0,0);
+ else
+ clif_openvendingreq(sd,2+sd->skilllv);
+ }
+ break;
+
+ case AL_TELEPORT: /* テレポ?ト */
+ if(sd) {
+ if (map[sd->bl.m].flag.noteleport) { /* テレポ禁止 */
+ clif_skill_teleportmessage(sd,0);
+ break;
+ }
+ if(!battle_config.duel_allow_teleport && sd->duel_group) { // duel restriction [LuzZza]
+ clif_displaymessage(sd->fd, "Duel: Can't use teleport in duel.");
+ break;
+ }
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ if(sd->skilllv == 1)
+ if(!battle_config.skip_teleport_lv1_menu) // possibility to skip menu [LuzZza]
+ clif_skill_warppoint(sd,skillid,"Random","","","");
+ else
+ pc_randomwarp(sd,3);
+ else {
+ clif_skill_warppoint(sd,skillid,"Random",
+ mapindex_id2name(sd->status.save_point.map),"","");
+ }
+ } else if(dstmd)
+ mob_warp(dstmd,-1,-1,-1,3);
+ break;
+
+ case AL_HOLYWATER: /* アクアベネディクタ */
+ if(sd) {
+ if (skill_produce_mix(sd, skillid, 523, 0, 0, 0, 1))
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ else
+ clif_skill_fail(sd,skillid,0,0);
+ }
+ break;
+
+ case TF_PICKSTONE:
+ if(sd) {
+ int eflag;
+ struct item item_tmp;
+ struct block_list tbl;
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ memset(&item_tmp,0,sizeof(item_tmp));
+ memset(&tbl,0,sizeof(tbl)); // [MouseJstr]
+ item_tmp.nameid = 7049;
+ item_tmp.identify = 1;
+ tbl.id = 0;
+ clif_takeitem(&sd->bl,&tbl);
+ eflag = pc_additem(sd,&item_tmp,1);
+ if(eflag) {
+ clif_additem(sd,0,0,eflag);
+ map_addflooritem(&item_tmp,1,sd->bl.m,sd->bl.x,sd->bl.y,NULL,NULL,NULL,0);
+ }
+ }
+ break;
+ case ASC_CDP:
+ if(sd) {
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ skill_produce_mix(sd, skillid, 678, 0, 0, 0, 1); //Produce a Deadly Poison Bottle.
+ }
+ break;
+
+ case RG_STRIPWEAPON: /* ストリップウェポン */
+ case RG_STRIPSHIELD: /* ストリップシ?[ルド */
+ case RG_STRIPARMOR: /* ストリップア?[マ?[ */
+ case RG_STRIPHELM: /* ストリップヘルム */
+ case ST_FULLSTRIP: // Rewritten most of the code [DracoRPG]
+ {
+ struct status_change *tsc_data = status_get_sc_data(bl);
+ int strip_fix, equip = 0;
+ int sclist[4] = {0,0,0,0};
+
+ if (skillid == RG_STRIPWEAPON || skillid == ST_FULLSTRIP)
+ equip |= EQP_WEAPON;
+ if (skillid == RG_STRIPSHIELD || skillid == ST_FULLSTRIP)
+ equip |= EQP_SHIELD;
+ if (skillid == RG_STRIPARMOR || skillid == ST_FULLSTRIP)
+ equip |= EQP_ARMOR;
+ if (skillid == RG_STRIPHELM || skillid == ST_FULLSTRIP)
+ equip |= EQP_HELM;
+
+ strip_fix = status_get_dex(src) - status_get_dex(bl);
+ if(strip_fix < 0)
+ strip_fix=0;
+ if (rand()%100 >= 5+2*skilllv+strip_fix/5)
+ {
+ if (sd)
+ clif_skill_fail(sd,skillid,0,0);
+ break;
+ }
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+
+ if (dstsd) {
+ for (i=0;i<11;i++) {
+ if (dstsd->equip_index[i]>=0 && dstsd->inventory_data[dstsd->equip_index[i]]) {
+ if (equip &EQP_WEAPON && (i == 9 || (i == 8 && dstsd->inventory_data[dstsd->equip_index[8]]->type == 4)) && !(dstsd->unstripable_equip &EQP_WEAPON) && !(tsc_data && tsc_data[SC_CP_WEAPON].timer != -1)) {
+ sclist[0] = SC_STRIPWEAPON; // Okay, we found a weapon to strip - It can be a right-hand, left-hand or two-handed weapon
+ pc_unequipitem(dstsd,dstsd->equip_index[i],3);
+ } else if (equip &EQP_SHIELD && i == 8 && dstsd->inventory_data[dstsd->equip_index[8]]->type == 5 && !(dstsd->unstripable_equip &EQP_SHIELD) && !(tsc_data && tsc_data[SC_CP_SHIELD].timer != -1)) {
+ sclist[1] = SC_STRIPSHIELD; // Okay, we found a shield to strip - It is really a shield, not a two-handed weapon or a left-hand weapon
+ pc_unequipitem(dstsd,dstsd->equip_index[i],3);
+ } else if (equip &EQP_ARMOR && i == 7 && !(dstsd->unstripable_equip &EQP_ARMOR) && !(tsc_data && tsc_data[SC_CP_ARMOR].timer != -1)) {
+ sclist[2] = SC_STRIPARMOR; // Okay, we found an armor to strip
+ pc_unequipitem(dstsd,dstsd->equip_index[i],3);
+ } else if (equip &EQP_HELM && i == 6 && !(dstsd->unstripable_equip &EQP_HELM) && !(tsc_data && tsc_data[SC_CP_HELM].timer != -1)) {
+ sclist[3] = SC_STRIPHELM; // Okay, we found a helm to strip
+ pc_unequipitem(dstsd,dstsd->equip_index[i],3);
+ }
+ }
+ }
+ } else if (dstmd && !(status_get_mode(bl)&MD_BOSS)) {
+ if (equip &EQP_WEAPON)
+ sclist[0] = SC_STRIPWEAPON;
+ if (equip &EQP_SHIELD)
+ sclist[1] = SC_STRIPSHIELD;
+ if (equip &EQP_ARMOR)
+ sclist[2] = SC_STRIPARMOR;
+ if (equip &EQP_HELM)
+ sclist[3] = SC_STRIPHELM;
+ }
+
+ for (i=0;i<4;i++) {
+ if (sclist[i] != 0) // Start the SC only if an equipment was stripped from this location
+ status_change_start(bl,sclist[i],skilllv,0,0,0,skill_get_time(skillid,skilllv)+strip_fix/2,0);
+ }
+
+ break;
+ }
+
+ /* PotionPitcher */
+ case AM_BERSERKPITCHER:
+ case AM_POTIONPITCHER: /* ポ?ションピッチャ? */
+ {
+ struct block_list tbl;
+ int i,x,hp = 0,sp = 0,bonus=100;
+ if(sd) {
+ x = skilllv%11 - 1;
+ i = pc_search_inventory(sd,skill_db[skillid].itemid[x]);
+ if(i < 0 || skill_db[skillid].itemid[x] <= 0) {
+ clif_skill_fail(sd,skillid,0,0);
+ map_freeblock_unlock();
+ return 1;
+ }
+ if(sd->inventory_data[i] == NULL || sd->status.inventory[i].amount < skill_db[skillid].amount[x]) {
+ clif_skill_fail(sd,skillid,0,0);
+ map_freeblock_unlock();
+ return 1;
+ }
+ if(skillid == AM_BERSERKPITCHER) { //Does not override use-level, and cannot be used on bows.
+ if (dstsd && (dstsd->status.base_level<(unsigned int)sd->inventory_data[i]->elv || dstsd->weapontype1 == 11)) {
+ clif_skill_fail(sd,skillid,0,0);
+ map_freeblock_unlock();
+ return 1;
+ }
+ }
+ potion_flag = 1;
+ potion_hp = potion_sp = potion_per_hp = potion_per_sp = 0;
+ potion_target = bl->id;
+ run_script(sd->inventory_data[i]->script,0,sd->bl.id,0);
+ pc_delitem(sd,i,skill_db[skillid].amount[x],0);
+ potion_flag = potion_target = 0;
+ if (sd->sc_data[SC_SPIRIT].timer != -1 && sd->sc_data[SC_SPIRIT].val2 == SL_ALCHEMIST)
+ bonus += sd->status.base_level;
+ if(potion_per_hp > 0 || potion_per_sp > 0) {
+ hp = status_get_max_hp(bl) * potion_per_hp / 100;
+ hp = hp * (100 + pc_checkskill(sd,AM_POTIONPITCHER)*10 + pc_checkskill(sd,AM_LEARNINGPOTION)*5)*bonus/10000;
+ if(dstsd) {
+ sp = dstsd->status.max_sp * potion_per_sp / 100;
+ sp = sp * (100 + pc_checkskill(sd,AM_POTIONPITCHER)*10 + pc_checkskill(sd,AM_LEARNINGPOTION)*5)*bonus/10000;
+ }
+ }
+ else {
+ if(potion_hp > 0) {
+ hp = potion_hp * (100 + pc_checkskill(sd,AM_POTIONPITCHER)*10 + pc_checkskill(sd,AM_LEARNINGPOTION)*5)*bonus/10000;
+ hp = hp * (100 + (status_get_vit(bl)<<1)) / 100;
+ if(dstsd)
+ hp = hp * (100 + pc_checkskill(dstsd,SM_RECOVERY)*10) / 100;
+ }
+ if(potion_sp > 0) {
+ sp = potion_sp * (100 + pc_checkskill(sd,AM_POTIONPITCHER)*10 + pc_checkskill(sd,AM_LEARNINGPOTION)*5)*bonus/10000;
+ sp = sp * (100 + (status_get_int(bl)<<1)) / 100;
+ if(dstsd)
+ sp = sp * (100 + pc_checkskill(dstsd,MG_SRECOVERY)*10) / 100;
+ }
+ }
+ }
+ else {
+ hp = (1 + rand()%400) * (100 + skilllv*10) / 100;
+ hp = hp * (100 + (status_get_vit(bl)<<1)) / 100;
+ if(dstsd)
+ hp = hp * (100 + pc_checkskill(dstsd,SM_RECOVERY)*10) / 100;
+ }
+ tbl.id = 0;
+ tbl.m = src->m;
+ tbl.x = src->x;
+ tbl.y = src->y;
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ if(hp > 0 || (skillid == AM_POTIONPITCHER && hp <= 0 && sp <= 0))
+ clif_skill_nodamage(&tbl,bl,AL_HEAL,hp,1);
+ if(sp > 0)
+ clif_skill_nodamage(&tbl,bl,MG_SRECOVERY,sp,1);
+ battle_heal(src,bl,hp,sp,0);
+ }
+ break;
+ case AM_CP_WEAPON:
+ case AM_CP_SHIELD:
+ case AM_CP_ARMOR:
+ case AM_CP_HELM:
+ {
+ int scid = SC_STRIPWEAPON + (skillid - AM_CP_WEAPON);
+ struct status_change *tsc_data = status_get_sc_data(bl);
+ if(tsc_data && tsc_data[scid].timer != -1)
+ status_change_end(bl, scid, -1 );
+ status_change_start(bl,SkillStatusChangeTable[skillid],skilllv,0,0,0,skill_get_time(skillid,skilllv),0 );
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ }
+ break;
+ case AM_TWILIGHT1:
+ if (sd) {
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ //Prepare 200 White Potions.
+ if (!skill_produce_mix(sd, skillid, 504, 0, 0, 0, 200))
+ clif_skill_fail(sd,skillid,0,0);
+ }
+ break;
+ case AM_TWILIGHT2:
+ if (sd) {
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ //Prepare 200 Slim White Potions.
+ if (!skill_produce_mix(sd, skillid, 547, 0, 0, 0, 200))
+ clif_skill_fail(sd,skillid,0,0);
+ }
+ break;
+ case AM_TWILIGHT3:
+ if (sd) {
+ //check if you can produce all three, if not, then fail:
+ if (!skill_can_produce_mix(sd,970,-1, 100) //100 Alcohol
+ || !skill_can_produce_mix(sd,7136,-1, 50) //50 Acid Bottle
+ || !skill_can_produce_mix(sd,7135,-1, 50) //50 Flame Bottle
+ ) {
+ clif_skill_fail(sd,skillid,0,0);
+ break;
+ }
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ skill_produce_mix(sd, skillid, 970, 0, 0, 0, 100);
+ skill_produce_mix(sd, skillid, 7136, 0, 0, 0, 50);
+ skill_produce_mix(sd, skillid, 7135, 0, 0, 0, 50);
+ }
+ break;
+ case SA_DISPELL: /* ディスペル */
+ {
+ int i;
+ struct status_change *tsc_data = status_get_sc_data(bl);
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ if (rand()%100 >= (50+10*skilllv)*sc_def_mdef/100 // Fixed & changed to use a proportionnal reduction (no info, but seems far more logical) [DracoRPG]
+ || tsc_data == NULL || (tsc_data[SC_SPIRIT].timer != -1 && tsc_data[SC_SPIRIT].val2 == SL_ROGUE)) //Rogue's spirit defends againt dispel.
+ {
+ if (sd)
+ clif_skill_fail(sd,skillid,0,0);
+ break;
+ }
+ if(status_isimmune(bl))
+ break;
+ for(i=0;i<SC_MAX;i++){
+ if (tsc_data[i].timer == -1)
+ continue;
+ if(i==SC_HALLUCINATION || i==SC_WEIGHT50 || i==SC_WEIGHT90
+ || i==SC_STRIPWEAPON || i==SC_STRIPSHIELD || i==SC_STRIPARMOR || i==SC_STRIPHELM
+ || i==SC_CP_WEAPON || i==SC_CP_SHIELD || i==SC_CP_ARMOR || i==SC_CP_HELM
+ || i==SC_COMBO || i==SC_DANCING || i==SC_GUILDAURA || i==SC_STEELBODY || i==SC_EDP
+ || i==SC_CARTBOOST || i==SC_MELTDOWN || i==SC_MOONLIT
+ )
+ continue;
+ if(i==SC_BERSERK) tsc_data[i].val4=1; //Mark a dispelled berserk to avoid setting hp to 100.
+ status_change_end(bl,i,-1);
+ }
+ }
+ break;
+
+ case TF_BACKSLIDING: //This is the correct implementation as per packet logging information. [Skotlex]
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ skill_blown(src,bl,skill_get_blewcount(skillid,skilllv)|0x10000);
+ break;
+
+ case TK_HIGHJUMP:
+ {
+ int x,y, dir = status_get_dir(src);
+
+ if (sd && !pc_can_move(sd))
+ return 0;
+ if (md && !mob_can_move(md))
+ return 0;
+
+ x = src->x + dirx[dir]*skilllv*2;
+ y = src->y + diry[dir]*skilllv*2;
+
+ clif_skill_nodamage(src,bl,TK_HIGHJUMP,skilllv,1);
+ if(map_getcell(src->m,x,y,CELL_CHKPASS)) {
+ if (sd) pc_movepos(sd,x,y,0);
+ if (md) mob_warp(md, src->m, x, y, 0);
+ clif_slide(src,x,y);
+ }
+ }
+ break;
+
+ case SA_CASTCANCEL:
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ skill_castcancel(src,1);
+ if(sd) {
+ int sp = skill_get_sp(sd->skillid_old,sd->skilllv_old);
+ sp = sp * (90 - (skilllv-1)*20) / 100;
+ if(sp < 0) sp = 0;
+ pc_heal(sd,0,-sp);
+ }
+ break;
+ case SA_SPELLBREAKER: // スペルブレイカ?
+ {
+ struct status_change *sc_data = status_get_sc_data(bl);
+ int sp;
+ if(sc_data && sc_data[SC_MAGICROD].timer != -1) {
+ if(dstsd) {
+ sp = skill_get_sp(skillid,skilllv);
+ sp = sp * sc_data[SC_MAGICROD].val2 / 100;
+ if(sp > 0x7fff) sp = 0x7fff;
+ else if(sp < 1) sp = 1;
+ if(dstsd->status.sp + sp > dstsd->status.max_sp) {
+ sp = dstsd->status.max_sp - dstsd->status.sp;
+ dstsd->status.sp = dstsd->status.max_sp;
+ }
+ else
+ dstsd->status.sp += sp;
+ clif_heal(dstsd->fd,SP_SP,sp);
+ }
+ clif_skill_nodamage(bl,bl,SA_MAGICROD,sc_data[SC_MAGICROD].val1,1);
+ if(sd) {
+ sp = sd->status.max_sp/5;
+ if(sp < 1) sp = 1;
+ pc_heal(sd,0,-sp);
+ }
+ }
+ else {
+ int bl_skillid=0,bl_skilllv=0,hp = 0;
+ if(bl->type == BL_PC) {
+ if(dstsd && dstsd->skilltimer != -1) {
+ bl_skillid = dstsd->skillid;
+ bl_skilllv = dstsd->skilllv;
+ if (map_flag_vs(bl->m))
+ hp = status_get_max_hp(bl)/50; //Recover 2% HP [Skotlex]
+ }
+ }
+ else if(bl->type == BL_MOB) {
+ if(dstmd && dstmd->skilltimer != -1) {
+ if (status_get_mode(bl) & MD_BOSS)
+ { //Only 10% success chance against bosses. [Skotlex]
+ if (rand()%100 < 90)
+ {
+ if (sd) clif_skill_fail(sd,skillid,0,0);
+ break;
+ }
+ } else
+ hp = status_get_max_hp(bl)/50; //Recover 2% HP [Skotlex]
+ bl_skillid = dstmd->skillid;
+ bl_skilllv = dstmd->skilllv;
+ }
+ }
+ if(bl_skillid > 0 /*&& bl_skillid != PA_PRESSURE && skill_db[bl_skillid].skill_type == BF_MAGIC*/) { //Reports indicate Spell Break cancels any type of skill, except Pressure. [Skotlex]
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ skill_castcancel(bl,0);
+ sp = skill_get_sp(bl_skillid,bl_skilllv);
+ if(dstsd)
+ pc_heal(dstsd,-hp,-sp);
+ if(sd) {
+ sp = sp*(25*(skilllv-1))/100;
+ if(skilllv > 1 && sp < 1) sp = 1;
+ if(sp > 0x7fff) sp = 0x7fff;
+ else if(sp < 1) sp = 1;
+ if(sd->status.sp + sp > sd->status.max_sp) {
+ sp = sd->status.max_sp - sd->status.sp;
+ sd->status.sp = sd->status.max_sp;
+ }
+ else
+ sd->status.sp += sp;
+
+ if (hp && skilllv > 5)
+ { //Recover half damaged HP at levels 6-10 [Skotlex]
+ hp /=2;
+ if(sd->status.hp + hp > sd->status.max_hp) {
+ hp = sd->status.max_hp - sd->status.hp;
+ sd->status.hp = sd->status.max_hp;
+ }
+ else
+ sd->status.hp += hp;
+
+ clif_heal(sd->fd,SP_HP,hp);
+ }
+ clif_heal(sd->fd,SP_SP,sp);
+ }
+ }
+ else if(sd)
+ clif_skill_fail(sd,skillid,0,0);
+ }
+ }
+ break;
+ case SA_MAGICROD:
+ if (status_isimmune(bl))
+ break;
+ status_change_start(bl,SkillStatusChangeTable[skillid],skilllv,0,0,0,skill_get_time(skillid,skilllv),0 );
+ break;
+ case SA_AUTOSPELL: /* オ?トスペル */
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ if(sd)
+ clif_autospell(sd,skilllv);
+ else {
+ int maxlv=1,spellid=0;
+ static const int spellarray[3] = { MG_COLDBOLT,MG_FIREBOLT,MG_LIGHTNINGBOLT };
+ if(skilllv >= 10) {
+ spellid = MG_FROSTDIVER;
+// if (sc_data && sc_data[SC_SPIRIT].timer != -1 && sc_data[SC_SPIRIT].val2 == SA_SAGE)
+// maxlv = 10;
+// else
+ maxlv = skilllv - 9;
+ }
+ else if(skilllv >=8) {
+ spellid = MG_FIREBALL;
+ maxlv = skilllv - 7;
+ }
+ else if(skilllv >=5) {
+ spellid = MG_SOULSTRIKE;
+ maxlv = skilllv - 4;
+ }
+ else if(skilllv >=2) {
+ int i = rand()%3;
+ spellid = spellarray[i];
+ maxlv = skilllv - 1;
+ }
+ else if(skilllv > 0) {
+ spellid = MG_NAPALMBEAT;
+ maxlv = 3;
+ }
+ if(spellid > 0)
+ status_change_start(src,SC_AUTOSPELL,skilllv,spellid,maxlv,0,
+ skill_get_time(SA_AUTOSPELL,skilllv),0);
+ }
+ break;
+
+ case BS_GREED:
+ if(sd){
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ map_foreachinarea(skill_greed,bl->m,bl->x-2,bl->y-2,bl->x+2,bl->y+2,BL_ITEM,bl);
+ }
+ break;
+
+ case SA_ELEMENTWATER:
+ case SA_ELEMENTFIRE:
+ case SA_ELEMENTGROUND:
+ case SA_ELEMENTWIND:
+ if(dstmd && !(status_get_mode(bl)&MD_BOSS)){
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ dstmd->def_ele = skill_get_pl(skillid);
+ dstmd->def_ele += (1+rand()%4)*20;
+ }
+ break;
+
+ /* ランダム??ォ?化?A???ォ?化?A地?A火?A風 */
+ case NPC_ATTRICHANGE:
+ case NPC_CHANGEWATER:
+ case NPC_CHANGEGROUND:
+ case NPC_CHANGEFIRE:
+ case NPC_CHANGEWIND:
+ /* 毒?A?ケ?A念?A闇 */
+ case NPC_CHANGEPOISON:
+ case NPC_CHANGEHOLY:
+ case NPC_CHANGEDARKNESS:
+ case NPC_CHANGETELEKINESIS:
+ case NPC_CHANGEUNDEAD:
+ if(md){
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ md->def_ele = skill_get_pl(skillid);
+ if (md->def_ele == 0) /* ランダム?化?Aただし?A*/
+ md->def_ele = rand()%10; /* 不死??ォは?怩ュ */
+ md->def_ele += (1+rand()%4)*20; /* ??ォレベルはランダム */
+ }
+ break;
+
+ case NPC_PROVOCATION:
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ if(md)
+ clif_pet_performance(src,md->db->skill[md->skillidx].val[0]);
+ break;
+
+ case NPC_HALLUCINATION:
+ if(status_isimmune(bl)) {
+ clif_skill_nodamage(src,bl,skillid,skilllv,0);
+ break;
+ }
+ status_change_start(bl,SkillStatusChangeTable[skillid],skilllv,0,0,0,skill_get_time(skillid,skilllv),0 );
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ break;
+
+ case NPC_KEEPING:
+ case NPC_BARRIER:
+ {
+ int skill_time = skill_get_time(skillid,skilllv);
+ status_change_start(bl,SkillStatusChangeTable[skillid],skilllv,0,0,0,skill_time,0 );
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ if (md)
+ mob_changestate(md,MS_DELAY,skill_time);
+ else if (sd)
+ sd->attackabletime = sd->canmove_tick = tick + skill_time;
+ }
+ break;
+
+ case NPC_REBIRTH:
+ if (md && md->state.state == MS_DEAD) {
+ mob_setdelayspawn (md->bl.id);
+ }
+ break;
+
+ case NPC_DARKBLESSING:
+ {
+ int sc_def = 100 - status_get_mdef(bl);
+ if(status_isimmune(bl) || status_get_elem_type(bl) == 7 || status_get_race(bl) == 6) {
+ clif_skill_nodamage(src,bl,skillid,skilllv,0);
+ break;
+ }
+ if(rand()%100 < sc_def*(50+skilllv*5)/100)
+ status_change_start(bl,SkillStatusChangeTable[skillid],skilllv,0,0,0,skill_get_time2(skillid,skilllv),0 );
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ }
+ break;
+
+ case NPC_LICK:
+ if (dstsd) {
+ if (dstsd->special_state.no_weapon_damage ) {
+ clif_skill_nodamage(src,bl,skillid,skilllv,0);
+ break;
+ }
+ pc_heal(dstsd,0,-100);
+ }
+ if(rand()%100 < (skilllv*5)*sc_def_vit/100)
+ status_change_start(bl,SkillStatusChangeTable[skillid],skilllv,0,0,0,skill_get_time2(skillid,skilllv),0 );
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ break;
+
+ case NPC_SUICIDE: /* 自決 */
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ battle_damage(NULL,src,status_get_hp(bl),3); //Suicidal Mobs should give neither exp (flag&1) not items (flag&2) [Skotlex]
+ break;
+
+ case NPC_SUMMONSLAVE: /* 手下?「喚 */
+ case NPC_SUMMONMONSTER: /* MOB?「喚 */
+ if(md)
+ mob_summonslave(md,md->db->skill[md->skillidx].val,skilllv,skillid);
+ break;
+
+ case NPC_CALLSLAVE: //取り巻き呼び戻し
+ mob_warpslave(src,AREA_SIZE/2);
+ break;
+
+ case NPC_RANDOMMOVE:
+ if (md) {
+ md->next_walktime = tick - 1;
+ mob_randomwalk(md,tick);
+ }
+ break;
+
+ case NPC_SPEEDUP:
+ {
+ // or does it increase casting rate? just a guess xD
+ int i = SC_ASPDPOTION0 + skilllv - 1;
+ if (i > SC_ASPDPOTION3)
+ i = SC_ASPDPOTION3;
+ status_change_start(bl,i,skilllv,0,0,0,skilllv * 60000,0);
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ }
+ break;
+
+ case NPC_REVENGE:
+ // not really needed... but adding here anyway ^^
+ if (md && md->master_id > 0) {
+ struct block_list *mbl, *tbl;
+ if ((mbl = map_id2bl(md->master_id)) == NULL ||
+ (tbl = battle_gettargeted(mbl)) == NULL)
+ break;
+ md->state.provoke_flag = tbl->id;
+ mob_target(md, tbl, md->db->range);
+ }
+ break;
+
+ case NPC_RUN: //後退
+ if(md) {
+ int dist = skilllv; //Run skillv tiles.
+ int dir = (bl == src)?md->dir:map_calc_dir(src,bl->x,bl->y); //If cast on self, run forward, else run away.
+ int mask[8][2] = {{0,-1},{1,-1},{1,0},{1,1},{0,1},{-1,1},{-1,0},{-1,-1}};
+
+ md->attacked_id = 0;
+ md->attacked_count = 0;
+ md->target_id = 0;
+ md->state.targettype = NONE_ATTACKABLE;
+ md->state.skillstate = MSS_IDLE;
+ mob_walktoxy(md, md->bl.x + dist * mask[dir][0], md->bl.y + dist * mask[dir][1], 0);
+ }
+ break;
+
+ case NPC_TRANSFORMATION:
+ case NPC_METAMORPHOSIS:
+ if(md) {
+ if (skilllv > 1)
+ { //Multiply skilllv times, the original instance must be silently killed. [Skotlex]
+ mob_summonslave(md,md->db->skill[md->skillidx].val,skilllv,skillid);
+ mob_delete(md);
+ }
+ else
+ { //Transform into another class.
+ int class_ = mob_random_class (md->db->skill[md->skillidx].val,0);
+ if (class_) mob_class_change(md, class_);
+ }
+ }
+ break;
+
+ case NPC_EMOTION_ON:
+ case NPC_EMOTION:
+ if(md)
+ {
+ clif_emotion(&md->bl,md->db->skill[md->skillidx].val[0]);
+ if(!md->special_state.ai && (md->db->skill[md->skillidx].val[1] || md->db->skill[md->skillidx].val[2]))
+ { //NPC_EMOTION & NPC_EMOTION_ON can change a mob's mode 'permanently' [Skotlex]
+ //val[1] 'sets' the mode, val[2] can add/remove from the current mode based on skill used:
+ //NPC_EMOTION_ON adds a mode / NPC_EMOTION removes it.
+ int mode, mode2;
+ mode = status_get_mode(src);
+ mode2 = (md->db->skill[md->skillidx].val[1])?(md->db->skill[md->skillidx].val[1]):mode;
+ if (md->db->skill[md->skillidx].val[2]) { //Alter the mode.
+ if (skillid == NPC_EMOTION_ON) //Add a mode
+ mode2|= md->db->skill[md->skillidx].val[2];
+ else //Remove a mode
+ mode2&= ~(md->db->skill[md->skillidx].val[2]);
+ }
+ if (mode == mode2)
+ break; //No change
+ md->mode = mode2;
+ if (md->mode == md->db->mode)
+ md->mode = 0; //Fallback to the db's mode.
+ //Since mode changed, reset their state.
+ mob_stopattack(md);
+ mob_stop_walking(md,0);
+ }
+ }
+ break;
+
+ case NPC_DEFENDER:
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ break;
+
+ // Equipment breaking monster skills [Celest]
+ case NPC_BREAKWEAPON:
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ if(dstsd && battle_config.equip_skill_break_rate)
+ pc_breakweapon(dstsd);
+ break;
+
+ case NPC_BREAKARMOR:
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ if(dstsd && battle_config.equip_skill_break_rate)
+ pc_breakarmor(dstsd);
+ break;
+
+ case NPC_BREAKHELM:
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ if(dstsd && battle_config.equip_skill_break_rate)
+ pc_breakhelm(dstsd);
+ break;
+
+ case NPC_BREAKSHIELD:
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ if(dstsd && battle_config.equip_skill_break_rate)
+ pc_breakshield(dstsd);
+ break;
+
+ case NPC_POWERUP: //NPC爆裂波動
+ status_change_start(bl,SC_EXPLOSIONSPIRITS,skilllv,0,0,0,skilllv * 60000,0);
+ // another random guess xP
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ status_change_start(bl,SC_INCALLSTATUS,skilllv * 5,0,0,0,skilllv * 60000,0);
+ break;
+
+ case NPC_AGIUP:
+ status_change_start(bl,SC_INCAGI,skilllv * 10,0,0,0,skilllv * 60000,0);
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ break;
+
+ case NPC_SIEGEMODE:
+ case NPC_INVISIBLE:
+ // not sure what it does
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ break;
+
+ case WE_MALE: /* 君だけは護るよ */
+ if(sd && dstsd){
+ int hp_rate=(skilllv <= 0)? 0:skill_db[skillid].hp_rate[skilllv-1];
+ int gain_hp=dstsd->status.max_hp*abs(hp_rate)/100;// The earned is the same % of the target HP than it costed the caster. [Skotlex]
+ gain_hp = battle_heal(NULL,bl,gain_hp,0,0);
+ clif_skill_nodamage(src,bl,skillid,gain_hp,1);
+ }
+ break;
+ case WE_FEMALE: /* あなたの?に??オになります */
+ if(sd && dstsd){
+ int sp_rate=(skilllv <= 0)? 0:skill_db[skillid].sp_rate[skilllv-1];
+ int gain_sp=dstsd->status.max_sp*abs(sp_rate)/100;// The earned is the same % of the target SP than it costed the caster. [Skotlex]
+ gain_sp = battle_heal(NULL,bl,0,gain_sp,0);
+ clif_skill_nodamage(src,bl,skillid,gain_sp,1);
+ }
+ break;
+
+ case WE_CALLPARTNER: /* あなたに?いたい */
+ if(sd){
+ if((dstsd = pc_get_partner(sd)) == NULL){
+ clif_skill_fail(sd,skillid,0,0);
+ map_freeblock_unlock();
+ return 0;
+ }
+ if(map[sd->bl.m].flag.nomemo || map[sd->bl.m].flag.nowarpto || map[dstsd->bl.m].flag.nowarp){
+ clif_skill_teleportmessage(sd,1);
+ map_freeblock_unlock();
+ return 0;
+ }
+ skill_unitsetting(src,skillid,skilllv,sd->bl.x,sd->bl.y,0);
+ pc_blockskill_start (sd, skillid, skill_get_time(skillid, skilllv));
+ }
+ break;
+
+// parent-baby skills
+ case WE_BABY:
+ if(sd){
+ struct map_session_data *f_sd = pc_get_father(sd);
+ struct map_session_data *m_sd = pc_get_mother(sd);
+ // if neither was found
+ if(!f_sd && !m_sd){
+ clif_skill_fail(sd,skillid,0,0);
+ map_freeblock_unlock();
+ return 0;
+ }
+ status_change_start(bl,SC_STAN,skilllv,0,0,0,skill_get_time2(skillid,skilllv),0);
+ if (f_sd) status_change_start(&f_sd->bl,SkillStatusChangeTable[skillid],skilllv,0,0,0,skill_get_time(skillid,skilllv),0 );
+ if (m_sd) status_change_start(&m_sd->bl,SkillStatusChangeTable[skillid],skilllv,0,0,0,skill_get_time(skillid,skilllv),0 );
+ }
+ break;
+
+ case WE_CALLPARENT:
+ if(sd){
+ struct map_session_data *f_sd = pc_get_father(sd);
+ struct map_session_data *m_sd = pc_get_mother(sd);
+ // if neither was found
+ if(!f_sd && !m_sd){
+ clif_skill_fail(sd,skillid,0,0);
+ map_freeblock_unlock();
+ return 0;
+ }
+ if(map[sd->bl.m].flag.nomemo || map[sd->bl.m].flag.nowarpto)
+ {
+ clif_skill_teleportmessage(sd,1);
+ map_freeblock_unlock();
+ return 0;
+ }
+ if((!f_sd && m_sd && map[m_sd->bl.m].flag.nowarp) ||
+ (!m_sd && f_sd && map[f_sd->bl.m].flag.nowarp))
+ { //Case where neither one can be warped.
+ clif_skill_teleportmessage(sd,1);
+ map_freeblock_unlock();
+ return 0;
+ }
+ //Warp those that can be warped.
+ if (f_sd && !map[f_sd->bl.m].flag.nowarp)
+ pc_setpos(f_sd,map[sd->bl.m].index,sd->bl.x,sd->bl.y,3);
+ if (m_sd && !map[m_sd->bl.m].flag.nowarp)
+ pc_setpos(m_sd,map[sd->bl.m].index,sd->bl.x,sd->bl.y,3);
+ }
+ break;
+
+ case WE_CALLBABY:
+ if(sd && dstsd)
+ {
+ if(map[sd->bl.m].flag.nomemo || map[sd->bl.m].flag.nowarpto || map[dstsd->bl.m].flag.nowarp){
+ clif_skill_teleportmessage(sd,1);
+ map_freeblock_unlock();
+ return 0;
+ }
+ pc_setpos(dstsd,map[sd->bl.m].index,sd->bl.x,sd->bl.y,3);
+ }
+ break;
+
+ case PF_HPCONVERSION: /* ライフ置き換え */
+ clif_skill_nodamage(src, bl, skillid, skilllv, 1);
+ if (sd) {
+ int hp, sp;
+ hp = sd->status.max_hp / 10; //基本はHPの10%
+ sp = hp * 10 * skilllv / 100;
+ if (sd->status.sp + sp > sd->status.max_sp)
+ sp = sd->status.max_sp - sd->status.sp;
+ // we need to check with the sp that was taken away when casting too
+ if (sd->status.sp + skill_get_sp(skillid, skilllv) >= sd->status.max_sp)
+ hp = sp = 0;
+ pc_heal(sd, -hp, sp);
+ clif_heal(sd->fd, SP_SP, sp);
+ clif_updatestatus(sd, SP_SP);
+ }
+ break;
+ case HT_REMOVETRAP: /* リム?ブトラップ */
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ {
+ struct skill_unit *su=NULL;
+ struct item item_tmp;
+ int flag;
+ if((bl->type==BL_SKILL) &&
+ (su=(struct skill_unit *)bl) &&
+ (su->group->src_id == src->id || map_flag_vs(bl->m)) &&
+ (skill_get_inf2(su->group->skill_id) & INF2_TRAP))
+ {
+ if(sd && su->group->val3 != BD_INTOABYSS)
+ { //Avoid collecting traps when it does not costs to place them down. [Skotlex]
+ if(battle_config.skill_removetrap_type){
+ for(i=0;i<10;i++) {
+ if(skill_db[su->group->skill_id].itemid[i] > 0){
+ memset(&item_tmp,0,sizeof(item_tmp));
+ item_tmp.nameid = skill_db[su->group->skill_id].itemid[i];
+ item_tmp.identify = 1;
+ if(item_tmp.nameid && (flag=pc_additem(sd,&item_tmp,skill_db[su->group->skill_id].amount[i]))){
+ clif_additem(sd,0,0,flag);
+ map_addflooritem(&item_tmp,skill_db[su->group->skill_id].amount[i],sd->bl.m,sd->bl.x,sd->bl.y,NULL,NULL,NULL,0);
+ }
+ }
+ }
+ }else{
+ memset(&item_tmp,0,sizeof(item_tmp));
+ item_tmp.nameid = 1065;
+ item_tmp.identify = 1;
+ if(item_tmp.nameid && (flag=pc_additem(sd,&item_tmp,1))){
+ clif_additem(sd,0,0,flag);
+ map_addflooritem(&item_tmp,1,sd->bl.m,sd->bl.x,sd->bl.y,NULL,NULL,NULL,0);
+ }
+ }
+ }
+ if(su->group->unit_id == UNT_ANKLESNARE && su->group->val2){
+ struct block_list *target=map_id2bl(su->group->val2);
+ if(target && (target->type == BL_PC || target->type == BL_MOB))
+ status_change_end(target,SC_ANKLE,-1);
+ }
+ skill_delunit(su);
+ }
+ }
+ break;
+ case HT_SPRINGTRAP: /* スプリングトラップ */
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ {
+ struct skill_unit *su=NULL;
+ if((bl->type==BL_SKILL) && (su=(struct skill_unit *)bl) && (su->group) ){
+ switch(su->group->unit_id){
+ case UNT_ANKLESNARE: // ankle snare
+ if (su->group->val2 != 0)
+ // if it is already trapping something don't spring it,
+ // remove trap should be used instead
+ break;
+ // otherwise fallthrough to below
+ case UNT_BLASTMINE:
+ case UNT_SKIDTRAP:
+ case UNT_LANDMINE:
+ case UNT_SHOCKWAVE:
+ case UNT_SANDMAN:
+ case UNT_FLASHER:
+ case UNT_FREEZINGTRAP:
+ case UNT_CLAYMORETRAP:
+ case UNT_TALKIEBOX:
+ su->group->unit_id = UNT_USED_TRAPS;
+ clif_changelook(bl,LOOK_BASE,su->group->unit_id);
+ su->group->limit=DIFF_TICK(tick+1500,su->group->tick);
+ su->limit=DIFF_TICK(tick+1500,su->group->tick);
+ }
+ }
+ }
+ break;
+ case BD_ENCORE: /* アンコ?ル */
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ if(sd)
+ skill_use_id(sd,src->id,sd->skillid_dance,sd->skilllv_dance);
+ break;
+
+ case AS_SPLASHER: /* ベナムスプラッシャ? */
+ if(status_get_max_hp(bl)*2/3 < status_get_hp(bl)) { //HPが2/3以??っていたら失敗
+ map_freeblock_unlock();
+ return 1;
+ }
+ status_change_start(bl,SkillStatusChangeTable[skillid],skilllv,skillid,src->id,skill_get_time(skillid,skilllv),1000,0 );
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ break;
+
+ case PF_MINDBREAKER: /* プ?ボック */
+ {
+ struct status_change *sc_data = status_get_sc_data(bl);
+
+ /* MVPmobと不死には?かない */
+ if(status_get_mode(bl)&MD_BOSS || battle_check_undead(status_get_race(bl),status_get_elem_type(bl))) //不死には?かない
+ {
+ map_freeblock_unlock();
+ return 1;
+ }
+
+ if (rand()%100 > 55 + skilllv*5)
+ { //Has a 55% + skilllv*5% success chance.
+ clif_skill_nodamage(src,bl,skillid,skilllv,0);
+ if (sd)
+ clif_skill_fail(sd,skillid,0,0);
+ map_freeblock_unlock();
+ return 0;
+ }
+ status_change_start(bl,SkillStatusChangeTable[skillid],skilllv,0,0,0,skill_get_time(skillid,skilllv),0 );
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+
+ if(dstmd && dstmd->skilltimer!=-1 && dstmd->state.skillcastcancel) // 詠?・妨害
+ skill_castcancel(bl,0);
+ if(dstsd && dstsd->skilltimer!=-1 && (!dstsd->special_state.no_castcancel || map_flag_gvg(bl->m))
+ && dstsd->state.skillcastcancel && !dstsd->special_state.no_castcancel2)
+ skill_castcancel(bl,0);
+
+ if(sc_data){
+ if(sc_data[SC_FREEZE].timer!=-1)
+ status_change_end(bl,SC_FREEZE,-1);
+ if(sc_data[SC_STONE].timer!=-1 && sc_data[SC_STONE].val2==0)
+ status_change_end(bl,SC_STONE,-1);
+ if(sc_data[SC_SLEEP].timer!=-1)
+ status_change_end(bl,SC_SLEEP,-1);
+ }
+
+ if(dstmd)
+ mob_target(dstmd,src,skill_get_range2(src,skillid,skilllv));
+ }
+ break;
+
+ case PF_SOULCHANGE:
+ {
+ int sp1 = 0, sp2 = 0;
+ if (sd) {
+ if (dstsd) {
+ sp1 = sd->status.sp > dstsd->status.max_sp ? dstsd->status.max_sp : sd->status.sp;
+ sp2 = dstsd->status.sp > sd->status.max_sp ? sd->status.max_sp : dstsd->status.sp;
+ sd->status.sp = sp2;
+ dstsd->status.sp = sp1;
+ clif_heal(sd->fd,SP_SP,sp2);
+ clif_updatestatus(sd,SP_SP);
+ clif_heal(dstsd->fd,SP_SP,sp1);
+ clif_updatestatus(dstsd,SP_SP);
+ } else if (dstmd) {
+ if (dstmd->state.soul_change_flag) {
+ clif_skill_fail(sd,skillid,0,0);
+ map_freeblock_unlock();
+ return 0;
+ }
+ sp2 = sd->status.max_sp * 3 /100;
+ if (sd->status.sp + sp2 > sd->status.max_sp)
+ sp2 = sd->status.max_sp - sd->status.sp;
+ sd->status.sp += sp2;
+ clif_heal(sd->fd,SP_SP,sp2);
+ clif_updatestatus(sd,SP_SP);
+ dstmd->state.soul_change_flag = 1;
+ }
+ }
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ }
+ break;
+
+ // Slim Pitcher
+ case CR_SLIMPITCHER:
+ {
+ if (sd && flag&1) {
+ struct block_list tbl;
+ int hp = potion_hp * (100 + pc_checkskill(sd,CR_SLIMPITCHER)*10 + pc_checkskill(sd,AM_POTIONPITCHER)*10 + pc_checkskill(sd,AM_LEARNINGPOTION)*5)/100;
+ hp = hp * (100 + (status_get_vit(bl)<<1))/100;
+ if (dstsd) {
+ hp = hp * (100 + pc_checkskill(dstsd,SM_RECOVERY)*10)/100;
+ }
+ tbl.id = 0;
+ tbl.m = src->m;
+ tbl.x = src->x;
+ tbl.y = src->y;
+ clif_skill_nodamage(&tbl,bl,AL_HEAL,hp,1);
+ battle_heal(NULL,bl,hp,0,0);
+ }
+ }
+ break;
+ // Full Chemical Protection
+ case CR_FULLPROTECTION:
+ {
+ int i, skilltime;
+ struct status_change *tsc_data = status_get_sc_data(bl);
+ skilltime = skill_get_time(skillid,skilllv);
+ for (i=0; i<4; i++) {
+ if(tsc_data && tsc_data[SC_STRIPWEAPON + i].timer != -1)
+ status_change_end(bl, SC_STRIPWEAPON + i, -1 );
+ status_change_start(bl,SC_CP_WEAPON + i,skilllv,0,0,0,skilltime,0 );
+ }
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ }
+ break;
+
+ case RG_CLEANER: //AppleGirl
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ break;
+
+ case ST_PRESERVE:
+ if (sd){
+ if (sd->sc_count && sd->sc_data[SC_PRESERVE].timer != -1)
+ status_change_end(src, SC_PRESERVE, -1 );
+ else
+ status_change_start(src,SC_PRESERVE,skilllv,0,0,0,skill_get_time(skillid, skilllv),0 );
+ clif_skill_nodamage(src,src,skillid,skilllv,1);
+ }
+ break;
+
+ case PF_DOUBLECASTING:
+ if (rand() % 100 > 30 + skilllv * 10) {
+ clif_skill_fail(sd,skillid,0,0);
+ map_freeblock_unlock();
+ return 0;
+ }
+ status_change_start(bl,SkillStatusChangeTable[skillid],skilllv,0,0,0,skill_get_time(skillid,skilllv),0 );
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ break;
+
+ case CG_LONGINGFREEDOM:
+ {
+ struct status_change *sc_data = status_get_sc_data(src);
+ if (sc_data && sc_data[SC_LONGING].timer == -1 && sc_data[SC_DANCING].timer != -1 && sc_data[SC_DANCING].val4
+ && sc_data[SC_DANCING].val1 != CG_MOONLIT) //Can't use Longing for Freedom while under Moonlight Petals. [Skotlex]
+ {
+ status_change_start(src,SkillStatusChangeTable[skillid],skilllv,0,0,0,skill_get_time(skillid,skilllv),0);
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ }
+ }
+ break;
+
+ case CG_TAROTCARD:
+ {
+ int eff, count = -1;
+ if (rand() % 100 > skilllv * 8) {
+ if (sd) clif_skill_fail(sd,skillid,0,0);
+ map_freeblock_unlock();
+ return 0;
+ }
+ do {
+ eff = rand() % 14;
+ clif_specialeffect(bl, 523 + eff, 0);
+ switch (eff)
+ {
+ case 0: // heals SP to 0
+ if (dstsd) pc_heal(dstsd,0,-dstsd->status.sp);
+ break;
+ case 1: // matk halved
+ status_change_start(bl,SC_INCMATKRATE,-50,0,0,0,30000,0);
+ break;
+ case 2: // all buffs removed
+ status_change_clear_buffs(bl);
+ break;
+ case 3: // 1000 damage, random armor destroyed
+ {
+ int where[] = { EQP_ARMOR, EQP_SHIELD, EQP_HELM };
+ battle_damage(src, bl, 1000, 0);
+ clif_damage(src,bl,tick,0,0,1000,0,0,0);
+ if (dstsd && battle_config.equip_skill_break_rate) pc_break_equip(dstsd, where[rand() % 3]);
+ }
+ break;
+ case 4: // atk halved
+ status_change_start(bl,SC_INCATKRATE,-50,0,0,0,30000,0);
+ break;
+ case 5: // 2000HP heal, random teleported
+ battle_heal(src, src, 2000, 0, 0);
+ if(sd && !map[src->m].flag.noteleport) pc_setpos(sd,sd->mapindex,-1,-1,3);
+ else if(md && !map[src->m].flag.monster_noteleport) mob_warp(md,-1,-1,-1,3);
+ break;
+ case 6: // random 2 other effects
+ if (count == -1)
+ count = 3;
+ else
+ count++; //Should not retrigger this one.
+ break;
+ case 7: // stop freeze or stoned
+ {
+ int sc[] = { SC_STOP, SC_FREEZE, SC_STONE };
+ status_change_start(bl,sc[rand()%3],skilllv,0,0,0,30000,0);
+ }
+ break;
+ case 8: // curse coma and poison
+ status_change_start(bl,SC_COMA,skilllv,0,0,0,30000,0);
+ status_change_start(bl,SC_CURSE,skilllv,0,0,0,30000,0);
+ status_change_start(bl,SC_POISON,skilllv,0,0,0,30000,0);
+ break;
+ case 9: // chaos
+ status_change_start(bl,SC_CONFUSION,skilllv,0,0,0,30000,0);
+ break;
+ case 10: // 6666 damage, atk matk halved, cursed
+ battle_damage(src, bl, 6666, 0);
+ clif_damage(src,bl,tick,0,0,6666,0,0,0);
+ status_change_start(bl,SC_INCATKRATE,-50,0,0,0,30000,0);
+ status_change_start(bl,SC_INCMATKRATE,-50,0,0,0,30000,0);
+ status_change_start(bl,SC_CURSE,skilllv,0,0,0,30000,0);
+ break;
+ case 11: // 4444 damage
+ battle_damage(src, bl, 4444, 0);
+ clif_damage(src,bl,tick,0,0,4444,0,0,0);
+ break;
+ case 12: // stun
+ status_change_start(bl,SC_STAN,skilllv,0,0,0,5000,0);
+ break;
+ case 13: // atk,matk,hit,flee,def reduced
+ status_change_start(bl,SC_INCATKRATE,-20,0,0,0,30000,0);
+ status_change_start(bl,SC_INCMATKRATE,-20,0,0,0,30000,0);
+ status_change_start(bl,SC_INCHITRATE,-20,0,0,0,30000,0);
+ status_change_start(bl,SC_INCFLEERATE,-20,0,0,0,30000,0);
+ status_change_start(bl,SC_INCDEFRATE,-20,0,0,0,30000,0);
+ break;
+ default:
+ break;
+ }
+ } while ((--count) > 0);
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ }
+ break;
+
+ case SL_ALCHEMIST:
+ case SL_ASSASIN:
+ case SL_BARDDANCER:
+ case SL_BLACKSMITH:
+ case SL_CRUSADER:
+ case SL_HUNTER:
+ case SL_KNIGHT:
+ case SL_MONK:
+ case SL_PRIEST:
+ case SL_ROGUE:
+ case SL_SAGE:
+ case SL_SOULLINKER:
+ case SL_STAR:
+ case SL_SUPERNOVICE:
+ case SL_WIZARD:
+ if (sd && !(dstsd && (dstsd->class_&MAPID_UPPERMASK) == SkillStatusChangeTable[skillid])) {
+ clif_skill_fail(sd,skillid,0,0);
+ break;
+ }
+ status_change_start(bl,SC_SPIRIT,skilllv,skillid,0,0,skill_get_time(skillid,skilllv),0);
+ status_change_start(src,SC_COMBO,SL_SMA,skilllv,0,0,skill_get_time2(skillid,skilllv),0);
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ break;
+ case SL_HIGH:
+ if (sd && !(dstsd && (dstsd->class_&JOBL_UPPER) && !(dstsd->class_&JOBL_2) && dstsd->status.base_level < 70)) {
+ clif_skill_fail(sd,skillid,0,0);
+ break;
+ }
+ status_change_start(bl,SkillStatusChangeTable[skillid],skilllv,skillid,0,0,skill_get_time(skillid,skilllv),0 );
+ status_change_start(src,SC_COMBO,SL_SMA,skilllv,0,0,skill_get_time2(skillid,skilllv),0);
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ break;
+
+ case SL_SKA: // [marquis007]
+ if (sd && bl->type != BL_MOB) {
+ status_change_start(src,SC_STAN,skilllv,0,0,0,3000,0);
+ clif_skill_fail(sd,skillid,0,0);
+ break;
+ }
+ if (sd && status_get_mode(bl)&MD_BOSS)
+ clif_skill_fail(sd,skillid,0,0);
+ else
+ {
+ status_change_start(bl,SkillStatusChangeTable[skillid],skilllv,0,0,0,skill_get_time(skillid,skilllv),0);
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ }
+ break;
+ case SL_SWOO:
+ if (sd && bl->type != BL_MOB) {
+ status_change_start(src,SC_STAN,skilllv,0,0,0,3000,0);
+ clif_skill_fail(sd,skillid,0,0);
+ break;
+ }
+ status_change_start(bl,SkillStatusChangeTable[skillid],skilllv,0,0,0,status_get_mode(bl)&MD_BOSS?skill_get_time(skillid,skilllv)/5:skill_get_time(skillid,skilllv),0);
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ break;
+
+ case SL_SKE:
+ if (sd && bl->type != BL_MOB) {
+ status_change_start(src,SC_STAN,skilllv,0,0,0,3000,0);
+ clif_skill_fail(sd,skillid,0,0);
+ break;
+ }
+ status_change_start(bl,SkillStatusChangeTable[skillid],skilllv,skillid,0,0,skill_get_time(skillid,skilllv),0 );
+ status_change_start(src,SC_COMBO,SL_SMA,skilllv,0,0,skill_get_time2(skillid,skilllv),0);
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ break;
+
+ // New guild skills [Celest]
+ case GD_BATTLEORDER:
+ {
+ struct guild *g = NULL;
+ // Only usable during WoE
+ if (!agit_flag) {
+ clif_skill_fail(sd,skillid,0,0);
+ map_freeblock_unlock();
+ return 0;
+ }
+ if(flag&1) {
+ if (dstsd && dstsd->status.guild_id == sd->status.guild_id) {
+ status_change_start(&dstsd->bl,SC_BATTLEORDERS,skilllv,0,0,0,0,0 );
+ }
+ }
+ else if (sd && sd->status.guild_id > 0 && (g = guild_search(sd->status.guild_id)) &&
+ strcmp(sd->status.name,g->master)==0) {
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ map_foreachinarea(skill_area_sub,
+ src->m,src->x-15,src->y-15,src->x+15,src->y+15,BL_CHAR,
+ src,skillid,skilllv,tick, flag|BCT_GUILD|1,
+ skill_castend_nodamage_id);
+ guild_block_skill(sd,skill_get_time2(skillid,skilllv));
+ }
+ }
+ break;
+ case GD_REGENERATION:
+ {
+ struct guild *g = NULL;
+ // Only usable during WoE
+ if (!agit_flag) {
+ clif_skill_fail(sd,skillid,0,0);
+ map_freeblock_unlock();
+ return 0;
+ }
+ if(flag&1) {
+ if (dstsd && dstsd->status.guild_id == sd->status.guild_id) {
+ status_change_start(&dstsd->bl,SC_REGENERATION,skilllv,0,0,0,0,0 );
+ }
+ }
+ else if (sd && sd->status.guild_id > 0 && (g = guild_search(sd->status.guild_id)) &&
+ strcmp(sd->status.name,g->master)==0) {
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ map_foreachinarea(skill_area_sub,
+ src->m,src->x-15,src->y-15,src->x+15,src->y+15,BL_CHAR,
+ src,skillid,skilllv,tick, flag|BCT_GUILD|1,
+ skill_castend_nodamage_id);
+ guild_block_skill(sd,skill_get_time2(skillid,skilllv));
+ }
+ }
+ break;
+ case GD_RESTORE:
+ {
+ struct guild *g = NULL;
+ // Only usable during WoE
+ if (!agit_flag) {
+ clif_skill_fail(sd,skillid,0,0);
+ map_freeblock_unlock();
+ return 0;
+ }
+ if(flag&1) {
+ if (dstsd && dstsd->status.guild_id == sd->status.guild_id) {
+ int hp, sp;
+ hp = dstsd->status.max_hp*9/10;
+ sp = dstsd->status.max_sp*9/10;
+ sp = dstsd->status.sp + sp <= dstsd->status.max_sp ? sp : dstsd->status.max_sp - dstsd->status.sp;
+ clif_skill_nodamage(src,bl,AL_HEAL,hp,1);
+ battle_heal(NULL,bl,hp,sp,0);
+ }
+ }
+ else if (sd && sd->status.guild_id > 0 && (g = guild_search(sd->status.guild_id)) &&
+ strcmp(sd->status.name,g->master)==0) {
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ map_foreachinarea(skill_area_sub,
+ src->m,src->x-15,src->y-15,src->x+15,src->y+15,BL_CHAR,
+ src,skillid,skilllv,tick, flag|BCT_GUILD|1,
+ skill_castend_nodamage_id);
+ guild_block_skill(sd,skill_get_time2(skillid,skilllv));
+ }
+ }
+ break;
+ case GD_EMERGENCYCALL:
+ {
+ int dx[9]={-1, 1, 0, 0,-1, 1,-1, 1, 0};
+ int dy[9]={ 0, 0, 1,-1, 1,-1,-1, 1, 0};
+ int j = 0;
+ struct guild *g = NULL;
+ if (!sd || !sd->state.gmaster_flag)
+ break;
+ //Reports say this particular skill is usable anywhere! o.o [Skotlex]
+ //And now people say that's not true... MEH. Will they EVER make up their mind?
+ if (/*map[sd->bl.m].flag.nowarpto &&*/ !map_flag_gvg(sd->bl.m))
+ { //if not allowed to warp to the map (castles are always allowed)
+ clif_skill_fail(sd,skillid,0,0);
+ map_freeblock_unlock();
+ return 0;
+ }
+ // i don't know if it actually summons in a circle, but oh well. ;P
+ g = sd->state.gmaster_flag;
+ for(i = 0; i < g->max_member; i++, j++) {
+ if (j>8) j=0;
+ if ((dstsd = g->member[i].sd) != NULL && sd != dstsd) {
+ if (map[dstsd->bl.m].flag.nowarp && !map_flag_gvg(dstsd->bl.m))
+ continue;
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ if(map_getcell(sd->bl.m,sd->bl.x+dx[j],sd->bl.y+dy[j],CELL_CHKNOPASS))
+ dx[j] = dy[j] = 0;
+ pc_setpos(dstsd, sd->mapindex, sd->bl.x+dx[j], sd->bl.y+dy[j], 2);
+ }
+ }
+ guild_block_skill(sd,skill_get_time2(skillid,skilllv));
+ }
+ break;
+
+ case SG_FEEL:
+ if (sd) {
+ if(!sd->feel_map[skilllv-1].index) {
+ sd->feel_level=skilllv-1;
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ clif_parse_ReqFeel(sd->fd,sd);
+ }
+ else
+ clif_feel_info(sd, skilllv-1);
+ }
+ break;
+
+ case SG_HATE:
+ if (sd) {
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ if(dstsd) //PC
+ {
+ sd->hate_mob[skilllv-1] = dstsd->status.class_;
+ pc_setglobalreg(sd,"PC_HATE_MOB_STAR",sd->hate_mob[skilllv-1]+1);
+ clif_hate_mob(sd,skilllv,sd->hate_mob[skilllv-1]);
+ }
+ else if(dstmd) // mob
+ {
+ switch(skilllv)
+ {
+ case 1:
+ if (status_get_size(bl)==0)
+ {
+ sd->hate_mob[0] = dstmd->class_;
+ pc_setglobalreg(sd,"PC_HATE_MOB_SUN",sd->hate_mob[0]+1);
+ clif_hate_mob(sd,skilllv,sd->hate_mob[skilllv-1]);
+ } else clif_skill_fail(sd,skillid,0,0);
+ break;
+ case 2:
+ if (status_get_size(bl)==1 && status_get_max_hp(bl)>=6000)
+ {
+ sd->hate_mob[1] = dstmd->class_;
+ pc_setglobalreg(sd,"PC_HATE_MOB_MOON",sd->hate_mob[1]+1);
+ clif_hate_mob(sd,skilllv,sd->hate_mob[skilllv-1]);
+ } else clif_skill_fail(sd,skillid,0,0);
+ break;
+ case 3:
+ if (status_get_size(bl)==2 && status_get_max_hp(bl)>=20000)
+ {
+ sd->hate_mob[2] = dstmd->class_;
+ pc_setglobalreg(sd,"PC_HATE_MOB_STAR",sd->hate_mob[2]+1);
+ clif_hate_mob(sd,skilllv,sd->hate_mob[skilllv-1]);
+ } else clif_skill_fail(sd,skillid,0,0);
+ break;
+ default:
+ clif_skill_fail(sd,skillid,0,0);
+ break;
+ }
+ }
+ }
+ break;
+ case SG_SUN_WARM:
+ case SG_MOON_WARM:
+ case SG_STAR_WARM:
+ status_change_start(bl,SkillStatusChangeTable[skillid],skilllv,0,skillid,skill_get_range(skillid,skilllv),skill_get_time(skillid,skilllv),0);
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ break;
+ case SG_SUN_COMFORT:
+ case SG_MOON_COMFORT:
+ case SG_STAR_COMFORT:
+ status_change_start(bl,SkillStatusChangeTable[skillid],skilllv,0,0,0,skill_get_time(skillid,skilllv),0);
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ break;
+ case SG_FUSION:
+ if (sd && sd->sc_data && sd->sc_data[SC_FUSION].timer != -1)
+ status_change_end(&sd->bl,SkillStatusChangeTable[skillid],-1);
+ else
+ status_change_start(bl,SkillStatusChangeTable[skillid],skilllv,0,0,0,skill_get_time(skillid,skilllv),0);
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ break;
+
+ default:
+ ShowWarning("Unknown skill used:%d\n",skillid);
+ map_freeblock_unlock();
+ return 1;
+ }
+
+ map_freeblock_unlock();
+ return 0;
+}
+
+/*==========================================
+ * スキル使用?i詠?・完了?AID指定?j
+ *------------------------------------------
+ */
+int skill_castend_id( int tid, unsigned int tick, int id,int data )
+{
+ struct map_session_data* sd = map_id2sd(id)/*,*target_sd=NULL*/;
+ struct block_list *bl;
+ int delay,inf2;
+
+ nullpo_retr(0, sd);
+
+//Code cleanup.
+#undef skill_failed
+#define skill_failed(sd) { sd->skillid = sd->skilllv = sd->skillitem = sd->skillitemlv = -1; sd->canact_tick = sd->canmove_tick = tick; }
+
+ if(sd->skillid != SA_CASTCANCEL && sd->skilltimer != tid )
+ { /* タイマIDの確認 */
+ ShowError("skill_castend_id: Timer mismatch %d!=%d!\n", sd->skilltimer, tid);
+ sd->skilltimer = -1;
+ return 0;
+ }
+
+ if( sd->bl.prev == NULL || sd->skillid == -1 || sd->skilllv == -1)
+ { //Finished casting between maps, or the skill has failed after starting casting{
+ sd->skilltimer = -1;
+ skill_failed(sd);
+ return 0;
+ }
+
+ if(sd->skillid != SA_CASTCANCEL && sd->skilltimer != -1 && (delay = pc_checkskill(sd,SA_FREECAST) > 0)) //Hope ya don't mind me borrowing delay :X
+ status_quick_recalc_speed(sd, SA_FREECAST, delay, 0);
+
+ if(sd->skillid != SA_CASTCANCEL)
+ sd->skilltimer=-1;
+
+ if((bl=map_id2bl(sd->skilltarget))==NULL ||
+ bl->prev==NULL || sd->bl.m != bl->m) {
+ skill_failed(sd);
+ return 0;
+ }
+
+ if(sd->skillid == RG_BACKSTAP) {
+ int dir = map_calc_dir(&sd->bl,bl->x,bl->y),t_dir = status_get_dir(bl);
+ if(bl->type != BL_SKILL && (check_distance_bl(&sd->bl, bl, 0) || map_check_dir(dir,t_dir))) {
+ clif_skill_fail(sd,sd->skillid,0,0);
+ skill_failed(sd);
+ return 0;
+ }
+ }
+
+ if (sd->skillid == PR_LEXDIVINA)
+ {
+ struct status_change *sc_data = status_get_sc_data(bl);
+ if (battle_check_target(&sd->bl,bl, BCT_ENEMY)<=0 &&
+ (!sc_data || sc_data[SC_SILENCE].timer == -1)) //If it's not an enemy, and not silenced, you can't use the skill on them. [Skotlex]
+ {
+ clif_skill_nodamage (&sd->bl, bl, sd->skillid, sd->skilllv, 0);
+ skill_failed(sd);
+ return 0;
+ }
+ } else {
+ inf2 = skill_get_inf(sd->skillid);
+ if((inf2&INF_ATTACK_SKILL ||
+ (inf2&INF_SELF_SKILL && sd->bl.id != bl->id && skill_get_nk(sd->skillid) != NK_NO_DAMAGE)) //Self skills that cause damage (EF, Combo Skills, etc)
+ && battle_check_target(&sd->bl,bl, BCT_ENEMY)<=0
+ ) {
+ skill_failed(sd);
+ return 0;
+ }
+ }
+ if (tid != -1 && !status_check_skilluse(&sd->bl, bl, sd->skillid, 1))
+ { //Avoid doing double checks for instant-cast skills.
+ if(sd->skillid == PR_LEXAETERNA) //Eh.. assuming skill failed due to opponent frozen/stone-cursed. [Skotlex]
+ clif_skill_fail(sd,sd->skillid,0,0);
+ skill_failed(sd);
+ return 0;
+ }
+
+ inf2 = skill_get_inf2(sd->skillid);
+ if(inf2 & (INF2_PARTY_ONLY|INF2_GUILD_ONLY) && sd->bl.id != bl->id) {
+ int fail_flag = 1;
+ if(inf2 & INF2_PARTY_ONLY && battle_check_target(&sd->bl,bl, BCT_PARTY) > 0)
+ fail_flag = 0;
+ else if(inf2 & INF2_GUILD_ONLY && battle_check_target(&sd->bl,bl, BCT_GUILD) > 0)
+ fail_flag = 0;
+
+ if (sd->skillid == PF_SOULCHANGE && map_flag_vs(sd->bl.m))
+ //Soul Change overrides this restriction during pvp/gvg [Skotlex]
+ fail_flag = 0;
+
+ if(fail_flag) {
+ clif_skill_fail(sd,sd->skillid,0,0);
+ skill_failed(sd);
+ return 0;
+ }
+ }
+
+ if(!check_distance_bl(&sd->bl, bl, skill_get_range2(&sd->bl,sd->skillid,sd->skilllv)+battle_config.pc_skill_add_range))
+ {
+ clif_skill_fail(sd,sd->skillid,0,0);
+ if(battle_config.skill_out_range_consume) //Consume items anyway. [Skotlex]
+ skill_check_condition(sd,1);
+
+ skill_failed(sd);
+ return 0;
+ }
+
+ if(!skill_check_condition(sd,1)) { /* 使用??チェック */
+ skill_failed(sd);
+ return 0;
+ }
+
+ if(battle_config.pc_skill_log)
+ ShowInfo("PC %d skill castend skill=%d\n",sd->bl.id,sd->skillid);
+ pc_stop_walking(sd,0);
+
+ if (sd->skillid == SA_MAGICROD)
+ delay = 0;
+ else
+ delay = skill_delayfix(&sd->bl, sd->skillid, sd->skilllv, 0);
+
+ sd->canact_tick = tick + delay;
+ if (skill_get_delaynowalk(sd->skillid, sd->skilllv)) //Skills that block you from moving until delay ends. [Skotlex]
+ sd->canmove_tick = tick + delay;
+ switch( skill_get_nk(sd->skillid) )
+ {
+ case NK_NO_DAMAGE:
+ skill_castend_nodamage_id(&sd->bl,bl,sd->skillid,sd->skilllv,tick,0);
+ break;
+ case NK_SPLASH_DAMAGE:
+ default:
+ skill_castend_damage_id(&sd->bl,bl,sd->skillid,sd->skilllv,tick,0);
+ break;
+ }
+
+ if(sd->sc_data[SC_MAGICPOWER].timer != -1 && sd->skillid != HW_MAGICPOWER)
+ status_change_end(&sd->bl,SC_MAGICPOWER,-1);
+
+ if (sd->skillid != AL_TELEPORT && sd->skillid != WS_WEAPONREFINE) {
+ sd->skillid = sd->skilllv = -1; //Clean this up for future references to battle_getcurrentskill. [Skotlex]
+ sd->skilltarget = 0;
+ }
+ return 0;
+}
+
+/*---------------------------------------------------------------------------- */
+
+/*==========================================
+ * スキル使用?i詠?・完了?A??且w定?j
+ *------------------------------------------
+ */
+int skill_castend_pos( int tid, unsigned int tick, int id,int data )
+{
+ struct map_session_data* sd=map_id2sd(id)/*,*target_sd=NULL*/;
+ int delay,maxcount;
+
+ nullpo_retr(0, sd);
+
+//Code cleanup.
+#undef skill_failed
+#define skill_failed(sd) { sd->skillid = sd->skilllv = sd->skillitem = sd->skillitemlv = -1; sd->canact_tick = sd->canmove_tick = tick; }
+
+ if( sd->skilltimer != tid )
+ { /* タイマIDの確認 */
+ ShowError("skill_castend_pos: Timer mismatch %d!=%d\n", sd->skilltimer, tid);
+ sd->skilltimer = -1;
+ return 0;
+ }
+
+ if(sd->skillid != SA_CASTCANCEL && sd->skilltimer != -1 && (delay = pc_checkskill(sd,SA_FREECAST) > 0)) //Hope ya don't mind me borrowing delay :X
+ status_quick_recalc_speed(sd, SA_FREECAST, delay, 0);
+
+ sd->skilltimer=-1;
+ if (sd->bl.prev == NULL || sd->skillid == -1 || sd->skilllv <= 0)
+ { // skill has failed after starting casting
+ return 0;
+ }
+
+ if (!battle_config.pc_skill_reiteration &&
+ skill_get_unit_flag(sd->skillid)&UF_NOREITERATION &&
+ skill_check_unit_range(sd->bl.m,sd->skillx,sd->skilly,sd->skillid,sd->skilllv)) {
+ clif_skill_fail(sd,sd->skillid,0,0);
+ skill_failed(sd);
+ return 0;
+ }
+
+ if (battle_config.pc_skill_nofootset &&
+ skill_get_unit_flag(sd->skillid)&UF_NOFOOTSET &&
+ skill_check_unit_range2(&sd->bl,sd->bl.m,sd->skillx,sd->skilly,sd->skillid,sd->skilllv)) {
+ clif_skill_fail(sd,sd->skillid,0,0);
+ skill_failed(sd);
+ return 0;
+ }
+ if(battle_config.pc_land_skill_limit) {
+ maxcount = skill_get_maxcount(sd->skillid);
+ if(maxcount > 0) {
+ int i,c;
+ for(i=c=0;i<MAX_SKILLUNITGROUP;i++) {
+ if(sd->skillunit[i].alive_count > 0 && sd->skillunit[i].skill_id == sd->skillid)
+ c++;
+ }
+ if(c >= maxcount) {
+ clif_skill_fail(sd,sd->skillid,0,0);
+ skill_failed(sd);
+ return 0;
+ }
+ }
+ }
+
+ if(tid != -1)
+ { //Avoid double checks on instant cast skills. [Skotlex]
+ if (!status_check_skilluse(&sd->bl, NULL, sd->skillid, 1))
+ {
+ skill_failed(sd);
+ return 0;
+ }
+ if(!check_distance_blxy(&sd->bl, sd->skillx, sd->skilly, skill_get_range2(&sd->bl,sd->skillid,sd->skilllv)+battle_config.pc_skill_add_range)) {
+ clif_skill_fail(sd,sd->skillid,0,0);
+ if(battle_config.skill_out_range_consume) //Consume items anyway.
+ skill_check_condition(sd,1);
+
+ skill_failed(sd);
+ return 0;
+ }
+ }
+
+ if(!skill_check_condition(sd,1)) { /* 使用??チェック */
+ skill_failed(sd);
+ return 0;
+ }
+ if(battle_config.pc_skill_log)
+ ShowInfo("PC %d skill castend skill=%d\n",sd->bl.id,sd->skillid);
+ pc_stop_walking(sd,0);
+
+ delay = skill_delayfix(&sd->bl, sd->skillid, sd->skilllv, 0);
+ sd->canact_tick = tick + delay;
+ if (skill_get_delaynowalk(sd->skillid, sd->skilllv)) //Skills that block you from moving until delay ends. [Skotlex]
+ sd->canmove_tick = tick + delay;
+
+ skill_castend_pos2(&sd->bl,sd->skillx,sd->skilly,sd->skillid,sd->skilllv,tick,0);
+
+ if (sd->skillid != AL_WARP)
+ sd->skillid = sd->skilllv = -1; //Clean this up for future references to battle_getcurrentskill. [Skotlex]
+ return 0;
+}
+
+/*==========================================
+ * スキル使用?i詠?・完了?A??且w定の??ロの???j
+ *------------------------------------------
+ */
+int skill_castend_pos2( struct block_list *src, int x,int y,int skillid,int skilllv,unsigned int tick,int flag)
+{
+ struct map_session_data *sd=NULL;
+ struct status_change *sc_data;
+ int i,tmpx = 0,tmpy = 0, x1 = 0, y1 = 0;
+
+ //if(skilllv <= 0) return 0;
+ if(skillid > 0 && skilllv <= 0) return 0; // celest
+
+ nullpo_retr(0, src);
+
+ if(src->type==BL_PC)
+ sd=(struct map_session_data *)src;
+
+ sc_data = status_get_sc_data(src); //Needed for Magic Power checks.
+
+ if( skillid != WZ_METEOR &&
+ skillid != AM_CANNIBALIZE &&
+ skillid != AM_SPHEREMINE &&
+ skillid != CR_CULTIVATION &&
+ skillid != AC_SHOWER)
+ clif_skill_poseffect(src,skillid,skilllv,x,y,tick);
+
+//Shouldn't be needed, skillnotok's return value is highly unlikely to have changed after you started casting. [Skotlex]
+// if (sd && skillnotok(skillid, sd)) // [MouseJstr]
+// return 0;
+
+ switch(skillid)
+ {
+ case PR_BENEDICTIO: /* ?ケ??~福 */
+ skill_area_temp[1] = src->id;
+ map_foreachinarea(skill_area_sub,
+ src->m, x-1, y-1, x+1, y+1, BL_PC,
+ src, skillid, skilllv, tick, flag|BCT_ALL|1,
+ skill_castend_nodamage_id);
+ map_foreachinarea(skill_area_sub,
+ src->m, x-1, y-1, x+1, y+1, BL_CHAR,
+ src, skillid, skilllv, tick, flag|BCT_ENEMY|1,
+ skill_castend_damage_id);
+ break;
+
+ case AC_SHOWER:
+ { //One of the few skills that can attack traps.
+ int r = 2;
+ map_foreachinarea (skill_area_sub,
+ src->m, x-r, y-r, x+r, y+r, BL_CHAR|BL_SKILL,
+ src, skillid, skilllv, tick, flag|BCT_ENEMY|1,
+ skill_castend_damage_id);
+ }
+ break;
+
+ case BS_HAMMERFALL:
+ {
+ int r = 2;
+ if (skilllv > 5) {
+ r = 14;
+ skilllv = 5; // スタン率?繧ェりすぎるため計算はLv5で固定
+ }
+ skill_area_temp[1] = src->id;
+ skill_area_temp[2] = x;
+ skill_area_temp[3] = y;
+ map_foreachinarea (skill_area_sub,
+ src->m, x-r, y-r, x+r, y+r, BL_CHAR,
+ src, skillid, skilllv, tick, flag|BCT_ENEMY|2,
+ skill_castend_nodamage_id);
+ }
+ break;
+
+ case HT_DETECTING: /* ディテクティング */
+ map_foreachinarea( status_change_timer_sub,
+ src->m, x-1, y-1, x+1,y+1,BL_CHAR,
+ src,SC_SIGHT,tick);
+ break;
+
+ case MG_SAFETYWALL: /* セイフティウォ?ル */
+ case MG_FIREWALL: /* ファイヤ?ウォ?ル */
+ case MG_THUNDERSTORM: /* サンダ?スト?ム */
+ case AL_PNEUMA: /* ニュ?マ */
+ case WZ_ICEWALL: /* アイスウォ?ル */
+ case WZ_FIREPILLAR: /* ファイアピラ? */
+ case WZ_QUAGMIRE: /* クァグマイア */
+ case WZ_VERMILION: /* ??ドオブヴァ?ミリオン */
+ case WZ_STORMGUST: /* スト?ムガスト */
+ case WZ_HEAVENDRIVE: /* ヘヴンズドライブ */
+ case PR_SANCTUARY: /* サンクチュアリ */
+ case PR_MAGNUS: /* マグヌスエクソシズム */
+ case CR_GRANDCROSS: /* グランドク?ス */
+ case NPC_GRANDDARKNESS: /*闇グランドク?ス*/
+ case HT_SKIDTRAP: /* スキッドトラップ */
+ case HT_LANDMINE: /* ランドマイン */
+ case HT_ANKLESNARE: /* アンクルスネア */
+ case HT_SHOCKWAVE: /* ショックウェ?ブトラップ */
+ case HT_SANDMAN: /* サンドマン */
+ case HT_FLASHER: /* フラッシャ? */
+ case HT_FREEZINGTRAP: /* フリ?ジングトラップ */
+ case HT_BLASTMINE: /* ブラストマイン */
+ case HT_CLAYMORETRAP: /* クレイモア?トラップ */
+ case AS_VENOMDUST: /* ベノムダスト */
+ case AM_DEMONSTRATION: /* デモンストレ?ション */
+ case PF_FOGWALL: /* フォグウォ?ル */
+ case PF_SPIDERWEB: /* スパイダ?ウェッブ */
+ case HT_TALKIEBOX: /* ト?キ?ボックス */
+ skill_unitsetting(src,skillid,skilllv,x,y,0);
+ break;
+
+ case RG_GRAFFITI: /* Graffiti [Valaris] */
+ skill_clear_unitgroup(src);
+ skill_unitsetting(src,skillid,skilllv,x,y,0);
+ break;
+
+ case RG_CLEANER: // [Valaris]
+ map_foreachinarea(skill_graffitiremover,src->m,x-5,y-5,x+5,y+5,BL_SKILL);
+ break;
+ case SA_VOLCANO: /* ボルケ?ノ */
+ case SA_DELUGE: /* デリュ?ジ */
+ case SA_VIOLENTGALE: /* バイオレントゲイル */
+ case SA_LANDPROTECTOR: /* ランドプ?テクタ? */
+ skill_unitsetting(src,skillid,skilllv,x,y,0);
+ break;
+
+ case WZ_METEOR: //?テオスト?ム
+ {
+ int flag=0;
+ if (sc_data && sc_data[SC_MAGICPOWER].timer != -1)
+ flag = flag|2; //Store the magic power flag for future use. [Skotlex]
+ for(i=0;i<2+(skilllv>>1);i++) {
+ int j=0;
+ do {
+ tmpx = x + (rand()%7 - 3);
+ tmpy = y + (rand()%7 - 3);
+ if(tmpx < 0)
+ tmpx = 0;
+ else if(tmpx >= map[src->m].xs)
+ tmpx = map[src->m].xs - 1;
+ if(tmpy < 0)
+ tmpy = 0;
+ else if(tmpy >= map[src->m].ys)
+ tmpy = map[src->m].ys - 1;
+ j++;
+ } while((map_getcell(src->m,tmpx,tmpy,CELL_CHKNOPASS)) && j<100);
+ if(j >= 100)
+ continue;
+ if(!(flag&1)){
+ clif_skill_poseffect(src,skillid,skilllv,tmpx,tmpy,tick);
+ flag=flag|1;
+ }
+ if(i > 0)
+ skill_addtimerskill(src,tick+i*1000,0,tmpx,tmpy,skillid,skilllv,(x1<<16)|y1,flag&2); //Only pass the Magic Power flag
+ x1 = tmpx;
+ y1 = tmpy;
+ }
+ skill_addtimerskill(src,tick+i*1000,0,tmpx,tmpy,skillid,skilllv,-1,flag&2); //Only pass the Magic Power flag
+ }
+ break;
+
+ case AL_WARP: /* ??プポ?タル */
+ if(sd) {
+ clif_skill_warppoint(sd,skillid,mapindex_id2name(sd->status.save_point.map),
+ (sd->skilllv>1)?mapindex_id2name(sd->status.memo_point[0].map):"",
+ (sd->skilllv>2)?mapindex_id2name(sd->status.memo_point[1].map):"",
+ (sd->skilllv>3)?mapindex_id2name(sd->status.memo_point[2].map):"");
+ }
+ break;
+
+ case MO_BODYRELOCATION:
+ if (sd) {
+ pc_movepos(sd, x, y, 1);
+ pc_blockskill_start (sd, MO_EXTREMITYFIST, 2000);
+ } else if (src->type == BL_MOB) {
+ struct mob_data *md = (struct mob_data *)src;
+ mob_warp(md, -1, x, y, 0);
+ clif_spawnmob(md);
+ }
+ break;
+ case AM_CANNIBALIZE: // バイオプラント
+ if(sd) {
+ int id;
+ int summons[5] = { 1020, 1068, 1118, 1500, 1368 };
+ struct mob_data *md;
+
+ // Correct info, don't change any of this! [celest]
+ id = mob_once_spawn (sd, "this", x, y, sd->status.name, summons[skilllv-1] ,1,"");
+
+ if( (md=(struct mob_data *)map_id2bl(id)) !=NULL ){
+ md->master_id = sd->bl.id;
+ // different levels of HP according to skill level
+ md->hp = 1500 + skilllv * 200 + sd->status.base_level * 10;
+ md->max_hp = md->hp; //Update the max, too! [Skotlex]
+ md->special_state.ai = 1;
+ //非移動でアクティブで反撃する[0x0:非移動 0x1:移動 0x4:ACT 0x8:非ACT 0x40:反撃無 0x80:反撃有]
+ md->mode = MD_CANATTACK|MD_AGGRESSIVE;
+ md->deletetimer = add_timer (gettick() + skill_get_time(skillid,skilllv), mob_timer_delete, id, 0);
+ }
+ // To-do: ?「還されるモンスタ?[には?「還したプレ?[ヤ?[の名前が付きます
+ // (attach name of player?)
+ clif_skill_poseffect(src,skillid,skilllv,x,y,tick);
+ }
+ break;
+ case AM_SPHEREMINE: // スフィア?マイン
+ if(sd){
+ int id;
+ struct mob_data *md;
+
+ id = mob_once_spawn(sd, "this", x, y, sd->status.name, 1142, 1, "");
+ if( (md=(struct mob_data *)map_id2bl(id)) !=NULL ){
+ md->master_id = sd->bl.id;
+ md->hp = 2000 + skilllv * 400;
+ md->max_hp = md->hp; //Update the max, too! [Skotlex]
+ md->mode = md->db->mode|MD_CANMOVE; //Needed for the skill
+ md->special_state.ai = 2;
+ md->deletetimer = add_timer (gettick() + skill_get_time(skillid,skilllv), mob_timer_delete, id, 0);
+ }
+ clif_skill_poseffect(src,skillid,skilllv,x,y,tick);
+ }
+ break;
+
+ // Slim Pitcher [Celest]
+ case CR_SLIMPITCHER:
+ {
+ if (sd) {
+ int i = skilllv%11 - 1;
+ int j = pc_search_inventory(sd,skill_db[skillid].itemid[i]);
+ if(j < 0 || skill_db[skillid].itemid[i] <= 0 || sd->inventory_data[j] == NULL ||
+ sd->status.inventory[j].amount < skill_db[skillid].amount[i]) {
+ clif_skill_fail(sd,skillid,0,0);
+ return 1;
+ }
+ potion_flag = 1;
+ potion_hp = 0;
+ run_script(sd->inventory_data[j]->script,0,sd->bl.id,0);
+ pc_delitem(sd,j,skill_db[skillid].amount[i],0);
+ potion_flag = 0;
+ clif_skill_poseffect(src,skillid,skilllv,x,y,tick);
+ if(potion_hp > 0) {
+ map_foreachinarea(skill_area_sub,
+ src->m,x-3,y-3,x+3,y+3,BL_CHAR,
+ src,skillid,skilllv,tick,flag|BCT_PARTY|BCT_GUILD|1,
+ skill_castend_nodamage_id);
+ }
+ }
+ }
+ break;
+
+ case HW_GANBANTEIN:
+ if (rand()%100 < 80) {
+ clif_skill_poseffect(src,skillid,skilllv,x,y,tick);
+ map_foreachinarea (skill_ganbatein, src->m, x-1, y-1, x+1, y+1, BL_SKILL);
+ } else {
+ clif_skill_fail(sd,skillid,0,0);
+ return 1;
+ }
+ break;
+
+ case HW_GRAVITATION:
+ {
+ struct skill_unit_group *sg;
+ clif_skill_poseffect(src,skillid,skilllv,x,y,tick);
+ sg = skill_unitsetting(src,skillid,skilllv,x,y,0);
+ status_change_start(src,SkillStatusChangeTable[skillid],skilllv,0,BCT_SELF,(int)sg,
+ skill_get_time(skillid,skilllv),0);
+ }
+ break;
+
+ // Plant Cultivation [Celest]
+ case CR_CULTIVATION:
+ {
+ if (sd) {
+ int i = skilllv - 1;
+ int j = pc_search_inventory(sd,skill_db[skillid].itemid[i]);
+ if(j < 0 || skill_db[skillid].itemid[i] <= 0 || sd->inventory_data[j] == NULL ||
+ sd->status.inventory[j].amount < skill_db[skillid].amount[i]) {
+ clif_skill_fail(sd,skillid,0,0);
+ return 1;
+ }
+ pc_delitem(sd,j,skill_db[skillid].amount[i],0);
+ clif_skill_poseffect(src,skillid,skilllv,x,y,tick);
+ if (rand()%100 < 50)
+ mob_once_spawn(sd, "this", x, y, "--ja--",(skilllv < 2 ? 1084+rand()%2 : 1078+rand()%6), 1, "");
+ else
+ clif_skill_fail(sd,skillid,0,0);
+ }
+ }
+ break;
+ }
+
+ if (sc_data && sc_data[SC_MAGICPOWER].timer != -1)
+ status_change_end(&sd->bl,SC_MAGICPOWER,-1);
+
+ return 0;
+}
+
+/*==========================================
+ * スキル使用?i詠?・完了?Amap指定?j
+ *------------------------------------------
+ */
+int skill_castend_map( struct map_session_data *sd,int skill_num, const char *map)
+{
+ int x=0,y=0;
+
+ nullpo_retr(0, sd);
+
+//Simplify skill_failed code.
+#undef skill_failed
+#define skill_failed(sd) { sd->skillid = sd->skilllv = sd->skillitem = sd->skillitemlv = -1; }
+
+ if( sd->bl.prev == NULL || pc_isdead(sd) )
+ return 0;
+
+//Shouldn't be needed, skillnotok's return value is highly unlikely to have changed after you started casting. [Skotlex]
+// if(skillnotok(skill_num, sd))
+// return 0;
+
+ if( sd->opt1>0 || sd->status.option&2 ) {
+ skill_failed(sd);
+ return 0;
+ }
+ //スキルが使えない?態異?中
+ if(sd->sc_count){
+ if( sd->sc_data[SC_SILENCE].timer!=-1 ||
+ sd->sc_data[SC_ROKISWEIL].timer!=-1 ||
+ sd->sc_data[SC_AUTOCOUNTER].timer != -1 ||
+ sd->sc_data[SC_STEELBODY].timer != -1 ||
+ sd->sc_data[SC_DANCING].timer!=-1 ||
+ sd->sc_data[SC_BERSERK].timer != -1 ||
+ sd->sc_data[SC_MARIONETTE].timer != -1)
+ return 0;
+ }
+
+ if( skill_num != sd->skillid) /* 不?ウパケットらしい */
+ return 0;
+
+ if (strlen(map) > MAP_NAME_LENGTH-1)
+ { //Map_length check, as it is sent by the client and we shouldn't trust it [Skotlex]
+ if (battle_config.error_log)
+ ShowError("skill_castend_map: Received map name '%s' too long!\n", map);
+ skill_failed(sd);
+ return 0;
+ }
+
+ pc_stopattack(sd);
+
+ if(battle_config.pc_skill_log)
+ ShowInfo("PC %d skill castend skill =%d map=%s\n",sd->bl.id,skill_num,map);
+ pc_stop_walking(sd,0);
+
+ if(strcmp(map,"cancel")==0) {
+ skill_failed(sd);
+ return 0;
+ }
+
+ switch(skill_num){
+ case AL_TELEPORT: /* テレポ?ト */
+ if(strcmp(map,"Random")==0)
+ pc_randomwarp(sd,3);
+ else
+ pc_setpos(sd,sd->status.save_point.map,
+ sd->status.save_point.x,sd->status.save_point.y,3);
+ break;
+
+ case AL_WARP: /* ??プポ?タル */
+ {
+ const struct point *p[4];
+ struct skill_unit_group *group;
+ int i;
+ int maxcount=0;
+ unsigned short mapindex;
+ mapindex = mapindex_name2id((char*)map);
+ if(!mapindex) { //Given map not found?
+ clif_skill_fail(sd,skill_num,0,0);
+ skill_failed(sd);
+ return 0;
+ }
+ p[0] = &sd->status.save_point;
+ p[1] = &sd->status.memo_point[0];
+ p[2] = &sd->status.memo_point[1];
+ p[3] = &sd->status.memo_point[2];
+
+ if((maxcount = skill_get_maxcount(skill_num)) > 0) {
+ int c;
+ for(i=c=0;i<MAX_SKILLUNITGROUP;i++) {
+ if(sd->skillunit[i].alive_count > 0 && sd->skillunit[i].skill_id == skill_num)
+ c++;
+ }
+ if(c >= maxcount) {
+ clif_skill_fail(sd,skill_num,0,0);
+ skill_failed(sd);
+ return 0;
+ }
+ }
+
+ if(sd->skilllv <= 0) return 0;
+ for(i=0;i<sd->skilllv;i++){
+ if(mapindex == p[i]->map){
+ x=p[i]->x;
+ y=p[i]->y;
+ break;
+ }
+ }
+ if(x==0 || y==0) { /* 不?ウパケット?H */
+ skill_failed(sd);
+ return 0;
+ }
+
+ if(!skill_check_condition(sd,3))
+ {
+ skill_failed(sd);
+ return 0;
+ }
+
+ if(skill_check_unit_range2(&sd->bl,sd->bl.m,sd->skillx,sd->skilly,skill_num,sd->skilllv) > 0) {
+ clif_skill_fail(sd,0,0,0);
+ skill_failed(sd);
+ return 0;
+ }
+ if((group=skill_unitsetting(&sd->bl,skill_num,sd->skilllv,sd->skillx,sd->skilly,0))==NULL) {
+ skill_failed(sd);
+ return 0;
+ }
+ //Now that there's a mapindex, use that in val3 rather than a string. [Skotlex]
+ group->val3 = mapindex;
+// group->valstr=(char *)aCallocA(MAP_NAME_LENGTH,sizeof(char));
+// memcpy(group->valstr,map,MAP_NAME_LENGTH-1);
+ group->val2=(x<<16)|y;
+ }
+ break;
+ }
+
+ sd->skillid = sd->skilllv = -1;
+ return 0;
+}
+
+/*==========================================
+ * Initializes and sets a ground skill.
+ * flag&1 is used to determine when the skill 'morphs' (Warp portal becomes active, or Fire Pillar becomes active)
+ * flag&2 is used to determine if this skill was casted with Magic Power active.
+ *------------------------------------------
+ */
+struct skill_unit_group *skill_unitsetting( struct block_list *src, int skillid,int skilllv,int x,int y,int flag)
+{
+ struct skill_unit_group *group;
+ int i,limit,val1=0,val2=0,val3=0;
+ int count=0;
+ int target,interval,range,unit_flag;
+ struct skill_unit_layout *layout;
+ struct status_change *sc_data;
+ int active_flag=1;
+
+ nullpo_retr(0, src);
+
+ limit = skill_get_time(skillid,skilllv);
+ range = skill_get_unit_range(skillid);
+ interval = skill_get_unit_interval(skillid);
+ target = skill_get_unit_target(skillid);
+ unit_flag = skill_get_unit_flag(skillid);
+ layout = skill_get_unit_layout(skillid,skilllv,src,x,y);
+
+ sc_data = status_get_sc_data(src); // for traps, firewall and fogwall - celest
+
+ switch(skillid){ /* ?ン定 */
+
+ case MG_SAFETYWALL: /* セイフティウォ?ル */
+ val2=skilllv+1;
+ break;
+ case MG_FIREWALL: /* ファイヤ?ウォ?ル */
+ if(sc_data && sc_data[SC_VIOLENTGALE].timer!=-1)
+ limit = limit*3/2;
+ val2=4+skilllv;
+ break;
+
+ case AL_WARP: /* ??プポ?タル */
+ val1=skilllv+6;
+ if(!(flag&1))
+ limit=2000;
+ active_flag=0;
+ break;
+
+ case PR_SANCTUARY: /* サンクチュアリ */
+ val1=(skilllv+3)*2;
+ val2=(skilllv>6)?777:skilllv*100;
+ break;
+
+ case WZ_FIREPILLAR: /* ファイア?ピラ? */
+ if((flag&1)!=0)
+ limit=1000;
+ val1=skilllv+2;
+ if(skilllv >= 6)
+ range=2;
+ break;
+ case WZ_METEOR:
+ if (skilllv > 10) //?L範囲?テオ
+ range = 10;
+ break;
+ case WZ_VERMILION:
+ if (skilllv > 10) //?L範囲LOV
+ range = 25;
+ break;
+ case WZ_QUAGMIRE: //The target changes to "all" if used in a gvg map. [Skotlex]
+ case AM_DEMONSTRATION:
+ if (map_flag_vs(src->m) && battle_config.vs_traps_bctall)
+ target = BCT_ALL;
+ break;
+ case HT_SHOCKWAVE: /* ショックウェ?ブトラップ */
+ val1=skilllv*15+10;
+ case HT_SANDMAN: /* サンドマン */
+ case HT_CLAYMORETRAP: /* クレイモア?トラップ */
+ case HT_SKIDTRAP: /* スキッドトラップ */
+ case HT_LANDMINE: /* ランドマイン */
+ case HT_ANKLESNARE: /* アンクルスネア */
+ case HT_FLASHER: /* フラッシャ? */
+ case HT_FREEZINGTRAP: /* フリ?ジングトラップ */
+ case HT_BLASTMINE: /* ブラストマイン */
+ if (sc_data && sc_data[SC_INTOABYSS].timer != -1)
+ val3 = BD_INTOABYSS; //Store into abyss state, to know it shouldn't give traps back. [Skotlex]
+ if (map_flag_gvg(src->m))
+ limit *= 4; // longer trap times in WOE [celest]
+ if (battle_config.vs_traps_bctall && map_flag_vs(src->m))
+ target = BCT_ALL; //Change target to all [Skotlex]
+ break;
+
+ case SA_LANDPROTECTOR: /* グランドク?ス */
+ {
+ int aoe_diameter; // -- aoe_diameter (moonsoul) added for sage Area Of Effect skills
+ val1=skilllv*15+10;
+ aoe_diameter=skilllv+skilllv%2+5;
+ count=aoe_diameter*aoe_diameter; // -- this will not function if changed to ^2 (moonsoul)
+ }
+ //No break because we also have to check if we use gemstones. [Skotlex]
+ case SA_VOLCANO:
+ case SA_DELUGE:
+ case SA_VIOLENTGALE:
+ {
+ struct skill_unit_group *old_sg;
+ if ((old_sg = skill_locate_element_field(src)) != NULL)
+ {
+ if (old_sg->skill_id == skillid && old_sg->limit > 0)
+ { //Use the previous limit (minus the elapsed time) [Skotlex]
+ limit = old_sg->limit - DIFF_TICK(gettick(), old_sg->tick);
+ if (limit < 0) //This can happen...
+ limit = skill_get_time(skillid,skilllv);
+ }
+ skill_clear_element_field(src);
+ }
+ break;
+ }
+
+ case BA_DISSONANCE:
+ case DC_UGLYDANCE:
+ val1 = 10; //FIXME: This value is not used anywhere, what is it for? [Skotlex]
+ break;
+ case BA_WHISTLE:
+ val1 = skilllv+(status_get_agi(src)/10); // Flee increase
+ val2 = ((skilllv+1)/2)+(status_get_luk(src)/10); // Perfect dodge increase
+ if(src->type == BL_PC){
+ val1 += pc_checkskill((struct map_session_data *)src,BA_MUSICALLESSON);
+ val2 += pc_checkskill((struct map_session_data *)src,BA_MUSICALLESSON);
+ }
+ break;
+ case DC_HUMMING:
+ val1 = 2*skilllv+(status_get_dex(src)/10); // Hit increase
+ if(src->type == BL_PC)
+ val1 += 2*pc_checkskill((struct map_session_data *)src,DC_DANCINGLESSON);
+ break;
+ case BA_POEMBRAGI:
+ val1 = 3*skilllv+(status_get_dex(src)/10); // Casting time reduction
+ val2 = 3*skilllv+(status_get_int(src)/10); // After-cast delay reduction
+ if(src->type == BL_PC){
+ val1 += pc_checkskill((struct map_session_data *)src,BA_MUSICALLESSON);
+ val2 += pc_checkskill((struct map_session_data *)src,BA_MUSICALLESSON);
+ }
+ break;
+ case DC_DONTFORGETME:
+ val1 = 3*skilllv+(status_get_dex(src)/10); // ASPD decrease
+ val2 = 2*skilllv+(status_get_agi(src)/10); // Movement speed decrease
+ if(src->type == BL_PC){
+ val1 += pc_checkskill((struct map_session_data *)src,DC_DANCINGLESSON);
+ val2 += pc_checkskill((struct map_session_data *)src,DC_DANCINGLESSON);
+ }
+ break;
+ case BA_APPLEIDUN:
+ val1 = 5+2*skilllv+(status_get_vit(src)/10); // MaxHP percent increase
+ val2 = 30+5*skilllv+5*(status_get_vit(src)/10); // HP recovery
+ if(src->type == BL_PC){
+ val1 += pc_checkskill((struct map_session_data *)src,BA_MUSICALLESSON);
+ val2 += 5*pc_checkskill((struct map_session_data *)src,BA_MUSICALLESSON);
+ }
+ break;
+ case DC_SERVICEFORYOU:
+ val1 = 10+skilllv+(status_get_int(src)/10); // MaxSP percent increase
+ val2 = 10+3*skilllv+(status_get_int(src)/10); // SP cost reduction
+ if(src->type == BL_PC){
+ val1 += pc_checkskill((struct map_session_data *)src,DC_DANCINGLESSON);
+ val2 += pc_checkskill((struct map_session_data *)src,DC_DANCINGLESSON);
+ }
+ break;
+ case BA_ASSASSINCROSS:
+ val1 = 10+skilllv+(status_get_agi(src)/10); // ASPD increase
+ if(src->type == BL_PC)
+ val1 += pc_checkskill((struct map_session_data *)src,BA_MUSICALLESSON);
+ break;
+ case DC_FORTUNEKISS:
+ val1 = 10+skilllv+(status_get_luk(src)/10); // Critical increase
+ if(src->type == BL_PC)
+ val1 += pc_checkskill((struct map_session_data *)src,DC_DANCINGLESSON);
+ break;
+ case BD_LULLABY:
+ val1 = 11; //FIXME: This value is not used anywhere, what is it for? [Skotlex]
+ break;
+ case BD_DRUMBATTLEFIELD:
+ val1 = (skilllv+1)*25; //Watk increase
+ val2 = (skilllv+1)*2; //Def increase
+ break;
+ case BD_RINGNIBELUNGEN:
+ val1 = (skilllv+2)*25; //Watk increase
+ break;
+ case BD_SIEGFRIED:
+ val1 = 55 + skilllv*5; //Elemental Resistance
+ val2 = skilllv*10; //Status ailment resistance
+ break;
+ case BD_ETERNALCHAOS:
+ break;
+ case PF_FOGWALL: /* フォグウォ?ル */
+ if(sc_data && sc_data[SC_DELUGE].timer!=-1) limit *= 2;
+ break;
+
+ case RG_GRAFFITI: /* Graffiti */
+ count=1; // Leave this at 1 [Valaris]
+ break;
+ }
+
+ if (val3==0 && (flag&2 || (sc_data && sc_data[SC_MAGICPOWER].timer != -1)))
+ val3 = HW_MAGICPOWER; //Store the magic power flag. [Skotlex]
+
+ nullpo_retr(NULL, group=skill_initunitgroup(src,(count > 0 ? count : layout->count),
+ skillid,skilllv,skill_get_unit_id(skillid,flag&1)));
+ group->limit=limit;
+ group->val1=val1;
+ group->val2=val2;
+ group->val3=val3;
+ group->target_flag=target;
+ group->bl_flag= skill_get_unit_bl_target(skillid);
+ group->interval=interval;
+ if(skillid==HT_TALKIEBOX ||
+ skillid==RG_GRAFFITI){
+ group->valstr=(char *) aCallocA(MESSAGE_SIZE, sizeof(char));
+ if(group->valstr==NULL){
+ ShowFatalError("skill_castend_map: out of memory !\n");
+ exit(1);
+ }
+ memcpy(group->valstr,talkie_mes,MESSAGE_SIZE-1);
+ }
+
+ //Why redefine local variables when the ones of the function can be reused? [Skotlex]
+ val1=skilllv;
+ val2=0;
+ limit=group->limit;
+ for(i=0;i<layout->count;i++){
+ struct skill_unit *unit;
+ int ux,uy,alive=1;
+ ux = x + layout->dx[i];
+ uy = y + layout->dy[i];
+ switch (skillid) {
+ case MG_FIREWALL: /* ファイヤ?ウォ?ル */
+ val2=group->val2;
+ break;
+ case WZ_ICEWALL: /* アイスウォ?ル */
+ if(skilllv <= 1)
+ val1 = 500;
+ else
+ val1 = 200 + 200*skilllv;
+ break;
+ case RG_GRAFFITI: /* Graffiti [Valaris] */
+ ux+=(i%5-2);
+ uy+=(i/5-2);
+ break;
+ }
+ //直?繝Xキルの???ン置?タ標?繧ノランドプ?テクタ?がないかチェック
+ if(range<=0)
+ map_foreachincell(skill_landprotector,src->m,ux,uy,BL_SKILL,skillid,&alive, src);
+
+ if(alive && map_getcell(src->m,ux,uy,CELL_CHKWALL))
+ alive = 0;
+
+ if (alive && battle_config.skill_wall_check) {
+ //Check if there's a path between cell and center of casting.
+ struct walkpath_data wpd;
+ if (path_search(&wpd,src->m,ux,uy,x,y,1)==-1)
+ alive = 0;
+ }
+
+ if(alive && skillid == WZ_ICEWALL) {
+ if(src->x == x && src->y==y) // Ice Wall not allowed on self [DracoRPG]
+ alive=0;
+ else {
+ val2=map_getcell(src->m,ux,uy,CELL_GETTYPE);
+ if(val2==5 || val2==1)
+ alive=0;
+ else
+ clif_changemapcell(src->m,ux,uy,5,0);
+ }
+ }
+
+ if(alive){
+ nullpo_retr(NULL, unit=skill_initunit(group,i,ux,uy));
+ unit->val1=val1;
+ unit->val2=val2;
+ unit->limit=limit;
+ unit->range=range;
+
+ if (range==0 && active_flag)
+ map_foreachincell(skill_unit_effect,unit->bl.m,
+ unit->bl.x,unit->bl.y,group->bl_flag,&unit->bl,gettick(),1);
+ }
+ }
+
+ return group;
+}
+
+/*==========================================
+ * スキルユニットの?動イベント
+ *------------------------------------------
+ */
+int skill_unit_onplace(struct skill_unit *src,struct block_list *bl,unsigned int tick)
+{
+ struct skill_unit_group *sg;
+ struct block_list *ss;
+ struct status_change *sc_data;
+ int type;
+ short *opt;
+
+ nullpo_retr(0, src);
+ nullpo_retr(0, bl);
+
+ if(bl->prev==NULL || !src->alive || status_isdead(bl))
+ return 0;
+
+ nullpo_retr(0, sg=src->group);
+ nullpo_retr(0, ss=map_id2bl(sg->src_id));
+
+ if (map_getcell(bl->m, bl->x, bl->y, CELL_CHKLANDPROTECTOR))
+ return 0; //AoE skills are ineffective. [Skotlex]
+
+ if (battle_check_target(&src->bl,bl,sg->target_flag)<=0)
+ return 0;
+
+ if ((opt = status_get_option(bl)) && ((*opt)&OPTION_HIDE) && sg->skill_id != WZ_HEAVENDRIVE)
+ return 0; //Hidden characters are inmune to AoE skills except Heaven's Drive. [Skotlex]
+
+ sc_data = status_get_sc_data(bl);
+ type = SkillStatusChangeTable[sg->skill_id];
+
+ switch (sg->unit_id) {
+ case UNT_SAFETYWALL:
+ //TODO: Find a more reliable way to handle the link to sg, this could cause dangling pointers. [Skotlex]
+ if (sc_data && sc_data[type].timer == -1)
+ status_change_start(bl,type,sg->skill_lv,sg->group_id,(int)sg,0,sg->limit,0);
+ break;
+
+ case UNT_PNEUMA:
+ if (sc_data && sc_data[type].timer == -1)
+ status_change_start(bl,type,sg->skill_lv,sg->group_id,0,0,sg->limit,0);
+ break;
+
+ case UNT_WARP_WAITING:
+ if(bl->type==BL_PC){
+ struct map_session_data *sd = (struct map_session_data *)bl;
+ if((!sd->chatID || battle_config.chat_warpportal)
+ && sd->to_x == src->bl.x && sd->to_y == src->bl.y) {
+ if (pc_setpos(sd,sg->val3,sg->val2>>16,sg->val2&0xffff,3) == 0) {
+ if (--sg->val1<=0 || sg->src_id == bl->id)
+ skill_delunitgroup(sg);
+ }
+ }
+ } else if(bl->type==BL_MOB && battle_config.mob_warpportal){
+ int m = map_mapindex2mapid(sg->val3);
+ mob_warp((struct mob_data *)bl,m,sg->val2>>16,sg->val2&0xffff,3);
+ }
+ break;
+
+ case UNT_QUAGMIRE:
+ if(status_isimmune(bl))
+ break;
+ if(sc_data && sc_data[type].timer==-1)
+ status_change_start(bl,type,sg->skill_lv,sg->group_id,0,0,sg->limit,0);
+ break;
+
+ case UNT_VOLCANO:
+ case UNT_DELUGE:
+ case UNT_VIOLENTGALE:
+ if(sc_data && sc_data[type].timer==-1)
+ status_change_start(bl,type,sg->skill_lv,sg->group_id,0,0,
+ skill_get_time2(sg->skill_id,sg->skill_lv),0);
+ break;
+
+ case UNT_RICHMANKIM:
+ case UNT_ETERNALCHAOS:
+ case UNT_DRUMBATTLEFIELD:
+ case UNT_RINGNIBELUNGEN:
+ case UNT_ROKISWEIL:
+ case UNT_INTOABYSS:
+ case UNT_SIEGFRIED:
+ //Needed to check when a dancer/bard leaves their ensemble area.
+ if (sg->src_id==bl->id && (!sc_data || sc_data[SC_SPIRIT].timer == -1 || sc_data[SC_SPIRIT].val2 != SL_BARDDANCER))
+ return sg->skill_id;
+ if (sc_data && sc_data[type].timer==-1)
+ status_change_start(bl,type,sg->skill_lv,sg->val1,sg->val2,0,sg->limit,0);
+ break;
+
+ case UNT_WHISTLE:
+ case UNT_ASSASSINCROSS:
+ case UNT_POEMBRAGI:
+ case UNT_APPLEIDUN:
+ case UNT_HUMMING:
+ case UNT_DONTFORGETME:
+ case UNT_FORTUNEKISS:
+ case UNT_SERVICEFORYOU:
+ case UNT_HERMODE:
+ if (sg->src_id==bl->id && (!sc_data || sc_data[SC_SPIRIT].timer == -1 || sc_data[SC_SPIRIT].val2 != SL_BARDDANCER))
+ return 0;
+ if (sc_data && sc_data[type].timer==-1)
+ status_change_start(bl,type,sg->skill_lv,sg->val1,sg->val2,0,sg->limit,0);
+ break;
+
+ case UNT_BASILICA:
+ if (!(status_get_mode(bl)&MD_BOSS) && battle_check_target(&src->bl,bl,BCT_ENEMY)>0)
+ skill_blown(&src->bl,bl,1);
+ break;
+
+ case UNT_FOGWALL:
+ if (sc_data && sc_data[type].timer==-1)
+ {
+ status_change_start (bl, type, sg->skill_lv, sg->val1, sg->val2, sg->group_id, sg->limit, 0);
+ if (battle_check_target(&src->bl,bl,BCT_ENEMY)>0)
+ skill_additional_effect (ss, bl, sg->skill_id, sg->skill_lv, BF_MISC, tick);
+ }
+ break;
+
+ case UNT_GRAVITATION:
+ if (sc_data && sc_data[type].timer==-1 && !status_get_mode(bl)&MD_BOSS)
+ status_change_start(bl,type,sg->skill_lv,5*sg->skill_lv,BCT_ENEMY,sg->group_id,sg->limit,0);
+ break;
+
+ case UNT_ICEWALL: //Destroy the cell. [Skotlex]
+ src->val1 = 0;
+ if(src->limit + sg->tick > tick + 700)
+ src->limit = DIFF_TICK(tick+700,sg->tick);
+ break;
+ case UNT_GOSPEL:
+ if (sg->src_id != bl->id && battle_check_target(ss,bl,BCT_PARTY)>0 &&
+ sc_data && sc_data[type].timer==-1) //Start Gospel Effect to prevent item usage affects party only. [Skotlex]
+ status_change_start(bl,type,sg->skill_lv,0,0,BCT_ALL,sg->limit,0);
+ break;
+ }
+
+ return sg->skill_id;
+}
+
+/*==========================================
+ * スキルユニットの発動イベント(タイマ?[発動)
+ *------------------------------------------
+ */
+int skill_unit_onplace_timer(struct skill_unit *src,struct block_list *bl,unsigned int tick)
+{
+ struct skill_unit_group *sg;
+ struct block_list *ss;
+ struct map_session_data *sd = NULL;
+ int splash_count=0;
+ struct status_change *sc_data, *ssc_data;
+ struct skill_unit_group_tickset *ts;
+ int type;
+ int diff=0;
+
+ nullpo_retr(0, src);
+ nullpo_retr(0, bl);
+
+ if (bl->prev==NULL || !src->alive || status_isdead(bl))
+ return 0;
+
+ nullpo_retr(0, sg=src->group);
+ nullpo_retr(0, ss=map_id2bl(sg->src_id));
+ if (ss->type == BL_PC) sd = (struct map_session_data*)ss;
+ ssc_data = status_get_sc_data(ss); //For magic power.
+ sc_data = status_get_sc_data(bl);
+ type = SkillStatusChangeTable[sg->skill_id];
+
+ if (sg->interval == -1 && (sg->unit_id == UNT_ANKLESNARE || sg->unit_id == UNT_SPIDERWEB || sg->unit_id == UNT_FIREPILLAR_ACTIVE))
+ //Ok, this case only happens with Ankle Snare/Spider Web (only skills that sets its interval to -1),
+ //and only happens when more than one target is stepping on the trap at the moment it was triggered
+ //(yet only the first mob standing on the trap will be captured) [Skotlex]
+ return 0;
+
+ if ((ts = skill_unitgrouptickset_search(bl,sg,tick)))
+ { //Not all have it, eg: Traps don't have it even though they can be hit by Heaven's Drive [Skotlex]
+ diff = DIFF_TICK(tick,ts->tick);
+ if (diff < 0)
+ return 0;
+ ts->tick = tick+sg->interval;
+
+ // GXは?dなっていたら3HITしない
+ if ((sg->skill_id==CR_GRANDCROSS || sg->skill_id==NPC_GRANDDARKNESS) && !battle_config.gx_allhit)
+ ts->tick += sg->interval*(map_count_oncell(bl->m,bl->x,bl->y,0)-1);
+ }
+ //Temporarily set magic power to have it take effect. [Skotlex]
+ if (sg->val3 == HW_MAGICPOWER && ssc_data && ssc_data[SC_MAGICPOWER].timer == -1 && ssc_data[SC_MAGICPOWER].val1 > 0)
+ {
+ if (sd)
+ { //This is needed since we are not going to recall status_calc_pc...
+ sd->matk1 += sd->matk1 * 5*ssc_data[SC_MAGICPOWER].val1/100;
+ sd->matk2 += sd->matk2 * 5*ssc_data[SC_MAGICPOWER].val1/100;
+ } else
+ ssc_data[SC_MAGICPOWER].timer = -2; //Note to NOT return from the function until this is unset!
+ }
+
+ switch (sg->unit_id) {
+ case UNT_FIREWALL:
+ {
+ int flag=0, t_ele = status_get_elem_type(bl);
+ if (t_ele == 3 || battle_check_undead(status_get_race(bl), t_ele))
+ flag = src->val2>battle_config.firewall_hits_on_undead?battle_config.firewall_hits_on_undead:src->val2;
+
+ skill_attack(BF_MAGIC,ss,&src->bl,bl,sg->skill_id,sg->skill_lv,tick,flag);
+ src->val2-=flag?flag:1;
+ if (src->val2<=0)
+ skill_delunit(src);
+ break;
+ }
+ case UNT_SANCTUARY:
+ {
+ int race = status_get_race(bl);
+
+ if (battle_check_undead(race, status_get_elem_type(bl)) || race==6) {
+ if (skill_attack(BF_MAGIC, ss, &src->bl, bl, sg->skill_id, sg->skill_lv, tick, 0)) {
+ // reduce healing count if this was meant for damaging [hekate]
+ sg->val1 -= 2;
+ }
+ } else {
+ int heal = sg->val2;
+ if (status_get_hp(bl) >= status_get_max_hp(bl))
+ break;
+ if (status_isimmune(bl))
+ heal = 0; /* 黄金蟲カ?[ド?iヒ?[ル量0?j */
+ clif_skill_nodamage(&src->bl, bl, AL_HEAL, heal, 1);
+ battle_heal(NULL, bl, heal, 0, 0);
+ if (diff >= 500)
+ sg->val1--; // ?V規に入ったユニットだけカウント
+ }
+ if (sg->val1 <= 0)
+ skill_delunitgroup(sg);
+ break;
+ }
+
+ case UNT_MAGNUS:
+ {
+ int race = status_get_race(bl);
+ if (!battle_check_undead(race,status_get_elem_type(bl)) && race!=6)
+ break;
+ skill_attack(BF_MAGIC,ss,&src->bl,bl,sg->skill_id,sg->skill_lv,tick,0);
+ src->val2++;
+ break;
+ }
+
+ case UNT_MAGIC_SKILLS:
+ skill_attack(BF_MAGIC,ss,&src->bl,bl,sg->skill_id,sg->skill_lv,tick,0);
+ break;
+
+ case UNT_FIREPILLAR_WAITING:
+ skill_delunit(src);
+ skill_unitsetting(ss,sg->skill_id,sg->skill_lv,src->bl.x,src->bl.y,1);
+ break;
+
+ case UNT_FIREPILLAR_ACTIVE:
+ map_foreachinarea(skill_attack_area,bl->m,bl->x-1,bl->y-1,bl->x+1,bl->y+1,sg->bl_flag,
+ BF_MAGIC,ss,&src->bl,sg->skill_id,sg->skill_lv,tick,0,BCT_ENEMY); // area damage [Celest]
+ sg->interval = -1; //Mark it used up so others can't trigger it for massive splash damage. [Skotlex]
+ sg->limit=DIFF_TICK(tick,sg->tick) + 1500;
+ break;
+
+ case UNT_SKIDTRAP:
+ {
+ skill_blown(&src->bl,bl,skill_get_blewcount(sg->skill_id,sg->skill_lv)|0x10000);
+ sg->unit_id = UNT_USED_TRAPS;
+ clif_changelook(&src->bl,LOOK_BASE,sg->unit_id);
+ sg->limit=DIFF_TICK(tick,sg->tick)+1500;
+ sg->val3 = BD_INTOABYSS; //Prevent Remove Trap from giving you the trap back. [Skotlex]
+ }
+ break;
+
+ case UNT_ANKLESNARE:
+ if(sg->val2==0 && sc_data && sc_data[SC_ANKLE].timer==-1){
+ int sec = skill_get_time2(sg->skill_id,sg->skill_lv) - status_get_agi(bl)*100;
+ if(status_get_mode(bl)&MD_BOSS) // Lasts 5 times less on bosses
+ sec = sec/5;
+ if (sec < 3000+30*sg->skill_lv) // Minimum trap time of 3+0.03*skilllv seconds [celest]
+ sec = 3000+30*sg->skill_lv;
+ battle_stopwalking(bl,1);
+ status_change_start(bl,SC_ANKLE,sg->skill_lv,0,0,0,sec,0);
+ map_moveblock(bl, src->bl.x, src->bl.y, tick);
+ clif_fixpos(bl);
+ //clif_01ac(&src->bl); //Removed? Check the openkore description of this packet: [Skotlex]
+ // 01AC: long ID
+ // Indicates that an object is trapped, but ID is not a
+ // valid monster or player ID.
+ sg->limit=DIFF_TICK(tick,sg->tick) + sec;
+ sg->val2=bl->id;
+ sg->interval = -1;
+ src->range = 0;
+ }
+ break;
+
+ case UNT_VENOMDUST:
+ if(sc_data && sc_data[type].timer==-1 )
+ status_change_start(bl,type,sg->skill_lv,sg->group_id,0,0,skill_get_time2(sg->skill_id,sg->skill_lv),0);
+ break;
+
+ case UNT_LANDMINE:
+ skill_attack(BF_MISC,ss,&src->bl,bl,sg->skill_id,sg->skill_lv,tick,0);
+ sg->unit_id = UNT_USED_TRAPS;
+ clif_changelook(&src->bl,LOOK_BASE,UNT_FIREPILLAR_ACTIVE);
+ sg->limit=DIFF_TICK(tick,sg->tick)+1500;
+ sg->val3 = BD_INTOABYSS; //Prevent Remove Trap from giving you the trap back. [Skotlex]
+ break;
+
+ case UNT_BLASTMINE:
+ case UNT_SHOCKWAVE:
+ case UNT_SANDMAN:
+ case UNT_FLASHER:
+ case UNT_FREEZINGTRAP:
+ case UNT_CLAYMORETRAP:
+ map_foreachinarea(skill_count_target,src->bl.m
+ ,src->bl.x-src->range,src->bl.y-src->range
+ ,src->bl.x+src->range,src->bl.y+src->range
+ ,sg->bl_flag,&src->bl,&splash_count);
+ map_foreachinarea(skill_trap_splash,src->bl.m
+ ,src->bl.x-src->range,src->bl.y-src->range
+ ,src->bl.x+src->range,src->bl.y+src->range
+ ,sg->bl_flag,&src->bl,tick,splash_count);
+ sg->unit_id = UNT_USED_TRAPS;
+ clif_changelook(&src->bl,LOOK_BASE,sg->unit_id);
+ sg->limit=DIFF_TICK(tick,sg->tick)+1500;
+ sg->val3 = BD_INTOABYSS; //Prevent Remove Trap from giving you the trap back. [Skotlex]
+ break;
+
+ case UNT_TALKIEBOX:
+ if (sg->src_id == bl->id) //自分が踏んでも発動しない
+ break;
+ if (sg->val2 == 0){
+ clif_talkiebox(&src->bl, sg->valstr);
+ sg->unit_id = UNT_USED_TRAPS;
+ clif_changelook(&src->bl, LOOK_BASE, sg->unit_id);
+ sg->limit = DIFF_TICK(tick, sg->tick) + 5000;
+ sg->val2 = -1; //踏んだ
+ sg->val3 = BD_INTOABYSS; //Prevent Remove Trap from giving you the trap back. [Skotlex]
+ }
+ break;
+
+ case UNT_LULLABY:
+ if (ss->id == bl->id)
+ break;
+ skill_additional_effect(ss, bl, sg->skill_id, sg->skill_lv, BF_LONG|BF_SKILL|BF_MISC, tick);
+ break;
+
+ case UNT_UGLYDANCE: //Ugly Dance [Skotlex]
+ if (ss->id == bl->id)
+ break;
+ if (bl->type == BL_PC)
+ skill_additional_effect(ss, bl, sg->skill_id, sg->skill_lv, BF_LONG|BF_SKILL|BF_MISC, tick);
+ break;
+
+ case UNT_DISSONANCE:
+ skill_attack(BF_MISC, ss, &src->bl, bl, sg->skill_id, sg->skill_lv, tick, 0);
+ break;
+
+ case UNT_APPLEIDUN: //Apple of Idun [Skotlex]
+ {
+ int heal;
+ if (sg->src_id == bl->id)
+ break;
+ heal = sg->val2;
+ clif_skill_nodamage(&src->bl, bl, AL_HEAL, heal, 1);
+ battle_heal(NULL, bl, heal, 0, 0);
+ break;
+ }
+
+ case UNT_DEMONSTRATION:
+ skill_attack(BF_WEAPON, ss, &src->bl, bl, sg->skill_id, sg->skill_lv, tick, 0);
+ break;
+
+ case UNT_GOSPEL:
+ if (rand()%100 > sg->skill_lv*10)
+ break;
+ if (ss != bl && battle_check_target(ss,bl,BCT_PARTY)>0) { // Support Effect only on party, not guild
+ int i = rand()%13; // Positive buff count
+ switch (i)
+ {
+ case 0: // Heal 1~9999 HP
+ {
+ int heal = rand() %9999+1;
+ clif_skill_nodamage(ss,bl,AL_HEAL,heal,1);
+ battle_heal(NULL,bl,heal,0,0);
+ }
+ break;
+ case 1: // End all negative status
+ status_change_clear_debuffs (bl);
+ break;
+ case 2: // Level 10 Blessing
+ status_change_start(bl,SC_BLESSING,10,0,0,0,skill_get_time2(sg->skill_id, sg->skill_lv),0);
+ break;
+ case 3: // Level 10 Increase AGI
+ status_change_start(bl,SC_INCREASEAGI,10,0,0,0,skill_get_time2(sg->skill_id, sg->skill_lv),0);
+ break;
+ case 4: // Enchant weapon with Holy element
+ status_change_start(bl,SC_ASPERSIO,1,0,0,0,skill_get_time2(sg->skill_id, sg->skill_lv),0);
+ break;
+ case 5: // Enchant armor with Holy element
+ status_change_start(bl,SC_BENEDICTIO,1,0,0,0,skill_get_time2(sg->skill_id, sg->skill_lv),0);
+ break;
+ case 6: // MaxHP +100%
+ status_change_start(bl,SC_INCMHPRATE,100,0,0,0,skill_get_time2(sg->skill_id, sg->skill_lv),0);
+ break;
+ case 7: // MaxSP +100%
+ status_change_start(bl,SC_INCMSPRATE,100,0,0,0,skill_get_time2(sg->skill_id, sg->skill_lv),0);
+ break;
+ case 8: // All stats +20
+ status_change_start(bl,SC_INCALLSTATUS,20,0,0,0,skill_get_time2(sg->skill_id, sg->skill_lv),0);
+ break;
+ case 9: // DEF +25%
+ status_change_start(bl,SC_INCDEFRATE,25,0,0,0,skill_get_time2(sg->skill_id, sg->skill_lv),0);
+ break;
+ case 10: // ATK +100%
+ status_change_start(bl,SC_INCATKRATE,100,0,0,0,skill_get_time2(sg->skill_id, sg->skill_lv),0);
+ break;
+ case 11: // HIT/Flee +50
+ status_change_start(bl,SC_INCHIT,50,0,0,0,skill_get_time2(sg->skill_id, sg->skill_lv),0);
+ status_change_start(bl,SC_INCFLEE,50,0,0,0,skill_get_time2(sg->skill_id, sg->skill_lv),0);
+ break;
+ case 12: // Immunity to all status
+ status_change_start(bl,SC_SCRESIST,100,0,0,0,skill_get_time2(sg->skill_id, sg->skill_lv),0);
+ break;
+ }
+ }
+ else if (battle_check_target(&src->bl,bl,BCT_ENEMY)>0) { // Offensive Effect
+ int i = rand()%9; // Negative buff count
+ switch (i)
+ {
+ case 0: // Deal 1~9999 damage
+ {
+ int dmg = rand() % 9999 +1;
+ clif_skill_damage(bl, bl, sg->tick,0,0,dmg,0,CR_HOLYCROSS,1,-1);
+ battle_damage(ss, bl, dmg,0);
+ break;
+ }
+ case 1: // Curse
+ status_change_start(bl,SC_CURSE,1,0,0,0,skill_get_time2(sg->skill_id, sg->skill_lv),0);
+ break;
+ case 2: // Blind
+ status_change_start(bl,SC_BLIND,1,0,0,0,skill_get_time2(sg->skill_id, sg->skill_lv),0);
+ break;
+ case 3: // Poison
+ status_change_start(bl,SC_POISON,1,0,0,0,skill_get_time2(sg->skill_id, sg->skill_lv),0);
+ break;
+ case 4: // Level 10 Provoke
+ status_change_start(bl,SC_PROVOKE,10,0,0,0,skill_get_time2(sg->skill_id, sg->skill_lv),0);
+ break;
+ case 5: // DEF -100%
+ status_change_start(bl,SC_INCDEFRATE,-100,0,0,0,skill_get_time2(sg->skill_id, sg->skill_lv),0);
+ break;
+ case 6: // ATK -100%
+ status_change_start(bl,SC_INCATKRATE,-100,0,0,0,skill_get_time2(sg->skill_id, sg->skill_lv),0);
+ break;
+ case 7: // Flee -100%
+ status_change_start(bl,SC_INCFLEERATE,-100,0,0,0,skill_get_time2(sg->skill_id, sg->skill_lv),0);
+ break;
+ case 8: // Speed/ASPD -25%
+ status_change_start(bl,SC_GOSPEL,1,0,0,BCT_ENEMY,skill_get_time2(sg->skill_id, sg->skill_lv),0);
+ break;
+ }
+ }
+ break;
+
+ case UNT_SPIDERWEB:
+ if(sg->val2==0 && (!sc_data || sc_data[type].timer==-1 )){
+ skill_additional_effect(ss,bl,sg->skill_id,sg->skill_lv,BF_MISC,tick);
+ map_moveblock(bl, src->bl.x, src->bl.y, tick);
+ clif_fixpos(bl);
+ sg->limit = DIFF_TICK(tick,sg->tick)+skill_get_time2(sg->skill_id,sg->skill_lv);
+ sg->val2=bl->id;
+ sg->interval = -1;
+ src->range = 0;
+ }
+ break;
+
+ case UNT_GRAVITATION:
+ skill_attack(BF_MAGIC,ss,&src->bl,bl,sg->skill_id,sg->skill_lv,tick,0);
+ break;
+ }
+ if (sg->val3 == HW_MAGICPOWER && ssc_data && ssc_data[SC_MAGICPOWER].timer < 0 && ssc_data[SC_MAGICPOWER].val1 > 0)
+ { //Unset Magic Power.
+ if (sd)
+ {
+ sd->matk1 = 100*sd->matk1/(100 + 5*ssc_data[SC_MAGICPOWER].val1);
+ sd->matk2 = 100*sd->matk2/(100 + 5*ssc_data[SC_MAGICPOWER].val1);
+ } else
+ ssc_data[SC_MAGICPOWER].timer = -1;
+ }
+
+ if (bl->type == BL_MOB && ss != bl) { /* スキル使用??のMOBスキル */
+ struct mob_data *md = (struct mob_data *)bl;
+ if (!md) return 0;
+ if (battle_config.mob_changetarget_byskill == 1) {
+ int target = md->target_id;
+ if (ss->type == BL_PC)
+ md->target_id = ss->id;
+ mobskill_use(md, tick, MSC_SKILLUSED|(sg->skill_id << 16));
+ md->target_id = target;
+ } else
+ mobskill_use(md, tick, MSC_SKILLUSED|(sg->skill_id << 16));
+ }
+
+ return sg->skill_id;
+}
+/*==========================================
+ * スキルユニットから離?する(もしくはしている)??
+ *------------------------------------------
+ */
+int skill_unit_onout(struct skill_unit *src,struct block_list *bl,unsigned int tick)
+{
+ struct skill_unit_group *sg;
+ struct status_change *sc_data;
+ int type;
+
+ nullpo_retr(0, src);
+ nullpo_retr(0, bl);
+ nullpo_retr(0, sg=src->group);
+ sc_data = status_get_sc_data(bl);
+ type = SkillStatusChangeTable[sg->skill_id];
+
+ if (bl->prev==NULL || !src->alive || //Need to delete the trap if the source died.
+ (status_isdead(bl) && sg->unit_id != UNT_ANKLESNARE && sg->unit_id != UNT_SPIDERWEB))
+ return 0;
+
+ switch(sg->unit_id){
+ case UNT_SAFETYWALL:
+ if (sc_data && sc_data[type].timer!=-1)
+ status_change_end(bl,type,-1);
+ break;
+ case UNT_ANKLESNARE:
+ {
+ struct block_list *target = map_id2bl(sg->val2);
+ if(target && target == bl){
+ status_change_end(bl,SC_ANKLE,-1);
+ sg->limit=DIFF_TICK(tick,sg->tick)+1000;
+ sg->val3 = BD_INTOABYSS; //Prevent Remove Trap from giving you the trap back. [Skotlex]
+ }
+ else
+ return 0;
+ break;
+ }
+ case UNT_BASILICA: //Clear basilica if the owner moved [Skotlex]
+ case UNT_HERMODE: //Clear Hermode if the owner moved.
+ if (sc_data && sc_data[type].timer!=-1 && sc_data[type].val3 == BCT_SELF && sc_data[type].val4 == sg->group_id)
+ status_change_end(bl,type,-1);
+ break;
+
+ case UNT_SPIDERWEB: /* スパイダ?ウェッブ */
+ {
+ struct block_list *target = map_id2bl(sg->val2);
+ if (target && target==bl)
+ {
+ status_change_end(bl,SC_SPIDERWEB,-1);
+ sg->limit = DIFF_TICK(tick,sg->tick)+1000;
+ }
+ break;
+ }
+ }
+ return sg->skill_id;
+}
+
+/*==========================================
+ * Triggered when a char steps out of a skill group [Skotlex]
+ *------------------------------------------
+ */
+static int skill_unit_onleft(int skill_id, struct block_list *bl,unsigned int tick)
+{
+ struct status_change *sc_data;
+ int type;
+
+ sc_data = status_get_sc_data(bl);
+ type = SkillStatusChangeTable[skill_id];
+
+ switch (skill_id)
+ {
+ case WZ_QUAGMIRE:
+ if (bl->type==BL_MOB)
+ break;
+ if (sc_data && sc_data[type].timer != -1)
+ status_change_end(bl, type, -1);
+ break;
+
+ case BD_RICHMANKIM:
+ case BD_ETERNALCHAOS:
+ case BD_DRUMBATTLEFIELD:
+ case BD_RINGNIBELUNGEN:
+ case BD_ROKISWEIL:
+ case BD_INTOABYSS:
+ case BD_SIEGFRIED:
+ if(sc_data && sc_data[SC_DANCING].timer != -1 && sc_data[SC_DANCING].val1 == skill_id)
+ { //Check if you just stepped out of your ensemble skill to cancel dancing. [Skotlex]
+ //We don't check for SC_LONGING because someone could always have knocked you back and out of the song/dance.
+ //FIXME: This code is not perfect, it doesn't checks for the real ensemble's owner,
+ //it only checks if you are doing the same ensemble. So if there's two chars doing an ensemble
+ //which overlaps, by stepping outside of the other parther's ensemble will cause you to cancel
+ //your own. Let's pray that scenario is pretty unlikely and noone will complain too much about it.
+ skill_stop_dancing(bl);
+ }
+ case MG_SAFETYWALL:
+ case AL_PNEUMA:
+ case SA_VOLCANO:
+ case SA_DELUGE:
+ case SA_VIOLENTGALE:
+ case CG_HERMODE:
+ case HW_GRAVITATION:
+ if (sc_data && sc_data[type].timer != -1)
+ status_change_end(bl, type, -1);
+ break;
+
+ case BA_POEMBRAGI:
+ case BA_WHISTLE:
+ case BA_ASSASSINCROSS:
+ case BA_APPLEIDUN:
+ case DC_HUMMING:
+ case DC_DONTFORGETME:
+ case DC_FORTUNEKISS:
+ case DC_SERVICEFORYOU:
+ if (sc_data && sc_data[type].timer != -1)
+ {
+ delete_timer(sc_data[type].timer, status_change_timer);
+ //NOTE: It'd be nice if we could get the skill_lv for a more accurate extra time, but alas...
+ //not possible on our current implementation.
+ sc_data[type].timer = add_timer(tick+skill_get_time2(skill_id,1), status_change_timer, bl->id, type);
+ }
+ break;
+ case PF_FOGWALL:
+ if (sc_data && sc_data[type].timer != -1)
+ {
+ status_change_end(bl,type,-1);
+ if (sc_data[SC_BLIND].timer!=-1)
+ {
+ if (bl->type == BL_PC) //Players get blind ended inmediately, others have it still for 30 secs. [Skotlex]
+ status_change_end(bl, SC_BLIND, -1);
+ else {
+ delete_timer(sc_data[SC_BLIND].timer, status_change_timer);
+ sc_data[SC_BLIND].timer = add_timer(30000+tick, status_change_timer, bl->id, SC_BLIND);
+ }
+ }
+ }
+ break;
+ case UNT_GOSPEL:
+ if (sc_data && sc_data[type].timer != -1 && sc_data[type].val4 == BCT_ALL) //End item-no-use Gospel Effect. [Skotlex]
+ status_change_end(bl, type, -1);
+ break;
+
+ }
+ return skill_id;
+}
+
+/*==========================================
+ * Invoked when a unit cell has been placed/removed/deleted.
+ * flag values:
+ * flag&1: Invoke onplace function (otherwise invoke onout)
+ * flag&4: Invoke a onleft call (the unit might be scheduled for deletion)
+ *------------------------------------------
+ */
+int skill_unit_effect(struct block_list *bl,va_list ap)
+{
+ struct skill_unit *unit;
+ struct skill_unit_group *group;
+ int flag;
+ unsigned int tick;
+
+ nullpo_retr(0, bl);
+ nullpo_retr(0, ap);
+ nullpo_retr(0, unit=va_arg(ap,struct skill_unit*));
+ tick = va_arg(ap,unsigned int);
+ flag = va_arg(ap,unsigned int);
+
+ if (!unit->alive || bl->prev==NULL)
+ return 0;
+
+ nullpo_retr(0, group=unit->group);
+
+ if (flag&1)
+ skill_unit_onplace(unit,bl,tick);
+ else
+ skill_unit_onout(unit,bl,tick);
+
+ if (flag&4) skill_unit_onleft(group->skill_id, bl, tick);
+ return 0;
+}
+
+/*==========================================
+ * スキルユニットの限界イベント
+ *------------------------------------------
+ */
+int skill_unit_onlimit(struct skill_unit *src,unsigned int tick)
+{
+ struct skill_unit_group *sg;
+ nullpo_retr(0, src);
+ nullpo_retr(0, sg=src->group);
+
+ switch(sg->unit_id){
+ case UNT_WARP_ACTIVE: /* ??プポ?タル(?動前) */
+ {
+ struct skill_unit_group *group=
+ skill_unitsetting(map_id2bl(sg->src_id),sg->skill_id,sg->skill_lv,
+ src->bl.x,src->bl.y,1);
+ if(group == NULL)
+ return 0;
+ group->val2=sg->val2; //Copy the (x,y) position you warp to
+ group->val3=sg->val3; //as well as the mapindex to warp to.
+ }
+ break;
+
+ case UNT_ICEWALL: /* アイスウォ?ル */
+ clif_changemapcell(src->bl.m,src->bl.x,src->bl.y,src->val2,1);
+ break;
+ case UNT_CALLPARTNER: /* あなたに?いたい */
+ {
+ struct map_session_data *sd = NULL;
+ if((sd = map_id2sd(sg->src_id)) == NULL)
+ return 0;
+ if((sd = pc_get_partner(sd)) == NULL)
+ return 0;
+
+ pc_setpos(sd,map[src->bl.m].index,src->bl.x,src->bl.y,3);
+ }
+ break;
+ }
+
+ return 0;
+}
+/*==========================================
+ * スキルユニットのダ??ジイベント
+ *------------------------------------------
+ */
+int skill_unit_ondamaged(struct skill_unit *src,struct block_list *bl,
+ int damage,unsigned int tick)
+{
+ struct skill_unit_group *sg;
+
+ nullpo_retr(0, src);
+ nullpo_retr(0, sg=src->group);
+
+ if (skill_get_inf2(sg->skill_id)&INF2_TRAP && damage > 0)
+ skill_delunitgroup(sg);
+ else
+ switch(sg->unit_id){
+ case UNT_ICEWALL:
+ src->val1-=damage;
+ break;
+ default:
+ damage = 0;
+ break;
+ }
+ return damage;
+}
+
+static int skill_moonlit_sub(struct block_list *bl, va_list ap) {
+ struct block_list *src = va_arg(ap, struct block_list*);
+ struct block_list *partner = va_arg(ap, struct block_list*);
+ int blowcount = va_arg(ap, int);
+ if (bl == src || bl == partner)
+ return 0;
+ skill_blown(src, bl, blowcount);
+ return 1;
+}
+
+/*==========================================
+ * Starts the moonlit effect by first knocking back all other characters in the vecinity.
+ * partner may be null, but src cannot be.
+ *------------------------------------------
+ */
+static void skill_moonlit(struct block_list* src, struct block_list* partner, int skilllv)
+{
+ int range = skill_get_range2(src, CG_MOONLIT, skilllv);
+ int blowcount = range+1, time = skill_get_time(CG_MOONLIT,skilllv);
+
+ map_foreachinarea(skill_moonlit_sub,src->m
+ ,src->x-range,src->y-range
+ ,src->x+range,src->y+range
+ ,BL_CHAR,src,partner,blowcount);
+ if(partner)
+ map_foreachinarea(skill_moonlit_sub,partner->m
+ ,partner->x-range,partner->y-range
+ ,partner->x+range,partner->y+range
+ ,BL_CHAR,src,partner,blowcount);
+
+ status_change_start(src,SC_DANCING,CG_MOONLIT,0,0,partner?partner->id:BCT_SELF,time+1000,0);
+ status_change_start(src,SkillStatusChangeTable[CG_MOONLIT],skilllv,0,0,0,time,0);
+
+ if (partner) {
+ status_change_start(partner,SC_DANCING,CG_MOONLIT,0,0,src->id,time+1000,0);
+ status_change_start(partner,SkillStatusChangeTable[CG_MOONLIT],skilllv,0,0,0,time,0);
+ }
+
+}
+/*==========================================
+ * 範??キャラ存?ン確認判定??(foreachinarea)
+ *------------------------------------------
+ */
+
+static int skill_check_condition_char_sub (struct block_list *bl, va_list ap)
+{
+ int *c, skillid;
+ struct block_list *src;
+ struct map_session_data *sd;
+ struct map_session_data *tsd;
+ int *p_sd; //Contains the list of characters found.
+ unsigned int tick = gettick();
+
+ nullpo_retr(0, bl);
+ nullpo_retr(0, ap);
+ nullpo_retr(0, tsd=(struct map_session_data*)bl);
+ nullpo_retr(0, src=va_arg(ap,struct block_list *));
+ nullpo_retr(0, sd=(struct map_session_data*)src);
+
+ c=va_arg(ap,int *);
+ p_sd = va_arg(ap, int *);
+ skillid = va_arg(ap,int);
+
+ if ((skillid != PR_BENEDICTIO && *c >=1) || *c >=2)
+ return 0; //Partner found for ensembles, or the two companions for Benedictio. [Skotlex]
+
+ if (bl == src)
+ return 0;
+
+ if(pc_isdead(tsd))
+ return 0;
+
+ if (tsd->sc_count && (tsd->sc_data[SC_SILENCE].timer != -1 || tsd->sc_data[SC_STAN].timer != -1))
+ return 0;
+
+ switch(skillid)
+ {
+ case PR_BENEDICTIO: /* ?ケ??~福 */
+ {
+ int dir = map_calc_dir(&sd->bl,tsd->bl.x,tsd->bl.y);
+ dir = (status_get_dir(&sd->bl) + dir)%8; //This adjusts dir to account for the direction the sd is facing.
+ if ((tsd->class_&MAPID_BASEMASK) == MAPID_ACOLYTE && (dir == 2 || dir == 6) //Must be standing to the left/right of Priest.
+ && sd->status.sp >= 10)
+ p_sd[(*c)++]=tsd->bl.id;
+ return 1;
+ }
+ default: //Warning: Assuming Ensemble Dance/Songs for code speed. [Skotlex]
+ {
+ int skilllv;
+ if(pc_issit(tsd) || tsd->skilltimer!=-1 || tsd->canmove_tick > tick)
+ return 0;
+ if (sd->status.sex != tsd->status.sex &&
+ (tsd->class_&MAPID_UPPERMASK) == MAPID_BARDDANCER &&
+ (skilllv = pc_checkskill(tsd, skillid)) > 0 &&
+ (tsd->weapontype1==13 || tsd->weapontype1==14) &&
+ sd->status.party_id && tsd->status.party_id &&
+ sd->status.party_id == tsd->status.party_id &&
+ tsd->sc_data[SC_DANCING].timer == -1)
+ {
+ p_sd[(*c)++]=tsd->bl.id;
+ return skilllv;
+ } else {
+ return 0;
+ }
+ }
+ break;
+ }
+ return 0;
+}
+
+/*==========================================
+ * Checks and stores partners for ensemble skills [Skotlex]
+ *------------------------------------------
+ */
+static int skill_check_pc_partner(struct map_session_data *sd, int skill_id, int* skill_lv, int range, int cast_flag)
+{
+ static int c=0;
+ static int p_sd[2] = { 0, 0 };
+ int i;
+ if (cast_flag)
+ { //Execute the skill on the partners.
+ struct map_session_data* tsd;
+ switch (skill_id)
+ {
+ case PR_BENEDICTIO:
+ for (i = 0; i < c; i++)
+ {
+ if ((tsd = map_id2sd(p_sd[i])) != NULL)
+ {
+ tsd->status.sp -= 10;
+ if (tsd->status.sp < 0)
+ tsd->status.sp = 0;
+ clif_updatestatus(tsd,SP_SP);
+ }
+ }
+ return c;
+ case CG_MOONLIT:
+ if (c > 0 && (tsd = map_id2sd(p_sd[0])) != NULL)
+ {
+ clif_skill_nodamage(&tsd->bl, &sd->bl, skill_id, *skill_lv, 1);
+ skill_moonlit(&sd->bl, &tsd->bl, *skill_lv);
+ tsd->skillid_dance = tsd->skillid = skill_id;
+ tsd->skilllv_dance = tsd->skilllv = *skill_lv;
+ }
+ return c;
+ default: //Warning: Assuming Ensemble skills here (for speed)
+ if (c > 0 && (tsd = map_id2sd(p_sd[0])) != NULL)
+ {
+ sd->sc_data[SC_DANCING].val4= tsd->bl.id;
+ status_change_start(&tsd->bl,SC_DANCING,skill_id,sd->sc_data[SC_DANCING].val2,0,sd->bl.id,skill_get_time(skill_id,*skill_lv)+1000,0);
+ clif_skill_nodamage(&tsd->bl, &sd->bl, skill_id, *skill_lv, 1);
+ tsd->skillid_dance = tsd->skillid = skill_id;
+ tsd->skilllv_dance = tsd->skilllv = *skill_lv;
+ }
+ return c;
+ }
+ }
+ //Else: new search for partners.
+ c = 0;
+ memset (p_sd, 0, sizeof(p_sd));
+ i = map_foreachinarea(skill_check_condition_char_sub, sd->bl.m,
+ sd->bl.x-range, sd->bl.y-range, sd->bl.x+range,
+ sd->bl.y+range, BL_PC, &sd->bl, &c, &p_sd, skill_id);
+
+ if (skill_id != PR_BENEDICTIO) //Apply the average lv to encore skills.
+ *skill_lv = (i+(*skill_lv))/(c+1); //I know c should be one, but this shows how it could be used for the average of n partners.
+ return c;
+}
+
+/*==========================================
+ * 範??バイオプラント?Aスフィアマイン用Mob存?ン確認判定??(foreachinarea)
+ *------------------------------------------
+ */
+
+static int skill_check_condition_mob_master_sub(struct block_list *bl,va_list ap)
+{
+ int *c,src_id=0,mob_class=0;
+ struct mob_data *md;
+
+ nullpo_retr(0, bl);
+ nullpo_retr(0, ap);
+ nullpo_retr(0, md=(struct mob_data*)bl);
+ nullpo_retr(0, src_id=va_arg(ap,int));
+ nullpo_retr(0, mob_class=va_arg(ap,int));
+ nullpo_retr(0, c=va_arg(ap,int *));
+
+ if(md->class_==mob_class && md->master_id==src_id)
+ (*c)++;
+ return 0;
+}
+
+static int skill_check_condition_hermod_sub(struct block_list *bl,va_list ap)
+{
+ int *c;
+ struct npc_data *nd;
+
+ nullpo_retr(0, bl);
+ nullpo_retr(0, ap);
+ nullpo_retr(0, nd=(struct npc_data*)bl);
+ nullpo_retr(0, c=va_arg(ap,int *));
+
+ if (nd->bl.subtype == WARP)
+ (*c)++;
+ return 0;
+}
+
+/*==========================================
+ * スキル使用???i?で使用失敗?j
+ *------------------------------------------
+ */
+int skill_check_condition(struct map_session_data *sd,int type)
+{
+ int i,hp,sp,hp_rate,sp_rate,zeny,weapon,state,spiritball,skill,lv,mhp;
+ int index[10],itemid[10],amount[10];
+ int arrow_flag = 0;
+ int force_gem_flag = 0;
+ int delitem_flag = 1, checkitem_flag = 1;
+
+ nullpo_retr(0, sd);
+
+ if( battle_config.gm_skilluncond>0 &&
+ pc_isGM(sd)>= battle_config.gm_skilluncond &&
+ sd->skillitem != sd->skillid)
+ { //GMs don't override the skillItem check, otherwise they can use items without them being consumed! [Skotlex]
+ sd->skillitem = sd->skillitemlv = -1;
+ return 1;
+ }
+
+ if( sd->opt1>0) {
+ clif_skill_fail(sd,sd->skillid,0,0);
+ sd->skillitem = sd->skillitemlv = -1;
+ return 0;
+ }
+ if(pc_is90overweight(sd)) {
+ clif_skill_fail(sd,sd->skillid,9,0);
+ sd->skillitem = sd->skillitemlv = -1;
+ return 0;
+ }
+
+ if (sd->state.abra_flag)
+ {
+ sd->skillitem = sd->skillitemlv = -1;
+ if(type&1) sd->state.abra_flag = 0;
+ return 1;
+ }
+
+ if (sd->state.produce_flag &&
+ (sd->skillid == AM_PHARMACY || sd->skillid == AC_MAKINGARROW || sd->skillid == BS_REPAIRWEAPON ||
+ sd->skillid == AM_TWILIGHT1 || sd->skillid == AM_TWILIGHT2 || sd->skillid == AM_TWILIGHT3
+ )) {
+ sd->skillitem = sd->skillitemlv = -1;
+ return 0;
+ }
+
+ if(sd->skillitem == sd->skillid) { /* アイテムの??無???ャ功 */
+ if(!type) //When a target was selected
+ { //Consume items that were skipped in pc_use_item [Skotlex]
+ if((i = sd->itemindex) == -1 ||
+ sd->status.inventory[i].nameid != sd->itemid ||
+ sd->inventory_data[i] == NULL ||
+ !sd->inventory_data[i]->flag.delay_consume ||
+ sd->status.inventory[i].amount < 1
+ )
+ { //Something went wrong, item exploit?
+ sd->itemid = sd->itemindex = -1;
+ return 0;
+ }
+ //Consume
+ sd->itemid = sd->itemindex = -1;
+ if(sd->skillid == WZ_EARTHSPIKE
+ && sd->sc_data[SC_TKDORI].timer != -1 && rand()%100 > sd->sc_data[SC_TKDORI].val2) // [marquis007]
+ ; //Do not consume item.
+ else
+ pc_delitem(sd,i,1,0);
+ }
+ if (type&1) //Casting finished
+ sd->skillitem = sd->skillitemlv = -1;
+ return 1;
+ }
+ if( sd->opt1>0 ){
+ clif_skill_fail(sd,sd->skillid,0,0);
+ return 0;
+ }
+ if(sd->sc_count){
+ if( sd->sc_data[SC_SILENCE].timer!=-1 ||
+ sd->sc_data[SC_ROKISWEIL].timer!=-1 ||
+ (sd->sc_data[SC_AUTOCOUNTER].timer != -1 && sd->skillid != KN_AUTOCOUNTER) ||
+ sd->sc_data[SC_STEELBODY].timer != -1 ||
+ sd->sc_data[SC_BERSERK].timer != -1 ||
+ (sd->sc_data[SC_MARIONETTE].timer != -1 && sd->skillid != CG_MARIONETTE)){
+ clif_skill_fail(sd,sd->skillid,0,0);
+ return 0; /* ?態異?や沈?など */
+ }
+ }
+
+ skill = sd->skillid;
+ lv = sd->skilllv;
+ if (lv <= 0) return 0;
+ // for the guild skills [celest]
+ if (skill >= 10000 && skill < 10015) skill-= 9500;
+ hp = skill_get_hp(skill, lv); /* ?チ費HP */
+ sp = skill_get_sp(skill, lv); /* ?チ費SP */
+ if((sd->skillid_old == BD_ENCORE) && skill == sd->skillid_dance)
+ sp=sp/2; //アンコ?ル時はSP?チ費が半分
+ hp_rate = (lv <= 0)? 0:skill_db[skill].hp_rate[lv-1];
+ sp_rate = (lv <= 0)? 0:skill_db[skill].sp_rate[lv-1];
+ zeny = skill_get_zeny(skill,lv);
+ weapon = skill_db[skill].weapon;
+ state = skill_db[skill].state;
+ spiritball = (lv <= 0)? 0:skill_db[skill].spiritball[lv-1];
+ mhp = skill_get_mhp(skill, lv); /* ?チ費HP */
+ for(i = 0; i < 10; i++) {
+ itemid[i] = skill_db[skill].itemid[i];
+ amount[i] = skill_db[skill].amount[i];
+ }
+ if(mhp > 0)
+ hp += (sd->status.max_hp * mhp)/100;
+ if(hp_rate > 0)
+ hp += (sd->status.hp * hp_rate)/100;
+ else
+ hp += (sd->status.max_hp * abs(hp_rate))/100;
+ if(sp_rate > 0)
+ sp += (sd->status.sp * sp_rate)/100;
+ else
+ sp += (sd->status.max_sp * abs(sp_rate))/100;
+
+ switch(skill) { // Check for cost reductions due to skills & SCs
+ case MC_MAMMONITE:
+ if(pc_checkskill(sd,BS_UNFAIRLYTRICK)>0)
+ zeny -= zeny*10/100;
+ break;
+ case AL_HOLYLIGHT:
+ if(sd->sc_data[SC_SPIRIT].timer!=-1 && sd->sc_data[SC_SPIRIT].val2 == SL_PRIEST)
+ sp *= 5;
+ break;
+ case SL_SMA:
+ case SL_STUN:
+ case SL_STIN:
+ {
+ int kaina_lv = pc_checkskill(sd,SL_KAINA);
+
+ if(kaina_lv==0 || sd->status.base_level<70)
+ break;
+ if(sd->status.base_level>=90)
+ sp -= sp*7*kaina_lv/100;
+ else if(sd->status.base_level>=80)
+ sp -= sp*5*kaina_lv/100;
+ else if(sd->status.base_level>=70)
+ sp -= sp*3*kaina_lv/100;
+ }
+ break;
+ case MO_TRIPLEATTACK:
+ case MO_CHAINCOMBO:
+ case MO_COMBOFINISH:
+ case CH_TIGERFIST:
+ case CH_CHAINCRUSH:
+ if(sd->sc_data[SC_SPIRIT].timer!=-1 && sd->sc_data[SC_SPIRIT].val2 == SL_MONK)
+ sp -= sp*25/100; //FIXME: Need real data. this is a custom value.
+ break;
+ }
+
+ if(sd->dsprate!=100)
+ sp=sp*sd->dsprate/100; /* ?チ費SP?C?ウ */
+
+ switch(skill) {
+ case SA_CASTCANCEL:
+ if(sd->skilltimer == -1) {
+ clif_skill_fail(sd,skill,0,0);
+ return 0;
+ }
+ break;
+ case BS_MAXIMIZE: /* マキシマイズパ?? */
+ case NV_TRICKDEAD: /* 死んだふり */
+ case TF_HIDING: /* ハイディング */
+ case AS_CLOAKING: /* ク??キング */
+ case CR_AUTOGUARD: /* オ?トガ?ド */
+ case CR_DEFENDER: /* ディフェンダ? */
+ case ST_CHASEWALK:
+ case PA_GOSPEL:
+ case CR_SHRINK:
+ if(sd->sc_data[SkillStatusChangeTable[skill]].timer!=-1)
+ return 1; /* 解?怩キる??はSP?チ費しない */
+ break;
+
+ case TK_RUN:
+ if(sd->sc_data[SC_RUN].timer!=-1){
+ status_change_end(&sd->bl,SC_RUN,-1);
+ if(sd->sc_data[SC_SPURT].timer!=-1)
+ status_change_end(&sd->bl,SC_SPURT,-1);
+ return 0;
+ }
+ break;
+
+ case AL_WARP:
+ if(!(type&2)) //Delete the item when the portal has been selected (type&2). [Skotlex]
+ delitem_flag = 0;
+ if(map[sd->bl.m].flag.nowarp) {
+ clif_skill_teleportmessage(sd,0);
+ return 0;
+ }
+ if(!battle_config.duel_allow_teleport && sd->duel_group) { // duel restriction [LuzZza]
+ clif_displaymessage(sd->fd, "Duel: Can't use warp in duel.");
+ return 0;
+ }
+ break;
+ case AL_TELEPORT:
+ if(map[sd->bl.m].flag.noteleport) {
+ clif_skill_teleportmessage(sd,0);
+ return 0;
+ }
+ break;
+ case MO_CALLSPIRITS: /* ?功 */
+ if(sd->spiritball >= lv) {
+ clif_skill_fail(sd,skill,0,0);
+ return 0;
+ }
+ break;
+ case CH_SOULCOLLECT: /* 狂?功 */
+ if(sd->spiritball >= 5) {
+ clif_skill_fail(sd,skill,0,0);
+ return 0;
+ }
+ break;
+ case MO_FINGEROFFENSIVE: //指?
+ if (sd->spiritball > 0 && sd->spiritball < spiritball) {
+ spiritball = sd->spiritball;
+ sd->spiritball_old = sd->spiritball;
+ }
+ else sd->spiritball_old = lv;
+ break;
+ case MO_BODYRELOCATION:
+ if (sd->sc_data[SC_EXPLOSIONSPIRITS].timer!=-1)
+ spiritball = 0;
+ break;
+ case MO_CHAINCOMBO: //連打?カ
+ if(sd->sc_data[SC_BLADESTOP].timer==-1){
+ if(sd->sc_data[SC_COMBO].timer == -1 || sd->sc_data[SC_COMBO].val1 != MO_TRIPLEATTACK)
+ return 0;
+ }
+ break;
+ case MO_COMBOFINISH: //猛龍?
+ if(sd->sc_data[SC_COMBO].timer == -1 || sd->sc_data[SC_COMBO].val1 != MO_CHAINCOMBO)
+ return 0;
+ break;
+ case CH_TIGERFIST: //伏虎?
+ if(sd->sc_data[SC_COMBO].timer == -1 || sd->sc_data[SC_COMBO].val1 != MO_COMBOFINISH)
+ return 0;
+ break;
+ case CH_CHAINCRUSH: //連柱崩?
+ if(sd->sc_data[SC_COMBO].timer == -1)
+ return 0;
+ if(sd->sc_data[SC_COMBO].val1 != MO_COMBOFINISH && sd->sc_data[SC_COMBO].val1 != CH_TIGERFIST)
+ return 0;
+ break;
+ case MO_EXTREMITYFIST: // 阿?C羅覇鳳?
+// if(sd->sc_data[SC_EXTREMITYFIST].timer != -1) //To disable Asura during the 5 min skill block uncomment this...
+// return 0;
+ if(sd->sc_data[SC_BLADESTOP].timer!=-1)
+ spiritball--;
+ else if (sd->sc_data[SC_COMBO].timer != -1) {
+ if (sd->sc_data[SC_COMBO].val1 == MO_COMBOFINISH)
+ spiritball = 4;
+ else if (sd->sc_data[SC_COMBO].val1 == CH_TIGERFIST)
+ spiritball = 3;
+ else if (sd->sc_data[SC_COMBO].val1 == CH_CHAINCRUSH)
+ spiritball = sd->spiritball?sd->spiritball:1;
+ //It should consume whatever is left as long as it's at least 1.
+ }
+ break;
+
+ case TK_MISSION: //Does not works on Non-Taekwon
+ if ((sd->class_&MAPID_UPPERMASK) != MAPID_TAEKWON) {
+ clif_skill_fail(sd,skill,0,0);
+ return 0;
+ }
+ break;
+
+ case TK_READYCOUNTER:
+ case TK_READYDOWN:
+ case TK_READYSTORM:
+ case TK_READYTURN:
+ case TK_JUMPKICK:
+ if ((sd->class_&MAPID_UPPERMASK) == MAPID_SOUL_LINKER) {
+ //They do not work on Soul Linkers.
+ clif_skill_fail(sd,skill,0,0);
+ return 0;
+ }
+ break;
+
+ case TK_TURNKICK:
+ case TK_STORMKICK:
+ case TK_DOWNKICK:
+ case TK_COUNTER:
+ if(sd->sc_data[SC_COMBO].timer != -1 && sd->sc_data[SC_COMBO].val1 == skill)
+ break; //Combo ready.
+ if (pc_istop10fame(sd->char_id,MAPID_TAEKWON))
+ break; //Unlimited Combo
+ return 0;
+ case BD_ADAPTATION: /* アドリブ */
+ {
+ struct skill_unit_group *group=NULL;
+ if(sd->sc_data[SC_DANCING].timer==-1 || ((group=(struct skill_unit_group*)sd->sc_data[SC_DANCING].val2) && (skill_get_time(sd->sc_data[SC_DANCING].val1,group->skill_lv) - sd->sc_data[SC_DANCING].val3*1000) <= skill_get_time2(skill,lv))){ //ダンス中で使用後5秒以?繧フみ?H
+ clif_skill_fail(sd,skill,0,0);
+ return 0;
+ }
+ }
+ break;
+ case PR_BENEDICTIO: /* ?ケ??~福 */
+ {
+ if (!battle_config.player_skill_partner_check)
+ break; //No need to do any partner checking [Skotlex]
+ if (!(type&1))
+ { //Started casting.
+ if (skill_check_pc_partner(sd, skill, &lv, 1, 0) < 2)
+ {
+ clif_skill_fail(sd,skill,0,0);
+ return 0;
+ }
+ }
+ else
+ { //Done casting
+ //Should I repeat the check? If so, it would be best to only do this on cast-ending. [Skotlex]
+ skill_check_pc_partner(sd, skill, &lv, 1, 1);
+ }
+ }
+ break;
+ case WE_CALLPARTNER: /* あなたに逢いたい */
+ if(!sd->status.partner_id){
+ clif_skill_fail(sd,skill,0,0);
+ return 0;
+ }
+ break;
+ case AM_CANNIBALIZE: /* バイオプラント */
+ case AM_SPHEREMINE: /* スフィア?マイン */
+ if(type&1){
+ int c=0;
+ int summons[5] = { 1020, 1068, 1118, 1500, 1368 };
+ int maxcount = (skill==AM_CANNIBALIZE)? 6-lv : skill_get_maxcount(skill);
+ int mob_class = (skill==AM_CANNIBALIZE)? summons[lv-1] :1142;
+ if(battle_config.pc_land_skill_limit && maxcount>0) {
+ map_foreachinmap(skill_check_condition_mob_master_sub ,sd->bl.m, BL_MOB, sd->bl.id, mob_class,&c );
+ if(c >= maxcount){
+ clif_skill_fail(sd,skill,0,0);
+ return 0;
+ }
+ }
+ }
+ break;
+ case MG_FIREWALL: /* ファイア?ウォ?ル */
+ case WZ_QUAGMIRE:
+ case PF_FOGWALL:
+ /* ??ァ限 */
+ if(battle_config.pc_land_skill_limit) {
+ int maxcount = skill_get_maxcount(skill);
+ if(maxcount > 0) {
+ int i,c;
+ for(i=c=0;i<MAX_SKILLUNITGROUP;i++) {
+ if(sd->skillunit[i].alive_count > 0 && sd->skillunit[i].skill_id == skill)
+ c++;
+ }
+ if(c >= maxcount) {
+ clif_skill_fail(sd,skill,0,0);
+ return 0;
+ }
+ }
+ }
+ break;
+ case WZ_FIREPILLAR: // celest
+ if (lv <= 5) // no gems required at level 1-5
+ itemid[0] = 0;
+ if(battle_config.pc_land_skill_limit) {
+ int maxcount = skill_get_maxcount(skill);
+ if(maxcount > 0) {
+ int i,c;
+ for(i=c=0;i<MAX_SKILLUNITGROUP;i++) {
+ if(sd->skillunit[i].alive_count > 0 && sd->skillunit[i].skill_id == skill)
+ c++;
+ }
+ if(c >= maxcount) {
+ clif_skill_fail(sd,skill,0,0);
+ return 0;
+ }
+ }
+ }
+ break;
+ case SL_SMA:
+ if(type) break; //Only do the combo check when the target is selected (type == 0)
+ if(sd->sc_data[SC_COMBO].timer == -1 || sd->sc_data[SC_COMBO].val1 != skill)
+ return 0;
+ break;
+ // skills require arrows as of 12/07 [celest]
+ case HT_POWER:
+ if(sd->sc_data[SC_COMBO].timer == -1 || sd->sc_data[SC_COMBO].val1 != skill)
+ return 0;
+ case AC_DOUBLE:
+ case AC_SHOWER:
+ case AC_CHARGEARROW:
+ case BA_MUSICALSTRIKE:
+ case DC_THROWARROW:
+ case SN_SHARPSHOOTING:
+ case CG_ARROWVULCAN:
+ arrow_flag = 1; //Venom Knife does not gets the arrow deleted because
+ //it gets deleted as part of the skill requirements. [Skotlex]
+ case AS_VENOMKNIFE:
+ if(sd->equip_index[10] < 0) {
+ clif_arrow_fail(sd,0);
+ return 0;
+ }
+ break;
+ case RG_BACKSTAP:
+ if(sd->status.weapon == 11) {
+ if (sd->equip_index[10] < 0) {
+ clif_arrow_fail(sd,0);
+ return 0;
+ }
+ arrow_flag = 1;
+ }
+ break;
+ case HW_GANBANTEIN:
+ force_gem_flag = 1;
+ break;
+ case AM_POTIONPITCHER:
+ case CR_SLIMPITCHER:
+ case MG_STONECURSE:
+ case CR_CULTIVATION:
+ case SA_FLAMELAUNCHER:
+ case SA_FROSTWEAPON:
+ case SA_LIGHTNINGLOADER:
+ case SA_SEISMICWEAPON:
+ delitem_flag = 0;
+ break;
+ case SA_DELUGE:
+ case SA_VOLCANO:
+ case SA_VIOLENTGALE:
+ case SA_LANDPROTECTOR:
+ { //Does not consumes if the skill is already active. [Skotlex]
+ struct skill_unit_group *sg;
+ if ((sg= skill_locate_element_field(&sd->bl)) != NULL && sg->skill_id == skill)
+ {
+ if (sg->limit - DIFF_TICK(gettick(), sg->tick) > 0)
+ checkitem_flag = delitem_flag = 0;
+ else sg->limit = 0; //Disable it.
+ }
+ break;
+ }
+ case CG_HERMODE:
+ {
+ int c = 0;
+ map_foreachinarea (skill_check_condition_hermod_sub, sd->bl.m,
+ sd->bl.x-3, sd->bl.y-3, sd->bl.x+3, sd->bl.y+3, BL_NPC, &c);
+ if (c < 1) {
+ clif_skill_fail(sd,skill,0,0);
+ return 0;
+ }
+ }
+ break;
+ case CG_MOONLIT: //Check there's no wall in the range+1 area around the caster. [Skotlex]
+ {
+ int i,x,y,range = skill_get_range2(&sd->bl, skill, lv)+1;
+ int size = range*2+1;
+ for (i=0;i<size*size;i++) {
+ x = sd->bl.x+(i%size-range);
+ y = sd->bl.y+(i/size-range);
+ if (map_getcell(sd->bl.m,x,y,CELL_CHKWALL)) {
+ clif_skill_fail(sd,skill,0,0);
+ return 0;
+ }
+ }
+ }
+ break;
+ case PR_REDEMPTIO:
+ {
+ int exp;
+ if(((exp = pc_nextbaseexp(sd)) > 0 && sd->status.base_exp*100/exp < 1) ||
+ ((exp = pc_nextjobexp(sd)) > 0 && sd->status.job_exp*100/exp < 1)) {
+ clif_skill_fail(sd,skill,0,0); //Not enough exp.
+ return 0;
+ }
+ break;
+ }
+ case AM_TWILIGHT2:
+ case AM_TWILIGHT3:
+ if (!party_skill_check(sd, sd->status.party_id, skill, lv))
+ {
+ clif_skill_fail(sd,skill,0,0);
+ return 0;
+ }
+ case SG_SUN_WARM:
+ if(sd->bl.m == sd->feel_map[0].m)
+ break;
+ clif_skill_fail(sd,skill,0,0);
+ return 0;
+ break;
+ case SG_MOON_WARM:
+ if(sd->bl.m == sd->feel_map[1].m)
+ break;
+ clif_skill_fail(sd,skill,0,0);
+ return 0;
+ break;
+ case SG_STAR_WARM:
+ if(sd->bl.m == sd->feel_map[2].m)
+ break;
+ clif_skill_fail(sd,skill,0,0);
+ return 0;
+ break;
+ case SG_SUN_COMFORT:
+ if(sd->bl.m == sd->feel_map[0].m && (battle_config.allow_skill_without_day || is_day_of_sun()))
+ break;
+ clif_skill_fail(sd,skill,0,0);
+ return 0;
+ case SG_MOON_COMFORT:
+ if(sd->bl.m == sd->feel_map[1].m && (battle_config.allow_skill_without_day || is_day_of_moon()))
+ break;
+ clif_skill_fail(sd,skill,0,0);
+ return 0;
+ case SG_STAR_COMFORT:
+ if(sd->bl.m == sd->feel_map[2].m && (battle_config.allow_skill_without_day || is_day_of_star()))
+ break;
+ clif_skill_fail(sd,skill,0,0);
+ return 0;
+ case SG_FUSION:
+ if ((sd->sc_data[SC_SPIRIT].timer != -1 && sd->sc_data[SC_SPIRIT].val2 == SL_STAR) || sd->sc_data[SC_FUSION].timer != -1)
+ break;
+ return 0;
+ }
+
+ if(!(type&2)){
+ if( hp>0 && sd->status.hp < hp) { /* HPチェック */
+ clif_skill_fail(sd,skill,2,0); /* HP不足?F失敗通知 */
+ return 0;
+ }
+ if( sp>0 && sd->status.sp < sp) { /* SPチェック */
+ clif_skill_fail(sd,skill,1,0); /* SP不足?F失敗通知 */
+ return 0;
+ }
+ if( zeny>0 && sd->status.zeny < zeny) {
+ clif_skill_fail(sd,skill,5,0);
+ return 0;
+ }
+ if(!(weapon & (1<<sd->status.weapon) ) ) {
+ clif_skill_fail(sd,skill,6,0);
+ return 0;
+ }
+ if( spiritball > 0 && sd->spiritball < spiritball) {
+ clif_skill_fail(sd,skill,0,0); // 氣球不足
+ return 0;
+ }
+ }
+
+ switch(state) {
+ case ST_HIDING:
+ if(!(sd->status.option&OPTION_HIDE)) {
+ clif_skill_fail(sd,skill,0,0);
+ return 0;
+ }
+ break;
+ case ST_CLOAKING:
+ if(!pc_iscloaking(sd)) {
+ clif_skill_fail(sd,skill,0,0);
+ return 0;
+ }
+ break;
+ case ST_HIDDEN:
+ if(!pc_ishiding(sd)) {
+ clif_skill_fail(sd,skill,0,0);
+ return 0;
+ }
+ break;
+ case ST_RIDING:
+ if(!pc_isriding(sd)) {
+ clif_skill_fail(sd,skill,0,0);
+ return 0;
+ }
+ break;
+ case ST_FALCON:
+ if(!pc_isfalcon(sd)) {
+ clif_skill_fail(sd,skill,0,0);
+ return 0;
+ }
+ break;
+ case ST_CART:
+ if(!pc_iscarton(sd)) {
+ clif_skill_fail(sd,skill,0,0);
+ return 0;
+ }
+ break;
+ case ST_SHIELD:
+ if(sd->status.shield <= 0) {
+ clif_skill_fail(sd,skill,0,0);
+ return 0;
+ }
+ break;
+ case ST_SIGHT:
+ if(sd->sc_data[SC_SIGHT].timer == -1 && type&1) {
+ clif_skill_fail(sd,skill,0,0);
+ return 0;
+ }
+ break;
+ case ST_EXPLOSIONSPIRITS:
+ if(sd->sc_data[SC_EXPLOSIONSPIRITS].timer == -1) {
+ clif_skill_fail(sd,skill,0,0);
+ return 0;
+ }
+ break;
+ case ST_CARTBOOST:
+ if(!pc_iscarton(sd) || sd->sc_data[SC_CARTBOOST].timer == -1) {
+ clif_skill_fail(sd,skill,0,0);
+ return 0;
+ }
+ break;
+ case ST_RECOV_WEIGHT_RATE:
+ if(battle_config.natural_heal_weight_rate <= 100 && sd->weight*100/sd->max_weight >= battle_config.natural_heal_weight_rate) {
+ clif_skill_fail(sd,skill,0,0);
+ return 0;
+ }
+ break;
+ case ST_MOVE_ENABLE:
+ {
+ struct walkpath_data wpd;
+ if(path_search(&wpd,sd->bl.m,sd->bl.x,sd->bl.y,sd->skillx,sd->skilly,1)==-1) {
+ clif_skill_fail(sd,skill,0,0);
+ return 0;
+ }
+ }
+ break;
+ case ST_WATER:
+ //??齡サ定
+ //(!map[sd->bl.m].flag.rain) && //they have removed RAIN effect. [Lupus]
+ if ( (sd->sc_data[SC_DELUGE].timer == -1) &&
+ (!map_getcell(sd->bl.m,sd->bl.x,sd->bl.y,CELL_CHKWATER)))
+ {
+ clif_skill_fail(sd,skill,0,0);
+ return 0;
+ }
+ break;
+
+ }
+
+ if (checkitem_flag) {
+ for(i=0;i<10;i++) {
+ int x = lv%11 - 1;
+ index[i] = -1;
+ if(itemid[i] <= 0)
+ continue;
+ if(itemid[i] >= 715 && itemid[i] <= 717 && sd->special_state.no_gemstone && !force_gem_flag)
+ continue;
+ if(((itemid[i] >= 715 && itemid[i] <= 717) || itemid[i] == 1065)
+ && sd->sc_data[SC_INTOABYSS].timer != -1 && !force_gem_flag)
+ continue;
+ if((skill == AM_POTIONPITCHER ||
+ skill == CR_SLIMPITCHER ||
+ skill == CR_CULTIVATION) && i != x)
+ continue;
+
+ index[i] = pc_search_inventory(sd,itemid[i]);
+ if(index[i] < 0 || sd->status.inventory[index[i]].amount < amount[i]) {
+ if(itemid[i] == 716 || itemid[i] == 717)
+ clif_skill_fail(sd,skill,(7+(itemid[i]-716)),0);
+ else
+ clif_skill_fail(sd,skill,0,0);
+ return 0;
+ }
+ if((itemid[i] >= 715 && itemid[i] <= 717) && sd->sc_data[SC_SPIRIT].timer != -1 && sd->sc_data[SC_SPIRIT].val2 == SL_WIZARD)
+ index[i] = -1; //Gemstones are checked, but not substracted from inventory.
+
+ }
+ }
+
+ if(!(type&1))
+ return 1;
+
+ if(delitem_flag) {
+ for(i=0;i<10;i++) {
+ if(index[i] >= 0)
+ pc_delitem(sd,index[i],amount[i],0); // アイテム?チ費
+ }
+ if (arrow_flag && battle_config.arrow_decrement)
+ pc_delitem(sd,sd->equip_index[10],1,0);
+ }
+
+ if(type&2)
+ return 1;
+
+ if(sp > 0) { // SP?チ費
+ sd->status.sp-=sp;
+ clif_updatestatus(sd,SP_SP);
+ }
+ if(hp > 0) { // HP?チ費
+ sd->status.hp-=hp;
+ clif_updatestatus(sd,SP_HP);
+ }
+ if(zeny > 0) // Zeny?チ費
+ pc_payzeny(sd,zeny);
+ if(spiritball > 0) // 氣球?チ費
+ pc_delspiritball(sd,spiritball,0);
+
+
+ return 1;
+}
+
+/*==========================================
+ * 詠?・時間計算
+ *------------------------------------------
+ */
+int skill_castfix( struct block_list *bl, int skill_id, int skill_lv, int time)
+{
+ struct status_change *sc_data;
+
+ nullpo_retr(0, bl);
+
+ if (!time && bl->type != BL_MOB)
+ time = skill_get_cast(skill_id, skill_lv);
+
+ if (bl->type == BL_PC){
+ struct map_session_data *sd = (struct map_session_data*)bl;
+ nullpo_retr(0, sd);
+
+ // calculate base cast time (reduced by dex)
+ if (!skill_get_castnodex(sd->skillid, sd->skilllv) > 0) {
+ int scale = battle_config.castrate_dex_scale - status_get_dex(bl);
+ if (scale > 0) // not instant cast
+ time = time * scale / battle_config.castrate_dex_scale;
+ else return 0; // instant cast
+ }
+
+ // config cast time multiplier
+ if (battle_config.cast_rate != 100)
+ time = time * battle_config.cast_rate / 100;
+
+ // calculate cast time reduced by card bonuses
+ if (sd->castrate != 100)
+ time -= time * (100 - sd->castrate) / 100;
+ } else if (bl->type == BL_PET) { //Skotlex: Simple scaling
+ int scale = battle_config.castrate_dex_scale - status_get_dex(bl);
+ if (scale > 0) // not instant cast
+ time = time * scale / battle_config.castrate_dex_scale;
+ else return 0; // instant cast
+
+ if (battle_config.cast_rate != 100)
+ time = time * battle_config.cast_rate / 100;
+ }
+
+ // calculate cast time reduced by skill bonuses
+ sc_data = status_get_sc_data(bl);
+ /* サフラギウム */
+ if (sc_data) {
+ if (sc_data[SC_SUFFRAGIUM].timer != -1) {
+ time -= time * (sc_data[SC_SUFFRAGIUM].val1 * 15) / 100;
+ status_change_end(bl, SC_SUFFRAGIUM, -1);
+ }
+ /* ブラギの? */
+ if (sc_data[SC_POEMBRAGI].timer != -1)
+ time -= time * sc_data[SC_POEMBRAGI].val2 / 100;
+ }
+
+ // return final cast time
+ return (time > 0) ? time : 0;
+}
+/*==========================================
+ * ディレイ計算
+ *------------------------------------------
+ */
+int skill_delayfix( struct block_list *bl, int skill_id, int skill_lv, int time )
+{
+ struct status_change *sc_data;
+
+ nullpo_retr(0, bl);
+
+ if (!time && bl->type != BL_MOB)
+ time = skill_get_delay(skill_id, skill_lv);
+
+ if (bl->type == BL_PC){
+ struct map_session_data *sd = (struct map_session_data*)bl;
+ nullpo_retr(0, sd);
+
+ // instant cast attack skills depend on aspd as delay [celest]
+ if (time == 0) {
+ if (skill_get_type(skill_id) == BF_WEAPON)
+ time = status_get_amotion(bl); //Use attack animation as default delay.
+ else
+ time = 300; // default delay, according to official servers
+ } else if (time < 0)
+ time = abs(time) + status_get_amotion(bl); // if set to <0, the attack motion is added.
+
+ if (battle_config.delay_dependon_dex && /* dexの影響を計算する */
+ !skill_get_delaynodex(skill_id, skill_lv)) // if skill casttime is allowed to be reduced by dex
+ {
+ int scale = battle_config.castrate_dex_scale - status_get_dex(bl);
+ if (scale < 0)
+ scale = 0;
+ time = time * scale / battle_config.castrate_dex_scale;
+ }
+
+ if (battle_config.delay_rate != 100)
+ time = time * battle_config.delay_rate / 100;
+
+ if (sd->delayrate != 100)
+ time = time * sd->delayrate / 100;
+
+ if (time < battle_config.min_skill_delay_limit) // check minimum skill delay
+ time = battle_config.min_skill_delay_limit;
+ }
+
+ /* ブラギの? */
+ sc_data = status_get_sc_data(bl);
+ if (sc_data) {
+ if (sc_data[SC_POEMBRAGI].timer != -1)
+ time -= time * sc_data[SC_POEMBRAGI].val3 / 100;
+ if (sc_data[SC_SPIRIT].timer != -1)
+ switch (skill_id) {
+ case CR_SHIELDBOOMERANG:
+ if (sc_data[SC_SPIRIT].val2 == SL_CRUSADER)
+ time /=2;
+ break;
+ case AS_SONICBLOW:
+ if (!map_flag_gvg(bl->m) && sc_data[SC_SPIRIT].val2 == SL_ASSASIN)
+ time /= 2;
+ break;
+ }
+ }
+
+ return (time > 0) ? time : 0;
+}
+
+/*==========================================
+ * スキル使用?iID指定?j
+ *------------------------------------------
+ */
+int skill_use_id (struct map_session_data *sd, int target_id, int skill_num, int skill_lv)
+{
+ struct map_session_data* tsd = NULL;
+ struct block_list *bl = NULL;
+ struct status_change *sc_data;
+ int casttime, forcecast = 0;
+ unsigned int tick = gettick();
+
+ nullpo_retr(0, sd);
+
+ if (skill_lv <= 0)
+ return 0;
+
+ sc_data = sd->sc_data;
+ switch(skill_num)
+ { //Check for skills that auto-select target
+ case MO_CHAINCOMBO:
+ target_id = sd->attacktarget;
+ if (sc_data[SC_BLADESTOP].timer != -1){
+ if ((bl=(struct block_list *)sc_data[SC_BLADESTOP].val4) == NULL) //タ?ゲットがいない?
+ return 0;
+ target_id = bl->id;
+ }
+ break;
+ case MO_COMBOFINISH:
+ case CH_CHAINCRUSH:
+ case CH_TIGERFIST:
+ case TK_STORMKICK: // Taekwon kicks [Dralnu]
+ case TK_DOWNKICK:
+ case TK_TURNKICK:
+ target_id = sd->attacktarget;
+ break;
+
+ case TK_JUMPKICK:
+ case TK_COUNTER:
+ case HT_POWER:
+ if (sc_data[SC_COMBO].timer != -1 && sc_data[SC_COMBO].val1 == skill_num)
+ target_id = sc_data[SC_COMBO].val2;
+ else if (skill_num == TK_COUNTER) //This one is for Ranking TKers
+ target_id = sd->attacktarget;
+ else if (skill_num == HT_POWER)
+ return 0;
+ break;
+// -- moonsoul (altered to allow proper usage of extremity from new champion combos)
+//
+ case MO_EXTREMITYFIST: /*阿修羅覇鳳拳*/
+ if (sc_data[SC_COMBO].timer != -1 &&
+ (sc_data[SC_COMBO].val1 == MO_COMBOFINISH ||
+ sc_data[SC_COMBO].val1 == CH_TIGERFIST ||
+ sc_data[SC_COMBO].val1 == CH_CHAINCRUSH))
+ target_id = sd->attacktarget;
+ break;
+ case WE_MALE:
+ case WE_FEMALE:
+ if (!sd->status.partner_id)
+ return 0;
+ tsd = map_charid2sd(sd->status.partner_id);
+ bl = (struct block_list *)tsd;
+ if (bl)
+ target_id = bl->id;
+ else
+ {
+ clif_skill_fail(sd,skill_num,0,0);
+ return 0;
+ }
+ break;
+ case WE_CALLBABY:
+ tsd = pc_get_child(sd);
+ bl = (struct block_list *)tsd;
+ if (bl)
+ target_id = bl->id;
+ else
+ {
+ clif_skill_fail(sd,skill_num,0,0);
+ return 0;
+ }
+ break;
+ }
+ if (bl == NULL && (bl = map_id2bl(target_id)) == NULL)
+ return 0;
+
+ if (bl->type == BL_PC)
+ tsd = (struct map_session_data*)bl;
+
+ if (bl->prev == NULL) //Prevent targeting enemies that are not in the map. [Skotlex]
+ return 0;
+
+ if(sd->bl.m != bl->m)
+ return 0;
+
+ if(sd->skilltimer != -1 && skill_num != SA_CASTCANCEL) //Normally not needed because clif.c checks for it, but the at/char/script commands don't! [Skotlex]
+ return 0;
+
+ if(skillnotok(skill_num, sd)) // [MouseJstr]
+ return 0;
+ if (tsd && (skill_num == ALL_RESURRECTION || skill_num == PR_REDEMPTIO) && !pc_isdead(tsd))
+ return 0;
+
+ if(skill_get_inf2(skill_num)&INF2_NO_TARGET_SELF && sd->bl.id == target_id)
+ return 0;
+ if(!status_check_skilluse(&sd->bl, bl, skill_num, 0))
+ {
+ if(skill_num == PR_LEXAETERNA) //Eh.. assuming skill failed due to opponent frozen/stone-cursed. [Skotlex]
+ clif_skill_fail(sd,skill_num,0,0);
+ return 0;
+ }
+
+ //直前のスキルが何か?える必要のあるスキル
+ switch (skill_num) {
+ case SA_CASTCANCEL:
+ if (sd->skillid != skill_num){ //キャストキャンセル自?は?えない
+ sd->skillid_old = sd->skillid;
+ sd->skilllv_old = sd->skilllv;
+ }
+ break;
+
+ case BD_ENCORE: /* アンコ?ル */
+ if (!sd->skillid_dance) { //前回使用した踊りがないとだめ
+ clif_skill_fail(sd,skill_num,0,0);
+ return 0;
+ } else {
+ sd->skillid_old = skill_num;
+ }
+ break;
+
+ case GD_BATTLEORDER:
+ case GD_REGENERATION:
+ case GD_RESTORE:
+ case GD_EMERGENCYCALL:
+ {
+ if (!sd->status.guild_id || !sd->state.gmaster_flag)
+ return 0;
+ skill_lv = guild_checkskill(sd->state.gmaster_flag, skill_num);
+ if (skill_lv <= 0) return 0;
+ }
+ break;
+
+ case BD_LULLABY: /* 子守歌 */
+ case BD_RICHMANKIM: /* ニヨルドの宴 */
+ case BD_ETERNALCHAOS: /* 永遠の?ャ沌 */
+ case BD_DRUMBATTLEFIELD: /* ?太鼓の響き */
+ case BD_RINGNIBELUNGEN: /* ニ?ベルングの指輪 */
+ case BD_ROKISWEIL: /* ?キの叫び */
+ case BD_INTOABYSS: /* ?[淵の中に */
+ case BD_SIEGFRIED: /* 不死?gのジ?クフリ?ド */
+ case CG_MOONLIT: /* 月明りの?に落ちる花びら */
+ {
+ if (battle_config.player_skill_partner_check)
+ {
+ if (skill_check_pc_partner(sd, skill_num, &skill_lv, 1, 0) < 1) //Note that skill_lv is automatically updated.
+ {
+ clif_skill_fail(sd,skill_num,0,0);
+ return 0;
+ }
+ }
+ break;
+ }
+ }
+
+ sd->skillid = skill_num;
+ sd->skilllv = skill_lv;
+ if (!skill_check_condition(sd,0)) return 0;
+
+ if(sd->bl.id != target_id){ // Don't check range for self skills, this is useless...
+ if(!battle_check_range(&sd->bl,bl,skill_get_range2(&sd->bl, skill_num,skill_lv)+1))
+ return 0;
+ }
+
+ if ((skill_num != MO_CHAINCOMBO &&
+ skill_num != MO_COMBOFINISH &&
+ skill_num != MO_EXTREMITYFIST &&
+ skill_num != CH_TIGERFIST &&
+ skill_num != CH_CHAINCRUSH &&
+ skill_num != TK_STORMKICK &&
+ skill_num != TK_DOWNKICK &&
+ skill_num != TK_TURNKICK &&
+ skill_num != TK_COUNTER) ||
+ (skill_num == MO_EXTREMITYFIST && sd->state.skill_flag))
+ pc_stopattack(sd);
+
+ casttime = skill_castfix(&sd->bl, skill_num, skill_lv, 0);
+ sd->state.skillcastcancel = skill_get_castcancel(skill_num);
+
+ switch (skill_num) { /* 何か特殊な??が必要 */
+ case ALL_RESURRECTION: /* リザレクション */
+ if (battle_check_undead(status_get_race(bl),status_get_elem_type(bl))) { /* 敵がアンデッドなら */
+ forcecast = 1; /* タ?ンアンデットと同じ詠?・時間 */
+ casttime = skill_castfix(&sd->bl, PR_TURNUNDEAD, skill_lv, 0);
+ }
+ break;
+
+ case MO_FINGEROFFENSIVE: /* 指? */
+ casttime += casttime * ((skill_lv > sd->spiritball) ? sd->spiritball : skill_lv);
+ break;
+
+// -- moonsoul (altered to allow proper usage of extremity from new champion combos)
+//
+ case MO_EXTREMITYFIST: /*阿?C羅覇鳳?*/
+ if (sc_data && sc_data[SC_COMBO].timer != -1 &&
+ (sc_data[SC_COMBO].val1 == MO_COMBOFINISH ||
+ sc_data[SC_COMBO].val1 == CH_TIGERFIST ||
+ sc_data[SC_COMBO].val1 == CH_CHAINCRUSH))
+ casttime = 0;
+ forcecast = 1;
+ break;
+
+ case SA_MAGICROD:
+ case SA_SPELLBREAKER:
+ forcecast = 1;
+ break;
+
+ case KN_CHARGEATK:
+ casttime *= distance_bl(&sd->bl, bl);
+ break;
+
+ // parent-baby skills
+ case WE_BABY:
+ case WE_CALLPARENT:
+ {
+ struct map_session_data *f_sd = pc_get_father(sd);
+ struct map_session_data *m_sd = pc_get_mother(sd);
+
+ // set target as any one of the parent
+ if (f_sd) target_id = f_sd->bl.id;
+ else if (m_sd) target_id = m_sd->bl.id;
+ else return 0; // neither are found
+ }
+ break;
+ case HP_BASILICA: /* バジリカ */
+ {
+ // cancel Basilica if already in effect
+ if (sc_data && sc_data[SC_BASILICA].timer != -1 && sc_data[SC_BASILICA].val3 == BCT_SELF) {
+ status_change_end(&sd->bl,SC_BASILICA,-1);
+ return 0;
+ }
+ }
+ break;
+ }
+
+ //?モライズ?態ならキャストタイムが1/3
+ if (sc_data && sc_data[SC_MEMORIZE].timer != -1 && casttime > 0) {
+ casttime = casttime/2;
+ if ((--sc_data[SC_MEMORIZE].val2) <= 0)
+ status_change_end(&sd->bl, SC_MEMORIZE, -1);
+ }
+
+ if (battle_config.pc_skill_log)
+ ShowInfo("PC %d skill use target_id=%d skill=%d lv=%d cast=%d\n",
+ sd->bl.id, target_id, skill_num, skill_lv, casttime);
+
+ if (casttime > 0 || forcecast) {
+ struct mob_data *md;
+ int mode;
+ if(sd->disguise) { // [Valaris]
+ clif_skillcasting(&sd->bl,sd->bl.id, target_id, 0,0, skill_num,0);
+ clif_skillcasting(&sd->bl,-sd->bl.id, target_id, 0,0, skill_num,casttime);
+ }
+ else
+ clif_skillcasting(&sd->bl,sd->bl.id, target_id, 0,0, skill_num,casttime);
+ /* 詠?・反?モンスタ? */
+ if (bl->type == BL_MOB && (mode = status_get_mode(bl))&MD_CASTSENSOR && (md = (struct mob_data *)bl) &&
+ (!md->special_state.ai || skill_get_inf(skill_num) != INF_SUPPORT_SKILL) //Avoid having summons target master from supportive skills. [Skotlex]
+ ) {
+ switch (md->state.skillstate) {
+ case MSS_ANGRY:
+ case MSS_RUSH:
+ case MSS_FOLLOW:
+ if (!(mode&(MD_AGGRESSIVE|MD_ANGRY)))
+ break; //Only Aggressive mobs change target while chasing.
+ case MSS_IDLE:
+ case MSS_WALK:
+ md->target_id = sd->bl.id;
+ md->state.targettype = ATTACKABLE;
+ md->state.aggressive = (mode&MD_ANGRY)?1:0;
+ md->min_chase = md->db->range3;
+ }
+ }
+ }
+
+ if (!(battle_config.pc_cloak_check_type&2) &&
+ sc_data && sc_data[SC_CLOAKING].timer != -1 &&
+ sd->skillid != AS_CLOAKING)
+ status_change_end(&sd->bl,SC_CLOAKING,-1);
+
+ sd->skilltarget = target_id;
+ sd->skillx = 0;
+ sd->skilly = 0;
+ sd->canact_tick = tick + casttime + 100;
+ //Recycling forcecast to store the skill's level. [Skotlex]
+ sd->canmove_tick = tick + (casttime>0 && (forcecast = pc_checkskill(sd,SA_FREECAST)) > 0?0:casttime);
+
+ if (casttime > 0) {
+ sd->skilltimer = add_timer (tick + casttime, skill_castend_id, sd->bl.id, 0);
+ if (forcecast > 0)
+ status_quick_recalc_speed (sd, SA_FREECAST, forcecast, 1);
+ else
+ pc_stop_walking(sd,0);
+ } else {
+ sd->state.skillcastcancel = 0; /* 詠?・の無いものはキャンセルされない */
+ if (skill_num != SA_CASTCANCEL)
+ sd->skilltimer = -1;
+ skill_castend_id(sd->skilltimer,tick,sd->bl.id,0);
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * スキル使用?i??且w定?j
+ *------------------------------------------
+ */
+int skill_use_pos (struct map_session_data *sd, int skill_x, int skill_y, int skill_num, int skill_lv)
+{
+ struct block_list bl;
+ struct status_change *sc_data;
+ int casttime, skill = 0;
+ unsigned int tick = gettick();
+
+ nullpo_retr(0, sd);
+
+ if (pc_isdead(sd))
+ return 0;
+ if (skill_lv <= 0)
+ return 0;
+ if (sd->skilltimer != -1) //Normally not needed since clif.c checks for it, but at/char/script commands don't! [Skotlex]
+ return 0;
+ if (skillnotok(skill_num, sd)) // [MouseJstr]
+ return 0;
+ if (skill_num == WZ_ICEWALL && map[sd->bl.m].flag.noicewall && !map[sd->bl.m].flag.pvp && !map[sd->bl.m].flag.gvg) { // noicewall flag [Valaris]
+ clif_skill_fail(sd,sd->skillid,0,0);
+ return 0;
+ }
+ if (map_getcell(sd->bl.m, skill_x, skill_y, CELL_CHKNOPASS))
+ { //prevent casting ground targeted spells on non-walkable areas. [Skotlex]
+
+ clif_skill_fail(sd,skill_num,0,0);
+ return 0;
+ }
+
+ sc_data = sd->sc_data;
+
+ if (!status_check_skilluse(&sd->bl, NULL, skill_num, 0))
+ return 0;
+
+ sd->skillid = skill_num;
+ sd->skilllv = skill_lv;
+ sd->skillx = skill_x;
+ sd->skilly = skill_y;
+ if (!skill_check_condition(sd,0)) return 0;
+
+ /* 射程と?瘧Q物チェック */
+ bl.type = BL_NUL;
+ bl.m = sd->bl.m;
+ bl.x = skill_x;
+ bl.y = skill_y;
+
+ if(!battle_check_range(&sd->bl,&bl,skill_get_range2(&sd->bl, skill_num,skill_lv)+1))
+ return 0;
+
+/* Previous code body, left here in case we have to rollback. [Skotlex]
+ {
+ int check_range_flag = 0;
+
+ range = skill_get_range(skill_num,skill_lv);
+ if(range < 0)
+ range = status_get_range(&sd->bl) - (range + 1);
+ // be lenient if the skill was cast before we have moved to the correct position [Celest]
+ if (sd->walktimer != -1)
+ range ++;
+ else check_range_flag = 1;
+ if(!battle_check_range(&sd->bl,&bl,range)) {
+ if (check_range_flag && pc_can_move(sd) && battle_check_range(&sd->bl,&bl,range + 1)) {
+ int mask[8][2] = {{0,1},{-1,1},{-1,0},{-1,-1},{0,-1},{1,-1},{1,0},{1,1}};
+ int dir = map_calc_dir(&sd->bl,bl.x,bl.y);
+ pc_walktoxy (sd, sd->bl.x + mask[dir][0], sd->bl.y + mask[dir][1]);
+ } else
+ return 0;
+ }
+ }
+*/
+ pc_stopattack(sd);
+
+ casttime = skill_castfix(&sd->bl, skill_num, skill_lv, 0);
+ sd->state.skillcastcancel = skill_db[skill_num].castcancel;
+
+ if (battle_config.pc_skill_log)
+ ShowInfo("PC %d skill use target_pos=(%d,%d) skill=%d lv=%d cast=%d\n",
+ sd->bl.id, skill_x, skill_y, skill_num, skill_lv, casttime);
+
+ //?モライズ?態ならキャストタイムが1/3
+ if (sc_data && sc_data[SC_MEMORIZE].timer != -1 && casttime > 0){
+ casttime = casttime/3;
+ if ((--sc_data[SC_MEMORIZE].val2)<=0)
+ status_change_end(&sd->bl, SC_MEMORIZE, -1);
+ }
+
+ if( casttime>0 ) { /* 詠?・が必要 */
+ if(sd->disguise) { // [Valaris]
+ clif_skillcasting(&sd->bl,sd->bl.id, 0, skill_x,skill_y, skill_num,0);
+ clif_skillcasting(&sd->bl,-sd->bl.id, 0, skill_x,skill_y, skill_num,casttime);
+ }
+ else
+ clif_skillcasting(&sd->bl,sd->bl.id, 0, skill_x,skill_y, skill_num,casttime);
+ }
+
+ if (!(battle_config.pc_cloak_check_type&2) &&
+ sc_data && sc_data[SC_CLOAKING].timer != -1)
+ status_change_end(&sd->bl,SC_CLOAKING,-1);
+
+ sd->skilltarget = 0;
+ sd->canact_tick = tick + casttime + 100;
+ sd->canmove_tick = tick + (casttime>0 && (skill = pc_checkskill(sd,SA_FREECAST))>0?0:casttime);
+
+ if (casttime > 0) {
+ sd->skilltimer = add_timer(tick + casttime, skill_castend_pos, sd->bl.id, 0);
+ if (skill > 0)
+ status_quick_recalc_speed (sd, SA_FREECAST, skill, 1);
+ else
+ pc_stop_walking(sd,0);
+ } else {
+ sd->state.skillcastcancel = 0; /* 詠?・の無いものはキャンセルされない */
+ sd->skilltimer = -1;
+ skill_castend_pos(sd->skilltimer,tick,sd->bl.id,0);
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * スキル詠?・キャンセル
+ *------------------------------------------
+ */
+int skill_castcancel (struct block_list *bl, int type)
+{
+ int ret = 0;
+
+ nullpo_retr(0, bl);
+
+ if (bl->type == BL_PC) {
+ struct map_session_data *sd = (struct map_session_data *)bl;
+ unsigned long tick = gettick();
+ nullpo_retr(0, sd);
+ sd->canact_tick = tick;
+ sd->canmove_tick = tick;
+ if (sd->skilltimer != -1) {
+ if ((ret = pc_checkskill(sd,SA_FREECAST)) > 0) {
+ status_quick_recalc_speed(sd, SA_FREECAST, ret, 0); //Updated to use calc_speed [Skotlex]
+ }
+ if (!type) {
+ if (skill_get_inf( sd->skillid ) & INF_GROUND_SKILL)
+ ret = delete_timer( sd->skilltimer, skill_castend_pos );
+ else
+ ret = delete_timer( sd->skilltimer, skill_castend_id );
+ if (ret < 0)
+ ShowError("delete timer error : skillid : %d\n", sd->skillid);
+ } else {
+ if (skill_get_inf( sd->skillid_old ) & INF_GROUND_SKILL)
+ ret = delete_timer( sd->skilltimer, skill_castend_pos );
+ else
+ ret = delete_timer( sd->skilltimer, skill_castend_id );
+ if (ret < 0)
+ ShowError("delete timer error : (old) skillid : %d\n", sd->skillid_old);
+ }
+ sd->skillid = sd->skilllv = -1;
+ sd->skilltimer = -1;
+ clif_skillcastcancel(bl);
+ }
+ return 0;
+ } else if (bl->type == BL_MOB) {
+ struct mob_data *md = (struct mob_data *)bl;
+ nullpo_retr(0, md);
+ if (md->skilltimer != -1) {
+ if (skill_get_inf( md->skillid ) & INF_GROUND_SKILL)
+ ret = delete_timer( md->skilltimer, mobskill_castend_pos );
+ else
+ ret = delete_timer( md->skilltimer, mobskill_castend_id );
+ md->skillid = md->skilllv = -1;
+ md->skilltimer = -1;
+ clif_skillcastcancel(bl);
+ }
+ if (ret < 0)
+ ShowError("delete timer error : skillid : %d\n", md->skillid);
+ return 0;
+ } if (bl->type == BL_PET) {
+ struct pet_data *pd = (struct pet_data*)bl;
+ pd->state.casting_flag = 0;
+ clif_skillcastcancel(bl);
+ if (pd->timer != -1)
+ { //Free the data attached to casting. [Skotlex]
+ struct TimerData *td = get_timer(pd->timer);
+ if (td && td->data)
+ {
+ aFree((struct cast_end_delay*)td->data);
+ td->data = 0;
+ }
+ }
+ //The timer is not deleted as the pet's attack will be resumed.
+ return 0;
+ }
+
+ return 1;
+}
+/*=========================================
+ * ブランディッシュスピア ?炎範?決定
+ *----------------------------------------
+ */
+void skill_brandishspear_first(struct square *tc,int dir,int x,int y){
+
+ nullpo_retv(tc);
+
+ if(dir == 0){
+ tc->val1[0]=x-2;
+ tc->val1[1]=x-1;
+ tc->val1[2]=x;
+ tc->val1[3]=x+1;
+ tc->val1[4]=x+2;
+ tc->val2[0]=
+ tc->val2[1]=
+ tc->val2[2]=
+ tc->val2[3]=
+ tc->val2[4]=y-1;
+ }
+ else if(dir==2){
+ tc->val1[0]=
+ tc->val1[1]=
+ tc->val1[2]=
+ tc->val1[3]=
+ tc->val1[4]=x+1;
+ tc->val2[0]=y+2;
+ tc->val2[1]=y+1;
+ tc->val2[2]=y;
+ tc->val2[3]=y-1;
+ tc->val2[4]=y-2;
+ }
+ else if(dir==4){
+ tc->val1[0]=x-2;
+ tc->val1[1]=x-1;
+ tc->val1[2]=x;
+ tc->val1[3]=x+1;
+ tc->val1[4]=x+2;
+ tc->val2[0]=
+ tc->val2[1]=
+ tc->val2[2]=
+ tc->val2[3]=
+ tc->val2[4]=y+1;
+ }
+ else if(dir==6){
+ tc->val1[0]=
+ tc->val1[1]=
+ tc->val1[2]=
+ tc->val1[3]=
+ tc->val1[4]=x-1;
+ tc->val2[0]=y+2;
+ tc->val2[1]=y+1;
+ tc->val2[2]=y;
+ tc->val2[3]=y-1;
+ tc->val2[4]=y-2;
+ }
+ else if(dir==1){
+ tc->val1[0]=x-1;
+ tc->val1[1]=x;
+ tc->val1[2]=x+1;
+ tc->val1[3]=x+2;
+ tc->val1[4]=x+3;
+ tc->val2[0]=y-4;
+ tc->val2[1]=y-3;
+ tc->val2[2]=y-1;
+ tc->val2[3]=y;
+ tc->val2[4]=y+1;
+ }
+ else if(dir==3){
+ tc->val1[0]=x+3;
+ tc->val1[1]=x+2;
+ tc->val1[2]=x+1;
+ tc->val1[3]=x;
+ tc->val1[4]=x-1;
+ tc->val2[0]=y-1;
+ tc->val2[1]=y;
+ tc->val2[2]=y+1;
+ tc->val2[3]=y+2;
+ tc->val2[4]=y+3;
+ }
+ else if(dir==5){
+ tc->val1[0]=x+1;
+ tc->val1[1]=x;
+ tc->val1[2]=x-1;
+ tc->val1[3]=x-2;
+ tc->val1[4]=x-3;
+ tc->val2[0]=y+3;
+ tc->val2[1]=y+2;
+ tc->val2[2]=y+1;
+ tc->val2[3]=y;
+ tc->val2[4]=y-1;
+ }
+ else if(dir==7){
+ tc->val1[0]=x-3;
+ tc->val1[1]=x-2;
+ tc->val1[2]=x-1;
+ tc->val1[3]=x;
+ tc->val1[4]=x+1;
+ tc->val2[1]=y;
+ tc->val2[0]=y+1;
+ tc->val2[2]=y-1;
+ tc->val2[3]=y-2;
+ tc->val2[4]=y-3;
+ }
+
+}
+
+/*=========================================
+ * ブランディッシュスピア 方向判定 範??張
+ *-----------------------------------------
+ */
+void skill_brandishspear_dir(struct square *tc,int dir,int are){
+
+ int c;
+
+ nullpo_retv(tc);
+
+ for(c=0;c<5;c++){
+ if(dir==0){
+ tc->val2[c]+=are;
+ }else if(dir==1){
+ tc->val1[c]-=are; tc->val2[c]+=are;
+ }else if(dir==2){
+ tc->val1[c]-=are;
+ }else if(dir==3){
+ tc->val1[c]-=are; tc->val2[c]-=are;
+ }else if(dir==4){
+ tc->val2[c]-=are;
+ }else if(dir==5){
+ tc->val1[c]+=are; tc->val2[c]-=are;
+ }else if(dir==6){
+ tc->val1[c]+=are;
+ }else if(dir==7){
+ tc->val1[c]+=are; tc->val2[c]+=are;
+ }
+ }
+}
+
+/*==========================================
+ * Weapon Repair [Celest/DracoRPG]
+ *------------------------------------------
+ */
+void skill_repairweapon(struct map_session_data *sd, int idx)
+{
+ int material;
+ int materials[4] = { 1002, 998, 999, 756 };
+ struct item *item;
+
+ nullpo_retv(sd);
+ nullpo_retv(sd->repair_target);
+
+ if(idx==0xFFFF) // No item selected ('Cancel' clicked)
+ return;
+
+ item = &sd->repair_target->status.inventory[idx];
+
+ if(sd!=sd->repair_target && !battle_check_range(&sd->bl,&sd->repair_target->bl,skill_get_range2(&sd->bl, sd->skillid,sd->skilllv))){
+ clif_item_repaireffect(sd,item->nameid,1);
+ return;
+ }
+
+ if(idx >= 0 && idx < MAX_INVENTORY) {
+ if(item->nameid > 0 && item->attribute == 1 ) {
+ if (itemdb_type(item->nameid)==4)
+ material = materials [itemdb_wlv(item->nameid)-1]; // Lv1/2/3/4 weapons consume 1 Iron Ore/Iron/Steel/Rough Oridecon
+ else
+ material = materials [2]; // Armors consume 1 Steel
+ if (pc_search_inventory(sd,material) < 0 ) {
+ clif_skill_fail(sd,sd->skillid,0,0);
+ return;
+ }
+ item->attribute=0;
+ clif_equiplist(sd->repair_target);
+ pc_delitem(sd,pc_search_inventory(sd,material),1,0);
+ clif_item_repaireffect(sd,item->nameid,0);
+ if(sd!=sd->repair_target)
+ clif_item_repaireffect(sd->repair_target,item->nameid,0);
+ sd->repair_target=NULL;
+ }
+ }
+}
+
+/*==========================================
+ * Item Appraisal
+ *------------------------------------------
+ */
+void skill_identify(struct map_session_data *sd,int idx)
+{
+ int flag=1;
+
+ nullpo_retv(sd);
+
+ if(idx >= 0 && idx < MAX_INVENTORY) {
+ if(sd->status.inventory[idx].nameid > 0 && sd->status.inventory[idx].identify == 0 ){
+ flag=0;
+ sd->status.inventory[idx].identify=1;
+ }
+ }
+ clif_item_identified(sd,idx,flag);
+}
+
+/*==========================================
+ * Weapon Refine [Celest]
+ *------------------------------------------
+ */
+void skill_weaponrefine(struct map_session_data *sd,int idx)
+{
+ int i = 0, ep = 0, per;
+ int material[5] = { 0, 1010, 1011, 984, 984 };
+ struct item *item;
+
+ nullpo_retv(sd);
+
+ if (idx >= 0 && idx < MAX_INVENTORY) {
+ struct item_data *ditem = sd->inventory_data[idx];
+ item = &sd->status.inventory[idx];
+
+ if(item->nameid > 0 && ditem->type == 4) {
+ if (item->refine >= sd->skilllv ||
+ item->refine >= MAX_REFINE || // if it's no longer refineable
+ ditem->flag.no_refine || // if the item isn't refinable
+ (i = pc_search_inventory(sd, material [ditem->wlv])) < 0 ) { //fixed by Lupus (item pos can be = 0!)
+ clif_skill_fail(sd,sd->skillid,0,0);
+ return;
+ }
+
+ per = percentrefinery [ditem->wlv][(int)item->refine];
+ per += (sd->status.job_level-50)/2; //Updated per the new kro descriptions. [Skotlex]
+
+ if (per > rand() % 100) {
+ item->refine++;
+ pc_delitem(sd, i, 1, 0);
+ if(item->equip) {
+ ep = item->equip;
+ pc_unequipitem(sd,idx,3);
+ }
+ clif_refine(sd->fd,sd,0,idx,item->refine);
+ clif_delitem(sd,idx,1);
+ clif_additem(sd,idx,1,0);
+ if (ep)
+ pc_equipitem(sd,idx,ep);
+ clif_misceffect(&sd->bl,3);
+ if(item->refine == MAX_REFINE && item->card[0] == 0x00ff && MakeDWord(item->card[2],item->card[3]) == sd->char_id){ // Fame point system [DracoRPG]
+ switch(ditem->wlv){
+ case 1:
+ pc_addfame(sd,1); // Success to refine to +10 a lv1 weapon you forged = +1 fame point
+ break;
+ case 2:
+ pc_addfame(sd,25); // Success to refine to +10 a lv2 weapon you forged = +25 fame point
+ break;
+ case 3:
+ pc_addfame(sd,1000); // Success to refine to +10 a lv3 weapon you forged = +1000 fame point
+ break;
+ }
+ }
+ } else {
+ pc_delitem(sd, i, 1, 0);
+ item->refine = 0;
+ if(item->equip)
+ pc_unequipitem(sd,idx,3);
+ clif_refine(sd->fd,sd,1,idx,item->refine);
+ pc_delitem(sd,idx,1,0);
+ clif_misceffect(&sd->bl,2);
+ clif_emotion(&sd->bl, 23);
+ }
+ }
+ }
+}
+
+/*==========================================
+ * オ?トスペル
+ *------------------------------------------
+ */
+int skill_autospell(struct map_session_data *sd,int skillid)
+{
+ int skilllv;
+ int maxlv=1,lv;
+
+ nullpo_retr(0, sd);
+
+ skilllv = pc_checkskill(sd,SA_AUTOSPELL);
+ if(skilllv <= 0) return 0;
+
+ if(skillid==MG_NAPALMBEAT) maxlv=3;
+ else if(skillid==MG_COLDBOLT || skillid==MG_FIREBOLT || skillid==MG_LIGHTNINGBOLT){
+ if (sd->sc_data[SC_SPIRIT].timer != -1 && sd->sc_data[SC_SPIRIT].val2 == SL_SAGE)
+ maxlv =10; //Soul Linker bonus. [Skotlex]
+ else if(skilllv==2) maxlv=1;
+ else if(skilllv==3) maxlv=2;
+ else if(skilllv>=4) maxlv=3;
+ }
+ else if(skillid==MG_SOULSTRIKE){
+ if(skilllv==5) maxlv=1;
+ else if(skilllv==6) maxlv=2;
+ else if(skilllv>=7) maxlv=3;
+ }
+ else if(skillid==MG_FIREBALL){
+ if(skilllv==8) maxlv=1;
+ else if(skilllv>=9) maxlv=2;
+ }
+ else if(skillid==MG_FROSTDIVER) maxlv=1;
+ else return 0;
+
+ if(maxlv > (lv=pc_checkskill(sd,skillid)))
+ maxlv = lv;
+
+ status_change_start(&sd->bl,SC_AUTOSPELL,skilllv,skillid,maxlv,0, // val1:スキルID val2:使用?ナ大Lv
+ skill_get_time(SA_AUTOSPELL,skilllv),0);// にしてみたけどbscriptが?曹ォ易い????H
+ return 0;
+}
+
+/*==========================================
+ * ギャングスタ?パラダイス判定??(foreachinarea)
+ *------------------------------------------
+ */
+
+static int skill_gangster_count(struct block_list *bl,va_list ap)
+{
+ int *c;
+ struct map_session_data *sd;
+ nullpo_retr(0, bl);
+ nullpo_retr(0, ap);
+
+ sd=(struct map_session_data*)bl;
+ c=va_arg(ap,int *);
+
+ if(sd && c && pc_issit(sd) && pc_checkskill(sd,RG_GANGSTER) > 0)
+ (*c)++;
+ return 0;
+}
+
+static int skill_gangster_in(struct block_list *bl,va_list ap)
+{
+ struct map_session_data *sd;
+ nullpo_retr(0, bl);
+ nullpo_retr(0, ap);
+
+ sd=(struct map_session_data*)bl;
+ if(sd && pc_issit(sd) && pc_checkskill(sd,RG_GANGSTER) > 0)
+ sd->state.gangsterparadise=1;
+ return 0;
+}
+
+static int skill_gangster_out(struct block_list *bl,va_list ap)
+{
+ struct map_session_data *sd;
+ nullpo_retr(0, bl);
+ nullpo_retr(0, ap);
+
+ sd=(struct map_session_data*)bl;
+ if(sd && sd->state.gangsterparadise)
+ sd->state.gangsterparadise=0;
+ return 0;
+}
+
+int skill_gangsterparadise(struct map_session_data *sd ,int type)
+{
+ int range=1;
+ int c=0;
+ nullpo_retr(0, sd);
+
+ if(pc_checkskill(sd,RG_GANGSTER) <= 0)
+ return 0;
+
+ if(type==1) {/* ?タった時の?? */
+ map_foreachinarea(skill_gangster_count,sd->bl.m,
+ sd->bl.x-range,sd->bl.y-range,
+ sd->bl.x+range,sd->bl.y+range,BL_PC,&c);
+ if(c > 1) {/*ギャングスタ??ャ功したら自分にもギャングスタ???ォ付?*/
+ map_foreachinarea(skill_gangster_in,sd->bl.m,
+ sd->bl.x-range,sd->bl.y-range,
+ sd->bl.x+range,sd->bl.y+range,BL_PC);
+ sd->state.gangsterparadise = 1;
+ }
+ return 0;
+ }
+ else if(type==0) {/* 立ち?繧ェったときの?? */
+ map_foreachinarea(skill_gangster_count,sd->bl.m,
+ sd->bl.x-range,sd->bl.y-range,
+ sd->bl.x+range,sd->bl.y+range,BL_PC,&c);
+ if(c < 2)
+ map_foreachinarea(skill_gangster_out,sd->bl.m,
+ sd->bl.x-range,sd->bl.y-range,
+ sd->bl.x+range,sd->bl.y+range,BL_PC);
+ sd->state.gangsterparadise = 0;
+ return 0;
+ }
+ return 0;
+}
+/*==========================================
+ * Taekwon TK_HPTIME and TK_SPTIME skills [Dralnu]
+ *------------------------------------------
+ */
+static int skill_rest_count(struct block_list *bl,va_list ap)
+{
+ int *c_r;
+ struct map_session_data *sd;
+ nullpo_retr(0, bl);
+ nullpo_retr(0, ap);
+
+ sd=(struct map_session_data*)bl;
+ c_r=va_arg(ap,int *);
+
+ if(sd && c_r && pc_issit(sd) && (pc_checkskill(sd,TK_HPTIME) > 0 || pc_checkskill(sd,TK_SPTIME) > 0 ))
+ (*c_r)++;
+ return 0;
+}
+
+static int skill_rest_in(struct block_list *bl,va_list ap)
+{
+ struct map_session_data *sd;
+ nullpo_retr(0, bl);
+ nullpo_retr(0, ap);
+
+ sd=(struct map_session_data*)bl;
+ if(sd && pc_issit(sd) && (pc_checkskill(sd,TK_HPTIME) > 0 || pc_checkskill(sd,TK_SPTIME) > 0 )){
+ sd->state.rest=1;
+ status_calc_pc(sd,0);
+ }
+ return 0;
+}
+
+static int skill_rest_out(struct block_list *bl,va_list ap)
+{
+ struct map_session_data *sd;
+ nullpo_retr(0, bl);
+ nullpo_retr(0, ap);
+
+ sd=(struct map_session_data*)bl;
+ if(sd && sd->state.rest != 0){
+ sd->state.rest=0;
+ }
+ return 0;
+}
+
+int skill_rest(struct map_session_data *sd ,int type)
+{
+ int range=1;
+ int c_r=0;
+ nullpo_retr(0, sd);
+
+ if(pc_checkskill(sd,TK_HPTIME) <= 0 && pc_checkskill(sd,TK_SPTIME) <= 0)
+ return 0;
+
+ if(type==1) { //When you sit down
+ map_foreachinarea(skill_rest_count,sd->bl.m,
+ sd->bl.x-range,sd->bl.y-range,
+ sd->bl.x+range,sd->bl.y+range,BL_PC,&c_r);
+ if(c_r > 1) {
+ map_foreachinarea(skill_rest_in,sd->bl.m,
+ sd->bl.x-range,sd->bl.y-range,
+ sd->bl.x+range,sd->bl.y+range,BL_PC);
+ sd->state.rest = 1;
+ status_calc_pc(sd,0);
+ }
+ return 0;
+ }
+ else if(type==0) { //When you stand up
+ map_foreachinarea(skill_rest_count,sd->bl.m,
+ sd->bl.x-range,sd->bl.y-range,
+ sd->bl.x+range,sd->bl.y+range,BL_PC,&c_r);
+ if(c_r < 2)
+ map_foreachinarea(skill_rest_out,sd->bl.m,
+ sd->bl.x-range,sd->bl.y-range,
+ sd->bl.x+range,sd->bl.y+range,BL_PC);
+ sd->state.rest = 0;
+ status_calc_pc(sd,0);
+ return 0;
+ }
+ return 0;
+}
+/*==========================================
+ * 寒いジョ?ク?スクリ?ム判定??(foreachinarea)
+ *------------------------------------------
+ */
+int skill_frostjoke_scream(struct block_list *bl,va_list ap)
+{
+ struct block_list *src;
+ int skillnum,skilllv;
+ unsigned int tick;
+
+ nullpo_retr(0, bl);
+ nullpo_retr(0, ap);
+ nullpo_retr(0, src=va_arg(ap,struct block_list*));
+
+ skillnum=va_arg(ap,int);
+ skilllv=va_arg(ap,int);
+ if(skilllv <= 0) return 0;
+ tick=va_arg(ap,unsigned int);
+
+ if (src == bl || //自分には?かない
+ bl->prev == NULL ||
+ status_isdead(bl))
+ return 0;
+ if (bl->type == BL_PC) {
+ struct map_session_data *sd = (struct map_session_data *)bl;
+ if (sd && sd->status.option & OPTION_INVISIBLE && pc_isGM(sd) > 0)
+ return 0;
+ }
+ //It has been reported that Scream/Joke works the same regardless of woe-setting. [Skotlex]
+ if(battle_check_target(src,bl,BCT_ENEMY) > 0)
+ skill_additional_effect(src,bl,skillnum,skilllv,BF_MISC,tick);
+ else if(battle_check_target(src,bl,BCT_PARTY) > 0 && rand()%100 < 10)
+ skill_additional_effect(src,bl,skillnum,skilllv,BF_MISC,tick);
+
+ return 0;
+}
+
+/*==========================================
+ * バジリカのセルを?ン定する
+ *------------------------------------------
+ */
+void skill_unitsetmapcell(struct skill_unit *src, int skill_num, int flag)
+{
+ int i,x,y,range = skill_get_unit_range(skill_num);
+ int size = range*2+1;
+
+ for (i=0;i<size*size;i++) {
+ x = src->bl.x+(i%size-range);
+ y = src->bl.y+(i/size-range);
+ map_setcell(src->bl.m,x,y,flag);
+ }
+}
+
+/*==========================================
+ * Sets a map cell around the caster, according to the skill's range.
+ *------------------------------------------
+ */
+void skill_setmapcell(struct block_list *src, int skill_num, int skill_lv, int flag)
+{
+ int i,x,y,range = skill_get_range2(src, skill_num, skill_lv);
+ int size = range*2+1;
+
+ for (i=0;i<size*size;i++) {
+ x = src->x+(i%size-range);
+ y = src->y+(i/size-range);
+ map_setcell(src->m,x,y,flag);
+ }
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int skill_attack_area(struct block_list *bl,va_list ap)
+{
+ struct block_list *src,*dsrc;
+ int atk_type,skillid,skilllv,flag,type;
+ unsigned int tick;
+
+ nullpo_retr(0, bl);
+ nullpo_retr(0, ap);
+
+ atk_type = va_arg(ap,int);
+ if((src=va_arg(ap,struct block_list*)) == NULL)
+ return 0;
+ if((dsrc=va_arg(ap,struct block_list*)) == NULL)
+ return 0;
+ skillid=va_arg(ap,int);
+ skilllv=va_arg(ap,int);
+ if(skillid > 0 && skilllv <= 0) return 0; // celest
+ tick=va_arg(ap,unsigned int);
+ flag=va_arg(ap,int);
+ type=va_arg(ap,int);
+
+ if(battle_check_target(dsrc,bl,type) > 0)
+ skill_attack(atk_type,src,dsrc,bl,skillid,skilllv,tick,flag);
+
+ return 0;
+}
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int skill_clear_element_field(struct block_list *bl)
+{
+ struct mob_data *md=NULL;
+ struct map_session_data *sd=NULL;
+ int i,max,skillid;
+
+ nullpo_retr(0, bl);
+
+ if (bl->type==BL_MOB) {
+ max = MAX_MOBSKILLUNITGROUP;
+ md = (struct mob_data *)bl;
+ } else if(bl->type==BL_PC) {
+ max = MAX_SKILLUNITGROUP;
+ sd = (struct map_session_data *)bl;
+ } else
+ return 0;
+
+ for (i=0;i<max;i++) {
+ if(sd){
+ skillid=sd->skillunit[i].skill_id;
+ if(skillid==SA_DELUGE||skillid==SA_VOLCANO||skillid==SA_VIOLENTGALE||skillid==SA_LANDPROTECTOR)
+ skill_delunitgroup(&sd->skillunit[i]);
+ }else if(md){
+ skillid=md->skillunit[i].skill_id;
+ if(skillid==SA_DELUGE||skillid==SA_VOLCANO||skillid==SA_VIOLENTGALE||skillid==SA_LANDPROTECTOR)
+ skill_delunitgroup(&md->skillunit[i]);
+ }
+ }
+ return 0;
+}
+
+/*==========================================
+ * Returns the first element field found [Skotlex]
+ *------------------------------------------
+ */
+struct skill_unit_group *skill_locate_element_field(struct block_list *bl)
+{
+ struct mob_data *md=NULL;
+ struct map_session_data *sd=NULL;
+ int i,max,skillid;
+
+ nullpo_retr(0, bl);
+
+ if (bl->type==BL_MOB) {
+ max = MAX_MOBSKILLUNITGROUP;
+ md = (struct mob_data *)bl;
+ } else if(bl->type==BL_PC) {
+ max = MAX_SKILLUNITGROUP;
+ sd = (struct map_session_data *)bl;
+ } else
+ return NULL;
+
+ for (i=0;i<max;i++) {
+ if(sd){
+ skillid=sd->skillunit[i].skill_id;
+ if(skillid==SA_DELUGE||skillid==SA_VOLCANO||skillid==SA_VIOLENTGALE||skillid==SA_LANDPROTECTOR)
+ return &sd->skillunit[i];
+ }else if(md){
+ skillid=md->skillunit[i].skill_id;
+ if(skillid==SA_DELUGE||skillid==SA_VOLCANO||skillid==SA_VIOLENTGALE||skillid==SA_LANDPROTECTOR)
+ return &md->skillunit[i];
+ }
+ }
+ return NULL;
+}
+
+// for graffiti cleaner [Valaris]
+int skill_graffitiremover(struct block_list *bl, va_list ap)
+{
+ struct skill_unit *unit=NULL;
+
+ nullpo_retr(0, bl);
+ nullpo_retr(0, ap);
+
+ if(bl->type!=BL_SKILL || (unit=(struct skill_unit *)bl) == NULL)
+ return 0;
+
+ if((unit->group) && (unit->group->unit_id == UNT_GRAFFITI))
+ skill_delunit(unit);
+
+ return 0;
+}
+
+int skill_greed(struct block_list *bl, va_list ap)
+{
+ struct block_list *src;
+ struct map_session_data *sd=NULL;
+ struct flooritem_data *fitem=NULL;
+
+ nullpo_retr(0, bl);
+ nullpo_retr(0, ap);
+ nullpo_retr(0, src = va_arg(ap,struct block_list *));
+
+ if(src->type == BL_PC && (sd=(struct map_session_data *)src) && bl->type==BL_ITEM && (fitem=(struct flooritem_data *)bl))
+ pc_takeitem(sd, fitem);
+
+ return 0;
+}
+
+/*==========================================
+ * ランドプ?テクタ?チェック(foreachinarea)
+ *------------------------------------------
+ */
+int skill_landprotector(struct block_list *bl, va_list ap )
+{
+ int skillid;
+ int *alive;
+ struct skill_unit *unit;
+ struct block_list *src;
+
+ nullpo_retr(0, bl);
+ nullpo_retr(0, ap);
+
+ skillid = va_arg(ap,int);
+ alive = va_arg(ap,int *);
+ src = va_arg(ap,struct block_list *);
+
+ if ((unit = (struct skill_unit *)bl) == NULL || unit->group == NULL)
+ return 0;
+
+ if (alive && skillid == SA_LANDPROTECTOR && unit->group->skill_id == SA_LANDPROTECTOR
+ && battle_check_target(bl, src, BCT_ENEMY) > 0)
+ { //Check for offensive Land Protector to delete both. [Skotlex]
+ (*alive) = 0;
+ skill_delunit(unit);
+ return 0;
+ }
+ if (skillid == SA_LANDPROTECTOR ||
+ skillid == HW_GANBANTEIN)
+ skill_delunit(unit);
+ else if (alive && unit->group->skill_id == SA_LANDPROTECTOR)
+ (*alive) = 0;
+ else if (alive && skillid == HP_BASILICA && unit->group->skill_id == HP_BASILICA)
+ (*alive) = 0; //Basilica can't be placed on top of itself to avoid map-cell stacking problems. [Skotlex]
+ return 0;
+}
+
+/*==========================================
+ * variation of skill_landprotector
+ *------------------------------------------
+ */
+int skill_ganbatein(struct block_list *bl, va_list ap )
+{
+ struct skill_unit *unit;
+
+ nullpo_retr(0, bl);
+ nullpo_retr(0, ap);
+ if ((unit = (struct skill_unit *)bl) == NULL || unit->group == NULL)
+ return 0;
+
+ if (unit->group->skill_id == SA_LANDPROTECTOR)
+ skill_delunit(unit);
+ else skill_delunitgroup(unit->group);
+
+ return 0;
+}
+
+/*==========================================
+ * 指定範??でsrcに?して有?なタ?ゲットのblの?を?える(foreachinarea)
+ *------------------------------------------
+ */
+int skill_count_target (struct block_list *bl, va_list ap)
+{
+ struct block_list *src;
+ int *c;
+
+ nullpo_retr(0, bl);
+ nullpo_retr(0, ap);
+
+ if ((src = va_arg(ap,struct block_list *)) == NULL)
+ return 0;
+ if ((c = va_arg(ap,int *)) == NULL)
+ return 0;
+ if (battle_check_target(src,bl,BCT_ENEMY) > 0)
+ (*c)++;
+ return 0;
+}
+/*==========================================
+ * トラップ範???(foreachinarea)
+ *------------------------------------------
+ */
+int skill_trap_splash (struct block_list *bl, va_list ap)
+{
+ struct block_list *src;
+ int tick;
+ int splash_count;
+ struct skill_unit *unit;
+ struct skill_unit_group *sg;
+ struct block_list *ss;
+ int i;
+
+ nullpo_retr(0, bl);
+ nullpo_retr(0, ap);
+ nullpo_retr(0, src = va_arg(ap,struct block_list *));
+ nullpo_retr(0, unit = (struct skill_unit *)src);
+ nullpo_retr(0, sg = unit->group);
+// nullpo_retr(0, ss = map_id2bl(sg->src_id));
+ if ((ss = map_id2bl(sg->src_id)) == NULL)
+ { //Temporal debug until this case is solved. [Skotlex]
+ ShowDebug("skill_trap_splash: Trap's source (id: %d) not found!\n", sg->src_id);
+ return 0;
+ }
+
+ tick = va_arg(ap,int);
+ splash_count = va_arg(ap,int);
+
+ if(battle_check_target(src,bl,BCT_ENEMY) > 0){
+ switch(sg->unit_id){
+ case UNT_SHOCKWAVE:
+ case UNT_SANDMAN:
+ case UNT_FLASHER:
+ skill_additional_effect(ss,bl,sg->skill_id,sg->skill_lv,BF_MISC,tick);
+ break;
+ case UNT_BLASTMINE:
+ case UNT_CLAYMORETRAP:
+ for(i=0;i<splash_count;i++){
+ skill_attack(BF_MISC,ss,src,bl,sg->skill_id,sg->skill_lv,tick,(sg->val2)?0x0500:0);
+ }
+ break;
+ case UNT_FREEZINGTRAP:
+ skill_attack(BF_WEAPON, ss,src,bl,sg->skill_id,sg->skill_lv,tick,(sg->val2)?0x0500:0);
+ break;
+ default:
+ break;
+ }
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * ステ?タス異??I了
+ *------------------------------------------
+ */
+int skill_enchant_elemental_end (struct block_list *bl, int type)
+{
+ struct status_change *sc_data;
+
+ nullpo_retr(0, bl);
+ nullpo_retr(0, sc_data = status_get_sc_data(bl));
+
+ if (type != SC_ENCPOISON && sc_data[SC_ENCPOISON].timer != -1) /* エンチャントポイズン解? */
+ status_change_end(bl, SC_ENCPOISON, -1);
+ if (type != SC_ASPERSIO && sc_data[SC_ASPERSIO].timer != -1) /* アスペルシオ解? */
+ status_change_end(bl, SC_ASPERSIO, -1);
+ if (type != SC_FIREWEAPON && sc_data[SC_FIREWEAPON].timer != -1) /* フレイムランチャ解? */
+ status_change_end(bl, SC_FIREWEAPON, -1);
+ if (type != SC_WATERWEAPON && sc_data[SC_WATERWEAPON].timer != -1) /* フ?ストウェポン解? */
+ status_change_end(bl, SC_WATERWEAPON, -1);
+ if (type != SC_WINDWEAPON && sc_data[SC_WINDWEAPON].timer != -1) /* ライトニング??ダ?解? */
+ status_change_end(bl, SC_WINDWEAPON, -1);
+ if (type != SC_EARTHWEAPON && sc_data[SC_EARTHWEAPON].timer != -1) /* サイスミックウェポン解? */
+ status_change_end(bl, SC_EARTHWEAPON, -1);
+ if (type != SC_SHADOWWEAPON && sc_data[SC_SHADOWWEAPON].timer != -1)
+ status_change_end(bl, SC_SHADOWWEAPON, -1);
+ if (type != SC_GHOSTWEAPON && sc_data[SC_GHOSTWEAPON].timer != -1)
+ status_change_end(bl, SC_GHOSTWEAPON, -1);
+ return 0;
+}
+
+/* ク??キング??ク?i周りに移動不可能地?があるか?j */
+int skill_check_cloaking(struct block_list *bl)
+{
+ struct map_session_data *sd = NULL;
+ static int dx[] = { 0, 1, 0, -1, -1, 1, 1, -1}; //optimized by Lupus
+ static int dy[] = {-1, 0, 1, 0, -1, -1, 1, 1};
+ int end = 1,i;
+
+ nullpo_retr(1, bl);
+
+ if (bl->type == BL_PC) {
+ nullpo_retr(1, sd = (struct map_session_data *)bl);
+ }
+
+ if ((bl->type == BL_PC && battle_config.pc_cloak_check_type&1) ||
+ (bl->type != BL_PC && battle_config.monster_cloak_check_type&1))
+ { //Check for walls.
+ for (i = 0; i < 8; i++)
+ if (map_getcell(bl->m, bl->x+dx[i], bl->y+dy[i], CELL_CHKNOPASS))
+ {
+ end = 0;
+ break;
+ }
+ } else
+ end = 0; //No wall check.
+
+ if(end){
+ if ((sd && pc_checkskill(sd,AS_CLOAKING)<3) || bl->type == BL_MOB) {
+ status_change_end(bl, SC_CLOAKING, -1);
+ }
+ else if (sd && sd->sc_data[SC_CLOAKING].val3 != 130) {
+ status_quick_recalc_speed (sd, AS_CLOAKING, 130, 1);
+ }
+ }
+ else {
+ if (sd && sd->sc_data[SC_CLOAKING].val3 != 103) {
+ status_quick_recalc_speed (sd, AS_CLOAKING, 103, 1);
+ }
+ }
+
+ return end;
+}
+
+/*
+ *----------------------------------------------------------------------------
+ * スキルユニット
+ *----------------------------------------------------------------------------
+ */
+
+/*==========================================
+ * 演奏/ダンスをやめる
+ * flag 1で?奏中なら相方にユニットを任せる
+ *
+ *------------------------------------------
+ */
+void skill_stop_dancing(struct block_list *src)
+{
+ struct status_change* sc_data;
+ struct skill_unit_group* group;
+ struct map_session_data* dsd = NULL;
+
+ nullpo_retv(src);
+ nullpo_retv(sc_data = status_get_sc_data(src));
+
+ if(sc_data[SC_DANCING].timer == -1)
+ return;
+
+ group = (struct skill_unit_group *)sc_data[SC_DANCING].val2;
+ sc_data[SC_DANCING].val2 = 0;
+
+ if (sc_data[SC_DANCING].val4)
+ {
+ if (sc_data[SC_DANCING].val4 != BCT_SELF)
+ dsd = map_id2sd(sc_data[SC_DANCING].val4);
+ sc_data[SC_DANCING].val4 = 0;
+ }
+
+ if (group)
+ skill_delunitgroup(group);
+
+ if (dsd)
+ {
+ dsd->sc_data[SC_DANCING].val4 = dsd->sc_data[SC_DANCING].val2 = 0;
+ status_change_end(&dsd->bl, SC_DANCING, -1);
+ }
+ status_change_end(src, SC_DANCING, -1);
+}
+
+/*==========================================
+ * スキルユニット?炎化
+ *------------------------------------------
+ */
+struct skill_unit *skill_initunit(struct skill_unit_group *group,int idx,int x,int y)
+{
+ struct skill_unit *unit;
+
+ nullpo_retr(NULL, group);
+ nullpo_retr(NULL, unit=&group->unit[idx]);
+
+ if(!unit->alive)
+ group->alive_count++;
+
+ unit->bl.id=map_addobject(&unit->bl);
+ unit->bl.type=BL_SKILL;
+ unit->bl.m=group->map;
+ unit->bl.x=x;
+ unit->bl.y=y;
+ unit->group=group;
+ unit->val1=unit->val2=0;
+ unit->alive=1;
+
+ map_addblock(&unit->bl);
+ clif_skill_setunit(unit);
+
+ switch (group->skill_id) {
+ case AL_PNEUMA:
+ skill_unitsetmapcell(unit,AL_PNEUMA,CELL_SETPNEUMA);
+ break;
+ case MG_SAFETYWALL:
+ skill_unitsetmapcell(unit,MG_SAFETYWALL,CELL_SETSAFETYWALL);
+ break;
+ case SA_LANDPROTECTOR:
+ skill_unitsetmapcell(unit,SA_LANDPROTECTOR,CELL_SETLANDPROTECTOR);
+ break;
+ case HP_BASILICA:
+ skill_unitsetmapcell(unit,HP_BASILICA,CELL_SETBASILICA);
+ break;
+ case WZ_ICEWALL:
+ skill_unitsetmapcell(unit,WZ_ICEWALL,CELL_SETICEWALL);
+ break;
+ }
+ return unit;
+}
+
+/*==========================================
+ * スキルユニット??
+ *------------------------------------------
+ */
+int skill_delunit(struct skill_unit *unit)
+{
+ struct skill_unit_group *group;
+
+ nullpo_retr(0, unit);
+ if(!unit->alive)
+ return 0;
+ nullpo_retr(0, group=unit->group);
+
+ /* onlimitイベント呼び?oし */
+ skill_unit_onlimit( unit,gettick() );
+
+ /* onoutイベント呼び?oし */
+ if (!unit->range) {
+ map_foreachincell(skill_unit_effect,unit->bl.m,
+ unit->bl.x,unit->bl.y,group->bl_flag,&unit->bl,gettick(),4);
+ }
+
+ switch (group->skill_id) {
+ case AL_PNEUMA:
+ skill_unitsetmapcell(unit,AL_PNEUMA,CELL_CLRPNEUMA);
+ break;
+ case MG_SAFETYWALL:
+ skill_unitsetmapcell(unit,MG_SAFETYWALL,CELL_CLRSAFETYWALL);
+ break;
+ case SA_LANDPROTECTOR:
+ skill_unitsetmapcell(unit,SA_LANDPROTECTOR,CELL_CLRLANDPROTECTOR);
+ break;
+ case HP_BASILICA:
+ skill_unitsetmapcell(unit,HP_BASILICA,CELL_CLRBASILICA);
+ break;
+ case WZ_ICEWALL:
+ skill_unitsetmapcell(unit,WZ_ICEWALL,CELL_CLRICEWALL);
+ break;
+ }
+
+ clif_skill_delunit(unit);
+
+ unit->group=NULL;
+ unit->alive=0;
+ map_delobjectnofree(unit->bl.id);
+ if(--group->alive_count==0)
+ skill_delunitgroup(group);
+
+ return 0;
+}
+/*==========================================
+ * スキルユニットグル?プ?炎化
+ *------------------------------------------
+ */
+static int skill_unit_group_newid = MAX_SKILL_DB;
+struct skill_unit_group *skill_initunitgroup(struct block_list *src,
+ int count,int skillid,int skilllv,int unit_id)
+{
+ int i;
+ struct skill_unit_group *group=NULL, *list=NULL;
+ int maxsug=0;
+
+ if(skilllv <= 0) return 0;
+
+ nullpo_retr(NULL, src);
+
+ if(src->type==BL_PC){
+ list=((struct map_session_data *)src)->skillunit;
+ maxsug=MAX_SKILLUNITGROUP;
+ }else if(src->type==BL_MOB){
+ list=((struct mob_data *)src)->skillunit;
+ maxsug=MAX_MOBSKILLUNITGROUP;
+ }else if(src->type==BL_PET){
+ list=((struct pet_data *)src)->skillunit;
+ maxsug=MAX_MOBSKILLUNITGROUP;
+ }
+ if(list){
+ for(i=0;i<maxsug;i++) /* 空いているもの?? */
+ if(list[i].group_id==0){
+ group=&list[i];
+ break;
+ }
+
+ if(group==NULL){ /* 空いてないので古いもの?? */
+ int j=0;
+ unsigned maxdiff=0,x,tick=gettick();
+ for(i=0;i<maxsug;i++)
+ if((x=DIFF_TICK(tick,list[i].tick))>maxdiff){
+ maxdiff=x;
+ j=i;
+ }
+ skill_delunitgroup(&list[j]);
+ group=&list[j];
+ }
+ }
+
+ if(group==NULL){
+ ShowFatalError("skill_initunitgroup: error unit group !\n");
+ exit(1);
+ }
+
+ group->src_id=src->id;
+ group->party_id=status_get_party_id(src);
+ group->guild_id=status_get_guild_id(src);
+ group->group_id=skill_unit_group_newid++;
+ if(skill_unit_group_newid<=0)
+ skill_unit_group_newid = MAX_SKILL_DB;
+ group->unit=(struct skill_unit *)aCalloc(count,sizeof(struct skill_unit));
+ group->unit_count=count;
+ group->val1=group->val2=0;
+ group->skill_id=skillid;
+ group->skill_lv=skilllv;
+ group->unit_id=unit_id;
+ group->map=src->m;
+ group->limit=10000;
+ group->interval=1000;
+ group->tick=gettick();
+ if (skillid == PR_SANCTUARY) //Sanctuary starts healing +1500ms after casted. [Skotlex]
+ group->tick += 1500;
+ group->valstr=NULL;
+
+ i = skill_get_unit_flag(skillid); //Reuse for faster access from here on. [Skotlex]
+ if (i&UF_DANCE) {
+ struct map_session_data *sd = NULL;
+ if(src->type==BL_PC && (sd=(struct map_session_data *)src) ){
+ sd->skillid_dance=skillid;
+ sd->skilllv_dance=skilllv;
+ }
+ status_change_start(src,SC_DANCING,skillid,(int)group,0,(i&UF_ENSEMBLE?BCT_SELF:0),skill_get_time(skillid,skilllv)+1000,0);
+ //?奏スキルは相方をダンス?態にする
+ if (sd && i&UF_ENSEMBLE &&
+ battle_config.player_skill_partner_check) {
+ skill_check_pc_partner(sd, skillid, &skilllv, 1, 1);
+ }
+ }
+ return group;
+}
+
+/*==========================================
+ * スキルユニットグル?プ??
+ *------------------------------------------
+ */
+int skill_delunitgroup(struct skill_unit_group *group)
+{
+ struct block_list *src;
+ int i;
+
+ nullpo_retr(0, group);
+ if(group->unit_count<=0)
+ return 0;
+
+ src=map_id2bl(group->src_id);
+ //ダンススキルはダンス?態を解?怩キる
+ if(src) {
+ if (skill_get_unit_flag(group->skill_id)&UF_DANCE)
+ {
+ struct status_change* sc_data = status_get_sc_data(src);
+ if (sc_data && sc_data[SC_DANCING].timer != -1)
+ {
+ sc_data[SC_DANCING].val2 = 0 ; //This prevents status_change_end attempting to redelete the group. [Skotlex]
+ status_change_end(src,SC_DANCING,-1);
+ }
+ }
+
+ if (group->unit_id == UNT_GOSPEL) { //Clear Gospel [Skotlex]
+ struct status_change *sc_data = status_get_sc_data(src);
+ if(sc_data && sc_data[SC_GOSPEL].timer != -1) {
+ sc_data[SC_GOSPEL].val3 = 0; //Remove reference to this group. [Skotlex]
+ status_change_end(src,SC_GOSPEL,-1);
+ }
+ }
+ }
+
+ group->alive_count=0;
+ if(group->unit!=NULL){
+ for(i=0;i<group->unit_count;i++)
+ if(group->unit[i].alive)
+ skill_delunit(&group->unit[i]);
+ }
+ if(group->valstr!=NULL){
+ //Supposedly Free remembers the size of the original Calloc/Malloc, so this should be safe [Skotlex]
+ aFree(group->valstr);
+ group->valstr=NULL;
+ }
+
+ map_freeblock((struct block_list*)group->unit); /* aFree()の替わり */
+ group->unit=NULL;
+ group->src_id=0;
+ group->group_id=0;
+ group->unit_count=0;
+ return 0;
+}
+
+/*==========================================
+ * スキルユニットグル?プ全??
+ *------------------------------------------
+ */
+int skill_clear_unitgroup(struct block_list *src)
+{
+ struct skill_unit_group *group=NULL;
+ int maxsug=0;
+
+ nullpo_retr(0, src);
+
+ if(src->type==BL_PC){
+ group=((struct map_session_data *)src)->skillunit;
+ maxsug=MAX_SKILLUNITGROUP;
+ } else if(src->type==BL_MOB){
+ group=((struct mob_data *)src)->skillunit;
+ maxsug=MAX_MOBSKILLUNITGROUP;
+ } else if(src->type==BL_PET){ // [Valaris]
+ group=((struct pet_data *)src)->skillunit;
+ maxsug=MAX_MOBSKILLUNITGROUP;
+ } else
+ return 0;
+ if(group){
+ int i;
+ for(i=0;i<maxsug;i++)
+ if(group[i].group_id>0 && group[i].src_id == src->id)
+ skill_delunitgroup(&group[i]);
+ }
+ return 0;
+}
+
+/*==========================================
+ * スキルユニットグル?プの被影響tick??
+ *------------------------------------------
+ */
+struct skill_unit_group_tickset *skill_unitgrouptickset_search(
+ struct block_list *bl,struct skill_unit_group *group,int tick)
+{
+ int i,j=-1,k,s,id;
+ struct skill_unit_group_tickset *set;
+
+ nullpo_retr(0, bl);
+ if (group->interval==-1)
+ return NULL;
+
+ if (bl->type == BL_PC)
+ set = ((struct map_session_data *)bl)->skillunittick;
+ else if (bl->type == BL_MOB)
+ set = ((struct mob_data *)bl)->skillunittick;
+ else
+ return 0;
+
+ if (skill_get_unit_flag(group->skill_id)&UF_NOOVERLAP)
+ id = s = group->skill_id;
+ else
+ id = s = group->group_id;
+
+ for (i=0; i<MAX_SKILLUNITGROUPTICKSET; i++) {
+ k = (i+s) % MAX_SKILLUNITGROUPTICKSET;
+ if (set[k].id == id)
+ return &set[k];
+ else if (j==-1 && (DIFF_TICK(tick,set[k].tick)>0 || set[k].id==0))
+ j=k;
+ }
+
+ if (j == -1) {
+ if(battle_config.error_log) {
+ ShowWarning ("skill_unitgrouptickset_search: tickset is full\n");
+ }
+ j = id % MAX_SKILLUNITGROUPTICKSET;
+ }
+
+ set[j].id = id;
+ set[j].tick = tick;
+ return &set[j];
+}
+
+/*==========================================
+ * スキルユニットタイマ??動??用(foreachinarea)
+ *------------------------------------------
+ */
+int skill_unit_timer_sub_onplace( struct block_list *bl, va_list ap )
+{
+ struct skill_unit *unit;
+ struct skill_unit_group *group;
+ unsigned int tick;
+
+ nullpo_retr(0, bl);
+ nullpo_retr(0, ap);
+ unit = va_arg(ap,struct skill_unit *);
+ tick = va_arg(ap,unsigned int);
+
+ if (!unit->alive || bl->prev==NULL)
+ return 0;
+
+ nullpo_retr(0, group=unit->group);
+
+ if (map_getcell(bl->m, bl->x, bl->y, CELL_CHKLANDPROTECTOR))
+ return 0; //AoE skills are ineffective. [Skotlex]
+
+ if (battle_check_target(&unit->bl,bl,group->target_flag)<=0)
+ return 0;
+
+ skill_unit_onplace_timer(unit,bl,tick);
+
+ return 0;
+}
+
+/*==========================================
+ * スキルユニットタイマ???用(foreachobject)
+ *------------------------------------------
+ */
+int skill_unit_timer_sub( struct block_list *bl, va_list ap )
+{
+ struct skill_unit *unit;
+ struct skill_unit_group *group;
+ int range;
+ unsigned int tick;
+
+ nullpo_retr(0, bl);
+ nullpo_retr(0, ap);
+ nullpo_retr(0, unit=(struct skill_unit *)bl);
+ tick=va_arg(ap,unsigned int);
+
+ if(!unit->alive)
+ return 0;
+ group=unit->group;
+
+ nullpo_retr(0, group);
+ range = unit->range;
+
+ /* onplace_timerイベント呼び?oし */
+ if (range>=0 && group->interval!=-1) {
+ map_foreachinarea(skill_unit_timer_sub_onplace, bl->m,
+ bl->x-range,bl->y-range,bl->x+range,bl->y+range,group->bl_flag,bl,tick);
+ if (!unit->alive)
+ return 0;
+ // マグヌスは発動したユニットは??怩キる
+ if (group->skill_id==PR_MAGNUS && unit->val2) {
+ skill_delunit(unit);
+ return 0;
+ }
+ }
+ /* 時間?リれ?? */
+ if((DIFF_TICK(tick,group->tick)>=group->limit || DIFF_TICK(tick,group->tick)>=unit->limit)){
+ switch(group->unit_id){
+ case UNT_BLASTMINE:
+ group->unit_id = UNT_USED_TRAPS;
+ clif_changelook(bl,LOOK_BASE,group->unit_id);
+ group->limit=DIFF_TICK(tick+1500,group->tick);
+ unit->limit=DIFF_TICK(tick+1500,group->tick);
+ break;
+ case UNT_SKIDTRAP:
+ case UNT_ANKLESNARE:
+ case UNT_LANDMINE:
+ case UNT_SHOCKWAVE:
+ case UNT_SANDMAN:
+ case UNT_FLASHER:
+ case UNT_FREEZINGTRAP:
+ case UNT_CLAYMORETRAP:
+ case UNT_TALKIEBOX:
+ {
+ struct block_list *src=map_id2bl(group->src_id);
+ if(group->unit_id == UNT_ANKLESNARE && group->val2);
+ else{
+ if(src && src->type==BL_PC && group->val3 != BD_INTOABYSS)
+ { //Avoid generating trap items when it did not cost to create them. [Skotlex]
+ struct item item_tmp;
+ memset(&item_tmp,0,sizeof(item_tmp));
+ item_tmp.nameid=1065;
+ item_tmp.identify=1;
+ map_addflooritem(&item_tmp,1,bl->m,bl->x,bl->y,NULL,NULL,NULL,0); // ?返還
+ }
+ }
+ skill_delunit(unit);
+ }
+ break;
+
+ case 0xc1:
+ case 0xc2:
+ case 0xc3:
+ case 0xc4:
+ {
+ struct block_list *src=map_id2bl(group->src_id);
+ if (src)
+ group->tick = tick;
+ }
+ break;
+
+ default:
+ skill_delunit(unit);
+ }
+ }
+
+ if(group->unit_id == UNT_ICEWALL) {
+ unit->val1 -= 5;
+ if(unit->val1 <= 0 && unit->limit + group->tick > tick + 700)
+ unit->limit = DIFF_TICK(tick+700,group->tick);
+ }
+
+ return 0;
+}
+/*==========================================
+ * スキルユニットタイマ???
+ *------------------------------------------
+ */
+int skill_unit_timer( int tid,unsigned int tick,int id,int data)
+{
+ map_freeblock_lock();
+
+ map_foreachobject( skill_unit_timer_sub, BL_SKILL, tick );
+
+ map_freeblock_unlock();
+
+ return 0;
+}
+
+/*==========================================
+ * スキルユニット移動時??用(foreachinarea)
+ *------------------------------------------
+ */
+int skill_unit_move_sub( struct block_list *bl, va_list ap )
+{
+ struct skill_unit *unit = (struct skill_unit *)bl;
+ struct block_list *target;
+ unsigned int tick,flag,result;
+ int skill_id;
+
+ target=va_arg(ap,struct block_list*);
+ tick = va_arg(ap,unsigned int);
+ flag = va_arg(ap,int);
+
+ nullpo_retr(0, unit->group);
+
+ if (!(unit->group->bl_flag&target->type))
+ return 0; //we don't target this type of bl
+
+ skill_id = unit->group->skill_id; //Necessary in case the group is deleted after calling on_place/on_out [Skotlex]
+
+ if (unit->group->interval!=-1 &&
+ !(skill_get_unit_flag(skill_id)&UF_DUALMODE)) //Skills in dual mode have to trigger both. [Skotlex]
+ return 0;
+
+ if (!unit->alive || target->prev==NULL)
+ return 0;
+
+ if (flag&1)
+ {
+ result = skill_unit_onplace(unit,target,tick);
+ if (flag&2 && result)
+ { //Clear skill ids we have stored in onout.
+ int i;
+ for(i=0; i<8 && skill_unit_temp[i]!=result; i++);
+ if (i<8)
+ skill_unit_temp[i] = 0;
+ }
+ }
+ else
+ {
+ result = skill_unit_onout(unit,target,tick);
+ if (flag&2 && skill_unit_index < 7 && result) //Store this unit id.
+ skill_unit_temp[skill_unit_index++] = result;
+ }
+ if (flag&4)
+ skill_unit_onleft(skill_id,target,tick);
+ return 1;
+}
+
+/*==========================================
+ * Invoked when a char has moved and unit cells must be invoked (onplace, onout, onleft)
+ * Flag values:
+ * flag&1: invoke skill_unit_onplace (otherwise invoke skill_unit_onout)
+ * flag&2: this function is being invoked twice as a bl moves, store in memory the affected
+ * units to figure out when they have left a group.
+ * flag&4: Force a onleft event (triggered when the bl is killed, for example)
+ *------------------------------------------
+ */
+int skill_unit_move(struct block_list *bl,unsigned int tick,int flag)
+{
+ nullpo_retr(0, bl);
+
+ if(bl->prev==NULL )
+ return 0;
+
+ if (flag&2 && !(flag&1))
+ { //Onout, clear data
+ memset (&skill_unit_temp,0,sizeof(skill_unit_temp));
+ skill_unit_index=0;
+ }
+
+ map_foreachincell(skill_unit_move_sub,
+ bl->m,bl->x,bl->y,BL_SKILL,bl,tick,flag);
+
+ if (flag&2 && flag&1)
+ { //Onplace, check any skill units you have left.
+ int i;
+ for (i=0; i< 8 && skill_unit_temp[i]>0; i++)
+ skill_unit_onleft(skill_unit_temp[i], bl, tick);
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * スキルユニット自?の移動時??
+ * 引?はグル?プと移動量
+ *------------------------------------------
+ */
+int skill_unit_move_unit_group( struct skill_unit_group *group, int m,int dx,int dy)
+{
+ int i,j;
+ unsigned int tick = gettick();
+ int *m_flag;
+ struct skill_unit *unit1;
+ struct skill_unit *unit2;
+
+ nullpo_retr(0, group);
+ if (group->unit_count<=0)
+ return 0;
+ if (group->unit==NULL)
+ return 0;
+
+ i = skill_get_unit_flag(group->skill_id); //Check the flag...
+ if (!(
+ (i&UF_DANCE && !(i&UF_ENSEMBLE)) || //Only non ensemble dances and traps can be moved.
+ skill_get_inf2(group->skill_id)&INF2_TRAP
+ ))
+ return 0;
+
+ m_flag = (int *) aMalloc(sizeof(int)*group->unit_count);
+ memset(m_flag,0,sizeof(int)*group->unit_count);// 移動フラグ
+ // m_flag
+ // 0: Neither of the following (skill_unit_onplace & skill_unit_onout are needed)
+ // 1: Unit will move to a slot that had another unit of the same group (skill_unit_onplace not needed)
+ // 2: Another unit from same group will end up positioned on this unit (skill_unit_onout not needed)
+ // 3: Both 1+2.
+ for(i=0;i<group->unit_count;i++){
+ unit1=&group->unit[i];
+ if (!unit1->alive || unit1->bl.m!=m)
+ continue;
+ for(j=0;j<group->unit_count;j++){
+ unit2=&group->unit[j];
+ if (!unit2->alive)
+ continue;
+ if (unit1->bl.x+dx==unit2->bl.x && unit1->bl.y+dy==unit2->bl.y){
+ m_flag[i] |= 0x1;
+ }
+ if (unit1->bl.x-dx==unit2->bl.x && unit1->bl.y-dy==unit2->bl.y){
+ m_flag[i] |= 0x2;
+ }
+ }
+ }
+ j = 0;
+ for (i=0;i<group->unit_count;i++) {
+ unit1=&group->unit[i];
+ if (!unit1->alive)
+ continue;
+ if (!(m_flag[i]&0x2)) {
+ map_foreachincell(skill_unit_effect,unit1->bl.m,
+ unit1->bl.x,unit1->bl.y,group->bl_flag,&unit1->bl,tick,4);
+ }
+ //Move Cell using "smart" criteria (avoid useless moving around)
+ switch(m_flag[i])
+ {
+ case 0:
+ //Cell moves independently, safely move it.
+ map_moveblock(&unit1->bl, unit1->bl.x+dx, unit1->bl.y+dy, tick);
+ clif_skill_setunit(unit1);
+ break;
+ case 1:
+ //Cell moves unto another cell, look for a replacement cell that won't collide
+ //and has no cell moving into it (flag == 2)
+ for(;j<group->unit_count;j++)
+ {
+ if(m_flag[j]!=2 || !group->unit[j].alive)
+ continue;
+ //Move to where this cell would had moved.
+ unit2 = &group->unit[j];
+ map_moveblock(&unit1->bl, unit2->bl.x+dx, unit2->bl.y+dy, tick);
+ clif_skill_setunit(unit1);
+ j++; //Skip this cell as we have used it.
+ break;
+ }
+ break;
+ case 2:
+ case 3:
+ break; //Don't move the cell as a cell will end on this tile anyway.
+ }
+ if (!(m_flag[i]&2)) { //We only moved the cell in 0-1
+ map_foreachincell(skill_unit_effect,unit1->bl.m,
+ unit1->bl.x,unit1->bl.y,group->bl_flag,&unit1->bl,tick,1);
+ }
+ }
+ aFree(m_flag);
+ return 0;
+}
+
+/*----------------------------------------------------------------------------
+ * アイテム??ャ
+ *----------------------------------------------------------------------------
+ */
+
+/*==========================================
+ * アイテム??ャ可能判定
+ *------------------------------------------
+ */
+int skill_can_produce_mix( struct map_session_data *sd, int nameid, int trigger, int qty)
+{
+ int i,j;
+
+ nullpo_retr(0, sd);
+
+ if(nameid<=0)
+ return 0;
+
+ for(i=0;i<MAX_SKILL_PRODUCE_DB;i++){
+ if(skill_produce_db[i].nameid == nameid )
+ break;
+ }
+ if( i >= MAX_SKILL_PRODUCE_DB ) /* デ?タベ?スにない */
+ return 0;
+
+ if(trigger>=0){
+ if(trigger>20) { // Non-weapon, non-food item (itemlv must match)
+ if(skill_produce_db[i].itemlv!=trigger)
+ return 0;
+ } else if(trigger>10) { // Food (itemlv must be higher or equal)
+ if(skill_produce_db[i].itemlv<=10 || skill_produce_db[i].itemlv>trigger)
+ return 0;
+ } else { // Weapon (itemlv must be higher or equal)
+ if(skill_produce_db[i].itemlv>trigger)
+ return 0;
+ }
+ }
+ if( (j=skill_produce_db[i].req_skill)>0 && pc_checkskill(sd,j)<=0 )
+ return 0; /* スキルが足りない */
+
+ for(j=0;j<MAX_PRODUCE_RESOURCE;j++){
+ int id,x,y;
+ if( (id=skill_produce_db[i].mat_id[j]) <= 0 ) /* これ以?繧ヘ?゙料要らない */
+ continue;
+ if(skill_produce_db[i].mat_amount[j] <= 0) {
+ if(pc_search_inventory(sd,id) < 0)
+ return 0;
+ }
+ else {
+ for(y=0,x=0;y<MAX_INVENTORY;y++)
+ if( sd->status.inventory[y].nameid == id )
+ x+=sd->status.inventory[y].amount;
+ if(x<qty*skill_produce_db[i].mat_amount[j]) /* アイテムが足りない */
+ return 0;
+ }
+ }
+ return i+1;
+}
+
+/*==========================================
+ * アイテム??ャ可能判定
+ *------------------------------------------
+ */
+int skill_produce_mix( struct map_session_data *sd, int skill_id,
+ int nameid, int slot1, int slot2, int slot3, int qty)
+{
+ int slot[3];
+ int i,sc,ele,idx,equip,wlv,make_per,flag;
+
+ nullpo_retr(0, sd);
+
+ if( !(idx=skill_can_produce_mix(sd,nameid,-1, qty)) ) /* ??不足 */
+ return 0;
+ idx--;
+
+ if (qty < 1)
+ qty = 1;
+
+ if (!skill_id) //A skill can be specified for some override cases.
+ skill_id = skill_produce_db[idx].req_skill;
+
+ slot[0]=slot1;
+ slot[1]=slot2;
+ slot[2]=slot3;
+
+ /* 埋め?み?? */
+ for(i=0,sc=0,ele=0;i<3;i++){ //Note that qty should always be one if you are using these!
+ int j;
+ if( slot[i]<=0 )
+ continue;
+ j = pc_search_inventory(sd,slot[i]);
+ if(j < 0) /* 不?ウパケット(アイテム存?ン)チェック */
+ continue;
+ if(slot[i]==1000){ /* Star Crumb */
+ pc_delitem(sd,j,1,1);
+ sc++;
+ }
+ if(slot[i]>=994 && slot[i]<=997 && ele==0){ /* Flame Heart . . . Great Nature */
+ static const int ele_table[4]={3,1,4,2};
+ pc_delitem(sd,j,1,1);
+ ele=ele_table[slot[i]-994];
+ }
+ }
+
+ /* ?゙料?チ費 */
+ for(i=0;i<MAX_PRODUCE_RESOURCE;i++){
+ int j,id,x;
+ if( (id=skill_produce_db[idx].mat_id[i]) <= 0 )
+ continue;
+ x=qty*skill_produce_db[idx].mat_amount[i]; /* 必要な個? */
+ do{ /* 2つ以?繧フインデックスにまたがっているかもしれない */
+ int y=0;
+ j = pc_search_inventory(sd,id);
+
+ if(j >= 0){
+ y = sd->status.inventory[j].amount;
+ if(y>x)y=x; /* 足りている */
+ pc_delitem(sd,j,y,0);
+ }else {
+ if(battle_config.error_log)
+ ShowError("skill_produce_mix: material item error\n");
+ }
+
+ x-=y; /* まだ足りない個?を計算 */
+ }while( j>=0 && x>0 ); /* ?゙料を?チ費するか?Aエラ?になるまで繰り返す */
+ }
+
+ if((equip=itemdb_isequip(nameid)))
+ wlv = itemdb_wlv(nameid);
+ if(!equip) {
+ switch(skill_id){
+ case BS_IRON:
+ case BS_STEEL:
+ case BS_ENCHANTEDSTONE:
+ { // Ores & Metals Refining - skill bonuses are straight from kRO website [DracoRPG]
+ int skill = pc_checkskill(sd,skill_id);
+ make_per = sd->status.job_level*20 + sd->paramc[4]*10 + sd->paramc[5]*10; //Base chance
+ switch(nameid){
+ case 998: // Iron
+ make_per += 4000+skill*500; // Temper Iron bonus: +26/+32/+38/+44/+50
+ break;
+ case 999: // Steel
+ make_per += 3000+skill*500; // Temper Steel bonus: +35/+40/+45/+50/+55
+ break;
+ case 1000: //Star Crumb
+ make_per = 100000; // Star Crumbs are 100% success crafting rate? (made 1000% so it succeeds even after penalties) [Skotlex]
+ break;
+ default: // Enchanted Stones
+ make_per += 1000+skill*500; // Enchantedstone Craft bonus: +15/+20/+25/+30/+35
+ break;
+ }
+ break;
+ case ASC_CDP:
+ make_per = (2000 + 40*sd->paramc[4] + 20*sd->paramc[5]);
+ break;
+ case AL_HOLYWATER:
+ make_per = 100000; //100% success
+ break;
+ case AM_PHARMACY: // Potion Preparation - reviewed with the help of various Ragnainfo sources [DracoRPG]
+ case AM_TWILIGHT1:
+ case AM_TWILIGHT2:
+ case AM_TWILIGHT3:
+ make_per = pc_checkskill(sd,AM_LEARNINGPOTION)*100
+ + pc_checkskill(sd,AM_PHARMACY)*300 + sd->status.job_level*20
+ + sd->paramc[3]*5 + sd->paramc[4]*10+sd->paramc[5]*10;
+ switch(nameid){
+ case 501: // Red Potion
+ case 503: // Yellow Potion
+ case 504: // White Potion
+ case 605: // Anodyne
+ case 606: // Aloevera
+ make_per += 2000;
+ break;
+ case 505: // Blue Potion
+ make_per -= 500;
+ break;
+ case 545: // Condensed Red Potion
+ case 546: // Condensed Yellow Potion
+ case 547: // Condensed White Potion
+ make_per -= 1000;
+ break;
+ case 970: // Alcohol
+ make_per += 1000;
+ break;
+ case 7139: // Glistening Coat
+ make_per -= 1000;
+ break;
+ case 7135: // Bottle Grenade
+ case 7136: // Acid Bottle
+ case 7137: // Plant Bottle
+ case 7138: // Marine Sphere Bottle
+ default:
+ break;
+ }
+ if(battle_config.pp_rate != 100)
+ make_per = make_per * battle_config.pp_rate / 100;
+ break;
+ case SA_CREATECON: // Elemental Converter Creation - skill bonuses are from kRO [DracoRPG]
+ make_per = pc_checkskill(sd, SA_ADVANCEDBOOK)*100 + //TODO: Advanced Book bonus is custom! [Skotlex]
+ sd->status.job_level*20 + sd->paramc[3]*10 + sd->paramc[4]*10;
+ switch(nameid){
+ case 12114:
+ flag = pc_checkskill(sd,SA_FLAMELAUNCHER);
+ if (flag > 0)
+ make_per += 1000*flag-500;
+ break;
+ case 12115:
+ flag = pc_checkskill(sd,SA_FROSTWEAPON);
+ if (flag > 0)
+ make_per += 1000*flag-500;
+ break;
+ case 12116:
+ flag = pc_checkskill(sd,SA_SEISMICWEAPON);
+ if (flag > 0)
+ make_per += 1000*flag-500;
+ break;
+ case 12117:
+ flag = pc_checkskill(sd,SA_LIGHTNINGLOADER);
+ if (flag > 0)
+ make_per += 1000*flag-500;
+ break;
+ }
+ break;
+ default:
+ make_per = 5000;
+ break;
+ }
+ }
+ } else { // Weapon Forging - skill bonuses are straight from kRO website, other things from a jRO calculator [DracoRPG]
+ make_per = 5000 + sd->status.job_level*20 + sd->paramc[4]*10 + sd->paramc[5]*10; // Base
+ make_per += pc_checkskill(sd,skill_id)*500; // Smithing skills bonus: +5/+10/+15
+ make_per += pc_checkskill(sd,BS_WEAPONRESEARCH)*100 +((wlv >= 3)? pc_checkskill(sd,BS_ORIDEOCON)*100:0); // Weaponry Research bonus: +1/+2/+3/+4/+5/+6/+7/+8/+9/+10, Oridecon Research bonus (custom): +1/+2/+3/+4/+5
+ make_per -= (ele?2000:0) + sc*1500 + (wlv>1?wlv*1000:0); // Element Stone: -20%, Star Crumb: -15% each, Weapon level malus: -0/-20/-30
+ if(pc_search_inventory(sd,989) > 0) make_per+= 1000; // Emperium Anvil: +10
+ else if(pc_search_inventory(sd,988) > 0) make_per+= 500; // Golden Anvil: +5
+ else if(pc_search_inventory(sd,987) > 0) make_per+= 300; // Oridecon Anvil: +3
+ else if(pc_search_inventory(sd,986) > 0) make_per+= 0; // Anvil: +0?
+ if(battle_config.wp_rate != 100)
+ make_per = make_per * battle_config.wp_rate / 100;
+ }
+// - Baby Class Penalty = 80% (from adult's chance) ----//
+ if (sd->class_&JOBL_BABY) //if it's a Baby Class
+ make_per = (make_per * 80) / 100; //Lupus
+
+ if(make_per < 1) make_per = 1;
+
+
+ if(rand()%10000 < make_per || qty > 1){ //Success, or crafting multiple items.
+ struct item tmp_item;
+ memset(&tmp_item,0,sizeof(tmp_item));
+ tmp_item.nameid=nameid;
+ tmp_item.amount=1;
+ tmp_item.identify=1;
+ if(equip){
+ tmp_item.card[0]=0x00ff;
+ tmp_item.card[1]=((sc*5)<<8)+ele;
+ tmp_item.card[2]=GetWord(sd->char_id,0); // CharId
+ tmp_item.card[3]=GetWord(sd->char_id,1);
+ } else {
+ //Flag is only used on the end, so it can be used here. [Skotlex]
+ switch (skill_id) {
+ case AM_PHARMACY:
+ case AM_TWILIGHT1:
+ case AM_TWILIGHT2:
+ case AM_TWILIGHT3:
+ flag = battle_config.produce_potion_name_input;
+ break;
+ case AL_HOLYWATER:
+ flag = battle_config.holywater_name_input;
+ break;
+ case ASC_CDP:
+ flag = battle_config.cdp_name_input;
+ break;
+ default:
+ flag = battle_config.produce_item_name_input;
+ break;
+ }
+ if (flag) {
+ tmp_item.card[0]=0x00fe;
+ tmp_item.card[1]=0;
+ tmp_item.card[2]=GetWord(sd->char_id,0); // CharId
+ tmp_item.card[3]=GetWord(sd->char_id,1);
+ }
+ }
+
+ if(log_config.produce > 0)
+ log_produce(sd,nameid,slot1,slot2,slot3,1);
+
+ if(equip){
+ clif_produceeffect(sd,0,nameid);
+ clif_misceffect(&sd->bl,3);
+ if(itemdb_wlv(nameid) >= 3 && ((ele? 1 : 0) + sc) >= 3) // Fame point system [DracoRPG]
+ pc_addfame(sd,10); // Success to forge a lv3 weapon with 3 additional ingredients = +10 fame point
+ } else {
+ int fame = 0;
+ tmp_item.amount = 0;
+ for (i=0; i< qty; i++)
+ { //Apply quantity modifiers.
+ if (rand()%10000 < make_per || qty == 1)
+ { //Success
+ tmp_item.amount++;
+ if(nameid < 545 || nameid > 547)
+ continue;
+ if(skill_id != AM_PHARMACY &&
+ skill_id != AM_TWILIGHT1 &&
+ skill_id != AM_TWILIGHT2 &&
+ skill_id != AM_TWILIGHT3)
+ continue;
+ //Add fame as needed.
+ switch(++sd->potion_success_counter) {
+ case 3:
+ fame+=1; // Success to prepare 3 Condensed Potions in a row
+ break;
+ case 5:
+ fame+=3; // Success to prepare 5 Condensed Potions in a row
+ break;
+ case 7:
+ fame+=10; // Success to prepare 7 Condensed Potions in a row
+ break;
+ case 10:
+ fame+=50; // Success to prepare 10 Condensed Potions in a row
+ sd->potion_success_counter = 0;
+ break;
+ }
+ } else //Failure
+ sd->potion_success_counter = 0;
+ }
+ if (fame)
+ pc_addfame(sd,fame);
+ //Visual effects and the like.
+ switch (skill_id) {
+ case AM_PHARMACY:
+ case AM_TWILIGHT1:
+ case AM_TWILIGHT2:
+ case AM_TWILIGHT3:
+ case ASC_CDP:
+ clif_produceeffect(sd,2,nameid);
+ clif_misceffect(&sd->bl,5);
+ break;
+ case BS_IRON:
+ case BS_STEEL:
+ case BS_ENCHANTEDSTONE:
+ clif_produceeffect(sd,0,nameid);
+ clif_misceffect(&sd->bl,3);
+ break;
+ default: //Those that don't require a skill?
+ if (skill_produce_db[idx].itemlv==11) //Cooking items.
+ clif_specialeffect(&sd->bl, 608, 0);
+ break;
+ }
+ }
+ if (tmp_item.amount) { //Success
+ if((flag = pc_additem(sd,&tmp_item,tmp_item.amount))) {
+ clif_additem(sd,0,0,flag);
+ map_addflooritem(&tmp_item,1,sd->bl.m,sd->bl.x,sd->bl.y,NULL,NULL,NULL,0);
+ }
+ return 1;
+ }
+ }
+ //Failure
+ if(log_config.produce)
+ log_produce(sd,nameid,slot1,slot2,slot3,0);
+
+ if(equip){
+ clif_produceeffect(sd,1,nameid);
+ clif_misceffect(&sd->bl,2);
+ } else {
+ switch (skill_id) {
+ case ASC_CDP: //Damage yourself, and display same effect as failed potion.
+ pc_heal(sd,-(sd->status.max_hp>>2),0);
+ case AM_PHARMACY:
+ case AM_TWILIGHT1:
+ case AM_TWILIGHT2:
+ case AM_TWILIGHT3:
+ clif_produceeffect(sd,3,nameid);
+ clif_misceffect(&sd->bl,6);
+ sd->potion_success_counter = 0; // Fame point system [DracoRPG]
+ break;
+ case BS_IRON:
+ case BS_STEEL:
+ case BS_ENCHANTEDSTONE:
+ clif_produceeffect(sd,1,nameid);
+ clif_misceffect(&sd->bl,2);
+ break;
+ default:
+ if (skill_produce_db[idx].itemlv==11)
+ clif_specialeffect(&sd->bl, 609, 0);
+ }
+ }
+ return 0;
+}
+
+int skill_arrow_create( struct map_session_data *sd,int nameid)
+{
+ int i,j,flag,index=-1;
+ struct item tmp_item;
+
+ nullpo_retr(0, sd);
+
+ if(nameid <= 0)
+ return 1;
+
+ for(i=0;i<MAX_SKILL_ARROW_DB;i++)
+ if(nameid == skill_arrow_db[i].nameid) {
+ index = i;
+ break;
+ }
+
+ if(index < 0 || (j = pc_search_inventory(sd,nameid)) < 0)
+ return 1;
+
+ pc_delitem(sd,j,1,0);
+ for(i=0;i<5;i++) {
+ memset(&tmp_item,0,sizeof(tmp_item));
+ tmp_item.identify = 1;
+ tmp_item.nameid = skill_arrow_db[index].cre_id[i];
+ tmp_item.amount = skill_arrow_db[index].cre_amount[i];
+ if(battle_config.making_arrow_name_input) {
+ tmp_item.card[0]=0x00fe;
+ tmp_item.card[1]=0;
+ tmp_item.card[2]=GetWord(sd->char_id,0); // CharId
+ tmp_item.card[3]=GetWord(sd->char_id,1);
+ }
+ if(tmp_item.nameid <= 0 || tmp_item.amount <= 0)
+ continue;
+ if((flag = pc_additem(sd,&tmp_item,tmp_item.amount))) {
+ clif_additem(sd,0,0,flag);
+ map_addflooritem(&tmp_item,tmp_item.amount,sd->bl.m,sd->bl.x,sd->bl.y,NULL,NULL,NULL,0);
+ }
+ }
+
+ return 0;
+}
+
+/*----------------------------------------------------------------------------
+ * ?炎化系
+ */
+
+/*
+ * 文字列??
+ * ',' で区?リって val に戻す
+ */
+int skill_split_str(char *str,char **val,int num)
+{
+ int i;
+
+ for (i=0; i<num && str; i++){
+ val[i] = str;
+ str = strchr(str,',');
+ if (str)
+ *str++=0;
+ }
+ return i;
+}
+/*
+ * 文字列??
+ * ':' で区?リってatoiしてvalに戻す
+ */
+int skill_split_atoi(char *str,int *val)
+{
+ int i, j, diff, step = 1;
+
+ for (i=0; i<MAX_SKILL_LEVEL; i++) {
+ if (!str) break;
+ val[i] = atoi(str);
+ str = strchr(str,':');
+ if (str)
+ *str++=0;
+ }
+ if(i==0) //No data found.
+ return 0;
+ if(i==1)
+ { //Single value, have the whole range have the same value.
+ for (; i < MAX_SKILL_LEVEL; i++)
+ val[i] = val[i-1];
+ return i;
+ }
+ //Check for linear change with increasing steps until we reach half of the data acquired.
+ for (step = 1; step <= i/2; step++)
+ {
+ diff = val[i-1] - val[i-step-1];
+ for(j = i-1; j >= step; j--)
+ if ((val[j]-val[j-step]) != diff)
+ break;
+
+ if (j>=step) //No match, try next step.
+ continue;
+
+ for(; i < MAX_SKILL_LEVEL; i++)
+ { //Apply linear increase
+ val[i] = val[i-step]+diff;
+ if (val[i] < 1 && val[i-1] >=0) //Check if we have switched from + to -, cap the decrease to 0 in said cases.
+ { val[i] = 1; diff = 0; step = 1; }
+ }
+ return i;
+ }
+ //Okay.. we can't figure this one out, just fill out the stuff with the previous value.
+ for (;i<MAX_SKILL_LEVEL; i++)
+ val[i] = val[i-1];
+ return i;
+}
+
+/*
+ * スキルユニットの配置?報??ャ
+ */
+void skill_init_unit_layout(void)
+{
+ int i,j,size,pos = 0;
+
+ memset(skill_unit_layout,0,sizeof(skill_unit_layout));
+ // 矩形のユニット配置を??ャする
+ for (i=0; i<=MAX_SQUARE_LAYOUT; i++) {
+ size = i*2+1;
+ skill_unit_layout[i].count = size*size;
+ for (j=0; j<size*size; j++) {
+ skill_unit_layout[i].dx[j] = (j%size-i);
+ skill_unit_layout[i].dy[j] = (j/size-i);
+ }
+ }
+ pos = i;
+ // 矩形以外のユニット配置を??ャする
+ for (i=0;i<MAX_SKILL_DB;i++) {
+ if (!skill_db[i].unit_id[0] || skill_db[i].unit_layout_type[0] != -1)
+ continue;
+ switch (i) {
+ case MG_FIREWALL:
+ case WZ_ICEWALL:
+ // ファイア?[ウォ?[ル?Aアイスウォ?[ルは方向で変わるので別??
+ break;
+ case PR_SANCTUARY:
+ {
+ static const int dx[] = {
+ -1, 0, 1,-2,-1, 0, 1, 2,-2,-1,
+ 0, 1, 2,-2,-1, 0, 1, 2,-1, 0, 1};
+ static const int dy[]={
+ -2,-2,-2,-1,-1,-1,-1,-1, 0, 0,
+ 0, 0, 0, 1, 1, 1, 1, 1, 2, 2, 2};
+ skill_unit_layout[pos].count = 21;
+ memcpy(skill_unit_layout[pos].dx,dx,sizeof(dx));
+ memcpy(skill_unit_layout[pos].dy,dy,sizeof(dy));
+ break;
+ }
+ case PR_MAGNUS:
+ {
+ static const int dx[] = {
+ -1, 0, 1,-1, 0, 1,-3,-2,-1, 0,
+ 1, 2, 3,-3,-2,-1, 0, 1, 2, 3,
+ -3,-2,-1, 0, 1, 2, 3,-1, 0, 1,-1, 0, 1};
+ static const int dy[] = {
+ -3,-3,-3,-2,-2,-2,-1,-1,-1,-1,
+ -1,-1,-1, 0, 0, 0, 0, 0, 0, 0,
+ 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 3, 3, 3};
+ skill_unit_layout[pos].count = 33;
+ memcpy(skill_unit_layout[pos].dx,dx,sizeof(dx));
+ memcpy(skill_unit_layout[pos].dy,dy,sizeof(dy));
+ break;
+ }
+ case AS_VENOMDUST:
+ {
+ static const int dx[] = {-1, 0, 0, 0, 1};
+ static const int dy[] = { 0,-1, 0, 1, 0};
+ skill_unit_layout[pos].count = 5;
+ memcpy(skill_unit_layout[pos].dx,dx,sizeof(dx));
+ memcpy(skill_unit_layout[pos].dy,dy,sizeof(dy));
+ break;
+ }
+ case CR_GRANDCROSS:
+ case NPC_GRANDDARKNESS:
+ {
+ static const int dx[] = {
+ 0, 0,-1, 0, 1,-2,-1, 0, 1, 2,
+ -4,-3,-2,-1, 0, 1, 2, 3, 4,-2,
+ -1, 0, 1, 2,-1, 0, 1, 0, 0};
+ static const int dy[] = {
+ -4,-3,-2,-2,-2,-1,-1,-1,-1,-1,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
+ 1, 1, 1, 1, 2, 2, 2, 3, 4};
+ skill_unit_layout[pos].count = 29;
+ memcpy(skill_unit_layout[pos].dx,dx,sizeof(dx));
+ memcpy(skill_unit_layout[pos].dy,dy,sizeof(dy));
+ break;
+ }
+ case PF_FOGWALL:
+ {
+ static const int dx[] = {
+ -2,-1, 0, 1, 2,-2,-1, 0, 1, 2,-2,-1, 0, 1, 2};
+ static const int dy[] = {
+ -1,-1,-1,-1,-1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1};
+ skill_unit_layout[pos].count = 15;
+ memcpy(skill_unit_layout[pos].dx,dx,sizeof(dx));
+ memcpy(skill_unit_layout[pos].dy,dy,sizeof(dy));
+ break;
+ }
+ case PA_GOSPEL:
+ {
+ static const int dx[] = {
+ -1, 0, 1,-1, 0, 1,-3,-2,-1, 0,
+ 1, 2, 3,-3,-2,-1, 0, 1, 2, 3,
+ -3,-2,-1, 0, 1, 2, 3,-1, 0, 1,
+ -1, 0, 1};
+ static const int dy[] = {
+ -3,-3,-3,-2,-2,-2,-1,-1,-1,-1,
+ -1,-1,-1, 0, 0, 0, 0, 0, 0, 0,
+ 1, 1, 1, 1, 1, 1, 1, 2, 2, 2,
+ 3, 3, 3};
+ skill_unit_layout[pos].count = 33;
+ memcpy(skill_unit_layout[pos].dx,dx,sizeof(dx));
+ memcpy(skill_unit_layout[pos].dy,dy,sizeof(dy));
+ break;
+ }
+ default:
+ ShowError("unknown unit layout at skill %d\n",i);
+ break;
+ }
+ if (!skill_unit_layout[pos].count)
+ continue;
+ for (j=0;j<MAX_SKILL_LEVEL;j++)
+ skill_db[i].unit_layout_type[j] = pos;
+ pos++;
+ }
+ // ファイヤ?[ウォ?[ル
+ firewall_unit_pos = pos;
+ for (i=0;i<8;i++) {
+ if (i&1) { /* 斜め配置 */
+ skill_unit_layout[pos].count = 5;
+ if (i&0x2) {
+ int dx[] = {-1,-1, 0, 0, 1};
+ int dy[] = { 1, 0, 0,-1,-1};
+ memcpy(skill_unit_layout[pos].dx,dx,sizeof(dx));
+ memcpy(skill_unit_layout[pos].dy,dy,sizeof(dy));
+ } else {
+ int dx[] = { 1, 1 ,0, 0,-1};
+ int dy[] = { 1, 0, 0,-1,-1};
+ memcpy(skill_unit_layout[pos].dx,dx,sizeof(dx));
+ memcpy(skill_unit_layout[pos].dy,dy,sizeof(dy));
+ }
+ } else { /* ?c横配置 */
+ skill_unit_layout[pos].count = 3;
+ if (i%4==0) { /* ?繪コ */
+ int dx[] = {-1, 0, 1};
+ int dy[] = { 0, 0, 0};
+ memcpy(skill_unit_layout[pos].dx,dx,sizeof(dx));
+ memcpy(skill_unit_layout[pos].dy,dy,sizeof(dy));
+ } else { /* ?カ右 */
+ int dx[] = { 0, 0, 0};
+ int dy[] = {-1, 0, 1};
+ memcpy(skill_unit_layout[pos].dx,dx,sizeof(dx));
+ memcpy(skill_unit_layout[pos].dy,dy,sizeof(dy));
+ }
+ }
+ pos++;
+ }
+ // アイスウォ?[ル
+ icewall_unit_pos = pos;
+ for (i=0;i<8;i++) {
+ skill_unit_layout[pos].count = 5;
+ if (i&1) { /* 斜め配置 */
+ if (i&0x2) {
+ int dx[] = {-2,-1, 0, 1, 2};
+ int dy[] = { 2,-1, 0,-1,-2};
+ memcpy(skill_unit_layout[pos].dx,dx,sizeof(dx));
+ memcpy(skill_unit_layout[pos].dy,dy,sizeof(dy));
+ } else {
+ int dx[] = { 2, 1 ,0,-1,-2};
+ int dy[] = { 2, 1, 0,-1,-2};
+ memcpy(skill_unit_layout[pos].dx,dx,sizeof(dx));
+ memcpy(skill_unit_layout[pos].dy,dy,sizeof(dy));
+ }
+ } else { /* ?c横配置 */
+ if (i%4==0) { /* ?繪コ */
+ int dx[] = {-2,-1, 0, 1, 2};
+ int dy[] = { 0, 0, 0, 0, 0};
+ memcpy(skill_unit_layout[pos].dx,dx,sizeof(dx));
+ memcpy(skill_unit_layout[pos].dy,dy,sizeof(dy));
+ } else { /* ?カ右 */
+ int dx[] = { 0, 0, 0, 0, 0};
+ int dy[] = {-2,-1, 0, 1, 2};
+ memcpy(skill_unit_layout[pos].dx,dx,sizeof(dx));
+ memcpy(skill_unit_layout[pos].dy,dy,sizeof(dy));
+ }
+ }
+ pos++;
+ }
+}
+
+/*==========================================
+ * スキル?係ファイル?み?み
+ * skill_db.txt スキルデ?タ
+ * skill_cast_db.txt スキルの詠?・時間とディレイデ?タ
+ * produce_db.txt アイテム??ャスキル用デ?タ
+ * create_arrow_db.txt 矢??ャスキル用デ?タ
+ * abra_db.txt アブラカダブラ?動スキルデ?タ
+ *------------------------------------------
+ */
+int skill_readdb(void)
+{
+ int i,j,k,l,m;
+ FILE *fp;
+ char line[1024],path[1024],*p;
+ char *filename[]={"produce_db.txt","produce_db2.txt"};
+
+ /* スキルデ?タベ?ス */
+ memset(skill_db,0,sizeof(skill_db));
+ sprintf(path, "%s/skill_db.txt", db_path);
+ fp=fopen(path,"r");
+ if(fp==NULL){
+ ShowError("can't read %s\n", path);
+ return 1;
+ }
+ while(fgets(line,1020,fp)){
+ char *split[50];
+ if(line[0]=='/' && line[1]=='/')
+ continue;
+ j = skill_split_str(line,split,14);
+ if(j < 14 || split[13]==NULL)
+ continue;
+
+ i=atoi(split[0]);
+ if (i>=10000 && i<10015) // for guild skills [Celest]
+ i -= 9500;
+ else if(i<=0 || i>MAX_SKILL_DB)
+ continue;
+
+ skill_split_atoi(split[1],skill_db[i].range);
+ skill_db[i].hit=atoi(split[2]);
+ skill_db[i].inf=atoi(split[3]);
+ skill_db[i].pl=atoi(split[4]);
+ skill_db[i].nk=atoi(split[5]);
+ skill_db[i].max=atoi(split[6]);
+ skill_split_atoi(split[7],skill_db[i].num);
+
+ if(strcmpi(split[8],"yes") == 0)
+ skill_db[i].castcancel=1;
+ else
+ skill_db[i].castcancel=0;
+ skill_db[i].cast_def_rate=atoi(split[9]);
+ skill_db[i].inf2=atoi(split[10]);
+ skill_db[i].maxcount=atoi(split[11]);
+ if(strcmpi(split[12],"weapon") == 0)
+ skill_db[i].skill_type=BF_WEAPON;
+ else if(strcmpi(split[12],"magic") == 0)
+ skill_db[i].skill_type=BF_MAGIC;
+ else if(strcmpi(split[12],"misc") == 0)
+ skill_db[i].skill_type=BF_MISC;
+ else
+ skill_db[i].skill_type=0;
+ skill_split_atoi(split[13],skill_db[i].blewcount);
+
+ for (j = 0; skill_names[j].id != 0; j++)
+ if (skill_names[j].id == i) {
+ skill_db[i].name = skill_names[j].name;
+ skill_db[i].desc = skill_names[j].desc;
+ break;
+ }
+ }
+ fclose(fp);
+ ShowStatus("Done reading '"CL_WHITE"%s"CL_RESET"'.\n",path);
+
+ sprintf(path, "%s/skill_require_db.txt", db_path);
+ fp=fopen(path,"r");
+ if(fp==NULL){
+ ShowError("can't read %s\n", path);
+ return 1;
+ }
+ while(fgets(line,1020,fp)){
+ char *split[50];
+ if(line[0]=='/' && line[1]=='/')
+ continue;
+ j = skill_split_str(line,split,30);
+ if(j < 30 || split[29]==NULL)
+ continue;
+
+ i=atoi(split[0]);
+ if (i>=10000 && i<10015) // for guild skills [Celest]
+ i -= 9500;
+ else if(i<=0 || i>MAX_SKILL_DB)
+ continue;
+
+ skill_split_atoi(split[1],skill_db[i].hp);
+ skill_split_atoi(split[2],skill_db[i].mhp);
+ skill_split_atoi(split[3],skill_db[i].sp);
+ skill_split_atoi(split[4],skill_db[i].hp_rate);
+ skill_split_atoi(split[5],skill_db[i].sp_rate);
+ skill_split_atoi(split[6],skill_db[i].zeny);
+
+ p = split[7];
+ for(j=0;j<32;j++){
+ l = atoi(p);
+ if (l==99) {
+ skill_db[i].weapon = 0xffffffff;
+ break;
+ }
+ else
+ skill_db[i].weapon |= 1<<l;
+ p=strchr(p,':');
+ if(!p)
+ break;
+ p++;
+ }
+
+ if( strcmpi(split[8],"hiding")==0 ) skill_db[i].state=ST_HIDING;
+ else if( strcmpi(split[8],"cloaking")==0 ) skill_db[i].state=ST_CLOAKING;
+ else if( strcmpi(split[8],"hidden")==0 ) skill_db[i].state=ST_HIDDEN;
+ else if( strcmpi(split[8],"riding")==0 ) skill_db[i].state=ST_RIDING;
+ else if( strcmpi(split[8],"falcon")==0 ) skill_db[i].state=ST_FALCON;
+ else if( strcmpi(split[8],"cart")==0 ) skill_db[i].state=ST_CART;
+ else if( strcmpi(split[8],"shield")==0 ) skill_db[i].state=ST_SHIELD;
+ else if( strcmpi(split[8],"sight")==0 ) skill_db[i].state=ST_SIGHT;
+ else if( strcmpi(split[8],"explosionspirits")==0 ) skill_db[i].state=ST_EXPLOSIONSPIRITS;
+ else if( strcmpi(split[8],"cartboost")==0 ) skill_db[i].state=ST_CARTBOOST;
+ else if( strcmpi(split[8],"recover_weight_rate")==0 ) skill_db[i].state=ST_RECOV_WEIGHT_RATE;
+ else if( strcmpi(split[8],"move_enable")==0 ) skill_db[i].state=ST_MOVE_ENABLE;
+ else if( strcmpi(split[8],"water")==0 ) skill_db[i].state=ST_WATER;
+ else skill_db[i].state=ST_NONE;
+
+ skill_split_atoi(split[9],skill_db[i].spiritball);
+ skill_db[i].itemid[0]=atoi(split[10]);
+ skill_db[i].amount[0]=atoi(split[11]);
+ skill_db[i].itemid[1]=atoi(split[12]);
+ skill_db[i].amount[1]=atoi(split[13]);
+ skill_db[i].itemid[2]=atoi(split[14]);
+ skill_db[i].amount[2]=atoi(split[15]);
+ skill_db[i].itemid[3]=atoi(split[16]);
+ skill_db[i].amount[3]=atoi(split[17]);
+ skill_db[i].itemid[4]=atoi(split[18]);
+ skill_db[i].amount[4]=atoi(split[19]);
+ skill_db[i].itemid[5]=atoi(split[20]);
+ skill_db[i].amount[5]=atoi(split[21]);
+ skill_db[i].itemid[6]=atoi(split[22]);
+ skill_db[i].amount[6]=atoi(split[23]);
+ skill_db[i].itemid[7]=atoi(split[24]);
+ skill_db[i].amount[7]=atoi(split[25]);
+ skill_db[i].itemid[8]=atoi(split[26]);
+ skill_db[i].amount[8]=atoi(split[27]);
+ skill_db[i].itemid[9]=atoi(split[28]);
+ skill_db[i].amount[9]=atoi(split[29]);
+ }
+ fclose(fp);
+ ShowStatus("Done reading '"CL_WHITE"%s"CL_RESET"'.\n",path);
+
+ /* キャスティングデ?タベ?ス */
+
+ sprintf(path, "%s/skill_cast_db.txt", db_path);
+ fp=fopen(path,"r");
+ if(fp==NULL){
+ ShowError("can't read %s\n", path);
+ return 1;
+ }
+ while(fgets(line,1020,fp)){
+ char *split[50];
+ memset(split,0,sizeof(split)); // [Valaris] thanks to fov
+ if(line[0]=='/' && line[1]=='/')
+ continue;
+ j = skill_split_str(line,split,5);
+ if(split[4]==NULL || j<5)
+ continue;
+
+ i=atoi(split[0]);
+ if (i>=10000 && i<10015) // for guild skills [Celest]
+ i -= 9500;
+ else if(i<=0 || i>MAX_SKILL_DB)
+ continue;
+
+ skill_split_atoi(split[1],skill_db[i].cast);
+ skill_split_atoi(split[2],skill_db[i].delay);
+ skill_split_atoi(split[3],skill_db[i].upkeep_time);
+ skill_split_atoi(split[4],skill_db[i].upkeep_time2);
+ }
+ fclose(fp);
+ ShowStatus("Done reading '"CL_WHITE"%s"CL_RESET"'.\n",path);
+
+ /* スキルユニットデ?[タベ?[ス */
+
+ sprintf(path, "%s/skill_unit_db.txt", db_path);
+ fp=fopen(path,"r");
+ if (fp==NULL) {
+ ShowError("can't read %s\n", path);
+ return 1;
+ }
+ k = 0;
+ while (fgets(line,1020,fp)) {
+ char *split[50];
+ if (line[0]=='/' && line[1]=='/')
+ continue;
+ j = skill_split_str(line,split,8);
+ if (split[7]==NULL || j<8)
+ continue;
+
+ i=atoi(split[0]);
+ if (i>=10000 && i<10015) // for guild skills [Celest]
+ i -= 9500;
+ else if(i<=0 || i>MAX_SKILL_DB)
+ continue;
+ skill_db[i].unit_id[0] = strtol(split[1],NULL,16);
+ skill_db[i].unit_id[1] = strtol(split[2],NULL,16);
+ skill_split_atoi(split[3],skill_db[i].unit_layout_type);
+ skill_db[i].unit_range = atoi(split[4]);
+ skill_db[i].unit_interval = atoi(split[5]);
+
+ if( strcmpi(split[6],"noenemy")==0 ) skill_db[i].unit_target=BCT_NOENEMY;
+ else if( strcmpi(split[6],"friend")==0 ) skill_db[i].unit_target=BCT_NOENEMY;
+ else if( strcmpi(split[6],"party")==0 ) skill_db[i].unit_target=BCT_PARTY;
+ else if( strcmpi(split[6],"ally")==0 ) skill_db[i].unit_target=BCT_PARTY|BCT_GUILD;
+ else if( strcmpi(split[6],"all")==0 ) skill_db[i].unit_target=BCT_ALL;
+ else if( strcmpi(split[6],"enemy")==0 ) skill_db[i].unit_target=BCT_ENEMY;
+ else if( strcmpi(split[6],"self")==0 ) skill_db[i].unit_target=BCT_SELF;
+ else if( strcmpi(split[6],"noone")==0 ) skill_db[i].unit_target=BCT_NOONE;
+ else skill_db[i].unit_target = strtol(split[6],NULL,16);
+
+ skill_db[i].unit_flag = strtol(split[7],NULL,16);
+
+ if (skill_db[i].unit_flag&UF_DEFNOTENEMY && battle_config.defnotenemy)
+ skill_db[i].unit_target=BCT_NOENEMY;
+
+ //By default, target just characters.
+ skill_db[i].unit_target |= BL_CHAR;
+ if (skill_db[i].unit_flag&UF_NOPC)
+ skill_db[i].unit_target &= ~BL_PC;
+ if (skill_db[i].unit_flag&UF_NOMOB)
+ skill_db[i].unit_target &= ~BL_MOB;
+ if (skill_db[i].unit_flag&UF_SKILL)
+ skill_db[i].unit_target |= BL_SKILL;
+ k++;
+ }
+ fclose(fp);
+ ShowStatus("Done reading '"CL_WHITE"%s"CL_RESET"'.\n",path);
+ skill_init_unit_layout();
+
+ /* ?サ造系スキルデ?タベ?ス */
+ memset(skill_produce_db,0,sizeof(skill_produce_db));
+ for(m=0;m<2;m++){
+ sprintf(path, "%s/%s", db_path, filename[m]);
+ fp=fopen(path,"r");
+ if(fp==NULL){
+ if(m>0)
+ continue;
+ ShowError("can't read %s\n",path);
+ return 1;
+ }
+ k=0;
+ while(fgets(line,1020,fp)){
+ char *split[6 + MAX_PRODUCE_RESOURCE * 2];
+ int x,y;
+ if(line[0]=='/' && line[1]=='/')
+ continue;
+ memset(split,0,sizeof(split));
+ j = skill_split_str(line,split,(3 + MAX_PRODUCE_RESOURCE * 2));
+ if(split[0]==0) //fixed by Lupus
+ continue;
+ i=atoi(split[0]);
+ if(i<=0) continue;
+
+ skill_produce_db[k].nameid=i;
+ skill_produce_db[k].itemlv=atoi(split[1]);
+ skill_produce_db[k].req_skill=atoi(split[2]);
+
+ for(x=3,y=0; split[x] && split[x+1] && y<MAX_PRODUCE_RESOURCE; x+=2,y++){
+ skill_produce_db[k].mat_id[y]=atoi(split[x]);
+ skill_produce_db[k].mat_amount[y]=atoi(split[x+1]);
+ }
+ k++;
+ if(k >= MAX_SKILL_PRODUCE_DB)
+ break;
+ }
+ fclose(fp);
+ ShowStatus("Done reading '"CL_WHITE"%d"CL_RESET"' entries in '"CL_WHITE"%s"CL_RESET"'.\n",k,path);
+ }
+
+ memset(skill_arrow_db,0,sizeof(skill_arrow_db));
+
+ sprintf(path, "%s/create_arrow_db.txt", db_path);
+ fp=fopen(path,"r");
+ if(fp==NULL){
+ ShowError("can't read %s\n", path);
+ return 1;
+ }
+ k=0;
+ while(fgets(line,1020,fp)){
+ char *split[16];
+ int x,y;
+ if(line[0]=='/' && line[1]=='/')
+ continue;
+ memset(split,0,sizeof(split));
+ j = skill_split_str(line,split,13);
+ if(split[0]==0) //fixed by Lupus
+ continue;
+ i=atoi(split[0]);
+ if(i<=0)
+ continue;
+
+ skill_arrow_db[k].nameid=i;
+
+ for(x=1,y=0;split[x] && split[x+1] && y<5;x+=2,y++){
+ skill_arrow_db[k].cre_id[y]=atoi(split[x]);
+ skill_arrow_db[k].cre_amount[y]=atoi(split[x+1]);
+ }
+ k++;
+ if(k >= MAX_SKILL_ARROW_DB)
+ break;
+ }
+ fclose(fp);
+ ShowStatus("Done reading '"CL_WHITE"%d"CL_RESET"' entries in '"CL_WHITE"%s"CL_RESET"'.\n",k,path);
+
+ memset(skill_abra_db,0,sizeof(skill_abra_db));
+ sprintf(path, "%s/abra_db.txt", db_path);
+ fp=fopen(path,"r");
+ if(fp==NULL){
+ ShowError("can't read %s\n", path);
+ return 1;
+ }
+ k=0;
+ while(fgets(line,1020,fp)){
+ char *split[16];
+ if(line[0]=='/' && line[1]=='/')
+ continue;
+ memset(split,0,sizeof(split));
+ j = skill_split_str(line,split,13);
+ if(split[0]==0) //fixed by Lupus
+ continue;
+ i=atoi(split[0]);
+ if(i<=0)
+ continue;
+
+ skill_abra_db[i].req_lv=atoi(split[2]);
+ skill_abra_db[i].per=atoi(split[3]);
+
+ k++;
+ if(k >= MAX_SKILL_ABRA_DB)
+ break;
+ }
+ fclose(fp);
+ ShowStatus("Done reading '"CL_WHITE"%d"CL_RESET"' entries in '"CL_WHITE"%s"CL_RESET"'.\n",k,path);
+
+ sprintf(path, "%s/skill_castnodex_db.txt", db_path);
+ fp=fopen(path,"r");
+ if(fp==NULL){
+ ShowError("can't read %s\n", path);
+ return 1;
+ }
+ while(fgets(line,1020,fp)){
+ char *split[50];
+ if(line[0]=='/' && line[1]=='/')
+ continue;
+ memset(split,0,sizeof(split));
+ j = skill_split_str(line,split,4);
+ if(split[0]==0) //fixed by Lupus
+ continue;
+ i=atoi(split[0]);
+ if (i>=10000 && i<10015) // for guild skills [Celest]
+ i -= 9500;
+ else if(i<=0 || i>MAX_SKILL_DB)
+ continue;
+
+ skill_split_atoi(split[1],skill_db[i].castnodex);
+ if (!split[2])
+ continue;
+ skill_split_atoi(split[2],skill_db[i].delaynodex);
+ if(!split[3])
+ continue;
+ skill_split_atoi(split[3],skill_db[i].delaynowalk);
+ }
+ fclose(fp);
+ ShowStatus("Done reading '"CL_WHITE"%s"CL_RESET"'.\n",path);
+
+ sprintf(path, "%s/skill_nocast_db.txt", db_path);
+ fp=fopen(path,"r");
+ if(fp==NULL){
+ ShowError("can't read %s\n", path);
+ return 1;
+ }
+ k=0;
+ while(fgets(line,1020,fp)){
+ char *split[16];
+ if(line[0]=='/' && line[1]=='/')
+ continue;
+ memset(split,0,sizeof(split));
+ j = skill_split_str(line,split,2);
+ if(split[0]==0) //fixed by Lupus
+ continue;
+ i=atoi(split[0]);
+ if (i>=10000 && i<10015) // for guild skills [Celest]
+ i -= 9500;
+ else if(i<=0 || i>MAX_SKILL_DB)
+ continue;
+ skill_db[i].nocast=atoi(split[1]);
+ k++;
+ }
+ fclose(fp);
+ ShowStatus("Done reading '"CL_WHITE"%s"CL_RESET"'.\n",path);
+
+ return 0;
+}
+
+// SQL Skill database reading [Valaris]
+#ifndef TXT_ONLY
+int skill_read_sqldb(void)
+{
+ const char unknown_str[NAME_LENGTH] ="unknown";
+ int i,j,k,l,m;
+ FILE *fp;
+ char line[1024],path[1024],*p;
+ char *filename[]={"produce_db.txt","produce_db2.txt"};
+ long unsigned int ln = 0;
+
+ /* スキルデ?タベ?ス */
+ memset(skill_db,0,sizeof(skill_db));
+
+ //For easier handling of converting. [Skotlex]
+#define TO_INT(a) (sql_row[a]==NULL?0:atoi(sql_row[a]))
+#define TO_STR(a) (sql_row[a]==NULL?unknown_str:sql_row[a])
+ sprintf (tmp_sql, "SELECT * FROM `%s`", skill_sqldb);
+ if (mysql_query(&mmysql_handle, tmp_sql)) {
+ ShowSQL("DB error (%s) - %s\n", skill_sqldb, mysql_error(&mmysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ return 1;
+ }
+ sql_res = mysql_store_result(&mmysql_handle);
+ if (sql_res) {
+ while((sql_row = mysql_fetch_row(sql_res))){
+ i=TO_INT(0);
+ if (i>=10000 && i<10015) // for guild skills [Celest]
+ i -= 9500;
+ else if(i<=0 || i>MAX_SKILL_DB)
+ continue;
+
+ ln++;
+
+ skill_split_atoi(sql_row[1],skill_db[i].range);
+ skill_db[i].hit=TO_INT(2);
+ skill_db[i].inf=TO_INT(3);
+ skill_db[i].pl=TO_INT(4);
+ skill_db[i].nk=TO_INT(5);
+ skill_db[i].max=TO_INT(6);
+ skill_split_atoi(sql_row[7],skill_db[i].num);
+
+ if(strcmpi(TO_STR(8),"yes") == 0)
+ skill_db[i].castcancel=1;
+ else
+ skill_db[i].castcancel=0;
+ skill_db[i].cast_def_rate=TO_INT(9);
+ skill_db[i].inf2=TO_INT(10);
+ skill_db[i].maxcount=TO_INT(11);
+ if(strcmpi(TO_STR(12),"weapon") == 0)
+ skill_db[i].skill_type=BF_WEAPON;
+ else if(strcmpi(TO_STR(12),"magic") == 0)
+ skill_db[i].skill_type=BF_MAGIC;
+ else if(strcmpi(TO_STR(12),"misc") == 0)
+ skill_db[i].skill_type=BF_MISC;
+ else
+ skill_db[i].skill_type=0;
+ skill_split_atoi(sql_row[13],skill_db[i].blewcount);
+
+ for (j = 0; skill_names[j].id != 0; j++)
+ if (skill_names[j].id == i) {
+ skill_db[i].name = skill_names[j].name;
+ skill_db[i].desc = skill_names[j].desc;
+ break;
+ }
+ }
+ mysql_free_result(sql_res);
+ ShowStatus("Done reading '"CL_WHITE"%lu"CL_RESET"' entries in '"CL_WHITE"%s"CL_RESET"'.\n", ln, skill_sqldb);
+ ln=0;
+ }
+
+ sprintf (tmp_sql, "SELECT * FROM `%s`", skill_require_sqldb);
+ if (mysql_query(&mmysql_handle, tmp_sql)) {
+ ShowSQL("DB error (%s) - %s\n", skill_require_sqldb, mysql_error(&mmysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ return 1;
+ }
+ sql_res = mysql_store_result(&mmysql_handle);
+ if (sql_res) {
+ while((sql_row = mysql_fetch_row(sql_res))){
+ i=TO_INT(0);
+ if (i>=10000 && i<10015) // for guild skills [Celest]
+ i -= 9500;
+ else if(i<=0 || i>MAX_SKILL_DB)
+ continue;
+
+ ln++;
+
+ skill_split_atoi(sql_row[1],skill_db[i].hp);
+ skill_split_atoi(sql_row[2],skill_db[i].mhp);
+ skill_split_atoi(sql_row[3],skill_db[i].sp);
+ skill_split_atoi(sql_row[4],skill_db[i].hp_rate);
+ skill_split_atoi(sql_row[5],skill_db[i].sp_rate);
+ skill_split_atoi(sql_row[6],skill_db[i].zeny);
+
+ p = sql_row[7];
+ for(j=0;j<32;j++){
+ l = atoi(p);
+ if (l==99) {
+ skill_db[i].weapon = 0xffffffff;
+ break;
+ }
+ else
+ skill_db[i].weapon |= 1<<l;
+ p=strchr(p,':');
+ if(!p)
+ break;
+ p++;
+ }
+
+ if( strcmpi(TO_STR(8),"hiding")==0 ) skill_db[i].state=ST_HIDING;
+ else if( strcmpi(TO_STR(8),"cloaking")==0 ) skill_db[i].state=ST_CLOAKING;
+ else if( strcmpi(TO_STR(8),"hidden")==0 ) skill_db[i].state=ST_HIDDEN;
+ else if( strcmpi(TO_STR(8),"riding")==0 ) skill_db[i].state=ST_RIDING;
+ else if( strcmpi(TO_STR(8),"falcon")==0 ) skill_db[i].state=ST_FALCON;
+ else if( strcmpi(TO_STR(8),"cart")==0 ) skill_db[i].state=ST_CART;
+ else if( strcmpi(TO_STR(8),"shield")==0 ) skill_db[i].state=ST_SHIELD;
+ else if( strcmpi(TO_STR(8),"sight")==0 ) skill_db[i].state=ST_SIGHT;
+ else if( strcmpi(TO_STR(8),"explosionspirits")==0 ) skill_db[i].state=ST_EXPLOSIONSPIRITS;
+ else if( strcmpi(TO_STR(8),"cartboost")==0 ) skill_db[i].state=ST_CARTBOOST;
+ else if( strcmpi(TO_STR(8),"recover_weight_rate")==0 ) skill_db[i].state=ST_RECOV_WEIGHT_RATE;
+ else if( strcmpi(TO_STR(8),"move_enable")==0 ) skill_db[i].state=ST_MOVE_ENABLE;
+ else if( strcmpi(TO_STR(8),"water")==0 ) skill_db[i].state=ST_WATER;
+ else skill_db[i].state=ST_NONE;
+
+ skill_split_atoi(sql_row[9],skill_db[i].spiritball);
+ skill_db[i].itemid[0]=TO_INT(10);
+ skill_db[i].amount[0]=TO_INT(11);
+ skill_db[i].itemid[1]=TO_INT(12);
+ skill_db[i].amount[1]=TO_INT(13);
+ skill_db[i].itemid[2]=TO_INT(14);
+ skill_db[i].amount[2]=TO_INT(15);
+ skill_db[i].itemid[3]=TO_INT(16);
+ skill_db[i].amount[3]=TO_INT(17);
+ skill_db[i].itemid[4]=TO_INT(18);
+ skill_db[i].amount[4]=TO_INT(19);
+ skill_db[i].itemid[5]=TO_INT(20);
+ skill_db[i].amount[5]=TO_INT(21);
+ skill_db[i].itemid[6]=TO_INT(22);
+ skill_db[i].amount[6]=TO_INT(23);
+ skill_db[i].itemid[7]=TO_INT(24);
+ skill_db[i].amount[7]=TO_INT(25);
+ skill_db[i].itemid[8]=TO_INT(26);
+ skill_db[i].amount[8]=TO_INT(27);
+ skill_db[i].itemid[9]=TO_INT(28);
+ skill_db[i].amount[9]=TO_INT(29);
+ }
+ mysql_free_result(sql_res);
+ ShowStatus("Done reading '"CL_WHITE"%lu"CL_RESET"' entries in '"CL_WHITE"%s"CL_RESET"'.\n", ln, skill_require_sqldb);
+ ln=0;
+ }
+
+ sprintf (tmp_sql, "SELECT * FROM `%s`", cast_sqldb);
+ if (mysql_query(&mmysql_handle, tmp_sql)) {
+ ShowSQL("DB error (%s) - %s\n", cast_sqldb, mysql_error(&mmysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ return 1;
+ }
+ sql_res = mysql_store_result(&mmysql_handle);
+ if (sql_res) {
+ while((sql_row = mysql_fetch_row(sql_res))){
+ i=TO_INT(0);
+ if (i>=10000 && i<10015) // for guild skills [Celest]
+ i -= 9500;
+ else if(i<=0 || i>MAX_SKILL_DB)
+ continue;
+
+ ln++;
+
+ skill_split_atoi(sql_row[1],skill_db[i].cast);
+ skill_split_atoi(sql_row[2],skill_db[i].delay);
+ skill_split_atoi(sql_row[3],skill_db[i].upkeep_time);
+ skill_split_atoi(sql_row[4],skill_db[i].upkeep_time2);
+ }
+ mysql_free_result(sql_res);
+ ShowStatus("Done reading '"CL_WHITE"%lu"CL_RESET"' entries in '"CL_WHITE"%s"CL_RESET"'.\n", ln, cast_sqldb);
+ ln=0;
+ }
+
+ /* スキルユニットデ?[タベ?[ス */
+
+ sprintf(path, "%s/skill_unit_db.txt", db_path);
+ fp=fopen(path,"r");
+ if (fp==NULL) {
+ ShowError("can't read %s\n", path);
+ return 1;
+ }
+ k = 0;
+ while (fgets(line,1020,fp)) {
+ char *split[50];
+ if (line[0]=='/' && line[1]=='/')
+ continue;
+ j = skill_split_str(line,split,8);
+ if (split[7]==NULL || j<8)
+ continue;
+
+ i=atoi(split[0]);
+ if (i>=10000 && i<10015) // for guild skills [Celest]
+ i -= 9500;
+ else if(i<=0 || i>MAX_SKILL_DB)
+ continue;
+ skill_db[i].unit_id[0] = strtol(split[1],NULL,16);
+ skill_db[i].unit_id[1] = strtol(split[2],NULL,16);
+ skill_split_atoi(split[3],skill_db[i].unit_layout_type);
+ skill_db[i].unit_range = atoi(split[4]);
+ skill_db[i].unit_interval = atoi(split[5]);
+
+ if( strcmpi(split[6],"noenemy")==0 ) skill_db[i].unit_target=BCT_NOENEMY;
+ else if( strcmpi(split[6],"friend")==0 ) skill_db[i].unit_target=BCT_NOENEMY;
+ else if( strcmpi(split[6],"party")==0 ) skill_db[i].unit_target=BCT_PARTY;
+ else if( strcmpi(split[6],"ally")==0 ) skill_db[i].unit_target=BCT_PARTY|BCT_GUILD;
+ else if( strcmpi(split[6],"all")==0 ) skill_db[i].unit_target=BCT_ALL;
+ else if( strcmpi(split[6],"enemy")==0 ) skill_db[i].unit_target=BCT_ENEMY;
+ else if( strcmpi(split[6],"self")==0 ) skill_db[i].unit_target=BCT_SELF;
+ else if( strcmpi(split[6],"noone")==0 ) skill_db[i].unit_target=BCT_NOONE;
+ else skill_db[i].unit_target = strtol(split[6],NULL,16);
+
+ skill_db[i].unit_flag = strtol(split[7],NULL,16);
+ if (skill_db[i].unit_flag&UF_DEFNOTENEMY && battle_config.defnotenemy)
+ skill_db[i].unit_target=BCT_NOENEMY;
+
+ //By default, target just characters.
+ skill_db[i].unit_target |= BL_CHAR;
+ if (skill_db[i].unit_flag&UF_NOPC)
+ skill_db[i].unit_target &= ~BL_PC;
+ if (skill_db[i].unit_flag&UF_NOMOB)
+ skill_db[i].unit_target &= ~BL_MOB;
+ if (skill_db[i].unit_flag&UF_SKILL)
+ skill_db[i].unit_target |= BL_SKILL;
+
+ k++;
+ }
+ fclose(fp);
+ ShowStatus("Done reading '"CL_WHITE"%s"CL_RESET"'.\n",path);
+ skill_init_unit_layout();
+
+ /* ?サ造系スキルデ?タベ?ス */
+ memset(skill_produce_db,0,sizeof(skill_produce_db));
+ for(m=0;m<2;m++){
+ sprintf(path, "%s/%s", db_path, filename[m]);
+ fp=fopen(path,"r");
+ if(fp==NULL){
+ if(m>0)
+ continue;
+ ShowError("can't read %s\n",path);
+ return 1;
+ }
+ k=0;
+ while(fgets(line,1020,fp)){
+ char *split[6 + MAX_PRODUCE_RESOURCE * 2];
+ int x,y;
+ if(line[0]=='/' && line[1]=='/')
+ continue;
+ memset(split,0,sizeof(split));
+ j = skill_split_str(line,split,(3 + MAX_PRODUCE_RESOURCE * 2));
+ if(split[0]==0) //fixed by Lupus
+ continue;
+ i=atoi(split[0]);
+ if(i<=0) continue;
+
+ skill_produce_db[k].nameid=i;
+ skill_produce_db[k].itemlv=atoi(split[1]);
+ skill_produce_db[k].req_skill=atoi(split[2]);
+
+ for(x=3,y=0; split[x] && split[x+1] && y<MAX_PRODUCE_RESOURCE; x+=2,y++){
+ skill_produce_db[k].mat_id[y]=atoi(split[x]);
+ skill_produce_db[k].mat_amount[y]=atoi(split[x+1]);
+ }
+ k++;
+ if(k >= MAX_SKILL_PRODUCE_DB)
+ break;
+ }
+ fclose(fp);
+ ShowStatus("Done reading '"CL_WHITE"%d"CL_RESET"' entries in '"CL_WHITE"%s"CL_RESET"'.\n",k,path);
+ }
+
+ memset(skill_arrow_db,0,sizeof(skill_arrow_db));
+
+ sprintf(path, "%s/create_arrow_db.txt", db_path);
+ fp=fopen(path,"r");
+ if(fp==NULL){
+ ShowError("can't read %s\n", path);
+ return 1;
+ }
+ k=0;
+ while(fgets(line,1020,fp)){
+ char *split[16];
+ int x,y;
+ if(line[0]=='/' && line[1]=='/')
+ continue;
+ memset(split,0,sizeof(split));
+ j = skill_split_str(line,split,13);
+ if(split[0]==0) //fixed by Lupus
+ continue;
+ i=atoi(split[0]);
+ if(i<=0)
+ continue;
+
+ skill_arrow_db[k].nameid=i;
+
+ for(x=1,y=0;split[x] && split[x+1] && y<5;x+=2,y++){
+ skill_arrow_db[k].cre_id[y]=atoi(split[x]);
+ skill_arrow_db[k].cre_amount[y]=atoi(split[x+1]);
+ }
+ k++;
+ if(k >= MAX_SKILL_ARROW_DB)
+ break;
+ }
+ fclose(fp);
+ ShowStatus("Done reading '"CL_WHITE"%d"CL_RESET"' entries in '"CL_WHITE"%s"CL_RESET"'.\n",k,path);
+
+ memset(skill_abra_db,0,sizeof(skill_abra_db));
+ sprintf(path, "%s/abra_db.txt", db_path);
+ fp=fopen(path,"r");
+ if(fp==NULL){
+ ShowError("can't read %s\n", path);
+ return 1;
+ }
+ k=0;
+ while(fgets(line,1020,fp)){
+ char *split[16];
+ if(line[0]=='/' && line[1]=='/')
+ continue;
+ memset(split,0,sizeof(split));
+ j = skill_split_str(line,split,13);
+ if(split[0]==0) //fixed by Lupus
+ continue;
+ i=atoi(split[0]);
+ if(i<=0)
+ continue;
+
+ skill_abra_db[i].req_lv=atoi(split[2]);
+ skill_abra_db[i].per=atoi(split[3]);
+
+ k++;
+ if(k >= MAX_SKILL_ABRA_DB)
+ break;
+ }
+ fclose(fp);
+ ShowStatus("Done reading '"CL_WHITE"%d"CL_RESET"' entries in '"CL_WHITE"%s"CL_RESET"'.\n",k,path);
+
+ sprintf(path, "%s/skill_castnodex_db.txt", db_path);
+ fp=fopen(path,"r");
+ if(fp==NULL){
+ ShowError("can't read %s\n", path);
+ return 1;
+ }
+ while(fgets(line,1020,fp)){
+ char *split[50];
+ if(line[0]=='/' && line[1]=='/')
+ continue;
+ memset(split,0,sizeof(split));
+ j = skill_split_str(line,split,4);
+ if(split[0]==0) //fixed by Lupus
+ continue;
+ i=atoi(split[0]);
+ if (i>=10000 && i<10015) // for guild skills [Celest]
+ i -= 9500;
+ else if(i<=0 || i>MAX_SKILL_DB)
+ continue;
+
+ skill_split_atoi(split[1],skill_db[i].castnodex);
+ if (!split[2])
+ continue;
+ skill_split_atoi(split[2],skill_db[i].delaynodex);
+ if(!split[3])
+ continue;
+ skill_split_atoi(split[3],skill_db[i].delaynowalk);
+ }
+ fclose(fp);
+ ShowStatus("Done reading '"CL_WHITE"%s"CL_RESET"'.\n",path);
+
+ sprintf(path, "%s/skill_nocast_db.txt", db_path);
+ fp=fopen(path,"r");
+ if(fp==NULL){
+ ShowError("can't read %s\n", path);
+ return 1;
+ }
+ k=0;
+ while(fgets(line,1020,fp)){
+ char *split[16];
+ if(line[0]=='/' && line[1]=='/')
+ continue;
+ memset(split,0,sizeof(split));
+ j = skill_split_str(line,split,2);
+ if(split[0]==0) //fixed by Lupus
+ continue;
+ i=atoi(split[0]);
+ if (i>=10000 && i<10015) // for guild skills [Celest]
+ i -= 9500;
+ else if(i<=0 || i>MAX_SKILL_DB)
+ continue;
+ skill_db[i].nocast=atoi(split[1]);
+ k++;
+ }
+ fclose(fp);
+ ShowStatus("Done reading '"CL_WHITE"%s"CL_RESET"'.\n",path);
+
+ return 0;
+}
+#endif
+
+/*===============================================
+ * For reading leveluseskillspamount.txt [Celest]
+ *-----------------------------------------------
+ */
+static int skill_read_skillspamount(void)
+{
+ char *buf,*p;
+ struct skill_db *skill = NULL;
+ int s, idx, new_flag=1, level=1, sp=0;
+
+ buf=(char *) grfio_reads("data\\leveluseskillspamount.txt",&s);
+
+ if(buf==NULL)
+ return -1;
+
+ buf[s]=0;
+ for(p=buf;p-buf<s;){
+ char buf2[64];
+
+ if (sscanf(p,"%[@]",buf2) == 1) {
+ level = 1;
+ new_flag = 1;
+ } else if (new_flag && sscanf(p,"%[^#]#",buf2) == 1) {
+ for (idx=0; skill_names[idx].id != 0; idx++) {
+ if (strstr(buf2, skill_names[idx].name) != NULL) {
+ skill = &skill_db[ skill_names[idx].id ];
+ new_flag = 0;
+ break;
+ }
+ }
+ } else if (!new_flag && sscanf(p,"%d#",&sp) == 1) {
+ skill->sp[level-1]=sp;
+ level++;
+ }
+
+ p=strchr(p,10);
+ if(!p) break;
+ p++;
+ }
+ aFree(buf);
+ ShowStatus("Done reading '"CL_WHITE"%s"CL_RESET"'.\n","data\\leveluseskillspamount.txt");
+
+ return 0;
+}
+
+void skill_reload(void)
+{
+ skill_readdb();
+ if (battle_config.skill_sp_override_grffile)
+ skill_read_skillspamount();
+}
+
+/*==========================================
+ * スキル?係?炎化??
+ *------------------------------------------
+ */
+int do_init_skill(void)
+{
+
+#ifndef TXT_ONLY
+ if(db_use_newsqldbs)
+ skill_read_sqldb();
+ else
+#endif /* TXT_ONLY */
+ skill_readdb();
+
+ if (battle_config.skill_sp_override_grffile)
+ skill_read_skillspamount();
+
+ add_timer_func_list(skill_unit_timer,"skill_unit_timer");
+ add_timer_func_list(skill_castend_id,"skill_castend_id");
+ add_timer_func_list(skill_castend_pos,"skill_castend_pos");
+ add_timer_func_list(skill_timerskill,"skill_timerskill");
+ add_timer_func_list(skill_castend_delay_sub,"skill_castend_delay_sub");
+
+ add_timer_interval(gettick()+SKILLUNITTIMER_INVERVAL,skill_unit_timer,0,0,SKILLUNITTIMER_INVERVAL);
+
+ return 0;
+}
diff --git a/src/map/skill.h b/src/map/skill.h
new file mode 100644
index 000000000..427533ed8
--- /dev/null
+++ b/src/map/skill.h
@@ -0,0 +1,879 @@
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#ifndef _SKILL_H_
+#define _SKILL_H_
+
+#include "map.h"
+
+#define MAX_SKILL_DB 1100
+#define MAX_SKILL_PRODUCE_DB 150
+#define MAX_PRODUCE_RESOURCE 12
+#define MAX_SKILL_ARROW_DB 150
+#define MAX_SKILL_ABRA_DB 350
+
+//Constants to identify the skill's inf value:
+#define INF_ATTACK_SKILL 1
+//For the time being, all trap-targetted skills ARE ground based:
+#define INF_GROUND_SKILL (2|32)
+// Skills casted on self where target is automatically chosen:
+#define INF_SELF_SKILL 4
+#define INF_SUPPORT_SKILL 16
+#define INF_TARGET_TRAP 32
+
+//Constants to identify a skill's nk value.
+//The NK value applies only to non INF_GROUND_SKILL skills.
+#define NK_NO_DAMAGE 1
+#define NK_SPLASH_DAMAGE 2
+
+//Constants to identify a skill's inf2 value.
+#define INF2_QUEST_SKILL 1
+//NPC skills are those that players can't have in their skill tree.
+#define INF2_NPC_SKILL 2
+#define INF2_WEDDING_SKILL 4
+#define INF2_SPIRIT_SKILL 8
+#define INF2_GUILD_SKILL 16
+#define INF2_SONG_DANCE 32
+#define INF2_ENSEMBLE_SKILL 64
+#define INF2_TRAP 128
+//Refers to ground placed skills that will target the caster as well (like Grandcross)
+#define INF2_TARGET_SELF 256
+#define INF2_NO_TARGET_SELF 512
+#define INF2_PARTY_ONLY 1024
+#define INF2_GUILD_ONLY 2048
+
+// スキルデ?タベ?ス
+struct skill_db {
+ char *name;
+ char *desc;
+ int range[MAX_SKILL_LEVEL],hit,inf,pl,nk,max;
+ int num[MAX_SKILL_LEVEL];
+ int cast[MAX_SKILL_LEVEL],delay[MAX_SKILL_LEVEL];
+ int upkeep_time[MAX_SKILL_LEVEL],upkeep_time2[MAX_SKILL_LEVEL];
+ int castcancel,cast_def_rate;
+ int inf2,maxcount,skill_type;
+ int blewcount[MAX_SKILL_LEVEL];
+ int hp[MAX_SKILL_LEVEL],sp[MAX_SKILL_LEVEL],mhp[MAX_SKILL_LEVEL],hp_rate[MAX_SKILL_LEVEL],sp_rate[MAX_SKILL_LEVEL],zeny[MAX_SKILL_LEVEL];
+ int weapon,state,spiritball[MAX_SKILL_LEVEL];
+ int itemid[10],amount[10];
+ int castnodex[MAX_SKILL_LEVEL];
+ int delaynodex[MAX_SKILL_LEVEL];
+ int delaynowalk[MAX_SKILL_LEVEL];
+ int nocast;
+ int unit_id[2];
+ int unit_layout_type[MAX_SKILL_LEVEL];
+ int unit_range;
+ int unit_interval;
+ int unit_target;
+ int unit_flag;
+};
+extern struct skill_db skill_db[MAX_SKILL_DB];
+
+struct skill_name_db {
+ int id; // skill id
+ char *name; // search strings
+ char *desc; // description that shows up for search's
+};
+
+#define MAX_SKILL_UNIT_LAYOUT 50
+#define MAX_SQUARE_LAYOUT 5 // 11*11のユニット配置が最大
+#define MAX_SKILL_UNIT_COUNT ((MAX_SQUARE_LAYOUT*2+1)*(MAX_SQUARE_LAYOUT*2+1))
+struct skill_unit_layout {
+ int count;
+ int dx[MAX_SKILL_UNIT_COUNT];
+ int dy[MAX_SKILL_UNIT_COUNT];
+};
+
+enum {
+ UF_DEFNOTENEMY = 0x0001, // defnotenemy 設定でBCT_NOENEMYに切り替え
+ UF_NOREITERATION = 0x0002, // 重複置き禁止
+ UF_NOFOOTSET = 0x0004, // 足元置き禁止
+ UF_NOOVERLAP = 0x0008, // ユニット効果が重複しない
+ UF_NOPC = 0x0010, //May not target players
+ UF_NOMOB = 0x0020, //May not target mobs
+ UF_SKILL = 0x0080, //May target skills
+ UF_DANCE = 0x0100, // ダンススキル
+ UF_ENSEMBLE = 0x0200, // 合奏スキル
+ UF_DUALMODE = 0x0800, //Spells should trigger both ontimer and onplace/onout/onleft effects.
+};
+
+// アイテム作成デ?タベ?ス
+struct skill_produce_db {
+ int nameid, trigger;
+ int req_skill,itemlv;
+ int mat_id[MAX_PRODUCE_RESOURCE],mat_amount[MAX_PRODUCE_RESOURCE];
+};
+extern struct skill_produce_db skill_produce_db[MAX_SKILL_PRODUCE_DB];
+
+// 矢作成デ?タベ?ス
+struct skill_arrow_db {
+ int nameid, trigger;
+ int cre_id[5],cre_amount[5];
+};
+extern struct skill_arrow_db skill_arrow_db[MAX_SKILL_ARROW_DB];
+
+// アブラカダブラデ?タベ?ス
+struct skill_abra_db {
+ int nameid;
+ int req_lv;
+ int per;
+};
+extern struct skill_abra_db skill_abra_db[MAX_SKILL_ABRA_DB];
+
+extern int enchant_eff[5];
+extern int deluge_eff[5];
+
+struct block_list;
+struct map_session_data;
+struct skill_unit;
+struct skill_unit_group;
+
+int do_init_skill(void);
+
+// スキルデ?タベ?スへのアクセサ
+int skill_get_hit( int id );
+int skill_get_inf( int id );
+int skill_get_pl( int id );
+int skill_get_nk( int id );
+int skill_get_max( int id );
+int skill_get_range( int id , int lv );
+int skill_get_range2(struct block_list *bl, int id, int lv);
+int skill_get_hp( int id ,int lv );
+int skill_get_mhp( int id ,int lv );
+int skill_get_sp( int id ,int lv );
+int skill_get_zeny( int id ,int lv );
+int skill_get_num( int id ,int lv );
+int skill_get_cast( int id ,int lv );
+int skill_get_delay( int id ,int lv );
+int skill_get_time( int id ,int lv );
+int skill_get_time2( int id ,int lv );
+int skill_get_castdef( int id );
+int skill_get_weapontype( int id );
+int skill_get_unit_id(int id,int flag);
+int skill_get_inf2( int id );
+int skill_get_maxcount( int id );
+int skill_get_blewcount( int id ,int lv );
+int skill_get_unit_flag( int id );
+int skill_get_unit_target( int id );
+int skill_tree_get_max( int id, int b_class ); // Celest
+const char* skill_get_name( int id ); // [Skotlex]
+
+// スキルの使用
+int skill_use_id( struct map_session_data *sd, int target_id,
+ int skill_num,int skill_lv);
+int skill_use_pos( struct map_session_data *sd,
+ int skill_x, int skill_y, int skill_num, int skill_lv);
+
+int skill_castend_map( struct map_session_data *sd,int skill_num, const char *map);
+
+int skill_cleartimerskill(struct block_list *src);
+int skill_addtimerskill(struct block_list *src,unsigned int tick,int target,int x,int y,int skill_id,int skill_lv,int type,int flag);
+
+// 追加?果
+int skill_additional_effect( struct block_list* src, struct block_list *bl,int skillid,int skilllv,int attack_type,unsigned int tick);
+int skill_counter_additional_effect( struct block_list* src, struct block_list *bl,int skillid,int skilllv,int attack_type,unsigned int tick);
+int skill_blown( struct block_list *src, struct block_list *target,int count);
+// ユニットスキル
+struct skill_unit_group *skill_unitsetting( struct block_list *src, int skillid,int skilllv,int x,int y,int flag);
+struct skill_unit *skill_initunit(struct skill_unit_group *group,int idx,int x,int y);
+int skill_delunit(struct skill_unit *unit);
+struct skill_unit_group *skill_initunitgroup(struct block_list *src,
+ int count,int skillid,int skilllv,int unit_id);
+int skill_delunitgroup(struct skill_unit_group *group);
+int skill_clear_unitgroup(struct block_list *src);
+
+int skill_unit_ondamaged(struct skill_unit *src,struct block_list *bl,
+ int damage,unsigned int tick);
+
+int skill_castfix( struct block_list *bl, int skill_id, int skill_lv, int time);
+int skill_delayfix( struct block_list *bl, int skill_id, int skill_lv, int time);
+int skill_check_unit_range(int m,int x,int y,int skillid, int skilllv);
+int skill_check_unit_range2(struct block_list *bl,int m,int x,int y,int skillid, int skilllv);
+// -- moonsoul (added skill_check_unit_cell)
+int skill_check_unit_cell(int skillid,int m,int x,int y,int unit_id);
+int skill_unit_out_all( struct block_list *bl,unsigned int tick,int range);
+int skill_unit_move(struct block_list *bl,unsigned int tick,int flag);
+int skill_unit_move_unit_group( struct skill_unit_group *group, int m,int dx,int dy);
+void skill_setmapcell(struct block_list *src, int skill_num, int skill_lv, int flag);
+
+struct skill_unit_group *skill_check_dancing( struct block_list *src );
+void skill_stop_dancing(struct block_list *src);
+
+// Guild skills [celest]
+int skill_guildaura_sub (struct block_list *bl,va_list ap);
+
+// 詠唱キャンセル
+int skill_castcancel(struct block_list *bl,int type);
+
+int skill_gangsterparadise(struct map_session_data *sd ,int type);
+int skill_rest(struct map_session_data *sd ,int type);
+void skill_brandishspear_first(struct square *tc,int dir,int x,int y);
+void skill_brandishspear_dir(struct square *tc,int dir,int are);
+void skill_repairweapon(struct map_session_data *sd, int idx);
+void skill_identify(struct map_session_data *sd,int idx);
+void skill_weaponrefine(struct map_session_data *sd,int idx); // [Celest]
+int skill_autospell(struct map_session_data *md,int skillid);
+
+#define skill_calc_heal(bl,skill_lv) (( status_get_lv(bl)+status_get_int(bl) )/8 *(4+ skill_lv*8))
+
+// その他
+int skill_check_cloaking(struct block_list *bl);
+
+// ステ?タス異常
+int skill_enchant_elemental_end(struct block_list *bl, int type);
+int skillnotok(int skillid, struct map_session_data *sd);
+
+// アイテム作成
+int skill_can_produce_mix( struct map_session_data *sd, int nameid, int trigger, int qty);
+int skill_produce_mix( struct map_session_data *sd,
+ int skill_id, int nameid, int slot1, int slot2, int slot3, int qty );
+
+int skill_arrow_create( struct map_session_data *sd,int nameid);
+
+// mobスキルのため
+int skill_castend_nodamage_id( struct block_list *src, struct block_list *bl,int skillid,int skilllv,unsigned int tick,int flag );
+int skill_castend_damage_id( struct block_list* src, struct block_list *bl,int skillid,int skilllv,unsigned int tick,int flag );
+int skill_castend_pos2( struct block_list *src, int x,int y,int skillid,int skilllv,unsigned int tick,int flag);
+
+// スキル攻?一括?理
+int skill_attack( int attack_type, struct block_list* src, struct block_list *dsrc,
+ struct block_list *bl,int skillid,int skilllv,unsigned int tick,int flag );
+
+void skill_reload(void);
+
+enum {
+ ST_NONE,ST_HIDING,ST_CLOAKING,ST_HIDDEN,ST_RIDING,ST_FALCON,ST_CART,ST_SHIELD,ST_SIGHT,ST_EXPLOSIONSPIRITS,ST_CARTBOOST,
+ ST_RECOV_WEIGHT_RATE,ST_MOVE_ENABLE,ST_WATER,
+};
+
+enum {
+ NV_BASIC = 1,
+
+ SM_SWORD,
+ SM_TWOHAND,
+ SM_RECOVERY,
+ SM_BASH,
+ SM_PROVOKE,
+ SM_MAGNUM,
+ SM_ENDURE,
+
+ MG_SRECOVERY,
+ MG_SIGHT,
+ MG_NAPALMBEAT,
+ MG_SAFETYWALL,
+ MG_SOULSTRIKE,
+ MG_COLDBOLT,
+ MG_FROSTDIVER,
+ MG_STONECURSE,
+ MG_FIREBALL,
+ MG_FIREWALL,
+ MG_FIREBOLT,
+ MG_LIGHTNINGBOLT,
+ MG_THUNDERSTORM,
+
+ AL_DP,
+ AL_DEMONBANE,
+ AL_RUWACH,
+ AL_PNEUMA,
+ AL_TELEPORT,
+ AL_WARP,
+ AL_HEAL,
+ AL_INCAGI,
+ AL_DECAGI,
+ AL_HOLYWATER,
+ AL_CRUCIS,
+ AL_ANGELUS,
+ AL_BLESSING,
+ AL_CURE,
+
+ MC_INCCARRY,
+ MC_DISCOUNT,
+ MC_OVERCHARGE,
+ MC_PUSHCART,
+ MC_IDENTIFY,
+ MC_VENDING,
+ MC_MAMMONITE,
+
+ AC_OWL,
+ AC_VULTURE,
+ AC_CONCENTRATION,
+ AC_DOUBLE,
+ AC_SHOWER,
+
+ TF_DOUBLE,
+ TF_MISS,
+ TF_STEAL,
+ TF_HIDING,
+ TF_POISON,
+ TF_DETOXIFY,
+
+ ALL_RESURRECTION,
+
+ KN_SPEARMASTERY,
+ KN_PIERCE,
+ KN_BRANDISHSPEAR,
+ KN_SPEARSTAB,
+ KN_SPEARBOOMERANG,
+ KN_TWOHANDQUICKEN,
+ KN_AUTOCOUNTER,
+ KN_BOWLINGBASH,
+ KN_RIDING,
+ KN_CAVALIERMASTERY,
+
+ PR_MACEMASTERY,
+ PR_IMPOSITIO,
+ PR_SUFFRAGIUM,
+ PR_ASPERSIO,
+ PR_BENEDICTIO,
+ PR_SANCTUARY,
+ PR_SLOWPOISON,
+ PR_STRECOVERY,
+ PR_KYRIE,
+ PR_MAGNIFICAT,
+ PR_GLORIA,
+ PR_LEXDIVINA,
+ PR_TURNUNDEAD,
+ PR_LEXAETERNA,
+ PR_MAGNUS,
+
+ WZ_FIREPILLAR,
+ WZ_SIGHTRASHER,
+ WZ_FIREIVY,
+ WZ_METEOR,
+ WZ_JUPITEL,
+ WZ_VERMILION,
+ WZ_WATERBALL,
+ WZ_ICEWALL,
+ WZ_FROSTNOVA,
+ WZ_STORMGUST,
+ WZ_EARTHSPIKE,
+ WZ_HEAVENDRIVE,
+ WZ_QUAGMIRE,
+ WZ_ESTIMATION,
+
+ BS_IRON,
+ BS_STEEL,
+ BS_ENCHANTEDSTONE,
+ BS_ORIDEOCON,
+ BS_DAGGER,
+ BS_SWORD,
+ BS_TWOHANDSWORD,
+ BS_AXE,
+ BS_MACE,
+ BS_KNUCKLE,
+ BS_SPEAR,
+ BS_HILTBINDING,
+ BS_FINDINGORE,
+ BS_WEAPONRESEARCH,
+ BS_REPAIRWEAPON,
+ BS_SKINTEMPER,
+ BS_HAMMERFALL,
+ BS_ADRENALINE,
+ BS_WEAPONPERFECT,
+ BS_OVERTHRUST,
+ BS_MAXIMIZE,
+
+ HT_SKIDTRAP,
+ HT_LANDMINE,
+ HT_ANKLESNARE,
+ HT_SHOCKWAVE,
+ HT_SANDMAN,
+ HT_FLASHER,
+ HT_FREEZINGTRAP,
+ HT_BLASTMINE,
+ HT_CLAYMORETRAP,
+ HT_REMOVETRAP,
+ HT_TALKIEBOX,
+ HT_BEASTBANE,
+ HT_FALCON,
+ HT_STEELCROW,
+ HT_BLITZBEAT,
+ HT_DETECTING,
+ HT_SPRINGTRAP,
+
+ AS_RIGHT,
+ AS_LEFT,
+ AS_KATAR,
+ AS_CLOAKING,
+ AS_SONICBLOW,
+ AS_GRIMTOOTH,
+ AS_ENCHANTPOISON,
+ AS_POISONREACT,
+ AS_VENOMDUST,
+ AS_SPLASHER,
+
+ NV_FIRSTAID,
+ NV_TRICKDEAD,
+ SM_MOVINGRECOVERY,
+ SM_FATALBLOW,
+ SM_AUTOBERSERK,
+ AC_MAKINGARROW,
+ AC_CHARGEARROW,
+ TF_SPRINKLESAND,
+ TF_BACKSLIDING,
+ TF_PICKSTONE,
+ TF_THROWSTONE,
+ MC_CARTREVOLUTION,
+ MC_CHANGECART,
+ MC_LOUD,
+ AL_HOLYLIGHT,
+ MG_ENERGYCOAT,
+
+ NPC_PIERCINGATT,
+ NPC_MENTALBREAKER,
+ NPC_RANGEATTACK,
+ NPC_ATTRICHANGE,
+ NPC_CHANGEWATER,
+ NPC_CHANGEGROUND,
+ NPC_CHANGEFIRE,
+ NPC_CHANGEWIND,
+ NPC_CHANGEPOISON,
+ NPC_CHANGEHOLY,
+ NPC_CHANGEDARKNESS,
+ NPC_CHANGETELEKINESIS,
+ NPC_CRITICALSLASH,
+ NPC_COMBOATTACK,
+ NPC_GUIDEDATTACK,
+ NPC_SELFDESTRUCTION,
+ NPC_SPLASHATTACK,
+ NPC_SUICIDE,
+ NPC_POISON,
+ NPC_BLINDATTACK,
+ NPC_SILENCEATTACK,
+ NPC_STUNATTACK,
+ NPC_PETRIFYATTACK,
+ NPC_CURSEATTACK,
+ NPC_SLEEPATTACK,
+ NPC_RANDOMATTACK,
+ NPC_WATERATTACK,
+ NPC_GROUNDATTACK,
+ NPC_FIREATTACK,
+ NPC_WINDATTACK,
+ NPC_POISONATTACK,
+ NPC_HOLYATTACK,
+ NPC_DARKNESSATTACK,
+ NPC_TELEKINESISATTACK,
+ NPC_MAGICALATTACK,
+ NPC_METAMORPHOSIS,
+ NPC_PROVOCATION,
+ NPC_SMOKING,
+ NPC_SUMMONSLAVE,
+ NPC_EMOTION,
+ NPC_TRANSFORMATION,
+ NPC_BLOODDRAIN,
+ NPC_ENERGYDRAIN,
+ NPC_KEEPING,
+ NPC_DARKBREATH,
+ NPC_DARKBLESSING,
+ NPC_BARRIER,
+ NPC_DEFENDER,
+ NPC_LICK,
+ NPC_HALLUCINATION,
+ NPC_REBIRTH,
+ NPC_SUMMONMONSTER,
+
+ RG_SNATCHER,
+ RG_STEALCOIN,
+ RG_BACKSTAP,
+ RG_TUNNELDRIVE,
+ RG_RAID,
+ RG_STRIPWEAPON,
+ RG_STRIPSHIELD,
+ RG_STRIPARMOR,
+ RG_STRIPHELM,
+ RG_INTIMIDATE,
+ RG_GRAFFITI,
+ RG_FLAGGRAFFITI,
+ RG_CLEANER,
+ RG_GANGSTER,
+ RG_COMPULSION,
+ RG_PLAGIARISM,
+
+ AM_AXEMASTERY,
+ AM_LEARNINGPOTION,
+ AM_PHARMACY,
+ AM_DEMONSTRATION,
+ AM_ACIDTERROR,
+ AM_POTIONPITCHER,
+ AM_CANNIBALIZE,
+ AM_SPHEREMINE,
+ AM_CP_WEAPON,
+ AM_CP_SHIELD,
+ AM_CP_ARMOR,
+ AM_CP_HELM,
+ AM_BIOETHICS,
+ AM_BIOTECHNOLOGY,
+ AM_CREATECREATURE,
+ AM_CULTIVATION,
+ AM_FLAMECONTROL,
+ AM_CALLHOMUN,
+ AM_REST,
+ AM_DRILLMASTER,
+ AM_HEALHOMUN,
+ AM_RESURRECTHOMUN,
+
+ CR_TRUST,
+ CR_AUTOGUARD,
+ CR_SHIELDCHARGE,
+ CR_SHIELDBOOMERANG,
+ CR_REFLECTSHIELD,
+ CR_HOLYCROSS,
+ CR_GRANDCROSS,
+ CR_DEVOTION,
+ CR_PROVIDENCE,
+ CR_DEFENDER,
+ CR_SPEARQUICKEN,
+
+ MO_IRONHAND,
+ MO_SPIRITSRECOVERY,
+ MO_CALLSPIRITS,
+ MO_ABSORBSPIRITS,
+ MO_TRIPLEATTACK,
+ MO_BODYRELOCATION,
+ MO_DODGE,
+ MO_INVESTIGATE,
+ MO_FINGEROFFENSIVE,
+ MO_STEELBODY,
+ MO_BLADESTOP,
+ MO_EXPLOSIONSPIRITS,
+ MO_EXTREMITYFIST,
+ MO_CHAINCOMBO,
+ MO_COMBOFINISH,
+
+ SA_ADVANCEDBOOK,
+ SA_CASTCANCEL,
+ SA_MAGICROD,
+ SA_SPELLBREAKER,
+ SA_FREECAST,
+ SA_AUTOSPELL,
+ SA_FLAMELAUNCHER,
+ SA_FROSTWEAPON,
+ SA_LIGHTNINGLOADER,
+ SA_SEISMICWEAPON,
+ SA_DRAGONOLOGY,
+ SA_VOLCANO,
+ SA_DELUGE,
+ SA_VIOLENTGALE,
+ SA_LANDPROTECTOR,
+ SA_DISPELL,
+ SA_ABRACADABRA,
+ SA_MONOCELL,
+ SA_CLASSCHANGE,
+ SA_SUMMONMONSTER,
+ SA_REVERSEORCISH,
+ SA_DEATH,
+ SA_FORTUNE,
+ SA_TAMINGMONSTER,
+ SA_QUESTION,
+ SA_GRAVITY,
+ SA_LEVELUP,
+ SA_INSTANTDEATH,
+ SA_FULLRECOVERY,
+ SA_COMA,
+
+ BD_ADAPTATION,
+ BD_ENCORE,
+ BD_LULLABY,
+ BD_RICHMANKIM,
+ BD_ETERNALCHAOS,
+ BD_DRUMBATTLEFIELD,
+ BD_RINGNIBELUNGEN,
+ BD_ROKISWEIL,
+ BD_INTOABYSS,
+ BD_SIEGFRIED,
+ BD_RAGNAROK,
+
+ BA_MUSICALLESSON,
+ BA_MUSICALSTRIKE,
+ BA_DISSONANCE,
+ BA_FROSTJOKE,
+ BA_WHISTLE,
+ BA_ASSASSINCROSS,
+ BA_POEMBRAGI,
+ BA_APPLEIDUN,
+
+ DC_DANCINGLESSON,
+ DC_THROWARROW,
+ DC_UGLYDANCE,
+ DC_SCREAM,
+ DC_HUMMING,
+ DC_DONTFORGETME,
+ DC_FORTUNEKISS,
+ DC_SERVICEFORYOU,
+
+ NPC_RANDOMMOVE,
+ NPC_SPEEDUP,
+ NPC_REVENGE,
+
+ WE_MALE,
+ WE_FEMALE,
+ WE_CALLPARTNER,
+
+ ITM_TOMAHAWK,
+
+ NPC_DARKCROSS,
+ NPC_GRANDDARKNESS,
+ NPC_DARKSTRIKE,
+ NPC_DARKTHUNDER,
+ NPC_STOP,
+ NPC_BREAKWEAPON,
+ NPC_BREAKARMOR,
+ NPC_BREAKHELM,
+ NPC_BREAKSHIELD,
+ NPC_UNDEADATTACK,
+ NPC_CHANGEUNDEAD,
+ NPC_POWERUP,
+ NPC_AGIUP,
+ NPC_SIEGEMODE,
+ NPC_CALLSLAVE,
+ NPC_INVISIBLE,
+ NPC_RUN,
+
+ LK_AURABLADE,
+ LK_PARRYING,
+ LK_CONCENTRATION,
+ LK_TENSIONRELAX,
+ LK_BERSERK,
+ LK_FURY,
+ HP_ASSUMPTIO,
+ HP_BASILICA,
+ HP_MEDITATIO,
+ HW_SOULDRAIN,
+ HW_MAGICCRASHER,
+ HW_MAGICPOWER,
+ PA_PRESSURE,
+ PA_SACRIFICE,
+ PA_GOSPEL,
+ CH_PALMSTRIKE,
+ CH_TIGERFIST,
+ CH_CHAINCRUSH,
+ PF_HPCONVERSION,
+ PF_SOULCHANGE,
+ PF_SOULBURN,
+ ASC_KATAR,
+ ASC_HALLUCINATION,
+ ASC_EDP,
+ ASC_BREAKER,
+ SN_SIGHT,
+ SN_FALCONASSAULT,
+ SN_SHARPSHOOTING,
+ SN_WINDWALK,
+ WS_MELTDOWN,
+ WS_CREATECOIN,
+ WS_CREATENUGGET,
+ WS_CARTBOOST,
+ WS_SYSTEMCREATE,
+ ST_CHASEWALK,
+ ST_REJECTSWORD,
+ ST_STEALBACKPACK,
+ CR_ALCHEMY,
+ CR_SYNTHESISPOTION,
+ CG_ARROWVULCAN,
+ CG_MOONLIT,
+ CG_MARIONETTE,
+ LK_SPIRALPIERCE,
+ LK_HEADCRUSH,
+ LK_JOINTBEAT,
+ HW_NAPALMVULCAN,
+ CH_SOULCOLLECT,
+ PF_MINDBREAKER,
+ PF_MEMORIZE,
+ PF_FOGWALL,
+ PF_SPIDERWEB,
+ ASC_METEORASSAULT,
+ ASC_CDP,
+ WE_BABY,
+ WE_CALLPARENT,
+ WE_CALLBABY,
+
+ TK_RUN,
+ TK_READYSTORM,
+ TK_STORMKICK,
+ TK_READYDOWN,
+ TK_DOWNKICK,
+ TK_READYTURN,
+ TK_TURNKICK,
+ TK_READYCOUNTER,
+ TK_COUNTER,
+ TK_DODGE,
+ TK_JUMPKICK,
+ TK_HPTIME,
+ TK_SPTIME,
+ TK_POWER,
+ TK_SEVENWIND,
+ TK_HIGHJUMP,
+ SG_FEEL,
+ SG_SUN_WARM,
+ SG_MOON_WARM,
+ SG_STAR_WARM,
+ SG_SUN_COMFORT,
+ SG_MOON_COMFORT,
+ SG_STAR_COMFORT,
+ SG_HATE,
+ SG_SUN_ANGER,
+ SG_MOON_ANGER,
+ SG_STAR_ANGER,
+ SG_SUN_BLESS,
+ SG_MOON_BLESS,
+ SG_STAR_BLESS,
+ SG_DEVIL,
+ SG_FRIEND,
+ SG_KNOWLEDGE,
+ SG_FUSION,
+ SL_ALCHEMIST,
+ AM_BERSERKPITCHER,
+ SL_MONK,
+ SL_STAR,
+ SL_SAGE,
+ SL_CRUSADER,
+ SL_SUPERNOVICE,
+ SL_KNIGHT,
+ SL_WIZARD,
+ SL_PRIEST,
+ SL_BARDDANCER,
+ SL_ROGUE,
+ SL_ASSASIN,
+ SL_BLACKSMITH,
+ BS_ADRENALINE2,
+ SL_HUNTER,
+ SL_SOULLINKER,
+ SL_KAIZEL,
+ SL_KAAHI,
+ SL_KAUPE,
+ SL_KAITE,
+ SL_KAINA,
+ SL_STIN,
+ SL_STUN,
+ SL_SMA,
+ SL_SWOO,
+ SL_SKE,
+ SL_SKA,
+
+ SM_SELFPROVOKE,
+ NPC_EMOTION_ON,
+ ST_PRESERVE,
+ ST_FULLSTRIP,
+ WS_WEAPONREFINE,
+ CR_SLIMPITCHER,
+ CR_FULLPROTECTION,
+ PA_SHIELDCHAIN,
+ HP_MANARECHARGE,
+ PF_DOUBLECASTING,
+ HW_GANBANTEIN,
+ HW_GRAVITATION,
+ WS_CARTTERMINATION,
+ WS_OVERTHRUSTMAX,
+ CG_LONGINGFREEDOM,
+ CG_HERMODE,
+ CG_TAROTCARD,
+ CR_ACIDDEMONSTRATION,
+ CR_CULTIVATION,
+ //492,missing?
+ TK_MISSION = 493,
+ SL_HIGH,
+ KN_ONEHAND,
+ AM_TWILIGHT1,
+ AM_TWILIGHT2,
+ AM_TWILIGHT3,
+ HT_POWER,
+
+ KN_CHARGEATK = 1001,
+ CR_SHRINK,
+ AS_SONICACCEL,
+ AS_VENOMKNIFE,
+ RG_CLOSECONFINE,
+ WZ_SIGHTBLASTER,
+ SA_CREATECON,
+ SA_ELEMENTWATER,
+ HT_PHANTASMIC,
+ BA_PANGVOICE,
+ DC_WINKCHARM,
+ BS_UNFAIRLYTRICK,
+ BS_GREED,
+ PR_REDEMPTIO,
+ MO_KITRANSLATION,
+ MO_BALKYOUNG,
+ SA_ELEMENTGROUND,
+ SA_ELEMENTFIRE,
+ SA_ELEMENTWIND,
+
+ HLIF_HEAL = 8001,
+ HLIF_AVOID,
+ HLIF_BRAIN,
+ HLIF_CHANGE,
+ HAMI_CASTLE,
+ HAMI_DEFENCE,
+ HAMI_SKIN,
+ HAMI_BLOODLUST,
+ HFLI_MOON,
+ HFLI_FLEET,
+ HFLI_SPEED,
+ HFLI_SBR44,
+ HVAN_CAPRICE,
+ HVAN_CHAOTIC,
+ HVAN_INSTRUCT,
+ HVAN_EXPLOSION,
+};
+
+enum {
+ UNT_SAFETYWALL = 0x7e,
+ UNT_FIREWALL,
+ UNT_WARP_WAITING,
+ UNT_WARP_ACTIVE,
+
+ UNT_SANCTUARY = 0x83,
+ UNT_MAGNUS,
+ UNT_PNEUMA,
+ UNT_MAGIC_SKILLS,
+ UNT_FIREPILLAR_WAITING,
+ UNT_FIREPILLAR_ACTIVE,
+
+ UNT_USED_TRAPS = 0x8c,
+ UNT_ICEWALL,
+ UNT_QUAGMIRE,
+ UNT_BLASTMINE,
+ UNT_SKIDTRAP,
+ UNT_ANKLESNARE,
+ UNT_VENOMDUST,
+ UNT_LANDMINE,
+ UNT_SHOCKWAVE,
+ UNT_SANDMAN,
+ UNT_FLASHER,
+ UNT_FREEZINGTRAP,
+ UNT_CLAYMORETRAP,
+ UNT_TALKIEBOX,
+ UNT_VOLCANO,
+ UNT_DELUGE,
+ UNT_VIOLENTGALE,
+ UNT_LANDPROTECTOR,
+ UNT_LULLABY,
+ UNT_RICHMANKIM,
+ UNT_ETERNALCHAOS,
+ UNT_DRUMBATTLEFIELD,
+ UNT_RINGNIBELUNGEN,
+ UNT_ROKISWEIL,
+ UNT_INTOABYSS,
+ UNT_SIEGFRIED,
+ UNT_DISSONANCE,
+ UNT_WHISTLE,
+ UNT_ASSASSINCROSS,
+ UNT_POEMBRAGI,
+ UNT_APPLEIDUN,
+ UNT_UGLYDANCE,
+ UNT_HUMMING,
+ UNT_DONTFORGETME,
+ UNT_FORTUNEKISS,
+ UNT_SERVICEFORYOU,
+ UNT_GRAFFITI,
+ UNT_DEMONSTRATION,
+ UNT_CALLPARTNER,
+ UNT_GOSPEL,
+ UNT_BASILICA,
+
+ UNT_FOGWALL = 0xb6,
+ UNT_SPIDERWEB,
+ UNT_GRAVITATION,
+ UNT_HERMODE,
+};
+
+#endif
diff --git a/src/map/status.c b/src/map/status.c
new file mode 100644
index 000000000..28896aceb
--- /dev/null
+++ b/src/map/status.c
@@ -0,0 +1,6038 @@
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#include <time.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <memory.h>
+#include <string.h>
+
+#include "pc.h"
+#include "map.h"
+#include "pet.h"
+#include "mob.h"
+#include "clif.h"
+#include "guild.h"
+#include "skill.h"
+#include "itemdb.h"
+#include "battle.h"
+#include "chrif.h"
+#include "status.h"
+
+#include "timer.h"
+#include "nullpo.h"
+#include "script.h"
+#include "showmsg.h"
+
+int SkillStatusChangeTable[]={
+ -1,-1,-1,-1,-1,-1,
+ SC_PROVOKE,
+ SC_WATK_ELEMENT, //Adds part of your final attack as elemental damage. [Skotlex]
+ SC_ENDURE,
+ -1,
+/* 10- */
+ SC_SIGHT, /* サイト */
+ -1,
+ SC_SAFETYWALL, /* セーフティーウォール */
+ -1,-1,-1,
+ SC_FREEZE, /* フロストダイバ? */
+ SC_STONE, /* スト?ンカ?ス */
+ -1,-1,
+/* 20- */
+ -1,-1,-1,-1,
+ SC_RUWACH, /* ルアフ */
+ -1,//SC_PNEUMA, Pneuma is no longer a status change. It is a cell type.
+ -1,-1,-1,
+ SC_INCREASEAGI, /* 速度?加 */
+/* 30- */
+ SC_DECREASEAGI, /* 速度減少 */
+ -1,
+ SC_SIGNUMCRUCIS, /* シグナムクルシス */
+ SC_ANGELUS, /* エンジェラス */
+ SC_BLESSING, /* ブレッシング */
+ -1,-1,-1,-1,-1,
+/* 40- */
+ -1,-1,-1,-1,-1,
+ SC_CONCENTRATE, /* 集中力向上 */
+ -1,-1,-1,-1,
+/* 50- */
+ -1,
+ SC_HIDING, /* ハイディング */
+ -1,-1,-1,-1,-1,-1,-1,-1,
+/* 60- */
+ SC_TWOHANDQUICKEN, /* 2HQ */
+ SC_AUTOCOUNTER,
+ -1,-1,-1,-1,
+ SC_IMPOSITIO, /* インポシティオマヌス */
+ SC_SUFFRAGIUM, /* サフラギウム */
+ SC_ASPERSIO, /* アスペルシオ */
+ SC_BENEDICTIO, /* 聖?降福 */
+/* 70- */
+ -1,
+ SC_SLOWPOISON,
+ -1,
+ SC_KYRIE, /* キリエエレイソン */
+ SC_MAGNIFICAT, /* マグニフィカ?ト */
+ SC_GLORIA, /* グロリア */
+ SC_SILENCE, /* レックスディビ?ナ */
+ -1,
+ SC_AETERNA, /* レックスエ?テルナ */
+ -1,
+/* 80- */
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+/* 90- */
+ -1,-1,
+ SC_QUAGMIRE, /* クァグマイア */
+ -1,-1,-1,-1,-1,-1,-1,
+/* 100- */
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+/* 110- */
+ -1,
+ SC_ADRENALINE, /* アドレナリンラッシュ */
+ SC_WEAPONPERFECTION,/* ウェポンパ?フェクション */
+ SC_OVERTHRUST, /* オ?バ?トラスト */
+ SC_MAXIMIZEPOWER, /* マキシマイズパワ? */
+ -1,-1,-1,-1,-1,
+/* 120- */
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+/* 130- */
+ -1,-1,-1,-1,-1,
+ SC_CLOAKING, /* クロ?キング */
+ SC_STAN, /* ソニックブロ? */
+ -1,
+ SC_ENCPOISON, /* エンチャントポイズン */
+ SC_POISONREACT, /* ポイズンリアクト */
+/* 140- */
+ SC_POISON, /* ベノムダスト */
+ SC_SPLASHER, /* ベナムスプラッシャ? */
+ -1,
+ SC_TRICKDEAD, /* 死んだふり */
+ -1,-1,
+ SC_AUTOBERSERK,
+ -1,-1,-1,
+/* 150- */
+ -1,-1,-1,-1,-1,
+ SC_LOUD, /* ラウドボイス */
+ -1,
+ SC_ENERGYCOAT, /* エナジ?コ?ト */
+ -1,-1,
+/* 160- */
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+/* 200 */
+ -1,
+ SC_KEEPING,
+ -1,
+ SC_COMA,
+ SC_BARRIER,
+ -1,
+ SC_STAN,
+ SC_HALLUCINATION,
+ -1,-1,
+/* 210- */
+ -1,-1,-1,-1,-1,
+ SC_STRIPWEAPON,
+ SC_STRIPSHIELD,
+ SC_STRIPARMOR,
+ SC_STRIPHELM,
+ -1,
+/* 220- */
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+/* 230- */
+ -1,-1,-1,-1,
+ SC_CP_WEAPON,
+ SC_CP_SHIELD,
+ SC_CP_ARMOR,
+ SC_CP_HELM,
+ -1,-1,
+/* 240- */
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,
+ SC_AUTOGUARD,
+/* 250- */
+ -1,-1,
+ SC_REFLECTSHIELD,
+ -1,-1,
+ SC_DEVOTION,
+ SC_PROVIDENCE,
+ SC_DEFENDER,
+ SC_SPEARSQUICKEN,
+ -1,
+/* 260- */
+ -1,-1,-1,-1,-1,-1,-1,-1,
+ SC_STEELBODY,
+ SC_BLADESTOP_WAIT,
+/* 270- */
+ SC_EXPLOSIONSPIRITS,
+ SC_EXTREMITYFIST,
+ -1,-1,-1,-1,
+ SC_MAGICROD,
+ -1,-1,-1,
+/* 280- */
+ SC_FIREWEAPON,
+ SC_WATERWEAPON,
+ SC_WINDWEAPON,
+ SC_EARTHWEAPON,
+ -1,
+ SC_VOLCANO,
+ SC_DELUGE,
+ SC_VIOLENTGALE,
+ SC_LANDPROTECTOR,
+ -1,
+/* 290- */
+ -1,-1,-1,-1,
+ SC_ORCISH,
+ -1,-1,-1,-1,-1,
+/* 300- */
+ -1,-1,-1,
+ SC_COMA,
+ -1,-1,
+ SC_LULLABY,
+ SC_RICHMANKIM,
+ SC_ETERNALCHAOS,
+ SC_DRUMBATTLE,
+/* 310- */
+ SC_NIBELUNGEN,
+ SC_ROKISWEIL,
+ SC_INTOABYSS,
+ SC_SIEGFRIED,
+ -1,-1,-1,-1,-1,
+ SC_WHISTLE,
+/* 320- */
+ SC_ASSNCROS,
+ SC_POEMBRAGI,
+ SC_APPLEIDUN,
+ -1,-1,
+ SC_UGLYDANCE,
+ -1,
+ SC_HUMMING,
+ SC_DONTFORGETME,
+ SC_FORTUNE,
+/* 330- */
+ SC_SERVICE4U,
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,
+/* 340- */
+ -1,-1,
+ SC_STOP,
+ -1,-1,-1,-1,-1,-1,-1,
+/* 350- */
+ -1,-1,-1,-1,-1,
+ SC_AURABLADE,
+ SC_PARRYING,
+ SC_CONCENTRATION,
+ SC_TENSIONRELAX,
+ SC_BERSERK,
+/* 360- */
+ -1,
+ SC_ASSUMPTIO,
+ SC_BASILICA,
+ -1,-1,-1,
+ SC_MAGICPOWER,
+ -1,
+ SC_SACRIFICE,
+ SC_GOSPEL,
+/* 370- */
+ -1,-1,-1,-1,-1,-1,-1,-1,
+ SC_EDP,
+ -1,
+/* 380- */
+ SC_TRUESIGHT,
+ -1,-1,
+ SC_WINDWALK,
+ SC_MELTDOWN,
+ -1,-1,
+ SC_CARTBOOST,
+ -1,
+ SC_CHASEWALK,
+/* 390- */
+ SC_REJECTSWORD,
+ -1,-1,-1,-1,
+ SC_MOONLIT,
+ SC_MARIONETTE,
+ -1,
+ SC_BLEEDING,
+ SC_JOINTBEAT,
+/* 400 */
+ -1,-1,
+ SC_MINDBREAKER,
+ SC_MEMORIZE,
+ SC_FOGWALL,
+ SC_SPIDERWEB,
+ -1,-1,
+ SC_BABY,
+ -1,
+/* 410- */
+ -1,
+ SC_RUN,
+ SC_READYSTORM,
+ -1,
+ SC_READYDOWN,
+ -1,
+ SC_READYTURN,
+ -1,
+ SC_READYCOUNTER,
+ -1,
+/* 420- */
+ SC_DODGE,
+ -1,-1,
+ SC_TKDORI,
+ -1,-1,-1,-1,
+ SC_WARM,
+ SC_WARM,
+/* 430- */
+ SC_WARM,
+ SC_SUN_COMFORT,
+ SC_MOON_COMFORT,
+ SC_STAR_COMFORT,
+ -1,-1,-1,-1,-1,-1,
+/* 440- */
+ -1,-1,-1,-1,
+ SC_FUSION,
+ MAPID_ALCHEMIST, // Storing the target job rather than simply SC_SPIRIT simplifies code later on.
+ -1,
+ MAPID_MONK,
+ MAPID_STAR_GLADIATOR,
+ MAPID_SAGE,
+/* 450- */
+ MAPID_CRUSADER,
+ MAPID_SUPER_NOVICE,
+ MAPID_KNIGHT,
+ MAPID_WIZARD,
+ MAPID_PRIEST,
+ MAPID_BARDDANCER,
+ MAPID_ROGUE,
+ MAPID_ASSASSIN,
+ MAPID_BLACKSMITH,
+ SC_ADRENALINE2,
+/* 460- */
+ MAPID_HUNTER,
+ MAPID_SOUL_LINKER,
+ SC_KAIZEL,
+ SC_KAAHI,
+ SC_KAUPE,
+ SC_KAITE,
+ -1,-1,-1,-1,
+/* 470- */
+ SC_SWOO, // [marquis007]
+ SC_SKE,
+ SC_SKA, // [marquis007]
+ -1,-1,
+ SC_PRESERVE,
+ -1,-1,-1,-1,
+/* 480- */
+ -1,-1,
+ SC_DOUBLECAST,
+ -1,
+ SC_GRAVITATION,
+ -1,
+ SC_MAXOVERTHRUST,
+ SC_LONGING,
+ SC_HERMODE,
+ -1,
+/* 490- */
+ -1,-1,-1,-1,
+ SC_SPIRIT,
+ SC_ONEHAND,
+ -1,-1,-1,-1,
+/* 500- */
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+/* 510- */
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+/* 520- */
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+/* 530- */
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+/* 540- */
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+/* 550- */
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+/* 560- */
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+/* 570- */
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+/* 580- */
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+/* 590- */
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+/* 600- */
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+/* 610- */
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+/* 620- */
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+/* 630- */
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+/* 640- */
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+/* 650- */
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+/* 660- */
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+/* 670- */
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+/* 680- */
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+/* 690- */
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+/* 700- */
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+/* 710- */
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+/* 720- */
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+/* 730- */
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+/* 740- */
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+/* 750- */
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+/* 760- */
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+/* 770- */
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+/* 780- */
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+/* 790- */
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+/* 800- */
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+/* 810- */
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+/* 820- */
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+/* 830- */
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+/* 840- */
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+/* 850- */
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+/* 860- */
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+/* 870- */
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+/* 880- */
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+/* 890- */
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+/* 900- */
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+/* 910- */
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+/* 920- */
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+/* 930- */
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+/* 940- */
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+/* 950- */
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+/* 960- */
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+/* 970- */
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+/* 980- */
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+/* 990- */
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+/* 1000- */
+ -1,-1,
+ SC_SHRINK,
+ -1,-1,
+ SC_CLOSECONFINE2,
+ SC_SIGHTBLASTER,
+ -1,-1,-1,
+/* 1010- */
+ -1,
+ SC_WINKCHARM,
+ -1,-1,
+ SC_COMA,
+ -1,-1,-1,-1,-1,
+/* 1020- */
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+/* 1030- */
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+/* 1040- */
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+/* 1050- */
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+/* 1060- */
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+/* 1070- */
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+/* 1080- */
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+/* 1090- */
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+};
+
+int StatusIconChangeTable[SC_MAX];
+
+static int max_weight_base[MAX_PC_CLASS];
+static int hp_coefficient[MAX_PC_CLASS];
+static int hp_coefficient2[MAX_PC_CLASS];
+static int hp_sigma_val[MAX_PC_CLASS][MAX_LEVEL];
+static int sp_coefficient[MAX_PC_CLASS];
+static int aspd_base[MAX_PC_CLASS][20];
+#define MAX_REFINE_BONUS 5
+static int refinebonus[MAX_REFINE_BONUS][3]; // 精錬ボーナステーブル(refine_db.txt)
+int percentrefinery[5][MAX_REFINE+1]; // 精錬成功率(refine_db.txt)
+static int atkmods[3][20]; // 武器ATKサイズ修正(size_fix.txt)
+static char job_bonus[MAX_PC_CLASS][MAX_LEVEL];
+
+int current_equip_item_index; //Contains inventory index of an equipped item. To pass it into the EQUP_SCRIPT [Lupus]
+//we need it for new cards 15 Feb 2005, to check if the combo cards are insrerted into the CURRENT weapon only
+//to avoid cards exploits
+
+//Initializes the StatusIconChangeTable variable. May seem somewhat slower than directly defining the array,
+//but it is much less prone to errors. [Skotlex]
+void initStatusIconChangeTable(void) {
+ int i;
+ for (i = 0; i < SC_MAX; i++)
+ StatusIconChangeTable[i] = SI_BLANK;
+
+ StatusIconChangeTable[SC_PROVOKE] = SI_PROVOKE;
+ StatusIconChangeTable[SC_ENDURE] = SI_ENDURE;
+ StatusIconChangeTable[SC_TWOHANDQUICKEN] = SI_TWOHANDQUICKEN;
+ StatusIconChangeTable[SC_CONCENTRATE] = SI_CONCENTRATE;
+ StatusIconChangeTable[SC_HIDING] = SI_HIDING;
+ StatusIconChangeTable[SC_CLOAKING] = SI_CLOAKING;
+ StatusIconChangeTable[SC_ENCPOISON] = SI_ENCPOISON;
+ StatusIconChangeTable[SC_POISONREACT] = SI_POISONREACT;
+ StatusIconChangeTable[SC_QUAGMIRE] = SI_QUAGMIRE;
+ StatusIconChangeTable[SC_ANGELUS] = SI_ANGELUS;
+ StatusIconChangeTable[SC_BLESSING] = SI_BLESSING;
+ StatusIconChangeTable[SC_SIGNUMCRUCIS] = SI_SIGNUMCRUCIS;
+ StatusIconChangeTable[SC_INCREASEAGI] = SI_INCREASEAGI;
+ StatusIconChangeTable[SC_DECREASEAGI] = SI_DECREASEAGI;
+ StatusIconChangeTable[SC_SLOWPOISON] = SI_SLOWPOISON;
+ StatusIconChangeTable[SC_IMPOSITIO] = SI_IMPOSITIO;
+ StatusIconChangeTable[SC_SUFFRAGIUM] = SI_SUFFRAGIUM;
+ StatusIconChangeTable[SC_ASPERSIO] = SI_ASPERSIO;
+ StatusIconChangeTable[SC_BENEDICTIO] = SI_BENEDICTIO;
+ StatusIconChangeTable[SC_KYRIE] = SI_KYRIE;
+ StatusIconChangeTable[SC_MAGNIFICAT] = SI_MAGNIFICAT;
+ StatusIconChangeTable[SC_GLORIA] = SI_GLORIA;
+ StatusIconChangeTable[SC_AETERNA] = SI_AETERNA;
+ StatusIconChangeTable[SC_ADRENALINE] = SI_ADRENALINE;
+ StatusIconChangeTable[SC_WEAPONPERFECTION] = SI_WEAPONPERFECTION;
+ StatusIconChangeTable[SC_OVERTHRUST] = SI_OVERTHRUST;
+ StatusIconChangeTable[SC_MAXIMIZEPOWER] = SI_MAXIMIZEPOWER;
+ StatusIconChangeTable[SC_TRICKDEAD] = SI_TRICKDEAD;
+ StatusIconChangeTable[SC_LOUD] = SI_LOUD;
+ StatusIconChangeTable[SC_ENERGYCOAT] = SI_ENERGYCOAT;
+ StatusIconChangeTable[SC_BROKENARMOR] = SI_BROKENARMOR;
+ StatusIconChangeTable[SC_BROKENWEAPON] = SI_BROKENWEAPON;
+ StatusIconChangeTable[SC_HALLUCINATION] = SI_HALLUCINATION;
+ StatusIconChangeTable[SC_WEIGHT50 ] = SI_WEIGHT50;
+ StatusIconChangeTable[SC_WEIGHT90] = SI_WEIGHT90;
+ StatusIconChangeTable[SC_ASPDPOTION0] = SI_ASPDPOTION;
+ StatusIconChangeTable[SC_ASPDPOTION1] = SI_ASPDPOTION;
+ StatusIconChangeTable[SC_ASPDPOTION2] = SI_ASPDPOTION;
+ StatusIconChangeTable[SC_ASPDPOTION3] = SI_ASPDPOTION;
+ StatusIconChangeTable[SC_SPEEDUP0] = SI_SPEEDPOTION;
+ StatusIconChangeTable[SC_SPEEDUP1] = SI_SPEEDPOTION;
+ StatusIconChangeTable[SC_STRIPWEAPON] = SI_STRIPWEAPON;
+ StatusIconChangeTable[SC_STRIPSHIELD] = SI_STRIPSHIELD;
+ StatusIconChangeTable[SC_STRIPARMOR] = SI_STRIPARMOR;
+ StatusIconChangeTable[SC_STRIPHELM] = SI_STRIPHELM;
+ StatusIconChangeTable[SC_CP_WEAPON] = SI_CP_WEAPON;
+ StatusIconChangeTable[SC_CP_SHIELD] = SI_CP_SHIELD;
+ StatusIconChangeTable[SC_CP_ARMOR] = SI_CP_ARMOR;
+ StatusIconChangeTable[SC_CP_HELM] = SI_CP_HELM;
+ StatusIconChangeTable[SC_AUTOGUARD] = SI_AUTOGUARD;
+ StatusIconChangeTable[SC_REFLECTSHIELD] = SI_REFLECTSHIELD;
+ StatusIconChangeTable[SC_PROVIDENCE] = SI_PROVIDENCE;
+ StatusIconChangeTable[SC_DEFENDER] = SI_DEFENDER;
+ StatusIconChangeTable[SC_AUTOSPELL] = SI_AUTOSPELL;
+ StatusIconChangeTable[SC_SPEARSQUICKEN] = SI_SPEARQUICKEN;
+ StatusIconChangeTable[SC_EXPLOSIONSPIRITS] = SI_EXPLOSIONSPIRITS;
+ StatusIconChangeTable[SC_FURY] = SI_FURY;
+ StatusIconChangeTable[SC_FIREWEAPON] = SI_FIREWEAPON;
+ StatusIconChangeTable[SC_WATERWEAPON] = SI_WATERWEAPON;
+ StatusIconChangeTable[SC_WINDWEAPON] = SI_WINDWEAPON;
+ StatusIconChangeTable[SC_EARTHWEAPON] = SI_EARTHWEAPON;
+ StatusIconChangeTable[SC_AURABLADE] = SI_AURABLADE;
+ StatusIconChangeTable[SC_PARRYING] = SI_PARRYING;
+ StatusIconChangeTable[SC_CONCENTRATION] = SI_CONCENTRATION;
+ StatusIconChangeTable[SC_TENSIONRELAX] = SI_TENSIONRELAX;
+ StatusIconChangeTable[SC_BERSERK] = SI_BERSERK;
+ StatusIconChangeTable[SC_ASSUMPTIO] = SI_ASSUMPTIO;
+ StatusIconChangeTable[SC_GUILDAURA] = SI_GUILDAURA;
+ StatusIconChangeTable[SC_MAGICPOWER] = SI_MAGICPOWER;
+ StatusIconChangeTable[SC_EDP] = SI_EDP;
+ StatusIconChangeTable[SC_TRUESIGHT] = SI_TRUESIGHT;
+ StatusIconChangeTable[SC_WINDWALK] = SI_WINDWALK;
+ StatusIconChangeTable[SC_MELTDOWN] = SI_MELTDOWN;
+ StatusIconChangeTable[SC_CARTBOOST] = SI_CARTBOOST;
+ StatusIconChangeTable[SC_CHASEWALK] = SI_CHASEWALK;
+ StatusIconChangeTable[SC_REJECTSWORD] = SI_REJECTSWORD;
+ StatusIconChangeTable[SC_MARIONETTE] = SI_MARIONETTE;
+ StatusIconChangeTable[SC_MARIONETTE2] = SI_MARIONETTE2;
+ StatusIconChangeTable[SC_MOONLIT] = SI_MOONLIT;
+ StatusIconChangeTable[SC_DEVOTION] = SI_DEVOTION;
+ StatusIconChangeTable[SC_STEELBODY] = SI_STEELBODY;
+ StatusIconChangeTable[SC_SPURT] = SI_SPURT;
+ StatusIconChangeTable[SC_SPIRIT] = SI_SPIRIT;
+ StatusIconChangeTable[SC_READYSTORM] = SI_READYSTORM;
+ StatusIconChangeTable[SC_READYDOWN] = SI_READYDOWN;
+ StatusIconChangeTable[SC_READYTURN] = SI_READYTURN;
+ StatusIconChangeTable[SC_READYCOUNTER] = SI_READYCOUNTER;
+ StatusIconChangeTable[SC_DODGE] = SI_DODGE;
+ StatusIconChangeTable[SC_SHADOWWEAPON] = SI_SHADOWWEAPON;
+ StatusIconChangeTable[SC_WARM] = SI_WARM;
+ StatusIconChangeTable[SC_SUN_COMFORT] = SI_SUN_COMFORT;
+ StatusIconChangeTable[SC_MOON_COMFORT] = SI_MOON_COMFORT;
+ StatusIconChangeTable[SC_STAR_COMFORT] = SI_STAR_COMFORT;
+ StatusIconChangeTable[SC_ADRENALINE2] = SI_ADRENALINE2;
+ StatusIconChangeTable[SC_GHOSTWEAPON] = SI_GHOSTWEAPON;
+ StatusIconChangeTable[SC_KAITE] = SI_KAITE;
+ StatusIconChangeTable[SC_KAIZEL] = SI_KAIZEL;
+ StatusIconChangeTable[SC_KAAHI] = SI_KAAHI;
+ StatusIconChangeTable[SC_KAUPE] = SI_KAUPE;
+ StatusIconChangeTable[SC_ONEHAND] = SI_ONEHAND;
+ StatusIconChangeTable[SC_PRESERVE] = SI_PRESERVE;
+ StatusIconChangeTable[SC_BATTLEORDERS] = SI_BATTLEORDERS;
+ StatusIconChangeTable[SC_DOUBLECAST] = SI_DOUBLECAST;
+ StatusIconChangeTable[SC_MAXOVERTHRUST] = SI_MAXOVERTHRUST;
+ StatusIconChangeTable[SC_SHRINK] = SI_SHRINK;
+ StatusIconChangeTable[SC_SIGHTBLASTER] = SI_SIGHTBLASTER;
+ StatusIconChangeTable[SC_WINKCHARM] = SI_WINKCHARM;
+ StatusIconChangeTable[SC_CLOSECONFINE] = SI_CLOSECONFINE;
+ StatusIconChangeTable[SC_CLOSECONFINE2] = SI_CLOSECONFINE2;
+}
+/*==========================================
+ * 精錬ボーナス
+ *------------------------------------------
+ */
+int status_getrefinebonus(int lv,int type)
+{
+ if (lv >= 0 && lv < 5 && type >= 0 && type < 3)
+ return refinebonus[lv][type];
+ return 0;
+}
+
+/*==========================================
+ * Checks whether the src can use the skill on the target,
+ * taking into account status/option of both source/target. [Skotlex]
+ * flag: 1 to indicate this call is done after the casting (target already selected)
+ * src MAY be null to indicate we shouldn't check it, this is a ground-based skill attack.
+ * target MAY Be null, in which case the checks are only to see
+ * whether the source can cast or not the skill on the ground.
+ *------------------------------------------
+ */
+int status_check_skilluse(struct block_list *src, struct block_list *target, int skill_num, int flag)
+{
+ int mode, race, hide_flag;
+ struct status_change *sc_data=NULL, *tsc_data;
+ short *option=NULL, *opt1=NULL;
+
+ if (src && status_isdead(src))
+ return 0;
+ if (target && status_isdead(target) && skill_num != ALL_RESURRECTION)
+ return 0;
+
+ if (skill_num == PA_PRESSURE && flag)
+ return 1; //Once Gloria Domini has been casted, there's nothing you can do to stop it. [Skotlex]
+
+ mode = src?status_get_mode(src):MD_CANATTACK;
+
+ if (!skill_num && !(mode&MD_CANATTACK))
+ return 0; //This mode is only needed for melee attacking.
+
+ if (((src && map_getcell(src->m,src->x,src->y,CELL_CHKBASILICA)) ||
+ (target && target != src && map_getcell(target->m,target->x,target->y,CELL_CHKBASILICA)))
+ && !(mode&MD_BOSS))
+ { //Basilica Check
+ if (!skill_num) return 0;
+ race = skill_get_inf(skill_num);
+ if (race&INF_ATTACK_SKILL)
+ return 0;
+ if (race&INF_GROUND_SKILL && skill_get_unit_target(skill_num)&BCT_ENEMY)
+ return 0;
+ }
+ if (src) {
+ option = status_get_option(src);
+ opt1 = status_get_opt1(src);
+ sc_data = status_get_sc_data(src);
+ }
+
+ if (opt1 && (*opt1) >0)
+ return 0;
+
+ if(sc_data)
+ {
+ if (
+ (sc_data[SC_TRICKDEAD].timer != -1 && skill_num != NV_TRICKDEAD)
+ || (sc_data[SC_AUTOCOUNTER].timer != -1 && skill_num != KN_AUTOCOUNTER)
+ || (sc_data[SC_GOSPEL].timer != -1 && sc_data[SC_GOSPEL].val4 == BCT_SELF && skill_num != PA_GOSPEL)
+ || sc_data[SC_GRAVITATION].timer != -1
+ )
+ return 0;
+
+ if (sc_data[SC_BLADESTOP].timer != -1) {
+ switch (sc_data[SC_BLADESTOP].val1)
+ {
+ case 1: return 0;
+ case 2: if (skill_num != MO_FINGEROFFENSIVE) return 0; break;
+ case 3: if (skill_num != MO_FINGEROFFENSIVE && skill_num != MO_INVESTIGATE) return 0; break;
+ case 4: if (skill_num != MO_FINGEROFFENSIVE && skill_num != MO_INVESTIGATE && skill_num != MO_CHAINCOMBO) return 0; break;
+ case 5: if (skill_num != MO_FINGEROFFENSIVE && skill_num != MO_INVESTIGATE && skill_num != MO_CHAINCOMBO && skill_num!=MO_EXTREMITYFIST) return 0; break;
+ default: return 0;
+ }
+ }
+ if (skill_num)
+ { //Skills blocked through status changes...
+ if ((sc_data[SC_VOLCANO].timer != -1 && skill_num == WZ_ICEWALL) ||
+ (sc_data[SC_ROKISWEIL].timer != -1 && skill_num != BD_ADAPTATION) ||
+ (sc_data[SC_MARIONETTE].timer != -1 && skill_num != CG_MARIONETTE) ||
+ (sc_data[SC_MARIONETTE2].timer != -1 && skill_num == CG_MARIONETTE) ||
+ (sc_data[SC_HERMODE].timer != -1 && skill_get_inf(skill_num) & INF_SUPPORT_SKILL) ||
+ sc_data[SC_SILENCE].timer != -1 || sc_data[SC_STEELBODY].timer != -1 ||
+ sc_data[SC_BERSERK].timer != -1 || sc_data[SC_SKA].timer != -1
+ )
+ return 0;
+
+ if (sc_data[SC_DANCING].timer != -1)
+ {
+ if (skill_num != BD_ADAPTATION && skill_num != CG_LONGINGFREEDOM
+ && skill_num != BA_MUSICALSTRIKE && skill_num != DC_THROWARROW)
+ return 0;
+ if (sc_data[SC_DANCING].val1 == CG_HERMODE && skill_num == BD_ADAPTATION)
+ return 0; //Can't amp out of Wand of Hermode :/ [Skotlex]
+ }
+ }
+ }
+
+ if (option)
+ {
+ if ((*option)&OPTION_HIDE && skill_num != TF_HIDING && skill_num != AS_GRIMTOOTH
+ && skill_num != RG_BACKSTAP && skill_num != RG_RAID)
+ return 0;
+ if ((*option)&OPTION_CLOAK && skill_num == TF_HIDING)
+ return 0;
+ if ((*option)&OPTION_CHASEWALK && skill_num != ST_CHASEWALK)
+ return 0;
+ }
+ if (target == NULL || target == src) //No further checking needed.
+ return 1;
+
+ tsc_data = status_get_sc_data(target);
+ if(tsc_data)
+ {
+ if (!(mode & MD_BOSS) && tsc_data[SC_TRICKDEAD].timer != -1)
+ return 0;
+ if(skill_num == WZ_STORMGUST && tsc_data[SC_FREEZE].timer != -1)
+ return 0;
+ if(skill_num == PR_LEXAETERNA && (tsc_data[SC_FREEZE].timer != -1 || (tsc_data[SC_STONE].timer != -1 && tsc_data[SC_STONE].val2 == 0)))
+ return 0;
+ }
+
+ if (src) {
+ race = status_get_race(src);
+ } else { //Ground skill, only earth-elemental skills have detecting-hitting capabilities.
+ race = 0;
+ if(skill_get_pl(skill_num) == 2)
+ mode|= MD_DETECTOR;
+ }
+ option = status_get_option(target);
+ hide_flag = flag?OPTION_HIDE:(OPTION_HIDE|OPTION_CLOAK|OPTION_CHASEWALK); //If targetting, cloak+hide protect you, otherwise only hiding does.
+
+ switch (target->type)
+ {
+ case BL_PC:
+ {
+ struct map_session_data *sd = (struct map_session_data*) target;
+ if (pc_isinvisible(sd))
+ return 0;
+ if (((*option)&hide_flag || sd->state.gangsterparadise)
+ && (sd->state.perfect_hiding || !(race == 4 || race == 6 || mode&MD_DETECTOR))
+ && !(mode&MD_BOSS))
+ return 0;
+ }
+ break;
+ case BL_PET:
+ return 0;
+ case BL_ITEM: //Allow targetting of items to pick'em up (or in the case of mobs, to loot them).
+ //TODO: Would be nice if this could be used to judge whether the player can or not pick up the item it targets. [Skotlex]
+ if (mode&MD_LOOTER)
+ return 1;
+ else
+ return 0;
+ default:
+ //Check for chase-walk/hiding/cloaking opponents.
+ if (option && !(mode&MD_BOSS))
+ {
+ if ((*option)&hide_flag && !(race == 4 || race == 6 || mode&MD_DETECTOR))
+ return 0;
+ }
+ }
+ return 1;
+}
+
+//Skotlex: Calculates the stats of the given pet.
+int status_calc_pet(struct map_session_data *sd, int first)
+{
+ struct pet_data *pd;
+
+ nullpo_retr(0, sd);
+ if (sd->status.pet_id == 0 || sd->pd == NULL)
+ return 1;
+
+ pd = sd->pd;
+
+ if (battle_config.pet_lv_rate && pd->status)
+ {
+ sd->pet.level = sd->status.base_level*battle_config.pet_lv_rate/100;
+ if (sd->pet.level < 0)
+ sd->pet.level = 1;
+ if (pd->status->level != sd->pet.level || first)
+ {
+ if (!first) //Lv Up animation
+ clif_misceffect(&pd->bl, 0);
+ pd->status->level = sd->pet.level;
+ pd->status->atk1 = (pd->db->atk1*pd->status->level)/pd->db->lv;
+ pd->status->atk2 = (pd->db->atk2*pd->status->level)/pd->db->lv;
+ pd->status->str = (pd->db->str*pd->status->level)/pd->db->lv;
+ pd->status->agi = (pd->db->agi*pd->status->level)/pd->db->lv;
+ pd->status->vit = (pd->db->vit*pd->status->level)/pd->db->lv;
+ pd->status->int_ = (pd->db->int_*pd->status->level)/pd->db->lv;
+ pd->status->dex = (pd->db->dex*pd->status->level)/pd->db->lv;
+ pd->status->luk = (pd->db->luk*pd->status->level)/pd->db->lv;
+
+ if (pd->status->atk1 > battle_config.pet_max_atk1) pd->status->atk1 = battle_config.pet_max_atk1;
+ if (pd->status->atk2 > battle_config.pet_max_atk2) pd->status->atk2 = battle_config.pet_max_atk2;
+
+ if (pd->status->str > battle_config.pet_max_stats) pd->status->str = battle_config.pet_max_stats;
+ else if (pd->status->str < 1) pd->status->str = 1;
+ if (pd->status->agi > battle_config.pet_max_stats) pd->status->agi = battle_config.pet_max_stats;
+ else if (pd->status->agi < 1) pd->status->agi = 1;
+ if (pd->status->vit > battle_config.pet_max_stats) pd->status->vit = battle_config.pet_max_stats;
+ else if (pd->status->vit < 1) pd->status->vit = 1;
+ if (pd->status->int_ > battle_config.pet_max_stats) pd->status->int_ = battle_config.pet_max_stats;
+ else if (pd->status->int_ < 1) pd->status->int_ = 1;
+ if (pd->status->dex > battle_config.pet_max_stats) pd->status->dex = battle_config.pet_max_stats;
+ else if (pd->status->dex < 1) pd->status->dex = 1;
+ if (pd->status->luk > battle_config.pet_max_stats) pd->status->luk = battle_config.pet_max_stats;
+ else if (pd->status->luk < 1) pd->status->luk = 1;
+
+ if (!first) //Not done the first time because the pet is not visible yet
+ clif_send_petstatus(sd);
+ }
+ }
+ //Support rate modifier (1000 = 100%)
+ pd->rate_fix = 1000*(sd->pet.intimate - battle_config.pet_support_min_friendly)/(1000- battle_config.pet_support_min_friendly) +500;
+ if(battle_config.pet_support_rate != 100)
+ pd->rate_fix = pd->rate_fix*battle_config.pet_support_rate/100;
+ return 0;
+}
+
+/*==========================================
+ * パラメータ計算
+ * first==0の時、計算対象のパラメータが呼び出し前から
+ * 変 化した場合自動でsendするが、
+ * 能動的に変化させたパラメータは自前でsendするように
+ *------------------------------------------
+ */
+
+int status_calc_pc(struct map_session_data* sd,int first)
+{
+ static int calculating = 0; //Check for recursive call preemption. [Skotlex]
+ int b_speed,b_max_hp,b_max_sp,b_hp,b_sp,b_weight,b_max_weight,b_paramb[6],b_parame[6],b_hit,b_flee;
+ int b_aspd,b_watk,b_def,b_watk2,b_def2,b_flee2,b_critical,b_attackrange,b_matk1,b_matk2,b_mdef,b_mdef2,b_class;
+ int b_base_atk;
+ struct skill b_skill[MAX_SKILL];
+ int i,bl,index;
+ int skill,wele,wele_,def_ele,refinedef=0;
+ int pele=0,pdef_ele=0;
+ int str,dstr,dex;
+
+ nullpo_retr(0, sd);
+ if (++calculating > 10) //Too many recursive calls to status_calc_pc!
+ return -1;
+
+ b_speed = sd->speed;
+ b_max_hp = sd->status.max_hp;
+ b_max_sp = sd->status.max_sp;
+ b_hp = sd->status.hp;
+ b_sp = sd->status.sp;
+ b_weight = sd->weight;
+ b_max_weight = sd->max_weight;
+ memcpy(b_paramb,&sd->paramb,sizeof(b_paramb));
+ memcpy(b_parame,&sd->paramc,sizeof(b_parame));
+ memcpy(b_skill,&sd->status.skill,sizeof(b_skill));
+ b_hit = sd->hit;
+ b_flee = sd->flee;
+ b_aspd = sd->aspd;
+ b_watk = sd->right_weapon.watk;
+ b_def = sd->def;
+ b_watk2 = sd->right_weapon.watk2;
+ b_def2 = sd->def2;
+ b_flee2 = sd->flee2;
+ b_critical = sd->critical;
+ b_attackrange = sd->attackrange;
+ b_matk1 = sd->matk1;
+ b_matk2 = sd->matk2;
+ b_mdef = sd->mdef;
+ b_mdef2 = sd->mdef2;
+ b_class = sd->view_class;
+ sd->view_class = sd->status.class_;
+ b_base_atk = sd->base_atk;
+
+ pc_calc_skilltree(sd); // スキルツリ?の計算
+
+ sd->max_weight = max_weight_base[sd->status.class_]+sd->status.str*300;
+
+ if(first&1) {
+ sd->weight=0;
+ for(i=0;i<MAX_INVENTORY;i++){
+ if(sd->status.inventory[i].nameid==0 || sd->inventory_data[i] == NULL)
+ continue;
+ sd->weight += sd->inventory_data[i]->weight*sd->status.inventory[i].amount;
+ }
+ sd->cart_max_weight=battle_config.max_cart_weight;
+ sd->cart_weight=0;
+ sd->cart_max_num=MAX_CART;
+ sd->cart_num=0;
+ for(i=0;i<MAX_CART;i++){
+ if(sd->status.cart[i].nameid==0)
+ continue;
+ sd->cart_weight+=itemdb_weight(sd->status.cart[i].nameid)*sd->status.cart[i].amount;
+ sd->cart_num++;
+ }
+ }
+
+ // these are not zeroed. [zzo]
+
+ sd->speed = DEFAULT_WALK_SPEED;
+ sd->hprate=battle_config.hp_rate;
+ sd->sprate=battle_config.sp_rate;
+ sd->castrate=100;
+ sd->delayrate=100;
+ sd->dsprate=100;
+ sd->aspd_rate = 100;
+ sd->speed_rate = 100;
+ sd->hprecov_rate = 100;
+ sd->sprecov_rate = 100;
+ sd->atk_rate = sd->matk_rate = 100;
+ sd->critical_rate = sd->hit_rate = sd->flee_rate = sd->flee2_rate = 100;
+ sd->def_rate = sd->def2_rate = sd->mdef_rate = sd->mdef2_rate = 100;
+ sd->speed_add_rate = sd->aspd_add_rate = 100;
+
+ // zeroed arays, order follows the order in map.h.
+ // add new arrays to the end of zeroed area in map.h (see comments) and size here. [zzo]
+ memset (sd->paramb, 0, sizeof(sd->paramb)
+ + sizeof(sd->parame)
+ + sizeof(sd->subele)
+ + sizeof(sd->subrace)
+ + sizeof(sd->subrace2)
+ + sizeof(sd->subsize)
+ + sizeof(sd->addeff)
+ + sizeof(sd->addeff2)
+ + sizeof(sd->reseff)
+ + sizeof(sd->weapon_coma_ele)
+ + sizeof(sd->weapon_coma_race)
+ + sizeof(sd->weapon_atk)
+ + sizeof(sd->weapon_atk_rate)
+ + sizeof(sd->arrow_addele)
+ + sizeof(sd->arrow_addrace)
+ + sizeof(sd->arrow_addsize)
+ + sizeof(sd->arrow_addeff)
+ + sizeof(sd->arrow_addeff2)
+ + sizeof(sd->magic_addele)
+ + sizeof(sd->magic_addrace)
+ + sizeof(sd->magic_addsize)
+ + sizeof(sd->critaddrace)
+ + sizeof(sd->expaddrace)
+ + sizeof(sd->itemhealrate)
+ + sizeof(sd->addeff3)
+ + sizeof(sd->addeff3_type)
+ + sizeof(sd->sp_gain_race)
+ + sizeof(sd->unequip_losehp)
+ + sizeof(sd->unequip_losesp)
+ );
+
+
+ memset (&sd->right_weapon.watk, 0, sizeof(sd->right_weapon) - sizeof(sd->right_weapon.atkmods));
+ memset (&sd->left_weapon.watk, 0, sizeof(sd->left_weapon) - sizeof(sd->left_weapon.atkmods));
+
+ memset(&sd->special_state,0,sizeof(sd->special_state));
+
+ sd->status.max_hp = 0;
+ sd->status.max_sp = 0;
+
+ //zero up structures...
+ memset(&sd->autospell,0,sizeof(sd->autospell)
+ + sizeof(sd->autospell2)
+ + sizeof(sd->skillatk)
+ + sizeof(sd->skillblown)
+ + sizeof(sd->add_def)
+ + sizeof(sd->add_mdef)
+ + sizeof(sd->add_dmg)
+ + sizeof(sd->add_mdmg)
+ + sizeof(sd->add_drop)
+ );
+
+ // vars zeroing. ints, shorts, chars. in that order.
+ memset (&sd->hit, 0, sizeof(sd->hit)
+ + sizeof(sd->flee)
+ + sizeof(sd->flee2)
+ + sizeof(sd->critical)
+ + sizeof(sd->aspd)
+ + sizeof(sd->def)
+ + sizeof(sd->mdef)
+ + sizeof(sd->def2)
+ + sizeof(sd->mdef2)
+ + sizeof(sd->def_ele)
+ + sizeof(sd->matk1)
+ + sizeof(sd->matk2)
+ + sizeof(sd->base_atk)
+ + sizeof(sd->arrow_atk)
+ + sizeof(sd->arrow_ele)
+ + sizeof(sd->arrow_cri)
+ + sizeof(sd->arrow_hit)
+ + sizeof(sd->arrow_range)
+ + sizeof(sd->nhealhp)
+ + sizeof(sd->nhealsp)
+ + sizeof(sd->nshealhp)
+ + sizeof(sd->nshealsp)
+ + sizeof(sd->nsshealhp)
+ + sizeof(sd->nsshealsp)
+ + sizeof(sd->critical_def)
+ + sizeof(sd->double_rate)
+ + sizeof(sd->long_attack_atk_rate)
+ + sizeof(sd->near_attack_def_rate)
+ + sizeof(sd->long_attack_def_rate)
+ + sizeof(sd->magic_def_rate)
+ + sizeof(sd->misc_def_rate)
+ + sizeof(sd->ignore_mdef_ele)
+ + sizeof(sd->ignore_mdef_race)
+ + sizeof(sd->perfect_hit)
+ + sizeof(sd->perfect_hit_add)
+ + sizeof(sd->get_zeny_rate)
+ + sizeof(sd->get_zeny_num)
+ + sizeof(sd->double_add_rate)
+ + sizeof(sd->short_weapon_damage_return)
+ + sizeof(sd->long_weapon_damage_return)
+ + sizeof(sd->magic_damage_return)
+ + sizeof(sd->random_attack_increase_add)
+ + sizeof(sd->random_attack_increase_per)
+ + sizeof(sd->break_weapon_rate)
+ + sizeof(sd->break_armor_rate)
+ + sizeof(sd->crit_atk_rate)
+ + sizeof(sd->hp_loss_rate)
+ + sizeof(sd->sp_loss_rate)
+ + sizeof(sd->classchange)
+ + sizeof(sd->setitem_hash)
+ + sizeof(sd->setitem_hash2)
+ // shorts
+ + sizeof(sd->attackrange)
+ + sizeof(sd->attackrange_)
+ + sizeof(sd->splash_range)
+ + sizeof(sd->splash_add_range)
+ + sizeof(sd->add_steal_rate)
+ + sizeof(sd->hp_loss_value)
+ + sizeof(sd->sp_loss_value)
+ + sizeof(sd->hp_loss_type)
+ + sizeof(sd->sp_drain_type)
+ + sizeof(sd->hp_gain_value)
+ + sizeof(sd->sp_gain_value)
+ + sizeof(sd->add_drop_count)
+ + sizeof(sd->unbreakable)
+ + sizeof(sd->unbreakable_equip)
+ + sizeof(sd->unstripable_equip)
+ + sizeof(sd->no_regen)
+ + sizeof(sd->add_def_count)
+ + sizeof(sd->add_mdef_count)
+ + sizeof(sd->add_dmg_count)
+ + sizeof(sd->add_mdmg_count)
+ );
+
+ if(!sd->state.disguised && sd->disguise) {
+ pc_stop_walking(sd,0);
+ clif_clearchar(&sd->bl, 0);
+ sd->disguise=0;
+ clif_changeoption(&sd->bl);
+ clif_spawnpc(sd);
+ }
+
+ for(i=0;i<10;i++) {
+ current_equip_item_index = index = sd->equip_index[i]; //We pass INDEX to current_equip_item_index - for EQUIP_SCRIPT (new cards solution) [Lupus]
+ if(index < 0)
+ continue;
+ if(i == 9 && sd->equip_index[8] == index)
+ continue;
+ if(i == 5 && sd->equip_index[4] == index)
+ continue;
+ if(i == 6 && (sd->equip_index[5] == index || sd->equip_index[4] == index))
+ continue;
+
+ if(sd->inventory_data[index]) {
+ if(sd->inventory_data[index]->type == 4) { // Weapon cards
+ if(sd->status.inventory[index].card[0]!=0x00ff && sd->status.inventory[index].card[0]!=0x00fe && sd->status.inventory[index].card[0]!=(short)0xff00) {
+ int j;
+ for(j=0;j<sd->inventory_data[index]->slot;j++){ // カ?ド
+ int c=sd->status.inventory[index].card[j];
+ if(c>0){
+ if(i == 8 && sd->status.inventory[index].equip == 0x20)
+ sd->state.lr_flag = 1;
+ run_script(itemdb_equipscript(c),0,sd->bl.id,0);
+ sd->state.lr_flag = 0;
+ if (!calculating) //Abort, run_script retriggered status_calc_pc. [Skotlex]
+ return 1;
+ }
+ }
+ }
+ }
+ else if(sd->inventory_data[index]->type==5){ // Non-weapon equipment cards
+ if(sd->status.inventory[index].card[0]!=0x00ff && sd->status.inventory[index].card[0]!=0x00fe && sd->status.inventory[index].card[0]!=(short)0xff00) {
+ int j;
+ for(j=0;j<sd->inventory_data[index]->slot;j++){ // カ?ド
+ int c=sd->status.inventory[index].card[j];
+ if(c>0) {
+ run_script(itemdb_equipscript(c),0,sd->bl.id,0);
+ if (!calculating) //Abort, run_script retriggered status_calc_pc. [Skotlex]
+ return 1;
+ }
+ }
+ }
+ }
+ }
+ }
+ wele = sd->right_weapon.atk_ele;
+ wele_ = sd->left_weapon.atk_ele;
+ def_ele = sd->def_ele;
+
+ if(sd->status.pet_id > 0) { // Pet
+ struct pet_data *pd=sd->pd;
+ if((pd && battle_config.pet_status_support) && (!battle_config.pet_equip_required || pd->equip > 0)) {
+ if(sd->pet.intimate > 0 && pd->state.skillbonus == 1 && pd->bonus) { //Skotlex: Readjusted for pets
+ pc_bonus(sd,pd->bonus->type, pd->bonus->val);
+ }
+ pele = sd->right_weapon.atk_ele;
+ pdef_ele = sd->def_ele;
+ sd->right_weapon.atk_ele = sd->def_ele = 0;
+ }
+ }
+ memcpy(sd->paramcard,sd->parame,sizeof(sd->paramcard));
+
+ // ?備品によるステ?タス?化はここで?行
+ for(i=0;i<10;i++) {
+ current_equip_item_index = index = sd->equip_index[i]; //We pass INDEX to current_equip_item_index - for EQUIP_SCRIPT (new cards solution) [Lupus]
+ if(index < 0)
+ continue;
+ if(i == 9 && sd->equip_index[8] == index)
+ continue;
+ if(i == 5 && sd->equip_index[4] == index)
+ continue;
+ if(i == 6 && (sd->equip_index[5] == index || sd->equip_index[4] == index))
+ continue;
+ if(sd->inventory_data[index]) {
+ sd->def += sd->inventory_data[index]->def;
+ if(sd->inventory_data[index]->type == 4) {
+ int r,wlv = sd->inventory_data[index]->wlv;
+ if (wlv >= MAX_REFINE_BONUS)
+ wlv = MAX_REFINE_BONUS - 1;
+ if(i == 8 && sd->status.inventory[index].equip == 0x20) { // Left-hand weapon
+ sd->left_weapon.watk += sd->inventory_data[index]->atk;
+ sd->left_weapon.watk2 = (r=sd->status.inventory[index].refine)* // 精?攻?力
+ refinebonus[wlv][0];
+ if( (r-=refinebonus[wlv][2])>0 ) // 過?精?ボ?ナス
+ sd->left_weapon.overrefine = r*refinebonus[wlv][1];
+
+ if(sd->status.inventory[index].card[0]==0x00ff){ // Forged weapon
+ sd->left_weapon.star = (sd->status.inventory[index].card[1]>>8); // 星のかけら
+ if(sd->left_weapon.star >= 15) sd->left_weapon.star = 40; // 3 Star Crumbs now give +40 dmg
+ if (pc_istop10fame( MakeDWord(sd->status.inventory[index].card[2],sd->status.inventory[index].card[3]) ,MAPID_BLACKSMITH))
+ sd->left_weapon.star += 10;
+ wele_= (sd->status.inventory[index].card[1]&0x0f); // ? 性
+ }
+ sd->attackrange_ += sd->inventory_data[index]->range;
+ sd->state.lr_flag = 1;
+ run_script(sd->inventory_data[index]->script,0,sd->bl.id,0);
+ sd->state.lr_flag = 0;
+ if (!calculating) //Abort, run_script retriggered status_calc_pc. [Skotlex]
+ return 1;
+ }
+ else { // Right-hand weapon
+ sd->right_weapon.watk += sd->inventory_data[index]->atk;
+ sd->right_weapon.watk2 += (r=sd->status.inventory[index].refine)* // 精?攻?力
+ refinebonus[wlv][0];
+ if( (r-=refinebonus[wlv][2])>0 ) // 過?精?ボ?ナス
+ sd->right_weapon.overrefine += r*refinebonus[wlv][1];
+
+ if(sd->status.inventory[index].card[0]==0x00ff){ // Forged weapon
+ sd->right_weapon.star += (sd->status.inventory[index].card[1]>>8); // 星のかけら
+ if(sd->right_weapon.star >= 15) sd->right_weapon.star = 40; // 3 Star Crumbs now give +40 dmg
+ if (pc_istop10fame( MakeDWord(sd->status.inventory[index].card[2],sd->status.inventory[index].card[3]) ,MAPID_BLACKSMITH))
+ sd->right_weapon.star += 10;
+ wele = (sd->status.inventory[index].card[1]&0x0f); // ? 性
+ }
+ sd->attackrange += sd->inventory_data[index]->range;
+ run_script(sd->inventory_data[index]->script,0,sd->bl.id,0);
+ if (!calculating) //Abort, run_script retriggered status_calc_pc. [Skotlex]
+ return 1;
+ }
+ }
+ else if(sd->inventory_data[index]->type == 5) {
+ sd->right_weapon.watk += sd->inventory_data[index]->atk;
+ refinedef += sd->status.inventory[index].refine*refinebonus[0][0];
+ run_script(sd->inventory_data[index]->script,0,sd->bl.id,0);
+ if (!calculating) //Abort, run_script retriggered status_calc_pc. [Skotlex]
+ return 1;
+ }
+ }
+ }
+
+ if(sd->equip_index[10] >= 0){ // 矢
+ index = sd->equip_index[10];
+ if(sd->inventory_data[index]){ // Arrows
+ sd->state.lr_flag = 2;
+ run_script(sd->inventory_data[index]->script,0,sd->bl.id,0);
+ sd->state.lr_flag = 0;
+ sd->arrow_atk += sd->inventory_data[index]->atk;
+ }
+ }
+ sd->def += (refinedef+50)/100;
+
+ if(sd->attackrange < 1) sd->attackrange = 1;
+ if(sd->attackrange_ < 1) sd->attackrange_ = 1;
+ if(sd->attackrange < sd->attackrange_)
+ sd->attackrange = sd->attackrange_;
+ if(sd->status.weapon == 11)
+ sd->attackrange += sd->arrow_range;
+ if(wele > 0)
+ sd->right_weapon.atk_ele = wele;
+ if(wele_ > 0)
+ sd->left_weapon.atk_ele = wele_;
+ if(def_ele > 0)
+ sd->def_ele = def_ele;
+ if(battle_config.pet_status_support) {
+ if(pele > 0 && !sd->right_weapon.atk_ele)
+ sd->right_weapon.atk_ele = pele;
+ if(pdef_ele > 0 && !sd->def_ele)
+ sd->def_ele = pdef_ele;
+ }
+ sd->double_rate += sd->double_add_rate;
+ sd->perfect_hit += sd->perfect_hit_add;
+ sd->splash_range += sd->splash_add_range;
+ if(sd->aspd_add_rate != 100)
+ sd->aspd_rate += sd->aspd_add_rate-100;
+ if(sd->speed_add_rate != 100)
+ sd->speed_rate += sd->speed_add_rate-100;
+
+ // Damage modifiers from weapon type
+ sd->right_weapon.atkmods[0] = atkmods[0][sd->weapontype1];
+ sd->right_weapon.atkmods[1] = atkmods[1][sd->weapontype1];
+ sd->right_weapon.atkmods[2] = atkmods[2][sd->weapontype1];
+ sd->left_weapon.atkmods[0] = atkmods[0][sd->weapontype2];
+ sd->left_weapon.atkmods[1] = atkmods[1][sd->weapontype2];
+ sd->left_weapon.atkmods[2] = atkmods[2][sd->weapontype2];
+
+// ----- STATS CALCULATION -----
+
+ // Job bonuses
+ for(i=0;i<(int)sd->status.job_level && i<MAX_LEVEL;i++){
+ if(job_bonus[sd->status.class_][i])
+ sd->paramb[job_bonus[sd->status.class_][i]-1]++;
+ }
+
+ // If a Super Novice has never died and is at least joblv 70, he gets all stats +10
+ if((sd->class_&MAPID_UPPERMASK) == MAPID_SUPER_NOVICE && sd->die_counter == 0 && sd->status.job_level >= 70){
+ sd->paramb[0] += 10;
+ sd->paramb[1] += 10;
+ sd->paramb[2] += 10;
+ sd->paramb[3] += 10;
+ sd->paramb[4] += 10;
+ sd->paramb[5] += 10;
+ }
+
+ // Absolute modifiers from passive skills
+ if(pc_checkskill(sd,BS_HILTBINDING)>0)
+ sd->paramb[0] ++;
+ if((skill=pc_checkskill(sd,SA_DRAGONOLOGY))>0)
+ sd->paramb[3] += (skill+1)/2; // +1 INT / 2 lv
+ if((skill=pc_checkskill(sd,AC_OWL))>0)
+ sd->paramb[4] += skill;
+
+ // Improve Concentration adds a percentage of base stat + job bonus + equipment bonus, but not from card bonus nor status change bonus
+ if(sd->sc_count && sd->sc_data[SC_CONCENTRATE].timer!=-1 && sd->sc_data[SC_QUAGMIRE].timer == -1){
+ sd->paramb[1] += (sd->status.agi+sd->paramb[1]+sd->parame[1]-sd->paramcard[1])*(2+sd->sc_data[SC_CONCENTRATE].val1)/100;
+ sd->paramb[4] += (sd->status.dex+sd->paramb[4]+sd->parame[4]-sd->paramcard[4])*(2+sd->sc_data[SC_CONCENTRATE].val1)/100;
+ }
+
+ // Absolute modifiers from status changes (only for PC)
+ if(sd->sc_count){
+ if (sd->sc_data[SC_BATTLEORDERS].timer != -1) {
+ sd->paramb[0] += 5;
+ sd->paramb[3] += 5;
+ sd->paramb[4] += 5;
+ }
+ if (sd->sc_data[SC_GUILDAURA].timer != -1) {
+ int guildflag = sd->sc_data[SC_GUILDAURA].val4;
+ for (i = 12; i >= 0; i -= 4) {
+ skill = guildflag >> i;
+ switch (i) {
+ case 12: sd->paramb[0] += skill; break;
+ case 8: sd->paramb[2] += skill; break;
+ case 4: sd->paramb[1] += skill; break;
+ case 0: sd->paramb[4] += skill; break;
+ }
+ guildflag ^= skill << i;
+ }
+ }
+ }
+
+ // Absolute, then relative modifiers from status changes (shared between PC and NPC)
+ sd->paramb[0] = status_calc_str(&sd->bl,sd->paramb[0]);
+ sd->paramb[1] = status_calc_agi(&sd->bl,sd->paramb[1]);
+ sd->paramb[2] = status_calc_vit(&sd->bl,sd->paramb[2]);
+ sd->paramb[3] = status_calc_int(&sd->bl,sd->paramb[3]);
+ sd->paramb[4] = status_calc_dex(&sd->bl,sd->paramb[4]);
+ sd->paramb[5] = status_calc_luk(&sd->bl,sd->paramb[5]);
+
+ // Relative modifiers from status changes (only for PC)
+ if(sd->sc_count){
+ if(sd->sc_data[SC_MARIONETTE].timer!=-1){
+ sd->paramb[0]-= sd->status.str/2;
+ sd->paramb[1]-= sd->status.agi/2;
+ sd->paramb[2]-= sd->status.vit/2;
+ sd->paramb[3]-= sd->status.int_/2;
+ sd->paramb[4]-= sd->status.dex/2;
+ sd->paramb[5]-= sd->status.luk/2;
+ }
+ else if(sd->sc_data[SC_MARIONETTE2].timer!=-1){
+ struct map_session_data *psd = map_id2sd(sd->sc_data[SC_MARIONETTE2].val3);
+ if (psd) { // if partner is found
+ sd->paramb[0] += sd->status.str+psd->status.str/2 > 99 ? 99-sd->status.str : psd->status.str/2;
+ sd->paramb[1] += sd->status.agi+psd->status.agi/2 > 99 ? 99-sd->status.agi : psd->status.agi/2;
+ sd->paramb[2] += sd->status.vit+psd->status.vit/2 > 99 ? 99-sd->status.vit : psd->status.vit/2;
+ sd->paramb[3] += sd->status.int_+psd->status.int_/2 > 99 ? 99-sd->status.int_ : psd->status.int_/2;
+ sd->paramb[4] += sd->status.dex+psd->status.dex/2 > 99 ? 99-sd->status.dex : psd->status.dex/2;
+ sd->paramb[5] += sd->status.luk+psd->status.luk/2 > 99 ? 99-sd->status.luk : psd->status.luk/2;
+ }
+ }
+ }
+
+ // Calculate total stats
+ sd->paramc[0]=sd->status.str+sd->paramb[0]+sd->parame[0];
+ sd->paramc[1]=sd->status.agi+sd->paramb[1]+sd->parame[1];
+ sd->paramc[2]=sd->status.vit+sd->paramb[2]+sd->parame[2];
+ sd->paramc[3]=sd->status.int_+sd->paramb[3]+sd->parame[3];
+ sd->paramc[4]=sd->status.dex+sd->paramb[4]+sd->parame[4];
+ sd->paramc[5]=sd->status.luk+sd->paramb[5]+sd->parame[5];
+
+ if(sd->sc_count){
+ if(sd->sc_data[SC_SPIRIT].timer!=-1 && sd->sc_data[SC_SPIRIT].val2 == SL_HIGH)
+ { //Ups any status under 50 to 50.
+ if (sd->paramc[0] < 50) {
+ sd->paramb[0] += 50-sd->paramc[0];
+ sd->paramc[0] = 50;
+ }
+ if (sd->paramc[1] < 50) {
+ sd->paramb[1] += 50-sd->paramc[1];
+ sd->paramc[1] = 50;
+ }
+ if (sd->paramc[2] < 50) {
+ sd->paramb[2] += 50-sd->paramc[2];
+ sd->paramc[2] = 50;
+ }
+ if (sd->paramc[3] < 50) {
+ sd->paramb[3] += 50-sd->paramc[3];
+ sd->paramc[3] = 50;
+ }
+ if (sd->paramc[4] < 50) {
+ sd->paramb[4] += 50-sd->paramc[4];
+ sd->paramc[4] = 50;
+ }
+ if (sd->paramc[5] < 50) {
+ sd->paramb[5] += 50-sd->paramc[5];
+ sd->paramc[5] = 50;
+ }
+ }
+ }
+ for(i=0;i<6;i++)
+ if(sd->paramc[i] < 0) sd->paramc[i] = 0;
+
+ if (sd->sc_count && sd->sc_data[SC_CURSE].timer!=-1)
+ sd->paramc[5] = 0;
+
+// ------ BASE ATTACK CALCULATION ------
+
+ // Basic Base ATK value
+ switch(sd->status.weapon){
+ case 11: // Bows
+ case 13: // Musical Instruments
+ case 14: // Whips
+ str = sd->paramc[4];
+ dex = sd->paramc[0];
+ break;
+ default:
+ str = sd->paramc[0];
+ dex = sd->paramc[4];
+ break;
+ }
+ dstr = str/10;
+ sd->base_atk += str + dstr*dstr + dex/5 + sd->paramc[5]/5;
+
+ // Absolute modifiers from passive skills
+ if((skill=pc_checkskill(sd,BS_HILTBINDING))>0)
+ sd->base_atk += 4;
+
+ // Absolute, then relative modifiers from status changes (shared between PC and NPC)
+ sd->base_atk = status_calc_batk(&sd->bl,sd->base_atk);
+
+ if(sd->base_atk < 1)
+ sd->base_atk = 1;
+
+// ----- WEAPON ATK CALCULATION -----
+
+ // Absolute, then relative modifiers from status changes (shared between PC and NPC)
+ sd->right_weapon.watk = status_calc_watk(&sd->bl,sd->right_weapon.watk);
+ if((index= sd->equip_index[8]) >= 0 && sd->inventory_data[index] && sd->inventory_data[index]->type == 4)
+ sd->left_weapon.watk = status_calc_watk(&sd->bl,sd->left_weapon.watk);
+
+// ----- WEAPON UPGRADE ATK CALCULATION -----
+
+ // Absolute modifiers from status changes (only for PC)
+ if(sd->sc_count){
+ if(sd->sc_data[SC_NIBELUNGEN].timer!=-1){
+ index = sd->equip_index[9];
+ if(index >= 0 && sd->inventory_data[index] && sd->inventory_data[index]->wlv == 4)
+ sd->right_weapon.watk2 += sd->sc_data[SC_NIBELUNGEN].val2;
+ index = sd->equip_index[8];
+ if(index >= 0 && sd->inventory_data[index] && sd->inventory_data[index]->wlv == 4)
+ sd->left_weapon.watk2 += sd->sc_data[SC_NIBELUNGEN].val2;
+ }
+ }
+
+// ----- MATK CALCULATION -----
+
+ // Basic MATK value
+ sd->matk1 += sd->paramc[3]+(sd->paramc[3]/5)*(sd->paramc[3]/5);
+ sd->matk2 += sd->paramc[3]+(sd->paramc[3]/7)*(sd->paramc[3]/7);
+ if(sd->matk1 < sd->matk2) {
+ int temp = sd->matk2;
+ sd->matk2 = sd->matk1;
+ sd->matk1 = temp;
+ }
+
+ // Absolute, then relative modifiers from status changes (shared between PC and NPC)
+ sd->matk1 = status_calc_matk(&sd->bl,sd->matk1);
+ sd->matk2 = status_calc_matk(&sd->bl,sd->matk2);
+
+ // Apply relative modifiers from equipment
+ if(sd->matk_rate != 100){
+ sd->matk1 = sd->matk1 * sd->matk_rate/100;
+ sd->matk2 = sd->matk2 * sd->matk_rate/100;
+ }
+
+// ----- CRIT CALCULATION -----
+
+ // Basic Crit value
+ sd->critical += (sd->paramc[5]*3)+10;
+
+ // Absolute, then relative modifiers from status changes (shared between PC and NPC)
+ sd->critical = status_calc_critical(&sd->bl,sd->critical);
+
+ // Apply relative modifiers from equipment
+ if(sd->critical_rate != 100)
+ sd->critical = sd->critical * sd->critical_rate/100;
+
+ if(sd->critical < 10) sd->critical = 10;
+
+// ----- HIT CALCULATION -----
+
+ // Basic Hit value
+ sd->hit += sd->paramc[4] + sd->status.base_level;
+
+ // Absolute modifiers from passive skills
+ if((skill=pc_checkskill(sd,BS_WEAPONRESEARCH))>0)
+ sd->hit += skill*2;
+ if((skill=pc_checkskill(sd,AC_VULTURE))>0){
+ sd->hit += skill;
+ if(sd->status.weapon == 11)
+ sd->attackrange += skill;
+ }
+
+ // Absolute, then relative modifiers from status changes (shared between PC and NPC)
+ sd->hit = status_calc_hit(&sd->bl,sd->hit);
+
+ // Apply relative modifiers from equipment
+ if(sd->hit_rate != 100)
+ sd->hit = sd->hit * sd->hit_rate/100;
+
+ if(sd->hit < 1) sd->hit = 1;
+
+// ----- FLEE CALCULATION -----
+
+ // Basic Flee value
+ sd->flee += sd->paramc[1] + sd->status.base_level;
+
+ // Absolute modifiers from passive skills
+ if((skill=pc_checkskill(sd,TF_MISS))>0)
+ sd->flee += skill*(sd->class_&JOBL_2 && (sd->class_&MAPID_BASEMASK) == MAPID_THIEF? 4 : 3);
+ if((skill=pc_checkskill(sd,MO_DODGE))>0)
+ sd->flee += (skill*3)>>1;
+
+ // Absolute, then relative modifiers from status changes (shared between PC and NPC)
+ sd->flee = status_calc_flee(&sd->bl,sd->flee);
+
+ // Apply relative modifiers from equipment
+ if(sd->flee_rate != 100)
+ sd->flee = sd->flee * sd->flee_rate/100;
+
+ if(sd->flee < 1) sd->flee = 1;
+
+// ----- PERFECT DODGE CALCULATION -----
+
+ // Basic Perfect Dodge value
+ sd->flee2 += sd->paramc[5]+10;
+
+ // Absolute, then relative modifiers from status changes (shared between PC and NPC)
+ sd->flee2 = status_calc_flee2(&sd->bl,sd->flee2);
+
+ // Apply relative modifiers from equipment
+ if(sd->flee2_rate != 100)
+ sd->flee2 = sd->flee2 * sd->flee2_rate/100;
+
+ if(sd->flee2 < 10) sd->flee2 = 10;
+
+// ----- VIT-DEF CALCULATION -----
+
+ // Special fixed values from status changes
+ if(sd->sc_count && sd->sc_data[SC_BERSERK].timer!=-1)
+ sd->def2 = 0;
+ else if(sd->sc_count && sd->sc_data[SC_ETERNALCHAOS].timer!=-1)
+ sd->def2 = 0;
+ else {
+ // Basic VIT-DEF value
+ sd->def2 += sd->paramc[2];
+
+ // Absolute, then relative modifiers from status changes (shared between PC and NPC)
+ sd->def2 = status_calc_def2(&sd->bl,sd->def2);
+
+ // Apply relative modifiers from equipment
+ if(sd->def2_rate != 100)
+ sd->def2 = sd->def2 * sd->def2_rate/100;
+
+ if(sd->def2 < 1) sd->def2 = 1;
+ }
+
+// ----- EQUIPMENT-DEF CALCULATION -----
+
+ // Absolute, then relative modifiers from status changes (shared between PC and NPC)
+ sd->def = status_calc_def(&sd->bl,sd->def);
+
+ // Apply relative modifiers from equipment
+ if(sd->def_rate != 100)
+ sd->def = sd->def * sd->def_rate/100;
+
+ if(sd->def < 0) sd->def = 0;
+
+ else if (!battle_config.player_defense_type && sd->def > battle_config.max_def)
+ {
+ sd->def2 += battle_config.over_def_bonus*(sd->def -battle_config.max_def);
+ sd->def = battle_config.max_def;
+ }
+
+// ----- INT-MDEF CALCULATION -----
+
+ // Special fixed values from status changes
+ if(sd->sc_count && sd->sc_data[SC_BERSERK].timer!=-1)
+ sd->mdef2 = 0;
+ else {
+ // Basic INT-MDEF value
+ sd->mdef2 += sd->paramc[3];
+
+ // sd->mdef2 = status_calc_mdef2(&sd->bl,sd->mdef2);
+
+ // Apply relative modifiers from equipment
+ if(sd->mdef2_rate != 100)
+ sd->mdef2 = sd->mdef2 * sd->mdef2_rate/100;
+
+ if(sd->mdef2 < 1) sd->mdef2 = 1;
+ }
+
+// ----- EQUIPMENT-MDEF CALCULATION -----
+
+ // Absolute, then relative modifiers from status changes (shared between PC and NPC)
+ sd->mdef = status_calc_mdef(&sd->bl,sd->mdef);
+
+ // Apply relative modifiers from equipment
+ if(sd->mdef_rate != 100)
+ sd->mdef = sd->mdef * sd->mdef_rate/100;
+
+ if(sd->mdef < 0) sd->mdef = 0;
+
+ else if (!battle_config.player_defense_type && sd->mdef > battle_config.max_def)
+ {
+ sd->mdef2 += battle_config.over_def_bonus*(sd->mdef -battle_config.max_def);
+ sd->mdef = battle_config.max_def;
+ }
+
+// ----- WALKING SPEED CALCULATION -----
+
+ // Relative, then absolute modifiers from status changes (shared between PC and NPC)
+ sd->speed = status_calc_speed(&sd->bl,sd->speed);
+
+ // Relative modifiers from passive skills
+ if((skill=pc_checkskill(sd,TF_MISS))>0 && (sd->class_&MAPID_UPPERMASK) == MAPID_ASSASSIN && sd->sc_data[SC_CLOAKING].timer==-1)
+ sd->speed -= sd->speed * skill/100;
+ if(pc_isriding(sd) && pc_checkskill(sd,KN_RIDING)>0)
+ sd->speed -= sd->speed * 25/100;
+ if(pc_ishiding(sd) && (skill=pc_checkskill(sd,RG_TUNNELDRIVE))>0)
+ sd->speed += sd->speed * (100-16*skill)/100;
+ if(pc_iscarton(sd) && (skill=pc_checkskill(sd,MC_PUSHCART))>0)
+ sd->speed += sd->speed * (100-10*skill)/100;
+ if(sd->skilltimer != -1 && (skill=pc_checkskill(sd,SA_FREECAST))>0) {
+ sd->prev_speed = sd->speed; //Store previous speed to correctly restore it. [Skotlex]
+ sd->speed += sd->speed * (75-5*skill)/100;
+ }
+ if(sd->sc_count && sd->sc_data[SC_DANCING].timer!=-1){
+ int s_rate = 500-40*pc_checkskill(sd,(sd->status.sex?BA_MUSICALLESSON:DC_DANCINGLESSON));
+ if (sd->sc_data[SC_SPIRIT].timer != -1 && sd->sc_data[SC_SPIRIT].val2 == SL_BARDDANCER)
+ s_rate -= 40; //TODO: Figure out real bonus rate.
+ if (sd->sc_data[SC_LONGING].timer!=-1)
+ s_rate -= 20 * sd->sc_data[SC_LONGING].val1;
+ sd->speed += sd->speed * s_rate/100;
+ }
+ if(sd->sc_data[SC_FUSION].timer != -1) //Additional movement speed from SG_FUSION [Komurka]
+ sd->speed -= sd->speed * 25/100;
+
+ // Apply relative modifiers from equipment
+ if(sd->speed_rate != 100)
+ sd->speed = sd->speed*sd->speed_rate/100;
+
+ if(sd->speed < battle_config.max_walk_speed)
+ sd->speed = battle_config.max_walk_speed;
+
+// ----- ASPD CALCULATION -----
+// Unlike other stats, ASPD rate modifiers from skills/SCs/items/etc are first all added together, then the final modifier is applied
+
+ // Basic ASPD value
+ if (sd->status.weapon <= 16)
+ sd->aspd += aspd_base[sd->status.class_][sd->status.weapon]-(sd->paramc[1]*4+sd->paramc[4])*aspd_base[sd->status.class_][sd->status.weapon]/1000;
+ else
+ sd->aspd += (
+ (aspd_base[sd->status.class_][sd->weapontype1]-(sd->paramc[1]*4+sd->paramc[4])*aspd_base[sd->status.class_][sd->weapontype1]/1000) +
+ (aspd_base[sd->status.class_][sd->weapontype2]-(sd->paramc[1]*4+sd->paramc[4])*aspd_base[sd->status.class_][sd->weapontype2]/1000)
+ ) *2/3; //From what I read in rodatazone, 2/3 should be more accurate than 0.7 -> 140 / 200; [Skotlex]
+
+ // Relative modifiers from passive skills
+ if((skill=pc_checkskill(sd,SA_ADVANCEDBOOK))>0)
+ sd->aspd_rate -= (skill/2);
+ if((skill = pc_checkskill(sd,SG_DEVIL)) > 0 && sd->status.job_level >= battle_config.max_job_level)
+ sd->aspd_rate -= (skill*3);
+
+ if(pc_isriding(sd))
+ sd->aspd_rate += 50-10*pc_checkskill(sd,KN_CAVALIERMASTERY);
+
+ // Relative modifiers from status changes (shared between PC and NPC)
+ sd->aspd_rate = status_calc_aspd_rate(&sd->bl,sd->aspd_rate);
+
+ // Apply all relative modifiers
+ if(sd->aspd_rate != 100)
+ sd->aspd = sd->aspd*sd->aspd_rate/100;
+
+ if(sd->aspd < battle_config.max_aspd) sd->aspd = battle_config.max_aspd;
+ sd->amotion = sd->aspd;
+
+// ----- HP MAX AND REGEN CALCULATION -----
+
+ // Basic MaxHP value
+ // here we recycle variable index, and do this calc apart to avoid mixing up the 30% bonus with card bonuses. [Skotlex]
+ bl = sd->status.base_level;
+ index = (3500 + bl*hp_coefficient2[sd->status.class_] +
+ hp_sigma_val[sd->status.class_][(bl > 0)? bl-1:0])/100 *
+ (100 + sd->paramc[2])/100 + (sd->parame[2] - sd->paramcard[2]);
+ if (sd->class_&JOBL_UPPER)
+ index += index * 30/100;
+ else if (sd->class_&JOBL_BABY)
+ index -= index * 30/100;
+ if ((sd->class_&MAPID_UPPERMASK) == MAPID_TAEKWON && sd->status.base_level >= 90 && pc_istop10fame(sd->char_id, MAPID_TAEKWON))
+ index *= 3; //Triple max HP for top ranking Taekwons over level 90.
+
+ sd->status.max_hp += index;
+
+ if((sd->class_&MAPID_UPPERMASK) == MAPID_SUPER_NOVICE && sd->status.base_level >= 99)
+ sd->status.max_hp = sd->status.max_hp + 2000;
+
+ // Absolute modifiers from passive skills
+ if((skill=pc_checkskill(sd,CR_TRUST))>0)
+ sd->status.max_hp += skill*200;
+
+ // Absolute, then relative modifiers from status changes (shared between PC and NPC)
+ sd->status.max_hp = status_calc_maxhp(&sd->bl,sd->status.max_hp);
+
+ // Apply relative modifiers from equipment
+ if(sd->hprate!=100)
+ sd->status.max_hp = sd->status.max_hp * sd->hprate/100;
+
+ if(sd->status.max_hp > battle_config.max_hp)
+ sd->status.max_hp = battle_config.max_hp;
+ if(sd->status.hp>sd->status.max_hp)
+ sd->status.hp=sd->status.max_hp;
+ if(sd->status.max_hp <= 0) sd->status.max_hp = 1;
+
+ // Basic natural HP regeneration value
+ sd->nhealhp = 1 + (sd->paramc[2]/5) + (sd->status.max_hp/200);
+
+ // Apply relative modifiers from equipment
+ if(sd->hprecov_rate != 100)
+ sd->nhealhp = sd->nhealhp*sd->hprecov_rate/100;
+
+ if(sd->nhealhp < 1) sd->nhealhp = 1;
+ if(sd->nhealhp > 0x7fff) sd->nhealhp = 0x7fff;
+
+ // Skill-related HP recovery
+ if((skill=pc_checkskill(sd,SM_RECOVERY)) > 0)
+ sd->nshealhp = skill*5 + (sd->status.max_hp*skill/500);
+ // Skill-related HP recovery (only when sit)
+ if((skill=pc_checkskill(sd,MO_SPIRITSRECOVERY)) > 0)
+ sd->nsshealhp = skill*4 + (sd->status.max_hp*skill/500);
+ if((skill=pc_checkskill(sd,TK_HPTIME)) > 0 && sd->state.rest == 1)
+ sd->nsshealhp = skill*30 + (sd->status.max_hp*skill/500);
+
+ if(sd->nshealhp > 0x7fff) sd->nshealhp = 0x7fff;
+ if(sd->nsshealhp > 0x7fff) sd->nsshealhp = 0x7fff;
+
+// ----- SP MAX AND REGEN CALCULATION -----
+
+ // Basic MaxSP value
+ // here we recycle variable index, and do this calc apart to avoid mixing up the 30% bonus with card bonuses. [Skotlex]
+ index = ((sp_coefficient[sd->status.class_] * bl) + 1000)/100 * (100 + sd->paramc[3])/100 + (sd->parame[3] - sd->paramcard[3]);
+ if (sd->class_&JOBL_UPPER)
+ index += index * 30/100;
+ else if (sd->class_&JOBL_BABY)
+ index -= index * 30/100;
+ if ((sd->class_&MAPID_UPPERMASK) == MAPID_TAEKWON && sd->status.base_level >= 90 && pc_istop10fame(sd->char_id, MAPID_TAEKWON))
+ index *= 3; //Triple max SP for top ranking Taekwons over level 90.
+
+ sd->status.max_sp += index;
+
+ // Absolute modifiers from passive skills
+ if((skill=pc_checkskill(sd,SL_KAINA))>0)
+ sd->status.max_sp += 30*skill;
+ if((skill=pc_checkskill(sd,HP_MEDITATIO))>0)
+ sd->status.max_sp += sd->status.max_sp * skill/100;
+ if((skill=pc_checkskill(sd,HW_SOULDRAIN))>0)
+ sd->status.max_sp += sd->status.max_sp * 2*skill/100;
+
+ // Absolute, then relative modifiers from status changes (shared between PC and NPC)
+ sd->status.max_sp = status_calc_maxsp(&sd->bl,sd->status.max_sp);
+
+ // Apply relative modifiers from equipment
+ if(sd->sprate!=100)
+ sd->status.max_sp = sd->status.max_sp * sd->sprate/100;
+
+ if(sd->status.max_sp > battle_config.max_sp)
+ sd->status.max_sp = battle_config.max_sp;
+ if(sd->status.sp>sd->status.max_sp)
+ sd->status.sp=sd->status.max_sp;
+ if(sd->status.max_sp <= 0) sd->status.max_sp = 1;
+
+ if(sd->sc_data[SC_DANCING].timer==-1){
+ // Basic natural SP regeneration value
+ sd->nhealsp = 1 + (sd->paramc[3]/6) + (sd->status.max_sp/100);
+ if(sd->paramc[3] >= 120)
+ sd->nhealsp += ((sd->paramc[3]-120)>>1) + 4;
+
+ // Relative modifiers from passive skills
+ if((skill=pc_checkskill(sd,HP_MEDITATIO)) > 0)
+ sd->nhealsp += sd->nhealsp * 3*skill/100;
+
+ // Apply relative modifiers from equipment
+ if(sd->sprecov_rate != 100)
+ sd->nhealsp = sd->nhealsp*sd->sprecov_rate/100;
+
+ if(sd->nhealsp < 1) sd->nhealsp = 1;
+ if(sd->nhealsp > 0x7fff) sd->nhealsp = 0x7fff;
+
+ // Skill-related SP recovery
+ if((skill=pc_checkskill(sd,MG_SRECOVERY)) > 0)
+ sd->nshealsp = skill*3 + (sd->status.max_sp*skill/500);
+ // Skill-related SP recovery (only when sit)
+ if((skill = pc_checkskill(sd,MO_SPIRITSRECOVERY)) > 0)
+ sd->nsshealsp = skill*2 + (sd->status.max_sp*skill/500);
+ if((skill=pc_checkskill(sd,TK_SPTIME)) > 0 && sd->state.rest == 1) {
+ sd->nsshealsp = skill*3 + (sd->status.max_sp*skill/500);
+ if ((skill=pc_checkskill(sd,SL_KAINA)) > 0) //Power up Enjoyable Rest
+ sd->nsshealsp += (30+10*skill)*sd->nsshealsp/100;
+ }
+ if(sd->nshealsp > 0x7fff) sd->nshealsp = 0x7fff;
+ if(sd->nsshealsp > 0x7fff) sd->nsshealsp = 0x7fff;
+ }
+
+// ----- MISC CALCULATIONS -----
+
+ //Even though people insist this is too slow, packet data reports this is the actual real equation.
+ sd->dmotion = 800-sd->paramc[1]*4;
+ if(sd->dmotion<400) sd->dmotion = 400;
+
+ // Weight
+ if((skill=pc_checkskill(sd,MC_INCCARRY))>0)
+ sd->max_weight += 2000*skill;
+ if(pc_isriding(sd) && pc_checkskill(sd,KN_RIDING)>0)
+ sd->max_weight += 10000;
+ if( (skill=pc_checkskill(sd,SG_KNOWLEDGE))>0) //SG skill [Komurka]
+ if(sd->bl.m == sd->feel_map[0].m || sd->bl.m == sd->feel_map[1].m || sd->bl.m == sd->feel_map[2].m)
+ sd->max_weight += sd->max_weight*skill/10;
+
+ // Skill SP cost
+ if((skill=pc_checkskill(sd,HP_MANARECHARGE))>0 )
+ sd->dsprate -= 4*skill;
+
+ if(sd->sc_count){
+ if(sd->sc_data[SC_SERVICE4U].timer!=-1)
+ sd->dsprate -= sd->sc_data[SC_SERVICE4U].val3;
+ }
+
+ if(sd->dsprate < 0) sd->dsprate = 0;
+
+ // Anti-element and anti-race
+ if((skill=pc_checkskill(sd,CR_TRUST))>0)
+ sd->subele[6] += skill*5;
+ if((skill=pc_checkskill(sd,BS_SKINTEMPER))>0) {
+ sd->subele[0] += skill;
+ sd->subele[3] += skill*4;
+ }
+ if((skill=pc_checkskill(sd,SA_DRAGONOLOGY))>0 ){
+ skill = skill*4;
+ sd->right_weapon.addrace[9]+=skill;
+ sd->left_weapon.addrace[9]+=skill;
+ sd->magic_addrace[9]+=skill;
+ sd->subrace[9]+=skill;
+ }
+
+ if(sd->sc_count){
+ if(sd->sc_data[SC_SIEGFRIED].timer!=-1){
+ sd->subele[1] += sd->sc_data[SC_SIEGFRIED].val2;
+ sd->subele[2] += sd->sc_data[SC_SIEGFRIED].val2;
+ sd->subele[3] += sd->sc_data[SC_SIEGFRIED].val2;
+ sd->subele[4] += sd->sc_data[SC_SIEGFRIED].val2;
+ sd->subele[5] += sd->sc_data[SC_SIEGFRIED].val2;
+ sd->subele[6] += sd->sc_data[SC_SIEGFRIED].val2;
+ sd->subele[7] += sd->sc_data[SC_SIEGFRIED].val2;
+ sd->subele[8] += sd->sc_data[SC_SIEGFRIED].val2;
+ sd->subele[9] += sd->sc_data[SC_SIEGFRIED].val2;
+ }
+ if(sd->sc_data[SC_PROVIDENCE].timer!=-1){
+ sd->subele[6] += sd->sc_data[SC_PROVIDENCE].val2;
+ sd->subrace[6] += sd->sc_data[SC_PROVIDENCE].val2;
+ }
+ }
+
+// ----- CLIENT-SIDE REFRESH -----
+ if(first&1) { //Since this is the initial loading, the Falcon and Peco icons must be loaded. [Skotlex]
+ if (sd->status.option&OPTION_FALCON)
+ clif_status_load(&sd->bl, SI_FALCON, 1);
+ if (sd->status.option&OPTION_RIDING)
+ clif_status_load(&sd->bl, SI_RIDING, 1);
+ }
+ if(first&4) {
+ calculating = 0;
+ return 0;
+ }
+ if(first&3) {
+ clif_updatestatus(sd,SP_SPEED);
+ clif_updatestatus(sd,SP_MAXHP);
+ clif_updatestatus(sd,SP_MAXSP);
+ if(first&1) {
+ clif_updatestatus(sd,SP_HP);
+ clif_updatestatus(sd,SP_SP);
+ }
+ calculating = 0;
+ return 0;
+ }
+
+
+ if(sd->sc_data[SC_WEDDING].timer != -1 && sd->view_class != JOB_WEDDING)
+ sd->view_class=JOB_WEDDING;
+
+ if(sd->sc_data[SC_XMAS].timer != -1 && sd->view_class != JOB_XMAS)
+ sd->view_class=JOB_XMAS;
+
+ if(b_class != sd->view_class) {
+ clif_changelook(&sd->bl,LOOK_BASE,sd->view_class);
+#if PACKETVER < 4
+ clif_changelook(&sd->bl,LOOK_WEAPON,sd->status.weapon);
+ clif_changelook(&sd->bl,LOOK_SHIELD,sd->status.shield);
+#else
+ clif_changelook(&sd->bl,LOOK_WEAPON,0);
+#endif
+ //Restoring cloth dye color after the view class changes. [Skotlex]
+ // Added Xmas Suit [Valaris]
+ if(battle_config.save_clothcolor && sd->status.clothes_color > 0 &&
+ ((sd->view_class != JOB_WEDDING && sd->view_class !=JOB_XMAS) || (sd->view_class==JOB_WEDDING && !battle_config.wedding_ignorepalette) ||
+ (sd->view_class==JOB_XMAS && !battle_config.xmas_ignorepalette)))
+ clif_changelook(&sd->bl,LOOK_CLOTHES_COLOR,sd->status.clothes_color);
+ }
+
+ if( memcmp(b_skill,sd->status.skill,sizeof(sd->status.skill)) || b_attackrange != sd->attackrange)
+ clif_skillinfoblock(sd);
+
+ if(b_speed != sd->speed)
+ clif_updatestatus(sd,SP_SPEED);
+ if(b_weight != sd->weight)
+ clif_updatestatus(sd,SP_WEIGHT);
+ if(b_max_weight != sd->max_weight) {
+ clif_updatestatus(sd,SP_MAXWEIGHT);
+ pc_checkweighticon(sd);
+ }
+ for(i=0;i<6;i++)
+ if(b_paramb[i] + b_parame[i] != sd->paramb[i] + sd->parame[i])
+ clif_updatestatus(sd,SP_STR+i);
+ if(b_hit != sd->hit)
+ clif_updatestatus(sd,SP_HIT);
+ if(b_flee != sd->flee)
+ clif_updatestatus(sd,SP_FLEE1);
+ if(b_aspd != sd->aspd)
+ clif_updatestatus(sd,SP_ASPD);
+ if(b_watk != sd->right_weapon.watk || b_base_atk != sd->base_atk)
+ clif_updatestatus(sd,SP_ATK1);
+ if(b_def != sd->def)
+ clif_updatestatus(sd,SP_DEF1);
+ if(b_watk2 != sd->right_weapon.watk2)
+ clif_updatestatus(sd,SP_ATK2);
+ if(b_def2 != sd->def2)
+ clif_updatestatus(sd,SP_DEF2);
+ if(b_flee2 != sd->flee2)
+ clif_updatestatus(sd,SP_FLEE2);
+ if(b_critical != sd->critical)
+ clif_updatestatus(sd,SP_CRITICAL);
+ if(b_matk1 != sd->matk1)
+ clif_updatestatus(sd,SP_MATK1);
+ if(b_matk2 != sd->matk2)
+ clif_updatestatus(sd,SP_MATK2);
+ if(b_mdef != sd->mdef)
+ clif_updatestatus(sd,SP_MDEF1);
+ if(b_mdef2 != sd->mdef2)
+ clif_updatestatus(sd,SP_MDEF2);
+ if(b_attackrange != sd->attackrange)
+ clif_updatestatus(sd,SP_ATTACKRANGE);
+ if(b_max_hp != sd->status.max_hp)
+ clif_updatestatus(sd,SP_MAXHP);
+ if(b_max_sp != sd->status.max_sp)
+ clif_updatestatus(sd,SP_MAXSP);
+ if(b_hp != sd->status.hp)
+ clif_updatestatus(sd,SP_HP);
+ if(b_sp != sd->status.sp)
+ clif_updatestatus(sd,SP_SP);
+
+ /* I don't think there's a need for this here. It should be handled in pc_damage and pc_heal. [Skotlex]
+ if(sd->status.hp<sd->status.max_hp>>2 && sd->sc_data[SC_AUTOBERSERK].timer!=-1 &&
+ (sd->sc_data[SC_PROVOKE].timer==-1 || sd->sc_data[SC_PROVOKE].val2==0) && !pc_isdead(sd))
+ status_change_start(&sd->bl,SC_PROVOKE,10,1,0,0,0,0);
+ */
+ calculating = 0;
+ return 0;
+}
+
+/*==========================================
+ * Apply shared stat mods from status changes [DracoRPG]
+ *------------------------------------------
+ */
+int status_calc_str(struct block_list *bl, int str)
+{
+ struct status_change *sc_data;
+ nullpo_retr(str,bl);
+ sc_data = status_get_sc_data(bl);
+
+ if(sc_data){
+ if(sc_data[SC_INCALLSTATUS].timer!=-1)
+ str += sc_data[SC_INCALLSTATUS].val1;
+ if(sc_data[SC_INCSTR].timer!=-1)
+ str += sc_data[SC_INCSTR].val1;
+ if(sc_data[SC_STRFOOD].timer!=-1)
+ str += sc_data[SC_STRFOOD].val1;
+ if(sc_data[SC_LOUD].timer!=-1)
+ str += 4;
+ if(sc_data[SC_TRUESIGHT].timer!=-1)
+ str += 5;
+ if(sc_data[SC_SPURT].timer!=-1)
+ str += 10; //Bonus is +!0 regardless of skill level
+ if(sc_data[SC_BLESSING].timer != -1){
+ int race = status_get_race(bl);
+ if(battle_check_undead(race,status_get_elem_type(bl)) || race == 6)
+ str >>= 1;
+ else str += sc_data[SC_BLESSING].val1;
+ }
+ }
+
+ return str;
+}
+
+int status_calc_agi(struct block_list *bl, int agi)
+{
+ struct status_change *sc_data;
+ nullpo_retr(agi,bl);
+ sc_data = status_get_sc_data(bl);
+
+ if(sc_data){
+ if(sc_data[SC_INCALLSTATUS].timer!=-1)
+ agi += sc_data[SC_INCALLSTATUS].val1;
+ if(sc_data[SC_INCAGI].timer!=-1)
+ agi += sc_data[SC_INCAGI].val1;
+ if(sc_data[SC_AGIFOOD].timer!=-1)
+ agi += sc_data[SC_AGIFOOD].val1;
+ if(sc_data[SC_TRUESIGHT].timer!=-1)
+ agi += 5;
+ if(sc_data[SC_INCREASEAGI].timer!=-1)
+ agi += 2 + sc_data[SC_INCREASEAGI].val1;
+ if(sc_data[SC_DECREASEAGI].timer!=-1)
+ agi -= 2 + sc_data[SC_DECREASEAGI].val1;
+ if(sc_data[SC_QUAGMIRE].timer!=-1)
+ agi -= sc_data[SC_QUAGMIRE].val1*(bl->type==BL_PC?5:10);
+ }
+
+ return agi;
+}
+
+int status_calc_vit(struct block_list *bl, int vit)
+{
+ struct status_change *sc_data;
+ nullpo_retr(vit,bl);
+ sc_data = status_get_sc_data(bl);
+
+ if(sc_data){
+ if(sc_data[SC_INCALLSTATUS].timer!=-1)
+ vit += sc_data[SC_INCALLSTATUS].val1;
+ if(sc_data[SC_INCVIT].timer!=-1)
+ vit += sc_data[SC_INCVIT].val1;
+ if(sc_data[SC_VITFOOD].timer!=-1)
+ vit += sc_data[SC_VITFOOD].val1;
+ if(sc_data[SC_TRUESIGHT].timer!=-1)
+ vit += 5;
+ if(sc_data[SC_STRIPARMOR].timer!=-1 && bl->type != BL_PC)
+ vit -= vit * 8*sc_data[SC_STRIPARMOR].val1/100;
+ }
+
+ return vit;
+}
+
+int status_calc_int(struct block_list *bl, int int_)
+{
+ struct status_change *sc_data;
+ nullpo_retr(int_,bl);
+ sc_data = status_get_sc_data(bl);
+
+ if(sc_data){
+ if(sc_data[SC_INCALLSTATUS].timer!=-1)
+ int_ += sc_data[SC_INCALLSTATUS].val1;
+ if(sc_data[SC_INCINT].timer!=-1)
+ int_ += sc_data[SC_INCINT].val1;
+ if(sc_data[SC_INTFOOD].timer!=-1)
+ int_ += sc_data[SC_INTFOOD].val1;
+ if(sc_data[SC_TRUESIGHT].timer!=-1)
+ int_ += 5;
+ if(sc_data[SC_BLESSING].timer != -1){
+ int race = status_get_race(bl);
+ if(battle_check_undead(race,status_get_elem_type(bl)) || race == 6)
+ int_ >>= 1;
+ else int_ += sc_data[SC_BLESSING].val1;
+ }
+ if(sc_data[SC_STRIPHELM].timer!=-1 && bl->type != BL_PC)
+ int_ -= int_ * 8*sc_data[SC_STRIPHELM].val1/100;
+ }
+
+ return int_;
+}
+
+int status_calc_dex(struct block_list *bl, int dex)
+{
+ struct status_change *sc_data;
+ nullpo_retr(dex,bl);
+ sc_data = status_get_sc_data(bl);
+
+ if(sc_data){
+ if(sc_data[SC_INCALLSTATUS].timer!=-1)
+ dex += sc_data[SC_INCALLSTATUS].val1;
+ if(sc_data[SC_INCDEX].timer!=-1)
+ dex += sc_data[SC_INCDEX].val1;
+ if(sc_data[SC_DEXFOOD].timer!=-1)
+ dex += sc_data[SC_DEXFOOD].val1;
+ if(sc_data[SC_TRUESIGHT].timer!=-1)
+ dex += 5;
+ if(sc_data[SC_QUAGMIRE].timer!=-1)
+ dex -= sc_data[SC_QUAGMIRE].val1*(bl->type==BL_PC?5:10);
+ if(sc_data[SC_BLESSING].timer != -1){
+ int race = status_get_race(bl);
+ if(battle_check_undead(race,status_get_elem_type(bl)) || race == 6)
+ dex >>= 1;
+ else dex += sc_data[SC_BLESSING].val1;
+ }
+ }
+
+ return dex;
+}
+
+int status_calc_luk(struct block_list *bl, int luk)
+{
+ struct status_change *sc_data;
+ nullpo_retr(luk,bl);
+ sc_data = status_get_sc_data(bl);
+
+ if(sc_data){
+ if(sc_data[SC_INCALLSTATUS].timer!=-1)
+ luk += sc_data[SC_INCALLSTATUS].val1;
+ if(sc_data[SC_INCLUK].timer!=-1)
+ luk += sc_data[SC_INCLUK].val1;
+ if(sc_data[SC_LUKFOOD].timer!=-1)
+ luk += sc_data[SC_LUKFOOD].val1;
+ if(sc_data[SC_TRUESIGHT].timer!=-1)
+ luk += 5;
+ if(sc_data[SC_GLORIA].timer!=-1)
+ luk += 30;
+ }
+
+ return luk;
+}
+
+int status_calc_batk(struct block_list *bl, int batk)
+{
+ struct status_change *sc_data;
+ nullpo_retr(batk,bl);
+ sc_data = status_get_sc_data(bl);
+
+ if(sc_data){
+ if(sc_data[SC_ATKPOTION].timer!=-1)
+ batk += sc_data[SC_ATKPOTION].val1;
+ if(sc_data[SC_BATKFOOD].timer!=-1)
+ batk += sc_data[SC_BATKFOOD].val1;
+ if(sc_data[SC_INCATKRATE].timer!=-1)
+ batk += batk * sc_data[SC_INCATKRATE].val1/100;
+ if(sc_data[SC_PROVOKE].timer!=-1)
+ batk += batk * (2+3*sc_data[SC_PROVOKE].val1)/100;
+ if(sc_data[SC_CONCENTRATION].timer!=-1)
+ batk += batk * 5*sc_data[SC_CONCENTRATION].val1/100;
+ if(sc_data[SC_SKE].timer!=-1)
+ batk += batk * 3;
+ if(sc_data[SC_JOINTBEAT].timer!=-1 && sc_data[SC_JOINTBEAT].val2==4)
+ batk -= batk * 25/100;
+ if(sc_data[SC_CURSE].timer!=-1)
+ batk -= batk * 25/100;
+ }
+
+ return batk;
+}
+
+int status_calc_watk(struct block_list *bl, int watk)
+{
+ struct status_change *sc_data;
+ nullpo_retr(watk,bl);
+ sc_data = status_get_sc_data(bl);
+
+ if(sc_data){
+ if(sc_data[SC_IMPOSITIO].timer!=-1)
+ watk += 5*sc_data[SC_IMPOSITIO].val1;
+ if(sc_data[SC_WATKFOOD].timer!=-1)
+ watk += sc_data[SC_WATKFOOD].val1;
+ if(sc_data[SC_DRUMBATTLE].timer!=-1)
+ watk += sc_data[SC_DRUMBATTLE].val2;
+ if(sc_data[SC_VOLCANO].timer!=-1 && status_get_elem_type(bl)==3)
+ watk += sc_data[SC_VOLCANO].val3;
+ if(sc_data[SC_INCATKRATE].timer!=-1)
+ watk += watk * sc_data[SC_INCATKRATE].val1/100;
+ if(sc_data[SC_PROVOKE].timer!=-1)
+ watk += watk * (2+3*sc_data[SC_PROVOKE].val1)/100;
+ if(sc_data[SC_CONCENTRATION].timer!=-1)
+ watk += watk * 5*sc_data[SC_CONCENTRATION].val1/100;
+ if(sc_data[SC_SKE].timer!=-1)
+ watk += watk * 3;
+ if(sc_data[SC_NIBELUNGEN].timer!=-1 && bl->type != BL_PC && (status_get_element(bl)/10)>=8)
+ watk += sc_data[SC_NIBELUNGEN].val2;
+ if(sc_data[SC_EXPLOSIONSPIRITS].timer!=-1 && bl->type != BL_PC)
+ watk += (1000*sc_data[SC_EXPLOSIONSPIRITS].val1);
+ if(sc_data[SC_CURSE].timer!=-1)
+ watk -= watk * 25/100;
+ if(sc_data[SC_STRIPWEAPON].timer!=-1 && bl->type != BL_PC)
+ watk -= watk * 5*sc_data[SC_STRIPWEAPON].val1/100;
+ }
+ return watk;
+}
+
+int status_calc_matk(struct block_list *bl, int matk)
+{
+ struct status_change *sc_data;
+ nullpo_retr(matk,bl);
+ sc_data = status_get_sc_data(bl);
+
+ if(sc_data){
+ if(sc_data[SC_MATKPOTION].timer!=-1)
+ matk += sc_data[SC_MATKPOTION].val1;
+ if(sc_data[SC_MATKFOOD].timer!=-1)
+ matk += sc_data[SC_MATKFOOD].val1;
+ if(sc_data[SC_MAGICPOWER].timer!=-1)
+ matk += matk * 5*sc_data[SC_MAGICPOWER].val1/100;
+ if(sc_data[SC_MINDBREAKER].timer!=-1)
+ matk += matk * 20*sc_data[SC_MINDBREAKER].val1/100;
+ if(sc_data[SC_INCMATKRATE].timer!=-1)
+ matk += matk * sc_data[SC_INCMATKRATE].val1/100;
+ }
+
+ return matk;
+}
+
+int status_calc_critical(struct block_list *bl, int critical)
+{
+ struct status_change *sc_data;
+ nullpo_retr(critical,bl);
+ sc_data = status_get_sc_data(bl);
+
+ if(sc_data){
+ if (sc_data[SC_EXPLOSIONSPIRITS].timer!=-1)
+ critical += sc_data[SC_EXPLOSIONSPIRITS].val2;
+ if (sc_data[SC_FORTUNE].timer!=-1)
+ critical += sc_data[SC_FORTUNE].val2*10;
+ if (sc_data[SC_TRUESIGHT].timer!=-1)
+ critical += sc_data[SC_TRUESIGHT].val1*10;
+ if(sc_data[SC_CLOAKING].timer!=-1)
+ critical += critical;
+ }
+
+ return critical;
+}
+
+int status_calc_hit(struct block_list *bl, int hit)
+{
+ struct status_change *sc_data;
+ nullpo_retr(hit,bl);
+ sc_data = status_get_sc_data(bl);
+
+ if(sc_data){
+ if(sc_data[SC_INCHIT].timer != -1)
+ hit += sc_data[SC_INCHIT].val1;
+ if(sc_data[SC_HITFOOD].timer!=-1)
+ hit += sc_data[SC_HITFOOD].val1;
+ if(sc_data[SC_TRUESIGHT].timer != -1)
+ hit += 3*sc_data[SC_TRUESIGHT].val1;
+ if(sc_data[SC_HUMMING].timer!=-1)
+ hit += sc_data[SC_HUMMING].val2;
+ if(sc_data[SC_CONCENTRATION].timer != -1)
+ hit += 10*sc_data[SC_CONCENTRATION].val1;
+ if(sc_data[SC_INCHITRATE].timer != -1)
+ hit += hit * sc_data[SC_INCHITRATE].val1/100;
+ if(sc_data[SC_BLIND].timer != -1)
+ hit -= hit * 25 / 100;
+ }
+
+ return hit;
+}
+
+int status_calc_flee(struct block_list *bl, int flee)
+{
+ struct status_change *sc_data;
+ nullpo_retr(flee,bl);
+ sc_data = status_get_sc_data(bl);
+
+ if(sc_data){
+ if(sc_data[SC_INCFLEE].timer!=-1)
+ flee += sc_data[SC_INCFLEE].val1;
+ if(sc_data[SC_FLEEFOOD].timer!=-1)
+ flee += sc_data[SC_FLEEFOOD].val1;
+ if(sc_data[SC_WHISTLE].timer!=-1)
+ flee += sc_data[SC_WHISTLE].val2;
+ if(sc_data[SC_WINDWALK].timer!=-1)
+ flee += flee * sc_data[SC_WINDWALK].val2/100;
+ if(sc_data[SC_INCFLEERATE].timer!=-1)
+ flee += flee * sc_data[SC_INCFLEERATE].val1/100;
+ if(sc_data[SC_VIOLENTGALE].timer!=-1 && status_get_elem_type(bl)==4)
+ flee += flee * sc_data[SC_VIOLENTGALE].val3/100;
+ if(sc_data[SC_MOON_COMFORT].timer!=-1) //SG skill [Komurka]
+ flee += (status_get_lv(bl) + status_get_dex(bl) + status_get_luk(bl))/10;
+ if(sc_data[SC_CLOSECONFINE].timer!=-1)
+ flee += 10;
+ if(sc_data[SC_SPIDERWEB].timer!=-1)
+ flee -= flee * 50/100;
+ if(sc_data[SC_BERSERK].timer!=-1)
+ flee -= flee * 50/100;
+ if(sc_data[SC_BLIND].timer!=-1)
+ flee -= flee * 25/100;
+ }
+
+ if (bl->type == BL_PC && map_flag_gvg(bl->m)) //GVG grounds flee penalty, placed here because it's "like" a status change. [Skotlex]
+ flee -= flee * battle_config.gvg_flee_penalty/100;
+ return flee;
+}
+
+int status_calc_flee2(struct block_list *bl, int flee2)
+{
+ struct status_change *sc_data;
+ nullpo_retr(flee2,bl);
+ sc_data = status_get_sc_data(bl);
+
+ if(sc_data){
+ if(sc_data[SC_WHISTLE].timer!=-1)
+ flee2 += sc_data[SC_WHISTLE].val3*10;
+ }
+
+ return flee2;
+}
+
+int status_calc_def(struct block_list *bl, int def)
+{
+ struct status_change *sc_data;
+ nullpo_retr(def,bl);
+ sc_data = status_get_sc_data(bl);
+
+ if(sc_data){
+ if(sc_data[SC_BERSERK].timer!=-1)
+ return 0;
+ if(sc_data[SC_KEEPING].timer!=-1)
+ return 100;
+ if(sc_data[SC_STEELBODY].timer!=-1)
+ return 90;
+ if(sc_data[SC_SKA].timer != -1) // [marquis007]
+ return 90;
+ if(sc_data[SC_DRUMBATTLE].timer!=-1)
+ def += sc_data[SC_DRUMBATTLE].val3;
+ if(sc_data[SC_INCDEFRATE].timer!=-1)
+ def += def * sc_data[SC_INCDEFRATE].val1/100;
+ if(sc_data[SC_SIGNUMCRUCIS].timer!=-1)
+ def -= def * sc_data[SC_SIGNUMCRUCIS].val2/100;
+ if(sc_data[SC_CONCENTRATION].timer!=-1)
+ def -= def * 5*sc_data[SC_CONCENTRATION].val1/100;
+ if(sc_data[SC_SKE].timer!=-1)
+ def -= def * 50/100;
+ if(sc_data[SC_PROVOKE].timer!=-1 && bl->type != BL_PC) // Provoke doesn't alter player defense.
+ def -= def * (5+5*sc_data[SC_PROVOKE].val1)/100;
+ if(sc_data[SC_STRIPSHIELD].timer!=-1 && bl->type != BL_PC)
+ def -= def * 3*sc_data[SC_STRIPSHIELD].val1/100;
+ }
+
+ return def;
+}
+
+int status_calc_def2(struct block_list *bl, int def2)
+{
+ struct status_change *sc_data;
+ nullpo_retr(def2,bl);
+ sc_data = status_get_sc_data(bl);
+
+ if(sc_data){
+ if(sc_data[SC_BERSERK].timer!=-1)
+ return 0;
+ if(sc_data[SC_ETERNALCHAOS].timer!=-1)
+ return 0;
+ if(sc_data[SC_SUN_COMFORT].timer!=-1)
+ def2 += (status_get_lv(bl) + status_get_dex(bl) + status_get_luk(bl))/2;
+ if(sc_data[SC_ANGELUS].timer!=-1)
+ def2 += def2 * (10+5*sc_data[SC_ANGELUS].val1)/100;
+ if(sc_data[SC_CONCENTRATION].timer!=-1)
+ def2 -= def2 * 5*sc_data[SC_CONCENTRATION].val1/100;
+ if(sc_data[SC_POISON].timer!=-1)
+ def2 -= def2 * 25/100;
+ if(sc_data[SC_SKE].timer!=-1)
+ def2 -= def2 * 50/100;
+ if(sc_data[SC_PROVOKE].timer!=-1)
+ def2 -= def2 * (5+5*sc_data[SC_PROVOKE].val1)/100;
+ if(sc_data[SC_SKE].timer!=-1)
+ def2 /= 2;
+ if(sc_data[SC_JOINTBEAT].timer!=-1){
+ if(sc_data[SC_JOINTBEAT].val2==3)
+ def2 -= def2 * 50/100;
+ else if(sc_data[SC_JOINTBEAT].val2==4)
+ def2 -= def2 * 25/100;
+ }
+ }
+
+ return def2;
+}
+
+int status_calc_mdef(struct block_list *bl, int mdef)
+{
+ struct status_change *sc_data;
+ nullpo_retr(mdef,bl);
+ sc_data = status_get_sc_data(bl);
+
+ if(sc_data){
+ if(sc_data[SC_BERSERK].timer!=-1)
+ return 0;
+ if(sc_data[SC_BARRIER].timer!=-1)
+ return 100;
+ if(sc_data[SC_STEELBODY].timer!=-1)
+ return 90;
+ if(sc_data[SC_SKA].timer != -1) // [marquis007]
+ return 90; // should it up mdef too?
+ if(sc_data[SC_ENDURE].timer!=-1)
+ mdef += sc_data[SC_ENDURE].val1;
+ }
+
+ return mdef;
+}
+
+int status_calc_mdef2(struct block_list *bl, int mdef2)
+{
+ struct status_change *sc_data;
+ nullpo_retr(mdef2,bl);
+ sc_data = status_get_sc_data(bl);
+
+ if(sc_data){
+ if(sc_data[SC_BERSERK].timer!=-1)
+ return 0;
+ if(sc_data[SC_MINDBREAKER].timer!=-1)
+ mdef2 -= mdef2 * 12*sc_data[SC_MINDBREAKER].val1/100;
+ }
+
+ return mdef2;
+}
+
+int status_calc_speed(struct block_list *bl, int speed)
+{
+ struct status_change *sc_data;
+ sc_data = status_get_sc_data(bl);
+
+ if(sc_data) {
+ if(sc_data[SC_CURSE].timer!=-1)
+ speed += 450;
+ if(sc_data[SC_SWOO].timer != -1) // [marquis007]
+ speed += 450; //Let's use Curse's slow down momentarily (exact value unknown)
+ if(sc_data[SC_SPEEDUP1].timer!=-1)
+ speed -= speed*50/100;
+ else if(sc_data[SC_SPEEDUP0].timer!=-1)
+ speed -= speed*25/100;
+ else if(sc_data[SC_INCREASEAGI].timer!=-1)
+ speed -= speed * 25/100;
+ else if(sc_data[SC_CARTBOOST].timer!=-1)
+ speed -= speed * 20/100;
+ else if(sc_data[SC_BERSERK].timer!=-1)
+ speed -= speed * 20/100;
+ else if(sc_data[SC_WINDWALK].timer!=-1)
+ speed -= speed * 4*sc_data[SC_WINDWALK].val2/100;
+ if(sc_data[SC_WEDDING].timer!=-1)
+ speed += speed * 50/100;
+ if(sc_data[SC_SLOWDOWN].timer!=-1)
+ speed += speed * 50/100;
+ if(sc_data[SC_DECREASEAGI].timer!=-1)
+ speed += speed * 25/100;
+ if(sc_data[SC_STEELBODY].timer!=-1)
+ speed += speed * 25/100;
+ if(sc_data[SC_SKA].timer!=-1)
+ speed += speed * 25/100;
+ if(sc_data[SC_QUAGMIRE].timer!=-1)
+ speed += speed * 50/100;
+ if(sc_data[SC_DONTFORGETME].timer!=-1)
+ speed += speed * sc_data[SC_DONTFORGETME].val3/100;
+ if(sc_data[SC_DEFENDER].timer!=-1)
+ speed += speed * (55-5*sc_data[SC_DEFENDER].val1)/100;
+ if(sc_data[SC_GOSPEL].timer!=-1 && sc_data[SC_GOSPEL].val4 == BCT_ENEMY)
+ speed += speed * 25/100;
+ if(sc_data[SC_JOINTBEAT].timer!=-1) {
+ if (sc_data[SC_JOINTBEAT].val2 == 0)
+ speed += speed * 50/100;
+ else if (sc_data[SC_JOINTBEAT].val2 == 2)
+ speed += speed * 30/100;
+ }
+ if(sc_data[SC_CLOAKING].timer!=-1)
+ speed = speed * (sc_data[SC_CLOAKING].val3-3*sc_data[SC_CLOAKING].val1) /100;
+ if(sc_data[SC_CHASEWALK].timer!=-1)
+ speed = speed * sc_data[SC_CHASEWALK].val3/100;
+ if(sc_data[SC_RUN].timer!=-1)/*駆け足による速度変化*/
+ speed -= speed * 25/100;
+
+ }
+
+ return speed;
+}
+
+int status_calc_aspd_rate(struct block_list *bl, int aspd_rate)
+{
+ struct status_change *sc_data;
+ sc_data = status_get_sc_data(bl);
+
+ if(sc_data) {
+ int i;
+ if(sc_data[SC_QUAGMIRE].timer==-1 && sc_data[SC_DONTFORGETME].timer==-1){
+ if(sc_data[SC_TWOHANDQUICKEN].timer!=-1)
+ aspd_rate -= 30;
+ else if(sc_data[SC_ONEHAND].timer!=-1)
+ aspd_rate -= 30;
+ else if(sc_data[SC_ADRENALINE2].timer!=-1)
+ aspd_rate -= (sc_data[SC_ADRENALINE2].val2 || !battle_config.party_skill_penalty)?30:20;
+ else if(sc_data[SC_ADRENALINE].timer!=-1)
+ aspd_rate -= (sc_data[SC_ADRENALINE].val2 || !battle_config.party_skill_penalty)?30:20;
+ else if(sc_data[SC_SPEARSQUICKEN].timer!=-1)
+ aspd_rate -= sc_data[SC_SPEARSQUICKEN].val2;
+ else if(sc_data[SC_ASSNCROS].timer!=-1 && (bl->type!=BL_PC || ((struct map_session_data*)bl)->status.weapon != 11))
+ aspd_rate -= sc_data[SC_ASSNCROS].val2;
+ }
+ if(sc_data[SC_BERSERK].timer!=-1)
+ aspd_rate -= 30;
+ if(sc_data[i=SC_ASPDPOTION3].timer!=-1 || sc_data[i=SC_ASPDPOTION2].timer!=-1 || sc_data[i=SC_ASPDPOTION1].timer!=-1 || sc_data[i=SC_ASPDPOTION0].timer!=-1)
+ aspd_rate -= sc_data[i].val2;
+ if(sc_data[SC_DONTFORGETME].timer!=-1)
+ aspd_rate += sc_data[SC_DONTFORGETME].val2;
+ if(sc_data[SC_STEELBODY].timer!=-1)
+ aspd_rate += 25;
+ if(sc_data[SC_SKA].timer!=-1)
+ aspd_rate += 25;
+ if(sc_data[SC_DEFENDER].timer != -1)
+ aspd_rate += 25 -sc_data[SC_DEFENDER].val1*5;
+ if(sc_data[SC_GOSPEL].timer!=-1 && sc_data[SC_GOSPEL].val4 == BCT_ENEMY)
+ aspd_rate += 25;
+ if(sc_data[SC_GRAVITATION].timer!=-1)
+ aspd_rate += sc_data[SC_GRAVITATION].val2;
+ if(sc_data[SC_JOINTBEAT].timer!=-1) {
+ if (sc_data[SC_JOINTBEAT].val2 == 1)
+ aspd_rate += 25;
+ else if (sc_data[SC_JOINTBEAT].val2 == 2)
+ aspd_rate += 10;
+
+ if(sc_data[SC_STAR_COMFORT].timer!=-1 && bl->m == ((struct map_session_data *)bl)->feel_map[2].m) //SG skill [Komurka]
+ aspd_rate -= (status_get_lv(bl) + status_get_dex(bl) + status_get_luk(bl))/10;
+ }
+ }
+
+ return aspd_rate;
+}
+
+int status_calc_maxhp(struct block_list *bl, int maxhp)
+{
+ struct status_change *sc_data;
+ sc_data = status_get_sc_data(bl);
+
+ if(sc_data) {
+ if(sc_data[SC_INCMHPRATE].timer!=-1)
+ maxhp += maxhp * sc_data[SC_INCMHPRATE].val1/100;
+ if(sc_data[SC_APPLEIDUN].timer!=-1)
+ maxhp += maxhp * sc_data[SC_APPLEIDUN].val2/100;
+ if(sc_data[SC_DELUGE].timer!=-1 && status_get_elem_type(bl)==1)
+ maxhp += maxhp * deluge_eff[sc_data[SC_DELUGE].val1-1]/100;
+ if(sc_data[SC_BERSERK].timer!=-1)
+ maxhp += maxhp * 2;
+ }
+
+ return maxhp;
+}
+
+int status_calc_maxsp(struct block_list *bl, int maxsp)
+{
+ struct status_change *sc_data;
+ sc_data = status_get_sc_data(bl);
+
+ if(sc_data) {
+ if(sc_data[SC_INCMSPRATE].timer!=-1)
+ maxsp += maxsp * sc_data[SC_INCMSPRATE].val1/100;
+ if(sc_data[SC_SERVICE4U].timer!=-1)
+ maxsp += maxsp * sc_data[SC_SERVICE4U].val2/100;
+ }
+
+ return maxsp;
+}
+
+/*==========================================
+ * For quick calculating [Celest] Adapted by [Skotlex]
+ *------------------------------------------
+ */
+int status_quick_recalc_speed(struct map_session_data *sd, int skill_num, int skill_lv, char start)
+{
+ /* [Skotlex]
+ This function individually changes a character's speed upon a skill change and restores it upon it's ending.
+ Should only be used on non-inclusive skills to avoid exploits.
+ Currently used for freedom of cast
+ and when cloaking changes it's val3 (in which case the new val3 value comes in the level.
+ */
+
+ int b_speed;
+
+ b_speed = sd->speed;
+
+ switch (skill_num)
+ {
+ case SA_FREECAST:
+ if (start)
+ {
+ sd->prev_speed = sd->speed;
+ sd->speed = sd->speed*(175 - skill_lv*5)/100;
+ }
+ else
+ sd->speed = sd->prev_speed;
+ break;
+ case AS_CLOAKING:
+ if (start && sd->sc_data[SC_CLOAKING].timer != -1)
+ { //There shouldn't be an "stop" case here.
+ //If the previous upgrade was
+ //SPEED_ADD_RATE(3*sd->sc_data[SC_CLOAKING].val1 -sd->sc_data[SC_CLOAKING].val3);
+ //Then just changing val3 should be a net difference of....
+ if (3*sd->sc_data[SC_CLOAKING].val1 != sd->sc_data[SC_CLOAKING].val3) //This reverts the previous value.
+ sd->speed = sd->speed * 100 /(sd->sc_data[SC_CLOAKING].val3-3*sd->sc_data[SC_CLOAKING].val1);
+ sd->sc_data[SC_CLOAKING].val3 = skill_lv;
+ sd->speed = sd->speed * (sd->sc_data[SC_CLOAKING].val3-sd->sc_data[SC_CLOAKING].val1*3) /100;
+ }
+ break;
+ }
+
+ if(sd->speed < battle_config.max_walk_speed)
+ sd->speed = battle_config.max_walk_speed;
+
+ if(b_speed != sd->speed)
+ clif_updatestatus(sd,SP_SPEED);
+
+ return 0;
+}
+
+/*==========================================
+ * 対象のClassを返す(汎用)
+ * 戻りは整数で0以上
+ *------------------------------------------
+ */
+int status_get_class(struct block_list *bl)
+{
+ nullpo_retr(0, bl);
+ if(bl->type==BL_MOB)
+ return ((struct mob_data *)bl)->class_;
+ if(bl->type==BL_PC)
+ return ((struct map_session_data *)bl)->status.class_;
+ if(bl->type==BL_PET)
+ return ((struct pet_data *)bl)->class_;
+ return 0;
+}
+/*==========================================
+ * 対象の方向を返す(汎用)
+ * 戻りは整数で0以上
+ *------------------------------------------
+ */
+int status_get_dir(struct block_list *bl)
+{
+ nullpo_retr(0, bl);
+ if(bl->type==BL_MOB)
+ return ((struct mob_data *)bl)->dir;
+ if(bl->type==BL_PC)
+ return ((struct map_session_data *)bl)->dir;
+ if(bl->type==BL_PET)
+ return ((struct pet_data *)bl)->dir;
+ return 0;
+}
+/*==========================================
+ * 対象のレベルを返す(汎用)
+ * 戻りは整数で0以上
+ *------------------------------------------
+ */
+int status_get_lv(struct block_list *bl)
+{
+ nullpo_retr(0, bl);
+ if(bl->type==BL_MOB)
+ return ((struct mob_data *)bl)->level;
+ if(bl->type==BL_PC)
+ return ((struct map_session_data *)bl)->status.base_level;
+ if(bl->type==BL_PET)
+ return ((struct pet_data *)bl)->msd->pet.level;
+ return 0;
+}
+
+/*==========================================
+ * 対象の射程を返す(汎用)
+ * 戻りは整数で0以上
+ *------------------------------------------
+ */
+int status_get_range(struct block_list *bl)
+{
+ nullpo_retr(0, bl);
+ if(bl->type==BL_MOB)
+ return ((struct mob_data *)bl)->db->range;
+ if(bl->type==BL_PC)
+ return ((struct map_session_data *)bl)->attackrange;
+ if(bl->type==BL_PET)
+ return ((struct pet_data *)bl)->db->range;
+ return 0;
+}
+/*==========================================
+ * 対象のHPを返す(汎用)
+ * 戻りは整数で0以上
+ *------------------------------------------
+ */
+int status_get_hp(struct block_list *bl)
+{
+ nullpo_retr(1, bl);
+ if(bl->type==BL_MOB)
+ return ((struct mob_data *)bl)->hp;
+ if(bl->type==BL_PC)
+ return ((struct map_session_data *)bl)->status.hp;
+ return 1;
+}
+/*==========================================
+ * 対象のMHPを返す(汎用)
+ * 戻りは整数で0以上
+ *------------------------------------------
+ */
+int status_get_max_hp(struct block_list *bl)
+{
+ nullpo_retr(1, bl);
+
+ if(bl->type==BL_PC)
+ return ((struct map_session_data *)bl)->status.max_hp;
+ else {
+ int max_hp = 1;
+
+ if(bl->type == BL_MOB) {
+ struct mob_data *md;
+ nullpo_retr(1, md = (struct mob_data *)bl);
+ max_hp = md->max_hp;
+
+ if(battle_config.mobs_level_up) // mobs leveling up increase [Valaris]
+ max_hp += (md->level - md->db->lv) * status_get_vit(bl);
+
+ }
+ else if(bl->type == BL_PET) {
+ struct pet_data *pd;
+ nullpo_retr(1, pd = (struct pet_data*)bl);
+ max_hp = pd->db->max_hp;
+ }
+
+ max_hp = status_calc_maxhp(bl,max_hp);
+ if(max_hp < 1) max_hp = 1;
+ return max_hp;
+ }
+}
+/*==========================================
+ * 対象のStrを返す(汎用)
+ * 戻りは整数で0以上
+ *------------------------------------------
+ */
+int status_get_str(struct block_list *bl)
+{
+ int str = 0;
+ nullpo_retr(0, bl);
+
+ if (bl->type == BL_PC)
+ return ((struct map_session_data *)bl)->paramc[0];
+ else {
+ if(bl->type == BL_MOB) {
+ str = ((struct mob_data *)bl)->db->str;
+ if(battle_config.mobs_level_up) // mobs leveling up increase [Valaris]
+ str += ((struct mob_data *)bl)->level - ((struct mob_data *)bl)->db->lv;
+ if(((struct mob_data*)bl)->special_state.size==1) // change for sized monsters [Valaris]
+ str/=2;
+ else if(((struct mob_data*)bl)->special_state.size==2)
+ str*=2;
+ } else if(bl->type == BL_PET){ //<Skotlex> Use pet's stats
+ if (battle_config.pet_lv_rate && ((struct pet_data *)bl)->status)
+ str = ((struct pet_data *)bl)->status->str;
+ else
+ str = ((struct pet_data *)bl)->db->str;
+ }
+
+ str = status_calc_str(bl,str);
+ }
+ if(str < 0) str = 0;
+ return str;
+}
+/*==========================================
+ * 対象のAgiを返す(汎用)
+ * 戻りは整数で0以上
+ *------------------------------------------
+ */
+
+int status_get_agi(struct block_list *bl)
+{
+ int agi=0;
+ nullpo_retr(0, bl);
+
+ if(bl->type==BL_PC)
+ return ((struct map_session_data *)bl)->paramc[1];
+ else {
+ if(bl->type == BL_MOB) {
+ agi = ((struct mob_data *)bl)->db->agi;
+ if(battle_config.mobs_level_up) // increase of mobs leveling up [Valaris]
+ agi += ((struct mob_data *)bl)->level - ((struct mob_data *)bl)->db->lv;
+ if(((struct mob_data*)bl)->special_state.size==1) // change for sized monsters [Valaris]
+ agi/=2;
+ else if(((struct mob_data*)bl)->special_state.size==2)
+ agi*=2;
+ } else if(bl->type == BL_PET) { //<Skotlex> Use pet's stats
+ if (battle_config.pet_lv_rate && ((struct pet_data *)bl)->status)
+ agi = ((struct pet_data *)bl)->status->agi;
+ else
+ agi = ((struct pet_data *)bl)->db->agi;
+ }
+
+ agi = status_calc_agi(bl,agi);
+ }
+ if(agi < 0) agi = 0;
+ return agi;
+}
+/*==========================================
+ * 対象のVitを返す(汎用)
+ * 戻りは整数で0以上
+ *------------------------------------------
+ */
+int status_get_vit(struct block_list *bl)
+{
+ int vit = 0;
+ nullpo_retr(0, bl);
+
+ if(bl->type == BL_PC)
+ return ((struct map_session_data *)bl)->paramc[2];
+ else {
+ if(bl->type == BL_MOB) {
+ vit = ((struct mob_data *)bl)->db->vit;
+ if(battle_config.mobs_level_up) // increase from mobs leveling up [Valaris]
+ vit += ((struct mob_data *)bl)->level - ((struct mob_data *)bl)->db->lv;
+ if(((struct mob_data*)bl)->special_state.size==1) // change for sizes monsters [Valaris]
+ vit/=2;
+ else if(((struct mob_data*)bl)->special_state.size==2)
+ vit*=2;
+ } else if(bl->type == BL_PET) { //<Skotlex> Use pet's stats
+ if (battle_config.pet_lv_rate && ((struct pet_data *)bl)->status)
+ vit = ((struct pet_data *)bl)->status->vit;
+ else
+ vit = ((struct pet_data *)bl)->db->vit;
+ }
+
+ vit = status_calc_vit(bl,vit);
+ }
+ if(vit < 0) vit = 0;
+ return vit;
+}
+/*==========================================
+ * 対象のIntを返す(汎用)
+ * 戻りは整数で0以上
+ *------------------------------------------
+ */
+int status_get_int(struct block_list *bl)
+{
+ int int_=0;
+ nullpo_retr(0, bl);
+
+ if(bl->type == BL_PC)
+ return ((struct map_session_data *)bl)->paramc[3];
+ else {
+ if(bl->type == BL_MOB) {
+ int_ = ((struct mob_data *)bl)->db->int_;
+ if(battle_config.mobs_level_up) // increase from mobs leveling up [Valaris]
+ int_ += ((struct mob_data *)bl)->level - ((struct mob_data *)bl)->db->lv;
+ if(((struct mob_data*)bl)->special_state.size==1) // change for sized monsters [Valaris]
+ int_/=2;
+ else if(((struct mob_data*)bl)->special_state.size==2)
+ int_*=2;
+ } else if(bl->type == BL_PET) { //<Skotlex> Use pet's stats
+ if (battle_config.pet_lv_rate && ((struct pet_data *)bl)->status)
+ int_ = ((struct pet_data *)bl)->status->int_;
+ else
+ int_ = ((struct pet_data *)bl)->db->int_;
+ }
+
+ int_ = status_calc_int(bl,int_);
+ }
+ if(int_ < 0) int_ = 0;
+ return int_;
+}
+/*==========================================
+ * 対象のDexを返す(汎用)
+ * 戻りは整数で0以上
+ *------------------------------------------
+ */
+int status_get_dex(struct block_list *bl)
+{
+ int dex = 0;
+ nullpo_retr(0, bl);
+
+ if(bl->type==BL_PC)
+ return ((struct map_session_data *)bl)->paramc[4];
+ else {
+ if(bl->type == BL_MOB) {
+ dex = ((struct mob_data *)bl)->db->dex;
+ if(battle_config.mobs_level_up) // increase from mobs leveling up [Valaris]
+ dex += ((struct mob_data *)bl)->level - ((struct mob_data *)bl)->db->lv;
+ if(((struct mob_data*)bl)->special_state.size==1) // change for sized monsters [Valaris]
+ dex/=2;
+ else if(((struct mob_data*)bl)->special_state.size==2)
+ dex*=2;
+ } else if(bl->type == BL_PET) { //<Skotlex> Use pet's stats
+ if (battle_config.pet_lv_rate && ((struct pet_data *)bl)->status)
+ dex = ((struct pet_data *)bl)->status->dex;
+ else
+ dex = ((struct pet_data *)bl)->db->dex;
+ }
+
+ dex = status_calc_dex(bl,dex);
+ }
+ if(dex < 0) dex = 0;
+ return dex;
+}
+/*==========================================
+ * 対象のLukを返す(汎用)
+ * 戻りは整数で0以上
+ *------------------------------------------
+ */
+int status_get_luk(struct block_list *bl)
+{
+ int luk = 0;
+ nullpo_retr(0, bl);
+
+ if(bl->type == BL_PC)
+ return ((struct map_session_data *)bl)->paramc[5];
+ else {
+ if(bl->type == BL_MOB) {
+ luk = ((struct mob_data *)bl)->db->luk;
+ if(battle_config.mobs_level_up) // increase from mobs leveling up [Valaris]
+ luk += ((struct mob_data *)bl)->level - ((struct mob_data *)bl)->db->lv;
+ if(((struct mob_data*)bl)->special_state.size==1) // change for sized monsters [Valaris]
+ luk/=2;
+ else if(((struct mob_data*)bl)->special_state.size==2)
+ luk*=2;
+ } else if(bl->type == BL_PET) { //<Skotlex> Use pet's stats
+ if (battle_config.pet_lv_rate && ((struct pet_data *)bl)->status)
+ luk = ((struct pet_data *)bl)->status->luk;
+ else
+ luk = ((struct pet_data *)bl)->db->luk;
+ }
+
+ luk = status_calc_luk(bl,luk);
+ }
+ if(luk < 0) luk = 0;
+ return luk;
+}
+
+/*==========================================
+ * 対象のFleeを返す(汎用)
+ * 戻りは整数で1以上
+ *------------------------------------------
+ */
+int status_get_flee(struct block_list *bl)
+{
+ int flee = 1;
+ nullpo_retr(1, bl);
+
+ if(bl->type == BL_PC)
+ return ((struct map_session_data *)bl)->flee;
+
+ flee = status_calc_flee(bl,status_get_agi(bl)+status_get_lv(bl));
+ if(flee < 1) flee = 1;
+ return flee;
+}
+/*==========================================
+ * 対象のHitを返す(汎用)
+ * 戻りは整数で1以上
+ *------------------------------------------
+ */
+int status_get_hit(struct block_list *bl)
+{
+ int hit = 1;
+ nullpo_retr(1, bl);
+ if (bl->type == BL_PC)
+ return ((struct map_session_data *)bl)->hit;
+
+ hit = status_calc_hit(bl,status_get_dex(bl)+status_get_lv(bl));
+ if(hit < 1) hit = 1;
+ return hit;
+}
+/*==========================================
+ * 対象の完全回避を返す(汎用)
+ * 戻りは整数で1以上
+ *------------------------------------------
+ */
+int status_get_flee2(struct block_list *bl)
+{
+ int flee2 = 1;
+ nullpo_retr(1, bl);
+
+ if (bl->type == BL_PC)
+ return ((struct map_session_data *)bl)->flee2;
+
+ flee2 = status_calc_flee2(bl,status_get_luk(bl)+10);
+ if (flee2 < 1) flee2 = 1;
+ return flee2;
+}
+/*==========================================
+ * 対象のクリティカルを返す(汎用)
+ * 戻りは整数で1以上
+ *------------------------------------------
+ */
+int status_get_critical(struct block_list *bl)
+{
+ int critical = 1;
+ nullpo_retr(1, bl);
+
+ if (bl->type == BL_PC)
+ return ((struct map_session_data *)bl)->critical;
+
+ critical = status_get_luk(bl)*3+10;
+ if(battle_config.enemy_critical_rate != 100)
+ critical = critical*battle_config.enemy_critical_rate/100;
+ critical = status_calc_critical(bl,critical);
+ if (critical < 1) critical = 1;
+ return critical;
+}
+/*==========================================
+ * base_atkの取得
+ * 戻りは整数で1以上
+ *------------------------------------------
+ */
+int status_get_batk(struct block_list *bl)
+{
+ int batk = 1;
+ nullpo_retr(1, bl);
+
+ if(bl->type==BL_PC) {
+ batk = ((struct map_session_data *)bl)->base_atk;
+ if (((struct map_session_data *)bl)->status.weapon < 16)
+ batk += ((struct map_session_data *)bl)->weapon_atk[((struct map_session_data *)bl)->status.weapon];
+ } else {
+ int str,dstr;
+ str = status_get_str(bl); //STR
+ dstr = str/10;
+ batk = dstr*dstr + str; //base_atkを計算する
+
+ if(bl->type == BL_MOB && ((struct mob_data *)bl)->guardian_data)
+ batk += batk * 10*((struct mob_data *)bl)->guardian_data->guardup_lv/100; // Strengthen Guardians - custom value +10% ATK / lv
+
+ batk = status_calc_batk(bl,batk);
+ }
+ if(batk < 1) batk = 1; //base_atkは最低でも1
+ return batk;
+}
+/*==========================================
+ * 対象のAtkを返す(汎用)
+ * 戻りは整数で0以上
+ *------------------------------------------
+ */
+int status_get_atk(struct block_list *bl)
+{
+ int atk=0;
+ nullpo_retr(0, bl);
+ switch (bl->type) {
+ case BL_PC:
+ return ((struct map_session_data*)bl)->right_weapon.watk;
+ case BL_MOB:
+ atk = ((struct mob_data*)bl)->db->atk1;
+ if(((struct mob_data *)bl)->guardian_data)
+ atk += atk * 10*((struct mob_data *)bl)->guardian_data->guardup_lv/100; // Strengthen Guardians - custom value +10% ATK / lv
+ break;
+ case BL_PET: //<Skotlex> Use pet's stats
+ if (battle_config.pet_lv_rate && ((struct pet_data *)bl)->status)
+ atk = ((struct pet_data *)bl)->status->atk1;
+ else
+ atk = ((struct pet_data*)bl)->db->atk1;
+ break;
+ }
+ // Absolute, then relative modifiers from status changes (shared between PC and NPC)
+ atk = status_calc_watk(bl,atk);
+ if(atk < 0) atk = 0;
+ return atk;
+}
+/*==========================================
+ * 対象の左手Atkを返す(汎用)
+ * 戻りは整数で0以上
+ *------------------------------------------
+ */
+int status_get_atk_(struct block_list *bl)
+{
+ nullpo_retr(0, bl);
+ if(bl->type==BL_PC){
+ return ((struct map_session_data*)bl)->left_weapon.watk;
+ }
+ return 0;
+}
+/*==========================================
+ * 対象のAtk2を返す(汎用)
+ * 戻りは整数で0以上
+ *------------------------------------------
+ */
+int status_get_atk2(struct block_list *bl)
+{
+ int atk2=0;
+ nullpo_retr(0, bl);
+
+ switch (bl->type) {
+ case BL_PC:
+ return ((struct map_session_data*)bl)->right_weapon.watk2;
+ case BL_MOB:
+ atk2 = ((struct mob_data*)bl)->db->atk2;
+
+ if(((struct mob_data *)bl)->guardian_data)
+ atk2 += atk2 * 10*((struct mob_data *)bl)->guardian_data->guardup_lv/100; // Strengthen Guardians - custom value +10% ATK / lv
+ break;
+ case BL_PET: //<Skotlex> Use pet's stats
+ if (battle_config.pet_lv_rate && ((struct pet_data *)bl)->status)
+ atk2 = ((struct pet_data *)bl)->status->atk2;
+ else
+ atk2 = ((struct pet_data*)bl)->db->atk2;
+ break;
+ }
+
+ // Absolute, then relative modifiers from status changes (shared between PC and NPC)
+ atk2 = status_calc_watk(bl,atk2);
+
+ if(atk2 < 0) atk2 = 0;
+ return atk2;
+}
+/*==========================================
+ * 対象の左手Atk2を返す(汎用)
+ * 戻りは整数で0以上
+ *------------------------------------------
+ */
+int status_get_atk_2(struct block_list *bl)
+{
+ nullpo_retr(0, bl);
+ if(bl->type==BL_PC)
+ return ((struct map_session_data*)bl)->left_weapon.watk2;
+ return 0;
+}
+/*==========================================
+ * 対象のMAtk1を返す(汎用)
+ * 戻りは整数で0以上
+ *------------------------------------------
+ */
+int status_get_matk1(struct block_list *bl)
+{
+ nullpo_retr(0, bl);
+
+ if(bl->type == BL_PC)
+ return ((struct map_session_data *)bl)->matk1;
+ else {
+ int matk = 0;
+ int int_ = status_get_int(bl);
+ matk = status_calc_matk(bl,int_+(int_/5)*(int_/5));
+ return matk;
+ }
+}
+/*==========================================
+ * 対象のMAtk2を返す(汎用)
+ * 戻りは整数で0以上
+ *------------------------------------------
+ */
+int status_get_matk2(struct block_list *bl)
+{
+ nullpo_retr(0, bl);
+
+ if(bl->type == BL_PC)
+ return ((struct map_session_data *)bl)->matk2;
+ else {
+ int matk = 0;
+ int int_ = status_get_int(bl);
+ matk = status_calc_matk(bl,int_+(int_/7)*(int_/7));
+ return matk;
+ }
+}
+/*==========================================
+ * 対象のDefを返す(汎用)
+ * 戻りは整数で0以上
+ *------------------------------------------
+ */
+int status_get_def(struct block_list *bl)
+{
+ int def=0;
+ nullpo_retr(0, bl);
+
+ if(bl->type==BL_PC){
+ def = ((struct map_session_data *)bl)->def;
+ if(((struct map_session_data *)bl)->skilltimer != -1)
+ def -= def * skill_get_castdef(((struct map_session_data *)bl)->skillid)/100;
+ } else if(bl->type==BL_MOB) {
+ def = ((struct mob_data *)bl)->db->def;
+ def -= def * skill_get_castdef(((struct mob_data *)bl)->skillid)/100;
+ } else if(bl->type==BL_PET)
+ def = ((struct pet_data *)bl)->db->def;
+
+ def = status_calc_def(bl,def);
+ if(def < 0) def = 0;
+
+ return def;
+}
+/*==========================================
+ * 対象のDef2を返す(汎用)
+ * 戻りは整数で1以上
+ *------------------------------------------
+ */
+int status_get_def2(struct block_list *bl)
+{
+ int def2 = 1;
+ nullpo_retr(1, bl);
+
+ if(bl->type==BL_PC)
+ return ((struct map_session_data *)bl)->def2;
+ else if(bl->type==BL_MOB)
+ def2 = ((struct mob_data *)bl)->db->vit;
+ else if(bl->type==BL_PET) { //<Skotlex> Use pet's stats
+ if (battle_config.pet_lv_rate && ((struct pet_data *)bl)->status)
+ def2 = ((struct pet_data *)bl)->status->vit;
+ else
+ def2 = ((struct pet_data *)bl)->db->vit;
+ }
+
+ def2 = status_calc_def2(bl,def2);
+ if(def2 < 1) def2 = 1;
+
+ return def2;
+}
+/*==========================================
+ * 対象のMDefを返す(汎用)
+ * 戻りは整数で0以上
+ *------------------------------------------
+ */
+int status_get_mdef(struct block_list *bl)
+{
+ int mdef=0;
+ nullpo_retr(0, bl);
+
+ if(bl->type==BL_PC)
+ return ((struct map_session_data *)bl)->mdef;
+ else if(bl->type==BL_MOB)
+ mdef = ((struct mob_data *)bl)->db->mdef;
+ else if(bl->type==BL_PET)
+ mdef = ((struct pet_data *)bl)->db->mdef;
+
+ mdef = status_calc_mdef(bl,mdef);
+ if(mdef < 0) mdef = 0;
+
+ return mdef;
+}
+/*==========================================
+ * 対象のMDef2を返す(汎用)
+ * 戻りは整数で0以上
+ *------------------------------------------
+ */
+int status_get_mdef2(struct block_list *bl)
+{
+ int mdef2=0;
+ nullpo_retr(0, bl);
+
+ if(bl->type == BL_PC)
+ return ((struct map_session_data *)bl)->mdef2 + (((struct map_session_data *)bl)->paramc[2]>>1);
+ else if(bl->type == BL_MOB)
+ mdef2 = ((struct mob_data *)bl)->db->int_ + (((struct mob_data *)bl)->db->vit>>1);
+ else if(bl->type == BL_PET) { //<Skotlex> Use pet's stats
+ if (battle_config.pet_lv_rate && ((struct pet_data *)bl)->status)
+ mdef2 = ((struct pet_data *)bl)->status->int_ +(((struct pet_data *)bl)->status->vit>>1);
+ else
+ mdef2 = ((struct pet_data *)bl)->db->int_ + (((struct pet_data *)bl)->db->vit>>1);
+ }
+
+ mdef2 = status_calc_mdef2(bl,mdef2);
+ if(mdef2 < 0) mdef2 = 0;
+
+ return mdef2;
+}
+/*==========================================
+ * 対象のSpeed(移動速度)を返す(汎用)
+ * 戻りは整数で1以上
+ * Speedは小さいほうが移動速度が速い
+ *------------------------------------------
+ */
+int status_get_speed(struct block_list *bl)
+{
+ int speed = 1000;
+ nullpo_retr(1000, bl);
+ if(bl->type==BL_PC)
+ return ((struct map_session_data *)bl)->speed;
+ else if(bl->type==BL_MOB) {
+ speed = ((struct mob_data *)bl)->speed;
+ if(battle_config.mobs_level_up) // increase from mobs leveling up [Valaris]
+ speed-=((struct mob_data *)bl)->level - ((struct mob_data *)bl)->db->lv;
+ }
+ else if(bl->type==BL_PET)
+ speed = ((struct pet_data *)bl)->msd->petDB->speed;
+ else if(bl->type==BL_NPC) //Added BL_NPC (Skotlex)
+ speed = ((struct npc_data *)bl)->speed;
+
+ speed = status_calc_speed(bl,speed);
+
+ if(speed < 1) speed = 1;
+ return speed;
+}
+/*==========================================
+ * 対象のaDelay(攻撃時ディレイ)を返す(汎用)
+ * aDelayは小さいほうが攻撃速度が速い
+ *------------------------------------------
+ */
+int status_get_adelay(struct block_list *bl)
+{
+ int adelay,aspd_rate;
+ nullpo_retr(4000, bl);
+ switch (bl->type) {
+ case BL_PC:
+ return (((struct map_session_data *)bl)->aspd<<1);
+ case BL_MOB:
+ adelay = ((struct mob_data *)bl)->db->adelay;
+ if(((struct mob_data *)bl)->guardian_data)
+ aspd_rate = 100 - 10*((struct mob_data *)bl)->guardian_data->guardup_lv; // Strengthen Guardians - custom value +10% ASPD / lv
+ else
+ aspd_rate = 100;
+ break;
+ case BL_PET:
+ adelay = ((struct pet_data *)bl)->db->adelay;
+ aspd_rate = 100;
+ break;
+ default:
+ adelay=4000;
+ aspd_rate = 100;
+ break;
+ }
+ aspd_rate = status_calc_aspd_rate(bl,aspd_rate);
+
+ if(aspd_rate != 100)
+ adelay = adelay*aspd_rate/100;
+ if(adelay < battle_config.monster_max_aspd<<1) adelay = battle_config.monster_max_aspd<<1;
+ return adelay;
+}
+int status_get_amotion(struct block_list *bl)
+{
+ nullpo_retr(2000, bl);
+ if(bl->type==BL_PC)
+ return ((struct map_session_data *)bl)->amotion;
+ else {
+ int amotion=2000,aspd_rate = 100;
+ if(bl->type==BL_MOB) {
+ amotion = ((struct mob_data *)bl)->db->amotion;
+
+ if(((struct mob_data *)bl)->guardian_data)
+ aspd_rate -= aspd_rate * 10*((struct mob_data *)bl)->guardian_data->guardup_lv/100; // Strengthen Guardians - custom value +10% ASPD / lv
+ } else if(bl->type==BL_PET)
+ amotion = ((struct pet_data *)bl)->db->amotion;
+
+ aspd_rate = status_calc_aspd_rate(bl,aspd_rate);
+
+ if(aspd_rate != 100)
+ amotion = amotion*aspd_rate/100;
+ if(amotion < battle_config.monster_max_aspd) amotion = battle_config.monster_max_aspd;
+ return amotion;
+ }
+ return 2000;
+}
+int status_get_dmotion(struct block_list *bl)
+{
+ int ret;
+ struct status_change *sc_data;
+
+ nullpo_retr(0, bl);
+ sc_data = status_get_sc_data(bl);
+ if(bl->type==BL_MOB){
+ ret=((struct mob_data *)bl)->db->dmotion;
+ if(battle_config.monster_damage_delay_rate != 100)
+ ret = ret*battle_config.monster_damage_delay_rate/100;
+ }
+ else if(bl->type==BL_PC){
+ ret=((struct map_session_data *)bl)->dmotion;
+ if(battle_config.pc_damage_delay_rate != 100)
+ ret = ret*battle_config.pc_damage_delay_rate/100;
+ }
+ else if(bl->type==BL_PET)
+ ret=((struct pet_data *)bl)->db->dmotion;
+ else
+ return 2000;
+
+ if(sc_data && (sc_data[SC_ENDURE].timer!=-1 || sc_data[SC_CONCENTRATION].timer!=-1 || sc_data[SC_BERSERK].timer!=-1))
+ if (!map_flag_gvg(bl->m)) //Only works on non-gvg grounds. [Skotlex]
+ return 0;
+
+ return ret;
+}
+int status_get_element(struct block_list *bl)
+{
+ // removed redundant variable ret [zzo]
+ struct status_change *sc_data = status_get_sc_data(bl);
+
+ nullpo_retr(20, bl);
+
+ if(sc_data) {
+ if( sc_data[SC_BENEDICTIO].timer!=-1 ) // 聖体降福
+ return 26;
+ if( sc_data[SC_FREEZE].timer!=-1 ) // 凍結
+ return 21;
+ if( sc_data[SC_STONE].timer!=-1 && sc_data[SC_STONE].val2==0)
+ return 22;
+ }
+ if(bl->type==BL_MOB) // 10の位=Lv*2、1の位=属性
+ return ((struct mob_data *)bl)->def_ele;
+ if(bl->type==BL_PC)
+ return 20+((struct map_session_data *)bl)->def_ele; // 防御属性Lv1
+ if(bl->type==BL_PET)
+ return ((struct pet_data *)bl)->db->element;
+
+ return 20;
+}
+//Retrieves the object's element acquired by status changes only.
+int status_get_attack_sc_element(struct block_list *bl)
+{
+ struct status_change *sc_data=status_get_sc_data(bl);
+ if(sc_data) {
+ if( sc_data[SC_WATERWEAPON].timer!=-1) // フロストウェポン
+ return 1;
+ if( sc_data[SC_EARTHWEAPON].timer!=-1) // サイズミックウェポン
+ return 2;
+ if( sc_data[SC_FIREWEAPON].timer!=-1) // フレームランチャー
+ return 3;
+ if( sc_data[SC_WINDWEAPON].timer!=-1) // ライトニングローダー
+ return 4;
+ if( sc_data[SC_ENCPOISON].timer!=-1) // エンチャントポイズン
+ return 5;
+ if( sc_data[SC_ASPERSIO].timer!=-1) // アスペルシオ
+ return 6;
+ if( sc_data[SC_SHADOWWEAPON].timer!=-1)
+ return 7;
+ if( sc_data[SC_GHOSTWEAPON].timer!=-1)
+ return 8;
+ }
+ return 0;
+}
+
+
+int status_get_attack_element(struct block_list *bl)
+{
+ int ret = status_get_attack_sc_element(bl);
+
+ nullpo_retr(0, bl);
+
+ if (ret) return ret;
+
+ if(bl->type==BL_MOB && (struct mob_data *)bl)
+ return 0;
+ if(bl->type==BL_PC && (struct map_session_data *)bl)
+ return ((struct map_session_data *)bl)->right_weapon.atk_ele;
+ if(bl->type==BL_PET && (struct pet_data *)bl)
+ return 0;
+
+ return 0;
+}
+int status_get_attack_element2(struct block_list *bl)
+{
+ nullpo_retr(0, bl);
+ if(bl->type==BL_PC) {
+ // removed redundant var, speeded up a bit [zzo]
+ int ret = status_get_attack_sc_element(bl);
+
+ if(ret) return ret;
+ return ((struct map_session_data *)bl)->left_weapon.atk_ele;
+ }
+ return 0;
+}
+int status_get_party_id(struct block_list *bl)
+{
+ nullpo_retr(0, bl);
+ if(bl->type==BL_PC)
+ return ((struct map_session_data *)bl)->status.party_id;
+ if(bl->type==BL_PET)
+ return ((struct pet_data *)bl)->msd->status.party_id;
+ if(bl->type==BL_MOB){
+ struct mob_data *md=(struct mob_data *)bl;
+ if( md->master_id>0 )
+ {
+ struct map_session_data *msd;
+ if (md->special_state.ai && (msd = map_id2sd(md->master_id)) != NULL)
+ return msd->status.party_id;
+ return -md->master_id;
+ }
+ return 0; //No party.
+ }
+ if(bl->type==BL_SKILL)
+ return ((struct skill_unit *)bl)->group->party_id;
+ return 0;
+}
+
+int status_get_guild_id(struct block_list *bl)
+{
+ nullpo_retr(0, bl);
+ if(bl->type==BL_PC)
+ return ((struct map_session_data *)bl)->status.guild_id;
+ if(bl->type==BL_PET)
+ return ((struct pet_data *)bl)->msd->status.guild_id;
+ if(bl->type==BL_MOB)
+ {
+ struct map_session_data *msd;
+ struct mob_data *md = (struct mob_data *)bl;
+ if (md->guardian_data) //Guardian's guild [Skotlex]
+ return md->guardian_data->guild_id;
+ if (md->special_state.ai && (msd = map_id2sd(md->master_id)) != NULL)
+ return msd->status.guild_id; //Alchemist's mobs [Skotlex]
+ return 0; //No guild.
+ }
+ if(bl->type==BL_SKILL)
+ return ((struct skill_unit *)bl)->group->guild_id;
+ return 0;
+}
+int status_get_race(struct block_list *bl)
+{
+ nullpo_retr(0, bl);
+ if(bl->type==BL_MOB)
+ return ((struct mob_data *)bl)->db->race;
+ if(bl->type==BL_PC)
+ return 7;
+ if(bl->type==BL_PET)
+ return ((struct pet_data *)bl)->db->race;
+ return 0;
+}
+int status_get_size(struct block_list *bl)
+{
+ nullpo_retr(1, bl);
+ switch (bl->type) {
+ case BL_MOB:
+ if (((struct mob_data *)bl)->sc_data[SC_SWOO].timer != -1) // [marquis007]
+ return 0;
+ return ((struct mob_data *)bl)->db->size;
+ case BL_PET:
+ return ((struct pet_data *)bl)->db->size;
+ case BL_PC:
+ {
+ struct map_session_data *sd = (struct map_session_data *)bl;
+ if (sd->sc_data[SC_SWOO].timer != -1)
+ return 0;
+ if (sd->class_&JOBL_BABY) //[Lupus]
+ return (pc_isriding(sd) && battle_config.character_size&2); //Baby Class Peco Rider + enabled option -> size = 1, else 0
+ return 1+(pc_isriding(sd) && battle_config.character_size&1); //Peco Rider + enabled option -> size = 2, else 1
+ }
+ }
+ return 1;
+}
+int status_get_mode(struct block_list *bl)
+{
+ nullpo_retr(MD_CANMOVE, bl);
+ if(bl->type==BL_MOB)
+ {
+ if (((struct mob_data *)bl)->mode)
+ return ((struct mob_data *)bl)->mode;
+ return ((struct mob_data *)bl)->db->mode;
+ }
+ if(bl->type==BL_PC)
+ return (MD_CANMOVE|MD_LOOTER|MD_CANATTACK);
+ if(bl->type==BL_PET)
+ return ((struct pet_data *)bl)->db->mode;
+ if (bl->type==BL_SKILL)
+ return (MD_CANATTACK|MD_CANMOVE); //Default mode for skills: Can attack, can move (think dances).
+ //Default universal mode, can move
+ return MD_CANMOVE; // とりあえず動くということで1
+}
+
+int status_get_mexp(struct block_list *bl)
+{
+ nullpo_retr(0, bl);
+ if(bl->type==BL_MOB)
+ return ((struct mob_data *)bl)->db->mexp;
+ if(bl->type==BL_PET)
+ return ((struct pet_data *)bl)->db->mexp;
+ return 0;
+}
+int status_get_race2(struct block_list *bl)
+{
+ nullpo_retr(0, bl);
+ if(bl->type == BL_MOB)
+ return ((struct mob_data *)bl)->db->race2;
+ if(bl->type==BL_PET)
+ return ((struct pet_data *)bl)->db->race2;
+ return 0;
+}
+int status_isdead(struct block_list *bl)
+{
+ nullpo_retr(0, bl);
+ if(bl->type == BL_MOB)
+ return ((struct mob_data *)bl)->state.state == MS_DEAD;
+ if(bl->type==BL_PC)
+ return pc_isdead((struct map_session_data *)bl);
+ return 0;
+}
+int status_isimmune(struct block_list *bl)
+{
+ struct map_session_data *sd = (struct map_session_data *)bl;
+
+ nullpo_retr(0, bl);
+ if (bl->type == BL_PC) {
+ if (sd->special_state.no_magic_damage)
+ return 1;
+ if (sd->sc_count && sd->sc_data[SC_HERMODE].timer != -1)
+ return 1;
+ }
+ return 0;
+}
+
+// StatusChange系の所得
+struct status_change *status_get_sc_data(struct block_list *bl)
+{
+ nullpo_retr(NULL, bl);
+ if(bl->type==BL_MOB)
+ return ((struct mob_data*)bl)->sc_data;
+ if(bl->type==BL_PC)
+ return ((struct map_session_data*)bl)->sc_data;
+ return NULL;
+}
+short *status_get_sc_count(struct block_list *bl)
+{
+ nullpo_retr(NULL, bl);
+ if(bl->type==BL_MOB)
+ return &((struct mob_data*)bl)->sc_count;
+ if(bl->type==BL_PC)
+ return &((struct map_session_data*)bl)->sc_count;
+ return NULL;
+}
+short *status_get_opt1(struct block_list *bl)
+{
+ nullpo_retr(0, bl);
+ if(bl->type==BL_MOB)
+ return &((struct mob_data*)bl)->opt1;
+ if(bl->type==BL_PC)
+ return &((struct map_session_data*)bl)->opt1;
+ if(bl->type==BL_NPC)
+ return &((struct npc_data*)bl)->opt1;
+ return 0;
+}
+short *status_get_opt2(struct block_list *bl)
+{
+ nullpo_retr(0, bl);
+ if(bl->type==BL_MOB)
+ return &((struct mob_data*)bl)->opt2;
+ if(bl->type==BL_PC)
+ return &((struct map_session_data*)bl)->opt2;
+ if(bl->type==BL_NPC)
+ return &((struct npc_data*)bl)->opt2;
+ return 0;
+}
+short *status_get_opt3(struct block_list *bl)
+{
+ nullpo_retr(0, bl);
+ if(bl->type==BL_MOB)
+ return &((struct mob_data*)bl)->opt3;
+ if(bl->type==BL_PC)
+ return &((struct map_session_data*)bl)->opt3;
+ if(bl->type==BL_NPC)
+ return &((struct npc_data*)bl)->opt3;
+ return 0;
+}
+short *status_get_option(struct block_list *bl)
+{
+ nullpo_retr(0, bl);
+ if(bl->type==BL_MOB)
+ return &((struct mob_data*)bl)->option;
+ if(bl->type==BL_PC)
+ return &((struct map_session_data*)bl)->status.option;
+ if(bl->type==BL_NPC)
+ return &((struct npc_data*)bl)->option;
+ return 0;
+}
+
+int status_get_sc_def(struct block_list *bl, int type)
+{
+ int sc_def;
+ nullpo_retr(0, bl);
+
+ switch (type)
+ {
+ case SP_MDEF1: // mdef
+ sc_def = 100 - (3 + status_get_mdef(bl) + status_get_luk(bl)/3);
+ break;
+ case SP_MDEF2: // int
+ sc_def = 100 - (3 + status_get_int(bl) + status_get_luk(bl)/3);
+ break;
+ case SP_DEF1: // def
+ sc_def = 100 - (3 + status_get_def(bl) + status_get_luk(bl)/3);
+ break;
+ case SP_DEF2: // vit
+ sc_def = 100 - (3 + status_get_vit(bl) + status_get_luk(bl)/3);
+ break;
+ case SP_LUK: // luck
+ sc_def = 100 - (3 + status_get_luk(bl));
+ break;
+
+ case SC_STONE:
+ case SC_FREEZE:
+ sc_def = 100 - (3 + status_get_mdef(bl) + status_get_luk(bl)/3);
+ break;
+ case SC_STAN:
+ case SC_POISON:
+ case SC_SILENCE:
+ sc_def = 100 - (3 + status_get_vit(bl) + status_get_luk(bl)/3);
+ break;
+ case SC_SLEEP:
+ case SC_CONFUSION:
+ sc_def = 100 - (3 + status_get_int(bl) + status_get_luk(bl)/3);
+ break;
+ case SC_BLIND:
+ sc_def = 100 - (3 + status_get_int(bl) + status_get_vit(bl)/3);
+ break;
+ case SC_CURSE:
+ sc_def = 100 - (3 + status_get_luk(bl) + status_get_vit(bl)/3);
+ break;
+
+ default:
+ sc_def = 100;
+ break;
+ }
+
+ if(bl->type == BL_MOB) {
+ struct mob_data *md = (struct mob_data *)bl;
+ if (md->class_ == MOBID_EMPERIUM)
+ return 0;
+ if (sc_def < 50)
+ sc_def = 50;
+ } else if(bl->type == BL_PC) {
+ struct status_change* sc_data = status_get_sc_data(bl);
+ if (sc_data)
+ {
+ if (sc_data[SC_SCRESIST].timer != -1)
+ sc_def -= sc_data[SC_SCRESIST].val1; //Status resist
+ else if (sc_data[SC_SIEGFRIED].timer != -1)
+ sc_def -= sc_data[SC_SIEGFRIED].val2; //Status resistance.
+ }
+ }
+ return (sc_def < 0) ? 0 : sc_def;
+}
+
+/*==========================================
+ * Starts a status change.
+ * type = type, val1~4 depend on the type.
+ * Tick is base duration
+ * flag:
+ * &1: Cannot be avoided (it has to start)
+ * &2: Tick should not be reduced (by vit, luk, lv, etc)
+ * &4: sc_data loaded, no value has to be altered.
+ *------------------------------------------
+ */
+int status_change_start(struct block_list *bl,int type,int val1,int val2,int val3,int val4,int tick,int flag)
+{
+ struct map_session_data *sd = NULL;
+ struct status_change* sc_data;
+ short *sc_count, *option, *opt1, *opt2, *opt3;
+ int opt_flag = 0, calc_flag = 0,updateflag = 0, save_flag = 0, race, mode, elem, undead_flag;
+ int scdef = 0;
+
+ nullpo_retr(0, bl);
+ switch (bl->type)
+ {
+ case BL_PC:
+ sd=(struct map_session_data *)bl;
+ if (status_isdead(bl))
+ return 0;
+ break;
+ case BL_MOB:
+ if (((struct mob_data*)bl)->class_ == MOBID_EMPERIUM && type != SC_SAFETYWALL)
+ return 0; //Emperium can't be afflicted by status changes.
+ if (status_isdead(bl))
+ return 0;
+ break;
+ case BL_PET: //Because pets can't have status changes.
+ case BL_SKILL: //These may happen by attacking traps or the like. [Skotlex]
+ return 0;
+ default:
+ if(battle_config.error_log)
+ ShowError("status_change_start: invalid source type (%d)!\n", bl->type);
+ return 0;
+ }
+ if(type < 0 || type >= SC_MAX) {
+ if(battle_config.error_log)
+ ShowError("status_change_start: invalid status change (%d)!\n", type);
+ return 0;
+ }
+ sc_data=status_get_sc_data(bl);
+ sc_count=status_get_sc_count(bl);
+ option=status_get_option(bl);
+ opt1=status_get_opt1(bl);
+ opt2=status_get_opt2(bl);
+ opt3=status_get_opt3(bl);
+
+ race=status_get_race(bl);
+ mode=status_get_mode(bl);
+ elem=status_get_elem_type(bl);
+ undead_flag=battle_check_undead(race,elem);
+
+ if(type == SC_AETERNA && (sc_data[SC_STONE].timer != -1 || sc_data[SC_FREEZE].timer != -1) )
+ return 0;
+ if(type == SC_OVERTHRUST && sc_data[SC_MAXOVERTHRUST].timer != -1)
+ return 0; //Overthrust can't take effect if under Max Overthrust. [Skotlex]
+ switch(type){
+ case SC_STONE:
+ case SC_FREEZE:
+ scdef=3+status_get_mdef(bl)+status_get_luk(bl)/3;
+ break;
+ case SC_STAN:
+ case SC_SILENCE:
+ case SC_POISON:
+ case SC_DPOISON:
+ scdef=3+status_get_vit(bl)+status_get_luk(bl)/3;
+ break;
+ case SC_SLEEP:
+ case SC_BLIND:
+ scdef=3+status_get_int(bl)+status_get_luk(bl)/3;
+ break;
+ case SC_CURSE:
+ scdef=3+status_get_luk(bl);
+ break;
+ default:
+ scdef=0;
+ }
+ if(scdef>=100)
+ return 0;
+ if(sd){
+ if(type == SC_ADRENALINE && !(skill_get_weapontype(BS_ADRENALINE)&(1<<sd->status.weapon)))
+ return 0;
+ if( sd && type == SC_ADRENALINE2 && !(skill_get_weapontype(BS_ADRENALINE2)&(1<<sd->status.weapon)))
+ return 0;
+
+ if(SC_COMMON_MIN<=type && type<=SC_COMMON_MAX && !(flag&1)){
+ if(sd->reseff[type-SC_COMMON_MIN] > 0 && rand()%10000<sd->reseff[type-SC_COMMON_MIN]){
+ if(battle_config.battle_log)
+ ShowInfo("PC %d skill_sc_start: status change %d blocked by reseff card (AID: %d).\n",type,bl->id);
+ return 0;
+ }
+ }
+ }
+
+ if((type==SC_FREEZE || type==SC_STONE) && undead_flag && !(flag&1))
+ //I've been informed that undead chars are inmune to stone curse too. [Skotlex]
+ return 0;
+
+
+ if (type==SC_BLESSING && (bl->type==BL_PC || (!undead_flag && race!=6))) {
+ if (sc_data[SC_CURSE].timer!=-1)
+ status_change_end(bl,SC_CURSE,-1);
+ if (sc_data[SC_STONE].timer!=-1 && sc_data[SC_STONE].val2==0)
+ status_change_end(bl,SC_STONE,-1);
+ }
+
+ if((type == SC_ADRENALINE || type==SC_ADRENALINE2 || type == SC_WEAPONPERFECTION || type == SC_OVERTHRUST) &&
+ sc_data[type].timer != -1 && sc_data[type].val2 && !val2)
+ return 0;
+
+ if(mode & MD_BOSS && !(flag&1) && ( (type>=SC_COMMON_MIN && type <= SC_COMMON_MAX)
+ || type==SC_QUAGMIRE || type==SC_DECREASEAGI || type==SC_SIGNUMCRUCIS || type==SC_PROVOKE || type==SC_ROKISWEIL
+ || type==SC_COMA
+ || (type == SC_BLESSING && (undead_flag || race == 6)))){
+ /* ボスには?かない(ただしカ?ドによる?果は適用される) */
+ return 0;
+ }
+
+ if(sc_data[type].timer != -1){ /* すでに同じ異常になっている場合タイマ解除 */
+ if(sc_data[type].val1 > val1 && type != SC_COMBO && type != SC_DANCING && type != SC_DEVOTION &&
+ type != SC_ASPDPOTION0 && type != SC_ASPDPOTION1 && type != SC_ASPDPOTION2 && type != SC_ASPDPOTION3
+ && type != SC_ATKPOTION && type != SC_MATKPOTION // added atk and matk potions [Valaris]
+ )
+ return 0;
+
+ if ((type >=SC_STAN && type <= SC_BLIND) || type == SC_DPOISON)
+ return 0;/* ?ぎ足しができない?態異常である時は?態異常を行わない */
+
+ if (type == SC_GOSPEL && sc_data[type].val4 == BCT_SELF) //Must not override a casting gospel char.
+ return 0;
+
+ (*sc_count)--;
+ delete_timer(sc_data[type].timer, status_change_timer);
+ sc_data[type].timer = -1;
+ }
+
+ if(type==SC_FREEZE || type==SC_STAN || type==SC_SLEEP || type==SC_STOP || type == SC_CONFUSION ||
+ type==SC_CLOSECONFINE || type==SC_CLOSECONFINE2)
+ battle_stopwalking(bl,1);
+
+ // クアグマイア/私を忘れないで中は無効なスキル
+ if ((sc_data[SC_QUAGMIRE].timer!=-1 || sc_data[SC_DONTFORGETME].timer!=-1) &&
+ (type==SC_CONCENTRATE || type==SC_INCREASEAGI ||
+ type==SC_TWOHANDQUICKEN || type==SC_SPEARSQUICKEN ||
+ type==SC_ADRENALINE || type==SC_ADRENALINE2 ||
+ type==SC_TRUESIGHT || type==SC_WINDWALK ||
+ type==SC_CARTBOOST || type==SC_ASSNCROS ||
+ type==SC_ONEHAND))
+ return 0;
+
+ switch(type){ /* 異常の種類ごとの?理 */
+ case SC_PROVOKE: /* プロボック */
+ calc_flag = 1;
+ if(tick <= 0) tick = 1000; /* (オ?トバ?サ?ク) */
+ break;
+ case SC_ENDURE: /* インデュア */
+ if(tick <= 0) tick = 1000 * 60;
+ calc_flag = 1; // for updating mdef
+ val2 = 7; // [Celest]
+ break;
+ case SC_AUTOBERSERK:
+ {
+ if (!(flag&4))
+ tick = 60*1000;
+ if (bl->type == BL_PC && sd->status.hp<sd->status.max_hp>>2 &&
+ (sc_data[SC_PROVOKE].timer==-1 || sc_data[SC_PROVOKE].val2==0))
+ status_change_start(bl,SC_PROVOKE,10,1,0,0,0,0);
+ }
+ break;
+
+ case SC_INCREASEAGI: /* 速度上昇 */
+ calc_flag = 1;
+ if(sc_data[SC_DECREASEAGI].timer!=-1 )
+ status_change_end(bl,SC_DECREASEAGI,-1);
+ break;
+ case SC_DECREASEAGI: /* 速度減少 */
+ if (bl->type == BL_PC && !(tick&2)) // Celest
+ tick>>=1;
+ calc_flag = 1;
+ if(sc_data[SC_INCREASEAGI].timer!=-1 )
+ status_change_end(bl,SC_INCREASEAGI,-1);
+ if(sc_data[SC_ADRENALINE].timer!=-1 )
+ status_change_end(bl,SC_ADRENALINE,-1);
+ if(sc_data[SC_ADRENALINE2].timer!=-1 )
+ status_change_end(bl,SC_ADRENALINE2,-1);
+ if(sc_data[SC_SPEARSQUICKEN].timer!=-1 )
+ status_change_end(bl,SC_SPEARSQUICKEN,-1);
+ if(sc_data[SC_TWOHANDQUICKEN].timer!=-1 )
+ status_change_end(bl,SC_TWOHANDQUICKEN,-1);
+ if(sc_data[SC_CARTBOOST].timer!=-1 )
+ status_change_end(bl,SC_CARTBOOST,-1);
+ if(sc_data[SC_ONEHAND].timer!=-1 )
+ status_change_end(bl,SC_ONEHAND,-1);
+ break;
+ case SC_SIGNUMCRUCIS: /* シグナムクルシス */
+ calc_flag = 1;
+ val2 = 10 + val1*2;
+ if (!(flag&4))
+ tick = 600*1000;
+ clif_emotion(bl,4);
+ break;
+ case SC_ONEHAND: //Removes the Aspd potion effect, as reported by Vicious. [Skotlex]
+ if(sc_data[SC_ASPDPOTION0].timer!=-1)
+ status_change_end(bl,SC_ASPDPOTION0,-1);
+ if(sc_data[SC_ASPDPOTION1].timer!=-1)
+ status_change_end(bl,SC_ASPDPOTION1,-1);
+ if(sc_data[SC_ASPDPOTION2].timer!=-1)
+ status_change_end(bl,SC_ASPDPOTION2,-1);
+ if(sc_data[SC_ASPDPOTION3].timer!=-1)
+ status_change_end(bl,SC_ASPDPOTION3,-1);
+ case SC_TWOHANDQUICKEN: /* 2HQ */
+ if(sc_data[SC_DECREASEAGI].timer!=-1)
+ return 0;
+ *opt3 |= 1;
+ calc_flag = 1;
+ break;
+ case SC_ADRENALINE2:
+ case SC_ADRENALINE: /* アドレナリンラッシュ */
+ if(sc_data[SC_DECREASEAGI].timer!=-1)
+ return 0;
+ if(bl->type == BL_PC && !(flag&2))
+ if(pc_checkskill(sd,BS_HILTBINDING)>0)
+ tick += tick / 10;
+ calc_flag = 1;
+ break;
+ case SC_WEAPONPERFECTION: /* ウェポンパ?フェクション */
+ if(bl->type == BL_PC && !(flag&2))
+ if(pc_checkskill(sd,BS_HILTBINDING)>0)
+ tick += tick / 10;
+ break;
+ case SC_OVERTHRUST: /* オ?バ?スラスト */
+ if(bl->type == BL_PC && !(flag&2))
+ if(pc_checkskill(sd,BS_HILTBINDING)>0)
+ tick += tick / 10;
+ *opt3 |= 2;
+ break;
+ case SC_MAXOVERTHRUST: //Cancels Normal Overthrust. [Skotlex]
+ if (sc_data[SC_OVERTHRUST].timer != -1)
+ status_change_end(bl, SC_OVERTHRUST, -1);
+ break;
+ case SC_MAXIMIZEPOWER: /* マキシマイズパワ?(SPが1減る時間,val2にも) */
+ if (!(flag&4))
+ {
+ if(bl->type != BL_PC)
+ tick = 5000;
+ val2 = tick;
+ }
+ break;
+ case SC_ENCPOISON: /* エンチャントポイズン */
+ calc_flag = 1;
+ val2=(((val1 - 1) / 2) + 3)*100; /* 毒付?確率 */
+ skill_enchant_elemental_end(bl,SC_ENCPOISON);
+ break;
+ case SC_EDP: // [Celest]
+ val2 = val1 + 2; /* 猛毒付?確率(%) */
+ calc_flag = 1;
+ break;
+ case SC_POISONREACT: /* ポイズンリアクト */
+ if (!(flag&4))
+ val2=val1/2 + val1%2; // [Celest]
+ break;
+ case SC_ENERGYCOAT: /* エナジ?コ?ト */
+ *opt3 |= 4;
+ break;
+ case SC_MAGICROD:
+ val2 = val1*20;
+ break;
+ case SC_KYRIE: /* キリエエレイソン */
+ if (!(flag&4))
+ {
+ val2 = status_get_max_hp(bl) * (val1 * 2 + 10) / 100;/* 耐久度 */
+ val3 = (val1 / 2 + 5); /* 回? */
+ }
+// -- moonsoul (added to undo assumptio status if target has it)
+ if(sc_data[SC_ASSUMPTIO].timer!=-1 )
+ status_change_end(bl,SC_ASSUMPTIO,-1);
+ break;
+ case SC_MINDBREAKER:
+ calc_flag = 1;
+ if(tick <= 0) tick = 1000; /* (オ?トバ?サ?ク) */
+ case SC_TRICKDEAD: /* 死んだふり */
+ if (bl->type == BL_PC) {
+ pc_stopattack(sd);
+ }
+ break;
+ case SC_QUAGMIRE: /* クァグマイア */
+ calc_flag = 1;
+ if(sc_data[SC_CONCENTRATE].timer!=-1 ) /* 集中力向上解除 */
+ status_change_end(bl,SC_CONCENTRATE,-1);
+ if(sc_data[SC_INCREASEAGI].timer!=-1 ) /* 速度上昇解除 */
+ status_change_end(bl,SC_INCREASEAGI,-1);
+ if(sc_data[SC_TWOHANDQUICKEN].timer!=-1 )
+ status_change_end(bl,SC_TWOHANDQUICKEN,-1);
+ if(sc_data[SC_ONEHAND].timer!=-1 )
+ status_change_end(bl,SC_ONEHAND,-1);
+ if(sc_data[SC_SPEARSQUICKEN].timer!=-1 )
+ status_change_end(bl,SC_SPEARSQUICKEN,-1);
+ if(sc_data[SC_ADRENALINE].timer!=-1 )
+ status_change_end(bl,SC_ADRENALINE,-1);
+ if(sc_data[SC_ADRENALINE2].timer!=-1 )
+ status_change_end(bl,SC_ADRENALINE2,-1);
+ if(sc_data[SC_TRUESIGHT].timer!=-1 ) /* トゥル?サイト */
+ status_change_end(bl,SC_TRUESIGHT,-1);
+ if(sc_data[SC_WINDWALK].timer!=-1 ) /* ウインドウォ?ク */
+ status_change_end(bl,SC_WINDWALK,-1);
+ if(sc_data[SC_CARTBOOST].timer!=-1 ) /* カ?トブ?スト */
+ status_change_end(bl,SC_CARTBOOST,-1);
+ break;
+ case SC_MAGICPOWER:
+ calc_flag = 1;
+ val2 = 1;
+ break;
+ case SC_SACRIFICE:
+ if (!(flag&4))
+ val2 = 5;
+ break;
+ case SC_ASPERSIO: /* アスペルシオ */
+ skill_enchant_elemental_end(bl,SC_ASPERSIO);
+ break;
+ case SC_FIREWEAPON: /* フレ?ムランチャ? */
+ skill_enchant_elemental_end(bl,SC_FIREWEAPON);
+ break;
+ case SC_WATERWEAPON: /* フロストウェポン */
+ skill_enchant_elemental_end(bl,SC_WATERWEAPON);
+ break;
+ case SC_WINDWEAPON: /* ライトニングロ?ダ? */
+ skill_enchant_elemental_end(bl,SC_WINDWEAPON);
+ break;
+ case SC_EARTHWEAPON: /* サイズミックウェポン */
+ skill_enchant_elemental_end(bl,SC_EARTHWEAPON);
+ break;
+ case SC_SHADOWWEAPON:
+ skill_enchant_elemental_end(bl,SC_SHADOWWEAPON);
+ break;
+ case SC_GHOSTWEAPON:
+ skill_enchant_elemental_end(bl,SC_GHOSTWEAPON);
+ break;
+ case SC_PROVIDENCE: /* プロヴィデンス */
+ calc_flag = 1;
+ val2=val1*5;
+ break;
+ case SC_REFLECTSHIELD:
+ val2=10+val1*3;
+ break;
+ case SC_STRIPWEAPON:
+ if (val2==0) val2=90;
+ break;
+ case SC_STRIPSHIELD:
+ if (val2==0) val2=85;
+ break;
+
+ case SC_AUTOSPELL: /* オ?トスペル */
+ val4 = 5 + val1*2;
+ break;
+
+ case SC_VOLCANO:
+ calc_flag = 1;
+ val3 = val1*10;
+ break;
+ case SC_DELUGE:
+ calc_flag = 1;
+ if (sc_data[SC_FOGWALL].timer != -1 && sc_data[SC_BLIND].timer != -1)
+ status_change_end(bl,SC_BLIND,-1);
+ break;
+ case SC_VIOLENTGALE:
+ calc_flag = 1;
+ val3 = val1*3;
+ break;
+
+ case SC_SPEARSQUICKEN: /* スピアクイッケン */
+ calc_flag = 1;
+ val2 = 20+val1;
+ *opt3 |= 1;
+ break;
+
+ case SC_BLADESTOP: /* 白刃取り */
+ if(val2==2) clif_bladestop((struct block_list *)val3,(struct block_list *)val4,1);
+ *opt3 |= 32;
+ break;
+
+ case SC_LULLABY: /* 子守唄 */
+ case SC_RICHMANKIM:
+ case SC_ROKISWEIL: /* ロキの叫び */
+ case SC_INTOABYSS: /* 深淵の中に */
+ case SC_POEMBRAGI: /* ブラギの詩 */
+ case SC_UGLYDANCE: /* 自分勝手なダンス */
+ break;
+ case SC_ETERNALCHAOS: /* エタ?ナルカオス */
+ case SC_DRUMBATTLE: /* ?太鼓の響き */
+ case SC_NIBELUNGEN: /* ニ?ベルングの指輪 */
+ case SC_SIEGFRIED: /* 不死身のジ?クフリ?ド */
+ case SC_WHISTLE: /* 口笛 */
+ case SC_ASSNCROS: /* 夕陽のアサシンクロス */
+ case SC_APPLEIDUN: /* イドゥンの林檎 */
+ case SC_HUMMING: /* ハミング */
+ case SC_ATKPOTION: // Valaris
+ case SC_MATKPOTION:
+ case SC_FORTUNE: /* 幸運のキス */
+ case SC_SERVICE4U: /* サ?ビスフォ?ユ? */
+ calc_flag = 1;
+ break;
+ case SC_DONTFORGETME: /* 私を忘れないで */
+ calc_flag = 1;
+ if(sc_data[SC_INCREASEAGI].timer!=-1 ) /* 速度上昇解除 */
+ status_change_end(bl,SC_INCREASEAGI,-1);
+ if(sc_data[SC_TWOHANDQUICKEN].timer!=-1 )
+ status_change_end(bl,SC_TWOHANDQUICKEN,-1);
+ if(sc_data[SC_ONEHAND].timer!=-1 )
+ status_change_end(bl,SC_ONEHAND,-1);
+ if(sc_data[SC_SPEARSQUICKEN].timer!=-1 )
+ status_change_end(bl,SC_SPEARSQUICKEN,-1);
+ if(sc_data[SC_ADRENALINE].timer!=-1 )
+ status_change_end(bl,SC_ADRENALINE,-1);
+ if(sc_data[SC_ADRENALINE2].timer!=-1 )
+ status_change_end(bl,SC_ADRENALINE2,-1);
+ if(sc_data[SC_ASSNCROS].timer!=-1 )
+ status_change_end(bl,SC_ASSNCROS,-1);
+ if(sc_data[SC_TRUESIGHT].timer!=-1 ) /* トゥル?サイト */
+ status_change_end(bl,SC_TRUESIGHT,-1);
+ if(sc_data[SC_WINDWALK].timer!=-1 ) /* ウインドウォ?ク */
+ status_change_end(bl,SC_WINDWALK,-1);
+ if(sc_data[SC_CARTBOOST].timer!=-1 ) /* カ?トブ?スト */
+ status_change_end(bl,SC_CARTBOOST,-1);
+ break;
+ case SC_MOONLIT:
+ val2 = bl->id;
+ skill_setmapcell(bl,CG_MOONLIT, val1, CELL_SETMOONLIT);
+ break;
+ case SC_DANCING: /* ダンス/演奏中 */
+ calc_flag = 1;
+ if (!(flag&4))
+ {
+ val3= tick / 1000;
+ tick = 1000;
+ }
+ break;
+
+ case SC_EXPLOSIONSPIRITS: // 爆裂波動
+ calc_flag = 1;
+ val2 = 75 + 25*val1;
+ *opt3 |= 8;
+ break;
+ case SC_STEELBODY: // 金剛
+ case SC_SKA:
+ calc_flag = 1;
+ *opt3 |= 16;
+ break;
+ case SC_AUTOCOUNTER:
+ val3 = val4 = 0;
+ break;
+
+ case SC_ASPDPOTION0: /* ?速ポ?ション */
+ case SC_ASPDPOTION1:
+ case SC_ASPDPOTION2:
+ case SC_ASPDPOTION3:
+ calc_flag = 1;
+ if (!(flag&4))
+ val2 = 5*(2+type-SC_ASPDPOTION0);
+ break;
+
+ case SC_XMAS: // Xmas Suit [Valaris]
+ case SC_WEDDING: //結婚用(結婚衣裳になって?くのが?いとか)
+ {
+ struct map_session_data *sd;
+ if (bl->type == BL_PC && (sd= (struct map_session_data *)bl))
+ { //Change look.
+ if(type==SC_WEDDING)
+ sd->view_class = JOB_WEDDING;
+ else if(type==SC_XMAS)
+ sd->view_class = JOB_XMAS;
+ clif_changelook(&sd->bl,LOOK_BASE,sd->view_class);
+#if PACKETVER < 4
+ clif_changelook(&sd->bl,LOOK_WEAPON,sd->status.weapon);
+ clif_changelook(&sd->bl,LOOK_SHIELD,sd->status.shield);
+#else
+ clif_changelook(&sd->bl,LOOK_WEAPON,0);
+#endif
+ if(battle_config.save_clothcolor && sd->status.clothes_color > 0 &&
+ ((type==SC_WEDDING && !battle_config.wedding_ignorepalette) ||
+ (type==SC_XMAS && !battle_config.xmas_ignorepalette)))
+ clif_changelook(&sd->bl,LOOK_CLOTHES_COLOR,sd->status.clothes_color);
+ }
+ }
+ break;
+ case SC_NOCHAT: //チャット禁止?態
+ {
+ if(!battle_config.muting_players)
+ return 0;
+
+ if (!(flag&4))
+ tick = 60000;
+ updateflag = SP_MANNER;
+ save_flag = 1; // celest
+ }
+ break;
+
+ /* option1 */
+ case SC_STONE: /* 石化 */
+ if(!(flag&2)) {
+ int sc_def = status_get_mdef(bl)*200;
+ tick = tick - sc_def;
+ }
+ if (!(flag&4))
+ val3 = tick/1000;
+ if(val3 < 1) val3 = 1;
+ if (!(flag&4))
+ tick = 5000;
+ val2 = 1;
+ break;
+ case SC_SLEEP: /* 睡眠 */
+ if(!(flag&4)) {
+ tick = 30000;//睡眠はステ?タス耐性に?わらず30秒
+ }
+ break;
+ case SC_FREEZE: /* 凍結 */
+ if(!(flag&2)) {
+ int sc_def = 100 - status_get_mdef(bl);
+ tick = tick * sc_def / 100;
+ }
+ break;
+ case SC_STAN: /* スタン(val2にミリ秒セット) */
+ if(!(flag&2)) {
+ int sc_def = status_get_sc_def_vit(bl);
+ tick = tick * sc_def / 100;
+ }
+ break;
+
+ /* option2 */
+ case SC_DPOISON: /* 猛毒 */
+ {
+ int hp = status_get_hp(bl);
+ int mhp = status_get_max_hp(bl);
+
+ // MHP?1/4????????
+ if (hp > mhp>>2) {
+ if(bl->type == BL_PC) {
+ int diff = mhp*10/100;
+ if (hp - diff < mhp>>2)
+ diff = hp - (mhp>>2);
+ pc_heal((struct map_session_data *)bl, -diff, 0);
+ } else if(bl->type == BL_MOB) {
+ struct mob_data *md = (struct mob_data *)bl;
+ hp -= mhp*15/100;
+ if (hp > mhp>>2)
+ md->hp = hp;
+ else
+ md->hp = mhp>>2;
+ }
+ }
+ } // fall through
+ case SC_POISON: /* 毒 */
+ {
+ int mhp;
+
+ calc_flag = 1;
+ if (flag&4)
+ break;
+ if(!(flag&2)) {
+ int sc_def = 100 - (status_get_vit(bl) + status_get_luk(bl)/5);
+ tick = tick * sc_def / 100;
+ }
+ val3 = tick/1000;
+ if(val3 < 1) val3 = 1;
+ tick = 1000;
+ mhp = status_get_max_hp(bl);
+ if (bl->type == BL_PC)
+ val4 = (type == SC_DPOISON) ? 3 + mhp/50 : 3 + mhp*3/200;
+ else
+ val4 = (type == SC_DPOISON) ? 3 + mhp/100 : 3 + mhp/200;
+
+ }
+ break;
+ case SC_SILENCE: /* 沈?(レックスデビ?ナ) */
+ if (sc_data && sc_data[SC_GOSPEL].timer!=-1) {
+ if (sc_data[SC_GOSPEL].val4 == BCT_SELF) { //Clear Gospel [Skotlex]
+ status_change_end(bl,SC_GOSPEL,-1);
+ }
+ break;
+ }
+ if(!(flag&2)) {
+ int sc_def = 100 - status_get_vit(bl);
+ tick = tick * sc_def / 100;
+ }
+ break;
+ case SC_CONFUSION:
+ clif_emotion(bl,1);
+ break;
+ case SC_BLIND: /* 暗? */
+ calc_flag = 1;
+ if(!(flag&4) && tick < 1000)
+ tick = 30000;
+ if(!(flag&2)) {
+ int sc_def = 100 - (status_get_lv(bl)/10 + status_get_int(bl)/15);
+ tick = tick*sc_def/100;
+ if (tick < 5000) //Minimum 5 secs?
+ tick = 5000;
+ }
+ break;
+ case SC_CURSE:
+ calc_flag = 1;
+ if(!(flag&2)) {
+ int sc_def = 100 - status_get_vit(bl);
+ tick = tick * sc_def / 100;
+ }
+ break;
+
+ case SC_BLEEDING:
+ if(!(flag&2)) {
+ int sc_def = 100 - (status_get_lv(bl)/5 +status_get_vit(bl));
+ tick = tick * sc_def / 100;
+ }
+ if(!(flag&4) && tick < 10000) //Minimum bleed time is 10 secs or this sc does nothing! [Skotlex]
+ tick = 10000;
+ val4 = tick;
+ tick = 10000;
+ break;
+
+ /* option */
+ case SC_HIDING: /* ハイディング */
+ calc_flag = 1;
+ if(bl->type == BL_PC && !(flag&4)) {
+ val2 = tick / 1000; /* 持?時間 */
+ tick = 1000;
+ }
+ break;
+ case SC_CHASEWALK:
+ case SC_CLOAKING: /* クロ?キング */
+ if (flag&4)
+ break;
+ if(bl->type != BL_PC)
+ tick = 5000*val1;
+ calc_flag = 1; // [Celest]
+ val2 = tick;
+ val3 = type==SC_CLOAKING ? 130-val1*3 : 135-val1*5;
+ break;
+ case SC_SIGHT: /* サイト/ルアフ */
+ case SC_RUWACH:
+ case SC_SIGHTBLASTER:
+ if (flag&4)
+ break;
+ val2 = tick/250;
+ tick = 10;
+ break;
+
+ case SC_WEIGHT50:
+ case SC_WEIGHT90:
+ case SC_BROKENWEAPON:
+ case SC_BROKENARMOR:
+ case SC_READYSTORM: // Taekwon stances SCs [Dralnu]
+ case SC_READYDOWN:
+ case SC_READYCOUNTER:
+ case SC_READYTURN:
+ case SC_DODGE:
+ if (flag&4)
+ break;
+ tick = 600*1000;
+ break;
+
+ case SC_AUTOGUARD:
+ if (!flag)
+ {
+ struct map_session_data *tsd;
+ int i,t;
+ for(i=val2=0;i<val1;i++) {
+ t = 5-(i>>1);
+ val2 += (t < 0)? 1:t;
+ }
+ if (sd)
+ for (i = 0; i < 5; i++)
+ { //Pass the status to the other affected chars. [Skotlex]
+ if (sd->devotion[i] && (tsd = map_id2sd(sd->devotion[i])))
+ status_change_start(&tsd->bl,SC_AUTOGUARD,val1,val2,0,0,tick,1);
+ }
+ }
+ break;
+
+ case SC_DEFENDER:
+ calc_flag = 1;
+ if (!flag)
+ {
+ struct map_session_data *tsd;
+ int i;
+ val2 = 5 + val1*15;
+ if (sd)
+ for (i = 0; i < 5; i++)
+ { //See if there are devoted characters, and pass the status to them. [Skotlex]
+ if (sd->devotion[i] && (tsd = map_id2sd(sd->devotion[i])))
+ status_change_start(&tsd->bl,SC_DEFENDER,val1,val2,0,0,tick,1);
+ }
+ }
+ break;
+
+ case SC_CONCENTRATION: /* コンセントレ?ション */
+ *opt3 |= 1;
+ calc_flag = 1;
+ break;
+
+ case SC_TENSIONRELAX: /* テンションリラックス */
+ if (flag&4)
+ break;
+ if(bl->type == BL_PC) {
+ tick = 10000;
+ } else return 0;
+ break;
+
+ case SC_PARRYING: /* パリイング */
+ val2 = 20 + val1*3;
+ break;
+
+ case SC_WINDWALK: /* ウインドウォ?ク */
+ calc_flag = 1;
+ val2 = (val1+1)/2; // Flee bonus is 1/1/2/2/3/3/4/4/5/5, movement speed % increase is 4 times that
+ break;
+
+ case SC_JOINTBEAT: // Random break [DracoRPG]
+ calc_flag = 1;
+ val2 = rand()%6;
+ if (val2 == 5) status_change_start(bl,SC_BLEEDING,val1,0,0,0,skill_get_time2(type,val1),0);
+ break;
+
+ case SC_BERSERK: /* バ?サ?ク */
+ if(sd && !(flag&4)){
+ sd->status.hp = sd->status.max_hp * 3;
+ sd->status.sp = 0;
+ clif_updatestatus(sd,SP_HP);
+ clif_updatestatus(sd,SP_SP);
+ sd->canregen_tick = gettick() + 300000;
+ }
+ *opt3 |= 128;
+ if (!(flag&4))
+ tick = 10000;
+ calc_flag = 1;
+ break;
+
+ case SC_ASSUMPTIO: /* アスムプティオ */
+ *opt3 |= 2048;
+ if(sc_data[SC_KYRIE].timer!=-1)
+ status_change_end(bl,SC_KYRIE,-1);
+ break;
+
+ case SC_WARM: //SG skills [Komurka]
+ if (!(flag&4)) {
+ val2 = tick/1000;
+ tick = 1000;
+ }
+ *opt3 |= 4096;
+ opt_flag = 1;
+ break;
+
+ case SC_GOSPEL:
+ if (val4 == BCT_SELF) { // self effect
+ if (flag&4)
+ break;
+ val2 = tick;
+ tick = 1000;
+ status_change_clear_buffs(bl);
+ status_change_clear_debuffs(bl); //Gospel clears both types.
+ } else
+ calc_flag = 1;
+ break;
+
+ case SC_MARIONETTE: /* マリオネットコントロ?ル */
+ case SC_MARIONETTE2:
+ if (flag&4)
+ break;
+ val2 = tick;
+ if (!val3)
+ return 0;
+ tick = 1000;
+ calc_flag = 1;
+ *opt3 |= 1024;
+ break;
+
+ case SC_REJECTSWORD: /* リジェクトソ?ド */
+ val2 = 3; //3回攻?を跳ね返す
+ break;
+
+ case SC_MEMORIZE: /* メモライズ */
+ val2 = 5; //回詠唱を1/3にする
+ break;
+
+ case SC_GRAVITATION:
+ if (sd) {
+ if (val3 == BCT_SELF) {
+ sd->canmove_tick += tick;
+ sd->canact_tick += tick;
+ } else calc_flag = 1;
+ }
+ break;
+
+ case SC_HERMODE:
+ status_change_clear_buffs(bl);
+ break;
+
+ case SC_REGENERATION:
+ val1 = 2;
+ case SC_BATTLEORDERS:
+ if (!(flag&4))
+ tick = 60000; // 1 minute
+ calc_flag = 1;
+ break;
+ case SC_GUILDAURA:
+ calc_flag = 1;
+ if (!(flag&4))
+ tick = 1000;
+ break;
+
+ case SC_DEVOTION: /* ディボ?ション */
+ {
+ struct map_session_data *src;
+ if ((src = map_id2sd(val1)) && src->sc_count)
+ { //Try to inherit the status from the Crusader [Skotlex]
+ //Ideally, we should calculate the remaining time and use that, but we'll trust that
+ //once the Crusader's status changes, it will reflect on the others.
+ if (src->sc_data[SC_AUTOGUARD].timer != -1)
+ status_change_start(bl,SC_AUTOGUARD,src->sc_data[SC_AUTOGUARD].val1,0,0,0,
+ skill_get_time(CR_AUTOGUARD,src->sc_data[SC_AUTOGUARD].val1),0);
+ if (src->sc_data[SC_DEFENDER].timer != -1)
+ status_change_start(bl,SC_DEFENDER,src->sc_data[SC_DEFENDER].val1,0,0,0,
+ skill_get_time(CR_DEFENDER,src->sc_data[SC_DEFENDER].val1),0);
+ }
+ break;
+ }
+
+ case SC_COMA: //Coma. Sends a char to 1HP/SP
+ battle_damage(NULL, bl, status_get_hp(bl)-1, 0);
+ if (sd) pc_heal(sd,0,-sd->status.sp+1);
+ return 0;
+
+ case SC_CARTBOOST: /* カ?トブ?スト */
+ if(sc_data[SC_DECREASEAGI].timer!=-1 )
+ { //Cancel Decrease Agi, but take no further effect [Skotlex]
+ status_change_end(bl,SC_DECREASEAGI,-1);
+ return 0;
+ }
+ calc_flag = 1;
+ break;
+
+ case SC_CLOSECONFINE2:
+ {
+ struct block_list *src = val2?map_id2bl(val2):NULL;
+ struct status_change *sc_data2 = src?status_get_sc_data(src):NULL;
+ if (src && sc_data2) {
+ if (sc_data2[SC_CLOSECONFINE].timer == -1) //Start lock on caster.
+ status_change_start(src,SC_CLOSECONFINE,1,0,0,0,tick+1000,0);
+ else { //Increase count of locked enemies and refresh time.
+ sc_data2[SC_CLOSECONFINE].val1++;
+ delete_timer(sc_data2[SC_CLOSECONFINE].timer, status_change_timer);
+ sc_data2[SC_CLOSECONFINE].timer = add_timer(gettick()+tick+1000, status_change_timer, src->id, SC_CLOSECONFINE);
+ }
+ }
+ }
+ break;
+ case SC_KAITE:
+ val2 = 1+val1/5; //Number of bounces: 1 + skilllv/5
+ break;
+ case SC_KAUPE:
+ if (flag&4)
+ break; //Do nothing when loading.
+ switch (val1) {
+ case 3: //33*3 + 1 -> 100%
+ val2++;
+ case 1:
+ case 2: //33, 66%
+ val2 += 33*val1;
+ val3 = 1; //Dodge 1 attack total.
+ break;
+ default: //Custom. For high level mob usage, higher level means more blocks. [Skotlex]
+ val2 = 100;
+ val3 = val1-2;
+ break;
+ }
+ break;
+ case SC_COMBO:
+ switch (val1) { //Val1 contains the skill id
+ case TK_STORMKICK:
+ clif_skill_nodamage(bl,bl,TK_READYSTORM,1,1);
+ if (sd) sd->attackabletime = gettick()+tick;
+ break;
+ case TK_DOWNKICK:
+ clif_skill_nodamage(bl,bl,TK_READYDOWN,1,1);
+ if (sd) sd->attackabletime = gettick()+tick;
+ break;
+ case TK_TURNKICK:
+ clif_skill_nodamage(bl,bl,TK_READYTURN,1,1);
+ if (sd) sd->attackabletime = gettick()+tick;
+ break;
+ case TK_COUNTER:
+ clif_skill_nodamage(bl,bl,TK_READYCOUNTER,1,1);
+ if (sd) sd->attackabletime = gettick()+tick;
+ break;
+ }
+ break;
+ case SC_SWOO: // [marquis007]
+ if (!(flag&4) && status_get_mode(bl)&MD_BOSS)
+ tick /= 4; //Reduce skill's duration. But for how long?
+// *opt3 |= 8192; //We haven't figured out this value yet...
+ opt_flag = 1;
+ calc_flag = 1;
+ break;
+ case SC_TKDORI:
+ val2 = 11-val1; //Chance to consume: 11-skilllv%
+ break;
+ case SC_RUN:
+ if (!(flag&4))
+ val4 = gettick(); //Store time at which you started running.
+ calc_flag = 1;
+ break;
+
+ case SC_CONCENTRATE: /* 集中力向上 */
+ case SC_BLESSING: /* ブレッシング */
+ case SC_ANGELUS: /* アンゼルス */
+ case SC_IMPOSITIO: /* インポシティオマヌス */
+ case SC_GLORIA: /* グロリア */
+ case SC_LOUD: /* ラウドボイス */
+ case SC_KEEPING:
+ case SC_BARRIER:
+ case SC_MELTDOWN: /* メルトダウン */
+ case SC_TRUESIGHT: /* トゥル?サイト */
+ case SC_SPIDERWEB: /* スパイダ?ウェッブ */
+ case SC_SLOWDOWN:
+ case SC_SPEEDUP0:
+ case SC_SPEEDUP1:
+ case SC_INCALLSTATUS:
+ case SC_INCHIT: /* HIT上昇 */
+ case SC_INCHITRATE: /* HIT%上昇 */
+ case SC_INCFLEE: /* FLEE上昇 */
+ case SC_INCFLEERATE: /* FLEE%上昇 */
+ case SC_INCMHPRATE: /* MHP%上昇 */
+ case SC_INCMSPRATE: /* MSP%上昇 */
+ case SC_INCATKRATE: /* ATK%上昇 */
+ case SC_INCMATKRATE:
+ case SC_INCDEFRATE:
+ case SC_INCSTR:
+ case SC_INCAGI:
+ case SC_INCVIT:
+ case SC_INCINT:
+ case SC_INCDEX:
+ case SC_INCLUK:
+ case SC_STRFOOD:
+ case SC_AGIFOOD:
+ case SC_VITFOOD:
+ case SC_INTFOOD:
+ case SC_DEXFOOD:
+ case SC_LUKFOOD:
+ case SC_FLEEFOOD:
+ case SC_HITFOOD:
+ case SC_BATKFOOD:
+ case SC_WATKFOOD:
+ case SC_MATKFOOD:
+ case SC_SPURT:
+ case SC_SPIRIT:
+ case SC_SUN_COMFORT:
+ case SC_MOON_COMFORT:
+ case SC_STAR_COMFORT:
+ case SC_FUSION:
+ case SC_SKE:
+ calc_flag = 1;
+ break;
+
+ case SC_SAFETYWALL:
+ case SC_SLOWPOISON: //Slow potion can be activated even if not poisoned.
+ case SC_SUFFRAGIUM: /* サフラギム */
+ case SC_BENEDICTIO: /* 聖? */
+ case SC_MAGNIFICAT: /* マグニフィカ?ト */
+ case SC_AETERNA: /* エ?テルナ */
+ case SC_STRIPARMOR:
+ case SC_STRIPHELM:
+ case SC_CP_WEAPON:
+ case SC_CP_SHIELD:
+ case SC_CP_ARMOR:
+ case SC_CP_HELM:
+ case SC_EXTREMITYFIST: /* 阿修羅覇凰拳 */
+ case SC_ANKLE: /* アンクル */
+ case SC_BLADESTOP_WAIT: /* 白刃取り(待ち) */
+ case SC_HALLUCINATION:
+ case SC_SPLASHER: /* ベナムスプラッシャ? */
+ case SC_FOGWALL:
+ case SC_PRESERVE:
+ case SC_DOUBLECAST:
+ case SC_AURABLADE: /* オ?ラブレ?ド */
+ case SC_BABY:
+ case SC_WATK_ELEMENT:
+ case SC_ARMOR_ELEMENT:
+ case SC_LONGING:
+ case SC_ORCISH:
+ case SC_SHRINK:
+ case SC_WINKCHARM:
+ case SC_SCRESIST:
+ case SC_STOP:
+ case SC_CLOSECONFINE:
+ case SC_SKILLRATE_UP:
+ case SC_KAIZEL:
+ case SC_KAAHI:
+ case SC_INTRAVISION:
+ case SC_BASILICA:
+ break;
+
+ default:
+ if(battle_config.error_log)
+ ShowError("UnknownStatusChange [%d]\n", type);
+ return 0;
+ }
+
+ if (bl->type == BL_PC && (battle_config.display_hallucination || type != SC_HALLUCINATION))
+ {
+ if (flag&4)
+ clif_status_load(bl,StatusIconChangeTable[type],1); //Sending to owner since they aren't in the map yet. [Skotlex]
+ clif_status_change(bl,StatusIconChangeTable[type],1);
+ }
+
+ /* optionの?更 */
+ switch(type){
+ case SC_STONE:
+ case SC_FREEZE:
+ case SC_STAN:
+ case SC_SLEEP:
+
+ // Cancel cast when get status [LuzZza]
+ if (bl->type == BL_PC) {
+ struct map_session_data *sd = (struct map_session_data *)bl; //Only Pressure is uninterruptable.
+ if (sd->skilltimer != -1 && sd->skillid != PA_PRESSURE) skill_castcancel(bl, 0);
+ } else
+ if (bl->type == BL_MOB) {
+ if (((struct mob_data *)bl)->skilltimer != -1) skill_castcancel(bl, 0);
+ }
+
+ battle_stopattack(bl); /* 攻?停止 */
+ skill_stop_dancing(bl); /* 演奏/ダンスの中? */
+ { /* 同時に掛からないステ?タス異常を解除 */
+ int i;
+ for(i = SC_STONE; i <= SC_SLEEP; i++){
+ if(sc_data[i].timer != -1){
+ (*sc_count)--;
+ delete_timer(sc_data[i].timer, status_change_timer);
+ sc_data[i].timer = -1;
+ }
+ }
+ }
+ if(type == SC_STONE)
+ *opt1 = OPT1_STONEWAIT;
+ else
+ *opt1 = OPT1_STONE + (type - SC_STONE);
+ opt_flag = 1;
+ break;
+ case SC_POISON:
+ case SC_CURSE:
+ case SC_SILENCE:
+ case SC_BLIND:
+ *opt2 |= 1<<(type-SC_POISON);
+ opt_flag = 1;
+ break;
+ case SC_DPOISON: // 暫定で毒のエフェクトを使用
+ *opt2 |= OPT2_DPOISON;
+ opt_flag = 1;
+ break;
+ case SC_SIGNUMCRUCIS:
+ *opt2 |= OPT2_SIGNUMCRUCIS;
+ opt_flag = 1;
+ break;
+ case SC_HIDING:
+ case SC_CLOAKING:
+ battle_stopattack(bl); /* 攻?停止 */
+ *option |= ((type==SC_HIDING)?OPTION_HIDE:OPTION_CLOAK);
+ opt_flag =1 ;
+ break;
+ case SC_CHASEWALK:
+ battle_stopattack(bl); /* 攻?停止 */
+ *option |= OPTION_CHASEWALK|OPTION_CLOAK;
+ opt_flag =1 ;
+ break;
+ case SC_SIGHT:
+ *option |= OPTION_SIGHT;
+ opt_flag = 1;
+ break;
+ case SC_RUWACH:
+ *option |= OPTION_RUWACH;
+ opt_flag = 1;
+ break;
+ case SC_WEDDING:
+ *option |= OPTION_WEDDING;
+ opt_flag = 1;
+ break;
+ case SC_ORCISH:
+ *option |= OPTION_ORCISH;
+ opt_flag = 1;
+ break;
+ case SC_SIGHTTRASHER:
+ *option |= OPTION_SIGHTTRASHER;
+ opt_flag = 1;
+ break;
+ case SC_FUSION:
+ *option |= OPTION_FLYING;
+ opt_flag = 1;
+ break;
+ }
+
+ if(opt_flag) /* optionの?更 */
+ clif_changeoption(bl);
+
+ (*sc_count)++; /* ステ?タス異常の? */
+
+ sc_data[type].val1 = val1;
+ sc_data[type].val2 = val2;
+ sc_data[type].val3 = val3;
+ sc_data[type].val4 = val4;
+ /* タイマ?設定 */
+ sc_data[type].timer = add_timer(
+ gettick() + tick, status_change_timer, bl->id, type);
+
+ if(bl->type==BL_PC && calc_flag)
+ status_calc_pc(sd,0); /* ステ?タス再計算 */
+
+ if(bl->type==BL_PC && save_flag)
+ chrif_save(sd,0); // save the player status
+
+ if(bl->type==BL_PC && updateflag)
+ clif_updatestatus(sd,updateflag); /* ステ?タスをクライアントに送る */
+
+ if (bl->type==BL_PC && sd->pd)
+ pet_sc_check(sd, type); //Skotlex: Pet Status Effect Healing
+
+ if(type==SC_RUN && bl->type==BL_PC)
+ pc_run(sd,val1,val2);
+
+ return 0;
+}
+/*==========================================
+ * ステータス異常全解除
+ *------------------------------------------
+ */
+int status_change_clear(struct block_list *bl,int type)
+{
+ struct status_change* sc_data;
+ short *sc_count, *option, *opt1, *opt2, *opt3;
+ int i;
+
+ nullpo_retr(0, bl);
+ nullpo_retr(0, sc_data = status_get_sc_data(bl));
+ nullpo_retr(0, sc_count = status_get_sc_count(bl));
+ nullpo_retr(0, option = status_get_option(bl));
+ nullpo_retr(0, opt1 = status_get_opt1(bl));
+ nullpo_retr(0, opt2 = status_get_opt2(bl));
+ nullpo_retr(0, opt3 = status_get_opt3(bl));
+
+ if (*sc_count == 0)
+ return 0;
+ for(i = 0; i < SC_MAX; i++)
+ {
+ //Type 0: PC killed -> EDP and Meltdown must not be dispelled. [Skotlex]
+ // Do not reset Xmas status when killed. [Valaris]
+ if(sc_data[i].timer == -1 ||
+ (type == 0 && (i == SC_EDP || i == SC_MELTDOWN || i == SC_XMAS)))
+ continue;
+
+ status_change_end(bl, i, -1);
+
+ if (type == 1 && sc_data[i].timer != -1)
+ { //If for some reason status_change_end decides to still keep the status when quitting. [Skotlex]
+ (*sc_count)--;
+ delete_timer(sc_data[i].timer, status_change_timer);
+ sc_data[i].timer = -1;
+ }
+ }
+ //We can't assume the count is 0, some status don't end even when dead! [Skotlex]
+ //(*sc_count) = 0;
+ *opt1 = 0;
+ *opt2 = 0;
+ *opt3 = 0;
+ *option &= OPTION_MASK;
+
+ if(!type || type&2)
+ clif_changeoption(bl);
+
+ return 0;
+}
+
+/*==========================================
+ * ステータス異常終了
+ *------------------------------------------
+ */
+int status_change_end( struct block_list* bl , int type,int tid )
+{
+ struct map_session_data *sd;
+ struct status_change* sc_data;
+ int opt_flag=0, calc_flag = 0;
+ short *sc_count, *option, *opt1, *opt2, *opt3;
+
+ nullpo_retr(0, bl);
+ if(bl->type!=BL_PC && bl->type!=BL_MOB) {
+ if(battle_config.error_log)
+ ShowError("status_change_end: neither MOB nor PC !\n");
+ return 0;
+ }
+
+ if(type < 0 || type >= SC_MAX)
+ return 0;
+
+ sd = bl->type==BL_PC?(struct map_session_data *)bl:NULL;
+
+ sc_data = status_get_sc_data(bl);
+ sc_count = status_get_sc_count(bl);
+ option = status_get_option(bl);
+ opt1 = status_get_opt1(bl);
+ opt2 = status_get_opt2(bl);
+ opt3 = status_get_opt3(bl);
+
+ if (sc_data[type].timer != -1 && (sc_data[type].timer == tid || tid == -1)) {
+
+ if (tid == -1) // タイマから呼ばれていないならタイマ削除をする
+ delete_timer(sc_data[type].timer,status_change_timer);
+
+ /* 該?の異常を正常に?す */
+ sc_data[type].timer=-1;
+ (*sc_count)--;
+
+ switch(type){ /* 異常の種類ごとの?理 */
+ case SC_PROVOKE: /* プロボック */
+ case SC_ENDURE: // celest
+ case SC_CONCENTRATE: /* 集中力向上 */
+ case SC_BLESSING: /* ブレッシング */
+ case SC_ANGELUS: /* アンゼルス */
+ case SC_INCREASEAGI: /* 速度上昇 */
+ case SC_DECREASEAGI: /* 速度減少 */
+ case SC_SIGNUMCRUCIS: /* シグナムクルシス */
+ case SC_HIDING:
+ case SC_ONEHAND:
+ case SC_TWOHANDQUICKEN: /* 2HQ */
+ case SC_ADRENALINE2:
+ case SC_ADRENALINE: /* アドレナリンラッシュ */
+ case SC_ENCPOISON: /* エンチャントポイズン */
+ case SC_IMPOSITIO: /* インポシティオマヌス */
+ case SC_GLORIA: /* グロリア */
+ case SC_LOUD: /* ラウドボイス */
+ case SC_QUAGMIRE: /* クァグマイア */
+ case SC_PROVIDENCE: /* プロヴィデンス */
+ case SC_SPEARSQUICKEN: /* スピアクイッケン */
+ case SC_VOLCANO:
+ case SC_DELUGE:
+ case SC_VIOLENTGALE:
+ case SC_ETERNALCHAOS: /* エタ?ナルカオス */
+ case SC_DRUMBATTLE: /* ?太鼓の響き */
+ case SC_NIBELUNGEN: /* ニ?ベルングの指輪 */
+ case SC_SIEGFRIED: /* 不死身のジ?クフリ?ド */
+ case SC_WHISTLE: /* 口笛 */
+ case SC_ASSNCROS: /* 夕陽のアサシンクロス */
+ case SC_HUMMING: /* ハミング */
+ case SC_DONTFORGETME: /* 私を忘れないで */
+ case SC_FORTUNE: /* 幸運のキス */
+ case SC_SERVICE4U: /* サ?ビスフォ?ユ? */
+ case SC_EXPLOSIONSPIRITS: // 爆裂波動
+ case SC_STEELBODY: // 金剛
+ case SC_APPLEIDUN: /* イドゥンの林檎 */
+ case SC_BLADESTOP_WAIT:
+ case SC_CONCENTRATION: /* コンセントレ?ション */
+ case SC_ASSUMPTIO: /* アシャンプティオ */
+ case SC_WINDWALK: /* ウインドウォ?ク */
+ case SC_TRUESIGHT: /* トゥル?サイト */
+ case SC_SPIDERWEB: /* スパイダ?ウェッブ */
+ case SC_MAGICPOWER: /* 魔法力?幅 */
+ case SC_CHASEWALK:
+ case SC_ATKPOTION: // [Valaris]
+ case SC_MATKPOTION: // [Valaris]
+ case SC_MELTDOWN: /* メルトダウン */
+ case SC_CARTBOOST:
+ case SC_MINDBREAKER: /* マインドブレーカー */
+ case SC_EDP: // Celest
+ case SC_SLOWDOWN:
+ case SC_ASPDPOTION0: /* ?速ポ?ション */
+ case SC_ASPDPOTION1:
+ case SC_ASPDPOTION2:
+ case SC_ASPDPOTION3:
+ case SC_SPEEDUP0:
+ case SC_SPEEDUP1:
+ case SC_INCALLSTATUS:
+ case SC_INCHIT: /* HIT上昇 */
+ case SC_INCHITRATE: /* HIT%上昇 */
+ case SC_INCFLEE: /* FLEE上昇 */
+ case SC_INCFLEERATE: /* FLEE%上昇 */
+ case SC_INCMHPRATE: /* MHP%上昇 */
+ case SC_INCMSPRATE: /* MSP%上昇 */
+ case SC_INCATKRATE: /* ATK%上昇 */
+ case SC_INCMATKRATE:
+ case SC_INCDEFRATE:
+ case SC_INCSTR:
+ case SC_INCAGI:
+ case SC_INCVIT:
+ case SC_INCINT:
+ case SC_INCDEX:
+ case SC_INCLUK:
+ case SC_STRFOOD:
+ case SC_AGIFOOD:
+ case SC_VITFOOD:
+ case SC_INTFOOD:
+ case SC_DEXFOOD:
+ case SC_LUKFOOD:
+ case SC_FLEEFOOD:
+ case SC_HITFOOD:
+ case SC_BATKFOOD:
+ case SC_WATKFOOD:
+ case SC_MATKFOOD:
+ case SC_BATTLEORDERS:
+ case SC_REGENERATION:
+ case SC_GUILDAURA:
+ case SC_SPURT:
+ case SC_SPIRIT:
+ case SC_SUN_COMFORT:
+ case SC_MOON_COMFORT:
+ case SC_STAR_COMFORT:
+ case SC_FUSION:
+ case SC_SKE:
+ case SC_SWOO: // [marquis007]
+ case SC_SKA: // [marquis007]
+ calc_flag = 1;
+ break;
+
+ case SC_XMAS: // Xmas Suit [Valaris]
+ case SC_WEDDING: //結婚用(結婚衣裳になって?くのが?いとか)
+ if (sd) {
+ //Restore look
+ sd->view_class = sd->status.class_;
+ clif_changelook(&sd->bl,LOOK_BASE,sd->view_class);
+#if PACKETVER < 4
+ clif_changelook(&sd->bl,LOOK_WEAPON,sd->status.weapon);
+ clif_changelook(&sd->bl,LOOK_SHIELD,sd->status.shield);
+#else
+ clif_changelook(&sd->bl,LOOK_WEAPON,0);
+#endif
+ if(battle_config.save_clothcolor && sd->status.clothes_color > 0)
+ clif_changelook(&sd->bl,LOOK_CLOTHES_COLOR,sd->status.clothes_color);
+ }
+ break;
+ case SC_RUN://駆け足
+ if (sd && sd->walktimer != -1)
+ pc_stop_walking(sd,1);
+ if (sc_data[type].val1 >= 7 &&
+ DIFF_TICK(gettick(), sc_data[type].val4) <= 1000 &&
+ (!sd || (sd->weapontype1 == 0 && sd->weapontype2 == 0))
+ )
+ status_change_start(bl,SC_SPURT,sc_data[type].val1,0,0,0,skill_get_time2(TK_RUN, sc_data[type].val1),0);
+ calc_flag = 1;
+ break;
+ case SC_AUTOBERSERK:
+ if (sc_data[SC_PROVOKE].timer != -1 && sc_data[SC_PROVOKE].val2 == 1)
+ status_change_end(bl,SC_PROVOKE,-1);
+ break;
+
+ case SC_DEFENDER:
+ calc_flag = 1;
+ case SC_AUTOGUARD:
+ if (sd) {
+ struct map_session_data *tsd;
+ int i;
+ for (i = 0; i < 5; i++)
+ { //Clear the status from the others too [Skotlex]
+ if (sd->devotion[i] && (tsd = map_id2sd(sd->devotion[i])) && tsd->sc_data[type].timer != -1)
+ status_change_end(&tsd->bl,type,-1);
+ }
+ }
+ break;
+ case SC_DEVOTION: /* ディボ?ション */
+ {
+ struct map_session_data *md = map_id2sd(sc_data[type].val1);
+ //The status could have changed because the Crusader left the game. [Skotlex]
+ if (md)
+ {
+ md->devotion[sc_data[type].val2] = 0;
+ clif_devotion(md);
+ }
+ //Remove AutoGuard and Defender [Skotlex]
+ if (sc_data[SC_AUTOGUARD].timer != -1)
+ status_change_end(bl,SC_AUTOGUARD,-1);
+ if (sc_data[SC_DEFENDER].timer != -1)
+ status_change_end(bl,SC_DEFENDER,-1);
+ }
+ break;
+ case SC_BLADESTOP:
+ {
+ struct status_change *t_sc_data = status_get_sc_data((struct block_list *)sc_data[type].val4);
+ //片方が切れたので相手の白刃?態が切れてないのなら解除
+ if(t_sc_data && t_sc_data[SC_BLADESTOP].timer!=-1)
+ status_change_end((struct block_list *)sc_data[type].val4,SC_BLADESTOP,-1);
+
+ if(sc_data[type].val2==2)
+ clif_bladestop((struct block_list *)sc_data[type].val3,(struct block_list *)sc_data[type].val4,0);
+ }
+ break;
+ case SC_DANCING:
+ {
+ struct map_session_data *dsd;
+ struct status_change *d_sc_data;
+ if(sc_data[type].val2)
+ {
+ skill_delunitgroup((struct skill_unit_group *)sc_data[type].val2);
+ sc_data[type].val2 = 0;
+ }
+ if(sc_data[type].val4 && sc_data[type].val4 != BCT_SELF && (dsd=map_id2sd(sc_data[type].val4))){
+ d_sc_data = dsd->sc_data;
+ //合奏で相手がいる場合相手のval4を0にする
+ if(d_sc_data && d_sc_data[type].timer!=-1)
+ {
+ d_sc_data[type].val2 = d_sc_data[type].val4 = 0; //This will prevent recursive loops.
+ status_change_end(&dsd->bl, type, -1);
+ }
+ }
+ if(sc_data[type].val1 == CG_MOONLIT) //Only dance that doesn't has ground tiles... [Skotlex]
+ status_change_end(bl, SC_MOONLIT, -1);
+ }
+ if (sc_data[SC_LONGING].timer!=-1)
+ status_change_end(bl,SC_LONGING,-1);
+ calc_flag = 1;
+ break;
+ case SC_NOCHAT: //チャット禁止?態
+ if (sd) {
+ if(battle_config.manner_system){
+ if (sd->status.manner >= 0) // weeee ^^ [celest]
+ sd->status.manner = 0;
+ clif_updatestatus(sd,SP_MANNER);
+ }
+ }
+ break;
+ case SC_SPLASHER: /* ベナムスプラッシャ? */
+ {
+ struct block_list *src=map_id2bl(sc_data[type].val3);
+ if(src && tid!=-1){
+ //自分にダメ?ジ&周?3*3にダメ?ジ
+ skill_castend_damage_id(src, bl,sc_data[type].val2,sc_data[type].val1,gettick(),0 );
+ }
+ }
+ break;
+ case SC_CLOSECONFINE2:
+ {
+ struct block_list *src = sc_data[type].val2?map_id2bl(sc_data[type].val2):NULL;
+ struct status_change *sc_data2 = src?status_get_sc_data(src):NULL;
+ if (src && sc_data2) {
+ if (sc_data2[SC_CLOSECONFINE].timer != -1) //If status was already ended, do nothing.
+ { //Decrease count
+ if (--sc_data2[SC_CLOSECONFINE].val1 <= 0) //No more holds, free him up.
+ status_change_end(src, SC_CLOSECONFINE, -1);
+ }
+ }
+ }
+ break;
+ case SC_CLOSECONFINE:
+ if (sc_data[type].val1 > 0) { //Caster has been unlocked... nearby chars need to be unlocked.
+ int range = 2*skill_get_range2(bl, RG_CLOSECONFINE, 1);
+ map_foreachinarea(status_change_timer_sub,
+ bl->m, bl->x-range, bl->y-range, bl->x+range,bl->y+range,BL_CHAR,bl,type,gettick());
+ }
+ break;
+ /* option1 */
+ case SC_FREEZE:
+ sc_data[type].val3 = 0;
+ break;
+
+ /* option2 */
+ case SC_POISON: /* 毒 */
+ case SC_BLIND: /* 暗? */
+ case SC_CURSE:
+ calc_flag = 1;
+ break;
+
+ case SC_MARIONETTE: /* マリオネットコントロ?ル */
+ case SC_MARIONETTE2: /// Marionette target
+ {
+ // check for partner and end their marionette status as well
+ int type2 = (type == SC_MARIONETTE) ? SC_MARIONETTE2 : SC_MARIONETTE;
+ struct block_list *pbl = map_id2bl(sc_data[type].val3);
+ if (pbl) {
+ struct status_change* sc_data;
+ if ((sc_data = status_get_sc_data(pbl)) && sc_data[type2].timer != -1)
+ status_change_end(pbl, type2, -1);
+ }
+ if (type == SC_MARIONETTE)
+ clif_marionette(bl, 0);
+ calc_flag = 1;
+ }
+ break;
+
+ case SC_BERSERK: //val4 indicates if the skill was dispelled. [Skotlex]
+ if (sd && sd->status.hp > 100 && !sc_data[type].val4) {
+ sd->status.hp = 100;
+ clif_updatestatus(sd,SP_HP);
+ }
+ calc_flag = 1;
+ break;
+
+ case SC_GRAVITATION:
+ if (sd) {
+ if (sc_data[type].val3 == BCT_SELF) {
+ unsigned int tick = gettick();
+ sd->canmove_tick = tick;
+ sd->canact_tick = tick;
+ } else calc_flag = 1;
+ }
+ break;
+
+ case SC_GOSPEL: //Clear the buffs from other chars.
+ if(sc_data[type].val4 != BCT_SELF)
+ calc_flag = 1;
+ else if (sc_data[type].val3) { //Clear the group.
+ struct skill_unit_group *group = (struct skill_unit_group *)sc_data[type].val3;
+ sc_data[type].val3 = 0;
+ skill_delunitgroup(group);
+ }
+ break;
+ case SC_HERMODE:
+ case SC_BASILICA: //Clear the skill area. [Skotlex]
+ if(sc_data[type].val3 == BCT_SELF)
+ skill_clear_unitgroup(bl);
+ break;
+ case SC_MOONLIT: //Clear the unit effect. [Skotlex]
+ skill_setmapcell(bl,CG_MOONLIT, sc_data[SC_MOONLIT].val1, CELL_CLRMOONLIT);
+ break;
+ }
+
+
+ if (sd && (battle_config.display_hallucination || type != SC_HALLUCINATION))
+ clif_status_change(bl,StatusIconChangeTable[type],0);
+
+ switch(type){ /* 正常に?るときなにか?理が必要 */
+ case SC_STONE:
+ case SC_FREEZE:
+ case SC_STAN:
+ case SC_SLEEP:
+ *opt1 = 0;
+ opt_flag = 1;
+ break;
+
+ case SC_POISON:
+ case SC_CURSE:
+ case SC_SILENCE:
+ case SC_BLIND:
+ *opt2 &= ~(1<<(type-SC_POISON));
+ opt_flag = 1;
+ break;
+ case SC_DPOISON:
+ *opt2 &= ~OPT2_DPOISON; // 毒?態解除
+ opt_flag = 1;
+ break;
+ case SC_SIGNUMCRUCIS:
+ *opt2 &= ~OPT2_SIGNUMCRUCIS;
+ opt_flag = 1;
+ break;
+
+ case SC_HIDING:
+ *option &= ~OPTION_HIDE;
+ opt_flag = 1 ;
+ break;
+ case SC_CLOAKING:
+ *option &= ~OPTION_CLOAK;
+ calc_flag = 1; // orn
+ opt_flag = 1 ;
+ break;
+ case SC_CHASEWALK:
+ *option &= ~(OPTION_CHASEWALK|OPTION_CLOAK);
+ opt_flag = 1 ;
+ break;
+ case SC_SIGHT:
+ *option &= ~OPTION_SIGHT;
+ opt_flag = 1;
+ break;
+ case SC_WEDDING: //結婚用(結婚衣裳になって?くのが?いとか)
+ *option &= ~OPTION_WEDDING;
+ opt_flag = 1;
+ break;
+ case SC_ORCISH:
+ *option &= ~OPTION_ORCISH;
+ opt_flag = 1;
+ break;
+ case SC_RUWACH:
+ *option &= ~OPTION_RUWACH;
+ opt_flag = 1;
+ break;
+ case SC_SIGHTTRASHER:
+ *option &= ~OPTION_SIGHTTRASHER;
+ opt_flag = 1;
+ break;
+ case SC_FUSION:
+ *option &= ~OPTION_FLYING;
+ opt_flag = 1;
+ break;
+ //opt3
+ case SC_TWOHANDQUICKEN: /* 2HQ */
+ case SC_ONEHAND: /* 1HQ */
+ case SC_SPEARSQUICKEN: /* スピアクイッケン */
+ case SC_CONCENTRATION: /* コンセントレ?ション */
+ *opt3 &= ~1;
+ break;
+ case SC_OVERTHRUST: /* オ?バ?スラスト */
+ *opt3 &= ~2;
+ break;
+ case SC_ENERGYCOAT: /* エナジ?コ?ト */
+ *opt3 &= ~4;
+ break;
+ case SC_EXPLOSIONSPIRITS: // 爆裂波動
+ *opt3 &= ~8;
+ break;
+ case SC_STEELBODY: // 金剛
+ case SC_SKA:
+ *opt3 &= ~16;
+ break;
+ case SC_BLADESTOP: /* 白刃取り */
+ *opt3 &= ~32;
+ break;
+ case SC_BERSERK: /* バ?サ?ク */
+ *opt3 &= ~128;
+ break;
+ case SC_MARIONETTE: /* マリオネットコントロ?ル */
+ case SC_MARIONETTE2:
+ *opt3 &= ~1024;
+ break;
+ case SC_ASSUMPTIO: /* アスムプティオ */
+ *opt3 &= ~2048;
+ break;
+ case SC_WARM: //SG skills [Komurka]
+ *opt3 &= ~4096;
+ opt_flag = 1;
+ break;
+ }
+
+ if(opt_flag) /* optionの?更を?える */
+ clif_changeoption(bl);
+
+ if (sd && calc_flag)
+ status_calc_pc((struct map_session_data *)bl,0); /* ステ?タス再計算 */
+ }
+
+ return 0;
+}
+
+
+/*==========================================
+ * ステータス異常終了タイマー
+ *------------------------------------------
+ */
+int status_change_timer(int tid, unsigned int tick, int id, int data)
+{
+ int type = data;
+ struct block_list *bl;
+ struct map_session_data *sd=NULL;
+ struct status_change *sc_data;
+ //short *sc_count; //使ってない?
+
+// security system to prevent forgetting timer removal
+ int temp_timerid;
+
+ bl=map_id2bl(id);
+#ifndef _WIN32
+ nullpo_retr_f(0, bl, "id=%d data=%d",id,data);
+#endif
+ nullpo_retr(0, sc_data=status_get_sc_data(bl));
+
+ if(bl->type==BL_PC)
+ nullpo_retr(0, sd=(struct map_session_data *)bl);
+
+ //sc_count=status_get_sc_count(bl); //使ってない?
+
+ if(sc_data[type].timer != tid) {
+ if(battle_config.error_log)
+ ShowError("status_change_timer %d != %d\n",tid,sc_data[type].timer);
+ return 0;
+ }
+
+ // security system to prevent forgetting timer removal
+ // you shouldn't be that careless inside the switch here
+ temp_timerid = sc_data[type].timer;
+ sc_data[type].timer = -1;
+
+ switch(type){ /* 特殊な?理になる場合 */
+ case SC_MAXIMIZEPOWER: /* マキシマイズパワ? */
+ case SC_CLOAKING:
+ if(!sd || sd->status.sp > 0)
+ {
+ if (sd)
+ {
+ sd->status.sp--;
+ clif_updatestatus(sd,SP_SP);
+ }
+ sc_data[type].timer=add_timer( /* タイマ?再設定 */
+ sc_data[type].val2+tick, status_change_timer, bl->id, data);
+ return 0;
+ }
+ break;
+
+ case SC_CHASEWALK:
+ if(sd){
+ int sp = 10+sc_data[SC_CHASEWALK].val1*2;
+ if (map_flag_gvg(sd->bl.m)) sp *= 5;
+ if (sd->status.sp > sp){
+ sd->status.sp -= sp; // update sp cost [Celest]
+ clif_updatestatus(sd,SP_SP);
+ if ((++sc_data[SC_CHASEWALK].val4) == 1) {
+ status_change_start(bl, SC_INCSTR, 1<<(sc_data[SC_CHASEWALK].val1-1), 0, 0, 0,
+ (sc_data[SC_SPIRIT].timer != -1 && sc_data[SC_SPIRIT].val2 == SL_ROGUE?10:1) //SL bonus -> x10 duration
+ *skill_get_time2(ST_CHASEWALK,sc_data[SC_CHASEWALK].val1), 0);
+ }
+ sc_data[type].timer = add_timer( /* タイマ?再設定 */
+ sc_data[type].val2+tick, status_change_timer, bl->id, data);
+ return 0;
+ }
+ }
+ break;
+
+ case SC_HIDING: /* ハイディング */
+ if(sd){ /* SPがあって、時間制限の間は持? */
+ if( sd->status.sp > 0 && (--sc_data[type].val2)>0 ){
+ if(sc_data[type].val2 % (sc_data[type].val1+3) ==0 ){
+ sd->status.sp--;
+ clif_updatestatus(sd,SP_SP);
+ }
+ sc_data[type].timer=add_timer( /* タイマ?再設定 */
+ 1000+tick, status_change_timer,
+ bl->id, data);
+ return 0;
+ }
+ }
+ break;
+
+ case SC_SIGHT: /* サイト */
+ case SC_RUWACH: /* ルアフ */
+ case SC_SIGHTBLASTER:
+ {
+ int range = skill_get_range2(bl, type==SC_SIGHT?MG_SIGHT:(type==SC_RUWACH?AL_RUWACH:WZ_SIGHTBLASTER), sc_data[type].val1);
+ map_foreachinarea( status_change_timer_sub,
+ bl->m, bl->x-range, bl->y-range, bl->x+range,bl->y+range,BL_CHAR,
+ bl,type,tick);
+
+ if( (--sc_data[type].val2)>0 ){
+ sc_data[type].timer=add_timer( /* タイマ?再設定 */
+ 250+tick, status_change_timer,
+ bl->id, data);
+ return 0;
+ }
+ }
+ break;
+
+ case SC_SIGNUMCRUCIS: /* シグナムクルシス */
+ {
+ int race = status_get_race(bl);
+ if(race == 6 || battle_check_undead(race,status_get_elem_type(bl))) {
+ sc_data[type].timer=add_timer(1000*600+tick,status_change_timer, bl->id, data );
+ return 0;
+ }
+ }
+ break;
+
+ case SC_WARM: //SG skills [Komurka]
+ if( (--sc_data[type].val2)>0){
+ map_foreachinarea( status_change_timer_sub,
+ bl->m, bl->x-sc_data[type].val4, bl->y-sc_data[type].val4, bl->x+sc_data[type].val4,bl->y+sc_data[type].val4,BL_CHAR,
+ bl,type,tick);
+ sc_data[type].timer=add_timer(tick+1000, status_change_timer,bl->id, data);
+ return 0;
+ }
+ break;
+
+ case SC_PROVOKE: /* プロボック/オ?トバ?サ?ク */
+ if(sc_data[type].val2!=0){ /* オ?トバ?サ?ク(1秒ごとにHPチェック) */
+ if(sd && sd->status.hp>sd->status.max_hp>>2) /* 停止 */
+ break;
+ sc_data[type].timer=add_timer( 1000+tick,status_change_timer, bl->id, data );
+ return 0;
+ }
+ break;
+
+ case SC_AUTOBERSERK: //Auto Berserk continues until triggered off manually. [Skotlex]
+ sc_data[type].timer=add_timer( 1000*60+tick,status_change_timer, bl->id, data );
+ return 0;
+
+ case SC_ENDURE: /* インデュア */
+ if(sd && sd->special_state.infinite_endure) {
+ sc_data[type].timer=add_timer( 1000*60+tick,status_change_timer, bl->id, data );
+ return 0;
+ }
+ break;
+
+ case SC_STONE:
+ if(sc_data[type].val2 != 0) {
+ short *opt1 = status_get_opt1(bl);
+ sc_data[type].val2 = 0;
+ sc_data[type].val4 = 0;
+ battle_stopwalking(bl,1);
+ if(opt1) {
+ *opt1 = OPT1_STONE;
+ clif_changeoption(bl);
+ }
+ sc_data[type].timer=add_timer(1000+tick,status_change_timer, bl->id, data );
+ return 0;
+ }
+ else if( (--sc_data[type].val3) > 0) {
+ int hp = status_get_max_hp(bl);
+ if((++sc_data[type].val4)%5 == 0 && status_get_hp(bl) > hp>>2) {
+ hp = hp/100;
+ if(hp < 1) hp = 1;
+ if(sd)
+ pc_heal(sd,-hp,0);
+ else if(bl->type == BL_MOB){
+ struct mob_data *md;
+ if((md=((struct mob_data *)bl)) == NULL)
+ break;
+ md->hp -= hp;
+ }
+ }
+ sc_data[type].timer=add_timer(1000+tick,status_change_timer, bl->id, data );
+ return 0;
+ }
+ break;
+
+ case SC_POISON:
+ if (status_get_hp(bl) <= status_get_max_hp(bl)>>2) //Stop damaging after 25% HP left.
+ break;
+ case SC_DPOISON:
+ if ((--sc_data[type].val3) > 0 && sc_data[SC_SLOWPOISON].timer == -1) {
+ if(sd) {
+ pc_heal(sd, -sc_data[type].val4, 0);
+ } else if (bl->type == BL_MOB) {
+ ((struct mob_data*)bl)->hp -= sc_data[type].val4;
+ if (battle_config.show_mob_hp)
+ clif_charnameack (0, bl);
+ } else
+ battle_heal(NULL, bl, -sc_data[type].val4, 0, 1);
+ }
+ if (sc_data[type].val3 > 0 && !status_isdead(bl))
+ {
+ sc_data[type].timer = add_timer (1000 + tick, status_change_timer, bl->id, data );
+ return 0;
+ }
+ break;
+
+ case SC_TENSIONRELAX: /* テンションリラックス */
+ if(sd){ /* SPがあって、HPが?タンでなければ?? */
+ if( sd->status.sp > 12 && sd->status.max_hp > sd->status.hp ){
+ sc_data[type].timer=add_timer( /* タイマ?再設定 */
+ 10000+tick, status_change_timer,
+ bl->id, data);
+ return 0;
+ }
+ if(sd->status.max_hp <= sd->status.hp)
+ {
+ status_change_end(&sd->bl,SC_TENSIONRELAX,-1);
+ return 0;
+ }
+ }
+ break;
+ case SC_BLEEDING: // [celest]
+ // i hope i haven't interpreted it wrong.. which i might ^^;
+ // Source:
+ // - 10ゥェエェネェヒHPェャハ盒
+ // - ェホェ゙ェ゙ォオ?ォミケヤムェ茘ォォーェキェニェ?ヘェマ眈ェィェハェ、
+ // To-do: bleeding effect increases damage taken?
+ if ((sc_data[type].val4 -= 10000) >= 0) {
+ int hp = rand()%300 + 400;
+ if(sd) {
+ pc_heal(sd,-hp,0);
+ } else if(bl->type == BL_MOB) {
+ struct mob_data *md = (struct mob_data *)bl;
+ if (md) md->hp -= hp;
+ }
+ if (!status_isdead(bl)) {
+ // walking and casting effect is lost
+ battle_stopwalking (bl, 1);
+ skill_castcancel (bl, 0);
+ sc_data[type].timer = add_timer(10000 + tick, status_change_timer, bl->id, data );
+ }
+ return 0;
+ }
+ break;
+
+ // Status changes that don't have a time limit
+ case SC_AETERNA:
+ case SC_TRICKDEAD:
+ case SC_WEIGHT50:
+ case SC_WEIGHT90:
+ case SC_MAGICPOWER:
+ case SC_REJECTSWORD:
+ case SC_MEMORIZE:
+ case SC_BROKENWEAPON:
+ case SC_BROKENARMOR:
+ case SC_SACRIFICE:
+ case SC_READYSTORM:
+ case SC_READYDOWN:
+ case SC_READYTURN:
+ case SC_READYCOUNTER:
+ case SC_RUN:
+ case SC_DODGE:
+ sc_data[type].timer=add_timer( 1000*600+tick,status_change_timer, bl->id, data );
+ return 0;
+
+ case SC_DANCING: //ダンススキルの時間SP消費
+ {
+ int s = 0;
+ int sp = 1;
+ if(sd && (--sc_data[type].val3) > 0) {
+ switch(sc_data[type].val1){
+ case BD_RICHMANKIM: /* ニヨルドの宴 3秒にSP1 */
+ case BD_DRUMBATTLEFIELD: /* ?太鼓の響き 3秒にSP1 */
+ case BD_RINGNIBELUNGEN: /* ニ?ベルングの指輪 3秒にSP1 */
+ case BD_SIEGFRIED: /* 不死身のジ?クフリ?ド 3秒にSP1 */
+ case BA_DISSONANCE: /* 不協和音 3秒でSP1 */
+ case BA_ASSASSINCROSS: /* 夕陽のアサシンクロス 3秒でSP1 */
+ case DC_UGLYDANCE: /* 自分勝手なダンス 3秒でSP1 */
+ s=3;
+ break;
+ case BD_LULLABY: /* 子守歌 4秒にSP1 */
+ case BD_ETERNALCHAOS: /* 永遠の混沌 4秒にSP1 */
+ case BD_ROKISWEIL: /* ロキの叫び 4秒にSP1 */
+ case DC_FORTUNEKISS: /* 幸運のキス 4秒でSP1 */
+ s=4;
+ break;
+ case CG_HERMODE: // Wand of Hermod
+ sp=5; //Upkeep = 5
+ case BD_INTOABYSS: /* 深淵の中に 5秒にSP1 */
+ case BA_WHISTLE: /* 口笛 5秒でSP1 */
+ case DC_HUMMING: /* ハミング 5秒でSP1 */
+ case BA_POEMBRAGI: /* ブラギの詩 5秒でSP1 */
+ case DC_SERVICEFORYOU: /* サ?ビスフォ?ユ? 5秒でSP1 */
+ s=5;
+ break;
+ case BA_APPLEIDUN: /* イドゥンの林檎 6秒でSP1 */
+ s=6;
+ break;
+ case CG_MOONLIT: /* 月明りの泉に落ちる花びら 10秒でSP1? */
+ sp= 4*sc_data[type].val2; //Moonlit's cost is 4sp*skill_lv [Skotlex]
+ //Upkeep is also every 10 secs.
+ case DC_DONTFORGETME: /* 私を忘れないで… 10秒でSP1 */
+ s=10;
+ break;
+ }
+ if (s && ((sc_data[type].val3 % s) == 0)) {
+ if (sc_data[SC_LONGING].timer != -1)
+ sp = s;
+ if (sp > sd->status.sp)
+ sp = sd->status.sp;
+ sd->status.sp -= sp;
+ clif_updatestatus(sd,SP_SP);
+ if (sd->status.sp <= 0)
+ break;
+ }
+ sc_data[type].timer=add_timer( /* タイマ?再設定 */
+ 1000+tick, status_change_timer,
+ bl->id, data);
+ return 0;
+ }
+ }
+ break;
+
+ case SC_DEVOTION:
+ { //Check range and timeleft to preserve status [Skotlex]
+ //This implementation won't work for mobs because of map_id2sd, but it's a small cost in exchange of the speed of map_id2sd over map_id2sd
+ struct map_session_data *md = map_id2sd(sc_data[type].val1);
+ if (md && battle_check_range(bl, &md->bl, sc_data[type].val3) && (sc_data[type].val4-=1000)>0)
+ {
+ sc_data[type].timer = add_timer(1000+tick, status_change_timer, bl->id, data);
+ return 0;
+ }
+ }
+ break;
+
+ case SC_BERSERK: /* バ?サ?ク */
+ if(sd){ /* HPが100以上なら?? */
+ if( (sd->status.hp - sd->status.max_hp*5/100) > 100 ){ // 5% every 10 seconds [DracoRPG]
+ sd->status.hp -= sd->status.max_hp*5/100; // changed to max hp [celest]
+ clif_updatestatus(sd,SP_HP);
+ sc_data[type].timer = add_timer( /* タイマ?再設定 */
+ 10000+tick, status_change_timer,
+ bl->id, data);
+ return 0;
+ }
+ else
+ sd->canregen_tick = gettick() + 300000;
+ }
+ break;
+ case SC_NOCHAT: //チャット禁止?態
+ if(sd && battle_config.manner_system){
+ sd->status.manner++;
+ clif_updatestatus(sd,SP_MANNER);
+ if (sd->status.manner < 0)
+ { //Every 60 seconds your manner goes up by 1 until it gets back to 0.
+ sc_data[type].timer=add_timer(60000+tick, status_change_timer, bl->id, data);
+ return 0;
+ }
+ }
+ break;
+
+ case SC_SPLASHER:
+ if (sc_data[type].val4 % 1000 == 0) {
+ char timer[2];
+ sprintf (timer, "%d", sc_data[type].val4/1000);
+ clif_message(bl, timer);
+ }
+ if((sc_data[type].val4 -= 500) > 0) {
+ sc_data[type].timer = add_timer(
+ 500 + tick, status_change_timer,
+ bl->id, data);
+ return 0;
+ }
+ break;
+
+ case SC_MARIONETTE: /* マリオネットコントロ?ル */
+ case SC_MARIONETTE2:
+ {
+ struct block_list *pbl = map_id2bl(sc_data[type].val3);
+ if (pbl && battle_check_range(bl, pbl, 7) &&
+ (sc_data[type].val2 -= 1000)>0) {
+ sc_data[type].timer = add_timer(
+ 1000 + tick, status_change_timer,
+ bl->id, data);
+ return 0;
+ }
+ }
+ break;
+
+ case SC_GOSPEL:
+ if(sc_data[type].val4 == BCT_SELF){
+ int hp, sp;
+ hp = (sc_data[type].val1 > 5) ? 45 : 30;
+ sp = (sc_data[type].val1 > 5) ? 35 : 20;
+ if(status_get_hp(bl) - hp > 0 &&
+ (sd == NULL || sd->status.sp - sp> 0))
+ {
+ if (sd)
+ pc_heal(sd,-hp,-sp);
+ else if (bl->type == BL_MOB)
+ mob_heal((struct mob_data *)bl,-hp);
+
+ if ((sc_data[type].val2 -= 10000) > 0) {
+ sc_data[type].timer = add_timer(
+ 10000+tick, status_change_timer,
+ bl->id, data);
+ return 0;
+ }
+ }
+ }
+ break;
+
+ case SC_GUILDAURA:
+ {
+ struct block_list *tbl = map_id2bl(sc_data[type].val2);
+
+ if (tbl && battle_check_range(bl, tbl, 2)){
+ sc_data[type].timer = add_timer(
+ 1000 + tick, status_change_timer,
+ bl->id, data);
+ return 0;
+ }
+ }
+ break;
+ }
+
+ // default for all non-handled control paths
+ // security system to prevent forgetting timer removal
+
+ // if we reach this point we need the timer for the next call,
+ // so restore it to have status_change_end handle a valid timer
+ sc_data[type].timer = temp_timerid;
+
+ return status_change_end( bl,type,tid );
+}
+
+/*==========================================
+ * ステータス異常タイマー範囲処理
+ *------------------------------------------
+ */
+int status_change_timer_sub(struct block_list *bl, va_list ap )
+{
+ struct block_list *src;
+ struct map_session_data* sd=NULL;
+ struct map_session_data* tsd=NULL;
+
+ int type;
+ unsigned int tick;
+
+ src=va_arg(ap,struct block_list*);
+ type=va_arg(ap,int);
+ tick=va_arg(ap,unsigned int);
+
+ if (status_isdead(bl))
+ return 0;
+ if (src->type==BL_PC) sd= (struct map_session_data*)src;
+ if (bl->type==BL_PC) tsd= (struct map_session_data*)bl;
+
+ switch( type ){
+ case SC_SIGHT: /* サイト */
+ case SC_CONCENTRATE:
+ if( (*status_get_option(bl))&(OPTION_HIDE|OPTION_CLOAK) ){
+ status_change_end( bl, SC_HIDING, -1);
+ status_change_end( bl, SC_CLOAKING, -1);
+ }
+ break;
+ case SC_RUWACH: /* ルアフ */
+ if( (*status_get_option(bl))&(OPTION_HIDE|OPTION_CLOAK) ){
+ struct status_change *sc_data = status_get_sc_data(bl); // check whether the target is hiding/cloaking [celest]
+ if (sc_data && (sc_data[SC_HIDING].timer != -1 || // if the target is using a special hiding, i.e not using normal hiding/cloaking, don't bother
+ sc_data[SC_CLOAKING].timer != -1)) {
+ status_change_end( bl, SC_HIDING, -1);
+ status_change_end( bl, SC_CLOAKING, -1);
+ if(battle_check_target( src, bl, BCT_ENEMY ) > 0)
+ skill_attack(BF_MAGIC,src,src,bl,AL_RUWACH,1,tick,0);
+ }
+ }
+ break;
+ case SC_SIGHTBLASTER:
+ {
+ struct status_change *sc_data = status_get_sc_data(src);
+ if (sc_data && sc_data[type].val2 > 0 && battle_check_target( src, bl, BCT_ENEMY ) > 0)
+ { //sc_ check prevents a single round of Sight Blaster hitting multiple opponents. [Skotlex]
+ skill_attack(BF_MAGIC,src,src,bl,WZ_SIGHTBLASTER,1,tick,0);
+ sc_data[type].val2 = 0; //This signals it to end.
+ }
+ }
+ break;
+ case SC_WARM: //SG skills [Komurka]
+ {
+ if(battle_check_target( src,bl, BCT_ENEMY ) > 0) {
+ struct status_change *sc_data = status_get_sc_data(src);
+ if(sd){
+ if(sd->status.sp<2) {
+ sd->sc_data[type].val2 = 0; //Makes it end on the next tick.
+ break;
+ }
+ sd->status.sp -= 2;
+ clif_updatestatus(sd,SP_SP);
+ }
+ skill_attack(BF_WEAPON,src,src,bl,sc_data?sc_data[type].val3:SG_SUN_WARM,1,tick,0);
+ }
+ }
+ break;
+ case SC_CLOSECONFINE:
+ { //Lock char has released the hold on everyone...
+ struct status_change *sc_data = status_get_sc_data(bl);
+ if (sc_data && sc_data[SC_CLOSECONFINE2].timer != -1 && sc_data[SC_CLOSECONFINE2].val2 == src->id) {
+ sc_data[SC_CLOSECONFINE2].val2 = 0;
+ status_change_end(bl, SC_CLOSECONFINE2, -1);
+ }
+ }
+ break;
+ }
+ return 0;
+}
+
+int status_change_clear_buffs (struct block_list *bl)
+{
+ int i;
+ struct status_change *sc_data = status_get_sc_data(bl);
+ if (!sc_data)
+ return 0;
+ for (i = 20; i < SC_MAX; i++) {
+ if(i==SC_HALLUCINATION || i==SC_WEIGHT50 || i==SC_WEIGHT90
+ || i == SC_QUAGMIRE || i == SC_SIGNUMCRUCIS || i == SC_DECREASEAGI
+ || i == SC_SLOWDOWN || i == SC_ANKLE|| i == SC_BLADESTOP
+ || i == SC_MINDBREAKER || i == SC_WINKCHARM
+ || i == SC_STOP || i == SC_NOCHAT || i == SC_ORCISH
+ || i == SC_STRIPWEAPON || i == SC_STRIPSHIELD || i == SC_STRIPARMOR || i == SC_STRIPHELM
+ || i == SC_COMBO || i == SC_DANCING || i == SC_GUILDAURA
+ )
+ continue;
+ if(sc_data[i].timer != -1)
+ status_change_end(bl,i,-1);
+ }
+ return 0;
+}
+int status_change_clear_debuffs (struct block_list *bl)
+{
+ int i;
+ struct status_change *sc_data = status_get_sc_data(bl);
+ if (!sc_data)
+ return 0;
+ for (i = SC_COMMON_MIN; i <= SC_COMMON_MAX; i++) {
+ if(sc_data[i].timer != -1)
+ status_change_end(bl,i,-1);
+ }
+ //Other ailments not in the common range.
+ if(sc_data[SC_HALLUCINATION].timer != -1)
+ status_change_end(bl,SC_HALLUCINATION,-1);
+ if(sc_data[SC_QUAGMIRE].timer != -1)
+ status_change_end(bl,SC_QUAGMIRE,-1);
+ if(sc_data[SC_SIGNUMCRUCIS].timer != -1)
+ status_change_end(bl,SC_SIGNUMCRUCIS,-1);
+ if(sc_data[SC_DECREASEAGI].timer != -1)
+ status_change_end(bl,SC_DECREASEAGI,-1);
+ if(sc_data[SC_SLOWDOWN].timer != -1)
+ status_change_end(bl,SC_SLOWDOWN,-1);
+ if(sc_data[SC_MINDBREAKER].timer != -1)
+ status_change_end(bl,SC_MINDBREAKER,-1);
+ if(sc_data[SC_WINKCHARM].timer != -1)
+ status_change_end(bl,SC_WINKCHARM,-1);
+ if(sc_data[SC_STOP].timer != -1)
+ status_change_end(bl,SC_STOP,-1);
+ if(sc_data[SC_ORCISH].timer != -1)
+ status_change_end(bl,SC_ORCISH,-1);
+ if(sc_data[SC_STRIPWEAPON].timer != -1)
+ status_change_end(bl,SC_STRIPWEAPON,-1);
+ if(sc_data[SC_STRIPSHIELD].timer != -1)
+ status_change_end(bl,SC_STRIPSHIELD,-1);
+ if(sc_data[SC_STRIPARMOR].timer != -1)
+ status_change_end(bl,SC_STRIPARMOR,-1);
+ if(sc_data[SC_STRIPHELM].timer != -1)
+ status_change_end(bl,SC_STRIPHELM,-1);
+ return 0;
+}
+
+static int status_calc_sigma(void)
+{
+ int i,j,k;
+
+ for(i=0;i<MAX_PC_CLASS;i++) {
+ memset(hp_sigma_val[i],0,sizeof(hp_sigma_val[i]));
+ for(k=0,j=2;j<=MAX_LEVEL;j++) {
+ k += hp_coefficient[i]*j + 50;
+ k -= k%100;
+ hp_sigma_val[i][j-1] = k;
+ }
+ }
+ return 0;
+}
+
+int status_readdb(void) {
+ int i,j;
+ FILE *fp;
+ char line[1024], path[1024],*p;
+
+ sprintf(path, "%s/job_db1.txt", db_path);
+ fp=fopen(path,"r"); // Job-specific values (weight, HP, SP, ASPD)
+ if(fp==NULL){
+ ShowError("can't read %s\n", path);
+ return 1;
+ }
+ while(fgets(line, sizeof(line)-1, fp)){
+ char *split[23];
+ if(line[0]=='/' && line[1]=='/')
+ continue;
+ for(j=0,p=line;j<22 && p;j++){
+ split[j]=p;
+ p=strchr(p,',');
+ if(p) *p++=0;
+ }
+ if(j<22)
+ continue;
+ if(atoi(split[0])>=MAX_PC_CLASS)
+ continue;
+ max_weight_base[atoi(split[0])]=atoi(split[1]);
+ hp_coefficient[atoi(split[0])]=atoi(split[2]);
+ hp_coefficient2[atoi(split[0])]=atoi(split[3]);
+ sp_coefficient[atoi(split[0])]=atoi(split[4]);
+ for(j=0;j<17;j++)
+ aspd_base[atoi(split[0])][j]=atoi(split[j+5]);
+ }
+ fclose(fp);
+ ShowStatus("Done reading '"CL_WHITE"%s"CL_RESET"'.\n","job_db1.txt");
+
+ memset(job_bonus,0,sizeof(job_bonus)); // Job-specific stats bonus
+ sprintf(path, "%s/job_db2.txt", db_path);
+ fp=fopen(path,"r");
+ if(fp==NULL){
+ ShowError("can't read %s\n", path);
+ return 1;
+ }
+ while(fgets(line, sizeof(line)-1, fp)){
+ char *split[MAX_LEVEL+1]; //Job Level is limited to MAX_LEVEL, so the bonuses should likewise be limited to it. [Skotlex]
+ if(line[0]=='/' && line[1]=='/')
+ continue;
+ for(j=0,p=line;j<MAX_LEVEL+1 && p;j++){
+ split[j]=p;
+ p=strchr(p,',');
+ if(p) *p++=0;
+ }
+ if(atoi(split[0])>=MAX_PC_CLASS)
+ continue;
+ for(i=1;i<j && split[i];i++)
+ job_bonus[atoi(split[0])][i-1]=atoi(split[i]);
+ }
+ fclose(fp);
+ ShowStatus("Done reading '"CL_WHITE"%s"CL_RESET"'.\n",path);
+
+ // サイズ補正テ?ブル
+ for(i=0;i<3;i++)
+ for(j=0;j<20;j++)
+ atkmods[i][j]=100;
+ sprintf(path, "%s/size_fix.txt", db_path);
+ fp=fopen(path,"r");
+ if(fp==NULL){
+ ShowError("can't read %s\n", path);
+ return 1;
+ }
+ i=0;
+ while(fgets(line, sizeof(line)-1, fp)){
+ char *split[20];
+ if(line[0]=='/' && line[1]=='/')
+ continue;
+ if(atoi(line)<=0)
+ continue;
+ memset(split,0,sizeof(split));
+ for(j=0,p=line;j<20 && p;j++){
+ split[j]=p;
+ p=strchr(p,',');
+ if(p) *p++=0;
+ }
+ for(j=0;j<20 && split[j];j++)
+ atkmods[i][j]=atoi(split[j]);
+ i++;
+ }
+ fclose(fp);
+ ShowStatus("Done reading '"CL_WHITE"%s"CL_RESET"'.\n",path);
+
+ // 精?デ?タテ?ブル
+ for(i=0;i<5;i++){
+ for(j=0;j<MAX_REFINE; j++)
+ percentrefinery[i][j]=100;
+ percentrefinery[i][j]=0; //Slot MAX+1 always has 0% success chance [Skotlex]
+ refinebonus[i][0]=0;
+ refinebonus[i][1]=0;
+ refinebonus[i][2]=10;
+ }
+
+ sprintf(path, "%s/refine_db.txt", db_path);
+ fp=fopen(path,"r");
+ if(fp==NULL){
+ ShowError("can't read %s\n", path);
+ return 1;
+ }
+ i=0;
+ while(fgets(line, sizeof(line)-1, fp)){
+ char *split[16];
+ if(line[0]=='/' && line[1]=='/')
+ continue;
+ if(atoi(line)<=0)
+ continue;
+ memset(split,0,sizeof(split));
+ for(j=0,p=line;j<16 && p;j++){
+ split[j]=p;
+ p=strchr(p,',');
+ if(p) *p++=0;
+ }
+ refinebonus[i][0]=atoi(split[0]); // 精?ボ?ナス
+ refinebonus[i][1]=atoi(split[1]); // 過?精?ボ?ナス
+ refinebonus[i][2]=atoi(split[2]); // 安全精?限界
+ for(j=0;j<MAX_REFINE && split[j];j++)
+ percentrefinery[i][j]=atoi(split[j+3]);
+ i++;
+ }
+ fclose(fp); //Lupus. close this file!!!
+ ShowStatus("Done reading '"CL_WHITE"%s"CL_RESET"'.\n",path);
+
+ return 0;
+}
+
+/*==========================================
+ * スキル関係初期化処理
+ *------------------------------------------
+ */
+int do_init_status(void)
+{
+ if (SC_MAX > MAX_STATUSCHANGE)
+ {
+ ShowDebug("status.h defines %d status changes, but the MAX_STATUSCHANGE is %d! Fix it.\n", SC_MAX, MAX_STATUSCHANGE);
+ exit(1);
+ }
+ add_timer_func_list(status_change_timer,"status_change_timer");
+ initStatusIconChangeTable();
+ status_readdb();
+ status_calc_sigma();
+ return 0;
+}
diff --git a/src/map/status.h b/src/map/status.h
new file mode 100644
index 000000000..e31667d0c
--- /dev/null
+++ b/src/map/status.h
@@ -0,0 +1,529 @@
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#ifndef _STATUS_H_
+#define _STATUS_H_
+
+#include "map.h"
+
+// Status changes listing. These code are for use by the server.
+enum {
+ //First we enumerate common status ailments which are often used around.
+ SC_STONE = 0,
+ SC_FREEZE,
+ SC_STAN,
+ SC_SLEEP,
+ SC_POISON,
+ SC_CURSE,
+ SC_SILENCE,
+ SC_CONFUSION,
+ SC_BLIND,
+ SC_BLEEDING,
+ SC_DPOISON, //10
+
+ //Next up, we continue on 20, to leave enough room for additional "common" ailments in the future.
+ SC_PROVOKE = 20,
+ SC_ENDURE,
+ SC_TWOHANDQUICKEN,
+ SC_CONCENTRATE,
+ SC_HIDING,
+ SC_CLOAKING,
+ SC_ENCPOISON,
+ SC_POISONREACT,
+ SC_QUAGMIRE,
+ SC_ANGELUS,
+ SC_BLESSING, //30
+ SC_SIGNUMCRUCIS,
+ SC_INCREASEAGI,
+ SC_DECREASEAGI,
+ SC_SLOWPOISON,
+ SC_IMPOSITIO ,
+ SC_SUFFRAGIUM,
+ SC_ASPERSIO,
+ SC_BENEDICTIO,
+ SC_KYRIE,
+ SC_MAGNIFICAT, //40
+ SC_GLORIA,
+ SC_AETERNA,
+ SC_ADRENALINE,
+ SC_WEAPONPERFECTION,
+ SC_OVERTHRUST,
+ SC_MAXIMIZEPOWER,
+ SC_TRICKDEAD,
+ SC_LOUD,
+ SC_ENERGYCOAT,
+ SC_BROKENARMOR, //50 - NOTE: These two aren't used anywhere, and they have an icon...
+ SC_BROKENWEAPON,
+ SC_HALLUCINATION,
+ SC_WEIGHT50 ,
+ SC_WEIGHT90,
+ SC_ASPDPOTION0,
+ SC_ASPDPOTION1,
+ SC_ASPDPOTION2,
+ SC_ASPDPOTION3,
+ SC_SPEEDUP0,
+ SC_SPEEDUP1, //60
+ SC_ATKPOTION,
+ SC_MATKPOTION,
+ SC_WEDDING,
+ SC_SLOWDOWN,
+ SC_ANKLE,
+ SC_KEEPING,
+ SC_BARRIER,
+ SC_STRIPWEAPON,
+ SC_STRIPSHIELD,
+ SC_STRIPARMOR, //70
+ SC_STRIPHELM,
+ SC_CP_WEAPON,
+ SC_CP_SHIELD,
+ SC_CP_ARMOR,
+ SC_CP_HELM,
+ SC_AUTOGUARD,
+ SC_REFLECTSHIELD,
+ SC_SPLASHER,
+ SC_PROVIDENCE,
+ SC_DEFENDER, //80
+ SC_MAGICROD,
+ SC_SPELLBREAKER,
+ SC_AUTOSPELL,
+ SC_SIGHTTRASHER,
+ SC_AUTOBERSERK,
+ SC_SPEARSQUICKEN,
+ SC_AUTOCOUNTER,
+ SC_SIGHT,
+ SC_SAFETYWALL,
+ SC_RUWACH, //90
+ SC_EXTREMITYFIST,
+ SC_EXPLOSIONSPIRITS,
+ SC_COMBO,
+ SC_BLADESTOP_WAIT,
+ SC_BLADESTOP,
+ SC_FIREWEAPON,
+ SC_WATERWEAPON,
+ SC_WINDWEAPON,
+ SC_EARTHWEAPON,
+ SC_VOLCANO, //100,
+ SC_DELUGE,
+ SC_VIOLENTGALE,
+ SC_WATK_ELEMENT,
+ SC_LANDPROTECTOR,
+ SC_ARMOR_ELEMENT,
+ SC_NOCHAT,
+ SC_BABY,
+ SC_AURABLADE,
+ SC_PARRYING,
+ SC_CONCENTRATION, //110
+ SC_TENSIONRELAX,
+ SC_BERSERK,
+ SC_FURY,
+ SC_GOSPEL,
+ SC_ASSUMPTIO,
+ SC_BASILICA,
+ SC_GUILDAURA,
+ SC_MAGICPOWER,
+ SC_EDP,
+ SC_TRUESIGHT, //120
+ SC_WINDWALK,
+ SC_MELTDOWN,
+ SC_CARTBOOST,
+ SC_CHASEWALK,
+ SC_REJECTSWORD,
+ SC_MARIONETTE,
+ SC_MARIONETTE2,
+ SC_MOONLIT,
+ SC_JOINTBEAT,
+ SC_MINDBREAKER, //130
+ SC_MEMORIZE,
+ SC_FOGWALL,
+ SC_SPIDERWEB,
+ SC_DEVOTION,
+ SC_SACRIFICE,
+ SC_STEELBODY,
+ SC_ORCISH,
+ SC_READYSTORM,
+ SC_READYDOWN,
+ SC_READYTURN, //140
+ SC_READYCOUNTER,
+ SC_DODGE,
+ SC_RUN,
+ SC_SHADOWWEAPON,
+ SC_ADRENALINE2,
+ SC_GHOSTWEAPON,
+ SC_KAIZEL,
+ SC_KAAHI,
+ SC_KAUPE,
+ SC_ONEHAND, //150
+ SC_PRESERVE,
+ SC_BATTLEORDERS,
+ SC_REGENERATION,
+ SC_DOUBLECAST,
+ SC_GRAVITATION,
+ SC_MAXOVERTHRUST,
+ SC_LONGING,
+ SC_HERMODE,
+ SC_SHRINK,
+ SC_SIGHTBLASTER, //160
+ SC_WINKCHARM,
+ SC_CLOSECONFINE,
+ SC_CLOSECONFINE2,
+ SC_DANCING,
+ SC_LULLABY,
+ SC_RICHMANKIM,
+ SC_ETERNALCHAOS,
+ SC_DRUMBATTLE,
+ SC_NIBELUNGEN,
+ SC_ROKISWEIL, //170
+ SC_INTOABYSS,
+ SC_SIEGFRIED,
+ SC_WHISTLE,
+ SC_ASSNCROS,
+ SC_POEMBRAGI,
+ SC_APPLEIDUN,
+ SC_UGLYDANCE,
+ SC_HUMMING,
+ SC_DONTFORGETME,
+ SC_FORTUNE, //180
+ SC_SERVICE4U,
+ SC_STOP, //Prevents inflicted chars from walking. [Skotlex]
+ SC_SPURT,
+ SC_SPIRIT,
+ SC_COMA, //Not a real SC_, it makes a char's HP/SP hit 1.
+ SC_INTRAVISION,
+ SC_INCALLSTATUS,
+ SC_INCSTR,
+ SC_INCAGI,
+ SC_INCVIT, //190
+ SC_INCINT,
+ SC_INCDEX,
+ SC_INCLUK,
+ SC_INCHIT,
+ SC_INCHITRATE,
+ SC_INCFLEE,
+ SC_INCFLEERATE,
+ SC_INCMHPRATE,
+ SC_INCMSPRATE,
+ SC_INCATKRATE, //200
+ SC_INCMATKRATE,
+ SC_INCDEFRATE,
+ SC_STRFOOD,
+ SC_AGIFOOD,
+ SC_VITFOOD,
+ SC_INTFOOD,
+ SC_DEXFOOD,
+ SC_LUKFOOD,
+ SC_HITFOOD,
+ SC_FLEEFOOD, //210
+ SC_BATKFOOD,
+ SC_WATKFOOD,
+ SC_MATKFOOD,
+ SC_SCRESIST, //Increases resistance to status changes.
+ SC_XMAS, // Xmas Suit [Valaris]
+ SC_WARM, //SG skills [Komurka]
+ SC_SUN_COMFORT,
+ SC_MOON_COMFORT,
+ SC_STAR_COMFORT,
+ SC_FUSION, //220
+ SC_SKILLRATE_UP,
+ SC_SKE,
+ SC_KAITE,
+ SC_SWOO, // [marquis007]
+ SC_SKA, // [marquis007]
+ SC_TKDORI, // [marquis007]
+ //
+ SC_MAX, //Automatically updated max, used in for's and at startup to check we are within bounds. [Skotlex]
+};
+extern int SkillStatusChangeTable[];
+
+//Numerates the Number for the status changes (client-dependent), imported from jA
+enum {
+ SI_BLANK = -1,
+ SI_PROVOKE = 0,
+ SI_ENDURE = 1,
+ SI_TWOHANDQUICKEN = 2,
+ SI_CONCENTRATE = 3,
+ SI_HIDING = 4,
+ SI_CLOAKING = 5,
+ SI_ENCPOISON = 6,
+ SI_POISONREACT = 7,
+ SI_QUAGMIRE = 8,
+ SI_ANGELUS = 9,
+ SI_BLESSING = 10,
+ SI_SIGNUMCRUCIS = 11,
+ SI_INCREASEAGI = 12,
+ SI_DECREASEAGI = 13,
+ SI_SLOWPOISON = 14,
+ SI_IMPOSITIO = 15,
+ SI_SUFFRAGIUM = 16,
+ SI_ASPERSIO = 17,
+ SI_BENEDICTIO = 18,
+ SI_KYRIE = 19,
+ SI_MAGNIFICAT = 20,
+ SI_GLORIA = 21,
+ SI_AETERNA = 22,
+ SI_ADRENALINE = 23,
+ SI_WEAPONPERFECTION = 24,
+ SI_OVERTHRUST = 25,
+ SI_MAXIMIZEPOWER = 26,
+ SI_RIDING = 27,
+ SI_FALCON = 28,
+ SI_TRICKDEAD = 29,
+ SI_LOUD = 30,
+ SI_ENERGYCOAT = 31,
+ SI_BROKENARMOR = 32,
+ SI_BROKENWEAPON = 33,
+ SI_HALLUCINATION = 34,
+ SI_WEIGHT50 = 35,
+ SI_WEIGHT90 = 36,
+ SI_ASPDPOTION = 37,
+ //38: Again Aspd Potion
+ //39: Again Aspd Potion
+ //40: Again Aspd Potion
+ SI_SPEEDPOTION = 41,
+ //42: Again Speed Up
+ SI_STRIPWEAPON = 50,
+ SI_STRIPSHIELD = 51,
+ SI_STRIPARMOR = 52,
+ SI_STRIPHELM = 53,
+ SI_CP_WEAPON = 54,
+ SI_CP_SHIELD = 55,
+ SI_CP_ARMOR = 56,
+ SI_CP_HELM = 57,
+ SI_AUTOGUARD = 58,
+ SI_REFLECTSHIELD = 59,
+ SI_PROVIDENCE = 61,
+ SI_DEFENDER = 62,
+ SI_AUTOSPELL = 65,
+ SI_SPEARQUICKEN = 68,
+ SI_EXPLOSIONSPIRITS = 86,
+ SI_FURY = 87,
+ SI_FIREWEAPON = 90,
+ SI_WATERWEAPON = 91,
+ SI_WINDWEAPON = 92,
+ SI_EARTHWEAPON = 93,
+// 102 = again gloria - from what I saw on screenshots, I wonder if it isn't gospel... [DracoRPG]
+ SI_AURABLADE = 103,
+ SI_PARRYING = 104,
+ SI_CONCENTRATION = 105,
+ SI_TENSIONRELAX = 106,
+ SI_BERSERK = 107,
+ SI_ASSUMPTIO = 110,
+ SI_GUILDAURA = 112,
+ SI_MAGICPOWER = 113,
+ SI_EDP = 114,
+ SI_TRUESIGHT = 115,
+ SI_WINDWALK = 116,
+ SI_MELTDOWN = 117,
+ SI_CARTBOOST = 118,
+ SI_REJECTSWORD = 120,
+ SI_MARIONETTE = 121,
+ SI_MARIONETTE2 = 122,
+ SI_MOONLIT = 123,
+ SI_BLEEDING = 124,
+ SI_JOINTBEAT = 125,
+ SI_DEVOTION = 130,
+ SI_STEELBODY = 132,
+ SI_CHASEWALK = 134,
+ SI_READYSTORM = 135,
+ SI_READYDOWN = 137,
+ SI_READYTURN = 139,
+ SI_READYCOUNTER = 141,
+ SI_DODGE = 143,
+ SI_SPURT = 145,
+ SI_SHADOWWEAPON = 146,
+ SI_ADRENALINE2 = 147,
+ SI_GHOSTWEAPON = 148,
+ SI_NIGHT = 149,
+ SI_SPIRIT = 149,
+ SI_DEVIL = 152,
+ SI_KAITE = 153,
+ SI_KAIZEL = 156,
+ SI_KAAHI = 157,
+ SI_KAUPE = 158,
+// 159 = blue sparks and item-heal sound effect. Looks like item-use effect.
+// 160
+ SI_ONEHAND = 161,
+ SI_WARM = 165,
+// 166 | The three show the exact same display: ultra red character (165, 166, 167)
+// 167 |
+ SI_SUN_COMFORT = 169,
+ SI_MOON_COMFORT = 170,
+ SI_STAR_COMFORT = 171,
+ SI_PRESERVE = 181,
+ SI_BATTLEORDERS = 182,
+// 184 = WTF?? creates the black shape of 4_m_02 NPC, with NPC talk cursor
+ SI_DOUBLECAST = 186,
+ SI_MAXOVERTHRUST = 188,
+ SI_TAROT = 191, // the icon allows no doubt... but what is it really used for ?? [DracoRPG]
+ SI_SHRINK = 197,
+ SI_SIGHTBLASTER = 198,
+ SI_WINKCHARM = 199,
+ SI_CLOSECONFINE = 200,
+ SI_CLOSECONFINE2 = 201,
+};
+extern int StatusIconChangeTable[];
+
+extern int current_equip_item_index;
+
+//Mode definitions to clear up code reading. [Skotlex]
+#define MD_CANMOVE 0x001
+#define MD_LOOTER 0x002
+//MD_ANGRY mobs are also aggressive.
+#define MD_AGGRESSIVE 0x804
+#define MD_ASSIST 0x008
+#define MD_CASTSENSOR 0x010
+#define MD_BOSS 0x020
+#define MD_PLANT 0x040
+#define MD_CANATTACK 0x080
+#define MD_DETECTOR 0x100
+//#define MD_CHANGETARGET 0x200 //Mode deprecated, figured out through mob_can_changetarget()
+#define MD_CHANGECHASE 0x400
+#define MD_ANGRY 0x800
+#define MD_MASK 0xFFF
+
+//Status change option definitions (options are what makes status changes visible to chars
+//who were not on your field of sight when it happened)
+//opt1: Non stackable status changes.
+enum {
+ OPT1_STONE = 1, //Petrified
+ OPT1_FREEZE,
+ OPT1_STUN,
+ OPT1_SLEEP,
+ //What is 5?
+ OPT1_STONEWAIT=6 //Petrifying
+};
+
+//opt2: Stackable status changes.
+#define OPT2_POISON 0x001
+#define OPT2_CURSE 0x002
+#define OPT2_SILENCE 0x004
+//0x008 Odd howl sound, Signum crucis?
+#define OPT2_SIGNUMCRUCIS 0x008
+#define OPT2_BLIND 0x010
+//0x020 - nothing
+//0x040 - nothing
+#define OPT2_DPOISON 0x080
+//0x100
+
+//Opt3: Skill state changes, stackable.
+#define OPT3_SPEEDUP 0x001 //Quicken skills
+#define OPT3_POWERUP 0x002 //Power Thrust
+#define OPT3_SHIELD 0x004 //Energy Coat
+#define OPT3_FURY 0x008 //Explosion spirits
+#define OPT3_ELECTRIC 0x010 //Steel Body
+#define OPT3_STOP 0x020 //Blade Stop
+//64 Unknown
+#define OPT3_BERSERK 0x080 //Berserk
+//256 Unknown
+//512 Unknown
+#define OPT3_PINKAURA 0x400 //Marionette
+#define OPT3_AURASHIELD 0x800 //Assumptio
+#define OPT3_HEAT 0x1000 //Warmth Skills
+
+// パラメータ所得系 battle.c より移動
+int status_get_class(struct block_list *bl);
+int status_get_dir(struct block_list *bl);
+int status_get_lv(struct block_list *bl);
+int status_get_range(struct block_list *bl);
+int status_get_hp(struct block_list *bl);
+int status_get_max_hp(struct block_list *bl);
+int status_get_str(struct block_list *bl);
+int status_get_agi(struct block_list *bl);
+int status_get_vit(struct block_list *bl);
+int status_get_int(struct block_list *bl);
+int status_get_dex(struct block_list *bl);
+int status_get_luk(struct block_list *bl);
+int status_get_hit(struct block_list *bl);
+int status_get_flee(struct block_list *bl);
+int status_get_def(struct block_list *bl);
+int status_get_mdef(struct block_list *bl);
+int status_get_flee2(struct block_list *bl);
+int status_get_def2(struct block_list *bl);
+int status_get_mdef2(struct block_list *bl);
+int status_get_batk(struct block_list *bl);
+int status_get_atk(struct block_list *bl);
+int status_get_atk2(struct block_list *bl);
+int status_get_speed(struct block_list *bl);
+int status_get_adelay(struct block_list *bl);
+int status_get_amotion(struct block_list *bl);
+int status_get_dmotion(struct block_list *bl);
+int status_get_element(struct block_list *bl);
+int status_get_attack_sc_element(struct block_list *bl);
+int status_get_attack_element(struct block_list *bl);
+int status_get_attack_element2(struct block_list *bl); //左手武器属性取得
+#define status_get_elem_type(bl) (status_get_element(bl)%10)
+#define status_get_elem_level(bl) (status_get_element(bl)/10/2)
+int status_get_party_id(struct block_list *bl);
+int status_get_guild_id(struct block_list *bl);
+int status_get_race(struct block_list *bl);
+int status_get_size(struct block_list *bl);
+int status_get_mode(struct block_list *bl);
+int status_get_mexp(struct block_list *bl);
+int status_get_race2(struct block_list *bl);
+
+struct status_change *status_get_sc_data(struct block_list *bl);
+short *status_get_sc_count(struct block_list *bl);
+short *status_get_opt1(struct block_list *bl);
+short *status_get_opt2(struct block_list *bl);
+short *status_get_opt3(struct block_list *bl);
+short *status_get_option(struct block_list *bl);
+
+int status_get_matk1(struct block_list *bl);
+int status_get_matk2(struct block_list *bl);
+int status_get_critical(struct block_list *bl);
+int status_get_atk_(struct block_list *bl);
+int status_get_atk_2(struct block_list *bl);
+int status_get_atk2(struct block_list *bl);
+
+int status_isdead(struct block_list *bl);
+int status_isimmune(struct block_list *bl);
+
+int status_get_sc_def(struct block_list *bl, int type);
+#define status_get_sc_def_mdef(bl) (status_get_sc_def(bl, SP_MDEF1))
+#define status_get_sc_def_vit(bl) (status_get_sc_def(bl, SP_DEF2))
+#define status_get_sc_def_int(bl) (status_get_sc_def(bl, SP_MDEF2))
+#define status_get_sc_def_luk(bl) (status_get_sc_def(bl, SP_LUK))
+
+// 状態異常関連 skill.c より移動
+int status_change_start(struct block_list *bl,int type,int val1,int val2,int val3,int val4,int tick,int flag);
+int status_change_end( struct block_list* bl , int type,int tid );
+int status_change_timer(int tid, unsigned int tick, int id, int data);
+int status_change_timer_sub(struct block_list *bl, va_list ap );
+int status_change_clear(struct block_list *bl,int type);
+int status_change_clear_buffs(struct block_list *bl);
+int status_change_clear_debuffs(struct block_list *bl);
+
+int status_calc_pet(struct map_session_data* sd, int first); // [Skotlex]
+int status_calc_pc(struct map_session_data* sd,int first);
+int status_calc_str(struct block_list *,int);
+int status_calc_agi(struct block_list *,int);
+int status_calc_vit(struct block_list *,int);
+int status_calc_int(struct block_list *,int);
+int status_calc_dex(struct block_list *,int);
+int status_calc_luk(struct block_list *,int);
+int status_calc_batk(struct block_list *,int);
+int status_calc_watk(struct block_list *,int);
+int status_calc_matk(struct block_list *,int);
+int status_calc_hit(struct block_list *,int);
+int status_calc_critical(struct block_list *,int);
+int status_calc_flee(struct block_list *,int);
+int status_calc_flee2(struct block_list *,int);
+int status_calc_def(struct block_list *,int);
+int status_calc_def2(struct block_list *,int);
+int status_calc_mdef(struct block_list *,int);
+int status_calc_mdef2(struct block_list *,int);
+int status_calc_speed(struct block_list *,int);
+int status_calc_aspd_rate(struct block_list *,int);
+int status_calc_maxhp(struct block_list *,int);
+int status_calc_maxsp(struct block_list *,int);
+int status_quick_recalc_speed(struct map_session_data*, int, int, char); // [Celest] - modified by [Skotlex]
+int status_getrefinebonus(int lv,int type);
+int status_check_skilluse(struct block_list *src, struct block_list *target, int skill_num, int flag); // [Skotlex]
+
+//Use this to refer the max refinery level [Skotlex]
+#define MAX_REFINE 10
+extern int percentrefinery[5][MAX_REFINE+1]; //The last slot always has a 0% success chance [Skotlex]
+
+int status_readdb(void);
+int do_init_status(void);
+
+#endif
diff --git a/src/map/storage.c b/src/map/storage.c
new file mode 100644
index 000000000..b90d2d306
--- /dev/null
+++ b/src/map/storage.c
@@ -0,0 +1,693 @@
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "../common/nullpo.h"
+#include "../common/malloc.h"
+#include "../common/showmsg.h"
+
+#include "storage.h"
+#include "chrif.h"
+#include "itemdb.h"
+#include "clif.h"
+#include "intif.h"
+#include "pc.h"
+#include "guild.h"
+#include "battle.h"
+#include "atcommand.h"
+
+static struct dbt *storage_db;
+static struct dbt *guild_storage_db;
+
+/*==========================================
+ * 倉庫内アイテムソート
+ *------------------------------------------
+ */
+int storage_comp_item(const void *_i1, const void *_i2)
+{
+ struct item *i1 = (struct item *)_i1;
+ struct item *i2 = (struct item *)_i2;
+
+ if (i1->nameid == i2->nameid)
+ return 0;
+ else if (!(i1->nameid) || !(i1->amount))
+ return 1;
+ else if (!(i2->nameid) || !(i2->amount))
+ return -1;
+ return i1->nameid - i2->nameid;
+}
+
+void sortage_sortitem (struct storage *stor)
+{
+ nullpo_retv(stor);
+ qsort(stor->storage_, MAX_STORAGE, sizeof(struct item), storage_comp_item);
+}
+
+void sortage_gsortitem (struct guild_storage* gstor)
+{
+ nullpo_retv(gstor);
+ qsort(gstor->storage_, MAX_GUILD_STORAGE, sizeof(struct item), storage_comp_item);
+}
+
+/*==========================================
+ * 初期化とか
+ *------------------------------------------
+ */
+int do_init_storage(void) // map.c::do_init()から呼ばれる
+{
+ storage_db=db_alloc(__FILE__,__LINE__,DB_INT,DB_OPT_RELEASE_DATA,sizeof(int));
+ guild_storage_db=db_alloc(__FILE__,__LINE__,DB_INT,DB_OPT_RELEASE_DATA,sizeof(int));
+ return 1;
+}
+void do_final_storage(void) // by [MC Cameri]
+{
+ storage_db->destroy(storage_db,NULL);
+ guild_storage_db->destroy(guild_storage_db,NULL);
+}
+
+
+static int storage_reconnect_sub(DBKey key,void *data,va_list ap)
+{ //Parses storage and saves 'dirty' ones upon reconnect. [Skotlex]
+ int type = va_arg(ap, int);
+ if (type)
+ { //Guild Storage
+ struct guild_storage* stor = (struct guild_storage*) data;
+ if (stor->dirty && stor->storage_status == 0) //Save closed storages.
+ storage_guild_storagesave(0, stor->guild_id);
+ }
+ else
+ { //Account Storage
+ struct storage* stor = (struct storage*) data;
+ if (stor->dirty && stor->storage_status == 0) //Save closed storages.
+ storage_storage_save(stor->account_id);
+ }
+ return 0;
+}
+
+//Function to be invoked upon server reconnection to char. To save all 'dirty' storages [Skotlex
+void do_reconnect_storage(void)
+{
+ storage_db->foreach(storage_db, storage_reconnect_sub, 0);
+ guild_storage_db->foreach(guild_storage_db, storage_reconnect_sub, 1);
+}
+
+static void* create_storage(DBKey key, va_list args) {
+ struct storage *stor;
+ stor = (struct storage *) aCallocA (sizeof(struct storage), 1);
+ stor->account_id = key.i;
+ return stor;
+}
+struct storage *account2storage(int account_id)
+{
+ return idb_ensure(storage_db,account_id,create_storage);
+}
+
+// Just to ask storage, without creation
+struct storage *account2storage2(int account_id)
+{
+ return idb_get(storage_db, account_id);
+}
+
+int storage_delete(int account_id)
+{
+ idb_remove(storage_db,account_id);
+ return 0;
+}
+
+/*==========================================
+ * カプラ倉庫を開く
+ *------------------------------------------
+ */
+int storage_storageopen(struct map_session_data *sd)
+{
+//#ifdef TXT_ONLY
+ struct storage *stor;
+//#endif
+ nullpo_retr(0, sd);
+
+ if(sd->state.finalsave) //Refuse to open storage when you had your last save done.
+ return 1;
+
+ if( pc_can_give_items(pc_isGM(sd)) ) { //check is this GM level is allowed to put items to storage
+ clif_displaymessage(sd->fd, msg_txt(246));
+ return 1;
+ }
+//Storage loading always from sql idea from Komurka [Skotlex] - removed as it opens exploits when server lags.
+//#ifdef TXT_ONLY
+ if((stor = idb_get(storage_db,sd->status.account_id)) != NULL) {
+ if (stor->storage_status == 0 && sd->state.storage_flag == 0) {
+ stor->storage_status = 1;
+ sd->state.storage_flag = 1;
+ clif_storageitemlist(sd,stor);
+ clif_storageequiplist(sd,stor);
+ clif_updatestorageamount(sd,stor);
+ return 0;
+ }
+ } else
+//#endif
+ intif_request_storage(sd->status.account_id);
+
+ return 1;
+}
+
+/*==========================================
+ * カプラ倉庫へアイテム追加
+ *------------------------------------------
+ */
+int storage_additem(struct map_session_data *sd,struct storage *stor,struct item *item_data,int amount)
+{
+ struct item_data *data;
+ int i;
+
+ nullpo_retr(1, sd);
+ nullpo_retr(1, stor);
+ nullpo_retr(1, item_data);
+
+ if(item_data->nameid <= 0 || amount <= 0)
+ return 1;
+ nullpo_retr(1, data = itemdb_search(item_data->nameid));
+
+ if (!itemdb_canstore(item_data->nameid, pc_isGM(sd)))
+ { //Check if item is storable. [Skotlex]
+ clif_displaymessage (sd->fd, msg_txt(264));
+ return 1;
+ }
+
+ i=MAX_STORAGE;
+ if(!itemdb_isequip2(data)){
+ // 装備品ではないので、既所有品なら個数のみ変化させる
+ for(i=0;i<MAX_STORAGE;i++){
+ if( compare_item (&stor->storage_[i], item_data)) {
+ if(stor->storage_[i].amount+amount > MAX_AMOUNT)
+ return 1;
+ stor->storage_[i].amount+=amount;
+ clif_storageitemadded(sd,stor,i,amount);
+ break;
+ }
+ }
+ }
+ if(i>=MAX_STORAGE){
+ // 装備品か未所有品だったので空き欄へ追加
+ for(i=0;i<MAX_STORAGE;i++){
+ if(stor->storage_[i].nameid==0){
+ memcpy(&stor->storage_[i],item_data,sizeof(stor->storage_[0]));
+ stor->storage_[i].amount=amount;
+ stor->storage_amount++;
+ clif_storageitemadded(sd,stor,i,amount);
+ clif_updatestorageamount(sd,stor);
+ break;
+ }
+ }
+ if(i>=MAX_STORAGE)
+ return 1;
+ }
+
+ stor->dirty = 1;
+ return 0;
+}
+/*==========================================
+ * カプラ倉庫アイテムを減らす
+ *------------------------------------------
+ */
+int storage_delitem(struct map_session_data *sd,struct storage *stor,int n,int amount)
+{
+ nullpo_retr(1, sd);
+ nullpo_retr(1, stor);
+
+ if(stor->storage_[n].nameid==0 || stor->storage_[n].amount<amount)
+ return 1;
+
+ stor->storage_[n].amount-=amount;
+ if(stor->storage_[n].amount==0){
+ memset(&stor->storage_[n],0,sizeof(stor->storage_[0]));
+ stor->storage_amount--;
+ clif_updatestorageamount(sd,stor);
+ }
+ clif_storageitemremoved(sd,n,amount);
+
+ stor->dirty = 1;
+ return 0;
+}
+/*==========================================
+ * カプラ倉庫へ入れる
+ *------------------------------------------
+ */
+int storage_storageadd(struct map_session_data *sd,int index,int amount)
+{
+ struct storage *stor;
+
+ nullpo_retr(0, sd);
+ nullpo_retr(0, stor=account2storage2(sd->status.account_id));
+
+ if( (stor->storage_amount <= MAX_STORAGE) && (stor->storage_status == 1) ) { // storage not full & storage open
+ if(index>=0 && index<MAX_INVENTORY) { // valid index
+ if( (amount <= sd->status.inventory[index].amount) && (amount > 0) ) { //valid amount
+// log_tostorage(sd, index, 0);
+ if(storage_additem(sd,stor,&sd->status.inventory[index],amount)==0)
+ // remove item from inventory
+ pc_delitem(sd,index,amount,0);
+ } // valid amount
+ }// valid index
+ }// storage not full & storage open
+
+ return 0;
+}
+
+/*==========================================
+ * カプラ倉庫から出す
+ *------------------------------------------
+ */
+int storage_storageget(struct map_session_data *sd,int index,int amount)
+{
+ struct storage *stor;
+ int flag;
+
+ nullpo_retr(0, sd);
+ nullpo_retr(0, stor=account2storage2(sd->status.account_id));
+
+ if(stor->storage_status == 1) { // storage open
+ if(index>=0 && index<MAX_STORAGE) { // valid index
+ if( (amount <= stor->storage_[index].amount) && (amount > 0) ) { //valid amount
+ if((flag = pc_additem(sd,&stor->storage_[index],amount)) == 0)
+ storage_delitem(sd,stor,index,amount);
+ //else //taken out because it can dupe items if the above fails somehow :) [Kevin]
+ //clif_additem(sd,0,0,flag);
+// log_fromstorage(sd, index, 0);
+ } // valid amount
+ }// valid index
+ }// storage open
+
+ return 0;
+}
+/*==========================================
+ * カプラ倉庫へカートから入れる
+ *------------------------------------------
+ */
+int storage_storageaddfromcart(struct map_session_data *sd,int index,int amount)
+{
+ struct storage *stor;
+
+ nullpo_retr(0, sd);
+ nullpo_retr(0, stor=account2storage2(sd->status.account_id));
+
+ if( (stor->storage_amount <= MAX_STORAGE) && (stor->storage_status == 1) ) { // storage not full & storage open
+ if(index>=0 && index<MAX_INVENTORY) { // valid index
+ if( (amount <= sd->status.cart[index].amount) && (amount > 0) ) { //valid amount
+ if(storage_additem(sd,stor,&sd->status.cart[index],amount)==0)
+ pc_cart_delitem(sd,index,amount,0);
+ } // valid amount
+ }// valid index
+ }// storage not full & storage open
+
+ return 0;
+}
+
+/*==========================================
+ * カプラ倉庫からカートへ出す
+ *------------------------------------------
+ */
+int storage_storagegettocart(struct map_session_data *sd,int index,int amount)
+{
+ struct storage *stor;
+
+ nullpo_retr(0, sd);
+ nullpo_retr(0, stor=account2storage2(sd->status.account_id));
+
+ if(stor->storage_status == 1) { // storage open
+ if(index>=0 && index<MAX_STORAGE) { // valid index
+ if( (amount <= stor->storage_[index].amount) && (amount > 0) ) { //valid amount
+ if(pc_cart_additem(sd,&stor->storage_[index],amount)==0){
+ storage_delitem(sd,stor,index,amount);
+ }
+ } // valid amount
+ }// valid index
+ }// storage open
+
+ return 0;
+}
+
+
+/*==========================================
+ * Modified By Valaris to save upon closing [massdriller]
+ *------------------------------------------
+ */
+int storage_storageclose(struct map_session_data *sd)
+{
+ struct storage *stor;
+
+ nullpo_retr(0, sd);
+ nullpo_retr(0, stor=account2storage2(sd->status.account_id));
+
+ clif_storageclose(sd);
+ chrif_save(sd, 0); //This will invoke the storage save function as well. [Skotlex]
+
+ stor->storage_status=0;
+ sd->state.storage_flag = 0;
+ return 0;
+}
+
+/*==========================================
+ * ログアウト時開いているカプラ倉庫の保存
+ *------------------------------------------
+ */
+int storage_storage_quit(struct map_session_data *sd, int flag)
+{
+ struct storage *stor;
+
+ nullpo_retr(0, sd);
+
+ stor = account2storage2(sd->status.account_id);
+ if(stor) {
+ chrif_save(sd, flag); //Invokes the storage saving as well.
+ stor->storage_status = 0;
+ sd->state.storage_flag = 0;
+ }
+
+ return 0;
+}
+
+void storage_storage_dirty(struct map_session_data *sd)
+{
+ struct storage *stor;
+
+ stor=account2storage2(sd->status.account_id);
+
+ if(stor)
+ stor->dirty = 1;
+}
+
+int storage_storage_save(int account_id)
+{
+ struct storage *stor;
+
+ stor=account2storage2(account_id);
+ if(stor && stor->dirty)
+ {
+ intif_send_storage(stor);
+ return 1;
+ }
+
+ return 0;
+}
+
+//Ack from Char-server indicating the storage was saved. [Skotlex]
+int storage_storage_saved(int account_id)
+{
+ struct storage *stor;
+
+ if((stor=account2storage2(account_id)) != NULL)
+ { //Only mark it clean if it's not in use. [Skotlex]
+ if (stor->dirty && stor->storage_status == 0)
+ {
+ stor->dirty = 0;
+ sortage_sortitem(stor);
+ }
+ return 1;
+ }
+ return 0;
+}
+
+static void* create_guildstorage(DBKey key, va_list args) {
+ struct guild_storage *gs = NULL;
+ gs = (struct guild_storage *) aCallocA(sizeof(struct guild_storage), 1);
+ gs->guild_id=key.i;
+ return gs;
+}
+struct guild_storage *guild2storage(int guild_id)
+{
+ struct guild_storage *gs = NULL;
+ if(guild_search(guild_id) != NULL)
+ gs=(struct guild_storage *) idb_ensure(guild_storage_db,guild_id,create_guildstorage);
+ return gs;
+}
+
+struct guild_storage *guild2storage2(int guild_id)
+{ //For just locating a storage without creating one. [Skotlex]
+ return idb_get(guild_storage_db,guild_id);
+}
+
+int guild_storage_delete(int guild_id)
+{
+ idb_remove(guild_storage_db,guild_id);
+ return 0;
+}
+
+int storage_guild_storageopen(struct map_session_data *sd)
+{
+ struct guild_storage *gstor;
+
+ nullpo_retr(0, sd);
+
+ if(sd->status.guild_id <= 0)
+ return 2;
+
+ if(sd->state.finalsave) //Refuse to open storage when you had your last save done.
+ return 1;
+
+ if( pc_can_give_items(pc_isGM(sd)) ) { //check is this GM level can open guild storage and store items [Lupus]
+ clif_displaymessage(sd->fd, msg_txt(246));
+ return 1;
+ }
+
+ if((gstor = guild2storage2(sd->status.guild_id)) != NULL) {
+ if(gstor->storage_status)
+ return 1;
+ if(sd->state.storage_flag)
+ return 1; //Can't open both storages at a time.
+ gstor->storage_status = 1;
+ sd->state.storage_flag = 2;
+ clif_guildstorageitemlist(sd,gstor);
+ clif_guildstorageequiplist(sd,gstor);
+ clif_updateguildstorageamount(sd,gstor);
+ return 0;
+ }
+ else {
+ gstor = guild2storage(sd->status.guild_id);
+ gstor->storage_status = 1;
+ intif_request_guild_storage(sd->status.account_id,sd->status.guild_id);
+ }
+
+ return 0;
+}
+
+int guild_storage_additem(struct map_session_data *sd,struct guild_storage *stor,struct item *item_data,int amount)
+{
+ struct item_data *data;
+ int i;
+
+ nullpo_retr(1, sd);
+ nullpo_retr(1, stor);
+ nullpo_retr(1, item_data);
+ nullpo_retr(1, data = itemdb_search(item_data->nameid));
+
+ if(item_data->nameid <= 0 || amount <= 0)
+ return 1;
+
+ if (!itemdb_canguildstore(item_data->nameid, pc_isGM(sd)))
+ { //Check if item is storable. [Skotlex]
+ clif_displaymessage (sd->fd, msg_txt(264));
+ return 1;
+ }
+
+ i=MAX_GUILD_STORAGE;
+ if(!itemdb_isequip2(data)){
+ // 装備品ではないので、既所有品なら個数のみ変化させる
+ for(i=0;i<MAX_GUILD_STORAGE;i++){
+ if(compare_item(&stor->storage_[i], item_data)) {
+ if(stor->storage_[i].amount+amount > MAX_AMOUNT)
+ return 1;
+ stor->storage_[i].amount+=amount;
+ clif_guildstorageitemadded(sd,stor,i,amount);
+ break;
+ }
+ }
+ }
+ if(i>=MAX_GUILD_STORAGE){
+ // 装備品か未所有品だったので空き欄へ追加
+ for(i=0;i<MAX_GUILD_STORAGE;i++){
+ if(stor->storage_[i].nameid==0){
+ memcpy(&stor->storage_[i],item_data,sizeof(stor->storage_[0]));
+ stor->storage_[i].amount=amount;
+ stor->storage_amount++;
+ clif_guildstorageitemadded(sd,stor,i,amount);
+ clif_updateguildstorageamount(sd,stor);
+ break;
+ }
+ }
+ if(i>=MAX_GUILD_STORAGE)
+ return 1;
+ }
+ stor->dirty = 1;
+ return 0;
+}
+
+int guild_storage_delitem(struct map_session_data *sd,struct guild_storage *stor,int n,int amount)
+{
+ nullpo_retr(1, sd);
+ nullpo_retr(1, stor);
+
+ if(stor->storage_[n].nameid==0 || stor->storage_[n].amount<amount)
+ return 1;
+
+ stor->storage_[n].amount-=amount;
+ if(stor->storage_[n].amount==0){
+ memset(&stor->storage_[n],0,sizeof(stor->storage_[0]));
+ stor->storage_amount--;
+ clif_updateguildstorageamount(sd,stor);
+ }
+ clif_storageitemremoved(sd,n,amount);
+ stor->dirty = 1;
+ return 0;
+}
+
+int storage_guild_storageadd(struct map_session_data *sd,int index,int amount)
+{
+ struct guild_storage *stor;
+
+ nullpo_retr(0, sd);
+
+ if((stor=guild2storage2(sd->status.guild_id)) != NULL) {
+ if( (stor->storage_amount <= MAX_GUILD_STORAGE) && (stor->storage_status == 1) ) { // storage not full & storage open
+ if(index>=0 && index<MAX_INVENTORY) { // valid index
+ if( (amount <= sd->status.inventory[index].amount) && (amount > 0) ) { //valid amount
+// log_tostorage(sd, index, 1);
+ if(guild_storage_additem(sd,stor,&sd->status.inventory[index],amount)==0)
+ // remove item from inventory
+ pc_delitem(sd,index,amount,0);
+ } // valid amount
+ }// valid index
+ }// storage not full & storage open
+ }
+
+ return 0;
+}
+
+int storage_guild_storageget(struct map_session_data *sd,int index,int amount)
+{
+ struct guild_storage *stor;
+ int flag;
+
+ nullpo_retr(0, sd);
+
+ if((stor=guild2storage2(sd->status.guild_id)) != NULL) {
+ if(stor->storage_status == 1) { // storage open
+ if(index>=0 && index<MAX_GUILD_STORAGE) { // valid index
+ if( (amount <= stor->storage_[index].amount) && (amount > 0) ) { //valid amount
+ if((flag = pc_additem(sd,&stor->storage_[index],amount)) == 0)
+ guild_storage_delitem(sd,stor,index,amount);
+ else
+ clif_additem(sd,0,0,flag);
+// log_fromstorage(sd, index, 1);
+ } // valid amount
+ }// valid index
+ }// storage open
+ }
+
+ return 0;
+}
+
+int storage_guild_storageaddfromcart(struct map_session_data *sd,int index,int amount)
+{
+ struct guild_storage *stor;
+
+ nullpo_retr(0, sd);
+
+ if((stor=guild2storage2(sd->status.guild_id)) != NULL) {
+ if( (stor->storage_amount <= MAX_GUILD_STORAGE) && (stor->storage_status == 1) ) { // storage not full & storage open
+ if(index>=0 && index<MAX_INVENTORY) { // valid index
+ if( (amount <= sd->status.cart[index].amount) && (amount > 0) ) { //valid amount
+ if(guild_storage_additem(sd,stor,&sd->status.cart[index],amount)==0)
+ pc_cart_delitem(sd,index,amount,0);
+ } // valid amount
+ }// valid index
+ }// storage not full & storage open
+ }
+
+ return 0;
+}
+
+int storage_guild_storagegettocart(struct map_session_data *sd,int index,int amount)
+{
+ struct guild_storage *stor;
+
+ nullpo_retr(0, sd);
+
+ if((stor=guild2storage2(sd->status.guild_id)) != NULL) {
+ if(stor->storage_status == 1) { // storage open
+ if(index>=0 && index<MAX_GUILD_STORAGE) { // valid index
+ if( (amount <= stor->storage_[index].amount) && (amount > 0) ) { //valid amount
+ if(pc_cart_additem(sd,&stor->storage_[index],amount)==0){
+ guild_storage_delitem(sd,stor,index,amount);
+ }
+ } // valid amount
+ }// valid index
+ }// storage open
+ }
+
+ return 0;
+}
+
+int storage_guild_storagesave(int account_id, int guild_id)
+{
+ struct guild_storage *stor = guild2storage2(guild_id);
+
+ if(stor && stor->dirty)
+ {
+ intif_send_guild_storage(account_id,stor);
+ return 1;
+ }
+ return 0;
+}
+
+int storage_guild_storagesaved(int account_id, int guild_id)
+{
+ struct guild_storage *stor;
+
+ if((stor=guild2storage2(guild_id)) != NULL) {
+ if (stor->dirty && stor->storage_status == 0)
+ { //Storage has been correctly saved.
+ stor->dirty = 0;
+ sortage_gsortitem(stor);
+ }
+ return 1;
+ }
+ return 0;
+}
+
+int storage_guild_storageclose(struct map_session_data *sd)
+{
+ struct guild_storage *stor;
+
+ nullpo_retr(0, sd);
+ nullpo_retr(0, stor=guild2storage2(sd->status.guild_id));
+
+ clif_storageclose(sd);
+ chrif_save(sd, 0); //This one also saves the storage. [Skotlex]
+
+ stor->storage_status=0;
+ sd->state.storage_flag = 0;
+
+ return 0;
+}
+
+int storage_guild_storage_quit(struct map_session_data *sd,int flag)
+{
+ struct guild_storage *stor;
+
+ nullpo_retr(0, sd);
+ nullpo_retr(0, stor=guild2storage2(sd->status.guild_id));
+
+ sd->state.storage_flag = 0;
+ stor->storage_status = 0;
+
+ chrif_save(sd,flag);
+ if(!flag) //Only during a guild break flag is 1.
+ storage_guild_storagesave(sd->status.account_id,sd->status.guild_id);
+ else //When the guild was broken, close the storage of he who has it open.
+ clif_storageclose(sd);
+
+ return 0;
+}
diff --git a/src/map/storage.h b/src/map/storage.h
new file mode 100644
index 000000000..946dfa62b
--- /dev/null
+++ b/src/map/storage.h
@@ -0,0 +1,45 @@
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#ifndef _STORAGE_H_
+#define _STORAGE_H_
+
+#include "../common/mmo.h"
+
+int storage_storageopen(struct map_session_data *sd);
+int storage_storageadd(struct map_session_data *sd,int index,int amount);
+int storage_storageget(struct map_session_data *sd,int index,int amount);
+int storage_storageaddfromcart(struct map_session_data *sd,int index,int amount);
+int storage_storagegettocart(struct map_session_data *sd,int index,int amount);
+int storage_storageclose(struct map_session_data *sd);
+int do_init_storage(void);
+void do_final_storage(void);
+void do_reconnect_storage(void);
+struct storage *account2storage(int account_id);
+struct storage *account2storage2(int account_id);
+int storage_delete(int account_id);
+int storage_storage_quit(struct map_session_data *sd, int flag);
+int storage_storage_save(int account_id);
+int storage_storage_saved(int account_id); //Ack from char server that guild store was saved.
+void storage_storage_dirty(struct map_session_data *sd);
+
+struct guild_storage *guild2storage(int guild_id);
+int guild_storage_delete(int guild_id);
+int storage_guild_storageopen(struct map_session_data *sd);
+int guild_storage_additem(struct map_session_data *sd,struct guild_storage *stor,struct item *item_data,int amount);
+int guild_storage_delitem(struct map_session_data *sd,struct guild_storage *stor,int n,int amount);
+int storage_guild_storageadd(struct map_session_data *sd,int index,int amount);
+int storage_guild_storageget(struct map_session_data *sd,int index,int amount);
+int storage_guild_storageaddfromcart(struct map_session_data *sd,int index,int amount);
+int storage_guild_storagegettocart(struct map_session_data *sd,int index,int amount);
+int storage_guild_storageclose(struct map_session_data *sd);
+int storage_guild_storage_quit(struct map_session_data *sd,int flag);
+int storage_guild_storagesave(int account_id, int guild_id);
+int storage_guild_storagesaved(int account_id, int guild_id); //Ack from char server that guild store was saved.
+
+int storage_comp_item(const void *_i1, const void *_i2);
+//int storage_comp_item(const struct item* i1, const struct item* i2);
+void sortage_sortitem(struct storage* stor);
+void sortage_gsortitem(struct guild_storage* gstor);
+
+#endif
diff --git a/src/map/trade.c b/src/map/trade.c
new file mode 100644
index 000000000..c9b9b21b0
--- /dev/null
+++ b/src/map/trade.c
@@ -0,0 +1,569 @@
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#include <stdio.h>
+#include <string.h>
+
+#include "../common/nullpo.h"
+#include "clif.h"
+#include "itemdb.h"
+#include "map.h"
+#include "trade.h"
+#include "pc.h"
+#include "npc.h"
+#include "battle.h"
+#include "chrif.h"
+#include "storage.h"
+#include "intif.h"
+#include "atcommand.h"
+#include "log.h"
+
+
+/*==========================================
+ * 取引要請を相手に送る
+ *------------------------------------------
+ */
+void trade_traderequest(struct map_session_data *sd, int target_id) {
+ struct map_session_data *target_sd;
+ int level;
+
+ nullpo_retv(sd);
+
+ if ((target_sd = map_id2sd(target_id)) != NULL) {
+ if (!battle_config.invite_request_check) {
+ if (target_sd->guild_invite > 0 || target_sd->party_invite > 0) {
+ clif_tradestart(sd, 2); // 相手はPT要請中かGuild要請中
+ return;
+ }
+ }
+ level = pc_isGM(sd);
+ if ( pc_can_give_items(level) || pc_can_give_items(pc_isGM(target_sd)) ) //check if both GMs are allowed to trade
+ {
+ clif_displaymessage(sd->fd, msg_txt(246));
+ trade_tradecancel(sd); // GM is not allowed to trade
+ } else if ((target_sd->trade_partner != 0) || (sd->trade_partner != 0)) {
+ trade_tradecancel(sd); // person is in another trade
+ } else {
+ //Fixed. Only real GMs can request trade from far away! [Lupus]
+ if (level < lowest_gm_level && (sd->bl.m != target_sd->bl.m ||
+ (sd->bl.x - target_sd->bl.x <= -5 || sd->bl.x - target_sd->bl.x >= 5) ||
+ (sd->bl.y - target_sd->bl.y <= -5 || sd->bl.y - target_sd->bl.y >= 5))) {
+ clif_tradestart(sd, 0); // too far
+ } else if (sd != target_sd) {
+ target_sd->trade_partner = sd->status.account_id;
+ sd->trade_partner = target_sd->status.account_id;
+ clif_traderequest(target_sd, sd->status.name);
+ }
+ }
+ } else {
+ clif_tradestart(sd, 1); // character does not exist
+ }
+}
+
+/*==========================================
+ * 取引要請
+ *------------------------------------------
+ */
+void trade_tradeack(struct map_session_data *sd, int type) {
+ struct map_session_data *target_sd;
+ nullpo_retv(sd);
+
+ if ((target_sd = map_id2sd(sd->trade_partner)) != NULL) {
+ clif_tradestart(target_sd, type);
+ clif_tradestart(sd, type);
+ if (type == 4) { // Cancel
+ sd->state.deal_locked = 0;
+ sd->trade_partner = 0;
+ target_sd->state.deal_locked = 0;
+ target_sd->trade_partner = 0;
+ }
+
+
+ if (type == 3) { //Initiate trade
+ memset(&sd->deal, 0, sizeof(sd->deal));
+ memset(&target_sd->deal, 0, sizeof(target_sd->deal));
+ }
+
+ if (sd->npc_id != 0)
+ npc_event_dequeue(sd);
+ if (target_sd->npc_id != 0)
+ npc_event_dequeue(target_sd);
+
+ //close STORAGE window if it's open. It protects from spooffing packets [Lupus]
+ if (sd->state.storage_flag == 1)
+ storage_storageclose(sd);
+ else if (sd->state.storage_flag == 2)
+ storage_guild_storageclose(sd);
+ }
+}
+
+/*==========================================
+ * Check here hacker for duplicate item in trade
+ * normal client refuse to have 2 same types of item (except equipment) in same trade window
+ * normal client authorise only no equiped item and only from inventory
+ *------------------------------------------
+ */
+int impossible_trade_check(struct map_session_data *sd) {
+ struct item inventory[MAX_INVENTORY];
+ char message_to_gm[200];
+ int i, index;
+
+ nullpo_retr(1, sd);
+
+ if(sd->deal.zeny > sd->status.zeny)
+ {
+ pc_setglobalreg(sd,"ZENY_HACKER",1);
+ return -1;
+ }
+
+ // get inventory of player
+ memcpy(&inventory, &sd->status.inventory, sizeof(struct item) * MAX_INVENTORY);
+
+ // remove this part: arrows can be trade and equiped
+ // re-added! [celest]
+ // remove equiped items (they can not be trade)
+ for (i = 0; i < MAX_INVENTORY; i++)
+ if (inventory[i].nameid > 0 && inventory[i].equip && !(inventory[i].equip & 0x8000))
+ memset(&inventory[i], 0, sizeof(struct item));
+
+ // check items in player inventory
+ for(i = 0; i < 10; i++)
+ if (sd->deal.item[i].amount < 0) { // negativ? -> hack
+// printf("Negativ amount in trade, by hack!\n"); // normal client send cancel when we type negativ amount
+ return -1;
+ } else if (sd->deal.item[i].amount > 0) {
+ index = sd->deal.item[i].index;
+ inventory[index].amount -= sd->deal.item[i].amount; // remove item from inventory
+// printf("%d items left\n", inventory[index].amount);
+ if (inventory[index].amount < 0) { // if more than the player have -> hack
+// printf("A player try to trade more items that he has: hack!\n");
+ sprintf(message_to_gm, msg_txt(538), sd->status.name, sd->status.account_id); // Hack on trade: character '%s' (account: %d) try to trade more items that he has.
+ intif_wis_message_to_gm(wisp_server_name, battle_config.hack_info_GM_level, message_to_gm);
+ sprintf(message_to_gm, msg_txt(539), sd->status.inventory[index].amount, sd->status.inventory[index].nameid, sd->status.inventory[index].amount - inventory[index].amount); // This player has %d of a kind of item (id: %d), and try to trade %d of them.
+ intif_wis_message_to_gm(wisp_server_name, battle_config.hack_info_GM_level, message_to_gm);
+ // if we block people
+ if (battle_config.ban_hack_trade < 0) {
+ chrif_char_ask_name(-1, sd->status.name, 1, 0, 0, 0, 0, 0, 0); // type: 1 - block
+ clif_setwaitclose(sd->fd); // forced to disconnect because of the hack
+ // message about the ban
+ sprintf(message_to_gm, msg_txt(540), battle_config.ban_spoof_namer); // This player has been definitivly blocked.
+ // if we ban people
+ } else if (battle_config.ban_hack_trade > 0) {
+ chrif_char_ask_name(-1, sd->status.name, 2, 0, 0, 0, 0, battle_config.ban_hack_trade, 0); // type: 2 - ban (year, month, day, hour, minute, second)
+ clif_setwaitclose(sd->fd); // forced to disconnect because of the hack
+ // message about the ban
+ sprintf(message_to_gm, msg_txt(507), battle_config.ban_spoof_namer); // This player has been banned for %d minute(s).
+ } else {
+ // message about the ban
+ sprintf(message_to_gm, msg_txt(508)); // This player hasn't been banned (Ban option is disabled).
+ }
+ intif_wis_message_to_gm(wisp_server_name, battle_config.hack_info_GM_level, message_to_gm);
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * Check here if we can add item in inventory (against full inventory)
+ *------------------------------------------
+ */
+int trade_check(struct map_session_data *sd) {
+ struct item inventory[MAX_INVENTORY];
+ struct item inventory2[MAX_INVENTORY];
+ struct item_data *data;
+ struct map_session_data *target_sd;
+ int trade_i, i, amount;
+
+ target_sd = map_id2sd(sd->trade_partner);
+
+ // get inventory of player
+ memcpy(&inventory, &sd->status.inventory, sizeof(struct item) * MAX_INVENTORY);
+ memcpy(&inventory2, &target_sd->status.inventory, sizeof(struct item) * MAX_INVENTORY);
+
+ // check free slot in both inventory
+ for(trade_i = 0; trade_i < 10; trade_i++) {
+ amount = sd->deal.item[trade_i].amount;
+ if (amount > 0) {
+ int n = sd->deal.item[trade_i].index;
+ // check quantity
+ if (amount > inventory[n].amount)
+ amount = inventory[n].amount;
+ if (amount > 0) {
+ data = itemdb_search(inventory[n].nameid);
+ i = MAX_INVENTORY;
+ // check for non-equipement item
+ if (!itemdb_isequip2(data)) {
+ for(i = 0; i < MAX_INVENTORY; i++)
+ if (inventory2[i].nameid == inventory[n].nameid &&
+ inventory2[i].card[0] == inventory[n].card[0] && inventory2[i].card[1] == inventory[n].card[1] &&
+ inventory2[i].card[2] == inventory[n].card[2] && inventory2[i].card[3] == inventory[n].card[3]) {
+ if (inventory2[i].amount + amount > MAX_AMOUNT) {
+ clif_displaymessage(sd->fd, msg_txt(592)); // Trade can not be done, because one of your doesn't have enough free slots in its inventory.
+ clif_displaymessage(target_sd->fd, msg_txt(592)); // Trade can not be done, because one of your doesn't have enough free slots in its inventory.
+ return 0;
+ }
+ inventory2[i].amount += amount;
+ inventory[n].amount -= amount;
+ if (inventory[n].amount <= 0)
+ memset(&inventory[n], 0, sizeof(struct item));
+ break;
+ }
+ }
+ // check for equipement
+ if (i == MAX_INVENTORY) {
+ for(i = 0; i < MAX_INVENTORY; i++) {
+ if (inventory2[i].nameid == 0) {
+ memcpy(&inventory2[i], &inventory[n], sizeof(struct item));
+ inventory2[i].amount = amount;
+ inventory[n].amount -= amount;
+ if (inventory[n].amount <= 0)
+ memset(&inventory[n], 0, sizeof(struct item));
+ break;
+ }
+ }
+ if (i == MAX_INVENTORY) {
+ clif_displaymessage(sd->fd, msg_txt(592)); // Trade can not be done, because one of your doesn't have enough free slots in its inventory.
+ clif_displaymessage(target_sd->fd, msg_txt(592)); // Trade can not be done, because one of your doesn't have enough free slots in its inventory.
+ return 0;
+ }
+ }
+ }
+ }
+ amount = target_sd->deal.item[trade_i].amount;
+ if (amount > 0) {
+ int n = target_sd->deal.item[trade_i].index;
+ // check quantity
+ if (amount > inventory2[n].amount)
+ amount = inventory2[n].amount;
+ if (amount > 0) {
+ // search if it's possible to add item (for full inventory)
+ data = itemdb_search(inventory2[n].nameid);
+ i = MAX_INVENTORY;
+ if (!itemdb_isequip2(data)) {
+ for(i = 0; i < MAX_INVENTORY; i++)
+ if (inventory[i].nameid == inventory2[n].nameid &&
+ inventory[i].card[0] == inventory2[n].card[0] && inventory[i].card[1] == inventory2[n].card[1] &&
+ inventory[i].card[2] == inventory2[n].card[2] && inventory[i].card[3] == inventory2[n].card[3]) {
+ if (inventory[i].amount + amount > MAX_AMOUNT) {
+ clif_displaymessage(sd->fd, msg_txt(592)); // Trade can not be done, because one of your doesn't have enough free slots in its inventory.
+ clif_displaymessage(target_sd->fd, msg_txt(592)); // Trade can not be done, because one of your doesn't have enough free slots in its inventory.
+ return 0;
+ }
+ inventory[i].amount += amount;
+ inventory2[n].amount -= amount;
+ if (inventory2[n].amount <= 0)
+ memset(&inventory2[n], 0, sizeof(struct item));
+ break;
+ }
+ }
+ if (i == MAX_INVENTORY) {
+ for(i = 0; i < MAX_INVENTORY; i++) {
+ if (inventory[i].nameid == 0) {
+ memcpy(&inventory[i], &inventory2[n], sizeof(struct item));
+ inventory[i].amount = amount;
+ inventory2[n].amount -= amount;
+ if (inventory2[n].amount <= 0)
+ memset(&inventory2[n], 0, sizeof(struct item));
+ break;
+ }
+ }
+ if (i == MAX_INVENTORY) {
+ clif_displaymessage(sd->fd, msg_txt(592)); // Trade can not be done, because one of your doesn't have enough free slots in its inventory.
+ clif_displaymessage(target_sd->fd, msg_txt(592)); // Trade can not be done, because one of your doesn't have enough free slots in its inventory.
+ return 0;
+ }
+ }
+ }
+ }
+ }
+
+ return 1;
+}
+
+/*==========================================
+ * Adds an item/qty to the trade window [rewrite by Skotlex]
+ *------------------------------------------
+ */
+void trade_tradeadditem(struct map_session_data *sd, int index, int amount) {
+ struct map_session_data *target_sd;
+ int trade_i, trade_weight, nameid;
+
+ nullpo_retv(sd);
+ if ((target_sd = map_id2sd(sd->trade_partner)) == NULL || sd->state.deal_locked > 0)
+ return; //Can't add stuff.
+
+ if (index == 0)
+ { //Adding Zeny
+ if (amount >= 0 && amount <= MAX_ZENY && amount <= sd->status.zeny && // check amount
+ (target_sd->status.zeny + amount) <= MAX_ZENY) // fix positiv overflow
+ { //Check Ok
+ sd->deal.zeny = amount;
+ clif_tradeadditem(sd, target_sd, 0, amount);
+ } else //Cancel Transaction
+ trade_tradecancel(sd);
+ return;
+ }
+ //Add an Item
+ index = index -2; //Why the actual index used is -2?
+ //Item checks...
+ if (index < 0 || index > MAX_INVENTORY)
+ return;
+ if (amount < 0 || amount > sd->status.inventory[index].amount)
+ return;
+
+ nameid = sd->inventory_data[index]->nameid;
+
+ if (!itemdb_cantrade(nameid, pc_isGM(sd), pc_isGM(target_sd)) && //Can't trade
+ (pc_get_partner(sd) != target_sd || !itemdb_canpartnertrade(nameid, pc_isGM(sd), pc_isGM(target_sd)))) //Can't partner-trade
+ {
+ clif_displaymessage (sd->fd, msg_txt(260));
+ return;
+ }
+
+ for(trade_i = 0; trade_i < 10; trade_i++)
+ { //Locate a trade position
+ if (sd->deal.item[trade_i].index == index ||
+ sd->deal.item[trade_i].amount == 0)
+ break;
+ }
+ if (trade_i >= 10) //No space left
+ return;
+
+ trade_weight = sd->inventory_data[index]->weight * amount;
+ if (target_sd->weight + sd->deal.weight + trade_weight > target_sd->max_weight)
+ { //fail to add item -- the player was over weighted.
+ clif_tradeitemok(sd, index, 1);
+ return;
+ }
+
+ if (sd->deal.item[trade_i].index == index)
+ { //The same item as before is being readjusted.
+ if (sd->deal.item[trade_i].amount + amount > sd->status.inventory[index].amount)
+ { //packet deal exploit check
+ amount = sd->status.inventory[index].amount - sd->deal.item[trade_i].amount;
+ trade_weight = sd->inventory_data[index]->weight * amount;
+ }
+ sd->deal.item[trade_i].amount += amount;
+ } else { //New deal item
+ sd->deal.item[trade_i].index = index;
+ sd->deal.item[trade_i].amount = amount;
+ }
+ sd->deal.weight += trade_weight;
+
+ if (impossible_trade_check(sd))
+ { // check exploit (trade more items that you have)
+ trade_tradecancel(sd);
+ return;
+ }
+
+ clif_tradeitemok(sd, index+2, 0); // Return the index as it was received
+ clif_tradeadditem(sd, target_sd, index+2, amount); //index fix
+}
+
+/*==========================================
+ * アイテム追加完了(ok押し)
+ *------------------------------------------
+ */
+void trade_tradeok(struct map_session_data *sd) {
+ struct map_session_data *target_sd;
+ int trade_i;
+
+ nullpo_retv(sd);
+
+ // check items
+ for(trade_i = 0; trade_i < 10; trade_i++) {
+ if ((((sd->deal.item[trade_i].index) >= 0) &&
+ (sd->deal.item[trade_i].amount > sd->status.inventory[sd->deal.item[trade_i].index].amount)) ||
+ (sd->deal.item[trade_i].amount < 0)) {
+ trade_tradecancel(sd);
+ return;
+ }
+ }
+
+ // check exploit (trade more items that you have)
+ if (impossible_trade_check(sd)) {
+ trade_tradecancel(sd);
+ return;
+ }
+
+ // check zeny
+ if (sd->deal.zeny < 0 || sd->deal.zeny > MAX_ZENY || sd->deal.zeny > sd->status.zeny) { // check amount
+ trade_tradecancel(sd);
+ return;
+ }
+
+ if ((target_sd = map_id2sd(sd->trade_partner)) != NULL) {
+ sd->state.deal_locked = 1;
+ clif_tradeitemok(sd, 0, 0);
+ clif_tradedeal_lock(sd, 0);
+ clif_tradedeal_lock(target_sd, 1);
+ }
+}
+
+/*==========================================
+ * 取引キャンセル
+ *------------------------------------------
+ */
+void trade_tradecancel(struct map_session_data *sd) {
+ struct map_session_data *target_sd;
+ int trade_i;
+
+ nullpo_retv(sd);
+
+ if ((target_sd = map_id2sd(sd->trade_partner)) != NULL) {
+ for(trade_i = 0; trade_i < 10; trade_i++) { // give items back (only virtual)
+ if (sd->deal.item[trade_i].amount != 0) {
+ clif_additem(sd, sd->deal.item[trade_i].index, sd->deal.item[trade_i].amount, 0);
+ sd->deal.item[trade_i].index = 0;
+ sd->deal.item[trade_i].amount = 0;
+ }
+ if (target_sd->deal.item[trade_i].amount != 0) {
+ clif_additem(target_sd, target_sd->deal.item[trade_i].index, target_sd->deal.item[trade_i].amount, 0);
+ target_sd->deal.item[trade_i].index = 0;
+ target_sd->deal.item[trade_i].amount = 0;
+ }
+ }
+ if (sd->deal.zeny) {
+ clif_updatestatus(sd, SP_ZENY);
+ sd->deal.zeny = 0;
+ }
+ if (target_sd->deal.zeny) {
+ clif_updatestatus(target_sd, SP_ZENY);
+ target_sd->deal.zeny = 0;
+ }
+ sd->state.deal_locked = 0;
+ sd->trade_partner = 0;
+ target_sd->state.deal_locked = 0;
+ target_sd->trade_partner = 0;
+ clif_tradecancelled(sd);
+ clif_tradecancelled(target_sd);
+ }
+}
+
+/*==========================================
+ * 取引許諾(trade押し)
+ *------------------------------------------
+ */
+void trade_tradecommit(struct map_session_data *sd) {
+ struct map_session_data *target_sd;
+ int trade_i;
+ int flag;
+
+ nullpo_retv(sd);
+
+ if ((target_sd = map_id2sd(sd->trade_partner)) != NULL) {
+ if ((sd->state.deal_locked >= 1) && (target_sd->state.deal_locked >= 1)) { // both have pressed 'ok'
+ if (sd->state.deal_locked < 2) { // set locked to 2
+ sd->state.deal_locked = 2;
+ }
+ if (target_sd->state.deal_locked == 2) { // the other one pressed 'trade' too
+ // check exploit (trade more items that you have)
+ if (impossible_trade_check(sd)) {
+ trade_tradecancel(sd);
+ return;
+ }
+ // check exploit (trade more items that you have)
+ if (impossible_trade_check(target_sd)) {
+ trade_tradecancel(target_sd);
+ return;
+ }
+ // check zenys value against hackers
+ if (sd->deal.zeny >= 0 && sd->deal.zeny <= MAX_ZENY && sd->deal.zeny <= sd->status.zeny && // check amount
+ (target_sd->status.zeny + sd->deal.zeny) <= MAX_ZENY && // fix positiv overflow
+ target_sd->deal.zeny >= 0 && target_sd->deal.zeny <= MAX_ZENY && target_sd->deal.zeny <= target_sd->status.zeny && // check amount
+ (sd->status.zeny + target_sd->deal.zeny) <= MAX_ZENY) { // fix positiv overflow
+
+ // check for full inventory (can not add traded items)
+ if (!trade_check(sd)) { // check the both players
+ trade_tradecancel(sd);
+ return;
+ }
+
+ // trade is accepted
+ for(trade_i = 0; trade_i < 10; trade_i++) {
+ if (sd->deal.item[trade_i].amount != 0) {
+ int n = sd->deal.item[trade_i].index;
+
+ if (sd->status.inventory[n].amount < sd->deal.item[trade_i].amount)
+ sd->deal.item[trade_i].amount = sd->status.inventory[n].amount;
+ log_trade(sd, target_sd, n, sd->deal.item[trade_i].amount);
+
+ flag = pc_additem(target_sd, &sd->status.inventory[n], sd->deal.item[trade_i].amount);
+ if (flag == 0) {
+ //Logs (T)rade [Lupus]
+ if(log_config.pick > 0 )
+ log_pick(sd, "T", 0, sd->status.inventory[n].nameid, -(sd->deal.item[trade_i].amount), &sd->status.inventory[n]);
+ log_pick(target_sd, "T", 0, sd->status.inventory[n].nameid, sd->deal.item[trade_i].amount, &sd->status.inventory[n]);
+ //Logs
+ pc_delitem(sd, n, sd->deal.item[trade_i].amount, 1);
+ } else {
+ clif_additem(sd, n, sd->deal.item[trade_i].amount, 0);
+ }
+ sd->deal.item[trade_i].index = 0;
+ sd->deal.item[trade_i].amount = 0;
+
+ }
+ if (target_sd->deal.item[trade_i].amount != 0) {
+ int n = target_sd->deal.item[trade_i].index;
+
+ if (target_sd->status.inventory[n].amount < target_sd->deal.item[trade_i].amount)
+ target_sd->deal.item[trade_i].amount = target_sd->status.inventory[n].amount;
+
+ log_trade(target_sd, sd, n, target_sd->deal.item[trade_i].amount);
+
+ flag = pc_additem(sd, &target_sd->status.inventory[n], target_sd->deal.item[trade_i].amount);
+ if (flag == 0) {
+ //Logs (T)rade [Lupus]
+ if(log_config.pick > 0 )
+ log_pick(target_sd, "T", 0, target_sd->status.inventory[n].nameid, -(target_sd->deal.item[trade_i].amount), &target_sd->status.inventory[n]);
+ log_pick(sd, "T", 0, target_sd->status.inventory[n].nameid, target_sd->deal.item[trade_i].amount, &target_sd->status.inventory[n]);
+ //Logs
+ pc_delitem(target_sd, n, target_sd->deal.item[trade_i].amount, 1);
+ } else {
+ clif_additem(target_sd, n, target_sd->deal.item[trade_i].amount, 0);
+ }
+ target_sd->deal.item[trade_i].index = 0;
+ target_sd->deal.item[trade_i].amount = 0;
+ }
+ }
+ if (sd->deal.zeny) {
+ //Logs Zeny (T)rade [Lupus]
+ if(log_config.zeny > 0 )
+ log_zeny(target_sd, "T", sd, sd->deal.zeny);
+ //Logs
+ sd->status.zeny -= sd->deal.zeny;
+ target_sd->status.zeny += sd->deal.zeny;
+ }
+ if (target_sd->deal.zeny) {
+ //Logs Zeny (T)rade [Lupus]
+ if(log_config.zeny > 0 )
+ log_zeny(sd, "T", target_sd, target_sd->deal.zeny);
+ //Logs
+ target_sd->status.zeny -= target_sd->deal.zeny;
+ sd->status.zeny += target_sd->deal.zeny;
+ }
+ if (sd->deal.zeny || target_sd->deal.zeny) {
+ clif_updatestatus(sd, SP_ZENY);
+ sd->deal.zeny = 0;
+ clif_updatestatus(target_sd, SP_ZENY);
+ target_sd->deal.zeny = 0;
+ }
+ sd->state.deal_locked = 0;
+ sd->trade_partner = 0;
+ target_sd->state.deal_locked = 0;
+ target_sd->trade_partner = 0;
+ clif_tradecompleted(sd, 0);
+ clif_tradecompleted(target_sd, 0);
+ // save both player to avoid crash: they always have no advantage/disadvantage between the 2 players
+ chrif_save(sd,0); // do pc_makesavestatus and save storage too
+ chrif_save(target_sd,0); // do pc_makesavestatus and save storage too
+ // zeny value was modified!!!! hacker with packet modified
+ } else {
+ trade_tradecancel(sd);
+ }
+ }
+ }
+ }
+}
diff --git a/src/map/trade.h b/src/map/trade.h
new file mode 100644
index 000000000..2114f5a42
--- /dev/null
+++ b/src/map/trade.h
@@ -0,0 +1,15 @@
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#ifndef _TRADE_H_
+#define _TRADE_H_
+
+#include "map.h"
+void trade_traderequest(struct map_session_data *sd,int target_id);
+void trade_tradeack(struct map_session_data *sd,int type);
+void trade_tradeadditem(struct map_session_data *sd,int index,int amount);
+void trade_tradeok(struct map_session_data *sd);
+void trade_tradecancel(struct map_session_data *sd);
+void trade_tradecommit(struct map_session_data *sd);
+
+#endif // _TRADE_H_
diff --git a/src/map/vending.c b/src/map/vending.c
new file mode 100644
index 000000000..53fa281f3
--- /dev/null
+++ b/src/map/vending.c
@@ -0,0 +1,261 @@
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#include <stdio.h>
+#include <string.h>
+
+#include "../common/nullpo.h"
+#include "clif.h"
+#include "itemdb.h"
+#include "atcommand.h"
+#include "map.h"
+#include "chrif.h"
+#include "vending.h"
+#include "pc.h"
+#include "skill.h"
+#include "battle.h"
+#include "log.h"
+
+/*==========================================
+ * 露店閉鎖
+ *------------------------------------------
+*/
+void vending_closevending(struct map_session_data *sd)
+{
+
+ nullpo_retv(sd);
+
+ sd->vender_id=0;
+ clif_closevendingboard(&sd->bl,0);
+}
+
+/*==========================================
+ * 露店アイテムリスト要求
+ *------------------------------------------
+ */
+void vending_vendinglistreq(struct map_session_data *sd,int id)
+{
+ struct map_session_data *vsd;
+
+ nullpo_retv(sd);
+
+ if( (vsd=map_id2sd(id)) == NULL )
+ return;
+ if(vsd->vender_id==0)
+ return;
+ clif_vendinglist(sd,id,vsd->vending);
+}
+
+/*==========================================
+ * 露店アイテム購入
+ *------------------------------------------
+ */
+void vending_purchasereq(struct map_session_data *sd,int len,int id,unsigned char *p)
+{
+ int i, j, w, new_ = 0, blank, vend_list[MAX_VENDING];
+ double z;
+ unsigned short amount;
+ short idx;
+ struct map_session_data *vsd = map_id2sd(id);
+ struct vending vending[MAX_VENDING]; // against duplicate packets
+
+ nullpo_retv(sd);
+
+ if (vsd == NULL)
+ return;
+ if (vsd->vender_id == 0)
+ return;
+ if (vsd->vender_id == sd->bl.id)
+ return;
+
+ // check number of buying items
+ if (len < 8 + 4 || len > 8 + 4 * MAX_VENDING) {
+ clif_buyvending(sd, 0, 32767, 4); // not enough quantity (index and amount are unknown)
+ return;
+ }
+
+ blank = pc_inventoryblank(sd); //number of free cells in the buyer's inventory
+
+ // duplicate item in vending to check hacker with multiple packets
+ memcpy(&vending, &vsd->vending, sizeof(struct vending) * MAX_VENDING); // copy vending list
+
+ // some checks
+ z = 0.;
+ w = 0;
+ for(i = 0; 8 + 4 * i < len; i++) {
+ amount = *(unsigned short*)(p + 4 * i);
+ idx = *(short*)(p + 2 + 4 * i) - 2;
+
+ if (amount <= 0)
+ return;
+
+ // check of item index in the cart
+ if (idx < 0 || idx >= MAX_CART)
+ return;
+
+ for(j = 0; j < vsd->vend_num; j++) {
+ if (vsd->vending[j].index == idx) {
+ vend_list[i] = j;
+ break;
+ }
+ }
+ if (j == vsd->vend_num)
+ return; //picked non-existing item
+
+ z += ((double)vsd->vending[j].value * (double)amount);
+ if (z > (double)sd->status.zeny || z < 0. || z > (double)MAX_ZENY) { // fix positiv overflow (buyer)
+ clif_buyvending(sd, idx, amount, 1); // you don't have enough zeny
+ return; // zenys'<
+ }
+ if (z + (double)vsd->status.zeny > (double)MAX_ZENY) { // fix positiv overflow (merchand)
+ clif_buyvending(sd, idx, vsd->vending[j].amount, 4); // too much zeny = overflow
+ return; // zenys'<
+ }
+ w += itemdb_weight(vsd->status.cart[idx].nameid) * amount;
+ if (w + sd->weight > sd->max_weight) {
+ clif_buyvending(sd, idx, amount, 2); // you can not buy, because overweight
+ return;
+ }
+
+ if (vending[j].amount > vsd->status.cart[idx].amount) //Check to see if cart/vend info is in sync.
+ vending[j].amount = vsd->status.cart[idx].amount;
+
+ // if they try to add packets (example: get twice or more 2 apples if marchand has only 3 apples).
+ // here, we check cumulativ amounts
+ if (vending[j].amount < amount) {
+ // send more quantity is not a hack (an other player can have buy items just before)
+ clif_buyvending(sd, idx, vsd->vending[j].amount, 4); // not enough quantity
+ return;
+ } else
+ vending[j].amount -= amount;
+
+ switch(pc_checkadditem(sd, vsd->status.cart[idx].nameid, amount)) {
+ case ADDITEM_EXIST:
+ break; //We'd add this item to the existing one (in buyers inventory)
+ case ADDITEM_NEW:
+ new_++;
+ if (new_ > blank)
+ return; //Buyer has no space in his inventory
+ break;
+ case ADDITEM_OVERAMOUNT:
+ return; //too many items
+ }
+ }
+
+ //Logs (V)ending Zeny [Lupus]
+ if(log_config.zeny > 0 )
+ log_zeny(vsd, "V", sd, (int)z);
+ //Logs
+
+ pc_payzeny(sd, (int)z);
+ pc_getzeny(vsd, (int)z);
+
+ for(i = 0; 8 + 4 * i < len; i++) {
+ amount = *(short*)(p + 4 *i);
+ idx = *(short*)(p + 2 + 4 * i) - 2;
+ //if (amount < 0) break; // tested at start of the function
+
+ //Logs sold (V)ending items [Lupus]
+ if(log_config.pick > 0 ) {
+ log_pick(vsd, "V", 0, vsd->status.cart[idx].nameid, -amount, (struct item*)&vsd->status.cart[idx]);
+ log_pick( sd, "V", 0, vsd->status.cart[idx].nameid, amount, (struct item*)&vsd->status.cart[idx]);
+ }
+ //Logs
+
+ //Old VENDING log added by Lupus
+ if(log_config.vend > 0) {
+ log_vend(sd,vsd, idx, amount, (int)z); // for Item + Zeny. log.
+ //we log ZENY only with the 1st item. Then zero it for the rest items
+ z = 0;
+ }
+
+ // vending item
+ pc_additem(sd, &vsd->status.cart[idx], amount);
+ vsd->vending[vend_list[i]].amount -= amount;
+ pc_cart_delitem(vsd, idx, amount, 0);
+ clif_vendingreport(vsd, idx, amount);
+
+ //print buyer's name
+ if(battle_config.buyer_name) {
+ char temp[256];
+ sprintf(temp, msg_txt(265), sd->status.name);
+ clif_disp_onlyself(vsd,temp,strlen(temp));
+ }
+ }
+
+ //Always save BOTH: buyer and customer
+ chrif_save(sd,0);
+ chrif_save(vsd,0);
+ //check for @AUTOTRADE users [durf]
+ if (vsd->state.autotrade)
+ {
+ //Close Vending (this was automatically done by the client, we have to do it manually for autovenders) [Skotlex]
+ for(i = 0; i < vsd->vend_num && vsd->vending[i].amount < 1; i++);
+ if (i == vsd->vend_num)
+ {
+ vending_closevending(vsd);
+ map_quit(vsd); //They have no reason to stay around anymore, do they?
+ }
+ }
+}
+
+/*==========================================
+ * 露店開設
+ *------------------------------------------
+ */
+void vending_openvending(struct map_session_data *sd,int len,char *message,int flag,unsigned char *p)
+{
+ int i, j;
+ int vending_skill_lvl;
+ nullpo_retv(sd);
+
+ //check shopname len
+ if(message[0] == '\0')
+ return;
+
+ vending_skill_lvl = pc_checkskill(sd, MC_VENDING);
+ if(!vending_skill_lvl || !pc_iscarton(sd)) { // cart skill and cart check [Valaris]
+ clif_skill_fail(sd,MC_VENDING,0,0);
+ return;
+ }
+
+ if (flag) {
+ // check number of items in shop
+ if (len < 85 + 8 || len > 85 + 8 * MAX_VENDING || len > 85 + 8 * (2 + vending_skill_lvl)) {
+ clif_skill_fail(sd, MC_VENDING, 0, 0);
+ return;
+ }
+ for(i = 0, j = 0; (85 + 8 * j < len) && (i < MAX_VENDING); i++, j++) {
+ sd->vending[i].index = *(short*)(p+8*j)-2;
+ if (sd->vending[i].index < 0 || sd->vending[i].index >= MAX_CART ||
+ !itemdb_cantrade(sd->status.cart[sd->vending[i].index].nameid, pc_isGM(sd), pc_isGM(sd)))
+ {
+ i--; //Preserve the vending index, skip to the next item.
+ continue;
+ }
+ sd->vending[i].amount = *(short*)(p+2+8*j);
+ sd->vending[i].value = *(int*)(p+4+8*j);
+ if(sd->vending[i].value > battle_config.vending_max_value)
+ sd->vending[i].value=battle_config.vending_max_value;
+ else if(sd->vending[i].value < 1)
+ sd->vending[i].value = 1000000; // auto set to 1 million [celest]
+ // カート内のアイテム数と販売するアイテム数に相違があったら中止
+ if(pc_cartitem_amount(sd, sd->vending[i].index, sd->vending[i].amount) < 0 || sd->vending[i].value < 0) { // fixes by Valaris and fritz
+ clif_skill_fail(sd, MC_VENDING, 0, 0);
+ return;
+ }
+ }
+ if (i != j)
+ { //Some items were not vended. [Skotlex]
+ clif_displaymessage (sd->fd, msg_txt(266));
+ }
+ sd->vender_id = sd->bl.id;
+ sd->vend_num = i;
+ memcpy(sd->message,message, MESSAGE_SIZE-1);
+ if (clif_openvending(sd,sd->vender_id,sd->vending) > 0)
+ clif_showvendingboard(&sd->bl,message,0);
+ else
+ sd->vender_id = 0;
+ }
+}
+
diff --git a/src/map/vending.h b/src/map/vending.h
new file mode 100644
index 000000000..021866d25
--- /dev/null
+++ b/src/map/vending.h
@@ -0,0 +1,14 @@
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#ifndef _VENDING_H_
+#define _VENDING_H_
+
+#include "map.h"
+
+void vending_closevending(struct map_session_data *sd);
+void vending_openvending(struct map_session_data *sd,int len,char *message,int flag,unsigned char *p);
+void vending_vendinglistreq(struct map_session_data *sd,int id);
+void vending_purchasereq(struct map_session_data *sd,int len,int id,unsigned char *p);
+
+#endif // _VENDING_H_